Modified from Simple Linear Regression - An example using R and Multiple Regression Analysis in R - First Steps by Felipe Rego.

Overview

This notebook will utilize the Prestige dataset from the car package library(car) to illustrate the application of linear regression.

The Prestige dataset is a data frame with 102 rows and 6 columns. Each row is an observation that relates to an occupation. The columns relate to predictors such as average years of education, percentage of women in the occupation, prestige of the occupation, etc.

# Load the dataset and utilities libraries
library(car)
library(tidyverse)
library(corrplot)
library(rgl)
library(rglwidget)
library(scatterplot3d)
library(knitr)
# knit_hooks$set(webgl = hook_webgl)
# Inspect the data
head(Prestige)
# View the data structure
str(Prestige)
'data.frame':   102 obs. of  6 variables:
 $ education: num  13.1 12.3 12.8 11.4 14.6 ...
 $ income   : int  12351 25879 9271 8865 8403 11030 8258 14163 11377 11023 ...
 $ women    : num  11.16 4.02 15.7 9.11 11.68 ...
 $ prestige : num  68.8 69.1 63.4 56.8 73.5 77.6 72.6 78.1 73.1 68.8 ...
 $ census   : int  1113 1130 1171 1175 2111 2113 2133 2141 2143 2153 ...
 $ type     : Factor w/ 3 levels "bc","prof","wc": 2 2 2 2 2 2 2 2 2 2 ...
# Summarize the data
summary(Prestige)
   education          income          women           prestige         census    
 Min.   : 6.380   Min.   :  611   Min.   : 0.000   Min.   :14.80   Min.   :1113  
 1st Qu.: 8.445   1st Qu.: 4106   1st Qu.: 3.592   1st Qu.:35.23   1st Qu.:3120  
 Median :10.540   Median : 5930   Median :13.600   Median :43.60   Median :5135  
 Mean   :10.738   Mean   : 6798   Mean   :28.979   Mean   :46.83   Mean   :5402  
 3rd Qu.:12.648   3rd Qu.: 8187   3rd Qu.:52.203   3rd Qu.:59.27   3rd Qu.:8312  
 Max.   :15.970   Max.   :25879   Max.   :97.510   Max.   :87.20   Max.   :9517  
   type   
 bc  :44  
 prof:31  
 wc  :23  
 NA's: 4  
          
          

Simple Linear Regression

The first part of the notebook illustrates a simple linear regression model. Our goal is to predict income using a single predictor education. Education refers to the average number of years of education that exists in each profession.

# Subset the data to capture only `income` and `education`
prestige <- Prestige %>% dplyr::select(income, education)

First, we’ll do some initial exploration of our variables.

We use histogram to inspect how the variables are distributed.

# Histogram of `education`
ggplot(prestige, aes(x = education)) +
  geom_histogram(binwidth = 1) +
  geom_vline(xintercept = mean(prestige$education), 
             color = "indianred") +
  geom_vline(xintercept = median(prestige$education), 
             color = "cornflowerblue")

# Histogram of `income`
ggplot(prestige, aes(x = income)) +
  geom_histogram(binwidth = 1000) +
  geom_vline(xintercept = mean(prestige$income), 
             color = "indianred") +
  geom_vline(xintercept = median(prestige$income), 
             color = "cornflowerblue")

Now we draw a scatterplot to visualize their relationship.

# Scatterplot of `income` and `education`
ggplot(prestige, aes(x = education, y = income)) +
  geom_point()

Each point in the graph represents a profession. Observe how income changes as years of education increases.

Here, we’ll fit a linear regression and see how well this model fits the observed data. In the most simplistic form, the equation we want to solve is:

\[ income = B_0 + B_1 \times education \]

The model will estimate the cooeficients: the intercept (\(B_0\)) and the slope (\(B_1\)). In our case, the intercept is the expected income value for the average number of years of education and the slope is the average increase in income associated with a one-unit increase in years of education. We want our model to fit a line across the observed relationship in a way that the line created is as close as possible to all data points.

Before we fit the model, we create a new variable called education.c. This new variable centers the value of the variable education on its mean. This transformation was applied on the education variable so we could have a meaningful interpretation of its intercept estimate.

Had we not centered Education, we would have gotten a negative intercept estimate from the model and we would have ended up with a nonsensical intercept meaning (essentially saying that for a zero years of education, income is estimated to be negative - or the same as saying that no education means you owe money!)

# Center `education` on its mean
education.c <-  scale(prestige$education, center = TRUE, scale = FALSE)

We use the lm() function to fit our linear model. Here we are using the Least Square approach.

# Fit a linear model and run a summary of its results
set.seed(1)
model <- lm(income ~ education.c, data = prestige)
summary(model)

Call:
lm(formula = income ~ education.c, data = prestige)

Residuals:
    Min      1Q  Median      3Q     Max 
-5493.2 -2433.8   -41.9  1491.5 17713.1 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   6797.9      344.9  19.709  < 2e-16 ***
education.c    898.8      127.0   7.075 2.08e-10 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 3483 on 100 degrees of freedom
Multiple R-squared:  0.3336,    Adjusted R-squared:  0.3269 
F-statistic: 50.06 on 1 and 100 DF,  p-value: 2.079e-10

Let’s examine how the fitted line looks like in a scatterplot.

ggplot(prestige, aes(x = education.c, y = income)) +
  geom_point() + 
  stat_smooth(method = "lm", col = "indianred") + 
  scale_y_continuous(breaks = seq(1000, 25000, by = 2000), minor_breaks = NULL)

From the model output and the scatterplot we can make some interesting observations:

Visually the scatterplot indicates the relationship between income and education does not follow a straight line. While this visual inspection alone is not a sufficient indication of non-linearity, this may suggest the relationship is in fact non-linear. Observe that our fitted line does not seem to follow pattern observed across all points.

While we can see a significant p-value (very close to zero), the model generated does not yield a strong \(R^2\). \(R^2\) (or coefficient of determination) is a measure that indicates the proportional variance of income explained by education. The closer the number is to 1, the better the model explains the variance shown. In our model results, the \(R^2\) we get is 0.33, a pretty low score. This suggests the linear model we just fit in the data is explaining a mere 33% of the variance observed in the data.

Another interesting point from the model output is the residual standard error which measures the average amount of income that will deviate from the true regression line for any given point. In our example, any prediction of income on the basis of education will be off by an average of $3,483! A fairly large number.

Given that the Residual standard error for income is $3483 and the mean income value is $6798, we can assume that the average percentage error for any given point is more than 51%! Again, a pretty large error rate.

To throw some further evidence supporting the lack of model fit, let’s plot the residuals against the predicted values:

plot(model, which = 1, pch = 16)

The graph above shows the model residuals (which is the average amount that the response will deviate from the true regression line) plotted against the fitted values (the model’s predicted value of income).

Ideally, when the model fits the data well, the residuals would be randomly scattered around the horizontal line. In our case here, there is strong evidence a non-linear pattern is present in the relationship. Also, there are points standing far away from the horizontal line. This could indicate the presence of outliers (note how the points for general managers, physicians and lawyers are way out there!).

Multiple Linear Regression

Now we’ll extend the our linear regression model to include multiple predictors.

Our goal is to predict income using 3 predictors: women, prestige, and education. Remember that Education refers to the average number of years of education that exists in each profession. The women variable refers to the percentage of women in the profession and the prestige variable refers to a prestige score for each occupation (given by a metric called Pineo-Porter), from a social survey conducted in the mid-1960s.

# Subset the data to capture only `income`, `women`, `prestige`, `education`
prestige <- Prestige %>% dplyr::select(income, women, prestige, education)
summary(prestige)
     income          women           prestige       education     
 Min.   :  611   Min.   : 0.000   Min.   :14.80   Min.   : 6.380  
 1st Qu.: 4106   1st Qu.: 3.592   1st Qu.:35.23   1st Qu.: 8.445  
 Median : 5930   Median :13.600   Median :43.60   Median :10.540  
 Mean   : 6798   Mean   :28.979   Mean   :46.83   Mean   :10.738  
 3rd Qu.: 8187   3rd Qu.:52.203   3rd Qu.:59.27   3rd Qu.:12.648  
 Max.   :25879   Max.   :97.510   Max.   :87.20   Max.   :15.970  

We first use a matrix scatterplot to visualize the relationship between the variables.

plot(prestige, pch = 16, col = "cornflowerblue")

The matrix plot above allows us to vizualise the relationship among all variables in one single image.

Here we learnt that average years of education increases with average income. Prestige seems to have a similar pattern relative to education when plotted against income. Interestingly, as the percentage of women increases in a profession, average income in the profession declines.

We’ll start by fitting a linear regression on this dataset and see how well it models the observed data. We’ll add all other predictors and give each of them a separate slope coefficient. We want to estimate the relationship and fit a plane (note that in a multi-dimensional setting, with two or more predictors and one response, the least squares regression line becomes a plane) that explains this relationship.

For our multiple linear regression example, we want to solve the following equation:

\[ income = B_0 + B_1 \times education + B_2 \times prestige + B_3 \times prestige \]

The model will estimate the value of the intercept (\(B_0\)) and each predictor’s slope (\(B_1\)) for education, (\(B_2\)) for prestige and (\(B_3\)) for women. The intercept is the average expected income value for the average value across all predictors. The value for each slope estimate will be the average increase in income associated with a one-unit increase in each predictor value, holding the others constant. We want our model to fit a line or plane across the observed relationship in a way that the line/plane created is as close as possible to all data points.

