1 Introduction

This data set was obtained from [Data World] https://data.world.com. The data consists of information on students gathered from two different schools in Portugal about students habits and lives outside of school to see what impact these external factors might have on their final grade in mathematics. The data was collected through school surveys and questionnaires.

1.1 Variable Description

  • school(x1) - student’s school (binary: ‘GP’ - Gabriel Pereira or ‘MS’ - Mousinho da Silveira)
  • sex(x2) - student’s sex (binary: ‘F’ - female or ‘M’ - male)
  • age(x3) - student’s age
  • address(x4) - student’s home address type (binary: ‘U’ - urban or ‘R’ - rural)
  • famsize(x5) - family size
  • Pstatus(x6) - parent’s cohabitation status (binary: ‘T’ - living together or ‘A’ - apart)
  • Medu(x7) - mother’s education (numeric: 0 - none, 1 - primary education (4th grade), 2 – 5th to 9th grade, 3 – secondary education or 4 – higher education)
  • Fedu(x8) - father’s education
  • Mjob(x9) - mother’s job (nominal: ‘teacher’, ‘health’ care related, civil ‘services’ (e.g. administrative or police), ‘at_home’ or ‘other’)
  • Fjob(x10) - father’s job
  • reason - reason to choose this school (nominal: close to ‘home’, school ‘reputation’, ‘course’ preference or ‘other’)
  • guardian(x11) - student’s guardian (nominal: ‘mother’, ‘father’ or ‘other’)
  • traveltime(x12) - home to school travel time (numeric: 1 - <15 min., 2 - 15 to 30 min., 3 - 30 min. to 1 hour, or 4 - >1 hour)
  • studytime(x13) - weekly study time (numeric: 1 - <2 hours, 2 - 2 to 5 hours, 3 - 5 to 10 hours, or 4 - >10 hours)
  • failures(x14) - number of past class failures (numeric: n if 1<=n<3, else 4)
  • schoolsup(x15) - extra educational support
  • famsup(x16) - family educational support
  • paid - extra paid classes within the course subject
  • activities(x17) - extra-curricular activities
  • nursery(x18) - attended nursery school
  • higher(x19) - wants to take higher education
  • internet(x20) - Internet access at home
  • romantic(x21) - with a romantic relationship
  • famrel(x22) - quality of family relationships (numeric: from 1 - very bad to 5 - excellent)
  • freetime(x23) - free time after school (numeric: from 1 - very low to 5 - very high)
  • goout(x24) - going out with friends (numeric: from 1 - very low to 5 - very high)
  • Dalc(x25) - workday alcohol consumption (numeric: from 1 - very low to 5 - very high)
  • Walc(x26) - weekend alcohol consumption (numeric: from 1 - very low to 5 - very high)
  • health(x27) - current health status (numeric: from 1 - very bad to 5 - very good)
  • absences(x28) - number of school absences
  • G1 -(x29) first period grade (numeric: from 0 to 20)
  • G2 -(x30) second period grade (numeric: from 0 to 20)
  • G3(y) - final grade (numeric: from 0 to 20, output target)

1.2 Practical Questions

The question trying to be answered throughout this data analysis is what the association is between students study habits, previous grades and lives outside of school, and their final math grade. Another question I have is how good are each of the selected variables at predicting a student’s final grade in math. Is there one that is the best? Also, which model will be the best in predicting which students will have the best math grade?

2 Exploratory Data Analysis

First, the data is uploaded.

students0 <- read.csv("https://raw.githubusercontent.com/AvaDeSt/STA-321/refs/heads/main/student-mat.csv", header = TRUE)
students=read.table("https://raw.githubusercontent.com/AvaDeSt/STA-321/refs/heads/main/student-mat.csv",sep=";",header=TRUE)

model = lm(G3 ~ Medu + Fedu + traveltime +  failures + freetime + goout + Walc + Dalc + famrel + absences + health + studytime + G1 + G2 , data = students)
kable(summary(model)$coef, caption ="Statistics of Regression Coefficients")
Statistics of Regression Coefficients
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3.5970277 0.8245461 -4.3624334 0.0000166
Medu 0.1294986 0.1154386 1.1217965 0.2626573
Fedu -0.1267697 0.1148130 -1.1041406 0.2702308
traveltime 0.1270425 0.1427922 0.8897017 0.3741890
failures -0.2918970 0.1440120 -2.0268930 0.0433711
freetime 0.0629164 0.1042155 0.6037150 0.5463933
goout -0.0164534 0.1006105 -0.1635356 0.8701837
Walc 0.1526548 0.1073597 1.4219008 0.1558753
Dalc -0.1465988 0.1434807 -1.0217314 0.3075581
famrel 0.3325243 0.1099238 3.0250450 0.0026550
absences 0.0367829 0.0122202 3.0099975 0.0027869
health 0.0702189 0.0701544 1.0009194 0.3175027
studytime -0.1498508 0.1213265 -1.2351029 0.2175553
G1 0.1395635 0.0562891 2.4794077 0.0135929
G2 0.9908354 0.0499823 19.8237135 0.0000000
Students <- students %>%
  mutate(GA = (G1 + G2) / 2)
Students.num <- select(Students,"absences", "famrel", "failures", "GA", "G3")

As part of my data preparation, I looked over all the numerical variables in my data set. I opted to keep the variables that had significant p-values. This included the number of classes failed previously, the student’s family relationship quality, the number of absences a student has had, and the first and second period grades of the student. To make the model more simple, I combined the first and second period grades into a new variable that takes the average of the two. I tried combing several other variables together to create new ones but none of them held any statistical significance.

2.1 Full Model

full.model = lm(G3 ~ absences +  famrel + GA + failures, data = Students.num)
kable(summary(full.model)$coef, caption ="Statistics of Regression Coefficients")
Statistics of Regression Coefficients
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3.5358762 0.6143636 -5.755348 0.0000000
absences 0.0387264 0.0130061 2.977546 0.0030870
famrel 0.2659179 0.1159748 2.292894 0.0223857
GA 1.1802399 0.0327789 36.006051 0.0000000
failures -0.2361464 0.1505524 -1.568533 0.1175677
par(mfrow=c(2,2))
plot(full.model)

The QQ plot indicates that the data is not quite a normal distribution. The variance of the data is also not constant and the data is clumped more together at the right side of the graph. There also appears to be one outlier to the far right in the lower right graph.

vif(full.model)
## absences   famrel       GA failures 
## 1.005934 1.004011 1.158279 1.163780
barplot(vif(full.model), main = "VIF Values", horiz = FALSE, col = "steelblue")

Since all of the VIF values are close to 1 and do not exceed 4, multicollinearity is not an issue.

2.2 Transformations

To help correct the non constant variance of the data, I am going to perform a boxcox transformation.

par(pty = "s", mfrow = c(2, 2), oma=c(.1,.1,.1,.1), mar=c(4, 0, 2, 0))
Students.num$G3_adjusted <- Students.num$G3 + 3

boxcox(G3_adjusted ~ absences + famrel + GA + log(failures +1)  
       , data = Students.num, lambda = seq(0, 1, length = 10), 
       xlab=expression(paste(lambda, ": log-failures")))

boxcox(G3_adjusted ~ absences+ famrel + GA + failures  
       , data = Students.num, lambda = seq(0, 1, length = 10), 
       xlab=expression(paste(lambda, ": failures")))

boxcox(G3_adjusted ~ absences + log(famrel) + GA + failures
       , data = Students.num, lambda = seq(0, 1, length = 10), 
       xlab=expression(paste(lambda, ": log-famrel")))

boxcox(G3_adjusted ~ absences+ famrel + GA + failures  
       , data = Students.num, lambda = seq(0, 1, length = 10), 
       xlab=expression(paste(lambda, ": famrel")))

In order to do the boxcox transformation, I had to change my response variable “G3” to be positive. Also, when taking the log of “failures” i also had to change that to be positive by adding 1.

2.3 Square Root Transformation

sqrt.G3.log.fa = lm((G3_adjusted)^0.5 ~ absences + log(famrel) + GA + failures, data = Students.num)
kable(summary(sqrt.G3.log.fa)$coef, caption = "log-transformed model")
log-transformed model
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.5080307 0.1202400 12.541834 0.0000000
absences 0.0099199 0.0024872 3.988330 0.0000795
log(famrel) 0.1182455 0.0677417 1.745534 0.0816795
GA 0.1745119 0.0062707 27.829736 0.0000000
failures -0.0599612 0.0288019 -2.081847 0.0380080
par(mfrow = c(2,2))
plot(sqrt.G3.log.fa)

Similar to the boxcox model, I had to adjust G3 to be positive.

2.4 Log Transformation

log.G3 = lm(log(G3_adjusted) ~ absences + failures + famrel + G3 , data = Students.num)
kable(summary(log.G3)$coef, caption = "log-transformed model")
log-transformed model
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.3702189 0.0379432 36.1123735 0.0000000
absences 0.0054941 0.0009126 6.0203578 0.0000000
failures -0.0054064 0.0105155 -0.5141379 0.6074468
famrel 0.0016374 0.0081272 0.2014675 0.8404381
G3 0.1051142 0.0017053 61.6397733 0.0000000
par(mfrow = c(2,2))
plot(log.G3)

## Goodness of Fit measures

