Introduction

In this project, we decided to study the relationship between personal variables and income from United States citizens. It was not easy to find a suitable dataset. The data was retrieved from the Minnesota Population Center, a database that allowed for manual selection of the variables to be included in the dataset. After creating the dataset of choice, the data was preprocessed to account for missing values and other inconsistencies. Several multiple linear regression models are applied to preprocessed data set, including a preliminary regression on all variables, a regression on manually selected variables, stepwise regression, and regression on data that was transformed using box-cox transformations. These models are compared based on their \(\bar{R}^2\) and the validation of the assumed model assumptions. Additionally, we explore the outliers in the data to make a potentially stronger model without these values.

Data preparation

The initially selected variables obtained from the Minnesota Population Center are:

  1. YEAR (Census year)
  2. DATANUM (Data set number)
  3. SERIAL (Household serial number)
  4. HHWT (Household weight)
  5. GQ (Group quarters status)
  6. PERNUM (Person number in sample unit)
  7. PERWT (Person weight)
  8. NCHILD (Number of own children in the household) 9. SEX (Sex)
  9. AGE (Age)
  10. MARRNO (Times married)
  11. RACE (Race [general version])
  12. HCOVANY (Any health insurance coverage)
  13. EDUC (Educational attainment [general version]) 15. SCHLTYPE (Public or private school)
  14. IND (Industry)
  15. WKSWORK2 (Weeks worked last year, intervalled) 18. UHRSWORK (Usual hours worked per week)
  16. INCTOT (Total personal income)
  17. INCWAGE (Wage and salary income)
  18. VETSTAT (Veteran status [general version])
  19. VETSTATD (Veteran status [detailed version])
  20. PWSTATE2 (Place of work: state)
  21. TRANTIME (Travel time to work)

However, there are some complexities of using the original dataset. First of all, the dataset has factor variables of a high level (e.g., up to 10k levels for the industry variable). Moreover, the dataset is presented in the form of a frequency table, with corresponding weight values showing the number of individuals representing each row. Finally, the number of rows is close to 10k, which is too big for the analysis.

In this section we preprocess the data to account for missing values and other inconsistencies. We aggregate several variables, apply weighting, and sample the dataset to 500 observations.

Preprocessing

df <- read.csv('dataset.csv')
df$VETSTAT <- as.factor(df$VETSTAT)
df$IND <- as.factor(df$IND)
df$SCHLTYPE_revised <- as.factor(df$SCHLTYPE_revised)
df_raw <- read.csv('dataset_original.csv')
head(df_raw)

This is the look of the original dataset.

income$IND_revised <- income$IND
income$IND_revised[income$IND_revised >= 170 & income$IND_revised <= 3990  ] <- "Agriculture, Forestry, Fishing and Hunting, and Mining"
income$IND_revised[income$IND_revised >= 4070 & income$IND_revised <= 4590  ] <- "Wholesale Trade"
income$IND_revised[income$IND_revised >= 4670 & income$IND_revised <= 5790  ] <- "Retail Trade"
income$IND_revised[income$IND_revised >= 6070 & income$IND_revised <= 6390  ] <- "Transportation and Warehousing, and Utilities"
income$IND_revised[income$IND_revised == 570 || income$IND_revised == 580 || income$IND_revised == 590 || income$IND_revised == 670 || income$IND_revised == 680 || income$IND_revised == 690] <- "Transportation and Warehousing, and Utilities"
income$IND_revised[income$IND_revised >= 6470 & income$IND_revised <= 6780  ] <-"Information"
income$IND_revised[income$IND_revised >= 6870 & income$IND_revised <= 7190  ] <- "Finance and Insurance, and Real Estate and Rental and Leasing"
income$IND_revised[income$IND_revised >= 7270 & income$IND_revised <= 7790  ] <- "Professional, Scientific, and Management, and Administrative, and Waste Management Services"
states_revised$IND_revised[states_revised$IND %in% c(570,580,590,670,680,690)] <- "Transportation and Warehousing, and Utilities"
income$IND_revised[income$IND_revised >= 7860 & income$IND_revised <= 8470  ] <- "Educational Services, and Health Care and Social Assistance"
income$IND_revised[income$IND_revised >= 8560 & income$IND_revised <= 8690  ] <- "Arts, Entertainment, and Recreation, and Accommodation and Food Services"
income$IND_revised[income$IND_revised >= 8770 & income$IND_revised <= 9290  ] <- "Other Services, Except Public Administration"
income$IND_revised[income$IND_revised >= 9370 & income$IND_revised <= 9590  ] <- "Public Administration"
income$IND_revised[income$IND_revised >= 9670 & income$IND_revised <= 9920  ] <- "Active Duty Military"

Industry variable represents industry codes. We aggregated IND variable by aggregating each data point into its appropriate industry category.

# aggregating the industry variable
par(mfrow=c(1,2))
plot(df$IND)
plot(df$IND_revised)

states_revised <- read.csv("dataset.csv")
table(states_revised$PWSTATE2_revised)
table(states_revised$PWSTATE2)
View(states_revised)
states_revised$PWSTATE2_revised <- states_revised$PWSTATE2
states_revised <- states_revised[!(states_revised$PWSTATE2_revised >= 61),]
states_revised <- states_revised[!(states_revised$PWSTATE2_revised == 0),]
states_revised$PWSTATE2_revised[states_revised$PWSTATE2_revised %in% c(53,41,6,30,16,56,32,49,8,4,35,2,15)] <- "WEST"
states_revised$PWSTATE2_revised[states_revised$PWSTATE2_revised %in% c(38,27,46,19,31,20,29,55,26,17,18,39)] <- "MIDWEST"
states_revised$PWSTATE2_revised[states_revised$PWSTATE2_revised %in% c(40,5,48,22,28,1,47,21,54,51,37,45,13,12,10,24,11)] <- "SOUTH"
states_revised$PWSTATE2_revised[states_revised$PWSTATE2_revised %in% c(33,50,23,25,44,9,36,34,42)] <- "NORTHEAST"

Transformed PWSTATE2 by categorizing each data point into four regions of united states.

cat_revised$RACE_revised <- cat_revised$RACE
table(cat_revised$RACE)
cat_revised$RACE_revised[cat_revised$RACE_revised == 1] <- "White"
cat_revised$RACE_revised[cat_revised$RACE_revised == 2] <- "Black"
cat_revised$RACE_revised[cat_revised$RACE_revised == 3] <- "American Indian"
cat_revised$RACE_revised[cat_revised$RACE_revised %in% c(4,5,6)] <- "Asian"
cat_revised$RACE_revised[cat_revised$RACE_revised %in% c(7,8,9)] <- "Other"

Transformed RACE variable by assigning descripitve names rather than just simple numbers.

cat_revised$HCOVANY_revised <- cat_revised$HCOVANY
cat_revised$HCOVANY_revised[cat_revised$HCOVANY_revised == 1] <- "No health insurance coverage"
cat_revised$HCOVANY_revised[cat_revised$HCOVANY_revised == 2] <- "With health insurance coverage"

Transformed HCOVANY variable by assigning descriptive names rather than just simple numbers.

cat_revised$WKSWORK2_revised <- cat_revised$WKSWORK2
cat_revised$WKSWORK2_revised[cat_revised$WKSWORK2_revised == 1] <- "1-13 weeks"
cat_revised$WKSWORK2_revised[cat_revised$WKSWORK2_revised == 2] <- "14-26 weeks"
cat_revised$WKSWORK2_revised[cat_revised$WKSWORK2_revised == 3] <- "27-39 weeks"
cat_revised$WKSWORK2_revised[cat_revised$WKSWORK2_revised == 4] <- "40-47 weeks"
cat_revised$WKSWORK2_revised[cat_revised$WKSWORK2_revised == 5] <- "48-49 weeks"
cat_revised$WKSWORK2_revised[cat_revised$WKSWORK2_revised == 6] <- "50-52 weeks"

Transformed WKSWORK2 variable by assigning descripitve names rather than just simple numbers.

cat_revised$VETSTAT_revised <- cat_revised$VETSTAT
cat_revised$VETSTAT_revised[cat_revised$VETSTAT_revised == 1] <- "Not a veteran"
cat_revised$VETSTAT_revised[cat_revised$VETSTAT_revised == 2] <- "Veteran"

Transformed VETSTAT variable by assigning descripitve names rather than just simple numbers.

# aggregate the education variable
df$EDUC_revised <- as.factor(sapply(df$EDUC, function(x) {
  if (x == 0) return('no school or N/A')
  else if (x < 7) return('1-12 years of pre-college')
  else if (x < 11) return('1-4 years of college')
  else return ('5+ years of college')
}))
par(mfrow=c(1,2))
plot(as.factor(df$EDUC))
plot(df$EDUC_revised)

We aggregated the EDUC variable into 4 levels that capture high school, college, and advanced college degrees.

library(ggplot2)
ggplot(df) +
  geom_point(aes(x = seq(1,nrow(df)), y = df$INCTOT, color=df$EDUC_revised)) +
  theme_bw()

Unsuprisingly, there is an obvious correlation between the education level and income.

# aggregate the school type variable
df$SCHLTYPE_revised <- as.factor(sapply(df$SCHLTYPE, function(x) {
  if (x == 0) return('N/A')
  else if (x == 1) return('Not Enrolled')
  else if (x == 2) return('Public School')
  else return ('Private School')
}))
par(mfrow=c(1,2))
plot(as.factor(df$SCHLTYPE))
plot(df$SCHLTYPE_revised)

We dropped INCWAGE variable after realizing it was not very discriptive. We decided to use total income as a response variable.

# drop old variables
dfn <- subset(df, select = -c(MARRNO_revised, RACE, HCOVANY, IND, VETSTAT, PWSTATE2, EDUC, SCHLTYPE))
# rename columns
colnames(dfn) <- c("WEIGHT", "NUM_CHILD", "SEX", "AGE", "NUM_MARR", "WEEKS_WORKED_NUMERIC", "NUM_HOURS_WEEK", "INCOME", "TRAVEL_TIME", "INDUSTRY", "EDUC", "DIVISION", "SCHL_TYPE", "RACE", "HEALTH_INS", "WEEKS_WORKED", "VETERAN")
head(dfn)

In the following script, we calculated the weighted (by WEIGHT) income for the observations with equal predictor variables.

library(data.table)
dt <- as.data.table(dfn)
# group by predictor variables
dt <- dt[,list(INCOME=weighted.mean(INCOME, WEIGHT)), setdiff(names(dt), c("WEIGHT", "INCOME"))]
head(dt)

The data is prepared, but the size of the dataset is still around 10,000 observations.

distributions <- function(dt) {
  par(mfrow=c(4,4))
  # show distributions of the variables
  for (i in names(dt)) {
    if (is.numeric(dt[[i]])) {
      hist(dt[[i]], main = i)
    } else {
      plot(dt[[i]], main = i)
    }
  }
}
# visualise distributions
distributions(dt)

It seems that taking a sample maintained the general distribution properties of the variables.

set.seed(1337)
dts <- dt[sample(nrow(dt), 500), ]
distributions(dts)

It can be seen that the general distribution properties are indeed captured in the 500 observation sample subset.

Documentation

After all preprocessing operations, the final dataset contained 16 variables:

Variable Description
NUM_CHILD Counts the number of own children (of any age or marital status) residing with each individual. Includes step-children and adopted children as well as biological children.
SEX Reports whether the person was male or female.
AGE Reports the person’s age in years as of the last birthday.
EDUC Reports the person’s education level.
NUM_MARR Reports the number of times ever-married persons have been married.
NUM_HOURS_WEEK Reports the usual number of hours the person earned per week.
TRAVEL_TIME Reports the number of minutes it usually took the person to get home from work.
INDUSTRY Reports the type of industry in which the person performed an occupation. Persons who worked in multiple industries were asked to report from which they earned the most money. Unemployed persons were to report their most recent industry.
DIVISION Reports the region of the country that the person’s primary workplace is located.
SCHL_TYPE Reports whether the person attended public or private school, if they were enrolled at all.
RACE Reports the race of the person.
HEALTH_INS Reports whether or not the person has health insurance coverage.
WEEKS_WORKED Reports the number of weeks the person worked last year, intervalled.
WEEKS_WORKED_NUMERIC Reports the number of week the person worked last year.
VETERAN Reports whether or not the person is a veteran.
INCOME Reports the total person’s pre-tax personal income from the previous year. This is the response variable in the analysis.

Multiple Linear Regression Analysis:

“Preliminary” model

In this first model, we decided to run multiple regression on income using all 14 of independent variables in the dataset. But before building the model, we made some final preprocessing steps:

  1. WEEKS_WORKED_NUMERIC variable has been removed from the data set
  2. NUM_MARR variable is converted from numerical variable to categorical variable
  3. long industry names shortened for prettier model summaries
orig_data <- read.csv("dataset_final_sample.csv")
orig_data <- orig_data[, !(colnames(orig_data) %in% c("WEEKS_WORKED_NUMERIC"))]
orig_data$INDUSTRY <- sapply(orig_data$INDUSTRY, function(x) substr(x, 0, 60))
orig_data$NUM_MARR <- as.factor(orig_data$NUM_MARR)
mlr <- lm(orig_data$INCOME ~ ., data = orig_data )
summary(mlr)

