Linear Regression: An Overview

Linear modeling is a fundamental aspect of statistics. One of the most ubiquitous predictive modeling methods, linear regression, is useful for its simplicity in implementation and interpretation. While linear regression is used to predict the value of a continuous response variable Y as a function of one or more explanatory variables X using a mathematical function, this technique operates with several underlying assumptions and is sensitive to outliers.

Linear regression is useful for analyzing relationships among variables of interest and determining whether there is a linear or non-linear association among them, which is essential for exploratory data analysis and in choosing suitable models. To check whether the data we are attempting to model is suitable for linear regression, we must examine four assumptions which are summarized as L.I.N.E:

  • Linear relationship [between the X and Y variables]
  • Independent observations [there is no relationship between the residuals and predictor variable(s)]
  • Normal residuals [the distribution should be symmetric, unimodal, and approximately bell-shaped]
  • Equal variance in residuals [there is no relationship between the residuals and fitted values]

The mathematical equation of linear regression can be modeled as:

\(Y = \beta_1 + \beta_2X + \epsilon\)

where \(\beta_1\) represents the intercept, \(\beta_2\) signifies the slope, and \(\epsilon\) stands for the error term. \(Y\) is the outcome variable, which is predicted by \(X\) as the input variable. Furthermore, there can be multiple \(X\) variables that are either continuous or categorical (i.e. \(X_2\), \(X_3\), etc.).


As an example, we will examine the Needle Exchange Data, a dataset gathered from amFAR (the Foundation of AIDS Research), to analyze how the distance to nearest syringe program (dist_SSP) relates to percent uninsured (pctunins). In the following overview, we will walk through how to perform EDA (Exploratory Data Analysis), review the L.I.N.E. assumptions,and fit the linear model to the data. In addition, we will provide guidance on data visualization and plot aesthetics. For the purpose of this example, we will only analyze the two variables mentioned above. Although it is relatively simple to add more explanatory variables to a linear model, analyzing and graphing the data can quickly become complicated. For further information, see this tutorial on Multivariate Regression: https://openintro.shinyapps.io/ims-04-multivariable-and-logistic-models-03/#section-multiple-regression

Importing the Data

To preface this overview, we must install and load the following packages into R:

  • tidyverse
  • tidymodels
  • patchwork
  • moderndive
  • GGally
  • readxl

First, load the data:

Note: You might have to specify the file path to the data if it is not uploaded to a folder that you can directly access from your ‘Home’ page as is shown below.

To download these files to follow along with the exercise, go to this GitHub page for the .csv file and this page for the .xlsx file.

library(tidyverse)
library(tidymodels)
library(patchwork)
library(moderndive)
library(GGally)
library(readxl)
needex = read_csv("dist_ssp_amfar.csv")
needex_codebook <- read_excel("opioid_county_codebook.xlsx", sheet = "Dictionary")
#It might be easier to open and read the .xlsx file in Excel rather than in R

We named the Needle Exchange Data as needex and will refer to it henceforth when calling the data. The needex_codebook contains a description for the variable names used in needex (as well as many others). You can locate the variable names quickly in the codebook by searching using Ctrl + F.


EDA

To start off, we should observe the variable types in the dataset:

head(needex)
## # A tibble: 6 x 7
##   county   STATEABBREVIATION dist_SSP HIVprevalence opioid_RxRate pctunins metro
##   <chr>    <chr>                <dbl>         <dbl>         <dbl>    <dbl> <chr>
## 1 wabasha~ MN                    47.4          49.7           8.3      5.1 metro
## 2 johnson~ GA                   125           283.            3       18.5 non-~
## 3 iron co~ MO                    77.2         103.          131.      15.9 non-~
## 4 koscius~ IN                    42.1          52.1          64.2     12.4 non-~
## 5 delawar~ OK                    49.2          77.9          78.7     18.6 non-~
## 6 scurry ~ TX                   141            70.7          81.5     14.8 non-~

Using the head() function, we can observe the first 6 rows and each variable name in the dataset. You may also specify the number of rows of data shown using n = ..., but the default is 6 rows.

Notice that we have three categorical variables: county, STATEABBREVIATION, and metro. Our four numerical variables are as follows: dist_SSP, HIVprevalence, opioid_RxRate, and pctunins.

You could check the needex_codebook for the descriptor for each variable, but for the sake of brevity we will clarify several of the variables:

  • dist_SSP = distance to nearest syringe services program (in miles)
  • HIVprevalence = # living people diagnosed w/ HIV per 100,000 (including adults and adolescents 13+)
  • opioid_RxRate = # opioid prescriptions per 100 people
  • pctunins = % civilian population (not in an institution) w/o health insurance coverage
  • metro = whether the geographical location is considered metropolitan or not (NOTE: This is a personal interpretation because the needex_codebook did not provide a definition)

We can then create a pairs plot - using the GGally package - to display a scatterplot matrix of all the quantitative variables from the needex dataset.

needex %>%
  select(dist_SSP, HIVprevalence, opioid_RxRate, pctunins) %>%
GGally::ggpairs(lower = list(continuous = wrap("smooth", size=0.1)))

Looking at the pairs plot, it appears that the strongest correlation of 0.0.413 among the quantitative variables is between dist_SSP and pctunins (shown at the top right) while the scatterplot of that relationship is displayed in the bottom left. Typically, a correlation coefficient between 0.3 to 0.7 is considered to be of moderate strength. Thus, we can say that the relationship between dist_SSP and pctunins is a positive, moderate correlation.

While you knew from earlier that we were going to analyze the relationship between dist_SSP and pctunins, we have shown through EDA how you can explore the relationships among your variables to determine which variables are appropriate to focus on and model further.

The code for producing a scatterplot of dist_SSP and pctunins is shown:

needex %>%
  ggplot(aes(x = pctunins, y = dist_SSP)) +
  geom_point() 


Plotting the Data

Does the scatterplot above seem a bit hard to interpret without context? It should. In R, you can visualize your graph in countless ways - adjusting the parameters, changing the titles and labels, layering geoms and aesthetics, adding colors and gradients and shapes and text, etc. We should start by changing the labels of the x- and y-axis to more appropriate titles.

needex %>%
  ggplot(aes(x = pctunins, y = dist_SSP)) +
  geom_point() +
  labs(title = "Modeling the Strongest Correlation of 'needex'",
       x = "Percent Uninsured",
       y = "Distance to Nearest Syringe Programs (mi)",
       caption = "Source: amFAR") #Caption is placed automatically to bottom right

To make the plot more aesthetically pleasing, you may also use the theme() function to adjust the size, color, font, and remove/add other features of the text in the labels.

needex %>%
  ggplot(aes(x = pctunins, y = dist_SSP)) +
  geom_point() +
  labs(title = "Modeling the Strongest Correlation of 'needex'",
       x = "Percent Uninsured",
       y = "Distance to Nearest Syringe Programs (mi)",
       caption = "Source: amFAR") +
  theme(plot.title = element_text(color = "navy", face = "bold"), #Changed color of title
        panel.grid.major.y = element_blank(), #Removed gridlines
        panel.grid.minor.y = element_blank(), #Removed gridlines
         panel.grid.major.x = element_blank(), #Removed gridlines
        panel.grid.minor.x = element_blank(), #Removed gridlines
        plot.background = element_rect(fill = "lightgrey")) #Changed color of plot background

We should add a least-squares line to the graph since we have already determined that there is a positive, moderate correlation between the explanatory and response variable. In addition, if you would rather not manually change the aesthetics of your graph, you can use ggthemes to choose from readily available formats at your preference. For an overview of available themes, you can check out https://yutannihilation.github.io/allYourFigureAreBelongToUs/ggthemes/. To reduce the amount of code written in the following chunk, I selected an arbitrary theme. I added the least-squares line using stat_smooth(method = "lm"), which instructs R to fit a linear model to the data.

needex %>%
  ggplot(aes(x = pctunins, y = dist_SSP)) +
  geom_point() +
  labs(title = "Modeling the Strongest Correlation of 'needex'",
       x = "Percent Uninsured",
       y = "Distance to Nearest Syringe Programs (mi)",
       caption = "Source: amFAR") +
  ggthemes::theme_gdocs() +
  stat_smooth(method = "lm") 

#NOTE: Changing themes shifted the automatic placement of the caption from 
#bottom right to bottom left

One thing you should notice is that the standard error remains relatively small around 3 - 20 percent. The standard error increases around the least-squares line at higher percentages of civilians without health insurance. This is a result of less values located between 20 - 36%. Standard error increases when standard deviation (variance of a population) increases, which occurs when the sample size decreases. You could possibly interpret that the larger standard error between 20 - 36 % percent comes from the smaller sample size within that range. Since the data clusters between 3 - 20 %, the standard error is smaller.

If you look closely, you’ll see that no dots appear between 0-3%. How did we know that the cutoff of the least-squares line was around 3 percent instead of 2 or 2.5 (which isn’t readily observable by the tick marks along the x-axis)? You can use the range() function to find the minimum and maximum of a vector. Out of curiosity, we can also examine the min and max of the outcome variable as well.

pct <- needex$pctunins
range(pct) #Min and Max of Percent Uninsured
## [1]  3.0 35.9
dist <- needex$dist_SSP
range(dist) #Min and Max of Distance from Nearest Program
## [1]   0 510

Interesting! It appears that the range of people without health insurance falls between 3 - 36 %, while the distance to the nearest syringe program can range between 0 - 510 miles. 510 miles is longer than the length of Florida!

However, this only tells us so much about the data. To gain more insight, we will fit the linear model to the data and check the L.I.N.E. assumptions to determine how well the model suits the data.


Fitting the Linear Model

We fit our model to the variables of interest:

lm_model = lm(dist_SSP ~ pctunins, data = needex) #Fitting the linear model

get_regression_table(lm_model) #Output of regression table
## # A tibble: 2 x 7
##   term      estimate std_error statistic p_value lower_ci upper_ci
##   <chr>        <dbl>     <dbl>     <dbl>   <dbl>    <dbl>    <dbl>
## 1 intercept    12.5     10.2        1.23   0.221    -7.51    32.5 
## 2 pctunins      7.82     0.773     10.1    0         6.30     9.34
needex %>% #Correlation between X and Y variable
  get_correlation(dist_SSP ~ pctunins)
## # A tibble: 1 x 1
##     cor
##   <dbl>
## 1 0.413

Fitting the model doesn’t mean that R will automatically display the values of that linear model to us. We used the function get_regression_table() to output the regression table. We then calculated the correlation between dist_SSP and pctunins and got 0.4126744 in R Markdown. Look familiar? It’s exactly the same as the 0.413 correlation from the pairs plot! We just wanted to demonstrate how to calculate the correlation between and among all the numerical variables of needex manually, yet also how much simpler (and faster!) GGally package can make it for us! The beauty of R is how versatile it is in terms of function and accessibility to statisticians of all levels!

Assumptions

Now that we have performed EDA, plotted the linear relationship between the explanatory X variable and outcome Y variable, and fitted the model to the data, we must check how suitable the model by checking the L.I.N.E. assumptions.

  1. There must be a Linear relationship between independent and dependent variables.

We have already shown this multiple times above, but we will include a graph here. We will format this in a new theme for fun since we’re in a new section. In addition, if you wish to remove the standard error of the line from the scatterplot, then simply set se = FALSE inside stat_smooth().

