The following analysis process was created using the data set “Mroz87”, containing information of 753 married woman in 1975.

0) Import csv file

df <- read.csv(file = "/Users/brendarangel/Desktop/Dataset.csv")

1) Variable renaming

df <- df %>%
rename(
    EDUCATION = educ, 
    WAGE = wage, 
    EXPERIENCE = exper, 
    CITY = city, 
    AGE = age, 
    LFP = lfp, 
    HOURS = hours, 
    UNEM = unem,
    MTR = mtr,
    KIDS_5 = kids5
    )

2) Summary statistics

# For neatness, we will create another dataframe out of the original just with the required columns. 
    df_2 <- df[c('EDUCATION', 'WAGE', 'EXPERIENCE','CITY','AGE','LFP','HOURS','KIDS_5')]
# Verify datatypes of columns
    str(df_2)
'data.frame':   753 obs. of  8 variables:
 $ EDUCATION : int  12 12 12 12 14 12 16 12 12 12 ...
 $ WAGE      : num  3.35 1.39 4.55 1.1 4.59 ...
 $ EXPERIENCE: int  14 5 15 6 7 33 11 35 24 21 ...
 $ CITY      : int  0 1 0 0 1 1 0 0 0 0 ...
 $ AGE       : int  32 30 35 34 31 54 37 54 48 39 ...
 $ LFP       : int  1 1 1 1 1 1 1 1 1 1 ...
 $ HOURS     : int  1610 1656 1980 456 1568 2032 1440 1020 1458 1600 ...
 $ KIDS_5    : int  1 0 1 0 1 0 0 0 0 0 ...
# Calculate descriptive statistics per column
    summary(df_2)
   EDUCATION          WAGE          EXPERIENCE         CITY             AGE             LFP        
 Min.   : 5.00   Min.   : 0.000   Min.   : 0.00   Min.   :0.0000   Min.   :30.00   Min.   :0.0000  
 1st Qu.:12.00   1st Qu.: 0.000   1st Qu.: 4.00   1st Qu.:0.0000   1st Qu.:36.00   1st Qu.:0.0000  
 Median :12.00   Median : 1.625   Median : 9.00   Median :1.0000   Median :43.00   Median :1.0000  
 Mean   :12.29   Mean   : 2.375   Mean   :10.63   Mean   :0.6428   Mean   :42.54   Mean   :0.5684  
 3rd Qu.:13.00   3rd Qu.: 3.788   3rd Qu.:15.00   3rd Qu.:1.0000   3rd Qu.:49.00   3rd Qu.:1.0000  
 Max.   :17.00   Max.   :25.000   Max.   :45.00   Max.   :1.0000   Max.   :60.00   Max.   :1.0000  
     HOURS            KIDS_5      
 Min.   :   0.0   Min.   :0.0000  
 1st Qu.:   0.0   1st Qu.:0.0000  
 Median : 288.0   Median :0.0000  
 Mean   : 740.6   Mean   :0.2377  
 3rd Qu.:1516.0   3rd Qu.:0.0000  
 Max.   :4950.0   Max.   :3.0000  
    
# Get general view of variable relationships by plotting
    pairs(df_2)

3) Get a better perspective of education-wage, kids-wage & unemployment rate-wage relationship

plot(
    df[c('EDUCATION','WAGE')], 
    main = "EDUCATION AND WAGE RELATIONSHIP", 
    xlab = "EDUCATION", 
    ylab ="WAGE",
    font.lab = 2
    )

plot(
    df[c('KIDS_5','WAGE')], 
    main = "KIDS AND  WAGE RELATIONSHIP", 
    xlab = "KIDS", 
    ylab ="WAGE",
    font.lab = 2
    )

We can observe a clear relationship between both paris of variables. The more education the married woman had, the more wage they perceived. On the other hand, the less children under 5 years old they had, the more wage they perceived.

4) Multiple regression using “WAGE” as dependent variable and “EXPERIENCE”, “CITY”, “EDUCATION”, “KIDS_5” as independent variables. Which variable has a greater impact? Which are statistically significant to the 95%.

First we are going to do an exploratory analysis and check if the cuantitative variables have a normal distribution or not.

        par("mfcol"=c(4, 1))
        hist(df$EXPERIENCE, col="blue")
        hist(df$EDUCATION, col="green")
        hist(df$WAGE, col="red")
        hist(df$KIDS_5, col="yellow") 
        par("mfcol"=c(1, 1))

We can visualy observe that variables EXPERIENCE, WAGE and KIDS_5 are not normalized; whereas EDUCATION seems to be normalized, but we are not sure. In order to be sure, we will use Shapiro Test to confirm it.

        shapiro.test(df$EDUCATION)

    Shapiro-Wilk normality test

data:  df$EDUCATION
W = 0.88634, p-value < 2.2e-16

EDUCATION is not normal either! (p-value < .05)

So all variables are not distributed normally, and besides, their numeric ranges are sort of different from each other, which could affect the model. We are going to go ahead and normalize the data. We could get the log of the variables, but since there are many 0s in our dataset, we will be using min max scaling normalization instead.

normalize <- function(x, na.rm = TRUE) {
            return((x- min(x)) /(max(x)-min(x)))
        }
        par("mfcol"=c(4, 1))
        hist(normalize(df$EXPERIENCE), col="blue")
        hist(normalize(df$EDUCATION), col="green")
        hist(normalize(df$WAGE), col="red")
        hist(normalize(df$KIDS_5), col="yellow") 
        par("mfcol"=c(1, 1))

They still have no normal distribution, but they have the same min - max range now, which can positively affect the model.

Now we are going to check if there is any visible multicolineality between the independent variables that could affect our multiple regression. Since the variables are not distributed normally, we are going to be using spearman correlation.

df_4 <- df[c('EXPERIENCE','CITY','EDUCATION','KIDS_5')]
    cor(df_4, method = 'spearman')
            EXPERIENCE        CITY  EDUCATION      KIDS_5
EXPERIENCE  1.00000000  0.02734453 0.08166274 -0.20686556
CITY        0.02734453  1.00000000 0.17150742 -0.03917689
EDUCATION   0.08166274  0.17150742 1.00000000  0.08951226
KIDS_5     -0.20686556 -0.03917689 0.08951226  1.00000000

Multicolineality Results: No visible strong correlation between them; no need to check p-value.

We are going to go ahead and create the multiple regression.

    model <- lm(normalize(WAGE) ~ normalize(EXPERIENCE) + normalize(CITY) + normalize(EDUCATION) + normalize(KIDS_5), data = df)
    summary(model)

Call:
lm(formula = normalize(WAGE) ~ normalize(EXPERIENCE) + normalize(CITY) + 
    normalize(EDUCATION) + normalize(KIDS_5), data = df)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.24165 -0.07207 -0.01772  0.04785  0.95351 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)           -0.065831   0.015778  -4.172 3.37e-05 ***
normalize(EXPERIENCE)  0.149613   0.024696   6.058 2.18e-09 ***
normalize(CITY)        0.002076   0.009158   0.227 0.820716    
normalize(EDUCATION)   0.215739   0.023319   9.252  < 2e-16 ***
normalize(KIDS_5)     -0.086709   0.025498  -3.401 0.000708 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.1186 on 748 degrees of freedom
Multiple R-squared:  0.1673,    Adjusted R-squared:  0.1629 
F-statistic: 37.58 on 4 and 748 DF,  p-value: < 2.2e-16
Analysis/Interpretation:

