Change log


## install packages with:
# if (!requireNamespace("remotes", quietly = TRUE)) {
#     install.packages("remotes")
# }
# remotes::install_github("statOmics/HDDAData")

library(HDDAData)

1 Introduction

In this lab session we will look at the following topics

  • FWER
  • FDR
  • Multiple testing problem in a real dataset

1.1 Testing many hypotheses

To demonstrate the ideas we will be working with, we will simulate artificial data. Note that since we are doing simulations, we can control everything and also know exactly what the underlying “true” distribution is (sometimes also called the “ground truth”). However, keep in mind that this is an unlikely representation of real-world data.

In particular, we will simulate data for multiple hypothesis tests where the null hypothesis is always true. I.e. for the \(i\)th test, we assume that \(\mu_{1i}\) and \(\mu_{2i}\) represent the means of the two populations of interest, the null hypothesis for comparing the two means is \(H_{0i} : \mu_{1i} = \mu_{2i}\). Let the alternative hypothesis of this test be \(H_{1i} : \mu_{1i} \neq \mu_{2i}\), i.e. we perform a two-tailed test. Suppose we have data collected from both populations, given by \(X_{1i}\) and \(X_{2i}\) of size \(n_{1i}\) and \(n_{2i}\), respectively, and assume that both populations have the same known variance \(\sigma_i^2\). Then we can test this hypothesis by using Z-scores, given by

\[ Z_i=\frac{\bar X_{1i}-\bar X_{2i}}{\sigma_i\sqrt{1/n_{1i}+1/n_{2i}}} \]

Under the null hypothesis, the scores will be distributed as a standard normal \(Z_i\sim N(0,1)\). The null hypothesis is rejected in favor of the alternative if \(z_i < \phi_{\alpha_i/2}\) or \(z_i > \phi_{1-\alpha_i/2}\), or equivalently if \(|z_i| > \phi_{1-\alpha_i/2}\), where \(\phi_{\alpha_i/2}\) is the \(\alpha_i/2\)th quantile of the standard normal distribution.

In a multiple hypothesis testing setting, we will perform \(m\) tests using the same test statistic. If the null were true for all hypotheses, we would end up with a sample \(z_1,\ldots,z_m\) from a standard normal distribution.

In this setting, we can only make one type of error: wrongly rejecting the null hypothesis, i.e. a type 1 error. The probability of making this error is given by

\[ \alpha_i=\text{P}\{\text{ reject } H_{0i} | H_{0i}\} \]

(the \(| H_{0i}\) part should be read as “given that the null hypothesis is true”).

If we now perform \(m_0\) such tests (using the 0 subscript to denote that they are all hypotheses for which the null is true), we can summarise the possible outcomes as in the table below:

Accept.H_0 Reject.H_0 Total
Null True U (True Negative) V (False Positive) m_0

Here, \(U\) and \(V\) represent the total number of true negative and false positive results we get, respectively, out of \(m_0\) total tests.

As an example, let’s simulate the scenario above. In the code below, the replicate function is used to repeat the same procedure a number of times. Essentially, the following steps are performed :

  1. Sample \(N = 12\) observations for 2 hypothetical groups from normal distributions with the same mean and known variance
  2. Calculate Z-scores based on the 2 group means
  3. Repeat steps 1 to 2 m0 times

This mimics performing m0 hypothesis tests on data for which we know the null hypothesis is true.

For simplicity we assume the variance to be known and equal to 1 for both groups. We simulate 10 observations for each group and calculate the denominator for the Z-scores since it’s the same for each test.

## Set parameters for the simulation
N <- 10 # samples per group
m0 <- 1000 # number of hypothesis tests
mu_1 <- 3 # true mean group 1
mu_2 <- 3 # true mean group 2
sigma <- 1 # known variance
denom <- sigma * sqrt(2 / N) # denominator for Z-scores

set.seed(123) # seed for reproducibility
null_z_scores <- replicate(m0, {
  group1 <- rnorm(N, mean = mu_1, sd = sqrt(sigma))
  group2 <- rnorm(N, mean = mu_2, sd = sqrt(sigma))

  ## Calculate Z-score
  (mean(group2) - mean(group1)) / denom
})

## Visualize Z-scores
hist(null_z_scores, breaks = 50, freq = FALSE)
## Overlay theoretical standard normal
lines(x <- seq(-5, 5, length.out = 100), dnorm(x), col = "dodgerblue", lwd = 3)
## Draw vertical lines at 2.5 and 97.5th percentiles
abline(v = qnorm(c(0.025, 0.975)), col = "firebrick", lty = 2, lwd = 3)

We see that the Z-scores are nicely distributed as a standard normal. The vertical dashed lines indicate the 2.5th and 97.5th percentiles of the standard normal. The regions outside these lines indicate the Z-scores that we would call significant if we used a cut-off of \(\alpha = 0.05\) for a two-tailed test. So, even though we simulated data under the null hypothesis, our Z-test still returns “significant” results for a number of cases just by chance!

Let’s calculate the p-values for our hypothesis tests and see what the damage is. To calculate the p-values, we use the pnorm() function in this case, which returns the value of the standard normal CDF (i.e. pnorm(x) = \(P(Z < x)\)). Since we consider a two-tailed test, we take the absolute values of the Z-scores and set the lower.tail argument in pnorm to FALSE (by default it’s TRUE), so that we get pnorm(abs(x), lower.tail = FALSE) = \(P(Z > |x|)\) and multiply this value by 2.

null_pvals <- 2 * pnorm(abs(null_z_scores), lower.tail = FALSE)

alpha <- 0.05 # significance cutoff

hist(null_pvals, breaks = seq(0, 1, by = 0.05))
abline(v = alpha, col = "firebrick", lwd = 3)


called <- (null_pvals < alpha)

## V = number of false positives, in this case: all significant tests
(V <- sum(called))
#> [1] 48
mean(called) # V / m0
#> [1] 0.048

So we get 48 significant tests (false positives) out of a total of 1000, which is, unsurprisingly, approximately equal to our significance cutoff \(\alpha\). Note also that the p-values are uniformly distibuted under the null hypothesis.

If we had carried out only a few tests (say 10) it would be very unlikely to observe a false positive (on average: 0.05 * 10 = 0.5 false positives) at \(\alpha = 0.05\), but since now we’re carrying out so many, we’re almost guaranteed to get false positives. This is what is known as the multiple hypothesis testing problem.

Note that in real-world data the quantities \(U\) and \(V\) are unknown (because we don’t know the truth! If we did, we wouldn’t have to carry out any hypothesis tests in the first place). However, by using simulated data, we do know the truth and so we can explore these quantities.

1.2 The family-wise error rate

If we carry out many tests, we’re almost guaranteed to get type I errors, just by chance. Therefore the type I error rate is no longer a relevant metric. Instead, we consider the Familywise Error Rate (FWER), given by

\[ \text{FWER}=\text{P}\{V > 0\} = \text{P}\{\text{rejecting at least one } H_{0i}| H_0\} \]

where \(H_0\) is the intersection of all partial nulls (\(H_{0i}\)) \(i=1,\ldots,m_0\). In general, we would prefer testing procedures that keep the FWER under control, i.e. correct for the multiplicity problem. So instead of choosing \(\alpha\) to control the probability of getting a false positive in each test, we try to control the FWER, i.e. the probability of getting at least one false positive in our set of hypothesis tests.

In the following exercises, we will explore this idea on some synthetic data.

Exercises: FWER null data simulation

We will simulate performing hypothesis tests for which the null distribution is always true for different values of m0.

To make the code more simple, we will directly sample the Z-score statistics from a standard normal (instead of sampling the individual groups and then performing the test on them, as shown above). We can also skip the p-value calculation and compare our Z-scores directly to the \((1 - \alpha) / 2\)th quantile of the standard normal distribution (using the qnorm() function).

Tasks

1. Set m0 = 5 and generate m0 Z-score statistics by sampling from the standard normal distribution. Check which ones are significant using a cutoff alpha = 0.05

To check significance you can use abs(z_score) > qnorm(1 - alpha / 2)

2. Calculate \(V\), the number of significant tests.

Note that to count a logical vector, you can just use sum on it, since TRUE is numerically interpreted as 1 and FALSE as 0.

3. Check that V > 0 (at least one significant) and store the result in a variable (e.g. at_least_one_signif <- V > 0).

4. Repeat steps 1-3 1000 times and keep track of the result in step 3 by storing it in a vector.

You can either use a for loop or replicate. If using a for loop, make sure to create an empty vector of the desired length first! Growing a vector is technically possible but is very inefficient. You can initialize an empty (logical) vector of a certain length with vec <- logical(length = x)

5. Now compute the FWER as the proportion of times \(V\) was greater than 0.

6. Repeat the same procedure for m0 = 50 and m0 = 1000. Interpret the results.

Solutions

Solution

I combined the exercises above into one code block, to avoid repeated code. Essentially, I wrapped the simulation in a for loop that goes over the different values of m0.

## Repeat 1000 times for m0 = 5, 50 and 1000, directly simulate test statistic
## record minimal p-value and check if smaller than alpha
set.seed(1)
B <- 1000 # number of simulations
m0_vec <- c(5, 50, 1000)
alpha <- 0.05
## Initialize empty vector to store FWER results
fwer <- numeric(length = length(m0_vec))
names(fwer) <- paste("m0 =", m0_vec)

## Loop over m0 values and simulate test statistics
for (i in seq_along(m0_vec)) {
  ## Simulate B times, each time recording V > 0
  at_least_one_signif <- replicate(B, {
    null_stats <- rnorm(m0_vec[i], mean = 0, sd = 1) # z-score distribution
    tests <- abs(null_stats) > qnorm(1 - alpha / 2)
    sum(tests) > 0
  })
  fwer[i] <- mean(at_least_one_signif) # proportion of V >= 1
}
fwer
#>    m0 = 5   m0 = 50 m0 = 1000 
#>     0.269     0.931     1.000

2 Controlling the FWER

We saw in the previous exercises that when conducting a large number of hypothesis tests the FWER becomes unacceptably large. So what can we do to lower it? The most straightforward way would be to just lower \(\alpha\) (so we get less false positives).

Note that we can write the FWER under the null hypothesis (so the p-values are uniformly distributed) as

\[ \begin{align} \text{FWER} = P(\text{at least one rejection}) &= 1 - P(\text{no rejections}) \\ &= 1 - \prod_{i=1}^{m} P(p_i > \alpha) \\ &= 1 - \prod_{i=1}^{m} (1 - \alpha) \\ &= 1 - (1 - \alpha)^{m} \end{align} \]

where \(p_i\) is the p-value for the \(i\)th test.

If we wanted to control the FWER at a certain level (e.g. 0.05), we can simply solve the equation above for \(\alpha\) and get, for a given FWER:

\[ \alpha = 1 - (1 - \text{FWER})^{1/m} \]

Exercise

Using your simulation code from before, confirm that when setting; alpha = 0.0102 and m0 = 5, alpha = 0.00102 and m0 = 50, and finally alpha = 0.000051292 and m0 = 1000 all result in an FWER of approximately 0.05.

Solution
## Repeat 1000 times for m0 = 5, 50 and 1000, directly simulate test statistic
## record minimal p-value and check if smaller than alpha
set.seed(1)
B <- 1000 # number of simulations
m0_vec <- c(5, 50, 1000)
alpha_vec <- c(0.0102, 0.00102, 0.000051292)
## Initialize empty vector to store FWER results
fwer <- numeric(length = length(m0_vec))
names(fwer) <- paste("m0 =", m0_vec)

## Loop over m0 values and simulate test statistics
for (i in seq_along(m0_vec)) {
  ## Simulate B times, each time recording V > 0
  at_least_one_signif <- replicate(B, {
    null_stats <- rnorm(m0_vec[i], mean = 0, sd = 1) # z-score distribution
    tests <- abs(null_stats) > qnorm(1 - alpha_vec[i] / 2)
    sum(tests) > 0
  })
  fwer[i] <- mean(at_least_one_signif) # proportion of V >= 1
}
fwer
#>    m0 = 5   m0 = 50 m0 = 1000 
#>     0.057     0.045     0.057

The procedure described above is also known as Sidak’s procedure. Another widely used technique to control the FWER is the Bonferroni correction. Note, however that in general, controlling the FWER at levels such as 0.05 is very conservative, i.e. if there were ture alternative cases we might miss them.

3 The False Discovery Rate (FDR)

Now let’s consider a situation where we have both true null and true alternative cases. In this situation we are prone to two types of errors: false positives (type 1, as in the previous case) and false negatives (type 2, i.e. accepting \(H_0\) while it’s actually false).

We can then extend the table from before:

