Binomial Test

To test the hypothesis :

\[H_{0} : \text{Probability of success } (p) = p*\]

Against its corresponding one-sided (\(H_{1} : p > p*\) or \(H_{1} : p < p*\)) or two-sided alternative (\(H_{1} : p \neq p*\))

We make use of the function binom.test provided by the mosaic package.

The syntax for binom.test is as follows :

binom.test(
  x,            # Data to be tested
  n = NULL,     # Sample size
  p,            # Hypotesized probability of success (default value is 0.5)
  alternative = c("two.sided","less","greater),   # Alt hypothesis (default is two.sided)
  conf.level = 0.95,  # Confidence level of significance to test (default value is 0.95)
  
  # Method for computing confidence interval
  ci.method = c("Clopper-Pearson", "binom.test", "Score", "Wilson,
                "prop.test", "Wald", "Agresti-Coull", "Plus4")
                
  data,         # Dataframe from which the sample "x" is accessed.
  success       # Level of variable to be considered success
)

We note several things regarding the input arguments for the binom.test function.

Example

  1. Suppose a food truck business is experimenting on a new item to sell. An experiment is then conducted to see how well the new product might sell in contrast to the old product. The experiment is conducted to 30 different people, each participant tastes the 2 products in random order and gives their opinion of which one is better. It turns out that 18 participant prefers the new product. To test if this is significant enough to conclude that the new product is better, we perform a binomial test as follows
# There are 18 successes from 30 experiments, and if we assume that the 2 products would be equally likely to be sold, the probability of success is 1/2, thus the test is 
mosaic :: binom.test(18,
                     n=30,
                     p=0.5,
                     alternative = "g")



data:  18 out of 30
number of successes = 18, number of trials = 30,
p-value = 0.1808
alternative hypothesis: true probability of success is greater than 0.5
95 percent confidence interval:
 0.4339453 1.0000000
sample estimates:
probability of success 
                   0.6 

Since the p-value is \(0.1808 > 0.05 = \alpha\), then at \(\alpha\) level of significance, we cannot conclude from the experiment, that the new food item will sell better than the old one.

  1. [Quantile testing].

    Suppose we have the data of gasoline consumption (in Km/L) of a taxi company with two different types of taxis, one with radial tires, and another with belted tires. We want to test if :

    1. the median gas consumption of the taxi with radial tires does not exceed 4.5 Km/L.
    2. the Q1 of gas consumption of the taxi with belted tires is \(\geq\) 5.5 Km/L
# Read the data
taxi_data
# Median gas consumption of taxi w/ radial tires

rad_med = 4.5 # Hypothesized median

# Under the null hypothesis, the probability of the taxi's gas consumption values > 4.5 and < 4.5 are equal (w/ probability 0.5).
rad_test = taxi_data$radial_tires <= 4.5

# The hypothesis is equivalent to testing
# H0 : P(X<=4.5) >= 0.5
# H1 : P(X<=4.5) < 0.5
# Test the hypothesis
mosaic :: binom.test(rad_test,
                     p = 0.5,
                     alternative = "l",
                     conf.level = 0.95)



data:  rad_test  [with success = TRUE]
number of successes = 2, number of trials = 16, p-value
= 0.00209
alternative hypothesis: true probability of success is less than 0.5
95 percent confidence interval:
 0.0000000 0.3438252
sample estimates:
probability of success 
                 0.125 

Since the p-value is \(0.00209 < 0.05\), thus at the significance level of \(0.05\), we can conclude that the median gas consumption of taxis with radial tires are greater than 4.5 Km/L.

# Q1 of gas consumption of taxis w/ belted tires
belt_q1 = 5.5 # Hypothesized Q1

# if Y is the gas consumption of taxis with belted tires, the hypothesis is equivalent to
# H0 : P(Y <= 5.5) <= 0.25
# H1 : P(Y <= 5.5) > 0.25

# We assign the observation w/ values <= 4.5 as TRUE, and else, as FALSE
belt_test = taxi_data$belted_tires <= belt_q1

# Hypothesis test
mosaic :: binom.test(belt_test,
                     p = 0.25,
                     alternative = "g",
                     conf.level = 0.95)



data:  belt_test  [with success = TRUE]
number of successes = 7, number of trials = 16, p-value
= 0.07956
alternative hypothesis: true probability of success is greater than 0.25
95 percent confidence interval:
 0.2266916 1.0000000
sample estimates:
probability of success 
                0.4375 

Since the p-value is \(0.07956 > 0.05\), we conclude that at the significance level of \(0.05\), the null hypothesis that the Q1 of gas consumption of taxis w/ belted tires is \(\geq\) 5.5 Km/L is not rejected.

Rank Tests

Sign Test

To perform a sign test, we use the function SignTest from the DescTools package.

The syntax for SignTest is as follows

SignTest(x,
         y = NULL,
         mu = 0,
         alternative = c("two.sided","less","greater"),
         conf.level = 0.95)

The input arguments are :

  • x : vector of data to be tested
  • y : vector of data to be tested (for 2 sample sign test). The default value is NULL
  • mu : hypothesized median. The default value is 0.
  • alternative : alternative hypothesis
  • conf.level : confidence level for the confidence interval (also the significance level of the hypothesis test).

The function will return a list of class htest which contains the summary of the hypothesis test (the statistic value, p-value, etc)

Example

  1. From the taxi data, we will use the sign test to test (at significance level \(0.05\)) the hypotheses :

    1. the median gas consumption of taxis with radial tires is equal to 4.5 Km/L against the hypothesis that it is greater than 4.5 Km/L

    2. the median gas consumption of taxis with belted tires is equal to 5 Km/L against the hypothesis that it is not equal to 5 Km/L

    3. if the tire type of the taxi affects the median gas consumption of the taxis. (Test for the median of the difference of gas consumption)

# 1a. Median gas consumption of taxis w/ radial tires
md_1a = 4.5  # Hypothesized median

# Test the hypothesis
SignTest(taxi_data$radial_tires,
         mu = md_1a,
         alternative = "g",  # Alternative hypothesis is > md_1a
         conf.level = 0.95)

    One-sample Sign-Test

data:  taxi_data$radial_tires
S = 14, number of differences = 15, p-value = 0.0004883
alternative hypothesis: true median is greater than 4.5
96.2 percent confidence interval:
 4.9 Inf
sample estimates:
median of the differences 
                     5.85 

From the output, we can gather the following information :

* S : the statistic which denotes the # of positive differences between the data and the hypothesized median (how many data points are > hypothesized median)

* number of differences : How many data points which are different from the hypothesized median. (data points which are equal to the hypothesized median are removed)

* sample estimate of the median of the difference : the sample median which is the estimate for the distribution's median.

Since the p-value for the statistic is \(0.0004883 < 0.05\), we conclude at the significance level of \(0.05\) that the median gas consumption of taxis with radial tires is greater than 4.5 Km/L.

* Note that the p-value is the same as performing a binomial test for quantile testing the median, where the data point which is equal to the hypothesized median, is removed.

# 1b. Median of taxis with belted tires.
md_1b = 5 # Hypothesized median

# Test the hypothesis
SignTest(taxi_data$belted_tires,
         mu = md_1b,
         alternative = "t",  # Alternative hypothesis is != 5
         conf.level = 0.95
         )

    One-sample Sign-Test

data:  taxi_data$belted_tires
S = 10, number of differences = 16, p-value = 0.4545
alternative hypothesis: true median is not equal to 5
97.9 percent confidence interval:
 4.9 6.8
sample estimates:
median of the differences 
                     5.75 

Since the p-value for the statistic is \(0.4545 > 0.05\), we conclude that the null hypothesis cannot be rejected at \(0.05\) level of significance.

# 1c. median of the difference
md_1c = 0 # Hypothesized median of the difference

# Test the hypothesis
SignTest(x = taxi_data$radial_tires,
         y = taxi_data$belted_tires,
         mu = md_1c,
         alternative = "t",
         conf.level = 0.95
         )

    Dependent-samples Sign-Test

data:  taxi_data$radial_tires and taxi_data$belted_tires
S = 11, number of differences = 14, p-value = 0.05737
alternative hypothesis: true median difference is not equal to 0
97.9 percent confidence interval:
 0.0 0.4
sample estimates:
median of the differences 
                      0.1 

Since the p-value is \(0.05737 > 0.05\), we conclude that at \(0.05\) level of significance, the null hypothesis that there exist no significant difference in the median gas consumption of the 2 types of taxis cannot be rejected.

Another way to do the sign test on differences of 2 samples (2-sample sign test) is to first calculate the differences between the two samples. An example of this is

# 1c

# Calculates the difference of the 2 sample first
d = taxi_data$radial_tires - taxi_data$belted_tires

# Test the hypothesis
SignTest(d,
         mu = md_1c,
         alternative = "t",
         conf.level = 0.95)

    One-sample Sign-Test

data:  d
S = 11, number of differences = 14, p-value = 0.05737
alternative hypothesis: true median is not equal to 0
97.9 percent confidence interval:
 0.0 0.4
sample estimates:
median of the differences 
                      0.1 

Note that this gives the same result as before.

Spearman’s Coefficient of Rank Correlation

To calculate the Spearman’s rank correlation coefficient of 2 variables from a given sample, we use the function cor from the stats package. The package is usually already installed along with R.

The syntax for the cor function is as follows :

cor(x,y,
    method = c("pearson","kendall","spearman"),
    use = "everything")

The input arguments are :

  • x, y : Vectors of data (numeric) (does not have to be ranked data). This can also be a matrix of data, if this is the case, then the function will return a correlation coefficient matrix.
  • method : The method that will be used to calculate the correlation coefficient
  • use : Specify which data are going to be used, the options are :
    • “everything” - If either the x component or the y component is NA, then the correlation will be NA (this is the default).
    • “all.obs” - Use all the data, if there are NAs, the function will raise an error.
    • “complete.obs” - If either of the variable has an NA, that observation is dropped from the calculation.
    • “na.or.complete” - Does the same thing as complete.obs, except if there are no complete observations, then the function will return NA.
    • “pairwise.complete.obs” - Does similar treatment to NAs as complete.obs, but observation with NAs are only omitted from calculations of each pairwise correlation. * For more information, one can read from the documentation of the function.

We can also test the hypothesis : \[ H_{0} : \text{The Spearman's Rank Correlation Coefficient } (\rho) = 0 \] against its one-sided or two-sided alternatives by using the function cor.test also from the stats package.

The syntax is similar to the cor function :

cor.test(x,y,
         method = c("pearson","kendall","spearman"),
         alternative = c("two.sided","less","greater"),
         conf.level = 0.95)

where alternative is the alternative hypothesis, and conf.level is the confidence level for the confidence interval.

Example

  1. Consider the following data regarding the rating given by participants for a particular product by 9 different manufacturers, and the price of the product from each manufacturer.
rating_price_data

We can plot the data to see visually if there might be indication of any correlation between the rating and the price

library(ggplot2)
ggplot(rating_price_data) + 
  geom_point(mapping = aes(x=Rating,y=Price),color = '#2980B9')

It seems as if the Rating and Price are not very much correlated. We now calculate the Spearman rank correlation coefficient for Rating and Price

# Spearman rank correlation coefficient
cor(rating_price_data$Rating,
    rating_price_data$Price,
    method="s")
[1] -0.4666667

There appears to be a slight negative correlation, we can test the significance of this result using cor.test as follows

# Hypothesis testing for significance of correlation
cor.test(rating_price_data$Rating,
         rating_price_data$Price,
         method="s",
         alternative = "t",
         )

    Spearman's rank correlation rho

data:  x and y
S = 176, p-value = 0.2125
alternative hypothesis: true rho is not equal to 0
sample estimates:
       rho 
-0.4666667 

We can see that the p-value for the statistic is \(0.2125 > 0.05\). Thus we conclude that at \(0.05\) level of significance, the hypothesis that rating and price are uncorrelated cannot be refuted.

  1. From the cars dataset, we will compute the Spearman rank correlation coefficient between a car’s speed and its distance.
cars
# Spearman rank correlation coefficient
cor(cars$speed,cars$dist,method="s")
[1] 0.8303568
# Hypothesis testing
cor.test(cars$speed,
         cars$dist,
         method = "s", 
         alternative = "t",
         )
Cannot compute exact p-value with ties

    Spearman's rank correlation rho

data:  x and y
S = 3532.8, p-value = 8.825e-14
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.8303568 

Note that calculating the Spearman rank correlation coefficient using the cor might be faulty since it does not handle with ties in the data very well.

Wilcoxon Signed Rank Test

To perform the Wilcoxon Signed Rank Test for testing the median for a single sample, or the median of difference in two-paired samples, we use the function wilcox.test from the stats package.

The syntax is as follows :

wilcox.test(x,
            y = NULL,
            alternative = c("two.sided","less","greater"),
            mu = 0,
            paired = FALSE,
            exact = NULL,
            conf.int = FALSE,
            conf.level = 0.95)

The input arguments are :

  • x : The sample to be tested
  • y : Another sample to be tested, if performing a paired wilcoxon test
  • mu : hypothesized median of the sample (the default value is 0)
  • paired : whether or not the test is a paired wilcoxon test or not (the default is FALSE), if we want to conduct a paired wilcoxon signed test, this needs to be changed to TRUE.
  • exact : whether or not the exact p-value for the statistic is calculated. (The default value is NULL, that is, if the size of the sample is < 50, and no ties are present, then the exact p-value is computed, else, a normal approximation will be used)
  • conf.int : whether or not a confidence interval will be shown in the output
  • conf.level : the confidence level for which the conf. interval is computed, if computed.

Examples

  1. [Single sample median testing]

    We will test using the taxi data, the same hypothesis as in the sign test, but using Wilcoxon Signed Rank Test.

    1. the median gas consumption of taxis with radial tires is equal to 4.5 Km/L against the hypothesis that it is greater than 4.5 Km/L

    2. the median gas consumption of taxis with belted tires is equal to 5 Km/L against the hypothesis that it is not equal to 5 Km/L

# 1a. Median of taxis with radial tires
w1a = wilcox.test(taxi_data$radial_tires,
            mu = 4.5,
            alternative = "g",
            exact = T,  # Calculate the p-value using the exact distribution if possible
            )
cannot compute exact p-value with tiescannot compute exact p-value with zeroes
w1a

    Wilcoxon signed rank test with continuity correction

data:  taxi_data$radial_tires
V = 118, p-value = 0.0005433
alternative hypothesis: true location is greater than 4.5
The test gives the Wilcoxon statistic, given as ```V``` in the output, and the corresponding p-value for the value of the statistic. Note that the function approximates the computation of the p-value since there are ties in the data. Also note that the Wilcoxon statistic that is computed (```V```) is the sum of ranks of observation w/ positive differences with respect to the hypothesized median. Thus, if we might want to consult with a Wilcoxon statistic table, we might have to calculate the Wilcoxon statistic corresponding to negative differences, which we can compute manually as follows
x = taxi_data$radial_tires
n = length(x[x != 4.5])  # Sample size of data omitting the observation that is equal to the hypothesized median
w_neg = n*(n+1)/2 - w1a$statistic   # The sum of ranks of observation w/ negative differences
w_neg
V 
2 
# 1b. Median of the taxi with belted tires
w1b = wilcox.test(taxi_data$belted_tires,
                  mu = 5,
                  alternative = "t",
                  exact = T)
cannot compute exact p-value with ties
w1b

    Wilcoxon signed rank test with continuity correction

data:  taxi_data$belted_tires
V = 111, p-value = 0.02785
alternative hypothesis: true location is not equal to 5

We see that the p-value for this test statistic is (approximately) \(0.02785 < 0.05\), thus at \(0.05\) level of significance, we can conclude that the median gas consumption of taxis with belted tires is not equal to 5 Km/L. We can also crosscheck this result by comparing the test statistic with the critical value from a Wilcoxon signed-rank statistic table.

# Calculating the negative difference Wilcoxon statistic
y = taxi_data$belted_tires
ny = length(y[y!=5])  # Sample size of data without the observation equalling the hypothesized median
wy_neg = ny*(ny+1)/2 - w1b$statistic # The negative difference Wilcoxon statistic
wy_neg
 V 
25 
  1. [Paired-sample tests]

    Consider the following data regarding joggers’ systolic blood pressure before and after an 8-kilometer run. We will test the hypothesis that the median systolic blood pressure increases by 8 points against the alternative that the increase is less than 8 points

jogger_data
# Hypothesis test
wilcox.test(jogger_data$After,
            jogger_data$Before,
            mu = 8, # Hypothesized median
            paired = T, # Because we are dealing with paired data
            alternative = "l"
            )
cannot compute exact p-value with tiescannot compute exact p-value with zeroes

    Wilcoxon signed rank test with continuity correction

data:  jogger_data$After and jogger_data$Before
V = 15, p-value = 0.01765
alternative hypothesis: true location shift is less than 8

Since the p-value is \(0.01765 < 0.05\), we conclude that at \(0.05\) level of significance, that the increase in median systolic blood pressure is less than 8 points.

Wilcoxon Rank-Sum Test / Mann-Whitney U Test

To perform the Wilcoxon rank sum test on two sample (unpaired) data, we can use the wilcox.test function as before, or use other functions such as wilcox_test from the coin package. ***The only difference is the value of the statistic that is presented, using wilcox.test will produce the U statistic, and using wilcox_test, we can get the W statistic.

library(coin) # Load the coin package (install it first)
Loading required package: survival

For using the wilcox.test function, the syntax is :

wilcox.test(x,
            y,
            mu = 0,
            paired = FALSE,
            alternative = c("two.sided","less","greater"),
            exact = NULL,
            correct = TRUE,
            ...)

The input arguments are the same as before, although to conduct a Wilcoxon Rank-Sum test, make sure that paired is set to FALSE (which is the default).

This will test the null hypothesis of \[ H_{0} : \widetilde{\mu}_x - \widetilde{\mu}_y = 0 \] against the corresponding alternative.

For the wilcox_test function, the syntax is :

wilcox_test(formula,
            data,
            subset = NULL,
            ties.method = c("mid-ranks","average-scores"),
            distribution = c("asymptotic","approximate","exact"),
            ...)

The input arguments are :

  • formula : a formula of the form x ~ g, where x is the numeric vector, and g is a factor denoting the groups from which we want to test
  • data : (optional) data frame from which the data (x and g) are taken
  • subset : (optional) vector specifying the subset of observations to be used (the default is NULL)
  • ties_method : how to rank ties in the data (the default is mid-ranks).
  • other inputs (weights, alternative, conf.int, etc), more on this on the documentation for the function.

These input arguments can also be used for the wilcox.test function.

Note that for using this function, the data needs to be in a certain shape, that is there is a column of numeric data, and a column of groups, specifying from which group/level the particular data point is from.

So, in order to do tests with our data, we need to reshape the data as follows.

# Suppose the original data is 
car_rating_data[,1:2]

To change the shape of the data, we do the following procedure

library(dplyr) # The library needed to reshape the data

D1 = na.omit(stack(car_rating_data[,1:2])) # Stacks the numerical value from the original dataframe and omits any NA data point

colnames(D1) = c("values","groups")   # Gives names for the columns of the new dataframe

# The result is
D1

Now we can apply the wilcox.test and wilcox_test function on the data.

Example

  1. Using the data mentioned above, we will do a hypothesis test to test if there is a difference between the two groups.
# Using wilcox.test
wilcox.test(values~groups, # The formula
            data = D1, # The dataframe to reference the data
            alternative = "t"
            )
cannot compute exact p-value with ties

    Wilcoxon rank sum test with continuity correction

data:  values by groups
W = 1.5, p-value = 0.008367
alternative hypothesis: true location shift is not equal to 0
# The null hypothesis is difference of median of group A - median of group B is 0

From the output, we can gather that the Mann-Whitney U statistic corresponding to the base level group (A) is W = 1.5 and the (approximate) p-value for this statistic value is \(0.008367 < 0.05\). So, we conclude that at \(0.05\) level of significance, there is a significant difference across the two groups.

# Using wilcox_test
wrs = wilcox_test(values~groups, # The formula
            data = D1, # The dataframe
            alternative = "t",
            distribution = "e", # Calculate the p-value using the exact distribution
            ties.method = "a" # Using average scores for ties
            )
wrs

    Exact Wilcoxon-Mann-Whitney Test

data:  values by groups (A, B)
Z = -2.7193, p-value = 0.008658
alternative hypothesis: true mu is not equal to 0

We can see that the p-value is different than the one that is produced by the wilcox.test function, this is because the calculation for the p-values might differ (for more information, refer to the documentation). Although, at \(0.05\) level of significance, the null hypothesis is still rejected. Note also that the statistic that is presented on the output of the wilcox_test is the “normalized” statistic, we can get the Wilcoxon rank sum statistic from this test by calling it specifically, for example

# The wilcoxon rank-sum statistic
w_statistic = statistic(wrs,"linear") # The wilcoxon rank sum statistic
w_statistic
      
A 22.5

Note that the statistic given is the statistic corresponding to the base level of the groups (that is A), to get the statistic corresponding to the other group, we can relevel the group/factor using the function relevel from the stats package

# Relevel the factors
D1$groups = relevel(D1$groups, ref = "B") # Changes the base level of the groups to "B"
# wilcox.test
wilcox.test(values~groups,data = D1, alternative = "t")
cannot compute exact p-value with ties

    Wilcoxon rank sum test with continuity correction

data:  values by groups
W = 34.5, p-value = 0.008367
alternative hypothesis: true location shift is not equal to 0
# wilcox_test
wrs_b = wilcox_test(values~groups,data = D1, distribution = "e", ties.method = "a")
w_statistic_b = statistic(wrs_b,"linear")
w_statistic_b
      
B 55.5
  1. Consider the following data regarding the nicotine content in two brands of cigarettes (brand A and brand B).
cigarette_data

We will test whether there is a difference in median nicotine content between the 2 brands of cigarettes.

# Reshaping the data
cig_data = na.omit(stack(cigarette_data)) 
colnames(cig_data) = c("nicotine_content","brand") # Renaming the columns
cig_data

# Hypothesis test
wilcox_test(nicotine_content~brand,
            data = cig_data,
            alternative = "t", # Alternative hypothesis
            distribution = "e", # Use the exact distribution
            ties_method = "a" # Use the average ranking for ties
            )

    Exact Wilcoxon-Mann-Whitney Test

data:  nicotine_content by brand (A, B)
Z = 1.5121, p-value = 0.1391
alternative hypothesis: true mu is not equal to 0

Since the p-value is \(0.1391 > 0.05\), we conclude that the null hypothesis that there is no significant difference in median nicotine content between the 2 brands of cigarettes is not rejected at \(0.05\) level of significance.

Kruskall-Wallis Test

To perform the Kruskall-Wallis Test, we make use of the function kruskal.test from the stats package.

The syntax is as follows :

kruskal.test(x,g)

or

kruskal.test(formula,
             data)

Where the input arguments are :

or

Example

  1. Consider the following data regarding the ratings given by 6 experts on 3 different cars (A, B, C)
car_rating_data

We will test if there are any differences in the median rating given by the experts for the 3 brands of cars.

# Reshape the data
car_data = na.omit(stack(car_rating_data))
colnames(car_data) = c("rating","brand") # Renaming the columns of the reshaped data
car_data
# Hypothesis testing
kruskal.test(rating~brand,
             data = car_data)

    Kruskal-Wallis rank sum test

data:  rating by brand
Kruskal-Wallis chi-squared = 9.3326, df = 2, p-value =
0.009407

We can see that the (approximate) chi-squared statistic is 9.3326 and the corresponding p-value is \(0.009407 < 0.05\). So we conclude that at \(0.05\) level of significance, the null hypothesis that there are no differences in the median rating given by the experts is rejected.

Before doing the Kruskal-Wallis test, it might be of interest to plot the data to see if visually, there are differences between the groups, we can do this as follows

# Boxplot of ratings for each groups
library(ggpubr)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
ggboxplot(data = car_data,
          x = "brand",
          y = "rating")

Runs Test

To perform the runs test, we use the function RunsTest from the DescTools package.

library(DescTools) # Loading the randtests library (Install it first)

The syntax is as follows :

RunsTest(x,
         alternative = c("t","l","g"),
         exact = NULL,
         correct = TRUE,
         na.rm = FALSE)

The input arguments are :

Note that if x is a numeric vector with more than 2 values, by default, it will be transformed to be a dichotomous vector (logical vector of TRUE and FALSE) by subtracting the median of the sample. If another threshold is to be chosen, say the mean of the sample we can use RunsTest(x > mean(x))

Example

  1. Suppose we want to test if the sequence : \[ A \ B \ A \ A \ B \ B \ B \ B \ A \ B \ B \ A \ A \ A \ B \ A \ B \]

is random

# The data vector
x1 = c("A","B","A","A","B","B","B","B","A","B","B","A","A","A","B","A","B")

# Runs Test
RunsTest(x1)

    Runs Test for Randomness

data:  x1
runs = 10, m = 8, n = 9, p-value = 0.8186
alternative hypothesis: true number of runs is not equal the expected number

Since the p-value is \(0.8186 > 0.05\), we conclude that at \(0.05\) level of significance, the null hypothesis that the sequence is random is not rejected.

  1. Suppose that a paint making machine dispenses an amount of paint thinner for each batch of paint, we want to test if the amount of paint thinner dispensed by the machine is random.
# Data vector (amount of paint thinner)
x2 = c(3.6,3.9,4.1,3.6,3.8,3.7,3.4,4.0,3.8,4.1,3.9,4.0,3.8,4.2,4.1)

# Runs Test (with the median as the threshold)
x2_test = x2[x2 != median(x2)] # omit any data point w/ value equal to the sample median

RunsTest(x2_test > median(x2),exact = T)

    Runs Test for Randomness

data:  x2_test > median(x2)
runs = 8, m = 7, n = 6, p-value = 0.796
alternative hypothesis: true number of runs is not equal the expected number

Since the computed p-value is \(0.796 > 0.05\), we conclude that at \(0.05\) level of significance, the null hypothesis that the process is random is not rejected.

Tolerance Limit

Given a sample of continuous data, we can calculate the tolerance interval/tolerance limit of the data using the function nptol.int from the tolerance package.

library(tolerance) # Load the package (install it first)

The syntax for the nptol.int function is as follows :

nptol.int(x,
          alpha = 0.05,
          P = 0.99,
          side = 1,
          method = c("WILKS","WALD","HM","YM"),
          upper = NULL,
          lower = NULL)

The input arguments are * x : numeric vector of the data * alpha : the significance level of the tolerance limit (default is 0.05) * P : the proportion of the data (default is 0.99) * side : whether it will calculate the 2-sided or 1 sided tolerance limit * method : the method that will be used to calculate the tolerance limit * upper : the upper bound of the data (if NULL, then max(x) is used) * lower : the lower bound of the data (if NULL, then min(x) is used)

Example

  1. Suppose we want to determine the 90% upper confidence bound for the 75th percentile of the data x2 used above.
nptol.int(x2,
          alpha = 1-0.90, # The significance level is 1-90%
          P = 0.75, # The proportion of the data is the 75%
          side = 1)

We see that the upper tolerance limit is 4.1

Goodness of Fit Test

Kolmogorov-Smirnov Test

To perform the Kolmogorov-Smirnov Test of equal distribution, we can use the function ks.test from the stats package.

The syntax for ks.test is as follows

ks.test(x,
        y, ...,
        alternative = c("two.sided","less","greater"),
        exact = NULL
        )

The input arguments are :

  • x : numeric vector of data to be tested
  • y : either a numeric vector of data (for comparing the distribution of x and y) or a character string naming a continuous (for example : “pnorm”, “pexp”, etc)
  • … : the parameters of the distribution of y, if y is a character string describing a particular continuous cdf
  • alternative : the alternative hypothesis (the default is “two.sided”). If “less”, then the alternative is that the cdf of x is less than the cdf of y. If “greater”, then the alternative is that the cdf of x is greater than the cdf of y
  • exact : whether an exact of an approximate p-value for the test is computed (by default, an exact p-value would be calculated if the conditions are satisfied, more on this conditions can be read on the documentation)

An important thing to note here is that is we want to compare the cdf of x to a known continuous cdf y, then the parameters of the cdf of y need to be known before hand. In the case that the parameters of the null distribution (cdf of y) is unknown and needed to be estimated using the sample statistics, we can use the function LcKS from the KSCorrect package.

Example

  1. Suppose we have a vector of data x and we want to test if the sample comes from a normally distributed population

For this example, we will use the cars data, available in R

# The data
x = cars$dist

# Quantile plot of x
qqnorm(x)


# We will test the distribution of x, against the normal distribution
ks.test(x,
        "pnorm")
ties should not be present for the Kolmogorov-Smirnov test

    One-sample Kolmogorov-Smirnov test

data:  x
D = 0.97997, p-value < 2.2e-16
alternative hypothesis: two-sided

We see that if we only input "pnorm" as the compared distribution, ks.test gives us a p-value which is considerably small, and thus the null hypothesis that x is “normally” distributed is rejected (Even though graphically, from the quantile plot, we might not reject that hypothesis). Keep in mind that by only giving the input "pnorm", then the compared distribution is that of a standard normal distribution (\(\mathcal{N} (0,1)\)), thus obviously if we compare a sample from a \(\mathcal{N} (100,1)\) distribution to the standard normal, then ks.test would give a p-value significantly small to reject the hypothesis that the sample from \(\mathcal{N}(100,1)\) is not normally distributed with mean 0 and variance 1. To properly test if x is normally distributed, we need to know the parameters of the population (that is the mean and variance of the distribution), which in most cases, are not known, and thus we need to use an approximation, such as the sample mean and sample variance. For example :

# Testing if x is normally distributed, w/ parameters approximated wih mean(x) and variance of x
ks.test(x,
        "pnorm",mean=mean(x),sd = sd(x), # We use sd(x) since that is the parameter that the functions in R use to parameterize the normal distribution
        )
ties should not be present for the Kolmogorov-Smirnov test

    One-sample Kolmogorov-Smirnov test

data:  x
D = 0.12675, p-value = 0.3979
alternative hypothesis: two-sided

We see that now, ks.test gives the p-value \(0.3979 > 0.05\), and thus the null hypothesis that x is normally distributed with mean equal to the sample mean and variance equal to the sample variance, is not rejected at \(0.05\) level of significance.

  1. Suppose we have 2 samples from 2 distributions, and we would like to test if the 2 samples came from the same distribution. We can do this as follows
# The samples
x = rnorm(100,mean = 25, sd = 2) # A normally distributed sample w/ mean = 25 and standard dev = 2
y = rexp(200,rate = 2) # An exponentially distributed sample w/ rate = 2

# Test if x and y are from the same distribution
ks.test(x,y)

    Two-sample Kolmogorov-Smirnov test

data:  x and y
D = 1, p-value < 2.2e-16
alternative hypothesis: two-sided

We see that the p-value is significantly small such that we may reject the null hypothesis thatx and y came from the same distribution at a quite small significance level (even at \(0.00000001\) significance, the null hypothesis can still be rejected). Thus, we conclude that x and y do not come from the same distribution.

Lilliefors Test

Lilliefors test for normality provides a correction to Kolmogorov-Smirnov Test when the parameters of the population is not known.

To perform the Lilliefors test for normality, we use the function lillie.test from the nortest package

library(nortest) # Load the library (install it first)

The syntax is as follows :

lillie.test(x)

The input argument is :

  • x : A numeric vector of data to be tested

Note that the statistic produced by this function will be equal to that if we use ks.test(x,"pnorm",mean=mean(x),sd=sd(x), but the p-value would be different.

Example

  1. Using the cars data, we will test if the distance traveled by the cars is normally distributed
# The data
x = cars$dist

# Test
lillie.test(x)

    Lilliefors (Kolmogorov-Smirnov) normality test

data:  x
D = 0.12675, p-value = 0.04335

Note that the statistic value is the same D = 0.12675 but the p-value is different, but still, at \(0.05\) level of significance, same conclusion that the null hypothesis that x is normally distributed is not rejected.

For other distribution, the function LcKS from the KScorrect package provides the Lilliefors-corrected Kolmogorov Smirnov test for other distributions, such as exponential, uniform, mixture normal, and others. For more information refer to the documentation of the function.

Kendall’s Tau

To calculate the Kendall’s Tau statistic between 2 vectors of data, we use the function cor from the stats package.

Given 2 numeric data vectors x,y, to calculate the Kendall’s Tau between the 2 data, we set the argument method = "k" or method = "kendall" in the cor function.

Example

  1. We will calculate the Kendall’s Tau statistic for the rating and price from the rating_price_data data.
rating_price_data
# Kendall's Tau statistic
cor(rating_price_data$Rating,
    rating_price_data$Price,
    method = "k",
    )
[1] -0.3333333

We see that the Kendall’s Tau is -0.3333333. To test the significance of this, we use the cor.test function

# significance of kendall's tau correlation
cor.test(rating_price_data$Rating,
         rating_price_data$Price,
         method = "k")

    Kendall's rank correlation tau

data:  x and y
T = 12, p-value = 0.2595
alternative hypothesis: true tau is not equal to 0
sample estimates:
       tau 
-0.3333333 

Since the p-value is \(0.2595 > 0.05\), we conclude that at \(0.05\) levle of significance, the null hypothesis that the Kendall’s Tau between the rating and price is 0 (i.e there is no correlation between rating and price according to Kendall’s Tau Statistic), is not rejected .

Cramer Coefficient

Given a contingency table, we can calculate the Cramer coefficient (Cramer’s V) between the categories in the rows and the categories in the columns by using the cramer function from the sjstats package.

The syntax for the cramer function is as follows :

cramer(tab)

The input argument is :

If the data at hand is not already a contingency table, we can still calculate the Cramer’s V between 2 variables using the function crosstable_statistics, also from the sjstats package. THe syntax for this function is as follows :

crosstable_statistics(data,
                      x1,
                      x2,
                      statistics = c("auto","cramer","phi","spearman","kendall","pearson","fisher"),
                      ...)

The input arguments are :

Example

  1. We will consider an example of calculating Cramer’s V by using the efc (Euro Family Care) data. (A dataset regarding the information of caretakers of elderly people in europe)
data(efc)
head(efc) # First 6 rows of the data

We will focus on 2 variables, e16sex (Gender of the elder) and c161sex (Gender of the caretaker). We will see if there are any association/correlation between the gender of the caretaker and the gender of the elder.

We can first generate the cross tabulation (contingency table) between the 2 variables by using the CrossTable function from the descr package.

# Load the required package (Install it first)
library(descr)

# Making the crosstabulation
CrossTable(x = efc$e16sex,
           y = efc$c161sex)
   Cell Contents 
|-------------------------|
|                       N | 
| Chi-square contribution | 
|           N / Row Total | 
|           N / Col Total | 
|         N / Table Total | 
|-------------------------|

===================================
              efc$c161sex
efc$e16sex        1       2   Total
-----------------------------------
1                61     234     295
              1.273   0.400        
              0.207   0.793   0.328
              0.284   0.342        
              0.068   0.260        
-----------------------------------
2               154     451     605
              0.621   0.195        
              0.255   0.745   0.672
              0.716   0.658        
              0.171   0.501        
-----------------------------------
Total           215     685     900
              0.239   0.761        
===================================

We can see the marginal frequencies for each gender of the caretaker and the gender of the elder.

# Calculate the cramer's V statistic using crosstable_statistics
crosstable_statistics(data = efc, # Dataframe
                      x1 = e16sex,
                      x2 = c161sex,
                      statistics = "c", # Calculate the cramer's V statistic
                      )

# Measure of Association for Contingency Tables

  Chi-squared: 2.2327
   Cramer's V: 0.0526
      p-value: 0.1351

We see that the Cramer’s V is \(0.0526\), which is quite close to \(0\) and the corresponding p-value is \(0.1351 > 0.05\). So, we conclude that at \(0.05\) level of significance, there is no association/correlation between the gender of the caretaker and the gender of the elder.

Another way to do it is to make a table of contingency table first between the 2 variables, and then applying the cramer function to it.

# Creating the contingency table for the 2 variables
cont_table = table(efc$e16sex,efc$c161sex)
cont_table
   
      1   2
  1  61 234
  2 154 451

We can calculate the Cramer’s V using the cramer function

cramer(cont_table)
[1] 0.05258249

We get that the Cramer’s V for the 2 variables is \(0.05258249\).

Phi Coefficient

We can calculate the Phi coefficient between 2 binary categorical variables by using the same crosstable_statistics function from the sjstats package as we use in calculating Cramer coefficient. To calculate the Phi coefficient of 2 variables, we set the statistics argument in the crosstable_statistics to "phi".

An analog to the cramer function can also be used to calculate the phi coefficient given a 2x2 contingency table, that is the phi function from sjstats package.

Example

  1. Using the same dataset as the example for Cramer coefficient, we will calculate the Phi Coefficient and its significance
# Calculate the Phi Coefficient
crosstable_statistics(efc,
                      x1 = e16sex,
                      x2 = c161sex,
                      statistics = "phi")

# Measure of Association for Contingency Tables

  Chi-squared: 2.2327
          Phi: 0.0526
      p-value: 0.1351

We can see that the Phi coefficient is \(0.0526\) and the corresponding p-value is \(0.1351 > 0.05\). Thus we conclude that the null hypothesis that there is no association/correlation/dependence between the gender of the caretaker and the elder is not rejected at \(0.05\) level of significance.

We can also use the phi function, by first creating the contingency table

# Creating the contingency table
cont_table_2 = table(efc$e16sex,efc$c161sex)
cont_table_2
   
      1   2
  1  61 234
  2 154 451

Now we can calculate the Phi coefficient

phi(cont_table_2)
[1] 0.05258249

Cochran’s Test and McNemar’s Test

To perform the Cochran’s Test, we use the function cochran.qtest from the RVAideMemoire package

The syntax for the cochran.qtest is as follows :

cochran.qtest(formula,
              data,
              alpha = 0.05,
              )

The input arguments are :

Example

  1. Suppose the following data regarding 51 people’s preference of beverage for breakfast.
breakfast

We would like to test if there are any beverage which is more preferred than the others. To do this, we conduct a Cochran’s Test.

Here the response vector (y) is response, the groups are beverage and the block is people.

# Change the data type of beverage and response to factor
breakfast$beverage = as.factor(breakfast$beverage)
breakfast$response = as.factor(breakfast$response)
breakfast
# Cochran test
cochran.qtest(response~beverage | people,
              data = breakfast,
              alpha = 0.05)

    Cochran's Q test

data:  response by beverage, block = people 
Q = 3.3333, df = 2, p-value = 0.1889
alternative hypothesis: true difference in probabilities is not equal to 0 
sample estimates:
proba in group coffee  proba in group juice 
            0.6470588             0.3529412 
   proba in group tea 
            0.3529412 

We see that the statistic value is \(3.3333\) and the corresponding p-value is \(0.1889 > 0.05\). Thus we conclude that at \(0.05\) level of significance, the null hypothesis that there is no difference in people’s preference of beverage is not rejected.

  1. Suppose the following data regarding a survey conducted on 14 students regarding some activities they are willing to do.

(from : https://rcompanion.org/handbook/H_07.html#:~:text=Cochran’s%20Q%20test%20is%20an,multiple%20categories%20with%20paired%20responses.&text=qtest%20which%20will%20conduct%20Cochran’s%20Q%20test%20on%20long%2Dformat%20data.)

students

We want to test if there are any activities which are more appealing for the students to do. To test this, we will conduct the Cochran’s Test, with Practice as groups, Student as the blocks, and Response as the response vector

# Changing the response vector and the groups as factor
students$Practice = as.factor(students$Practice)
students$Response = as.factor(students$Response)

# Cochran's Test
cochran.qtest(Response ~ Practice | Student,
              data = students,
              alpha = 0.05)

    Cochran's Q test

data:  Response by Practice, block = Student 
Q = 16.9535, df = 3, p-value = 0.0007225
alternative hypothesis: true difference in probabilities is not equal to 0 
sample estimates:
 proba in group Clippings proba in group Irrigation 
                0.5714286                 0.1428571 
 proba in group MowHeight   proba in group SoilTest 
                0.8571429                 0.7857143 

        Pairwise comparisons using Wilcoxon sign test

           Clippings Irrigation MowHeight
Irrigation    0.0625          -         -
MowHeight     0.3281    0.03516         -
SoilTest      0.5438    0.03516         1

P value adjustment method: fdr

Since the p-value is \(0.0007225 < 0.05\), we conclude that there is a difference in students’ preferred activities (Some activities have more probability that the students will be willing to do than others).

Note that the function also produces a pairwise comparison between the groups using pairwise Wilcoxon Sign Test if the null hypothesis of the Cochran’s Test is rejected at alpha level. Another alternative (that is more frequently done), is to do pairwise McNemar Tests on each group to test which group have a higher probability compared to the other.

To do pairwise McNemar Tests, we make use of the function pairwiseMcnemar from the rcompanion package

The syntax for the function pairwiseMcnemar is as follows

pairwiseMcnemar(formula,
                data,
                x,
                g,
                block,
                test,
                ...
                )

The input arguments are :

  • formula : formula of the form x ~ g | b, similar to the one used as in the cochran.qtest, x is the response vector, g is the group, and b is the block.
  • data : data frame for data reference
  • x : response vector
  • g : group variable
  • block : block variable
  • test : which test to use, either "exact", "mcnemar", or "permutation". “exact” denotes an exact test of symmetry analogous to McNemar test, "mcnemar" denotes the McNemar test of symmetry, and "permutation" denotes a permutation test analogous to the McNemar test.
  • …, other inputs (refer to the documentation of the function)

The input data should be either including formula and data, or x,g, and block.

# Pairwise McNnmar test on the students data
pair_mcnemar = pairwiseMcnemar(Response ~ Practice | Student,
                data = students,
                test = "mcnemar")
pair_mcnemar
$Test.method

$Adustment.method

$Pairwise
NA

We can gather the p-value for each comparison. At \(0.05\) level of significance, we can see that the probability that the students prefer to do the activity “Irrigation” is significantly different compared to the activity “Clippings”, “MowHeight” and “SoilTest”.

Theil-Sen Regression

To do Theil-Sen regression, we make use of the function mblm from the mblm package

The syntax for the mblm function is

mblm(formula,
     dataframe,
     repeated = TRUE)

The input arguments are :

Similar to the lm function, the mblm function returns a list of items regarding the model (such as residuals, parameter estimates, etc)

Example

  1. Suppose we have the following data
data = theilsenreg
data

where y is the response vector and x is the regressor

We will create a Theil Sen regression model for this data

# Create the theil sen regression model
ts_model = mblm(y~x,
                data = data)

# Summary of the theil-sen regression
summary(ts_model)
cannot compute exact p-value with tiescannot compute exact p-value with tiescannot compute exact p-value with tiescannot compute exact p-value with ties

Call:
mblm(formula = y ~ x, dataframe = data)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.8310 -1.1370 -0.1368  1.6025 23.5975 

Coefficients:
            Estimate     MAD V value Pr(>|V|)    
(Intercept)  19.6495  3.3022     209 0.000111 ***
x             2.0876  0.2928     210 9.54e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.401 on 18 degrees of freedom

We see that the slope parameter’s p-value (the one under the Pr(>|V|) on the row x) is less than \(0.05\), thus we conclude that the variable x is significant in explaining the variability in y (in other words, the regression is significant).

We will now compare this regression model to the ordinary least squares regression model (simple linear regression model).

# Ordinary least squares regression model (simple linear regression)
ols_model = lm(y~x, data = data)

# Creating a data frame to store the predicted values of each model
reg_data = data.frame(x = data$x, y = data$y,
                      ts_pred = ts_model$fitted.values, # prediction of theil-sen model
                      ols_pred = ols_model$fitted.values # prediction of simple linear regression model)
)
ggplot(reg_data,aes(x = x, y= y)) + 
  geom_point(size = 1) + 
  geom_line(aes(y = ts_pred,color = "Theil-Sen Regression")) + 
  geom_line(aes(y = ols_pred,color = "Simple Linear Regression"),linetype="dashed") + 
  scale_color_manual(values = c("Simple Linear Regression" = "darkblue","Theil-Sen Regression" = "red")) + 
  labs(color = "Regression Model")

Note that the Theil-Sen regression model is less affected by outliers in the data, where as the simple linear regression model is affected by outliers, the (estimated) slope of the simple linear regression model is slightly larger than the Theil-Sen one, since it is being “pulled” by the 2 outliers. Thus, the nonparametric Theil-Sen regression is more robust towards outliers.

Bootstrapping Techniques

Bootstrapping

Given a single sample of data, to estimate a parameter of the distribution by bootstrapping, we can use the function boot from the boot package

The syntax for the ``boot``` function is as follows :

boot(x,
     statistic,
     R,
     ...)

The input arguments are :

  • x : the data, could either be a vector, matrix, or a dataframe. If x is a dataframe or a matrix, each row is considered an observation from a multivariate distribution.
  • statistic : A function which calculates the statistic of interest when applied to x. The function needs to have at least 2 input arguments, since 1 argument is required for the resampling in the bootstrapping process
  • R : How many times the sample is resampled.
  • … : Other input arguments (Refer to the documentation)

Example

  1. Suppose we want to estimate the coefficients of a multiple linear regression model from the mtcars dataset, where we want to model a car’s miles per gallon as a function of its weight and displacement.
# Read the data
boot_data = mtcars

# Define the function needed to calculate the estimated coefficients for the regression model
reg_coef = function(data, formula, i){ # The function takes 3 arguments, data, formula, and i
  dat = data[i,] # For the boot to select samples, we will use the sample dat, taken from data, to make an estimate of the regression coefficient
  lin_model = lm(formula, data = dat) # create the linear regression model from dat
  return(lin_model$coefficients) # Return the coefficients of the regression model
}

# Bootstrapping
set.seed(100) # Set the seed for the bootstrap resampling, so that the result can be reproducible (If this is removed, the result of the bootstrap may vary with each execution)

reg_boot = boot(data = mtcars,
                statistic = reg_coef,
                R = 5000,
                formula = mpg ~ wt + disp # the formula for the reg_coef function
)

reg_boot

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = mtcars, statistic = reg_coef, R = 5000, formula = mpg ~ 
    wt + disp)


Bootstrap Statistics :
       original        bias    std. error
t1* 34.96055404  0.1513454138 2.488647226
t2* -3.35082533 -0.0929682077 1.130996757
t3* -0.01772474  0.0002886382 0.008380343

We can gather from the output that the estimate for the regression coefficients are :

  • \(34.96055405\) for the intercept coefficient
  • \(-3.35082533\) for the coefficient corresponding to wt
  • \(-0.01772474\) for the coefficient corresponding to disp

each with their own bias and standard error.

Furthermore, we can produce a confidence interval for these estimates as follows

# Confidence interval for the bootstrap estimates
boot.ci(reg_boot,conf=0.95) # 95% conf. interval for the intercept coefficient
NaNs producedextreme order statistics used as endpointsNaNs produced
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 5000 bootstrap replicates

CALL : 
boot.ci(boot.out = reg_boot, conf = 0.95)

Intervals : 
Level      Normal              Basic             Studentized     
95%   (29.93, 39.69 )   (30.00, 39.81 )   (  NaN,   NaN )  

Level     Percentile            BCa          
95%   (30.11, 39.92 )   (29.54, 39.53 )  
Calculations and Intervals on Original Scale
Warning : Studentized Intervals used Extreme Quantiles
Some studentized intervals may be unstable
boot.ci(reg_boot, conf = 0.95) # 95% conf.interval for tha coefficient corresponding to wt
NaNs producedextreme order statistics used as endpointsNaNs produced
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 5000 bootstrap replicates

CALL : 
boot.ci(boot.out = reg_boot, conf = 0.95)

Intervals : 
Level      Normal              Basic             Studentized     
95%   (29.93, 39.69 )   (30.00, 39.81 )   (  NaN,   NaN )  

Level     Percentile            BCa          
95%   (30.11, 39.92 )   (29.54, 39.53 )  
Calculations and Intervals on Original Scale
Warning : Studentized Intervals used Extreme Quantiles
Some studentized intervals may be unstable
boot.ci(reg_boot, conf = 0.95) # 95% conf.interval for the coefficeint corresponding to disp
NaNs producedextreme order statistics used as endpointsNaNs produced
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 5000 bootstrap replicates

CALL : 
boot.ci(boot.out = reg_boot, conf = 0.95)

Intervals : 
Level      Normal              Basic             Studentized     
95%   (29.93, 39.69 )   (30.00, 39.81 )   (  NaN,   NaN )  

Level     Percentile            BCa          
95%   (30.11, 39.92 )   (29.54, 39.53 )  
Calculations and Intervals on Original Scale
Warning : Studentized Intervals used Extreme Quantiles
Some studentized intervals may be unstable

The confidence interval for each coefficient is calculated with 3 methods. For more information, refer to the documentation of the function boot.ci.

Jackknife

To do the jackknife method for estimating a distribution’s parameter by resampling, we can use the function jackknife from the bootstrap package.

library(bootstrap) # Load the library (install it first)

Attaching package: ‘bootstrap’

The following object is masked from ‘package:RVAideMemoire’:

    bootstrap

The following object is masked from ‘package:sjstats’:

    bootstrap

The syntax is as follows :

jackknife(x,
          theta,...)

The input arguments are

  • x : numeric vector of sample data
  • theta : a function which will calculate the statistic of interest if applied to x. Note that unlike for the boot function, theta need not have at least 2 input arguments.
  • … : additional arguments for the `theta function

Example

  1. Suppose from the mtcars dataset, we want to estimate the mean of car’s miles per gallon by using the jackknife procedure
# The data
car_mpg = mtcars$mpg

# Define the mean function for theta
mean_jack = function(x){
  return(mean(x))
}

# Jackknife
jack_res = jackknife(x,mean_jack)

jack_res
$jack.se
[1] 3.64434

$jack.bias
[1] 0

$jack.values
 [1] 43.81633 43.65306 43.77551 43.40816 43.53061 43.65306
 [7] 43.48980 43.32653 43.16327 43.51020 43.28571 43.57143
[13] 43.44898 43.36735 43.28571 43.32653 43.16327 43.16327
[19] 42.91837 43.32653 43.12245 42.63265 42.22449 43.44898
[25] 43.32653 42.75510 43.20408 43.04082 43.20408 43.04082
[31] 42.83673 43.00000 42.71429 42.30612 42.14286 43.12245
[37] 42.91837 42.46939 43.20408 42.87755 42.79592 42.71429
[43] 42.55102 42.51020 42.75510 42.42857 41.97959 41.95918
[49] 41.40816 42.12245

$call
jackknife(x = x, theta = mean_jack)

The function jackknife returns a list of objects, they are the estimates of the standard error of the estimated parameter, bias of the estimated parameter, and the (-i) values of the parameter estimates (estimates of the parameter, with the ith observation removed), which can be used to calculate the jackknife estimate, by taking its mean.

# The jackknife parameter estimate
jack_mean_est = mean(jack_res$jack.values)
jack_mean_est
[1] 42.98

We see that the jackknife estimate for the mean is 42.98, and this estimate has an estimate standard error of 3.64434, and an estimate bias of 0.

LS0tCnRpdGxlOiAiTm9ucGFyYW1ldHJpYyBUZXN0cyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKLS0tCgojIEJpbm9taWFsIFRlc3QKClRvIHRlc3QgdGhlIGh5cG90aGVzaXMgOgoKXFtIX3swfSA6IFx0ZXh0e1Byb2JhYmlsaXR5IG9mIHN1Y2Nlc3MgfSAocCkgPSBwKlxdCgpBZ2FpbnN0IGl0cyBjb3JyZXNwb25kaW5nIG9uZS1zaWRlZCAoJEhfezF9IDogcCA+IHAqJCBvciAkSF97MX0gOiBwIDwgcCokKSBvciB0d28tc2lkZWQgYWx0ZXJuYXRpdmUgKCRIX3sxfSA6IHAgXG5lcSBwKiQpCgpXZSBtYWtlIHVzZSBvZiB0aGUgZnVuY3Rpb24gYGBgYmlub20udGVzdGBgYCBwcm92aWRlZCBieSB0aGUgKiptb3NhaWMqKiBwYWNrYWdlLgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KIyBJbnN0YWxsIHRoZSBtb3NhaWMgcGFja2FnZSBpZiBpdCBoYXNuJ3QgYmVlbiBpbnN0YWxsZWQKbGlicmFyeShtb3NhaWMpICMgTG9hZCB0aGUgcGFja2FnZQpgYGAKCgpUaGUgc3ludGF4IGZvciBgYGBiaW5vbS50ZXN0YGBgIGlzIGFzIGZvbGxvd3MgOiAKCmBgYApiaW5vbS50ZXN0KAogIHgsICAgICAgICAgICAgIyBEYXRhIHRvIGJlIHRlc3RlZAogIG4gPSBOVUxMLCAgICAgIyBTYW1wbGUgc2l6ZQogIHAsICAgICAgICAgICAgIyBIeXBvdGVzaXplZCBwcm9iYWJpbGl0eSBvZiBzdWNjZXNzIChkZWZhdWx0IHZhbHVlIGlzIDAuNSkKICBhbHRlcm5hdGl2ZSA9IGMoInR3by5zaWRlZCIsImxlc3MiLCJncmVhdGVyKSwgICAjIEFsdCBoeXBvdGhlc2lzIChkZWZhdWx0IGlzIHR3by5zaWRlZCkKICBjb25mLmxldmVsID0gMC45NSwgICMgQ29uZmlkZW5jZSBsZXZlbCBvZiBzaWduaWZpY2FuY2UgdG8gdGVzdCAoZGVmYXVsdCB2YWx1ZSBpcyAwLjk1KQogIAogICMgTWV0aG9kIGZvciBjb21wdXRpbmcgY29uZmlkZW5jZSBpbnRlcnZhbAogIGNpLm1ldGhvZCA9IGMoIkNsb3BwZXItUGVhcnNvbiIsICJiaW5vbS50ZXN0IiwgIlNjb3JlIiwgIldpbHNvbiwKICAgICAgICAgICAgICAgICJwcm9wLnRlc3QiLCAiV2FsZCIsICJBZ3Jlc3RpLUNvdWxsIiwgIlBsdXM0IikKICAgICAgICAgICAgICAgIAogIGRhdGEsICAgICAgICAgIyBEYXRhZnJhbWUgZnJvbSB3aGljaCB0aGUgc2FtcGxlICJ4IiBpcyBhY2Nlc3NlZC4KICBzdWNjZXNzICAgICAgICMgTGV2ZWwgb2YgdmFyaWFibGUgdG8gYmUgY29uc2lkZXJlZCBzdWNjZXNzCikKYGBgCldlIG5vdGUgc2V2ZXJhbCB0aGluZ3MgcmVnYXJkaW5nIHRoZSBpbnB1dCBhcmd1bWVudHMgZm9yIHRoZSBgYGBiaW5vbS50ZXN0YGBgIGZ1bmN0aW9uLiAKCiogKip4KiogY2FuIGJlIGVpdGhlciBvZiB0aGUgZm9sbG93aW5nIG9iamVjdHMgOiAKICArIFwjIG9mIHN1Y2Nlc3NlcyAoaS5lIGhvdyBtYW55IG9mIHRoZSBkYXRhIHBvaW50cyBhcmUgY29uc2lkZXJlZCBhICJzdWNjZXNzIikKICArIEEgdmVjdG9yIG9mIFwjIG9mIHN1Y2Nlc3NlcyBhbmQgXCMgb2YgZmFpbHVyZXMgKGV4IDogRnJvbSBhIHNhbXBsZSBvZiBzaXplIDIwMCwgaWYgICAgICAxNzUgb2YgdGhlbSBhcmUgY29uc2lkZXJlZCAic3VjY2Vzc2VzIiAsIHggY291bGQgYmUgYGBgYygxNzUsMjUpYGBgKQogICsgQSB2ZWN0b3IgY29uc2lzdGluZyBvZiAwJ3MgYW5kIDEncyAodXN1YWxseSAwIGRlbm90ZXMgYSBmYWlsdXJlLCBhbmQgMSBkZW5vdGVzIGEgc3VjY2VzcykKICArIEEgbG9naWNhbCB2ZWN0b3IgKHVzdWFsbHkgVFJVRSBkZW5vdGVzIGEgc3VjY2VzcywgYW5kIEZBTFNFIGRlbm90ZXMgYSBmYWlsdXJlKQogIAoqIFRvIHNwZWNpZnkgd2hpY2ggKiphbHRlcm5hdGl2ZSoqIG9yICoqY2kubWV0aG9kKiosIHdlIGNhbiB1c2UgdGhlIGluaXRpYWxzIG9mIGVhY2ggb3B0aW9uLiBGb3IgZXhhbXBsZSwgaWYgd2Ugd2FudGVkIHRvIHRlc3QgYWdhaW5zdCB0aGUgb25lLXNpZGVkICh1cHBlcikgaHlwb3RoZXNpcywgd2UgY2FuIHNwZWNpZnkgYGBgYWx0ZXJuYXRpdmUgPSAiZyIgYGBgIGFzIG9wcG9zZWQgdG8gYGBgYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIgYGBgLgoKIyMjIEV4YW1wbGUgCjEuIFN1cHBvc2UgYSBmb29kIHRydWNrIGJ1c2luZXNzIGlzIGV4cGVyaW1lbnRpbmcgb24gYSBuZXcgaXRlbSB0byBzZWxsLiBBbiBleHBlcmltZW50IGlzIHRoZW4gY29uZHVjdGVkIHRvIHNlZSBob3cgd2VsbCB0aGUgbmV3IHByb2R1Y3QgbWlnaHQgc2VsbCBpbiBjb250cmFzdCB0byB0aGUgb2xkIHByb2R1Y3QuIFRoZSBleHBlcmltZW50IGlzIGNvbmR1Y3RlZCB0byAzMCBkaWZmZXJlbnQgcGVvcGxlLCBlYWNoIHBhcnRpY2lwYW50IHRhc3RlcyB0aGUgMiBwcm9kdWN0cyBpbiByYW5kb20gb3JkZXIgYW5kIGdpdmVzIHRoZWlyIG9waW5pb24gb2Ygd2hpY2ggb25lIGlzIGJldHRlci4gSXQgdHVybnMgb3V0IHRoYXQgMTggcGFydGljaXBhbnQgcHJlZmVycyB0aGUgbmV3IHByb2R1Y3QuIFRvIHRlc3QgaWYgdGhpcyBpcyBzaWduaWZpY2FudCBlbm91Z2ggdG8gY29uY2x1ZGUgdGhhdCB0aGUgbmV3IHByb2R1Y3QgaXMgYmV0dGVyLCB3ZSBwZXJmb3JtIGEgYmlub21pYWwgdGVzdCBhcyBmb2xsb3dzCgpgYGB7cn0KIyBUaGVyZSBhcmUgMTggc3VjY2Vzc2VzIGZyb20gMzAgZXhwZXJpbWVudHMsIGFuZCBpZiB3ZSBhc3N1bWUgdGhhdCB0aGUgMiBwcm9kdWN0cyB3b3VsZCBiZSBlcXVhbGx5IGxpa2VseSB0byBiZSBzb2xkLCB0aGUgcHJvYmFiaWxpdHkgb2Ygc3VjY2VzcyBpcyAxLzIsIHRodXMgdGhlIHRlc3QgaXMgCm1vc2FpYyA6OiBiaW5vbS50ZXN0KDE4LAogICAgICAgICAgICAgICAgICAgICBuPTMwLAogICAgICAgICAgICAgICAgICAgICBwPTAuNSwKICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAiZyIpCmBgYApTaW5jZSB0aGUgcC12YWx1ZSBpcyAkMC4xODA4ID4gMC4wNSA9IFxhbHBoYSQsIHRoZW4gYXQgJFxhbHBoYSQgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlLCB3ZSBjYW5ub3QgY29uY2x1ZGUgZnJvbSB0aGUgZXhwZXJpbWVudCwgdGhhdCB0aGUgbmV3IGZvb2QgaXRlbSB3aWxsIHNlbGwgYmV0dGVyIHRoYW4gdGhlIG9sZCBvbmUuCgoyLiBbUXVhbnRpbGUgdGVzdGluZ10uCgogICAgU3VwcG9zZSB3ZSBoYXZlIHRoZSBkYXRhIG9mIGdhc29saW5lIGNvbnN1bXB0aW9uIChpbiBLbS9MKSBvZiBhIHRheGkgY29tcGFueSB3aXRoIHR3byBkaWZmZXJlbnQgdHlwZXMgb2YgdGF4aXMsIG9uZSB3aXRoIHJhZGlhbCB0aXJlcywgYW5kIGFub3RoZXIgd2l0aCBiZWx0ZWQgdGlyZXMuIFdlIHdhbnQgdG8gdGVzdCBpZiA6CiAgICBhLiB0aGUgbWVkaWFuIGdhcyBjb25zdW1wdGlvbiBvZiB0aGUgdGF4aSB3aXRoIHJhZGlhbCB0aXJlcyBkb2VzIG5vdCBleGNlZWQgNC41IEttL0wuCiAgICBiLiB0aGUgUTEgb2YgZ2FzIGNvbnN1bXB0aW9uIG9mIHRoZSB0YXhpIHdpdGggYmVsdGVkIHRpcmVzIGlzICRcZ2VxJCA1LjUgS20vTApgYGB7cn0KIyBSZWFkIHRoZSBkYXRhCnRheGlfZGF0YQpgYGAKYGBge3J9CiMgTWVkaWFuIGdhcyBjb25zdW1wdGlvbiBvZiB0YXhpIHcvIHJhZGlhbCB0aXJlcwoKcmFkX21lZCA9IDQuNSAjIEh5cG90aGVzaXplZCBtZWRpYW4KCiMgVW5kZXIgdGhlIG51bGwgaHlwb3RoZXNpcywgdGhlIHByb2JhYmlsaXR5IG9mIHRoZSB0YXhpJ3MgZ2FzIGNvbnN1bXB0aW9uIHZhbHVlcyA+IDQuNSBhbmQgPCA0LjUgYXJlIGVxdWFsICh3LyBwcm9iYWJpbGl0eSAwLjUpLgpyYWRfdGVzdCA9IHRheGlfZGF0YSRyYWRpYWxfdGlyZXMgPD0gNC41CgojIFRoZSBoeXBvdGhlc2lzIGlzIGVxdWl2YWxlbnQgdG8gdGVzdGluZwojIEgwIDogUChYPD00LjUpID49IDAuNQojIEgxIDogUChYPD00LjUpIDwgMC41CiMgVGVzdCB0aGUgaHlwb3RoZXNpcwptb3NhaWMgOjogYmlub20udGVzdChyYWRfdGVzdCwKICAgICAgICAgICAgICAgICAgICAgcCA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAibCIsCiAgICAgICAgICAgICAgICAgICAgIGNvbmYubGV2ZWwgPSAwLjk1KQpgYGAKCiAgU2luY2UgdGhlIHAtdmFsdWUgaXMgJDAuMDAyMDkgPCAwLjA1JCwgdGh1cyBhdCB0aGUgc2lnbmlmaWNhbmNlIGxldmVsIG9mICQwLjA1JCwgIHdlIGNhbiBjb25jbHVkZSB0aGF0IHRoZSBtZWRpYW4gZ2FzIGNvbnN1bXB0aW9uIG9mIHRheGlzIHdpdGggcmFkaWFsIHRpcmVzIGFyZSBncmVhdGVyIHRoYW4gNC41IEttL0wuCiAgICAgIApgYGB7cn0KIyBRMSBvZiBnYXMgY29uc3VtcHRpb24gb2YgdGF4aXMgdy8gYmVsdGVkIHRpcmVzCmJlbHRfcTEgPSA1LjUgIyBIeXBvdGhlc2l6ZWQgUTEKCiMgaWYgWSBpcyB0aGUgZ2FzIGNvbnN1bXB0aW9uIG9mIHRheGlzIHdpdGggYmVsdGVkIHRpcmVzLCB0aGUgaHlwb3RoZXNpcyBpcyBlcXVpdmFsZW50IHRvCiMgSDAgOiBQKFkgPD0gNS41KSA8PSAwLjI1CiMgSDEgOiBQKFkgPD0gNS41KSA+IDAuMjUKCiMgV2UgYXNzaWduIHRoZSBvYnNlcnZhdGlvbiB3LyB2YWx1ZXMgPD0gNC41IGFzIFRSVUUsIGFuZCBlbHNlLCBhcyBGQUxTRQpiZWx0X3Rlc3QgPSB0YXhpX2RhdGEkYmVsdGVkX3RpcmVzIDw9IGJlbHRfcTEKCiMgSHlwb3RoZXNpcyB0ZXN0Cm1vc2FpYyA6OiBiaW5vbS50ZXN0KGJlbHRfdGVzdCwKICAgICAgICAgICAgICAgICAgICAgcCA9IDAuMjUsCiAgICAgICAgICAgICAgICAgICAgIGFsdGVybmF0aXZlID0gImciLAogICAgICAgICAgICAgICAgICAgICBjb25mLmxldmVsID0gMC45NSkKCmBgYAoKICBTaW5jZSB0aGUgcC12YWx1ZSBpcyAkMC4wNzk1NiA+IDAuMDUkLCB3ZSBjb25jbHVkZSB0aGF0IGF0IHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgb2YgJDAuMDUkLCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlIFExIG9mIGdhcyBjb25zdW1wdGlvbiBvZiB0YXhpcyB3LyBiZWx0ZWQgdGlyZXMgaXMgJFxnZXEkIDUuNSBLbS9MIGlzIG5vdCByZWplY3RlZC4KCgojIFJhbmsgVGVzdHMKCiMjIFNpZ24gVGVzdApUbyBwZXJmb3JtIGEgc2lnbiB0ZXN0LCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGBgYFNpZ25UZXN0YGBgIGZyb20gdGhlICoqRGVzY1Rvb2xzKiogcGFja2FnZS4KCmBgYHtyIGluY2x1ZGU9RkFMU0V9CiMgSW5zdGFsbGluZyB0aGUgcGFja2FnZSAiRGVzY1Rvb2xzIgppbnN0YWxsLnBhY2thZ2VzKCJEZXNjVG9vbHMiKQpsaWJyYXJ5KERlc2NUb29scykKYGBgCgpUaGUgc3ludGF4IGZvciBgYGBTaWduVGVzdGBgYCBpcyBhcyBmb2xsb3dzCgpgYGAKU2lnblRlc3QoeCwKICAgICAgICAgeSA9IE5VTEwsCiAgICAgICAgIG11ID0gMCwKICAgICAgICAgYWx0ZXJuYXRpdmUgPSBjKCJ0d28uc2lkZWQiLCJsZXNzIiwiZ3JlYXRlciIpLAogICAgICAgICBjb25mLmxldmVsID0gMC45NSkKYGBgClRoZSBpbnB1dCBhcmd1bWVudHMgYXJlIDogCgoqICoqeCoqIDogdmVjdG9yIG9mIGRhdGEgdG8gYmUgdGVzdGVkCiogKip5KiogOiB2ZWN0b3Igb2YgZGF0YSB0byBiZSB0ZXN0ZWQgKGZvciAyIHNhbXBsZSBzaWduIHRlc3QpLiBUaGUgZGVmYXVsdCB2YWx1ZSBpcyBOVUxMCiogKiptdSoqIDogaHlwb3RoZXNpemVkIG1lZGlhbi4gVGhlIGRlZmF1bHQgdmFsdWUgaXMgMC4KKiAqKmFsdGVybmF0aXZlKiogOiBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzCiogKipjb25mLmxldmVsKiogOiBjb25maWRlbmNlIGxldmVsIGZvciB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbCAoYWxzbyB0aGUgc2lnbmlmaWNhbmNlIGxldmVsIG9mIHRoZSBoeXBvdGhlc2lzIHRlc3QpLgoKVGhlIGZ1bmN0aW9uIHdpbGwgcmV0dXJuIGEgbGlzdCBvZiBjbGFzcyBgYGBodGVzdGBgYCB3aGljaCBjb250YWlucyB0aGUgc3VtbWFyeSBvZiB0aGUgaHlwb3RoZXNpcyB0ZXN0ICh0aGUgc3RhdGlzdGljIHZhbHVlLCBwLXZhbHVlLCBldGMpCgojIyMgRXhhbXBsZQoxLiBGcm9tIHRoZSB0YXhpIGRhdGEsIHdlIHdpbGwgdXNlIHRoZSBzaWduIHRlc3QgdG8gdGVzdCAoYXQgc2lnbmlmaWNhbmNlIGxldmVsICQwLjA1JCkgdGhlIGh5cG90aGVzZXMgOiAKCiAgICBhLiB0aGUgbWVkaWFuIGdhcyBjb25zdW1wdGlvbiBvZiB0YXhpcyB3aXRoIHJhZGlhbCB0aXJlcyBpcyBlcXVhbCB0byA0LjUgS20vTCBhZ2FpbnN0IHRoZSBoeXBvdGhlc2lzIHRoYXQgaXQgaXMgZ3JlYXRlciB0aGFuIDQuNSBLbS9MCiAgCiAgICBiLiB0aGUgbWVkaWFuIGdhcyBjb25zdW1wdGlvbiBvZiB0YXhpcyB3aXRoIGJlbHRlZCB0aXJlcyBpcyBlcXVhbCB0byA1IEttL0wgYWdhaW5zdCB0aGUgaHlwb3RoZXNpcyB0aGF0IGl0IGlzIG5vdCBlcXVhbCB0byA1IEttL0wKICAKICAgIGMuIGlmIHRoZSB0aXJlIHR5cGUgb2YgdGhlIHRheGkgYWZmZWN0cyB0aGUgbWVkaWFuIGdhcyBjb25zdW1wdGlvbiBvZiB0aGUgdGF4aXMuIChUZXN0IGZvciB0aGUgbWVkaWFuIG9mIHRoZSBkaWZmZXJlbmNlIG9mIGdhcyBjb25zdW1wdGlvbikKICAKYGBge3J9CiMgMWEuIE1lZGlhbiBnYXMgY29uc3VtcHRpb24gb2YgdGF4aXMgdy8gcmFkaWFsIHRpcmVzCm1kXzFhID0gNC41ICAjIEh5cG90aGVzaXplZCBtZWRpYW4KCiMgVGVzdCB0aGUgaHlwb3RoZXNpcwpTaWduVGVzdCh0YXhpX2RhdGEkcmFkaWFsX3RpcmVzLAogICAgICAgICBtdSA9IG1kXzFhLAogICAgICAgICBhbHRlcm5hdGl2ZSA9ICJnIiwgICMgQWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyBpcyA+IG1kXzFhCiAgICAgICAgIGNvbmYubGV2ZWwgPSAwLjk1KQpgYGAKICBGcm9tIHRoZSBvdXRwdXQsIHdlIGNhbiBnYXRoZXIgdGhlIGZvbGxvd2luZyBpbmZvcm1hdGlvbiA6CiAgCiAgICAqIFMgOiB0aGUgc3RhdGlzdGljIHdoaWNoIGRlbm90ZXMgdGhlICMgb2YgcG9zaXRpdmUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgZGF0YSBhbmQgdGhlIGh5cG90aGVzaXplZCBtZWRpYW4gKGhvdyBtYW55IGRhdGEgcG9pbnRzIGFyZSA+IGh5cG90aGVzaXplZCBtZWRpYW4pCiAgCiAgICAqIG51bWJlciBvZiBkaWZmZXJlbmNlcyA6IEhvdyBtYW55IGRhdGEgcG9pbnRzIHdoaWNoIGFyZSBkaWZmZXJlbnQgZnJvbSB0aGUgaHlwb3RoZXNpemVkIG1lZGlhbi4gKGRhdGEgcG9pbnRzIHdoaWNoIGFyZSBlcXVhbCB0byB0aGUgaHlwb3RoZXNpemVkIG1lZGlhbiBhcmUgcmVtb3ZlZCkKICAgIAogICAgKiBzYW1wbGUgZXN0aW1hdGUgb2YgdGhlIG1lZGlhbiBvZiB0aGUgZGlmZmVyZW5jZSA6IHRoZSBzYW1wbGUgbWVkaWFuIHdoaWNoIGlzIHRoZSBlc3RpbWF0ZSBmb3IgdGhlIGRpc3RyaWJ1dGlvbidzIG1lZGlhbi4KICAKICBTaW5jZSB0aGUgcC12YWx1ZSBmb3IgdGhlIHN0YXRpc3RpYyBpcyAkMC4wMDA0ODgzIDwgMC4wNSQsIHdlIGNvbmNsdWRlIGF0IHRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgb2YgJDAuMDUkIHRoYXQgdGhlIG1lZGlhbiBnYXMgY29uc3VtcHRpb24gb2YgdGF4aXMgd2l0aCByYWRpYWwgdGlyZXMgaXMgZ3JlYXRlciB0aGFuIDQuNSBLbS9MLgogIAogIFwqIE5vdGUgdGhhdCB0aGUgcC12YWx1ZSBpcyB0aGUgc2FtZSBhcyBwZXJmb3JtaW5nIGEgYmlub21pYWwgdGVzdCBmb3IgcXVhbnRpbGUgdGVzdGluZyB0aGUgbWVkaWFuLCB3aGVyZSB0aGUgZGF0YSBwb2ludCB3aGljaCBpcyBlcXVhbCB0byB0aGUgaHlwb3RoZXNpemVkIG1lZGlhbiwgaXMgcmVtb3ZlZC4KICAKYGBge3J9CiMgMWIuIE1lZGlhbiBvZiB0YXhpcyB3aXRoIGJlbHRlZCB0aXJlcy4KbWRfMWIgPSA1ICMgSHlwb3RoZXNpemVkIG1lZGlhbgoKIyBUZXN0IHRoZSBoeXBvdGhlc2lzClNpZ25UZXN0KHRheGlfZGF0YSRiZWx0ZWRfdGlyZXMsCiAgICAgICAgIG11ID0gbWRfMWIsCiAgICAgICAgIGFsdGVybmF0aXZlID0gInQiLCAgIyBBbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIGlzICE9IDUKICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUKICAgICAgICAgKQpgYGAKICBTaW5jZSB0aGUgcC12YWx1ZSBmb3IgdGhlIHN0YXRpc3RpYyBpcyAkMC40NTQ1ID4gMC4wNSQsIHdlIGNvbmNsdWRlIHRoYXQgdGhlIG51bGwgaHlwb3RoZXNpcyBjYW5ub3QgYmUgcmVqZWN0ZWQgYXQgJDAuMDUkIGxldmVsIG9mIHNpZ25pZmljYW5jZS4KICAKYGBge3J9CiMgMWMuIG1lZGlhbiBvZiB0aGUgZGlmZmVyZW5jZQptZF8xYyA9IDAgIyBIeXBvdGhlc2l6ZWQgbWVkaWFuIG9mIHRoZSBkaWZmZXJlbmNlCgojIFRlc3QgdGhlIGh5cG90aGVzaXMKU2lnblRlc3QoeCA9IHRheGlfZGF0YSRyYWRpYWxfdGlyZXMsCiAgICAgICAgIHkgPSB0YXhpX2RhdGEkYmVsdGVkX3RpcmVzLAogICAgICAgICBtdSA9IG1kXzFjLAogICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0IiwKICAgICAgICAgY29uZi5sZXZlbCA9IDAuOTUKICAgICAgICAgKQpgYGAKICBTaW5jZSB0aGUgcC12YWx1ZSBpcyAkMC4wNTczNyA+IDAuMDUkLCB3ZSBjb25jbHVkZSB0aGF0IGF0ICQwLjA1JCBsZXZlbCBvZiBzaWduaWZpY2FuY2UsIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGVyZSBleGlzdCBubyBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGluIHRoZSBtZWRpYW4gZ2FzIGNvbnN1bXB0aW9uIG9mIHRoZSAyIHR5cGVzIG9mIHRheGlzIGNhbm5vdCBiZSByZWplY3RlZC4KICAKICBBbm90aGVyIHdheSB0byBkbyB0aGUgc2lnbiB0ZXN0IG9uIGRpZmZlcmVuY2VzIG9mIDIgc2FtcGxlcyAoMi1zYW1wbGUgc2lnbiB0ZXN0KSBpcyB0byBmaXJzdCBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHR3byBzYW1wbGVzLiBBbiBleGFtcGxlIG9mIHRoaXMgaXMgCmBgYHtyfQojIDFjCgojIENhbGN1bGF0ZXMgdGhlIGRpZmZlcmVuY2Ugb2YgdGhlIDIgc2FtcGxlIGZpcnN0CmQgPSB0YXhpX2RhdGEkcmFkaWFsX3RpcmVzIC0gdGF4aV9kYXRhJGJlbHRlZF90aXJlcwoKIyBUZXN0IHRoZSBoeXBvdGhlc2lzClNpZ25UZXN0KGQsCiAgICAgICAgIG11ID0gbWRfMWMsCiAgICAgICAgIGFsdGVybmF0aXZlID0gInQiLAogICAgICAgICBjb25mLmxldmVsID0gMC45NSkKYGBgCiAgTm90ZSB0aGF0IHRoaXMgZ2l2ZXMgdGhlIHNhbWUgcmVzdWx0IGFzIGJlZm9yZS4KICAKICAKIyMgU3BlYXJtYW4ncyBDb2VmZmljaWVudCBvZiBSYW5rIENvcnJlbGF0aW9uClRvIGNhbGN1bGF0ZSB0aGUgU3BlYXJtYW4ncyByYW5rIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG9mIDIgdmFyaWFibGVzIGZyb20gYSBnaXZlbiBzYW1wbGUsIHdlIHVzZSB0aGUgZnVuY3Rpb24gYGBgY29yYGBgIGZyb20gdGhlIGBgYHN0YXRzYGBgIHBhY2thZ2UuIFRoZSBwYWNrYWdlIGlzIHVzdWFsbHkgYWxyZWFkeSBpbnN0YWxsZWQgYWxvbmcgd2l0aCBSLgoKVGhlIHN5bnRheCBmb3IgdGhlIGBgYGNvcmBgYCBmdW5jdGlvbiBpcyBhcyBmb2xsb3dzIDogCgpgYGAKY29yKHgseSwKICAgIG1ldGhvZCA9IGMoInBlYXJzb24iLCJrZW5kYWxsIiwic3BlYXJtYW4iKSwKICAgIHVzZSA9ICJldmVyeXRoaW5nIikKYGBgClRoZSBpbnB1dCBhcmd1bWVudHMgYXJlIDogCgoqICoqKngsIHkqKiogOiBWZWN0b3JzIG9mIGRhdGEgKG51bWVyaWMpIChkb2VzIG5vdCBoYXZlIHRvIGJlIHJhbmtlZCBkYXRhKS4gVGhpcyBjYW4gYWxzbyBiZSBhIG1hdHJpeCBvZiBkYXRhLCBpZiB0aGlzIGlzIHRoZSBjYXNlLCB0aGVuIHRoZSBmdW5jdGlvbiB3aWxsIHJldHVybiBhIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IG1hdHJpeC4KKiAqKiptZXRob2QqKiogOiBUaGUgbWV0aG9kIHRoYXQgd2lsbCBiZSB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQKKiAqKip1c2UqKiogOiBTcGVjaWZ5IHdoaWNoIGRhdGEgYXJlIGdvaW5nIHRvIGJlIHVzZWQsIHRoZSBvcHRpb25zIGFyZSA6IAogICsgImV2ZXJ5dGhpbmciIC0gSWYgZWl0aGVyIHRoZSB4IGNvbXBvbmVudCBvciB0aGUgeSBjb21wb25lbnQgaXMgTkEsIHRoZW4gdGhlIGNvcnJlbGF0aW9uIHdpbGwgYmUgTkEgKHRoaXMgaXMgdGhlIGRlZmF1bHQpLgogICsgImFsbC5vYnMiIC0gVXNlIGFsbCB0aGUgZGF0YSwgaWYgdGhlcmUgYXJlIE5BcywgdGhlIGZ1bmN0aW9uIHdpbGwgcmFpc2UgYW4gZXJyb3IuCiAgKyAiY29tcGxldGUub2JzIiAtIElmIGVpdGhlciBvZiB0aGUgdmFyaWFibGUgaGFzIGFuIE5BLCB0aGF0IG9ic2VydmF0aW9uIGlzIGRyb3BwZWQgZnJvbSB0aGUgY2FsY3VsYXRpb24uCiAgKyAibmEub3IuY29tcGxldGUiIC0gRG9lcyB0aGUgc2FtZSB0aGluZyBhcyBjb21wbGV0ZS5vYnMsIGV4Y2VwdCBpZiB0aGVyZSBhcmUgbm8gY29tcGxldGUgb2JzZXJ2YXRpb25zLCB0aGVuIHRoZSBmdW5jdGlvbiB3aWxsIHJldHVybiBOQS4KICArICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiIC0gRG9lcyBzaW1pbGFyIHRyZWF0bWVudCB0byBOQXMgYXMgY29tcGxldGUub2JzLCBidXQgb2JzZXJ2YXRpb24gd2l0aCBOQXMgYXJlIG9ubHkgb21pdHRlZCBmcm9tIGNhbGN1bGF0aW9ucyBvZiBlYWNoIHBhaXJ3aXNlIGNvcnJlbGF0aW9uLgogIFwqIEZvciBtb3JlIGluZm9ybWF0aW9uLCBvbmUgY2FuIHJlYWQgZnJvbSB0aGUgZG9jdW1lbnRhdGlvbiBvZiB0aGUgZnVuY3Rpb24uCiAgCldlIGNhbiBhbHNvIHRlc3QgdGhlIGh5cG90aGVzaXMgOiAKXFsKSF97MH0gOiBcdGV4dHtUaGUgU3BlYXJtYW4ncyBSYW5rIENvcnJlbGF0aW9uIENvZWZmaWNpZW50IH0gKFxyaG8pID0gMApcXQphZ2FpbnN0IGl0cyBvbmUtc2lkZWQgb3IgdHdvLXNpZGVkIGFsdGVybmF0aXZlcyBieSB1c2luZyB0aGUgZnVuY3Rpb24gYGBgY29yLnRlc3RgYGAgYWxzbyBmcm9tIHRoZSBgYGBzdGF0c2BgYCBwYWNrYWdlLgoKVGhlIHN5bnRheCBpcyBzaW1pbGFyIHRvIHRoZSBgYGBjb3JgYGAgZnVuY3Rpb24gOiAKCmBgYApjb3IudGVzdCh4LHksCiAgICAgICAgIG1ldGhvZCA9IGMoInBlYXJzb24iLCJrZW5kYWxsIiwic3BlYXJtYW4iKSwKICAgICAgICAgYWx0ZXJuYXRpdmUgPSBjKCJ0d28uc2lkZWQiLCJsZXNzIiwiZ3JlYXRlciIpLAogICAgICAgICBjb25mLmxldmVsID0gMC45NSkKYGBgCndoZXJlICoqKmFsdGVybmF0aXZlKioqIGlzIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzLCBhbmQgKioqY29uZi5sZXZlbCoqKiBpcyB0aGUgY29uZmlkZW5jZSBsZXZlbCBmb3IgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwuCgojIyMgRXhhbXBsZSAKCjEuIENvbnNpZGVyIHRoZSBmb2xsb3dpbmcgZGF0YSByZWdhcmRpbmcgdGhlIHJhdGluZyBnaXZlbiBieSBwYXJ0aWNpcGFudHMgZm9yIGEgcGFydGljdWxhciBwcm9kdWN0IGJ5IDkgZGlmZmVyZW50IG1hbnVmYWN0dXJlcnMsIGFuZCB0aGUgcHJpY2Ugb2YgdGhlIHByb2R1Y3QgZnJvbSBlYWNoIG1hbnVmYWN0dXJlci4KCmBgYHtyfQpyYXRpbmdfcHJpY2VfZGF0YQpgYGAKCldlIGNhbiBwbG90IHRoZSBkYXRhIHRvIHNlZSB2aXN1YWxseSBpZiB0aGVyZSBtaWdodCBiZSBpbmRpY2F0aW9uIG9mIGFueSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSByYXRpbmcgYW5kIHRoZSBwcmljZQpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QocmF0aW5nX3ByaWNlX2RhdGEpICsgCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHg9UmF0aW5nLHk9UHJpY2UpLGNvbG9yID0gJyMyOTgwQjknKQpgYGAKSXQgc2VlbXMgYXMgaWYgdGhlIFJhdGluZyBhbmQgUHJpY2UgYXJlIG5vdCB2ZXJ5IG11Y2ggY29ycmVsYXRlZC4gV2Ugbm93IGNhbGN1bGF0ZSB0aGUgU3BlYXJtYW4gcmFuayBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBmb3IgUmF0aW5nIGFuZCBQcmljZQpgYGB7cn0KIyBTcGVhcm1hbiByYW5rIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50CmNvcihyYXRpbmdfcHJpY2VfZGF0YSRSYXRpbmcsCiAgICByYXRpbmdfcHJpY2VfZGF0YSRQcmljZSwKICAgIG1ldGhvZD0icyIpCmBgYApUaGVyZSBhcHBlYXJzIHRvIGJlIGEgc2xpZ2h0IG5lZ2F0aXZlIGNvcnJlbGF0aW9uLCB3ZSBjYW4gdGVzdCB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoaXMgcmVzdWx0IHVzaW5nIGBgYGNvci50ZXN0YGBgIGFzIGZvbGxvd3MKYGBge3J9CiMgSHlwb3RoZXNpcyB0ZXN0aW5nIGZvciBzaWduaWZpY2FuY2Ugb2YgY29ycmVsYXRpb24KY29yLnRlc3QocmF0aW5nX3ByaWNlX2RhdGEkUmF0aW5nLAogICAgICAgICByYXRpbmdfcHJpY2VfZGF0YSRQcmljZSwKICAgICAgICAgbWV0aG9kPSJzIiwKICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidCIsCiAgICAgICAgICkKYGBgCgpXZSBjYW4gc2VlIHRoYXQgdGhlIHAtdmFsdWUgZm9yIHRoZSBzdGF0aXN0aWMgaXMgJDAuMjEyNSA+IDAuMDUkLiBUaHVzIHdlIGNvbmNsdWRlIHRoYXQgYXQgJDAuMDUkIGxldmVsIG9mIHNpZ25pZmljYW5jZSwgdGhlIGh5cG90aGVzaXMgdGhhdCByYXRpbmcgYW5kIHByaWNlIGFyZSB1bmNvcnJlbGF0ZWQgY2Fubm90IGJlIHJlZnV0ZWQuCgoyLiBGcm9tIHRoZSBgYGBjYXJzYGBgIGRhdGFzZXQsIHdlIHdpbGwgY29tcHV0ZSB0aGUgU3BlYXJtYW4gcmFuayBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBiZXR3ZWVuIGEgY2FyJ3Mgc3BlZWQgYW5kIGl0cyBkaXN0YW5jZS4KYGBge3J9CmNhcnMKYGBgCmBgYHtyfQojIFNwZWFybWFuIHJhbmsgY29ycmVsYXRpb24gY29lZmZpY2llbnQKY29yKGNhcnMkc3BlZWQsY2FycyRkaXN0LG1ldGhvZD0icyIpCmBgYAoKYGBge3J9CiMgSHlwb3RoZXNpcyB0ZXN0aW5nCmNvci50ZXN0KGNhcnMkc3BlZWQsCiAgICAgICAgIGNhcnMkZGlzdCwKICAgICAgICAgbWV0aG9kID0gInMiLCAKICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidCIsCiAgICAgICAgICkKYGBgCk5vdGUgdGhhdCBjYWxjdWxhdGluZyB0aGUgU3BlYXJtYW4gcmFuayBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCB1c2luZyB0aGUgYGBgY29yYGBgIG1pZ2h0IGJlIGZhdWx0eSBzaW5jZSBpdCBkb2VzIG5vdCBoYW5kbGUgd2l0aCB0aWVzIGluIHRoZSBkYXRhIHZlcnkgd2VsbC4KCiMjIFdpbGNveG9uIFNpZ25lZCBSYW5rIFRlc3QKClRvIHBlcmZvcm0gdGhlIFdpbGNveG9uIFNpZ25lZCBSYW5rIFRlc3QgZm9yIHRlc3RpbmcgdGhlIG1lZGlhbiBmb3IgYSBzaW5nbGUgc2FtcGxlLCBvciB0aGUgbWVkaWFuIG9mIGRpZmZlcmVuY2UgaW4gdHdvLXBhaXJlZCBzYW1wbGVzLCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGBgYHdpbGNveC50ZXN0YGBgIGZyb20gdGhlIGBgYHN0YXRzYGBgIHBhY2thZ2UuCgpUaGUgc3ludGF4IGlzIGFzIGZvbGxvd3MgOiAKCmBgYAp3aWxjb3gudGVzdCh4LAogICAgICAgICAgICB5ID0gTlVMTCwKICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSBjKCJ0d28uc2lkZWQiLCJsZXNzIiwiZ3JlYXRlciIpLAogICAgICAgICAgICBtdSA9IDAsCiAgICAgICAgICAgIHBhaXJlZCA9IEZBTFNFLAogICAgICAgICAgICBleGFjdCA9IE5VTEwsCiAgICAgICAgICAgIGNvbmYuaW50ID0gRkFMU0UsCiAgICAgICAgICAgIGNvbmYubGV2ZWwgPSAwLjk1KQpgYGAKClRoZSBpbnB1dCBhcmd1bWVudHMgYXJlIDogCgoqICoqKngqKiogOiBUaGUgc2FtcGxlIHRvIGJlIHRlc3RlZAoqICoqKnkqKiogOiBBbm90aGVyIHNhbXBsZSB0byBiZSB0ZXN0ZWQsIGlmIHBlcmZvcm1pbmcgYSBwYWlyZWQgd2lsY294b24gdGVzdAoqICoqKm11KioqIDogaHlwb3RoZXNpemVkIG1lZGlhbiBvZiB0aGUgc2FtcGxlICh0aGUgZGVmYXVsdCB2YWx1ZSBpcyAwKQoqICoqKnBhaXJlZCoqKiA6IHdoZXRoZXIgb3Igbm90IHRoZSB0ZXN0IGlzIGEgcGFpcmVkIHdpbGNveG9uIHRlc3Qgb3Igbm90ICh0aGUgZGVmYXVsdCBpcyBGQUxTRSksIGlmIHdlIHdhbnQgdG8gY29uZHVjdCBhIHBhaXJlZCB3aWxjb3hvbiBzaWduZWQgdGVzdCwgdGhpcyBuZWVkcyB0byBiZSBjaGFuZ2VkIHRvIFRSVUUuCiogKioqZXhhY3QqKiogOiB3aGV0aGVyIG9yIG5vdCB0aGUgZXhhY3QgcC12YWx1ZSBmb3IgdGhlIHN0YXRpc3RpYyBpcyBjYWxjdWxhdGVkLiAoVGhlIGRlZmF1bHQgdmFsdWUgaXMgTlVMTCwgdGhhdCBpcywgaWYgdGhlIHNpemUgb2YgdGhlIHNhbXBsZSBpcyA8IDUwLCBhbmQgbm8gdGllcyBhcmUgcHJlc2VudCwgdGhlbiB0aGUgZXhhY3QgcC12YWx1ZSBpcyBjb21wdXRlZCwgZWxzZSwgYSBub3JtYWwgYXBwcm94aW1hdGlvbiB3aWxsIGJlIHVzZWQpCiogKioqY29uZi5pbnQqKiogOiB3aGV0aGVyIG9yIG5vdCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgd2lsbCBiZSBzaG93biBpbiB0aGUgb3V0cHV0CiogKioqY29uZi5sZXZlbCoqKiA6IHRoZSBjb25maWRlbmNlIGxldmVsIGZvciB3aGljaCB0aGUgY29uZi4gaW50ZXJ2YWwgaXMgY29tcHV0ZWQsIGlmIGNvbXB1dGVkLgoKIyMjIEV4YW1wbGVzCjEuIFtTaW5nbGUgc2FtcGxlIG1lZGlhbiB0ZXN0aW5nXQoKICAgIFdlIHdpbGwgdGVzdCB1c2luZyB0aGUgdGF4aSBkYXRhLCB0aGUgc2FtZSBoeXBvdGhlc2lzIGFzIGluIHRoZSBzaWduIHRlc3QsIGJ1dCB1c2luZyBXaWxjb3hvbiBTaWduZWQgUmFuayBUZXN0LgogICAgYS4gdGhlIG1lZGlhbiBnYXMgY29uc3VtcHRpb24gb2YgdGF4aXMgd2l0aCByYWRpYWwgdGlyZXMgaXMgZXF1YWwgdG8gNC41IEttL0wgYWdhaW5zdCB0aGUgaHlwb3RoZXNpcyB0aGF0IGl0IGlzIGdyZWF0ZXIgdGhhbiA0LjUgS20vTAogIAogICAgYi4gdGhlIG1lZGlhbiBnYXMgY29uc3VtcHRpb24gb2YgdGF4aXMgd2l0aCBiZWx0ZWQgdGlyZXMgaXMgZXF1YWwgdG8gNSBLbS9MIGFnYWluc3QgdGhlIGh5cG90aGVzaXMgdGhhdCBpdCBpcyBub3QgZXF1YWwgdG8gNSBLbS9MCgogICAgCmBgYHtyfQojIDFhLiBNZWRpYW4gb2YgdGF4aXMgd2l0aCByYWRpYWwgdGlyZXMKdzFhID0gd2lsY294LnRlc3QodGF4aV9kYXRhJHJhZGlhbF90aXJlcywKICAgICAgICAgICAgbXUgPSA0LjUsCiAgICAgICAgICAgIGFsdGVybmF0aXZlID0gImciLAogICAgICAgICAgICBleGFjdCA9IFQsICAjIENhbGN1bGF0ZSB0aGUgcC12YWx1ZSB1c2luZyB0aGUgZXhhY3QgZGlzdHJpYnV0aW9uIGlmIHBvc3NpYmxlCiAgICAgICAgICAgICkKdzFhCmBgYAogICAgVGhlIHRlc3QgZ2l2ZXMgdGhlIFdpbGNveG9uIHN0YXRpc3RpYywgZ2l2ZW4gYXMgYGBgVmBgYCBpbiB0aGUgb3V0cHV0LCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgcC12YWx1ZSBmb3IgdGhlIHZhbHVlIG9mIHRoZSBzdGF0aXN0aWMuIE5vdGUgdGhhdCB0aGUgZnVuY3Rpb24gYXBwcm94aW1hdGVzIHRoZSBjb21wdXRhdGlvbiBvZiB0aGUgcC12YWx1ZSBzaW5jZSB0aGVyZSBhcmUgdGllcyBpbiB0aGUgZGF0YS4gQWxzbyBub3RlIHRoYXQgdGhlIFdpbGNveG9uIHN0YXRpc3RpYyB0aGF0IGlzIGNvbXB1dGVkIChgYGBWYGBgKSBpcyB0aGUgc3VtIG9mIHJhbmtzIG9mIG9ic2VydmF0aW9uIHcvIHBvc2l0aXZlIGRpZmZlcmVuY2VzIHdpdGggcmVzcGVjdCB0byB0aGUgaHlwb3RoZXNpemVkIG1lZGlhbi4gVGh1cywgaWYgd2UgbWlnaHQgd2FudCB0byBjb25zdWx0IHdpdGggYSBXaWxjb3hvbiBzdGF0aXN0aWMgdGFibGUsIHdlIG1pZ2h0IGhhdmUgdG8gY2FsY3VsYXRlIHRoZSBXaWxjb3hvbiBzdGF0aXN0aWMgY29ycmVzcG9uZGluZyB0byBuZWdhdGl2ZSBkaWZmZXJlbmNlcywgd2hpY2ggd2UgY2FuIGNvbXB1dGUgbWFudWFsbHkgYXMgZm9sbG93cwpgYGB7cn0KeCA9IHRheGlfZGF0YSRyYWRpYWxfdGlyZXMKbiA9IGxlbmd0aCh4W3ggIT0gNC41XSkgICMgU2FtcGxlIHNpemUgb2YgZGF0YSBvbWl0dGluZyB0aGUgb2JzZXJ2YXRpb24gdGhhdCBpcyBlcXVhbCB0byB0aGUgaHlwb3RoZXNpemVkIG1lZGlhbgp3X25lZyA9IG4qKG4rMSkvMiAtIHcxYSRzdGF0aXN0aWMgICAjIFRoZSBzdW0gb2YgcmFua3Mgb2Ygb2JzZXJ2YXRpb24gdy8gbmVnYXRpdmUgZGlmZmVyZW5jZXMKd19uZWcKYGBgCgpgYGB7cn0KIyAxYi4gTWVkaWFuIG9mIHRoZSB0YXhpIHdpdGggYmVsdGVkIHRpcmVzCncxYiA9IHdpbGNveC50ZXN0KHRheGlfZGF0YSRiZWx0ZWRfdGlyZXMsCiAgICAgICAgICAgICAgICAgIG11ID0gNSwKICAgICAgICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidCIsCiAgICAgICAgICAgICAgICAgIGV4YWN0ID0gVCkKdzFiCmBgYAogIFdlIHNlZSB0aGF0IHRoZSBwLXZhbHVlIGZvciB0aGlzIHRlc3Qgc3RhdGlzdGljIGlzIChhcHByb3hpbWF0ZWx5KSAkMC4wMjc4NSA8IDAuMDUkLCB0aHVzIGF0ICQwLjA1JCBsZXZlbCBvZiBzaWduaWZpY2FuY2UsIHdlIGNhbiBjb25jbHVkZSB0aGF0IHRoZSBtZWRpYW4gZ2FzIGNvbnN1bXB0aW9uIG9mIHRheGlzIHdpdGggYmVsdGVkIHRpcmVzIGlzIG5vdCBlcXVhbCB0byA1IEttL0wuIFdlIGNhbiBhbHNvIGNyb3NzY2hlY2sgdGhpcyByZXN1bHQgYnkgY29tcGFyaW5nIHRoZSB0ZXN0IHN0YXRpc3RpYyB3aXRoIHRoZSBjcml0aWNhbCB2YWx1ZSBmcm9tIGEgV2lsY294b24gc2lnbmVkLXJhbmsgc3RhdGlzdGljIHRhYmxlLgpgYGB7cn0KIyBDYWxjdWxhdGluZyB0aGUgbmVnYXRpdmUgZGlmZmVyZW5jZSBXaWxjb3hvbiBzdGF0aXN0aWMKeSA9IHRheGlfZGF0YSRiZWx0ZWRfdGlyZXMKbnkgPSBsZW5ndGgoeVt5IT01XSkgICMgU2FtcGxlIHNpemUgb2YgZGF0YSB3aXRob3V0IHRoZSBvYnNlcnZhdGlvbiBlcXVhbGxpbmcgdGhlIGh5cG90aGVzaXplZCBtZWRpYW4Kd3lfbmVnID0gbnkqKG55KzEpLzIgLSB3MWIkc3RhdGlzdGljICMgVGhlIG5lZ2F0aXZlIGRpZmZlcmVuY2UgV2lsY294b24gc3RhdGlzdGljCnd5X25lZwpgYGAKICAKMi4gW1BhaXJlZC1zYW1wbGUgdGVzdHNdCgogICAgQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBkYXRhIHJlZ2FyZGluZyBqb2dnZXJzJyBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSBiZWZvcmUgYW5kIGFmdGVyIGFuIDgta2lsb21ldGVyIHJ1bi4gV2Ugd2lsbCB0ZXN0IHRoZSBoeXBvdGhlc2lzIHRoYXQgdGhlIG1lZGlhbiBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSBpbmNyZWFzZXMgYnkgOCBwb2ludHMgYWdhaW5zdCB0aGUgYWx0ZXJuYXRpdmUgdGhhdCB0aGUgaW5jcmVhc2UgaXMgbGVzcyB0aGFuIDggcG9pbnRzCmBgYHtyfQpqb2dnZXJfZGF0YQpgYGAKYGBge3J9CiMgSHlwb3RoZXNpcyB0ZXN0CndpbGNveC50ZXN0KGpvZ2dlcl9kYXRhJEFmdGVyLAogICAgICAgICAgICBqb2dnZXJfZGF0YSRCZWZvcmUsCiAgICAgICAgICAgIG11ID0gOCwgIyBIeXBvdGhlc2l6ZWQgbWVkaWFuCiAgICAgICAgICAgIHBhaXJlZCA9IFQsICMgQmVjYXVzZSB3ZSBhcmUgZGVhbGluZyB3aXRoIHBhaXJlZCBkYXRhCiAgICAgICAgICAgIGFsdGVybmF0aXZlID0gImwiCiAgICAgICAgICAgICkKYGBgCiAgU2luY2UgdGhlIHAtdmFsdWUgaXMgJDAuMDE3NjUgPCAwLjA1JCwgd2UgY29uY2x1ZGUgdGhhdCBhdCAkMC4wNSQgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlLCB0aGF0IHRoZSBpbmNyZWFzZSBpbiBtZWRpYW4gc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUgaXMgbGVzcyB0aGFuIDggcG9pbnRzLgogIAojIyBXaWxjb3hvbiBSYW5rLVN1bSBUZXN0IC8gTWFubi1XaGl0bmV5IFUgVGVzdApUbyBwZXJmb3JtIHRoZSBXaWxjb3hvbiByYW5rIHN1bSB0ZXN0IG9uIHR3byBzYW1wbGUgKHVucGFpcmVkKSBkYXRhLCB3ZSBjYW4gdXNlIHRoZSBgYGB3aWxjb3gudGVzdGBgYCBmdW5jdGlvbiBhcyBiZWZvcmUsIG9yIHVzZSBvdGhlciBmdW5jdGlvbnMgc3VjaCBhcyBgYGB3aWxjb3hfdGVzdGBgYCBmcm9tIHRoZSBgYGBjb2luYGBgIHBhY2thZ2UuICoqKlRoZSBvbmx5IGRpZmZlcmVuY2UgaXMgdGhlIHZhbHVlIG9mIHRoZSBzdGF0aXN0aWMgdGhhdCBpcyBwcmVzZW50ZWQsIHVzaW5nIGBgYHdpbGNveC50ZXN0YGBgIHdpbGwgcHJvZHVjZSB0aGUgVSBzdGF0aXN0aWMsIGFuZCB1c2luZyBgYGB3aWxjb3hfdGVzdGBgYCwgd2UgY2FuIGdldCB0aGUgVyBzdGF0aXN0aWMuCgpgYGB7cn0KbGlicmFyeShjb2luKSAjIExvYWQgdGhlIGNvaW4gcGFja2FnZSAoaW5zdGFsbCBpdCBmaXJzdCkKYGBgCgoKRm9yIHVzaW5nIHRoZSBgYGB3aWxjb3gudGVzdGBgYCBmdW5jdGlvbiwgdGhlIHN5bnRheCBpcyA6IAoKYGBgCndpbGNveC50ZXN0KHgsCiAgICAgICAgICAgIHksCiAgICAgICAgICAgIG11ID0gMCwKICAgICAgICAgICAgcGFpcmVkID0gRkFMU0UsCiAgICAgICAgICAgIGFsdGVybmF0aXZlID0gYygidHdvLnNpZGVkIiwibGVzcyIsImdyZWF0ZXIiKSwKICAgICAgICAgICAgZXhhY3QgPSBOVUxMLAogICAgICAgICAgICBjb3JyZWN0ID0gVFJVRSwKICAgICAgICAgICAgLi4uKQpgYGAKClRoZSBpbnB1dCBhcmd1bWVudHMgYXJlIHRoZSBzYW1lIGFzIGJlZm9yZSwgYWx0aG91Z2ggdG8gY29uZHVjdCBhIFdpbGNveG9uIFJhbmstU3VtIHRlc3QsIG1ha2Ugc3VyZSB0aGF0IGBgYHBhaXJlZGBgYCBpcyBzZXQgdG8gYGBgRkFMU0VgYGAgKHdoaWNoIGlzIHRoZSBkZWZhdWx0KS4KClRoaXMgd2lsbCB0ZXN0IHRoZSBudWxsIGh5cG90aGVzaXMgb2YgClxbCkhfezB9IDogXHdpZGV0aWxkZXtcbXV9X3ggLSBcd2lkZXRpbGRle1xtdX1feSA9IDAKXF0KYWdhaW5zdCB0aGUgY29ycmVzcG9uZGluZyBhbHRlcm5hdGl2ZS4KCkZvciB0aGUgYGBgd2lsY294X3Rlc3RgYGAgZnVuY3Rpb24sIHRoZSBzeW50YXggaXMgOiAKCmBgYAp3aWxjb3hfdGVzdChmb3JtdWxhLAogICAgICAgICAgICBkYXRhLAogICAgICAgICAgICBzdWJzZXQgPSBOVUxMLAogICAgICAgICAgICB0aWVzLm1ldGhvZCA9IGMoIm1pZC1yYW5rcyIsImF2ZXJhZ2Utc2NvcmVzIiksCiAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9IGMoImFzeW1wdG90aWMiLCJhcHByb3hpbWF0ZSIsImV4YWN0IiksCiAgICAgICAgICAgIC4uLikKYGBgCgpUaGUgaW5wdXQgYXJndW1lbnRzIGFyZSA6IAoKKiBmb3JtdWxhIDogYSBmb3JtdWxhIG9mIHRoZSBmb3JtIHggfiBnLCB3aGVyZSB4IGlzIHRoZSBudW1lcmljIHZlY3RvciwgYW5kIGcgaXMgYSBmYWN0b3IgZGVub3RpbmcgdGhlIGdyb3VwcyBmcm9tIHdoaWNoIHdlIHdhbnQgdG8gdGVzdAoqIGRhdGEgOiAob3B0aW9uYWwpIGRhdGEgZnJhbWUgZnJvbSB3aGljaCB0aGUgZGF0YSAoeCBhbmQgZykgYXJlIHRha2VuCiogc3Vic2V0IDogKG9wdGlvbmFsKSB2ZWN0b3Igc3BlY2lmeWluZyB0aGUgc3Vic2V0IG9mIG9ic2VydmF0aW9ucyB0byBiZSB1c2VkICh0aGUgZGVmYXVsdCBpcyBOVUxMKQoqIHRpZXNfbWV0aG9kIDogaG93IHRvIHJhbmsgdGllcyBpbiB0aGUgZGF0YSAodGhlIGRlZmF1bHQgaXMgbWlkLXJhbmtzKS4KKiBvdGhlciBpbnB1dHMgKHdlaWdodHMsIGFsdGVybmF0aXZlLCBjb25mLmludCwgZXRjKSwgbW9yZSBvbiB0aGlzIG9uIHRoZSBkb2N1bWVudGF0aW9uIGZvciB0aGUgZnVuY3Rpb24uCgpUaGVzZSBpbnB1dCBhcmd1bWVudHMgY2FuIGFsc28gYmUgdXNlZCBmb3IgdGhlIGBgYHdpbGNveC50ZXN0YGBgIGZ1bmN0aW9uLgoKTm90ZSB0aGF0IGZvciB1c2luZyB0aGlzIGZ1bmN0aW9uLCB0aGUgZGF0YSBuZWVkcyB0byBiZSBpbiBhIGNlcnRhaW4gc2hhcGUsIHRoYXQgaXMgdGhlcmUgaXMgYSBjb2x1bW4gb2YgbnVtZXJpYyBkYXRhLCBhbmQgYSBjb2x1bW4gb2YgZ3JvdXBzLCBzcGVjaWZ5aW5nIGZyb20gd2hpY2ggZ3JvdXAvbGV2ZWwgdGhlIHBhcnRpY3VsYXIgZGF0YSBwb2ludCBpcyBmcm9tLgpgYGB7ciBlY2hvPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKSAjIFRoZSBsaWJyYXJ5IG5lZWRlZCB0byByZXNoYXBlIHRoZSBkYXRhCgpEMSA9IG5hLm9taXQoc3RhY2soY2FyX3JhdGluZ19kYXRhWywxOjJdKSkgIyBTdGFja3MgdGhlIG51bWVyaWNhbCB2YWx1ZSBmcm9tIHRoZSBvcmlnaW5hbCBkYXRhZnJhbWUgYW5kIG9taXRzIGFueSBOQSBkYXRhIHBvaW50Cgpjb2xuYW1lcyhEMSkgPSBjKCJ2YWx1ZXMiLCJncm91cHMiKSAgICMgR2l2ZXMgbmFtZXMgZm9yIHRoZSBjb2x1bW5zIG9mIHRoZSBuZXcgZGF0YWZyYW1lCgojIFRoZSByZXN1bHQgaXMKRDEKYGBgClNvLCBpbiBvcmRlciB0byBkbyB0ZXN0cyB3aXRoIG91ciBkYXRhLCB3ZSBuZWVkIHRvIHJlc2hhcGUgdGhlIGRhdGEgYXMgZm9sbG93cy4KCmBgYHtyfQojIFN1cHBvc2UgdGhlIG9yaWdpbmFsIGRhdGEgaXMgCmNhcl9yYXRpbmdfZGF0YVssMToyXQpgYGAKVG8gY2hhbmdlIHRoZSBzaGFwZSBvZiB0aGUgZGF0YSwgd2UgZG8gdGhlIGZvbGxvd2luZyBwcm9jZWR1cmUKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKSAjIFRoZSBsaWJyYXJ5IG5lZWRlZCB0byByZXNoYXBlIHRoZSBkYXRhCgpEMSA9IG5hLm9taXQoc3RhY2soY2FyX3JhdGluZ19kYXRhWywxOjJdKSkgIyBTdGFja3MgdGhlIG51bWVyaWNhbCB2YWx1ZSBmcm9tIHRoZSBvcmlnaW5hbCBkYXRhZnJhbWUgYW5kIG9taXRzIGFueSBOQSBkYXRhIHBvaW50Cgpjb2xuYW1lcyhEMSkgPSBjKCJ2YWx1ZXMiLCJncm91cHMiKSAgICMgR2l2ZXMgbmFtZXMgZm9yIHRoZSBjb2x1bW5zIG9mIHRoZSBuZXcgZGF0YWZyYW1lCgojIFRoZSByZXN1bHQgaXMKRDEKYGBgCk5vdyB3ZSBjYW4gYXBwbHkgdGhlIGBgYHdpbGNveC50ZXN0YGBgIGFuZCBgYGB3aWxjb3hfdGVzdGBgYCBmdW5jdGlvbiBvbiB0aGUgZGF0YS4KCiMjIyBFeGFtcGxlCjEuIFVzaW5nIHRoZSBkYXRhIG1lbnRpb25lZCBhYm92ZSwgd2Ugd2lsbCBkbyBhIGh5cG90aGVzaXMgdGVzdCB0byB0ZXN0IGlmIHRoZXJlIGlzIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzLgoKYGBge3J9CiMgVXNpbmcgd2lsY294LnRlc3QKd2lsY294LnRlc3QodmFsdWVzfmdyb3VwcywgIyBUaGUgZm9ybXVsYQogICAgICAgICAgICBkYXRhID0gRDEsICMgVGhlIGRhdGFmcmFtZSB0byByZWZlcmVuY2UgdGhlIGRhdGEKICAgICAgICAgICAgYWx0ZXJuYXRpdmUgPSAidCIKICAgICAgICAgICAgKQoKIyBUaGUgbnVsbCBoeXBvdGhlc2lzIGlzIGRpZmZlcmVuY2Ugb2YgbWVkaWFuIG9mIGdyb3VwIEEgLSBtZWRpYW4gb2YgZ3JvdXAgQiBpcyAwCmBgYApGcm9tIHRoZSBvdXRwdXQsIHdlIGNhbiBnYXRoZXIgdGhhdCB0aGUgTWFubi1XaGl0bmV5IFUgc3RhdGlzdGljIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGJhc2UgbGV2ZWwgZ3JvdXAgKEEpIGlzIGBgYFcgPSAxLjVgYGAgYW5kIHRoZSAoYXBwcm94aW1hdGUpIHAtdmFsdWUgZm9yIHRoaXMgc3RhdGlzdGljIHZhbHVlIGlzICQwLjAwODM2NyA8IDAuMDUkLiBTbywgd2UgY29uY2x1ZGUgdGhhdCBhdCAkMC4wNSQgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlLCB0aGVyZSBpcyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYWNyb3NzIHRoZSB0d28gZ3JvdXBzLgoKYGBge3J9CiMgVXNpbmcgd2lsY294X3Rlc3QKd3JzID0gd2lsY294X3Rlc3QodmFsdWVzfmdyb3VwcywgIyBUaGUgZm9ybXVsYQogICAgICAgICAgICBkYXRhID0gRDEsICMgVGhlIGRhdGFmcmFtZQogICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0IiwKICAgICAgICAgICAgZGlzdHJpYnV0aW9uID0gImUiLCAjIENhbGN1bGF0ZSB0aGUgcC12YWx1ZSB1c2luZyB0aGUgZXhhY3QgZGlzdHJpYnV0aW9uCiAgICAgICAgICAgIHRpZXMubWV0aG9kID0gImEiICMgVXNpbmcgYXZlcmFnZSBzY29yZXMgZm9yIHRpZXMKICAgICAgICAgICAgKQp3cnMKYGBgCldlIGNhbiBzZWUgdGhhdCB0aGUgcC12YWx1ZSBpcyBkaWZmZXJlbnQgdGhhbiB0aGUgb25lIHRoYXQgaXMgcHJvZHVjZWQgYnkgdGhlIGBgYHdpbGNveC50ZXN0YGBgIGZ1bmN0aW9uLCB0aGlzIGlzIGJlY2F1c2UgdGhlIGNhbGN1bGF0aW9uIGZvciB0aGUgcC12YWx1ZXMgbWlnaHQgZGlmZmVyIChmb3IgbW9yZSBpbmZvcm1hdGlvbiwgcmVmZXIgdG8gdGhlIGRvY3VtZW50YXRpb24pLiBBbHRob3VnaCwgYXQgJDAuMDUkIGxldmVsIG9mIHNpZ25pZmljYW5jZSwgdGhlIG51bGwgaHlwb3RoZXNpcyBpcyBzdGlsbCByZWplY3RlZC4gTm90ZSBhbHNvIHRoYXQgdGhlIHN0YXRpc3RpYyB0aGF0IGlzIHByZXNlbnRlZCBvbiB0aGUgb3V0cHV0IG9mIHRoZSBgYGB3aWxjb3hfdGVzdGBgYCBpcyB0aGUgIm5vcm1hbGl6ZWQiIHN0YXRpc3RpYywgd2UgY2FuIGdldCB0aGUgV2lsY294b24gcmFuayBzdW0gc3RhdGlzdGljIGZyb20gdGhpcyB0ZXN0IGJ5IGNhbGxpbmcgaXQgc3BlY2lmaWNhbGx5LCBmb3IgZXhhbXBsZQpgYGB7cn0KIyBUaGUgd2lsY294b24gcmFuay1zdW0gc3RhdGlzdGljCndfc3RhdGlzdGljID0gc3RhdGlzdGljKHdycywibGluZWFyIikgIyBUaGUgd2lsY294b24gcmFuayBzdW0gc3RhdGlzdGljCndfc3RhdGlzdGljCmBgYApOb3RlIHRoYXQgdGhlIHN0YXRpc3RpYyBnaXZlbiBpcyB0aGUgc3RhdGlzdGljIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGJhc2UgbGV2ZWwgb2YgdGhlIGdyb3VwcyAodGhhdCBpcyBBKSwgdG8gZ2V0IHRoZSBzdGF0aXN0aWMgY29ycmVzcG9uZGluZyB0byB0aGUgb3RoZXIgZ3JvdXAsIHdlIGNhbiByZWxldmVsIHRoZSBncm91cC9mYWN0b3IgdXNpbmcgdGhlIGZ1bmN0aW9uIGBgYHJlbGV2ZWxgYGAgZnJvbSB0aGUgYGBgc3RhdHNgYGAgcGFja2FnZQpgYGB7cn0KIyBSZWxldmVsIHRoZSBmYWN0b3JzCkQxJGdyb3VwcyA9IHJlbGV2ZWwoRDEkZ3JvdXBzLCByZWYgPSAiQiIpICMgQ2hhbmdlcyB0aGUgYmFzZSBsZXZlbCBvZiB0aGUgZ3JvdXBzIHRvICJCIgpgYGAKCmBgYHtyfQojIHdpbGNveC50ZXN0CndpbGNveC50ZXN0KHZhbHVlc35ncm91cHMsZGF0YSA9IEQxLCBhbHRlcm5hdGl2ZSA9ICJ0IikKYGBgCgpgYGB7cn0KIyB3aWxjb3hfdGVzdAp3cnNfYiA9IHdpbGNveF90ZXN0KHZhbHVlc35ncm91cHMsZGF0YSA9IEQxLCBkaXN0cmlidXRpb24gPSAiZSIsIHRpZXMubWV0aG9kID0gImEiKQp3X3N0YXRpc3RpY19iID0gc3RhdGlzdGljKHdyc19iLCJsaW5lYXIiKQp3X3N0YXRpc3RpY19iCmBgYAoKCjIuIENvbnNpZGVyIHRoZSBmb2xsb3dpbmcgZGF0YSByZWdhcmRpbmcgdGhlIG5pY290aW5lIGNvbnRlbnQgaW4gdHdvIGJyYW5kcyBvZiBjaWdhcmV0dGVzIChicmFuZCBBIGFuZCBicmFuZCBCKS4gCmBgYHtyfQpjaWdhcmV0dGVfZGF0YQpgYGAKV2Ugd2lsbCB0ZXN0IHdoZXRoZXIgdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGluIG1lZGlhbiBuaWNvdGluZSBjb250ZW50IGJldHdlZW4gdGhlIDIgYnJhbmRzIG9mIGNpZ2FyZXR0ZXMuCmBgYHtyfQojIFJlc2hhcGluZyB0aGUgZGF0YQpjaWdfZGF0YSA9IG5hLm9taXQoc3RhY2soY2lnYXJldHRlX2RhdGEpKSAKY29sbmFtZXMoY2lnX2RhdGEpID0gYygibmljb3RpbmVfY29udGVudCIsImJyYW5kIikgIyBSZW5hbWluZyB0aGUgY29sdW1ucwpjaWdfZGF0YQoKIyBIeXBvdGhlc2lzIHRlc3QKd2lsY294X3Rlc3Qobmljb3RpbmVfY29udGVudH5icmFuZCwKICAgICAgICAgICAgZGF0YSA9IGNpZ19kYXRhLAogICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJ0IiwgIyBBbHRlcm5hdGl2ZSBoeXBvdGhlc2lzCiAgICAgICAgICAgIGRpc3RyaWJ1dGlvbiA9ICJlIiwgIyBVc2UgdGhlIGV4YWN0IGRpc3RyaWJ1dGlvbgogICAgICAgICAgICB0aWVzX21ldGhvZCA9ICJhIiAjIFVzZSB0aGUgYXZlcmFnZSByYW5raW5nIGZvciB0aWVzCiAgICAgICAgICAgICkKYGBgClNpbmNlIHRoZSBwLXZhbHVlIGlzICQwLjEzOTEgPiAwLjA1JCwgd2UgY29uY2x1ZGUgdGhhdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgdGhlcmUgaXMgbm8gc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBtZWRpYW4gbmljb3RpbmUgY29udGVudCBiZXR3ZWVuIHRoZSAyIGJyYW5kcyBvZiBjaWdhcmV0dGVzIGlzIG5vdCByZWplY3RlZCBhdCAkMC4wNSQgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlLiAKCiMgS3J1c2thbGwtV2FsbGlzIFRlc3QKVG8gcGVyZm9ybSB0aGUgS3J1c2thbGwtV2FsbGlzIFRlc3QsIHdlIG1ha2UgdXNlIG9mIHRoZSBmdW5jdGlvbiBgYGBrcnVza2FsLnRlc3RgYGAgZnJvbSB0aGUgYGBgc3RhdHNgYGAgcGFja2FnZS4KClRoZSBzeW50YXggaXMgYXMgZm9sbG93cyA6IApgYGAKa3J1c2thbC50ZXN0KHgsZykKYGBgCm9yIAoKYGBgCmtydXNrYWwudGVzdChmb3JtdWxhLAogICAgICAgICAgICAgZGF0YSkKYGBgCgpXaGVyZSB0aGUgaW5wdXQgYXJndW1lbnRzIGFyZSA6CgoqIHggOiBudW1lcmljIHZlY3RvciBvZiBkYXRhIHZhbHVlcwoqIGcgOiBmYWN0b3Igb2JqZWN0IHNwZWNpZnlpbmcgdGhlIGdyb3VwIG9mIGVhY2ggZWxlbWVudHMgb2YgeAoKb3IgCgoqIGZvcm11bGEgOiBhIGZvcm11bGEgb2YgdGhlIGZvcm0geCB+IGcsIHdoZXJlIHggaXMgYSBudW1lcmljIHZlY3RvciBvZiBkYXRhIHZhbHVlcyBhbmQgZyBpcyBhIGZhY3RvciBvYmplY3Qgc3BlY2lmeWluZyB0aGUgZ3JvdXBzIG9mIGVsZW1lbnRzIG9mIHgKKiBkYXRhIDogKG9wdGlvbmFsKSBkYXRhZnJhbWUgZnJvbSB3aGljaCB0byByZWZlcmVuY2UgeCBhbmQgZwoKIyMjIEV4YW1wbGUKMS4gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBkYXRhIHJlZ2FyZGluZyB0aGUgcmF0aW5ncyBnaXZlbiBieSA2IGV4cGVydHMgb24gMyBkaWZmZXJlbnQgY2FycyAoQSwgQiwgQykKYGBge3J9CmNhcl9yYXRpbmdfZGF0YQpgYGAKV2Ugd2lsbCB0ZXN0IGlmIHRoZXJlIGFyZSBhbnkgZGlmZmVyZW5jZXMgaW4gdGhlIG1lZGlhbiByYXRpbmcgZ2l2ZW4gYnkgdGhlIGV4cGVydHMgZm9yIHRoZSAzIGJyYW5kcyBvZiBjYXJzLgpgYGB7cn0KIyBSZXNoYXBlIHRoZSBkYXRhCmNhcl9kYXRhID0gbmEub21pdChzdGFjayhjYXJfcmF0aW5nX2RhdGEpKQpjb2xuYW1lcyhjYXJfZGF0YSkgPSBjKCJyYXRpbmciLCJicmFuZCIpICMgUmVuYW1pbmcgdGhlIGNvbHVtbnMgb2YgdGhlIHJlc2hhcGVkIGRhdGEKY2FyX2RhdGEKYGBgCmBgYHtyfQojIEh5cG90aGVzaXMgdGVzdGluZwprcnVza2FsLnRlc3QocmF0aW5nfmJyYW5kLAogICAgICAgICAgICAgZGF0YSA9IGNhcl9kYXRhKQpgYGAKV2UgY2FuIHNlZSB0aGF0IHRoZSAoYXBwcm94aW1hdGUpIGNoaS1zcXVhcmVkIHN0YXRpc3RpYyBpcyA5LjMzMjYgYW5kIHRoZSBjb3JyZXNwb25kaW5nIHAtdmFsdWUgaXMgJDAuMDA5NDA3IDwgMC4wNSQuIFNvIHdlIGNvbmNsdWRlIHRoYXQgYXQgJDAuMDUkIGxldmVsIG9mIHNpZ25pZmljYW5jZSwgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0IHRoZXJlIGFyZSBubyBkaWZmZXJlbmNlcyBpbiB0aGUgbWVkaWFuIHJhdGluZyBnaXZlbiBieSB0aGUgZXhwZXJ0cyBpcyByZWplY3RlZC4KCkJlZm9yZSBkb2luZyB0aGUgS3J1c2thbC1XYWxsaXMgdGVzdCwgaXQgbWlnaHQgYmUgb2YgaW50ZXJlc3QgdG8gcGxvdCB0aGUgZGF0YSB0byBzZWUgaWYgdmlzdWFsbHksIHRoZXJlIGFyZSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBncm91cHMsIHdlIGNhbiBkbyB0aGlzIGFzIGZvbGxvd3MKYGBge3J9CiMgQm94cGxvdCBvZiByYXRpbmdzIGZvciBlYWNoIGdyb3VwcwpsaWJyYXJ5KGdncHVicikKZ2dib3hwbG90KGRhdGEgPSBjYXJfZGF0YSwKICAgICAgICAgIHggPSAiYnJhbmQiLAogICAgICAgICAgeSA9ICJyYXRpbmciKQpgYGAKCgojIFJ1bnMgVGVzdApUbyBwZXJmb3JtIHRoZSBydW5zIHRlc3QsIHdlIHVzZSB0aGUgZnVuY3Rpb24gYGBgUnVuc1Rlc3RgYGAgZnJvbSB0aGUgYGBgRGVzY1Rvb2xzYGBgIHBhY2thZ2UuIApgYGB7cn0KbGlicmFyeShEZXNjVG9vbHMpICMgTG9hZGluZyB0aGUgcmFuZHRlc3RzIGxpYnJhcnkgKEluc3RhbGwgaXQgZmlyc3QpCmBgYAoKVGhlIHN5bnRheCBpcyBhcyBmb2xsb3dzIDogCmBgYApSdW5zVGVzdCh4LAogICAgICAgICBhbHRlcm5hdGl2ZSA9IGMoInQiLCJsIiwiZyIpLAogICAgICAgICBleGFjdCA9IE5VTEwsCiAgICAgICAgIGNvcnJlY3QgPSBUUlVFLAogICAgICAgICBuYS5ybSA9IEZBTFNFKQpgYGAKClRoZSBpbnB1dCBhcmd1bWVudHMgYXJlIDogCgoqIHggOiBudW1lcmljIHZlY3RvciBvZiBkYXRhIHRvIGJlIHRlc3RlZAoqIGFsdGVybmF0aXZlIDogYWx0ZXJuYXRpdmUgaHlwb3RoZXNpcyAodGhlIGRlZmF1bHQgaXMgInR3by5zaWRlZCIpCiogZXhhY3QgOiB3aGV0aGVyIG9yIG5vdCB0aGUgZXhhY3QgZGlzdHJpYnV0aW9uIGlzIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBwLXZhbHVlICh0aGUgZGVmYXVsdCBpcyBOVUxMLCB0aGF0IGlzLCBpZiBsZW5ndGgoeCkgJFxsZXEgMzAkLCB0aGVuIHRoZSBleGFjdCBkaXN0cmlidXRpb24gd2lsbCBiZSB1c2VkLCBlbHNlLCBhIG5vcm1hbCBhcHByb3hpbWF0aW9uIHdvdWxkIGJlIHVzZWQpLgoqIGNvcnJlY3QgOiB3aGV0aGVyIGNvbnRpbnVpdHkgY29ycmVjdGlvbiBpcyB1c2VkCiogbmEucm0gOiB3aGV0aGVyIE5BcyBpbiB0aGUgZGF0YSBhcmUgcmVtb3ZlZCAodGhlIGRlZmF1bHQgaXMgRkFMU0UpCgpOb3RlIHRoYXQgaWYgeCBpcyBhIG51bWVyaWMgdmVjdG9yIHdpdGggbW9yZSB0aGFuIDIgdmFsdWVzLCBieSBkZWZhdWx0LCBpdCB3aWxsIGJlIHRyYW5zZm9ybWVkIHRvIGJlIGEgZGljaG90b21vdXMgdmVjdG9yIChsb2dpY2FsIHZlY3RvciBvZiBUUlVFIGFuZCBGQUxTRSkgYnkgc3VidHJhY3RpbmcgdGhlIG1lZGlhbiBvZiB0aGUgc2FtcGxlLiBJZiBhbm90aGVyIHRocmVzaG9sZCBpcyB0byBiZSBjaG9zZW4sIHNheSB0aGUgbWVhbiBvZiB0aGUgc2FtcGxlIHdlIGNhbiB1c2UgYGBgUnVuc1Rlc3QoeCA+IG1lYW4oeCkpYGBgCgojIyMgRXhhbXBsZQoxLiBTdXBwb3NlIHdlIHdhbnQgdG8gdGVzdCBpZiB0aGUgc2VxdWVuY2UgOiAKXFsKQSBcIEIgXCBBIFwgQSBcIEIgXCBCIFwgQiBcIEIgXCBBIFwgQiBcIEIgXCBBIFwgQSBcIEEgXCBCIFwgQSBcIEIKXF0KCmlzIHJhbmRvbQoKYGBge3J9CiMgVGhlIGRhdGEgdmVjdG9yCngxID0gYygiQSIsIkIiLCJBIiwiQSIsIkIiLCJCIiwiQiIsIkIiLCJBIiwiQiIsIkIiLCJBIiwiQSIsIkEiLCJCIiwiQSIsIkIiKQoKIyBSdW5zIFRlc3QKUnVuc1Rlc3QoeDEpCmBgYApTaW5jZSB0aGUgcC12YWx1ZSBpcyAkMC44MTg2ID4gMC4wNSQsIHdlIGNvbmNsdWRlIHRoYXQgYXQgJDAuMDUkIGxldmVsIG9mIHNpZ25pZmljYW5jZSwgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0IHRoZSBzZXF1ZW5jZSBpcyByYW5kb20gaXMgbm90IHJlamVjdGVkLgoKMi4gU3VwcG9zZSB0aGF0IGEgcGFpbnQgbWFraW5nIG1hY2hpbmUgZGlzcGVuc2VzIGFuIGFtb3VudCBvZiBwYWludCB0aGlubmVyIGZvciBlYWNoIGJhdGNoIG9mIHBhaW50LCB3ZSB3YW50IHRvIHRlc3QgaWYgdGhlIGFtb3VudCBvZiBwYWludCB0aGlubmVyIGRpc3BlbnNlZCBieSB0aGUgbWFjaGluZSBpcyByYW5kb20uIApgYGB7cn0KIyBEYXRhIHZlY3RvciAoYW1vdW50IG9mIHBhaW50IHRoaW5uZXIpCngyID0gYygzLjYsMy45LDQuMSwzLjYsMy44LDMuNywzLjQsNC4wLDMuOCw0LjEsMy45LDQuMCwzLjgsNC4yLDQuMSkKCiMgUnVucyBUZXN0ICh3aXRoIHRoZSBtZWRpYW4gYXMgdGhlIHRocmVzaG9sZCkKeDJfdGVzdCA9IHgyW3gyICE9IG1lZGlhbih4MildICMgb21pdCBhbnkgZGF0YSBwb2ludCB3LyB2YWx1ZSBlcXVhbCB0byB0aGUgc2FtcGxlIG1lZGlhbgoKUnVuc1Rlc3QoeDJfdGVzdCA+IG1lZGlhbih4MiksZXhhY3QgPSBUKQpgYGAKU2luY2UgdGhlIGNvbXB1dGVkIHAtdmFsdWUgaXMgJDAuNzk2ID4gMC4wNSQsIHdlIGNvbmNsdWRlIHRoYXQgYXQgJDAuMDUkIGxldmVsIG9mIHNpZ25pZmljYW5jZSwgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0IHRoZSBwcm9jZXNzIGlzIHJhbmRvbSBpcyBub3QgcmVqZWN0ZWQuCgojIFRvbGVyYW5jZSBMaW1pdApHaXZlbiBhIHNhbXBsZSBvZiBjb250aW51b3VzIGRhdGEsIHdlIGNhbiBjYWxjdWxhdGUgdGhlIHRvbGVyYW5jZSBpbnRlcnZhbC90b2xlcmFuY2UgbGltaXQgb2YgdGhlIGRhdGEgdXNpbmcgdGhlIGZ1bmN0aW9uIGBgYG5wdG9sLmludGBgYCBmcm9tIHRoZSBgYGB0b2xlcmFuY2VgYGAgcGFja2FnZS4KYGBge3J9CmxpYnJhcnkodG9sZXJhbmNlKSAjIExvYWQgdGhlIHBhY2thZ2UgKGluc3RhbGwgaXQgZmlyc3QpCmBgYAoKVGhlIHN5bnRheCBmb3IgdGhlIGBgYG5wdG9sLmludGBgYCBmdW5jdGlvbiBpcyBhcyBmb2xsb3dzIDoKCmBgYApucHRvbC5pbnQoeCwKICAgICAgICAgIGFscGhhID0gMC4wNSwKICAgICAgICAgIFAgPSAwLjk5LAogICAgICAgICAgc2lkZSA9IDEsCiAgICAgICAgICBtZXRob2QgPSBjKCJXSUxLUyIsIldBTEQiLCJITSIsIllNIiksCiAgICAgICAgICB1cHBlciA9IE5VTEwsCiAgICAgICAgICBsb3dlciA9IE5VTEwpCmBgYApUaGUgaW5wdXQgYXJndW1lbnRzIGFyZQoqIHggOiBudW1lcmljIHZlY3RvciBvZiB0aGUgZGF0YQoqIGFscGhhIDogdGhlIHNpZ25pZmljYW5jZSBsZXZlbCBvZiB0aGUgdG9sZXJhbmNlIGxpbWl0IChkZWZhdWx0IGlzIDAuMDUpCiogUCA6IHRoZSBwcm9wb3J0aW9uIG9mIHRoZSBkYXRhIChkZWZhdWx0IGlzIDAuOTkpCiogc2lkZSA6IHdoZXRoZXIgaXQgd2lsbCBjYWxjdWxhdGUgdGhlIDItc2lkZWQgb3IgMSBzaWRlZCB0b2xlcmFuY2UgbGltaXQKKiBtZXRob2QgOiB0aGUgbWV0aG9kIHRoYXQgd2lsbCBiZSB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgdG9sZXJhbmNlIGxpbWl0CiogdXBwZXIgOiB0aGUgdXBwZXIgYm91bmQgb2YgdGhlIGRhdGEgKGlmIE5VTEwsIHRoZW4gbWF4KHgpIGlzIHVzZWQpCiogbG93ZXIgOiB0aGUgbG93ZXIgYm91bmQgb2YgdGhlIGRhdGEgKGlmIE5VTEwsIHRoZW4gbWluKHgpIGlzIHVzZWQpCgojIyMgRXhhbXBsZQoxLiBTdXBwb3NlIHdlIHdhbnQgdG8gZGV0ZXJtaW5lIHRoZSA5MCUgdXBwZXIgY29uZmlkZW5jZSBib3VuZCBmb3IgdGhlIDc1dGggcGVyY2VudGlsZSBvZiB0aGUgZGF0YSBgYGB4MmBgYCB1c2VkIGFib3ZlLiAKYGBge3J9Cm5wdG9sLmludCh4MiwKICAgICAgICAgIGFscGhhID0gMS0wLjkwLCAjIFRoZSBzaWduaWZpY2FuY2UgbGV2ZWwgaXMgMS05MCUKICAgICAgICAgIFAgPSAwLjc1LCAjIFRoZSBwcm9wb3J0aW9uIG9mIHRoZSBkYXRhIGlzIHRoZSA3NSUKICAgICAgICAgIHNpZGUgPSAxKQpgYGAKV2Ugc2VlIHRoYXQgdGhlIHVwcGVyIHRvbGVyYW5jZSBsaW1pdCBpcyA0LjEKCiMgR29vZG5lc3Mgb2YgRml0IFRlc3QKCiMjIEtvbG1vZ29yb3YtU21pcm5vdiBUZXN0CgpUbyBwZXJmb3JtIHRoZSBLb2xtb2dvcm92LVNtaXJub3YgVGVzdCBvZiBlcXVhbCBkaXN0cmlidXRpb24sIHdlIGNhbiB1c2UgdGhlIGZ1bmN0aW9uIGBgYGtzLnRlc3RgYGAgZnJvbSB0aGUgYGBgc3RhdHNgYGAgcGFja2FnZS4KClRoZSBzeW50YXggZm9yIGBgYGtzLnRlc3RgYGAgaXMgYXMgZm9sbG93cwoKYGBgCmtzLnRlc3QoeCwKICAgICAgICB5LCAuLi4sCiAgICAgICAgYWx0ZXJuYXRpdmUgPSBjKCJ0d28uc2lkZWQiLCJsZXNzIiwiZ3JlYXRlciIpLAogICAgICAgIGV4YWN0ID0gTlVMTAogICAgICAgICkKYGBgCgpUaGUgaW5wdXQgYXJndW1lbnRzIGFyZSA6IAoKKiB4IDogbnVtZXJpYyB2ZWN0b3Igb2YgZGF0YSB0byBiZSB0ZXN0ZWQKKiB5IDogZWl0aGVyIGEgbnVtZXJpYyB2ZWN0b3Igb2YgZGF0YSAoZm9yIGNvbXBhcmluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHggYW5kIHkpIG9yIGEgY2hhcmFjdGVyIHN0cmluZyBuYW1pbmcgYSBjb250aW51b3VzIChmb3IgZXhhbXBsZSA6ICJwbm9ybSIsICJwZXhwIiwgZXRjKQoqIC4uLiA6IHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgeSwgaWYgeSBpcyBhIGNoYXJhY3RlciBzdHJpbmcgZGVzY3JpYmluZyBhIHBhcnRpY3VsYXIgY29udGludW91cyBjZGYKKiBhbHRlcm5hdGl2ZSA6IHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzICh0aGUgZGVmYXVsdCBpcyAidHdvLnNpZGVkIikuIElmICJsZXNzIiwgdGhlbiB0aGUgYWx0ZXJuYXRpdmUgaXMgdGhhdCB0aGUgY2RmIG9mIHggaXMgbGVzcyB0aGFuIHRoZSBjZGYgb2YgeS4gSWYgImdyZWF0ZXIiLCB0aGVuIHRoZSBhbHRlcm5hdGl2ZSBpcyB0aGF0IHRoZSBjZGYgb2YgeCBpcyBncmVhdGVyIHRoYW4gdGhlIGNkZiBvZiB5CiogZXhhY3QgOiB3aGV0aGVyIGFuIGV4YWN0IG9mIGFuIGFwcHJveGltYXRlIHAtdmFsdWUgZm9yIHRoZSB0ZXN0IGlzIGNvbXB1dGVkIChieSBkZWZhdWx0LCBhbiBleGFjdCBwLXZhbHVlIHdvdWxkIGJlIGNhbGN1bGF0ZWQgaWYgdGhlIGNvbmRpdGlvbnMgYXJlIHNhdGlzZmllZCwgbW9yZSBvbiB0aGlzIGNvbmRpdGlvbnMgY2FuIGJlIHJlYWQgb24gdGhlIGRvY3VtZW50YXRpb24pCgpBbiBpbXBvcnRhbnQgdGhpbmcgdG8gbm90ZSBoZXJlIGlzIHRoYXQgaXMgd2Ugd2FudCB0byBjb21wYXJlIHRoZSBjZGYgb2YgeCB0byBhIGtub3duIGNvbnRpbnVvdXMgY2RmIHksIHRoZW4gdGhlIHBhcmFtZXRlcnMgb2YgdGhlIGNkZiBvZiB5IG5lZWQgdG8gYmUga25vd24gYmVmb3JlIGhhbmQuIEluIHRoZSBjYXNlIHRoYXQgdGhlIHBhcmFtZXRlcnMgb2YgdGhlIG51bGwgZGlzdHJpYnV0aW9uIChjZGYgb2YgeSkgaXMgdW5rbm93biBhbmQgbmVlZGVkIHRvIGJlIGVzdGltYXRlZCB1c2luZyB0aGUgc2FtcGxlIHN0YXRpc3RpY3MsIHdlIGNhbiB1c2UgdGhlIGZ1bmN0aW9uIGBgYExjS1NgYGAgZnJvbSB0aGUgYGBgS1NDb3JyZWN0YGBgIHBhY2thZ2UuCgojIyMgRXhhbXBsZQoxLiBTdXBwb3NlIHdlIGhhdmUgYSB2ZWN0b3Igb2YgZGF0YSBgYGB4YGBgIGFuZCB3ZSB3YW50IHRvIHRlc3QgaWYgdGhlIHNhbXBsZSBjb21lcyBmcm9tIGEgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgcG9wdWxhdGlvbgoKRm9yIHRoaXMgZXhhbXBsZSwgd2Ugd2lsbCB1c2UgdGhlIGBgYGNhcnNgYGAgZGF0YSwgYXZhaWxhYmxlIGluIFIKYGBge3J9CiMgVGhlIGRhdGEKeCA9IGNhcnMkZGlzdAoKIyBRdWFudGlsZSBwbG90IG9mIHgKcXFub3JtKHgpCgojIFdlIHdpbGwgdGVzdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHgsIGFnYWluc3QgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24Ka3MudGVzdCh4LAogICAgICAgICJwbm9ybSIpCmBgYApXZSBzZWUgdGhhdCBpZiB3ZSBvbmx5IGlucHV0IGBgYCJwbm9ybSJgYGAgYXMgdGhlIGNvbXBhcmVkIGRpc3RyaWJ1dGlvbiwgYGBga3MudGVzdGBgYCBnaXZlcyB1cyBhIHAtdmFsdWUgd2hpY2ggaXMgY29uc2lkZXJhYmx5IHNtYWxsLCBhbmQgdGh1cyB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgeCBpcyAibm9ybWFsbHkiIGRpc3RyaWJ1dGVkIGlzIHJlamVjdGVkIChFdmVuIHRob3VnaCBncmFwaGljYWxseSwgZnJvbSB0aGUgcXVhbnRpbGUgcGxvdCwgd2UgbWlnaHQgbm90IHJlamVjdCB0aGF0IGh5cG90aGVzaXMpLiBLZWVwIGluIG1pbmQgdGhhdCBieSBvbmx5IGdpdmluZyB0aGUgaW5wdXQgYGBgInBub3JtImBgYCwgdGhlbiB0aGUgY29tcGFyZWQgZGlzdHJpYnV0aW9uIGlzIHRoYXQgb2YgYSAqc3RhbmRhcmQqIG5vcm1hbCBkaXN0cmlidXRpb24gKCRcbWF0aGNhbHtOfSAoMCwxKSQpLCB0aHVzIG9idmlvdXNseSBpZiB3ZSBjb21wYXJlIGEgc2FtcGxlIGZyb20gYSAkXG1hdGhjYWx7Tn0gKDEwMCwxKSQgZGlzdHJpYnV0aW9uIHRvIHRoZSBzdGFuZGFyZCBub3JtYWwsIHRoZW4gYGBga3MudGVzdGBgYCB3b3VsZCBnaXZlIGEgcC12YWx1ZSBzaWduaWZpY2FudGx5IHNtYWxsIHRvIHJlamVjdCB0aGUgaHlwb3RoZXNpcyB0aGF0IHRoZSBzYW1wbGUgZnJvbSAKJFxtYXRoY2Fse059KDEwMCwxKSQgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdpdGggbWVhbiAwIGFuZCB2YXJpYW5jZSAxLiBUbyBwcm9wZXJseSB0ZXN0IGlmIGBgYHhgYGAgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHdlIG5lZWQgdG8ga25vdyB0aGUgcGFyYW1ldGVycyBvZiB0aGUgcG9wdWxhdGlvbiAodGhhdCBpcyB0aGUgbWVhbiBhbmQgdmFyaWFuY2Ugb2YgdGhlIGRpc3RyaWJ1dGlvbiksIHdoaWNoIGluIG1vc3QgY2FzZXMsIGFyZSBub3Qga25vd24sIGFuZCB0aHVzIHdlIG5lZWQgdG8gdXNlIGFuIGFwcHJveGltYXRpb24sIHN1Y2ggYXMgdGhlIHNhbXBsZSBtZWFuIGFuZCBzYW1wbGUgdmFyaWFuY2UuIEZvciBleGFtcGxlIDogCgpgYGB7cn0KIyBUZXN0aW5nIGlmIHggaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHcvIHBhcmFtZXRlcnMgYXBwcm94aW1hdGVkIHdpaCBtZWFuKHgpIGFuZCB2YXJpYW5jZSBvZiB4CmtzLnRlc3QoeCwKICAgICAgICAicG5vcm0iLG1lYW49bWVhbih4KSxzZCA9IHNkKHgpLCAjIFdlIHVzZSBzZCh4KSBzaW5jZSB0aGF0IGlzIHRoZSBwYXJhbWV0ZXIgdGhhdCB0aGUgZnVuY3Rpb25zIGluIFIgdXNlIHRvIHBhcmFtZXRlcml6ZSB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbgogICAgICAgICkKYGBgCldlIHNlZSB0aGF0IG5vdywgYGBga3MudGVzdGBgYCBnaXZlcyB0aGUgcC12YWx1ZSAkMC4zOTc5ID4gMC4wNSQsIGFuZCB0aHVzIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB4IGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdpdGggbWVhbiBlcXVhbCB0byB0aGUgc2FtcGxlIG1lYW4gYW5kIHZhcmlhbmNlIGVxdWFsIHRvIHRoZSBzYW1wbGUgdmFyaWFuY2UsIGlzIG5vdCByZWplY3RlZCBhdCAkMC4wNSQgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlLgoKMi4gU3VwcG9zZSB3ZSBoYXZlIDIgc2FtcGxlcyBmcm9tIDIgZGlzdHJpYnV0aW9ucywgYW5kIHdlIHdvdWxkIGxpa2UgdG8gdGVzdCBpZiB0aGUgMiBzYW1wbGVzIGNhbWUgZnJvbSB0aGUgc2FtZSBkaXN0cmlidXRpb24uIFdlIGNhbiBkbyB0aGlzIGFzIGZvbGxvd3MKYGBge3J9CiMgVGhlIHNhbXBsZXMKeCA9IHJub3JtKDEwMCxtZWFuID0gMjUsIHNkID0gMikgIyBBIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHNhbXBsZSB3LyBtZWFuID0gMjUgYW5kIHN0YW5kYXJkIGRldiA9IDIKeSA9IHJleHAoMjAwLHJhdGUgPSAyKSAjIEFuIGV4cG9uZW50aWFsbHkgZGlzdHJpYnV0ZWQgc2FtcGxlIHcvIHJhdGUgPSAyCgojIFRlc3QgaWYgeCBhbmQgeSBhcmUgZnJvbSB0aGUgc2FtZSBkaXN0cmlidXRpb24Ka3MudGVzdCh4LHkpCmBgYApXZSBzZWUgdGhhdCB0aGUgcC12YWx1ZSBpcyBzaWduaWZpY2FudGx5IHNtYWxsIHN1Y2ggdGhhdCB3ZSBtYXkgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdGBgYHhgYGAgYW5kIGBgYHlgYGAgY2FtZSBmcm9tIHRoZSBzYW1lIGRpc3RyaWJ1dGlvbiBhdCBhIHF1aXRlIHNtYWxsIHNpZ25pZmljYW5jZSBsZXZlbCAoZXZlbiBhdCAkMC4wMDAwMDAwMSQgc2lnbmlmaWNhbmNlLCB0aGUgbnVsbCBoeXBvdGhlc2lzIGNhbiBzdGlsbCBiZSByZWplY3RlZCkuIFRodXMsIHdlIGNvbmNsdWRlIHRoYXQgYGBgeGBgYCBhbmQgIGBgYHlgYGAgZG8gbm90IGNvbWUgZnJvbSB0aGUgc2FtZSBkaXN0cmlidXRpb24uCgojIyBMaWxsaWVmb3JzIFRlc3QKTGlsbGllZm9ycyB0ZXN0IGZvciBub3JtYWxpdHkgcHJvdmlkZXMgYSBjb3JyZWN0aW9uIHRvIEtvbG1vZ29yb3YtU21pcm5vdiBUZXN0IHdoZW4gdGhlIHBhcmFtZXRlcnMgb2YgdGhlIHBvcHVsYXRpb24gaXMgbm90IGtub3duLgoKVG8gcGVyZm9ybSB0aGUgTGlsbGllZm9ycyB0ZXN0IGZvciBub3JtYWxpdHksIHdlIHVzZSB0aGUgZnVuY3Rpb24gYGBgbGlsbGllLnRlc3RgYGAgZnJvbSB0aGUgYGBgbm9ydGVzdGBgYCBwYWNrYWdlCgpgYGB7cn0KbGlicmFyeShub3J0ZXN0KSAjIExvYWQgdGhlIGxpYnJhcnkgKGluc3RhbGwgaXQgZmlyc3QpCmBgYAoKClRoZSBzeW50YXggaXMgYXMgZm9sbG93cyA6IAoKYGBgCmxpbGxpZS50ZXN0KHgpCmBgYApUaGUgaW5wdXQgYXJndW1lbnQgaXMgOgoKKiB4IDogQSBudW1lcmljIHZlY3RvciBvZiBkYXRhIHRvIGJlIHRlc3RlZAoKTm90ZSB0aGF0IHRoZSBzdGF0aXN0aWMgcHJvZHVjZWQgYnkgdGhpcyBmdW5jdGlvbiB3aWxsIGJlIGVxdWFsIHRvIHRoYXQgaWYgd2UgdXNlIGBgYGtzLnRlc3QoeCwicG5vcm0iLG1lYW49bWVhbih4KSxzZD1zZCh4KWBgYCwgYnV0IHRoZSBwLXZhbHVlIHdvdWxkIGJlIGRpZmZlcmVudC4KCiMjIyBFeGFtcGxlCjEuIFVzaW5nIHRoZSBgYGBjYXJzYGBgIGRhdGEsIHdlIHdpbGwgdGVzdCBpZiB0aGUgZGlzdGFuY2UgdHJhdmVsZWQgYnkgdGhlIGNhcnMgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQKCmBgYHtyfQojIFRoZSBkYXRhCnggPSBjYXJzJGRpc3QKCiMgVGVzdApsaWxsaWUudGVzdCh4KQpgYGAKTm90ZSB0aGF0IHRoZSBzdGF0aXN0aWMgdmFsdWUgaXMgdGhlIHNhbWUgYGBgRCA9IDAuMTI2NzVgYGAgYnV0IHRoZSBwLXZhbHVlIGlzIGRpZmZlcmVudCwgYnV0IHN0aWxsLCBhdCAkMC4wNSQgbGV2ZWwgb2Ygc2lnbmlmaWNhbmNlLCBzYW1lIGNvbmNsdXNpb24gdGhhdCB0aGUgbnVsbCBoeXBvdGhlc2lzIHRoYXQgYGBgeGBgYCBpcyBub3JtYWxseSBkaXN0cmlidXRlZCBpcyBub3QgcmVqZWN0ZWQuCgpGb3Igb3RoZXIgZGlzdHJpYnV0aW9uLCB0aGUgZnVuY3Rpb24gYGBgTGNLU2BgYCBmcm9tIHRoZSBgYGBLU2NvcnJlY3RgYGAgcGFja2FnZSBwcm92aWRlcyB0aGUgTGlsbGllZm9ycy1jb3JyZWN0ZWQgS29sbW9nb3JvdiBTbWlybm92IHRlc3QgZm9yIG90aGVyIGRpc3RyaWJ1dGlvbnMsIHN1Y2ggYXMgZXhwb25lbnRpYWwsIHVuaWZvcm0sIG1peHR1cmUgbm9ybWFsLCBhbmQgb3RoZXJzLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiByZWZlciB0byB0aGUgZG9jdW1lbnRhdGlvbiBvZiB0aGUgZnVuY3Rpb24uCgojIEtlbmRhbGwncyBUYXUKVG8gY2FsY3VsYXRlIHRoZSBLZW5kYWxsJ3MgVGF1IHN0YXRpc3RpYyBiZXR3ZWVuIDIgdmVjdG9ycyBvZiBkYXRhLCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGBgYGNvcmBgYCBmcm9tIHRoZSBgYGBzdGF0c2BgYCBwYWNrYWdlLgoKR2l2ZW4gMiBudW1lcmljIGRhdGEgdmVjdG9ycyBgYGB4LHlgYGAsIHRvIGNhbGN1bGF0ZSB0aGUgS2VuZGFsbCdzIFRhdSBiZXR3ZWVuIHRoZSAyIGRhdGEsIHdlIHNldCB0aGUgYXJndW1lbnQgYGBgbWV0aG9kID0gImsiYGBgIG9yIGBgYG1ldGhvZCA9ICJrZW5kYWxsImBgYCBpbiB0aGUgYGBgY29yYGBgIGZ1bmN0aW9uLgoKIyMjIEV4YW1wbGUKMS4gV2Ugd2lsbCBjYWxjdWxhdGUgdGhlIEtlbmRhbGwncyBUYXUgc3RhdGlzdGljIGZvciB0aGUgYGBgcmF0aW5nYGBgIGFuZCBgYGBwcmljZWBgYCBmcm9tIHRoZSBgYGByYXRpbmdfcHJpY2VfZGF0YWBgYCBkYXRhLgpgYGB7cn0KcmF0aW5nX3ByaWNlX2RhdGEKYGBgCgpgYGB7cn0KIyBLZW5kYWxsJ3MgVGF1IHN0YXRpc3RpYwpjb3IocmF0aW5nX3ByaWNlX2RhdGEkUmF0aW5nLAogICAgcmF0aW5nX3ByaWNlX2RhdGEkUHJpY2UsCiAgICBtZXRob2QgPSAiayIsCiAgICApCmBgYApXZSBzZWUgdGhhdCB0aGUgS2VuZGFsbCdzIFRhdSBpcyAtMC4zMzMzMzMzLiBUbyB0ZXN0IHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhpcywgd2UgdXNlIHRoZSBgYGBjb3IudGVzdGBgYCBmdW5jdGlvbgpgYGB7cn0KIyBzaWduaWZpY2FuY2Ugb2Yga2VuZGFsbCdzIHRhdSBjb3JyZWxhdGlvbgpjb3IudGVzdChyYXRpbmdfcHJpY2VfZGF0YSRSYXRpbmcsCiAgICAgICAgIHJhdGluZ19wcmljZV9kYXRhJFByaWNlLAogICAgICAgICBtZXRob2QgPSAiayIpCmBgYApTaW5jZSB0aGUgcC12YWx1ZSBpcyAkMC4yNTk1ID4gMC4wNSQsIHdlIGNvbmNsdWRlIHRoYXQgYXQgJDAuMDUkIGxldmxlIG9mIHNpZ25pZmljYW5jZSwgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0IHRoZSBLZW5kYWxsJ3MgVGF1IGJldHdlZW4gdGhlIHJhdGluZyBhbmQgcHJpY2UgaXMgMCAoaS5lIHRoZXJlIGlzIG5vIGNvcnJlbGF0aW9uIGJldHdlZW4gcmF0aW5nIGFuZCBwcmljZSBhY2NvcmRpbmcgdG8gS2VuZGFsbCdzIFRhdSBTdGF0aXN0aWMpLCBpcyBub3QgcmVqZWN0ZWQgLgoKCiMgQ3JhbWVyIENvZWZmaWNpZW50CkdpdmVuIGEgY29udGluZ2VuY3kgdGFibGUsIHdlIGNhbiBjYWxjdWxhdGUgdGhlIENyYW1lciBjb2VmZmljaWVudCAoQ3JhbWVyJ3MgVikgYmV0d2VlbiB0aGUgY2F0ZWdvcmllcyBpbiB0aGUgcm93cyBhbmQgdGhlIGNhdGVnb3JpZXMgaW4gdGhlIGNvbHVtbnMgYnkgdXNpbmcgdGhlIGBgYGNyYW1lcmBgYCBmdW5jdGlvbiBmcm9tIHRoZSBgYGBzanN0YXRzYGBgIHBhY2thZ2UuCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHNqc3RhdHMpICMgTG9hZGluZyB0aGUgc2pzdGF0cyBsaWJyYXJ5IChpbnN0YWxsIGl0IGZpcnN0KQpgYGAKClRoZSBzeW50YXggZm9yIHRoZSBgYGBjcmFtZXJgYGAgZnVuY3Rpb24gaXMgYXMgZm9sbG93cyA6IAoKYGBgCmNyYW1lcih0YWIpCmBgYApUaGUgaW5wdXQgYXJndW1lbnQgaXMgOgoKKiB0YWIgOiB0aGUgY29udGluZ2VuY3kgdGFibGUKCklmIHRoZSBkYXRhIGF0IGhhbmQgaXMgbm90IGFscmVhZHkgYSBjb250aW5nZW5jeSB0YWJsZSwgd2UgY2FuIHN0aWxsIGNhbGN1bGF0ZSB0aGUgQ3JhbWVyJ3MgViBiZXR3ZWVuIDIgdmFyaWFibGVzIHVzaW5nIHRoZSBmdW5jdGlvbiBgYGBjcm9zc3RhYmxlX3N0YXRpc3RpY3NgYGAsIGFsc28gZnJvbSB0aGUgYGBgc2pzdGF0c2BgYCBwYWNrYWdlLiBUSGUgc3ludGF4IGZvciB0aGlzIGZ1bmN0aW9uIGlzIGFzIGZvbGxvd3MgOgpgYGAKY3Jvc3N0YWJsZV9zdGF0aXN0aWNzKGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICB4MSwKICAgICAgICAgICAgICAgICAgICAgIHgyLAogICAgICAgICAgICAgICAgICAgICAgc3RhdGlzdGljcyA9IGMoImF1dG8iLCJjcmFtZXIiLCJwaGkiLCJzcGVhcm1hbiIsImtlbmRhbGwiLCJwZWFyc29uIiwiZmlzaGVyIiksCiAgICAgICAgICAgICAgICAgICAgICAuLi4pCmBgYAoKVGhlIGlucHV0IGFyZ3VtZW50cyBhcmUgOiAKCiogZGF0YSA6IERhdGFmcmFtZSBmcm9tIHdoaWNoIHRvIGFjY2VzcyBgYGB4MWBgYCBhbmQgYGBgeDJgYGAKKiB4MSx4MiA6IFRoZSB2YXJpYWJsZXMgZnJvbSB0aGUgZGF0YSAoY29sdW1ucyBvZiB0aGUgZGF0YWZyYW1lKSBmcm9tIHdoaWNoIHRoZSBzdGF0aXN0aWNzIHdpbGwgYmUgY29tcHV0ZWQuCiogc3RhdGlzdGljcyA6IFdoaWNoIHN0YXRpc3RpY3MgaXMgY2FsY3VsYXRlZAoKIyMjIEV4YW1wbGUKMS4gV2Ugd2lsbCBjb25zaWRlciBhbiBleGFtcGxlIG9mIGNhbGN1bGF0aW5nIENyYW1lcidzIFYgYnkgdXNpbmcgdGhlIGBgYGVmY2BgYCAoRXVybyBGYW1pbHkgQ2FyZSkgZGF0YS4gKEEgZGF0YXNldCByZWdhcmRpbmcgdGhlIGluZm9ybWF0aW9uIG9mIGNhcmV0YWtlcnMgb2YgZWxkZXJseSBwZW9wbGUgaW4gZXVyb3BlKSAKYGBge3J9CmRhdGEoZWZjKQpoZWFkKGVmYykgIyBGaXJzdCA2IHJvd3Mgb2YgdGhlIGRhdGEKYGBgCldlIHdpbGwgZm9jdXMgb24gMiB2YXJpYWJsZXMsIGBgYGUxNnNleGBgYCAoR2VuZGVyIG9mIHRoZSBlbGRlcikgYW5kIGBgYGMxNjFzZXhgYGAgKEdlbmRlciBvZiB0aGUgY2FyZXRha2VyKS4gV2Ugd2lsbCBzZWUgaWYgdGhlcmUgYXJlIGFueSBhc3NvY2lhdGlvbi9jb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBnZW5kZXIgb2YgdGhlIGNhcmV0YWtlciBhbmQgdGhlIGdlbmRlciBvZiB0aGUgZWxkZXIuCgpXZSBjYW4gZmlyc3QgZ2VuZXJhdGUgdGhlIGNyb3NzIHRhYnVsYXRpb24gKGNvbnRpbmdlbmN5IHRhYmxlKSBiZXR3ZWVuIHRoZSAyIHZhcmlhYmxlcyBieSB1c2luZyB0aGUgYGBgQ3Jvc3NUYWJsZWBgYCBmdW5jdGlvbiBmcm9tIHRoZSBgYGBkZXNjcmBgYCBwYWNrYWdlLiAKCmBgYHtyfQojIExvYWQgdGhlIHJlcXVpcmVkIHBhY2thZ2UgKEluc3RhbGwgaXQgZmlyc3QpCmxpYnJhcnkoZGVzY3IpCgojIE1ha2luZyB0aGUgY3Jvc3N0YWJ1bGF0aW9uCkNyb3NzVGFibGUoeCA9IGVmYyRlMTZzZXgsCiAgICAgICAgICAgeSA9IGVmYyRjMTYxc2V4KQpgYGAKV2UgY2FuIHNlZSB0aGUgbWFyZ2luYWwgZnJlcXVlbmNpZXMgZm9yIGVhY2ggZ2VuZGVyIG9mIHRoZSBjYXJldGFrZXIgYW5kIHRoZSBnZW5kZXIgb2YgdGhlIGVsZGVyLgoKYGBge3J9CiMgQ2FsY3VsYXRlIHRoZSBjcmFtZXIncyBWIHN0YXRpc3RpYyB1c2luZyBjcm9zc3RhYmxlX3N0YXRpc3RpY3MKY3Jvc3N0YWJsZV9zdGF0aXN0aWNzKGRhdGEgPSBlZmMsICMgRGF0YWZyYW1lCiAgICAgICAgICAgICAgICAgICAgICB4MSA9IGUxNnNleCwKICAgICAgICAgICAgICAgICAgICAgIHgyID0gYzE2MXNleCwKICAgICAgICAgICAgICAgICAgICAgIHN0YXRpc3RpY3MgPSAiYyIsICMgQ2FsY3VsYXRlIHRoZSBjcmFtZXIncyBWIHN0YXRpc3RpYwogICAgICAgICAgICAgICAgICAgICAgKQpgYGAKV2Ugc2VlIHRoYXQgdGhlIENyYW1lcidzIFYgaXMgJDAuMDUyNiQsIHdoaWNoIGlzIHF1aXRlIGNsb3NlIHRvICQwJCBhbmQgdGhlIGNvcnJlc3BvbmRpbmcgcC12YWx1ZSBpcyAkMC4xMzUxID4gMC4wNSQuIFNvLCB3ZSBjb25jbHVkZSB0aGF0IGF0ICQwLjA1JCBsZXZlbCBvZiBzaWduaWZpY2FuY2UsIHRoZXJlIGlzIG5vIGFzc29jaWF0aW9uL2NvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGdlbmRlciBvZiB0aGUgY2FyZXRha2VyIGFuZCB0aGUgZ2VuZGVyIG9mIHRoZSBlbGRlci4KCkFub3RoZXIgd2F5IHRvIGRvIGl0IGlzIHRvIG1ha2UgYSB0YWJsZSBvZiBjb250aW5nZW5jeSB0YWJsZSBmaXJzdCBiZXR3ZWVuIHRoZSAyIHZhcmlhYmxlcywgYW5kIHRoZW4gYXBwbHlpbmcgdGhlIGBgYGNyYW1lcmBgYCBmdW5jdGlvbiB0byBpdC4KYGBge3J9CiMgQ3JlYXRpbmcgdGhlIGNvbnRpbmdlbmN5IHRhYmxlIGZvciB0aGUgMiB2YXJpYWJsZXMKY29udF90YWJsZSA9IHRhYmxlKGVmYyRlMTZzZXgsZWZjJGMxNjFzZXgpCmNvbnRfdGFibGUKYGBgCldlIGNhbiBjYWxjdWxhdGUgdGhlIENyYW1lcidzIFYgdXNpbmcgdGhlIGBgYGNyYW1lcmBgYCBmdW5jdGlvbgpgYGB7cn0KY3JhbWVyKGNvbnRfdGFibGUpCmBgYApXZSBnZXQgdGhhdCB0aGUgQ3JhbWVyJ3MgViBmb3IgdGhlIDIgdmFyaWFibGVzIGlzICQwLjA1MjU4MjQ5JC4KCiMgUGhpIENvZWZmaWNpZW50CldlIGNhbiBjYWxjdWxhdGUgdGhlIFBoaSBjb2VmZmljaWVudCBiZXR3ZWVuIDIgYmluYXJ5IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBieSB1c2luZyB0aGUgc2FtZSBgYGBjcm9zc3RhYmxlX3N0YXRpc3RpY3NgYGAgZnVuY3Rpb24gZnJvbSB0aGUgYGBgc2pzdGF0c2BgYCBwYWNrYWdlIGFzIHdlIHVzZSBpbiBjYWxjdWxhdGluZyBDcmFtZXIgY29lZmZpY2llbnQuIFRvIGNhbGN1bGF0ZSB0aGUgUGhpIGNvZWZmaWNpZW50IG9mIDIgdmFyaWFibGVzLCB3ZSBzZXQgdGhlIGBgYHN0YXRpc3RpY3NgYGAgYXJndW1lbnQgaW4gdGhlIGBgYGNyb3NzdGFibGVfc3RhdGlzdGljc2BgYCB0byBgYGAicGhpImBgYC4gCgpBbiBhbmFsb2cgdG8gdGhlIGBgYGNyYW1lcmBgYCBmdW5jdGlvbiBjYW4gYWxzbyBiZSB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgcGhpIGNvZWZmaWNpZW50IGdpdmVuIGEgMngyIGNvbnRpbmdlbmN5IHRhYmxlLCB0aGF0IGlzIHRoZSBgYGBwaGlgYGAgZnVuY3Rpb24gZnJvbSBgYGBzanN0YXRzYGBgIHBhY2thZ2UuCgojIyMgRXhhbXBsZSAKMS4gVXNpbmcgdGhlIHNhbWUgZGF0YXNldCBhcyB0aGUgZXhhbXBsZSBmb3IgQ3JhbWVyIGNvZWZmaWNpZW50LCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgUGhpIENvZWZmaWNpZW50IGFuZCBpdHMgc2lnbmlmaWNhbmNlCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgUGhpIENvZWZmaWNpZW50CmNyb3NzdGFibGVfc3RhdGlzdGljcyhlZmMsCiAgICAgICAgICAgICAgICAgICAgICB4MSA9IGUxNnNleCwKICAgICAgICAgICAgICAgICAgICAgIHgyID0gYzE2MXNleCwKICAgICAgICAgICAgICAgICAgICAgIHN0YXRpc3RpY3MgPSAicGhpIikKYGBgCldlIGNhbiBzZWUgdGhhdCB0aGUgUGhpIGNvZWZmaWNpZW50IGlzICQwLjA1MjYkIGFuZCB0aGUgY29ycmVzcG9uZGluZyBwLXZhbHVlIGlzICQwLjEzNTEgPiAwLjA1JC4gVGh1cyB3ZSBjb25jbHVkZSB0aGF0IHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGVyZSBpcyBubyBhc3NvY2lhdGlvbi9jb3JyZWxhdGlvbi9kZXBlbmRlbmNlIGJldHdlZW4gdGhlIGdlbmRlciBvZiB0aGUgY2FyZXRha2VyIGFuZCB0aGUgZWxkZXIgaXMgbm90IHJlamVjdGVkIGF0ICQwLjA1JCBsZXZlbCBvZiBzaWduaWZpY2FuY2UuCgpXZSBjYW4gYWxzbyB1c2UgdGhlIGBgYHBoaWBgYCBmdW5jdGlvbiwgYnkgZmlyc3QgY3JlYXRpbmcgdGhlIGNvbnRpbmdlbmN5IHRhYmxlCmBgYHtyfQojIENyZWF0aW5nIHRoZSBjb250aW5nZW5jeSB0YWJsZQpjb250X3RhYmxlXzIgPSB0YWJsZShlZmMkZTE2c2V4LGVmYyRjMTYxc2V4KQpjb250X3RhYmxlXzIKYGBgCk5vdyB3ZSBjYW4gY2FsY3VsYXRlIHRoZSBQaGkgY29lZmZpY2llbnQKYGBge3J9CnBoaShjb250X3RhYmxlXzIpCmBgYAoKCiMgQ29jaHJhbidzIFRlc3QgYW5kIE1jTmVtYXIncyBUZXN0ClRvIHBlcmZvcm0gdGhlIENvY2hyYW4ncyBUZXN0LCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIGBgYGNvY2hyYW4ucXRlc3RgYGAgZnJvbSB0aGUgYGBgUlZBaWRlTWVtb2lyZWBgYCBwYWNrYWdlCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KFJWQWlkZU1lbW9pcmUpCmBgYAoKVGhlIHN5bnRheCBmb3IgdGhlIGBgYGNvY2hyYW4ucXRlc3RgYGAgaXMgYXMgZm9sbG93cyA6CgpgYGAKY29jaHJhbi5xdGVzdChmb3JtdWxhLAogICAgICAgICAgICAgIGRhdGEsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjA1LAogICAgICAgICAgICAgICkKYGBgCgpUaGUgaW5wdXQgYXJndW1lbnRzIGFyZSA6IAoKKiBmb3JtdWxhIDogYSBmb3JtdWxhIG9mIHRoZSBmb3JtIHkgfiBnIHwgYiwgd2hlcmUgeSBpcyBhIHZlY3RvciBvZiBvbmx5IDIgdmFsdWVzIG9yIGEgZmFjdG9yIChpLmUgeSBvbmx5IHRha2VzIDIgdmFsdWVzLCBmb3IgZXhhbXBsZSAwIGFuZCAxLCBvciBUUlVFIGFuZCBGQUxTRSwgZXRjKSBhbmQgZyBpcyBhIGZhY3RvciBvZiBncm91cHMgc3BlY2lmeWluZyB0aGUgcmVzcG9uc2UncyBncm91cCwgYW5kIGIgaXMgYSBmYWN0b3Igb2YgYmxvY2tzIHRvIGJlIHRlc3RlZChUaGlzIGlzIHNpbWlsYXIgdG8gdGhlIGZvcm11bGEgZm9ybSBvZiB0aGUgS3J1c2thbC1XYWxsaXMgVGVzdCkKKiBkYXRhIDogKG9wdGlvbmFsKSBkYXRhIGZyYW1lIGNvbnRhaW5pbmcgeSBhbmQgZwoqIGFscGhhIDogc2lnbmlmaWNhbmNlIGxldmVsIGF0IHdoaWNoIHRoZSBoeXBvdGhlc2lzIGlzIHRlc3RlZAoKIyMjIEV4YW1wbGUKMS4gU3VwcG9zZSB0aGUgZm9sbG93aW5nIGRhdGEgcmVnYXJkaW5nIDUxIHBlb3BsZSdzIHByZWZlcmVuY2Ugb2YgYmV2ZXJhZ2UgZm9yIGJyZWFrZmFzdC4KYGBge3J9CmJyZWFrZmFzdApgYGAKV2Ugd291bGQgbGlrZSB0byB0ZXN0IGlmIHRoZXJlIGFyZSBhbnkgYmV2ZXJhZ2Ugd2hpY2ggaXMgbW9yZSBwcmVmZXJyZWQgdGhhbiB0aGUgb3RoZXJzLiBUbyBkbyB0aGlzLCB3ZSBjb25kdWN0IGEgQ29jaHJhbidzIFRlc3QuCgpIZXJlIHRoZSByZXNwb25zZSB2ZWN0b3IgKHkpIGlzIGBgYHJlc3BvbnNlYGBgLCB0aGUgZ3JvdXBzIGFyZSBgYGBiZXZlcmFnZWBgYCBhbmQgdGhlIGJsb2NrIGlzIGBgYHBlb3BsZWBgYC4KCmBgYHtyfQojIENoYW5nZSB0aGUgZGF0YSB0eXBlIG9mIGJldmVyYWdlIGFuZCByZXNwb25zZSB0byBmYWN0b3IKYnJlYWtmYXN0JGJldmVyYWdlID0gYXMuZmFjdG9yKGJyZWFrZmFzdCRiZXZlcmFnZSkKYnJlYWtmYXN0JHJlc3BvbnNlID0gYXMuZmFjdG9yKGJyZWFrZmFzdCRyZXNwb25zZSkKYnJlYWtmYXN0CmBgYAoKYGBge3J9CiMgQ29jaHJhbiB0ZXN0CmNvY2hyYW4ucXRlc3QocmVzcG9uc2V+YmV2ZXJhZ2UgfCBwZW9wbGUsCiAgICAgICAgICAgICAgZGF0YSA9IGJyZWFrZmFzdCwKICAgICAgICAgICAgICBhbHBoYSA9IDAuMDUpCmBgYApXZSBzZWUgdGhhdCB0aGUgc3RhdGlzdGljIHZhbHVlIGlzICQzLjMzMzMkIGFuZCB0aGUgY29ycmVzcG9uZGluZyBwLXZhbHVlIGlzICQwLjE4ODkgPiAwLjA1JC4gVGh1cyB3ZSBjb25jbHVkZSB0aGF0IGF0ICQwLjA1JCBsZXZlbCBvZiBzaWduaWZpY2FuY2UsIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGluIHBlb3BsZSdzIHByZWZlcmVuY2Ugb2YgYmV2ZXJhZ2UgaXMgbm90IHJlamVjdGVkLgoKMi4gU3VwcG9zZSB0aGUgZm9sbG93aW5nIGRhdGEgcmVnYXJkaW5nIGEgc3VydmV5IGNvbmR1Y3RlZCBvbiAxNCBzdHVkZW50cyByZWdhcmRpbmcgc29tZSBhY3Rpdml0aWVzIHRoZXkgYXJlIHdpbGxpbmcgdG8gZG8uIAoKKGZyb20gOiAgaHR0cHM6Ly9yY29tcGFuaW9uLm9yZy9oYW5kYm9vay9IXzA3Lmh0bWwjOn46dGV4dD1Db2NocmFuJ3MlMjBRJTIwdGVzdCUyMGlzJTIwYW4sbXVsdGlwbGUlMjBjYXRlZ29yaWVzJTIwd2l0aCUyMHBhaXJlZCUyMHJlc3BvbnNlcy4mdGV4dD1xdGVzdCUyMHdoaWNoJTIwd2lsbCUyMGNvbmR1Y3QlMjBDb2NocmFuJ3MlMjBRJTIwdGVzdCUyMG9uJTIwbG9uZyUyRGZvcm1hdCUyMGRhdGEuKQpgYGB7cn0Kc3R1ZGVudHMKYGBgCldlIHdhbnQgdG8gdGVzdCBpZiB0aGVyZSBhcmUgYW55IGFjdGl2aXRpZXMgd2hpY2ggYXJlIG1vcmUgYXBwZWFsaW5nIGZvciB0aGUgc3R1ZGVudHMgdG8gZG8uIFRvIHRlc3QgdGhpcywgd2Ugd2lsbCBjb25kdWN0IHRoZSBDb2NocmFuJ3MgVGVzdCwgd2l0aCBgYGBQcmFjdGljZWBgYCBhcyBncm91cHMsIGBgYFN0dWRlbnRgYGAgYXMgdGhlIGJsb2NrcywgYW5kIGBgYFJlc3BvbnNlYGBgIGFzIHRoZSByZXNwb25zZSB2ZWN0b3IKCmBgYHtyfQojIENoYW5naW5nIHRoZSByZXNwb25zZSB2ZWN0b3IgYW5kIHRoZSBncm91cHMgYXMgZmFjdG9yCnN0dWRlbnRzJFByYWN0aWNlID0gYXMuZmFjdG9yKHN0dWRlbnRzJFByYWN0aWNlKQpzdHVkZW50cyRSZXNwb25zZSA9IGFzLmZhY3RvcihzdHVkZW50cyRSZXNwb25zZSkKCiMgQ29jaHJhbidzIFRlc3QKY29jaHJhbi5xdGVzdChSZXNwb25zZSB+IFByYWN0aWNlIHwgU3R1ZGVudCwKICAgICAgICAgICAgICBkYXRhID0gc3R1ZGVudHMsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjA1KQpgYGAKU2luY2UgdGhlIHAtdmFsdWUgaXMgJDAuMDAwNzIyNSA8IDAuMDUkLCB3ZSBjb25jbHVkZSB0aGF0IHRoZXJlIGlzIGEgZGlmZmVyZW5jZSBpbiBzdHVkZW50cycgcHJlZmVycmVkIGFjdGl2aXRpZXMgKFNvbWUgYWN0aXZpdGllcyBoYXZlIG1vcmUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgc3R1ZGVudHMgd2lsbCBiZSB3aWxsaW5nIHRvIGRvIHRoYW4gb3RoZXJzKS4KCk5vdGUgdGhhdCB0aGUgZnVuY3Rpb24gYWxzbyBwcm9kdWNlcyBhIHBhaXJ3aXNlIGNvbXBhcmlzb24gYmV0d2VlbiB0aGUgZ3JvdXBzIHVzaW5nIHBhaXJ3aXNlIFdpbGNveG9uIFNpZ24gVGVzdCBpZiB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIHRoZSBDb2NocmFuJ3MgVGVzdCBpcyByZWplY3RlZCBhdCBgYGBhbHBoYWBgYCBsZXZlbC4gQW5vdGhlciBhbHRlcm5hdGl2ZSAodGhhdCBpcyBtb3JlIGZyZXF1ZW50bHkgZG9uZSksIGlzIHRvIGRvIHBhaXJ3aXNlIE1jTmVtYXIgVGVzdHMgb24gZWFjaCBncm91cCB0byB0ZXN0IHdoaWNoIGdyb3VwIGhhdmUgYSBoaWdoZXIgcHJvYmFiaWxpdHkgY29tcGFyZWQgdG8gdGhlIG90aGVyLgoKVG8gZG8gcGFpcndpc2UgTWNOZW1hciBUZXN0cywgd2UgbWFrZSB1c2Ugb2YgdGhlIGZ1bmN0aW9uIGBgYHBhaXJ3aXNlTWNuZW1hcmBgYCBmcm9tIHRoZSBgYGByY29tcGFuaW9uYGBgIHBhY2thZ2UKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkocmNvbXBhbmlvbikgIyBMb2FkIHRoZSBwYWNrYWdlIChpbnN0YWxsIGl0IGZpcnN0KQpgYGAKClRoZSBzeW50YXggZm9yIHRoZSBmdW5jdGlvbiBgYGBwYWlyd2lzZU1jbmVtYXJgYGAgaXMgYXMgZm9sbG93cwoKYGBgCnBhaXJ3aXNlTWNuZW1hcihmb3JtdWxhLAogICAgICAgICAgICAgICAgZGF0YSwKICAgICAgICAgICAgICAgIHgsCiAgICAgICAgICAgICAgICBnLAogICAgICAgICAgICAgICAgYmxvY2ssCiAgICAgICAgICAgICAgICB0ZXN0LAogICAgICAgICAgICAgICAgLi4uCiAgICAgICAgICAgICAgICApCmBgYAoKVGhlIGlucHV0IGFyZ3VtZW50cyBhcmUgOgoKKiBmb3JtdWxhIDogZm9ybXVsYSBvZiB0aGUgZm9ybSB4IH4gZyB8IGIsIHNpbWlsYXIgdG8gdGhlIG9uZSB1c2VkIGFzIGluIHRoZSBgYGBjb2NocmFuLnF0ZXN0YGBgLCB4IGlzIHRoZSByZXNwb25zZSB2ZWN0b3IsIGcgaXMgdGhlIGdyb3VwLCBhbmQgYiBpcyB0aGUgYmxvY2suCiogZGF0YSA6IGRhdGEgZnJhbWUgZm9yIGRhdGEgcmVmZXJlbmNlCiogeCA6IHJlc3BvbnNlIHZlY3RvcgoqIGcgOiBncm91cCB2YXJpYWJsZQoqIGJsb2NrIDogYmxvY2sgdmFyaWFibGUKKiB0ZXN0IDogd2hpY2ggdGVzdCB0byB1c2UsIGVpdGhlciBgYGAiZXhhY3QiYGBgLCBgYGAibWNuZW1hciJgYGAsIG9yIGBgYCJwZXJtdXRhdGlvbiJgYGAuICJleGFjdCIgZGVub3RlcyBhbiBleGFjdCB0ZXN0IG9mIHN5bW1ldHJ5IGFuYWxvZ291cyB0byBNY05lbWFyIHRlc3QsIGBgYCJtY25lbWFyImBgYCBkZW5vdGVzIHRoZSBNY05lbWFyIHRlc3Qgb2Ygc3ltbWV0cnksIGFuZCBgYGAicGVybXV0YXRpb24iYGBgIGRlbm90ZXMgYSBwZXJtdXRhdGlvbiB0ZXN0IGFuYWxvZ291cyB0byB0aGUgTWNOZW1hciB0ZXN0LgoqIC4uLiwgb3RoZXIgaW5wdXRzIChyZWZlciB0byB0aGUgZG9jdW1lbnRhdGlvbiBvZiB0aGUgZnVuY3Rpb24pCgpUaGUgaW5wdXQgZGF0YSBzaG91bGQgYmUgZWl0aGVyIGluY2x1ZGluZyBgYGBmb3JtdWxhYGBgIGFuZCBgYGBkYXRhYGBgLCBvciBgYGB4YGBgLGBgYGdgYGAsIGFuZCBgYGBibG9ja2BgYC4KCmBgYHtyfQojIFBhaXJ3aXNlIE1jTm5tYXIgdGVzdCBvbiB0aGUgc3R1ZGVudHMgZGF0YQpwYWlyX21jbmVtYXIgPSBwYWlyd2lzZU1jbmVtYXIoUmVzcG9uc2UgfiBQcmFjdGljZSB8IFN0dWRlbnQsCiAgICAgICAgICAgICAgICBkYXRhID0gc3R1ZGVudHMsCiAgICAgICAgICAgICAgICB0ZXN0ID0gIm1jbmVtYXIiKQpwYWlyX21jbmVtYXIKYGBgCldlIGNhbiBnYXRoZXIgdGhlIHAtdmFsdWUgZm9yIGVhY2ggY29tcGFyaXNvbi4gQXQgJDAuMDUkIGxldmVsIG9mIHNpZ25pZmljYW5jZSwgd2UgY2FuIHNlZSB0aGF0IHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBzdHVkZW50cyBwcmVmZXIgdG8gZG8gdGhlIGFjdGl2aXR5ICJJcnJpZ2F0aW9uIiBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBjb21wYXJlZCB0byB0aGUgYWN0aXZpdHkgIkNsaXBwaW5ncyIsICJNb3dIZWlnaHQiIGFuZCAiU29pbFRlc3QiLgoKIyBUaGVpbC1TZW4gUmVncmVzc2lvbgpUbyBkbyBUaGVpbC1TZW4gcmVncmVzc2lvbiwgd2UgbWFrZSB1c2Ugb2YgdGhlIGZ1bmN0aW9uIGBgYG1ibG1gYGAgZnJvbSB0aGUgYGBgbWJsbWBgYCBwYWNrYWdlCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KG1ibG0pICMgTG9hZGluZyB0aGUgbGlicmFyeSAoaW5zdGFsbCBpdCBmaXJzdCkKYGBgCgpUaGUgc3ludGF4IGZvciB0aGUgYGBgbWJsbWBgYCBmdW5jdGlvbiBpcwoKYGBgCm1ibG0oZm9ybXVsYSwKICAgICBkYXRhZnJhbWUsCiAgICAgcmVwZWF0ZWQgPSBUUlVFKQpgYGAKClRoZSBpbnB1dCBhcmd1bWVudHMgYXJlIDogCgoqIGZvcm11bGEgOiBhIGZvcm11bGEgb2YgdGhlIGZvcm0geSB+IHgsIHdoZXJlIHkgaXMgdGhlIHJlc3BvbnNlLCBhbmQgeCBpcyB0aGUgcHJlZGljdG9yL3JlZ3Jlc3Nvci4gKEJvdGggeSBhbmQgeCBhcmUgbnVtZXJpYyB2ZWN0b3JzKQoqIGRhdGFmcmFtZSA6IChvcHRpb25hbCkgZGF0YWZyYW1lIHRvIHJlZmVyZW5jZSB5IGFuZCB4LgoqIHJlcGVhdGVkIDogd2hldGhlciBvciBub3QgcmVwZWF0ZWQgbWVkaWFuIGNhbGN1bGF0aW9uIGlzIHVzZWQgb24gZGV0ZXJtaW5pbmcgdGhlIHNsb3BlIHBhcmFtZXRlci4gKEZvciBtb3JlIGluZm9ybWF0aW9uLCByZWZlciB0byB0aGUgZG9jdW1lbnRhdGlvbiBvZiB0aGUgZnVuY3Rpb24pCgpTaW1pbGFyIHRvIHRoZSBgYGBsbWBgYCBmdW5jdGlvbiwgdGhlIGBgYG1ibG1gYGAgZnVuY3Rpb24gcmV0dXJucyBhIGxpc3Qgb2YgaXRlbXMgcmVnYXJkaW5nIHRoZSBtb2RlbCAoc3VjaCBhcyByZXNpZHVhbHMsIHBhcmFtZXRlciBlc3RpbWF0ZXMsIGV0YykKCiMjIyBFeGFtcGxlCjEuIFN1cHBvc2Ugd2UgaGF2ZSB0aGUgZm9sbG93aW5nIGRhdGEKCmBgYHtyfQpkYXRhID0gdGhlaWxzZW5yZWcKZGF0YQpgYGAKd2hlcmUgYGBgeWBgYCBpcyB0aGUgcmVzcG9uc2UgdmVjdG9yIGFuZCBgYGB4YGBgIGlzIHRoZSByZWdyZXNzb3IKCldlIHdpbGwgY3JlYXRlIGEgVGhlaWwgU2VuIHJlZ3Jlc3Npb24gbW9kZWwgZm9yIHRoaXMgZGF0YQpgYGB7cn0KIyBDcmVhdGUgdGhlIHRoZWlsIHNlbiByZWdyZXNzaW9uIG1vZGVsCnRzX21vZGVsID0gbWJsbSh5fngsCiAgICAgICAgICAgICAgICBkYXRhID0gZGF0YSkKCiMgU3VtbWFyeSBvZiB0aGUgdGhlaWwtc2VuIHJlZ3Jlc3Npb24Kc3VtbWFyeSh0c19tb2RlbCkKYGBgCldlIHNlZSB0aGF0IHRoZSBzbG9wZSBwYXJhbWV0ZXIncyBwLXZhbHVlICh0aGUgb25lIHVuZGVyIHRoZSBgYGBQcig+fFZ8KWBgYCBvbiB0aGUgcm93IGBgYHhgYGApIGlzIGxlc3MgdGhhbiAkMC4wNSQsIHRodXMgd2UgY29uY2x1ZGUgdGhhdCB0aGUgdmFyaWFibGUgYGBgeGBgYCBpcyBzaWduaWZpY2FudCBpbiBleHBsYWluaW5nIHRoZSB2YXJpYWJpbGl0eSBpbiBgYGB5YGBgIChpbiBvdGhlciB3b3JkcywgdGhlIHJlZ3Jlc3Npb24gaXMgc2lnbmlmaWNhbnQpLgoKV2Ugd2lsbCBub3cgY29tcGFyZSB0aGlzIHJlZ3Jlc3Npb24gbW9kZWwgdG8gdGhlIG9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgcmVncmVzc2lvbiBtb2RlbCAoc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsKS4KCmBgYHtyfQojIE9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgcmVncmVzc2lvbiBtb2RlbCAoc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uKQpvbHNfbW9kZWwgPSBsbSh5fngsIGRhdGEgPSBkYXRhKQoKIyBDcmVhdGluZyBhIGRhdGEgZnJhbWUgdG8gc3RvcmUgdGhlIHByZWRpY3RlZCB2YWx1ZXMgb2YgZWFjaCBtb2RlbApyZWdfZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGRhdGEkeCwgeSA9IGRhdGEkeSwKICAgICAgICAgICAgICAgICAgICAgIHRzX3ByZWQgPSB0c19tb2RlbCRmaXR0ZWQudmFsdWVzLCAjIHByZWRpY3Rpb24gb2YgdGhlaWwtc2VuIG1vZGVsCiAgICAgICAgICAgICAgICAgICAgICBvbHNfcHJlZCA9IG9sc19tb2RlbCRmaXR0ZWQudmFsdWVzICMgcHJlZGljdGlvbiBvZiBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwpCikKYGBgCgpgYGB7cn0KZ2dwbG90KHJlZ19kYXRhLGFlcyh4ID0geCwgeT0geSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMSkgKyAKICBnZW9tX2xpbmUoYWVzKHkgPSB0c19wcmVkLGNvbG9yID0gIlRoZWlsLVNlbiBSZWdyZXNzaW9uIikpICsgCiAgZ2VvbV9saW5lKGFlcyh5ID0gb2xzX3ByZWQsY29sb3IgPSAiU2ltcGxlIExpbmVhciBSZWdyZXNzaW9uIiksbGluZXR5cGU9ImRhc2hlZCIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiIgPSAiZGFya2JsdWUiLCJUaGVpbC1TZW4gUmVncmVzc2lvbiIgPSAicmVkIikpICsgCiAgbGFicyhjb2xvciA9ICJSZWdyZXNzaW9uIE1vZGVsIikKYGBgCk5vdGUgdGhhdCB0aGUgVGhlaWwtU2VuIHJlZ3Jlc3Npb24gbW9kZWwgaXMgbGVzcyBhZmZlY3RlZCBieSBvdXRsaWVycyBpbiB0aGUgZGF0YSwgd2hlcmUgYXMgdGhlIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpcyBhZmZlY3RlZCBieSBvdXRsaWVycywgdGhlIChlc3RpbWF0ZWQpIHNsb3BlIG9mIHRoZSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgaXMgc2xpZ2h0bHkgbGFyZ2VyIHRoYW4gdGhlIFRoZWlsLVNlbiBvbmUsIHNpbmNlIGl0IGlzIGJlaW5nICJwdWxsZWQiIGJ5IHRoZSAyIG91dGxpZXJzLiBUaHVzLCB0aGUgbm9ucGFyYW1ldHJpYyBUaGVpbC1TZW4gcmVncmVzc2lvbiBpcyBtb3JlIHJvYnVzdCB0b3dhcmRzIG91dGxpZXJzLgoKIyBCb290c3RyYXBwaW5nIFRlY2huaXF1ZXMKIyMgQm9vdHN0cmFwcGluZwpHaXZlbiBhIHNpbmdsZSBzYW1wbGUgb2YgZGF0YSwgdG8gZXN0aW1hdGUgYSBwYXJhbWV0ZXIgb2YgdGhlIGRpc3RyaWJ1dGlvbiBieSBib290c3RyYXBwaW5nLCB3ZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgYGBib290YGBgIGZyb20gdGhlIGBgYGJvb3RgYGAgcGFja2FnZQoKYGBge3IgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShib290KSAjIExvYWQgdGhlIHBhY2thZ2VzIChpbnN0YWxsIGl0IGZpcnN0KQpgYGAKClRoZSBzeW50YXggZm9yIHRoZSBgYGJvb3RgYGAgZnVuY3Rpb24gaXMgYXMgZm9sbG93cyA6IAoKYGBgCmJvb3QoeCwKICAgICBzdGF0aXN0aWMsCiAgICAgUiwKICAgICAuLi4pCmBgYAoKVGhlIGlucHV0IGFyZ3VtZW50cyBhcmUgOiAKCiogeCA6IHRoZSBkYXRhLCBjb3VsZCBlaXRoZXIgYmUgYSB2ZWN0b3IsIG1hdHJpeCwgb3IgYSBkYXRhZnJhbWUuIElmIHggaXMgYSBkYXRhZnJhbWUgb3IgYSBtYXRyaXgsIGVhY2ggcm93IGlzIGNvbnNpZGVyZWQgYW4gb2JzZXJ2YXRpb24gZnJvbSBhIG11bHRpdmFyaWF0ZSBkaXN0cmlidXRpb24uCiogc3RhdGlzdGljIDogQSBmdW5jdGlvbiB3aGljaCBjYWxjdWxhdGVzIHRoZSBzdGF0aXN0aWMgb2YgaW50ZXJlc3Qgd2hlbiBhcHBsaWVkIHRvIGBgYHhgYGAuIFRoZSBmdW5jdGlvbiBuZWVkcyB0byBoYXZlIGF0IGxlYXN0IDIgaW5wdXQgYXJndW1lbnRzLCBzaW5jZSAxIGFyZ3VtZW50IGlzIHJlcXVpcmVkIGZvciB0aGUgcmVzYW1wbGluZyBpbiB0aGUgYm9vdHN0cmFwcGluZyBwcm9jZXNzCiogUiA6IEhvdyBtYW55IHRpbWVzIHRoZSBzYW1wbGUgaXMgcmVzYW1wbGVkLgoqIC4uLiA6IE90aGVyIGlucHV0IGFyZ3VtZW50cyAoUmVmZXIgdG8gdGhlIGRvY3VtZW50YXRpb24pCgojIyMgRXhhbXBsZSAKMS4gU3VwcG9zZSB3ZSB3YW50IHRvIGVzdGltYXRlIHRoZSBjb2VmZmljaWVudHMgb2YgYSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBmcm9tIHRoZSBgYGBtdGNhcnNgYGAgZGF0YXNldCwgd2hlcmUgd2Ugd2FudCB0byBtb2RlbCBhIGNhcidzIG1pbGVzIHBlciBnYWxsb24gYXMgYSBmdW5jdGlvbiBvZiBpdHMgd2VpZ2h0IGFuZCBkaXNwbGFjZW1lbnQuCmBgYHtyfQojIFJlYWQgdGhlIGRhdGEKYm9vdF9kYXRhID0gbXRjYXJzCgojIERlZmluZSB0aGUgZnVuY3Rpb24gbmVlZGVkIHRvIGNhbGN1bGF0ZSB0aGUgZXN0aW1hdGVkIGNvZWZmaWNpZW50cyBmb3IgdGhlIHJlZ3Jlc3Npb24gbW9kZWwKcmVnX2NvZWYgPSBmdW5jdGlvbihkYXRhLCBmb3JtdWxhLCBpKXsgIyBUaGUgZnVuY3Rpb24gdGFrZXMgMyBhcmd1bWVudHMsIGRhdGEsIGZvcm11bGEsIGFuZCBpCiAgZGF0ID0gZGF0YVtpLF0gIyBGb3IgdGhlIGJvb3QgdG8gc2VsZWN0IHNhbXBsZXMsIHdlIHdpbGwgdXNlIHRoZSBzYW1wbGUgZGF0LCB0YWtlbiBmcm9tIGRhdGEsIHRvIG1ha2UgYW4gZXN0aW1hdGUgb2YgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQKICBsaW5fbW9kZWwgPSBsbShmb3JtdWxhLCBkYXRhID0gZGF0KSAjIGNyZWF0ZSB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgZnJvbSBkYXQKICByZXR1cm4obGluX21vZGVsJGNvZWZmaWNpZW50cykgIyBSZXR1cm4gdGhlIGNvZWZmaWNpZW50cyBvZiB0aGUgcmVncmVzc2lvbiBtb2RlbAp9CgojIEJvb3RzdHJhcHBpbmcKc2V0LnNlZWQoMTAwKSAjIFNldCB0aGUgc2VlZCBmb3IgdGhlIGJvb3RzdHJhcCByZXNhbXBsaW5nLCBzbyB0aGF0IHRoZSByZXN1bHQgY2FuIGJlIHJlcHJvZHVjaWJsZSAoSWYgdGhpcyBpcyByZW1vdmVkLCB0aGUgcmVzdWx0IG9mIHRoZSBib290c3RyYXAgbWF5IHZhcnkgd2l0aCBlYWNoIGV4ZWN1dGlvbikKCnJlZ19ib290ID0gYm9vdChkYXRhID0gbXRjYXJzLAogICAgICAgICAgICAgICAgc3RhdGlzdGljID0gcmVnX2NvZWYsCiAgICAgICAgICAgICAgICBSID0gNTAwMCwKICAgICAgICAgICAgICAgIGZvcm11bGEgPSBtcGcgfiB3dCArIGRpc3AgIyB0aGUgZm9ybXVsYSBmb3IgdGhlIHJlZ19jb2VmIGZ1bmN0aW9uCikKCnJlZ19ib290CmBgYApXZSBjYW4gZ2F0aGVyIGZyb20gdGhlIG91dHB1dCB0aGF0IHRoZSBlc3RpbWF0ZSBmb3IgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGFyZSA6IAoKKiAkMzQuOTYwNTU0MDUkIGZvciB0aGUgaW50ZXJjZXB0IGNvZWZmaWNpZW50CiogJC0zLjM1MDgyNTMzJCBmb3IgdGhlIGNvZWZmaWNpZW50IGNvcnJlc3BvbmRpbmcgdG8gYGBgd3RgYGAKKiAkLTAuMDE3NzI0NzQkIGZvciB0aGUgY29lZmZpY2llbnQgY29ycmVzcG9uZGluZyB0byBgYGBkaXNwYGBgCgplYWNoIHdpdGggdGhlaXIgb3duIGJpYXMgYW5kIHN0YW5kYXJkIGVycm9yLgoKRnVydGhlcm1vcmUsIHdlIGNhbiBwcm9kdWNlIGEgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlc2UgZXN0aW1hdGVzIGFzIGZvbGxvd3MKYGBge3J9CiMgQ29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgdGhlIGJvb3RzdHJhcCBlc3RpbWF0ZXMKYm9vdC5jaShyZWdfYm9vdCxjb25mPTAuOTUpICMgOTUlIGNvbmYuIGludGVydmFsIGZvciB0aGUgaW50ZXJjZXB0IGNvZWZmaWNpZW50Cgpib290LmNpKHJlZ19ib290LCBjb25mID0gMC45NSkgIyA5NSUgY29uZi5pbnRlcnZhbCBmb3IgdGhhIGNvZWZmaWNpZW50IGNvcnJlc3BvbmRpbmcgdG8gd3QKCmJvb3QuY2kocmVnX2Jvb3QsIGNvbmYgPSAwLjk1KSAjIDk1JSBjb25mLmludGVydmFsIGZvciB0aGUgY29lZmZpY2VpbnQgY29ycmVzcG9uZGluZyB0byBkaXNwCmBgYApUaGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgZWFjaCBjb2VmZmljaWVudCBpcyBjYWxjdWxhdGVkIHdpdGggMyBtZXRob2RzLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgcmVmZXIgdG8gdGhlIGRvY3VtZW50YXRpb24gb2YgdGhlIGZ1bmN0aW9uIGBgYGJvb3QuY2lgYGAuCgojIyBKYWNra25pZmUKVG8gZG8gdGhlIGphY2trbmlmZSBtZXRob2QgZm9yIGVzdGltYXRpbmcgYSBkaXN0cmlidXRpb24ncyBwYXJhbWV0ZXIgYnkgcmVzYW1wbGluZywgd2UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYGBgamFja2tuaWZlYGBgIGZyb20gdGhlIGBgYGJvb3RzdHJhcGBgYCBwYWNrYWdlLgoKYGBge3J9CmxpYnJhcnkoYm9vdHN0cmFwKSAjIExvYWQgdGhlIGxpYnJhcnkgKGluc3RhbGwgaXQgZmlyc3QpCmBgYAoKVGhlIHN5bnRheCBpcyBhcyBmb2xsb3dzIDogCgpgYGAKamFja2tuaWZlKHgsCiAgICAgICAgICB0aGV0YSwuLi4pCmBgYApUaGUgaW5wdXQgYXJndW1lbnRzIGFyZQoKKiB4IDogbnVtZXJpYyB2ZWN0b3Igb2Ygc2FtcGxlIGRhdGEKKiB0aGV0YSA6IGEgZnVuY3Rpb24gd2hpY2ggd2lsbCBjYWxjdWxhdGUgdGhlIHN0YXRpc3RpYyBvZiBpbnRlcmVzdCBpZiBhcHBsaWVkIHRvIGBgYHhgYGAuIE5vdGUgdGhhdCB1bmxpa2UgZm9yIHRoZSBgYGBib290YGBgIGZ1bmN0aW9uLCBgYGB0aGV0YWBgYCBuZWVkIG5vdCBoYXZlIGF0IGxlYXN0IDIgaW5wdXQgYXJndW1lbnRzLgoqIC4uLiA6IGFkZGl0aW9uYWwgYXJndW1lbnRzIGZvciB0aGUgYGBgYHRoZXRhYGBgIGZ1bmN0aW9uCgojIyMgRXhhbXBsZQoxLiBTdXBwb3NlIGZyb20gdGhlIGBgYG10Y2Fyc2BgYCBkYXRhc2V0LCB3ZSB3YW50IHRvIGVzdGltYXRlIHRoZSBtZWFuIG9mIGNhcidzIG1pbGVzIHBlciBnYWxsb24gYnkgdXNpbmcgdGhlIGphY2trbmlmZSBwcm9jZWR1cmUKYGBge3J9CiMgVGhlIGRhdGEKY2FyX21wZyA9IG10Y2FycyRtcGcKCiMgRGVmaW5lIHRoZSBtZWFuIGZ1bmN0aW9uIGZvciB0aGV0YQptZWFuX2phY2sgPSBmdW5jdGlvbih4KXsKICByZXR1cm4obWVhbih4KSkKfQoKIyBKYWNra25pZmUKamFja19yZXMgPSBqYWNra25pZmUoeCxtZWFuX2phY2spCgpqYWNrX3JlcwpgYGAKVGhlIGZ1bmN0aW9uIGBgYGphY2trbmlmZWBgYCByZXR1cm5zIGEgbGlzdCBvZiBvYmplY3RzLCB0aGV5IGFyZSB0aGUgZXN0aW1hdGVzIG9mIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgZXN0aW1hdGVkIHBhcmFtZXRlciwgYmlhcyBvZiB0aGUgZXN0aW1hdGVkIHBhcmFtZXRlciwgYW5kIHRoZSAoLWkpIHZhbHVlcyBvZiB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcyAoZXN0aW1hdGVzIG9mIHRoZSBwYXJhbWV0ZXIsIHdpdGggdGhlIGl0aCBvYnNlcnZhdGlvbiByZW1vdmVkKSwgd2hpY2ggY2FuIGJlIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBqYWNra25pZmUgZXN0aW1hdGUsIGJ5IHRha2luZyBpdHMgbWVhbi4KYGBge3J9CiMgVGhlIGphY2trbmlmZSBwYXJhbWV0ZXIgZXN0aW1hdGUKamFja19tZWFuX2VzdCA9IG1lYW4oamFja19yZXMkamFjay52YWx1ZXMpCmphY2tfbWVhbl9lc3QKYGBgCldlIHNlZSB0aGF0IHRoZSBqYWNra25pZmUgZXN0aW1hdGUgZm9yIHRoZSBtZWFuIGlzIDQyLjk4LCBhbmQgdGhpcyBlc3RpbWF0ZSBoYXMgYW4gZXN0aW1hdGUgc3RhbmRhcmQgZXJyb3Igb2YgMy42NDQzNCwgYW5kIGFuIGVzdGltYXRlIGJpYXMgb2YgMC4K