Introduction

Multiple linear regression (MLR) is an extension of simple linear regression that allows for the prediction of a dependent variable using multiple independent variables. It is a fundamental statistical technique widely used in causal predictive modeling, particularly in domains such as finance and healthcare, where multiple factors influence a (numerically quantifiable) outcome.

This lesson is a primer and introduction to multiple linear regression. For more detailed coverage of regression, consult these lessons:

Overview

At the core of multiple linear regression is a linear mathematical expression that defines the relationship between the dependent variable (target feature) and multiple independent variables (predictive features). Mathematically, a regression model is expressed as the sum of the independent variables multiplied by a coefficient that is calculated from the data:

\[ Y = \beta_0 + \beta_1X_1 + \beta_2X_2 + \dots + \beta_pX_p + \epsilon \]

In this equation, \(Y\) represents the dependent variable, while \(X_1, X_2, ..., X_p\) denote the independent variables. The term \(\beta_0\) corresponds to the intercept, whereas \(\beta_1, \beta_2, ..., \beta_p\) are the regression coefficients that quantify the contribution of each predictor. The error term \(\epsilon\) accounts for variability not explained by the independent variables.

To estimate the regression coefficients, the ordinary least squares (OLS) method is employed. This technique minimizes the sum of the squared residuals (the difference between the actual and predicted value). The coefficients provide insight into the impact of each independent variable on the dependent variable.

To determine how well the model fits the data and how good a prediction is, the model’s “fit” is evaluated using various metrics, which are presented later.

Building a Regression Model

Implementing multiple linear regression begins with selecting an appropriate dataset that is suitable for regression. Regression is not a universal technique and when its assumptions are not met, an alternative approach (such as kNN regression or regression trees) should be used.

This section provides a step-by-step tutorial on building a regression model in R.

1. Load the Data

The data should be in tabular form containing the predictor features (independent variables) and the target feature (dependent variable) in columns. Most commonly, the data will be in CSV files but can be provided in other format too which would then need to be converted to a tabular format.

To illustrate the practical application of multiple linear regression, we will use a dataset (insurance.csv) that contains numerous factors influencing medical costs.

The first step involves loading and exploring the dataset in R:

df <- read.csv("sample-data/insurance.csv")
str(df)
## 'data.frame':    1338 obs. of  7 variables:
##  $ age     : int  19 18 28 33 32 31 46 37 37 60 ...
##  $ sex     : chr  "female" "male" "male" "male" ...
##  $ bmi     : num  27.9 33.8 33 22.7 28.9 ...
##  $ children: int  0 1 3 0 0 0 1 3 2 0 ...
##  $ smoker  : chr  "yes" "no" "no" "no" ...
##  $ region  : chr  "southwest" "southeast" "southeast" "northwest" ...
##  $ charges : num  16885 1726 4449 21984 3867 ...

The target feature is charges, the other features are the predictors: age, sex, bmi, children, smoker, region. The goal is to develop of regression equation that would allow us to predict the medical costs incurred by an insured (charges).

We have three numeric features (age, bmi, children), two binary features (sex and smoker) and one categorical feature (region). Each requires different treatment and preparation.

Preparing the data is an essential preliminary step and involves handling missing values, encoding categorical variables, evaluating distribution of the features, identifying outliers, and ensuring proper feature scaling where necessary.

2. Manage Outliers

The first step is to identify any outliers in the numeric features. Generally, values that are more than three standard deviations from the mean on either size (i.e., having a z-score > 3) are considered outliers and those observations should either be removed or the values should be treated as missing values and imputed.

Lesson 3.203 – Detecting and Managing Outliers provides a more detailed treatment of this subject.

## calculate mean and sd for each numeric column
numeric.cols <- sapply(df, is.numeric)

for (c in 1:length(numeric.cols)) {
  if (numeric.cols[c] == TRUE) {
    ## find outliers in numeric column
    m <- mean(df[,c], na.rm = T)
    s <- sd(df[,c], na.rm = T)
    
    outliers <- which(abs((m - df[,c]) / s) > 3.0)
    
    if (length(outliers) > 0) {
      ## found outliers; replace with NA and impute later
      cat("Found outliers in column '", names(df)[c], "': \n")
      cat("   --> ", df[outliers,c], "\n\n")
    }
  }
}
## Found outliers in column ' bmi ': 
##    -->  360.05 
## 
## Found outliers in column ' children ': 
##    -->  5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 
## 
## Found outliers in column ' charges ': 
##    -->  51194.56 63770.43 58571.07 55135.4 52590.83 60021.4 62592.87

We need to be careful to ignore missing values in the calculation of the mean and standard deviation as those have not yet been dealt with.

So, three columns contain outlier values. While having 5 children might be considered an outlier in this dataset, it is by no means an unusual value. Therefore, we will ignore those “outliers”. Likewise for the high charges might be outliers but could be justified depending on the medical procedures that were performed. Again, it is best to ignore those in this situation.

On the other hand, the extremely high value for BMI is indeed very likely incorrect and is probably a data entry error; perhaps a slipped decimal when someone wanted to enter 36.005 instead of 360.05. Now, we could replace this value with NA and then treat it as an actual missing value or apply a correction that makes sense from a domain perspective. We will choose the latter in this situation.

df$bmi[which(df$bmi > 360)] <- 36.005

The treatment of outliers is situational and there is not always a clear course of action. Each method to manage outliers must be viewed in the context of the business and the goals of the analysis and regression modeling.

3. Impute Missing Values

Missing values must be addressed either by eliminating any observation (row) that has a missing value in any of its features or by imputing the missing value with an estimate. For numeric features, a common strategy is to impute the missing value with the mean or median, while for categorical features, the mode is often used.

Lesson 3.204 – Managing Missing Values in Data provides a more detailed treatment of this subject.

The first step is to check each feature for missing values. While we can certainly check each feature manually, iterating over all columns is more practical. The code below uses a simple loop, but other approaches can be used as well, such as using sapply().

For missing numeric values we can check for NA with the function is.na(). However, that will not reveal missing values in a “character” column, such as smoker or region. For this we need to check whether the text is ““.

num.Rows <- nrow(df)
num.Cols <- ncol(df)

found <- F

for (c in 1:num.Cols){
  missing.Values <- which(is.na(df[,c]) | df[,c] == "")
  num.Missing.Values <- length(missing.Values)
  if (num.Missing.Values > 0) {
    print(paste0("Column '", names(df)[c], "' has ", num.Missing.Values, " missing values"))
    found <- T
  }
}
## [1] "Column 'bmi' has 3 missing values"
## [1] "Column 'smoker' has 2 missing values"
if (!found) {
  print("no missing values detected")
}

The numeric column bmi has three missing values, while the categorical column smoker has two missing values.

The simplest approach for numeric features is to replace the missing values with the mean of the column since bmi is a numeric feature. However, the mean can be skewed by outliers so the median might often be better. Now, in this data, there is likely a significant difference between the BMI of male vs female insured, so using the median of the appropriate sex might be more reasonable. In fact, we could use a t-test to test whether that difference is statistically significant.

Let’s replace the missing values for bmi with the median of the respective sex, of course, ignoring any missing values in the calculation of the median.

median.Male <- median(df$bmi[which(df$sex == "male")], na.rm = T)
median.Female <- median(df$bmi[which(df$sex == "female")], na.rm = T)

missing.Values <- which(is.na(df$bmi))

df$bmi[missing.Values] <- ifelse(df$sex[missing.Values] == "female", 
                                 median.Female, 
                                 median.Male)

Rather than using ifelse we could have also used a loop. Using ifelse on a vector means that we are using a vector operation. It is more efficient than a loop, albeit a bit harder to understand. The code also assume only two values for sex: “male” and “female”, and no missing values for the column sex.

We will use a similar approach for the missing values in the column smoker by replacing the value with the mode for each sex. One complication is that R does not have a “mode” function1, so we need to do the work ourselves. The table() function counts the number of occurrences for each type of smoker.

num.Male.Smokers <- table(df[which(df$sex == "male"), "smoker"])
mode.Male.Smokers <- names(num.Male.Smokers)[which.max(num.Male.Smokers)]

num.Female.Smokers <- table(df[which(df$sex == "female"), "smoker"])
mode.Female.Smokers <- names(num.Female.Smokers)[which.max(num.Female.Smokers)]

missing.Values.Female <- which(df$smoker == "" & df$sex == "female")
missing.Values.Male <- which(df$smoker == "" & df$sex == "male")

if (length(missing.Values.Female) > 0) {
  df$smoker[missing.Values.Female] <- mode.Female.Smokers
}

if (length(missing.Values.Male) > 0) {
  df$smoker[missing.Values.Male] <- mode.Male.Smokers
}

At this point in the data preparation phase, we have dealt with both outliers and missing values. Now, we need to check the suitability of the features for regression.

4. Resolve Multicollinearity

Multicollinearity refers to a situation in multiple linear regression where two or more independent variables exhibit a high degree of correlation. When multicollinearity is present, the regression model encounters difficulty in estimating the individual contribution of each predictor to the dependent variable because the predictors convey redundant or overlapping information; in other words, features are “double counted”.

The presence of multicollinearity does not affect the predictive capability of the regression model but severely undermines the interpretability of the coefficients. When independent variables are highly correlated, small changes in the dataset can result in vastly different coefficient estimates. This instability complicates decision-making, particularly in applications such as finance and healthcare, where precise variable impact assessments are crucial.

The are a number of ways to check for multicollinearity, but a correlation matrix is a common tool. Examining the correlation matrix of independent variables provides an initial indication of multicollinearity. If two or more numeric features exhibit correlations above 0.8 or so, multicollinearity may be an issue.

## find numeric columns only
numeric.cols <- sapply(df, is.numeric)

## create correlation matrix
cor(df[,numeric.cols])
##                age        bmi   children    charges
## age      1.0000000 0.10866959 0.04246900 0.29900819
## bmi      0.1086696 1.00000000 0.01102236 0.19813004
## children 0.0424690 0.01102236 1.00000000 0.06799823
## charges  0.2990082 0.19813004 0.06799823 1.00000000

For our dataset, there is no multicollinearity. However, if we had found correlations above some threshold (generally, about 0.8), then various strategies can be employed to mitigate its impact. One approach is to exclude one of the two variables in the regression modeling. This decision should be guided by domain knowledge and feature importance analysis. Instead of removing a variable, highly correlated predictors can be combined into a single feature, such as creating an index or an average score. This approach retains the predictive information while reducing redundancy. More advanced approaches are to use Principal Component Analysis (PCA) to transform them into a set of uncorrelated principal components or to use ridge regression instead. Scaling all features to a common scale can also be helpful.

6. Scale Features

In cases where multicollinearity arises due to the scaling of variables, centering (subtracting the mean) or standardizing (scaling to unit variance) may reduce its impact. This, of course, only applies to numeric features.

A drawback of scaling is that it changes the interpretability of the coefficients.

7. Encode Categorical Features

So far, we have primarily dealt with the numeric features. Let’s turn our attention to the categorical features. Binary categorical features (where there are only two possible values) should be turned into a 0/1 encoding.

In our dataset, we have two binary categorical features: smoker and sex. Naturally, in other domains, those features may be multi-level categorical rather than binary categorical.

## encode sex: male as 0 and female as 1
df$sex <- ifelse(df$sex == "male", 0, 1)

## encode smoker: 1 = yes and 0 = no
df$smoker <- ifelse(df$smoker == "yes", 1, 0)

The choice of 0 and 1 is arbitrary; we could have just as easily made “yes” for smoker 0 instead of 1.

The feature region is a multi-level categorical feature. There are several approaches to encoding categorical features as numeric values. We will only discuss one method: one-hot encoding. While it is simple and has high interpretability, it does not work well when there are more than about four levels.

Let’s start by finding out all of the different values for region, i.e., the levels.

table(df$region)
## 
## northeast northwest southeast southwest 
##       324       325       364       325

Before we get into details on how to perform one-hot encoding in R, let’s first explain the method.In one-hot encoding, we add additional binary columns (0/1) to the data. One column less than what we have levels. So, in our dataset, we have four levels for region: northeast, northwest, southeast, southwest. This is a partial set of columns and rows of the original arrangement:

head(df[1:5,c(1,2,6)])
##   age sex    region
## 1  19   1 southwest
## 2  18   0 southeast
## 3  28   0 southeast
## 4  33   0 northwest
## 5  32   0 northwest

We now add three additional columns: northeast, northwest, southeast and encode the values as follows:

region northeast northwest southeast
northeast 1 0 0
northwest 0 1 0
southeast 0 0 1
southwest 0 0 0

Each unique category becomes a new column except one of them, and only one column has a 1 per row. The category left out has all 0 in the row

In R, we can either use a loop or the apply() functions to make the assignments, or use functions from a package. Note that the lm function for building a regression model will do one-hot encoding automatically if the categorical column is a factor variable. We will show how to do one-hot encoding ourselves, so we have full control and flexibility.

n <- nrow(df)

## add new columns
df$northeast <- 0
df$northwest <- 0
df$southeast <- 0

for (i in 1:n) {
  if (df$region[i] == "northeast")
    df$northeast[i] <- 1
  else if (df$region[i] == "northwest")
    df$northwest[i] <- 1
  else if (df$region[i] == "southeast")
    df$southeast[i] <- 1
}

Here’s our updated dataset with the one-hot encoded categorical feature:

head(df[1:5,c(1,2,6,8,9,10)])
##   age sex    region northeast northwest southeast
## 1  19   1 southwest         0         0         0
## 2  18   0 southeast         0         0         1
## 3  28   0 southeast         0         0         1
## 4  33   0 northwest         0         1         0
## 5  32   0 northwest         0         1         0

Once we have done the encoding, we need to either remove the categorical variable from the dataframe or be sure to exclude it when building the regression model.

One-hot, or dummy, encoding is a common method used in machine learning and data preprocessing to convert categorical variables into a numerical format that can be used by algorithms. It represents each category as a binary vector, where each category gets its own column and is marked as 1 (present) or 0 (absent). Lesson 3.207 - Encoding Categorical Features provides a more detailed treatment of this subject.

Alternatives to One-Hot Encoding are Label Encoding where we assign numbers to categories (e.g., "North" → 1, "South" → 2), but this implies an ordinal relationship, which may be incorrect, Target Encoding which replaces categories with their mean target value (used in predictive modeling), or **Frequency Encoding” where each categories with their frequency of occurrence.

CAUTION: Some datasets may use numeric values to encode categorical features but that does not make the feature numeric. For example, the regions could have been defined as 1 for “northeast”, 2 for “northwest”, etc. That does not make them numeric as it would make no sense to talk about an “average region” or the standard deviation of the regions, or the arithmetic difference between northeast and southwest.

8. Select Features

There is no strict rule on how many features are too many in regression, as it depends on the size of the dataset and the nature of the problem. However, some general guidelines help determine whether the number of features is excessive:

  • Rule of Thumb: The Sample Size-to-Feature Ratio: A common heuristic suggests that the number of observations should be at least 10 times the number of features for reliable regression estimates. If the number of predictors approaches or exceeds the number of observations, the model is likely overfitted.

  • Adjusted \(R^2\) and Model Complexity: Unlike \(R^2\), which always increases as more predictors are added, adjusted \(R^2\) accounts for the number of features. If adjusted \(R^2\) starts to decline as more variables are introduced, it suggests that additional features do not contribute useful information.

  • Feature Importance Analysis: If many predictors have little to no effect on the dependent variable, their inclusion only adds noise. Identifying and removing irrelevant features improves model performance.

To mitigate the problems associated with too many features, various feature selection techniques can be applied to retain only the most informative predictors.

Filter methods evaluate individual feature relevance using statistical criteria before fitting the regression model:

  • Correlation Analysis: Identifies features that are highly correlated with each other and can be removed to avoid redundancy.
  • Variance Thresholding: Features with very low variance provide little discriminatory power and can be removed.
  • Stepwise Regression: A common method that iteratively adds or removes features based on model performance.
  • Forward Selection starts with no variables and adds predictors one at a time based on significance.
  • Backward Elimination starts with all variables and removes the least significant predictors iteratively.
  • Stepwise Selection is a combination of forward and backward selection, balancing feature inclusion and removal.

In addition, methods such as PCA and Ridge Regression are useful. All of these are beyond the scope of this introductory lesson.

9. Build Regression Model

After ensuring the data is clean and properly formatted, the regression model is built using the lm() function. Notice the “formula”: the dependent variables is on the left side, followed by a ~ and then a linear additive combination of the independent feature variables. The feature variables are the names of the numeric columns in the dataframe referenced with the data parameter. Feature selection guides which variables are included in the regression model.

The R code below demonstrates the construction of a regression model.

model <- lm(charges ~ age + sex + bmi + children + smoker + northeast + northwest + southeast, 
            data = df)

