url = "https://raw.githubusercontent.com/ChloeWinters79/STA321/refs/heads/main/Data/diabetes_prediction_dataset.csv"

bmi.diabetes = read.csv(url, header = TRUE)

1 Introduction

This assignment is going to look into implementing various different model-building techniques in order to find the best model.

2 Materials

2.1 Description of Data Set

This data set, Diabetes Prediction, is a combination of both demographic and medical data from over 100,000 patients including their diabetic status, which indicates whether or not the patient has diabetes but not what type of diabetes the patient has. The data set on diabetes prediction contains the following variables,

gender: chr

age: num

hypertension: int

heart_disease: int

smoking_history: chr

bmi: num

HbA1c_level: num

blood_glucose_level: int

diabetes: int

However, while this data set was created with the original intention of helping predict whether or not a patient has diabetes, it will have a different purpose in this assignment. Instead, we will be looking at if variables like blood_glucose_level, heart_disease, diabetes, hypertension, and HbA1C_level can be used to predict a patients bmi.

2.2 Exploratory Data Analysis

HbA1c <- bmi.diabetes$HbA1c_level

Diabetic <- bmi.diabetes$diabetes

BloodGlucose <- bmi.diabetes$blood_glucose_level

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

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

Using these parameters the variable prediabetic was created to look at patients who had both HbA1c_ and blood glucose levels that fell outside the normal range but were not diagnosed diabetics.

prediabetic = (HbA1c > 5.69)  & (BloodGlucose > 99) & (Diabetic < 1 )

bmi.diabetes$prediabetic = as.character(prediabetic)
final.data = bmi.diabetes[, -c(1,5)]

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

3 Methodology and Analysis

3.1 Model and Diagnostics

full.model = lm(bmi ~ ., data = final.data)
kable(summary(full.model)$coef, caption ="Statistics of Regression Coefficients")
Statistics of Regression Coefficients
Estimate Std. Error t value Pr(>|t|)
(Intercept) 23.3978892 0.1742104 134.3082107 0.0000000
age 0.0881169 0.0009342 94.3215337 0.0000000
hypertension 1.2788508 0.0775352 16.4938182 0.0000000
heart_disease -1.2847612 0.1040464 -12.3479645 0.0000000
HbA1c_level -0.0176640 0.0274817 -0.6427553 0.5203844
blood_glucose_level 0.0000913 0.0005897 0.1548745 0.8769206
diabetes 3.2022017 0.1124902 28.4664906 0.0000000
prediabeticTRUE -0.0005028 0.0601540 -0.0083593 0.9933303
par(mfrow=c(2,2))
plot(full.model)

Looking at the residual plots above there are some violations present. First, the variance of the residuals on the Residuals vs Fitted plot are not constant. Looking at the Q-Q plot, it clearly diverts off from a normal distribution. Additionally, there is some curvature present on the residual plot.

vif(full.model)
##                 age        hypertension       heart_disease         HbA1c_level 
##            1.160913            1.092179            1.075466            2.271378 
## blood_glucose_level            diabetes         prediabetic 
##            1.511673            2.582034            2.300982

Since none of the variables depict a variance inflation factor (VIF) above 4 there does not appear to be any significant multicollinearity issues. We can use a bar plot to further confirm this information.

barplot(vif(full.model), main = "Diabetes Prediction VIF Values", horiz = FALSE)

The bar plot also depicts no variables with a VIF above 4, which further confirms that there are no significant multicollinearity issues.

3.2 Box-Cox Transformation

par(pty = "s", mfrow = c(2, 2), oma=c(.1,.1,.1,.1), mar=c(4, 0, 2, 0))
##
boxcox(bmi ~ age + hypertension + heart_disease +  HbA1c_level
       + log(blood_glucose_level) + diabetes + prediabetic, data = final.data, lambda = seq(-1, 1, length = 10), 
       xlab=expression(paste(lambda, ": log blood_glucose_level")))
##
boxcox(bmi ~ age + hypertension + heart_disease + HbA1c_level  
       + blood_glucose_level + diabetes + prediabetic, data = final.data, lambda = seq(-1, 1, length = 10), 
       xlab=expression(paste(lambda, ": blood_glucose_level")))
##
boxcox(bmi ~ age + hypertension + heart_disease +  log(HbA1c_level)  
       + blood_glucose_level + diabetes + prediabetic, data = final.data, lambda = seq(-1, 1, length = 10), 
       xlab=expression(paste(lambda, ": log HbA1C_level")))
##
boxcox(bmi ~ age + hypertension + heart_disease +  log(HbA1c_level)  
       + log(blood_glucose_level) + diabetes + prediabetic, data = final.data, lambda = seq(-1, 1, length = 10), 
      xlab=expression(paste(lambda, ": log HbA1C_Levels, log blood_glucose_level")))

##
boxcox(bmi ~ log(age) + hypertension + heart_disease +  HbA1c_level 
       + blood_glucose_level + diabetes + prediabetic, data = final.data, lambda = seq(-1.5, 1, length = 10), 
      xlab=expression(paste(lambda, ": log age")))
##
boxcox(bmi ~ log(age) + hypertension + heart_disease +  log(HbA1c_level)  
       + blood_glucose_level + diabetes + prediabetic, data = final.data, lambda = seq(-1.5, 1, length = 10), 
      xlab=expression(paste(lambda, ": log age, log HbA1c_level")))

##
boxcox(bmi ~ log(age) + hypertension + heart_disease +  HbA1c_level  
       + log(blood_glucose_level) + diabetes + prediabetic, data = final.data, lambda = seq(-1.5, 1, length = 10), 
      xlab=expression(paste(lambda, ": log age, log blood_glucose_level")))

## 

boxcox(bmi ~ log(age) + hypertension + heart_disease +  log(HbA1c_level)  
       + log(blood_glucose_level) + diabetes + prediabetic, data = final.data, lambda = seq(-1.5, 1, length = 10), 
      xlab=expression(paste(lambda, ": log age, log HbA1c_level, log blood_glucose_level")))

The Box-cox transformation plots are used to determine the optimal under different transformed variables. The log transformation on age impacts the coefficient of the power transformation .

3.3 Square-root Transformation

Now it is time to depict the Box-Cox transformation with log-transformed age on the following model,

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 = "Age Log-Transformed Model")
Age Log-Transformed 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
par(mfrow = c(2,2))
plot(bmi.log.age)

After the log-transformation there is some clear improvements to the residual diagnostic plots. The Q-Q plots depicts a significant improvement from the previous graph, but still has room to improve. Additionally the Residuals vs Fitted plot depicts more constant and equal variances, and it appears that the weak curvature has flattened out.

In an effort to no have any violation with the assumption of normality, we can take a log transformation of bmi and build a model based on log bmi.

log.bmi = lm(log(bmi) ~ age + hypertension + heart_disease +  HbA1c_level
       + blood_glucose_level + diabetes + prediabetic, data = final.data)

