Hypothesis test and z-score

A/B testing

This an experimental design technique, which has roots in hypothesis testing, to test different scenarios and see which has the best performance

Stack Overflow Developer Survey 2020

Each year, Stack Overflow surveys its users, who are primarily software developers, about themselves, how they use Stack Overflow, their work, and the development tools they use. In this analysis we’ll look at a subset of the survey responses, from users who identified as Data Scientists.

# Load libraries
library(tidyr)
library(dplyr)
library(ggplot2)
library(fst)
library(tibble)
library(readr)
stack_overflow <- read_rds(ruta_stckov)
stack_overflow
colnames(stack_overflow)
[1] "respondent"         "age_first_code_cut" "converted_comp"     "job_sat"            "purple_link"        "age_cat"            "age"               
[8] "hobbyist"          
glimpse(stack_overflow)
Rows: 2,261
Columns: 8
$ respondent         <dbl> 36, 47, 69, 125, 147, 152, 166, 170, 187, 196, 221, 235, 262, 265, 271, 277, 322, 349, 414, 448, 518, 559, 563, 587, 607, 637, 653, 6…
$ age_first_code_cut <chr> "adult", "child", "child", "adult", "adult", "adult", "child", "adult", "child", "adult", "adult", "adult", "child", "adult", "adult"…
$ converted_comp     <dbl> 77556, 74970, 594539, 2000000, 37816, 121980, 48644, 130000, 94539, 1920000, 48644, 54049, 171000, 125000, 85000, 165000, 4188, 13000…
$ job_sat            <fct> Slightly satisfied, Very satisfied, Very satisfied, Very satisfied, Very satisfied, Very satisfied, Neither, Very satisfied, Slightly…
$ purple_link        <chr> "Hello, old friend", "Hello, old friend", "Hello, old friend", "Amused", "Hello, old friend", "Hello, old friend", "Hello, old friend…
$ age_cat            <chr> "At least 30", "At least 30", "Under 30", "At least 30", "Under 30", "Under 30", "Under 30", "Under 30", "At least 30", "Under 30", "…
$ age                <dbl> 34, 53, 25, 41, 28, 30, 28, 26, 43, 23, 24, 35, 37, 51, 22, 43, 24, 27, 24, 41, 27, 37, 29, 28, 25, 46, 36, 31, 28, 31, 21, 54, 32, 3…
$ hobbyist           <chr> "Yes", "Yes", "Yes", "Yes", "No", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "No", "Yes", "Yes", "Yes", "Y…

Hypothesizing about the mean

Let’s hypothesize that the mean annual compensation of the population of data scientists is $110000. The first thing we can do to check this is to examine the sample data in the survey. Annual compensation, converted to dollars, is included in the converted_comp column. The sample mean is a type of point estimate, which is another name for a summary statistic. You can calculate it with base-R, or more verbosely with dplyr. Either way, the answer is $122000. That’s different from our hypothesis, but is it meaningfully different?

mean_comp_samp <- mean(stack_overflow$converted_comp)
mean_comp_samp
[1] 119574.7
mean_comp_samp <- stack_overflow %>%
  summarise(mean_compensation = mean(converted_comp)) %>%
  pull(mean_compensation)
mean_comp_samp
[1] 119574.7

Generating a bootstrap distribution

To answer this, we need to generate a bootstrap distribution of sample means. This is done by resampling the dataset, calculating the sample mean for that resample, then repeating those steps.

# Repeat steps 1 & 2 many times 
so_boot_distn <- replicate(
  n = 5000,
  expr = {
    #Step 1. Resample
    stack_overflow %>%
      slice_sample(prop = 1, replace = TRUE) %>%
      # Step 2. Calculate point estimate
      summarise(mean_compensation = mean(converted_comp)) %>%
      pull(mean_compensation)
  }
)
head(so_boot_distn)
[1] 121172.8 122346.3 115986.9 120278.9 115449.6 118904.6

Visualizing the bootstrap distribution

Here’s a histogram of the bootstrap distribution. Its bell shape means it’s roughly normally distributed. Notice that \(110000\) is on the left of the distribution.

tibble(resample_mean = so_boot_distn) %>%
  ggplot(aes(resample_mean)) + 
  geom_histogram(binwidth = 1000)

Standard Error

Recall that the standard deviation of the sample statistics in the bootstrap distribution estimates the standard error of the statistic.

std_error <- sd(so_boot_distn)
std_error
[1] 5508.39

Z-scores

Since variables have arbitrary units and ranges, before we test our hypothesis, we need to standardize the values. A common way of standardizing values is to subtract the mean, and divide by the standard deviation. For hypothesis testing, we use a variation where we take the sample statistic, subtract the hypothesized parameter value, and divide by the standard deviation of the statistic, which is the standard error. The result is called a z-score. The sample mean annual compensation for data scientists of $122000 , minus the hypothesized compensation for data scientists of $110000, divided by the bootstrap standard error of $5590 gives a z-score 1.71

mean_comp_hyp <- 110000
mean_comp_samp
[1] 119574.7
z_score <- (mean_comp_samp - mean_comp_hyp) / std_error
z_score
[1] 1.738206

Testing the hypothesis

Is that a big or small number? Determining that is the goal of this course. In particular, we can now state one of the uses of hypothesis testing. Determining whether a sample statistic is close to or far away from an expected value.

Standard normal (z) distribution

Here’s a density plot of the probability density function for the standard normal distribution. That is, a normal distribution with mean zero and standard deviation one. It’s often called the z-distribution. As you might guess, z-scores are related to the z-distribution.

# Create a dataset of z-distribution and plot it
tibble(x = seq(-4, 4, 0.01)) %>%
  ggplot(aes(x)) + 
  stat_function(fun = dnorm) + 
    ylab("PDF(x)")

P-values

Hypothesis tests are like criminal trials.

The true situation is that the defendant committed the crime, or they didn’t. There are also two possible outcomes. The judge gives a guilty or a not guilty verdict. The initial assumption is that the defendant is not guilty. It’s up to the prosecution team to come up with evidence beyond a reasonable doubt that the defendant committed the crime in order for a guilty verdict to be given.

Age of first programming experience

Let’s return to our survey. The age_first_code_cut variable classifies when the user began programming. If they were 14 or older, they are classified as “adult”; otherwise “child”.

Suppose previous research suggests that 35% of software developers programmed as children. This raises a question answerable with our dataset.

Does our sample provide evidence that data scientists have a greater proportion starting programming as a child?

Definitions

Hypothesis is a statement about a population parameter. We don’t know the true value of this population parameter; we can only make inferences about it from the data. Hypothesis tests compare two competing hypotheses. These two hypotheses are the null hypothesis, representing the existing idea, and the alternative hypothesis, representing a new idea.

They are denoted \(H_{0}\) and \(H_{A}\) respectively. The null hypothesis is like the current champion, and the alternative hypothesis is like a challenger trying to overthrow that champion. Here, the null hypothesis is that our data won’t tell us anything new, and that the proportion of data scientists starting programming as children follows the previous research on software developers, at 35%. The alternative hypothesis is that our hunch is correct, and that the percentage is greater than 35%.

Criminal trials vs. Hypothesis Testing

Let’s compare the criminal trial with the hypothesis test. The defendant committing the crime is equivalent to the alternative hypothesis being true, and the defendant not committing the crime is equivalent to the null hypothesis being true. Rather than saying we accept the alternative hypothesis, the verdicts are rejecting the null hypothesis, or failing to reject the null hypothesis. Initially, we assume that the null hypothesis is true, and this only changes if the sample provides enough evidence to reject it.

The hypothesis testing equivalent of “beyond a reasonable doubt” is known as the significance level.

One-tailed & two-tailed tests

The tails of a distribution are the left and right edges of its PDF. Hypothesis tests determine whether the sample statistics lie in the tails of the distribution. There are three types of tests, and the phrasing of the alternative hypothesis determines which type you should use. In this case, we need a right-tailed test.

P-values

p-values measure the strength of support for the null hypothesis. Small p-values mean our statistic is producing an unlikely result in a tail of our null distribution. p-values are probabilities, so they are always between zero and one.

Defining p-values

The definition of a p-value has four parts. It’s a probability related to where the statistic from our sample lies on the null distribution. It measures how many values fall farther away than what we observed in the direction of the alternative hypothesis. It is based on the null distribution, which assumes that the null hypothesis is true. The p-value measures evidence. Does it favor the original assumption or the new alternative?

Calculating the z-score

To calculate the p-value, we need to run through the same steps as before. We get the sample statistic, in this case the proportion of data scientists who started programming as children. We get the hypothesized value from the null hypothesis, \(35\%\). We get the standard error from the bootstrap distribution. The z-score is the difference between the proportions, divided by the standard error.

prop_child_samp <- stack_overflow %>%
  summarise(point_estimate = mean(age_first_code_cut == "child")) %>%
  pull(point_estimate)

prop_child_samp
[1] 0.3914197
sd_boot_distn <- replicate(
  n = 5000,
  expr = {
    #Step 1. Resample
    stack_overflow %>%
      slice_sample(prop = 1, replace = TRUE) %>%
      # Step 2. Calculate point estimate
      summarise(sample_mean = mean(age_first_code_cut == "child")) %>%
      pull(sample_mean)
  }
)
head(sd_boot_distn)
[1] 0.3958425 0.3918620 0.4073419 0.3808050 0.3927466 0.3900929
std_err = sd(sd_boot_distn)
std_err
[1] 0.01026421
prop_child_hyp <- 0.35
#std_err <- 0.0096028
z_score <- (prop_child_samp - prop_child_hyp) / std_err
z_score
[1] 4.035356

Calculating the p-value

The last step is new. We pass the z-score to the normal cumulative distribution function, pnorm. Since we want the right-hand tail for this test, we set lower-dot-tail to FALSE. The p-value is 8.04 e -10 That’s a very small number, but is it small enough to reject the null hypothesis?

p_value <- pnorm(z_score, lower.tail = FALSE)
p_value
[1] 2.725982e-05

Statistical significance

p-values quantify how much evidence there is for the null hypothesis. Large p-values lead us to not have enough evidence for the alternative hypothesis, sticking with the assumed null hypothesis instead. Small p-values make us doubt this original assumption in favor of the alternative hypothesis. What defines the cutoff point between a small p-value and a large one?

Significance level

The cutoff point is known as the significance level, and is denoted alpha (α). The appropriate significance level depends on the dataset and the discipline you are working in. 5% is the most common choice, but 10% and 1% are also popular. The significance level gives us a decision process for which hypothesis to support. If the p-value is less than alpha, we reject the null hypothesis. Otherwise we fail to reject it. It’s important that you decide what the appropriate significance level should be before you run your test. Otherwise there is a temptation to decide on a significance level that lets you choose the hypothesis you want.

Calculating the p-value

The workflow is now to set the significance level, in this case 0.05. Then calculate the z-score and use the standard normal CDF to get the p-value. In this case, the p-value of 8 e-06 is less than or equal to point-zero-five, so we reject the null hypothesis. We have strong evidence for the alternative hypothesis that the proportion of data scientists starting programming as children is greater than 35%.

alpha <- 0.05

prop_child_samp <- stack_overflow %>%
  summarise(point_estimate = mean(age_first_code_cut == "child")) %>%
  pull(point_estimate)

prop_child_hyp <- 0.35 
std_error <- 0.0096028
z_score <- (prop_child_samp - prop_child_hyp) / std_error
p_value = pnorm(z_score, lower.tail = FALSE)
p_value
[1] 8.041901e-06
p_value <= alpha
[1] TRUE

\[ p-value \ is \ less \ than \ or \ equal \ to \ \alpha, so \ reject \ H_{0} \ and \ accept \ H_{A} \]

Confidence intervals

To get a sense as to potential values of the population parameter, it’s common to choose a confidence interval of one minus the significance level. For a significance level of 0.05, we’d use a 95% confidence interval. Here’s the calculation using the quantile method. The interval runs from 0.37 to 0.41 giving a range of plausible values for the proportion of data scientists starting programming as children.

first_code_boot_samp <- replicate(
  n = 5000,
  expr = {
    stack_overflow %>%
      slice_sample(prop = 0.25, replace = TRUE) %>%
      summarise(point_estimate = mean(age_first_code_cut == "child")) %>%
      pull(point_estimate)
  }
)

first_code_boot_distn <- tibble(first_code_child_rate = first_code_boot_samp)
head(first_code_boot_distn)
conf_int <- first_code_boot_distn %>%
  summarise(lower = quantile(first_code_child_rate, 0.025),
            upper = quantile(first_code_child_rate, 0.975))

conf_int

Types of errors

Returning to the criminal trial analogy, there are two possible truth states and two possible outcomes, for four combinations in total. Two of these indicate that the verdict was correct. If a defendant didn’t commit the crime, but the verdict was guilty, they are wrongfully convicted. If the defendant committed the crime but the verdict was not guilty, they got away with it. These are both errors in justice. Similarly, for hypothesis testing, there are two ways to get it right, and two types of error. If you support the alternative hypothesis when the null hypothesis was correct, you made a false positive error. If you support the null hypothesis when the alternative hypothesis was correct, you made a false negative error. These errors are sometimes known as type one and type two errors.

Actual H0 Actual HA
Choosen H0 correct false negative
Choosen HA false positive correct

Possible errors in our example

\[ if \ p \leqslant \alpha, we \ reject \ \ H_{0} \]

A false positive (Type I) error could have ocurred: We thought that data scientists starting coding as children at a higher rate when in reality they did not

\[ if \ p > \alpha, \ we \ fail \ to \ reject \ H_{0} \]

A false negative (Type II) error could have ocurred: we thought that data scientists coded as children at the same rate as software engineers when in reality they coded as children at a higher rate.

Performing t-tests

Here, we’ll look at the related problem of comparing sample statistics across groups of a variable. In the Stack Overflow dataset, converted_comp is a numerical variable of annual compensation. age_first_code_cut is a categorical variable with two levels: child and adult, which describe when the user started programming. We can ask questions about differences in compensation across the two age groups. That is, do users who first programmed as a child tend to be compensated higher than those that started as adults?

Hypotheses

The null hypothesis is that the sample mean for the two groups is the same, and the alternative hypothesis is that the sample statistic for users who started coding as children is greater than for users who started coding as adults. We can write these hypotheses using equations. Mu (µ) represents an unknown population mean, and we use subscripts to denote which group the sample mean belongs to. An alternate way of writing the equations is to compare the differences in sample means to zero.

Calculating group wise summary statistics

Calculating the summary statistics for each group is straightforward. You start with the sample, group by the categorical variable, then summarize the numeric variable. A dplyr way of doing this is shown, but there are many possibilities. Pause the video for a moment and think about different ways of solving this. Here, the child programmers have a mean compensation of $132419.6 dollars compared to $111313.3 for adult programmers. Is that increase statistically significant or could it be explained by sampling variability?

stack_overflow %>%
  group_by(age_first_code_cut) %>%
  summarise(mean_compensation = mean(converted_comp))

Test statistics

Although we don’t know the population mean, we estimate it using the sample mean. x-bar is used to denote a sample mean. Then we use subscripts to denote which group a sample mean corresponds to. The difference between these two sample means is called the test statistic for the hypothesis test. The z-scores are another type of test statistic.

Standardizing the test statistic

z-scores are calculated by taking the sample statistic, subtracting the mean of this statistic as the population parameter of interest, then dividing by the standard error. In the two sample case, the test statistic, denoted t, uses a similar equation. You take the difference between the sample statistics for the two groups, subtract the hypothesized difference between the two groups, then divide by the standard error. \[ x = \frac{sample \ stat + population \ parameter}{standard \ error} \]

\[ t = \frac{difference \ in \ sample \ stats \ - difference \ in \ population \ parameters}{standard \ error} \]

\[ t = \frac{(\bar{x}_{child} \ - \bar{x}_{adult}) \ - (\mu_{child} \ - \mu_{adult})}{SE(\bar{x}_{child} \ - \bar{x}_{adult})} \]

Standard Error

To calculate the standard error, needed for the denominator of the test statistic equation, bootstrapping is most accurate. However, there is an easier way to approximate it. You calculate the standard deviation of the numeric variable for each group in the sample, and the number of observations for each group. Then it’s just some arithmetic.

Assuming the null hypothesis is true

Here’s the test statistic equation again. If we assume that the null hypothesis is true, there’s a simplification we can make. The null hypothesis assumes that the population means are equal. That makes one term in the numerator disappear. Using the approximation for the standard error, we now have a way of calculating the test statistic using only calculations on the sample dataset. We need the mean, standard deviation, and number of observations for each group. This is another group-by and summarize combination. \[ SE(\bar{x}_{child} \ - \bar{x}_{adult}) \approx \sqrt{\frac{s^2_{child}}{n_{child}} \ + \frac{s^2_{adult}}{n_{adult}}} \] \[ H_{0}: \mu_{child} \ - \mu_{adult} = 0 \]

\[ t = \frac{(\bar{x}_{child} \ - \bar{x}_{adult})}{SE(\bar{x}_{child} \ - \bar{x}_{adult})} \]

\[ t = \frac{(\bar{x}_{child} \ - \bar{x}_{adult})}{\sqrt{\frac{s^2_{child}}{n_{child}}} \ + \frac{s^2_{adult}}{n_{adult}}} \]

group_tests <- stack_overflow %>%
  group_by(age_first_code_cut) %>%
  summarise(x_bar = mean(converted_comp), s = sd(converted_comp), n = n())

# See the results
group_tests

Calculating the test statistic

Annoyingly, it requires some grim data manipulation code to calculate the test statistic from this data frame. Since this isn’t a course on data manipulation, let’s do the calculation with separate variables. The numerator is a simple subtraction, and the denominator is like a weighted hypotenuse. The t-statistic is 1.86. Just as with z-scores, we can’t do anything with that number yet

\[ t = \frac{(\bar{x}_{child} \ - \bar{x}_{adult})}{\sqrt{\frac{s^2_{child}}{n_{child}} \ + \frac{s^2_{adult}}{n_{adult}}}} \]

numerator <- group_tests$x_bar[2] - group_tests$x_bar[1]
denominator <- sqrt((group_tests$s[2]^2 / group_tests$n[2]) + (group_tests$s[1]^2 / group_tests$n[1]))
t_stat <- numerator / denominator
t_stat
[1] 1.869931

Hypothesis testing workflow

  1. Identify population parameter that is hypothesized about

  2. Specify the null & alternative hypothesis

  3. Determine (standardized) test statistic & corresponding null distribution

  4. Conduct hypothesis test in R

  5. Measure evidence against the null hypothesis

  6. Make a decision comparing evidence to significance level

  7. Interpret the results in the context of the original problem

Calculating p-values from t-statistics

The test statistic, t, follows a t-distribution. t-distributions have a parameter called the degrees of freedom, or df for short. Here’s a line plot of the PDF of a t-distribution with one degree of freedom in yellow, and the PDF of a normal distribution in blue dashes. Notice that the t-distribution for small degrees of freedom has fatter tails than the normal distribution, but otherwise they look similar.

Degrees of freedom

As you increase the degrees of freedom, the t-distribution gets closer to the normal distribution. In fact, a normal distribution is a t-distribution with infinite degrees of freedom. Degrees of freedom are defined as the maximum number of logically independent values in the data sample. That’s a fairly tricky concept, so let’s try an example.

Calculating degrees of freedom

Suppose your dataset has 5 independent observations. Four of the values are 2, 6, 8, and 5. You also know the sample mean is 5. The last value is no longer independent; it must be 4. Even though all five observations in the sample were independent, because you know an additional fact about it, you only have 4 degrees of freedom. In our two sample case, there are as many degrees of freedom as observations, minus two because we know two sample statistics.

Hypotheses

Recall the hypotheses for our Stack Overflow question about compensation for the two age groups. Since this is a “greater than” alternative hypothesis, we need a right-tailed test.

\(H_{0}:\) The mean compensation (in USD) is the same for those that coded first as a child and those that coded first as an adult.

\(H_{A}:\) The mean compensation (in USD) is greater for those that coded first as a child compared to those that coded first as an adult.

Use a right-tailed test

Significance level

We’re going to calculate a p-value in a moment. We need to decide on a significance level before we do that. There are several possibilities; I’m going to use 0.1

That means that if the p-value is less than point-one, we reject the null hypothesis in favor of the alternative.

\[\alpha = 0.1\]

\[ if \ p \leqslant \alpha \ then \ reject \ H_{0} \]

Calculating p-values: One proportion vs. a value

p_value <- pnorm(z_score, loer.tail = FALSE)

Calculating p-values: Two means from different groups

\[ t = \frac{(\bar{x}_{child} \ - \bar{x}_{adult})}{\sqrt{\frac{s^2_{child}}{n_{child}} \ + \frac{s^2_{adult}}{n_{adult}}}} \]

Now we are calculating means rather than proportions, the z-score is replaced with a t-test statistic. The calculation also needs the degrees of freedom, which is the number of observations in both groups, minus two.

In the previous steps, we used an approximation using sample information (not bootstrapping) for the test statistic standard error. A consequence of this is that to calculate the p-value, we need to transform the test statistic using the t-distribution CDF instead of the normal distribution CDF. Using this approximation adds more uncertainty and that’s why this is a t instead of a z problem.

The t distribution allows for more uncertainty when using multiple estimates in a single statistic calculation. Notice the use of pt() instead of pnorm(), and that the df argument is set to the degrees of freedom. This p-value is less than the significance level of 0.1, we should reject the null hypothesis in favor of the alternative hypothesis that Stack Overflow data scientists who started coding as a child earn more.

numerator <- group_tests$x_bar[2] - group_tests$x_bar[1]
denominator <- sqrt((group_tests$s[2]^2 / group_tests$n[2]) + (group_tests$s[1]^2 / group_tests$n[1]))
t_stat <- numerator / denominator
t_stat
[1] 1.869931
degrees_of_freedom <- group_tests$n[2] + group_tests$n[1] - 2
degrees_of_freedom
[1] 2259
p_value <- pt(t_stat, df = degrees_of_freedom, lower.tail = FALSE)
p_value
[1] 0.0308113

Why is t needed?

The process for calculating p-values is to start with the sample statistic, standardize it to get a test statistic, then transform it via a cumulative distribution function. Previously, that final transformation was denoted z, and the CDF transformation used the (standard normal) z-distribution. The test statistic was denoted t, and the transformation used the t-distribution.

In which hypothesis testing scenario is a t-distribution needed instead of the z-distribution?

Using a sample standard deviation to estimate the standard error is computationally easier than using bootstrapping. However, to correct for the approximation, you need to use a t-distribution when transforming the test statistic to get the p-value.

ANOVA tests

We´ve seen how to compare two groups in the unpaired and paired cases. What if there are more than two groups?

Job satisfaction: 5 categories

The Stack Overflow survey includes a job satisfaction variable, with five categories from “Very dissatisfied” to “Very satisfied”.

stack_overflow %>%
  count(job_sat)

Visualizing multiple distributions

Suppose we want to know if mean annual compensation is different for each of the levels of job satisfaction. The first thing to do is visualize the distributions with box plots. I’ve used coord_flip to swap the x and y axes, making the category labels easier to read. “Very satisfied” looks slightly higher than the others, but to see if they are significantly different, we’ll need to use hypothesis tests.

Question: Is mean annual compensation different for different levels of job satisfaction?

stack_overflow %>% 
  ggplot(aes(x = job_sat, y = converted_comp)) + 
  geom_boxplot() + 
  coord_flip()

Analysis of variance (ANOVA)

ANOVA tests determine whether there are differences between the groups. First, you fit a linear regression. You call lm(), specifying the numeric variable as the response on the left-hand-side of the formula, and the categories as the explanatory variable on the right-hand side. Then you call anova() to perform an analysis of variance test.

In the job_sat row, the right-hand column contains a p-value, which is 0.0013. The two stars next to it tell you that this p-value is significant at the point-zero-one level. That means that at least two of the categories of job satisfaction have significant differences between their compensation levels. The problem is that this method doesn’t tell you which two categories they are. For this reason, ANOVA is less useful than pairwise t-tests.

mdl_comp_vs_sat <- lm(converted_comp ~ job_sat, data = stack_overflow)
mdl_comp_vs_sat

Call:
lm(formula = converted_comp ~ job_sat, data = stack_overflow)

Coefficients:
                 (Intercept)  job_satSlightly dissatisfied                job_satNeither     job_satSlightly satisfied         job_satVery satisfied  
                      127540                        -30896                        -16946                        -31455                         19742  
anova(mdl_comp_vs_sat)
Analysis of Variance Table

Response: converted_comp
            Df     Sum Sq    Mean Sq F value   Pr(>F)   
job_sat      4 1.2561e+12 3.1403e+11  4.4805 0.001315 **
Residuals 2256 1.5812e+14 7.0088e+10                    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Pairwise tests

To compare all five categories of job satisfaction using hypothesis tests, we can test each pair. There are ten ways of choosing two items from a set of five, so we have ten tests to perform. We’ll set the significance level to point-two.

  • \(\mu_{very \ dissatisfied} \neq \mu_{slightly \ dissatisfied}\)

  • \(\mu_{very \ dissatisfied} \neq \mu_{Neither}\)

  • \(\mu_{very \ dissatisfied} \neq \mu_{Slightly \ satisfied}\)

  • \(\mu_{very \ dissatisfied} \neq \mu_{Very \ satisfied}\)

  • \(\mu_{Slightly \ dissatisfied} \neq \mu_{Neither}\)

  • \(\mu_{Slightly \ dissatisfied} \neq \mu_{Slightly \ satisfied}\)

  • \(\mu_{Slightly \ dissatisfied} \neq \mu_{Very \ satisfied}\)

  • \(\mu_{Neither} \neq \mu_{Slightly \ satisfied}\)

  • \(\mu_{Neither} \neq \mu_{Very \ satisfied}\)

  • \(\mu_{Slightly \ satisfied} \neq \mu_{Very \ satisfied}\)

pairwise.t.test()

To run all these hypothesis tests in one go, you can use pairwise.t-test(). The first argument is the numeric variable whose sample means you are interested in. The second argument is the categorical variable defining the groups. We’ll discuss p.adjust.method shortly. The result shows a matrix of ten p-values. Three of these are less than our significance level of point-two.

anova_results <- pairwise.t.test(stack_overflow$converted_comp, stack_overflow$job_sat, p.adjust.method = "none")
anova_results

    Pairwise comparisons using t tests with pooled SD 

data:  stack_overflow$converted_comp and stack_overflow$job_sat 

                      Very dissatisfied Slightly dissatisfied Neither Slightly satisfied
Slightly dissatisfied 0.22417           -                     -       -                 
Neither               0.54651           0.55331               -       -                 
Slightly satisfied    0.17755           0.97462               0.49492 -                 
Very satisfied        0.38698           0.00272               0.07645 0.00016           

P value adjustment method: none 
#p_values <- as.vector(unlist(na.omit(anova_results$p.value)))
p_values <- as.vector(unlist(anova_results$p.value))
p_values <- na.omit(p_values)
#p_values <- anova_results$p.value[complete.cases(anova_results$p.value)]
p_values
 [1] 0.2241721404 0.5465094192 0.1775493945 0.3869751120 0.5533059965 0.9746235725 0.0027180465 0.4949171311 0.0764534042 0.0001567448
attr(,"na.action")
[1]  5  9 10 13 14 15
attr(,"class")
[1] "omit"

As the no. of groups increases…

In this case we have five groups, resulting in ten pairs. As the number of groups increases, the number of pairs - and hence the number of hypothesis tests - increases quadratically. The more tests you run, the higher the chance that at least one of them will give a false positive significant result. With a significance level of 0.2, if you run one test, the chance of a false positive result is 0.2. With five groups and ten tests, the probability of at least one false positive is around 0.7. With twenty groups, it’s almost guaranteed that you’ll get at least one false positive.

Bonferroni correction

The solution to this is to apply an adjustment to increase the p-values, reducing the chance of getting a false positive. One common adjustment is the Bonferroni correction. Now only two of the pairs appear to have significant differences.

pairwise.t.test(stack_overflow$converted_comp, stack_overflow$job_sat, p.adjust.method = "bonferroni")

    Pairwise comparisons using t tests with pooled SD 

data:  stack_overflow$converted_comp and stack_overflow$job_sat 

                      Very dissatisfied Slightly dissatisfied Neither Slightly satisfied
Slightly dissatisfied 1.0000            -                     -       -                 
Neither               1.0000            1.0000                -       -                 
Slightly satisfied    1.0000            1.0000                1.0000  -                 
Very satisfied        1.0000            0.0272                0.7645  0.0016            

P value adjustment method: bonferroni 

More methods

R provides several methods for adjusting the p-values. You can list their names with p.adjust.methods =.

Holm adjustment is the default. It’s less strict than Bonferroni, but works well in most situations.

p.adjust.methods
[1] "holm"       "hochberg"   "hommel"     "bonferroni" "BH"         "BY"         "fdr"        "none"      

Bonferroni and Hold adjustments

If you have ten tests, Bonferroni just multiplies the p-values by ten up to a maximum of one. Holm will multiply the smallest by ten, then the second smallest by nine, and so on. There’s a possible extra correction to make sure that the order of p-values from smallest to largest is preserved, but that’s the gist of it.

p_values
 [1] 0.2241721404 0.5465094192 0.1775493945 0.3869751120 0.5533059965 0.9746235725 0.0027180465 0.4949171311 0.0764534042 0.0001567448
attr(,"na.action")
[1]  5  9 10 13 14 15
attr(,"class")
[1] "omit"

Bonferroni

pmin(1, 10 * p_values)
 [1] 1.000000000 1.000000000 1.000000000 1.000000000 1.000000000 1.000000000 0.027180465 1.000000000 0.764534042 0.001567448

Holm

pmin(1, 1:10 * sort(p_values))
 [1] 0.0001567448 0.0054360930 0.2293602126 0.7101975781 1.0000000000 1.0000000000 1.0000000000 1.0000000000 1.0000000000 1.0000000000

One-sample proportion tests

The previous example tested a single proportion against a specific value. As with means, we can also test differences between proportions in two populations.

Recap

The previous hypothesis tests measured whether or not an unknown population proportion was equal to some value. We used a bootstrap distribution of the sample to estimate the standard error of the sample statistic. The standard error was used to calculate a standardized test statistic, which was used to get a p-value, which was used to decide whether or not to reject the null hypothesis. Bootstrap distributions can be computationally intensive to calculate, so this time we’ll calculate the test statistic without it.

Standardized test statistic for proportions

An unknown population parameter that is a proportion, or population proportion for short, is denoted p. The sample proportion is denoted \(\hat{p}\), and the hypothesized value for the population proportion is denoted \(p_{0}\) . As seen previously, the standardized test statistic is a z-score. You calculate it by starting with the sample statistic, subtracting its mean, then dividing by its standard error.

\(\hat{p}\) minus the mean of \(\hat{p}\) , divided by the standard error of \(\hat{p}\) .

Recall from Sampling in R, the mean of \(\hat{p}\) is \(p\). Under the null hypothesis, the unknown proportion p is assumed to be the hypothesized population parameter \(p_{0}\). The z-score is now \(\hat{p}\) minus \(p_{0}\), divided by the standard error of \(\hat{p}\).

\(p\) : Sample proportion

\(\hat{p}\) : sample proportion (sample statistic)

\(p_{0}\) : hypothesized population proportion

\[ z = \frac{\hat{p} \ - mean(\hat{p})}{standard \ error(\hat{p})} = \frac{\hat{p} \ - p}{standard \ error(\hat{p})} \]

Assuming \(H_{0}\) is true. \(p = p_{0}\). so

\[ z = \frac{\hat{p} \ - p_{0}}{standard \ error(\hat{p})} \]

Easier standard error calculations

Here’s the approximate standard error for two sample means. For proportions, under \(H_{0}\), the standard error of \(\hat{p}\) is \(p_{0}\) times \(1 - p_{0}\) , divided by the number of observations, then square-rooted. We can substitute this into our equation for the z-score. This is easy to calculate because it only uses \(\hat{p}\) and \(n\), which we get from the sample, and \(p_{0}\), which we chose.

\[ SE(\bar{x}_{child} \ - \bar{x}_{adult}) \approx \sqrt{\frac{s^2_{child}}{n_{child}} \ + \frac{s^2_{adult}}{n_{adult}}} \]

\[ SE_{\hat{p}} \ = \sqrt{\frac{p_{0} \ * (1 - p_{0})}{n}} \]

Assuming \(H_{0}\) is TRUE,

\[ z \ = \frac{\hat{p} \ - p_{0}}{\sqrt{\frac{p_{0} \ * \ (1 \ - p_{0})}{n}}} \]

This only uses sample information (\(\hat{p}\) and \(n\)) and the hypothesized parameter (\(p_{0}\))

Why z instead of t?

Why we used a z-distribution here, but a t-distribution previously?. This is the test statistic equation for the two sample mean case. The standard deviation of the sample, s, is calculated from the sample mean, \(\bar{x}\). That means that \(\bar{x}\) is used in the numerator to estimate the population mean, and in the denominator to estimate the population standard deviation. This dual usage causes an increase in our uncertainty about the estimate of the population parameter. Since t-distributions are effectively a normal distribution with fatter tails, we can use them to account for this extra uncertainty. In effect, the t-distribution provides extra caution against mistakenly rejecting the null hypothesis. For proportions, we only use \(\hat{p}\) in the numerator, thus avoiding the problem with uncertainty, and a z-distribution is fine.

\[ t \ = \frac{(\bar{x}_{child} \ - \bar{x}_{adult})}{\sqrt{\frac{a^2_{child}}{n_{child}} \ + \frac{s^2_{adult}}{n_{adult}}}} \]

  • \(s\) is calculated from \(\bar{x}\), so \(\bar{x}\) is used to estimate the population mean and to estimate the population standard deviation

  • This increases the uncertainty in our estimate of the population parameter.

  • t-distribution has fatter tails than a normal distribution

  • This gives an extra level of caution

  • \(\hat{p}\) only appears in the numerator, so z-scores are fine

Stack overflow age categories

Let’s hypothesize that half the users are under thirty. We’ll set a significance level of 0.01. Just over half the users are under thirty.

\(H_{0}\) : The proportion of SO users under thirty is equal to 0.5

\(H_{A}\) : The proportion of SO users under thirty is not equal to 0.5

alpha <- 0.01

stack_overflow %>%
  count(age_cat)

Variables for z

Let’s get the numbers needed for the z-score. \(\hat{p}\) is the proportion of rows in the sample where age_cat equals under thirty. \(p_{0}\) is 0.5 according to the null hypothesis. n is the number of rows in the dataset.