## print summary of the model for investigation
summary(model)
## 
## Call:
## lm(formula = charges ~ age + sex + bmi + children + smoker + 
##     northeast + northwest + southeast, data = df)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -11276.3  -2846.9   -980.3   1409.4  30004.6 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -13106.23    1033.96 -12.676  < 2e-16 ***
## age            256.85      11.89  21.599  < 2e-16 ***
## sex            135.42     332.77   0.407 0.684111    
## bmi            341.38      28.61  11.931  < 2e-16 ***
## children       478.27     137.72   3.473 0.000532 ***
## smoker       23854.58     412.92  57.770  < 2e-16 ***
## northeast      959.71     477.64   2.009 0.044711 *  
## northwest      613.80     476.96   1.287 0.198352    
## southeast      -82.32     470.40  -0.175 0.861113    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 6059 on 1329 degrees of freedom
## Multiple R-squared:  0.7512, Adjusted R-squared:  0.7497 
## F-statistic: 501.6 on 8 and 1329 DF,  p-value: < 2.2e-16

Interpreting the output of this regression model involves examining the estimated coefficients, assessing their statistical significance, and evaluating confidence intervals. Model diagnostics are also necessary to identify potential violations of regression assumptions.

Backward Feature Elimination

Any coefficients that are not statistically significant, i.e., have a p-value of more than 0.05, should be eliminated. The tutorial below explains this approach of backward elimination based on p-values. This method iteratively removes the least significant predictor variable, i.e., the variable with the highest p-value, until all remaining variables are statistically significant.

Step-by-Step Explanation

  1. Fit the Full Model: Start with all predictor variables in the model.
  2. Identify the Least Significant Predictor: Find the predictor variable with the highest p-value
  3. Remove the Least Significant Predictor: If the highest p-value is above a certain threshold (commonly 0.05), remove the corresponding predictor variable.
  4. Refit the Model: Fit the model again without the removed predictor.
  5. Repeat: Continue the process until all remaining predictor variables have p-values below the threshold.

A few tips to keep in mind:

  • ignore the p-value of the intercept
  • either include all or none of the columns that encode a categorical variables using dummy codes, even if some have a p-value greater than 0.05

9. Check Normality of Residuals

While it is not strictly necessary for numeric features (independent variables) to be normally distributed when building a regression model, there are nevertheless some important statistical considerations. Specifically, regression models estimate relationships between variables, and these relationships can be valid even when the predictors are not fully normally distributed. However, if predictors are highly skewed or have outliers, transformations (e.g., log, square root) may improve model performance.

What does matter is the distribution of residuals (errors) rather than the independent variables. In ordinary least squares (OLS) regression, an assumption is that residuals (differences between observed and predicted values) should be normally distributed for valid hypothesis testing (e.g., t-tests, confidence intervals, p-values). If residuals are non-normally distributed, it may indicate heteroscedasticity (non-constant variance), omitted variables, or model misspecification.

To check if residuals are normally distributed, you can use the following strategies for a partial model that simplifies the explanation:

# Fit linear regression model
model <- lm(charges ~ age + bmi + children + smoker, 
            data = df)

A histogram of the residuals should look some “normal”, i.e., follow a Gaussian Distribution or Bell Curve.

# Check residual normality with a histogram
hist(resid(model), main = "Histogram of Residuals", col = "lightblue")

There is some skewness to this distribution and transforming the target feature using a log transform might be appropriate.

An alternative is a Q-Q Plot which should be a line at a 45° angle. If it is more like a “hockey stick” then the distribution is not normal.

# Q-Q plot for normality check
qqnorm(resid(model))
qqline(resid(model), col = "red")

An alternative to a visual inspection of the distribution of the residuals is the Shapiro-Wilk statistical test of normality. The two key values in the result are the W-statistic (Test Statistic) and the p-value. A value for W close to 1 suggests normality, while a value far from 1 suggests deviation from normality. If the p-value is greater than 0.05 then we fail to reject the null hypothesis, which means that the data is normally distributed. Likewise, a p-value less than 0.05 implies that we must reject the null hypothesis and consequently the data is not normally distributed.

# Shapiro-Wilk test for normality
shapiro.test(resid(model))
## 
##  Shapiro-Wilk normality test
## 
## data:  resid(model)
## W = 0.90025, p-value < 2.2e-16

In the above result, the distribution of the residuals is not normally distributed if we base our determination on the p value alone. However, the plots are somewhat reasonable and the W statistic indicates normality, so while not perfect, we can accept the regression model.

9. Express Regression Equation

The regression equation is derived using the estimated coefficients from the model. The general form of a multiple linear regression equation is:

\[ \hat{Y} = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \beta_3 X_3 \]

Assume we have constructed a regression model and used summary() to obtain the following coefficients.

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept) -12169.64     942.84 -12.907  < 2e-16 ***
age            257.85      11.89  21.688  < 2e-16 ***
bmi            323.78      27.39  11.823  < 2e-16 ***
children       476.23     137.72   3.458 0.000561 ***
smoker       23816.00     411.00  57.947  < 2e-16 ***

We can then write an equation which becomes out deployable model (with some rounding for display clarity and writing the intercept at the end rather than the beginning):

\[ \hat{\text{charges}} = (258 \times \text{age}) + (324 \times \text{bmi}) + (476 \times \text{children}) + (23816 \times \text{smoker}) - 12170) \]

This equation predicts the expected medical charges based on age, BMI, number of children, and whether the person is a smoker or not. It is a simple equation that can be added to web applications, spreadsheet models, or calculated by hand. Making a prediction is extremely fast as it just involves simple addition and multiplication.

Note how easily the model can be interpreted. For example, being a smoker (1) adds an additional $23,816 to the medical charges, while each child accounts for an additional $476. Each point that the BMI goes up, increases medical expenses by $324.

10. Make Prediction

Once the linear model (model) has been trained using the lm() function, predictions can be made using the predict() function in R or simply using the regression equation.

To predict charges for a patient with specific values of age, bmi, children, and smoker, we create a new data frame containing the input values and use the predict() function.

# Define the new observation as a data frame
new.data <- data.frame(
  age = 47,
  bmi = 31,
  children = 2,
  smoker = 0
)

# use the regression model to predict medical expenses
prediction <- predict(model, new.data)

The predicted medical charges or expenses are $1.0939^{4}.

If you want to predict for multiple companies at once, create a data frame containing multiple rows.

To obtain a confidence interval around the prediction, specify interval = "confidence".

predicted_confidence <- predict(model, new.data, interval = "confidence")
print(predicted_confidence)
##        fit      lwr      upr
## 1 10938.94 10468.93 11408.95

This returns a lower bound, predicted value, and upper bound for each prediction.

Prediction intervals provide a range in which future observations are expected to fall.

predicted_prediction <- predict(model, new.data, interval = "prediction")
print(predicted_prediction)
##        fit       lwr      upr
## 1 10938.94 -967.4271 22845.31

This is wider than the confidence interval because it accounts for individual variance in future observations. If the goal is forecasting or making decisions based on individual outcomes, the prediction interval is more useful because it provides a range of likely values for a new observation. If the goal is to understand how well the model estimates the mean, the confidence interval is more relevant.

11. Evaluate Regression Model

Evaluating the performance of a regression model is essential to determine how well it fits the data and how reliable its predictions are. The “goodness” of a regression model is typically assessed using statistical measures, diagnostic plots, and residual analysis. The choice of evaluation metrics depends on the objective — whether the goal is to maximize explanatory power, assess predictive accuracy, or detect violations of assumptions.

The most two common evaluation metrics are the \(R^2\) statistic and mean squared error.

Coefficient of Determination

The \(R^2\) statistic, also known as the coefficient of determination, measures the proportion of variance in the dependent variable that is explained by the independent variables and is mathematically defined as:

\[ R^2 = 1 - \frac{\sum (Y_i - \hat{Y}_i)^2}{\sum (Y_i - \bar{Y})^2} \]

\(R^2\) ranges from 0 to 1, where a value closer to 1 indicates that the model explains most of the variability in the response variable. However, a high \(R^2\) does not necessarily mean the model is good; it does not account for overfitting.

In R, we can find the \(R^2\) and the Adjusted \(R^2\) as follows:

model <- lm(charges ~ age + bmi + sex + smoker, data = df)

r.squared <- summary(model)$r.squared

The \(R^2\) for the above regression model is 0.748. An \(R^2\) above 0.7 is generally reasonable, while above 0.8 is good, and above 0.9 is excellent. The model above has a reasonable \(R^2\) that explains 70% of the variance observed.

Unlike \(R^2\), the adjusted \(R^2\) accounts for the number of predictors in the model and penalizes unnecessary variables.

\[ R^2_{adj} = 1 - \left(\frac{(1 - R^2)(n-1)}{n-k-1}\right) \]

where \(n\) is the number of observations and \(k\) is the number of predictors. Adjusted \(R^2\) prevents the illusion of improvement when adding irrelevant predictors. If adding new predictors causes Adjusted \(R^2\) to decrease, those predictors do not contribute significantly to the model. The Adjusted \(R^2\) can be read using summary(model)$adj.r.squared.

Mean Squared Error (MSE)

The mean squared error measures the average squared difference between the actual and predicted values.

\[ MSE = \frac{1}{n} \sum (Y_i - \hat{Y}_i)^2 \]

A smaller MSE indicates a better model fit. It penalizes large errors more than small ones due to squaring. We can calculate MSE in R as follows:

model <- lm(charges ~ age + bmi + sex + smoker, data = df)

mse <- mean((df$charges - predict(model))^2)
print(mse)
## [1] 36966773

Root mean squared error (RMSE) is simply the square root of MSE, making it interpretable in the same units as the dependent variable.

\[ RMSE = \sqrt{MSE} \]

As before, a lower RMSE values indicate a better fit. Unlike MSE, RMSE is directly interpretable in terms of the original response variable.

rmse <- sqrt(mean((df$charges - predict(model))^2))
print(rmse)
## [1] 6080.031

In addition, to the two metrics shown here, other metrics include Mean Absolute Deviation (MAD) and Mean Absolute Percentage Error (MAPE).

12. Deploy Model

A regression model is one of the simplest models to deploy and use to make predictions as one simply has to provide the regression equation.

Regression Assumptions

While regression models (such as linear and multiple linear regression) are widely used for predicting numeric target variables, there are scenarios where they may not be the best choice. Regression assumes a linear relationship between predictors and the target variable, but real-world data often violate these assumptions. Below are key situations where regression may not be appropriate, along with alternative machine learning models better suited for each case.

1. When the Relationship Between Predictors and the Target Variable is Non-Linear

Multiple linear regression assumes that the dependent variable is a linear function of the independent variables. If the true relationship is highly non-linear, adding polynomial terms can improve fit, but it often leads to overfitting or poor generalization.

2. When There is Multicollinearity Among Independent Variables

Multicollinearity occurs when predictor variables are highly correlated with each other, making coefficient estimates unstable. Regression struggles because it cannot distinguish between the effects of correlated variables.

3. When the Data Contains Many Outliers

Ordinary least squares (OLS) regression minimizes squared errors, making it highly sensitive to outliers. A few extreme values can disproportionately influence the model, distorting predictions.

4. When the Data is Highly Skewed or Has Heteroscedasticity

If residual variance is not constant (heteroscedasticity), regression coefficients may be inefficient, leading to incorrect inferences. Skewed distributions cause biased coefficient estimates. Log Transformation or Box-Cox Transformation should be used before regression to stabilize variance.

5. When There are High-Dimensional or Sparse Features

If there are more predictors than observations (e.g., genetic data, text data), traditional regression overfits.Many features may be irrelevant, diluting the predictive power of relevant ones.

6. When There are Interactions or Non-Additive Effects Between Predictors

Traditional regression assumes independent effects of predictors unless interaction terms are explicitly added. Complex interactions can be difficult to specify manually.

7. When the Dataset is Large and Regression Becomes Computationally Inefficient

Linear regression performs matrix operations that become computationally expensive in very large datasets. Complex relationships may not be effectively captured with a simple linear function.

8. When the Data is Temporal (Time Series) and Regression Ignores Sequential Dependency

Standard regression does not account for time-based patterns such as trends, seasonality, or autocorrelation. Residuals in regression models may be autocorrelated, violating assumptions.

Summary

Multiple linear regression remains a fundamental technique for predictive modeling across various domains, including finance and healthcare. Through its ability to quantify relationships between multiple predictors and an outcome, it provides valuable insights for decision-making and forecasting. The practical examples presented in this tutorial illustrate how MLR can be applied to real-world problems, emphasizing the importance of model evaluation and diagnostics.

Regression models work well when relationships are linear, independent, homoscedastic, and free from severe multicollinearity or outliers. However, when these assumptions do not hold, alternative machine learning algorithms provide better predictive performance and robustness.


Files & Resources

All Files for Lesson 3.440

References

No references.

Errata

Let us know.


  1. there is a function mode but it returns the type of an object↩︎

