Simple Linear Regression
While the linear regression is perhaps the most widely applied method in Data Science, it relies on a strict set of assumptions about the relationship between predictor and outcome variables. The most obvious (but crucial!) assumption is a linear relationship between the predictor and outcome. Following from this assumption is one key observation about any variables we want to include in our model which must be tested before building a model:
The expected value of the outcome variable is be a straight-line function of exclusively the predictor variable. The best test for this relationship is quite straightforward–– we can just visualize the relationship between the predictor and outcome variables as a scatterplot. A linear relationship will resemble a straight line with a slope not equal to zero, like the relationship between spending on TV ads and the overall sales volume of the related product found in our advertising dataset.
ggplot(advertising, aes(x = tv, y = sales)) +
geom_point() +
geom_smooth(method=lm, se=FALSE)

We can also quantitatively test for a linear relationship by computing the correlation coefficient. The correlation coefficient is always between positive one and negative one. A coefficient close to 0 (roughly between -0.20 and 0.20) suggests a weak linear relationship between two variables. A coefficient closer to positive or negative one suggests a stronger linear relationship. In R, we can compute the correlation coefficient using the cor.test() method as follows:
coefficient <- cor.test(advertising$tv, advertising$sales)
coefficient$estimate
cor
0.7812404
Use a combination of the base ggplot() function and geom_bar() to plot the distribution of the clicks variable, a measure of how many times a user clicked on an advertisement. Save the result to a variable called clicks_dist. Call clicks_dist.
# save viz to object
clicks_dist <- conversion %>% ggplot(aes(x = clicks)) + geom_bar()
clicks_dist

Take a closer look at the clicks_dist visualization. What is the approximate range of the clicks variable? What seems like the most common value (otherwise called the mode) of clicks? Set clicks_mode equal to approximate value of the clicks mode.
clicks_mode <- 1
Assign the result of calling cor.test(), with conversion$total_convert and conversion$clicks as input parameters, to a variable called correlation. Print out correlation$estimate. Does the coefficient value suggest that the variables have a linear relationship?
correlation <- cor.test(conversion$total_convert, conversion$clicks)
correlation$estimate
cor
0.6814551
Yes, seems to have positive linear correlation.
Assumptions of Linear Regression (Outliers)
Our next step is to check for outlier data points. Linear regression models also assume that there are no extreme values in the data set that are not representative of the actual relationship between predictor and outcome variables. A box-and-whisker plot is a common method used to quickly determine whether a data set contains any outliers, or data points that differ significantly from other observations in a dataset. An outlier may be caused by variability in measurement, or it might be a sign of an error in the collection of data.
Regardless, ggplot’s geom_boxplot() method allows for the easy creation of box-and-whisker plots. To plot the distribution of a single variable, like advertising$sales––the total number of sales for a product in a month–– we pass in the same variable as both x and y in our call to geom:
plot <- advertising %>%
ggplot(aes(sales, tv)) +
geom_boxplot()
plot

If we have negative sales values in the dataset, this is not what we would expect given our understanding of the data; how could an entire market have negative average sales over an entire year? This seems like an error stemming from the collection of this data into a spreadsheet format. In this case, we will filter out these negative datapoints from our dataset using the filter() method. We can pass a boolean argument into filter() to exclude values that resolve to false.
advertising <- advertising %>% filter(sales > 0)
Let’s check our variables for any outliers. Use a combination of the base ggplot() function and geom_boxplot() to plot a boxplot of the clicks variable; assign the result to a variable called clicks_bx_plot. Don’t forget to pass clicks in as the y variable, and afterwards call clicks_bx_plot in order to view the plot!
# create viz object
clicks_bx_plot <- ggplot(conversion, aes(x = clicks, y = clicks)) +
geom_boxplot()
# print out object
clicks_bx_plot

Do any data points look like outliers? Set threshold equal to the value of clicks above which all data points fall outside of the whiskers of our box plot.
# set threshold value
threshold <- 100
Use the filter() method to remove all outlying values from clicks. Save the resulting data frame to a variable called convert_clean.
# remove outliers
convert_clean <- conversion %>%
filter(clicks < threshold)
Let’s check our work! Create another box plot of clicks, but this time, use the convert_clean dataset and save the plot to clean_bx_plot. Call clean_bx_plot and note any differences from clicks_bx_plot. Are the outliers gone?
# creat second box plot
clean_bx_plot <- convert_clean %>%
ggplot(aes(clicks, clicks)) +
geom_boxplot()
clean_bx_plot

Building a Simple Linear Model
Simple linear regression is not a misnomer–– it is an uncomplicated technique for predicting a continuous outcome variable, Y, on the basis of just one predictor variable, X. As detailed in previous exercises, a number of assumptions are made so that we can model the relationship between X and Y as a linear function. Using our advertising dataset, we could model the relationship between the amount spent on podcast advertising in a month and the number of respective products eventually sold as follows:
$ Y= beta_0 + beta_1∗X + error$
Where…
Y: represents the dollar value of products sold
X: represents the amount spent on respective product podcast ads
Beta_0: is the intercept, or the number of products sold when no money has been spent on podcasts
Beta_1: is the coefficient, or the slope, of the line representing the relationship
Error: represents the random variation in the relationship between the two variables
To build this model in R, using the standard lm() package, we use the formula notation of Y ~ X:
model <- lm(sales ~ podcast, data = train)
But wait! Before building this model, we need to split our data into test and training sets For the development of this simple model, we’ll use a standard 60/40 split of our data; where 60% is used to train the model, and 40% is used to test the model’s accuracy and generalizability. We can randomly assign data points to test or training using base R’s sample() method and list indexing functionality
# specify 60/40 split
sample <- sample(c(TRUE, FALSE), nrow(advertising), replace = T, prob = c(0.6,0.4))
# subset data points into train and test sets
train <- advertising[sample, ]
test <- advertising[!sample, ]
First, let’s split our conversion_clean dataset into 60/40 train/test subsets
Create a variable named data_sample by assigning the result of calling sample(), with c(TRUE, FALSE), nrow(conversion_clean), and prob = c(0.6,0.4) as parameters.
- Using list indexing, assign all data points in sample to a variable called train
- Using list indexing, assign all data points in not in sample to a variable called test
# set sampling seed
set.seed(123)
# specify 60/40 split
data_sample <- sample(c(TRUE, FALSE), nrow(convert_clean), replace = T, prob = c(0.6, 0.4))
# subset data points into train and test sets
train <- convert_clean[data_sample, ]
test <- convert_clean[!data_sample, ]
Let’s fit a linear model of the relationship between the number of products sold and the number of clicks on the respective product advertisement; this means that conversion’s total_convert value, the total number of product purchases by a single user, will be our Y variable, or outcome variable. clicks the total number of times a user clicks on a version of an ad, will be our X variable, or predictor variable.
Assign the result of calling lm(), using ~ formula notation to set a linear relationship between total_conversions and clicks, to a variable called model. Don’t forget to set the data parameter equal to train!
# build model
model <- lm(total_convert ~ clicks, data = train)
model
Call:
lm(formula = total_convert ~ clicks, data = train)
Coefficients:
(Intercept) clicks
1.04046 0.05037
Quantifying Model Fit
Once we have an understanding of the kind of relationship our model describes, we want to understand the extent to which this modeled relationship actually fits the data. This is typically referred to as the goodness-of-fit. In simple linear models, we can measure this quantitatively by assessing two things:
Residual standard error (RSE)
R squared (R^2) The RSE is an estimate of the standard deviation of the error of the model (error in our mathematical definition of linear regression). Roughly speaking, it is the average amount that the response will deviate from the true regression line. We get the RSE at the bottom of summary(model), we can also get it directly with
sigma(model)
#output 3.2
An RSE value of 3.2 means the actual sales in each market will deviate from the true regression line by approximately 3,200 units, on average. Is this too large of a deviation?
Well, that’s subjective, but when compared to the average value of sales over all markets the percentage error is 22%:
sigma(model)/mean(train$sales)
# output
[1] 0.2207373
The RSE provides an absolute measure of lack of fit of our model to the data. But since it is measured in the units of Y, it is not always clear what constitutes a good RSE.
The R^2 statistic provides an alternative measure of fit. It represents the proportion of variance explained, so it always takes on a value between 0 and 1, and is independent of the scale of Y, our outcome variable. Similar to RSE, the R^2 can be found at the bottom of summary(model) but we can also extract it directly by calling summary(model)$r.squared. The result below suggests that podcast advertising budget can explain 64% of the variability in the total sales value.
summary(model)$r.squared
# output
[1] 0.6372581
# compute avg_rse
avg_rse = sigma(model)/mean(train$total_convert)
#uncomment f-string below
sprintf("The percentage error of the model is %s . Any prediction drawn from this model could be %s percent off from the actual observed value.", avg_rse, avg_rse)
[1] "The percentage error of the model is 0.803572334607046 . Any prediction drawn from this model could be 0.803572334607046 percent off from the actual observed value."
Model fit is often quantified in comparison to other models, then used to determine which variation of a modeled relationship best fits the data. Let’s build a second model so that we can contextualize our fit metrics.
Assign the result of building a simple linear model of impressions, the total number of times a user views a version of an advertisement, on total_convert to the variable model_2.
model_2 <- lm(total_convert ~ impressions, data = train)
Let’s use a combination of R’s variable selection syntax, the $ character, and summary() to investigate the percent of variability explained by both model and model2.
Call extract the r-squared measure from model, and save the result to a variable called r_sq.
# compute r-squared
r_sq = summary(model)$r.squared
Call extract the r-squared measure from model, and save the result to a variable called r_sq_2.
r_sq_2 = summary(model_2)$r.squared
Print out both r-square variables. Which model better explains a user’s likelihood of purchasing a product they have been shown an advertisement for?
# print out r-squared values
r_sq
[1] 0.3716766
r_sq_2
[1] 0.5018523
# uncomment f-string below
sprintf("Based on a pair of simple linear regression models, we have determined that %s percent of the variation in user purchase behavior can be explained by the number of times a user viewed on a relevant ad campaign; whereas only %s percent of this variation can be explained by the number of times a user clicked on a relevant ad.", r_sq_2, r_sq)
[1] "Based on a pair of simple linear regression models, we have determined that 0.501852326225873 percent of the variation in user purchase behavior can be explained by the number of times a user viewed on a relevant ad campaign; whereas only 0.371676623224315 percent of this variation can be explained by the number of times a user clicked on a relevant ad."
Checking Model Residuals
Great! We can build a model! But… how do we know if it’s any good? Also, if another data scientist builds a different model using a different independent variable, how can we tell which model is “best”? Even within Statistics, “best” can be a subjective qualifier. However, scientists who use regression models generally agree that the best model is the one that minimizes the distance between a data point and the estimation line drawn by a model. The vertical distance between a datapoint and the line estimated by a regression model is called a residual; residuals and their aggregations are the fundamental units of measures of regression model fit and accuracy.
Because residuals are based on cartesian distances, it often helps to visualize their values. For instance, consider the plot of a simple linear regression alongside its’ training data below. Note one point is 4 units above the regression estimate line; in this example, the residual for that point is 4. Meanwhile, another point is 2 units below the regression estimate line; the residual for that point is -2. A data point is best fit by the model which results in the smallest residual for that point.
When scientists make quantitative arguments for a best fit model, they rely on an aggregation, often the sum or average, of residual values across an entire dataset. While is it easy to be overwhelmed by the variety of measures used to argue that one model is better than the other, it is crucial to realize that all measures are grounded in the simple difference between regression estimate and observed data point. Let’s produce a visualization of our own model of clicks on total_convert to better understand our model residuals.
First, let’s pull out the points that make up the estimate line, and respective residual values, from our model. We can save them to columns back in our train dataset called estimate and residuals in our main train training dataset.
Call predict() on model and save the result to train$estimate.
train$estimate <- predict(model)
Call residuals() on model and save the result to train$residuals
train$residuals <- residuals(model)
lot the values of clicks and total_convert using a combination of ggplot() and geom_point(). Save the result to a variable called plot. Don’t forget to pass in clicks as as X variable, and total_convert as a Y variable. Make sure you’re using train as the dataset.
Call plot to view your visualization.
#create visualization
plot <- ggplot(train, aes(clicks, total_convert)) +
geom_point()
plot

Plot the observed data points in our train dataset by adding another call to geom_point(). Make sure to explicitly pass in estimate as a y value, and set the color parameter equal to “blue”.
plot <- ggplot(train, aes(clicks, total_convert)) +
geom_point() +
geom_point(aes(y = estimate), color = "blue")
plot

Let’s explicitly plot the vertical distance between estimate values and their respective observed data point. Add a call to geom_segment(), passing in xend = clicks and yend = estimate as arguments to aes(). Don’t forget to set color = “gray”
plot <- ggplot(train, aes(clicks, total_convert)) +
geom_point() +
geom_point(aes(y = estimate), color = "blue") +
geom_segment(aes(xend = clicks, yend = estimate), color = "gray")
plot

Finally, we should provide another way to observe the size of residuals. Update our first call to geom_point() by passing in size = abs(residuals) as an argument to aes(). As total_convert increases, how do the value of model residuals change?
plot <- ggplot(train, aes(clicks, total_convert)) +
geom_point(aes(size = abs(residuals))) +
geom_point(aes(y = estimate), color = "blue") +
geom_segment(aes(xend = clicks, yend = estimate), color = "gray")
plot

Visualizing Model Fit
In addition to the quantitative measures that characterize our model accuracy, it is alway a best practice to produce visual summaries to assess our model. First, we should always visualize our model within our data. For simple linear regression this is quite simple; we can use geom_point() to plot our observed values, and geom_smooth(method = “lm”) to plot our model. In addition, we can include a second call to geom_smooth(), with parameters (se = FALSE, color = “red”). This combination of function calls allows us to compare the linearity of our model, visualized below as the blue line with the 95% confidence interval covering the shaded region, in comparison to a non-linear LOESS smoother visualized in red.
ad_sample <- sample(c(TRUE, FALSE), nrow(advertising), replace = T, prob = c(0.6, 0.4))
# subset data points into train and test sets
advertising[ad_sample, ] %>%
ggplot(aes(podcast, sales)) +
geom_point() +
geom_smooth(method = "lm") +
geom_smooth(se = FALSE, color = "red")

LOESS smoothers plot a line based on the weighted value of data points; the line produced by a LOESS smoother is similar to taking a moving average of data points as our x-axis variable increases. The smoother should not be used to predict new values, as it relies heavily on our training data, but it is a helpful tool for visualizing where our linear model diverges from our training data.
Considering the LOESS smoother remains within the confidence interval of our model, we can assume the linear trend fits the essence of this relationship. However, we should note that as the podcast advertising budget gets closer to 0 there is a stronger reduction in sales beyond what the linear trend follows; this means that our model might be less accurate in instances where the podcast budget is very low.
We’ve plotted clicks against total converts. Let’s add a LOESS smoother. Add two calls of geom_smooth() to plot. The first should use the parameter method = “lm”. The second should use the parameters se = FALSE and color = “red”.
# build plot of clicks on total_convert below
plot <- ggplot(train, aes(clicks, total_convert)) +
geom_point() +
geom_smooth(method = "lm") +
geom_smooth(se = FALSE, color = "red")
plot

How closely does the relationship between clicks and conversion follow a linear trend? Set the variable linear_relationship equal to either “a”, “b”, “c”, or “d” depending on the statement that best characterizes the relations:
A. The relationship is less linear when clicks approaches large values.
B. There is a clear divergence from a linear relationship when clicks approaches zero or when clicks approaches infinity.
C. The relationship between clicks and total_conversion is perfectly linear.
D. There is no linear relationship between clicks and total_conversion
linear_relationship = "a"
Let’s extend our linearity analysis to our model2, which describes the relationship between impressions and total_conversion. Add the two calls to geom_smooth() to plot_2 to make a comparison to a LOESS model.
# build plot of impressions on total_convert below
plot_2 <- ggplot(train, aes(impressions, total_convert)) +
geom_point() +
geom_smooth(method = "lm") +
geom_smooth(se = FALSE, color = "red")
plot_2

