Background

In this course we will explain the theory behind common statistical tests to compare two or more groups. We will illustrate this using biologically-motivated examples and R code that uses the “tidyverse” techniques of data manipulation and visualization.

To install the entire collection of tidyverse packages (which includes other useful data-related packages that we will not use today), you may wish to use the following:-

## May take some time!
install.packages("tidyverse")

Statistics has been a fundamental part of the R language from the beginning. A vast array of statistical tests and associated visualizations are supported. However, the functions to perform these predate the creation of the tidyverse and are used in a manner that might be somewhat counter-intuitive to someone that is familiar with the tidyverse. Therefore we will use a couple of add-on packages that have been created to perform statistics in a manner that is compatible with the tidyverse eco-system.

The specific set of required packages is as follows:-

install.packages(c("dplyr",
                   "ggplot2",
                   "readr",
                   "readxl",
                   "rstatix",
                   "ggpubr",
                   "rmarkdown",
                   "tidyr",
                   "vcd")) 

Part I - Contingency tables

When working with categorical variables, we are usually interested in the frequencies of the different categories in our sample. To display data for two or more categorical variables, cross-tabulations, or contingency tables, are commonly used - with 2 x 2 tables being the simplest. We can then test whether there is an association between the row factor and the column factor by a chi-squared test or a Fisher’s exact test.

To demonstrate the analysis of contingency tables we will use a dataset provided with the vcd package. You will need to install this package using the install.packages R function.

install.packages("vcd")
## Load the packages we will need
library(dplyr)
library(ggplot2)
library(rstatix)
library(ggpubr)
library(vcd)

The data frame Arthritis should then be accessible which is described as:-

Data from Koch & Edwards (1988) from a double-blind clinical trial investigating a new treatment for rheumatoid arthritis.

#let's use the'Arthritis' dataset in the 'vcd' package 
Arthritis

The count function, included in dplyr can be used to give a tabulation of the values in any column in the data frame.

##one way table (count of one variable)
count(Arthritis,Sex)

We can quickly visualise these counts as a barplot with ggplot2. The geom_bar plot type will automatically count the number of observations that will be plotted on the y-axis.

ggplot(Arthritis, aes(x = Sex)) + geom_bar()

The function freq_table from rstatix is another way of making the counts and will also add the proportions as an extra column.

#to get proportion of males and females
freq_table(Arthritis,Sex) 

The count function can also compare two columns from a data frame if both columns are given as arguments.

count(Arthritis,Sex,Improved)

These can also be plotted, but this time we have to use geom_col to specify the values for the y-axis.

count(Arthritis,Sex,Improved) %>% 
ggplot(aes(x = Sex, fill = Improved, y = n)) + geom_col()

count(Arthritis,Sex,Improved) %>% 
ggplot(aes(x = Sex, fill = Improved, y = n)) + geom_col(position = "dodge")

The freq_table function can be used to calculate the proportions. However, we need to pay attention to the order in which the variables are specified as this will dictate how the proportions are calculated

Compare the output of:-

freq_table(Arthritis,Sex, Improved)

to:-

freq_table(Arthritis,Improved, Sex)

Having explored our data, we can now perform statistical testing. The function chisq_test can be used to assess whether differences in proportions are significant or not. We actually don’t need to calculate the proportions; R will do this for us.

However, we need to re-format the data slightly into a wide table rather than the default long nature of a data frame in the tidyverse. We the pivot_wider function we create a two-by-two table that is typically used for a contingency analysis.

Arthritis %>% 
  count(Improved, Treatment) %>% 
  tidyr::pivot_wider(values_from = n,names_from = Improved)
##would also work with names_from Treatment

And now remove the Treatment column as the chisq_test function is only expecting numeric data. The results are presented in a tidyverse tibble that and we can interpret the test statistics and p-value from this table

Arthritis %>% 
  count(Improved, Treatment) %>% 
  tidyr::pivot_wider(values_from = n,names_from = Improved) %>% 
select(-Treatment)  %>% 
  chisq_test()

Note that the only line of code to perform the test is chisq_test. The preceding lines are used to manipulate our data into the correct format. If our data are already in a numeric table we might be able to use chisq_test directly.

The chi-squared test works by comparing the frequencies in each cell of our table to what we would expect by chance (i.e. if there was no significant association). If we want to see what the expected frequencies would be we can run expected_freq after chisq_test.

Arthritis %>% 
  count(Improved, Treatment) %>% 
  tidyr::pivot_wider(values_from = n,names_from = Improved) %>%
select(-Treatment)  %>% 
  chisq_test() %>% expected_freq()
     None     Some   Marked
[1,] 21.5 7.166667 14.33333
[2,] 20.5 6.833333 13.66667

However, the chisq_test function is not appropriate in all circumstances.

Arthritis %>% 
  count(Improved, Sex) %>% 
  tidyr::pivot_wider(values_from = n,names_from = Improved) %>% 
  select(-Sex)  %>% 
  chisq_test 
Warning: Chi-squared approximation may be incorrect

The Fisher test is recommended for tables with low numbers of observations (e.g. when more than 20% of cells have expected frequencies < 5)

Arthritis %>% 
  count(Improved, Sex) %>% 
  tidyr::pivot_wider(values_from = n,names_from = Improved) %>% 
 select(-Sex)  %>% 
  fisher_test

In this case we had some low expected frequencies so the Fisher test was more appropriate

Arthritis %>% 
  count(Improved, Sex) %>% 
  tidyr::pivot_wider(values_from = n,names_from = Improved) %>% 
  select(-Sex)  %>% 
  chisq_test %>% 
  expected_freq()
Warning: Chi-squared approximation may be incorrect
     None     Some    Marked
[1,] 29.5 9.833333 19.666667
[2,] 12.5 4.166667  8.333333

Exercise

1- Read the excel file called Ex Biostat P1.xlsx into R (see below for the required code). We recommend using make_clean_names function from rstatix to make sure the column names are “clean” (i.e. without spaces or other characters that could cause issues for R).

2- Use the counts function to make a cross-tabulation of Tumor grade against Gender

3- Determine the proportion of Grade III tumors within females

4- Use the appropriate test to check if the tumor grade depends on the gender

## the readxl package is required to read xls and xlsx files into R
## However, csv and tsv files are recommended to store data
tab <- readxl::read_xlsx("data/EX Biostat P1.xlsx") %>% 
  make_clean_names()

Part II - How to assess normality

We will read some example data to illustrate how one would test for a normally-distributed variable. This property is important as it influences which test we should use.

One of the best ways of displaying data is by using a graph. Graphs can make both simple and complex data easier to understand by making it easier to spot trends and patterns. We can use plots to view the distribution of our data (minimum, maximum, mid-point, spread etc) and to ensure that the values in our dataset seem realistic (e.g. no outliers). Many statistical tests rely on the assumption that the data are normally distributed.

The data for this section are to be found in the file normal_example.csv in the data folder. You will need to specify the file path accordingly.

library(readr)
df1 <- read_csv("data/normal_example.csv")

We can inspect the data in RStudio and discover that it consists of a tidy dataset with numeric values in a column called Values and a column Var to indicate a variable name (x)

View(df1)

Various graphical methods are available to assess the distribution. The first of which is a histogram. In this graph the data are split into “bins” and the value on the y axis corresponds to the number of observations in that bin. The user only has to specify the variable to be plotted, and function takes care of the binning. From this plot we can judge what the average value of the data is, and the spread.

Histograms can be made in ggplot2 by using the geom_hist function (or indeed the base hist function). Here we are going to make use of the gghistogram function from ggpubr as it is a bit more convenient.

gghistogram(df1,x="Value")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

When assessing the distribution of a variable, you might be tempted to plot the histogram and density on the same plot. gghistogram has the argument add_density, which should do the job.

gghistogram(df1,x="Value",add_density = TRUE)
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

However, this doesn’t work. If you check the y-axis limits on the histogram and density plots, you’ll notice they are on a different scale. Conveniently there is an argument in gghistogram that solves this issue.

gghistogram(df1,x="Value",
            add_density = TRUE,
            y="..density..")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

We can add the standard normal curve with the following code. To help distingush the standard normal we can change the colour this is plotted in.

gghistogram(df1,x="Value",add_density = TRUE,y="..density..") + stat_overlay_normal_density(col="steelblue",lwd=2,lty=2)
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

A box plot is an excellent way of displaying continuous data when you are interested in the spread of your data. The “box” of the box plot corresponds to the lower and upper quartiles of the respective observations and the bar within the box, the median. The whiskers of the box plot correspond to the distance between the lower/upper quartile and the smaller of: the smaller/largest measurement OR 1.5 times the inter quartile range. A disadvantage of the box plot is that you don’t see the exact data points. However, box plots are very useful in large datasets where plotting all of the data may give an unclear picture of the shape of your data.

Again, we use ggboxplot from ggpubr for convenience.

ggboxplot(df1, y = "Value")

A violin plot is sometimes instead of the boxplot to provide more information about the density.

ggviolin(df1, y = "Value")

Individual points can also be added with the jitter argument; avoiding over-plotting by adding random noise along the x-axis.

ggviolin(df1, y = "Value",add = "jitter")

Finally, we have a “qq-plot” which allows to compare the quantiles of our dataset against a theoretical normal distribution. If the majority of points lie on a diagonal line then the data are approximately normal. The ggqqplot function in ggpubr is the most convenient way of creating this plot.

ggqqplot(df1, x="Value")

These graphical methods are by far the easiest way to assess if a given dataset is normally-distributed. However, you do not necessarily need to generate and report all the plots for your data; just as many as you to inform your decision.

For “real-life” data, the results are unlikely to give a perfect plot, so some degree of judgement and prior experience with the data type are required. Indeed, it should be noted that the dataset visualised in the above plots was sampled from a normal distribution. Even then, the plots were not 100% convincing!

Tests for normality

Although their usage is contentious amongst statisticians, there are a few methods for testing whether variables are normally-distributed or not. If the p-value is sufficiently small from these methods then we conclude that the data are not normally distributed. However, some statisticians prefer to use graphical methods and their intuition about the data or prior knowledge of the data type (e.g. some measures are generally believed to be normally-distributed)

#shapiro test from rstatix
#p<0.05 ...difference between data and normality..data not normal
#p>0.05 ...no diff between data and normality ..data normally distributed
shapiro_test(df1,Value) 

Descriptive Statistics

When performing a statistical analysis it is common practice to report on the average and variability. Our decision about whether the data are normally-distributed will influence what measures we report. In rstatix there is a function called get_summary_stats that can calculate such summaries.

For a dataset that is normally-distributed, appropriate measures of the average and variability are the mean and standard deviation.

df1 %>% 
  get_summary_stats(type="common") 
df1 %>% 
  get_summary_stats(type = "common") %>% 
  select(mean, sd)

Exercise

1- Read the excel file called Ex Biostat P2.xlsx into R

2- Decide if the age and hospitalization days are normally distributed. Vote for your findings on wooclap

3- Calculate the appropriate descriptive statistics [mean and SD, or median and IQR] for each variable

Part III - Significance tests for continuous variables

In this part we will show how to perform tests to compare 1, 2 (or more) continuous variables. The dataset, provided by MASH at The University of Sheffield, describes individuals that have been following different diets and their age and gender. The main goal of interest is to determine which of three competing diet regimes results in the greatest weight loss. However, we can use the dataset to demonstrate other types of test.

diet <- read_csv("data/diet.csv")
diet

One-sample test

The first hypothesis we will test is whether the people in the study are overweight or not. This first involves some manipulation of the table to calculate an extra variable; the Body Mass Index (BMI). We will test if people in our study are overweight, where overweight is defined as having a BMI over 25.

\(BMI = weight / height^2\)

(where the weight is measured in kg, and the height in metres)

Exercise (short) - Add a new variable to the data frame for the BMI of each person + you might want to do this in multiple steps using the %>% notation

diet <- diet %>% 
  mutate(BMI =initial.weight / (height/100)^2)

We can now test our new variable for normality using the plots and tests from earlier, although we will not show all the plots here.

gghistogram(diet, x = "BMI", ,add_density = TRUE,y="..density..")+ stat_overlay_normal_density(col="steelblue")

The one-sample t-test is implemented in the function t_test (as are the various types of t-test that we will see). The variables to be used in the test are defined using R’s formula ~ syntax.

Y ~ X

Where Y is a numeric variable and X is a categorical variable indicating the groups that each particular value of Y belongs to.

In the case of a one-sample test we are testing one numeric variable against a known or population mean (mu). As we don’t have a groups to compare, this is written as:-

Y ~ 1

diet %>% 
  t_test(BMI ~1)

The various statistical tests in rstatix all have the _test suffix (e.g. t_test, chisq_test, shapiro_test). The naming of these functions is presumably chosen to be consisent with the base R implementations which are called t.test chisq.test etc. However, the .test functions are older and do not work well with tidy data. Make sure you are using the correct function.

We get a hugely significant result! However, if we look at the description for t_test it is testing against a population mean of \(0\). It is no surprise that we get a significant result! By changing the mu argument we can perform a test to see if the people in the study are overweight to begin with (using 25 as the population or known mean).

diet %>% 
  t_test(BMI ~1,mu = 25,alternative = "greater")
## 25 is the cutoff for overweight

Two-sample tests

A two-sample t-test should be used if you want to compare the measurements of two populations. There are two types of two-sample t-test: independent (unpaired) and paired (dependent). To make the correct choice, you need to understand your underlying data.

  • An independent two-sample t-test is used when the two samples are independent of each other, e.g. comparing the mean response of two groups of patients on treatment vs. control in a clinical trial.

  • As the name suggests,a paired two-sample t-test is used when the two samples are paired, e.g. comparing the mean blood pressure of patients before and after treatment (two measurements per patient).

Back to our dataset, we might wonder if there is actually any effect due to diet so we will compare the intial and final weights.

Re-formating the dataset for tidy analysis

The dataset in it’s current form is not suitable for analysis with tidy methods. Before proceeding we need to re-format the data into two columns.

  • One column containing numeric variables (each value of weight)
  • An indicator (or categorical) variable to denote the group each numeric observation belongs to (initial or final weight)

The pivot_longer function supports this transformation. As we want to use all columns in our existing dataset we have to use the everything() shortcut to select the columns.

diet_long <- diet %>% 
  select(contains("weight")) %>% 
  tidyr::pivot_longer(everything(),names_to = "time_point", values_to = "weight") 

diet_long

So now weight contains all our observations of weight and time_point indicates when the weights were measured. The gghistogram function we introduced earlier is able to visualise data in this form, and we can use the facet.by argument to produce separate plots for each type of weight measurement.

diet_long %>% 
  gghistogram(x = "weight", facet.by = "time_point",add_density = TRUE, y = "..density..") + stat_overlay_normal_density(col="red")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

We can run the shapiro test on the two variables:-

diet_long %>%
  group_by(time_point) %>% 
  shapiro_test(weight)

The t_test function requires us to create a formula as before. This time we are doing a two-sided test, so the formula is Y ~ X where ‘Y’ is the numeric variable and ‘X’ is the categorical/groups variable.

diet_long %>%
  t_test(weight ~ time_point)

The p-value is significant and shows that overall the weights of individuals is different before and after diet. However, this test is not specifically testing for a decrease after the diet (which we would really hope to be the case). By adding an extra argument alternative=less we get a different result

diet_long %>%
  t_test(weight ~ time_point, alternative = "less")

There is also extra information that we could employ; namely that the measurements of weight are made on the same person before and after dieting. This is a classic example of when to apply a paired t-test. Again, we do not need to use a different function to perform the test; only add an argument paired=TRUE to t_test.

diet_long %>%
  t_test(weight ~ time_point, alternative = "less",paired=TRUE)