The associated p-value of the F-statistic is less than .05, indicating that the model is significant.

If we analyse the p-values of the variables, we can observe that both variables EXPERIENCE, EDUCATION and KIDS_5 are statistically significant (95%). The variable CITY however can be removed from the multiple regression.

# Multiple Regression with no CITY
model <- lm(normalize(WAGE) ~ normalize(EXPERIENCE) + normalize(EDUCATION) + normalize(KIDS_5), data = df)
summary(model)

Call:
lm(formula = normalize(WAGE) ~ normalize(EXPERIENCE) + normalize(EDUCATION) + 
    normalize(KIDS_5), data = df)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.24325 -0.07178 -0.01762  0.04750  0.95435 

Coefficients:
                      Estimate Std. Error t value Pr(>|t|)    
(Intercept)           -0.06498    0.01532  -4.242 2.49e-05 ***
normalize(EXPERIENCE)  0.14955    0.02468   6.060 2.16e-09 ***
normalize(EDUCATION)   0.21661    0.02298   9.425  < 2e-16 ***
normalize(KIDS_5)     -0.08707    0.02543  -3.424 0.000652 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.1186 on 749 degrees of freedom
Multiple R-squared:  0.1673,    Adjusted R-squared:  0.164 
F-statistic: 50.16 on 3 and 749 DF,  p-value: < 2.2e-16
Observations:

By removing the CITY variable, the adjusted R squared only changed from 0.1629 to 0.164, meaning that evidently CITY had not much impact in the model. However, having an R squared this low means that only 16.4 % of the variance in the measure of wages can be predicted by experience, education and having small kids.

Final Answers:

ML Regression: WAGE = -0.06498 + 0.14955EXPERIENCE + 0.21661EDUCATION -.08707*KIDS_5.
Variable with greater impact: EDUCATION.
Statistically significant variables (to 95%): EXPERIENCE, EDUCATION, KIDS_5.

5) From the following pair of variables, which one has more correlation? LFP-AGE; LFP-WAGE; LFP-HOURS.

Without doing any analysis, common sense might suggest that LFP-HOURS will have the strongest correlation, since the more hours of work will obviously mean labor force participation. However, lets prove this through analysis.

First we are going to plot this pairs in order to get a visual perspective of their correlations.

    plot(df[c('LFP','AGE')], main = "LFP vs AGE")

    plot(df[c('LFP','WAGE')], main = "LFP vs WAGE")

    plot(df[c('LFP','HOURS')], main = "LFP vs HOURS")

We can easily observe that variable LFP is dichotomic, whereas the rest are cuantitative. For this reason we will be using the point-biserial correlation for each pair.

x <- unlist(df[c('LFP')])
    cor.test(x,unlist(df[c('AGE')]))

    Pearson's product-moment correlation

data:  x and unlist(df[c("AGE")])
t = -2.2132, df = 751, p-value = 0.02718
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.151075065 -0.009104645
sample estimates:
        cor 
-0.08049811 
    cor.test(x,unlist(df[c('WAGE')]))

    Pearson's product-moment correlation

data:  x and unlist(df[c("WAGE")])
t = 22.748, df = 751, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5943858 0.6791617
sample estimates:
     cor 
0.638708 
    cor.test(x,unlist(df[c('HOURS')]))

    Pearson's product-moment correlation

data:  x and unlist(df[c("HOURS")])
t = 30.254, df = 751, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.7071441 0.7717269
sample estimates:
      cor 
0.7411454 
Final Answer & Analysis:

Out of the three pairs of variables, the one with more correlation is LFP vs HOURS, just as we suggested.

The point-biserial correlations coefficient for this pair is of 0.7411 (a lot higher than the other two pairs) meaning they have a positive correlation (when LFP takes de value of 1, the variables HOURS tends to be higher).

Its p-value is less than .05, meaning that we accept the alterative hypothest and that the correlation is statistically significant. Additionally, the confidence interval further confirms that this correlation is significant.

6) Logistic regression using LFP as dependent variable and AGE, KIDS_5, EDUCATION, HOURS, WAGE, UNEM , CITY & EXPERIENCE as independent variables. Which of these variables are statistically significant to the 90%? Which of them have a positive and which have a negative impact on “LFP”?

First we are going to do an exploratory analysis and check if the cuantitative variables have a normal distribution or not. From excersise 4 we already know that EDUCATION, WAGE, EXPERIENCE and KIDS_5 are not normally distributed, so we are going to do focus on AGE, UNEM, and HOURS.

      par("mfcol"=c(3, 1))
        hist(df$AGE, col="blue")
        hist(df$UNEM, col="green")
        hist(df$HOURS, col="red")
        par("mfcol"=c(1, 1))

None of the variables seem to be normally distributed. Any how, we will be doing once again the Shapiro Test to confirm.

shapiro.test(df$AGE)

    Shapiro-Wilk normality test

data:  df$AGE
W = 0.96162, p-value = 3.829e-13
shapiro.test(df$UNEM)

    Shapiro-Wilk normality test

data:  df$UNEM
W = 0.92283, p-value < 2.2e-16
shapiro.test(df$HOURS)

    Shapiro-Wilk normality test

data:  df$HOURS
W = 0.80675, p-value < 2.2e-16

As expected, none of them follow a normal distribution. Knowing this we are going to go ahead and normalize the variables using min max scaling normalization. The result will probably not create a normal distribution for the variables, but will make the data range equivalent (in terms of range) to the rest of the variables.

  par("mfcol"=c(3, 1))
    hist(normalize(df$AGE), col="blue")
    hist(normalize(df$UNEM), col="green")
    hist(normalize(df$HOURS), col="red")
    par("mfcol"=c(1, 1))

We are now going to see if there is multicolineality between the independent variables that could affect our logistic regression. For this, we will be using spearman, since the variables are not normally distributed.

    df_6 <- df[c('LFP', 'AGE', 'EDUCATION','WAGE','UNEM','HOURS','KIDS_5','CITY','EXPERIENCE')]
    df_6$AGE = normalize(df$AGE)
    df_6$EDUCATION = normalize(df$EDUCATION)
    df_6$WAGE = normalize(df$WAGE)
    df_6$UNEM = normalize(df$UNEM)
    df_6$HOURS = normalize(df$HOURS)
    df_6$KIDS_5 = normalize(df$KIDS_5)
    df_6$EXPERIENCE = normalize(df$EXPERIENCE)
    res <- rcorr(as.matrix(df_6))
    res
             LFP   AGE EDUCATION  WAGE  UNEM HOURS KIDS_5  CITY EXPERIENCE
LFP         1.00 -0.08      0.19  0.64 -0.03  0.74  -0.21 -0.01       0.34
AGE        -0.08  1.00     -0.12 -0.03  0.08 -0.03  -0.43  0.10       0.33
EDUCATION   0.19 -0.12      1.00  0.32  0.07  0.11   0.11  0.16       0.07
WAGE        0.64 -0.03      0.32  1.00  0.00  0.42  -0.12  0.07       0.25
UNEM       -0.03  0.08      0.07  0.00  1.00 -0.06  -0.01  0.18       0.00
HOURS       0.74 -0.03      0.11  0.42 -0.06  1.00  -0.22 -0.02       0.40
KIDS_5     -0.21 -0.43      0.11 -0.12 -0.01 -0.22   1.00 -0.04      -0.19
CITY       -0.01  0.10      0.16  0.07  0.18 -0.02  -0.04  1.00       0.01
EXPERIENCE  0.34  0.33      0.07  0.25  0.00  0.40  -0.19  0.01       1.00