LS0tCnRpdGxlOiAiTXVsdGlwbGUgUmVncmVzc2lvbjogQSBQcmltZXIiCnBhcmFtczoKICBjYXRlZ29yeTogMwogIHN0YWNrczogMAogIG51bWJlcjogNDQwCiAgdGltZTogNzUKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiByZWdyZXNzaW9uLG9scyxtYWNoaW5lIGxlYXJuaW5nCiAgZGVzY3JpcHRpb246ICJJbnRyb2R1Y2VzIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uLiBTaG93cwogICAgICAgICAgICAgICAgaG93IHRvIGJ1aWxkIGFuZCBldmFsdWF0ZSBhIHJlZ3Jlc3Npb24gbW9kZWwgaW4gUi4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKTXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gKE1MUikgaXMgYW4gZXh0ZW5zaW9uIG9mIHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiB0aGF0IGFsbG93cyBmb3IgdGhlIHByZWRpY3Rpb24gb2YgYSBkZXBlbmRlbnQgdmFyaWFibGUgdXNpbmcgbXVsdGlwbGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiBJdCBpcyBhIGZ1bmRhbWVudGFsIHN0YXRpc3RpY2FsIHRlY2huaXF1ZSB3aWRlbHkgdXNlZCBpbiBjYXVzYWwgcHJlZGljdGl2ZSBtb2RlbGluZywgcGFydGljdWxhcmx5IGluIGRvbWFpbnMgc3VjaCBhcyBmaW5hbmNlIGFuZCBoZWFsdGhjYXJlLCB3aGVyZSBtdWx0aXBsZSBmYWN0b3JzIGluZmx1ZW5jZSBhIChudW1lcmljYWxseSBxdWFudGlmaWFibGUpIG91dGNvbWUuCgpUaGlzIGxlc3NvbiBpcyBhIHByaW1lciBhbmQgaW50cm9kdWN0aW9uIHRvIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uLiBGb3IgbW9yZSBkZXRhaWxlZCBjb3ZlcmFnZSBvZiByZWdyZXNzaW9uLCBjb25zdWx0IHRoZXNlIGxlc3NvbnM6CgotICAgWzMuNDQxIC0tIE9yZGluYXJ5IExlYXN0IFNxdWFyZXMgUmVncmVzc2lvbl0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wMy5tbC9sLTMtNDQxLW9scy1yZWdyZXNzaW9uL2wtMy00NDEuaHRtbCkKLSAgIFszLjQ0MiAtLSBSaWRnZSBhbmQgTGFzc28gUmVncmVzc2lvbl0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wMy5tbC9sLTMtNDQyLXJpZGdlLWxhc3NvLXJlZ3Jlc3Npb24vbC0zLTQ0Mi5odG1sKQoKIyMgT3ZlcnZpZXcKCkF0IHRoZSBjb3JlIG9mIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIGlzIGEgbGluZWFyIG1hdGhlbWF0aWNhbCBleHByZXNzaW9uIHRoYXQgZGVmaW5lcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGRlcGVuZGVudCB2YXJpYWJsZSAodGFyZ2V0IGZlYXR1cmUpIGFuZCBtdWx0aXBsZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgKHByZWRpY3RpdmUgZmVhdHVyZXMpLiBNYXRoZW1hdGljYWxseSwgYSByZWdyZXNzaW9uIG1vZGVsIGlzIGV4cHJlc3NlZCBhcyB0aGUgc3VtIG9mIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgbXVsdGlwbGllZCBieSBhIGNvZWZmaWNpZW50IHRoYXQgaXMgY2FsY3VsYXRlZCBmcm9tIHRoZSBkYXRhOgoKJCQKWSA9IFxiZXRhXzAgKyBcYmV0YV8xWF8xICsgXGJldGFfMlhfMiArIFxkb3RzICsgXGJldGFfcFhfcCArIFxlcHNpbG9uCiQkCgpJbiB0aGlzIGVxdWF0aW9uLCAkWSQgcmVwcmVzZW50cyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLCB3aGlsZSAkWF8xLCBYXzIsIC4uLiwgWF9wJCBkZW5vdGUgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcy4gVGhlIHRlcm0gJFxiZXRhXzAkIGNvcnJlc3BvbmRzIHRvIHRoZSBpbnRlcmNlcHQsIHdoZXJlYXMgJFxiZXRhXzEsIFxiZXRhXzIsIC4uLiwgXGJldGFfcCQgYXJlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyB0aGF0IHF1YW50aWZ5IHRoZSBjb250cmlidXRpb24gb2YgZWFjaCBwcmVkaWN0b3IuIFRoZSBlcnJvciB0ZXJtICRcZXBzaWxvbiQgYWNjb3VudHMgZm9yIHZhcmlhYmlsaXR5IG5vdCBleHBsYWluZWQgYnkgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcy4KClRvIGVzdGltYXRlIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cywgdGhlIG9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgKE9MUykgbWV0aG9kIGlzIGVtcGxveWVkLiBUaGlzIHRlY2huaXF1ZSBtaW5pbWl6ZXMgdGhlIHN1bSBvZiB0aGUgc3F1YXJlZCByZXNpZHVhbHMgKHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGFjdHVhbCBhbmQgcHJlZGljdGVkIHZhbHVlKS4gVGhlIGNvZWZmaWNpZW50cyBwcm92aWRlIGluc2lnaHQgaW50byB0aGUgaW1wYWN0IG9mIGVhY2ggaW5kZXBlbmRlbnQgdmFyaWFibGUgb24gdGhlIGRlcGVuZGVudCB2YXJpYWJsZS4KClRvIGRldGVybWluZSBob3cgd2VsbCB0aGUgbW9kZWwgZml0cyB0aGUgZGF0YSBhbmQgaG93IGdvb2QgYSBwcmVkaWN0aW9uIGlzLCB0aGUgbW9kZWwncyAiZml0IiBpcyBldmFsdWF0ZWQgdXNpbmcgdmFyaW91cyBtZXRyaWNzLCB3aGljaCBhcmUgcHJlc2VudGVkIGxhdGVyLgoKIyMgQnVpbGRpbmcgYSBSZWdyZXNzaW9uIE1vZGVsCgpJbXBsZW1lbnRpbmcgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gYmVnaW5zIHdpdGggc2VsZWN0aW5nIGFuIGFwcHJvcHJpYXRlIGRhdGFzZXQgdGhhdCBpcyBzdWl0YWJsZSBmb3IgcmVncmVzc2lvbi4gUmVncmVzc2lvbiBpcyBub3QgYSB1bml2ZXJzYWwgdGVjaG5pcXVlIGFuZCB3aGVuIGl0cyBhc3N1bXB0aW9ucyBhcmUgbm90IG1ldCwgYW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggKHN1Y2ggYXMga05OIHJlZ3Jlc3Npb24gb3IgcmVncmVzc2lvbiB0cmVlcykgc2hvdWxkIGJlIHVzZWQuCgpUaGlzIHNlY3Rpb24gcHJvdmlkZXMgYSBzdGVwLWJ5LXN0ZXAgdHV0b3JpYWwgb24gYnVpbGRpbmcgYSByZWdyZXNzaW9uIG1vZGVsIGluIFIuCgojIyMgMS4gTG9hZCB0aGUgRGF0YQoKVGhlIGRhdGEgc2hvdWxkIGJlIGluIHRhYnVsYXIgZm9ybSBjb250YWluaW5nIHRoZSBwcmVkaWN0b3IgZmVhdHVyZXMgKGluZGVwZW5kZW50IHZhcmlhYmxlcykgYW5kIHRoZSB0YXJnZXQgZmVhdHVyZSAoZGVwZW5kZW50IHZhcmlhYmxlKSBpbiBjb2x1bW5zLiBNb3N0IGNvbW1vbmx5LCB0aGUgZGF0YSB3aWxsIGJlIGluIENTViBmaWxlcyBidXQgY2FuIGJlIHByb3ZpZGVkIGluIG90aGVyIGZvcm1hdCB0b28gd2hpY2ggd291bGQgdGhlbiBuZWVkIHRvIGJlIGNvbnZlcnRlZCB0byBhIHRhYnVsYXIgZm9ybWF0LgoKVG8gaWxsdXN0cmF0ZSB0aGUgcHJhY3RpY2FsIGFwcGxpY2F0aW9uIG9mIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uLCB3ZSB3aWxsIHVzZSBhIGRhdGFzZXQgKFtpbnN1cmFuY2UuY3N2XShzYW1wbGUtZGF0YS9pbnN1cmFuY2UuY3N2KSkgdGhhdCBjb250YWlucyBudW1lcm91cyBmYWN0b3JzIGluZmx1ZW5jaW5nIG1lZGljYWwgY29zdHMuCgpUaGUgZmlyc3Qgc3RlcCBpbnZvbHZlcyBsb2FkaW5nIGFuZCBleHBsb3JpbmcgdGhlIGRhdGFzZXQgaW4gUjoKCmBgYHtyfQpkZiA8LSByZWFkLmNzdigic2FtcGxlLWRhdGEvaW5zdXJhbmNlLmNzdiIpCnN0cihkZikKYGBgCgpUaGUgdGFyZ2V0IGZlYXR1cmUgaXMgKmNoYXJnZXMqLCB0aGUgb3RoZXIgZmVhdHVyZXMgYXJlIHRoZSBwcmVkaWN0b3JzOiAqYWdlKiwgKnNleCosICpibWkqLCAqY2hpbGRyZW4qLCAqc21va2VyKiwgKnJlZ2lvbiouIFRoZSBnb2FsIGlzIHRvIGRldmVsb3Agb2YgcmVncmVzc2lvbiBlcXVhdGlvbiB0aGF0IHdvdWxkIGFsbG93IHVzIHRvIHByZWRpY3QgdGhlIG1lZGljYWwgY29zdHMgaW5jdXJyZWQgYnkgYW4gaW5zdXJlZCAoKmNoYXJnZXMqKS4KCldlIGhhdmUgdGhyZWUgbnVtZXJpYyBmZWF0dXJlcyAoKmFnZSosICpibWkqLCAqY2hpbGRyZW4qKSwgdHdvIGJpbmFyeSBmZWF0dXJlcyAoKnNleCogYW5kICpzbW9rZXIqKSBhbmQgb25lIGNhdGVnb3JpY2FsIGZlYXR1cmUgKCpyZWdpb24qKS4gRWFjaCByZXF1aXJlcyBkaWZmZXJlbnQgdHJlYXRtZW50IGFuZCBwcmVwYXJhdGlvbi4KClByZXBhcmluZyB0aGUgZGF0YSBpcyBhbiBlc3NlbnRpYWwgcHJlbGltaW5hcnkgc3RlcCBhbmQgaW52b2x2ZXMgaGFuZGxpbmcgbWlzc2luZyB2YWx1ZXMsIGVuY29kaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcywgZXZhbHVhdGluZyBkaXN0cmlidXRpb24gb2YgdGhlIGZlYXR1cmVzLCBpZGVudGlmeWluZyBvdXRsaWVycywgYW5kIGVuc3VyaW5nIHByb3BlciBmZWF0dXJlIHNjYWxpbmcgd2hlcmUgbmVjZXNzYXJ5LgoKIyMjIDIuIE1hbmFnZSBPdXRsaWVycwoKVGhlIGZpcnN0IHN0ZXAgaXMgdG8gaWRlbnRpZnkgYW55IG91dGxpZXJzIGluIHRoZSBudW1lcmljIGZlYXR1cmVzLiBHZW5lcmFsbHksIHZhbHVlcyB0aGF0IGFyZSBtb3JlIHRoYW4gdGhyZWUgc3RhbmRhcmQgZGV2aWF0aW9ucyBmcm9tIHRoZSBtZWFuIG9uIGVpdGhlciBzaXplICgqaS5lLiosIGhhdmluZyBhICp6Ki1zY29yZSBcPiAzKSBhcmUgY29uc2lkZXJlZCBvdXRsaWVycyBhbmQgdGhvc2Ugb2JzZXJ2YXRpb25zIHNob3VsZCBlaXRoZXIgYmUgcmVtb3ZlZCBvciB0aGUgdmFsdWVzIHNob3VsZCBiZSB0cmVhdGVkIGFzIG1pc3NpbmcgdmFsdWVzIGFuZCBpbXB1dGVkLgoKTGVzc29uIFszLjIwMyAtLSBEZXRlY3RpbmcgYW5kIE1hbmFnaW5nIE91dGxpZXJzXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzAzLm1sL2wtMy0yMDMtb3V0bGllcnMvbC0zLTIwMy5odG1sKSBwcm92aWRlcyBhIG1vcmUgZGV0YWlsZWQgdHJlYXRtZW50IG9mIHRoaXMgc3ViamVjdC4KCmBgYHtyfQojIyBjYWxjdWxhdGUgbWVhbiBhbmQgc2QgZm9yIGVhY2ggbnVtZXJpYyBjb2x1bW4KbnVtZXJpYy5jb2xzIDwtIHNhcHBseShkZiwgaXMubnVtZXJpYykKCmZvciAoYyBpbiAxOmxlbmd0aChudW1lcmljLmNvbHMpKSB7CiAgaWYgKG51bWVyaWMuY29sc1tjXSA9PSBUUlVFKSB7CiAgICAjIyBmaW5kIG91dGxpZXJzIGluIG51bWVyaWMgY29sdW1uCiAgICBtIDwtIG1lYW4oZGZbLGNdLCBuYS5ybSA9IFQpCiAgICBzIDwtIHNkKGRmWyxjXSwgbmEucm0gPSBUKQogICAgCiAgICBvdXRsaWVycyA8LSB3aGljaChhYnMoKG0gLSBkZlssY10pIC8gcykgPiAzLjApCiAgICAKICAgIGlmIChsZW5ndGgob3V0bGllcnMpID4gMCkgewogICAgICAjIyBmb3VuZCBvdXRsaWVyczsgcmVwbGFjZSB3aXRoIE5BIGFuZCBpbXB1dGUgbGF0ZXIKICAgICAgY2F0KCJGb3VuZCBvdXRsaWVycyBpbiBjb2x1bW4gJyIsIG5hbWVzKGRmKVtjXSwgIic6IFxuIikKICAgICAgY2F0KCIgICAtLT4gIiwgZGZbb3V0bGllcnMsY10sICJcblxuIikKICAgIH0KICB9Cn0KYGBgCgpXZSBuZWVkIHRvIGJlIGNhcmVmdWwgdG8gaWdub3JlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBjYWxjdWxhdGlvbiBvZiB0aGUgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGFzIHRob3NlIGhhdmUgbm90IHlldCBiZWVuIGRlYWx0IHdpdGguCgpTbywgdGhyZWUgY29sdW1ucyBjb250YWluIG91dGxpZXIgdmFsdWVzLiBXaGlsZSBoYXZpbmcgNSBjaGlsZHJlbiBtaWdodCBiZSBjb25zaWRlcmVkIGFuIG91dGxpZXIgaW4gdGhpcyBkYXRhc2V0LCBpdCBpcyBieSBubyBtZWFucyBhbiB1bnVzdWFsIHZhbHVlLiBUaGVyZWZvcmUsIHdlIHdpbGwgaWdub3JlIHRob3NlICJvdXRsaWVycyIuIExpa2V3aXNlIGZvciB0aGUgaGlnaCBjaGFyZ2VzIG1pZ2h0IGJlIG91dGxpZXJzIGJ1dCBjb3VsZCBiZSBqdXN0aWZpZWQgZGVwZW5kaW5nIG9uIHRoZSBtZWRpY2FsIHByb2NlZHVyZXMgdGhhdCB3ZXJlIHBlcmZvcm1lZC4gQWdhaW4sIGl0IGlzIGJlc3QgdG8gaWdub3JlIHRob3NlIGluIHRoaXMgc2l0dWF0aW9uLgoKT24gdGhlIG90aGVyIGhhbmQsIHRoZSBleHRyZW1lbHkgaGlnaCB2YWx1ZSBmb3IgQk1JIGlzIGluZGVlZCB2ZXJ5IGxpa2VseSBpbmNvcnJlY3QgYW5kIGlzIHByb2JhYmx5IGEgZGF0YSBlbnRyeSBlcnJvcjsgcGVyaGFwcyBhIHNsaXBwZWQgZGVjaW1hbCB3aGVuIHNvbWVvbmUgd2FudGVkIHRvIGVudGVyIDM2LjAwNSBpbnN0ZWFkIG9mIDM2MC4wNS4gTm93LCB3ZSBjb3VsZCByZXBsYWNlIHRoaXMgdmFsdWUgd2l0aCAqTkEqIGFuZCB0aGVuIHRyZWF0IGl0IGFzIGFuIGFjdHVhbCBtaXNzaW5nIHZhbHVlIG9yIGFwcGx5IGEgY29ycmVjdGlvbiB0aGF0IG1ha2VzIHNlbnNlIGZyb20gYSBkb21haW4gcGVyc3BlY3RpdmUuIFdlIHdpbGwgY2hvb3NlIHRoZSBsYXR0ZXIgaW4gdGhpcyBzaXR1YXRpb24uCgpgYGB7cn0KZGYkYm1pW3doaWNoKGRmJGJtaSA+IDM2MCldIDwtIDM2LjAwNQpgYGAKClRoZSB0cmVhdG1lbnQgb2Ygb3V0bGllcnMgaXMgc2l0dWF0aW9uYWwgYW5kIHRoZXJlIGlzIG5vdCBhbHdheXMgYSBjbGVhciBjb3Vyc2Ugb2YgYWN0aW9uLiBFYWNoIG1ldGhvZCB0byBtYW5hZ2Ugb3V0bGllcnMgbXVzdCBiZSB2aWV3ZWQgaW4gdGhlIGNvbnRleHQgb2YgdGhlIGJ1c2luZXNzIGFuZCB0aGUgZ29hbHMgb2YgdGhlIGFuYWx5c2lzIGFuZCByZWdyZXNzaW9uIG1vZGVsaW5nLgoKIyMjIDMuIEltcHV0ZSBNaXNzaW5nIFZhbHVlcwoKTWlzc2luZyB2YWx1ZXMgbXVzdCBiZSBhZGRyZXNzZWQgZWl0aGVyIGJ5IGVsaW1pbmF0aW5nIGFueSBvYnNlcnZhdGlvbiAocm93KSB0aGF0IGhhcyBhIG1pc3NpbmcgdmFsdWUgaW4gYW55IG9mIGl0cyBmZWF0dXJlcyBvciBieSBpbXB1dGluZyB0aGUgbWlzc2luZyB2YWx1ZSB3aXRoIGFuIGVzdGltYXRlLiBGb3IgbnVtZXJpYyBmZWF0dXJlcywgYSBjb21tb24gc3RyYXRlZ3kgaXMgdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlIHdpdGggdGhlIG1lYW4gb3IgbWVkaWFuLCB3aGlsZSBmb3IgY2F0ZWdvcmljYWwgZmVhdHVyZXMsIHRoZSBtb2RlIGlzIG9mdGVuIHVzZWQuCgpMZXNzb24gWzMuMjA0IC0tIE1hbmFnaW5nIE1pc3NpbmcgVmFsdWVzIGluIERhdGFdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvMDMubWwvbC0zLTIwNC1taXNzaW5nLXZhbHVlcy9sLTMtMjA0Lmh0bWwpIHByb3ZpZGVzIGEgbW9yZSBkZXRhaWxlZCB0cmVhdG1lbnQgb2YgdGhpcyBzdWJqZWN0LgoKVGhlIGZpcnN0IHN0ZXAgaXMgdG8gY2hlY2sgZWFjaCBmZWF0dXJlIGZvciBtaXNzaW5nIHZhbHVlcy4gV2hpbGUgd2UgY2FuIGNlcnRhaW5seSBjaGVjayBlYWNoIGZlYXR1cmUgbWFudWFsbHksIGl0ZXJhdGluZyBvdmVyIGFsbCBjb2x1bW5zIGlzIG1vcmUgcHJhY3RpY2FsLiBUaGUgY29kZSBiZWxvdyB1c2VzIGEgc2ltcGxlIGxvb3AsIGJ1dCBvdGhlciBhcHByb2FjaGVzIGNhbiBiZSB1c2VkIGFzIHdlbGwsIHN1Y2ggYXMgdXNpbmcgYHNhcHBseSgpYC4KCkZvciBtaXNzaW5nIG51bWVyaWMgdmFsdWVzIHdlIGNhbiBjaGVjayBmb3IgKk5BKiB3aXRoIHRoZSBmdW5jdGlvbiBgaXMubmEoKWAuIEhvd2V2ZXIsIHRoYXQgd2lsbCBub3QgcmV2ZWFsIG1pc3NpbmcgdmFsdWVzIGluIGEgImNoYXJhY3RlciIgY29sdW1uLCBzdWNoIGFzICpzbW9rZXIqIG9yICpyZWdpb24qLiBGb3IgdGhpcyB3ZSBuZWVkIHRvIGNoZWNrIHdoZXRoZXIgdGhlIHRleHQgaXMgIiIuCgpgYGB7ciBjaGVja01pc3NpbmdWYWx1ZXN9Cm51bS5Sb3dzIDwtIG5yb3coZGYpCm51bS5Db2xzIDwtIG5jb2woZGYpCgpmb3VuZCA8LSBGCgpmb3IgKGMgaW4gMTpudW0uQ29scyl7CiAgbWlzc2luZy5WYWx1ZXMgPC0gd2hpY2goaXMubmEoZGZbLGNdKSB8IGRmWyxjXSA9PSAiIikKICBudW0uTWlzc2luZy5WYWx1ZXMgPC0gbGVuZ3RoKG1pc3NpbmcuVmFsdWVzKQogIGlmIChudW0uTWlzc2luZy5WYWx1ZXMgPiAwKSB7CiAgICBwcmludChwYXN0ZTAoIkNvbHVtbiAnIiwgbmFtZXMoZGYpW2NdLCAiJyBoYXMgIiwgbnVtLk1pc3NpbmcuVmFsdWVzLCAiIG1pc3NpbmcgdmFsdWVzIikpCiAgICBmb3VuZCA8LSBUCiAgfQp9CgppZiAoIWZvdW5kKSB7CiAgcHJpbnQoIm5vIG1pc3NpbmcgdmFsdWVzIGRldGVjdGVkIikKfQpgYGAKClRoZSBudW1lcmljIGNvbHVtbiAqYm1pKiBoYXMgdGhyZWUgbWlzc2luZyB2YWx1ZXMsIHdoaWxlIHRoZSBjYXRlZ29yaWNhbCBjb2x1bW4gKnNtb2tlciogaGFzIHR3byBtaXNzaW5nIHZhbHVlcy4KClRoZSBzaW1wbGVzdCBhcHByb2FjaCBmb3IgbnVtZXJpYyBmZWF0dXJlcyBpcyB0byByZXBsYWNlIHRoZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHRoZSBtZWFuIG9mIHRoZSBjb2x1bW4gc2luY2UgKmJtaSogaXMgYSBudW1lcmljIGZlYXR1cmUuIEhvd2V2ZXIsIHRoZSBtZWFuIGNhbiBiZSBza2V3ZWQgYnkgb3V0bGllcnMgc28gdGhlIG1lZGlhbiBtaWdodCBvZnRlbiBiZSBiZXR0ZXIuIE5vdywgaW4gdGhpcyBkYXRhLCB0aGVyZSBpcyBsaWtlbHkgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIEJNSSBvZiBtYWxlIHZzIGZlbWFsZSBpbnN1cmVkLCBzbyB1c2luZyB0aGUgbWVkaWFuIG9mIHRoZSBhcHByb3ByaWF0ZSBzZXggbWlnaHQgYmUgbW9yZSByZWFzb25hYmxlLiBJbiBmYWN0LCB3ZSBjb3VsZCB1c2UgYSAqdCotdGVzdCB0byB0ZXN0IHdoZXRoZXIgdGhhdCBkaWZmZXJlbmNlIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuCgpMZXQncyByZXBsYWNlIHRoZSBtaXNzaW5nIHZhbHVlcyBmb3IgKmJtaSogd2l0aCB0aGUgbWVkaWFuIG9mIHRoZSByZXNwZWN0aXZlIHNleCwgb2YgY291cnNlLCBpZ25vcmluZyBhbnkgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGNhbGN1bGF0aW9uIG9mIHRoZSBtZWRpYW4uCgpgYGB7cn0KbWVkaWFuLk1hbGUgPC0gbWVkaWFuKGRmJGJtaVt3aGljaChkZiRzZXggPT0gIm1hbGUiKV0sIG5hLnJtID0gVCkKbWVkaWFuLkZlbWFsZSA8LSBtZWRpYW4oZGYkYm1pW3doaWNoKGRmJHNleCA9PSAiZmVtYWxlIildLCBuYS5ybSA9IFQpCgptaXNzaW5nLlZhbHVlcyA8LSB3aGljaChpcy5uYShkZiRibWkpKQoKZGYkYm1pW21pc3NpbmcuVmFsdWVzXSA8LSBpZmVsc2UoZGYkc2V4W21pc3NpbmcuVmFsdWVzXSA9PSAiZmVtYWxlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lZGlhbi5GZW1hbGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWRpYW4uTWFsZSkKYGBgCgpSYXRoZXIgdGhhbiB1c2luZyBgaWZlbHNlYCB3ZSBjb3VsZCBoYXZlIGFsc28gdXNlZCBhIGxvb3AuIFVzaW5nIGBpZmVsc2VgIG9uIGEgdmVjdG9yIG1lYW5zIHRoYXQgd2UgYXJlIHVzaW5nIGEgdmVjdG9yIG9wZXJhdGlvbi4gSXQgaXMgbW9yZSBlZmZpY2llbnQgdGhhbiBhIGxvb3AsIGFsYmVpdCBhIGJpdCBoYXJkZXIgdG8gdW5kZXJzdGFuZC4gVGhlIGNvZGUgYWxzbyBhc3N1bWUgb25seSB0d28gdmFsdWVzIGZvciAqc2V4KjogIm1hbGUiIGFuZCAiZmVtYWxlIiwgYW5kIG5vIG1pc3NpbmcgdmFsdWVzIGZvciB0aGUgY29sdW1uICpzZXgqLgoKV2Ugd2lsbCB1c2UgYSBzaW1pbGFyIGFwcHJvYWNoIGZvciB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGNvbHVtbiAqc21va2VyKiBieSByZXBsYWNpbmcgdGhlIHZhbHVlIHdpdGggdGhlIG1vZGUgZm9yIGVhY2ggc2V4LiBPbmUgY29tcGxpY2F0aW9uIGlzIHRoYXQgUiBkb2VzIG5vdCBoYXZlIGEgIm1vZGUiIGZ1bmN0aW9uW14xXSwgc28gd2UgbmVlZCB0byBkbyB0aGUgd29yayBvdXJzZWx2ZXMuIFRoZSBgdGFibGUoKWAgZnVuY3Rpb24gY291bnRzIHRoZSBudW1iZXIgb2Ygb2NjdXJyZW5jZXMgZm9yIGVhY2ggdHlwZSBvZiBzbW9rZXIuCgpbXjFdOiB0aGVyZSBpcyBhIGZ1bmN0aW9uIGBtb2RlYCBidXQgaXQgcmV0dXJucyB0aGUgdHlwZSBvZiBhbiBvYmplY3QKCmBgYHtyfQpudW0uTWFsZS5TbW9rZXJzIDwtIHRhYmxlKGRmW3doaWNoKGRmJHNleCA9PSAibWFsZSIpLCAic21va2VyIl0pCm1vZGUuTWFsZS5TbW9rZXJzIDwtIG5hbWVzKG51bS5NYWxlLlNtb2tlcnMpW3doaWNoLm1heChudW0uTWFsZS5TbW9rZXJzKV0KCm51bS5GZW1hbGUuU21va2VycyA8LSB0YWJsZShkZlt3aGljaChkZiRzZXggPT0gImZlbWFsZSIpLCAic21va2VyIl0pCm1vZGUuRmVtYWxlLlNtb2tlcnMgPC0gbmFtZXMobnVtLkZlbWFsZS5TbW9rZXJzKVt3aGljaC5tYXgobnVtLkZlbWFsZS5TbW9rZXJzKV0KCm1pc3NpbmcuVmFsdWVzLkZlbWFsZSA8LSB3aGljaChkZiRzbW9rZXIgPT0gIiIgJiBkZiRzZXggPT0gImZlbWFsZSIpCm1pc3NpbmcuVmFsdWVzLk1hbGUgPC0gd2hpY2goZGYkc21va2VyID09ICIiICYgZGYkc2V4ID09ICJtYWxlIikKCmlmIChsZW5ndGgobWlzc2luZy5WYWx1ZXMuRmVtYWxlKSA+IDApIHsKICBkZiRzbW9rZXJbbWlzc2luZy5WYWx1ZXMuRmVtYWxlXSA8LSBtb2RlLkZlbWFsZS5TbW9rZXJzCn0KCmlmIChsZW5ndGgobWlzc2luZy5WYWx1ZXMuTWFsZSkgPiAwKSB7CiAgZGYkc21va2VyW21pc3NpbmcuVmFsdWVzLk1hbGVdIDwtIG1vZGUuTWFsZS5TbW9rZXJzCn0KYGBgCgpBdCB0aGlzIHBvaW50IGluIHRoZSBkYXRhIHByZXBhcmF0aW9uIHBoYXNlLCB3ZSBoYXZlIGRlYWx0IHdpdGggYm90aCBvdXRsaWVycyBhbmQgbWlzc2luZyB2YWx1ZXMuIE5vdywgd2UgbmVlZCB0byBjaGVjayB0aGUgc3VpdGFiaWxpdHkgb2YgdGhlIGZlYXR1cmVzIGZvciByZWdyZXNzaW9uLgoKIyMjIDQuIFJlc29sdmUgTXVsdGljb2xsaW5lYXJpdHkKCk11bHRpY29sbGluZWFyaXR5IHJlZmVycyB0byBhIHNpdHVhdGlvbiBpbiBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiB3aGVyZSB0d28gb3IgbW9yZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgZXhoaWJpdCBhIGhpZ2ggZGVncmVlIG9mIGNvcnJlbGF0aW9uLiBXaGVuIG11bHRpY29sbGluZWFyaXR5IGlzIHByZXNlbnQsIHRoZSByZWdyZXNzaW9uIG1vZGVsIGVuY291bnRlcnMgZGlmZmljdWx0eSBpbiBlc3RpbWF0aW5nIHRoZSBpbmRpdmlkdWFsIGNvbnRyaWJ1dGlvbiBvZiBlYWNoIHByZWRpY3RvciB0byB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGJlY2F1c2UgdGhlIHByZWRpY3RvcnMgY29udmV5IHJlZHVuZGFudCBvciBvdmVybGFwcGluZyBpbmZvcm1hdGlvbjsgaW4gb3RoZXIgd29yZHMsIGZlYXR1cmVzIGFyZSAiZG91YmxlIGNvdW50ZWQiLgoKVGhlIHByZXNlbmNlIG9mIG11bHRpY29sbGluZWFyaXR5IGRvZXMgbm90IGFmZmVjdCB0aGUgcHJlZGljdGl2ZSBjYXBhYmlsaXR5IG9mIHRoZSByZWdyZXNzaW9uIG1vZGVsIGJ1dCBzZXZlcmVseSB1bmRlcm1pbmVzIHRoZSBpbnRlcnByZXRhYmlsaXR5IG9mIHRoZSBjb2VmZmljaWVudHMuIFdoZW4gaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFyZSBoaWdobHkgY29ycmVsYXRlZCwgc21hbGwgY2hhbmdlcyBpbiB0aGUgZGF0YXNldCBjYW4gcmVzdWx0IGluIHZhc3RseSBkaWZmZXJlbnQgY29lZmZpY2llbnQgZXN0aW1hdGVzLiBUaGlzIGluc3RhYmlsaXR5IGNvbXBsaWNhdGVzIGRlY2lzaW9uLW1ha2luZywgcGFydGljdWxhcmx5IGluIGFwcGxpY2F0aW9ucyBzdWNoIGFzIGZpbmFuY2UgYW5kIGhlYWx0aGNhcmUsIHdoZXJlIHByZWNpc2UgdmFyaWFibGUgaW1wYWN0IGFzc2Vzc21lbnRzIGFyZSBjcnVjaWFsLgoKVGhlIGFyZSBhIG51bWJlciBvZiB3YXlzIHRvIGNoZWNrIGZvciBtdWx0aWNvbGxpbmVhcml0eSwgYnV0IGEgY29ycmVsYXRpb24gbWF0cml4IGlzIGEgY29tbW9uIHRvb2wuIEV4YW1pbmluZyB0aGUgY29ycmVsYXRpb24gbWF0cml4IG9mIGluZGVwZW5kZW50IHZhcmlhYmxlcyBwcm92aWRlcyBhbiBpbml0aWFsIGluZGljYXRpb24gb2YgbXVsdGljb2xsaW5lYXJpdHkuIElmIHR3byBvciBtb3JlIG51bWVyaWMgZmVhdHVyZXMgZXhoaWJpdCBjb3JyZWxhdGlvbnMgYWJvdmUgMC44IG9yIHNvLCBtdWx0aWNvbGxpbmVhcml0eSBtYXkgYmUgYW4gaXNzdWUuCgpgYGB7ciBjaGVja011bHRpY29sbGluZWFyaXR5fQojIyBmaW5kIG51bWVyaWMgY29sdW1ucyBvbmx5Cm51bWVyaWMuY29scyA8LSBzYXBwbHkoZGYsIGlzLm51bWVyaWMpCgojIyBjcmVhdGUgY29ycmVsYXRpb24gbWF0cml4CmNvcihkZlssbnVtZXJpYy5jb2xzXSkKYGBgCgpGb3Igb3VyIGRhdGFzZXQsIHRoZXJlIGlzIG5vIG11bHRpY29sbGluZWFyaXR5LiBIb3dldmVyLCBpZiB3ZSBoYWQgZm91bmQgY29ycmVsYXRpb25zIGFib3ZlIHNvbWUgdGhyZXNob2xkIChnZW5lcmFsbHksIGFib3V0IDAuOCksIHRoZW4gdmFyaW91cyBzdHJhdGVnaWVzIGNhbiBiZSBlbXBsb3llZCB0byBtaXRpZ2F0ZSBpdHMgaW1wYWN0LiBPbmUgYXBwcm9hY2ggaXMgdG8gZXhjbHVkZSBvbmUgb2YgdGhlIHR3byB2YXJpYWJsZXMgaW4gdGhlIHJlZ3Jlc3Npb24gbW9kZWxpbmcuIFRoaXMgZGVjaXNpb24gc2hvdWxkIGJlIGd1aWRlZCBieSBkb21haW4ga25vd2xlZGdlIGFuZCBmZWF0dXJlIGltcG9ydGFuY2UgYW5hbHlzaXMuIEluc3RlYWQgb2YgcmVtb3ZpbmcgYSB2YXJpYWJsZSwgaGlnaGx5IGNvcnJlbGF0ZWQgcHJlZGljdG9ycyBjYW4gYmUgY29tYmluZWQgaW50byBhIHNpbmdsZSBmZWF0dXJlLCBzdWNoIGFzIGNyZWF0aW5nIGFuIGluZGV4IG9yIGFuIGF2ZXJhZ2Ugc2NvcmUuIFRoaXMgYXBwcm9hY2ggcmV0YWlucyB0aGUgcHJlZGljdGl2ZSBpbmZvcm1hdGlvbiB3aGlsZSByZWR1Y2luZyByZWR1bmRhbmN5LiBNb3JlIGFkdmFuY2VkIGFwcHJvYWNoZXMgYXJlIHRvIHVzZSBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpIHRvIHRyYW5zZm9ybSB0aGVtIGludG8gYSBzZXQgb2YgdW5jb3JyZWxhdGVkIHByaW5jaXBhbCBjb21wb25lbnRzIG9yIHRvIHVzZSByaWRnZSByZWdyZXNzaW9uIGluc3RlYWQuIFNjYWxpbmcgYWxsIGZlYXR1cmVzIHRvIGEgY29tbW9uIHNjYWxlIGNhbiBhbHNvIGJlIGhlbHBmdWwuCgojIyMgNi4gU2NhbGUgRmVhdHVyZXMKCkluIGNhc2VzIHdoZXJlIG11bHRpY29sbGluZWFyaXR5IGFyaXNlcyBkdWUgdG8gdGhlIHNjYWxpbmcgb2YgdmFyaWFibGVzLCBjZW50ZXJpbmcgKHN1YnRyYWN0aW5nIHRoZSBtZWFuKSBvciBzdGFuZGFyZGl6aW5nIChzY2FsaW5nIHRvIHVuaXQgdmFyaWFuY2UpIG1heSByZWR1Y2UgaXRzIGltcGFjdC4gVGhpcywgb2YgY291cnNlLCBvbmx5IGFwcGxpZXMgdG8gbnVtZXJpYyBmZWF0dXJlcy4KCkEgZHJhd2JhY2sgb2Ygc2NhbGluZyBpcyB0aGF0IGl0IGNoYW5nZXMgdGhlIGludGVycHJldGFiaWxpdHkgb2YgdGhlIGNvZWZmaWNpZW50cy4KCiMjIyA3LiBFbmNvZGUgQ2F0ZWdvcmljYWwgRmVhdHVyZXMKClNvIGZhciwgd2UgaGF2ZSBwcmltYXJpbHkgZGVhbHQgd2l0aCB0aGUgbnVtZXJpYyBmZWF0dXJlcy4gTGV0J3MgdHVybiBvdXIgYXR0ZW50aW9uIHRvIHRoZSBjYXRlZ29yaWNhbCBmZWF0dXJlcy4gQmluYXJ5IGNhdGVnb3JpY2FsIGZlYXR1cmVzICh3aGVyZSB0aGVyZSBhcmUgb25seSB0d28gcG9zc2libGUgdmFsdWVzKSBzaG91bGQgYmUgdHVybmVkIGludG8gYSAwLzEgZW5jb2RpbmcuCgpJbiBvdXIgZGF0YXNldCwgd2UgaGF2ZSB0d28gYmluYXJ5IGNhdGVnb3JpY2FsIGZlYXR1cmVzOiAqc21va2VyKiBhbmQgKnNleCouIE5hdHVyYWxseSwgaW4gb3RoZXIgZG9tYWlucywgdGhvc2UgZmVhdHVyZXMgbWF5IGJlIG11bHRpLWxldmVsIGNhdGVnb3JpY2FsIHJhdGhlciB0aGFuIGJpbmFyeSBjYXRlZ29yaWNhbC4KCmBgYHtyfQojIyBlbmNvZGUgc2V4OiBtYWxlIGFzIDAgYW5kIGZlbWFsZSBhcyAxCmRmJHNleCA8LSBpZmVsc2UoZGYkc2V4ID09ICJtYWxlIiwgMCwgMSkKCiMjIGVuY29kZSBzbW9rZXI6IDEgPSB5ZXMgYW5kIDAgPSBubwpkZiRzbW9rZXIgPC0gaWZlbHNlKGRmJHNtb2tlciA9PSAieWVzIiwgMSwgMCkKYGBgCgpUaGUgY2hvaWNlIG9mIDAgYW5kIDEgaXMgYXJiaXRyYXJ5OyB3ZSBjb3VsZCBoYXZlIGp1c3QgYXMgZWFzaWx5IG1hZGUgInllcyIgZm9yIHNtb2tlciAwIGluc3RlYWQgb2YgMS4KClRoZSBmZWF0dXJlICpyZWdpb24qIGlzIGEgbXVsdGktbGV2ZWwgY2F0ZWdvcmljYWwgZmVhdHVyZS4gVGhlcmUgYXJlIHNldmVyYWwgYXBwcm9hY2hlcyB0byBlbmNvZGluZyBjYXRlZ29yaWNhbCBmZWF0dXJlcyBhcyBudW1lcmljIHZhbHVlcy4gV2Ugd2lsbCBvbmx5IGRpc2N1c3Mgb25lIG1ldGhvZDogb25lLWhvdCBlbmNvZGluZy4gV2hpbGUgaXQgaXMgc2ltcGxlIGFuZCBoYXMgaGlnaCBpbnRlcnByZXRhYmlsaXR5LCBpdCBkb2VzIG5vdCB3b3JrIHdlbGwgd2hlbiB0aGVyZSBhcmUgbW9yZSB0aGFuIGFib3V0IGZvdXIgbGV2ZWxzLgoKTGV0J3Mgc3RhcnQgYnkgZmluZGluZyBvdXQgYWxsIG9mIHRoZSBkaWZmZXJlbnQgdmFsdWVzIGZvciAqcmVnaW9uKiwgKmkuZS4qLCB0aGUgbGV2ZWxzLgoKYGBge3J9CnRhYmxlKGRmJHJlZ2lvbikKYGBgCgpCZWZvcmUgd2UgZ2V0IGludG8gZGV0YWlscyBvbiBob3cgdG8gcGVyZm9ybSBvbmUtaG90IGVuY29kaW5nIGluIFIsIGxldCdzIGZpcnN0IGV4cGxhaW4gdGhlIG1ldGhvZC5JbiBvbmUtaG90IGVuY29kaW5nLCB3ZSBhZGQgYWRkaXRpb25hbCBiaW5hcnkgY29sdW1ucyAoMC8xKSB0byB0aGUgZGF0YS4gT25lIGNvbHVtbiBsZXNzIHRoYW4gd2hhdCB3ZSBoYXZlIGxldmVscy4gU28sIGluIG91ciBkYXRhc2V0LCB3ZSBoYXZlIGZvdXIgbGV2ZWxzIGZvciAqcmVnaW9uKjogbm9ydGhlYXN0LCBub3J0aHdlc3QsIHNvdXRoZWFzdCwgc291dGh3ZXN0LiBUaGlzIGlzIGEgcGFydGlhbCBzZXQgb2YgY29sdW1ucyBhbmQgcm93cyBvZiB0aGUgb3JpZ2luYWwgYXJyYW5nZW1lbnQ6CgpgYGB7cn0KaGVhZChkZlsxOjUsYygxLDIsNildKQpgYGAKCldlIG5vdyBhZGQgdGhyZWUgYWRkaXRpb25hbCBjb2x1bW5zOiBub3J0aGVhc3QsIG5vcnRod2VzdCwgc291dGhlYXN0IGFuZCBlbmNvZGUgdGhlIHZhbHVlcyBhcyBmb2xsb3dzOgoKfCByZWdpb24gICAgfCBub3J0aGVhc3QgfCBub3J0aHdlc3QgfCBzb3V0aGVhc3QgfAp8LS0tLS0tLS0tLS18LS0tLS0tLS0tLS18LS0tLS0tLS0tLS18LS0tLS0tLS0tLS18Cnwgbm9ydGhlYXN0IHwgMSAgICAgICAgIHwgMCAgICAgICAgIHwgMCAgICAgICAgIHwKfCBub3J0aHdlc3QgfCAwICAgICAgICAgfCAxICAgICAgICAgfCAwICAgICAgICAgfAp8IHNvdXRoZWFzdCB8IDAgICAgICAgICB8IDAgICAgICAgICB8IDEgICAgICAgICB8Cnwgc291dGh3ZXN0IHwgMCAgICAgICAgIHwgMCAgICAgICAgIHwgMCAgICAgICAgIHwKCkVhY2ggKip1bmlxdWUgY2F0ZWdvcnkqKiBiZWNvbWVzIGEgbmV3IGNvbHVtbiBleGNlcHQgb25lIG9mIHRoZW0sIGFuZCBvbmx5ICoqb25lIGNvbHVtbiBoYXMgYSAxIHBlciByb3cqKi4gVGhlIGNhdGVnb3J5IGxlZnQgb3V0IGhhcyBhbGwgMCBpbiB0aGUgcm93CgpJbiBSLCB3ZSBjYW4gZWl0aGVyIHVzZSBhIGxvb3Agb3IgdGhlIGBhcHBseSgpYCBmdW5jdGlvbnMgdG8gbWFrZSB0aGUgYXNzaWdubWVudHMsIG9yIHVzZSBmdW5jdGlvbnMgZnJvbSBhIHBhY2thZ2UuIE5vdGUgdGhhdCB0aGUgYGxtYCBmdW5jdGlvbiBmb3IgYnVpbGRpbmcgYSByZWdyZXNzaW9uIG1vZGVsIHdpbGwgZG8gb25lLWhvdCBlbmNvZGluZyBhdXRvbWF0aWNhbGx5IGlmIHRoZSBjYXRlZ29yaWNhbCBjb2x1bW4gaXMgYSAqZmFjdG9yIHZhcmlhYmxlKi4gV2Ugd2lsbCBzaG93IGhvdyB0byBkbyBvbmUtaG90IGVuY29kaW5nIG91cnNlbHZlcywgc28gd2UgaGF2ZSBmdWxsIGNvbnRyb2wgYW5kIGZsZXhpYmlsaXR5LgoKYGBge3Igb25lSG90RW5jb2Rpbmd9Cm4gPC0gbnJvdyhkZikKCiMjIGFkZCBuZXcgY29sdW1ucwpkZiRub3J0aGVhc3QgPC0gMApkZiRub3J0aHdlc3QgPC0gMApkZiRzb3V0aGVhc3QgPC0gMAoKZm9yIChpIGluIDE6bikgewogIGlmIChkZiRyZWdpb25baV0gPT0gIm5vcnRoZWFzdCIpCiAgICBkZiRub3J0aGVhc3RbaV0gPC0gMQogIGVsc2UgaWYgKGRmJHJlZ2lvbltpXSA9PSAibm9ydGh3ZXN0IikKICAgIGRmJG5vcnRod2VzdFtpXSA8LSAxCiAgZWxzZSBpZiAoZGYkcmVnaW9uW2ldID09ICJzb3V0aGVhc3QiKQogICAgZGYkc291dGhlYXN0W2ldIDwtIDEKfQpgYGAKCkhlcmUncyBvdXIgdXBkYXRlZCBkYXRhc2V0IHdpdGggdGhlIG9uZS1ob3QgZW5jb2RlZCBjYXRlZ29yaWNhbCBmZWF0dXJlOgoKYGBge3J9CmhlYWQoZGZbMTo1LGMoMSwyLDYsOCw5LDEwKV0pCmBgYAoKT25jZSB3ZSBoYXZlIGRvbmUgdGhlIGVuY29kaW5nLCB3ZSBuZWVkIHRvIGVpdGhlciByZW1vdmUgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGZyb20gdGhlIGRhdGFmcmFtZSBvciBiZSBzdXJlIHRvIGV4Y2x1ZGUgaXQgd2hlbiBidWlsZGluZyB0aGUgcmVncmVzc2lvbiBtb2RlbC4KCk9uZS1ob3QsIG9yIGR1bW15LCBlbmNvZGluZyBpcyBhIGNvbW1vbiBtZXRob2QgdXNlZCBpbiBtYWNoaW5lIGxlYXJuaW5nIGFuZCBkYXRhIHByZXByb2Nlc3NpbmcgdG8gY29udmVydCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaW50byBhIG51bWVyaWNhbCBmb3JtYXQgdGhhdCBjYW4gYmUgdXNlZCBieSBhbGdvcml0aG1zLiBJdCByZXByZXNlbnRzIGVhY2ggY2F0ZWdvcnkgYXMgYSBiaW5hcnkgdmVjdG9yLCB3aGVyZSBlYWNoIGNhdGVnb3J5IGdldHMgaXRzIG93biBjb2x1bW4gYW5kIGlzIG1hcmtlZCBhcyBgMWAgKHByZXNlbnQpIG9yIGAwYCAoYWJzZW50KS4gTGVzc29uIFszLjIwNyAtIEVuY29kaW5nIENhdGVnb3JpY2FsIEZlYXR1cmVzXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzAzLm1sL2wtMy0yMDctY2F0ZWdvcmljYWwtZW5jb2RpbmcvbC0zLTIwNy5odG1sKSBwcm92aWRlcyBhIG1vcmUgZGV0YWlsZWQgdHJlYXRtZW50IG9mIHRoaXMgc3ViamVjdC4KCkFsdGVybmF0aXZlcyB0byAqKk9uZS1Ib3QgRW5jb2RpbmcqKiBhcmUgKipMYWJlbCBFbmNvZGluZyoqIHdoZXJlIHdlIGFzc2lnbiBudW1iZXJzIHRvIGNhdGVnb3JpZXMgKCplLmcuKiwgYCJOb3J0aCIg4oaSIDFgLCBgIlNvdXRoIiDihpIgMmApLCBidXQgdGhpcyBpbXBsaWVzIGFuICoqb3JkaW5hbCByZWxhdGlvbnNoaXAqKiwgd2hpY2ggbWF5IGJlIGluY29ycmVjdCwgKipUYXJnZXQgRW5jb2RpbmcqKiB3aGljaCByZXBsYWNlcyBjYXRlZ29yaWVzIHdpdGggdGhlaXIgbWVhbiB0YXJnZXQgdmFsdWUgKHVzZWQgaW4gcHJlZGljdGl2ZSBtb2RlbGluZyksIG9yIFwqXCpGcmVxdWVuY3kgRW5jb2RpbmciIHdoZXJlIGVhY2ggY2F0ZWdvcmllcyB3aXRoIHRoZWlyIGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlLgoKKipDQVVUSU9OKio6IFNvbWUgZGF0YXNldHMgbWF5IHVzZSBudW1lcmljIHZhbHVlcyB0byBlbmNvZGUgY2F0ZWdvcmljYWwgZmVhdHVyZXMgYnV0IHRoYXQgZG9lcyBub3QgbWFrZSB0aGUgZmVhdHVyZSBudW1lcmljLiBGb3IgZXhhbXBsZSwgdGhlIHJlZ2lvbnMgY291bGQgaGF2ZSBiZWVuIGRlZmluZWQgYXMgMSBmb3IgIm5vcnRoZWFzdCIsIDIgZm9yICJub3J0aHdlc3QiLCAqZXRjLiogVGhhdCBkb2VzIG5vdCBtYWtlIHRoZW0gbnVtZXJpYyBhcyBpdCB3b3VsZCBtYWtlIG5vIHNlbnNlIHRvIHRhbGsgYWJvdXQgYW4gImF2ZXJhZ2UgcmVnaW9uIiBvciB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSByZWdpb25zLCBvciB0aGUgYXJpdGhtZXRpYyBkaWZmZXJlbmNlIGJldHdlZW4gbm9ydGhlYXN0IGFuZCBzb3V0aHdlc3QuCgojIyMgOC4gU2VsZWN0IEZlYXR1cmVzCgpUaGVyZSBpcyBubyBzdHJpY3QgcnVsZSBvbiBob3cgbWFueSBmZWF0dXJlcyBhcmUgdG9vIG1hbnkgaW4gcmVncmVzc2lvbiwgYXMgaXQgZGVwZW5kcyBvbiB0aGUgc2l6ZSBvZiB0aGUgZGF0YXNldCBhbmQgdGhlIG5hdHVyZSBvZiB0aGUgcHJvYmxlbS4gSG93ZXZlciwgc29tZSBnZW5lcmFsIGd1aWRlbGluZXMgaGVscCBkZXRlcm1pbmUgd2hldGhlciB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzIGlzIGV4Y2Vzc2l2ZToKCi0gICAqKlJ1bGUgb2YgVGh1bWI6IFRoZSBTYW1wbGUgU2l6ZS10by1GZWF0dXJlIFJhdGlvKio6IEEgY29tbW9uIGhldXJpc3RpYyBzdWdnZXN0cyB0aGF0IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHNob3VsZCBiZSBhdCBsZWFzdCAxMCB0aW1lcyB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzIGZvciByZWxpYWJsZSByZWdyZXNzaW9uIGVzdGltYXRlcy4gSWYgdGhlIG51bWJlciBvZiBwcmVkaWN0b3JzIGFwcHJvYWNoZXMgb3IgZXhjZWVkcyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucywgdGhlIG1vZGVsIGlzIGxpa2VseSBvdmVyZml0dGVkLgoKLSAgICoqQWRqdXN0ZWQqKiAkUl4yJCBhbmQgTW9kZWwgQ29tcGxleGl0eTogVW5saWtlICRSXjIkLCB3aGljaCBhbHdheXMgaW5jcmVhc2VzIGFzIG1vcmUgcHJlZGljdG9ycyBhcmUgYWRkZWQsIGFkanVzdGVkICRSXjIkIGFjY291bnRzIGZvciB0aGUgbnVtYmVyIG9mIGZlYXR1cmVzLiBJZiBhZGp1c3RlZCAkUl4yJCBzdGFydHMgdG8gZGVjbGluZSBhcyBtb3JlIHZhcmlhYmxlcyBhcmUgaW50cm9kdWNlZCwgaXQgc3VnZ2VzdHMgdGhhdCBhZGRpdGlvbmFsIGZlYXR1cmVzIGRvIG5vdCBjb250cmlidXRlIHVzZWZ1bCBpbmZvcm1hdGlvbi4KCi0gICAqKkZlYXR1cmUgSW1wb3J0YW5jZSBBbmFseXNpcyoqOiBJZiBtYW55IHByZWRpY3RvcnMgaGF2ZSBsaXR0bGUgdG8gbm8gZWZmZWN0IG9uIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUsIHRoZWlyIGluY2x1c2lvbiBvbmx5IGFkZHMgbm9pc2UuIElkZW50aWZ5aW5nIGFuZCByZW1vdmluZyBpcnJlbGV2YW50IGZlYXR1cmVzIGltcHJvdmVzIG1vZGVsIHBlcmZvcm1hbmNlLgoKVG8gbWl0aWdhdGUgdGhlIHByb2JsZW1zIGFzc29jaWF0ZWQgd2l0aCB0b28gbWFueSBmZWF0dXJlcywgdmFyaW91cyBmZWF0dXJlIHNlbGVjdGlvbiB0ZWNobmlxdWVzIGNhbiBiZSBhcHBsaWVkIHRvIHJldGFpbiBvbmx5IHRoZSBtb3N0IGluZm9ybWF0aXZlIHByZWRpY3RvcnMuCgpGaWx0ZXIgbWV0aG9kcyBldmFsdWF0ZSBpbmRpdmlkdWFsIGZlYXR1cmUgcmVsZXZhbmNlIHVzaW5nIHN0YXRpc3RpY2FsIGNyaXRlcmlhIGJlZm9yZSBmaXR0aW5nIHRoZSByZWdyZXNzaW9uIG1vZGVsOgoKLSAgICoqQ29ycmVsYXRpb24gQW5hbHlzaXMqKjogSWRlbnRpZmllcyBmZWF0dXJlcyB0aGF0IGFyZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGVhY2ggb3RoZXIgYW5kIGNhbiBiZSByZW1vdmVkIHRvIGF2b2lkIHJlZHVuZGFuY3kuCi0gICAqKlZhcmlhbmNlIFRocmVzaG9sZGluZyoqOiBGZWF0dXJlcyB3aXRoIHZlcnkgbG93IHZhcmlhbmNlIHByb3ZpZGUgbGl0dGxlIGRpc2NyaW1pbmF0b3J5IHBvd2VyIGFuZCBjYW4gYmUgcmVtb3ZlZC4KLSAgICoqU3RlcHdpc2UgUmVncmVzc2lvbioqOiBBIGNvbW1vbiBtZXRob2QgdGhhdCBpdGVyYXRpdmVseSBhZGRzIG9yIHJlbW92ZXMgZmVhdHVyZXMgYmFzZWQgb24gbW9kZWwgcGVyZm9ybWFuY2UuCi0gICAqKkZvcndhcmQgU2VsZWN0aW9uKiogc3RhcnRzIHdpdGggbm8gdmFyaWFibGVzIGFuZCBhZGRzIHByZWRpY3RvcnMgb25lIGF0IGEgdGltZSBiYXNlZCBvbiBzaWduaWZpY2FuY2UuXAotICAgKipCYWNrd2FyZCBFbGltaW5hdGlvbioqIHN0YXJ0cyB3aXRoIGFsbCB2YXJpYWJsZXMgYW5kIHJlbW92ZXMgdGhlIGxlYXN0IHNpZ25pZmljYW50IHByZWRpY3RvcnMgaXRlcmF0aXZlbHkuXAotICAgKipTdGVwd2lzZSBTZWxlY3Rpb24qKiBpcyBhIGNvbWJpbmF0aW9uIG9mIGZvcndhcmQgYW5kIGJhY2t3YXJkIHNlbGVjdGlvbiwgYmFsYW5jaW5nIGZlYXR1cmUgaW5jbHVzaW9uIGFuZCByZW1vdmFsLgoKSW4gYWRkaXRpb24sIG1ldGhvZHMgc3VjaCBhcyBQQ0EgYW5kIFJpZGdlIFJlZ3Jlc3Npb24gYXJlIHVzZWZ1bC4gQWxsIG9mIHRoZXNlIGFyZSBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgaW50cm9kdWN0b3J5IGxlc3Nvbi4KCiMjIyA5LiBCdWlsZCBSZWdyZXNzaW9uIE1vZGVsCgpBZnRlciBlbnN1cmluZyB0aGUgZGF0YSBpcyBjbGVhbiBhbmQgcHJvcGVybHkgZm9ybWF0dGVkLCB0aGUgcmVncmVzc2lvbiBtb2RlbCBpcyBidWlsdCB1c2luZyB0aGUgYGxtKClgIGZ1bmN0aW9uLiBOb3RpY2UgdGhlICJmb3JtdWxhIjogdGhlIGRlcGVuZGVudCB2YXJpYWJsZXMgaXMgb24gdGhlIGxlZnQgc2lkZSwgZm9sbG93ZWQgYnkgYSBcfiBhbmQgdGhlbiBhIGxpbmVhciBhZGRpdGl2ZSBjb21iaW5hdGlvbiBvZiB0aGUgaW5kZXBlbmRlbnQgZmVhdHVyZSB2YXJpYWJsZXMuIFRoZSBmZWF0dXJlIHZhcmlhYmxlcyBhcmUgdGhlIG5hbWVzIG9mIHRoZSBudW1lcmljIGNvbHVtbnMgaW4gdGhlIGRhdGFmcmFtZSByZWZlcmVuY2VkIHdpdGggdGhlICpkYXRhKiBwYXJhbWV0ZXIuIEZlYXR1cmUgc2VsZWN0aW9uIGd1aWRlcyB3aGljaCB2YXJpYWJsZXMgYXJlIGluY2x1ZGVkIGluIHRoZSByZWdyZXNzaW9uIG1vZGVsLgoKVGhlIFIgY29kZSBiZWxvdyBkZW1vbnN0cmF0ZXMgdGhlIGNvbnN0cnVjdGlvbiBvZiBhIHJlZ3Jlc3Npb24gbW9kZWwuCgpgYGB7cn0KbW9kZWwgPC0gbG0oY2hhcmdlcyB+IGFnZSArIHNleCArIGJtaSArIGNoaWxkcmVuICsgc21va2VyICsgbm9ydGhlYXN0ICsgbm9ydGh3ZXN0ICsgc291dGhlYXN0LCAKICAgICAgICAgICAgZGF0YSA9IGRmKQoKIyMgcHJpbnQgc3VtbWFyeSBvZiB0aGUgbW9kZWwgZm9yIGludmVzdGlnYXRpb24Kc3VtbWFyeShtb2RlbCkKYGBgCgpJbnRlcnByZXRpbmcgdGhlIG91dHB1dCBvZiB0aGlzIHJlZ3Jlc3Npb24gbW9kZWwgaW52b2x2ZXMgZXhhbWluaW5nIHRoZSBlc3RpbWF0ZWQgY29lZmZpY2llbnRzLCBhc3Nlc3NpbmcgdGhlaXIgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlLCBhbmQgZXZhbHVhdGluZyBjb25maWRlbmNlIGludGVydmFscy4gTW9kZWwgZGlhZ25vc3RpY3MgYXJlIGFsc28gbmVjZXNzYXJ5IHRvIGlkZW50aWZ5IHBvdGVudGlhbCB2aW9sYXRpb25zIG9mIHJlZ3Jlc3Npb24gYXNzdW1wdGlvbnMuCgojIyMjIEJhY2t3YXJkIEZlYXR1cmUgRWxpbWluYXRpb24KCkFueSBjb2VmZmljaWVudHMgdGhhdCBhcmUgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsICppLmUuKiwgaGF2ZSBhICpwKi12YWx1ZSBvZiBtb3JlIHRoYW4gMC4wNSwgc2hvdWxkIGJlIGVsaW1pbmF0ZWQuIFRoZSB0dXRvcmlhbCBiZWxvdyBleHBsYWlucyB0aGlzIGFwcHJvYWNoIG9mIGJhY2t3YXJkIGVsaW1pbmF0aW9uIGJhc2VkIG9uICpwKi12YWx1ZXMuIFRoaXMgbWV0aG9kIGl0ZXJhdGl2ZWx5IHJlbW92ZXMgdGhlIGxlYXN0IHNpZ25pZmljYW50IHByZWRpY3RvciB2YXJpYWJsZSwgKmkuZS4qLCB0aGUgdmFyaWFibGUgd2l0aCB0aGUgaGlnaGVzdCAqcCotdmFsdWUsIHVudGlsIGFsbCByZW1haW5pbmcgdmFyaWFibGVzIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LgoKPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC9IUDNSaGpMaFJqWT9zaT02R2xvYk5zSWZjMTVoNGRBIiB0aXRsZT0iWW91VHViZSB2aWRlbyBwbGF5ZXIiIGZyYW1lYm9yZGVyPSIwIiBhbGxvdz0iYWNjZWxlcm9tZXRlcjsgYXV0b3BsYXk7IGNsaXBib2FyZC13cml0ZTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZTsgd2ViLXNoYXJlIiByZWZlcnJlcnBvbGljeT0ic3RyaWN0LW9yaWdpbi13aGVuLWNyb3NzLW9yaWdpbiIgYWxsb3dmdWxsc2NyZWVuIGRhdGEtZXh0ZXJuYWw9IjEiPgoKPC9pZnJhbWU+CgoqKlN0ZXAtYnktU3RlcCBFeHBsYW5hdGlvbioqCgoxLiAgKipGaXQgdGhlIEZ1bGwgTW9kZWwqKjogU3RhcnQgd2l0aCBhbGwgcHJlZGljdG9yIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWwuCjIuICAqKklkZW50aWZ5IHRoZSBMZWFzdCBTaWduaWZpY2FudCBQcmVkaWN0b3IqKjogRmluZCB0aGUgcHJlZGljdG9yIHZhcmlhYmxlIHdpdGggdGhlIGhpZ2hlc3QgKnAqLXZhbHVlCjMuICAqKlJlbW92ZSB0aGUgTGVhc3QgU2lnbmlmaWNhbnQgUHJlZGljdG9yKio6IElmIHRoZSBoaWdoZXN0ICpwKi12YWx1ZSBpcyBhYm92ZSBhIGNlcnRhaW4gdGhyZXNob2xkIChjb21tb25seSAwLjA1KSwgcmVtb3ZlIHRoZSBjb3JyZXNwb25kaW5nIHByZWRpY3RvciB2YXJpYWJsZS4KNC4gICoqUmVmaXQgdGhlIE1vZGVsKio6IEZpdCB0aGUgbW9kZWwgYWdhaW4gd2l0aG91dCB0aGUgcmVtb3ZlZCBwcmVkaWN0b3IuCjUuICAqKlJlcGVhdCoqOiBDb250aW51ZSB0aGUgcHJvY2VzcyB1bnRpbCBhbGwgcmVtYWluaW5nIHByZWRpY3RvciB2YXJpYWJsZXMgaGF2ZSAqcCotdmFsdWVzIGJlbG93IHRoZSB0aHJlc2hvbGQuCgpBIGZldyB0aXBzIHRvIGtlZXAgaW4gbWluZDoKCi0gICBpZ25vcmUgdGhlICpwKi12YWx1ZSBvZiB0aGUgaW50ZXJjZXB0Ci0gICBlaXRoZXIgaW5jbHVkZSBhbGwgb3Igbm9uZSBvZiB0aGUgY29sdW1ucyB0aGF0IGVuY29kZSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB1c2luZyBkdW1teSBjb2RlcywgZXZlbiBpZiBzb21lIGhhdmUgYSAqcCotdmFsdWUgZ3JlYXRlciB0aGFuIDAuMDUKCiMjIyA5LiBDaGVjayBOb3JtYWxpdHkgb2YgUmVzaWR1YWxzCgpXaGlsZSBpdCBpcyBub3Qgc3RyaWN0bHkgbmVjZXNzYXJ5IGZvciBudW1lcmljIGZlYXR1cmVzIChpbmRlcGVuZGVudCB2YXJpYWJsZXMpIHRvIGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdoZW4gYnVpbGRpbmcgYSByZWdyZXNzaW9uIG1vZGVsLCB0aGVyZSBhcmUgbmV2ZXJ0aGVsZXNzIHNvbWUgaW1wb3J0YW50IHN0YXRpc3RpY2FsIGNvbnNpZGVyYXRpb25zLiBTcGVjaWZpY2FsbHksIHJlZ3Jlc3Npb24gbW9kZWxzIGVzdGltYXRlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB2YXJpYWJsZXMsIGFuZCB0aGVzZSByZWxhdGlvbnNoaXBzIGNhbiBiZSB2YWxpZCBldmVuIHdoZW4gdGhlIHByZWRpY3RvcnMgYXJlIG5vdCBmdWxseSBub3JtYWxseSBkaXN0cmlidXRlZC4gSG93ZXZlciwgaWYgcHJlZGljdG9ycyBhcmUgaGlnaGx5IHNrZXdlZCBvciBoYXZlIG91dGxpZXJzLCB0cmFuc2Zvcm1hdGlvbnMgKCplLmcuKiwgbG9nLCBzcXVhcmUgcm9vdCkgbWF5IGltcHJvdmUgbW9kZWwgcGVyZm9ybWFuY2UuCgpXaGF0IGRvZXMgbWF0dGVyIGlzIHRoZSAqZGlzdHJpYnV0aW9uIG9mIHJlc2lkdWFscyogKGVycm9ycykgcmF0aGVyIHRoYW4gdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcy4gSW4gb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcyAoT0xTKSByZWdyZXNzaW9uLCBhbiBhc3N1bXB0aW9uIGlzIHRoYXQgcmVzaWR1YWxzIChkaWZmZXJlbmNlcyBiZXR3ZWVuIG9ic2VydmVkIGFuZCBwcmVkaWN0ZWQgdmFsdWVzKSBzaG91bGQgYmUgKipub3JtYWxseSBkaXN0cmlidXRlZCoqIGZvciB2YWxpZCBoeXBvdGhlc2lzIHRlc3RpbmcgKCplLmcuKiwgdC10ZXN0cywgY29uZmlkZW5jZSBpbnRlcnZhbHMsICpwKi12YWx1ZXMpLiBJZiByZXNpZHVhbHMgYXJlIG5vbi1ub3JtYWxseSBkaXN0cmlidXRlZCwgaXQgbWF5IGluZGljYXRlICpoZXRlcm9zY2VkYXN0aWNpdHkgKG5vbi1jb25zdGFudCB2YXJpYW5jZSksIG9taXR0ZWQgdmFyaWFibGVzLCBvciBtb2RlbCBtaXNzcGVjaWZpY2F0aW9uKi4KClRvIGNoZWNrIGlmIHJlc2lkdWFscyBhcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHlvdSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgc3RyYXRlZ2llcyBmb3IgYSBwYXJ0aWFsIG1vZGVsIHRoYXQgc2ltcGxpZmllcyB0aGUgZXhwbGFuYXRpb246CgpgYGB7cn0KIyBGaXQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwKbW9kZWwgPC0gbG0oY2hhcmdlcyB+IGFnZSArIGJtaSArIGNoaWxkcmVuICsgc21va2VyLCAKICAgICAgICAgICAgZGF0YSA9IGRmKQpgYGAKCkEgaGlzdG9ncmFtIG9mIHRoZSByZXNpZHVhbHMgc2hvdWxkIGxvb2sgc29tZSAibm9ybWFsIiwgKmkuZS4qLCBmb2xsb3cgYSBHYXVzc2lhbiBEaXN0cmlidXRpb24gb3IgQmVsbCBDdXJ2ZS4KCmBgYHtyfQojIENoZWNrIHJlc2lkdWFsIG5vcm1hbGl0eSB3aXRoIGEgaGlzdG9ncmFtCmhpc3QocmVzaWQobW9kZWwpLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBSZXNpZHVhbHMiLCBjb2wgPSAibGlnaHRibHVlIikKYGBgCgpUaGVyZSBpcyBzb21lIHNrZXduZXNzIHRvIHRoaXMgZGlzdHJpYnV0aW9uIGFuZCB0cmFuc2Zvcm1pbmcgdGhlIHRhcmdldCBmZWF0dXJlIHVzaW5nIGEgbG9nIHRyYW5zZm9ybSBtaWdodCBiZSBhcHByb3ByaWF0ZS4KCkFuIGFsdGVybmF0aXZlIGlzIGEgUS1RIFBsb3Qgd2hpY2ggc2hvdWxkIGJlIGEgbGluZSBhdCBhIDQ1wrAgYW5nbGUuIElmIGl0IGlzIG1vcmUgbGlrZSBhICJob2NrZXkgc3RpY2siIHRoZW4gdGhlIGRpc3RyaWJ1dGlvbiBpcyBub3Qgbm9ybWFsLgoKYGBge3J9CiMgUS1RIHBsb3QgZm9yIG5vcm1hbGl0eSBjaGVjawpxcW5vcm0ocmVzaWQobW9kZWwpKQpxcWxpbmUocmVzaWQobW9kZWwpLCBjb2wgPSAicmVkIikKYGBgCgpBbiBhbHRlcm5hdGl2ZSB0byBhIHZpc3VhbCBpbnNwZWN0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHJlc2lkdWFscyBpcyB0aGUgU2hhcGlyby1XaWxrIHN0YXRpc3RpY2FsIHRlc3Qgb2Ygbm9ybWFsaXR5LiBUaGUgdHdvIGtleSB2YWx1ZXMgaW4gdGhlIHJlc3VsdCBhcmUgdGhlICpXKi1zdGF0aXN0aWMgKFRlc3QgU3RhdGlzdGljKSBhbmQgdGhlICpwKi12YWx1ZS4gQSB2YWx1ZSBmb3IgKlcqIGNsb3NlIHRvIDEgc3VnZ2VzdHMgbm9ybWFsaXR5LCB3aGlsZSBhIHZhbHVlIGZhciBmcm9tIDEgc3VnZ2VzdHMgZGV2aWF0aW9uIGZyb20gbm9ybWFsaXR5LiBJZiB0aGUgKnAqLXZhbHVlIGlzIGdyZWF0ZXIgdGhhbiAwLjA1IHRoZW4gd2UgZmFpbCB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcywgd2hpY2ggbWVhbnMgdGhhdCB0aGUgZGF0YSBpcyBub3JtYWxseSBkaXN0cmlidXRlZC4gTGlrZXdpc2UsIGEgKnAqLXZhbHVlIGxlc3MgdGhhbiAwLjA1IGltcGxpZXMgdGhhdCB3ZSBtdXN0IHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGFuZCBjb25zZXF1ZW50bHkgdGhlIGRhdGEgaXMgKipub3QqKiBub3JtYWxseSBkaXN0cmlidXRlZC4KCmBgYHtyfQojIFNoYXBpcm8tV2lsayB0ZXN0IGZvciBub3JtYWxpdHkKc2hhcGlyby50ZXN0KHJlc2lkKG1vZGVsKSkKYGBgCgpJbiB0aGUgYWJvdmUgcmVzdWx0LCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSByZXNpZHVhbHMgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGlmIHdlIGJhc2Ugb3VyIGRldGVybWluYXRpb24gb24gdGhlICpwKiB2YWx1ZSBhbG9uZS4gSG93ZXZlciwgdGhlIHBsb3RzIGFyZSBzb21ld2hhdCByZWFzb25hYmxlIGFuZCB0aGUgKlcqIHN0YXRpc3RpYyBpbmRpY2F0ZXMgbm9ybWFsaXR5LCBzbyB3aGlsZSBub3QgcGVyZmVjdCwgd2UgY2FuIGFjY2VwdCB0aGUgcmVncmVzc2lvbiBtb2RlbC4KCiMjIyA5LiBFeHByZXNzIFJlZ3Jlc3Npb24gRXF1YXRpb24KClRoZSByZWdyZXNzaW9uIGVxdWF0aW9uIGlzIGRlcml2ZWQgdXNpbmcgdGhlIGVzdGltYXRlZCBjb2VmZmljaWVudHMgZnJvbSB0aGUgbW9kZWwuIFRoZSBnZW5lcmFsIGZvcm0gb2YgYSBtdWx0aXBsZSBsaW5lYXIgcmVncmVzc2lvbiBlcXVhdGlvbiBpczoKCiQkClxoYXR7WX0gPSBcYmV0YV8wICsgXGJldGFfMSBYXzEgKyBcYmV0YV8yIFhfMiArIFxiZXRhXzMgWF8zCiQkCgpBc3N1bWUgd2UgaGF2ZSBjb25zdHJ1Y3RlZCBhIHJlZ3Jlc3Npb24gbW9kZWwgYW5kIHVzZWQgYHN1bW1hcnkoKWAgdG8gb2J0YWluIHRoZSBmb2xsb3dpbmcgY29lZmZpY2llbnRzLgoKYGBgICAgICAgICAgCkNvZWZmaWNpZW50czoKICAgICAgICAgICAgIEVzdGltYXRlIFN0ZC4gRXJyb3IgdCB2YWx1ZSBQcig+fHR8KSAgICAKKEludGVyY2VwdCkgLTEyMTY5LjY0ICAgICA5NDIuODQgLTEyLjkwNyAgPCAyZS0xNiAqKioKYWdlICAgICAgICAgICAgMjU3Ljg1ICAgICAgMTEuODkgIDIxLjY4OCAgPCAyZS0xNiAqKioKYm1pICAgICAgICAgICAgMzIzLjc4ICAgICAgMjcuMzkgIDExLjgyMyAgPCAyZS0xNiAqKioKY2hpbGRyZW4gICAgICAgNDc2LjIzICAgICAxMzcuNzIgICAzLjQ1OCAwLjAwMDU2MSAqKioKc21va2VyICAgICAgIDIzODE2LjAwICAgICA0MTEuMDAgIDU3Ljk0NyAgPCAyZS0xNiAqKioKYGBgCgpXZSBjYW4gdGhlbiB3cml0ZSBhbiBlcXVhdGlvbiB3aGljaCBiZWNvbWVzIG91dCBkZXBsb3lhYmxlIG1vZGVsICh3aXRoIHNvbWUgcm91bmRpbmcgZm9yIGRpc3BsYXkgY2xhcml0eSBhbmQgd3JpdGluZyB0aGUgaW50ZXJjZXB0IGF0IHRoZSBlbmQgcmF0aGVyIHRoYW4gdGhlIGJlZ2lubmluZyk6CgokJApcaGF0e1x0ZXh0e2NoYXJnZXN9fSA9ICgyNTggXHRpbWVzIFx0ZXh0e2FnZX0pICsgKDMyNCBcdGltZXMgXHRleHR7Ym1pfSkgKyAoNDc2IFx0aW1lcyBcdGV4dHtjaGlsZHJlbn0pICsgKDIzODE2IFx0aW1lcyBcdGV4dHtzbW9rZXJ9KSAtIDEyMTcwKQokJAoKVGhpcyBlcXVhdGlvbiBwcmVkaWN0cyB0aGUgZXhwZWN0ZWQgbWVkaWNhbCBjaGFyZ2VzIGJhc2VkIG9uIGFnZSwgQk1JLCBudW1iZXIgb2YgY2hpbGRyZW4sIGFuZCB3aGV0aGVyIHRoZSBwZXJzb24gaXMgYSBzbW9rZXIgb3Igbm90LiBJdCBpcyBhIHNpbXBsZSBlcXVhdGlvbiB0aGF0IGNhbiBiZSBhZGRlZCB0byB3ZWIgYXBwbGljYXRpb25zLCBzcHJlYWRzaGVldCBtb2RlbHMsIG9yIGNhbGN1bGF0ZWQgYnkgaGFuZC4gTWFraW5nIGEgcHJlZGljdGlvbiBpcyBleHRyZW1lbHkgZmFzdCBhcyBpdCBqdXN0IGludm9sdmVzIHNpbXBsZSBhZGRpdGlvbiBhbmQgbXVsdGlwbGljYXRpb24uCgpOb3RlIGhvdyBlYXNpbHkgdGhlIG1vZGVsIGNhbiBiZSBpbnRlcnByZXRlZC4gRm9yIGV4YW1wbGUsIGJlaW5nIGEgc21va2VyICgxKSBhZGRzIGFuIGFkZGl0aW9uYWwgXCQyMyw4MTYgdG8gdGhlIG1lZGljYWwgY2hhcmdlcywgd2hpbGUgZWFjaCBjaGlsZCBhY2NvdW50cyBmb3IgYW4gYWRkaXRpb25hbCBcJDQ3Ni4gRWFjaCBwb2ludCB0aGF0IHRoZSBCTUkgZ29lcyB1cCwgaW5jcmVhc2VzIG1lZGljYWwgZXhwZW5zZXMgYnkgXCQzMjQuCgojIyMgMTAuIE1ha2UgUHJlZGljdGlvbgoKT25jZSB0aGUgbGluZWFyIG1vZGVsIChgbW9kZWxgKSBoYXMgYmVlbiB0cmFpbmVkIHVzaW5nIHRoZSBgbG0oKWAgZnVuY3Rpb24sIHByZWRpY3Rpb25zIGNhbiBiZSBtYWRlIHVzaW5nIHRoZSBgcHJlZGljdCgpYCBmdW5jdGlvbiBpbiBSIG9yIHNpbXBseSB1c2luZyB0aGUgcmVncmVzc2lvbiBlcXVhdGlvbi4KClRvIHByZWRpY3QgKipjaGFyZ2VzKiogZm9yIGEgcGF0aWVudCB3aXRoIHNwZWNpZmljIHZhbHVlcyBvZiAqYWdlKiwgKmJtaSosICpjaGlsZHJlbiosIGFuZCAqc21va2VyKiwgd2UgY3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgY29udGFpbmluZyB0aGUgaW5wdXQgdmFsdWVzIGFuZCB1c2UgdGhlIGBwcmVkaWN0KClgIGZ1bmN0aW9uLgoKYGBge3J9CiMgRGVmaW5lIHRoZSBuZXcgb2JzZXJ2YXRpb24gYXMgYSBkYXRhIGZyYW1lCm5ldy5kYXRhIDwtIGRhdGEuZnJhbWUoCiAgYWdlID0gNDcsCiAgYm1pID0gMzEsCiAgY2hpbGRyZW4gPSAyLAogIHNtb2tlciA9IDAKKQoKIyB1c2UgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgdG8gcHJlZGljdCBtZWRpY2FsIGV4cGVuc2VzCnByZWRpY3Rpb24gPC0gcHJlZGljdChtb2RlbCwgbmV3LmRhdGEpCmBgYAoKVGhlIHByZWRpY3RlZCBtZWRpY2FsIGNoYXJnZXMgb3IgZXhwZW5zZXMgYXJlIFwkYHIgcm91bmQocHJlZGljdGlvbiwwKWAuCgpJZiB5b3Ugd2FudCB0byBwcmVkaWN0IGZvciBtdWx0aXBsZSBjb21wYW5pZXMgYXQgb25jZSwgY3JlYXRlIGEgZGF0YSBmcmFtZSBjb250YWluaW5nIG11bHRpcGxlIHJvd3MuCgpUbyBvYnRhaW4gYSAqKmNvbmZpZGVuY2UgaW50ZXJ2YWwqKiBhcm91bmQgdGhlIHByZWRpY3Rpb24sIHNwZWNpZnkgYGludGVydmFsID0gImNvbmZpZGVuY2UiYC4KCmBgYHtyfQpwcmVkaWN0ZWRfY29uZmlkZW5jZSA8LSBwcmVkaWN0KG1vZGVsLCBuZXcuZGF0YSwgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpCnByaW50KHByZWRpY3RlZF9jb25maWRlbmNlKQpgYGAKClRoaXMgcmV0dXJucyBhICoqbG93ZXIgYm91bmQsIHByZWRpY3RlZCB2YWx1ZSwgYW5kIHVwcGVyIGJvdW5kKiogZm9yIGVhY2ggcHJlZGljdGlvbi4KClByZWRpY3Rpb24gaW50ZXJ2YWxzIHByb3ZpZGUgYSByYW5nZSBpbiB3aGljaCAqKmZ1dHVyZSBvYnNlcnZhdGlvbnMqKiBhcmUgZXhwZWN0ZWQgdG8gZmFsbC4KCmBgYHtyfQpwcmVkaWN0ZWRfcHJlZGljdGlvbiA8LSBwcmVkaWN0KG1vZGVsLCBuZXcuZGF0YSwgaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIpCnByaW50KHByZWRpY3RlZF9wcmVkaWN0aW9uKQpgYGAKClRoaXMgaXMgKip3aWRlciB0aGFuIHRoZSBjb25maWRlbmNlIGludGVydmFsKiogYmVjYXVzZSBpdCBhY2NvdW50cyBmb3IgaW5kaXZpZHVhbCB2YXJpYW5jZSBpbiBmdXR1cmUgb2JzZXJ2YXRpb25zLiBJZiB0aGUgZ29hbCBpcyAqKmZvcmVjYXN0aW5nIG9yIG1ha2luZyBkZWNpc2lvbnMgYmFzZWQgb24gaW5kaXZpZHVhbCBvdXRjb21lcyoqLCB0aGUgKipwcmVkaWN0aW9uIGludGVydmFsIGlzIG1vcmUgdXNlZnVsKiogYmVjYXVzZSBpdCBwcm92aWRlcyBhIHJhbmdlIG9mIGxpa2VseSB2YWx1ZXMgZm9yIGEgbmV3IG9ic2VydmF0aW9uLiBJZiB0aGUgZ29hbCBpcyB0byAqKnVuZGVyc3RhbmQgaG93IHdlbGwgdGhlIG1vZGVsIGVzdGltYXRlcyB0aGUgbWVhbioqLCB0aGUgKipjb25maWRlbmNlIGludGVydmFsKiogaXMgbW9yZSByZWxldmFudC4KCiMjIyAxMS4gRXZhbHVhdGUgUmVncmVzc2lvbiBNb2RlbAoKRXZhbHVhdGluZyB0aGUgcGVyZm9ybWFuY2Ugb2YgYSByZWdyZXNzaW9uIG1vZGVsIGlzIGVzc2VudGlhbCB0byBkZXRlcm1pbmUgaG93IHdlbGwgaXQgZml0cyB0aGUgZGF0YSBhbmQgaG93IHJlbGlhYmxlIGl0cyBwcmVkaWN0aW9ucyBhcmUuIFRoZSDigJxnb29kbmVzc+KAnSBvZiBhIHJlZ3Jlc3Npb24gbW9kZWwgaXMgdHlwaWNhbGx5IGFzc2Vzc2VkIHVzaW5nIHN0YXRpc3RpY2FsIG1lYXN1cmVzLCBkaWFnbm9zdGljIHBsb3RzLCBhbmQgcmVzaWR1YWwgYW5hbHlzaXMuIFRoZSBjaG9pY2Ugb2YgZXZhbHVhdGlvbiBtZXRyaWNzIGRlcGVuZHMgb24gdGhlIG9iamVjdGl2ZSDigJQgd2hldGhlciB0aGUgZ29hbCBpcyB0byBtYXhpbWl6ZSBleHBsYW5hdG9yeSBwb3dlciwgYXNzZXNzIHByZWRpY3RpdmUgYWNjdXJhY3ksIG9yIGRldGVjdCB2aW9sYXRpb25zIG9mIGFzc3VtcHRpb25zLgoKVGhlIG1vc3QgdHdvIGNvbW1vbiBldmFsdWF0aW9uIG1ldHJpY3MgYXJlIHRoZSAkUl4yJCBzdGF0aXN0aWMgYW5kIG1lYW4gc3F1YXJlZCBlcnJvci4KCiMjIyMgQ29lZmZpY2llbnQgb2YgRGV0ZXJtaW5hdGlvbgoKVGhlICRSXjIkIHN0YXRpc3RpYywgYWxzbyBrbm93biBhcyB0aGUgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiwgbWVhc3VyZXMgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgaW4gdGhlIGRlcGVuZGVudCB2YXJpYWJsZSB0aGF0IGlzIGV4cGxhaW5lZCBieSB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCBpcyBtYXRoZW1hdGljYWxseSBkZWZpbmVkIGFzOgoKJCQKUl4yID0gMSAtIFxmcmFje1xzdW0gKFlfaSAtIFxoYXR7WX1faSleMn17XHN1bSAoWV9pIC0gXGJhcntZfSleMn0KJCQKCiRSXjIkIHJhbmdlcyBmcm9tIDAgdG8gMSwgd2hlcmUgYSB2YWx1ZSBjbG9zZXIgdG8gMSBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgZXhwbGFpbnMgbW9zdCBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiBIb3dldmVyLCBhIGhpZ2ggJFJeMiQgZG9lcyBub3QgbmVjZXNzYXJpbHkgbWVhbiB0aGUgbW9kZWwgaXMgZ29vZDsgaXQgZG9lcyBub3QgYWNjb3VudCBmb3Igb3ZlcmZpdHRpbmcuCgpJbiBSLCB3ZSBjYW4gZmluZCB0aGUgJFJeMiQgYW5kIHRoZSBBZGp1c3RlZCAkUl4yJCBhcyBmb2xsb3dzOgoKYGBge3J9Cm1vZGVsIDwtIGxtKGNoYXJnZXMgfiBhZ2UgKyBibWkgKyBzZXggKyBzbW9rZXIsIGRhdGEgPSBkZikKCnIuc3F1YXJlZCA8LSBzdW1tYXJ5KG1vZGVsKSRyLnNxdWFyZWQKYGBgCgpUaGUgJFJeMiQgZm9yIHRoZSBhYm92ZSByZWdyZXNzaW9uIG1vZGVsIGlzIGByIHJvdW5kKHIuc3F1YXJlZCwgMylgLiBBbiAkUl4yJCBhYm92ZSAwLjcgaXMgZ2VuZXJhbGx5IHJlYXNvbmFibGUsIHdoaWxlIGFib3ZlIDAuOCBpcyBnb29kLCBhbmQgYWJvdmUgMC45IGlzIGV4Y2VsbGVudC4gVGhlIG1vZGVsIGFib3ZlIGhhcyBhIHJlYXNvbmFibGUgJFJeMiQgdGhhdCBleHBsYWlucyBgciByb3VuZChyLnNxdWFyZWQsIDEpKjEwMGAlIG9mIHRoZSB2YXJpYW5jZSBvYnNlcnZlZC4KClVubGlrZSAkUl4yJCwgdGhlIGFkanVzdGVkICRSXjIkIGFjY291bnRzIGZvciB0aGUgbnVtYmVyIG9mIHByZWRpY3RvcnMgaW4gdGhlIG1vZGVsIGFuZCBwZW5hbGl6ZXMgdW5uZWNlc3NhcnkgdmFyaWFibGVzLgoKJCQKUl4yX3thZGp9ID0gMSAtIFxsZWZ0KFxmcmFjeygxIC0gUl4yKShuLTEpfXtuLWstMX1ccmlnaHQpCiQkCgp3aGVyZSAkbiQgaXMgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgYW5kICRrJCBpcyB0aGUgbnVtYmVyIG9mIHByZWRpY3RvcnMuIEFkanVzdGVkICRSXjIkIHByZXZlbnRzIHRoZSBpbGx1c2lvbiBvZiBpbXByb3ZlbWVudCB3aGVuIGFkZGluZyBpcnJlbGV2YW50IHByZWRpY3RvcnMuIElmIGFkZGluZyBuZXcgcHJlZGljdG9ycyBjYXVzZXMgQWRqdXN0ZWQgJFJeMiQgdG8gZGVjcmVhc2UsIHRob3NlIHByZWRpY3RvcnMgZG8gbm90IGNvbnRyaWJ1dGUgc2lnbmlmaWNhbnRseSB0byB0aGUgbW9kZWwuIFRoZSBBZGp1c3RlZCAkUl4yJCBjYW4gYmUgcmVhZCB1c2luZyBgc3VtbWFyeShtb2RlbCkkYWRqLnIuc3F1YXJlZGAuCgojIyMjIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKQoKVGhlIG1lYW4gc3F1YXJlZCBlcnJvciBtZWFzdXJlcyB0aGUgYXZlcmFnZSBzcXVhcmVkIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgYWN0dWFsIGFuZCBwcmVkaWN0ZWQgdmFsdWVzLgoKJCQKTVNFID0gXGZyYWN7MX17bn0gXHN1bSAoWV9pIC0gXGhhdHtZfV9pKV4yCiQkCgpBIHNtYWxsZXIgTVNFIGluZGljYXRlcyBhIGJldHRlciBtb2RlbCBmaXQuIEl0IHBlbmFsaXplcyBsYXJnZSBlcnJvcnMgbW9yZSB0aGFuIHNtYWxsIG9uZXMgZHVlIHRvIHNxdWFyaW5nLiBXZSBjYW4gY2FsY3VsYXRlIE1TRSBpbiBSIGFzIGZvbGxvd3M6CgpgYGB7cn0KbW9kZWwgPC0gbG0oY2hhcmdlcyB+IGFnZSArIGJtaSArIHNleCArIHNtb2tlciwgZGF0YSA9IGRmKQoKbXNlIDwtIG1lYW4oKGRmJGNoYXJnZXMgLSBwcmVkaWN0KG1vZGVsKSleMikKcHJpbnQobXNlKQpgYGAKClJvb3QgbWVhbiBzcXVhcmVkIGVycm9yIChSTVNFKSBpcyBzaW1wbHkgdGhlIHNxdWFyZSByb290IG9mIE1TRSwgbWFraW5nIGl0IGludGVycHJldGFibGUgaW4gdGhlIHNhbWUgdW5pdHMgYXMgdGhlIGRlcGVuZGVudCB2YXJpYWJsZS4KCiQkClJNU0UgPSBcc3FydHtNU0V9CiQkCgpBcyBiZWZvcmUsIGEgbG93ZXIgUk1TRSB2YWx1ZXMgaW5kaWNhdGUgYSBiZXR0ZXIgZml0LiBVbmxpa2UgTVNFLCBSTVNFIGlzIGRpcmVjdGx5IGludGVycHJldGFibGUgaW4gdGVybXMgb2YgdGhlIG9yaWdpbmFsIHJlc3BvbnNlIHZhcmlhYmxlLgoKYGBge3J9CnJtc2UgPC0gc3FydChtZWFuKChkZiRjaGFyZ2VzIC0gcHJlZGljdChtb2RlbCkpXjIpKQpwcmludChybXNlKQpgYGAKCkluIGFkZGl0aW9uLCB0byB0aGUgdHdvIG1ldHJpY3Mgc2hvd24gaGVyZSwgb3RoZXIgbWV0cmljcyBpbmNsdWRlIE1lYW4gQWJzb2x1dGUgRGV2aWF0aW9uIChNQUQpIGFuZCBNZWFuIEFic29sdXRlIFBlcmNlbnRhZ2UgRXJyb3IgKE1BUEUpLgoKIyMjIDEyLiBEZXBsb3kgTW9kZWwKCkEgcmVncmVzc2lvbiBtb2RlbCBpcyBvbmUgb2YgdGhlIHNpbXBsZXN0IG1vZGVscyB0byBkZXBsb3kgYW5kIHVzZSB0byBtYWtlIHByZWRpY3Rpb25zIGFzIG9uZSBzaW1wbHkgaGFzIHRvIHByb3ZpZGUgdGhlIHJlZ3Jlc3Npb24gZXF1YXRpb24uCgojIyBSZWdyZXNzaW9uIEFzc3VtcHRpb25zCgpXaGlsZSByZWdyZXNzaW9uIG1vZGVscyAoc3VjaCBhcyBsaW5lYXIgYW5kIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uKSBhcmUgd2lkZWx5IHVzZWQgZm9yIHByZWRpY3RpbmcgbnVtZXJpYyB0YXJnZXQgdmFyaWFibGVzLCB0aGVyZSBhcmUgc2NlbmFyaW9zIHdoZXJlIHRoZXkgbWF5IG5vdCBiZSB0aGUgYmVzdCBjaG9pY2UuIFJlZ3Jlc3Npb24gYXNzdW1lcyBhIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBwcmVkaWN0b3JzIGFuZCB0aGUgdGFyZ2V0IHZhcmlhYmxlLCBidXQgcmVhbC13b3JsZCBkYXRhIG9mdGVuIHZpb2xhdGUgdGhlc2UgYXNzdW1wdGlvbnMuIEJlbG93IGFyZSBrZXkgc2l0dWF0aW9ucyB3aGVyZSByZWdyZXNzaW9uIG1heSBub3QgYmUgYXBwcm9wcmlhdGUsIGFsb25nIHdpdGggYWx0ZXJuYXRpdmUgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgYmV0dGVyIHN1aXRlZCBmb3IgZWFjaCBjYXNlLgoKKioxLiBXaGVuIHRoZSBSZWxhdGlvbnNoaXAgQmV0d2VlbiBQcmVkaWN0b3JzIGFuZCB0aGUgVGFyZ2V0IFZhcmlhYmxlIGlzIE5vbi1MaW5lYXIqKgoKTXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gYXNzdW1lcyB0aGF0IHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgaXMgYSBsaW5lYXIgZnVuY3Rpb24gb2YgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcy4gSWYgdGhlIHRydWUgcmVsYXRpb25zaGlwIGlzIGhpZ2hseSBub24tbGluZWFyLCBhZGRpbmcgcG9seW5vbWlhbCB0ZXJtcyBjYW4gaW1wcm92ZSBmaXQsIGJ1dCBpdCBvZnRlbiBsZWFkcyB0byBvdmVyZml0dGluZyBvciBwb29yIGdlbmVyYWxpemF0aW9uLgoKKioyLiBXaGVuIFRoZXJlIGlzIE11bHRpY29sbGluZWFyaXR5IEFtb25nIEluZGVwZW5kZW50IFZhcmlhYmxlcyoqCgpNdWx0aWNvbGxpbmVhcml0eSBvY2N1cnMgd2hlbiBwcmVkaWN0b3IgdmFyaWFibGVzIGFyZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGVhY2ggb3RoZXIsIG1ha2luZyBjb2VmZmljaWVudCBlc3RpbWF0ZXMgdW5zdGFibGUuIFJlZ3Jlc3Npb24gc3RydWdnbGVzIGJlY2F1c2UgaXQgY2Fubm90IGRpc3Rpbmd1aXNoIGJldHdlZW4gdGhlIGVmZmVjdHMgb2YgY29ycmVsYXRlZCB2YXJpYWJsZXMuCgoqKjMuIFdoZW4gdGhlIERhdGEgQ29udGFpbnMgTWFueSBPdXRsaWVycyoqCgpPcmRpbmFyeSBsZWFzdCBzcXVhcmVzIChPTFMpIHJlZ3Jlc3Npb24gbWluaW1pemVzIHNxdWFyZWQgZXJyb3JzLCBtYWtpbmcgaXQgaGlnaGx5IHNlbnNpdGl2ZSB0byBvdXRsaWVycy4gQSBmZXcgZXh0cmVtZSB2YWx1ZXMgY2FuIGRpc3Byb3BvcnRpb25hdGVseSBpbmZsdWVuY2UgdGhlIG1vZGVsLCBkaXN0b3J0aW5nIHByZWRpY3Rpb25zLgoKKio0LiBXaGVuIHRoZSBEYXRhIGlzIEhpZ2hseSBTa2V3ZWQgb3IgSGFzIEhldGVyb3NjZWRhc3RpY2l0eSoqCgpJZiByZXNpZHVhbCB2YXJpYW5jZSBpcyBub3QgY29uc3RhbnQgKGhldGVyb3NjZWRhc3RpY2l0eSksIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIG1heSBiZSBpbmVmZmljaWVudCwgbGVhZGluZyB0byBpbmNvcnJlY3QgaW5mZXJlbmNlcy4gU2tld2VkIGRpc3RyaWJ1dGlvbnMgY2F1c2UgYmlhc2VkIGNvZWZmaWNpZW50IGVzdGltYXRlcy4gTG9nIFRyYW5zZm9ybWF0aW9uIG9yIEJveC1Db3ggVHJhbnNmb3JtYXRpb24gc2hvdWxkIGJlIHVzZWQgYmVmb3JlIHJlZ3Jlc3Npb24gdG8gc3RhYmlsaXplIHZhcmlhbmNlLgoKKio1LiBXaGVuIFRoZXJlIGFyZSBIaWdoLURpbWVuc2lvbmFsIG9yIFNwYXJzZSBGZWF0dXJlcyoqCgpJZiB0aGVyZSBhcmUgbW9yZSBwcmVkaWN0b3JzIHRoYW4gb2JzZXJ2YXRpb25zICgqZS5nLiosIGdlbmV0aWMgZGF0YSwgdGV4dCBkYXRhKSwgdHJhZGl0aW9uYWwgcmVncmVzc2lvbiBvdmVyZml0cy5NYW55IGZlYXR1cmVzIG1heSBiZSBpcnJlbGV2YW50LCBkaWx1dGluZyB0aGUgcHJlZGljdGl2ZSBwb3dlciBvZiByZWxldmFudCBvbmVzLgoKKio2LiBXaGVuIFRoZXJlIGFyZSBJbnRlcmFjdGlvbnMgb3IgTm9uLUFkZGl0aXZlIEVmZmVjdHMgQmV0d2VlbiBQcmVkaWN0b3JzKioKClRyYWRpdGlvbmFsIHJlZ3Jlc3Npb24gYXNzdW1lcyBpbmRlcGVuZGVudCBlZmZlY3RzIG9mIHByZWRpY3RvcnMgdW5sZXNzIGludGVyYWN0aW9uIHRlcm1zIGFyZSBleHBsaWNpdGx5IGFkZGVkLiBDb21wbGV4IGludGVyYWN0aW9ucyBjYW4gYmUgZGlmZmljdWx0IHRvIHNwZWNpZnkgbWFudWFsbHkuCgoqKjcuIFdoZW4gdGhlIERhdGFzZXQgaXMgTGFyZ2UgYW5kIFJlZ3Jlc3Npb24gQmVjb21lcyBDb21wdXRhdGlvbmFsbHkgSW5lZmZpY2llbnQqKgoKTGluZWFyIHJlZ3Jlc3Npb24gcGVyZm9ybXMgbWF0cml4IG9wZXJhdGlvbnMgdGhhdCBiZWNvbWUgY29tcHV0YXRpb25hbGx5IGV4cGVuc2l2ZSBpbiB2ZXJ5IGxhcmdlIGRhdGFzZXRzLiBDb21wbGV4IHJlbGF0aW9uc2hpcHMgbWF5IG5vdCBiZSBlZmZlY3RpdmVseSBjYXB0dXJlZCB3aXRoIGEgc2ltcGxlIGxpbmVhciBmdW5jdGlvbi4KCioqOC4gV2hlbiB0aGUgRGF0YSBpcyBUZW1wb3JhbCAoVGltZSBTZXJpZXMpIGFuZCBSZWdyZXNzaW9uIElnbm9yZXMgU2VxdWVudGlhbCBEZXBlbmRlbmN5KioKClN0YW5kYXJkIHJlZ3Jlc3Npb24gZG9lcyBub3QgYWNjb3VudCBmb3IgdGltZS1iYXNlZCBwYXR0ZXJucyBzdWNoIGFzIHRyZW5kcywgc2Vhc29uYWxpdHksIG9yIGF1dG9jb3JyZWxhdGlvbi4gUmVzaWR1YWxzIGluIHJlZ3Jlc3Npb24gbW9kZWxzIG1heSBiZSBhdXRvY29ycmVsYXRlZCwgdmlvbGF0aW5nIGFzc3VtcHRpb25zLgoKIyMgU3VtbWFyeQoKTXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gcmVtYWlucyBhIGZ1bmRhbWVudGFsIHRlY2huaXF1ZSBmb3IgcHJlZGljdGl2ZSBtb2RlbGluZyBhY3Jvc3MgdmFyaW91cyBkb21haW5zLCBpbmNsdWRpbmcgZmluYW5jZSBhbmQgaGVhbHRoY2FyZS4gVGhyb3VnaCBpdHMgYWJpbGl0eSB0byBxdWFudGlmeSByZWxhdGlvbnNoaXBzIGJldHdlZW4gbXVsdGlwbGUgcHJlZGljdG9ycyBhbmQgYW4gb3V0Y29tZSwgaXQgcHJvdmlkZXMgdmFsdWFibGUgaW5zaWdodHMgZm9yIGRlY2lzaW9uLW1ha2luZyBhbmQgZm9yZWNhc3RpbmcuIFRoZSBwcmFjdGljYWwgZXhhbXBsZXMgcHJlc2VudGVkIGluIHRoaXMgdHV0b3JpYWwgaWxsdXN0cmF0ZSBob3cgTUxSIGNhbiBiZSBhcHBsaWVkIHRvIHJlYWwtd29ybGQgcHJvYmxlbXMsIGVtcGhhc2l6aW5nIHRoZSBpbXBvcnRhbmNlIG9mIG1vZGVsIGV2YWx1YXRpb24gYW5kIGRpYWdub3N0aWNzLgoKUmVncmVzc2lvbiBtb2RlbHMgd29yayB3ZWxsIHdoZW4gcmVsYXRpb25zaGlwcyBhcmUgKipsaW5lYXIsIGluZGVwZW5kZW50LCBob21vc2NlZGFzdGljLCBhbmQgZnJlZSBmcm9tIHNldmVyZSBtdWx0aWNvbGxpbmVhcml0eSBvciBvdXRsaWVycyoqLiBIb3dldmVyLCB3aGVuIHRoZXNlIGFzc3VtcHRpb25zIGRvIG5vdCBob2xkLCBhbHRlcm5hdGl2ZSBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMgcHJvdmlkZSBiZXR0ZXIgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZSBhbmQgcm9idXN0bmVzcy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRmlsZXMgJiBSZXNvdXJjZXMKCmBgYHtyIHppcEZpbGVzLCBlY2hvPUZBTFNFfQp6aXBOYW1lID0gc3ByaW50ZigiTGVzc29uRmlsZXMtJXMtJXMuemlwIiwgCiAgICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LAogICAgICAgICAgICAgICAgIHBhcmFtcyRudW1iZXIpCgp0ZXh0QUxpbmsgPSBwYXN0ZTAoIkFsbCBGaWxlcyBmb3IgTGVzc29uICIsIAogICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksIi4iLHBhcmFtcyRudW1iZXIpCgojIGRvd25sb2FkRmlsZXNMaW5rKCkgaXMgaW5jbHVkZWQgZnJvbSBfaW5zZXJ0MkRCLlIKa25pdHI6OnJhd19odG1sKGRvd25sb2FkRmlsZXNMaW5rKCIuIiwgemlwTmFtZSwgdGV4dEFMaW5rKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFJlZmVyZW5jZXMKCk5vIHJlZmVyZW5jZXMuCgojIyBFcnJhdGEKCltMZXQgdXMga25vd10oaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tLzIxMjE4NzA3Mjc4NDE1Nyl7dGFyZ2V0PSJfYmxhbmsifS4K