2.1 Simulation Run
Parameter Definitions
## set seed to make simulations reproducible
set.seed(20210108)
## let's start with some parameter definitions
nsamp = 100
beta = 1 #effect size
h2 = 0.1 #heritability
sig2X = h2
sig2epsi = (1 - sig2X) * beta^2
sigX = sqrt(sig2X) #genetic standard deviation
sigepsi = sqrt(sig2epsi)
#simulate X
X = rnorm(nsamp,mean=0, sd= sigX) #SNP
epsi = rnorm(nsamp,mean=0, sd=sigepsi) #environmental effects
#simulate Ynull
Ynull = rnorm(nsamp, mean=0, sd=beta) #phenotype
#simulate Yalt
Yalt = X * beta + epsi #phenotype
First, defining fastlm to repeat linear regression efficiently
fastlm = function(xx,yy)
{
## compute betahat (regression coef) and pvalue with Ftest
## for now it does not take covariates
df1 = 2
df0 = 1
ind = !is.na(xx) & !is.na(yy)
xx = xx[ind]
yy = yy[ind]
n = sum(ind)
xbar = mean(xx)
ybar = mean(yy)
xx = xx - xbar
yy = yy - ybar
SXX = sum( xx^2 )
SYY = sum( yy^2 )
SXY = sum( xx * yy )
betahat = SXY / SXX
RSS1 = sum( ( yy - xx * betahat )^2 )
RSS0 = SYY
fstat = ( ( RSS0 - RSS1 ) / ( df1 - df0 ) ) / ( RSS1 / ( n - df1 ) )
pval = 1 - pf(fstat, df1 = ( df1 - df0 ), df2 = ( n - df1 ))
res = list(betahat = betahat, pval = pval)
return(res)
}
nsim = 10000
## simulate normally distributed X and epsi
Xmat = matrix(rnorm(nsim * nsamp,mean=0, sd= sigX), nsamp, nsim)
epsimat = matrix(rnorm(nsim * nsamp,mean=0, sd=sigepsi), nsamp, nsim)
## generate Yalt (X has an effect on Yalt)
Ymat_alt = Xmat * beta + epsimat
## generate Ynull (X has no effect on Ynull)
Ymat_null = matrix(rnorm(nsim * nsamp, mean=0, sd=beta), nsamp, nsim)
## let's look at the dimensions of the simulated matrices
print(dim(Ymat_null))
[1] 100 10000
print(dim(Ymat_alt))
[1] 100 10000
The matrix dimensions for Ynull and Yalt is 100 x 10000
#Running simulation 10,000 times for Ynull
pvec_null = rep(NA,nsim)
bvec_null = rep(NA,nsim)
for(ss in 1:nsim)
{
fit = fastlm(Xmat[,ss], Ymat_null[,ss]) #linear regression
pvec_null[ss] = fit$pval #find p-values and save them
bvec_null[ss] = fit$betahat #find coefficients and save them
}
summary(pvec_null)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 0.244 0.491 0.494 0.740 1.000
#This is for Yalt
pvec_alt = rep(NA,nsim)
bvec_alt = rep(NA,nsim)
for(ss in 1:nsim)
{
fit = fastlm(Xmat[,ss], Ymat_alt[,ss]) #linear regression
pvec_alt[ss] = fit$pval #find p-values and save them
bvec_alt[ss] = fit$betahat #find coefficients and save them
}
summary(pvec_alt)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0001 0.0013 0.0205 0.0098 0.9727
2.2 P-value Distributions
Distribution of p-values under the Null
hist(pvec_null,xlab="p-value",main="Histogram of p-values under Null")

The histogram of the p-values is uniformly distributed. This is
because if the null hypothesis is true, then all possible p-values are
equally likely across the interval (0,1).
Distribution of p-values under the Alternative
hist(pvec_alt,xlab="p-value",main="Histogram of p-values under Alt")

Under the alternative hypothesis, we simulated the data so that there
WOULD be association between X and Y. As such, we expect to see strong
evidence against the null. This means many p-values will be small and
close to 0, leading to a left-ward skew.
2.3 Mixed P-values
is_null = sample(c(rep(TRUE, nsim * 0.9), rep(FALSE, nsim * 0.1)))
p_mixed = ifelse(is_null, pvec_null, pvec_alt)
#histogram
hist(p_mixed,xlab="p-value",main="Histogram of p-values under mixture of null and alt")

