1 Introduction

This project will demonstrate the use of variable selection to determine the final model. Bootstrapping procedures will be used to generate estimated confidence intervals for the coefficients of the regression model identified in a previous assignment. Specifically a model that looks at how different variables like age, hypertension, and diabetes can help predict a patients bmi.

1.1 Description of the Dataset

The data set being used is called Diabetes Prediction. The data set contains over 100,000 patient observations on both their medical and demographic information. This includes a patients diabetic status, hypertension status, heart disease status, as either having or not having the medical ailment. The full in order list of the data sets variables is as follows,

gender: chr

age: num

hypertension: int

heart_disease: int

smoking_history: chr

bmi: num

HbA1c_level: num

blood_glucose_level: int

diabetes: int

2 Materials

2.1 Data Preparation

url = "https://raw.githubusercontent.com/ChloeWinters79/STA321/refs/heads/main/Data/diabetes_prediction_dataset.csv"
bmi.diabetes = read.csv(url, header = TRUE)

HbA1c <- bmi.diabetes$HbA1c_level
Diabetic <- bmi.diabetes$diabetes
BloodGlucose <- bmi.diabetes$blood_glucose_level


prediabetic = (HbA1c > 5.69)  & (BloodGlucose > 99) & (Diabetic < 1 ) #create the variable prediabetic from those with blood glucose and HbA1c levels that fall above the normal range (determined by the American Diabetes Association) for non diabetic patients. 
bmi.diabetes$prediabetic = as.character(prediabetic) #convert from logic to character

The variables HbA1c_level, blood_glucose_level, and diabetes are used to define the variable prediabetic. The variable prediabetic is defined as follows, prediabetic = TRUE if HbA1c_level > 5.69, blood_glucose_level > 99 & diabetes < 1; prediabetic = FALSE otherwise.

American Diabetes Association (ADA) published that a normal HbA1c level is under 5.7, while a prediabetic HbA1c is between 5.7 and 6.5, and anything above 6.5 is considered diabetic. Additionally, the ADA finds that blood glucose (bg) levels under 100 are withing the normal range, bg levels from 100 to 125 are prediabetic, and bg levels 126 or over are diabetic.

These parameters from the ADA were used to create the variable prediabetic. Any patient with HbA1c and blood glucose levels outside of the normal range that are not diagnosed diabetics were flagged at prediabetic patients.

final.data = bmi.diabetes[, -c(1,5)] #remove gender and smoking_history, the variables not used in the initial candidate model
kable(head(final.data))
age hypertension heart_disease bmi HbA1c_level blood_glucose_level diabetes prediabetic
80 0 1 25.19 6.6 140 0 TRUE
54 0 0 27.32 6.6 80 0 FALSE
28 0 0 27.32 5.7 158 0 TRUE
36 0 0 23.45 5.0 155 0 FALSE
76 1 1 20.14 4.8 155 0 FALSE
20 0 0 27.32 6.6 85 0 FALSE
bmi.log.age = lm((bmi)^-0.5 ~ log(age) + hypertension + heart_disease +  HbA1c_level
       + blood_glucose_level + diabetes + prediabetic, data = final.data)

kable(summary(bmi.log.age)$coef, caption = "Inferential Statistics of Model")
Inferential Statistics of Model
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.2421664 0.0005924 408.7822241 0.0000000
log(age) -0.0133497 0.0000692 -192.8523791 0.0000000
hypertension -0.0027677 0.0002457 -11.2636679 0.0000000
heart_disease 0.0040241 0.0003292 12.2222941 0.0000000
HbA1c_level 0.0000510 0.0000879 0.5801947 0.5617846
blood_glucose_level -0.0000001 0.0000019 -0.0617243 0.9507825
diabetes -0.0079106 0.0003583 -22.0761603 0.0000000
prediabeticTRUE -0.0000032 0.0001923 -0.0163902 0.9869231

Considering the fact that three variables have high p-values and low t-values, it seems like some backwards selection is necessary before moving forward. In this case we are going to use the t-statistic to decide which value to throw away during each step. The variable selection will stop when all variables have a t-value higher than 1. Since blood_glucose_level has the smallest t-value we will start by removing that variable from the model.

bmi.log.age.6 = lm((bmi)^-0.5 ~ log(age) + hypertension + heart_disease +  HbA1c_level
                 + diabetes + prediabetic, data = final.data)
summary(bmi.log.age.6)
## 
## Call:
## lm(formula = (bmi)^-0.5 ~ log(age) + hypertension + heart_disease + 
##     HbA1c_level + diabetes + prediabetic, data = final.data)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.114252 -0.011085  0.000521  0.011184  0.132117 
## 
## Coefficients:
##                   Estimate Std. Error  t value Pr(>|t|)    
## (Intercept)      2.421e-01  4.726e-04  512.317   <2e-16 ***
## log(age)        -1.335e-02  6.922e-05 -192.854   <2e-16 ***
## hypertension    -2.768e-03  2.457e-04  -11.264   <2e-16 ***
## heart_disease    4.024e-03  3.292e-04   12.223   <2e-16 ***
## HbA1c_level      5.265e-05  8.362e-05    0.630    0.529    
## diabetes        -7.923e-03  3.003e-04  -26.382   <2e-16 ***
## prediabeticTRUE -8.424e-06  1.723e-04   -0.049    0.961    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.01974 on 99993 degrees of freedom
## Multiple R-squared:  0.3084, Adjusted R-squared:  0.3084 
## F-statistic:  7433 on 6 and 99993 DF,  p-value: < 2.2e-16

There are still variables with a t-statistic less than zero so the backwards selection continues until no more variable with a low t-statistic remain.

bmi.log.age.5 = lm((bmi)^-0.5 ~ log(age) + hypertension + heart_disease +  HbA1c_level
                 + diabetes, data = final.data)
summary(bmi.log.age.5)
## 
## Call:
## lm(formula = (bmi)^-0.5 ~ log(age) + hypertension + heart_disease + 
##     HbA1c_level + diabetes, data = final.data)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.114255 -0.011083  0.000519  0.011182  0.132114 
## 
## Coefficients:
##                 Estimate Std. Error  t value Pr(>|t|)    
## (Intercept)    2.422e-01  4.206e-04  575.682   <2e-16 ***
## log(age)      -1.335e-02  6.922e-05 -192.855   <2e-16 ***
## hypertension  -2.768e-03  2.457e-04  -11.264   <2e-16 ***
## heart_disease  4.024e-03  3.292e-04   12.223   <2e-16 ***
## HbA1c_level    5.000e-05  6.364e-05    0.786    0.432    
## diabetes      -7.915e-03  2.533e-04  -31.248   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.01974 on 99994 degrees of freedom
## Multiple R-squared:  0.3084, Adjusted R-squared:  0.3084 
## F-statistic:  8920 on 5 and 99994 DF,  p-value: < 2.2e-16
# Output shows 1 remaining variable with a t-statistic under 1 so we continue with backwards selection. 

bmi.log.age.4 = lm((bmi)^-0.5 ~ log(age) + hypertension + heart_disease
                 + diabetes, data = final.data)
summary(bmi.log.age.4)
## 
## Call:
## lm(formula = (bmi)^-0.5 ~ log(age) + hypertension + heart_disease + 
##     diabetes, data = final.data)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.114220 -0.011113  0.000545  0.011190  0.132144 
## 
## Coefficients:
##                 Estimate Std. Error t value Pr(>|t|)    
## (Intercept)    2.424e-01  2.422e-04 1000.92   <2e-16 ***
## log(age)      -1.335e-02  6.922e-05 -192.86   <2e-16 ***
## hypertension  -2.767e-03  2.457e-04  -11.26   <2e-16 ***
## heart_disease  4.024e-03  3.292e-04   12.22   <2e-16 ***
## diabetes      -7.838e-03  2.336e-04  -33.55   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.01974 on 99995 degrees of freedom
## Multiple R-squared:  0.3084, Adjusted R-squared:  0.3084 
## F-statistic: 1.115e+04 on 4 and 99995 DF,  p-value: < 2.2e-16

Now that all the variables have a t-statistic above 1, we can continue onto determining the final model.

bmi.coef.4 <- summary(bmi.log.age.4)$coef
kable(summary(bmi.log.age.4)$coef, caption = "Inferential Statistics of Final Model")
Inferential Statistics of Final Model
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.2424251 0.0002422 1000.92041 0
log(age) -0.0133499 0.0000692 -192.85792 0
hypertension -0.0027673 0.0002457 -11.26224 0
heart_disease 0.0040239 0.0003292 12.22173 0
diabetes -0.0078379 0.0002336 -33.55337 0

The final model is,

\[ \text{bmi} ^ {-0.5} = 0.2424251 - 0.0133499\times \log(\text{age}) - 0.0027673\times \text{hypertension} + 0.0040239\times \text{heart_disease} - 0.0078379\times \text{diabetes} \]

3 Methodology and Analysis

3.1 Bootstrapping