An intuitive way of comparing the distributions and showing the test result on the plot is using a boxplot. For this we will revert to ggplot2, but we are to include a p-value using the stat_compare_means function (from ggpubr.

diet_long %>% 
  ggplot(aes(x = time_point, y = weight)) + geom_boxplot() + geom_jitter(width=0.1) + 
  stat_compare_means(method = "t.test",paired=TRUE, method.args = list(alternative = "less"))


?stat_compare_means

A convenient plot in ggpubr will allow us to visualise the paired differences

ggpaired(diet, cond1="initial.weight", cond2="final.weight",line.color = "grey") + stat_compare_means(paired=TRUE,method = "t.test")

The Independent t test, with two independent groups

Lets consider that we want to compare whether males or females lost more weight during the trial. So, let’s create a new variable called weight loss

diet <- diet %>% 
mutate(wt.loss = initial.weight - final.weight)

Here we have two groups (males and females), and these can be treated as independent variables as each group has different participants than the other group.

The null hypothesis for such a test would be that the weight loss is the same between groups male and female. We seek to evidence to reject this hypothesis by calculating a test statistic.

Remember, the t-test assumes normal distribution and equal variance

Firstly, we have to check for normality:-

diet %>% 
ggdensity("wt.loss",color = "gender") + stat_overlay_normal_density()


diet %>%  
  group_by(gender) %>% 
  shapiro_test(wt.loss)

Then we check for equal variances using the Levene test


diet %>%
  levene_test(wt.loss~ gender)
Warning: group coerced to factor.
#if p value is significant --> difference in variance (unequal variance) -->  use Welch t-test (R default) 
#if p value is not significant --> no difference in variance (equal variance) --> use t-test (add argument var.equal =TRUE)

We conclude that the variances are approximately the same and both variables are normally-distributed

We can use the t_test function to perform an independent test. As before, the formula argument to t_test is the R formula notation for the test being performed. However, this time we do not need the pivot_longer function as we already the gender and weight.loss variables in our data frame

The t_test function allows various type of test to be performed by changing the appropriate arguments (see the help for t_test for details (?t_test)). For instance, we can tell the test that we believe our variances are equal or not.

diet %>% 
  t_test(wt.loss ~ gender,var.equal = TRUE) 
NA

We can see that the t-statistic we observe is consistent with the null hypothesis, that the weight loss in males and females is the same. That is, the probability of observing a t-statistic of 0.2 or more, or -0.2 or less, is quite high.

This is not a significant result (p>0.05), so there is no evidence of a difference in weight loss between males and females

diet %>% 
  ggplot(aes(x = gender, y=wt.loss)) + geom_boxplot() + stat_compare_means(method="t.test")

Non- Parametric alternatives (e.g. the Wilcoxon test)

Being able to use the t_test relies on the your data being normally-distributed. If we do not sufficient confidence in this assumption, there are different statistical tests that can be applied. Rather than calculating and comparing the means and variances of different groups they are rank-based methods. However, they still come with a set of assumptions and involve the generation of test statistics and p-values.

Independent samples = Wilcoxon rank sum test (Mann Whitney U test)

This test has many different names including the Wilcoxon, Wilcoxon two sample test, Wilcoxon-Mann-Whitney, Wilcoxon rank sum and the Mann-Whitney-U test. However, this test should not be confused with the Wilcoxon signed rank test (which is used for paired tests). To avoid confusion this test is usually referred to as the Mann-Whitney U test, which is used when the dependent variable to be examined is continuous but the assumptions for parametric tests are violated.

Fortunately, the rstatix developers have made the function to do a Wilcox-test similar to doing a t_test. The difficulty is in choosing the correct test to apply - which R will not advise you on.

Let’s go back to our example of comparing weight loss between groups male and female. The equivalent non-parametric version of the test we performed before is:-

diet %>% 
  wilcox_test(wt.loss ~ gender)

The wilcox_test is flexible in much the same way that t_test is. We can switch to applying a paired test by adding the argument paired=TRUE.

diet_long %>% 
  wilcox_test(weight ~ time_point, paired=TRUE)

Compare between more than two groups

Parametric (ANOVA)

The two-sample t-test is useful when we have just two groups of continuous data to compare. When we want to compare more than two groups, a one-way ANOVA can be used to simultaneously compare all groups, rather than carrying out several individual two-sample t-tests. The main advantage of doing this is that it reduces the number of tests being carried out, meaning that the type I error rate (the probability of seeing a significant result just by chance) does not become inflated.

In order to justify if an ANOVA test is appropriate we have to test for normality.

#by histogram
diet %>%   
gghistogram(x = "wt.loss", facet.by = "diet.type", add_density = TRUE, y = "..density..") + stat_overlay_normal_density(col="red")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

#by Q-Q plot
diet %>% 
  ggqqplot(x = "wt.loss", facet.by = "diet.type")


#by shapiro test
group_by(diet, diet.type) %>% 
  shapiro_test(wt.loss)

A one-way ANOVA compares group means by partitioning the variation in the data into between group variance and within group variance. Like the other statistical tests we have encountered, the functions in R do the hard work of calculating the statistics. The anova_test function is a tidy version of the ANOVA test.

diet %>% 
  anova_test(wt.loss ~ diet.type)
ANOVA Table (type II tests)

     Effect DFn DFd     F     p p<.05   ges
1 diet.type   2  73 5.383 0.007     * 0.129

When the test provides a significant result (like above) it tells us that there is at least on difference in the groups. However, it does not tell us which group is different. For this, we can apply a “post-hoc test” such as the Tukey test. If anova_test did not produce a significant p-value, we wouldn’t proceed with this step

diet %>%  
  tukey_hsd(wt.loss ~ diet.type)

As we have seen previously, a standard method of presenting the differences between groups is to use the stat_compare_means function to automatically add p-values to a boxplot or violin plot

ggplot(diet, aes(x = diet.type, y = wt.loss)) + geom_violin() + geom_jitter(width=0.1) + stat_compare_means(method="anova")

However, in the case of more than two groups it will only show a single p-value from the ANOVA rather than individual comparisons. We can explicitly list particular contrasts we are interested in.

my_comparisons <- list( c("A", "B"), c("A", "C"), c("B", "C") )
ggplot(diet, aes(x = diet.type, y = wt.loss)) + geom_violin() + geom_jitter(width=0.1) + 
  stat_compare_means(method = "t.test",comparisons = my_comparisons)

Alternatively, we can manually-compute the p-values and add these to the plot.

stat_res <- diet %>% 
  tukey_hsd(wt.loss ~ diet.type)


ggplot(diet, aes(x = diet.type, y = wt.loss)) + geom_violin() + geom_jitter(width=0.1)+
  stat_pvalue_manual(stat_res, label = "p.adj",y.position = c(11, 13, 15))

Non-Parametric (Kruskal Wallis)

Data that do not meet the assumptions of ANOVA (e.g. normality) can be tested using a non-parametric alternative. The Kruskal-Wallis test is derived from the one-way ANOVA, but uses ranks rather than actual observations. It is also the extension of the Mann-Whitney U test to more than two groups.

diet %>% 
  kruskal_test(wt.loss ~ diet.type)

Like the one-way ANOVA this will only tell us that at least one group is different and not specifically which group(s). The post-hoc dunn.test is recommended which also performs multiple testing correction.

diet %>% 
  dunn_test(wt.loss ~ diet.type, p.adjust.method = "bonferroni")

At this point we could be about to recommend diet C to those that wish to lose weight. However, are there any other factors in the data that we should be considering? With ggplot2 we can quite easily visualise the effects of multiple factors on the data. Lets add both gender and diet type into the plot. It now appears that diet C is having an effect on males but not females.

ggplot(diet, aes(x = diet.type, y = wt.loss,fill=gender)) + geom_boxplot()

Two-way ANOVA

The formula notation allows us to specify an interaction between gender and diet type. In other words, we are looking to see if the effect of diet type is different for males and females. In R, the formula for an interaction is specified using a * between the variables that we are interested in assessing the interaction for.

diet %>% 
  anova_test(wt.loss ~ diet.type*gender)
ANOVA Table (type II tests)

            Effect DFn DFd     F     p p<.05      ges
1        diet.type   2  70 5.619 0.005     * 0.138000
2           gender   1  70 0.031 0.860       0.000448
3 diet.type:gender   2  70 3.153 0.049     * 0.083000

This tells us that an effect exists between diet type and gender, but like before we have to run a post-hoc test to discover more

diet %>% 
  tukey_hsd(wt.loss ~ diet.type*gender) %>% 
  filter(p.adj.signif != "ns")

Exercise

The excel file ‘RCC2’ contains data about the expression levels of some genes in patients with renal cell carcinoma. In your study, you put the following hypotheses. Please test those alternative hypotheses and state whether you will accept or reject each one.

  1. Females have a higher level of E2F3 than males
  2. ANXA expression levels vary between unilateral and bilateral tumors
  3. Individuals with RCC grade II have different levels of E2F3 than those with grade III or IV
  4. The mean value of miR499 decreases significantly after treatment
  5. DFFA is higher in patients with grade IV tumors

Solutions

To be revealed during the workshop!

Exercise 1

## NEW

tab <- readxl::read_xlsx("data/EX Biostat P1.xlsx") %>% make_clean_names()
##2-Make a cross table showing the gender in the rows and tumor grade in the columns

count(tab, Gender, Tumor.grade)


##3-Define the percentage of Grade III tumors within females 
freq_table(tab, Gender, Tumor.grade)

##4-Use the appropriate test to check if the tumor grade depends on the gender 

count(tab, Gender, Tumor.grade) %>% 
    tidyr::pivot_wider(names_from = Gender,values_from = n) %>% 
  select(-Tumor.grade) %>% 
  chisq_test()
NA

Exercise 2

##1-Read the excel file called 'EX Biostat P2' into R 
Biostat2 <- readxl::read_xlsx("data/EX Biostat P2.xlsx")

gghistogram(Biostat2, x = "Age")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

gghistogram(Biostat2, x = "Age", add_density = TRUE, y = "..density..") + stat_overlay_normal_density(col="blue")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

ggqqplot(Biostat2, x = "Age")

Biostat2 %>% 
  shapiro_test(vars = "Age")
# Age is normally distributed

gghistogram(Biostat2, x = "hospitalization days")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

gghistogram(Biostat2, x = "hospitalization days", add_density = TRUE, y = "..density..") + stat_overlay_normal_density(col="blue")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

ggqqplot(Biostat2, x = "hospitalization days")

Biostat2 %>% 
  shapiro_test(vars = "hospitalization days")

# hospitalization days NOT normally distributed

get_summary_stats(Biostat2)
NA

Exercise 3

3.1

RCC2 <- readxl::read_xlsx("data/RCC2.xlsx")

gghistogram(RCC2, x = "E2F3", 
            y = "..density..",
            facet.by = "gender") + stat_overlay_normal_density(col="blue")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

RCC2 %>% 
  group_by(gender) %>% 
  shapiro_test(E2F3)
#both groups normally distributed
#parametric * 2 groups --> t-test

RCC2 %>% 
  group_by(gender) %>% 
  get_summary_stats("E2F3",type="common")

t_test(RCC2, E2F3 ~ gender,var.equal = TRUE)
#ANSWER=REJECT

ggplot(RCC2, aes(x = gender, y = E2F3)) + geom_violin() + geom_jitter(width = 0.1) + stat_compare_means(method = "t.test")

3.2

gghistogram(RCC2, x = "ANXA", facet.by = "Side")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

RCC2 %>% 
  group_by(Side) %>% 
  shapiro_test(ANXA)
#not normal
#non-parametric * 2 groups --> Wilcoxon (Mann Whitney U)
wilcox_test(RCC2, ANXA ~ Side)
#ANSWER=REJECT
ggplot(RCC2, aes(x = Side, y = ANXA)) + geom_violin() + geom_jitter() + stat_compare_means()

3.3

gghistogram(RCC2, x = "E2F3", facet.by = "Grade", y = "..density..") + stat_overlay_normal_density() 
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

#3 groups normal
#parametric * > 2 groups --> ANOVA

anova_test(RCC2, E2F3 ~ Grade)
ANOVA Table (type II tests)

  Effect DFn DFd     F     p p<.05  ges
1  Grade   2 111 5.492 0.005     * 0.09
#there is a difference but between which groups? --> post Hoc
tukey_hsd(RCC2, E2F3 ~ Grade)
#ANSWER= ACCEPT
my_comparisons <- list( c("II", "III"), c("II", "IV"), c("III", "IV") )

ggplot(RCC2, aes(x = Grade, y = E2F3)) + geom_violin() + geom_jitter(width=0.1) + stat_compare_means(method="t.test",comparisons = my_comparisons)

NA
NA

3.4

mir499_data <- 
  RCC2 %>%
  select(contains("mir499")) %>% 
  tidyr::pivot_longer(everything())
gghistogram(mir499_data, x = "value", facet.by = "name")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

mir499_data %>% 
  group_by(name) %>% 
  shapiro_test(value)
#not normal
#non-parametric * 2 paired groups --> Wilcoxon signed rank test

mir499_data %>% 
  wilcox_test(value ~ name, paired = TRUE,alternative = "greater")
mir499_data %>% 
  group_by(name) %>% 
  get_summary_stats(value)
#it increases after ttt
#ANSWER = REJECT   

3.5

gghistogram(RCC2, x = "DFFA", facet.by = "Grade")
Warning: Using `bins = 30` by default. Pick better value with the argument `bins`.

#not normal
#non-parametric * > 2 groups --> kruskal Wallis
RCC2 %>% 
  kruskal_test(DFFA ~ Grade)
#ANSWER= REJECT
#No need to perform post-hoc test
Ci0tLQp0aXRsZTogIlN0YXRpc3RpY2FsIEFuYWx5c2lzIG9mIEJpb2xvZ2ljYWwgRGF0YSIKYXV0aG9yOiAiTWFyayBEdW5uaW5nLCBOaWFtaCBFcnJpbmd0b24gYW5kIEF5YSBFbHdhemlyIgpkYXRlOiAnYHIgZm9ybWF0KFN5cy50aW1lKCksICJMYXN0IG1vZGlmaWVkOiAlZCAlYiAlWSIpYCcKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgY3NzOiBzdHlsZXNoZWV0cy9zdHlsZXMuY3NzCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSxtZXNzYWdlPUZBTFNFKQpgYGAKCiMgTGluayB0byB0ZXN0IHN1bW1hcnkgc2xpZGUKCi0gW1N1bW1hcnkgc2xpZGUgZm9yIHBhcnQgMyBzdGF0XShTdW1tYXJ5X3NsaWRlLnBwdHgpCgojIEJhY2tncm91bmQKCkluIHRoaXMgY291cnNlIHdlIHdpbGwgZXhwbGFpbiB0aGUgdGhlb3J5IGJlaGluZCBjb21tb24gc3RhdGlzdGljYWwgdGVzdHMgdG8gY29tcGFyZSB0d28gb3IgbW9yZSBncm91cHMuIFdlIHdpbGwgaWxsdXN0cmF0ZSB0aGlzIHVzaW5nIGJpb2xvZ2ljYWxseS1tb3RpdmF0ZWQgZXhhbXBsZXMgYW5kIFIgY29kZSB0aGF0IHVzZXMgdGhlICJ0aWR5dmVyc2UiIHRlY2huaXF1ZXMgb2YgZGF0YSBtYW5pcHVsYXRpb24gYW5kIHZpc3VhbGl6YXRpb24uCgpUbyBpbnN0YWxsIHRoZSBlbnRpcmUgY29sbGVjdGlvbiBvZiB0aWR5dmVyc2UgcGFja2FnZXMgKHdoaWNoIGluY2x1ZGVzIG90aGVyIHVzZWZ1bCBkYXRhLXJlbGF0ZWQgcGFja2FnZXMgdGhhdCB3ZSB3aWxsIG5vdCB1c2UgdG9kYXkpLCB5b3UgbWF5IHdpc2ggdG8gdXNlIHRoZSBmb2xsb3dpbmc6LQoKYGBge3IgZXZhbD1GQUxTRX0KIyMgTWF5IHRha2Ugc29tZSB0aW1lIQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQpgYGAKClN0YXRpc3RpY3MgaGFzIGJlZW4gYSBmdW5kYW1lbnRhbCBwYXJ0IG9mIHRoZSBSIGxhbmd1YWdlIGZyb20gdGhlIGJlZ2lubmluZy4gQSB2YXN0IGFycmF5IG9mIHN0YXRpc3RpY2FsIHRlc3RzIGFuZCBhc3NvY2lhdGVkIHZpc3VhbGl6YXRpb25zIGFyZSBzdXBwb3J0ZWQuIEhvd2V2ZXIsIHRoZSBmdW5jdGlvbnMgdG8gcGVyZm9ybSB0aGVzZSBwcmVkYXRlIHRoZSBjcmVhdGlvbiBvZiB0aGUgYHRpZHl2ZXJzZWAgYW5kIGFyZSB1c2VkIGluIGEgbWFubmVyIHRoYXQgbWlnaHQgYmUgc29tZXdoYXQgY291bnRlci1pbnR1aXRpdmUgdG8gc29tZW9uZSB0aGF0IGlzIGZhbWlsaWFyIHdpdGggdGhlIGB0aWR5dmVyc2VgLiBUaGVyZWZvcmUgd2Ugd2lsbCB1c2UgYSBjb3VwbGUgb2YgYWRkLW9uIHBhY2thZ2VzIHRoYXQgaGF2ZSBiZWVuIGNyZWF0ZWQgdG8gcGVyZm9ybSBzdGF0aXN0aWNzIGluIGEgbWFubmVyIHRoYXQgaXMgY29tcGF0aWJsZSB3aXRoIHRoZSB0aWR5dmVyc2UgZWNvLXN5c3RlbS4KCi0gW3JzdGF0aXg6IFN0YXRpc3RpY2FsIHRlc3RpbmcgZm9yIHRoZSB0aWR5dmVyc2VdKGh0dHBzOi8vcnBrZ3MuZGF0YW5vdmlhLmNvbS9yc3RhdGl4LykKLSBbZ2dwdWJyOiDigJhnZ3Bsb3Qy4oCZIEJhc2VkIFB1YmxpY2F0aW9uIFJlYWR5IFBsb3RzXShodHRwczovL3Jwa2dzLmRhdGFub3ZpYS5jb20vZ2dwdWJyLykKCgpUaGUgc3BlY2lmaWMgc2V0IG9mIHJlcXVpcmVkIHBhY2thZ2VzIGlzIGFzIGZvbGxvd3M6LQoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcyhjKCJkcGx5ciIsCiAgICAgICAgICAgICAgICAgICAiZ2dwbG90MiIsCiAgICAgICAgICAgICAgICAgICAicmVhZHIiLAogICAgICAgICAgICAgICAgICAgInJlYWR4bCIsCiAgICAgICAgICAgICAgICAgICAicnN0YXRpeCIsCiAgICAgICAgICAgICAgICAgICAiZ2dwdWJyIiwKICAgICAgICAgICAgICAgICAgICJybWFya2Rvd24iLAogICAgICAgICAgICAgICAgICAgInRpZHlyIiwKICAgICAgICAgICAgICAgICAgICJ2Y2QiKSkgCmBgYAoKCgojIFBhcnQgSSAtIENvbnRpbmdlbmN5IHRhYmxlcwoKV2hlbiB3b3JraW5nIHdpdGggY2F0ZWdvcmljYWwgdmFyaWFibGVzLCB3ZSBhcmUgdXN1YWxseSBpbnRlcmVzdGVkIGluIHRoZSAqZnJlcXVlbmNpZXMqIG9mIHRoZSBkaWZmZXJlbnQgY2F0ZWdvcmllcyBpbiBvdXIgc2FtcGxlLiBUbyBkaXNwbGF5IGRhdGEgZm9yIHR3byBvciBtb3JlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgY3Jvc3MtdGFidWxhdGlvbnMsIG9yIGNvbnRpbmdlbmN5IHRhYmxlcywgYXJlIGNvbW1vbmx5IHVzZWQgLSB3aXRoIDIgeCAyIHRhYmxlcyBiZWluZyB0aGUgc2ltcGxlc3QuIFdlIGNhbiB0aGVuIHRlc3Qgd2hldGhlciB0aGVyZSBpcyBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuIHRoZSByb3cgZmFjdG9yIGFuZCB0aGUgY29sdW1uIGZhY3RvciBieSBhICpjaGktc3F1YXJlZCB0ZXN0KiBvciBhICpGaXNoZXLigJlzIGV4YWN0IHRlc3QqLgoKVG8gZGVtb25zdHJhdGUgdGhlIGFuYWx5c2lzIG9mIGNvbnRpbmdlbmN5IHRhYmxlcyB3ZSB3aWxsIHVzZSBhIGRhdGFzZXQgcHJvdmlkZWQgd2l0aCB0aGUgYHZjZGAgcGFja2FnZS4gWW91IHdpbGwgbmVlZCB0byBpbnN0YWxsIHRoaXMgcGFja2FnZSB1c2luZyB0aGUgYGluc3RhbGwucGFja2FnZXNgIFIgZnVuY3Rpb24uCgoKYGBge3IgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygidmNkIikKYGBgCgoKYGBge3J9CiMjIExvYWQgdGhlIHBhY2thZ2VzIHdlIHdpbGwgbmVlZApsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocnN0YXRpeCkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkodmNkKQpgYGAKCgpUaGUgZGF0YSBmcmFtZSBgQXJ0aHJpdGlzYCBzaG91bGQgdGhlbiBiZSBhY2Nlc3NpYmxlIHdoaWNoIGlzIGRlc2NyaWJlZCBhczotIAoKPiBEYXRhIGZyb20gS29jaCAmIEVkd2FyZHMgKDE5ODgpIGZyb20gYSBkb3VibGUtYmxpbmQgY2xpbmljYWwgdHJpYWwgaW52ZXN0aWdhdGluZyBhIG5ldyB0cmVhdG1lbnQgZm9yIHJoZXVtYXRvaWQgYXJ0aHJpdGlzLgoKYGBge3J9CiNsZXQncyB1c2UgdGhlJ0FydGhyaXRpcycgZGF0YXNldCBpbiB0aGUgJ3ZjZCcgcGFja2FnZSAKQXJ0aHJpdGlzCmBgYAoKVGhlIGBjb3VudGAgZnVuY3Rpb24sIGluY2x1ZGVkIGluIGBkcGx5cmAgY2FuIGJlIHVzZWQgdG8gZ2l2ZSBhIHRhYnVsYXRpb24gb2YgdGhlIHZhbHVlcyBpbiBhbnkgY29sdW1uIGluIHRoZSBkYXRhIGZyYW1lLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KIyNvbmUgd2F5IHRhYmxlIChjb3VudCBvZiBvbmUgdmFyaWFibGUpCmNvdW50KEFydGhyaXRpcyxTZXgpCmBgYAoKV2UgY2FuIHF1aWNrbHkgdmlzdWFsaXNlIHRoZXNlIGNvdW50cyBhcyBhICpiYXJwbG90KiB3aXRoIGBnZ3Bsb3QyYC4gVGhlIGBnZW9tX2JhcmAgcGxvdCB0eXBlIHdpbGwgYXV0b21hdGljYWxseSBjb3VudCB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyB0aGF0IHdpbGwgYmUgcGxvdHRlZCBvbiB0aGUgeS1heGlzLgoKYGBge3J9CmdncGxvdChBcnRocml0aXMsIGFlcyh4ID0gU2V4KSkgKyBnZW9tX2JhcigpCmBgYAoKVGhlIGZ1bmN0aW9uIGBmcmVxX3RhYmxlYCBmcm9tIGByc3RhdGl4YCBpcyBhbm90aGVyIHdheSBvZiBtYWtpbmcgdGhlIGNvdW50cyBhbmQgd2lsbCBhbHNvIGFkZCB0aGUgcHJvcG9ydGlvbnMgYXMgYW4gZXh0cmEgY29sdW1uLgoKYGBge3J9CiN0byBnZXQgcHJvcG9ydGlvbiBvZiBtYWxlcyBhbmQgZmVtYWxlcwpmcmVxX3RhYmxlKEFydGhyaXRpcyxTZXgpIApgYGAKClRoZSBgY291bnRgIGZ1bmN0aW9uIGNhbiBhbHNvIGNvbXBhcmUgdHdvIGNvbHVtbnMgZnJvbSBhIGRhdGEgZnJhbWUgaWYgYm90aCBjb2x1bW5zIGFyZSBnaXZlbiBhcyBhcmd1bWVudHMuCgpgYGB7cn0KY291bnQoQXJ0aHJpdGlzLFNleCxJbXByb3ZlZCkKYGBgCgpUaGVzZSBjYW4gYWxzbyBiZSBwbG90dGVkLCBidXQgdGhpcyB0aW1lIHdlIGhhdmUgdG8gdXNlIGBnZW9tX2NvbGAgdG8gc3BlY2lmeSB0aGUgdmFsdWVzIGZvciB0aGUgeS1heGlzLgoKYGBge3J9CmNvdW50KEFydGhyaXRpcyxTZXgsSW1wcm92ZWQpICU+JSAKZ2dwbG90KGFlcyh4ID0gU2V4LCBmaWxsID0gSW1wcm92ZWQsIHkgPSBuKSkgKyBnZW9tX2NvbCgpCmBgYAoKCmBgYHtyfQpjb3VudChBcnRocml0aXMsU2V4LEltcHJvdmVkKSAlPiUgCmdncGxvdChhZXMoeCA9IFNleCwgZmlsbCA9IEltcHJvdmVkLCB5ID0gbikpICsgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKClRoZSBgZnJlcV90YWJsZWAgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9ucy4gSG93ZXZlciwgd2UgbmVlZCB0byBwYXkgYXR0ZW50aW9uIHRvIHRoZSBvcmRlciBpbiB3aGljaCB0aGUgdmFyaWFibGVzIGFyZSBzcGVjaWZpZWQgYXMgdGhpcyB3aWxsIGRpY3RhdGUgaG93IHRoZSBwcm9wb3J0aW9ucyBhcmUgY2FsY3VsYXRlZAoKQ29tcGFyZSB0aGUgb3V0cHV0IG9mOi0KCmBgYHtyfQpmcmVxX3RhYmxlKEFydGhyaXRpcyxTZXgsIEltcHJvdmVkKQpgYGAKCnRvOi0gCgpgYGB7cn0KZnJlcV90YWJsZShBcnRocml0aXMsSW1wcm92ZWQsIFNleCkKYGBgCgoKSGF2aW5nIGV4cGxvcmVkIG91ciBkYXRhLCB3ZSBjYW4gbm93IHBlcmZvcm0gc3RhdGlzdGljYWwgdGVzdGluZy4gVGhlIGZ1bmN0aW9uIGBjaGlzcV90ZXN0YCBjYW4gYmUgdXNlZCB0byBhc3Nlc3Mgd2hldGhlciBkaWZmZXJlbmNlcyBpbiBwcm9wb3J0aW9ucyBhcmUgc2lnbmlmaWNhbnQgb3Igbm90LiBXZSBhY3R1YWxseSBkb24ndCBuZWVkIHRvIGNhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbnM7IFIgd2lsbCBkbyB0aGlzIGZvciB1cy4KCkhvd2V2ZXIsIHdlIG5lZWQgdG8gcmUtZm9ybWF0IHRoZSBkYXRhIHNsaWdodGx5IGludG8gYSAqd2lkZSogdGFibGUgcmF0aGVyIHRoYW4gdGhlIGRlZmF1bHQgKmxvbmcqIG5hdHVyZSBvZiBhIGRhdGEgZnJhbWUgaW4gdGhlIGB0aWR5dmVyc2VgLiBXZSB0aGUgYHBpdm90X3dpZGVyYCBmdW5jdGlvbiB3ZSBjcmVhdGUgYSB0d28tYnktdHdvIHRhYmxlIHRoYXQgaXMgdHlwaWNhbGx5IHVzZWQgZm9yIGEgY29udGluZ2VuY3kgYW5hbHlzaXMuCgpgYGB7cn0KQXJ0aHJpdGlzICU+JSAKICBjb3VudChJbXByb3ZlZCwgVHJlYXRtZW50KSAlPiUgCiAgdGlkeXI6OnBpdm90X3dpZGVyKHZhbHVlc19mcm9tID0gbixuYW1lc19mcm9tID0gSW1wcm92ZWQpCiMjd291bGQgYWxzbyB3b3JrIHdpdGggbmFtZXNfZnJvbSBUcmVhdG1lbnQKYGBgCgpBbmQgbm93IHJlbW92ZSB0aGUgYFRyZWF0bWVudGAgY29sdW1uIGFzIHRoZSBgY2hpc3FfdGVzdGAgZnVuY3Rpb24gaXMgb25seSBleHBlY3RpbmcgbnVtZXJpYyBkYXRhLiBUaGUgcmVzdWx0cyBhcmUgcHJlc2VudGVkIGluIGEgYHRpZHl2ZXJzZWAgdGliYmxlIHRoYXQgYW5kIHdlIGNhbiBpbnRlcnByZXQgdGhlIHRlc3Qgc3RhdGlzdGljcyBhbmQgcC12YWx1ZSBmcm9tIHRoaXMgdGFibGUKCmBgYHtyfQpBcnRocml0aXMgJT4lIAogIGNvdW50KEltcHJvdmVkLCBUcmVhdG1lbnQpICU+JSAKICB0aWR5cjo6cGl2b3Rfd2lkZXIodmFsdWVzX2Zyb20gPSBuLG5hbWVzX2Zyb20gPSBJbXByb3ZlZCkgJT4lIApzZWxlY3QoLVRyZWF0bWVudCkgICU+JSAKICBjaGlzcV90ZXN0KCkKYGBgCgpOb3RlIHRoYXQgdGhlIG9ubHkgbGluZSBvZiBjb2RlIHRvIHBlcmZvcm0gdGhlIHRlc3QgaXMgYGNoaXNxX3Rlc3RgLiBUaGUgcHJlY2VkaW5nIGxpbmVzIGFyZSB1c2VkIHRvIG1hbmlwdWxhdGUgb3VyIGRhdGEgaW50byB0aGUgY29ycmVjdCBmb3JtYXQuIElmIG91ciBkYXRhIGFyZSBhbHJlYWR5IGluIGEgbnVtZXJpYyB0YWJsZSB3ZSBtaWdodCBiZSBhYmxlIHRvIHVzZSBgY2hpc3FfdGVzdGAgZGlyZWN0bHkuCgpUaGUgY2hpLXNxdWFyZWQgdGVzdCB3b3JrcyBieSBjb21wYXJpbmcgdGhlIGZyZXF1ZW5jaWVzIGluIGVhY2ggY2VsbCBvZiBvdXIgdGFibGUgdG8gd2hhdCB3ZSB3b3VsZCBleHBlY3QgYnkgY2hhbmNlIChpLmUuIGlmIHRoZXJlIHdhcyBubyBzaWduaWZpY2FudCBhc3NvY2lhdGlvbikuIElmIHdlIHdhbnQgdG8gc2VlIHdoYXQgdGhlIGV4cGVjdGVkIGZyZXF1ZW5jaWVzIHdvdWxkIGJlIHdlIGNhbiBydW4gYGV4cGVjdGVkX2ZyZXFgIGFmdGVyIGBjaGlzcV90ZXN0YC4KCmBgYHtyfQpBcnRocml0aXMgJT4lIAogIGNvdW50KEltcHJvdmVkLCBUcmVhdG1lbnQpICU+JSAKICB0aWR5cjo6cGl2b3Rfd2lkZXIodmFsdWVzX2Zyb20gPSBuLG5hbWVzX2Zyb20gPSBJbXByb3ZlZCkgJT4lCnNlbGVjdCgtVHJlYXRtZW50KSAgJT4lIAogIGNoaXNxX3Rlc3QoKSAlPiUgZXhwZWN0ZWRfZnJlcSgpCmBgYAoKSG93ZXZlciwgdGhlIGBjaGlzcV90ZXN0YCBmdW5jdGlvbiBpcyBub3QgYXBwcm9wcmlhdGUgaW4gYWxsIGNpcmN1bXN0YW5jZXMuCgpgYGB7cn0KQXJ0aHJpdGlzICU+JSAKICBjb3VudChJbXByb3ZlZCwgU2V4KSAlPiUgCiAgdGlkeXI6OnBpdm90X3dpZGVyKHZhbHVlc19mcm9tID0gbixuYW1lc19mcm9tID0gSW1wcm92ZWQpICU+JSAKICBzZWxlY3QoLVNleCkgICU+JSAKICBjaGlzcV90ZXN0IApgYGAKClRoZSBGaXNoZXIgdGVzdCBpcyByZWNvbW1lbmRlZCBmb3IgdGFibGVzIHdpdGggbG93IG51bWJlcnMgb2Ygb2JzZXJ2YXRpb25zIChlLmcuIHdoZW4gbW9yZSB0aGFuIDIwJSBvZiBjZWxscyBoYXZlICpleHBlY3RlZCBmcmVxdWVuY2llcyogPCA1KQoKYGBge3J9CkFydGhyaXRpcyAlPiUgCiAgY291bnQoSW1wcm92ZWQsIFNleCkgJT4lIAogIHRpZHlyOjpwaXZvdF93aWRlcih2YWx1ZXNfZnJvbSA9IG4sbmFtZXNfZnJvbSA9IEltcHJvdmVkKSAlPiUgCiBzZWxlY3QoLVNleCkgICU+JSAKICBmaXNoZXJfdGVzdApgYGAKCkluIHRoaXMgY2FzZSB3ZSBoYWQgc29tZSBsb3cgZXhwZWN0ZWQgZnJlcXVlbmNpZXMgc28gdGhlIEZpc2hlciB0ZXN0IHdhcyBtb3JlIGFwcHJvcHJpYXRlCgpgYGB7cn0KQXJ0aHJpdGlzICU+JSAKICBjb3VudChJbXByb3ZlZCwgU2V4KSAlPiUgCiAgdGlkeXI6OnBpdm90X3dpZGVyKHZhbHVlc19mcm9tID0gbixuYW1lc19mcm9tID0gSW1wcm92ZWQpICU+JSAKICBzZWxlY3QoLVNleCkgICU+JSAKICBjaGlzcV90ZXN0ICU+JSAKICBleHBlY3RlZF9mcmVxKCkKYGBgCgoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgoKKipFeGVyY2lzZSoqCgoxLSBSZWFkIHRoZSBleGNlbCBmaWxlIGNhbGxlZCBgRXggQmlvc3RhdCBQMS54bHN4YCBpbnRvIFIgKHNlZSBiZWxvdyBmb3IgdGhlIHJlcXVpcmVkIGNvZGUpLiBXZSByZWNvbW1lbmQgdXNpbmcgYG1ha2VfY2xlYW5fbmFtZXNgIGZ1bmN0aW9uIGZyb20gYHJzdGF0aXhgIHRvIG1ha2Ugc3VyZSB0aGUgY29sdW1uIG5hbWVzIGFyZSAiY2xlYW4iIChpLmUuIHdpdGhvdXQgc3BhY2VzIG9yIG90aGVyIGNoYXJhY3RlcnMgdGhhdCBjb3VsZCBjYXVzZSBpc3N1ZXMgZm9yIFIpLgoKMi0gVXNlIHRoZSBgY291bnRzYCBmdW5jdGlvbiB0byBtYWtlIGEgY3Jvc3MtdGFidWxhdGlvbiBvZiBUdW1vciBncmFkZSBhZ2FpbnN0IEdlbmRlcgoKMy0gRGV0ZXJtaW5lIHRoZSBwcm9wb3J0aW9uIG9mIEdyYWRlIElJSSB0dW1vcnMgd2l0aGluIGZlbWFsZXMgCgo0LSBVc2UgdGhlIGFwcHJvcHJpYXRlIHRlc3QgdG8gY2hlY2sgaWYgdGhlIHR1bW9yIGdyYWRlIGRlcGVuZHMgb24gdGhlIGdlbmRlciAKCmBgYHtyIGV2YWw9RkFMU0V9CiMjIHRoZSByZWFkeGwgcGFja2FnZSBpcyByZXF1aXJlZCB0byByZWFkIHhscyBhbmQgeGxzeCBmaWxlcyBpbnRvIFIKIyMgSG93ZXZlciwgY3N2IGFuZCB0c3YgZmlsZXMgYXJlIHJlY29tbWVuZGVkIHRvIHN0b3JlIGRhdGEKdGFiIDwtIHJlYWR4bDo6cmVhZF94bHN4KCJkYXRhL0VYIEJpb3N0YXQgUDEueGxzeCIpICU+JSAKICBtYWtlX2NsZWFuX25hbWVzKCkKYGBgCgo8L2Rpdj4KCiMgUGFydCBJSSAtIEhvdyB0byBhc3Nlc3Mgbm9ybWFsaXR5CgpXZSB3aWxsIHJlYWQgc29tZSBleGFtcGxlIGRhdGEgdG8gaWxsdXN0cmF0ZSBob3cgb25lIHdvdWxkIHRlc3QgZm9yIGEgbm9ybWFsbHktZGlzdHJpYnV0ZWQgdmFyaWFibGUuIFRoaXMgcHJvcGVydHkgaXMgaW1wb3J0YW50IGFzIGl0IGluZmx1ZW5jZXMgd2hpY2ggdGVzdCB3ZSBzaG91bGQgdXNlLgoKT25lIG9mIHRoZSBiZXN0IHdheXMgb2YgZGlzcGxheWluZyBkYXRhIGlzIGJ5IHVzaW5nIGEgZ3JhcGguIEdyYXBocyBjYW4gbWFrZSBib3RoIHNpbXBsZSBhbmQgY29tcGxleCBkYXRhIGVhc2llciB0byB1bmRlcnN0YW5kIGJ5IG1ha2luZyBpdCBlYXNpZXIgdG8gc3BvdCB0cmVuZHMgYW5kIHBhdHRlcm5zLiBXZSBjYW4gdXNlIHBsb3RzIHRvIHZpZXcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBvdXIgZGF0YSAobWluaW11bSwgbWF4aW11bSwgbWlkLXBvaW50LCBzcHJlYWQgZXRjKSBhbmQgdG8gZW5zdXJlIHRoYXQgdGhlIHZhbHVlcyBpbiBvdXIgZGF0YXNldCBzZWVtIHJlYWxpc3RpYyAoZS5nLiBubyBvdXRsaWVycykuIE1hbnkgc3RhdGlzdGljYWwgdGVzdHMgcmVseSBvbiB0aGUgYXNzdW1wdGlvbiB0aGF0IHRoZSBkYXRhIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZC4KClRoZSBkYXRhIGZvciB0aGlzIHNlY3Rpb24gYXJlIHRvIGJlIGZvdW5kIGluIHRoZSBmaWxlIGBub3JtYWxfZXhhbXBsZS5jc3ZgIGluIHRoZSBgZGF0YWAgZm9sZGVyLiBZb3Ugd2lsbCBuZWVkIHRvIHNwZWNpZnkgdGhlIGZpbGUgcGF0aCBhY2NvcmRpbmdseS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocmVhZHIpCmRmMSA8LSByZWFkX2NzdigiZGF0YS9ub3JtYWxfZXhhbXBsZS5jc3YiKQpgYGAKCldlIGNhbiBpbnNwZWN0IHRoZSBkYXRhIGluIFJTdHVkaW8gYW5kIGRpc2NvdmVyIHRoYXQgaXQgY29uc2lzdHMgb2YgYSB0aWR5IGRhdGFzZXQgd2l0aCBudW1lcmljIHZhbHVlcyBpbiBhIGNvbHVtbiBjYWxsZWQgYFZhbHVlc2AgYW5kIGEgY29sdW1uIGBWYXJgIHRvIGluZGljYXRlIGEgdmFyaWFibGUgbmFtZSAoYHhgKQoKYGBge3J9ClZpZXcoZGYxKQpgYGAKClZhcmlvdXMgZ3JhcGhpY2FsIG1ldGhvZHMgYXJlIGF2YWlsYWJsZSB0byBhc3Nlc3MgdGhlIGRpc3RyaWJ1dGlvbi4gVGhlIGZpcnN0IG9mIHdoaWNoIGlzIGEgKmhpc3RvZ3JhbSouIEluIHRoaXMgZ3JhcGggdGhlIGRhdGEgYXJlIHNwbGl0IGludG8gImJpbnMiIGFuZCB0aGUgdmFsdWUgb24gdGhlIGB5YCBheGlzIGNvcnJlc3BvbmRzIHRvIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoYXQgYmluLiBUaGUgdXNlciBvbmx5IGhhcyB0byBzcGVjaWZ5IHRoZSB2YXJpYWJsZSB0byBiZSBwbG90dGVkLCBhbmQgZnVuY3Rpb24gdGFrZXMgY2FyZSBvZiB0aGUgYmlubmluZy4gRnJvbSB0aGlzIHBsb3Qgd2UgY2FuIGp1ZGdlIHdoYXQgdGhlIGF2ZXJhZ2UgdmFsdWUgb2YgdGhlIGRhdGEgaXMsIGFuZCB0aGUgc3ByZWFkLgoKSGlzdG9ncmFtcyBjYW4gYmUgbWFkZSBpbiBgZ2dwbG90MmAgYnkgdXNpbmcgdGhlIGBnZW9tX2hpc3RgIGZ1bmN0aW9uIChvciBpbmRlZWQgdGhlIGJhc2UgYGhpc3RgIGZ1bmN0aW9uKS4gSGVyZSB3ZSBhcmUgZ29pbmcgdG8gbWFrZSB1c2Ugb2YgdGhlIGBnZ2hpc3RvZ3JhbWAgZnVuY3Rpb24gZnJvbSBgZ2dwdWJyYCBhcyBpdCBpcyBhIGJpdCBtb3JlIGNvbnZlbmllbnQuCgpgYGB7cn0KZ2doaXN0b2dyYW0oZGYxLHg9IlZhbHVlIikKYGBgCgoKV2hlbiBhc3Nlc3NpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHZhcmlhYmxlLCB5b3UgbWlnaHQgYmUgdGVtcHRlZCB0byBwbG90IHRoZSBoaXN0b2dyYW0gYW5kICpkZW5zaXR5KiBvbiB0aGUgc2FtZSBwbG90LiBgZ2doaXN0b2dyYW1gIGhhcyB0aGUgYXJndW1lbnQgYGFkZF9kZW5zaXR5YCwgd2hpY2ggc2hvdWxkIGRvIHRoZSBqb2IuCgpgYGB7cn0KZ2doaXN0b2dyYW0oZGYxLHg9IlZhbHVlIixhZGRfZGVuc2l0eSA9IFRSVUUpCmBgYAoKSG93ZXZlciwgdGhpcyBkb2Vzbid0IHdvcmsuIElmIHlvdSBjaGVjayB0aGUgeS1heGlzIGxpbWl0cyBvbiB0aGUgaGlzdG9ncmFtIGFuZCBkZW5zaXR5IHBsb3RzLCB5b3UnbGwgbm90aWNlIHRoZXkgYXJlIG9uIGEgZGlmZmVyZW50IHNjYWxlLiBDb252ZW5pZW50bHkgdGhlcmUgaXMgYW4gYXJndW1lbnQgaW4gYGdnaGlzdG9ncmFtYCB0aGF0IHNvbHZlcyB0aGlzIGlzc3VlLgoKCmBgYHtyfQpnZ2hpc3RvZ3JhbShkZjEseD0iVmFsdWUiLAogICAgICAgICAgICBhZGRfZGVuc2l0eSA9IFRSVUUsCiAgICAgICAgICAgIHk9Ii4uZGVuc2l0eS4uIikKYGBgCgpXZSBjYW4gYWRkIHRoZSAqc3RhbmRhcmQqIG5vcm1hbCBjdXJ2ZSB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZS4gVG8gaGVscCBkaXN0aW5ndXNoIHRoZSBzdGFuZGFyZCBub3JtYWwgd2UgY2FuIGNoYW5nZSB0aGUgY29sb3VyIHRoaXMgaXMgcGxvdHRlZCBpbi4KCmBgYHtyfQpnZ2hpc3RvZ3JhbShkZjEseD0iVmFsdWUiLGFkZF9kZW5zaXR5ID0gVFJVRSx5PSIuLmRlbnNpdHkuLiIpICsgc3RhdF9vdmVybGF5X25vcm1hbF9kZW5zaXR5KGNvbD0ic3RlZWxibHVlIixsd2Q9MixsdHk9MikKYGBgCgoKQSBib3ggcGxvdCBpcyBhbiBleGNlbGxlbnQgd2F5IG9mIGRpc3BsYXlpbmcgY29udGludW91cyBkYXRhIHdoZW4geW91IGFyZSBpbnRlcmVzdGVkIGluIHRoZSBzcHJlYWQgb2YgeW91ciBkYXRhLiBUaGUgImJveCIgb2YgdGhlIGJveCBwbG90IGNvcnJlc3BvbmRzIHRvIHRoZSBsb3dlciBhbmQgdXBwZXIgcXVhcnRpbGVzIG9mIHRoZSByZXNwZWN0aXZlIG9ic2VydmF0aW9ucyBhbmQgdGhlIGJhciB3aXRoaW4gdGhlIGJveCwgdGhlIG1lZGlhbi4gVGhlIHdoaXNrZXJzIG9mIHRoZSBib3ggcGxvdCBjb3JyZXNwb25kIHRvIHRoZSBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBsb3dlci91cHBlciBxdWFydGlsZSBhbmQgdGhlIHNtYWxsZXIgb2Y6IHRoZSBzbWFsbGVyL2xhcmdlc3QgbWVhc3VyZW1lbnQgKk9SKiAxLjUgdGltZXMgdGhlIGludGVyIHF1YXJ0aWxlIHJhbmdlLiBBIGRpc2FkdmFudGFnZSBvZiB0aGUgYm94IHBsb3QgaXMgdGhhdCB5b3UgZG9u4oCZdCBzZWUgdGhlIGV4YWN0IGRhdGEgcG9pbnRzLiBIb3dldmVyLCBib3ggcGxvdHMgYXJlIHZlcnkgdXNlZnVsIGluIGxhcmdlIGRhdGFzZXRzIHdoZXJlIHBsb3R0aW5nIGFsbCBvZiB0aGUgZGF0YSBtYXkgZ2l2ZSBhbiB1bmNsZWFyIHBpY3R1cmUgb2YgdGhlIHNoYXBlIG9mIHlvdXIgZGF0YS4KCkFnYWluLCB3ZSB1c2UgYGdnYm94cGxvdGAgZnJvbSBgZ2dwdWJyYCBmb3IgY29udmVuaWVuY2UuCgpgYGB7cn0KZ2dib3hwbG90KGRmMSwgeSA9ICJWYWx1ZSIpCmBgYAoKQSAqdmlvbGluIHBsb3QqIGlzIHNvbWV0aW1lcyBpbnN0ZWFkIG9mIHRoZSBib3hwbG90IHRvIHByb3ZpZGUgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGVuc2l0eS4KCmBgYHtyfQpnZ3Zpb2xpbihkZjEsIHkgPSAiVmFsdWUiKQpgYGAKCkluZGl2aWR1YWwgcG9pbnRzIGNhbiBhbHNvIGJlIGFkZGVkIHdpdGggdGhlIGBqaXR0ZXJgIGFyZ3VtZW50OyBhdm9pZGluZyBvdmVyLXBsb3R0aW5nIGJ5IGFkZGluZyByYW5kb20gbm9pc2UgYWxvbmcgdGhlIHgtYXhpcy4KCmBgYHtyfQpnZ3Zpb2xpbihkZjEsIHkgPSAiVmFsdWUiLGFkZCA9ICJqaXR0ZXIiKQpgYGAKCgpGaW5hbGx5LCB3ZSBoYXZlIGEgIipxcS1wbG90KiIgd2hpY2ggYWxsb3dzIHRvIGNvbXBhcmUgdGhlIHF1YW50aWxlcyBvZiBvdXIgZGF0YXNldCBhZ2FpbnN0IGEgdGhlb3JldGljYWwgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gSWYgdGhlIG1ham9yaXR5IG9mIHBvaW50cyBsaWUgb24gYSBkaWFnb25hbCBsaW5lIHRoZW4gdGhlIGRhdGEgYXJlIGFwcHJveGltYXRlbHkgbm9ybWFsLiBUaGUgYGdncXFwbG90YCBmdW5jdGlvbiBpbiBgZ2dwdWJyYCBpcyB0aGUgbW9zdCBjb252ZW5pZW50IHdheSBvZiBjcmVhdGluZyB0aGlzIHBsb3QuCgpgYGB7cn0KZ2dxcXBsb3QoZGYxLCB4PSJWYWx1ZSIpCmBgYAoKVGhlc2UgZ3JhcGhpY2FsIG1ldGhvZHMgYXJlIGJ5IGZhciB0aGUgZWFzaWVzdCB3YXkgdG8gYXNzZXNzIGlmIGEgZ2l2ZW4gZGF0YXNldCBpcyBub3JtYWxseS1kaXN0cmlidXRlZC4gSG93ZXZlciwgeW91IGRvIG5vdCBuZWNlc3NhcmlseSBuZWVkIHRvIGdlbmVyYXRlIGFuZCByZXBvcnQgYWxsIHRoZSBwbG90cyBmb3IgeW91ciBkYXRhOyBqdXN0IGFzIG1hbnkgYXMgeW91IHRvIGluZm9ybSB5b3VyIGRlY2lzaW9uLgoKRm9yICJyZWFsLWxpZmUiIGRhdGEsIHRoZSByZXN1bHRzIGFyZSB1bmxpa2VseSB0byBnaXZlIGEgcGVyZmVjdCBwbG90LCBzbyBzb21lIGRlZ3JlZSBvZiBqdWRnZW1lbnQgYW5kIHByaW9yIGV4cGVyaWVuY2Ugd2l0aCB0aGUgZGF0YSB0eXBlIGFyZSByZXF1aXJlZC4gIEluZGVlZCwgaXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQgdGhlIGRhdGFzZXQgdmlzdWFsaXNlZCBpbiB0aGUgYWJvdmUgcGxvdHMgd2FzIHNhbXBsZWQgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24uIEV2ZW4gdGhlbiwgdGhlIHBsb3RzIHdlcmUgbm90IDEwMCUgY29udmluY2luZyEKCiMjIFRlc3RzIGZvciBub3JtYWxpdHkKCkFsdGhvdWdoIHRoZWlyIHVzYWdlIGlzIGNvbnRlbnRpb3VzIGFtb25nc3Qgc3RhdGlzdGljaWFucywgdGhlcmUgYXJlIGEgZmV3IG1ldGhvZHMgZm9yIHRlc3Rpbmcgd2hldGhlciB2YXJpYWJsZXMgYXJlIG5vcm1hbGx5LWRpc3RyaWJ1dGVkIG9yIG5vdC4gSWYgdGhlIHAtdmFsdWUgaXMgc3VmZmljaWVudGx5IHNtYWxsIGZyb20gdGhlc2UgbWV0aG9kcyB0aGVuIHdlIGNvbmNsdWRlIHRoYXQgdGhlIGRhdGEgYXJlICpub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQqLiBIb3dldmVyLCBzb21lIHN0YXRpc3RpY2lhbnMgcHJlZmVyIHRvIHVzZSBncmFwaGljYWwgbWV0aG9kcyBhbmQgdGhlaXIgaW50dWl0aW9uIGFib3V0IHRoZSBkYXRhIG9yIHByaW9yIGtub3dsZWRnZSBvZiB0aGUgZGF0YSB0eXBlIChlLmcuIHNvbWUgbWVhc3VyZXMgYXJlIGdlbmVyYWxseSBiZWxpZXZlZCB0byBiZSBub3JtYWxseS1kaXN0cmlidXRlZCkKCmBgYHtyfQojc2hhcGlybyB0ZXN0IGZyb20gcnN0YXRpeAojcDwwLjA1IC4uLmRpZmZlcmVuY2UgYmV0d2VlbiBkYXRhIGFuZCBub3JtYWxpdHkuLmRhdGEgbm90IG5vcm1hbAojcD4wLjA1IC4uLm5vIGRpZmYgYmV0d2VlbiBkYXRhIGFuZCBub3JtYWxpdHkgLi5kYXRhIG5vcm1hbGx5IGRpc3RyaWJ1dGVkCnNoYXBpcm9fdGVzdChkZjEsVmFsdWUpIApgYGAKCiMjIERlc2NyaXB0aXZlIFN0YXRpc3RpY3MKCldoZW4gcGVyZm9ybWluZyBhIHN0YXRpc3RpY2FsIGFuYWx5c2lzIGl0IGlzIGNvbW1vbiBwcmFjdGljZSB0byByZXBvcnQgb24gdGhlIGF2ZXJhZ2UgYW5kIHZhcmlhYmlsaXR5LiBPdXIgZGVjaXNpb24gYWJvdXQgd2hldGhlciB0aGUgZGF0YSBhcmUgbm9ybWFsbHktZGlzdHJpYnV0ZWQgd2lsbCBpbmZsdWVuY2Ugd2hhdCBtZWFzdXJlcyB3ZSByZXBvcnQuIEluIGByc3RhdGl4YCB0aGVyZSBpcyBhIGZ1bmN0aW9uIGNhbGxlZCBgZ2V0X3N1bW1hcnlfc3RhdHNgIHRoYXQgY2FuIGNhbGN1bGF0ZSBzdWNoIHN1bW1hcmllcy4KCkZvciBhIGRhdGFzZXQgdGhhdCBpcyBub3JtYWxseS1kaXN0cmlidXRlZCwgYXBwcm9wcmlhdGUgbWVhc3VyZXMgb2YgdGhlIGF2ZXJhZ2UgYW5kIHZhcmlhYmlsaXR5IGFyZSB0aGUgKm1lYW4qIGFuZCAqc3RhbmRhcmQgZGV2aWF0aW9uKi4gCgoKYGBge3J9CmRmMSAlPiUgCiAgZ2V0X3N1bW1hcnlfc3RhdHModHlwZT0iY29tbW9uIikgCmBgYAoKYGBge3J9CmRmMSAlPiUgCiAgZ2V0X3N1bW1hcnlfc3RhdHModHlwZSA9ICJjb21tb24iKSAlPiUgCiAgc2VsZWN0KG1lYW4sIHNkKQpgYGAKCgo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+CioqRXhlcmNpc2UqKgoKMS0gUmVhZCB0aGUgZXhjZWwgZmlsZSBjYWxsZWQgYEV4IEJpb3N0YXQgUDIueGxzeGAgaW50byBSIAoKMi0gRGVjaWRlIGlmIHRoZSBhZ2UgYW5kIGhvc3BpdGFsaXphdGlvbiBkYXlzIGFyZSBub3JtYWxseSBkaXN0cmlidXRlZC4gVm90ZSBmb3IgeW91ciBmaW5kaW5ncyBvbiAqKndvb2NsYXAqKgoKMy0gQ2FsY3VsYXRlIHRoZSBhcHByb3ByaWF0ZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIFttZWFuIGFuZCBTRCwgIG9yIG1lZGlhbiBhbmQgSVFSXSBmb3IgZWFjaCB2YXJpYWJsZQoKPC9kaXY+CgoKCiMgUGFydCBJSUkgLSBTaWduaWZpY2FuY2UgdGVzdHMgZm9yIGNvbnRpbnVvdXMgdmFyaWFibGVzCgpJbiB0aGlzIHBhcnQgd2Ugd2lsbCBzaG93IGhvdyB0byBwZXJmb3JtIHRlc3RzIHRvIGNvbXBhcmUgMSwgMiAob3IgbW9yZSkgY29udGludW91cyB2YXJpYWJsZXMuIFRoZSBkYXRhc2V0LCBwcm92aWRlZCBieSBNQVNIIGF0IFRoZSBVbml2ZXJzaXR5IG9mIFNoZWZmaWVsZCwgZGVzY3JpYmVzIGluZGl2aWR1YWxzIHRoYXQgaGF2ZSBiZWVuIGZvbGxvd2luZyBkaWZmZXJlbnQgZGlldHMgYW5kIHRoZWlyIGFnZSBhbmQgZ2VuZGVyLiBUaGUgbWFpbiBnb2FsIG9mIGludGVyZXN0IGlzIHRvIGRldGVybWluZSB3aGljaCBvZiB0aHJlZSBjb21wZXRpbmcgZGlldCByZWdpbWVzIHJlc3VsdHMgaW4gdGhlIGdyZWF0ZXN0IHdlaWdodCBsb3NzLiBIb3dldmVyLCB3ZSBjYW4gdXNlIHRoZSBkYXRhc2V0IHRvIGRlbW9uc3RyYXRlIG90aGVyIHR5cGVzIG9mIHRlc3QuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpkaWV0IDwtIHJlYWRfY3N2KCJkYXRhL2RpZXQuY3N2IikKZGlldApgYGAKCgoKIyMgT25lLXNhbXBsZSB0ZXN0CgpUaGUgZmlyc3QgaHlwb3RoZXNpcyB3ZSB3aWxsIHRlc3QgaXMgd2hldGhlciB0aGUgcGVvcGxlIGluIHRoZSBzdHVkeSBhcmUgb3ZlcndlaWdodCBvciBub3QuIFRoaXMgZmlyc3QgaW52b2x2ZXMgc29tZSBtYW5pcHVsYXRpb24gb2YgdGhlIHRhYmxlIHRvIGNhbGN1bGF0ZSBhbiBleHRyYSB2YXJpYWJsZTsgdGhlIEJvZHkgTWFzcyBJbmRleCAoQk1JKS4gV2Ugd2lsbCB0ZXN0IGlmIHBlb3BsZSBpbiBvdXIgc3R1ZHkgYXJlIG92ZXJ3ZWlnaHQsIHdoZXJlIG92ZXJ3ZWlnaHQgaXMgZGVmaW5lZCBhcyBoYXZpbmcgYSBCTUkgb3ZlciAyNS4KCiRCTUkgPSB3ZWlnaHQgIC8gaGVpZ2h0XjIkCgooKndoZXJlIHRoZSB3ZWlnaHQgaXMgbWVhc3VyZWQgaW4ga2csIGFuZCB0aGUgaGVpZ2h0IGluIG1ldHJlcyopCgo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+CioqRXhlcmNpc2UgKHNob3J0KSoqCi0gQWRkIGEgbmV3IHZhcmlhYmxlIHRvIHRoZSBkYXRhIGZyYW1lIGZvciB0aGUgQk1JIG9mIGVhY2ggcGVyc29uIAogICAgKyB5b3UgbWlnaHQgd2FudCB0byBkbyB0aGlzIGluIG11bHRpcGxlIHN0ZXBzIHVzaW5nIHRoZSBgJT4lYCBub3RhdGlvbgo8L2Rpdj4KCmBgYHtyfQpkaWV0IDwtIGRpZXQgJT4lIAogIG11dGF0ZShCTUkgPWluaXRpYWwud2VpZ2h0IC8gKGhlaWdodC8xMDApXjIpCgpgYGAKCldlIGNhbiBub3cgdGVzdCBvdXIgbmV3IHZhcmlhYmxlIGZvciBub3JtYWxpdHkgdXNpbmcgdGhlIHBsb3RzIGFuZCB0ZXN0cyBmcm9tIGVhcmxpZXIsIGFsdGhvdWdoIHdlIHdpbGwgbm90IHNob3cgYWxsIHRoZSBwbG90cyBoZXJlLgoKYGBge3IgZXZhbD1GQUxTRX0KZ2doaXN0b2dyYW0oZGlldCwgeCA9ICJCTUkiLCAsYWRkX2RlbnNpdHkgPSBUUlVFLHk9Ii4uZGVuc2l0eS4uIikrIHN0YXRfb3ZlcmxheV9ub3JtYWxfZGVuc2l0eShjb2w9InN0ZWVsYmx1ZSIpCmBgYAoKVGhlIG9uZS1zYW1wbGUgdC10ZXN0IGlzIGltcGxlbWVudGVkIGluIHRoZSBmdW5jdGlvbiBgdF90ZXN0YCAoYXMgYXJlIHRoZSB2YXJpb3VzIHR5cGVzIG9mIHQtdGVzdCB0aGF0IHdlIHdpbGwgc2VlKS4gVGhlIHZhcmlhYmxlcyB0byBiZSB1c2VkIGluIHRoZSB0ZXN0IGFyZSBkZWZpbmVkIHVzaW5nIFIncyBmb3JtdWxhIGB+YCBzeW50YXguIAoKYFkgfiBYYAoKV2hlcmUgYFlgIGlzIGEgKm51bWVyaWMqIHZhcmlhYmxlIGFuZCBgWGAgaXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpbmRpY2F0aW5nIHRoZSBncm91cHMgdGhhdCBlYWNoIHBhcnRpY3VsYXIgdmFsdWUgb2YgYFlgIGJlbG9uZ3MgdG8uCgpJbiB0aGUgY2FzZSBvZiBhIG9uZS1zYW1wbGUgdGVzdCB3ZSBhcmUgdGVzdGluZyBvbmUgbnVtZXJpYyB2YXJpYWJsZSBhZ2FpbnN0IGEga25vd24gb3IgcG9wdWxhdGlvbiBtZWFuIChgbXVgKS4gQXMgd2UgZG9uJ3QgaGF2ZSBhIGdyb3VwcyB0byBjb21wYXJlLCB0aGlzIGlzIHdyaXR0ZW4gYXM6LQoKYFkgfiAxYAoKCmBgYHtyfQpkaWV0ICU+JSAKICB0X3Rlc3QoQk1JIH4xKQpgYGAKPGRpdiBjbGFzcz0iaW5mb3JtYXRpb24iPgpUaGUgdmFyaW91cyBzdGF0aXN0aWNhbCB0ZXN0cyBpbiBgcnN0YXRpeGAgYWxsIGhhdmUgdGhlIGBfdGVzdGAgc3VmZml4IChlLmcuIGB0X3Rlc3RgLCBgY2hpc3FfdGVzdGAsIGBzaGFwaXJvX3Rlc3RgKS4gVGhlIG5hbWluZyBvZiB0aGVzZSBmdW5jdGlvbnMgaXMgcHJlc3VtYWJseSBjaG9zZW4gdG8gYmUgY29uc2lzZW50IHdpdGggdGhlICpiYXNlKiBSIGltcGxlbWVudGF0aW9ucyB3aGljaCBhcmUgY2FsbGVkIGB0LnRlc3RgIGBjaGlzcS50ZXN0YCBldGMuIEhvd2V2ZXIsIHRoZSBgLnRlc3RgIGZ1bmN0aW9ucyBhcmUgb2xkZXIgYW5kIGRvIG5vdCB3b3JrIHdlbGwgd2l0aCB0aWR5IGRhdGEuIE1ha2Ugc3VyZSB5b3UgYXJlIHVzaW5nIHRoZSBjb3JyZWN0IGZ1bmN0aW9uLgo8L2Rpdj4KCldlIGdldCBhIGh1Z2VseSBzaWduaWZpY2FudCByZXN1bHQhIEhvd2V2ZXIsIGlmIHdlIGxvb2sgYXQgdGhlIGRlc2NyaXB0aW9uIGZvciBgdF90ZXN0YCBpdCBpcyB0ZXN0aW5nIGFnYWluc3QgYSBwb3B1bGF0aW9uIG1lYW4gb2YgJDAkLiBJdCBpcyBubyBzdXJwcmlzZSB0aGF0IHdlIGdldCBhIHNpZ25pZmljYW50IHJlc3VsdCEgQnkgY2hhbmdpbmcgdGhlIGBtdWAgYXJndW1lbnQgd2UgY2FuIHBlcmZvcm0gYSB0ZXN0IHRvIHNlZSBpZiB0aGUgcGVvcGxlIGluIHRoZSBzdHVkeSBhcmUgb3ZlcndlaWdodCB0byBiZWdpbiB3aXRoICh1c2luZyAyNSBhcyB0aGUgcG9wdWxhdGlvbiBvciBrbm93biBtZWFuKS4KCmBgYHtyfQpkaWV0ICU+JSAKICB0X3Rlc3QoQk1JIH4xLG11ID0gMjUsYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpCiMjIDI1IGlzIHRoZSBjdXRvZmYgZm9yIG92ZXJ3ZWlnaHQKYGBgCgoKIyMgIFR3by1zYW1wbGUgdGVzdHMKCkEgdHdvLXNhbXBsZSB0LXRlc3Qgc2hvdWxkIGJlIHVzZWQgaWYgeW91IHdhbnQgdG8gY29tcGFyZSB0aGUgbWVhc3VyZW1lbnRzIG9mIHR3byBwb3B1bGF0aW9ucy4gVGhlcmUgYXJlIHR3byB0eXBlcyBvZiB0d28tc2FtcGxlIHQtdGVzdDogaW5kZXBlbmRlbnQgKHVucGFpcmVkKSBhbmQgcGFpcmVkIChkZXBlbmRlbnQpLiBUbyBtYWtlIHRoZSBjb3JyZWN0IGNob2ljZSwgeW91IG5lZWQgdG8gdW5kZXJzdGFuZCB5b3VyIHVuZGVybHlpbmcgZGF0YS4gCgotIEFuIGluZGVwZW5kZW50IHR3by1zYW1wbGUgdC10ZXN0IGlzIHVzZWQgd2hlbiB0aGUgdHdvIHNhbXBsZXMgYXJlIGluZGVwZW5kZW50IG9mIGVhY2ggb3RoZXIsIGUuZy4gKmNvbXBhcmluZyB0aGUgbWVhbiByZXNwb25zZSBvZiB0d28gZ3JvdXBzIG9mIHBhdGllbnRzIG9uIHRyZWF0bWVudCB2cy4gY29udHJvbCBpbiBhIGNsaW5pY2FsIHRyaWFsKi4gCgotIEFzIHRoZSBuYW1lIHN1Z2dlc3RzLGEgcGFpcmVkIHR3by1zYW1wbGUgdC10ZXN0IGlzIHVzZWQgd2hlbiB0aGUgdHdvIHNhbXBsZXMgYXJlIHBhaXJlZCwgZS5nLiAqY29tcGFyaW5nIHRoZSBtZWFuIGJsb29kIHByZXNzdXJlIG9mIHBhdGllbnRzIGJlZm9yZSBhbmQgYWZ0ZXIgdHJlYXRtZW50KiAodHdvIG1lYXN1cmVtZW50cyBwZXIgcGF0aWVudCkuCgpCYWNrIHRvIG91ciBkYXRhc2V0LCB3ZSBtaWdodCB3b25kZXIgaWYgdGhlcmUgaXMgYWN0dWFsbHkgYW55IGVmZmVjdCBkdWUgdG8gZGlldCBzbyB3ZSB3aWxsIGNvbXBhcmUgdGhlIGludGlhbCBhbmQgZmluYWwgd2VpZ2h0cy4KCiMjIFJlLWZvcm1hdGluZyB0aGUgZGF0YXNldCBmb3IgdGlkeSBhbmFseXNpcwoKVGhlIGRhdGFzZXQgaW4gaXQncyBjdXJyZW50IGZvcm0gaXMgbm90IHN1aXRhYmxlIGZvciBhbmFseXNpcyB3aXRoIHRpZHkgbWV0aG9kcy4gQmVmb3JlIHByb2NlZWRpbmcgd2UgbmVlZCB0byByZS1mb3JtYXQgdGhlIGRhdGEgaW50byB0d28gY29sdW1ucy4gCgotIE9uZSBjb2x1bW4gY29udGFpbmluZyBudW1lcmljIHZhcmlhYmxlcyAoZWFjaCB2YWx1ZSBvZiB3ZWlnaHQpCi0gQW4gaW5kaWNhdG9yIChvciBjYXRlZ29yaWNhbCkgdmFyaWFibGUgdG8gZGVub3RlIHRoZSBncm91cCBlYWNoIG51bWVyaWMgb2JzZXJ2YXRpb24gYmVsb25ncyB0byAoaW5pdGlhbCBvciBmaW5hbCB3ZWlnaHQpCgpUaGUgYHBpdm90X2xvbmdlcmAgZnVuY3Rpb24gc3VwcG9ydHMgdGhpcyB0cmFuc2Zvcm1hdGlvbi4gQXMgd2Ugd2FudCB0byB1c2UgYWxsIGNvbHVtbnMgaW4gb3VyIGV4aXN0aW5nIGRhdGFzZXQgd2UgaGF2ZSB0byB1c2UgdGhlIGBldmVyeXRoaW5nKClgIHNob3J0Y3V0IHRvIHNlbGVjdCB0aGUgY29sdW1ucy4gCgpgYGB7cn0KZGlldF9sb25nIDwtIGRpZXQgJT4lIAogIHNlbGVjdChjb250YWlucygid2VpZ2h0IikpICU+JSAKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSxuYW1lc190byA9ICJ0aW1lX3BvaW50IiwgdmFsdWVzX3RvID0gIndlaWdodCIpIAoKZGlldF9sb25nCmBgYAoKU28gbm93IGB3ZWlnaHRgIGNvbnRhaW5zIGFsbCBvdXIgb2JzZXJ2YXRpb25zIG9mIHdlaWdodCBhbmQgYHRpbWVfcG9pbnRgIGluZGljYXRlcyB3aGVuIHRoZSB3ZWlnaHRzIHdlcmUgbWVhc3VyZWQuIFRoZSBgZ2doaXN0b2dyYW1gIGZ1bmN0aW9uIHdlIGludHJvZHVjZWQgZWFybGllciBpcyBhYmxlIHRvIHZpc3VhbGlzZSBkYXRhIGluIHRoaXMgZm9ybSwgYW5kIHdlIGNhbiB1c2UgdGhlIGBmYWNldC5ieWAgYXJndW1lbnQgdG8gcHJvZHVjZSBzZXBhcmF0ZSBwbG90cyBmb3IgZWFjaCB0eXBlIG9mIHdlaWdodCBtZWFzdXJlbWVudC4KCmBgYHtyfQpkaWV0X2xvbmcgJT4lIAogIGdnaGlzdG9ncmFtKHggPSAid2VpZ2h0IiwgZmFjZXQuYnkgPSAidGltZV9wb2ludCIsYWRkX2RlbnNpdHkgPSBUUlVFLCB5ID0gIi4uZGVuc2l0eS4uIikgKyBzdGF0X292ZXJsYXlfbm9ybWFsX2RlbnNpdHkoY29sPSJyZWQiKQpgYGAKCldlIGNhbiBydW4gdGhlIHNoYXBpcm8gdGVzdCBvbiB0aGUgdHdvIHZhcmlhYmxlczotIAoKYGBge3J9CmRpZXRfbG9uZyAlPiUKICBncm91cF9ieSh0aW1lX3BvaW50KSAlPiUgCiAgc2hhcGlyb190ZXN0KHdlaWdodCkKYGBgClRoZSBgdF90ZXN0YCBmdW5jdGlvbiByZXF1aXJlcyB1cyB0byBjcmVhdGUgYSAqZm9ybXVsYSogYXMgYmVmb3JlLiBUaGlzIHRpbWUgd2UgYXJlIGRvaW5nIGEgdHdvLXNpZGVkIHRlc3QsIHNvIHRoZSBmb3JtdWxhIGlzIGBZIH4gWGAgd2hlcmUgJ1knIGlzIHRoZSBudW1lcmljIHZhcmlhYmxlIGFuZCAnWCcgaXMgdGhlIGNhdGVnb3JpY2FsL2dyb3VwcyB2YXJpYWJsZS4KCmBgYHtyfQpkaWV0X2xvbmcgJT4lCiAgdF90ZXN0KHdlaWdodCB+IHRpbWVfcG9pbnQpCmBgYAoKVGhlIHAtdmFsdWUgaXMgc2lnbmlmaWNhbnQgYW5kIHNob3dzIHRoYXQgb3ZlcmFsbCB0aGUgd2VpZ2h0cyBvZiBpbmRpdmlkdWFscyBpcyAqZGlmZmVyZW50KiBiZWZvcmUgYW5kIGFmdGVyIGRpZXQuIEhvd2V2ZXIsIHRoaXMgdGVzdCBpcyBub3Qgc3BlY2lmaWNhbGx5IHRlc3RpbmcgZm9yIGEgKmRlY3JlYXNlKiBhZnRlciB0aGUgZGlldCAod2hpY2ggd2Ugd291bGQgcmVhbGx5IGhvcGUgdG8gYmUgdGhlIGNhc2UpLiBCeSBhZGRpbmcgYW4gZXh0cmEgYXJndW1lbnQgYGFsdGVybmF0aXZlPWxlc3NgIHdlIGdldCBhIGRpZmZlcmVudCByZXN1bHQKCmBgYHtyfQpkaWV0X2xvbmcgJT4lCiAgdF90ZXN0KHdlaWdodCB+IHRpbWVfcG9pbnQsIGFsdGVybmF0aXZlID0gImxlc3MiKQpgYGAKVGhlcmUgaXMgYWxzbyBleHRyYSBpbmZvcm1hdGlvbiB0aGF0IHdlIGNvdWxkIGVtcGxveTsgbmFtZWx5IHRoYXQgdGhlIG1lYXN1cmVtZW50cyBvZiB3ZWlnaHQgYXJlIG1hZGUgKipvbiB0aGUgc2FtZSBwZXJzb24qKiBiZWZvcmUgYW5kIGFmdGVyIGRpZXRpbmcuIFRoaXMgaXMgYSBjbGFzc2ljIGV4YW1wbGUgb2Ygd2hlbiB0byBhcHBseSBhIHBhaXJlZCB0LXRlc3QuIEFnYWluLCB3ZSBkbyBub3QgbmVlZCB0byB1c2UgYSBkaWZmZXJlbnQgZnVuY3Rpb24gdG8gcGVyZm9ybSB0aGUgdGVzdDsgb25seSBhZGQgYW4gYXJndW1lbnQgYHBhaXJlZD1UUlVFYCB0byBgdF90ZXN0YC4KCmBgYHtyfQpkaWV0X2xvbmcgJT4lCiAgdF90ZXN0KHdlaWdodCB+IHRpbWVfcG9pbnQsIGFsdGVybmF0aXZlID0gImxlc3MiLHBhaXJlZD1UUlVFKQpgYGAKCkFuIGludHVpdGl2ZSB3YXkgb2YgY29tcGFyaW5nIHRoZSBkaXN0cmlidXRpb25zIGFuZCBzaG93aW5nIHRoZSB0ZXN0IHJlc3VsdCBvbiB0aGUgcGxvdCBpcyB1c2luZyBhIGJveHBsb3QuIEZvciB0aGlzIHdlIHdpbGwgcmV2ZXJ0IHRvIGBnZ3Bsb3QyYCwgYnV0IHdlIGFyZSB0byBpbmNsdWRlIGEgcC12YWx1ZSB1c2luZyB0aGUgYHN0YXRfY29tcGFyZV9tZWFuc2AgZnVuY3Rpb24gKGZyb20gYGdncHVicmAuCgoKYGBge3J9CmRpZXRfbG9uZyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9wb2ludCwgeSA9IHdlaWdodCkpICsgZ2VvbV9ib3hwbG90KCkgKyBnZW9tX2ppdHRlcih3aWR0aD0wLjEpICsgCiAgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZCA9ICJ0LnRlc3QiLHBhaXJlZD1UUlVFLCBtZXRob2QuYXJncyA9IGxpc3QoYWx0ZXJuYXRpdmUgPSAibGVzcyIpKQoKP3N0YXRfY29tcGFyZV9tZWFucwpgYGAKCgoKCkEgY29udmVuaWVudCBwbG90IGluIGBnZ3B1YnJgIHdpbGwgYWxsb3cgdXMgdG8gdmlzdWFsaXNlIHRoZSBwYWlyZWQgZGlmZmVyZW5jZXMKCmBgYHtyfQpnZ3BhaXJlZChkaWV0LCBjb25kMT0iaW5pdGlhbC53ZWlnaHQiLCBjb25kMj0iZmluYWwud2VpZ2h0IixsaW5lLmNvbG9yID0gImdyZXkiKSArIHN0YXRfY29tcGFyZV9tZWFucyhwYWlyZWQ9VFJVRSxtZXRob2QgPSAidC50ZXN0IikKYGBgCgoKIyMgVGhlIEluZGVwZW5kZW50IHQgdGVzdCwgd2l0aCB0d28gaW5kZXBlbmRlbnQgZ3JvdXBzIAoKTGV0cyBjb25zaWRlciB0aGF0IHdlIHdhbnQgdG8gY29tcGFyZSB3aGV0aGVyIG1hbGVzIG9yIGZlbWFsZXMgbG9zdCBtb3JlIHdlaWdodCBkdXJpbmcgdGhlIHRyaWFsLiBTbywgbGV0J3MgY3JlYXRlIGEgKm5ldyB2YXJpYWJsZSBjYWxsZWQgd2VpZ2h0IGxvc3MqCgpgYGB7cn0KZGlldCA8LSBkaWV0ICU+JSAKbXV0YXRlKHd0Lmxvc3MgPSBpbml0aWFsLndlaWdodCAtIGZpbmFsLndlaWdodCkKCmBgYAoKCkhlcmUgd2UgaGF2ZSB0d28gZ3JvdXBzIChtYWxlcyBhbmQgZmVtYWxlcyksIGFuZCB0aGVzZSBjYW4gYmUgdHJlYXRlZCBhcyAqaW5kZXBlbmRlbnQqIHZhcmlhYmxlcyBhcyBlYWNoIGdyb3VwIGhhcyBkaWZmZXJlbnQgcGFydGljaXBhbnRzIHRoYW4gdGhlIG90aGVyIGdyb3VwLiAKClRoZSAqbnVsbCBoeXBvdGhlc2lzKiBmb3Igc3VjaCBhIHRlc3Qgd291bGQgYmUgdGhhdCB0aGUgd2VpZ2h0IGxvc3MgaXMgdGhlIHNhbWUgYmV0d2VlbiBncm91cHMgbWFsZSBhbmQgZmVtYWxlLiBXZSBzZWVrIHRvIGV2aWRlbmNlIHRvIHJlamVjdCB0aGlzIGh5cG90aGVzaXMgYnkgY2FsY3VsYXRpbmcgYSB0ZXN0IHN0YXRpc3RpYy4gCgpSZW1lbWJlciwgdGhlIHQtdGVzdCBhc3N1bWVzIG5vcm1hbCBkaXN0cmlidXRpb24gYW5kIGVxdWFsIHZhcmlhbmNlCgpGaXJzdGx5LCB3ZSBoYXZlIHRvIGNoZWNrIGZvciBub3JtYWxpdHk6LQoKYGBge3J9CmRpZXQgJT4lIApnZ2RlbnNpdHkoInd0Lmxvc3MiLGNvbG9yID0gImdlbmRlciIpICsgc3RhdF9vdmVybGF5X25vcm1hbF9kZW5zaXR5KCkKCmRpZXQgJT4lICAKICBncm91cF9ieShnZW5kZXIpICU+JSAKICBzaGFwaXJvX3Rlc3Qod3QubG9zcykKYGBgClRoZW4gd2UgY2hlY2sgZm9yIGVxdWFsIHZhcmlhbmNlcyB1c2luZyB0aGUgTGV2ZW5lIHRlc3QKCgpgYGB7cn0KCmRpZXQgJT4lCiAgbGV2ZW5lX3Rlc3Qod3QubG9zc34gZ2VuZGVyKQoKI2lmIHAgdmFsdWUgaXMgc2lnbmlmaWNhbnQgLS0+IGRpZmZlcmVuY2UgaW4gdmFyaWFuY2UgKHVuZXF1YWwgdmFyaWFuY2UpIC0tPiAgdXNlIFdlbGNoIHQtdGVzdCAoUiBkZWZhdWx0KSAKI2lmIHAgdmFsdWUgaXMgbm90IHNpZ25pZmljYW50IC0tPiBubyBkaWZmZXJlbmNlIGluIHZhcmlhbmNlIChlcXVhbCB2YXJpYW5jZSkgLS0+IHVzZSB0LXRlc3QgKGFkZCBhcmd1bWVudCB2YXIuZXF1YWwgPVRSVUUpCgpgYGAKCldlIGNvbmNsdWRlIHRoYXQgdGhlIHZhcmlhbmNlcyBhcmUgYXBwcm94aW1hdGVseSB0aGUgc2FtZSBhbmQgYm90aCB2YXJpYWJsZXMgYXJlIG5vcm1hbGx5LWRpc3RyaWJ1dGVkCgpXZSBjYW4gdXNlIHRoZSBgdF90ZXN0YCBmdW5jdGlvbiB0byBwZXJmb3JtIGFuICppbmRlcGVuZGVudCogdGVzdC4gQXMgYmVmb3JlLCB0aGUgYGZvcm11bGFgIGFyZ3VtZW50IHRvIGB0X3Rlc3RgIGlzIHRoZSAqUiBmb3JtdWxhKiBub3RhdGlvbiBmb3IgdGhlIHRlc3QgYmVpbmcgcGVyZm9ybWVkLiBIb3dldmVyLCB0aGlzIHRpbWUgd2UgKmRvIG5vdCBuZWVkIHRoZSBgcGl2b3RfbG9uZ2VyYCBmdW5jdGlvbiogYXMgd2UgYWxyZWFkeSB0aGUgYGdlbmRlcmAgYW5kIGB3ZWlnaHQubG9zc2AgdmFyaWFibGVzIGluIG91ciBkYXRhIGZyYW1lCgpUaGUgYHRfdGVzdGAgZnVuY3Rpb24gYWxsb3dzIHZhcmlvdXMgdHlwZSBvZiB0ZXN0IHRvIGJlIHBlcmZvcm1lZCBieSBjaGFuZ2luZyB0aGUgYXBwcm9wcmlhdGUgYXJndW1lbnRzIChzZWUgdGhlIGhlbHAgZm9yIGB0X3Rlc3RgIGZvciBkZXRhaWxzIChgP3RfdGVzdGApKS4gRm9yIGluc3RhbmNlLCB3ZSBjYW4gdGVsbCB0aGUgdGVzdCB0aGF0IHdlIGJlbGlldmUgb3VyIHZhcmlhbmNlcyBhcmUgZXF1YWwgb3Igbm90LgoKCmBgYHtyfQpkaWV0ICU+JSAKICB0X3Rlc3Qod3QubG9zcyB+IGdlbmRlcix2YXIuZXF1YWwgPSBUUlVFKSAKCmBgYApXZSBjYW4gc2VlIHRoYXQgdGhlIHQtc3RhdGlzdGljIHdlIG9ic2VydmUgaXMgY29uc2lzdGVudCB3aXRoIHRoZSBudWxsIGh5cG90aGVzaXMsIHRoYXQgdGhlIHdlaWdodCBsb3NzIGluIG1hbGVzIGFuZCBmZW1hbGVzIGlzIHRoZSBzYW1lLiBUaGF0IGlzLCB0aGUgcHJvYmFiaWxpdHkgb2Ygb2JzZXJ2aW5nIGEgdC1zdGF0aXN0aWMgb2YgMC4yIG9yIG1vcmUsIG9yIC0wLjIgb3IgbGVzcywgaXMgcXVpdGUgaGlnaC4KCipUaGlzIGlzIG5vdCBhIHNpZ25pZmljYW50IHJlc3VsdCAocD4wLjA1KSwgc28gdGhlcmUgaXMgbm8gZXZpZGVuY2Ugb2YgYSBkaWZmZXJlbmNlIGluIHdlaWdodCBsb3NzIGJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXMqCgpgYGB7cn0KZGlldCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZ2VuZGVyLCB5PXd0Lmxvc3MpKSArIGdlb21fYm94cGxvdCgpICsgc3RhdF9jb21wYXJlX21lYW5zKG1ldGhvZD0idC50ZXN0IikKYGBgCgojIyBOb24tIFBhcmFtZXRyaWMgYWx0ZXJuYXRpdmVzIChlLmcuIHRoZSBXaWxjb3hvbiB0ZXN0KQoKQmVpbmcgYWJsZSB0byB1c2UgdGhlIGB0X3Rlc3RgIHJlbGllcyBvbiB0aGUgeW91ciBkYXRhIGJlaW5nIG5vcm1hbGx5LWRpc3RyaWJ1dGVkLiBJZiB3ZSBkbyBub3Qgc3VmZmljaWVudCBjb25maWRlbmNlIGluIHRoaXMgYXNzdW1wdGlvbiwgdGhlcmUgYXJlIGRpZmZlcmVudCBzdGF0aXN0aWNhbCB0ZXN0cyB0aGF0IGNhbiBiZSBhcHBsaWVkLiBSYXRoZXIgdGhhbiBjYWxjdWxhdGluZyBhbmQgY29tcGFyaW5nIHRoZSAqbWVhbnMqIGFuZCAqdmFyaWFuY2VzKiBvZiBkaWZmZXJlbnQgZ3JvdXBzIHRoZXkgYXJlICpyYW5rLWJhc2VkKiBtZXRob2RzLiBIb3dldmVyLCB0aGV5IHN0aWxsIGNvbWUgd2l0aCBhIHNldCBvZiBhc3N1bXB0aW9ucyBhbmQgaW52b2x2ZSB0aGUgZ2VuZXJhdGlvbiBvZiB0ZXN0IHN0YXRpc3RpY3MgYW5kIHAtdmFsdWVzLgoKIyMjIEluZGVwZW5kZW50IHNhbXBsZXMgPSBXaWxjb3hvbiByYW5rIHN1bSB0ZXN0IChNYW5uIFdoaXRuZXkgVSB0ZXN0KQoKVGhpcyB0ZXN0IGhhcyBtYW55IGRpZmZlcmVudCBuYW1lcyBpbmNsdWRpbmcgdGhlIFdpbGNveG9uLCBXaWxjb3hvbiB0d28gc2FtcGxlIHRlc3QsIFdpbGNveG9uLU1hbm4tV2hpdG5leSwgV2lsY294b24gcmFuayBzdW0gYW5kIHRoZSBNYW5uLVdoaXRuZXktVSB0ZXN0LiBIb3dldmVyLCB0aGlzIHRlc3Qgc2hvdWxkIG5vdCBiZSBjb25mdXNlZCB3aXRoIHRoZSBXaWxjb3hvbiBzaWduZWQgcmFuayB0ZXN0ICh3aGljaCBpcyB1c2VkIGZvciBwYWlyZWQgdGVzdHMpLiBUbyBhdm9pZCBjb25mdXNpb24gdGhpcyB0ZXN0IGlzIHVzdWFsbHkgcmVmZXJyZWQgdG8gYXMgdGhlIE1hbm4tV2hpdG5leSBVIHRlc3QsIHdoaWNoIGlzIHVzZWQgd2hlbiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIHRvIGJlIGV4YW1pbmVkIGlzIGNvbnRpbnVvdXMgYnV0IHRoZSBhc3N1bXB0aW9ucyBmb3IgcGFyYW1ldHJpYyB0ZXN0cyBhcmUgdmlvbGF0ZWQuCgpGb3J0dW5hdGVseSwgdGhlIGByc3RhdGl4YCBkZXZlbG9wZXJzIGhhdmUgbWFkZSB0aGUgZnVuY3Rpb24gdG8gZG8gYSBXaWxjb3gtdGVzdCBzaW1pbGFyIHRvIGRvaW5nIGEgdF90ZXN0LiAqKlRoZSBkaWZmaWN1bHR5IGlzIGluIGNob29zaW5nIHRoZSBjb3JyZWN0IHRlc3QgdG8gYXBwbHkgLSB3aGljaCBSIHdpbGwgbm90IGFkdmlzZSB5b3Ugb24qKi4KCkxldCdzIGdvIGJhY2sgdG8gb3VyIGV4YW1wbGUgb2YgY29tcGFyaW5nIHdlaWdodCBsb3NzIGJldHdlZW4gZ3JvdXBzIG1hbGUgYW5kIGZlbWFsZS4gVGhlIGVxdWl2YWxlbnQgbm9uLXBhcmFtZXRyaWMgdmVyc2lvbiBvZiB0aGUgdGVzdCB3ZSBwZXJmb3JtZWQgYmVmb3JlIGlzOi0KCmBgYHtyfQpkaWV0ICU+JSAKICB3aWxjb3hfdGVzdCh3dC5sb3NzIH4gZ2VuZGVyKQpgYGAKClRoZSBgd2lsY294X3Rlc3RgIGlzIGZsZXhpYmxlIGluIG11Y2ggdGhlIHNhbWUgd2F5IHRoYXQgYHRfdGVzdGAgaXMuIFdlIGNhbiBzd2l0Y2ggdG8gYXBwbHlpbmcgYSBwYWlyZWQgdGVzdCBieSBhZGRpbmcgdGhlIGFyZ3VtZW50IGBwYWlyZWQ9VFJVRWAuCgpgYGB7cn0KZGlldF9sb25nICU+JSAKICB3aWxjb3hfdGVzdCh3ZWlnaHQgfiB0aW1lX3BvaW50LCBwYWlyZWQ9VFJVRSkKYGBgCgoKIyMgQ29tcGFyZSBiZXR3ZWVuICptb3JlIHRoYW4gdHdvKiBncm91cHMKCiMjIyMgUGFyYW1ldHJpYyAoQU5PVkEpCgpUaGUgdHdvLXNhbXBsZSB0LXRlc3QgaXMgdXNlZnVsIHdoZW4gd2UgaGF2ZSBqdXN0IHR3byBncm91cHMgb2YgY29udGludW91cyBkYXRhIHRvIGNvbXBhcmUuIFdoZW4gd2Ugd2FudCB0byBjb21wYXJlIG1vcmUgdGhhbiB0d28gZ3JvdXBzLCBhIG9uZS13YXkgQU5PVkEgY2FuIGJlIHVzZWQgdG8gc2ltdWx0YW5lb3VzbHkgY29tcGFyZSBhbGwgZ3JvdXBzLCByYXRoZXIgdGhhbiBjYXJyeWluZyBvdXQgc2V2ZXJhbCBpbmRpdmlkdWFsIHR3by1zYW1wbGUgdC10ZXN0cy4gIFRoZSBtYWluIGFkdmFudGFnZSBvZiBkb2luZyB0aGlzIGlzIHRoYXQgaXQgcmVkdWNlcyB0aGUgbnVtYmVyIG9mIHRlc3RzIGJlaW5nIGNhcnJpZWQgb3V0LCBtZWFuaW5nIHRoYXQgdGhlIHR5cGUgSSBlcnJvciByYXRlICh0aGUgcHJvYmFiaWxpdHkgb2Ygc2VlaW5nIGEgc2lnbmlmaWNhbnQgcmVzdWx0IGp1c3QgYnkgY2hhbmNlKSBkb2VzIG5vdCBiZWNvbWUgaW5mbGF0ZWQuIAoKSW4gb3JkZXIgdG8ganVzdGlmeSBpZiBhbiBBTk9WQSB0ZXN0IGlzIGFwcHJvcHJpYXRlIHdlIGhhdmUgdG8gdGVzdCBmb3Igbm9ybWFsaXR5LgoKYGBge3J9CiNieSBoaXN0b2dyYW0KZGlldCAlPiUgICAKZ2doaXN0b2dyYW0oeCA9ICJ3dC5sb3NzIiwgZmFjZXQuYnkgPSAiZGlldC50eXBlIiwgYWRkX2RlbnNpdHkgPSBUUlVFLCB5ID0gIi4uZGVuc2l0eS4uIikgKyBzdGF0X292ZXJsYXlfbm9ybWFsX2RlbnNpdHkoY29sPSJyZWQiKQoKI2J5IFEtUSBwbG90CmRpZXQgJT4lIAogIGdncXFwbG90KHggPSAid3QubG9zcyIsIGZhY2V0LmJ5ID0gImRpZXQudHlwZSIpCgojYnkgc2hhcGlybyB0ZXN0Cmdyb3VwX2J5KGRpZXQsIGRpZXQudHlwZSkgJT4lIAogIHNoYXBpcm9fdGVzdCh3dC5sb3NzKQpgYGAKCgpBIG9uZS13YXkgQU5PVkEgY29tcGFyZXMgZ3JvdXAgbWVhbnMgYnkgcGFydGl0aW9uaW5nIHRoZSB2YXJpYXRpb24gaW4gdGhlIGRhdGEgaW50byAqYmV0d2VlbiBncm91cCB2YXJpYW5jZSogYW5kICp3aXRoaW4gZ3JvdXAgdmFyaWFuY2UqLiBMaWtlIHRoZSBvdGhlciBzdGF0aXN0aWNhbCB0ZXN0cyB3ZSBoYXZlIGVuY291bnRlcmVkLCB0aGUgZnVuY3Rpb25zIGluIFIgZG8gdGhlIGhhcmQgd29yayBvZiBjYWxjdWxhdGluZyB0aGUgc3RhdGlzdGljcy4gVGhlIGBhbm92YV90ZXN0YCBmdW5jdGlvbiBpcyBhIHRpZHkgdmVyc2lvbiBvZiB0aGUgQU5PVkEgdGVzdC4KCmBgYHtyfQpkaWV0ICU+JSAKICBhbm92YV90ZXN0KHd0Lmxvc3MgfiBkaWV0LnR5cGUpCgpgYGAKCgpXaGVuIHRoZSB0ZXN0IHByb3ZpZGVzIGEgc2lnbmlmaWNhbnQgcmVzdWx0IChsaWtlIGFib3ZlKSBpdCB0ZWxscyB1cyB0aGF0IHRoZXJlIGlzIGF0IGxlYXN0IG9uIGRpZmZlcmVuY2UgaW4gdGhlIGdyb3Vwcy4gSG93ZXZlciwgaXQgZG9lcyBub3QgdGVsbCB1cyB3aGljaCBncm91cCBpcyBkaWZmZXJlbnQuIEZvciB0aGlzLCB3ZSBjYW4gYXBwbHkgYSAicG9zdC1ob2MgdGVzdCIgc3VjaCBhcyB0aGUgVHVrZXkgdGVzdC4gSWYgYGFub3ZhX3Rlc3RgIGRpZCBub3QgcHJvZHVjZSBhIHNpZ25pZmljYW50IHAtdmFsdWUsIHdlIHdvdWxkbid0IHByb2NlZWQgd2l0aCB0aGlzIHN0ZXAKCmBgYHtyfQpkaWV0ICU+JSAgCiAgdHVrZXlfaHNkKHd0Lmxvc3MgfiBkaWV0LnR5cGUpCmBgYAoKQXMgd2UgaGF2ZSBzZWVuIHByZXZpb3VzbHksIGEgc3RhbmRhcmQgbWV0aG9kIG9mIHByZXNlbnRpbmcgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gZ3JvdXBzIGlzIHRvIHVzZSB0aGUgYHN0YXRfY29tcGFyZV9tZWFuc2AgZnVuY3Rpb24gdG8gYXV0b21hdGljYWxseSBhZGQgcC12YWx1ZXMgdG8gYSBib3hwbG90IG9yIHZpb2xpbiBwbG90CgpgYGB7cn0KZ2dwbG90KGRpZXQsIGFlcyh4ID0gZGlldC50eXBlLCB5ID0gd3QubG9zcykpICsgZ2VvbV92aW9saW4oKSArIGdlb21faml0dGVyKHdpZHRoPTAuMSkgKyBzdGF0X2NvbXBhcmVfbWVhbnMobWV0aG9kPSJhbm92YSIpCmBgYAoKSG93ZXZlciwgaW4gdGhlIGNhc2Ugb2YgbW9yZSB0aGFuIHR3byBncm91cHMgaXQgd2lsbCBvbmx5IHNob3cgYSBzaW5nbGUgcC12YWx1ZSBmcm9tIHRoZSBBTk9WQSByYXRoZXIgdGhhbiBpbmRpdmlkdWFsIGNvbXBhcmlzb25zLiBXZSBjYW4gZXhwbGljaXRseSBsaXN0IHBhcnRpY3VsYXIgY29udHJhc3RzIHdlIGFyZSBpbnRlcmVzdGVkIGluLgoKYGBge3J9Cm15X2NvbXBhcmlzb25zIDwtIGxpc3QoIGMoIkEiLCAiQiIpLCBjKCJBIiwgIkMiKSwgYygiQiIsICJDIikgKQpnZ3Bsb3QoZGlldCwgYWVzKHggPSBkaWV0LnR5cGUsIHkgPSB3dC5sb3NzKSkgKyBnZW9tX3Zpb2xpbigpICsgZ2VvbV9qaXR0ZXIod2lkdGg9MC4xKSArIAogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAidC50ZXN0Iixjb21wYXJpc29ucyA9IG15X2NvbXBhcmlzb25zKQpgYGAKQWx0ZXJuYXRpdmVseSwgd2UgY2FuIG1hbnVhbGx5LWNvbXB1dGUgdGhlIHAtdmFsdWVzIGFuZCBhZGQgdGhlc2UgdG8gdGhlIHBsb3QuCgpgYGB7cn0Kc3RhdF9yZXMgPC0gZGlldCAlPiUgCiAgdHVrZXlfaHNkKHd0Lmxvc3MgfiBkaWV0LnR5cGUpCgoKZ2dwbG90KGRpZXQsIGFlcyh4ID0gZGlldC50eXBlLCB5ID0gd3QubG9zcykpICsgZ2VvbV92aW9saW4oKSArIGdlb21faml0dGVyKHdpZHRoPTAuMSkrCiAgc3RhdF9wdmFsdWVfbWFudWFsKHN0YXRfcmVzLCBsYWJlbCA9ICJwLmFkaiIseS5wb3NpdGlvbiA9IGMoMTEsIDEzLCAxNSkpCmBgYAoKCgojIyMgTm9uLVBhcmFtZXRyaWMgKEtydXNrYWwgV2FsbGlzKSAKCkRhdGEgdGhhdCBkbyBub3QgbWVldCB0aGUgYXNzdW1wdGlvbnMgb2YgQU5PVkEgKGUuZy4gbm9ybWFsaXR5KSBjYW4gYmUgdGVzdGVkIHVzaW5nIGEgbm9uLXBhcmFtZXRyaWMgYWx0ZXJuYXRpdmUuIFRoZSAqS3J1c2thbC1XYWxsaXMqIHRlc3QgaXMgZGVyaXZlZCBmcm9tIHRoZSBvbmUtd2F5IEFOT1ZBLCBidXQgdXNlcyByYW5rcyByYXRoZXIgdGhhbiBhY3R1YWwgb2JzZXJ2YXRpb25zLiBJdCBpcyBhbHNvIHRoZSBleHRlbnNpb24gb2YgdGhlIE1hbm4tV2hpdG5leSBVIHRlc3QgdG8gbW9yZSB0aGFuIHR3byBncm91cHMuCgpgYGB7cn0KZGlldCAlPiUgCiAga3J1c2thbF90ZXN0KHd0Lmxvc3MgfiBkaWV0LnR5cGUpCmBgYApMaWtlIHRoZSBvbmUtd2F5IEFOT1ZBIHRoaXMgd2lsbCBvbmx5IHRlbGwgdXMgdGhhdCBhdCBsZWFzdCBvbmUgZ3JvdXAgaXMgZGlmZmVyZW50IGFuZCBub3Qgc3BlY2lmaWNhbGx5IHdoaWNoIGdyb3VwKHMpLiBUaGUgcG9zdC1ob2MgYGR1bm4udGVzdGAgaXMgcmVjb21tZW5kZWQgd2hpY2ggYWxzbyBwZXJmb3JtcyBtdWx0aXBsZSB0ZXN0aW5nIGNvcnJlY3Rpb24uCgpgYGB7cn0KZGlldCAlPiUgCiAgZHVubl90ZXN0KHd0Lmxvc3MgfiBkaWV0LnR5cGUsIHAuYWRqdXN0Lm1ldGhvZCA9ICJib25mZXJyb25pIikKYGBgCgoKQXQgdGhpcyBwb2ludCB3ZSBjb3VsZCBiZSBhYm91dCB0byByZWNvbW1lbmQgZGlldCBDIHRvIHRob3NlIHRoYXQgd2lzaCB0byBsb3NlIHdlaWdodC4gSG93ZXZlciwgYXJlIHRoZXJlIGFueSBvdGhlciBmYWN0b3JzIGluIHRoZSBkYXRhIHRoYXQgd2Ugc2hvdWxkIGJlIGNvbnNpZGVyaW5nPyBXaXRoIGBnZ3Bsb3QyYCB3ZSBjYW4gcXVpdGUgZWFzaWx5IHZpc3VhbGlzZSB0aGUgZWZmZWN0cyBvZiBtdWx0aXBsZSBmYWN0b3JzIG9uIHRoZSBkYXRhLiBMZXRzIGFkZCBib3RoIGdlbmRlciBhbmQgZGlldCB0eXBlIGludG8gdGhlIHBsb3QuIEl0IG5vdyBhcHBlYXJzIHRoYXQgZGlldCBDIGlzIGhhdmluZyBhbiBlZmZlY3Qgb24gbWFsZXMgYnV0IG5vdCBmZW1hbGVzLgoKYGBge3J9CmdncGxvdChkaWV0LCBhZXMoeCA9IGRpZXQudHlwZSwgeSA9IHd0Lmxvc3MsZmlsbD1nZW5kZXIpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKIyMgVHdvLXdheSBBTk9WQQoKVGhlICpmb3JtdWxhKiBub3RhdGlvbiBhbGxvd3MgdXMgdG8gc3BlY2lmeSBhbiAqaW50ZXJhY3Rpb24qIGJldHdlZW4gZ2VuZGVyIGFuZCBkaWV0IHR5cGUuIEluIG90aGVyIHdvcmRzLCB3ZSBhcmUgbG9va2luZyB0byBzZWUgaWYgdGhlIGVmZmVjdCBvZiBkaWV0IHR5cGUgaXMgZGlmZmVyZW50IGZvciBtYWxlcyBhbmQgZmVtYWxlcy4gSW4gUiwgdGhlIGZvcm11bGEgZm9yIGFuIGludGVyYWN0aW9uIGlzIHNwZWNpZmllZCB1c2luZyBhIGAqYCBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMgdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBhc3Nlc3NpbmcgdGhlIGludGVyYWN0aW9uIGZvci4KCmBgYHtyfQpkaWV0ICU+JSAKICBhbm92YV90ZXN0KHd0Lmxvc3MgfiBkaWV0LnR5cGUqZ2VuZGVyKQpgYGAKCgpUaGlzIHRlbGxzIHVzIHRoYXQgYW4gZWZmZWN0IGV4aXN0cyBiZXR3ZWVuIGRpZXQgdHlwZSBhbmQgZ2VuZGVyLCBidXQgbGlrZSBiZWZvcmUgd2UgaGF2ZSB0byBydW4gYSBwb3N0LWhvYyB0ZXN0IHRvIGRpc2NvdmVyIG1vcmUKCmBgYHtyfQpkaWV0ICU+JSAKICB0dWtleV9oc2Qod3QubG9zcyB+IGRpZXQudHlwZSpnZW5kZXIpICU+JSAKICBmaWx0ZXIocC5hZGouc2lnbmlmICE9ICJucyIpCmBgYAoKCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KKipFeGVyY2lzZSoqCgpUaGUgZXhjZWwgZmlsZSDigJhSQ0My4oCZIGNvbnRhaW5zIGRhdGEgYWJvdXQgdGhlIGV4cHJlc3Npb24gbGV2ZWxzIG9mIHNvbWUgZ2VuZXMgaW4gcGF0aWVudHMgd2l0aCByZW5hbCBjZWxsIGNhcmNpbm9tYS4gSW4geW91ciBzdHVkeSwgeW91IHB1dCB0aGUgZm9sbG93aW5nIGh5cG90aGVzZXMuClBsZWFzZSB0ZXN0IHRob3NlIGFsdGVybmF0aXZlIGh5cG90aGVzZXMgYW5kIHN0YXRlIHdoZXRoZXIgeW91IHdpbGwgYWNjZXB0IG9yIHJlamVjdCBlYWNoIG9uZS4KCjEuCUZlbWFsZXMgaGF2ZSBhIGhpZ2hlciBsZXZlbCBvZiBFMkYzIHRoYW4gbWFsZXMgCjIuCUFOWEEgZXhwcmVzc2lvbiBsZXZlbHMgdmFyeSBiZXR3ZWVuIHVuaWxhdGVyYWwgYW5kIGJpbGF0ZXJhbCB0dW1vcnMKMy4JSW5kaXZpZHVhbHMgd2l0aCBSQ0MgZ3JhZGUgSUkgaGF2ZSBkaWZmZXJlbnQgbGV2ZWxzIG9mIEUyRjMgdGhhbiB0aG9zZSB3aXRoIGdyYWRlIElJSSBvciBJViAKNC4JVGhlIG1lYW4gdmFsdWUgb2YgbWlSNDk5IGRlY3JlYXNlcyBzaWduaWZpY2FudGx5IGFmdGVyIHRyZWF0bWVudAo1LglERkZBIGlzIGhpZ2hlciBpbiBwYXRpZW50cyB3aXRoIGdyYWRlIElWIHR1bW9ycwo8L2Rpdj4KCiMgU29sdXRpb25zCgoKKipUbyBiZSByZXZlYWxlZCBkdXJpbmcgdGhlIHdvcmtzaG9wISoqCgojIyBFeGVyY2lzZSAxCgpgYGB7cn0KIyMgTkVXCgp0YWIgPC0gcmVhZHhsOjpyZWFkX3hsc3goImRhdGEvRVggQmlvc3RhdCBQMS54bHN4IikgJT4lIG1ha2VfY2xlYW5fbmFtZXMoKQojIzItTWFrZSBhIGNyb3NzIHRhYmxlIHNob3dpbmcgdGhlIGdlbmRlciBpbiB0aGUgcm93cyBhbmQgdHVtb3IgZ3JhZGUgaW4gdGhlIGNvbHVtbnMKCmNvdW50KHRhYiwgR2VuZGVyLCBUdW1vci5ncmFkZSkKCgojIzMtRGVmaW5lIHRoZSBwZXJjZW50YWdlIG9mIEdyYWRlIElJSSB0dW1vcnMgd2l0aGluIGZlbWFsZXMgCmZyZXFfdGFibGUodGFiLCBHZW5kZXIsIFR1bW9yLmdyYWRlKQoKIyM0LVVzZSB0aGUgYXBwcm9wcmlhdGUgdGVzdCB0byBjaGVjayBpZiB0aGUgdHVtb3IgZ3JhZGUgZGVwZW5kcyBvbiB0aGUgZ2VuZGVyIAoKY291bnQodGFiLCBHZW5kZXIsIFR1bW9yLmdyYWRlKSAlPiUgCiAgICB0aWR5cjo6cGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEdlbmRlcix2YWx1ZXNfZnJvbSA9IG4pICU+JSAKICBzZWxlY3QoLVR1bW9yLmdyYWRlKSAlPiUgCiAgY2hpc3FfdGVzdCgpCgpgYGAKCiMjIEV4ZXJjaXNlIDIKCgpgYGB7cn0KIyMxLVJlYWQgdGhlIGV4Y2VsIGZpbGUgY2FsbGVkICdFWCBCaW9zdGF0IFAyJyBpbnRvIFIgCkJpb3N0YXQyIDwtIHJlYWR4bDo6cmVhZF94bHN4KCJkYXRhL0VYIEJpb3N0YXQgUDIueGxzeCIpCgpnZ2hpc3RvZ3JhbShCaW9zdGF0MiwgeCA9ICJBZ2UiKQoKZ2doaXN0b2dyYW0oQmlvc3RhdDIsIHggPSAiQWdlIiwgYWRkX2RlbnNpdHkgPSBUUlVFLCB5ID0gIi4uZGVuc2l0eS4uIikgKyBzdGF0X292ZXJsYXlfbm9ybWFsX2RlbnNpdHkoY29sPSJibHVlIikKCgpnZ3FxcGxvdChCaW9zdGF0MiwgeCA9ICJBZ2UiKQpCaW9zdGF0MiAlPiUgCiAgc2hhcGlyb190ZXN0KHZhcnMgPSAiQWdlIikKIyBBZ2UgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQKCmdnaGlzdG9ncmFtKEJpb3N0YXQyLCB4ID0gImhvc3BpdGFsaXphdGlvbiBkYXlzIikKCmdnaGlzdG9ncmFtKEJpb3N0YXQyLCB4ID0gImhvc3BpdGFsaXphdGlvbiBkYXlzIiwgYWRkX2RlbnNpdHkgPSBUUlVFLCB5ID0gIi4uZGVuc2l0eS4uIikgKyBzdGF0X292ZXJsYXlfbm9ybWFsX2RlbnNpdHkoY29sPSJibHVlIikKCgpnZ3FxcGxvdChCaW9zdGF0MiwgeCA9ICJob3NwaXRhbGl6YXRpb24gZGF5cyIpCkJpb3N0YXQyICU+JSAKICBzaGFwaXJvX3Rlc3QodmFycyA9ICJob3NwaXRhbGl6YXRpb24gZGF5cyIpCgojIGhvc3BpdGFsaXphdGlvbiBkYXlzIE5PVCBub3JtYWxseSBkaXN0cmlidXRlZAoKZ2V0X3N1bW1hcnlfc3RhdHMoQmlvc3RhdDIpCgpgYGAKCiMjIEV4ZXJjaXNlIDMKCiMjIyAzLjEKCmBgYHtyfQpSQ0MyIDwtIHJlYWR4bDo6cmVhZF94bHN4KCJkYXRhL1JDQzIueGxzeCIpCgpnZ2hpc3RvZ3JhbShSQ0MyLCB4ID0gIkUyRjMiLCAKICAgICAgICAgICAgeSA9ICIuLmRlbnNpdHkuLiIsCiAgICAgICAgICAgIGZhY2V0LmJ5ID0gImdlbmRlciIpICsgc3RhdF9vdmVybGF5X25vcm1hbF9kZW5zaXR5KGNvbD0iYmx1ZSIpCgpSQ0MyICU+JSAKICBncm91cF9ieShnZW5kZXIpICU+JSAKICBzaGFwaXJvX3Rlc3QoRTJGMykKI2JvdGggZ3JvdXBzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkCiNwYXJhbWV0cmljICogMiBncm91cHMgLS0+IHQtdGVzdAoKUkNDMiAlPiUgCiAgZ3JvdXBfYnkoZ2VuZGVyKSAlPiUgCiAgZ2V0X3N1bW1hcnlfc3RhdHMoIkUyRjMiLHR5cGU9ImNvbW1vbiIpCgp0X3Rlc3QoUkNDMiwgRTJGMyB+IGdlbmRlcix2YXIuZXF1YWwgPSBUUlVFKQojQU5TV0VSPVJFSkVDVAoKZ2dwbG90KFJDQzIsIGFlcyh4ID0gZ2VuZGVyLCB5ID0gRTJGMykpICsgZ2VvbV92aW9saW4oKSArIGdlb21faml0dGVyKHdpZHRoID0gMC4xKSArIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSAidC50ZXN0IikKCmBgYAoKIyMjIDMuMgoKYGBge3J9CmdnaGlzdG9ncmFtKFJDQzIsIHggPSAiQU5YQSIsIGZhY2V0LmJ5ID0gIlNpZGUiKQoKUkNDMiAlPiUgCiAgZ3JvdXBfYnkoU2lkZSkgJT4lIAogIHNoYXBpcm9fdGVzdChBTlhBKQojbm90IG5vcm1hbAojbm9uLXBhcmFtZXRyaWMgKiAyIGdyb3VwcyAtLT4gV2lsY294b24gKE1hbm4gV2hpdG5leSBVKQp3aWxjb3hfdGVzdChSQ0MyLCBBTlhBIH4gU2lkZSkKI0FOU1dFUj1SRUpFQ1QKZ2dwbG90KFJDQzIsIGFlcyh4ID0gU2lkZSwgeSA9IEFOWEEpKSArIGdlb21fdmlvbGluKCkgKyBnZW9tX2ppdHRlcigpICsgc3RhdF9jb21wYXJlX21lYW5zKCkKCmBgYAoKCgojIyMgMy4zCgoKYGBge3J9CmdnaGlzdG9ncmFtKFJDQzIsIHggPSAiRTJGMyIsIGZhY2V0LmJ5ID0gIkdyYWRlIiwgeSA9ICIuLmRlbnNpdHkuLiIpICsgc3RhdF9vdmVybGF5X25vcm1hbF9kZW5zaXR5KCkgCgojMyBncm91cHMgbm9ybWFsCiNwYXJhbWV0cmljICogPiAyIGdyb3VwcyAtLT4gQU5PVkEKCmFub3ZhX3Rlc3QoUkNDMiwgRTJGMyB+IEdyYWRlKQojdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGJ1dCBiZXR3ZWVuIHdoaWNoIGdyb3Vwcz8gLS0+IHBvc3QgSG9jCnR1a2V5X2hzZChSQ0MyLCBFMkYzIH4gR3JhZGUpCiNBTlNXRVI9IEFDQ0VQVApteV9jb21wYXJpc29ucyA8LSBsaXN0KCBjKCJJSSIsICJJSUkiKSwgYygiSUkiLCAiSVYiKSwgYygiSUlJIiwgIklWIikgKQoKZ2dwbG90KFJDQzIsIGFlcyh4ID0gR3JhZGUsIHkgPSBFMkYzKSkgKyBnZW9tX3Zpb2xpbigpICsgZ2VvbV9qaXR0ZXIod2lkdGg9MC4xKSArIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2Q9InQudGVzdCIsY29tcGFyaXNvbnMgPSBteV9jb21wYXJpc29ucykKCgpgYGAKCgoKIyMjIDMuNAoKCmBgYHtyfQptaXI0OTlfZGF0YSA8LSAKICBSQ0MyICU+JQogIHNlbGVjdChjb250YWlucygibWlyNDk5IikpICU+JSAKICB0aWR5cjo6cGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSkKZ2doaXN0b2dyYW0obWlyNDk5X2RhdGEsIHggPSAidmFsdWUiLCBmYWNldC5ieSA9ICJuYW1lIikKCm1pcjQ5OV9kYXRhICU+JSAKICBncm91cF9ieShuYW1lKSAlPiUgCiAgc2hhcGlyb190ZXN0KHZhbHVlKQojbm90IG5vcm1hbAojbm9uLXBhcmFtZXRyaWMgKiAyIHBhaXJlZCBncm91cHMgLS0+IFdpbGNveG9uIHNpZ25lZCByYW5rIHRlc3QKCm1pcjQ5OV9kYXRhICU+JSAKICB3aWxjb3hfdGVzdCh2YWx1ZSB+IG5hbWUsIHBhaXJlZCA9IFRSVUUsYWx0ZXJuYXRpdmUgPSAiZ3JlYXRlciIpCm1pcjQ5OV9kYXRhICU+JSAKICBncm91cF9ieShuYW1lKSAlPiUgCiAgZ2V0X3N1bW1hcnlfc3RhdHModmFsdWUpCiNpdCBpbmNyZWFzZXMgYWZ0ZXIgdHR0CiNBTlNXRVIgPSBSRUpFQ1QgICAKYGBgCiMjIyAzLjUKCmBgYHtyfQpnZ2hpc3RvZ3JhbShSQ0MyLCB4ID0gIkRGRkEiLCBmYWNldC5ieSA9ICJHcmFkZSIpCiNub3Qgbm9ybWFsCiNub24tcGFyYW1ldHJpYyAqID4gMiBncm91cHMgLS0+IGtydXNrYWwgV2FsbGlzClJDQzIgJT4lIAogIGtydXNrYWxfdGVzdChERkZBIH4gR3JhZGUpCiNBTlNXRVI9IFJFSkVDVAojTm8gbmVlZCB0byBwZXJmb3JtIHBvc3QtaG9jIHRlc3QKYGBgCgoKCg==