Course by Prof. Andrew Conway

Multiple regression is a powerful statistical technique, and here you will discover why and how to use it. Part of the course will focus on matrix algebra since it is essential if you want to start estimating regression coefficients in the regression equation. The final chapter will introduce dummy coding as a technique to handle categorical variables.

setwd("d:/Cursos/Datacamp/RMultipleRegression")
fs <- read.csv('fs.csv')
fs

A gentle introduction to the principles of multiple regression

Multiple regression: starting off

This chapter will help to develop your understanding of the basic framework of multiple regressions. To this purpose, you will use a pre-loaded dataset fs that contains the yearly wages of professors at a certain university. By going through the exercises, you will familiarize yourself with the implementation of, and the intuition behind, the most important aspects of multiple regression in R.

As always, start by exploring the data in order to make sure that you get a global view of the data that you are working with. This first step is vital as it helps you to truly understand things. As such, you will be able to move through the exercises more easily. Use the R console to perform these actions.

Can you tell which description below most accurately matches the characteristics of a typical professor (the “average person”) in the data set?

summary(fs)
     salary            age            years            pubs        dept  
 Min.   : 60072   Min.   :31.00   Min.   : 5.00   Min.   : 14.00   H:28  
 1st Qu.:101818   1st Qu.:44.00   1st Qu.:17.75   1st Qu.: 44.00   P:35  
 Median :133049   Median :49.00   Median :23.50   Median : 66.00   S:37  
 Mean   :133607   Mean   :50.33   Mean   :24.14   Mean   : 66.93         
 3rd Qu.:170374   3rd Qu.:59.00   3rd Qu.:31.25   3rd Qu.: 90.00         
 Max.   :199606   Max.   :67.00   Max.   :41.00   Max.   :125.00         

A professor with a yearly salary around $ 133,000 a year, approximately 24 years of working experience and a publication record of around 67 scientific articles.

Multiple regression: visualization of the relationships

Now that you have briefly looked at the data in the previous exercise, a good next step would be to get a view on the nature of the empirical relationships between the outcome variable (the annual salary of the professors) and two specific predictor variables (number of years as a faculty member and number of publications).

Intuitively, one would expect that a professor with more working experience would earn more. In addition, it would also make sense that a faculty member with a larger expertise in terms of numbers of publications would have a higher salary as well. Let us see whether this holds in practice by getting visual confirmation by means of some scatter plots!

# fs is available in your working environment
str(fs)
'data.frame':   100 obs. of  5 variables:
 $ salary: int  60072 61017 61618 61976 66398 67083 69314 71653 72519 74821 ...
 $ age   : int  38 39 38 31 32 39 32 31 66 66 ...
 $ years : int  16 14 18 15 30 25 24 29 32 26 ...
 $ pubs  : int  22 23 23 14 32 27 29 37 61 69 ...
 $ dept  : Factor w/ 3 levels "H","P","S": 2 2 2 2 2 2 2 3 3 3 ...
# Perform the two single regressions and save them in a variable
model_years <- lm(salary~years,data=fs)
model_pubs <- lm(salary~pubs,data=fs)
# Plot both enhanced scatter plots in one plot matrix of 1 by 2
# fs is available in your working environment
str(fs)
'data.frame':   100 obs. of  5 variables:
 $ salary: int  60072 61017 61618 61976 66398 67083 69314 71653 72519 74821 ...
 $ age   : int  38 39 38 31 32 39 32 31 66 66 ...
 $ years : int  16 14 18 15 30 25 24 29 32 26 ...
 $ pubs  : int  22 23 23 14 32 27 29 37 61 69 ...
 $ dept  : Factor w/ 3 levels "H","P","S": 2 2 2 2 2 2 2 3 3 3 ...
# Perform the two single regressions and save them in a variable
model_years <- lm(salary~years,data=fs)
model_pubs <- lm(salary~pubs,data=fs)
# Plot both enhanced scatter plots in one plot matrix of 1 by 2
par(mfrow = c(1, 2))
plot(fs$salary ~ fs$years, main = "plot_years", xlab = 'years', ylab = 'salary')
abline(model_years)
plot(fs$salary ~ fs$pubs, main = 'plot_pubs', xlab = 'pubs', ylab = 'salary')
abline(model_pubs)

Multiple regression: model selection

In the video, Professor Conway talked about the R2 coefficients of regression models. These coefficients are often used in practice to select the best regression model in case of competing models. The R2 coefficient of a regression model is defined as the percentage of the variation in the outcome variable that can be explained by the predictor variables of the model. In general, the R2

coefficient of a model increases when more predictor variables are added to the model. After all, adding more predictor variables to the model tends to increase the odds of explaining more variation in the outcome variable.

Check this generality by comparing the R2 coefficient of a single regression model with that of a multiple regression model.

# Do a single regression of salary onto years of experience and check the output
model_1 <- lm(fs$salary ~ fs$years)
summary(model_1)

Call:
lm(formula = fs$salary ~ fs$years)

Residuals:
   Min     1Q Median     3Q    Max 
-82972  -9537   4305  17703  57949 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  68672.5     8259.0   8.315 5.38e-13 ***
fs$years      2689.9      318.4   8.448 2.78e-13 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 30220 on 98 degrees of freedom
Multiple R-squared:  0.4214,    Adjusted R-squared:  0.4155 
F-statistic: 71.37 on 1 and 98 DF,  p-value: 2.779e-13
# Do a multiple regression of salary onto years of experience and numbers of publications and check the output
model_2 <- lm(fs$salary ~ fs$years + fs$pubs)
summary(model_2)

Call:
lm(formula = fs$salary ~ fs$years + fs$pubs)

Residuals:
   Min     1Q Median     3Q    Max 
-67835 -14589   2362  13358  69613 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  58828.7     7605.9   7.735 9.79e-12 ***
fs$years      1337.4      387.1   3.455 0.000819 ***
fs$pubs        634.9      123.6   5.137 1.44e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 26930 on 97 degrees of freedom
Multiple R-squared:  0.5451,    Adjusted R-squared:  0.5357 
F-statistic: 58.12 on 2 and 97 DF,  p-value: < 2.2e-16
# Save the R squared of both models in preliminary variables
preliminary_model_1 <- summary(model_1)$r.squared
preliminary_model_2 <-summary(model_2)$r.squared
# Round them off while you save them in new variables
r_squared <- c()
r_squared[1] <- round(preliminary_model_1,3)
r_squared[2] <- round(preliminary_model_2,3)
# Print out the vector to see both R squared coefficients
r_squared
[1] 0.421 0.545

Multiple regression: beware of redundancy

Although the R2

coefficient of a regression model tends to increase in general when adding more predictor variables to the mix, more is not always better. Throwing in every variable at your disposal into a multiple regression is usually not a good idea. If the extra variables do not add extra value to the model -when the corresponding regression coefficients are not significantly different from zero- it tends to be a good practice to just leave them out.

In the next example you will see that adding the age of the professors as an extra predictor value does not necessarily lead to a better econometric model, even though the R2 coefficient increases in the process. Hence, a good statistician should not blindly depend on the R2 coefficients while selecting relevant regression models.

# Do multiple regression and check the regression output
model_3 <- lm(fs$salary ~ fs$years + fs$pubs + fs$age)
summary(model_3)

Call:
lm(formula = fs$salary ~ fs$years + fs$pubs + fs$age)

Residuals:
   Min     1Q Median     3Q    Max 
-70488 -14268   2502  13233  70413 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  51538.4    13726.9   3.755 0.000298 ***
fs$years      1210.0      436.5   2.772 0.006691 ** 
fs$pubs        618.9      126.5   4.894 3.98e-06 ***
fs$age         227.2      355.6   0.639 0.524444    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 27010 on 96 degrees of freedom
Multiple R-squared:  0.5471,    Adjusted R-squared:  0.5329 
F-statistic: 38.65 on 3 and 96 DF,  p-value: < 2.2e-16
# Round off the R squared coefficients and save the result in the vector (in one step!)
r_squared[3] <- round(summary(model_3)$r.squared,3)
# Print out the vector in order to display all R squared coefficients simultaneously
r_squared
[1] 0.421 0.545 0.547

Can you tell that the R2 coefficient indeed increases when adding age as an extra predictor variable to the regression model? This would imply that this extended model is preferred according to the R2 coefficient criterium. However, this is not the case as this model is redundant: the regression coefficient for age tends to be insignificant. Remember that a good statistician not only looks at the R2 coefficient when selecting a relevant regression model!

Multiple regression: interpretation regression constants

Can you give the correct interpretation of the regression constant in a multiple regression model? > It is the predicted value for the outcome variable when all predictor variables are set equal to zero.

Multiple regression: interpretation regression coefficients

Now that you are an expert in the domain of multiple regressions, retake the multiple regression model of the outcome variable salary on predictor variables years and pubs once more. The required dataset fs is still pre-loaded into R. Print out the output to help you to answer the following question, that essentially consists of two parts. If you feel unsure about doing this, return to the earlier questions to revise the basics of multiple regression before moving on.

Which of the following is the correct value of the regression coefficient for publications? And how would you interpret this number? > The regression coefficient for publications is equal to 634.9. This means that the predicted increase in salary for a unit increase in the number of publications is equal to $634.90 for those professors who have already completed their PhD an average amount of years ago.

library('QuantPsyc')
lm.beta(model_2)
 fs$years   fs$pubs 
0.3227385 0.4798728 

Intuition behind estimation of multiple regression coefficients

Definition of matrices

This chapter will help to understand how the correlation matrix and regression coefficients are constructed. To this purpose, you will first refresh and practice some basic matrix concepts. By going through the exercises, you will acquaint yourself with the implementation of the theoretical concepts in R and become familiar with the step-by-step construction of the correlation matrix and regression coefficients.

A matrix is a rectangular table that contains known or unknown numbers. The size, or order, of the table is given by the number of rows and columns it contains. For example, matrix m consists of 4 rows and 2 columns, which means that the order of matrix m is 4x2. 72468⎤⎦⎥⎥⎥

m <- matrix(1:8,ncol=2,byrow = T)
m
     [,1] [,2]
[1,]    1    2
[2,]    3    4
[3,]    5    6
[4,]    7    8

A trick that is often used in matrix algebra is taking the transpose of a matrix. The transpose of a matrix is constructed by rewriting its rows as columns. As a result, the order will change as the matrix will be reversed. For example, taking the transpose of matrix m results in a 2x4 matrix

t(m)
     [,1] [,2] [,3] [,4]
[1,]    1    3    5    7
[2,]    2    4    6    8

Start by creating and defining matrices r and s to be used in the next exercises. Use the R terminal to perform these actions.

# Construction of 3 by 8 matrix r that contains the numbers 1 up to 24
r <- matrix(1:24, nrow=3, ncol=8)
r
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]    1    4    7   10   13   16   19   22
[2,]    2    5    8   11   14   17   20   23
[3,]    3    6    9   12   15   18   21   24
# Construction of 3 by 8 matrix s that contains the numbers 21 up to 44
s <- matrix(21:44, nrow=3, ncol=8)
s
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]   21   24   27   30   33   36   39   42
[2,]   22   25   28   31   34   37   40   43
[3,]   23   26   29   32   35   38   41   44
# Take the transpose t of matrix r
t <- t(r)
t
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9
[4,]   10   11   12
[5,]   13   14   15
[6,]   16   17   18
[7,]   19   20   21
[8,]   22   23   24

Addition, subtraction and multiplication of matrices

You can perform operations on matrices. Some operations put forward some requirements that need to be respected. For example, the addition or subtraction of two matrices is only possible if the matrices are of the same size or order. Elements with the same row and column number in the two matrices are added or subtracted and lead to a new matrix.

The multiplication of two matrices is only possible when they are conformable: the number of columns of the first matrix must equal the number of rows of the second matrix: > R=MT⋅N

with M a k-by-i matrix and N a k-by-i matrix. Matrix M and N can not be multiplied since both matrices have an identical order. Taking the transpose of matrix M leads to a solution: the two matrices are conformable.

# Compute the sum of matrices r and s
operation_1 <- r + s
# Compute the difference between matrices r and s
operation_2 <- r - s
# Multiply matrices t and s
operation_3 <- t %*% s

Row vector of sums

In the previous video, Professor Conway explained how you can go from a raw dataframe to a correlation matrix. Here you will construct such a correlation matrix yourself from scratch.

In your R workspace, a raw dataframe X (a 10-by-3 matrix) is already loaded in. The first step is to sum up its 3 columns. In other words, we need to construct the row vector of sums:

T1p=11nXnp

where T1p the 1-by-p row vector of sums is, Xnp an n-by-p raw dataframe, and 11n a 1-by-n matrix of ones.

irislen <- nrow(iris)
X <- as.matrix(iris[,1:3])
head(X)
     Sepal.Length Sepal.Width Petal.Length