n= 753 


P
           LFP    AGE    EDUCATION WAGE   UNEM   HOURS  KIDS_5 CITY   EXPERIENCE
LFP               0.0272 0.0000    0.0000 0.4311 0.0000 0.0000 0.8658 0.0000    
AGE        0.0272        0.0009    0.3436 0.0345 0.3642 0.0000 0.0081 0.0000    
EDUCATION  0.0000 0.0009           0.0000 0.0478 0.0036 0.0028 0.0000 0.0692    
WAGE       0.0000 0.3436 0.0000           0.9972 0.0000 0.0007 0.0728 0.0000    
UNEM       0.4311 0.0345 0.0478    0.9972        0.0983 0.8042 0.0000 0.9033    
HOURS      0.0000 0.3642 0.0036    0.0000 0.0983        0.0000 0.6568 0.0000    
KIDS_5     0.0000 0.0000 0.0028    0.0007 0.8042 0.0000        0.2426 0.0000    
CITY       0.8658 0.0081 0.0000    0.0728 0.0000 0.6568 0.2426        0.7583    
EXPERIENCE 0.0000 0.0000 0.0692    0.0000 0.9033 0.0000 0.0000 0.7583           
Multicolineality:

There are indeed correlations between independent variables that exceed |.30| in value with p-value of 0, indicating that these correlations are statistically significant. This can be reduced once we remove some variables through out the creation of the model.

Correlation Y and X:

HOURS has the strongest correlation, followed by WAGE. The rest of them have lower correlations with Y. UNEM and CITY have a p-value above .05, meaning they have no relationship with LFP and can be excluded from the model.

Now we are going to check if the target variable is biased (if the target has its data equally distribute between 0s and 1s)

table(df_6$LFP)

  0   1 
325 428 

Since the data is divided similarly, we consider there is no class bias and can continue with our analysis.

We are going to go ahead and create our test/train samples for the model, and create the model afterwards.

df_6 <- df_6[c('LFP', 'AGE', 'EDUCATION','WAGE','HOURS','KIDS_5','EXPERIENCE')]
bound <- floor((nrow(df_6)/4)*3)        
df_6 <- df_6[sample(nrow(df_6)), ]        
df_6.train <- df_6[1:bound, ]     
df_6.test <- df_6[(bound+1):nrow(df_6), ] 
mylogit <- glm(LFP ~ EDUCATION + WAGE + HOURS + KIDS_5 + EXPERIENCE + AGE, data=df_6.train, family=binomial(link="logit"), control = list(maxit = 100))
glm.fit: fitted probabilities numerically 0 or 1 occurred

While creating the model, an error appeared.

This Warning message indicates that there is a variable that perfectly separates zeroes and ones in the target variable. Since this excercise is about creating a logistic regression and not jumping into any other sort of regression, we are going to go ahead and remove this variable from the equation.

To find the variable causing this perfect separation we are going to do a logistics regression for each separate X-Y pair, and remove the one that gives us a warning.

    mylogit2 <- glm(LFP ~ EDUCATION, data = df_6.test, family = "binomial", control = list(maxit = 100))
    mylogit2 <- glm(LFP ~ WAGE, data = df_6.test, family = "binomial", control = list(maxit = 100))
glm.fit: fitted probabilities numerically 0 or 1 occurred
    mylogit2 <- glm(LFP ~ HOURS, data = df_6.test, family = "binomial", control = list(maxit = 100))
glm.fit: fitted probabilities numerically 0 or 1 occurred
    mylogit2 <- glm(LFP ~ KIDS_5, data = df_6.test, family = "binomial", control = list(maxit = 100))
    mylogit2 <- glm(LFP ~ EXPERIENCE, data = df_6.test, family = "binomial", control = list(maxit = 100))
    mylogit2 <- glm(LFP ~ AGE, data = df_6.test, family = "binomial", control = list(maxit = 100))

We can observe that the one giving us a warning is WAGE and HOURS, so we are going to remove them and go ahead and create de logistic regression with the rest of the variables and get the predicted scores.

mylogit <- glm(LFP ~ EDUCATION + KIDS_5 + EXPERIENCE + AGE  , data=df_6.train, family=binomial, control = list(maxit = 100))
predicted <- plogis(predict(mylogit, df_6.test))
summary(mylogit)

Call:
glm(formula = LFP ~ EDUCATION + KIDS_5 + EXPERIENCE + AGE, family = binomial, 
    data = df_6.train, control = list(maxit = 100))

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-2.4286  -0.9645   0.4685   0.9008   2.0606  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept)  -0.7324     0.4028  -1.818 0.069046 .  
EDUCATION     2.0180     0.5548   3.637 0.000276 ***
KIDS_5       -3.8559     0.6486  -5.945 2.77e-09 ***
EXPERIENCE    5.7521     0.6985   8.235  < 2e-16 ***
AGE          -2.9286     0.4608  -6.355 2.08e-10 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 771.05  on 563  degrees of freedom
Residual deviance: 622.88  on 559  degrees of freedom
AIC: 632.88

Number of Fisher Scoring iterations: 4

Next we are going to calculate the best cutoff for the model. Normaly it would be 0.5 (since we have 0s and 1s), but getting the optimal cutoff might improve results.

library(InformationValue)
optCutOff <- optimalCutoff(df_6.test$LFP, predicted) 
optCutOff
[1] 0.4370756

And now we will get the percentage mismatch between predicted and actual values.

misClassError(df_6.test$LFP, predicted, threshold = optCutOff)
[1] 0.2116

The ROC curve.

plotROC(df_6.test$LFP, predicted)

And confusion Matrix.

confusionMatrix(df_6.test$LFP, predicted, threshold = optCutOff)

Interpretation:

We can observe the difference between the null deviance vs the residual deviance in our model’s summary, showing that our model is indeed doing better against a null model.

The percentage mismatch between predicted and actual values is low, which is a good indicator.

The ROC AUC score is of .8218, indicating a good discrimination.

The confusion matrix shows us relatively good results, with an accuracy of 78.83%, precision of 85.98% and sensitivity of 78.63%.

In general, the labor force participation from married woman in 1975 can be widely explained by examining their education, experience, age, and if they had kids under 5 years old.