Now that the final model has been determine, bootstrapping can be used to help find the confidence intervals for the coefficients in the final regression model.

B = 1000       # choose the number of bootstrap replicates.
## 
num.p = dim(model.frame(bmi.log.age.4))[2]  # returns number of parameters in the model
smpl.n = dim(model.frame(bmi.log.age.4))[1] # sample size
## zero matrix to store bootstrap coefficients 
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) # fit final model to the bootstrap sample
  bmi.log.age.boot = lm((bmi)^-0.5 ~ log(age) + hypertension + heart_disease
                 + diabetes, data = final.data[bootc.id,])     
  coef.mtrx[i,] = coef(bmi.log.age.boot)}    # extract coefs from bootstrap regression model    

3.1.1 Histograms

An R function was defined to make the histograms of the bootstrap regression coefficients. This function is also used later on to create the histograms for the residual bootstrap estimated regression coefficients.

boot.hist = function(bmi.coef.4, 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))  # histograms of bootstrap coefs
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=1, var.nm ="Intercept" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=2, var.nm ="Log Age" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=3, var.nm ="Hypertension" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=4, var.nm ="Heart Disease" )
boot.hist(bt.coef.mtrx=coef.mtrx, var.id=5, var.nm ="Diabetes" )

The histograms were created to depict to normal density curves on top of each of the histograms. The first curve is red and it uses the estimated regression coefficients and their standard error in the output of the regression procedure. The red curve is the basis for the reported p-values. The second curve on the histograms is blue. The blue curve is a non-parametric estimate of the density of the bootstrap sampling distribution. The blue curve is the basis for the reported confidence intervals.

All of the density curves on the histograms seem to be quite close, with some of them even sharing a substantial amount of overlap. None of the curves appear to show any skewness, they are all normally distributed.

3.1.2 Confidence Intervals

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

kable(as.data.frame(cbind(formatC(bmi.coef.4, 4, format = "f"), boot.ci.95=boot.ci)), caption = "Regression Coefficient Matrix With 95% Residiual Bootstrap Confidence Interval")
Regression Coefficient Matrix With 95% Residiual Bootstrap Confidence Interval
Estimate Std. Error t value Pr(>|t|) boot.ci.95
(Intercept) 0.2424 0.0002 1000.9204 0.0000 [ 0.2418 , 0.243 ]
log(age) -0.0133 0.0001 -192.8579 0.0000 [ -0.0135 , -0.0132 ]
hypertension -0.0028 0.0002 -11.2622 0.0000 [ -0.0033 , -0.0023 ]
heart_disease 0.0040 0.0003 12.2217 0.0000 [ 0.0034 , 0.0046 ]
diabetes -0.0078 0.0002 -33.5534 0.0000 [ -0.0083 , -0.0074 ]

Looking at the confidence intervals for the variables, we want to confirm that none of them contain the value 0. From the output, it seems that none of the 95% confidence intervals span 0 and they are all consistent. Additionally, all the p-values are smaller than 0.05. This output aligns with what is depicted in the normal density curves on the histograms above.

3.2 Bootstrapping Residuals

Now we are going use the bootstrap residual methods to create bootstrap confidence intervals.

3.2.1 Residuals

hist(sort(bmi.log.age.4$residuals),
     xlab = "Residuals",
     main = "Histogram of Residuals")

The residual plot shows that the distribution of the residuals has no outlines and appears to have a normal distribution.

Now we are going to use the below code to generate the bootstrap confidence intervals of the regression coefficients

final.bmi.model = lm((bmi)^-0.5 ~ log(age) + hypertension + heart_disease
                 + diabetes, data = final.data)

model.residual = final.bmi.model$residuals
num.p = dim(model.matrix(final.bmi.model))[2]
sample.num = dim(model.matrix(final.bmi.model))[1]
boot.matrix = matrix(rep(0,5*B), ncol = num.p)

for (i in 1:B) {
  boot.log.bmi = final.bmi.model$fitted.values +
  sample(final.bmi.model$residuals, sample.num, replace = TRUE)
  final.data$boot.log.bmi = boot.log.bmi
  bootr.model = lm(boot.log.bmi ~ log(age) + hypertension + heart_disease
                 + diabetes, data = final.data)   
  boot.matrix[i, ] = bootr.model$coefficients}

3.2.2 Histograms

Now using the function that was mentioned earlier we are going to make the histograms of the residual bootstrap estimates of the regression coefficients.

boot.res.hist = function(bt.res.mtrx, var.id, var.nm){
  x1.1 <- seq(min(bt.res.mtrx[,var.id]), max(bt.res.mtrx[,var.id]), length=300 )
  y1.1 <- dnorm(x1.1, mean(bt.res.mtrx[,var.id]), sd(bt.res.mtrx[,var.id]))
  highestbar = max(hist(bt.res.mtrx[,var.id], plot = FALSE)$density) 
  ylimit <- max(c(y1.1,highestbar))
  hist(bt.res.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.res.mtrx[,var.id], adjust=2), col="blue")}
par(mfrow=c(2,3))  # histograms of bootstrap residuals
boot.res.hist(bt.res.mtrx=boot.matrix, var.id=1, var.nm ="Intercept" )
boot.res.hist(bt.res.mtrx=boot.matrix, var.id=2, var.nm ="Log Age" )
boot.res.hist(bt.res.mtrx=boot.matrix, var.id=3, var.nm ="Hypertension" )
boot.res.hist(bt.res.mtrx=boot.matrix, var.id=4, var.nm ="Heart Disease" )
boot.res.hist(bt.res.mtrx=boot.matrix, var.id=5, var.nm ="Diabetes" )

The curves on all the histograms seem to be very close and have good overlap similar to the earlier histograms. There does seems to be some slight skewness from the center for both the Intercept and Log Age histograms, however the skewness seems to be very slight and nothing to be extremely concerned with. The confidence intervals should yield similar information to the histograms so we can confirm that the skewness is minor when analyzing the confidence intervals. The 95% residual bootstrap confidence intervals are given below,

3.2.3 Confidence Intervals

num.p = dim(boot.matrix)[2]
boot.r.ci = NULL
boot.r.wd = NULL
for (i in 1:num.p){
  lowerci0.025.res = round(quantile(boot.matrix[, i], 0.025, type = 2), 8)
  upperci0.975.res = round(quantile(boot.matrix [, i], 0.975, type =2), 8)
  boot.r.wd[i] = upperci0.975.res - lowerci0.025.res
  boot.r.ci[i] = paste ("[", round(lowerci0.025.res, 4), " , ", round(upperci0.975.res,4), "]")}

kable(as.data.frame(cbind(formatC(bmi.coef.4, 4, format = "f"), boot.r.ci.95=boot.r.ci)), caption = "Regression Coefficient Matric")
Regression Coefficient Matric
Estimate Std. Error t value Pr(>|t|) boot.r.ci.95
(Intercept) 0.2424 0.0002 1000.9204 0.0000 [ 0.2419 , 0.2429 ]
log(age) -0.0133 0.0001 -192.8579 0.0000 [ -0.0135 , -0.0132 ]
hypertension -0.0028 0.0002 -11.2622 0.0000 [ -0.0033 , -0.0023 ]
heart_disease 0.0040 0.0003 12.2217 0.0000 [ 0.0033 , 0.0047 ]
diabetes -0.0078 0.0002 -33.5534 0.0000 [ -0.0083 , -0.0074 ]

The residual bootstrap confidence intervals yield the same results as the p-values. This is to be expected considering the significantly large sample size. The large sample size allows for the distribution of the estimated coefficients to have a good approximation of the normal distributions.

4 Results and Conclusions

4.1 Inferential Statistics

kable(as.data.frame(cbind(formatC(bmi.coef.4[, -3], 4, format ="f"), btc.ct.95 = boot.ci, btr.ci.95 = boot.r.ci)),
      caption = "Final Combined Inferential Statistics with p-values and Bootstrap Confidence Intervals")
Final Combined Inferential Statistics with p-values and Bootstrap Confidence Intervals
Estimate Std. Error Pr(>|t|) btc.ct.95 btr.ci.95
(Intercept) 0.2424 0.0002 0.0000 [ 0.2418 , 0.243 ] [ 0.2419 , 0.2429 ]
log(age) -0.0133 0.0001 0.0000 [ -0.0135 , -0.0132 ] [ -0.0135 , -0.0132 ]
hypertension -0.0028 0.0002 0.0000 [ -0.0033 , -0.0023 ] [ -0.0033 , -0.0023 ]
heart_disease 0.0040 0.0003 0.0000 [ 0.0034 , 0.0046 ] [ 0.0033 , 0.0047 ]
diabetes -0.0078 0.0002 0.0000 [ -0.0083 , -0.0074 ] [ -0.0083 , -0.0074 ]

Looking at the above output it is clear that the three methods yield the same results, which is the significance of the individual explanatory variables. This makes sense considering that the final model \[ \text{bmi} = 0.2424251 - 0.0133499\times \log(\text{age}) - 0.0027673\times \text{hypertension} + 0.0040239\times \text{heart_disease} - 0.0078379\times \text{diabetes} \] does not have serious violations of the model assumptions.