First we create a centered version of all predictor variables each ending with a .c in their names. These new variables were centered on their mean. This transformation was applied on each variable so we could have a meaningful interpretation of the intercept estimates.

# Center all predictors on their means
education.c = scale(prestige$education, center = TRUE, scale = FALSE)
prestige.c = scale(prestige$prestige, center = TRUE, scale = FALSE)
women.c = scale(prestige$women, center = TRUE, scale = FALSE)
# Bind the new variables into a dataframe
centered_predictors <- cbind(education.c, prestige.c, women.c)
prestige <- cbind(prestige, centered_predictors)
names(prestige)[5:7] = c("education.c", "prestige.c", "women.c" )
summary(prestige)
     income          women           prestige       education       education.c    
 Min.   :  611   Min.   : 0.000   Min.   :14.80   Min.   : 6.380   Min.   :-4.358  
 1st Qu.: 4106   1st Qu.: 3.592   1st Qu.:35.23   1st Qu.: 8.445   1st Qu.:-2.293  
 Median : 5930   Median :13.600   Median :43.60   Median :10.540   Median :-0.198  
 Mean   : 6798   Mean   :28.979   Mean   :46.83   Mean   :10.738   Mean   : 0.000  
 3rd Qu.: 8187   3rd Qu.:52.203   3rd Qu.:59.27   3rd Qu.:12.648   3rd Qu.: 1.909  
 Max.   :25879   Max.   :97.510   Max.   :87.20   Max.   :15.970   Max.   : 5.232  
   prestige.c         women.c      
 Min.   :-32.033   Min.   :-28.98  
 1st Qu.:-11.608   1st Qu.:-25.39  
 Median : -3.233   Median :-15.38  
 Mean   :  0.000   Mean   :  0.00  
 3rd Qu.: 12.442   3rd Qu.: 23.22  
 Max.   : 40.367   Max.   : 68.53  

Now we fit the linear regression model.

# Fit a linear model and run a summary of its results
model1 <- lm(income ~ education.c + prestige.c + women.c, data = prestige)
summary(model1)

Call:
lm(formula = income ~ education.c + prestige.c + women.c, data = prestige)

Residuals:
    Min      1Q  Median      3Q     Max 
-7715.3  -929.7  -231.2   689.7 14391.8 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 6797.902    254.934  26.665  < 2e-16 ***
education.c  177.199    187.632   0.944    0.347    
prestige.c   141.435     29.910   4.729 7.58e-06 ***
women.c      -50.896      8.556  -5.948 4.19e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2575 on 98 degrees of freedom
Multiple R-squared:  0.6432,    Adjusted R-squared:  0.6323 
F-statistic: 58.89 on 3 and 98 DF,  p-value: < 2.2e-16

Centering of the predictors on their mean allows us to say that the estimated income is $6,798 when we consider the average number of years of education, the average percent of women and the average prestige from the dataset.

From the model output and the scatterplot we can make some interesting observations:

For any given level of education and prestige in a profession, improving one percentage point of women in a given profession will see the average income decline by $-50.9. Similarly, for any given level of education and percent of women, seeing an improvement in prestige by one point in a given profession will lead to an an extra $141.4 in average income.

Note also our Adjusted R-squared value (we’re now looking at adjusted R-square as a more appropriate metric of variability as the adjusted R-squared increases only if the new term added ends up improving the model more than would be expected by chance). In this model, we arrived in a larger R-squared number of 0.6322843 (compared to roughly 0.37 from our last simple linear regression exercise).

Recall from our previous simple linear regression exmaple that our centered education predictor variable had a significant p-value (close to zero). But from the multiple regression model output above, education no longer displays a significant p-value. Here, education represents the average effect while holding the other variables women and prestige constant. From the matrix scatterplot shown above, we can see the pattern income takes when regressed on education and prestige. Note how closely aligned their pattern is with each other. So in essence, when they are put together in the model, education is no longer significant after adjusting for prestige. When we have two or more predictor variables strongly correlated, we face a problem of collinearity (the predictors are collinear).

Let’s validate this situation with a correlation plot:

prestige_cor <- cor(prestige[1:4])
corrplot(prestige_cor, method = "number")

The correlation matrix shown above highlights the situation we encoutered with the model output. Notice that the correlation between education and prestige is very high at 0.85. This reveals each profession’s level of education is strongly aligned to each profession’s level of prestige. So in essence, education’s high p-value indicates that women and prestige are related to income, but there is no evidence that education is associated with income, at least not when these other two predictors are also considered in the model.

The model output can also help answer whether there is a relationship between the response and the predictors used. We can use the value of our F-Statistic to test whether all our coefficients are equal to zero (testing for the null hypothesis which means). The F-Statistic value from our model is 58.89 on 3 and 98 degrees of freedom. So assuming that the number of data points is appropriate and given that the p-values returned are low, we have some evidence that at least one of the predictors is associated with income.

Given that we have indications that at least one of the predictors is associated with income, and based on the fact that education here has a high p-value, we can consider removing education from the model and see how the model fit changes (we are not going to run a variable selection procedure such as forward, backward or mixed selection in this example):

# Fit a linear model excluding `education`
model2 <- lm(income ~ prestige.c + women.c, data = prestige)
summary(model2)

Call:
lm(formula = income ~ prestige.c + women.c, data = prestige)

Residuals:
    Min      1Q  Median      3Q     Max 
-7620.9 -1008.7  -240.4   873.1 14180.0 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 6797.902    254.795  26.680  < 2e-16 ***
prestige.c   165.875     14.988  11.067  < 2e-16 ***
women.c      -48.385      8.128  -5.953 4.02e-08 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 2573 on 99 degrees of freedom
Multiple R-squared:   0.64, Adjusted R-squared:  0.6327 
F-statistic: 87.98 on 2 and 99 DF,  p-value: < 2.2e-16

The model excluding education has in fact improved our F-Statistic from 58.89 to 87.98 but no substantial improvement was achieved in residual standard error and adjusted R-square value. This is possibly due to the presence of outlier points in the data.

Let’s plot this last model’s residuals:

plot(model2, which = 1, pch = 16)

Let’s visualize a three-dimensional interactive graph with both predictors and the target variable and the linear fit from the last model:

new_data <- expand.grid(prestige.c = seq(-35, 45, by = 5),
                        women.c = seq(-25, 70, by = 5))
new_data$plane <- predict(model2, newdata = new_data)
with(prestige, plot3d(prestige.c, women.c, income, size = 1, type = "s"))
with(new_data, surface3d(unique(prestige.c), unique(women.c), plane,
                      alpha = 0.5, color = "tomato2", 
                      front = "fill", back = "fill"))
rglwidget()
Loading required namespace: rmarkdown

Note from the 3D graph above how this view more clearly highlights the pattern existent across prestige and women relative to income. Also, this interactive view allows us to more clearly see those three or four outlier points as well as how well our last linear model fit the data.

At this stage we could try a few different transformations on both the predictors and the response variable to see how this would improve the model fit. For now, let’s apply a logarithmic transformation with the log function on the income variable (the log function here transforms using the natural log. If base 10 is desired log10 is the function to be used). Also, we could try to square both predictors. Let’s apply these suggested transformations directly into the model function and see what happens with both the model fit and the model accuracy.

# Fit a model excluding `education`,  log the `income`
model3 = lm(log(income) ~ prestige.c + I(prestige.c^2) + women.c + I(women.c^2), 
            data = prestige)
summary(model3)

Call:
lm(formula = log(income) ~ prestige.c + I(prestige.c^2) + women.c + 
    I(women.c^2), data = prestige)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.01614 -0.10973  0.00966  0.14479  0.80844 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)      8.809e+00  5.944e-02 148.188  < 2e-16 ***
prestige.c       2.518e-02  1.787e-03  14.096  < 2e-16 ***
I(prestige.c^2) -2.605e-04  9.396e-05  -2.773  0.00666 ** 
women.c         -6.306e-03  1.476e-03  -4.271 4.53e-05 ***
I(women.c^2)    -7.194e-05  4.014e-05  -1.792  0.07620 .  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.293 on 97 degrees of freedom
Multiple R-squared:  0.7643,    Adjusted R-squared:  0.7546 
F-statistic: 78.64 on 4 and 97 DF,  p-value: < 2.2e-16
# Plot model residuals.
plot(model3, which = 1, pch = 16)

new_data2 <- expand.grid(prestige.c = seq(-35, 45, by = 5),
                        women.c = seq(-25, 70, by = 5))
new_data2$plane <- predict(model3, newdata = new_data2)
with(prestige, plot3d(prestige.c, women.c, log(income), size = 1, type = "s"))
with(new_data2, surface3d(unique(prestige.c), unique(women.c), plane,
                      alpha = 0.5, color = "tomato2", 
                      front = "fill", back = "fill"))
rglwidget()

By transforming both the predictors and the target variable, we achieve an improved model fit. Note how the adjusted R-square has jumped to 0.7545965. Most predictors’ p-values are significant. Here, the squared women.c predictor yields a weak p-value (maybe an indication that in the presence of other predictors, it is not relevant to include and we could exclude it from the model.)

Let’s go on and remove the squared women.c variable from the model to see how it changes:

# Fit a model excluding `education`,  log `income`.
model4 = lm(log(income) ~ prestige.c + I(prestige.c^2) + women.c , data = prestige)
summary(model4)

Call:
lm(formula = log(income) ~ prestige.c + I(prestige.c^2) + women.c, 
    data = prestige)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.12302 -0.09650  0.02764  0.13913  0.78303 