Accept.H_0 Reject.H_0 Total
Null True U (True Negative) V (False Positive) m_0
Alternative True T (False Negative) S (True Positive m_1
Total W R m

In addition to the FWER, another important concept for multiple hypothesis testing is the False Discovery Rate (FDR):

\[ \text{FDR} = E[V/R] \]

i.e. the expected proportion of false positives among all positive tests. The ratio \(V/R\) is also called the False Discovery Proportion (FDP).

However, the only quantities from the table above that are observable in real data, are \(W\), \(R\) and \(m\). So to illustrate the concept, we will again make use of simulated data.

In the code below, 1.000 tests are simulated, for which 90% come from cases where the null is true and the other 10% for which the alternative is true. We assume the effect size (difference between the 2 groups) to be equal to 3 under the alternative, so that we can sample the test statistics from a normal distribution with mean 3.

m <- 1000 # total number of hypotheses
p0 <- 0.9 # 90% of cases are true null
m0 <- round(p0 * m) # round to avoid floating point problems
m1 <- round((1 - p0) * m)

set.seed(1)
alpha <- 0.05
null_stats <- rnorm(m0)
alt_stats <- rnorm(m1, mean = 3)
null_tests <- abs(null_stats) > qnorm(1 - alpha / 2)
alt_tests <- abs(alt_stats) > qnorm(1 - alpha / 2)

(U <- sum(!null_tests)) # true negatives
#> [1] 842
(V <- sum(null_tests)) # false positives
#> [1] 58
(S <- sum(alt_tests)) # true positives
#> [1] 87
(T <- sum(!alt_tests)) # false negatives
#> [1] 13

R <- V + S # total number of positives

(FDP <- V / R)
#> [1] 0.4

We see that 40% of the positive cases are actually false positives. To get an idea of the FDR, the expected FDP, we will repeat this procedure a number of times in the following exercises.

4 Exercises: FDR simulations

Tasks

1. Set m0 = 90 and m1 = 10 and generate m = 100 test statistics, m0 of them from the standard normal distribution (N(0, 1)) (null distribution) and m1 of them from N(3, 1) (the alternative distribution).

2. Test for significance at alpha = 0.05 by comparing the test statistics to the standard normal, for both sets of tests.

3. Calculate \(V\), \(S\), \(R\) and the FDP.

4. Repeat steps 1-3 1000 times and keep track of the FDP for each iteration. Then compute the FDR as the mean of the FDPs. Interpret the results.

Solutions

Solution
set.seed(1)
B <- 1000 # number of simulations
m0 <- 90
m1 <- 10
alpha <- 0.05

## Simulate B times, each time recording V > 0
FDP <- replicate(B, {
  null_stats <- rnorm(m0, mean = 0)
  alt_stats <- rnorm(m1, mean = 3)
  null_tests <- abs(null_stats) > qnorm(1 - alpha / 2)
  alt_tests <- abs(alt_stats) > qnorm(1 - alpha / 2)
  V <- sum(null_tests) # false positives
  S <- sum(alt_tests) # true positives
  R <- V + S
  ## Return V / R
  V / R
})

(FDR <- mean(FDP))
#> [1] 0.3426611

The FDR tells us the expected proportion of positive tests that will be false positives.

As with the FWER, we can now think of methods to control the FDR. A very popular method that is widely used in large-scale inference for high-throughput sequencing data is the Benjamini-Hochberg correction. The details of this technique lie outside the scope of this exercise session, but essentially it adjusts the p-values such that the FDR is controlled at a certain level. Note that this is a somewhat different approach then the FWER-controlling techniques, where significance cut-off \(\alpha\) was adjusted instead of the p-values themselves.

The Benjamini-Hochberg (and other techniques) are readily available in base R in the p.adjust() function (see ?p.adjust for details). You just supply it a vector of p-values and the desired method (for Benjamini-Hochberg, use method = "BH").

5 Exercises: real data (Alon et al. (1999))

We will take another look at the dataset by Alon et al. (1999) on gene expression levels in 40 tumour and 22 normal colon tissue samples. We used this data also in Lab 4. However, this time we’re interested in finding genes that expressed significantly different between the tumor and normal samples. In other words, we will perform hypothesis tests for each of the 2000 genes. This is clearly a multiple testing problem.

Tasks

1. Load the data

Just run the code given below.

data("Alon1999")
str(Alon1999[, 1:10]) # first 10 columns
#> 'data.frame':    62 obs. of  10 variables:
#>  $ Y : chr  "t" "n" "t" "n" ...
#>  $ X1: num  8589 9164 3826 6246 3230 ...
#>  $ X2: num  5468 6720 6970 7824 3694 ...
#>  $ X3: num  4263 4883 5370 5956 3401 ...
#>  $ X4: num  4065 3718 4706 3976 3464 ...
#>  $ X5: num  1998 2015 1167 2003 2181 ...
#>  $ X6: num  5282 5570 1572 2131 2923 ...
#>  $ X7: num  2170 3849 1325 1531 2069 ...
#>  $ X8: num  2773 2793 1472 1715 2949 ...
#>  $ X9: num  7526 7018 3297 3870 3303 ...
table(Alon1999$Y)  # contingency table of tissue types
#> 
#>  n  t 
#> 22 40

As a reminder, the data consists of gene expression levels in 40 tumor and 22 normal colon tissue samples. The expression on 6500 human genes were measured using the Affymetrix oligonucleotide array. As in Alon et al. (1999), we use the 2000 genes with the highest minimal intensity across the samples.

The dataset contains one variable named Y with the values t and n. This variable indicates whether the sample came from tumourous (t) or normal (n) tissue.

2. Perform 2-sample t-tests for each gene, comparing the tumor and normal tissue groups

Use the t.test() function in base R. By default, this will compute a two-sample t-test with unequal variances (Welch’s t-test). You can supply a formula (using the ~) to t.test() similar to lm().

Use either a for loop to iterate over all genes or the apply() function.

Store the test statistic, p-value and degrees of freedom of each test in a matrix (each row should be a gene, 1 column containing the test statistcs 1 column the p-values and 1 for the degrees of freedom). You can find these values in the t.test() result under the $p.value, $statistic and $parameter slots.

Solution

In the solution below, I used the apply function to loop over the genes, essentially this is similar to a for loop but is a bit more succinct to write and takes care of pre-allocating memory instead of having te set up empty matrices first manually. Setting the second argument (MARGIN) to 2 tells apply to loop over the columns of the input matrix instead of the rows. For details, see ?apply. Briefly, apply() takes a matrix, margin and function as input arguments, it then applies the function to each column or row (depending on the margin) of the matrix. The results are either a vector if each function call returns a single value or a matrix if a vector is returned at each call. Note that apply will return the resulting matrix with the calculated values in the rows. As it makes more sense to have these in columns, the matrix is transposed.

## Split gene data and grouping variable and convert to matrix
gene_data <- as.matrix(Alon1999[, -1])
group <- Alon1999$Y

## Use `apply` to loop over columns of gene data and perform t-tests, extracting
## p-values, test stastistics, and degrees of freedom
ttest_results <- t(apply(gene_data, 2, function(x) {
  t_test <- t.test(x ~ group)
  p_val <- t_test$p.value
  stat <- t_test$statistic
  df <- t_test$parameter
  ## Return values in named vector
  c(stat, "p_val" = p_val, df)
}))

## Take a look at results
head(ttest_results)
#>             t      p_val       df
#> X1 -1.6728613 0.10068961 49.30072
#> X2 -1.4319967 0.15787615 54.30090
#> X3 -2.1292624 0.03758201 56.77667
#> X4 -0.9455604 0.34869002 52.67760
#> X5 -1.7852252 0.07953894 57.07236
#> X6 -1.4214810 0.16143092 49.62772

3. Plot a histogram of the p-values and interpret

Solution

Setting breaks from 0 to 1 with a width of 0.05 makes sense for p-values because the first bar will then represent the number of p-values smaller than 0.05 (which we often use as cutoff).

p_vals <- ttest_results[, "p_val"]
hist(
  p_vals,
  breaks = seq(0, 1, by = 0.05), main = "", xlab = "p-value",
  ylim = c(0, 500)
)

4. How many discoveries / significant genes do you find when using \(\alpha = 0.05\)?

Solution
alpha <- 0.05
sum(p_vals < alpha)
#> [1] 478

5. Correct for the multiplicity problem by controlling the FWER at 5%

  • What should we use as \(\alpha\)?

  • How many discoveries are left over when using the FWER-adjusted \(\alpha\)?

Solution

Remember that \(\alpha\) can be calculated for a given FWER as

\[ \alpha = 1 - (1 - \text{FWER})^{1/m} \]

m <- nrow(ttest_results) # number of tests performed
fwer <- 0.05
adj_alpha <- 1 - (1 - fwer)^(1/m)

## Number of significant discoveries at FWER 5%
sum(p_vals < adj_alpha)
#> [1] 11

Note that you can also use the Bonferroni correction to get a similar result. This is also implemented in the p.adjust function by using method = "bonferroni". Essentially, the Bonferroni correction sets a new \(\alpha_m = \alpha / m\) by dividing the original with the number of tests performed \(m\). So the result is slightly different than the procedure given above (which is known as Sidak’s procedure but is less commonly used). The p.adjust function returns adjusted p-values, since modifying the cut-off \(\alpha\) by dividing with \(m\) is the same as multiplying the p-values with \(m\) and then using the original cut-off.

bonf_pvals <- p.adjust(p_vals, method = "bonferroni")
sum(bonf_pvals < alpha)
#> [1] 11

## Note that bonferroni correction is simply multiplying the original p-values
## with m (and cutting off any values bigger than 1)
idx <- which(bonf_pvals < 1)
head(bonf_pvals[idx])
#>        X15        X26        X31        X43        X47        X62 
#> 0.77687260 0.08333083 0.22133271 0.03115825 0.11433842 0.07860578
head(p_vals[idx] * m)
#>        X15        X26        X31        X43        X47        X62 
#> 0.77687260 0.08333083 0.22133271 0.03115825 0.11433842 0.07860578

6. Use the FDR method to adjust the original p-values

  • Use the p.adjust() function with method = "BH"

  • Plot the adjusted p-values against the originals, what is the effect of the adjustment?

  • How many discoveries are left when controlling the FDR at 5%?

Solution
fdr <- p.adjust(p_vals, method = "BH")

plot(
  p_vals[order(p_vals)], fdr[order(p_vals)],
  pch = 19, cex = 0.6, xlab = "p-value", ylab = "Adjusted p-value", col = 4
)
points(
  p_vals[order(p_vals)], bonf_pvals[order(p_vals)],
  pch = 19, cex = 0.6, col = 2
)
abline(a = 0, b = 1)
legend("bottomright",
  c("Benjamini-Hochberg", "Bonferroni"),
  col = c(4, 2), pch = 19
)


sum(fdr < 0.05)
#> [1] 190

7. Compare the FWER and FDR corrections. Which one is more conservative?

Further reading

Session info

Session info
#> [1] "2024-10-07 12:42:40 CEST"
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.4.0 RC (2024-04-16 r86468)
#>  os       macOS Big Sur 11.6
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       Europe/Brussels
#>  date     2024-10-07
#>  pandoc   3.1.1 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version date (UTC) lib source
#>  bookdown      0.40    2024-07-02 [1] CRAN (R 4.4.0)
#>  bslib         0.8.0   2024-07-29 [1] CRAN (R 4.4.0)
#>  cachem        1.1.0   2024-05-16 [1] CRAN (R 4.4.0)
#>  cli           3.6.3   2024-06-21 [1] CRAN (R 4.4.0)
#>  digest        0.6.37  2024-08-19 [1] CRAN (R 4.4.1)
#>  evaluate      1.0.0   2024-09-17 [1] CRAN (R 4.4.1)
#>  fastmap       1.2.0   2024-05-15 [1] CRAN (R 4.4.0)
#>  HDDAData    * 1.0.1   2024-10-02 [1] Github (statOmics/HDDAData@b832c71)
#>  highr         0.11    2024-05-26 [1] CRAN (R 4.4.0)
#>  htmltools     0.5.8.1 2024-04-04 [1] CRAN (R 4.4.0)
#>  jquerylib     0.1.4   2021-04-26 [1] CRAN (R 4.4.0)
#>  jsonlite      1.8.9   2024-09-20 [1] CRAN (R 4.4.1)
#>  knitr         1.48    2024-07-07 [1] CRAN (R 4.4.0)
#>  lifecycle     1.0.4   2023-11-07 [1] CRAN (R 4.4.0)
#>  R6            2.5.1   2021-08-19 [1] CRAN (R 4.4.0)
#>  rlang         1.1.4   2024-06-04 [1] CRAN (R 4.4.0)
#>  rmarkdown     2.28    2024-08-17 [1] CRAN (R 4.4.0)
#>  rstudioapi    0.16.0  2024-03-24 [1] CRAN (R 4.4.0)
#>  sass          0.4.9   2024-03-15 [1] CRAN (R 4.4.0)
#>  sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.4.0)
#>  xfun          0.47    2024-08-17 [1] CRAN (R 4.4.0)
#>  yaml          2.3.10  2024-07-26 [1] CRAN (R 4.4.0)
#> 
#>  [1] /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────

References

Alon, Uri, Naama Barkai, Daniel A Notterman, Kurt Gish, Suzanne Ybarra, Daniel Mack, and Arnold J Levine. 1999. “Broad Patterns of Gene Expression Revealed by Clustering Analysis of Tumor and Normal Colon Tissues Probed by Oligonucleotide Arrays.” Proceedings of the National Academy of Sciences 96 (12): 6745–50.
LS0tCnRpdGxlOiAiTGFiIDY6IExhcmdlIFNjYWxlIEluZmVyZW5jZSIKc3VidGl0bGU6ICJIaWdoIERpbWVuc2lvbmFsIERhdGEgQW5hbHlzaXMgcHJhY3RpY2FscyIKYXV0aG9yOiAiQWRhcHRlZCBieSBNaWxhbiBNYWxmYWl0IgpkYXRlOiAiMjEgRmViIDIwMjIgPGJyLz4gKExhc3QgdXBkYXRlZDogMjAyMi0wMi0yMSkiCnJlZmVyZW5jZXM6Ci0gaWQ6IGFsb24xOTk5YnJvYWQKICB0eXBlOiBhcnRpY2xlLWpvdXJuYWwKICBhdXRob3I6CiAgLSBmYW1pbHk6IEFsb24KICAgIGdpdmVuOiBVcmkKICAtIGZhbWlseTogQmFya2FpCiAgICBnaXZlbjogTmFhbWEKICAtIGZhbWlseTogTm90dGVybWFuCiAgICBnaXZlbjogRGFuaWVsIEEKICAtIGZhbWlseTogR2lzaAogICAgZ2l2ZW46IEt1cnQKICAtIGZhbWlseTogWWJhcnJhCiAgICBnaXZlbjogU3V6YW5uZQogIC0gZmFtaWx5OiBNYWNrCiAgICBnaXZlbjogRGFuaWVsCiAgLSBmYW1pbHk6IExldmluZQogICAgZ2l2ZW46IEFybm9sZCBKCiAgaXNzdWVkOgogIC0geWVhcjogMTk5OQogIHRpdGxlOiBCcm9hZCBwYXR0ZXJucyBvZiBnZW5lIGV4cHJlc3Npb24gcmV2ZWFsZWQgYnkgY2x1c3RlcmluZyBhbmFseXNpcyBvZiB0dW1vcgogICAgYW5kIG5vcm1hbCBjb2xvbiB0aXNzdWVzIHByb2JlZCBieSBvbGlnb251Y2xlb3RpZGUgYXJyYXlzCiAgY29udGFpbmVyLXRpdGxlOiBQcm9jZWVkaW5ncyBvZiB0aGUgTmF0aW9uYWwgQWNhZGVteSBvZiBTY2llbmNlcwogIHB1Ymxpc2hlcjogTmF0aW9uYWwgQWNhZCBTY2llbmNlcwogIHBhZ2U6IDY3NDUtNjc1MAogIHZvbHVtZTogJzk2JwogIGlzc3VlOiAnMTInCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIGNhY2hlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgY29sbGFwc2UgPSBUUlVFLAogIGNvbW1lbnQgPSAiIz4iLAogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLAogIGZpZy53aWR0aCA9IDgsCiAgZmlnLmFzcCA9IDAuNjE4LAogIG91dC53aWR0aCA9ICIxMDAlIgopCmBgYAoKIyMjIFtDaGFuZ2UgbG9nXShodHRwczovL2dpdGh1Yi5jb20vc3RhdE9taWNzL0hEREEvY29tbWl0cy9tYXN0ZXIvTGFiNS1MYXJnZS1TY2FsZS1JbmZlcmVuY2UuUm1kKSB7LX0KCioqKgoKYGBge3IgbGlicmFyaWVzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIyBpbnN0YWxsIHBhY2thZ2VzIHdpdGg6CiMgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJyZW1vdGVzIiwgcXVpZXRseSA9IFRSVUUpKSB7CiMgICAgIGluc3RhbGwucGFja2FnZXMoInJlbW90ZXMiKQojIH0KIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigic3RhdE9taWNzL0hEREFEYXRhIikKCmxpYnJhcnkoSEREQURhdGEpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCioqSW4gdGhpcyBsYWIgc2Vzc2lvbiB3ZSB3aWxsIGxvb2sgYXQgdGhlIGZvbGxvd2luZyB0b3BpY3MqKgoKICAtIEZXRVIKICAtIEZEUgogIC0gTXVsdGlwbGUgdGVzdGluZyBwcm9ibGVtIGluIGEgcmVhbCBkYXRhc2V0CgoKIyMgVGVzdGluZyBtYW55IGh5cG90aGVzZXMgeyN0ZXN0aW5nLWh5cG90aGVzZXN9CgpUbyBkZW1vbnN0cmF0ZSB0aGUgaWRlYXMgd2Ugd2lsbCBiZSB3b3JraW5nIHdpdGgsIHdlIHdpbGwgc2ltdWxhdGUgYXJ0aWZpY2lhbCBkYXRhLgpOb3RlIHRoYXQgc2luY2Ugd2UgYXJlIGRvaW5nIHNpbXVsYXRpb25zLCB3ZSBjYW4gY29udHJvbCBldmVyeXRoaW5nIGFuZCBhbHNvIGtub3cKZXhhY3RseSB3aGF0IHRoZSB1bmRlcmx5aW5nICJ0cnVlIiBkaXN0cmlidXRpb24gaXMgKHNvbWV0aW1lcyBhbHNvIGNhbGxlZCB0aGUgImdyb3VuZCB0cnV0aCIpLgpIb3dldmVyLCBrZWVwIGluIG1pbmQgdGhhdCB0aGlzIGlzIGFuIHVubGlrZWx5IHJlcHJlc2VudGF0aW9uIG9mIHJlYWwtd29ybGQgZGF0YS4KCkluIHBhcnRpY3VsYXIsIHdlIHdpbGwgc2ltdWxhdGUgZGF0YSBmb3IgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0cyB3aGVyZQpfX3RoZSBudWxsIGh5cG90aGVzaXMgaXMgYWx3YXlzIHRydWVfXy4gSS5lLiBmb3IgdGhlICRpJHRoIHRlc3QsIHdlIGFzc3VtZSB0aGF0CiRcbXVfezFpfSQgYW5kICRcbXVfezJpfSQgcmVwcmVzZW50IHRoZSBtZWFucyBvZiB0aGUgdHdvIHBvcHVsYXRpb25zIG9mIGludGVyZXN0LAp0aGUgbnVsbCBoeXBvdGhlc2lzIGZvciBjb21wYXJpbmcgdGhlIHR3byBtZWFucyBpcwokSF97MGl9IDogXG11X3sxaX0gPSBcbXVfezJpfSQuIExldCB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyBvZiB0aGlzIHRlc3QgYmUKJEhfezFpfSA6IFxtdV97MWl9IFxuZXEgXG11X3syaX0kLCBpLmUuIHdlIHBlcmZvcm0gYSAqdHdvLXRhaWxlZCogdGVzdC4KU3VwcG9zZSB3ZSBoYXZlIGRhdGEgY29sbGVjdGVkIGZyb20gYm90aCBwb3B1bGF0aW9ucywgZ2l2ZW4gYnkgJFhfezFpfSQgYW5kICRYX3syaX0kCm9mIHNpemUgJG5fezFpfSQgYW5kICRuX3syaX0kLCByZXNwZWN0aXZlbHksIGFuZCBhc3N1bWUgdGhhdCBib3RoIHBvcHVsYXRpb25zCmhhdmUgdGhlIHNhbWUga25vd24gdmFyaWFuY2UgJFxzaWdtYV9pXjIkLiBUaGVuIHdlIGNhbiB0ZXN0IHRoaXMgaHlwb3RoZXNpcyBieSB1c2luZwpfX1otc2NvcmVzX18sIGdpdmVuIGJ5CgokJCBaX2k9XGZyYWN7XGJhciBYX3sxaX0tXGJhciBYX3syaX19e1xzaWdtYV9pXHNxcnR7MS9uX3sxaX0rMS9uX3syaX19fSAkJAoKVW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcywgdGhlIHNjb3JlcyB3aWxsIGJlIGRpc3RyaWJ1dGVkIGFzIGEgKnN0YW5kYXJkIG5vcm1hbCoKJFpfaVxzaW0gTigwLDEpJC4gVGhlIG51bGwgaHlwb3RoZXNpcyBpcyByZWplY3RlZCBpbiBmYXZvciBvZiB0aGUgYWx0ZXJuYXRpdmUgaWYKJHpfaSA8IFxwaGlfe1xhbHBoYV9pLzJ9JCBvciAkel9pID4gXHBoaV97MS1cYWxwaGFfaS8yfSQsIG9yIGVxdWl2YWxlbnRseSBpZgokfHpfaXwgPiBccGhpX3sxLVxhbHBoYV9pLzJ9JCwgd2hlcmUgJFxwaGlfe1xhbHBoYV9pLzJ9JCBpcyB0aGUgJFxhbHBoYV9pLzIkdGgKcXVhbnRpbGUgb2YgdGhlIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24uCgpJbiBhIG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdGluZyBzZXR0aW5nLCB3ZSB3aWxsIHBlcmZvcm0gJG0kIHRlc3RzIHVzaW5nIHRoZQpzYW1lIHRlc3Qgc3RhdGlzdGljLiBJZiB0aGUgKm51bGwqIHdlcmUgdHJ1ZSBmb3IgYWxsIGh5cG90aGVzZXMsIHdlIHdvdWxkIGVuZCB1cAp3aXRoIGEgc2FtcGxlICR6XzEsXGxkb3RzLHpfbSQgZnJvbSBhIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24uCgpJbiB0aGlzIHNldHRpbmcsIHdlIGNhbiBvbmx5IG1ha2Ugb25lIHR5cGUgb2YgZXJyb3I6IHdyb25nbHkgcmVqZWN0aW5nIHRoZSBudWxsCmh5cG90aGVzaXMsIGkuZS4gYSBfX3R5cGUgMSBlcnJvcl9fLiBUaGUgcHJvYmFiaWxpdHkgb2YgbWFraW5nIHRoaXMgZXJyb3IgaXMgZ2l2ZW4KYnkKCiQkIFxhbHBoYV9pPVx0ZXh0e1B9XHtcdGV4dHsgcmVqZWN0IH0gSF97MGl9IHwgSF97MGl9XH0gJCQKCih0aGUgJHwgSF97MGl9JCBwYXJ0IHNob3VsZCBiZSByZWFkIGFzICJnaXZlbiB0aGF0IHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZSIpLgoKSWYgd2Ugbm93IHBlcmZvcm0gJG1fMCQgc3VjaCB0ZXN0cyAodXNpbmcgdGhlIDAgc3Vic2NyaXB0IHRvIGRlbm90ZSB0aGF0IHRoZXkKYXJlIGFsbCBoeXBvdGhlc2VzIGZvciB3aGljaCB0aGUgKm51bGwqIGlzIHRydWUpLCB3ZSBjYW4gc3VtbWFyaXNlIHRoZSBwb3NzaWJsZQpvdXRjb21lcyBhcyBpbiB0aGUgdGFibGUgYmVsb3c6CgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmthYmxlKAogIGRhdGEuZnJhbWUoCiAgICAiQWNjZXB0IEhfMCIgPSBjKCJVIChUcnVlIE5lZ2F0aXZlKSIsICIiKSwKICAgICJSZWplY3QgSF8wIiA9IGMoIlYgKEZhbHNlIFBvc2l0aXZlKSIsICIiKSwKICAgICJUb3RhbCIgPSBjKCJtXzAiLCAiIiksCiAgICByb3cubmFtZXMgPSBjKCJOdWxsIFRydWUiLCAiIikKICApCikKYGBgCgoKSGVyZSwgJFUkIGFuZCAkViQgcmVwcmVzZW50IHRoZSB0b3RhbCBudW1iZXIgb2YgdHJ1ZSBuZWdhdGl2ZSBhbmQgZmFsc2UgcG9zaXRpdmUKcmVzdWx0cyB3ZSBnZXQsIHJlc3BlY3RpdmVseSwgb3V0IG9mICRtXzAkIHRvdGFsIHRlc3RzLgoKQXMgYW4gZXhhbXBsZSwgbGV0J3Mgc2ltdWxhdGUgdGhlIHNjZW5hcmlvIGFib3ZlLiBJbiB0aGUgY29kZSBiZWxvdywgdGhlIGByZXBsaWNhdGVgCmZ1bmN0aW9uIGlzIHVzZWQgdG8gcmVwZWF0IHRoZSBzYW1lIHByb2NlZHVyZSBhIG51bWJlciBvZiB0aW1lcy4KRXNzZW50aWFsbHksIHRoZSBmb2xsb3dpbmcgc3RlcHMgYXJlIHBlcmZvcm1lZCA6CgoxLiBTYW1wbGUgJE4gPSAxMiQgb2JzZXJ2YXRpb25zIGZvciAyIGh5cG90aGV0aWNhbCBncm91cHMgZnJvbSBub3JtYWwgZGlzdHJpYnV0aW9ucwogICB3aXRoIHRoZSBzYW1lIG1lYW4gYW5kIGtub3duIHZhcmlhbmNlCjIuIENhbGN1bGF0ZSBaLXNjb3JlcyBiYXNlZCBvbiB0aGUgMiBncm91cCBtZWFucwozLiBSZXBlYXQgc3RlcHMgMSB0byAyIGBtMGAgdGltZXMKClRoaXMgbWltaWNzIHBlcmZvcm1pbmcgYG0wYCBoeXBvdGhlc2lzIHRlc3RzIG9uIGRhdGEgZm9yIHdoaWNoIHdlIGtub3cgdGhlIG51bGwKaHlwb3RoZXNpcyBpcyB0cnVlLgoKRm9yIHNpbXBsaWNpdHkgd2UgYXNzdW1lIHRoZSB2YXJpYW5jZSB0byBiZSBrbm93biBhbmQgZXF1YWwgdG8gMSBmb3IgYm90aCBncm91cHMuCldlIHNpbXVsYXRlIDEwIG9ic2VydmF0aW9ucyBmb3IgZWFjaCBncm91cCBhbmQgY2FsY3VsYXRlIHRoZSBkZW5vbWluYXRvciBmb3IgdGhlClotc2NvcmVzIHNpbmNlIGl0J3MgdGhlIHNhbWUgZm9yIGVhY2ggdGVzdC4KCmBgYHtyfQojIyBTZXQgcGFyYW1ldGVycyBmb3IgdGhlIHNpbXVsYXRpb24KTiA8LSAxMCAjIHNhbXBsZXMgcGVyIGdyb3VwCm0wIDwtIDEwMDAgIyBudW1iZXIgb2YgaHlwb3RoZXNpcyB0ZXN0cwptdV8xIDwtIDMgIyB0cnVlIG1lYW4gZ3JvdXAgMQptdV8yIDwtIDMgIyB0cnVlIG1lYW4gZ3JvdXAgMgpzaWdtYSA8LSAxICMga25vd24gdmFyaWFuY2UKZGVub20gPC0gc2lnbWEgKiBzcXJ0KDIgLyBOKSAjIGRlbm9taW5hdG9yIGZvciBaLXNjb3JlcwoKc2V0LnNlZWQoMTIzKSAjIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpudWxsX3pfc2NvcmVzIDwtIHJlcGxpY2F0ZShtMCwgewogIGdyb3VwMSA8LSBybm9ybShOLCBtZWFuID0gbXVfMSwgc2QgPSBzcXJ0KHNpZ21hKSkKICBncm91cDIgPC0gcm5vcm0oTiwgbWVhbiA9IG11XzIsIHNkID0gc3FydChzaWdtYSkpCgogICMjIENhbGN1bGF0ZSBaLXNjb3JlCiAgKG1lYW4oZ3JvdXAyKSAtIG1lYW4oZ3JvdXAxKSkgLyBkZW5vbQp9KQoKIyMgVmlzdWFsaXplIFotc2NvcmVzCmhpc3QobnVsbF96X3Njb3JlcywgYnJlYWtzID0gNTAsIGZyZXEgPSBGQUxTRSkKIyMgT3ZlcmxheSB0aGVvcmV0aWNhbCBzdGFuZGFyZCBub3JtYWwKbGluZXMoeCA8LSBzZXEoLTUsIDUsIGxlbmd0aC5vdXQgPSAxMDApLCBkbm9ybSh4KSwgY29sID0gImRvZGdlcmJsdWUiLCBsd2QgPSAzKQojIyBEcmF3IHZlcnRpY2FsIGxpbmVzIGF0IDIuNSBhbmQgOTcuNXRoIHBlcmNlbnRpbGVzCmFibGluZSh2ID0gcW5vcm0oYygwLjAyNSwgMC45NzUpKSwgY29sID0gImZpcmVicmljayIsIGx0eSA9IDIsIGx3ZCA9IDMpCmBgYAoKV2Ugc2VlIHRoYXQgdGhlIFotc2NvcmVzIGFyZSBuaWNlbHkgZGlzdHJpYnV0ZWQgYXMgYSBzdGFuZGFyZCBub3JtYWwuClRoZSB2ZXJ0aWNhbCBkYXNoZWQgbGluZXMgaW5kaWNhdGUgdGhlIDIuNXRoIGFuZCA5Ny41dGggcGVyY2VudGlsZXMgb2YgdGhlIHN0YW5kYXJkCm5vcm1hbC4gVGhlIHJlZ2lvbnMgb3V0c2lkZSB0aGVzZSBsaW5lcyBpbmRpY2F0ZSB0aGUgWi1zY29yZXMgdGhhdCB3ZSB3b3VsZCBjYWxsCnNpZ25pZmljYW50IGlmIHdlIHVzZWQgYSBjdXQtb2ZmIG9mICRcYWxwaGEgPSAwLjA1JCBmb3IgYSAqdHdvLXRhaWxlZCogdGVzdC4KU28sIGV2ZW4gdGhvdWdoIHdlIHNpbXVsYXRlZCBkYXRhIHVuZGVyIHRoZSBudWxsIGh5cG90aGVzaXMsIG91ciBaLXRlc3Qgc3RpbGwKcmV0dXJucyAic2lnbmlmaWNhbnQiIHJlc3VsdHMgZm9yIGEgbnVtYmVyIG9mIGNhc2VzIGp1c3QgYnkgY2hhbmNlIQoKTGV0J3MgY2FsY3VsYXRlIHRoZSBwLXZhbHVlcyBmb3Igb3VyIGh5cG90aGVzaXMgdGVzdHMgYW5kIHNlZSB3aGF0IHRoZSBkYW1hZ2UgaXMuClRvIGNhbGN1bGF0ZSB0aGUgcC12YWx1ZXMsIHdlIHVzZSB0aGUgYHBub3JtKClgIGZ1bmN0aW9uIGluIHRoaXMgY2FzZSwgd2hpY2ggcmV0dXJucwp0aGUgdmFsdWUgb2YgdGhlIHN0YW5kYXJkIG5vcm1hbCBDREYgKGkuZS4gYHBub3JtKHgpYCA9ICRQKFogPCB4KSQpLiBTaW5jZSB3ZSBjb25zaWRlcgphIHR3by10YWlsZWQgdGVzdCwgd2UgdGFrZSB0aGUgYWJzb2x1dGUgdmFsdWVzIG9mIHRoZSBaLXNjb3JlcyBhbmQgc2V0IHRoZSBgbG93ZXIudGFpbGAKYXJndW1lbnQgaW4gYHBub3JtYCB0byBgRkFMU0VgIChieSBkZWZhdWx0IGl0J3MgYFRSVUVgKSwgc28gdGhhdCB3ZSBnZXQKYHBub3JtKGFicyh4KSwgbG93ZXIudGFpbCA9IEZBTFNFKWAgPSAkUChaID4gfHh8KSQgYW5kIG11bHRpcGx5IHRoaXMgdmFsdWUgYnkgMi4KCmBgYHtyfQpudWxsX3B2YWxzIDwtIDIgKiBwbm9ybShhYnMobnVsbF96X3Njb3JlcyksIGxvd2VyLnRhaWwgPSBGQUxTRSkKCmFscGhhIDwtIDAuMDUgIyBzaWduaWZpY2FuY2UgY3V0b2ZmCgpoaXN0KG51bGxfcHZhbHMsIGJyZWFrcyA9IHNlcSgwLCAxLCBieSA9IDAuMDUpKQphYmxpbmUodiA9IGFscGhhLCBjb2wgPSAiZmlyZWJyaWNrIiwgbHdkID0gMykKCmNhbGxlZCA8LSAobnVsbF9wdmFscyA8IGFscGhhKQoKIyMgViA9IG51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMsIGluIHRoaXMgY2FzZTogYWxsIHNpZ25pZmljYW50IHRlc3RzCihWIDwtIHN1bShjYWxsZWQpKQptZWFuKGNhbGxlZCkgIyBWIC8gbTAKYGBgCgpTbyB3ZSBnZXQgNDggc2lnbmlmaWNhbnQgdGVzdHMgKGZhbHNlIHBvc2l0aXZlcykgb3V0IG9mIGEgdG90YWwgb2YgMTAwMCwgd2hpY2ggaXMsCnVuc3VycHJpc2luZ2x5LCBhcHByb3hpbWF0ZWx5IGVxdWFsIHRvIG91ciBzaWduaWZpY2FuY2UgY3V0b2ZmICRcYWxwaGEkLgpOb3RlIGFsc28gdGhhdCB0aGUgcC12YWx1ZXMgYXJlIHVuaWZvcm1seSBkaXN0aWJ1dGVkIHVuZGVyIHRoZSBudWxsIGh5cG90aGVzaXMuCgpJZiB3ZSBoYWQgY2FycmllZCBvdXQgb25seSBhIGZldyB0ZXN0cyAoc2F5IDEwKSBpdCB3b3VsZCBiZSB2ZXJ5IHVubGlrZWx5IHRvIG9ic2VydmUKYSBmYWxzZSBwb3NpdGl2ZSAob24gYXZlcmFnZTogMC4wNSAqIDEwID0gMC41IGZhbHNlIHBvc2l0aXZlcykgYXQgJFxhbHBoYSA9IDAuMDUkLApidXQgc2luY2Ugbm93IHdlJ3JlIGNhcnJ5aW5nIG91dCBzbyBtYW55LCB3ZSdyZSBhbG1vc3QgZ3VhcmFudGVlZCB0byBnZXQgZmFsc2UgcG9zaXRpdmVzLgpUaGlzIGlzIHdoYXQgaXMga25vd24gYXMgdGhlIF9fbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nIHByb2JsZW1fXy4KCk5vdGUgdGhhdCBpbiByZWFsLXdvcmxkIGRhdGEgdGhlIHF1YW50aXRpZXMgJFUkIGFuZCAkViQgYXJlICp1bmtub3duKiAoYmVjYXVzZQp3ZSBkb24ndCBrbm93IHRoZSB0cnV0aCEgSWYgd2UgZGlkLCB3ZSB3b3VsZG4ndCBoYXZlIHRvIGNhcnJ5IG91dCBhbnkgaHlwb3RoZXNpcwp0ZXN0cyBpbiB0aGUgZmlyc3QgcGxhY2UpLiBIb3dldmVyLCBieSB1c2luZyBzaW11bGF0ZWQgZGF0YSwgd2UgZG8ga25vdyB0aGUgdHJ1dGgKYW5kIHNvIHdlIGNhbiBleHBsb3JlIHRoZXNlIHF1YW50aXRpZXMuCgoKIyMgVGhlIGZhbWlseS13aXNlIGVycm9yIHJhdGUKCklmIHdlIGNhcnJ5IG91dCBtYW55IHRlc3RzLCB3ZSdyZSBhbG1vc3QgZ3VhcmFudGVlZCB0byBnZXQgdHlwZSBJIGVycm9ycywganVzdCBieQpjaGFuY2UuIFRoZXJlZm9yZSB0aGUgdHlwZSBJIGVycm9yIHJhdGUgaXMgbm8gbG9uZ2VyIGEgcmVsZXZhbnQgbWV0cmljLgpJbnN0ZWFkLCB3ZSBjb25zaWRlciB0aGUgX19GYW1pbHl3aXNlIEVycm9yIFJhdGUgKEZXRVIpX18sIGdpdmVuIGJ5CgokJApcdGV4dHtGV0VSfT1cdGV4dHtQfVx7ViA+IDBcfQogID0gXHRleHR7UH1ce1x0ZXh0e3JlamVjdGluZyBhdCBsZWFzdCBvbmUgfSBIX3swaX18IEhfMFx9CiQkCgp3aGVyZSAkSF8wJCBpcyB0aGUgaW50ZXJzZWN0aW9uIG9mIGFsbCAgcGFydGlhbCBudWxscyAoJEhfezBpfSQpICRpPTEsXGxkb3RzLG1fMCQuCkluIGdlbmVyYWwsIHdlIHdvdWxkIHByZWZlciB0ZXN0aW5nIHByb2NlZHVyZXMgdGhhdCBrZWVwIHRoZSBGV0VSIHVuZGVyIGNvbnRyb2wsCmkuZS4gY29ycmVjdCBmb3IgdGhlIG11bHRpcGxpY2l0eSBwcm9ibGVtLgpTbyBpbnN0ZWFkIG9mIGNob29zaW5nICRcYWxwaGEkIHRvIGNvbnRyb2wgdGhlIHByb2JhYmlsaXR5IG9mIGdldHRpbmcgYSBmYWxzZSBwb3NpdGl2ZQppbiBlYWNoIHRlc3QsIHdlIHRyeSB0byBjb250cm9sIHRoZSBGV0VSLCBpLmUuIHRoZSBwcm9iYWJpbGl0eSBvZiBnZXR0aW5nCiphdCBsZWFzdCogb25lIGZhbHNlIHBvc2l0aXZlIGluIG91ciAqc2V0KiBvZiBoeXBvdGhlc2lzIHRlc3RzLgoKSW4gdGhlIGZvbGxvd2luZyBleGVyY2lzZXMsIHdlIHdpbGwgZXhwbG9yZSB0aGlzIGlkZWEgb24gc29tZSBzeW50aGV0aWMgZGF0YS4KCgoKIyBFeGVyY2lzZXM6IEZXRVIgbnVsbCBkYXRhIHNpbXVsYXRpb24gey19CgpXZSB3aWxsIHNpbXVsYXRlIHBlcmZvcm1pbmcgaHlwb3RoZXNpcyB0ZXN0cyBmb3Igd2hpY2ggdGhlIG51bGwgZGlzdHJpYnV0aW9uIGlzCmFsd2F5cyB0cnVlIGZvciBkaWZmZXJlbnQgdmFsdWVzIG9mIGBtMGAuCgpUbyBtYWtlIHRoZSBjb2RlIG1vcmUgc2ltcGxlLCB3ZSB3aWxsIGRpcmVjdGx5IHNhbXBsZSB0aGUgWi1zY29yZSBzdGF0aXN0aWNzIGZyb20KYSBzdGFuZGFyZCBub3JtYWwgKGluc3RlYWQgb2Ygc2FtcGxpbmcgdGhlIGluZGl2aWR1YWwgZ3JvdXBzIGFuZCB0aGVuIHBlcmZvcm1pbmcKdGhlIHRlc3Qgb24gdGhlbSwgYXMgc2hvd24gYWJvdmUpLiBXZSBjYW4gYWxzbyBza2lwIHRoZSBwLXZhbHVlIGNhbGN1bGF0aW9uIGFuZApjb21wYXJlIG91ciBaLXNjb3JlcyBkaXJlY3RseSB0byB0aGUgJCgxIC0gXGFscGhhKSAvIDIkdGggcXVhbnRpbGUgb2YgdGhlIHN0YW5kYXJkCm5vcm1hbCBkaXN0cmlidXRpb24gKHVzaW5nIHRoZSBgcW5vcm0oKWAgZnVuY3Rpb24pLgoKCiMjIyBUYXNrcyB7LX0KCiMjIyMgMS4gU2V0IGBtMCA9IDVgIGFuZCBnZW5lcmF0ZSBgbTBgIFotc2NvcmUgc3RhdGlzdGljcyBieSBzYW1wbGluZyBmcm9tIHRoZSBzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uLiBDaGVjayB3aGljaCBvbmVzIGFyZSBzaWduaWZpY2FudCB1c2luZyBhIGN1dG9mZiBgYWxwaGEgPSAwLjA1YCB7LX0KClRvIGNoZWNrIHNpZ25pZmljYW5jZSB5b3UgY2FuIHVzZSBgYWJzKHpfc2NvcmUpID4gcW5vcm0oMSAtIGFscGhhIC8gMilgCgoKIyMjIyAyLiBDYWxjdWxhdGUgJFYkLCB0aGUgbnVtYmVyIG9mIHNpZ25pZmljYW50IHRlc3RzLiB7LX0KCk5vdGUgdGhhdCB0byBjb3VudCBhIGxvZ2ljYWwgdmVjdG9yLCB5b3UgY2FuIGp1c3QgdXNlIGBzdW1gIG9uIGl0LCBzaW5jZSBgVFJVRWAKaXMgbnVtZXJpY2FsbHkgaW50ZXJwcmV0ZWQgYXMgMSBhbmQgYEZBTFNFYCBhcyAwLgoKCiMjIyMgMy4gQ2hlY2sgdGhhdCBgViA+IDBgIChhdCBsZWFzdCBvbmUgc2lnbmlmaWNhbnQpIGFuZCBzdG9yZSB0aGUgcmVzdWx0IGluIGEgdmFyaWFibGUgKGUuZy4gYGF0X2xlYXN0X29uZV9zaWduaWYgPC0gViA+IDBgKS4gey19CgoKIyMjIyA0LiBSZXBlYXQgc3RlcHMgMS0zIDEwMDAgdGltZXMgYW5kIGtlZXAgdHJhY2sgb2YgdGhlIHJlc3VsdCBpbiBzdGVwIDMgYnkgc3RvcmluZyBpdCBpbiBhIHZlY3Rvci4gey19CgpZb3UgY2FuIGVpdGhlciB1c2UgYSBgZm9yYCBsb29wIG9yIGByZXBsaWNhdGVgLiBJZiB1c2luZyBhIGBmb3JgIGxvb3AsIG1ha2Ugc3VyZSB0bwpjcmVhdGUgYW4gZW1wdHkgdmVjdG9yIG9mIHRoZSBkZXNpcmVkIGxlbmd0aCBmaXJzdCEgR3Jvd2luZyBhIHZlY3RvciBpcyB0ZWNobmljYWxseQpwb3NzaWJsZSBidXQgaXMgdmVyeSBpbmVmZmljaWVudC4gWW91IGNhbiBpbml0aWFsaXplIGFuIGVtcHR5IChsb2dpY2FsKSB2ZWN0b3Igb2YKYSBjZXJ0YWluIGxlbmd0aCB3aXRoIGB2ZWMgPC0gbG9naWNhbChsZW5ndGggPSB4KWAKCgojIyMjIDUuIE5vdyBjb21wdXRlIHRoZSBGV0VSIGFzIHRoZSBwcm9wb3J0aW9uIG9mIHRpbWVzICRWJCB3YXMgZ3JlYXRlciB0aGFuIDAuIHstfQoKCiMjIyMgNi4gUmVwZWF0IHRoZSBzYW1lIHByb2NlZHVyZSBmb3IgYG0wID0gNTBgIGFuZCBgbTAgPSAxMDAwYC4gSW50ZXJwcmV0IHRoZSByZXN1bHRzLiB7LX0KCgojIyMgU29sdXRpb25zIHstfQoKPGRldGFpbHM+PHN1bW1hcnk+U29sdXRpb248L3N1bW1hcnk+CgpJIGNvbWJpbmVkIHRoZSBleGVyY2lzZXMgYWJvdmUgaW50byBvbmUgY29kZSBibG9jaywgdG8gYXZvaWQgcmVwZWF0ZWQgY29kZS4KRXNzZW50aWFsbHksIEkgd3JhcHBlZCB0aGUgc2ltdWxhdGlvbiBpbiBhIGBmb3JgIGxvb3AgdGhhdCBnb2VzIG92ZXIgdGhlIGRpZmZlcmVudAp2YWx1ZXMgb2YgYG0wYC4KCmBgYHtyfQojIyBSZXBlYXQgMTAwMCB0aW1lcyBmb3IgbTAgPSA1LCA1MCBhbmQgMTAwMCwgZGlyZWN0bHkgc2ltdWxhdGUgdGVzdCBzdGF0aXN0aWMKIyMgcmVjb3JkIG1pbmltYWwgcC12YWx1ZSBhbmQgY2hlY2sgaWYgc21hbGxlciB0aGFuIGFscGhhCnNldC5zZWVkKDEpCkIgPC0gMTAwMCAjIG51bWJlciBvZiBzaW11bGF0aW9ucwptMF92ZWMgPC0gYyg1LCA1MCwgMTAwMCkKYWxwaGEgPC0gMC4wNQojIyBJbml0aWFsaXplIGVtcHR5IHZlY3RvciB0byBzdG9yZSBGV0VSIHJlc3VsdHMKZndlciA8LSBudW1lcmljKGxlbmd0aCA9IGxlbmd0aChtMF92ZWMpKQpuYW1lcyhmd2VyKSA8LSBwYXN0ZSgibTAgPSIsIG0wX3ZlYykKCiMjIExvb3Agb3ZlciBtMCB2YWx1ZXMgYW5kIHNpbXVsYXRlIHRlc3Qgc3RhdGlzdGljcwpmb3IgKGkgaW4gc2VxX2Fsb25nKG0wX3ZlYykpIHsKICAjIyBTaW11bGF0ZSBCIHRpbWVzLCBlYWNoIHRpbWUgcmVjb3JkaW5nIFYgPiAwCiAgYXRfbGVhc3Rfb25lX3NpZ25pZiA8LSByZXBsaWNhdGUoQiwgewogICAgbnVsbF9zdGF0cyA8LSBybm9ybShtMF92ZWNbaV0sIG1lYW4gPSAwLCBzZCA9IDEpICMgei1zY29yZSBkaXN0cmlidXRpb24KICAgIHRlc3RzIDwtIGFicyhudWxsX3N0YXRzKSA+IHFub3JtKDEgLSBhbHBoYSAvIDIpCiAgICBzdW0odGVzdHMpID4gMAogIH0pCiAgZndlcltpXSA8LSBtZWFuKGF0X2xlYXN0X29uZV9zaWduaWYpICMgcHJvcG9ydGlvbiBvZiBWID49IDEKfQpmd2VyCmBgYAoKPC9kZXRhaWxzPgoKCgojIENvbnRyb2xsaW5nIHRoZSBGV0VSCgpXZSBzYXcgaW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlcyB0aGF0IHdoZW4gY29uZHVjdGluZyBhIGxhcmdlIG51bWJlciBvZiBoeXBvdGhlc2lzCnRlc3RzIHRoZSBGV0VSIGJlY29tZXMgdW5hY2NlcHRhYmx5IGxhcmdlLiBTbyB3aGF0IGNhbiB3ZSBkbyB0byBsb3dlciBpdD8KVGhlIG1vc3Qgc3RyYWlnaHRmb3J3YXJkIHdheSB3b3VsZCBiZSB0byBqdXN0IGxvd2VyICRcYWxwaGEkIChzbyB3ZSBnZXQgbGVzcyBmYWxzZSBwb3NpdGl2ZXMpLgoKTm90ZSB0aGF0IHdlIGNhbiB3cml0ZSB0aGUgRldFUiB1bmRlciB0aGUgbnVsbCBoeXBvdGhlc2lzIChzbyB0aGUgcC12YWx1ZXMgYXJlIHVuaWZvcm1seSBkaXN0cmlidXRlZCkgYXMKCiQkClxiZWdpbnthbGlnbn0KXHRleHR7RldFUn0gPSBQKFx0ZXh0e2F0IGxlYXN0IG9uZSByZWplY3Rpb259KSAmPSAxIC0gUChcdGV4dHtubyByZWplY3Rpb25zfSkgXFwKICAmPSAxIC0gXHByb2Rfe2k9MX1ee219IFAocF9pID4gXGFscGhhKSBcXAogICY9IDEgLSBccHJvZF97aT0xfV57bX0gKDEgLSBcYWxwaGEpIFxcCiAgJj0gMSAtICgxIC0gXGFscGhhKV57bX0KXGVuZHthbGlnbn0KJCQKCndoZXJlICRwX2kkIGlzIHRoZSBwLXZhbHVlIGZvciB0aGUgJGkkdGggdGVzdC4KCklmIHdlIHdhbnRlZCB0byBjb250cm9sIHRoZSBGV0VSIGF0IGEgY2VydGFpbiBsZXZlbCAoZS5nLiAwLjA1KSwgd2UgY2FuIHNpbXBseSBzb2x2ZQp0aGUgZXF1YXRpb24gYWJvdmUgZm9yICRcYWxwaGEkIGFuZCBnZXQsIGZvciBhIGdpdmVuIEZXRVI6CgokJCBcYWxwaGEgPSAxIC0gKDEgLSBcdGV4dHtGV0VSfSleezEvbX0gJCQKCgojIyBFeGVyY2lzZSB7LX0KClVzaW5nIHlvdXIgc2ltdWxhdGlvbiBjb2RlIGZyb20gYmVmb3JlLCBjb25maXJtIHRoYXQgd2hlbiBzZXR0aW5nOyBgYWxwaGEgPSAwLjAxMDJgCmFuZCBgbTAgPSA1YCwgYGFscGhhID0gMC4wMDEwMmAgYW5kIGBtMCA9IDUwYCwgYW5kIGZpbmFsbHkgYGFscGhhID0gMC4wMDAwNTEyOTJgCmFuZCBgbTAgPSAxMDAwYCBhbGwgcmVzdWx0IGluIGFuIEZXRVIgb2YgYXBwcm94aW1hdGVseSAwLjA1LgoKPGRldGFpbHM+PHN1bW1hcnk+U29sdXRpb248L3N1bW1hcnk+CgpgYGB7cn0KIyMgUmVwZWF0IDEwMDAgdGltZXMgZm9yIG0wID0gNSwgNTAgYW5kIDEwMDAsIGRpcmVjdGx5IHNpbXVsYXRlIHRlc3Qgc3RhdGlzdGljCiMjIHJlY29yZCBtaW5pbWFsIHAtdmFsdWUgYW5kIGNoZWNrIGlmIHNtYWxsZXIgdGhhbiBhbHBoYQpzZXQuc2VlZCgxKQpCIDwtIDEwMDAgIyBudW1iZXIgb2Ygc2ltdWxhdGlvbnMKbTBfdmVjIDwtIGMoNSwgNTAsIDEwMDApCmFscGhhX3ZlYyA8LSBjKDAuMDEwMiwgMC4wMDEwMiwgMC4wMDAwNTEyOTIpCiMjIEluaXRpYWxpemUgZW1wdHkgdmVjdG9yIHRvIHN0b3JlIEZXRVIgcmVzdWx0cwpmd2VyIDwtIG51bWVyaWMobGVuZ3RoID0gbGVuZ3RoKG0wX3ZlYykpCm5hbWVzKGZ3ZXIpIDwtIHBhc3RlKCJtMCA9IiwgbTBfdmVjKQoKIyMgTG9vcCBvdmVyIG0wIHZhbHVlcyBhbmQgc2ltdWxhdGUgdGVzdCBzdGF0aXN0aWNzCmZvciAoaSBpbiBzZXFfYWxvbmcobTBfdmVjKSkgewogICMjIFNpbXVsYXRlIEIgdGltZXMsIGVhY2ggdGltZSByZWNvcmRpbmcgViA+IDAKICBhdF9sZWFzdF9vbmVfc2lnbmlmIDwtIHJlcGxpY2F0ZShCLCB7CiAgICBudWxsX3N0YXRzIDwtIHJub3JtKG0wX3ZlY1tpXSwgbWVhbiA9IDAsIHNkID0gMSkgIyB6LXNjb3JlIGRpc3RyaWJ1dGlvbgogICAgdGVzdHMgPC0gYWJzKG51bGxfc3RhdHMpID4gcW5vcm0oMSAtIGFscGhhX3ZlY1tpXSAvIDIpCiAgICBzdW0odGVzdHMpID4gMAogIH0pCiAgZndlcltpXSA8LSBtZWFuKGF0X2xlYXN0X29uZV9zaWduaWYpICMgcHJvcG9ydGlvbiBvZiBWID49IDEKfQpmd2VyCmBgYAoKPC9kZXRhaWxzPgoKVGhlIHByb2NlZHVyZSBkZXNjcmliZWQgYWJvdmUgaXMgYWxzbyBrbm93biBhcyBTaWRhaydzIHByb2NlZHVyZS4KQW5vdGhlciB3aWRlbHkgdXNlZCB0ZWNobmlxdWUgdG8gY29udHJvbCB0aGUgRldFUiBpcyB0aGUgQm9uZmVycm9uaSBjb3JyZWN0aW9uLgpOb3RlLCBob3dldmVyIHRoYXQgaW4gZ2VuZXJhbCwgY29udHJvbGxpbmcgdGhlIEZXRVIgYXQgbGV2ZWxzIHN1Y2ggYXMgMC4wNSBpcyB2ZXJ5CmNvbnNlcnZhdGl2ZSwgaS5lLiBpZiB0aGVyZSB3ZXJlIHR1cmUgYWx0ZXJuYXRpdmUgY2FzZXMgd2UgbWlnaHQgbWlzcyB0aGVtLgoKCiMgVGhlIEZhbHNlIERpc2NvdmVyeSBSYXRlIChGRFIpCgpOb3cgbGV0J3MgY29uc2lkZXIgYSBzaXR1YXRpb24gd2hlcmUgd2UgaGF2ZSBib3RoIHRydWUgbnVsbCBhbmQgdHJ1ZSBhbHRlcm5hdGl2ZQpjYXNlcy4gSW4gdGhpcyBzaXR1YXRpb24gd2UgYXJlIHByb25lIHRvIHR3byB0eXBlcyBvZiBlcnJvcnM6IGZhbHNlIHBvc2l0aXZlcwoodHlwZSAxLCBhcyBpbiB0aGUgcHJldmlvdXMgY2FzZSkgYW5kIGZhbHNlIG5lZ2F0aXZlcyAodHlwZSAyLCBpLmUuIGFjY2VwdGluZwokSF8wJCB3aGlsZSBpdCdzIGFjdHVhbGx5IGZhbHNlKS4KCldlIGNhbiB0aGVuIGV4dGVuZCB0aGUgdGFibGUgZnJvbSBbYmVmb3JlXSgjdGVzdGluZy1oeXBvdGhlc2VzKToKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6a2FibGUoCiAgZGF0YS5mcmFtZSgKICAgICJBY2NlcHQgSF8wIiA9IGMoIlUgKFRydWUgTmVnYXRpdmUpIiwgIlQgKEZhbHNlIE5lZ2F0aXZlKSIsICJXIiksCiAgICAiUmVqZWN0IEhfMCIgPSBjKCJWIChGYWxzZSBQb3NpdGl2ZSkiLCAiUyAoVHJ1ZSBQb3NpdGl2ZSIsICJSIiksCiAgICAiVG90YWwiID0gYygibV8wIiwgIm1fMSIsICJtIiksCiAgICByb3cubmFtZXMgPSBjKCJOdWxsIFRydWUiLCAiQWx0ZXJuYXRpdmUgVHJ1ZSIsICJUb3RhbCIpCiAgKQopCmBgYAoKSW4gYWRkaXRpb24gdG8gdGhlIEZXRVIsIGFub3RoZXIgaW1wb3J0YW50IGNvbmNlcHQgZm9yIG11bHRpcGxlIGh5cG90aGVzaXMKdGVzdGluZyBpcyB0aGUgX19GYWxzZSBEaXNjb3ZlcnkgUmF0ZSAoRkRSKV9fOgoKJCQKXHRleHR7RkRSfSA9IEVbVi9SXQokJAoKaS5lLiB0aGUgZXhwZWN0ZWQgcHJvcG9ydGlvbiBvZiBmYWxzZSBwb3NpdGl2ZXMgYW1vbmcgYWxsIHBvc2l0aXZlIHRlc3RzLgpUaGUgcmF0aW8gJFYvUiQgaXMgYWxzbyBjYWxsZWQgdGhlIEZhbHNlIERpc2NvdmVyeSBQcm9wb3J0aW9uIChGRFApLgoKSG93ZXZlciwgdGhlIG9ubHkgcXVhbnRpdGllcyBmcm9tIHRoZSB0YWJsZSBhYm92ZSB0aGF0IGFyZSBvYnNlcnZhYmxlCmluIHJlYWwgZGF0YSwgYXJlICRXJCwgJFIkIGFuZCAkbSQuIFNvIHRvIGlsbHVzdHJhdGUgdGhlIGNvbmNlcHQsIHdlIHdpbGwgYWdhaW4KbWFrZSB1c2Ugb2Ygc2ltdWxhdGVkIGRhdGEuCgpJbiB0aGUgY29kZSBiZWxvdywgMS4wMDAgdGVzdHMgYXJlIHNpbXVsYXRlZCwgZm9yIHdoaWNoIDkwJSBjb21lIGZyb20gY2FzZXMKd2hlcmUgdGhlIG51bGwgaXMgdHJ1ZSBhbmQgdGhlIG90aGVyIDEwJSBmb3Igd2hpY2ggdGhlIGFsdGVybmF0aXZlIGlzIHRydWUuCldlIGFzc3VtZSB0aGUgKmVmZmVjdCBzaXplKiAoZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSAyIGdyb3VwcykgdG8gYmUgZXF1YWwgdG8gMwp1bmRlciB0aGUgYWx0ZXJuYXRpdmUsIHNvIHRoYXQgd2UgY2FuIHNhbXBsZSB0aGUgdGVzdCBzdGF0aXN0aWNzIGZyb20gYSBub3JtYWwKZGlzdHJpYnV0aW9uIHdpdGggbWVhbiAzLgoKYGBge3J9Cm0gPC0gMTAwMCAjIHRvdGFsIG51bWJlciBvZiBoeXBvdGhlc2VzCnAwIDwtIDAuOSAjIDkwJSBvZiBjYXNlcyBhcmUgdHJ1ZSBudWxsCm0wIDwtIHJvdW5kKHAwICogbSkgIyByb3VuZCB0byBhdm9pZCBmbG9hdGluZyBwb2ludCBwcm9ibGVtcwptMSA8LSByb3VuZCgoMSAtIHAwKSAqIG0pCgpzZXQuc2VlZCgxKQphbHBoYSA8LSAwLjA1Cm51bGxfc3RhdHMgPC0gcm5vcm0obTApCmFsdF9zdGF0cyA8LSBybm9ybShtMSwgbWVhbiA9IDMpCm51bGxfdGVzdHMgPC0gYWJzKG51bGxfc3RhdHMpID4gcW5vcm0oMSAtIGFscGhhIC8gMikKYWx0X3Rlc3RzIDwtIGFicyhhbHRfc3RhdHMpID4gcW5vcm0oMSAtIGFscGhhIC8gMikKCihVIDwtIHN1bSghbnVsbF90ZXN0cykpICMgdHJ1ZSBuZWdhdGl2ZXMKKFYgPC0gc3VtKG51bGxfdGVzdHMpKSAjIGZhbHNlIHBvc2l0aXZlcwooUyA8LSBzdW0oYWx0X3Rlc3RzKSkgIyB0cnVlIHBvc2l0aXZlcwooVCA8LSBzdW0oIWFsdF90ZXN0cykpICMgZmFsc2UgbmVnYXRpdmVzCgpSIDwtIFYgKyBTICMgdG90YWwgbnVtYmVyIG9mIHBvc2l0aXZlcwoKKEZEUCA8LSBWIC8gUikKYGBgCgpXZSBzZWUgdGhhdCA0MCUgb2YgdGhlIHBvc2l0aXZlIGNhc2VzIGFyZSBhY3R1YWxseSBmYWxzZSBwb3NpdGl2ZXMuClRvIGdldCBhbiBpZGVhIG9mIHRoZSBGRFIsIHRoZSBleHBlY3RlZCBGRFAsIHdlIHdpbGwgcmVwZWF0IHRoaXMgcHJvY2VkdXJlIGEKbnVtYmVyIG9mIHRpbWVzIGluIHRoZSBmb2xsb3dpbmcgZXhlcmNpc2VzLgoKCiMgRXhlcmNpc2VzOiBGRFIgc2ltdWxhdGlvbnMKCgojIyMgVGFza3Mgey19CgojIyMjIDEuIFNldCBgbTAgPSA5MGAgYW5kIGBtMSA9IDEwYCBhbmQgZ2VuZXJhdGUgYG0gPSAxMDBgIHRlc3Qgc3RhdGlzdGljcywgYG0wYCBvZiB0aGVtIGZyb20gdGhlIHN0YW5kYXJkIG5vcm1hbCBkaXN0cmlidXRpb24gKE4oMCwgMSkpIChudWxsIGRpc3RyaWJ1dGlvbikgYW5kIG0xIG9mIHRoZW0gZnJvbSBOKDMsIDEpICh0aGUgYWx0ZXJuYXRpdmUgZGlzdHJpYnV0aW9uKS4gey19CgoKIyMjIyAyLiBUZXN0IGZvciBzaWduaWZpY2FuY2UgYXQgYGFscGhhID0gMC4wNWAgYnkgY29tcGFyaW5nIHRoZSB0ZXN0IHN0YXRpc3RpY3MgdG8gdGhlIHN0YW5kYXJkIG5vcm1hbCwgZm9yIGJvdGggc2V0cyBvZiB0ZXN0cy4gey19CgoKIyMjIyAzLiBDYWxjdWxhdGUgJFYkLCAkUyQsICRSJCBhbmQgdGhlIEZEUC4gey19CgoKIyMjIyA0LiBSZXBlYXQgc3RlcHMgMS0zIDEwMDAgdGltZXMgYW5kIGtlZXAgdHJhY2sgb2YgdGhlIEZEUCBmb3IgZWFjaCBpdGVyYXRpb24uIFRoZW4gY29tcHV0ZSB0aGUgRkRSIGFzIHRoZSBtZWFuIG9mIHRoZSBGRFBzLiBJbnRlcnByZXQgdGhlIHJlc3VsdHMuIHstfQoKCiMjIyBTb2x1dGlvbnMgey19Cgo8ZGV0YWlscz48c3VtbWFyeT5Tb2x1dGlvbjwvc3VtbWFyeT4KCmBgYHtyfQpzZXQuc2VlZCgxKQpCIDwtIDEwMDAgIyBudW1iZXIgb2Ygc2ltdWxhdGlvbnMKbTAgPC0gOTAKbTEgPC0gMTAKYWxwaGEgPC0gMC4wNQoKIyMgU2ltdWxhdGUgQiB0aW1lcywgZWFjaCB0aW1lIHJlY29yZGluZyBWID4gMApGRFAgPC0gcmVwbGljYXRlKEIsIHsKICBudWxsX3N0YXRzIDwtIHJub3JtKG0wLCBtZWFuID0gMCkKICBhbHRfc3RhdHMgPC0gcm5vcm0obTEsIG1lYW4gPSAzKQogIG51bGxfdGVzdHMgPC0gYWJzKG51bGxfc3RhdHMpID4gcW5vcm0oMSAtIGFscGhhIC8gMikKICBhbHRfdGVzdHMgPC0gYWJzKGFsdF9zdGF0cykgPiBxbm9ybSgxIC0gYWxwaGEgLyAyKQogIFYgPC0gc3VtKG51bGxfdGVzdHMpICMgZmFsc2UgcG9zaXRpdmVzCiAgUyA8LSBzdW0oYWx0X3Rlc3RzKSAjIHRydWUgcG9zaXRpdmVzCiAgUiA8LSBWICsgUwogICMjIFJldHVybiBWIC8gUgogIFYgLyBSCn0pCgooRkRSIDwtIG1lYW4oRkRQKSkKYGBgCgpUaGUgRkRSIHRlbGxzIHVzIHRoZSBleHBlY3RlZCBwcm9wb3J0aW9uIG9mIHBvc2l0aXZlIHRlc3RzIHRoYXQgd2lsbCBiZSBmYWxzZQpwb3NpdGl2ZXMuCgo8L2RldGFpbHM+CgpBcyB3aXRoIHRoZSBGV0VSLCB3ZSBjYW4gbm93IHRoaW5rIG9mIG1ldGhvZHMgdG8gY29udHJvbCB0aGUgRkRSLiBBIHZlcnkgcG9wdWxhcgptZXRob2QgdGhhdCBpcyB3aWRlbHkgdXNlZCBpbiBsYXJnZS1zY2FsZSBpbmZlcmVuY2UgZm9yIGhpZ2gtdGhyb3VnaHB1dApzZXF1ZW5jaW5nIGRhdGEgaXMgdGhlIF9fQmVuamFtaW5pLUhvY2hiZXJnIGNvcnJlY3Rpb25fXy4gVGhlIGRldGFpbHMgb2YgdGhpcwp0ZWNobmlxdWUgbGllIG91dHNpZGUgdGhlIHNjb3BlIG9mIHRoaXMgZXhlcmNpc2Ugc2Vzc2lvbiwgYnV0IGVzc2VudGlhbGx5IGl0CiphZGp1c3RzKiB0aGUgcC12YWx1ZXMgc3VjaCB0aGF0IHRoZSBGRFIgaXMgY29udHJvbGxlZCBhdCBhIGNlcnRhaW4gbGV2ZWwuCk5vdGUgdGhhdCB0aGlzIGlzIGEgc29tZXdoYXQgZGlmZmVyZW50IGFwcHJvYWNoIHRoZW4gdGhlIEZXRVItY29udHJvbGxpbmcKdGVjaG5pcXVlcywgd2hlcmUgc2lnbmlmaWNhbmNlIGN1dC1vZmYgJFxhbHBoYSQgd2FzIGFkanVzdGVkIGluc3RlYWQgb2YgdGhlCnAtdmFsdWVzIHRoZW1zZWx2ZXMuCgpUaGUgQmVuamFtaW5pLUhvY2hiZXJnIChhbmQgb3RoZXIgdGVjaG5pcXVlcykgYXJlIHJlYWRpbHkgYXZhaWxhYmxlIGluIGJhc2UgUiBpbgp0aGUgYHAuYWRqdXN0KClgIGZ1bmN0aW9uIChzZWUgYD9wLmFkanVzdGAgZm9yIGRldGFpbHMpLiBZb3UganVzdCBzdXBwbHkgaXQgYQp2ZWN0b3Igb2YgcC12YWx1ZXMgYW5kIHRoZSBkZXNpcmVkIG1ldGhvZCAoZm9yIEJlbmphbWluaS1Ib2NoYmVyZywgdXNlCmBtZXRob2QgPSAiQkgiYCkuCgoKCiMgRXhlcmNpc2VzOiByZWFsIGRhdGEgKEFsb24gKmV0IGFsLiogKDE5OTkpKQoKV2Ugd2lsbCB0YWtlIGFub3RoZXIgbG9vayBhdCB0aGUgZGF0YXNldCBieSBAYWxvbjE5OTlicm9hZCBvbiBnZW5lIGV4cHJlc3Npb24KbGV2ZWxzIGluIDQwIHR1bW91ciBhbmQgMjIgbm9ybWFsIGNvbG9uIHRpc3N1ZSBzYW1wbGVzLgpXZSB1c2VkIHRoaXMgZGF0YSBhbHNvIGluIFtMYWIgNF0oLi9MYWI0LVNwYXJzZS1QQ0EtTERBLmh0bWwpLgpIb3dldmVyLCB0aGlzIHRpbWUgd2UncmUgaW50ZXJlc3RlZCBpbiBmaW5kaW5nIGdlbmVzIHRoYXQgZXhwcmVzc2VkICpzaWduaWZpY2FudGx5KgpkaWZmZXJlbnQgYmV0d2VlbiB0aGUgdHVtb3IgYW5kIG5vcm1hbCBzYW1wbGVzLiBJbiBvdGhlciB3b3Jkcywgd2Ugd2lsbCBwZXJmb3JtCmh5cG90aGVzaXMgdGVzdHMgZm9yIGVhY2ggb2YgdGhlIF9fMjAwMCBnZW5lc19fLiBUaGlzIGlzIGNsZWFybHkgYSBtdWx0aXBsZQp0ZXN0aW5nIHByb2JsZW0uCgojIyMgVGFza3Mgey19CgojIyMjIDEuIExvYWQgdGhlIGRhdGEgey19CgpKdXN0IHJ1biB0aGUgY29kZSBnaXZlbiBiZWxvdy4KCmBgYHtyIGxvYWQtZGF0YX0KZGF0YSgiQWxvbjE5OTkiKQpzdHIoQWxvbjE5OTlbLCAxOjEwXSkgIyBmaXJzdCAxMCBjb2x1bW5zCnRhYmxlKEFsb24xOTk5JFkpICAjIGNvbnRpbmdlbmN5IHRhYmxlIG9mIHRpc3N1ZSB0eXBlcwpgYGAKCkFzIGEgcmVtaW5kZXIsIHRoZSBkYXRhIGNvbnNpc3RzIG9mIGdlbmUgZXhwcmVzc2lvbiBsZXZlbHMgaW4gNDAgdHVtb3IgYW5kIDIyCm5vcm1hbCBjb2xvbiB0aXNzdWUgc2FtcGxlcy4gVGhlIGV4cHJlc3Npb24gb24gNjUwMCBodW1hbiBnZW5lcyB3ZXJlIG1lYXN1cmVkCnVzaW5nIHRoZSBBZmZ5bWV0cml4IG9saWdvbnVjbGVvdGlkZSBhcnJheS4KQXMgaW4gQGFsb24xOTk5YnJvYWQsIHdlIHVzZSB0aGUgKjIwMDAgZ2VuZXMgd2l0aCB0aGUgaGlnaGVzdCBtaW5pbWFsIGludGVuc2l0eSoKYWNyb3NzIHRoZSBzYW1wbGVzLgoKVGhlIGRhdGFzZXQgY29udGFpbnMgb25lIHZhcmlhYmxlIG5hbWVkIGBZYCB3aXRoIHRoZSB2YWx1ZXMgYHRgIGFuZCBgbmAuClRoaXMgdmFyaWFibGUgaW5kaWNhdGVzIHdoZXRoZXIgdGhlIHNhbXBsZSBjYW1lIGZyb20gdHVtb3Vyb3VzIChgdGApIG9yCm5vcm1hbCAoYG5gKSB0aXNzdWUuCgoKIyMjIyAyLiBQZXJmb3JtIDItc2FtcGxlIHQtdGVzdHMgZm9yIGVhY2ggZ2VuZSwgY29tcGFyaW5nIHRoZSB0dW1vciBhbmQgbm9ybWFsIHRpc3N1ZSBncm91cHMgey19CgpVc2UgdGhlIGB0LnRlc3QoKWAgZnVuY3Rpb24gaW4gYmFzZSBSLiBCeSBkZWZhdWx0LCB0aGlzIHdpbGwgY29tcHV0ZSBhIHR3by1zYW1wbGUKdC10ZXN0IHdpdGggKnVuZXF1YWwgdmFyaWFuY2VzKiAoW1dlbGNoJ3MgdC10ZXN0XVtXZWxjaF0pLgpZb3UgY2FuIHN1cHBseSBhICpmb3JtdWxhKiAodXNpbmcgdGhlIGB+YCkgdG8gYHQudGVzdCgpYCBzaW1pbGFyIHRvIGBsbSgpYC4KClVzZSBlaXRoZXIgYSBgZm9yYCBsb29wIHRvIGl0ZXJhdGUgb3ZlciBhbGwgZ2VuZXMgb3IgdGhlIGBhcHBseSgpYCBmdW5jdGlvbi4KClN0b3JlIHRoZSB0ZXN0IHN0YXRpc3RpYywgcC12YWx1ZSBhbmQgZGVncmVlcyBvZiBmcmVlZG9tIG9mIGVhY2ggdGVzdCBpbiBhCm1hdHJpeCAoZWFjaCByb3cgc2hvdWxkIGJlIGEgZ2VuZSwgMSBjb2x1bW4gY29udGFpbmluZyB0aGUgdGVzdCBzdGF0aXN0Y3MgMQpjb2x1bW4gdGhlIHAtdmFsdWVzIGFuZCAxIGZvciB0aGUgZGVncmVlcyBvZiBmcmVlZG9tKS4gWW91IGNhbiBmaW5kIHRoZXNlIHZhbHVlcwppbiB0aGUgYHQudGVzdCgpYCByZXN1bHQgdW5kZXIgdGhlIGAkcC52YWx1ZWAsIGAkc3RhdGlzdGljYCBhbmQgYCRwYXJhbWV0ZXJgCnNsb3RzLgoKPGRldGFpbHM+PHN1bW1hcnk+U29sdXRpb248L3N1bW1hcnk+CgpJbiB0aGUgc29sdXRpb24gYmVsb3csIEkgdXNlZCB0aGUgYGFwcGx5YCBmdW5jdGlvbiB0byBsb29wIG92ZXIgdGhlIGdlbmVzLCBlc3NlbnRpYWxseQp0aGlzIGlzIHNpbWlsYXIgdG8gYSBgZm9yYCBsb29wIGJ1dCBpcyBhIGJpdCBtb3JlIHN1Y2NpbmN0IHRvIHdyaXRlIGFuZCB0YWtlcwpjYXJlIG9mIHByZS1hbGxvY2F0aW5nIG1lbW9yeSBpbnN0ZWFkIG9mIGhhdmluZyB0ZSBzZXQgdXAgZW1wdHkgbWF0cmljZXMgZmlyc3QKbWFudWFsbHkuIFNldHRpbmcgdGhlIHNlY29uZCBhcmd1bWVudCAoYE1BUkdJTmApIHRvIDIgdGVsbHMgYGFwcGx5YCB0byBsb29wIG92ZXIKdGhlICpjb2x1bW5zKiBvZiB0aGUgaW5wdXQgbWF0cml4IGluc3RlYWQgb2YgdGhlICpyb3dzKi4gRm9yIGRldGFpbHMsIHNlZSBgP2FwcGx5YC4KQnJpZWZseSwgYGFwcGx5KClgIHRha2VzIGEgbWF0cml4LCBtYXJnaW4gYW5kIGZ1bmN0aW9uIGFzIGlucHV0IGFyZ3VtZW50cywgaXQgdGhlbgphcHBsaWVzIHRoZSBmdW5jdGlvbiB0byBlYWNoIGNvbHVtbiBvciByb3cgKGRlcGVuZGluZyBvbiB0aGUgbWFyZ2luKSBvZiB0aGUgbWF0cml4LgpUaGUgcmVzdWx0cyBhcmUgZWl0aGVyIGEgdmVjdG9yIGlmIGVhY2ggZnVuY3Rpb24gY2FsbCByZXR1cm5zIGEgc2luZ2xlIHZhbHVlIG9yCmEgbWF0cml4IGlmIGEgdmVjdG9yIGlzIHJldHVybmVkIGF0IGVhY2ggY2FsbC4gTm90ZSB0aGF0IGBhcHBseWAgd2lsbCByZXR1cm4gdGhlCnJlc3VsdGluZyBtYXRyaXggd2l0aCB0aGUgY2FsY3VsYXRlZCB2YWx1ZXMgaW4gdGhlIHJvd3MuIEFzIGl0IG1ha2VzIG1vcmUgc2Vuc2UKdG8gaGF2ZSB0aGVzZSBpbiBjb2x1bW5zLCB0aGUgbWF0cml4IGlzIHRyYW5zcG9zZWQuCgpgYGB7cn0KIyMgU3BsaXQgZ2VuZSBkYXRhIGFuZCBncm91cGluZyB2YXJpYWJsZSBhbmQgY29udmVydCB0byBtYXRyaXgKZ2VuZV9kYXRhIDwtIGFzLm1hdHJpeChBbG9uMTk5OVssIC0xXSkKZ3JvdXAgPC0gQWxvbjE5OTkkWQoKIyMgVXNlIGBhcHBseWAgdG8gbG9vcCBvdmVyIGNvbHVtbnMgb2YgZ2VuZSBkYXRhIGFuZCBwZXJmb3JtIHQtdGVzdHMsIGV4dHJhY3RpbmcKIyMgcC12YWx1ZXMsIHRlc3Qgc3Rhc3Rpc3RpY3MsIGFuZCBkZWdyZWVzIG9mIGZyZWVkb20KdHRlc3RfcmVzdWx0cyA8LSB0KGFwcGx5KGdlbmVfZGF0YSwgMiwgZnVuY3Rpb24oeCkgewogIHRfdGVzdCA8LSB0LnRlc3QoeCB+IGdyb3VwKQogIHBfdmFsIDwtIHRfdGVzdCRwLnZhbHVlCiAgc3RhdCA8LSB0X3Rlc3Qkc3RhdGlzdGljCiAgZGYgPC0gdF90ZXN0JHBhcmFtZXRlcgogICMjIFJldHVybiB2YWx1ZXMgaW4gbmFtZWQgdmVjdG9yCiAgYyhzdGF0LCAicF92YWwiID0gcF92YWwsIGRmKQp9KSkKCiMjIFRha2UgYSBsb29rIGF0IHJlc3VsdHMKaGVhZCh0dGVzdF9yZXN1bHRzKQpgYGAKCjwvZGV0YWlscz4KCgojIyMjIDMuIFBsb3QgYSBoaXN0b2dyYW0gb2YgdGhlIHAtdmFsdWVzIGFuZCBpbnRlcnByZXQgey19Cgo8ZGV0YWlscz48c3VtbWFyeT5Tb2x1dGlvbjwvc3VtbWFyeT4KClNldHRpbmcgYGJyZWFrc2AgZnJvbSAwIHRvIDEgd2l0aCBhIHdpZHRoIG9mIDAuMDUgbWFrZXMgc2Vuc2UgZm9yIHAtdmFsdWVzIGJlY2F1c2UKdGhlIGZpcnN0IGJhciB3aWxsIHRoZW4gcmVwcmVzZW50IHRoZSBudW1iZXIgb2YgcC12YWx1ZXMgc21hbGxlciB0aGFuIDAuMDUgKHdoaWNoCndlIG9mdGVuIHVzZSBhcyBjdXRvZmYpLgoKYGBge3IgcF92YWwtaGlzdH0KcF92YWxzIDwtIHR0ZXN0X3Jlc3VsdHNbLCAicF92YWwiXQpoaXN0KAogIHBfdmFscywKICBicmVha3MgPSBzZXEoMCwgMSwgYnkgPSAwLjA1KSwgbWFpbiA9ICIiLCB4bGFiID0gInAtdmFsdWUiLAogIHlsaW0gPSBjKDAsIDUwMCkKKQpgYGAKCjwvZGV0YWlscz4KCiMjIyMgNC4gSG93IG1hbnkgZGlzY292ZXJpZXMgLyBzaWduaWZpY2FudCBnZW5lcyBkbyB5b3UgZmluZCB3aGVuIHVzaW5nICRcYWxwaGEgPSAwLjA1JD8gey19Cgo8ZGV0YWlscz48c3VtbWFyeT5Tb2x1dGlvbjwvc3VtbWFyeT4KCmBgYHtyfQphbHBoYSA8LSAwLjA1CnN1bShwX3ZhbHMgPCBhbHBoYSkKYGBgCgo8L2RldGFpbHM+CgojIyMjIDUuIENvcnJlY3QgZm9yIHRoZSBtdWx0aXBsaWNpdHkgcHJvYmxlbSBieSBjb250cm9sbGluZyB0aGUgRldFUiBhdCA1JSB7LX0KCi0gV2hhdCBzaG91bGQgd2UgdXNlIGFzICRcYWxwaGEkPwoKLSBIb3cgbWFueSBkaXNjb3ZlcmllcyBhcmUgbGVmdCBvdmVyIHdoZW4gdXNpbmcgdGhlIEZXRVItYWRqdXN0ZWQgJFxhbHBoYSQ/Cgo8ZGV0YWlscz48c3VtbWFyeT5Tb2x1dGlvbjwvc3VtbWFyeT4KClJlbWVtYmVyIHRoYXQgJFxhbHBoYSQgY2FuIGJlIGNhbGN1bGF0ZWQgZm9yIGEgZ2l2ZW4gRldFUiBhcwoKJCQgXGFscGhhID0gMSAtICgxIC0gXHRleHR7RldFUn0pXnsxL219ICQkCgpgYGB7cn0KbSA8LSBucm93KHR0ZXN0X3Jlc3VsdHMpICMgbnVtYmVyIG9mIHRlc3RzIHBlcmZvcm1lZApmd2VyIDwtIDAuMDUKYWRqX2FscGhhIDwtIDEgLSAoMSAtIGZ3ZXIpXigxL20pCgojIyBOdW1iZXIgb2Ygc2lnbmlmaWNhbnQgZGlzY292ZXJpZXMgYXQgRldFUiA1JQpzdW0ocF92YWxzIDwgYWRqX2FscGhhKQpgYGAKCk5vdGUgdGhhdCB5b3UgY2FuIGFsc28gdXNlIHRoZSAqQm9uZmVycm9uaSogY29ycmVjdGlvbiB0byBnZXQgYSBzaW1pbGFyIHJlc3VsdC4KVGhpcyBpcyBhbHNvIGltcGxlbWVudGVkIGluIHRoZSBgcC5hZGp1c3RgIGZ1bmN0aW9uIGJ5IHVzaW5nIGBtZXRob2QgPSAiYm9uZmVycm9uaSJgLgpFc3NlbnRpYWxseSwgdGhlIEJvbmZlcnJvbmkgY29ycmVjdGlvbiBzZXRzIGEgbmV3ICRcYWxwaGFfbSA9IFxhbHBoYSAvIG0kIGJ5CmRpdmlkaW5nIHRoZSBvcmlnaW5hbCB3aXRoIHRoZSBudW1iZXIgb2YgdGVzdHMgcGVyZm9ybWVkICRtJC4gU28gdGhlIHJlc3VsdCBpcwpzbGlnaHRseSBkaWZmZXJlbnQgdGhhbiB0aGUgcHJvY2VkdXJlIGdpdmVuIGFib3ZlICh3aGljaCBpcyBrbm93biBhcyBTaWRhaydzCnByb2NlZHVyZSBidXQgaXMgbGVzcyBjb21tb25seSB1c2VkKS4gVGhlIGBwLmFkanVzdGAgZnVuY3Rpb24gcmV0dXJucyBhZGp1c3RlZApwLXZhbHVlcywgc2luY2UgbW9kaWZ5aW5nIHRoZSBjdXQtb2ZmICRcYWxwaGEkIGJ5IGRpdmlkaW5nIHdpdGggJG0kIGlzIHRoZSBzYW1lCmFzIG11bHRpcGx5aW5nIHRoZSBwLXZhbHVlcyB3aXRoICRtJCBhbmQgdGhlbiB1c2luZyB0aGUgb3JpZ2luYWwgY3V0LW9mZi4KCmBgYHtyfQpib25mX3B2YWxzIDwtIHAuYWRqdXN0KHBfdmFscywgbWV0aG9kID0gImJvbmZlcnJvbmkiKQpzdW0oYm9uZl9wdmFscyA8IGFscGhhKQoKIyMgTm90ZSB0aGF0IGJvbmZlcnJvbmkgY29ycmVjdGlvbiBpcyBzaW1wbHkgbXVsdGlwbHlpbmcgdGhlIG9yaWdpbmFsIHAtdmFsdWVzCiMjIHdpdGggbSAoYW5kIGN1dHRpbmcgb2ZmIGFueSB2YWx1ZXMgYmlnZ2VyIHRoYW4gMSkKaWR4IDwtIHdoaWNoKGJvbmZfcHZhbHMgPCAxKQpoZWFkKGJvbmZfcHZhbHNbaWR4XSkKaGVhZChwX3ZhbHNbaWR4XSAqIG0pCmBgYAoKPC9kZXRhaWxzPgoKCiMjIyMgNi4gVXNlIHRoZSBGRFIgbWV0aG9kIHRvIGFkanVzdCB0aGUgb3JpZ2luYWwgcC12YWx1ZXMgey19CgotIFVzZSB0aGUgYHAuYWRqdXN0KClgIGZ1bmN0aW9uIHdpdGggYG1ldGhvZCA9ICJCSCJgCgotIFBsb3QgdGhlIGFkanVzdGVkIHAtdmFsdWVzIGFnYWluc3QgdGhlIG9yaWdpbmFscywgd2hhdCBpcyB0aGUgZWZmZWN0IG9mIHRoZSBhZGp1c3RtZW50PwoKLSBIb3cgbWFueSBkaXNjb3ZlcmllcyBhcmUgbGVmdCB3aGVuIGNvbnRyb2xsaW5nIHRoZSBGRFIgYXQgNSU/Cgo8ZGV0YWlscz48c3VtbWFyeT5Tb2x1dGlvbjwvc3VtbWFyeT4KCmBgYHtyIGZkci1hZGp1c3R9CmZkciA8LSBwLmFkanVzdChwX3ZhbHMsIG1ldGhvZCA9ICJCSCIpCgpwbG90KAogIHBfdmFsc1tvcmRlcihwX3ZhbHMpXSwgZmRyW29yZGVyKHBfdmFscyldLAogIHBjaCA9IDE5LCBjZXggPSAwLjYsIHhsYWIgPSAicC12YWx1ZSIsIHlsYWIgPSAiQWRqdXN0ZWQgcC12YWx1ZSIsIGNvbCA9IDQKKQpwb2ludHMoCiAgcF92YWxzW29yZGVyKHBfdmFscyldLCBib25mX3B2YWxzW29yZGVyKHBfdmFscyldLAogIHBjaCA9IDE5LCBjZXggPSAwLjYsIGNvbCA9IDIKKQphYmxpbmUoYSA9IDAsIGIgPSAxKQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwKICBjKCJCZW5qYW1pbmktSG9jaGJlcmciLCAiQm9uZmVycm9uaSIpLAogIGNvbCA9IGMoNCwgMiksIHBjaCA9IDE5CikKCnN1bShmZHIgPCAwLjA1KQpgYGAKCjwvZGV0YWlscz4KCgojIyMjIDcuIENvbXBhcmUgdGhlIEZXRVIgYW5kIEZEUiBjb3JyZWN0aW9ucy4gV2hpY2ggb25lIGlzIG1vcmUgY29uc2VydmF0aXZlPyB7LX0KCgoKIyBGdXJ0aGVyIHJlYWRpbmcgey19CgotIENoYXB0ZXIgNiBvZiB0aGUgW0Jpb21lZGljYWwgRGF0YSBTY2llbmNlXShodHRwOi8vZ2Vub21pY3NjbGFzcy5naXRodWIuaW8vYm9vay8pIGJvb2sKCltXZWxjaF06aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvV2VsY2glMjdzX3QtdGVzdAoKYGBge3IsIGNoaWxkPSJfc2Vzc2lvbi1pbmZvLlJtZCJ9CmBgYAoKIyBSZWZlcmVuY2VzIHstfQo=