How closely does the relationship between impressions and conversion follow a linear trend? Set the variable linear_relationship_2 equal to “a”, “b”, “c”, or “d” depending on the statement that best characterizes the relations:
A. The relationship between impressions and total_conversion is perfectly linear.
B. There is a clear divergence from a linear relationship when impressions approaches zero and when impressions is around 500,000.
C. The relationship is less linear when impressions approaches very large values and when impressions is around 500,000.
D. There is no linear relationship between impressions and total_conversion
linear_relationship_2 = "c"
Reading Model Results
Ready for the real fun? We’ve done our due diligence and confirmed that our data fulfills the assumptions of simple linear regression models; we’ve split our data into test and training subsets, and properly built a model using y ~ x + b notation; we’ve even taken the time to assess the fit of our model using both quantitative and qualitative approaches. Now we can finally analyze the results of our model and discover the relationship between user advertisement clicks and the purchase rate of related products!
We can view the results of a linear regression model in R by calling summary() on the model variable to which we saved the results of our call to lm(). The summary() function will print out a lot of information about our model–– but don’t be overwhelmed! There are four primary sections of quantitative results that are crucial to interpreting regression models:
### Call
This section simple displays the call to lm() which created these model results. It’s a helpful reminder of which version of a outcome-predictor pair is currently under analysis.
Residuals
As covered in our earlier exercises, a residual is the difference between the value of an outcome variable predicted by the model and the actual observed value of the variable. The summary() output displays a set of numbers that summarize the distribution of residuals in our model, including minimum/maximum residual values, the values first/third quantiles, and the median residual value for the model. We’ve already analyzed our residual values by creating a plot in an earlier exercise, but these summary values are a helpful reminder of the overall spread of our model errors.
Coefficients
Estimate
Coefficients are most important results in the interpretation of regression models. The number you see in the Estimate column, (a value of 0.048939 for clicks) is called a regression coefficient. Looking back to formal definition of a linear regression model:
$ Y= beta_0 + beta_1∗X + error$
The regression coefficient is represented by the beta_1 variable. This linear regression equation tells us that the regression coefficient represents the expected change in the dependent variable (in our case total_convert) for a one-unit increase in the independent variable (clicks). In other words, for every additional click on an advertisement, the expected sales of a related product are estimated to increase by 0.049 dollars. In addition to the size of the coefficient, it is also important to note the sign of the coefficient. If our clicks coefficient was negative, our model would be estimating that the sales of a product actually decreases every time an advertisement is clicked.
Std. Error
The column adjacent to Estimate is called Std. Error; the standard error of each coefficient is the estimate of the standard deviation of the coefficient. It is crucial to note that the standard error is not a quantity of interest by itself, but depends on the value of our regression coefficient
T-value and Pr(>|t|)
The t value and Pr(>|t|) inherently answer the same question–– given the value of our variable’s regression coefficient and its’ standard error, does the variable explain a significant part of the change in our outcome variable? However, the Pr(>|t|) column purposely provides a more concise response to this question, using the asterisk notation that corresponds with the Signif. codes legend at the bottom of the Coefficients results section. In R model output, one asterisk means “p < .05”. Two asterisks mean “p < .01”; and three asterisks mean “p < .001”. These values are referred to as p-values in scientific literature. How can we use p-values to answer our question around model significance?
Asterisks in a regression table indicate the level of the statistical significance of a regression coefficient. Our understanding of statistical significance is based off of the idea of a random sample. When interpreting these asterisk values, we ask ourselves: if there truly is no relationship between clicks on an advertisement and product sales, then what are chances that, across many user clicks on an ad, we see behavior that suggests that there is no relationship?
For our clicks variable, with *** in the Pr(>|t|) column, the answer is very unlikely. The value of ***, or p < .001, means that random sample resulting in the regression coefficient and standard error that we observed for clicks-given that there was truly no difference relationship between clicks and product purchase—would occur in less than one time in a random draw of 100, on average. Given that we would so rarely observe the situation that suggests that there is no relationship between our click and total_convert, we can say that there is a statistically significant relationship between the two variables. Generally speaking, scientists accept that a variable coefficient with p-value less than 0.05 is statistically significant.
Measures of Model Fit
At the bottom of the output of summary() are a series of labeled metrics, like Residual Standard Error (RSE) and Multiple R-squared, which quantify the fit of our model. Our previous exercises have covered how to interpret and plot many of these measures, but it’s a helpful reminder for them to be summarized along with other model output.
Assessing Simple Linear Regression
Let’s practice our model interpretation skills! We know that for continuous independent variables, like podcasts, the regression coefficient represents the difference in the predicted value of sales for each one-dollar increase in podcasts. Given the output of calling summary(model) below, we can correctly say that for every one dollar increase in podcast advertisement spending, total sales of the related project increases by 1.742 dollars.
summary(model)
#output
Call:
lm(formula = sales ~ radio, data = train)
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 9.57927 0.91176 10.506 < 2e-16 ***
podcast 1.74240 0.03255 5.353 3.71e-07 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
We could also extract the value of the podcast coefficient, the second coefficient returned by our model, using list indexing as follows:
podcast_coefficent <- model$coefficients[2]
It is important to note that the interpretation of the intercept coefficient is slightly different from that of variable coefficients. The intercept coefficient represents the value we would predict for our outcome variable, sales, given that podcast spending is equal to zero. It is crucial to remember that the intercept coefficient is only interpretable if we can reasonably expect a zero value for all independent variables in a model. Assuming, just as our simple linear model does, that spending on podcasts is the only variable that explains changes in sales, it does not make sense for any sales to occur without podcast spending. Therefore, for this model, our intercept coefficient is not interpretable.
However, in many cases, intercept coefficients are interpretable! The analysis of any model results requires a thorough understanding of our data, the system that produces this data, and a critical approach to interpretation of coefficient values.
Use summary() to print out the results of model and model2.
summary(model)
Call:
lm(formula = total_convert ~ clicks, data = train)
Residuals:
Min 1Q Median 3Q Max
-5.0196 -0.4219 -0.0908 -0.0405 16.1315
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.040462 0.074873 13.90 <2e-16 ***
clicks 0.050369 0.002628 19.17 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.513 on 621 degrees of freedom
Multiple R-squared: 0.3717, Adjusted R-squared: 0.3707
F-statistic: 367.3 on 1 and 621 DF, p-value: < 2.2e-16
summary(model_2)
Call:
lm(formula = total_convert ~ impressions, data = train)
Residuals:
Min 1Q Median 3Q Max
-4.0444 -0.4048 0.0140 0.0784 15.7470
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 9.154e-01 6.640e-02 13.79 <2e-16 ***
impressions 9.793e-06 3.915e-07 25.01 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.347 on 621 degrees of freedom
Multiple R-squared: 0.5019, Adjusted R-squared: 0.5011
F-statistic: 625.6 on 1 and 621 DF, p-value: < 2.2e-16
Save the results of calling model$coefficients[2] to a variable called clicks_coefficient;
clicks_coefficient <- model$coefficients[2]
sprintf("Based on a simple regression of `total_convert` by `clicks`, we estimate that for every additional click, the number of product purchases increases by %s.", clicks_coefficient)
[1] "Based on a simple regression of `total_convert` by `clicks`, we estimate that for every additional click, the number of product purchases increases by 0.0503687412817466."
How might we interpret the coefficient estimate for our model intercept? Set the variable intercept_coefficient equal to the lowercase letter (ex. “a”) associated with the statement that correctly interprets the estimate value:
A. The intercept coefficient is not significantly different from zero, therefore the model suggests that when clicks equal zero, the number of total_converts is likely to be around zero. This is reasonable because if a user has not clicked on an ad, we do not expect them to purchase the related product.
B. The intercept coefficient is negative and significant; this means that when clicks equal zero, we expect the number of total_converts to be less than zero. This makes sense because the user did not click on an ad, so we expect zero or less products to be purchased.
C. The intercept coefficient is greater than zero, so when clicks equals zero we expect total_converts to be somewhen between 0.90 and 1.25. Even though a user does not click on an ad, they have been exposed to the product brand and might purchase the product by later searching for it through a search engine.
D. The The intercept coefficient is close to zero; this means that when when clicks equal zero, the number of total_converts is likely to be around zero. This is reasonable because if a user has not clicked on an ad, we do not expect them to purchase the related product.
intercept_coefficient <- "c"
Making Predictions
Data Scientists are often interested in building models to make predictions on new data. While the add_predictions() function from the modelr package makes it easy to predict new values from a technical standpoint, it is far more difficult to develop and assess accurate predicted values.
The most common metric used to compute the accuracy of predicted values is mean squared error on test data. Similar to residual squared error (RSE) and R-squared, MSE measures the average squared difference between predicted and observed values. When we are working with just one model, it is helpful to compare the difference between MSE on our training dataset, and MSE on test data. We can calculate training MSE for model using a combination of add_predictions() and summarise(). add_predictions() creates and adds predicted values from a model to a column called pred. summarise() then allows us to calculate the mean of the squared difference between our observed values (sales) and predicted values (pred).
train %>%
add_predictions(model) %>%
summarise(MSE = mean((sales - pred)^2))
#output
MSE
31.60713
We can use the same combination of functions to calculate MSE for our test dataset, which results in a MSE of around 32.5. Testing MSE will almost always be higher than training MSE, as the model has been built off of training data; however, it is important to confirm that there is not a substantial difference between model training and test MSE. The value of using MSE to quantify prediction accuracy is more clear when comparing multiple models, as it allows us to determine which versions of a model best predicts an outcome variable. For instance, we could compute the MSE for a model of tv spending on sales.
model2 <- lm(sales ~ tv, data = train)
train %>%
add_predictions(model2) %>%
summarise(MSE = mean((sales - pred)^2))
#output
MSE
27.28415
Comparing the train MSE for our tv-based model, at 27.28, to our train MSE for a podcast-based model, at 31.60, it is clear that the predictions from the tv-based model are more accurate, as the model’s MSE is lower. If a data scientist was trying to predict the expected volume of sales for a future business quarter, it would be a better idea for them to base their estimations off of a tv-based model.
Use add_predictions() and summarize() to calculate the test mean squared error of model (MSE), and save the result to a variable called mse_clicks.
Make sure to use the test dataset.
library(modelr)
mse_clicks <- test %>%
add_predictions(model) %>%
summarise(MSE = mean((total_convert - pred)^2))
mse_clicks
Print out both mse_clicks and mse_impressions. Which one is smaller? What does this tell us about the accuracy of our models on the test data?
mse_impressions <- test %>%
add_predictions(model_2) %>%
summarise(MSE = mean((total_convert - pred)^2))
mse_impressions
Let’s plot the predicted test values of the model with the smallest MSE against the observed test data.
First, use a combination of add_predictions(), ggplot() and geom_point()to plot the observed values from the test data.
When creating the ggplot() add an aes() where the x and y values correspond to the variables used when making the correct model.
Save the visualization to a variable called plot, then print out the variable.
plot <- test %>%
add_predictions(model_2) %>%
ggplot(aes(impressions, total_convert)) +
geom_point() +
geom_point(aes(y = pred), color = "blue")
plot