In the histogram above, we see a mixture between the histogram of
p-values under the null distribution and the histogram under the
alternative distribution. We would expect to see a non-uniform
distribution because some of the p-values were observed under the
alternative hypothesis, where there is an association between X and Y.
There is a greater frequency of significant p-values (less than 0.05)
compared to all other p-values, as shown by the spike on the left.
p_unif <- runif(nsim) #simulating random uniform variables
qqplot(-log10(p_unif), -log10(p_mixed),
main = "QQ Plot of P-values",
xlab = "Expected -log10(P)",
ylab = "Observed -log10(P)",
pch = 1)
abline(0, 1, col = "red", lwd = 2, lty = 2) #identity line (y=x)

Deviations from the identity line shows a departure from the expected
distribution. In other words, if the p-values are from the null
hypothesis (uniform), they should lie along the identity line. The plot
indicates a mix of null and non-null effects.The initial proximity to
the identity line demonstrates some p-values align with the null
distribution. Then, there is an upwards stretch that moves away from the
red line, indicating that other p-values are significant under the
alternative distribution.
2.4 Confusion Matrix & Reality Check
alpha = 0.05
#TN: null is true and test does not reject
TN = sum(is_null & (p_mixed >= alpha))
#FP: null is true but test rejected
FP = sum(is_null & (p_mixed < alpha))
#FN: null is false but test fails to reject
FN = sum(!is_null & (p_mixed >= alpha))
#TP: null is false and test fails to reject
TP = sum(!is_null & (p_mixed < alpha))
cat("true negatives:", TN, "\n")
true negatives: 8521
cat("false positives:", FP, "\n")
false positives: 479
cat("false negatives:", FN, "\n")
false negatives: 98
cat("true positives:", TP, "\n")
true positives: 902
This table cannot be generated in real data analysis. In a
simulation, we create the data, therefore we know the “ground truth”
(i.e. which events are TRULY significant/not significant). However, in
real data analysis, we can only observe which tests are called
significant or not based on p-values. We cannot directly determine which
hypotheses are truly null or alternative because the true state (ground
truth) of each hypothesis is unknown.
2.5 Bonferroni Correction
bonferroni <- 0.05 / nsim
cat("Bonferroni-adjusted threshold:", bonferroni, "\n")
Bonferroni-adjusted threshold: 0.000005
sig_bonferroni <- sum(p_mixed < bonferroni)
cat("Number of significant tests:", sig_bonferroni, "\n")
Number of significant tests: 83
TN_bonferroni <- sum(is_null & (p_mixed >= bonferroni))
FP_bonferroni <- sum(is_null & (p_mixed < bonferroni))
FN_bonferroni <- sum(!is_null & (p_mixed >= bonferroni))
TP_bonferroni <- sum(!is_null & (p_mixed < bonferroni))
cat("true negatives:", TN_bonferroni, "\n")
true negatives: 8999
cat("false positives:", FP_bonferroni, "\n")
false positives: 1
cat("false negatives:", FN_bonferroni, "\n")
false negatives: 918
cat("true positives:", TP_bonferroni, "\n")
true positives: 82
Without the correction, there is a higher rate of false positives in
exchange for more true positives, making it less conservative. With the
correction, the number of false positives drops dramatically from 479 to
1, indicating a much more strict threshold for calling something
significant. As such, the correction minimizes the number of false
positives but simultaneously identifies fewer true positives (902 vs
82). The trade-off with the correction is fewer false positives but also
fewer true positives.
2.6 FDR Control with qvalue
qres_mix = qvalue::qvalue(p_mixed) #calculates qvalues for pvalues in mix
qres_mix$pi0
[1] 0.8601
When looking at the pi0 in the mixed simulations, we see quite a high
proportion of true nulls. I simulated a mixture of null and alternative
distributions, with 10% alternative hypotheses (prop_alt = 0.10). This
means that 90% are null tests. 0.86 from the qvalues is close to 0.9,
which confirms that the qvalue method is accurately estimating the
proportion of true nulls.
#count how many tests are significant at an FDR threshold of 0.1
sig_tests_FDR <- sum(qres$qvalues <= 0.1)
cat("Number of significant tests at FDR threshold 0.1:", sig_tests_FDR, "\n")
Number of significant tests at FDR threshold 0.1: 867
#count how TRUE positves
sig_tests_FDR_true <- sum((qres$qvalues <= 0.1) & !is_null)
cat("Number of true positives using FDR threshold", sig_tests_FDR_true, "\n")
Number of true positives using FDR threshold 762
The number of true positives in the uncorrected is 902. With the
correction, the number of true positives is 82. The FDR threshold has
867 positives, with 762 being true positives. The number of true
positives using FDR is between that of the uncorrected and of the
Bonferroni correction. FDR balances the uncorrected with the perhaps
overly stringent threshold of the Bonferroni. FDR is useful for finding
more true positives without excessively increasing false positives (such
as in uncorrected).
The conceptual difference between FWER such as Bonferroni and FDR is
that FWER strictly controls the probability of making any false positive
errors while FDR controls the proportion of false positives but less
strictly.
LS0tCnRpdGxlOiAiSG9tZXdvcmsgMiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIDIuMSBTaW11bGF0aW9uIFJ1bgoKIyMjIyMgUGFyYW1ldGVyIERlZmluaXRpb25zCgpgYGB7cn0KIyMgc2V0IHNlZWQgdG8gbWFrZSBzaW11bGF0aW9ucyByZXByb2R1Y2libGUKc2V0LnNlZWQoMjAyMTAxMDgpCgojIyBsZXQncyBzdGFydCB3aXRoIHNvbWUgcGFyYW1ldGVyIGRlZmluaXRpb25zCm5zYW1wID0gMTAwCmJldGEgPSAxICNlZmZlY3Qgc2l6ZSAKaDIgPSAwLjEgI2hlcml0YWJpbGl0eQpzaWcyWCA9IGgyCnNpZzJlcHNpID0gKDEgLSBzaWcyWCkgKiBiZXRhXjIKc2lnWCA9IHNxcnQoc2lnMlgpICNnZW5ldGljIHN0YW5kYXJkIGRldmlhdGlvbgpzaWdlcHNpID0gc3FydChzaWcyZXBzaSkKYGBgCgpgYGB7cn0KI3NpbXVsYXRlIFgKWCA9IHJub3JtKG5zYW1wLG1lYW49MCwgc2Q9IHNpZ1gpICNTTlAKZXBzaSA9IHJub3JtKG5zYW1wLG1lYW49MCwgc2Q9c2lnZXBzaSkgI2Vudmlyb25tZW50YWwgZWZmZWN0cwoKI3NpbXVsYXRlIFludWxsClludWxsID0gcm5vcm0obnNhbXAsIG1lYW49MCwgc2Q9YmV0YSkgI3BoZW5vdHlwZQoKI3NpbXVsYXRlIFlhbHQKWWFsdCA9IFggKiBiZXRhICsgZXBzaSAjcGhlbm90eXBlCmBgYAoKIyMjIyMgRmlyc3QsIGRlZmluaW5nIGZhc3RsbSB0byByZXBlYXQgbGluZWFyIHJlZ3Jlc3Npb24gZWZmaWNpZW50bHkKCmBgYHtyfQpmYXN0bG0gPSBmdW5jdGlvbih4eCx5eSkKewogICMjIGNvbXB1dGUgYmV0YWhhdCAocmVncmVzc2lvbiBjb2VmKSBhbmQgcHZhbHVlIHdpdGggRnRlc3QKICAjIyBmb3Igbm93IGl0IGRvZXMgbm90IHRha2UgY292YXJpYXRlcwogIAogIGRmMSA9IDIKICBkZjAgPSAxCiAgaW5kID0gIWlzLm5hKHh4KSAmICFpcy5uYSh5eSkKICB4eCA9IHh4W2luZF0KICB5eSA9IHl5W2luZF0KICBuID0gc3VtKGluZCkKICB4YmFyID0gbWVhbih4eCkKICB5YmFyID0gbWVhbih5eSkKICB4eCA9IHh4IC0geGJhcgogIHl5ID0geXkgLSB5YmFyCiAgCiAgU1hYID0gc3VtKCB4eF4yICkKICBTWVkgPSBzdW0oIHl5XjIgKQogIFNYWSA9IHN1bSggeHggKiB5eSApCiAgCiAgYmV0YWhhdCA9IFNYWSAvIFNYWAogIAogIFJTUzEgPSBzdW0oICggeXkgLSB4eCAqIGJldGFoYXQgKV4yICkKICBSU1MwID0gU1lZCiAgCiAgZnN0YXQgPSAoICggUlNTMCAtIFJTUzEgKSAvICggZGYxIC0gZGYwICkgKSAgLyAoIFJTUzEgLyAoIG4gLSBkZjEgKSApCiAgcHZhbCA9IDEgLSBwZihmc3RhdCwgZGYxID0gKCBkZjEgLSBkZjAgKSwgZGYyID0gKCBuIC0gZGYxICkpCiAgcmVzID0gbGlzdChiZXRhaGF0ID0gYmV0YWhhdCwgcHZhbCA9IHB2YWwpCiAgCiAgcmV0dXJuKHJlcykKfQpgYGAKCmBgYHtyfQpuc2ltID0gMTAwMDAKIyMgc2ltdWxhdGUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgWCBhbmQgZXBzaQpYbWF0ID0gbWF0cml4KHJub3JtKG5zaW0gKiBuc2FtcCxtZWFuPTAsIHNkPSBzaWdYKSwgbnNhbXAsIG5zaW0pCmVwc2ltYXQgPSBtYXRyaXgocm5vcm0obnNpbSAqIG5zYW1wLG1lYW49MCwgc2Q9c2lnZXBzaSksIG5zYW1wLCBuc2ltKQoKIyMgZ2VuZXJhdGUgWWFsdCAoWCBoYXMgYW4gZWZmZWN0IG9uIFlhbHQpClltYXRfYWx0ID0gWG1hdCAqIGJldGEgKyBlcHNpbWF0CgojIyBnZW5lcmF0ZSBZbnVsbCAoWCBoYXMgbm8gZWZmZWN0IG9uIFludWxsKQpZbWF0X251bGwgPSBtYXRyaXgocm5vcm0obnNpbSAqIG5zYW1wLCBtZWFuPTAsIHNkPWJldGEpLCBuc2FtcCwgbnNpbSkKCiMjIGxldCdzIGxvb2sgYXQgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIHNpbXVsYXRlZCBtYXRyaWNlcwpwcmludChkaW0oWW1hdF9udWxsKSkKcHJpbnQoZGltKFltYXRfYWx0KSkKYGBgCgpUaGUgbWF0cml4IGRpbWVuc2lvbnMgZm9yIFludWxsIGFuZCBZYWx0IGlzIDEwMCB4IDEwMDAwCgpgYGB7cn0KI1J1bm5pbmcgc2ltdWxhdGlvbiAxMCwwMDAgdGltZXMgZm9yIFludWxsCnB2ZWNfbnVsbCA9IHJlcChOQSxuc2ltKSAjdmVjdG9yIGZvciBudWxsCmJ2ZWNfbnVsbCA9IHJlcChOQSxuc2ltKQoKZm9yKHNzIGluIDE6bnNpbSkKewogIGZpdCA9IGZhc3RsbShYbWF0Wyxzc10sIFltYXRfbnVsbFssc3NdKSAjbGluZWFyIHJlZ3Jlc3Npb24KICBwdmVjX251bGxbc3NdID0gZml0JHB2YWwgICNmaW5kIHAtdmFsdWVzIGFuZCBzYXZlIHRoZW0KICBidmVjX251bGxbc3NdID0gZml0JGJldGFoYXQgI2ZpbmQgY29lZmZpY2llbnRzIGFuZCBzYXZlIHRoZW0KfQoKc3VtbWFyeShwdmVjX251bGwpCmBgYAoKYGBge3J9CiNSdW5uaW5nIHNpbXVsYXRpb24gMTAsMDAwIHRpbWVzIGZvciBZYWx0CnB2ZWNfYWx0ID0gcmVwKE5BLG5zaW0pICN2ZWN0b3IgZm9yIGFsdCAKYnZlY19hbHQgPSByZXAoTkEsbnNpbSkKCmZvcihzcyBpbiAxOm5zaW0pCnsKICBmaXQgPSBmYXN0bG0oWG1hdFssc3NdLCBZbWF0X2FsdFssc3NdKSAjbGluZWFyIHJlZ3Jlc3Npb24KICBwdmVjX2FsdFtzc10gPSBmaXQkcHZhbCAgICAjZmluZCBwLXZhbHVlcyBhbmQgc2F2ZSB0aGVtCiAgYnZlY19hbHRbc3NdID0gZml0JGJldGFoYXQgI2ZpbmQgY29lZmZpY2llbnRzIGFuZCBzYXZlIHRoZW0KfQoKc3VtbWFyeShwdmVjX2FsdCkKYGBgCgojIyMgMi4yIFAtdmFsdWUgRGlzdHJpYnV0aW9ucwoKIyMjIyBEaXN0cmlidXRpb24gb2YgcC12YWx1ZXMgdW5kZXIgdGhlIE51bGwKCmBgYHtyfQpoaXN0KHB2ZWNfbnVsbCx4bGFiPSJwLXZhbHVlIixtYWluPSJIaXN0b2dyYW0gb2YgcC12YWx1ZXMgdW5kZXIgTnVsbCIpCmBgYAoKVGhlIGhpc3RvZ3JhbSBvZiB0aGUgcC12YWx1ZXMgaXMgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkLiBUaGlzIGlzIGJlY2F1c2UgaWYgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLCB0aGVuIGFsbCBwb3NzaWJsZSBwLXZhbHVlcyBhcmUgZXF1YWxseSBsaWtlbHkgYWNyb3NzIHRoZSBpbnRlcnZhbCAoMCwxKS4KCiMjIyMgRGlzdHJpYnV0aW9uIG9mIHAtdmFsdWVzIHVuZGVyIHRoZSBBbHRlcm5hdGl2ZQoKYGBge3J9Cmhpc3QocHZlY19hbHQseGxhYj0icC12YWx1ZSIsbWFpbj0iSGlzdG9ncmFtIG9mIHAtdmFsdWVzIHVuZGVyIEFsdCIpCmBgYAoKVW5kZXIgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMsIHdlIHNpbXVsYXRlZCB0aGUgZGF0YSBzbyB0aGF0IHRoZXJlIFdPVUxEIGJlIGFzc29jaWF0aW9uIGJldHdlZW4gWCBhbmQgWS4gQXMgc3VjaCwgd2UgZXhwZWN0IHRvIHNlZSBzdHJvbmcgZXZpZGVuY2UgYWdhaW5zdCB0aGUgbnVsbC4gVGhpcyBtZWFucyBtYW55IHAtdmFsdWVzIHdpbGwgYmUgc21hbGwgYW5kIGNsb3NlIHRvIDAsIGxlYWRpbmcgdG8gYSBsZWZ0LXdhcmQgc2tldy4KCiMjIyAyLjMgTWl4ZWQgUC12YWx1ZXMKCmBgYHtyfQppc19udWxsID0gc2FtcGxlKGMocmVwKFRSVUUsIG5zaW0gKiAwLjkpLCByZXAoRkFMU0UsIG5zaW0gKiAwLjEpKSkKCnBfbWl4ZWQgPSBpZmVsc2UoaXNfbnVsbCwgcHZlY19udWxsLCBwdmVjX2FsdCkKCiNoaXN0b2dyYW0KaGlzdChwX21peGVkLHhsYWI9InAtdmFsdWUiLG1haW49Ikhpc3RvZ3JhbSBvZiBwLXZhbHVlcyB1bmRlciBtaXh0dXJlIG9mIG51bGwgYW5kIGFsdCIpCmBgYAoKSW4gdGhlIGhpc3RvZ3JhbSBhYm92ZSwgd2Ugc2VlIGEgbWl4dHVyZSBiZXR3ZWVuIHRoZSBoaXN0b2dyYW0gb2YgcC12YWx1ZXMgdW5kZXIgdGhlIG51bGwgZGlzdHJpYnV0aW9uIGFuZCB0aGUgaGlzdG9ncmFtIHVuZGVyIHRoZSBhbHRlcm5hdGl2ZSBkaXN0cmlidXRpb24uIFdlIHdvdWxkIGV4cGVjdCB0byBzZWUgYSBub24tdW5pZm9ybSBkaXN0cmlidXRpb24gYmVjYXVzZSBzb21lIG9mIHRoZSBwLXZhbHVlcyB3ZXJlIG9ic2VydmVkIHVuZGVyIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzLCB3aGVyZSB0aGVyZSBpcyBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuIFggYW5kIFkuIFRoZXJlIGlzIGEgZ3JlYXRlciBmcmVxdWVuY3kgb2Ygc2lnbmlmaWNhbnQgcC12YWx1ZXMgKGxlc3MgdGhhbiAwLjA1KSBjb21wYXJlZCB0byBhbGwgb3RoZXIgcC12YWx1ZXMsIGFzIHNob3duIGJ5IHRoZSBzcGlrZSBvbiB0aGUgbGVmdC4KCmBgYHtyfQpwX3VuaWYgPC0gcnVuaWYobnNpbSkgI3NpbXVsYXRpbmcgcmFuZG9tIHVuaWZvcm0gdmFyaWFibGVzIApxcXBsb3QoLWxvZzEwKHBfdW5pZiksIC1sb2cxMChwX21peGVkKSwgCiAgICAgICBtYWluID0gIlFRIFBsb3Qgb2YgUC12YWx1ZXMiLAogICAgICAgeGxhYiA9ICJFeHBlY3RlZCAtbG9nMTAoUCkiLAogICAgICAgeWxhYiA9ICJPYnNlcnZlZCAtbG9nMTAoUCkiLAogICAgICAgcGNoID0gMSkKCmFibGluZSgwLCAxLCBjb2wgPSAicmVkIiwgbHdkID0gMiwgbHR5ID0gMikgI2lkZW50aXR5IGxpbmUgKHk9eCkKYGBgCgpEZXZpYXRpb25zIGZyb20gdGhlIGlkZW50aXR5IGxpbmUgc2hvd3MgYSBkZXBhcnR1cmUgZnJvbSB0aGUgZXhwZWN0ZWQgZGlzdHJpYnV0aW9uLiBJbiBvdGhlciB3b3JkcywgaWYgdGhlIHAtdmFsdWVzIGFyZSBmcm9tIHRoZSBudWxsIGh5cG90aGVzaXMgKHVuaWZvcm0pLCB0aGV5IHNob3VsZCBsaWUgYWxvbmcgdGhlIGlkZW50aXR5IGxpbmUuIFRoZSBwbG90IGluZGljYXRlcyBhIG1peCBvZiBudWxsIGFuZCBub24tbnVsbCBlZmZlY3RzLlRoZSBpbml0aWFsIHByb3hpbWl0eSB0byB0aGUgaWRlbnRpdHkgbGluZSBkZW1vbnN0cmF0ZXMgc29tZSBwLXZhbHVlcyBhbGlnbiB3aXRoIHRoZSBudWxsIGRpc3RyaWJ1dGlvbi4gVGhlbiwgdGhlcmUgaXMgYW4gdXB3YXJkcyBzdHJldGNoIHRoYXQgbW92ZXMgYXdheSBmcm9tIHRoZSByZWQgbGluZSwgaW5kaWNhdGluZyB0aGF0IG90aGVyIHAtdmFsdWVzIGFyZSBzaWduaWZpY2FudCB1bmRlciB0aGUgYWx0ZXJuYXRpdmUgZGlzdHJpYnV0aW9uLgoKIyMjIDIuNCBDb25mdXNpb24gTWF0cml4ICYgUmVhbGl0eSBDaGVjawoKYGBge3J9CmFscGhhID0gMC4wNQoKI1ROOiBudWxsIGlzIHRydWUgYW5kIHRlc3QgZG9lcyBub3QgcmVqZWN0ClROID0gc3VtKGlzX251bGwgJiAocF9taXhlZCA+PSBhbHBoYSkpCgojRlA6IG51bGwgaXMgdHJ1ZSBidXQgdGVzdCByZWplY3RlZApGUCA9IHN1bShpc19udWxsICYgKHBfbWl4ZWQgPCBhbHBoYSkpIAoKI0ZOOiBudWxsIGlzIGZhbHNlIGJ1dCB0ZXN0IGZhaWxzIHRvIHJlamVjdApGTiA9IHN1bSghaXNfbnVsbCAmIChwX21peGVkID49IGFscGhhKSkKCiNUUDogbnVsbCBpcyBmYWxzZSBhbmQgdGVzdCBmYWlscyB0byByZWplY3QKVFAgPSBzdW0oIWlzX251bGwgJiAocF9taXhlZCA8IGFscGhhKSkKCmNhdCgidHJ1ZSBuZWdhdGl2ZXM6IiwgVE4sICJcbiIpCmNhdCgiZmFsc2UgcG9zaXRpdmVzOiIsIEZQLCAiXG4iKQpjYXQoImZhbHNlIG5lZ2F0aXZlczoiLCBGTiwgIlxuIikKY2F0KCJ0cnVlIHBvc2l0aXZlczoiLCBUUCwgIlxuIikKYGBgCgpUaGlzIHRhYmxlIGNhbm5vdCBiZSBnZW5lcmF0ZWQgaW4gcmVhbCBkYXRhIGFuYWx5c2lzLiBJbiBhIHNpbXVsYXRpb24sIHdlIGNyZWF0ZSB0aGUgZGF0YSwgdGhlcmVmb3JlIHdlIGtub3cgdGhlICJncm91bmQgdHJ1dGgiIChpLmUuIHdoaWNoIGV2ZW50cyBhcmUgVFJVTFkgc2lnbmlmaWNhbnQvbm90IHNpZ25pZmljYW50KS4gSG93ZXZlciwgaW4gcmVhbCBkYXRhIGFuYWx5c2lzLCB3ZSBjYW4gb25seSBvYnNlcnZlIHdoaWNoIHRlc3RzIGFyZSBjYWxsZWQgc2lnbmlmaWNhbnQgb3Igbm90IGJhc2VkIG9uIHAtdmFsdWVzLiBXZSBjYW5ub3QgZGlyZWN0bHkgZGV0ZXJtaW5lIHdoaWNoIGh5cG90aGVzZXMgYXJlIHRydWx5IG51bGwgb3IgYWx0ZXJuYXRpdmUgYmVjYXVzZSB0aGUgdHJ1ZSBzdGF0ZSAoZ3JvdW5kIHRydXRoKSBvZiBlYWNoIGh5cG90aGVzaXMgaXMgdW5rbm93bi4KCiMjIyAyLjUgQm9uZmVycm9uaSBDb3JyZWN0aW9uCgpgYGB7cn0KYm9uZmVycm9uaSA8LSAwLjA1IC8gbnNpbSAKY2F0KCJCb25mZXJyb25pLWFkanVzdGVkIHRocmVzaG9sZDoiLCBib25mZXJyb25pLCAiXG4iKQpgYGAKCmBgYHtyfQpzaWdfYm9uZmVycm9uaSA8LSBzdW0ocF9taXhlZCA8IGJvbmZlcnJvbmkpCmNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IHRlc3RzOiIsIHNpZ19ib25mZXJyb25pLCAiXG4iKQpgYGAKCmBgYHtyfQpUTl9ib25mZXJyb25pIDwtIHN1bShpc19udWxsICYgKHBfbWl4ZWQgPj0gYm9uZmVycm9uaSkpIApGUF9ib25mZXJyb25pIDwtIHN1bShpc19udWxsICYgKHBfbWl4ZWQgPCBib25mZXJyb25pKSkgIApGTl9ib25mZXJyb25pIDwtIHN1bSghaXNfbnVsbCAmIChwX21peGVkID49IGJvbmZlcnJvbmkpKQpUUF9ib25mZXJyb25pIDwtIHN1bSghaXNfbnVsbCAmIChwX21peGVkIDwgYm9uZmVycm9uaSkpIAoKY2F0KCJ0cnVlIG5lZ2F0aXZlczoiLCBUTl9ib25mZXJyb25pLCAiXG4iKQpjYXQoImZhbHNlIHBvc2l0aXZlczoiLCBGUF9ib25mZXJyb25pLCAiXG4iKQpjYXQoImZhbHNlIG5lZ2F0aXZlczoiLCBGTl9ib25mZXJyb25pLCAiXG4iKQpjYXQoInRydWUgcG9zaXRpdmVzOiIsIFRQX2JvbmZlcnJvbmksICJcbiIpCmBgYAoKV2l0aG91dCB0aGUgY29ycmVjdGlvbiwgdGhlcmUgaXMgYSBoaWdoZXIgcmF0ZSBvZiBmYWxzZSBwb3NpdGl2ZXMgaW4gZXhjaGFuZ2UgZm9yIG1vcmUgdHJ1ZSBwb3NpdGl2ZXMsIG1ha2luZyBpdCBsZXNzIGNvbnNlcnZhdGl2ZS4gV2l0aCB0aGUgY29ycmVjdGlvbiwgdGhlIG51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMgZHJvcHMgZHJhbWF0aWNhbGx5IGZyb20gNDc5IHRvIDEsIGluZGljYXRpbmcgYSBtdWNoIG1vcmUgc3RyaWN0IHRocmVzaG9sZCBmb3IgY2FsbGluZyBzb21ldGhpbmcgc2lnbmlmaWNhbnQuIEFzIHN1Y2gsIHRoZSBjb3JyZWN0aW9uIG1pbmltaXplcyB0aGUgbnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcyBidXQgc2ltdWx0YW5lb3VzbHkgaWRlbnRpZmllcyBmZXdlciB0cnVlIHBvc2l0aXZlcyAoOTAyIHZzIDgyKS4gVGhlIHRyYWRlLW9mZiB3aXRoIHRoZSBjb3JyZWN0aW9uIGlzIGZld2VyIGZhbHNlIHBvc2l0aXZlcyBidXQgYWxzbyBmZXdlciB0cnVlIHBvc2l0aXZlcy4KCiMjIyAyLjYgRkRSIENvbnRyb2wgd2l0aCBxdmFsdWUKCmBgYHtyfQpxcmVzX21peCA9IHF2YWx1ZTo6cXZhbHVlKHBfbWl4ZWQpICNjYWxjdWxhdGVzIHF2YWx1ZXMgZm9yIHB2YWx1ZXMgaW4gbWl4CgpxcmVzX21peCRwaTAKYGBgCgpXaGVuIGxvb2tpbmcgYXQgdGhlIHBpMCBpbiB0aGUgbWl4ZWQgc2ltdWxhdGlvbnMsIHdlIHNlZSBxdWl0ZSBhIGhpZ2ggcHJvcG9ydGlvbiBvZiB0cnVlIG51bGxzLiBJIHNpbXVsYXRlZCBhIG1peHR1cmUgb2YgbnVsbCBhbmQgYWx0ZXJuYXRpdmUgZGlzdHJpYnV0aW9ucywgd2l0aCAxMCUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNlcyAocHJvcF9hbHQgPSAwLjEwKS4gVGhpcyBtZWFucyB0aGF0IDkwJSBhcmUgbnVsbCB0ZXN0cy4gMC44NiBmcm9tIHRoZSBxdmFsdWVzIGlzIGNsb3NlIHRvIDAuOSwgd2hpY2ggY29uZmlybXMgdGhhdCB0aGUgcXZhbHVlIG1ldGhvZCBpcyBhY2N1cmF0ZWx5IGVzdGltYXRpbmcgdGhlIHByb3BvcnRpb24gb2YgdHJ1ZSBudWxscy4KCmBgYHtyfQojY291bnQgaG93IG1hbnkgdGVzdHMgYXJlIHNpZ25pZmljYW50IGF0IGFuIEZEUiB0aHJlc2hvbGQgb2YgMC4xCnNpZ190ZXN0c19GRFIgPC0gc3VtKHFyZXMkcXZhbHVlcyA8PSAwLjEpCmNhdCgiTnVtYmVyIG9mIHNpZ25pZmljYW50IHRlc3RzIGF0IEZEUiB0aHJlc2hvbGQgMC4xOiIsIHNpZ190ZXN0c19GRFIsICJcbiIpCgojY291bnQgaG93IFRSVUUgcG9zaXR2ZXMKc2lnX3Rlc3RzX0ZEUl90cnVlIDwtIHN1bSgocXJlcyRxdmFsdWVzIDw9IDAuMSkgJiAhaXNfbnVsbCkKY2F0KCJOdW1iZXIgb2YgdHJ1ZSBwb3NpdGl2ZXMgdXNpbmcgRkRSIHRocmVzaG9sZCIsIHNpZ190ZXN0c19GRFJfdHJ1ZSwgIlxuIikKCmBgYAoKVGhlIG51bWJlciBvZiB0cnVlIHBvc2l0aXZlcyBpbiB0aGUgdW5jb3JyZWN0ZWQgaXMgOTAyLiBXaXRoIHRoZSBjb3JyZWN0aW9uLCB0aGUgbnVtYmVyIG9mIHRydWUgcG9zaXRpdmVzIGlzIDgyLiBUaGUgRkRSIHRocmVzaG9sZCBoYXMgODY3IHBvc2l0aXZlcywgd2l0aCA3NjIgYmVpbmcgdHJ1ZSBwb3NpdGl2ZXMuIFRoZSBudW1iZXIgb2YgdHJ1ZSBwb3NpdGl2ZXMgdXNpbmcgRkRSIGlzIGJldHdlZW4gdGhhdCBvZiB0aGUgdW5jb3JyZWN0ZWQgYW5kIG9mIHRoZSBCb25mZXJyb25pIGNvcnJlY3Rpb24uIEZEUiBiYWxhbmNlcyB0aGUgdW5jb3JyZWN0ZWQgd2l0aCB0aGUgcGVyaGFwcyBvdmVybHkgc3RyaW5nZW50IHRocmVzaG9sZCBvZiB0aGUgQm9uZmVycm9uaS4gRkRSIGlzIHVzZWZ1bCBmb3IgZmluZGluZyBtb3JlIHRydWUgcG9zaXRpdmVzIHdpdGhvdXQgZXhjZXNzaXZlbHkgaW5jcmVhc2luZyBmYWxzZSBwb3NpdGl2ZXMgKHN1Y2ggYXMgaW4gdW5jb3JyZWN0ZWQpLgoKVGhlIGNvbmNlcHR1YWwgZGlmZmVyZW5jZSBiZXR3ZWVuIEZXRVIgc3VjaCBhcyBCb25mZXJyb25pIGFuZCBGRFIgaXMgdGhhdCBGV0VSIHN0cmljdGx5IGNvbnRyb2xzIHRoZSBwcm9iYWJpbGl0eSBvZiBtYWtpbmcgYW55IGZhbHNlIHBvc2l0aXZlIGVycm9ycyB3aGlsZSBGRFIgY29udHJvbHMgdGhlIHByb3BvcnRpb24gb2YgZmFsc2UgcG9zaXRpdmVzIGJ1dCBsZXNzIHN0cmljdGx5Lgo=