[1,]          5.1         3.5          1.4
[2,]          4.9         3.0          1.4
[3,]          4.7         3.2          1.3
[4,]          4.6         3.1          1.5
[5,]          5.0         3.6          1.4
[6,]          5.4         3.9          1.7
I = matrix(rep(1,irislen),ncol = irislen, nrow = 1)
t_mat <- I %*% X
print("Sum of columns")
[1] "Sum of columns"
t_mat
     Sepal.Length Sepal.Width Petal.Length
[1,]        876.5       458.6        563.7

Row vector of means and matrix of means

Now that you have the row vector of sums t_mat, a 1-by-3 matrix, you are ready to construct the row vector of means M via:

M1p=T1pN−1

with M1p the 1-by-p row vector of means, T1p the 1-by-p row vector of sums, and N−1

the inverse number of observations for each variable.

Given the row vector of means M, you can also construct the matrix of means MM by multiplying the row vector of means with a column vector:

MMnp=1n1M1p

with MMnp the n-by-p matrix of means, M1p the 1-by-p row vector of means, and 1n1 a n-by-1 column vector.

# Number of observations
n = irislen
# Compute the row vector of means
M <- t_mat * n^-1
# Construction of 10 by 1 matrix J of which the elements are all 1
J <- matrix(rep(1,n), nrow=n, ncol=1)
# Compute the matrix of means
MM <- J %*% M

Matrix of deviation scores

Why do you need a matrix of means? Because you want a matrix of deviation scores! You are interested in the deviation of an observation for that variable to its mean.

As Prof. Conway showed in the video, the formula to calculate a matrix of deviation scores D is:

Dnp=Xnp−MMnp

where Dnp is an n-by-p deviation matrix, Xnp the n-by-p raw dataframe, and MMnp the n-by-p matrix of means (the one you calculated in the previous exercise).

# Matrix of deviation scores D
D <- X - MM
tail(D)
       Sepal.Length Sepal.Width Petal.Length
[145,]   0.85666667  0.24266667        1.942
[146,]   0.85666667 -0.05733333        1.442
[147,]   0.45666667 -0.55733333        1.242
[148,]   0.65666667 -0.05733333        1.442
[149,]   0.35666667  0.34266667        1.642
[150,]   0.05666667 -0.05733333        1.342

Sum of squares and sum of cross products matrix

Ok, so you have reached the really cool part now ;-)

If you now take your matrix of deviation scores D and multiply it with its transpose, just like prof. Conway did in the video, you get the matrix of sum of squares and sum of cross products S. The formula is:

Sxx=DTpn.Dnp

with SXX the matrix of sum of squares and sum of cross products, DTpn the transpose of the deviation matrix, and Dnp the n-by-p deviation matrix.

# Sum of squares and sum of cross products matrix
S <- t(D) %*% D
S
             Sepal.Length Sepal.Width Petal.Length
Sepal.Length   102.168333   -6.322667     189.8730
Sepal.Width     -6.322667   28.306933     -49.1188
Petal.Length   189.873000  -49.118800     464.3254
diag(S)
Sepal.Length  Sepal.Width Petal.Length 
   102.16833     28.30693    464.32540 

Note that the sum of squares are in the diagonal, and the sum of cross products across the diagonal.

Calculating the correlation matrix

Almost at the finish line! Using the sums of squares and the sums of cross products matrix S you can calculate the variance-covariance matrix C:

CXX=SXXN−1

with CXX the variance-covariance matrix, SXX the matrix of sum of squares and sum of cross products, and N−1

the inverse number of observations for each variable.

Next, you can standardize this variance-covariance matrix by multiplying it with the standard deviation matrix SD. This gives us the correlation matrix R:

RXX=(SDXX)−1CXX(SDXX)−1

with RXX the correlation matrix, CXX the variance-covariance matrix, and SDXX the standard deviation matrix.

  # Construct the variance-covariance matrix
C <- S * n^-1
# Generate the standard deviations matrix
SD <- diag(x = diag(C)^(1/2), nrow = 3, ncol = 3)
# Compute the correlation matrix
R <- solve(SD) %*% C %*% solve(SD)
R
           [,1]       [,2]       [,3]
[1,]  1.0000000 -0.1175698  0.8717538
[2,] -0.1175698  1.0000000 -0.4284401
[3,]  0.8717538 -0.4284401  1.0000000
head(X)
     Sepal.Length Sepal.Width Petal.Length
[1,]          5.1         3.5          1.4
[2,]          4.9         3.0          1.4
[3,]          4.7         3.2          1.3
[4,]          4.6         3.1          1.5
[5,]          5.0         3.6          1.4
[6,]          5.4         3.9          1.7

Dummy coding

Starting off

This chapter will teach you multiple methods to transform categorical variables into numerical variables by means of dummy coding. As such, you will be able to use these “dummies” alongside other numerical variables in multiple regressions. Additionally, a special version of dummy coding called “effects coding” will also be introduced at the end of the chapter.

Again, you will use the dataset fs. Besides the independent variable, yearly wages (salary), and other characteristics of professors at a certain university, this dataset also contains a categorical variable (dept), that holds the information on the department that each professor belongs to. There are three departments: history (h), psychology (p) and sociology (s).

As always, start off by exploring the data of each department in order to make sure that you get a good view of the data that you are working with.

library(psych)
describeBy(fs, fs$dept)

 Descriptive statistics by group 
group: H
       vars  n      mean       sd median   trimmed      mad   min    max range  skew kurtosis      se
salary    1 28 137421.29 33736.24 138411 137679.38 45224.49 89387 181427 92040 -0.04    -1.61 6375.55
age       2 28     49.04    11.00     48     49.04    14.08    32     66    34 -0.01    -1.37    2.08
years     3 28     22.25    10.95     22     22.21    14.08     5     40    35 -0.03    -1.34    2.07
pubs      4 28     63.32    29.13     57     62.62    28.17    17    122   105  0.29    -1.14    5.50
dept*     5 28      1.00     0.00      1      1.00     0.00     1      1     0   NaN      NaN    0.00
------------------------------------------------------------------------------------------------------------------------------------------------ 
group: P
       vars  n      mean       sd median   trimmed      mad   min    max  range  skew kurtosis      se
salary    1 35 129067.37 43738.84 131222 130061.03 48036.24 60072 189447 129375 -0.18    -1.33 7393.21
age       2 35     48.23     9.63     45     48.24    10.38    31     67     36  0.15    -0.97    1.63
years     3 35     22.09     8.35     22     21.93     8.90     5     40     35  0.20    -0.53    1.41
pubs      4 35     59.91    30.52     57     57.86    34.10    14    122    108  0.48    -0.65    5.16
dept*     5 35      2.00     0.00      2      2.00     0.00     2      2      0   NaN      NaN    0.00
------------------------------------------------------------------------------------------------------------------------------------------------ 
group: S
       vars  n      mean       sd median   trimmed      mad   min    max  range  skew kurtosis      se
salary    1 37 135015.59 40034.84 135837 135091.03 52777.59 71653 199606 127953 -0.02    -1.32 6581.69
age       2 37     53.30    10.01     54     54.10    11.86    31     66     35 -0.47    -0.72    1.64
years     3 37     27.51     8.72     29     28.00    10.38     5     41     36 -0.45    -0.16    1.43
pubs      4 37     76.30    28.11     67     76.19    31.13    25    125    100  0.21    -0.90    4.62
dept*     5 37      3.00     0.00      3      3.00     0.00     3      3      0   NaN      NaN    0.00

Can you also tell which department has the largest number of professors?

Creating dummy variables (1)

In order to automatically create dummy variables, the dummy.code() function of the psych package is easy to use.

The function takes a categorical variable as argument and automatically creates the required dummy variables: all levels are ranked alphabetically and the first one is taken as the reference group. Remember that only (N-1) dummies are created for a categorical variable with N levels. Consequently, the category which is not directly linked with a dummy variable is defined as the reference category.

head(dept_code)
[1] P P P P P P
attr(,"contrasts")
             .L         .Q
H -7.071068e-01  0.4082483
P -7.850462e-17 -0.8164966
S  7.071068e-01  0.4082483
Levels: H P S

Before switching to some more advanced exercises, think about the summary results for the extended_fs dataframe. Is there a way to see which department has the largest faculty staff using the dummy variables? Another way to look up which department tends to have the highest number of professors is to look at the mean of the dummy variables and to multiply it by 100. After all, the mean of each dummy variable is equal to the sum of one times the number of professors in a certain department, divided by the total number of professors (=100).

Creating dummy variables (2)

In order to include a categorical variable in a regression, the variable needs to be converted into a numeric variable by the means of a dummy variable. Previously, dummy variables have been generated using the intuitive, but less general dummy.code() function from the psych library.

From this point onwards the contrast C() function is used to create dummy variables. Do not confuse this function with the c() function that is used to combine values in a vector or list. The contrast C() takes a categorical variable as a first argument and the treatment as a second argument. The latter tells R to rank all levels alphabetically and to take the first category as the reference group.

This exercise will illustrate the inclusion of the categorical variable dept in a multiple regression. The code on the right estimates the regression without categorical variable. The summary() function is used to get the summary of the regression results of model and the confint() function is used to create the confidence intervals.

# Regress salary against years and publications
model <- lm(fs$salary ~ fs$years + fs$pubs)
# Apply the summary function to get summarized results for model
summary(model)

Call:
lm(formula = fs$salary ~ fs$years + fs$pubs)

Residuals:
   Min     1Q Median     3Q    Max 
-67835 -14589   2362  13358  69613 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  58828.7     7605.9   7.735 9.79e-12 ***
fs$years      1337.4      387.1   3.455 0.000819 ***
fs$pubs        634.9      123.6   5.137 1.44e-06 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 26930 on 97 degrees of freedom
Multiple R-squared:  0.5451,    Adjusted R-squared:  0.5357 
F-statistic: 58.12 on 2 and 97 DF,  p-value: < 2.2e-16
# Compute the confidence intervals for model
confint(model)
                 2.5 %     97.5 %
(Intercept) 43733.1268 73924.1805
fs$years      569.0514  2105.6692
fs$pubs       389.5972   880.2303
# Create dummies for the categorical variable fs$dept by using the C() function
dept_code <- C(fs$dept, contr.poly)
dept_code
  [1] P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P
 [95] P S S S S S
attr(,"contrasts")
             .L         .Q
H -7.071068e-01  0.4082483
P -7.850462e-17 -0.8164966
S  7.071068e-01  0.4082483
Levels: H P S
# Regress salary against years, publications and department
model_dummy <- lm(fs$salary ~ fs$years + fs$pubs + dept_code)
# Apply the summary function to get summarized results for model_dummy
summary(model_dummy)

Call:
lm(formula = fs$salary ~ fs$years + fs$pubs + dept_code)

Residuals:
   Min     1Q Median     3Q    Max 
-59233 -12490  -1488  13227  67294 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  54125.3     7597.6   7.124 2.01e-10 ***
fs$years      1507.8      379.4   3.974 0.000138 ***
fs$pubs        655.5      120.2   5.452 3.93e-07 ***
dept_code.L -13327.3     4742.3  -2.810 0.006011 ** 
dept_code.Q  -2899.4     4543.9  -0.638 0.524947    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 26080 on 95 degrees of freedom
Multiple R-squared:  0.5824,    Adjusted R-squared:  0.5648 
F-statistic: 33.12 on 4 and 95 DF,  p-value: < 2.2e-16
# Compute the confidence intervals for model_dummy
confint(model_dummy)
                  2.5 %     97.5 %
(Intercept)  39042.0765 69208.4611
fs$years       754.5720  2260.9439
fs$pubs        416.8113   894.2219
dept_code.L -22742.0151 -3912.6476
dept_code.Q -11920.2193  6121.3400

Some info from data

summary(variance2)
                Df    Sum Sq   Mean Sq F value   Pr(>F)    
years            1 6.518e+10 6.518e+10  99.853 3.72e-16 ***
pubs             1 1.914e+10 1.914e+10  29.320 5.26e-07 ***
dept             2 5.765e+09 2.882e+09   4.416   0.0149 *  
years:pubs       1 1.074e+05 1.074e+05   0.000   0.9898    
years:dept       2 1.995e+08 9.976e+07   0.153   0.8585    
pubs:dept        2 4.523e+09 2.262e+09   3.465   0.0356 *  
years:pubs:dept  2 2.429e+09 1.214e+09   1.860   0.1617    
Residuals       88 5.744e+10 6.528e+08                     
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Model selection: ANOVA

In a next step, we would like to test if the inclusion of the categorical variable in the model improves the fit. The dataset fs and regressions model and model_dummy are available in your workspace. The anova() function compares both models and reports whether or not the models differ in a significant way.

If the inclusion of the categorical variable effectively improves the fit, one might wonder to which extent the specific departments differ from each other.

summary(aov(fs$salary ~ dept_code * fs$years * fs$pubs))
                           Df    Sum Sq   Mean Sq F value  Pr(>F)    