LS0tCnRpdGxlOiAiVVNBIFdvbWVuJ3MgTGFib3IgRm9yY2UgUGFydGljaXBhdGlvbiBBbmFseXNpcyIKc3VidGl0bGU6ICJCcmVuZGEgUmFuZ2VsIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUaGUgZm9sbG93aW5nIGFuYWx5c2lzIHByb2Nlc3Mgd2FzIGNyZWF0ZWQgdXNpbmcgdGhlIGRhdGEgc2V0ICJNcm96ODciLCBjb250YWluaW5nIGluZm9ybWF0aW9uIG9mIDc1MyBtYXJyaWVkIHdvbWFuIGluIDE5NzUuIAoKIyMjIDApIEltcG9ydCBjc3YgZmlsZQpgYGB7cn0KZGYgPC0gcmVhZC5jc3YoZmlsZSA9ICIvVXNlcnMvYnJlbmRhcmFuZ2VsL0Rlc2t0b3AvRGF0YXNldC5jc3YiKQpgYGAKCgojIyMgMSkgIFZhcmlhYmxlIHJlbmFtaW5nCmBgYHtyfQpkZiA8LSBkZiAlPiUKcmVuYW1lKAoJRURVQ0FUSU9OID0gZWR1YywgCglXQUdFID0gd2FnZSwgCglFWFBFUklFTkNFID0gZXhwZXIsIAoJQ0lUWSA9IGNpdHksIAoJQUdFID0gYWdlLCAKCUxGUCA9IGxmcCwgCglIT1VSUyA9IGhvdXJzLCAKCVVORU0gPSB1bmVtLAoJTVRSID0gbXRyLAoJS0lEU181ID0ga2lkczUKCSkKYGBgCgojIyMgMikgIFN1bW1hcnkgc3RhdGlzdGljcyAKYGBge3J9CiMgRm9yIG5lYXRuZXNzLCB3ZSB3aWxsIGNyZWF0ZSBhbm90aGVyIGRhdGFmcmFtZSBvdXQgb2YgdGhlIG9yaWdpbmFsIGp1c3Qgd2l0aCB0aGUgcmVxdWlyZWQgY29sdW1ucy4gCglkZl8yIDwtIGRmW2MoJ0VEVUNBVElPTicsICdXQUdFJywgJ0VYUEVSSUVOQ0UnLCdDSVRZJywnQUdFJywnTEZQJywnSE9VUlMnLCdLSURTXzUnKV0KCiMgVmVyaWZ5IGRhdGF0eXBlcyBvZiBjb2x1bW5zCglzdHIoZGZfMikKCiMgQ2FsY3VsYXRlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgcGVyIGNvbHVtbgoJc3VtbWFyeShkZl8yKQoJCiMgR2V0IGdlbmVyYWwgdmlldyBvZiB2YXJpYWJsZSByZWxhdGlvbnNoaXBzIGJ5IHBsb3R0aW5nCglwYWlycyhkZl8yKQpgYGAKCgojIyMgMykgR2V0IGEgYmV0dGVyIHBlcnNwZWN0aXZlIG9mIGVkdWNhdGlvbi13YWdlLCBraWRzLXdhZ2UgJiB1bmVtcGxveW1lbnQgcmF0ZS13YWdlIHJlbGF0aW9uc2hpcApgYGB7cn0KcGxvdCgKCWRmW2MoJ0VEVUNBVElPTicsJ1dBR0UnKV0sIAoJbWFpbiA9ICJFRFVDQVRJT04gQU5EIFdBR0UgUkVMQVRJT05TSElQIiwgCgl4bGFiID0gIkVEVUNBVElPTiIsIAoJeWxhYiA9IldBR0UiLAoJZm9udC5sYWIgPSAyCgkpCgpwbG90KAoJZGZbYygnS0lEU181JywnV0FHRScpXSwgCgltYWluID0gIktJRFMgQU5EICBXQUdFIFJFTEFUSU9OU0hJUCIsIAoJeGxhYiA9ICJLSURTIiwgCgl5bGFiID0iV0FHRSIsCglmb250LmxhYiA9IDIKCSkKCmBgYAoKV2UgY2FuIG9ic2VydmUgYSBjbGVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiBib3RoIHBhcmlzIG9mIHZhcmlhYmxlcy4gVGhlIG1vcmUgZWR1Y2F0aW9uIHRoZSBtYXJyaWVkIHdvbWFuIGhhZCwgdGhlIG1vcmUgd2FnZSB0aGV5IHBlcmNlaXZlZC4gT24gdGhlIG90aGVyIGhhbmQsIHRoZSBsZXNzIGNoaWxkcmVuIHVuZGVyIDUgeWVhcnMgb2xkIHRoZXkgaGFkLCB0aGUgbW9yZSB3YWdlIHRoZXkgcGVyY2VpdmVkLiAKCiMjIyA0KSBNdWx0aXBsZSByZWdyZXNzaW9uIHVzaW5nICJXQUdFIiBhcyBkZXBlbmRlbnQgdmFyaWFibGUgYW5kICJFWFBFUklFTkNFIiwgIkNJVFkiLCAiRURVQ0FUSU9OIiwgIktJRFNfNSIgYXMgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiBXaGljaCB2YXJpYWJsZSBoYXMgYSBncmVhdGVyIGltcGFjdD8gV2hpY2ggYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgdG8gdGhlIDk1JS4gCgpGaXJzdCB3ZSBhcmUgZ29pbmcgdG8gZG8gYW4gZXhwbG9yYXRvcnkgYW5hbHlzaXMgYW5kIGNoZWNrIGlmIHRoZSBjdWFudGl0YXRpdmUgdmFyaWFibGVzIGhhdmUgYSBub3JtYWwgZGlzdHJpYnV0aW9uIG9yIG5vdC4KCmBgYHtyfQoKCQlwYXIoIm1mY29sIj1jKDQsIDEpKQoJCWhpc3QoZGYkRVhQRVJJRU5DRSwgY29sPSJibHVlIikKCQloaXN0KGRmJEVEVUNBVElPTiwgY29sPSJncmVlbiIpCgkJaGlzdChkZiRXQUdFLCBjb2w9InJlZCIpCgkJaGlzdChkZiRLSURTXzUsIGNvbD0ieWVsbG93IikgCgkJcGFyKCJtZmNvbCI9YygxLCAxKSkKCmBgYAoKV2UgY2FuIHZpc3VhbHkgb2JzZXJ2ZSB0aGF0IHZhcmlhYmxlcyBFWFBFUklFTkNFLCBXQUdFIGFuZCBLSURTXzUgYXJlIG5vdCBub3JtYWxpemVkOyB3aGVyZWFzIEVEVUNBVElPTiBzZWVtcyB0byBiZSBub3JtYWxpemVkLCBidXQgd2UgYXJlIG5vdCBzdXJlLiBJbiBvcmRlciB0byBiZSBzdXJlLCB3ZSB3aWxsIHVzZSBTaGFwaXJvIFRlc3QgdG8gY29uZmlybSBpdC4gCgpgYGB7cn0KCQlzaGFwaXJvLnRlc3QoZGYkRURVQ0FUSU9OKQpgYGAKCkVEVUNBVElPTiBpcyBub3Qgbm9ybWFsIGVpdGhlciEgKHAtdmFsdWUgPCAuMDUpCgpTbyBhbGwgdmFyaWFibGVzIGFyZSBub3QgZGlzdHJpYnV0ZWQgbm9ybWFsbHksIGFuZCBiZXNpZGVzLCB0aGVpciBudW1lcmljIHJhbmdlcyBhcmUgc29ydCBvZiBkaWZmZXJlbnQgZnJvbSBlYWNoIG90aGVyLCB3aGljaCBjb3VsZCBhZmZlY3QgdGhlIG1vZGVsLiBXZSBhcmUgZ29pbmcgdG8gZ28gYWhlYWQgYW5kIG5vcm1hbGl6ZSB0aGUgZGF0YS4gV2UgY291bGQgZ2V0IHRoZSBsb2cgb2YgdGhlIHZhcmlhYmxlcywgYnV0IHNpbmNlIHRoZXJlIGFyZSBtYW55IDBzIGluIG91ciBkYXRhc2V0LCB3ZSB3aWxsIGJlIHVzaW5nIG1pbiBtYXggc2NhbGluZyBub3JtYWxpemF0aW9uIGluc3RlYWQuIAoKYGBge3J9Cm5vcm1hbGl6ZSA8LSBmdW5jdGlvbih4LCBuYS5ybSA9IFRSVUUpIHsKCQkgICAgcmV0dXJuKCh4LSBtaW4oeCkpIC8obWF4KHgpLW1pbih4KSkpCgkJfQoKCQlwYXIoIm1mY29sIj1jKDQsIDEpKQoJCWhpc3Qobm9ybWFsaXplKGRmJEVYUEVSSUVOQ0UpLCBjb2w9ImJsdWUiKQoJCWhpc3Qobm9ybWFsaXplKGRmJEVEVUNBVElPTiksIGNvbD0iZ3JlZW4iKQoJCWhpc3Qobm9ybWFsaXplKGRmJFdBR0UpLCBjb2w9InJlZCIpCgkJaGlzdChub3JtYWxpemUoZGYkS0lEU181KSwgY29sPSJ5ZWxsb3ciKSAKCQlwYXIoIm1mY29sIj1jKDEsIDEpKQpgYGAKClRoZXkgc3RpbGwgaGF2ZSBubyBub3JtYWwgZGlzdHJpYnV0aW9uLCBidXQgdGhleSBoYXZlIHRoZSBzYW1lIG1pbiAtIG1heCByYW5nZSBub3csIHdoaWNoIGNhbiBwb3NpdGl2ZWx5IGFmZmVjdCB0aGUgbW9kZWwuICAKCk5vdyB3ZSBhcmUgZ29pbmcgdG8gY2hlY2sgaWYgdGhlcmUgaXMgYW55IHZpc2libGUgbXVsdGljb2xpbmVhbGl0eSBiZXR3ZWVuIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgdGhhdCBjb3VsZCBhZmZlY3Qgb3VyIG11bHRpcGxlIHJlZ3Jlc3Npb24uIFNpbmNlIHRoZSB2YXJpYWJsZXMgYXJlIG5vdCBkaXN0cmlidXRlZCBub3JtYWxseSwgd2UgYXJlIGdvaW5nIHRvIGJlIHVzaW5nIHNwZWFybWFuIGNvcnJlbGF0aW9uLiAKCmBgYHtyfQpkZl80IDwtIGRmW2MoJ0VYUEVSSUVOQ0UnLCdDSVRZJywnRURVQ0FUSU9OJywnS0lEU181JyldCgljb3IoZGZfNCwgbWV0aG9kID0gJ3NwZWFybWFuJykKYGBgCgpNdWx0aWNvbGluZWFsaXR5IFJlc3VsdHM6IE5vIHZpc2libGUgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlbTsgbm8gbmVlZCB0byBjaGVjayBwLXZhbHVlLgoKV2UgYXJlIGdvaW5nIHRvIGdvIGFoZWFkIGFuZCBjcmVhdGUgdGhlIG11bHRpcGxlIHJlZ3Jlc3Npb24uCgoKYGBge3J9Cgltb2RlbCA8LSBsbShub3JtYWxpemUoV0FHRSkgfiBub3JtYWxpemUoRVhQRVJJRU5DRSkgKyBub3JtYWxpemUoQ0lUWSkgKyBub3JtYWxpemUoRURVQ0FUSU9OKSArIG5vcm1hbGl6ZShLSURTXzUpLCBkYXRhID0gZGYpCiAgICBzdW1tYXJ5KG1vZGVsKQpgYGAKIyMjIyMgQW5hbHlzaXMvSW50ZXJwcmV0YXRpb246ClRoZSBhc3NvY2lhdGVkIHAtdmFsdWUgb2YgdGhlIEYtc3RhdGlzdGljIGlzIGxlc3MgdGhhbiAuMDUsIGluZGljYXRpbmcgdGhhdCB0aGUgbW9kZWwgaXMgc2lnbmlmaWNhbnQuICAKCklmIHdlIGFuYWx5c2UgdGhlIHAtdmFsdWVzIG9mIHRoZSB2YXJpYWJsZXMsIHdlIGNhbiBvYnNlcnZlIHRoYXQgYm90aCB2YXJpYWJsZXMgRVhQRVJJRU5DRSwgRURVQ0FUSU9OIGFuZCBLSURTXzUgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKDk1JSkuIFRoZSB2YXJpYWJsZSBDSVRZIGhvd2V2ZXIgY2FuIGJlIHJlbW92ZWQgZnJvbSB0aGUgbXVsdGlwbGUgcmVncmVzc2lvbi4KCmBgYHtyfQojIE11bHRpcGxlIFJlZ3Jlc3Npb24gd2l0aCBubyBDSVRZCm1vZGVsIDwtIGxtKG5vcm1hbGl6ZShXQUdFKSB+IG5vcm1hbGl6ZShFWFBFUklFTkNFKSArIG5vcm1hbGl6ZShFRFVDQVRJT04pICsgbm9ybWFsaXplKEtJRFNfNSksIGRhdGEgPSBkZikKc3VtbWFyeShtb2RlbCkKYGBgCiMjIyMjIE9ic2VydmF0aW9uczogCkJ5IHJlbW92aW5nIHRoZSBDSVRZIHZhcmlhYmxlLCB0aGUgYWRqdXN0ZWQgUiBzcXVhcmVkIG9ubHkgY2hhbmdlZCBmcm9tIDAuMTYyOSB0byAwLjE2NCwgbWVhbmluZyB0aGF0IGV2aWRlbnRseSBDSVRZIGhhZCBub3QgbXVjaCBpbXBhY3QgaW4gdGhlIG1vZGVsLiBIb3dldmVyLCBoYXZpbmcgYW4gUiBzcXVhcmVkIHRoaXMgbG93IG1lYW5zIHRoYXQgb25seSAxNi40ICUgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBtZWFzdXJlIG9mIHdhZ2VzIGNhbiBiZSBwcmVkaWN0ZWQgYnkgZXhwZXJpZW5jZSwgZWR1Y2F0aW9uIGFuZCBoYXZpbmcgc21hbGwga2lkcy4gCgojIyMjIyBGaW5hbCBBbnN3ZXJzOiAKTUwgUmVncmVzc2lvbjogV0FHRSA9IC0wLjA2NDk4ICsgMC4xNDk1NSpFWFBFUklFTkNFICsgMC4yMTY2MSpFRFVDQVRJT04gLS4wODcwNypLSURTXzUuICAKVmFyaWFibGUgd2l0aCBncmVhdGVyIGltcGFjdDogRURVQ0FUSU9OLiAgClN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgdmFyaWFibGVzICh0byA5NSUpOiBFWFBFUklFTkNFLCBFRFVDQVRJT04sIEtJRFNfNS4gIAoKIyMjIDUpIEZyb20gdGhlIGZvbGxvd2luZyBwYWlyIG9mIHZhcmlhYmxlcywgd2hpY2ggb25lIGhhcyBtb3JlIGNvcnJlbGF0aW9uPyBMRlAtQUdFOyBMRlAtV0FHRTsgTEZQLUhPVVJTLgoKV2l0aG91dCBkb2luZyBhbnkgYW5hbHlzaXMsIGNvbW1vbiBzZW5zZSBtaWdodCBzdWdnZXN0IHRoYXQgTEZQLUhPVVJTIHdpbGwgaGF2ZSB0aGUgc3Ryb25nZXN0IGNvcnJlbGF0aW9uLCBzaW5jZSB0aGUgbW9yZSBob3VycyBvZiB3b3JrIHdpbGwgb2J2aW91c2x5IG1lYW4gbGFib3IgZm9yY2UgcGFydGljaXBhdGlvbi4gSG93ZXZlciwgbGV0cyBwcm92ZSB0aGlzIHRocm91Z2ggYW5hbHlzaXMuIAoKRmlyc3Qgd2UgYXJlIGdvaW5nIHRvIHBsb3QgdGhpcyBwYWlycyBpbiBvcmRlciB0byBnZXQgYSB2aXN1YWwgcGVyc3BlY3RpdmUgb2YgdGhlaXIgY29ycmVsYXRpb25zLiAKCmBgYHtyfQoJcGxvdChkZltjKCdMRlAnLCdBR0UnKV0sIG1haW4gPSAiTEZQIHZzIEFHRSIpCglwbG90KGRmW2MoJ0xGUCcsJ1dBR0UnKV0sIG1haW4gPSAiTEZQIHZzIFdBR0UiKQoJcGxvdChkZltjKCdMRlAnLCdIT1VSUycpXSwgbWFpbiA9ICJMRlAgdnMgSE9VUlMiKQpgYGAKCldlIGNhbiBlYXNpbHkgb2JzZXJ2ZSB0aGF0IHZhcmlhYmxlIExGUCBpcyBkaWNob3RvbWljLCB3aGVyZWFzIHRoZSByZXN0IGFyZSBjdWFudGl0YXRpdmUuIEZvciB0aGlzIHJlYXNvbiB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBwb2ludC1iaXNlcmlhbCBjb3JyZWxhdGlvbiBmb3IgZWFjaCBwYWlyLgoJCmBgYHtyfQp4IDwtIHVubGlzdChkZltjKCdMRlAnKV0pCgoJY29yLnRlc3QoeCx1bmxpc3QoZGZbYygnQUdFJyldKSkKCWNvci50ZXN0KHgsdW5saXN0KGRmW2MoJ1dBR0UnKV0pKQoJY29yLnRlc3QoeCx1bmxpc3QoZGZbYygnSE9VUlMnKV0pKQoKYGBgCgojIyMjIyBGaW5hbCBBbnN3ZXIgJiBBbmFseXNpczogCgkJCk91dCBvZiB0aGUgdGhyZWUgcGFpcnMgb2YgdmFyaWFibGVzLCB0aGUgb25lIHdpdGggbW9yZSBjb3JyZWxhdGlvbiBpcyBMRlAgdnMgSE9VUlMsIGp1c3QgYXMgd2Ugc3VnZ2VzdGVkLiAKClRoZSBwb2ludC1iaXNlcmlhbCBjb3JyZWxhdGlvbnMgY29lZmZpY2llbnQgZm9yIHRoaXMgcGFpciBpcyBvZiAwLjc0MTEgKGEgbG90IGhpZ2hlciB0aGFuIHRoZSBvdGhlciB0d28gcGFpcnMpIG1lYW5pbmcgdGhleSBoYXZlIGEgcG9zaXRpdmUgY29ycmVsYXRpb24gKHdoZW4gTEZQIHRha2VzIGRlIHZhbHVlIG9mIDEsIHRoZSB2YXJpYWJsZXMgSE9VUlMgdGVuZHMgdG8gYmUgaGlnaGVyKS4gIAoKSXRzIHAtdmFsdWUgaXMgbGVzcyB0aGFuIC4wNSwgbWVhbmluZyB0aGF0IHdlIGFjY2VwdCB0aGUgYWx0ZXJhdGl2ZSBoeXBvdGhlc3QgYW5kIHRoYXQgdGhlIGNvcnJlbGF0aW9uIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuIEFkZGl0aW9uYWxseSwgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZnVydGhlciBjb25maXJtcyB0aGF0IHRoaXMgY29ycmVsYXRpb24gaXMgc2lnbmlmaWNhbnQuICAKCgoKIyMjIDYpIExvZ2lzdGljIHJlZ3Jlc3Npb24gdXNpbmcgTEZQIGFzIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgQUdFLCBLSURTXzUsIEVEVUNBVElPTiwgSE9VUlMsIFdBR0UsIFVORU0gLCBDSVRZICYgRVhQRVJJRU5DRSBhcyBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIFdoaWNoIG9mIHRoZXNlIHZhcmlhYmxlcyBhcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCB0byB0aGUgOTAlPyBXaGljaCBvZiB0aGVtIGhhdmUgYSBwb3NpdGl2ZSBhbmQgd2hpY2ggaGF2ZSBhIG5lZ2F0aXZlIGltcGFjdCBvbiAiTEZQIj8KCkZpcnN0IHdlIGFyZSBnb2luZyB0byBkbyBhbiBleHBsb3JhdG9yeSBhbmFseXNpcyBhbmQgY2hlY2sgaWYgdGhlIGN1YW50aXRhdGl2ZSB2YXJpYWJsZXMgaGF2ZSBhIG5vcm1hbCBkaXN0cmlidXRpb24gb3Igbm90LiBGcm9tIGV4Y2Vyc2lzZSA0IHdlIGFscmVhZHkga25vdyB0aGF0IEVEVUNBVElPTiwgV0FHRSwgRVhQRVJJRU5DRSBhbmQgS0lEU181IGFyZSBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHNvIHdlIGFyZSBnb2luZyB0byBkbyBmb2N1cyBvbiBBR0UsIFVORU0sIGFuZCBIT1VSUy4KCmBgYHtyfQoJICBwYXIoIm1mY29sIj1jKDMsIDEpKQoJCWhpc3QoZGYkQUdFLCBjb2w9ImJsdWUiKQoJCWhpc3QoZGYkVU5FTSwgY29sPSJncmVlbiIpCgkJaGlzdChkZiRIT1VSUywgY29sPSJyZWQiKQoJCXBhcigibWZjb2wiPWMoMSwgMSkpCmBgYAoKTm9uZSBvZiB0aGUgdmFyaWFibGVzIHNlZW0gdG8gYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQuIEFueSBob3csIHdlIHdpbGwgYmUgZG9pbmcgb25jZSBhZ2FpbiB0aGUgU2hhcGlybyBUZXN0IHRvIGNvbmZpcm0uCgpgYGB7cn0Kc2hhcGlyby50ZXN0KGRmJEFHRSkKc2hhcGlyby50ZXN0KGRmJFVORU0pCnNoYXBpcm8udGVzdChkZiRIT1VSUykKYGBgCkFzIGV4cGVjdGVkLCBub25lIG9mIHRoZW0gZm9sbG93IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gS25vd2luZyB0aGlzIHdlIGFyZSBnb2luZyB0byBnbyBhaGVhZCBhbmQgbm9ybWFsaXplIHRoZSB2YXJpYWJsZXMgdXNpbmcgbWluIG1heCBzY2FsaW5nIG5vcm1hbGl6YXRpb24uIFRoZSByZXN1bHQgd2lsbCBwcm9iYWJseSBub3QgY3JlYXRlIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBmb3IgdGhlIHZhcmlhYmxlcywgYnV0IHdpbGwgbWFrZSB0aGUgZGF0YSByYW5nZSBlcXVpdmFsZW50IChpbiB0ZXJtcyBvZiByYW5nZSkgdG8gdGhlIHJlc3Qgb2YgdGhlIHZhcmlhYmxlcy4KCmBgYHtyfQogIHBhcigibWZjb2wiPWMoMywgMSkpCgloaXN0KG5vcm1hbGl6ZShkZiRBR0UpLCBjb2w9ImJsdWUiKQoJaGlzdChub3JtYWxpemUoZGYkVU5FTSksIGNvbD0iZ3JlZW4iKQoJaGlzdChub3JtYWxpemUoZGYkSE9VUlMpLCBjb2w9InJlZCIpCglwYXIoIm1mY29sIj1jKDEsIDEpKQpgYGAKCldlIGFyZSBub3cgZ29pbmcgdG8gc2VlIGlmIHRoZXJlIGlzIG11bHRpY29saW5lYWxpdHkgYmV0d2VlbiB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIHRoYXQgY291bGQgYWZmZWN0IG91ciBsb2dpc3RpYyByZWdyZXNzaW9uLiBGb3IgdGhpcywgd2Ugd2lsbCBiZSB1c2luZyBzcGVhcm1hbiwgc2luY2UgdGhlIHZhcmlhYmxlcyBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiAKCgpgYGB7cn0KCWRmXzYgPC0gZGZbYygnTEZQJywgJ0FHRScsICdFRFVDQVRJT04nLCdXQUdFJywnVU5FTScsJ0hPVVJTJywnS0lEU181JywnQ0lUWScsJ0VYUEVSSUVOQ0UnKV0KCWRmXzYkQUdFID0gbm9ybWFsaXplKGRmJEFHRSkKCWRmXzYkRURVQ0FUSU9OID0gbm9ybWFsaXplKGRmJEVEVUNBVElPTikKCWRmXzYkV0FHRSA9IG5vcm1hbGl6ZShkZiRXQUdFKQoJZGZfNiRVTkVNID0gbm9ybWFsaXplKGRmJFVORU0pCglkZl82JEhPVVJTID0gbm9ybWFsaXplKGRmJEhPVVJTKQoJZGZfNiRLSURTXzUgPSBub3JtYWxpemUoZGYkS0lEU181KQoJZGZfNiRFWFBFUklFTkNFID0gbm9ybWFsaXplKGRmJEVYUEVSSUVOQ0UpCglyZXMgPC0gcmNvcnIoYXMubWF0cml4KGRmXzYpKQoJcmVzCmBgYAoKIyMjIyMgTXVsdGljb2xpbmVhbGl0eTogClRoZXJlIGFyZSBpbmRlZWQgY29ycmVsYXRpb25zIGJldHdlZW4gaW5kZXBlbmRlbnQgdmFyaWFibGVzIHRoYXQgZXhjZWVkIHwuMzB8IGluIHZhbHVlIHdpdGggcC12YWx1ZSBvZiAwLCBpbmRpY2F0aW5nIHRoYXQgdGhlc2UgY29ycmVsYXRpb25zIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBUaGlzIGNhbiBiZSByZWR1Y2VkIG9uY2Ugd2UgcmVtb3ZlIHNvbWUgdmFyaWFibGVzIHRocm91Z2ggb3V0IHRoZSBjcmVhdGlvbiBvZiB0aGUgbW9kZWwuIAoKIyMjIyMgQ29ycmVsYXRpb24gWSBhbmQgWDogIApIT1VSUyBoYXMgdGhlIHN0cm9uZ2VzdCBjb3JyZWxhdGlvbiwgZm9sbG93ZWQgYnkgV0FHRS4gVGhlIHJlc3Qgb2YgdGhlbSBoYXZlIGxvd2VyIGNvcnJlbGF0aW9ucyB3aXRoIFkuIApVTkVNIGFuZCBDSVRZIGhhdmUgYSBwLXZhbHVlIGFib3ZlIC4wNSwgbWVhbmluZyB0aGV5IGhhdmUgbm8gcmVsYXRpb25zaGlwIHdpdGggTEZQIGFuZCBjYW4gYmUgZXhjbHVkZWQgZnJvbSB0aGUgbW9kZWwuIAoKTm93IHdlIGFyZSBnb2luZyB0byBjaGVjayBpZiB0aGUgdGFyZ2V0IHZhcmlhYmxlIGlzIGJpYXNlZCAoaWYgdGhlIHRhcmdldCBoYXMgaXRzIGRhdGEgZXF1YWxseSBkaXN0cmlidXRlIGJldHdlZW4gMHMgYW5kIDFzKQoKYGBge3J9CnRhYmxlKGRmXzYkTEZQKQpgYGAKU2luY2UgdGhlIGRhdGEgaXMgZGl2aWRlZCBzaW1pbGFybHksIHdlIGNvbnNpZGVyIHRoZXJlIGlzIG5vIGNsYXNzIGJpYXMgYW5kIGNhbiBjb250aW51ZSB3aXRoIG91ciBhbmFseXNpcy4gCgpXZSBhcmUgZ29pbmcgdG8gZ28gYWhlYWQgYW5kIGNyZWF0ZSBvdXIgdGVzdC90cmFpbiBzYW1wbGVzIGZvciB0aGUgbW9kZWwsIGFuZCBjcmVhdGUgdGhlIG1vZGVsIGFmdGVyd2FyZHMuIAoKYGBge3J9CmRmXzYgPC0gZGZfNltjKCdMRlAnLCAnQUdFJywgJ0VEVUNBVElPTicsJ1dBR0UnLCdIT1VSUycsJ0tJRFNfNScsJ0VYUEVSSUVOQ0UnKV0KYm91bmQgPC0gZmxvb3IoKG5yb3coZGZfNikvNCkqMykgICAgICAgIAoKZGZfNiA8LSBkZl82W3NhbXBsZShucm93KGRmXzYpKSwgXSAgICAgICAgCmRmXzYudHJhaW4gPC0gZGZfNlsxOmJvdW5kLCBdICAgICAKZGZfNi50ZXN0IDwtIGRmXzZbKGJvdW5kKzEpOm5yb3coZGZfNiksIF0gCgpteWxvZ2l0IDwtIGdsbShMRlAgfiBFRFVDQVRJT04gKyBXQUdFICsgSE9VUlMgKyBLSURTXzUgKyBFWFBFUklFTkNFICsgQUdFLCBkYXRhPWRmXzYudHJhaW4sIGZhbWlseT1iaW5vbWlhbChsaW5rPSJsb2dpdCIpLCBjb250cm9sID0gbGlzdChtYXhpdCA9IDEwMCkpCmBgYAoKV2hpbGUgY3JlYXRpbmcgdGhlIG1vZGVsLCBhbiBlcnJvciBhcHBlYXJlZC4gCgpUaGlzIFdhcm5pbmcgbWVzc2FnZSBpbmRpY2F0ZXMgdGhhdCB0aGVyZSBpcyBhIHZhcmlhYmxlIHRoYXQgcGVyZmVjdGx5IHNlcGFyYXRlcyB6ZXJvZXMgYW5kIG9uZXMgaW4gdGhlIHRhcmdldCB2YXJpYWJsZS4gU2luY2UgdGhpcyBleGNlcmNpc2UgaXMgYWJvdXQgY3JlYXRpbmcgYSBsb2dpc3RpYyByZWdyZXNzaW9uIGFuZCBub3QganVtcGluZyBpbnRvIGFueSBvdGhlciBzb3J0IG9mIHJlZ3Jlc3Npb24sIHdlIGFyZSBnb2luZyB0byBnbyBhaGVhZCBhbmQgcmVtb3ZlIHRoaXMgdmFyaWFibGUgZnJvbSB0aGUgZXF1YXRpb24uCgpUbyBmaW5kIHRoZSB2YXJpYWJsZSBjYXVzaW5nIHRoaXMgcGVyZmVjdCBzZXBhcmF0aW9uIHdlIGFyZSBnb2luZyB0byBkbyBhIGxvZ2lzdGljcyByZWdyZXNzaW9uIGZvciBlYWNoIHNlcGFyYXRlIFgtWSBwYWlyLCBhbmQgcmVtb3ZlIHRoZSBvbmUgdGhhdCBnaXZlcyB1cyBhIHdhcm5pbmcuIAoKYGBge3J9CglteWxvZ2l0MiA8LSBnbG0oTEZQIH4gRURVQ0FUSU9OLCBkYXRhID0gZGZfNi50ZXN0LCBmYW1pbHkgPSAiYmlub21pYWwiLCBjb250cm9sID0gbGlzdChtYXhpdCA9IDEwMCkpCglteWxvZ2l0MiA8LSBnbG0oTEZQIH4gV0FHRSwgZGF0YSA9IGRmXzYudGVzdCwgZmFtaWx5ID0gImJpbm9taWFsIiwgY29udHJvbCA9IGxpc3QobWF4aXQgPSAxMDApKQoJbXlsb2dpdDIgPC0gZ2xtKExGUCB+IEhPVVJTLCBkYXRhID0gZGZfNi50ZXN0LCBmYW1pbHkgPSAiYmlub21pYWwiLCBjb250cm9sID0gbGlzdChtYXhpdCA9IDEwMCkpCglteWxvZ2l0MiA8LSBnbG0oTEZQIH4gS0lEU181LCBkYXRhID0gZGZfNi50ZXN0LCBmYW1pbHkgPSAiYmlub21pYWwiLCBjb250cm9sID0gbGlzdChtYXhpdCA9IDEwMCkpCglteWxvZ2l0MiA8LSBnbG0oTEZQIH4gRVhQRVJJRU5DRSwgZGF0YSA9IGRmXzYudGVzdCwgZmFtaWx5ID0gImJpbm9taWFsIiwgY29udHJvbCA9IGxpc3QobWF4aXQgPSAxMDApKQoJbXlsb2dpdDIgPC0gZ2xtKExGUCB+IEFHRSwgZGF0YSA9IGRmXzYudGVzdCwgZmFtaWx5ID0gImJpbm9taWFsIiwgY29udHJvbCA9IGxpc3QobWF4aXQgPSAxMDApKQpgYGAKCgpXZSBjYW4gb2JzZXJ2ZSB0aGF0IHRoZSBvbmUgZ2l2aW5nIHVzIGEgd2FybmluZyBpcyBXQUdFIGFuZCBIT1VSUywgc28gd2UgYXJlIGdvaW5nIHRvIHJlbW92ZSB0aGVtIGFuZCBnbyBhaGVhZCBhbmQgY3JlYXRlIGRlIGxvZ2lzdGljIHJlZ3Jlc3Npb24gd2l0aCB0aGUgcmVzdCBvZiB0aGUgdmFyaWFibGVzIGFuZCBnZXQgdGhlIHByZWRpY3RlZCBzY29yZXMuCgoKYGBge3J9Cm15bG9naXQgPC0gZ2xtKExGUCB+IEVEVUNBVElPTiArIEtJRFNfNSArIEVYUEVSSUVOQ0UgKyBBR0UgICwgZGF0YT1kZl82LnRyYWluLCBmYW1pbHk9Ymlub21pYWwsIGNvbnRyb2wgPSBsaXN0KG1heGl0ID0gMTAwKSkKCnByZWRpY3RlZCA8LSBwbG9naXMocHJlZGljdChteWxvZ2l0LCBkZl82LnRlc3QpKQoKc3VtbWFyeShteWxvZ2l0KQpgYGAKCk5leHQgd2UgYXJlIGdvaW5nIHRvIGNhbGN1bGF0ZSB0aGUgYmVzdCBjdXRvZmYgZm9yIHRoZSBtb2RlbC4gTm9ybWFseSBpdCB3b3VsZCBiZSAwLjUgKHNpbmNlIHdlIGhhdmUgMHMgYW5kIDFzKSwgYnV0IGdldHRpbmcgdGhlIG9wdGltYWwgY3V0b2ZmIG1pZ2h0IGltcHJvdmUgcmVzdWx0cy4gCmBgYHtyfQpsaWJyYXJ5KEluZm9ybWF0aW9uVmFsdWUpCm9wdEN1dE9mZiA8LSBvcHRpbWFsQ3V0b2ZmKGRmXzYudGVzdCRMRlAsIHByZWRpY3RlZCkgCm9wdEN1dE9mZgpgYGAKCkFuZCBub3cgd2Ugd2lsbCBnZXQgdGhlIHBlcmNlbnRhZ2UgbWlzbWF0Y2ggYmV0d2VlbiBwcmVkaWN0ZWQgYW5kIGFjdHVhbCB2YWx1ZXMuIApgYGB7cn0KbWlzQ2xhc3NFcnJvcihkZl82LnRlc3QkTEZQLCBwcmVkaWN0ZWQsIHRocmVzaG9sZCA9IG9wdEN1dE9mZikKYGBgClRoZSBST0MgY3VydmUuIApgYGB7cn0KcGxvdFJPQyhkZl82LnRlc3QkTEZQLCBwcmVkaWN0ZWQpCmBgYApBbmQgY29uZnVzaW9uIE1hdHJpeC4KYGBge3J9CmNvbmZ1c2lvbk1hdHJpeChkZl82LnRlc3QkTEZQLCBwcmVkaWN0ZWQsIHRocmVzaG9sZCA9IG9wdEN1dE9mZikKYGBgCgoKIyMjIyBJbnRlcnByZXRhdGlvbjogIApXZSBjYW4gb2JzZXJ2ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBudWxsIGRldmlhbmNlIHZzIHRoZSByZXNpZHVhbCBkZXZpYW5jZSBpbiBvdXIgbW9kZWwncyBzdW1tYXJ5LCBzaG93aW5nIHRoYXQgb3VyIG1vZGVsIGlzIGluZGVlZCBkb2luZyBiZXR0ZXIgYWdhaW5zdCBhIG51bGwgbW9kZWwuCgpUaGUgcGVyY2VudGFnZSBtaXNtYXRjaCBiZXR3ZWVuIHByZWRpY3RlZCBhbmQgYWN0dWFsIHZhbHVlcyBpcyBsb3csIHdoaWNoIGlzIGEgZ29vZCBpbmRpY2F0b3IuIAoKVGhlIFJPQyBBVUMgc2NvcmUgaXMgb2YgLjgyMTgsIGluZGljYXRpbmcgYSBnb29kIGRpc2NyaW1pbmF0aW9uLgoKVGhlIGNvbmZ1c2lvbiBtYXRyaXggc2hvd3MgdXMgcmVsYXRpdmVseSBnb29kIHJlc3VsdHMsIHdpdGggYW4gYWNjdXJhY3kgb2YgNzguODMlLCBwcmVjaXNpb24gb2YgODUuOTglIGFuZCBzZW5zaXRpdml0eSBvZiA3OC42MyUuCgpJbiBnZW5lcmFsLCB0aGUgbGFib3IgZm9yY2UgcGFydGljaXBhdGlvbiBmcm9tIG1hcnJpZWQgd29tYW4gaW4gMTk3NSBjYW4gYmUgd2lkZWx5IGV4cGxhaW5lZCBieSBleGFtaW5pbmcgdGhlaXIgZWR1Y2F0aW9uLCBleHBlcmllbmNlLCBhZ2UsIGFuZCBpZiB0aGV5IGhhZCBraWRzIHVuZGVyIDUgeWVhcnMgb2xkLiAKCgo=