select=function(m){ 
 e = m$resid                           
 n0 = length(e)                        
 SSE=(m$df)*(summary(m)$sigma)^2       
 R.sq=summary(m)$r.squared             
 R.adj=summary(m)$adj.r                
 MSE=(summary(m)$sigma)^2              
 Cp=(SSE/MSE)-(n0-2*(n0-m$df))         
 AIC=n0*log(SSE)-n0*log(n0)+2*(n0-m$df)          
 SBC=n0*log(SSE)-n0*log(n0)+(log(n0))*(n0-m$df)  
 X=model.matrix(m)                     
 H=X%*%solve(t(X)%*%X)%*%t(X)          
 d=e/(1-diag(H))                       
 PRESS=t(d)%*%d   
 tbl = as.data.frame(cbind(SSE=SSE, R.sq=R.sq, R.adj = R.adj, Cp = Cp, AIC = AIC, SBC = SBC, PRD = PRESS))
 names(tbl)=c("SSE", "R.sq", "R.adj", "Cp", "AIC", "SBC", "PRESS")
 tbl
}
output.sum = rbind(select(full.model), select(sqrt.G3.log.fa), select(log.G3))
row.names(output.sum) = c("full.model", "sqrt.G3.log.fa", "log.G3")
kable(output.sum, caption = "Goodness-of-fit Measures of Candidate Models")
Goodness-of-fit Measures of Candidate Models
SSE R.sq R.adj Cp AIC SBC PRESS
full.model 1655.021182 0.7998743 0.7978217 5 575.9099 595.8043 1706.276101
sqrt.G3.log.fa 60.549357 0.7114041 0.7084442 5 -730.7936 -710.8992 62.617761
log.G3 8.116601 0.9199537 0.9191327 5 -1524.5648 -1504.6704 8.442339

The best model to use regarding this data set is the full model because it has the second highest R squared value. I was originally going to select the log transformed model as my final model because it has the highest R squared value. But after looking at the residual plot and other graphs I believe that this model has too many violations. The residual plot is not normal as the residuals take on a strange V pattern. The data is also neither normal nor linear. There are also two P values that are not significant in the log transformed model that are not present in the other two models. So until these errors can be correct, the full model will be used as the final model.

2.5 Final Model

kable(summary(full.model)$coef, caption = "Inferential Statistics of Final Model")
Inferential Statistics of Final Model
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3.5358762 0.6143636 -5.755348 0.0000000
absences 0.0387264 0.0130061 2.977546 0.0030870
famrel 0.2659179 0.1159748 2.292894 0.0223857
GA 1.1802399 0.0327789 36.006051 0.0000000
failures -0.2361464 0.1505524 -1.568533 0.1175677

The model can be written as the following:

G3 = -3.54 + 0.04(absences) + 0.27(famrel) + 1.18(GA) - 0.24(failures)

I was originally going to select the log transformed model as my final model because it has the highest R^value. But after looking at the residual plot and other graphs I believe that this model has too many violations. The residual plot is not normal as the residuals take on some strange patterns

3 Bootstraping the Final Model

3.1 Bootstraping Records

B = 1000  
num.p = dim(model.frame(full.model))[2] 
smpl.n = dim(model.frame(full.model))[1] 

coef.mtrx = matrix(rep(0, B*num.p), ncol = num.p)       
for (i in 1:B){
  bootc.id = sample(1:smpl.n, smpl.n, replace = TRUE)
  full.model.btc = lm(G3 ~ absences + famrel + failures + GA, data = Students.num[bootc.id,])     
  coef.mtrx[i,] = coef(full.model.btc)      
}
cmtrx <- summary(full.model)$coef
num.p = dim(coef.mtrx)[2]  
btc.ci = NULL
btc.wd = NULL
for (i in 1:num.p){
  lci.025 = round(quantile(coef.mtrx[, i], 0.025, type = 2),4)
  uci.975 = round(quantile(coef.mtrx[, i],0.975, type = 2 ),4)
  btc.wd[i] =  uci.975 - lci.025
  btc.ci[i] = paste("[", round(lci.025,4),", ", round(uci.975,4),"]")

}
  
kable(as.data.frame(cbind(formatC(cmtrx,4,format="f"), btc.ci.95=btc.ci)), 
      caption = "Regression Coefficient Matrix")
Regression Coefficient Matrix
Estimate Std. Error t value Pr(>|t|) btc.ci.95
(Intercept) -3.5359 0.6144 -5.7553 0.0000 [ -4.8839 , -2.2345 ]
absences 0.0387 0.0130 2.9775 0.0031 [ 0.0164 , 0.077 ]
famrel 0.2659 0.1160 2.2929 0.0224 [ 0.0237 , 0.5124 ]
GA 1.1802 0.0328 36.0061 0.0000 [ -0.6443 , 0.1001 ]
failures -0.2361 0.1506 -1.5685 0.1176 [ 1.1213 , 1.2374 ]

The bootstrap confidence intervals look mostly good but the one for “GA” contains 0 which might indicate the model is not a good predictor for the final grade. To help visualize what the issue is, I am going to view histograms.

boot.hist = function(cmtrx, bt.coef.mtrx, var.id, var.nm){


  x1.1 <- seq(min(bt.coef.mtrx[,var.id]), max(bt.coef.mtrx[,var.id]), length=300 )
  y1.1 <- dnorm(x1.1, mean(bt.coef.mtrx[,var.id]), sd(bt.coef.mtrx[,var.id]))

  highestbar = max(hist(bt.coef.mtrx[,var.id], plot = FALSE)$density) 
  ylimit <- max(c(y1.1,highestbar))
  hist(bt.coef.mtrx[,var.id], probability = TRUE, main = var.nm, xlab="", 
       col = "azure1",ylim=c(0,ylimit), border="lightseagreen")
  lines(x = x1.1, y = y1.1, col = "red3")
  lines(density(bt.coef.mtrx[,var.id], adjust=2), col="blue") 

}
par(mfrow=c(2,3))
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=1, var.nm ="GA" )

Nothing seems off about the blue density curve histogram which represents the bootstrap confidence intervals. I trided increasing the number of trials but still got the same results.

B = 2000  
num.p = dim(model.frame(full.model))[2] 
smpl.n = dim(model.frame(full.model))[1] 

coef.mtrx = matrix(rep(0, B*num.p), ncol = num.p)       
for (i in 1:B){
  bootc.id = sample(1:smpl.n, smpl.n, replace = TRUE)
  full.model.btc = lm(G3 ~ absences + famrel + failures + GA, data = Students.num[bootc.id,])     
  coef.mtrx[i,] = coef(full.model.btc)      
}
cmtrx <- summary(full.model)$coef
num.p = dim(coef.mtrx)[2]  
btc.ci = NULL
btc.wd = NULL
for (i in 1:num.p){
  lci.025 = round(quantile(coef.mtrx[, i], 0.025, type = 2),4)
  uci.975 = round(quantile(coef.mtrx[, i],0.975, type = 2 ),4)
  btc.wd[i] =  uci.975 - lci.025
  btc.ci[i] = paste("[", round(lci.025,4),", ", round(uci.975,4),"]")

}
  
kable(as.data.frame(cbind(formatC(cmtrx,4,format="f"), btc.ci.95=btc.ci)), 
      caption = "Regression Coefficient Matrix")
Regression Coefficient Matrix
Estimate Std. Error t value Pr(>|t|) btc.ci.95
(Intercept) -3.5359 0.6144 -5.7553 0.0000 [ -5.0028 , -2.2309 ]
absences 0.0387 0.0130 2.9775 0.0031 [ 0.0174 , 0.0776 ]
famrel 0.2659 0.1160 2.2929 0.0224 [ 0.0416 , 0.5151 ]
GA 1.1802 0.0328 36.0061 0.0000 [ -0.6565 , 0.124 ]
failures -0.2361 0.1506 -1.5685 0.1176 [ 1.1199 , 1.2401 ]

3.2 Bootstrap Confidence Intervals for Residuals

model.resid = full.model$residuals

B=1000
num.p = dim(model.matrix(full.model))[2]   
samp.n = dim(model.matrix(full.model))[1]  
btr.mtrx = matrix(rep(0,6*B), ncol=num.p) 
for (i in 1:B){

  bt.lg.fm = full.model$fitted.values + 
        sample(full.model$residuals, samp.n, replace = TRUE)  

  Students.num$bt.lg.fm =  bt.lg.fm  
  btr.model = lm(bt.lg.fm ~ absences + famrel + GA +  
                 failures, data = Students.num) 
  btr.mtrx[i,]=btr.model$coefficients
}



num.p = dim(coef.mtrx)[2] 
btr.ci = NULL
btr.wd = NULL
for (i in 1:num.p){
  lci.025 = round(quantile(btr.mtrx[, i], 0.025, type = 2),4)
  uci.975 = round(quantile(btr.mtrx[, i],0.975, type = 2 ),4)
  btr.wd[i] = uci.975 - lci.025
  btr.ci[i] = paste("[", round(lci.025,4),", ", round(uci.975,4),"]")
}

kable(as.data.frame(cbind(formatC(cmtrx,4,format="f"), btr.ci.95=btr.ci)), 
      caption = "Regression Coefficient Matrix with 95% Residual Bootstrap CI")
Regression Coefficient Matrix with 95% Residual Bootstrap CI
Estimate Std. Error t value Pr(>|t|) btr.ci.95
(Intercept) -3.5359 0.6144 -5.7553 0.0000 [ -4.6974 , 0 ]
absences 0.0387 0.0130 2.9775 0.0031 [ 0 , 0.0634 ]
famrel 0.2659 0.1160 2.2929 0.0224 [ 0 , 0.477 ]
GA 1.1802 0.0328 36.0061 0.0000 [ 0 , 1.246 ]
failures -0.2361 0.1506 -1.5685 0.1176 [ -0.5298 , 0.0443 ]

Even though using the residual bootstrap confidence intervals is not always recommended, I just wanted to compare it to the previous one. We see from the chart above that the bootstrap confidence intervals for the residuals contain 0. this can indicate that there is not a lot of bias in the model

3.3 Combining Results