dept_code                   2 1.202e+09 6.010e+08   0.921  0.4020    
fs$years                    1 6.867e+10 6.867e+10 105.204 < 2e-16 ***
fs$pubs                     1 2.021e+10 2.021e+10  30.959 2.8e-07 ***
dept_code:fs$years          2 1.621e+08 8.104e+07   0.124  0.8834    
dept_code:fs$pubs           2 4.528e+09 2.264e+09   3.469  0.0355 *  
fs$years:fs$pubs            1 3.236e+07 3.236e+07   0.050  0.8243    
dept_code:fs$years:fs$pubs  2 2.429e+09 1.214e+09   1.860  0.1617    
Residuals                  88 5.744e+10 6.528e+08                    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Model selection: interpretation

Retrieve the output of the previous exercise by means of the anova() function. Based on this output, can you tell whether the inclusion of the department dummy variable dept_code significantly improves the fit of the model? And why? > The inclusion of department significantly improves the fit of the model as it results in a significant p-value at a 5 % significance level.

Discrepancy between actual and predicted means

To see what role that the department plays in explaining the professors’ salary, you can take a look at the actual differences in mean salary among departments.

In order to compute the actual means of the salaries for each department easily, use the tapply() function, in which you enter the variable, the categorical variable and the requested summary statistic (that is, the mean).

# Actual means of fs$salary
tapply(fs$salary, fs$dept, mean)

Discrepancy between actual and predicted means: get intuitive!

As seen in the previous exercise, the actual means do not largely differ among the different departments. There has to be an explanation why the predicted salaries of the professors of the sociology department tend to be lower on average than those of professors of the history department. Remember that the regression coefficient for the sociology department was significantly different from zero in a multiple regression in which a dummy variable for department was included.

How would you explain the apparent discrepancy between the actual and the predicted mean salary of professors across departments? Take a look at the actual means of the other predictor variables years and publications by means of the tapply() function to help you to come up with a well-considered answer.

The variables defined in the previous exercises remain available. Check via ls()

tapply(fs$salary,fs$dept,mean)
tapply(fs$pubs,fs$dept,mean)
tapply(fs$years,fs$dept,mean)

Because sociology professors have more years on the job and more publications on average compared to other departments, their actual mean salary is comparable to that of other departments. The discrepancy between actual and predicted salary is solely the consequence of the fact that predicted mean salaries only hold for professors who have an average amount of publications and an average numbers of years of experience across all professors of the university.

Unweighted effects coding

Professor Conway mentions effects coding as an interesting dummy coding scheme. In the effects coding scheme, a reference category still needs to be appointed but this time the reference category gets a weight - 1. The number of dummies that must be generated equals “the number of levels - 1”. The effects coding scheme for a categorical variable with 4 levels of which the fourth category is picked out as the reference category, is shown below:

dummy = [1 0 0;0 1 0;0 0 0;-1-1 -1]

This type of effects coding is called unweighted effects coding since the number of observations within each category of the variable is not taken into account.

In the following steps, you will learn to set up a regression by using the unweighted effects coding scheme for a categorical variable. In the example, the dependent variable salary is regressed against the independent variable dept. The data is already loaded in the workspace under the name fs.

# Number of levels
fs$dept
  [1] P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P
 [95] P S S S S S
Levels: H P S
# Factorize the categorical variable fs$dept and name the factorized variable dept.f
dept.f <- factor(fs$dept)
dept.f
  [1] P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P P S S S S S S S S H H H H H H H P P P P P P
 [95] P S S S S S
Levels: H P S
# Assign the 3 levels generated in step 2 to dept.f
contrasts(dept.f) <-contr.sum(3)
# Regress salary against dept.f
model_unweighted <- lm(fs$salary ~ dept.f)
# Apply the summary() function
summary(model_unweighted)

Call:
lm(formula = fs$salary ~ dept.f)

Residuals:
   Min     1Q Median     3Q    Max 
-68995 -27249   1488  36471  64590 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   133835       4007  33.403   <2e-16 ***
dept.f1         3586       5907   0.607    0.545    
dept.f2        -4767       5579  -0.855    0.395    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 39780 on 97 degrees of freedom
Multiple R-squared:  0.007771,  Adjusted R-squared:  -0.01269 
F-statistic: 0.3799 on 2 and 97 DF,  p-value: 0.685

Weighted effects coding

Weighted effects coding differs from unweighted effects coding with respect to the weights, fractions. A reference category is chosen and the weights form the following dummy coding scheme:

dummies weighted. with n = the number of observations of each group and index N = the number of levels. The weights represent the number of observation of a non-reference category relative to those of the reference category.

If the weights are computed, the regression of the dependent variable against the non-categorical and categorical variables using the weighted effects coding scheme can start.

# Factorize the categorical variable fs$dept and name the factorized variable dept.g
dept.g <- factor(fs$dept)

# Assign the weights matrix to dept.g
contrasts(dept.g) <- contr.sum(3)

# Regress salary against dept.f and apply the summary() function
model_weighted <- lm(fs$salary ~ dept.g)

