Puromycin dataset
Data on the velocity of an enzymatic reaction were obtained by Treloar (1974). The number of counts per minute of radioactive product from the reaction was measured as a function of substrate concentration in parts per million (ppm) and from these counts the initial rate (or velocity) of the reaction was calculated (counts/min/min). The experiment was conducted once with the enzyme treated with Puromycin, and once with the enzyme untreated.
Import libraries
Import data
In contrast to the other datasets we have worked with so far, this dataset is not available through a URL link. In stead, the data is directly available from an R package that was pre-installed in your R working environment. As such, we can simple do
and an object called Puromycin
is immediately available in your working environment.
Data wrangling
Filter the data so that we are left with only the “treated” enzymes.
Data exploration
In the original publication the authors observed a linear relationship between the log10 substrate concentration and the reaction rate. Make a visualization that allows for reproducing this finding.
Puromycin %>%
ggplot(...) + # select which elements of the dataset we need to visualize
... # use a relevant plotting geometry
stat_smooth(...) + # draw a smooth line through the data cloud
stat_smooth(...) # draw a straight (linear regression) line through the data cloud
... # you can add some extra elements like axis labels, title, ...
Note, that the researchers have chosen 6 different substrate concentrations and conducted an experiment where they assessed the initial reaction rate twice for every concentration.
Question 1
Use the data to calculate the power to pick up an association that is as least as strong as the association you observed in the dataset when using an experiment with the same design.
Simulation function
For this first question, we will need a function that allows us to simulate data similar to that of our experiment under our model assumptions. We provide this function for you:
simFast <- function(form, data, betas, sd, contrasts, alpha = .05, nSim = 10000) {
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 <- c(t(contrasts) %*% fitAll$cov.coefficients %*% contrasts)
contrasts <- fitAll$coefficients %*% contrasts
seContrasts <- varUnscaled^.5 * fitAll$sigma
tstats <- contrasts / seContrasts
pvals <- pt(abs(tstats), fitAll$df.residual, lower.tail = FALSE) * 2
return(mean(pvals < alpha))
}
Without going into the full code details, this function allows us to simulate data similar to that of our experiment under our model assumptions, given the following inputs:
form
: model formula for the experiment we want to simulate
data
: the target dataset on which we want to base our simulations on
betas
: the linear regression coefficients for the target dataset
sd
: the residual standard errors from the linear regression model fit on the target dataset
contrasts
: comparison of interest, i.e. which (combination of) model parameters we would like to assess
alpha
: alpha-level at which to conduct the hypothesis testing
nSim
: number of datasets we would like to simulate
To simulate new data based on our target dataset Puromycin
, we will need to fill in all the arguments to the simFast
function.
Hint: for the betas and sd, we will need to fit a linear model first!
power1 <- simFast(
form = ...,
data = ...,
betas = ...,
sd = ...,
contrasts = ...,
alpha = ...,
nSim = ...
)
power1
Question 2
Use the data to calculate the power to pick up an association where the reaction rate increases on average with 10 counts/min when the substrate concentration is 10 times higher (\(\beta_1=10\)).
Again, we will need to fill in all the arguments to the simFast
function. This time, however, the question is slightly different from question 1. Adjust the inputs accordingly!
power2 <- simFast(
form = ...,
data = ...,
betas = ...,
sd = ...,
contrasts = ...,
alpha = ...,
nSim = ...
)
power2
Compare your result with the one from question 1!
Question 3
Use the data to calculate the number of repeats you need for each concentration to pick up an association where the reaction rate increases on average with 10 counts/min when the substrate concentration is 10 times higher (\(\beta_1=10\)) with a power of at least 90%.
Hint: this will require us to repeat the simulation process as many times as we have concentration values.
concentrations <- Puromycin %>%
pull(conc) %>%
unique()
powers <- data.frame(n = 1:10, power = NA) # to later store our results
for (i in 1:10)
{
simData <- data.frame(conc = rep(concentrations, each = i))
powers[i, 2] <- simFast(
form = ...,
data = ...,
betas = ...,
sd = ...,
contrasts = ...,
alpha = ...,
nSim = ...
)
}
Make a visualization that displays the power in function of the number of repeats in the data.
powers %>%
ggplot(...) +
geom_line() +
geom_hline(yintercept = 0.9, lty = 2)
Question 4
Suppose that you would set up an experiment with a design similar with the same concentrations as in the puromycin dataset and you have the following restriction: you need to use each concentration at least once and can setup at most 12 reactions, how would you choose your design points? Calculate the power for this design when the effect size is 10 counts/min per 10 times increase in the substrate concentration (\(\beta_1=10\)).
concentrations <- ...
simData <- data.frame(conc = c(
concentrations, rep(min(concentrations), 3),
rep(max(concentrations), 3)
))
powerOpt <- simFast(
form = ...,
data = ...,
betas = ...,
sd = ...,
contrasts = ...,
alpha = ...,
nSim = ...
)
simData
powerOpt
Make a visualization that displays the power in function of the number of repeats in the data.
Inspect and interpret the results.
LS0tCnRpdGxlOiAiRXhwZXJpbWVudGFsIERlc2lnbiBJSTogcmVwbGljYXRpb24gYW5kIHBvd2VyIGV4ZXJjaXNlIDIiCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50ICYgQWxleGFuZHJlIFNlZ2VycyIKZGF0ZTogInN0YXRPbWljcywgR2hlbnQgVW5pdmVyc2l0eSAoaHR0cHM6Ly9zdGF0b21pY3MuZ2l0aHViLmlvKSIKLS0tCgo8YSByZWw9ImxpY2Vuc2UiIGhyZWY9Imh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1zYS80LjAiPjxpbWcgYWx0PSJDcmVhdGl2ZSBDb21tb25zIExpY2Vuc2UiIHN0eWxlPSJib3JkZXItd2lkdGg6MCIgc3JjPSJodHRwczovL2kuY3JlYXRpdmVjb21tb25zLm9yZy9sL2J5LW5jLXNhLzQuMC84OHgzMS5wbmciIC8+PC9hPgoKIyBQdXJvbXljaW4gZGF0YXNldAoKRGF0YSBvbiB0aGUgdmVsb2NpdHkgb2YgYW4gZW56eW1hdGljIHJlYWN0aW9uIHdlcmUgb2J0YWluZWQgYnkgVHJlbG9hciAoMTk3NCkuClRoZSBudW1iZXIgb2YgY291bnRzIHBlciBtaW51dGUgb2YgcmFkaW9hY3RpdmUgcHJvZHVjdCBmcm9tIHRoZSByZWFjdGlvbiB3YXMKbWVhc3VyZWQgYXMgYSBmdW5jdGlvbiBvZiBzdWJzdHJhdGUgY29uY2VudHJhdGlvbiBpbiBwYXJ0cyBwZXIgbWlsbGlvbiAocHBtKSBhbmQKZnJvbSB0aGVzZSBjb3VudHMgdGhlIGluaXRpYWwgcmF0ZSAob3IgdmVsb2NpdHkpIG9mIHRoZSByZWFjdGlvbiB3YXMgY2FsY3VsYXRlZCAoY291bnRzL21pbi9taW4pLiBUaGUgZXhwZXJpbWVudCB3YXMgY29uZHVjdGVkIG9uY2Ugd2l0aCB0aGUgZW56eW1lIHRyZWF0ZWQKd2l0aCBQdXJvbXljaW4sIGFuZCBvbmNlIHdpdGggdGhlIGVuenltZSB1bnRyZWF0ZWQuCgpJbXBvcnQgbGlicmFyaWVzCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmBgYAoKIyBJbXBvcnQgZGF0YQoKSW4gY29udHJhc3QgdG8gdGhlIG90aGVyIGRhdGFzZXRzIHdlIGhhdmUgd29ya2VkIHdpdGggc28gZmFyLCB0aGlzIGRhdGFzZXQgaXMKbm90IGF2YWlsYWJsZSB0aHJvdWdoIGEgVVJMIGxpbmsuIEluIHN0ZWFkLCB0aGUgZGF0YSBpcyBkaXJlY3RseSBhdmFpbGFibGUgZnJvbQphbiBSIHBhY2thZ2UgdGhhdCB3YXMgcHJlLWluc3RhbGxlZCBpbiB5b3VyIFIgd29ya2luZyBlbnZpcm9ubWVudC4gQXMgc3VjaCwgd2UKY2FuIHNpbXBsZSBkbwoKYGBge3J9CmRhdGEoUHVyb215Y2luKQpgYGAKCmFuZCBhbiBvYmplY3QgY2FsbGVkIGBQdXJvbXljaW5gIGlzIGltbWVkaWF0ZWx5IGF2YWlsYWJsZSBpbiB5b3VyIHdvcmtpbmcKZW52aXJvbm1lbnQuCgojIERhdGEgd3JhbmdsaW5nCgpGaWx0ZXIgdGhlIGRhdGEgc28gdGhhdCB3ZSBhcmUgbGVmdCB3aXRoIG9ubHkgdGhlICJ0cmVhdGVkIiBlbnp5bWVzLgoKYGBge3J9CgpgYGAKCiMgRGF0YSBleHBsb3JhdGlvbgoKSW4gdGhlIG9yaWdpbmFsIHB1YmxpY2F0aW9uIHRoZSBhdXRob3JzIG9ic2VydmVkIGEgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuCnRoZSBsb2cxMCBzdWJzdHJhdGUgY29uY2VudHJhdGlvbiBhbmQgdGhlIHJlYWN0aW9uIHJhdGUuIE1ha2UgYSB2aXN1YWxpemF0aW9uCnRoYXQgYWxsb3dzIGZvciByZXByb2R1Y2luZyB0aGlzIGZpbmRpbmcuCgpgYGB7ciwgZXZhbD1GQUxTRX0KUHVyb215Y2luICU+JQogIGdncGxvdCguLi4pICsgIyBzZWxlY3Qgd2hpY2ggZWxlbWVudHMgb2YgdGhlIGRhdGFzZXQgd2UgbmVlZCB0byB2aXN1YWxpemUKICAuLi4gIyB1c2UgYSByZWxldmFudCBwbG90dGluZyBnZW9tZXRyeQpzdGF0X3Ntb290aCguLi4pICsgIyBkcmF3IGEgc21vb3RoIGxpbmUgdGhyb3VnaCB0aGUgZGF0YSBjbG91ZAogIHN0YXRfc21vb3RoKC4uLikgIyBkcmF3IGEgc3RyYWlnaHQgKGxpbmVhciByZWdyZXNzaW9uKSBsaW5lIHRocm91Z2ggdGhlIGRhdGEgY2xvdWQKLi4uICMgeW91IGNhbiBhZGQgc29tZSBleHRyYSBlbGVtZW50cyBsaWtlIGF4aXMgbGFiZWxzLCB0aXRsZSwgLi4uCmBgYAoKTm90ZSwgdGhhdCB0aGUgcmVzZWFyY2hlcnMgaGF2ZSBjaG9zZW4gNiBkaWZmZXJlbnQgc3Vic3RyYXRlIGNvbmNlbnRyYXRpb25zIGFuZApjb25kdWN0ZWQgYW4gZXhwZXJpbWVudCB3aGVyZSB0aGV5IGFzc2Vzc2VkIHRoZSBpbml0aWFsIHJlYWN0aW9uIHJhdGUgdHdpY2UgZm9yCmV2ZXJ5IGNvbmNlbnRyYXRpb24uCgojIFF1ZXN0aW9uIDEKClVzZSB0aGUgZGF0YSB0byBjYWxjdWxhdGUgdGhlIHBvd2VyIHRvIHBpY2sgdXAgYW4gYXNzb2NpYXRpb24gdGhhdCBpcyBhcwpsZWFzdCBhcyBzdHJvbmcgYXMgdGhlIGFzc29jaWF0aW9uIHlvdSBvYnNlcnZlZCBpbiB0aGUgZGF0YXNldCB3aGVuIHVzaW5nIGFuCmV4cGVyaW1lbnQgd2l0aCB0aGUgc2FtZSBkZXNpZ24uCgojIyBTaW11bGF0aW9uIGZ1bmN0aW9uCgpGb3IgdGhpcyBmaXJzdCBxdWVzdGlvbiwgd2Ugd2lsbCBuZWVkIGEgZnVuY3Rpb24gdGhhdCBhbGxvd3MgdXMgdG8gc2ltdWxhdGUgZGF0YQpzaW1pbGFyIHRvIHRoYXQgb2Ygb3VyIGV4cGVyaW1lbnQgdW5kZXIgb3VyIG1vZGVsIGFzc3VtcHRpb25zLiBXZSBwcm92aWRlIHRoaXMKZnVuY3Rpb24gZm9yIHlvdToKCmBgYHtyfQpzaW1GYXN0IDwtIGZ1bmN0aW9uKGZvcm0sIGRhdGEsIGJldGFzLCBzZCwgY29udHJhc3RzLCBhbHBoYSA9IC4wNSwgblNpbSA9IDEwMDAwKSB7CiAgeVNpbSA8LSBybm9ybShucm93KGRhdGEpICogblNpbSwgc2QgPSBzZCkKICBkaW0oeVNpbSkgPC0gYyhucm93KGRhdGEpLCBuU2ltKQogIGRlc2lnbiA8LSBtb2RlbC5tYXRyaXgoZm9ybSwgZGF0YSkKICB5U2ltIDwtIHlTaW0gKyBjKGRlc2lnbiAlKiUgYmV0YXMpCiAgeVNpbSA8LSB0KHlTaW0pCgogICMjIyBGaXR0aW5nCiAgZml0QWxsIDwtIGxpbW1hOjpsbUZpdCh5U2ltLCBkZXNpZ24pCgogICMjIyBJbmZlcmVuY2UKICB2YXJVbnNjYWxlZCA8LSBjKHQoY29udHJhc3RzKSAlKiUgZml0QWxsJGNvdi5jb2VmZmljaWVudHMgJSolIGNvbnRyYXN0cykKICBjb250cmFzdHMgPC0gZml0QWxsJGNvZWZmaWNpZW50cyAlKiUgY29udHJhc3RzCiAgc2VDb250cmFzdHMgPC0gdmFyVW5zY2FsZWReLjUgKiBmaXRBbGwkc2lnbWEKICB0c3RhdHMgPC0gY29udHJhc3RzIC8gc2VDb250cmFzdHMKICBwdmFscyA8LSBwdChhYnModHN0YXRzKSwgZml0QWxsJGRmLnJlc2lkdWFsLCBsb3dlci50YWlsID0gRkFMU0UpICogMgogIHJldHVybihtZWFuKHB2YWxzIDwgYWxwaGEpKQp9CmBgYAoKV2l0aG91dCBnb2luZyBpbnRvIHRoZSBmdWxsIGNvZGUgZGV0YWlscywgdGhpcyBmdW5jdGlvbiBhbGxvd3MgdXMgdG8gc2ltdWxhdGUKZGF0YSBzaW1pbGFyIHRvIHRoYXQgb2Ygb3VyIGV4cGVyaW1lbnQgdW5kZXIgb3VyIG1vZGVsIGFzc3VtcHRpb25zLCBnaXZlbiB0aGUKZm9sbG93aW5nIGlucHV0czoKCi0gYGZvcm1gOiBtb2RlbCBmb3JtdWxhIGZvciB0aGUgZXhwZXJpbWVudCB3ZSB3YW50IHRvIHNpbXVsYXRlCi0gYGRhdGFgOiB0aGUgdGFyZ2V0IGRhdGFzZXQgb24gd2hpY2ggd2Ugd2FudCB0byBiYXNlIG91ciBzaW11bGF0aW9ucyBvbgotIGBiZXRhc2A6IHRoZSBsaW5lYXIgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgZm9yIHRoZSB0YXJnZXQgZGF0YXNldAotIGBzZGA6IHRoZSByZXNpZHVhbCBzdGFuZGFyZCBlcnJvcnMgZnJvbSB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZml0IG9uCnRoZSB0YXJnZXQgZGF0YXNldAotIGBjb250cmFzdHNgOiBjb21wYXJpc29uIG9mIGludGVyZXN0LCBpLmUuIHdoaWNoIChjb21iaW5hdGlvbiBvZikgbW9kZWwKcGFyYW1ldGVycyB3ZSB3b3VsZCBsaWtlIHRvIGFzc2VzcwotIGBhbHBoYWA6IGFscGhhLWxldmVsIGF0IHdoaWNoIHRvIGNvbmR1Y3QgdGhlIGh5cG90aGVzaXMgdGVzdGluZwotIGBuU2ltYDogbnVtYmVyIG9mIGRhdGFzZXRzIHdlIHdvdWxkIGxpa2UgdG8gc2ltdWxhdGUKClRvIHNpbXVsYXRlIG5ldyBkYXRhIGJhc2VkIG9uIG91ciB0YXJnZXQgZGF0YXNldCBgUHVyb215Y2luYCwgd2Ugd2lsbCBuZWVkIHRvCmZpbGwgaW4gYWxsIHRoZSBhcmd1bWVudHMgdG8gdGhlIGBzaW1GYXN0YCBmdW5jdGlvbi4KCioqSGludDogZm9yIHRoZSBiZXRhcyBhbmQgc2QsIHdlIHdpbGwgbmVlZCB0byBmaXQgYSBsaW5lYXIgbW9kZWwgZmlyc3QhKioKCmBgYHtyLCBldmFsPUZBTFNFfQpwb3dlcjEgPC0gc2ltRmFzdCgKICBmb3JtID0gLi4uLAogIGRhdGEgPSAuLi4sCiAgYmV0YXMgPSAuLi4sCiAgc2QgPSAuLi4sCiAgY29udHJhc3RzID0gLi4uLAogIGFscGhhID0gLi4uLAogIG5TaW0gPSAuLi4KKQpwb3dlcjEKYGBgCgojIFF1ZXN0aW9uIDIKClVzZSB0aGUgZGF0YSB0byBjYWxjdWxhdGUgdGhlIHBvd2VyIHRvIHBpY2sgdXAgYW4gYXNzb2NpYXRpb24gd2hlcmUgdGhlCnJlYWN0aW9uIHJhdGUgaW5jcmVhc2VzIG9uIGF2ZXJhZ2Ugd2l0aCAxMCBjb3VudHMvbWluIHdoZW4gdGhlIHN1YnN0cmF0ZQpjb25jZW50cmF0aW9uIGlzIDEwIHRpbWVzIGhpZ2hlciAoJFxiZXRhXzE9MTAkKS4KCkFnYWluLCB3ZSB3aWxsIG5lZWQgdG8gZmlsbCBpbiBhbGwgdGhlIGFyZ3VtZW50cyB0byB0aGUgYHNpbUZhc3RgIGZ1bmN0aW9uLgpUaGlzIHRpbWUsIGhvd2V2ZXIsIHRoZSBxdWVzdGlvbiBpcyBzbGlnaHRseSBkaWZmZXJlbnQgZnJvbSBxdWVzdGlvbiAxLgpBZGp1c3QgdGhlIGlucHV0cyBhY2NvcmRpbmdseSEKCmBgYHtyLCBldmFsPUZBTFNFfQpwb3dlcjIgPC0gc2ltRmFzdCgKICBmb3JtID0gLi4uLAogIGRhdGEgPSAuLi4sCiAgYmV0YXMgPSAuLi4sCiAgc2QgPSAuLi4sCiAgY29udHJhc3RzID0gLi4uLAogIGFscGhhID0gLi4uLAogIG5TaW0gPSAuLi4KKQpwb3dlcjIKYGBgCgoqKkNvbXBhcmUgeW91ciByZXN1bHQgd2l0aCB0aGUgb25lIGZyb20gcXVlc3Rpb24gMSEqKgoKIyBRdWVzdGlvbiAzCgpVc2UgdGhlIGRhdGEgdG8gY2FsY3VsYXRlIHRoZSBudW1iZXIgb2YgcmVwZWF0cyB5b3UgbmVlZCBmb3IgZWFjaApjb25jZW50cmF0aW9uIHRvIHBpY2sgdXAgYW4gYXNzb2NpYXRpb24gd2hlcmUgdGhlIHJlYWN0aW9uIHJhdGUgaW5jcmVhc2VzIG9uCmF2ZXJhZ2Ugd2l0aCAxMCBjb3VudHMvbWluIHdoZW4gdGhlIHN1YnN0cmF0ZSBjb25jZW50cmF0aW9uIGlzIDEwIHRpbWVzIGhpZ2hlcgooJFxiZXRhXzE9MTAkKSB3aXRoIGEgcG93ZXIgb2YgYXQgbGVhc3QgOTAlLgoKKipIaW50OiB0aGlzIHdpbGwgcmVxdWlyZSB1cyB0byByZXBlYXQgdGhlIHNpbXVsYXRpb24gcHJvY2VzcyBhcyBtYW55IHRpbWVzKioKKiphcyB3ZSBoYXZlIGNvbmNlbnRyYXRpb24gdmFsdWVzLioqCgpgYGB7ciwgZXZhbD1GQUxTRX0KY29uY2VudHJhdGlvbnMgPC0gUHVyb215Y2luICU+JQogIHB1bGwoY29uYykgJT4lCiAgdW5pcXVlKCkKCnBvd2VycyA8LSBkYXRhLmZyYW1lKG4gPSAxOjEwLCBwb3dlciA9IE5BKSAjIHRvIGxhdGVyIHN0b3JlIG91ciByZXN1bHRzCgpmb3IgKGkgaW4gMToxMCkKewogIHNpbURhdGEgPC0gZGF0YS5mcmFtZShjb25jID0gcmVwKGNvbmNlbnRyYXRpb25zLCBlYWNoID0gaSkpCiAgcG93ZXJzW2ksIDJdIDwtIHNpbUZhc3QoCiAgICBmb3JtID0gLi4uLAogICAgZGF0YSA9IC4uLiwKICAgIGJldGFzID0gLi4uLAogICAgc2QgPSAuLi4sCiAgICBjb250cmFzdHMgPSAuLi4sCiAgICBhbHBoYSA9IC4uLiwKICAgIG5TaW0gPSAuLi4KICApCn0KYGBgCgpNYWtlIGEgdmlzdWFsaXphdGlvbiB0aGF0IGRpc3BsYXlzIHRoZSBwb3dlciBpbiBmdW5jdGlvbiBvZiB0aGUgbnVtYmVyIG9mCnJlcGVhdHMgaW4gdGhlIGRhdGEuCgpgYGB7ciwgZXZhbD1GQUxTRX0KcG93ZXJzICU+JQogIGdncGxvdCguLi4pICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMC45LCBsdHkgPSAyKQpgYGAKCiMgUXVlc3Rpb24gNAoKU3VwcG9zZSB0aGF0IHlvdSB3b3VsZCBzZXQgdXAgYW4gZXhwZXJpbWVudCB3aXRoIGEgZGVzaWduIHNpbWlsYXIgd2l0aCB0aGUKc2FtZSBjb25jZW50cmF0aW9ucyBhcyBpbiB0aGUgcHVyb215Y2luIGRhdGFzZXQgYW5kIHlvdSBoYXZlIHRoZSBmb2xsb3dpbmcKcmVzdHJpY3Rpb246IHlvdSBuZWVkIHRvIHVzZSBlYWNoIGNvbmNlbnRyYXRpb24gYXQgbGVhc3Qgb25jZSBhbmQgY2FuIHNldHVwIGF0Cm1vc3QgMTIgcmVhY3Rpb25zLCBob3cgd291bGQgeW91IGNob29zZSB5b3VyIGRlc2lnbiBwb2ludHM/IENhbGN1bGF0ZSB0aGUgcG93ZXIKZm9yIHRoaXMgZGVzaWduIHdoZW4gdGhlIGVmZmVjdCBzaXplIGlzIDEwIGNvdW50cy9taW4gcGVyIDEwIHRpbWVzIGluY3JlYXNlIGluCnRoZSBzdWJzdHJhdGUgY29uY2VudHJhdGlvbiAoJFxiZXRhXzE9MTAkKS4KCmBgYHtyLCBldmFsPUZBTFNFfQpjb25jZW50cmF0aW9ucyA8LSAuLi4KCgpzaW1EYXRhIDwtIGRhdGEuZnJhbWUoY29uYyA9IGMoCiAgY29uY2VudHJhdGlvbnMsIHJlcChtaW4oY29uY2VudHJhdGlvbnMpLCAzKSwKICByZXAobWF4KGNvbmNlbnRyYXRpb25zKSwgMykKKSkKCnBvd2VyT3B0IDwtIHNpbUZhc3QoCiAgZm9ybSA9IC4uLiwKICBkYXRhID0gLi4uLAogIGJldGFzID0gLi4uLAogIHNkID0gLi4uLAogIGNvbnRyYXN0cyA9IC4uLiwKICBhbHBoYSA9IC4uLiwKICBuU2ltID0gLi4uCikKCnNpbURhdGEKcG93ZXJPcHQKYGBgCgpNYWtlIGEgdmlzdWFsaXphdGlvbiB0aGF0IGRpc3BsYXlzIHRoZSBwb3dlciBpbiBmdW5jdGlvbiBvZiB0aGUgbnVtYmVyIG9mCnJlcGVhdHMgaW4gdGhlIGRhdGEuCgpgYGB7ciwgZXZhbD1GQUxTRX0KCmBgYAoKSW5zcGVjdCBhbmQgaW50ZXJwcmV0IHRoZSByZXN1bHRzLgo=