kable(as.data.frame(cbind(formatC(cmtrx[,-3],4,format="f"), btc.ci.95=btc.ci,btr.ci.95=btr.ci)), 
      caption="Final Combined Inferential Statistics: p-values and Bootstrap CIs")
Final Combined Inferential Statistics: p-values and Bootstrap CIs
Estimate Std. Error Pr(>|t|) btc.ci.95 btr.ci.95
(Intercept) -3.5359 0.6144 0.0000 [ -5.0028 , -2.2309 ] [ -4.6974 , 0 ]
absences 0.0387 0.0130 0.0031 [ 0.0174 , 0.0776 ] [ 0 , 0.0634 ]
famrel 0.2659 0.1160 0.0224 [ 0.0416 , 0.5151 ] [ 0 , 0.477 ]
GA 1.1802 0.0328 0.0000 [ -0.6565 , 0.124 ] [ 0 , 1.246 ]
failures -0.2361 0.1506 0.1176 [ 1.1199 , 1.2401 ] [ -0.5298 , 0.0443 ]
kable(cmtrx, caption = "Inferential Statistics of Final Model")
Inferential Statistics of Final Model
Estimate Std. Error t value Pr(>|t|)
(Intercept) -3.5358762 0.6143636 -5.755348 0.0000000
absences 0.0387264 0.0130061 2.977546 0.0030870
famrel 0.2659179 0.1159748 2.292894 0.0223857
GA 1.1802399 0.0327789 36.006051 0.0000000
failures -0.2361464 0.1505524 -1.568533 0.1175677

The bootstrap and the parametric models all gave similar results, but I am choosing to use the bootstrap method to report the final model of my data because my data is not normally distributed and it contains enough observations where the bootstrap method can be applied. The final model can be described as:

G3 = -3.536 + 0.039(absences) + 0.266(famrel) + 1.18(GA) - 0.236(failures)

From this model, we can see that first and second period grades, the number of absences, and the family relationship positively impact the final grade a student receives in math. The number of class failures typically cause the final grade in math to decrease. We can see from the model that when the amount of failures increases by one, the final math grade decreases by 0.236 points. When the average of the student’s first and second period grades increases by a point, the students final grade increases by 1.18.

kable(as.data.frame(cbind(formatC(cmtrx,4,format="f"), btc.ci.95=btc.ci)), 
      caption = "Inferential Statistics of Final Model")
Inferential Statistics of Final Model
Estimate Std. Error t value Pr(>|t|) btc.ci.95
(Intercept) -3.5359 0.6144 -5.7553 0.0000 [ -5.0028 , -2.2309 ]
absences 0.0387 0.0130 2.9775 0.0031 [ 0.0174 , 0.0776 ]
famrel 0.2659 0.1160 2.2929 0.0224 [ 0.0416 , 0.5151 ]
GA 1.1802 0.0328 36.0061 0.0000 [ -0.6565 , 0.124 ]
failures -0.2361 0.1506 -1.5685 0.1176 [ 1.1199 , 1.2401 ]

4 Conclusion and Discussion

To conclude, I only took into account several explanatory variables that had significant p values. I created a total of three different models: the full model, the log transformed model, and the square root transformed model. Each model contains the same number of variables. Even though it ended up being the best model, the full model had several violations. The variance of the model was not constant, and the box cox transformation was done in order to try to correct this. The data was also not normal, this violation remains uncorrected. Also, the variable “failures” is not as statistically significant like the other ones. the full model had and R squared value of about 0.79. Even though the log transformation model had the higher R squared value, I selected this model to use for the second part of the assignment.

For the second part of the assignment I did 95% bootstrap confidence intervals for the coefficients and the residuals. I concluded that a bootstrap model of the coefficients would be the best way to evaluate the full model, despite the fact that the bootstrap confidence interval contains a 0 for the variable “GA”. Recommendations for pursuing this project further include figuring out how the data for the log transformed model could be used as the final model since it has a higher R squared value. I would also either use different variables or transform the existing variables so the bootstrap confidence interval for GA did not contain 0. i would also use some of the variables from another set on data.world.com that was by the same author with data from the same schools and surveys about the same student’s final English grades. A students final grade in English is likely a good predictor of their final math grade.