new_theme = theme_minimal() #New theme is this style

a1 <- needex %>%
  ggplot(aes(x = pctunins, y = dist_SSP)) +
  geom_point() +
  labs(title = "Modeling 'needex'",
       x = "Percent Uninsured",
       y = "Distance to Nearest Syringe Programs (mi)",
       caption = "Source: amFAR") +
  new_theme + #Call new theme
  stat_smooth(method = "lm", se = FALSE) 

a1

While we know that the positive correlation is only of moderate strength, it was the strongest correlation among the numerical variables of needex and so was the most suited for a linear model. Just to double check, let’s layer other models to see if they fit the data well.

needex %>%
  ggplot(aes(x = pctunins, y = dist_SSP)) +
  
  geom_point() +
  
  geom_smooth(method = "gam", se = FALSE, aes(col = "GAM")) +
  
  geom_smooth(method = "loess", span = .25, se = FALSE, aes(col = "LOESS")) + 
  
  geom_smooth(method = "lm", se = FALSE, alpha =.2, aes(col = "OLS")) +
  
  new_theme + #Add new theme
  
   labs(title = "Model Type for 'needex'",
       x = "Percent Uninsured",
       y = "Distance to Nearest Syringe Programs (mi)",
       caption = "Source: amFAR") +
  
  scale_color_viridis_d("Model", end = .75, option = "C") 

The General Additive Model (GAM) is an extension of linear models with a smoothing function. Thus, it doesn’t differ much from the Linear Regression model using the ordinary least squares (OLS) line. However, the Local Regression (LOESS) model jumps up and down a lot from about 3 - 15 %. LOESS is used to fit multiple regressions in a local range, which is most often applied to smooth a fluctuating time series. In this case, it doesn’t add much relevant detail to the scatterplot at first glance, and this is a scatterplot instead of a time series.

Let’s check the other assumptions.

  1. The observations must be Independent such that there is no relationship between residuals and the predictor X variable.
lm_resid = augment(lm_model, interval = "prediction") #Set interval type as 'predict'
#in a new dataframe

ggplot(lm_resid, aes(x = pctunins, y = .resid)) +
  geom_point() +
  labs(x = "Percent Uninsured", 
       y = "Residuals", 
       title = "Independent Observations") + 
  new_theme 

While at first glance it appears that this scatterplot displays a slight downwards correlation (due to the cluster of points below 0 on the y-axis and between 3 - 20% on the x-axis), we should check the correlation between the predictor variable and the residuals as well as add an OLS line.

lm_resid %>%
  get_correlation(.resid ~ pctunins) #Correlation coefficient between X variable
## # A tibble: 1 x 1
##         cor
##       <dbl>
## 1 -5.14e-16
#and residuals

a2 <- ggplot(lm_resid, aes(x = pctunins, y = .resid)) +
  geom_point() +
  geom_smooth(method = "lm") + #Add the OLS line
  labs(x = "Percent Uninsured", 
       y = "Residuals", 
       title = "Independent Observations") + 
  new_theme 

a2

Surprise (or maybe not)! The correlation coefficient is quite low (orders of magnitude away from a concerning value for the assumption) and adding the line shows a near perfect horizontal slope, which indicates that there is no relationship between the % uninsured and residuals. The observations are independent of each other such that the order in which the data was collected does not matter.

  1. A Normal distribution of residuals would exhibit a symmetric, unimodal, bell-shaped curve.
a3 <- ggplot(lm_resid, aes(x = .resid)) + 
  geom_histogram(bins = 25, color = "lightgrey") + 
  #Color 'lightgrey' to see individual bars 
  labs(title = "Distribution of Residuals",
       x = "Residuals",
       y = "Count") +
  new_theme

a3

While the distribution seems slightly right-skewed, the distribution is unimodal and roughly bell-shaped. We can note these observations, but overall shouldn’t be highly concerned about the shape of this particular distribution.

  1. We should see Equal variance in residuals. In a scatter plot of residuals by fitted values, there should be no relationship. The fitted values are simply the predicted response values given an input.
a4 <- ggplot(lm_resid, aes(x = .fitted, y = .resid)) +
  geom_point() +
  geom_smooth(method = "lm") +
  labs(x = "Fitted Values", y = "Residuals") +
  ggthemes::theme_gdocs() +
  labs(title = "Variance in Residuals") +
  new_theme 

a4

There appears to be no relationship between the residuals and fitted values, which indicates that the residuals vary equally.

If you would also like to see the plots of the assumptions side by side, we can combine these ggplots using one of the features of the patchwork package. One thing to note is that the plots for ‘Independent Observations’ and ‘Variance in Residuals’ look very similar to one another, which is not always the case. Certainly we would want to see no relationships between the axes, but this does not guarantee that the plots would look similar.

a1 + a2 + a3 + a4

In fact, here’s an image from a lab that displays non-similar plots for the assumptions of Independent Observations (leftmost graph) and Equal Variance in Residuals (rightmost graph) from http://www.swarthmore.edu/NatSci/aluby1/stat041/Labs/Lab05-ols.html#.

Now let’s take a look at the coefficients of our linear model:

b <- tidy(lm_model, conf.int = TRUE)
b
## # A tibble: 2 x 7
##   term        estimate std.error statistic  p.value conf.low conf.high
##   <chr>          <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
## 1 (Intercept)    12.5     10.2        1.23 2.21e- 1    -7.51     32.5 
## 2 pctunins        7.82     0.773     10.1  5.56e-22     6.30      9.34
ggplot() +
  geom_pointrange(data = b,
                  mapping = aes(x = estimate, 
                                y = term, 
                              xmin = conf.low, 
                              xmax = conf.high), 
                  color = "blue", 
                  fill = "white", 
                  shape = 22) +
  labs(title = "Parameter Estimates with 95% Confidence Intervals",
       x = "OLS Estimate",
       y = "Term") +
  new_theme

Coefficient plots are helpful in visualizing estimates in a regression model (usually with many parameters) in terms of uncertainty and magnitude of effect - which is not really shown here since we only included one estimate aside from the intercept. They are also used to compare the estimates of models, in which smaller confidence intervals indicate less uncertainty. For more information and in-depth examples exploring the mentioned uses of coefficient plots, you can visit https://cran.r-project.org/web/packages/jtools/vignettes/summ.html.

Congratulations! You have performed all four L.I.N.E. checks, which indicates that using a linear regression model was an appropriate choice to fit to this data.

Bonus Analysis

So far, we’ve only explored the linear relationship between dist_SSP and pctunins - two numerical variables of needex. Let’s check out the relationship between pctunins and metro. Since metro is a categorical variable and binary (metro or non-metro), we should plot the data as a boxplot instead.

means = needex %>% #Create a dataframe for the means
  group_by(metro) %>%
  summarize(mean_pct = mean(pctunins)) %>%
  mutate(group = 1)

needex %>%
  ggplot() +
    geom_boxplot(aes(x = pctunins, y = metro, fill = metro), width = 0.5) +
  labs(title = "Percent Uninsured in Metro and Non-Metro Areas",
       x = "Percent Uninsured",
       y = "Area Status") +
  geom_line(data = means, col = "darkblue", aes(x = mean_pct, y = metro, group = group), size = 1) +
  new_theme +
  theme(legend.position = "none", #Remove legend 
        panel.grid.minor.x = element_blank(),
        panel.grid.major.y = element_blank(),
        axis.line.x = element_line(),
        axis.line.y = element_line())

Note that the vertical lines in the middle of the boxplots represents the median, not the mean. Instead, the diagonal red line connects the mean of the non-metro and metro groups.

The median (and mean) percentage of uninsured people of non-metro appear(s) somewhat higher than metro. For statistical analyses, instead of using a linear model, this calls for a t-test - or more specifically, a two-sample t-test. This t-test will compare the two means to test whether the mean of the non-metro group is significantly different from that of the metro group.

t_res = t.test(pctunins ~ metro, data = needex, var.equal = TRUE)
t_res
## 
##  Two Sample t-test
## 
## data:  pctunins by metro
## t = -4.1892, df = 498, p-value = 3.312e-05
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -2.7055006 -0.9779436
## sample estimates:
##     mean in group metro mean in group non-metro 
##                11.17434                13.01606

The means of the two groups are extremely statistically significant (P = 0.0003) from each other. In addition, we can specifically analyze a subset of the data using the filter() function. For instance:

need1 = needex %>%
  filter(STATEABBREVIATION == "PA") #Filter for data only from PA

head(need1) #Check that you filtered only values from PA
## # A tibble: 6 x 7
##   county    STATEABBREVIATI~ dist_SSP HIVprevalence opioid_RxRate pctunins metro
##   <chr>     <chr>               <dbl>         <dbl>         <dbl>    <dbl> <chr>
## 1 lebanon ~ PA                   60.2          39.7          48.1     10.8 metro
## 2 york cou~ PA                   42.1          70.8          51.6      7.2 metro
## 3 huntingd~ PA                   95.5          48.1          49.9      7.6 non-~
## 4 fayette ~ PA                   23.2          27.1         107        7.6 metro
## 5 clearfie~ PA                   88            25.8          61.3      8.5 non-~
## 6 susqueha~ PA                   28.9          27.9          48       10.2 non-~
t_res_pa = t.test(pctunins ~ metro, data = need1, var.equal = TRUE)
t_res_pa
## 
##  Two Sample t-test
## 
## data:  pctunins by metro
## t = -0.92033, df = 12, p-value = 0.3755
## alternative hypothesis: true difference in means is not equal to 0
## 95 percent confidence interval:
##  -3.656068  1.484639
## sample estimates:
##     mean in group metro mean in group non-metro 
##                8.571429                9.657143

Incredible! When comparing the mean percentages of uninsured people from non-metro and metro areas in PA, they are not statistically significant from each other (P = 0.3755)even though the non-metro mean is still a bit higher. This also demonstrates how the overall patterns found in the whole dataset might mask individual trends in subsets and highlights the importance of analyzing data on multiple levels.


Conclusion

Our hope is that with this exercise, you have developed a greater understanding of linear regression and how it is constructed in R. We have reviewed how to import packages and data into R, perform steps to EDA, adjust aesthetic features of plots, work through stages of fitting a model and check assumptions, and even briefly include how to build and interpret a two-sample t-test. While the analyses of this data led to interesting results, what is more important (and exciting!) is that you are now able to apply what you have learned to construct a linear regression model and test its suitability for almost any dataset. In the scenario that using a linear model is not appropriate, you can look to logistic regression, transformation, or segmentation/clustering of data.

If you’re interested in learning more information about linear regression, see here: http://r-statistics.co/Linear-Regression.html