kable(round(cbind(boot.wd, boot.r.wd), 4), caption = "Widths of both Bootstrap Confidence Intervals")
Widths of both Bootstrap Confidence Intervals
boot.wd boot.r.wd
0.0012 0.0010
0.0003 0.0003
0.0010 0.0009
0.0012 0.0014
0.0009 0.0009

The above table shows that the widths of the residual and case bootstrap confidence intervals are either very similar or exactly the same as each other.

kable(bmi.coef.4, caption = "Inferential Statistics of Final Model")
Inferential Statistics of Final Model
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.2424251 0.0002422 1000.92041 0
log(age) -0.0133499 0.0000692 -192.85792 0
hypertension -0.0027673 0.0002457 -11.26224 0
heart_disease 0.0040239 0.0003292 12.22173 0
diabetes -0.0078379 0.0002336 -33.55337 0

The regression coefficients explain the correlation between the adjusted bmi and the corresponding variables, with log(age) instead of age. Taking into consideration that both the confidence intervals and the p-values depicted that these variables had a significant ability to determine the response variable that was not 0. We can determine that this model can be used to help predict a patients expected bmi based on their other factors like hypertension, and age.

Additionally, we can explain the estimated regression coefficient of -0.0078379 as follows, since diabetes is a binary variable it can only be expressed as 0 or 1. Due to the transformation on BMI during model building the variable diabetes would be expressed as follows,

\[ Diabetes_1^{-0.5} - Diabetes_0^{-0.5} = -0.0078379 \] Since, 1 raised to any power will always be 1, and 0 raised to -0.5 is undefined it simplifies to the following.

\[ Diabetes_1 = -0.0078379 \] Using this model, patients with diabetes with see a 0.78% decrease in the bmi of the patient. The other binary variables in the model can be interpreted similarly.

5 General Discussion

While the model depicted several significant predictive variables for the response variable in the context of this data set it is important to note that while the variables are significant their impact on the value of bmi itself is rather small. Unfortunately there is the misconception when it comes to the medical field that everyone who has issues like diabetes, hypertension, or heart disease must be overweight and all their problems come from being overweight. However, this model depicts only a small change to the predicted bmi if a patient has diabetes, hypertension, or heart disease or not. While the American Diabetes Association does note that having a higher bmi does put patients at risk for having diabetes, anyone can become diabetic, regardless of their weight. Any misconceptions on a exclusive relationship between bmi and diabetic status are simply that, misconceptions.

Also, it is important to note that this data set is incredibly large with over 100,000 observations. Large data sets make it very easy to get p-values that are close to if not zero. Ideally, it would be better to use a smaller data set when analyzing the variables for the final model to see what the p-values look like when they are not pushed to zero by a large sample size.

However, the model was not determined solely on the p-value, and there was additional testing that took place. Additionally, while ethically using factors like hypertension, heart disease and diabetes should not be the main aspects of trying to determine a patients bmi. Due to the fact that is would most likely further the connotation that only people with those ailments have high bmi’s, the model could technically be used in that way.

6 References

American Diabetes Association. (2023). Understanding Diabetes Diagnosis. Diabetes.org. https://diabetes.org/about-diabetes/diagnosis

American Diabetes Association. Extra Weight, Extra Risk Diabetes.org. https://diabetes.org/health-wellness/weight-management/extra-weight-extra-risk