p_hat <- stack_overflow %>%
  summarise(prop_under_30 = mean(age_cat == "Under 30")) %>%
  pull(prop_under_30)

p_hat
[1] 0.5356037
p_0 <- 0.5
n <- nrow(stack_overflow)
n
[1] 2261

Calculating the z-score

Calculating the z-score is just arithmetic; the value is three-point-five.

\[ z \ = \frac{\hat{p} \ - p_{0}}{\sqrt{\frac{p_{0} \ * \ (1 \ - p_{0})}{n}}} \]

numerator <- p_hat - p_0
denominator <- sqrt((p_0 * (1 - p_0)) / n)
z_score <- numerator / denominator
z_score
[1] 3.385911

Calculating the p-value

For left-tailed alternative hypotheses, you transform the z-score into a p-value using the pnorm with the default of lower-dot-tail equals TRUE. For right-tailed alternative hypotheses you set lower-dot-tail to FALSE. For two-tailed alternative hypotheses, you check whether the test statistic lies in either tail, so the p-value is the sum of these two values. Since the normal distribution PDF is symmetric, here this simplifies to twice the left-tailed p-value. Here, the p-value is less than the significance level of point-zero-one, so we reject the null hypothesis and conclude that the proportion of users under thirty is not equal to point-five.

# Left tailed ("less than")
p_value_less <- pnorm(z_score)
p_value_less
[1] 0.9996453
# Right tailed ("greater than")
p_value <- pnorm(z_score, lower.tail = FALSE)
p_value
[1] 0.0003547114
# Two-tailed ("not equal")
p_value_2t <- pnorm(z_score) + pnorm(z_score, lower.tail = FALSE)
p_value_2t
[1] 1
p_value_a <- 2 * pnorm(z_score)
p_value_a
[1] 1.999291
p_value <= alpha
[1] TRUE

Two sample proportion tests

The previous example tested a single proportion against a specific value. As with means, we can also test differences between proportions in two populations.

Comparing two proportions

The Stack Overflow survey contains a hobbyist variable. The value “Yes” means the user described themself as a hobbyist; “No” means they described themself as a professional. We can hypothesize that the proportion of users who are hobbyists is the same for the under thirty age category as the at least thirty category.

More formally, the null hypothesis is that the difference between the population parameters for each group is zero. I’ve set a significance level of 0.05.

\(H_{0}\) : The proportion of SO users who are hobbyists is the same for those under thirty as those at least thirty

\[ H_{0}: p_{\geq 30} \ - p_{< 30} \ = 0 \]

\(H_{A}\) : The proportion of SO users who are hobbyists is different for those under thirty as those at least thirty

\[ H_{A}: p_{\geq 30} \ - p_{<30} \neq 0 \]

Calculating the z-score

Let’s break down this z-score equation. The sample statistic is the difference in the proportions for each category \((\hat{p}_{\geq30} \ - \hat{p}_{<30}) \ - 0\). That’s the two \(\hat{p}\) values on the numerator. We subtract the hypothesized value of the population parameter. Assuming the null hypothesis is true, that’s just zero, so the term disappears. The denominator is the standard error of the sample statistic \(SE(\hat{p}_{\geq30} \ - \hat{p}_{<30})\).

Again we can avoid having to generate a bootstrap distribution. The equation for the standard error is a slightly more complicated version of the equation for the one sample case. In this equation, note that p-hat is the sample proportion for the whole dataset, not for each category. This whole dataset \(\hat{p}\) is known as a pooled estimate of the population proportion. We need one more equation to get \(\hat{p}\). It’s a weighted mean of the p-hats for each category. This looks horrendous, but it’s just arithmetic, and R is pretty good at that. The good news is that we only need four numbers from the sample dataset to do these calculations.

\[ z = \frac{(\hat{p}_{\geq30} \ - \hat{p}_{<30}) \ - 0}{SE(\hat{p}_{\geq30} \ - \hat{p}_{<30})} \]

\[ SE(\hat{p}_{\geq0} \ - \hat{p}_{<0}) \ = \sqrt{\frac{\hat{p} \times \ (1 \ - \hat{p})}{n_{\geq30}} \ + \frac{\hat{p} \times \ (1 \ - \hat{p})}{n_{<30}}} \]

\(\hat{p}\) is a pooled estimate for \(p\) (common unknown proportion of successes)

\[ \hat{p} \ = \frac{n_{\geq30} \times \hat{p}_{\geq30} \ + n_{<30} \times \hat{p}_{<30}}{n_{\geq30} \ + n_{<30}} \]

We only need to calculate 4 numbers: \(\hat{p}_{\geq30}, \ \hat{p}_{<30}, \ n_{\geq30}, \ n_{<30}\)

Getting the numbers for the z-score

To calculate these four numbers you group_by() the categories, and summarize to calculate the sample proportion and row counts. After that, you can do the arithmetic to get the test statistic - the z-score is minus four-point-two - and call pnorm to get the p-value. I’m not going to run through these steps because it’s exactly the same as you’ve seen already. Instead, I’ll show you an easier way.

stack_overflow %>%
  group_by(age_cat) %>%
  summarise(p_hat = mean(hobbyist == "Yes"), n = n())

Proportion tests using prop_test()

Naturally, R has at least two functions for performing this type of hypothesis test, known as a proportion test. Base-R has a function named prop.test(), though unfortunately, its interface is somewhat peculiar. To make your life easier it’s best to use prop_test() from infer. This takes a formula, with your numeric variable on the left and the categories on the right. The order argument specifies which \(\hat{p}\) should be subtracted from the other. Here, it is specified so that you start with the “at least thirty” \(\hat{p}_{\geq30}\) , then subtract the “under thirty” \(\hat{p}_{<30}\)

The success argument specifies which of the two response levels to get the proportion of. alternative takes the same values as t-test. Here, we have a two-sided alternative hypothesis. The correct argument specifies whether or not to apply Yates’ continuity correction. This is a fudge factor needed for technical reasons when the sample size is very small. Since each group has over one thousand observations, we don’t need it. The p-value is in the third column of the resulting tibble. It’s smaller than the significance level we specified earlier, so we can conclude that there is a difference in the proportion of Stack Overflow hobbyists between the under and over thirty groups.

library(infer)
stack_overflow %>%
  prop_test(hobbyist ~ age_cat, order = c("At least 30", "Under 30"), success = "Yes", 
            alternative = "two-sided", correct = FALSE)

Chi-square test of independence

Just as ANOVA extends t-tests to more than two groups, chi-square tests of independence extend proportion tests to more than two groups.

Revisiting the proportion test

Here’s the proportion test from last time. In the first column, the test statistic is the square of the z-score. Minus four-point-two-two squared is seventeen-point-eight. In the second column, chisq_df means the degrees of freedom of a chi-square test. Its value is one.

library(infer)
stack_overflow %>%
  prop_test(hobbyist ~ age_cat, order = c("At least 30", "Under 30"), alternative = "two-sided", correct = FALSE)

Independence of variables

That proportion test had a positive result. There was evidence that the hobbyist and age category variables had an association. If that wasn’t the case, and the proportion of hobbyists was the same for each age category, the variables would be considered statistically independent. More formally, statistical independence of two categorical variables is when the proportion of successes in the response variable is the same across all categories of the explanatory variable.

Job satisfaction and age category

Recall that the Stack Overflow sample has an age category variable with two categories and a job satisfaction variable with five categories.

stack_overflow %>%
  count(age_cat)
stack_overflow %>%
  count(job_sat)

Declaring the hypothesis

We can declare hypotheses to test for independence of these variables. Here, age category is the response variable, and job satisfaction is the explanatory variable. The null hypothesis is that independence occurs. I’ve set a significance level of 0.1. The test statistic is denoted chi-square. It quantifies how far away the observed results are from the values you’d expect if independence was true.

\(H_{0}\) : Age categories are independent of job satisfaction levels

\(H_{A}\) : Age categories are not independent of job satisfaction levels

Test statistic denoted \(\chi^2\)

Assuming the independence, how far away are the observed results from the expected values?

alpha = 0.1

Exploratory visualization: Proportional stacked bar plot

Let’s explore the data using a proportional stacked bar plot. fill means two different things here. In the aesthetics, fill refers to the fill color of the bars. In geom_bar’s position argument, "fill" makes each bar the same height, so you can compare proportions. If the age category was independent of the job satisfaction, the split between the age categories would be at the same height in each of the five bars. There’s some variation here, but we’ll need the hypothesis test to determine whether it’s a significant difference.

ggplot(stack_overflow, aes(job_sat, fill = age_cat)) + 
  geom_bar(position = "fill") +
  ylab("proportion")

Chi-square independence test using chisq_test()

The hypothesis test for independence is called a chi-square independence test. There’s a base-R function for it called chisq.test(), but like prop.test(), it’s fiddly to use. The easiest option is to use infer’s chisq_test(). Pipe from the sample dataset, passing a formula with the response variable on the left and the explanatory variable on the right. The p-value is 0.23, which is above the significance level we set, so we conclude that age categories are independent of job satisfaction. The chi-square distribution, whose CDF is used to calculate the p-value from the test statistic, has a degrees of freedom argument, just like the t-distribution. The results show that there are four degrees of freedom. This is the number of response categories minus one, times the number of explanatory categories minus one. Two minus one times five minus one is four.

stack_overflow %>%
  chisq_test(age_cat ~ job_sat)

Degrees of freedom

\[ (No. \ of \ response \ categories \ - 1) \times (No. \ of \ explanatory \ categories \ - 1) \\(2 \ - 1) \times (5 \ - 1) \ = 4 \]

Swapping the variables

If we swap the variables, so age category is the response, and job satisfaction is the explanatory variable, the visual inspection technique is the same: see if the splits for each bar are in similar places.

ggplot(stack_overflow, aes(age_cat, fill = job_sat)) + 
  geom_bar(position = "fill") + 
  ylab("proportion")

Chi-square both ways

If we run the chi-square test with the variables swapped, then the results are identical. We should ask questions like “are variables X and Y independent?”, not “is variable X independent from variable Y?”, since the order doesn’t matter.

stack_overflow %>%
  chisq_test(age_cat ~ job_sat)
stack_overflow %>%
  chisq_test(job_sat ~ age_cat)

What about directions and tails?

We didn’t worry about tails in this test, and in fact chisq_test() doesn’t have an alternative argument. This is because the test statistic is based on the square of observed and expected counts, and square numbers are non-negative. That means that chi-square tests tend to be right-tailed tests.

1 Left-tailed chi-square tests are used in statistical forensics to detect is a fit is suspiciously good because the data was fabricated. Chi-square tests of variance can be two-tailed. These are niche uses though.

args(chisq_test)
function (x, formula, response = NULL, explanatory = NULL, ...) 
NULL

Chi-square goodness of fitness test

Last time you used a chi-square test to compare proportions in two categorical variables. This time, we’ll use another variant of the chi-square test to compare a single categorical variable to a hypothesized distribution.

Declaring the hypothesis

Let’s hypothesize that half the users in the population would respond “Hello old friend”, and the other three responses would get one sixth each. The tribble function provides a convenient way of manually entering values for a data frame, and returns a standard tibble. We can specify the hypotheses as whether or not the sample matches this hypothesized distribution. The test statistic measures how far the distribution of proportions observed in the sample is from the hypothesized distribution of proportions. I’ll set a significance level of point-zero-one.

1 tribble is short for “row-wise tibble”; not to be confused with the alien species from Star Trek

hypothesized <- tribble(~ purple_link, ~ prop,
                        "Hello, old friend", 1/2,
                        "Amused", 1/6,
                        "Indifferent", 1/6,
                        "Indifferent", 1/6,
                        "Annoyed", 1/6)
hypothesized

Hypothesized counts by category

To visualize the distribution of the purple links, it will help to have the hypothesized counts. These are the hypothesized proportions times the total number of observations in the sample.

n_total <- nrow(stack_overflow)
hypothesized <- tribble(~ purple_link, ~ prop,
                        "Hello, old friend", 1/2,
                        "Amused", 1/6,
                        "Indifferent", 1/6,
                        "Indifferent", 1/6,
                        "Annoyed", 1/6) %>%
  mutate(n = prop * n_total)

hypothesized

Visualizing counts

The natural way to visualize the categories is with a bar plot. We’re using geom_col() here since we already calculated the counts. The purple points show the hypothesized counts, so you can compare the sample distribution to the hypothesized distribution. Two of the bars are close to the values we hypothesized and two are slightly different. We’ll need to run the hypothesis test to see if the differences are statistically significant.

ggplot(purple_link_counts, aes(purple_link, n)) +
  geom_col() + 
  geom_point(data = hypothesized, color = "red")

Chi-square goodness of fit test using chisq_test()

The one sample chi-square test is called a goodness of fit test. To run it, we need the hypothesized proportions in vector form rather than in a tibble. Again we use chisq_test() from infer. However, this time the arguments are different. Piping from the dataset, we set response to the name of the column of interest, and set \(p\) to the hypothesized distribution. The degrees of freedom are one less than the number of choices in the survey. Four minus one is three. The p-value is very small, much lower than the significance level we set. Thus we conclude that the sample distribution of proportions is different than the hypothesized distribution of proportions.

hypothesized_props <- c("Hello, old friend" = 1/2, Amused = 1/6, Indifferent = 1/6, Annoyed = 1/6)
stack_overflow %>%
  chisq_test(response = purple_link, p = hypothesized_props)

Testing sample size

In order to conduct a hypothesis test, and be sure that the result is fair, a sample must meet three requirements: it is a random sample of the population; the observations are independent; and there are enough observations. Of these, only the last condition is easily testable with code.

The minimum sample size depends on the type of hypothesis tests you want to perform. Let’s test some scenarios on the late_shipments dataset.

  • Using the late_shipments dataset, get counts by the freight_cost_group columns.

  • Insert a suitable number to inspect whether the counts are “big enough” for a two sample t-test.

# Get counts by freight_cost_group
counts <- late_shipments %>%
  count(freight_cost_group)
Error in count(., freight_cost_group) : object 'late_shipments' not found
  • Using the late_shipments dataset, get counts by the late column.

  • Insert a suitable number to inspect whether the counts are “big enough” for a one sample proportion test.

  • Using the late_shipments dataset, get counts by the vendor_inco_term and freight_cost_group columns.

  • Insert a suitable number to inspect whether the counts are “big enough” for a chi-square independence test.

  • Using the late_shipments dataset, get counts by the shipment_mode column.

  • Insert a suitable number to inspect whether the counts are “big enough” for an ANOVA test.

```r
# Count the values of shipment_mode
counts <- late_shipments %>%
  count(shipment_mode)

# See the result
counts
```
<div data-pagedtable="false">
  <script data-pagedtable-source type="application/json">
{"columns":[{"label":["shipment_mode"],"name":[1],"type":["chr"],"align":["left"]},{"label":["n"],"name":[2],"type":["int"],"align":["right"]}],"data":[{"1":"Air","2":"909"},{"1":"Air Charter","2":"6"},{"1":"Ocean","2":"84"},{"1":"NA","2":"1"}],"options":{"columns":{"min":{},"max":[10],"total":[2]},"rows":{"min":[10],"max":[10],"total":[4]},"pages":{}}}
  </script>
</div>
```r

# Inspect whether the counts are big enough
all(counts$n >= 30)
```
```
[1] FALSE
```

The “There is only one test” framework

Traditional hypothesis tests make assumptions about the sample dataset. Here, we’ll look at what to do when those assumptions don’t hold.

Imbalance data

Sometimes you can’t get a sample representing all parts of your population. Consider this subset of the Stack Overflow survey. There are fewer users with hobbyist “No” than “Yes”, and fewer users with “At least 30” ages than “Under 30”. In fact, there are no users with hobbyist “No” and age “At least 30”. This is a problem for a proportion test, since it requires that every group has at least ten observations. I used count(…, .drop) argument to include subgroups with a zero count. A dataset where some group’s counts are much larger than others is called “imbalanced”.

Hypotheses

We can declare hypotheses to test. I’ll set a significance level of point-one.

\(H_{0}\) : The proportion of hobbyists under 30 is the same as the proportion of hobbyists at least 30.

\(H_{A}\) : The proportion of hobbists under 30 is different from the proportion of hobbyists at least 30

Proceeding with a proportion test regardless

Let’s ignore the small sample problems and run a proportion test. The p-value is \(2.403 \times 10^{-5}\) , which is below the significance level. This test suggests that there is enough evidence to reject the null hypothesis. We’ll revisit this later.

A grammar of graphics

Traditionally, every type of plot has a name, like scatter plot or line plot. In base-R you decide the plot type by specifying arguments to the plot function, or by calling a dedicated function like hist. This is fine until you want to draw a custom plot like a histogram with polar coordinates. If a function to draw that doesn’t exist, you are stuck. The beauty of the grammar of graphics system that ggplot2 implements is that plots are broken down into components that can be combined in many ways. It’s more flexible creating a new function for every plot type you can think of. What if there was a grammar of hypothesis tests?

Plot type Base-R ggplot2
Scatter plot plot(, type = "p") ggplot( ) + geom_point( )
Line plot plot(, type = "l") ggplot( ) + geom_line( )
Histogram hist( ) ggplot( ) + geom_histogram( )
Box plot boxplot( ) ggplot( ) + geom_boxplot( )
Bar plot barplot( ) ggplot( ) + geom_bar( )
Pie plot pie( ) ggplot( ) + geom_bar( ) + coord_polar( )

A grammar of hypothesis tests

The good news is that there is a grammar of hypothesis tests. It was invented by DataCamp instructor Allen Downey, and is called the “There is only one test” framework. The framework was implemented in R in the infer package that you’ve used for proportion and chi-square tests. All hypothesis tests can be performed with this code flow, based on specify, hypothesize, generate, and calculate. By changing the arguments to those functions you can run standard tests like t-tests, or devise your own custom tests. We’ll cover specify and hypothesize now, and the others in the next video. generate is worth mentioning briefly. It creates simulated data reflecting the null hypothesis. Using simulation rather than arithmetic equations to get the test statistic is computationally expensive, so it has only become accessible with modern computing power. However, simulation has a benefit that it works well even when you have small samples or imbalanced data.

1 Allen Downey teaches “Exploratory Data Analysis in Python”.

Specifying the variables of interest

The Stack Overflow survey contains many variables, but for this test, we only care about two of them. specify() works like dplyr’s select, returning only the response and explanatory columns.

specify()

To call specify, you pipe from the dataset, and pass a formula with the response on the left and the explanatory variable on the right. In the one sample case, use NULL for the explanatory value. Just as when you called prop_test(), you need to tell specify which response value is regarded as a success. specify returns a tibble with attributes noting the response and explanatory variable names.

  • For 2 sample tests, use: response ~ explanatory

  • For 1 sample tests use response ~ NULL

hypothesize()

hypothesize() declares the type of null hypothesis. Proportion tests are a special case of the chi-square independence test, so we choose “independence”. hypothesize doesn’t calculate anything; it simply adds another attribute to the dataset.

  • For 2 sample tests, use "independence" or "point"

  • For 1 sample tests, use "point"

Continuing the infer pipeline

Previously we saw part of the infer pipeline for simulation-based hypothesis tests.

Recap: hypotheses and dataset

We were testing the hypothesis that the proportion of hobbyists was the same for two age categories. However, this subset of the Stack Overflow survey was imbalanced, violating the assumptions of the traditional proportion test.

\(H_{0}\) : The proportion of hobbiyists under 30 is the same as the prop´n of hobbyists at least 30.

\(H_{A}\) : The proportion of hobbyists under 30 is different from the prop´n of hobbyists at least 30.

Recap: workflow

You saw the complete workflow for a custom hypothesis test. So far, we’d done the specify and hypothesize steps, resulting in a tibble with two columns and some extra attributes describing the response and explanatory variables, and the type of null hypothesis.

Motivating generate()

If we assume the null hypothesis is true, then it shouldn’t matter which hobbyist value is matched up to each age category, since the proportion of hobbyists is the same in each age category. We can generate a simulated dataset by shuffling the response hobbyist values while keeping the explanatory age category values the same.

\(H_{0}\) : The proportion of hobbiyists under 30 is the same as the prop´n of hobbyists at least 30.

if \(H_{0}\) is true, then

  • In each row, the hobbist value could have appeared with either age category with equal probability.

  • To simulate this, we can permute (shuffle) the hobbyist values while keeping the age categories fixed

One permutation

Conceptually, this is how generate shuffles the dataset. It selects the response column, randomly samples the whole column without replacement, then binds it back to the explanatory column. Compare the original dataset on the left, and the shuffled dataset on the right. The hobbyist values have changed but the age category values have not.

Generating many replicates

Since randomness is used in the shuffling step, we can’t just create one simulated dataset, or the results would be unreliable. generate() performs the simulation step many times. Each simulated dataset is called a replicate and represents an example of what we might expect the two columns to look like in a universe where the null hypothesis is true.

generate()

To call generate(), tell it how many replicates you want. For independence tests, the generation type should always be “permute”. For convenience, generate combines all the simulated datasets into a single tibble. This is big! It has as many rows as the original dataset, times the number of replicates.

  • For “independence” null hypothesis, set type to "permute".

  • For “point” null hypotheses, set type to "bootstrap" or "simulate"

Calculating the test statistic

For each replicate, we calculate the test statistic, in this case the difference in proportions of hobbyists between the two age categories. 5000 replicates gives 5000z differences in proportions. We have a distribution of test statistics. This is known as the null distribution.

calculate()

To use the difference in proportions as the test statistic, set the stat argument to “diff in props”. We need to tell calculate which proportion to subtract from which by setting the order.

1 The ?calculate help page lists all possible test statistics.

calculate() calculates a distribution of test statistics known as the null distribution

Visualizing the null distribution

To visualize this null distribution as a histogram, call visualize. This histogram doesn’t have a normal bell curve. That means the assumptions for the proportion test didn’t hold. Also notice that the test statistic only takes nine distinct values.

Calculating the test statistic on the original dataset

To calculate a p-value, we need to compare the null distribution to the test statistic from the original dataset.

Observed statistic: specify() %>% calculate()

To calculate the test statistic on the original sample dataset, you reuse the specify and calculate steps. Copy and paste the null distribution code, then remove the hypothesize and generate steps. Here, the observed statistic is -0.07.

Visualizing the null distribution vs. the observed stat

Here’s the null distribution histogram with a vertical line added at the observed statistic. The observed statistic is at one edge of the distribution. Does this make it different enough from the null distribution that we should reject the null hypothesis? We’ll need to calculate the p-value to find out.

Get the p-value

To get the p-value, call get_p_value(), passing the null distribution and observed statistic. You also need to set the type of alternative hypothesis. The argument is called direction rather than alternative, and “two sided” is separated with a space rather than a dot. This time the p-value is point-one-five, which is greater than the significance level of point-one. That means that we should fail to reject the null hypothesis sticking with there being no difference in proportions of hobbyists between age categories. Recall the results from the dubious proportion test we used before. That had the opposite conclusion. Hopefully this illustrates the danger of using traditional hypothesis tests when the assumptions aren’t met. Although it takes more code, the simulation-based hypothesis tests are more robust against small samples, and will help prevent you reaching poor conclusions.

Non-parametric ANOVA and unpaired t-tests

The simulation-based proportion test we performed using the infer pipeline avoided the assumption that the test statistic is normally distributed.

Non-parametric tests

Hypothesis tests that don’t assume a probability distribution for the test statistic are called non-parametric tests. There are two types of non-parametric test. The infer pipeline performs simulation-based tests. Other tests use the rank of the data.

Two types of non-parametric tests:

  1. Simulation-based

  2. Rank-based

t_test()

Here’s a t-test. This tested differences in mean annual compensation for Stack Overflow users depending on whether they first learned to program as a child or as an adult. Rather than calculating it step-by step, We’ve used t_test from infer. The degrees of freedom are different here because they are calculated using an equation called the Welch approximation, instead of taking the number of observations minus two. In practice, the difference isn’t important. The p-value is 0.03082 instead of 0. Let’s explore some non-parametric alternatives to the t-test, starting with the simulation-based infer pipeline.

\[ H_{0} : \mu_{child} \ - \mu_{adult} \ = 0 \ \ \ H_{A} : \mu_{child} \ - \mu_{adult} \ > 0 \]

Calculating the null distribution

Just as you did with the simulation-based proportion test, you start by specifying the variables of interest. It’s the same formula passed to t_test: the numeric compensation on the left and the age categories on the right. Next you declare the null hypothesis, that compensation is independent of the age category. Thirdly, you generate permutation replicates of the data. Finally, you calculate the non-standardized test statistic for each replicate. The stat argument is "diff in means". The order argument is the same as in the call to t_test.

Calculating the observed statistic

To calculate the difference in means observed in the Stack Overflow survey, we reuse the specify and calculate steps from the previous pipeline.

Get the p-value

Finally, we get the p-value from the null distribution and observed statistic. The direction argument to get_p_value is the same as the alternative argument in t_test. The p-value is 0.0362 rather than 0.0308

Ranks of vectors

Consider this numeric vector, x. The first value of x, one, is the smallest. The second value, fifteen, is the fifth smallest. These orderings from smallest to largest are known as the ranks of the elements of x. You can access them with the rank function. You can avoid assumptions about normally distributed data by performing hypothesis tests on the ranks of a numeric input. The Wilcoxon-Mann-Whitney test is, very roughly speaking, a t-test on ranked data.

Wilcoxon-Mann-Whitney test

You can run a Wilcoxon-Mann-Whitney test using wilcox.test() from base-R. It accepts a formula and data argument, though these are swapped compared to the infer functions, so they are less pipe-friendly. alternative sets the type of alternative hypothesis, and correct determines whether or not to apply a continuity correction to the z-scores. Since ranks are integers and the normal distribution is continuous, this correction can improve the approximation. However, for large sample sizes its effect is trivial and not needed. Here, the p-value is shown as less than two times ten to the minus sixteen, less than the significance level.

1 Also known as the “Wilcoxon rank-sum test” and the “Mann-Whitney U test”.

Kruskal-Wallis test

In the same way that ANOVA extends t-tests to more than two groups, the Kruskal-Wallace test extends the Wilcoxon-Mann-Whitney to more than two groups. That is, the Kruskal-Wallace test is a non-parametric version of ANOVA. You pass a formula with the numerical variable on the left and the categorical variable on the right, and set data to the sample data frame. Again, the p-value here is very small.