# Apply the summary() function
summary(model_weighted)
LS0tDQp0aXRsZTogIkludHJvIHRvIFN0YXRpc3RpY3Mgd2l0aCBSOiBNdWx0aXBsZSBSZWdyZXNzaW9uIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCmF1dGhvcjogR2l1bGlhbm8gTGVtZXMNCi0tLQ0KDQojIyBDb3Vyc2UgYnkgUHJvZi4gQW5kcmV3IENvbndheQ0KTXVsdGlwbGUgcmVncmVzc2lvbiBpcyBhIHBvd2VyZnVsIHN0YXRpc3RpY2FsIHRlY2huaXF1ZSwgYW5kIGhlcmUgeW91IHdpbGwgZGlzY292ZXIgd2h5IGFuZCBob3cgdG8gdXNlIGl0LiBQYXJ0IG9mIHRoZSBjb3Vyc2Ugd2lsbCBmb2N1cyBvbiBtYXRyaXggYWxnZWJyYSBzaW5jZSBpdCBpcyBlc3NlbnRpYWwgaWYgeW91IHdhbnQgdG8gc3RhcnQgZXN0aW1hdGluZyByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBpbiB0aGUgcmVncmVzc2lvbiBlcXVhdGlvbi4gVGhlIGZpbmFsIGNoYXB0ZXIgd2lsbCBpbnRyb2R1Y2UgZHVtbXkgY29kaW5nIGFzIGEgdGVjaG5pcXVlIHRvIGhhbmRsZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuDQpgYGB7cn0NCnNldHdkKCJkOi9DdXJzb3MvRGF0YWNhbXAvUk11bHRpcGxlUmVncmVzc2lvbiIpDQpmcyA8LSByZWFkLmNzdignZnMuY3N2JykNCmZzDQoNCmBgYA0KIyMgQSBnZW50bGUgaW50cm9kdWN0aW9uIHRvIHRoZSBwcmluY2lwbGVzIG9mIG11bHRpcGxlIHJlZ3Jlc3Npb24NCg0KIyMjIE11bHRpcGxlIHJlZ3Jlc3Npb246IHN0YXJ0aW5nIG9mZg0KDQpUaGlzIGNoYXB0ZXIgd2lsbCBoZWxwIHRvIGRldmVsb3AgeW91ciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBiYXNpYyBmcmFtZXdvcmsgb2YgbXVsdGlwbGUgcmVncmVzc2lvbnMuIFRvIHRoaXMgcHVycG9zZSwgeW91IHdpbGwgdXNlIGEgcHJlLWxvYWRlZCBkYXRhc2V0IGZzIHRoYXQgY29udGFpbnMgdGhlIHllYXJseSB3YWdlcyBvZiBwcm9mZXNzb3JzIGF0IGEgY2VydGFpbiB1bml2ZXJzaXR5LiBCeSBnb2luZyB0aHJvdWdoIHRoZSBleGVyY2lzZXMsIHlvdSB3aWxsIGZhbWlsaWFyaXplIHlvdXJzZWxmIHdpdGggdGhlIGltcGxlbWVudGF0aW9uIG9mLCBhbmQgdGhlIGludHVpdGlvbiBiZWhpbmQsIHRoZSBtb3N0IGltcG9ydGFudCBhc3BlY3RzIG9mIG11bHRpcGxlIHJlZ3Jlc3Npb24gaW4gUi4NCg0KQXMgYWx3YXlzLCBzdGFydCBieSBleHBsb3JpbmcgdGhlIGRhdGEgaW4gb3JkZXIgdG8gbWFrZSBzdXJlIHRoYXQgeW91IGdldCBhIGdsb2JhbCB2aWV3IG9mIHRoZSBkYXRhIHRoYXQgeW91IGFyZSB3b3JraW5nIHdpdGguIFRoaXMgZmlyc3Qgc3RlcCBpcyB2aXRhbCBhcyBpdCBoZWxwcyB5b3UgdG8gdHJ1bHkgdW5kZXJzdGFuZCB0aGluZ3MuIEFzIHN1Y2gsIHlvdSB3aWxsIGJlIGFibGUgdG8gbW92ZSB0aHJvdWdoIHRoZSBleGVyY2lzZXMgbW9yZSBlYXNpbHkuIFVzZSB0aGUgUiBjb25zb2xlIHRvIHBlcmZvcm0gdGhlc2UgYWN0aW9ucy4NCg0KQ2FuIHlvdSB0ZWxsIHdoaWNoIGRlc2NyaXB0aW9uIGJlbG93IG1vc3QgYWNjdXJhdGVseSBtYXRjaGVzIHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgYSB0eXBpY2FsIHByb2Zlc3NvciAodGhlICJhdmVyYWdlIHBlcnNvbiIpIGluIHRoZSBkYXRhIHNldD8NCmBgYHtyfQ0Kc3VtbWFyeShmcykNCmBgYA0KKkEgcHJvZmVzc29yIHdpdGggYSB5ZWFybHkgc2FsYXJ5IGFyb3VuZCAkIDEzMywwMDAgYSB5ZWFyLCBhcHByb3hpbWF0ZWx5IDI0IHllYXJzIG9mIHdvcmtpbmcgZXhwZXJpZW5jZSBhbmQgYSBwdWJsaWNhdGlvbiByZWNvcmQgb2YgYXJvdW5kIDY3IHNjaWVudGlmaWMgYXJ0aWNsZXMuKg0KDQojIyMgTXVsdGlwbGUgcmVncmVzc2lvbjogdmlzdWFsaXphdGlvbiBvZiB0aGUgcmVsYXRpb25zaGlwcw0KDQpOb3cgdGhhdCB5b3UgaGF2ZSBicmllZmx5IGxvb2tlZCBhdCB0aGUgZGF0YSBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIGEgZ29vZCBuZXh0IHN0ZXAgd291bGQgYmUgdG8gZ2V0IGEgdmlldyBvbiB0aGUgbmF0dXJlIG9mIHRoZSBlbXBpcmljYWwgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBvdXRjb21lIHZhcmlhYmxlICh0aGUgYW5udWFsIHNhbGFyeSBvZiB0aGUgcHJvZmVzc29ycykgYW5kIHR3byBzcGVjaWZpYyBwcmVkaWN0b3IgdmFyaWFibGVzIChudW1iZXIgb2YgeWVhcnMgYXMgYSBmYWN1bHR5IG1lbWJlciBhbmQgbnVtYmVyIG9mIHB1YmxpY2F0aW9ucykuDQoNCkludHVpdGl2ZWx5LCBvbmUgd291bGQgZXhwZWN0IHRoYXQgYSBwcm9mZXNzb3Igd2l0aCBtb3JlIHdvcmtpbmcgZXhwZXJpZW5jZSB3b3VsZCBlYXJuIG1vcmUuIEluIGFkZGl0aW9uLCBpdCB3b3VsZCBhbHNvIG1ha2Ugc2Vuc2UgdGhhdCBhIGZhY3VsdHkgbWVtYmVyIHdpdGggYSBsYXJnZXIgZXhwZXJ0aXNlIGluIHRlcm1zIG9mIG51bWJlcnMgb2YgcHVibGljYXRpb25zIHdvdWxkIGhhdmUgYSBoaWdoZXIgc2FsYXJ5IGFzIHdlbGwuIExldCB1cyBzZWUgd2hldGhlciB0aGlzIGhvbGRzIGluIHByYWN0aWNlIGJ5IGdldHRpbmcgdmlzdWFsIGNvbmZpcm1hdGlvbiBieSBtZWFucyBvZiBzb21lIHNjYXR0ZXIgcGxvdHMhDQoNCmBgYHtyfQ0KIyBmcyBpcyBhdmFpbGFibGUgaW4geW91ciB3b3JraW5nIGVudmlyb25tZW50DQpzdHIoZnMpDQojIFBlcmZvcm0gdGhlIHR3byBzaW5nbGUgcmVncmVzc2lvbnMgYW5kIHNhdmUgdGhlbSBpbiBhIHZhcmlhYmxlDQptb2RlbF95ZWFycyA8LSBsbShzYWxhcnl+eWVhcnMsZGF0YT1mcykNCm1vZGVsX3B1YnMgPC0gbG0oc2FsYXJ5fnB1YnMsZGF0YT1mcykNCg0KIyBQbG90IGJvdGggZW5oYW5jZWQgc2NhdHRlciBwbG90cyBpbiBvbmUgcGxvdCBtYXRyaXggb2YgMSBieSAyDQojIGZzIGlzIGF2YWlsYWJsZSBpbiB5b3VyIHdvcmtpbmcgZW52aXJvbm1lbnQNCnN0cihmcykNCiMgUGVyZm9ybSB0aGUgdHdvIHNpbmdsZSByZWdyZXNzaW9ucyBhbmQgc2F2ZSB0aGVtIGluIGEgdmFyaWFibGUNCm1vZGVsX3llYXJzIDwtIGxtKHNhbGFyeX55ZWFycyxkYXRhPWZzKQ0KbW9kZWxfcHVicyA8LSBsbShzYWxhcnl+cHVicyxkYXRhPWZzKQ0KDQojIFBsb3QgYm90aCBlbmhhbmNlZCBzY2F0dGVyIHBsb3RzIGluIG9uZSBwbG90IG1hdHJpeCBvZiAxIGJ5IDINCnBhcihtZnJvdyA9IGMoMSwgMikpDQpwbG90KGZzJHNhbGFyeSB+IGZzJHllYXJzLCBtYWluID0gInBsb3RfeWVhcnMiLCB4bGFiID0gJ3llYXJzJywgeWxhYiA9ICdzYWxhcnknKQ0KYWJsaW5lKG1vZGVsX3llYXJzKQ0KcGxvdChmcyRzYWxhcnkgfiBmcyRwdWJzLCBtYWluID0gJ3Bsb3RfcHVicycsIHhsYWIgPSAncHVicycsIHlsYWIgPSAnc2FsYXJ5JykNCmFibGluZShtb2RlbF9wdWJzKQ0KYGBgDQojIyMgTXVsdGlwbGUgcmVncmVzc2lvbjogbW9kZWwgc2VsZWN0aW9uDQoNCkluIHRoZSB2aWRlbywgUHJvZmVzc29yIENvbndheSB0YWxrZWQgYWJvdXQgdGhlIFIyDQpjb2VmZmljaWVudHMgb2YgcmVncmVzc2lvbiBtb2RlbHMuIFRoZXNlIGNvZWZmaWNpZW50cyBhcmUgb2Z0ZW4gdXNlZCBpbiBwcmFjdGljZSB0byBzZWxlY3QgdGhlIGJlc3QgcmVncmVzc2lvbiBtb2RlbCBpbiBjYXNlIG9mIGNvbXBldGluZyBtb2RlbHMuIFRoZSBSMiBjb2VmZmljaWVudCBvZiBhIHJlZ3Jlc3Npb24gbW9kZWwgaXMgZGVmaW5lZCBhcyB0aGUgcGVyY2VudGFnZSBvZiB0aGUgdmFyaWF0aW9uIGluIHRoZSBvdXRjb21lIHZhcmlhYmxlIHRoYXQgY2FuIGJlIGV4cGxhaW5lZCBieSB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyBvZiB0aGUgbW9kZWwuIEluIGdlbmVyYWwsIHRoZSBSMg0KDQpjb2VmZmljaWVudCBvZiBhIG1vZGVsIGluY3JlYXNlcyB3aGVuIG1vcmUgcHJlZGljdG9yIHZhcmlhYmxlcyBhcmUgYWRkZWQgdG8gdGhlIG1vZGVsLiBBZnRlciBhbGwsIGFkZGluZyBtb3JlIHByZWRpY3RvciB2YXJpYWJsZXMgdG8gdGhlIG1vZGVsIHRlbmRzIHRvIGluY3JlYXNlIHRoZSBvZGRzIG9mIGV4cGxhaW5pbmcgbW9yZSB2YXJpYXRpb24gaW4gdGhlIG91dGNvbWUgdmFyaWFibGUuDQoNCkNoZWNrIHRoaXMgZ2VuZXJhbGl0eSBieSBjb21wYXJpbmcgdGhlIFIyDQpjb2VmZmljaWVudCBvZiBhIHNpbmdsZSByZWdyZXNzaW9uIG1vZGVsIHdpdGggdGhhdCBvZiBhIG11bHRpcGxlIHJlZ3Jlc3Npb24gbW9kZWwuDQpgYGB7cn0NCiMgRG8gYSBzaW5nbGUgcmVncmVzc2lvbiBvZiBzYWxhcnkgb250byB5ZWFycyBvZiBleHBlcmllbmNlIGFuZCBjaGVjayB0aGUgb3V0cHV0DQptb2RlbF8xIDwtIGxtKGZzJHNhbGFyeSB+IGZzJHllYXJzKQ0Kc3VtbWFyeShtb2RlbF8xKQ0KDQojIERvIGEgbXVsdGlwbGUgcmVncmVzc2lvbiBvZiBzYWxhcnkgb250byB5ZWFycyBvZiBleHBlcmllbmNlIGFuZCBudW1iZXJzIG9mIHB1YmxpY2F0aW9ucyBhbmQgY2hlY2sgdGhlIG91dHB1dA0KbW9kZWxfMiA8LSBsbShmcyRzYWxhcnkgfiBmcyR5ZWFycyArIGZzJHB1YnMpDQpzdW1tYXJ5KG1vZGVsXzIpDQoNCiMgU2F2ZSB0aGUgUiBzcXVhcmVkIG9mIGJvdGggbW9kZWxzIGluIHByZWxpbWluYXJ5IHZhcmlhYmxlcw0KcHJlbGltaW5hcnlfbW9kZWxfMSA8LSBzdW1tYXJ5KG1vZGVsXzEpJHIuc3F1YXJlZA0KcHJlbGltaW5hcnlfbW9kZWxfMiA8LXN1bW1hcnkobW9kZWxfMikkci5zcXVhcmVkDQoNCiMgUm91bmQgdGhlbSBvZmYgd2hpbGUgeW91IHNhdmUgdGhlbSBpbiBuZXcgdmFyaWFibGVzDQpyX3NxdWFyZWQgPC0gYygpDQpyX3NxdWFyZWRbMV0gPC0gcm91bmQocHJlbGltaW5hcnlfbW9kZWxfMSwzKQ0Kcl9zcXVhcmVkWzJdIDwtIHJvdW5kKHByZWxpbWluYXJ5X21vZGVsXzIsMykNCg0KIyBQcmludCBvdXQgdGhlIHZlY3RvciB0byBzZWUgYm90aCBSIHNxdWFyZWQgY29lZmZpY2llbnRzDQpyX3NxdWFyZWQNCmBgYA0KDQojIyMgTXVsdGlwbGUgcmVncmVzc2lvbjogYmV3YXJlIG9mIHJlZHVuZGFuY3kNCg0KQWx0aG91Z2ggdGhlIFIyDQoNCmNvZWZmaWNpZW50IG9mIGEgcmVncmVzc2lvbiBtb2RlbCB0ZW5kcyB0byBpbmNyZWFzZSBpbiBnZW5lcmFsIHdoZW4gYWRkaW5nIG1vcmUgcHJlZGljdG9yIHZhcmlhYmxlcyB0byB0aGUgbWl4LCBtb3JlIGlzIG5vdCBhbHdheXMgYmV0dGVyLiBUaHJvd2luZyBpbiBldmVyeSB2YXJpYWJsZSBhdCB5b3VyIGRpc3Bvc2FsIGludG8gYSBtdWx0aXBsZSByZWdyZXNzaW9uIGlzIHVzdWFsbHkgbm90IGEgZ29vZCBpZGVhLiBJZiB0aGUgZXh0cmEgdmFyaWFibGVzIGRvIG5vdCBhZGQgZXh0cmEgdmFsdWUgdG8gdGhlIG1vZGVsIC13aGVuIHRoZSBjb3JyZXNwb25kaW5nIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGFyZSBub3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSB6ZXJvLSBpdCB0ZW5kcyB0byBiZSBhIGdvb2QgcHJhY3RpY2UgdG8ganVzdCBsZWF2ZSB0aGVtIG91dC4NCg0KSW4gdGhlIG5leHQgZXhhbXBsZSB5b3Ugd2lsbCBzZWUgdGhhdCBhZGRpbmcgdGhlIGFnZSBvZiB0aGUgcHJvZmVzc29ycyBhcyBhbiBleHRyYSBwcmVkaWN0b3IgdmFsdWUgZG9lcyBub3QgbmVjZXNzYXJpbHkgbGVhZCB0byBhIGJldHRlciBlY29ub21ldHJpYyBtb2RlbCwgZXZlbiB0aG91Z2ggdGhlIFIyDQpjb2VmZmljaWVudCBpbmNyZWFzZXMgaW4gdGhlIHByb2Nlc3MuIEhlbmNlLCBhIGdvb2Qgc3RhdGlzdGljaWFuIHNob3VsZCBub3QgYmxpbmRseSBkZXBlbmQgb24gdGhlIFIyIGNvZWZmaWNpZW50cyB3aGlsZSBzZWxlY3RpbmcgcmVsZXZhbnQgcmVncmVzc2lvbiBtb2RlbHMuDQpgYGB7cn0NCg0KIyBEbyBtdWx0aXBsZSByZWdyZXNzaW9uIGFuZCBjaGVjayB0aGUgcmVncmVzc2lvbiBvdXRwdXQNCm1vZGVsXzMgPC0gbG0oZnMkc2FsYXJ5IH4gZnMkeWVhcnMgKyBmcyRwdWJzICsgZnMkYWdlKQ0Kc3VtbWFyeShtb2RlbF8zKQ0KDQojIFJvdW5kIG9mZiB0aGUgUiBzcXVhcmVkIGNvZWZmaWNpZW50cyBhbmQgc2F2ZSB0aGUgcmVzdWx0IGluIHRoZSB2ZWN0b3IgKGluIG9uZSBzdGVwISkNCnJfc3F1YXJlZFszXSA8LSByb3VuZChzdW1tYXJ5KG1vZGVsXzMpJHIuc3F1YXJlZCwzKQ0KDQojIFByaW50IG91dCB0aGUgdmVjdG9yIGluIG9yZGVyIHRvIGRpc3BsYXkgYWxsIFIgc3F1YXJlZCBjb2VmZmljaWVudHMgc2ltdWx0YW5lb3VzbHkNCnJfc3F1YXJlZA0KYGBgDQoqQ2FuIHlvdSB0ZWxsIHRoYXQgdGhlIFIyIGNvZWZmaWNpZW50IGluZGVlZCBpbmNyZWFzZXMgd2hlbiBhZGRpbmcgYWdlIGFzIGFuIGV4dHJhIHByZWRpY3RvciB2YXJpYWJsZSB0byB0aGUgcmVncmVzc2lvbiBtb2RlbD8gVGhpcyB3b3VsZCBpbXBseSB0aGF0IHRoaXMgZXh0ZW5kZWQgbW9kZWwgaXMgcHJlZmVycmVkIGFjY29yZGluZyB0byB0aGUgUjIgY29lZmZpY2llbnQgY3JpdGVyaXVtLiBIb3dldmVyLCB0aGlzIGlzIG5vdCB0aGUgY2FzZSBhcyB0aGlzIG1vZGVsIGlzIHJlZHVuZGFudDogdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgZm9yIGFnZSB0ZW5kcyB0byBiZSBpbnNpZ25pZmljYW50LiBSZW1lbWJlciB0aGF0IGEgZ29vZCBzdGF0aXN0aWNpYW4gbm90IG9ubHkgbG9va3MgYXQgdGhlIFIyIGNvZWZmaWNpZW50IHdoZW4gc2VsZWN0aW5nIGEgcmVsZXZhbnQgcmVncmVzc2lvbiBtb2RlbCEqDQoNCiMjIyBNdWx0aXBsZSByZWdyZXNzaW9uOiBpbnRlcnByZXRhdGlvbiByZWdyZXNzaW9uIGNvbnN0YW50cw0KDQpDYW4geW91IGdpdmUgdGhlIGNvcnJlY3QgaW50ZXJwcmV0YXRpb24gb2YgdGhlIHJlZ3Jlc3Npb24gY29uc3RhbnQgaW4gYSBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVsPw0KPiBJdCBpcyB0aGUgcHJlZGljdGVkIHZhbHVlIGZvciB0aGUgb3V0Y29tZSB2YXJpYWJsZSB3aGVuIGFsbCBwcmVkaWN0b3IgdmFyaWFibGVzIGFyZSBzZXQgZXF1YWwgdG8gemVyby4NCg0KIyMjIE11bHRpcGxlIHJlZ3Jlc3Npb246IGludGVycHJldGF0aW9uIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzDQoNCk5vdyB0aGF0IHlvdSBhcmUgYW4gZXhwZXJ0IGluIHRoZSBkb21haW4gb2YgbXVsdGlwbGUgcmVncmVzc2lvbnMsIHJldGFrZSB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbiBtb2RlbCBvZiB0aGUgb3V0Y29tZSB2YXJpYWJsZSBzYWxhcnkgb24gcHJlZGljdG9yIHZhcmlhYmxlcyB5ZWFycyBhbmQgcHVicyBvbmNlIG1vcmUuIFRoZSByZXF1aXJlZCBkYXRhc2V0IGZzIGlzIHN0aWxsIHByZS1sb2FkZWQgaW50byBSLiBQcmludCBvdXQgdGhlIG91dHB1dCB0byBoZWxwIHlvdSB0byBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbiwgdGhhdCBlc3NlbnRpYWxseSBjb25zaXN0cyBvZiB0d28gcGFydHMuIElmIHlvdSBmZWVsIHVuc3VyZSBhYm91dCBkb2luZyB0aGlzLCByZXR1cm4gdG8gdGhlIGVhcmxpZXIgcXVlc3Rpb25zIHRvIHJldmlzZSB0aGUgYmFzaWNzIG9mIG11bHRpcGxlIHJlZ3Jlc3Npb24gYmVmb3JlIG1vdmluZyBvbi4NCg0KV2hpY2ggb2YgdGhlIGZvbGxvd2luZyBpcyB0aGUgY29ycmVjdCB2YWx1ZSBvZiB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudCBmb3IgcHVibGljYXRpb25zPyBBbmQgaG93IHdvdWxkIHlvdSBpbnRlcnByZXQgdGhpcyBudW1iZXI/DQo+IFRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGZvciBwdWJsaWNhdGlvbnMgaXMgZXF1YWwgdG8gNjM0LjkuIFRoaXMgbWVhbnMgdGhhdCB0aGUgcHJlZGljdGVkIGluY3JlYXNlIGluIHNhbGFyeSBmb3IgYSB1bml0IGluY3JlYXNlIGluIHRoZSBudW1iZXIgb2YgcHVibGljYXRpb25zIGlzIGVxdWFsIHRvICQ2MzQuOTAgZm9yIHRob3NlIHByb2Zlc3NvcnMgd2hvIGhhdmUgYWxyZWFkeSBjb21wbGV0ZWQgdGhlaXIgUGhEIGFuIGF2ZXJhZ2UgYW1vdW50IG9mIHllYXJzIGFnby4NCg0KYGBge3J9DQpsaWJyYXJ5KCdRdWFudFBzeWMnKQ0KbG0uYmV0YShtb2RlbF8yKQ0KYGBgDQojIyBJbnR1aXRpb24gYmVoaW5kIGVzdGltYXRpb24gb2YgbXVsdGlwbGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMNCiMjIyBEZWZpbml0aW9uIG9mIG1hdHJpY2VzDQoNClRoaXMgY2hhcHRlciB3aWxsIGhlbHAgdG8gdW5kZXJzdGFuZCBob3cgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBhbmQgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYXJlIGNvbnN0cnVjdGVkLiBUbyB0aGlzIHB1cnBvc2UsIHlvdSB3aWxsIGZpcnN0IHJlZnJlc2ggYW5kIHByYWN0aWNlIHNvbWUgYmFzaWMgbWF0cml4IGNvbmNlcHRzLiBCeSBnb2luZyB0aHJvdWdoIHRoZSBleGVyY2lzZXMsIHlvdSB3aWxsIGFjcXVhaW50IHlvdXJzZWxmIHdpdGggdGhlIGltcGxlbWVudGF0aW9uIG9mIHRoZSB0aGVvcmV0aWNhbCBjb25jZXB0cyBpbiBSIGFuZCBiZWNvbWUgZmFtaWxpYXIgd2l0aCB0aGUgc3RlcC1ieS1zdGVwIGNvbnN0cnVjdGlvbiBvZiB0aGUgY29ycmVsYXRpb24gbWF0cml4IGFuZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4NCg0KQSBtYXRyaXggaXMgYSByZWN0YW5ndWxhciB0YWJsZSB0aGF0IGNvbnRhaW5zIGtub3duIG9yIHVua25vd24gbnVtYmVycy4gVGhlIHNpemUsIG9yIG9yZGVyLCBvZiB0aGUgdGFibGUgaXMgZ2l2ZW4gYnkgdGhlIG51bWJlciBvZiByb3dzIGFuZCBjb2x1bW5zIGl0IGNvbnRhaW5zLiBGb3IgZXhhbXBsZSwgbWF0cml4IG0gY29uc2lzdHMgb2YgNCByb3dzIGFuZCAyIGNvbHVtbnMsIHdoaWNoIG1lYW5zIHRoYXQgdGhlIG9yZGVyIG9mIG1hdHJpeCBtIGlzIDR4Mi4NCjcyNDY44o6k4o6m4o6l4o6l4o6lDQpgYGB7cn0NCm0gPC0gbWF0cml4KDE6OCxuY29sPTIsYnlyb3cgPSBUKQ0KbQ0KYGBgDQoNCkEgdHJpY2sgdGhhdCBpcyBvZnRlbiB1c2VkIGluIG1hdHJpeCBhbGdlYnJhIGlzIHRha2luZyB0aGUgdHJhbnNwb3NlIG9mIGEgbWF0cml4LiBUaGUgdHJhbnNwb3NlIG9mIGEgbWF0cml4IGlzIGNvbnN0cnVjdGVkIGJ5IHJld3JpdGluZyBpdHMgcm93cyBhcyBjb2x1bW5zLiBBcyBhIHJlc3VsdCwgdGhlIG9yZGVyIHdpbGwgY2hhbmdlIGFzIHRoZSBtYXRyaXggd2lsbCBiZSByZXZlcnNlZC4gRm9yIGV4YW1wbGUsIHRha2luZyB0aGUgdHJhbnNwb3NlIG9mIG1hdHJpeCBtIHJlc3VsdHMgaW4gYSAyeDQgbWF0cml4DQpgYGB7cn0NCnQobSkNCmBgYA0KDQoNClN0YXJ0IGJ5IGNyZWF0aW5nIGFuZCBkZWZpbmluZyBtYXRyaWNlcyByIGFuZCBzIHRvIGJlIHVzZWQgaW4gdGhlIG5leHQgZXhlcmNpc2VzLiBVc2UgdGhlIFIgdGVybWluYWwgdG8gcGVyZm9ybSB0aGVzZSBhY3Rpb25zLg0KYGBge3J9DQojIENvbnN0cnVjdGlvbiBvZiAzIGJ5IDggbWF0cml4IHIgdGhhdCBjb250YWlucyB0aGUgbnVtYmVycyAxIHVwIHRvIDI0DQpyIDwtIG1hdHJpeCgxOjI0LCBucm93PTMsIG5jb2w9OCkNCnINCiMgQ29uc3RydWN0aW9uIG9mIDMgYnkgOCBtYXRyaXggcyB0aGF0IGNvbnRhaW5zIHRoZSBudW1iZXJzIDIxIHVwIHRvIDQ0DQpzIDwtIG1hdHJpeCgyMTo0NCwgbnJvdz0zLCBuY29sPTgpDQpzDQojIFRha2UgdGhlIHRyYW5zcG9zZSB0IG9mIG1hdHJpeCByDQp0IDwtIHQocikNCnQNCmBgYA0KIyMjIEFkZGl0aW9uLCBzdWJ0cmFjdGlvbiBhbmQgbXVsdGlwbGljYXRpb24gb2YgbWF0cmljZXMNCg0KWW91IGNhbiBwZXJmb3JtIG9wZXJhdGlvbnMgb24gbWF0cmljZXMuIFNvbWUgb3BlcmF0aW9ucyBwdXQgZm9yd2FyZCBzb21lIHJlcXVpcmVtZW50cyB0aGF0IG5lZWQgdG8gYmUgcmVzcGVjdGVkLiBGb3IgZXhhbXBsZSwgdGhlIGFkZGl0aW9uIG9yIHN1YnRyYWN0aW9uIG9mIHR3byBtYXRyaWNlcyBpcyBvbmx5IHBvc3NpYmxlIGlmIHRoZSBtYXRyaWNlcyBhcmUgb2YgdGhlIHNhbWUgc2l6ZSBvciBvcmRlci4gRWxlbWVudHMgd2l0aCB0aGUgc2FtZSByb3cgYW5kIGNvbHVtbiBudW1iZXIgaW4gdGhlIHR3byBtYXRyaWNlcyBhcmUgYWRkZWQgb3Igc3VidHJhY3RlZCBhbmQgbGVhZCB0byBhIG5ldyBtYXRyaXguDQoNClRoZSBtdWx0aXBsaWNhdGlvbiBvZiB0d28gbWF0cmljZXMgaXMgb25seSBwb3NzaWJsZSB3aGVuIHRoZXkgYXJlIGNvbmZvcm1hYmxlOiB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgb2YgdGhlIGZpcnN0IG1hdHJpeCBtdXN0IGVxdWFsIHRoZSBudW1iZXIgb2Ygcm93cyBvZiB0aGUgc2Vjb25kIG1hdHJpeDoNCj4gUj1NVOKLhU4NCg0Kd2l0aCBNIGEgay1ieS1pIG1hdHJpeCBhbmQgTiBhIGstYnktaSBtYXRyaXguIE1hdHJpeCBNIGFuZCBOIGNhbiBub3QgYmUgbXVsdGlwbGllZCBzaW5jZSBib3RoIG1hdHJpY2VzIGhhdmUgYW4gaWRlbnRpY2FsIG9yZGVyLiBUYWtpbmcgdGhlIHRyYW5zcG9zZSBvZiBtYXRyaXggTSBsZWFkcyB0byBhIHNvbHV0aW9uOiB0aGUgdHdvIG1hdHJpY2VzIGFyZSBjb25mb3JtYWJsZS4NCmBgYHtyfQ0KDQojIENvbXB1dGUgdGhlIHN1bSBvZiBtYXRyaWNlcyByIGFuZCBzDQpvcGVyYXRpb25fMSA8LSByICsgcw0KDQojIENvbXB1dGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBtYXRyaWNlcyByIGFuZCBzDQpvcGVyYXRpb25fMiA8LSByIC0gcw0KDQojIE11bHRpcGx5IG1hdHJpY2VzIHQgYW5kIHMNCm9wZXJhdGlvbl8zIDwtIHQgJSolIHMNCmBgYA0KDQojIyMgUm93IHZlY3RvciBvZiBzdW1zDQoNCkluIHRoZSBwcmV2aW91cyB2aWRlbywgUHJvZmVzc29yIENvbndheSBleHBsYWluZWQgaG93IHlvdSBjYW4gZ28gZnJvbSBhIHJhdyBkYXRhZnJhbWUgdG8gYSBjb3JyZWxhdGlvbiBtYXRyaXguIEhlcmUgeW91IHdpbGwgY29uc3RydWN0IHN1Y2ggYSBjb3JyZWxhdGlvbiBtYXRyaXggeW91cnNlbGYgZnJvbSBzY3JhdGNoLg0KDQpJbiB5b3VyIFIgd29ya3NwYWNlLCBhIHJhdyBkYXRhZnJhbWUgWCAoYSAxMC1ieS0zIG1hdHJpeCkgaXMgYWxyZWFkeSBsb2FkZWQgaW4uIFRoZSBmaXJzdCBzdGVwIGlzIHRvIHN1bSB1cCBpdHMgMyBjb2x1bW5zLiBJbiBvdGhlciB3b3Jkcywgd2UgbmVlZCB0byBjb25zdHJ1Y3QgdGhlIHJvdyB2ZWN0b3Igb2Ygc3VtczoNCg0KPiBUMXA9MTFuWG5wDQoNCndoZXJlIFQxcA0KdGhlIDEtYnktcCByb3cgdmVjdG9yIG9mIHN1bXMgaXMsIFhucCBhbiBuLWJ5LXAgcmF3IGRhdGFmcmFtZSwgYW5kIDExbiBhIDEtYnktbiBtYXRyaXggb2Ygb25lcy4NCmBgYHtyfQ0KaXJpc2xlbiA8LSBucm93KGlyaXMpDQpYIDwtIGFzLm1hdHJpeChpcmlzWywxOjNdKQ0KaGVhZChYKQ0KSSA9IG1hdHJpeChyZXAoMSxpcmlzbGVuKSxuY29sID0gaXJpc2xlbiwgbnJvdyA9IDEpDQp0X21hdCA8LSBJICUqJSBYDQpwcmludCgiU3VtIG9mIGNvbHVtbnMiKQ0KdF9tYXQNCmBgYA0KIyMjIFJvdyB2ZWN0b3Igb2YgbWVhbnMgYW5kIG1hdHJpeCBvZiBtZWFucw0KDQpOb3cgdGhhdCB5b3UgaGF2ZSB0aGUgcm93IHZlY3RvciBvZiBzdW1zIHRfbWF0LCBhIDEtYnktMyBtYXRyaXgsIHlvdSBhcmUgcmVhZHkgdG8gY29uc3RydWN0IHRoZSByb3cgdmVjdG9yIG9mIG1lYW5zIE0gdmlhOg0KDQo+IE0xcD1UMXBO4oiSMQ0KDQp3aXRoIE0xcA0KdGhlIDEtYnktcCByb3cgdmVjdG9yIG9mIG1lYW5zLCBUMXAgdGhlIDEtYnktcCByb3cgdmVjdG9yIG9mIHN1bXMsIGFuZCBO4oiSMQ0KDQp0aGUgaW52ZXJzZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZvciBlYWNoIHZhcmlhYmxlLg0KDQpHaXZlbiB0aGUgcm93IHZlY3RvciBvZiBtZWFucyBNLCB5b3UgY2FuIGFsc28gY29uc3RydWN0IHRoZSBtYXRyaXggb2YgbWVhbnMgTU0gYnkgbXVsdGlwbHlpbmcgdGhlIHJvdyB2ZWN0b3Igb2YgbWVhbnMgd2l0aCBhIGNvbHVtbiB2ZWN0b3I6DQoNCk1NbnA9MW4xTTFwDQoNCndpdGggTU1ucA0KdGhlIG4tYnktcCBtYXRyaXggb2YgbWVhbnMsIE0xcCB0aGUgMS1ieS1wIHJvdyB2ZWN0b3Igb2YgbWVhbnMsIGFuZCAxbjEgYSBuLWJ5LTEgY29sdW1uIHZlY3Rvci4NCmBgYHtyfQ0KIyBOdW1iZXIgb2Ygb2JzZXJ2YXRpb25zDQpuID0gaXJpc2xlbg0KDQojIENvbXB1dGUgdGhlIHJvdyB2ZWN0b3Igb2YgbWVhbnMNCk0gPC0gdF9tYXQgKiBuXi0xDQoNCiMgQ29uc3RydWN0aW9uIG9mIDEwIGJ5IDEgbWF0cml4IEogb2Ygd2hpY2ggdGhlIGVsZW1lbnRzIGFyZSBhbGwgMQ0KSiA8LSBtYXRyaXgocmVwKDEsbiksIG5yb3c9biwgbmNvbD0xKQ0KDQojIENvbXB1dGUgdGhlIG1hdHJpeCBvZiBtZWFucw0KTU0gPC0gSiAlKiUgTQ0KYGBgDQoNCiMjIyBNYXRyaXggb2YgZGV2aWF0aW9uIHNjb3Jlcw0KDQpXaHkgZG8geW91IG5lZWQgYSBtYXRyaXggb2YgbWVhbnM/IEJlY2F1c2UgeW91IHdhbnQgYSBtYXRyaXggb2YgZGV2aWF0aW9uIHNjb3JlcyEgWW91IGFyZSBpbnRlcmVzdGVkIGluIHRoZSBkZXZpYXRpb24gb2YgYW4gb2JzZXJ2YXRpb24gZm9yIHRoYXQgdmFyaWFibGUgdG8gaXRzIG1lYW4uDQoNCkFzIFByb2YuIENvbndheSBzaG93ZWQgaW4gdGhlIHZpZGVvLCB0aGUgZm9ybXVsYSB0byBjYWxjdWxhdGUgYSBtYXRyaXggb2YgZGV2aWF0aW9uIHNjb3JlcyBEIGlzOg0KDQo+IERucD1YbnDiiJJNTW5wDQoNCndoZXJlIERucA0KaXMgYW4gbi1ieS1wIGRldmlhdGlvbiBtYXRyaXgsIFhucCB0aGUgbi1ieS1wIHJhdyBkYXRhZnJhbWUsIGFuZCBNTW5wIHRoZSBuLWJ5LXAgbWF0cml4IG9mIG1lYW5zICh0aGUgb25lIHlvdSBjYWxjdWxhdGVkIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSkuDQoNCmBgYHtyfQ0KIyBNYXRyaXggb2YgZGV2aWF0aW9uIHNjb3JlcyBEDQpEIDwtIFggLSBNTQ0KdGFpbChEKQ0KYGBgDQoNCiMjIyBTdW0gb2Ygc3F1YXJlcyBhbmQgc3VtIG9mIGNyb3NzIHByb2R1Y3RzIG1hdHJpeA0KDQpPaywgc28geW91IGhhdmUgcmVhY2hlZCB0aGUgcmVhbGx5IGNvb2wgcGFydCBub3cgOy0pDQoNCklmIHlvdSBub3cgdGFrZSB5b3VyIG1hdHJpeCBvZiBkZXZpYXRpb24gc2NvcmVzIEQgYW5kIG11bHRpcGx5IGl0IHdpdGggaXRzIHRyYW5zcG9zZSwganVzdCBsaWtlIHByb2YuIENvbndheSBkaWQgaW4gdGhlIHZpZGVvLCB5b3UgZ2V0IHRoZSBtYXRyaXggb2Ygc3VtIG9mIHNxdWFyZXMgYW5kIHN1bSBvZiBjcm9zcyBwcm9kdWN0cyBTLiBUaGUgZm9ybXVsYSBpczoNCg0KPiBTeHg9RFRwbi5EbnANCg0Kd2l0aCBTWFgNCnRoZSBtYXRyaXggb2Ygc3VtIG9mIHNxdWFyZXMgYW5kIHN1bSBvZiBjcm9zcyBwcm9kdWN0cywgRFRwbiB0aGUgdHJhbnNwb3NlIG9mIHRoZSBkZXZpYXRpb24gbWF0cml4LCBhbmQgRG5wIHRoZSBuLWJ5LXAgZGV2aWF0aW9uIG1hdHJpeC4NCg0KYGBge3J9DQojIFN1bSBvZiBzcXVhcmVzIGFuZCBzdW0gb2YgY3Jvc3MgcHJvZHVjdHMgbWF0cml4DQpTIDwtIHQoRCkgJSolIEQNClMNCmRpYWcoUykNCmBgYA0KPiBOb3RlIHRoYXQgdGhlIHN1bSBvZiBzcXVhcmVzIGFyZSBpbiB0aGUgZGlhZ29uYWwsIGFuZCB0aGUgc3VtIG9mIGNyb3NzIHByb2R1Y3RzIGFjcm9zcyB0aGUgZGlhZ29uYWwuDQoNCiMjIyBDYWxjdWxhdGluZyB0aGUgY29ycmVsYXRpb24gbWF0cml4DQoNCkFsbW9zdCBhdCB0aGUgZmluaXNoIGxpbmUhIFVzaW5nIHRoZSBzdW1zIG9mIHNxdWFyZXMgYW5kIHRoZSBzdW1zIG9mIGNyb3NzIHByb2R1Y3RzIG1hdHJpeCBTIHlvdSBjYW4gY2FsY3VsYXRlIHRoZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCBDOg0KDQo+IENYWD1TWFhO4oiSMQ0KDQp3aXRoIENYWA0KdGhlIHZhcmlhbmNlLWNvdmFyaWFuY2UgbWF0cml4LCBTWFggdGhlIG1hdHJpeCBvZiBzdW0gb2Ygc3F1YXJlcyBhbmQgc3VtIG9mIGNyb3NzIHByb2R1Y3RzLCBhbmQgTuKIkjENCg0KdGhlIGludmVyc2UgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBmb3IgZWFjaCB2YXJpYWJsZS4NCg0KTmV4dCwgeW91IGNhbiBzdGFuZGFyZGl6ZSB0aGlzIHZhcmlhbmNlLWNvdmFyaWFuY2UgbWF0cml4IGJ5IG11bHRpcGx5aW5nIGl0IHdpdGggdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBtYXRyaXggU0QuIFRoaXMgZ2l2ZXMgdXMgdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBSOg0KDQo+IFJYWD0oU0RYWCniiJIxQ1hYKFNEWFgp4oiSMQ0KDQp3aXRoIFJYWA0KdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCwgQ1hYIHRoZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCwgYW5kIFNEWFggdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBtYXRyaXguDQpgYGB7cn0NCiAgIyBDb25zdHJ1Y3QgdGhlIHZhcmlhbmNlLWNvdmFyaWFuY2UgbWF0cml4DQpDIDwtIFMgKiBuXi0xDQoNCiMgR2VuZXJhdGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbnMgbWF0cml4DQpTRCA8LSBkaWFnKHggPSBkaWFnKEMpXigxLzIpLCBucm93ID0gMywgbmNvbCA9IDMpDQoNCiMgQ29tcHV0ZSB0aGUgY29ycmVsYXRpb24gbWF0cml4DQpSIDwtIHNvbHZlKFNEKSAlKiUgQyAlKiUgc29sdmUoU0QpDQpSDQpoZWFkKFgpDQpgYGANCg0KIyMgRHVtbXkgY29kaW5nDQoNCiMjIyBTdGFydGluZyBvZmYNCg0KVGhpcyBjaGFwdGVyIHdpbGwgdGVhY2ggeW91IG11bHRpcGxlIG1ldGhvZHMgdG8gdHJhbnNmb3JtIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBpbnRvIG51bWVyaWNhbCB2YXJpYWJsZXMgYnkgbWVhbnMgb2YgZHVtbXkgY29kaW5nLiBBcyBzdWNoLCB5b3Ugd2lsbCBiZSBhYmxlIHRvIHVzZSB0aGVzZSAiZHVtbWllcyIgYWxvbmdzaWRlIG90aGVyIG51bWVyaWNhbCB2YXJpYWJsZXMgaW4gbXVsdGlwbGUgcmVncmVzc2lvbnMuIEFkZGl0aW9uYWxseSwgYSBzcGVjaWFsIHZlcnNpb24gb2YgZHVtbXkgY29kaW5nIGNhbGxlZCAiZWZmZWN0cyBjb2RpbmciIHdpbGwgYWxzbyBiZSBpbnRyb2R1Y2VkIGF0IHRoZSBlbmQgb2YgdGhlIGNoYXB0ZXIuDQoNCkFnYWluLCB5b3Ugd2lsbCB1c2UgdGhlIGRhdGFzZXQgZnMuIEJlc2lkZXMgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlLCB5ZWFybHkgd2FnZXMgKHNhbGFyeSksIGFuZCBvdGhlciBjaGFyYWN0ZXJpc3RpY3Mgb2YgcHJvZmVzc29ycyBhdCBhIGNlcnRhaW4gdW5pdmVyc2l0eSwgdGhpcyBkYXRhc2V0IGFsc28gY29udGFpbnMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSAoZGVwdCksIHRoYXQgaG9sZHMgdGhlIGluZm9ybWF0aW9uIG9uIHRoZSBkZXBhcnRtZW50IHRoYXQgZWFjaCBwcm9mZXNzb3IgYmVsb25ncyB0by4gVGhlcmUgYXJlIHRocmVlIGRlcGFydG1lbnRzOiBoaXN0b3J5IChoKSwgcHN5Y2hvbG9neSAocCkgYW5kIHNvY2lvbG9neSAocykuDQoNCkFzIGFsd2F5cywgc3RhcnQgb2ZmIGJ5IGV4cGxvcmluZyB0aGUgZGF0YSBvZiBlYWNoIGRlcGFydG1lbnQgaW4gb3JkZXIgdG8gbWFrZSBzdXJlIHRoYXQgeW91IGdldCBhIGdvb2QgdmlldyBvZiB0aGUgZGF0YSB0aGF0IHlvdSBhcmUgd29ya2luZyB3aXRoLg0KDQpgYGB7cn0NCmxpYnJhcnkocHN5Y2gpDQpkZXNjcmliZUJ5KGZzLCBmcyRkZXB0KQ0KYGBgDQo+IENhbiB5b3UgYWxzbyB0ZWxsIHdoaWNoIGRlcGFydG1lbnQgaGFzIHRoZSBsYXJnZXN0IG51bWJlciBvZiBwcm9mZXNzb3JzPyANCg0KIyMjIENyZWF0aW5nIGR1bW15IHZhcmlhYmxlcyAoMSkNCg0KSW4gb3JkZXIgdG8gYXV0b21hdGljYWxseSBjcmVhdGUgZHVtbXkgdmFyaWFibGVzLCB0aGUgZHVtbXkuY29kZSgpIGZ1bmN0aW9uIG9mIHRoZSBwc3ljaCBwYWNrYWdlIGlzIGVhc3kgdG8gdXNlLg0KDQpUaGUgZnVuY3Rpb24gdGFrZXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhcyBhcmd1bWVudCBhbmQgYXV0b21hdGljYWxseSBjcmVhdGVzIHRoZSByZXF1aXJlZCBkdW1teSB2YXJpYWJsZXM6IGFsbCBsZXZlbHMgYXJlIHJhbmtlZCBhbHBoYWJldGljYWxseSBhbmQgdGhlIGZpcnN0IG9uZSBpcyB0YWtlbiBhcyB0aGUgcmVmZXJlbmNlIGdyb3VwLiBSZW1lbWJlciB0aGF0IG9ubHkgKE4tMSkgZHVtbWllcyBhcmUgY3JlYXRlZCBmb3IgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIE4gbGV2ZWxzLiBDb25zZXF1ZW50bHksIHRoZSBjYXRlZ29yeSB3aGljaCBpcyBub3QgZGlyZWN0bHkgbGlua2VkIHdpdGggYSBkdW1teSB2YXJpYWJsZSBpcyBkZWZpbmVkIGFzIHRoZSByZWZlcmVuY2UgY2F0ZWdvcnkuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgdGhlIGR1bW15IHZhcmlhYmxlcw0KZGVwdF9jb2RlIDwtIGR1bW15LmNvZGUoZnMkZGVwdCkNCmhlYWQoZGVwdF9jb2RlKQ0KIyBNZXJnZSB0aGUgZGF0YXNldCBpbiBhbiBleHRlbmRlZCBkYXRhZnJhbWUNCmV4dGVuZGVkX2ZzIDwtIGNiaW5kKGRlcHRfY29kZSxmcykNCg0KIyBMb29rIGF0IHRoZSBleHRlbmRlZCBkYXRhZnJhbWUNCmV4dGVuZGVkX2ZzDQoNCiMgUHJvdmlkZSBzdW1tYXJ5IHN0YXRpc3RpY3MNCnN1bW1hcnkoZXh0ZW5kZWRfZnMpDQpgYGANCj4gQmVmb3JlIHN3aXRjaGluZyB0byBzb21lIG1vcmUgYWR2YW5jZWQgZXhlcmNpc2VzLCB0aGluayBhYm91dCB0aGUgc3VtbWFyeSByZXN1bHRzIGZvciB0aGUgZXh0ZW5kZWRfZnMgZGF0YWZyYW1lLiBJcyB0aGVyZSBhIHdheSB0byBzZWUgd2hpY2ggZGVwYXJ0bWVudCBoYXMgdGhlIGxhcmdlc3QgZmFjdWx0eSBzdGFmZiB1c2luZyB0aGUgZHVtbXkgdmFyaWFibGVzPyBBbm90aGVyIHdheSB0byBsb29rIHVwIHdoaWNoIGRlcGFydG1lbnQgdGVuZHMgdG8gaGF2ZSB0aGUgaGlnaGVzdCBudW1iZXIgb2YgcHJvZmVzc29ycyBpcyB0byBsb29rIGF0IHRoZSBtZWFuIG9mIHRoZSBkdW1teSB2YXJpYWJsZXMgYW5kIHRvIG11bHRpcGx5IGl0IGJ5IDEwMC4gQWZ0ZXIgYWxsLCB0aGUgbWVhbiBvZiBlYWNoIGR1bW15IHZhcmlhYmxlIGlzIGVxdWFsIHRvIHRoZSBzdW0gb2Ygb25lIHRpbWVzIHRoZSBudW1iZXIgb2YgcHJvZmVzc29ycyBpbiBhIGNlcnRhaW4gZGVwYXJ0bWVudCwgZGl2aWRlZCBieSB0aGUgdG90YWwgbnVtYmVyIG9mIHByb2Zlc3NvcnMgKD0xMDApLiANCg0KIyMjIENyZWF0aW5nIGR1bW15IHZhcmlhYmxlcyAoMikNCg0KSW4gb3JkZXIgdG8gaW5jbHVkZSBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGluIGEgcmVncmVzc2lvbiwgdGhlIHZhcmlhYmxlIG5lZWRzIHRvIGJlIGNvbnZlcnRlZCBpbnRvIGEgbnVtZXJpYyB2YXJpYWJsZSBieSB0aGUgbWVhbnMgb2YgYSBkdW1teSB2YXJpYWJsZS4gUHJldmlvdXNseSwgZHVtbXkgdmFyaWFibGVzIGhhdmUgYmVlbiBnZW5lcmF0ZWQgdXNpbmcgdGhlIGludHVpdGl2ZSwgYnV0IGxlc3MgZ2VuZXJhbCBkdW1teS5jb2RlKCkgZnVuY3Rpb24gZnJvbSB0aGUgcHN5Y2ggbGlicmFyeS4NCg0KRnJvbSB0aGlzIHBvaW50IG9ud2FyZHMgdGhlIGNvbnRyYXN0IEMoKSBmdW5jdGlvbiBpcyB1c2VkIHRvIGNyZWF0ZSBkdW1teSB2YXJpYWJsZXMuIERvIG5vdCBjb25mdXNlIHRoaXMgZnVuY3Rpb24gd2l0aCB0aGUgYygpIGZ1bmN0aW9uIHRoYXQgaXMgdXNlZCB0byBjb21iaW5lIHZhbHVlcyBpbiBhIHZlY3RvciBvciBsaXN0LiBUaGUgY29udHJhc3QgQygpIHRha2VzIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgYXMgYSBmaXJzdCBhcmd1bWVudCBhbmQgdGhlIHRyZWF0bWVudCBhcyBhIHNlY29uZCBhcmd1bWVudC4gVGhlIGxhdHRlciB0ZWxscyBSIHRvIHJhbmsgYWxsIGxldmVscyBhbHBoYWJldGljYWxseSBhbmQgdG8gdGFrZSB0aGUgZmlyc3QgY2F0ZWdvcnkgYXMgdGhlIHJlZmVyZW5jZSBncm91cC4NCg0KVGhpcyBleGVyY2lzZSB3aWxsIGlsbHVzdHJhdGUgdGhlIGluY2x1c2lvbiBvZiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgZGVwdCBpbiBhIG11bHRpcGxlIHJlZ3Jlc3Npb24uIFRoZSBjb2RlIG9uIHRoZSByaWdodCBlc3RpbWF0ZXMgdGhlIHJlZ3Jlc3Npb24gd2l0aG91dCBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gVGhlIHN1bW1hcnkoKSBmdW5jdGlvbiBpcyB1c2VkIHRvIGdldCB0aGUgc3VtbWFyeSBvZiB0aGUgcmVncmVzc2lvbiByZXN1bHRzIG9mIG1vZGVsIGFuZCB0aGUgY29uZmludCgpIGZ1bmN0aW9uIGlzIHVzZWQgdG8gY3JlYXRlIHRoZSBjb25maWRlbmNlIGludGVydmFscy4NCmBgYHtyfQ0KDQojIFJlZ3Jlc3Mgc2FsYXJ5IGFnYWluc3QgeWVhcnMgYW5kIHB1YmxpY2F0aW9ucw0KbW9kZWwgPC0gbG0oZnMkc2FsYXJ5IH4gZnMkeWVhcnMgKyBmcyRwdWJzKQ0KDQojIEFwcGx5IHRoZSBzdW1tYXJ5IGZ1bmN0aW9uIHRvIGdldCBzdW1tYXJpemVkIHJlc3VsdHMgZm9yIG1vZGVsDQpzdW1tYXJ5KG1vZGVsKQ0KDQojIENvbXB1dGUgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciBtb2RlbA0KY29uZmludChtb2RlbCkNCg0KIyBDcmVhdGUgZHVtbWllcyBmb3IgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGZzJGRlcHQgYnkgdXNpbmcgdGhlIEMoKSBmdW5jdGlvbg0KZGVwdF9jb2RlIDwtIEMoZnMkZGVwdCwgY29udHIucG9seSkNCmRlcHRfY29kZQ0KIyBSZWdyZXNzIHNhbGFyeSBhZ2FpbnN0IHllYXJzLCBwdWJsaWNhdGlvbnMgYW5kIGRlcGFydG1lbnQNCm1vZGVsX2R1bW15IDwtIGxtKGZzJHNhbGFyeSB+IGZzJHllYXJzICsgZnMkcHVicyArIGRlcHRfY29kZSkNCg0KIyBBcHBseSB0aGUgc3VtbWFyeSBmdW5jdGlvbiB0byBnZXQgc3VtbWFyaXplZCByZXN1bHRzIGZvciBtb2RlbF9kdW1teQ0Kc3VtbWFyeShtb2RlbF9kdW1teSkNCg0KIyBDb21wdXRlIHRoZSBjb25maWRlbmNlIGludGVydmFscyBmb3IgbW9kZWxfZHVtbXkNCmNvbmZpbnQobW9kZWxfZHVtbXkpDQpgYGANCiMjIyBTb21lIGluZm8gZnJvbSBkYXRhDQpgYGB7cn0NCmNoaXNxLnRlc3QoZGVwdF9jb2RlLGZzJHNhbGFyeSkNCnZhcmlhbmNlIDwtIGFvdihmcyRzYWxhcnkgfiBmcyRkZXB0KQ0Kc3VtbWFyeSh2YXJpYW5jZSkNCnR1a2V5IDwtIFR1a2V5SFNEKHZhcmlhbmNlKQ0KdHVrZXkNCnZhcmlhbmNlMiA8LSBhb3Yoc2FsYXJ5IH4geWVhcnMgKiBwdWJzICogZGVwdCwgZGF0YT0gZnMpDQpzdW1tYXJ5KHZhcmlhbmNlMikNCmBgYA0KDQoNCg0KIyMjIE1vZGVsIHNlbGVjdGlvbjogQU5PVkENCg0KSW4gYSBuZXh0IHN0ZXAsIHdlIHdvdWxkIGxpa2UgdG8gdGVzdCBpZiB0aGUgaW5jbHVzaW9uIG9mIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBpbiB0aGUgbW9kZWwgaW1wcm92ZXMgdGhlIGZpdC4gVGhlIGRhdGFzZXQgZnMgYW5kIHJlZ3Jlc3Npb25zIG1vZGVsIGFuZCBtb2RlbF9kdW1teSBhcmUgYXZhaWxhYmxlIGluIHlvdXIgd29ya3NwYWNlLiBUaGUgYW5vdmEoKSBmdW5jdGlvbiBjb21wYXJlcyBib3RoIG1vZGVscyBhbmQgcmVwb3J0cyB3aGV0aGVyIG9yIG5vdCB0aGUgbW9kZWxzIGRpZmZlciBpbiBhIHNpZ25pZmljYW50IHdheS4NCg0KSWYgdGhlIGluY2x1c2lvbiBvZiB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgZWZmZWN0aXZlbHkgaW1wcm92ZXMgdGhlIGZpdCwgb25lIG1pZ2h0IHdvbmRlciB0byB3aGljaCBleHRlbnQgdGhlIHNwZWNpZmljIGRlcGFydG1lbnRzIGRpZmZlciBmcm9tIGVhY2ggb3RoZXIuDQpgYGB7cn0NCiMgQ29tcGFyZSBtb2RlbCA0IHdpdGggbW9kZWwgMw0KDQphbm92YShtb2RlbCwgbW9kZWxfZHVtbXkpDQoNCmBgYA0KIyMjIE1vZGVsIHNlbGVjdGlvbjogaW50ZXJwcmV0YXRpb24NCg0KUmV0cmlldmUgdGhlIG91dHB1dCBvZiB0aGUgcHJldmlvdXMgZXhlcmNpc2UgYnkgbWVhbnMgb2YgdGhlIGFub3ZhKCkgZnVuY3Rpb24uIEJhc2VkIG9uIHRoaXMgb3V0cHV0LCBjYW4geW91IHRlbGwgd2hldGhlciB0aGUgaW5jbHVzaW9uIG9mIHRoZSBkZXBhcnRtZW50IGR1bW15IHZhcmlhYmxlIGRlcHRfY29kZSBzaWduaWZpY2FudGx5IGltcHJvdmVzIHRoZSBmaXQgb2YgdGhlIG1vZGVsPyBBbmQgd2h5Pw0KPiBUaGUgaW5jbHVzaW9uIG9mIGRlcGFydG1lbnQgc2lnbmlmaWNhbnRseSBpbXByb3ZlcyB0aGUgZml0IG9mIHRoZSBtb2RlbCBhcyBpdCByZXN1bHRzIGluIGEgc2lnbmlmaWNhbnQgcC12YWx1ZSBhdCBhIDUgJSBzaWduaWZpY2FuY2UgbGV2ZWwuDQoNCiMjIyBEaXNjcmVwYW5jeSBiZXR3ZWVuIGFjdHVhbCBhbmQgcHJlZGljdGVkIG1lYW5zDQoNClRvIHNlZSB3aGF0IHJvbGUgdGhhdCB0aGUgZGVwYXJ0bWVudCBwbGF5cyBpbiBleHBsYWluaW5nIHRoZSBwcm9mZXNzb3JzJyBzYWxhcnksIHlvdSBjYW4gdGFrZSBhIGxvb2sgYXQgdGhlIGFjdHVhbCBkaWZmZXJlbmNlcyBpbiBtZWFuIHNhbGFyeSBhbW9uZyBkZXBhcnRtZW50cy4NCg0KSW4gb3JkZXIgdG8gY29tcHV0ZSB0aGUgYWN0dWFsIG1lYW5zIG9mIHRoZSBzYWxhcmllcyBmb3IgZWFjaCBkZXBhcnRtZW50IGVhc2lseSwgdXNlIHRoZSB0YXBwbHkoKSBmdW5jdGlvbiwgaW4gd2hpY2ggeW91IGVudGVyIHRoZSB2YXJpYWJsZSwgdGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGFuZCB0aGUgcmVxdWVzdGVkIHN1bW1hcnkgc3RhdGlzdGljICh0aGF0IGlzLCB0aGUgbWVhbikuDQoNCmBgYHtyfQ0KIyBBY3R1YWwgbWVhbnMgb2YgZnMkc2FsYXJ5DQp0YXBwbHkoZnMkc2FsYXJ5LCBmcyRkZXB0LCBtZWFuKQ0KYGBgDQoNCiMjIyBEaXNjcmVwYW5jeSBiZXR3ZWVuIGFjdHVhbCBhbmQgcHJlZGljdGVkIG1lYW5zOiBnZXQgaW50dWl0aXZlIQ0KDQpBcyBzZWVuIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZSwgdGhlIGFjdHVhbCBtZWFucyBkbyBub3QgbGFyZ2VseSBkaWZmZXIgYW1vbmcgdGhlIGRpZmZlcmVudCBkZXBhcnRtZW50cy4gVGhlcmUgaGFzIHRvIGJlIGFuIGV4cGxhbmF0aW9uIHdoeSB0aGUgcHJlZGljdGVkIHNhbGFyaWVzIG9mIHRoZSBwcm9mZXNzb3JzIG9mIHRoZSBzb2Npb2xvZ3kgZGVwYXJ0bWVudCB0ZW5kIHRvIGJlIGxvd2VyIG9uIGF2ZXJhZ2UgdGhhbiB0aG9zZSBvZiBwcm9mZXNzb3JzIG9mIHRoZSBoaXN0b3J5IGRlcGFydG1lbnQuIFJlbWVtYmVyIHRoYXQgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgZm9yIHRoZSBzb2Npb2xvZ3kgZGVwYXJ0bWVudCB3YXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSB6ZXJvIGluIGEgbXVsdGlwbGUgcmVncmVzc2lvbiBpbiB3aGljaCBhIGR1bW15IHZhcmlhYmxlIGZvciBkZXBhcnRtZW50IHdhcyBpbmNsdWRlZC4NCg0KSG93IHdvdWxkIHlvdSBleHBsYWluIHRoZSBhcHBhcmVudCBkaXNjcmVwYW5jeSBiZXR3ZWVuIHRoZSBhY3R1YWwgYW5kIHRoZSBwcmVkaWN0ZWQgbWVhbiBzYWxhcnkgb2YgcHJvZmVzc29ycyBhY3Jvc3MgZGVwYXJ0bWVudHM/IFRha2UgYSBsb29rIGF0IHRoZSBhY3R1YWwgbWVhbnMgb2YgdGhlIG90aGVyIHByZWRpY3RvciB2YXJpYWJsZXMgeWVhcnMgYW5kIHB1YmxpY2F0aW9ucyBieSBtZWFucyBvZiB0aGUgdGFwcGx5KCkgZnVuY3Rpb24gdG8gaGVscCB5b3UgdG8gY29tZSB1cCB3aXRoIGEgd2VsbC1jb25zaWRlcmVkIGFuc3dlci4NCg0KVGhlIHZhcmlhYmxlcyBkZWZpbmVkIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZXMgcmVtYWluIGF2YWlsYWJsZS4gQ2hlY2sgdmlhIGxzKCkNCmBgYHtyfQ0KdGFwcGx5KGZzJHNhbGFyeSxmcyRkZXB0LG1lYW4pDQp0YXBwbHkoZnMkcHVicyxmcyRkZXB0LG1lYW4pDQp0YXBwbHkoZnMkeWVhcnMsZnMkZGVwdCxtZWFuKQ0KDQpgYGANCg0KPiBCZWNhdXNlIHNvY2lvbG9neSBwcm9mZXNzb3JzIGhhdmUgbW9yZSB5ZWFycyBvbiB0aGUgam9iIGFuZCBtb3JlIHB1YmxpY2F0aW9ucyBvbiBhdmVyYWdlIGNvbXBhcmVkIHRvIG90aGVyIGRlcGFydG1lbnRzLCB0aGVpciBhY3R1YWwgbWVhbiBzYWxhcnkgaXMgY29tcGFyYWJsZSB0byB0aGF0IG9mIG90aGVyIGRlcGFydG1lbnRzLiBUaGUgZGlzY3JlcGFuY3kgYmV0d2VlbiBhY3R1YWwgYW5kIHByZWRpY3RlZCBzYWxhcnkgaXMgc29sZWx5IHRoZSBjb25zZXF1ZW5jZSBvZiB0aGUgZmFjdCB0aGF0IHByZWRpY3RlZCBtZWFuIHNhbGFyaWVzIG9ubHkgaG9sZCBmb3IgcHJvZmVzc29ycyB3aG8gaGF2ZSBhbiBhdmVyYWdlIGFtb3VudCBvZiBwdWJsaWNhdGlvbnMgYW5kIGFuIGF2ZXJhZ2UgbnVtYmVycyBvZiB5ZWFycyBvZiBleHBlcmllbmNlIGFjcm9zcyBhbGwgcHJvZmVzc29ycyBvZiB0aGUgdW5pdmVyc2l0eS4NCg0KIyMjIFVud2VpZ2h0ZWQgZWZmZWN0cyBjb2RpbmcNCg0KUHJvZmVzc29yIENvbndheSBtZW50aW9ucyBlZmZlY3RzIGNvZGluZyBhcyBhbiBpbnRlcmVzdGluZyBkdW1teSBjb2Rpbmcgc2NoZW1lLiBJbiB0aGUgZWZmZWN0cyBjb2Rpbmcgc2NoZW1lLCBhIHJlZmVyZW5jZSBjYXRlZ29yeSBzdGlsbCBuZWVkcyB0byBiZSBhcHBvaW50ZWQgYnV0IHRoaXMgdGltZSB0aGUgcmVmZXJlbmNlIGNhdGVnb3J5IGdldHMgYSB3ZWlnaHQgLSAxLiBUaGUgbnVtYmVyIG9mIGR1bW1pZXMgdGhhdCBtdXN0IGJlIGdlbmVyYXRlZCBlcXVhbHMgInRoZSBudW1iZXIgb2YgbGV2ZWxzIC0gMSIuIFRoZSBlZmZlY3RzIGNvZGluZyBzY2hlbWUgZm9yIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgd2l0aCA0IGxldmVscyBvZiB3aGljaCB0aGUgZm91cnRoIGNhdGVnb3J5IGlzIHBpY2tlZCBvdXQgYXMgdGhlIHJlZmVyZW5jZSBjYXRlZ29yeSwgaXMgc2hvd24gYmVsb3c6DQoNCj4gZHVtbXkgPSBbMSAgMCAgMDswICAxICAwOzAgIDAgIDA7LTEtMSAtMV0NCg0KVGhpcyB0eXBlIG9mIGVmZmVjdHMgY29kaW5nIGlzIGNhbGxlZCB1bndlaWdodGVkIGVmZmVjdHMgY29kaW5nIHNpbmNlIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHdpdGhpbiBlYWNoIGNhdGVnb3J5IG9mIHRoZSB2YXJpYWJsZSBpcyBub3QgdGFrZW4gaW50byBhY2NvdW50Lg0KDQpJbiB0aGUgZm9sbG93aW5nIHN0ZXBzLCB5b3Ugd2lsbCBsZWFybiB0byBzZXQgdXAgYSByZWdyZXNzaW9uIGJ5IHVzaW5nIHRoZSB1bndlaWdodGVkIGVmZmVjdHMgY29kaW5nIHNjaGVtZSBmb3IgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gSW4gdGhlIGV4YW1wbGUsIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgc2FsYXJ5IGlzIHJlZ3Jlc3NlZCBhZ2FpbnN0IHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZSBkZXB0LiBUaGUgZGF0YSBpcyBhbHJlYWR5IGxvYWRlZCBpbiB0aGUgd29ya3NwYWNlIHVuZGVyIHRoZSBuYW1lIGZzLg0KDQpgYGB7cn0NCiMgTnVtYmVyIG9mIGxldmVscw0KZnMkZGVwdA0KDQojIEZhY3Rvcml6ZSB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgZnMkZGVwdCBhbmQgbmFtZSB0aGUgZmFjdG9yaXplZCB2YXJpYWJsZSBkZXB0LmYNCmRlcHQuZiA8LSBmYWN0b3IoZnMkZGVwdCkNCmRlcHQuZg0KIyBBc3NpZ24gdGhlIDMgbGV2ZWxzIGdlbmVyYXRlZCBpbiBzdGVwIDIgdG8gZGVwdC5mDQpjb250cmFzdHMoZGVwdC5mKSA8LWNvbnRyLnN1bSgzKQ0KDQojIFJlZ3Jlc3Mgc2FsYXJ5IGFnYWluc3QgZGVwdC5mDQptb2RlbF91bndlaWdodGVkIDwtIGxtKGZzJHNhbGFyeSB+IGRlcHQuZikNCg0KIyBBcHBseSB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uDQpzdW1tYXJ5KG1vZGVsX3Vud2VpZ2h0ZWQpDQpgYGANCg0KDQojIyMgV2VpZ2h0ZWQgZWZmZWN0cyBjb2RpbmcNCg0KV2VpZ2h0ZWQgZWZmZWN0cyBjb2RpbmcgZGlmZmVycyBmcm9tIHVud2VpZ2h0ZWQgZWZmZWN0cyBjb2Rpbmcgd2l0aCByZXNwZWN0IHRvIHRoZSB3ZWlnaHRzLCBmcmFjdGlvbnMuIEEgcmVmZXJlbmNlIGNhdGVnb3J5IGlzIGNob3NlbiBhbmQgdGhlIHdlaWdodHMgZm9ybSB0aGUgZm9sbG93aW5nIGR1bW15IGNvZGluZyBzY2hlbWU6DQoNCg0KIVtkdW1taWVzIHdlaWdodGVkLl0oZDovQ3Vyc29zL0RhdGFjYW1wL1JNdWx0aXBsZVJlZ3Jlc3Npb24vZHVtbXl3ZWlnaHQucG5nKQ0Kd2l0aCBuID0gdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgb2YgZWFjaCBncm91cCBhbmQgaW5kZXggTiA9IHRoZSBudW1iZXIgb2YgbGV2ZWxzLiBUaGUgd2VpZ2h0cyByZXByZXNlbnQgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbiBvZiBhIG5vbi1yZWZlcmVuY2UgY2F0ZWdvcnkgcmVsYXRpdmUgdG8gdGhvc2Ugb2YgdGhlIHJlZmVyZW5jZSBjYXRlZ29yeS4NCg0KSWYgdGhlIHdlaWdodHMgYXJlIGNvbXB1dGVkLCB0aGUgcmVncmVzc2lvbiBvZiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIGFnYWluc3QgdGhlIG5vbi1jYXRlZ29yaWNhbCBhbmQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHVzaW5nIHRoZSB3ZWlnaHRlZCBlZmZlY3RzIGNvZGluZyBzY2hlbWUgY2FuIHN0YXJ0Lg0KYGBge3J9DQojIEZhY3Rvcml6ZSB0aGUgY2F0ZWdvcmljYWwgdmFyaWFibGUgZnMkZGVwdCBhbmQgbmFtZSB0aGUgZmFjdG9yaXplZCB2YXJpYWJsZSBkZXB0LmcNCmRlcHQuZyA8LSBmYWN0b3IoZnMkZGVwdCkNCg0KIyBBc3NpZ24gdGhlIHdlaWdodHMgbWF0cml4IHRvIGRlcHQuZw0KY29udHJhc3RzKGRlcHQuZykgPC0gY29udHIuc3VtKDMpDQoNCiMgUmVncmVzcyBzYWxhcnkgYWdhaW5zdCBkZXB0LmYgYW5kIGFwcGx5IHRoZSBzdW1tYXJ5KCkgZnVuY3Rpb24NCm1vZGVsX3dlaWdodGVkIDwtIGxtKGZzJHNhbGFyeSB+IGRlcHQuZykNCg0KIyBBcHBseSB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uDQpzdW1tYXJ5KG1vZGVsX3dlaWdodGVkKQ0KYGBgDQoNCg==