Call:
lm(formula = orig_data$INCOME ~ ., data = orig_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-128772  -27124   -3844   15457  499322 

Coefficients:
                                                                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                                          -165788.76   70528.26  -2.351 0.019160 *  
NUM_CHILD                                                               2046.01    2748.93   0.744 0.457079    
SEXM                                                                    1134.39    6154.00   0.184 0.853832    
AGE                                                                      830.05     242.86   3.418 0.000687 ***
NUM_MARR1                                                              -6869.47    8157.50  -0.842 0.400166    
NUM_MARR2                                                              -8278.84   11200.04  -0.739 0.460174    
NUM_MARR3                                                             -12990.01   17548.30  -0.740 0.459529    
NUM_HOURS_WEEK                                                          1624.57     243.89   6.661 7.76e-11 ***
TRAVEL_TIME                                                               88.69     123.45   0.718 0.472858    
INDUSTRYAgriculture, Forestry, Fishing and Hunting, and Mining         86980.34   61230.35   1.421 0.156126    
INDUSTRYArts, Entertainment, and Recreation, and Accommodation and F   71257.54   61407.54   1.160 0.246485    
INDUSTRYEducational Services, and Health Care and Social Assistance    69823.35   60625.52   1.152 0.250035    
INDUSTRYFinance and Insurance, and Real Estate and Rental and Leasin   93888.77   61451.75   1.528 0.127237    
INDUSTRYInformation                                                    85829.84   63137.54   1.359 0.174681    
INDUSTRYOther Services, Except Public Administration                   72156.69   61784.31   1.168 0.243459    
INDUSTRYProfessional, Scientific, and Management, and Administrative   92833.81   60938.76   1.523 0.128346    
INDUSTRYPublic Administration                                          76667.05   61591.52   1.245 0.213850    
INDUSTRYRetail Trade                                                   63849.68   61220.12   1.043 0.297517    
INDUSTRYTransportation and Warehousing, and Utilities                  92340.16   62031.19   1.489 0.137274    
INDUSTRYWholesale Trade                                                92271.72   62373.79   1.479 0.139734    
EDUC1-4 years of college                                               17001.63    6254.97   2.718 0.006813 ** 
EDUC5+ years of college                                                73774.17    9826.65   7.508 3.14e-13 ***
EDUCno school or N/A                                                    2006.79   28248.65   0.071 0.943397    
DIVISIONNORTHEAST                                                      47545.64    8970.93   5.300 1.80e-07 ***
DIVISIONSOUTH                                                          13721.27    7422.74   1.849 0.065164 .  
DIVISIONWEST                                                           10935.99    8346.16   1.310 0.190746    
SCHL_TYPEPrivate School                                                -4270.07   17321.88  -0.247 0.805395    
SCHL_TYPEPublic School                                                  8276.74   11767.30   0.703 0.482181    
RACEAsian                                                              11384.88   26644.44   0.427 0.669368    
RACEBlack                                                              -2060.72   25130.19  -0.082 0.934681    
RACEOther                                                              13034.37   25764.01   0.506 0.613159    
RACEWhite                                                               9501.71   23492.58   0.404 0.686065    
HEALTH_INSWith health insurance coverage                               11342.18   10730.66   1.057 0.291071    
WEEKS_WORKED14-26 weeks                                               -28019.15   26296.32  -1.066 0.287200    
WEEKS_WORKED27-39 weeks                                               -10906.77   26017.77  -0.419 0.675262    
WEEKS_WORKED40-47 weeks                                               -31954.51   24974.32  -1.279 0.201367    
WEEKS_WORKED48-49 weeks                                                 2138.16   27505.78   0.078 0.938073    
WEEKS_WORKED50-52 weeks                                                -6065.70   23081.82  -0.263 0.792829    
VETERANVeteran                                                          1993.57   13615.86   0.146 0.883658    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 59190 on 461 degrees of freedom
Multiple R-squared:  0.3618,    Adjusted R-squared:  0.3092 
F-statistic: 6.878 on 38 and 461 DF,  p-value: < 2.2e-16

For this model, \(\bar{R}^2\) turnes out to be \(0.3092\), which means that the model fails to describe the data accurately.

F test and p-value for “preliminary” model

qf(0.95, df1 = 38, df2 = 461)
[1] 1.431182

Running F-test on the preliminary model reveals that the model is significant at alpha = 0.05 because F-statistic of the model (\(6.878\)) is greater than \(F(0.05) = 1.431182\). Also, p-value is close to being \(0\).

VIF test (multicollinearity test)

library(car)
vif(mlr)
                   GVIF Df GVIF^(1/(2*Df))
NUM_CHILD      1.275544  1        1.129400
SEX            1.350841  1        1.162257
AGE            1.890971  1        1.375126
NUM_MARR       2.286037  3        1.147750
NUM_HOURS_WEEK 1.429269  1        1.195520
TRAVEL_TIME    1.117092  1        1.056926
INDUSTRY       2.964278 11        1.050633
EDUC           1.618329  3        1.083539
DIVISION       1.386669  3        1.055996
SCHL_TYPE      1.564106  2        1.118321
RACE           1.660683  4        1.065457
HEALTH_INS     1.153855  1        1.074176
WEEKS_WORKED   1.727593  5        1.056195
VETERAN        1.160938  1        1.077468
mean(vif(mlr)[,1]) # mean of vifs = 1.6133
[1] 1.6133
max(vif(mlr)[,1]) # maximum value of vifs = 2.964278
[1] 2.964278

Since mean of vifs = \(1.6133\) is not substantially greater than \(1\) and maximum value of vifs = \(2.964278\) is less than \(10\), we can conclude that our “preliminary” model doesn’t exhibit multicollinearity.

Residual analysis of “preliminary” model

# Split the plotting panel into a 2 x 2 grid
par(mfrow = c(2, 2), pch = 16, cex = .5)
plot(mlr)
not plotting observations with leverage one:
  24

Four plots above shows that our model does not satisfy the regression assumptions:

  • Specifically, the Residuals vs. Fitted Values plot shows an obvious pattern that fans out as the fitted values increase, showing that the variance of the error terms are not constant.
  • The Normal Probability Plot shows curvature toward the ends, indicating that the error terms are not normally distributed.

“Manual” model

For the second model, we decided to select variables that passed 95% significance test on the “preliminary” model. They are AGE, NUM_HOURS_WEEK, EDUC and DIVISION.

mlr_t <- lm(orig_data$INCOME ~ AGE + NUM_HOURS_WEEK + EDUC + DIVISION, data =orig_data)
summary(mlr_t)

Call:
lm(formula = orig_data$INCOME ~ AGE + NUM_HOURS_WEEK + EDUC + 
    DIVISION, data = orig_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-136847  -25843   -6832   17561  516542 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)              -78920.2    13214.0  -5.972 4.49e-09 ***
AGE                         772.2      180.1   4.288 2.18e-05 ***
NUM_HOURS_WEEK             1826.2      207.2   8.815  < 2e-16 ***
EDUC1-4 years of college  17439.2     5790.9   3.011  0.00273 ** 
EDUC5+ years of college   70118.3     8850.6   7.922 1.57e-14 ***
EDUCno school or N/A       4534.5    27067.9   0.168  0.86703    
DIVISIONNORTHEAST         45091.6     8809.4   5.119 4.43e-07 ***
DIVISIONSOUTH              7645.3     7061.9   1.083  0.27951    
DIVISIONWEST              10141.6     7924.9   1.280  0.20125    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 59190 on 491 degrees of freedom
Multiple R-squared:  0.3203,    Adjusted R-squared:  0.3092 
F-statistic: 28.92 on 8 and 491 DF,  p-value: < 2.2e-16

For this model, \(\bar{R}^2\) turned out to be \(0.3092\), which is the same as the one we got from “preliminary” model. It means that by reducing the dimensionality, we did not lose much information about the predicted variable.

F test for “manual” model

qf(0.95, df1 = 8, df2 = 491)
[1] 1.957253

“Manual” model is statistically significant because F-statistic: \(28.9\) is greater than \(F(alpha = 0.05) = 1.957253\)

Plot “manual” model

par(mfrow = c(2, 2), pch = 16, cex = .5)
plot(mlr_t)

Four plots above shows that the second model does not satisfy the regression assumptions. Similarly to the first model, The Residuals vs. Fitted Values plot, as well as the Normal Probability Plot show variations from the expected plots, indicating the error terms are not normally distributed with constant variance.

Automatic model building

Backward elimination

set.seed(1337)
step(mlr, direction = "backward", trace = F)

Call:
lm(formula = orig_data$INCOME ~ SEX + AGE + NUM_HOURS_WEEK + 
    EDUC + DIVISION, data = orig_data)

Coefficients:
             (Intercept)                      SEXM                       AGE            NUM_HOURS_WEEK  EDUC1-4 years of college  
                  -81462                      7924                       777                      1774                     18103  
 EDUC5+ years of college      EDUCno school or N/A         DIVISIONNORTHEAST             DIVISIONSOUTH              DIVISIONWEST  
                   72010                      2280                     44859                      7582                     10098  

Running backward elimination method reveals that we should include SEX, AGE, NUM_HOURS_WEEK, EDUC, DIVISION as our independent variables. It should be noted that these variables are the same as the significant variables chosen for the manual model above, with the addition of the SEX variable here.

Stepwise regression

step(mlr, direction = "both", trace = F)

Call:
lm(formula = orig_data$INCOME ~ SEX + AGE + NUM_HOURS_WEEK + 
    EDUC + DIVISION, data = orig_data)

Coefficients:
             (Intercept)                      SEXM                       AGE            NUM_HOURS_WEEK  EDUC1-4 years of college  
                  -81462                      7924                       777                      1774                     18103  
 EDUC5+ years of college      EDUCno school or N/A         DIVISIONNORTHEAST             DIVISIONSOUTH              DIVISIONWEST  
                   72010                      2280                     44859                      7582                     10098  

Running stepwise regression method reveals that we should include SEX, AGE, NUM_HOURS_WEEK, EDUC, DIVISION as our independent variables. This is the same conclusion drawn from the backward elimination.

Multiple Linear Regression: “Automatic” model

The automatic model building procedure suggested that we should use SEX, AGE, NUM_HOURS_WEEK, EDUC, DIVISION as our independent variables.

mlr_bs <- lm(orig_data$INCOME ~ SEX + AGE + NUM_HOURS_WEEK + EDUC + DIVISION, data = orig_data)
summary(mlr_bs)

Call:
lm(formula = orig_data$INCOME ~ SEX + AGE + NUM_HOURS_WEEK + 
    EDUC + DIVISION, data = orig_data)

Residuals:
    Min      1Q  Median      3Q     Max 
-142127  -25991   -6689   17095  520568 

Coefficients:
                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)              -81462.3    13312.7  -6.119 1.93e-09 ***
SEXM                       7924.1     5420.1   1.462  0.14439    
AGE                         777.0      179.9   4.319 1.90e-05 ***
NUM_HOURS_WEEK             1774.0      210.0   8.448 3.39e-16 ***
EDUC1-4 years of college  18102.6     5802.0   3.120  0.00191 ** 
EDUC5+ years of college   72009.6     8934.5   8.060 5.89e-15 ***
EDUCno school or N/A       2280.3    27080.5   0.084  0.93293    
DIVISIONNORTHEAST         44859.1     8800.7   5.097 4.93e-07 ***
DIVISIONSOUTH              7581.7     7053.9   1.075  0.28298    
DIVISIONWEST              10098.3     7915.8   1.276  0.20266    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 59120 on 490 degrees of freedom
Multiple R-squared:  0.3232,    Adjusted R-squared:  0.3108 
F-statistic:    26 on 9 and 490 DF,  p-value: < 2.2e-16

Adjusted \(\bar{R}^2\) turned out to be \(0.3108\), which is slightly better than the first models.

Residual analysis of “automatic” model

Residual plots

par(mfrow = c(2, 2), pch = 16, cex = .5)
plot(mlr_bs)

Although the \(\bar{R}^2\) was slightly improved compared to the prior models, the residual plots still shows that our model doesn’t satisfy the regression assumptions.

Histogram plot of “automatic” model’s residuals.

hist(mlr_bs$residuals, col = "lightgray")

Plot of residuals confirms that they are definitely not distributed normally, but in fact are positively skewed. We can try to apply some transformations to normalize the residuals.

Finding Box-Cox transformation’s value of lambda

library(MASS)
b_bs <- boxcox(mlr, lambda = seq(0,0.2,.1))

boxcox_values_bs <- cbind(b_bs$x,b_bs$y)
value_bs <- boxcox_values_bs[order(-b_bs$y),][1,1]
value_bs
[1] 0.01414141

Running Box-Cox test on the original data reveals that we should use \(\lambda = 0.01414141\) for our transformation on our dependent variable which is income. It can be seen from the above plot that the maximum likelihood estimation has the highest peak at \(\lambda = 0.01414141\).

Box-Cox transformed model

We created a new model using transformed predicted variable. Re-running backward elimination again (steps omitted) showed that we should include AGE, NUM_HOURS_WEEK, TRAVEL_TIME,INDUSTRY, EDUC, DIVISION, SCHL_TYPE, HEALTH_INS, WEEKS_WORKED. It is notable that these variables are different than the ones picked out from the non-transformed data: SEX, AGE, NUM_HOURS_WEEK, EDUC, DIVISION.

orig_data$INCOME_TRANSFORMED <- orig_data$INCOME^value_bs
mlr_transform_bs <- lm(formula = orig_data$INCOME_TRANSFORMED ~ AGE + NUM_HOURS_WEEK + 
    TRAVEL_TIME + INDUSTRY + EDUC + DIVISION + SCHL_TYPE + HEALTH_INS + WEEKS_WORKED, data = orig_data)
summary(mlr_transform_bs)

Call:
lm(formula = orig_data$INCOME_TRANSFORMED ~ AGE + NUM_HOURS_WEEK + 
    TRAVEL_TIME + INDUSTRY + EDUC + DIVISION + SCHL_TYPE + HEALTH_INS + 
    WEEKS_WORKED, data = orig_data)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.033972 -0.005723  0.000365  0.006087  0.038519 

Coefficients:
                                                                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                                           1.083e+00  1.166e-02  92.880  < 2e-16 ***
AGE                                                                   2.339e-04  3.647e-05   6.414 3.44e-10 ***
NUM_HOURS_WEEK                                                        4.454e-04  4.205e-05  10.593  < 2e-16 ***
TRAVEL_TIME                                                           4.370e-05  2.143e-05   2.039  0.04196 *  
INDUSTRYAgriculture, Forestry, Fishing and Hunting, and Mining        1.533e-02  1.069e-02   1.435  0.15196    
INDUSTRYArts, Entertainment, and Recreation, and Accommodation and F  5.329e-03  1.074e-02   0.496  0.62015    
INDUSTRYEducational Services, and Health Care and Social Assistance   9.123e-03  1.062e-02   0.859  0.39080    
INDUSTRYFinance and Insurance, and Real Estate and Rental and Leasin  1.513e-02  1.074e-02   1.408  0.15976    
INDUSTRYInformation                                                   1.451e-02  1.103e-02   1.315  0.18900    
INDUSTRYOther Services, Except Public Administration                  1.035e-02  1.081e-02   0.958  0.33851    
INDUSTRYProfessional, Scientific, and Management, and Administrative  1.437e-02  1.065e-02   1.349  0.17804    
INDUSTRYPublic Administration                                         1.417e-02  1.076e-02   1.318  0.18831    
INDUSTRYRetail Trade                                                  9.134e-03  1.070e-02   0.853  0.39385    
INDUSTRYTransportation and Warehousing, and Utilities                 1.379e-02  1.082e-02   1.274  0.20326    
INDUSTRYWholesale Trade                                               1.572e-02  1.087e-02   1.446  0.14884    
EDUC1-4 years of college                                              5.522e-03  1.085e-03   5.089 5.22e-07 ***
EDUC5+ years of college                                               1.330e-02  1.714e-03   7.757 5.43e-14 ***
EDUCno school or N/A                                                  1.491e-05  4.890e-03   0.003  0.99757    
DIVISIONNORTHEAST                                                     6.735e-03  1.571e-03   4.286 2.21e-05 ***
DIVISIONSOUTH                                                         9.181e-05  1.277e-03   0.072  0.94273    
DIVISIONWEST                                                          2.440e-03  1.436e-03   1.699  0.08992 .  
SCHL_TYPEPrivate School                                              -4.918e-03  3.017e-03  -1.630  0.10381    
SCHL_TYPEPublic School                                               -5.215e-03  2.031e-03  -2.568  0.01054 *  
HEALTH_INSWith health insurance coverage                              4.412e-03  1.879e-03   2.348  0.01930 *  
WEEKS_WORKED14-26 weeks                                               1.456e-02  4.562e-03   3.191  0.00151 ** 
WEEKS_WORKED27-39 weeks                                               2.360e-02  4.508e-03   5.235 2.49e-07 ***
WEEKS_WORKED40-47 weeks                                               2.177e-02  4.350e-03   5.005 7.92e-07 ***
WEEKS_WORKED48-49 weeks                                               3.154e-02  4.745e-03   6.646 8.32e-11 ***
WEEKS_WORKED50-52 weeks                                               2.789e-02  3.997e-03   6.978 1.02e-11 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.01042 on 471 degrees of freedom
Multiple R-squared:  0.6251,    Adjusted R-squared:  0.6029 
F-statistic: 28.05 on 28 and 471 DF,  p-value: < 2.2e-16

The transformed model has \(\bar{R}^2\) equal to \(0.6029\), which is a big improvement from all previous models.

Residual analysis of the transformed model

Residual plots

par(mfrow = c(2, 2), pch = 16, cex = .5)
plot(mlr_transform_bs)
not plotting observations with leverage one:
  24

The transformed model still seems to not quite satisfy the regression assumptions. There are, however, improvements in the Normal Probability Plot and Residuals vs. Fitted Values plot, indicating that this model more accurately follows the assumptions of constant variance and normality than the previous models discussed:

  1. The Normal Probability Plot appears fairly linear, indicating the normality assumption may be satisfied.
  2. The Residials vs. Fitted Values plot doesn’t appear to have any obvious patterns, with only slight fanning out for larger fitted values (suspected outliers). There is no clear indication that the constant variance assumption may be invalidated.

Histogram of the transformed model’s residuals

hist(mlr_transform_bs$residuals, col = "lightgray")

Histogram plot above can be said appproximately normal, further indicating that the normality assumption may hold for the transformed model. However, the Anderson-Darling (AD) Statistic test can be run to further determine whether or not the normality condition is met.

Anderson-Darling statistic test

library(nortest)
ad.test(mlr_transform_bs$residuals)

    Anderson-Darling normality test

data:  mlr_transform_bs$residuals
A = 1.4942, p-value = 0.0007409

Applyting AD test on our box-cox transfomed and stepwise regression applied model reveal that normality assumption is not satisfied because we can reject the null hypothesis of AD test at \(\alpha = 0.05\).

Cook’s Distance Measure plot for the transformed model

library(car)
plot(mlr_transform_bs, pch = 18, col ="red", which = c(4)) 
cook <- cooks.distance(mlr_transform_bs)
abline(h = 4/nrow(orig_data), col="blue")  # add cutoff line

According to the plot of the Cook’s distances, there might be several outlying values.

k <- length(coef(mlr_transform_bs)) - 1
f_50 <- qf(0.50, df1 = k+1, df2 = mlr_transform_bs$df)
f_80 <- qf(0.20, df1 = k+1, df2 = mlr_transform_bs$df)
any(cook > f_50, na.rm = T)
[1] FALSE

There seems to be no influential observations according to Cook’s distance test described in the textbook. None of the observations have cook’s distance values greater than \(F_{[0.5]}\), but every observation has a Cook’s distance value less than \(F_{[0.8]}\). Some sources suggest different cut-off values to use for spotting highly influential points.