Multiple Linear Regression
We’ve been able to really dig into the results of simple linear regression models and show how the results convey a substantial amount of information about the relationship between two variables. However, by this point you might be wondering–– what if I think variables other than podcast have contribute to the total sales of a product? You might remember that the primary assumption behind simple linear models is that the expected value of the outcome variable is a straight-line function of exclusively the predictor variable. This means that our simple linear models assume that all variation in the outcome variable is explained by the predictor variable. In the case of our sales dataset, we know this is almost certainly not true; oftentimes more money is spent on TV or newspaper ads than on podcasts, so this spending might have an even larger effect than podcast spend.
Thankfully, there are methods to include the effects of TV and newspaper in linear regression models. We can expand our model definition from a simple model of one predictor variable to a multiple model of, you guessed it, multiple predictor variables. The formal definition of multiple linear regression models is a direct extension of the formula for simple linear regression:
$ Y= beta_0 + beta_1∗X + beta_2∗X+error$
As in a simple linear model, Y represents the dollar value of products sold, X represents the amount spent on respective product podcast ads, and Beta_0 is the model intercept. Now, Beta_1, Beta_2, and Beta_3 represent each the coefficients of predictor variables. To build a similar model in R, using the standard lm() package, we still use the formula notation of Y ~ X:
model <- lm(sales ~ podcast + tv, data = train)
While building a multiple regression model is a straightforward extension of the code used to a build a simple model and the output of the model results below looks quite similar, a bit more effort goes into the interpretation of the results of this model. Remember that in a simple linear regression model, the regression coefficient represents the expected change in the dependent variable for a one-unit increase in the independent variable. In other words, the coefficient for podcast represents the expected increase in sales given a one dollar increase podcast advertisement spend. Because multiple linear regression includes more than one predictor variable, the coefficient estimates must be interpreted differently. In multiple linear regression, the regression coefficient represents the expected change in the dependent variable for a one-unit increase in the independent variable, holding all other variables in the model constant.
summary(model)
#output
Call:
lm(formula = sales ~ TV + podcast, data = train)
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.583386 1.024616 4.473 1.65e-05 ***
TV 3.006340 1.004924 7.380 1.62e-11 ***
podcast 1.049249 1.027665 5.395 3.10e-07 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
For example, a call to summary(model) shows that the coefficient for podcasts is equal to 1.04944. This means that, when one more dollar is spent on podcast advertising, about 1.049 more dollars of the related product is sold, given that there is no increase in the amount of money spent on tv advertisements. In this way, multiple linear regression models allow us to isolate the unique effect of one predictor variable on the outcome variable.
As this example shows, the selection of variables in a regression model can have wide-ranging impacts on the results and interpretation of our models! Let’s dive into one more exercise to practice building and interpreting multiple linear regression models.
Assessing Multiple Linear Regression
Time to pull it all together! The interpretation of coefficents in multiple linear regression is slightly different than that of coefficents in simple linear regression. Coefficent of independent continunous variables, like podcasts, represents the difference in the predicted value of sales for each one-dollar increase in podcasts, given that all other variables in the model, including tv, are held constant. Given the output of calling summary(model) below, we can correctly say that for every one dollar increase in podcast advertisement spending, while holding the amount spent on tv and newspaper constant, the total sales of the related product increases by 1.049 dollars.
summary(model)
#output
Call:
lm(formula = sales ~ TV + podcast + newspaper, data = train)
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.583386 1.024616 4.473 1.65e-05 ***
TV 3.006340 1.004924 7.380 1.62e-11 ***
podcast 1.049249 1.027665 5.395 3.10e-07 ***
newspaper 1.006340 1.002924 6.380 1.12e-11 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
In addition, the interpretation of boolean categorical variables differs slightly from that of continous variables. The coefficent value associated with a boolean categorical variable represents the effect of changing from one category to another. for instance, the coefficient value of 1.006 for newspaper tell us that running print advertisements results in a 1.006 dollar increase in sales, holding the values of TV and podcast constant.
Data scientists often build many variations of a model with different combinations of independent variables before ultimately commiting to the model that best fits test data. Let’s practice building, interpreting, and selecting the best fit multi-linear model for our convert_clean dataset!
Build a multiple linear regression model which regresses impressions, clicks, and gender on total_convert, using our train dataset. Save the result to a variable called model; then call summary(model) to view the model results.
model <- lm(formula = total_convert ~ impressions + clicks + gender, data = train)
summary(model)
Call:
lm(formula = total_convert ~ impressions + clicks + gender, data = train)
Residuals:
Min 1Q Median 3Q Max
-4.1869 -0.3854 -0.1383 0.1819 15.7961
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.148e+00 8.821e-02 13.016 < 2e-16 ***
impressions 1.717e-05 1.158e-06 14.825 < 2e-16 ***
clicks -4.672e-02 6.941e-03 -6.731 3.86e-11 ***
genderM -3.356e-01 1.090e-01 -3.079 0.00217 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.301 on 619 degrees of freedom
Multiple R-squared: 0.5369, Adjusted R-squared: 0.5346
F-statistic: 239.2 on 3 and 619 DF, p-value: < 2.2e-16
How might we interpret the coefficient estimate for gender? Set the variable gender_coefficient equal to the statement that most correctly interprets the estimate value — either “a”, “b”, or “c”:
A. The coefficient of the gender variable is not statistically significant, so we cannot come to any substantive conclusion from its’ value.
B. The coefficient of the gender variable is negative, which means that as total_convert, clicks, and impressions increases, men are less likely to purchase a advertised product.
C. The coefficient of the gender variable is negative. This means that a men are less likely than women––with the same value of clicks and impressions–– to purchase an advertised product.
gender_coefficient = "c"
Let’s build a second, simpler model so that we can confirm adding gender to our model increases its’ accuracy. Build a multiple linear regression model which regresses impressions, and clicks on total_convert. Save the result to a variable called model2
model_2 <- lm(formula = total_convert ~ impressions + clicks, data = train)
Compute the R-squared value for model and model2, and save the results to rsq_model and rsq_model2 respectively. Call both variables to view their values.
# compute r-squared below
rsq_model <- summary(model)$r.squared
rsq_model
[1] 0.536877
rsq_model2 <- summary(model_2)$r.squared
rsq_model2
[1] 0.5297831
Which model best fits our data? Set the variable best_fit equal to the larger r-squared value.
# define best_fit below
best_fit <- rsq_model
Set the variable gender_diff equal to the difference between rsq_model and rsq_model2.
# define gender_diff below
gender_diff <- rsq_model - rsq_model2
sprintf("Based on the results of a series of multiple linear regressions on total_convert, we estimate that user gender accounts for approximately %s percent of the variation in product purchase rate.", gender_diff)
[1] "Based on the results of a series of multiple linear regressions on total_convert, we estimate that user gender accounts for approximately 0.00709394202260971 percent of the variation in product purchase rate."
LS0tCnRpdGxlOiAiTGluZWFyIFJlZ3Jlc3Npb24iCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIAp0b2NfZGVwdGg6IDMKLS0tCiMgTGluZWFyIFJlZ3Jlc3Npb24KCkxpbmVhciBSZWdyZXNzaW9uIGlzIHRoZSB3b3JraG9yc2Ugb2YgYXBwbGllZCBEYXRhIFNjaWVuY2U7IGl0IGhhcyBsb25nIGJlZW4gdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBtZXRob2QgYnkgc2NpZW50aXN0cyBhbmQgY2FuIGJlIGFwcGxpZWQgdG8gYSB3aWRlIHZhcmlldHkgb2YgZGF0YXNldHMgYW5kIHF1ZXN0aW9ucy4gVW5saWtlIG1vcmUgcmVjZW50bHkgZGV2ZWxvcGVkIG1ldGhvZHMgaW4gTWFjaGluZSBMZWFybmluZywgTGluZWFyIFJlZ3Jlc3Npb24gbW9kZWxzIGNhbiBiZSB1c2VkIHRvIHByZWRpY3QgbmV3IGRhdGEgcG9pbnRzIGFuZCBoZWxwIHVzIHVuZGVyc3RhbmQgdGhlIHJlbGF0aXZlIGltcGFjdCBvbmUgdmFyaWFibGUgaGFzIG9uIGFub3RoZXIuIEZvciBleGFtcGxlLCBvbmUgd2VsbC1kZXNpZ25lZCByZWdyZXNzaW9uIG1vZGVsIGNhbiBhbnN3ZXI6CgotIEhvdywgYW5kIHRvIHdoYXQgZXh0ZW50LCBkb2VzIGFkdmVydGlzaW5nIGluIHByaW50IG1lZGlhIGVmZmVjdCB0aGUgdG90YWwgc2FsZXMgb2YgYSBwcm9kdWN0PwotIFdoYXQgYXJlIHRoZSBwcmVkaWN0ZWQgdG90YWwgc2FsZXMgZm9yIGEgcHJvZHVjdCwgZ2l2ZW4gdGhlIGFtb3VudCBzcGVudCBvbiBwcmludCBhZHZlcnRpc2VtZW50IHRoaXMgbW9udGg/Cgpob3cgdG8gaGFybmVzcyB0aGUgbWFsbGVhYmlsaXR5IGFuZCBleHBsYW5hdG9yeSBwb3dlciBvZiBMaW5lYXIgUmVncmVzc2lvbiBtb2RlbHMgYnkgZm9sbG93aW5nIHRoZSBmb3VyIHByaW1hcnkgc3RlcHMgb2Ygc3RhdGlzdGljYWwgbW9kZWwgYnVpbGRpbmc6IGNvbmZpcm1pbmcgZGF0YSBhc3N1bXB0aW9ucywgYnVpbGRpbmcgYSBtb2RlbCBvbiB0cmFpbmluZyBkYXRhLCBhc3Nlc3NpbmcgbW9kZWwgZml0LCBhbmQgYW5hbHl6aW5nIG1vZGVsIHJlc3VsdHMuClVzaW5nIHR3byByZWFsLWxpZmUgZGF0YXNldHMsIGNvbnZlcnNpb24gYW5kIGFkdmVydGlzaW5nLCB0aGlzIGxlc3NvbiB3aWxsIGFsc28gZm9jdXMgb24gdGhlIGFwcGxpY2F0aW9uIG9mIHJlZ3Jlc3Npb24gbW9kZWxpbmcgZm9yIHVzZSBpbiBtYXJrZXRpbmcgZGF0YSBzY2llbmNlLiAKCmNvbnZlcnNpb24gaXMgYW4gZXhhbXBsZSBvZiBkYXRhIHRoYXQgYSBzY2llbnRpc3Qgd291bGQgcmVjZWl2ZSBmcm9tIGEgbGFyZ2Ugc29jaWFsIG1lZGlhIHBsYXRmb3JtLCBsaWtlIEZhY2Vib29rIG9yIFR3aXR0ZXIsIGFuZCBjYW4gYmUgdXNlZCB0byBhbnN3ZXIgcXVlc3Rpb25zIGFib3V0IHRoZSBwZXJmb3JtYW5jZSBvZiBuZXdzZmVlZCBhZHZlcnRpc2VtZW50cy4gYWR2ZXJ0aXNpbmcgaXMgYW4gZXhhbXBsZSBvZiBkYXRhIHRoYXQgd291bGQgYmUgcmVjb3JkZWQgYnkgdGhlIG1hcmtldGluZyBkZXBhcnRtZW50IG9mIGEgY29tcGFueTsgYSBtYXJrZXRpbmcgZGF0YSBzY2llbnRpc3Qgd291bGQgb2Z0ZW4gYmUgZ2l2ZW4gdGhpcyBkYXRhIGFuZCBleHBlY3RlZCB0byBhbnN3ZXIgcXVlc3Rpb25zIGFyb3VuZCBvZiB0aGUgcmV0dXJuIG9uIGludmVzdG1lbnQgZm9yIHZhcmlvdXMgbWVkaWEgY2hhbm5lbHMsIGxpa2UgcG9kY2FzdCBvciB0diBhZHZlcnRpc2VtZW50cy4KYGBge3J9CiMgbG9hZCBsaWJyYXJpZXMgYW5kIGRhdGEKbGlicmFyeShkcGx5cikKbGlicmFyeShyZWFkcikKbGlicmFyeShnZ3Bsb3QyKQojIGxvYWQgZGF0YQpjb252ZXJzaW9uIDwtIHJlYWQuY3N2KCdjb252ZXJzaW9uLmNzdicsIGhlYWRlcj0gVCkKYWR2ZXJ0aXNpbmcgPC0gcmVhZC5jc3YoJ2FkdmVydGlzaW5nLmNzdicsIGhlYWRlcj0gVCkKYGBgCiMjIEV4cGxvcmluZyB0aGUgZGF0YXNldHMKCkEgZ29vZCBzdGF0aXN0aWNhbCB3b3JrZmxvdyBhbHdheXMgaW52b2x2ZXMgYW5kIHRob3JvdWdoIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGRhdGEgYXZhaWxhYmxlIHRvIG1vZGVsIGFuZCBhIHF1YWxpdGF0aXZlIGFuYWx5c2lzIG9mIHJlbGV2YW50IHZhcmlhYmxlcy4gVXNlIHN0cigpIHRvIHdyaXRlIG91dCB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0IGFuZCBsaXN0IG9mIHZhcmlhYmxlcyB0eXBlcy4gV2hpY2ggdmFyaWFibGVzIHNlZW0gbGlrZSBwb3NzaWJsZSBwcmVkaWN0b3JzIG9mIHB1cmNoYXNlLCBvciB0b3RhbF9jb252ZXJ0PwpgYGB7cn0Kc3RyKGNvbnZlcnNpb24pCnN0cihhZHZlcnRpc2luZykKYGBgCk1lYW4gb2Ygc2FsZXM6CmBgYHtyfQptZWFuKGFkdmVydGlzaW5nJHNhbGVzKQpgYGAKYGBge3J9CmdncGxvdChhZHZlcnRpc2luZywgYWVzKHggPSB0diwgeSA9IHNhbGVzKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKYGBge3J9CmdncGxvdChhZHZlcnRpc2luZywgYWVzKHggPSBwb2RjYXN0LCB5ID0gc2FsZXMpKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKIyBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24KCldoaWxlIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBpcyBwZXJoYXBzIHRoZSBtb3N0IHdpZGVseSBhcHBsaWVkIG1ldGhvZCBpbiBEYXRhIFNjaWVuY2UsIGl0IHJlbGllcyBvbiBhIHN0cmljdCBzZXQgb2YgYXNzdW1wdGlvbnMgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHByZWRpY3RvciBhbmQgb3V0Y29tZSB2YXJpYWJsZXMuIFRoZSBtb3N0IG9idmlvdXMgKGJ1dCBjcnVjaWFsISkgYXNzdW1wdGlvbiBpcyBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcHJlZGljdG9yIGFuZCBvdXRjb21lLiBGb2xsb3dpbmcgZnJvbSB0aGlzIGFzc3VtcHRpb24gaXMgb25lIGtleSBvYnNlcnZhdGlvbiBhYm91dCBhbnkgdmFyaWFibGVzIHdlIHdhbnQgdG8gaW5jbHVkZSBpbiBvdXIgbW9kZWwgd2hpY2ggbXVzdCBiZSB0ZXN0ZWQgYmVmb3JlIGJ1aWxkaW5nIGEgbW9kZWw6CgpUaGUgZXhwZWN0ZWQgdmFsdWUgb2YgdGhlIG91dGNvbWUgdmFyaWFibGUgaXMgYmUgYSBzdHJhaWdodC1saW5lIGZ1bmN0aW9uIG9mIGV4Y2x1c2l2ZWx5IHRoZSBwcmVkaWN0b3IgdmFyaWFibGUuIFRoZSBiZXN0IHRlc3QgZm9yIHRoaXMgcmVsYXRpb25zaGlwIGlzIHF1aXRlIHN0cmFpZ2h0Zm9yd2FyZOKAk+KAkyB3ZSBjYW4ganVzdCB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBwcmVkaWN0b3IgYW5kIG91dGNvbWUgdmFyaWFibGVzIGFzIGEgc2NhdHRlcnBsb3QuIEEgbGluZWFyIHJlbGF0aW9uc2hpcCB3aWxsIHJlc2VtYmxlIGEgc3RyYWlnaHQgbGluZSB3aXRoIGEgc2xvcGUgbm90IGVxdWFsIHRvIHplcm8sIGxpa2UgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNwZW5kaW5nIG9uIFRWIGFkcyBhbmQgdGhlIG92ZXJhbGwgc2FsZXMgdm9sdW1lIG9mIHRoZSByZWxhdGVkIHByb2R1Y3QgZm91bmQgaW4gb3VyIGFkdmVydGlzaW5nIGRhdGFzZXQuCmBgYHtyfQpnZ3Bsb3QoYWR2ZXJ0aXNpbmcsIGFlcyh4ID0gdHYsIHkgPSBzYWxlcykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgc2U9RkFMU0UpCmBgYApXZSBjYW4gYWxzbyBxdWFudGl0YXRpdmVseSB0ZXN0IGZvciBhIGxpbmVhciByZWxhdGlvbnNoaXAgYnkgY29tcHV0aW5nIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudC4gVGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGlzIGFsd2F5cyBiZXR3ZWVuIHBvc2l0aXZlIG9uZSBhbmQgbmVnYXRpdmUgb25lLiBBIGNvZWZmaWNpZW50IGNsb3NlIHRvIDAgKHJvdWdobHkgYmV0d2VlbiAtMC4yMCBhbmQgMC4yMCkgc3VnZ2VzdHMgYSB3ZWFrIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gdmFyaWFibGVzLiBBIGNvZWZmaWNpZW50IGNsb3NlciB0byBwb3NpdGl2ZSBvciBuZWdhdGl2ZSBvbmUgc3VnZ2VzdHMgYSBzdHJvbmdlciBsaW5lYXIgcmVsYXRpb25zaGlwLiBJbiBSLCB3ZSBjYW4gY29tcHV0ZSB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgdXNpbmcgdGhlIGNvci50ZXN0KCkgbWV0aG9kIGFzIGZvbGxvd3M6CmBgYHtyfQpjb2VmZmljaWVudCA8LSBjb3IudGVzdChhZHZlcnRpc2luZyR0diwgYWR2ZXJ0aXNpbmckc2FsZXMpCmNvZWZmaWNpZW50JGVzdGltYXRlCmBgYApVc2UgYSBjb21iaW5hdGlvbiBvZiB0aGUgYmFzZSBnZ3Bsb3QoKSBmdW5jdGlvbiBhbmQgZ2VvbV9iYXIoKSB0byBwbG90IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGNsaWNrcyB2YXJpYWJsZSwgYSBtZWFzdXJlIG9mIGhvdyBtYW55IHRpbWVzIGEgdXNlciBjbGlja2VkIG9uIGFuIGFkdmVydGlzZW1lbnQuIFNhdmUgdGhlIHJlc3VsdCB0byBhIHZhcmlhYmxlIGNhbGxlZCBjbGlja3NfZGlzdC4gQ2FsbCBjbGlja3NfZGlzdC4KYGBge3J9CiMgc2F2ZSB2aXogdG8gb2JqZWN0CmNsaWNrc19kaXN0IDwtIGNvbnZlcnNpb24gJT4lIGdncGxvdChhZXMoeCA9IGNsaWNrcykpICsgZ2VvbV9iYXIoKQpjbGlja3NfZGlzdApgYGAKVGFrZSBhIGNsb3NlciBsb29rIGF0IHRoZSBjbGlja3NfZGlzdCB2aXN1YWxpemF0aW9uLiBXaGF0IGlzIHRoZSBhcHByb3hpbWF0ZSByYW5nZSBvZiB0aGUgY2xpY2tzIHZhcmlhYmxlPyBXaGF0IHNlZW1zIGxpa2UgdGhlIG1vc3QgY29tbW9uIHZhbHVlIChvdGhlcndpc2UgY2FsbGVkIHRoZSBtb2RlKSBvZiBjbGlja3M/IFNldCBjbGlja3NfbW9kZSBlcXVhbCB0byBhcHByb3hpbWF0ZSB2YWx1ZSBvZiB0aGUgY2xpY2tzIG1vZGUuCmBgYHtyfQpjbGlja3NfbW9kZSA8LSAxCmBgYApBc3NpZ24gdGhlIHJlc3VsdCBvZiBjYWxsaW5nIGNvci50ZXN0KCksIHdpdGggY29udmVyc2lvblwkdG90YWxfY29udmVydCBhbmQgY29udmVyc2lvblwkY2xpY2tzIGFzIGlucHV0IHBhcmFtZXRlcnMsIHRvIGEgdmFyaWFibGUgY2FsbGVkIGNvcnJlbGF0aW9uLiBQcmludCBvdXQgY29ycmVsYXRpb24kZXN0aW1hdGUuIERvZXMgdGhlIGNvZWZmaWNpZW50IHZhbHVlIHN1Z2dlc3QgdGhhdCB0aGUgdmFyaWFibGVzIGhhdmUgYSBsaW5lYXIgcmVsYXRpb25zaGlwPwpgYGB7cn0KY29ycmVsYXRpb24gPC0gY29yLnRlc3QoY29udmVyc2lvbiR0b3RhbF9jb252ZXJ0LCBjb252ZXJzaW9uJGNsaWNrcykKY29ycmVsYXRpb24kZXN0aW1hdGUKYGBgClllcywgc2VlbXMgdG8gaGF2ZSBwb3NpdGl2ZSBsaW5lYXIgY29ycmVsYXRpb24uCgojIyBBc3N1bXB0aW9ucyBvZiBMaW5lYXIgUmVncmVzc2lvbiAoT3V0bGllcnMpCgpPdXIgbmV4dCBzdGVwIGlzIHRvIGNoZWNrIGZvciBvdXRsaWVyIGRhdGEgcG9pbnRzLiBMaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMgYWxzbyBhc3N1bWUgdGhhdCB0aGVyZSBhcmUgbm8gZXh0cmVtZSB2YWx1ZXMgaW4gdGhlIGRhdGEgc2V0IHRoYXQgYXJlIG5vdCByZXByZXNlbnRhdGl2ZSBvZiB0aGUgYWN0dWFsIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHByZWRpY3RvciBhbmQgb3V0Y29tZSB2YXJpYWJsZXMuIEEgYm94LWFuZC13aGlza2VyIHBsb3QgaXMgYSBjb21tb24gbWV0aG9kIHVzZWQgdG8gcXVpY2tseSBkZXRlcm1pbmUgd2hldGhlciBhIGRhdGEgc2V0IGNvbnRhaW5zIGFueSBvdXRsaWVycywgb3IgZGF0YSBwb2ludHMgdGhhdCBkaWZmZXIgc2lnbmlmaWNhbnRseSBmcm9tIG90aGVyIG9ic2VydmF0aW9ucyBpbiBhIGRhdGFzZXQuIEFuIG91dGxpZXIgbWF5IGJlIGNhdXNlZCBieSB2YXJpYWJpbGl0eSBpbiBtZWFzdXJlbWVudCwgb3IgaXQgbWlnaHQgYmUgYSBzaWduIG9mIGFuIGVycm9yIGluIHRoZSBjb2xsZWN0aW9uIG9mIGRhdGEuCgpSZWdhcmRsZXNzLCBnZ3Bsb3TigJlzIGdlb21fYm94cGxvdCgpIG1ldGhvZCBhbGxvd3MgZm9yIHRoZSBlYXN5IGNyZWF0aW9uIG9mIGJveC1hbmQtd2hpc2tlciBwbG90cy4gVG8gcGxvdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgc2luZ2xlIHZhcmlhYmxlLCBsaWtlIGFkdmVydGlzaW5nJHNhbGVz4oCT4oCTdGhlIHRvdGFsIG51bWJlciBvZiBzYWxlcyBmb3IgYSBwcm9kdWN0IGluIGEgbW9udGjigJPigJMgd2UgcGFzcyBpbiB0aGUgc2FtZSB2YXJpYWJsZSBhcyBib3RoIHggYW5kIHkgaW4gb3VyIGNhbGwgdG8gZ2VvbToKYGBge3J9CnBsb3QgPC0gYWR2ZXJ0aXNpbmcgJT4lCiAgZ2dwbG90KGFlcyhzYWxlcywgdHYpKSArCiAgZ2VvbV9ib3hwbG90KCkKcGxvdApgYGAKCklmIHdlIGhhdmUgbmVnYXRpdmUgc2FsZXMgdmFsdWVzIGluIHRoZSBkYXRhc2V0LCB0aGlzIGlzIG5vdCB3aGF0IHdlIHdvdWxkIGV4cGVjdCBnaXZlbiBvdXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGF0YTsgaG93IGNvdWxkIGFuIGVudGlyZSBtYXJrZXQgaGF2ZSBuZWdhdGl2ZSBhdmVyYWdlIHNhbGVzIG92ZXIgYW4gZW50aXJlIHllYXI/IFRoaXMgc2VlbXMgbGlrZSBhbiBlcnJvciBzdGVtbWluZyBmcm9tIHRoZSBjb2xsZWN0aW9uIG9mIHRoaXMgZGF0YSBpbnRvIGEgc3ByZWFkc2hlZXQgZm9ybWF0LiBJbiB0aGlzIGNhc2UsIHdlIHdpbGwgZmlsdGVyIG91dCB0aGVzZSBuZWdhdGl2ZSBkYXRhcG9pbnRzIGZyb20gb3VyIGRhdGFzZXQgdXNpbmcgdGhlIGZpbHRlcigpIG1ldGhvZC4gV2UgY2FuIHBhc3MgYSBib29sZWFuIGFyZ3VtZW50IGludG8gZmlsdGVyKCkgdG8gZXhjbHVkZSB2YWx1ZXMgdGhhdCByZXNvbHZlIHRvIGZhbHNlLgpgYGB7cn0KYWR2ZXJ0aXNpbmcgPC0gYWR2ZXJ0aXNpbmcgJT4lIGZpbHRlcihzYWxlcyA+IDApCmBgYApMZXTigJlzIGNoZWNrIG91ciB2YXJpYWJsZXMgZm9yIGFueSBvdXRsaWVycy4gVXNlIGEgY29tYmluYXRpb24gb2YgdGhlIGJhc2UgZ2dwbG90KCkgZnVuY3Rpb24gYW5kIGdlb21fYm94cGxvdCgpIHRvIHBsb3QgYSBib3hwbG90IG9mIHRoZSBjbGlja3MgdmFyaWFibGU7IGFzc2lnbiB0aGUgcmVzdWx0IHRvIGEgdmFyaWFibGUgY2FsbGVkIGNsaWNrc19ieF9wbG90LiBEb27igJl0IGZvcmdldCB0byBwYXNzIGNsaWNrcyBpbiBhcyB0aGUgeSB2YXJpYWJsZSwgYW5kIGFmdGVyd2FyZHMgY2FsbCBjbGlja3NfYnhfcGxvdCBpbiBvcmRlciB0byB2aWV3IHRoZSBwbG90IQpgYGB7ciBjcmVhdGVfdml6fQojIGNyZWF0ZSB2aXogb2JqZWN0CmNsaWNrc19ieF9wbG90IDwtIGdncGxvdChjb252ZXJzaW9uLCBhZXMoeCA9IGNsaWNrcywgeSA9IGNsaWNrcykpICsKZ2VvbV9ib3hwbG90KCkKCiMgcHJpbnQgb3V0IG9iamVjdCAKY2xpY2tzX2J4X3Bsb3QKYGBgCkRvIGFueSBkYXRhIHBvaW50cyBsb29rIGxpa2Ugb3V0bGllcnM/IFNldCB0aHJlc2hvbGQgZXF1YWwgdG8gdGhlIHZhbHVlIG9mIGNsaWNrcyBhYm92ZSB3aGljaCBhbGwgZGF0YSBwb2ludHMgZmFsbCBvdXRzaWRlIG9mIHRoZSB3aGlza2VycyBvZiBvdXIgYm94IHBsb3QuCmBgYHtyfQojIHNldCB0aHJlc2hvbGQgdmFsdWUKdGhyZXNob2xkIDwtIDEwMApgYGAKVXNlIHRoZSBmaWx0ZXIoKSBtZXRob2QgdG8gcmVtb3ZlIGFsbCBvdXRseWluZyB2YWx1ZXMgZnJvbSBjbGlja3MuIFNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZyYW1lIHRvIGEgdmFyaWFibGUgY2FsbGVkIGNvbnZlcnRfY2xlYW4uCmBgYHtyfQojIHJlbW92ZSBvdXRsaWVycwpjb252ZXJ0X2NsZWFuIDwtIGNvbnZlcnNpb24gJT4lCiAgZmlsdGVyKGNsaWNrcyA8IHRocmVzaG9sZCkKYGBgCkxldOKAmXMgY2hlY2sgb3VyIHdvcmshIENyZWF0ZSBhbm90aGVyIGJveCBwbG90IG9mIGNsaWNrcywgYnV0IHRoaXMgdGltZSwgdXNlIHRoZSBjb252ZXJ0X2NsZWFuIGRhdGFzZXQgYW5kIHNhdmUgdGhlIHBsb3QgdG8gY2xlYW5fYnhfcGxvdC4gQ2FsbCBjbGVhbl9ieF9wbG90IGFuZCBub3RlIGFueSBkaWZmZXJlbmNlcyBmcm9tIGNsaWNrc19ieF9wbG90LiBBcmUgdGhlIG91dGxpZXJzIGdvbmU/CmBgYHtyfQojIGNyZWF0IHNlY29uZCBib3ggcGxvdCAKY2xlYW5fYnhfcGxvdCA8LSBjb252ZXJ0X2NsZWFuICU+JQogIGdncGxvdChhZXMoY2xpY2tzLCBjbGlja3MpKSArCiAgZ2VvbV9ib3hwbG90KCkKCmNsZWFuX2J4X3Bsb3QKYGBgCiMjIEJ1aWxkaW5nIGEgU2ltcGxlIExpbmVhciBNb2RlbAoKU2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGlzIG5vdCBhIG1pc25vbWVy4oCT4oCTIGl0IGlzIGFuIHVuY29tcGxpY2F0ZWQgdGVjaG5pcXVlIGZvciBwcmVkaWN0aW5nIGEgY29udGludW91cyBvdXRjb21lIHZhcmlhYmxlLCBZLCBvbiB0aGUgYmFzaXMgb2YganVzdCBvbmUgcHJlZGljdG9yIHZhcmlhYmxlLCBYLiBBcyBkZXRhaWxlZCBpbiBwcmV2aW91cyBleGVyY2lzZXMsIGEgbnVtYmVyIG9mIGFzc3VtcHRpb25zIGFyZSBtYWRlIHNvIHRoYXQgd2UgY2FuIG1vZGVsIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBYIGFuZCBZIGFzIGEgbGluZWFyIGZ1bmN0aW9uLiBVc2luZyBvdXIgYWR2ZXJ0aXNpbmcgZGF0YXNldCwgd2UgY291bGQgbW9kZWwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBhbW91bnQgc3BlbnQgb24gcG9kY2FzdCBhZHZlcnRpc2luZyBpbiBhIG1vbnRoIGFuZCB0aGUgbnVtYmVyIG9mIHJlc3BlY3RpdmUgcHJvZHVjdHMgZXZlbnR1YWxseSBzb2xkIGFzIGZvbGxvd3M6CgokIFk9IGJldGFfMCArIGJldGFfMeKIl1ggKyBlcnJvciQKCldoZXJl4oCmCgpZOiByZXByZXNlbnRzIHRoZSBkb2xsYXIgdmFsdWUgb2YgcHJvZHVjdHMgc29sZAoKWDogcmVwcmVzZW50cyB0aGUgYW1vdW50IHNwZW50IG9uIHJlc3BlY3RpdmUgcHJvZHVjdCBwb2RjYXN0IGFkcwoKQmV0YV8wOiBpcyB0aGUgaW50ZXJjZXB0LCBvciB0aGUgbnVtYmVyIG9mIHByb2R1Y3RzIHNvbGQgd2hlbiBubyBtb25leSBoYXMgYmVlbiBzcGVudCBvbiBwb2RjYXN0cwoKQmV0YV8xOiBpcyB0aGUgY29lZmZpY2llbnQsIG9yIHRoZSBzbG9wZSwgb2YgdGhlIGxpbmUgcmVwcmVzZW50aW5nIHRoZSByZWxhdGlvbnNoaXAKCkVycm9yOiByZXByZXNlbnRzIHRoZSByYW5kb20gdmFyaWF0aW9uIGluIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcwoKVG8gYnVpbGQgdGhpcyBtb2RlbCBpbiBSLCB1c2luZyB0aGUgc3RhbmRhcmQgbG0oKSBwYWNrYWdlLCB3ZSB1c2UgdGhlIGZvcm11bGEgbm90YXRpb24gb2YgWSB+IFg6CgogICAgbW9kZWwgPC0gbG0oc2FsZXMgfiBwb2RjYXN0LCBkYXRhID0gdHJhaW4pCgpCdXQgd2FpdCEgQmVmb3JlIGJ1aWxkaW5nIHRoaXMgbW9kZWwsIHdlIG5lZWQgdG8gc3BsaXQgb3VyIGRhdGEgaW50byB0ZXN0IGFuZCB0cmFpbmluZyBzZXRzIEZvciB0aGUgZGV2ZWxvcG1lbnQgb2YgdGhpcyBzaW1wbGUgbW9kZWwsIHdl4oCZbGwgdXNlIGEgc3RhbmRhcmQgNjAvNDAgc3BsaXQgb2Ygb3VyIGRhdGE7IHdoZXJlIDYwJSBpcyB1c2VkIHRvIHRyYWluIHRoZSBtb2RlbCwgYW5kIDQwJSBpcyB1c2VkIHRvIHRlc3QgdGhlIG1vZGVs4oCZcyBhY2N1cmFjeSBhbmQgZ2VuZXJhbGl6YWJpbGl0eS4gV2UgY2FuIHJhbmRvbWx5IGFzc2lnbiBkYXRhIHBvaW50cyB0byB0ZXN0IG9yIHRyYWluaW5nIHVzaW5nIGJhc2UgUuKAmXMgc2FtcGxlKCkgbWV0aG9kIGFuZCBsaXN0IGluZGV4aW5nIGZ1bmN0aW9uYWxpdHkKCiAgICAjIHNwZWNpZnkgNjAvNDAgc3BsaXQKICAgIHNhbXBsZSA8LSBzYW1wbGUoYyhUUlVFLCBGQUxTRSksIG5yb3coYWR2ZXJ0aXNpbmcpLCByZXBsYWNlID0gVCwgcHJvYiA9ICAgICBjKDAuNiwwLjQpKQogICAgIyBzdWJzZXQgZGF0YSBwb2ludHMgaW50byB0cmFpbiBhbmQgdGVzdCBzZXRzCiAgICB0cmFpbiA8LSBhZHZlcnRpc2luZ1tzYW1wbGUsIF0KICAgIHRlc3QgPC0gYWR2ZXJ0aXNpbmdbIXNhbXBsZSwgXQoKRmlyc3QsIGxldOKAmXMgc3BsaXQgb3VyIGNvbnZlcnNpb25fY2xlYW4gZGF0YXNldCBpbnRvIDYwLzQwIHRyYWluL3Rlc3Qgc3Vic2V0cwoKQ3JlYXRlIGEgdmFyaWFibGUgbmFtZWQgZGF0YV9zYW1wbGUgYnkgYXNzaWduaW5nIHRoZSByZXN1bHQgb2YgY2FsbGluZyBzYW1wbGUoKSwgd2l0aCBjKFRSVUUsIEZBTFNFKSwgbnJvdyhjb252ZXJzaW9uX2NsZWFuKSwgYW5kIHByb2IgPSBjKDAuNiwwLjQpIGFzIHBhcmFtZXRlcnMuCgotIFVzaW5nIGxpc3QgaW5kZXhpbmcsIGFzc2lnbiBhbGwgZGF0YSBwb2ludHMgaW4gc2FtcGxlIHRvIGEgdmFyaWFibGUgY2FsbGVkIHRyYWluCi0gVXNpbmcgbGlzdCBpbmRleGluZywgYXNzaWduIGFsbCBkYXRhIHBvaW50cyBpbiBub3QgaW4gc2FtcGxlIHRvIGEgdmFyaWFibGUgY2FsbGVkIHRlc3QKCmBgYHtyIHNwbGl0X3RyYWluX3Rlc3R9CiMgc2V0IHNhbXBsaW5nIHNlZWQKc2V0LnNlZWQoMTIzKQojIHNwZWNpZnkgNjAvNDAgc3BsaXQKZGF0YV9zYW1wbGUgPC0gc2FtcGxlKGMoVFJVRSwgRkFMU0UpLCBucm93KGNvbnZlcnRfY2xlYW4pLCByZXBsYWNlID0gVCwgcHJvYiA9IGMoMC42LCAwLjQpKQoKIyBzdWJzZXQgZGF0YSBwb2ludHMgaW50byB0cmFpbiBhbmQgdGVzdCBzZXRzCnRyYWluIDwtIGNvbnZlcnRfY2xlYW5bZGF0YV9zYW1wbGUsIF0KdGVzdCA8LSBjb252ZXJ0X2NsZWFuWyFkYXRhX3NhbXBsZSwgXQpgYGAKTGV04oCZcyBmaXQgYSBsaW5lYXIgbW9kZWwgb2YgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBudW1iZXIgb2YgcHJvZHVjdHMgc29sZCBhbmQgdGhlIG51bWJlciBvZiBjbGlja3Mgb24gdGhlIHJlc3BlY3RpdmUgcHJvZHVjdCBhZHZlcnRpc2VtZW50OyB0aGlzIG1lYW5zIHRoYXQgY29udmVyc2lvbuKAmXMgdG90YWxfY29udmVydCB2YWx1ZSwgdGhlIHRvdGFsIG51bWJlciBvZiBwcm9kdWN0IHB1cmNoYXNlcyBieSBhIHNpbmdsZSB1c2VyLCB3aWxsIGJlIG91ciBZIHZhcmlhYmxlLCBvciBvdXRjb21lIHZhcmlhYmxlLiBjbGlja3MgdGhlIHRvdGFsIG51bWJlciBvZiB0aW1lcyBhIHVzZXIgY2xpY2tzIG9uIGEgdmVyc2lvbiBvZiBhbiBhZCwgd2lsbCBiZSBvdXIgWCB2YXJpYWJsZSwgb3IgcHJlZGljdG9yIHZhcmlhYmxlLgoKQXNzaWduIHRoZSByZXN1bHQgb2YgY2FsbGluZyBsbSgpLCB1c2luZyB+IGZvcm11bGEgbm90YXRpb24gdG8gc2V0IGEgbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRvdGFsX2NvbnZlcnNpb25zIGFuZCBjbGlja3MsIHRvIGEgdmFyaWFibGUgY2FsbGVkIG1vZGVsLiBEb27igJl0IGZvcmdldCB0byBzZXQgdGhlIGRhdGEgcGFyYW1ldGVyIGVxdWFsIHRvIHRyYWluIQpgYGB7ciBidWlsZF9tb2RlbH0KIyBidWlsZCBtb2RlbAptb2RlbCA8LSBsbSh0b3RhbF9jb252ZXJ0IH4gY2xpY2tzLCBkYXRhID0gdHJhaW4pCm1vZGVsCmBgYAojIyBRdWFudGlmeWluZyBNb2RlbCBGaXQKCk9uY2Ugd2UgaGF2ZSBhbiB1bmRlcnN0YW5kaW5nIG9mIHRoZSBraW5kIG9mIHJlbGF0aW9uc2hpcCBvdXIgbW9kZWwgZGVzY3JpYmVzLCB3ZSB3YW50IHRvIHVuZGVyc3RhbmQgdGhlIGV4dGVudCB0byB3aGljaCB0aGlzIG1vZGVsZWQgcmVsYXRpb25zaGlwIGFjdHVhbGx5IGZpdHMgdGhlIGRhdGEuIFRoaXMgaXMgdHlwaWNhbGx5IHJlZmVycmVkIHRvIGFzIHRoZSBnb29kbmVzcy1vZi1maXQuIEluIHNpbXBsZSBsaW5lYXIgbW9kZWxzLCB3ZSBjYW4gbWVhc3VyZSB0aGlzIHF1YW50aXRhdGl2ZWx5IGJ5IGFzc2Vzc2luZyB0d28gdGhpbmdzOgoKMS4gUmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgKFJTRSkKMi4gUiBzcXVhcmVkIChSXjIpClRoZSBSU0UgaXMgYW4gZXN0aW1hdGUgb2YgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgZXJyb3Igb2YgdGhlIG1vZGVsIChlcnJvciBpbiBvdXIgbWF0aGVtYXRpY2FsIGRlZmluaXRpb24gb2YgbGluZWFyIHJlZ3Jlc3Npb24pLiBSb3VnaGx5IHNwZWFraW5nLCBpdCBpcyB0aGUgYXZlcmFnZSBhbW91bnQgdGhhdCB0aGUgcmVzcG9uc2Ugd2lsbCBkZXZpYXRlIGZyb20gdGhlIHRydWUgcmVncmVzc2lvbiBsaW5lLiBXZSBnZXQgdGhlIFJTRSBhdCB0aGUgYm90dG9tIG9mIHN1bW1hcnkobW9kZWwpLCB3ZSBjYW4gYWxzbyBnZXQgaXQgZGlyZWN0bHkgd2l0aAoKICAgIHNpZ21hKG1vZGVsKQoKICAgICNvdXRwdXQKICAgIDMuMgogICAgCkFuIFJTRSB2YWx1ZSBvZiAzLjIgbWVhbnMgdGhlIGFjdHVhbCBzYWxlcyBpbiBlYWNoIG1hcmtldCB3aWxsIGRldmlhdGUgZnJvbSB0aGUgdHJ1ZSByZWdyZXNzaW9uIGxpbmUgYnkgYXBwcm94aW1hdGVseSAzLDIwMCB1bml0cywgb24gYXZlcmFnZS4gSXMgdGhpcyB0b28gbGFyZ2Ugb2YgYSBkZXZpYXRpb24/CgpXZWxsLCB0aGF04oCZcyBzdWJqZWN0aXZlLCBidXQgd2hlbiBjb21wYXJlZCB0byB0aGUgYXZlcmFnZSB2YWx1ZSBvZiBzYWxlcyBvdmVyIGFsbCBtYXJrZXRzIHRoZSBwZXJjZW50YWdlIGVycm9yIGlzIDIyJToKCiAgICBzaWdtYShtb2RlbCkvbWVhbih0cmFpbiRzYWxlcykKICAgIAogICAgIyBvdXRwdXQKICAgIFsxXSAwLjIyMDczNzMKClRoZSBSU0UgcHJvdmlkZXMgYW4gYWJzb2x1dGUgbWVhc3VyZSBvZiBsYWNrIG9mIGZpdCBvZiBvdXIgbW9kZWwgdG8gdGhlIGRhdGEuIEJ1dCBzaW5jZSBpdCBpcyBtZWFzdXJlZCBpbiB0aGUgdW5pdHMgb2YgWSwgaXQgaXMgbm90IGFsd2F5cyBjbGVhciB3aGF0IGNvbnN0aXR1dGVzIGEgZ29vZCBSU0UuCgpUaGUgUl4yIHN0YXRpc3RpYyBwcm92aWRlcyBhbiBhbHRlcm5hdGl2ZSBtZWFzdXJlIG9mIGZpdC4gSXQgcmVwcmVzZW50cyB0aGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQsIHNvIGl0IGFsd2F5cyB0YWtlcyBvbiBhIHZhbHVlIGJldHdlZW4gMCBhbmQgMSwgYW5kIGlzIGluZGVwZW5kZW50IG9mIHRoZSBzY2FsZSBvZiBZLCBvdXIgb3V0Y29tZSB2YXJpYWJsZS4gU2ltaWxhciB0byBSU0UsIHRoZSBSXjIgY2FuIGJlIGZvdW5kIGF0IHRoZSBib3R0b20gb2Ygc3VtbWFyeShtb2RlbCkgYnV0IHdlIGNhbiBhbHNvIGV4dHJhY3QgaXQgZGlyZWN0bHkgYnkgY2FsbGluZyBzdW1tYXJ5KG1vZGVsKSRyLnNxdWFyZWQuIFRoZSByZXN1bHQgYmVsb3cgc3VnZ2VzdHMgdGhhdCBwb2RjYXN0IGFkdmVydGlzaW5nIGJ1ZGdldCBjYW4gZXhwbGFpbiA2NCUgb2YgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSB0b3RhbCBzYWxlcyB2YWx1ZS4KCiAgICBzdW1tYXJ5KG1vZGVsKSRyLnNxdWFyZWQKICAgIAogICAgIyBvdXRwdXQKICAgIFsxXSAwLjYzNzI1ODEKICAgIApgYGB7cn0KIyBjb21wdXRlIGF2Z19yc2UKYXZnX3JzZSA9IHNpZ21hKG1vZGVsKS9tZWFuKHRyYWluJHRvdGFsX2NvbnZlcnQpCiN1bmNvbW1lbnQgZi1zdHJpbmcgYmVsb3cKc3ByaW50ZigiVGhlIHBlcmNlbnRhZ2UgZXJyb3Igb2YgdGhlIG1vZGVsIGlzICVzIC4gQW55IHByZWRpY3Rpb24gZHJhd24gZnJvbSB0aGlzIG1vZGVsIGNvdWxkIGJlICVzIHBlcmNlbnQgb2ZmIGZyb20gdGhlIGFjdHVhbCBvYnNlcnZlZCB2YWx1ZS4iLCBhdmdfcnNlLCBhdmdfcnNlKQpgYGAKTW9kZWwgZml0IGlzIG9mdGVuIHF1YW50aWZpZWQgaW4gY29tcGFyaXNvbiB0byBvdGhlciBtb2RlbHMsIHRoZW4gdXNlZCB0byBkZXRlcm1pbmUgd2hpY2ggdmFyaWF0aW9uIG9mIGEgbW9kZWxlZCByZWxhdGlvbnNoaXAgYmVzdCBmaXRzIHRoZSBkYXRhLiBMZXTigJlzIGJ1aWxkIGEgc2Vjb25kIG1vZGVsIHNvIHRoYXQgd2UgY2FuIGNvbnRleHR1YWxpemUgb3VyIGZpdCBtZXRyaWNzLgoKQXNzaWduIHRoZSByZXN1bHQgb2YgYnVpbGRpbmcgYSBzaW1wbGUgbGluZWFyIG1vZGVsIG9mIGltcHJlc3Npb25zLCB0aGUgdG90YWwgbnVtYmVyIG9mIHRpbWVzIGEgdXNlciB2aWV3cyBhIHZlcnNpb24gb2YgYW4gYWR2ZXJ0aXNlbWVudCwgb24gdG90YWxfY29udmVydCB0byB0aGUgdmFyaWFibGUgbW9kZWxfMi4KYGBge3IgYnVpbGRfbW9kZWwyfQptb2RlbF8yIDwtIGxtKHRvdGFsX2NvbnZlcnQgfiBpbXByZXNzaW9ucywgZGF0YSA9IHRyYWluKQpgYGAKTGV04oCZcyB1c2UgYSBjb21iaW5hdGlvbiBvZiBS4oCZcyB2YXJpYWJsZSBzZWxlY3Rpb24gc3ludGF4LCB0aGUgJCBjaGFyYWN0ZXIsIGFuZCBzdW1tYXJ5KCkgdG8gaW52ZXN0aWdhdGUgdGhlIHBlcmNlbnQgb2YgdmFyaWFiaWxpdHkgZXhwbGFpbmVkIGJ5IGJvdGggbW9kZWwgYW5kIG1vZGVsMi4KCkNhbGwgZXh0cmFjdCB0aGUgci1zcXVhcmVkIG1lYXN1cmUgZnJvbSBtb2RlbCwgYW5kIHNhdmUgdGhlIHJlc3VsdCB0byBhIHZhcmlhYmxlIGNhbGxlZCByX3NxLgpgYGB7cn0KIyBjb21wdXRlIHItc3F1YXJlZApyX3NxID0gc3VtbWFyeShtb2RlbCkkci5zcXVhcmVkCmBgYAoKQ2FsbCBleHRyYWN0IHRoZSByLXNxdWFyZWQgbWVhc3VyZSBmcm9tIG1vZGVsLCBhbmQgc2F2ZSB0aGUgcmVzdWx0IHRvIGEgdmFyaWFibGUgY2FsbGVkIHJfc3FfMi4KYGBge3J9CnJfc3FfMiA9IHN1bW1hcnkobW9kZWxfMikkci5zcXVhcmVkCmBgYAoKUHJpbnQgb3V0IGJvdGggci1zcXVhcmUgdmFyaWFibGVzLiBXaGljaCBtb2RlbCBiZXR0ZXIgZXhwbGFpbnMgYSB1c2Vy4oCZcyBsaWtlbGlob29kIG9mIHB1cmNoYXNpbmcgYSBwcm9kdWN0IHRoZXkgaGF2ZSBiZWVuIHNob3duIGFuIGFkdmVydGlzZW1lbnQgZm9yPwpgYGB7cn0KIyBwcmludCBvdXQgci1zcXVhcmVkIHZhbHVlcwpyX3NxCnJfc3FfMgpgYGAKYGBge3J9CiMgdW5jb21tZW50IGYtc3RyaW5nIGJlbG93CiBzcHJpbnRmKCJCYXNlZCBvbiBhIHBhaXIgb2Ygc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscywgd2UgaGF2ZSBkZXRlcm1pbmVkIHRoYXQgJXMgcGVyY2VudCBvZiB0aGUgdmFyaWF0aW9uIGluIHVzZXIgcHVyY2hhc2UgYmVoYXZpb3IgY2FuIGJlIGV4cGxhaW5lZCBieSB0aGUgbnVtYmVyIG9mIHRpbWVzIGEgdXNlciB2aWV3ZWQgb24gYSByZWxldmFudCBhZCBjYW1wYWlnbjsgd2hlcmVhcyBvbmx5ICVzIHBlcmNlbnQgb2YgdGhpcyB2YXJpYXRpb24gY2FuIGJlIGV4cGxhaW5lZCBieSB0aGUgbnVtYmVyIG9mIHRpbWVzIGEgdXNlciBjbGlja2VkIG9uIGEgcmVsZXZhbnQgYWQuIiwgcl9zcV8yLCByX3NxKQpgYGAKIyMgQ2hlY2tpbmcgTW9kZWwgUmVzaWR1YWxzCgpHcmVhdCEgV2UgY2FuIGJ1aWxkIGEgbW9kZWwhIEJ1dOKApiBob3cgZG8gd2Uga25vdyBpZiBpdOKAmXMgYW55IGdvb2Q/IEFsc28sIGlmIGFub3RoZXIgZGF0YSBzY2llbnRpc3QgYnVpbGRzIGEgZGlmZmVyZW50IG1vZGVsIHVzaW5nIGEgZGlmZmVyZW50IGluZGVwZW5kZW50IHZhcmlhYmxlLCBob3cgY2FuIHdlIHRlbGwgd2hpY2ggbW9kZWwgaXMg4oCcYmVzdOKAnT8gRXZlbiB3aXRoaW4gU3RhdGlzdGljcywg4oCcYmVzdOKAnSBjYW4gYmUgYSBzdWJqZWN0aXZlIHF1YWxpZmllci4gSG93ZXZlciwgc2NpZW50aXN0cyB3aG8gdXNlIHJlZ3Jlc3Npb24gbW9kZWxzIGdlbmVyYWxseSBhZ3JlZSB0aGF0IHRoZSBiZXN0IG1vZGVsIGlzIHRoZSBvbmUgdGhhdCBtaW5pbWl6ZXMgdGhlIGRpc3RhbmNlIGJldHdlZW4gYSBkYXRhIHBvaW50IGFuZCB0aGUgZXN0aW1hdGlvbiBsaW5lIGRyYXduIGJ5IGEgbW9kZWwuIFRoZSB2ZXJ0aWNhbCBkaXN0YW5jZSBiZXR3ZWVuIGEgZGF0YXBvaW50IGFuZCB0aGUgbGluZSBlc3RpbWF0ZWQgYnkgYSByZWdyZXNzaW9uIG1vZGVsIGlzIGNhbGxlZCBhIHJlc2lkdWFsOyByZXNpZHVhbHMgYW5kIHRoZWlyIGFnZ3JlZ2F0aW9ucyBhcmUgdGhlIGZ1bmRhbWVudGFsIHVuaXRzIG9mIG1lYXN1cmVzIG9mIHJlZ3Jlc3Npb24gbW9kZWwgZml0IGFuZCBhY2N1cmFjeS4KCkJlY2F1c2UgcmVzaWR1YWxzIGFyZSBiYXNlZCBvbiBjYXJ0ZXNpYW4gZGlzdGFuY2VzLCBpdCBvZnRlbiBoZWxwcyB0byB2aXN1YWxpemUgdGhlaXIgdmFsdWVzLiBGb3IgaW5zdGFuY2UsIGNvbnNpZGVyIHRoZSBwbG90IG9mIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGFsb25nc2lkZSBpdHPigJkgdHJhaW5pbmcgZGF0YSBiZWxvdy4gTm90ZSBvbmUgcG9pbnQgaXMgNCB1bml0cyBhYm92ZSB0aGUgcmVncmVzc2lvbiBlc3RpbWF0ZSBsaW5lOyBpbiB0aGlzIGV4YW1wbGUsIHRoZSByZXNpZHVhbCBmb3IgdGhhdCBwb2ludCBpcyA0LiBNZWFud2hpbGUsIGFub3RoZXIgcG9pbnQgaXMgMiB1bml0cyBiZWxvdyB0aGUgcmVncmVzc2lvbiBlc3RpbWF0ZSBsaW5lOyB0aGUgcmVzaWR1YWwgZm9yIHRoYXQgcG9pbnQgaXMgLTIuIEEgZGF0YSBwb2ludCBpcyBiZXN0IGZpdCBieSB0aGUgbW9kZWwgd2hpY2ggcmVzdWx0cyBpbiB0aGUgc21hbGxlc3QgcmVzaWR1YWwgZm9yIHRoYXQgcG9pbnQuCgohW3Jlc2lkdWFsYXJyb3ddKHJlc2lkdWFsQXJyb3cucG5nKQpXaGVuIHNjaWVudGlzdHMgbWFrZSBxdWFudGl0YXRpdmUgYXJndW1lbnRzIGZvciBhIGJlc3QgZml0IG1vZGVsLCB0aGV5IHJlbHkgb24gYW4gYWdncmVnYXRpb24sIG9mdGVuIHRoZSBzdW0gb3IgYXZlcmFnZSwgb2YgcmVzaWR1YWwgdmFsdWVzIGFjcm9zcyBhbiBlbnRpcmUgZGF0YXNldC4gV2hpbGUgaXMgaXQgZWFzeSB0byBiZSBvdmVyd2hlbG1lZCBieSB0aGUgdmFyaWV0eSBvZiBtZWFzdXJlcyB1c2VkIHRvIGFyZ3VlIHRoYXQgb25lIG1vZGVsIGlzIGJldHRlciB0aGFuIHRoZSBvdGhlciwgaXQgaXMgY3J1Y2lhbCB0byByZWFsaXplIHRoYXQgYWxsIG1lYXN1cmVzIGFyZSBncm91bmRlZCBpbiB0aGUgc2ltcGxlIGRpZmZlcmVuY2UgYmV0d2VlbiByZWdyZXNzaW9uIGVzdGltYXRlIGFuZCBvYnNlcnZlZCBkYXRhIHBvaW50LiBMZXTigJlzIHByb2R1Y2UgYSB2aXN1YWxpemF0aW9uIG9mIG91ciBvd24gbW9kZWwgb2YgY2xpY2tzIG9uIHRvdGFsX2NvbnZlcnQgdG8gYmV0dGVyIHVuZGVyc3RhbmQgb3VyIG1vZGVsIHJlc2lkdWFscy4KCkZpcnN0LCBsZXTigJlzIHB1bGwgb3V0IHRoZSBwb2ludHMgdGhhdCBtYWtlIHVwIHRoZSBlc3RpbWF0ZSBsaW5lLCBhbmQgcmVzcGVjdGl2ZSByZXNpZHVhbCB2YWx1ZXMsIGZyb20gb3VyIG1vZGVsLiBXZSBjYW4gc2F2ZSB0aGVtIHRvIGNvbHVtbnMgYmFjayBpbiBvdXIgdHJhaW4gZGF0YXNldCBjYWxsZWQgZXN0aW1hdGUgYW5kIHJlc2lkdWFscyBpbiBvdXIgbWFpbiB0cmFpbiB0cmFpbmluZyBkYXRhc2V0LgoKQ2FsbCBwcmVkaWN0KCkgb24gbW9kZWwgYW5kIHNhdmUgdGhlIHJlc3VsdCB0byB0cmFpbiRlc3RpbWF0ZS4KYGBge3J9CnRyYWluJGVzdGltYXRlIDwtIHByZWRpY3QobW9kZWwpCmBgYAoKQ2FsbCByZXNpZHVhbHMoKSBvbiBtb2RlbCBhbmQgc2F2ZSB0aGUgcmVzdWx0IHRvIHRyYWluJHJlc2lkdWFscwpgYGB7cn0KdHJhaW4kcmVzaWR1YWxzIDwtIHJlc2lkdWFscyhtb2RlbCkKYGBgCmxvdCB0aGUgdmFsdWVzIG9mIGNsaWNrcyBhbmQgdG90YWxfY29udmVydCB1c2luZyBhIGNvbWJpbmF0aW9uIG9mIGdncGxvdCgpIGFuZCBnZW9tX3BvaW50KCkuIFNhdmUgdGhlIHJlc3VsdCB0byBhIHZhcmlhYmxlIGNhbGxlZCBwbG90LiBEb27igJl0IGZvcmdldCB0byBwYXNzIGluIGNsaWNrcyBhcyBhcyBYIHZhcmlhYmxlLCBhbmQgdG90YWxfY29udmVydCBhcyBhIFkgdmFyaWFibGUuIE1ha2Ugc3VyZSB5b3XigJlyZSB1c2luZyB0cmFpbiBhcyB0aGUgZGF0YXNldC4KCkNhbGwgcGxvdCB0byB2aWV3IHlvdXIgdmlzdWFsaXphdGlvbi4KYGBge3J9CiNjcmVhdGUgdmlzdWFsaXphdGlvbgpwbG90IDwtIGdncGxvdCh0cmFpbiwgYWVzKGNsaWNrcywgdG90YWxfY29udmVydCkpICsKZ2VvbV9wb2ludCgpCgpwbG90CmBgYApQbG90IHRoZSBvYnNlcnZlZCBkYXRhIHBvaW50cyBpbiBvdXIgdHJhaW4gZGF0YXNldCBieSBhZGRpbmcgYW5vdGhlciBjYWxsIHRvIGdlb21fcG9pbnQoKS4gTWFrZSBzdXJlIHRvIGV4cGxpY2l0bHkgcGFzcyBpbiBlc3RpbWF0ZSBhcyBhIHkgdmFsdWUsIGFuZCBzZXQgdGhlIGNvbG9yIHBhcmFtZXRlciBlcXVhbCB0byAiYmx1ZSIuCmBgYHtyfQpwbG90IDwtIGdncGxvdCh0cmFpbiwgYWVzKGNsaWNrcywgdG90YWxfY29udmVydCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fcG9pbnQoYWVzKHkgPSBlc3RpbWF0ZSksIGNvbG9yID0gImJsdWUiKSAKCnBsb3QKYGBgCkxldOKAmXMgZXhwbGljaXRseSBwbG90IHRoZSB2ZXJ0aWNhbCBkaXN0YW5jZSBiZXR3ZWVuIGVzdGltYXRlIHZhbHVlcyBhbmQgdGhlaXIgcmVzcGVjdGl2ZSBvYnNlcnZlZCBkYXRhIHBvaW50LiBBZGQgYSBjYWxsIHRvIGdlb21fc2VnbWVudCgpLCBwYXNzaW5nIGluIHhlbmQgPSBjbGlja3MgYW5kIHllbmQgPSBlc3RpbWF0ZSBhcyBhcmd1bWVudHMgdG8gYWVzKCkuIERvbuKAmXQgZm9yZ2V0IHRvIHNldCBjb2xvciA9ICJncmF5IgpgYGB7cn0KcGxvdCA8LSBnZ3Bsb3QodHJhaW4sIGFlcyhjbGlja3MsIHRvdGFsX2NvbnZlcnQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3BvaW50KGFlcyh5ID0gZXN0aW1hdGUpLCBjb2xvciA9ICJibHVlIikgKwogIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IGNsaWNrcywgeWVuZCA9IGVzdGltYXRlKSwgY29sb3IgPSAiZ3JheSIpCgpwbG90CmBgYApGaW5hbGx5LCB3ZSBzaG91bGQgcHJvdmlkZSBhbm90aGVyIHdheSB0byBvYnNlcnZlIHRoZSBzaXplIG9mIHJlc2lkdWFscy4gVXBkYXRlIG91ciBmaXJzdCBjYWxsIHRvIGdlb21fcG9pbnQoKSBieSBwYXNzaW5nIGluIHNpemUgPSBhYnMocmVzaWR1YWxzKSBhcyBhbiBhcmd1bWVudCB0byBhZXMoKS4gQXMgdG90YWxfY29udmVydCBpbmNyZWFzZXMsIGhvdyBkbyB0aGUgdmFsdWUgb2YgbW9kZWwgcmVzaWR1YWxzIGNoYW5nZT8KYGBge3J9CnBsb3QgPC0gZ2dwbG90KHRyYWluLCBhZXMoY2xpY2tzLCB0b3RhbF9jb252ZXJ0KSkgKwogIGdlb21fcG9pbnQoYWVzKHNpemUgPSBhYnMocmVzaWR1YWxzKSkpICsKICBnZW9tX3BvaW50KGFlcyh5ID0gZXN0aW1hdGUpLCBjb2xvciA9ICJibHVlIikgKwogIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IGNsaWNrcywgeWVuZCA9IGVzdGltYXRlKSwgY29sb3IgPSAiZ3JheSIpCgpwbG90CmBgYAojIyBWaXN1YWxpemluZyBNb2RlbCBGaXQKCkluIGFkZGl0aW9uIHRvIHRoZSBxdWFudGl0YXRpdmUgbWVhc3VyZXMgdGhhdCBjaGFyYWN0ZXJpemUgb3VyIG1vZGVsIGFjY3VyYWN5LCBpdCBpcyBhbHdheSBhIGJlc3QgcHJhY3RpY2UgdG8gcHJvZHVjZSB2aXN1YWwgc3VtbWFyaWVzIHRvIGFzc2VzcyBvdXIgbW9kZWwuIEZpcnN0LCB3ZSBzaG91bGQgYWx3YXlzIHZpc3VhbGl6ZSBvdXIgbW9kZWwgd2l0aGluIG91ciBkYXRhLiBGb3Igc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIHRoaXMgaXMgcXVpdGUgc2ltcGxlOyB3ZSBjYW4gdXNlIGdlb21fcG9pbnQoKSB0byBwbG90IG91ciBvYnNlcnZlZCB2YWx1ZXMsIGFuZCBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSB0byBwbG90IG91ciBtb2RlbC4gSW4gYWRkaXRpb24sIHdlIGNhbiBpbmNsdWRlIGEgc2Vjb25kIGNhbGwgdG8gZ2VvbV9zbW9vdGgoKSwgd2l0aCBwYXJhbWV0ZXJzIChzZSA9IEZBTFNFLCBjb2xvciA9ICJyZWQiKS4gVGhpcyBjb21iaW5hdGlvbiBvZiBmdW5jdGlvbiBjYWxscyBhbGxvd3MgdXMgdG8gY29tcGFyZSB0aGUgbGluZWFyaXR5IG9mIG91ciBtb2RlbCwgdmlzdWFsaXplZCBiZWxvdyBhcyB0aGUgYmx1ZSBsaW5lIHdpdGggdGhlIDk1JSBjb25maWRlbmNlIGludGVydmFsIGNvdmVyaW5nIHRoZSBzaGFkZWQgcmVnaW9uLCBpbiBjb21wYXJpc29uIHRvIGEgbm9uLWxpbmVhciBMT0VTUyBzbW9vdGhlciB2aXN1YWxpemVkIGluIHJlZC4KYGBge3J9CgphZF9zYW1wbGUgPC0gc2FtcGxlKGMoVFJVRSwgRkFMU0UpLCBucm93KGFkdmVydGlzaW5nKSwgcmVwbGFjZSA9IFQsIHByb2IgPSBjKDAuNiwgMC40KSkKCiMgc3Vic2V0IGRhdGEgcG9pbnRzIGludG8gdHJhaW4gYW5kIHRlc3Qgc2V0cwphZHZlcnRpc2luZ1thZF9zYW1wbGUsIF0gJT4lIApnZ3Bsb3QoYWVzKHBvZGNhc3QsIHNhbGVzKSkgKwpnZW9tX3BvaW50KCkgKwpnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCmdlb21fc21vb3RoKHNlID0gRkFMU0UsIGNvbG9yID0gInJlZCIpIApgYGAKTE9FU1Mgc21vb3RoZXJzIHBsb3QgYSBsaW5lIGJhc2VkIG9uIHRoZSB3ZWlnaHRlZCB2YWx1ZSBvZiBkYXRhIHBvaW50czsgdGhlIGxpbmUgcHJvZHVjZWQgYnkgYSBMT0VTUyBzbW9vdGhlciBpcyBzaW1pbGFyIHRvIHRha2luZyBhIG1vdmluZyBhdmVyYWdlIG9mIGRhdGEgcG9pbnRzIGFzIG91ciB4LWF4aXMgdmFyaWFibGUgaW5jcmVhc2VzLiBUaGUgc21vb3RoZXIgc2hvdWxkIG5vdCBiZSB1c2VkIHRvIHByZWRpY3QgbmV3IHZhbHVlcywgYXMgaXQgcmVsaWVzIGhlYXZpbHkgb24gb3VyIHRyYWluaW5nIGRhdGEsIGJ1dCBpdCBpcyBhIGhlbHBmdWwgdG9vbCBmb3IgdmlzdWFsaXppbmcgd2hlcmUgb3VyIGxpbmVhciBtb2RlbCBkaXZlcmdlcyBmcm9tIG91ciB0cmFpbmluZyBkYXRhLgoKQ29uc2lkZXJpbmcgdGhlIExPRVNTIHNtb290aGVyIHJlbWFpbnMgd2l0aGluIHRoZSBjb25maWRlbmNlIGludGVydmFsIG9mIG91ciBtb2RlbCwgd2UgY2FuIGFzc3VtZSB0aGUgbGluZWFyIHRyZW5kIGZpdHMgdGhlIGVzc2VuY2Ugb2YgdGhpcyByZWxhdGlvbnNoaXAuIEhvd2V2ZXIsIHdlIHNob3VsZCBub3RlIHRoYXQgYXMgdGhlIHBvZGNhc3QgYWR2ZXJ0aXNpbmcgYnVkZ2V0IGdldHMgY2xvc2VyIHRvIDAgdGhlcmUgaXMgYSBzdHJvbmdlciByZWR1Y3Rpb24gaW4gc2FsZXMgYmV5b25kIHdoYXQgdGhlIGxpbmVhciB0cmVuZCBmb2xsb3dzOyB0aGlzIG1lYW5zIHRoYXQgb3VyIG1vZGVsIG1pZ2h0IGJlIGxlc3MgYWNjdXJhdGUgaW4gaW5zdGFuY2VzIHdoZXJlIHRoZSBwb2RjYXN0IGJ1ZGdldCBpcyB2ZXJ5IGxvdy4KCldl4oCZdmUgcGxvdHRlZCBjbGlja3MgYWdhaW5zdCB0b3RhbCBjb252ZXJ0cy4gTGV04oCZcyBhZGQgYSBMT0VTUyBzbW9vdGhlci4gQWRkIHR3byBjYWxscyBvZiBnZW9tX3Ntb290aCgpIHRvIHBsb3QuIFRoZSBmaXJzdCBzaG91bGQgdXNlIHRoZSBwYXJhbWV0ZXIgbWV0aG9kID0gImxtIi4gVGhlIHNlY29uZCBzaG91bGQgdXNlIHRoZSBwYXJhbWV0ZXJzIHNlID0gRkFMU0UgYW5kIGNvbG9yID0gInJlZCIuCmBgYHtyfQojIGJ1aWxkIHBsb3Qgb2YgY2xpY2tzIG9uIHRvdGFsX2NvbnZlcnQgYmVsb3cKcGxvdCA8LSBnZ3Bsb3QodHJhaW4sIGFlcyhjbGlja3MsIHRvdGFsX2NvbnZlcnQpKSArCmdlb21fcG9pbnQoKSArCmdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgY29sb3IgPSAicmVkIikKCnBsb3QKYGBgCkhvdyBjbG9zZWx5IGRvZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNsaWNrcyBhbmQgY29udmVyc2lvbiBmb2xsb3cgYSBsaW5lYXIgdHJlbmQ/IFNldCB0aGUgdmFyaWFibGUgbGluZWFyX3JlbGF0aW9uc2hpcCBlcXVhbCB0byBlaXRoZXIgImEiLCAiYiIsICJjIiwgb3IgImQiIGRlcGVuZGluZyBvbiB0aGUgc3RhdGVtZW50IHRoYXQgYmVzdCBjaGFyYWN0ZXJpemVzIHRoZSByZWxhdGlvbnM6CgpBLiBUaGUgcmVsYXRpb25zaGlwIGlzIGxlc3MgbGluZWFyIHdoZW4gY2xpY2tzIGFwcHJvYWNoZXMgbGFyZ2UgdmFsdWVzLgoKQi4gVGhlcmUgaXMgYSBjbGVhciBkaXZlcmdlbmNlIGZyb20gYSBsaW5lYXIgcmVsYXRpb25zaGlwIHdoZW4gY2xpY2tzIGFwcHJvYWNoZXMgemVybyBvciB3aGVuIGNsaWNrcyBhcHByb2FjaGVzIGluZmluaXR5LgoKQy4gVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNsaWNrcyBhbmQgdG90YWxfY29udmVyc2lvbiBpcyBwZXJmZWN0bHkgbGluZWFyLgoKRC4gVGhlcmUgaXMgbm8gbGluZWFyIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNsaWNrcyBhbmQgdG90YWxfY29udmVyc2lvbgpgYGB7cn0KbGluZWFyX3JlbGF0aW9uc2hpcCA9ICJhIgpgYGAKTGV04oCZcyBleHRlbmQgb3VyIGxpbmVhcml0eSBhbmFseXNpcyB0byBvdXIgbW9kZWwyLCB3aGljaCBkZXNjcmliZXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGltcHJlc3Npb25zIGFuZCB0b3RhbF9jb252ZXJzaW9uLiBBZGQgdGhlIHR3byBjYWxscyB0byBnZW9tX3Ntb290aCgpIHRvIHBsb3RfMiB0byBtYWtlIGEgY29tcGFyaXNvbiB0byBhIExPRVNTIG1vZGVsLgpgYGB7cn0KIyBidWlsZCBwbG90IG9mIGltcHJlc3Npb25zIG9uIHRvdGFsX2NvbnZlcnQgYmVsb3cKcGxvdF8yIDwtIGdncGxvdCh0cmFpbiwgYWVzKGltcHJlc3Npb25zLCB0b3RhbF9jb252ZXJ0KSkgKwpnZW9tX3BvaW50KCkgKwpnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCmdlb21fc21vb3RoKHNlID0gRkFMU0UsIGNvbG9yID0gInJlZCIpCgpwbG90XzIKYGBgCgpIb3cgY2xvc2VseSBkb2VzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBpbXByZXNzaW9ucyBhbmQgY29udmVyc2lvbiBmb2xsb3cgYSBsaW5lYXIgdHJlbmQ/IFNldCB0aGUgdmFyaWFibGUgbGluZWFyX3JlbGF0aW9uc2hpcF8yIGVxdWFsIHRvICJhIiwgImIiLCAiYyIsIG9yICJkIiBkZXBlbmRpbmcgb24gdGhlIHN0YXRlbWVudCB0aGF0IGJlc3QgY2hhcmFjdGVyaXplcyB0aGUgcmVsYXRpb25zOgoKQS4gVGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGltcHJlc3Npb25zIGFuZCB0b3RhbF9jb252ZXJzaW9uIGlzIHBlcmZlY3RseSBsaW5lYXIuCgpCLiBUaGVyZSBpcyBhIGNsZWFyIGRpdmVyZ2VuY2UgZnJvbSBhIGxpbmVhciByZWxhdGlvbnNoaXAgd2hlbiBpbXByZXNzaW9ucyBhcHByb2FjaGVzIHplcm8gYW5kIHdoZW4gaW1wcmVzc2lvbnMgaXMgYXJvdW5kIDUwMCwwMDAuCgpDLiBUaGUgcmVsYXRpb25zaGlwIGlzIGxlc3MgbGluZWFyIHdoZW4gaW1wcmVzc2lvbnMgYXBwcm9hY2hlcyB2ZXJ5IGxhcmdlIHZhbHVlcyBhbmQgd2hlbiBpbXByZXNzaW9ucyBpcyBhcm91bmQgNTAwLDAwMC4KCkQuIFRoZXJlIGlzIG5vIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBpbXByZXNzaW9ucyBhbmQgdG90YWxfY29udmVyc2lvbgpgYGB7cn0KbGluZWFyX3JlbGF0aW9uc2hpcF8yICA9ICJjIgpgYGAKIyMgUmVhZGluZyBNb2RlbCBSZXN1bHRzCgpSZWFkeSBmb3IgdGhlIHJlYWwgZnVuPyBXZeKAmXZlIGRvbmUgb3VyIGR1ZSBkaWxpZ2VuY2UgYW5kIGNvbmZpcm1lZCB0aGF0IG91ciBkYXRhIGZ1bGZpbGxzIHRoZSBhc3N1bXB0aW9ucyBvZiBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzOyB3ZeKAmXZlIHNwbGl0IG91ciBkYXRhIGludG8gdGVzdCBhbmQgdHJhaW5pbmcgc3Vic2V0cywgYW5kIHByb3Blcmx5IGJ1aWx0IGEgbW9kZWwgdXNpbmcgeSB+IHggKyBiIG5vdGF0aW9uOyB3ZeKAmXZlIGV2ZW4gdGFrZW4gdGhlIHRpbWUgdG8gYXNzZXNzIHRoZSBmaXQgb2Ygb3VyIG1vZGVsIHVzaW5nIGJvdGggcXVhbnRpdGF0aXZlIGFuZCBxdWFsaXRhdGl2ZSBhcHByb2FjaGVzLiBOb3cgd2UgY2FuIGZpbmFsbHkgYW5hbHl6ZSB0aGUgcmVzdWx0cyBvZiBvdXIgbW9kZWwgYW5kIGRpc2NvdmVyIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB1c2VyIGFkdmVydGlzZW1lbnQgY2xpY2tzIGFuZCB0aGUgcHVyY2hhc2UgcmF0ZSBvZiByZWxhdGVkIHByb2R1Y3RzIQoKV2UgY2FuIHZpZXcgdGhlIHJlc3VsdHMgb2YgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpbiBSIGJ5IGNhbGxpbmcgc3VtbWFyeSgpIG9uIHRoZSBtb2RlbCB2YXJpYWJsZSB0byB3aGljaCB3ZSBzYXZlZCB0aGUgcmVzdWx0cyBvZiBvdXIgY2FsbCB0byBsbSgpLiBUaGUgc3VtbWFyeSgpIGZ1bmN0aW9uIHdpbGwgcHJpbnQgb3V0IGEgbG90IG9mIGluZm9ybWF0aW9uIGFib3V0IG91ciBtb2RlbOKAk+KAkyBidXQgZG9u4oCZdCBiZSBvdmVyd2hlbG1lZCEgVGhlcmUgYXJlIGZvdXIgcHJpbWFyeSBzZWN0aW9ucyBvZiBxdWFudGl0YXRpdmUgcmVzdWx0cyB0aGF0IGFyZSBjcnVjaWFsIHRvIGludGVycHJldGluZyByZWdyZXNzaW9uIG1vZGVsczoKCiFbc3VtbWFyeV0oc3VtbWFyeV9tb2RlbC5wbmcpCiMjIyBDYWxsCgpUaGlzIHNlY3Rpb24gc2ltcGxlIGRpc3BsYXlzIHRoZSBjYWxsIHRvIGxtKCkgd2hpY2ggY3JlYXRlZCB0aGVzZSBtb2RlbCByZXN1bHRzLiBJdOKAmXMgYSBoZWxwZnVsIHJlbWluZGVyIG9mIHdoaWNoIHZlcnNpb24gb2YgYSBvdXRjb21lLXByZWRpY3RvciBwYWlyIGlzIGN1cnJlbnRseSB1bmRlciBhbmFseXNpcy4KCiMjIyBSZXNpZHVhbHMKCkFzIGNvdmVyZWQgaW4gb3VyIGVhcmxpZXIgZXhlcmNpc2VzLCBhIHJlc2lkdWFsIGlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHZhbHVlIG9mIGFuIG91dGNvbWUgdmFyaWFibGUgcHJlZGljdGVkIGJ5IHRoZSBtb2RlbCBhbmQgdGhlIGFjdHVhbCBvYnNlcnZlZCB2YWx1ZSBvZiB0aGUgdmFyaWFibGUuIFRoZSBzdW1tYXJ5KCkgb3V0cHV0IGRpc3BsYXlzIGEgc2V0IG9mIG51bWJlcnMgdGhhdCBzdW1tYXJpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiByZXNpZHVhbHMgaW4gb3VyIG1vZGVsLCBpbmNsdWRpbmcgbWluaW11bS9tYXhpbXVtIHJlc2lkdWFsIHZhbHVlcywgdGhlIHZhbHVlcyBmaXJzdC90aGlyZCBxdWFudGlsZXMsIGFuZCB0aGUgbWVkaWFuIHJlc2lkdWFsIHZhbHVlIGZvciB0aGUgbW9kZWwuIFdl4oCZdmUgYWxyZWFkeSBhbmFseXplZCBvdXIgcmVzaWR1YWwgdmFsdWVzIGJ5IGNyZWF0aW5nIGEgcGxvdCBpbiBhbiBlYXJsaWVyIGV4ZXJjaXNlLCBidXQgdGhlc2Ugc3VtbWFyeSB2YWx1ZXMgYXJlIGEgaGVscGZ1bCByZW1pbmRlciBvZiB0aGUgb3ZlcmFsbCBzcHJlYWQgb2Ygb3VyIG1vZGVsIGVycm9ycy4KCiMjIyBDb2VmZmljaWVudHMKCiMjIyMgRXN0aW1hdGUKCkNvZWZmaWNpZW50cyBhcmUgbW9zdCBpbXBvcnRhbnQgcmVzdWx0cyBpbiB0aGUgaW50ZXJwcmV0YXRpb24gb2YgcmVncmVzc2lvbiBtb2RlbHMuIFRoZSBudW1iZXIgeW91IHNlZSBpbiB0aGUgRXN0aW1hdGUgY29sdW1uLCAoYSB2YWx1ZSBvZiAwLjA0ODkzOSBmb3IgY2xpY2tzKSBpcyBjYWxsZWQgYSByZWdyZXNzaW9uIGNvZWZmaWNpZW50LiBMb29raW5nIGJhY2sgdG8gZm9ybWFsIGRlZmluaXRpb24gb2YgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbDoKCiQgWT0gYmV0YV8wICsgYmV0YV8x4oiXWCArIGVycm9yJAoKVGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgaXMgcmVwcmVzZW50ZWQgYnkgdGhlIGJldGFfMSB2YXJpYWJsZS4gVGhpcyBsaW5lYXIgcmVncmVzc2lvbiBlcXVhdGlvbiB0ZWxscyB1cyB0aGF0IHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50IHJlcHJlc2VudHMgdGhlIGV4cGVjdGVkIGNoYW5nZSBpbiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIChpbiBvdXIgY2FzZSB0b3RhbF9jb252ZXJ0KSBmb3IgYSBvbmUtdW5pdCBpbmNyZWFzZSBpbiB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUgKGNsaWNrcykuIEluIG90aGVyIHdvcmRzLCBmb3IgZXZlcnkgYWRkaXRpb25hbCBjbGljayBvbiBhbiBhZHZlcnRpc2VtZW50LCB0aGUgZXhwZWN0ZWQgc2FsZXMgb2YgYSByZWxhdGVkIHByb2R1Y3QgYXJlIGVzdGltYXRlZCB0byBpbmNyZWFzZSBieSAwLjA0OSBkb2xsYXJzLiBJbiBhZGRpdGlvbiB0byB0aGUgc2l6ZSBvZiB0aGUgY29lZmZpY2llbnQsIGl0IGlzIGFsc28gaW1wb3J0YW50IHRvIG5vdGUgdGhlIHNpZ24gb2YgdGhlIGNvZWZmaWNpZW50LiBJZiBvdXIgY2xpY2tzIGNvZWZmaWNpZW50IHdhcyBuZWdhdGl2ZSwgb3VyIG1vZGVsIHdvdWxkIGJlIGVzdGltYXRpbmcgdGhhdCB0aGUgc2FsZXMgb2YgYSBwcm9kdWN0IGFjdHVhbGx5IGRlY3JlYXNlcyBldmVyeSB0aW1lIGFuIGFkdmVydGlzZW1lbnQgaXMgY2xpY2tlZC4KCiMjIyMgU3RkLiBFcnJvcgoKVGhlIGNvbHVtbiBhZGphY2VudCB0byBFc3RpbWF0ZSBpcyBjYWxsZWQgU3RkLiBFcnJvcjsgdGhlIHN0YW5kYXJkIGVycm9yIG9mIGVhY2ggY29lZmZpY2llbnQgaXMgdGhlIGVzdGltYXRlIG9mIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGNvZWZmaWNpZW50LiBJdCBpcyBjcnVjaWFsIHRvIG5vdGUgdGhhdCB0aGUgc3RhbmRhcmQgZXJyb3IgaXMgbm90IGEgcXVhbnRpdHkgb2YgaW50ZXJlc3QgYnkgaXRzZWxmLCBidXQgZGVwZW5kcyBvbiB0aGUgdmFsdWUgb2Ygb3VyIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQKCiMjIyMgVC12YWx1ZSBhbmQgUHIoPnx0fCkKClRoZSB0IHZhbHVlIGFuZCBQcig+fHR8KSBpbmhlcmVudGx5IGFuc3dlciB0aGUgc2FtZSBxdWVzdGlvbuKAk+KAkyBnaXZlbiB0aGUgdmFsdWUgb2Ygb3VyIHZhcmlhYmxl4oCZcyByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGFuZCBpdHPigJkgc3RhbmRhcmQgZXJyb3IsIGRvZXMgdGhlIHZhcmlhYmxlIGV4cGxhaW4gYSBzaWduaWZpY2FudCBwYXJ0IG9mIHRoZSBjaGFuZ2UgaW4gb3VyIG91dGNvbWUgdmFyaWFibGU/IEhvd2V2ZXIsIHRoZSBQcig+fHR8KSBjb2x1bW4gcHVycG9zZWx5IHByb3ZpZGVzIGEgbW9yZSBjb25jaXNlIHJlc3BvbnNlIHRvIHRoaXMgcXVlc3Rpb24sIHVzaW5nIHRoZSBhc3RlcmlzayBub3RhdGlvbiB0aGF0IGNvcnJlc3BvbmRzIHdpdGggdGhlIFNpZ25pZi4gY29kZXMgbGVnZW5kIGF0IHRoZSBib3R0b20gb2YgdGhlIENvZWZmaWNpZW50cyByZXN1bHRzIHNlY3Rpb24uIEluIFIgbW9kZWwgb3V0cHV0LCBvbmUgYXN0ZXJpc2sgbWVhbnMg4oCccCA8IC4wNeKAnS4gVHdvIGFzdGVyaXNrcyBtZWFuIOKAnHAgPCAuMDHigJ07IGFuZCB0aHJlZSBhc3Rlcmlza3MgbWVhbiDigJxwIDwgLjAwMeKAnS4gVGhlc2UgdmFsdWVzIGFyZSByZWZlcnJlZCB0byBhcyBwLXZhbHVlcyBpbiBzY2llbnRpZmljIGxpdGVyYXR1cmUuIEhvdyBjYW4gd2UgdXNlIHAtdmFsdWVzIHRvIGFuc3dlciBvdXIgcXVlc3Rpb24gYXJvdW5kIG1vZGVsIHNpZ25pZmljYW5jZT8KCkFzdGVyaXNrcyBpbiBhIHJlZ3Jlc3Npb24gdGFibGUgaW5kaWNhdGUgdGhlIGxldmVsIG9mIHRoZSBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2Ugb2YgYSByZWdyZXNzaW9uIGNvZWZmaWNpZW50LiBPdXIgdW5kZXJzdGFuZGluZyBvZiBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgaXMgYmFzZWQgb2ZmIG9mIHRoZSBpZGVhIG9mIGEgcmFuZG9tIHNhbXBsZS4gV2hlbiBpbnRlcnByZXRpbmcgdGhlc2UgYXN0ZXJpc2sgdmFsdWVzLCB3ZSBhc2sgb3Vyc2VsdmVzOiBpZiB0aGVyZSB0cnVseSBpcyBubyByZWxhdGlvbnNoaXAgYmV0d2VlbiBjbGlja3Mgb24gYW4gYWR2ZXJ0aXNlbWVudCBhbmQgcHJvZHVjdCBzYWxlcywgdGhlbiB3aGF0IGFyZSBjaGFuY2VzIHRoYXQsIGFjcm9zcyBtYW55IHVzZXIgY2xpY2tzIG9uIGFuIGFkLCB3ZSBzZWUgYmVoYXZpb3IgdGhhdCBzdWdnZXN0cyB0aGF0IHRoZXJlIGlzIG5vIHJlbGF0aW9uc2hpcD8KCkZvciBvdXIgY2xpY2tzIHZhcmlhYmxlLCB3aXRoICoqKiBpbiB0aGUgUHIoPnx0fCkgY29sdW1uLCB0aGUgYW5zd2VyIGlzIHZlcnkgdW5saWtlbHkuIFRoZSB2YWx1ZSBvZiAqKiosIG9yIHAgPCAuMDAxLCBtZWFucyB0aGF0IHJhbmRvbSBzYW1wbGUgcmVzdWx0aW5nIGluIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGFuZCBzdGFuZGFyZCBlcnJvciB0aGF0IHdlIG9ic2VydmVkIGZvciBjbGlja3MtZ2l2ZW4gdGhhdCB0aGVyZSB3YXMgdHJ1bHkgbm8gZGlmZmVyZW5jZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBjbGlja3MgYW5kIHByb2R1Y3QgcHVyY2hhc2XigJR3b3VsZCBvY2N1ciBpbiBsZXNzIHRoYW4gb25lIHRpbWUgaW4gYSByYW5kb20gZHJhdyBvZiAxMDAsIG9uIGF2ZXJhZ2UuIEdpdmVuIHRoYXQgd2Ugd291bGQgc28gcmFyZWx5IG9ic2VydmUgdGhlIHNpdHVhdGlvbiB0aGF0IHN1Z2dlc3RzIHRoYXQgdGhlcmUgaXMgbm8gcmVsYXRpb25zaGlwIGJldHdlZW4gb3VyIGNsaWNrIGFuZCB0b3RhbF9jb252ZXJ0LCB3ZSBjYW4gc2F5IHRoYXQgdGhlcmUgaXMgYSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLiBHZW5lcmFsbHkgc3BlYWtpbmcsIHNjaWVudGlzdHMgYWNjZXB0IHRoYXQgYSB2YXJpYWJsZSBjb2VmZmljaWVudCB3aXRoIHAtdmFsdWUgbGVzcyB0aGFuIDAuMDUgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4KCiMjIyBNZWFzdXJlcyBvZiBNb2RlbCBGaXQKCkF0IHRoZSBib3R0b20gb2YgdGhlIG91dHB1dCBvZiBzdW1tYXJ5KCkgYXJlIGEgc2VyaWVzIG9mIGxhYmVsZWQgbWV0cmljcywgbGlrZSBSZXNpZHVhbCBTdGFuZGFyZCBFcnJvciAoUlNFKSBhbmQgTXVsdGlwbGUgUi1zcXVhcmVkLCB3aGljaCBxdWFudGlmeSB0aGUgZml0IG9mIG91ciBtb2RlbC4gT3VyIHByZXZpb3VzIGV4ZXJjaXNlcyBoYXZlIGNvdmVyZWQgaG93IHRvIGludGVycHJldCBhbmQgcGxvdCBtYW55IG9mIHRoZXNlIG1lYXN1cmVzLCBidXQgaXTigJlzIGEgaGVscGZ1bCByZW1pbmRlciBmb3IgdGhlbSB0byBiZSBzdW1tYXJpemVkIGFsb25nIHdpdGggb3RoZXIgbW9kZWwgb3V0cHV0LgoKIyMgQXNzZXNzaW5nIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbgoKTGV04oCZcyBwcmFjdGljZSBvdXIgbW9kZWwgaW50ZXJwcmV0YXRpb24gc2tpbGxzISBXZSBrbm93IHRoYXQgZm9yIGNvbnRpbnVvdXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzLCBsaWtlIHBvZGNhc3RzLCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudCByZXByZXNlbnRzIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBwcmVkaWN0ZWQgdmFsdWUgb2Ygc2FsZXMgZm9yIGVhY2ggb25lLWRvbGxhciBpbmNyZWFzZSBpbiBwb2RjYXN0cy4gR2l2ZW4gdGhlIG91dHB1dCBvZiBjYWxsaW5nIHN1bW1hcnkobW9kZWwpIGJlbG93LCB3ZSBjYW4gY29ycmVjdGx5IHNheSB0aGF0IGZvciBldmVyeSBvbmUgZG9sbGFyIGluY3JlYXNlIGluIHBvZGNhc3QgYWR2ZXJ0aXNlbWVudCBzcGVuZGluZywgdG90YWwgc2FsZXMgb2YgdGhlIHJlbGF0ZWQgcHJvamVjdCBpbmNyZWFzZXMgYnkgMS43NDIgZG9sbGFycy4KCiAgICBzdW1tYXJ5KG1vZGVsKQogICAgCiAgICAjb3V0cHV0IAogICAgQ2FsbDoKICAgIGxtKGZvcm11bGEgPSBzYWxlcyB+IHJhZGlvLCBkYXRhID0gdHJhaW4pCiAgICAKICAgIENvZWZmaWNpZW50czoKICAgICAgICAgICAgICAgIEVzdGltYXRlIFN0ZC4gRXJyb3IgdCB2YWx1ZSBQcig+fHR8KSAgICAKICAgIChJbnRlcmNlcHQpICA5LjU3OTI3ICAgIDAuOTExNzYgIDEwLjUwNiAgPCAyZS0xNiAqKioKICAgIHBvZGNhc3QgICAgICAxLjc0MjQwICAgIDAuMDMyNTUgICA1LjM1MyAzLjcxZS0wNyAqKioKICAgIC0tLSAgICAKICAgIFNpZ25pZi4gY29kZXM6ICAwIOKAmCoqKuKAmSAwLjAwMSDigJgqKuKAmSAwLjAxIOKAmCrigJkgMC4wNSDigJgu4oCZIDAuMSDigJgg4oCZIDEKICAgIAogIApXZSBjb3VsZCBhbHNvIGV4dHJhY3QgdGhlIHZhbHVlIG9mIHRoZSBwb2RjYXN0IGNvZWZmaWNpZW50LCB0aGUgc2Vjb25kIGNvZWZmaWNpZW50IHJldHVybmVkIGJ5IG91ciBtb2RlbCwgdXNpbmcgbGlzdCBpbmRleGluZyBhcyBmb2xsb3dzOgogIAogICAgcG9kY2FzdF9jb2VmZmljZW50IDwtIG1vZGVsJGNvZWZmaWNpZW50c1syXSAKCkl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBpbnRlcmNlcHQgY29lZmZpY2llbnQgaXMgc2xpZ2h0bHkgZGlmZmVyZW50IGZyb20gdGhhdCBvZiB2YXJpYWJsZSBjb2VmZmljaWVudHMuIFRoZSBpbnRlcmNlcHQgY29lZmZpY2llbnQgcmVwcmVzZW50cyB0aGUgdmFsdWUgd2Ugd291bGQgcHJlZGljdCBmb3Igb3VyIG91dGNvbWUgdmFyaWFibGUsIHNhbGVzLCBnaXZlbiB0aGF0IHBvZGNhc3Qgc3BlbmRpbmcgaXMgZXF1YWwgdG8gemVyby4gSXQgaXMgY3J1Y2lhbCB0byByZW1lbWJlciB0aGF0IHRoZSBpbnRlcmNlcHQgY29lZmZpY2llbnQgaXMgb25seSBpbnRlcnByZXRhYmxlIGlmIHdlIGNhbiByZWFzb25hYmx5IGV4cGVjdCBhIHplcm8gdmFsdWUgZm9yIGFsbCBpbmRlcGVuZGVudCB2YXJpYWJsZXMgaW4gYSBtb2RlbC4gQXNzdW1pbmcsIGp1c3QgYXMgb3VyIHNpbXBsZSBsaW5lYXIgbW9kZWwgZG9lcywgdGhhdCBzcGVuZGluZyBvbiBwb2RjYXN0cyBpcyB0aGUgb25seSB2YXJpYWJsZSB0aGF0IGV4cGxhaW5zIGNoYW5nZXMgaW4gc2FsZXMsIGl0IGRvZXMgbm90IG1ha2Ugc2Vuc2UgZm9yIGFueSBzYWxlcyB0byBvY2N1ciB3aXRob3V0IHBvZGNhc3Qgc3BlbmRpbmcuIFRoZXJlZm9yZSwgZm9yIHRoaXMgbW9kZWwsIG91ciBpbnRlcmNlcHQgY29lZmZpY2llbnQgaXMgbm90IGludGVycHJldGFibGUuCgpIb3dldmVyLCBpbiBtYW55IGNhc2VzLCBpbnRlcmNlcHQgY29lZmZpY2llbnRzIGFyZSBpbnRlcnByZXRhYmxlISBUaGUgYW5hbHlzaXMgb2YgYW55IG1vZGVsIHJlc3VsdHMgcmVxdWlyZXMgYSB0aG9yb3VnaCB1bmRlcnN0YW5kaW5nIG9mIG91ciBkYXRhLCB0aGUgc3lzdGVtIHRoYXQgcHJvZHVjZXMgdGhpcyBkYXRhLCBhbmQgYSBjcml0aWNhbCBhcHByb2FjaCB0byBpbnRlcnByZXRhdGlvbiBvZiBjb2VmZmljaWVudCB2YWx1ZXMuCgpVc2Ugc3VtbWFyeSgpIHRvIHByaW50IG91dCB0aGUgcmVzdWx0cyBvZiBtb2RlbCBhbmQgbW9kZWwyLgpgYGB7cn0Kc3VtbWFyeShtb2RlbCkKCnN1bW1hcnkobW9kZWxfMikKYGBgClNhdmUgdGhlIHJlc3VsdHMgb2YgY2FsbGluZyBtb2RlbCRjb2VmZmljaWVudHNbMl0gdG8gYSB2YXJpYWJsZSBjYWxsZWQgY2xpY2tzX2NvZWZmaWNpZW50OwpgYGB7cn0KY2xpY2tzX2NvZWZmaWNpZW50IDwtIG1vZGVsJGNvZWZmaWNpZW50c1syXQoKc3ByaW50ZigiQmFzZWQgb24gYSBzaW1wbGUgcmVncmVzc2lvbiBvZiBgdG90YWxfY29udmVydGAgYnkgYGNsaWNrc2AsIHdlIGVzdGltYXRlIHRoYXQgZm9yIGV2ZXJ5IGFkZGl0aW9uYWwgY2xpY2ssIHRoZSBudW1iZXIgb2YgcHJvZHVjdCBwdXJjaGFzZXMgaW5jcmVhc2VzIGJ5ICVzLiIsIGNsaWNrc19jb2VmZmljaWVudCkKYGBgCkhvdyBtaWdodCB3ZSBpbnRlcnByZXQgdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlIGZvciBvdXIgbW9kZWwgaW50ZXJjZXB0PyBTZXQgdGhlIHZhcmlhYmxlIGludGVyY2VwdF9jb2VmZmljaWVudCBlcXVhbCB0byB0aGUgbG93ZXJjYXNlIGxldHRlciAoZXguICJhIikgYXNzb2NpYXRlZCB3aXRoIHRoZSBzdGF0ZW1lbnQgdGhhdCBjb3JyZWN0bHkgaW50ZXJwcmV0cyB0aGUgZXN0aW1hdGUgdmFsdWU6CgpBLiBUaGUgaW50ZXJjZXB0IGNvZWZmaWNpZW50IGlzIG5vdCBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIHplcm8sIHRoZXJlZm9yZSB0aGUgbW9kZWwgc3VnZ2VzdHMgdGhhdCB3aGVuIGNsaWNrcyBlcXVhbCB6ZXJvLCB0aGUgbnVtYmVyIG9mIHRvdGFsX2NvbnZlcnRzIGlzIGxpa2VseSB0byBiZSBhcm91bmQgemVyby4gVGhpcyBpcyByZWFzb25hYmxlIGJlY2F1c2UgaWYgYSB1c2VyIGhhcyBub3QgY2xpY2tlZCBvbiBhbiBhZCwgd2UgZG8gbm90IGV4cGVjdCB0aGVtIHRvIHB1cmNoYXNlIHRoZSByZWxhdGVkIHByb2R1Y3QuCgpCLiBUaGUgaW50ZXJjZXB0IGNvZWZmaWNpZW50IGlzIG5lZ2F0aXZlIGFuZCBzaWduaWZpY2FudDsgdGhpcyBtZWFucyB0aGF0IHdoZW4gY2xpY2tzIGVxdWFsIHplcm8sIHdlIGV4cGVjdCB0aGUgbnVtYmVyIG9mIHRvdGFsX2NvbnZlcnRzIHRvIGJlIGxlc3MgdGhhbiB6ZXJvLiBUaGlzIG1ha2VzIHNlbnNlIGJlY2F1c2UgdGhlIHVzZXIgZGlkIG5vdCBjbGljayBvbiBhbiBhZCwgc28gd2UgZXhwZWN0IHplcm8gb3IgbGVzcyBwcm9kdWN0cyB0byBiZSBwdXJjaGFzZWQuCgpDLiBUaGUgaW50ZXJjZXB0IGNvZWZmaWNpZW50IGlzIGdyZWF0ZXIgdGhhbiB6ZXJvLCBzbyB3aGVuIGNsaWNrcyBlcXVhbHMgemVybyB3ZSBleHBlY3QgdG90YWxfY29udmVydHMgdG8gYmUgc29tZXdoZW4gYmV0d2VlbiAwLjkwIGFuZCAxLjI1LiBFdmVuIHRob3VnaCBhIHVzZXIgZG9lcyBub3QgY2xpY2sgb24gYW4gYWQsIHRoZXkgaGF2ZSBiZWVuIGV4cG9zZWQgdG8gdGhlIHByb2R1Y3QgYnJhbmQgYW5kIG1pZ2h0IHB1cmNoYXNlIHRoZSBwcm9kdWN0IGJ5IGxhdGVyIHNlYXJjaGluZyBmb3IgaXQgdGhyb3VnaCBhIHNlYXJjaCBlbmdpbmUuCgpELiBUaGUgVGhlIGludGVyY2VwdCBjb2VmZmljaWVudCBpcyBjbG9zZSB0byB6ZXJvOyB0aGlzIG1lYW5zIHRoYXQgd2hlbiB3aGVuIGNsaWNrcyBlcXVhbCB6ZXJvLCB0aGUgbnVtYmVyIG9mIHRvdGFsX2NvbnZlcnRzIGlzIGxpa2VseSB0byBiZSBhcm91bmQgemVyby4gVGhpcyBpcyByZWFzb25hYmxlIGJlY2F1c2UgaWYgYSB1c2VyIGhhcyBub3QgY2xpY2tlZCBvbiBhbiBhZCwgd2UgZG8gbm90IGV4cGVjdCB0aGVtIHRvIHB1cmNoYXNlIHRoZSByZWxhdGVkIHByb2R1Y3QuCmBgYHtyfQppbnRlcmNlcHRfY29lZmZpY2llbnQgPC0gImMiCmBgYAojIyBNYWtpbmcgUHJlZGljdGlvbnMKCkRhdGEgU2NpZW50aXN0cyBhcmUgb2Z0ZW4gaW50ZXJlc3RlZCBpbiBidWlsZGluZyBtb2RlbHMgdG8gbWFrZSBwcmVkaWN0aW9ucyBvbiBuZXcgZGF0YS4gV2hpbGUgdGhlIGFkZF9wcmVkaWN0aW9ucygpIGZ1bmN0aW9uIGZyb20gdGhlIG1vZGVsciBwYWNrYWdlIG1ha2VzIGl0IGVhc3kgdG8gcHJlZGljdCBuZXcgdmFsdWVzIGZyb20gYSB0ZWNobmljYWwgc3RhbmRwb2ludCwgaXQgaXMgZmFyIG1vcmUgZGlmZmljdWx0IHRvIGRldmVsb3AgYW5kIGFzc2VzcyBhY2N1cmF0ZSBwcmVkaWN0ZWQgdmFsdWVzLgoKVGhlIG1vc3QgY29tbW9uIG1ldHJpYyB1c2VkIHRvIGNvbXB1dGUgdGhlIGFjY3VyYWN5IG9mIHByZWRpY3RlZCB2YWx1ZXMgaXMgbWVhbiBzcXVhcmVkIGVycm9yIG9uIHRlc3QgZGF0YS4gU2ltaWxhciB0byByZXNpZHVhbCBzcXVhcmVkIGVycm9yIChSU0UpIGFuZCBSLXNxdWFyZWQsIE1TRSBtZWFzdXJlcyB0aGUgYXZlcmFnZSBzcXVhcmVkIGRpZmZlcmVuY2UgYmV0d2VlbiBwcmVkaWN0ZWQgYW5kIG9ic2VydmVkIHZhbHVlcy4gV2hlbiB3ZSBhcmUgd29ya2luZyB3aXRoIGp1c3Qgb25lIG1vZGVsLCBpdCBpcyBoZWxwZnVsIHRvIGNvbXBhcmUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBNU0Ugb24gb3VyIHRyYWluaW5nIGRhdGFzZXQsIGFuZCBNU0Ugb24gdGVzdCBkYXRhLiBXZSBjYW4gY2FsY3VsYXRlIHRyYWluaW5nIE1TRSBmb3IgbW9kZWwgdXNpbmcgYSBjb21iaW5hdGlvbiBvZiBhZGRfcHJlZGljdGlvbnMoKSBhbmQgc3VtbWFyaXNlKCkuIGFkZF9wcmVkaWN0aW9ucygpIGNyZWF0ZXMgYW5kIGFkZHMgcHJlZGljdGVkIHZhbHVlcyBmcm9tIGEgbW9kZWwgdG8gYSBjb2x1bW4gY2FsbGVkIHByZWQuIHN1bW1hcmlzZSgpIHRoZW4gYWxsb3dzIHVzIHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiBvZiB0aGUgc3F1YXJlZCBkaWZmZXJlbmNlIGJldHdlZW4gb3VyIG9ic2VydmVkIHZhbHVlcyAoc2FsZXMpIGFuZCBwcmVkaWN0ZWQgdmFsdWVzIChwcmVkKS4KCiAgICB0cmFpbiAlPiUgCiAgICAgIGFkZF9wcmVkaWN0aW9ucyhtb2RlbCkgJT4lCiAgICAgIHN1bW1hcmlzZShNU0UgPSBtZWFuKChzYWxlcyAtIHByZWQpXjIpKQogICAgCiAgICAjb3V0cHV0CiAgICAKICAgICAgICAgICBNU0UKICAgICAgMzEuNjA3MTMKICAKV2UgY2FuIHVzZSB0aGUgc2FtZSBjb21iaW5hdGlvbiBvZiBmdW5jdGlvbnMgdG8gY2FsY3VsYXRlIE1TRSBmb3Igb3VyIHRlc3QgZGF0YXNldCwgd2hpY2ggcmVzdWx0cyBpbiBhIE1TRSBvZiBhcm91bmQgMzIuNS4gVGVzdGluZyBNU0Ugd2lsbCBhbG1vc3QgYWx3YXlzIGJlIGhpZ2hlciB0aGFuIHRyYWluaW5nIE1TRSwgYXMgdGhlIG1vZGVsIGhhcyBiZWVuIGJ1aWx0IG9mZiBvZiB0cmFpbmluZyBkYXRhOyBob3dldmVyLCBpdCBpcyBpbXBvcnRhbnQgdG8gY29uZmlybSB0aGF0IHRoZXJlIGlzIG5vdCBhIHN1YnN0YW50aWFsIGRpZmZlcmVuY2UgYmV0d2VlbiBtb2RlbCB0cmFpbmluZyBhbmQgdGVzdCBNU0UuIFRoZSB2YWx1ZSBvZiB1c2luZyBNU0UgdG8gcXVhbnRpZnkgcHJlZGljdGlvbiBhY2N1cmFjeSBpcyBtb3JlIGNsZWFyIHdoZW4gY29tcGFyaW5nIG11bHRpcGxlIG1vZGVscywgYXMgaXQgYWxsb3dzIHVzIHRvIGRldGVybWluZSB3aGljaCB2ZXJzaW9ucyBvZiBhIG1vZGVsIGJlc3QgcHJlZGljdHMgYW4gb3V0Y29tZSB2YXJpYWJsZS4gRm9yIGluc3RhbmNlLCB3ZSBjb3VsZCBjb21wdXRlIHRoZSBNU0UgZm9yIGEgbW9kZWwgb2YgdHYgc3BlbmRpbmcgb24gc2FsZXMuCgogICAgbW9kZWwyIDwtIGxtKHNhbGVzIH4gdHYsIGRhdGEgPSB0cmFpbikKICAgIAogICAgdHJhaW4gJT4lIAogICAgICBhZGRfcHJlZGljdGlvbnMobW9kZWwyKSAlPiUKICAgICAgc3VtbWFyaXNlKE1TRSA9IG1lYW4oKHNhbGVzIC0gcHJlZCleMikpCiAgICAKICAgICNvdXRwdXQKICAgICAgICBNU0UKICAgICAgICAyNy4yODQxNQogIApDb21wYXJpbmcgdGhlIHRyYWluIE1TRSBmb3Igb3VyIHR2LWJhc2VkIG1vZGVsLCBhdCAyNy4yOCwgdG8gb3VyIHRyYWluIE1TRSBmb3IgYSBwb2RjYXN0LWJhc2VkIG1vZGVsLCBhdCAzMS42MCwgaXQgaXMgY2xlYXIgdGhhdCB0aGUgcHJlZGljdGlvbnMgZnJvbSB0aGUgdHYtYmFzZWQgbW9kZWwgYXJlIG1vcmUgYWNjdXJhdGUsIGFzIHRoZSBtb2RlbOKAmXMgTVNFIGlzIGxvd2VyLiBJZiBhIGRhdGEgc2NpZW50aXN0IHdhcyB0cnlpbmcgdG8gcHJlZGljdCB0aGUgZXhwZWN0ZWQgdm9sdW1lIG9mIHNhbGVzIGZvciBhIGZ1dHVyZSBidXNpbmVzcyBxdWFydGVyLCBpdCB3b3VsZCBiZSBhIGJldHRlciBpZGVhIGZvciB0aGVtIHRvIGJhc2UgdGhlaXIgZXN0aW1hdGlvbnMgb2ZmIG9mIGEgdHYtYmFzZWQgbW9kZWwuCgpVc2UgYWRkX3ByZWRpY3Rpb25zKCkgYW5kIHN1bW1hcml6ZSgpIHRvIGNhbGN1bGF0ZSB0aGUgdGVzdCBtZWFuIHNxdWFyZWQgZXJyb3Igb2YgbW9kZWwgKE1TRSksIGFuZCBzYXZlIHRoZSByZXN1bHQgdG8gYSB2YXJpYWJsZSBjYWxsZWQgbXNlX2NsaWNrcy4KCk1ha2Ugc3VyZSB0byB1c2UgdGhlIHRlc3QgZGF0YXNldC4KCmBgYHtyfQpsaWJyYXJ5KG1vZGVscikKCm1zZV9jbGlja3MgPC0gdGVzdCAlPiUKICBhZGRfcHJlZGljdGlvbnMobW9kZWwpICU+JQogIHN1bW1hcmlzZShNU0UgPSBtZWFuKCh0b3RhbF9jb252ZXJ0IC0gcHJlZCleMikpCm1zZV9jbGlja3MKYGBgClByaW50IG91dCBib3RoIG1zZV9jbGlja3MgYW5kIG1zZV9pbXByZXNzaW9ucy4gV2hpY2ggb25lIGlzIHNtYWxsZXI/IFdoYXQgZG9lcyB0aGlzIHRlbGwgdXMgYWJvdXQgdGhlIGFjY3VyYWN5IG9mIG91ciBtb2RlbHMgb24gdGhlIHRlc3QgZGF0YT8KYGBge3J9Cm1zZV9pbXByZXNzaW9ucyA8LSB0ZXN0ICU+JQogIGFkZF9wcmVkaWN0aW9ucyhtb2RlbF8yKSAlPiUKICBzdW1tYXJpc2UoTVNFID0gbWVhbigodG90YWxfY29udmVydCAtIHByZWQpXjIpKQptc2VfaW1wcmVzc2lvbnMKYGBgCkxldOKAmXMgcGxvdCB0aGUgcHJlZGljdGVkIHRlc3QgdmFsdWVzIG9mIHRoZSBtb2RlbCB3aXRoIHRoZSBzbWFsbGVzdCBNU0UgYWdhaW5zdCB0aGUgb2JzZXJ2ZWQgdGVzdCBkYXRhLgoKRmlyc3QsIHVzZSBhIGNvbWJpbmF0aW9uIG9mIGFkZF9wcmVkaWN0aW9ucygpLCBnZ3Bsb3QoKSBhbmQgZ2VvbV9wb2ludCgpdG8gcGxvdCB0aGUgb2JzZXJ2ZWQgdmFsdWVzIGZyb20gdGhlIHRlc3QgZGF0YS4KCldoZW4gY3JlYXRpbmcgdGhlIGdncGxvdCgpIGFkZCBhbiBhZXMoKSB3aGVyZSB0aGUgeCBhbmQgeSB2YWx1ZXMgY29ycmVzcG9uZCB0byB0aGUgdmFyaWFibGVzIHVzZWQgd2hlbiBtYWtpbmcgdGhlIGNvcnJlY3QgbW9kZWwuCgpTYXZlIHRoZSB2aXN1YWxpemF0aW9uIHRvIGEgdmFyaWFibGUgY2FsbGVkIHBsb3QsIHRoZW4gcHJpbnQgb3V0IHRoZSB2YXJpYWJsZS4KYGBge3J9CnBsb3QgPC0gdGVzdCAlPiUKICAgIGFkZF9wcmVkaWN0aW9ucyhtb2RlbF8yKSAlPiUKICAgIGdncGxvdChhZXMoaW1wcmVzc2lvbnMsIHRvdGFsX2NvbnZlcnQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9wb2ludChhZXMoeSA9IHByZWQpLCBjb2xvciA9ICJibHVlIikKcGxvdApgYGAKIyBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbgoKIVtsaW5lYXJ2c211bHRpcGxlXShsaW5lYXJtdWx0aXBsZS5wbmcpCgpXZeKAmXZlIGJlZW4gYWJsZSB0byByZWFsbHkgZGlnIGludG8gdGhlIHJlc3VsdHMgb2Ygc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyBhbmQgc2hvdyBob3cgdGhlIHJlc3VsdHMgY29udmV5IGEgc3Vic3RhbnRpYWwgYW1vdW50IG9mIGluZm9ybWF0aW9uIGFib3V0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gdmFyaWFibGVzLiBIb3dldmVyLCBieSB0aGlzIHBvaW50IHlvdSBtaWdodCBiZSB3b25kZXJpbmfigJPigJMgd2hhdCBpZiBJIHRoaW5rIHZhcmlhYmxlcyBvdGhlciB0aGFuIHBvZGNhc3QgaGF2ZSBjb250cmlidXRlIHRvIHRoZSB0b3RhbCBzYWxlcyBvZiBhIHByb2R1Y3Q/IFlvdSBtaWdodCByZW1lbWJlciB0aGF0IHRoZSBwcmltYXJ5IGFzc3VtcHRpb24gYmVoaW5kIHNpbXBsZSBsaW5lYXIgbW9kZWxzIGlzIHRoYXQgdGhlIGV4cGVjdGVkIHZhbHVlIG9mIHRoZSBvdXRjb21lIHZhcmlhYmxlIGlzIGEgc3RyYWlnaHQtbGluZSBmdW5jdGlvbiBvZiBleGNsdXNpdmVseSB0aGUgcHJlZGljdG9yIHZhcmlhYmxlLiBUaGlzIG1lYW5zIHRoYXQgb3VyIHNpbXBsZSBsaW5lYXIgbW9kZWxzIGFzc3VtZSB0aGF0IGFsbCB2YXJpYXRpb24gaW4gdGhlIG91dGNvbWUgdmFyaWFibGUgaXMgZXhwbGFpbmVkIGJ5IHRoZSBwcmVkaWN0b3IgdmFyaWFibGUuIEluIHRoZSBjYXNlIG9mIG91ciBzYWxlcyBkYXRhc2V0LCB3ZSBrbm93IHRoaXMgaXMgYWxtb3N0IGNlcnRhaW5seSBub3QgdHJ1ZTsgb2Z0ZW50aW1lcyBtb3JlIG1vbmV5IGlzIHNwZW50IG9uIFRWIG9yIG5ld3NwYXBlciBhZHMgdGhhbiBvbiBwb2RjYXN0cywgc28gdGhpcyBzcGVuZGluZyBtaWdodCBoYXZlIGFuIGV2ZW4gbGFyZ2VyIGVmZmVjdCB0aGFuIHBvZGNhc3Qgc3BlbmQuCgpUaGFua2Z1bGx5LCB0aGVyZSBhcmUgbWV0aG9kcyB0byBpbmNsdWRlIHRoZSBlZmZlY3RzIG9mIFRWIGFuZCBuZXdzcGFwZXIgaW4gbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzLiBXZSBjYW4gZXhwYW5kIG91ciBtb2RlbCBkZWZpbml0aW9uIGZyb20gYSBzaW1wbGUgbW9kZWwgb2Ygb25lIHByZWRpY3RvciB2YXJpYWJsZSB0byBhIG11bHRpcGxlIG1vZGVsIG9mLCB5b3UgZ3Vlc3NlZCBpdCwgbXVsdGlwbGUgcHJlZGljdG9yIHZhcmlhYmxlcy4gVGhlIGZvcm1hbCBkZWZpbml0aW9uIG9mIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyBpcyBhIGRpcmVjdCBleHRlbnNpb24gb2YgdGhlIGZvcm11bGEgZm9yIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbjoKCiQgWT0gYmV0YV8wICsgYmV0YV8x4oiXWCArIGJldGFfMuKIl1grZXJyb3IkCgpBcyBpbiBhIHNpbXBsZSBsaW5lYXIgbW9kZWwsIFkgcmVwcmVzZW50cyB0aGUgZG9sbGFyIHZhbHVlIG9mIHByb2R1Y3RzIHNvbGQsIFggcmVwcmVzZW50cyB0aGUgYW1vdW50IHNwZW50IG9uIHJlc3BlY3RpdmUgcHJvZHVjdCBwb2RjYXN0IGFkcywgYW5kIEJldGFfMCBpcyB0aGUgbW9kZWwgaW50ZXJjZXB0LiBOb3csIEJldGFfMSwgQmV0YV8yLCBhbmQgQmV0YV8zIHJlcHJlc2VudCBlYWNoIHRoZSBjb2VmZmljaWVudHMgb2YgcHJlZGljdG9yIHZhcmlhYmxlcy4gVG8gYnVpbGQgYSBzaW1pbGFyIG1vZGVsIGluIFIsIHVzaW5nIHRoZSBzdGFuZGFyZCBsbSgpIHBhY2thZ2UsIHdlIHN0aWxsIHVzZSB0aGUgZm9ybXVsYSBub3RhdGlvbiBvZiBZIH4gWDoKCiAgICBtb2RlbCA8LSBsbShzYWxlcyB+IHBvZGNhc3QgKyB0diwgZGF0YSA9IHRyYWluKQoKV2hpbGUgYnVpbGRpbmcgYSBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsIGlzIGEgc3RyYWlnaHRmb3J3YXJkIGV4dGVuc2lvbiBvZiB0aGUgY29kZSB1c2VkIHRvIGEgYnVpbGQgYSBzaW1wbGUgbW9kZWwgYW5kIHRoZSBvdXRwdXQgb2YgdGhlIG1vZGVsIHJlc3VsdHMgYmVsb3cgbG9va3MgcXVpdGUgc2ltaWxhciwgYSBiaXQgbW9yZSBlZmZvcnQgZ29lcyBpbnRvIHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgcmVzdWx0cyBvZiB0aGlzIG1vZGVsLiBSZW1lbWJlciB0aGF0IGluIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudCByZXByZXNlbnRzIHRoZSBleHBlY3RlZCBjaGFuZ2UgaW4gdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBmb3IgYSBvbmUtdW5pdCBpbmNyZWFzZSBpbiB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGUuIEluIG90aGVyIHdvcmRzLCB0aGUgY29lZmZpY2llbnQgZm9yIHBvZGNhc3QgcmVwcmVzZW50cyB0aGUgZXhwZWN0ZWQgaW5jcmVhc2UgaW4gc2FsZXMgZ2l2ZW4gYSBvbmUgZG9sbGFyIGluY3JlYXNlIHBvZGNhc3QgYWR2ZXJ0aXNlbWVudCBzcGVuZC4gQmVjYXVzZSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBpbmNsdWRlcyBtb3JlIHRoYW4gb25lIHByZWRpY3RvciB2YXJpYWJsZSwgdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlcyBtdXN0IGJlIGludGVycHJldGVkIGRpZmZlcmVudGx5LiBJbiBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiwgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgcmVwcmVzZW50cyB0aGUgZXhwZWN0ZWQgY2hhbmdlIGluIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgZm9yIGEgb25lLXVuaXQgaW5jcmVhc2UgaW4gdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlLCBob2xkaW5nIGFsbCBvdGhlciB2YXJpYWJsZXMgaW4gdGhlIG1vZGVsIGNvbnN0YW50LgoKICAgIHN1bW1hcnkobW9kZWwpCiAgICAKICAgICNvdXRwdXQKICAgIENhbGw6CiAgICBsbShmb3JtdWxhID0gc2FsZXMgfiBUViArIHBvZGNhc3QsIGRhdGEgPSB0cmFpbikKICAgIAogICAgQ29lZmZpY2llbnRzOgogICAgICAgICAgICAgICAgRXN0aW1hdGUgU3RkLiBFcnJvciB0IHZhbHVlIFByKD58dHwpICAgIAogICAgKEludGVyY2VwdCkgNC41ODMzODYgICAxLjAyNDYxNiAgIDQuNDczIDEuNjVlLTA1ICoqKgogICAgVFYgICAgICAgICAgMy4wMDYzNDAgICAxLjAwNDkyNCAgIDcuMzgwIDEuNjJlLTExICoqKgogICAgcG9kY2FzdCAgICAgMS4wNDkyNDkgICAxLjAyNzY2NSAgIDUuMzk1IDMuMTBlLTA3ICoqKgogICAgLS0tCiAgICBTaWduaWYuIGNvZGVzOiAgMCDigJgqKirigJkgMC4wMDEg4oCYKirigJkgMC4wMSDigJgq4oCZIDAuMDUg4oCYLuKAmSAwLjEg4oCYIOKAmSAxCgpGb3IgZXhhbXBsZSwgYSBjYWxsIHRvIHN1bW1hcnkobW9kZWwpIHNob3dzIHRoYXQgdGhlIGNvZWZmaWNpZW50IGZvciBwb2RjYXN0cyBpcyBlcXVhbCB0byAxLjA0OTQ0LiBUaGlzIG1lYW5zIHRoYXQsIHdoZW4gb25lIG1vcmUgZG9sbGFyIGlzIHNwZW50IG9uIHBvZGNhc3QgYWR2ZXJ0aXNpbmcsIGFib3V0IDEuMDQ5IG1vcmUgZG9sbGFycyBvZiB0aGUgcmVsYXRlZCBwcm9kdWN0IGlzIHNvbGQsIGdpdmVuIHRoYXQgdGhlcmUgaXMgbm8gaW5jcmVhc2UgaW4gdGhlIGFtb3VudCBvZiBtb25leSBzcGVudCBvbiB0diBhZHZlcnRpc2VtZW50cy4gSW4gdGhpcyB3YXksIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyBhbGxvdyB1cyB0byBpc29sYXRlIHRoZSB1bmlxdWUgZWZmZWN0IG9mIG9uZSBwcmVkaWN0b3IgdmFyaWFibGUgb24gdGhlIG91dGNvbWUgdmFyaWFibGUuCgpBcyB0aGlzIGV4YW1wbGUgc2hvd3MsIHRoZSBzZWxlY3Rpb24gb2YgdmFyaWFibGVzIGluIGEgcmVncmVzc2lvbiBtb2RlbCBjYW4gaGF2ZSB3aWRlLXJhbmdpbmcgaW1wYWN0cyBvbiB0aGUgcmVzdWx0cyBhbmQgaW50ZXJwcmV0YXRpb24gb2Ygb3VyIG1vZGVscyEgTGV04oCZcyBkaXZlIGludG8gb25lIG1vcmUgZXhlcmNpc2UgdG8gcHJhY3RpY2UgYnVpbGRpbmcgYW5kIGludGVycHJldGluZyBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMuCgojIyBBc3Nlc3NpbmcgTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24KClRpbWUgdG8gcHVsbCBpdCBhbGwgdG9nZXRoZXIhIFRoZSBpbnRlcnByZXRhdGlvbiBvZiBjb2VmZmljZW50cyBpbiBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBpcyBzbGlnaHRseSBkaWZmZXJlbnQgdGhhbiB0aGF0IG9mIGNvZWZmaWNlbnRzIGluIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbi4gQ29lZmZpY2VudCBvZiBpbmRlcGVuZGVudCBjb250aW51bm91cyB2YXJpYWJsZXMsIGxpa2UgcG9kY2FzdHMsIHJlcHJlc2VudHMgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIHByZWRpY3RlZCB2YWx1ZSBvZiBzYWxlcyBmb3IgZWFjaCBvbmUtZG9sbGFyIGluY3JlYXNlIGluIHBvZGNhc3RzLCBnaXZlbiB0aGF0IGFsbCBvdGhlciB2YXJpYWJsZXMgaW4gdGhlIG1vZGVsLCBpbmNsdWRpbmcgdHYsIGFyZSBoZWxkIGNvbnN0YW50LiBHaXZlbiB0aGUgb3V0cHV0IG9mIGNhbGxpbmcgc3VtbWFyeShtb2RlbCkgYmVsb3csIHdlIGNhbiBjb3JyZWN0bHkgc2F5IHRoYXQgZm9yIGV2ZXJ5IG9uZSBkb2xsYXIgaW5jcmVhc2UgaW4gcG9kY2FzdCBhZHZlcnRpc2VtZW50IHNwZW5kaW5nLCB3aGlsZSBob2xkaW5nIHRoZSBhbW91bnQgc3BlbnQgb24gdHYgYW5kIG5ld3NwYXBlciBjb25zdGFudCwgdGhlIHRvdGFsIHNhbGVzIG9mIHRoZSByZWxhdGVkIHByb2R1Y3QgaW5jcmVhc2VzIGJ5IDEuMDQ5IGRvbGxhcnMuCgogICAgc3VtbWFyeShtb2RlbCkKICAgIAogICAgI291dHB1dAogICAgQ2FsbDoKICAgIGxtKGZvcm11bGEgPSBzYWxlcyB+IFRWICsgcG9kY2FzdCArIG5ld3NwYXBlciwgZGF0YSA9IHRyYWluKQogICAgCiAgICBDb2VmZmljaWVudHM6CiAgICAgICAgICAgICAgICBFc3RpbWF0ZSBTdGQuIEVycm9yIHQgdmFsdWUgUHIoPnx0fCkgICAgCiAgICAoSW50ZXJjZXB0KSA0LjU4MzM4NiAgIDEuMDI0NjE2ICAgNC40NzMgMS42NWUtMDUgKioqCiAgICBUViAgICAgICAgICAzLjAwNjM0MCAgIDEuMDA0OTI0ICAgNy4zODAgMS42MmUtMTEgKioqCiAgICBwb2RjYXN0ICAgICAxLjA0OTI0OSAgIDEuMDI3NjY1ICAgNS4zOTUgMy4xMGUtMDcgKioqCiAgICBuZXdzcGFwZXIgICAxLjAwNjM0MCAgIDEuMDAyOTI0ICAgNi4zODAgMS4xMmUtMTEgKioqCiAgICAtLS0KICAgIFNpZ25pZi4gY29kZXM6ICAwIOKAmCoqKuKAmSAwLjAwMSDigJgqKuKAmSAwLjAxIOKAmCrigJkgMC4wNSDigJgu4oCZIDAuMSDigJgg4oCZIDEKICAgIApJbiBhZGRpdGlvbiwgdGhlIGludGVycHJldGF0aW9uIG9mIGJvb2xlYW4gY2F0ZWdvcmljYWwgdmFyaWFibGVzIGRpZmZlcnMgc2xpZ2h0bHkgZnJvbSB0aGF0IG9mIGNvbnRpbm91cyB2YXJpYWJsZXMuIFRoZSBjb2VmZmljZW50IHZhbHVlIGFzc29jaWF0ZWQgd2l0aCBhIGJvb2xlYW4gY2F0ZWdvcmljYWwgdmFyaWFibGUgcmVwcmVzZW50cyB0aGUgZWZmZWN0IG9mIGNoYW5naW5nIGZyb20gb25lIGNhdGVnb3J5IHRvIGFub3RoZXIuIGZvciBpbnN0YW5jZSwgdGhlIGNvZWZmaWNpZW50IHZhbHVlIG9mIDEuMDA2IGZvciBuZXdzcGFwZXIgdGVsbCB1cyB0aGF0IHJ1bm5pbmcgcHJpbnQgYWR2ZXJ0aXNlbWVudHMgcmVzdWx0cyBpbiBhIDEuMDA2IGRvbGxhciBpbmNyZWFzZSBpbiBzYWxlcywgaG9sZGluZyB0aGUgdmFsdWVzIG9mIFRWIGFuZCBwb2RjYXN0IGNvbnN0YW50LgoKRGF0YSBzY2llbnRpc3RzIG9mdGVuIGJ1aWxkIG1hbnkgdmFyaWF0aW9ucyBvZiBhIG1vZGVsIHdpdGggZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBpbmRlcGVuZGVudCB2YXJpYWJsZXMgYmVmb3JlIHVsdGltYXRlbHkgY29tbWl0aW5nIHRvIHRoZSBtb2RlbCB0aGF0IGJlc3QgZml0cyB0ZXN0IGRhdGEuIExldOKAmXMgcHJhY3RpY2UgYnVpbGRpbmcsIGludGVycHJldGluZywgYW5kIHNlbGVjdGluZyB0aGUgYmVzdCBmaXQgbXVsdGktbGluZWFyIG1vZGVsIGZvciBvdXIgY29udmVydF9jbGVhbiBkYXRhc2V0IQoKQnVpbGQgYSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCB3aGljaCByZWdyZXNzZXMgaW1wcmVzc2lvbnMsIGNsaWNrcywgYW5kIGdlbmRlciBvbiB0b3RhbF9jb252ZXJ0LCB1c2luZyBvdXIgdHJhaW4gZGF0YXNldC4gU2F2ZSB0aGUgcmVzdWx0IHRvIGEgdmFyaWFibGUgY2FsbGVkIG1vZGVsOyB0aGVuIGNhbGwgc3VtbWFyeShtb2RlbCkgdG8gdmlldyB0aGUgbW9kZWwgcmVzdWx0cy4KCmBgYHtyfQptb2RlbCA8LSBsbShmb3JtdWxhID0gdG90YWxfY29udmVydCB+IGltcHJlc3Npb25zICsgY2xpY2tzICsgZ2VuZGVyLCBkYXRhID0gdHJhaW4pCgpzdW1tYXJ5KG1vZGVsKQpgYGAKSG93IG1pZ2h0IHdlIGludGVycHJldCB0aGUgY29lZmZpY2llbnQgZXN0aW1hdGUgZm9yIGdlbmRlcj8gU2V0IHRoZSB2YXJpYWJsZSBnZW5kZXJfY29lZmZpY2llbnQgZXF1YWwgdG8gdGhlIHN0YXRlbWVudCB0aGF0IG1vc3QgY29ycmVjdGx5IGludGVycHJldHMgdGhlIGVzdGltYXRlIHZhbHVlIOKAlCBlaXRoZXIgImEiLCAiYiIsIG9yICJjIjoKCkEuIFRoZSBjb2VmZmljaWVudCBvZiB0aGUgZ2VuZGVyIHZhcmlhYmxlIGlzIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LCBzbyB3ZSBjYW5ub3QgY29tZSB0byBhbnkgc3Vic3RhbnRpdmUgY29uY2x1c2lvbiBmcm9tIGl0c+KAmSB2YWx1ZS4KCkIuIFRoZSBjb2VmZmljaWVudCBvZiB0aGUgZ2VuZGVyIHZhcmlhYmxlIGlzIG5lZ2F0aXZlLCB3aGljaCBtZWFucyB0aGF0IGFzIHRvdGFsX2NvbnZlcnQsIGNsaWNrcywgYW5kIGltcHJlc3Npb25zIGluY3JlYXNlcywgbWVuIGFyZSBsZXNzIGxpa2VseSB0byBwdXJjaGFzZSBhIGFkdmVydGlzZWQgcHJvZHVjdC4KCkMuIFRoZSBjb2VmZmljaWVudCBvZiB0aGUgZ2VuZGVyIHZhcmlhYmxlIGlzIG5lZ2F0aXZlLiBUaGlzIG1lYW5zIHRoYXQgYSBtZW4gYXJlIGxlc3MgbGlrZWx5IHRoYW4gd29tZW7igJPigJN3aXRoIHRoZSBzYW1lIHZhbHVlIG9mIGNsaWNrcyBhbmQgaW1wcmVzc2lvbnPigJPigJMgdG8gcHVyY2hhc2UgYW4gYWR2ZXJ0aXNlZCBwcm9kdWN0LgoKYGBge3J9CmdlbmRlcl9jb2VmZmljaWVudCA9ICJjIgpgYGAKTGV04oCZcyBidWlsZCBhIHNlY29uZCwgc2ltcGxlciBtb2RlbCBzbyB0aGF0IHdlIGNhbiBjb25maXJtIGFkZGluZyBnZW5kZXIgdG8gb3VyIG1vZGVsIGluY3JlYXNlcyBpdHPigJkgYWNjdXJhY3kuIEJ1aWxkIGEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgd2hpY2ggcmVncmVzc2VzIGltcHJlc3Npb25zLCBhbmQgY2xpY2tzIG9uIHRvdGFsX2NvbnZlcnQuIFNhdmUgdGhlIHJlc3VsdCB0byBhIHZhcmlhYmxlIGNhbGxlZCBtb2RlbDIKYGBge3J9Cm1vZGVsXzIgPC0gbG0oZm9ybXVsYSA9IHRvdGFsX2NvbnZlcnQgfiBpbXByZXNzaW9ucyArIGNsaWNrcywgZGF0YSA9IHRyYWluKQpgYGAKQ29tcHV0ZSB0aGUgUi1zcXVhcmVkIHZhbHVlIGZvciBtb2RlbCBhbmQgbW9kZWwyLCBhbmQgc2F2ZSB0aGUgcmVzdWx0cyB0byByc3FfbW9kZWwgYW5kIHJzcV9tb2RlbDIgcmVzcGVjdGl2ZWx5LiBDYWxsIGJvdGggdmFyaWFibGVzIHRvIHZpZXcgdGhlaXIgdmFsdWVzLgpgYGB7cn0KIyBjb21wdXRlIHItc3F1YXJlZCBiZWxvdwpyc3FfbW9kZWwgPC0gc3VtbWFyeShtb2RlbCkkci5zcXVhcmVkIApyc3FfbW9kZWwKcnNxX21vZGVsMiA8LSBzdW1tYXJ5KG1vZGVsXzIpJHIuc3F1YXJlZCAKcnNxX21vZGVsMgpgYGAKV2hpY2ggbW9kZWwgYmVzdCBmaXRzIG91ciBkYXRhPyBTZXQgdGhlIHZhcmlhYmxlIGJlc3RfZml0IGVxdWFsIHRvIHRoZSBsYXJnZXIgci1zcXVhcmVkIHZhbHVlLgpgYGB7cn0KIyBkZWZpbmUgYmVzdF9maXQgYmVsb3cKYmVzdF9maXQgPC0gcnNxX21vZGVsCmBgYAoKU2V0IHRoZSB2YXJpYWJsZSBnZW5kZXJfZGlmZiBlcXVhbCB0byB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHJzcV9tb2RlbCBhbmQgcnNxX21vZGVsMi4gCmBgYHtyfQojIGRlZmluZSBnZW5kZXJfZGlmZiBiZWxvdwpnZW5kZXJfZGlmZiA8LSByc3FfbW9kZWwgIC0gcnNxX21vZGVsMgpzcHJpbnRmKCJCYXNlZCBvbiB0aGUgcmVzdWx0cyBvZiBhIHNlcmllcyBvZiBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbnMgb24gdG90YWxfY29udmVydCwgd2UgZXN0aW1hdGUgdGhhdCB1c2VyIGdlbmRlciBhY2NvdW50cyBmb3IgYXBwcm94aW1hdGVseSAlcyBwZXJjZW50IG9mIHRoZSB2YXJpYXRpb24gaW4gcHJvZHVjdCBwdXJjaGFzZSByYXRlLiIsIGdlbmRlcl9kaWZmKQpgYGAKIyBSZXZpZXcKCldoZXcsIHRoYXTigJlzIGEgd3JhcCEgWW914oCZdmUgY292ZXJlZCBhIGxvdCBvZiBtYXRlcmlhbCByZWxhdGVkIHRvIGxpbmVhciByZWdyZXNzaW9uIGFuZCBpdHMgaW1wbGVtZW50YXRpb24gaW4gUi4gSGVyZSBhcmUgdGhlIG1haW4gY29uY2VwdHMgd2XigJl2ZSBjb3ZlcmVkOgoKLSBTdGF0aXN0aWNhbCBtb2RlbCBidWlsZGluZyBlbnRhaWxzIGZvdXIgbWFpbiBzdGVwczogMSkgY29uZmlybWluZyBkYXRhIGFzc3VtcHRpb25zLCAyKSBidWlsZGluZyBhIG1vZGVsIG9uIHRyYWluaW5nIGRhdGEsIDMpIGFzc2Vzc2luZyBtb2RlbCBmaXQsIGFuZCA0KSBhbmFseXppbmcgbW9kZWwgcmVzdWx0cy4KCi0gV2UgY2FuIHVzZSBhIGNvbWJpbmF0aW9uIG9mIHF1YWxpdGF0aXZlIG1ldGhvZHMsIHN1Y2ggYXMgYm94LXBsb3RzLCBhbmQgcXVhbnRpdGF0aXZlIG1ldGhvZHMsIGxpa2UgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50LCB0byBhc3Nlc3MgdGhhdCBkYXRhIG1lZXRzIG91ciBhc3N1bXB0aW9ucwoKLSBXZSB1c2UgdGhlIGxtKCkgbWV0aG9kIGFuZCBZIH4gWCBub3RhdGlvbiB0byBidWlsZCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLiBUaGUgWSB2YXJpYWJsZSBpcyByZWZlcnJlZCB0byBhcyB0aGUgb3V0Y29tZSB2YXJpYWJsZSBvZiB0aGUgbW9kZWwsIGFuZCBhbnkgWCB2YXJpYWJsZSBpcyByZWZlcnJlZCB0byBhcyB0aGUgcHJlZGljdG9yIHZhcmlhYmxlLgoKLSBXZSBjYW4gdXNlIGEgc2ltaWxhciBzZXQgb2YgcXVhbGl0YXRpdmUgYW5kIHF1YW50aXRhdGl2ZSBtZXRob2RzIHRvIGV2YWx1YXRlIHRoZSBmaXQgb2Ygb3VyIG1vZGVsLCBpbmNsdWRpbmcgYSBjb21wYXJpc29uIG9mIHRoZSBwbG90dGVkIG1vZGVsIHRvIGEgTE9FU1Mgc21vb3RoZXIgYW5kIHN0YXRpc3RpY3MgbGlrZSBtZWFuIHNxdWFyZWQgZXJyb3IgKE1TRSkgYW5kIFItc3F1YXJlZC4KCi0gTVNFIGFuZCBSLXNxdWFyZWQgc3RhdGlzdGljcyBhcmUgc3VtbWFyaWVzIG9mIHRoZSBvdmVyYWxsIHZhbHVlIG9mIHRoZSBtb2RlbCByZXNpZHVhbHMuIEEgcmVzaWR1YWwgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdmFsdWUgb2YgYSBkYXRhIHBvaW50IHByZWRpY3RlZCBieSBhIG1vZGVsIGFuZCBpdHMgYWN0dWFsIG9ic2VydmVkIHZhbHVlLgoKLSBUaGUgcmVzdWx0cyBvZiBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIGluY2x1ZGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMuIFRoZXNlIGNvZWZmaWNpZW50cyByZXByZXNlbnQgdGhlIGVmZmVjdCB0aGVpciByZXNwZWN0aXZlIHByZWRpY3RvciB2YXJpYWJsZSBoYXMgb24gdGhlIG1vZGVs4oCZcyBvdXRjb21lIHZhcmlhYmxlLgoKLSBJbiBhIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiwgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgcmVwcmVzZW50cyB0aGUgZWZmZWN0IG9mIGEgb25lLXVuaXQgaW5jcmVhc2UgaW4gdGhlIHByZWRpY3RvciB2YXJpYWJsZS4KClQtIGhlIGludGVyY2VwdCBjb2VmZmljaWVudCByZXByZXNlbnRzIHRoZSB2YWx1ZSBvZiB0aGUgb3V0Y29tZSB2YXJpYWJsZSBnaXZlbiB0aGF0IHRoZSBwcmVkaWN0b3IgdmFyaWFibGUgaXMgZXF1YWwgdG8gemVybzsgdGhpcyBjb2VmZmljaWVudCBpc27igJl0IGFsd2F5cyBtZWFuaW5nZnVsIGFuZCBkZXBlbmRzIG9uIHRoZSBzaXR1YXRpb24gYmVpbmcgbW9kZWxlZC4KCi0gVGhlIHAtdmFsdWUgYXNzb2NpYXRlZCB3aXRoIGEgcmVncmVzc2lvbiBjb2VmZmljaWVudCBoZWxwcyB1cyB1bmRlcnN0YW5kIHdoZXRoZXIgdGhlIGVmZmVjdCBvZiBhIHZhcmlhYmxlIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuCgotIE11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIGlzIHNpbWlsYXIgdG8gc2ltcGxlIHJlZ3Jlc3Npb24sIGV4Y2VwdCB0aGF0IGl0IGluY2x1ZGVzIG11bHRpcGxlIHByZWRpY3RvciB2YXJpYWJsZXMuCgotIEluIGEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24sIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50IHJlcHJlc2VudHMgdGhlIGVmZmVjdCBvZiBhIG9uZS11bml0IGluY3JlYXNlIGluIHRoZSByZXNwZWN0aXZlIHByZWRpY3RvciB2YXJpYWJsZSwgZ2l2ZW4gdGhhdCBhbGwgb3RoZXIgcHJlZGljdG9yIHZhcmlhYmxlcyBhcmUgaGVsZCBjb25zdGFudC4KCi0gSW4gYm90aCBzaW1wbGUgYW5kIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uLCBib29sZWFuIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyByZXByZXNlbnQgdGhlIHRvdGFsIGVmZmVjdCBvZiBzd2l0Y2hpbmcgZnJvbSBvbmUgY2F0ZWdvcnkgdG8gYW5vdGhlci4=