LS0tCnRpdGxlOiAnU1RBIFByb2plY3QgMSAtIE1MUiAmIEJvb3RzdHJhcHBpbmcnCmF1dGhvcjogJ0NobG9lIFdpbnRlcnMnCmRhdGU6ICI5LzI5LzIwMjQiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ193aWR0aDogNgogICAgZmlnX2hlaWdodDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvY19jb2xsYXBzZWQ6IHllcwogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIHNtb290aF9zY3JvbGw6IHllcwogICAgdGhlbWU6IGx1bWVuCiAgcGRmX2RvY3VtZW50OiAKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6ICc0JwotLS0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8KCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovCiAgZm9udC1zaXplOiAyNHB4OwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLwogIGZvbnQtc2l6ZTogMjBweDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDIycHg7CiAgICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8KICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CgpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCjwvc3R5bGU+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IAojIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dCBmaWxlcy4KbGlicmFyeShrbml0cikKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICAgICAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5ncyA9IEZBTFNFLCAgICAgICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgb3V0cHV0IGZpbGUuIAogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0cyA9IFRSVUUgICAgICAgICAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluIHRoZSBvdXRwdXQgZmlsZS4KICAgICAgICAgICAgICAgICAgICAgICkgICAKCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkoTUFTUykKbGlicmFyeShjYXIpCmxpYnJhcnkoInNjYWxlcyIpIAoKYGBgCgojIEludHJvZHVjdGlvbgoKVGhpcyBwcm9qZWN0IHdpbGwgZGVtb25zdHJhdGUgdGhlIHVzZSBvZiB2YXJpYWJsZSBzZWxlY3Rpb24gdG8gZGV0ZXJtaW5lIHRoZSBmaW5hbCBtb2RlbC4gQm9vdHN0cmFwcGluZyBwcm9jZWR1cmVzIHdpbGwgYmUgdXNlZCB0byBnZW5lcmF0ZSBlc3RpbWF0ZWQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSBjb2VmZmljaWVudHMgb2YgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgaWRlbnRpZmllZCBpbiBhIHByZXZpb3VzIGFzc2lnbm1lbnQuIFNwZWNpZmljYWxseSBhIG1vZGVsIHRoYXQgbG9va3MgYXQgaG93IGRpZmZlcmVudCB2YXJpYWJsZXMgbGlrZSBhZ2UsIGh5cGVydGVuc2lvbiwgYW5kIGRpYWJldGVzIGNhbiBoZWxwIHByZWRpY3QgYSBwYXRpZW50cyBibWkuIAoKIyMgRGVzY3JpcHRpb24gb2YgdGhlIERhdGFzZXQKClRoZSBkYXRhIHNldCBiZWluZyB1c2VkIGlzIGNhbGxlZCBEaWFiZXRlcyBQcmVkaWN0aW9uLiBUaGUgZGF0YSBzZXQgY29udGFpbnMgb3ZlciAxMDAsMDAwIHBhdGllbnQgb2JzZXJ2YXRpb25zIG9uIGJvdGggdGhlaXIgbWVkaWNhbCBhbmQgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24uIFRoaXMgaW5jbHVkZXMgYSBwYXRpZW50cyBkaWFiZXRpYyBzdGF0dXMsIGh5cGVydGVuc2lvbiBzdGF0dXMsIGhlYXJ0IGRpc2Vhc2Ugc3RhdHVzLCBhcyBlaXRoZXIgaGF2aW5nIG9yIG5vdCBoYXZpbmcgdGhlIG1lZGljYWwgYWlsbWVudC4gVGhlIGZ1bGwgaW4gb3JkZXIgbGlzdCBvZiB0aGUgZGF0YSBzZXRzIHZhcmlhYmxlcyBpcyBhcyBmb2xsb3dzLCAKCmdlbmRlcjogY2hyCgphZ2U6IG51bQoKaHlwZXJ0ZW5zaW9uOiBpbnQKCmhlYXJ0X2Rpc2Vhc2U6IGludAoKc21va2luZ19oaXN0b3J5OiBjaHIKCmJtaTogbnVtCgpIYkExY19sZXZlbDogbnVtCgpibG9vZF9nbHVjb3NlX2xldmVsOiBpbnQKCmRpYWJldGVzOiBpbnQKCgojIE1hdGVyaWFscwoKIyMgRGF0YSBQcmVwYXJhdGlvbgoKYGBge3J9CnVybCA9ICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQ2hsb2VXaW50ZXJzNzkvU1RBMzIxL3JlZnMvaGVhZHMvbWFpbi9EYXRhL2RpYWJldGVzX3ByZWRpY3Rpb25fZGF0YXNldC5jc3YiCmJtaS5kaWFiZXRlcyA9IHJlYWQuY3N2KHVybCwgaGVhZGVyID0gVFJVRSkKCkhiQTFjIDwtIGJtaS5kaWFiZXRlcyRIYkExY19sZXZlbApEaWFiZXRpYyA8LSBibWkuZGlhYmV0ZXMkZGlhYmV0ZXMKQmxvb2RHbHVjb3NlIDwtIGJtaS5kaWFiZXRlcyRibG9vZF9nbHVjb3NlX2xldmVsCgoKcHJlZGlhYmV0aWMgPSAoSGJBMWMgPiA1LjY5KSAgJiAoQmxvb2RHbHVjb3NlID4gOTkpICYgKERpYWJldGljIDwgMSApICNjcmVhdGUgdGhlIHZhcmlhYmxlIHByZWRpYWJldGljIGZyb20gdGhvc2Ugd2l0aCBibG9vZCBnbHVjb3NlIGFuZCBIYkExYyBsZXZlbHMgdGhhdCBmYWxsIGFib3ZlIHRoZSBub3JtYWwgcmFuZ2UgKGRldGVybWluZWQgYnkgdGhlIEFtZXJpY2FuIERpYWJldGVzIEFzc29jaWF0aW9uKSBmb3Igbm9uIGRpYWJldGljIHBhdGllbnRzLiAKYm1pLmRpYWJldGVzJHByZWRpYWJldGljID0gYXMuY2hhcmFjdGVyKHByZWRpYWJldGljKSAjY29udmVydCBmcm9tIGxvZ2ljIHRvIGNoYXJhY3RlcgpgYGAKClRoZSB2YXJpYWJsZXMgSGJBMWNfbGV2ZWwsIGJsb29kX2dsdWNvc2VfbGV2ZWwsIGFuZCBkaWFiZXRlcyBhcmUgdXNlZCB0byBkZWZpbmUgdGhlIHZhcmlhYmxlIHByZWRpYWJldGljLiBUaGUgdmFyaWFibGUgcHJlZGlhYmV0aWMgaXMgZGVmaW5lZCBhcyBmb2xsb3dzLCBwcmVkaWFiZXRpYyA9IFRSVUUgaWYgSGJBMWNfbGV2ZWwgPiA1LjY5LCAgYmxvb2RfZ2x1Y29zZV9sZXZlbCA+IDk5ICYgZGlhYmV0ZXMgPCAxOyBwcmVkaWFiZXRpYyA9IEZBTFNFIG90aGVyd2lzZS4gCgpBbWVyaWNhbiBEaWFiZXRlcyBBc3NvY2lhdGlvbiAoQURBKSBwdWJsaXNoZWQgdGhhdCBhIG5vcm1hbCBIYkExYyBsZXZlbCBpcyB1bmRlciA1LjcsIHdoaWxlIGEgcHJlZGlhYmV0aWMgSGJBMWMgaXMgYmV0d2VlbiA1LjcgYW5kIDYuNSwgYW5kIGFueXRoaW5nIGFib3ZlIDYuNSBpcyBjb25zaWRlcmVkIGRpYWJldGljLiBBZGRpdGlvbmFsbHksIHRoZSBBREEgZmluZHMgdGhhdCBibG9vZCBnbHVjb3NlIChiZykgbGV2ZWxzIHVuZGVyIDEwMCBhcmUgd2l0aGluZyB0aGUgbm9ybWFsIHJhbmdlLCBiZyBsZXZlbHMgZnJvbSAxMDAgdG8gMTI1IGFyZSBwcmVkaWFiZXRpYywgYW5kIGJnIGxldmVscyAxMjYgb3Igb3ZlciBhcmUgZGlhYmV0aWMuIAoKVGhlc2UgcGFyYW1ldGVycyBmcm9tIHRoZSBBREEgd2VyZSB1c2VkIHRvIGNyZWF0ZSB0aGUgdmFyaWFibGUgcHJlZGlhYmV0aWMuIEFueSBwYXRpZW50IHdpdGggSGJBMWMgYW5kIGJsb29kIGdsdWNvc2UgbGV2ZWxzIG91dHNpZGUgb2YgdGhlIG5vcm1hbCByYW5nZSB0aGF0IGFyZSBub3QgZGlhZ25vc2VkIGRpYWJldGljcyB3ZXJlIGZsYWdnZWQgYXQgcHJlZGlhYmV0aWMgcGF0aWVudHMuICAKCgoKYGBge3J9CmZpbmFsLmRhdGEgPSBibWkuZGlhYmV0ZXNbLCAtYygxLDUpXSAjcmVtb3ZlIGdlbmRlciBhbmQgc21va2luZ19oaXN0b3J5LCB0aGUgdmFyaWFibGVzIG5vdCB1c2VkIGluIHRoZSBpbml0aWFsIGNhbmRpZGF0ZSBtb2RlbAprYWJsZShoZWFkKGZpbmFsLmRhdGEpKQoKCmJtaS5sb2cuYWdlID0gbG0oKGJtaSleLTAuNSB+IGxvZyhhZ2UpICsgaHlwZXJ0ZW5zaW9uICsgaGVhcnRfZGlzZWFzZSArICBIYkExY19sZXZlbAogICAgICAgKyBibG9vZF9nbHVjb3NlX2xldmVsICsgZGlhYmV0ZXMgKyBwcmVkaWFiZXRpYywgZGF0YSA9IGZpbmFsLmRhdGEpCgprYWJsZShzdW1tYXJ5KGJtaS5sb2cuYWdlKSRjb2VmLCBjYXB0aW9uID0gIkluZmVyZW50aWFsIFN0YXRpc3RpY3Mgb2YgTW9kZWwiKQoKYGBgCgoKQ29uc2lkZXJpbmcgdGhlIGZhY3QgdGhhdCB0aHJlZSB2YXJpYWJsZXMgaGF2ZSBoaWdoIHAtdmFsdWVzIGFuZCBsb3cgdC12YWx1ZXMsIGl0IHNlZW1zIGxpa2Ugc29tZSBiYWNrd2FyZHMgc2VsZWN0aW9uIGlzIG5lY2Vzc2FyeSBiZWZvcmUgbW92aW5nIGZvcndhcmQuIEluIHRoaXMgY2FzZSB3ZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSB0LXN0YXRpc3RpYyB0byBkZWNpZGUgd2hpY2ggdmFsdWUgdG8gdGhyb3cgYXdheSBkdXJpbmcgZWFjaCBzdGVwLiBUaGUgdmFyaWFibGUgc2VsZWN0aW9uIHdpbGwgc3RvcCB3aGVuIGFsbCB2YXJpYWJsZXMgaGF2ZSBhIHQtdmFsdWUgaGlnaGVyIHRoYW4gMS4gU2luY2UgYmxvb2RfZ2x1Y29zZV9sZXZlbCBoYXMgdGhlIHNtYWxsZXN0IHQtdmFsdWUgd2Ugd2lsbCBzdGFydCBieSByZW1vdmluZyB0aGF0IHZhcmlhYmxlIGZyb20gdGhlIG1vZGVsLgoKYGBge3J9CgpibWkubG9nLmFnZS42ID0gbG0oKGJtaSleLTAuNSB+IGxvZyhhZ2UpICsgaHlwZXJ0ZW5zaW9uICsgaGVhcnRfZGlzZWFzZSArICBIYkExY19sZXZlbAogICAgICAgICAgICAgICAgICsgZGlhYmV0ZXMgKyBwcmVkaWFiZXRpYywgZGF0YSA9IGZpbmFsLmRhdGEpCnN1bW1hcnkoYm1pLmxvZy5hZ2UuNikKYGBgCgpUaGVyZSBhcmUgc3RpbGwgdmFyaWFibGVzIHdpdGggYSB0LXN0YXRpc3RpYyBsZXNzIHRoYW4gemVybyBzbyB0aGUgYmFja3dhcmRzIHNlbGVjdGlvbiBjb250aW51ZXMgdW50aWwgbm8gbW9yZSB2YXJpYWJsZSB3aXRoIGEgbG93IHQtc3RhdGlzdGljIHJlbWFpbi4KCmBgYHtyfQoKYm1pLmxvZy5hZ2UuNSA9IGxtKChibWkpXi0wLjUgfiBsb2coYWdlKSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyAgSGJBMWNfbGV2ZWwKICAgICAgICAgICAgICAgICArIGRpYWJldGVzLCBkYXRhID0gZmluYWwuZGF0YSkKc3VtbWFyeShibWkubG9nLmFnZS41KQoKIyBPdXRwdXQgc2hvd3MgMSByZW1haW5pbmcgdmFyaWFibGUgd2l0aCBhIHQtc3RhdGlzdGljIHVuZGVyIDEgc28gd2UgY29udGludWUgd2l0aCBiYWNrd2FyZHMgc2VsZWN0aW9uLiAKCmJtaS5sb2cuYWdlLjQgPSBsbSgoYm1pKV4tMC41IH4gbG9nKGFnZSkgKyBoeXBlcnRlbnNpb24gKyBoZWFydF9kaXNlYXNlCiAgICAgICAgICAgICAgICAgKyBkaWFiZXRlcywgZGF0YSA9IGZpbmFsLmRhdGEpCnN1bW1hcnkoYm1pLmxvZy5hZ2UuNCkKYGBgCgpOb3cgdGhhdCBhbGwgdGhlIHZhcmlhYmxlcyBoYXZlIGEgdC1zdGF0aXN0aWMgYWJvdmUgMSwgd2UgY2FuIGNvbnRpbnVlIG9udG8gZGV0ZXJtaW5pbmcgdGhlIGZpbmFsIG1vZGVsLgoKYGBge3J9CgpibWkuY29lZi40IDwtIHN1bW1hcnkoYm1pLmxvZy5hZ2UuNCkkY29lZgprYWJsZShzdW1tYXJ5KGJtaS5sb2cuYWdlLjQpJGNvZWYsIGNhcHRpb24gPSAiSW5mZXJlbnRpYWwgU3RhdGlzdGljcyBvZiBGaW5hbCBNb2RlbCIpCgpgYGAKClRoZSBmaW5hbCBtb2RlbCBpcywKCiQkClx0ZXh0e2JtaX0gXiB7LTAuNX0gPSAwLjI0MjQyNTEgLSAwLjAxMzM0OTlcdGltZXMgXGxvZyhcdGV4dHthZ2V9KSAtIDAuMDAyNzY3M1x0aW1lcyBcdGV4dHtoeXBlcnRlbnNpb259ICsgCjAuMDA0MDIzOVx0aW1lcyBcdGV4dHtoZWFydF9kaXNlYXNlfSAtIDAuMDA3ODM3OVx0aW1lcyBcdGV4dHtkaWFiZXRlc30gCiQkCgojIE1ldGhvZG9sb2d5IGFuZCBBbmFseXNpcwoKIyMgQm9vdHN0cmFwcGluZwoKTm93IHRoYXQgdGhlIGZpbmFsIG1vZGVsIGhhcyBiZWVuIGRldGVybWluZSwgYm9vdHN0cmFwcGluZyBjYW4gYmUgdXNlZCB0byBoZWxwIGZpbmQgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGZvciB0aGUgY29lZmZpY2llbnRzIGluIHRoZSBmaW5hbCByZWdyZXNzaW9uIG1vZGVsLiAKCmBgYHtyfQpCID0gMTAwMCAgICAgICAjIGNob29zZSB0aGUgbnVtYmVyIG9mIGJvb3RzdHJhcCByZXBsaWNhdGVzLgojIyAKbnVtLnAgPSBkaW0obW9kZWwuZnJhbWUoYm1pLmxvZy5hZ2UuNCkpWzJdICAjIHJldHVybnMgbnVtYmVyIG9mIHBhcmFtZXRlcnMgaW4gdGhlIG1vZGVsCnNtcGwubiA9IGRpbShtb2RlbC5mcmFtZShibWkubG9nLmFnZS40KSlbMV0gIyBzYW1wbGUgc2l6ZQojIyB6ZXJvIG1hdHJpeCB0byBzdG9yZSBib290c3RyYXAgY29lZmZpY2llbnRzIApjb2VmLm10cnggPSBtYXRyaXgocmVwKDAsIEIqbnVtLnApLCBuY29sID0gbnVtLnApICAgICAgIAojIyAKZm9yIChpIGluIDE6Qil7CiAgYm9vdGMuaWQgPSBzYW1wbGUoMTpzbXBsLm4sIHNtcGwubiwgcmVwbGFjZSA9IFRSVUUpICMgZml0IGZpbmFsIG1vZGVsIHRvIHRoZSBib290c3RyYXAgc2FtcGxlCiAgYm1pLmxvZy5hZ2UuYm9vdCA9IGxtKChibWkpXi0wLjUgfiBsb2coYWdlKSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UKICAgICAgICAgICAgICAgICArIGRpYWJldGVzLCBkYXRhID0gZmluYWwuZGF0YVtib290Yy5pZCxdKSAgICAgCiAgY29lZi5tdHJ4W2ksXSA9IGNvZWYoYm1pLmxvZy5hZ2UuYm9vdCl9ICAgICMgZXh0cmFjdCBjb2VmcyBmcm9tIGJvb3RzdHJhcCByZWdyZXNzaW9uIG1vZGVsICAgIApgYGAKCiMjIyBIaXN0b2dyYW1zCgpBbiBSIGZ1bmN0aW9uIHdhcyBkZWZpbmVkIHRvIG1ha2UgdGhlIGhpc3RvZ3JhbXMgb2YgdGhlIGJvb3RzdHJhcCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4gVGhpcyBmdW5jdGlvbiBpcyBhbHNvIHVzZWQgbGF0ZXIgb24gdG8gY3JlYXRlIHRoZSBoaXN0b2dyYW1zIGZvciB0aGUgcmVzaWR1YWwgYm9vdHN0cmFwIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4gCgpgYGB7cn0KYm9vdC5oaXN0ID0gZnVuY3Rpb24oYm1pLmNvZWYuNCwgYnQuY29lZi5tdHJ4LCB2YXIuaWQsIHZhci5ubSl7CiAgeDEuMSA8LSBzZXEobWluKGJ0LmNvZWYubXRyeFssdmFyLmlkXSksIG1heChidC5jb2VmLm10cnhbLHZhci5pZF0pLCBsZW5ndGg9MzAwICkKICB5MS4xIDwtIGRub3JtKHgxLjEsIG1lYW4oYnQuY29lZi5tdHJ4Wyx2YXIuaWRdKSwgc2QoYnQuY29lZi5tdHJ4Wyx2YXIuaWRdKSkKICBoaWdoZXN0YmFyID0gbWF4KGhpc3QoYnQuY29lZi5tdHJ4Wyx2YXIuaWRdLCBwbG90ID0gRkFMU0UpJGRlbnNpdHkpIAogIHlsaW1pdCA8LSBtYXgoYyh5MS4xLGhpZ2hlc3RiYXIpKQogIGhpc3QoYnQuY29lZi5tdHJ4Wyx2YXIuaWRdLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSB2YXIubm0sIHhsYWI9IiIsIAogICAgICAgY29sID0gImF6dXJlMSIseWxpbT1jKDAseWxpbWl0KSwgYm9yZGVyPSJsaWdodHNlYWdyZWVuIikKICBsaW5lcyh4ID0geDEuMSwgeSA9IHkxLjEsIGNvbCA9ICJyZWQzIikKICBsaW5lcyhkZW5zaXR5KGJ0LmNvZWYubXRyeFssdmFyLmlkXSwgYWRqdXN0PTIpLCBjb2w9ImJsdWUiKX0KYGBgCgpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIGZpZy53aWR0aD0gOSwgZmlnLmhlaWdodD0gN30KcGFyKG1mcm93PWMoMiwzKSkgICMgaGlzdG9ncmFtcyBvZiBib290c3RyYXAgY29lZnMKYm9vdC5oaXN0KGJ0LmNvZWYubXRyeD1jb2VmLm10cngsIHZhci5pZD0xLCB2YXIubm0gPSJJbnRlcmNlcHQiICkKYm9vdC5oaXN0KGJ0LmNvZWYubXRyeD1jb2VmLm10cngsIHZhci5pZD0yLCB2YXIubm0gPSJMb2cgQWdlIiApCmJvb3QuaGlzdChidC5jb2VmLm10cng9Y29lZi5tdHJ4LCB2YXIuaWQ9MywgdmFyLm5tID0iSHlwZXJ0ZW5zaW9uIiApCmJvb3QuaGlzdChidC5jb2VmLm10cng9Y29lZi5tdHJ4LCB2YXIuaWQ9NCwgdmFyLm5tID0iSGVhcnQgRGlzZWFzZSIgKQpib290Lmhpc3QoYnQuY29lZi5tdHJ4PWNvZWYubXRyeCwgdmFyLmlkPTUsIHZhci5ubSA9IkRpYWJldGVzIiApCmBgYAoKVGhlIGhpc3RvZ3JhbXMgd2VyZSBjcmVhdGVkIHRvIGRlcGljdCB0byBub3JtYWwgZGVuc2l0eSBjdXJ2ZXMgb24gdG9wIG9mIGVhY2ggb2YgdGhlIGhpc3RvZ3JhbXMuIFRoZSBmaXJzdCBjdXJ2ZSBpcyByZWQgYW5kIGl0IHVzZXMgdGhlIGVzdGltYXRlZCByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhbmQgdGhlaXIgc3RhbmRhcmQgZXJyb3IgaW4gdGhlIG91dHB1dCBvZiB0aGUgcmVncmVzc2lvbiBwcm9jZWR1cmUuIFRoZSByZWQgY3VydmUgaXMgdGhlIGJhc2lzIGZvciB0aGUgcmVwb3J0ZWQgcC12YWx1ZXMuIFRoZSBzZWNvbmQgY3VydmUgb24gdGhlIGhpc3RvZ3JhbXMgaXMgYmx1ZS4gVGhlIGJsdWUgY3VydmUgaXMgYSBub24tcGFyYW1ldHJpYyBlc3RpbWF0ZSBvZiB0aGUgZGVuc2l0eSBvZiB0aGUgYm9vdHN0cmFwIHNhbXBsaW5nIGRpc3RyaWJ1dGlvbi4gVGhlIGJsdWUgY3VydmUgaXMgdGhlIGJhc2lzIGZvciB0aGUgcmVwb3J0ZWQgY29uZmlkZW5jZSBpbnRlcnZhbHMuIAoKQWxsIG9mIHRoZSBkZW5zaXR5IGN1cnZlcyBvbiB0aGUgaGlzdG9ncmFtcyBzZWVtIHRvIGJlIHF1aXRlIGNsb3NlLCB3aXRoIHNvbWUgb2YgdGhlbSBldmVuIHNoYXJpbmcgYSBzdWJzdGFudGlhbCBhbW91bnQgb2Ygb3ZlcmxhcC4gTm9uZSBvZiB0aGUgY3VydmVzIGFwcGVhciB0byBzaG93IGFueSBza2V3bmVzcywgdGhleSBhcmUgYWxsIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiAKCiMjIyBDb25maWRlbmNlIEludGVydmFscwoKYGBge3J9Cm51bS5wID0gZGltKGNvZWYubXRyeClbMl0KYm9vdC5jaSA9IE5VTEwKYm9vdC53ZCA9IE5VTEwKZm9yIChpIGluIDE6bnVtLnApewogIGxvd2VyY2kwLjAyNSA9IHJvdW5kKHF1YW50aWxlKGNvZWYubXRyeFssIGldLCAwLjAyNSwgdHlwZSA9IDIpLCA4KQogIHVwcGVyY2kwLjk3NSA9IHJvdW5kKHF1YW50aWxlKGNvZWYubXRyeCBbLCBpXSwgMC45NzUsIHR5cGUgPTIpLCA4KQogIGJvb3Qud2RbaV0gPSB1cHBlcmNpMC45NzUgLSBsb3dlcmNpMC4wMjUKICBib290LmNpW2ldID0gcGFzdGUgKCJbIiwgcm91bmQobG93ZXJjaTAuMDI1LCA0KSwgIiAsICIsIHJvdW5kKHVwcGVyY2kwLjk3NSw0KSwgIl0iKX0KCmthYmxlKGFzLmRhdGEuZnJhbWUoY2JpbmQoZm9ybWF0QyhibWkuY29lZi40LCA0LCBmb3JtYXQgPSAiZiIpLCBib290LmNpLjk1PWJvb3QuY2kpKSwgY2FwdGlvbiA9ICJSZWdyZXNzaW9uIENvZWZmaWNpZW50IE1hdHJpeCBXaXRoIDk1JSBSZXNpZGl1YWwgQm9vdHN0cmFwIENvbmZpZGVuY2UgSW50ZXJ2YWwiKQpgYGAKTG9va2luZyBhdCB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSB2YXJpYWJsZXMsIHdlIHdhbnQgdG8gY29uZmlybSB0aGF0IG5vbmUgb2YgdGhlbSBjb250YWluIHRoZSB2YWx1ZSAwLiBGcm9tIHRoZSBvdXRwdXQsIGl0IHNlZW1zIHRoYXQgbm9uZSBvZiB0aGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHNwYW4gMCBhbmQgdGhleSBhcmUgYWxsIGNvbnNpc3RlbnQuIEFkZGl0aW9uYWxseSwgYWxsIHRoZSBwLXZhbHVlcyBhcmUgc21hbGxlciB0aGFuIDAuMDUuIFRoaXMgb3V0cHV0IGFsaWducyB3aXRoIHdoYXQgaXMgZGVwaWN0ZWQgaW4gdGhlIG5vcm1hbCBkZW5zaXR5IGN1cnZlcyBvbiB0aGUgaGlzdG9ncmFtcyBhYm92ZS4gCgojIyBCb290c3RyYXBwaW5nIFJlc2lkdWFscwoKTm93IHdlIGFyZSBnb2luZyB1c2UgdGhlIGJvb3RzdHJhcCByZXNpZHVhbCBtZXRob2RzIHRvIGNyZWF0ZSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMuIAoKIyMjIFJlc2lkdWFscyAKCgpgYGB7cn0KaGlzdChzb3J0KGJtaS5sb2cuYWdlLjQkcmVzaWR1YWxzKSwKICAgICB4bGFiID0gIlJlc2lkdWFscyIsCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgUmVzaWR1YWxzIikKYGBgCgpUaGUgcmVzaWR1YWwgcGxvdCBzaG93cyB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHJlc2lkdWFscyBoYXMgbm8gb3V0bGluZXMgYW5kIGFwcGVhcnMgdG8gaGF2ZSBhIG5vcm1hbCBkaXN0cmlidXRpb24uIAoKCk5vdyB3ZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBiZWxvdyBjb2RlIHRvIGdlbmVyYXRlIHRoZSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMgb2YgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzCgpgYGB7cn0KZmluYWwuYm1pLm1vZGVsID0gbG0oKGJtaSleLTAuNSB+IGxvZyhhZ2UpICsgaHlwZXJ0ZW5zaW9uICsgaGVhcnRfZGlzZWFzZQogICAgICAgICAgICAgICAgICsgZGlhYmV0ZXMsIGRhdGEgPSBmaW5hbC5kYXRhKQoKbW9kZWwucmVzaWR1YWwgPSBmaW5hbC5ibWkubW9kZWwkcmVzaWR1YWxzCm51bS5wID0gZGltKG1vZGVsLm1hdHJpeChmaW5hbC5ibWkubW9kZWwpKVsyXQpzYW1wbGUubnVtID0gZGltKG1vZGVsLm1hdHJpeChmaW5hbC5ibWkubW9kZWwpKVsxXQpib290Lm1hdHJpeCA9IG1hdHJpeChyZXAoMCw1KkIpLCBuY29sID0gbnVtLnApCgpmb3IgKGkgaW4gMTpCKSB7CiAgYm9vdC5sb2cuYm1pID0gZmluYWwuYm1pLm1vZGVsJGZpdHRlZC52YWx1ZXMgKwogIHNhbXBsZShmaW5hbC5ibWkubW9kZWwkcmVzaWR1YWxzLCBzYW1wbGUubnVtLCByZXBsYWNlID0gVFJVRSkKICBmaW5hbC5kYXRhJGJvb3QubG9nLmJtaSA9IGJvb3QubG9nLmJtaQogIGJvb3RyLm1vZGVsID0gbG0oYm9vdC5sb2cuYm1pIH4gbG9nKGFnZSkgKyBoeXBlcnRlbnNpb24gKyBoZWFydF9kaXNlYXNlCiAgICAgICAgICAgICAgICAgKyBkaWFiZXRlcywgZGF0YSA9IGZpbmFsLmRhdGEpICAgCiAgYm9vdC5tYXRyaXhbaSwgXSA9IGJvb3RyLm1vZGVsJGNvZWZmaWNpZW50c30KYGBgCgoKCiMjIyBIaXN0b2dyYW1zCgpOb3cgdXNpbmcgdGhlIGZ1bmN0aW9uIHRoYXQgd2FzIG1lbnRpb25lZCBlYXJsaWVyIHdlIGFyZSBnb2luZyB0byBtYWtlIHRoZSBoaXN0b2dyYW1zIG9mIHRoZSByZXNpZHVhbCBib290c3RyYXAgZXN0aW1hdGVzIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4gCgpgYGB7cn0KYm9vdC5yZXMuaGlzdCA9IGZ1bmN0aW9uKGJ0LnJlcy5tdHJ4LCB2YXIuaWQsIHZhci5ubSl7CiAgeDEuMSA8LSBzZXEobWluKGJ0LnJlcy5tdHJ4Wyx2YXIuaWRdKSwgbWF4KGJ0LnJlcy5tdHJ4Wyx2YXIuaWRdKSwgbGVuZ3RoPTMwMCApCiAgeTEuMSA8LSBkbm9ybSh4MS4xLCBtZWFuKGJ0LnJlcy5tdHJ4Wyx2YXIuaWRdKSwgc2QoYnQucmVzLm10cnhbLHZhci5pZF0pKQogIGhpZ2hlc3RiYXIgPSBtYXgoaGlzdChidC5yZXMubXRyeFssdmFyLmlkXSwgcGxvdCA9IEZBTFNFKSRkZW5zaXR5KSAKICB5bGltaXQgPC0gbWF4KGMoeTEuMSxoaWdoZXN0YmFyKSkKICBoaXN0KGJ0LnJlcy5tdHJ4Wyx2YXIuaWRdLCBwcm9iYWJpbGl0eSA9IFRSVUUsIG1haW4gPSB2YXIubm0sIHhsYWI9IiIsIAogICAgICAgY29sID0gImF6dXJlMSIseWxpbT1jKDAseWxpbWl0KSwgYm9yZGVyPSJsaWdodHNlYWdyZWVuIikKICBsaW5lcyh4ID0geDEuMSwgeSA9IHkxLjEsIGNvbCA9ICJyZWQzIikKICBsaW5lcyhkZW5zaXR5KGJ0LnJlcy5tdHJ4Wyx2YXIuaWRdLCBhZGp1c3Q9MiksIGNvbD0iYmx1ZSIpfQpgYGAKCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLndpZHRoPSA5LCBmaWcuaGVpZ2h0PSA3fQpwYXIobWZyb3c9YygyLDMpKSAgIyBoaXN0b2dyYW1zIG9mIGJvb3RzdHJhcCByZXNpZHVhbHMKYm9vdC5yZXMuaGlzdChidC5yZXMubXRyeD1ib290Lm1hdHJpeCwgdmFyLmlkPTEsIHZhci5ubSA9IkludGVyY2VwdCIgKQpib290LnJlcy5oaXN0KGJ0LnJlcy5tdHJ4PWJvb3QubWF0cml4LCB2YXIuaWQ9MiwgdmFyLm5tID0iTG9nIEFnZSIgKQpib290LnJlcy5oaXN0KGJ0LnJlcy5tdHJ4PWJvb3QubWF0cml4LCB2YXIuaWQ9MywgdmFyLm5tID0iSHlwZXJ0ZW5zaW9uIiApCmJvb3QucmVzLmhpc3QoYnQucmVzLm10cng9Ym9vdC5tYXRyaXgsIHZhci5pZD00LCB2YXIubm0gPSJIZWFydCBEaXNlYXNlIiApCmJvb3QucmVzLmhpc3QoYnQucmVzLm10cng9Ym9vdC5tYXRyaXgsIHZhci5pZD01LCB2YXIubm0gPSJEaWFiZXRlcyIgKQpgYGAKClRoZSBjdXJ2ZXMgb24gYWxsIHRoZSBoaXN0b2dyYW1zIHNlZW0gdG8gYmUgdmVyeSBjbG9zZSBhbmQgaGF2ZSBnb29kIG92ZXJsYXAgc2ltaWxhciB0byB0aGUgZWFybGllciBoaXN0b2dyYW1zLiBUaGVyZSBkb2VzIHNlZW1zIHRvIGJlIHNvbWUgc2xpZ2h0IHNrZXduZXNzIGZyb20gdGhlIGNlbnRlciBmb3IgYm90aCB0aGUgSW50ZXJjZXB0IGFuZCBMb2cgQWdlIGhpc3RvZ3JhbXMsIGhvd2V2ZXIgdGhlIHNrZXduZXNzIHNlZW1zIHRvIGJlIHZlcnkgc2xpZ2h0IGFuZCBub3RoaW5nIHRvIGJlIGV4dHJlbWVseSBjb25jZXJuZWQgd2l0aC4gVGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHNob3VsZCB5aWVsZCBzaW1pbGFyIGluZm9ybWF0aW9uIHRvIHRoZSBoaXN0b2dyYW1zIHNvIHdlIGNhbiBjb25maXJtIHRoYXQgdGhlIHNrZXduZXNzIGlzIG1pbm9yIHdoZW4gYW5hbHl6aW5nIHRoZSBjb25maWRlbmNlIGludGVydmFscy4gVGhlIDk1JSByZXNpZHVhbCBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMgYXJlIGdpdmVuIGJlbG93LAoKIyMjIENvbmZpZGVuY2UgSW50ZXJ2YWxzCgpgYGB7cn0KbnVtLnAgPSBkaW0oYm9vdC5tYXRyaXgpWzJdCmJvb3Quci5jaSA9IE5VTEwKYm9vdC5yLndkID0gTlVMTApmb3IgKGkgaW4gMTpudW0ucCl7CiAgbG93ZXJjaTAuMDI1LnJlcyA9IHJvdW5kKHF1YW50aWxlKGJvb3QubWF0cml4WywgaV0sIDAuMDI1LCB0eXBlID0gMiksIDgpCiAgdXBwZXJjaTAuOTc1LnJlcyA9IHJvdW5kKHF1YW50aWxlKGJvb3QubWF0cml4IFssIGldLCAwLjk3NSwgdHlwZSA9MiksIDgpCiAgYm9vdC5yLndkW2ldID0gdXBwZXJjaTAuOTc1LnJlcyAtIGxvd2VyY2kwLjAyNS5yZXMKICBib290LnIuY2lbaV0gPSBwYXN0ZSAoIlsiLCByb3VuZChsb3dlcmNpMC4wMjUucmVzLCA0KSwgIiAsICIsIHJvdW5kKHVwcGVyY2kwLjk3NS5yZXMsNCksICJdIil9CgprYWJsZShhcy5kYXRhLmZyYW1lKGNiaW5kKGZvcm1hdEMoYm1pLmNvZWYuNCwgNCwgZm9ybWF0ID0gImYiKSwgYm9vdC5yLmNpLjk1PWJvb3Quci5jaSkpLCBjYXB0aW9uID0gIlJlZ3Jlc3Npb24gQ29lZmZpY2llbnQgTWF0cmljIikKYGBgClRoZSByZXNpZHVhbCBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMgeWllbGQgdGhlIHNhbWUgcmVzdWx0cyBhcyB0aGUgcC12YWx1ZXMuIFRoaXMgaXMgdG8gYmUgZXhwZWN0ZWQgY29uc2lkZXJpbmcgdGhlIHNpZ25pZmljYW50bHkgbGFyZ2Ugc2FtcGxlIHNpemUuIFRoZSBsYXJnZSBzYW1wbGUgc2l6ZSBhbGxvd3MgZm9yIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGVzdGltYXRlZCBjb2VmZmljaWVudHMgdG8gaGF2ZSBhIGdvb2QgYXBwcm94aW1hdGlvbiBvZiB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbnMuIAoKIyBSZXN1bHRzIGFuZCBDb25jbHVzaW9ucwoKIyMgSW5mZXJlbnRpYWwgU3RhdGlzdGljcwoKYGBgIHtyfQprYWJsZShhcy5kYXRhLmZyYW1lKGNiaW5kKGZvcm1hdEMoYm1pLmNvZWYuNFssIC0zXSwgNCwgZm9ybWF0ID0iZiIpLCBidGMuY3QuOTUgPSBib290LmNpLCBidHIuY2kuOTUgPSBib290LnIuY2kpKSwKICAgICAgY2FwdGlvbiA9ICJGaW5hbCBDb21iaW5lZCBJbmZlcmVudGlhbCBTdGF0aXN0aWNzIHdpdGggcC12YWx1ZXMgYW5kIEJvb3RzdHJhcCBDb25maWRlbmNlIEludGVydmFscyIpCmBgYAoKTG9va2luZyBhdCB0aGUgYWJvdmUgb3V0cHV0IGl0IGlzIGNsZWFyIHRoYXQgdGhlIHRocmVlIG1ldGhvZHMgeWllbGQgdGhlIHNhbWUgcmVzdWx0cywgd2hpY2ggaXMgdGhlIHNpZ25pZmljYW5jZSBvZiB0aGUgaW5kaXZpZHVhbCBleHBsYW5hdG9yeSB2YXJpYWJsZXMuIFRoaXMgbWFrZXMgc2Vuc2UgY29uc2lkZXJpbmcgdGhhdCB0aGUgZmluYWwgbW9kZWwgCiQkIFx0ZXh0e2JtaX0gPSAwLjI0MjQyNTEgLSAwLjAxMzM0OTlcdGltZXMgXGxvZyhcdGV4dHthZ2V9KSAtIDAuMDAyNzY3M1x0aW1lcyBcdGV4dHtoeXBlcnRlbnNpb259ICsgCjAuMDA0MDIzOVx0aW1lcyBcdGV4dHtoZWFydF9kaXNlYXNlfSAtIDAuMDA3ODM3OVx0aW1lcyBcdGV4dHtkaWFiZXRlc30gJCQKZG9lcyBub3QgaGF2ZSBzZXJpb3VzIHZpb2xhdGlvbnMgb2YgdGhlIG1vZGVsIGFzc3VtcHRpb25zLiAKCmBgYCB7cn0Ka2FibGUocm91bmQoY2JpbmQoYm9vdC53ZCwgYm9vdC5yLndkKSwgNCksIGNhcHRpb24gPSAiV2lkdGhzIG9mIGJvdGggQm9vdHN0cmFwIENvbmZpZGVuY2UgSW50ZXJ2YWxzIikKYGBgCgpUaGUgYWJvdmUgdGFibGUgc2hvd3MgdGhhdCB0aGUgd2lkdGhzIG9mIHRoZSByZXNpZHVhbCBhbmQgY2FzZSBib290c3RyYXAgY29uZmlkZW5jZSBpbnRlcnZhbHMgYXJlIGVpdGhlciB2ZXJ5IHNpbWlsYXIgb3IgZXhhY3RseSB0aGUgc2FtZSBhcyBlYWNoIG90aGVyLiAKCmBgYCB7cn0Ka2FibGUoYm1pLmNvZWYuNCwgY2FwdGlvbiA9ICJJbmZlcmVudGlhbCBTdGF0aXN0aWNzIG9mIEZpbmFsIE1vZGVsIikKYGBgCgpUaGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgZXhwbGFpbiB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgYWRqdXN0ZWQgYm1pIGFuZCB0aGUgY29ycmVzcG9uZGluZyB2YXJpYWJsZXMsIHdpdGggbG9nKGFnZSkgaW5zdGVhZCBvZiBhZ2UuIFRha2luZyBpbnRvIGNvbnNpZGVyYXRpb24gdGhhdCBib3RoIHRoZSBjb25maWRlbmNlIGludGVydmFscyBhbmQgdGhlIHAtdmFsdWVzIGRlcGljdGVkIHRoYXQgdGhlc2UgdmFyaWFibGVzIGhhZCBhIHNpZ25pZmljYW50IGFiaWxpdHkgdG8gZGV0ZXJtaW5lIHRoZSByZXNwb25zZSB2YXJpYWJsZSB0aGF0IHdhcyBub3QgMC4gV2UgY2FuIGRldGVybWluZSB0aGF0IHRoaXMgbW9kZWwgY2FuIGJlIHVzZWQgdG8gaGVscCBwcmVkaWN0IGEgcGF0aWVudHMgZXhwZWN0ZWQgYm1pIGJhc2VkIG9uIHRoZWlyIG90aGVyIGZhY3RvcnMgbGlrZSBoeXBlcnRlbnNpb24sIGFuZCBhZ2UuIAoKQWRkaXRpb25hbGx5LCB3ZSBjYW4gZXhwbGFpbiB0aGUgZXN0aW1hdGVkIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgb2YgLTAuMDA3ODM3OQlhcyBmb2xsb3dzLCBzaW5jZSBkaWFiZXRlcyBpcyBhIGJpbmFyeSB2YXJpYWJsZSBpdCBjYW4gb25seSBiZSBleHByZXNzZWQgYXMgMCBvciAxLiBEdWUgdG8gdGhlIHRyYW5zZm9ybWF0aW9uIG9uIEJNSSBkdXJpbmcgbW9kZWwgYnVpbGRpbmcgdGhlIHZhcmlhYmxlIGRpYWJldGVzIHdvdWxkIGJlIGV4cHJlc3NlZCBhcyBmb2xsb3dzLCAKCiQkCkRpYWJldGVzXzFeey0wLjV9IC0gRGlhYmV0ZXNfMF57LTAuNX0gPSAtMC4wMDc4Mzc5CiQkClNpbmNlLCAxIHJhaXNlZCB0byBhbnkgcG93ZXIgd2lsbCBhbHdheXMgYmUgMSwgYW5kIDAgcmFpc2VkIHRvIC0wLjUgaXMgdW5kZWZpbmVkIGl0IHNpbXBsaWZpZXMgdG8gdGhlIGZvbGxvd2luZy4gCgokJApEaWFiZXRlc18xICA9IC0wLjAwNzgzNzkKJCQKVXNpbmcgdGhpcyBtb2RlbCwgcGF0aWVudHMgd2l0aCBkaWFiZXRlcyB3aXRoIHNlZSBhIDAuNzglIGRlY3JlYXNlIGluIHRoZSBibWkgb2YgdGhlIHBhdGllbnQuIFRoZSBvdGhlciBiaW5hcnkgdmFyaWFibGVzIGluIHRoZSBtb2RlbCBjYW4gYmUgaW50ZXJwcmV0ZWQgc2ltaWxhcmx5LiAKCiMgR2VuZXJhbCBEaXNjdXNzaW9uCgpXaGlsZSB0aGUgbW9kZWwgZGVwaWN0ZWQgc2V2ZXJhbCBzaWduaWZpY2FudCBwcmVkaWN0aXZlIHZhcmlhYmxlcyBmb3IgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGluIHRoZSBjb250ZXh0IG9mIHRoaXMgZGF0YSBzZXQgaXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCB3aGlsZSB0aGUgdmFyaWFibGVzIGFyZSBzaWduaWZpY2FudCB0aGVpciBpbXBhY3Qgb24gdGhlIHZhbHVlIG9mIGJtaSBpdHNlbGYgaXMgcmF0aGVyIHNtYWxsLiBVbmZvcnR1bmF0ZWx5IHRoZXJlIGlzIHRoZSBtaXNjb25jZXB0aW9uIHdoZW4gaXQgY29tZXMgdG8gdGhlIG1lZGljYWwgZmllbGQgdGhhdCBldmVyeW9uZSB3aG8gaGFzIGlzc3VlcyBsaWtlIGRpYWJldGVzLCBoeXBlcnRlbnNpb24sIG9yIGhlYXJ0IGRpc2Vhc2UgbXVzdCBiZSBvdmVyd2VpZ2h0IGFuZCBhbGwgdGhlaXIgcHJvYmxlbXMgY29tZSBmcm9tIGJlaW5nIG92ZXJ3ZWlnaHQuIEhvd2V2ZXIsIHRoaXMgbW9kZWwgZGVwaWN0cyBvbmx5IGEgc21hbGwgY2hhbmdlIHRvIHRoZSBwcmVkaWN0ZWQgYm1pIGlmIGEgcGF0aWVudCBoYXMgZGlhYmV0ZXMsIGh5cGVydGVuc2lvbiwgb3IgaGVhcnQgZGlzZWFzZSBvciBub3QuIFdoaWxlIHRoZSBBbWVyaWNhbiBEaWFiZXRlcyBBc3NvY2lhdGlvbiBkb2VzIG5vdGUgdGhhdCBoYXZpbmcgYSBoaWdoZXIgYm1pIGRvZXMgcHV0IHBhdGllbnRzIGF0IHJpc2sgZm9yIGhhdmluZyBkaWFiZXRlcywgYW55b25lIGNhbiBiZWNvbWUgZGlhYmV0aWMsIHJlZ2FyZGxlc3Mgb2YgdGhlaXIgd2VpZ2h0LiBBbnkgbWlzY29uY2VwdGlvbnMgb24gYSBleGNsdXNpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gYm1pIGFuZCBkaWFiZXRpYyBzdGF0dXMgYXJlIHNpbXBseSB0aGF0LCBtaXNjb25jZXB0aW9ucy4KCkFsc28sIGl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhpcyBkYXRhIHNldCBpcyBpbmNyZWRpYmx5IGxhcmdlIHdpdGggb3ZlciAxMDAsMDAwIG9ic2VydmF0aW9ucy4gTGFyZ2UgZGF0YSBzZXRzIG1ha2UgaXQgdmVyeSBlYXN5IHRvIGdldCBwLXZhbHVlcyB0aGF0IGFyZSBjbG9zZSB0byBpZiBub3QgemVyby4gSWRlYWxseSwgaXQgd291bGQgYmUgYmV0dGVyIHRvIHVzZSBhIHNtYWxsZXIgZGF0YSBzZXQgd2hlbiBhbmFseXppbmcgdGhlIHZhcmlhYmxlcyBmb3IgdGhlIGZpbmFsIG1vZGVsIHRvIHNlZSB3aGF0IHRoZSBwLXZhbHVlcyBsb29rIGxpa2Ugd2hlbiB0aGV5IGFyZSBub3QgcHVzaGVkIHRvIHplcm8gYnkgYSBsYXJnZSBzYW1wbGUgc2l6ZS4gCgpIb3dldmVyLCB0aGUgbW9kZWwgd2FzIG5vdCBkZXRlcm1pbmVkIHNvbGVseSBvbiB0aGUgcC12YWx1ZSwgYW5kIHRoZXJlIHdhcyBhZGRpdGlvbmFsIHRlc3RpbmcgdGhhdCB0b29rIHBsYWNlLiBBZGRpdGlvbmFsbHksIHdoaWxlIGV0aGljYWxseSB1c2luZyBmYWN0b3JzIGxpa2UgaHlwZXJ0ZW5zaW9uLCBoZWFydCBkaXNlYXNlIGFuZCBkaWFiZXRlcyBzaG91bGQgbm90IGJlIHRoZSBtYWluIGFzcGVjdHMgb2YgdHJ5aW5nIHRvIGRldGVybWluZSBhIHBhdGllbnRzIGJtaS4gRHVlIHRvIHRoZSBmYWN0IHRoYXQgaXMgd291bGQgbW9zdCBsaWtlbHkgZnVydGhlciB0aGUgY29ubm90YXRpb24gdGhhdCBvbmx5IHBlb3BsZSB3aXRoIHRob3NlIGFpbG1lbnRzIGhhdmUgaGlnaCBibWkncywgdGhlIG1vZGVsIGNvdWxkIHRlY2huaWNhbGx5IGJlIHVzZWQgaW4gdGhhdCB3YXkuIAoKIyBSZWZlcmVuY2VzCgpBbWVyaWNhbiBEaWFiZXRlcyBBc3NvY2lhdGlvbi4gKDIwMjMpLiBVbmRlcnN0YW5kaW5nIERpYWJldGVzIERpYWdub3Npcy4gRGlhYmV0ZXMub3JnLiBodHRwczovL2RpYWJldGVzLm9yZy9hYm91dC1kaWFiZXRlcy9kaWFnbm9zaXMKCkFtZXJpY2FuIERpYWJldGVzIEFzc29jaWF0aW9uLiBFeHRyYSBXZWlnaHQsIEV4dHJhIFJpc2sgIERpYWJldGVzLm9yZy4gaHR0cHM6Ly9kaWFiZXRlcy5vcmcvaGVhbHRoLXdlbGxuZXNzL3dlaWdodC1tYW5hZ2VtZW50L2V4dHJhLXdlaWdodC1leHRyYS1yaXNrCgoK