omit_cook <- which(cook > 4/nrow(orig_data))
length(omit_cook)
[1] 39

Using the cut-off value \(D_i>4/n\), where \(n\) is the number of observations (shown as a horisontal line on the plot above), we detected 39 outlying values.

Studentized residuals of the transormed model

stud_residual <- rstandard(mlr_transform_bs) # studentized residuals
omit_stud <- which(stud_residual < -2 | stud_residual > 2 | is.na(stud_residual)) #studentized residuals greater than absolute value of 2 and na values
length(omit_stud) # 28 potential outliers wrt y values
[1] 29

After studentizing residuals for transformed model, we selected 29 potential outliers with respect to \(y\) values.

Leverage values for the transformed model

lev <- hatvalues(mlr_transform_bs)
cutoff_lev <- 2 * (k +1) / n
plot(lev, pch = 16, cex = .5)
abline(h = cutoff_lev, col="red")  # add cutoff line

omit_lev <- which(lev > cutoff_lev)
length(omit_lev) 
[1] 25

There are 25 observations that are associated with large leverage values.

Dropping potential outliers from the transformed model

Studying the nature of outliers is beyond the scope of this analysis, so we decided to see how the model will improve if we exclude the outliers with respect to \(y\) them from the model. We didn’t exclude outliers wrt to \(x\) because it showed to decrease the utility of the model.

orig_data_stud_omit <- orig_data[-c(omit_stud, omit_cook),]
mlr_transform_orig_bs_omit <- lm(INCOME_TRANSFORMED ~ AGE + NUM_HOURS_WEEK + 
    TRAVEL_TIME + INDUSTRY + EDUC + DIVISION + SCHL_TYPE + HEALTH_INS + 
    WEEKS_WORKED, data = orig_data_stud_omit)
summary(mlr_transform_orig_bs_omit)

Call:
lm(formula = INCOME_TRANSFORMED ~ AGE + NUM_HOURS_WEEK + TRAVEL_TIME + 
    INDUSTRY + EDUC + DIVISION + SCHL_TYPE + HEALTH_INS + WEEKS_WORKED, 
    data = orig_data_stud_omit)

Residuals:
       Min         1Q     Median         3Q        Max 
-0.0221479 -0.0054799  0.0003281  0.0052736  0.0193001 

Coefficients:
                                                                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)                                                           1.092e+00  6.123e-03 178.317  < 2e-16 ***
AGE                                                                   2.222e-04  2.905e-05   7.650 1.36e-13 ***
NUM_HOURS_WEEK                                                        4.196e-04  3.362e-05  12.483  < 2e-16 ***
TRAVEL_TIME                                                           7.416e-05  1.789e-05   4.145 4.10e-05 ***
INDUSTRYArts, Entertainment, and Recreation, and Accommodation and F -9.026e-03  1.677e-03  -5.383 1.21e-07 ***
INDUSTRYEducational Services, and Health Care and Social Assistance  -5.963e-03  1.302e-03  -4.581 6.08e-06 ***
INDUSTRYFinance and Insurance, and Real Estate and Rental and Leasin -1.529e-03  1.892e-03  -0.808 0.419343    
INDUSTRYInformation                                                   9.638e-04  2.593e-03   0.372 0.710311    
INDUSTRYOther Services, Except Public Administration                 -4.914e-03  1.822e-03  -2.697 0.007284 ** 
INDUSTRYProfessional, Scientific, and Management, and Administrative -1.718e-03  1.519e-03  -1.131 0.258726    
INDUSTRYPublic Administration                                        -4.715e-04  1.898e-03  -0.248 0.803929    
INDUSTRYRetail Trade                                                 -4.926e-03  1.453e-03  -3.391 0.000761 ***
INDUSTRYTransportation and Warehousing, and Utilities                -3.328e-03  2.045e-03  -1.627 0.104403    
INDUSTRYWholesale Trade                                               3.210e-03  2.132e-03   1.506 0.132889    
EDUC1-4 years of college                                              5.475e-03  8.474e-04   6.461 2.84e-10 ***
EDUC5+ years of college                                               1.526e-02  1.405e-03  10.865  < 2e-16 ***
EDUCno school or N/A                                                 -6.480e-03  8.056e-03  -0.804 0.421649    
DIVISIONNORTHEAST                                                     6.014e-03  1.247e-03   4.823 1.97e-06 ***
DIVISIONSOUTH                                                         9.501e-04  1.008e-03   0.942 0.346620    
DIVISIONWEST                                                          2.074e-03  1.146e-03   1.810 0.071052 .  
SCHL_TYPEPrivate School                                              -4.361e-03  2.636e-03  -1.655 0.098719 .  
SCHL_TYPEPublic School                                               -5.455e-03  1.604e-03  -3.402 0.000733 ***
HEALTH_INSWith health insurance coverage                              5.516e-03  1.446e-03   3.814 0.000157 ***
WEEKS_WORKED14-26 weeks                                               2.310e-02  6.004e-03   3.847 0.000138 ***
WEEKS_WORKED27-39 weeks                                               3.331e-02  6.074e-03   5.484 7.15e-08 ***
WEEKS_WORKED40-47 weeks                                               2.564e-02  5.934e-03   4.320 1.94e-05 ***
WEEKS_WORKED48-49 weeks                                               3.586e-02  6.101e-03   5.878 8.40e-09 ***
WEEKS_WORKED50-52 weeks                                               3.389e-02  5.762e-03   5.883 8.19e-09 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.007834 on 425 degrees of freedom
Multiple R-squared:  0.7139,    Adjusted R-squared:  0.6957 
F-statistic: 39.28 on 27 and 425 DF,  p-value: < 2.2e-16
nrow(orig_data) - nrow(orig_data_stud_omit) # number of excluded observations
[1] 47

After dropping 47 observations that were classified as potential outliers model has \(\bar{R}^2\) equal to \(0.6957\). This is notably increased from the \(0.6029\) value before the outliers were dropped.

Anderson-Darling statistic test

library(nortest)
ad.test(mlr_transform_orig_bs_omit$residuals)

    Anderson-Darling normality test

data:  mlr_transform_orig_bs_omit$residuals
A = 0.67046, p-value = 0.07951

Applyting AD test on our final best model reveal that normality assumption is satisfied because we fail to reject the null hypothesis of AD test at \(\alpha = 0.05\).

Residual Plots

par(mfrow = c(2, 2), pch = 16, cex = .5)
plot(mlr_transform_orig_bs_omit)
not plotting observations with leverage one:
  103

4 plots above seem to indicate that the model without suspected outliers satisfies the regresion assumptions. The Normal Probability Plot seems linear, and the Residuals vs. Fitted Value Plot shows only minor signs of violation of the constant variance assumption (slight fanning out).

Interpreting the model

Our final model demonstrates satisfactory adherence to the regression assumptions and has a relatively high adjusted R-squared score of 0.6957. It means that we can draw somewhat reasoned conclusions from its interpretation.

Unsurprisingly, some of the most significant predictors are NUM_HOURS_WEEK and WEEKS_WORKED because they are usually highly correlated with a person’s income. More interesting relationships are demonstrated by the variables corresponding to Northeastern division of the United States, health insurance coverage, 5+ years of college, public school type, etc. To understand the relationships of these variables to the person’s income their estimated coefficients must be interpreted. Interpreting the coefficients of a model with transformed predicted variable turns out to be a difficult task. Since the value of \(\lambda = 0.01414141\) in our transformation was close to zero, we decided to follow the procedure described here assuming that the transformation was logarithmic. Thus, we can interpret estimated coefficients to be an approximate percentage change in the person’s income. For example, the estimated coefficient for a health insurance variable (HEALTH_INS) is \(5.516e-03\). \(100(e^{5.516e-03} - 1) = 0.55\) means that the expected difference in yearly income is 0.55% for an individual with health insurance compared to an individual without one.

Some of the most interesting regression analysis results can be summed up as follows:

  • Individuals with 5+ years of college have 1.53% higher income
  • Individuals working in the Northeastern division have 0.6% higher income than those working in Midwest
  • Each additional commute hour increases income by 0.4%
  • Every ten years, individual’s income increases by 0.2%

Since SEX variable was not included in the final model, we conclude that gender does not have a significant relationship to the individual’s income.

Conclusion

Our preliminary model has adjusted \(\bar{R}^2 = 0.3092\) and F-test indicates that the overall model is significant at \(\alpha=0.05\). However, plotting residuals reveal that the preliminary model doesn’t satisfy the regression assumptions. The manual model doesn’t do any better than the preliminary model in terms of adjusted \(\bar{R}^2\). The vif test reveals that the preliminary model doesn’t seem to exhibit any serious multicollinearity among the independent variables. The model was further narrowed down by runnning both backward elimination and stepwise regression on preliminary model. Both methods leave us with SEX, AGE, NUM_HOURS_WEEK, EDUC, and DIVISION variables to run regression on income variable. This automatic model does slightly better than the preliminary model in terms of adjusted \(\bar{R}^2 = 0.3108\), but the plot of residuals shows that “automatic” model still doesn’t satisfy the regression assumptions.

In order to meet the regression assumptions, we had to transform income, the dependent variable, using the box-cox transformation method. If we box-cox transform our income variable, we get a preliminary transformed model, which has adjusted \(\bar{R}^2 = 0.6029\). Then, we sequentially apply stepwise regression or backward elimination method on this model. Both methods give us two identical models using same independent variables. Finally, we can test for influential observations wrt both y and x values. We decided to exclude outliers wrt y and ran a new regression using reduced data set. At last, we have achieved our best model so far. The preliminary transformed model has adjusted \(\bar{R}^2 = 0.6957\) after stepwise regression, and plots of residuals approximately satisfies the regression assumption even though we have few observations that are possibly outliers wrt to x values.

Lastly, we give an interpretation of the final model. We faced a problem of coefficient interpretation of a transformed model. Here, to interpret the effect of a predictor variable to the outcome variable, we have to:

  1. Assume the transformation to be a natural logarithm transformation (since the value of \(\lambda\) in our transformation was close to zero);
  2. Approximate the inverse of the transformation to be exponentiation \(e^β\) (since exponentiation is the inverse of logarithm function).

As a result, exponentiated coefficients approximately correspond to changes in the ratio of the expected geometric means of the original outcome variable. Consequently, we study the relationships of the most influential variables and present a summary of the most important conclusions.

Data Reference

Steven Ruggles, Katie Genadek, Ronald Goeken, Josiah Grover, and Matthew Sobek. Integrated Public UseMicrodata Series: Version 7.0 [dataset]. Minneapolis, MN: University of Minnesota, 2017.https://doi.org/10.18128/D010.V7.0