Coefficients:
                  Estimate Std. Error t value Pr(>|t|)    
(Intercept)      8.729e+00  4.023e-02 216.995  < 2e-16 ***
prestige.c       2.499e-02  1.803e-03  13.858  < 2e-16 ***
I(prestige.c^2) -2.351e-04  9.392e-05  -2.503    0.014 *  
women.c         -8.365e-03  9.376e-04  -8.922 2.64e-14 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2963 on 98 degrees of freedom
Multiple R-squared:  0.7565,    Adjusted R-squared:  0.7491 
F-statistic: 101.5 on 3 and 98 DF,  p-value: < 2.2e-16
# Plot model residuals.
plot(model4, which = 1, pch = 16)

new_data3 <- expand.grid(prestige.c = seq(-35, 45, by = 5),
                        women.c = seq(-25, 70, by = 5))
new_data3$plane <- predict(model4, newdata = new_data3)
with(prestige, plot3d(prestige.c, women.c, log(income), size = 1, type = "s"))
with(new_data3, surface3d(unique(prestige.c), unique(women.c), plane,
                      alpha = 0.5, color = "tomato2", 
                      front = "fill", back = "fill"))
rglwidget()

Note now that this updated model yields a much better R-square measure of 0.7490565, with all predictor p-values highly significant and improved F-Statistic value (101.5). The residuals plot also shows a randomly scattered plot indicating a relatively good fit given the transformations applied due to the non-linearity nature of the data.

In summary, we’ve seen a few different multiple linear regression models applied to the Prestige dataset. We tried an linear approach. We created a correlation matrix to understand how each variable was correlated. Subsequently, we transformed the variables to see the effect in the model. We’ve created three-dimensional plots to visualize the relationship of the variables and how the model was fitting the data in hand.

