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=