LS0tDQp0aXRsZTogJ0ZhY3RvcnMgdGhhdCBJbmZsdWVuY2UgRmluYWwgTWF0aCBHcmFkZXMnDQphdXRob3I6ICdBdmEgRGVTdGVmYW5vJw0KZGF0ZTogIjEwLTI3LTI0Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX3dpZHRoOiA2DQogICAgZmlnX2hlaWdodDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGZpZ193aWR0aDogNQ0KICAgIGZpZ19oZWlnaHQ6IDQNCi0tLQ0KDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDI0cHg7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1zaXplOiAyMHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgRGV0ZWN0LCBpbnN0YWxsIGFuZCBsb2FkIHBhY2thZ2VzIGlmIG5lZWRlZC4NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgiTUFTUyIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCiAgIGxpYnJhcnkoTUFTUykNCn0NCmlmICghcmVxdWlyZSgibmxlcXNsdiIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJubGVxc2x2IikNCiAgIGxpYnJhcnkobmxlcXNsdikNCn0NCiMNCmlmICghcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpDQogICBsaWJyYXJ5KHBhbmRlcikNCn0NCg0KaWYgKCFyZXF1aXJlKCJwc3ljaCIpKSB7ICAgDQogIGluc3RhbGwucGFja2FnZXMoInBzeWNoIikNCiAgIGxpYnJhcnkocHN5Y2gpDQp9DQppZiAoIXJlcXVpcmUoIk1BU1MiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCiAgIGxpYnJhcnkoTUFTUykNCn0NCmlmICghcmVxdWlyZSgiY2FyIikpIHsgICANCiAgaW5zdGFsbC5wYWNrYWdlcygiY2FyIikNCiAgIGxpYnJhcnkoY2FyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgeyAgIA0KICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQojDQojIHNwZWNpZmljYXRpb25zIG9mIG91dHB1dHMgb2YgY29kZSBpbiBjb2RlIGNodW5rcw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmdzID0gRkFMU0UsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZXMgPSBGQUxTRSwgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFICAgICANCiAgICAgICAgICAgICAgICAgICAgICApICAgDQpgYGANCg0KDQojIEludHJvZHVjdGlvbg0KDQpUaGlzIGRhdGEgc2V0IHdhcyBvYnRhaW5lZCBmcm9tICBbRGF0YSBXb3JsZF0gaHR0cHM6Ly9kYXRhLndvcmxkLmNvbS4gVGhlIGRhdGEgY29uc2lzdHMgb2YgaW5mb3JtYXRpb24gb24gc3R1ZGVudHMgZ2F0aGVyZWQgZnJvbSB0d28gZGlmZmVyZW50IHNjaG9vbHMgaW4gUG9ydHVnYWwgYWJvdXQgc3R1ZGVudHMgaGFiaXRzIGFuZCBsaXZlcyBvdXRzaWRlIG9mIHNjaG9vbCB0byBzZWUgd2hhdCBpbXBhY3QgdGhlc2UgZXh0ZXJuYWwgZmFjdG9ycyBtaWdodCBoYXZlIG9uIHRoZWlyIGZpbmFsIGdyYWRlIGluIG1hdGhlbWF0aWNzLiBUaGUgZGF0YSB3YXMgY29sbGVjdGVkIHRocm91Z2ggc2Nob29sIHN1cnZleXMgYW5kIHF1ZXN0aW9ubmFpcmVzLiANCg0KDQojIyBWYXJpYWJsZSBEZXNjcmlwdGlvbg0KDQoqIHNjaG9vbCh4MSkgLSBzdHVkZW50J3Mgc2Nob29sIChiaW5hcnk6ICdHUCcgLSBHYWJyaWVsIFBlcmVpcmEgb3IgJ01TJyAtIE1vdXNpbmhvIGRhIFNpbHZlaXJhKQ0KKiBzZXgoeDIpIC0gc3R1ZGVudCdzIHNleCAoYmluYXJ5OiAnRicgLSBmZW1hbGUgb3IgJ00nIC0gbWFsZSkNCiogYWdlKHgzKSAtIHN0dWRlbnQncyBhZ2UgDQoqIGFkZHJlc3MoeDQpIC0gc3R1ZGVudCdzIGhvbWUgYWRkcmVzcyB0eXBlIChiaW5hcnk6ICdVJyAtIHVyYmFuIG9yICdSJyAtIHJ1cmFsKQ0KKiBmYW1zaXplKHg1KSAtIGZhbWlseSBzaXplDQoqIFBzdGF0dXMoeDYpIC0gcGFyZW50J3MgY29oYWJpdGF0aW9uIHN0YXR1cyAoYmluYXJ5OiAnVCcgLSBsaXZpbmcgdG9nZXRoZXIgb3IgJ0EnIC0gYXBhcnQpDQoqIE1lZHUoeDcpIC0gbW90aGVyJ3MgZWR1Y2F0aW9uIChudW1lcmljOiAwIC0gbm9uZSwgMSAtIHByaW1hcnkgZWR1Y2F0aW9uICg0dGggZ3JhZGUpLCAyIMOi4oKs4oCcIDV0aCB0byA5dGggZ3JhZGUsIDMgw6LigqzigJwgc2Vjb25kYXJ5IGVkdWNhdGlvbiBvciA0IMOi4oKs4oCcIGhpZ2hlciBlZHVjYXRpb24pDQoqIEZlZHUoeDgpIC0gZmF0aGVyJ3MgZWR1Y2F0aW9uIA0KKiBNam9iKHg5KSAtIG1vdGhlcidzIGpvYiAobm9taW5hbDogJ3RlYWNoZXInLCAnaGVhbHRoJyBjYXJlIHJlbGF0ZWQsIGNpdmlsICdzZXJ2aWNlcycgKGUuZy4gYWRtaW5pc3RyYXRpdmUgb3IgcG9saWNlKSwgJ2F0X2hvbWUnIG9yICdvdGhlcicpDQoqIEZqb2IoeDEwKSAtIGZhdGhlcidzIGpvYg0KKiByZWFzb24gLSByZWFzb24gdG8gY2hvb3NlIHRoaXMgc2Nob29sIChub21pbmFsOiBjbG9zZSB0byAnaG9tZScsIHNjaG9vbCAncmVwdXRhdGlvbicsICdjb3Vyc2UnIHByZWZlcmVuY2Ugb3IgJ290aGVyJykNCiogZ3VhcmRpYW4oeDExKSAtIHN0dWRlbnQncyBndWFyZGlhbiAobm9taW5hbDogJ21vdGhlcicsICdmYXRoZXInIG9yICdvdGhlcicpDQoqIHRyYXZlbHRpbWUoeDEyKSAtIGhvbWUgdG8gc2Nob29sIHRyYXZlbCB0aW1lIChudW1lcmljOiAxIC0gPDE1IG1pbi4sIDIgLSAxNSB0byAzMCBtaW4uLCAzIC0gMzAgbWluLiB0byAxIGhvdXIsIG9yIDQgLSA+MSBob3VyKQ0KKiBzdHVkeXRpbWUoeDEzKSAtIHdlZWtseSBzdHVkeSB0aW1lIChudW1lcmljOiAxIC0gPDIgaG91cnMsIDIgLSAyIHRvIDUgaG91cnMsIDMgLSA1IHRvIDEwIGhvdXJzLCBvciA0IC0gPjEwIGhvdXJzKQ0KKiBmYWlsdXJlcyh4MTQpIC0gbnVtYmVyIG9mIHBhc3QgY2xhc3MgZmFpbHVyZXMgKG51bWVyaWM6IG4gaWYgMTw9bjwzLCBlbHNlIDQpDQoqIHNjaG9vbHN1cCh4MTUpIC0gZXh0cmEgZWR1Y2F0aW9uYWwgc3VwcG9ydA0KKiBmYW1zdXAoeDE2KSAtIGZhbWlseSBlZHVjYXRpb25hbCBzdXBwb3J0IA0KKiBwYWlkIC0gZXh0cmEgcGFpZCBjbGFzc2VzIHdpdGhpbiB0aGUgY291cnNlIHN1YmplY3QgDQoqIGFjdGl2aXRpZXMoeDE3KSAtIGV4dHJhLWN1cnJpY3VsYXIgYWN0aXZpdGllcyANCiogbnVyc2VyeSh4MTgpIC0gYXR0ZW5kZWQgbnVyc2VyeSBzY2hvb2wgDQoqIGhpZ2hlcih4MTkpIC0gd2FudHMgdG8gdGFrZSBoaWdoZXIgZWR1Y2F0aW9uIA0KKiBpbnRlcm5ldCh4MjApIC0gSW50ZXJuZXQgYWNjZXNzIGF0IGhvbWUgDQoqIHJvbWFudGljKHgyMSkgLSB3aXRoIGEgcm9tYW50aWMgcmVsYXRpb25zaGlwIA0KKiBmYW1yZWwoeDIyKSAtIHF1YWxpdHkgb2YgZmFtaWx5IHJlbGF0aW9uc2hpcHMgKG51bWVyaWM6IGZyb20gMSAtIHZlcnkgYmFkIHRvIDUgLSBleGNlbGxlbnQpDQoqIGZyZWV0aW1lKHgyMykgLSBmcmVlIHRpbWUgYWZ0ZXIgc2Nob29sIChudW1lcmljOiBmcm9tIDEgLSB2ZXJ5IGxvdyB0byA1IC0gdmVyeSBoaWdoKQ0KKiBnb291dCh4MjQpIC0gZ29pbmcgb3V0IHdpdGggZnJpZW5kcyAobnVtZXJpYzogZnJvbSAxIC0gdmVyeSBsb3cgdG8gNSAtIHZlcnkgaGlnaCkNCiogRGFsYyh4MjUpIC0gd29ya2RheSBhbGNvaG9sIGNvbnN1bXB0aW9uIChudW1lcmljOiBmcm9tIDEgLSB2ZXJ5IGxvdyB0byA1IC0gdmVyeSBoaWdoKQ0KKiBXYWxjKHgyNikgLSB3ZWVrZW5kIGFsY29ob2wgY29uc3VtcHRpb24gKG51bWVyaWM6IGZyb20gMSAtIHZlcnkgbG93IHRvIDUgLSB2ZXJ5IGhpZ2gpDQoqIGhlYWx0aCh4MjcpIC0gY3VycmVudCBoZWFsdGggc3RhdHVzIChudW1lcmljOiBmcm9tIDEgLSB2ZXJ5IGJhZCB0byA1IC0gdmVyeSBnb29kKQ0KKiBhYnNlbmNlcyh4MjgpIC0gbnVtYmVyIG9mIHNjaG9vbCBhYnNlbmNlcyANCiogRzEgLSh4MjkpIGZpcnN0IHBlcmlvZCBncmFkZSAobnVtZXJpYzogZnJvbSAwIHRvIDIwKQ0KKiBHMiAtKHgzMCkgc2Vjb25kIHBlcmlvZCBncmFkZSAobnVtZXJpYzogZnJvbSAwIHRvIDIwKQ0KKiBHMyh5KSAtIGZpbmFsIGdyYWRlIChudW1lcmljOiBmcm9tIDAgdG8gMjAsIG91dHB1dCB0YXJnZXQpDQoNCiMjIFByYWN0aWNhbCBRdWVzdGlvbnMNCg0KVGhlIHF1ZXN0aW9uIHRyeWluZyB0byBiZSBhbnN3ZXJlZCB0aHJvdWdob3V0IHRoaXMgZGF0YSBhbmFseXNpcyBpcyB3aGF0IHRoZSBhc3NvY2lhdGlvbiBpcyBiZXR3ZWVuIHN0dWRlbnRzIHN0dWR5IGhhYml0cywgcHJldmlvdXMgZ3JhZGVzIGFuZCBsaXZlcyBvdXRzaWRlIG9mIHNjaG9vbCwgYW5kIHRoZWlyIGZpbmFsIG1hdGggZ3JhZGUuIEFub3RoZXIgcXVlc3Rpb24gSSBoYXZlIGlzIGhvdyBnb29kIGFyZSBlYWNoIG9mIHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMgYXQgcHJlZGljdGluZyBhIHN0dWRlbnQncyBmaW5hbCBncmFkZSBpbiBtYXRoLiBJcyB0aGVyZSBvbmUgdGhhdCBpcyB0aGUgYmVzdD8gQWxzbywgd2hpY2ggbW9kZWwgd2lsbCBiZSB0aGUgYmVzdCBpbiBwcmVkaWN0aW5nIHdoaWNoIHN0dWRlbnRzIHdpbGwgaGF2ZSB0aGUgYmVzdCBtYXRoIGdyYWRlPw0KIA0KIA0KIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzDQoNCkZpcnN0LCB0aGUgZGF0YSBpcyB1cGxvYWRlZC4NCmBgYHtyfQ0Kc3R1ZGVudHMwIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQXZhRGVTdC9TVEEtMzIxL3JlZnMvaGVhZHMvbWFpbi9zdHVkZW50LW1hdC5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0Kc3R1ZGVudHM9cmVhZC50YWJsZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0F2YURlU3QvU1RBLTMyMS9yZWZzL2hlYWRzL21haW4vc3R1ZGVudC1tYXQuY3N2IixzZXA9IjsiLGhlYWRlcj1UUlVFKQ0KDQptb2RlbCA9IGxtKEczIH4gTWVkdSArIEZlZHUgKyB0cmF2ZWx0aW1lICsgIGZhaWx1cmVzICsgZnJlZXRpbWUgKyBnb291dCArIFdhbGMgKyBEYWxjICsgZmFtcmVsICsgYWJzZW5jZXMgKyBoZWFsdGggKyBzdHVkeXRpbWUgKyBHMSArIEcyICwgZGF0YSA9IHN0dWRlbnRzKQ0Ka2FibGUoc3VtbWFyeShtb2RlbCkkY29lZiwgY2FwdGlvbiA9IlN0YXRpc3RpY3Mgb2YgUmVncmVzc2lvbiBDb2VmZmljaWVudHMiKQ0KDQpTdHVkZW50cyA8LSBzdHVkZW50cyAlPiUNCiAgbXV0YXRlKEdBID0gKEcxICsgRzIpIC8gMikNCg0KYGBgDQoNCg0KYGBge3J9DQpTdHVkZW50cy5udW0gPC0gc2VsZWN0KFN0dWRlbnRzLCJhYnNlbmNlcyIsICJmYW1yZWwiLCAiZmFpbHVyZXMiLCAiR0EiLCAiRzMiKQ0KYGBgDQoNCkFzIHBhcnQgb2YgbXkgZGF0YSBwcmVwYXJhdGlvbiwgSSBsb29rZWQgb3ZlciBhbGwgdGhlIG51bWVyaWNhbCB2YXJpYWJsZXMgaW4gbXkgZGF0YSBzZXQuIEkgb3B0ZWQgdG8ga2VlcCB0aGUgdmFyaWFibGVzIHRoYXQgaGFkIHNpZ25pZmljYW50IHAtdmFsdWVzLiBUaGlzIGluY2x1ZGVkIHRoZSBudW1iZXIgb2YgY2xhc3NlcyBmYWlsZWQgcHJldmlvdXNseSwgdGhlIHN0dWRlbnQncyBmYW1pbHkgcmVsYXRpb25zaGlwIHF1YWxpdHksIHRoZSBudW1iZXIgb2YgYWJzZW5jZXMgYSBzdHVkZW50IGhhcyBoYWQsIGFuZCB0aGUgZmlyc3QgYW5kIHNlY29uZCBwZXJpb2QgZ3JhZGVzIG9mIHRoZSBzdHVkZW50LiBUbyBtYWtlIHRoZSBtb2RlbCBtb3JlIHNpbXBsZSwgSSBjb21iaW5lZCB0aGUgZmlyc3QgYW5kIHNlY29uZCBwZXJpb2QgZ3JhZGVzIGludG8gYSBuZXcgdmFyaWFibGUgdGhhdCB0YWtlcyB0aGUgYXZlcmFnZSBvZiB0aGUgdHdvLiBJIHRyaWVkIGNvbWJpbmcgc2V2ZXJhbCBvdGhlciB2YXJpYWJsZXMgdG9nZXRoZXIgdG8gY3JlYXRlIG5ldyBvbmVzIGJ1dCBub25lIG9mIHRoZW0gaGVsZCBhbnkgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlLg0KDQoNCiMjIEZ1bGwgTW9kZWwNCg0KYGBge3J9DQpmdWxsLm1vZGVsID0gbG0oRzMgfiBhYnNlbmNlcyArICBmYW1yZWwgKyBHQSArIGZhaWx1cmVzLCBkYXRhID0gU3R1ZGVudHMubnVtKQ0Ka2FibGUoc3VtbWFyeShmdWxsLm1vZGVsKSRjb2VmLCBjYXB0aW9uID0iU3RhdGlzdGljcyBvZiBSZWdyZXNzaW9uIENvZWZmaWNpZW50cyIpDQoNCnBhcihtZnJvdz1jKDIsMikpDQpwbG90KGZ1bGwubW9kZWwpDQpgYGANClRoZSBRUSBwbG90IGluZGljYXRlcyB0aGF0IHRoZSBkYXRhIGlzIG5vdCBxdWl0ZSBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFRoZSB2YXJpYW5jZSBvZiB0aGUgZGF0YSBpcyBhbHNvIG5vdCBjb25zdGFudCBhbmQgdGhlIGRhdGEgaXMgY2x1bXBlZCBtb3JlIHRvZ2V0aGVyIGF0IHRoZSByaWdodCBzaWRlIG9mIHRoZSBncmFwaC4gVGhlcmUgYWxzbyBhcHBlYXJzIHRvIGJlIG9uZSBvdXRsaWVyIHRvIHRoZSBmYXIgcmlnaHQgaW4gdGhlIGxvd2VyIHJpZ2h0IGdyYXBoLiANCg0KYGBge3J9DQp2aWYoZnVsbC5tb2RlbCkNCmJhcnBsb3QodmlmKGZ1bGwubW9kZWwpLCBtYWluID0gIlZJRiBWYWx1ZXMiLCBob3JpeiA9IEZBTFNFLCBjb2wgPSAic3RlZWxibHVlIikNCmBgYA0KU2luY2UgYWxsIG9mIHRoZSBWSUYgdmFsdWVzIGFyZSBjbG9zZSB0byAxIGFuZCBkbyBub3QgZXhjZWVkIDQsIG11bHRpY29sbGluZWFyaXR5IGlzIG5vdCBhbiBpc3N1ZS4NCg0KDQojIyBUcmFuc2Zvcm1hdGlvbnMgDQoNClRvIGhlbHAgY29ycmVjdCB0aGUgbm9uIGNvbnN0YW50IHZhcmlhbmNlIG9mIHRoZSBkYXRhLCBJIGFtIGdvaW5nIHRvIHBlcmZvcm0gYSBib3hjb3ggdHJhbnNmb3JtYXRpb24uDQoNCmBgYHtyfQ0KcGFyKHB0eSA9ICJzIiwgbWZyb3cgPSBjKDIsIDIpLCBvbWE9YyguMSwuMSwuMSwuMSksIG1hcj1jKDQsIDAsIDIsIDApKQ0KU3R1ZGVudHMubnVtJEczX2FkanVzdGVkIDwtIFN0dWRlbnRzLm51bSRHMyArIDMNCg0KYm94Y294KEczX2FkanVzdGVkIH4gYWJzZW5jZXMgKyBmYW1yZWwgKyBHQSArIGxvZyhmYWlsdXJlcyArMSkgIA0KICAgICAgICwgZGF0YSA9IFN0dWRlbnRzLm51bSwgbGFtYmRhID0gc2VxKDAsIDEsIGxlbmd0aCA9IDEwKSwgDQogICAgICAgeGxhYj1leHByZXNzaW9uKHBhc3RlKGxhbWJkYSwgIjogbG9nLWZhaWx1cmVzIikpKQ0KDQpib3hjb3goRzNfYWRqdXN0ZWQgfiBhYnNlbmNlcysgZmFtcmVsICsgR0EgKyBmYWlsdXJlcyAgDQogICAgICAgLCBkYXRhID0gU3R1ZGVudHMubnVtLCBsYW1iZGEgPSBzZXEoMCwgMSwgbGVuZ3RoID0gMTApLCANCiAgICAgICB4bGFiPWV4cHJlc3Npb24ocGFzdGUobGFtYmRhLCAiOiBmYWlsdXJlcyIpKSkNCg0KYm94Y294KEczX2FkanVzdGVkIH4gYWJzZW5jZXMgKyBsb2coZmFtcmVsKSArIEdBICsgZmFpbHVyZXMNCiAgICAgICAsIGRhdGEgPSBTdHVkZW50cy5udW0sIGxhbWJkYSA9IHNlcSgwLCAxLCBsZW5ndGggPSAxMCksIA0KICAgICAgIHhsYWI9ZXhwcmVzc2lvbihwYXN0ZShsYW1iZGEsICI6IGxvZy1mYW1yZWwiKSkpDQoNCmJveGNveChHM19hZGp1c3RlZCB+IGFic2VuY2VzKyBmYW1yZWwgKyBHQSArIGZhaWx1cmVzICANCiAgICAgICAsIGRhdGEgPSBTdHVkZW50cy5udW0sIGxhbWJkYSA9IHNlcSgwLCAxLCBsZW5ndGggPSAxMCksIA0KICAgICAgIHhsYWI9ZXhwcmVzc2lvbihwYXN0ZShsYW1iZGEsICI6IGZhbXJlbCIpKSkNCg0KYGBgDQpJbiBvcmRlciB0byBkbyB0aGUgYm94Y294IHRyYW5zZm9ybWF0aW9uLCBJIGhhZCB0byBjaGFuZ2UgbXkgcmVzcG9uc2UgdmFyaWFibGUgIkczIiB0byBiZSBwb3NpdGl2ZS4gQWxzbywgd2hlbiB0YWtpbmcgdGhlIGxvZyBvZiAiZmFpbHVyZXMiIGkgYWxzbyBoYWQgdG8gY2hhbmdlIHRoYXQgdG8gYmUgcG9zaXRpdmUgYnkgYWRkaW5nIDEuIA0KDQojIyBTcXVhcmUgUm9vdCBUcmFuc2Zvcm1hdGlvbg0KDQpgYGB7cn0NCnNxcnQuRzMubG9nLmZhID0gbG0oKEczX2FkanVzdGVkKV4wLjUgfiBhYnNlbmNlcyArIGxvZyhmYW1yZWwpICsgR0EgKyBmYWlsdXJlcywgZGF0YSA9IFN0dWRlbnRzLm51bSkNCmthYmxlKHN1bW1hcnkoc3FydC5HMy5sb2cuZmEpJGNvZWYsIGNhcHRpb24gPSAibG9nLXRyYW5zZm9ybWVkIG1vZGVsIikNCg0KcGFyKG1mcm93ID0gYygyLDIpKQ0KcGxvdChzcXJ0LkczLmxvZy5mYSkNCmBgYA0KU2ltaWxhciB0byB0aGUgYm94Y294IG1vZGVsLCBJIGhhZCB0byBhZGp1c3QgRzMgdG8gYmUgcG9zaXRpdmUuIA0KDQojIyBMb2cgVHJhbnNmb3JtYXRpb24NCg0KYGBge3J9DQpsb2cuRzMgPSBsbShsb2coRzNfYWRqdXN0ZWQpIH4gYWJzZW5jZXMgKyBmYWlsdXJlcyArIGZhbXJlbCArIEczICwgZGF0YSA9IFN0dWRlbnRzLm51bSkNCmthYmxlKHN1bW1hcnkobG9nLkczKSRjb2VmLCBjYXB0aW9uID0gImxvZy10cmFuc2Zvcm1lZCBtb2RlbCIpDQoNCnBhcihtZnJvdyA9IGMoMiwyKSkNCnBsb3QobG9nLkczKQ0KYGBgDQojIyBHb29kbmVzcyBvZiBGaXQgbWVhc3VyZXMNCg0KYGBge3J9DQpzZWxlY3Q9ZnVuY3Rpb24obSl7IA0KIGUgPSBtJHJlc2lkICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogbjAgPSBsZW5ndGgoZSkgICAgICAgICAgICAgICAgICAgICAgICANCiBTU0U9KG0kZGYpKihzdW1tYXJ5KG0pJHNpZ21hKV4yICAgICAgIA0KIFIuc3E9c3VtbWFyeShtKSRyLnNxdWFyZWQgICAgICAgICAgICAgDQogUi5hZGo9c3VtbWFyeShtKSRhZGouciAgICAgICAgICAgICAgICANCiBNU0U9KHN1bW1hcnkobSkkc2lnbWEpXjIgICAgICAgICAgICAgIA0KIENwPShTU0UvTVNFKS0objAtMioobjAtbSRkZikpICAgICAgICAgDQogQUlDPW4wKmxvZyhTU0UpLW4wKmxvZyhuMCkrMioobjAtbSRkZikgICAgICAgICAgDQogU0JDPW4wKmxvZyhTU0UpLW4wKmxvZyhuMCkrKGxvZyhuMCkpKihuMC1tJGRmKSAgDQogWD1tb2RlbC5tYXRyaXgobSkgICAgICAgICAgICAgICAgICAgICANCiBIPVglKiVzb2x2ZSh0KFgpJSolWCklKiV0KFgpICAgICAgICAgIA0KIGQ9ZS8oMS1kaWFnKEgpKSAgICAgICAgICAgICAgICAgICAgICAgDQogUFJFU1M9dChkKSUqJWQgICANCiB0YmwgPSBhcy5kYXRhLmZyYW1lKGNiaW5kKFNTRT1TU0UsIFIuc3E9Ui5zcSwgUi5hZGogPSBSLmFkaiwgQ3AgPSBDcCwgQUlDID0gQUlDLCBTQkMgPSBTQkMsIFBSRCA9IFBSRVNTKSkNCiBuYW1lcyh0YmwpPWMoIlNTRSIsICJSLnNxIiwgIlIuYWRqIiwgIkNwIiwgIkFJQyIsICJTQkMiLCAiUFJFU1MiKQ0KIHRibA0KfQ0KYGBgDQoNCmBgYHtyfQ0Kb3V0cHV0LnN1bSA9IHJiaW5kKHNlbGVjdChmdWxsLm1vZGVsKSwgc2VsZWN0KHNxcnQuRzMubG9nLmZhKSwgc2VsZWN0KGxvZy5HMykpDQpyb3cubmFtZXMob3V0cHV0LnN1bSkgPSBjKCJmdWxsLm1vZGVsIiwgInNxcnQuRzMubG9nLmZhIiwgImxvZy5HMyIpDQprYWJsZShvdXRwdXQuc3VtLCBjYXB0aW9uID0gIkdvb2RuZXNzLW9mLWZpdCBNZWFzdXJlcyBvZiBDYW5kaWRhdGUgTW9kZWxzIikNCmBgYA0KVGhlIGJlc3QgbW9kZWwgdG8gdXNlIHJlZ2FyZGluZyB0aGlzIGRhdGEgc2V0IGlzIHRoZSBmdWxsIG1vZGVsICBiZWNhdXNlIGl0IGhhcyB0aGUgc2Vjb25kIGhpZ2hlc3QgUiBzcXVhcmVkIHZhbHVlLiBJIHdhcyBvcmlnaW5hbGx5IGdvaW5nIHRvIHNlbGVjdCB0aGUgbG9nIHRyYW5zZm9ybWVkIG1vZGVsIGFzIG15IGZpbmFsIG1vZGVsIGJlY2F1c2UgaXQgaGFzIHRoZSBoaWdoZXN0IFIgc3F1YXJlZCB2YWx1ZS4gQnV0IGFmdGVyIGxvb2tpbmcgYXQgdGhlIHJlc2lkdWFsIHBsb3QgYW5kIG90aGVyIGdyYXBocyBJIGJlbGlldmUgdGhhdCB0aGlzIG1vZGVsIGhhcyB0b28gbWFueSB2aW9sYXRpb25zLiBUaGUgcmVzaWR1YWwgcGxvdCBpcyBub3Qgbm9ybWFsIGFzIHRoZSByZXNpZHVhbHMgdGFrZSBvbiBhIHN0cmFuZ2UgViBwYXR0ZXJuLiBUaGUgZGF0YSBpcyBhbHNvIG5laXRoZXIgbm9ybWFsIG5vciBsaW5lYXIuIFRoZXJlIGFyZSBhbHNvIHR3byBQIHZhbHVlcyB0aGF0IGFyZSBub3Qgc2lnbmlmaWNhbnQgaW4gdGhlIGxvZyB0cmFuc2Zvcm1lZCBtb2RlbCB0aGF0IGFyZSBub3QgcHJlc2VudCBpbiB0aGUgb3RoZXIgdHdvIG1vZGVscy4gU28gdW50aWwgdGhlc2UgZXJyb3JzIGNhbiBiZSBjb3JyZWN0LCB0aGUgZnVsbCBtb2RlbCB3aWxsIGJlIHVzZWQgYXMgdGhlIGZpbmFsIG1vZGVsLiANCg0KIyMgRmluYWwgTW9kZWwNCg0KYGBge3J9DQprYWJsZShzdW1tYXJ5KGZ1bGwubW9kZWwpJGNvZWYsIGNhcHRpb24gPSAiSW5mZXJlbnRpYWwgU3RhdGlzdGljcyBvZiBGaW5hbCBNb2RlbCIpDQpgYGANCg0KDQpUaGUgbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXMgdGhlIGZvbGxvd2luZzogDQoNCkczID0gLTMuNTQgKyAwLjA0KGFic2VuY2VzKSArIDAuMjcoZmFtcmVsKSArIDEuMTgoR0EpIC0gMC4yNChmYWlsdXJlcykNCg0KSSB3YXMgb3JpZ2luYWxseSBnb2luZyB0byBzZWxlY3QgdGhlIGxvZyB0cmFuc2Zvcm1lZCBtb2RlbCBhcyBteSBmaW5hbCBtb2RlbCBiZWNhdXNlIGl0IGhhcyB0aGUgaGlnaGVzdCBSXnZhbHVlLiBCdXQgYWZ0ZXIgbG9va2luZyBhdCB0aGUgcmVzaWR1YWwgcGxvdCBhbmQgb3RoZXIgZ3JhcGhzIEkgYmVsaWV2ZSB0aGF0IHRoaXMgbW9kZWwgaGFzIHRvbyBtYW55IHZpb2xhdGlvbnMuIFRoZSByZXNpZHVhbCBwbG90IGlzIG5vdCBub3JtYWwgYXMgdGhlIHJlc2lkdWFscyB0YWtlIG9uIHNvbWUgc3RyYW5nZSBwYXR0ZXJucw0KDQojIEJvb3RzdHJhcGluZyB0aGUgRmluYWwgTW9kZWwNCg0KIyMgQm9vdHN0cmFwaW5nIFJlY29yZHMNCg0KYGBge3J9DQpCID0gMTAwMCAgDQpudW0ucCA9IGRpbShtb2RlbC5mcmFtZShmdWxsLm1vZGVsKSlbMl0gDQpzbXBsLm4gPSBkaW0obW9kZWwuZnJhbWUoZnVsbC5tb2RlbCkpWzFdIA0KDQpjb2VmLm10cnggPSBtYXRyaXgocmVwKDAsIEIqbnVtLnApLCBuY29sID0gbnVtLnApICAgICAgIA0KZm9yIChpIGluIDE6Qil7DQogIGJvb3RjLmlkID0gc2FtcGxlKDE6c21wbC5uLCBzbXBsLm4sIHJlcGxhY2UgPSBUUlVFKQ0KICBmdWxsLm1vZGVsLmJ0YyA9IGxtKEczIH4gYWJzZW5jZXMgKyBmYW1yZWwgKyBmYWlsdXJlcyArIEdBLCBkYXRhID0gU3R1ZGVudHMubnVtW2Jvb3RjLmlkLF0pICAgICANCiAgY29lZi5tdHJ4W2ksXSA9IGNvZWYoZnVsbC5tb2RlbC5idGMpICAgICAgDQp9DQpgYGANCg0KYGBge3J9DQpjbXRyeCA8LSBzdW1tYXJ5KGZ1bGwubW9kZWwpJGNvZWYNCm51bS5wID0gZGltKGNvZWYubXRyeClbMl0gIA0KYnRjLmNpID0gTlVMTA0KYnRjLndkID0gTlVMTA0KZm9yIChpIGluIDE6bnVtLnApew0KICBsY2kuMDI1ID0gcm91bmQocXVhbnRpbGUoY29lZi5tdHJ4WywgaV0sIDAuMDI1LCB0eXBlID0gMiksNCkNCiAgdWNpLjk3NSA9IHJvdW5kKHF1YW50aWxlKGNvZWYubXRyeFssIGldLDAuOTc1LCB0eXBlID0gMiApLDQpDQogIGJ0Yy53ZFtpXSA9ICB1Y2kuOTc1IC0gbGNpLjAyNQ0KICBidGMuY2lbaV0gPSBwYXN0ZSgiWyIsIHJvdW5kKGxjaS4wMjUsNCksIiwgIiwgcm91bmQodWNpLjk3NSw0KSwiXSIpDQoNCn0NCiAgDQprYWJsZShhcy5kYXRhLmZyYW1lKGNiaW5kKGZvcm1hdEMoY210cngsNCxmb3JtYXQ9ImYiKSwgYnRjLmNpLjk1PWJ0Yy5jaSkpLCANCiAgICAgIGNhcHRpb24gPSAiUmVncmVzc2lvbiBDb2VmZmljaWVudCBNYXRyaXgiKQ0KDQpgYGANClRoZSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMgbG9vayBtb3N0bHkgZ29vZCBidXQgdGhlIG9uZSBmb3IgIkdBIiBjb250YWlucyAwIHdoaWNoIG1pZ2h0IGluZGljYXRlIHRoZSBtb2RlbCBpcyBub3QgYSBnb29kIHByZWRpY3RvciBmb3IgdGhlIGZpbmFsIGdyYWRlLiBUbyBoZWxwIHZpc3VhbGl6ZSB3aGF0IHRoZSBpc3N1ZSBpcywgSSBhbSBnb2luZyB0byB2aWV3IGhpc3RvZ3JhbXMuDQoNCmBgYHtyfQ0KYm9vdC5oaXN0ID0gZnVuY3Rpb24oY210cngsIGJ0LmNvZWYubXRyeCwgdmFyLmlkLCB2YXIubm0pew0KDQoNCiAgeDEuMSA8LSBzZXEobWluKGJ0LmNvZWYubXRyeFssdmFyLmlkXSksIG1heChidC5jb2VmLm10cnhbLHZhci5pZF0pLCBsZW5ndGg9MzAwICkNCiAgeTEuMSA8LSBkbm9ybSh4MS4xLCBtZWFuKGJ0LmNvZWYubXRyeFssdmFyLmlkXSksIHNkKGJ0LmNvZWYubXRyeFssdmFyLmlkXSkpDQoNCiAgaGlnaGVzdGJhciA9IG1heChoaXN0KGJ0LmNvZWYubXRyeFssdmFyLmlkXSwgcGxvdCA9IEZBTFNFKSRkZW5zaXR5KSANCiAgeWxpbWl0IDwtIG1heChjKHkxLjEsaGlnaGVzdGJhcikpDQogIGhpc3QoYnQuY29lZi5tdHJ4Wyx2YXIuaWRdLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSB2YXIubm0sIHhsYWI9IiIsIA0KICAgICAgIGNvbCA9ICJhenVyZTEiLHlsaW09YygwLHlsaW1pdCksIGJvcmRlcj0ibGlnaHRzZWFncmVlbiIpDQogIGxpbmVzKHggPSB4MS4xLCB5ID0geTEuMSwgY29sID0gInJlZDMiKQ0KICBsaW5lcyhkZW5zaXR5KGJ0LmNvZWYubXRyeFssdmFyLmlkXSwgYWRqdXN0PTIpLCBjb2w9ImJsdWUiKSANCg0KfQ0KcGFyKG1mcm93PWMoMiwzKSkNCmJvb3QuaGlzdChidC5jb2VmLm10cng9Y29lZi5tdHJ4LCB2YXIuaWQ9MSwgdmFyLm5tID0iR0EiICkNCmBgYA0KTm90aGluZyBzZWVtcyBvZmYgYWJvdXQgdGhlIGJsdWUgZGVuc2l0eSBjdXJ2ZSBoaXN0b2dyYW0gd2hpY2ggcmVwcmVzZW50cyB0aGUgYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWxzLiBJIHRyaWRlZCBpbmNyZWFzaW5nIHRoZSBudW1iZXIgb2YgdHJpYWxzIGJ1dCBzdGlsbCBnb3QgdGhlIHNhbWUgcmVzdWx0cy4NCg0KYGBge3J9DQpCID0gMjAwMCAgDQpudW0ucCA9IGRpbShtb2RlbC5mcmFtZShmdWxsLm1vZGVsKSlbMl0gDQpzbXBsLm4gPSBkaW0obW9kZWwuZnJhbWUoZnVsbC5tb2RlbCkpWzFdIA0KDQpjb2VmLm10cnggPSBtYXRyaXgocmVwKDAsIEIqbnVtLnApLCBuY29sID0gbnVtLnApICAgICAgIA0KZm9yIChpIGluIDE6Qil7DQogIGJvb3RjLmlkID0gc2FtcGxlKDE6c21wbC5uLCBzbXBsLm4sIHJlcGxhY2UgPSBUUlVFKQ0KICBmdWxsLm1vZGVsLmJ0YyA9IGxtKEczIH4gYWJzZW5jZXMgKyBmYW1yZWwgKyBmYWlsdXJlcyArIEdBLCBkYXRhID0gU3R1ZGVudHMubnVtW2Jvb3RjLmlkLF0pICAgICANCiAgY29lZi5tdHJ4W2ksXSA9IGNvZWYoZnVsbC5tb2RlbC5idGMpICAgICAgDQp9DQpgYGANCg0KYGBge3J9DQpjbXRyeCA8LSBzdW1tYXJ5KGZ1bGwubW9kZWwpJGNvZWYNCm51bS5wID0gZGltKGNvZWYubXRyeClbMl0gIA0KYnRjLmNpID0gTlVMTA0KYnRjLndkID0gTlVMTA0KZm9yIChpIGluIDE6bnVtLnApew0KICBsY2kuMDI1ID0gcm91bmQocXVhbnRpbGUoY29lZi5tdHJ4WywgaV0sIDAuMDI1LCB0eXBlID0gMiksNCkNCiAgdWNpLjk3NSA9IHJvdW5kKHF1YW50aWxlKGNvZWYubXRyeFssIGldLDAuOTc1LCB0eXBlID0gMiApLDQpDQogIGJ0Yy53ZFtpXSA9ICB1Y2kuOTc1IC0gbGNpLjAyNQ0KICBidGMuY2lbaV0gPSBwYXN0ZSgiWyIsIHJvdW5kKGxjaS4wMjUsNCksIiwgIiwgcm91bmQodWNpLjk3NSw0KSwiXSIpDQoNCn0NCiAgDQprYWJsZShhcy5kYXRhLmZyYW1lKGNiaW5kKGZvcm1hdEMoY210cngsNCxmb3JtYXQ9ImYiKSwgYnRjLmNpLjk1PWJ0Yy5jaSkpLCANCiAgICAgIGNhcHRpb24gPSAiUmVncmVzc2lvbiBDb2VmZmljaWVudCBNYXRyaXgiKQ0KYGBgDQoNCg0KDQoNCiMjIEJvb3RzdHJhcCBDb25maWRlbmNlIEludGVydmFscyBmb3IgUmVzaWR1YWxzDQpgYGB7cn0NCm1vZGVsLnJlc2lkID0gZnVsbC5tb2RlbCRyZXNpZHVhbHMNCg0KQj0xMDAwDQpudW0ucCA9IGRpbShtb2RlbC5tYXRyaXgoZnVsbC5tb2RlbCkpWzJdICAgDQpzYW1wLm4gPSBkaW0obW9kZWwubWF0cml4KGZ1bGwubW9kZWwpKVsxXSAgDQpidHIubXRyeCA9IG1hdHJpeChyZXAoMCw2KkIpLCBuY29sPW51bS5wKSANCmZvciAoaSBpbiAxOkIpew0KDQogIGJ0LmxnLmZtID0gZnVsbC5tb2RlbCRmaXR0ZWQudmFsdWVzICsgDQogICAgICAgIHNhbXBsZShmdWxsLm1vZGVsJHJlc2lkdWFscywgc2FtcC5uLCByZXBsYWNlID0gVFJVRSkgIA0KDQogIFN0dWRlbnRzLm51bSRidC5sZy5mbSA9ICBidC5sZy5mbSAgDQogIGJ0ci5tb2RlbCA9IGxtKGJ0LmxnLmZtIH4gYWJzZW5jZXMgKyBmYW1yZWwgKyBHQSArICANCiAgICAgICAgICAgICAgICAgZmFpbHVyZXMsIGRhdGEgPSBTdHVkZW50cy5udW0pIA0KICBidHIubXRyeFtpLF09YnRyLm1vZGVsJGNvZWZmaWNpZW50cw0KfQ0KDQoNCg0KbnVtLnAgPSBkaW0oY29lZi5tdHJ4KVsyXSANCmJ0ci5jaSA9IE5VTEwNCmJ0ci53ZCA9IE5VTEwNCmZvciAoaSBpbiAxOm51bS5wKXsNCiAgbGNpLjAyNSA9IHJvdW5kKHF1YW50aWxlKGJ0ci5tdHJ4WywgaV0sIDAuMDI1LCB0eXBlID0gMiksNCkNCiAgdWNpLjk3NSA9IHJvdW5kKHF1YW50aWxlKGJ0ci5tdHJ4WywgaV0sMC45NzUsIHR5cGUgPSAyICksNCkNCiAgYnRyLndkW2ldID0gdWNpLjk3NSAtIGxjaS4wMjUNCiAgYnRyLmNpW2ldID0gcGFzdGUoIlsiLCByb3VuZChsY2kuMDI1LDQpLCIsICIsIHJvdW5kKHVjaS45NzUsNCksIl0iKQ0KfQ0KDQprYWJsZShhcy5kYXRhLmZyYW1lKGNiaW5kKGZvcm1hdEMoY210cngsNCxmb3JtYXQ9ImYiKSwgYnRyLmNpLjk1PWJ0ci5jaSkpLCANCiAgICAgIGNhcHRpb24gPSAiUmVncmVzc2lvbiBDb2VmZmljaWVudCBNYXRyaXggd2l0aCA5NSUgUmVzaWR1YWwgQm9vdHN0cmFwIENJIikNCmBgYA0KRXZlbiB0aG91Z2ggdXNpbmcgdGhlIHJlc2lkdWFsIGJvb3RzdHJhcCBjb25maWRlbmNlIGludGVydmFscyBpcyBub3QgYWx3YXlzIHJlY29tbWVuZGVkLCBJIGp1c3Qgd2FudGVkIHRvIGNvbXBhcmUgaXQgdG8gdGhlIHByZXZpb3VzIG9uZS4gV2Ugc2VlIGZyb20gdGhlIGNoYXJ0IGFib3ZlIHRoYXQgdGhlIGJvb3RzdHJhcCBjb25maWRlbmNlIGludGVydmFscyBmb3IgdGhlIHJlc2lkdWFscyBjb250YWluIDAuIHRoaXMgY2FuIGluZGljYXRlIHRoYXQgdGhlcmUgaXMgbm90IGEgbG90IG9mIGJpYXMgaW4gdGhlIG1vZGVsDQoNCiMjIENvbWJpbmluZyBSZXN1bHRzDQoNCmBgYHtyfQ0Ka2FibGUoYXMuZGF0YS5mcmFtZShjYmluZChmb3JtYXRDKGNtdHJ4WywtM10sNCxmb3JtYXQ9ImYiKSwgYnRjLmNpLjk1PWJ0Yy5jaSxidHIuY2kuOTU9YnRyLmNpKSksIA0KICAgICAgY2FwdGlvbj0iRmluYWwgQ29tYmluZWQgSW5mZXJlbnRpYWwgU3RhdGlzdGljczogcC12YWx1ZXMgYW5kIEJvb3RzdHJhcCBDSXMiKQ0KYGBgDQpgYGB7cn0NCmthYmxlKGNtdHJ4LCBjYXB0aW9uID0gIkluZmVyZW50aWFsIFN0YXRpc3RpY3Mgb2YgRmluYWwgTW9kZWwiKQ0KYGBgDQpUaGUgYm9vdHN0cmFwIGFuZCB0aGUgcGFyYW1ldHJpYyBtb2RlbHMgYWxsIGdhdmUgc2ltaWxhciByZXN1bHRzLCBidXQgSSBhbSBjaG9vc2luZyB0byB1c2UgdGhlIGJvb3RzdHJhcCBtZXRob2QgdG8gcmVwb3J0IHRoZSBmaW5hbCBtb2RlbCBvZiBteSBkYXRhIGJlY2F1c2UgbXkgZGF0YSBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgYW5kIGl0IGNvbnRhaW5zIGVub3VnaCBvYnNlcnZhdGlvbnMgd2hlcmUgdGhlIGJvb3RzdHJhcCBtZXRob2QgY2FuIGJlIGFwcGxpZWQuIFRoZSBmaW5hbCBtb2RlbCBjYW4gYmUgZGVzY3JpYmVkIGFzOiANCg0KRzMgPSAtMy41MzYgKyAwLjAzOShhYnNlbmNlcykgKyAwLjI2NihmYW1yZWwpICsgMS4xOChHQSkgLSAwLjIzNihmYWlsdXJlcykNCg0KRnJvbSB0aGlzIG1vZGVsLCB3ZSBjYW4gc2VlIHRoYXQgZmlyc3QgYW5kIHNlY29uZCBwZXJpb2QgZ3JhZGVzLCB0aGUgbnVtYmVyIG9mIGFic2VuY2VzLCBhbmQgdGhlIGZhbWlseSByZWxhdGlvbnNoaXAgcG9zaXRpdmVseSBpbXBhY3QgdGhlIGZpbmFsIGdyYWRlIGEgc3R1ZGVudCByZWNlaXZlcyBpbiBtYXRoLiBUaGUgbnVtYmVyIG9mIGNsYXNzIGZhaWx1cmVzIHR5cGljYWxseSBjYXVzZSB0aGUgZmluYWwgZ3JhZGUgaW4gbWF0aCB0byBkZWNyZWFzZS4gV2UgY2FuIHNlZSBmcm9tIHRoZSBtb2RlbCB0aGF0IHdoZW4gdGhlIGFtb3VudCBvZiBmYWlsdXJlcyBpbmNyZWFzZXMgYnkgb25lLCB0aGUgZmluYWwgbWF0aCBncmFkZSBkZWNyZWFzZXMgYnkgMC4yMzYgcG9pbnRzLiBXaGVuIHRoZSBhdmVyYWdlIG9mIHRoZSBzdHVkZW50J3MgZmlyc3QgYW5kIHNlY29uZCBwZXJpb2QgZ3JhZGVzIGluY3JlYXNlcyBieSBhIHBvaW50LCB0aGUgc3R1ZGVudHMgZmluYWwgZ3JhZGUgaW5jcmVhc2VzIGJ5IDEuMTguICANCg0KDQpgYGB7cn0NCmthYmxlKGFzLmRhdGEuZnJhbWUoY2JpbmQoZm9ybWF0QyhjbXRyeCw0LGZvcm1hdD0iZiIpLCBidGMuY2kuOTU9YnRjLmNpKSksIA0KICAgICAgY2FwdGlvbiA9ICJJbmZlcmVudGlhbCBTdGF0aXN0aWNzIG9mIEZpbmFsIE1vZGVsIikNCmBgYA0KDQoNCiMgQ29uY2x1c2lvbiBhbmQgRGlzY3Vzc2lvbiANCg0KVG8gY29uY2x1ZGUsIEkgb25seSB0b29rIGludG8gYWNjb3VudCBzZXZlcmFsIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyB0aGF0IGhhZCBzaWduaWZpY2FudCBwIHZhbHVlcy4gSSBjcmVhdGVkIGEgdG90YWwgb2YgdGhyZWUgZGlmZmVyZW50IG1vZGVsczogdGhlIGZ1bGwgbW9kZWwsIHRoZSBsb2cgdHJhbnNmb3JtZWQgbW9kZWwsIGFuZCB0aGUgc3F1YXJlIHJvb3QgdHJhbnNmb3JtZWQgbW9kZWwuIEVhY2ggbW9kZWwgY29udGFpbnMgdGhlIHNhbWUgbnVtYmVyIG9mIHZhcmlhYmxlcy4gRXZlbiB0aG91Z2ggaXQgZW5kZWQgdXAgYmVpbmcgdGhlIGJlc3QgbW9kZWwsIHRoZSBmdWxsIG1vZGVsIGhhZCBzZXZlcmFsIHZpb2xhdGlvbnMuIFRoZSB2YXJpYW5jZSBvZiB0aGUgbW9kZWwgd2FzIG5vdCBjb25zdGFudCwgYW5kIHRoZSBib3ggY294IHRyYW5zZm9ybWF0aW9uIHdhcyBkb25lIGluIG9yZGVyIHRvIHRyeSB0byBjb3JyZWN0IHRoaXMuIFRoZSBkYXRhIHdhcyBhbHNvIG5vdCBub3JtYWwsIHRoaXMgdmlvbGF0aW9uIHJlbWFpbnMgdW5jb3JyZWN0ZWQuIEFsc28sIHRoZSB2YXJpYWJsZSAiZmFpbHVyZXMiIGlzIG5vdCBhcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGxpa2UgdGhlIG90aGVyIG9uZXMuIHRoZSBmdWxsIG1vZGVsIGhhZCBhbmQgUiBzcXVhcmVkIHZhbHVlIG9mIGFib3V0IDAuNzkuIEV2ZW4gdGhvdWdoIHRoZSBsb2cgdHJhbnNmb3JtYXRpb24gbW9kZWwgaGFkIHRoZSBoaWdoZXIgUiBzcXVhcmVkIHZhbHVlLCBJIHNlbGVjdGVkIHRoaXMgbW9kZWwgdG8gdXNlIGZvciB0aGUgc2Vjb25kIHBhcnQgb2YgdGhlIGFzc2lnbm1lbnQuIA0KDQpGb3IgdGhlIHNlY29uZCBwYXJ0IG9mIHRoZSBhc3NpZ25tZW50IEkgZGlkIDk1JSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSBjb2VmZmljaWVudHMgYW5kIHRoZSByZXNpZHVhbHMuIEkgY29uY2x1ZGVkIHRoYXQgYSBib290c3RyYXAgbW9kZWwgb2YgdGhlIGNvZWZmaWNpZW50cyB3b3VsZCBiZSB0aGUgYmVzdCB3YXkgdG8gZXZhbHVhdGUgdGhlIGZ1bGwgbW9kZWwsIGRlc3BpdGUgdGhlIGZhY3QgdGhhdCB0aGUgYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWwgY29udGFpbnMgYSAwIGZvciB0aGUgdmFyaWFibGUgIkdBIi4gUmVjb21tZW5kYXRpb25zIGZvciBwdXJzdWluZyB0aGlzIHByb2plY3QgZnVydGhlciBpbmNsdWRlIGZpZ3VyaW5nIG91dCBob3cgdGhlIGRhdGEgZm9yIHRoZSBsb2cgdHJhbnNmb3JtZWQgbW9kZWwgY291bGQgYmUgdXNlZCBhcyB0aGUgZmluYWwgbW9kZWwgc2luY2UgaXQgaGFzIGEgaGlnaGVyIFIgc3F1YXJlZCB2YWx1ZS4gSSB3b3VsZCBhbHNvIGVpdGhlciB1c2UgZGlmZmVyZW50IHZhcmlhYmxlcyBvciB0cmFuc2Zvcm0gdGhlIGV4aXN0aW5nIHZhcmlhYmxlcyBzbyB0aGUgYm9vdHN0cmFwIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIEdBIGRpZCBub3QgY29udGFpbiAwLiBpIHdvdWxkIGFsc28gdXNlIHNvbWUgb2YgdGhlIHZhcmlhYmxlcyBmcm9tIGFub3RoZXIgc2V0IG9uIGRhdGEud29ybGQuY29tIHRoYXQgd2FzIGJ5IHRoZSBzYW1lIGF1dGhvciB3aXRoIGRhdGEgZnJvbSB0aGUgc2FtZSBzY2hvb2xzIGFuZCBzdXJ2ZXlzIGFib3V0IHRoZSBzYW1lIHN0dWRlbnQncyBmaW5hbCBFbmdsaXNoIGdyYWRlcy4gQSBzdHVkZW50cyBmaW5hbCBncmFkZSBpbiBFbmdsaXNoIGlzIGxpa2VseSBhIGdvb2QgcHJlZGljdG9yIG9mIHRoZWlyIGZpbmFsIG1hdGggZ3JhZGUuDQoNCg0K