LS0tCnRpdGxlOiAiUHJlc3RpZ2Ug4oCUIExpbmVhciBSZWdyZXNzaW9uIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICBjb2RlX2ZvbGRpbmc6IG51bGwKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgIyBybWFya2Rvd246OmdpdGh1Yl9kb2N1bWVudAotLS0KCioqTW9kaWZpZWQgZnJvbSBbU2ltcGxlIExpbmVhciBSZWdyZXNzaW9uIC0gQW4gZXhhbXBsZSB1c2luZyBSXShodHRwczovL3JwdWJzLmNvbS9GZWxpcGVSZWdvL1NpbXBsZUxpbmVhclJlZ3Jlc3Npb24pIGFuZCBbTXVsdGlwbGUgUmVncmVzc2lvbiBBbmFseXNpcyBpbiBSIC0gRmlyc3QgU3RlcHNdKGh0dHBzOi8vcnB1YnMuY29tL0ZlbGlwZVJlZ28vTXVsdGlwbGVMaW5lYXJSZWdyZXNzaW9uSW5SRmlyc3RTdGVwcykgYnkgRmVsaXBlIFJlZ28uKioKCiMjIE92ZXJ2aWV3CgpUaGlzIG5vdGVib29rIHdpbGwgdXRpbGl6ZSB0aGUgKipQcmVzdGlnZSoqIGRhdGFzZXQgZnJvbSB0aGUgY2FyIHBhY2thZ2UgYGxpYnJhcnkoY2FyKWAgdG8gaWxsdXN0cmF0ZSB0aGUgYXBwbGljYXRpb24gb2YgKipsaW5lYXIgcmVncmVzc2lvbioqLgoKVGhlICoqUHJlc3RpZ2UqKiBkYXRhc2V0IGlzIGEgZGF0YSBmcmFtZSB3aXRoIDEwMiByb3dzIGFuZCA2IGNvbHVtbnMuIEVhY2ggcm93IGlzIGFuIG9ic2VydmF0aW9uIHRoYXQgcmVsYXRlcyB0byBhbiBvY2N1cGF0aW9uLiBUaGUgY29sdW1ucyByZWxhdGUgdG8gcHJlZGljdG9ycyBzdWNoIGFzIGF2ZXJhZ2UgeWVhcnMgb2YgZWR1Y2F0aW9uLCBwZXJjZW50YWdlIG9mIHdvbWVuIGluIHRoZSBvY2N1cGF0aW9uLCBwcmVzdGlnZSBvZiB0aGUgb2NjdXBhdGlvbiwgZXRjLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9CiMgTG9hZCB0aGUgZGF0YXNldCBhbmQgdXRpbGl0aWVzIGxpYnJhcmllcwpsaWJyYXJ5KGNhcikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkocmdsKQpsaWJyYXJ5KHJnbHdpZGdldCkKbGlicmFyeShzY2F0dGVycGxvdDNkKQpsaWJyYXJ5KGtuaXRyKQojIGtuaXRfaG9va3Mkc2V0KHdlYmdsID0gaG9va193ZWJnbCkKYGBgCgpgYGB7cn0KIyBJbnNwZWN0IHRoZSBkYXRhCmhlYWQoUHJlc3RpZ2UpCmBgYAoKYGBge3J9CiMgVmlldyB0aGUgZGF0YSBzdHJ1Y3R1cmUKc3RyKFByZXN0aWdlKQojIFN1bW1hcml6ZSB0aGUgZGF0YQpzdW1tYXJ5KFByZXN0aWdlKQpgYGAKCiMjIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbgoKVGhlIGZpcnN0IHBhcnQgb2YgdGhlIG5vdGVib29rIGlsbHVzdHJhdGVzIGEgKipzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24qKiBtb2RlbC4gT3VyIGdvYWwgaXMgdG8gcHJlZGljdCBgaW5jb21lYCB1c2luZyBhIHNpbmdsZSBwcmVkaWN0b3IgYGVkdWNhdGlvbmAuIEVkdWNhdGlvbiByZWZlcnMgdG8gdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHllYXJzIG9mIGVkdWNhdGlvbiB0aGF0IGV4aXN0cyBpbiBlYWNoIHByb2Zlc3Npb24uCgpgYGB7cn0KIyBTdWJzZXQgdGhlIGRhdGEgdG8gY2FwdHVyZSBvbmx5IGBpbmNvbWVgIGFuZCBgZWR1Y2F0aW9uYApwcmVzdGlnZSA8LSBQcmVzdGlnZSAlPiUgZHBseXI6OnNlbGVjdChpbmNvbWUsIGVkdWNhdGlvbikKYGBgCgpGaXJzdCwgd2UnbGwgZG8gc29tZSBpbml0aWFsIGV4cGxvcmF0aW9uIG9mIG91ciB2YXJpYWJsZXMuIAoKV2UgdXNlIGhpc3RvZ3JhbSB0byBpbnNwZWN0IGhvdyB0aGUgdmFyaWFibGVzIGFyZSBkaXN0cmlidXRlZC4KCmBgYHtyfQojIEhpc3RvZ3JhbSBvZiBgZWR1Y2F0aW9uYApnZ3Bsb3QocHJlc3RpZ2UsIGFlcyh4ID0gZWR1Y2F0aW9uKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4ocHJlc3RpZ2UkZWR1Y2F0aW9uKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJpbmRpYW5yZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVkaWFuKHByZXN0aWdlJGVkdWNhdGlvbiksIAogICAgICAgICAgICAgY29sb3IgPSAiY29ybmZsb3dlcmJsdWUiKQpgYGAKCmBgYHtyfQojIEhpc3RvZ3JhbSBvZiBgaW5jb21lYApnZ3Bsb3QocHJlc3RpZ2UsIGFlcyh4ID0gaW5jb21lKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwMCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4ocHJlc3RpZ2UkaW5jb21lKSwgCiAgICAgICAgICAgICBjb2xvciA9ICJpbmRpYW5yZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVkaWFuKHByZXN0aWdlJGluY29tZSksIAogICAgICAgICAgICAgY29sb3IgPSAiY29ybmZsb3dlcmJsdWUiKQpgYGAKCk5vdyB3ZSBkcmF3IGEgc2NhdHRlcnBsb3QgdG8gdmlzdWFsaXplIHRoZWlyIHJlbGF0aW9uc2hpcC4KCmBgYHtyfQojIFNjYXR0ZXJwbG90IG9mIGBpbmNvbWVgIGFuZCBgZWR1Y2F0aW9uYApnZ3Bsb3QocHJlc3RpZ2UsIGFlcyh4ID0gZWR1Y2F0aW9uLCB5ID0gaW5jb21lKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCkVhY2ggcG9pbnQgaW4gdGhlIGdyYXBoIHJlcHJlc2VudHMgYSBwcm9mZXNzaW9uLiBPYnNlcnZlIGhvdyBpbmNvbWUgY2hhbmdlcyBhcyB5ZWFycyBvZiBlZHVjYXRpb24gaW5jcmVhc2VzLgoKSGVyZSwgd2UnbGwgZml0IGEgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIHNlZSBob3cgd2VsbCB0aGlzIG1vZGVsIGZpdHMgdGhlIG9ic2VydmVkIGRhdGEuIEluIHRoZSBtb3N0IHNpbXBsaXN0aWMgZm9ybSwgdGhlIGVxdWF0aW9uIHdlIHdhbnQgdG8gc29sdmUgaXM6CgpcWyAKaW5jb21lID0gQl8wICsgQl8xIFx0aW1lcyBlZHVjYXRpb24KXF0KClRoZSBtb2RlbCB3aWxsIGVzdGltYXRlIHRoZSBjb29lZmljaWVudHM6IHRoZSAqKmludGVyY2VwdCoqICgkQl8wJCkgYW5kIHRoZSAqKnNsb3BlKiogKCRCXzEkKS4gSW4gb3VyIGNhc2UsIHRoZSBpbnRlcmNlcHQgaXMgdGhlIGV4cGVjdGVkIGluY29tZSB2YWx1ZSBmb3IgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHllYXJzIG9mIGVkdWNhdGlvbiBhbmQgdGhlIHNsb3BlIGlzIHRoZSBhdmVyYWdlIGluY3JlYXNlIGluIGluY29tZSBhc3NvY2lhdGVkIHdpdGggYSBvbmUtdW5pdCBpbmNyZWFzZSBpbiB5ZWFycyBvZiBlZHVjYXRpb24uIFdlIHdhbnQgb3VyIG1vZGVsIHRvIGZpdCBhIGxpbmUgYWNyb3NzIHRoZSBvYnNlcnZlZCByZWxhdGlvbnNoaXAgaW4gYSB3YXkgdGhhdCB0aGUgbGluZSBjcmVhdGVkIGlzIGFzIGNsb3NlIGFzIHBvc3NpYmxlIHRvIGFsbCBkYXRhIHBvaW50cy4KCkJlZm9yZSB3ZSBmaXQgdGhlIG1vZGVsLCB3ZSBjcmVhdGUgYSBuZXcgdmFyaWFibGUgY2FsbGVkIGBlZHVjYXRpb24uY2AuIFRoaXMgbmV3IHZhcmlhYmxlIGNlbnRlcnMgdGhlIHZhbHVlIG9mIHRoZSB2YXJpYWJsZSBlZHVjYXRpb24gb24gaXRzIG1lYW4uIFRoaXMgdHJhbnNmb3JtYXRpb24gd2FzIGFwcGxpZWQgb24gdGhlIGVkdWNhdGlvbiB2YXJpYWJsZSBzbyB3ZSBjb3VsZCBoYXZlIGEgbWVhbmluZ2Z1bCBpbnRlcnByZXRhdGlvbiBvZiBpdHMgaW50ZXJjZXB0IGVzdGltYXRlLgoKSGFkIHdlIG5vdCBjZW50ZXJlZCBFZHVjYXRpb24sIHdlIHdvdWxkIGhhdmUgZ290dGVuIGEgbmVnYXRpdmUgaW50ZXJjZXB0IGVzdGltYXRlIGZyb20gdGhlIG1vZGVsIGFuZCB3ZSB3b3VsZCBoYXZlIGVuZGVkIHVwIHdpdGggYSBub25zZW5zaWNhbCBpbnRlcmNlcHQgbWVhbmluZyAoZXNzZW50aWFsbHkgc2F5aW5nIHRoYXQgZm9yIGEgemVybyB5ZWFycyBvZiBlZHVjYXRpb24sIGluY29tZSBpcyBlc3RpbWF0ZWQgdG8gYmUgbmVnYXRpdmUgLSBvciB0aGUgc2FtZSBhcyBzYXlpbmcgdGhhdCBubyBlZHVjYXRpb24gbWVhbnMgeW91IG93ZSBtb25leSEpCgpgYGB7cn0KIyBDZW50ZXIgYGVkdWNhdGlvbmAgb24gaXRzIG1lYW4KZWR1Y2F0aW9uLmMgPC0gIHNjYWxlKHByZXN0aWdlJGVkdWNhdGlvbiwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBGQUxTRSkKYGBgCgpXZSB1c2UgdGhlIGBsbSgpYCBmdW5jdGlvbiB0byBmaXQgb3VyIGxpbmVhciBtb2RlbC4gSGVyZSB3ZSBhcmUgdXNpbmcgdGhlICoqTGVhc3QgU3F1YXJlKiogYXBwcm9hY2guCgpgYGB7cn0KIyBGaXQgYSBsaW5lYXIgbW9kZWwgYW5kIHJ1biBhIHN1bW1hcnkgb2YgaXRzIHJlc3VsdHMKc2V0LnNlZWQoMSkKbW9kZWwgPC0gbG0oaW5jb21lIH4gZWR1Y2F0aW9uLmMsIGRhdGEgPSBwcmVzdGlnZSkKc3VtbWFyeShtb2RlbCkKYGBgCgpMZXTigJlzIGV4YW1pbmUgaG93IHRoZSBmaXR0ZWQgbGluZSBsb29rcyBsaWtlIGluIGEgc2NhdHRlcnBsb3QuCgpgYGB7cn0KZ2dwbG90KHByZXN0aWdlLCBhZXMoeCA9IGVkdWNhdGlvbi5jLCB5ID0gaW5jb21lKSkgKwogIGdlb21fcG9pbnQoKSArIAogIHN0YXRfc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbCA9ICJpbmRpYW5yZWQiKSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMTAwMCwgMjUwMDAsIGJ5ID0gMjAwMCksIG1pbm9yX2JyZWFrcyA9IE5VTEwpCmBgYAoKRnJvbSB0aGUgbW9kZWwgb3V0cHV0IGFuZCB0aGUgc2NhdHRlcnBsb3Qgd2UgY2FuIG1ha2Ugc29tZSBpbnRlcmVzdGluZyBvYnNlcnZhdGlvbnM6CgpWaXN1YWxseSB0aGUgc2NhdHRlcnBsb3QgaW5kaWNhdGVzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBpbmNvbWUgYW5kIGVkdWNhdGlvbiBkb2VzIG5vdCBmb2xsb3cgYSBzdHJhaWdodCBsaW5lLiBXaGlsZSB0aGlzIHZpc3VhbCBpbnNwZWN0aW9uIGFsb25lIGlzIG5vdCBhIHN1ZmZpY2llbnQgaW5kaWNhdGlvbiBvZiBub24tbGluZWFyaXR5LCB0aGlzIG1heSBzdWdnZXN0IHRoZSByZWxhdGlvbnNoaXAgaXMgaW4gZmFjdCBub24tbGluZWFyLiBPYnNlcnZlIHRoYXQgb3VyIGZpdHRlZCBsaW5lIGRvZXMgbm90IHNlZW0gdG8gZm9sbG93IHBhdHRlcm4gb2JzZXJ2ZWQgYWNyb3NzIGFsbCBwb2ludHMuCgpXaGlsZSB3ZSBjYW4gc2VlIGEgc2lnbmlmaWNhbnQgcC12YWx1ZSAodmVyeSBjbG9zZSB0byB6ZXJvKSwgdGhlIG1vZGVsIGdlbmVyYXRlZCBkb2VzIG5vdCB5aWVsZCBhIHN0cm9uZyAkUl4yJC4gJFJeMiQgKG9yIGNvZWZmaWNpZW50IG9mIGRldGVybWluYXRpb24pIGlzIGEgbWVhc3VyZSB0aGF0IGluZGljYXRlcyB0aGUgcHJvcG9ydGlvbmFsIHZhcmlhbmNlIG9mIGluY29tZSBleHBsYWluZWQgYnkgZWR1Y2F0aW9uLiBUaGUgY2xvc2VyIHRoZSBudW1iZXIgaXMgdG8gMSwgdGhlIGJldHRlciB0aGUgbW9kZWwgZXhwbGFpbnMgdGhlIHZhcmlhbmNlIHNob3duLiBJbiBvdXIgbW9kZWwgcmVzdWx0cywgdGhlICRSXjIkIHdlIGdldCBpcyAwLjMzLCBhIHByZXR0eSBsb3cgc2NvcmUuIFRoaXMgc3VnZ2VzdHMgdGhlIGxpbmVhciBtb2RlbCB3ZSBqdXN0IGZpdCBpbiB0aGUgZGF0YSBpcyBleHBsYWluaW5nIGEgbWVyZSAzMyUgb2YgdGhlIHZhcmlhbmNlIG9ic2VydmVkIGluIHRoZSBkYXRhLgoKQW5vdGhlciBpbnRlcmVzdGluZyBwb2ludCBmcm9tIHRoZSBtb2RlbCBvdXRwdXQgaXMgdGhlIHJlc2lkdWFsIHN0YW5kYXJkIGVycm9yIHdoaWNoIG1lYXN1cmVzIHRoZSBhdmVyYWdlIGFtb3VudCBvZiBpbmNvbWUgdGhhdCB3aWxsIGRldmlhdGUgZnJvbSB0aGUgdHJ1ZSByZWdyZXNzaW9uIGxpbmUgZm9yIGFueSBnaXZlbiBwb2ludC4gSW4gb3VyIGV4YW1wbGUsIGFueSBwcmVkaWN0aW9uIG9mIGluY29tZSBvbiB0aGUgYmFzaXMgb2YgZWR1Y2F0aW9uIHdpbGwgYmUgb2ZmIGJ5IGFuIGF2ZXJhZ2Ugb2YgJDMsNDgzISBBIGZhaXJseSBsYXJnZSBudW1iZXIuCgpHaXZlbiB0aGF0IHRoZSBSZXNpZHVhbCBzdGFuZGFyZCBlcnJvciBmb3IgaW5jb21lIGlzICQzNDgzIGFuZCB0aGUgbWVhbiBpbmNvbWUgdmFsdWUgaXMgJDY3OTgsIHdlIGNhbiBhc3N1bWUgdGhhdCB0aGUgYXZlcmFnZSBwZXJjZW50YWdlIGVycm9yIGZvciBhbnkgZ2l2ZW4gcG9pbnQgaXMgbW9yZSB0aGFuIDUxJSEgQWdhaW4sIGEgcHJldHR5IGxhcmdlIGVycm9yIHJhdGUuCgpUbyB0aHJvdyBzb21lIGZ1cnRoZXIgZXZpZGVuY2Ugc3VwcG9ydGluZyB0aGUgbGFjayBvZiBtb2RlbCBmaXQsIGxldOKAmXMgcGxvdCB0aGUgcmVzaWR1YWxzIGFnYWluc3QgdGhlIHByZWRpY3RlZCB2YWx1ZXM6CgpgYGB7cn0KcGxvdChtb2RlbCwgd2hpY2ggPSAxLCBwY2ggPSAxNikKYGBgCgpUaGUgZ3JhcGggYWJvdmUgc2hvd3MgdGhlIG1vZGVsIHJlc2lkdWFscyAod2hpY2ggaXMgdGhlIGF2ZXJhZ2UgYW1vdW50IHRoYXQgdGhlIHJlc3BvbnNlIHdpbGwgZGV2aWF0ZSBmcm9tIHRoZSB0cnVlIHJlZ3Jlc3Npb24gbGluZSkgcGxvdHRlZCBhZ2FpbnN0IHRoZSBmaXR0ZWQgdmFsdWVzICh0aGUgbW9kZWzigJlzIHByZWRpY3RlZCB2YWx1ZSBvZiBpbmNvbWUpLgoKSWRlYWxseSwgd2hlbiB0aGUgbW9kZWwgZml0cyB0aGUgZGF0YSB3ZWxsLCB0aGUgcmVzaWR1YWxzIHdvdWxkIGJlIHJhbmRvbWx5IHNjYXR0ZXJlZCBhcm91bmQgdGhlIGhvcml6b250YWwgbGluZS4gSW4gb3VyIGNhc2UgaGVyZSwgdGhlcmUgaXMgc3Ryb25nIGV2aWRlbmNlIGEgbm9uLWxpbmVhciBwYXR0ZXJuIGlzIHByZXNlbnQgaW4gdGhlIHJlbGF0aW9uc2hpcC4gQWxzbywgdGhlcmUgYXJlIHBvaW50cyBzdGFuZGluZyBmYXIgYXdheSBmcm9tIHRoZSBob3Jpem9udGFsIGxpbmUuIFRoaXMgY291bGQgaW5kaWNhdGUgdGhlIHByZXNlbmNlIG9mIG91dGxpZXJzIChub3RlIGhvdyB0aGUgcG9pbnRzIGZvciBnZW5lcmFsIG1hbmFnZXJzLCBwaHlzaWNpYW5zIGFuZCBsYXd5ZXJzIGFyZSB3YXkgb3V0IHRoZXJlISkuCgojIyBNdWx0aXBsZSBMaW5lYXIgUmVncmVzc2lvbgoKTm93IHdlJ2xsIGV4dGVuZCB0aGUgb3VyIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIGluY2x1ZGUgbXVsdGlwbGUgcHJlZGljdG9ycy4KCk91ciBnb2FsIGlzIHRvIHByZWRpY3QgYGluY29tZWAgdXNpbmcgMyBwcmVkaWN0b3JzOiBgd29tZW5gLCBgcHJlc3RpZ2VgLCBhbmQgYGVkdWNhdGlvbmAuIFJlbWVtYmVyIHRoYXQgRWR1Y2F0aW9uIHJlZmVycyB0byB0aGUgYXZlcmFnZSBudW1iZXIgb2YgeWVhcnMgb2YgZWR1Y2F0aW9uIHRoYXQgZXhpc3RzIGluIGVhY2ggcHJvZmVzc2lvbi4gVGhlIHdvbWVuIHZhcmlhYmxlIHJlZmVycyB0byB0aGUgcGVyY2VudGFnZSBvZiB3b21lbiBpbiB0aGUgcHJvZmVzc2lvbiBhbmQgdGhlIHByZXN0aWdlIHZhcmlhYmxlIHJlZmVycyB0byBhIHByZXN0aWdlIHNjb3JlIGZvciBlYWNoIG9jY3VwYXRpb24gKGdpdmVuIGJ5IGEgbWV0cmljIGNhbGxlZCBQaW5lby1Qb3J0ZXIpLCBmcm9tIGEgc29jaWFsIHN1cnZleSBjb25kdWN0ZWQgaW4gdGhlIG1pZC0xOTYwcy4KCmBgYHtyfQojIFN1YnNldCB0aGUgZGF0YSB0byBjYXB0dXJlIG9ubHkgYGluY29tZWAsIGB3b21lbmAsIGBwcmVzdGlnZWAsIGBlZHVjYXRpb25gCnByZXN0aWdlIDwtIFByZXN0aWdlICU+JSBkcGx5cjo6c2VsZWN0KGluY29tZSwgd29tZW4sIHByZXN0aWdlLCBlZHVjYXRpb24pCnN1bW1hcnkocHJlc3RpZ2UpCmBgYAoKV2UgZmlyc3QgdXNlIGEgbWF0cml4IHNjYXR0ZXJwbG90IHRvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHZhcmlhYmxlcy4KCmBgYHtyfQpwbG90KHByZXN0aWdlLCBwY2ggPSAxNiwgY29sID0gImNvcm5mbG93ZXJibHVlIikKYGBgCgpUaGUgbWF0cml4IHBsb3QgYWJvdmUgYWxsb3dzIHVzIHRvIHZpenVhbGlzZSB0aGUgcmVsYXRpb25zaGlwIGFtb25nIGFsbCB2YXJpYWJsZXMgaW4gb25lIHNpbmdsZSBpbWFnZS4gCgpIZXJlIHdlIGxlYXJudCB0aGF0IGF2ZXJhZ2UgeWVhcnMgb2YgZWR1Y2F0aW9uIGluY3JlYXNlcyB3aXRoIGF2ZXJhZ2UgaW5jb21lLiBQcmVzdGlnZSBzZWVtcyB0byBoYXZlIGEgc2ltaWxhciBwYXR0ZXJuIHJlbGF0aXZlIHRvIGVkdWNhdGlvbiB3aGVuIHBsb3R0ZWQgYWdhaW5zdCBpbmNvbWUuIEludGVyZXN0aW5nbHksIGFzIHRoZSBwZXJjZW50YWdlIG9mIHdvbWVuIGluY3JlYXNlcyBpbiBhIHByb2Zlc3Npb24sIGF2ZXJhZ2UgaW5jb21lIGluIHRoZSBwcm9mZXNzaW9uIGRlY2xpbmVzLgoKV2XigJlsbCBzdGFydCBieSBmaXR0aW5nIGEgbGluZWFyIHJlZ3Jlc3Npb24gb24gdGhpcyBkYXRhc2V0IGFuZCBzZWUgaG93IHdlbGwgaXQgbW9kZWxzIHRoZSBvYnNlcnZlZCBkYXRhLiBXZeKAmWxsIGFkZCBhbGwgb3RoZXIgcHJlZGljdG9ycyBhbmQgZ2l2ZSBlYWNoIG9mIHRoZW0gYSBzZXBhcmF0ZSBzbG9wZSBjb2VmZmljaWVudC4gV2Ugd2FudCB0byBlc3RpbWF0ZSB0aGUgcmVsYXRpb25zaGlwIGFuZCBmaXQgYSBwbGFuZSAobm90ZSB0aGF0IGluIGEgbXVsdGktZGltZW5zaW9uYWwgc2V0dGluZywgd2l0aCB0d28gb3IgbW9yZSBwcmVkaWN0b3JzIGFuZCBvbmUgcmVzcG9uc2UsIHRoZSBsZWFzdCBzcXVhcmVzIHJlZ3Jlc3Npb24gbGluZSBiZWNvbWVzIGEgcGxhbmUpIHRoYXQgZXhwbGFpbnMgdGhpcyByZWxhdGlvbnNoaXAuCgpGb3Igb3VyIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIGV4YW1wbGUsIHdlIHdhbnQgdG8gc29sdmUgdGhlIGZvbGxvd2luZyBlcXVhdGlvbjoKClxbCmluY29tZSA9IEJfMCArIEJfMSBcdGltZXMgZWR1Y2F0aW9uICsgQl8yIFx0aW1lcyBwcmVzdGlnZSArIEJfMyBcdGltZXMgcHJlc3RpZ2UKXF0KClRoZSBtb2RlbCB3aWxsIGVzdGltYXRlIHRoZSB2YWx1ZSBvZiB0aGUgaW50ZXJjZXB0ICgkQl8wJCkgYW5kIGVhY2ggcHJlZGljdG9y4oCZcyBzbG9wZSAoJEJfMSQpIGZvciBlZHVjYXRpb24sICgkQl8yJCkgZm9yIHByZXN0aWdlIGFuZCAoJEJfMyQpIGZvciB3b21lbi4gVGhlIGludGVyY2VwdCBpcyB0aGUgYXZlcmFnZSBleHBlY3RlZCBpbmNvbWUgdmFsdWUgZm9yIHRoZSBhdmVyYWdlIHZhbHVlIGFjcm9zcyBhbGwgcHJlZGljdG9ycy4gVGhlIHZhbHVlIGZvciBlYWNoIHNsb3BlIGVzdGltYXRlIHdpbGwgYmUgdGhlIGF2ZXJhZ2UgaW5jcmVhc2UgaW4gaW5jb21lIGFzc29jaWF0ZWQgd2l0aCBhIG9uZS11bml0IGluY3JlYXNlIGluIGVhY2ggcHJlZGljdG9yIHZhbHVlLCBob2xkaW5nIHRoZSBvdGhlcnMgY29uc3RhbnQuIFdlIHdhbnQgb3VyIG1vZGVsIHRvIGZpdCBhIGxpbmUgb3IgcGxhbmUgYWNyb3NzIHRoZSBvYnNlcnZlZCByZWxhdGlvbnNoaXAgaW4gYSB3YXkgdGhhdCB0aGUgbGluZS9wbGFuZSBjcmVhdGVkIGlzIGFzIGNsb3NlIGFzIHBvc3NpYmxlIHRvIGFsbCBkYXRhIHBvaW50cy4KCkZpcnN0IHdlIGNyZWF0ZSBhIGNlbnRlcmVkIHZlcnNpb24gb2YgYWxsIHByZWRpY3RvciB2YXJpYWJsZXMgZWFjaCBlbmRpbmcgd2l0aCBhIGAuY2AgaW4gdGhlaXIgbmFtZXMuIFRoZXNlIG5ldyB2YXJpYWJsZXMgd2VyZSBjZW50ZXJlZCBvbiB0aGVpciBtZWFuLiBUaGlzIHRyYW5zZm9ybWF0aW9uIHdhcyBhcHBsaWVkIG9uIGVhY2ggdmFyaWFibGUgc28gd2UgY291bGQgaGF2ZSBhIG1lYW5pbmdmdWwgaW50ZXJwcmV0YXRpb24gb2YgdGhlIGludGVyY2VwdCBlc3RpbWF0ZXMuIAoKYGBge3J9CiMgQ2VudGVyIGFsbCBwcmVkaWN0b3JzIG9uIHRoZWlyIG1lYW5zCmVkdWNhdGlvbi5jID0gc2NhbGUocHJlc3RpZ2UkZWR1Y2F0aW9uLCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IEZBTFNFKQpwcmVzdGlnZS5jID0gc2NhbGUocHJlc3RpZ2UkcHJlc3RpZ2UsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gRkFMU0UpCndvbWVuLmMgPSBzY2FsZShwcmVzdGlnZSR3b21lbiwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBGQUxTRSkKCiMgQmluZCB0aGUgbmV3IHZhcmlhYmxlcyBpbnRvIGEgZGF0YWZyYW1lCmNlbnRlcmVkX3ByZWRpY3RvcnMgPC0gY2JpbmQoZWR1Y2F0aW9uLmMsIHByZXN0aWdlLmMsIHdvbWVuLmMpCnByZXN0aWdlIDwtIGNiaW5kKHByZXN0aWdlLCBjZW50ZXJlZF9wcmVkaWN0b3JzKQpuYW1lcyhwcmVzdGlnZSlbNTo3XSA9IGMoImVkdWNhdGlvbi5jIiwgInByZXN0aWdlLmMiLCAid29tZW4uYyIgKQpzdW1tYXJ5KHByZXN0aWdlKQpgYGAKCk5vdyB3ZSBmaXQgdGhlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLgoKYGBge3J9CiMgRml0IGEgbGluZWFyIG1vZGVsIGFuZCBydW4gYSBzdW1tYXJ5IG9mIGl0cyByZXN1bHRzCm1vZGVsMSA8LSBsbShpbmNvbWUgfiBlZHVjYXRpb24uYyArIHByZXN0aWdlLmMgKyB3b21lbi5jLCBkYXRhID0gcHJlc3RpZ2UpCnN1bW1hcnkobW9kZWwxKQpgYGAKCkNlbnRlcmluZyBvZiB0aGUgcHJlZGljdG9ycyBvbiB0aGVpciBtZWFuIGFsbG93cyB1cyB0byBzYXkgdGhhdCB0aGUgZXN0aW1hdGVkIGluY29tZSBpcyAqKiQ2LDc5OCoqIHdoZW4gd2UgY29uc2lkZXIgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHllYXJzIG9mIGVkdWNhdGlvbiwgdGhlIGF2ZXJhZ2UgcGVyY2VudCBvZiB3b21lbiBhbmQgdGhlIGF2ZXJhZ2UgcHJlc3RpZ2UgZnJvbSB0aGUgZGF0YXNldC4KCkZyb20gdGhlIG1vZGVsIG91dHB1dCBhbmQgdGhlIHNjYXR0ZXJwbG90IHdlIGNhbiBtYWtlIHNvbWUgaW50ZXJlc3Rpbmcgb2JzZXJ2YXRpb25zOgoKRm9yIGFueSBnaXZlbiBsZXZlbCBvZiBlZHVjYXRpb24gYW5kIHByZXN0aWdlIGluIGEgcHJvZmVzc2lvbiwgaW1wcm92aW5nIG9uZSBwZXJjZW50YWdlIHBvaW50IG9mIHdvbWVuIGluIGEgZ2l2ZW4gcHJvZmVzc2lvbiB3aWxsIHNlZSB0aGUgYXZlcmFnZSBpbmNvbWUgZGVjbGluZSBieSAqKiQtNTAuOSoqLiBTaW1pbGFybHksIGZvciBhbnkgZ2l2ZW4gbGV2ZWwgb2YgZWR1Y2F0aW9uIGFuZCBwZXJjZW50IG9mIHdvbWVuLCBzZWVpbmcgYW4gaW1wcm92ZW1lbnQgaW4gcHJlc3RpZ2UgYnkgb25lIHBvaW50IGluIGEgZ2l2ZW4gcHJvZmVzc2lvbiB3aWxsIGxlYWQgdG8gYW4gYW4gZXh0cmEgKiokMTQxLjQqKiBpbiBhdmVyYWdlIGluY29tZS4KCk5vdGUgYWxzbyBvdXIgQWRqdXN0ZWQgUi1zcXVhcmVkIHZhbHVlICh3ZeKAmXJlIG5vdyBsb29raW5nIGF0IGFkanVzdGVkIFItc3F1YXJlIGFzIGEgbW9yZSBhcHByb3ByaWF0ZSBtZXRyaWMgb2YgdmFyaWFiaWxpdHkgYXMgdGhlIGFkanVzdGVkIFItc3F1YXJlZCBpbmNyZWFzZXMgb25seSBpZiB0aGUgbmV3IHRlcm0gYWRkZWQgZW5kcyB1cCBpbXByb3ZpbmcgdGhlIG1vZGVsIG1vcmUgdGhhbiB3b3VsZCBiZSBleHBlY3RlZCBieSBjaGFuY2UpLiBJbiB0aGlzIG1vZGVsLCB3ZSBhcnJpdmVkIGluIGEgbGFyZ2VyIFItc3F1YXJlZCBudW1iZXIgb2YgKiowLjYzMjI4NDMqKiAoY29tcGFyZWQgdG8gcm91Z2hseSAqKjAuMzcqKiBmcm9tIG91ciBsYXN0IHNpbXBsZSBsaW5lYXIgcmVncmVzc2lvbiBleGVyY2lzZSkuCgpSZWNhbGwgZnJvbSBvdXIgcHJldmlvdXMgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGV4bWFwbGUgdGhhdCBvdXIgY2VudGVyZWQgZWR1Y2F0aW9uIHByZWRpY3RvciB2YXJpYWJsZSBoYWQgYSBzaWduaWZpY2FudCBwLXZhbHVlIChjbG9zZSB0byB6ZXJvKS4gQnV0IGZyb20gdGhlIG11bHRpcGxlIHJlZ3Jlc3Npb24gbW9kZWwgb3V0cHV0IGFib3ZlLCBlZHVjYXRpb24gbm8gbG9uZ2VyIGRpc3BsYXlzIGEgc2lnbmlmaWNhbnQgcC12YWx1ZS4gSGVyZSwgZWR1Y2F0aW9uIHJlcHJlc2VudHMgdGhlIGF2ZXJhZ2UgZWZmZWN0IHdoaWxlIGhvbGRpbmcgdGhlIG90aGVyIHZhcmlhYmxlcyB3b21lbiBhbmQgcHJlc3RpZ2UgY29uc3RhbnQuIEZyb20gdGhlIG1hdHJpeCBzY2F0dGVycGxvdCBzaG93biBhYm92ZSwgd2UgY2FuIHNlZSB0aGUgcGF0dGVybiBpbmNvbWUgdGFrZXMgd2hlbiByZWdyZXNzZWQgb24gZWR1Y2F0aW9uIGFuZCBwcmVzdGlnZS4gTm90ZSBob3cgY2xvc2VseSBhbGlnbmVkIHRoZWlyIHBhdHRlcm4gaXMgd2l0aCBlYWNoIG90aGVyLiBTbyBpbiBlc3NlbmNlLCB3aGVuIHRoZXkgYXJlIHB1dCB0b2dldGhlciBpbiB0aGUgbW9kZWwsIGVkdWNhdGlvbiBpcyBubyBsb25nZXIgc2lnbmlmaWNhbnQgYWZ0ZXIgYWRqdXN0aW5nIGZvciBwcmVzdGlnZS4gV2hlbiB3ZSBoYXZlIHR3byBvciBtb3JlIHByZWRpY3RvciB2YXJpYWJsZXMgc3Ryb25nbHkgY29ycmVsYXRlZCwgd2UgZmFjZSBhIHByb2JsZW0gb2YgY29sbGluZWFyaXR5ICh0aGUgcHJlZGljdG9ycyBhcmUgY29sbGluZWFyKS4KCkxldOKAmXMgdmFsaWRhdGUgdGhpcyBzaXR1YXRpb24gd2l0aCBhIGNvcnJlbGF0aW9uIHBsb3Q6CgpgYGB7cn0KcHJlc3RpZ2VfY29yIDwtIGNvcihwcmVzdGlnZVsxOjRdKQpjb3JycGxvdChwcmVzdGlnZV9jb3IsIG1ldGhvZCA9ICJudW1iZXIiKQpgYGAKClRoZSBjb3JyZWxhdGlvbiBtYXRyaXggc2hvd24gYWJvdmUgaGlnaGxpZ2h0cyB0aGUgc2l0dWF0aW9uIHdlIGVuY291dGVyZWQgd2l0aCB0aGUgbW9kZWwgb3V0cHV0LiBOb3RpY2UgdGhhdCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBlZHVjYXRpb24gYW5kIHByZXN0aWdlIGlzIHZlcnkgaGlnaCBhdCAqKjAuODUqKi4gVGhpcyByZXZlYWxzIGVhY2ggcHJvZmVzc2lvbuKAmXMgbGV2ZWwgb2YgZWR1Y2F0aW9uIGlzIHN0cm9uZ2x5IGFsaWduZWQgdG8gZWFjaCBwcm9mZXNzaW9u4oCZcyBsZXZlbCBvZiBwcmVzdGlnZS4gU28gaW4gZXNzZW5jZSwgZWR1Y2F0aW9u4oCZcyBoaWdoIHAtdmFsdWUgaW5kaWNhdGVzIHRoYXQgd29tZW4gYW5kIHByZXN0aWdlIGFyZSByZWxhdGVkIHRvIGluY29tZSwgYnV0IHRoZXJlIGlzIG5vIGV2aWRlbmNlIHRoYXQgZWR1Y2F0aW9uIGlzIGFzc29jaWF0ZWQgd2l0aCBpbmNvbWUsIGF0IGxlYXN0IG5vdCB3aGVuIHRoZXNlIG90aGVyIHR3byBwcmVkaWN0b3JzIGFyZSBhbHNvIGNvbnNpZGVyZWQgaW4gdGhlIG1vZGVsLgoKVGhlIG1vZGVsIG91dHB1dCBjYW4gYWxzbyBoZWxwIGFuc3dlciB3aGV0aGVyIHRoZXJlIGlzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHJlc3BvbnNlIGFuZCB0aGUgcHJlZGljdG9ycyB1c2VkLiBXZSBjYW4gdXNlIHRoZSB2YWx1ZSBvZiBvdXIgRi1TdGF0aXN0aWMgdG8gdGVzdCB3aGV0aGVyIGFsbCBvdXIgY29lZmZpY2llbnRzIGFyZSBlcXVhbCB0byB6ZXJvICh0ZXN0aW5nIGZvciB0aGUgbnVsbCBoeXBvdGhlc2lzIHdoaWNoIG1lYW5zKS4gVGhlIEYtU3RhdGlzdGljIHZhbHVlIGZyb20gb3VyIG1vZGVsIGlzICoqNTguODkqKiBvbiAzIGFuZCA5OCBkZWdyZWVzIG9mIGZyZWVkb20uIFNvIGFzc3VtaW5nIHRoYXQgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyBpcyBhcHByb3ByaWF0ZSBhbmQgZ2l2ZW4gdGhhdCB0aGUgcC12YWx1ZXMgcmV0dXJuZWQgYXJlIGxvdywgd2UgaGF2ZSBzb21lIGV2aWRlbmNlIHRoYXQgYXQgbGVhc3Qgb25lIG9mIHRoZSBwcmVkaWN0b3JzIGlzIGFzc29jaWF0ZWQgd2l0aCBpbmNvbWUuCgpHaXZlbiB0aGF0IHdlIGhhdmUgaW5kaWNhdGlvbnMgdGhhdCBhdCBsZWFzdCBvbmUgb2YgdGhlIHByZWRpY3RvcnMgaXMgYXNzb2NpYXRlZCB3aXRoIGluY29tZSwgYW5kIGJhc2VkIG9uIHRoZSBmYWN0IHRoYXQgZWR1Y2F0aW9uIGhlcmUgaGFzIGEgaGlnaCBwLXZhbHVlLCB3ZSBjYW4gY29uc2lkZXIgcmVtb3ZpbmcgZWR1Y2F0aW9uIGZyb20gdGhlIG1vZGVsIGFuZCBzZWUgaG93IHRoZSBtb2RlbCBmaXQgY2hhbmdlcyAod2UgYXJlIG5vdCBnb2luZyB0byBydW4gYSB2YXJpYWJsZSBzZWxlY3Rpb24gcHJvY2VkdXJlIHN1Y2ggYXMgZm9yd2FyZCwgYmFja3dhcmQgb3IgbWl4ZWQgc2VsZWN0aW9uIGluIHRoaXMgZXhhbXBsZSk6CgpgYGB7cn0KIyBGaXQgYSBsaW5lYXIgbW9kZWwgZXhjbHVkaW5nIGBlZHVjYXRpb25gCm1vZGVsMiA8LSBsbShpbmNvbWUgfiBwcmVzdGlnZS5jICsgd29tZW4uYywgZGF0YSA9IHByZXN0aWdlKQpzdW1tYXJ5KG1vZGVsMikKYGBgCgpUaGUgbW9kZWwgZXhjbHVkaW5nIGVkdWNhdGlvbiBoYXMgaW4gZmFjdCBpbXByb3ZlZCBvdXIgRi1TdGF0aXN0aWMgZnJvbSAqKjU4Ljg5KiogdG8gKio4Ny45OCoqIGJ1dCBubyBzdWJzdGFudGlhbCBpbXByb3ZlbWVudCB3YXMgYWNoaWV2ZWQgaW4gcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgYW5kIGFkanVzdGVkIFItc3F1YXJlIHZhbHVlLiBUaGlzIGlzIHBvc3NpYmx5IGR1ZSB0byB0aGUgcHJlc2VuY2Ugb2Ygb3V0bGllciBwb2ludHMgaW4gdGhlIGRhdGEuCgpMZXTigJlzIHBsb3QgdGhpcyBsYXN0IG1vZGVs4oCZcyByZXNpZHVhbHM6CgpgYGB7cn0KcGxvdChtb2RlbDIsIHdoaWNoID0gMSwgcGNoID0gMTYpCmBgYAoKTGV04oCZcyB2aXN1YWxpemUgYSB0aHJlZS1kaW1lbnNpb25hbCBpbnRlcmFjdGl2ZSBncmFwaCB3aXRoIGJvdGggcHJlZGljdG9ycyBhbmQgdGhlIHRhcmdldCB2YXJpYWJsZSBhbmQgdGhlIGxpbmVhciBmaXQgZnJvbSB0aGUgbGFzdCBtb2RlbDoKCmBgYHtyIHdlYmdsPVRSVUV9Cm5ld19kYXRhIDwtIGV4cGFuZC5ncmlkKHByZXN0aWdlLmMgPSBzZXEoLTM1LCA0NSwgYnkgPSA1KSwKICAgICAgICAgICAgICAgICAgICAgICAgd29tZW4uYyA9IHNlcSgtMjUsIDcwLCBieSA9IDUpKQpuZXdfZGF0YSRwbGFuZSA8LSBwcmVkaWN0KG1vZGVsMiwgbmV3ZGF0YSA9IG5ld19kYXRhKQp3aXRoKHByZXN0aWdlLCBwbG90M2QocHJlc3RpZ2UuYywgd29tZW4uYywgaW5jb21lLCBzaXplID0gMSwgdHlwZSA9ICJzIikpCndpdGgobmV3X2RhdGEsIHN1cmZhY2UzZCh1bmlxdWUocHJlc3RpZ2UuYyksIHVuaXF1ZSh3b21lbi5jKSwgcGxhbmUsCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSwgY29sb3IgPSAidG9tYXRvMiIsIAogICAgICAgICAgICAgICAgICAgICAgZnJvbnQgPSAiZmlsbCIsIGJhY2sgPSAiZmlsbCIpKQpyZ2x3aWRnZXQoKQpgYGAKCk5vdGUgZnJvbSB0aGUgM0QgZ3JhcGggYWJvdmUgaG93IHRoaXMgdmlldyBtb3JlIGNsZWFybHkgaGlnaGxpZ2h0cyB0aGUgcGF0dGVybiBleGlzdGVudCBhY3Jvc3MgYHByZXN0aWdlYCBhbmQgYHdvbWVuYCByZWxhdGl2ZSB0byBgaW5jb21lYC4gQWxzbywgdGhpcyBpbnRlcmFjdGl2ZSB2aWV3IGFsbG93cyB1cyB0byBtb3JlIGNsZWFybHkgc2VlIHRob3NlIHRocmVlIG9yIGZvdXIgb3V0bGllciBwb2ludHMgYXMgd2VsbCBhcyBob3cgd2VsbCBvdXIgbGFzdCBsaW5lYXIgbW9kZWwgZml0IHRoZSBkYXRhLgoKQXQgdGhpcyBzdGFnZSB3ZSBjb3VsZCB0cnkgYSBmZXcgZGlmZmVyZW50IHRyYW5zZm9ybWF0aW9ucyBvbiBib3RoIHRoZSBwcmVkaWN0b3JzIGFuZCB0aGUgcmVzcG9uc2UgdmFyaWFibGUgdG8gc2VlIGhvdyB0aGlzIHdvdWxkIGltcHJvdmUgdGhlIG1vZGVsIGZpdC4gRm9yIG5vdywgbGV04oCZcyBhcHBseSBhIGxvZ2FyaXRobWljIHRyYW5zZm9ybWF0aW9uIHdpdGggdGhlIGxvZyBmdW5jdGlvbiBvbiB0aGUgaW5jb21lIHZhcmlhYmxlICh0aGUgbG9nIGZ1bmN0aW9uIGhlcmUgdHJhbnNmb3JtcyB1c2luZyB0aGUgbmF0dXJhbCBsb2cuIElmIGJhc2UgMTAgaXMgZGVzaXJlZCBsb2cxMCBpcyB0aGUgZnVuY3Rpb24gdG8gYmUgdXNlZCkuIEFsc28sIHdlIGNvdWxkIHRyeSB0byBzcXVhcmUgYm90aCBwcmVkaWN0b3JzLiBMZXTigJlzIGFwcGx5IHRoZXNlIHN1Z2dlc3RlZCB0cmFuc2Zvcm1hdGlvbnMgZGlyZWN0bHkgaW50byB0aGUgbW9kZWwgZnVuY3Rpb24gYW5kIHNlZSB3aGF0IGhhcHBlbnMgd2l0aCBib3RoIHRoZSBtb2RlbCBmaXQgYW5kIHRoZSBtb2RlbCBhY2N1cmFjeS4KCmBgYHtyfQojIEZpdCBhIG1vZGVsIGV4Y2x1ZGluZyBgZWR1Y2F0aW9uYCwgIGxvZyB0aGUgYGluY29tZWAKbW9kZWwzID0gbG0obG9nKGluY29tZSkgfiBwcmVzdGlnZS5jICsgSShwcmVzdGlnZS5jXjIpICsgd29tZW4uYyArIEkod29tZW4uY14yKSwgCiAgICAgICAgICAgIGRhdGEgPSBwcmVzdGlnZSkKc3VtbWFyeShtb2RlbDMpCmBgYAoKYGBge3J9CiMgUGxvdCBtb2RlbCByZXNpZHVhbHMuCnBsb3QobW9kZWwzLCB3aGljaCA9IDEsIHBjaCA9IDE2KQpgYGAKCmBgYHtyIHdlYmdsPVRSVUV9Cm5ld19kYXRhMiA8LSBleHBhbmQuZ3JpZChwcmVzdGlnZS5jID0gc2VxKC0zNSwgNDUsIGJ5ID0gNSksCiAgICAgICAgICAgICAgICAgICAgICAgIHdvbWVuLmMgPSBzZXEoLTI1LCA3MCwgYnkgPSA1KSkKbmV3X2RhdGEyJHBsYW5lIDwtIHByZWRpY3QobW9kZWwzLCBuZXdkYXRhID0gbmV3X2RhdGEyKQp3aXRoKHByZXN0aWdlLCBwbG90M2QocHJlc3RpZ2UuYywgd29tZW4uYywgbG9nKGluY29tZSksIHNpemUgPSAxLCB0eXBlID0gInMiKSkKd2l0aChuZXdfZGF0YTIsIHN1cmZhY2UzZCh1bmlxdWUocHJlc3RpZ2UuYyksIHVuaXF1ZSh3b21lbi5jKSwgcGxhbmUsCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSwgY29sb3IgPSAidG9tYXRvMiIsIAogICAgICAgICAgICAgICAgICAgICAgZnJvbnQgPSAiZmlsbCIsIGJhY2sgPSAiZmlsbCIpKQpyZ2x3aWRnZXQoKQpgYGAKCkJ5IHRyYW5zZm9ybWluZyBib3RoIHRoZSBwcmVkaWN0b3JzIGFuZCB0aGUgdGFyZ2V0IHZhcmlhYmxlLCB3ZSBhY2hpZXZlIGFuIGltcHJvdmVkIG1vZGVsIGZpdC4gTm90ZSBob3cgdGhlIGFkanVzdGVkIFItc3F1YXJlIGhhcyBqdW1wZWQgdG8gKiowLjc1NDU5NjUqKi4gTW9zdCBwcmVkaWN0b3Jz4oCZIHAtdmFsdWVzIGFyZSBzaWduaWZpY2FudC4gSGVyZSwgdGhlIHNxdWFyZWQgd29tZW4uYyBwcmVkaWN0b3IgeWllbGRzIGEgd2VhayBwLXZhbHVlIChtYXliZSBhbiBpbmRpY2F0aW9uIHRoYXQgaW4gdGhlIHByZXNlbmNlIG9mIG90aGVyIHByZWRpY3RvcnMsIGl0IGlzIG5vdCByZWxldmFudCB0byBpbmNsdWRlIGFuZCB3ZSBjb3VsZCBleGNsdWRlIGl0IGZyb20gdGhlIG1vZGVsLikKCkxldOKAmXMgZ28gb24gYW5kIHJlbW92ZSB0aGUgc3F1YXJlZCB3b21lbi5jIHZhcmlhYmxlIGZyb20gdGhlIG1vZGVsIHRvIHNlZSBob3cgaXQgY2hhbmdlczoKCmBgYHtyfQojIEZpdCBhIG1vZGVsIGV4Y2x1ZGluZyBgZWR1Y2F0aW9uYCwgIGxvZyBgaW5jb21lYC4KbW9kZWw0ID0gbG0obG9nKGluY29tZSkgfiBwcmVzdGlnZS5jICsgSShwcmVzdGlnZS5jXjIpICsgd29tZW4uYyAsIGRhdGEgPSBwcmVzdGlnZSkKc3VtbWFyeShtb2RlbDQpCmBgYAoKYGBge3J9CiMgUGxvdCBtb2RlbCByZXNpZHVhbHMuCnBsb3QobW9kZWw0LCB3aGljaCA9IDEsIHBjaCA9IDE2KQpgYGAKCmBgYHtyIHdlYmdsPVRSVUV9Cm5ld19kYXRhMyA8LSBleHBhbmQuZ3JpZChwcmVzdGlnZS5jID0gc2VxKC0zNSwgNDUsIGJ5ID0gNSksCiAgICAgICAgICAgICAgICAgICAgICAgIHdvbWVuLmMgPSBzZXEoLTI1LCA3MCwgYnkgPSA1KSkKbmV3X2RhdGEzJHBsYW5lIDwtIHByZWRpY3QobW9kZWw0LCBuZXdkYXRhID0gbmV3X2RhdGEzKQp3aXRoKHByZXN0aWdlLCBwbG90M2QocHJlc3RpZ2UuYywgd29tZW4uYywgbG9nKGluY29tZSksIHNpemUgPSAxLCB0eXBlID0gInMiKSkKd2l0aChuZXdfZGF0YTMsIHN1cmZhY2UzZCh1bmlxdWUocHJlc3RpZ2UuYyksIHVuaXF1ZSh3b21lbi5jKSwgcGxhbmUsCiAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSwgY29sb3IgPSAidG9tYXRvMiIsIAogICAgICAgICAgICAgICAgICAgICAgZnJvbnQgPSAiZmlsbCIsIGJhY2sgPSAiZmlsbCIpKQpyZ2x3aWRnZXQoKQpgYGAKCk5vdGUgbm93IHRoYXQgdGhpcyB1cGRhdGVkIG1vZGVsIHlpZWxkcyBhIG11Y2ggYmV0dGVyIFItc3F1YXJlIG1lYXN1cmUgb2YgKiowLjc0OTA1NjUqKiwgd2l0aCBhbGwgcHJlZGljdG9yIHAtdmFsdWVzIGhpZ2hseSBzaWduaWZpY2FudCBhbmQgaW1wcm92ZWQgRi1TdGF0aXN0aWMgdmFsdWUgKCoqMTAxLjUqKikuIFRoZSByZXNpZHVhbHMgcGxvdCBhbHNvIHNob3dzIGEgcmFuZG9tbHkgc2NhdHRlcmVkIHBsb3QgaW5kaWNhdGluZyBhIHJlbGF0aXZlbHkgZ29vZCBmaXQgZ2l2ZW4gdGhlIHRyYW5zZm9ybWF0aW9ucyBhcHBsaWVkIGR1ZSB0byB0aGUgbm9uLWxpbmVhcml0eSBuYXR1cmUgb2YgdGhlIGRhdGEuCgpJbiBzdW1tYXJ5LCB3ZeKAmXZlIHNlZW4gYSBmZXcgZGlmZmVyZW50IG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyBhcHBsaWVkIHRvIHRoZSBQcmVzdGlnZSBkYXRhc2V0LiBXZSB0cmllZCBhbiBsaW5lYXIgYXBwcm9hY2guIFdlIGNyZWF0ZWQgYSBjb3JyZWxhdGlvbiBtYXRyaXggdG8gdW5kZXJzdGFuZCBob3cgZWFjaCB2YXJpYWJsZSB3YXMgY29ycmVsYXRlZC4gU3Vic2VxdWVudGx5LCB3ZSB0cmFuc2Zvcm1lZCB0aGUgdmFyaWFibGVzIHRvIHNlZSB0aGUgZWZmZWN0IGluIHRoZSBtb2RlbC4gV2XigJl2ZSBjcmVhdGVkIHRocmVlLWRpbWVuc2lvbmFsIHBsb3RzIHRvIHZpc3VhbGl6ZSB0aGUgcmVsYXRpb25zaGlwIG9mIHRoZSB2YXJpYWJsZXMgYW5kIGhvdyB0aGUgbW9kZWwgd2FzIGZpdHRpbmcgdGhlIGRhdGEgaW4gaGFuZC4K