knitr::opts_chunk$set(echo = TRUE)
knitr::opts_chunk$set(eval = TRUE)
# Remark: If need Math font, 'Save with encoding as UTF' which makes the R Markdown compile

R Markdown

This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.

When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:

Chapter 1: What is Regression?

In this chapter we introduce the concept of regression from a machine learning point of view. We will present the fundamental regression method: linear regression. We will show how to fit a linear regression model and to make predictions from the model.

1.1: Identify the regression tasks

From a machine learning perspective, the term regression generally encompasses the prediction of continuous values. Statistically, these predictions are the expected value, or the average value one would observe for the given input values.

Which of the following tasks are regression problems?

Answer the question

50 XP

Possible Answers

  1. Predict (yes/no) whether a student will pass the final exam, given scores on midterms and homework.

  2. Predict the student’s score (0 - 100) on the final exam, given scores on midterms and homework. [ans]

  3. Predict the student’s final grade (A, B, C, D, F) in the class given scores on midterms and homework (before they’ve taken the final exam).

1.2: Code a simple one-variable regression

For the first coding exercise, you’ll create a formula to define a one-variable modeling task, and then fit a linear model to the data. You are given the rates of male and female unemployment in the United States over several years (Source).

The task is to predict the rate of female unemployment from the observed rate of male unemployment. The outcome is female_unemployment, and the input is male_unemployment.

The sign of the variable coefficient tells you whether the outcome increases (+) or decreases (-) as the variable increases.

Recall the calling interface for lm() is:

lm(formula, data = ___)

Instructions

100 XP

  • The data frame unemployment is in your workspace.

  • Define a formula that expresses female_unemployment as a function of male_unemployment. Assign the formula to the variable fmla and print it.

  • Then use lm() and fmla to fit a linear model to predict female unemployment from male unemployment using the data set unemployment.

  • Print the model. Is the coefficent for male unemployment consistent with what you would expect? Does female unemployment increase as male unemployment does?

# unemployment is loaded in the workspace
unemployment<-readRDS("unemployment.rds")
summary(unemployment)
 male_unemployment female_unemployment
 Min.   :2.900     Min.   :4.000      
 1st Qu.:4.900     1st Qu.:4.400      
 Median :6.000     Median :5.200      
 Mean   :5.954     Mean   :5.569      
 3rd Qu.:6.700     3rd Qu.:6.100      
 Max.   :9.800     Max.   :7.900      
# Define a formula to express female_unemployment as a function of male_unemployment
fmla <- female_unemployment ~ male_unemployment
# Print it
fmla
female_unemployment ~ male_unemployment
# Use the formula to fit a model: unemployment_model
unemployment_model <- lm(fmla, data = unemployment)
# Print it
unemployment_model

Call:
lm(formula = fmla, data = unemployment)

Coefficients:
      (Intercept)  male_unemployment  
           1.4341             0.6945  

1.3: Examining a model

Let’s look at the model unemployment_model that you have just created. There are a variety of different ways to examine a model; each way provides different information. We will use summary(), broom::glance(), and sigr::wrapFTest().

Instructions

100 XP

  • The object unemployment_model is in your workspace.

  • Print unemployment_model again. What information does it report?

  • Call summary() on unemployment_model. In addition to the coefficient values, you get standard errors on the coefficient estimates, and some goodness-of-fit metrics like R-squared.

  • Call glance() on the model to see the performance metrics in an orderly data frame. Can you match the information from summary() to the columns of glance()?

  • Now call wrapFTest() on the model to see the R-squared again.

# broom and sigr are already loaded in your workspace
# Print unemployment_model
install.packages("nlme")
Warning in install.packages :
  unable to access index for repository http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5:
  cannot open URL 'http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5/PACKAGES'
trying URL 'https://mran.microsoft.com/snapshot/2018-08-01/bin/windows/contrib/3.5/nlme_3.1-137.zip'
Content type 'application/zip' length 2351049 bytes (2.2 MB)
downloaded 2.2 MB
package ‘nlme’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\cweiqiang\AppData\Local\Temp\RtmpueSsh9\downloaded_packages
install.packages("broom")
Warning in install.packages :
  unable to access index for repository http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5:
  cannot open URL 'http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5/PACKAGES'
trying URL 'https://mran.microsoft.com/snapshot/2018-08-01/bin/windows/contrib/3.5/broom_0.5.0.zip'
Content type 'application/zip' length 2009854 bytes (1.9 MB)
downloaded 1.9 MB
package ‘broom’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\cweiqiang\AppData\Local\Temp\RtmpueSsh9\downloaded_packages
install.packages("sigr")
Warning in install.packages :
  unable to access index for repository http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5:
  cannot open URL 'http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5/PACKAGES'
trying URL 'https://mran.microsoft.com/snapshot/2018-08-01/bin/windows/contrib/3.5/sigr_0.2.8.zip'
Content type 'application/zip' length 200307 bytes (195 KB)
downloaded 195 KB
package ‘sigr’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\cweiqiang\AppData\Local\Temp\RtmpueSsh9\downloaded_packages
library(nlme)
library(broom)
library(sigr)
unemployment_model

Call:
lm(formula = fmla, data = unemployment)

Coefficients:
      (Intercept)  male_unemployment  
           1.4341             0.6945  
# Call summary() on unemployment_model to get more details
summary(unemployment_model)

Call:
lm(formula = fmla, data = unemployment)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.77621 -0.34050 -0.09004  0.27911  1.31254 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        1.43411    0.60340   2.377   0.0367 *  
male_unemployment  0.69453    0.09767   7.111 1.97e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5803 on 11 degrees of freedom
Multiple R-squared:  0.8213,    Adjusted R-squared:  0.8051 
F-statistic: 50.56 on 1 and 11 DF,  p-value: 1.966e-05
# Call glance() on unemployment_model to see the details in a tidier form
glance(unemployment_model)
# Call wrapFTest() on unemployment_model to see the most relevant details
wrapFTest(unemployment_model)
[1] "F Test summary: (R2=0.82, F(1,11)=51, p=2e-05)."

Remark: here are several different ways to get diagnostics for your model. Use the one that suits your needs or preferences the best.

1.4: Predicting from the unemployment model

In this exercise, you will use your unemployment model unemployment_model to make predictions from the unemployment data, and compare predicted female unemployment rates to the actual observed female unemployment rates on the training data, unemployment. You will also use your model to predict on the new data in newrates, which consists of only one observation, where male unemployment is 5%.

The predict() interface for lm models takes the form

predict(model, newdata)

You will use the ggplot2 package to make the plots, so you will add the prediction column to the unemployment data frame. You will plot outcome versus prediction, and compare them to the line that represents perfect predictions (that is when the outcome is equal to the predicted value).

The ggplot2 command to plot a scatterplot of dframe\(outcome versus dframe\)pred (pred on the x axis, outcome on the y axis), along with a blue line where outcome == pred is as follows:

ggplot(dframe, aes(x = pred, y = outcome)) + geom_point() +
geom_abline(color = “blue”)

Instructions

100 XP

  • The objects unemployment, unemployment_model and newrates are in your workspace.

  • Use predict() to predict female unemployment rates from the unemployment data. Assign it to a new column: prediction.

  • Use the library() command to load the ggplot2 package.

  • Use ggplot() to compare the predictions to actual unemployment rates. Put the predictions on the x axis. How close are the results to the line of perfect prediction?

  • Use the data frame newrates to predict expected female unemployment rate when male unemployment is 5%. Assign the answer to the variable pred and print it.

newrates<-read.csv("new_rates.csv")
# unemployment is in your workspace
summary(unemployment)
 male_unemployment female_unemployment
 Min.   :2.900     Min.   :4.000      
 1st Qu.:4.900     1st Qu.:4.400      
 Median :6.000     Median :5.200      
 Mean   :5.954     Mean   :5.569      
 3rd Qu.:6.700     3rd Qu.:6.100      
 Max.   :9.800     Max.   :7.900      
# newrates is in your workspace
newrates
# Predict female unemployment in the unemployment data set
unemployment$prediction <-  predict(unemployment_model)
# load the ggplot2 package
library(ggplot2)

Attaching package: 㤼㸱ggplot2㤼㸲

The following object is masked _by_ 㤼㸱.GlobalEnv㤼㸲:

    mpg
# Make a plot to compare predictions to actual (prediction on x axis)
ggplot(unemployment, aes(x = prediction, y = female_unemployment)) + 
  geom_point() +
  geom_abline(color = "blue")

# Predict female unemployment rate when male unemployment is 5%
pred <- predict(unemployment_model, newdata = newrates)
# Print it
pred
       1 
4.906757 

1.5: Multivariate linear regression (Part 1)

In this exercise, you will work with the blood pressure dataset (Source), and model blood_pressure as a function of weight and age.

Instructions

100 XP

  • The data frame bloodpressure is in the workspace.

  • Define a formula that expresses blood_pressure explicitly as a function of age and weight. Assign the formula to the variable fmla and print it.

  • Use fmla to fit a linear model to predict blood_pressure from age and weight in the data set bloodpressure. Call the model bloodpressure_model.

  • Print the model and call summary() on it. Does blood pressure increase or decrease with age? With weight?

# bloodpressure is in the workspace
bloodpressure<-readRDS("bloodpressure.rds")
summary(bloodpressure)
 blood_pressure       age            weight   
 Min.   :128.0   Min.   :46.00   Min.   :167  
 1st Qu.:140.0   1st Qu.:56.50   1st Qu.:186  
 Median :153.0   Median :64.00   Median :194  
 Mean   :150.1   Mean   :62.45   Mean   :195  
 3rd Qu.:160.5   3rd Qu.:69.50   3rd Qu.:209  
 Max.   :168.0   Max.   :74.00   Max.   :220  
# Create the formula and print it
fmla <- blood_pressure~age+weight
fmla
blood_pressure ~ age + weight
# Fit the model: bloodpressure_model
bloodpressure_model <- lm(fmla,data=bloodpressure)
# Print bloodpressure_model and call summary() 
bloodpressure_model

Call:
lm(formula = fmla, data = bloodpressure)

Coefficients:
(Intercept)          age       weight  
    30.9941       0.8614       0.3349  
summary(bloodpressure_model)

Call:
lm(formula = fmla, data = bloodpressure)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.4640 -1.1949 -0.4078  1.8511  2.6981 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)   
(Intercept)  30.9941    11.9438   2.595  0.03186 * 
age           0.8614     0.2482   3.470  0.00844 **
weight        0.3349     0.1307   2.563  0.03351 * 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2.318 on 8 degrees of freedom
Multiple R-squared:  0.9768,    Adjusted R-squared:  0.9711 
F-statistic: 168.8 on 2 and 8 DF,  p-value: 2.874e-07

1.6: Multivariate linear regression (Part 2)

Now you will make predictions using the blood pressure model bloodpressure_model that you fit in the previous exercise.

You will also compare the predictions to outcomes graphically. ggplot2 is already loaded in your workspace. Recall the plot command takes the form:

ggplot(dframe, aes(x = pred, y = outcome)) + geom_point() + geom_abline(color = “blue”) `

Instructions

100 XP

  • The objects bloodpressure and bloodpressure_model are in the workspace.

  • Use predict() to predict blood pressure in the bloodpressure dataset. Assign the predictions to the column prediction.

  • Graphically compare the predictions to actual blood pressures. Put predictions on the x axis. How close are the results to the line of perfect prediction?

# bloodpressure is in your workspace
summary(bloodpressure)
 blood_pressure       age            weight   
 Min.   :128.0   Min.   :46.00   Min.   :167  
 1st Qu.:140.0   1st Qu.:56.50   1st Qu.:186  
 Median :153.0   Median :64.00   Median :194  
 Mean   :150.1   Mean   :62.45   Mean   :195  
 3rd Qu.:160.5   3rd Qu.:69.50   3rd Qu.:209  
 Max.   :168.0   Max.   :74.00   Max.   :220  
# bloodpressure_model is in your workspace
bloodpressure_model

Call:
lm(formula = fmla, data = bloodpressure)

Coefficients:
(Intercept)          age       weight  
    30.9941       0.8614       0.3349  
# predict blood pressure using bloodpressure_model :prediction
bloodpressure$prediction <- predict(bloodpressure_model)
# plot the results
ggplot(bloodpressure, aes(x = prediction, y = blood_pressure))+ 
    geom_point() +
    geom_abline(color = "blue")

Chapter 2: Training and Evaluating Regression Models

Now that we have learned how to fit basic linear regression models, we will learn how to evaluate how well our models perform. We will review evaluating a model graphically, and look at two basic metrics for regression models. We will also learn how to train a model that will perform well in the wild, not just on training data. Although we will demonstrate these techniques using linear regression, all these concepts apply to models fit with any regression algorithm.

2.1: Graphically evaluate the unemployment model

In this exercise you will graphically evaluate the unemployment model, unemployment_model, that you fit to the unemployment data in the previous chapter. Recall that the model predicts female_unemployment from male_unemployment.

You will plot the model’s predictions against the actual female_unemployment; recall the command is of the form

ggplot(dframe, aes(x = pred, y = outcome)) + geom_point() +
geom_abline() Then you will calculate the residuals:

residuals <- actual outcome - predicted outcome and plot predictions against residuals. The residual graph will take a slightly different form: you compare the residuals to the horizonal line x=0 (using geom_hline()) rather than to the line x=y. The command will be provided.

Instructions

100 XP

  • The data frame unemployment and model unemployment_model are available in the workspace.

  • Use predict() to get the model predictions and add them to unemployment as the column predictions.

  • Plot predictions (on the x-axis) versus actual female unemployment rates. Are the predictions near the x=y line?

  • Calculate the residuals between the predictions and actual unemployment rates. Add these residuals to unemployment as the column residuals.

  • Fill in the blanks to plot predictions (on the x-axis) versus residuals (on the y-axis). This gives you a different view of the model’s predictions as compared to ground truth.

# unemployment is in the workspace
summary(unemployment)
 male_unemployment female_unemployment   prediction   
 Min.   :2.900     Min.   :4.000       Min.   :3.448  
 1st Qu.:4.900     1st Qu.:4.400       1st Qu.:4.837  
 Median :6.000     Median :5.200       Median :5.601  
 Mean   :5.954     Mean   :5.569       Mean   :5.569  
 3rd Qu.:6.700     3rd Qu.:6.100       3rd Qu.:6.087  
 Max.   :9.800     Max.   :7.900       Max.   :8.240  
# unemployment_model is in the workspace
summary(unemployment_model)

Call:
lm(formula = fmla, data = unemployment)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.77621 -0.34050 -0.09004  0.27911  1.31254 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        1.43411    0.60340   2.377   0.0367 *  
male_unemployment  0.69453    0.09767   7.111 1.97e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5803 on 11 degrees of freedom
Multiple R-squared:  0.8213,    Adjusted R-squared:  0.8051 
F-statistic: 50.56 on 1 and 11 DF,  p-value: 1.966e-05
# Make predictions from the model
unemployment$predictions <- predict(unemployment_model)
# Fill in the blanks to plot predictions (on x-axis) versus the female_unemployment rates
ggplot(unemployment, aes(x = predictions, y = female_unemployment)) + 
  geom_point() + 
  geom_abline()

# Calculate residuals
unemployment$residuals <- unemployment$female_unemployment - unemployment$predictions
# Fill in the blanks to plot predictions (on x-axis) versus the residuals
ggplot(unemployment, aes(x = predictions, y = residuals)) + 
  geom_pointrange(aes(ymin = 0, ymax = residuals)) + 
  geom_hline(yintercept = 0, linetype = 3) + 
  ggtitle("residuals vs. linear model prediction")

2.2: The gain curve to evaluate the unemployment model

In the previous exercise you made predictions about female_unemployment and visualized the predictions and the residuals. Now, you will also plot the gain curve of the unemployment_model’s predictions against actual female_unemployment using the WVPlots::GainCurvePlot() function.

For situations where order is more important than exact values, the gain curve helps you check if the model’s predictions sort in the same order as the true outcome.

Calls to the function GainCurvePlot() look like:

GainCurvePlot(frame, xvar, truthvar, title)

where

  • frame is a data frame
  • xvar and truthvar are strings naming the prediction and actual outcome columns of frame
  • title is the title of the plot

When the predictions sort in exactly the same order, the relative Gini coefficient is 1. When the model sorts poorly, the relative Gini coefficient is close to zero, or even negative.

Instructions

100 XP

  • The data frame unemployment and the model unemployment_model are in the workspace.

  • Load the package WVPlots.

  • Plot the gain curve. Give the plot the title “Unemployment model”. Do the model’s predictions sort correctly?

# unemployment is in the workspace (with predictions)
summary(unemployment)
 male_unemployment female_unemployment   prediction     predictions      residuals       
 Min.   :2.900     Min.   :4.000       Min.   :3.448   Min.   :3.448   Min.   :-0.77621  
 1st Qu.:4.900     1st Qu.:4.400       1st Qu.:4.837   1st Qu.:4.837   1st Qu.:-0.34050  
 Median :6.000     Median :5.200       Median :5.601   Median :5.601   Median :-0.09004  
 Mean   :5.954     Mean   :5.569       Mean   :5.569   Mean   :5.569   Mean   : 0.00000  
 3rd Qu.:6.700     3rd Qu.:6.100       3rd Qu.:6.087   3rd Qu.:6.087   3rd Qu.: 0.27911  
 Max.   :9.800     Max.   :7.900       Max.   :8.240   Max.   :8.240   Max.   : 1.31254  
# unemployment_model is in the workspace
summary(unemployment_model)

Call:
lm(formula = fmla, data = unemployment)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.77621 -0.34050 -0.09004  0.27911  1.31254 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        1.43411    0.60340   2.377   0.0367 *  
male_unemployment  0.69453    0.09767   7.111 1.97e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5803 on 11 degrees of freedom
Multiple R-squared:  0.8213,    Adjusted R-squared:  0.8051 
F-statistic: 50.56 on 1 and 11 DF,  p-value: 1.966e-05
# Load the package WVPlots
install.packages("WVPlots")
Warning in install.packages :
  unable to access index for repository http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5:
  cannot open URL 'http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5/PACKAGES'
trying URL 'https://mran.microsoft.com/snapshot/2018-08-01/bin/windows/contrib/3.5/WVPlots_1.0.2.zip'
Content type 'application/zip' length 2193056 bytes (2.1 MB)
downloaded 2.1 MB
package ‘WVPlots’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\cweiqiang\AppData\Local\Temp\RtmpueSsh9\downloaded_packages
library(WVPlots)
# Plot the Gain Curve
GainCurvePlot(unemployment, "predictions", "female_unemployment", "Unemployment model")

2.3: Calculate RMSE

In this exercise you will calculate the RMSE of your unemployment model. In the previous coding exercises, you added two columns to the unemployment dataset:

the model’s predictions (predictions column) the residuals between the predictions and the outcome (residuals column) You can calculate the RMSE from a vector of residuals, res, as:

RMSE=sqrt{mean(res 2)}

You want RMSE to be small. How small is “small”? One heuristic is to compare the RMSE to the standard deviation of the outcome. With a good model, the RMSE should be smaller.

Instructions

100 XP

  • The data frame unemployment is in your workspace.

  • Review the unemployment data from the previous exercise.

  • For convenience, assign the residuals column from unemployment to the variable res.

  • Calculate RMSE: square res, take its mean, and then square root it. Assign this to the variable rmse and print it.

  • Tip: you can do this in one step by wrapping the assignment in parentheses: (rmse <- ___)

  • Calculate the standard deviation of female_unemployment and assign it to the variable sd_unemployment.

  • Print it. How does the rmse of the model compare to the standard deviation of the data?

# unemployment is in the workspace
summary(unemployment)
 male_unemployment female_unemployment   prediction     predictions      residuals       
 Min.   :2.900     Min.   :4.000       Min.   :3.448   Min.   :3.448   Min.   :-0.77621  
 1st Qu.:4.900     1st Qu.:4.400       1st Qu.:4.837   1st Qu.:4.837   1st Qu.:-0.34050  
 Median :6.000     Median :5.200       Median :5.601   Median :5.601   Median :-0.09004  
 Mean   :5.954     Mean   :5.569       Mean   :5.569   Mean   :5.569   Mean   : 0.00000  
 3rd Qu.:6.700     3rd Qu.:6.100       3rd Qu.:6.087   3rd Qu.:6.087   3rd Qu.: 0.27911  
 Max.   :9.800     Max.   :7.900       Max.   :8.240   Max.   :8.240   Max.   : 1.31254  
# For convenience put the residuals in the variable res
res <- unemployment$predictions-unemployment$female_unemployment
# Calculate RMSE, assign it to the variable rmse and print it
(rmse <- sqrt(mean(res^2)))
[1] 0.5337612
# Calculate the standard deviation of female_unemployment and print it
(sd_unemployment <- sd(unemployment$female_unemployment))
[1] 1.314271
sd_unemployment
[1] 1.314271

2.4: Calculate R-Squared

Now that you’ve calculated the RMSE of your model’s predictions, you will examine how well the model fits the data: that is, how much variance does it explain. You can do this using R2.

Suppose y is the true outcome, p is the prediction from the model, and res=y−p are the residuals of the predictions.

  • Then the total sum of squares tss (“total variance”) of the data is: \(tss=\sum (y−\bar{y})^{2}\) where \(\bar{y}\) is the mean value of y.

  • The residual sum of squared errors of the model, rss is: \(rss=\sum res^{2}\)

  • \(R^{2}\) (R-Squared), the “variance explained” by the model, is then: \(1−\frac{rss}{tss}\)

After you calculate \(R^{2}\) , you will compare what you computed with the \(R^{2}\) reported by glance(). glance() returns a one-row data frame; for a linear regression model, one of the columns returned is the R2 of the model on the training data.

The data frame unemployment is in your workspace, with the columns predictions and residuals that you calculated in a previous exercise.

Instructions

100 XP

  • The data frame unemployment and the model unemployment_model are in the workspace.

  • Calculate the mean female_unemployment and assign it to the variable fe_mean.

  • Calculate the total sum of squares and assign it to the variable tss.

  • Calculate the residual sum of squares and assign it to the variable rss.

  • Calculate R2. Is it a good fit (R2 near 1)?

  • Use glance() to get R2 from the model. Is it the same as what you calculated?

# unemployment is in your workspace
summary(unemployment)
 male_unemployment female_unemployment   prediction     predictions      residuals       
 Min.   :2.900     Min.   :4.000       Min.   :3.448   Min.   :3.448   Min.   :-0.77621  
 1st Qu.:4.900     1st Qu.:4.400       1st Qu.:4.837   1st Qu.:4.837   1st Qu.:-0.34050  
 Median :6.000     Median :5.200       Median :5.601   Median :5.601   Median :-0.09004  
 Mean   :5.954     Mean   :5.569       Mean   :5.569   Mean   :5.569   Mean   : 0.00000  
 3rd Qu.:6.700     3rd Qu.:6.100       3rd Qu.:6.087   3rd Qu.:6.087   3rd Qu.: 0.27911  
 Max.   :9.800     Max.   :7.900       Max.   :8.240   Max.   :8.240   Max.   : 1.31254  
# unemployment_model is in the workspace
summary(unemployment_model)

Call:
lm(formula = fmla, data = unemployment)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.77621 -0.34050 -0.09004  0.27911  1.31254 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        1.43411    0.60340   2.377   0.0367 *  
male_unemployment  0.69453    0.09767   7.111 1.97e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5803 on 11 degrees of freedom
Multiple R-squared:  0.8213,    Adjusted R-squared:  0.8051 
F-statistic: 50.56 on 1 and 11 DF,  p-value: 1.966e-05
# Calculate mean female_unemployment: fe_mean. Print it
(fe_mean <- mean(unemployment$female_unemployment))
[1] 5.569231
fe_mean
[1] 5.569231
# Calculate total sum of squares: tss. Print it
(tss <- sum((unemployment$female_unemployment - fe_mean)^2))
[1] 20.72769
# Calculate residual sum of squares: rss. Print it
(rss <- sum((unemployment$female_unemployment - unemployment$predictions)^2))
[1] 3.703714
# Calculate R-squared: rsq. Print it. Is it a good fit?
(rsq <- 1-rss/tss)
[1] 0.8213157
# Get R-squared from glance. Print it
(rsq_glance <- glance(unemployment_model)$r.squared)
[1] 0.8213157

2.5: Correlation and R-squared

The linear correlation of two variables, x and y, measures the strength of the linear relationship between them. When x and y are respectively:

the outcomes of a regression model that minimizes squared-error (like linear regression) and the true outcomes of the training data, then the square of the correlation is the same as R2. You will verify that in this exercise.

Instructions

100 XP

  • Use cor() to get the correlation between the predictions and female unemployment. Assign it to the variable rho and print it. Make sure you use Pearson correlation (the default).

  • Square rho and assign it to rho2. Print it.

  • Compare rho2 to R2 from the model (using glance()). Is it the same?

# unemployment is in your workspace
summary(unemployment)
 male_unemployment female_unemployment   prediction     predictions      residuals       
 Min.   :2.900     Min.   :4.000       Min.   :3.448   Min.   :3.448   Min.   :-0.77621  
 1st Qu.:4.900     1st Qu.:4.400       1st Qu.:4.837   1st Qu.:4.837   1st Qu.:-0.34050  
 Median :6.000     Median :5.200       Median :5.601   Median :5.601   Median :-0.09004  
 Mean   :5.954     Mean   :5.569       Mean   :5.569   Mean   :5.569   Mean   : 0.00000  
 3rd Qu.:6.700     3rd Qu.:6.100       3rd Qu.:6.087   3rd Qu.:6.087   3rd Qu.: 0.27911  
 Max.   :9.800     Max.   :7.900       Max.   :8.240   Max.   :8.240   Max.   : 1.31254  
# unemployment_model is in the workspace
summary(unemployment_model)

Call:
lm(formula = fmla, data = unemployment)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.77621 -0.34050 -0.09004  0.27911  1.31254 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)        1.43411    0.60340   2.377   0.0367 *  
male_unemployment  0.69453    0.09767   7.111 1.97e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5803 on 11 degrees of freedom
Multiple R-squared:  0.8213,    Adjusted R-squared:  0.8051 
F-statistic: 50.56 on 1 and 11 DF,  p-value: 1.966e-05
# Get the correlation between the prediction and true outcome: rho and print it
(rho <- cor(unemployment$predictions, unemployment$female_unemployment))
[1] 0.9062647
# Square rho: rho2 and print it
(rho2 <- rho^2)
[1] 0.8213157
# Get R-squared from glance and print it
(rsq_glance <- glance(unemployment_model)$r.squared)
[1] 0.8213157

2.6: Generating a random test/train split

For the next several exercises you will use the mpg data from the package ggplot2. The data describes the characteristics of several makes and models of cars from different years. The goal is to predict city fuel efficiency from highway fuel efficiency.

In this exercise, you will split mpg into a training set mpg_train (75% of the data) and a test set mpg_test (25% of the data). One way to do this is to generate a column of uniform random numbers between 0 and 1, using the function runif().

If you have a data set dframe of size N, and you want a random subset of approximately size 100∗X% of N (where X is between 0 and 1), then:

Generate a vector of uniform random numbers: gp = runif(N). dframe[gp < X,] will be about the right size. dframe[gp >= X,] will be the complement.

Instructions

100 XP

  • The data frame mpg is in the workspace.

  • Use the function nrow to get the number of rows in the data frame mpg. Assign this count to the variable N and print it.

  • Calculate about how many rows 75% of N should be. Assign it to the variable target and print it.

  • Use runif() to generate a vector of N uniform random numbers, called gp.

-Use gp to split mpg into mpg_train and mpg_test (with mpg_train containing approximately 75% of the data).

  • Use nrow() to check the size of mpg_train and mpg_test. Are they about the right size?
# mpg is in the workspace
summary(mpg)
 manufacturer          model               displ            year           cyl       
 Length:234         Length:234         Min.   :1.600   Min.   :1999   Min.   :4.000  
 Class :character   Class :character   1st Qu.:2.400   1st Qu.:1999   1st Qu.:4.000  
 Mode  :character   Mode  :character   Median :3.300   Median :2004   Median :6.000  
                                       Mean   :3.472   Mean   :2004   Mean   :5.889  
                                       3rd Qu.:4.600   3rd Qu.:2008   3rd Qu.:8.000  
                                       Max.   :7.000   Max.   :2008   Max.   :8.000  
    trans               drv                 cty             hwy             fl           
 Length:234         Length:234         Min.   : 9.00   Min.   :12.00   Length:234        
 Class :character   Class :character   1st Qu.:14.00   1st Qu.:18.00   Class :character  
 Mode  :character   Mode  :character   Median :17.00   Median :24.00   Mode  :character  
                                       Mean   :16.86   Mean   :23.44                     
                                       3rd Qu.:19.00   3rd Qu.:27.00                     
                                       Max.   :35.00   Max.   :44.00                     
    class              pred.cv            pred       
 Length:234         Min.   : 8.827   Min.   : 9.043  
 Class :character   1st Qu.:13.177   1st Qu.:13.142  
 Mode  :character   Median :17.313   Median :17.241  
                    Mean   :16.858   Mean   :16.859  
                    3rd Qu.:19.405   3rd Qu.:19.291  
                    Max.   :30.338   Max.   :30.906  
dim(mpg)
[1] 234  13
# Use nrow to get the number of rows in mpg (N) and print it
(N <- nrow(mpg))
[1] 234
# Calculate how many rows 75% of N should be and print it
# Hint: use round() to get an integer
(target <- round(0.75*N))
[1] 176
# Create the vector of N uniform random variables: gp
gp <- runif(N, min = 0, max = 1)
select.training<-gp<0.75
# Use gp to create the training set: mpg_train (75% of data) and mpg_test (25% of data)
mpg_train <- mpg[select.training,]
mpg_test <- mpg[!select.training,]
# Use nrow() to examine mpg_train and mpg_test
nrow(mpg_train)
[1] 179
nrow(mpg_test)
[1] 55

2.7: Train a model using test/train split

Now that you have split the mpg dataset into mpg_train and mpg_test, you will use mpg_train to train a model to predict city fuel efficiency (cty) from highway fuel efficiency (hwy).

Instructions

100 XP

  • The data frame mpg_train is in the workspace.

  • Create a formula fmla that expresses the relationship cty as a function of hwy. Print it.

  • Train a model mpg_model on mpg_train to predict cty from hwy using fmla and lm().

  • Use summary() to examine the model.

# mpg_train is in the workspace
summary(mpg_train)
 manufacturer          model               displ           year           cyl       
 Length:179         Length:179         Min.   :1.60   Min.   :1999   Min.   :4.000  
 Class :character   Class :character   1st Qu.:2.50   1st Qu.:1999   1st Qu.:4.000  
 Mode  :character   Mode  :character   Median :3.40   Median :2008   Median :6.000  
                                       Mean   :3.57   Mean   :2004   Mean   :6.006  
                                       3rd Qu.:4.70   3rd Qu.:2008   3rd Qu.:8.000  
                                       Max.   :7.00   Max.   :2008   Max.   :8.000  
    trans               drv                 cty             hwy            fl           
 Length:179         Length:179         Min.   : 9.00   Min.   :12.0   Length:179        
 Class :character   Class :character   1st Qu.:13.00   1st Qu.:17.0   Class :character  
 Mode  :character   Mode  :character   Median :16.00   Median :24.0   Mode  :character  
                                       Mean   :16.44   Mean   :22.9                     
                                       3rd Qu.:19.00   3rd Qu.:27.0                     
                                       Max.   :29.00   Max.   :41.0                     
    class              pred.cv            pred       
 Length:179         Min.   : 8.827   Min.   : 9.043  
 Class :character   1st Qu.:12.580   1st Qu.:12.459  
 Mode  :character   Median :17.289   Median :17.241  
                    Mean   :16.501   Mean   :16.490  
                    3rd Qu.:19.221   3rd Qu.:19.291  
                    Max.   :28.355   Max.   :28.856  
# Create a formula to express cty as a function of hwy: fmla and print it.
(fmla <- cty~hwy)
cty ~ hwy
# Now use lm() to build a model mpg_model from mpg_train that predicts cty from hwy 
mpg_model <- lm(fmla,data=mpg_train)
# Use summary() to examine the model
summary(mpg_model)

Call:
lm(formula = fmla, data = mpg_train)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.8605 -0.8068 -0.1043  0.6663  4.7345 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.96995    0.36128   2.685  0.00795 ** 
hwy          0.67562    0.01529  44.182  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.189 on 177 degrees of freedom
Multiple R-squared:  0.9169,    Adjusted R-squared:  0.9164 
F-statistic:  1952 on 1 and 177 DF,  p-value: < 2.2e-16

2.8: Evaluate a model using test/train split

Now you will test the model mpg_model on the test data, mpg_test. Functions rmse() and r_squared() to calculate RMSE and R-squared have been provided for convenience:

rmse(predcol, ycol)

r_squared(predcol, ycol)

where:

  • predcol: The predicted values

  • ycol: The actual outcome

You will also plot the predictions vs. the outcome.

Generally, model performance is better on the training data than the test data (though sometimes the test set “gets lucky”). A slight difference in performance is okay; if the performance on training is significantly better, there is a problem.

Instructions

100 XP

The data frames mpg_train and mpg_test, and the model mpg_model are in the workspace, along with the functions rmse() and r_squared().

  • Predict city fuel efficiency from hwy on the mpg_train data. Assign the predictions to the column pred.

  • Predict city fuel efficiency from hwy on the mpg_test data. Assign the predictions to the column pred.

  • Use rmse() to evaluate rmse for both the test and training sets. Compare. Are the performances similar?

  • Do the same with r_squared(). Are the performances similar?

  • Use ggplot2 to plot the predictions against cty on the test data.

# Examine the objects in the workspace
#ls.str()
# predict cty from hwy for the training set
mpg_train$pred <- predict(mpg_model)
# predict cty from hwy for the test set
mpg_test$pred <- predict(mpg_model,newdata=mpg_test)
#install.packages("Metrics")
library(Metrics)
# Evaluate the rmse on both training and test data and print them
(rmse_train <- rmse(mpg_train$cty,mpg_train$pred))
[1] 1.182395
(rmse_test <- rmse(mpg_test$cty,mpg_test$pred))
[1] 1.444506
# Evaluate the r-squared on both training and test data.and print them
#(rsq_train <- r_squared(mpg_train$pred,mpg_train$cty))
#(rsq_test <- r_squared(mpg_test$pred,mpg_test$cty))
(rsq_train <- 1-sse(mpg_train$pred,mpg_train$cty)/sse(mean(mpg_train$cty),mpg_train$cty))
[1] 0.9168634
(rsq_test <- 1-sse(mpg_test$pred,mpg_test$cty)/sse(mean(mpg_test$cty),mpg_test$cty))
[1] 0.8934799
# Plot the predictions (on the x-axis) against the outcome (cty) on the test data
ggplot(mpg_test, aes(x = pred, y = cty)) + 
  geom_point() + 
  geom_abline()

2.9: Create a cross validation plan

There are several ways to implement an n-fold cross validation plan. In this exercise you will create such a plan using vtreat::kWayCrossValidation(), and examine it.

kWayCrossValidation() creates a cross validation plan with the following call:

splitPlan <- kWayCrossValidation(nRows, nSplits, dframe, y) where nRows is the number of rows of data to be split, and nSplits is the desired number of cross-validation folds.

Strictly speaking, dframe and y aren’t used by kWayCrossValidation; they are there for compatibility with other vtreat data partitioning functions. You can set them both to NULL.

The resulting splitPlan is a list of nSplits elements; each element contains two vectors:

train: the indices of dframe that will form the training set app: the indices of dframe that will form the test (or application) set In this exercise you will create a 3-fold cross-validation plan for the data set mpg.

Instructions

100 XP

  • Load the package vtreat.

  • Get the number of rows in mpg and assign it to the variable nRows.

  • Call kWayCrossValidation to create a 3-fold cross validation plan and assign it to the variable splitPlan. You can set the last two arguments of the function to NULL.

  • Call str() to examine the structure of splitPlan.

# Load the package vtreat
install.packages("vtreat")
Error in install.packages : Updating loaded packages
library(vtreat)
# mpg is in the workspace
summary(mpg)
 manufacturer          model               displ            year           cyl       
 Length:234         Length:234         Min.   :1.600   Min.   :1999   Min.   :4.000  
 Class :character   Class :character   1st Qu.:2.400   1st Qu.:1999   1st Qu.:4.000  
 Mode  :character   Mode  :character   Median :3.300   Median :2004   Median :6.000  
                                       Mean   :3.472   Mean   :2004   Mean   :5.889  
                                       3rd Qu.:4.600   3rd Qu.:2008   3rd Qu.:8.000  
                                       Max.   :7.000   Max.   :2008   Max.   :8.000  
    trans               drv                 cty             hwy             fl           
 Length:234         Length:234         Min.   : 9.00   Min.   :12.00   Length:234        
 Class :character   Class :character   1st Qu.:14.00   1st Qu.:18.00   Class :character  
 Mode  :character   Mode  :character   Median :17.00   Median :24.00   Mode  :character  
                                       Mean   :16.86   Mean   :23.44                     
                                       3rd Qu.:19.00   3rd Qu.:27.00                     
                                       Max.   :35.00   Max.   :44.00                     
    class              pred.cv            pred       
 Length:234         Min.   : 8.827   Min.   : 9.043  
 Class :character   1st Qu.:13.177   1st Qu.:13.142  
 Mode  :character   Median :17.313   Median :17.241  
                    Mean   :16.858   Mean   :16.859  
                    3rd Qu.:19.405   3rd Qu.:19.291  
                    Max.   :30.338   Max.   :30.906  
# Get the number of rows in mpg
nRows <- nrow(mpg)
# Implement the 3-fold cross-fold plan with vtreat
splitPlan <- kWayCrossValidation(nRows, 3, NULL, NULL)
# Examine the split plan
str(splitPlan)
List of 3
 $ :List of 2
  ..$ train: int [1:156] 2 3 4 6 7 8 9 11 13 14 ...
  ..$ app  : int [1:78] 229 204 40 100 41 67 5 54 12 21 ...
 $ :List of 2
  ..$ train: int [1:156] 1 4 5 6 8 10 12 13 15 17 ...
  ..$ app  : int [1:78] 184 203 81 78 112 93 143 174 165 139 ...
 $ :List of 2
  ..$ train: int [1:156] 1 2 3 5 7 9 10 11 12 14 ...
  ..$ app  : int [1:78] 4 64 101 150 28 176 147 45 57 82 ...
 - attr(*, "splitmethod")= chr "kwaycross"

2.10: Evaluate a modeling procedure using n-fold cross-validation

In this exercise you will use splitPlan, the 3-fold cross validation plan from the previous exercise, to make predictions from a model that predicts mpg\(cty from mpg\)hwy.

If dframe is the training data, then one way to add a column of cross-validation predictions to the frame is as follows:

  • Initialize a column of the appropriate length

dframe$pred.cv <- 0

  • k is the number of folds

  • splitPlan is the cross validation plan

for(i in 1:k) { # Get the ith split split <- splitPlan[[i]]

# Build a model on the training data # from this split # (lm, in this case) model <- lm(fmla, data = dframe[split$train,])

# make predictions on the # application data from this split dframe\(pred.cv[split\)app] <- predict(model, newdata = dframe[split$app,]) }

Cross-validation predicts how well a model built from all the data will perform on new data. As with the test/train split, for a good modeling procedure, cross-validation performance and training performance should be close.

Instructions

100 XP

  • The data frame mpg, the cross validation plan splitPlan, and the function to calculate RMSE (rmse()) from one of the previous exercises is available in your workspace.

  • Run the 3-fold cross validation plan from splitPlan and put the predictions in the column mpg$pred.cv.

  • Use lm() and the formula cty ~ hwy.

  • Create a linear regression model on all the mpg data (formula cty ~ hwy) and assign the predictions to mpg$pred.

  • Use rmse() to get the root mean squared error of the predictions from the full model (mpg$pred). Recall that rmse() takes two arguments, the predicted values, and the actual outcome.

  • Get the root mean squared error of the cross-validation predictions. Are the two values about the same?

# mpg is in the workspace
summary(mpg)
 manufacturer          model               displ            year           cyl       
 Length:234         Length:234         Min.   :1.600   Min.   :1999   Min.   :4.000  
 Class :character   Class :character   1st Qu.:2.400   1st Qu.:1999   1st Qu.:4.000  
 Mode  :character   Mode  :character   Median :3.300   Median :2004   Median :6.000  
                                       Mean   :3.472   Mean   :2004   Mean   :5.889  
                                       3rd Qu.:4.600   3rd Qu.:2008   3rd Qu.:8.000  
                                       Max.   :7.000   Max.   :2008   Max.   :8.000  
    trans               drv                 cty             hwy             fl           
 Length:234         Length:234         Min.   : 9.00   Min.   :12.00   Length:234        
 Class :character   Class :character   1st Qu.:14.00   1st Qu.:18.00   Class :character  
 Mode  :character   Mode  :character   Median :17.00   Median :24.00   Mode  :character  
                                       Mean   :16.86   Mean   :23.44                     
                                       3rd Qu.:19.00   3rd Qu.:27.00                     
                                       Max.   :35.00   Max.   :44.00                     
    class              pred.cv            pred       
 Length:234         Min.   : 8.827   Min.   : 9.043  
 Class :character   1st Qu.:13.177   1st Qu.:13.142  
 Mode  :character   Median :17.313   Median :17.241  
                    Mean   :16.858   Mean   :16.859  
                    3rd Qu.:19.405   3rd Qu.:19.291  
                    Max.   :30.338   Max.   :30.906  
# splitPlan is in the workspace
str(splitPlan)
List of 3
 $ :List of 2
  ..$ train: int [1:156] 2 3 4 6 7 8 9 11 13 14 ...
  ..$ app  : int [1:78] 229 204 40 100 41 67 5 54 12 21 ...
 $ :List of 2
  ..$ train: int [1:156] 1 4 5 6 8 10 12 13 15 17 ...
  ..$ app  : int [1:78] 184 203 81 78 112 93 143 174 165 139 ...
 $ :List of 2
  ..$ train: int [1:156] 1 2 3 5 7 9 10 11 12 14 ...
  ..$ app  : int [1:78] 4 64 101 150 28 176 147 45 57 82 ...
 - attr(*, "splitmethod")= chr "kwaycross"
# Run the 3-fold cross validation plan from splitPlan
k <- 3 # Number of folds
mpg$pred.cv <- 0 
for(i in 1:k) {
  split <- splitPlan[[i]]
  model <- lm(cty~hwy, data = mpg[split$train,])
  mpg$pred.cv[split$app] <- predict(model, newdata = mpg[split$app,])
}
# Predict from a full model
mpg$pred <- predict(lm(cty ~ hwy, data = mpg))
# Get the rmse of the full model's predictions
rmse(mpg$pred, mpg$cty)
[1] 1.247045
# Get the rmse of the cross-validation predictions
rmse(mpg$pred.cv, mpg$cty)
[1] 1.253743

Chapter 3:

3.1: Examining the structure of categorical inputs

For this exercise you will call model.matrix() to examine how R represents data with both categorical and numerical inputs for modeling. The dataset flowers (derived from the Sleuth3 package) is loaded into your workspace. It has the following columns:

Flowers: the average number of flowers on a meadowfoam plant Intensity: the intensity of a light treatment applied to the plant Time: A categorical variable - when (Late or Early) in the lifecycle the light treatment occurred The ultimate goal is to predict Flowers as a function of Time and Intensity.

Instructions

50 XP

  • The data frame flowers is in your workspace.

  • Call the str() function on flowers to see the types of each column.

  • Use the unique() function on the column flowers$Time to see the possible values that Time takes. How many unique values are there?

  • Create a formula to express Flowers as a function of Intensity and Time. Assign it to the variable fmla and print it.

  • Use fmla and model.matrix() to create the model matrix for the data frame flowers. Assign it to the variable mmat.

  • Use head() to examine the first 20 lines of flowers.

  • Now examine the first 20 lines of mmat.

  • Is the numeric column Intensity different?

  • What happened to the categorical column Time from flowers?
  • How is Time == ‘Early’ represented? And Time = ‘Late’?

# Call str on flowers to see the types of each column
flowers<-read.csv("flowers.csv")
str(flowers)
'data.frame':   24 obs. of  4 variables:
 $ X        : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Flowers  : num  62.3 77.4 55.3 54.2 49.6 61.9 39.4 45.7 31.3 44.9 ...
 $ Time     : Factor w/ 2 levels "Early","Late": 2 2 2 2 2 2 2 2 2 2 ...
 $ Intensity: int  150 150 300 300 450 450 600 600 750 750 ...
# Use unique() to see how many possible values Time takes
unique(flowers$Time)
[1] Late  Early
Levels: Early Late
# Build a formula to express Flowers as a function of Intensity and Time: fmla. Print it
(fmla <- as.formula("Flowers ~ Intensity + Time"))
Flowers ~ Intensity + Time
# Use fmla and model.matrix to see how the data is represented for modeling
mmat <- model.matrix(fmla, data = flowers)
# Examine the first 20 lines of flowers
head(flowers, n=20)
# Examine the first 20 lines of mmat
head(mmat, n=20)
   (Intercept) Intensity TimeLate
1            1       150        1
2            1       150        1
3            1       300        1
4            1       300        1
5            1       450        1
6            1       450        1
7            1       600        1
8            1       600        1
9            1       750        1
10           1       750        1
11           1       900        1
12           1       900        1
13           1       150        0
14           1       150        0
15           1       300        0
16           1       300        0
17           1       450        0
18           1       450        0
19           1       600        0
20           1       600        0

3.3: Modeling with categorical inputs

For this exercise you will fit a linear model to the flowers data, to predict Flowers as a function of Time and Intensity.

The model formula fmla that you created in the previous exercise is still in your workspace, as is the model matrix mmat.

Instructions 100 XP

  • Use fmla and lm to train a linear model that predicts Flowers from Intensity and Time. Assign the model to the variable flower_model.

  • Use summary() to remind yourself of the structure of mmat.

-Use summary() to examine the flower_model. Do the variables match what you saw in mmat?

  • Use flower_model to predict the number of flowers. Add the predictions to flowers as the column predictions.

  • Fill in the blanks to plot predictions vs. actual flowers (predictions on the x-axis).

# flowers in is the workspace
str(flowers)
'data.frame':   24 obs. of  4 variables:
 $ X        : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Flowers  : num  62.3 77.4 55.3 54.2 49.6 61.9 39.4 45.7 31.3 44.9 ...
 $ Time     : Factor w/ 2 levels "Early","Late": 2 2 2 2 2 2 2 2 2 2 ...
 $ Intensity: int  150 150 300 300 450 450 600 600 750 750 ...
# fmla is in the workspace
fmla
Flowers ~ Intensity + Time
# Fit a model to predict Flowers from Intensity and Time : flower_model
flower_model <- lm(fmla,data=flowers)
# Use summary on mmat to remind yourself of its structure
summary(mmat)
  (Intercept)   Intensity      TimeLate  
 Min.   :1    Min.   :150   Min.   :0.0  
 1st Qu.:1    1st Qu.:300   1st Qu.:0.0  
 Median :1    Median :525   Median :0.5  
 Mean   :1    Mean   :525   Mean   :0.5  
 3rd Qu.:1    3rd Qu.:750   3rd Qu.:1.0  
 Max.   :1    Max.   :900   Max.   :1.0  
# Use summary to examine flower_model 
summary(flower_model)

Call:
lm(formula = fmla, data = flowers)

Residuals:
   Min     1Q Median     3Q    Max 
-9.652 -4.139 -1.558  5.632 12.165 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  83.464167   3.273772  25.495  < 2e-16 ***
Intensity    -0.040471   0.005132  -7.886 1.04e-07 ***
TimeLate    -12.158333   2.629557  -4.624 0.000146 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.441 on 21 degrees of freedom
Multiple R-squared:  0.7992,    Adjusted R-squared:   0.78 
F-statistic: 41.78 on 2 and 21 DF,  p-value: 4.786e-08
# Predict the number of flowers on each plant
flowers$predictions <- predict(flower_model)
# Plot predictions vs actual flowers (predictions on x-axis)
ggplot(flowers, aes(x = predictions, y = Flowers)) + 
  geom_point() +
  geom_abline(color = "blue") 

3.4: Modeling an interaction

In this exercise you will use interactions to model the effect of gender and gastric activity on alcohol metabolism.

The data frame alcohol has columns:

  1. Metabol: the alcohol metabolism rate

  2. Gastric: the rate of gastric alcohol dehydrogenase activity

  3. Sex: the sex of the drinker (Male or Female)

In the video, we fit three models to the alcohol data:

We saw that one of the models with interaction terms had a better R-squared than the additive model, suggesting that using interaction terms gives a better fit. In this exercise we will compare the R-squared of one of the interaction models to the main-effects-only model.

Recall that the operator : designates the interaction between two variables. The operator * designates the interaction between the two variables, plus the main effects.

x*y = x + y + x:y

Instructions

100 XP

# alcohol is in the workspace
alcohol<-read.csv("alcohol.csv")
summary(alcohol)
       X            Subject         Metabol          Gastric          Sex              Alcohol  
 Min.   : 1.00   Min.   : 1.00   Min.   : 0.100   Min.   :0.800   Female:18   Alcoholic    : 8  
 1st Qu.: 8.75   1st Qu.: 8.75   1st Qu.: 0.600   1st Qu.:1.200   Male  :14   Non-alcoholic:24  
 Median :16.50   Median :16.50   Median : 1.700   Median :1.600                                 
 Mean   :16.50   Mean   :16.50   Mean   : 2.422   Mean   :1.863                                 
 3rd Qu.:24.25   3rd Qu.:24.25   3rd Qu.: 2.925   3rd Qu.:2.200                                 
 Max.   :32.00   Max.   :32.00   Max.   :12.300   Max.   :5.200                                 
# Create the formula with main effects only
(fmla_add <- as.formula(Metabol~Gastric+Sex))
Metabol ~ Gastric + Sex
# Create the formula with interactions
(fmla_interaction <- as.formula(Metabol~Gastric+Gastric:Sex))
Metabol ~ Gastric + Gastric:Sex
# Fit the main effects only model
model_add <- lm(fmla_add,data=alcohol)
# Fit the interaction model
model_interaction <- lm(fmla_interaction,data=alcohol)
# Call summary on both models and compare
summary(model_add)

Call:
lm(formula = fmla_add, data = alcohol)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.2779 -0.6328 -0.0966  0.5783  4.5703 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -1.9466     0.5198  -3.745 0.000796 ***
Gastric       1.9656     0.2674   7.352 4.24e-08 ***
SexMale       1.6174     0.5114   3.163 0.003649 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.331 on 29 degrees of freedom
Multiple R-squared:  0.7654,    Adjusted R-squared:  0.7492 
F-statistic: 47.31 on 2 and 29 DF,  p-value: 7.41e-10
summary(model_interaction)

Call:
lm(formula = fmla_interaction, data = alcohol)

Residuals:
    Min      1Q  Median      3Q     Max 
-2.4656 -0.5091  0.0143  0.5660  4.0668 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)      -0.7504     0.5310  -1.413 0.168236    
Gastric           1.1489     0.3450   3.331 0.002372 ** 
Gastric:SexMale   1.0422     0.2412   4.321 0.000166 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.204 on 29 degrees of freedom
Multiple R-squared:  0.8081,    Adjusted R-squared:  0.7948 
F-statistic: 61.05 on 2 and 29 DF,  p-value: 4.033e-11

3.5: Modeling an interaction (2)

In this exercise, you will compare the performance of the interaction model you fit in the previous exercise to the performance of a main-effects only model. Because this data set is small, we will use cross-validation to simulate making predictions on out-of-sample data.

You will begin to use the dplyr package to do calculations.

mutate() adds new columns to a tbl (a type of data frame) group_by() specifies how rows are grouped in a tbl summarize() computes summary statistics of a column You will also use tidyr’s gather() which takes multiple columns and collapses them into key-value pairs.

Instructions

100 XP

  • The data frame alcohol and the formulas fmla_add and fmla_interaction are in the workspace.

  • Use kWayCrossValidation() to create a splitting plan for a 3-fold cross validation.

  • The first argument is the number of rows to be split.

  • The second argument is the number of folds for the cross-validation.

  • You can set the 3rd and 4th arguments of the function to NULL.

  • Examine and run the sample code to get the 3-fold cross-validation predictions of a model with no interactions and assign them to the column pred_add.

  • Get the 3-fold cross-validation predictions of the model with interactions. Assign the predictions to the column pred_interaction.

  • The sample code shows you the procedure.

  • Use the same splitPlan that you already created.

  • Fill in the blanks to gather the predictions into a single column pred.

  • add a column of residuals (actual outcome - predicted outcome).

  • get the RMSE of the cross-validation predictions for each model type.

  • Compare the RMSEs. Based on these results, which model should you use?

# alcohol is in the workspace
summary(alcohol)
       X            Subject         Metabol          Gastric          Sex              Alcohol  
 Min.   : 1.00   Min.   : 1.00   Min.   : 0.100   Min.   :0.800   Female:18   Alcoholic    : 8  
 1st Qu.: 8.75   1st Qu.: 8.75   1st Qu.: 0.600   1st Qu.:1.200   Male  :14   Non-alcoholic:24  
 Median :16.50   Median :16.50   Median : 1.700   Median :1.600                                 
 Mean   :16.50   Mean   :16.50   Mean   : 2.422   Mean   :1.863                                 
 3rd Qu.:24.25   3rd Qu.:24.25   3rd Qu.: 2.925   3rd Qu.:2.200                                 
 Max.   :32.00   Max.   :32.00   Max.   :12.300   Max.   :5.200                                 
# Both the formulae are in the workspace
fmla_add
Metabol ~ Gastric + Sex
fmla_interaction
Metabol ~ Gastric + Gastric:Sex
# Create the splitting plan for 3-fold cross validation
set.seed(34245)  # set the seed for reproducibility
splitPlan <- kWayCrossValidation(nrow(alcohol), 3, NULL, NULL)
# Sample code: Get cross-val predictions for main-effects only model
alcohol$pred_add <- 0  # initialize the prediction vector
for(i in 1:3) {
  split <- splitPlan[[i]]
  model_add <- lm(fmla_add, data = alcohol[split$train, ])
  alcohol$pred_add[split$app] <- predict(model_add, newdata = alcohol[split$app, ])
}
# Get the cross-val predictions for the model with interactions
alcohol$pred_interaction <- 0 # initialize the prediction vector
for(i in 1:3) {
  split <- splitPlan[[i]]
  model_interaction <- lm(fmla_interaction, data = alcohol[split$train,])
  alcohol$pred_interaction[split$app] <- predict(model_interaction, newdata = alcohol[split$app, ])
}
install.packages("magrittr")
Error in install.packages : Updating loaded packages
library(magrittr) # for %>% filter function
library('tidyr')

Attaching package: 㤼㸱tidyr㤼㸲

The following object is masked from 㤼㸱package:magrittr㤼㸲:

    extract
example('gather')

gather> library(dplyr)

Attaching package: 㤼㸱dplyr㤼㸲

The following object is masked from 㤼㸱package:nlme㤼㸲:

    collapse

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union

gather> # From http://stackoverflow.com/questions/1181060
gather> stocks <- tibble(
gather+   time = as.Date('2009-01-01') + 0:9,
gather+   X = rnorm(10, 0, 1),
gather+   Y = rnorm(10, 0, 2),
gather+   Z = rnorm(10, 0, 4)
gather+ )

gather> gather(stocks, stock, price, -time)

gather> stocks %>% gather(stock, price, -time)

gather> # get first observation for each Species in iris data -- base R
gather> mini_iris <- iris[c(1, 51, 101), ]

gather> # gather Sepal.Length, Sepal.Width, Petal.Length, Petal.Width
gather> gather(mini_iris, key = flower_att, value = measurement,
gather+        Sepal.Length, Sepal.Width, Petal.Length, Petal.Width)
      Species   flower_att measurement
1      setosa Sepal.Length         5.1
2  versicolor Sepal.Length         7.0
3   virginica Sepal.Length         6.3
4      setosa  Sepal.Width         3.5
5  versicolor  Sepal.Width         3.2
6   virginica  Sepal.Width         3.3
7      setosa Petal.Length         1.4
8  versicolor Petal.Length         4.7
9   virginica Petal.Length         6.0
10     setosa  Petal.Width         0.2
11 versicolor  Petal.Width         1.4
12  virginica  Petal.Width         2.5

gather> # same result but less verbose
gather> gather(mini_iris, key = flower_att, value = measurement, -Species)
      Species   flower_att measurement
1      setosa Sepal.Length         5.1
2  versicolor Sepal.Length         7.0
3   virginica Sepal.Length         6.3
4      setosa  Sepal.Width         3.5
5  versicolor  Sepal.Width         3.2
6   virginica  Sepal.Width         3.3
7      setosa Petal.Length         1.4
8  versicolor Petal.Length         4.7
9   virginica Petal.Length         6.0
10     setosa  Petal.Width         0.2
11 versicolor  Petal.Width         1.4
12  virginica  Petal.Width         2.5

gather> # repeat iris example using dplyr and the pipe operator
gather> library(dplyr)

gather> mini_iris <-
gather+   iris %>%
gather+   group_by(Species) %>%
gather+   slice(1)

gather> mini_iris %>% gather(key = flower_att, value = measurement, -Species)
# Get RMSE
alcohol %>% 
  gather(key = modeltype, value = pred, pred_add, pred_interaction) %>%
  mutate(residuals = Metabol-pred) %>%      
  group_by(modeltype) %>%
  summarize(rmse = sqrt(mean(residuals^2)))

3.6: Relative error

In this exercise, you will compare relative error to absolute error. For the purposes of modeling, we will define relative error as

\(rel=\frac{(pred−y)}{y}\)

that is, the error is relative to the true outcome. You will measure the overall relative error of a model using root mean squared relative error:

\(rmse_{rel}=\sqrt{\bar{rel^{2}}}\) where \(\bar{rel^{2}}\) is the mean of \(rel^{2}\).

The example (toy) dataset fdata is loaded in your workspace. It includes the columns:

  • y: the true output to be predicted by some model; imagine it is the amount of money a customer will spend on a visit to your store.

  • pred: the predictions of a model that predicts y.

  • label: categorical: whether y comes from a population that makes small purchases, or large ones.

You want to know which model does “better”: the one predicting the small purchases, or the one predicting large ones.

Instructions

100 XP

  • The data frame fdata is in the workspace.

  • Fill in the blanks to examine the data. Notice that large purchases tend to be about 100 times larger than small ones.

  • Fill in the blanks to create error columns:

  • Define residual as pred - y.

  • Define relative error as residual / y.

  • Fill in the blanks to calculate and compare RMSE and relative RMSE.

  • How do the absolute errors compare? The relative errors?

  • Examine the plot of predictions versus outcome. In your opinion, which model does “better”?

# fdata is in the workspace
fdata<-read.csv("fdata.csv")
summary(fdata)
       X                y                 pred                      label   
 Min.   :  1.00   Min.   :  -5.894   Min.   :   1.072   large purchases:50  
 1st Qu.: 25.75   1st Qu.:   5.407   1st Qu.:   6.373   small purchases:50  
 Median : 50.50   Median :  57.374   Median :  55.693                       
 Mean   : 50.50   Mean   : 306.204   Mean   : 305.905                       
 3rd Qu.: 75.25   3rd Qu.: 550.903   3rd Qu.: 547.886                       
 Max.   :100.00   Max.   :1101.619   Max.   :1098.896                       
# Examine the data: generate the summaries for the groups large and small:
fdata %>% 
    group_by(label) %>%     # group by small/large purchases
    summarize(min  = min(y),   # min of y
              mean = mean(y),   # mean of y
              max  = max(y))   # max of y
# Fill in the blanks to add error columns
fdata2 <- fdata %>% 
         group_by(label) %>%       # group by label
           mutate(residual = pred-y,  # Residual
                  relerr   = residual/y)  # Relative error
# Compare the rmse and rmse.rel of the large and small groups:
fdata2 %>% 
  group_by(label) %>% 
  summarize(rmse     = sqrt(mean((pred-y)^2)),   # RMSE
            rmse.rel = sqrt(mean(((pred-y)/y)^2)))   # Root mean squared relative error
            
# Plot the predictions for both groups of purchases
ggplot(fdata2, aes(x = pred, y = y, color = label)) + 
  geom_point() + 
  geom_abline() + 
  facet_wrap(~ label, ncol = 1, scales = "free") + 
  ggtitle("Outcome vs prediction")

Remark: Notice from this example how a model with larger RMSE might still be better, if relative errors are more important than absolute errors.

3.7: Modeling log-transformed monetary output

In this exercise, you will practice modeling on log-transformed monetary output, and then transforming the “log-money” predictions back into monetary units. The data loaded into your workspace records subjects’ incomes in 2005 (Income2005), as well as the results of several aptitude tests taken by the subjects in 1981:

  • Arith

  • Word

  • Parag

  • Math

  • AFQT (Percentile on the Armed Forces Qualifying Test)

The data have already been split into training and test sets (income_train and income_test respectively) and are in the workspace. You will build a model of log(income) from the inputs, and then convert log(income) back into income.

Instructions

100 XP

  • Call summary() on income_train$Income2005 to see the summary statistics of income in the training set.

  • Write a formula to express log(Income2005) as a function of the five tests as the variable fmla.log. Print it.

  • Fit a linear model of log(Income2005) to the income_train data: model.log.

  • Use model.log to predict income on the income_test dataset. Put it in the column logpred.

  • Check summary() of logpred to see that the magnitudes are much different from those of Income2005.

  • Reverse the log transformation to put the predictions into “monetary units”: exp(income_test$logpred).

  • Check summary() of pred.income and see that the magnitudes are now similar to Income2005 magnitudes.

  • Fill in the blanks to plot a scatter plot of predicted income vs income on the test set.

# Examine Income2005 in the training set
income_train<-read.csv("income_train.csv")
income_test<-read.csv("income_test.csv")
summary(income_train$Income2005)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
     63   23000   39000   49894   61500  703637 
# Write the formula for log income as a function of the tests and print it
(fmla.log <- log(Income2005)~Arith+Word+Parag+Math+AFQT)
log(Income2005) ~ Arith + Word + Parag + Math + AFQT
# Fit the linear model
model.log <-  lm(fmla.log,data=income_train)
# Make predictions on income_test
income_test$logpred <- predict(model.log,newdata=income_test)
summary(income_test$logpred)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  9.766  10.133  10.423  10.419  10.705  11.006 
# Convert the predictions to monetary units
income_test$pred.income <- exp(income_test$logpred)
summary(income_test$pred.income)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  17432   25167   33615   35363   44566   60217 
#  Plot predicted income (x axis) vs income
ggplot(income_test, aes(x = pred.income, y = Income2005)) + 
  geom_point() + 
  geom_abline(color = "blue")

3.8: Comparing RMSE and root-mean-squared Relative Error

In this exercise, you will show that log-transforming a monetary output before modeling improves mean relative error (but increases RMSE) compared to modeling the monetary output directly. You will compare the results of model.log from the previous exercise to a model (model.abs) that directly fits income.

The income_train and income_test datasets are loaded in your workspace, along with your model, model.log.

Also in the workspace:

model.abs: a model that directly fits income to the inputs using the formula

Income2005 ~ Arith + Word + Parag + Math + AFQT

Instructions

100 XP

  • Fill in the blanks to add predictions from the models to income_test.

  • Don’t forget to take the exponent of the predictions from model.log to undo the log transform!

  • Fill in the blanks to gather() the predictions and calculate the residuals and relative error.

  • Fill in the blanks to calculate the RMSE and relative RMSE for predictions.

  • Which model has larger absolute error? Larger relative error?

# fmla.abs is in the workspace
fmla.abs<-as.formula(Income2005 ~ Arith + Word + Parag + Math + AFQT)
model.abs<-lm(formula = fmla.abs, data = income_train)
# model.abs is in the workspace
summary(model.abs)

Call:
lm(formula = fmla.abs, data = income_train)

Residuals:
   Min     1Q Median     3Q    Max 
-78728 -24137  -6979  11964 648573 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  17516.7     6420.1   2.728  0.00642 ** 
Arith         1552.3      303.4   5.116 3.41e-07 ***
Word          -132.3      265.0  -0.499  0.61754    
Parag        -1155.1      618.3  -1.868  0.06189 .  
Math           725.5      372.0   1.950  0.05127 .  
AFQT           177.8      144.1   1.234  0.21734    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 45500 on 2063 degrees of freedom
Multiple R-squared:  0.1165,    Adjusted R-squared:  0.1144 
F-statistic:  54.4 on 5 and 2063 DF,  p-value: < 2.2e-16
# Add predictions to the test set
income_test <- income_test %>%
  mutate(pred.absmodel = predict(model.abs, income_test),        # predictions from model.abs
         pred.logmodel = exp(predict(model.log, income_test)))   # predictions from model.log
# Gather the predictions and calculate residuals and relative error
income_long <- income_test %>% 
  gather(key = modeltype, value = pred, pred.absmodel, pred.logmodel) %>%
  mutate(residual = pred-Income2005,   # residuals
         relerr   = residual/Income2005)   # relative error
# Calculate RMSE and relative RMSE and compare
income_long %>% 
  group_by(modeltype) %>%      # group by modeltype
  summarize(rmse     = sqrt(mean(residual^2)),    # RMSE
            rmse.rel = sqrt(mean(relerr^2)))    # Root mean squared relative error

3.9: Input transforms: the “hockey stick”

In this exercise, we will build a model to predict price from a measure of the house’s size (surface area). The data set houseprice has the columns:

  • price : house price in units of $1000

  • size: surface area

A scatterplot of the data shows that the data is quite non-linear: a sort of “hockey-stick” where price is fairly flat for smaller houses, but rises steeply as the house gets larger. Quadratics and tritics are often good functional forms to express hockey-stick like relationships. Note that there may not be a “physical” reason that price is related to the square of the size; a quadratic is simply a closed form approximation of the observed relationship.

scatterplot

You will fit a model to predict price as a function of the squared size, and look at its fit on the training data.

Because ^ is also a symbol to express interactions, use the function I() to treat the expression x^2 “as is”: that is, as the square of x rather than the interaction of x with itself.

exampleFormula = y ~ I(x^2)

Instructions

100 XP

  • The data set houseprice is in the workspace.

  • Write a formula, fmla_sqr, to express price as a function of squared size. Print it.

  • Fit a model model_sqr to the data using fmla_sqr

  • For comparison, fit a linear model model_lin to the data using the formula price ~ size.

  • Fill in the blanks to make predictions from the training data from the two models,

  • gather the predictions into a single column pred

  • graphically compare the predictions of the two models to the data. Which fits better?

# houseprice is in the workspace
houseprice<-readRDS("houseprice.rds")
summary(houseprice)
      size           price      
 Min.   : 44.0   Min.   : 42.0  
 1st Qu.: 73.5   1st Qu.:164.5  
 Median : 91.0   Median :203.5  
 Mean   : 94.3   Mean   :249.2  
 3rd Qu.:118.5   3rd Qu.:287.8  
 Max.   :150.0   Max.   :573.0  
# Create the formula for price as a function of squared size
(fmla_sqr <- price~I(size^2))
price ~ I(size^2)
# Fit a model of price as a function of squared size (use fmla_sqr)
model_sqr <- lm(fmla_sqr, data=houseprice)
# Fit a model of price as a linear function of size
model_lin <- lm(price~size, data=houseprice)
# Make predictions and compare
houseprice %>% 
    mutate(pred_lin = predict(model_lin),       # predictions from linear model
           pred_sqr = predict(model_sqr)) %>%   # predictions from quadratic model 
    gather(key = modeltype, value = pred, pred_lin, pred_sqr) %>% # gather the predictions
    ggplot(aes(x = size)) + 
       geom_point(aes(y = price)) +                   # actual prices
       geom_line(aes(y = pred, color = modeltype)) + # the predictions
       scale_color_brewer(palette = "Dark2")

3.10: Input transforms: the “hockey stick” Part (2)

In this exercise, we will build a model to predict price from a measure of the house’s size (surface area). The data set houseprice has the columns:

  • price : house price in units of $1000

  • size: surface area

A scatterplot of the data shows that the data is quite non-linear: a sort of “hockey-stick” where price is fairly flat for smaller houses, but rises steeply as the house gets larger. Quadratics and tritics are often good functional forms to express hockey-stick like relationships. Note that there may not be a “physical” reason that price is related to the square of the size; a quadratic is simply a closed form approximation of the observed relationship.

scatterplot

You will fit a model to predict price as a function of the squared size, and look at its fit on the training data.

Because ^ is also a symbol to express interactions, use the function I() to treat the expression x^2 “as is”: that is, as the square of x rather than the interaction of x with itself.

exampleFormula = y ~ I(x^2)

Instructions

100 XP

  • The data set houseprice is in the workspace.

  • Write a formula, fmla_sqr, to express price as a function of squared size. Print it.

  • Fit a model model_sqr to the data using fmla_sqr

  • For comparison, fit a linear model model_lin to the data using the formula price ~ size.

  • Fill in the blanks to make predictions from the training data from the two models

  • gather the predictions into a single column pred

  • graphically compare the predictions of the two models to the data. Which fits better?

# houseprice is in the workspace
summary(houseprice)
      size           price      
 Min.   : 44.0   Min.   : 42.0  
 1st Qu.: 73.5   1st Qu.:164.5  
 Median : 91.0   Median :203.5  
 Mean   : 94.3   Mean   :249.2  
 3rd Qu.:118.5   3rd Qu.:287.8  
 Max.   :150.0   Max.   :573.0  
# fmla_sqr is in the workspace
fmla_sqr
price ~ I(size^2)
# Create a splitting plan for 3-fold cross validation
set.seed(34245)  # set the seed for reproducibility
splitPlan <-  kWayCrossValidation(nrow(houseprice),3,NULL,NULL)
# Sample code: get cross-val predictions for price ~ size
houseprice$pred_lin <- 0  # initialize the prediction vector
for(i in 1:3) {
  split <- splitPlan[[i]]
  model_lin <- lm(price ~ size, data = houseprice[split$train,])
  houseprice$pred_lin[split$app] <- predict(model_lin, newdata = houseprice[split$app,])
}
# Get cross-val predictions for price as a function of size^2 (use fmla_sqr)
houseprice$pred_sqr <- 0 # initialize the prediction vector
for(i in 1:3) {
  split <- splitPlan[[i]]
  model_sqr <- lm(fmla_sqr, data = houseprice[split$train, ])
  houseprice$pred_sqr[split$app] <- predict(model_sqr, newdata = houseprice[split$app, ])
}
# Gather the predictions and calculate the residuals
houseprice_long <- houseprice %>%
  gather(key = modeltype, value = pred, pred_lin, pred_sqr) %>%
  mutate(residuals = pred-price)
# Compare the cross-validated RMSE for the two models
houseprice_long %>% 
  group_by(modeltype) %>% # group by modeltype
  summarize(rmse = sqrt(mean(residuals^2)))

Chapter 4: Dealing with Non-Linear Responses

Now that we have mastered linear models, we will begin to look at techniques for modeling situations that don’t meet the assumptions of linearity. This includes predicting probabilities and frequencies (values bounded between 0 and 1); predicting counts (nonnegative integer values, and associated rates); and responses that have a non-linear but additive relationship to the inputs. These algorithms are variations on the standard linear model.

4.1: Fit a model of sparrow survival probability

In this exercise, you will estimate the probability that a sparrow survives a severe winter storm, based on physical characteristics of the sparrow. The dataset sparrow is loaded into your workspace. The outcome to be predicted is status (“Survived”, “Perished”). The variables we will consider are:

  • total_length: length of the bird from tip of beak to tip of tail (mm)

  • weight: in grams

  • humerus : length of humerus (“upper arm bone” that connects the wing to the body) (inches)

Remember that when using glm() to create a logistic regression model, you must explicitly specify that family = binomial:

glm(formula, data = data, family = binomial)

You will call summary(), broom::glance() to see different functions for examining a logistic regression model. One of the diagnostics that you will look at is the analog to R2, called pseudo-R2.

pseudoR2=1−deviancenull.deviance

You can think of deviance as analogous to variance: it is a measure of the variation in categorical data. The pseudo-R2 is analogous to R2 for standard regression: R2 is a measure of the “variance explained” of a regression model. The pseudo-R2 is a measure of the “deviance explained”.

Instructions

100 XP

  • The data frame sparrow and the package broom are loaded in the workspace.

  • As suggested in the video, you will predict on the outcomes TRUE and FALSE. Create a new column survived in the sparrow data frame that is TRUE when status == “Survived”.

  • Create the formula fmla that expresses survived as a function of the variables of interest. Print it.

  • Fit a logistic regression model to predict the probability of sparrow survival. Assign the model to the variable sparrow_model.

  • Call summary() to see the coefficients of the model, the deviance and the null deviance.

  • Call glance() on the model to see the deviances and other diagnostics in a data frame. Assign the output from glance() to the variable perf.

  • Calculate the pseudo-R2.

# sparrow is in the workspace
sparrow<-readRDS("sparrow.rds")
summary(sparrow)
      status       age             total_length      wingspan         weight       beak_head    
 Perished:36   Length:87          Min.   :153.0   Min.   :236.0   Min.   :23.2   Min.   :29.80  
 Survived:51   Class :character   1st Qu.:158.0   1st Qu.:245.0   1st Qu.:24.7   1st Qu.:31.40  
               Mode  :character   Median :160.0   Median :247.0   Median :25.8   Median :31.70  
                                  Mean   :160.4   Mean   :247.5   Mean   :25.8   Mean   :31.64  
                                  3rd Qu.:162.5   3rd Qu.:251.0   3rd Qu.:26.7   3rd Qu.:32.10  
                                  Max.   :167.0   Max.   :256.0   Max.   :31.0   Max.   :33.00  
    humerus           femur           legbone          skull           sternum      
 Min.   :0.6600   Min.   :0.6500   Min.   :1.010   Min.   :0.5600   Min.   :0.7700  
 1st Qu.:0.7250   1st Qu.:0.7000   1st Qu.:1.110   1st Qu.:0.5900   1st Qu.:0.8300  
 Median :0.7400   Median :0.7100   Median :1.130   Median :0.6000   Median :0.8500  
 Mean   :0.7353   Mean   :0.7134   Mean   :1.131   Mean   :0.6032   Mean   :0.8511  
 3rd Qu.:0.7500   3rd Qu.:0.7300   3rd Qu.:1.160   3rd Qu.:0.6100   3rd Qu.:0.8800  
 Max.   :0.7800   Max.   :0.7600   Max.   :1.230   Max.   :0.6400   Max.   :0.9300  
# Create the survived column
sparrow$survived <- sparrow$status=="Survived"
# Create the formula
(fmla <- survived~total_length+weight+humerus)
survived ~ total_length + weight + humerus
# Fit the logistic regression model
sparrow_model <- glm(fmla,data=sparrow, family="binomial")
# Call summary
summary(sparrow_model)

Call:
glm(formula = fmla, family = "binomial", data = sparrow)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.1117  -0.6026   0.2871   0.6577   1.7082  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept)   46.8813    16.9631   2.764 0.005715 ** 
total_length  -0.5435     0.1409  -3.858 0.000115 ***
weight        -0.5689     0.2771  -2.053 0.040060 *  
humerus       75.4610    19.1586   3.939 8.19e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 118.008  on 86  degrees of freedom
Residual deviance:  75.094  on 83  degrees of freedom
AIC: 83.094

Number of Fisher Scoring iterations: 5
# Call glance
(perf <- glance(sparrow_model))
# Calculate pseudo-R-squared
(pseudoR2 <- 1-perf$deviance/perf$null.deviance)
[1] 0.3636526

4.2: Predict sparrow survival

In this exercise you will predict the probability of survival using the sparrow survival model from the previous exercise.

Recall that when calling predict() to get the predicted probabilities from a glm() model, you must specify that you want the response:

predict(model, type = “response”) Otherwise, predict() on a logistic regression model returns the predicted log-odds of the event, not the probability.

You will also use the GainCurvePlot() function to plot the gain curve from the model predictions. If the model’s gain curve is close to the ideal (“wizard”) gain curve, then the model sorted the sparrows well: that is, the model predicted that sparrows that actually survived would have a higher probability of survival. The inputs to the GainCurvePlot() function are:

  • frame: data frame with prediction column and ground truth column

  • xvar: the name of the column of predictions (as a string)

  • truthVar: the name of the column with actual outcome (as a string)

  • title: a title for the plot (as a string)

  • GainCurvePlot(frame, xvar, truthVar, title)

Instructions

100 XP

  • The dataframe sparrow and the model sparrow_model are in the workspace.

  • Create a new column in sparrow called pred that contains the predictions on the training data.

  • Call GainCurvePlot() to create the gain curve of predictions. Does the model do a good job of sorting the sparrows by whether or not they actually survived?

# sparrow is in the workspace
summary(sparrow)
      status       age             total_length      wingspan         weight       beak_head    
 Perished:36   Length:87          Min.   :153.0   Min.   :236.0   Min.   :23.2   Min.   :29.80  
 Survived:51   Class :character   1st Qu.:158.0   1st Qu.:245.0   1st Qu.:24.7   1st Qu.:31.40  
               Mode  :character   Median :160.0   Median :247.0   Median :25.8   Median :31.70  
                                  Mean   :160.4   Mean   :247.5   Mean   :25.8   Mean   :31.64  
                                  3rd Qu.:162.5   3rd Qu.:251.0   3rd Qu.:26.7   3rd Qu.:32.10  
                                  Max.   :167.0   Max.   :256.0   Max.   :31.0   Max.   :33.00  
    humerus           femur           legbone          skull           sternum      
 Min.   :0.6600   Min.   :0.6500   Min.   :1.010   Min.   :0.5600   Min.   :0.7700  
 1st Qu.:0.7250   1st Qu.:0.7000   1st Qu.:1.110   1st Qu.:0.5900   1st Qu.:0.8300  
 Median :0.7400   Median :0.7100   Median :1.130   Median :0.6000   Median :0.8500  
 Mean   :0.7353   Mean   :0.7134   Mean   :1.131   Mean   :0.6032   Mean   :0.8511  
 3rd Qu.:0.7500   3rd Qu.:0.7300   3rd Qu.:1.160   3rd Qu.:0.6100   3rd Qu.:0.8800  
 Max.   :0.7800   Max.   :0.7600   Max.   :1.230   Max.   :0.6400   Max.   :0.9300  
  survived      
 Mode :logical  
 FALSE:36       
 TRUE :51       
                
                
                
# sparrow_model is in the workspace
summary(sparrow_model)

Call:
glm(formula = fmla, family = "binomial", data = sparrow)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.1117  -0.6026   0.2871   0.6577   1.7082  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)    
(Intercept)   46.8813    16.9631   2.764 0.005715 ** 
total_length  -0.5435     0.1409  -3.858 0.000115 ***
weight        -0.5689     0.2771  -2.053 0.040060 *  
humerus       75.4610    19.1586   3.939 8.19e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 118.008  on 86  degrees of freedom
Residual deviance:  75.094  on 83  degrees of freedom
AIC: 83.094

Number of Fisher Scoring iterations: 5
# Make predictions
sparrow$pred <- predict(sparrow_model,type="response")
# Look at gain curve
GainCurvePlot(sparrow, "pred", "survived", "sparrow survival model")

Remark: You see from the gain curve that the model follows the wizard curve for about the first 30% of the data, identifying about 45% of the surviving sparrows with only a few false positives.

4.3: Poisson or quasipoisson

One of the assumptions of Poisson regression to predict counts is that the event you are counting is Poisson distributed: the average count per unit time is the same as the variance of the count. In practice, “the same” means that the mean and the variance should be of a similar order of magnitude.

When the variance is much larger than the mean, the Poisson assumption doesn’t apply, and one solution is to use quasipoisson regression, which does not assume that variance=mean.

For each of the following situations, decide if poisson regression would be suitable, or if you should use quasipoisson regression.

For which situations can you use poisson regression?

  1. Number of days students are absent: mean 5.9, variance 49

  2. Number of awards a student wins: mean 0.6, variance 1.1

  3. Number of hits per website page: mean 108.2, variance 108.5

  4. Number of bikes rented per day: mean 273, variance 45863.84

Answer the question

50 XP

Possible Answers

  1. All of them

  2. 1 and 4

  3. 2 and 3

  4. 1 and 3

  5. 2 and 4

4.4: Fit a model to predict bike rental counts

In this exercise you will build a model to predict the number of bikes rented in an hour as a function of the weather, the type of day (holiday, working day, or weekend), and the time of day. You will train the model on data from the month of July.

The data frame has the columns:

  • cnt: the number of bikes rented in that hour (the outcome)

  • hr: the hour of the day (0-23, as a factor)

  • holiday: TRUE/FALSE

  • workingday: TRUE if neither a holiday nor a weekend, else FALSE

  • weathersit: categorical, “Clear to partly cloudy”/“Light Precipitation”/“Misty”

  • temp: normalized temperature in Celsius

  • atemp: normalized “feeling” temperature in Celsius

  • hum: normalized humidity

  • windspeed: normalized windspeed

  • instant: the time index – number of hours since beginning of data set (not a variable) mnth and yr: month and year indices (not variables)

Remember that you must specify family = poisson or family = quasipoisson when using glm() to fit a count model.

Since there are a lot of input variables, for convenience we will specify the outcome and the inputs in variables, and use paste() to assemble a string representing the model formula.

Instructions

100 XP

  • The data frame bikesJuly is in the workspace. The names of the outcome variable and the input variables are also in the workspace as the variables outcome and vars respectively.

  • Fill in the blanks to create the formula fmla expressing cnt as a function of the inputs. Print it.

  • Calculate the mean (mean()) and variance (var()) of bikesJuly$cnt.

  • Should you use poisson or quasipoisson regression?

  • Use glm() to fit a model to the bikesJuly data: bike_model.

  • Use glance() to look at the model’s fit statistics. Assign the output of glance() to the variable perf.

  • Calculate the pseudo-R-squared of the model.

bikesJuly<-read.csv("bikesJuly.csv")
# bikesJuly is in the workspace
str(bikesJuly)
'data.frame':   744 obs. of  12 variables:
 $ hr        : int  0 1 2 3 4 5 6 7 8 9 ...
 $ holiday   : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
 $ workingday: logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
 $ weathersit: Factor w/ 3 levels "Clear to partly cloudy",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ temp      : num  0.76 0.74 0.72 0.72 0.7 0.68 0.7 0.74 0.78 0.82 ...
 $ atemp     : num  0.727 0.697 0.697 0.712 0.667 ...
 $ hum       : num  0.66 0.7 0.74 0.84 0.79 0.79 0.79 0.7 0.62 0.56 ...
 $ windspeed : num  0 0.1343 0.0896 0.1343 0.194 ...
 $ cnt       : int  149 93 90 33 4 10 27 50 142 219 ...
 $ instant   : int  13004 13005 13006 13007 13008 13009 13010 13011 13012 13013 ...
 $ mnth      : int  7 7 7 7 7 7 7 7 7 7 ...
 $ yr        : int  1 1 1 1 1 1 1 1 1 1 ...
# The outcome column
outcome<-c("cnt")
# The inputs to use
vars<-c("hr","holiday", "workingday", "weathersit", "temp", "atemp", "hum", "windspeed")
# Create the formula string for bikes rented as a function of the inputs
(fmla <- paste(outcome, "~", paste(vars, collapse = " + ")))
[1] "cnt ~ hr + holiday + workingday + weathersit + temp + atemp + hum + windspeed"
# Calculate the mean and variance of the outcome
(mean_bikes <- mean(bikesJuly$cnt))
[1] 273.6653
(var_bikes <- var(bikesJuly$cnt))
[1] 45863.84
# Fit the model
bike_model <- glm(fmla,data=bikesJuly,family=quasipoisson)
# Call glance
(perf <- glance(bike_model))
# Calculate pseudo-R-squared
(pseudoR2 <- 1-perf$deviance/perf$null.deviance)
[1] 0.3472145

4.5: Predict bike rentals on new data

In this exercise you will use the model you built in the previous exercise to make predictions for the month of August. The data set bikesAugust has the same columns as bikesJuly.

Recall that you must specify type = “response” with predict() when predicting counts from a glm poisson or quasipoisson model.

Instructions

100 XP

  • The model bike_model and the data frame bikesAugust are in the workspace.

  • Use predict to predict the number of bikes per hour on the bikesAugust data. Assign the predictions to the column bikesAugust$pred.

  • Fill in the blanks to get the RMSE of the predictions on the August data.

  • Fill in the blanks to generate the plot of predictions to actual counts.

  • Do any of the predictions appear negative?

bikesAugust<-read.csv("bikesAugust.csv")
# bikesAugust is in the workspace
str(bikesAugust)
'data.frame':   744 obs. of  13 variables:
 $ hr        : int  0 1 2 3 4 5 6 7 8 9 ...
 $ holiday   : logi  FALSE FALSE FALSE FALSE FALSE FALSE ...
 $ workingday: logi  TRUE TRUE TRUE TRUE TRUE TRUE ...
 $ weathersit: Factor w/ 3 levels "Clear to partly cloudy",..: 1 1 1 1 3 3 1 3 3 3 ...
 $ temp      : num  0.68 0.66 0.64 0.64 0.64 0.64 0.64 0.64 0.66 0.68 ...
 $ atemp     : num  0.636 0.606 0.576 0.576 0.591 ...
 $ hum       : num  0.79 0.83 0.83 0.83 0.78 0.78 0.78 0.83 0.78 0.74 ...
 $ windspeed : num  0.1642 0.0896 0.1045 0.1045 0.1343 ...
 $ cnt       : int  47 33 13 7 4 49 185 487 681 350 ...
 $ instant   : int  13748 13749 13750 13751 13752 13753 13754 13755 13756 13757 ...
 $ mnth      : int  8 8 8 8 8 8 8 8 8 8 ...
 $ yr        : int  1 1 1 1 1 1 1 1 1 1 ...
 $ pred      : num  94.9 51.7 37.9 17.5 9.3 ...
# bike_model is in the workspace
summary(bike_model)

Call:
glm(formula = fmla, family = quasipoisson, data = bikesJuly)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-20.613   -9.712   -3.379    4.536   34.847  

Coefficients:
                               Estimate Std. Error t value Pr(>|t|)    
(Intercept)                    5.783187   0.716088   8.076 2.74e-15 ***
hr                             0.052373   0.004225  12.395  < 2e-16 ***
holidayTRUE                    0.050045   0.142381   0.351   0.7253    
workingdayTRUE                 0.041671   0.060260   0.692   0.4895    
weathersitLight Precipitation -0.049635   0.126787  -0.391   0.6956    
weathersitMisty                0.114739   0.065897   1.741   0.0821 .  
temp                          -2.231584   1.859082  -1.200   0.2304    
atemp                          2.266270   1.573259   1.440   0.1502    
hum                           -1.563113   0.390401  -4.004 6.87e-05 ***
windspeed                      0.590418   0.263472   2.241   0.0253 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for quasipoisson family taken to be 129.1499)

    Null deviance: 133365  on 743  degrees of freedom
Residual deviance:  87059  on 734  degrees of freedom
AIC: NA

Number of Fisher Scoring iterations: 5
# Make predictions on August data
bikesAugust$pred  <- predict(bike_model,newdata=bikesAugust, type="response")
# Calculate the RMSE
bikesAugust %>% 
  mutate(residual = pred-cnt) %>%
  summarize(rmse  = sqrt(mean(residual^2)))
# Plot predictions vs cnt (pred on x-axis)
ggplot(bikesAugust, aes(x = pred, y = cnt)) +
  geom_point() + 
  geom_abline(color = "darkblue")

4.6: Visualize the Bike Rental Predictions

In the previous exercise, you visualized the bike model’s predictions using the standard “outcome vs. prediction” scatter plot. Since the bike rental data is time series data, you might be interested in how the model performs as a function of time. In this exercise, you will compare the predictions and actual rentals on an hourly basis, for the first 14 days of August.

To create the plot you will use the function tidyr::gather() to consolidate the predicted and actual values from bikesAugust in a single column. gather() takes as arguments:

  • The “wide” data frame to be gathered (implicit in a pipe)

  • The name of the key column to be created - contains the names of the gathered columns.

  • The name of the value column to be created - contains the values of the gathered columns.

  • The names of the columns to be gathered into a single column.

You’ll use the gathered data frame to compare the actual and predicted rental counts as a function of time. The time index, instant counts the number of observations since the beginning of data collection. The sample code converts the instants to daily units, starting from 0.

Instructions

100 XP

  • The data frame bikesAugust, with the predictions (bikesAugust$pred) is in the workspace.

  • Fill in the blanks to plot the predictions and actual counts by hour for the first 14 days of August.

  • convert instant to be in day units, rather than hour

  • gather() the cnt and pred columns into a column called value, with a key called valuetype.

  • filter() for the first two weeks of August

  • Plot value as a function of instant (day).

  • Does the model see the general time patterns in bike rentals?

# Plot predictions and cnt by date/time
quasipoisson_plot<-bikesAugust %>% 
  # set start to 0, convert unit to days
  mutate(instant = (instant - min(instant))/24) %>%  
  # gather cnt and pred into a value column
  gather(key = valuetype, value = value, cnt, pred) %>%
  filter(instant < 14) %>% # restric to first 14 days
  # plot value by instant
  ggplot(aes(x = instant, y = value, color = valuetype, linetype = valuetype)) + 
  geom_point() + 
  geom_line() + 
  scale_x_continuous("Day", breaks = 0:14, labels = 0:14) + 
  scale_color_brewer(palette = "Dark2") + 
  ggtitle("Predicted August bike rentals, Quasipoisson model")
quasipoisson_plot

Remark: This model mostly identifies the slow and busy hours of the day, although it often underestimates peak demand.

4.7: Writing formulas for GAM models

When using gam() to model outcome as an additive function of the inputs, you can use the s() function inside formulas to designate that you want use a spline to model the non-linear relationship of a continuous variable to the outcome.

Suppose that you want to predict how much weight (Wtloss) a dieter will lose over a 2-year diet plan as a function of:

  • Diet type (categorical)

  • Sex (categorical)

  • Age at beginning of diet (continuous)

  • BMI (body mass index) at beginning of diet (continuous)

You do not want to assume that any of the relationships are linear.

Which is the most appropriate formula?

Answer the question

50 XP

Possible Answers

  1. Wtloss ~ Diet + Sex + Age + BMI

  2. Wtloss ~ s(Diet) + s(Sex) + s(Age) + s(BMI)

  3. Wtloss ~ Diet + Sex + s(Age) + s(BMI) [ans]

Remark: Categorial variables cannot be ‘splined’, piecewise polynomially modelled

4.8: Writing formulas for GAM models (2)

Suppose that in the diet problem from the previous exercise, you now also want to take into account

the dieter’s resting metabolic rate (BMR – continuous) and the dieter’s average number hours of aerobic exercise per day (E – continuous) at the beginning of the study.

You have reason to believe that the relationship between BMR and weight loss is linear (and you want to model it that way), but not necessarily the relationship between aerobic exercise and weight loss.

Which is the most appropriate formula?

Answer the question

50 XP

Possible Answers

  1. Wtloss ~ Diet + Sex + s(Age) + s(BMI) + s(BMR) + s(E)

  2. Wtloss ~ Diet + Sex + s(Age) + s(BMI) + BMR + s(E) [ans]

  3. Wtloss ~ Diet + Sex + s(Age) + s(BMI) + s(BMR) + E

4.9: Model soybean growth with GAM

In this exercise you will model the average leaf weight on a soybean plant as a function of time (after planting). As you will see, the soybean plant doesn’t grow at a steady rate, but rather has a “growth spurt” that eventually tapers off. Hence, leaf weight is not well described by a linear model.

Recall that you can designate which variable you want to model non-linearly in a formula with the s() function:

y ~ s(x)

Also remember that gam() from the package mgcv has the calling interface

gam(formula, family, data)

For standard regression, use family = gaussian (the default).

The soybean training data, soybean_train is loaded into your workspace. It has two columns: the outcome weight and the variable Time. For comparison, the linear model model.lin, which was fit using the formula weight ~ Time has already been loaded into the workspace as well.

Instructions

100 XP

Instructions

100 XP

  • Fill in the blanks to plot weight versus Time (Time on x-axis). Does the relationship look linear?

  • Load the package mgcv.

  • Create the formula fmla.gam to express weight as a non-linear function of Time. Print it.

  • Fit a generalized additive model on soybean_train using fmla.gam.

  • Call summary() on the linear model model.lin (already in your workspace). What is the R2?

  • Call summary() on ’model.gam. The “deviance explained” reports the model’s unadjusted R2. What is the R2?

  • Which model appears to be a better fit to the training data?

  • Call plot() on model.gam to see the derived relationship between Time and weight.

# soybean_train is in the workspace
soybean_train<-read.csv("soybean_train.csv")
summary(soybean_train)
       X              Plot     Variety      Year           Time           weight       
 Min.   :  1.0   1988F6 : 10   F:161   Min.   :1988   Min.   :14.00   Min.   : 0.0290  
 1st Qu.:101.2   1988F7 :  9   P:169   1st Qu.:1988   1st Qu.:27.00   1st Qu.: 0.6663  
 Median :207.5   1988P1 :  9           Median :1989   Median :42.00   Median : 3.5233  
 Mean   :207.3   1988P2 :  9           Mean   :1989   Mean   :43.56   Mean   : 6.1645  
 3rd Qu.:307.8   1988P8 :  9           3rd Qu.:1990   3rd Qu.:56.00   3rd Qu.:10.3808  
 Max.   :412.0   1988F3 :  8           Max.   :1990   Max.   :84.00   Max.   :27.3700  
                 (Other):276                                                           
# Plot weight vs Time (Time on x axis)
ggplot(soybean_train, aes(x = Time, y = weight)) + 
  geom_point()
# Load the package mgcv
#install.packages("nlme")
#install.packages("mgcv")
#library(nlme)
#library(mgcv)
install.packages('gam')
Warning in install.packages :
  unable to access index for repository http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5:
  cannot open URL 'http://www.stats.ox.ac.uk/pub/RWin/bin/windows/contrib/3.5/PACKAGES'
trying URL 'https://mran.microsoft.com/snapshot/2018-08-01/bin/windows/contrib/3.5/gam_1.16.zip'
Content type 'application/zip' length 409705 bytes (400 KB)
downloaded 400 KB
package ‘gam’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\cweiqiang\AppData\Local\Temp\RtmpueSsh9\downloaded_packages

Restarting R session...

4.10: Predict with the soybean model on test data

In this exercise you will apply the soybean models from the previous exercise (model.lin and model.gam, already in your workspace) to new data: soybean_test.

Instructions

100 XP

  • The data frame soybean.test and the models model.lin and model.gam are in the workspace.

  • Create a column soybean_test$pred.lin with predictions from the linear model model.lin.

  • Create a column soybean_test$pred.gam with predictions from the gam model model.gam.

  • For GAM models, the predict() method returns a matrix, so use as.numeric() to convert the matrix to a vector.

  • Fill in the blanks to gather() the prediction columns into a single value column pred with key column modeltype. Call the long dataframe soybean_long.

  • Calculate and compare the RMSE of both models.

  • Which model does better?

  • Run the code to compare the predictions of each model against the actual average leaf weights.

  • A scatter plot of weight as a function of Time.

  • Point-and-line plots of the predictions (pred) as a function of Time.

  • Notice that the linear model sometimes predicts negative weights! Does the gam model?

Remark: The GAM learns the non-linear growth function of the soybean plants, including the fact that weight is never negative.

Chapter 5: Tree-Based Methods

In this chapter we will look at modeling algorithms that do not assume linearity or additivity, and that can learn limited types of interactions among input variables. These algorithms are tree-based methods that work by combining ensembles of decision trees that are learned from the training data.

5.1: Build a random forest model for bike rentals

In this exercise you will again build a model to predict the number of bikes rented in an hour as a function of the weather, the type of day (holiday, working day, or weekend), and the time of day. You will train the model on data from the month of July.

You will use the ranger package to fit the random forest model. For this exercise, the key arguments to the ranger() call are:

  • formula

  • data

  • num.trees: the number of trees in the forest.

  • respect.unordered.factors : Specifies how to treat unordered factor variables. We recommend setting this to “order” for regression.

  • seed: because this is a random algorithm, you will set the seed to get reproducible results

Since there are a lot of input variables, for convenience we will specify the outcome and the inputs in the variables outcome and vars, and use paste() to assemble a string representing the model formula.

Instructions

100 XP

The data frame bikesJuly is in the workspace. The sample code specifies the names of the outcome and input variables.

  • Fill in the blanks to create the formula fmla expressing cnt as a function of the inputs. Print it.

  • Load the package ranger.

  • Use ranger to fit a model to the bikesJuly data: bike_model_rf.

  • The first argument to ranger() is the formula, fmla.

  • Use 500 trees and respect.unordered.factors = “order”.

  • Set the seed to seed for reproducible results.

  • Print the model. What is the R-squared?

5.2: Predict bike rentals with the random forest model

In this exercise you will use the model that you fit in the previous exercise to predict bike rentals for the month of August.

The predict() function for a ranger model produces a list. One of the elements of this list is predictions, a vector of predicted values. You can access predictions with the $ notation for accessing named elements of a list:

predict(model, data)$predictions

Instructions

100 XP

  • The model bike_model_rf and the dataset bikesAugust (for evaluation) are loaded into your workspace.

  • Call predict() on bikesAugust to predict the number of bikes rented in August (cnt). Add the predictions to bikesAugust as the column pred.

  • Fill in the blanks to calculate the root mean squared error of the predictions.

  • The poisson model you built for this data gave an RMSE of about 112.6. How does this model compare?

  • Fill in the blanks to plot actual bike rental counts (cnt) versus the predictions (pred on x-axis).

5.3: Visualize random forest bike model predictions

In the previous exercise, you saw that the random forest bike model did better on the August data than the quasiposson model, in terms of RMSE.

In this exercise you will visualize the random forest model’s August predictions as a function of time. The corresponding plot from the quasipoisson model that you built in a previous exercise is in the workspace for you to compare.

Recall that the quasipoisson model mostly identified the pattern of slow and busy hours in the day, but it somewhat underestimated peak demands. You would like to see how the random forest model compares.

Instructions

100 XP

  • The data frame bikesAugust (with predictions) is in the workspace. The plot quasipoisson_plot of quasipoisson model predictions as a function of time is also in the workspace.

  • Print quasipoisson_plot to review the quasipoisson model’s behavior.

  • Fill in the blanks to plot the predictions and actual counts by hour for the first 14 days of August. gather the cnt and pred columns into a column called value, with a key called valuetype.

  • Plot value as a function of instant (day).

  • How does the random forest model compare?

5.4: vtreat on a small example

In this exercise you will use vtreat to one-hot-encode a categorical variable on a small example. vtreat creates a treatment plan to transform categorical variables into indicator variables (coded “lev”), and to clean bad values out of numerical variables (coded “clean”).

To design a treatment plan use the function designTreatmentsZ()

  • treatplan <- designTreatmentsZ(data, varlist)

  • data: the original training data frame

  • varlist: a vector of input variables to be treated (as strings).

  • designTreatmentsZ() returns a list with an element scoreFrame: a data frame that includes the names and types of the new variables:

scoreFrame <- treatplan %>% magrittr::use_series(scoreFrame) %>% select(varName, origName, code)

  • varName: the name of the new treated variable

  • origName: the name of the original variable that the treated variable comes from
  • code: the type of the new variable.

  • “clean”: a numerical variable with no NAs or NaNs
  • “lev”: an indicator variable for a specific level of the original categorical variable. (magrittr::use_series() is an alias for $ that you can use in pipes.)

For these exercises, we want varName where code is either “clean” or “lev”:

newvarlist <- scoreFrame %>% filter(code %in% c(“clean”, “lev”) %>% magrittr::use_series(varName)

To transform the data set into all numerical and one-hot-encoded variables, use prepare():

data.treat <- prepare(treatplan, data, varRestrictions = newvarlist)

  • treatplan: the treatment plan

  • data: the data frame to be treated

  • varRestrictions: the variables desired in the treated data

Instructions

100 XP

The data frame dframe and the package magrittr are loaded in the workspace.

  1. Print dframe. We will assume that color and size are input variables, and popularity is the outcome to be predicted.

  2. Create a vector called vars with the names of the input variables (as strings).

  3. Load the package vtreat.

  4. Use designTreatmentsZ() to create a treatment plan for the variables in vars. Assign it to the variable treatplan.

  5. Get and examine the scoreFrame from the treatment plan to see the mapping from old variables to new variables.

  6. You only need the columns varName, origName and code.

  7. What are the names of the new indicator variables? Of the continuous variable?

  8. Create a vector newvars that contains the variable varName where code is either clean or lev. Print it.

  9. Use prepare() to create a new data frame dframe.treat that is a one-hot-encoded version of dframe (without the outcome column).

10.Print it and compare to dframe.

5.5: Novel levels

When a level of a categorical variable is rare, sometimes it will fail to show up in training data. If that rare level then appears in future data, downstream models may not know what to do with it. When such novel levels appear, using model.matrix or caret::dummyVars to one-hot-encode will not work correctly.

vtreat is a “safer” alternative to model.matrix for one-hot-encoding, because it can manage novel levels safely. vtreat also manages missing values in the data (both categorical and continuous).

In this exercise you will see how vtreat handles categorical values that did not appear in the training set. The treatment plan treatplan and the set of variables newvars from the previous exercise are still in your workspace. dframe and a new data frame testframe are also in your workspace.

Instructions

100 XP

  • Print dframe and testframe.

  • Are there colors in testframe that didn’t appear in dframe?

  • Call prepare() to create a one-hot-encoded version of testframe (without the outcome). Call it testframe.treat and print it.

  • Use the varRestriction argument to restrict to only the variables in newvars.

  • How are the yellow rows encoded?

5.6: vtreat the bike rental data

In this exercise you will create one-hot-encoded data frames of the July/August bike data, for use with xgboost later on.

The data frames bikesJuly and bikesAugust are in the workspace.

For your convenience, we have defined the variable vars with the list of variable columns for the model.

Instructions

100 XP

  • Load the package vtreat.

  • Use designTreatmentsZ() to create a treatment plan treatplan for the variables in vars from bikesJuly (the training data).

  • Set the flag verbose=FALSE to prevent the function from printing too many messages.

  • Fill in the blanks to create a vector newvars that contains only the names of the clean and lev transformed variables. Print it.

  • Use prepare() to create a one-hot-encoded training data frame bikesJuly.treat.

  • Use the varRestrictions argument to restrict the variables you will use to newvars.

  • Use prepare() to create a one-hot-encoded test frame bikesAugust.treat from bikesAugust in the same way.

  • Call str() on both prepared test frames to see the structure.

5.7: Find the right number of trees for a gradient boosting machine

In this exercise you will get ready to build a gradient boosting model to predict the number of bikes rented in an hour as a function of the weather and the type and time of day. You will train the model on data from the month of July.

The July data is loaded into your workspace. Remember that bikesJuly.treat no longer has the outcome column, so you must get it from the untreated data: bikesJuly$cnt.

You will use the xgboost package to fit the random forest model. The function xgb.cv() uses cross-validation to estimate the out-of-sample learning error as each new tree is added to the model. The appropriate number of trees to use in the final model is the number that minimizes the holdout RMSE.

For this exercise, the key arguments to the xgb.cv() call are:

  • data: a numeric matrix.

  • label: vector of outcomes (also numeric).

  • nrounds: the maximum number of rounds (trees to build).

  • nfold: the number of folds for the cross-validation. 5 is a good number.

  • objective: “reg:linear” for continuous outcomes.

  • eta: the learning rate.

  • max_depth: depth of trees.

  • early_stopping_rounds: after this many rounds without improvement, stop.

  • verbose: 0 to stay silent.

Instructions

100 XP

The data frames bikesJuly and bikesJuly.treat are in the workspace.

  1. Load the package xgboost.

  2. Fill in the blanks to run xgb.cv() on the treated training data; assign the output to the variable cv.

  3. Use as.matrix() to convert the vtreated data frame to a matrix.

  4. Use 100 rounds, and 5-fold cross validation.

  5. Set early_stopping_rounds to 10.

  6. Set eta to 0.3, max_depth to 6.

  7. Get the data frame evaluation_log from cv and assign it to the variable elog. Each row of the evaluation_log corresponds to an additional tree, so the row number tells you the number of trees in the model.

  8. Fill in the blanks to get the number of trees with the minimum value of the columns train_rmse_mean and test_rmse_mean.

  9. which.min() returns the index of the minimum value in a vector. How many trees do you need?

5.8: Fit an xgboost bike rental model and predict

In this exercise you will fit a gradient boosting model using xgboost() to predict the number of bikes rented in an hour as a function of the weather and the type and time of day. You will train the model on data from the month of July and predict on data for the month of August.

The datasets for July and August are loaded into your workspace. Remember the vtreat-ed data no longer has the outcome column, so you must get it from the original data (the cnt column).

For convenience, the number of trees to use, ntrees from the previous exercise is in the workspace.

The arguments to xgboost() are similar to those of xgb.cv().

Instructions

100 XP

  • The data frames bikesJuly, bikesJuly.treat, bikesAugust and bikesAugust.treat are in the workspace. The number of trees ntrees is in the workspace.

  • Fill in the blanks to run xgboost() on the July data. Assign the model to the variable model.

  • Use as.matrix() to convert the vtreated data frame to a matrix.

  • The objective should be “reg:linear”.

  • Use ntrees rounds.

  • Set eta to 0.3, depth to 6, and verbose to 0 (silent).

  • Now call predict() on bikesAugust.treat to predict the number of bikes rented in August.

  • Use as.matrix() to convert the vtreat-ed test data into a matrix.

  • Add the predictions tobikesAugust as the column pred.

  • Fill in the blanks to plot actual bike rental counts versus the predictions (predictions on the x-axis). Do you see a possible problem with the predictions?

5.9: Evaluate the xgboost bike rental model

In this exercise you will evaluate the gradient boosting model bike_model_xgb that you fit in the last exercise, using data from the month of August. You’ll compare this model’s RMSE for August to the RMSE of previous models that you’ve built.

The dataset bikesAugust is in the workspace. You have already made predictions using the xgboost model; they are in the column pred.

Instructions

100 XP

  • Fill in the blanks to calculate the RMSE of the predictions.

  • How does it compare to the RMSE from the poisson model (approx. 112.6) and the random forest model (approx. 96.7)?

5.10: Visualize the xgboost bike rental model

You’ve now seen three different ways to model the bike rental data. For this example, you’ve seen that the gradient boosting model had the smallest RMSE. To finish up the course, let’s compare the gradient boosting model’s predictions to the other two models as a function of time.

On completing this exercise, you will have completed the course. Congratulations! Now you have the tools to apply a variety of approaches to your regression tasks.

Instructions

100 XP

  • The data frame bikesAugust with predictions, is in the workspace. The plots quasipoisson_plot and randomforest_plot are also in the workspace.

  • Print quasipoisson_plot to review the quasipoisson model’s behavior.

  • Print randomforest_plot to review the random forest model’s behavior.

  • Fill in the blanks to plot the gradient boosting predictions and actual counts by hour for the first 14 days of August.

  • gather() the cnt and pred columns into a column called value, with a key called valuetype.

  • Plot value as a function of instant (day). How does the gradient boosting model compare to the previous models?

Remark: The gradient boosting pattern captures rental variations due to time of day and other factors better than the previous models.

LS0tDQp0aXRsZTogIkRhdGFjYW1wIFIgLSBTdXBlcnZpc2VkIExlYXJuaW5nIGluIFIgUmVncmVzc2lvbiINCmF1dGhvcjogIkNoZW4gV2VpcWlhbmciDQpkYXRlOiAiTm92ZW1iZXIgMjYsIDIwMTgiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1UUlVFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWwgPSBUUlVFKQ0KIyBSZW1hcms6IElmIG5lZWQgTWF0aCBmb250LCAnU2F2ZSB3aXRoIGVuY29kaW5nIGFzIFVURicgd2hpY2ggbWFrZXMgdGhlIFIgTWFya2Rvd24gY29tcGlsZQ0KYGBgDQoNCiMgUiBNYXJrZG93bg0KDQpUaGlzIGlzIGFuIFIgTWFya2Rvd24gZG9jdW1lbnQuIE1hcmtkb3duIGlzIGEgc2ltcGxlIGZvcm1hdHRpbmcgc3ludGF4IGZvciBhdXRob3JpbmcgSFRNTCwgUERGLCBhbmQgTVMgV29yZCBkb2N1bWVudHMuIEZvciBtb3JlIGRldGFpbHMgb24gdXNpbmcgUiBNYXJrZG93biBzZWUgPGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20+Lg0KDQpXaGVuIHlvdSBjbGljayB0aGUgKipLbml0KiogYnV0dG9uIGEgZG9jdW1lbnQgd2lsbCBiZSBnZW5lcmF0ZWQgdGhhdCBpbmNsdWRlcyBib3RoIGNvbnRlbnQgYXMgd2VsbCBhcyB0aGUgb3V0cHV0IG9mIGFueSBlbWJlZGRlZCBSIGNvZGUgY2h1bmtzIHdpdGhpbiB0aGUgZG9jdW1lbnQuIFlvdSBjYW4gZW1iZWQgYW4gUiBjb2RlIGNodW5rIGxpa2UgdGhpczoNCg0KDQojIENoYXB0ZXIgMTogV2hhdCBpcyBSZWdyZXNzaW9uPw0KDQpJbiB0aGlzIGNoYXB0ZXIgd2UgaW50cm9kdWNlIHRoZSBjb25jZXB0IG9mIHJlZ3Jlc3Npb24gZnJvbSBhIG1hY2hpbmUgbGVhcm5pbmcgcG9pbnQgb2Ygdmlldy4gV2Ugd2lsbCBwcmVzZW50IHRoZSBmdW5kYW1lbnRhbCByZWdyZXNzaW9uIG1ldGhvZDogbGluZWFyIHJlZ3Jlc3Npb24uIFdlIHdpbGwgc2hvdyBob3cgdG8gZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYW5kIHRvIG1ha2UgcHJlZGljdGlvbnMgZnJvbSB0aGUgbW9kZWwuDQoNCiMjIDEuMTogSWRlbnRpZnkgdGhlIHJlZ3Jlc3Npb24gdGFza3MNCg0KRnJvbSBhIG1hY2hpbmUgbGVhcm5pbmcgcGVyc3BlY3RpdmUsIHRoZSB0ZXJtIHJlZ3Jlc3Npb24gZ2VuZXJhbGx5IGVuY29tcGFzc2VzIHRoZSBwcmVkaWN0aW9uIG9mIGNvbnRpbnVvdXMgdmFsdWVzLiBTdGF0aXN0aWNhbGx5LCB0aGVzZSBwcmVkaWN0aW9ucyBhcmUgdGhlIGV4cGVjdGVkIHZhbHVlLCBvciB0aGUgYXZlcmFnZSB2YWx1ZSBvbmUgd291bGQgb2JzZXJ2ZSBmb3IgdGhlIGdpdmVuIGlucHV0IHZhbHVlcy4NCg0KV2hpY2ggb2YgdGhlIGZvbGxvd2luZyB0YXNrcyBhcmUgcmVncmVzc2lvbiBwcm9ibGVtcz8NCg0KQW5zd2VyIHRoZSBxdWVzdGlvbg0KDQo1MCBYUA0KDQpQb3NzaWJsZSBBbnN3ZXJzDQoNCjEuIFByZWRpY3QgKHllcy9ubykgd2hldGhlciBhIHN0dWRlbnQgd2lsbCBwYXNzIHRoZSBmaW5hbCBleGFtLCBnaXZlbiBzY29yZXMgb24gbWlkdGVybXMgYW5kIGhvbWV3b3JrLg0KDQoyLiBQcmVkaWN0IHRoZSBzdHVkZW50J3Mgc2NvcmUgKDAgLSAxMDApIG9uIHRoZSBmaW5hbCBleGFtLCBnaXZlbiBzY29yZXMgb24gbWlkdGVybXMgYW5kIGhvbWV3b3JrLiBbYW5zXQ0KDQozLiBQcmVkaWN0IHRoZSBzdHVkZW50J3MgZmluYWwgZ3JhZGUgKEEsIEIsIEMsIEQsIEYpIGluIHRoZSBjbGFzcyBnaXZlbiBzY29yZXMgb24gbWlkdGVybXMgYW5kIGhvbWV3b3JrIChiZWZvcmUgdGhleSd2ZSB0YWtlbiB0aGUgZmluYWwgZXhhbSkuDQoNCiMjIDEuMjogQ29kZSBhIHNpbXBsZSBvbmUtdmFyaWFibGUgcmVncmVzc2lvbg0KDQpGb3IgdGhlIGZpcnN0IGNvZGluZyBleGVyY2lzZSwgeW91J2xsIGNyZWF0ZSBhIGZvcm11bGEgdG8gZGVmaW5lIGEgb25lLXZhcmlhYmxlIG1vZGVsaW5nIHRhc2ssIGFuZCB0aGVuIGZpdCBhIGxpbmVhciBtb2RlbCB0byB0aGUgZGF0YS4gWW91IGFyZSBnaXZlbiB0aGUgcmF0ZXMgb2YgbWFsZSBhbmQgZmVtYWxlIHVuZW1wbG95bWVudCBpbiB0aGUgVW5pdGVkIFN0YXRlcyBvdmVyIHNldmVyYWwgeWVhcnMgKFNvdXJjZSkuDQoNClRoZSB0YXNrIGlzIHRvIHByZWRpY3QgdGhlIHJhdGUgb2YgZmVtYWxlIHVuZW1wbG95bWVudCBmcm9tIHRoZSBvYnNlcnZlZCByYXRlIG9mIG1hbGUgdW5lbXBsb3ltZW50LiBUaGUgb3V0Y29tZSBpcyBmZW1hbGVfdW5lbXBsb3ltZW50LCBhbmQgdGhlIGlucHV0IGlzIG1hbGVfdW5lbXBsb3ltZW50Lg0KDQpUaGUgc2lnbiBvZiB0aGUgdmFyaWFibGUgY29lZmZpY2llbnQgdGVsbHMgeW91IHdoZXRoZXIgdGhlIG91dGNvbWUgaW5jcmVhc2VzICgrKSBvciBkZWNyZWFzZXMgKC0pIGFzIHRoZSB2YXJpYWJsZSBpbmNyZWFzZXMuDQoNClJlY2FsbCB0aGUgY2FsbGluZyBpbnRlcmZhY2UgZm9yIGxtKCkgaXM6DQoNCmxtKGZvcm11bGEsIGRhdGEgPSBfX18pDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgZGF0YSBmcmFtZSB1bmVtcGxveW1lbnQgaXMgaW4geW91ciB3b3Jrc3BhY2UuDQoNCi0gRGVmaW5lIGEgZm9ybXVsYSB0aGF0IGV4cHJlc3NlcyBmZW1hbGVfdW5lbXBsb3ltZW50IGFzIGEgZnVuY3Rpb24gb2YgbWFsZV91bmVtcGxveW1lbnQuIEFzc2lnbiB0aGUgZm9ybXVsYSB0byB0aGUgdmFyaWFibGUgZm1sYSBhbmQgcHJpbnQgaXQuDQoNCi0gVGhlbiB1c2UgbG0oKSBhbmQgZm1sYSB0byBmaXQgYSBsaW5lYXIgbW9kZWwgdG8gcHJlZGljdCBmZW1hbGUgdW5lbXBsb3ltZW50IGZyb20gbWFsZSB1bmVtcGxveW1lbnQgdXNpbmcgdGhlIGRhdGEgc2V0IHVuZW1wbG95bWVudC4NCg0KLSBQcmludCB0aGUgbW9kZWwuIElzIHRoZSBjb2VmZmljZW50IGZvciBtYWxlIHVuZW1wbG95bWVudCBjb25zaXN0ZW50IHdpdGggd2hhdCB5b3Ugd291bGQgZXhwZWN0PyBEb2VzIGZlbWFsZSB1bmVtcGxveW1lbnQgaW5jcmVhc2UgYXMgbWFsZSB1bmVtcGxveW1lbnQgZG9lcz8NCg0KYGBge3J9DQojIHVuZW1wbG95bWVudCBpcyBsb2FkZWQgaW4gdGhlIHdvcmtzcGFjZQ0KdW5lbXBsb3ltZW50PC1yZWFkUkRTKCJ1bmVtcGxveW1lbnQucmRzIikNCnN1bW1hcnkodW5lbXBsb3ltZW50KQ0KDQojIERlZmluZSBhIGZvcm11bGEgdG8gZXhwcmVzcyBmZW1hbGVfdW5lbXBsb3ltZW50IGFzIGEgZnVuY3Rpb24gb2YgbWFsZV91bmVtcGxveW1lbnQNCmZtbGEgPC0gZmVtYWxlX3VuZW1wbG95bWVudCB+IG1hbGVfdW5lbXBsb3ltZW50DQoNCiMgUHJpbnQgaXQNCmZtbGENCg0KIyBVc2UgdGhlIGZvcm11bGEgdG8gZml0IGEgbW9kZWw6IHVuZW1wbG95bWVudF9tb2RlbA0KdW5lbXBsb3ltZW50X21vZGVsIDwtIGxtKGZtbGEsIGRhdGEgPSB1bmVtcGxveW1lbnQpDQoNCiMgUHJpbnQgaXQNCnVuZW1wbG95bWVudF9tb2RlbA0KYGBgDQoNCiMjIDEuMzogRXhhbWluaW5nIGEgbW9kZWwNCg0KTGV0J3MgbG9vayBhdCB0aGUgbW9kZWwgdW5lbXBsb3ltZW50X21vZGVsIHRoYXQgeW91IGhhdmUganVzdCBjcmVhdGVkLiBUaGVyZSBhcmUgYSB2YXJpZXR5IG9mIGRpZmZlcmVudCB3YXlzIHRvIGV4YW1pbmUgYSBtb2RlbDsgZWFjaCB3YXkgcHJvdmlkZXMgZGlmZmVyZW50IGluZm9ybWF0aW9uLiBXZSB3aWxsIHVzZSBzdW1tYXJ5KCksIGJyb29tOjpnbGFuY2UoKSwgYW5kIHNpZ3I6OndyYXBGVGVzdCgpLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIG9iamVjdCB1bmVtcGxveW1lbnRfbW9kZWwgaXMgaW4geW91ciB3b3Jrc3BhY2UuDQoNCi0gUHJpbnQgdW5lbXBsb3ltZW50X21vZGVsIGFnYWluLiBXaGF0IGluZm9ybWF0aW9uIGRvZXMgaXQgcmVwb3J0Pw0KDQotIENhbGwgc3VtbWFyeSgpIG9uIHVuZW1wbG95bWVudF9tb2RlbC4gSW4gYWRkaXRpb24gdG8gdGhlIGNvZWZmaWNpZW50IHZhbHVlcywgeW91IGdldCBzdGFuZGFyZCBlcnJvcnMgb24gdGhlIGNvZWZmaWNpZW50IGVzdGltYXRlcywgYW5kIHNvbWUgZ29vZG5lc3Mtb2YtZml0IG1ldHJpY3MgbGlrZSBSLXNxdWFyZWQuDQoNCi0gQ2FsbCBnbGFuY2UoKSBvbiB0aGUgbW9kZWwgdG8gc2VlIHRoZSBwZXJmb3JtYW5jZSBtZXRyaWNzIGluIGFuIG9yZGVybHkgZGF0YSBmcmFtZS4gQ2FuIHlvdSBtYXRjaCB0aGUgaW5mb3JtYXRpb24gZnJvbSBzdW1tYXJ5KCkgdG8gdGhlIGNvbHVtbnMgb2YgZ2xhbmNlKCk/DQoNCi0gTm93IGNhbGwgd3JhcEZUZXN0KCkgb24gdGhlIG1vZGVsIHRvIHNlZSB0aGUgUi1zcXVhcmVkIGFnYWluLg0KDQpgYGB7cn0NCiMgYnJvb20gYW5kIHNpZ3IgYXJlIGFscmVhZHkgbG9hZGVkIGluIHlvdXIgd29ya3NwYWNlDQojIFByaW50IHVuZW1wbG95bWVudF9tb2RlbA0KaW5zdGFsbC5wYWNrYWdlcygibmxtZSIpDQppbnN0YWxsLnBhY2thZ2VzKCJicm9vbSIpDQppbnN0YWxsLnBhY2thZ2VzKCJzaWdyIikNCmxpYnJhcnkobmxtZSkNCmxpYnJhcnkoYnJvb20pDQpsaWJyYXJ5KHNpZ3IpDQp1bmVtcGxveW1lbnRfbW9kZWwNCg0KIyBDYWxsIHN1bW1hcnkoKSBvbiB1bmVtcGxveW1lbnRfbW9kZWwgdG8gZ2V0IG1vcmUgZGV0YWlscw0Kc3VtbWFyeSh1bmVtcGxveW1lbnRfbW9kZWwpDQoNCiMgQ2FsbCBnbGFuY2UoKSBvbiB1bmVtcGxveW1lbnRfbW9kZWwgdG8gc2VlIHRoZSBkZXRhaWxzIGluIGEgdGlkaWVyIGZvcm0NCmdsYW5jZSh1bmVtcGxveW1lbnRfbW9kZWwpDQoNCiMgQ2FsbCB3cmFwRlRlc3QoKSBvbiB1bmVtcGxveW1lbnRfbW9kZWwgdG8gc2VlIHRoZSBtb3N0IHJlbGV2YW50IGRldGFpbHMNCndyYXBGVGVzdCh1bmVtcGxveW1lbnRfbW9kZWwpDQpgYGANClJlbWFyazogaGVyZSBhcmUgc2V2ZXJhbCBkaWZmZXJlbnQgd2F5cyB0byBnZXQgZGlhZ25vc3RpY3MgZm9yIHlvdXIgbW9kZWwuIFVzZSB0aGUgb25lIHRoYXQgc3VpdHMgeW91ciBuZWVkcyBvciBwcmVmZXJlbmNlcyB0aGUgYmVzdC4NCg0KIyMgMS40OiBQcmVkaWN0aW5nIGZyb20gdGhlIHVuZW1wbG95bWVudCBtb2RlbA0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCB1c2UgeW91ciB1bmVtcGxveW1lbnQgbW9kZWwgdW5lbXBsb3ltZW50X21vZGVsIHRvIG1ha2UgcHJlZGljdGlvbnMgZnJvbSB0aGUgdW5lbXBsb3ltZW50IGRhdGEsIGFuZCBjb21wYXJlIHByZWRpY3RlZCBmZW1hbGUgdW5lbXBsb3ltZW50IHJhdGVzIHRvIHRoZSBhY3R1YWwgb2JzZXJ2ZWQgZmVtYWxlIHVuZW1wbG95bWVudCByYXRlcyBvbiB0aGUgdHJhaW5pbmcgZGF0YSwgdW5lbXBsb3ltZW50LiBZb3Ugd2lsbCBhbHNvIHVzZSB5b3VyIG1vZGVsIHRvIHByZWRpY3Qgb24gdGhlIG5ldyBkYXRhIGluIG5ld3JhdGVzLCB3aGljaCBjb25zaXN0cyBvZiBvbmx5IG9uZSBvYnNlcnZhdGlvbiwgd2hlcmUgbWFsZSB1bmVtcGxveW1lbnQgaXMgNSUuDQoNClRoZSBwcmVkaWN0KCkgaW50ZXJmYWNlIGZvciBsbSBtb2RlbHMgdGFrZXMgdGhlIGZvcm0NCg0KcHJlZGljdChtb2RlbCwgbmV3ZGF0YSkNCg0KWW91IHdpbGwgdXNlIHRoZSBnZ3Bsb3QyIHBhY2thZ2UgdG8gbWFrZSB0aGUgcGxvdHMsIHNvIHlvdSB3aWxsIGFkZCB0aGUgcHJlZGljdGlvbiBjb2x1bW4gdG8gdGhlIHVuZW1wbG95bWVudCBkYXRhIGZyYW1lLiBZb3Ugd2lsbCBwbG90IG91dGNvbWUgdmVyc3VzIHByZWRpY3Rpb24sIGFuZCBjb21wYXJlIHRoZW0gdG8gdGhlIGxpbmUgdGhhdCByZXByZXNlbnRzIHBlcmZlY3QgcHJlZGljdGlvbnMgKHRoYXQgaXMgd2hlbiB0aGUgb3V0Y29tZSBpcyBlcXVhbCB0byB0aGUgcHJlZGljdGVkIHZhbHVlKS4NCg0KVGhlIGdncGxvdDIgY29tbWFuZCB0byBwbG90IGEgc2NhdHRlcnBsb3Qgb2YgZGZyYW1lJG91dGNvbWUgdmVyc3VzIGRmcmFtZSRwcmVkIChwcmVkIG9uIHRoZSB4IGF4aXMsIG91dGNvbWUgb24gdGhlIHkgYXhpcyksIGFsb25nIHdpdGggYSBibHVlIGxpbmUgd2hlcmUgb3V0Y29tZSA9PSBwcmVkIGlzIGFzIGZvbGxvd3M6DQoNCmdncGxvdChkZnJhbWUsIGFlcyh4ID0gcHJlZCwgeSA9IG91dGNvbWUpKSArIA0KICAgICAgIGdlb21fcG9pbnQoKSArICANCiAgICAgICBnZW9tX2FibGluZShjb2xvciA9ICJibHVlIikNCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBvYmplY3RzIHVuZW1wbG95bWVudCwgdW5lbXBsb3ltZW50X21vZGVsIGFuZCBuZXdyYXRlcyBhcmUgaW4geW91ciB3b3Jrc3BhY2UuDQoNCi0gVXNlIHByZWRpY3QoKSB0byBwcmVkaWN0IGZlbWFsZSB1bmVtcGxveW1lbnQgcmF0ZXMgZnJvbSB0aGUgdW5lbXBsb3ltZW50IGRhdGEuIEFzc2lnbiBpdCB0byBhIG5ldyBjb2x1bW46IHByZWRpY3Rpb24uDQoNCi0gVXNlIHRoZSBsaWJyYXJ5KCkgY29tbWFuZCB0byBsb2FkIHRoZSBnZ3Bsb3QyIHBhY2thZ2UuDQoNCg0KLSBVc2UgZ2dwbG90KCkgdG8gY29tcGFyZSB0aGUgcHJlZGljdGlvbnMgdG8gYWN0dWFsIHVuZW1wbG95bWVudCByYXRlcy4gUHV0IHRoZSBwcmVkaWN0aW9ucyBvbiB0aGUgeCBheGlzLg0KSG93IGNsb3NlIGFyZSB0aGUgcmVzdWx0cyB0byB0aGUgbGluZSBvZiBwZXJmZWN0IHByZWRpY3Rpb24/DQoNCi0gVXNlIHRoZSBkYXRhIGZyYW1lIG5ld3JhdGVzIHRvIHByZWRpY3QgZXhwZWN0ZWQgZmVtYWxlIHVuZW1wbG95bWVudCByYXRlIHdoZW4gbWFsZSB1bmVtcGxveW1lbnQgaXMgNSUuIEFzc2lnbiB0aGUgYW5zd2VyIHRvIHRoZSB2YXJpYWJsZSBwcmVkIGFuZCBwcmludCBpdC4NCg0KYGBge3J9DQpuZXdyYXRlczwtcmVhZC5jc3YoIm5ld19yYXRlcy5jc3YiKQ0KDQojIHVuZW1wbG95bWVudCBpcyBpbiB5b3VyIHdvcmtzcGFjZQ0Kc3VtbWFyeSh1bmVtcGxveW1lbnQpDQoNCiMgbmV3cmF0ZXMgaXMgaW4geW91ciB3b3Jrc3BhY2UNCm5ld3JhdGVzDQoNCiMgUHJlZGljdCBmZW1hbGUgdW5lbXBsb3ltZW50IGluIHRoZSB1bmVtcGxveW1lbnQgZGF0YSBzZXQNCnVuZW1wbG95bWVudCRwcmVkaWN0aW9uIDwtICBwcmVkaWN0KHVuZW1wbG95bWVudF9tb2RlbCkNCg0KIyBsb2FkIHRoZSBnZ3Bsb3QyIHBhY2thZ2UNCmxpYnJhcnkoZ2dwbG90MikNCg0KIyBNYWtlIGEgcGxvdCB0byBjb21wYXJlIHByZWRpY3Rpb25zIHRvIGFjdHVhbCAocHJlZGljdGlvbiBvbiB4IGF4aXMpDQpnZ3Bsb3QodW5lbXBsb3ltZW50LCBhZXMoeCA9IHByZWRpY3Rpb24sIHkgPSBmZW1hbGVfdW5lbXBsb3ltZW50KSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9hYmxpbmUoY29sb3IgPSAiYmx1ZSIpDQoNCiMgUHJlZGljdCBmZW1hbGUgdW5lbXBsb3ltZW50IHJhdGUgd2hlbiBtYWxlIHVuZW1wbG95bWVudCBpcyA1JQ0KcHJlZCA8LSBwcmVkaWN0KHVuZW1wbG95bWVudF9tb2RlbCwgbmV3ZGF0YSA9IG5ld3JhdGVzKQ0KIyBQcmludCBpdA0KcHJlZA0KDQpgYGANCg0KIyMgMS41OiBNdWx0aXZhcmlhdGUgbGluZWFyIHJlZ3Jlc3Npb24gKFBhcnQgMSkNCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgd29yayB3aXRoIHRoZSBibG9vZCBwcmVzc3VyZSBkYXRhc2V0IChTb3VyY2UpLCBhbmQgbW9kZWwgYmxvb2RfcHJlc3N1cmUgYXMgYSBmdW5jdGlvbiBvZiB3ZWlnaHQgYW5kIGFnZS4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBkYXRhIGZyYW1lIGJsb29kcHJlc3N1cmUgaXMgaW4gdGhlIHdvcmtzcGFjZS4NCg0KLSBEZWZpbmUgYSBmb3JtdWxhIHRoYXQgZXhwcmVzc2VzIGJsb29kX3ByZXNzdXJlIGV4cGxpY2l0bHkgYXMgYSBmdW5jdGlvbiBvZiBhZ2UgYW5kIHdlaWdodC4gQXNzaWduIHRoZSBmb3JtdWxhIHRvIHRoZSB2YXJpYWJsZSBmbWxhIGFuZCBwcmludCBpdC4NCg0KLSBVc2UgZm1sYSB0byBmaXQgYSBsaW5lYXIgbW9kZWwgdG8gcHJlZGljdCBibG9vZF9wcmVzc3VyZSBmcm9tIGFnZSBhbmQgd2VpZ2h0IGluIHRoZSBkYXRhIHNldCBibG9vZHByZXNzdXJlLiBDYWxsIHRoZSBtb2RlbCBibG9vZHByZXNzdXJlX21vZGVsLg0KDQotIFByaW50IHRoZSBtb2RlbCBhbmQgY2FsbCBzdW1tYXJ5KCkgb24gaXQuIERvZXMgYmxvb2QgcHJlc3N1cmUgaW5jcmVhc2Ugb3IgZGVjcmVhc2Ugd2l0aCBhZ2U/IFdpdGggd2VpZ2h0Pw0KDQpgYGB7cn0NCiMgYmxvb2RwcmVzc3VyZSBpcyBpbiB0aGUgd29ya3NwYWNlDQpibG9vZHByZXNzdXJlPC1yZWFkUkRTKCJibG9vZHByZXNzdXJlLnJkcyIpDQpzdW1tYXJ5KGJsb29kcHJlc3N1cmUpDQoNCiMgQ3JlYXRlIHRoZSBmb3JtdWxhIGFuZCBwcmludCBpdA0KZm1sYSA8LSBibG9vZF9wcmVzc3VyZX5hZ2Urd2VpZ2h0DQpmbWxhDQoNCiMgRml0IHRoZSBtb2RlbDogYmxvb2RwcmVzc3VyZV9tb2RlbA0KYmxvb2RwcmVzc3VyZV9tb2RlbCA8LSBsbShmbWxhLGRhdGE9Ymxvb2RwcmVzc3VyZSkNCg0KIyBQcmludCBibG9vZHByZXNzdXJlX21vZGVsIGFuZCBjYWxsIHN1bW1hcnkoKSANCmJsb29kcHJlc3N1cmVfbW9kZWwNCnN1bW1hcnkoYmxvb2RwcmVzc3VyZV9tb2RlbCkNCmBgYA0KDQojIyAxLjY6IE11bHRpdmFyaWF0ZSBsaW5lYXIgcmVncmVzc2lvbiAoUGFydCAyKQ0KDQpOb3cgeW91IHdpbGwgbWFrZSBwcmVkaWN0aW9ucyB1c2luZyB0aGUgYmxvb2QgcHJlc3N1cmUgbW9kZWwgYmxvb2RwcmVzc3VyZV9tb2RlbCB0aGF0IHlvdSBmaXQgaW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLg0KDQpZb3Ugd2lsbCBhbHNvIGNvbXBhcmUgdGhlIHByZWRpY3Rpb25zIHRvIG91dGNvbWVzIGdyYXBoaWNhbGx5LiBnZ3Bsb3QyIGlzIGFscmVhZHkgbG9hZGVkIGluIHlvdXIgd29ya3NwYWNlLiBSZWNhbGwgdGhlIHBsb3QgY29tbWFuZCB0YWtlcyB0aGUgZm9ybToNCg0KZ2dwbG90KGRmcmFtZSwgYWVzKHggPSBwcmVkLCB5ID0gb3V0Y29tZSkpICsgDQogICAgIGdlb21fcG9pbnQoKSArIA0KICAgICBnZW9tX2FibGluZShjb2xvciA9ICJibHVlIikNCmANCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBvYmplY3RzIGJsb29kcHJlc3N1cmUgYW5kIGJsb29kcHJlc3N1cmVfbW9kZWwgYXJlIGluIHRoZSB3b3Jrc3BhY2UuDQoNCi0gVXNlIHByZWRpY3QoKSB0byBwcmVkaWN0IGJsb29kIHByZXNzdXJlIGluIHRoZSBibG9vZHByZXNzdXJlIGRhdGFzZXQuIEFzc2lnbiB0aGUgcHJlZGljdGlvbnMgdG8gdGhlIGNvbHVtbiBwcmVkaWN0aW9uLg0KDQotIEdyYXBoaWNhbGx5IGNvbXBhcmUgdGhlIHByZWRpY3Rpb25zIHRvIGFjdHVhbCBibG9vZCBwcmVzc3VyZXMuIFB1dCBwcmVkaWN0aW9ucyBvbiB0aGUgeCBheGlzLiBIb3cgY2xvc2UgYXJlIHRoZSByZXN1bHRzIHRvIHRoZSBsaW5lIG9mIHBlcmZlY3QgcHJlZGljdGlvbj8NCg0KYGBge3J9DQojIGJsb29kcHJlc3N1cmUgaXMgaW4geW91ciB3b3Jrc3BhY2UNCnN1bW1hcnkoYmxvb2RwcmVzc3VyZSkNCg0KIyBibG9vZHByZXNzdXJlX21vZGVsIGlzIGluIHlvdXIgd29ya3NwYWNlDQpibG9vZHByZXNzdXJlX21vZGVsDQoNCiMgcHJlZGljdCBibG9vZCBwcmVzc3VyZSB1c2luZyBibG9vZHByZXNzdXJlX21vZGVsIDpwcmVkaWN0aW9uDQpibG9vZHByZXNzdXJlJHByZWRpY3Rpb24gPC0gcHJlZGljdChibG9vZHByZXNzdXJlX21vZGVsKQ0KDQojIHBsb3QgdGhlIHJlc3VsdHMNCmdncGxvdChibG9vZHByZXNzdXJlLCBhZXMoeCA9IHByZWRpY3Rpb24sIHkgPSBibG9vZF9wcmVzc3VyZSkpKyANCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIGdlb21fYWJsaW5lKGNvbG9yID0gImJsdWUiKQ0KYGBgDQoNCiMgQ2hhcHRlciAyOiBUcmFpbmluZyBhbmQgRXZhbHVhdGluZyBSZWdyZXNzaW9uIE1vZGVscw0KDQpOb3cgdGhhdCB3ZSBoYXZlIGxlYXJuZWQgaG93IHRvIGZpdCBiYXNpYyBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMsIHdlIHdpbGwgbGVhcm4gaG93IHRvIGV2YWx1YXRlIGhvdyB3ZWxsIG91ciBtb2RlbHMgcGVyZm9ybS4gV2Ugd2lsbCByZXZpZXcgZXZhbHVhdGluZyBhIG1vZGVsIGdyYXBoaWNhbGx5LCBhbmQgbG9vayBhdCB0d28gYmFzaWMgbWV0cmljcyBmb3IgcmVncmVzc2lvbiBtb2RlbHMuIFdlIHdpbGwgYWxzbyBsZWFybiBob3cgdG8gdHJhaW4gYSBtb2RlbCB0aGF0IHdpbGwgcGVyZm9ybSB3ZWxsIGluIHRoZSB3aWxkLCBub3QganVzdCBvbiB0cmFpbmluZyBkYXRhLiBBbHRob3VnaCB3ZSB3aWxsIGRlbW9uc3RyYXRlIHRoZXNlIHRlY2huaXF1ZXMgdXNpbmcgbGluZWFyIHJlZ3Jlc3Npb24sIGFsbCB0aGVzZSBjb25jZXB0cyBhcHBseSB0byBtb2RlbHMgZml0IHdpdGggYW55IHJlZ3Jlc3Npb24gYWxnb3JpdGhtLg0KDQojIyAyLjE6IEdyYXBoaWNhbGx5IGV2YWx1YXRlIHRoZSB1bmVtcGxveW1lbnQgbW9kZWwNCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBncmFwaGljYWxseSBldmFsdWF0ZSB0aGUgdW5lbXBsb3ltZW50IG1vZGVsLCB1bmVtcGxveW1lbnRfbW9kZWwsIHRoYXQgeW91IGZpdCB0byB0aGUgdW5lbXBsb3ltZW50IGRhdGEgaW4gdGhlIHByZXZpb3VzIGNoYXB0ZXIuIFJlY2FsbCB0aGF0IHRoZSBtb2RlbCBwcmVkaWN0cyBmZW1hbGVfdW5lbXBsb3ltZW50IGZyb20gbWFsZV91bmVtcGxveW1lbnQuDQoNCllvdSB3aWxsIHBsb3QgdGhlIG1vZGVsJ3MgcHJlZGljdGlvbnMgYWdhaW5zdCB0aGUgYWN0dWFsIGZlbWFsZV91bmVtcGxveW1lbnQ7IHJlY2FsbCB0aGUgY29tbWFuZCBpcyBvZiB0aGUgZm9ybQ0KDQpnZ3Bsb3QoZGZyYW1lLCBhZXMoeCA9IHByZWQsIHkgPSBvdXRjb21lKSkgKyANCiAgICAgICBnZW9tX3BvaW50KCkgKyAgDQogICAgICAgZ2VvbV9hYmxpbmUoKQ0KVGhlbiB5b3Ugd2lsbCBjYWxjdWxhdGUgdGhlIHJlc2lkdWFsczoNCg0KcmVzaWR1YWxzIDwtIGFjdHVhbCBvdXRjb21lIC0gcHJlZGljdGVkIG91dGNvbWUNCmFuZCBwbG90IHByZWRpY3Rpb25zIGFnYWluc3QgcmVzaWR1YWxzLiBUaGUgcmVzaWR1YWwgZ3JhcGggd2lsbCB0YWtlIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGZvcm06IHlvdSBjb21wYXJlIHRoZSByZXNpZHVhbHMgdG8gdGhlIGhvcml6b25hbCBsaW5lIHg9MCAodXNpbmcgZ2VvbV9obGluZSgpKSByYXRoZXIgdGhhbiB0byB0aGUgbGluZSB4PXkuIFRoZSBjb21tYW5kIHdpbGwgYmUgcHJvdmlkZWQuDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgZGF0YSBmcmFtZSB1bmVtcGxveW1lbnQgYW5kIG1vZGVsIHVuZW1wbG95bWVudF9tb2RlbCBhcmUgYXZhaWxhYmxlIGluIHRoZSB3b3Jrc3BhY2UuDQoNCi0gVXNlIHByZWRpY3QoKSB0byBnZXQgdGhlIG1vZGVsIHByZWRpY3Rpb25zIGFuZCBhZGQgdGhlbSB0byB1bmVtcGxveW1lbnQgYXMgdGhlIGNvbHVtbiBwcmVkaWN0aW9ucy4NCg0KLSBQbG90IHByZWRpY3Rpb25zIChvbiB0aGUgeC1heGlzKSB2ZXJzdXMgYWN0dWFsIGZlbWFsZSB1bmVtcGxveW1lbnQgcmF0ZXMuIEFyZSB0aGUgcHJlZGljdGlvbnMgbmVhciB0aGUgeD15IGxpbmU/DQoNCi0gQ2FsY3VsYXRlIHRoZSByZXNpZHVhbHMgYmV0d2VlbiB0aGUgcHJlZGljdGlvbnMgYW5kIGFjdHVhbCB1bmVtcGxveW1lbnQgcmF0ZXMuIEFkZCB0aGVzZSByZXNpZHVhbHMgdG8gdW5lbXBsb3ltZW50IGFzIHRoZSBjb2x1bW4gcmVzaWR1YWxzLg0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBwbG90IHByZWRpY3Rpb25zIChvbiB0aGUgeC1heGlzKSB2ZXJzdXMgcmVzaWR1YWxzIChvbiB0aGUgeS1heGlzKS4gVGhpcyBnaXZlcyB5b3UgYSBkaWZmZXJlbnQgdmlldyBvZiB0aGUgbW9kZWwncyBwcmVkaWN0aW9ucyBhcyBjb21wYXJlZCB0byBncm91bmQgdHJ1dGguDQoNCmBgYHtyfQ0KIyB1bmVtcGxveW1lbnQgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeSh1bmVtcGxveW1lbnQpDQoNCiMgdW5lbXBsb3ltZW50X21vZGVsIGlzIGluIHRoZSB3b3Jrc3BhY2UNCnN1bW1hcnkodW5lbXBsb3ltZW50X21vZGVsKQ0KDQojIE1ha2UgcHJlZGljdGlvbnMgZnJvbSB0aGUgbW9kZWwNCnVuZW1wbG95bWVudCRwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KHVuZW1wbG95bWVudF9tb2RlbCkNCg0KIyBGaWxsIGluIHRoZSBibGFua3MgdG8gcGxvdCBwcmVkaWN0aW9ucyAob24geC1heGlzKSB2ZXJzdXMgdGhlIGZlbWFsZV91bmVtcGxveW1lbnQgcmF0ZXMNCmdncGxvdCh1bmVtcGxveW1lbnQsIGFlcyh4ID0gcHJlZGljdGlvbnMsIHkgPSBmZW1hbGVfdW5lbXBsb3ltZW50KSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fYWJsaW5lKCkNCg0KIyBDYWxjdWxhdGUgcmVzaWR1YWxzDQp1bmVtcGxveW1lbnQkcmVzaWR1YWxzIDwtIHVuZW1wbG95bWVudCRmZW1hbGVfdW5lbXBsb3ltZW50IC0gdW5lbXBsb3ltZW50JHByZWRpY3Rpb25zDQoNCiMgRmlsbCBpbiB0aGUgYmxhbmtzIHRvIHBsb3QgcHJlZGljdGlvbnMgKG9uIHgtYXhpcykgdmVyc3VzIHRoZSByZXNpZHVhbHMNCmdncGxvdCh1bmVtcGxveW1lbnQsIGFlcyh4ID0gcHJlZGljdGlvbnMsIHkgPSByZXNpZHVhbHMpKSArIA0KICBnZW9tX3BvaW50cmFuZ2UoYWVzKHltaW4gPSAwLCB5bWF4ID0gcmVzaWR1YWxzKSkgKyANCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAzKSArIA0KICBnZ3RpdGxlKCJyZXNpZHVhbHMgdnMuIGxpbmVhciBtb2RlbCBwcmVkaWN0aW9uIikNCmBgYA0KDQojIyAyLjI6IFRoZSBnYWluIGN1cnZlIHRvIGV2YWx1YXRlIHRoZSB1bmVtcGxveW1lbnQgbW9kZWwNCg0KSW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIHlvdSBtYWRlIHByZWRpY3Rpb25zIGFib3V0IGZlbWFsZV91bmVtcGxveW1lbnQgYW5kIHZpc3VhbGl6ZWQgdGhlIHByZWRpY3Rpb25zIGFuZCB0aGUgcmVzaWR1YWxzLiBOb3csIHlvdSB3aWxsIGFsc28gcGxvdCB0aGUgZ2FpbiBjdXJ2ZSBvZiB0aGUgdW5lbXBsb3ltZW50X21vZGVsJ3MgcHJlZGljdGlvbnMgYWdhaW5zdCBhY3R1YWwgZmVtYWxlX3VuZW1wbG95bWVudCB1c2luZyB0aGUgV1ZQbG90czo6R2FpbkN1cnZlUGxvdCgpIGZ1bmN0aW9uLg0KDQpGb3Igc2l0dWF0aW9ucyB3aGVyZSBvcmRlciBpcyBtb3JlIGltcG9ydGFudCB0aGFuIGV4YWN0IHZhbHVlcywgdGhlIGdhaW4gY3VydmUgaGVscHMgeW91IGNoZWNrIGlmIHRoZSBtb2RlbCdzIHByZWRpY3Rpb25zIHNvcnQgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlIHRydWUgb3V0Y29tZS4NCg0KQ2FsbHMgdG8gdGhlIGZ1bmN0aW9uIEdhaW5DdXJ2ZVBsb3QoKSBsb29rIGxpa2U6DQoNCkdhaW5DdXJ2ZVBsb3QoZnJhbWUsIHh2YXIsIHRydXRodmFyLCB0aXRsZSkNCg0Kd2hlcmUNCg0KLSBmcmFtZSBpcyBhIGRhdGEgZnJhbWUNCi0geHZhciBhbmQgdHJ1dGh2YXIgYXJlIHN0cmluZ3MgbmFtaW5nIHRoZSBwcmVkaWN0aW9uIGFuZCBhY3R1YWwgb3V0Y29tZSBjb2x1bW5zIG9mIGZyYW1lDQotIHRpdGxlIGlzIHRoZSB0aXRsZSBvZiB0aGUgcGxvdA0KDQpXaGVuIHRoZSBwcmVkaWN0aW9ucyBzb3J0IGluIGV4YWN0bHkgdGhlIHNhbWUgb3JkZXIsIHRoZSByZWxhdGl2ZSBHaW5pIGNvZWZmaWNpZW50IGlzIDEuIFdoZW4gdGhlIG1vZGVsIHNvcnRzIHBvb3JseSwgdGhlIHJlbGF0aXZlIEdpbmkgY29lZmZpY2llbnQgaXMgY2xvc2UgdG8gemVybywgb3IgZXZlbiBuZWdhdGl2ZS4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBkYXRhIGZyYW1lIHVuZW1wbG95bWVudCBhbmQgdGhlIG1vZGVsIHVuZW1wbG95bWVudF9tb2RlbCBhcmUgaW4gdGhlIHdvcmtzcGFjZS4NCg0KLSBMb2FkIHRoZSBwYWNrYWdlIFdWUGxvdHMuDQoNCi0gUGxvdCB0aGUgZ2FpbiBjdXJ2ZS4gR2l2ZSB0aGUgcGxvdCB0aGUgdGl0bGUgIlVuZW1wbG95bWVudCBtb2RlbCIuIERvIHRoZSBtb2RlbCdzIHByZWRpY3Rpb25zIHNvcnQgY29ycmVjdGx5Pw0KDQpgYGB7cn0NCiMgdW5lbXBsb3ltZW50IGlzIGluIHRoZSB3b3Jrc3BhY2UgKHdpdGggcHJlZGljdGlvbnMpDQpzdW1tYXJ5KHVuZW1wbG95bWVudCkNCg0KIyB1bmVtcGxveW1lbnRfbW9kZWwgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeSh1bmVtcGxveW1lbnRfbW9kZWwpDQoNCiMgTG9hZCB0aGUgcGFja2FnZSBXVlBsb3RzDQppbnN0YWxsLnBhY2thZ2VzKCJXVlBsb3RzIikNCmxpYnJhcnkoV1ZQbG90cykNCg0KIyBQbG90IHRoZSBHYWluIEN1cnZlDQpHYWluQ3VydmVQbG90KHVuZW1wbG95bWVudCwgInByZWRpY3Rpb25zIiwgImZlbWFsZV91bmVtcGxveW1lbnQiLCAiVW5lbXBsb3ltZW50IG1vZGVsIikNCmBgYA0KDQojIyAyLjM6IENhbGN1bGF0ZSBSTVNFDQoNCkluIHRoaXMgZXhlcmNpc2UgeW91IHdpbGwgY2FsY3VsYXRlIHRoZSBSTVNFIG9mIHlvdXIgdW5lbXBsb3ltZW50IG1vZGVsLiBJbiB0aGUgcHJldmlvdXMgY29kaW5nIGV4ZXJjaXNlcywgeW91IGFkZGVkIHR3byBjb2x1bW5zIHRvIHRoZSB1bmVtcGxveW1lbnQgZGF0YXNldDoNCg0KdGhlIG1vZGVsJ3MgcHJlZGljdGlvbnMgKHByZWRpY3Rpb25zIGNvbHVtbikNCnRoZSByZXNpZHVhbHMgYmV0d2VlbiB0aGUgcHJlZGljdGlvbnMgYW5kIHRoZSBvdXRjb21lIChyZXNpZHVhbHMgY29sdW1uKQ0KWW91IGNhbiBjYWxjdWxhdGUgdGhlIFJNU0UgZnJvbSBhIHZlY3RvciBvZiByZXNpZHVhbHMsIHJlcywgYXM6DQoNClJNU0U9c3FydHttZWFuKHJlcyAyKX0NCg0KWW91IHdhbnQgUk1TRSB0byBiZSBzbWFsbC4gSG93IHNtYWxsIGlzICJzbWFsbCI/IE9uZSBoZXVyaXN0aWMgaXMgdG8gY29tcGFyZSB0aGUgUk1TRSB0byB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBvdXRjb21lLiBXaXRoIGEgZ29vZCBtb2RlbCwgdGhlIFJNU0Ugc2hvdWxkIGJlIHNtYWxsZXIuDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgZGF0YSBmcmFtZSB1bmVtcGxveW1lbnQgaXMgaW4geW91ciB3b3Jrc3BhY2UuDQoNCi0gUmV2aWV3IHRoZSB1bmVtcGxveW1lbnQgZGF0YSBmcm9tIHRoZSBwcmV2aW91cyBleGVyY2lzZS4NCg0KLSBGb3IgY29udmVuaWVuY2UsIGFzc2lnbiB0aGUgcmVzaWR1YWxzIGNvbHVtbiBmcm9tIHVuZW1wbG95bWVudCB0byB0aGUgdmFyaWFibGUgcmVzLg0KDQotIENhbGN1bGF0ZSBSTVNFOiBzcXVhcmUgcmVzLCB0YWtlIGl0cyBtZWFuLCBhbmQgdGhlbiBzcXVhcmUgcm9vdCBpdC4gQXNzaWduIHRoaXMgdG8gdGhlIHZhcmlhYmxlIHJtc2UgYW5kIHByaW50IGl0Lg0KDQotIFRpcDogeW91IGNhbiBkbyB0aGlzIGluIG9uZSBzdGVwIGJ5IHdyYXBwaW5nIHRoZSBhc3NpZ25tZW50IGluIHBhcmVudGhlc2VzOiAocm1zZSA8LSBfX18pDQoNCi0gQ2FsY3VsYXRlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZmVtYWxlX3VuZW1wbG95bWVudCBhbmQgYXNzaWduIGl0IHRvIHRoZSB2YXJpYWJsZSBzZF91bmVtcGxveW1lbnQuDQoNCi0gUHJpbnQgaXQuIEhvdyBkb2VzIHRoZSBybXNlIG9mIHRoZSBtb2RlbCBjb21wYXJlIHRvIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIGRhdGE/DQoNCmBgYHtyfQ0KIyB1bmVtcGxveW1lbnQgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeSh1bmVtcGxveW1lbnQpDQoNCiMgRm9yIGNvbnZlbmllbmNlIHB1dCB0aGUgcmVzaWR1YWxzIGluIHRoZSB2YXJpYWJsZSByZXMNCnJlcyA8LSB1bmVtcGxveW1lbnQkcHJlZGljdGlvbnMtdW5lbXBsb3ltZW50JGZlbWFsZV91bmVtcGxveW1lbnQNCg0KIyBDYWxjdWxhdGUgUk1TRSwgYXNzaWduIGl0IHRvIHRoZSB2YXJpYWJsZSBybXNlIGFuZCBwcmludCBpdA0KKHJtc2UgPC0gc3FydChtZWFuKHJlc14yKSkpDQoNCiMgQ2FsY3VsYXRlIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgZmVtYWxlX3VuZW1wbG95bWVudCBhbmQgcHJpbnQgaXQNCihzZF91bmVtcGxveW1lbnQgPC0gc2QodW5lbXBsb3ltZW50JGZlbWFsZV91bmVtcGxveW1lbnQpKQ0Kc2RfdW5lbXBsb3ltZW50DQpgYGANCg0KIyMgMi40OiBDYWxjdWxhdGUgUi1TcXVhcmVkDQoNCk5vdyB0aGF0IHlvdSd2ZSBjYWxjdWxhdGVkIHRoZSBSTVNFIG9mIHlvdXIgbW9kZWwncyBwcmVkaWN0aW9ucywgeW91IHdpbGwgZXhhbWluZSBob3cgd2VsbCB0aGUgbW9kZWwgZml0cyB0aGUgZGF0YTogdGhhdCBpcywgaG93IG11Y2ggdmFyaWFuY2UgZG9lcyBpdCBleHBsYWluLiBZb3UgY2FuIGRvIHRoaXMgdXNpbmcgUjIuDQoNClN1cHBvc2UgeSBpcyB0aGUgdHJ1ZSBvdXRjb21lLCBwIGlzIHRoZSBwcmVkaWN0aW9uIGZyb20gdGhlIG1vZGVsLCBhbmQgcmVzPXniiJJwIGFyZSB0aGUgcmVzaWR1YWxzIG9mIHRoZSBwcmVkaWN0aW9ucy4NCg0KLSBUaGVuIHRoZSB0b3RhbCBzdW0gb2Ygc3F1YXJlcyB0c3MgKCJ0b3RhbCB2YXJpYW5jZSIpIG9mIHRoZSBkYXRhIGlzOiAkdHNzPVxzdW0gKHniiJJcYmFye3l9KV57Mn0kIHdoZXJlICRcYmFye3l9JCBpcyB0aGUgbWVhbiB2YWx1ZSBvZiB5Lg0KDQotIFRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlZCBlcnJvcnMgb2YgdGhlIG1vZGVsLCByc3MgaXM6ICRyc3M9XHN1bSByZXNeezJ9JA0KDQoNCi0gJFJeezJ9JCAoUi1TcXVhcmVkKSwgdGhlICJ2YXJpYW5jZSBleHBsYWluZWQiIGJ5IHRoZSBtb2RlbCwgaXMgdGhlbjogJDHiiJJcZnJhY3tyc3N9e3Rzc30kDQoNCkFmdGVyIHlvdSBjYWxjdWxhdGUgJFJeezJ9JCAsIHlvdSB3aWxsIGNvbXBhcmUgd2hhdCB5b3UgY29tcHV0ZWQgd2l0aCB0aGUgJFJeezJ9JCAgcmVwb3J0ZWQgYnkgZ2xhbmNlKCkuIGdsYW5jZSgpIHJldHVybnMgYSBvbmUtcm93IGRhdGEgZnJhbWU7IGZvciBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLCBvbmUgb2YgdGhlIGNvbHVtbnMgcmV0dXJuZWQgaXMgdGhlIFIyIG9mIHRoZSBtb2RlbCBvbiB0aGUgdHJhaW5pbmcgZGF0YS4NCg0KVGhlIGRhdGEgZnJhbWUgdW5lbXBsb3ltZW50IGlzIGluIHlvdXIgd29ya3NwYWNlLCB3aXRoIHRoZSBjb2x1bW5zIHByZWRpY3Rpb25zIGFuZCByZXNpZHVhbHMgdGhhdCB5b3UgY2FsY3VsYXRlZCBpbiBhIHByZXZpb3VzIGV4ZXJjaXNlLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIGRhdGEgZnJhbWUgdW5lbXBsb3ltZW50IGFuZCB0aGUgbW9kZWwgdW5lbXBsb3ltZW50X21vZGVsIGFyZSBpbiB0aGUgd29ya3NwYWNlLg0KDQotIENhbGN1bGF0ZSB0aGUgbWVhbiBmZW1hbGVfdW5lbXBsb3ltZW50IGFuZCBhc3NpZ24gaXQgdG8gdGhlIHZhcmlhYmxlIGZlX21lYW4uDQoNCi0gQ2FsY3VsYXRlIHRoZSB0b3RhbCBzdW0gb2Ygc3F1YXJlcyBhbmQgYXNzaWduIGl0IHRvIHRoZSB2YXJpYWJsZSB0c3MuDQoNCi0gQ2FsY3VsYXRlIHRoZSByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyBhbmQgYXNzaWduIGl0IHRvIHRoZSB2YXJpYWJsZSByc3MuDQoNCi0gQ2FsY3VsYXRlIFIyLiBJcyBpdCBhIGdvb2QgZml0IChSMiBuZWFyIDEpPw0KDQotIFVzZSBnbGFuY2UoKSB0byBnZXQgUjIgZnJvbSB0aGUgbW9kZWwuIElzIGl0IHRoZSBzYW1lIGFzIHdoYXQgeW91IGNhbGN1bGF0ZWQ/DQoNCg0KYGBge3J9DQojIHVuZW1wbG95bWVudCBpcyBpbiB5b3VyIHdvcmtzcGFjZQ0Kc3VtbWFyeSh1bmVtcGxveW1lbnQpDQoNCiMgdW5lbXBsb3ltZW50X21vZGVsIGlzIGluIHRoZSB3b3Jrc3BhY2UNCnN1bW1hcnkodW5lbXBsb3ltZW50X21vZGVsKQ0KDQojIENhbGN1bGF0ZSBtZWFuIGZlbWFsZV91bmVtcGxveW1lbnQ6IGZlX21lYW4uIFByaW50IGl0DQooZmVfbWVhbiA8LSBtZWFuKHVuZW1wbG95bWVudCRmZW1hbGVfdW5lbXBsb3ltZW50KSkNCmZlX21lYW4NCiMgQ2FsY3VsYXRlIHRvdGFsIHN1bSBvZiBzcXVhcmVzOiB0c3MuIFByaW50IGl0DQoodHNzIDwtIHN1bSgodW5lbXBsb3ltZW50JGZlbWFsZV91bmVtcGxveW1lbnQgLSBmZV9tZWFuKV4yKSkNCg0KIyBDYWxjdWxhdGUgcmVzaWR1YWwgc3VtIG9mIHNxdWFyZXM6IHJzcy4gUHJpbnQgaXQNCihyc3MgPC0gc3VtKCh1bmVtcGxveW1lbnQkZmVtYWxlX3VuZW1wbG95bWVudCAtIHVuZW1wbG95bWVudCRwcmVkaWN0aW9ucyleMikpDQoNCiMgQ2FsY3VsYXRlIFItc3F1YXJlZDogcnNxLiBQcmludCBpdC4gSXMgaXQgYSBnb29kIGZpdD8NCihyc3EgPC0gMS1yc3MvdHNzKQ0KDQojIEdldCBSLXNxdWFyZWQgZnJvbSBnbGFuY2UuIFByaW50IGl0DQoocnNxX2dsYW5jZSA8LSBnbGFuY2UodW5lbXBsb3ltZW50X21vZGVsKSRyLnNxdWFyZWQpDQpgYGANCg0KIyMgMi41OiBDb3JyZWxhdGlvbiBhbmQgUi1zcXVhcmVkDQoNClRoZSBsaW5lYXIgY29ycmVsYXRpb24gb2YgdHdvIHZhcmlhYmxlcywgeCBhbmQgeSwgbWVhc3VyZXMgdGhlIHN0cmVuZ3RoIG9mIHRoZSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlbS4gV2hlbiB4IGFuZCB5IGFyZSByZXNwZWN0aXZlbHk6DQoNCnRoZSBvdXRjb21lcyBvZiBhIHJlZ3Jlc3Npb24gbW9kZWwgdGhhdCBtaW5pbWl6ZXMgc3F1YXJlZC1lcnJvciAobGlrZSBsaW5lYXIgcmVncmVzc2lvbikgYW5kDQp0aGUgdHJ1ZSBvdXRjb21lcyBvZiB0aGUgdHJhaW5pbmcgZGF0YSwNCnRoZW4gdGhlIHNxdWFyZSBvZiB0aGUgY29ycmVsYXRpb24gaXMgdGhlIHNhbWUgYXMgUjIuIFlvdSB3aWxsIHZlcmlmeSB0aGF0IGluIHRoaXMgZXhlcmNpc2UuDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBVc2UgY29yKCkgdG8gZ2V0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBwcmVkaWN0aW9ucyBhbmQgZmVtYWxlIHVuZW1wbG95bWVudC4gQXNzaWduIGl0IHRvIHRoZSB2YXJpYWJsZSByaG8gYW5kIHByaW50IGl0LiBNYWtlIHN1cmUgeW91IHVzZSBQZWFyc29uIGNvcnJlbGF0aW9uICh0aGUgZGVmYXVsdCkuDQoNCi0gU3F1YXJlIHJobyBhbmQgYXNzaWduIGl0IHRvIHJobzIuIFByaW50IGl0Lg0KDQotIENvbXBhcmUgcmhvMiB0byBSMiBmcm9tIHRoZSBtb2RlbCAodXNpbmcgZ2xhbmNlKCkpLiBJcyBpdCB0aGUgc2FtZT8NCg0KYGBge3J9DQojIHVuZW1wbG95bWVudCBpcyBpbiB5b3VyIHdvcmtzcGFjZQ0Kc3VtbWFyeSh1bmVtcGxveW1lbnQpDQoNCiMgdW5lbXBsb3ltZW50X21vZGVsIGlzIGluIHRoZSB3b3Jrc3BhY2UNCnN1bW1hcnkodW5lbXBsb3ltZW50X21vZGVsKQ0KDQojIEdldCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgcHJlZGljdGlvbiBhbmQgdHJ1ZSBvdXRjb21lOiByaG8gYW5kIHByaW50IGl0DQoocmhvIDwtIGNvcih1bmVtcGxveW1lbnQkcHJlZGljdGlvbnMsIHVuZW1wbG95bWVudCRmZW1hbGVfdW5lbXBsb3ltZW50KSkNCg0KIyBTcXVhcmUgcmhvOiByaG8yIGFuZCBwcmludCBpdA0KKHJobzIgPC0gcmhvXjIpDQoNCiMgR2V0IFItc3F1YXJlZCBmcm9tIGdsYW5jZSBhbmQgcHJpbnQgaXQNCihyc3FfZ2xhbmNlIDwtIGdsYW5jZSh1bmVtcGxveW1lbnRfbW9kZWwpJHIuc3F1YXJlZCkNCmBgYA0KIyMgMi42OiBHZW5lcmF0aW5nIGEgcmFuZG9tIHRlc3QvdHJhaW4gc3BsaXQNCg0KRm9yIHRoZSBuZXh0IHNldmVyYWwgZXhlcmNpc2VzIHlvdSB3aWxsIHVzZSB0aGUgbXBnIGRhdGEgZnJvbSB0aGUgcGFja2FnZSBnZ3Bsb3QyLiBUaGUgZGF0YSBkZXNjcmliZXMgdGhlIGNoYXJhY3RlcmlzdGljcyBvZiBzZXZlcmFsIG1ha2VzIGFuZCBtb2RlbHMgb2YgY2FycyBmcm9tIGRpZmZlcmVudCB5ZWFycy4gVGhlIGdvYWwgaXMgdG8gcHJlZGljdCBjaXR5IGZ1ZWwgZWZmaWNpZW5jeSBmcm9tIGhpZ2h3YXkgZnVlbCBlZmZpY2llbmN5Lg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBzcGxpdCBtcGcgaW50byBhIHRyYWluaW5nIHNldCBtcGdfdHJhaW4gKDc1JSBvZiB0aGUgZGF0YSkgYW5kIGEgdGVzdCBzZXQgbXBnX3Rlc3QgKDI1JSBvZiB0aGUgZGF0YSkuIE9uZSB3YXkgdG8gZG8gdGhpcyBpcyB0byBnZW5lcmF0ZSBhIGNvbHVtbiBvZiB1bmlmb3JtIHJhbmRvbSBudW1iZXJzIGJldHdlZW4gMCBhbmQgMSwgdXNpbmcgdGhlIGZ1bmN0aW9uIHJ1bmlmKCkuDQoNCklmIHlvdSBoYXZlIGEgZGF0YSBzZXQgZGZyYW1lIG9mIHNpemUgTiwgYW5kIHlvdSB3YW50IGEgcmFuZG9tIHN1YnNldCBvZiBhcHByb3hpbWF0ZWx5IHNpemUgMTAw4oiXWCUgb2YgTiAod2hlcmUgWCBpcyBiZXR3ZWVuIDAgYW5kIDEpLCB0aGVuOg0KDQpHZW5lcmF0ZSBhIHZlY3RvciBvZiB1bmlmb3JtIHJhbmRvbSBudW1iZXJzOiBncCA9IHJ1bmlmKE4pLg0KZGZyYW1lW2dwIDwgWCxdIHdpbGwgYmUgYWJvdXQgdGhlIHJpZ2h0IHNpemUuDQpkZnJhbWVbZ3AgPj0gWCxdIHdpbGwgYmUgdGhlIGNvbXBsZW1lbnQuDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgZGF0YSBmcmFtZSBtcGcgaXMgaW4gdGhlIHdvcmtzcGFjZS4NCg0KLSBVc2UgdGhlIGZ1bmN0aW9uIG5yb3cgdG8gZ2V0IHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YSBmcmFtZSBtcGcuIEFzc2lnbiB0aGlzIGNvdW50IHRvIHRoZSB2YXJpYWJsZSBOIGFuZCBwcmludCBpdC4NCg0KLSBDYWxjdWxhdGUgYWJvdXQgaG93IG1hbnkgcm93cyA3NSUgb2YgTiBzaG91bGQgYmUuIEFzc2lnbiBpdCB0byB0aGUgdmFyaWFibGUgdGFyZ2V0IGFuZCBwcmludCBpdC4NCg0KLSBVc2UgcnVuaWYoKSB0byBnZW5lcmF0ZSBhIHZlY3RvciBvZiBOIHVuaWZvcm0gcmFuZG9tIG51bWJlcnMsIGNhbGxlZCBncC4NCg0KLVVzZSBncCB0byBzcGxpdCBtcGcgaW50byBtcGdfdHJhaW4gYW5kIG1wZ190ZXN0ICh3aXRoIG1wZ190cmFpbiBjb250YWluaW5nIGFwcHJveGltYXRlbHkgNzUlIG9mIHRoZSBkYXRhKS4NCg0KLSBVc2UgbnJvdygpIHRvIGNoZWNrIHRoZSBzaXplIG9mIG1wZ190cmFpbiBhbmQgbXBnX3Rlc3QuIEFyZSB0aGV5IGFib3V0IHRoZSByaWdodCBzaXplPw0KDQoNCg0KYGBge3J9DQojIG1wZyBpcyBpbiB0aGUgd29ya3NwYWNlDQpzdW1tYXJ5KG1wZykNCmRpbShtcGcpDQoNCiMgVXNlIG5yb3cgdG8gZ2V0IHRoZSBudW1iZXIgb2Ygcm93cyBpbiBtcGcgKE4pIGFuZCBwcmludCBpdA0KKE4gPC0gbnJvdyhtcGcpKQ0KDQojIENhbGN1bGF0ZSBob3cgbWFueSByb3dzIDc1JSBvZiBOIHNob3VsZCBiZSBhbmQgcHJpbnQgaXQNCiMgSGludDogdXNlIHJvdW5kKCkgdG8gZ2V0IGFuIGludGVnZXINCih0YXJnZXQgPC0gcm91bmQoMC43NSpOKSkNCg0KIyBDcmVhdGUgdGhlIHZlY3RvciBvZiBOIHVuaWZvcm0gcmFuZG9tIHZhcmlhYmxlczogZ3ANCmdwIDwtIHJ1bmlmKE4sIG1pbiA9IDAsIG1heCA9IDEpDQpzZWxlY3QudHJhaW5pbmc8LWdwPDAuNzUNCiMgVXNlIGdwIHRvIGNyZWF0ZSB0aGUgdHJhaW5pbmcgc2V0OiBtcGdfdHJhaW4gKDc1JSBvZiBkYXRhKSBhbmQgbXBnX3Rlc3QgKDI1JSBvZiBkYXRhKQ0KbXBnX3RyYWluIDwtIG1wZ1tzZWxlY3QudHJhaW5pbmcsXQ0KbXBnX3Rlc3QgPC0gbXBnWyFzZWxlY3QudHJhaW5pbmcsXQ0KDQojIFVzZSBucm93KCkgdG8gZXhhbWluZSBtcGdfdHJhaW4gYW5kIG1wZ190ZXN0DQpucm93KG1wZ190cmFpbikNCm5yb3cobXBnX3Rlc3QpDQpgYGANCg0KIyMgMi43OiBUcmFpbiBhIG1vZGVsIHVzaW5nIHRlc3QvdHJhaW4gc3BsaXQNCg0KTm93IHRoYXQgeW91IGhhdmUgc3BsaXQgdGhlIG1wZyBkYXRhc2V0IGludG8gbXBnX3RyYWluIGFuZCBtcGdfdGVzdCwgeW91IHdpbGwgdXNlIG1wZ190cmFpbiB0byB0cmFpbiBhIG1vZGVsIHRvIHByZWRpY3QgY2l0eSBmdWVsIGVmZmljaWVuY3kgKGN0eSkgZnJvbSBoaWdod2F5IGZ1ZWwgZWZmaWNpZW5jeSAoaHd5KS4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBkYXRhIGZyYW1lIG1wZ190cmFpbiBpcyBpbiB0aGUgd29ya3NwYWNlLg0KDQotIENyZWF0ZSBhIGZvcm11bGEgZm1sYSB0aGF0IGV4cHJlc3NlcyB0aGUgcmVsYXRpb25zaGlwIGN0eSBhcyBhIGZ1bmN0aW9uIG9mIGh3eS4gUHJpbnQgaXQuDQoNCi0gVHJhaW4gYSBtb2RlbCBtcGdfbW9kZWwgb24gbXBnX3RyYWluIHRvIHByZWRpY3QgY3R5IGZyb20gaHd5IHVzaW5nIGZtbGEgYW5kIGxtKCkuDQoNCi0gVXNlIHN1bW1hcnkoKSB0byBleGFtaW5lIHRoZSBtb2RlbC4NCg0KDQpgYGB7cn0NCiMgbXBnX3RyYWluIGlzIGluIHRoZSB3b3Jrc3BhY2UNCnN1bW1hcnkobXBnX3RyYWluKQ0KDQojIENyZWF0ZSBhIGZvcm11bGEgdG8gZXhwcmVzcyBjdHkgYXMgYSBmdW5jdGlvbiBvZiBod3k6IGZtbGEgYW5kIHByaW50IGl0Lg0KKGZtbGEgPC0gY3R5fmh3eSkNCg0KIyBOb3cgdXNlIGxtKCkgdG8gYnVpbGQgYSBtb2RlbCBtcGdfbW9kZWwgZnJvbSBtcGdfdHJhaW4gdGhhdCBwcmVkaWN0cyBjdHkgZnJvbSBod3kgDQptcGdfbW9kZWwgPC0gbG0oZm1sYSxkYXRhPW1wZ190cmFpbikNCg0KIyBVc2Ugc3VtbWFyeSgpIHRvIGV4YW1pbmUgdGhlIG1vZGVsDQpzdW1tYXJ5KG1wZ19tb2RlbCkNCmBgYA0KDQojIyAyLjg6IEV2YWx1YXRlIGEgbW9kZWwgdXNpbmcgdGVzdC90cmFpbiBzcGxpdA0KDQpOb3cgeW91IHdpbGwgdGVzdCB0aGUgbW9kZWwgbXBnX21vZGVsIG9uIHRoZSB0ZXN0IGRhdGEsIG1wZ190ZXN0LiBGdW5jdGlvbnMgcm1zZSgpIGFuZCByX3NxdWFyZWQoKSB0byBjYWxjdWxhdGUgUk1TRSBhbmQgUi1zcXVhcmVkIGhhdmUgYmVlbiBwcm92aWRlZCBmb3IgY29udmVuaWVuY2U6DQoNCnJtc2UocHJlZGNvbCwgeWNvbCkNCg0Kcl9zcXVhcmVkKHByZWRjb2wsIHljb2wpDQoNCndoZXJlOg0KDQotIHByZWRjb2w6IFRoZSBwcmVkaWN0ZWQgdmFsdWVzDQoNCi0geWNvbDogVGhlIGFjdHVhbCBvdXRjb21lDQoNCllvdSB3aWxsIGFsc28gcGxvdCB0aGUgcHJlZGljdGlvbnMgdnMuIHRoZSBvdXRjb21lLg0KDQpHZW5lcmFsbHksIG1vZGVsIHBlcmZvcm1hbmNlIGlzIGJldHRlciBvbiB0aGUgdHJhaW5pbmcgZGF0YSB0aGFuIHRoZSB0ZXN0IGRhdGEgKHRob3VnaCBzb21ldGltZXMgdGhlIHRlc3Qgc2V0ICJnZXRzIGx1Y2t5IikuIEEgc2xpZ2h0IGRpZmZlcmVuY2UgaW4gcGVyZm9ybWFuY2UgaXMgb2theTsgaWYgdGhlIHBlcmZvcm1hbmNlIG9uIHRyYWluaW5nIGlzIHNpZ25pZmljYW50bHkgYmV0dGVyLCB0aGVyZSBpcyBhIHByb2JsZW0uDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KVGhlIGRhdGEgZnJhbWVzIG1wZ190cmFpbiBhbmQgbXBnX3Rlc3QsIGFuZCB0aGUgbW9kZWwgbXBnX21vZGVsIGFyZSBpbiB0aGUgd29ya3NwYWNlLCBhbG9uZyB3aXRoIHRoZSBmdW5jdGlvbnMgcm1zZSgpIGFuZCByX3NxdWFyZWQoKS4NCg0KLSBQcmVkaWN0IGNpdHkgZnVlbCBlZmZpY2llbmN5IGZyb20gaHd5IG9uIHRoZSBtcGdfdHJhaW4gZGF0YS4gQXNzaWduIHRoZSBwcmVkaWN0aW9ucyB0byB0aGUgY29sdW1uIHByZWQuDQoNCi0gUHJlZGljdCBjaXR5IGZ1ZWwgZWZmaWNpZW5jeSBmcm9tIGh3eSBvbiB0aGUgbXBnX3Rlc3QgZGF0YS4gQXNzaWduIHRoZSBwcmVkaWN0aW9ucyB0byB0aGUgY29sdW1uIHByZWQuDQoNCi0gVXNlIHJtc2UoKSB0byBldmFsdWF0ZSBybXNlIGZvciBib3RoIHRoZSB0ZXN0IGFuZCB0cmFpbmluZyBzZXRzLiBDb21wYXJlLiBBcmUgdGhlIHBlcmZvcm1hbmNlcyBzaW1pbGFyPw0KDQotIERvIHRoZSBzYW1lIHdpdGggcl9zcXVhcmVkKCkuIEFyZSB0aGUgcGVyZm9ybWFuY2VzIHNpbWlsYXI/DQoNCi0gVXNlIGdncGxvdDIgdG8gcGxvdCB0aGUgcHJlZGljdGlvbnMgYWdhaW5zdCBjdHkgb24gdGhlIHRlc3QgZGF0YS4NCg0KYGBge3J9DQojIEV4YW1pbmUgdGhlIG9iamVjdHMgaW4gdGhlIHdvcmtzcGFjZQ0KI2xzLnN0cigpDQoNCiMgcHJlZGljdCBjdHkgZnJvbSBod3kgZm9yIHRoZSB0cmFpbmluZyBzZXQNCm1wZ190cmFpbiRwcmVkIDwtIHByZWRpY3QobXBnX21vZGVsKQ0KDQojIHByZWRpY3QgY3R5IGZyb20gaHd5IGZvciB0aGUgdGVzdCBzZXQNCm1wZ190ZXN0JHByZWQgPC0gcHJlZGljdChtcGdfbW9kZWwsbmV3ZGF0YT1tcGdfdGVzdCkNCg0KI2luc3RhbGwucGFja2FnZXMoIk1ldHJpY3MiKQ0KbGlicmFyeShNZXRyaWNzKQ0KDQojIEV2YWx1YXRlIHRoZSBybXNlIG9uIGJvdGggdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBhbmQgcHJpbnQgdGhlbQ0KKHJtc2VfdHJhaW4gPC0gcm1zZShtcGdfdHJhaW4kY3R5LG1wZ190cmFpbiRwcmVkKSkNCihybXNlX3Rlc3QgPC0gcm1zZShtcGdfdGVzdCRjdHksbXBnX3Rlc3QkcHJlZCkpDQoNCg0KIyBFdmFsdWF0ZSB0aGUgci1zcXVhcmVkIG9uIGJvdGggdHJhaW5pbmcgYW5kIHRlc3QgZGF0YS5hbmQgcHJpbnQgdGhlbQ0KIyhyc3FfdHJhaW4gPC0gcl9zcXVhcmVkKG1wZ190cmFpbiRwcmVkLG1wZ190cmFpbiRjdHkpKQ0KIyhyc3FfdGVzdCA8LSByX3NxdWFyZWQobXBnX3Rlc3QkcHJlZCxtcGdfdGVzdCRjdHkpKQ0KKHJzcV90cmFpbiA8LSAxLXNzZShtcGdfdHJhaW4kcHJlZCxtcGdfdHJhaW4kY3R5KS9zc2UobWVhbihtcGdfdHJhaW4kY3R5KSxtcGdfdHJhaW4kY3R5KSkNCihyc3FfdGVzdCA8LSAxLXNzZShtcGdfdGVzdCRwcmVkLG1wZ190ZXN0JGN0eSkvc3NlKG1lYW4obXBnX3Rlc3QkY3R5KSxtcGdfdGVzdCRjdHkpKQ0KIyBQbG90IHRoZSBwcmVkaWN0aW9ucyAob24gdGhlIHgtYXhpcykgYWdhaW5zdCB0aGUgb3V0Y29tZSAoY3R5KSBvbiB0aGUgdGVzdCBkYXRhDQpnZ3Bsb3QobXBnX3Rlc3QsIGFlcyh4ID0gcHJlZCwgeSA9IGN0eSkpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX2FibGluZSgpDQpgYGANCiMjIDIuOTogQ3JlYXRlIGEgY3Jvc3MgdmFsaWRhdGlvbiBwbGFuDQoNClRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gaW1wbGVtZW50IGFuIG4tZm9sZCBjcm9zcyB2YWxpZGF0aW9uIHBsYW4uIEluIHRoaXMgZXhlcmNpc2UgeW91IHdpbGwgY3JlYXRlIHN1Y2ggYSBwbGFuIHVzaW5nIHZ0cmVhdDo6a1dheUNyb3NzVmFsaWRhdGlvbigpLCBhbmQgZXhhbWluZSBpdC4NCg0Ka1dheUNyb3NzVmFsaWRhdGlvbigpIGNyZWF0ZXMgYSBjcm9zcyB2YWxpZGF0aW9uIHBsYW4gd2l0aCB0aGUgZm9sbG93aW5nIGNhbGw6DQoNCnNwbGl0UGxhbiA8LSBrV2F5Q3Jvc3NWYWxpZGF0aW9uKG5Sb3dzLCBuU3BsaXRzLCBkZnJhbWUsIHkpDQp3aGVyZSBuUm93cyBpcyB0aGUgbnVtYmVyIG9mIHJvd3Mgb2YgZGF0YSB0byBiZSBzcGxpdCwgYW5kIG5TcGxpdHMgaXMgdGhlIGRlc2lyZWQgbnVtYmVyIG9mIGNyb3NzLXZhbGlkYXRpb24gZm9sZHMuDQoNClN0cmljdGx5IHNwZWFraW5nLCBkZnJhbWUgYW5kIHkgYXJlbid0IHVzZWQgYnkga1dheUNyb3NzVmFsaWRhdGlvbjsgdGhleSBhcmUgdGhlcmUgZm9yIGNvbXBhdGliaWxpdHkgd2l0aCBvdGhlciB2dHJlYXQgZGF0YSBwYXJ0aXRpb25pbmcgZnVuY3Rpb25zLiBZb3UgY2FuIHNldCB0aGVtIGJvdGggdG8gTlVMTC4NCg0KVGhlIHJlc3VsdGluZyBzcGxpdFBsYW4gaXMgYSBsaXN0IG9mIG5TcGxpdHMgZWxlbWVudHM7IGVhY2ggZWxlbWVudCBjb250YWlucyB0d28gdmVjdG9yczoNCg0KdHJhaW46IHRoZSBpbmRpY2VzIG9mIGRmcmFtZSB0aGF0IHdpbGwgZm9ybSB0aGUgdHJhaW5pbmcgc2V0DQphcHA6IHRoZSBpbmRpY2VzIG9mIGRmcmFtZSB0aGF0IHdpbGwgZm9ybSB0aGUgdGVzdCAob3IgYXBwbGljYXRpb24pIHNldA0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBjcmVhdGUgYSAzLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbiBwbGFuIGZvciB0aGUgZGF0YSBzZXQgbXBnLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gTG9hZCB0aGUgcGFja2FnZSB2dHJlYXQuDQoNCi0gR2V0IHRoZSBudW1iZXIgb2Ygcm93cyBpbiBtcGcgYW5kIGFzc2lnbiBpdCB0byB0aGUgdmFyaWFibGUgblJvd3MuDQoNCi0gQ2FsbCBrV2F5Q3Jvc3NWYWxpZGF0aW9uIHRvIGNyZWF0ZSBhIDMtZm9sZCBjcm9zcyB2YWxpZGF0aW9uIHBsYW4gYW5kIGFzc2lnbiBpdCB0byB0aGUgdmFyaWFibGUgc3BsaXRQbGFuLiBZb3UgY2FuIHNldCB0aGUgbGFzdCB0d28gYXJndW1lbnRzIG9mIHRoZSBmdW5jdGlvbiB0byBOVUxMLg0KDQotIENhbGwgc3RyKCkgdG8gZXhhbWluZSB0aGUgc3RydWN0dXJlIG9mIHNwbGl0UGxhbi4NCg0KDQpgYGB7cn0NCiMgTG9hZCB0aGUgcGFja2FnZSB2dHJlYXQNCmluc3RhbGwucGFja2FnZXMoInZ0cmVhdCIpDQpsaWJyYXJ5KHZ0cmVhdCkNCg0KIyBtcGcgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeShtcGcpDQoNCiMgR2V0IHRoZSBudW1iZXIgb2Ygcm93cyBpbiBtcGcNCm5Sb3dzIDwtIG5yb3cobXBnKQ0KDQojIEltcGxlbWVudCB0aGUgMy1mb2xkIGNyb3NzLWZvbGQgcGxhbiB3aXRoIHZ0cmVhdA0Kc3BsaXRQbGFuIDwtIGtXYXlDcm9zc1ZhbGlkYXRpb24oblJvd3MsIDMsIE5VTEwsIE5VTEwpDQoNCiMgRXhhbWluZSB0aGUgc3BsaXQgcGxhbg0Kc3RyKHNwbGl0UGxhbikNCmBgYA0KDQojIyAyLjEwOiBFdmFsdWF0ZSBhIG1vZGVsaW5nIHByb2NlZHVyZSB1c2luZyBuLWZvbGQgY3Jvc3MtdmFsaWRhdGlvbg0KDQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSB3aWxsIHVzZSBzcGxpdFBsYW4sIHRoZSAzLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiBwbGFuIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCB0byBtYWtlIHByZWRpY3Rpb25zIGZyb20gYSBtb2RlbCB0aGF0IHByZWRpY3RzIG1wZyRjdHkgZnJvbSBtcGckaHd5Lg0KDQpJZiBkZnJhbWUgaXMgdGhlIHRyYWluaW5nIGRhdGEsIHRoZW4gb25lIHdheSB0byBhZGQgYSBjb2x1bW4gb2YgY3Jvc3MtdmFsaWRhdGlvbiBwcmVkaWN0aW9ucyB0byB0aGUgZnJhbWUgaXMgYXMgZm9sbG93czoNCg0KLSBJbml0aWFsaXplIGEgY29sdW1uIG9mIHRoZSBhcHByb3ByaWF0ZSBsZW5ndGgNCg0KZGZyYW1lJHByZWQuY3YgPC0gMCANCg0KLSBrIGlzIHRoZSBudW1iZXIgb2YgZm9sZHMNCg0KLSBzcGxpdFBsYW4gaXMgdGhlIGNyb3NzIHZhbGlkYXRpb24gcGxhbg0KDQpmb3IoaSBpbiAxOmspIHsNCiAgIyBHZXQgdGhlIGl0aCBzcGxpdA0KICBzcGxpdCA8LSBzcGxpdFBsYW5bW2ldXQ0KDQogICMgQnVpbGQgYSBtb2RlbCBvbiB0aGUgdHJhaW5pbmcgZGF0YSANCiAgIyBmcm9tIHRoaXMgc3BsaXQgDQogICMgKGxtLCBpbiB0aGlzIGNhc2UpDQogIG1vZGVsIDwtIGxtKGZtbGEsIGRhdGEgPSBkZnJhbWVbc3BsaXQkdHJhaW4sXSkNCg0KICAjIG1ha2UgcHJlZGljdGlvbnMgb24gdGhlIA0KICAjIGFwcGxpY2F0aW9uIGRhdGEgZnJvbSB0aGlzIHNwbGl0DQogIGRmcmFtZSRwcmVkLmN2W3NwbGl0JGFwcF0gPC0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YSA9IGRmcmFtZVtzcGxpdCRhcHAsXSkNCn0NCg0KDQpDcm9zcy12YWxpZGF0aW9uIHByZWRpY3RzIGhvdyB3ZWxsIGEgbW9kZWwgYnVpbHQgZnJvbSBhbGwgdGhlIGRhdGEgd2lsbCBwZXJmb3JtIG9uIG5ldyBkYXRhLiBBcyB3aXRoIHRoZSB0ZXN0L3RyYWluIHNwbGl0LCBmb3IgYSBnb29kIG1vZGVsaW5nIHByb2NlZHVyZSwgY3Jvc3MtdmFsaWRhdGlvbiBwZXJmb3JtYW5jZSBhbmQgdHJhaW5pbmcgcGVyZm9ybWFuY2Ugc2hvdWxkIGJlIGNsb3NlLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIGRhdGEgZnJhbWUgbXBnLCB0aGUgY3Jvc3MgdmFsaWRhdGlvbiBwbGFuIHNwbGl0UGxhbiwgYW5kIHRoZSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgUk1TRSAocm1zZSgpKSBmcm9tIG9uZSBvZiB0aGUgcHJldmlvdXMgZXhlcmNpc2VzIGlzIGF2YWlsYWJsZSBpbiB5b3VyIHdvcmtzcGFjZS4NCg0KLSBSdW4gdGhlIDMtZm9sZCBjcm9zcyB2YWxpZGF0aW9uIHBsYW4gZnJvbSBzcGxpdFBsYW4gYW5kIHB1dCB0aGUgcHJlZGljdGlvbnMgaW4gdGhlIGNvbHVtbiBtcGckcHJlZC5jdi4NCg0KLSBVc2UgbG0oKSBhbmQgdGhlIGZvcm11bGEgY3R5IH4gaHd5Lg0KDQotIENyZWF0ZSBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIG9uIGFsbCB0aGUgbXBnIGRhdGEgKGZvcm11bGEgY3R5IH4gaHd5KSBhbmQgYXNzaWduIHRoZSBwcmVkaWN0aW9ucyB0byANCm1wZyRwcmVkLg0KDQotIFVzZSBybXNlKCkgdG8gZ2V0IHRoZSByb290IG1lYW4gc3F1YXJlZCBlcnJvciBvZiB0aGUgcHJlZGljdGlvbnMgZnJvbSB0aGUgZnVsbCBtb2RlbCAobXBnJHByZWQpLiBSZWNhbGwgdGhhdCBybXNlKCkgdGFrZXMgdHdvIGFyZ3VtZW50cywgdGhlIHByZWRpY3RlZCB2YWx1ZXMsIGFuZCB0aGUgYWN0dWFsIG91dGNvbWUuDQoNCi0gR2V0IHRoZSByb290IG1lYW4gc3F1YXJlZCBlcnJvciBvZiB0aGUgY3Jvc3MtdmFsaWRhdGlvbiBwcmVkaWN0aW9ucy4gQXJlIHRoZSB0d28gdmFsdWVzIGFib3V0IHRoZSBzYW1lPw0KDQpgYGB7cn0NCiMgbXBnIGlzIGluIHRoZSB3b3Jrc3BhY2UNCnN1bW1hcnkobXBnKQ0KDQojIHNwbGl0UGxhbiBpcyBpbiB0aGUgd29ya3NwYWNlDQpzdHIoc3BsaXRQbGFuKQ0KDQojIFJ1biB0aGUgMy1mb2xkIGNyb3NzIHZhbGlkYXRpb24gcGxhbiBmcm9tIHNwbGl0UGxhbg0KayA8LSAzICMgTnVtYmVyIG9mIGZvbGRzDQptcGckcHJlZC5jdiA8LSAwIA0KZm9yKGkgaW4gMTprKSB7DQogIHNwbGl0IDwtIHNwbGl0UGxhbltbaV1dDQogIG1vZGVsIDwtIGxtKGN0eX5od3ksIGRhdGEgPSBtcGdbc3BsaXQkdHJhaW4sXSkNCiAgbXBnJHByZWQuY3Zbc3BsaXQkYXBwXSA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gbXBnW3NwbGl0JGFwcCxdKQ0KfQ0KDQojIFByZWRpY3QgZnJvbSBhIGZ1bGwgbW9kZWwNCm1wZyRwcmVkIDwtIHByZWRpY3QobG0oY3R5IH4gaHd5LCBkYXRhID0gbXBnKSkNCg0KIyBHZXQgdGhlIHJtc2Ugb2YgdGhlIGZ1bGwgbW9kZWwncyBwcmVkaWN0aW9ucw0Kcm1zZShtcGckcHJlZCwgbXBnJGN0eSkNCg0KIyBHZXQgdGhlIHJtc2Ugb2YgdGhlIGNyb3NzLXZhbGlkYXRpb24gcHJlZGljdGlvbnMNCnJtc2UobXBnJHByZWQuY3YsIG1wZyRjdHkpDQpgYGANCg0KIyBDaGFwdGVyIDM6IA0KDQojIyAzLjE6IEV4YW1pbmluZyB0aGUgc3RydWN0dXJlIG9mIGNhdGVnb3JpY2FsIGlucHV0cw0KDQpGb3IgdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBjYWxsIG1vZGVsLm1hdHJpeCgpIHRvIGV4YW1pbmUgaG93IFIgcmVwcmVzZW50cyBkYXRhIHdpdGggYm90aCBjYXRlZ29yaWNhbCBhbmQgbnVtZXJpY2FsIGlucHV0cyBmb3IgbW9kZWxpbmcuIFRoZSBkYXRhc2V0IGZsb3dlcnMgKGRlcml2ZWQgZnJvbSB0aGUgU2xldXRoMyBwYWNrYWdlKSBpcyBsb2FkZWQgaW50byB5b3VyIHdvcmtzcGFjZS4gSXQgaGFzIHRoZSBmb2xsb3dpbmcgY29sdW1uczoNCg0KRmxvd2VyczogdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGZsb3dlcnMgb24gYSBtZWFkb3dmb2FtIHBsYW50DQpJbnRlbnNpdHk6IHRoZSBpbnRlbnNpdHkgb2YgYSBsaWdodCB0cmVhdG1lbnQgYXBwbGllZCB0byB0aGUgcGxhbnQNClRpbWU6IEEgY2F0ZWdvcmljYWwgdmFyaWFibGUgLSB3aGVuIChMYXRlIG9yIEVhcmx5KSBpbiB0aGUgbGlmZWN5Y2xlIHRoZSBsaWdodCB0cmVhdG1lbnQgb2NjdXJyZWQNClRoZSB1bHRpbWF0ZSBnb2FsIGlzIHRvIHByZWRpY3QgRmxvd2VycyBhcyBhIGZ1bmN0aW9uIG9mIFRpbWUgYW5kIEludGVuc2l0eS4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjUwIFhQDQoNCg0KLSBUaGUgZGF0YSBmcmFtZSBmbG93ZXJzIGlzIGluIHlvdXIgd29ya3NwYWNlLg0KDQotIENhbGwgdGhlIHN0cigpIGZ1bmN0aW9uIG9uIGZsb3dlcnMgdG8gc2VlIHRoZSB0eXBlcyBvZiBlYWNoIGNvbHVtbi4NCg0KLSBVc2UgdGhlIHVuaXF1ZSgpIGZ1bmN0aW9uIG9uIHRoZSBjb2x1bW4gZmxvd2VycyRUaW1lIHRvIHNlZSB0aGUgcG9zc2libGUgdmFsdWVzIHRoYXQgVGltZSB0YWtlcy4gSG93IG1hbnkgdW5pcXVlIHZhbHVlcyBhcmUgdGhlcmU/DQoNCi0gQ3JlYXRlIGEgZm9ybXVsYSB0byBleHByZXNzIEZsb3dlcnMgYXMgYSBmdW5jdGlvbiBvZiBJbnRlbnNpdHkgYW5kIFRpbWUuIEFzc2lnbiBpdCB0byB0aGUgdmFyaWFibGUgZm1sYSBhbmQgcHJpbnQgaXQuDQoNCi0gVXNlIGZtbGEgYW5kIG1vZGVsLm1hdHJpeCgpIHRvIGNyZWF0ZSB0aGUgbW9kZWwgbWF0cml4IGZvciB0aGUgZGF0YSBmcmFtZSBmbG93ZXJzLiBBc3NpZ24gaXQgdG8gdGhlIHZhcmlhYmxlIG1tYXQuDQoNCi0gVXNlIGhlYWQoKSB0byBleGFtaW5lIHRoZSBmaXJzdCAyMCBsaW5lcyBvZiBmbG93ZXJzLg0KDQotIE5vdyBleGFtaW5lIHRoZSBmaXJzdCAyMCBsaW5lcyBvZiBtbWF0Lg0KDQotIElzIHRoZSBudW1lcmljIGNvbHVtbiBJbnRlbnNpdHkgZGlmZmVyZW50Pw0KDQotIFdoYXQgaGFwcGVuZWQgdG8gdGhlIGNhdGVnb3JpY2FsIGNvbHVtbiBUaW1lIGZyb20gZmxvd2Vycz8NCi0gSG93IGlzIFRpbWUgPT0gJ0Vhcmx5JyByZXByZXNlbnRlZD8gQW5kIFRpbWUgPSAnTGF0ZSc/DQoNCmBgYHtyfQ0KIyBDYWxsIHN0ciBvbiBmbG93ZXJzIHRvIHNlZSB0aGUgdHlwZXMgb2YgZWFjaCBjb2x1bW4NCg0KZmxvd2VyczwtcmVhZC5jc3YoImZsb3dlcnMuY3N2IikNCnN0cihmbG93ZXJzKQ0KDQojIFVzZSB1bmlxdWUoKSB0byBzZWUgaG93IG1hbnkgcG9zc2libGUgdmFsdWVzIFRpbWUgdGFrZXMNCnVuaXF1ZShmbG93ZXJzJFRpbWUpDQoNCiMgQnVpbGQgYSBmb3JtdWxhIHRvIGV4cHJlc3MgRmxvd2VycyBhcyBhIGZ1bmN0aW9uIG9mIEludGVuc2l0eSBhbmQgVGltZTogZm1sYS4gUHJpbnQgaXQNCihmbWxhIDwtIGFzLmZvcm11bGEoIkZsb3dlcnMgfiBJbnRlbnNpdHkgKyBUaW1lIikpDQoNCiMgVXNlIGZtbGEgYW5kIG1vZGVsLm1hdHJpeCB0byBzZWUgaG93IHRoZSBkYXRhIGlzIHJlcHJlc2VudGVkIGZvciBtb2RlbGluZw0KbW1hdCA8LSBtb2RlbC5tYXRyaXgoZm1sYSwgZGF0YSA9IGZsb3dlcnMpDQoNCiMgRXhhbWluZSB0aGUgZmlyc3QgMjAgbGluZXMgb2YgZmxvd2Vycw0KaGVhZChmbG93ZXJzLCBuPTIwKQ0KDQojIEV4YW1pbmUgdGhlIGZpcnN0IDIwIGxpbmVzIG9mIG1tYXQNCmhlYWQobW1hdCwgbj0yMCkNCmBgYA0KDQojIyAzLjM6IE1vZGVsaW5nIHdpdGggY2F0ZWdvcmljYWwgaW5wdXRzDQoNCkZvciB0aGlzIGV4ZXJjaXNlIHlvdSB3aWxsIGZpdCBhIGxpbmVhciBtb2RlbCB0byB0aGUgZmxvd2VycyBkYXRhLCB0byBwcmVkaWN0IEZsb3dlcnMgYXMgYSBmdW5jdGlvbiBvZiBUaW1lIGFuZCBJbnRlbnNpdHkuDQoNClRoZSBtb2RlbCBmb3JtdWxhIGZtbGEgdGhhdCB5b3UgY3JlYXRlZCBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UgaXMgc3RpbGwgaW4geW91ciB3b3Jrc3BhY2UsIGFzIGlzIHRoZSBtb2RlbCBtYXRyaXggbW1hdC4NCg0KSW5zdHJ1Y3Rpb25zDQoxMDAgWFANCg0KLSBVc2UgZm1sYSBhbmQgbG0gdG8gdHJhaW4gYSBsaW5lYXIgbW9kZWwgdGhhdCBwcmVkaWN0cyBGbG93ZXJzIGZyb20gSW50ZW5zaXR5IGFuZCBUaW1lLiBBc3NpZ24gdGhlIG1vZGVsIHRvIHRoZSB2YXJpYWJsZSBmbG93ZXJfbW9kZWwuDQoNCi0gVXNlIHN1bW1hcnkoKSB0byByZW1pbmQgeW91cnNlbGYgb2YgdGhlIHN0cnVjdHVyZSBvZiBtbWF0Lg0KDQotVXNlIHN1bW1hcnkoKSB0byBleGFtaW5lIHRoZSBmbG93ZXJfbW9kZWwuIERvIHRoZSB2YXJpYWJsZXMgbWF0Y2ggd2hhdCB5b3Ugc2F3IGluIG1tYXQ/DQoNCi0gVXNlIGZsb3dlcl9tb2RlbCB0byBwcmVkaWN0IHRoZSBudW1iZXIgb2YgZmxvd2Vycy4gQWRkIHRoZSBwcmVkaWN0aW9ucyB0byBmbG93ZXJzIGFzIHRoZSBjb2x1bW4gcHJlZGljdGlvbnMuDQoNCi0gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIHBsb3QgcHJlZGljdGlvbnMgdnMuIGFjdHVhbCBmbG93ZXJzIChwcmVkaWN0aW9ucyBvbiB0aGUgeC1heGlzKS4NCg0KYGBge3J9DQojIGZsb3dlcnMgaW4gaXMgdGhlIHdvcmtzcGFjZQ0Kc3RyKGZsb3dlcnMpDQoNCiMgZm1sYSBpcyBpbiB0aGUgd29ya3NwYWNlDQpmbWxhDQoNCiMgRml0IGEgbW9kZWwgdG8gcHJlZGljdCBGbG93ZXJzIGZyb20gSW50ZW5zaXR5IGFuZCBUaW1lIDogZmxvd2VyX21vZGVsDQpmbG93ZXJfbW9kZWwgPC0gbG0oZm1sYSxkYXRhPWZsb3dlcnMpDQoNCiMgVXNlIHN1bW1hcnkgb24gbW1hdCB0byByZW1pbmQgeW91cnNlbGYgb2YgaXRzIHN0cnVjdHVyZQ0Kc3VtbWFyeShtbWF0KQ0KDQojIFVzZSBzdW1tYXJ5IHRvIGV4YW1pbmUgZmxvd2VyX21vZGVsIA0Kc3VtbWFyeShmbG93ZXJfbW9kZWwpDQoNCiMgUHJlZGljdCB0aGUgbnVtYmVyIG9mIGZsb3dlcnMgb24gZWFjaCBwbGFudA0KZmxvd2VycyRwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGZsb3dlcl9tb2RlbCkNCg0KIyBQbG90IHByZWRpY3Rpb25zIHZzIGFjdHVhbCBmbG93ZXJzIChwcmVkaWN0aW9ucyBvbiB4LWF4aXMpDQpnZ3Bsb3QoZmxvd2VycywgYWVzKHggPSBwcmVkaWN0aW9ucywgeSA9IEZsb3dlcnMpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2FibGluZShjb2xvciA9ICJibHVlIikgDQpgYGANCg0KIyAzLjQ6IE1vZGVsaW5nIGFuIGludGVyYWN0aW9uDQoNCkluIHRoaXMgZXhlcmNpc2UgeW91IHdpbGwgdXNlIGludGVyYWN0aW9ucyB0byBtb2RlbCB0aGUgZWZmZWN0IG9mIGdlbmRlciBhbmQgZ2FzdHJpYyBhY3Rpdml0eSBvbiBhbGNvaG9sIG1ldGFib2xpc20uDQoNClRoZSBkYXRhIGZyYW1lIGFsY29ob2wgaGFzIGNvbHVtbnM6DQoNCjEuIE1ldGFib2w6IHRoZSBhbGNvaG9sIG1ldGFib2xpc20gcmF0ZQ0KDQoyLiBHYXN0cmljOiB0aGUgcmF0ZSBvZiBnYXN0cmljIGFsY29ob2wgZGVoeWRyb2dlbmFzZSBhY3Rpdml0eQ0KDQozLiBTZXg6IHRoZSBzZXggb2YgdGhlIGRyaW5rZXIgKE1hbGUgb3IgRmVtYWxlKQ0KDQpJbiB0aGUgdmlkZW8sIHdlIGZpdCB0aHJlZSBtb2RlbHMgdG8gdGhlIGFsY29ob2wgZGF0YToNCg0KLSBvbmUgd2l0aCBvbmx5IGFkZGl0aXZlIChtYWluIGVmZmVjdCkgdGVybXMgOiBNZXRhYm9sIH4gR2FzdHJpYyArIFNleA0KDQotIHR3byBtb2RlbHMsIGVhY2ggd2l0aCBpbnRlcmFjdGlvbnMgYmV0d2VlbiBnYXN0cmljIGFjdGl2aXR5IGFuZCBzZXgNCg0KV2Ugc2F3IHRoYXQgb25lIG9mIHRoZSBtb2RlbHMgd2l0aCBpbnRlcmFjdGlvbiB0ZXJtcyBoYWQgYSBiZXR0ZXIgUi1zcXVhcmVkIHRoYW4gdGhlIGFkZGl0aXZlIG1vZGVsLCBzdWdnZXN0aW5nIHRoYXQgdXNpbmcgaW50ZXJhY3Rpb24gdGVybXMgZ2l2ZXMgYSBiZXR0ZXIgZml0LiBJbiB0aGlzIGV4ZXJjaXNlIHdlIHdpbGwgY29tcGFyZSB0aGUgUi1zcXVhcmVkIG9mIG9uZSBvZiB0aGUgaW50ZXJhY3Rpb24gbW9kZWxzIHRvIHRoZSBtYWluLWVmZmVjdHMtb25seSBtb2RlbC4NCg0KUmVjYWxsIHRoYXQgdGhlIG9wZXJhdG9yIDogZGVzaWduYXRlcyB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiB0d28gdmFyaWFibGVzLiBUaGUgb3BlcmF0b3IgKiBkZXNpZ25hdGVzIHRoZSBpbnRlcmFjdGlvbiBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLCBwbHVzIHRoZSBtYWluIGVmZmVjdHMuDQoNCngqeSA9IHggKyB5ICsgeDp5DQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KDQotIFRoZSBkYXRhIGZyYW1lIGFsY29ob2wgaXMgaW4geW91ciB3b3Jrc3BhY2UuDQoNCi0gV3JpdGUgYSBmb3JtdWxhIHRoYXQgZXhwcmVzc2VzIE1ldGFib2wgYXMgYSBmdW5jdGlvbiBvZiBHYXN0cmljIGFuZCBTZXggd2l0aCBubyBpbnRlcmFjdGlvbnMuDQoNCi0gQXNzaWduIHRoZSBmb3JtdWxhIHRvIHRoZSB2YXJpYWJsZSBmbWxhX2FkZCBhbmQgcHJpbnQgaXQuDQoNCi0gV3JpdGUgYSBmb3JtdWxhIHRoYXQgZXhwcmVzc2VzIE1ldGFib2wgYXMgYSBmdW5jdGlvbiBvZiB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiBHYXN0cmljIGFuZCBTZXguIEFkZCBHYXN0cmljIGFzIGEgbWFpbiBlZmZlY3QsIGJ1dCBub3QgU2V4Lg0KDQotIEFzc2lnbiB0aGUgZm9ybXVsYSB0byB0aGUgdmFyaWFibGUgZm1sYV9pbnRlcmFjdGlvbiBhbmQgcHJpbnQgaXQuDQoNCi0gRml0IGEgbGluZWFyIG1vZGVsIHdpdGggb25seSBtYWluIGVmZmVjdHM6IG1vZGVsX2FkZCB0byB0aGUgZGF0YS4NCg0KLSBGaXQgYSBsaW5lYXIgbW9kZWwgd2l0aCB0aGUgaW50ZXJhY3Rpb246IG1vZGVsX2ludGVyYWN0aW9uIHRvIHRoZSBkYXRhLg0KDQotIENhbGwgc3VtbWFyeSgpIG9uIGJvdGggbW9kZWxzLiBXaGljaCBoYXMgYSBiZXR0ZXIgUi1zcXVhcmVkPw0KDQpgYGB7cn0NCiMgYWxjb2hvbCBpcyBpbiB0aGUgd29ya3NwYWNlDQphbGNvaG9sPC1yZWFkLmNzdigiYWxjb2hvbC5jc3YiKQ0Kc3VtbWFyeShhbGNvaG9sKQ0KDQojIENyZWF0ZSB0aGUgZm9ybXVsYSB3aXRoIG1haW4gZWZmZWN0cyBvbmx5DQooZm1sYV9hZGQgPC0gYXMuZm9ybXVsYShNZXRhYm9sfkdhc3RyaWMrU2V4KSkNCg0KIyBDcmVhdGUgdGhlIGZvcm11bGEgd2l0aCBpbnRlcmFjdGlvbnMNCihmbWxhX2ludGVyYWN0aW9uIDwtIGFzLmZvcm11bGEoTWV0YWJvbH5HYXN0cmljK0dhc3RyaWM6U2V4KSkNCiMgRml0IHRoZSBtYWluIGVmZmVjdHMgb25seSBtb2RlbA0KbW9kZWxfYWRkIDwtIGxtKGZtbGFfYWRkLGRhdGE9YWxjb2hvbCkNCg0KIyBGaXQgdGhlIGludGVyYWN0aW9uIG1vZGVsDQptb2RlbF9pbnRlcmFjdGlvbiA8LSBsbShmbWxhX2ludGVyYWN0aW9uLGRhdGE9YWxjb2hvbCkNCg0KIyBDYWxsIHN1bW1hcnkgb24gYm90aCBtb2RlbHMgYW5kIGNvbXBhcmUNCnN1bW1hcnkobW9kZWxfYWRkKQ0Kc3VtbWFyeShtb2RlbF9pbnRlcmFjdGlvbikNCmBgYA0KDQojIyAzLjU6IE1vZGVsaW5nIGFuIGludGVyYWN0aW9uICgyKQ0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjb21wYXJlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgaW50ZXJhY3Rpb24gbW9kZWwgeW91IGZpdCBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UgdG8gdGhlIHBlcmZvcm1hbmNlIG9mIGEgbWFpbi1lZmZlY3RzIG9ubHkgbW9kZWwuIEJlY2F1c2UgdGhpcyBkYXRhIHNldCBpcyBzbWFsbCwgd2Ugd2lsbCB1c2UgY3Jvc3MtdmFsaWRhdGlvbiB0byBzaW11bGF0ZSBtYWtpbmcgcHJlZGljdGlvbnMgb24gb3V0LW9mLXNhbXBsZSBkYXRhLg0KDQpZb3Ugd2lsbCBiZWdpbiB0byB1c2UgdGhlIGRwbHlyIHBhY2thZ2UgdG8gZG8gY2FsY3VsYXRpb25zLg0KDQptdXRhdGUoKSBhZGRzIG5ldyBjb2x1bW5zIHRvIGEgdGJsIChhIHR5cGUgb2YgZGF0YSBmcmFtZSkNCmdyb3VwX2J5KCkgc3BlY2lmaWVzIGhvdyByb3dzIGFyZSBncm91cGVkIGluIGEgdGJsDQpzdW1tYXJpemUoKSBjb21wdXRlcyBzdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgYSBjb2x1bW4NCllvdSB3aWxsIGFsc28gdXNlIHRpZHlyJ3MgZ2F0aGVyKCkgd2hpY2ggdGFrZXMgbXVsdGlwbGUgY29sdW1ucyBhbmQgY29sbGFwc2VzIHRoZW0gaW50byBrZXktdmFsdWUgcGFpcnMuDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgZGF0YSBmcmFtZSBhbGNvaG9sIGFuZCB0aGUgZm9ybXVsYXMgZm1sYV9hZGQgYW5kIGZtbGFfaW50ZXJhY3Rpb24gYXJlIGluIHRoZSB3b3Jrc3BhY2UuDQoNCi0gVXNlIGtXYXlDcm9zc1ZhbGlkYXRpb24oKSB0byBjcmVhdGUgYSBzcGxpdHRpbmcgcGxhbiBmb3IgYSAzLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbi4NCg0KLSBUaGUgZmlyc3QgYXJndW1lbnQgaXMgdGhlIG51bWJlciBvZiByb3dzIHRvIGJlIHNwbGl0Lg0KDQotIFRoZSBzZWNvbmQgYXJndW1lbnQgaXMgdGhlIG51bWJlciBvZiBmb2xkcyBmb3IgdGhlIGNyb3NzLXZhbGlkYXRpb24uDQoNCi0gWW91IGNhbiBzZXQgdGhlIDNyZCBhbmQgNHRoIGFyZ3VtZW50cyBvZiB0aGUgZnVuY3Rpb24gdG8gTlVMTC4NCg0KLSBFeGFtaW5lIGFuZCBydW4gdGhlIHNhbXBsZSBjb2RlIHRvIGdldCB0aGUgMy1mb2xkIGNyb3NzLXZhbGlkYXRpb24gcHJlZGljdGlvbnMgb2YgYSBtb2RlbCB3aXRoIG5vIGludGVyYWN0aW9ucyBhbmQgYXNzaWduIHRoZW0gdG8gdGhlIGNvbHVtbiBwcmVkX2FkZC4NCg0KLSBHZXQgdGhlIDMtZm9sZCBjcm9zcy12YWxpZGF0aW9uIHByZWRpY3Rpb25zIG9mIHRoZSBtb2RlbCB3aXRoIGludGVyYWN0aW9ucy4gQXNzaWduIHRoZSBwcmVkaWN0aW9ucyB0byB0aGUgY29sdW1uIHByZWRfaW50ZXJhY3Rpb24uDQoNCi0gVGhlIHNhbXBsZSBjb2RlIHNob3dzIHlvdSB0aGUgcHJvY2VkdXJlLg0KDQotIFVzZSB0aGUgc2FtZSBzcGxpdFBsYW4gdGhhdCB5b3UgYWxyZWFkeSBjcmVhdGVkLg0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0bw0KZ2F0aGVyIHRoZSBwcmVkaWN0aW9ucyBpbnRvIGEgc2luZ2xlIGNvbHVtbiBwcmVkLg0KDQotIGFkZCBhIGNvbHVtbiBvZiByZXNpZHVhbHMgKGFjdHVhbCBvdXRjb21lIC0gcHJlZGljdGVkIG91dGNvbWUpLg0KDQotIGdldCB0aGUgUk1TRSBvZiB0aGUgY3Jvc3MtdmFsaWRhdGlvbiBwcmVkaWN0aW9ucyBmb3IgZWFjaCBtb2RlbCB0eXBlLg0KDQotIENvbXBhcmUgdGhlIFJNU0VzLiBCYXNlZCBvbiB0aGVzZSByZXN1bHRzLCB3aGljaCBtb2RlbCBzaG91bGQgeW91IHVzZT8NCg0KYGBge3J9DQojIGFsY29ob2wgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeShhbGNvaG9sKQ0KDQojIEJvdGggdGhlIGZvcm11bGFlIGFyZSBpbiB0aGUgd29ya3NwYWNlDQpmbWxhX2FkZA0KZm1sYV9pbnRlcmFjdGlvbg0KDQojIENyZWF0ZSB0aGUgc3BsaXR0aW5nIHBsYW4gZm9yIDMtZm9sZCBjcm9zcyB2YWxpZGF0aW9uDQpzZXQuc2VlZCgzNDI0NSkgICMgc2V0IHRoZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNwbGl0UGxhbiA8LSBrV2F5Q3Jvc3NWYWxpZGF0aW9uKG5yb3coYWxjb2hvbCksIDMsIE5VTEwsIE5VTEwpDQoNCiMgU2FtcGxlIGNvZGU6IEdldCBjcm9zcy12YWwgcHJlZGljdGlvbnMgZm9yIG1haW4tZWZmZWN0cyBvbmx5IG1vZGVsDQphbGNvaG9sJHByZWRfYWRkIDwtIDAgICMgaW5pdGlhbGl6ZSB0aGUgcHJlZGljdGlvbiB2ZWN0b3INCmZvcihpIGluIDE6Mykgew0KICBzcGxpdCA8LSBzcGxpdFBsYW5bW2ldXQ0KICBtb2RlbF9hZGQgPC0gbG0oZm1sYV9hZGQsIGRhdGEgPSBhbGNvaG9sW3NwbGl0JHRyYWluLCBdKQ0KICBhbGNvaG9sJHByZWRfYWRkW3NwbGl0JGFwcF0gPC0gcHJlZGljdChtb2RlbF9hZGQsIG5ld2RhdGEgPSBhbGNvaG9sW3NwbGl0JGFwcCwgXSkNCn0NCg0KIyBHZXQgdGhlIGNyb3NzLXZhbCBwcmVkaWN0aW9ucyBmb3IgdGhlIG1vZGVsIHdpdGggaW50ZXJhY3Rpb25zDQphbGNvaG9sJHByZWRfaW50ZXJhY3Rpb24gPC0gMCAjIGluaXRpYWxpemUgdGhlIHByZWRpY3Rpb24gdmVjdG9yDQpmb3IoaSBpbiAxOjMpIHsNCiAgc3BsaXQgPC0gc3BsaXRQbGFuW1tpXV0NCiAgbW9kZWxfaW50ZXJhY3Rpb24gPC0gbG0oZm1sYV9pbnRlcmFjdGlvbiwgZGF0YSA9IGFsY29ob2xbc3BsaXQkdHJhaW4sXSkNCiAgYWxjb2hvbCRwcmVkX2ludGVyYWN0aW9uW3NwbGl0JGFwcF0gPC0gcHJlZGljdChtb2RlbF9pbnRlcmFjdGlvbiwgbmV3ZGF0YSA9IGFsY29ob2xbc3BsaXQkYXBwLCBdKQ0KfQ0KDQppbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIpDQpsaWJyYXJ5KG1hZ3JpdHRyKSAjIGZvciAlPiUgZmlsdGVyIGZ1bmN0aW9uDQoNCmxpYnJhcnkoJ3RpZHlyJykNCmV4YW1wbGUoJ2dhdGhlcicpDQojIEdldCBSTVNFDQphbGNvaG9sICU+JSANCiAgZ2F0aGVyKGtleSA9IG1vZGVsdHlwZSwgdmFsdWUgPSBwcmVkLCBwcmVkX2FkZCwgcHJlZF9pbnRlcmFjdGlvbikgJT4lDQogIG11dGF0ZShyZXNpZHVhbHMgPSBNZXRhYm9sLXByZWQpICU+JSAgICAgIA0KICBncm91cF9ieShtb2RlbHR5cGUpICU+JQ0KICBzdW1tYXJpemUocm1zZSA9IHNxcnQobWVhbihyZXNpZHVhbHNeMikpKQ0KYGBgDQojIyAzLjY6IFJlbGF0aXZlIGVycm9yDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGNvbXBhcmUgcmVsYXRpdmUgZXJyb3IgdG8gYWJzb2x1dGUgZXJyb3IuIEZvciB0aGUgcHVycG9zZXMgb2YgbW9kZWxpbmcsIHdlIHdpbGwgZGVmaW5lIHJlbGF0aXZlIGVycm9yIGFzDQoNCiRyZWw9XGZyYWN7KHByZWTiiJJ5KX17eX0kDQoNCnRoYXQgaXMsIHRoZSBlcnJvciBpcyByZWxhdGl2ZSB0byB0aGUgdHJ1ZSBvdXRjb21lLiBZb3Ugd2lsbCBtZWFzdXJlIHRoZSBvdmVyYWxsIHJlbGF0aXZlIGVycm9yIG9mIGEgbW9kZWwgdXNpbmcgcm9vdCBtZWFuIHNxdWFyZWQgcmVsYXRpdmUgZXJyb3I6DQoNCiRybXNlX3tyZWx9PVxzcXJ0e1xiYXJ7cmVsXnsyfX19JA0Kd2hlcmUgJFxiYXJ7cmVsXnsyfX0kIGlzIHRoZSBtZWFuIG9mICRyZWxeezJ9JC4NCg0KVGhlIGV4YW1wbGUgKHRveSkgZGF0YXNldCBmZGF0YSBpcyBsb2FkZWQgaW4geW91ciB3b3Jrc3BhY2UuIEl0IGluY2x1ZGVzIHRoZSBjb2x1bW5zOg0KDQotIHk6IHRoZSB0cnVlIG91dHB1dCB0byBiZSBwcmVkaWN0ZWQgYnkgc29tZSBtb2RlbDsgaW1hZ2luZSBpdCBpcyB0aGUgYW1vdW50IG9mIG1vbmV5IGEgY3VzdG9tZXIgd2lsbCBzcGVuZCBvbiBhIHZpc2l0IHRvIHlvdXIgc3RvcmUuDQoNCi0gcHJlZDogdGhlIHByZWRpY3Rpb25zIG9mIGEgbW9kZWwgdGhhdCBwcmVkaWN0cyB5Lg0KDQotIGxhYmVsOiBjYXRlZ29yaWNhbDogd2hldGhlciB5IGNvbWVzIGZyb20gYSBwb3B1bGF0aW9uIHRoYXQgbWFrZXMgc21hbGwgcHVyY2hhc2VzLCBvciBsYXJnZSBvbmVzLg0KDQpZb3Ugd2FudCB0byBrbm93IHdoaWNoIG1vZGVsIGRvZXMgImJldHRlciI6IHRoZSBvbmUgcHJlZGljdGluZyB0aGUgc21hbGwgcHVyY2hhc2VzLCBvciB0aGUgb25lIHByZWRpY3RpbmcgbGFyZ2Ugb25lcy4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBkYXRhIGZyYW1lIGZkYXRhIGlzIGluIHRoZSB3b3Jrc3BhY2UuDQoNCi0gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIGV4YW1pbmUgdGhlIGRhdGEuIE5vdGljZSB0aGF0IGxhcmdlIHB1cmNoYXNlcyB0ZW5kIHRvIGJlIGFib3V0IDEwMCB0aW1lcyBsYXJnZXIgdGhhbiBzbWFsbCBvbmVzLg0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBjcmVhdGUgZXJyb3IgY29sdW1uczoNCg0KLSBEZWZpbmUgcmVzaWR1YWwgYXMgcHJlZCAtIHkuDQoNCi0gRGVmaW5lIHJlbGF0aXZlIGVycm9yIGFzIHJlc2lkdWFsIC8geS4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gY2FsY3VsYXRlIGFuZCBjb21wYXJlIFJNU0UgYW5kIHJlbGF0aXZlIFJNU0UuDQoNCi0gSG93IGRvIHRoZSBhYnNvbHV0ZSBlcnJvcnMgY29tcGFyZT8gVGhlIHJlbGF0aXZlIGVycm9ycz8NCg0KLSBFeGFtaW5lIHRoZSBwbG90IG9mIHByZWRpY3Rpb25zIHZlcnN1cyBvdXRjb21lLiBJbiB5b3VyIG9waW5pb24sIHdoaWNoIG1vZGVsIGRvZXMgImJldHRlciI/DQoNCmBgYHtyfQ0KIyBmZGF0YSBpcyBpbiB0aGUgd29ya3NwYWNlDQpmZGF0YTwtcmVhZC5jc3YoImZkYXRhLmNzdiIpDQpzdW1tYXJ5KGZkYXRhKQ0KDQojIEV4YW1pbmUgdGhlIGRhdGE6IGdlbmVyYXRlIHRoZSBzdW1tYXJpZXMgZm9yIHRoZSBncm91cHMgbGFyZ2UgYW5kIHNtYWxsOg0KZmRhdGEgJT4lIA0KICAgIGdyb3VwX2J5KGxhYmVsKSAlPiUgICAgICMgZ3JvdXAgYnkgc21hbGwvbGFyZ2UgcHVyY2hhc2VzDQogICAgc3VtbWFyaXplKG1pbiAgPSBtaW4oeSksICAgIyBtaW4gb2YgeQ0KICAgICAgICAgICAgICBtZWFuID0gbWVhbih5KSwgICAjIG1lYW4gb2YgeQ0KICAgICAgICAgICAgICBtYXggID0gbWF4KHkpKSAgICMgbWF4IG9mIHkNCg0KIyBGaWxsIGluIHRoZSBibGFua3MgdG8gYWRkIGVycm9yIGNvbHVtbnMNCmZkYXRhMiA8LSBmZGF0YSAlPiUgDQogICAgICAgICBncm91cF9ieShsYWJlbCkgJT4lICAgICAgICMgZ3JvdXAgYnkgbGFiZWwNCiAgICAgICAgICAgbXV0YXRlKHJlc2lkdWFsID0gcHJlZC15LCAgIyBSZXNpZHVhbA0KICAgICAgICAgICAgICAgICAgcmVsZXJyICAgPSByZXNpZHVhbC95KSAgIyBSZWxhdGl2ZSBlcnJvcg0KDQojIENvbXBhcmUgdGhlIHJtc2UgYW5kIHJtc2UucmVsIG9mIHRoZSBsYXJnZSBhbmQgc21hbGwgZ3JvdXBzOg0KZmRhdGEyICU+JSANCiAgZ3JvdXBfYnkobGFiZWwpICU+JSANCiAgc3VtbWFyaXplKHJtc2UgICAgID0gc3FydChtZWFuKChwcmVkLXkpXjIpKSwgICAjIFJNU0UNCiAgICAgICAgICAgIHJtc2UucmVsID0gc3FydChtZWFuKCgocHJlZC15KS95KV4yKSkpICAgIyBSb290IG1lYW4gc3F1YXJlZCByZWxhdGl2ZSBlcnJvcg0KICAgICAgICAgICAgDQojIFBsb3QgdGhlIHByZWRpY3Rpb25zIGZvciBib3RoIGdyb3VwcyBvZiBwdXJjaGFzZXMNCmdncGxvdChmZGF0YTIsIGFlcyh4ID0gcHJlZCwgeSA9IHksIGNvbG9yID0gbGFiZWwpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9hYmxpbmUoKSArIA0KICBmYWNldF93cmFwKH4gbGFiZWwsIG5jb2wgPSAxLCBzY2FsZXMgPSAiZnJlZSIpICsgDQogIGdndGl0bGUoIk91dGNvbWUgdnMgcHJlZGljdGlvbiIpDQpgYGANClJlbWFyazogTm90aWNlIGZyb20gdGhpcyBleGFtcGxlIGhvdyBhIG1vZGVsIHdpdGggbGFyZ2VyIFJNU0UgbWlnaHQgc3RpbGwgYmUgYmV0dGVyLCBpZiByZWxhdGl2ZSBlcnJvcnMgYXJlIG1vcmUgaW1wb3J0YW50IHRoYW4gYWJzb2x1dGUgZXJyb3JzLg0KDQojIyAzLjc6IE1vZGVsaW5nIGxvZy10cmFuc2Zvcm1lZCBtb25ldGFyeSBvdXRwdXQNCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgcHJhY3RpY2UgbW9kZWxpbmcgb24gbG9nLXRyYW5zZm9ybWVkIG1vbmV0YXJ5IG91dHB1dCwgYW5kIHRoZW4gdHJhbnNmb3JtaW5nIHRoZSAibG9nLW1vbmV5IiBwcmVkaWN0aW9ucyBiYWNrIGludG8gbW9uZXRhcnkgdW5pdHMuIFRoZSBkYXRhIGxvYWRlZCBpbnRvIHlvdXIgd29ya3NwYWNlIHJlY29yZHMgc3ViamVjdHMnIGluY29tZXMgaW4gMjAwNSAoSW5jb21lMjAwNSksIGFzIHdlbGwgYXMgdGhlIHJlc3VsdHMgb2Ygc2V2ZXJhbCBhcHRpdHVkZSB0ZXN0cyB0YWtlbiBieSB0aGUgc3ViamVjdHMgaW4gMTk4MToNCg0KLSBBcml0aA0KDQotIFdvcmQNCg0KLSBQYXJhZw0KDQotIE1hdGgNCg0KLSBBRlFUIChQZXJjZW50aWxlIG9uIHRoZSBBcm1lZCBGb3JjZXMgUXVhbGlmeWluZyBUZXN0KQ0KDQpUaGUgZGF0YSBoYXZlIGFscmVhZHkgYmVlbiBzcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IHNldHMgKGluY29tZV90cmFpbiBhbmQgaW5jb21lX3Rlc3QgcmVzcGVjdGl2ZWx5KSBhbmQgYXJlIGluIHRoZSB3b3Jrc3BhY2UuIFlvdSB3aWxsIGJ1aWxkIGEgbW9kZWwgb2YgbG9nKGluY29tZSkgZnJvbSB0aGUgaW5wdXRzLCBhbmQgdGhlbiBjb252ZXJ0IGxvZyhpbmNvbWUpIGJhY2sgaW50byBpbmNvbWUuDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBDYWxsIHN1bW1hcnkoKSBvbiBpbmNvbWVfdHJhaW4kSW5jb21lMjAwNSB0byBzZWUgdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBvZiBpbmNvbWUgaW4gdGhlIHRyYWluaW5nIHNldC4NCg0KLSBXcml0ZSBhIGZvcm11bGEgdG8gZXhwcmVzcyBsb2coSW5jb21lMjAwNSkgYXMgYSBmdW5jdGlvbiBvZiB0aGUgZml2ZSB0ZXN0cyBhcyB0aGUgdmFyaWFibGUgZm1sYS5sb2cuIFByaW50IGl0Lg0KDQotIEZpdCBhIGxpbmVhciBtb2RlbCBvZiBsb2coSW5jb21lMjAwNSkgdG8gdGhlIGluY29tZV90cmFpbiBkYXRhOiBtb2RlbC5sb2cuDQoNCi0gVXNlIG1vZGVsLmxvZyB0byBwcmVkaWN0IGluY29tZSBvbiB0aGUgaW5jb21lX3Rlc3QgZGF0YXNldC4gUHV0IGl0IGluIHRoZSBjb2x1bW4gbG9ncHJlZC4NCg0KLSBDaGVjayBzdW1tYXJ5KCkgb2YgbG9ncHJlZCB0byBzZWUgdGhhdCB0aGUgbWFnbml0dWRlcyBhcmUgbXVjaCBkaWZmZXJlbnQgZnJvbSB0aG9zZSBvZiBJbmNvbWUyMDA1Lg0KDQotIFJldmVyc2UgdGhlIGxvZyB0cmFuc2Zvcm1hdGlvbiB0byBwdXQgdGhlIHByZWRpY3Rpb25zIGludG8gIm1vbmV0YXJ5IHVuaXRzIjogZXhwKGluY29tZV90ZXN0JGxvZ3ByZWQpLg0KDQotIENoZWNrIHN1bW1hcnkoKSBvZiBwcmVkLmluY29tZSBhbmQgc2VlIHRoYXQgdGhlIG1hZ25pdHVkZXMgYXJlIG5vdyBzaW1pbGFyIHRvIEluY29tZTIwMDUgbWFnbml0dWRlcy4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gcGxvdCBhIHNjYXR0ZXIgcGxvdCBvZiBwcmVkaWN0ZWQgaW5jb21lIHZzIGluY29tZSBvbiB0aGUgdGVzdCBzZXQuDQoNCmBgYHtyfQ0KIyBFeGFtaW5lIEluY29tZTIwMDUgaW4gdGhlIHRyYWluaW5nIHNldA0KaW5jb21lX3RyYWluPC1yZWFkLmNzdigiaW5jb21lX3RyYWluLmNzdiIpDQppbmNvbWVfdGVzdDwtcmVhZC5jc3YoImluY29tZV90ZXN0LmNzdiIpDQpzdW1tYXJ5KGluY29tZV90cmFpbiRJbmNvbWUyMDA1KQ0KDQojIFdyaXRlIHRoZSBmb3JtdWxhIGZvciBsb2cgaW5jb21lIGFzIGEgZnVuY3Rpb24gb2YgdGhlIHRlc3RzIGFuZCBwcmludCBpdA0KKGZtbGEubG9nIDwtIGxvZyhJbmNvbWUyMDA1KX5Bcml0aCtXb3JkK1BhcmFnK01hdGgrQUZRVCkNCg0KIyBGaXQgdGhlIGxpbmVhciBtb2RlbA0KbW9kZWwubG9nIDwtICBsbShmbWxhLmxvZyxkYXRhPWluY29tZV90cmFpbikNCg0KIyBNYWtlIHByZWRpY3Rpb25zIG9uIGluY29tZV90ZXN0DQppbmNvbWVfdGVzdCRsb2dwcmVkIDwtIHByZWRpY3QobW9kZWwubG9nLG5ld2RhdGE9aW5jb21lX3Rlc3QpDQpzdW1tYXJ5KGluY29tZV90ZXN0JGxvZ3ByZWQpDQoNCiMgQ29udmVydCB0aGUgcHJlZGljdGlvbnMgdG8gbW9uZXRhcnkgdW5pdHMNCmluY29tZV90ZXN0JHByZWQuaW5jb21lIDwtIGV4cChpbmNvbWVfdGVzdCRsb2dwcmVkKQ0Kc3VtbWFyeShpbmNvbWVfdGVzdCRwcmVkLmluY29tZSkNCg0KIyAgUGxvdCBwcmVkaWN0ZWQgaW5jb21lICh4IGF4aXMpIHZzIGluY29tZQ0KZ2dwbG90KGluY29tZV90ZXN0LCBhZXMoeCA9IHByZWQuaW5jb21lLCB5ID0gSW5jb21lMjAwNSkpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX2FibGluZShjb2xvciA9ICJibHVlIikNCmBgYA0KDQojIyAzLjg6IENvbXBhcmluZyBSTVNFIGFuZCByb290LW1lYW4tc3F1YXJlZCBSZWxhdGl2ZSBFcnJvcg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBzaG93IHRoYXQgbG9nLXRyYW5zZm9ybWluZyBhIG1vbmV0YXJ5IG91dHB1dCBiZWZvcmUgbW9kZWxpbmcgaW1wcm92ZXMgbWVhbiByZWxhdGl2ZSBlcnJvciAoYnV0IGluY3JlYXNlcyBSTVNFKSBjb21wYXJlZCB0byBtb2RlbGluZyB0aGUgbW9uZXRhcnkgb3V0cHV0IGRpcmVjdGx5LiBZb3Ugd2lsbCBjb21wYXJlIHRoZSByZXN1bHRzIG9mIG1vZGVsLmxvZyBmcm9tIHRoZSBwcmV2aW91cyBleGVyY2lzZSB0byBhIG1vZGVsIChtb2RlbC5hYnMpIHRoYXQgZGlyZWN0bHkgZml0cyBpbmNvbWUuDQoNClRoZSBpbmNvbWVfdHJhaW4gYW5kIGluY29tZV90ZXN0IGRhdGFzZXRzIGFyZSBsb2FkZWQgaW4geW91ciB3b3Jrc3BhY2UsIGFsb25nIHdpdGggeW91ciBtb2RlbCwgbW9kZWwubG9nLg0KDQpBbHNvIGluIHRoZSB3b3Jrc3BhY2U6DQoNCm1vZGVsLmFiczogYSBtb2RlbCB0aGF0IGRpcmVjdGx5IGZpdHMgaW5jb21lIHRvIHRoZSBpbnB1dHMgdXNpbmcgdGhlIGZvcm11bGENCg0KSW5jb21lMjAwNSB+IEFyaXRoICsgV29yZCArIFBhcmFnICsgTWF0aCArIEFGUVQNCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBhZGQgcHJlZGljdGlvbnMgZnJvbSB0aGUgbW9kZWxzIHRvIGluY29tZV90ZXN0Lg0KDQotIERvbuKAmXQgZm9yZ2V0IHRvIHRha2UgdGhlIGV4cG9uZW50IG9mIHRoZSBwcmVkaWN0aW9ucyBmcm9tIG1vZGVsLmxvZyB0byB1bmRvIHRoZSBsb2cgdHJhbnNmb3JtIQ0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBnYXRoZXIoKSB0aGUgcHJlZGljdGlvbnMgYW5kIGNhbGN1bGF0ZSB0aGUgcmVzaWR1YWxzIGFuZCByZWxhdGl2ZSBlcnJvci4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gY2FsY3VsYXRlIHRoZSBSTVNFIGFuZCByZWxhdGl2ZSBSTVNFIGZvciBwcmVkaWN0aW9ucy4NCg0KLSBXaGljaCBtb2RlbCBoYXMgbGFyZ2VyIGFic29sdXRlIGVycm9yPyBMYXJnZXIgcmVsYXRpdmUgZXJyb3I/DQoNCmBgYHtyfQ0KIyBmbWxhLmFicyBpcyBpbiB0aGUgd29ya3NwYWNlDQpmbWxhLmFiczwtYXMuZm9ybXVsYShJbmNvbWUyMDA1IH4gQXJpdGggKyBXb3JkICsgUGFyYWcgKyBNYXRoICsgQUZRVCkNCm1vZGVsLmFiczwtbG0oZm9ybXVsYSA9IGZtbGEuYWJzLCBkYXRhID0gaW5jb21lX3RyYWluKQ0KIyBtb2RlbC5hYnMgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeShtb2RlbC5hYnMpDQoNCiMgQWRkIHByZWRpY3Rpb25zIHRvIHRoZSB0ZXN0IHNldA0KaW5jb21lX3Rlc3QgPC0gaW5jb21lX3Rlc3QgJT4lDQogIG11dGF0ZShwcmVkLmFic21vZGVsID0gcHJlZGljdChtb2RlbC5hYnMsIGluY29tZV90ZXN0KSwgICAgICAgICMgcHJlZGljdGlvbnMgZnJvbSBtb2RlbC5hYnMNCiAgICAgICAgIHByZWQubG9nbW9kZWwgPSBleHAocHJlZGljdChtb2RlbC5sb2csIGluY29tZV90ZXN0KSkpICAgIyBwcmVkaWN0aW9ucyBmcm9tIG1vZGVsLmxvZw0KDQojIEdhdGhlciB0aGUgcHJlZGljdGlvbnMgYW5kIGNhbGN1bGF0ZSByZXNpZHVhbHMgYW5kIHJlbGF0aXZlIGVycm9yDQppbmNvbWVfbG9uZyA8LSBpbmNvbWVfdGVzdCAlPiUgDQogIGdhdGhlcihrZXkgPSBtb2RlbHR5cGUsIHZhbHVlID0gcHJlZCwgcHJlZC5hYnNtb2RlbCwgcHJlZC5sb2dtb2RlbCkgJT4lDQogIG11dGF0ZShyZXNpZHVhbCA9IHByZWQtSW5jb21lMjAwNSwgICAjIHJlc2lkdWFscw0KICAgICAgICAgcmVsZXJyICAgPSByZXNpZHVhbC9JbmNvbWUyMDA1KSAgICMgcmVsYXRpdmUgZXJyb3INCg0KIyBDYWxjdWxhdGUgUk1TRSBhbmQgcmVsYXRpdmUgUk1TRSBhbmQgY29tcGFyZQ0KaW5jb21lX2xvbmcgJT4lIA0KICBncm91cF9ieShtb2RlbHR5cGUpICU+JSAgICAgICMgZ3JvdXAgYnkgbW9kZWx0eXBlDQogIHN1bW1hcml6ZShybXNlICAgICA9IHNxcnQobWVhbihyZXNpZHVhbF4yKSksICAgICMgUk1TRQ0KICAgICAgICAgICAgcm1zZS5yZWwgPSBzcXJ0KG1lYW4ocmVsZXJyXjIpKSkgICAgIyBSb290IG1lYW4gc3F1YXJlZCByZWxhdGl2ZSBlcnJvcg0KYGBgDQoNCiMjIDMuOTogSW5wdXQgdHJhbnNmb3JtczogdGhlICJob2NrZXkgc3RpY2siDQoNCkluIHRoaXMgZXhlcmNpc2UsIHdlIHdpbGwgYnVpbGQgYSBtb2RlbCB0byBwcmVkaWN0IHByaWNlIGZyb20gYSBtZWFzdXJlIG9mIHRoZSBob3VzZSdzIHNpemUgKHN1cmZhY2UgYXJlYSkuIFRoZSBkYXRhIHNldCBob3VzZXByaWNlIGhhcyB0aGUgY29sdW1uczoNCg0KLSBwcmljZSA6IGhvdXNlIHByaWNlIGluIHVuaXRzIG9mICQxMDAwDQoNCi0gc2l6ZTogc3VyZmFjZSBhcmVhDQoNCkEgc2NhdHRlcnBsb3Qgb2YgdGhlIGRhdGEgc2hvd3MgdGhhdCB0aGUgZGF0YSBpcyBxdWl0ZSBub24tbGluZWFyOiBhIHNvcnQgb2YgImhvY2tleS1zdGljayIgd2hlcmUgcHJpY2UgaXMgZmFpcmx5IGZsYXQgZm9yIHNtYWxsZXIgaG91c2VzLCBidXQgcmlzZXMgc3RlZXBseSBhcyB0aGUgaG91c2UgZ2V0cyBsYXJnZXIuIFF1YWRyYXRpY3MgYW5kIHRyaXRpY3MgYXJlIG9mdGVuIGdvb2QgZnVuY3Rpb25hbCBmb3JtcyB0byBleHByZXNzIGhvY2tleS1zdGljayBsaWtlIHJlbGF0aW9uc2hpcHMuIE5vdGUgdGhhdCB0aGVyZSBtYXkgbm90IGJlIGEgInBoeXNpY2FsIiByZWFzb24gdGhhdCBwcmljZSBpcyByZWxhdGVkIHRvIHRoZSBzcXVhcmUgb2YgdGhlIHNpemU7IGEgcXVhZHJhdGljIGlzIHNpbXBseSBhIGNsb3NlZCBmb3JtIGFwcHJveGltYXRpb24gb2YgdGhlIG9ic2VydmVkIHJlbGF0aW9uc2hpcC4NCg0Kc2NhdHRlcnBsb3QNCg0KWW91IHdpbGwgZml0IGEgbW9kZWwgdG8gcHJlZGljdCBwcmljZSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBzcXVhcmVkIHNpemUsIGFuZCBsb29rIGF0IGl0cyBmaXQgb24gdGhlIHRyYWluaW5nIGRhdGEuDQoNCkJlY2F1c2UgXiBpcyBhbHNvIGEgc3ltYm9sIHRvIGV4cHJlc3MgaW50ZXJhY3Rpb25zLCB1c2UgdGhlIGZ1bmN0aW9uIEkoKSB0byB0cmVhdCB0aGUgZXhwcmVzc2lvbiB4XjIg4oCcYXMgaXPigJ06IHRoYXQgaXMsIGFzIHRoZSBzcXVhcmUgb2YgeCByYXRoZXIgdGhhbiB0aGUgaW50ZXJhY3Rpb24gb2YgeCB3aXRoIGl0c2VsZi4NCg0KZXhhbXBsZUZvcm11bGEgPSB5IH4gSSh4XjIpDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgZGF0YSBzZXQgaG91c2VwcmljZSBpcyBpbiB0aGUgd29ya3NwYWNlLg0KDQotIFdyaXRlIGEgZm9ybXVsYSwgZm1sYV9zcXIsIHRvIGV4cHJlc3MgcHJpY2UgYXMgYSBmdW5jdGlvbiBvZiBzcXVhcmVkIHNpemUuIFByaW50IGl0Lg0KDQotIEZpdCBhIG1vZGVsIG1vZGVsX3NxciB0byB0aGUgZGF0YSB1c2luZyBmbWxhX3Nxcg0KDQotIEZvciBjb21wYXJpc29uLCBmaXQgYSBsaW5lYXIgbW9kZWwgbW9kZWxfbGluIHRvIHRoZSBkYXRhIHVzaW5nIHRoZSBmb3JtdWxhIHByaWNlIH4gc2l6ZS4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gbWFrZSBwcmVkaWN0aW9ucyBmcm9tIHRoZSB0cmFpbmluZyBkYXRhIGZyb20gdGhlIHR3byBtb2RlbHMsIA0KDQotIGdhdGhlciB0aGUgcHJlZGljdGlvbnMgaW50byBhIHNpbmdsZSBjb2x1bW4gcHJlZA0KDQotIGdyYXBoaWNhbGx5IGNvbXBhcmUgdGhlIHByZWRpY3Rpb25zIG9mIHRoZSB0d28gbW9kZWxzIHRvIHRoZSBkYXRhLiBXaGljaCBmaXRzIGJldHRlcj8NCg0KYGBge3J9DQojIGhvdXNlcHJpY2UgaXMgaW4gdGhlIHdvcmtzcGFjZQ0KaG91c2VwcmljZTwtcmVhZFJEUygiaG91c2VwcmljZS5yZHMiKQ0Kc3VtbWFyeShob3VzZXByaWNlKQ0KDQojIENyZWF0ZSB0aGUgZm9ybXVsYSBmb3IgcHJpY2UgYXMgYSBmdW5jdGlvbiBvZiBzcXVhcmVkIHNpemUNCihmbWxhX3NxciA8LSBwcmljZX5JKHNpemVeMikpDQoNCiMgRml0IGEgbW9kZWwgb2YgcHJpY2UgYXMgYSBmdW5jdGlvbiBvZiBzcXVhcmVkIHNpemUgKHVzZSBmbWxhX3NxcikNCm1vZGVsX3NxciA8LSBsbShmbWxhX3NxciwgZGF0YT1ob3VzZXByaWNlKQ0KDQojIEZpdCBhIG1vZGVsIG9mIHByaWNlIGFzIGEgbGluZWFyIGZ1bmN0aW9uIG9mIHNpemUNCm1vZGVsX2xpbiA8LSBsbShwcmljZX5zaXplLCBkYXRhPWhvdXNlcHJpY2UpDQoNCiMgTWFrZSBwcmVkaWN0aW9ucyBhbmQgY29tcGFyZQ0KaG91c2VwcmljZSAlPiUgDQogICAgbXV0YXRlKHByZWRfbGluID0gcHJlZGljdChtb2RlbF9saW4pLCAgICAgICAjIHByZWRpY3Rpb25zIGZyb20gbGluZWFyIG1vZGVsDQogICAgICAgICAgIHByZWRfc3FyID0gcHJlZGljdChtb2RlbF9zcXIpKSAlPiUgICAjIHByZWRpY3Rpb25zIGZyb20gcXVhZHJhdGljIG1vZGVsIA0KICAgIGdhdGhlcihrZXkgPSBtb2RlbHR5cGUsIHZhbHVlID0gcHJlZCwgcHJlZF9saW4sIHByZWRfc3FyKSAlPiUgIyBnYXRoZXIgdGhlIHByZWRpY3Rpb25zDQogICAgZ2dwbG90KGFlcyh4ID0gc2l6ZSkpICsgDQogICAgICAgZ2VvbV9wb2ludChhZXMoeSA9IHByaWNlKSkgKyAgICAgICAgICAgICAgICAgICAjIGFjdHVhbCBwcmljZXMNCiAgICAgICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkLCBjb2xvciA9IG1vZGVsdHlwZSkpICsgIyB0aGUgcHJlZGljdGlvbnMNCiAgICAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpDQpgYGANCg0KIyMgMy4xMDogSW5wdXQgdHJhbnNmb3JtczogdGhlICJob2NrZXkgc3RpY2siIFBhcnQgKDIpDQoNCkluIHRoaXMgZXhlcmNpc2UsIHdlIHdpbGwgYnVpbGQgYSBtb2RlbCB0byBwcmVkaWN0IHByaWNlIGZyb20gYSBtZWFzdXJlIG9mIHRoZSBob3VzZSdzIHNpemUgKHN1cmZhY2UgYXJlYSkuIFRoZSBkYXRhIHNldCBob3VzZXByaWNlIGhhcyB0aGUgY29sdW1uczoNCg0KLSBwcmljZSA6IGhvdXNlIHByaWNlIGluIHVuaXRzIG9mICQxMDAwDQoNCi0gc2l6ZTogc3VyZmFjZSBhcmVhDQoNCkEgc2NhdHRlcnBsb3Qgb2YgdGhlIGRhdGEgc2hvd3MgdGhhdCB0aGUgZGF0YSBpcyBxdWl0ZSBub24tbGluZWFyOiBhIHNvcnQgb2YgImhvY2tleS1zdGljayIgd2hlcmUgcHJpY2UgaXMgZmFpcmx5IGZsYXQgZm9yIHNtYWxsZXIgaG91c2VzLCBidXQgcmlzZXMgc3RlZXBseSBhcyB0aGUgaG91c2UgZ2V0cyBsYXJnZXIuIFF1YWRyYXRpY3MgYW5kIHRyaXRpY3MgYXJlIG9mdGVuIGdvb2QgZnVuY3Rpb25hbCBmb3JtcyB0byBleHByZXNzIGhvY2tleS1zdGljayBsaWtlIHJlbGF0aW9uc2hpcHMuIE5vdGUgdGhhdCB0aGVyZSBtYXkgbm90IGJlIGEgInBoeXNpY2FsIiByZWFzb24gdGhhdCBwcmljZSBpcyByZWxhdGVkIHRvIHRoZSBzcXVhcmUgb2YgdGhlIHNpemU7IGEgcXVhZHJhdGljIGlzIHNpbXBseSBhIGNsb3NlZCBmb3JtIGFwcHJveGltYXRpb24gb2YgdGhlIG9ic2VydmVkIHJlbGF0aW9uc2hpcC4NCg0Kc2NhdHRlcnBsb3QNCg0KWW91IHdpbGwgZml0IGEgbW9kZWwgdG8gcHJlZGljdCBwcmljZSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBzcXVhcmVkIHNpemUsIGFuZCBsb29rIGF0IGl0cyBmaXQgb24gdGhlIHRyYWluaW5nIGRhdGEuDQoNCkJlY2F1c2UgXiBpcyBhbHNvIGEgc3ltYm9sIHRvIGV4cHJlc3MgaW50ZXJhY3Rpb25zLCB1c2UgdGhlIGZ1bmN0aW9uIEkoKSB0byB0cmVhdCB0aGUgZXhwcmVzc2lvbiB4XjIg4oCcYXMgaXPigJ06IHRoYXQgaXMsIGFzIHRoZSBzcXVhcmUgb2YgeCByYXRoZXIgdGhhbiB0aGUgaW50ZXJhY3Rpb24gb2YgeCB3aXRoIGl0c2VsZi4NCg0KZXhhbXBsZUZvcm11bGEgPSB5IH4gSSh4XjIpDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgZGF0YSBzZXQgaG91c2VwcmljZSBpcyBpbiB0aGUgd29ya3NwYWNlLg0KDQotIFdyaXRlIGEgZm9ybXVsYSwgZm1sYV9zcXIsIHRvIGV4cHJlc3MgcHJpY2UgYXMgYSBmdW5jdGlvbiBvZiBzcXVhcmVkIHNpemUuIFByaW50IGl0Lg0KDQotIEZpdCBhIG1vZGVsIG1vZGVsX3NxciB0byB0aGUgZGF0YSB1c2luZyBmbWxhX3Nxcg0KDQotIEZvciBjb21wYXJpc29uLCBmaXQgYSBsaW5lYXIgbW9kZWwgbW9kZWxfbGluIHRvIHRoZSBkYXRhIHVzaW5nIHRoZSBmb3JtdWxhIHByaWNlIH4gc2l6ZS4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gbWFrZSBwcmVkaWN0aW9ucyBmcm9tIHRoZSB0cmFpbmluZyBkYXRhIGZyb20gdGhlIHR3byBtb2RlbHMgDQoNCi0gZ2F0aGVyIHRoZSBwcmVkaWN0aW9ucyBpbnRvIGEgc2luZ2xlIGNvbHVtbiBwcmVkIA0KDQotIGdyYXBoaWNhbGx5IGNvbXBhcmUgdGhlIHByZWRpY3Rpb25zIG9mIHRoZSB0d28gbW9kZWxzIHRvIHRoZSBkYXRhLiBXaGljaCBmaXRzIGJldHRlcj8NCg0KYGBge3J9DQojIGhvdXNlcHJpY2UgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeShob3VzZXByaWNlKQ0KDQojIGZtbGFfc3FyIGlzIGluIHRoZSB3b3Jrc3BhY2UNCmZtbGFfc3FyDQoNCiMgQ3JlYXRlIGEgc3BsaXR0aW5nIHBsYW4gZm9yIDMtZm9sZCBjcm9zcyB2YWxpZGF0aW9uDQpzZXQuc2VlZCgzNDI0NSkgICMgc2V0IHRoZSBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkNCnNwbGl0UGxhbiA8LSAga1dheUNyb3NzVmFsaWRhdGlvbihucm93KGhvdXNlcHJpY2UpLDMsTlVMTCxOVUxMKQ0KDQojIFNhbXBsZSBjb2RlOiBnZXQgY3Jvc3MtdmFsIHByZWRpY3Rpb25zIGZvciBwcmljZSB+IHNpemUNCmhvdXNlcHJpY2UkcHJlZF9saW4gPC0gMCAgIyBpbml0aWFsaXplIHRoZSBwcmVkaWN0aW9uIHZlY3Rvcg0KZm9yKGkgaW4gMTozKSB7DQogIHNwbGl0IDwtIHNwbGl0UGxhbltbaV1dDQogIG1vZGVsX2xpbiA8LSBsbShwcmljZSB+IHNpemUsIGRhdGEgPSBob3VzZXByaWNlW3NwbGl0JHRyYWluLF0pDQogIGhvdXNlcHJpY2UkcHJlZF9saW5bc3BsaXQkYXBwXSA8LSBwcmVkaWN0KG1vZGVsX2xpbiwgbmV3ZGF0YSA9IGhvdXNlcHJpY2Vbc3BsaXQkYXBwLF0pDQp9DQoNCiMgR2V0IGNyb3NzLXZhbCBwcmVkaWN0aW9ucyBmb3IgcHJpY2UgYXMgYSBmdW5jdGlvbiBvZiBzaXplXjIgKHVzZSBmbWxhX3NxcikNCmhvdXNlcHJpY2UkcHJlZF9zcXIgPC0gMCAjIGluaXRpYWxpemUgdGhlIHByZWRpY3Rpb24gdmVjdG9yDQpmb3IoaSBpbiAxOjMpIHsNCiAgc3BsaXQgPC0gc3BsaXRQbGFuW1tpXV0NCiAgbW9kZWxfc3FyIDwtIGxtKGZtbGFfc3FyLCBkYXRhID0gaG91c2VwcmljZVtzcGxpdCR0cmFpbiwgXSkNCiAgaG91c2VwcmljZSRwcmVkX3NxcltzcGxpdCRhcHBdIDwtIHByZWRpY3QobW9kZWxfc3FyLCBuZXdkYXRhID0gaG91c2VwcmljZVtzcGxpdCRhcHAsIF0pDQp9DQoNCiMgR2F0aGVyIHRoZSBwcmVkaWN0aW9ucyBhbmQgY2FsY3VsYXRlIHRoZSByZXNpZHVhbHMNCmhvdXNlcHJpY2VfbG9uZyA8LSBob3VzZXByaWNlICU+JQ0KICBnYXRoZXIoa2V5ID0gbW9kZWx0eXBlLCB2YWx1ZSA9IHByZWQsIHByZWRfbGluLCBwcmVkX3NxcikgJT4lDQogIG11dGF0ZShyZXNpZHVhbHMgPSBwcmVkLXByaWNlKQ0KDQojIENvbXBhcmUgdGhlIGNyb3NzLXZhbGlkYXRlZCBSTVNFIGZvciB0aGUgdHdvIG1vZGVscw0KaG91c2VwcmljZV9sb25nICU+JSANCiAgZ3JvdXBfYnkobW9kZWx0eXBlKSAlPiUgIyBncm91cCBieSBtb2RlbHR5cGUNCiAgc3VtbWFyaXplKHJtc2UgPSBzcXJ0KG1lYW4ocmVzaWR1YWxzXjIpKSkNCmBgYA0KDQojIENoYXB0ZXIgNDogRGVhbGluZyB3aXRoIE5vbi1MaW5lYXIgUmVzcG9uc2VzDQoNCk5vdyB0aGF0IHdlIGhhdmUgbWFzdGVyZWQgbGluZWFyIG1vZGVscywgd2Ugd2lsbCBiZWdpbiB0byBsb29rIGF0IHRlY2huaXF1ZXMgZm9yIG1vZGVsaW5nIHNpdHVhdGlvbnMgdGhhdCBkb24ndCBtZWV0IHRoZSBhc3N1bXB0aW9ucyBvZiBsaW5lYXJpdHkuIFRoaXMgaW5jbHVkZXMgcHJlZGljdGluZyBwcm9iYWJpbGl0aWVzIGFuZCBmcmVxdWVuY2llcyAodmFsdWVzIGJvdW5kZWQgYmV0d2VlbiAwIGFuZCAxKTsgcHJlZGljdGluZyBjb3VudHMgKG5vbm5lZ2F0aXZlIGludGVnZXIgdmFsdWVzLCBhbmQgYXNzb2NpYXRlZCByYXRlcyk7IGFuZCByZXNwb25zZXMgdGhhdCBoYXZlIGEgbm9uLWxpbmVhciBidXQgYWRkaXRpdmUgcmVsYXRpb25zaGlwIHRvIHRoZSBpbnB1dHMuIFRoZXNlIGFsZ29yaXRobXMgYXJlIHZhcmlhdGlvbnMgb24gdGhlIHN0YW5kYXJkIGxpbmVhciBtb2RlbC4NCg0KIyMgNC4xOiBGaXQgYSBtb2RlbCBvZiBzcGFycm93IHN1cnZpdmFsIHByb2JhYmlsaXR5DQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGEgc3BhcnJvdyBzdXJ2aXZlcyBhIHNldmVyZSB3aW50ZXIgc3Rvcm0sIGJhc2VkIG9uIHBoeXNpY2FsIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgc3BhcnJvdy4gVGhlIGRhdGFzZXQgc3BhcnJvdyBpcyBsb2FkZWQgaW50byB5b3VyIHdvcmtzcGFjZS4gVGhlIG91dGNvbWUgdG8gYmUgcHJlZGljdGVkIGlzIHN0YXR1cyAoIlN1cnZpdmVkIiwgIlBlcmlzaGVkIikuIFRoZSB2YXJpYWJsZXMgd2Ugd2lsbCBjb25zaWRlciBhcmU6DQoNCi0gdG90YWxfbGVuZ3RoOiBsZW5ndGggb2YgdGhlIGJpcmQgZnJvbSB0aXAgb2YgYmVhayB0byB0aXAgb2YgdGFpbCAobW0pDQoNCi0gd2VpZ2h0OiBpbiBncmFtcw0KDQotIGh1bWVydXMgOiBsZW5ndGggb2YgaHVtZXJ1cyAoInVwcGVyIGFybSBib25lIiB0aGF0IGNvbm5lY3RzIHRoZSB3aW5nIHRvIHRoZSBib2R5KSAoaW5jaGVzKQ0KDQpSZW1lbWJlciB0aGF0IHdoZW4gdXNpbmcgZ2xtKCkgdG8gY3JlYXRlIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCwgeW91IG11c3QgZXhwbGljaXRseSBzcGVjaWZ5IHRoYXQgZmFtaWx5ID0gYmlub21pYWw6DQoNCmdsbShmb3JtdWxhLCBkYXRhID0gZGF0YSwgZmFtaWx5ID0gYmlub21pYWwpDQoNCllvdSB3aWxsIGNhbGwgc3VtbWFyeSgpLCBicm9vbTo6Z2xhbmNlKCkgdG8gc2VlIGRpZmZlcmVudCBmdW5jdGlvbnMgZm9yIGV4YW1pbmluZyBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuIE9uZSBvZiB0aGUgZGlhZ25vc3RpY3MgdGhhdCB5b3Ugd2lsbCBsb29rIGF0IGlzIHRoZSBhbmFsb2cgdG8gUjIsIGNhbGxlZCBwc2V1ZG8tUjIuDQoNCnBzZXVkb1IyPTHiiJJkZXZpYW5jZW51bGwuZGV2aWFuY2UNCg0KWW91IGNhbiB0aGluayBvZiBkZXZpYW5jZSBhcyBhbmFsb2dvdXMgdG8gdmFyaWFuY2U6IGl0IGlzIGEgbWVhc3VyZSBvZiB0aGUgdmFyaWF0aW9uIGluIGNhdGVnb3JpY2FsIGRhdGEuIFRoZSBwc2V1ZG8tUjIgaXMgYW5hbG9nb3VzIHRvIFIyIGZvciBzdGFuZGFyZCByZWdyZXNzaW9uOiBSMiBpcyBhIG1lYXN1cmUgb2YgdGhlICJ2YXJpYW5jZSBleHBsYWluZWQiIG9mIGEgcmVncmVzc2lvbiBtb2RlbC4gVGhlIHBzZXVkby1SMiBpcyBhIG1lYXN1cmUgb2YgdGhlICJkZXZpYW5jZSBleHBsYWluZWQiLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIGRhdGEgZnJhbWUgc3BhcnJvdyBhbmQgdGhlIHBhY2thZ2UgYnJvb20gYXJlIGxvYWRlZCBpbiB0aGUgd29ya3NwYWNlLg0KDQotIEFzIHN1Z2dlc3RlZCBpbiB0aGUgdmlkZW8sIHlvdSB3aWxsIHByZWRpY3Qgb24gdGhlIG91dGNvbWVzIFRSVUUgYW5kIEZBTFNFLiBDcmVhdGUgYSBuZXcgY29sdW1uIHN1cnZpdmVkIGluIHRoZSBzcGFycm93IGRhdGEgZnJhbWUgdGhhdCBpcyBUUlVFIHdoZW4gc3RhdHVzID09ICJTdXJ2aXZlZCIuDQoNCi0gQ3JlYXRlIHRoZSBmb3JtdWxhIGZtbGEgdGhhdCBleHByZXNzZXMgc3Vydml2ZWQgYXMgYSBmdW5jdGlvbiBvZiB0aGUgdmFyaWFibGVzIG9mIGludGVyZXN0LiBQcmludCBpdC4NCg0KLSBGaXQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgdGhlIHByb2JhYmlsaXR5IG9mIHNwYXJyb3cgc3Vydml2YWwuIEFzc2lnbiB0aGUgbW9kZWwgdG8gdGhlIHZhcmlhYmxlIHNwYXJyb3dfbW9kZWwuDQoNCi0gQ2FsbCBzdW1tYXJ5KCkgdG8gc2VlIHRoZSBjb2VmZmljaWVudHMgb2YgdGhlIG1vZGVsLCB0aGUgZGV2aWFuY2UgYW5kIHRoZSBudWxsIGRldmlhbmNlLg0KDQotIENhbGwgZ2xhbmNlKCkgb24gdGhlIG1vZGVsIHRvIHNlZSB0aGUgZGV2aWFuY2VzIGFuZCBvdGhlciBkaWFnbm9zdGljcyBpbiBhIGRhdGEgZnJhbWUuIEFzc2lnbiB0aGUgb3V0cHV0IGZyb20gZ2xhbmNlKCkgdG8gdGhlIHZhcmlhYmxlIHBlcmYuDQoNCi0gQ2FsY3VsYXRlIHRoZSBwc2V1ZG8tUjIuDQoNCmBgYHtyfQ0KIyBzcGFycm93IGlzIGluIHRoZSB3b3Jrc3BhY2UNCnNwYXJyb3c8LXJlYWRSRFMoInNwYXJyb3cucmRzIikNCnN1bW1hcnkoc3BhcnJvdykNCg0KIyBDcmVhdGUgdGhlIHN1cnZpdmVkIGNvbHVtbg0Kc3BhcnJvdyRzdXJ2aXZlZCA8LSBzcGFycm93JHN0YXR1cz09IlN1cnZpdmVkIg0KDQojIENyZWF0ZSB0aGUgZm9ybXVsYQ0KKGZtbGEgPC0gc3Vydml2ZWR+dG90YWxfbGVuZ3RoK3dlaWdodCtodW1lcnVzKQ0KDQojIEZpdCB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbA0Kc3BhcnJvd19tb2RlbCA8LSBnbG0oZm1sYSxkYXRhPXNwYXJyb3csIGZhbWlseT0iYmlub21pYWwiKQ0KDQojIENhbGwgc3VtbWFyeQ0Kc3VtbWFyeShzcGFycm93X21vZGVsKQ0KDQojIENhbGwgZ2xhbmNlDQoocGVyZiA8LSBnbGFuY2Uoc3BhcnJvd19tb2RlbCkpDQoNCiMgQ2FsY3VsYXRlIHBzZXVkby1SLXNxdWFyZWQNCihwc2V1ZG9SMiA8LSAxLXBlcmYkZGV2aWFuY2UvcGVyZiRudWxsLmRldmlhbmNlKQ0KYGBgDQojIyA0LjI6IFByZWRpY3Qgc3BhcnJvdyBzdXJ2aXZhbA0KDQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSB3aWxsIHByZWRpY3QgdGhlIHByb2JhYmlsaXR5IG9mIHN1cnZpdmFsIHVzaW5nIHRoZSBzcGFycm93IHN1cnZpdmFsIG1vZGVsIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLg0KDQpSZWNhbGwgdGhhdCB3aGVuIGNhbGxpbmcgcHJlZGljdCgpIHRvIGdldCB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgZnJvbSBhIGdsbSgpIG1vZGVsLCB5b3UgbXVzdCBzcGVjaWZ5IHRoYXQgeW91IHdhbnQgdGhlIHJlc3BvbnNlOg0KDQpwcmVkaWN0KG1vZGVsLCB0eXBlID0gInJlc3BvbnNlIikNCk90aGVyd2lzZSwgcHJlZGljdCgpIG9uIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCByZXR1cm5zIHRoZSBwcmVkaWN0ZWQgbG9nLW9kZHMgb2YgdGhlIGV2ZW50LCBub3QgdGhlIHByb2JhYmlsaXR5Lg0KDQpZb3Ugd2lsbCBhbHNvIHVzZSB0aGUgR2FpbkN1cnZlUGxvdCgpIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIGdhaW4gY3VydmUgZnJvbSB0aGUgbW9kZWwgcHJlZGljdGlvbnMuIElmIHRoZSBtb2RlbCdzIGdhaW4gY3VydmUgaXMgY2xvc2UgdG8gdGhlIGlkZWFsICgid2l6YXJkIikgZ2FpbiBjdXJ2ZSwgdGhlbiB0aGUgbW9kZWwgc29ydGVkIHRoZSBzcGFycm93cyB3ZWxsOiB0aGF0IGlzLCB0aGUgbW9kZWwgcHJlZGljdGVkIHRoYXQgc3BhcnJvd3MgdGhhdCBhY3R1YWxseSBzdXJ2aXZlZCB3b3VsZCBoYXZlIGEgaGlnaGVyIHByb2JhYmlsaXR5IG9mIHN1cnZpdmFsLiBUaGUgaW5wdXRzIHRvIHRoZSBHYWluQ3VydmVQbG90KCkgZnVuY3Rpb24gYXJlOg0KDQotIGZyYW1lOiBkYXRhIGZyYW1lIHdpdGggcHJlZGljdGlvbiBjb2x1bW4gYW5kIGdyb3VuZCB0cnV0aCBjb2x1bW4NCg0KLSB4dmFyOiB0aGUgbmFtZSBvZiB0aGUgY29sdW1uIG9mIHByZWRpY3Rpb25zIChhcyBhIHN0cmluZykNCg0KLSB0cnV0aFZhcjogdGhlIG5hbWUgb2YgdGhlIGNvbHVtbiB3aXRoIGFjdHVhbCBvdXRjb21lIChhcyBhIHN0cmluZykNCg0KLSB0aXRsZTogYSB0aXRsZSBmb3IgdGhlIHBsb3QgKGFzIGEgc3RyaW5nKQ0KDQotIEdhaW5DdXJ2ZVBsb3QoZnJhbWUsIHh2YXIsIHRydXRoVmFyLCB0aXRsZSkNCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBkYXRhZnJhbWUgc3BhcnJvdyBhbmQgdGhlIG1vZGVsIHNwYXJyb3dfbW9kZWwgYXJlIGluIHRoZSB3b3Jrc3BhY2UuDQoNCi0gQ3JlYXRlIGEgbmV3IGNvbHVtbiBpbiBzcGFycm93IGNhbGxlZCBwcmVkIHRoYXQgY29udGFpbnMgdGhlIHByZWRpY3Rpb25zIG9uIHRoZSB0cmFpbmluZyBkYXRhLg0KDQotIENhbGwgR2FpbkN1cnZlUGxvdCgpIHRvIGNyZWF0ZSB0aGUgZ2FpbiBjdXJ2ZSBvZiBwcmVkaWN0aW9ucy4gRG9lcyB0aGUgbW9kZWwgZG8gYSBnb29kIGpvYiBvZiBzb3J0aW5nIHRoZSBzcGFycm93cyBieSB3aGV0aGVyIG9yIG5vdCB0aGV5IGFjdHVhbGx5IHN1cnZpdmVkPw0KYGBge3J9DQojIHNwYXJyb3cgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeShzcGFycm93KQ0KDQojIHNwYXJyb3dfbW9kZWwgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc3VtbWFyeShzcGFycm93X21vZGVsKQ0KDQojIE1ha2UgcHJlZGljdGlvbnMNCnNwYXJyb3ckcHJlZCA8LSBwcmVkaWN0KHNwYXJyb3dfbW9kZWwsdHlwZT0icmVzcG9uc2UiKQ0KDQojIExvb2sgYXQgZ2FpbiBjdXJ2ZQ0KR2FpbkN1cnZlUGxvdChzcGFycm93LCAicHJlZCIsICJzdXJ2aXZlZCIsICJzcGFycm93IHN1cnZpdmFsIG1vZGVsIikNCmBgYA0KUmVtYXJrOiBZb3Ugc2VlIGZyb20gdGhlIGdhaW4gY3VydmUgdGhhdCB0aGUgbW9kZWwgZm9sbG93cyB0aGUgd2l6YXJkIGN1cnZlIGZvciBhYm91dCB0aGUgZmlyc3QgMzAlIG9mIHRoZSBkYXRhLCBpZGVudGlmeWluZyBhYm91dCA0NSUgb2YgdGhlIHN1cnZpdmluZyBzcGFycm93cyB3aXRoIG9ubHkgYSBmZXcgZmFsc2UgcG9zaXRpdmVzLg0KDQojIyAgNC4zOiBQb2lzc29uIG9yIHF1YXNpcG9pc3Nvbg0KDQpPbmUgb2YgdGhlIGFzc3VtcHRpb25zIG9mIFBvaXNzb24gcmVncmVzc2lvbiB0byBwcmVkaWN0IGNvdW50cyBpcyB0aGF0IHRoZSBldmVudCB5b3UgYXJlIGNvdW50aW5nIGlzIFBvaXNzb24gZGlzdHJpYnV0ZWQ6IHRoZSBhdmVyYWdlIGNvdW50IHBlciB1bml0IHRpbWUgaXMgdGhlIHNhbWUgYXMgdGhlIHZhcmlhbmNlIG9mIHRoZSBjb3VudC4gSW4gcHJhY3RpY2UsICJ0aGUgc2FtZSIgbWVhbnMgdGhhdCB0aGUgbWVhbiBhbmQgdGhlIHZhcmlhbmNlIHNob3VsZCBiZSBvZiBhIHNpbWlsYXIgb3JkZXIgb2YgbWFnbml0dWRlLg0KDQpXaGVuIHRoZSB2YXJpYW5jZSBpcyBtdWNoIGxhcmdlciB0aGFuIHRoZSBtZWFuLCB0aGUgUG9pc3NvbiBhc3N1bXB0aW9uIGRvZXNuJ3QgYXBwbHksIGFuZCBvbmUgc29sdXRpb24gaXMgdG8gdXNlIHF1YXNpcG9pc3NvbiByZWdyZXNzaW9uLCB3aGljaCBkb2VzIG5vdCBhc3N1bWUgdGhhdCB2YXJpYW5jZT1tZWFuLg0KDQpGb3IgZWFjaCBvZiB0aGUgZm9sbG93aW5nIHNpdHVhdGlvbnMsIGRlY2lkZSBpZiBwb2lzc29uIHJlZ3Jlc3Npb24gd291bGQgYmUgc3VpdGFibGUsIG9yIGlmIHlvdSBzaG91bGQgdXNlIHF1YXNpcG9pc3NvbiByZWdyZXNzaW9uLg0KDQpGb3Igd2hpY2ggc2l0dWF0aW9ucyBjYW4geW91IHVzZSBwb2lzc29uIHJlZ3Jlc3Npb24/DQoNCjEuIE51bWJlciBvZiBkYXlzIHN0dWRlbnRzIGFyZSBhYnNlbnQ6IG1lYW4gNS45LCB2YXJpYW5jZSA0OQ0KDQoyLiBOdW1iZXIgb2YgYXdhcmRzIGEgc3R1ZGVudCB3aW5zOiBtZWFuIDAuNiwgdmFyaWFuY2UgMS4xDQoNCjMuIE51bWJlciBvZiBoaXRzIHBlciB3ZWJzaXRlIHBhZ2U6IG1lYW4gMTA4LjIsIHZhcmlhbmNlIDEwOC41DQoNCjQuIE51bWJlciBvZiBiaWtlcyByZW50ZWQgcGVyIGRheTogbWVhbiAyNzMsIHZhcmlhbmNlIDQ1ODYzLjg0DQoNCkFuc3dlciB0aGUgcXVlc3Rpb24NCg0KNTAgWFANCg0KUG9zc2libGUgQW5zd2Vycw0KDQoxLiBBbGwgb2YgdGhlbQ0KDQoNCjIuIDEgYW5kIDQNCg0KMy4gMiBhbmQgMw0KDQoNCjQuIDEgYW5kIDMNCg0KNS4gMiBhbmQgNA0KDQoNCiMjIDQuNDogRml0IGEgbW9kZWwgdG8gcHJlZGljdCBiaWtlIHJlbnRhbCBjb3VudHMNCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBidWlsZCBhIG1vZGVsIHRvIHByZWRpY3QgdGhlIG51bWJlciBvZiBiaWtlcyByZW50ZWQgaW4gYW4gaG91ciBhcyBhIGZ1bmN0aW9uIG9mIHRoZSB3ZWF0aGVyLCB0aGUgdHlwZSBvZiBkYXkgKGhvbGlkYXksIHdvcmtpbmcgZGF5LCBvciB3ZWVrZW5kKSwgYW5kIHRoZSB0aW1lIG9mIGRheS4gWW91IHdpbGwgdHJhaW4gdGhlIG1vZGVsIG9uIGRhdGEgZnJvbSB0aGUgbW9udGggb2YgSnVseS4NCg0KVGhlIGRhdGEgZnJhbWUgaGFzIHRoZSBjb2x1bW5zOg0KDQotIGNudDogdGhlIG51bWJlciBvZiBiaWtlcyByZW50ZWQgaW4gdGhhdCBob3VyICh0aGUgb3V0Y29tZSkNCg0KLSBocjogdGhlIGhvdXIgb2YgdGhlIGRheSAoMC0yMywgYXMgYSBmYWN0b3IpDQoNCi0gaG9saWRheTogVFJVRS9GQUxTRQ0KDQotIHdvcmtpbmdkYXk6IFRSVUUgaWYgbmVpdGhlciBhIGhvbGlkYXkgbm9yIGEgd2Vla2VuZCwgZWxzZSBGQUxTRQ0KDQotIHdlYXRoZXJzaXQ6IGNhdGVnb3JpY2FsLCAiQ2xlYXIgdG8gcGFydGx5IGNsb3VkeSIvIkxpZ2h0IFByZWNpcGl0YXRpb24iLyJNaXN0eSINCg0KLSB0ZW1wOiBub3JtYWxpemVkIHRlbXBlcmF0dXJlIGluIENlbHNpdXMNCg0KLSBhdGVtcDogbm9ybWFsaXplZCAiZmVlbGluZyIgdGVtcGVyYXR1cmUgaW4gQ2Vsc2l1cw0KDQotIGh1bTogbm9ybWFsaXplZCBodW1pZGl0eQ0KDQotIHdpbmRzcGVlZDogbm9ybWFsaXplZCB3aW5kc3BlZWQNCg0KLSBpbnN0YW50OiB0aGUgdGltZSBpbmRleCAtLSBudW1iZXIgb2YgaG91cnMgc2luY2UgYmVnaW5uaW5nIG9mIGRhdGEgc2V0IChub3QgYSB2YXJpYWJsZSkNCm1udGggYW5kIHlyOiBtb250aCBhbmQgeWVhciBpbmRpY2VzIChub3QgdmFyaWFibGVzKQ0KDQpSZW1lbWJlciB0aGF0IHlvdSBtdXN0IHNwZWNpZnkgZmFtaWx5ID0gcG9pc3NvbiBvciBmYW1pbHkgPSBxdWFzaXBvaXNzb24gd2hlbiB1c2luZyBnbG0oKSB0byBmaXQgYSBjb3VudCBtb2RlbC4NCg0KU2luY2UgdGhlcmUgYXJlIGEgbG90IG9mIGlucHV0IHZhcmlhYmxlcywgZm9yIGNvbnZlbmllbmNlIHdlIHdpbGwgc3BlY2lmeSB0aGUgb3V0Y29tZSBhbmQgdGhlIGlucHV0cyBpbiB2YXJpYWJsZXMsIGFuZCB1c2UgcGFzdGUoKSB0byBhc3NlbWJsZSBhIHN0cmluZyByZXByZXNlbnRpbmcgdGhlIG1vZGVsIGZvcm11bGEuDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgZGF0YSBmcmFtZSBiaWtlc0p1bHkgaXMgaW4gdGhlIHdvcmtzcGFjZS4gVGhlIG5hbWVzIG9mIHRoZSBvdXRjb21lIHZhcmlhYmxlIGFuZCB0aGUgaW5wdXQgdmFyaWFibGVzIGFyZSBhbHNvIGluIHRoZSB3b3Jrc3BhY2UgYXMgdGhlIHZhcmlhYmxlcyBvdXRjb21lIGFuZCB2YXJzIHJlc3BlY3RpdmVseS4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gY3JlYXRlIHRoZSBmb3JtdWxhIGZtbGEgZXhwcmVzc2luZyBjbnQgYXMgYSBmdW5jdGlvbiBvZiB0aGUgaW5wdXRzLiBQcmludCBpdC4NCg0KLSBDYWxjdWxhdGUgdGhlIG1lYW4gKG1lYW4oKSkgYW5kIHZhcmlhbmNlICh2YXIoKSkgb2YgYmlrZXNKdWx5JGNudC4NCg0KLSBTaG91bGQgeW91IHVzZSBwb2lzc29uIG9yIHF1YXNpcG9pc3NvbiByZWdyZXNzaW9uPw0KDQotIFVzZSBnbG0oKSB0byBmaXQgYSBtb2RlbCB0byB0aGUgYmlrZXNKdWx5IGRhdGE6IGJpa2VfbW9kZWwuDQoNCi0gVXNlIGdsYW5jZSgpIHRvIGxvb2sgYXQgdGhlIG1vZGVsJ3MgZml0IHN0YXRpc3RpY3MuIEFzc2lnbiB0aGUgb3V0cHV0IG9mIGdsYW5jZSgpIHRvIHRoZSB2YXJpYWJsZSBwZXJmLg0KDQotIENhbGN1bGF0ZSB0aGUgcHNldWRvLVItc3F1YXJlZCBvZiB0aGUgbW9kZWwuDQoNCmBgYHtyfQ0KYmlrZXNKdWx5PC1yZWFkLmNzdigiYmlrZXNKdWx5LmNzdiIpDQojIGJpa2VzSnVseSBpcyBpbiB0aGUgd29ya3NwYWNlDQpzdHIoYmlrZXNKdWx5KQ0KIyBUaGUgb3V0Y29tZSBjb2x1bW4NCm91dGNvbWU8LWMoImNudCIpDQoNCiMgVGhlIGlucHV0cyB0byB1c2UNCnZhcnM8LWMoImhyIiwiaG9saWRheSIsICJ3b3JraW5nZGF5IiwgIndlYXRoZXJzaXQiLCAidGVtcCIsICJhdGVtcCIsICJodW0iLCAid2luZHNwZWVkIikNCg0KIyBDcmVhdGUgdGhlIGZvcm11bGEgc3RyaW5nIGZvciBiaWtlcyByZW50ZWQgYXMgYSBmdW5jdGlvbiBvZiB0aGUgaW5wdXRzDQooZm1sYSA8LSBwYXN0ZShvdXRjb21lLCAifiIsIHBhc3RlKHZhcnMsIGNvbGxhcHNlID0gIiArICIpKSkNCg0KIyBDYWxjdWxhdGUgdGhlIG1lYW4gYW5kIHZhcmlhbmNlIG9mIHRoZSBvdXRjb21lDQoobWVhbl9iaWtlcyA8LSBtZWFuKGJpa2VzSnVseSRjbnQpKQ0KKHZhcl9iaWtlcyA8LSB2YXIoYmlrZXNKdWx5JGNudCkpDQoNCiMgRml0IHRoZSBtb2RlbA0KYmlrZV9tb2RlbCA8LSBnbG0oZm1sYSxkYXRhPWJpa2VzSnVseSxmYW1pbHk9cXVhc2lwb2lzc29uKQ0KDQojIENhbGwgZ2xhbmNlDQoocGVyZiA8LSBnbGFuY2UoYmlrZV9tb2RlbCkpDQoNCiMgQ2FsY3VsYXRlIHBzZXVkby1SLXNxdWFyZWQNCihwc2V1ZG9SMiA8LSAxLXBlcmYkZGV2aWFuY2UvcGVyZiRudWxsLmRldmlhbmNlKQ0KYGBgDQoNCiMjIDQuNTogUHJlZGljdCBiaWtlIHJlbnRhbHMgb24gbmV3IGRhdGENCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCB1c2UgdGhlIG1vZGVsIHlvdSBidWlsdCBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UgdG8gbWFrZSBwcmVkaWN0aW9ucyBmb3IgdGhlIG1vbnRoIG9mIEF1Z3VzdC4gVGhlIGRhdGEgc2V0IGJpa2VzQXVndXN0IGhhcyB0aGUgc2FtZSBjb2x1bW5zIGFzIGJpa2VzSnVseS4NCg0KUmVjYWxsIHRoYXQgeW91IG11c3Qgc3BlY2lmeSB0eXBlID0gInJlc3BvbnNlIiB3aXRoIHByZWRpY3QoKSB3aGVuIHByZWRpY3RpbmcgY291bnRzIGZyb20gYSBnbG0gcG9pc3NvbiBvciBxdWFzaXBvaXNzb24gbW9kZWwuDQoNCkluc3RydWN0aW9ucw0KDQoxMDAgWFANCg0KLSBUaGUgbW9kZWwgYmlrZV9tb2RlbCBhbmQgdGhlIGRhdGEgZnJhbWUgYmlrZXNBdWd1c3QgYXJlIGluIHRoZSB3b3Jrc3BhY2UuDQoNCi0gVXNlIHByZWRpY3QgdG8gcHJlZGljdCB0aGUgbnVtYmVyIG9mIGJpa2VzIHBlciBob3VyIG9uIHRoZSBiaWtlc0F1Z3VzdCBkYXRhLiBBc3NpZ24gdGhlIHByZWRpY3Rpb25zIHRvIHRoZSBjb2x1bW4gYmlrZXNBdWd1c3QkcHJlZC4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gZ2V0IHRoZSBSTVNFIG9mIHRoZSBwcmVkaWN0aW9ucyBvbiB0aGUgQXVndXN0IGRhdGEuDQoNCi0gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIGdlbmVyYXRlIHRoZSBwbG90IG9mIHByZWRpY3Rpb25zIHRvIGFjdHVhbCBjb3VudHMuDQoNCi0gRG8gYW55IG9mIHRoZSBwcmVkaWN0aW9ucyBhcHBlYXIgbmVnYXRpdmU/DQoNCmBgYHtyfQ0KYmlrZXNBdWd1c3Q8LXJlYWQuY3N2KCJiaWtlc0F1Z3VzdC5jc3YiKQ0KIyBiaWtlc0F1Z3VzdCBpcyBpbiB0aGUgd29ya3NwYWNlDQpzdHIoYmlrZXNBdWd1c3QpDQoNCiMgYmlrZV9tb2RlbCBpcyBpbiB0aGUgd29ya3NwYWNlDQpzdW1tYXJ5KGJpa2VfbW9kZWwpDQoNCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiBBdWd1c3QgZGF0YQ0KYmlrZXNBdWd1c3QkcHJlZCAgPC0gcHJlZGljdChiaWtlX21vZGVsLG5ld2RhdGE9YmlrZXNBdWd1c3QsIHR5cGU9InJlc3BvbnNlIikNCg0KIyBDYWxjdWxhdGUgdGhlIFJNU0UNCmJpa2VzQXVndXN0ICU+JSANCiAgbXV0YXRlKHJlc2lkdWFsID0gcHJlZC1jbnQpICU+JQ0KICBzdW1tYXJpemUocm1zZSAgPSBzcXJ0KG1lYW4ocmVzaWR1YWxeMikpKQ0KDQojIFBsb3QgcHJlZGljdGlvbnMgdnMgY250IChwcmVkIG9uIHgtYXhpcykNCmdncGxvdChiaWtlc0F1Z3VzdCwgYWVzKHggPSBwcmVkLCB5ID0gY250KSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9hYmxpbmUoY29sb3IgPSAiZGFya2JsdWUiKQ0KYGBgDQoNCiMjIDQuNjogVmlzdWFsaXplIHRoZSBCaWtlIFJlbnRhbCBQcmVkaWN0aW9ucw0KDQpJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHlvdSB2aXN1YWxpemVkIHRoZSBiaWtlIG1vZGVsJ3MgcHJlZGljdGlvbnMgdXNpbmcgdGhlIHN0YW5kYXJkICJvdXRjb21lIHZzLiBwcmVkaWN0aW9uIiBzY2F0dGVyIHBsb3QuIFNpbmNlIHRoZSBiaWtlIHJlbnRhbCBkYXRhIGlzIHRpbWUgc2VyaWVzIGRhdGEsIHlvdSBtaWdodCBiZSBpbnRlcmVzdGVkIGluIGhvdyB0aGUgbW9kZWwgcGVyZm9ybXMgYXMgYSBmdW5jdGlvbiBvZiB0aW1lLiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjb21wYXJlIHRoZSBwcmVkaWN0aW9ucyBhbmQgYWN0dWFsIHJlbnRhbHMgb24gYW4gaG91cmx5IGJhc2lzLCBmb3IgdGhlIGZpcnN0IDE0IGRheXMgb2YgQXVndXN0Lg0KDQpUbyBjcmVhdGUgdGhlIHBsb3QgeW91IHdpbGwgdXNlIHRoZSBmdW5jdGlvbiB0aWR5cjo6Z2F0aGVyKCkgdG8gY29uc29saWRhdGUgdGhlIHByZWRpY3RlZCBhbmQgYWN0dWFsIHZhbHVlcyBmcm9tIGJpa2VzQXVndXN0IGluIGEgc2luZ2xlIGNvbHVtbi4gZ2F0aGVyKCkgdGFrZXMgYXMgYXJndW1lbnRzOg0KDQotIFRoZSAid2lkZSIgZGF0YSBmcmFtZSB0byBiZSBnYXRoZXJlZCAoaW1wbGljaXQgaW4gYSBwaXBlKQ0KDQotIFRoZSBuYW1lIG9mIHRoZSBrZXkgY29sdW1uIHRvIGJlIGNyZWF0ZWQgLSBjb250YWlucyB0aGUgbmFtZXMgb2YgdGhlIGdhdGhlcmVkIGNvbHVtbnMuDQoNCi0gVGhlIG5hbWUgb2YgdGhlIHZhbHVlIGNvbHVtbiB0byBiZSBjcmVhdGVkIC0gY29udGFpbnMgdGhlIHZhbHVlcyBvZiB0aGUgZ2F0aGVyZWQgY29sdW1ucy4NCg0KLSBUaGUgbmFtZXMgb2YgdGhlIGNvbHVtbnMgdG8gYmUgZ2F0aGVyZWQgaW50byBhIHNpbmdsZSBjb2x1bW4uDQoNCllvdSdsbCB1c2UgdGhlIGdhdGhlcmVkIGRhdGEgZnJhbWUgdG8gY29tcGFyZSB0aGUgYWN0dWFsIGFuZCBwcmVkaWN0ZWQgcmVudGFsIGNvdW50cyBhcyBhIGZ1bmN0aW9uIG9mIHRpbWUuIFRoZSB0aW1lIGluZGV4LCBpbnN0YW50IGNvdW50cyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBzaW5jZSB0aGUgYmVnaW5uaW5nIG9mIGRhdGEgY29sbGVjdGlvbi4gVGhlIHNhbXBsZSBjb2RlIGNvbnZlcnRzIHRoZSBpbnN0YW50cyB0byBkYWlseSB1bml0cywgc3RhcnRpbmcgZnJvbSAwLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIGRhdGEgZnJhbWUgYmlrZXNBdWd1c3QsIHdpdGggdGhlIHByZWRpY3Rpb25zIChiaWtlc0F1Z3VzdCRwcmVkKSBpcyBpbiB0aGUgd29ya3NwYWNlLg0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBwbG90IHRoZSBwcmVkaWN0aW9ucyBhbmQgYWN0dWFsIGNvdW50cyBieSBob3VyIGZvciB0aGUgZmlyc3QgMTQgZGF5cyBvZiBBdWd1c3QuDQoNCi0gY29udmVydCBpbnN0YW50IHRvIGJlIGluIGRheSB1bml0cywgcmF0aGVyIHRoYW4gaG91cg0KDQotIGdhdGhlcigpIHRoZSBjbnQgYW5kIHByZWQgY29sdW1ucyBpbnRvIGEgY29sdW1uIGNhbGxlZCB2YWx1ZSwgd2l0aCBhIGtleSBjYWxsZWQgdmFsdWV0eXBlLg0KDQotIGZpbHRlcigpIGZvciB0aGUgZmlyc3QgdHdvIHdlZWtzIG9mIEF1Z3VzdA0KDQotIFBsb3QgdmFsdWUgYXMgYSBmdW5jdGlvbiBvZiBpbnN0YW50IChkYXkpLg0KDQotIERvZXMgdGhlIG1vZGVsIHNlZSB0aGUgZ2VuZXJhbCB0aW1lIHBhdHRlcm5zIGluIGJpa2UgcmVudGFscz8NCg0KYGBge3J9DQojIFBsb3QgcHJlZGljdGlvbnMgYW5kIGNudCBieSBkYXRlL3RpbWUNCnF1YXNpcG9pc3Nvbl9wbG90PC1iaWtlc0F1Z3VzdCAlPiUgDQogICMgc2V0IHN0YXJ0IHRvIDAsIGNvbnZlcnQgdW5pdCB0byBkYXlzDQogIG11dGF0ZShpbnN0YW50ID0gKGluc3RhbnQgLSBtaW4oaW5zdGFudCkpLzI0KSAlPiUgIA0KICAjIGdhdGhlciBjbnQgYW5kIHByZWQgaW50byBhIHZhbHVlIGNvbHVtbg0KICBnYXRoZXIoa2V5ID0gdmFsdWV0eXBlLCB2YWx1ZSA9IHZhbHVlLCBjbnQsIHByZWQpICU+JQ0KICBmaWx0ZXIoaW5zdGFudCA8IDE0KSAlPiUgIyByZXN0cmljIHRvIGZpcnN0IDE0IGRheXMNCiAgIyBwbG90IHZhbHVlIGJ5IGluc3RhbnQNCiAgZ2dwbG90KGFlcyh4ID0gaW5zdGFudCwgeSA9IHZhbHVlLCBjb2xvciA9IHZhbHVldHlwZSwgbGluZXR5cGUgPSB2YWx1ZXR5cGUpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9saW5lKCkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKCJEYXkiLCBicmVha3MgPSAwOjE0LCBsYWJlbHMgPSAwOjE0KSArIA0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsgDQogIGdndGl0bGUoIlByZWRpY3RlZCBBdWd1c3QgYmlrZSByZW50YWxzLCBRdWFzaXBvaXNzb24gbW9kZWwiKQ0KcXVhc2lwb2lzc29uX3Bsb3QNCmBgYA0KDQpSZW1hcms6IFRoaXMgbW9kZWwgbW9zdGx5IGlkZW50aWZpZXMgdGhlIHNsb3cgYW5kIGJ1c3kgaG91cnMgb2YgdGhlIGRheSwgYWx0aG91Z2ggaXQgb2Z0ZW4gdW5kZXJlc3RpbWF0ZXMgcGVhayBkZW1hbmQuDQoNCiMjIDQuNzogV3JpdGluZyBmb3JtdWxhcyBmb3IgR0FNIG1vZGVscw0KDQpXaGVuIHVzaW5nIGdhbSgpIHRvIG1vZGVsIG91dGNvbWUgYXMgYW4gYWRkaXRpdmUgZnVuY3Rpb24gb2YgdGhlIGlucHV0cywgeW91IGNhbiB1c2UgdGhlIHMoKSBmdW5jdGlvbiBpbnNpZGUgZm9ybXVsYXMgdG8gZGVzaWduYXRlIHRoYXQgeW91IHdhbnQgdXNlIGEgc3BsaW5lIHRvIG1vZGVsIHRoZSBub24tbGluZWFyIHJlbGF0aW9uc2hpcCBvZiBhIGNvbnRpbnVvdXMgdmFyaWFibGUgdG8gdGhlIG91dGNvbWUuDQoNClN1cHBvc2UgdGhhdCB5b3Ugd2FudCB0byBwcmVkaWN0IGhvdyBtdWNoIHdlaWdodCAoV3Rsb3NzKSBhIGRpZXRlciB3aWxsIGxvc2Ugb3ZlciBhIDIteWVhciBkaWV0IHBsYW4gYXMgYSBmdW5jdGlvbiBvZjoNCg0KLSBEaWV0IHR5cGUgKGNhdGVnb3JpY2FsKQ0KDQotIFNleCAoY2F0ZWdvcmljYWwpDQoNCi0gQWdlIGF0IGJlZ2lubmluZyBvZiBkaWV0IChjb250aW51b3VzKQ0KDQotIEJNSSAoYm9keSBtYXNzIGluZGV4KSBhdCBiZWdpbm5pbmcgb2YgZGlldCAoY29udGludW91cykNCg0KWW91IGRvIG5vdCB3YW50IHRvIGFzc3VtZSB0aGF0IGFueSBvZiB0aGUgcmVsYXRpb25zaGlwcyBhcmUgbGluZWFyLg0KDQpXaGljaCBpcyB0aGUgbW9zdCBhcHByb3ByaWF0ZSBmb3JtdWxhPw0KDQpBbnN3ZXIgdGhlIHF1ZXN0aW9uDQoNCjUwIFhQDQoNClBvc3NpYmxlIEFuc3dlcnMNCg0KMS4gV3Rsb3NzIH4gRGlldCArIFNleCArIEFnZSArIEJNSQ0KDQoyLiBXdGxvc3MgfiBzKERpZXQpICsgcyhTZXgpICsgcyhBZ2UpICsgcyhCTUkpDQoNCjMuIFd0bG9zcyB+IERpZXQgKyBTZXggKyBzKEFnZSkgKyBzKEJNSSkgW2Fuc10NCg0KUmVtYXJrOiBDYXRlZ29yaWFsIHZhcmlhYmxlcyBjYW5ub3QgYmUgJ3NwbGluZWQnLCBwaWVjZXdpc2UgcG9seW5vbWlhbGx5IG1vZGVsbGVkDQoNCiMjIDQuODogV3JpdGluZyBmb3JtdWxhcyBmb3IgR0FNIG1vZGVscyAoMikNCg0KU3VwcG9zZSB0aGF0IGluIHRoZSBkaWV0IHByb2JsZW0gZnJvbSB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHlvdSBub3cgYWxzbyB3YW50IHRvIHRha2UgaW50byBhY2NvdW50DQoNCnRoZSBkaWV0ZXIncyByZXN0aW5nIG1ldGFib2xpYyByYXRlIChCTVIgLS0gY29udGludW91cykgYW5kDQp0aGUgZGlldGVyJ3MgYXZlcmFnZSBudW1iZXIgaG91cnMgb2YgYWVyb2JpYyBleGVyY2lzZSBwZXIgZGF5IChFIC0tIGNvbnRpbnVvdXMpIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIHN0dWR5Lg0KDQpZb3UgaGF2ZSByZWFzb24gdG8gYmVsaWV2ZSB0aGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBCTVIgYW5kIHdlaWdodCBsb3NzIGlzIGxpbmVhciAoYW5kIHlvdSB3YW50IHRvIG1vZGVsIGl0IHRoYXQgd2F5KSwgYnV0IG5vdCBuZWNlc3NhcmlseSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYWVyb2JpYyBleGVyY2lzZSBhbmQgd2VpZ2h0IGxvc3MuDQoNCldoaWNoIGlzIHRoZSBtb3N0IGFwcHJvcHJpYXRlIGZvcm11bGE/DQoNCkFuc3dlciB0aGUgcXVlc3Rpb24NCg0KNTAgWFANCg0KUG9zc2libGUgQW5zd2Vycw0KDQoxLiBXdGxvc3MgfiBEaWV0ICsgU2V4ICsgcyhBZ2UpICsgcyhCTUkpICsgcyhCTVIpICsgcyhFKQ0KDQoyLiBXdGxvc3MgfiBEaWV0ICsgU2V4ICsgcyhBZ2UpICsgcyhCTUkpICsgQk1SICsgcyhFKSBbYW5zXQ0KDQozLiBXdGxvc3MgfiBEaWV0ICsgU2V4ICsgcyhBZ2UpICsgcyhCTUkpICsgcyhCTVIpICsgRQ0KDQojIyA0Ljk6IE1vZGVsIHNveWJlYW4gZ3Jvd3RoIHdpdGggR0FNDQoNCkluIHRoaXMgZXhlcmNpc2UgeW91IHdpbGwgbW9kZWwgdGhlIGF2ZXJhZ2UgbGVhZiB3ZWlnaHQgb24gYSBzb3liZWFuIHBsYW50IGFzIGEgZnVuY3Rpb24gb2YgdGltZSAoYWZ0ZXIgcGxhbnRpbmcpLiBBcyB5b3Ugd2lsbCBzZWUsIHRoZSBzb3liZWFuIHBsYW50IGRvZXNuJ3QgZ3JvdyBhdCBhIHN0ZWFkeSByYXRlLCBidXQgcmF0aGVyIGhhcyBhICJncm93dGggc3B1cnQiIHRoYXQgZXZlbnR1YWxseSB0YXBlcnMgb2ZmLiBIZW5jZSwgbGVhZiB3ZWlnaHQgaXMgbm90IHdlbGwgZGVzY3JpYmVkIGJ5IGEgbGluZWFyIG1vZGVsLg0KDQpSZWNhbGwgdGhhdCB5b3UgY2FuIGRlc2lnbmF0ZSB3aGljaCB2YXJpYWJsZSB5b3Ugd2FudCB0byBtb2RlbCBub24tbGluZWFybHkgaW4gYSBmb3JtdWxhIHdpdGggdGhlIHMoKSBmdW5jdGlvbjoNCg0KeSB+IHMoeCkNCg0KQWxzbyByZW1lbWJlciB0aGF0IGdhbSgpIGZyb20gdGhlIHBhY2thZ2UgbWdjdiBoYXMgdGhlIGNhbGxpbmcgaW50ZXJmYWNlDQoNCmdhbShmb3JtdWxhLCBmYW1pbHksIGRhdGEpDQoNCkZvciBzdGFuZGFyZCByZWdyZXNzaW9uLCB1c2UgZmFtaWx5ID0gZ2F1c3NpYW4gKHRoZSBkZWZhdWx0KS4NCg0KVGhlIHNveWJlYW4gdHJhaW5pbmcgZGF0YSwgc295YmVhbl90cmFpbiBpcyBsb2FkZWQgaW50byB5b3VyIHdvcmtzcGFjZS4gSXQgaGFzIHR3byBjb2x1bW5zOiB0aGUgb3V0Y29tZSB3ZWlnaHQgYW5kIHRoZSB2YXJpYWJsZSBUaW1lLiBGb3IgY29tcGFyaXNvbiwgdGhlIGxpbmVhciBtb2RlbCBtb2RlbC5saW4sIHdoaWNoIHdhcyBmaXQgdXNpbmcgdGhlIGZvcm11bGEgd2VpZ2h0IH4gVGltZSBoYXMgYWxyZWFkeSBiZWVuIGxvYWRlZCBpbnRvIHRoZSB3b3Jrc3BhY2UgYXMgd2VsbC4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIHBsb3Qgd2VpZ2h0IHZlcnN1cyBUaW1lIChUaW1lIG9uIHgtYXhpcykuIERvZXMgdGhlIHJlbGF0aW9uc2hpcCBsb29rIGxpbmVhcj8NCg0KLSBMb2FkIHRoZSBwYWNrYWdlIG1nY3YuDQoNCi0gQ3JlYXRlIHRoZSBmb3JtdWxhIGZtbGEuZ2FtIHRvIGV4cHJlc3Mgd2VpZ2h0IGFzIGEgbm9uLWxpbmVhciBmdW5jdGlvbiBvZiBUaW1lLiBQcmludCBpdC4NCg0KLSBGaXQgYSBnZW5lcmFsaXplZCBhZGRpdGl2ZSBtb2RlbCBvbiBzb3liZWFuX3RyYWluIHVzaW5nIGZtbGEuZ2FtLg0KDQotIENhbGwgc3VtbWFyeSgpIG9uIHRoZSBsaW5lYXIgbW9kZWwgbW9kZWwubGluIChhbHJlYWR5IGluIHlvdXIgd29ya3NwYWNlKS4gV2hhdCBpcyB0aGUgUjI/DQoNCi0gQ2FsbCBzdW1tYXJ5KCkgb24gJ21vZGVsLmdhbS4gVGhlICJkZXZpYW5jZSBleHBsYWluZWQiIHJlcG9ydHMgdGhlIG1vZGVsJ3MgdW5hZGp1c3RlZCBSMi4gV2hhdCBpcyB0aGUgUjI/DQoNCi0gV2hpY2ggbW9kZWwgYXBwZWFycyB0byBiZSBhIGJldHRlciBmaXQgdG8gdGhlIHRyYWluaW5nIGRhdGE/DQoNCi0gQ2FsbCBwbG90KCkgb24gbW9kZWwuZ2FtIHRvIHNlZSB0aGUgZGVyaXZlZCByZWxhdGlvbnNoaXAgYmV0d2VlbiBUaW1lIGFuZCB3ZWlnaHQuDQoNCmBgYHtyfQ0KIyBzb3liZWFuX3RyYWluIGlzIGluIHRoZSB3b3Jrc3BhY2UNCnNveWJlYW5fdHJhaW48LXJlYWQuY3N2KCJzb3liZWFuX3RyYWluLmNzdiIpDQpzdW1tYXJ5KHNveWJlYW5fdHJhaW4pDQoNCiMgUGxvdCB3ZWlnaHQgdnMgVGltZSAoVGltZSBvbiB4IGF4aXMpDQpnZ3Bsb3Qoc295YmVhbl90cmFpbiwgYWVzKHggPSBUaW1lLCB5ID0gd2VpZ2h0KSkgKyANCiAgZ2VvbV9wb2ludCgpDQoNCiMgTG9hZCB0aGUgcGFja2FnZSBtZ2N2DQojaW5zdGFsbC5wYWNrYWdlcygibmxtZSIpDQojaW5zdGFsbC5wYWNrYWdlcygibWdjdiIpDQojbGlicmFyeShubG1lKQ0KI2xpYnJhcnkobWdjdikNCg0KaW5zdGFsbC5wYWNrYWdlcygnZ2FtJykNCmxpYnJhcnkoZ2FtKQ0KIyBDcmVhdGUgdGhlIGZvcm11bGEgDQooZm1sYS5nYW0gPC0gd2VpZ2h0fnMoVGltZSkgKQ0KKGZtbGEubGluIDwtIHdlaWdodH5UaW1lICkNCiMgRml0IHRoZSBHQU0gTW9kZWwNCm1vZGVsLmdhbSA8LSBnYW0oZm1sYS5nYW0sIGRhdGE9c295YmVhbl90cmFpbikNCm1vZGVsLmxpbiA8LSBnYW0oZm1sYS5saW4sIGRhdGE9c295YmVhbl90cmFpbikNCiMgQ2FsbCBzdW1tYXJ5KCkgb24gbW9kZWwubGluIGFuZCBsb29rIGZvciBSLXNxdWFyZWQNCnN1bW1hcnkobW9kZWwubGluKQ0KDQojIENhbGwgc3VtbWFyeSgpIG9uIG1vZGVsLmdhbSBhbmQgbG9vayBmb3IgUi1zcXVhcmVkDQpzdW1tYXJ5KG1vZGVsLmdhbSkNCg0KIyBDYWxsIHBsb3QoKSBvbiBtb2RlbC5nYW0NCnBsb3QobW9kZWwuZ2FtKQ0KYGBgDQoNCiMjIDQuMTA6IFByZWRpY3Qgd2l0aCB0aGUgc295YmVhbiBtb2RlbCBvbiB0ZXN0IGRhdGENCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBhcHBseSB0aGUgc295YmVhbiBtb2RlbHMgZnJvbSB0aGUgcHJldmlvdXMgZXhlcmNpc2UgKG1vZGVsLmxpbiBhbmQgbW9kZWwuZ2FtLCBhbHJlYWR5IGluIHlvdXIgd29ya3NwYWNlKSB0byBuZXcgZGF0YTogc295YmVhbl90ZXN0Lg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIGRhdGEgZnJhbWUgc295YmVhbi50ZXN0IGFuZCB0aGUgbW9kZWxzIG1vZGVsLmxpbiBhbmQgbW9kZWwuZ2FtIGFyZSBpbiB0aGUgd29ya3NwYWNlLg0KDQotIENyZWF0ZSBhIGNvbHVtbiBzb3liZWFuX3Rlc3QkcHJlZC5saW4gd2l0aCBwcmVkaWN0aW9ucyBmcm9tIHRoZSBsaW5lYXIgbW9kZWwgbW9kZWwubGluLg0KDQotIENyZWF0ZSBhIGNvbHVtbiBzb3liZWFuX3Rlc3QkcHJlZC5nYW0gd2l0aCBwcmVkaWN0aW9ucyBmcm9tIHRoZSBnYW0gbW9kZWwgbW9kZWwuZ2FtLg0KDQotIEZvciBHQU0gbW9kZWxzLCB0aGUgcHJlZGljdCgpIG1ldGhvZCByZXR1cm5zIGEgbWF0cml4LCBzbyB1c2UgYXMubnVtZXJpYygpIHRvIGNvbnZlcnQgdGhlIG1hdHJpeCB0byBhIHZlY3Rvci4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gZ2F0aGVyKCkgdGhlIHByZWRpY3Rpb24gY29sdW1ucyBpbnRvIGEgc2luZ2xlIHZhbHVlIGNvbHVtbiBwcmVkIHdpdGgga2V5IGNvbHVtbiBtb2RlbHR5cGUuIENhbGwgdGhlIGxvbmcgZGF0YWZyYW1lIHNveWJlYW5fbG9uZy4NCg0KLSBDYWxjdWxhdGUgYW5kIGNvbXBhcmUgdGhlIFJNU0Ugb2YgYm90aCBtb2RlbHMuDQoNCi0gV2hpY2ggbW9kZWwgZG9lcyBiZXR0ZXI/DQoNCi0gUnVuIHRoZSBjb2RlIHRvIGNvbXBhcmUgdGhlIHByZWRpY3Rpb25zIG9mIGVhY2ggbW9kZWwgYWdhaW5zdCB0aGUgYWN0dWFsIGF2ZXJhZ2UgbGVhZiB3ZWlnaHRzLg0KDQotIEEgc2NhdHRlciBwbG90IG9mIHdlaWdodCBhcyBhIGZ1bmN0aW9uIG9mIFRpbWUuDQoNCi0gUG9pbnQtYW5kLWxpbmUgcGxvdHMgb2YgdGhlIHByZWRpY3Rpb25zIChwcmVkKSBhcyBhIGZ1bmN0aW9uIG9mIFRpbWUuDQoNCi0gTm90aWNlIHRoYXQgdGhlIGxpbmVhciBtb2RlbCBzb21ldGltZXMgcHJlZGljdHMgbmVnYXRpdmUgd2VpZ2h0cyEgRG9lcyB0aGUgZ2FtIG1vZGVsPw0KYGBge3J9DQojbGlicmFyeShtZ2N2KQ0KIyBzb3liZWFuX3Rlc3QgaXMgaW4gdGhlIHdvcmtzcGFjZQ0Kc295YmVhbl90ZXN0PC1yZWFkLmNzdigic295YmVhbl90ZXN0LmNzdiIpDQpzdW1tYXJ5KHNveWJlYW5fdGVzdCkNCg0KIyBHZXQgcHJlZGljdGlvbnMgZnJvbSBsaW5lYXIgbW9kZWwNCnNveWJlYW5fdGVzdCRwcmVkLmxpbiA8LSBwcmVkaWN0KG1vZGVsLmxpbiwgbmV3ZGF0YSA9IHNveWJlYW5fdGVzdCkNCg0KIyBHZXQgcHJlZGljdGlvbnMgZnJvbSBnYW0gbW9kZWwNCnNveWJlYW5fdGVzdCRwcmVkLmdhbSA8LSBhcy5udW1lcmljKHByZWRpY3QobW9kZWwuZ2FtLCBuZXdkYXRhID0gc295YmVhbl90ZXN0KSkNCg0KIyBHYXRoZXIgdGhlIHByZWRpY3Rpb25zIGludG8gYSAibG9uZyIgZGF0YXNldA0Kc295YmVhbl9sb25nIDwtIHNveWJlYW5fdGVzdCAlPiUNCiAgZ2F0aGVyKGtleSA9IG1vZGVsdHlwZSwgdmFsdWUgPSBwcmVkLCBwcmVkLmxpbiwgcHJlZC5nYW0pDQoNCiMgQ2FsY3VsYXRlIHRoZSBybXNlDQpzb3liZWFuX2xvbmcgJT4lDQogIG11dGF0ZShyZXNpZHVhbCA9IHdlaWdodCAtIHByZWQpICU+JSAgICAgIyByZXNpZHVhbHMNCiAgZ3JvdXBfYnkobW9kZWx0eXBlKSAlPiUgICAgICAgICAgICAgICAgICAjIGdyb3VwIGJ5IG1vZGVsdHlwZQ0KICBzdW1tYXJpemUocm1zZSA9IHNxcnQobWVhbihyZXNpZHVhbF4yKSkpICMgY2FsY3VsYXRlIHRoZSBSTVNFDQoNCiMgQ29tcGFyZSB0aGUgcHJlZGljdGlvbnMgYWdhaW5zdCBhY3R1YWwgd2VpZ2h0cyBvbiB0aGUgdGVzdCBkYXRhDQpzb3liZWFuX2xvbmcgJT4lDQogIGdncGxvdChhZXMoeCA9IFRpbWUpKSArICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBjb2x1bW4gZm9yIHRoZSB4IGF4aXMNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHdlaWdodCkpICsgICAgICAgICAgICAgICAgICAgICMgdGhlIHktY29sdW1uIGZvciB0aGUgc2NhdHRlcnBsb3QNCiAgZ2VvbV9wb2ludChhZXMoeSA9IHByZWQsIGNvbG9yID0gbW9kZWx0eXBlKSkgKyAgICMgdGhlIHktY29sdW1uIGZvciB0aGUgcG9pbnQtYW5kLWxpbmUgcGxvdA0KICBnZW9tX2xpbmUoYWVzKHkgPSBwcmVkLCBjb2xvciA9IG1vZGVsdHlwZSwgbGluZXR5cGUgPSBtb2RlbHR5cGUpKSArICMgdGhlIHktY29sdW1uIGZvciB0aGUgcG9pbnQtYW5kLWxpbmUgcGxvdA0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpDQogIA0KYGBgDQpSZW1hcms6IFRoZSBHQU0gbGVhcm5zIHRoZSBub24tbGluZWFyIGdyb3d0aCBmdW5jdGlvbiBvZiB0aGUgc295YmVhbiBwbGFudHMsIGluY2x1ZGluZyB0aGUgZmFjdCB0aGF0IHdlaWdodCBpcyBuZXZlciBuZWdhdGl2ZS4NCg0KIyBDaGFwdGVyIDU6IFRyZWUtQmFzZWQgTWV0aG9kcw0KDQpJbiB0aGlzIGNoYXB0ZXIgd2Ugd2lsbCBsb29rIGF0IG1vZGVsaW5nIGFsZ29yaXRobXMgdGhhdCBkbyBub3QgYXNzdW1lIGxpbmVhcml0eSBvciBhZGRpdGl2aXR5LCBhbmQgdGhhdCBjYW4gbGVhcm4gbGltaXRlZCB0eXBlcyBvZiBpbnRlcmFjdGlvbnMgYW1vbmcgaW5wdXQgdmFyaWFibGVzLiBUaGVzZSBhbGdvcml0aG1zIGFyZSAqdHJlZS1iYXNlZCogbWV0aG9kcyB0aGF0IHdvcmsgYnkgY29tYmluaW5nIGVuc2VtYmxlcyBvZiAqZGVjaXNpb24gdHJlZXMqIHRoYXQgYXJlIGxlYXJuZWQgZnJvbSB0aGUgdHJhaW5pbmcgZGF0YS4NCg0KIyMgNS4xOiBCdWlsZCBhIHJhbmRvbSBmb3Jlc3QgbW9kZWwgZm9yIGJpa2UgcmVudGFscw0KDQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSB3aWxsIGFnYWluIGJ1aWxkIGEgbW9kZWwgdG8gcHJlZGljdCB0aGUgbnVtYmVyIG9mIGJpa2VzIHJlbnRlZCBpbiBhbiBob3VyIGFzIGEgZnVuY3Rpb24gb2YgdGhlIHdlYXRoZXIsIHRoZSB0eXBlIG9mIGRheSAoaG9saWRheSwgd29ya2luZyBkYXksIG9yIHdlZWtlbmQpLCBhbmQgdGhlIHRpbWUgb2YgZGF5LiBZb3Ugd2lsbCB0cmFpbiB0aGUgbW9kZWwgb24gZGF0YSBmcm9tIHRoZSBtb250aCBvZiBKdWx5Lg0KDQpZb3Ugd2lsbCB1c2UgdGhlIHJhbmdlciBwYWNrYWdlIHRvIGZpdCB0aGUgcmFuZG9tIGZvcmVzdCBtb2RlbC4gRm9yIHRoaXMgZXhlcmNpc2UsIHRoZSBrZXkgYXJndW1lbnRzIHRvIHRoZSByYW5nZXIoKSBjYWxsIGFyZToNCg0KLSBmb3JtdWxhDQoNCi0gZGF0YQ0KDQotIG51bS50cmVlczogdGhlIG51bWJlciBvZiB0cmVlcyBpbiB0aGUgZm9yZXN0Lg0KDQotIHJlc3BlY3QudW5vcmRlcmVkLmZhY3RvcnMgOiBTcGVjaWZpZXMgaG93IHRvIHRyZWF0IHVub3JkZXJlZCBmYWN0b3IgdmFyaWFibGVzLiBXZSByZWNvbW1lbmQgc2V0dGluZyB0aGlzIHRvICJvcmRlciIgZm9yIHJlZ3Jlc3Npb24uDQoNCi0gc2VlZDogYmVjYXVzZSB0aGlzIGlzIGEgcmFuZG9tIGFsZ29yaXRobSwgeW91IHdpbGwgc2V0IHRoZSBzZWVkIHRvIGdldCByZXByb2R1Y2libGUgcmVzdWx0cw0KDQpTaW5jZSB0aGVyZSBhcmUgYSBsb3Qgb2YgaW5wdXQgdmFyaWFibGVzLCBmb3IgY29udmVuaWVuY2Ugd2Ugd2lsbCBzcGVjaWZ5IHRoZSBvdXRjb21lIGFuZCB0aGUgaW5wdXRzIGluIHRoZSB2YXJpYWJsZXMgb3V0Y29tZSBhbmQgdmFycywgYW5kIHVzZSBwYXN0ZSgpIHRvIGFzc2VtYmxlIGEgc3RyaW5nIHJlcHJlc2VudGluZyB0aGUgbW9kZWwgZm9ybXVsYS4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQpUaGUgZGF0YSBmcmFtZSBiaWtlc0p1bHkgaXMgaW4gdGhlIHdvcmtzcGFjZS4gVGhlIHNhbXBsZSBjb2RlIHNwZWNpZmllcyB0aGUgbmFtZXMgb2YgdGhlIG91dGNvbWUgYW5kIGlucHV0IHZhcmlhYmxlcy4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gY3JlYXRlIHRoZSBmb3JtdWxhIGZtbGEgZXhwcmVzc2luZyBjbnQgYXMgYSBmdW5jdGlvbiBvZiB0aGUgaW5wdXRzLiBQcmludCBpdC4NCg0KLSBMb2FkIHRoZSBwYWNrYWdlIHJhbmdlci4NCg0KLSBVc2UgcmFuZ2VyIHRvIGZpdCBhIG1vZGVsIHRvIHRoZSBiaWtlc0p1bHkgZGF0YTogYmlrZV9tb2RlbF9yZi4NCg0KLSBUaGUgZmlyc3QgYXJndW1lbnQgdG8gcmFuZ2VyKCkgaXMgdGhlIGZvcm11bGEsIGZtbGEuDQoNCi0gVXNlIDUwMCB0cmVlcyBhbmQgcmVzcGVjdC51bm9yZGVyZWQuZmFjdG9ycyA9ICJvcmRlciIuDQoNCi0gU2V0IHRoZSBzZWVkIHRvIHNlZWQgZm9yIHJlcHJvZHVjaWJsZSByZXN1bHRzLg0KDQotIFByaW50IHRoZSBtb2RlbC4gV2hhdCBpcyB0aGUgUi1zcXVhcmVkPw0KDQpgYGB7cn0NCiMgYmlrZXNKdWx5IGlzIGluIHRoZSB3b3Jrc3BhY2UNCnN0cihiaWtlc0p1bHkpDQoNCiMgUmFuZG9tIHNlZWQgdG8gcmVwcm9kdWNlIHJlc3VsdHMNCnNlZWQ8LTQyMzU2Mw0KDQojIFRoZSBvdXRjb21lIGNvbHVtbg0KKG91dGNvbWUgPC0gImNudCIpDQoNCiMgVGhlIGlucHV0IHZhcmlhYmxlcw0KKHZhcnMgPC0gYygiaHIiLCAiaG9saWRheSIsICJ3b3JraW5nZGF5IiwgIndlYXRoZXJzaXQiLCAidGVtcCIsICJhdGVtcCIsICJodW0iLCAid2luZHNwZWVkIikpDQoNCiMgQ3JlYXRlIHRoZSBmb3JtdWxhIHN0cmluZyBmb3IgYmlrZXMgcmVudGVkIGFzIGEgZnVuY3Rpb24gb2YgdGhlIGlucHV0cw0KKGZtbGEgPC0gcGFzdGUob3V0Y29tZSwgIn4iLCBwYXN0ZSh2YXJzLCBjb2xsYXBzZSA9ICIgKyAiKSkpDQoNCiMgTG9hZCB0aGUgcGFja2FnZSByYW5nZXINCmluc3RhbGwucGFja2FnZXMoInJhbmdlciIpDQpsaWJyYXJ5KHJhbmdlcikNCg0KIyBGaXQgYW5kIHByaW50IHRoZSByYW5kb20gZm9yZXN0IG1vZGVsDQooYmlrZV9tb2RlbF9yZiA8LSByYW5nZXIoZm1sYSwgIyBmb3JtdWxhIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGJpa2VzSnVseSwgIyBkYXRhDQogICAgICAgICAgICAgICAgICAgICAgICAgbnVtLnRyZWVzID0gNTAwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICByZXNwZWN0LnVub3JkZXJlZC5mYWN0b3JzID0gIm9yZGVyIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgc2VlZCA9IHNlZWQpKQ0KYGBgDQoNCiMjIDUuMjogUHJlZGljdCBiaWtlIHJlbnRhbHMgd2l0aCB0aGUgcmFuZG9tIGZvcmVzdCBtb2RlbA0KDQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSB3aWxsIHVzZSB0aGUgbW9kZWwgdGhhdCB5b3UgZml0IGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSB0byBwcmVkaWN0IGJpa2UgcmVudGFscyBmb3IgdGhlIG1vbnRoIG9mIEF1Z3VzdC4NCg0KVGhlIHByZWRpY3QoKSBmdW5jdGlvbiBmb3IgYSByYW5nZXIgbW9kZWwgcHJvZHVjZXMgYSBsaXN0LiBPbmUgb2YgdGhlIGVsZW1lbnRzIG9mIHRoaXMgbGlzdCBpcyBwcmVkaWN0aW9ucywgYSB2ZWN0b3Igb2YgcHJlZGljdGVkIHZhbHVlcy4gWW91IGNhbiBhY2Nlc3MgcHJlZGljdGlvbnMgd2l0aCB0aGUgJCBub3RhdGlvbiBmb3IgYWNjZXNzaW5nIG5hbWVkIGVsZW1lbnRzIG9mIGEgbGlzdDoNCg0KcHJlZGljdChtb2RlbCwgZGF0YSkkcHJlZGljdGlvbnMNCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBtb2RlbCBiaWtlX21vZGVsX3JmIGFuZCB0aGUgZGF0YXNldCBiaWtlc0F1Z3VzdCAoZm9yIGV2YWx1YXRpb24pIGFyZSBsb2FkZWQgaW50byB5b3VyIHdvcmtzcGFjZS4NCg0KLSBDYWxsIHByZWRpY3QoKSBvbiBiaWtlc0F1Z3VzdCB0byBwcmVkaWN0IHRoZSBudW1iZXIgb2YgYmlrZXMgcmVudGVkIGluIEF1Z3VzdCAoY250KS4gQWRkIHRoZSBwcmVkaWN0aW9ucyB0byBiaWtlc0F1Z3VzdCBhcyB0aGUgY29sdW1uIHByZWQuDQoNCi0gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIGNhbGN1bGF0ZSB0aGUgcm9vdCBtZWFuIHNxdWFyZWQgZXJyb3Igb2YgdGhlIHByZWRpY3Rpb25zLg0KDQotIFRoZSBwb2lzc29uIG1vZGVsIHlvdSBidWlsdCBmb3IgdGhpcyBkYXRhIGdhdmUgYW4gUk1TRSBvZiBhYm91dCAxMTIuNi4gSG93IGRvZXMgdGhpcyBtb2RlbCBjb21wYXJlPw0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBwbG90IGFjdHVhbCBiaWtlIHJlbnRhbCBjb3VudHMgKGNudCkgdmVyc3VzIHRoZSBwcmVkaWN0aW9ucyAocHJlZCBvbiB4LWF4aXMpLg0KYGBge3J9DQojIGJpa2VzQXVndXN0IGlzIGluIHRoZSB3b3Jrc3BhY2UNCnN0cihiaWtlc0F1Z3VzdCkNCg0KIyBiaWtlX21vZGVsX3JmIGlzIGluIHRoZSB3b3Jrc3BhY2UNCmJpa2VfbW9kZWxfcmYNCg0KIyBNYWtlIHByZWRpY3Rpb25zIG9uIHRoZSBBdWd1c3QgZGF0YQ0KYmlrZXNBdWd1c3QkcHJlZCA8LSBwcmVkaWN0KGJpa2VfbW9kZWxfcmYsIGJpa2VzQXVndXN0KSRwcmVkaWN0aW9ucw0KDQojIENhbGN1bGF0ZSB0aGUgUk1TRSBvZiB0aGUgcHJlZGljdGlvbnMNCmJpa2VzQXVndXN0ICU+JSANCiAgbXV0YXRlKHJlc2lkdWFsID0gY250LXByZWQpICAlPiUgIyBjYWxjdWxhdGUgdGhlIHJlc2lkdWFsDQogIHN1bW1hcml6ZShybXNlICA9IHNxcnQobWVhbihyZXNpZHVhbF4yKSkpICAgICAgIyBjYWxjdWxhdGUgcm1zZQ0KDQojIFBsb3QgYWN0dWFsIG91dGNvbWUgdnMgcHJlZGljdGlvbnMgKHByZWRpY3Rpb25zIG9uIHgtYXhpcykNCmdncGxvdChiaWtlc0F1Z3VzdCwgYWVzKHggPSBwcmVkLCB5ID0gY250KSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fYWJsaW5lKCkNCmBgYA0KDQojIyA1LjM6IFZpc3VhbGl6ZSByYW5kb20gZm9yZXN0IGJpa2UgbW9kZWwgcHJlZGljdGlvbnMNCg0KSW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCB5b3Ugc2F3IHRoYXQgdGhlIHJhbmRvbSBmb3Jlc3QgYmlrZSBtb2RlbCBkaWQgYmV0dGVyIG9uIHRoZSBBdWd1c3QgZGF0YSB0aGFuIHRoZSBxdWFzaXBvc3NvbiBtb2RlbCwgaW4gdGVybXMgb2YgUk1TRS4NCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCB2aXN1YWxpemUgdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwncyBBdWd1c3QgcHJlZGljdGlvbnMgYXMgYSBmdW5jdGlvbiBvZiB0aW1lLiBUaGUgY29ycmVzcG9uZGluZyBwbG90IGZyb20gdGhlIHF1YXNpcG9pc3NvbiBtb2RlbCB0aGF0IHlvdSBidWlsdCBpbiBhIHByZXZpb3VzIGV4ZXJjaXNlIGlzIGluIHRoZSB3b3Jrc3BhY2UgZm9yIHlvdSB0byBjb21wYXJlLg0KDQpSZWNhbGwgdGhhdCB0aGUgcXVhc2lwb2lzc29uIG1vZGVsIG1vc3RseSBpZGVudGlmaWVkIHRoZSBwYXR0ZXJuIG9mIHNsb3cgYW5kIGJ1c3kgaG91cnMgaW4gdGhlIGRheSwgYnV0IGl0IHNvbWV3aGF0IHVuZGVyZXN0aW1hdGVkIHBlYWsgZGVtYW5kcy4gWW91IHdvdWxkIGxpa2UgdG8gc2VlIGhvdyB0aGUgcmFuZG9tIGZvcmVzdCBtb2RlbCBjb21wYXJlcy4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQotIFRoZSBkYXRhIGZyYW1lIGJpa2VzQXVndXN0ICh3aXRoIHByZWRpY3Rpb25zKSBpcyBpbiB0aGUgd29ya3NwYWNlLiBUaGUgcGxvdCBxdWFzaXBvaXNzb25fcGxvdCBvZiBxdWFzaXBvaXNzb24gbW9kZWwgcHJlZGljdGlvbnMgYXMgYSBmdW5jdGlvbiBvZiB0aW1lIGlzIGFsc28gaW4gdGhlIHdvcmtzcGFjZS4NCg0KLSBQcmludCBxdWFzaXBvaXNzb25fcGxvdCB0byByZXZpZXcgdGhlIHF1YXNpcG9pc3NvbiBtb2RlbCdzIGJlaGF2aW9yLg0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBwbG90IHRoZSBwcmVkaWN0aW9ucyBhbmQgYWN0dWFsIGNvdW50cyBieSBob3VyIGZvciB0aGUgZmlyc3QgMTQgZGF5cyBvZiBBdWd1c3QuDQpnYXRoZXIgdGhlIGNudCBhbmQgcHJlZCBjb2x1bW5zIGludG8gYSBjb2x1bW4gY2FsbGVkIHZhbHVlLCB3aXRoIGEga2V5IGNhbGxlZCB2YWx1ZXR5cGUuDQoNCi0gUGxvdCB2YWx1ZSBhcyBhIGZ1bmN0aW9uIG9mIGluc3RhbnQgKGRheSkuDQoNCi0gSG93IGRvZXMgdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwgY29tcGFyZT8NCg0KYGBge3J9DQojIFByaW50IHF1YXNpcG9pc3Nvbl9wbG90DQpxdWFzaXBvaXNzb25fcGxvdA0KDQojIFBsb3QgcHJlZGljdGlvbnMgYW5kIGNudCBieSBkYXRlL3RpbWUNCnJhbmRvbWZvcmVzdF9wbG90PC1iaWtlc0F1Z3VzdCAlPiUgDQogIG11dGF0ZShpbnN0YW50ID0gKGluc3RhbnQgLSBtaW4oaW5zdGFudCkpLzI0KSAlPiUgICMgc2V0IHN0YXJ0IHRvIDAsIGNvbnZlcnQgdW5pdCB0byBkYXlzDQogIGdhdGhlcihrZXkgPSB2YWx1ZXR5cGUsIHZhbHVlID0gdmFsdWUsIGNudCwgcHJlZCkgJT4lDQogIGZpbHRlcihpbnN0YW50IDwgMTQpICU+JSAjIGZpcnN0IHR3byB3ZWVrcw0KICBnZ3Bsb3QoYWVzKHggPSBpbnN0YW50LCB5ID0gdmFsdWUsIGNvbG9yID0gdmFsdWV0eXBlLCBsaW5ldHlwZSA9IHZhbHVldHlwZSkpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX2xpbmUoKSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoIkRheSIsIGJyZWFrcyA9IDA6MTQsIGxhYmVscyA9IDA6MTQpICsgDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKyANCiAgZ2d0aXRsZSgiUHJlZGljdGVkIEF1Z3VzdCBiaWtlIHJlbnRhbHMsIFJhbmRvbSBGb3Jlc3QgcGxvdCIpDQoNCnJhbmRvbWZvcmVzdF9wbG90DQoNCmBgYA0KDQojIyA1LjQ6IHZ0cmVhdCBvbiBhIHNtYWxsIGV4YW1wbGUNCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCB1c2UgdnRyZWF0IHRvIG9uZS1ob3QtZW5jb2RlIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgb24gYSBzbWFsbCBleGFtcGxlLiB2dHJlYXQgY3JlYXRlcyBhIHRyZWF0bWVudCBwbGFuIHRvIHRyYW5zZm9ybSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaW50byBpbmRpY2F0b3IgdmFyaWFibGVzIChjb2RlZCAibGV2IiksIGFuZCB0byBjbGVhbiBiYWQgdmFsdWVzIG91dCBvZiBudW1lcmljYWwgdmFyaWFibGVzIChjb2RlZCAiY2xlYW4iKS4NCg0KVG8gZGVzaWduIGEgdHJlYXRtZW50IHBsYW4gdXNlIHRoZSBmdW5jdGlvbiBkZXNpZ25UcmVhdG1lbnRzWigpDQoNCi0gdHJlYXRwbGFuIDwtIGRlc2lnblRyZWF0bWVudHNaKGRhdGEsIHZhcmxpc3QpDQoNCi0gZGF0YTogdGhlIG9yaWdpbmFsIHRyYWluaW5nIGRhdGEgZnJhbWUNCg0KLSB2YXJsaXN0OiBhIHZlY3RvciBvZiBpbnB1dCB2YXJpYWJsZXMgdG8gYmUgdHJlYXRlZCAoYXMgc3RyaW5ncykuDQoNCi0gZGVzaWduVHJlYXRtZW50c1ooKSByZXR1cm5zIGEgbGlzdCB3aXRoIGFuIGVsZW1lbnQgc2NvcmVGcmFtZTogYSBkYXRhIGZyYW1lIHRoYXQgaW5jbHVkZXMgdGhlIG5hbWVzIGFuZCB0eXBlcyBvZiB0aGUgbmV3IHZhcmlhYmxlczoNCg0Kc2NvcmVGcmFtZSA8LSB0cmVhdHBsYW4gJT4lIA0KICAgICAgICAgICAgbWFncml0dHI6OnVzZV9zZXJpZXMoc2NvcmVGcmFtZSkgJT4lIA0KICAgICAgICAgICAgc2VsZWN0KHZhck5hbWUsIG9yaWdOYW1lLCBjb2RlKQ0KDQotIHZhck5hbWU6IHRoZSBuYW1lIG9mIHRoZSBuZXcgdHJlYXRlZCB2YXJpYWJsZQ0KDQotIG9yaWdOYW1lOiB0aGUgbmFtZSBvZiB0aGUgb3JpZ2luYWwgdmFyaWFibGUgdGhhdCB0aGUgdHJlYXRlZCB2YXJpYWJsZSBjb21lcyBmcm9tDQotIGNvZGU6IHRoZSB0eXBlIG9mIHRoZSBuZXcgdmFyaWFibGUuDQoNCi0gImNsZWFuIjogYSBudW1lcmljYWwgdmFyaWFibGUgd2l0aCBubyBOQXMgb3IgTmFOcw0KLSAibGV2IjogYW4gaW5kaWNhdG9yIHZhcmlhYmxlIGZvciBhIHNwZWNpZmljIGxldmVsIG9mIHRoZSBvcmlnaW5hbCBjYXRlZ29yaWNhbCB2YXJpYWJsZS4NCihtYWdyaXR0cjo6dXNlX3NlcmllcygpIGlzIGFuIGFsaWFzIGZvciAkIHRoYXQgeW91IGNhbiB1c2UgaW4gcGlwZXMuKQ0KDQpGb3IgdGhlc2UgZXhlcmNpc2VzLCB3ZSB3YW50IHZhck5hbWUgd2hlcmUgY29kZSBpcyBlaXRoZXIgImNsZWFuIiBvciAibGV2IjoNCg0KbmV3dmFybGlzdCA8LSBzY29yZUZyYW1lICU+JSANCiAgICAgICAgICAgICBmaWx0ZXIoY29kZSAlaW4lIGMoImNsZWFuIiwgImxldiIpICU+JQ0KICAgICAgICAgICAgIG1hZ3JpdHRyOjp1c2Vfc2VyaWVzKHZhck5hbWUpDQoNClRvIHRyYW5zZm9ybSB0aGUgZGF0YSBzZXQgaW50byBhbGwgbnVtZXJpY2FsIGFuZCBvbmUtaG90LWVuY29kZWQgdmFyaWFibGVzLCB1c2UgcHJlcGFyZSgpOg0KDQpkYXRhLnRyZWF0IDwtIHByZXBhcmUodHJlYXRwbGFuLCBkYXRhLCB2YXJSZXN0cmljdGlvbnMgPSBuZXd2YXJsaXN0KQ0KDQotIHRyZWF0cGxhbjogdGhlIHRyZWF0bWVudCBwbGFuDQoNCi0gZGF0YTogdGhlIGRhdGEgZnJhbWUgdG8gYmUgdHJlYXRlZA0KDQotIHZhclJlc3RyaWN0aW9uczogdGhlIHZhcmlhYmxlcyBkZXNpcmVkIGluIHRoZSB0cmVhdGVkIGRhdGENCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQpUaGUgZGF0YSBmcmFtZSBkZnJhbWUgYW5kIHRoZSBwYWNrYWdlIG1hZ3JpdHRyIGFyZSBsb2FkZWQgaW4gdGhlIHdvcmtzcGFjZS4NCg0KMS4gUHJpbnQgZGZyYW1lLiBXZSB3aWxsIGFzc3VtZSB0aGF0IGNvbG9yIGFuZCBzaXplIGFyZSBpbnB1dCB2YXJpYWJsZXMsIGFuZCBwb3B1bGFyaXR5IGlzIHRoZSBvdXRjb21lIHRvIGJlIHByZWRpY3RlZC4NCg0KMi4gQ3JlYXRlIGEgdmVjdG9yIGNhbGxlZCB2YXJzIHdpdGggdGhlIG5hbWVzIG9mIHRoZSBpbnB1dCB2YXJpYWJsZXMgKGFzIHN0cmluZ3MpLg0KDQozLiBMb2FkIHRoZSBwYWNrYWdlIHZ0cmVhdC4NCg0KNC4gVXNlIGRlc2lnblRyZWF0bWVudHNaKCkgdG8gY3JlYXRlIGEgdHJlYXRtZW50IHBsYW4gZm9yIHRoZSB2YXJpYWJsZXMgaW4gdmFycy4gQXNzaWduIGl0IHRvIHRoZSB2YXJpYWJsZSB0cmVhdHBsYW4uDQoNCjUuIEdldCBhbmQgZXhhbWluZSB0aGUgc2NvcmVGcmFtZSBmcm9tIHRoZSB0cmVhdG1lbnQgcGxhbiB0byBzZWUgdGhlIG1hcHBpbmcgZnJvbSBvbGQgdmFyaWFibGVzIHRvIG5ldyB2YXJpYWJsZXMuDQoNCjYuIFlvdSBvbmx5IG5lZWQgdGhlIGNvbHVtbnMgdmFyTmFtZSwgb3JpZ05hbWUgYW5kIGNvZGUuDQoNCjcuIFdoYXQgYXJlIHRoZSBuYW1lcyBvZiB0aGUgbmV3IGluZGljYXRvciB2YXJpYWJsZXM/IE9mIHRoZSBjb250aW51b3VzIHZhcmlhYmxlPw0KDQo4LiBDcmVhdGUgYSB2ZWN0b3IgbmV3dmFycyB0aGF0IGNvbnRhaW5zIHRoZSB2YXJpYWJsZSB2YXJOYW1lIHdoZXJlIGNvZGUgaXMgZWl0aGVyIGNsZWFuIG9yIGxldi4gUHJpbnQgaXQuDQoNCjkuIFVzZSBwcmVwYXJlKCkgdG8gY3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgZGZyYW1lLnRyZWF0IHRoYXQgaXMgYSBvbmUtaG90LWVuY29kZWQgdmVyc2lvbiBvZiBkZnJhbWUgKHdpdGhvdXQgdGhlIG91dGNvbWUgY29sdW1uKS4NCg0KMTAuUHJpbnQgaXQgYW5kIGNvbXBhcmUgdG8gZGZyYW1lLg0KDQpgYGB7cn0NCmRmcmFtZTwtcmVhZC5jc3YoImRmcmFtZS5jc3YiKQ0KIyBkZnJhbWUgaXMgaW4gdGhlIHdvcmtzcGFjZQ0KZGZyYW1lDQoNCiMgQ3JlYXRlIGFuZCBwcmludCBhIHZlY3RvciBvZiB2YXJpYWJsZSBuYW1lcw0KKHZhcnMgPC0gYygiY29sb3IiLCAic2l6ZSIpKQ0KDQojIExvYWQgdGhlIHBhY2thZ2UgdnRyZWF0DQpsaWJyYXJ5KHZ0cmVhdCkNCg0KIyBDcmVhdGUgdGhlIHRyZWF0bWVudCBwbGFuDQp0cmVhdHBsYW4gPC0gZGVzaWduVHJlYXRtZW50c1ooZGZyYW1lLCB2YXJzKQ0KDQojIEV4YW1pbmUgdGhlIHNjb3JlRnJhbWUNCihzY29yZUZyYW1lIDwtIHRyZWF0cGxhbiAlPiUNCiAgICB1c2Vfc2VyaWVzKHNjb3JlRnJhbWUpICU+JQ0KICAgIHNlbGVjdCh2YXJOYW1lLCBvcmlnTmFtZSwgY29kZSkpDQoNCiMgV2Ugb25seSB3YW50IHRoZSByb3dzIHdpdGggY29kZXMgImNsZWFuIiBvciAibGV2Ig0KKG5ld3ZhcnMgPC0gc2NvcmVGcmFtZSAlPiUNCiAgICBmaWx0ZXIoY29kZSAlaW4lIGMoImNsZWFuIiwgImxldiIpKSAlPiUNCiAgICB1c2Vfc2VyaWVzKHZhck5hbWUpKQ0KDQojIENyZWF0ZSB0aGUgdHJlYXRlZCB0cmFpbmluZyBkYXRhDQooZGZyYW1lLnRyZWF0IDwtIHByZXBhcmUodHJlYXRwbGFuLCBkZnJhbWUsIHZhclJlc3RyaWN0aW9uID0gbmV3dmFycykpDQpgYGANCg0KIyMgNS41OiBOb3ZlbCBsZXZlbHMNCg0KV2hlbiBhIGxldmVsIG9mIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgaXMgcmFyZSwgc29tZXRpbWVzIGl0IHdpbGwgZmFpbCB0byBzaG93IHVwIGluIHRyYWluaW5nIGRhdGEuIElmIHRoYXQgcmFyZSBsZXZlbCB0aGVuIGFwcGVhcnMgaW4gZnV0dXJlIGRhdGEsIGRvd25zdHJlYW0gbW9kZWxzIG1heSBub3Qga25vdyB3aGF0IHRvIGRvIHdpdGggaXQuIFdoZW4gc3VjaCBub3ZlbCBsZXZlbHMgYXBwZWFyLCB1c2luZyBtb2RlbC5tYXRyaXggb3IgY2FyZXQ6OmR1bW15VmFycyB0byBvbmUtaG90LWVuY29kZSB3aWxsIG5vdCB3b3JrIGNvcnJlY3RseS4NCg0KdnRyZWF0IGlzIGEgInNhZmVyIiBhbHRlcm5hdGl2ZSB0byBtb2RlbC5tYXRyaXggZm9yIG9uZS1ob3QtZW5jb2RpbmcsIGJlY2F1c2UgaXQgY2FuIG1hbmFnZSBub3ZlbCBsZXZlbHMgc2FmZWx5LiB2dHJlYXQgYWxzbyBtYW5hZ2VzIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBkYXRhIChib3RoIGNhdGVnb3JpY2FsIGFuZCBjb250aW51b3VzKS4NCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBzZWUgaG93IHZ0cmVhdCBoYW5kbGVzIGNhdGVnb3JpY2FsIHZhbHVlcyB0aGF0IGRpZCBub3QgYXBwZWFyIGluIHRoZSB0cmFpbmluZyBzZXQuIFRoZSB0cmVhdG1lbnQgcGxhbiB0cmVhdHBsYW4gYW5kIHRoZSBzZXQgb2YgdmFyaWFibGVzIG5ld3ZhcnMgZnJvbSB0aGUgcHJldmlvdXMgZXhlcmNpc2UgYXJlIHN0aWxsIGluIHlvdXIgd29ya3NwYWNlLiBkZnJhbWUgYW5kIGEgbmV3IGRhdGEgZnJhbWUgdGVzdGZyYW1lIGFyZSBhbHNvIGluIHlvdXIgd29ya3NwYWNlLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gUHJpbnQgZGZyYW1lIGFuZCB0ZXN0ZnJhbWUuDQoNCi0gQXJlIHRoZXJlIGNvbG9ycyBpbiB0ZXN0ZnJhbWUgdGhhdCBkaWRuJ3QgYXBwZWFyIGluIGRmcmFtZT8NCg0KLSBDYWxsIHByZXBhcmUoKSB0byBjcmVhdGUgYSBvbmUtaG90LWVuY29kZWQgdmVyc2lvbiBvZiB0ZXN0ZnJhbWUgKHdpdGhvdXQgdGhlIG91dGNvbWUpLiBDYWxsIGl0IHRlc3RmcmFtZS50cmVhdCBhbmQgcHJpbnQgaXQuDQoNCi0gVXNlIHRoZSB2YXJSZXN0cmljdGlvbiBhcmd1bWVudCB0byByZXN0cmljdCB0byBvbmx5IHRoZSB2YXJpYWJsZXMgaW4gbmV3dmFycy4NCg0KLSBIb3cgYXJlIHRoZSB5ZWxsb3cgcm93cyBlbmNvZGVkPw0KDQpgYGB7cn0NCnN1bW1hcnkodHJlYXRwbGFuKQ0KbmV3dmFycw0KDQp0ZXN0ZnJhbWU8LXJlYWQuY3N2KCJ0ZXN0ZnJhbWUuY3N2IikNCiMgUHJpbnQgZGZyYW1lIGFuZCB0ZXN0ZnJhbWUNCmRmcmFtZQ0KdGVzdGZyYW1lDQoNCiMgVXNlIHByZXBhcmUoKSB0byBvbmUtaG90LWVuY29kZSB0ZXN0ZnJhbWUNCih0ZXN0ZnJhbWUudHJlYXQgPC0gcHJlcGFyZSh0cmVhdHBsYW4sIHRlc3RmcmFtZSwgdmFyUmVzdHJpY3Rpb24gPSBuZXd2YXJzKSkNCmBgYA0KDQojIyA1LjY6IHZ0cmVhdCB0aGUgYmlrZSByZW50YWwgZGF0YQ0KDQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSB3aWxsIGNyZWF0ZSBvbmUtaG90LWVuY29kZWQgZGF0YSBmcmFtZXMgb2YgdGhlIEp1bHkvQXVndXN0IGJpa2UgZGF0YSwgZm9yIHVzZSB3aXRoIHhnYm9vc3QgbGF0ZXIgb24uDQoNClRoZSBkYXRhIGZyYW1lcyBiaWtlc0p1bHkgYW5kIGJpa2VzQXVndXN0IGFyZSBpbiB0aGUgd29ya3NwYWNlLg0KDQpGb3IgeW91ciBjb252ZW5pZW5jZSwgd2UgaGF2ZSBkZWZpbmVkIHRoZSB2YXJpYWJsZSB2YXJzIHdpdGggdGhlIGxpc3Qgb2YgdmFyaWFibGUgY29sdW1ucyBmb3IgdGhlIG1vZGVsLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gTG9hZCB0aGUgcGFja2FnZSB2dHJlYXQuDQoNCi0gVXNlIGRlc2lnblRyZWF0bWVudHNaKCkgdG8gY3JlYXRlIGEgdHJlYXRtZW50IHBsYW4gdHJlYXRwbGFuIGZvciB0aGUgdmFyaWFibGVzIGluIHZhcnMgZnJvbSBiaWtlc0p1bHkgKHRoZSB0cmFpbmluZyBkYXRhKS4NCg0KLSBTZXQgdGhlIGZsYWcgdmVyYm9zZT1GQUxTRSB0byBwcmV2ZW50IHRoZSBmdW5jdGlvbiBmcm9tIHByaW50aW5nIHRvbyBtYW55IG1lc3NhZ2VzLg0KDQotIEZpbGwgaW4gdGhlIGJsYW5rcyB0byBjcmVhdGUgYSB2ZWN0b3IgbmV3dmFycyB0aGF0IGNvbnRhaW5zIG9ubHkgdGhlIG5hbWVzIG9mIHRoZSBjbGVhbiBhbmQgbGV2IHRyYW5zZm9ybWVkIHZhcmlhYmxlcy4gUHJpbnQgaXQuDQoNCi0gVXNlIHByZXBhcmUoKSB0byBjcmVhdGUgYSBvbmUtaG90LWVuY29kZWQgdHJhaW5pbmcgZGF0YSBmcmFtZSBiaWtlc0p1bHkudHJlYXQuDQoNCi0gVXNlIHRoZSB2YXJSZXN0cmljdGlvbnMgYXJndW1lbnQgdG8gcmVzdHJpY3QgdGhlIHZhcmlhYmxlcyB5b3Ugd2lsbCB1c2UgdG8gbmV3dmFycy4NCg0KLSBVc2UgcHJlcGFyZSgpIHRvIGNyZWF0ZSBhIG9uZS1ob3QtZW5jb2RlZCB0ZXN0IGZyYW1lIGJpa2VzQXVndXN0LnRyZWF0IGZyb20gYmlrZXNBdWd1c3QgaW4gdGhlIHNhbWUgd2F5Lg0KDQotIENhbGwgc3RyKCkgb24gYm90aCBwcmVwYXJlZCB0ZXN0IGZyYW1lcyB0byBzZWUgdGhlIHN0cnVjdHVyZS4NCg0KYGBge3J9DQojIFRoZSBvdXRjb21lIGNvbHVtbg0KKG91dGNvbWUgPC0gImNudCIpDQoNCiMgVGhlIGlucHV0IGNvbHVtbnMNCih2YXJzIDwtIGMoImhyIiwgImhvbGlkYXkiLCAid29ya2luZ2RheSIsICJ3ZWF0aGVyc2l0IiwgInRlbXAiLCAiYXRlbXAiLCAiaHVtIiwgIndpbmRzcGVlZCIpKQ0KDQojIExvYWQgdGhlIHBhY2thZ2UgdnRyZWF0DQpsaWJyYXJ5KHZ0cmVhdCkNCg0KIyBDcmVhdGUgdGhlIHRyZWF0bWVudCBwbGFuIGZyb20gYmlrZXNKdWx5ICh0aGUgdHJhaW5pbmcgZGF0YSkNCnRyZWF0cGxhbiA8LSBkZXNpZ25UcmVhdG1lbnRzWihiaWtlc0p1bHksdmFycywgdmVyYm9zZSA9IEZBTFNFKQ0KDQojIEdldCB0aGUgImNsZWFuIiBhbmQgImxldiIgdmFyaWFibGVzIGZyb20gdGhlIHNjb3JlRnJhbWUNCihuZXd2YXJzIDwtIHRyZWF0cGxhbiAlPiUNCiAgdXNlX3NlcmllcyhzY29yZUZyYW1lKSAlPiUgICAgICAgIA0KICBmaWx0ZXIoY29kZSAlaW4lIGMoImNsZWFuIiwibGV2IikpICU+JSAgIyBnZXQgdGhlIHJvd3MgeW91IGNhcmUgYWJvdXQNCiAgdXNlX3Nlcmllcyh2YXJOYW1lKSkgICAgICAgICAgICMgZ2V0IHRoZSB2YXJOYW1lIGNvbHVtbg0KDQoNCiMgUHJlcGFyZSB0aGUgdHJhaW5pbmcgZGF0YQ0KYmlrZXNKdWx5LnRyZWF0IDwtIHByZXBhcmUodHJlYXRwbGFuLCBiaWtlc0p1bHksICB2YXJSZXN0cmljdGlvbiA9IG5ld3ZhcnMpDQoNCiMgUHJlcGFyZSB0aGUgdGVzdCBkYXRhDQpiaWtlc0F1Z3VzdC50cmVhdCA8LSBwcmVwYXJlKHRyZWF0cGxhbiwgYmlrZXNBdWd1c3QsICB2YXJSZXN0cmljdGlvbiA9IG5ld3ZhcnMpDQoNCiMgQ2FsbCBzdHIoKSBvbiB0aGUgdHJlYXRlZCBkYXRhDQpzdHIoYmlrZXNKdWx5LnRyZWF0KQ0Kc3RyKGJpa2VzQXVndXN0LnRyZWF0KQ0KYGBgDQoNCiMjIDUuNzogRmluZCB0aGUgcmlnaHQgbnVtYmVyIG9mIHRyZWVzIGZvciBhIGdyYWRpZW50IGJvb3N0aW5nIG1hY2hpbmUNCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBnZXQgcmVhZHkgdG8gYnVpbGQgYSBncmFkaWVudCBib29zdGluZyBtb2RlbCB0byBwcmVkaWN0IHRoZSBudW1iZXIgb2YgYmlrZXMgcmVudGVkIGluIGFuIGhvdXIgYXMgYSBmdW5jdGlvbiBvZiB0aGUgd2VhdGhlciBhbmQgdGhlIHR5cGUgYW5kIHRpbWUgb2YgZGF5LiBZb3Ugd2lsbCB0cmFpbiB0aGUgbW9kZWwgb24gZGF0YSBmcm9tIHRoZSBtb250aCBvZiBKdWx5Lg0KDQpUaGUgSnVseSBkYXRhIGlzIGxvYWRlZCBpbnRvIHlvdXIgd29ya3NwYWNlLiBSZW1lbWJlciB0aGF0IGJpa2VzSnVseS50cmVhdCBubyBsb25nZXIgaGFzIHRoZSBvdXRjb21lIGNvbHVtbiwgc28geW91IG11c3QgZ2V0IGl0IGZyb20gdGhlIHVudHJlYXRlZCBkYXRhOiBiaWtlc0p1bHkkY250Lg0KDQpZb3Ugd2lsbCB1c2UgdGhlIHhnYm9vc3QgcGFja2FnZSB0byBmaXQgdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwuIFRoZSBmdW5jdGlvbiB4Z2IuY3YoKSB1c2VzIGNyb3NzLXZhbGlkYXRpb24gdG8gZXN0aW1hdGUgdGhlIG91dC1vZi1zYW1wbGUgbGVhcm5pbmcgZXJyb3IgYXMgZWFjaCBuZXcgdHJlZSBpcyBhZGRlZCB0byB0aGUgbW9kZWwuIFRoZSBhcHByb3ByaWF0ZSBudW1iZXIgb2YgdHJlZXMgdG8gdXNlIGluIHRoZSBmaW5hbCBtb2RlbCBpcyB0aGUgbnVtYmVyIHRoYXQgbWluaW1pemVzIHRoZSBob2xkb3V0IFJNU0UuDQoNCkZvciB0aGlzIGV4ZXJjaXNlLCB0aGUga2V5IGFyZ3VtZW50cyB0byB0aGUgeGdiLmN2KCkgY2FsbCBhcmU6DQoNCi0gZGF0YTogYSBudW1lcmljIG1hdHJpeC4NCg0KLSBsYWJlbDogdmVjdG9yIG9mIG91dGNvbWVzIChhbHNvIG51bWVyaWMpLg0KDQotIG5yb3VuZHM6IHRoZSBtYXhpbXVtIG51bWJlciBvZiByb3VuZHMgKHRyZWVzIHRvIGJ1aWxkKS4NCg0KLSBuZm9sZDogdGhlIG51bWJlciBvZiBmb2xkcyBmb3IgdGhlIGNyb3NzLXZhbGlkYXRpb24uIDUgaXMgYSBnb29kIG51bWJlci4NCg0KLSBvYmplY3RpdmU6ICJyZWc6bGluZWFyIiBmb3IgY29udGludW91cyBvdXRjb21lcy4NCg0KLSBldGE6IHRoZSBsZWFybmluZyByYXRlLg0KDQotIG1heF9kZXB0aDogZGVwdGggb2YgdHJlZXMuDQoNCi0gZWFybHlfc3RvcHBpbmdfcm91bmRzOiBhZnRlciB0aGlzIG1hbnkgcm91bmRzIHdpdGhvdXQgaW1wcm92ZW1lbnQsIHN0b3AuDQoNCi0gdmVyYm9zZTogMCB0byBzdGF5IHNpbGVudC4NCg0KSW5zdHJ1Y3Rpb25zDQoNCjEwMCBYUA0KDQoNClRoZSBkYXRhIGZyYW1lcyBiaWtlc0p1bHkgYW5kIGJpa2VzSnVseS50cmVhdCBhcmUgaW4gdGhlIHdvcmtzcGFjZS4NCg0KMS4gTG9hZCB0aGUgcGFja2FnZSB4Z2Jvb3N0Lg0KDQoyLiBGaWxsIGluIHRoZSBibGFua3MgdG8gcnVuIHhnYi5jdigpIG9uIHRoZSB0cmVhdGVkIHRyYWluaW5nIGRhdGE7IGFzc2lnbiB0aGUgb3V0cHV0IHRvIHRoZSB2YXJpYWJsZSBjdi4NCg0KMy4gVXNlIGFzLm1hdHJpeCgpIHRvIGNvbnZlcnQgdGhlIHZ0cmVhdGVkIGRhdGEgZnJhbWUgdG8gYSBtYXRyaXguDQoNCjQuIFVzZSAxMDAgcm91bmRzLCBhbmQgNS1mb2xkIGNyb3NzIHZhbGlkYXRpb24uDQoNCjUuIFNldCBlYXJseV9zdG9wcGluZ19yb3VuZHMgdG8gMTAuDQoNCjYuIFNldCBldGEgdG8gMC4zLCBtYXhfZGVwdGggdG8gNi4NCg0KNy4gR2V0IHRoZSBkYXRhIGZyYW1lIGV2YWx1YXRpb25fbG9nIGZyb20gY3YgYW5kIGFzc2lnbiBpdCB0byB0aGUgdmFyaWFibGUgZWxvZy4gRWFjaCByb3cgb2YgdGhlIGV2YWx1YXRpb25fbG9nIGNvcnJlc3BvbmRzIHRvIGFuIGFkZGl0aW9uYWwgdHJlZSwgc28gdGhlIHJvdyBudW1iZXIgdGVsbHMgeW91IHRoZSBudW1iZXIgb2YgdHJlZXMgaW4gdGhlIG1vZGVsLg0KDQo4LiBGaWxsIGluIHRoZSBibGFua3MgdG8gZ2V0IHRoZSBudW1iZXIgb2YgdHJlZXMgd2l0aCB0aGUgbWluaW11bSB2YWx1ZSBvZiB0aGUgY29sdW1ucyB0cmFpbl9ybXNlX21lYW4gYW5kIHRlc3Rfcm1zZV9tZWFuLg0KDQo5LiB3aGljaC5taW4oKSByZXR1cm5zIHRoZSBpbmRleCBvZiB0aGUgbWluaW11bSB2YWx1ZSBpbiBhIHZlY3Rvci4NCkhvdyBtYW55IHRyZWVzIGRvIHlvdSBuZWVkPw0KDQpgYGB7cn0NCiMgVGhlIEp1bHkgZGF0YSBpcyBpbiB0aGUgd29ya3NwYWNlDQojbHMoKQ0KDQojIExvYWQgdGhlIHBhY2thZ2UgeGdib29zdA0KaW5zdGFsbC5wYWNrYWdlcygieGdib29zdCIpDQpsaWJyYXJ5KHhnYm9vc3QpDQoNCiMgUnVuIHhnYi5jdg0KY3YgPC0geGdiLmN2KGRhdGEgPSBhcy5tYXRyaXgoYmlrZXNKdWx5LnRyZWF0KSwgDQogICAgICAgICAgICBsYWJlbCA9IGJpa2VzSnVseSRjbnQsDQogICAgICAgICAgICBucm91bmRzID0gMTAwLA0KICAgICAgICAgICAgbmZvbGQgPSA1LA0KICAgICAgICAgICAgb2JqZWN0aXZlID0gInJlZzpsaW5lYXIiLA0KICAgICAgICAgICAgZXRhID0gMC4zLA0KICAgICAgICAgICAgbWF4X2RlcHRoID0gNiwNCiAgICAgICAgICAgIGVhcmx5X3N0b3BwaW5nX3JvdW5kcyA9IDEwLA0KICAgICAgICAgICAgdmVyYm9zZSA9IDAgICAgIyBzaWxlbnQNCikNCg0KIyBHZXQgdGhlIGV2YWx1YXRpb24gbG9nIA0KZWxvZyA8LSBjdiRldmFsdWF0aW9uX2xvZw0KDQojIERldGVybWluZSBhbmQgcHJpbnQgaG93IG1hbnkgdHJlZXMgbWluaW1pemUgdHJhaW5pbmcgYW5kIHRlc3QgZXJyb3INCmVsb2cgJT4lIA0KICAgc3VtbWFyaXplKG50cmVlcy50cmFpbiA9IHdoaWNoLm1pbih0cmFpbl9ybXNlX21lYW4pLCAgICMgZmluZCB0aGUgaW5kZXggb2YgbWluKHRyYWluX3Jtc2VfbWVhbikNCiAgICAgICAgICAgICBudHJlZXMudGVzdCAgPSB3aGljaC5taW4odGVzdF9ybXNlX21lYW4pICkgICMgZmluZCB0aGUgaW5kZXggb2YgbWluKHRlc3Rfcm1zZV9tZWFuKQ0KYGBgDQoNCiMjIDUuODogRml0IGFuIHhnYm9vc3QgYmlrZSByZW50YWwgbW9kZWwgYW5kIHByZWRpY3QNCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBmaXQgYSBncmFkaWVudCBib29zdGluZyBtb2RlbCB1c2luZyB4Z2Jvb3N0KCkgdG8gcHJlZGljdCB0aGUgbnVtYmVyIG9mIGJpa2VzIHJlbnRlZCBpbiBhbiBob3VyIGFzIGEgZnVuY3Rpb24gb2YgdGhlIHdlYXRoZXIgYW5kIHRoZSB0eXBlIGFuZCB0aW1lIG9mIGRheS4gWW91IHdpbGwgdHJhaW4gdGhlIG1vZGVsIG9uIGRhdGEgZnJvbSB0aGUgbW9udGggb2YgSnVseSBhbmQgcHJlZGljdCBvbiBkYXRhIGZvciB0aGUgbW9udGggb2YgQXVndXN0Lg0KDQpUaGUgZGF0YXNldHMgZm9yIEp1bHkgYW5kIEF1Z3VzdCBhcmUgbG9hZGVkIGludG8geW91ciB3b3Jrc3BhY2UuIFJlbWVtYmVyIHRoZSB2dHJlYXQtZWQgZGF0YSBubyBsb25nZXIgaGFzIHRoZSBvdXRjb21lIGNvbHVtbiwgc28geW91IG11c3QgZ2V0IGl0IGZyb20gdGhlIG9yaWdpbmFsIGRhdGEgKHRoZSBjbnQgY29sdW1uKS4NCg0KRm9yIGNvbnZlbmllbmNlLCB0aGUgbnVtYmVyIG9mIHRyZWVzIHRvIHVzZSwgbnRyZWVzIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlIGlzIGluIHRoZSB3b3Jrc3BhY2UuDQoNClRoZSBhcmd1bWVudHMgdG8geGdib29zdCgpIGFyZSBzaW1pbGFyIHRvIHRob3NlIG9mIHhnYi5jdigpLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIGRhdGEgZnJhbWVzIGJpa2VzSnVseSwgYmlrZXNKdWx5LnRyZWF0LCBiaWtlc0F1Z3VzdCBhbmQgYmlrZXNBdWd1c3QudHJlYXQgYXJlIGluIHRoZSB3b3Jrc3BhY2UuIFRoZSBudW1iZXIgb2YgdHJlZXMgbnRyZWVzIGlzIGluIHRoZSB3b3Jrc3BhY2UuDQoNCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gcnVuIHhnYm9vc3QoKSBvbiB0aGUgSnVseSBkYXRhLiBBc3NpZ24gdGhlIG1vZGVsIHRvIHRoZSB2YXJpYWJsZSBtb2RlbC4NCg0KLSBVc2UgYXMubWF0cml4KCkgdG8gY29udmVydCB0aGUgdnRyZWF0ZWQgZGF0YSBmcmFtZSB0byBhIG1hdHJpeC4NCg0KLSBUaGUgb2JqZWN0aXZlIHNob3VsZCBiZSAicmVnOmxpbmVhciIuDQoNCi0gVXNlIG50cmVlcyByb3VuZHMuDQoNCi0gU2V0IGV0YSB0byAwLjMsIGRlcHRoIHRvIDYsIGFuZCB2ZXJib3NlIHRvIDAgKHNpbGVudCkuDQoNCi0gTm93IGNhbGwgcHJlZGljdCgpIG9uIGJpa2VzQXVndXN0LnRyZWF0IHRvIHByZWRpY3QgdGhlIG51bWJlciBvZiBiaWtlcyByZW50ZWQgaW4gQXVndXN0Lg0KDQotIFVzZSBhcy5tYXRyaXgoKSB0byBjb252ZXJ0IHRoZSB2dHJlYXQtZWQgdGVzdCBkYXRhIGludG8gYSBtYXRyaXguDQoNCi0gQWRkIHRoZSBwcmVkaWN0aW9ucyB0b2Jpa2VzQXVndXN0IGFzIHRoZSBjb2x1bW4gcHJlZC4NCg0KLSBGaWxsIGluIHRoZSBibGFua3MgdG8gcGxvdCBhY3R1YWwgYmlrZSByZW50YWwgY291bnRzIHZlcnN1cyB0aGUgcHJlZGljdGlvbnMgKHByZWRpY3Rpb25zIG9uIHRoZSB4LWF4aXMpLiBEbyB5b3Ugc2VlIGEgcG9zc2libGUgcHJvYmxlbSB3aXRoIHRoZSBwcmVkaWN0aW9ucz8NCg0KYGBge3J9DQojIEV4YW1pbmUgdGhlIHdvcmtzcGFjZQ0KI2xzKCkNCg0KIyBUaGUgbnVtYmVyIG9mIHRyZWVzIHRvIHVzZSwgYXMgZGV0ZXJtaW5lZCBieSB4Z2IuY3YNCiNudHJlZXM8LWVsb2ckbnRyZWVzLnRyYWluK2Vsb2ckbnRyZWVzLnRlc3QNCm50cmVlczwtODQNCiMgUnVuIHhnYm9vc3QNCmJpa2VfbW9kZWxfeGdiIDwtIHhnYm9vc3QoZGF0YSA9IGFzLm1hdHJpeChiaWtlc0p1bHkudHJlYXQpLCAjIHRyYWluaW5nIGRhdGEgYXMgbWF0cml4DQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSBiaWtlc0p1bHkkY250LCAgIyBjb2x1bW4gb2Ygb3V0Y29tZXMNCiAgICAgICAgICAgICAgICAgICBucm91bmRzID0gbnRyZWVzLCAgICAgICAjIG51bWJlciBvZiB0cmVlcyB0byBidWlsZA0KICAgICAgICAgICAgICAgICAgIG9iamVjdGl2ZSA9ICJyZWc6bGluZWFyIiwgIyBvYmplY3RpdmUNCiAgICAgICAgICAgICAgICAgICBldGEgPSAwLjMsDQogICAgICAgICAgICAgICAgICAgZGVwdGggPSA2LA0KICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSAwICAjIHNpbGVudA0KKQ0KDQojIE1ha2UgcHJlZGljdGlvbnMNCmJpa2VzQXVndXN0JHByZWQgPC0gcHJlZGljdChiaWtlX21vZGVsX3hnYiwgYXMubWF0cml4KGJpa2VzQXVndXN0LnRyZWF0KSkNCg0KIyBQbG90IHByZWRpY3Rpb25zIChvbiB4IGF4aXMpIHZzIGFjdHVhbCBiaWtlIHJlbnRhbCBjb3VudA0KZ2dwbG90KGJpa2VzQXVndXN0LCBhZXMoeCA9IHByZWQsIHkgPSBjbnQpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9hYmxpbmUoKQ0KYGBgDQoNCiMjIDUuOTogRXZhbHVhdGUgdGhlIHhnYm9vc3QgYmlrZSByZW50YWwgbW9kZWwNCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBldmFsdWF0ZSB0aGUgZ3JhZGllbnQgYm9vc3RpbmcgbW9kZWwgYmlrZV9tb2RlbF94Z2IgdGhhdCB5b3UgZml0IGluIHRoZSBsYXN0IGV4ZXJjaXNlLCB1c2luZyBkYXRhIGZyb20gdGhlIG1vbnRoIG9mIEF1Z3VzdC4gWW91J2xsIGNvbXBhcmUgdGhpcyBtb2RlbCdzIFJNU0UgZm9yIEF1Z3VzdCB0byB0aGUgUk1TRSBvZiBwcmV2aW91cyBtb2RlbHMgdGhhdCB5b3UndmUgYnVpbHQuDQoNClRoZSBkYXRhc2V0IGJpa2VzQXVndXN0IGlzIGluIHRoZSB3b3Jrc3BhY2UuIFlvdSBoYXZlIGFscmVhZHkgbWFkZSBwcmVkaWN0aW9ucyB1c2luZyB0aGUgeGdib29zdCBtb2RlbDsgdGhleSBhcmUgaW4gdGhlIGNvbHVtbiBwcmVkLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIGNhbGN1bGF0ZSB0aGUgUk1TRSBvZiB0aGUgcHJlZGljdGlvbnMuDQoNCi0gSG93IGRvZXMgaXQgY29tcGFyZSB0byB0aGUgUk1TRSBmcm9tIHRoZSBwb2lzc29uIG1vZGVsIChhcHByb3guIDExMi42KSBhbmQgdGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwgKGFwcHJveC4gOTYuNyk/DQoNCmBgYHtyfQ0KIyBiaWtlc0F1Z3VzdCBpcyBpbiB0aGUgd29ya3NwYWNlDQpzdHIoYmlrZXNBdWd1c3QpDQoNCiMgQ2FsY3VsYXRlIFJNU0UNCmJpa2VzQXVndXN0ICU+JQ0KICBtdXRhdGUocmVzaWR1YWxzID0gY250IC0gcHJlZCkgJT4lDQogIHN1bW1hcml6ZShybXNlID0gc3FydChtZWFuKHJlc2lkdWFsc14yKSkpDQpgYGANCg0KDQojIyA1LjEwOiBWaXN1YWxpemUgdGhlIHhnYm9vc3QgYmlrZSByZW50YWwgbW9kZWwNCg0KWW91J3ZlIG5vdyBzZWVuIHRocmVlIGRpZmZlcmVudCB3YXlzIHRvIG1vZGVsIHRoZSBiaWtlIHJlbnRhbCBkYXRhLiBGb3IgdGhpcyBleGFtcGxlLCB5b3UndmUgc2VlbiB0aGF0IHRoZSBncmFkaWVudCBib29zdGluZyBtb2RlbCBoYWQgdGhlIHNtYWxsZXN0IFJNU0UuIFRvIGZpbmlzaCB1cCB0aGUgY291cnNlLCBsZXQncyBjb21wYXJlIHRoZSBncmFkaWVudCBib29zdGluZyBtb2RlbCdzIHByZWRpY3Rpb25zIHRvIHRoZSBvdGhlciB0d28gbW9kZWxzIGFzIGEgZnVuY3Rpb24gb2YgdGltZS4NCg0KT24gY29tcGxldGluZyB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBoYXZlIGNvbXBsZXRlZCB0aGUgY291cnNlLiBDb25ncmF0dWxhdGlvbnMhIE5vdyB5b3UgaGF2ZSB0aGUgdG9vbHMgdG8gYXBwbHkgYSB2YXJpZXR5IG9mIGFwcHJvYWNoZXMgdG8geW91ciByZWdyZXNzaW9uIHRhc2tzLg0KDQpJbnN0cnVjdGlvbnMNCg0KMTAwIFhQDQoNCi0gVGhlIGRhdGEgZnJhbWUgYmlrZXNBdWd1c3Qgd2l0aCBwcmVkaWN0aW9ucywgaXMgaW4gdGhlIHdvcmtzcGFjZS4gVGhlIHBsb3RzIHF1YXNpcG9pc3Nvbl9wbG90IGFuZCByYW5kb21mb3Jlc3RfcGxvdCBhcmUgYWxzbyBpbiB0aGUgd29ya3NwYWNlLg0KDQotIFByaW50IHF1YXNpcG9pc3Nvbl9wbG90IHRvIHJldmlldyB0aGUgcXVhc2lwb2lzc29uIG1vZGVsJ3MgYmVoYXZpb3IuDQoNCi0gUHJpbnQgcmFuZG9tZm9yZXN0X3Bsb3QgdG8gcmV2aWV3IHRoZSByYW5kb20gZm9yZXN0IG1vZGVsJ3MgYmVoYXZpb3IuDQoNCi0gRmlsbCBpbiB0aGUgYmxhbmtzIHRvIHBsb3QgdGhlIGdyYWRpZW50IGJvb3N0aW5nIHByZWRpY3Rpb25zIGFuZCBhY3R1YWwgY291bnRzIGJ5IGhvdXIgZm9yIHRoZSBmaXJzdCAxNCBkYXlzIG9mIEF1Z3VzdC4NCg0KLSBnYXRoZXIoKSB0aGUgY250IGFuZCBwcmVkIGNvbHVtbnMgaW50byBhIGNvbHVtbiBjYWxsZWQgdmFsdWUsIHdpdGggYSBrZXkgY2FsbGVkIHZhbHVldHlwZS4NCg0KLSBQbG90IHZhbHVlIGFzIGEgZnVuY3Rpb24gb2YgaW5zdGFudCAoZGF5KS4NCkhvdyBkb2VzIHRoZSBncmFkaWVudCBib29zdGluZyBtb2RlbCBjb21wYXJlIHRvIHRoZSBwcmV2aW91cyBtb2RlbHM/DQpgYGB7cn0NCiMgUHJpbnQgcXVhc2lwb2lzc29uX3Bsb3QNCnF1YXNpcG9pc3Nvbl9wbG90DQoNCiMgUHJpbnQgcmFuZG9tZm9yZXN0X3Bsb3QNCnJhbmRvbWZvcmVzdF9wbG90DQoNCiMgUGxvdCBwcmVkaWN0aW9ucyBhbmQgYWN0dWFsIGJpa2UgcmVudGFscyBhcyBhIGZ1bmN0aW9uIG9mIHRpbWUgKGRheXMpDQpiaWtlc0F1Z3VzdCAlPiUgDQogIG11dGF0ZShpbnN0YW50ID0gKGluc3RhbnQgLSBtaW4oaW5zdGFudCkpLzI0KSAlPiUgICMgc2V0IHN0YXJ0IHRvIDAsIGNvbnZlcnQgdW5pdCB0byBkYXlzDQogIGdhdGhlcihrZXkgPSB2YWx1ZXR5cGUsIHZhbHVlID0gdmFsdWUsIGNudCwgcHJlZCkgJT4lDQogIGZpbHRlcihpbnN0YW50IDwgMTQpICU+JSAjIGZpcnN0IHR3byB3ZWVrcw0KICBnZ3Bsb3QoYWVzKHggPSBpbnN0YW50LCB5ID0gdmFsdWUsIGNvbG9yID0gdmFsdWV0eXBlLCBsaW5ldHlwZSA9IHZhbHVldHlwZSkpICsgDQogIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX2xpbmUoKSArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoIkRheSIsIGJyZWFrcyA9IDA6MTQsIGxhYmVscyA9IDA6MTQpICsgDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKyANCiAgZ2d0aXRsZSgiUHJlZGljdGVkIEF1Z3VzdCBiaWtlIHJlbnRhbHMsIEdyYWRpZW50IEJvb3N0aW5nIG1vZGVsIikNCmBgYA0KUmVtYXJrOiBUaGUgZ3JhZGllbnQgYm9vc3RpbmcgcGF0dGVybiBjYXB0dXJlcyByZW50YWwgdmFyaWF0aW9ucyBkdWUgdG8gdGltZSBvZiBkYXkgYW5kIG90aGVyIGZhY3RvcnMgYmV0dGVyIHRoYW4gdGhlIHByZXZpb3VzIG1vZGVscy4NCg0K