LS0tDQp0aXRsZTogIkxlc3NvbiBvbiBMaW5lYXIgUmVncmVzc2lvbiINCmF1dGhvcjogIkVsaXphYmV0aCBTaGluIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KIyBMaW5lYXIgUmVncmVzc2lvbjogQW4gT3ZlcnZpZXcNCg0KTGluZWFyIG1vZGVsaW5nIGlzIGEgZnVuZGFtZW50YWwgYXNwZWN0IG9mIHN0YXRpc3RpY3MuIE9uZSBvZiB0aGUgbW9zdCB1YmlxdWl0b3VzIHByZWRpY3RpdmUgbW9kZWxpbmcgbWV0aG9kcywgbGluZWFyIHJlZ3Jlc3Npb24sIGlzIHVzZWZ1bCBmb3IgaXRzIHNpbXBsaWNpdHkgaW4gaW1wbGVtZW50YXRpb24gYW5kIGludGVycHJldGF0aW9uLiBXaGlsZSBsaW5lYXIgcmVncmVzc2lvbiBpcyB1c2VkIHRvIHByZWRpY3QgdGhlIHZhbHVlIG9mIGEgY29udGludW91cyByZXNwb25zZSB2YXJpYWJsZSAqWSogYXMgYSBmdW5jdGlvbiBvZiBvbmUgb3IgbW9yZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMgKlgqIHVzaW5nIGEgbWF0aGVtYXRpY2FsIGZ1bmN0aW9uLCB0aGlzIHRlY2huaXF1ZSBvcGVyYXRlcyB3aXRoIHNldmVyYWwgdW5kZXJseWluZyBhc3N1bXB0aW9ucyBhbmQgaXMgc2Vuc2l0aXZlIHRvIG91dGxpZXJzLiANCg0KTGluZWFyIHJlZ3Jlc3Npb24gaXMgdXNlZnVsIGZvciBhbmFseXppbmcgcmVsYXRpb25zaGlwcyBhbW9uZyB2YXJpYWJsZXMgb2YgaW50ZXJlc3QgYW5kIGRldGVybWluaW5nIHdoZXRoZXIgdGhlcmUgaXMgYSBsaW5lYXIgb3Igbm9uLWxpbmVhciBhc3NvY2lhdGlvbiBhbW9uZyB0aGVtLCB3aGljaCBpcyBlc3NlbnRpYWwgZm9yIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgYW5kIGluIGNob29zaW5nIHN1aXRhYmxlIG1vZGVscy4gVG8gY2hlY2sgd2hldGhlciB0aGUgZGF0YSB3ZSBhcmUgYXR0ZW1wdGluZyB0byBtb2RlbCBpcyBzdWl0YWJsZSBmb3IgbGluZWFyIHJlZ3Jlc3Npb24sIHdlIG11c3QgZXhhbWluZSBmb3VyIGFzc3VtcHRpb25zIHdoaWNoIGFyZSBzdW1tYXJpemVkIGFzICoqTC5JLk4uRSoqOg0KDQogICsgKipMKippbmVhciByZWxhdGlvbnNoaXAgW2JldHdlZW4gdGhlICpYKiBhbmQgKlkqIHZhcmlhYmxlc10NCiAgKyAqKkkqKm5kZXBlbmRlbnQgb2JzZXJ2YXRpb25zIFt0aGVyZSBpcyBubyByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcmVzaWR1YWxzIGFuZCBwcmVkaWN0b3IgdmFyaWFibGUocyldDQogICsgKipOKipvcm1hbCByZXNpZHVhbHMgW3RoZSBkaXN0cmlidXRpb24gc2hvdWxkIGJlICpzeW1tZXRyaWMqLCAqdW5pbW9kYWwqLCBhbmQgYXBwcm94aW1hdGVseSAqYmVsbC1zaGFwZWQqXQ0KICArICoqRSoqcXVhbCB2YXJpYW5jZSBpbiByZXNpZHVhbHMgW3RoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSByZXNpZHVhbHMgYW5kIGZpdHRlZCB2YWx1ZXNdDQogIA0KVGhlIG1hdGhlbWF0aWNhbCBlcXVhdGlvbiBvZiBsaW5lYXIgcmVncmVzc2lvbiBjYW4gYmUgbW9kZWxlZCBhczoNCg0KJFkgPSBcYmV0YV8xICsgXGJldGFfMlggKyBcZXBzaWxvbiQNCg0Kd2hlcmUgJFxiZXRhXzEkIHJlcHJlc2VudHMgdGhlIGludGVyY2VwdCwgJFxiZXRhXzIkIHNpZ25pZmllcyB0aGUgc2xvcGUsIGFuZCAkXGVwc2lsb24kIHN0YW5kcyBmb3IgdGhlIGVycm9yIHRlcm0uICRZJCBpcyB0aGUgb3V0Y29tZSB2YXJpYWJsZSwgd2hpY2ggaXMgcHJlZGljdGVkIGJ5ICRYJCBhcyB0aGUgaW5wdXQgdmFyaWFibGUuIEZ1cnRoZXJtb3JlLCB0aGVyZSBjYW4gYmUgbXVsdGlwbGUgJFgkIHZhcmlhYmxlcyB0aGF0IGFyZSBlaXRoZXIgY29udGludW91cyBvciBjYXRlZ29yaWNhbCAoaS5lLiAkWF8yJCwgJFhfMyQsIGV0Yy4pLg0KDQotLS0NCg0KQXMgYW4gZXhhbXBsZSwgd2Ugd2lsbCBleGFtaW5lIHRoZSBOZWVkbGUgRXhjaGFuZ2UgRGF0YSwgYSBkYXRhc2V0IGdhdGhlcmVkIGZyb20gKiphbUZBUioqICh0aGUgRm91bmRhdGlvbiBvZiBBSURTIFJlc2VhcmNoKSwgdG8gYW5hbHl6ZSBob3cgdGhlIGRpc3RhbmNlIHRvIG5lYXJlc3Qgc3lyaW5nZSBwcm9ncmFtIChgYGBkaXN0X1NTUGBgYCkgcmVsYXRlcyB0byBwZXJjZW50IHVuaW5zdXJlZCAoYGBgcGN0dW5pbnNgYGApLiBJbiB0aGUgZm9sbG93aW5nIG92ZXJ2aWV3LCB3ZSB3aWxsIHdhbGsgdGhyb3VnaCBob3cgdG8gcGVyZm9ybSAqKkVEQSoqIChFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzKSwgcmV2aWV3IHRoZSAqKkwuSS5OLkUuKiogYXNzdW1wdGlvbnMsYW5kIGZpdCB0aGUgbGluZWFyIG1vZGVsIHRvIHRoZSBkYXRhLiBJbiBhZGRpdGlvbiwgd2Ugd2lsbCBwcm92aWRlIGd1aWRhbmNlIG9uIGRhdGEgdmlzdWFsaXphdGlvbiBhbmQgcGxvdCBhZXN0aGV0aWNzLiBGb3IgdGhlIHB1cnBvc2Ugb2YgdGhpcyBleGFtcGxlLCB3ZSB3aWxsIG9ubHkgYW5hbHl6ZSB0aGUgdHdvIHZhcmlhYmxlcyBtZW50aW9uZWQgYWJvdmUuIEFsdGhvdWdoIGl0IGlzIHJlbGF0aXZlbHkgc2ltcGxlIHRvIGFkZCBtb3JlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyB0byBhIGxpbmVhciBtb2RlbCwgYW5hbHl6aW5nIGFuZCBncmFwaGluZyB0aGUgZGF0YSBjYW4gcXVpY2tseSBiZWNvbWUgY29tcGxpY2F0ZWQuIEZvciBmdXJ0aGVyIGluZm9ybWF0aW9uLCBzZWUgdGhpcyB0dXRvcmlhbCBvbiBNdWx0aXZhcmlhdGUgUmVncmVzc2lvbjogPGh0dHBzOi8vb3BlbmludHJvLnNoaW55YXBwcy5pby9pbXMtMDQtbXVsdGl2YXJpYWJsZS1hbmQtbG9naXN0aWMtbW9kZWxzLTAzLyNzZWN0aW9uLW11bHRpcGxlLXJlZ3Jlc3Npb24+DQoNCiMgSW1wb3J0aW5nIHRoZSBEYXRhDQoNClRvIHByZWZhY2UgdGhpcyBvdmVydmlldywgd2UgbXVzdCBpbnN0YWxsIGFuZCBsb2FkIHRoZSBmb2xsb3dpbmcgcGFja2FnZXMgaW50byBSOg0KDQogICsgYGBgdGlkeXZlcnNlYGBgDQogICsgYGBgdGlkeW1vZGVsc2BgYA0KICArIGBgYHBhdGNod29ya2BgYA0KICArIGBgYG1vZGVybmRpdmVgYGANCiAgKyBgYGBHR2FsbHlgYGANCiAgKyBgYGByZWFkeGxgYGANCiAgDQpGaXJzdCwgbG9hZCB0aGUgZGF0YToNCg0KKk5vdGU6IFlvdSBtaWdodCBoYXZlIHRvIHNwZWNpZnkgdGhlIGZpbGUgcGF0aCB0byB0aGUgZGF0YSBpZiBpdCBpcyBub3QgdXBsb2FkZWQgdG8gYSBmb2xkZXIgdGhhdCB5b3UgY2FuIGRpcmVjdGx5IGFjY2VzcyBmcm9tIHlvdXIgJ0hvbWUnIHBhZ2UgYXMgaXMgc2hvd24gYmVsb3cuKg0KDQpUbyBkb3dubG9hZCB0aGVzZSBmaWxlcyB0byBmb2xsb3cgYWxvbmcgd2l0aCB0aGUgZXhlcmNpc2UsIGdvIHRvIHRoaXMgW0dpdEh1YiBwYWdlXSgiaHR0cHM6Ly9naXRodWIuY29tL0VsaTIwMjIvUGVyc29uYWwtUHJvamVjdHMvYmxvYi9tYWluL1Byb2plY3RfTGluZWFyX1JlZ3Jlc3Npb25fTGVzc29uL2Rpc3Rfc3NwX2FtZmFyLmNzdiIpIGZvciB0aGUgYC5jc3YgZmlsZWAgYW5kIFt0aGlzIHBhZ2VdKCJodHRwczovL2dpdGh1Yi5jb20vRWxpMjAyMi9QZXJzb25hbC1Qcm9qZWN0cy9ibG9iL21haW4vUHJvamVjdF9MaW5lYXJfUmVncmVzc2lvbl9MZXNzb24vb3Bpb2lkX2NvdW50eV9jb2RlYm9vay54bHN4IikgZm9yIHRoZSBgLnhsc3ggZmlsZWAuDQoNCg0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHRpZHltb2RlbHMpDQpsaWJyYXJ5KHBhdGNod29yaykNCmxpYnJhcnkobW9kZXJuZGl2ZSkNCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShyZWFkeGwpDQpuZWVkZXggPSByZWFkX2NzdigiZGlzdF9zc3BfYW1mYXIuY3N2IikNCm5lZWRleF9jb2RlYm9vayA8LSByZWFkX2V4Y2VsKCJvcGlvaWRfY291bnR5X2NvZGVib29rLnhsc3giLCBzaGVldCA9ICJEaWN0aW9uYXJ5IikNCiNJdCBtaWdodCBiZSBlYXNpZXIgdG8gb3BlbiBhbmQgcmVhZCB0aGUgLnhsc3ggZmlsZSBpbiBFeGNlbCByYXRoZXIgdGhhbiBpbiBSDQpgYGANCg0KDQoNCldlIG5hbWVkIHRoZSBOZWVkbGUgRXhjaGFuZ2UgRGF0YSBhcyBgYGBuZWVkZXhgYGAgYW5kIHdpbGwgcmVmZXIgdG8gaXQgaGVuY2Vmb3J0aCB3aGVuIGNhbGxpbmcgdGhlIGRhdGEuIFRoZSBgYGBuZWVkZXhfY29kZWJvb2tgYGAgY29udGFpbnMgYSBkZXNjcmlwdGlvbiBmb3IgdGhlIHZhcmlhYmxlIG5hbWVzIHVzZWQgaW4gYGBgbmVlZGV4YGBgIChhcyB3ZWxsIGFzIG1hbnkgb3RoZXJzKS4gWW91IGNhbiBsb2NhdGUgdGhlIHZhcmlhYmxlIG5hbWVzIHF1aWNrbHkgaW4gdGhlIGNvZGVib29rIGJ5IHNlYXJjaGluZyB1c2luZyAqKkN0cmwgKyBGKiouDQoNCg0KDQotLS0NCg0KDQoNCiMgRURBDQoNCg0KDQpUbyBzdGFydCBvZmYsIHdlIHNob3VsZCBvYnNlcnZlIHRoZSB2YXJpYWJsZSB0eXBlcyBpbiB0aGUgZGF0YXNldDoNCg0KDQoNCmBgYHtyIEVEQX0NCmhlYWQobmVlZGV4KQ0KYGBgDQoNCg0KDQpVc2luZyB0aGUgYGBgaGVhZCgpYGBgIGZ1bmN0aW9uLCB3ZSBjYW4gb2JzZXJ2ZSB0aGUgZmlyc3QgNiByb3dzIGFuZCBlYWNoIHZhcmlhYmxlIG5hbWUgaW4gdGhlIGRhdGFzZXQuIFlvdSBtYXkgYWxzbyBzcGVjaWZ5IHRoZSBudW1iZXIgb2Ygcm93cyBvZiBkYXRhIHNob3duIHVzaW5nIGBgYG4gPSAuLi5gYGAsIGJ1dCB0aGUgZGVmYXVsdCBpcyA2IHJvd3MuDQoNCk5vdGljZSB0aGF0IHdlIGhhdmUgdGhyZWUgY2F0ZWdvcmljYWwgdmFyaWFibGVzOiBgYGBjb3VudHlgYGAsIGBgYFNUQVRFQUJCUkVWSUFUSU9OYGBgLCBhbmQgYGBgbWV0cm9gYGAuIE91ciBmb3VyIG51bWVyaWNhbCB2YXJpYWJsZXMgYXJlIGFzIGZvbGxvd3M6IGBgYGRpc3RfU1NQYGBgLCBgYGBISVZwcmV2YWxlbmNlYGBgLCBgYGBvcGlvaWRfUnhSYXRlYGBgLCBhbmQgYGBgcGN0dW5pbnNgYGAuDQoNCllvdSBjb3VsZCBjaGVjayB0aGUgYGBgbmVlZGV4X2NvZGVib29rYGBgIGZvciB0aGUgZGVzY3JpcHRvciBmb3IgZWFjaCB2YXJpYWJsZSwgYnV0IGZvciB0aGUgc2FrZSBvZiBicmV2aXR5IHdlIHdpbGwgY2xhcmlmeSBzZXZlcmFsIG9mIHRoZSB2YXJpYWJsZXM6DQoNCiAgKyBgYGBkaXN0X1NTUGBgYCA9IGRpc3RhbmNlIHRvIG5lYXJlc3Qgc3lyaW5nZSBzZXJ2aWNlcyBwcm9ncmFtIChpbiBtaWxlcykNCiAgKyBgYGBISVZwcmV2YWxlbmNlYGBgID0gIyBsaXZpbmcgcGVvcGxlIGRpYWdub3NlZCB3LyBISVYgcGVyIDEwMCwwMDAgKGluY2x1ZGluZyBhZHVsdHMgYW5kIGFkb2xlc2NlbnRzIDEzKykNCiAgKyBgYGBvcGlvaWRfUnhSYXRlYGBgID0gIyBvcGlvaWQgcHJlc2NyaXB0aW9ucyBwZXIgMTAwIHBlb3BsZQ0KICArIGBgYHBjdHVuaW5zYGBgID0gJSBjaXZpbGlhbiBwb3B1bGF0aW9uIChub3QgaW4gYW4gaW5zdGl0dXRpb24pIHcvbyBoZWFsdGggaW5zdXJhbmNlIGNvdmVyYWdlDQogICsgYGBgbWV0cm9gYGAgPSB3aGV0aGVyIHRoZSBnZW9ncmFwaGljYWwgbG9jYXRpb24gaXMgY29uc2lkZXJlZCBtZXRyb3BvbGl0YW4gb3Igbm90ICooTk9URTogVGhpcyBpcyBhIHBlcnNvbmFsIGludGVycHJldGF0aW9uIGJlY2F1c2UgdGhlIGBgYG5lZWRleF9jb2RlYm9va2BgYCBkaWQgbm90IHByb3ZpZGUgYSBkZWZpbml0aW9uKSoNCg0KV2UgY2FuIHRoZW4gY3JlYXRlIGEgKnBhaXJzIHBsb3QqIC0gdXNpbmcgdGhlIGBgYEdHYWxseWBgYCBwYWNrYWdlIC0gdG8gZGlzcGxheSBhIHNjYXR0ZXJwbG90IG1hdHJpeCBvZiBhbGwgdGhlIHF1YW50aXRhdGl2ZSB2YXJpYWJsZXMgZnJvbSB0aGUgYGBgbmVlZGV4YGBgIGRhdGFzZXQuDQoNCg0KDQpgYGB7ciBQYWlycyBQbG90LCBtZXNzYWdlID0gRkFMU0V9DQpuZWVkZXggJT4lDQogIHNlbGVjdChkaXN0X1NTUCwgSElWcHJldmFsZW5jZSwgb3Bpb2lkX1J4UmF0ZSwgcGN0dW5pbnMpICU+JQ0KR0dhbGx5OjpnZ3BhaXJzKGxvd2VyID0gbGlzdChjb250aW51b3VzID0gd3JhcCgic21vb3RoIiwgc2l6ZT0wLjEpKSkNCmBgYA0KDQoNCg0KTG9va2luZyBhdCB0aGUgKnBhaXJzIHBsb3QqLCBpdCBhcHBlYXJzIHRoYXQgdGhlIHN0cm9uZ2VzdCBjb3JyZWxhdGlvbiBvZiAqKjAuMC40MTMqKiBhbW9uZyB0aGUgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyBpcyBiZXR3ZWVuICBgYGBkaXN0X1NTUGBgYCBhbmQgYGBgcGN0dW5pbnNgYGAgKHNob3duIGF0IHRoZSB0b3AgcmlnaHQpIHdoaWxlIHRoZSBzY2F0dGVycGxvdCBvZiB0aGF0IHJlbGF0aW9uc2hpcCBpcyBkaXNwbGF5ZWQgaW4gdGhlIGJvdHRvbSBsZWZ0LiBUeXBpY2FsbHksIGEgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYmV0d2VlbiAwLjMgdG8gMC43IGlzIGNvbnNpZGVyZWQgdG8gYmUgb2YgKm1vZGVyYXRlKiBzdHJlbmd0aC4gVGh1cywgd2UgY2FuIHNheSB0aGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgYGBkaXN0X1NTUGBgYCBhbmQgYGBgcGN0dW5pbnNgYGAgaXMgYSAqcG9zaXRpdmUqLCBtb2RlcmF0ZSBjb3JyZWxhdGlvbi4NCg0KV2hpbGUgeW91IGtuZXcgZnJvbSBlYXJsaWVyIHRoYXQgd2Ugd2VyZSBnb2luZyB0byBhbmFseXplIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgYGBkaXN0X1NTUGBgYCBhbmQgYGBgcGN0dW5pbnNgYGAsIHdlIGhhdmUgc2hvd24gdGhyb3VnaCAqKkVEQSoqIGhvdyB5b3UgY2FuIGV4cGxvcmUgdGhlIHJlbGF0aW9uc2hpcHMgYW1vbmcgeW91ciB2YXJpYWJsZXMgdG8gZGV0ZXJtaW5lIHdoaWNoIHZhcmlhYmxlcyBhcmUgYXBwcm9wcmlhdGUgdG8gZm9jdXMgb24gYW5kIG1vZGVsIGZ1cnRoZXIuDQoNClRoZSBjb2RlIGZvciBwcm9kdWNpbmcgYSBzY2F0dGVycGxvdCBvZiBgYGBkaXN0X1NTUGBgYCBhbmQgYGBgcGN0dW5pbnNgYGAgaXMgc2hvd246DQoNCg0KDQpgYGB7ciBTY2F0dGVycGxvdH0NCm5lZWRleCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcGN0dW5pbnMsIHkgPSBkaXN0X1NTUCkpICsNCiAgZ2VvbV9wb2ludCgpIA0KDQpgYGANCg0KLS0tDQoNCiMgUGxvdHRpbmcgdGhlIERhdGENCg0KRG9lcyB0aGUgc2NhdHRlcnBsb3QgYWJvdmUgc2VlbSBhIGJpdCBoYXJkIHRvIGludGVycHJldCB3aXRob3V0IGNvbnRleHQ/IEl0IHNob3VsZC4gSW4gUiwgeW91IGNhbiB2aXN1YWxpemUgeW91ciBncmFwaCBpbiBjb3VudGxlc3Mgd2F5cyAtIGFkanVzdGluZyB0aGUgcGFyYW1ldGVycywgY2hhbmdpbmcgdGhlIHRpdGxlcyBhbmQgbGFiZWxzLCBsYXllcmluZyBgYGBnZW9tc2BgYCBhbmQgYGBgYWVzdGhldGljc2BgYCwgYWRkaW5nIGNvbG9ycyBhbmQgZ3JhZGllbnRzIGFuZCBzaGFwZXMgYW5kIHRleHQsIGV0Yy4gV2Ugc2hvdWxkIHN0YXJ0IGJ5IGNoYW5naW5nIHRoZSBsYWJlbHMgb2YgdGhlIHgtIGFuZCB5LWF4aXMgdG8gbW9yZSBhcHByb3ByaWF0ZSB0aXRsZXMuDQoNCg0KDQpgYGB7ciwgIG1lc3NhZ2UgPSBGQUxTRX0NCm5lZWRleCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcGN0dW5pbnMsIHkgPSBkaXN0X1NTUCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgbGFicyh0aXRsZSA9ICJNb2RlbGluZyB0aGUgU3Ryb25nZXN0IENvcnJlbGF0aW9uIG9mICduZWVkZXgnIiwNCiAgICAgICB4ID0gIlBlcmNlbnQgVW5pbnN1cmVkIiwNCiAgICAgICB5ID0gIkRpc3RhbmNlIHRvIE5lYXJlc3QgU3lyaW5nZSBQcm9ncmFtcyAobWkpIiwNCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogYW1GQVIiKSAjQ2FwdGlvbiBpcyBwbGFjZWQgYXV0b21hdGljYWxseSB0byBib3R0b20gcmlnaHQNCmBgYA0KDQoNCg0KVG8gbWFrZSB0aGUgcGxvdCBtb3JlIGFlc3RoZXRpY2FsbHkgcGxlYXNpbmcsIHlvdSBtYXkgYWxzbyB1c2UgdGhlIGBgYHRoZW1lKClgYGAgZnVuY3Rpb24gdG8gYWRqdXN0IHRoZSBzaXplLCBjb2xvciwgZm9udCwgYW5kIHJlbW92ZS9hZGQgb3RoZXIgZmVhdHVyZXMgb2YgdGhlIHRleHQgaW4gdGhlIGxhYmVscy4NCg0KDQoNCmBgYHtyfQ0KbmVlZGV4ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwY3R1bmlucywgeSA9IGRpc3RfU1NQKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHRpdGxlID0gIk1vZGVsaW5nIHRoZSBTdHJvbmdlc3QgQ29ycmVsYXRpb24gb2YgJ25lZWRleCciLA0KICAgICAgIHggPSAiUGVyY2VudCBVbmluc3VyZWQiLA0KICAgICAgIHkgPSAiRGlzdGFuY2UgdG8gTmVhcmVzdCBTeXJpbmdlIFByb2dyYW1zIChtaSkiLA0KICAgICAgIGNhcHRpb24gPSAiU291cmNlOiBhbUZBUiIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJuYXZ5IiwgZmFjZSA9ICJib2xkIiksICNDaGFuZ2VkIGNvbG9yIG9mIHRpdGxlDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwgI1JlbW92ZWQgZ3JpZGxpbmVzDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwgI1JlbW92ZWQgZ3JpZGxpbmVzDQogICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksICNSZW1vdmVkIGdyaWRsaW5lcw0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksICNSZW1vdmVkIGdyaWRsaW5lcw0KICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJsaWdodGdyZXkiKSkgI0NoYW5nZWQgY29sb3Igb2YgcGxvdCBiYWNrZ3JvdW5kDQpgYGANCiAgDQogIA0KICANCldlIHNob3VsZCBhZGQgYSBsZWFzdC1zcXVhcmVzIGxpbmUgdG8gdGhlIGdyYXBoIHNpbmNlIHdlIGhhdmUgYWxyZWFkeSBkZXRlcm1pbmVkIHRoYXQgdGhlcmUgaXMgYSBwb3NpdGl2ZSwgKm1vZGVyYXRlKiBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBleHBsYW5hdG9yeSBhbmQgcmVzcG9uc2UgdmFyaWFibGUuIEluIGFkZGl0aW9uLCBpZiB5b3Ugd291bGQgcmF0aGVyIG5vdCBtYW51YWxseSBjaGFuZ2UgdGhlIGFlc3RoZXRpY3Mgb2YgeW91ciBncmFwaCwgeW91IGNhbiB1c2UgYGBgZ2d0aGVtZXNgYGAgdG8gY2hvb3NlIGZyb20gcmVhZGlseSBhdmFpbGFibGUgZm9ybWF0cyBhdCB5b3VyIHByZWZlcmVuY2UuIEZvciBhbiBvdmVydmlldyBvZiBhdmFpbGFibGUgdGhlbWVzLCB5b3UgY2FuIGNoZWNrIG91dCA8aHR0cHM6Ly95dXRhbm5paGlsYXRpb24uZ2l0aHViLmlvL2FsbFlvdXJGaWd1cmVBcmVCZWxvbmdUb1VzL2dndGhlbWVzLz4uIFRvIHJlZHVjZSB0aGUgYW1vdW50IG9mIGNvZGUgd3JpdHRlbiBpbiB0aGUgZm9sbG93aW5nIGNodW5rLCBJIHNlbGVjdGVkIGFuIGFyYml0cmFyeSB0aGVtZS4gSSBhZGRlZCB0aGUgbGVhc3Qtc3F1YXJlcyBsaW5lIHVzaW5nIGBgYHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIpYGBgLCB3aGljaCBpbnN0cnVjdHMgUiB0byBmaXQgYSAqKmxpbmVhciBtb2RlbCoqIHRvIHRoZSBkYXRhLg0KDQoNCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0NCm5lZWRleCAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcGN0dW5pbnMsIHkgPSBkaXN0X1NTUCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgbGFicyh0aXRsZSA9ICJNb2RlbGluZyB0aGUgU3Ryb25nZXN0IENvcnJlbGF0aW9uIG9mICduZWVkZXgnIiwNCiAgICAgICB4ID0gIlBlcmNlbnQgVW5pbnN1cmVkIiwNCiAgICAgICB5ID0gIkRpc3RhbmNlIHRvIE5lYXJlc3QgU3lyaW5nZSBQcm9ncmFtcyAobWkpIiwNCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogYW1GQVIiKSArDQogIGdndGhlbWVzOjp0aGVtZV9nZG9jcygpICsNCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gImxtIikgDQojTk9URTogQ2hhbmdpbmcgdGhlbWVzIHNoaWZ0ZWQgdGhlIGF1dG9tYXRpYyBwbGFjZW1lbnQgb2YgdGhlIGNhcHRpb24gZnJvbSANCiNib3R0b20gcmlnaHQgdG8gYm90dG9tIGxlZnQNCmBgYA0KDQoNCg0KT25lIHRoaW5nIHlvdSBzaG91bGQgbm90aWNlIGlzIHRoYXQgdGhlIHN0YW5kYXJkIGVycm9yIHJlbWFpbnMgcmVsYXRpdmVseSBzbWFsbCBhcm91bmQgMyAtIDIwIHBlcmNlbnQuIFRoZSBzdGFuZGFyZCBlcnJvciBpbmNyZWFzZXMgYXJvdW5kIHRoZSBsZWFzdC1zcXVhcmVzIGxpbmUgYXQgaGlnaGVyIHBlcmNlbnRhZ2VzIG9mIGNpdmlsaWFucyB3aXRob3V0IGhlYWx0aCBpbnN1cmFuY2UuIFRoaXMgaXMgYSByZXN1bHQgb2YgbGVzcyB2YWx1ZXMgbG9jYXRlZCBiZXR3ZWVuIDIwIC0gMzYlLiBTdGFuZGFyZCBlcnJvciBpbmNyZWFzZXMgd2hlbiBzdGFuZGFyZCBkZXZpYXRpb24gKHZhcmlhbmNlIG9mIGEgcG9wdWxhdGlvbikgaW5jcmVhc2VzLCB3aGljaCBvY2N1cnMgd2hlbiB0aGUgc2FtcGxlIHNpemUgZGVjcmVhc2VzLiBZb3UgY291bGQgcG9zc2libHkgaW50ZXJwcmV0IHRoYXQgdGhlIGxhcmdlciBzdGFuZGFyZCBlcnJvciBiZXR3ZWVuIDIwIC0gMzYgJSBwZXJjZW50IGNvbWVzIGZyb20gdGhlIHNtYWxsZXIgc2FtcGxlIHNpemUgd2l0aGluIHRoYXQgcmFuZ2UuIFNpbmNlIHRoZSBkYXRhIGNsdXN0ZXJzIGJldHdlZW4gMyAtIDIwICUsIHRoZSBzdGFuZGFyZCBlcnJvciBpcyBzbWFsbGVyLg0KDQpJZiB5b3UgbG9vayBjbG9zZWx5LCB5b3UnbGwgc2VlIHRoYXQgbm8gZG90cyBhcHBlYXIgYmV0d2VlbiAwLTMlLiBIb3cgZGlkIHdlIGtub3cgdGhhdCB0aGUgY3V0b2ZmIG9mIHRoZSBsZWFzdC1zcXVhcmVzIGxpbmUgd2FzIGFyb3VuZCAzIHBlcmNlbnQgaW5zdGVhZCBvZiAyIG9yIDIuNSAod2hpY2ggaXNuJ3QgcmVhZGlseSBvYnNlcnZhYmxlIGJ5IHRoZSB0aWNrIG1hcmtzIGFsb25nIHRoZSB4LWF4aXMpPyBZb3UgY2FuIHVzZSB0aGUgYGBgcmFuZ2UoKWBgYCBmdW5jdGlvbiB0byBmaW5kIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIG9mIGEgdmVjdG9yLiBPdXQgb2YgY3VyaW9zaXR5LCB3ZSBjYW4gYWxzbyBleGFtaW5lIHRoZSBtaW4gYW5kIG1heCBvZiB0aGUgb3V0Y29tZSB2YXJpYWJsZSBhcyB3ZWxsLiANCg0KDQoNCmBgYHtyIFJhbmdlc30NCnBjdCA8LSBuZWVkZXgkcGN0dW5pbnMNCnJhbmdlKHBjdCkgI01pbiBhbmQgTWF4IG9mIFBlcmNlbnQgVW5pbnN1cmVkDQoNCmRpc3QgPC0gbmVlZGV4JGRpc3RfU1NQDQpyYW5nZShkaXN0KSAjTWluIGFuZCBNYXggb2YgRGlzdGFuY2UgZnJvbSBOZWFyZXN0IFByb2dyYW0NCmBgYA0KDQoNCg0KSW50ZXJlc3RpbmchIEl0IGFwcGVhcnMgdGhhdCB0aGUgcmFuZ2Ugb2YgcGVvcGxlIHdpdGhvdXQgaGVhbHRoIGluc3VyYW5jZSBmYWxscyBiZXR3ZWVuIDMgLSAzNiAlLCB3aGlsZSB0aGUgZGlzdGFuY2UgdG8gdGhlIG5lYXJlc3Qgc3lyaW5nZSBwcm9ncmFtIGNhbiByYW5nZSBiZXR3ZWVuIDAgLSA1MTAgbWlsZXMuIDUxMCBtaWxlcyBpcyBsb25nZXIgdGhhbiB0aGUgbGVuZ3RoIG9mIEZsb3JpZGEhIA0KDQpIb3dldmVyLCB0aGlzIG9ubHkgdGVsbHMgdXMgc28gbXVjaCBhYm91dCB0aGUgZGF0YS4gVG8gZ2FpbiBtb3JlIGluc2lnaHQsIHdlIHdpbGwgZml0IHRoZSBsaW5lYXIgbW9kZWwgdG8gdGhlIGRhdGEgYW5kIGNoZWNrIHRoZSAqKkwuSS5OLkUuKiogYXNzdW1wdGlvbnMgdG8gZGV0ZXJtaW5lIGhvdyB3ZWxsIHRoZSBtb2RlbCBzdWl0cyB0aGUgZGF0YS4NCg0KLS0tDQoNCiMgRml0dGluZyB0aGUgTGluZWFyIE1vZGVsDQoNCldlIGZpdCBvdXIgbW9kZWwgdG8gdGhlIHZhcmlhYmxlcyBvZiBpbnRlcmVzdDoNCg0KDQoNCmBgYHtyIExpbmVhciBNb2RlbH0NCmxtX21vZGVsID0gbG0oZGlzdF9TU1AgfiBwY3R1bmlucywgZGF0YSA9IG5lZWRleCkgI0ZpdHRpbmcgdGhlIGxpbmVhciBtb2RlbA0KDQpnZXRfcmVncmVzc2lvbl90YWJsZShsbV9tb2RlbCkgI091dHB1dCBvZiByZWdyZXNzaW9uIHRhYmxlDQoNCm5lZWRleCAlPiUgI0NvcnJlbGF0aW9uIGJldHdlZW4gWCBhbmQgWSB2YXJpYWJsZQ0KICBnZXRfY29ycmVsYXRpb24oZGlzdF9TU1AgfiBwY3R1bmlucykNCmBgYA0KDQoNCg0KRml0dGluZyB0aGUgbW9kZWwgZG9lc24ndCBtZWFuIHRoYXQgUiB3aWxsIGF1dG9tYXRpY2FsbHkgZGlzcGxheSB0aGUgdmFsdWVzIG9mIHRoYXQgbGluZWFyIG1vZGVsIHRvIHVzLiBXZSB1c2VkIHRoZSBmdW5jdGlvbiBgYGBnZXRfcmVncmVzc2lvbl90YWJsZSgpYGBgIHRvIG91dHB1dCB0aGUgcmVncmVzc2lvbiB0YWJsZS4gV2UgdGhlbiBjYWxjdWxhdGVkIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGBgYGRpc3RfU1NQYGBgIGFuZCBgYGBwY3R1bmluc2BgYCBhbmQgZ290IGBgYDAuNDEyNjc0NGBgYCBpbiBSIE1hcmtkb3duLiBMb29rIGZhbWlsaWFyPyBJdCdzIGV4YWN0bHkgdGhlIHNhbWUgYXMgdGhlIGBgYDAuNDEzYGBgIGNvcnJlbGF0aW9uIGZyb20gdGhlICpwYWlycyBwbG90KiEgV2UganVzdCB3YW50ZWQgdG8gZGVtb25zdHJhdGUgaG93IHRvIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBhbmQgYW1vbmcgYWxsIHRoZSBudW1lcmljYWwgdmFyaWFibGVzIG9mIGBgYG5lZWRleGBgYCBtYW51YWxseSwgeWV0IGFsc28gaG93IG11Y2ggc2ltcGxlciAoYW5kIGZhc3RlciEpIGBgYEdHYWxseWBgYCBwYWNrYWdlIGNhbiBtYWtlIGl0IGZvciB1cyEgVGhlIGJlYXV0eSBvZiBSIGlzIGhvdyB2ZXJzYXRpbGUgaXQgaXMgaW4gdGVybXMgb2YgZnVuY3Rpb24gYW5kIGFjY2Vzc2liaWxpdHkgdG8gc3RhdGlzdGljaWFucyBvZiBhbGwgbGV2ZWxzIQ0KDQojIyBBc3N1bXB0aW9ucw0KDQpOb3cgdGhhdCB3ZSBoYXZlIHBlcmZvcm1lZCBFREEsIHBsb3R0ZWQgdGhlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZXhwbGFuYXRvcnkgKlgqIHZhcmlhYmxlIGFuZCBvdXRjb21lICpZKiB2YXJpYWJsZSwgYW5kIGZpdHRlZCB0aGUgbW9kZWwgdG8gdGhlIGRhdGEsIHdlIG11c3QgY2hlY2sgaG93IHN1aXRhYmxlIHRoZSBtb2RlbCBieSBjaGVja2luZyB0aGUgKipMLkkuTi5FLioqIGFzc3VtcHRpb25zLg0KDQoNCg0KMSkgVGhlcmUgbXVzdCBiZSBhICoqTCoqaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gaW5kZXBlbmRlbnQgYW5kIGRlcGVuZGVudCB2YXJpYWJsZXMuIA0KDQpXZSBoYXZlIGFscmVhZHkgc2hvd24gdGhpcyBtdWx0aXBsZSB0aW1lcyBhYm92ZSwgYnV0IHdlIHdpbGwgaW5jbHVkZSBhIGdyYXBoIGhlcmUuIFdlIHdpbGwgZm9ybWF0IHRoaXMgaW4gYSBuZXcgdGhlbWUgZm9yIGZ1biBzaW5jZSB3ZSdyZSBpbiBhIG5ldyBzZWN0aW9uLiBJbiBhZGRpdGlvbiwgaWYgeW91IHdpc2ggdG8gcmVtb3ZlIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbGluZSBmcm9tIHRoZSBzY2F0dGVycGxvdCwgdGhlbiBzaW1wbHkgc2V0IGBgYHNlID0gRkFMU0VgYGAgaW5zaWRlIGBgYHN0YXRfc21vb3RoKClgYGAuIA0KDQoNCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0NCm5ld190aGVtZSA9IHRoZW1lX21pbmltYWwoKSAjTmV3IHRoZW1lIGlzIHRoaXMgc3R5bGUNCg0KYTEgPC0gbmVlZGV4ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwY3R1bmlucywgeSA9IGRpc3RfU1NQKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHRpdGxlID0gIk1vZGVsaW5nICduZWVkZXgnIiwNCiAgICAgICB4ID0gIlBlcmNlbnQgVW5pbnN1cmVkIiwNCiAgICAgICB5ID0gIkRpc3RhbmNlIHRvIE5lYXJlc3QgU3lyaW5nZSBQcm9ncmFtcyAobWkpIiwNCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogYW1GQVIiKSArDQogIG5ld190aGVtZSArICNDYWxsIG5ldyB0aGVtZQ0KICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKSANCg0KYTENCmBgYA0KDQoNCg0KV2hpbGUgd2Uga25vdyB0aGF0IHRoZSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBpcyBvbmx5IG9mICptb2RlcmF0ZSogc3RyZW5ndGgsIGl0IHdhcyB0aGUgc3Ryb25nZXN0IGNvcnJlbGF0aW9uIGFtb25nIHRoZSBudW1lcmljYWwgdmFyaWFibGVzIG9mIGBgYG5lZWRleGBgYCBhbmQgc28gd2FzIHRoZSBtb3N0IHN1aXRlZCBmb3IgYSBsaW5lYXIgbW9kZWwuIEp1c3QgdG8gZG91YmxlIGNoZWNrLCBsZXQncyBsYXllciBvdGhlciBtb2RlbHMgdG8gc2VlIGlmIHRoZXkgZml0IHRoZSBkYXRhIHdlbGwuIA0KDQoNCg0KYGBge3IgQWxsIE1vZGVscywgbWVzc2FnZSA9IEZBTFNFfQ0KbmVlZGV4ICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBwY3R1bmlucywgeSA9IGRpc3RfU1NQKSkgKw0KICANCiAgZ2VvbV9wb2ludCgpICsNCiAgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJnYW0iLCBzZSA9IEZBTFNFLCBhZXMoY29sID0gIkdBTSIpKSArDQogIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiLCBzcGFuID0gLjI1LCBzZSA9IEZBTFNFLCBhZXMoY29sID0gIkxPRVNTIikpICsgDQogIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBhbHBoYSA9LjIsIGFlcyhjb2wgPSAiT0xTIikpICsNCiAgDQogIG5ld190aGVtZSArICNBZGQgbmV3IHRoZW1lDQogIA0KICAgbGFicyh0aXRsZSA9ICJNb2RlbCBUeXBlIGZvciAnbmVlZGV4JyIsDQogICAgICAgeCA9ICJQZXJjZW50IFVuaW5zdXJlZCIsDQogICAgICAgeSA9ICJEaXN0YW5jZSB0byBOZWFyZXN0IFN5cmluZ2UgUHJvZ3JhbXMgKG1pKSIsDQogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IGFtRkFSIikgKw0KICANCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKCJNb2RlbCIsIGVuZCA9IC43NSwgb3B0aW9uID0gIkMiKSANCmBgYA0KDQoNCg0KVGhlIEdlbmVyYWwgQWRkaXRpdmUgTW9kZWwgKEdBTSkgaXMgYW4gZXh0ZW5zaW9uIG9mIGxpbmVhciBtb2RlbHMgd2l0aCBhIHNtb290aGluZyBmdW5jdGlvbi4gVGh1cywgaXQgZG9lc24ndCBkaWZmZXIgbXVjaCBmcm9tIHRoZSBMaW5lYXIgUmVncmVzc2lvbiBtb2RlbCB1c2luZyB0aGUgb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcyAoT0xTKSBsaW5lLiBIb3dldmVyLCB0aGUgTG9jYWwgUmVncmVzc2lvbiAoTE9FU1MpIG1vZGVsIGp1bXBzIHVwIGFuZCBkb3duIGEgbG90IGZyb20gYWJvdXQgMyAtIDE1ICUuIExPRVNTIGlzIHVzZWQgdG8gZml0IG11bHRpcGxlIHJlZ3Jlc3Npb25zIGluIGEgbG9jYWwgcmFuZ2UsIHdoaWNoIGlzIG1vc3Qgb2Z0ZW4gYXBwbGllZCB0byBzbW9vdGggYSBmbHVjdHVhdGluZyB0aW1lIHNlcmllcy4gSW4gdGhpcyBjYXNlLCBpdCBkb2Vzbid0IGFkZCBtdWNoIHJlbGV2YW50IGRldGFpbCB0byB0aGUgc2NhdHRlcnBsb3QgYXQgZmlyc3QgZ2xhbmNlLCBhbmQgdGhpcyBpcyBhIHNjYXR0ZXJwbG90IGluc3RlYWQgb2YgYSB0aW1lIHNlcmllcy4NCg0KTGV0J3MgY2hlY2sgdGhlIG90aGVyIGFzc3VtcHRpb25zLg0KDQoNCg0KMikgVGhlIG9ic2VydmF0aW9ucyBtdXN0IGJlICoqSSoqbmRlcGVuZGVudCBzdWNoIHRoYXQgdGhlcmUgaXMgKm5vKiByZWxhdGlvbnNoaXAgYmV0d2VlbiByZXNpZHVhbHMgYW5kIHRoZSBwcmVkaWN0b3IgKlgqIHZhcmlhYmxlLiANCg0KDQoNCmBgYHtyIEluZGVwZW5kZW50IE9ic2VydmF0aW9uc30NCmxtX3Jlc2lkID0gYXVnbWVudChsbV9tb2RlbCwgaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIpICNTZXQgaW50ZXJ2YWwgdHlwZSBhcyAncHJlZGljdCcNCiNpbiBhIG5ldyBkYXRhZnJhbWUNCg0KZ2dwbG90KGxtX3Jlc2lkLCBhZXMoeCA9IHBjdHVuaW5zLCB5ID0gLnJlc2lkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHggPSAiUGVyY2VudCBVbmluc3VyZWQiLCANCiAgICAgICB5ID0gIlJlc2lkdWFscyIsIA0KICAgICAgIHRpdGxlID0gIkluZGVwZW5kZW50IE9ic2VydmF0aW9ucyIpICsgDQogIG5ld190aGVtZSANCmBgYA0KDQoNCg0KV2hpbGUgYXQgZmlyc3QgZ2xhbmNlIGl0IGFwcGVhcnMgdGhhdCB0aGlzIHNjYXR0ZXJwbG90IGRpc3BsYXlzIGEgc2xpZ2h0IGRvd253YXJkcyBjb3JyZWxhdGlvbiAoZHVlIHRvIHRoZSBjbHVzdGVyIG9mIHBvaW50cyBiZWxvdyAwIG9uIHRoZSB5LWF4aXMgYW5kIGJldHdlZW4gMyAtIDIwJSBvbiB0aGUgeC1heGlzKSwgd2Ugc2hvdWxkIGNoZWNrIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwcmVkaWN0b3IgdmFyaWFibGUgYW5kIHRoZSByZXNpZHVhbHMgYXMgd2VsbCBhcyBhZGQgYW4gT0xTIGxpbmUuDQoNCg0KDQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQ0KbG1fcmVzaWQgJT4lDQogIGdldF9jb3JyZWxhdGlvbigucmVzaWQgfiBwY3R1bmlucykgI0NvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGJldHdlZW4gWCB2YXJpYWJsZQ0KI2FuZCByZXNpZHVhbHMNCg0KYTIgPC0gZ2dwbG90KGxtX3Jlc2lkLCBhZXMoeCA9IHBjdHVuaW5zLCB5ID0gLnJlc2lkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArICNBZGQgdGhlIE9MUyBsaW5lDQogIGxhYnMoeCA9ICJQZXJjZW50IFVuaW5zdXJlZCIsIA0KICAgICAgIHkgPSAiUmVzaWR1YWxzIiwgDQogICAgICAgdGl0bGUgPSAiSW5kZXBlbmRlbnQgT2JzZXJ2YXRpb25zIikgKyANCiAgbmV3X3RoZW1lIA0KDQphMg0KYGBgDQoNCg0KDQpTdXJwcmlzZSAob3IgbWF5YmUgbm90KSEgVGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGlzIHF1aXRlIGxvdyAob3JkZXJzIG9mIG1hZ25pdHVkZSBhd2F5IGZyb20gYSBjb25jZXJuaW5nIHZhbHVlIGZvciB0aGUgYXNzdW1wdGlvbikgYW5kIGFkZGluZyB0aGUgbGluZSBzaG93cyBhIG5lYXIgcGVyZmVjdCBob3Jpem9udGFsIHNsb3BlLCB3aGljaCBpbmRpY2F0ZXMgdGhhdCB0aGVyZSBpcyBubyByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgJSB1bmluc3VyZWQgYW5kIHJlc2lkdWFscy4gVGhlIG9ic2VydmF0aW9ucyBhcmUgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlciBzdWNoIHRoYXQgdGhlIG9yZGVyIGluIHdoaWNoIHRoZSBkYXRhIHdhcyBjb2xsZWN0ZWQgZG9lcyAqbm90KiBtYXR0ZXIuDQoNCg0KDQozKSBBICoqTioqb3JtYWwgZGlzdHJpYnV0aW9uIG9mIHJlc2lkdWFscyB3b3VsZCBleGhpYml0IGEgc3ltbWV0cmljLCB1bmltb2RhbCwgYmVsbC1zaGFwZWQgY3VydmUuIA0KDQoNCg0KYGBge3IgTm9ybWFsIFJlc2lkdWFsIERpc3RyaWJ1dGlvbn0NCmEzIDwtIGdncGxvdChsbV9yZXNpZCwgYWVzKHggPSAucmVzaWQpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUsIGNvbG9yID0gImxpZ2h0Z3JleSIpICsgDQogICNDb2xvciAnbGlnaHRncmV5JyB0byBzZWUgaW5kaXZpZHVhbCBiYXJzIA0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBSZXNpZHVhbHMiLA0KICAgICAgIHggPSAiUmVzaWR1YWxzIiwNCiAgICAgICB5ID0gIkNvdW50IikgKw0KICBuZXdfdGhlbWUNCg0KYTMNCmBgYA0KDQoNCg0KV2hpbGUgdGhlIGRpc3RyaWJ1dGlvbiBzZWVtcyBzbGlnaHRseSByaWdodC1za2V3ZWQsIHRoZSBkaXN0cmlidXRpb24gaXMgdW5pbW9kYWwgYW5kIHJvdWdobHkgYmVsbC1zaGFwZWQuIFdlIGNhbiBub3RlIHRoZXNlIG9ic2VydmF0aW9ucywgYnV0IG92ZXJhbGwgc2hvdWxkbid0IGJlIGhpZ2hseSBjb25jZXJuZWQgYWJvdXQgdGhlIHNoYXBlIG9mIHRoaXMgcGFydGljdWxhciBkaXN0cmlidXRpb24uDQoNCg0KDQo0KSBXZSBzaG91bGQgc2VlICoqRSoqcXVhbCB2YXJpYW5jZSBpbiByZXNpZHVhbHMuIEluIGEgc2NhdHRlciBwbG90IG9mIHJlc2lkdWFscyBieSBmaXR0ZWQgdmFsdWVzLCB0aGVyZSBzaG91bGQgYmUgbm8gcmVsYXRpb25zaGlwLiBUaGUgZml0dGVkIHZhbHVlcyBhcmUgc2ltcGx5IHRoZSBwcmVkaWN0ZWQgcmVzcG9uc2UgdmFsdWVzIGdpdmVuIGFuIGlucHV0Lg0KDQoNCg0KYGBge3IgRXF1YWwgUmVzaWR1YWwgVmFyaWFuY2UsIG1lc3NhZ2UgPSBGQUxTRX0NCmE0IDwtIGdncGxvdChsbV9yZXNpZCwgYWVzKHggPSAuZml0dGVkLCB5ID0gLnJlc2lkKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArDQogIGxhYnMoeCA9ICJGaXR0ZWQgVmFsdWVzIiwgeSA9ICJSZXNpZHVhbHMiKSArDQogIGdndGhlbWVzOjp0aGVtZV9nZG9jcygpICsNCiAgbGFicyh0aXRsZSA9ICJWYXJpYW5jZSBpbiBSZXNpZHVhbHMiKSArDQogIG5ld190aGVtZSANCg0KYTQNCmBgYA0KDQoNCg0KVGhlcmUgYXBwZWFycyB0byBiZSBubyByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcmVzaWR1YWxzIGFuZCBmaXR0ZWQgdmFsdWVzLCB3aGljaCBpbmRpY2F0ZXMgdGhhdCB0aGUgcmVzaWR1YWxzIHZhcnkgZXF1YWxseS4NCg0KDQoNCklmIHlvdSB3b3VsZCBhbHNvIGxpa2UgdG8gc2VlIHRoZSBwbG90cyBvZiB0aGUgYXNzdW1wdGlvbnMgc2lkZSBieSBzaWRlLCB3ZSBjYW4gY29tYmluZSB0aGVzZSBgYGBnZ3Bsb3RzYGBgIHVzaW5nIG9uZSBvZiB0aGUgZmVhdHVyZXMgb2YgdGhlIGBgYHBhdGNod29ya2BgYCBwYWNrYWdlLiBPbmUgdGhpbmcgdG8gbm90ZSBpcyB0aGF0IHRoZSBwbG90cyBmb3IgJ0luZGVwZW5kZW50IE9ic2VydmF0aW9ucycgYW5kICdWYXJpYW5jZSBpbiBSZXNpZHVhbHMnIGxvb2sgdmVyeSBzaW1pbGFyIHRvIG9uZSBhbm90aGVyLCB3aGljaCBpcyBub3QgYWx3YXlzIHRoZSBjYXNlLiBDZXJ0YWlubHkgd2Ugd291bGQgd2FudCB0byBzZWUgKm5vKiByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlIGF4ZXMsIGJ1dCB0aGlzIGRvZXMgbm90IGd1YXJhbnRlZSB0aGF0IHRoZSBwbG90cyB3b3VsZCBsb29rIHNpbWlsYXIuDQoNCg0KDQpgYGB7ciBTaWRlIGJ5IFNpZGUgUGxvdHMsIG1lc3NhZ2UgPSBGQUxTRX0NCmExICsgYTIgKyBhMyArIGE0DQpgYGANCg0KDQoNCkluIGZhY3QsIGhlcmUncyBhbiBpbWFnZSBmcm9tIGEgbGFiIHRoYXQgZGlzcGxheXMgbm9uLXNpbWlsYXIgcGxvdHMgZm9yIHRoZSBhc3N1bXB0aW9ucyBvZiAqKkkqKm5kZXBlbmRlbnQgT2JzZXJ2YXRpb25zIChsZWZ0bW9zdCBncmFwaCkgYW5kICoqRSoqcXVhbCBWYXJpYW5jZSBpbiBSZXNpZHVhbHMgKHJpZ2h0bW9zdCBncmFwaCkgZnJvbSA8aHR0cDovL3d3dy5zd2FydGhtb3JlLmVkdS9OYXRTY2kvYWx1YnkxL3N0YXQwNDEvTGFicy9MYWIwNS1vbHMuaHRtbCM+Lg0KDQoNCg0KYGBge3IgUGljdHVyZSwgZWNobyA9IEZBTFNFfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoJ0xJTkUgYXNzdW1wdGlvbnMuanBnJykgI1RoaXMgd2lsbCBvbmx5IGxvYWQgaWYgeW91IGhhdmUNCiN0aGUganBnIGZpbGUgaW4geW91ciBkaXJlY3QgZm9sZGVyDQpgYGANCg0KDQoNCk5vdyBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgY29lZmZpY2llbnRzIG9mIG91ciBsaW5lYXIgbW9kZWw6DQoNCg0KDQpgYGB7cn0NCmIgPC0gdGlkeShsbV9tb2RlbCwgY29uZi5pbnQgPSBUUlVFKQ0KYg0KYGBgDQoNCg0KDQpgYGB7ciBDb2VmZmljaWVudCBQbG90fQ0KZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50cmFuZ2UoZGF0YSA9IGIsDQogICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBlc3RpbWF0ZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSB0ZXJtLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhtaW4gPSBjb25mLmxvdywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4bWF4ID0gY29uZi5oaWdoKSwgDQogICAgICAgICAgICAgICAgICBjb2xvciA9ICJibHVlIiwgDQogICAgICAgICAgICAgICAgICBmaWxsID0gIndoaXRlIiwgDQogICAgICAgICAgICAgICAgICBzaGFwZSA9IDIyKSArDQogIGxhYnModGl0bGUgPSAiUGFyYW1ldGVyIEVzdGltYXRlcyB3aXRoIDk1JSBDb25maWRlbmNlIEludGVydmFscyIsDQogICAgICAgeCA9ICJPTFMgRXN0aW1hdGUiLA0KICAgICAgIHkgPSAiVGVybSIpICsNCiAgbmV3X3RoZW1lDQpgYGANCg0KDQoNCkNvZWZmaWNpZW50IHBsb3RzIGFyZSBoZWxwZnVsIGluIHZpc3VhbGl6aW5nIGVzdGltYXRlcyBpbiBhIHJlZ3Jlc3Npb24gbW9kZWwgKHVzdWFsbHkgd2l0aCBtYW55IHBhcmFtZXRlcnMpIGluIHRlcm1zIG9mIHVuY2VydGFpbnR5IGFuZCBtYWduaXR1ZGUgb2YgZWZmZWN0IC0gd2hpY2ggaXMgbm90IHJlYWxseSBzaG93biBoZXJlIHNpbmNlIHdlIG9ubHkgaW5jbHVkZWQgb25lIGVzdGltYXRlIGFzaWRlIGZyb20gdGhlIGludGVyY2VwdC4gVGhleSBhcmUgYWxzbyB1c2VkIHRvIGNvbXBhcmUgdGhlIGVzdGltYXRlcyBvZiBtb2RlbHMsIGluIHdoaWNoIHNtYWxsZXIgY29uZmlkZW5jZSBpbnRlcnZhbHMgaW5kaWNhdGUgbGVzcyB1bmNlcnRhaW50eS4gRm9yIG1vcmUgaW5mb3JtYXRpb24gYW5kIGluLWRlcHRoIGV4YW1wbGVzIGV4cGxvcmluZyB0aGUgbWVudGlvbmVkIHVzZXMgb2YgY29lZmZpY2llbnQgcGxvdHMsIHlvdSBjYW4gdmlzaXQgPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9qdG9vbHMvdmlnbmV0dGVzL3N1bW0uaHRtbD4uDQoNCg0KDQo+ICpDb25ncmF0dWxhdGlvbnMhKiBZb3UgaGF2ZSBwZXJmb3JtZWQgYWxsIGZvdXIgKipMLkkuTi5FLioqIGNoZWNrcywgd2hpY2ggaW5kaWNhdGVzIHRoYXQgdXNpbmcgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3YXMgYW4gYXBwcm9wcmlhdGUgY2hvaWNlIHRvIGZpdCB0byB0aGlzIGRhdGEuDQoNCiMjIEJvbnVzIEFuYWx5c2lzDQoNClNvIGZhciwgd2UndmUgb25seSBleHBsb3JlZCB0aGUgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGBgYGRpc3RfU1NQYGBgIGFuZCBgYGBwY3R1bmluc2BgYCAtIHR3byBudW1lcmljYWwgdmFyaWFibGVzIG9mIGBgYG5lZWRleGBgYC4gTGV0J3MgY2hlY2sgb3V0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBgYGBwY3R1bmluc2BgYCBhbmQgYGBgbWV0cm9gYGAuIFNpbmNlIGBgYG1ldHJvYGBgIGlzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgKmFuZCogYmluYXJ5IChgYGBtZXRyb2BgYCBvciBgYGBub24tbWV0cm9gYGApLCB3ZSBzaG91bGQgcGxvdCB0aGUgZGF0YSBhcyBhIGJveHBsb3QgaW5zdGVhZC4NCg0KDQoNCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9DQptZWFucyA9IG5lZWRleCAlPiUgI0NyZWF0ZSBhIGRhdGFmcmFtZSBmb3IgdGhlIG1lYW5zDQogIGdyb3VwX2J5KG1ldHJvKSAlPiUNCiAgc3VtbWFyaXplKG1lYW5fcGN0ID0gbWVhbihwY3R1bmlucykpICU+JQ0KICBtdXRhdGUoZ3JvdXAgPSAxKQ0KDQpuZWVkZXggJT4lDQogIGdncGxvdCgpICsNCiAgICBnZW9tX2JveHBsb3QoYWVzKHggPSBwY3R1bmlucywgeSA9IG1ldHJvLCBmaWxsID0gbWV0cm8pLCB3aWR0aCA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIlBlcmNlbnQgVW5pbnN1cmVkIGluIE1ldHJvIGFuZCBOb24tTWV0cm8gQXJlYXMiLA0KICAgICAgIHggPSAiUGVyY2VudCBVbmluc3VyZWQiLA0KICAgICAgIHkgPSAiQXJlYSBTdGF0dXMiKSArDQogIGdlb21fbGluZShkYXRhID0gbWVhbnMsIGNvbCA9ICJkYXJrYmx1ZSIsIGFlcyh4ID0gbWVhbl9wY3QsIHkgPSBtZXRybywgZ3JvdXAgPSBncm91cCksIHNpemUgPSAxKSArDQogIG5ld190aGVtZSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgI1JlbW92ZSBsZWdlbmQgDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZSgpLA0KICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfbGluZSgpKQ0KYGBgDQoNCg0KDQpOb3RlIHRoYXQgdGhlIHZlcnRpY2FsIGxpbmVzIGluIHRoZSBtaWRkbGUgb2YgdGhlIGJveHBsb3RzIHJlcHJlc2VudHMgdGhlICoqbWVkaWFuKiosICpub3QqIHRoZSAqKm1lYW4qKi4gSW5zdGVhZCwgdGhlIGRpYWdvbmFsIHJlZCBsaW5lIGNvbm5lY3RzIHRoZSAqKm1lYW4qKiBvZiB0aGUgYGBgbm9uLW1ldHJvYGBgIGFuZCBgYGBtZXRyb2BgYCBncm91cHMuDQoNClRoZSBtZWRpYW4gKGFuZCBtZWFuKSBwZXJjZW50YWdlIG9mIHVuaW5zdXJlZCBwZW9wbGUgb2YgYGBgbm9uLW1ldHJvYGBgIGFwcGVhcihzKSBzb21ld2hhdCBoaWdoZXIgdGhhbiBgYGBtZXRyb2BgYC4gRm9yIHN0YXRpc3RpY2FsIGFuYWx5c2VzLCBpbnN0ZWFkIG9mIHVzaW5nIGEgbGluZWFyIG1vZGVsLCB0aGlzIGNhbGxzIGZvciBhIHQtdGVzdCAtIG9yIG1vcmUgc3BlY2lmaWNhbGx5LCBhICoqdHdvLXNhbXBsZSB0LXRlc3QqKi4gVGhpcyB0LXRlc3Qgd2lsbCBjb21wYXJlIHRoZSB0d28gbWVhbnMgdG8gdGVzdCB3aGV0aGVyIHRoZSBtZWFuIG9mIHRoZSBgYGBub24tbWV0cm9gYGAgZ3JvdXAgaXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSB0aGF0IG9mIHRoZSBgYGBtZXRyb2BgYCBncm91cC4NCg0KDQoNCmBgYHtyIHQtdGVzdH0NCnRfcmVzID0gdC50ZXN0KHBjdHVuaW5zIH4gbWV0cm8sIGRhdGEgPSBuZWVkZXgsIHZhci5lcXVhbCA9IFRSVUUpDQp0X3Jlcw0KYGBgDQoNCg0KDQpUaGUgbWVhbnMgb2YgdGhlIHR3byBncm91cHMgYXJlIGV4dHJlbWVseSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50ICgqUCogPSAwLjAwMDMpIGZyb20gZWFjaCBvdGhlci4gSW4gYWRkaXRpb24sIHdlIGNhbiBzcGVjaWZpY2FsbHkgYW5hbHl6ZSBhIHN1YnNldCBvZiB0aGUgZGF0YSB1c2luZyB0aGUgYGBgZmlsdGVyKClgYGAgZnVuY3Rpb24uIEZvciBpbnN0YW5jZToNCg0KDQpgYGB7ciBmbCB0LXRlc3R9DQpuZWVkMSA9IG5lZWRleCAlPiUNCiAgZmlsdGVyKFNUQVRFQUJCUkVWSUFUSU9OID09ICJQQSIpICNGaWx0ZXIgZm9yIGRhdGEgb25seSBmcm9tIFBBDQoNCmhlYWQobmVlZDEpICNDaGVjayB0aGF0IHlvdSBmaWx0ZXJlZCBvbmx5IHZhbHVlcyBmcm9tIFBBDQoNCnRfcmVzX3BhID0gdC50ZXN0KHBjdHVuaW5zIH4gbWV0cm8sIGRhdGEgPSBuZWVkMSwgdmFyLmVxdWFsID0gVFJVRSkNCnRfcmVzX3BhDQpgYGANCg0KDQoNCkluY3JlZGlibGUhIFdoZW4gY29tcGFyaW5nIHRoZSBtZWFuIHBlcmNlbnRhZ2VzIG9mIHVuaW5zdXJlZCBwZW9wbGUgZnJvbSBgYGBub24tbWV0cm9gYGAgYW5kIGBgYG1ldHJvYGBgIGFyZWFzIGluIFBBLCB0aGV5IGFyZSBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBmcm9tIGVhY2ggb3RoZXIgKCpQKiA9IDAuMzc1NSlldmVuIHRob3VnaCB0aGUgYGBgbm9uLW1ldHJvYGBgIG1lYW4gaXMgc3RpbGwgYSBiaXQgaGlnaGVyLiBUaGlzIGFsc28gZGVtb25zdHJhdGVzIGhvdyB0aGUgb3ZlcmFsbCBwYXR0ZXJucyBmb3VuZCBpbiB0aGUgd2hvbGUgZGF0YXNldCBtaWdodCBtYXNrIGluZGl2aWR1YWwgdHJlbmRzIGluIHN1YnNldHMgYW5kIGhpZ2hsaWdodHMgdGhlIGltcG9ydGFuY2Ugb2YgYW5hbHl6aW5nIGRhdGEgb24gbXVsdGlwbGUgbGV2ZWxzLg0KDQotLS0NCg0KIyBDb25jbHVzaW9uDQoNCk91ciBob3BlIGlzIHRoYXQgd2l0aCB0aGlzIGV4ZXJjaXNlLCB5b3UgaGF2ZSBkZXZlbG9wZWQgYSBncmVhdGVyIHVuZGVyc3RhbmRpbmcgb2YgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIGhvdyBpdCBpcyBjb25zdHJ1Y3RlZCBpbiBSLiBXZSBoYXZlIHJldmlld2VkIGhvdyB0byBpbXBvcnQgcGFja2FnZXMgYW5kIGRhdGEgaW50byBSLCBwZXJmb3JtIHN0ZXBzIHRvIEVEQSwgYWRqdXN0IGFlc3RoZXRpYyBmZWF0dXJlcyBvZiBwbG90cywgd29yayB0aHJvdWdoIHN0YWdlcyBvZiBmaXR0aW5nIGEgbW9kZWwgYW5kIGNoZWNrIGFzc3VtcHRpb25zLCBhbmQgZXZlbiBicmllZmx5IGluY2x1ZGUgaG93IHRvIGJ1aWxkIGFuZCBpbnRlcnByZXQgYSB0d28tc2FtcGxlIHQtdGVzdC4gV2hpbGUgdGhlIGFuYWx5c2VzIG9mIHRoaXMgZGF0YSBsZWQgdG8gaW50ZXJlc3RpbmcgcmVzdWx0cywgd2hhdCBpcyBtb3JlIGltcG9ydGFudCAoYW5kIGV4Y2l0aW5nISkgaXMgdGhhdCB5b3UgYXJlIG5vdyBhYmxlIHRvIGFwcGx5IHdoYXQgeW91IGhhdmUgbGVhcm5lZCB0byBjb25zdHJ1Y3QgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBhbmQgdGVzdCBpdHMgc3VpdGFiaWxpdHkgZm9yIGFsbW9zdCBhbnkgZGF0YXNldC4gSW4gdGhlIHNjZW5hcmlvIHRoYXQgdXNpbmcgYSBsaW5lYXIgbW9kZWwgaXMgbm90IGFwcHJvcHJpYXRlLCB5b3UgY2FuIGxvb2sgdG8gbG9naXN0aWMgcmVncmVzc2lvbiwgdHJhbnNmb3JtYXRpb24sIG9yIHNlZ21lbnRhdGlvbi9jbHVzdGVyaW5nIG9mIGRhdGEuDQoNCklmIHlvdSdyZSBpbnRlcmVzdGVkIGluIGxlYXJuaW5nIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgbGluZWFyIHJlZ3Jlc3Npb24sIHNlZSBoZXJlOiA8aHR0cDovL3Itc3RhdGlzdGljcy5jby9MaW5lYXItUmVncmVzc2lvbi5odG1sPg0KDQoNCg==