kable(summary(log.bmi)$coef, caption = "BMI Log-Transformed Model")
BMI Log-Transformed Model
Estimate Std. Error t value Pr(>|t|)
(Intercept) 3.1078109 0.0061400 506.1547793 0.0000000
age 0.0039233 0.0000329 119.1524965 0.0000000
hypertension 0.0353802 0.0027327 12.9468457 0.0000000
heart_disease -0.0508245 0.0036671 -13.8595347 0.0000000
HbA1c_level -0.0003969 0.0009686 -0.4097767 0.6819707
blood_glucose_level 0.0000068 0.0000208 0.3252589 0.7449858
diabetes 0.0934044 0.0039647 23.5589130 0.0000000
prediabeticTRUE -0.0005694 0.0021201 -0.2685502 0.7882764
par(mfrow = c(2,2))
plot(log.bmi)

The residual diagnostic plots for the log(bmi) model do not depict a significant improvement for the assumption of normality, and brings back some of the weak curvature from the original models. This means that none of the three models statisfy the assumption of normality.

#define plotting area
par(pty = "s", mfrow = c(1, 3))
#Q-Q plot for original model
qqnorm(full.model$residuals, main = "Full-Model")
qqline(full.model$residuals)
#Q-Q plot for Box-Cox transformed model
qqnorm(log.bmi$residuals, main = "Log BMI")
qqline(log.bmi$residuals)
#display both Q-Q plots
qqnorm(bmi.log.age$residuals, main = "BMI log Age")
qqline(bmi.log.age$residuals)

3.4 Goodness-of-Fit

select=function(m){ # m is an object: model
 e = m$resid                           # residuals
 n0 = length(e)                        # sample size
 SSE=(m$df)*(summary(m)$sigma)^2       # sum of squared error
 R.sq=summary(m)$r.squared             # Coefficient of determination: R square!
 R.adj=summary(m)$adj.r                # Adjusted R square
 MSE=(summary(m)$sigma)^2              # square error
 Cp=(SSE/MSE)-(n0-2*(n0-m$df))         # Mellow's p
 AIC=n0*log(SSE)-n0*log(n0)+2*(n0-m$df)          # Akaike information criterion
 SBC=n0*log(SSE)-n0*log(n0)+(log(n0))*(n0-m$df)  # Schwarz Bayesian Information criterion
 X=model.matrix(m)                     # design matrix of the model
 H=X%*%solve(t(X)%*%X)%*%t(X)          # hat matrix
 d=e/(1-diag(H))                       
 PRESS=t(d)%*%d   # predicted residual error sum of squares (PRESS)- a cross-validation measure
 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
 }

Note: Due to the size of the data set RStudio was unable to fun the code on Goodness of Fit could not run without crashing RStudio or exhausting the memory vector. To remedy this issue, a random sample of 10,000 observations was taken from the data set and used for the the following code. Anything using this random sample data set is depicted by ending in .rand. Due to this change in the data set, the Q-Q plots were reprinted to make sure this smaller sample of the data was still an accurate representation of the larger data set.

final.data.rand <- final.data[sample(nrow(final.data), size = 10000, replace = FALSE), ]
full.model.rand = lm(bmi ~ ., data = final.data.rand)
log.bmi.rand = lm(log(bmi) ~ age + hypertension + heart_disease +  HbA1c_level
       + blood_glucose_level + diabetes + prediabetic, data = final.data.rand)
bmi.log.age.rand = lm((bmi)^-0.5 ~ log(age) + hypertension + heart_disease +  HbA1c_level
       + blood_glucose_level + diabetes + prediabetic, data = final.data.rand)
par(pty = "s", mfrow = c(1, 3))
#Q-Q plot for original model
qqnorm(full.model.rand$residuals, main = "Full-Model")
qqline(full.model.rand$residuals)
#Q-Q plot for Box-Cox transformed model
qqnorm(log.bmi.rand$residuals, main = "Log BMI")
qqline(log.bmi$residuals)
#display both Q-Q plots
qqnorm(bmi.log.age.rand$residuals, main = "BMI log Age")
qqline(bmi.log.age$residuals)

The Q-Q plots for the 10,000 observation data set is similar to the Q-Q plots for the 100,000 observation data set. Now, it is possible to move forward with the Goodness-of-Fit tests.

output.sum = rbind(select(full.model.rand), select(bmi.log.age.rand), select(log.bmi.rand))
row.names(output.sum) = c("full.model.rand", "bmi.log.age.rand", "log.bmi.rand")
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.rand 3.907917e+05 0.1286410 0.1280306 8 36671.90 36729.58 3.914916e+05
bmi.log.age.rand 3.934383e+00 0.3061495 0.3056634 8 -78389.86 -78332.18 3.940886e+00
log.bmi.rand 4.810988e+02 0.1668803 0.1662967 8 -30326.68 -30269.00 4.819005e+02

Looking at the outputs for \(R^2, R^2_{adj}\), and \(C_p\) it is clear that the second model, bmi.log.age.rand is the best of the three models. This means the second model will be the final model.

3.5 Final Model

kable(summary(bmi.log.age)$coef, caption = "Inferential Statistics on the Final Model")
Inferential Statistics on the Final 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

Due the data set has over 100,000 observations the Central Limit Theorem can be used as the argument for validating the p-values. The variables log(age), hypertension, heart_disease, and diabetes all depict p-values close to zero meaning their coefficients are signifcantly differnt from 0. However, the variables HbA1c_level, blood_glucose_level, and prediabetic have p-values much higher than 0 which means these variables are not statistically significant when it comes to the prediction of bmi.

4 Results and Conclusion

4.1 Summary of Model

The final model can be written as follows,

bmi = 0.2422 - 0.01335 x log(age) - 0.002768 x hypertension + 0.004024 x heart_disease - 0.007911 x diabetes

The variables log(age), hpyertension and diabetes all have a negative association with bmi while heart_disease is the only variable with a positive association.

5 General Discussion

Several regression techniques, including the Box-cox transformation was used to determine the final model in this study. While the final data set consists of 8 variables, 3 of them were not statistically significant and thus were not used in the final model. All of the models that were compared using the goodness-of-fit measure model criteria consisted of the same 8 variables.

However, due to the size of the original final data set being significantly large, with over 100,000 observations, a smaller random sample of the final data set, consisting of 10,000 observations was created to run the goodness-of-fit measure in a way that did not shutdown RStudio. Additionally, the goodness-of-fit measure was used due to the fact that the violation to the normality assumption on the residuals. This normality assumption remains uncorrected even after several transformation techniques were used on the data set. The central limit theorem (CLT) was used to base inferences on the regression coefficients.

The final model used the log(age) Box-cox transformation with a -0.5 adjustment on bmi. This model was selected over the full model and the log(bmi) model because even though none of the models met the normality assumption the log(age) model had the best \(R^2, R^2_{adj}\), and \(C_p\) outputs after running the goodness-of-fit measure on all three models. Additionally, from the selected final model, three variables were removed from the original eight due to them having non-significant p-values. After removing those variables the final model was complete.

6 References

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