LS0tDQp0aXRsZTogIkh5cG90aGVzaXMgdGVzdGluZyAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmF1dGhvcjogSnVhbiBGZXJuYW5kbyBNb3NxdWVyYSBBcmF1am8NCmRhdGU6IDIwMjQtMDMtMTUNCi0tLQ0KDQojIyMgKipIeXBvdGhlc2lzIHRlc3QgYW5kIHotc2NvcmUqKg0KDQojIyMgKipBL0IgdGVzdGluZyoqDQoNClRoaXMgYW4gZXhwZXJpbWVudGFsIGRlc2lnbiB0ZWNobmlxdWUsIHdoaWNoIGhhcyByb290cyBpbiBoeXBvdGhlc2lzIHRlc3RpbmcsIHRvIHRlc3QgZGlmZmVyZW50IHNjZW5hcmlvcyBhbmQgc2VlIHdoaWNoIGhhcyB0aGUgYmVzdCBwZXJmb3JtYW5jZQ0KDQojIyAqKlN0YWNrIE92ZXJmbG93IERldmVsb3BlciBTdXJ2ZXkgMjAyMCoqDQoNCkVhY2ggeWVhciwgU3RhY2sgT3ZlcmZsb3cgc3VydmV5cyBpdHMgdXNlcnMsIHdobyBhcmUgcHJpbWFyaWx5IHNvZnR3YXJlIGRldmVsb3BlcnMsIGFib3V0IHRoZW1zZWx2ZXMsIGhvdyB0aGV5IHVzZSBTdGFjayBPdmVyZmxvdywgdGhlaXIgd29yaywgYW5kIHRoZSBkZXZlbG9wbWVudCB0b29scyB0aGV5IHVzZS4gSW4gdGhpcyBhbmFseXNpcyB3ZSdsbCBsb29rIGF0IGEgc3Vic2V0IG9mIHRoZSBzdXJ2ZXkgcmVzcG9uc2VzLCBmcm9tIHVzZXJzIHdobyBpZGVudGlmaWVkIGFzIERhdGEgU2NpZW50aXN0cy4NCg0KYGBge3J9DQojIExvYWQgbGlicmFyaWVzDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZnN0KQ0KbGlicmFyeSh0aWJibGUpDQpsaWJyYXJ5KHJlYWRyKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpydXRhX3N0Y2tvdiA8LSAiQzovVXNlcnMvSnVhbkZlciBNb3NxdWVyYS9Eb2N1bWVudHMvZGF0YXNldHMvc3RhY2tfb3ZlcmZsb3cucmRzIg0KYGBgDQoNCmBgYHtyfQ0Kc3RhY2tfb3ZlcmZsb3cgPC0gcmVhZF9yZHMocnV0YV9zdGNrb3YpDQpzdGFja19vdmVyZmxvdw0KY29sbmFtZXMoc3RhY2tfb3ZlcmZsb3cpDQpgYGANCg0KYGBge3J9DQpnbGltcHNlKHN0YWNrX292ZXJmbG93KQ0KYGBgDQoNCiMjIyAqKkh5cG90aGVzaXppbmcgYWJvdXQgdGhlIG1lYW4qKg0KDQpMZXQncyBoeXBvdGhlc2l6ZSB0aGF0IHRoZSBtZWFuIGFubnVhbCBjb21wZW5zYXRpb24gb2YgdGhlIHBvcHVsYXRpb24gb2YgZGF0YSBzY2llbnRpc3RzIGlzIFwkMTEwMDAwLiBUaGUgZmlyc3QgdGhpbmcgd2UgY2FuIGRvIHRvIGNoZWNrIHRoaXMgaXMgdG8gZXhhbWluZSB0aGUgc2FtcGxlIGRhdGEgaW4gdGhlIHN1cnZleS4gQW5udWFsIGNvbXBlbnNhdGlvbiwgY29udmVydGVkIHRvIGRvbGxhcnMsIGlzIGluY2x1ZGVkIGluIHRoZSBjb252ZXJ0ZWRfY29tcCBjb2x1bW4uIFRoZSBzYW1wbGUgbWVhbiBpcyBhIHR5cGUgb2YgcG9pbnQgZXN0aW1hdGUsIHdoaWNoIGlzIGFub3RoZXIgbmFtZSBmb3IgYSBzdW1tYXJ5IHN0YXRpc3RpYy4gWW91IGNhbiBjYWxjdWxhdGUgaXQgd2l0aCBiYXNlLVIsIG9yIG1vcmUgdmVyYm9zZWx5IHdpdGggZHBseXIuIEVpdGhlciB3YXksIHRoZSBhbnN3ZXIgaXMgXCQxMjIwMDAuIFRoYXQncyBkaWZmZXJlbnQgZnJvbSBvdXIgaHlwb3RoZXNpcywgYnV0IGlzIGl0IG1lYW5pbmdmdWxseSBkaWZmZXJlbnQ/DQoNCmBgYHtyfQ0KbWVhbl9jb21wX3NhbXAgPC0gbWVhbihzdGFja19vdmVyZmxvdyRjb252ZXJ0ZWRfY29tcCkNCm1lYW5fY29tcF9zYW1wDQpgYGANCg0KYGBge3J9DQptZWFuX2NvbXBfc2FtcCA8LSBzdGFja19vdmVyZmxvdyAlPiUNCiAgc3VtbWFyaXNlKG1lYW5fY29tcGVuc2F0aW9uID0gbWVhbihjb252ZXJ0ZWRfY29tcCkpICU+JQ0KICBwdWxsKG1lYW5fY29tcGVuc2F0aW9uKQ0KbWVhbl9jb21wX3NhbXANCmBgYA0KDQojIyMgKipHZW5lcmF0aW5nIGEgYm9vdHN0cmFwIGRpc3RyaWJ1dGlvbioqDQoNClRvIGFuc3dlciB0aGlzLCB3ZSBuZWVkIHRvIGdlbmVyYXRlIGEgYm9vdHN0cmFwIGRpc3RyaWJ1dGlvbiBvZiBzYW1wbGUgbWVhbnMuIFRoaXMgaXMgZG9uZSBieSByZXNhbXBsaW5nIHRoZSBkYXRhc2V0LCBjYWxjdWxhdGluZyB0aGUgc2FtcGxlIG1lYW4gZm9yIHRoYXQgcmVzYW1wbGUsIHRoZW4gcmVwZWF0aW5nIHRob3NlIHN0ZXBzLg0KDQpgYGB7cn0NCiMgUmVwZWF0IHN0ZXBzIDEgJiAyIG1hbnkgdGltZXMgDQpzb19ib290X2Rpc3RuIDwtIHJlcGxpY2F0ZSgNCiAgbiA9IDUwMDAsDQogIGV4cHIgPSB7DQogICAgI1N0ZXAgMS4gUmVzYW1wbGUNCiAgICBzdGFja19vdmVyZmxvdyAlPiUNCiAgICAgIHNsaWNlX3NhbXBsZShwcm9wID0gMSwgcmVwbGFjZSA9IFRSVUUpICU+JQ0KICAgICAgIyBTdGVwIDIuIENhbGN1bGF0ZSBwb2ludCBlc3RpbWF0ZQ0KICAgICAgc3VtbWFyaXNlKG1lYW5fY29tcGVuc2F0aW9uID0gbWVhbihjb252ZXJ0ZWRfY29tcCkpICU+JQ0KICAgICAgcHVsbChtZWFuX2NvbXBlbnNhdGlvbikNCiAgfQ0KKQ0KaGVhZChzb19ib290X2Rpc3RuKQ0KYGBgDQoNCiMjIyAqKlZpc3VhbGl6aW5nIHRoZSBib290c3RyYXAgZGlzdHJpYnV0aW9uKioNCg0KSGVyZSdzIGEgaGlzdG9ncmFtIG9mIHRoZSBib290c3RyYXAgZGlzdHJpYnV0aW9uLiBJdHMgYmVsbCBzaGFwZSBtZWFucyBpdCdzIHJvdWdobHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIE5vdGljZSB0aGF0ICQxMTAwMDAkIGlzIG9uIHRoZSBsZWZ0IG9mIHRoZSBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KdGliYmxlKHJlc2FtcGxlX21lYW4gPSBzb19ib290X2Rpc3RuKSAlPiUNCiAgZ2dwbG90KGFlcyhyZXNhbXBsZV9tZWFuKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDAwKQ0KYGBgDQoNCiMjIyAqKlN0YW5kYXJkIEVycm9yKioNCg0KUmVjYWxsIHRoYXQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgc2FtcGxlIHN0YXRpc3RpY3MgaW4gdGhlIGJvb3RzdHJhcCBkaXN0cmlidXRpb24gZXN0aW1hdGVzIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgc3RhdGlzdGljLg0KDQpgYGB7cn0NCnN0ZF9lcnJvciA8LSBzZChzb19ib290X2Rpc3RuKQ0Kc3RkX2Vycm9yDQpgYGANCg0KIyMjICoqWi1zY29yZXMqKg0KDQpTaW5jZSB2YXJpYWJsZXMgaGF2ZSBhcmJpdHJhcnkgdW5pdHMgYW5kIHJhbmdlcywgYmVmb3JlIHdlIHRlc3Qgb3VyIGh5cG90aGVzaXMsIHdlIG5lZWQgdG8gc3RhbmRhcmRpemUgdGhlIHZhbHVlcy4gQSBjb21tb24gd2F5IG9mIHN0YW5kYXJkaXppbmcgdmFsdWVzIGlzIHRvICoqc3VidHJhY3QgdGhlIG1lYW4qKiwgYW5kICoqZGl2aWRlIGJ5IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24qKi4gRm9yIGh5cG90aGVzaXMgdGVzdGluZywgd2UgdXNlIGEgdmFyaWF0aW9uIHdoZXJlIHdlIHRha2UgdGhlICoqc2FtcGxlIHN0YXRpc3RpYywgc3VidHJhY3QgdGhlIGh5cG90aGVzaXplZCBwYXJhbWV0ZXIgdmFsdWUsIGFuZCBkaXZpZGUgYnkgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgc3RhdGlzdGljKiosICoqd2hpY2ggaXMgdGhlIHN0YW5kYXJkIGVycm9yKiouIFRoZSByZXN1bHQgaXMgY2FsbGVkIGEgKip6LXNjb3JlKiouIFRoZSBzYW1wbGUgbWVhbiBhbm51YWwgY29tcGVuc2F0aW9uIGZvciBkYXRhIHNjaWVudGlzdHMgb2YgXCQxMjIwMDAgLCBtaW51cyB0aGUgaHlwb3RoZXNpemVkIGNvbXBlbnNhdGlvbiBmb3IgZGF0YSBzY2llbnRpc3RzIG9mIFwkMTEwMDAwLCBkaXZpZGVkIGJ5IHRoZSBib290c3RyYXAgc3RhbmRhcmQgZXJyb3Igb2YgXCQ1NTkwIGdpdmVzIGEgei1zY29yZSAxLjcxDQoNCmBgYHtyfQ0KbWVhbl9jb21wX2h5cCA8LSAxMTAwMDANCm1lYW5fY29tcF9zYW1wDQoNCnpfc2NvcmUgPC0gKG1lYW5fY29tcF9zYW1wIC0gbWVhbl9jb21wX2h5cCkgLyBzdGRfZXJyb3INCnpfc2NvcmUNCmBgYA0KDQojIyMgKipUZXN0aW5nIHRoZSBoeXBvdGhlc2lzKioNCg0KSXMgdGhhdCBhIGJpZyBvciBzbWFsbCBudW1iZXI/IERldGVybWluaW5nIHRoYXQgaXMgdGhlIGdvYWwgb2YgdGhpcyBjb3Vyc2UuIEluIHBhcnRpY3VsYXIsIHdlIGNhbiBub3cgc3RhdGUgb25lIG9mIHRoZSB1c2VzIG9mIGh5cG90aGVzaXMgdGVzdGluZy4gRGV0ZXJtaW5pbmcgd2hldGhlciBhIHNhbXBsZSBzdGF0aXN0aWMgaXMgY2xvc2UgdG8gb3IgZmFyIGF3YXkgZnJvbSBhbiBleHBlY3RlZCB2YWx1ZS4NCg0KIyMjICoqU3RhbmRhcmQgbm9ybWFsICh6KSBkaXN0cmlidXRpb24qKg0KDQpIZXJlJ3MgYSBkZW5zaXR5IHBsb3Qgb2YgdGhlIHByb2JhYmlsaXR5IGRlbnNpdHkgZnVuY3Rpb24gZm9yIHRoZSBzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uLiBUaGF0IGlzLCBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuIHplcm8gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvbmUuIEl0J3Mgb2Z0ZW4gY2FsbGVkIHRoZSB6LWRpc3RyaWJ1dGlvbi4gQXMgeW91IG1pZ2h0IGd1ZXNzLCB6LXNjb3JlcyBhcmUgcmVsYXRlZCB0byB0aGUgei1kaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSBkYXRhc2V0IG9mIHotZGlzdHJpYnV0aW9uIGFuZCBwbG90IGl0DQp0aWJibGUoeCA9IHNlcSgtNCwgNCwgMC4wMSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHgpKSArIA0KICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtKSArIA0KICAgIHlsYWIoIlBERih4KSIpDQpgYGANCg0KIyMjICoqUC12YWx1ZXMqKg0KDQpIeXBvdGhlc2lzIHRlc3RzIGFyZSBsaWtlIGNyaW1pbmFsIHRyaWFscy4NCg0KVGhlIHRydWUgc2l0dWF0aW9uIGlzIHRoYXQgdGhlIGRlZmVuZGFudCBjb21taXR0ZWQgdGhlIGNyaW1lLCBvciB0aGV5IGRpZG4ndC4gVGhlcmUgYXJlIGFsc28gdHdvIHBvc3NpYmxlIG91dGNvbWVzLiBUaGUganVkZ2UgZ2l2ZXMgYSBndWlsdHkgb3IgYSBub3QgZ3VpbHR5IHZlcmRpY3QuIFRoZSBpbml0aWFsIGFzc3VtcHRpb24gaXMgdGhhdCB0aGUgZGVmZW5kYW50IGlzIG5vdCBndWlsdHkuIEl0J3MgdXAgdG8gdGhlIHByb3NlY3V0aW9uIHRlYW0gdG8gY29tZSB1cCB3aXRoIGV2aWRlbmNlIGJleW9uZCBhIHJlYXNvbmFibGUgZG91YnQgdGhhdCB0aGUgZGVmZW5kYW50IGNvbW1pdHRlZCB0aGUgY3JpbWUgaW4gb3JkZXIgZm9yIGEgZ3VpbHR5IHZlcmRpY3QgdG8gYmUgZ2l2ZW4uDQoNCiMjIyAqKkFnZSBvZiBmaXJzdCBwcm9ncmFtbWluZyBleHBlcmllbmNlKioNCg0KTGV0J3MgcmV0dXJuIHRvIG91ciBzdXJ2ZXkuIFRoZSBgYWdlX2ZpcnN0X2NvZGVfY3V0YCB2YXJpYWJsZSBjbGFzc2lmaWVzIHdoZW4gdGhlIHVzZXIgYmVnYW4gcHJvZ3JhbW1pbmcuIElmIHRoZXkgd2VyZSAxNCBvciBvbGRlciwgdGhleSBhcmUgY2xhc3NpZmllZCBhcyAqKiJhZHVsdCIqKjsgb3RoZXJ3aXNlICoqImNoaWxkIioqLg0KDQpTdXBwb3NlIHByZXZpb3VzIHJlc2VhcmNoIHN1Z2dlc3RzIHRoYXQgMzUlIG9mIHNvZnR3YXJlIGRldmVsb3BlcnMgcHJvZ3JhbW1lZCBhcyBjaGlsZHJlbi4gVGhpcyByYWlzZXMgYSBxdWVzdGlvbiBhbnN3ZXJhYmxlIHdpdGggb3VyIGRhdGFzZXQuDQoNCioqRG9lcyBvdXIgc2FtcGxlIHByb3ZpZGUgZXZpZGVuY2UgdGhhdCBkYXRhIHNjaWVudGlzdHMgaGF2ZSBhIGdyZWF0ZXIgcHJvcG9ydGlvbiBzdGFydGluZyBwcm9ncmFtbWluZyBhcyBhIGNoaWxkKio/DQoNCiMjIyAqKkRlZmluaXRpb25zKioNCg0KSHlwb3RoZXNpcyBpcyBhIHN0YXRlbWVudCBhYm91dCBhICoqcG9wdWxhdGlvbiBwYXJhbWV0ZXIqKi4gV2UgZG9uJ3Qga25vdyB0aGUgdHJ1ZSB2YWx1ZSBvZiB0aGlzIHBvcHVsYXRpb24gcGFyYW1ldGVyOyB3ZSBjYW4gb25seSBtYWtlIGluZmVyZW5jZXMgYWJvdXQgaXQgZnJvbSB0aGUgZGF0YS4gKipIeXBvdGhlc2lzIHRlc3RzIGNvbXBhcmUgdHdvIGNvbXBldGluZyBoeXBvdGhlc2VzKiouIFRoZXNlIHR3byBoeXBvdGhlc2VzIGFyZSB0aGUgKipudWxsIGh5cG90aGVzaXMqKiwgcmVwcmVzZW50aW5nIHRoZSBleGlzdGluZyBpZGVhLCBhbmQgdGhlICoqYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyoqLCByZXByZXNlbnRpbmcgYSBuZXcgaWRlYS4NCg0KVGhleSBhcmUgZGVub3RlZCAkSF97MH0kIGFuZCAkSF97QX0kIHJlc3BlY3RpdmVseS4gKipUaGUgbnVsbCBoeXBvdGhlc2lzIGlzIGxpa2UgdGhlIGN1cnJlbnQgY2hhbXBpb24sIGFuZCB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyBpcyBsaWtlIGEgY2hhbGxlbmdlciB0cnlpbmcgdG8gb3ZlcnRocm93IHRoYXQgY2hhbXBpb24qKi4gSGVyZSwgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0aGF0IG91ciBkYXRhIHdvbid0IHRlbGwgdXMgYW55dGhpbmcgbmV3LCBhbmQgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBkYXRhIHNjaWVudGlzdHMgc3RhcnRpbmcgcHJvZ3JhbW1pbmcgYXMgY2hpbGRyZW4gZm9sbG93cyB0aGUgcHJldmlvdXMgcmVzZWFyY2ggb24gc29mdHdhcmUgZGV2ZWxvcGVycywgYXQgMzUlLiBUaGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyBpcyB0aGF0IG91ciBodW5jaCBpcyBjb3JyZWN0LCBhbmQgdGhhdCB0aGUgcGVyY2VudGFnZSBpcyBncmVhdGVyIHRoYW4gMzUlLg0KDQojIyMgKipDcmltaW5hbCB0cmlhbHMgdnMuIEh5cG90aGVzaXMgVGVzdGluZyoqDQoNCkxldCdzIGNvbXBhcmUgdGhlIGNyaW1pbmFsIHRyaWFsIHdpdGggdGhlIGh5cG90aGVzaXMgdGVzdC4gVGhlIGRlZmVuZGFudCBjb21taXR0aW5nIHRoZSBjcmltZSBpcyBlcXVpdmFsZW50IHRvIHRoZSAqKmFsdGVybmF0aXZlIGh5cG90aGVzaXMgYmVpbmcgdHJ1ZSoqLCBhbmQgdGhlIGRlZmVuZGFudCBub3QgY29tbWl0dGluZyB0aGUgY3JpbWUgaXMgZXF1aXZhbGVudCB0byB0aGUgKipudWxsIGh5cG90aGVzaXMgYmVpbmcgdHJ1ZSoqLiBSYXRoZXIgdGhhbiBzYXlpbmcgd2UgYWNjZXB0IHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzLCAqKnRoZSB2ZXJkaWN0cyBhcmUgcmVqZWN0aW5nIHRoZSBudWxsIGh5cG90aGVzaXMqKiwgb3IgKipmYWlsaW5nIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzKiouIEluaXRpYWxseSwgd2UgYXNzdW1lIHRoYXQgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLCBhbmQgdGhpcyBvbmx5IGNoYW5nZXMgaWYgdGhlIHNhbXBsZSBwcm92aWRlcyBlbm91Z2ggZXZpZGVuY2UgdG8gcmVqZWN0IGl0Lg0KDQpUaGUgaHlwb3RoZXNpcyB0ZXN0aW5nIGVxdWl2YWxlbnQgb2YgImJleW9uZCBhIHJlYXNvbmFibGUgZG91YnQiIGlzIGtub3duIGFzIHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwuDQoNCiMjIyAqKk9uZS10YWlsZWQgJiB0d28tdGFpbGVkIHRlc3RzKioNCg0KVGhlIHRhaWxzIG9mIGEgZGlzdHJpYnV0aW9uIGFyZSB0aGUgbGVmdCBhbmQgcmlnaHQgZWRnZXMgb2YgaXRzIFBERi4gKipIeXBvdGhlc2lzIHRlc3RzIGRldGVybWluZSB3aGV0aGVyIHRoZSBzYW1wbGUgc3RhdGlzdGljcyBsaWUgaW4gdGhlIHRhaWxzIG9mIHRoZSBkaXN0cmlidXRpb24qKi4gVGhlcmUgYXJlIHRocmVlIHR5cGVzIG9mIHRlc3RzLCBhbmQgdGhlIHBocmFzaW5nIG9mIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIGRldGVybWluZXMgd2hpY2ggdHlwZSB5b3Ugc2hvdWxkIHVzZS4gSW4gdGhpcyBjYXNlLCB3ZSBuZWVkIGEgKipyaWdodC10YWlsZWQgdGVzdCoqLg0KDQojIyMgKipQLXZhbHVlcyoqDQoNCioqcC12YWx1ZXMqKiBtZWFzdXJlIHRoZSBzdHJlbmd0aCBvZiBzdXBwb3J0IGZvciB0aGUgbnVsbCBoeXBvdGhlc2lzLiAqKlNtYWxsIHAtdmFsdWVzIG1lYW4gb3VyIHN0YXRpc3RpYyBpcyBwcm9kdWNpbmcgYW4gdW5saWtlbHkgcmVzdWx0IGluIGEgdGFpbCBvZiBvdXIgbnVsbCBkaXN0cmlidXRpb24qKi4gKipwLXZhbHVlcyoqIGFyZSBwcm9iYWJpbGl0aWVzLCBzbyB0aGV5IGFyZSBhbHdheXMgKipiZXR3ZWVuIHplcm8gYW5kIG9uZSoqLg0KDQojIyMgKipEZWZpbmluZyBwLXZhbHVlcyoqDQoNClRoZSBkZWZpbml0aW9uIG9mIGEgcC12YWx1ZSBoYXMgZm91ciBwYXJ0cy4gSXQncyBhIHByb2JhYmlsaXR5IHJlbGF0ZWQgdG8gd2hlcmUgdGhlIHN0YXRpc3RpYyBmcm9tIG91ciBzYW1wbGUgbGllcyBvbiB0aGUgbnVsbCBkaXN0cmlidXRpb24uICoqSXQgbWVhc3VyZXMgaG93IG1hbnkgdmFsdWVzIGZhbGwgZmFydGhlciBhd2F5IHRoYW4gd2hhdCB3ZSBvYnNlcnZlZCBpbiB0aGUgZGlyZWN0aW9uIG9mIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzKiouIEl0IGlzIGJhc2VkIG9uIHRoZSBudWxsIGRpc3RyaWJ1dGlvbiwgd2hpY2ggYXNzdW1lcyB0aGF0IHRoZSBudWxsIGh5cG90aGVzaXMgaXMgdHJ1ZS4gVGhlICoqcC12YWx1ZSoqIG1lYXN1cmVzIGV2aWRlbmNlLiBEb2VzIGl0IGZhdm9yIHRoZSBvcmlnaW5hbCBhc3N1bXB0aW9uIG9yIHRoZSBuZXcgYWx0ZXJuYXRpdmU/DQoNCiMjIyAqKkNhbGN1bGF0aW5nIHRoZSB6LXNjb3JlKioNCg0KVG8gY2FsY3VsYXRlIHRoZSAqKnAtdmFsdWUqKiwgd2UgbmVlZCB0byBydW4gdGhyb3VnaCB0aGUgc2FtZSBzdGVwcyBhcyBiZWZvcmUuIFdlIGdldCB0aGUgKipzYW1wbGUgc3RhdGlzdGljKiosIGluIHRoaXMgY2FzZSAqKnRoZSBwcm9wb3J0aW9uIG9mIGRhdGEgc2NpZW50aXN0cyB3aG8gc3RhcnRlZCBwcm9ncmFtbWluZyBhcyBjaGlsZHJlbioqLiBXZSBnZXQgdGhlIGh5cG90aGVzaXplZCB2YWx1ZSBmcm9tIHRoZSBudWxsIGh5cG90aGVzaXMsICQzNVwlJC4gV2UgZ2V0IHRoZSBzdGFuZGFyZCBlcnJvciBmcm9tIHRoZSBib290c3RyYXAgZGlzdHJpYnV0aW9uLiBUaGUgKip6LXNjb3JlKiogaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgcHJvcG9ydGlvbnMsIGRpdmlkZWQgYnkgdGhlIHN0YW5kYXJkIGVycm9yLg0KDQpgYGB7cn0NCnByb3BfY2hpbGRfc2FtcCA8LSBzdGFja19vdmVyZmxvdyAlPiUNCiAgc3VtbWFyaXNlKHBvaW50X2VzdGltYXRlID0gbWVhbihhZ2VfZmlyc3RfY29kZV9jdXQgPT0gImNoaWxkIikpICU+JQ0KICBwdWxsKHBvaW50X2VzdGltYXRlKQ0KDQpwcm9wX2NoaWxkX3NhbXANCmBgYA0KDQpgYGB7cn0NCnNkX2Jvb3RfZGlzdG4gPC0gcmVwbGljYXRlKA0KICBuID0gNTAwMCwNCiAgZXhwciA9IHsNCiAgICAjU3RlcCAxLiBSZXNhbXBsZQ0KICAgIHN0YWNrX292ZXJmbG93ICU+JQ0KICAgICAgc2xpY2Vfc2FtcGxlKHByb3AgPSAxLCByZXBsYWNlID0gVFJVRSkgJT4lDQogICAgICAjIFN0ZXAgMi4gQ2FsY3VsYXRlIHBvaW50IGVzdGltYXRlDQogICAgICBzdW1tYXJpc2Uoc2FtcGxlX21lYW4gPSBtZWFuKGFnZV9maXJzdF9jb2RlX2N1dCA9PSAiY2hpbGQiKSkgJT4lDQogICAgICBwdWxsKHNhbXBsZV9tZWFuKQ0KICB9DQopDQpoZWFkKHNkX2Jvb3RfZGlzdG4pDQpgYGANCg0KYGBge3J9DQpzdGRfZXJyID0gc2Qoc2RfYm9vdF9kaXN0bikNCnN0ZF9lcnINCmBgYA0KDQpgYGB7cn0NCnByb3BfY2hpbGRfaHlwIDwtIDAuMzUNCiNzdGRfZXJyIDwtIDAuMDA5NjAyOA0Kel9zY29yZSA8LSAocHJvcF9jaGlsZF9zYW1wIC0gcHJvcF9jaGlsZF9oeXApIC8gc3RkX2Vycg0Kel9zY29yZQ0KYGBgDQoNCiMjIyAqKkNhbGN1bGF0aW5nIHRoZSBwLXZhbHVlKioNCg0KVGhlIGxhc3Qgc3RlcCBpcyBuZXcuIFdlIHBhc3MgdGhlIHotc2NvcmUgdG8gdGhlIG5vcm1hbCBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbiwgcG5vcm0uIFNpbmNlIHdlIHdhbnQgdGhlIHJpZ2h0LWhhbmQgdGFpbCBmb3IgdGhpcyB0ZXN0LCB3ZSBzZXQgbG93ZXItZG90LXRhaWwgdG8gRkFMU0UuIFRoZSBwLXZhbHVlIGlzIDguMDQgZSAtMTAgVGhhdCdzIGEgdmVyeSBzbWFsbCBudW1iZXIsIGJ1dCBpcyBpdCBzbWFsbCBlbm91Z2ggdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXM/DQoNCmBgYHtyfQ0KcF92YWx1ZSA8LSBwbm9ybSh6X3Njb3JlLCBsb3dlci50YWlsID0gRkFMU0UpDQpwX3ZhbHVlDQpgYGANCg0KIyMjICoqU3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlKioNCg0KKipwLXZhbHVlcyoqIHF1YW50aWZ5IGhvdyBtdWNoIGV2aWRlbmNlIHRoZXJlIGlzIGZvciB0aGUgbnVsbCBoeXBvdGhlc2lzLiAqKkxhcmdlIHAtdmFsdWVzIGxlYWQgdXMgdG8gbm90IGhhdmUgZW5vdWdoIGV2aWRlbmNlIGZvciB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyoqLCBzdGlja2luZyB3aXRoIHRoZSBhc3N1bWVkIG51bGwgaHlwb3RoZXNpcyBpbnN0ZWFkLiAqKlNtYWxsIHAtdmFsdWVzIG1ha2UgdXMgZG91YnQgdGhpcyBvcmlnaW5hbCBhc3N1bXB0aW9uIGluIGZhdm9yIG9mIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzKiouIFdoYXQgZGVmaW5lcyB0aGUgY3V0b2ZmIHBvaW50IGJldHdlZW4gYSBzbWFsbCBwLXZhbHVlIGFuZCBhIGxhcmdlIG9uZT8NCg0KIyMjICoqU2lnbmlmaWNhbmNlIGxldmVsKioNCg0KVGhlIGN1dG9mZiBwb2ludCBpcyBrbm93biBhcyB0aGUgKipzaWduaWZpY2FuY2UgbGV2ZWwqKiwgYW5kIGlzIGRlbm90ZWQgYWxwaGEgKCoqzrEqKikuIFRoZSBhcHByb3ByaWF0ZSBzaWduaWZpY2FuY2UgbGV2ZWwgZGVwZW5kcyBvbiB0aGUgZGF0YXNldCBhbmQgdGhlIGRpc2NpcGxpbmUgeW91IGFyZSB3b3JraW5nIGluLiAqKjUlKiogaXMgdGhlIG1vc3QgY29tbW9uIGNob2ljZSwgYnV0ICoqMTAlKiogYW5kICoqMSUqKiBhcmUgYWxzbyBwb3B1bGFyLiBUaGUgc2lnbmlmaWNhbmNlIGxldmVsIGdpdmVzIHVzIGEgZGVjaXNpb24gcHJvY2VzcyBmb3Igd2hpY2ggaHlwb3RoZXNpcyB0byBzdXBwb3J0LiAqKklmIHRoZSBwLXZhbHVlIGlzIGxlc3MgdGhhbiBhbHBoYSoqLCB3ZSAqKnJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzKiouICoqT3RoZXJ3aXNlIHdlIGZhaWwgdG8gcmVqZWN0IGl0KiouIEl0J3MgaW1wb3J0YW50IHRoYXQgeW91ICoqZGVjaWRlIHdoYXQgdGhlIGFwcHJvcHJpYXRlIHNpZ25pZmljYW5jZSBsZXZlbCBzaG91bGQgYmUgYmVmb3JlIHlvdSBydW4geW91ciB0ZXN0KiouIE90aGVyd2lzZSB0aGVyZSBpcyBhIHRlbXB0YXRpb24gdG8gZGVjaWRlIG9uIGEgc2lnbmlmaWNhbmNlIGxldmVsIHRoYXQgbGV0cyB5b3UgY2hvb3NlIHRoZSBoeXBvdGhlc2lzIHlvdSB3YW50Lg0KDQojIyMgKipDYWxjdWxhdGluZyB0aGUgcC12YWx1ZSoqDQoNClRoZSB3b3JrZmxvdyBpcyBub3cgdG8gc2V0IHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwsIGluIHRoaXMgY2FzZSAwLjA1LiBUaGVuIGNhbGN1bGF0ZSB0aGUgei1zY29yZSBhbmQgdXNlIHRoZSBzdGFuZGFyZCBub3JtYWwgQ0RGIHRvIGdldCB0aGUgcC12YWx1ZS4gSW4gdGhpcyBjYXNlLCB0aGUgcC12YWx1ZSBvZiAqKjggZS0wNioqIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byBwb2ludC16ZXJvLWZpdmUsIHNvIHdlIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLiBXZSBoYXZlIHN0cm9uZyBldmlkZW5jZSBmb3IgdGhlIGFsdGVybmF0aXZlIGh5cG90aGVzaXMgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBkYXRhIHNjaWVudGlzdHMgc3RhcnRpbmcgcHJvZ3JhbW1pbmcgYXMgY2hpbGRyZW4gaXMgZ3JlYXRlciB0aGFuICoqMzUlKiouDQoNCmBgYHtyfQ0KYWxwaGEgPC0gMC4wNQ0KDQpwcm9wX2NoaWxkX3NhbXAgPC0gc3RhY2tfb3ZlcmZsb3cgJT4lDQogIHN1bW1hcmlzZShwb2ludF9lc3RpbWF0ZSA9IG1lYW4oYWdlX2ZpcnN0X2NvZGVfY3V0ID09ICJjaGlsZCIpKSAlPiUNCiAgcHVsbChwb2ludF9lc3RpbWF0ZSkNCg0KcHJvcF9jaGlsZF9oeXAgPC0gMC4zNSANCnN0ZF9lcnJvciA8LSAwLjAwOTYwMjgNCnpfc2NvcmUgPC0gKHByb3BfY2hpbGRfc2FtcCAtIHByb3BfY2hpbGRfaHlwKSAvIHN0ZF9lcnJvcg0KcF92YWx1ZSA9IHBub3JtKHpfc2NvcmUsIGxvd2VyLnRhaWwgPSBGQUxTRSkNCnBfdmFsdWUNCmBgYA0KDQpgYGB7cn0NCnBfdmFsdWUgPD0gYWxwaGENCmBgYA0KDQojIyMjIA0KDQokJA0KcC12YWx1ZSBcIGlzIFwgbGVzcyBcIHRoYW4gXCBvciBcIGVxdWFsIFwgdG8gXCBcYWxwaGEsIHNvIFwgcmVqZWN0IFwgSF97MH0gXCBhbmQgXCBhY2NlcHQgXCBIX3tBfQ0KJCQNCg0KIyMjICoqQ29uZmlkZW5jZSBpbnRlcnZhbHMqKg0KDQpUbyBnZXQgYSBzZW5zZSBhcyB0byBwb3RlbnRpYWwgdmFsdWVzIG9mIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlciwgaXQncyBjb21tb24gdG8gY2hvb3NlIGEgY29uZmlkZW5jZSBpbnRlcnZhbCBvZiBvbmUgbWludXMgdGhlIHNpZ25pZmljYW5jZSBsZXZlbC4gRm9yIGEgc2lnbmlmaWNhbmNlIGxldmVsIG9mICoqMC4wNSoqLCB3ZSdkIHVzZSBhIDk1JSBjb25maWRlbmNlIGludGVydmFsLiBIZXJlJ3MgdGhlIGNhbGN1bGF0aW9uIHVzaW5nIHRoZSBxdWFudGlsZSBtZXRob2QuIFRoZSBpbnRlcnZhbCBydW5zIGZyb20gKiowLjM3KiogdG8gKiowLjQxKiogZ2l2aW5nIGEgcmFuZ2Ugb2YgcGxhdXNpYmxlIHZhbHVlcyBmb3IgdGhlIHByb3BvcnRpb24gb2YgZGF0YSBzY2llbnRpc3RzIHN0YXJ0aW5nIHByb2dyYW1taW5nIGFzIGNoaWxkcmVuLg0KDQpgYGB7cn0NCmZpcnN0X2NvZGVfYm9vdF9zYW1wIDwtIHJlcGxpY2F0ZSgNCiAgbiA9IDUwMDAsDQogIGV4cHIgPSB7DQogICAgc3RhY2tfb3ZlcmZsb3cgJT4lDQogICAgICBzbGljZV9zYW1wbGUocHJvcCA9IDAuMjUsIHJlcGxhY2UgPSBUUlVFKSAlPiUNCiAgICAgIHN1bW1hcmlzZShwb2ludF9lc3RpbWF0ZSA9IG1lYW4oYWdlX2ZpcnN0X2NvZGVfY3V0ID09ICJjaGlsZCIpKSAlPiUNCiAgICAgIHB1bGwocG9pbnRfZXN0aW1hdGUpDQogIH0NCikNCg0KZmlyc3RfY29kZV9ib290X2Rpc3RuIDwtIHRpYmJsZShmaXJzdF9jb2RlX2NoaWxkX3JhdGUgPSBmaXJzdF9jb2RlX2Jvb3Rfc2FtcCkNCmhlYWQoZmlyc3RfY29kZV9ib290X2Rpc3RuKQ0KYGBgDQoNCmBgYHtyfQ0KY29uZl9pbnQgPC0gZmlyc3RfY29kZV9ib290X2Rpc3RuICU+JQ0KICBzdW1tYXJpc2UobG93ZXIgPSBxdWFudGlsZShmaXJzdF9jb2RlX2NoaWxkX3JhdGUsIDAuMDI1KSwNCiAgICAgICAgICAgIHVwcGVyID0gcXVhbnRpbGUoZmlyc3RfY29kZV9jaGlsZF9yYXRlLCAwLjk3NSkpDQoNCmNvbmZfaW50DQpgYGANCg0KIyMjICoqVHlwZXMgb2YgZXJyb3JzKioNCg0KUmV0dXJuaW5nIHRvIHRoZSBjcmltaW5hbCB0cmlhbCBhbmFsb2d5LCB0aGVyZSBhcmUgdHdvIHBvc3NpYmxlIHRydXRoIHN0YXRlcyBhbmQgdHdvIHBvc3NpYmxlIG91dGNvbWVzLCBmb3IgZm91ciBjb21iaW5hdGlvbnMgaW4gdG90YWwuIFR3byBvZiB0aGVzZSBpbmRpY2F0ZSB0aGF0IHRoZSB2ZXJkaWN0IHdhcyBjb3JyZWN0LiBJZiBhIGRlZmVuZGFudCBkaWRuJ3QgY29tbWl0IHRoZSBjcmltZSwgYnV0IHRoZSB2ZXJkaWN0IHdhcyBndWlsdHksIHRoZXkgYXJlIHdyb25nZnVsbHkgY29udmljdGVkLiBJZiB0aGUgZGVmZW5kYW50IGNvbW1pdHRlZCB0aGUgY3JpbWUgYnV0IHRoZSB2ZXJkaWN0IHdhcyBub3QgZ3VpbHR5LCB0aGV5IGdvdCBhd2F5IHdpdGggaXQuIFRoZXNlIGFyZSBib3RoIGVycm9ycyBpbiBqdXN0aWNlLiBTaW1pbGFybHksIGZvciBoeXBvdGhlc2lzIHRlc3RpbmcsIHRoZXJlIGFyZSB0d28gd2F5cyB0byBnZXQgaXQgcmlnaHQsIGFuZCB0d28gdHlwZXMgb2YgZXJyb3IuIElmIHlvdSBzdXBwb3J0IHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIHdoZW4gdGhlIG51bGwgaHlwb3RoZXNpcyB3YXMgY29ycmVjdCwgeW91IG1hZGUgYSBmYWxzZSBwb3NpdGl2ZSBlcnJvci4gSWYgeW91IHN1cHBvcnQgdGhlIG51bGwgaHlwb3RoZXNpcyB3aGVuIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIHdhcyBjb3JyZWN0LCB5b3UgbWFkZSBhIGZhbHNlIG5lZ2F0aXZlIGVycm9yLiBUaGVzZSBlcnJvcnMgYXJlIHNvbWV0aW1lcyBrbm93biBhcyB0eXBlIG9uZSBhbmQgdHlwZSB0d28gZXJyb3JzLg0KDQp8ICAgICAgICAgICAgICAgIHwgQWN0dWFsIEgwICAgICAgfCBBY3R1YWwgSEEgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS18DQp8ICoqQ2hvb3NlbiBIMCoqIHwgY29ycmVjdCAgICAgICAgfCBmYWxzZSBuZWdhdGl2ZSB8DQp8ICoqQ2hvb3NlbiBIQSoqIHwgZmFsc2UgcG9zaXRpdmUgfCBjb3JyZWN0ICAgICAgICB8DQoNCiMjIyAqKlBvc3NpYmxlIGVycm9ycyBpbiBvdXIgZXhhbXBsZSoqDQoNCiMjIyMgDQoNCiQkDQppZiBcIHAgXGxlcXNsYW50IFxhbHBoYSwgd2UgXCByZWplY3QgXCBcIEhfezB9IA0KJCQNCg0KQSBmYWxzZSBwb3NpdGl2ZSAoVHlwZSBJKSBlcnJvciBjb3VsZCBoYXZlIG9jdXJyZWQ6IFdlIHRob3VnaHQgdGhhdCBkYXRhIHNjaWVudGlzdHMgc3RhcnRpbmcgY29kaW5nIGFzIGNoaWxkcmVuIGF0IGEgaGlnaGVyIHJhdGUgd2hlbiBpbiByZWFsaXR5IHRoZXkgZGlkIG5vdA0KDQojIyMjIA0KDQokJA0KaWYgXCBwID4gXGFscGhhLCBcIHdlIFwgZmFpbCBcIHRvIFwgcmVqZWN0IFwgSF97MH0NCiQkDQoNCkEgZmFsc2UgbmVnYXRpdmUgKFR5cGUgSUkpIGVycm9yIGNvdWxkIGhhdmUgb2N1cnJlZDogd2UgdGhvdWdodCB0aGF0IGRhdGEgc2NpZW50aXN0cyBjb2RlZCBhcyBjaGlsZHJlbiBhdCB0aGUgc2FtZSByYXRlIGFzIHNvZnR3YXJlIGVuZ2luZWVycyB3aGVuIGluIHJlYWxpdHkgdGhleSBjb2RlZCBhcyBjaGlsZHJlbiBhdCBhIGhpZ2hlciByYXRlLg0KDQojIyMgKipQZXJmb3JtaW5nIHQtdGVzdHMqKg0KDQpIZXJlLCB3ZSdsbCBsb29rIGF0IHRoZSByZWxhdGVkIHByb2JsZW0gb2YgKipjb21wYXJpbmcgc2FtcGxlIHN0YXRpc3RpY3MgYWNyb3NzIGdyb3VwcyBvZiBhIHZhcmlhYmxlKiouIEluIHRoZSBTdGFjayBPdmVyZmxvdyBkYXRhc2V0LCBgY29udmVydGVkX2NvbXBgIGlzIGEgbnVtZXJpY2FsIHZhcmlhYmxlIG9mIGFubnVhbCBjb21wZW5zYXRpb24uIGBhZ2VfZmlyc3RfY29kZV9jdXRgIGlzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aCB0d28gbGV2ZWxzOiAqKmNoaWxkIGFuZCBhZHVsdCoqLCB3aGljaCBkZXNjcmliZSB3aGVuIHRoZSB1c2VyIHN0YXJ0ZWQgcHJvZ3JhbW1pbmcuIFdlIGNhbiBhc2sgcXVlc3Rpb25zIGFib3V0IGRpZmZlcmVuY2VzIGluIGNvbXBlbnNhdGlvbiBhY3Jvc3MgdGhlIHR3byBhZ2UgZ3JvdXBzLiBUaGF0IGlzLCAqKmRvIHVzZXJzIHdobyBmaXJzdCBwcm9ncmFtbWVkIGFzIGEgY2hpbGQgdGVuZCB0byBiZSBjb21wZW5zYXRlZCBoaWdoZXIgdGhhbiB0aG9zZSB0aGF0IHN0YXJ0ZWQgYXMgYWR1bHRzKio/DQoNCiMjIyAqKkh5cG90aGVzZXMqKg0KDQpUaGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRoYXQgKip0aGUgc2FtcGxlIG1lYW4gZm9yIHRoZSB0d28gZ3JvdXBzIGlzIHRoZSBzYW1lKiosIGFuZCB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyBpcyB0aGF0ICoqdGhlIHNhbXBsZSBzdGF0aXN0aWMgZm9yIHVzZXJzIHdobyBzdGFydGVkIGNvZGluZyBhcyBjaGlsZHJlbiBpcyBncmVhdGVyIHRoYW4gZm9yIHVzZXJzIHdobyBzdGFydGVkIGNvZGluZyBhcyBhZHVsdHMqKi4gV2UgY2FuIHdyaXRlIHRoZXNlIGh5cG90aGVzZXMgdXNpbmcgZXF1YXRpb25zLiBNdSAoKirCtSoqKSByZXByZXNlbnRzIGFuIHVua25vd24gcG9wdWxhdGlvbiBtZWFuLCBhbmQgd2UgdXNlIHN1YnNjcmlwdHMgdG8gZGVub3RlIHdoaWNoIGdyb3VwIHRoZSBzYW1wbGUgbWVhbiBiZWxvbmdzIHRvLiBBbiBhbHRlcm5hdGUgd2F5IG9mIHdyaXRpbmcgdGhlIGVxdWF0aW9ucyBpcyB0byBjb21wYXJlIHRoZSBkaWZmZXJlbmNlcyBpbiBzYW1wbGUgbWVhbnMgdG8gemVyby4NCg0KIyMjICoqQ2FsY3VsYXRpbmcgZ3JvdXAgd2lzZSBzdW1tYXJ5IHN0YXRpc3RpY3MqKg0KDQpDYWxjdWxhdGluZyB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciBlYWNoIGdyb3VwIGlzIHN0cmFpZ2h0Zm9yd2FyZC4gWW91IHN0YXJ0IHdpdGggdGhlIHNhbXBsZSwgZ3JvdXAgYnkgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlLCB0aGVuIHN1bW1hcml6ZSB0aGUgbnVtZXJpYyB2YXJpYWJsZS4gQSBgZHBseXJgIHdheSBvZiBkb2luZyB0aGlzIGlzIHNob3duLCBidXQgdGhlcmUgYXJlIG1hbnkgcG9zc2liaWxpdGllcy4gUGF1c2UgdGhlIHZpZGVvIGZvciBhIG1vbWVudCBhbmQgdGhpbmsgYWJvdXQgZGlmZmVyZW50IHdheXMgb2Ygc29sdmluZyB0aGlzLiBIZXJlLCB0aGUgY2hpbGQgcHJvZ3JhbW1lcnMgaGF2ZSBhIG1lYW4gY29tcGVuc2F0aW9uIG9mIFwkMTMyNDE5LjYgZG9sbGFycyBjb21wYXJlZCB0byBcJDExMTMxMy4zIGZvciBhZHVsdCBwcm9ncmFtbWVycy4gSXMgdGhhdCBpbmNyZWFzZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IG9yIGNvdWxkIGl0IGJlIGV4cGxhaW5lZCBieSBzYW1wbGluZyB2YXJpYWJpbGl0eT8NCg0KYGBge3J9DQpzdGFja19vdmVyZmxvdyAlPiUNCiAgZ3JvdXBfYnkoYWdlX2ZpcnN0X2NvZGVfY3V0KSAlPiUNCiAgc3VtbWFyaXNlKG1lYW5fY29tcGVuc2F0aW9uID0gbWVhbihjb252ZXJ0ZWRfY29tcCkpDQpgYGANCg0KIyMjICoqVGVzdCBzdGF0aXN0aWNzKioNCg0KQWx0aG91Z2ggd2UgZG9uJ3Qga25vdyB0aGUgcG9wdWxhdGlvbiBtZWFuLCAqKndlIGVzdGltYXRlIGl0IHVzaW5nIHRoZSBzYW1wbGUgbWVhbioqLiAqKngtYmFyKiogaXMgdXNlZCB0byBkZW5vdGUgYSBzYW1wbGUgbWVhbi4gVGhlbiB3ZSB1c2Ugc3Vic2NyaXB0cyB0byBkZW5vdGUgd2hpY2ggZ3JvdXAgYSBzYW1wbGUgbWVhbiBjb3JyZXNwb25kcyB0by4gKipUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZXNlIHR3byBzYW1wbGUgbWVhbnMgaXMgY2FsbGVkIHRoZSB0ZXN0IHN0YXRpc3RpYyBmb3IgdGhlIGh5cG90aGVzaXMgdGVzdCoqLiBUaGUgei1zY29yZXMgYXJlIGFub3RoZXIgdHlwZSBvZiB0ZXN0IHN0YXRpc3RpYy4NCg0KIyMjICoqU3RhbmRhcmRpemluZyB0aGUgdGVzdCBzdGF0aXN0aWMqKg0KDQp6LXNjb3JlcyBhcmUgY2FsY3VsYXRlZCBieSB0YWtpbmcgdGhlIHNhbXBsZSBzdGF0aXN0aWMsIHN1YnRyYWN0aW5nIHRoZSBtZWFuIG9mIHRoaXMgc3RhdGlzdGljIGFzIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlciBvZiBpbnRlcmVzdCwgdGhlbiBkaXZpZGluZyBieSB0aGUgc3RhbmRhcmQgZXJyb3IuIEluIHRoZSB0d28gc2FtcGxlIGNhc2UsIHRoZSB0ZXN0IHN0YXRpc3RpYywgZGVub3RlZCB0LCB1c2VzIGEgc2ltaWxhciBlcXVhdGlvbi4gWW91IHRha2UgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgc2FtcGxlIHN0YXRpc3RpY3MgZm9yIHRoZSB0d28gZ3JvdXBzLCBzdWJ0cmFjdCB0aGUgaHlwb3RoZXNpemVkIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHdvIGdyb3VwcywgdGhlbiBkaXZpZGUgYnkgdGhlIHN0YW5kYXJkIGVycm9yLiAkJA0KeCA9IA0KXGZyYWN7c2FtcGxlIFwgc3RhdCArIHBvcHVsYXRpb24gXCBwYXJhbWV0ZXJ9e3N0YW5kYXJkIFwgZXJyb3J9DQokJA0KDQokJA0KdCA9IFxmcmFje2RpZmZlcmVuY2UgXCBpbiBcIHNhbXBsZSBcIHN0YXRzIFwgLSBkaWZmZXJlbmNlIFwgaW4gXCBwb3B1bGF0aW9uIFwgcGFyYW1ldGVyc317c3RhbmRhcmQgXCBlcnJvcn0NCiQkDQoNCiQkDQp0ID0gXGZyYWN7KFxiYXJ7eH1fe2NoaWxkfSBcIC0gXGJhcnt4fV97YWR1bHR9KSBcIC0gKFxtdV97Y2hpbGR9IFwgLSBcbXVfe2FkdWx0fSl9e1NFKFxiYXJ7eH1fe2NoaWxkfSBcIC0gXGJhcnt4fV97YWR1bHR9KX0NCiQkDQoNCiMjIyAqKlN0YW5kYXJkIEVycm9yKioNCg0KVG8gY2FsY3VsYXRlIHRoZSBzdGFuZGFyZCBlcnJvciwgbmVlZGVkIGZvciB0aGUgZGVub21pbmF0b3Igb2YgdGhlIHRlc3Qgc3RhdGlzdGljIGVxdWF0aW9uLCBib290c3RyYXBwaW5nIGlzIG1vc3QgYWNjdXJhdGUuIEhvd2V2ZXIsIHRoZXJlIGlzIGFuIGVhc2llciB3YXkgdG8gYXBwcm94aW1hdGUgaXQuIFlvdSBjYWxjdWxhdGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgbnVtZXJpYyB2YXJpYWJsZSBmb3IgZWFjaCBncm91cCBpbiB0aGUgc2FtcGxlLCBhbmQgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZm9yIGVhY2ggZ3JvdXAuIFRoZW4gaXQncyBqdXN0IHNvbWUgYXJpdGhtZXRpYy4NCg0KIyMjICoqQXNzdW1pbmcgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlKioNCg0KSGVyZSdzIHRoZSB0ZXN0IHN0YXRpc3RpYyBlcXVhdGlvbiBhZ2Fpbi4gSWYgd2UgYXNzdW1lIHRoYXQgdGhlICoqbnVsbCBoeXBvdGhlc2lzIGlzIHRydWUqKiwgdGhlcmUncyBhIHNpbXBsaWZpY2F0aW9uIHdlIGNhbiBtYWtlLiAqKlRoZSBudWxsIGh5cG90aGVzaXMgYXNzdW1lcyB0aGF0IHRoZSBwb3B1bGF0aW9uIG1lYW5zIGFyZSBlcXVhbCoqLiBUaGF0IG1ha2VzIG9uZSB0ZXJtIGluIHRoZSBudW1lcmF0b3IgZGlzYXBwZWFyLiBVc2luZyB0aGUgYXBwcm94aW1hdGlvbiBmb3IgdGhlIHN0YW5kYXJkIGVycm9yLCB3ZSBub3cgaGF2ZSBhIHdheSBvZiBjYWxjdWxhdGluZyB0aGUgdGVzdCBzdGF0aXN0aWMgdXNpbmcgb25seSBjYWxjdWxhdGlvbnMgb24gdGhlIHNhbXBsZSBkYXRhc2V0LiBXZSBuZWVkIHRoZSBtZWFuLCBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZvciBlYWNoIGdyb3VwLiBUaGlzIGlzIGFub3RoZXIgZ3JvdXAtYnkgYW5kIHN1bW1hcml6ZSBjb21iaW5hdGlvbi4gJCQNClNFKFxiYXJ7eH1fe2NoaWxkfSBcIC0gXGJhcnt4fV97YWR1bHR9KSBcYXBwcm94IFxzcXJ0e1xmcmFje3NeMl97Y2hpbGR9fXtuX3tjaGlsZH19IFwgKyBcZnJhY3tzXjJfe2FkdWx0fX17bl97YWR1bHR9fX0NCiQkICQkDQpIX3swfTogXG11X3tjaGlsZH0gXCAtIFxtdV97YWR1bHR9ID0gMA0KJCQNCg0KJCQNCnQgPSBcZnJhY3soXGJhcnt4fV97Y2hpbGR9IFwgLSBcYmFye3h9X3thZHVsdH0pfXtTRShcYmFye3h9X3tjaGlsZH0gXCAtIFxiYXJ7eH1fe2FkdWx0fSl9DQokJA0KDQokJA0KdCA9IFxmcmFjeyhcYmFye3h9X3tjaGlsZH0gXCAtIFxiYXJ7eH1fe2FkdWx0fSl9e1xzcXJ0e1xmcmFje3NeMl97Y2hpbGR9fXtuX3tjaGlsZH19fSBcICsgXGZyYWN7c14yX3thZHVsdH19e25fe2FkdWx0fX19DQokJA0KDQpgYGB7cn0NCmdyb3VwX3Rlc3RzIDwtIHN0YWNrX292ZXJmbG93ICU+JQ0KICBncm91cF9ieShhZ2VfZmlyc3RfY29kZV9jdXQpICU+JQ0KICBzdW1tYXJpc2UoeF9iYXIgPSBtZWFuKGNvbnZlcnRlZF9jb21wKSwgcyA9IHNkKGNvbnZlcnRlZF9jb21wKSwgbiA9IG4oKSkNCg0KIyBTZWUgdGhlIHJlc3VsdHMNCmdyb3VwX3Rlc3RzDQpgYGANCg0KIyMjICoqQ2FsY3VsYXRpbmcgdGhlIHRlc3Qgc3RhdGlzdGljKioNCg0KQW5ub3lpbmdseSwgaXQgcmVxdWlyZXMgc29tZSBncmltIGRhdGEgbWFuaXB1bGF0aW9uIGNvZGUgdG8gY2FsY3VsYXRlIHRoZSB0ZXN0IHN0YXRpc3RpYyBmcm9tIHRoaXMgZGF0YSBmcmFtZS4gU2luY2UgdGhpcyBpc24ndCBhIGNvdXJzZSBvbiBkYXRhIG1hbmlwdWxhdGlvbiwgbGV0J3MgZG8gdGhlIGNhbGN1bGF0aW9uIHdpdGggc2VwYXJhdGUgdmFyaWFibGVzLiBUaGUgbnVtZXJhdG9yIGlzIGEgc2ltcGxlIHN1YnRyYWN0aW9uLCBhbmQgdGhlIGRlbm9taW5hdG9yIGlzIGxpa2UgYSB3ZWlnaHRlZCBoeXBvdGVudXNlLiBUaGUgdC1zdGF0aXN0aWMgaXMgKioxLjg2KiouIEp1c3QgYXMgd2l0aCB6LXNjb3Jlcywgd2UgY2FuJ3QgZG8gYW55dGhpbmcgd2l0aCB0aGF0IG51bWJlciB5ZXQNCg0KIyMjIyANCg0KJCQNCnQgPSBcZnJhY3soXGJhcnt4fV97Y2hpbGR9IFwgLSBcYmFye3h9X3thZHVsdH0pfXtcc3FydHtcZnJhY3tzXjJfe2NoaWxkfX17bl97Y2hpbGR9fSBcICsgXGZyYWN7c14yX3thZHVsdH19e25fe2FkdWx0fX19fQ0KJCQNCg0KYGBge3J9DQpudW1lcmF0b3IgPC0gZ3JvdXBfdGVzdHMkeF9iYXJbMl0gLSBncm91cF90ZXN0cyR4X2JhclsxXQ0KZGVub21pbmF0b3IgPC0gc3FydCgoZ3JvdXBfdGVzdHMkc1syXV4yIC8gZ3JvdXBfdGVzdHMkblsyXSkgKyAoZ3JvdXBfdGVzdHMkc1sxXV4yIC8gZ3JvdXBfdGVzdHMkblsxXSkpDQp0X3N0YXQgPC0gbnVtZXJhdG9yIC8gZGVub21pbmF0b3INCnRfc3RhdA0KYGBgDQoNCiMjIyAqKkh5cG90aGVzaXMgdGVzdGluZyB3b3JrZmxvdyoqDQoNCjEuICBJZGVudGlmeSBwb3B1bGF0aW9uIHBhcmFtZXRlciB0aGF0IGlzIGh5cG90aGVzaXplZCBhYm91dA0KDQoyLiAgU3BlY2lmeSB0aGUgbnVsbCAmIGFsdGVybmF0aXZlIGh5cG90aGVzaXMNCg0KMy4gIERldGVybWluZSAoc3RhbmRhcmRpemVkKSB0ZXN0IHN0YXRpc3RpYyAmIGNvcnJlc3BvbmRpbmcgbnVsbCBkaXN0cmlidXRpb24NCg0KNC4gIENvbmR1Y3QgaHlwb3RoZXNpcyB0ZXN0IGluIFINCg0KNS4gIE1lYXN1cmUgZXZpZGVuY2UgYWdhaW5zdCB0aGUgbnVsbCBoeXBvdGhlc2lzDQoNCjYuICBNYWtlIGEgZGVjaXNpb24gY29tcGFyaW5nIGV2aWRlbmNlIHRvIHNpZ25pZmljYW5jZSBsZXZlbA0KDQo3LiAgSW50ZXJwcmV0IHRoZSByZXN1bHRzIGluIHRoZSBjb250ZXh0IG9mIHRoZSBvcmlnaW5hbCBwcm9ibGVtDQoNCiMjIyAqKkNhbGN1bGF0aW5nIHAtdmFsdWVzIGZyb20gdC1zdGF0aXN0aWNzKioNCg0KVGhlIHRlc3Qgc3RhdGlzdGljLCB0LCBmb2xsb3dzIGEgdC1kaXN0cmlidXRpb24uIHQtZGlzdHJpYnV0aW9ucyBoYXZlIGEgcGFyYW1ldGVyIGNhbGxlZCB0aGUgKipkZWdyZWVzIG9mIGZyZWVkb20qKiwgb3IgKipkZioqIGZvciBzaG9ydC4gSGVyZSdzIGEgbGluZSBwbG90IG9mIHRoZSBQREYgb2YgYSB0LWRpc3RyaWJ1dGlvbiB3aXRoIG9uZSBkZWdyZWUgb2YgZnJlZWRvbSBpbiB5ZWxsb3csIGFuZCB0aGUgUERGIG9mIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpbiBibHVlIGRhc2hlcy4gTm90aWNlIHRoYXQgdGhlIHQtZGlzdHJpYnV0aW9uIGZvciBzbWFsbCBkZWdyZWVzIG9mIGZyZWVkb20gaGFzIGZhdHRlciB0YWlscyB0aGFuIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uLCBidXQgb3RoZXJ3aXNlIHRoZXkgbG9vayBzaW1pbGFyLg0KDQojIyMgKipEZWdyZWVzIG9mIGZyZWVkb20qKg0KDQpBcyB5b3UgaW5jcmVhc2UgdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSwgdGhlIHQtZGlzdHJpYnV0aW9uIGdldHMgY2xvc2VyIHRvIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uLiBJbiBmYWN0LCAqKmEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpcyBhIHQtZGlzdHJpYnV0aW9uIHdpdGggaW5maW5pdGUgZGVncmVlcyBvZiBmcmVlZG9tKiouIERlZ3JlZXMgb2YgZnJlZWRvbSBhcmUgZGVmaW5lZCBhcyAqKnRoZSBtYXhpbXVtIG51bWJlciBvZiBsb2dpY2FsbHkgaW5kZXBlbmRlbnQgdmFsdWVzIGluIHRoZSBkYXRhIHNhbXBsZSoqLiBUaGF0J3MgYSBmYWlybHkgdHJpY2t5IGNvbmNlcHQsIHNvIGxldCdzIHRyeSBhbiBleGFtcGxlLg0KDQojIyMgKipDYWxjdWxhdGluZyBkZWdyZWVzIG9mIGZyZWVkb20qKg0KDQpTdXBwb3NlIHlvdXIgZGF0YXNldCBoYXMgNSBpbmRlcGVuZGVudCBvYnNlcnZhdGlvbnMuIEZvdXIgb2YgdGhlIHZhbHVlcyBhcmUgMiwgNiwgOCwgYW5kIDUuIFlvdSBhbHNvIGtub3cgdGhlIHNhbXBsZSBtZWFuIGlzIDUuIFRoZSBsYXN0IHZhbHVlIGlzIG5vIGxvbmdlciBpbmRlcGVuZGVudDsgaXQgbXVzdCBiZSA0LiBFdmVuIHRob3VnaCBhbGwgZml2ZSBvYnNlcnZhdGlvbnMgaW4gdGhlIHNhbXBsZSB3ZXJlIGluZGVwZW5kZW50LCBiZWNhdXNlIHlvdSBrbm93IGFuIGFkZGl0aW9uYWwgZmFjdCBhYm91dCBpdCwgeW91IG9ubHkgaGF2ZSA0IGRlZ3JlZXMgb2YgZnJlZWRvbS4gSW4gb3VyIHR3byBzYW1wbGUgY2FzZSwgdGhlcmUgYXJlIGFzIG1hbnkgZGVncmVlcyBvZiBmcmVlZG9tIGFzIG9ic2VydmF0aW9ucywgbWludXMgdHdvIGJlY2F1c2Ugd2Uga25vdyB0d28gc2FtcGxlIHN0YXRpc3RpY3MuDQoNCiMjIyAqKkh5cG90aGVzZXMqKg0KDQpSZWNhbGwgdGhlIGh5cG90aGVzZXMgZm9yIG91ciBTdGFjayBPdmVyZmxvdyBxdWVzdGlvbiBhYm91dCBjb21wZW5zYXRpb24gZm9yIHRoZSB0d28gYWdlIGdyb3Vwcy4gU2luY2UgdGhpcyBpcyBhICJncmVhdGVyIHRoYW4iIGFsdGVybmF0aXZlIGh5cG90aGVzaXMsIHdlIG5lZWQgYSByaWdodC10YWlsZWQgdGVzdC4NCg0KJEhfezB9OiQgKlRoZSBtZWFuIGNvbXBlbnNhdGlvbiAoaW4gVVNEKSBpcyB0aGUgc2FtZSBmb3IgdGhvc2UgdGhhdCBjb2RlZCBmaXJzdCBhcyBhIGNoaWxkIGFuZCB0aG9zZSB0aGF0IGNvZGVkIGZpcnN0IGFzIGFuIGFkdWx0LioNCg0KJEhfe0F9OiQgKlRoZSBtZWFuIGNvbXBlbnNhdGlvbiAoaW4gVVNEKSBpcyAqKmdyZWF0ZXIqKiBmb3IgdGhvc2UgdGhhdCBjb2RlZCBmaXJzdCBhcyBhIGNoaWxkIGNvbXBhcmVkIHRvIHRob3NlIHRoYXQgY29kZWQgZmlyc3QgYXMgYW4gYWR1bHQuKg0KDQpVc2UgYSAqKnJpZ2h0LXRhaWxlZCB0ZXN0KioNCg0KIyMjICoqU2lnbmlmaWNhbmNlIGxldmVsKioNCg0KV2UncmUgZ29pbmcgdG8gY2FsY3VsYXRlIGEgcC12YWx1ZSBpbiBhIG1vbWVudC4gV2UgbmVlZCB0byBkZWNpZGUgb24gYSBzaWduaWZpY2FuY2UgbGV2ZWwgYmVmb3JlIHdlIGRvIHRoYXQuIFRoZXJlIGFyZSBzZXZlcmFsIHBvc3NpYmlsaXRpZXM7IEknbSBnb2luZyB0byB1c2UgMC4xDQoNClRoYXQgbWVhbnMgdGhhdCBpZiB0aGUgcC12YWx1ZSBpcyBsZXNzIHRoYW4gcG9pbnQtb25lLCB3ZSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyBpbiBmYXZvciBvZiB0aGUgYWx0ZXJuYXRpdmUuDQoNCiQkXGFscGhhID0gMC4xJCQNCg0KJCQNCmlmIFwgcCBcbGVxc2xhbnQgXGFscGhhIFwgdGhlbiBcIHJlamVjdCBcIEhfezB9DQokJA0KDQojIyMgKipDYWxjdWxhdGluZyBwLXZhbHVlczogT25lIHByb3BvcnRpb24gdnMuIGEgdmFsdWUqKg0KDQoqKmBwX3ZhbHVlIDwtIHBub3JtKHpfc2NvcmUsIGxvZXIudGFpbCA9IEZBTFNFKWAqKg0KDQojIyMgKipDYWxjdWxhdGluZyBwLXZhbHVlczogVHdvIG1lYW5zIGZyb20gZGlmZmVyZW50IGdyb3VwcyoqDQoNCiQkDQp0ID0gXGZyYWN7KFxiYXJ7eH1fe2NoaWxkfSBcIC0gXGJhcnt4fV97YWR1bHR9KX17XHNxcnR7XGZyYWN7c14yX3tjaGlsZH19e25fe2NoaWxkfX0gXCArIFxmcmFje3NeMl97YWR1bHR9fXtuX3thZHVsdH19fX0NCiQkDQoNCk5vdyB3ZSBhcmUgY2FsY3VsYXRpbmcgbWVhbnMgcmF0aGVyIHRoYW4gcHJvcG9ydGlvbnMsIHRoZSB6LXNjb3JlIGlzIHJlcGxhY2VkIHdpdGggYSB0LXRlc3Qgc3RhdGlzdGljLiBUaGUgY2FsY3VsYXRpb24gYWxzbyBuZWVkcyB0aGUgZGVncmVlcyBvZiBmcmVlZG9tLCB3aGljaCBpcyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBib3RoIGdyb3VwcywgbWludXMgdHdvLg0KDQpJbiB0aGUgcHJldmlvdXMgc3RlcHMsIHdlIHVzZWQgYW4gYXBwcm94aW1hdGlvbiB1c2luZyBzYW1wbGUgaW5mb3JtYXRpb24gKG5vdCBib290c3RyYXBwaW5nKSBmb3IgdGhlIHRlc3Qgc3RhdGlzdGljIHN0YW5kYXJkIGVycm9yLiBBIGNvbnNlcXVlbmNlIG9mIHRoaXMgaXMgdGhhdCB0byBjYWxjdWxhdGUgdGhlIHAtdmFsdWUsIHdlIG5lZWQgdG8gdHJhbnNmb3JtIHRoZSB0ZXN0IHN0YXRpc3RpYyB1c2luZyB0aGUgdC1kaXN0cmlidXRpb24gQ0RGIGluc3RlYWQgb2YgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gQ0RGLiBVc2luZyB0aGlzIGFwcHJveGltYXRpb24gYWRkcyBtb3JlIHVuY2VydGFpbnR5IGFuZCB0aGF0J3Mgd2h5IHRoaXMgaXMgYSB0IGluc3RlYWQgb2YgYSB6IHByb2JsZW0uDQoNClRoZSB0IGRpc3RyaWJ1dGlvbiBhbGxvd3MgZm9yIG1vcmUgdW5jZXJ0YWludHkgd2hlbiB1c2luZyBtdWx0aXBsZSBlc3RpbWF0ZXMgaW4gYSBzaW5nbGUgc3RhdGlzdGljIGNhbGN1bGF0aW9uLiBOb3RpY2UgdGhlIHVzZSBvZiBgcHQoKWAgaW5zdGVhZCBvZiBgcG5vcm0oKWAsIGFuZCB0aGF0IHRoZSBkZiBhcmd1bWVudCBpcyBzZXQgdG8gdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbS4gVGhpcyBwLXZhbHVlIGlzIGxlc3MgdGhhbiB0aGUgc2lnbmlmaWNhbmNlIGxldmVsIG9mICoqMC4xKiosIHdlIHNob3VsZCByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyBpbiBmYXZvciBvZiB0aGUgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyB0aGF0IFN0YWNrIE92ZXJmbG93IGRhdGEgc2NpZW50aXN0cyB3aG8gc3RhcnRlZCBjb2RpbmcgYXMgYSBjaGlsZCBlYXJuIG1vcmUuDQoNCmBgYHtyfQ0KbnVtZXJhdG9yIDwtIGdyb3VwX3Rlc3RzJHhfYmFyWzJdIC0gZ3JvdXBfdGVzdHMkeF9iYXJbMV0NCmRlbm9taW5hdG9yIDwtIHNxcnQoKGdyb3VwX3Rlc3RzJHNbMl1eMiAvIGdyb3VwX3Rlc3RzJG5bMl0pICsgKGdyb3VwX3Rlc3RzJHNbMV1eMiAvIGdyb3VwX3Rlc3RzJG5bMV0pKQ0KdF9zdGF0IDwtIG51bWVyYXRvciAvIGRlbm9taW5hdG9yDQp0X3N0YXQNCmBgYA0KDQpgYGB7cn0NCmRlZ3JlZXNfb2ZfZnJlZWRvbSA8LSBncm91cF90ZXN0cyRuWzJdICsgZ3JvdXBfdGVzdHMkblsxXSAtIDINCmRlZ3JlZXNfb2ZfZnJlZWRvbQ0KYGBgDQoNCmBgYHtyfQ0KcF92YWx1ZSA8LSBwdCh0X3N0YXQsIGRmID0gZGVncmVlc19vZl9mcmVlZG9tLCBsb3dlci50YWlsID0gRkFMU0UpDQpwX3ZhbHVlDQpgYGANCg0KIyMjICoqV2h5IGlzIHQgbmVlZGVkPyoqDQoNClRoZSBwcm9jZXNzIGZvciBjYWxjdWxhdGluZyBwLXZhbHVlcyBpcyB0byBzdGFydCB3aXRoIHRoZSBzYW1wbGUgc3RhdGlzdGljLCBzdGFuZGFyZGl6ZSBpdCB0byBnZXQgYSB0ZXN0IHN0YXRpc3RpYywgdGhlbiB0cmFuc2Zvcm0gaXQgdmlhIGEgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24uIFByZXZpb3VzbHksIHRoYXQgZmluYWwgdHJhbnNmb3JtYXRpb24gd2FzIGRlbm90ZWTCoGB6YCwgYW5kIHRoZSBDREYgdHJhbnNmb3JtYXRpb24gdXNlZCB0aGUgKHN0YW5kYXJkIG5vcm1hbCkgei1kaXN0cmlidXRpb24uIFRoZSB0ZXN0IHN0YXRpc3RpYyB3YXMgZGVub3RlZMKgYHRgLCBhbmQgdGhlIHRyYW5zZm9ybWF0aW9uIHVzZWQgdGhlIHQtZGlzdHJpYnV0aW9uLg0KDQpJbiB3aGljaCBoeXBvdGhlc2lzIHRlc3Rpbmcgc2NlbmFyaW8gaXMgYSB0LWRpc3RyaWJ1dGlvbiBuZWVkZWQgaW5zdGVhZCBvZiB0aGUgei1kaXN0cmlidXRpb24/DQoNCioqVXNpbmcgYSBzYW1wbGUgc3RhbmRhcmQgZGV2aWF0aW9uIHRvIGVzdGltYXRlIHRoZSBzdGFuZGFyZCBlcnJvciBpcyBjb21wdXRhdGlvbmFsbHkgZWFzaWVyIHRoYW4gdXNpbmcgYm9vdHN0cmFwcGluZy4gSG93ZXZlciwgdG8gY29ycmVjdCBmb3IgdGhlIGFwcHJveGltYXRpb24sIHlvdSBuZWVkIHRvIHVzZSBhIHQtZGlzdHJpYnV0aW9uIHdoZW4gdHJhbnNmb3JtaW5nIHRoZSB0ZXN0IHN0YXRpc3RpYyB0byBnZXQgdGhlIHAtdmFsdWUuKioNCg0KIyMgKipBTk9WQSB0ZXN0cyoqDQoNCldlwrR2ZSBzZWVuIGhvdyB0byBjb21wYXJlIHR3byBncm91cHMgaW4gdGhlIHVucGFpcmVkIGFuZCBwYWlyZWQgY2FzZXMuIFdoYXQgaWYgdGhlcmUgYXJlIG1vcmUgdGhhbiB0d28gZ3JvdXBzPw0KDQojIyMgKipKb2Igc2F0aXNmYWN0aW9uOiA1IGNhdGVnb3JpZXMqKg0KDQpUaGUgU3RhY2sgT3ZlcmZsb3cgc3VydmV5IGluY2x1ZGVzIGEgam9iIHNhdGlzZmFjdGlvbiB2YXJpYWJsZSwgd2l0aCBmaXZlIGNhdGVnb3JpZXMgZnJvbSAiVmVyeSBkaXNzYXRpc2ZpZWQiIHRvICJWZXJ5IHNhdGlzZmllZCIuDQoNCmBgYHtyfQ0Kc3RhY2tfb3ZlcmZsb3cgJT4lDQogIGNvdW50KGpvYl9zYXQpDQpgYGANCg0KIyMjICoqVmlzdWFsaXppbmcgbXVsdGlwbGUgZGlzdHJpYnV0aW9ucyoqDQoNClN1cHBvc2Ugd2Ugd2FudCB0byBrbm93IGlmIG1lYW4gYW5udWFsIGNvbXBlbnNhdGlvbiBpcyBkaWZmZXJlbnQgZm9yIGVhY2ggb2YgdGhlIGxldmVscyBvZiBqb2Igc2F0aXNmYWN0aW9uLiBUaGUgZmlyc3QgdGhpbmcgdG8gZG8gaXMgdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb25zIHdpdGggYm94IHBsb3RzLiBJJ3ZlIHVzZWQgY29vcmRfZmxpcCB0byBzd2FwIHRoZSB4IGFuZCB5IGF4ZXMsIG1ha2luZyB0aGUgY2F0ZWdvcnkgbGFiZWxzIGVhc2llciB0byByZWFkLiAiVmVyeSBzYXRpc2ZpZWQiIGxvb2tzIHNsaWdodGx5IGhpZ2hlciB0aGFuIHRoZSBvdGhlcnMsIGJ1dCB0byBzZWUgaWYgdGhleSBhcmUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQsIHdlJ2xsIG5lZWQgdG8gdXNlIGh5cG90aGVzaXMgdGVzdHMuDQoNCiMjIyMgUXVlc3Rpb246IElzIG1lYW4gYW5udWFsIGNvbXBlbnNhdGlvbiBkaWZmZXJlbnQgZm9yIGRpZmZlcmVudCBsZXZlbHMgb2Ygam9iIHNhdGlzZmFjdGlvbj8NCg0KYGBge3J9DQpzdGFja19vdmVyZmxvdyAlPiUgDQogIGdncGxvdChhZXMoeCA9IGpvYl9zYXQsIHkgPSBjb252ZXJ0ZWRfY29tcCkpICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCiMjIyAqKkFuYWx5c2lzIG9mIHZhcmlhbmNlIChBTk9WQSkqKg0KDQoqKkFOT1ZBIHRlc3RzIGRldGVybWluZSB3aGV0aGVyIHRoZXJlIGFyZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBncm91cHMqKi4gRmlyc3QsIHlvdSBmaXQgYSBsaW5lYXIgcmVncmVzc2lvbi4gWW91IGNhbGwgYGxtKClgLCAqKnNwZWNpZnlpbmcgdGhlIG51bWVyaWMgdmFyaWFibGUgYXMgdGhlIHJlc3BvbnNlIG9uIHRoZSBsZWZ0LWhhbmQtc2lkZSBvZiB0aGUgZm9ybXVsYSwgYW5kIHRoZSBjYXRlZ29yaWVzIGFzIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZSBvbiB0aGUgcmlnaHQtaGFuZCBzaWRlKiouIFRoZW4geW91IGNhbGwgYGFub3ZhKClgIHRvIHBlcmZvcm0gYW4gYW5hbHlzaXMgb2YgdmFyaWFuY2UgdGVzdC4NCg0KSW4gdGhlIGBqb2Jfc2F0YCByb3csIHRoZSByaWdodC1oYW5kIGNvbHVtbiBjb250YWlucyBhIHAtdmFsdWUsIHdoaWNoIGlzICoqMC4wMDEzKiouIFRoZSB0d28gc3RhcnMgbmV4dCB0byBpdCB0ZWxsIHlvdSB0aGF0ICoqdGhpcyBwLXZhbHVlIGlzIHNpZ25pZmljYW50IGF0IHRoZSBwb2ludC16ZXJvLW9uZSBsZXZlbCoqLiBUaGF0IG1lYW5zIHRoYXQgYXQgbGVhc3QgdHdvIG9mIHRoZSBjYXRlZ29yaWVzIG9mIGpvYiBzYXRpc2ZhY3Rpb24gaGF2ZSBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZWlyIGNvbXBlbnNhdGlvbiBsZXZlbHMuIFRoZSBwcm9ibGVtIGlzIHRoYXQgdGhpcyBtZXRob2QgZG9lc24ndCB0ZWxsIHlvdSB3aGljaCB0d28gY2F0ZWdvcmllcyB0aGV5IGFyZS4gRm9yIHRoaXMgcmVhc29uLCBBTk9WQSBpcyBsZXNzIHVzZWZ1bCB0aGFuIHBhaXJ3aXNlIHQtdGVzdHMuDQoNCmBgYHtyfQ0KbWRsX2NvbXBfdnNfc2F0IDwtIGxtKGNvbnZlcnRlZF9jb21wIH4gam9iX3NhdCwgZGF0YSA9IHN0YWNrX292ZXJmbG93KQ0KbWRsX2NvbXBfdnNfc2F0DQpgYGANCg0KYGBge3J9DQphbm92YShtZGxfY29tcF92c19zYXQpDQpgYGANCg0KIyMjICoqUGFpcndpc2UgdGVzdHMqKg0KDQpUbyBjb21wYXJlIGFsbCBmaXZlIGNhdGVnb3JpZXMgb2Ygam9iIHNhdGlzZmFjdGlvbiB1c2luZyBoeXBvdGhlc2lzIHRlc3RzLCB3ZSBjYW4gdGVzdCBlYWNoIHBhaXIuIFRoZXJlIGFyZSB0ZW4gd2F5cyBvZiBjaG9vc2luZyB0d28gaXRlbXMgZnJvbSBhIHNldCBvZiBmaXZlLCBzbyB3ZSBoYXZlIHRlbiB0ZXN0cyB0byBwZXJmb3JtLiBXZSdsbCBzZXQgdGhlIHNpZ25pZmljYW5jZSBsZXZlbCB0byBwb2ludC10d28uDQoNCi0gICAkXG11X3t2ZXJ5IFwgZGlzc2F0aXNmaWVkfSBcbmVxIFxtdV97c2xpZ2h0bHkgXCBkaXNzYXRpc2ZpZWR9JA0KDQotICAgJFxtdV97dmVyeSBcIGRpc3NhdGlzZmllZH0gXG5lcSBcbXVfe05laXRoZXJ9JA0KDQotICAgJFxtdV97dmVyeSBcIGRpc3NhdGlzZmllZH0gXG5lcSBcbXVfe1NsaWdodGx5IFwgc2F0aXNmaWVkfSQNCg0KLSAgICRcbXVfe3ZlcnkgXCBkaXNzYXRpc2ZpZWR9IFxuZXEgXG11X3tWZXJ5IFwgc2F0aXNmaWVkfSQNCg0KLSAgICRcbXVfe1NsaWdodGx5IFwgZGlzc2F0aXNmaWVkfSBcbmVxIFxtdV97TmVpdGhlcn0kDQoNCi0gICAkXG11X3tTbGlnaHRseSBcIGRpc3NhdGlzZmllZH0gXG5lcSBcbXVfe1NsaWdodGx5IFwgc2F0aXNmaWVkfSQNCg0KLSAgICRcbXVfe1NsaWdodGx5IFwgZGlzc2F0aXNmaWVkfSBcbmVxIFxtdV97VmVyeSBcIHNhdGlzZmllZH0kDQoNCi0gICAkXG11X3tOZWl0aGVyfSBcbmVxIFxtdV97U2xpZ2h0bHkgXCBzYXRpc2ZpZWR9JA0KDQotICAgJFxtdV97TmVpdGhlcn0gXG5lcSBcbXVfe1ZlcnkgXCBzYXRpc2ZpZWR9JA0KDQotICAgJFxtdV97U2xpZ2h0bHkgXCBzYXRpc2ZpZWR9IFxuZXEgXG11X3tWZXJ5IFwgc2F0aXNmaWVkfSQNCg0KIyMjICoqcGFpcndpc2UudC50ZXN0KCkqKg0KDQpUbyBydW4gYWxsIHRoZXNlIGh5cG90aGVzaXMgdGVzdHMgaW4gb25lIGdvLCB5b3UgY2FuIHVzZSBgcGFpcndpc2UudC10ZXN0KClgLiBUaGUgZmlyc3QgYXJndW1lbnQgaXMgdGhlIG51bWVyaWMgdmFyaWFibGUgd2hvc2Ugc2FtcGxlIG1lYW5zIHlvdSBhcmUgaW50ZXJlc3RlZCBpbi4gVGhlIHNlY29uZCBhcmd1bWVudCBpcyB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgZGVmaW5pbmcgdGhlIGdyb3Vwcy4gV2UnbGwgZGlzY3VzcyBgcC5hZGp1c3QubWV0aG9kYCBzaG9ydGx5LiBUaGUgcmVzdWx0IHNob3dzIGEgbWF0cml4IG9mIHRlbiBwLXZhbHVlcy4gVGhyZWUgb2YgdGhlc2UgYXJlIGxlc3MgdGhhbiBvdXIgc2lnbmlmaWNhbmNlIGxldmVsIG9mIHBvaW50LXR3by4NCg0KYGBge3J9DQphbm92YV9yZXN1bHRzIDwtIHBhaXJ3aXNlLnQudGVzdChzdGFja19vdmVyZmxvdyRjb252ZXJ0ZWRfY29tcCwgc3RhY2tfb3ZlcmZsb3ckam9iX3NhdCwgcC5hZGp1c3QubWV0aG9kID0gIm5vbmUiKQ0KYW5vdmFfcmVzdWx0cw0KI3BfdmFsdWVzIDwtIGFzLnZlY3Rvcih1bmxpc3QobmEub21pdChhbm92YV9yZXN1bHRzJHAudmFsdWUpKSkNCnBfdmFsdWVzIDwtIGFzLnZlY3Rvcih1bmxpc3QoYW5vdmFfcmVzdWx0cyRwLnZhbHVlKSkNCnBfdmFsdWVzIDwtIG5hLm9taXQocF92YWx1ZXMpDQojcF92YWx1ZXMgPC0gYW5vdmFfcmVzdWx0cyRwLnZhbHVlW2NvbXBsZXRlLmNhc2VzKGFub3ZhX3Jlc3VsdHMkcC52YWx1ZSldDQpgYGANCg0KYGBge3J9DQpwX3ZhbHVlcw0KYGBgDQoNCiMjIyAqKkFzIHRoZSBuby4gb2YgZ3JvdXBzIGluY3JlYXNlcy4uLioqDQoNCkluIHRoaXMgY2FzZSB3ZSBoYXZlIGZpdmUgZ3JvdXBzLCByZXN1bHRpbmcgaW4gdGVuIHBhaXJzLiAqKkFzIHRoZSBudW1iZXIgb2YgZ3JvdXBzIGluY3JlYXNlcywgdGhlIG51bWJlciBvZiBwYWlycyAtIGFuZCBoZW5jZSB0aGUgbnVtYmVyIG9mIGh5cG90aGVzaXMgdGVzdHMgLSBpbmNyZWFzZXMgcXVhZHJhdGljYWxseSoqLiBUaGUgbW9yZSB0ZXN0cyB5b3UgcnVuLCB0aGUgaGlnaGVyIHRoZSBjaGFuY2UgdGhhdCBhdCBsZWFzdCBvbmUgb2YgdGhlbSB3aWxsIGdpdmUgYSBmYWxzZSBwb3NpdGl2ZSBzaWduaWZpY2FudCByZXN1bHQuIFdpdGggYSBzaWduaWZpY2FuY2UgbGV2ZWwgb2YgKiowLjIqKiwgaWYgeW91IHJ1biBvbmUgdGVzdCwgdGhlIGNoYW5jZSBvZiBhIGZhbHNlIHBvc2l0aXZlIHJlc3VsdCBpcyAqKjAuMioqLiBXaXRoIGZpdmUgZ3JvdXBzIGFuZCB0ZW4gdGVzdHMsIHRoZSBwcm9iYWJpbGl0eSBvZiBhdCBsZWFzdCBvbmUgZmFsc2UgcG9zaXRpdmUgaXMgYXJvdW5kIDAuNy4gV2l0aCB0d2VudHkgZ3JvdXBzLCBpdCdzIGFsbW9zdCBndWFyYW50ZWVkIHRoYXQgeW91J2xsIGdldCBhdCBsZWFzdCBvbmUgZmFsc2UgcG9zaXRpdmUuDQoNCiMjIyAqKkJvbmZlcnJvbmkgY29ycmVjdGlvbioqDQoNClRoZSBzb2x1dGlvbiB0byB0aGlzIGlzIHRvIGFwcGx5IGFuIGFkanVzdG1lbnQgdG8gaW5jcmVhc2UgdGhlIHAtdmFsdWVzLCByZWR1Y2luZyB0aGUgY2hhbmNlIG9mIGdldHRpbmcgYSBmYWxzZSBwb3NpdGl2ZS4gT25lIGNvbW1vbiBhZGp1c3RtZW50IGlzIHRoZSBCb25mZXJyb25pIGNvcnJlY3Rpb24uIE5vdyBvbmx5IHR3byBvZiB0aGUgcGFpcnMgYXBwZWFyIHRvIGhhdmUgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMuDQoNCmBgYHtyfQ0KcGFpcndpc2UudC50ZXN0KHN0YWNrX292ZXJmbG93JGNvbnZlcnRlZF9jb21wLCBzdGFja19vdmVyZmxvdyRqb2Jfc2F0LCBwLmFkanVzdC5tZXRob2QgPSAiYm9uZmVycm9uaSIpDQpgYGANCg0KIyMjICoqTW9yZSBtZXRob2RzKioNCg0KUiBwcm92aWRlcyBzZXZlcmFsIG1ldGhvZHMgZm9yIGFkanVzdGluZyB0aGUgcC12YWx1ZXMuIFlvdSBjYW4gbGlzdCB0aGVpciBuYW1lcyB3aXRoIGBwLmFkanVzdC5tZXRob2RzID1gLg0KDQoqKkhvbG0qKiBhZGp1c3RtZW50IGlzIHRoZSBkZWZhdWx0LiBJdCdzIGxlc3Mgc3RyaWN0IHRoYW4gKipCb25mZXJyb25pKiosIGJ1dCB3b3JrcyB3ZWxsIGluIG1vc3Qgc2l0dWF0aW9ucy4NCg0KYGBge3J9DQpwLmFkanVzdC5tZXRob2RzDQpgYGANCg0KIyMjICoqQm9uZmVycm9uaSBhbmQgSG9sZCBhZGp1c3RtZW50cyoqDQoNCklmIHlvdSBoYXZlIHRlbiB0ZXN0cywgQm9uZmVycm9uaSBqdXN0IG11bHRpcGxpZXMgdGhlIHAtdmFsdWVzIGJ5IHRlbiB1cCB0byBhIG1heGltdW0gb2Ygb25lLiBIb2xtIHdpbGwgbXVsdGlwbHkgdGhlIHNtYWxsZXN0IGJ5IHRlbiwgdGhlbiB0aGUgc2Vjb25kIHNtYWxsZXN0IGJ5IG5pbmUsIGFuZCBzbyBvbi4gVGhlcmUncyBhIHBvc3NpYmxlIGV4dHJhIGNvcnJlY3Rpb24gdG8gbWFrZSBzdXJlIHRoYXQgdGhlIG9yZGVyIG9mIHAtdmFsdWVzIGZyb20gc21hbGxlc3QgdG8gbGFyZ2VzdCBpcyBwcmVzZXJ2ZWQsIGJ1dCB0aGF0J3MgdGhlIGdpc3Qgb2YgaXQuDQoNCmBgYHtyfQ0KcF92YWx1ZXMNCmBgYA0KDQojIyMjICoqQm9uZmVycm9uaSoqDQoNCmBgYHtyfQ0KcG1pbigxLCAxMCAqIHBfdmFsdWVzKQ0KYGBgDQoNCiMjIyMgKipIb2xtKioNCg0KYGBge3J9DQpwbWluKDEsIDE6MTAgKiBzb3J0KHBfdmFsdWVzKSkNCmBgYA0KDQojIyAqKk9uZS1zYW1wbGUgcHJvcG9ydGlvbiB0ZXN0cyoqDQoNClRoZSBwcmV2aW91cyBleGFtcGxlIHRlc3RlZCBhIHNpbmdsZSBwcm9wb3J0aW9uIGFnYWluc3QgYSBzcGVjaWZpYyB2YWx1ZS4gQXMgd2l0aCBtZWFucywgd2UgY2FuIGFsc28gdGVzdCBkaWZmZXJlbmNlcyBiZXR3ZWVuIHByb3BvcnRpb25zIGluIHR3byBwb3B1bGF0aW9ucy4NCg0KIyMjICoqUmVjYXAqKg0KDQpUaGUgcHJldmlvdXMgaHlwb3RoZXNpcyB0ZXN0cyAqKm1lYXN1cmVkIHdoZXRoZXIgb3Igbm90IGFuIHVua25vd24gcG9wdWxhdGlvbiBwcm9wb3J0aW9uIHdhcyBlcXVhbCB0byBzb21lIHZhbHVlKiouIFdlIHVzZWQgYSBib290c3RyYXAgZGlzdHJpYnV0aW9uIG9mIHRoZSBzYW1wbGUgdG8gZSoqc3RpbWF0ZSB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIHNhbXBsZSBzdGF0aXN0aWMqKi4gVGhlIHN0YW5kYXJkIGVycm9yIHdhcyB1c2VkIHRvIGNhbGN1bGF0ZSBhIHN0YW5kYXJkaXplZCB0ZXN0IHN0YXRpc3RpYywgd2hpY2ggd2FzIHVzZWQgdG8gZ2V0IGEgcC12YWx1ZSwgd2hpY2ggd2FzIHVzZWQgdG8gZGVjaWRlIHdoZXRoZXIgb3Igbm90IHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLiBCb290c3RyYXAgZGlzdHJpYnV0aW9ucyBjYW4gYmUgY29tcHV0YXRpb25hbGx5IGludGVuc2l2ZSB0byBjYWxjdWxhdGUsIHNvIHRoaXMgdGltZSB3ZSdsbCBjYWxjdWxhdGUgdGhlIHRlc3Qgc3RhdGlzdGljIHdpdGhvdXQgaXQuDQoNCiMjIyAqKlN0YW5kYXJkaXplZCB0ZXN0IHN0YXRpc3RpYyBmb3IgcHJvcG9ydGlvbnMqKg0KDQpBbiB1bmtub3duIHBvcHVsYXRpb24gcGFyYW1ldGVyIHRoYXQgaXMgYSBwcm9wb3J0aW9uLCAqKm9yIHBvcHVsYXRpb24gcHJvcG9ydGlvbiBmb3Igc2hvcnQsKiogaXMgZGVub3RlZCBgcGAuIFRoZSBzYW1wbGUgcHJvcG9ydGlvbiBpcyBkZW5vdGVkICRcaGF0e3B9JCwgYW5kIHRoZSBoeXBvdGhlc2l6ZWQgdmFsdWUgZm9yIHRoZSBwb3B1bGF0aW9uIHByb3BvcnRpb24gaXMgZGVub3RlZCAkcF97MH0kIC4gQXMgc2VlbiBwcmV2aW91c2x5LCB0aGUgc3RhbmRhcmRpemVkIHRlc3Qgc3RhdGlzdGljIGlzIGEgei1zY29yZS4gKipZb3UgY2FsY3VsYXRlIGl0IGJ5IHN0YXJ0aW5nIHdpdGggdGhlIHNhbXBsZSBzdGF0aXN0aWMsIHN1YnRyYWN0aW5nIGl0cyBtZWFuLCB0aGVuIGRpdmlkaW5nIGJ5IGl0cyBzdGFuZGFyZCBlcnJvcioqLg0KDQokXGhhdHtwfSQgbWludXMgdGhlIG1lYW4gb2YgJFxoYXR7cH0kICwgZGl2aWRlZCBieSB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgJFxoYXR7cH0kIC4NCg0KUmVjYWxsIGZyb20gU2FtcGxpbmcgaW4gUiwgdGhlIG1lYW4gb2YgJFxoYXR7cH0kIGlzICRwJC4gVW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcywgdGhlIHVua25vd24gcHJvcG9ydGlvbiBwIGlzIGFzc3VtZWQgdG8gYmUgdGhlIGh5cG90aGVzaXplZCBwb3B1bGF0aW9uIHBhcmFtZXRlciAkcF97MH0kLiBUaGUgei1zY29yZSBpcyBub3cgJFxoYXR7cH0kIG1pbnVzICRwX3swfSQsIGRpdmlkZWQgYnkgdGhlIHN0YW5kYXJkIGVycm9yIG9mICRcaGF0e3B9JC4NCg0KJHAkIDogU2FtcGxlIHByb3BvcnRpb24NCg0KJFxoYXR7cH0kIDogc2FtcGxlIHByb3BvcnRpb24gKHNhbXBsZSBzdGF0aXN0aWMpDQoNCiRwX3swfSQgOiBoeXBvdGhlc2l6ZWQgcG9wdWxhdGlvbiBwcm9wb3J0aW9uDQoNCiQkIHogPSBcZnJhY3tcaGF0e3B9IFwgLSBtZWFuKFxoYXR7cH0pfXtzdGFuZGFyZCBcIGVycm9yKFxoYXR7cH0pfSA9IFxmcmFje1xoYXR7cH0gXCAtIHB9e3N0YW5kYXJkIFwgZXJyb3IoXGhhdHtwfSl9ICQkDQoNCkFzc3VtaW5nICRIX3swfSQgaXMgdHJ1ZS4gJHAgPSBwX3swfSQuIHNvDQoNCiQkIHogPSBcZnJhY3tcaGF0e3B9IFwgLSBwX3swfX17c3RhbmRhcmQgXCBlcnJvcihcaGF0e3B9KX0gJCQNCg0KIyMjICoqRWFzaWVyIHN0YW5kYXJkIGVycm9yIGNhbGN1bGF0aW9ucyoqDQoNCkhlcmUncyB0aGUgYXBwcm94aW1hdGUgc3RhbmRhcmQgZXJyb3IgZm9yIHR3byBzYW1wbGUgbWVhbnMuIEZvciBwcm9wb3J0aW9ucywgdW5kZXIgJEhfezB9JCwgdGhlIHN0YW5kYXJkIGVycm9yIG9mICRcaGF0e3B9JCBpcyAkcF97MH0kIHRpbWVzICQxIC0gcF97MH0kICwgZGl2aWRlZCBieSB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucywgdGhlbiBzcXVhcmUtcm9vdGVkLiBXZSBjYW4gc3Vic3RpdHV0ZSB0aGlzIGludG8gb3VyIGVxdWF0aW9uIGZvciB0aGUgei1zY29yZS4gVGhpcyBpcyBlYXN5IHRvIGNhbGN1bGF0ZSBiZWNhdXNlIGl0IG9ubHkgdXNlcyAkXGhhdHtwfSQgYW5kICRuJCwgd2hpY2ggd2UgZ2V0IGZyb20gdGhlIHNhbXBsZSwgYW5kICRwX3swfSQsIHdoaWNoIHdlIGNob3NlLg0KDQokJCBTRShcYmFye3h9X3tjaGlsZH0gXCAtIFxiYXJ7eH1fe2FkdWx0fSkgXGFwcHJveCBcc3FydHtcZnJhY3tzXjJfe2NoaWxkfX17bl97Y2hpbGR9fSBcICsgXGZyYWN7c14yX3thZHVsdH19e25fe2FkdWx0fX19ICQkDQoNCiQkIFNFX3tcaGF0e3B9fSBcID0gXHNxcnR7XGZyYWN7cF97MH0gXCAqICgxIC0gcF97MH0pfXtufX0gJCQNCg0KQXNzdW1pbmcgJEhfezB9JCBpcyBUUlVFLA0KDQokJCB6IFwgPSBcZnJhY3tcaGF0e3B9IFwgLSBwX3swfX17XHNxcnR7XGZyYWN7cF97MH0gXCAqIFwgKDEgXCAtIHBfezB9KX17bn19fSAkJA0KDQpUaGlzIG9ubHkgdXNlcyBzYW1wbGUgaW5mb3JtYXRpb24gKCRcaGF0e3B9JCBhbmQgJG4kKSBhbmQgdGhlIGh5cG90aGVzaXplZCBwYXJhbWV0ZXIgKCRwX3swfSQpDQoNCiMjIyAqKldoeSB6IGluc3RlYWQgb2YgdD8qKg0KDQpXaHkgd2UgdXNlZCBhIHotZGlzdHJpYnV0aW9uIGhlcmUsIGJ1dCBhIHQtZGlzdHJpYnV0aW9uIHByZXZpb3VzbHk/LiAqKlRoaXMgaXMgdGhlIHRlc3Qgc3RhdGlzdGljIGVxdWF0aW9uIGZvciB0aGUgdHdvIHNhbXBsZSBtZWFuIGNhc2UqKi4gVGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgc2FtcGxlLCAqKnMqKiwgaXMgY2FsY3VsYXRlZCBmcm9tIHRoZSBzYW1wbGUgbWVhbiwgJFxiYXJ7eH0kLiBUaGF0IG1lYW5zIHRoYXQgJFxiYXJ7eH0kIGlzIHVzZWQgaW4gdGhlIG51bWVyYXRvciB0byBlc3RpbWF0ZSB0aGUgcG9wdWxhdGlvbiBtZWFuLCBhbmQgaW4gdGhlIGRlbm9taW5hdG9yIHRvIGVzdGltYXRlIHRoZSBwb3B1bGF0aW9uIHN0YW5kYXJkIGRldmlhdGlvbi4gVGhpcyBkdWFsIHVzYWdlIGNhdXNlcyBhbiBpbmNyZWFzZSBpbiBvdXIgdW5jZXJ0YWludHkgYWJvdXQgdGhlIGVzdGltYXRlIG9mIHRoZSBwb3B1bGF0aW9uIHBhcmFtZXRlci4gU2luY2UgdC1kaXN0cmlidXRpb25zIGFyZSBlZmZlY3RpdmVseSBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBmYXR0ZXIgdGFpbHMsIHdlIGNhbiB1c2UgdGhlbSB0byBhY2NvdW50IGZvciB0aGlzIGV4dHJhIHVuY2VydGFpbnR5LiBJbiBlZmZlY3QsIHRoZSB0LWRpc3RyaWJ1dGlvbiBwcm92aWRlcyBleHRyYSBjYXV0aW9uIGFnYWluc3QgbWlzdGFrZW5seSByZWplY3RpbmcgdGhlIG51bGwgaHlwb3RoZXNpcy4gRm9yIHByb3BvcnRpb25zLCB3ZSBvbmx5IHVzZSAkXGhhdHtwfSQgaW4gdGhlIG51bWVyYXRvciwgdGh1cyBhdm9pZGluZyB0aGUgcHJvYmxlbSB3aXRoIHVuY2VydGFpbnR5LCBhbmQgYSB6LWRpc3RyaWJ1dGlvbiBpcyBmaW5lLg0KDQokJCB0IFwgPSBcZnJhY3soXGJhcnt4fV97Y2hpbGR9IFwgLSBcYmFye3h9X3thZHVsdH0pfXtcc3FydHtcZnJhY3thXjJfe2NoaWxkfX17bl97Y2hpbGR9fSBcICsgXGZyYWN7c14yX3thZHVsdH19e25fe2FkdWx0fX19fSAkJA0KDQotICAgJHMkIGlzIGNhbGN1bGF0ZWQgZnJvbSAkXGJhcnt4fSQsIHNvICRcYmFye3h9JCBpcyB1c2VkIHRvIGVzdGltYXRlIHRoZSBwb3B1bGF0aW9uIG1lYW4gYW5kIHRvIGVzdGltYXRlIHRoZSBwb3B1bGF0aW9uIHN0YW5kYXJkIGRldmlhdGlvbg0KDQotICAgVGhpcyBpbmNyZWFzZXMgdGhlIHVuY2VydGFpbnR5IGluIG91ciBlc3RpbWF0ZSBvZiB0aGUgcG9wdWxhdGlvbiBwYXJhbWV0ZXIuDQoNCi0gICB0LWRpc3RyaWJ1dGlvbiBoYXMgZmF0dGVyIHRhaWxzIHRoYW4gYSBub3JtYWwgZGlzdHJpYnV0aW9uDQoNCi0gICBUaGlzIGdpdmVzIGFuIGV4dHJhIGxldmVsIG9mIGNhdXRpb24NCg0KLSAgICRcaGF0e3B9JCBvbmx5IGFwcGVhcnMgaW4gdGhlIG51bWVyYXRvciwgc28gei1zY29yZXMgYXJlIGZpbmUNCg0KIyMjICoqU3RhY2sgb3ZlcmZsb3cgYWdlIGNhdGVnb3JpZXMqKg0KDQpMZXQncyBoeXBvdGhlc2l6ZSB0aGF0IGhhbGYgdGhlIHVzZXJzIGFyZSB1bmRlciB0aGlydHkuIFdlJ2xsIHNldCBhIHNpZ25pZmljYW5jZSBsZXZlbCBvZiAwLjAxLiBKdXN0IG92ZXIgaGFsZiB0aGUgdXNlcnMgYXJlIHVuZGVyIHRoaXJ0eS4NCg0KJEhfezB9JCA6IFRoZSBwcm9wb3J0aW9uIG9mIFNPIHVzZXJzIHVuZGVyIHRoaXJ0eSBpcyBlcXVhbCB0byAwLjUNCg0KJEhfe0F9JCA6IFRoZSBwcm9wb3J0aW9uIG9mIFNPIHVzZXJzIHVuZGVyIHRoaXJ0eSBpcyBub3QgZXF1YWwgdG8gMC41DQoNCmBgYHtyfQ0KYWxwaGEgPC0gMC4wMQ0KDQpzdGFja19vdmVyZmxvdyAlPiUNCiAgY291bnQoYWdlX2NhdCkNCmBgYA0KDQojIyMgKipWYXJpYWJsZXMgZm9yIHoqKg0KDQpMZXQncyBnZXQgdGhlIG51bWJlcnMgbmVlZGVkIGZvciB0aGUgei1zY29yZS4gJFxoYXR7cH0kIGlzIHRoZSBwcm9wb3J0aW9uIG9mIHJvd3MgaW4gdGhlIHNhbXBsZSB3aGVyZSBhZ2VfY2F0IGVxdWFscyB1bmRlciB0aGlydHkuICRwX3swfSQgaXMgMC41IGFjY29yZGluZyB0byB0aGUgbnVsbCBoeXBvdGhlc2lzLiBuIGlzIHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YXNldC4NCg0KYGBge3J9DQpwX2hhdCA8LSBzdGFja19vdmVyZmxvdyAlPiUNCiAgc3VtbWFyaXNlKHByb3BfdW5kZXJfMzAgPSBtZWFuKGFnZV9jYXQgPT0gIlVuZGVyIDMwIikpICU+JQ0KICBwdWxsKHByb3BfdW5kZXJfMzApDQoNCnBfaGF0DQpgYGANCg0KYGBge3J9DQpwXzAgPC0gMC41DQpuIDwtIG5yb3coc3RhY2tfb3ZlcmZsb3cpDQpuDQpgYGANCg0KIyMjICoqQ2FsY3VsYXRpbmcgdGhlIHotc2NvcmUqKg0KDQpDYWxjdWxhdGluZyB0aGUgei1zY29yZSBpcyBqdXN0IGFyaXRobWV0aWM7IHRoZSB2YWx1ZSBpcyB0aHJlZS1wb2ludC1maXZlLg0KDQokJCB6IFwgPSBcZnJhY3tcaGF0e3B9IFwgLSBwX3swfX17XHNxcnR7XGZyYWN7cF97MH0gXCAqIFwgKDEgXCAtIHBfezB9KX17bn19fSAkJA0KDQpgYGB7cn0NCm51bWVyYXRvciA8LSBwX2hhdCAtIHBfMA0KZGVub21pbmF0b3IgPC0gc3FydCgocF8wICogKDEgLSBwXzApKSAvIG4pDQp6X3Njb3JlIDwtIG51bWVyYXRvciAvIGRlbm9taW5hdG9yDQp6X3Njb3JlDQpgYGANCg0KIyMjICoqQ2FsY3VsYXRpbmcgdGhlIHAtdmFsdWUqKg0KDQpGb3IgbGVmdC10YWlsZWQgYWx0ZXJuYXRpdmUgaHlwb3RoZXNlcywgeW91IHRyYW5zZm9ybSB0aGUgei1zY29yZSBpbnRvIGEgcC12YWx1ZSB1c2luZyB0aGUgcG5vcm0gd2l0aCB0aGUgZGVmYXVsdCBvZiBsb3dlci1kb3QtdGFpbCBlcXVhbHMgVFJVRS4gRm9yIHJpZ2h0LXRhaWxlZCBhbHRlcm5hdGl2ZSBoeXBvdGhlc2VzIHlvdSBzZXQgbG93ZXItZG90LXRhaWwgdG8gRkFMU0UuIEZvciB0d28tdGFpbGVkIGFsdGVybmF0aXZlIGh5cG90aGVzZXMsIHlvdSBjaGVjayB3aGV0aGVyIHRoZSB0ZXN0IHN0YXRpc3RpYyBsaWVzIGluIGVpdGhlciB0YWlsLCBzbyB0aGUgcC12YWx1ZSBpcyB0aGUgc3VtIG9mIHRoZXNlIHR3byB2YWx1ZXMuIFNpbmNlIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uIFBERiBpcyBzeW1tZXRyaWMsIGhlcmUgdGhpcyBzaW1wbGlmaWVzIHRvIHR3aWNlIHRoZSBsZWZ0LXRhaWxlZCBwLXZhbHVlLiBIZXJlLCB0aGUgcC12YWx1ZSBpcyBsZXNzIHRoYW4gdGhlIHNpZ25pZmljYW5jZSBsZXZlbCBvZiBwb2ludC16ZXJvLW9uZSwgc28gd2UgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgYW5kIGNvbmNsdWRlIHRoYXQgdGhlIHByb3BvcnRpb24gb2YgdXNlcnMgdW5kZXIgdGhpcnR5IGlzIG5vdCBlcXVhbCB0byBwb2ludC1maXZlLg0KDQpgYGB7cn0NCiMgTGVmdCB0YWlsZWQgKCJsZXNzIHRoYW4iKQ0KcF92YWx1ZV9sZXNzIDwtIHBub3JtKHpfc2NvcmUpDQpwX3ZhbHVlX2xlc3MNCmBgYA0KDQpgYGB7cn0NCiMgUmlnaHQgdGFpbGVkICgiZ3JlYXRlciB0aGFuIikNCnBfdmFsdWUgPC0gcG5vcm0oel9zY29yZSwgbG93ZXIudGFpbCA9IEZBTFNFKQ0KcF92YWx1ZQ0KYGBgDQoNCmBgYHtyfQ0KIyBUd28tdGFpbGVkICgibm90IGVxdWFsIikNCnBfdmFsdWVfMnQgPC0gcG5vcm0oel9zY29yZSkgKyBwbm9ybSh6X3Njb3JlLCBsb3dlci50YWlsID0gRkFMU0UpDQpwX3ZhbHVlXzJ0DQpgYGANCg0KYGBge3J9DQpwX3ZhbHVlX2EgPC0gMiAqIHBub3JtKHpfc2NvcmUpDQpwX3ZhbHVlX2ENCmBgYA0KDQpgYGB7cn0NCnBfdmFsdWUgPD0gYWxwaGENCmBgYA0KDQojIyBUd28gc2FtcGxlIHByb3BvcnRpb24gdGVzdHMNCg0KVGhlIHByZXZpb3VzIGV4YW1wbGUgKip0ZXN0ZWQgYSBzaW5nbGUgcHJvcG9ydGlvbiBhZ2FpbnN0IGEgc3BlY2lmaWMgdmFsdWUqKi4gQXMgd2l0aCBtZWFucywgd2UgY2FuIGFsc28gdGVzdCBkaWZmZXJlbmNlcyBiZXR3ZWVuIHByb3BvcnRpb25zIGluIHR3byBwb3B1bGF0aW9ucy4NCg0KIyMjICoqQ29tcGFyaW5nIHR3byBwcm9wb3J0aW9ucyoqDQoNClRoZSBTdGFjayBPdmVyZmxvdyBzdXJ2ZXkgY29udGFpbnMgYSBob2JieWlzdCB2YXJpYWJsZS4gVGhlIHZhbHVlICJZZXMiIG1lYW5zIHRoZSB1c2VyIGRlc2NyaWJlZCB0aGVtc2VsZiBhcyBhIGhvYmJ5aXN0OyAiTm8iIG1lYW5zIHRoZXkgZGVzY3JpYmVkIHRoZW1zZWxmIGFzIGEgcHJvZmVzc2lvbmFsLiAqKldlIGNhbiBoeXBvdGhlc2l6ZSB0aGF0IHRoZSBwcm9wb3J0aW9uIG9mIHVzZXJzIHdobyBhcmUgaG9iYnlpc3RzIGlzIHRoZSBzYW1lIGZvciB0aGUgdW5kZXIgdGhpcnR5IGFnZSBjYXRlZ29yeSBhcyB0aGUgYXQgbGVhc3QgdGhpcnR5IGNhdGVnb3J5KiouDQoNCk1vcmUgZm9ybWFsbHksICoqdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0aGF0IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHBvcHVsYXRpb24gcGFyYW1ldGVycyBmb3IgZWFjaCBncm91cCBpcyB6ZXJvKiouIEkndmUgc2V0IGEgc2lnbmlmaWNhbmNlIGxldmVsIG9mIDAuMDUuDQoNCiRIX3swfSQgOiBUaGUgcHJvcG9ydGlvbiBvZiBTTyB1c2VycyB3aG8gYXJlIGhvYmJ5aXN0cyBpcyB0aGUgc2FtZSBmb3IgdGhvc2UgdW5kZXIgdGhpcnR5IGFzIHRob3NlIGF0IGxlYXN0IHRoaXJ0eQ0KDQokJA0KSF97MH06IHBfe1xnZXEgMzB9IFwgLSBwX3s8IDMwfSBcID0gMA0KJCQNCg0KJEhfe0F9JCA6IFRoZSBwcm9wb3J0aW9uIG9mIFNPIHVzZXJzIHdobyBhcmUgaG9iYnlpc3RzIGlzIGRpZmZlcmVudCBmb3IgdGhvc2UgdW5kZXIgdGhpcnR5IGFzIHRob3NlIGF0IGxlYXN0IHRoaXJ0eQ0KDQokJA0KSF97QX06IHBfe1xnZXEgMzB9IFwgLSBwX3s8MzB9IFxuZXEgMA0KJCQNCg0KIyMjICoqQ2FsY3VsYXRpbmcgdGhlIHotc2NvcmUqKg0KDQpMZXQncyBicmVhayBkb3duIHRoaXMgei1zY29yZSBlcXVhdGlvbi4gVGhlIHNhbXBsZSBzdGF0aXN0aWMgaXMgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIHByb3BvcnRpb25zIGZvciBlYWNoIGNhdGVnb3J5ICQoXGhhdHtwfV97XGdlcTMwfSBcIC0gXGhhdHtwfV97PDMwfSkgXCAtIDAkLiBUaGF0J3MgdGhlIHR3byAkXGhhdHtwfSQgdmFsdWVzIG9uIHRoZSBudW1lcmF0b3IuIFdlIHN1YnRyYWN0IHRoZSBoeXBvdGhlc2l6ZWQgdmFsdWUgb2YgdGhlIHBvcHVsYXRpb24gcGFyYW1ldGVyLiBBc3N1bWluZyB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRydWUsICoqdGhhdCdzIGp1c3QgemVybyoqLCBzbyB0aGUgdGVybSBkaXNhcHBlYXJzLiBUaGUgZGVub21pbmF0b3IgaXMgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBzYW1wbGUgc3RhdGlzdGljICRTRShcaGF0e3B9X3tcZ2VxMzB9IFwgLSBcaGF0e3B9X3s8MzB9KSQuDQoNCkFnYWluIHdlIGNhbiBhdm9pZCBoYXZpbmcgdG8gZ2VuZXJhdGUgYSBib290c3RyYXAgZGlzdHJpYnV0aW9uLiBUaGUgZXF1YXRpb24gZm9yIHRoZSBzdGFuZGFyZCBlcnJvciBpcyBhIHNsaWdodGx5IG1vcmUgY29tcGxpY2F0ZWQgdmVyc2lvbiBvZiB0aGUgZXF1YXRpb24gZm9yIHRoZSBvbmUgc2FtcGxlIGNhc2UuIEluIHRoaXMgZXF1YXRpb24sIG5vdGUgdGhhdCBwLWhhdCBpcyB0aGUgc2FtcGxlIHByb3BvcnRpb24gZm9yIHRoZSB3aG9sZSBkYXRhc2V0LCBub3QgZm9yIGVhY2ggY2F0ZWdvcnkuIFRoaXMgd2hvbGUgZGF0YXNldCAkXGhhdHtwfSQgaXMga25vd24gYXMgYSAqKnBvb2xlZCBlc3RpbWF0ZSBvZiB0aGUgcG9wdWxhdGlvbiBwcm9wb3J0aW9uKiouIFdlIG5lZWQgb25lIG1vcmUgZXF1YXRpb24gdG8gZ2V0ICRcaGF0e3B9JC4gSXQncyBhIHdlaWdodGVkIG1lYW4gb2YgdGhlIHAtaGF0cyBmb3IgZWFjaCBjYXRlZ29yeS4gVGhpcyBsb29rcyBob3JyZW5kb3VzLCBidXQgaXQncyBqdXN0IGFyaXRobWV0aWMsIGFuZCBSIGlzIHByZXR0eSBnb29kIGF0IHRoYXQuIFRoZSBnb29kIG5ld3MgaXMgdGhhdCB3ZSBvbmx5IG5lZWQgZm91ciBudW1iZXJzIGZyb20gdGhlIHNhbXBsZSBkYXRhc2V0IHRvIGRvIHRoZXNlIGNhbGN1bGF0aW9ucy4NCg0KJCQNCnogPSBcZnJhY3soXGhhdHtwfV97XGdlcTMwfSBcIC0gXGhhdHtwfV97PDMwfSkgXCAtIDB9e1NFKFxoYXR7cH1fe1xnZXEzMH0gXCAtIFxoYXR7cH1fezwzMH0pfQ0KJCQNCg0KJCQNClNFKFxoYXR7cH1fe1xnZXEwfSBcIC0gXGhhdHtwfV97PDB9KSBcID0gXHNxcnR7XGZyYWN7XGhhdHtwfSBcdGltZXMgXCAoMSBcIC0gXGhhdHtwfSl9e25fe1xnZXEzMH19IFwgKyBcZnJhY3tcaGF0e3B9IFx0aW1lcyBcICgxIFwgLSBcaGF0e3B9KX17bl97PDMwfX19IA0KJCQNCg0KJFxoYXR7cH0kIGlzIGEgcG9vbGVkIGVzdGltYXRlIGZvciAkcCQgKGNvbW1vbiB1bmtub3duIHByb3BvcnRpb24gb2Ygc3VjY2Vzc2VzKQ0KDQokJA0KXGhhdHtwfSBcID0gXGZyYWN7bl97XGdlcTMwfSBcdGltZXMgXGhhdHtwfV97XGdlcTMwfSBcICsgbl97PDMwfSBcdGltZXMgXGhhdHtwfV97PDMwfX17bl97XGdlcTMwfSBcICsgbl97PDMwfX0NCiQkDQoNCldlIG9ubHkgbmVlZCB0byBjYWxjdWxhdGUgNCBudW1iZXJzOiAkXGhhdHtwfV97XGdlcTMwfSwgXCBcaGF0e3B9X3s8MzB9LCBcIG5fe1xnZXEzMH0sIFwgbl97PDMwfSQNCg0KIyMjICoqR2V0dGluZyB0aGUgbnVtYmVycyBmb3IgdGhlIHotc2NvcmUqKg0KDQpUbyBjYWxjdWxhdGUgdGhlc2UgZm91ciBudW1iZXJzIHlvdSBgZ3JvdXBfYnkoKWAgdGhlIGNhdGVnb3JpZXMsIGFuZCBzdW1tYXJpemUgdG8gY2FsY3VsYXRlIHRoZSBzYW1wbGUgcHJvcG9ydGlvbiBhbmQgcm93IGNvdW50cy4gQWZ0ZXIgdGhhdCwgeW91IGNhbiBkbyB0aGUgYXJpdGhtZXRpYyB0byBnZXQgdGhlIHRlc3Qgc3RhdGlzdGljIC0gdGhlIHotc2NvcmUgaXMgbWludXMgZm91ci1wb2ludC10d28gLSBhbmQgY2FsbCBwbm9ybSB0byBnZXQgdGhlIHAtdmFsdWUuIEknbSBub3QgZ29pbmcgdG8gcnVuIHRocm91Z2ggdGhlc2Ugc3RlcHMgYmVjYXVzZSBpdCdzIGV4YWN0bHkgdGhlIHNhbWUgYXMgeW91J3ZlIHNlZW4gYWxyZWFkeS4gSW5zdGVhZCwgSSdsbCBzaG93IHlvdSBhbiBlYXNpZXIgd2F5Lg0KDQpgYGB7cn0NCnN0YWNrX292ZXJmbG93ICU+JQ0KICBncm91cF9ieShhZ2VfY2F0KSAlPiUNCiAgc3VtbWFyaXNlKHBfaGF0ID0gbWVhbihob2JieWlzdCA9PSAiWWVzIiksIG4gPSBuKCkpDQpgYGANCg0KIyMjICoqUHJvcG9ydGlvbiB0ZXN0cyB1c2luZyBgcHJvcF90ZXN0KClgKioNCg0KTmF0dXJhbGx5LCAqKlIgaGFzIGF0IGxlYXN0IHR3byBmdW5jdGlvbnMgZm9yIHBlcmZvcm1pbmcgdGhpcyB0eXBlIG9mIGh5cG90aGVzaXMgdGVzdCoqLCBrbm93biBhcyBhICoqcHJvcG9ydGlvbiB0ZXN0KiouIEJhc2UtUiBoYXMgYSBmdW5jdGlvbiBuYW1lZCBgcHJvcC50ZXN0KClgLCB0aG91Z2ggdW5mb3J0dW5hdGVseSwgaXRzIGludGVyZmFjZSBpcyBzb21ld2hhdCBwZWN1bGlhci4gKipUbyBtYWtlIHlvdXIgbGlmZSBlYXNpZXIgaXQncyBiZXN0IHRvIHVzZSBgcHJvcF90ZXN0KClgIGZyb20gaW5mZXIqKi4gVGhpcyB0YWtlcyBhIGZvcm11bGEsICoqd2l0aCB5b3VyIG51bWVyaWMgdmFyaWFibGUgb24gdGhlIGxlZnQgYW5kIHRoZSBjYXRlZ29yaWVzIG9uIHRoZSByaWdodCoqLiBUaGUgb3JkZXIgYXJndW1lbnQgc3BlY2lmaWVzIHdoaWNoICRcaGF0e3B9JCBzaG91bGQgYmUgc3VidHJhY3RlZCBmcm9tIHRoZSBvdGhlci4gSGVyZSwgaXQgaXMgc3BlY2lmaWVkIHNvIHRoYXQgeW91IHN0YXJ0IHdpdGggdGhlICJhdCBsZWFzdCB0aGlydHkiICRcaGF0e3B9X3tcZ2VxMzB9JCAsIHRoZW4gc3VidHJhY3QgdGhlICJ1bmRlciB0aGlydHkiICRcaGF0e3B9X3s8MzB9JA0KDQpUaGUgYHN1Y2Nlc3NgIGFyZ3VtZW50IHNwZWNpZmllcyAqKndoaWNoIG9mIHRoZSB0d28gcmVzcG9uc2UgbGV2ZWxzIHRvIGdldCB0aGUgcHJvcG9ydGlvbiBvZioqLiBgYWx0ZXJuYXRpdmVgIHRha2VzIHRoZSBzYW1lIHZhbHVlcyBhcyB0LXRlc3QuIEhlcmUsICoqd2UgaGF2ZSBhIHR3by1zaWRlZCBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzKiouIFRoZSBgY29ycmVjdGAgYXJndW1lbnQgKipzcGVjaWZpZXMgd2hldGhlciBvciBub3QgdG8gYXBwbHkgWWF0ZXMnIGNvbnRpbnVpdHkgY29ycmVjdGlvbioqLiBUaGlzIGlzIGEgZnVkZ2UgZmFjdG9yICoqbmVlZGVkIGZvciB0ZWNobmljYWwgcmVhc29ucyB3aGVuIHRoZSBzYW1wbGUgc2l6ZSBpcyB2ZXJ5IHNtYWxsKiouIFNpbmNlICoqZWFjaCBncm91cCBoYXMgb3ZlciBvbmUgdGhvdXNhbmQgb2JzZXJ2YXRpb25zLCB3ZSBkb24ndCBuZWVkIGl0KiouIFRoZSBwLXZhbHVlIGlzIGluIHRoZSB0aGlyZCBjb2x1bW4gb2YgdGhlIHJlc3VsdGluZyB0aWJibGUuIEl0J3Mgc21hbGxlciB0aGFuIHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgd2Ugc3BlY2lmaWVkIGVhcmxpZXIsIHNvICoqd2UgY2FuIGNvbmNsdWRlIHRoYXQgdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGluIHRoZSBwcm9wb3J0aW9uIG9mIFN0YWNrIE92ZXJmbG93IGhvYmJ5aXN0cyBiZXR3ZWVuIHRoZSB1bmRlciBhbmQgb3ZlciB0aGlydHkgZ3JvdXBzKiouDQoNCmBgYHtyfQ0KbGlicmFyeShpbmZlcikNCnN0YWNrX292ZXJmbG93ICU+JQ0KICBwcm9wX3Rlc3QoaG9iYnlpc3QgfiBhZ2VfY2F0LCBvcmRlciA9IGMoIkF0IGxlYXN0IDMwIiwgIlVuZGVyIDMwIiksIHN1Y2Nlc3MgPSAiWWVzIiwgDQogICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0d28tc2lkZWQiLCBjb3JyZWN0ID0gRkFMU0UpDQpgYGANCg0KIyMjICoqQ2hpLXNxdWFyZSB0ZXN0IG9mIGluZGVwZW5kZW5jZSoqDQoNCkp1c3QgYXMgQU5PVkEgZXh0ZW5kcyB0LXRlc3RzIHRvIG1vcmUgdGhhbiB0d28gZ3JvdXBzLCBjaGktc3F1YXJlIHRlc3RzIG9mIGluZGVwZW5kZW5jZSBleHRlbmQgcHJvcG9ydGlvbiB0ZXN0cyB0byBtb3JlIHRoYW4gdHdvIGdyb3Vwcy4NCg0KIyMjICoqUmV2aXNpdGluZyB0aGUgcHJvcG9ydGlvbiB0ZXN0KioNCg0KSGVyZSdzIHRoZSBwcm9wb3J0aW9uIHRlc3QgZnJvbSBsYXN0IHRpbWUuIEluIHRoZSBmaXJzdCBjb2x1bW4sIHRoZSB0ZXN0IHN0YXRpc3RpYyBpcyB0aGUgc3F1YXJlIG9mIHRoZSB6LXNjb3JlLiBNaW51cyBmb3VyLXBvaW50LXR3by10d28gc3F1YXJlZCBpcyBzZXZlbnRlZW4tcG9pbnQtZWlnaHQuIEluIHRoZSBzZWNvbmQgY29sdW1uLCBjaGlzcV9kZiBtZWFucyB0aGUgZGVncmVlcyBvZiBmcmVlZG9tIG9mIGEgY2hpLXNxdWFyZSB0ZXN0LiBJdHMgdmFsdWUgaXMgb25lLg0KDQpgYGB7cn0NCmxpYnJhcnkoaW5mZXIpDQpzdGFja19vdmVyZmxvdyAlPiUNCiAgcHJvcF90ZXN0KGhvYmJ5aXN0IH4gYWdlX2NhdCwgb3JkZXIgPSBjKCJBdCBsZWFzdCAzMCIsICJVbmRlciAzMCIpLCBhbHRlcm5hdGl2ZSA9ICJ0d28tc2lkZWQiLCBjb3JyZWN0ID0gRkFMU0UpDQpgYGANCg0KIyMjICoqSW5kZXBlbmRlbmNlIG9mIHZhcmlhYmxlcyoqDQoNClRoYXQgcHJvcG9ydGlvbiB0ZXN0IGhhZCBhIHBvc2l0aXZlIHJlc3VsdC4gVGhlcmUgd2FzIGV2aWRlbmNlIHRoYXQgdGhlIGhvYmJ5aXN0IGFuZCBhZ2UgY2F0ZWdvcnkgdmFyaWFibGVzIGhhZCBhbiBhc3NvY2lhdGlvbi4gSWYgdGhhdCB3YXNuJ3QgdGhlIGNhc2UsIGFuZCB0aGUgcHJvcG9ydGlvbiBvZiBob2JieWlzdHMgd2FzIHRoZSBzYW1lIGZvciBlYWNoIGFnZSBjYXRlZ29yeSwgdGhlIHZhcmlhYmxlcyB3b3VsZCBiZSBjb25zaWRlcmVkIHN0YXRpc3RpY2FsbHkgaW5kZXBlbmRlbnQuIE1vcmUgZm9ybWFsbHksICoqc3RhdGlzdGljYWwgaW5kZXBlbmRlbmNlIG9mIHR3byBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaXMgd2hlbiB0aGUgcHJvcG9ydGlvbiBvZiBzdWNjZXNzZXMgaW4gdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGlzIHRoZSBzYW1lIGFjcm9zcyBhbGwgY2F0ZWdvcmllcyBvZiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUqKi4NCg0KIyMjICoqSm9iIHNhdGlzZmFjdGlvbiBhbmQgYWdlIGNhdGVnb3J5KioNCg0KUmVjYWxsIHRoYXQgdGhlIFN0YWNrIE92ZXJmbG93IHNhbXBsZSBoYXMgYW4gYWdlIGNhdGVnb3J5IHZhcmlhYmxlIHdpdGggdHdvIGNhdGVnb3JpZXMgYW5kIGEgam9iIHNhdGlzZmFjdGlvbiB2YXJpYWJsZSB3aXRoIGZpdmUgY2F0ZWdvcmllcy4NCg0KYGBge3J9DQpzdGFja19vdmVyZmxvdyAlPiUNCiAgY291bnQoYWdlX2NhdCkNCmBgYA0KDQpgYGB7cn0NCnN0YWNrX292ZXJmbG93ICU+JQ0KICBjb3VudChqb2Jfc2F0KQ0KYGBgDQoNCiMjIyAqKkRlY2xhcmluZyB0aGUgaHlwb3RoZXNpcyoqDQoNCldlIGNhbiBkZWNsYXJlIGh5cG90aGVzZXMgdG8gdGVzdCBmb3IgaW5kZXBlbmRlbmNlIG9mIHRoZXNlIHZhcmlhYmxlcy4gSGVyZSwgKiphZ2UgY2F0ZWdvcnkgaXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlKiosIGFuZCAqKmpvYiBzYXRpc2ZhY3Rpb24gaXMgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlKiouICoqVGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0aGF0IGluZGVwZW5kZW5jZSBvY2N1cnMuKiogSSd2ZSBzZXQgYSAqKnNpZ25pZmljYW5jZSBsZXZlbCBvZiAwLjEqKi4gVGhlIHRlc3Qgc3RhdGlzdGljIGlzIGRlbm90ZWQgY2hpLXNxdWFyZS4gKipJdCBxdWFudGlmaWVzIGhvdyBmYXIgYXdheSB0aGUgb2JzZXJ2ZWQgcmVzdWx0cyBhcmUgZnJvbSB0aGUgdmFsdWVzIHlvdSdkIGV4cGVjdCBpZiBpbmRlcGVuZGVuY2Ugd2FzIHRydWUqKi4NCg0KJEhfezB9JCA6IEFnZSBjYXRlZ29yaWVzIGFyZSBpbmRlcGVuZGVudCBvZiBqb2Igc2F0aXNmYWN0aW9uIGxldmVscw0KDQokSF97QX0kIDogQWdlIGNhdGVnb3JpZXMgYXJlIG5vdCBpbmRlcGVuZGVudCBvZiBqb2Igc2F0aXNmYWN0aW9uIGxldmVscw0KDQpUZXN0IHN0YXRpc3RpYyBkZW5vdGVkICRcY2hpXjIkDQoNCkFzc3VtaW5nIHRoZSBpbmRlcGVuZGVuY2UsIGhvdyBmYXIgYXdheSBhcmUgdGhlIG9ic2VydmVkIHJlc3VsdHMgZnJvbSB0aGUgZXhwZWN0ZWQgdmFsdWVzPw0KDQpgYGB7cn0NCmFscGhhID0gMC4xDQpgYGANCg0KIyMjICoqRXhwbG9yYXRvcnkgdmlzdWFsaXphdGlvbjogUHJvcG9ydGlvbmFsIHN0YWNrZWQgYmFyIHBsb3QqKg0KDQpMZXQncyBleHBsb3JlIHRoZSBkYXRhIHVzaW5nIGEgcHJvcG9ydGlvbmFsIHN0YWNrZWQgYmFyIHBsb3QuIGBmaWxsYCBtZWFucyB0d28gZGlmZmVyZW50IHRoaW5ncyBoZXJlLiBJbiB0aGUgYWVzdGhldGljcywgYGZpbGxgIHJlZmVycyB0byB0aGUgZmlsbCBjb2xvciBvZiB0aGUgYmFycy4gSW4gYGdlb21fYmFyYCdzIHBvc2l0aW9uIGFyZ3VtZW50LCBgImZpbGwiYCBtYWtlcyBlYWNoIGJhciB0aGUgc2FtZSBoZWlnaHQsIHNvIHlvdSBjYW4gY29tcGFyZSBwcm9wb3J0aW9ucy4gSWYgdGhlIGFnZSBjYXRlZ29yeSB3YXMgaW5kZXBlbmRlbnQgb2YgdGhlIGpvYiBzYXRpc2ZhY3Rpb24sIHRoZSBzcGxpdCBiZXR3ZWVuIHRoZSBhZ2UgY2F0ZWdvcmllcyB3b3VsZCBiZSBhdCB0aGUgc2FtZSBoZWlnaHQgaW4gZWFjaCBvZiB0aGUgZml2ZSBiYXJzLiBUaGVyZSdzIHNvbWUgdmFyaWF0aW9uIGhlcmUsIGJ1dCB3ZSdsbCBuZWVkIHRoZSBoeXBvdGhlc2lzIHRlc3QgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgaXQncyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UuDQoNCmBgYHtyfQ0KZ2dwbG90KHN0YWNrX292ZXJmbG93LCBhZXMoam9iX3NhdCwgZmlsbCA9IGFnZV9jYXQpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICB5bGFiKCJwcm9wb3J0aW9uIikNCmBgYA0KDQojIyMgKipDaGktc3F1YXJlIGluZGVwZW5kZW5jZSB0ZXN0IHVzaW5nIGBjaGlzcV90ZXN0KClgKioNCg0KVGhlIGh5cG90aGVzaXMgdGVzdCBmb3IgaW5kZXBlbmRlbmNlIGlzIGNhbGxlZCBhICoqY2hpLXNxdWFyZSBpbmRlcGVuZGVuY2UgdGVzdC4qKiBUaGVyZSdzIGEgYmFzZS1SIGZ1bmN0aW9uIGZvciBpdCBjYWxsZWQgYGNoaXNxLnRlc3QoKWAsIGJ1dCBsaWtlIGBwcm9wLnRlc3QoKWAsIGl0J3MgZmlkZGx5IHRvIHVzZS4gVGhlIGVhc2llc3Qgb3B0aW9uIGlzIHRvIHVzZSBpbmZlcidzIGBjaGlzcV90ZXN0KClgLiBQaXBlIGZyb20gdGhlIHNhbXBsZSBkYXRhc2V0LCAqKnBhc3NpbmcgYSBmb3JtdWxhIHdpdGggdGhlIHJlc3BvbnNlIHZhcmlhYmxlIG9uIHRoZSBsZWZ0IGFuZCB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUgb24gdGhlIHJpZ2h0KiouIFRoZSBwLXZhbHVlIGlzICoqMC4yMyoqLCB3aGljaCBpcyAqKmFib3ZlIHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwqKiB3ZSBzZXQsIHNvICoqd2UgY29uY2x1ZGUgdGhhdCBhZ2UgY2F0ZWdvcmllcyBhcmUgaW5kZXBlbmRlbnQgb2Ygam9iIHNhdGlzZmFjdGlvbioqLiBUaGUgY2hpLXNxdWFyZSBkaXN0cmlidXRpb24sIHdob3NlIENERiBpcyB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgcC12YWx1ZSBmcm9tIHRoZSB0ZXN0IHN0YXRpc3RpYywgaGFzIGEgZGVncmVlcyBvZiBmcmVlZG9tIGFyZ3VtZW50LCBqdXN0IGxpa2UgdGhlIHQtZGlzdHJpYnV0aW9uLiBUaGUgcmVzdWx0cyBzaG93IHRoYXQgdGhlcmUgYXJlIGZvdXIgZGVncmVlcyBvZiBmcmVlZG9tLiBUaGlzIGlzIHRoZSBudW1iZXIgb2YgcmVzcG9uc2UgY2F0ZWdvcmllcyBtaW51cyBvbmUsIHRpbWVzIHRoZSBudW1iZXIgb2YgZXhwbGFuYXRvcnkgY2F0ZWdvcmllcyBtaW51cyBvbmUuIFR3byBtaW51cyBvbmUgdGltZXMgZml2ZSBtaW51cyBvbmUgaXMgZm91ci4NCg0KYGBge3J9DQpzdGFja19vdmVyZmxvdyAlPiUNCiAgY2hpc3FfdGVzdChhZ2VfY2F0IH4gam9iX3NhdCkNCmBgYA0KDQojIyMjICoqRGVncmVlcyBvZiBmcmVlZG9tKioNCg0KJCQNCihOby4gXCBvZiBcIHJlc3BvbnNlIFwgY2F0ZWdvcmllcyBcIC0gMSkgXHRpbWVzIChOby4gXCBvZiBcIGV4cGxhbmF0b3J5IFwgY2F0ZWdvcmllcyBcIC0gMSkNClxcKDIgXCAtIDEpIFx0aW1lcyAoNSBcIC0gMSkgXCA9IDQNCiQkDQoNCiMjIyAqKlN3YXBwaW5nIHRoZSB2YXJpYWJsZXMqKg0KDQpJZiB3ZSBzd2FwIHRoZSB2YXJpYWJsZXMsIHNvIGFnZSBjYXRlZ29yeSBpcyB0aGUgcmVzcG9uc2UsIGFuZCBqb2Igc2F0aXNmYWN0aW9uIGlzIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZSwgdGhlIHZpc3VhbCBpbnNwZWN0aW9uIHRlY2huaXF1ZSBpcyB0aGUgc2FtZTogc2VlIGlmIHRoZSBzcGxpdHMgZm9yIGVhY2ggYmFyIGFyZSBpbiBzaW1pbGFyIHBsYWNlcy4NCg0KYGBge3J9DQpnZ3Bsb3Qoc3RhY2tfb3ZlcmZsb3csIGFlcyhhZ2VfY2F0LCBmaWxsID0gam9iX3NhdCkpICsgDQogIGdlb21fYmFyKHBvc2l0aW9uID0gImZpbGwiKSArIA0KICB5bGFiKCJwcm9wb3J0aW9uIikNCmBgYA0KDQojIyMgKipDaGktc3F1YXJlIGJvdGggd2F5cyoqDQoNCklmIHdlIHJ1biB0aGUgY2hpLXNxdWFyZSB0ZXN0IHdpdGggdGhlIHZhcmlhYmxlcyBzd2FwcGVkLCB0aGVuIHRoZSByZXN1bHRzIGFyZSBpZGVudGljYWwuICoqV2Ugc2hvdWxkIGFzayBxdWVzdGlvbnMgbGlrZSAiYXJlIHZhcmlhYmxlcyBYIGFuZCBZIGluZGVwZW5kZW50PyIqKiwgbm90ICJpcyB2YXJpYWJsZSBYIGluZGVwZW5kZW50IGZyb20gdmFyaWFibGUgWT8iLCBzaW5jZSB0aGUgb3JkZXIgZG9lc24ndCBtYXR0ZXIuDQoNCmBgYHtyfQ0Kc3RhY2tfb3ZlcmZsb3cgJT4lDQogIGNoaXNxX3Rlc3QoYWdlX2NhdCB+IGpvYl9zYXQpDQpgYGANCg0KYGBge3J9DQpzdGFja19vdmVyZmxvdyAlPiUNCiAgY2hpc3FfdGVzdChqb2Jfc2F0IH4gYWdlX2NhdCkNCmBgYA0KDQojIyMgKipXaGF0IGFib3V0IGRpcmVjdGlvbnMgYW5kIHRhaWxzPyoqDQoNCldlIGRpZG4ndCB3b3JyeSBhYm91dCB0YWlscyBpbiB0aGlzIHRlc3QsIGFuZCBpbiBmYWN0IGBjaGlzcV90ZXN0KClgICoqZG9lc24ndCBoYXZlIGFuIGFsdGVybmF0aXZlIGFyZ3VtZW50KiouIFRoaXMgaXMgKipiZWNhdXNlIHRoZSB0ZXN0IHN0YXRpc3RpYyBpcyBiYXNlZCBvbiB0aGUgc3F1YXJlIG9mIG9ic2VydmVkIGFuZCBleHBlY3RlZCBjb3VudHMqKiwgYW5kIHNxdWFyZSBudW1iZXJzIGFyZSBub24tbmVnYXRpdmUuICoqVGhhdCBtZWFucyB0aGF0IGNoaS1zcXVhcmUgdGVzdHMgdGVuZCB0byBiZSByaWdodC10YWlsZWQgdGVzdHMqKi4NCg0KXjFeIExlZnQtdGFpbGVkIGNoaS1zcXVhcmUgdGVzdHMgYXJlIHVzZWQgaW4gc3RhdGlzdGljYWwgZm9yZW5zaWNzIHRvIGRldGVjdCBpcyBhIGZpdCBpcyBzdXNwaWNpb3VzbHkgZ29vZCBiZWNhdXNlIHRoZSBkYXRhIHdhcyBmYWJyaWNhdGVkLiBDaGktc3F1YXJlIHRlc3RzIG9mIHZhcmlhbmNlIGNhbiBiZSB0d28tdGFpbGVkLiBUaGVzZSBhcmUgbmljaGUgdXNlcyB0aG91Z2guDQoNCmBgYHtyfQ0KYXJncyhjaGlzcV90ZXN0KQ0KYGBgDQoNCiMjIyAqKkNoaS1zcXVhcmUgZ29vZG5lc3Mgb2YgZml0bmVzcyB0ZXN0KioNCg0KTGFzdCB0aW1lIHlvdSB1c2VkIGEgY2hpLXNxdWFyZSB0ZXN0IHRvIGNvbXBhcmUgcHJvcG9ydGlvbnMgaW4gdHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4gVGhpcyB0aW1lLCB3ZSdsbCB1c2UgYW5vdGhlciB2YXJpYW50IG9mIHRoZSBjaGktc3F1YXJlIHRlc3QgdG8gY29tcGFyZSBhIHNpbmdsZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB0byBhIGh5cG90aGVzaXplZCBkaXN0cmlidXRpb24uDQoNCiMjIyAqKlB1cnBsZSBsaW5rcyoqDQoNClRoZSBTdGFjayBPdmVyZmxvdyBzdXJ2ZXkgY29udGFpbnMgYSBmdW4gcXVlc3Rpb24gYWJvdXQgaG93IHRoZSB1c2VyIGZlZWxzIHdoZW4gdGhleSBkaXNjb3ZlciB0aGF0IHRoZXkgYWxyZWFkeSB2aXNpdGVkIHRoZSB0b3AgcmVzb3VyY2Ugd2hlbiB0cnlpbmcgdG8gc29sdmUgYSBjb2RpbmcgcHJvYmxlbS4gVGhlcmUgYXJlIGZvdXIgcG9zc2libGUgYW5zd2Vycywgc3RvcmVkIGluIHRoZSBwdXJwbGVfbGluayB2YXJpYWJsZS4NCg0KYGBge3J9DQpwdXJwbGVfbGlua19jb3VudHMgPC0gc3RhY2tfb3ZlcmZsb3cgJT4lDQogIGNvdW50KHB1cnBsZV9saW5rKSAlPiUNCiAgYXJyYW5nZShkZXNjKG4pKQ0KDQpwdXJwbGVfbGlua19jb3VudHMNCmBgYA0KDQojIyMgKipEZWNsYXJpbmcgdGhlIGh5cG90aGVzaXMqKg0KDQpMZXQncyBoeXBvdGhlc2l6ZSB0aGF0IGhhbGYgdGhlIHVzZXJzIGluIHRoZSBwb3B1bGF0aW9uIHdvdWxkIHJlc3BvbmQgIkhlbGxvIG9sZCBmcmllbmQiLCBhbmQgdGhlIG90aGVyIHRocmVlIHJlc3BvbnNlcyB3b3VsZCBnZXQgb25lIHNpeHRoIGVhY2guIFRoZSBgdHJpYmJsZWAgZnVuY3Rpb24gcHJvdmlkZXMgYSBjb252ZW5pZW50IHdheSBvZiBtYW51YWxseSBlbnRlcmluZyB2YWx1ZXMgZm9yIGEgZGF0YSBmcmFtZSwgYW5kIHJldHVybnMgYSBzdGFuZGFyZCB0aWJibGUuIFdlIGNhbiBzcGVjaWZ5IHRoZSBoeXBvdGhlc2VzIGFzIHdoZXRoZXIgb3Igbm90IHRoZSBzYW1wbGUgbWF0Y2hlcyB0aGlzIGh5cG90aGVzaXplZCBkaXN0cmlidXRpb24uIFRoZSB0ZXN0IHN0YXRpc3RpYyBtZWFzdXJlcyBob3cgZmFyIHRoZSBkaXN0cmlidXRpb24gb2YgcHJvcG9ydGlvbnMgb2JzZXJ2ZWQgaW4gdGhlIHNhbXBsZSBpcyBmcm9tIHRoZSBoeXBvdGhlc2l6ZWQgZGlzdHJpYnV0aW9uIG9mIHByb3BvcnRpb25zLiBJJ2xsIHNldCBhIHNpZ25pZmljYW5jZSBsZXZlbCBvZiBwb2ludC16ZXJvLW9uZS4NCg0KXjFeIHRyaWJibGUgaXMgc2hvcnQgZm9yICJyb3ctd2lzZSB0aWJibGUiOyBub3QgdG8gYmUgY29uZnVzZWQgd2l0aCB0aGUgYWxpZW4gc3BlY2llcyBmcm9tIFN0YXIgVHJlaw0KDQpgYGB7cn0NCmh5cG90aGVzaXplZCA8LSB0cmliYmxlKH4gcHVycGxlX2xpbmssIH4gcHJvcCwNCiAgICAgICAgICAgICAgICAgICAgICAgICJIZWxsbywgb2xkIGZyaWVuZCIsIDEvMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJBbXVzZWQiLCAxLzYsDQogICAgICAgICAgICAgICAgICAgICAgICAiSW5kaWZmZXJlbnQiLCAxLzYsDQogICAgICAgICAgICAgICAgICAgICAgICAiSW5kaWZmZXJlbnQiLCAxLzYsDQogICAgICAgICAgICAgICAgICAgICAgICAiQW5ub3llZCIsIDEvNikNCmh5cG90aGVzaXplZA0KYGBgDQoNCiMjIyAqKkh5cG90aGVzaXplZCBjb3VudHMgYnkgY2F0ZWdvcnkqKg0KDQpUbyB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcHVycGxlIGxpbmtzLCBpdCB3aWxsIGhlbHAgdG8gaGF2ZSB0aGUgaHlwb3RoZXNpemVkIGNvdW50cy4gVGhlc2UgYXJlIHRoZSBoeXBvdGhlc2l6ZWQgcHJvcG9ydGlvbnMgdGltZXMgdGhlIHRvdGFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gdGhlIHNhbXBsZS4NCg0KYGBge3J9DQpuX3RvdGFsIDwtIG5yb3coc3RhY2tfb3ZlcmZsb3cpDQpoeXBvdGhlc2l6ZWQgPC0gdHJpYmJsZSh+IHB1cnBsZV9saW5rLCB+IHByb3AsDQogICAgICAgICAgICAgICAgICAgICAgICAiSGVsbG8sIG9sZCBmcmllbmQiLCAxLzIsDQogICAgICAgICAgICAgICAgICAgICAgICAiQW11c2VkIiwgMS82LA0KICAgICAgICAgICAgICAgICAgICAgICAgIkluZGlmZmVyZW50IiwgMS82LA0KICAgICAgICAgICAgICAgICAgICAgICAgIkluZGlmZmVyZW50IiwgMS82LA0KICAgICAgICAgICAgICAgICAgICAgICAgIkFubm95ZWQiLCAxLzYpICU+JQ0KICBtdXRhdGUobiA9IHByb3AgKiBuX3RvdGFsKQ0KDQpoeXBvdGhlc2l6ZWQNCmBgYA0KDQojIyMgKipWaXN1YWxpemluZyBjb3VudHMqKg0KDQpUaGUgbmF0dXJhbCB3YXkgdG8gdmlzdWFsaXplIHRoZSBjYXRlZ29yaWVzIGlzIHdpdGggYSBiYXIgcGxvdC4gV2UncmUgdXNpbmcgYGdlb21fY29sKClgIGhlcmUgc2luY2Ugd2UgYWxyZWFkeSBjYWxjdWxhdGVkIHRoZSBjb3VudHMuIFRoZSBwdXJwbGUgcG9pbnRzIHNob3cgdGhlIGh5cG90aGVzaXplZCBjb3VudHMsIHNvIHlvdSBjYW4gY29tcGFyZSB0aGUgc2FtcGxlIGRpc3RyaWJ1dGlvbiB0byB0aGUgaHlwb3RoZXNpemVkIGRpc3RyaWJ1dGlvbi4gVHdvIG9mIHRoZSBiYXJzIGFyZSBjbG9zZSB0byB0aGUgdmFsdWVzIHdlIGh5cG90aGVzaXplZCBhbmQgdHdvIGFyZSBzbGlnaHRseSBkaWZmZXJlbnQuIFdlJ2xsIG5lZWQgdG8gcnVuIHRoZSBoeXBvdGhlc2lzIHRlc3QgdG8gc2VlIGlmIHRoZSBkaWZmZXJlbmNlcyBhcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4NCg0KYGBge3J9DQpnZ3Bsb3QocHVycGxlX2xpbmtfY291bnRzLCBhZXMocHVycGxlX2xpbmssIG4pKSArDQogIGdlb21fY29sKCkgKyANCiAgZ2VvbV9wb2ludChkYXRhID0gaHlwb3RoZXNpemVkLCBjb2xvciA9ICJyZWQiKQ0KYGBgDQoNCiMjIyAqKkNoaS1zcXVhcmUgZ29vZG5lc3Mgb2YgZml0IHRlc3QgdXNpbmcgYGNoaXNxX3Rlc3QoKWAqKg0KDQpUaGUgb25lIHNhbXBsZSBjaGktc3F1YXJlIHRlc3QgaXMgY2FsbGVkIGEgZ29vZG5lc3Mgb2YgZml0IHRlc3QuIFRvIHJ1biBpdCwgd2UgbmVlZCB0aGUgaHlwb3RoZXNpemVkIHByb3BvcnRpb25zIGluIHZlY3RvciBmb3JtIHJhdGhlciB0aGFuIGluIGEgdGliYmxlLiBBZ2FpbiB3ZSB1c2UgYGNoaXNxX3Rlc3QoKWAgZnJvbSBgaW5mZXJgLiBIb3dldmVyLCB0aGlzIHRpbWUgdGhlIGFyZ3VtZW50cyBhcmUgZGlmZmVyZW50LiBQaXBpbmcgZnJvbSB0aGUgZGF0YXNldCwgd2Ugc2V0IHJlc3BvbnNlIHRvIHRoZSBuYW1lIG9mIHRoZSBjb2x1bW4gb2YgaW50ZXJlc3QsIGFuZCBzZXQgJHAkIHRvIHRoZSBoeXBvdGhlc2l6ZWQgZGlzdHJpYnV0aW9uLiAqKlRoZSBkZWdyZWVzIG9mIGZyZWVkb20gYXJlIG9uZSBsZXNzIHRoYW4gdGhlIG51bWJlciBvZiBjaG9pY2VzIGluIHRoZSBzdXJ2ZXkqKi4gRm91ciBtaW51cyBvbmUgaXMgdGhyZWUuIFRoZSAqKnAtdmFsdWUgaXMgdmVyeSBzbWFsbCwgbXVjaCBsb3dlciB0aGFuIHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgd2Ugc2V0KiouIFRodXMgKip3ZSBjb25jbHVkZSB0aGF0IHRoZSBzYW1wbGUgZGlzdHJpYnV0aW9uIG9mIHByb3BvcnRpb25zIGlzIGRpZmZlcmVudCB0aGFuIHRoZSBoeXBvdGhlc2l6ZWQgZGlzdHJpYnV0aW9uIG9mIHByb3BvcnRpb25zKiouDQoNCmBgYHtyfQ0KaHlwb3RoZXNpemVkX3Byb3BzIDwtIGMoIkhlbGxvLCBvbGQgZnJpZW5kIiA9IDEvMiwgQW11c2VkID0gMS82LCBJbmRpZmZlcmVudCA9IDEvNiwgQW5ub3llZCA9IDEvNikNCmBgYA0KDQpgYGB7cn0NCnN0YWNrX292ZXJmbG93ICU+JQ0KICBjaGlzcV90ZXN0KHJlc3BvbnNlID0gcHVycGxlX2xpbmssIHAgPSBoeXBvdGhlc2l6ZWRfcHJvcHMpDQpgYGANCg0KIyMjIFRlc3Rpbmcgc2FtcGxlIHNpemUNCg0KSW4gb3JkZXIgdG8gY29uZHVjdCBhIGh5cG90aGVzaXMgdGVzdCwgYW5kIGJlIHN1cmUgdGhhdCB0aGUgcmVzdWx0IGlzIGZhaXIsIGEgc2FtcGxlIG11c3QgbWVldCB0aHJlZSByZXF1aXJlbWVudHM6IGl0IGlzIGEgcmFuZG9tIHNhbXBsZSBvZiB0aGUgcG9wdWxhdGlvbjsgdGhlIG9ic2VydmF0aW9ucyBhcmUgaW5kZXBlbmRlbnQ7IGFuZCB0aGVyZSBhcmUgZW5vdWdoIG9ic2VydmF0aW9ucy4gT2YgdGhlc2UsIG9ubHkgdGhlIGxhc3QgY29uZGl0aW9uIGlzIGVhc2lseSB0ZXN0YWJsZSB3aXRoIGNvZGUuDQoNClRoZSBtaW5pbXVtIHNhbXBsZSBzaXplIGRlcGVuZHMgb24gdGhlIHR5cGUgb2YgaHlwb3RoZXNpcyB0ZXN0cyB5b3Ugd2FudCB0byBwZXJmb3JtLiBMZXQncyB0ZXN0IHNvbWUgc2NlbmFyaW9zIG9uIHRoZSBgbGF0ZV9zaGlwbWVudHNgIGRhdGFzZXQuDQoNCi0gICBVc2luZyB0aGUgYGxhdGVfc2hpcG1lbnRzYCBkYXRhc2V0LCBnZXQgY291bnRzIGJ5IHRoZSBgZnJlaWdodF9jb3N0X2dyb3VwYCBjb2x1bW5zLg0KDQotICAgSW5zZXJ0IGEgc3VpdGFibGUgbnVtYmVyIHRvIGluc3BlY3Qgd2hldGhlciB0aGUgY291bnRzIGFyZSAiYmlnIGVub3VnaCIgZm9yIGEgdHdvIHNhbXBsZSB0LXRlc3QuDQoNCmBgYHtyfQ0KIyBHZXQgY291bnRzIGJ5IGZyZWlnaHRfY29zdF9ncm91cA0KY291bnRzIDwtIGxhdGVfc2hpcG1lbnRzICU+JQ0KICBjb3VudChmcmVpZ2h0X2Nvc3RfZ3JvdXApDQoNCiMgU2VlIHRoZSByZXN1bHQgDQpjb3VudHMNCg0KIyBJbnNwZWN0IHdoZXRoZXIgdGhlIGNvdW50cyBhcmUgYmlnIGVub3VnaA0KcHJpbnQoYWxsKGNvdW50cyRuID49IDMwKSkNCmBgYA0KDQotICAgVXNpbmcgdGhlIGBsYXRlX3NoaXBtZW50c2AgZGF0YXNldCwgZ2V0IGNvdW50cyBieSB0aGUgYGxhdGVgIGNvbHVtbi4NCg0KLSAgIEluc2VydCBhIHN1aXRhYmxlIG51bWJlciB0byBpbnNwZWN0IHdoZXRoZXIgdGhlIGNvdW50cyBhcmUgImJpZyBlbm91Z2giIGZvciBhIG9uZSBzYW1wbGUgcHJvcG9ydGlvbiB0ZXN0Lg0KDQpgYGB7cn0NCiMgR2V0IGNvdW50cyBieSBsYXRlDQpjb3VudHMgPC0gbGF0ZV9zaGlwbWVudHMgJT4lDQogIGNvdW50KGxhdGUpDQoNCiMgU2VlIHRoZSByZXN1bHRzIA0KY291bnRzDQoNCiMgSW5zcGVjdCB3aGV0aGVyIHRoZSBjb3VudHMgYXJlIGJpZyBlbm91Z2gNCmFsbChjb3VudHMkbiA+PSAxMCkNCmBgYA0KDQotICAgVXNpbmcgdGhlIGBsYXRlX3NoaXBtZW50c2AgZGF0YXNldCwgZ2V0IGNvdW50cyBieSB0aGUgYHZlbmRvcl9pbmNvX3Rlcm1gIGFuZCBgZnJlaWdodF9jb3N0X2dyb3VwYCBjb2x1bW5zLg0KDQotICAgSW5zZXJ0IGEgc3VpdGFibGUgbnVtYmVyIHRvIGluc3BlY3Qgd2hldGhlciB0aGUgY291bnRzIGFyZSAiYmlnIGVub3VnaCIgZm9yIGEgY2hpLXNxdWFyZSBpbmRlcGVuZGVuY2UgdGVzdC4NCg0KYGBge3J9DQojIENvdW50IHRoZSB2YWx1ZXMgb2YgdmVuZG9yX2luY29fdGVybSBhbmQgZnJlaWdodF9jb3N0X2dyb3VwDQpjb3VudHMgPC0gbGF0ZV9zaGlwbWVudHMgJT4lDQogIGNvdW50KHZlbmRvcl9pbmNvX3Rlcm0sIGZyZWlnaHRfY29zdF9ncm91cCkNCg0KIyBTZWUgdGhlIHJlc3VsdHMgDQpjb3VudHMNCg0KIyBJbnNwZWN0IHdoZXRoZXIgdGhlIGNvdW50cyBhcmUgYmlnIGVub3VnaA0KYWxsKGNvdW50cyRuID49IDUpDQpgYGANCg0KLSAgIFVzaW5nIHRoZSBgbGF0ZV9zaGlwbWVudHNgIGRhdGFzZXQsIGdldCBjb3VudHMgYnkgdGhlIGBzaGlwbWVudF9tb2RlYCBjb2x1bW4uDQoNCi0gICBJbnNlcnQgYSBzdWl0YWJsZSBudW1iZXIgdG8gaW5zcGVjdCB3aGV0aGVyIHRoZSBjb3VudHMgYXJlICJiaWcgZW5vdWdoIiBmb3IgYW4gQU5PVkEgdGVzdC4NCg0KICAgIGBgYHtyfQ0KICAgICMgQ291bnQgdGhlIHZhbHVlcyBvZiBzaGlwbWVudF9tb2RlDQogICAgY291bnRzIDwtIGxhdGVfc2hpcG1lbnRzICU+JQ0KICAgICAgY291bnQoc2hpcG1lbnRfbW9kZSkNCg0KICAgICMgU2VlIHRoZSByZXN1bHQNCiAgICBjb3VudHMNCg0KICAgICMgSW5zcGVjdCB3aGV0aGVyIHRoZSBjb3VudHMgYXJlIGJpZyBlbm91Z2gNCiAgICBhbGwoY291bnRzJG4gPj0gMzApDQogICAgYGBgDQoNCiMjIyAqKlRoZSAiVGhlcmUgaXMgb25seSBvbmUgdGVzdCIgZnJhbWV3b3JrKioNCg0KVHJhZGl0aW9uYWwgaHlwb3RoZXNpcyB0ZXN0cyBtYWtlIGFzc3VtcHRpb25zIGFib3V0IHRoZSBzYW1wbGUgZGF0YXNldC4gSGVyZSwgd2UnbGwgbG9vayBhdCB3aGF0IHRvIGRvIHdoZW4gdGhvc2UgYXNzdW1wdGlvbnMgZG9uJ3QgaG9sZC4NCg0KIyMjICoqSW1iYWxhbmNlIGRhdGEqKg0KDQpTb21ldGltZXMgeW91IGNhbid0IGdldCBhIHNhbXBsZSByZXByZXNlbnRpbmcgYWxsIHBhcnRzIG9mIHlvdXIgcG9wdWxhdGlvbi4gQ29uc2lkZXIgdGhpcyBzdWJzZXQgb2YgdGhlIFN0YWNrIE92ZXJmbG93IHN1cnZleS4gVGhlcmUgYXJlIGZld2VyIHVzZXJzIHdpdGggaG9iYnlpc3QgIk5vIiB0aGFuICJZZXMiLCBhbmQgZmV3ZXIgdXNlcnMgd2l0aCAiQXQgbGVhc3QgMzAiIGFnZXMgdGhhbiAiVW5kZXIgMzAiLiBJbiBmYWN0LCB0aGVyZSBhcmUgbm8gdXNlcnMgd2l0aCBob2JieWlzdCAiTm8iIGFuZCBhZ2UgIkF0IGxlYXN0IDMwIi4gVGhpcyBpcyBhIHByb2JsZW0gZm9yIGEgcHJvcG9ydGlvbiB0ZXN0LCBzaW5jZSBpdCByZXF1aXJlcyB0aGF0IGV2ZXJ5IGdyb3VwIGhhcyBhdCBsZWFzdCB0ZW4gb2JzZXJ2YXRpb25zLiBJIHVzZWQgYGNvdW50KOKApiwgLmRyb3ApYCBhcmd1bWVudCB0byBpbmNsdWRlIHN1Ymdyb3VwcyB3aXRoIGEgemVybyBjb3VudC4gKipBIGRhdGFzZXQgd2hlcmUgc29tZSBncm91cCdzIGNvdW50cyBhcmUgbXVjaCBsYXJnZXIgdGhhbiBvdGhlcnMgaXMgY2FsbGVkICJpbWJhbGFuY2VkKioiLg0KDQpgYGB7cn0NCnN0YWNrX292ZXJmbG93ICU+JSANCiAgY291bnQoaG9iYnlpc3QsIGFnZV9jYXQsIC5kcm9wID0gRkFMU0UpDQpgYGANCg0KIyMjICoqSHlwb3RoZXNlcyoqDQoNCldlIGNhbiBkZWNsYXJlIGh5cG90aGVzZXMgdG8gdGVzdC4gSSdsbCBzZXQgYSBzaWduaWZpY2FuY2UgbGV2ZWwgb2YgcG9pbnQtb25lLg0KDQokSF97MH0kIDogVGhlIHByb3BvcnRpb24gb2YgaG9iYnlpc3RzIHVuZGVyIDMwIGlzIHRoZSBzYW1lIGFzIHRoZSBwcm9wb3J0aW9uIG9mIGhvYmJ5aXN0cyBhdCBsZWFzdCAzMC4NCg0KJEhfe0F9JCA6IFRoZSBwcm9wb3J0aW9uIG9mIGhvYmJpc3RzIHVuZGVyIDMwIGlzIGRpZmZlcmVudCBmcm9tIHRoZSBwcm9wb3J0aW9uIG9mIGhvYmJ5aXN0cyBhdCBsZWFzdCAzMA0KDQpgYGB7cn0NCmFscGhhIDwtIDAuMQ0KYGBgDQoNCiMjIyAqKlByb2NlZWRpbmcgd2l0aCBhIHByb3BvcnRpb24gdGVzdCByZWdhcmRsZXNzKioNCg0KTGV0J3MgaWdub3JlIHRoZSBzbWFsbCBzYW1wbGUgcHJvYmxlbXMgYW5kIHJ1biBhIHByb3BvcnRpb24gdGVzdC4gVGhlIHAtdmFsdWUgaXMgJDIuNDAzIFx0aW1lcyAxMF57LTV9JCAsIHdoaWNoIGlzIGJlbG93IHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwuIFRoaXMgdGVzdCBzdWdnZXN0cyB0aGF0IHRoZXJlIGlzIGVub3VnaCBldmlkZW5jZSB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcy4gV2UnbGwgcmV2aXNpdCB0aGlzIGxhdGVyLg0KDQpgYGB7cn0NCnN0YWNrX292ZXJmbG93ICU+JQ0KICBwcm9wX3Rlc3QoaG9iYnlpc3QgfiBhZ2VfY2F0LCBvcmRlciA9IGMoIkF0IGxlYXN0IDMwIiwgIlVuZGVyIDMwIiksIHN1Y2Nlc3MgPSAiWWVzIiwNCiAgICAgICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIsIGNvcnJlY3QgPSBGQUxTRSkNCmBgYA0KDQojIyMgKipBIGdyYW1tYXIgb2YgZ3JhcGhpY3MqKg0KDQpUcmFkaXRpb25hbGx5LCBldmVyeSB0eXBlIG9mIHBsb3QgaGFzIGEgbmFtZSwgbGlrZSBzY2F0dGVyIHBsb3Qgb3IgbGluZSBwbG90LiBJbiBiYXNlLVIgeW91IGRlY2lkZSB0aGUgcGxvdCB0eXBlIGJ5IHNwZWNpZnlpbmcgYXJndW1lbnRzIHRvIHRoZSBwbG90IGZ1bmN0aW9uLCBvciBieSBjYWxsaW5nIGEgZGVkaWNhdGVkIGZ1bmN0aW9uIGxpa2UgaGlzdC4gVGhpcyBpcyBmaW5lIHVudGlsIHlvdSB3YW50IHRvIGRyYXcgYSBjdXN0b20gcGxvdCBsaWtlIGEgaGlzdG9ncmFtIHdpdGggcG9sYXIgY29vcmRpbmF0ZXMuIElmIGEgZnVuY3Rpb24gdG8gZHJhdyB0aGF0IGRvZXNuJ3QgZXhpc3QsIHlvdSBhcmUgc3R1Y2suIFRoZSBiZWF1dHkgb2YgdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3Mgc3lzdGVtIHRoYXQgZ2dwbG90MiBpbXBsZW1lbnRzIGlzIHRoYXQgcGxvdHMgYXJlIGJyb2tlbiBkb3duIGludG8gY29tcG9uZW50cyB0aGF0IGNhbiBiZSBjb21iaW5lZCBpbiBtYW55IHdheXMuIEl0J3MgbW9yZSBmbGV4aWJsZSBjcmVhdGluZyBhIG5ldyBmdW5jdGlvbiBmb3IgZXZlcnkgcGxvdCB0eXBlIHlvdSBjYW4gdGhpbmsgb2YuIFdoYXQgaWYgdGhlcmUgd2FzIGEgZ3JhbW1hciBvZiBoeXBvdGhlc2lzIHRlc3RzPw0KDQp8IFBsb3QgdHlwZSAgICAgICAgfCBCYXNlLVIgICAgICAgICAgICAgICB8IGdncGxvdDIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8ICoqU2NhdHRlciBwbG90KiogfCBgcGxvdCgsIHR5cGUgPSAicCIpYCB8IGBnZ3Bsb3QoICkgKyBnZW9tX3BvaW50KCApYCAgICAgICAgICAgICAgICB8DQp8ICoqTGluZSBwbG90KiogICAgfCBgcGxvdCgsIHR5cGUgPSAibCIpYCB8IGBnZ3Bsb3QoICkgKyBnZW9tX2xpbmUoIClgICAgICAgICAgICAgICAgICB8DQp8ICoqSGlzdG9ncmFtKiogICAgfCBgaGlzdCggKWAgICAgICAgICAgICB8IGBnZ3Bsb3QoICkgKyBnZW9tX2hpc3RvZ3JhbSggKWAgICAgICAgICAgICB8DQp8ICoqQm94IHBsb3QqKiAgICAgfCBgYm94cGxvdCggKWAgICAgICAgICB8IGBnZ3Bsb3QoICkgKyBnZW9tX2JveHBsb3QoIClgICAgICAgICAgICAgICB8DQp8ICoqQmFyIHBsb3QqKiAgICAgfCBgYmFycGxvdCggKWAgICAgICAgICB8IGBnZ3Bsb3QoICkgKyBnZW9tX2JhciggKWAgICAgICAgICAgICAgICAgICB8DQp8ICoqUGllIHBsb3QqKiAgICAgfCBgcGllKCApYCAgICAgICAgICAgICB8IGBnZ3Bsb3QoICkgKyBnZW9tX2JhciggKSArIGNvb3JkX3BvbGFyKCApYCB8DQoNCiMjIyAqKkEgZ3JhbW1hciBvZiBoeXBvdGhlc2lzIHRlc3RzKioNCg0KVGhlIGdvb2QgbmV3cyBpcyB0aGF0IHRoZXJlIGlzIGEgZ3JhbW1hciBvZiBoeXBvdGhlc2lzIHRlc3RzLiBJdCB3YXMgaW52ZW50ZWQgYnkgRGF0YUNhbXAgaW5zdHJ1Y3RvciBBbGxlbiBEb3duZXksIGFuZCBpcyBjYWxsZWQgdGhlICoqIlRoZXJlIGlzIG9ubHkgb25lIHRlc3QiKiogZnJhbWV3b3JrLiBUaGUgZnJhbWV3b3JrIHdhcyBpbXBsZW1lbnRlZCBpbiBSIGluIHRoZSBgaW5mZXJgIHBhY2thZ2UgdGhhdCB5b3UndmUgdXNlZCBmb3IgcHJvcG9ydGlvbiBhbmQgY2hpLXNxdWFyZSB0ZXN0cy4gQWxsIGh5cG90aGVzaXMgdGVzdHMgY2FuIGJlIHBlcmZvcm1lZCB3aXRoIHRoaXMgY29kZSBmbG93LCBiYXNlZCBvbiBzcGVjaWZ5LCBoeXBvdGhlc2l6ZSwgZ2VuZXJhdGUsIGFuZCBjYWxjdWxhdGUuIEJ5IGNoYW5naW5nIHRoZSBhcmd1bWVudHMgdG8gdGhvc2UgZnVuY3Rpb25zIHlvdSBjYW4gcnVuIHN0YW5kYXJkIHRlc3RzIGxpa2UgdC10ZXN0cywgb3IgZGV2aXNlIHlvdXIgb3duIGN1c3RvbSB0ZXN0cy4gV2UnbGwgY292ZXIgc3BlY2lmeSBhbmQgaHlwb3RoZXNpemUgbm93LCBhbmQgdGhlIG90aGVycyBpbiB0aGUgbmV4dCB2aWRlby4gZ2VuZXJhdGUgaXMgd29ydGggbWVudGlvbmluZyBicmllZmx5LiBJdCBjcmVhdGVzIHNpbXVsYXRlZCBkYXRhIHJlZmxlY3RpbmcgdGhlIG51bGwgaHlwb3RoZXNpcy4gKipVc2luZyBzaW11bGF0aW9uIHJhdGhlciB0aGFuIGFyaXRobWV0aWMgZXF1YXRpb25zIHRvIGdldCB0aGUgdGVzdCBzdGF0aXN0aWMgaXMgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSoqLCBzbyBpdCBoYXMgb25seSBiZWNvbWUgYWNjZXNzaWJsZSB3aXRoIG1vZGVybiBjb21wdXRpbmcgcG93ZXIuIEhvd2V2ZXIsIHNpbXVsYXRpb24gaGFzIGEgYmVuZWZpdCB0aGF0IGl0IHdvcmtzIHdlbGwgZXZlbiB3aGVuIHlvdSBoYXZlIHNtYWxsIHNhbXBsZXMgb3IgaW1iYWxhbmNlZCBkYXRhLg0KDQpeMV4gQWxsZW4gRG93bmV5IHRlYWNoZXMgIkV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgaW4gUHl0aG9uIi4NCg0KYGBge3J9DQpudWxsX2Rpc3RuIDwtIGRhdGFzZXQgJT4lDQogIHNwZWNpZnkoKSAlPiUNCiAgaHlwb3RoZXNpemUoKSAlPiUNCiAgZ2VuZXJhdGUoKSAlPiUNCiAgY2FsY3VsYXRlKCkNCmBgYA0KDQpgYGB7cn0NCm9ic19zdGF0IDwtIGRhdGFzZXQgJT4lDQogIHNwZWNpZnkoKSAlPiUNCiAgY2FsY3VsYXRlKCkNCmBgYA0KDQpgYGB7cn0NCmdldF9wX3ZhbHVlKG51bGxfZGlzdG4gb2JzX3N0YXQpDQpgYGANCg0KIyMjICoqU3BlY2lmeWluZyB0aGUgdmFyaWFibGVzIG9mIGludGVyZXN0KioNCg0KVGhlIFN0YWNrIE92ZXJmbG93IHN1cnZleSBjb250YWlucyBtYW55IHZhcmlhYmxlcywgYnV0IGZvciB0aGlzIHRlc3QsIHdlIG9ubHkgY2FyZSBhYm91dCB0d28gb2YgdGhlbS4gYHNwZWNpZnkoKWAgd29ya3MgbGlrZSBkcGx5cidzIHNlbGVjdCwgcmV0dXJuaW5nIG9ubHkgdGhlIHJlc3BvbnNlIGFuZCBleHBsYW5hdG9yeSBjb2x1bW5zLg0KDQojIyMgKipzcGVjaWZ5KCkqKg0KDQpUbyBjYWxsIHNwZWNpZnksIHlvdSBwaXBlIGZyb20gdGhlIGRhdGFzZXQsIGFuZCBwYXNzIGEgZm9ybXVsYSB3aXRoIHRoZSByZXNwb25zZSBvbiB0aGUgbGVmdCBhbmQgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlIG9uIHRoZSByaWdodC4gSW4gdGhlIG9uZSBzYW1wbGUgY2FzZSwgdXNlIE5VTEwgZm9yIHRoZSBleHBsYW5hdG9yeSB2YWx1ZS4gSnVzdCBhcyB3aGVuIHlvdSBjYWxsZWQgYHByb3BfdGVzdCgpYCwgeW91IG5lZWQgdG8gdGVsbCBzcGVjaWZ5IHdoaWNoIHJlc3BvbnNlIHZhbHVlIGlzIHJlZ2FyZGVkIGFzIGEgc3VjY2Vzcy4gc3BlY2lmeSByZXR1cm5zIGEgdGliYmxlIHdpdGggYXR0cmlidXRlcyBub3RpbmcgdGhlIHJlc3BvbnNlIGFuZCBleHBsYW5hdG9yeSB2YXJpYWJsZSBuYW1lcy4NCg0KLSAgIEZvciAyIHNhbXBsZSB0ZXN0cywgdXNlOiBgcmVzcG9uc2UgfiBleHBsYW5hdG9yeWANCg0KLSAgIEZvciAxIHNhbXBsZSB0ZXN0cyB1c2UgYHJlc3BvbnNlIH4gTlVMTGANCg0KYGBge3J9DQpzdGFja19vdmVyZmxvdyAlPiUNCiAgc3BlY2lmeShob2JieWlzdCB+IGFnZV9jYXQsIHN1Y2Nlc3MgPSAiWWVzIikNCmBgYA0KDQojIyMgKipgaHlwb3RoZXNpemUoKWAqKg0KDQpgaHlwb3RoZXNpemUoKWAgZGVjbGFyZXMgdGhlIHR5cGUgb2YgbnVsbCBoeXBvdGhlc2lzLiBQcm9wb3J0aW9uIHRlc3RzIGFyZSBhIHNwZWNpYWwgY2FzZSBvZiB0aGUgY2hpLXNxdWFyZSBpbmRlcGVuZGVuY2UgdGVzdCwgc28gd2UgY2hvb3NlICJpbmRlcGVuZGVuY2UiLiBoeXBvdGhlc2l6ZSBkb2Vzbid0IGNhbGN1bGF0ZSBhbnl0aGluZzsgaXQgc2ltcGx5IGFkZHMgYW5vdGhlciBhdHRyaWJ1dGUgdG8gdGhlIGRhdGFzZXQuDQoNCi0gICBGb3IgMiBzYW1wbGUgdGVzdHMsIHVzZSBgImluZGVwZW5kZW5jZSJgIG9yIGAicG9pbnQiYA0KDQotICAgRm9yIDEgc2FtcGxlIHRlc3RzLCB1c2UgYCJwb2ludCJgDQoNCmBgYHtyfQ0Kc3RhY2tfb3ZlcmZsb3cgJT4lDQogIHNwZWNpZnkoaG9iYnlpc3QgfiBhZ2VfY2F0LCBzdWNjZXNzID0gIlllcyIpICU+JQ0KICBoeXBvdGhlc2lzZShudWxsID0gImluZGVwZW5kZW5jZSIpDQpgYGANCg0KIyMgKipDb250aW51aW5nIHRoZSBpbmZlciBwaXBlbGluZSoqDQoNClByZXZpb3VzbHkgd2Ugc2F3IHBhcnQgb2YgdGhlIGluZmVyIHBpcGVsaW5lIGZvciBzaW11bGF0aW9uLWJhc2VkIGh5cG90aGVzaXMgdGVzdHMuDQoNCiMjIyAqKlJlY2FwOiBoeXBvdGhlc2VzIGFuZCBkYXRhc2V0KioNCg0KV2Ugd2VyZSB0ZXN0aW5nIHRoZSBoeXBvdGhlc2lzIHRoYXQgdGhlIHByb3BvcnRpb24gb2YgaG9iYnlpc3RzIHdhcyB0aGUgc2FtZSBmb3IgdHdvIGFnZSBjYXRlZ29yaWVzLiBIb3dldmVyLCB0aGlzIHN1YnNldCBvZiB0aGUgU3RhY2sgT3ZlcmZsb3cgc3VydmV5IHdhcyBpbWJhbGFuY2VkLCB2aW9sYXRpbmcgdGhlIGFzc3VtcHRpb25zIG9mIHRoZSB0cmFkaXRpb25hbCBwcm9wb3J0aW9uIHRlc3QuDQoNCiRIX3swfSQgOiBUaGUgcHJvcG9ydGlvbiBvZiBob2JiaXlpc3RzIHVuZGVyIDMwIGlzIHRoZSBzYW1lIGFzIHRoZSBwcm9wwrRuIG9mIGhvYmJ5aXN0cyBhdCBsZWFzdCAzMC4NCg0KJEhfe0F9JCA6IFRoZSBwcm9wb3J0aW9uIG9mIGhvYmJ5aXN0cyB1bmRlciAzMCBpcyBkaWZmZXJlbnQgZnJvbSB0aGUgcHJvcMK0biBvZiBob2JieWlzdHMgYXQgbGVhc3QgMzAuDQoNCmBgYHtyfQ0KYWxwaGEgPC0gMC4xDQpgYGANCg0KIyMjICoqUmVjYXA6IHdvcmtmbG93KioNCg0KWW91IHNhdyB0aGUgY29tcGxldGUgd29ya2Zsb3cgZm9yIGEgY3VzdG9tIGh5cG90aGVzaXMgdGVzdC4gU28gZmFyLCB3ZSdkIGRvbmUgdGhlIHNwZWNpZnkgYW5kIGh5cG90aGVzaXplIHN0ZXBzLCByZXN1bHRpbmcgaW4gYSB0aWJibGUgd2l0aCB0d28gY29sdW1ucyBhbmQgc29tZSBleHRyYSBhdHRyaWJ1dGVzIGRlc2NyaWJpbmcgdGhlIHJlc3BvbnNlIGFuZCBleHBsYW5hdG9yeSB2YXJpYWJsZXMsIGFuZCB0aGUgdHlwZSBvZiBudWxsIGh5cG90aGVzaXMuDQoNCmBgYHtyfQ0KbnVsbF9kaXN0biA8LSBkYXRhc2V0ICU+JQ0KICBzcGVjaWZ5KCkgJT4lDQogIGh5cG90aGVzaXplKCkgJT4lDQogIGdlbmVyYXRlKCkgJT4lDQogIGNhbGN1bGF0ZSgpDQpgYGANCg0KYGBge3J9DQpvYnNlcnZlZF9zdGF0IDwtIGRhdGFzZXQgJT4lDQogIHNwZWNpZnkoKSAlPiUNCiAgY2FsY3VsYXRlKCkNCmBgYA0KDQpgYGB7cn0NCmdldF9wX3ZhbHVlKG51bGxfZGlzdG4sIG9ic2VydmVkX3N0YXQpDQpgYGANCg0KIyMjICoqTW90aXZhdGluZyBgZ2VuZXJhdGUoKWAqKg0KDQpJZiB3ZSBhc3N1bWUgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLCB0aGVuIGl0IHNob3VsZG4ndCBtYXR0ZXIgd2hpY2ggaG9iYnlpc3QgdmFsdWUgaXMgbWF0Y2hlZCB1cCB0byBlYWNoIGFnZSBjYXRlZ29yeSwgc2luY2UgdGhlIHByb3BvcnRpb24gb2YgaG9iYnlpc3RzIGlzIHRoZSBzYW1lIGluIGVhY2ggYWdlIGNhdGVnb3J5LiBXZSBjYW4gZ2VuZXJhdGUgYSBzaW11bGF0ZWQgZGF0YXNldCBieSBzaHVmZmxpbmcgdGhlIHJlc3BvbnNlIGhvYmJ5aXN0IHZhbHVlcyB3aGlsZSBrZWVwaW5nIHRoZSBleHBsYW5hdG9yeSBhZ2UgY2F0ZWdvcnkgdmFsdWVzIHRoZSBzYW1lLg0KDQokSF97MH0kIDogVGhlIHByb3BvcnRpb24gb2YgaG9iYml5aXN0cyB1bmRlciAzMCBpcyB0aGUgc2FtZSBhcyB0aGUgcHJvcMK0biBvZiBob2JieWlzdHMgYXQgbGVhc3QgMzAuDQoNCmlmICRIX3swfSQgaXMgdHJ1ZSwgdGhlbg0KDQotICAgSW4gZWFjaCByb3csIHRoZSBob2JiaXN0IHZhbHVlIGNvdWxkIGhhdmUgYXBwZWFyZWQgd2l0aCBlaXRoZXIgYWdlIGNhdGVnb3J5IHdpdGggZXF1YWwgcHJvYmFiaWxpdHkuDQoNCi0gICBUbyBzaW11bGF0ZSB0aGlzLCB3ZSBjYW4gcGVybXV0ZSAoc2h1ZmZsZSkgdGhlIGhvYmJ5aXN0IHZhbHVlcyB3aGlsZSBrZWVwaW5nIHRoZSBhZ2UgY2F0ZWdvcmllcyBmaXhlZA0KDQojIyMgKipPbmUgcGVybXV0YXRpb24qKg0KDQpDb25jZXB0dWFsbHksIHRoaXMgaXMgaG93IGdlbmVyYXRlIHNodWZmbGVzIHRoZSBkYXRhc2V0LiBJdCBzZWxlY3RzIHRoZSByZXNwb25zZSBjb2x1bW4sIHJhbmRvbWx5IHNhbXBsZXMgdGhlIHdob2xlIGNvbHVtbiB3aXRob3V0IHJlcGxhY2VtZW50LCB0aGVuIGJpbmRzIGl0IGJhY2sgdG8gdGhlIGV4cGxhbmF0b3J5IGNvbHVtbi4gQ29tcGFyZSB0aGUgb3JpZ2luYWwgZGF0YXNldCBvbiB0aGUgbGVmdCwgYW5kIHRoZSBzaHVmZmxlZCBkYXRhc2V0IG9uIHRoZSByaWdodC4gVGhlIGhvYmJ5aXN0IHZhbHVlcyBoYXZlIGNoYW5nZWQgYnV0IHRoZSBhZ2UgY2F0ZWdvcnkgdmFsdWVzIGhhdmUgbm90Lg0KDQpgYGB7cn0NCmJpbmRfY29scyhzdGFja19vdmVyZmxvdyAlPiUNCiAgICAgICAgICAgIHNlbGVjdChob2JieWlzdCkgJT4lDQogICAgICAgICAgICBzbGljZV9zYW1wbGUocHJvcCA9IDEpLA0KICAgICAgICAgIHN0YWNrX292ZXJmbG93ICU+JQ0KICAgICAgICAgICAgc2VsZWN0KGFnZV9jYXQpDQogICAgICAgICAgKQ0KYGBgDQoNCiMjIyAqKkdlbmVyYXRpbmcgbWFueSByZXBsaWNhdGVzKioNCg0KU2luY2UgcmFuZG9tbmVzcyBpcyB1c2VkIGluIHRoZSBzaHVmZmxpbmcgc3RlcCwgd2UgY2FuJ3QganVzdCBjcmVhdGUgb25lIHNpbXVsYXRlZCBkYXRhc2V0LCBvciB0aGUgcmVzdWx0cyB3b3VsZCBiZSB1bnJlbGlhYmxlLiBgZ2VuZXJhdGUoKWAgcGVyZm9ybXMgdGhlIHNpbXVsYXRpb24gc3RlcCBtYW55IHRpbWVzLiBFYWNoIHNpbXVsYXRlZCBkYXRhc2V0IGlzIGNhbGxlZCBhICoqcmVwbGljYXRlKiogYW5kIHJlcHJlc2VudHMgYW4gZXhhbXBsZSBvZiB3aGF0IHdlIG1pZ2h0IGV4cGVjdCB0aGUgdHdvIGNvbHVtbnMgdG8gbG9vayBsaWtlIGluIGEgdW5pdmVyc2Ugd2hlcmUgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyB0cnVlLg0KDQojIyMgKipgZ2VuZXJhdGUoKWAqKg0KDQpUbyBjYWxsIGBnZW5lcmF0ZSgpYCwgdGVsbCBpdCBob3cgbWFueSByZXBsaWNhdGVzIHlvdSB3YW50LiBGb3IgaW5kZXBlbmRlbmNlIHRlc3RzLCB0aGUgZ2VuZXJhdGlvbiB0eXBlIHNob3VsZCBhbHdheXMgYmUgInBlcm11dGUiLiBGb3IgY29udmVuaWVuY2UsIGdlbmVyYXRlIGNvbWJpbmVzIGFsbCB0aGUgc2ltdWxhdGVkIGRhdGFzZXRzIGludG8gYSBzaW5nbGUgdGliYmxlLiBUaGlzIGlzIGJpZyEgSXQgaGFzIGFzIG1hbnkgcm93cyBhcyB0aGUgb3JpZ2luYWwgZGF0YXNldCwgdGltZXMgdGhlIG51bWJlciBvZiByZXBsaWNhdGVzLg0KDQotICAgRm9yICJpbmRlcGVuZGVuY2UiIG51bGwgaHlwb3RoZXNpcywgc2V0IGB0eXBlYCB0byBgInBlcm11dGUiYC4NCg0KLSAgIEZvciAicG9pbnQiIG51bGwgaHlwb3RoZXNlcywgc2V0IGB0eXBlYCB0byBgImJvb3RzdHJhcCJgIG9yIGAic2ltdWxhdGUiYA0KDQpgYGB7cn0NCnN0YWNrX292ZXJmbG93ICU+JQ0KICBzcGVjaWZ5KGhvYmJ5aXN0IH4gYWdlX2NhdCwgc3VjY2VzcyA9ICJZZXMiKSAlPiUNCiAgaHlwb3RoZXNpemUobnVsbCA9ICJpbmRlcGVuZGVuY2UiKSAlPiUNCiAgZ2VuZXJhdGUocmVwcyA9IDUwMDAsIHR5cGUgPSAicGVybXV0ZSIpDQpgYGANCg0KIyMjICoqQ2FsY3VsYXRpbmcgdGhlIHRlc3Qgc3RhdGlzdGljKioNCg0KRm9yIGVhY2ggcmVwbGljYXRlLCB3ZSBjYWxjdWxhdGUgdGhlIHRlc3Qgc3RhdGlzdGljLCBpbiB0aGlzIGNhc2UgdGhlIGRpZmZlcmVuY2UgaW4gcHJvcG9ydGlvbnMgb2YgaG9iYnlpc3RzIGJldHdlZW4gdGhlIHR3byBhZ2UgY2F0ZWdvcmllcy4gNTAwMCByZXBsaWNhdGVzIGdpdmVzIDUwMDB6IGRpZmZlcmVuY2VzIGluIHByb3BvcnRpb25zLiBXZSBoYXZlIGEgZGlzdHJpYnV0aW9uIG9mIHRlc3Qgc3RhdGlzdGljcy4gVGhpcyBpcyBrbm93biBhcyB0aGUgbnVsbCBkaXN0cmlidXRpb24uDQoNCiMjIyAqKmBjYWxjdWxhdGUoKWAqKg0KDQpUbyB1c2UgdGhlIGRpZmZlcmVuY2UgaW4gcHJvcG9ydGlvbnMgYXMgdGhlIHRlc3Qgc3RhdGlzdGljLCBzZXQgdGhlIHN0YXQgYXJndW1lbnQgdG8gImRpZmYgaW4gcHJvcHMiLiBXZSBuZWVkIHRvIHRlbGwgY2FsY3VsYXRlIHdoaWNoIHByb3BvcnRpb24gdG8gc3VidHJhY3QgZnJvbSB3aGljaCBieSBzZXR0aW5nIHRoZSBvcmRlci4NCg0KXjFeIFRoZSA/Y2FsY3VsYXRlIGhlbHAgcGFnZSBsaXN0cyBhbGwgcG9zc2libGUgdGVzdCBzdGF0aXN0aWNzLg0KDQpgY2FsY3VsYXRlKClgIGNhbGN1bGF0ZXMgYSBkaXN0cmlidXRpb24gb2YgdGVzdCBzdGF0aXN0aWNzIGtub3duIGFzIHRoZSAqbnVsbCBkaXN0cmlidXRpb24qDQoNCmBgYHtyfQ0KbnVsbF9kaXN0biA8LSBzdGFja19vdmVyZmxvdyAlPiUNCiAgc3BlY2lmeShob2JieWlzdCB+IGFnZV9jYXQsIHN1Y2Nlc3MgPSAiWWVzIikgJT4lDQogIGh5cG90aGVzaXplKG51bGwgPSAiaW5kZXBlbmRlbmNlIikgJT4lDQogIGdlbmVyYXRlKHJlcHMgPSA1MDAwLCB0eXBlID0gInBlcm11dGUiKSAlPiUNCiAgY2FsY3VsYXRlKHN0YXQgPSAiZGlmZiBpbiBwcm9wcyIsIG9yZGVyID0gYygiQXQgbGVhc3QgMzAiLCAiVW5kZXIgMzAiKSkNCg0KbnVsbF9kaXN0bg0KYGBgDQoNCiMjIyAqKlZpc3VhbGl6aW5nIHRoZSBudWxsIGRpc3RyaWJ1dGlvbioqDQoNClRvIHZpc3VhbGl6ZSB0aGlzIG51bGwgZGlzdHJpYnV0aW9uIGFzIGEgaGlzdG9ncmFtLCBjYWxsIHZpc3VhbGl6ZS4gVGhpcyBoaXN0b2dyYW0gZG9lc24ndCBoYXZlIGEgbm9ybWFsIGJlbGwgY3VydmUuIFRoYXQgbWVhbnMgdGhlIGFzc3VtcHRpb25zIGZvciB0aGUgcHJvcG9ydGlvbiB0ZXN0IGRpZG4ndCBob2xkLiBBbHNvIG5vdGljZSB0aGF0IHRoZSB0ZXN0IHN0YXRpc3RpYyBvbmx5IHRha2VzIG5pbmUgZGlzdGluY3QgdmFsdWVzLg0KDQpgYGB7cn0NCnZpc3VhbGlzZShudWxsX2Rpc3RuKQ0KYGBgDQoNCiMjIyAqKkNhbGN1bGF0aW5nIHRoZSB0ZXN0IHN0YXRpc3RpYyBvbiB0aGUgb3JpZ2luYWwgZGF0YXNldCoqDQoNClRvIGNhbGN1bGF0ZSBhIHAtdmFsdWUsIHdlIG5lZWQgdG8gY29tcGFyZSB0aGUgbnVsbCBkaXN0cmlidXRpb24gdG8gdGhlIHRlc3Qgc3RhdGlzdGljIGZyb20gdGhlIG9yaWdpbmFsIGRhdGFzZXQuDQoNCiMjIyAqKk9ic2VydmVkIHN0YXRpc3RpYzogYHNwZWNpZnkoKSAlPiUgY2FsY3VsYXRlKClgKioNCg0KVG8gY2FsY3VsYXRlIHRoZSB0ZXN0IHN0YXRpc3RpYyBvbiB0aGUgb3JpZ2luYWwgc2FtcGxlIGRhdGFzZXQsIHlvdSByZXVzZSB0aGUgc3BlY2lmeSBhbmQgY2FsY3VsYXRlIHN0ZXBzLiBDb3B5IGFuZCBwYXN0ZSB0aGUgbnVsbCBkaXN0cmlidXRpb24gY29kZSwgdGhlbiByZW1vdmUgdGhlIGh5cG90aGVzaXplIGFuZCBnZW5lcmF0ZSBzdGVwcy4gSGVyZSwgdGhlIG9ic2VydmVkIHN0YXRpc3RpYyBpcyAtMC4wNy4NCg0KYGBge3J9DQpvYnNfc3RhdCA8LSBzdGFja19vdmVyZmxvdyAlPiUNCiAgc3BlY2lmeShob2JieWlzdCB+IGFnZV9jYXQsIHN1Y2Nlc3MgPSAiWWVzIikgJT4lDQogICMgaHlwb3Rlc2l6ZShudWxsID0gImluZGVwZW5kZW5jZSIpICU+JQ0KICAjIGdlbmVyYXRlKHJlcHMgPSA1MDAwLCB0eXBlID0gInBlcm11dGUiKSAlPiUNCiAgY2FsY3VsYXRlKHN0YXQgPSAiZGlmZiBpbiBwcm9wcyIsIG9yZGVyID0gYygiQXQgbGVhc3QgMzAiLCAiVW5kZXIgMzAiKSkNCg0Kb2JzX3N0YXQNCmBgYA0KDQojIyMgKipWaXN1YWxpemluZyB0aGUgbnVsbCBkaXN0cmlidXRpb24gdnMuIHRoZSBvYnNlcnZlZCBzdGF0KioNCg0KSGVyZSdzIHRoZSBudWxsIGRpc3RyaWJ1dGlvbiBoaXN0b2dyYW0gd2l0aCBhIHZlcnRpY2FsIGxpbmUgYWRkZWQgYXQgdGhlIG9ic2VydmVkIHN0YXRpc3RpYy4gVGhlIG9ic2VydmVkIHN0YXRpc3RpYyBpcyBhdCBvbmUgZWRnZSBvZiB0aGUgZGlzdHJpYnV0aW9uLiBEb2VzIHRoaXMgbWFrZSBpdCBkaWZmZXJlbnQgZW5vdWdoIGZyb20gdGhlIG51bGwgZGlzdHJpYnV0aW9uIHRoYXQgd2Ugc2hvdWxkIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzPyBXZSdsbCBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgcC12YWx1ZSB0byBmaW5kIG91dC4NCg0KYGBge3J9DQp2aXN1YWxpc2UobnVsbF9kaXN0bikgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gc3RhdCksIGRhdGEgPSBvYnNfc3RhdCwgY29sb3IgPSAicmVkIikNCmBgYA0KDQojIyMgKipHZXQgdGhlIHAtdmFsdWUqKg0KDQpUbyBnZXQgdGhlIHAtdmFsdWUsIGNhbGwgYGdldF9wX3ZhbHVlKClgLCBwYXNzaW5nIHRoZSBudWxsIGRpc3RyaWJ1dGlvbiBhbmQgb2JzZXJ2ZWQgc3RhdGlzdGljLiBZb3UgYWxzbyBuZWVkIHRvIHNldCB0aGUgdHlwZSBvZiBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzLiAqKlRoZSBhcmd1bWVudCBpcyBjYWxsZWQgZGlyZWN0aW9uIHJhdGhlciB0aGFuIGFsdGVybmF0aXZlKiosIGFuZCAidHdvIHNpZGVkIiBpcyBzZXBhcmF0ZWQgd2l0aCBhIHNwYWNlIHJhdGhlciB0aGFuIGEgZG90LiBUaGlzIHRpbWUgdGhlIHAtdmFsdWUgaXMgcG9pbnQtb25lLWZpdmUsIHdoaWNoIGlzIGdyZWF0ZXIgdGhhbiB0aGUgc2lnbmlmaWNhbmNlIGxldmVsIG9mIHBvaW50LW9uZS4gVGhhdCBtZWFucyB0aGF0IHdlIHNob3VsZCBmYWlsIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHN0aWNraW5nIHdpdGggdGhlcmUgYmVpbmcgbm8gZGlmZmVyZW5jZSBpbiBwcm9wb3J0aW9ucyBvZiBob2JieWlzdHMgYmV0d2VlbiBhZ2UgY2F0ZWdvcmllcy4gUmVjYWxsIHRoZSByZXN1bHRzIGZyb20gdGhlIGR1YmlvdXMgcHJvcG9ydGlvbiB0ZXN0IHdlIHVzZWQgYmVmb3JlLiBUaGF0IGhhZCB0aGUgb3Bwb3NpdGUgY29uY2x1c2lvbi4gSG9wZWZ1bGx5IHRoaXMgaWxsdXN0cmF0ZXMgdGhlIGRhbmdlciBvZiB1c2luZyB0cmFkaXRpb25hbCBoeXBvdGhlc2lzIHRlc3RzIHdoZW4gdGhlIGFzc3VtcHRpb25zIGFyZW4ndCBtZXQuIEFsdGhvdWdoIGl0IHRha2VzIG1vcmUgY29kZSwgdGhlIHNpbXVsYXRpb24tYmFzZWQgaHlwb3RoZXNpcyB0ZXN0cyBhcmUgbW9yZSByb2J1c3QgYWdhaW5zdCBzbWFsbCBzYW1wbGVzLCBhbmQgd2lsbCBoZWxwIHByZXZlbnQgeW91IHJlYWNoaW5nIHBvb3IgY29uY2x1c2lvbnMuDQoNCmBgYHtyfQ0KZ2V0X3BfdmFsdWUobnVsbF9kaXN0biwgb2JzX3N0YXQsIGRpcmVjdGlvbiA9ICJ0d28gc2lkZWQiKSAjIG5vdCBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiDQpgYGANCg0KIyMgKipOb24tcGFyYW1ldHJpYyBBTk9WQSBhbmQgdW5wYWlyZWQgdC10ZXN0cyoqDQoNClRoZSBzaW11bGF0aW9uLWJhc2VkIHByb3BvcnRpb24gdGVzdCB3ZSBwZXJmb3JtZWQgdXNpbmcgdGhlIGluZmVyIHBpcGVsaW5lIGF2b2lkZWQgdGhlIGFzc3VtcHRpb24gdGhhdCB0aGUgdGVzdCBzdGF0aXN0aWMgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuDQoNCiMjIyAqKk5vbi1wYXJhbWV0cmljIHRlc3RzKioNCg0KKipIeXBvdGhlc2lzIHRlc3RzIHRoYXQgZG9uJ3QgYXNzdW1lIGEgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGZvciB0aGUgdGVzdCBzdGF0aXN0aWMgYXJlIGNhbGxlZCBub24tcGFyYW1ldHJpYyB0ZXN0cyoqLiBUaGVyZSBhcmUgdHdvIHR5cGVzIG9mIG5vbi1wYXJhbWV0cmljIHRlc3QuIFRoZSBpbmZlciBwaXBlbGluZSBwZXJmb3JtcyBzaW11bGF0aW9uLWJhc2VkIHRlc3RzLiBPdGhlciB0ZXN0cyB1c2UgdGhlIHJhbmsgb2YgdGhlIGRhdGEuDQoNClR3byB0eXBlcyBvZiBub24tcGFyYW1ldHJpYyB0ZXN0czoNCg0KMS4gIFNpbXVsYXRpb24tYmFzZWQNCg0KMi4gIFJhbmstYmFzZWQNCg0KIyMjICoqYHRfdGVzdCgpYCoqDQoNCkhlcmUncyBhIHQtdGVzdC4gVGhpcyB0ZXN0ZWQgZGlmZmVyZW5jZXMgaW4gbWVhbiBhbm51YWwgY29tcGVuc2F0aW9uIGZvciBTdGFjayBPdmVyZmxvdyB1c2VycyBkZXBlbmRpbmcgb24gd2hldGhlciB0aGV5IGZpcnN0IGxlYXJuZWQgdG8gcHJvZ3JhbSBhcyBhIGNoaWxkIG9yIGFzIGFuIGFkdWx0LiBSYXRoZXIgdGhhbiBjYWxjdWxhdGluZyBpdCBzdGVwLWJ5IHN0ZXAsIFdlJ3ZlIHVzZWQgdF90ZXN0IGZyb20gaW5mZXIuIFRoZSBkZWdyZWVzIG9mIGZyZWVkb20gYXJlIGRpZmZlcmVudCBoZXJlIGJlY2F1c2UgdGhleSBhcmUgY2FsY3VsYXRlZCB1c2luZyBhbiBlcXVhdGlvbiBjYWxsZWQgdGhlICoqV2VsY2ggYXBwcm94aW1hdGlvbioqLCBpbnN0ZWFkIG9mIHRha2luZyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBtaW51cyB0d28uIEluIHByYWN0aWNlLCB0aGUgZGlmZmVyZW5jZSBpc24ndCBpbXBvcnRhbnQuIFRoZSBwLXZhbHVlIGlzIDAuMDMwODIgaW5zdGVhZCBvZiAwLiBMZXQncyBleHBsb3JlIHNvbWUgbm9uLXBhcmFtZXRyaWMgYWx0ZXJuYXRpdmVzIHRvIHRoZSB0LXRlc3QsIHN0YXJ0aW5nIHdpdGggdGhlIHNpbXVsYXRpb24tYmFzZWQgaW5mZXIgcGlwZWxpbmUuDQoNCiQkDQpIX3swfSA6IFxtdV97Y2hpbGR9IFwgLSBcbXVfe2FkdWx0fSBcID0gMCBcIFwgXCAgSF97QX0gOiBcbXVfe2NoaWxkfSBcIC0gXG11X3thZHVsdH0gXCA+IDANCiQkDQoNCmBgYHtyfQ0Kc3RhY2tfb3ZlcmZsb3cgJT4lDQogIHRfdGVzdChjb252ZXJ0ZWRfY29tcCB+IGFnZV9maXJzdF9jb2RlX2N1dCwgb3JkZXIgPSBjKCJjaGlsZCIsICJhZHVsdCIpLCBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyIikNCg0KYGBgDQoNCiMjIyAqKkNhbGN1bGF0aW5nIHRoZSBudWxsIGRpc3RyaWJ1dGlvbioqDQoNCkp1c3QgYXMgeW91IGRpZCB3aXRoIHRoZSBzaW11bGF0aW9uLWJhc2VkIHByb3BvcnRpb24gdGVzdCwgeW91IHN0YXJ0IGJ5IHNwZWNpZnlpbmcgdGhlIHZhcmlhYmxlcyBvZiBpbnRlcmVzdC4gSXQncyB0aGUgc2FtZSBmb3JtdWxhIHBhc3NlZCB0byB0X3Rlc3Q6IHRoZSBudW1lcmljIGNvbXBlbnNhdGlvbiBvbiB0aGUgbGVmdCBhbmQgdGhlIGFnZSBjYXRlZ29yaWVzIG9uIHRoZSByaWdodC4gTmV4dCB5b3UgZGVjbGFyZSB0aGUgbnVsbCBoeXBvdGhlc2lzLCAqKnRoYXQgY29tcGVuc2F0aW9uIGlzIGluZGVwZW5kZW50IG9mIHRoZSBhZ2UgY2F0ZWdvcnkqKi4gVGhpcmRseSwgeW91ICoqZ2VuZXJhdGUgcGVybXV0YXRpb24gcmVwbGljYXRlcyBvZiB0aGUgZGF0YSoqLiBGaW5hbGx5LCB5b3UgYyoqYWxjdWxhdGUgdGhlIG5vbi1zdGFuZGFyZGl6ZWQgdGVzdCBzdGF0aXN0aWMgZm9yIGVhY2ggcmVwbGljYXRlKiouIFRoZSBzdGF0IGFyZ3VtZW50IGlzIGAiZGlmZiBpbiBtZWFucyJgLiBUaGUgb3JkZXIgYXJndW1lbnQgaXMgdGhlIHNhbWUgYXMgaW4gdGhlIGNhbGwgdG8gdF90ZXN0Lg0KDQpgYGB7cn0NCiMgU2ltdWxhdGlvbi1iYXNlZCBwaXBlbGluZQ0KbnVsbF9kaXN0biA8LSBzdGFja19vdmVyZmxvdyAlPiUNCiAgc3BlY2lmeShjb252ZXJ0ZWRfY29tcCB+IGFnZV9maXJzdF9jb2RlX2N1dCkgJT4lDQogIGh5cG90aGVzaXplKG51bGwgPSAiaW5kZXBlbmRlbmNlIikgJT4lDQogIGdlbmVyYXRlKHJlcHMgPSA1MDAwLCB0eXBlID0gInBlcm11dGUiKSAlPiUNCiAgY2FsY3VsYXRlKHN0YXQgPSAiZGlmZiBpbiBtZWFucyIsIG9yZGVyID0gYygiY2hpbGQiLCAiYWR1bHQiKQ0KICApDQoNCm51bGxfZGlzdG4NCmBgYA0KDQpgYGB7cn0NCiMgdC10ZXN0LCBmb3IgY29tcGFyaXNvbiANCnN0YWNrX292ZXJmbG93ICU+JQ0KICB0X3Rlc3QoY29udmVydGVkX2NvbXAgfiBhZ2VfZmlyc3RfY29kZV9jdXQsIG9yZGVyID0gYygiY2hpbGQiLCAiYWR1bHQiKSwgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpDQpgYGANCg0KIyMjICoqQ2FsY3VsYXRpbmcgdGhlIG9ic2VydmVkIHN0YXRpc3RpYyoqDQoNClRvIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBpbiBtZWFucyBvYnNlcnZlZCBpbiB0aGUgU3RhY2sgT3ZlcmZsb3cgc3VydmV5LCB3ZSByZXVzZSB0aGUgc3BlY2lmeSBhbmQgY2FsY3VsYXRlIHN0ZXBzIGZyb20gdGhlIHByZXZpb3VzIHBpcGVsaW5lLg0KDQpgYGB7cn0NCiMgU2ltdWxhdGlvbi1iYXNlZCBwaXBlbGluZQ0Kb2JzX3N0YXQgPC0gc3RhY2tfb3ZlcmZsb3cgJT4lDQogIHNwZWNpZnkoY29udmVydGVkX2NvbXAgfiBhZ2VfZmlyc3RfY29kZV9jdXQpICU+JQ0KICBjYWxjdWxhdGUoc3RhdCA9ICJkaWZmIGluIG1lYW5zIiwgb3JkZXIgPSBjKCJjaGlsZCIsICJhZHVsdCIpKQ0KDQpvYnNfc3RhdA0KYGBgDQoNCmBgYHtyfQ0KIyB0LXRlc3QgZm9yIGNvbXBhcmlzb24NCnN0YWNrX292ZXJmbG93ICU+JQ0KICB0X3Rlc3QoY29udmVydGVkX2NvbXAgfmFnZV9maXJzdF9jb2RlX2N1dCwgb3JkZXIgPSBjKCJjaGlsZCIsICJhZHVsdCIpLCBhbHRlcm5hdGl2ZSA9ICJncmVhdGVyIikNCmBgYA0KDQojIyMgKipHZXQgdGhlIHAtdmFsdWUqKg0KDQpGaW5hbGx5LCB3ZSBnZXQgdGhlIHAtdmFsdWUgZnJvbSB0aGUgbnVsbCBkaXN0cmlidXRpb24gYW5kIG9ic2VydmVkIHN0YXRpc3RpYy4gVGhlIGRpcmVjdGlvbiBhcmd1bWVudCB0byBnZXRfcF92YWx1ZSBpcyB0aGUgc2FtZSBhcyB0aGUgYWx0ZXJuYXRpdmUgYXJndW1lbnQgaW4gdF90ZXN0LiBUaGUgcC12YWx1ZSBpcyAwLjAzNjIgcmF0aGVyIHRoYW4gMC4wMzA4DQoNCmBgYHtyfQ0KIyBTaW11bGF0aW9uLWJhc2VkIHBpcGVsaW5lDQpnZXRfcF92YWx1ZShudWxsX2Rpc3RuLCBvYnNfc3RhdCwgZGlyZWN0aW9uID0gImdyZWF0ZXIiKQ0KYGBgDQoNCmBgYHtyfQ0KIyB0LXRlc3QgZm9yIGNvbXBhcmlzb24NCnN0YWNrX292ZXJmbG93ICU+JQ0KICB0X3Rlc3QoY29udmVydGVkX2NvbXAgfiBhZ2VfZmlyc3RfY29kZV9jdXQsIG9yZGVyID0gYygiY2hpbGQiLCAiYWR1bHQiKSwgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpDQpgYGANCg0KIyMjICoqUmFua3Mgb2YgdmVjdG9ycyoqDQoNCkNvbnNpZGVyIHRoaXMgbnVtZXJpYyB2ZWN0b3IsIHguIFRoZSBmaXJzdCB2YWx1ZSBvZiB4LCBvbmUsIGlzIHRoZSBzbWFsbGVzdC4gVGhlIHNlY29uZCB2YWx1ZSwgZmlmdGVlbiwgaXMgdGhlIGZpZnRoIHNtYWxsZXN0LiAqKlRoZXNlIG9yZGVyaW5ncyBmcm9tIHNtYWxsZXN0IHRvIGxhcmdlc3QgYXJlIGtub3duIGFzIHRoZSByYW5rcyBvZiB0aGUgZWxlbWVudHMgb2YgeCoqLiBZb3UgY2FuIGFjY2VzcyB0aGVtIHdpdGggdGhlIHJhbmsgZnVuY3Rpb24uIFlvdSBjYW4gYXZvaWQgYXNzdW1wdGlvbnMgYWJvdXQgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgZGF0YSBieSBwZXJmb3JtaW5nIGh5cG90aGVzaXMgdGVzdHMgb24gdGhlIHJhbmtzIG9mIGEgbnVtZXJpYyBpbnB1dC4gKioqVGhlIFdpbGNveG9uLU1hbm4tV2hpdG5leSoqKiB0ZXN0IGlzLCB2ZXJ5IHJvdWdobHkgc3BlYWtpbmcsIGEgdC10ZXN0IG9uIHJhbmtlZCBkYXRhLg0KDQpgYGB7cn0NCnggPSBjKDEsIDE1LCAzLCAxMCwgNikNCnJhbmsoeCkNCmBgYA0KDQojIyMgKipXaWxjb3hvbi1NYW5uLVdoaXRuZXkgdGVzdCoqDQoNCllvdSBjYW4gcnVuIGEgKioqV2lsY294b24tTWFubi1XaGl0bmV5KioqIHRlc3QgdXNpbmcgYHdpbGNveC50ZXN0KClgIGZyb20gYmFzZS1SLiAqKkl0IGFjY2VwdHMgYSBmb3JtdWxhIGFuZCBkYXRhIGFyZ3VtZW50KiosIHRob3VnaCB0aGVzZSBhcmUgc3dhcHBlZCBjb21wYXJlZCB0byB0aGUgaW5mZXIgZnVuY3Rpb25zLCBzbyB0aGV5IGFyZSBsZXNzIHBpcGUtZnJpZW5kbHkuIGFsdGVybmF0aXZlIHNldHMgdGhlIHR5cGUgb2YgYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcywgYW5kIGNvcnJlY3QgZGV0ZXJtaW5lcyB3aGV0aGVyIG9yIG5vdCB0byBhcHBseSBhIGNvbnRpbnVpdHkgY29ycmVjdGlvbiB0byB0aGUgei1zY29yZXMuIFNpbmNlIHJhbmtzIGFyZSBpbnRlZ2VycyBhbmQgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gaXMgY29udGludW91cywgdGhpcyBjb3JyZWN0aW9uIGNhbiBpbXByb3ZlIHRoZSBhcHByb3hpbWF0aW9uLiBIb3dldmVyLCBmb3IgbGFyZ2Ugc2FtcGxlIHNpemVzIGl0cyBlZmZlY3QgaXMgdHJpdmlhbCBhbmQgbm90IG5lZWRlZC4gSGVyZSwgdGhlIHAtdmFsdWUgaXMgc2hvd24gYXMgbGVzcyB0aGFuIHR3byB0aW1lcyB0ZW4gdG8gdGhlIG1pbnVzIHNpeHRlZW4sIGxlc3MgdGhhbiB0aGUgc2lnbmlmaWNhbmNlIGxldmVsLg0KDQpeMV4gQWxzbyBrbm93biBhcyB0aGUgIldpbGNveG9uIHJhbmstc3VtIHRlc3QiIGFuZCB0aGUgIk1hbm4tV2hpdG5leSBVIHRlc3QiLg0KDQpgYGB7cn0NCndpbGNveC50ZXN0KGNvbnZlcnRlZF9jb21wIH4gYWdlX2ZpcnN0X2NvZGVfY3V0LCBkYXRhID0gc3RhY2tfb3ZlcmZsb3csIGFsdGVybmF0aXZlID0gImdyZWF0ZXIiLCBjb3JyZWN0ID0gRkFMU0UpDQpgYGANCg0KIyMjICoqS3J1c2thbC1XYWxsaXMgdGVzdCoqDQoNCkluIHRoZSBzYW1lIHdheSB0aGF0IEFOT1ZBIGV4dGVuZHMgdC10ZXN0cyB0byBtb3JlIHRoYW4gdHdvIGdyb3VwcywgdGhlICoqKktydXNrYWwtV2FsbGFjZSoqKiB0ZXN0IGV4dGVuZHMgdGhlICoqKldpbGNveG9uLU1hbm4tV2hpdG5leSoqKiB0byBtb3JlIHRoYW4gdHdvIGdyb3Vwcy4gVGhhdCBpcywgdGhlICoqKktydXNrYWwtV2FsbGFjZSoqKiB0ZXN0IGlzIGEgbm9uLXBhcmFtZXRyaWMgdmVyc2lvbiBvZiBBTk9WQS4gWW91IHBhc3MgYSBmb3JtdWxhIHdpdGggdGhlIG51bWVyaWNhbCB2YXJpYWJsZSBvbiB0aGUgbGVmdCBhbmQgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlIG9uIHRoZSByaWdodCwgYW5kIHNldCBkYXRhIHRvIHRoZSBzYW1wbGUgZGF0YSBmcmFtZS4gQWdhaW4sIHRoZSBwLXZhbHVlIGhlcmUgaXMgdmVyeSBzbWFsbC4NCg0KYGBge3J9DQprcnVza2FsLnRlc3QoY29udmVydGVkX2NvbXAgfiBqb2Jfc2F0LCBkYXRhID0gc3RhY2tfb3ZlcmZsb3cpDQpgYGANCg==