LS0tCnRpdGxlOiAiSW5jb21lIFJlZ3Jlc3Npb24gQW5hbHlzaXM6IFByb2plY3QgcmVwb3J0IgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRoZW1lOiB1bml0ZWQKYXV0aG9yOiBDaHVsIEhvIENob2ksIFZhZHltIE92Y2hhcmVua28sIE1hcmlzc2EgQXNobmVyCi0tLQoKIyBJbnRyb2R1Y3Rpb24KSW4gdGhpcyBwcm9qZWN0LCB3ZSBkZWNpZGVkIHRvIHN0dWR5IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBwZXJzb25hbCB2YXJpYWJsZXMgYW5kIGluY29tZSBmcm9tIFVuaXRlZCBTdGF0ZXMgY2l0aXplbnMuIEl0IHdhcyBub3QgZWFzeSB0byBmaW5kIGEgc3VpdGFibGUgZGF0YXNldC4gVGhlIGRhdGEgd2FzIHJldHJpZXZlZCBmcm9tIHRoZSBNaW5uZXNvdGEgUG9wdWxhdGlvbiBDZW50ZXIsIGEgZGF0YWJhc2UgdGhhdCBhbGxvd2VkIGZvciBtYW51YWwgc2VsZWN0aW9uIG9mIHRoZSB2YXJpYWJsZXMgdG8gYmUgaW5jbHVkZWQgaW4gdGhlIGRhdGFzZXQuIEFmdGVyIGNyZWF0aW5nIHRoZSBkYXRhc2V0IG9mIGNob2ljZSwgdGhlIGRhdGEgd2FzIHByZXByb2Nlc3NlZCB0byBhY2NvdW50IGZvciBtaXNzaW5nIHZhbHVlcyBhbmQgb3RoZXIgaW5jb25zaXN0ZW5jaWVzLiBTZXZlcmFsIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyBhcmUgYXBwbGllZCB0byBwcmVwcm9jZXNzZWQgZGF0YSBzZXQsIGluY2x1ZGluZyBhIHByZWxpbWluYXJ5IHJlZ3Jlc3Npb24gb24gYWxsIHZhcmlhYmxlcywgYSByZWdyZXNzaW9uIG9uIG1hbnVhbGx5IHNlbGVjdGVkIHZhcmlhYmxlcywgc3RlcHdpc2UgcmVncmVzc2lvbiwgYW5kIHJlZ3Jlc3Npb24gb24gZGF0YSB0aGF0IHdhcyB0cmFuc2Zvcm1lZCB1c2luZyBib3gtY294IHRyYW5zZm9ybWF0aW9ucy4gVGhlc2UgbW9kZWxzIGFyZSBjb21wYXJlZCBiYXNlZCBvbiB0aGVpciAkXGJhcntSfV4yJCBhbmQgdGhlIHZhbGlkYXRpb24gb2YgdGhlIGFzc3VtZWQgbW9kZWwgYXNzdW1wdGlvbnMuIEFkZGl0aW9uYWxseSwgd2UgZXhwbG9yZSB0aGUgb3V0bGllcnMgaW4gdGhlIGRhdGEgdG8gbWFrZSBhIHBvdGVudGlhbGx5IHN0cm9uZ2VyIG1vZGVsIHdpdGhvdXQgdGhlc2UgdmFsdWVzLiAKCiMgRGF0YSBwcmVwYXJhdGlvbgoKVGhlIGluaXRpYWxseSBzZWxlY3RlZCB2YXJpYWJsZXMgb2J0YWluZWQgZnJvbSB0aGUgTWlubmVzb3RhIFBvcHVsYXRpb24gQ2VudGVyIGFyZToKCjEuIFlFQVIgKENlbnN1cyB5ZWFyKQoyLiBEQVRBTlVNIChEYXRhIHNldCBudW1iZXIpCjMuIFNFUklBTCAoSG91c2Vob2xkIHNlcmlhbCBudW1iZXIpCjQuIEhIV1QgKEhvdXNlaG9sZCB3ZWlnaHQpCjUuIEdRIChHcm91cCBxdWFydGVycyBzdGF0dXMpCjYuIFBFUk5VTSAoUGVyc29uIG51bWJlciBpbiBzYW1wbGUgdW5pdCkKNy4gUEVSV1QgKFBlcnNvbiB3ZWlnaHQpCjguIE5DSElMRCAoTnVtYmVyIG9mIG93biBjaGlsZHJlbiBpbiB0aGUgaG91c2Vob2xkKSA5LiBTRVggKFNleCkKMTAuIEFHRSAoQWdlKQoxMS4gTUFSUk5PIChUaW1lcyBtYXJyaWVkKQoxMi4gUkFDRSAoUmFjZSBbZ2VuZXJhbCB2ZXJzaW9uXSkKMTMuIEhDT1ZBTlkgKEFueSBoZWFsdGggaW5zdXJhbmNlIGNvdmVyYWdlKQoxNC4gRURVQyAoRWR1Y2F0aW9uYWwgYXR0YWlubWVudCBbZ2VuZXJhbCB2ZXJzaW9uXSkgMTUuIFNDSExUWVBFIChQdWJsaWMgb3IgcHJpdmF0ZSBzY2hvb2wpCjE2LiBJTkQgKEluZHVzdHJ5KQoxNy4gV0tTV09SSzIgKFdlZWtzIHdvcmtlZCBsYXN0IHllYXIsIGludGVydmFsbGVkKSAxOC4gVUhSU1dPUksgKFVzdWFsIGhvdXJzIHdvcmtlZCBwZXIgd2VlaykKMTkuIElOQ1RPVCAoVG90YWwgcGVyc29uYWwgaW5jb21lKQoyMC4gSU5DV0FHRSAoV2FnZSBhbmQgc2FsYXJ5IGluY29tZSkKMjEuIFZFVFNUQVQgKFZldGVyYW4gc3RhdHVzIFtnZW5lcmFsIHZlcnNpb25dKQoyMi4gVkVUU1RBVEQgKFZldGVyYW4gc3RhdHVzIFtkZXRhaWxlZCB2ZXJzaW9uXSkKMjMuIFBXU1RBVEUyIChQbGFjZSBvZiB3b3JrOiBzdGF0ZSkKMjQuIFRSQU5USU1FIChUcmF2ZWwgdGltZSB0byB3b3JrKQoKSG93ZXZlciwgdGhlcmUgYXJlIHNvbWUgY29tcGxleGl0aWVzIG9mIHVzaW5nIHRoZSBvcmlnaW5hbCBkYXRhc2V0LiBGaXJzdCBvZiBhbGwsIHRoZSBkYXRhc2V0IGhhcyBmYWN0b3IgdmFyaWFibGVzIG9mIGEgaGlnaCBsZXZlbCAoZS5nLiwgdXAgdG8gMTBrIGxldmVscyBmb3IgdGhlIGluZHVzdHJ5IHZhcmlhYmxlKS4gTW9yZW92ZXIsIHRoZSBkYXRhc2V0IGlzIHByZXNlbnRlZCBpbiB0aGUgZm9ybSBvZiBhIGZyZXF1ZW5jeSB0YWJsZSwgd2l0aCBjb3JyZXNwb25kaW5nIHdlaWdodCB2YWx1ZXMgc2hvd2luZyB0aGUgbnVtYmVyIG9mIGluZGl2aWR1YWxzIHJlcHJlc2VudGluZyBlYWNoIHJvdy4gRmluYWxseSwgdGhlIG51bWJlciBvZiByb3dzIGlzIGNsb3NlIHRvIDEwaywgd2hpY2ggaXMgdG9vIGJpZyBmb3IgdGhlIGFuYWx5c2lzLiAKCkluIHRoaXMgc2VjdGlvbiB3ZSBwcmVwcm9jZXNzIHRoZSBkYXRhIHRvIGFjY291bnQgZm9yIG1pc3NpbmcgdmFsdWVzIGFuZCBvdGhlciBpbmNvbnNpc3RlbmNpZXMuIFdlIGFnZ3JlZ2F0ZSBzZXZlcmFsIHZhcmlhYmxlcywgYXBwbHkgd2VpZ2h0aW5nLCBhbmQgc2FtcGxlIHRoZSBkYXRhc2V0IHRvIDUwMCBvYnNlcnZhdGlvbnMuIAoKIyMgUHJlcHJvY2Vzc2luZwoKYGBge3J9CmRmIDwtIHJlYWQuY3N2KCdkYXRhc2V0LmNzdicpCmRmJFZFVFNUQVQgPC0gYXMuZmFjdG9yKGRmJFZFVFNUQVQpCmRmJElORCA8LSBhcy5mYWN0b3IoZGYkSU5EKQpkZiRTQ0hMVFlQRV9yZXZpc2VkIDwtIGFzLmZhY3RvcihkZiRTQ0hMVFlQRV9yZXZpc2VkKQpkZl9yYXcgPC0gcmVhZC5jc3YoJ2RhdGFzZXRfb3JpZ2luYWwuY3N2JykKaGVhZChkZl9yYXcpCmBgYApUaGlzIGlzIHRoZSBsb29rIG9mIHRoZSBvcmlnaW5hbCBkYXRhc2V0LiAKCmBgYHtyfQppbmNvbWUkSU5EX3JldmlzZWQgPC0gaW5jb21lJElORAppbmNvbWUkSU5EX3JldmlzZWRbaW5jb21lJElORF9yZXZpc2VkID49IDE3MCAmIGluY29tZSRJTkRfcmV2aXNlZCA8PSAzOTkwICBdIDwtICJBZ3JpY3VsdHVyZSwgRm9yZXN0cnksIEZpc2hpbmcgYW5kIEh1bnRpbmcsIGFuZCBNaW5pbmciCmluY29tZSRJTkRfcmV2aXNlZFtpbmNvbWUkSU5EX3JldmlzZWQgPj0gNDA3MCAmIGluY29tZSRJTkRfcmV2aXNlZCA8PSA0NTkwICBdIDwtICJXaG9sZXNhbGUgVHJhZGUiCmluY29tZSRJTkRfcmV2aXNlZFtpbmNvbWUkSU5EX3JldmlzZWQgPj0gNDY3MCAmIGluY29tZSRJTkRfcmV2aXNlZCA8PSA1NzkwICBdIDwtICJSZXRhaWwgVHJhZGUiCmluY29tZSRJTkRfcmV2aXNlZFtpbmNvbWUkSU5EX3JldmlzZWQgPj0gNjA3MCAmIGluY29tZSRJTkRfcmV2aXNlZCA8PSA2MzkwICBdIDwtICJUcmFuc3BvcnRhdGlvbiBhbmQgV2FyZWhvdXNpbmcsIGFuZCBVdGlsaXRpZXMiCmluY29tZSRJTkRfcmV2aXNlZFtpbmNvbWUkSU5EX3JldmlzZWQgPT0gNTcwIHx8IGluY29tZSRJTkRfcmV2aXNlZCA9PSA1ODAgfHwgaW5jb21lJElORF9yZXZpc2VkID09IDU5MCB8fCBpbmNvbWUkSU5EX3JldmlzZWQgPT0gNjcwIHx8IGluY29tZSRJTkRfcmV2aXNlZCA9PSA2ODAgfHwgaW5jb21lJElORF9yZXZpc2VkID09IDY5MF0gPC0gIlRyYW5zcG9ydGF0aW9uIGFuZCBXYXJlaG91c2luZywgYW5kIFV0aWxpdGllcyIKaW5jb21lJElORF9yZXZpc2VkW2luY29tZSRJTkRfcmV2aXNlZCA+PSA2NDcwICYgaW5jb21lJElORF9yZXZpc2VkIDw9IDY3ODAgIF0gPC0iSW5mb3JtYXRpb24iCmluY29tZSRJTkRfcmV2aXNlZFtpbmNvbWUkSU5EX3JldmlzZWQgPj0gNjg3MCAmIGluY29tZSRJTkRfcmV2aXNlZCA8PSA3MTkwICBdIDwtICJGaW5hbmNlIGFuZCBJbnN1cmFuY2UsIGFuZCBSZWFsIEVzdGF0ZSBhbmQgUmVudGFsIGFuZCBMZWFzaW5nIgppbmNvbWUkSU5EX3JldmlzZWRbaW5jb21lJElORF9yZXZpc2VkID49IDcyNzAgJiBpbmNvbWUkSU5EX3JldmlzZWQgPD0gNzc5MCAgXSA8LSAiUHJvZmVzc2lvbmFsLCBTY2llbnRpZmljLCBhbmQgTWFuYWdlbWVudCwgYW5kIEFkbWluaXN0cmF0aXZlLCBhbmQgV2FzdGUgTWFuYWdlbWVudCBTZXJ2aWNlcyIKc3RhdGVzX3JldmlzZWQkSU5EX3JldmlzZWRbc3RhdGVzX3JldmlzZWQkSU5EICVpbiUgYyg1NzAsNTgwLDU5MCw2NzAsNjgwLDY5MCldIDwtICJUcmFuc3BvcnRhdGlvbiBhbmQgV2FyZWhvdXNpbmcsIGFuZCBVdGlsaXRpZXMiCmluY29tZSRJTkRfcmV2aXNlZFtpbmNvbWUkSU5EX3JldmlzZWQgPj0gNzg2MCAmIGluY29tZSRJTkRfcmV2aXNlZCA8PSA4NDcwICBdIDwtICJFZHVjYXRpb25hbCBTZXJ2aWNlcywgYW5kIEhlYWx0aCBDYXJlIGFuZCBTb2NpYWwgQXNzaXN0YW5jZSIKaW5jb21lJElORF9yZXZpc2VkW2luY29tZSRJTkRfcmV2aXNlZCA+PSA4NTYwICYgaW5jb21lJElORF9yZXZpc2VkIDw9IDg2OTAgIF0gPC0gIkFydHMsIEVudGVydGFpbm1lbnQsIGFuZCBSZWNyZWF0aW9uLCBhbmQgQWNjb21tb2RhdGlvbiBhbmQgRm9vZCBTZXJ2aWNlcyIKaW5jb21lJElORF9yZXZpc2VkW2luY29tZSRJTkRfcmV2aXNlZCA+PSA4NzcwICYgaW5jb21lJElORF9yZXZpc2VkIDw9IDkyOTAgIF0gPC0gIk90aGVyIFNlcnZpY2VzLCBFeGNlcHQgUHVibGljIEFkbWluaXN0cmF0aW9uIgppbmNvbWUkSU5EX3JldmlzZWRbaW5jb21lJElORF9yZXZpc2VkID49IDkzNzAgJiBpbmNvbWUkSU5EX3JldmlzZWQgPD0gOTU5MCAgXSA8LSAiUHVibGljIEFkbWluaXN0cmF0aW9uIgppbmNvbWUkSU5EX3JldmlzZWRbaW5jb21lJElORF9yZXZpc2VkID49IDk2NzAgJiBpbmNvbWUkSU5EX3JldmlzZWQgPD0gOTkyMCAgXSA8LSAiQWN0aXZlIER1dHkgTWlsaXRhcnkiCmBgYApJbmR1c3RyeSB2YXJpYWJsZSByZXByZXNlbnRzIGluZHVzdHJ5IGNvZGVzLiBXZSBhZ2dyZWdhdGVkIGBJTkRgIHZhcmlhYmxlIGJ5IGFnZ3JlZ2F0aW5nIGVhY2ggZGF0YSBwb2ludCBpbnRvIGl0cyBhcHByb3ByaWF0ZSBpbmR1c3RyeSBjYXRlZ29yeS4KCmBgYHtyfQojIGFnZ3JlZ2F0aW5nIHRoZSBpbmR1c3RyeSB2YXJpYWJsZQpwYXIobWZyb3c9YygxLDIpKQpwbG90KGRmJElORCkKcGxvdChkZiRJTkRfcmV2aXNlZCkKYGBgCgoKYGBge3J9CnN0YXRlc19yZXZpc2VkIDwtIHJlYWQuY3N2KCJkYXRhc2V0LmNzdiIpCnRhYmxlKHN0YXRlc19yZXZpc2VkJFBXU1RBVEUyX3JldmlzZWQpCnRhYmxlKHN0YXRlc19yZXZpc2VkJFBXU1RBVEUyKQpWaWV3KHN0YXRlc19yZXZpc2VkKQpzdGF0ZXNfcmV2aXNlZCRQV1NUQVRFMl9yZXZpc2VkIDwtIHN0YXRlc19yZXZpc2VkJFBXU1RBVEUyCnN0YXRlc19yZXZpc2VkIDwtIHN0YXRlc19yZXZpc2VkWyEoc3RhdGVzX3JldmlzZWQkUFdTVEFURTJfcmV2aXNlZCA+PSA2MSksXQpzdGF0ZXNfcmV2aXNlZCA8LSBzdGF0ZXNfcmV2aXNlZFshKHN0YXRlc19yZXZpc2VkJFBXU1RBVEUyX3JldmlzZWQgPT0gMCksXQpzdGF0ZXNfcmV2aXNlZCRQV1NUQVRFMl9yZXZpc2VkW3N0YXRlc19yZXZpc2VkJFBXU1RBVEUyX3JldmlzZWQgJWluJSBjKDUzLDQxLDYsMzAsMTYsNTYsMzIsNDksOCw0LDM1LDIsMTUpXSA8LSAiV0VTVCIKc3RhdGVzX3JldmlzZWQkUFdTVEFURTJfcmV2aXNlZFtzdGF0ZXNfcmV2aXNlZCRQV1NUQVRFMl9yZXZpc2VkICVpbiUgYygzOCwyNyw0NiwxOSwzMSwyMCwyOSw1NSwyNiwxNywxOCwzOSldIDwtICJNSURXRVNUIgpzdGF0ZXNfcmV2aXNlZCRQV1NUQVRFMl9yZXZpc2VkW3N0YXRlc19yZXZpc2VkJFBXU1RBVEUyX3JldmlzZWQgJWluJSBjKDQwLDUsNDgsMjIsMjgsMSw0NywyMSw1NCw1MSwzNyw0NSwxMywxMiwxMCwyNCwxMSldIDwtICJTT1VUSCIKc3RhdGVzX3JldmlzZWQkUFdTVEFURTJfcmV2aXNlZFtzdGF0ZXNfcmV2aXNlZCRQV1NUQVRFMl9yZXZpc2VkICVpbiUgYygzMyw1MCwyMywyNSw0NCw5LDM2LDM0LDQyKV0gPC0gIk5PUlRIRUFTVCIKYGBgClRyYW5zZm9ybWVkIGBQV1NUQVRFMmAgYnkgY2F0ZWdvcml6aW5nIGVhY2ggZGF0YSBwb2ludCBpbnRvIGZvdXIgcmVnaW9ucyBvZiB1bml0ZWQgc3RhdGVzLgoKYGBge3J9CmNhdF9yZXZpc2VkJFJBQ0VfcmV2aXNlZCA8LSBjYXRfcmV2aXNlZCRSQUNFCnRhYmxlKGNhdF9yZXZpc2VkJFJBQ0UpCmNhdF9yZXZpc2VkJFJBQ0VfcmV2aXNlZFtjYXRfcmV2aXNlZCRSQUNFX3JldmlzZWQgPT0gMV0gPC0gIldoaXRlIgpjYXRfcmV2aXNlZCRSQUNFX3JldmlzZWRbY2F0X3JldmlzZWQkUkFDRV9yZXZpc2VkID09IDJdIDwtICJCbGFjayIKY2F0X3JldmlzZWQkUkFDRV9yZXZpc2VkW2NhdF9yZXZpc2VkJFJBQ0VfcmV2aXNlZCA9PSAzXSA8LSAiQW1lcmljYW4gSW5kaWFuIgpjYXRfcmV2aXNlZCRSQUNFX3JldmlzZWRbY2F0X3JldmlzZWQkUkFDRV9yZXZpc2VkICVpbiUgYyg0LDUsNildIDwtICJBc2lhbiIKY2F0X3JldmlzZWQkUkFDRV9yZXZpc2VkW2NhdF9yZXZpc2VkJFJBQ0VfcmV2aXNlZCAlaW4lIGMoNyw4LDkpXSA8LSAiT3RoZXIiCmBgYApUcmFuc2Zvcm1lZCBgUkFDRWAgdmFyaWFibGUgYnkgYXNzaWduaW5nIGRlc2NyaXBpdHZlIG5hbWVzIHJhdGhlciB0aGFuIGp1c3Qgc2ltcGxlIG51bWJlcnMuCgpgYGB7cn0KY2F0X3JldmlzZWQkSENPVkFOWV9yZXZpc2VkIDwtIGNhdF9yZXZpc2VkJEhDT1ZBTlkKY2F0X3JldmlzZWQkSENPVkFOWV9yZXZpc2VkW2NhdF9yZXZpc2VkJEhDT1ZBTllfcmV2aXNlZCA9PSAxXSA8LSAiTm8gaGVhbHRoIGluc3VyYW5jZSBjb3ZlcmFnZSIKY2F0X3JldmlzZWQkSENPVkFOWV9yZXZpc2VkW2NhdF9yZXZpc2VkJEhDT1ZBTllfcmV2aXNlZCA9PSAyXSA8LSAiV2l0aCBoZWFsdGggaW5zdXJhbmNlIGNvdmVyYWdlIgpgYGAKVHJhbnNmb3JtZWQgYEhDT1ZBTllgIHZhcmlhYmxlIGJ5IGFzc2lnbmluZyBkZXNjcmlwdGl2ZSBuYW1lcyByYXRoZXIgdGhhbiBqdXN0IHNpbXBsZSBudW1iZXJzLgoKYGBge3J9CmNhdF9yZXZpc2VkJFdLU1dPUksyX3JldmlzZWQgPC0gY2F0X3JldmlzZWQkV0tTV09SSzIKY2F0X3JldmlzZWQkV0tTV09SSzJfcmV2aXNlZFtjYXRfcmV2aXNlZCRXS1NXT1JLMl9yZXZpc2VkID09IDFdIDwtICIxLTEzIHdlZWtzIgpjYXRfcmV2aXNlZCRXS1NXT1JLMl9yZXZpc2VkW2NhdF9yZXZpc2VkJFdLU1dPUksyX3JldmlzZWQgPT0gMl0gPC0gIjE0LTI2IHdlZWtzIgpjYXRfcmV2aXNlZCRXS1NXT1JLMl9yZXZpc2VkW2NhdF9yZXZpc2VkJFdLU1dPUksyX3JldmlzZWQgPT0gM10gPC0gIjI3LTM5IHdlZWtzIgpjYXRfcmV2aXNlZCRXS1NXT1JLMl9yZXZpc2VkW2NhdF9yZXZpc2VkJFdLU1dPUksyX3JldmlzZWQgPT0gNF0gPC0gIjQwLTQ3IHdlZWtzIgpjYXRfcmV2aXNlZCRXS1NXT1JLMl9yZXZpc2VkW2NhdF9yZXZpc2VkJFdLU1dPUksyX3JldmlzZWQgPT0gNV0gPC0gIjQ4LTQ5IHdlZWtzIgpjYXRfcmV2aXNlZCRXS1NXT1JLMl9yZXZpc2VkW2NhdF9yZXZpc2VkJFdLU1dPUksyX3JldmlzZWQgPT0gNl0gPC0gIjUwLTUyIHdlZWtzIgpgYGAKVHJhbnNmb3JtZWQgYFdLU1dPUksyYCB2YXJpYWJsZSBieSBhc3NpZ25pbmcgZGVzY3JpcGl0dmUgbmFtZXMgcmF0aGVyIHRoYW4ganVzdCBzaW1wbGUgbnVtYmVycy4gCgpgYGB7cn0KY2F0X3JldmlzZWQkVkVUU1RBVF9yZXZpc2VkIDwtIGNhdF9yZXZpc2VkJFZFVFNUQVQKY2F0X3JldmlzZWQkVkVUU1RBVF9yZXZpc2VkW2NhdF9yZXZpc2VkJFZFVFNUQVRfcmV2aXNlZCA9PSAxXSA8LSAiTm90IGEgdmV0ZXJhbiIKY2F0X3JldmlzZWQkVkVUU1RBVF9yZXZpc2VkW2NhdF9yZXZpc2VkJFZFVFNUQVRfcmV2aXNlZCA9PSAyXSA8LSAiVmV0ZXJhbiIKYGBgClRyYW5zZm9ybWVkIGBWRVRTVEFUYCB2YXJpYWJsZSBieSBhc3NpZ25pbmcgZGVzY3JpcGl0dmUgbmFtZXMgcmF0aGVyIHRoYW4ganVzdCBzaW1wbGUgbnVtYmVycy4gCgpgYGB7cn0KIyBhZ2dyZWdhdGUgdGhlIGVkdWNhdGlvbiB2YXJpYWJsZQpkZiRFRFVDX3JldmlzZWQgPC0gYXMuZmFjdG9yKHNhcHBseShkZiRFRFVDLCBmdW5jdGlvbih4KSB7CiAgaWYgKHggPT0gMCkgcmV0dXJuKCdubyBzY2hvb2wgb3IgTi9BJykKICBlbHNlIGlmICh4IDwgNykgcmV0dXJuKCcxLTEyIHllYXJzIG9mIHByZS1jb2xsZWdlJykKICBlbHNlIGlmICh4IDwgMTEpIHJldHVybignMS00IHllYXJzIG9mIGNvbGxlZ2UnKQogIGVsc2UgcmV0dXJuICgnNSsgeWVhcnMgb2YgY29sbGVnZScpCn0pKQpwYXIobWZyb3c9YygxLDIpKQpwbG90KGFzLmZhY3RvcihkZiRFRFVDKSkKcGxvdChkZiRFRFVDX3JldmlzZWQpCmBgYApXZSBhZ2dyZWdhdGVkIHRoZSBgRURVQ2AgdmFyaWFibGUgaW50byA0IGxldmVscyB0aGF0IGNhcHR1cmUgaGlnaCBzY2hvb2wsIGNvbGxlZ2UsIGFuZCBhZHZhbmNlZCBjb2xsZWdlIGRlZ3JlZXMuCgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRmKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHNlcSgxLG5yb3coZGYpKSwgeSA9IGRmJElOQ1RPVCwgY29sb3I9ZGYkRURVQ19yZXZpc2VkKSkgKwogIHRoZW1lX2J3KCkKYGBgClVuc3VwcmlzaW5nbHksIHRoZXJlIGlzIGFuIG9idmlvdXMgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgZWR1Y2F0aW9uIGxldmVsIGFuZCBpbmNvbWUuCgpgYGB7cn0KIyBhZ2dyZWdhdGUgdGhlIHNjaG9vbCB0eXBlIHZhcmlhYmxlCmRmJFNDSExUWVBFX3JldmlzZWQgPC0gYXMuZmFjdG9yKHNhcHBseShkZiRTQ0hMVFlQRSwgZnVuY3Rpb24oeCkgewogIGlmICh4ID09IDApIHJldHVybignTi9BJykKICBlbHNlIGlmICh4ID09IDEpIHJldHVybignTm90IEVucm9sbGVkJykKICBlbHNlIGlmICh4ID09IDIpIHJldHVybignUHVibGljIFNjaG9vbCcpCiAgZWxzZSByZXR1cm4gKCdQcml2YXRlIFNjaG9vbCcpCn0pKQpwYXIobWZyb3c9YygxLDIpKQpwbG90KGFzLmZhY3RvcihkZiRTQ0hMVFlQRSkpCnBsb3QoZGYkU0NITFRZUEVfcmV2aXNlZCkKYGBgCgpXZSBkcm9wcGVkIGBJTkNXQUdFYCB2YXJpYWJsZSBhZnRlciByZWFsaXppbmcgaXQgd2FzIG5vdCB2ZXJ5IGRpc2NyaXB0aXZlLiBXZSBkZWNpZGVkIHRvIHVzZSB0b3RhbCBpbmNvbWUgYXMgYSByZXNwb25zZSB2YXJpYWJsZS4KYGBge3J9Cmhpc3QoZGYkSU5DVE9ULCBicmVha3MgPSA4MCwgY29sID0gImxpZ2h0Z3JleSIsIG1haW4gPSAiSG9zdG9ncmFtIG9mIGluY29tZSIpCmBgYAoKYGBge3J9CiMgZHJvcCBvbGQgdmFyaWFibGVzCmRmbiA8LSBzdWJzZXQoZGYsIHNlbGVjdCA9IC1jKE1BUlJOT19yZXZpc2VkLCBSQUNFLCBIQ09WQU5ZLCBJTkQsIFZFVFNUQVQsIFBXU1RBVEUyLCBFRFVDLCBTQ0hMVFlQRSkpCiMgcmVuYW1lIGNvbHVtbnMKY29sbmFtZXMoZGZuKSA8LSBjKCJXRUlHSFQiLCAiTlVNX0NISUxEIiwgIlNFWCIsICJBR0UiLCAiTlVNX01BUlIiLCAiV0VFS1NfV09SS0VEX05VTUVSSUMiLCAiTlVNX0hPVVJTX1dFRUsiLCAiSU5DT01FIiwgIlRSQVZFTF9USU1FIiwgIklORFVTVFJZIiwgIkVEVUMiLCAiRElWSVNJT04iLCAiU0NITF9UWVBFIiwgIlJBQ0UiLCAiSEVBTFRIX0lOUyIsICJXRUVLU19XT1JLRUQiLCAiVkVURVJBTiIpCmhlYWQoZGZuKQpgYGAKCkluIHRoZSBmb2xsb3dpbmcgc2NyaXB0LCB3ZSBjYWxjdWxhdGVkIHRoZSB3ZWlnaHRlZCAoYnkgYFdFSUdIVGApIGluY29tZSBmb3IgdGhlIG9ic2VydmF0aW9ucyB3aXRoIGVxdWFsIHByZWRpY3RvciB2YXJpYWJsZXMuCmBgYHtyfQpsaWJyYXJ5KGRhdGEudGFibGUpCmR0IDwtIGFzLmRhdGEudGFibGUoZGZuKQojIGdyb3VwIGJ5IHByZWRpY3RvciB2YXJpYWJsZXMKZHQgPC0gZHRbLGxpc3QoSU5DT01FPXdlaWdodGVkLm1lYW4oSU5DT01FLCBXRUlHSFQpKSwgc2V0ZGlmZihuYW1lcyhkdCksIGMoIldFSUdIVCIsICJJTkNPTUUiKSldCmhlYWQoZHQpCmBgYApUaGUgZGF0YSBpcyBwcmVwYXJlZCwgYnV0IHRoZSBzaXplIG9mIHRoZSBkYXRhc2V0IGlzIHN0aWxsIGFyb3VuZCAxMCwwMDAgb2JzZXJ2YXRpb25zLiAKCmBgYHtyfQpkaXN0cmlidXRpb25zIDwtIGZ1bmN0aW9uKGR0KSB7CiAgcGFyKG1mcm93PWMoNCw0KSkKICAjIHNob3cgZGlzdHJpYnV0aW9ucyBvZiB0aGUgdmFyaWFibGVzCiAgZm9yIChpIGluIG5hbWVzKGR0KSkgewogICAgaWYgKGlzLm51bWVyaWMoZHRbW2ldXSkpIHsKICAgICAgaGlzdChkdFtbaV1dLCBtYWluID0gaSkKICAgIH0gZWxzZSB7CiAgICAgIHBsb3QoZHRbW2ldXSwgbWFpbiA9IGkpCiAgICB9CiAgfQp9CgojIHZpc3VhbGlzZSBkaXN0cmlidXRpb25zCmRpc3RyaWJ1dGlvbnMoZHQpCmBgYApJdCBzZWVtcyB0aGF0IHRha2luZyBhIHNhbXBsZSBtYWludGFpbmVkIHRoZSBnZW5lcmFsIGRpc3RyaWJ1dGlvbiBwcm9wZXJ0aWVzIG9mIHRoZSB2YXJpYWJsZXMuCgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKZHRzIDwtIGR0W3NhbXBsZShucm93KGR0KSwgNTAwKSwgXQpkaXN0cmlidXRpb25zKGR0cykKYGBgCkl0IGNhbiBiZSBzZWVuIHRoYXQgdGhlIGdlbmVyYWwgZGlzdHJpYnV0aW9uIHByb3BlcnRpZXMgYXJlIGluZGVlZCBjYXB0dXJlZCBpbiB0aGUgNTAwIG9ic2VydmF0aW9uIHNhbXBsZSBzdWJzZXQuCgojIyBEb2N1bWVudGF0aW9uCkFmdGVyIGFsbCBwcmVwcm9jZXNzaW5nIG9wZXJhdGlvbnMsIHRoZSBmaW5hbCBkYXRhc2V0IGNvbnRhaW5lZCAxNiB2YXJpYWJsZXM6Cgp8IFZhcmlhYmxlICAgICAgfCBEZXNjcmlwdGlvbiB8CnwtLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBOVU1fQ0hJTEQgICAgIHwgQ291bnRzIHRoZSBudW1iZXIgb2Ygb3duIGNoaWxkcmVuIChvZiBhbnkgYWdlIG9yIG1hcml0YWwgc3RhdHVzKSByZXNpZGluZyB3aXRoIGVhY2ggaW5kaXZpZHVhbC4gSW5jbHVkZXMgc3RlcC1jaGlsZHJlbiBhbmQgYWRvcHRlZCBjaGlsZHJlbiBhcyB3ZWxsIGFzIGJpb2xvZ2ljYWwgY2hpbGRyZW4uCnwgU0VYICAgICAgICAgICB8IFJlcG9ydHMgd2hldGhlciB0aGUgcGVyc29uIHdhcyBtYWxlIG9yIGZlbWFsZS4KfCBBR0UgICAgICAgICAgIHwgUmVwb3J0cyB0aGUgcGVyc29uJ3MgYWdlIGluIHllYXJzIGFzIG9mIHRoZSBsYXN0IGJpcnRoZGF5Lgp8IEVEVUMgICAgICAgICAgfCBSZXBvcnRzIHRoZSBwZXJzb24ncyBlZHVjYXRpb24gbGV2ZWwuCnwgTlVNX01BUlIgICAgICB8IFJlcG9ydHMgdGhlIG51bWJlciBvZiB0aW1lcyBldmVyLW1hcnJpZWQgcGVyc29ucyBoYXZlIGJlZW4gbWFycmllZC4KfCBOVU1fSE9VUlNfV0VFS3wgUmVwb3J0cyB0aGUgdXN1YWwgbnVtYmVyIG9mIGhvdXJzIHRoZSBwZXJzb24gZWFybmVkIHBlciB3ZWVrLgp8IFRSQVZFTF9USU1FICAgfCBSZXBvcnRzIHRoZSBudW1iZXIgb2YgbWludXRlcyBpdCB1c3VhbGx5IHRvb2sgdGhlIHBlcnNvbiB0byBnZXQgaG9tZSBmcm9tIHdvcmsuCnwgSU5EVVNUUlkgICAgICB8IFJlcG9ydHMgdGhlIHR5cGUgb2YgaW5kdXN0cnkgaW4gd2hpY2ggdGhlIHBlcnNvbiBwZXJmb3JtZWQgYW4gb2NjdXBhdGlvbi4gUGVyc29ucyB3aG8gd29ya2VkIGluIG11bHRpcGxlIGluZHVzdHJpZXMgd2VyZSBhc2tlZCB0byByZXBvcnQgZnJvbSB3aGljaCB0aGV5IGVhcm5lZCB0aGUgbW9zdCBtb25leS4gVW5lbXBsb3llZCBwZXJzb25zIHdlcmUgdG8gcmVwb3J0IHRoZWlyIG1vc3QgcmVjZW50IGluZHVzdHJ5LiAKfCBESVZJU0lPTiAgICAgIHwgUmVwb3J0cyB0aGUgcmVnaW9uIG9mIHRoZSBjb3VudHJ5IHRoYXQgdGhlIHBlcnNvbidzIHByaW1hcnkgd29ya3BsYWNlIGlzIGxvY2F0ZWQuIAp8IFNDSExfVFlQRSAgICAgfCBSZXBvcnRzIHdoZXRoZXIgdGhlIHBlcnNvbiBhdHRlbmRlZCBwdWJsaWMgb3IgcHJpdmF0ZSBzY2hvb2wsIGlmIHRoZXkgd2VyZSBlbnJvbGxlZCBhdCBhbGwuIAp8IFJBQ0UgICAgICAgICAgfCBSZXBvcnRzIHRoZSByYWNlIG9mIHRoZSBwZXJzb24uCnwgSEVBTFRIX0lOUyAgICB8IFJlcG9ydHMgd2hldGhlciBvciBub3QgdGhlIHBlcnNvbiBoYXMgaGVhbHRoIGluc3VyYW5jZSBjb3ZlcmFnZS4KfCBXRUVLU19XT1JLRUQgIHwgUmVwb3J0cyB0aGUgbnVtYmVyIG9mIHdlZWtzIHRoZSBwZXJzb24gd29ya2VkIGxhc3QgeWVhciwgaW50ZXJ2YWxsZWQuIAp8IFdFRUtTX1dPUktFRF9OVU1FUklDfCBSZXBvcnRzIHRoZSBudW1iZXIgb2Ygd2VlayB0aGUgcGVyc29uIHdvcmtlZCBsYXN0IHllYXIuCnwgVkVURVJBTiAgICAgICB8IFJlcG9ydHMgd2hldGhlciBvciBub3QgdGhlIHBlcnNvbiBpcyBhIHZldGVyYW4uCnwgSU5DT01FICAgICAgICB8IFJlcG9ydHMgdGhlIHRvdGFsIHBlcnNvbidzIHByZS10YXggcGVyc29uYWwgaW5jb21lIGZyb20gdGhlIHByZXZpb3VzIHllYXIuIFRoaXMgaXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGluIHRoZSBhbmFseXNpcy4gCgojIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uIEFuYWx5c2lzOiAKCiMjICJQcmVsaW1pbmFyeSIgbW9kZWwKCkluIHRoaXMgZmlyc3QgbW9kZWwsIHdlIGRlY2lkZWQgdG8gcnVuIG11bHRpcGxlIHJlZ3Jlc3Npb24gb24gaW5jb21lIHVzaW5nIGFsbCAxNCBvZiBpbmRlcGVuZGVudCB2YXJpYWJsZXMgaW4gdGhlIGRhdGFzZXQuIEJ1dCBiZWZvcmUgYnVpbGRpbmcgdGhlIG1vZGVsLCB3ZSBtYWRlIHNvbWUgZmluYWwgcHJlcHJvY2Vzc2luZyBzdGVwczoKCigxKSBgV0VFS1NfV09SS0VEX05VTUVSSUNgIHZhcmlhYmxlIGhhcyBiZWVuIHJlbW92ZWQgZnJvbSB0aGUgZGF0YSBzZXQKKDIpIGBOVU1fTUFSUmAgdmFyaWFibGUgaXMgY29udmVydGVkIGZyb20gbnVtZXJpY2FsIHZhcmlhYmxlIHRvIGNhdGVnb3JpY2FsIHZhcmlhYmxlCigzKSBsb25nIGluZHVzdHJ5IG5hbWVzIHNob3J0ZW5lZCBmb3IgcHJldHRpZXIgbW9kZWwgc3VtbWFyaWVzCgpgYGB7cn0Kb3JpZ19kYXRhIDwtIHJlYWQuY3N2KCJkYXRhc2V0X2ZpbmFsX3NhbXBsZS5jc3YiKQpvcmlnX2RhdGEgPC0gb3JpZ19kYXRhWywgIShjb2xuYW1lcyhvcmlnX2RhdGEpICVpbiUgYygiV0VFS1NfV09SS0VEX05VTUVSSUMiKSldCm9yaWdfZGF0YSRJTkRVU1RSWSA8LSBzYXBwbHkob3JpZ19kYXRhJElORFVTVFJZLCBmdW5jdGlvbih4KSBzdWJzdHIoeCwgMCwgNjApKQpvcmlnX2RhdGEkTlVNX01BUlIgPC0gYXMuZmFjdG9yKG9yaWdfZGF0YSROVU1fTUFSUikKbWxyIDwtIGxtKG9yaWdfZGF0YSRJTkNPTUUgfiAuLCBkYXRhID0gb3JpZ19kYXRhICkKc3VtbWFyeShtbHIpCmBgYApGb3IgdGhpcyBtb2RlbCwgJFxiYXJ7Un1eMiQgdHVybmVzIG91dCB0byBiZSAkMC4zMDkyJCwgd2hpY2ggbWVhbnMgdGhhdCB0aGUgbW9kZWwgZmFpbHMgdG8gZGVzY3JpYmUgdGhlIGRhdGEgYWNjdXJhdGVseS4gCgojIyMgRiB0ZXN0IGFuZCBwLXZhbHVlIGZvciAicHJlbGltaW5hcnkiIG1vZGVsCgpgYGB7cn0KcWYoMC45NSwgZGYxID0gMzgsIGRmMiA9IDQ2MSkKYGBgClJ1bm5pbmcgRi10ZXN0IG9uIHRoZSBwcmVsaW1pbmFyeSBtb2RlbCByZXZlYWxzIHRoYXQgdGhlIG1vZGVsIGlzIHNpZ25pZmljYW50IGF0IGFscGhhID0gMC4wNSBiZWNhdXNlIEYtc3RhdGlzdGljIG9mIHRoZSBtb2RlbCAoJDYuODc4JCkgaXMgZ3JlYXRlciB0aGFuICRGKDAuMDUpID0gMS40MzExODIkLiBBbHNvLCBwLXZhbHVlIGlzIGNsb3NlIHRvIGJlaW5nICQwJC4gCgojIyMgYFZJRmAgdGVzdCAobXVsdGljb2xsaW5lYXJpdHkgdGVzdCkKCmBgYHtyfQpsaWJyYXJ5KGNhcikKdmlmKG1scikKbWVhbih2aWYobWxyKVssMV0pICMgbWVhbiBvZiB2aWZzID0gMS42MTMzCm1heCh2aWYobWxyKVssMV0pICMgbWF4aW11bSB2YWx1ZSBvZiB2aWZzID0gMi45NjQyNzgKYGBgClNpbmNlIG1lYW4gb2YgdmlmcyA9ICQxLjYxMzMkIGlzIG5vdCBzdWJzdGFudGlhbGx5IGdyZWF0ZXIgdGhhbiAkMSQgYW5kIG1heGltdW0gdmFsdWUgb2YgdmlmcyA9ICQyLjk2NDI3OCQgaXMgbGVzcyB0aGFuICQxMCQsIHdlIGNhbiBjb25jbHVkZSB0aGF0IG91ciAicHJlbGltaW5hcnkiIG1vZGVsIGRvZXNuJ3QgZXhoaWJpdCBtdWx0aWNvbGxpbmVhcml0eS4KCiMjIyBSZXNpZHVhbCBhbmFseXNpcyBvZiAicHJlbGltaW5hcnkiIG1vZGVsCgpgYGB7cn0KIyBTcGxpdCB0aGUgcGxvdHRpbmcgcGFuZWwgaW50byBhIDIgeCAyIGdyaWQKcGFyKG1mcm93ID0gYygyLCAyKSwgcGNoID0gMTYsIGNleCA9IC41KQpwbG90KG1scikKYGBgCkZvdXIgcGxvdHMgYWJvdmUgc2hvd3MgdGhhdCBvdXIgbW9kZWwgZG9lcyBub3Qgc2F0aXNmeSB0aGUgcmVncmVzc2lvbiBhc3N1bXB0aW9uczoKCiogU3BlY2lmaWNhbGx5LCB0aGUgKipSZXNpZHVhbHMgdnMuIEZpdHRlZCBWYWx1ZXMqKiBwbG90IHNob3dzIGFuIG9idmlvdXMgcGF0dGVybiB0aGF0IGZhbnMgb3V0IGFzIHRoZSBmaXR0ZWQgdmFsdWVzIGluY3JlYXNlLCBzaG93aW5nIHRoYXQgdGhlIHZhcmlhbmNlIG9mIHRoZSBlcnJvciB0ZXJtcyBhcmUgbm90IGNvbnN0YW50LiAKKiBUaGUgKipOb3JtYWwgUHJvYmFiaWxpdHkgUGxvdCoqIHNob3dzIGN1cnZhdHVyZSB0b3dhcmQgdGhlIGVuZHMsIGluZGljYXRpbmcgdGhhdCB0aGUgZXJyb3IgdGVybXMgYXJlIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZC4gCgoKIyMgIk1hbnVhbCIgbW9kZWwKCkZvciB0aGUgc2Vjb25kIG1vZGVsLCB3ZSBkZWNpZGVkIHRvIHNlbGVjdCB2YXJpYWJsZXMgdGhhdCBwYXNzZWQgOTUlIHNpZ25pZmljYW5jZSB0ZXN0IG9uIHRoZSAicHJlbGltaW5hcnkiIG1vZGVsLiBUaGV5IGFyZSBgQUdFYCwgYE5VTV9IT1VSU19XRUVLYCwgYEVEVUNgIGFuZCBgRElWSVNJT05gLgoKYGBge3J9Cm1scl90IDwtIGxtKG9yaWdfZGF0YSRJTkNPTUUgfiBBR0UgKyBOVU1fSE9VUlNfV0VFSyArIEVEVUMgKyBESVZJU0lPTiwgZGF0YSA9b3JpZ19kYXRhKQpzdW1tYXJ5KG1scl90KQpgYGAKRm9yIHRoaXMgbW9kZWwsICRcYmFye1J9XjIkIHR1cm5lZCBvdXQgdG8gYmUgJDAuMzA5MiQsIHdoaWNoIGlzIHRoZSBzYW1lIGFzIHRoZSBvbmUgd2UgZ290IGZyb20gInByZWxpbWluYXJ5IiBtb2RlbC4gSXQgbWVhbnMgdGhhdCBieSByZWR1Y2luZyB0aGUgZGltZW5zaW9uYWxpdHksIHdlIGRpZCBub3QgbG9zZSBtdWNoIGluZm9ybWF0aW9uIGFib3V0IHRoZSBwcmVkaWN0ZWQgdmFyaWFibGUuCgojIyMgRiB0ZXN0IGZvciAibWFudWFsIiBtb2RlbAoKYGBge3J9CnFmKDAuOTUsIGRmMSA9IDgsIGRmMiA9IDQ5MSkKYGBgCiJNYW51YWwiIG1vZGVsIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYmVjYXVzZSBGLXN0YXRpc3RpYzogJDI4LjkkIGlzIGdyZWF0ZXIgdGhhbiAkRihhbHBoYSA9IDAuMDUpID0gMS45NTcyNTMkCgojIyMgUGxvdCAibWFudWFsIiBtb2RlbAoKYGBge3J9CnBhcihtZnJvdyA9IGMoMiwgMiksIHBjaCA9IDE2LCBjZXggPSAuNSkKcGxvdChtbHJfdCkKYGBgCkZvdXIgcGxvdHMgYWJvdmUgc2hvd3MgdGhhdCB0aGUgc2Vjb25kIG1vZGVsIGRvZXMgbm90IHNhdGlzZnkgdGhlIHJlZ3Jlc3Npb24gYXNzdW1wdGlvbnMuIFNpbWlsYXJseSB0byB0aGUgZmlyc3QgbW9kZWwsIFRoZSAqKlJlc2lkdWFscyB2cy4gRml0dGVkIFZhbHVlcyoqIHBsb3QsIGFzIHdlbGwgYXMgdGhlICoqTm9ybWFsIFByb2JhYmlsaXR5IFBsb3QqKiBzaG93IHZhcmlhdGlvbnMgZnJvbSB0aGUgZXhwZWN0ZWQgcGxvdHMsIGluZGljYXRpbmcgdGhlIGVycm9yIHRlcm1zIGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgd2l0aCBjb25zdGFudCB2YXJpYW5jZS4gCgojIyBBdXRvbWF0aWMgbW9kZWwgYnVpbGRpbmcKCiMjIyBCYWNrd2FyZCBlbGltaW5hdGlvbgoKYGBge3J9CnNldC5zZWVkKDEzMzcpCnN0ZXAobWxyLCBkaXJlY3Rpb24gPSAiYmFja3dhcmQiLCB0cmFjZSA9IEYpCmBgYApSdW5uaW5nIGJhY2t3YXJkIGVsaW1pbmF0aW9uIG1ldGhvZCByZXZlYWxzIHRoYXQgd2Ugc2hvdWxkIGluY2x1ZGUgYFNFWGAsIGBBR0VgLCBgTlVNX0hPVVJTX1dFRUtgLCBgRURVQ2AsIGBESVZJU0lPTmAgYXMgb3VyIGluZGVwZW5kZW50IHZhcmlhYmxlcy4gSXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQgdGhlc2UgdmFyaWFibGVzIGFyZSB0aGUgc2FtZSBhcyB0aGUgc2lnbmlmaWNhbnQgdmFyaWFibGVzIGNob3NlbiBmb3IgdGhlIG1hbnVhbCBtb2RlbCBhYm92ZSwgd2l0aCB0aGUgYWRkaXRpb24gb2YgdGhlIGBTRVhgIHZhcmlhYmxlIGhlcmUuIAoKIyMjIFN0ZXB3aXNlIHJlZ3Jlc3Npb24KCmBgYHtyfQpzdGVwKG1sciwgZGlyZWN0aW9uID0gImJvdGgiLCB0cmFjZSA9IEYpCmBgYAoKUnVubmluZyBzdGVwd2lzZSByZWdyZXNzaW9uIG1ldGhvZCByZXZlYWxzIHRoYXQgd2Ugc2hvdWxkIGluY2x1ZGUgYFNFWGAsIGBBR0VgLCBgTlVNX0hPVVJTX1dFRUtgLCBgRURVQ2AsIGBESVZJU0lPTmAgYXMgb3VyIGluZGVwZW5kZW50IHZhcmlhYmxlcy4gVGhpcyBpcyB0aGUgc2FtZSBjb25jbHVzaW9uIGRyYXduIGZyb20gdGhlIGJhY2t3YXJkIGVsaW1pbmF0aW9uLiAKCiMjIE11bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uOiAiQXV0b21hdGljIiBtb2RlbAoKVGhlIGF1dG9tYXRpYyBtb2RlbCBidWlsZGluZyBwcm9jZWR1cmUgc3VnZ2VzdGVkIHRoYXQgd2Ugc2hvdWxkIHVzZSBgU0VYYCwgYEFHRWAsIGBOVU1fSE9VUlNfV0VFS2AsIGBFRFVDYCwgYERJVklTSU9OYCBhcyBvdXIgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiAKYGBge3J9Cm1scl9icyA8LSBsbShvcmlnX2RhdGEkSU5DT01FIH4gU0VYICsgQUdFICsgTlVNX0hPVVJTX1dFRUsgKyBFRFVDICsgRElWSVNJT04sIGRhdGEgPSBvcmlnX2RhdGEpCnN1bW1hcnkobWxyX2JzKQpgYGAKQWRqdXN0ZWQgJFxiYXJ7Un1eMiQgdHVybmVkIG91dCB0byBiZSAkMC4zMTA4JCwgd2hpY2ggaXMgc2xpZ2h0bHkgYmV0dGVyIHRoYW4gdGhlIGZpcnN0IG1vZGVscy4gCgojIyMgUmVzaWR1YWwgYW5hbHlzaXMgb2YgImF1dG9tYXRpYyIgbW9kZWwKCiMjIyMgUmVzaWR1YWwgcGxvdHMKCmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsIDIpLCBwY2ggPSAxNiwgY2V4ID0gLjUpCnBsb3QobWxyX2JzKQpgYGAKQWx0aG91Z2ggdGhlICRcYmFye1J9XjIkIHdhcyBzbGlnaHRseSBpbXByb3ZlZCBjb21wYXJlZCB0byB0aGUgcHJpb3IgbW9kZWxzLCB0aGUgcmVzaWR1YWwgcGxvdHMgc3RpbGwgc2hvd3MgdGhhdCBvdXIgbW9kZWwgZG9lc24ndCBzYXRpc2Z5IHRoZSByZWdyZXNzaW9uIGFzc3VtcHRpb25zLiAgCgojIyMjIEhpc3RvZ3JhbSBwbG90IG9mICJhdXRvbWF0aWMiIG1vZGVsJ3MgcmVzaWR1YWxzLgoKYGBge3J9Cmhpc3QobWxyX2JzJHJlc2lkdWFscywgY29sID0gImxpZ2h0Z3JheSIpCmBgYAoKUGxvdCBvZiByZXNpZHVhbHMgY29uZmlybXMgdGhhdCB0aGV5IGFyZSBkZWZpbml0ZWx5IG5vdCBkaXN0cmlidXRlZCBub3JtYWxseSwgYnV0IGluIGZhY3QgYXJlIHBvc2l0aXZlbHkgc2tld2VkLiBXZSBjYW4gdHJ5IHRvIGFwcGx5IHNvbWUgdHJhbnNmb3JtYXRpb25zIHRvIG5vcm1hbGl6ZSB0aGUgcmVzaWR1YWxzLgoKIyMjIEZpbmRpbmcgQm94LUNveCB0cmFuc2Zvcm1hdGlvbidzIHZhbHVlIG9mIGxhbWJkYQoKYGBge3J9CmxpYnJhcnkoTUFTUykKYl9icyA8LSBib3hjb3gobWxyLCBsYW1iZGEgPSBzZXEoMCwwLjIsLjEpKQpib3hjb3hfdmFsdWVzX2JzIDwtIGNiaW5kKGJfYnMkeCxiX2JzJHkpCnZhbHVlX2JzIDwtIGJveGNveF92YWx1ZXNfYnNbb3JkZXIoLWJfYnMkeSksXVsxLDFdCnZhbHVlX2JzCmBgYApSdW5uaW5nIEJveC1Db3ggdGVzdCBvbiB0aGUgb3JpZ2luYWwgZGF0YSByZXZlYWxzIHRoYXQgd2Ugc2hvdWxkIHVzZSAkXGxhbWJkYSA9IDAuMDE0MTQxNDEkIGZvciBvdXIgdHJhbnNmb3JtYXRpb24gb24gb3VyIGRlcGVuZGVudCB2YXJpYWJsZSB3aGljaCBpcyBpbmNvbWUuIEl0IGNhbiBiZSBzZWVuIGZyb20gdGhlIGFib3ZlIHBsb3QgdGhhdCB0aGUgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24gaGFzIHRoZSBoaWdoZXN0IHBlYWsgYXQgJFxsYW1iZGEgPSAwLjAxNDE0MTQxJC4KCiMjIEJveC1Db3ggdHJhbnNmb3JtZWQgbW9kZWwKCldlIGNyZWF0ZWQgYSBuZXcgbW9kZWwgdXNpbmcgdHJhbnNmb3JtZWQgcHJlZGljdGVkIHZhcmlhYmxlLiBSZS1ydW5uaW5nIGJhY2t3YXJkIGVsaW1pbmF0aW9uIGFnYWluIChzdGVwcyBvbWl0dGVkKSBzaG93ZWQgdGhhdCB3ZSBzaG91bGQgaW5jbHVkZSBgQUdFYCwgYE5VTV9IT1VSU19XRUVLYCwgYFRSQVZFTF9USU1FYCxgSU5EVVNUUllgLCBgRURVQ2AsIGBESVZJU0lPTmAsIGBTQ0hMX1RZUEVgLCBgSEVBTFRIX0lOU2AsIGBXRUVLU19XT1JLRURgLiBJdCBpcyBub3RhYmxlIHRoYXQgdGhlc2UgdmFyaWFibGVzIGFyZSBkaWZmZXJlbnQgdGhhbiB0aGUgb25lcyBwaWNrZWQgb3V0IGZyb20gdGhlIG5vbi10cmFuc2Zvcm1lZCBkYXRhOiBgU0VYYCwgYEFHRWAsIGBOVU1fSE9VUlNfV0VFS2AsIGBFRFVDYCwgYERJVklTSU9OYC4KCmBgYHtyfQpvcmlnX2RhdGEkSU5DT01FX1RSQU5TRk9STUVEIDwtIG9yaWdfZGF0YSRJTkNPTUVedmFsdWVfYnMKbWxyX3RyYW5zZm9ybV9icyA8LSBsbShmb3JtdWxhID0gb3JpZ19kYXRhJElOQ09NRV9UUkFOU0ZPUk1FRCB+IEFHRSArIE5VTV9IT1VSU19XRUVLICsgCiAgICBUUkFWRUxfVElNRSArIElORFVTVFJZICsgRURVQyArIERJVklTSU9OICsgU0NITF9UWVBFICsgSEVBTFRIX0lOUyArIFdFRUtTX1dPUktFRCwgZGF0YSA9IG9yaWdfZGF0YSkKc3VtbWFyeShtbHJfdHJhbnNmb3JtX2JzKQpgYGAKClRoZSB0cmFuc2Zvcm1lZCBtb2RlbCBoYXMgJFxiYXJ7Un1eMiQgZXF1YWwgdG8gJDAuNjAyOSQsIHdoaWNoIGlzIGEgYmlnIGltcHJvdmVtZW50IGZyb20gYWxsIHByZXZpb3VzIG1vZGVscy4gCgojIyBSZXNpZHVhbCBhbmFseXNpcyBvZiB0aGUgdHJhbnNmb3JtZWQgbW9kZWwKCiMjIyBSZXNpZHVhbCBwbG90cwoKYGBge3J9CnBhcihtZnJvdyA9IGMoMiwgMiksIHBjaCA9IDE2LCBjZXggPSAuNSkKcGxvdChtbHJfdHJhbnNmb3JtX2JzKQpgYGAKVGhlIHRyYW5zZm9ybWVkIG1vZGVsIHN0aWxsIHNlZW1zIHRvIG5vdCBxdWl0ZSBzYXRpc2Z5IHRoZSByZWdyZXNzaW9uIGFzc3VtcHRpb25zLiBUaGVyZSBhcmUsIGhvd2V2ZXIsIGltcHJvdmVtZW50cyBpbiB0aGUgKipOb3JtYWwgUHJvYmFiaWxpdHkgUGxvdCoqIGFuZCAqKlJlc2lkdWFscyB2cy4gRml0dGVkIFZhbHVlcyoqIHBsb3QsIGluZGljYXRpbmcgdGhhdCB0aGlzIG1vZGVsIG1vcmUgYWNjdXJhdGVseSBmb2xsb3dzIHRoZSBhc3N1bXB0aW9ucyBvZiBjb25zdGFudCB2YXJpYW5jZSBhbmQgbm9ybWFsaXR5IHRoYW4gdGhlIHByZXZpb3VzIG1vZGVscyBkaXNjdXNzZWQ6CgooMSkgVGhlIE5vcm1hbCBQcm9iYWJpbGl0eSBQbG90IGFwcGVhcnMgZmFpcmx5IGxpbmVhciwgaW5kaWNhdGluZyB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24gbWF5IGJlIHNhdGlzZmllZC4gCigyKSBUaGUgUmVzaWRpYWxzIHZzLiBGaXR0ZWQgVmFsdWVzIHBsb3QgZG9lc24ndCBhcHBlYXIgdG8gaGF2ZSBhbnkgb2J2aW91cyBwYXR0ZXJucywgd2l0aCBvbmx5IHNsaWdodCBmYW5uaW5nIG91dCBmb3IgbGFyZ2VyIGZpdHRlZCB2YWx1ZXMgKHN1c3BlY3RlZCBvdXRsaWVycykuIFRoZXJlIGlzIG5vIGNsZWFyIGluZGljYXRpb24gdGhhdCB0aGUgY29uc3RhbnQgdmFyaWFuY2UgYXNzdW1wdGlvbiBtYXkgYmUgaW52YWxpZGF0ZWQuIAoKIyMjIEhpc3RvZ3JhbSBvZiB0aGUgdHJhbnNmb3JtZWQgbW9kZWwncyByZXNpZHVhbHMKCmBgYHtyfQpoaXN0KG1scl90cmFuc2Zvcm1fYnMkcmVzaWR1YWxzLCBjb2wgPSAibGlnaHRncmF5IikKYGBgCkhpc3RvZ3JhbSBwbG90IGFib3ZlIGNhbiBiZSBzYWlkIGFwcHByb3hpbWF0ZWx5IG5vcm1hbCwgZnVydGhlciBpbmRpY2F0aW5nIHRoYXQgdGhlIG5vcm1hbGl0eSBhc3N1bXB0aW9uIG1heSBob2xkIGZvciB0aGUgdHJhbnNmb3JtZWQgbW9kZWwuIEhvd2V2ZXIsIHRoZSBBbmRlcnNvbi1EYXJsaW5nIChBRCkgU3RhdGlzdGljIHRlc3QgY2FuIGJlIHJ1biB0byBmdXJ0aGVyIGRldGVybWluZSB3aGV0aGVyIG9yIG5vdCB0aGUgbm9ybWFsaXR5IGNvbmRpdGlvbiBpcyBtZXQuIAoKIyMjIEFuZGVyc29uLURhcmxpbmcgc3RhdGlzdGljIHRlc3QKYGBge3J9CmxpYnJhcnkobm9ydGVzdCkKYWQudGVzdChtbHJfdHJhbnNmb3JtX2JzJHJlc2lkdWFscykKYGBgCkFwcGx5dGluZyBBRCB0ZXN0IG9uIG91ciBib3gtY294IHRyYW5zZm9tZWQgYW5kIHN0ZXB3aXNlIHJlZ3Jlc3Npb24gYXBwbGllZCBtb2RlbCByZXZlYWwgdGhhdCBub3JtYWxpdHkgYXNzdW1wdGlvbiBpcyBub3Qgc2F0aXNmaWVkIGJlY2F1c2Ugd2UgY2FuIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIEFEIHRlc3QgYXQgJFxhbHBoYSA9IDAuMDUkLiAKCiMjIyBDb29rJ3MgRGlzdGFuY2UgTWVhc3VyZSBwbG90IGZvciB0aGUgdHJhbnNmb3JtZWQgbW9kZWwKCmBgYHtyfQpsaWJyYXJ5KGNhcikKcGxvdChtbHJfdHJhbnNmb3JtX2JzLCBwY2ggPSAxOCwgY29sID0icmVkIiwgd2hpY2ggPSBjKDQpKSAKY29vayA8LSBjb29rcy5kaXN0YW5jZShtbHJfdHJhbnNmb3JtX2JzKQphYmxpbmUoaCA9IDQvbnJvdyhvcmlnX2RhdGEpLCBjb2w9ImJsdWUiKSAgIyBhZGQgY3V0b2ZmIGxpbmUKYGBgCkFjY29yZGluZyB0byB0aGUgcGxvdCBvZiB0aGUgQ29vaydzIGRpc3RhbmNlcywgdGhlcmUgbWlnaHQgYmUgc2V2ZXJhbCBvdXRseWluZyB2YWx1ZXMuIAoKYGBge3J9CmsgPC0gbGVuZ3RoKGNvZWYobWxyX3RyYW5zZm9ybV9icykpIC0gMQpmXzUwIDwtIHFmKDAuNTAsIGRmMSA9IGsrMSwgZGYyID0gbWxyX3RyYW5zZm9ybV9icyRkZikKZl84MCA8LSBxZigwLjIwLCBkZjEgPSBrKzEsIGRmMiA9IG1scl90cmFuc2Zvcm1fYnMkZGYpCmFueShjb29rID4gZl81MCwgbmEucm0gPSBUKQpgYGAKVGhlcmUgc2VlbXMgdG8gYmUgbm8gaW5mbHVlbnRpYWwgb2JzZXJ2YXRpb25zIGFjY29yZGluZyB0byBDb29rJ3MgZGlzdGFuY2UgdGVzdCBkZXNjcmliZWQgaW4gdGhlIHRleHRib29rLiBOb25lIG9mIHRoZSBvYnNlcnZhdGlvbnMgaGF2ZSBjb29rJ3MgZGlzdGFuY2UgdmFsdWVzIGdyZWF0ZXIgdGhhbiAkRl97WzAuNV19JCwgYnV0IGV2ZXJ5IG9ic2VydmF0aW9uIGhhcyBhIENvb2sncyBkaXN0YW5jZSB2YWx1ZSBsZXNzIHRoYW4gJEZfe1swLjhdfSQuIFNvbWUgc291cmNlcyBzdWdnZXN0IGRpZmZlcmVudCBjdXQtb2ZmIHZhbHVlcyB0byB1c2UgZm9yIHNwb3R0aW5nIGhpZ2hseSBpbmZsdWVudGlhbCBwb2ludHMuIAoKYGBge3J9Cm9taXRfY29vayA8LSB3aGljaChjb29rID4gNC9ucm93KG9yaWdfZGF0YSkpCmxlbmd0aChvbWl0X2Nvb2spCmBgYApVc2luZyB0aGUgY3V0LW9mZiB2YWx1ZSAkRF9pPjQvbiQsIHdoZXJlICRuJCBpcyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyAoc2hvd24gYXMgYSBob3Jpc29udGFsIGxpbmUgb24gdGhlIHBsb3QgYWJvdmUpLCB3ZSBkZXRlY3RlZCAzOSBvdXRseWluZyB2YWx1ZXMuCgojIyMgU3R1ZGVudGl6ZWQgcmVzaWR1YWxzIG9mIHRoZSB0cmFuc29ybWVkIG1vZGVsCgpgYGB7cn0Kc3R1ZF9yZXNpZHVhbCA8LSByc3RhbmRhcmQobWxyX3RyYW5zZm9ybV9icykgIyBzdHVkZW50aXplZCByZXNpZHVhbHMKb21pdF9zdHVkIDwtIHdoaWNoKHN0dWRfcmVzaWR1YWwgPCAtMiB8IHN0dWRfcmVzaWR1YWwgPiAyIHwgaXMubmEoc3R1ZF9yZXNpZHVhbCkpICNzdHVkZW50aXplZCByZXNpZHVhbHMgZ3JlYXRlciB0aGFuIGFic29sdXRlIHZhbHVlIG9mIDIgYW5kIG5hIHZhbHVlcwpsZW5ndGgob21pdF9zdHVkKSAjIDI4IHBvdGVudGlhbCBvdXRsaWVycyB3cnQgeSB2YWx1ZXMKYGBgCkFmdGVyIHN0dWRlbnRpemluZyByZXNpZHVhbHMgZm9yIHRyYW5zZm9ybWVkIG1vZGVsLCB3ZSBzZWxlY3RlZCAyOSBwb3RlbnRpYWwgb3V0bGllcnMgd2l0aCByZXNwZWN0IHRvICR5JCB2YWx1ZXMuIAoKIyMjICBMZXZlcmFnZSB2YWx1ZXMgZm9yIHRoZSB0cmFuc2Zvcm1lZCBtb2RlbAoKYGBge3J9CmxldiA8LSBoYXR2YWx1ZXMobWxyX3RyYW5zZm9ybV9icykKY3V0b2ZmX2xldiA8LSAyICogKGsgKzEpIC8gbgpwbG90KGxldiwgcGNoID0gMTYsIGNleCA9IC41KQphYmxpbmUoaCA9IGN1dG9mZl9sZXYsIGNvbD0icmVkIikgICMgYWRkIGN1dG9mZiBsaW5lCm9taXRfbGV2IDwtIHdoaWNoKGxldiA+IGN1dG9mZl9sZXYpCmxlbmd0aChvbWl0X2xldikgCmBgYApUaGVyZSBhcmUgMjUgb2JzZXJ2YXRpb25zIHRoYXQgYXJlIGFzc29jaWF0ZWQgd2l0aCBsYXJnZSBsZXZlcmFnZSB2YWx1ZXMuCgojIyMgRHJvcHBpbmcgcG90ZW50aWFsIG91dGxpZXJzIGZyb20gdGhlIHRyYW5zZm9ybWVkIG1vZGVsCgpTdHVkeWluZyB0aGUgbmF0dXJlIG9mIG91dGxpZXJzIGlzIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyBhbmFseXNpcywgc28gd2UgZGVjaWRlZCB0byBzZWUgaG93IHRoZSBtb2RlbCB3aWxsIGltcHJvdmUgaWYgd2UgZXhjbHVkZSB0aGUgb3V0bGllcnMgd2l0aCByZXNwZWN0IHRvICR5JCB0aGVtIGZyb20gdGhlIG1vZGVsLiBXZSBkaWRuJ3QgZXhjbHVkZSBvdXRsaWVycyB3cnQgdG8gJHgkIGJlY2F1c2UgaXQgc2hvd2VkIHRvIGRlY3JlYXNlIHRoZSB1dGlsaXR5IG9mIHRoZSBtb2RlbC4gCgpgYGB7cn0Kb3JpZ19kYXRhX3N0dWRfb21pdCA8LSBvcmlnX2RhdGFbLWMob21pdF9zdHVkLCBvbWl0X2Nvb2spLF0KbWxyX3RyYW5zZm9ybV9vcmlnX2JzX29taXQgPC0gbG0oSU5DT01FX1RSQU5TRk9STUVEIH4gQUdFICsgTlVNX0hPVVJTX1dFRUsgKyAKICAgIFRSQVZFTF9USU1FICsgSU5EVVNUUlkgKyBFRFVDICsgRElWSVNJT04gKyBTQ0hMX1RZUEUgKyBIRUFMVEhfSU5TICsgCiAgICBXRUVLU19XT1JLRUQsIGRhdGEgPSBvcmlnX2RhdGFfc3R1ZF9vbWl0KQpzdW1tYXJ5KG1scl90cmFuc2Zvcm1fb3JpZ19ic19vbWl0KQpucm93KG9yaWdfZGF0YSkgLSBucm93KG9yaWdfZGF0YV9zdHVkX29taXQpICMgbnVtYmVyIG9mIGV4Y2x1ZGVkIG9ic2VydmF0aW9ucwpgYGAKQWZ0ZXIgZHJvcHBpbmcgNDcgb2JzZXJ2YXRpb25zIHRoYXQgd2VyZSBjbGFzc2lmaWVkIGFzIHBvdGVudGlhbCBvdXRsaWVycyBtb2RlbCBoYXMgJFxiYXJ7Un1eMiQgZXF1YWwgdG8gJDAuNjk1NyQuIFRoaXMgaXMgbm90YWJseSBpbmNyZWFzZWQgZnJvbSB0aGUgJDAuNjAyOSQgdmFsdWUgYmVmb3JlIHRoZSBvdXRsaWVycyB3ZXJlIGRyb3BwZWQuIAoKIyMjIEFuZGVyc29uLURhcmxpbmcgc3RhdGlzdGljIHRlc3QKYGBge3J9CmxpYnJhcnkobm9ydGVzdCkKYWQudGVzdChtbHJfdHJhbnNmb3JtX29yaWdfYnNfb21pdCRyZXNpZHVhbHMpCmBgYApBcHBseXRpbmcgQUQgdGVzdCBvbiBvdXIgZmluYWwgYmVzdCBtb2RlbCByZXZlYWwgdGhhdCBub3JtYWxpdHkgYXNzdW1wdGlvbiBpcyBzYXRpc2ZpZWQgYmVjYXVzZSB3ZSBmYWlsIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIEFEIHRlc3QgYXQgJFxhbHBoYSA9IDAuMDUkLgoKIyMjIFJlc2lkdWFsIFBsb3RzCmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsIDIpLCBwY2ggPSAxNiwgY2V4ID0gLjUpCnBsb3QobWxyX3RyYW5zZm9ybV9vcmlnX2JzX29taXQpCmBgYAo0IHBsb3RzIGFib3ZlIHNlZW0gdG8gaW5kaWNhdGUgdGhhdCB0aGUgbW9kZWwgd2l0aG91dCBzdXNwZWN0ZWQgb3V0bGllcnMgc2F0aXNmaWVzIHRoZSByZWdyZXNpb24gYXNzdW1wdGlvbnMuIFRoZSAqKk5vcm1hbCBQcm9iYWJpbGl0eSBQbG90Kiogc2VlbXMgbGluZWFyLCBhbmQgdGhlICoqUmVzaWR1YWxzIHZzLiBGaXR0ZWQgVmFsdWUqKiBQbG90IHNob3dzIG9ubHkgbWlub3Igc2lnbnMgb2YgdmlvbGF0aW9uIG9mIHRoZSBjb25zdGFudCB2YXJpYW5jZSBhc3N1bXB0aW9uIChzbGlnaHQgZmFubmluZyBvdXQpLiAKCiMjIEludGVycHJldGluZyB0aGUgbW9kZWwKT3VyIGZpbmFsIG1vZGVsIGRlbW9uc3RyYXRlcyBzYXRpc2ZhY3RvcnkgYWRoZXJlbmNlIHRvIHRoZSByZWdyZXNzaW9uIGFzc3VtcHRpb25zIGFuZCBoYXMgYSByZWxhdGl2ZWx5IGhpZ2ggYWRqdXN0ZWQgUi1zcXVhcmVkIHNjb3JlIG9mIDAuNjk1Ny4gSXQgbWVhbnMgdGhhdCB3ZSBjYW4gZHJhdyBzb21ld2hhdCByZWFzb25lZCBjb25jbHVzaW9ucyBmcm9tIGl0cyBpbnRlcnByZXRhdGlvbi4gCgpVbnN1cnByaXNpbmdseSwgc29tZSBvZiB0aGUgbW9zdCBzaWduaWZpY2FudCBwcmVkaWN0b3JzIGFyZSBgTlVNX0hPVVJTX1dFRUtgIGFuZCBgV0VFS1NfV09SS0VEYCBiZWNhdXNlIHRoZXkgYXJlIHVzdWFsbHkgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCBhIHBlcnNvbidzIGluY29tZS4gTW9yZSBpbnRlcmVzdGluZyByZWxhdGlvbnNoaXBzIGFyZSBkZW1vbnN0cmF0ZWQgYnkgdGhlIHZhcmlhYmxlcyBjb3JyZXNwb25kaW5nIHRvIE5vcnRoZWFzdGVybiBkaXZpc2lvbiBvZiB0aGUgVW5pdGVkIFN0YXRlcywgaGVhbHRoIGluc3VyYW5jZSBjb3ZlcmFnZSwgNSsgeWVhcnMgb2YgY29sbGVnZSwgcHVibGljIHNjaG9vbCB0eXBlLCBldGMuIFRvIHVuZGVyc3RhbmQgdGhlIHJlbGF0aW9uc2hpcHMgb2YgdGhlc2UgdmFyaWFibGVzIHRvIHRoZSBwZXJzb24ncyBpbmNvbWUgdGhlaXIgZXN0aW1hdGVkIGNvZWZmaWNpZW50cyBtdXN0IGJlIGludGVycHJldGVkLiBJbnRlcnByZXRpbmcgdGhlIGNvZWZmaWNpZW50cyBvZiBhIG1vZGVsIHdpdGggdHJhbnNmb3JtZWQgcHJlZGljdGVkIHZhcmlhYmxlIHR1cm5zIG91dCB0byBiZSBhIGRpZmZpY3VsdCB0YXNrLiBTaW5jZSB0aGUgdmFsdWUgb2YgJFxsYW1iZGEgPSAwLjAxNDE0MTQxJCBpbiBvdXIgdHJhbnNmb3JtYXRpb24gd2FzIGNsb3NlIHRvIHplcm8sIHdlIGRlY2lkZWQgdG8gZm9sbG93IHRoZSBwcm9jZWR1cmUgZGVzY3JpYmVkIFtoZXJlXShodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvb3RoZXIvbXVsdC1wa2cvZmFxL2dlbmVyYWwvZmFxaG93LWRvLWktaW50ZXJwcmV0LWEtcmVncmVzc2lvbi1tb2RlbC13aGVuLXNvbWUtdmFyaWFibGVzLWFyZS1sb2ctdHJhbnNmb3JtZWQvKSBhc3N1bWluZyB0aGF0IHRoZSB0cmFuc2Zvcm1hdGlvbiB3YXMgbG9nYXJpdGhtaWMuIFRodXMsIHdlIGNhbiBpbnRlcnByZXQgZXN0aW1hdGVkIGNvZWZmaWNpZW50cyB0byBiZSBhbiBhcHByb3hpbWF0ZSBwZXJjZW50YWdlIGNoYW5nZSBpbiB0aGUgcGVyc29uJ3MgaW5jb21lLiBGb3IgZXhhbXBsZSwgdGhlIGVzdGltYXRlZCBjb2VmZmljaWVudCBmb3IgYSBoZWFsdGggaW5zdXJhbmNlIHZhcmlhYmxlIChgSEVBTFRIX0lOU2ApIGlzICQ1LjUxNmUtMDMkLiAkMTAwKGVeezUuNTE2ZS0wM30gLSAxKSA9IDAuNTUkIG1lYW5zIHRoYXQgdGhlIGV4cGVjdGVkIGRpZmZlcmVuY2UgaW4geWVhcmx5IGluY29tZSBpcyAwLjU1JSBmb3IgYW4gaW5kaXZpZHVhbCB3aXRoIGhlYWx0aCBpbnN1cmFuY2UgY29tcGFyZWQgdG8gYW4gaW5kaXZpZHVhbCB3aXRob3V0IG9uZS4KClNvbWUgb2YgdGhlIG1vc3QgaW50ZXJlc3RpbmcgcmVncmVzc2lvbiBhbmFseXNpcyByZXN1bHRzIGNhbiBiZSBzdW1tZWQgdXAgYXMgZm9sbG93czoKCiogSW5kaXZpZHVhbHMgd2l0aCA1KyB5ZWFycyBvZiBjb2xsZWdlIGhhdmUgMS41MyUgaGlnaGVyIGluY29tZQoqIEluZGl2aWR1YWxzIHdvcmtpbmcgaW4gdGhlIE5vcnRoZWFzdGVybiBkaXZpc2lvbiBoYXZlIDAuNiUgaGlnaGVyIGluY29tZSB0aGFuIHRob3NlIHdvcmtpbmcgaW4gTWlkd2VzdAoqIEVhY2ggYWRkaXRpb25hbCBjb21tdXRlIGhvdXIgaW5jcmVhc2VzIGluY29tZSBieSAwLjQlCiogRXZlcnkgdGVuIHllYXJzLCBpbmRpdmlkdWFsJ3MgaW5jb21lIGluY3JlYXNlcyBieSAwLjIlCgpTaW5jZSBgU0VYYCB2YXJpYWJsZSB3YXMgbm90IGluY2x1ZGVkIGluIHRoZSBmaW5hbCBtb2RlbCwgd2UgY29uY2x1ZGUgdGhhdCBnZW5kZXIgZG9lcyBub3QgaGF2ZSBhIHNpZ25pZmljYW50IHJlbGF0aW9uc2hpcCB0byB0aGUgaW5kaXZpZHVhbCdzIGluY29tZS4KCiMgQ29uY2x1c2lvbgpPdXIgcHJlbGltaW5hcnkgbW9kZWwgaGFzIGFkanVzdGVkICRcYmFye1J9XjIgPSAwLjMwOTIkIGFuZCBGLXRlc3QgaW5kaWNhdGVzIHRoYXQgdGhlIG92ZXJhbGwgbW9kZWwgaXMgc2lnbmlmaWNhbnQgYXQgJFxhbHBoYT0wLjA1JC4gSG93ZXZlciwgcGxvdHRpbmcgcmVzaWR1YWxzIHJldmVhbCB0aGF0IHRoZSBwcmVsaW1pbmFyeSBtb2RlbCBkb2Vzbid0IHNhdGlzZnkgdGhlIHJlZ3Jlc3Npb24gYXNzdW1wdGlvbnMuIFRoZSBtYW51YWwgbW9kZWwgZG9lc24ndCBkbyBhbnkgYmV0dGVyIHRoYW4gdGhlIHByZWxpbWluYXJ5IG1vZGVsIGluIHRlcm1zIG9mIGFkanVzdGVkICRcYmFye1J9XjIkLiBUaGUgYHZpZmAgdGVzdCByZXZlYWxzIHRoYXQgdGhlIHByZWxpbWluYXJ5IG1vZGVsIGRvZXNuJ3Qgc2VlbSB0byBleGhpYml0IGFueSBzZXJpb3VzIG11bHRpY29sbGluZWFyaXR5IGFtb25nIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIFRoZSBtb2RlbCB3YXMgZnVydGhlciBuYXJyb3dlZCBkb3duIGJ5IHJ1bm5uaW5nIGJvdGggYmFja3dhcmQgZWxpbWluYXRpb24gYW5kIHN0ZXB3aXNlIHJlZ3Jlc3Npb24gb24gcHJlbGltaW5hcnkgbW9kZWwuIEJvdGggbWV0aG9kcyBsZWF2ZSB1cyB3aXRoIGBTRVhgLCBgQUdFYCwgYE5VTV9IT1VSU19XRUVLYCwgYEVEVUNgLCBhbmQgYERJVklTSU9OYCB2YXJpYWJsZXMgdG8gcnVuIHJlZ3Jlc3Npb24gb24gaW5jb21lIHZhcmlhYmxlLiBUaGlzIGF1dG9tYXRpYyBtb2RlbCBkb2VzIHNsaWdodGx5IGJldHRlciB0aGFuIHRoZSBwcmVsaW1pbmFyeSBtb2RlbCBpbiB0ZXJtcyBvZiBhZGp1c3RlZCAkXGJhcntSfV4yID0gMC4zMTA4JCwgYnV0IHRoZSBwbG90IG9mIHJlc2lkdWFscyBzaG93cyB0aGF0ICJhdXRvbWF0aWMiIG1vZGVsIHN0aWxsIGRvZXNuJ3Qgc2F0aXNmeSB0aGUgcmVncmVzc2lvbiBhc3N1bXB0aW9ucy4gCgpJbiBvcmRlciB0byBtZWV0IHRoZSByZWdyZXNzaW9uIGFzc3VtcHRpb25zLCB3ZSBoYWQgdG8gdHJhbnNmb3JtIGluY29tZSwgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSwgdXNpbmcgdGhlIGJveC1jb3ggdHJhbnNmb3JtYXRpb24gbWV0aG9kLiBJZiB3ZSBib3gtY294IHRyYW5zZm9ybSBvdXIgaW5jb21lIHZhcmlhYmxlLCB3ZSBnZXQgYSBwcmVsaW1pbmFyeSB0cmFuc2Zvcm1lZCBtb2RlbCwgd2hpY2ggaGFzIGFkanVzdGVkICRcYmFye1J9XjIgPSAwLjYwMjkkLiBUaGVuLCB3ZSBzZXF1ZW50aWFsbHkgYXBwbHkgc3RlcHdpc2UgcmVncmVzc2lvbiBvciBiYWNrd2FyZCBlbGltaW5hdGlvbiBtZXRob2Qgb24gdGhpcyBtb2RlbC4gQm90aCBtZXRob2RzIGdpdmUgdXMgdHdvIGlkZW50aWNhbCBtb2RlbHMgdXNpbmcgc2FtZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIEZpbmFsbHksIHdlIGNhbiB0ZXN0IGZvciBpbmZsdWVudGlhbCBvYnNlcnZhdGlvbnMgd3J0IGJvdGggeSBhbmQgeCB2YWx1ZXMuIFdlIGRlY2lkZWQgdG8gZXhjbHVkZSBvdXRsaWVycyB3cnQgeSBhbmQgcmFuIGEgbmV3IHJlZ3Jlc3Npb24gdXNpbmcgcmVkdWNlZCBkYXRhIHNldC4gQXQgbGFzdCwgd2UgaGF2ZSBhY2hpZXZlZCBvdXIgYmVzdCBtb2RlbCBzbyBmYXIuIFRoZSBwcmVsaW1pbmFyeSB0cmFuc2Zvcm1lZCBtb2RlbCBoYXMgYWRqdXN0ZWQgJFxiYXJ7Un1eMiA9IDAuNjk1NyQgYWZ0ZXIgc3RlcHdpc2UgcmVncmVzc2lvbiwgYW5kIHBsb3RzIG9mIHJlc2lkdWFscyBhcHByb3hpbWF0ZWx5IHNhdGlzZmllcyB0aGUgcmVncmVzc2lvbiBhc3N1bXB0aW9uIGV2ZW4gdGhvdWdoIHdlIGhhdmUgZmV3IG9ic2VydmF0aW9ucyB0aGF0IGFyZSBwb3NzaWJseSBvdXRsaWVycyB3cnQgdG8geCB2YWx1ZXMuICAKCkxhc3RseSwgd2UgZ2l2ZSBhbiBpbnRlcnByZXRhdGlvbiBvZiB0aGUgZmluYWwgbW9kZWwuIFdlIGZhY2VkIGEgW3Byb2JsZW1dKGh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9vdGhlci9tdWx0LXBrZy9mYXEvZ2VuZXJhbC9mYXFob3ctZG8taS1pbnRlcnByZXQtYS1yZWdyZXNzaW9uLW1vZGVsLXdoZW4tc29tZS12YXJpYWJsZXMtYXJlLWxvZy10cmFuc2Zvcm1lZC8pIG9mIGNvZWZmaWNpZW50IGludGVycHJldGF0aW9uIG9mIGEgdHJhbnNmb3JtZWQgbW9kZWwuIEhlcmUsIHRvIGludGVycHJldCB0aGUgZWZmZWN0IG9mIGEgcHJlZGljdG9yIHZhcmlhYmxlIHRvIHRoZSBvdXRjb21lIHZhcmlhYmxlLCB3ZSBoYXZlIHRvOgoKKGEpIEFzc3VtZSB0aGUgdHJhbnNmb3JtYXRpb24gdG8gYmUgYSBuYXR1cmFsIGxvZ2FyaXRobSB0cmFuc2Zvcm1hdGlvbiAoc2luY2UgdGhlIHZhbHVlIG9mICRcbGFtYmRhJCBpbiBvdXIgdHJhbnNmb3JtYXRpb24gd2FzIGNsb3NlIHRvIHplcm8pOwooYikgQXBwcm94aW1hdGUgdGhlIGludmVyc2Ugb2YgdGhlIHRyYW5zZm9ybWF0aW9uIHRvIGJlIGV4cG9uZW50aWF0aW9uICRlXs6yJCAoc2luY2UgZXhwb25lbnRpYXRpb24gaXMgdGhlIGludmVyc2Ugb2YgbG9nYXJpdGhtIGZ1bmN0aW9uKS4KCkFzIGEgcmVzdWx0LCBleHBvbmVudGlhdGVkIGNvZWZmaWNpZW50cyBhcHByb3hpbWF0ZWx5IGNvcnJlc3BvbmQgdG8gY2hhbmdlcyBpbiB0aGUgcmF0aW8gb2YgdGhlIGV4cGVjdGVkIGdlb21ldHJpYyBtZWFucyBvZiB0aGUgb3JpZ2luYWwgb3V0Y29tZSB2YXJpYWJsZS4gQ29uc2VxdWVudGx5LCB3ZSBzdHVkeSB0aGUgcmVsYXRpb25zaGlwcyBvZiB0aGUgbW9zdCBpbmZsdWVudGlhbCB2YXJpYWJsZXMgYW5kIHByZXNlbnQgYSBzdW1tYXJ5IG9mIHRoZSBtb3N0IGltcG9ydGFudCBjb25jbHVzaW9ucy4KCiMgRGF0YSBSZWZlcmVuY2UgClN0ZXZlbiBSdWdnbGVzLCBLYXRpZSBHZW5hZGVrLCBSb25hbGQgR29la2VuLCBKb3NpYWggR3JvdmVyLCBhbmQgTWF0dGhldyBTb2Jlay4gSW50ZWdyYXRlZCBQdWJsaWMgVXNlTWljcm9kYXRhIFNlcmllczogVmVyc2lvbiA3LjAgW2RhdGFzZXRdLiBNaW5uZWFwb2xpcywgTU46IFVuaXZlcnNpdHkgb2YgTWlubmVzb3RhLCAyMDE3Lmh0dHBzOi8vZG9pLm9yZy8xMC4xODEyOC9EMDEwLlY3LjAK