LS0tCnRpdGxlOiAnU1RBMzIxOiBXZWVrICMwNCBBc3NpZ25tZW50JwphdXRob3I6ICdDaGxvZSBXaW50ZXJzJwpkYXRlOiAiOS8yMi8yMDI0IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICBmaWdfd2lkdGg6IDYKICAgIGZpZ19oZWlnaHQ6IDQKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBsdW1lbgogIHBkZl9kb2N1bWVudDogCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogIHdvcmRfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnNCcKLS0tCmBgYHs9aHRtbH0KCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CgovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8KCmgxLnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovCiAgZm9udC1zaXplOiAyNHB4OwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBmb250LWZhbWlseTogIkdpbGwgU2FucyIsIHNhbnMtc2VyaWY7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLwogIGZvbnQtc2l6ZTogMjBweDsKICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogIGNvbG9yOiBEYXJrUmVkOwogIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8KICBmb250LXNpemU6IDE4cHg7CiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsKICBjb2xvcjogRGFya0JsdWU7CiAgdGV4dC1hbGlnbjogY2VudGVyOwp9CmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDIycHg7CiAgICBmb250LWZhbWlseTogc3lzdGVtLXVpOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8KICAgIGZvbnQtc2l6ZTogMjBweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IG5hdnk7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9CgpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovCiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLwogICAgZm9udC1zaXplOiAxOHB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmJvZHkgeyBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyB9CgouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQoKcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0KCjwvc3R5bGU+CmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCAKIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuCmxpYnJhcnkoa25pdHIpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZ3MgPSBGQUxTRSwgICAgICAgIyBzb21ldGltZXMsIHlvdSBjb2RlIG1heSBwcm9kdWNlIHdhcm5pbmcgbWVzc2FnZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHlvdSBjYW4gY2hvb3NlIHRvIGluY2x1ZGUgdGhlIHdhcm5pbmcgbWVzc2FnZXMgaW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiAKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFICAgICAgICAgICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbiB0aGUgb3V0cHV0IGZpbGUuCiAgICAgICAgICAgICAgICAgICAgICApICAgCgpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KCJzY2FsZXMiKSAKCmBgYAoKCgpgYGB7cn0KdXJsID0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9DaGxvZVdpbnRlcnM3OS9TVEEzMjEvcmVmcy9oZWFkcy9tYWluL0RhdGEvZGlhYmV0ZXNfcHJlZGljdGlvbl9kYXRhc2V0LmNzdiIKCmJtaS5kaWFiZXRlcyA9IHJlYWQuY3N2KHVybCwgaGVhZGVyID0gVFJVRSkKCmBgYAoKCiMgSW50cm9kdWN0aW9uCgpUaGlzIGFzc2lnbm1lbnQgaXMgZ29pbmcgdG8gbG9vayBpbnRvIGltcGxlbWVudGluZyB2YXJpb3VzIGRpZmZlcmVudCBtb2RlbC1idWlsZGluZyB0ZWNobmlxdWVzIGluIG9yZGVyIHRvIGZpbmQgdGhlIGJlc3QgbW9kZWwuIAoKCiMgTWF0ZXJpYWxzIAoKIyMgRGVzY3JpcHRpb24gb2YgRGF0YSBTZXQKClRoaXMgZGF0YSBzZXQsIERpYWJldGVzIFByZWRpY3Rpb24sIGlzIGEgY29tYmluYXRpb24gb2YgYm90aCBkZW1vZ3JhcGhpYyBhbmQgbWVkaWNhbCBkYXRhIGZyb20gb3ZlciAxMDAsMDAwIHBhdGllbnRzIGluY2x1ZGluZyB0aGVpciBkaWFiZXRpYyBzdGF0dXMsIHdoaWNoIGluZGljYXRlcyB3aGV0aGVyIG9yIG5vdCB0aGUgcGF0aWVudCBoYXMgZGlhYmV0ZXMgYnV0IG5vdCB3aGF0IHR5cGUgb2YgZGlhYmV0ZXMgdGhlIHBhdGllbnQgaGFzLiBUaGUgZGF0YSBzZXQgb24gZGlhYmV0ZXMgcHJlZGljdGlvbiBjb250YWlucyB0aGUgZm9sbG93aW5nIHZhcmlhYmxlcywgCgpnZW5kZXI6IGNocgoKYWdlOiBudW0KCmh5cGVydGVuc2lvbjogaW50CgpoZWFydF9kaXNlYXNlOiBpbnQKCnNtb2tpbmdfaGlzdG9yeTogY2hyCgpibWk6IG51bQoKSGJBMWNfbGV2ZWw6IG51bQoKYmxvb2RfZ2x1Y29zZV9sZXZlbDogaW50CgpkaWFiZXRlczogaW50CgpIb3dldmVyLCB3aGlsZSB0aGlzIGRhdGEgc2V0IHdhcyBjcmVhdGVkIHdpdGggdGhlIG9yaWdpbmFsIGludGVudGlvbiBvZiBoZWxwaW5nIHByZWRpY3Qgd2hldGhlciBvciBub3QgYSBwYXRpZW50IGhhcyBkaWFiZXRlcywgaXQgd2lsbCBoYXZlIGEgZGlmZmVyZW50IHB1cnBvc2UgaW4gdGhpcyBhc3NpZ25tZW50LiBJbnN0ZWFkLCB3ZSB3aWxsIGJlIGxvb2tpbmcgYXQgaWYgdmFyaWFibGVzIGxpa2UgYmxvb2RfZ2x1Y29zZV9sZXZlbCwgaGVhcnRfZGlzZWFzZSwgZGlhYmV0ZXMsIGh5cGVydGVuc2lvbiwgYW5kIEhiQTFDX2xldmVsIGNhbiBiZSB1c2VkIHRvIHByZWRpY3QgYSBwYXRpZW50cyBibWkuIAoKIyMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcwoKYGBge3J9CkhiQTFjIDwtIGJtaS5kaWFiZXRlcyRIYkExY19sZXZlbAoKRGlhYmV0aWMgPC0gYm1pLmRpYWJldGVzJGRpYWJldGVzCgpCbG9vZEdsdWNvc2UgPC0gYm1pLmRpYWJldGVzJGJsb29kX2dsdWNvc2VfbGV2ZWwKCmBgYAoKVXNpbmcgdGhlIHZhcmlhYmxlcywgSGJBMWNfbGV2ZWwsIGJsb29kX2dsdWNvc2VfbGV2ZWwsIGFuZCBkaWFiZXRlcyB0byBkZWZpbmUgdGhlIHZhcmlhYmxlIHByZWRpYWJldGljIGFzIGZvbGxvd3MsIHByZWRpYWJldGljID0gVFJVRSBpZiBIYkExY19sZXZlbCA+IDUuNjksICBibG9vZF9nbHVjb3NlX2xldmVsID4gOTkgJiBkaWFiZXRlcyA8IDE7IHByZWRpYWJldGljID0gRkFMU0Ugb3RoZXJ3aXNlLiAKCkFjY29yZGluZyB0byB0aGUgQW1lcmljYW4gRGlhYmV0ZXMgQXNzb2NpYXRpb24gKEFEQSksIGEgbm9ybWFsIEhiQTFjIGxldmVsIGlzIHVuZGVyIDUuNywgd2hpbGUgcHJlZGlhYmV0aWMgaXMgYmV0d2VlbiA1LjcgYW5kIDYuNSwgYW5kIGFueXRoaW5nIGFib3ZlIDYuNSBpcyBjb25zaWRlcmVkIGRpYWJldGljLiBBZGRpdGlvbmFsbHksIHRoZSBBREEgZmluZHMgYmxvb2QgZ2x1Y29zZSBsZXZlbHMgdW5kZXIgMTAwIHRvIGJlIHdpdGhpbmcgdGhlIG5vcm1hbCByYW5nZSwgbGV2ZWxzIGZyb20gMTAwIHRvIDEyNSBhcyBwcmVkaWFiZXRpYywgYW5kIGFueXRoaW5nIDEyNiBvciBvdmVyIGFzIGRpYWJldGljLiAKClVzaW5nIHRoZXNlIHBhcmFtZXRlcnMgdGhlIHZhcmlhYmxlIHByZWRpYWJldGljIHdhcyBjcmVhdGVkIHRvIGxvb2sgYXQgcGF0aWVudHMgd2hvIGhhZCBib3RoIEhiQTFjXyBhbmQgYmxvb2QgZ2x1Y29zZSBsZXZlbHMgdGhhdCBmZWxsIG91dHNpZGUgdGhlIG5vcm1hbCByYW5nZSBidXQgd2VyZSBub3QgZGlhZ25vc2VkIGRpYWJldGljcy4gCgpgYGB7cn0KCnByZWRpYWJldGljID0gKEhiQTFjID4gNS42OSkgICYgKEJsb29kR2x1Y29zZSA+IDk5KSAmIChEaWFiZXRpYyA8IDEgKQoKYm1pLmRpYWJldGVzJHByZWRpYWJldGljID0gYXMuY2hhcmFjdGVyKHByZWRpYWJldGljKQoKYGBgCgpgYGB7cn0KZmluYWwuZGF0YSA9IGJtaS5kaWFiZXRlc1ssIC1jKDEsNSldCgprYWJsZShoZWFkKGZpbmFsLmRhdGEpKQpgYGAKCiMgTWV0aG9kb2xvZ3kgYW5kIEFuYWx5c2lzIAoKIyMgTW9kZWwgYW5kIERpYWdub3N0aWNzCgpgYGB7cn0KZnVsbC5tb2RlbCA9IGxtKGJtaSB+IC4sIGRhdGEgPSBmaW5hbC5kYXRhKQprYWJsZShzdW1tYXJ5KGZ1bGwubW9kZWwpJGNvZWYsIGNhcHRpb24gPSJTdGF0aXN0aWNzIG9mIFJlZ3Jlc3Npb24gQ29lZmZpY2llbnRzIikKYGBgCmBgYCB7cn0KCnBhcihtZnJvdz1jKDIsMikpCnBsb3QoZnVsbC5tb2RlbCkKCmBgYAogCkxvb2tpbmcgYXQgdGhlIHJlc2lkdWFsIHBsb3RzIGFib3ZlIHRoZXJlIGFyZSBzb21lIHZpb2xhdGlvbnMgcHJlc2VudC4gRmlyc3QsIHRoZSB2YXJpYW5jZSBvZiB0aGUgcmVzaWR1YWxzIG9uIHRoZSBSZXNpZHVhbHMgdnMgRml0dGVkIHBsb3QgYXJlIG5vdCBjb25zdGFudC4gTG9va2luZyBhdCB0aGUgUS1RIHBsb3QsIGl0IGNsZWFybHkgZGl2ZXJ0cyBvZmYgZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24uIEFkZGl0aW9uYWxseSwgdGhlcmUgaXMgc29tZSBjdXJ2YXR1cmUgcHJlc2VudCBvbiB0aGUgcmVzaWR1YWwgcGxvdC4gCiAKCmBgYHtyfQoKdmlmKGZ1bGwubW9kZWwpCgpgYGAKClNpbmNlIG5vbmUgb2YgdGhlIHZhcmlhYmxlcyBkZXBpY3QgYSB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9yIChWSUYpIGFib3ZlIDQgdGhlcmUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGFueSBzaWduaWZpY2FudCBtdWx0aWNvbGxpbmVhcml0eSBpc3N1ZXMuIFdlIGNhbiB1c2UgYSBiYXIgcGxvdCB0byBmdXJ0aGVyIGNvbmZpcm0gdGhpcyBpbmZvcm1hdGlvbi4KCmBgYCB7cn0KCmJhcnBsb3QodmlmKGZ1bGwubW9kZWwpLCBtYWluID0gIkRpYWJldGVzIFByZWRpY3Rpb24gVklGIFZhbHVlcyIsIGhvcml6ID0gRkFMU0UpCgpgYGAKClRoZSBiYXIgcGxvdCBhbHNvIGRlcGljdHMgbm8gdmFyaWFibGVzIHdpdGggYSBWSUYgYWJvdmUgNCwgd2hpY2ggZnVydGhlciBjb25maXJtcyB0aGF0IHRoZXJlIGFyZSBubyBzaWduaWZpY2FudCBtdWx0aWNvbGxpbmVhcml0eSBpc3N1ZXMuIAoKIyMgQm94LUNveCBUcmFuc2Zvcm1hdGlvbgoKYGBgIHtyfQpwYXIocHR5ID0gInMiLCBtZnJvdyA9IGMoMiwgMiksIG9tYT1jKC4xLC4xLC4xLC4xKSwgbWFyPWMoNCwgMCwgMiwgMCkpCiMjCmJveGNveChibWkgfiBhZ2UgKyBoeXBlcnRlbnNpb24gKyBoZWFydF9kaXNlYXNlICsgIEhiQTFjX2xldmVsCiAgICAgICArIGxvZyhibG9vZF9nbHVjb3NlX2xldmVsKSArIGRpYWJldGVzICsgcHJlZGlhYmV0aWMsIGRhdGEgPSBmaW5hbC5kYXRhLCBsYW1iZGEgPSBzZXEoLTEsIDEsIGxlbmd0aCA9IDEwKSwgCiAgICAgICB4bGFiPWV4cHJlc3Npb24ocGFzdGUobGFtYmRhLCAiOiBsb2cgYmxvb2RfZ2x1Y29zZV9sZXZlbCIpKSkKIyMKYm94Y294KGJtaSB+IGFnZSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyBIYkExY19sZXZlbCAgCiAgICAgICArIGJsb29kX2dsdWNvc2VfbGV2ZWwgKyBkaWFiZXRlcyArIHByZWRpYWJldGljLCBkYXRhID0gZmluYWwuZGF0YSwgbGFtYmRhID0gc2VxKC0xLCAxLCBsZW5ndGggPSAxMCksIAogICAgICAgeGxhYj1leHByZXNzaW9uKHBhc3RlKGxhbWJkYSwgIjogYmxvb2RfZ2x1Y29zZV9sZXZlbCIpKSkKIyMKYm94Y294KGJtaSB+IGFnZSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyAgbG9nKEhiQTFjX2xldmVsKSAgCiAgICAgICArIGJsb29kX2dsdWNvc2VfbGV2ZWwgKyBkaWFiZXRlcyArIHByZWRpYWJldGljLCBkYXRhID0gZmluYWwuZGF0YSwgbGFtYmRhID0gc2VxKC0xLCAxLCBsZW5ndGggPSAxMCksIAogICAgICAgeGxhYj1leHByZXNzaW9uKHBhc3RlKGxhbWJkYSwgIjogbG9nIEhiQTFDX2xldmVsIikpKQojIwpib3hjb3goYm1pIH4gYWdlICsgaHlwZXJ0ZW5zaW9uICsgaGVhcnRfZGlzZWFzZSArICBsb2coSGJBMWNfbGV2ZWwpICAKICAgICAgICsgbG9nKGJsb29kX2dsdWNvc2VfbGV2ZWwpICsgZGlhYmV0ZXMgKyBwcmVkaWFiZXRpYywgZGF0YSA9IGZpbmFsLmRhdGEsIGxhbWJkYSA9IHNlcSgtMSwgMSwgbGVuZ3RoID0gMTApLCAKICAgICAgeGxhYj1leHByZXNzaW9uKHBhc3RlKGxhbWJkYSwgIjogbG9nIEhiQTFDX0xldmVscywgbG9nIGJsb29kX2dsdWNvc2VfbGV2ZWwiKSkpCiMjCmJveGNveChibWkgfiBsb2coYWdlKSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyAgSGJBMWNfbGV2ZWwgCiAgICAgICArIGJsb29kX2dsdWNvc2VfbGV2ZWwgKyBkaWFiZXRlcyArIHByZWRpYWJldGljLCBkYXRhID0gZmluYWwuZGF0YSwgbGFtYmRhID0gc2VxKC0xLjUsIDEsIGxlbmd0aCA9IDEwKSwgCiAgICAgIHhsYWI9ZXhwcmVzc2lvbihwYXN0ZShsYW1iZGEsICI6IGxvZyBhZ2UiKSkpCiMjCmJveGNveChibWkgfiBsb2coYWdlKSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyAgbG9nKEhiQTFjX2xldmVsKSAgCiAgICAgICArIGJsb29kX2dsdWNvc2VfbGV2ZWwgKyBkaWFiZXRlcyArIHByZWRpYWJldGljLCBkYXRhID0gZmluYWwuZGF0YSwgbGFtYmRhID0gc2VxKC0xLjUsIDEsIGxlbmd0aCA9IDEwKSwgCiAgICAgIHhsYWI9ZXhwcmVzc2lvbihwYXN0ZShsYW1iZGEsICI6IGxvZyBhZ2UsIGxvZyBIYkExY19sZXZlbCIpKSkKCiMjCmJveGNveChibWkgfiBsb2coYWdlKSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyAgSGJBMWNfbGV2ZWwgIAogICAgICAgKyBsb2coYmxvb2RfZ2x1Y29zZV9sZXZlbCkgKyBkaWFiZXRlcyArIHByZWRpYWJldGljLCBkYXRhID0gZmluYWwuZGF0YSwgbGFtYmRhID0gc2VxKC0xLjUsIDEsIGxlbmd0aCA9IDEwKSwgCiAgICAgIHhsYWI9ZXhwcmVzc2lvbihwYXN0ZShsYW1iZGEsICI6IGxvZyBhZ2UsIGxvZyBibG9vZF9nbHVjb3NlX2xldmVsIikpKQoKIyMgCgpib3hjb3goYm1pIH4gbG9nKGFnZSkgKyBoeXBlcnRlbnNpb24gKyBoZWFydF9kaXNlYXNlICsgIGxvZyhIYkExY19sZXZlbCkgIAogICAgICAgKyBsb2coYmxvb2RfZ2x1Y29zZV9sZXZlbCkgKyBkaWFiZXRlcyArIHByZWRpYWJldGljLCBkYXRhID0gZmluYWwuZGF0YSwgbGFtYmRhID0gc2VxKC0xLjUsIDEsIGxlbmd0aCA9IDEwKSwgCiAgICAgIHhsYWI9ZXhwcmVzc2lvbihwYXN0ZShsYW1iZGEsICI6IGxvZyBhZ2UsIGxvZyBIYkExY19sZXZlbCwgbG9nIGJsb29kX2dsdWNvc2VfbGV2ZWwiKSkpCgpgYGAKClRoZSBCb3gtY294IHRyYW5zZm9ybWF0aW9uIHBsb3RzIGFyZSB1c2VkIHRvIGRldGVybWluZSB0aGUgb3B0aW1hbCBcbGFtZGEgdW5kZXIgZGlmZmVyZW50IHRyYW5zZm9ybWVkIHZhcmlhYmxlcy4gVGhlIGxvZyB0cmFuc2Zvcm1hdGlvbiBvbiBhZ2UgaW1wYWN0cyB0aGUgY29lZmZpY2llbnQgb2YgdGhlIHBvd2VyIHRyYW5zZm9ybWF0aW9uIFxsYW1kYS4gCgojIyBTcXVhcmUtcm9vdCBUcmFuc2Zvcm1hdGlvbgoKTm93IGl0IGlzIHRpbWUgdG8gZGVwaWN0IHRoZSBCb3gtQ294IHRyYW5zZm9ybWF0aW9uIHdpdGggbG9nLXRyYW5zZm9ybWVkIGFnZSBvbiB0aGUgZm9sbG93aW5nIG1vZGVsLCAKCmBgYHtyfQoKYm1pLmxvZy5hZ2UgPSBsbSgoYm1pKV4tMC41IH4gbG9nKGFnZSkgKyBoeXBlcnRlbnNpb24gKyBoZWFydF9kaXNlYXNlICsgIEhiQTFjX2xldmVsCiAgICAgICArIGJsb29kX2dsdWNvc2VfbGV2ZWwgKyBkaWFiZXRlcyArIHByZWRpYWJldGljLCBkYXRhID0gZmluYWwuZGF0YSkKa2FibGUoc3VtbWFyeShibWkubG9nLmFnZSkkY29lZiwgY2FwdGlvbiA9ICJBZ2UgTG9nLVRyYW5zZm9ybWVkIE1vZGVsIikKCmBgYAoKYGBge3J9CnBhcihtZnJvdyA9IGMoMiwyKSkKcGxvdChibWkubG9nLmFnZSkKCmBgYAoKCkFmdGVyIHRoZSBsb2ctdHJhbnNmb3JtYXRpb24gdGhlcmUgaXMgc29tZSBjbGVhciBpbXByb3ZlbWVudHMgdG8gdGhlIHJlc2lkdWFsIGRpYWdub3N0aWMgcGxvdHMuIFRoZSBRLVEgcGxvdHMgZGVwaWN0cyBhIHNpZ25pZmljYW50IGltcHJvdmVtZW50IGZyb20gdGhlIHByZXZpb3VzIGdyYXBoLCBidXQgc3RpbGwgaGFzIHJvb20gdG8gaW1wcm92ZS4gQWRkaXRpb25hbGx5IHRoZSBSZXNpZHVhbHMgdnMgRml0dGVkIHBsb3QgZGVwaWN0cyBtb3JlIGNvbnN0YW50IGFuZCBlcXVhbCB2YXJpYW5jZXMsIGFuZCBpdCBhcHBlYXJzIHRoYXQgdGhlIHdlYWsgY3VydmF0dXJlIGhhcyBmbGF0dGVuZWQgb3V0LgoKSW4gYW4gZWZmb3J0IHRvIG5vIGhhdmUgYW55IHZpb2xhdGlvbiB3aXRoIHRoZSBhc3N1bXB0aW9uIG9mIG5vcm1hbGl0eSwgd2UgY2FuIHRha2UgYSBsb2cgdHJhbnNmb3JtYXRpb24gb2YgYm1pIGFuZCBidWlsZCBhIG1vZGVsIGJhc2VkIG9uIGxvZyBibWkuIAoKYGBge3J9CmxvZy5ibWkgPSBsbShsb2coYm1pKSB+IGFnZSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyAgSGJBMWNfbGV2ZWwKICAgICAgICsgYmxvb2RfZ2x1Y29zZV9sZXZlbCArIGRpYWJldGVzICsgcHJlZGlhYmV0aWMsIGRhdGEgPSBmaW5hbC5kYXRhKQoKa2FibGUoc3VtbWFyeShsb2cuYm1pKSRjb2VmLCBjYXB0aW9uID0gIkJNSSBMb2ctVHJhbnNmb3JtZWQgTW9kZWwiKQoKYGBgCmBgYHtyfQpwYXIobWZyb3cgPSBjKDIsMikpCnBsb3QobG9nLmJtaSkKCmBgYAoKClRoZSByZXNpZHVhbCBkaWFnbm9zdGljIHBsb3RzIGZvciB0aGUgbG9nKGJtaSkgbW9kZWwgZG8gbm90IGRlcGljdCBhIHNpZ25pZmljYW50IGltcHJvdmVtZW50IGZvciB0aGUgYXNzdW1wdGlvbiBvZiBub3JtYWxpdHksIGFuZCBicmluZ3MgYmFjayBzb21lIG9mIHRoZSB3ZWFrIGN1cnZhdHVyZSBmcm9tIHRoZSBvcmlnaW5hbCBtb2RlbHMuIFRoaXMgbWVhbnMgdGhhdCBub25lIG9mIHRoZSB0aHJlZSBtb2RlbHMgc3RhdGlzZnkgdGhlIGFzc3VtcHRpb24gb2Ygbm9ybWFsaXR5LiAKCgpgYGB7cn0KI2RlZmluZSBwbG90dGluZyBhcmVhCnBhcihwdHkgPSAicyIsIG1mcm93ID0gYygxLCAzKSkKI1EtUSBwbG90IGZvciBvcmlnaW5hbCBtb2RlbApxcW5vcm0oZnVsbC5tb2RlbCRyZXNpZHVhbHMsIG1haW4gPSAiRnVsbC1Nb2RlbCIpCnFxbGluZShmdWxsLm1vZGVsJHJlc2lkdWFscykKI1EtUSBwbG90IGZvciBCb3gtQ294IHRyYW5zZm9ybWVkIG1vZGVsCnFxbm9ybShsb2cuYm1pJHJlc2lkdWFscywgbWFpbiA9ICJMb2cgQk1JIikKcXFsaW5lKGxvZy5ibWkkcmVzaWR1YWxzKQojZGlzcGxheSBib3RoIFEtUSBwbG90cwpxcW5vcm0oYm1pLmxvZy5hZ2UkcmVzaWR1YWxzLCBtYWluID0gIkJNSSBsb2cgQWdlIikKcXFsaW5lKGJtaS5sb2cuYWdlJHJlc2lkdWFscykKYGBgCgoKIyMgR29vZG5lc3Mtb2YtRml0CgpgYGB7cn0Kc2VsZWN0PWZ1bmN0aW9uKG0peyAjIG0gaXMgYW4gb2JqZWN0OiBtb2RlbAogZSA9IG0kcmVzaWQgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHJlc2lkdWFscwogbjAgPSBsZW5ndGgoZSkgICAgICAgICAgICAgICAgICAgICAgICAjIHNhbXBsZSBzaXplCiBTU0U9KG0kZGYpKihzdW1tYXJ5KG0pJHNpZ21hKV4yICAgICAgICMgc3VtIG9mIHNxdWFyZWQgZXJyb3IKIFIuc3E9c3VtbWFyeShtKSRyLnNxdWFyZWQgICAgICAgICAgICAgIyBDb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uOiBSIHNxdWFyZSEKIFIuYWRqPXN1bW1hcnkobSkkYWRqLnIgICAgICAgICAgICAgICAgIyBBZGp1c3RlZCBSIHNxdWFyZQogTVNFPShzdW1tYXJ5KG0pJHNpZ21hKV4yICAgICAgICAgICAgICAjIHNxdWFyZSBlcnJvcgogQ3A9KFNTRS9NU0UpLShuMC0yKihuMC1tJGRmKSkgICAgICAgICAjIE1lbGxvdydzIHAKIEFJQz1uMCpsb2coU1NFKS1uMCpsb2cobjApKzIqKG4wLW0kZGYpICAgICAgICAgICMgQWthaWtlIGluZm9ybWF0aW9uIGNyaXRlcmlvbgogU0JDPW4wKmxvZyhTU0UpLW4wKmxvZyhuMCkrKGxvZyhuMCkpKihuMC1tJGRmKSAgIyBTY2h3YXJ6IEJheWVzaWFuIEluZm9ybWF0aW9uIGNyaXRlcmlvbgogWD1tb2RlbC5tYXRyaXgobSkgICAgICAgICAgICAgICAgICAgICAjIGRlc2lnbiBtYXRyaXggb2YgdGhlIG1vZGVsCiBIPVglKiVzb2x2ZSh0KFgpJSolWCklKiV0KFgpICAgICAgICAgICMgaGF0IG1hdHJpeAogZD1lLygxLWRpYWcoSCkpICAgICAgICAgICAgICAgICAgICAgICAKIFBSRVNTPXQoZCklKiVkICAgIyBwcmVkaWN0ZWQgcmVzaWR1YWwgZXJyb3Igc3VtIG9mIHNxdWFyZXMgKFBSRVNTKS0gYSBjcm9zcy12YWxpZGF0aW9uIG1lYXN1cmUKIHRibCA9IGFzLmRhdGEuZnJhbWUoY2JpbmQoU1NFPVNTRSwgUi5zcT1SLnNxLCBSLmFkaiA9IFIuYWRqLCBDcCA9IENwLCBBSUMgPSBBSUMsIFNCQyA9IFNCQywgUFJEID0gUFJFU1MpKQogbmFtZXModGJsKT1jKCJTU0UiLCAiUi5zcSIsICJSLmFkaiIsICJDcCIsICJBSUMiLCAiU0JDIiwgIlBSRVNTIikKIHRibAogfQoKYGBgCgoKTm90ZTogRHVlIHRvIHRoZSBzaXplIG9mIHRoZSBkYXRhIHNldCBSU3R1ZGlvIHdhcyB1bmFibGUgdG8gZnVuIHRoZSBjb2RlIG9uIEdvb2RuZXNzIG9mIEZpdCBjb3VsZCBub3QgcnVuIHdpdGhvdXQgY3Jhc2hpbmcgUlN0dWRpbyBvciBleGhhdXN0aW5nIHRoZSBtZW1vcnkgdmVjdG9yLiBUbyByZW1lZHkgdGhpcyBpc3N1ZSwgYSByYW5kb20gc2FtcGxlIG9mIDEwLDAwMCBvYnNlcnZhdGlvbnMgd2FzIHRha2VuIGZyb20gdGhlIGRhdGEgc2V0IGFuZCB1c2VkIGZvciB0aGUgdGhlIGZvbGxvd2luZyBjb2RlLiBBbnl0aGluZyB1c2luZyB0aGlzIHJhbmRvbSBzYW1wbGUgZGF0YSBzZXQgaXMgZGVwaWN0ZWQgYnkgZW5kaW5nIGluIC5yYW5kLiBEdWUgdG8gdGhpcyBjaGFuZ2UgaW4gdGhlIGRhdGEgc2V0LCB0aGUgUS1RIHBsb3RzIHdlcmUgcmVwcmludGVkIHRvIG1ha2Ugc3VyZSB0aGlzIHNtYWxsZXIgc2FtcGxlIG9mIHRoZSBkYXRhIHdhcyBzdGlsbCBhbiBhY2N1cmF0ZSByZXByZXNlbnRhdGlvbiBvZiB0aGUgbGFyZ2VyIGRhdGEgc2V0LgoKYGBge3J9CgpmaW5hbC5kYXRhLnJhbmQgPC0gZmluYWwuZGF0YVtzYW1wbGUobnJvdyhmaW5hbC5kYXRhKSwgc2l6ZSA9IDEwMDAwLCByZXBsYWNlID0gRkFMU0UpLCBdCmZ1bGwubW9kZWwucmFuZCA9IGxtKGJtaSB+IC4sIGRhdGEgPSBmaW5hbC5kYXRhLnJhbmQpCmxvZy5ibWkucmFuZCA9IGxtKGxvZyhibWkpIH4gYWdlICsgaHlwZXJ0ZW5zaW9uICsgaGVhcnRfZGlzZWFzZSArICBIYkExY19sZXZlbAogICAgICAgKyBibG9vZF9nbHVjb3NlX2xldmVsICsgZGlhYmV0ZXMgKyBwcmVkaWFiZXRpYywgZGF0YSA9IGZpbmFsLmRhdGEucmFuZCkKYm1pLmxvZy5hZ2UucmFuZCA9IGxtKChibWkpXi0wLjUgfiBsb2coYWdlKSArIGh5cGVydGVuc2lvbiArIGhlYXJ0X2Rpc2Vhc2UgKyAgSGJBMWNfbGV2ZWwKICAgICAgICsgYmxvb2RfZ2x1Y29zZV9sZXZlbCArIGRpYWJldGVzICsgcHJlZGlhYmV0aWMsIGRhdGEgPSBmaW5hbC5kYXRhLnJhbmQpCgpgYGAKCgpgYGB7cn0KCnBhcihwdHkgPSAicyIsIG1mcm93ID0gYygxLCAzKSkKI1EtUSBwbG90IGZvciBvcmlnaW5hbCBtb2RlbApxcW5vcm0oZnVsbC5tb2RlbC5yYW5kJHJlc2lkdWFscywgbWFpbiA9ICJGdWxsLU1vZGVsIikKcXFsaW5lKGZ1bGwubW9kZWwucmFuZCRyZXNpZHVhbHMpCiNRLVEgcGxvdCBmb3IgQm94LUNveCB0cmFuc2Zvcm1lZCBtb2RlbApxcW5vcm0obG9nLmJtaS5yYW5kJHJlc2lkdWFscywgbWFpbiA9ICJMb2cgQk1JIikKcXFsaW5lKGxvZy5ibWkkcmVzaWR1YWxzKQojZGlzcGxheSBib3RoIFEtUSBwbG90cwpxcW5vcm0oYm1pLmxvZy5hZ2UucmFuZCRyZXNpZHVhbHMsIG1haW4gPSAiQk1JIGxvZyBBZ2UiKQpxcWxpbmUoYm1pLmxvZy5hZ2UkcmVzaWR1YWxzKQpgYGAKClRoZSBRLVEgcGxvdHMgZm9yIHRoZSAxMCwwMDAgb2JzZXJ2YXRpb24gZGF0YSBzZXQgaXMgc2ltaWxhciB0byB0aGUgUS1RIHBsb3RzIGZvciB0aGUgMTAwLDAwMCBvYnNlcnZhdGlvbiBkYXRhIHNldC4gTm93LCBpdCBpcyBwb3NzaWJsZSB0byBtb3ZlIGZvcndhcmQgd2l0aCB0aGUgR29vZG5lc3Mtb2YtRml0IHRlc3RzLgoKCgpgYGAge3J9Cm91dHB1dC5zdW0gPSByYmluZChzZWxlY3QoZnVsbC5tb2RlbC5yYW5kKSwgc2VsZWN0KGJtaS5sb2cuYWdlLnJhbmQpLCBzZWxlY3QobG9nLmJtaS5yYW5kKSkKcm93Lm5hbWVzKG91dHB1dC5zdW0pID0gYygiZnVsbC5tb2RlbC5yYW5kIiwgImJtaS5sb2cuYWdlLnJhbmQiLCAibG9nLmJtaS5yYW5kIikKa2FibGUob3V0cHV0LnN1bSwgY2FwdGlvbiA9ICJHb29kbmVzcy1vZi1maXQgTWVhc3VyZXMgb2YgQ2FuZGlkYXRlIE1vZGVscyIpCmBgYAoKTG9va2luZyBhdCB0aGUgb3V0cHV0cyBmb3IgKiokUl4yLCBSXjJfe2Fkan0kLCBhbmQgJENfcCQqKiBpdCBpcyBjbGVhciB0aGF0IHRoZSBzZWNvbmQgbW9kZWwsIGJtaS5sb2cuYWdlLnJhbmQgaXMgdGhlIGJlc3Qgb2YgdGhlIHRocmVlIG1vZGVscy4gVGhpcyBtZWFucyB0aGUgc2Vjb25kIG1vZGVsIHdpbGwgYmUgdGhlIGZpbmFsIG1vZGVsLiAKCiMjIEZpbmFsIE1vZGVsCgpgYGAge3J9CgprYWJsZShzdW1tYXJ5KGJtaS5sb2cuYWdlKSRjb2VmLCBjYXB0aW9uID0gIkluZmVyZW50aWFsIFN0YXRpc3RpY3Mgb24gdGhlIEZpbmFsIE1vZGVsIikKCmBgYAoKRHVlIHRoZSBkYXRhIHNldCBoYXMgb3ZlciAxMDAsMDAwIG9ic2VydmF0aW9ucyB0aGUgQ2VudHJhbCBMaW1pdCBUaGVvcmVtIGNhbiBiZSB1c2VkIGFzIHRoZSBhcmd1bWVudCBmb3IgdmFsaWRhdGluZyB0aGUgcC12YWx1ZXMuIFRoZSB2YXJpYWJsZXMgbG9nKGFnZSksIGh5cGVydGVuc2lvbiwgaGVhcnRfZGlzZWFzZSwgYW5kIGRpYWJldGVzIGFsbCBkZXBpY3QgcC12YWx1ZXMgY2xvc2UgdG8gemVybyBtZWFuaW5nIHRoZWlyIGNvZWZmaWNpZW50cyBhcmUgc2lnbmlmY2FudGx5IGRpZmZlcm50IGZyb20gMC4gSG93ZXZlciwgdGhlIHZhcmlhYmxlcyBIYkExY19sZXZlbCwgYmxvb2RfZ2x1Y29zZV9sZXZlbCwgYW5kIHByZWRpYWJldGljIGhhdmUgcC12YWx1ZXMgbXVjaCBoaWdoZXIgdGhhbiAwIHdoaWNoIG1lYW5zIHRoZXNlIHZhcmlhYmxlcyBhcmUgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgd2hlbiBpdCBjb21lcyB0byB0aGUgcHJlZGljdGlvbiBvZiBibWkuICAKCgojIFJlc3VsdHMgYW5kIENvbmNsdXNpb24KCiMjIFN1bW1hcnkgb2YgTW9kZWwgCgpUaGUgZmluYWwgbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXMgZm9sbG93cywgCgpibWkgPSAwLjI0MjIgLSAwLjAxMzM1IHggbG9nKGFnZSkgLSAwLjAwMjc2OCB4IGh5cGVydGVuc2lvbiArIDAuMDA0MDI0IHggaGVhcnRfZGlzZWFzZSAtIDAuMDA3OTExIHggZGlhYmV0ZXMKClRoZSB2YXJpYWJsZXMgbG9nKGFnZSksIGhweWVydGVuc2lvbiBhbmQgZGlhYmV0ZXMgYWxsIGhhdmUgYSBuZWdhdGl2ZSBhc3NvY2lhdGlvbiB3aXRoIGJtaSB3aGlsZSBoZWFydF9kaXNlYXNlIGlzIHRoZSBvbmx5IHZhcmlhYmxlIHdpdGggYSBwb3NpdGl2ZSBhc3NvY2lhdGlvbi4gCgojIEdlbmVyYWwgRGlzY3Vzc2lvbiAKClNldmVyYWwgcmVncmVzc2lvbiB0ZWNobmlxdWVzLCBpbmNsdWRpbmcgdGhlIEJveC1jb3ggdHJhbnNmb3JtYXRpb24gd2FzIHVzZWQgdG8gZGV0ZXJtaW5lIHRoZSBmaW5hbCBtb2RlbCBpbiB0aGlzIHN0dWR5LiBXaGlsZSB0aGUgZmluYWwgZGF0YSBzZXQgY29uc2lzdHMgb2YgOCB2YXJpYWJsZXMsIDMgb2YgdGhlbSB3ZXJlIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGFuZCB0aHVzIHdlcmUgbm90IHVzZWQgaW4gdGhlIGZpbmFsIG1vZGVsLiBBbGwgb2YgdGhlIG1vZGVscyB0aGF0IHdlcmUgY29tcGFyZWQgdXNpbmcgdGhlIGdvb2RuZXNzLW9mLWZpdCBtZWFzdXJlIG1vZGVsIGNyaXRlcmlhIGNvbnNpc3RlZCBvZiB0aGUgc2FtZSA4IHZhcmlhYmxlcy4gCgpIb3dldmVyLCBkdWUgdG8gdGhlIHNpemUgb2YgdGhlIG9yaWdpbmFsIGZpbmFsIGRhdGEgc2V0IGJlaW5nIHNpZ25pZmljYW50bHkgbGFyZ2UsIHdpdGggb3ZlciAxMDAsMDAwIG9ic2VydmF0aW9ucywgYSBzbWFsbGVyIHJhbmRvbSBzYW1wbGUgb2YgdGhlIGZpbmFsIGRhdGEgc2V0LCBjb25zaXN0aW5nIG9mIDEwLDAwMCBvYnNlcnZhdGlvbnMgd2FzIGNyZWF0ZWQgdG8gcnVuIHRoZSBnb29kbmVzcy1vZi1maXQgbWVhc3VyZSBpbiBhIHdheSB0aGF0IGRpZCBub3Qgc2h1dGRvd24gUlN0dWRpby4gQWRkaXRpb25hbGx5LCB0aGUgZ29vZG5lc3Mtb2YtZml0IG1lYXN1cmUgd2FzIHVzZWQgZHVlIHRvIHRoZSBmYWN0IHRoYXQgdGhlIHZpb2xhdGlvbiB0byB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24gb24gdGhlIHJlc2lkdWFscy4gVGhpcyBub3JtYWxpdHkgYXNzdW1wdGlvbiByZW1haW5zIHVuY29ycmVjdGVkIGV2ZW4gYWZ0ZXIgc2V2ZXJhbCB0cmFuc2Zvcm1hdGlvbiB0ZWNobmlxdWVzIHdlcmUgdXNlZCBvbiB0aGUgZGF0YSBzZXQuIFRoZSBjZW50cmFsIGxpbWl0IHRoZW9yZW0gKENMVCkgd2FzIHVzZWQgdG8gYmFzZSBpbmZlcmVuY2VzIG9uIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cy4gCgpUaGUgZmluYWwgbW9kZWwgdXNlZCB0aGUgbG9nKGFnZSkgQm94LWNveCB0cmFuc2Zvcm1hdGlvbiB3aXRoIGEgLTAuNSBhZGp1c3RtZW50IG9uIGJtaS4gVGhpcyBtb2RlbCB3YXMgc2VsZWN0ZWQgb3ZlciB0aGUgZnVsbCBtb2RlbCBhbmQgdGhlIGxvZyhibWkpIG1vZGVsIGJlY2F1c2UgZXZlbiB0aG91Z2ggbm9uZSBvZiB0aGUgbW9kZWxzIG1ldCB0aGUgbm9ybWFsaXR5IGFzc3VtcHRpb24gdGhlIGxvZyhhZ2UpIG1vZGVsIGhhZCB0aGUgYmVzdCAqKiRSXjIsIFJeMl97YWRqfSQsIGFuZCAkQ19wJCoqIG91dHB1dHMgYWZ0ZXIgcnVubmluZyB0aGUgZ29vZG5lc3Mtb2YtZml0IG1lYXN1cmUgb24gYWxsIHRocmVlIG1vZGVscy4gQWRkaXRpb25hbGx5LCBmcm9tIHRoZSBzZWxlY3RlZCBmaW5hbCBtb2RlbCwgdGhyZWUgdmFyaWFibGVzIHdlcmUgcmVtb3ZlZCBmcm9tIHRoZSBvcmlnaW5hbCBlaWdodCBkdWUgdG8gdGhlbSBoYXZpbmcgbm9uLXNpZ25pZmljYW50IHAtdmFsdWVzLiBBZnRlciByZW1vdmluZyB0aG9zZSB2YXJpYWJsZXMgdGhlIGZpbmFsIG1vZGVsIHdhcyBjb21wbGV0ZS4gCgojIFJlZmVyZW5jZXMKCkFtZXJpY2FuIERpYWJldGVzIEFzc29jaWF0aW9uLiAoMjAyMykuIFVuZGVyc3RhbmRpbmcgRGlhYmV0ZXMgRGlhZ25vc2lzLiBEaWFiZXRlcy5vcmcuIGh0dHBzOi8vZGlhYmV0ZXMub3JnL2Fib3V0LWRpYWJldGVzL2RpYWdub3NpcwoKCgo=