1 Introduction

Obesity is defined by the CDC as “having a body mass index (BMI) of 30.0 or higher, with severe obesity being defined as a BMI of 40.0 or higher” [1]. The prevalence of both adult and childhood obesity and severe obesity have been rising steadily since 1990 [1, 2, 3]. In 2024, a staggering 35 million children under the age of 5 were considered overweight according to the World Health Organization. While once thought to be a symptom of excess, obesity rates have been rising in developing countries, indicating the severity of the obesity epidemic worldwide. In Africa alone the number of overweight children under 5 years of age rose a staggering 12.1% since 2000 [3].

A novel challenge has been raised by these global spikes in obesity; children are facing obesity and malnutrition simultaneously. Due to the increase in exposure to poor nutrient quality, low cost, high fat and high sugar foods paired with lowered participation in physical exercise, children are developing elevated weights while simultaneously experiencing malnutrition [3]. Due to the decreased access to healthy foods, the obesity rate in children increases the closer their family gets to the federal poverty line. According to CDC and the US Department of Agriculture, currently at least 25.8% of children living with a family income at 130% of the federal poverty line or lower are experiencing obesity [2].

Obesity care places a massive burden on hospitals, healthcare institutions, and healthcare systems as a whole due to its high comorbidity rates [1, 2, 3]. According to the WHO, in 2021, elevated BMI was linked to approximately 3.7 million deaths from non-communicable diseases including but not limited to heart diseases, diabetes, cancers, digestive disorders, chronic respiratory diseases, and neurological disorders [3]. In 2017 the CDC found that 58% of adults in the United States that are considered obese also have comorbid high blood pressure, and 23% have comorbid diabetes. Additionally, the CDC identified that in 2019 annual medical costs for those with obesity were on average $1,861 higher per person per year for those with obesity, and $3,097 per person per year for those with severe (a.k.a. morbid) obesity.

As outlined by the UConn Rudd Center for Food Policy and Health those with overweight or obese weight status experience weight based stigma in a broad variety of life aspects. Many persons with obesity have gone as far as requesting weight no longer be collected at routine medical appointments in attempt to combat these stigmas and reduce potential inequalities in healthcare [4]. While this band-aid approach seeks to return comfort and dignity to the patient, the categorization of patients into weight rankings is pivotal for targeted preventative care and reducing future healthcare burden for patients and the healthcare system as a whole. In order to ensure adequate care is initiated at the appropriate time, researchers have set out to identify alternative ways to predict a patient’s weight status or future weight status. While some patients may not currently be overweight or obese, their behaviors and daily activities may indicate they are on track to develop overweight or obese weight status, further underpinning the need for weight based predictions that do not rely on patients’ current weight.

obese <- read.csv("https://nlepera.github.io/sta552/HW05/data/obesity.csv")

colnames(obese)[colnames(obese) == "family_history_with_overweight"] <- "FH.Overweight"
colnames(obese)[colnames(obese) == "NObeyesdad"] <- "Ob.Rank"

1.1 Data Collection and Summary Detials

In order to address this problem of behavioral based weight status identification, the researchers at Universidad de la Costa (CUC) compiled personal health and daily behavioral data from persons ages 14 through 61 in Mexico, Peru, and Columbia. Participants were presented with a web survey to anonymously capture their eating habits, behavioral choices, and demographic information. This survey collection resulted in 2111 unique observations over 16 variables, and the subsequent calculation of 1 variable to classify the respondent’s obesity rank [5]. More information on the variables collected is included below in Table 1

vars <- data.frame(
  Name = c("Gender", "Age", "Height", "Weight", "Family History Overweight", "High Calorie Foods Often", "Vegetables in Meals", "Meals Per Day", "Food Between Meals", "Smoking Status", "Daily Water Consumption", "Calorie Counting", "Physical Activity Rate", "Technology Use", "Alcohol", "Transportation", "Obesity Level"),
  Type = c("Continuous", "Continuous", "Continuous", "Continuous", "Binary", "Binary", "Continuous", "Continuous", "Categorical", "Binary", "Continuous", "Binary", "Continuous", "Continuous", "Categorical", "Categorical", "Categorical"),
  Questions = c("", "", "", "", "Has a family member suffered or suffers from overweight?", "Do you eat high caloric food frequently?", "Do you usually eat vegetables in your meals?", "How many main meals do you have daily?", "Do you eat any food between meals?", "Do you smoke?", "How much water do you drink daily?", "Do you monitor the calories you eat daily?", "How often do you have physical activity?", "How much time do you use technological devices such as cell phone, videogames, television, computer and others?", "How often do you drink alcohol?", "Which transportation do you usually use?", ""),
  Responses = c("Male/Female", "Age (y)", "Height (m)", "Weight (kg)", "Yes/No", "Yes/No", "", "", "No/Sometimes/Frequently/Always", "Yes/No", "", "Yes/No", "", "", "Never/Sometimes/Frequently/Always", "Auto/Motorbike/Bike/Public Trans/Walking", "Insuf. Weight/Normal Weight/Overweight 1/Overweight 2/Obese 1/Obese 2/Obese 3"),
  Var = c("Gender", "Age", "Height", "Weight", "fh.overweight", "FAVC", "FCVC", "NCP", "CAEC", "SMOKE", "CH20", "SCC", "FAF", "TUE", "CALC", "MTRANS", "Ob.Rank")
)
kable(vars, align = c("l", "c", "c", "c"), col.names = c("Variable Name", "Type", "Associated Survey Question", "Response Options / Levels", "Coded Var. Name"), caption = "Table 1:
      <br>Summary details of all variables in the obesity data.") %>% 
  kable_styling()
Table 1:
Summary details of all variables in the obesity data.
Variable Name Type Associated Survey Question Response Options / Levels Coded Var. Name
Gender Continuous Male/Female Gender
Age Continuous Age (y) Age
Height Continuous Height (m) Height
Weight Continuous Weight (kg) Weight
Family History Overweight Binary Has a family member suffered or suffers from overweight? Yes/No fh.overweight
High Calorie Foods Often Binary Do you eat high caloric food frequently? Yes/No FAVC
Vegetables in Meals Continuous Do you usually eat vegetables in your meals? FCVC
Meals Per Day Continuous How many main meals do you have daily? NCP
Food Between Meals Categorical Do you eat any food between meals? No/Sometimes/Frequently/Always CAEC
Smoking Status Binary Do you smoke? Yes/No SMOKE
Daily Water Consumption Continuous How much water do you drink daily? CH20
Calorie Counting Binary Do you monitor the calories you eat daily? Yes/No SCC
Physical Activity Rate Continuous How often do you have physical activity? FAF
Technology Use Continuous How much time do you use technological devices such as cell phone, videogames, television, computer and others? TUE
Alcohol Categorical How often do you drink alcohol? Never/Sometimes/Frequently/Always CALC
Transportation Categorical Which transportation do you usually use? Auto/Motorbike/Bike/Public Trans/Walking MTRANS
Obesity Level Categorical Insuf. Weight/Normal Weight/Overweight 1/Overweight 2/Obese 1/Obese 2/Obese 3 Ob.Rank
obese <- obese %>% mutate_if(is.character, as.factor)

1.2 Analysis Goals

The goals of this analysis are to develop and tune various prediction models to utilize the dietary, behavioral, and demographic data (without patient weight) to predict a patients’ overweight/obesity status. This will allow researchers to better identify patients that are overweight/obese that may not normally have access to preventative healthcare and to predict patients that may not yet be suffering from overweight or obesity yet their behaviors and dietary habits place them on track to developing these conditions.

As the overweight/obesity rank as captured by Palechor and Manotas was encoded as a factor variable with 7 levels both linear and multinomial regression approaches were utilized. In addition to standard regression approaches, CART Regression, CART Classification, BAGGING regression, and BAGGING classification were employed for assessment.

2 Data Preparation

In order to accommodate for both linear regression and categorical classification/multinomial regression the data was copied in to duplicate data sets. One data set retained the categorical values as factors, while the other first converted categorical values to factors, then again converted these values to numeric from factor. Summary statistics of the factor based data set are included below in Table 2.

As the purpose of this analysis is to predict the participant’s overweight/obese status without utilizing participants weight, the weight variable was dropped from subsequent analysis following Table 2. Additionally, a cursory evaluation of Table 2 indicates there are no missing values in the data set, therefore imputation is not required.

kable(summary(obese), align = "c" , caption = "Table 2:
      <br>Summary statistics of obesity data.") %>% 
  kable_styling() %>% 
  scroll_box(width = "100%")
Table 2:
Summary statistics of obesity data.
Gender Age Height Weight FH.Overweight FAVC FCVC NCP CAEC SMOKE CH2O SCC FAF TUE CALC MTRANS Ob.Rank
Female:1043 Min. :14.00 Min. :1.450 Min. : 39.00 no : 385 no : 245 Min. :1.000 Min. :1.000 Always : 53 no :2067 Min. :1.000 no :2015 Min. :0.0000 Min. :0.0000 Always : 1 Automobile : 457 Insufficient_Weight:272
Male :1068 1st Qu.:19.95 1st Qu.:1.630 1st Qu.: 65.47 yes:1726 yes:1866 1st Qu.:2.000 1st Qu.:2.659 Frequently: 242 yes: 44 1st Qu.:1.585 yes: 96 1st Qu.:0.1245 1st Qu.:0.0000 Frequently: 70 Bike : 7 Normal_Weight :287
NA Median :22.78 Median :1.700 Median : 83.00 NA NA Median :2.386 Median :3.000 no : 51 NA Median :2.000 NA Median :1.0000 Median :0.6253 no : 639 Motorbike : 11 Obesity_Type_I :351
NA Mean :24.31 Mean :1.702 Mean : 86.59 NA NA Mean :2.419 Mean :2.686 Sometimes :1765 NA Mean :2.008 NA Mean :1.0103 Mean :0.6579 Sometimes :1401 Public_Transportation:1580 Obesity_Type_II :297
NA 3rd Qu.:26.00 3rd Qu.:1.768 3rd Qu.:107.43 NA NA 3rd Qu.:3.000 3rd Qu.:3.000 NA NA 3rd Qu.:2.477 NA 3rd Qu.:1.6667 3rd Qu.:1.0000 NA Walking : 56 Obesity_Type_III :324
NA Max. :61.00 Max. :1.980 Max. :173.00 NA NA Max. :3.000 Max. :4.000 NA NA Max. :3.000 NA Max. :3.0000 Max. :2.0000 NA NA Overweight_Level_I :290
NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA Overweight_Level_II:290

2.1 Exploratory Data Analysis

The pruned obesity data was then assessed for potential correlation between all variables. The below Figure 1 demonstrates both the magnitude and direction through the respective sizes and colors of the potted circles. Variables sharing a pearson correlation coefficient (r) of greater than or equal to 0.60 are considered to have at least moderate correlation. A total of four (4) variables in two (2) unique combinations were identified as at least moderately correlated, as captured below in Figure 2 and Table 3.

#drop weight from obese
obese <- obese[-4]
#create all numeric version of obese for correlation plot 
obese.numeric <- obese %>% mutate_if(is.factor, as.numeric)

cor.obese <- cor(obese.numeric)

corrplot(cor.obese, type = "upper", order = "hclust", tl.col = "black", tl.srt = 65, diag = T)
title(main = "Variable Correlation", line = -3.75, cex.main =2)
title(sub = "Figure 1:
Pairwise correlation visualization of all variables in obese data. Character variables 
were transformed to factors then numeric.
Size and color of circles are determined by strength and direction of correlation.", line = 0, cex.sub = 0.85)

#set up correlation table for filtering
cor.table <- data.frame(
  row = rownames(cor.obese)[row(cor.obese)],
  col = colnames(cor.obese)[col(cor.obese)],
  cor = c(cor.obese)
)

#filter for 0.6 < cor < 1.0 to filter for high cor but ignore cor = 1 (same variable match)
cor.t2 <- filter(cor.table, abs(cor) >= 0.6) %>% filter(abs(cor) != 1)
cor.t2$cor <- round(cor.t2$cor, 3)
cor.t2 <- cor.t2[order(cor.t2$cor),]

cor.mtrans_age <- ggplot(obese, aes (x = MTRANS, y = Age)) +
  geom_boxplot(aes(fill = MTRANS), alpha = 0.6) + 
  theme_bw() +
  theme(axis.text.x = element_text(angle = 35, vjust = 1, hjust=1), legend.position = "none") 

cor.gen_height <- ggplot(obese, aes (x = Gender, y = Height)) +
  geom_boxplot(aes(fill = Gender), alpha = 0.6) + 
  theme_bw() +
  theme(axis.text.x = element_text(angle = 35, vjust = 1, hjust=1), legend.position = "none") 
grid.arrange(
  arrangeGrob(cor.mtrans_age, cor.gen_height, ncol = 2, nrow = 1),
  top = textGrob("Pairwise Correlation Plots
                 ", gp = gpar(fontsize = 18, fontface = "bold")),
  bottom = textGrob("Figure 2:
Visualizatons of Obesity variable correlations where |r| > 0.6")
)

kable(cor.t2[c(1,4),], align = "c", row.names = F, col.names = c("Variable 1 (x)", "Variable 2 (y)", "Correlation (r)"), caption = "Table 3:
      <br>Identified moderate to strong correlations from obese data.  
      <br>Duplicate entries have been removed, favoring categorical values as the x variable to allow for easy visualization in Figure 2.
      <br>Moderate to strong correlations defined as 0.6 ≤ r ≤ 1") %>% 
  kable_styling()
Table 3:
Identified moderate to strong correlations from obese data.

Duplicate entries have been removed, favoring categorical values as the x variable to allow for easy visualization in Figure 2.
Moderate to strong correlations defined as 0.6 ≤ r ≤ 1
Variable 1 (x) Variable 2 (y) Correlation (r)
MTRANS Age -0.602
Gender Height 0.618

The correlated variables as outlined in Figre 2 and Table 3 prove to be logical and do not require further investigation.

  • The average male height globally is greater than the global average male height, therefore the positive correlation between gender and height is logical.
  • As there is a minimum license age for utilizing two(2) of the five (5) transportation methods (automobile & motorbike) and elderly adults tend to avoid higher phsyical burden methods of transportation such as walking and bikes, the negative correlation between age and method of transportation is logical with the support of Figure 2.

2.2 Feature Engineering

In order to properly utilize the correlated variables without data loss, feature variables were created to allow their inclusion without the implications of collinearity. Additionally, a third feature variable was created to allow for additional dimensionality to the prediction models. The following feature variables were created prior to analysis:

  • Vegetables per Day (veg.day) = vegetables per meal (FCVC) * meals per day (NCP)
  • Height by Gender (gen.height) = height / gender
  • Transportation method by Age (trans.age) = transportation method (MTRANS) / age (Age)

Summary statistics of the newly created feature variables are included below in Table 4.

#adding feature engineered variables
obese$veg.day <- obese$FCVC*obese$NCP
obese$gen.height <- obese$Height/as.numeric(obese$Gender)
obese$trans.age <- as.numeric(obese$MTRANS)/as.numeric(obese$Age)

#pulling an all numeric copy just in case needed later
obese.numeric <- obese %>% mutate_if(is.factor, as.numeric)

kable(summary(obese[c(17:19)]), align = "c", col.names = c("Vegetables per Day", "Height by Gender", "Transport Method by Age"),caption = "Table 4:
      <br>Summary statistics of feature engineered variables (veg.day, gen.height, trans.age") %>% 
  kable_styling()
Table 4:
Summary statistics of feature engineered variables (veg.day, gen.height, trans.age
Vegetables per Day Height by Gender Transport Method by Age
Min. : 1.000 Min. :0.7800 Min. :0.01786
1st Qu.: 5.018 1st Qu.:0.8799 1st Qu.:0.13120
Median : 6.196 Median :0.9571 Median :0.17391
Mean : 6.514 Mean :1.2568 Mean :0.15248
3rd Qu.: 9.000 3rd Qu.:1.6376 3rd Qu.:0.20000
Max. :12.000 Max. :1.8434 Max. :0.35714

2.3 Data Splitting for Cross Validation

In order to create the prediction models for patient obesity status, the data was first split in to 80:20 training:test groups to allow for model training and tuning on the test group. This split selection process will ensure the prediction models are not over-fitted to the initial data. Preventing over fitting of the models will allow for use with future collected data and participant data sourced from other countries by ensuring flexibility.

#80:20 split for CV
#numeric
cv.index <- sample(1:nrow(obese.numeric), size = 0.8*nrow(obese.numeric))
ob.train <- obese.numeric[cv.index,]
ob.test <- obese.numeric[-cv.index,]

#categorical
cv.index1 <- sample(1:nrow(obese), size = 0.8*nrow(obese))
ob.train1 <- obese[cv.index,]
ob.test1 <- obese[-cv.index,]

3 CART Models

Rather than creating a regression model the CART algorithm can be utilized to create prediction models for both regression and classification. The CART algorithm functions through creating decision trees utilizing supervised learning algorithms based on the target response variable. The model initiates by splitting the first identified variable based on an algorithmic defined cutoff value creating the root node that branches into two inner nodes. The inner nodes are subsequently split into either an additional two inner nodes or two terminal (leaf) nodes. This process continues until the stop point is reached; the stop point is algorithmically determined to prevent over fitting and ensure the model is generalized enough that newly collected data can be utilized with the same model.

As the obesity rank variable is an integer value, both CART Regression and CART Classification trees were created to determine the best fit prediction model.

3.1 CART Regression

CART regression was initiated utilizing the rpart() package utilizing the full model. The regression tree was created utilizing the ANOVA method indicating that the tree splits are completed utilizing Mean Square Error (MSE) reduction techniques. This MSE reduction approach follows the below equation to ensure child (inner & terminal) nodes are selected based on splits that minimize the MSE of each node.

\[ \underline{\mbox{Mean Squared Error (MSE) Reduction}} \\ MSE = \frac{\sum_{i = 1}^{n} (y_{i} - \hat{y})^2}{n} \]

3.1.1 Model Creation

As the data was preemptively split into training and test data, the training data was utilized for 10-fold cross validation as part of the initial full model creation process. This cross validation ensures the produced model is the best fit for the training data as provided. Initially the hyperparameters were initiated as:

  • minimum split = 20
    • minimum observations in a group before the algorithm will try to split
  • minimum bucket = 7
    • minimum observations allowed in a leaf node
  • complexity parameter (cp) = 0.01
    • minimum improvement in target parameter to allow for split (CART Reg = MSE reduction)
  • maximum depth = 5
    • maximum number of levels in tree output
#create CART regression tree (initial full model)
reg.tree <- rpart(Ob.Rank ~ ., data = ob.train, method = "anova", 
                  control = rpart.control(
                    minsplit = 20,
                    minbucket = 7,
                    cp = 0.01, #reduction in MSE needed for split
                    maxdepth = 5,
                    xval = 10
                   ))
#plot full model regression tree
fancyRpartPlot(reg.tree,  type = 5, tweak = 1.01, main = "Initial CART Regression Tree" , caption = "Figure 3:
Initial CART regression tree model (unpruned)")

3.1.2 Hyperparameter Tuning

With the initial CART regression tree model created above in Figure 3 the model was then tuned to assign better fit hyperparameter values ensuring improved fit and an overall reduction in model size. The model’s CP values were compared with respect to the cross validated error (xerror) and standard error of the cross validated error (xstd) to identify new CP values for use with the initial model. Both the CP associated with the minimum xerror (known as the min CP) and the largest CP associated with the xerror within 1 standard error of the minimum xerror value (known as the 1se CP value) were collected for subsequent tuning.

#plot of CP values based on table size
kable(reg.tree$cptable, caption = "Table 5:
      <br> CP values for CART regression tree pruning") %>% 
  kable_styling(font_size = 12)
Table 5:
CP values for CART regression tree pruning
CP nsplit rel error xerror xstd
0.1500394 0 1.0000000 1.0020815 0.0218632
0.0546224 1 0.8499606 0.8539561 0.0243810
0.0284149 2 0.7953382 0.8030927 0.0234886
0.0194583 4 0.7385084 0.7461165 0.0237059
0.0188729 5 0.7190501 0.7363270 0.0241970
0.0136356 7 0.6813043 0.7160616 0.0240365
0.0124554 9 0.6540331 0.7218069 0.0250967
0.0109945 11 0.6291222 0.7079445 0.0247246
0.0109240 12 0.6181278 0.7049123 0.0246909
0.0100000 13 0.6072037 0.6963265 0.0242530
par(oma =c(2,1,5,1))
plotcp(reg.tree)
mtext("CP Cutoff Test", side = 3, line = 5, outside = T, font = 2, cex = 2)
mtext("Figure 4:
CP hyperparameter tuning, best fit value at cross of dotted line.", side = 1, line = 5, outside = T, font = 1, cex = 0.9)

3.1.3 Model Pruning

The calculated CP values from the previous section were then fed in to the model pruning function prune() to create two pruned CART regression tree models. The first tree model (Figure 5) was created using the 1se CP value while the second (Figure 6) was created using the minimum CP value. As demonstrated by the below regression trees, the 1se method of tuning the CP hyperparameter results in a more significantly reduced regression tree model.

cp.reg.table <- reg.tree$cptable

#get values for pruned trees

xerror.min <- min(cp.reg.table[,"xerror"]) #min xerror

#extract row with minimum xerror as standalone data frame, then pull CP value out of that row
min.cp.row <- which.min(cp.reg.table[, "xerror"])

min.cp <- cp.reg.table[min.cp.row, "CP"] #min.cp for mincp tree

#get xstd value from minimum xerror row
xerror.xstd <- cp.reg.table[min.cp.row, "xstd"]

threshold <- xerror.min + xerror.xstd #add xstd value to min xerror to get 1 se from min

best.cp.row <- which(cp.reg.table[,"xerror"] <= threshold)[1] #pull row where xerror is closest to but below min(xerror) + 1se threshold
best.cp <- cp.reg.table[best.cp.row, "CP"] #pull CP value from row where xerror w.in 1se

reg.tree.bestcp <- prune(reg.tree, cp = best.cp) #prune tree with 1se parameter
reg.tree.mincp <- prune(reg.tree, cp = min.cp) #prune tree with min cp parameter
fancyRpartPlot(reg.tree.bestcp,  type = 5, tweak = 1.01, main = "Pruned CART Regression Tree 
CP = 1se", caption = paste("Figure 5:
Pruned CART Regression tree (1se model).  CP =", round(best.cp, 5)))

fancyRpartPlot(reg.tree.mincp,  type = 5, tweak = 1.01, main = "Pruned CART Regression Tree 
CP = min", caption = paste("Figure 6:
Pruned CART Regression tree (min model).  CP =", round(min.cp, 5)))

As demonstrated in the above Figure 5, the 1se CART regression tree resulted in a model that incorporated the following feature variables:

  • CAEC
  • gen.height
  • Age
  • NCP
  • FH.Overweight

Meanwhile, as demonstrated in Figure 6 the minimum CART regression tree resulted in a model that incorporated the following variables:

  • CAEC
  • gen.height
  • Age
  • NCP
  • FH.Overweight
  • MTRANS
  • FAF
  • CH2O

As the minimum CP model utilized a lower CP value, this model required less of a change in MSE before nodes were split, thus resulting in a broader model that incorporates a larger number of feature variables.

3.1.4 Model Evaluation

Both the 1se and minimum CP CART regression tree models were then utilized to generate prediction values for obesity ranks in utilizing the test data set. These predicted values were compared to the actual values in the test data set to assess for model accuracy and reliability. These CART regression models were additionally compared to two linear regression models to determine their efficacy in relation to other more established statistical prediction methods. The first regression model was comprised utilizing the variables as identified in the 1se CART regression tree model, while the second was constructed utilizing the stepAIC() package to automatically identify the best variables for inclusion.

The linear regression equations utilized are as follows:

\[ \underline{\mbox{1se Linear Regression Model}} \\ \\ \begin{align} \hat{Y}_{Ob.Rank} \sim \mbox{CAEC}*x_{1} + &\mbox{gen.height}*x_2 + \mbox{Age}*x_3 + \mbox{NCP}*x_4 + \mbox{FH.Overweight}*x_5 \end{align} \]

\[ \underline{\mbox{stepAIC Linear Regression Model}} \\ \\ \begin{align} \hat{Y}_{Ob.Rank} \sim \mbox{CAEC}*x_{1} + &\mbox{gen.height}*x_2 + \mbox{Age}*x_3 + \\ \mbox{NCP}*x_4 + &\mbox{FH.Overweight}*x_5 + \mbox{MTRANS}*x_6 + \mbox{FAF}*x_7 + \mbox{CH2O}*x_8 \end{align} \]

obrank.reg.tree.best <- predict(reg.tree.bestcp, newdata = ob.test)
obrank.reg.tree.min <- predict(reg.tree.mincp, newdata = ob.test)

#BestCP Model
mse.reg.best <- mean((ob.test$Ob.Rank - obrank.reg.tree.best)**2)
rmse.reg.best <- sqrt(mse.reg.best)
mae.reg.best <- mean(abs(ob.test$Ob.Rank - obrank.reg.tree.best))
rsq.reg.best <- (cor(as.numeric(ob.test$Ob.Rank), obrank.reg.tree.best))**2

#MinCP Model
mse.reg.min <- mean((ob.test$Ob.Rank - obrank.reg.tree.min)**2)
rmse.reg.min <- sqrt(mse.reg.min)
mae.reg.min <- mean(abs(ob.test$Ob.Rank - obrank.reg.tree.min))
rsq.reg.min <- (cor(as.numeric(ob.test$Ob.Rank), obrank.reg.tree.min))**2

#make linear regression models
#model using variables from 1se tree
lin.1se <- lm(Ob.Rank ~ CAEC + gen.height + Age + NCP + FH.Overweight, data = ob.train)
lin.pred <- predict(lin.1se, newdata = ob.test)
mse.lin.min <- mean((ob.test$Ob.Rank - lin.pred)**2)
rmse.lin.min <- sqrt(mse.lin.min)
mae.lin.min <- mean(abs(ob.test$Ob.Rank - lin.pred))
rsq.lin.min <- (cor(as.numeric(ob.test$Ob.Rank), lin.pred))**2

#stepwise selection from all variables
step.lin <- lm(Ob.Rank ~., data = ob.train)
AIC.lin <- stepAIC(step.lin, direction = "both", trace = F)
step.pred <- predict(AIC.lin, newdata = ob.test)
mse.step.min <- mean((ob.test$Ob.Rank - step.pred)**2)
rmse.step.min <- sqrt(mse.step.min)
mae.step.min <- mean(abs(ob.test$Ob.Rank - step.pred))
rsq.step.min <- (cor(as.numeric(ob.test$Ob.Rank), step.pred))**2

reg.errors <- data.frame(
  measures = c("Mean Square Error (MSE)", "Root Mean Square Error (RMSE)", "Mean Absolute Error (MAE)", "R Squared"),
  tree.r1se = c(mse.reg.best, rmse.reg.best, mae.reg.best, rsq.reg.best),
  tree.rmin = c(mse.reg.min, rmse.reg.min, mae.reg.min, rsq.reg.min),
  lin.1se = c(mse.lin.min, rmse.lin.min, mae.lin.min, rsq.lin.min),
  step.lin = c(mse.step.min, rmse.step.min, mae.step.min, rsq.step.min)
)

3.1.4.1 MSE, RMSE, MAE, and R Squared

The model assessment statistics are included below in Table 6. As demonstrated by the lowest MSE, RMSE, and MAE with the highest r\(^2\) value, the Min CP CART tree resulted in the best fit prediction model. Albeit, while the Min CP CART Tree preformed the best of the four available regression based models, none of the models preformed well. All returned an r\(^2\) value of less than 0.26 indicating these models only capture less than 26% of the variance in the obesity data.

kable(reg.errors, align = c("l", "c","c","c","c") , col.names = c("Asm. Measures", "1se CART Tree", "Min CP CART Tree", "1se Linear", "stepAIC Linear") , caption = "Table 6:
      <br>Model assessment statistics") %>% 
  kable_styling()
Table 6:
Model assessment statistics
Asm. Measures 1se CART Tree Min CP CART Tree 1se Linear stepAIC Linear
Mean Square Error (MSE) 2.7792004 2.7090165 3.2114684 2.8910271
Root Mean Square Error (RMSE) 1.6670934 1.6459090 1.7920570 1.7003021
Mean Absolute Error (MAE) 1.2933708 1.2571838 1.5385074 1.4111261
R Squared 0.2762515 0.2996549 0.1662987 0.2512238

3.1.4.2 Variable Importance

A summary of the variable importance for both the 1se CART regression and min CP CART regression trees is included below in Figure 7. While a majority of the variables as represented in the above trees in Figure 5 and Figure 6 are represented in the below importance graphs, some variables are considered important but omitted. This omission of important variables can be caused by a number of reasons including but not limited to:

  • Identified collinearity: Tree models automatically drop some or all collinear variables
  • Splitting criteria: despite a variable’s calculated significance value, variables may not impact the MSE when split and thus are subsequently dropped
  • CP pruning: CP cutoff prunes splits that only partially fit the model, causing potentially important variables from being dropped
imp.tree1se <- reg.tree.bestcp$variable.importance
imp.treemin <- reg.tree.mincp$variable.importance



par(mfrow = c(1,2), oma = c(5,2,3,2))
barplot(imp.tree1se,
        horiz = T,
        main = list("1se cp Regression Tree Model", font = 1),
        xlab = "Importance",
        las = 1)
barplot(imp.treemin,
        horiz = T,
        main = list("Min cp Regression Tree Model", font = 1),
        xlab = "Importance",
        las = 1)
mtext("Variable Importance", side = 3, line = 1, outer = T, cex = 1.5, font = 2)
mtext("Figure 7:
Variable importance plots for both CART Regression tree models", side = 1, line = 1, outer = T)

3.2 CART Classification

CART classification was initiated utilizing the rpart() package utilizing the full model as a starting point. The regression tree was created utilizing the Class method and Gini impurity method indicating that the tree splits are completed utilizing splits to maximize the Gini gain. This Gini reduction approach follows the below equation to ensure child (inner & terminal) nodes are selected based on splits that maximize the Gini gain (purity) of each node.

\[ \underline{\mbox{Gini Impurity Reduction}} \\ \begin{align*} \mbox{Gini} &= 1 - \sum_{i = 1}^{k} p_{i}^{2} \ \ \ &\mbox{where} \ \ &p_{i} \ \ \mbox{is a proportion of class} \ i \\ \\ \Delta G &= \mbox{Gini} * S - \frac{n_L}{n} * \mbox{Gini}*S_L - \frac{n_R}{n}*\mbox{Gini}*S_r &\mbox{where} \ &n_L \ = \mbox{left node sample size} \\ &&&n_R = \mbox{right node sample size} \end{align*} \]

3.2.1 Model Creation

As the data was preemptively split into training and test data, the training data was utilized for 10-fold cross validation as part of the initial full model creation process. This cross validation ensures the produced model is the best fit for the training data as provided. Initially the hyperparameters were initiated as:

  • minimum split = 20
    • minimum observations in a group before the algorithm will try to split
  • minimum bucket = 10
    • minimum observations allowed in a leaf node
  • complexity parameter (cp) = 0.001
    • minimum improvement in target parameter to allow for split (CART Class = Gini gain)
  • maximum depth = 5
    • maximum number of levels in tree output
  • Gini impurity
#create CART regression tree (initial full model)
class.tree <- rpart(Ob.Rank~., data = ob.train1, method = "class",
                    parms = list(split = "gini"),
                    control = rpart.control(
                    minsplit = 20,
                    minbucket = 10,
                    cp = 0.001, #reduction in complexity param needed for split
                    maxdepth = 5,
                    xval = 10
                   ))
#plot full model class tree
fancyRpartPlot(class.tree,  type = 5, tweak = 1.1, main = "Initial CART Gini Classification Tree" , caption = "Figure 8:
Initial CART Gini classification tree model (unpruned)")

3.2.2 Hyperparameter Tuning

With the initial CART classification tree model created above in Figure 8 the model was then tuned to assign better fit hyperparameter values ensuring improved fit and an overall reduction in model size. The model’s CP values were compared with respect to the cross validated error (xerror) and standard error of the cross validated error (xstd) to identify new CP values for use with the initial model. Both the CP associated with the minimum xerror (known as the min CP) and the largest CP associated with the xerror within 1 standard error of the minimum xerror value (known as the 1se CP value) were collected for subsequent tuning.

#plot of CP values based on table size
kable(class.tree$cptable, caption = "Table 7:
      <br> CP values for CART Gini classification tree pruning") %>% 
  kable_styling(font_size = 12)
Table 7:
CP values for CART Gini classification tree pruning
CP nsplit rel error xerror xstd
0.1757619 0 1.0000000 1.0106308 0.0105438
0.0666194 1 0.8242381 0.8242381 0.0134790
0.0375620 3 0.6909993 0.6945429 0.0143687
0.0283487 4 0.6534373 0.6739901 0.0144414
0.0262225 5 0.6250886 0.6364281 0.0145291
0.0219702 6 0.5988661 0.6243799 0.0145449
0.0184266 7 0.5768958 0.6017009 0.0145587
0.0170092 8 0.5584692 0.5761871 0.0145491
0.0163005 10 0.5244507 0.5648476 0.0145363
0.0127569 11 0.5081502 0.5492558 0.0145102
0.0063785 13 0.4826364 0.5138200 0.0144135
0.0049610 15 0.4698795 0.5081502 0.0143932
0.0042523 17 0.4599575 0.4989369 0.0143572
0.0021262 21 0.4429483 0.4897236 0.0143177
0.0010000 22 0.4408221 0.4904323 0.0143209
par(oma =c(2,1,5,1))
plotcp(class.tree)
mtext("CP Cutoff Test", side = 3, line = 5, outside = T, font = 2, cex = 2)
mtext("Figure 9:
CP hyperparameter tuning, best fit value at cross of dotted line.", side = 1, line = 5, outside = T, font = 1, cex = 0.9)

3.2.3 Model Pruning

The calculated CP values from the previous section were then fed in to the model pruning function prune() to create two pruned CART Gini classification tree models. The first tree model (Figure 10) was created using the 1se CP value while the second (Figure 11) was created using the minimum CP value. As demonstrated by the below regression trees, the 1se method of tuning the CP hyperparameter results in a more significantly reduced regression tree model.

cp.class.table <- class.tree$cptable

#get values for pruned trees

xerror.min2 <- min(cp.class.table[,"xerror"]) #min xerror

#extract row with minimum xerror as standalone data frame, then pull CP value out of that row
min.cp.row2 <- which.min(cp.class.table[, "xerror"])

min.cp2 <- cp.class.table[min.cp.row2, "CP"] #min.cp for mincp tree

#get xstd value from minimum xerror row
xerror.xstd2 <- cp.class.table[min.cp.row2, "xstd"]

threshold2 <- xerror.min2 + xerror.xstd2 #add xstd value to min xerror to get 1 se from min

best.cp.row2 <- which(cp.class.table[,"xerror"] <= threshold2)[1] #pull row where xerror is closest to but below min(xerror) + 1se threshold
best.cp2 <- cp.class.table[best.cp.row2, "CP"] #pull CP value from row where xerror w.in 1se

class.bestcp <- prune(class.tree, cp = best.cp2) #prune tree with 1se parameter
class.mincp <- prune(class.tree, cp = min.cp2) #prune tree with min cp parameter
fancyRpartPlot(class.bestcp,  type = 5, tweak = 1.02, main = "Pruned CART Gini classification Tree 
CP = 1se", caption = paste("Figure 10:
Pruned CART Regression tree (1se model).  CP =", round(best.cp2, 5)))

fancyRpartPlot(class.mincp,  type = 5, tweak = 1.02, main = "Pruned CART Gini classification Tree 
CP = min", caption = paste("Figure 11:
Pruned CART Regression tree (min model).  CP =", round(min.cp2, 5)))

As demonstrated in the above Figure 10, the 1se CART Gini classification tree resulted in a model that incorporated the following feature variables:

  • veg.day
  • FH.Overweight
  • gen.height
  • Age
  • NCP
  • CAEC
  • trans.age
  • Height
  • FAVC
  • MTRANS

Meanwhile, as demonstrated in Figure 11 the minimum CP CART Gini classification tree resulted in a model that incorporated the following variables:

  • veg.day
  • FH.Overweight
  • gen.height
  • Age
  • NCP
  • CAEC
  • trans.age
  • Height
  • FAVC
  • MTRANS
  • TUE

As the minimum CP model utilized a lower CP value, this model required less of a change in the Gini gain before nodes were split, thus resulting in a broader model that incorporates a larger number of feature variables.

3.2.4 Model Evaluation

Both the 1se and minimum CP CART Gini classification tree models were then utilized to generate prediction values for obesity ranks in utilizing the test data set. These predicted values were compared to the actual values in the test data set to assess for model accuracy and reliability. These CART Gini classification models were additionally compared to a multinomial regression model to determine their efficacy in relation to other more established statistical prediction methods. The multinomial model was comprised utilizing the variables as identified in the 1se CART regression tree model.

#preds from tree model (class)
obrank.class.best.lab <- predict(class.bestcp, newdata = ob.test1, type = "class")
obrank.class.best.prob <- predict(class.bestcp, newdata = ob.test1, type = "prob")[,2]
obrank.class.min.lab <- predict(class.mincp, newdata = ob.test1, type = "class")
obrank.class.min.prob <- predict(class.mincp, newdata = ob.test1, type = "prob")[,2]

#make logistic regression models
#model using variables from 1se tree
log.1se <- multinom(Ob.Rank ~ veg.day + FH.Overweight + gen.height + Age + NCP + CAEC + trans.age + Height + FAVC + MTRANS, data = ob.train1, trace=F)


#log model predictions
log.pred.lab <- predict(log.1se, newdata = ob.test1, type = "class")
log.pred.prob <- predict(log.1se, newdata = ob.test1, type = "prob")[,2]


#make ROC curves
ROC.class.1se <- roc(ob.test1$Ob.Rank, obrank.class.best.prob)
ROC.class.min <- roc(ob.test1$Ob.Rank, obrank.class.min.prob)
ROC.log.1se <- roc(ob.test1$Ob.Rank, log.pred.prob)

#sensitivities
roc.c1.sen <- ROC.class.1se$sensitivities
roc.cm.sen <- ROC.class.min$sensitivities
roc.log.sen <- ROC.log.1se$sensitivities

#1 - specificities
roc.c1.spe <- (1 - ROC.class.1se$specificities)
roc.cm.spe <- (1 - ROC.class.min$specificities)
roc.log.spe <- (1 - ROC.log.1se$specificities)

#AUC
roc.c1.auc <- ROC.class.1se$auc
roc.cm.auc <- ROC.class.min$auc
roc.log.auc <- ROC.log.1se$auc

#best cutoffs
c1.cp <- coords(ROC.class.1se, "best", ret = c("threshold", "sensitivity", "specificity"))
cm.cp <- coords(ROC.class.min, "best", ret = c("threshold", "sensitivity", "specificity"))
l1.cp <- coords(ROC.log.1se, "best", ret = c("threshold", "sensitivity", "specificity"))

cutpoffs.c <- NULL
cutoffs.c <- data.frame(
  measure = c("Threshold", "Sensitivity", "Specificity", "AUC"),
  c1.cp = c(c1.cp[,1], c1.cp[,2], c1.cp[,3], roc.c1.auc),
  cm.cp = c(cm.cp[,1], cm.cp[,2], cm.cp[,3], roc.cm.auc),
  l1.cp = c(l1.cp[,1], l1.cp[,2], l1.cp[,3], roc.log.auc)
)

3.2.4.1 ROC Curve and AUC Evaluation

In order to compare the efficacy of each CART Gini classification tree and the multinomial regression model, an ROC plot was constructed and area under the curve (AUC) values were calculated in the below Figure 12. Optimal cutoff points to maximize threshold, sensitivity, and specificity were calculated and plotted in the below ROC plot. These cutoff values are additionally listed in Table 8 for ease of reference. While neither model proved to incredibly reliable, the CART Gini classification tree with the 1se CP value returned the best AUC value while retaining the highest possible sensitivity and specificity.

par(oma = c(6,2,2,2), cex.main = 2)
plot(roc.c1.spe, roc.c1.sen, type = "l", xlim = c(0,1), xlab = "1 - Specificity (False Positive)", ylim = c(0,1), ylab = "Sensitivity (True Positive)", main = "ROC Curves") + 
  lines(roc.c1.spe, roc.c1.sen, col = "darkviolet") +
  lines(roc.log.spe, roc.log.sen, col = "coral3") +
  lines(roc.cm.spe, roc.cm.sen, col = "blue3") +
  lines(x = c(0:1), y=c(0:1), col = "darkred", lty = 4)
FALSE integer(0)
  points((1-c1.cp[1,3]), c1.cp[1,2], pch = 22, bg = adjustcolor("darkviolet", alpha = 0.5), cex = 1.75) +
  points((1-cm.cp[1,3]), cm.cp[1,2], pch = 23, bg = adjustcolor("blue3", alpha = 0.5), cex = 1.5) +
  points((1-l1.cp[1,3]), l1.cp[1,2], pch = 21, bg = adjustcolor("coral3", alpha = 0.5), cex = 1.25)
FALSE integer(0)
  legend("bottomright", c(paste("Tree.1se ", "AUC =", round(roc.c1.auc, 4)),
                          paste("Tree.min ", "AUC =", round(roc.cm.auc, 4)),
                          paste("Logi.1se ", "AUC =", round(roc.log.auc, 4))),
         col = c("darkviolet", "blue3", "coral3"),
         lwd = 2,
         lty = 1,
         cex = 0.8,
         bty = "o")
mtext("Figure 12: 
ROC Comparrison of CART  Gini classification trees & multinomial regression model", side = 1, line = 6, outside = T)

kable(cutoffs.c, align = c("l", "c", "c", "c"), col.names = c("Cutoff Details", "Class 1se Model", "Class Min Model", "Logistic Model 1se Vars"), caption = "Table 8:
      <br>A summary of calculated best/maximum cutoff points and model AUC values.") %>% 
  kable_styling()
Table 8:
A summary of calculated best/maximum cutoff points and model AUC values.
Cutoff Details Class 1se Model Class Min Model Logistic Model 1se Vars
Threshold 0.2582288 0.1588050 0.2118908
Sensitivity 0.5178571 0.5714286 0.5535714
Specificity 0.8412698 0.8253968 0.6507937
AUC 0.6502268 0.6676587 0.5847506

4 Bootstrap BAGGING

To further improve upon the CART regression and classification trees created to predict obesity ranking, additional BAGGING algorithms were employed. Bootstrap aggregation with CART models (BAGGING) is a process that consists of repeatedly re-sampling the test data with replacement and calculating the mean for each sample collected. The distribution of these bootstrap means are then analysed to determine the regression coefficients to build the prediction model, with weighting considered for the frequency of these calculated means. Through this averaging of multiple models the BAGGING process reduces model variance without leading to increased bias. This approach introduces a layer of randomness to the model building process further preventing overfitting. The introduction of randomness in the model building process is essential to ensuring a model’s adaptability to new population data for prediction.

4.1 BAGGING Regression

As the data was preemptively split into training and test data, the training data was utilized for 10-fold cross validation as part of the initial BAGGING regression model creation process. This cross validation ensures the produced model is the best fit for the training data as provided. Initially the hyperparameters were initiated as multiple values with a for loop to identify the best fit parameter values:

  • nbagg = 10 & 25
    • number of bagged trees
  • complexity parameter (cp) = 0.01 & 0.5
    • minimum improvement in target parameter to allow for split
  • maximum depth = 5 & 10
    • maximum number of levels in tree output
#CV control
ctrl.bag <- trainControl(method = "cv", number = 5, verboseIter = T)

#define params to test
nbagg.val <- c(10, 25)
cp.val <- c(0.01, 0.5)
maxdepth.val <- c(5, 10)

bag <- data.frame(NULL)

for (nbagg in nbagg.val) {
  for (cp in cp.val) {
    for (maxdepth in maxdepth.val) {
      set.seed(1245)
      model <- bagging(Ob.Rank ~., data = ob.train,
                       nbagg = nbagg,
                       coob = T,
                       trControl = ctrl.bag,
                       control = rpart.control(cp = cp,
                                               maxdepth = maxdepth)
                       )
      oob.error <- model$err
      bag <- rbind(bag, data.frame(nbagg = nbagg,
                                   cp = cp,
                                   maxdepth = maxdepth,
                                   oob.error = oob.error))
    }
  }
}
#pull out best values
best.bagg <- bag[which.min(bag$oob.error),]
kable(best.bagg, caption = "Table 9:
      <br>A summary of best hyperparameter values.") %>% 
  kable_styling()
Table 9:
A summary of best hyperparameter values.
nbagg cp maxdepth oob.error
6 25 0.01 10 1.415986

4.1.1 RMSE MAE and R Squared

Utilizing the best identified hyperparameter values as listed above in Table 9 the initial BAGGING regression model was trained. Once trained, the model was utilized to obtain model assessment statistics such as RMSE, R squared and MAE. As demonstrated below in Table 9 the BAGGING regression model has proven to be the best fit of all five regression based models, returning the greatest r\(^2\) value and lowest RMSE and MSE values of all models compared.

#train final model
bag.final <- bagging(
  Ob.Rank ~.,
  ob.train,
  nbagg = best.bagg$nbagg,
  coob = T,
  trControl = ctrl.bag,
  control = rpart.control(cp = best.bagg$cp,
                          maxdepth = best.bagg$maxdepth),
  importance = T
)

bagg.pred <- predict(bag.final, newdata = ob.test)

bag.error <- postResample(pred = bagg.pred, obs = ob.test$Ob.Rank)

join.error <- reg.errors[-1,]
join.error$bag.reg <- c(as.numeric(bag.error[1]), as.numeric(bag.error[3]), as.numeric(bag.error[2]))

kable(join.error, col.names = c("Asm. Measures", "1se CART Tree", "Min CP CART Tree", "1se Linear", "stepAIC Linear", "BAGGING Regression") , caption = "Table 9:
      <br>Model assessment statistics") %>% 
  kable_styling()
Table 9:
Model assessment statistics
Asm. Measures 1se CART Tree Min CP CART Tree 1se Linear stepAIC Linear BAGGING Regression
2 Root Mean Square Error (RMSE) 1.6670934 1.6459090 1.7920570 1.7003021 1.4262659
3 Mean Absolute Error (MAE) 1.2933708 1.2571838 1.5385074 1.4111261 1.0760547
4 R Squared 0.2762515 0.2996549 0.1662987 0.2512238 0.4795678

4.1.2 Variable Importance

A summary of the variable importance for both the BAGGING regression is included below in Figure 13. Unlike what was seen with the CART regression models, the BAGGING regression model places significantly greater importance on all variables in the data set.

bag.import <- varImp(bag.final)

get.bagging.importance <- function(model) {
  trees <- model$mtrees
  
  imp <- numeric(length(trees[[1]]$btree$variable.importance))
  names(imp) <- names(trees[[1]]$btree$variable.importance)
  
  for(tree in trees){
    imp[names(tree$btree$variable.importance)] <- imp[names(tree$btree$variable.importance)] + tree$btree$variable.importance
  }
  
  imp <- imp/length(trees)
  return(imp)
}

bag.imp.sc <- sort(get.bagging.importance(bag.final), decreasing = T)
par(oma = c(5,2,3,2))
barplot(bag.imp.sc,
        horiz = T,
        main = list("Bagging Regression Tree Model", font = 1),
        xlab = "Importance",
        las = 1)
mtext("Variable Importance", side = 3, line = 1, outer = T, cex = 1.5, font = 2)
mtext("Figure 13:
Variable importance plots for BAGGING Regression tree model", side = 1, line = 1, outer = T)

4.2 BAGGING Classification

As the data was preemptively split into training and test data, the training data was utilized for 10-fold cross validation as part of the initial BAGGING classification model creation process. This cross validation ensures the produced model is the best fit for the training data as provided. Initially the hyperparameters were initiated as multiple values with a for loop to identify the best fit parameter values:

  • nbagg = 10 & 25
    • number of bagged trees
  • minimum split = 5 & 10
    • minimum number of variables required in a node prior to split attempts
  • complexity parameter (cp) = 0.01 & 0.001
    • minimum improvement in target parameter to allow for split
  • maximum depth = 5 & 10
    • maximum number of levels in tree output
baghp.grid <- expand.grid(
  nbagg = c(25, 50),
  minsplit = c(5, 10),
  maxdepth = c(5, 10),
  cp = c(0.01, 0.001)
)

bag.results <- data.frame(NULL)
bag.ac <- 0
bag.param <- list(NULL)

for(i in 1:nrow(baghp.grid)) {
  params <- baghp.grid[i,]
  
  rpart.control <- rpart.control(
    minsplit = params$minsplit,
    maxdepth = params$maxdepth,
    cp = params$cp
  )
  
  bag.model <- bagging(Ob.Rank ~., ob.train1,
                       nbagg = params$nbagg,
                       coob = T,
                       control = rpart.control
                       )
  
  bag.preds <- predict(bag.model, newdata = ob.test1)
  
  bag.conf <- confusionMatrix(bag.preds, ob.test1$Ob.Rank)
  accuracy <- bag.conf$overall["Accuracy"]
  
  bag.results <- rbind(bag.results, data.frame(
    nbagg = params$nbagg,
    minsplit = params$minsplit,
    maxdepth = params$maxdepth,
    cp = params$cp,
    Accuracy = accuracy
  ))
  
  if(accuracy > bag.ac){
    bag.ac <- accuracy
    bag.param <- params
  }
}
kable(bag.param, caption = "Table 10:
      <br>A summary of best hyperparameter values.") %>% 
  kable_styling
Table 10:
A summary of best hyperparameter values.
nbagg minsplit maxdepth cp
16 50 10 10 0.001

The BAGGING classification model was then utilized to generate prediction values for obesity ranks in utilizing the test data set. These predicted values were compared to the actual values in the test data set to assess for model accuracy and reliability. This BAGGING classification model was additionally compared to the previously constructed CART Gini classification model and the previously utilized multinomial regression model to determine their efficacy in relation to other more established statistical prediction methods.

A confusion matrix of the predicted obesity rank values produced by the BAGGING classification model compared to the test data values is included below in Table 11.

best.control <- rpart.control(
  minsplit = bag.param$minsplit,
  maxdepth = bag.param$maxdepth,
  cp = bag.param$cp
)

final.bag.clas <- bagging(
  Ob.Rank ~., ob.train1,
  nbagg = bag.param$nbagg,
  coob = T,
  control = best.control
)

bag.c.preds <- predict(final.bag.clas, newdata = ob.test1)

final.conf <- confusionMatrix(bag.c.preds, ob.test1$Ob.Rank)

kable(final.conf$table, col.names = c("Predicted Values", "Actual Insf. Weight", "Actual Normal Weight", "Actual Obesity 1", "Actual Obesity 2", "Actual Obesity 3", "Actual Overweight 1", "Actual Overweight 2"), caption = "Table 11:
<br>Confusion matrix of BAGGING classification predicted obesity rank values.") %>% 
  kable_styling() %>% 
  scroll_box("100%")
Table 11:
Confusion matrix of BAGGING classification predicted obesity rank values.
Predicted Values Actual Insf. Weight Actual Normal Weight Actual Obesity 1 Actual Obesity 2 Actual Obesity 3 Actual Overweight 1 Actual Overweight 2
Insufficient_Weight 50 5 0 0 0 4 2
Normal_Weight 9 36 4 1 0 8 5
Obesity_Type_I 2 5 60 0 0 4 4
Obesity_Type_II 0 1 2 61 0 0 2
Obesity_Type_III 0 2 1 0 59 0 0
Overweight_Level_I 1 3 0 0 0 33 1
Overweight_Level_II 1 4 7 3 0 2 41
#make ROC curves
roc.bag <- roc(ob.test1$Ob.Rank, as.numeric(bag.c.preds))

#sensitivities
bag.sen <- roc.bag$sensitivities

#1 - specificities
bag.spe <- (1 - roc.bag$specificities)

#AUC
bag.auc <- ROC.class.1se$auc

#best cutoffs
bag.cp <- coords(roc.bag, "best", ret = c("threshold", "sensitivity", "specificity"))

cutpoffs.b <- NULL
cutoffs.b <- data.frame(
  measure = c("Threshold", "Sensitivity", "Specificity", "AUC"),
  bag.cp = c(bag.cp[,1], bag.cp[,2], bag.cp[,3], bag.auc)
)

4.2.1 ROC Curve and AUC Evaluation

In order to compare the efficacy of the model, an ROC plot was constructed and area under the curve (AUC) values were calculated in the below Figure 13. Optimal cutoff points to maximize threshold, sensitivity, and specificity were calculated and plotted in the below ROC plot. These cutoff values are additionally listed in Table 12 for ease of reference. While neither model proved to incredibly reliable, the BAGGING classification returned the best AUC value while retaining the highest possible sensitivity and specificity.

par(oma = c(6,2,2,2), cex.main = 2)
plot(roc.c1.spe, roc.c1.sen, type = "l", xlim = c(0,1), xlab = "1 - Specificity (False Positive)", ylim = c(0,1), ylab = "Sensitivity (True Positive)", main = "ROC Curves", col = "darkviolet") +
  lines(roc.cm.spe, roc.cm.sen, col = "blue3") +
  lines(roc.log.spe, roc.log.sen, col = "coral3") +
  lines(bag.spe, bag.sen, col = "yellow4") +
  lines(x = c(0:1), y = c(0,1), col = "darkred", lty = 3) +
  points((1-c1.cp[1,3]), c1.cp[1,2], pch = 21, bg = adjustcolor("darkviolet", alpha = 0.5), cex = 1.75) +
  points((1-cm.cp[1,3]), cm.cp[1,2], pch = 22, bg = adjustcolor("blue3", alpha = 0.5), cex = 1.5) +
  points((1-l1.cp[1,3]), l1.cp[1,2], pch = 23, bg = adjustcolor("coral3", alpha = 0.5), cex = 1.25) +
  points((1-bag.cp[1,3]), bag.cp[1,2], pch = 24, bg = adjustcolor("yellow4", alpha = 0.5), cex = 1.5)
FALSE integer(0)
  legend("bottomright", c(paste("Tree.1se ", "AUC =", round(roc.c1.auc, 4)),
                          paste("Tree.min ", "AUC =", round(roc.cm.auc, 4)),
                          paste("Logi.1se ", "AUC =", round(roc.log.auc, 4)),
                          paste("BAGGING", "AUC =", round(bag.auc, 4))),
         col = c("darkviolet", "blue3", "coral3", "yellow4"),
         lwd = 2,
         lty = 1,
         cex = 0.8,
         bty = "o")
mtext("Figure 13: 
ROC Comparrison of BAGGING classification model, 
CART Gini classification trees & multinomial 
regression model", side = 1, line = 7.5, outside = T)

cutoffs <- cbind(cutoffs.c, cutoffs.b[2])

kable(cutoffs, align = c("l", "c", "c", "c", "c"), col.names = c("Cutoff Details", "Class 1se Model", "Class Min Model", "Logistic Model 1se Vars", "BAGGING Class Model"), caption = "Table 12:
      <br>A summary of calculated best/maximum cutoff points & AUC values") %>% 
  kable_styling()
Table 12:
A summary of calculated best/maximum cutoff points & AUC values
Cutoff Details Class 1se Model Class Min Model Logistic Model 1se Vars BAGGING Class Model
Threshold 0.2582288 0.1588050 0.2118908 1.5000000
Sensitivity 0.5178571 0.5714286 0.5535714 0.9107143
Specificity 0.8412698 0.8253968 0.6507937 0.7936508
AUC 0.6502268 0.6676587 0.5847506 0.6502268

5 Conclusions

Overall, the BAGGING regression and classification models produced the most reliable predictions for individuals’ obesity ratings when omitting weight data. This approach introduces randomness to the models as well, further improving the models’ performance as new test data is introduced. Additionally, treating the obesity rank values as categorical proved to yield more accurate prediction results, therefore when building future prediction models for obesity rank this approach should be incorporated.

While these models are far from perfect in their predictive capabilities, they provide a starting point to identify both individuals that are overweight and or obese that would not traditionally seek routine medical care due to financial or societal barriers through utilization of simple web surveys. Additionally, as weight was removed from these prediction models, they can also be utilized to identify individuals that may not yet be overweight or obese, but their behavior and dietary choices align with those that are overweight or obese, acting essentially as an obesity predictor. As weight is a sensitive subject for most adults, it is ideal that these prediction models do not include participant weight removing the potential for deceptive answers and encouraging survey completion. Through these identification and obesity prediction methods, communities can be targeted for outreach programs to increase access to care or targeted marketing to promote healthy lifestyles promotions (i.e. gym membership discounts from helth insurance providers).

LS0tDQp0aXRsZTogIkJpZyBCYWNrIEJlaGF2aW9yIDxpbWcgc3JjPVwiaHR0cHM6Ly9ubGVwZXJhLmdpdGh1Yi5pby9zdGE1NTEvSFcwMS9pbWcvcGVuZ3Vpbl9jdXRlLnBuZ1wiIHN0eWxlPVwiZmxvYXQ6IHJpZ2h0OyB3aWR0aDogMTIlXCIvPiINCnN1YnRpdGxlOiAiUHJlZGljdGluZyBvYmVzaXR5IHJhbmtpbmdzIGJhc2VkIG9uIGJlaGF2aW9yYWwgc3VydmV5IHJlc3VsdHMiDQphdXRob3I6DQotIG5hbWU6IE5hdGFsaWUgTGVQZXJhDQogIGFmZmlsaWF0aW9uOiBXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSB8IFNUQTU1MiAtIEhXIDA1DQpkYXRlOiAiMDYgTWF5IDIwMjUiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICB0b2NfY29sbGFwc2U6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGZpZ19hbGlnbjogY2VudGVyDQogICAgZGZfcHJpbnQ6IGthYmxlDQotLS0NCg0KYGBge2NzcywgZWNobyA9IEZBTFNFfQ0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgY29sb3I6IGRhcmttYWdlbnRhIDsNCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQpoMS5zdWJ0aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXdlaWdodDpib2xkOw0KICBjb2xvcjogZGFya21hZ2VudGEgOw0KICBmb250LWZhbWlseTogdmVyZGFuYTsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQogIGNvbG9yOiBuYXZ5Ow0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KICBjb2xvcjogbmF2eTsNCn0NCmgxIHsgLyogSGVhZGVyIDEgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAxIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICBmb250LWZhbWlseTogdmVyZGFuYTsNCn0NCmgyIHsgLyogSGVhZGVyIDIgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBsZXZlbCAyIHNlY3Rpb24gdGl0bGUgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICBmb250LWZhbWlseTogdmVyZGFuYTsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCi5ocmVkIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpib2R5IHsNCiAgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsNCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCn0NCg0KLmhpZ2hsaWdodG1lIHsgDQogIGJhY2tncm91bmQtY29sb3I6eWVsbG93OyANCn0NCg0KcCB7IA0KICBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyANCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCmg1IHsNCiAgY29sb3I6IG5hdnk7DQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KfQ0KDQouaWZyYW1lIHsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQphOmxpbmsgew0KICBjb2xvcjogZGFya21hZ2VudGE7DQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KfQ0KDQouZmlnbGFiZWwgew0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGNvbG9yOiBzbGF0ZWdyYXk7DQogIGZvbnQtc3R5bGU6IGl0YWxpYzsNCiAgZm9udC1zaXplOiAxODsNCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCi50ZDEgew0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCnRoLCB0ZCB7DQogIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZGRkOw0KICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQp0cjpob3ZlciB7YmFja2dyb3VuZC1jb2xvcjogY29yYWw7fQ0KYGBgDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQppZiAoIXJlcXVpcmUoImRwbHlyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJkcGx5ciIpDQp9DQppZiAoIXJlcXVpcmUoIm1pc3R5IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJtaXN0eSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtaXN0eSIpDQp9DQoNCmlmICghcmVxdWlyZSgicGx5ciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicGx5ciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwbHlyIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJzdHJpbmdyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJzdHJpbmdyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInN0cmluZ3IiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBsb3RseSIpDQp9DQoNCmlmICghcmVxdWlyZSgiTUFTUyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiTUFTUyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJNQVNTIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJncmlkRXh0cmEiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdyaWRFeHRyYSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJncmlkRXh0cmEiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJubmV0IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJubmV0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIm5uZXQiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImdyaWQiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdyaWQiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ3JpZCIpDQp9DQppZiAoIXJlcXVpcmUoInJhc3RlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicmFzdGVyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInJhc3RlciIpDQp9DQppZiAoIXJlcXVpcmUoInJhdHRsZSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicmF0dGxlIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInJhdHRsZSIpDQp9DQppZiAoIXJlcXVpcmUoInBST0MiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBST0MiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicFJPQyIpDQp9DQppZiAoIXJlcXVpcmUoImdncmlkZ2VzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JpZGdlcyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ3JpZGdlcyIpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJrbml0ciIpDQp9DQppZiAoIXJlcXVpcmUoIkdHYWxseSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIkdHYWxseSIpDQp9DQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dscG90MiIpDQp9DQppZiAoIXJlcXVpcmUoImNsdXN0ZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNsdXN0ZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiY2x1c3RlciIpDQp9DQppZiAoIXJlcXVpcmUoImthYmxlRXh0cmEiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImthYmxlRXh0cmEiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgia2FibGVFeHRyYSIpDQp9DQppZiAoIXJlcXVpcmUoImZvcmNhdHMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImZvcmNhdHMiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZm9yY2F0cyIpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJycGFydCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJycGFydCIpDQp9DQppZiAoIXJlcXVpcmUoInJwYXJ0LnBsb3QiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInJwYXJ0LnBsb3QiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicnBhcnQucGxvdCIpDQp9DQppZiAoIXJlcXVpcmUoIm1ldGFuIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJtZXRhbiIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtZXRhbiIpDQp9DQogaWYgKCFyZXF1aXJlKCJNb2RlbE1ldHJpY3MiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiTW9kZWxNZXRyaWNzIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoIk1vZGVsTWV0cmljcyIpDQogfQ0KIGlmICghcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJwYW5kZXIiKQ0KIH0NCg0KIGlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoImdncGxvdDIiKQ0KIH0NCg0KIGlmICghcmVxdWlyZSgiZTEwNzEiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZTEwNzEiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgiZTEwNzEiKQ0KIH0NCiBpZiAoIXJlcXVpcmUoInByZXNlbnRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwcmVzZW50ZXIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgicHJlc2VudGVyIikNCiB9DQogaWYgKCFyZXF1aXJlKCJtaWNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm1pY2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgibWljZSIpDQogfQ0KIGlmICghcmVxdWlyZSgiZ2dtaWNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImdnbWljZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJnZ21pY2UiKQ0KIH0NCmlmICghcmVxdWlyZSgiY29ycnBsb3QiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJjb3JycGxvdCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIGxpYnJhcnkoImNvcnJwbG90IikNCn0NCg0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImNhcmV0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgbGlicmFyeSgiY2FyZXQiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImdncHViciIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImdncHViciIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIGxpYnJhcnkoImdncHViciIpDQp9DQoNCmlmICghcmVxdWlyZSgiZ2xtbmV0IikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2xtbmV0IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgbGlicmFyeSgiZ2xtbmV0IikNCn0NCmlmICghcmVxdWlyZSgiaXByZWQiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJpcHJlZCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIGxpYnJhcnkoImlwcmVkIikNCn0NCg0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHQgPSBUUlVFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29uc29sZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICdjZW50ZXInKQ0KDQpvcHRpb25zKERULm9wdGlvbnMgPSBsaXN0KHBhZ2VMZW5ndGggPSA1LCBzY3JvbGxYID0gVFJVRSkpDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24NCg0KT2Jlc2l0eSBpcyBkZWZpbmVkIGJ5IHRoZSBDREMgYXMgImhhdmluZyBhIGJvZHkgbWFzcyBpbmRleCAoQk1JKSBvZiAzMC4wIG9yIGhpZ2hlciwgd2l0aCBzZXZlcmUgb2Jlc2l0eSBiZWluZyBkZWZpbmVkIGFzIGEgQk1JIG9mIDQwLjAgb3IgaGlnaGVyIiBbMV0uICAgVGhlIHByZXZhbGVuY2Ugb2YgYm90aCBhZHVsdCBhbmQgY2hpbGRob29kIG9iZXNpdHkgYW5kIHNldmVyZSBvYmVzaXR5IGhhdmUgYmVlbiByaXNpbmcgc3RlYWRpbHkgc2luY2UgMTk5MCBbMSwgMiwgM10uIEluIDIwMjQsIGEgc3RhZ2dlcmluZyAzNSBtaWxsaW9uIGNoaWxkcmVuIDxiPnVuZGVyIHRoZSBhZ2Ugb2YgNTwvYj4gd2VyZSBjb25zaWRlcmVkIG92ZXJ3ZWlnaHQgYWNjb3JkaW5nIHRvIHRoZSBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uLiAgV2hpbGUgb25jZSB0aG91Z2h0IHRvIGJlIGEgc3ltcHRvbSBvZiBleGNlc3MsIG9iZXNpdHkgcmF0ZXMgaGF2ZSBiZWVuIHJpc2luZyBpbiBkZXZlbG9waW5nIGNvdW50cmllcywgaW5kaWNhdGluZyB0aGUgc2V2ZXJpdHkgb2YgdGhlIG9iZXNpdHkgZXBpZGVtaWMgd29ybGR3aWRlLiBJbiBBZnJpY2EgYWxvbmUgdGhlIG51bWJlciBvZiBvdmVyd2VpZ2h0IGNoaWxkcmVuIHVuZGVyIDUgeWVhcnMgb2YgYWdlIHJvc2UgYSBzdGFnZ2VyaW5nIDEyLjElIHNpbmNlIDIwMDAgWzNdLg0KDQpBIG5vdmVsIGNoYWxsZW5nZSBoYXMgYmVlbiByYWlzZWQgYnkgdGhlc2UgZ2xvYmFsIHNwaWtlcyBpbiBvYmVzaXR5OyBjaGlsZHJlbiBhcmUgZmFjaW5nIG9iZXNpdHkgYW5kIG1hbG51dHJpdGlvbiBzaW11bHRhbmVvdXNseS4gIER1ZSB0byB0aGUgaW5jcmVhc2UgaW4gZXhwb3N1cmUgdG8gcG9vciBudXRyaWVudCBxdWFsaXR5LCBsb3cgY29zdCwgaGlnaCBmYXQgYW5kIGhpZ2ggc3VnYXIgZm9vZHMgcGFpcmVkIHdpdGggbG93ZXJlZCBwYXJ0aWNpcGF0aW9uIGluIHBoeXNpY2FsIGV4ZXJjaXNlLCBjaGlsZHJlbiBhcmUgZGV2ZWxvcGluZyBlbGV2YXRlZCB3ZWlnaHRzIHdoaWxlIHNpbXVsdGFuZW91c2x5IGV4cGVyaWVuY2luZyBtYWxudXRyaXRpb24gWzNdLiAgRHVlIHRvIHRoZSBkZWNyZWFzZWQgYWNjZXNzIHRvIGhlYWx0aHkgZm9vZHMsIHRoZSBvYmVzaXR5IHJhdGUgaW4gY2hpbGRyZW4gaW5jcmVhc2VzIHRoZSBjbG9zZXIgdGhlaXIgZmFtaWx5IGdldHMgdG8gdGhlIGZlZGVyYWwgcG92ZXJ0eSBsaW5lLiAgQWNjb3JkaW5nIHRvIENEQyBhbmQgdGhlIFVTIERlcGFydG1lbnQgb2YgQWdyaWN1bHR1cmUsIGN1cnJlbnRseSBhdCBsZWFzdCAyNS44JSBvZiBjaGlsZHJlbiBsaXZpbmcgd2l0aCBhIGZhbWlseSBpbmNvbWUgYXQgMTMwJSBvZiB0aGUgZmVkZXJhbCBwb3ZlcnR5IGxpbmUgb3IgbG93ZXIgYXJlIGV4cGVyaWVuY2luZyBvYmVzaXR5IFsyXS4NCg0KT2Jlc2l0eSBjYXJlIHBsYWNlcyBhIG1hc3NpdmUgYnVyZGVuIG9uIGhvc3BpdGFscywgaGVhbHRoY2FyZSBpbnN0aXR1dGlvbnMsIGFuZCBoZWFsdGhjYXJlIHN5c3RlbXMgYXMgYSB3aG9sZSBkdWUgdG8gaXRzIGhpZ2ggY29tb3JiaWRpdHkgcmF0ZXMgWzEsIDIsIDNdLiBBY2NvcmRpbmcgdG8gdGhlIFdITywgaW4gMjAyMSwgZWxldmF0ZWQgQk1JIHdhcyBsaW5rZWQgdG8gYXBwcm94aW1hdGVseSAzLjcgbWlsbGlvbiBkZWF0aHMgZnJvbSBub24tY29tbXVuaWNhYmxlIGRpc2Vhc2VzIGluY2x1ZGluZyBidXQgbm90IGxpbWl0ZWQgdG8gaGVhcnQgZGlzZWFzZXMsIGRpYWJldGVzLCBjYW5jZXJzLCBkaWdlc3RpdmUgZGlzb3JkZXJzLCBjaHJvbmljIHJlc3BpcmF0b3J5IGRpc2Vhc2VzLCBhbmQgbmV1cm9sb2dpY2FsIGRpc29yZGVycyBbM10uICBJbiAyMDE3IHRoZSBDREMgZm91bmQgdGhhdCA1OCUgb2YgYWR1bHRzIGluIHRoZSBVbml0ZWQgU3RhdGVzIHRoYXQgYXJlIGNvbnNpZGVyZWQgb2Jlc2UgYWxzbyBoYXZlIGNvbW9yYmlkIGhpZ2ggYmxvb2QgcHJlc3N1cmUsIGFuZCAyMyUgaGF2ZSBjb21vcmJpZCBkaWFiZXRlcy4gQWRkaXRpb25hbGx5LCB0aGUgQ0RDIGlkZW50aWZpZWQgdGhhdCBpbiAyMDE5IGFubnVhbCBtZWRpY2FsIGNvc3RzIGZvciB0aG9zZSB3aXRoIG9iZXNpdHkgd2VyZSBvbiBhdmVyYWdlIFwkMSw4NjEgaGlnaGVyIHBlciBwZXJzb24gcGVyIHllYXIgZm9yIHRob3NlIHdpdGggb2Jlc2l0eSwgYW5kIFwkMywwOTcgcGVyIHBlcnNvbiBwZXIgeWVhciBmb3IgdGhvc2Ugd2l0aCBzZXZlcmUgKGEuay5hLiBtb3JiaWQpIG9iZXNpdHkuIA0KDQpBcyBvdXRsaW5lZCBieSB0aGUgVUNvbm4gUnVkZCBDZW50ZXIgZm9yIEZvb2QgUG9saWN5IGFuZCBIZWFsdGggdGhvc2Ugd2l0aCBvdmVyd2VpZ2h0IG9yIG9iZXNlIHdlaWdodCBzdGF0dXMgZXhwZXJpZW5jZSB3ZWlnaHQgYmFzZWQgc3RpZ21hIGluIGEgYnJvYWQgdmFyaWV0eSBvZiBsaWZlIGFzcGVjdHMuIE1hbnkgcGVyc29ucyB3aXRoIG9iZXNpdHkgaGF2ZSBnb25lIGFzIGZhciBhcyByZXF1ZXN0aW5nIHdlaWdodCBubyBsb25nZXIgYmUgY29sbGVjdGVkIGF0IHJvdXRpbmUgbWVkaWNhbCBhcHBvaW50bWVudHMgaW4gYXR0ZW1wdCB0byBjb21iYXQgdGhlc2Ugc3RpZ21hcyBhbmQgcmVkdWNlIHBvdGVudGlhbCBpbmVxdWFsaXRpZXMgaW4gaGVhbHRoY2FyZSBbNF0uIFdoaWxlIHRoaXMgYmFuZC1haWQgYXBwcm9hY2ggc2Vla3MgdG8gcmV0dXJuIGNvbWZvcnQgYW5kIGRpZ25pdHkgdG8gdGhlIHBhdGllbnQsIHRoZSBjYXRlZ29yaXphdGlvbiBvZiBwYXRpZW50cyBpbnRvIHdlaWdodCByYW5raW5ncyBpcyBwaXZvdGFsIGZvciB0YXJnZXRlZCBwcmV2ZW50YXRpdmUgY2FyZSBhbmQgcmVkdWNpbmcgZnV0dXJlIGhlYWx0aGNhcmUgYnVyZGVuIGZvciBwYXRpZW50cyBhbmQgdGhlIGhlYWx0aGNhcmUgc3lzdGVtIGFzIGEgd2hvbGUuICBJbiBvcmRlciB0byBlbnN1cmUgYWRlcXVhdGUgY2FyZSBpcyBpbml0aWF0ZWQgYXQgdGhlIGFwcHJvcHJpYXRlIHRpbWUsIHJlc2VhcmNoZXJzIGhhdmUgc2V0IG91dCB0byBpZGVudGlmeSBhbHRlcm5hdGl2ZSB3YXlzIHRvIHByZWRpY3QgYSBwYXRpZW50J3Mgd2VpZ2h0IHN0YXR1cyBvciBmdXR1cmUgd2VpZ2h0IHN0YXR1cy4gIFdoaWxlIHNvbWUgcGF0aWVudHMgbWF5IG5vdCBjdXJyZW50bHkgYmUgb3ZlcndlaWdodCBvciBvYmVzZSwgdGhlaXIgYmVoYXZpb3JzIGFuZCBkYWlseSBhY3Rpdml0aWVzIG1heSBpbmRpY2F0ZSB0aGV5IGFyZSBvbiB0cmFjayB0byBkZXZlbG9wIG92ZXJ3ZWlnaHQgb3Igb2Jlc2Ugd2VpZ2h0IHN0YXR1cywgZnVydGhlciB1bmRlcnBpbm5pbmcgdGhlIG5lZWQgZm9yIHdlaWdodCBiYXNlZCBwcmVkaWN0aW9ucyB0aGF0IGRvIG5vdCByZWx5IG9uIHBhdGllbnRzJyBjdXJyZW50IHdlaWdodC4NCg0KDQpgYGB7cn0NCm9iZXNlIDwtIHJlYWQuY3N2KCJodHRwczovL25sZXBlcmEuZ2l0aHViLmlvL3N0YTU1Mi9IVzA1L2RhdGEvb2Jlc2l0eS5jc3YiKQ0KDQpjb2xuYW1lcyhvYmVzZSlbY29sbmFtZXMob2Jlc2UpID09ICJmYW1pbHlfaGlzdG9yeV93aXRoX292ZXJ3ZWlnaHQiXSA8LSAiRkguT3ZlcndlaWdodCINCmNvbG5hbWVzKG9iZXNlKVtjb2xuYW1lcyhvYmVzZSkgPT0gIk5PYmV5ZXNkYWQiXSA8LSAiT2IuUmFuayINCmBgYA0KDQojIyBEYXRhIENvbGxlY3Rpb24gYW5kIFN1bW1hcnkgRGV0aWFscw0KDQpJbiBvcmRlciB0byBhZGRyZXNzIHRoaXMgcHJvYmxlbSBvZiBiZWhhdmlvcmFsIGJhc2VkIHdlaWdodCBzdGF0dXMgaWRlbnRpZmljYXRpb24sIHRoZSByZXNlYXJjaGVycyBhdCBVbml2ZXJzaWRhZCBkZSBsYSBDb3N0YSAoQ1VDKSBjb21waWxlZCBwZXJzb25hbCBoZWFsdGggYW5kIGRhaWx5IGJlaGF2aW9yYWwgZGF0YSBmcm9tIHBlcnNvbnMgYWdlcyAxNCB0aHJvdWdoIDYxIGluIE1leGljbywgUGVydSwgYW5kIENvbHVtYmlhLiAgUGFydGljaXBhbnRzIHdlcmUgcHJlc2VudGVkIHdpdGggYSB3ZWIgc3VydmV5IHRvIGFub255bW91c2x5IGNhcHR1cmUgdGhlaXIgZWF0aW5nIGhhYml0cywgYmVoYXZpb3JhbCBjaG9pY2VzLCBhbmQgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24uICBUaGlzIHN1cnZleSBjb2xsZWN0aW9uIHJlc3VsdGVkIGluIDIxMTEgdW5pcXVlIG9ic2VydmF0aW9ucyBvdmVyIDE2IHZhcmlhYmxlcywgYW5kIHRoZSBzdWJzZXF1ZW50IGNhbGN1bGF0aW9uIG9mIDEgdmFyaWFibGUgdG8gY2xhc3NpZnkgdGhlIHJlc3BvbmRlbnQncyBvYmVzaXR5IHJhbmsgWzVdLiBNb3JlIGluZm9ybWF0aW9uIG9uIHRoZSB2YXJpYWJsZXMgY29sbGVjdGVkIGlzIGluY2x1ZGVkIGJlbG93IGluIGBUYWJsZSAxYA0KDQpgYGB7cn0NCnZhcnMgPC0gZGF0YS5mcmFtZSgNCiAgTmFtZSA9IGMoIkdlbmRlciIsICJBZ2UiLCAiSGVpZ2h0IiwgIldlaWdodCIsICJGYW1pbHkgSGlzdG9yeSBPdmVyd2VpZ2h0IiwgIkhpZ2ggQ2Fsb3JpZSBGb29kcyBPZnRlbiIsICJWZWdldGFibGVzIGluIE1lYWxzIiwgIk1lYWxzIFBlciBEYXkiLCAiRm9vZCBCZXR3ZWVuIE1lYWxzIiwgIlNtb2tpbmcgU3RhdHVzIiwgIkRhaWx5IFdhdGVyIENvbnN1bXB0aW9uIiwgIkNhbG9yaWUgQ291bnRpbmciLCAiUGh5c2ljYWwgQWN0aXZpdHkgUmF0ZSIsICJUZWNobm9sb2d5IFVzZSIsICJBbGNvaG9sIiwgIlRyYW5zcG9ydGF0aW9uIiwgIk9iZXNpdHkgTGV2ZWwiKSwNCiAgVHlwZSA9IGMoIkNvbnRpbnVvdXMiLCAiQ29udGludW91cyIsICJDb250aW51b3VzIiwgIkNvbnRpbnVvdXMiLCAiQmluYXJ5IiwgIkJpbmFyeSIsICJDb250aW51b3VzIiwgIkNvbnRpbnVvdXMiLCAiQ2F0ZWdvcmljYWwiLCAiQmluYXJ5IiwgIkNvbnRpbnVvdXMiLCAiQmluYXJ5IiwgIkNvbnRpbnVvdXMiLCAiQ29udGludW91cyIsICJDYXRlZ29yaWNhbCIsICJDYXRlZ29yaWNhbCIsICJDYXRlZ29yaWNhbCIpLA0KICBRdWVzdGlvbnMgPSBjKCIiLCAiIiwgIiIsICIiLCAiSGFzIGEgZmFtaWx5IG1lbWJlciBzdWZmZXJlZCBvciBzdWZmZXJzIGZyb20gb3ZlcndlaWdodD8iLCAiRG8geW91IGVhdCBoaWdoIGNhbG9yaWMgZm9vZCBmcmVxdWVudGx5PyIsICJEbyB5b3UgdXN1YWxseSBlYXQgdmVnZXRhYmxlcyBpbiB5b3VyIG1lYWxzPyIsICJIb3cgbWFueSBtYWluIG1lYWxzIGRvIHlvdSBoYXZlIGRhaWx5PyIsICJEbyB5b3UgZWF0IGFueSBmb29kIGJldHdlZW4gbWVhbHM/IiwgIkRvIHlvdSBzbW9rZT8iLCAiSG93IG11Y2ggd2F0ZXIgZG8geW91IGRyaW5rIGRhaWx5PyIsICJEbyB5b3UgbW9uaXRvciB0aGUgY2Fsb3JpZXMgeW91IGVhdCBkYWlseT8iLCAiSG93IG9mdGVuIGRvIHlvdSBoYXZlIHBoeXNpY2FsIGFjdGl2aXR5PyIsICJIb3cgbXVjaCB0aW1lIGRvIHlvdSB1c2UgdGVjaG5vbG9naWNhbCBkZXZpY2VzIHN1Y2ggYXMgY2VsbCBwaG9uZSwgdmlkZW9nYW1lcywgdGVsZXZpc2lvbiwgY29tcHV0ZXIgYW5kIG90aGVycz8iLCAiSG93IG9mdGVuIGRvIHlvdSBkcmluayBhbGNvaG9sPyIsICJXaGljaCB0cmFuc3BvcnRhdGlvbiBkbyB5b3UgdXN1YWxseSB1c2U/IiwgIiIpLA0KICBSZXNwb25zZXMgPSBjKCJNYWxlL0ZlbWFsZSIsICJBZ2UgKHkpIiwgIkhlaWdodCAobSkiLCAiV2VpZ2h0IChrZykiLCAiWWVzL05vIiwgIlllcy9ObyIsICIiLCAiIiwgIk5vL1NvbWV0aW1lcy9GcmVxdWVudGx5L0Fsd2F5cyIsICJZZXMvTm8iLCAiIiwgIlllcy9ObyIsICIiLCAiIiwgIk5ldmVyL1NvbWV0aW1lcy9GcmVxdWVudGx5L0Fsd2F5cyIsICJBdXRvL01vdG9yYmlrZS9CaWtlL1B1YmxpYyBUcmFucy9XYWxraW5nIiwgIkluc3VmLiBXZWlnaHQvTm9ybWFsIFdlaWdodC9PdmVyd2VpZ2h0IDEvT3ZlcndlaWdodCAyL09iZXNlIDEvT2Jlc2UgMi9PYmVzZSAzIiksDQogIFZhciA9IGMoIkdlbmRlciIsICJBZ2UiLCAiSGVpZ2h0IiwgIldlaWdodCIsICJmaC5vdmVyd2VpZ2h0IiwgIkZBVkMiLCAiRkNWQyIsICJOQ1AiLCAiQ0FFQyIsICJTTU9LRSIsICJDSDIwIiwgIlNDQyIsICJGQUYiLCAiVFVFIiwgIkNBTEMiLCAiTVRSQU5TIiwgIk9iLlJhbmsiKQ0KKQ0Ka2FibGUodmFycywgYWxpZ24gPSBjKCJsIiwgImMiLCAiYyIsICJjIiksIGNvbC5uYW1lcyA9IGMoIlZhcmlhYmxlIE5hbWUiLCAiVHlwZSIsICJBc3NvY2lhdGVkIFN1cnZleSBRdWVzdGlvbiIsICJSZXNwb25zZSBPcHRpb25zIC8gTGV2ZWxzIiwgIkNvZGVkIFZhci4gTmFtZSIpLCBjYXB0aW9uID0gIlRhYmxlIDE6DQogICAgICA8YnI+U3VtbWFyeSBkZXRhaWxzIG9mIGFsbCB2YXJpYWJsZXMgaW4gdGhlIG9iZXNpdHkgZGF0YS4iKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KDQpvYmVzZSA8LSBvYmVzZSAlPiUgbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgYXMuZmFjdG9yKQ0KICANCmBgYA0KDQojIyBBbmFseXNpcyBHb2Fscw0KDQpUaGUgZ29hbHMgb2YgdGhpcyBhbmFseXNpcyBhcmUgdG8gZGV2ZWxvcCBhbmQgdHVuZSB2YXJpb3VzIHByZWRpY3Rpb24gbW9kZWxzIHRvIHV0aWxpemUgdGhlIGRpZXRhcnksIGJlaGF2aW9yYWwsIGFuZCBkZW1vZ3JhcGhpYyBkYXRhICh3aXRob3V0IHBhdGllbnQgd2VpZ2h0KSB0byBwcmVkaWN0IGEgcGF0aWVudHMnIG92ZXJ3ZWlnaHQvb2Jlc2l0eSBzdGF0dXMuICBUaGlzIHdpbGwgYWxsb3cgcmVzZWFyY2hlcnMgdG8gYmV0dGVyIGlkZW50aWZ5IHBhdGllbnRzIHRoYXQgYXJlIG92ZXJ3ZWlnaHQvb2Jlc2UgdGhhdCBtYXkgbm90IG5vcm1hbGx5IGhhdmUgYWNjZXNzIHRvIHByZXZlbnRhdGl2ZSBoZWFsdGhjYXJlIGFuZCB0byBwcmVkaWN0IHBhdGllbnRzIHRoYXQgbWF5IG5vdCB5ZXQgYmUgc3VmZmVyaW5nIGZyb20gb3ZlcndlaWdodCBvciBvYmVzaXR5IHlldCB0aGVpciBiZWhhdmlvcnMgYW5kIGRpZXRhcnkgaGFiaXRzIHBsYWNlIHRoZW0gb24gdHJhY2sgdG8gZGV2ZWxvcGluZyB0aGVzZSBjb25kaXRpb25zLiANCg0KQXMgdGhlIG92ZXJ3ZWlnaHQvb2Jlc2l0eSByYW5rIGFzIGNhcHR1cmVkIGJ5IFBhbGVjaG9yIGFuZCBNYW5vdGFzIHdhcyBlbmNvZGVkIGFzIGEgZmFjdG9yIHZhcmlhYmxlIHdpdGggNyBsZXZlbHMgYm90aCBsaW5lYXIgYW5kIG11bHRpbm9taWFsIHJlZ3Jlc3Npb24gYXBwcm9hY2hlcyB3ZXJlIHV0aWxpemVkLiAgSW4gYWRkaXRpb24gdG8gc3RhbmRhcmQgcmVncmVzc2lvbiBhcHByb2FjaGVzLCBDQVJUIFJlZ3Jlc3Npb24sIENBUlQgQ2xhc3NpZmljYXRpb24sIEJBR0dJTkcgcmVncmVzc2lvbiwgYW5kIEJBR0dJTkcgY2xhc3NpZmljYXRpb24gd2VyZSBlbXBsb3llZCBmb3IgYXNzZXNzbWVudC4gDQoNCiMgRGF0YSBQcmVwYXJhdGlvbg0KDQpJbiBvcmRlciB0byBhY2NvbW1vZGF0ZSBmb3IgYm90aCBsaW5lYXIgcmVncmVzc2lvbiBhbmQgY2F0ZWdvcmljYWwgY2xhc3NpZmljYXRpb24vbXVsdGlub21pYWwgcmVncmVzc2lvbiB0aGUgZGF0YSB3YXMgY29waWVkIGluIHRvIGR1cGxpY2F0ZSBkYXRhIHNldHMuICBPbmUgZGF0YSBzZXQgcmV0YWluZWQgdGhlIGNhdGVnb3JpY2FsIHZhbHVlcyBhcyBmYWN0b3JzLCB3aGlsZSB0aGUgb3RoZXIgZmlyc3QgY29udmVydGVkIGNhdGVnb3JpY2FsIHZhbHVlcyB0byBmYWN0b3JzLCB0aGVuIGFnYWluIGNvbnZlcnRlZCB0aGVzZSB2YWx1ZXMgdG8gbnVtZXJpYyBmcm9tIGZhY3Rvci4gIFN1bW1hcnkgc3RhdGlzdGljcyBvZiB0aGUgZmFjdG9yIGJhc2VkIGRhdGEgc2V0IGFyZSBpbmNsdWRlZCBiZWxvdyBpbiBgVGFibGUgMmAuDQoNCkFzIHRoZSBwdXJwb3NlIG9mIHRoaXMgYW5hbHlzaXMgaXMgdG8gcHJlZGljdCB0aGUgcGFydGljaXBhbnQncyBvdmVyd2VpZ2h0L29iZXNlIHN0YXR1cyB3aXRob3V0IHV0aWxpemluZyBwYXJ0aWNpcGFudHMgd2VpZ2h0LCB0aGUgd2VpZ2h0IHZhcmlhYmxlIHdhcyBkcm9wcGVkIGZyb20gc3Vic2VxdWVudCBhbmFseXNpcyBmb2xsb3dpbmcgYFRhYmxlIDJgLiAgQWRkaXRpb25hbGx5LCBhIGN1cnNvcnkgZXZhbHVhdGlvbiBvZiBgVGFibGUgMmAgaW5kaWNhdGVzIHRoZXJlIGFyZSBubyBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgZGF0YSBzZXQsIHRoZXJlZm9yZSBpbXB1dGF0aW9uIGlzIG5vdCByZXF1aXJlZC4gDQoNCmBgYHtyfQ0Ka2FibGUoc3VtbWFyeShvYmVzZSksIGFsaWduID0gImMiICwgY2FwdGlvbiA9ICJUYWJsZSAyOg0KICAgICAgPGJyPlN1bW1hcnkgc3RhdGlzdGljcyBvZiBvYmVzaXR5IGRhdGEuIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkgJT4lIA0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KYGBgDQoNCiMjIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCg0KVGhlIHBydW5lZCBvYmVzaXR5IGRhdGEgd2FzIHRoZW4gYXNzZXNzZWQgZm9yIHBvdGVudGlhbCBjb3JyZWxhdGlvbiBiZXR3ZWVuIGFsbCB2YXJpYWJsZXMuICBUaGUgYmVsb3cgYEZpZ3VyZSAxYCBkZW1vbnN0cmF0ZXMgYm90aCB0aGUgbWFnbml0dWRlIGFuZCBkaXJlY3Rpb24gdGhyb3VnaCB0aGUgcmVzcGVjdGl2ZSBzaXplcyBhbmQgY29sb3JzIG9mIHRoZSBwb3R0ZWQgY2lyY2xlcy4gIFZhcmlhYmxlcyBzaGFyaW5nIGEgcGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCAocikgb2YgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDAuNjAgYXJlIGNvbnNpZGVyZWQgdG8gaGF2ZSBhdCBsZWFzdCBtb2RlcmF0ZSBjb3JyZWxhdGlvbi4gIEEgdG90YWwgb2YgZm91ciAoNCkgdmFyaWFibGVzIGluIHR3byAoMikgdW5pcXVlIGNvbWJpbmF0aW9ucyB3ZXJlIGlkZW50aWZpZWQgYXMgYXQgbGVhc3QgbW9kZXJhdGVseSBjb3JyZWxhdGVkLCBhcyBjYXB0dXJlZCBiZWxvdyBpbiBgRmlndXJlIDJgIGFuZCBgVGFibGUgM2AuIA0KDQpgYGB7cn0NCiNkcm9wIHdlaWdodCBmcm9tIG9iZXNlDQpvYmVzZSA8LSBvYmVzZVstNF0NCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPSA4LCBmaWcuaGVpZ2h0PTEwfQ0KI2NyZWF0ZSBhbGwgbnVtZXJpYyB2ZXJzaW9uIG9mIG9iZXNlIGZvciBjb3JyZWxhdGlvbiBwbG90IA0Kb2Jlc2UubnVtZXJpYyA8LSBvYmVzZSAlPiUgbXV0YXRlX2lmKGlzLmZhY3RvciwgYXMubnVtZXJpYykNCg0KY29yLm9iZXNlIDwtIGNvcihvYmVzZS5udW1lcmljKQ0KDQpjb3JycGxvdChjb3Iub2Jlc2UsIHR5cGUgPSAidXBwZXIiLCBvcmRlciA9ICJoY2x1c3QiLCB0bC5jb2wgPSAiYmxhY2siLCB0bC5zcnQgPSA2NSwgZGlhZyA9IFQpDQp0aXRsZShtYWluID0gIlZhcmlhYmxlIENvcnJlbGF0aW9uIiwgbGluZSA9IC0zLjc1LCBjZXgubWFpbiA9MikNCnRpdGxlKHN1YiA9ICJGaWd1cmUgMToNClBhaXJ3aXNlIGNvcnJlbGF0aW9uIHZpc3VhbGl6YXRpb24gb2YgYWxsIHZhcmlhYmxlcyBpbiBvYmVzZSBkYXRhLiBDaGFyYWN0ZXIgdmFyaWFibGVzIA0Kd2VyZSB0cmFuc2Zvcm1lZCB0byBmYWN0b3JzIHRoZW4gbnVtZXJpYy4NClNpemUgYW5kIGNvbG9yIG9mIGNpcmNsZXMgYXJlIGRldGVybWluZWQgYnkgc3RyZW5ndGggYW5kIGRpcmVjdGlvbiBvZiBjb3JyZWxhdGlvbi4iLCBsaW5lID0gMCwgY2V4LnN1YiA9IDAuODUpDQpgYGANCg0KYGBge3J9DQojc2V0IHVwIGNvcnJlbGF0aW9uIHRhYmxlIGZvciBmaWx0ZXJpbmcNCmNvci50YWJsZSA8LSBkYXRhLmZyYW1lKA0KICByb3cgPSByb3duYW1lcyhjb3Iub2Jlc2UpW3Jvdyhjb3Iub2Jlc2UpXSwNCiAgY29sID0gY29sbmFtZXMoY29yLm9iZXNlKVtjb2woY29yLm9iZXNlKV0sDQogIGNvciA9IGMoY29yLm9iZXNlKQ0KKQ0KDQojZmlsdGVyIGZvciAwLjYgPCBjb3IgPCAxLjAgdG8gZmlsdGVyIGZvciBoaWdoIGNvciBidXQgaWdub3JlIGNvciA9IDEgKHNhbWUgdmFyaWFibGUgbWF0Y2gpDQpjb3IudDIgPC0gZmlsdGVyKGNvci50YWJsZSwgYWJzKGNvcikgPj0gMC42KSAlPiUgZmlsdGVyKGFicyhjb3IpICE9IDEpDQpjb3IudDIkY29yIDwtIHJvdW5kKGNvci50MiRjb3IsIDMpDQpjb3IudDIgPC0gY29yLnQyW29yZGVyKGNvci50MiRjb3IpLF0NCg0KY29yLm10cmFuc19hZ2UgPC0gZ2dwbG90KG9iZXNlLCBhZXMgKHggPSBNVFJBTlMsIHkgPSBBZ2UpKSArDQogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IE1UUkFOUyksIGFscGhhID0gMC42KSArIA0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzNSwgdmp1c3QgPSAxLCBoanVzdD0xKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSANCg0KY29yLmdlbl9oZWlnaHQgPC0gZ2dwbG90KG9iZXNlLCBhZXMgKHggPSBHZW5kZXIsIHkgPSBIZWlnaHQpKSArDQogIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IEdlbmRlciksIGFscGhhID0gMC42KSArIA0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzNSwgdmp1c3QgPSAxLCBoanVzdD0xKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSANCiAgDQpgYGANCg0KYGBge3J9DQpncmlkLmFycmFuZ2UoDQogIGFycmFuZ2VHcm9iKGNvci5tdHJhbnNfYWdlLCBjb3IuZ2VuX2hlaWdodCwgbmNvbCA9IDIsIG5yb3cgPSAxKSwNCiAgdG9wID0gdGV4dEdyb2IoIlBhaXJ3aXNlIENvcnJlbGF0aW9uIFBsb3RzDQogICAgICAgICAgICAgICAgICIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE4LCBmb250ZmFjZSA9ICJib2xkIikpLA0KICBib3R0b20gPSB0ZXh0R3JvYigiRmlndXJlIDI6DQpWaXN1YWxpemF0b25zIG9mIE9iZXNpdHkgdmFyaWFibGUgY29ycmVsYXRpb25zIHdoZXJlIHxyfCA+IDAuNiIpDQopDQpgYGANCg0KDQpgYGB7cn0NCmthYmxlKGNvci50MltjKDEsNCksXSwgYWxpZ24gPSAiYyIsIHJvdy5uYW1lcyA9IEYsIGNvbC5uYW1lcyA9IGMoIlZhcmlhYmxlIDEgKHgpIiwgIlZhcmlhYmxlIDIgKHkpIiwgIkNvcnJlbGF0aW9uIChyKSIpLCBjYXB0aW9uID0gIlRhYmxlIDM6DQogICAgICA8YnI+SWRlbnRpZmllZCBtb2RlcmF0ZSB0byBzdHJvbmcgY29ycmVsYXRpb25zIGZyb20gb2Jlc2UgZGF0YS4gIA0KICAgICAgPGJyPkR1cGxpY2F0ZSBlbnRyaWVzIGhhdmUgYmVlbiByZW1vdmVkLCBmYXZvcmluZyBjYXRlZ29yaWNhbCB2YWx1ZXMgYXMgdGhlIHggdmFyaWFibGUgdG8gYWxsb3cgZm9yIGVhc3kgdmlzdWFsaXphdGlvbiBpbiBGaWd1cmUgMi4NCiAgICAgIDxicj5Nb2RlcmF0ZSB0byBzdHJvbmcgY29ycmVsYXRpb25zIGRlZmluZWQgYXMgMC42IOKJpCByIOKJpCAxIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpUaGUgY29ycmVsYXRlZCB2YXJpYWJsZXMgYXMgb3V0bGluZWQgaW4gYEZpZ3JlIDJgIGFuZCBgVGFibGUgM2AgcHJvdmUgdG8gYmUgbG9naWNhbCBhbmQgZG8gbm90IHJlcXVpcmUgZnVydGhlciBpbnZlc3RpZ2F0aW9uLiANCg0KICAtIFRoZSBhdmVyYWdlIG1hbGUgaGVpZ2h0IGdsb2JhbGx5IGlzIGdyZWF0ZXIgdGhhbiB0aGUgZ2xvYmFsIGF2ZXJhZ2UgbWFsZSBoZWlnaHQsIHRoZXJlZm9yZSB0aGUgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBnZW5kZXIgYW5kIGhlaWdodCBpcyBsb2dpY2FsLg0KICAtIEFzIHRoZXJlIGlzIGEgbWluaW11bSBsaWNlbnNlIGFnZSBmb3IgdXRpbGl6aW5nIHR3bygyKSBvZiB0aGUgZml2ZSAoNSkgdHJhbnNwb3J0YXRpb24gbWV0aG9kcyAoYXV0b21vYmlsZSAmIG1vdG9yYmlrZSkgYW5kIGVsZGVybHkgYWR1bHRzIHRlbmQgdG8gYXZvaWQgaGlnaGVyIHBoc3lpY2FsIGJ1cmRlbiBtZXRob2RzIG9mIHRyYW5zcG9ydGF0aW9uIHN1Y2ggYXMgd2Fsa2luZyBhbmQgYmlrZXMsIHRoZSBuZWdhdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGFnZSBhbmQgbWV0aG9kIG9mIHRyYW5zcG9ydGF0aW9uIGlzIGxvZ2ljYWwgd2l0aCB0aGUgc3VwcG9ydCBvZiBgRmlndXJlIDJgLg0KDQojIyBGZWF0dXJlIEVuZ2luZWVyaW5nDQoNCkluIG9yZGVyIHRvIHByb3Blcmx5IHV0aWxpemUgdGhlIGNvcnJlbGF0ZWQgdmFyaWFibGVzIHdpdGhvdXQgZGF0YSBsb3NzLCBmZWF0dXJlIHZhcmlhYmxlcyB3ZXJlIGNyZWF0ZWQgdG8gYWxsb3cgdGhlaXIgaW5jbHVzaW9uIHdpdGhvdXQgdGhlIGltcGxpY2F0aW9ucyBvZiBjb2xsaW5lYXJpdHkuICBBZGRpdGlvbmFsbHksIGEgdGhpcmQgZmVhdHVyZSB2YXJpYWJsZSB3YXMgY3JlYXRlZCB0byBhbGxvdyBmb3IgYWRkaXRpb25hbCBkaW1lbnNpb25hbGl0eSB0byB0aGUgcHJlZGljdGlvbiBtb2RlbHMuICBUaGUgZm9sbG93aW5nIGZlYXR1cmUgdmFyaWFibGVzIHdlcmUgY3JlYXRlZCBwcmlvciB0byBhbmFseXNpczoNCg0KICAtIFZlZ2V0YWJsZXMgcGVyIERheSAodmVnLmRheSkgPSB2ZWdldGFibGVzIHBlciBtZWFsIChGQ1ZDKSAqIG1lYWxzIHBlciBkYXkgKE5DUCkNCiAgLSBIZWlnaHQgYnkgR2VuZGVyIChnZW4uaGVpZ2h0KSA9IGhlaWdodCAvIGdlbmRlcg0KICAtIFRyYW5zcG9ydGF0aW9uIG1ldGhvZCBieSBBZ2UgKHRyYW5zLmFnZSkgPSB0cmFuc3BvcnRhdGlvbiBtZXRob2QgKE1UUkFOUykgLyBhZ2UgKEFnZSkNCg0KU3VtbWFyeSBzdGF0aXN0aWNzIG9mIHRoZSBuZXdseSBjcmVhdGVkIGZlYXR1cmUgdmFyaWFibGVzIGFyZSBpbmNsdWRlZCBiZWxvdyBpbiBgVGFibGUgNGAuDQoNCmBgYHtyfQ0KI2FkZGluZyBmZWF0dXJlIGVuZ2luZWVyZWQgdmFyaWFibGVzDQpvYmVzZSR2ZWcuZGF5IDwtIG9iZXNlJEZDVkMqb2Jlc2UkTkNQDQpvYmVzZSRnZW4uaGVpZ2h0IDwtIG9iZXNlJEhlaWdodC9hcy5udW1lcmljKG9iZXNlJEdlbmRlcikNCm9iZXNlJHRyYW5zLmFnZSA8LSBhcy5udW1lcmljKG9iZXNlJE1UUkFOUykvYXMubnVtZXJpYyhvYmVzZSRBZ2UpDQoNCiNwdWxsaW5nIGFuIGFsbCBudW1lcmljIGNvcHkganVzdCBpbiBjYXNlIG5lZWRlZCBsYXRlcg0Kb2Jlc2UubnVtZXJpYyA8LSBvYmVzZSAlPiUgbXV0YXRlX2lmKGlzLmZhY3RvciwgYXMubnVtZXJpYykNCg0Ka2FibGUoc3VtbWFyeShvYmVzZVtjKDE3OjE5KV0pLCBhbGlnbiA9ICJjIiwgY29sLm5hbWVzID0gYygiVmVnZXRhYmxlcyBwZXIgRGF5IiwgIkhlaWdodCBieSBHZW5kZXIiLCAiVHJhbnNwb3J0IE1ldGhvZCBieSBBZ2UiKSxjYXB0aW9uID0gIlRhYmxlIDQ6DQogICAgICA8YnI+U3VtbWFyeSBzdGF0aXN0aWNzIG9mIGZlYXR1cmUgZW5naW5lZXJlZCB2YXJpYWJsZXMgKHZlZy5kYXksIGdlbi5oZWlnaHQsIHRyYW5zLmFnZSIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KIyMgRGF0YSBTcGxpdHRpbmcgZm9yIENyb3NzIFZhbGlkYXRpb24NCg0KSW4gb3JkZXIgdG8gY3JlYXRlIHRoZSBwcmVkaWN0aW9uIG1vZGVscyBmb3IgcGF0aWVudCBvYmVzaXR5IHN0YXR1cywgdGhlIGRhdGEgd2FzIGZpcnN0IHNwbGl0IGluIHRvIDgwOjIwIHRyYWluaW5nOnRlc3QgZ3JvdXBzIHRvIGFsbG93IGZvciBtb2RlbCB0cmFpbmluZyBhbmQgdHVuaW5nIG9uIHRoZSB0ZXN0IGdyb3VwLiAgVGhpcyBzcGxpdCBzZWxlY3Rpb24gcHJvY2VzcyB3aWxsIGVuc3VyZSB0aGUgcHJlZGljdGlvbiBtb2RlbHMgYXJlIG5vdCBvdmVyLWZpdHRlZCB0byB0aGUgaW5pdGlhbCBkYXRhLiBQcmV2ZW50aW5nIG92ZXIgZml0dGluZyBvZiB0aGUgbW9kZWxzIHdpbGwgYWxsb3cgZm9yIHVzZSB3aXRoIGZ1dHVyZSBjb2xsZWN0ZWQgZGF0YSBhbmQgcGFydGljaXBhbnQgZGF0YSBzb3VyY2VkIGZyb20gb3RoZXIgY291bnRyaWVzIGJ5IGVuc3VyaW5nIGZsZXhpYmlsaXR5Lg0KDQpgYGB7cn0NCiM4MDoyMCBzcGxpdCBmb3IgQ1YNCiNudW1lcmljDQpjdi5pbmRleCA8LSBzYW1wbGUoMTpucm93KG9iZXNlLm51bWVyaWMpLCBzaXplID0gMC44Km5yb3cob2Jlc2UubnVtZXJpYykpDQpvYi50cmFpbiA8LSBvYmVzZS5udW1lcmljW2N2LmluZGV4LF0NCm9iLnRlc3QgPC0gb2Jlc2UubnVtZXJpY1stY3YuaW5kZXgsXQ0KDQojY2F0ZWdvcmljYWwNCmN2LmluZGV4MSA8LSBzYW1wbGUoMTpucm93KG9iZXNlKSwgc2l6ZSA9IDAuOCpucm93KG9iZXNlKSkNCm9iLnRyYWluMSA8LSBvYmVzZVtjdi5pbmRleCxdDQpvYi50ZXN0MSA8LSBvYmVzZVstY3YuaW5kZXgsXQ0KYGBgDQoNCg0KDQojIENBUlQgTW9kZWxzDQoNClJhdGhlciB0aGFuIGNyZWF0aW5nIGEgcmVncmVzc2lvbiBtb2RlbCB0aGUgQ0FSVCBhbGdvcml0aG0gY2FuIGJlIHV0aWxpemVkIHRvIGNyZWF0ZSBwcmVkaWN0aW9uIG1vZGVscyBmb3IgYm90aCByZWdyZXNzaW9uIGFuZCBjbGFzc2lmaWNhdGlvbi4gIFRoZSBDQVJUIGFsZ29yaXRobSBmdW5jdGlvbnMgdGhyb3VnaCBjcmVhdGluZyBkZWNpc2lvbiB0cmVlcyB1dGlsaXppbmcgc3VwZXJ2aXNlZCBsZWFybmluZyBhbGdvcml0aG1zIGJhc2VkIG9uIHRoZSB0YXJnZXQgcmVzcG9uc2UgdmFyaWFibGUuICBUaGUgbW9kZWwgaW5pdGlhdGVzIGJ5IHNwbGl0dGluZyB0aGUgZmlyc3QgaWRlbnRpZmllZCB2YXJpYWJsZSBiYXNlZCBvbiBhbiBhbGdvcml0aG1pYyBkZWZpbmVkIGN1dG9mZiB2YWx1ZSBjcmVhdGluZyB0aGUgcm9vdCBub2RlIHRoYXQgYnJhbmNoZXMgaW50byB0d28gaW5uZXIgbm9kZXMuICBUaGUgaW5uZXIgbm9kZXMgYXJlIHN1YnNlcXVlbnRseSBzcGxpdCBpbnRvIGVpdGhlciBhbiBhZGRpdGlvbmFsIHR3byBpbm5lciBub2RlcyBvciB0d28gdGVybWluYWwgKGxlYWYpIG5vZGVzLiBUaGlzIHByb2Nlc3MgY29udGludWVzIHVudGlsIHRoZSBzdG9wIHBvaW50IGlzIHJlYWNoZWQ7IHRoZSBzdG9wIHBvaW50IGlzIGFsZ29yaXRobWljYWxseSBkZXRlcm1pbmVkIHRvIHByZXZlbnQgb3ZlciBmaXR0aW5nIGFuZCBlbnN1cmUgdGhlIG1vZGVsIGlzIGdlbmVyYWxpemVkIGVub3VnaCB0aGF0IG5ld2x5IGNvbGxlY3RlZCBkYXRhIGNhbiBiZSB1dGlsaXplZCB3aXRoIHRoZSBzYW1lIG1vZGVsLg0KDQpBcyB0aGUgb2Jlc2l0eSByYW5rIHZhcmlhYmxlIGlzIGFuIGludGVnZXIgdmFsdWUsIGJvdGggQ0FSVCBSZWdyZXNzaW9uIGFuZCBDQVJUIENsYXNzaWZpY2F0aW9uIHRyZWVzIHdlcmUgY3JlYXRlZCB0byBkZXRlcm1pbmUgdGhlIGJlc3QgZml0IHByZWRpY3Rpb24gbW9kZWwuIA0KDQojIyBDQVJUIFJlZ3Jlc3Npb24NCg0KQ0FSVCByZWdyZXNzaW9uIHdhcyBpbml0aWF0ZWQgdXRpbGl6aW5nIHRoZSA8Yj5ycGFydCgpPC9iPiBwYWNrYWdlIHV0aWxpemluZyB0aGUgZnVsbCBtb2RlbC4gIFRoZSByZWdyZXNzaW9uIHRyZWUgd2FzIGNyZWF0ZWQgdXRpbGl6aW5nIHRoZSA8Yj5BTk9WQTwvYj4gbWV0aG9kIGluZGljYXRpbmcgdGhhdCB0aGUgdHJlZSBzcGxpdHMgYXJlIGNvbXBsZXRlZCB1dGlsaXppbmcgTWVhbiBTcXVhcmUgRXJyb3IgKE1TRSkgcmVkdWN0aW9uIHRlY2huaXF1ZXMuICBUaGlzIE1TRSByZWR1Y3Rpb24gYXBwcm9hY2ggZm9sbG93cyB0aGUgYmVsb3cgZXF1YXRpb24gdG8gZW5zdXJlIGNoaWxkIChpbm5lciAmIHRlcm1pbmFsKSBub2RlcyBhcmUgc2VsZWN0ZWQgYmFzZWQgb24gc3BsaXRzIHRoYXQgbWluaW1pemUgdGhlIE1TRSBvZiBlYWNoIG5vZGUuDQoNCiQkDQpcdW5kZXJsaW5le1xtYm94e01lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBSZWR1Y3Rpb259fSBcXCANCk1TRSA9IFxmcmFje1xzdW1fe2kgPSAxfV57bn0gKHlfe2l9IC0gXGhhdHt5fSleMn17bn0NCiQkDQoNCiMjIyBNb2RlbCBDcmVhdGlvbg0KDQpBcyB0aGUgZGF0YSB3YXMgcHJlZW1wdGl2ZWx5IHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSwgdGhlIHRyYWluaW5nIGRhdGEgd2FzIHV0aWxpemVkIGZvciAxMC1mb2xkIGNyb3NzIHZhbGlkYXRpb24gYXMgcGFydCBvZiB0aGUgaW5pdGlhbCBmdWxsIG1vZGVsIGNyZWF0aW9uIHByb2Nlc3MuICBUaGlzIGNyb3NzIHZhbGlkYXRpb24gZW5zdXJlcyB0aGUgcHJvZHVjZWQgbW9kZWwgaXMgdGhlIGJlc3QgZml0IGZvciB0aGUgdHJhaW5pbmcgZGF0YSBhcyBwcm92aWRlZC4gIEluaXRpYWxseSB0aGUgaHlwZXJwYXJhbWV0ZXJzIHdlcmUgaW5pdGlhdGVkIGFzOg0KDQogIC0gbWluaW11bSBzcGxpdCA9IDIwIA0KICAgIC0gbWluaW11bSBvYnNlcnZhdGlvbnMgaW4gYSBncm91cCBiZWZvcmUgdGhlIGFsZ29yaXRobSB3aWxsIHRyeSB0byBzcGxpdA0KICAtIG1pbmltdW0gYnVja2V0ID0gNw0KICAgIC0gbWluaW11bSBvYnNlcnZhdGlvbnMgYWxsb3dlZCBpbiBhIGxlYWYgbm9kZQ0KICAtIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgPSAwLjAxDQogICAgLSBtaW5pbXVtIGltcHJvdmVtZW50IGluIHRhcmdldCBwYXJhbWV0ZXIgdG8gYWxsb3cgZm9yIHNwbGl0IChDQVJUIFJlZyA9IE1TRSByZWR1Y3Rpb24pDQogIC0gbWF4aW11bSBkZXB0aCA9IDUNCiAgICAtIG1heGltdW0gbnVtYmVyIG9mIGxldmVscyBpbiB0cmVlIG91dHB1dA0KDQpgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9OX0NCiNjcmVhdGUgQ0FSVCByZWdyZXNzaW9uIHRyZWUgKGluaXRpYWwgZnVsbCBtb2RlbCkNCnJlZy50cmVlIDwtIHJwYXJ0KE9iLlJhbmsgfiAuLCBkYXRhID0gb2IudHJhaW4sIG1ldGhvZCA9ICJhbm92YSIsIA0KICAgICAgICAgICAgICAgICAgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woDQogICAgICAgICAgICAgICAgICAgIG1pbnNwbGl0ID0gMjAsDQogICAgICAgICAgICAgICAgICAgIG1pbmJ1Y2tldCA9IDcsDQogICAgICAgICAgICAgICAgICAgIGNwID0gMC4wMSwgI3JlZHVjdGlvbiBpbiBNU0UgbmVlZGVkIGZvciBzcGxpdA0KICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IDUsDQogICAgICAgICAgICAgICAgICAgIHh2YWwgPSAxMA0KICAgICAgICAgICAgICAgICAgICkpDQoNCmBgYA0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBvdXQud2lkdGg9IjEwMCUifQ0KI3Bsb3QgZnVsbCBtb2RlbCByZWdyZXNzaW9uIHRyZWUNCmZhbmN5UnBhcnRQbG90KHJlZy50cmVlLCAgdHlwZSA9IDUsIHR3ZWFrID0gMS4wMSwgbWFpbiA9ICJJbml0aWFsIENBUlQgUmVncmVzc2lvbiBUcmVlIiAsIGNhcHRpb24gPSAiRmlndXJlIDM6DQpJbml0aWFsIENBUlQgcmVncmVzc2lvbiB0cmVlIG1vZGVsICh1bnBydW5lZCkiKQ0KYGBgDQoNCiMjIyBIeXBlcnBhcmFtZXRlciBUdW5pbmcNCg0KV2l0aCB0aGUgaW5pdGlhbCBDQVJUIHJlZ3Jlc3Npb24gdHJlZSBtb2RlbCBjcmVhdGVkIGFib3ZlIGluIGBGaWd1cmUgM2AgdGhlIG1vZGVsIHdhcyB0aGVuIHR1bmVkIHRvIGFzc2lnbiBiZXR0ZXIgZml0IGh5cGVycGFyYW1ldGVyIHZhbHVlcyBlbnN1cmluZyBpbXByb3ZlZCBmaXQgYW5kIGFuIG92ZXJhbGwgcmVkdWN0aW9uIGluIG1vZGVsIHNpemUuICBUaGUgbW9kZWwncyBDUCB2YWx1ZXMgd2VyZSBjb21wYXJlZCB3aXRoIHJlc3BlY3QgdG8gdGhlIGNyb3NzIHZhbGlkYXRlZCBlcnJvciAoeGVycm9yKSBhbmQgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIGNyb3NzIHZhbGlkYXRlZCBlcnJvciAoeHN0ZCkgdG8gaWRlbnRpZnkgbmV3IENQIHZhbHVlcyBmb3IgdXNlIHdpdGggdGhlIGluaXRpYWwgbW9kZWwuICBCb3RoIHRoZSBDUCBhc3NvY2lhdGVkIHdpdGggdGhlIG1pbmltdW0geGVycm9yIChrbm93biBhcyB0aGUgbWluIENQKSBhbmQgdGhlIGxhcmdlc3QgQ1AgYXNzb2NpYXRlZCB3aXRoIHRoZSB4ZXJyb3Igd2l0aGluIDEgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1pbmltdW0geGVycm9yIHZhbHVlIChrbm93biBhcyB0aGUgMXNlIENQIHZhbHVlKSB3ZXJlIGNvbGxlY3RlZCBmb3Igc3Vic2VxdWVudCB0dW5pbmcuIA0KDQo8dGFibGU+DQo8dHI+DQo8dGQ+DQpgYGB7cn0NCiNwbG90IG9mIENQIHZhbHVlcyBiYXNlZCBvbiB0YWJsZSBzaXplDQprYWJsZShyZWcudHJlZSRjcHRhYmxlLCBjYXB0aW9uID0gIlRhYmxlIDU6DQogICAgICA8YnI+IENQIHZhbHVlcyBmb3IgQ0FSVCByZWdyZXNzaW9uIHRyZWUgcHJ1bmluZyIpICU+JSANCiAga2FibGVfc3R5bGluZyhmb250X3NpemUgPSAxMikNCmBgYA0KPC90ZD4NCjx0ZD4NCmBgYHtyLCBmaWcuaGVpZ2h0PTd9DQpwYXIob21hID1jKDIsMSw1LDEpKQ0KcGxvdGNwKHJlZy50cmVlKQ0KbXRleHQoIkNQIEN1dG9mZiBUZXN0Iiwgc2lkZSA9IDMsIGxpbmUgPSA1LCBvdXRzaWRlID0gVCwgZm9udCA9IDIsIGNleCA9IDIpDQptdGV4dCgiRmlndXJlIDQ6DQpDUCBoeXBlcnBhcmFtZXRlciB0dW5pbmcsIGJlc3QgZml0IHZhbHVlIGF0IGNyb3NzIG9mIGRvdHRlZCBsaW5lLiIsIHNpZGUgPSAxLCBsaW5lID0gNSwgb3V0c2lkZSA9IFQsIGZvbnQgPSAxLCBjZXggPSAwLjkpDQpgYGANCjwvdGQ+DQo8L3RyPg0KPC90YWJsZT4NCg0KDQojIyMgTW9kZWwgUHJ1bmluZyANCg0KVGhlIGNhbGN1bGF0ZWQgQ1AgdmFsdWVzIGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb24gd2VyZSB0aGVuIGZlZCBpbiB0byB0aGUgbW9kZWwgcHJ1bmluZyBmdW5jdGlvbiA8Yj5wcnVuZSgpPC9iPiB0byBjcmVhdGUgdHdvIHBydW5lZCBDQVJUIHJlZ3Jlc3Npb24gdHJlZSBtb2RlbHMuICBUaGUgZmlyc3QgdHJlZSBtb2RlbCAoYEZpZ3VyZSA1YCkgd2FzIGNyZWF0ZWQgdXNpbmcgdGhlIDFzZSBDUCB2YWx1ZSB3aGlsZSB0aGUgc2Vjb25kIChgRmlndXJlIDZgKSB3YXMgY3JlYXRlZCB1c2luZyB0aGUgbWluaW11bSBDUCB2YWx1ZS4gQXMgZGVtb25zdHJhdGVkIGJ5IHRoZSBiZWxvdyByZWdyZXNzaW9uIHRyZWVzLCB0aGUgMXNlIG1ldGhvZCBvZiB0dW5pbmcgdGhlIENQIGh5cGVycGFyYW1ldGVyIHJlc3VsdHMgaW4gYSBtb3JlIHNpZ25pZmljYW50bHkgcmVkdWNlZCByZWdyZXNzaW9uIHRyZWUgbW9kZWwuIA0KDQpgYGB7cn0NCmNwLnJlZy50YWJsZSA8LSByZWcudHJlZSRjcHRhYmxlDQoNCiNnZXQgdmFsdWVzIGZvciBwcnVuZWQgdHJlZXMNCg0KeGVycm9yLm1pbiA8LSBtaW4oY3AucmVnLnRhYmxlWywieGVycm9yIl0pICNtaW4geGVycm9yDQoNCiNleHRyYWN0IHJvdyB3aXRoIG1pbmltdW0geGVycm9yIGFzIHN0YW5kYWxvbmUgZGF0YSBmcmFtZSwgdGhlbiBwdWxsIENQIHZhbHVlIG91dCBvZiB0aGF0IHJvdw0KbWluLmNwLnJvdyA8LSB3aGljaC5taW4oY3AucmVnLnRhYmxlWywgInhlcnJvciJdKQ0KDQptaW4uY3AgPC0gY3AucmVnLnRhYmxlW21pbi5jcC5yb3csICJDUCJdICNtaW4uY3AgZm9yIG1pbmNwIHRyZWUNCg0KI2dldCB4c3RkIHZhbHVlIGZyb20gbWluaW11bSB4ZXJyb3Igcm93DQp4ZXJyb3IueHN0ZCA8LSBjcC5yZWcudGFibGVbbWluLmNwLnJvdywgInhzdGQiXQ0KDQp0aHJlc2hvbGQgPC0geGVycm9yLm1pbiArIHhlcnJvci54c3RkICNhZGQgeHN0ZCB2YWx1ZSB0byBtaW4geGVycm9yIHRvIGdldCAxIHNlIGZyb20gbWluDQoNCmJlc3QuY3Aucm93IDwtIHdoaWNoKGNwLnJlZy50YWJsZVssInhlcnJvciJdIDw9IHRocmVzaG9sZClbMV0gI3B1bGwgcm93IHdoZXJlIHhlcnJvciBpcyBjbG9zZXN0IHRvIGJ1dCBiZWxvdyBtaW4oeGVycm9yKSArIDFzZSB0aHJlc2hvbGQNCmJlc3QuY3AgPC0gY3AucmVnLnRhYmxlW2Jlc3QuY3Aucm93LCAiQ1AiXSAjcHVsbCBDUCB2YWx1ZSBmcm9tIHJvdyB3aGVyZSB4ZXJyb3Igdy5pbiAxc2UNCg0KcmVnLnRyZWUuYmVzdGNwIDwtIHBydW5lKHJlZy50cmVlLCBjcCA9IGJlc3QuY3ApICNwcnVuZSB0cmVlIHdpdGggMXNlIHBhcmFtZXRlcg0KcmVnLnRyZWUubWluY3AgPC0gcHJ1bmUocmVnLnRyZWUsIGNwID0gbWluLmNwKSAjcHJ1bmUgdHJlZSB3aXRoIG1pbiBjcCBwYXJhbWV0ZXINCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgb3V0LndpZHRoPSIxMDAlIn0NCmZhbmN5UnBhcnRQbG90KHJlZy50cmVlLmJlc3RjcCwgIHR5cGUgPSA1LCB0d2VhayA9IDEuMDEsIG1haW4gPSAiUHJ1bmVkIENBUlQgUmVncmVzc2lvbiBUcmVlIA0KQ1AgPSAxc2UiLCBjYXB0aW9uID0gcGFzdGUoIkZpZ3VyZSA1Og0KUHJ1bmVkIENBUlQgUmVncmVzc2lvbiB0cmVlICgxc2UgbW9kZWwpLiAgQ1AgPSIsIHJvdW5kKGJlc3QuY3AsIDUpKSkNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD0xMCwgb3V0LndpZHRoPSIxMDAlIn0NCmZhbmN5UnBhcnRQbG90KHJlZy50cmVlLm1pbmNwLCAgdHlwZSA9IDUsIHR3ZWFrID0gMS4wMSwgbWFpbiA9ICJQcnVuZWQgQ0FSVCBSZWdyZXNzaW9uIFRyZWUgDQpDUCA9IG1pbiIsIGNhcHRpb24gPSBwYXN0ZSgiRmlndXJlIDY6DQpQcnVuZWQgQ0FSVCBSZWdyZXNzaW9uIHRyZWUgKG1pbiBtb2RlbCkuICBDUCA9Iiwgcm91bmQobWluLmNwLCA1KSkpDQpgYGANCg0KQXMgZGVtb25zdHJhdGVkIGluIHRoZSBhYm92ZSBgRmlndXJlIDVgLCB0aGUgMXNlIENBUlQgcmVncmVzc2lvbiB0cmVlIHJlc3VsdGVkIGluIGEgbW9kZWwgdGhhdCBpbmNvcnBvcmF0ZWQgdGhlIGZvbGxvd2luZyBmZWF0dXJlIHZhcmlhYmxlczoNCg0KICAtIENBRUMNCiAgLSBnZW4uaGVpZ2h0DQogIC0gQWdlDQogIC0gTkNQDQogIC0gRkguT3ZlcndlaWdodA0KICANCk1lYW53aGlsZSwgYXMgZGVtb25zdHJhdGVkIGluIGBGaWd1cmUgNmAgdGhlIG1pbmltdW0gQ0FSVCByZWdyZXNzaW9uIHRyZWUgcmVzdWx0ZWQgaW4gYSBtb2RlbCB0aGF0IGluY29ycG9yYXRlZCB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczoNCg0KICAtIENBRUMNCiAgLSBnZW4uaGVpZ2h0DQogIC0gQWdlDQogIC0gTkNQDQogIC0gRkguT3ZlcndlaWdodA0KICAtIE1UUkFOUw0KICAtIEZBRg0KICAtIENIMk8NCiAgDQpBcyB0aGUgbWluaW11bSBDUCBtb2RlbCB1dGlsaXplZCBhIGxvd2VyIENQIHZhbHVlLCB0aGlzIG1vZGVsIHJlcXVpcmVkIGxlc3Mgb2YgYSBjaGFuZ2UgaW4gTVNFIGJlZm9yZSBub2RlcyB3ZXJlIHNwbGl0LCB0aHVzIHJlc3VsdGluZyBpbiBhIGJyb2FkZXIgbW9kZWwgdGhhdCBpbmNvcnBvcmF0ZXMgYSBsYXJnZXIgbnVtYmVyIG9mIGZlYXR1cmUgdmFyaWFibGVzLiANCg0KIyMjIE1vZGVsIEV2YWx1YXRpb24NCg0KQm90aCB0aGUgMXNlIGFuZCBtaW5pbXVtIENQIENBUlQgcmVncmVzc2lvbiB0cmVlIG1vZGVscyB3ZXJlIHRoZW4gdXRpbGl6ZWQgdG8gZ2VuZXJhdGUgcHJlZGljdGlvbiB2YWx1ZXMgZm9yIG9iZXNpdHkgcmFua3MgaW4gdXRpbGl6aW5nIHRoZSB0ZXN0IGRhdGEgc2V0LiBUaGVzZSBwcmVkaWN0ZWQgdmFsdWVzIHdlcmUgY29tcGFyZWQgdG8gdGhlIGFjdHVhbCB2YWx1ZXMgaW4gdGhlIHRlc3QgZGF0YSBzZXQgdG8gYXNzZXNzIGZvciBtb2RlbCBhY2N1cmFjeSBhbmQgcmVsaWFiaWxpdHkuICBUaGVzZSBDQVJUIHJlZ3Jlc3Npb24gbW9kZWxzIHdlcmUgYWRkaXRpb25hbGx5IGNvbXBhcmVkIHRvIHR3byBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMgdG8gZGV0ZXJtaW5lIHRoZWlyIGVmZmljYWN5IGluIHJlbGF0aW9uIHRvIG90aGVyIG1vcmUgZXN0YWJsaXNoZWQgc3RhdGlzdGljYWwgcHJlZGljdGlvbiBtZXRob2RzLiAgVGhlIGZpcnN0IHJlZ3Jlc3Npb24gbW9kZWwgd2FzIGNvbXByaXNlZCB1dGlsaXppbmcgdGhlIHZhcmlhYmxlcyBhcyBpZGVudGlmaWVkIGluIHRoZSAxc2UgQ0FSVCByZWdyZXNzaW9uIHRyZWUgbW9kZWwsIHdoaWxlIHRoZSBzZWNvbmQgd2FzIGNvbnN0cnVjdGVkIHV0aWxpemluZyB0aGUgPGI+c3RlcEFJQygpPC9iPiBwYWNrYWdlIHRvIGF1dG9tYXRpY2FsbHkgaWRlbnRpZnkgdGhlIGJlc3QgdmFyaWFibGVzIGZvciBpbmNsdXNpb24uDQoNClRoZSBsaW5lYXIgcmVncmVzc2lvbiBlcXVhdGlvbnMgdXRpbGl6ZWQgYXJlIGFzIGZvbGxvd3M6DQoNCiQkDQpcdW5kZXJsaW5le1xtYm94ezFzZSBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbH19IFxcIFxcIA0KXGJlZ2lue2FsaWdufQ0KXGhhdHtZfV97T2IuUmFua30gXHNpbSBcbWJveHtDQUVDfSp4X3sxfSArICZcbWJveHtnZW4uaGVpZ2h0fSp4XzIgKyBcbWJveHtBZ2V9KnhfMyArIFxtYm94e05DUH0qeF80ICsgXG1ib3h7RkguT3ZlcndlaWdodH0qeF81IA0KXGVuZHthbGlnbn0NCiQkDQoNCiQkDQpcdW5kZXJsaW5le1xtYm94e3N0ZXBBSUMgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWx9fSBcXCBcXA0KXGJlZ2lue2FsaWdufQ0KXGhhdHtZfV97T2IuUmFua30gXHNpbSBcbWJveHtDQUVDfSp4X3sxfSArICZcbWJveHtnZW4uaGVpZ2h0fSp4XzIgKyBcbWJveHtBZ2V9KnhfMyArIFxcDQpcbWJveHtOQ1B9KnhfNCArICZcbWJveHtGSC5PdmVyd2VpZ2h0fSp4XzUgKyBcbWJveHtNVFJBTlN9KnhfNiArIFxtYm94e0ZBRn0qeF83ICsgXG1ib3h7Q0gyT30qeF84DQpcZW5ke2FsaWdufQ0KJCQNCg0KDQpgYGB7cn0NCm9icmFuay5yZWcudHJlZS5iZXN0IDwtIHByZWRpY3QocmVnLnRyZWUuYmVzdGNwLCBuZXdkYXRhID0gb2IudGVzdCkNCm9icmFuay5yZWcudHJlZS5taW4gPC0gcHJlZGljdChyZWcudHJlZS5taW5jcCwgbmV3ZGF0YSA9IG9iLnRlc3QpDQoNCiNCZXN0Q1AgTW9kZWwNCm1zZS5yZWcuYmVzdCA8LSBtZWFuKChvYi50ZXN0JE9iLlJhbmsgLSBvYnJhbmsucmVnLnRyZWUuYmVzdCkqKjIpDQpybXNlLnJlZy5iZXN0IDwtIHNxcnQobXNlLnJlZy5iZXN0KQ0KbWFlLnJlZy5iZXN0IDwtIG1lYW4oYWJzKG9iLnRlc3QkT2IuUmFuayAtIG9icmFuay5yZWcudHJlZS5iZXN0KSkNCnJzcS5yZWcuYmVzdCA8LSAoY29yKGFzLm51bWVyaWMob2IudGVzdCRPYi5SYW5rKSwgb2JyYW5rLnJlZy50cmVlLmJlc3QpKSoqMg0KDQojTWluQ1AgTW9kZWwNCm1zZS5yZWcubWluIDwtIG1lYW4oKG9iLnRlc3QkT2IuUmFuayAtIG9icmFuay5yZWcudHJlZS5taW4pKioyKQ0Kcm1zZS5yZWcubWluIDwtIHNxcnQobXNlLnJlZy5taW4pDQptYWUucmVnLm1pbiA8LSBtZWFuKGFicyhvYi50ZXN0JE9iLlJhbmsgLSBvYnJhbmsucmVnLnRyZWUubWluKSkNCnJzcS5yZWcubWluIDwtIChjb3IoYXMubnVtZXJpYyhvYi50ZXN0JE9iLlJhbmspLCBvYnJhbmsucmVnLnRyZWUubWluKSkqKjINCg0KI21ha2UgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzDQojbW9kZWwgdXNpbmcgdmFyaWFibGVzIGZyb20gMXNlIHRyZWUNCmxpbi4xc2UgPC0gbG0oT2IuUmFuayB+IENBRUMgKyBnZW4uaGVpZ2h0ICsgQWdlICsgTkNQICsgRkguT3ZlcndlaWdodCwgZGF0YSA9IG9iLnRyYWluKQ0KbGluLnByZWQgPC0gcHJlZGljdChsaW4uMXNlLCBuZXdkYXRhID0gb2IudGVzdCkNCm1zZS5saW4ubWluIDwtIG1lYW4oKG9iLnRlc3QkT2IuUmFuayAtIGxpbi5wcmVkKSoqMikNCnJtc2UubGluLm1pbiA8LSBzcXJ0KG1zZS5saW4ubWluKQ0KbWFlLmxpbi5taW4gPC0gbWVhbihhYnMob2IudGVzdCRPYi5SYW5rIC0gbGluLnByZWQpKQ0KcnNxLmxpbi5taW4gPC0gKGNvcihhcy5udW1lcmljKG9iLnRlc3QkT2IuUmFuayksIGxpbi5wcmVkKSkqKjINCg0KI3N0ZXB3aXNlIHNlbGVjdGlvbiBmcm9tIGFsbCB2YXJpYWJsZXMNCnN0ZXAubGluIDwtIGxtKE9iLlJhbmsgfi4sIGRhdGEgPSBvYi50cmFpbikNCkFJQy5saW4gPC0gc3RlcEFJQyhzdGVwLmxpbiwgZGlyZWN0aW9uID0gImJvdGgiLCB0cmFjZSA9IEYpDQpzdGVwLnByZWQgPC0gcHJlZGljdChBSUMubGluLCBuZXdkYXRhID0gb2IudGVzdCkNCm1zZS5zdGVwLm1pbiA8LSBtZWFuKChvYi50ZXN0JE9iLlJhbmsgLSBzdGVwLnByZWQpKioyKQ0Kcm1zZS5zdGVwLm1pbiA8LSBzcXJ0KG1zZS5zdGVwLm1pbikNCm1hZS5zdGVwLm1pbiA8LSBtZWFuKGFicyhvYi50ZXN0JE9iLlJhbmsgLSBzdGVwLnByZWQpKQ0KcnNxLnN0ZXAubWluIDwtIChjb3IoYXMubnVtZXJpYyhvYi50ZXN0JE9iLlJhbmspLCBzdGVwLnByZWQpKSoqMg0KDQpyZWcuZXJyb3JzIDwtIGRhdGEuZnJhbWUoDQogIG1lYXN1cmVzID0gYygiTWVhbiBTcXVhcmUgRXJyb3IgKE1TRSkiLCAiUm9vdCBNZWFuIFNxdWFyZSBFcnJvciAoUk1TRSkiLCAiTWVhbiBBYnNvbHV0ZSBFcnJvciAoTUFFKSIsICJSIFNxdWFyZWQiKSwNCiAgdHJlZS5yMXNlID0gYyhtc2UucmVnLmJlc3QsIHJtc2UucmVnLmJlc3QsIG1hZS5yZWcuYmVzdCwgcnNxLnJlZy5iZXN0KSwNCiAgdHJlZS5ybWluID0gYyhtc2UucmVnLm1pbiwgcm1zZS5yZWcubWluLCBtYWUucmVnLm1pbiwgcnNxLnJlZy5taW4pLA0KICBsaW4uMXNlID0gYyhtc2UubGluLm1pbiwgcm1zZS5saW4ubWluLCBtYWUubGluLm1pbiwgcnNxLmxpbi5taW4pLA0KICBzdGVwLmxpbiA9IGMobXNlLnN0ZXAubWluLCBybXNlLnN0ZXAubWluLCBtYWUuc3RlcC5taW4sIHJzcS5zdGVwLm1pbikNCikNCmBgYA0KDQojIyMjIE1TRSwgUk1TRSwgTUFFLCBhbmQgUiBTcXVhcmVkDQoNClRoZSBtb2RlbCBhc3Nlc3NtZW50IHN0YXRpc3RpY3MgYXJlIGluY2x1ZGVkIGJlbG93IGluIGBUYWJsZSA2YC4gIEFzIGRlbW9uc3RyYXRlZCBieSB0aGUgbG93ZXN0IE1TRSwgUk1TRSwgYW5kIE1BRSB3aXRoIHRoZSBoaWdoZXN0IHIkXjIkIHZhbHVlLCB0aGUgTWluIENQIENBUlQgdHJlZSByZXN1bHRlZCBpbiB0aGUgYmVzdCBmaXQgcHJlZGljdGlvbiBtb2RlbC4gIEFsYmVpdCwgd2hpbGUgdGhlIE1pbiBDUCBDQVJUIFRyZWUgcHJlZm9ybWVkIHRoZSBiZXN0IG9mIHRoZSBmb3VyIGF2YWlsYWJsZSByZWdyZXNzaW9uIGJhc2VkIG1vZGVscywgbm9uZSBvZiB0aGUgbW9kZWxzIHByZWZvcm1lZCB3ZWxsLiAgQWxsIHJldHVybmVkIGFuIHIkXjIkIHZhbHVlIG9mIGxlc3MgdGhhbiAwLjI2IGluZGljYXRpbmcgdGhlc2UgbW9kZWxzIG9ubHkgY2FwdHVyZSBsZXNzIHRoYW4gMjYlIG9mIHRoZSB2YXJpYW5jZSBpbiB0aGUgb2Jlc2l0eSBkYXRhLg0KDQpgYGB7cn0NCmthYmxlKHJlZy5lcnJvcnMsIGFsaWduID0gYygibCIsICJjIiwiYyIsImMiLCJjIikgLCBjb2wubmFtZXMgPSBjKCJBc20uIE1lYXN1cmVzIiwgIjFzZSBDQVJUIFRyZWUiLCAiTWluIENQIENBUlQgVHJlZSIsICIxc2UgTGluZWFyIiwgInN0ZXBBSUMgTGluZWFyIikgLCBjYXB0aW9uID0gIlRhYmxlIDY6DQogICAgICA8YnI+TW9kZWwgYXNzZXNzbWVudCBzdGF0aXN0aWNzIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQojIyMjIFZhcmlhYmxlIEltcG9ydGFuY2UNCg0KQSBzdW1tYXJ5IG9mIHRoZSB2YXJpYWJsZSBpbXBvcnRhbmNlIGZvciBib3RoIHRoZSAxc2UgQ0FSVCByZWdyZXNzaW9uIGFuZCBtaW4gQ1AgQ0FSVCByZWdyZXNzaW9uIHRyZWVzIGlzIGluY2x1ZGVkIGJlbG93IGluIGBGaWd1cmUgN2AuICBXaGlsZSBhIG1ham9yaXR5IG9mIHRoZSB2YXJpYWJsZXMgYXMgcmVwcmVzZW50ZWQgaW4gdGhlIGFib3ZlIHRyZWVzIGluIGBGaWd1cmUgNWAgYW5kIGBGaWd1cmUgNmAgYXJlIHJlcHJlc2VudGVkIGluIHRoZSBiZWxvdyBpbXBvcnRhbmNlIGdyYXBocywgc29tZSB2YXJpYWJsZXMgYXJlIGNvbnNpZGVyZWQgaW1wb3J0YW50IGJ1dCBvbWl0dGVkLiAgVGhpcyBvbWlzc2lvbiBvZiBpbXBvcnRhbnQgdmFyaWFibGVzIGNhbiBiZSBjYXVzZWQgYnkgYSBudW1iZXIgb2YgcmVhc29ucyBpbmNsdWRpbmcgYnV0IG5vdCBsaW1pdGVkIHRvOiANCg0KICAtIElkZW50aWZpZWQgY29sbGluZWFyaXR5OiBUcmVlIG1vZGVscyBhdXRvbWF0aWNhbGx5IGRyb3Agc29tZSBvciBhbGwgY29sbGluZWFyIHZhcmlhYmxlcw0KICAtIFNwbGl0dGluZyBjcml0ZXJpYTogZGVzcGl0ZSBhIHZhcmlhYmxlJ3MgY2FsY3VsYXRlZCBzaWduaWZpY2FuY2UgdmFsdWUsIHZhcmlhYmxlcyBtYXkgbm90IGltcGFjdCB0aGUgTVNFIHdoZW4gc3BsaXQgYW5kIHRodXMgYXJlIHN1YnNlcXVlbnRseSBkcm9wcGVkDQogIC0gQ1AgcHJ1bmluZzogQ1AgY3V0b2ZmIHBydW5lcyBzcGxpdHMgdGhhdCBvbmx5IHBhcnRpYWxseSBmaXQgdGhlIG1vZGVsLCBjYXVzaW5nIHBvdGVudGlhbGx5IGltcG9ydGFudCB2YXJpYWJsZXMgZnJvbSBiZWluZyBkcm9wcGVkDQoNCmBgYHtyLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0PTcsIG91dC53aWR0aD0iMTAwJSJ9DQppbXAudHJlZTFzZSA8LSByZWcudHJlZS5iZXN0Y3AkdmFyaWFibGUuaW1wb3J0YW5jZQ0KaW1wLnRyZWVtaW4gPC0gcmVnLnRyZWUubWluY3AkdmFyaWFibGUuaW1wb3J0YW5jZQ0KDQoNCg0KcGFyKG1mcm93ID0gYygxLDIpLCBvbWEgPSBjKDUsMiwzLDIpKQ0KYmFycGxvdChpbXAudHJlZTFzZSwNCiAgICAgICAgaG9yaXogPSBULA0KICAgICAgICBtYWluID0gbGlzdCgiMXNlIGNwIFJlZ3Jlc3Npb24gVHJlZSBNb2RlbCIsIGZvbnQgPSAxKSwNCiAgICAgICAgeGxhYiA9ICJJbXBvcnRhbmNlIiwNCiAgICAgICAgbGFzID0gMSkNCmJhcnBsb3QoaW1wLnRyZWVtaW4sDQogICAgICAgIGhvcml6ID0gVCwNCiAgICAgICAgbWFpbiA9IGxpc3QoIk1pbiBjcCBSZWdyZXNzaW9uIFRyZWUgTW9kZWwiLCBmb250ID0gMSksDQogICAgICAgIHhsYWIgPSAiSW1wb3J0YW5jZSIsDQogICAgICAgIGxhcyA9IDEpDQptdGV4dCgiVmFyaWFibGUgSW1wb3J0YW5jZSIsIHNpZGUgPSAzLCBsaW5lID0gMSwgb3V0ZXIgPSBULCBjZXggPSAxLjUsIGZvbnQgPSAyKQ0KbXRleHQoIkZpZ3VyZSA3Og0KVmFyaWFibGUgaW1wb3J0YW5jZSBwbG90cyBmb3IgYm90aCBDQVJUIFJlZ3Jlc3Npb24gdHJlZSBtb2RlbHMiLCBzaWRlID0gMSwgbGluZSA9IDEsIG91dGVyID0gVCkNCmBgYA0KDQoNCg0KIyMgQ0FSVCBDbGFzc2lmaWNhdGlvbg0KDQpDQVJUIGNsYXNzaWZpY2F0aW9uIHdhcyBpbml0aWF0ZWQgdXRpbGl6aW5nIHRoZSA8Yj5ycGFydCgpPC9iPiBwYWNrYWdlIHV0aWxpemluZyB0aGUgZnVsbCBtb2RlbCBhcyBhIHN0YXJ0aW5nIHBvaW50LiAgVGhlIHJlZ3Jlc3Npb24gdHJlZSB3YXMgY3JlYXRlZCB1dGlsaXppbmcgdGhlIDxiPkNsYXNzPC9iPiBtZXRob2QgYW5kIDxiPkdpbmk8L2I+IGltcHVyaXR5IG1ldGhvZCBpbmRpY2F0aW5nIHRoYXQgdGhlIHRyZWUgc3BsaXRzIGFyZSBjb21wbGV0ZWQgdXRpbGl6aW5nIHNwbGl0cyB0byBtYXhpbWl6ZSB0aGUgR2luaSBnYWluLiAgVGhpcyBHaW5pIHJlZHVjdGlvbiBhcHByb2FjaCBmb2xsb3dzIHRoZSBiZWxvdyBlcXVhdGlvbiB0byBlbnN1cmUgY2hpbGQgKGlubmVyICYgdGVybWluYWwpIG5vZGVzIGFyZSBzZWxlY3RlZCBiYXNlZCBvbiBzcGxpdHMgdGhhdCBtYXhpbWl6ZSB0aGUgR2luaSBnYWluIChwdXJpdHkpIG9mIGVhY2ggbm9kZS4NCg0KJCQNClx1bmRlcmxpbmV7XG1ib3h7R2luaSBJbXB1cml0eSBSZWR1Y3Rpb259fSBcXCANClxiZWdpbnthbGlnbip9DQpcbWJveHtHaW5pfSAmPSAgMSAtIFxzdW1fe2kgPSAxfV57a30gcF97aX1eezJ9IFwgXCBcICAmXG1ib3h7d2hlcmV9IFwgXCAmcF97aX0gXCBcIFxtYm94e2lzIGEgcHJvcG9ydGlvbiBvZiBjbGFzc30gXCBpIFxcIFxcIA0KXERlbHRhIEcgJj0gXG1ib3h7R2luaX0gKiBTIC0gXGZyYWN7bl9MfXtufSAqIFxtYm94e0dpbml9KlNfTCAtIFxmcmFje25fUn17bn0qXG1ib3h7R2luaX0qU19yIA0KICZcbWJveHt3aGVyZX0gXCAmbl9MIFwgPSBcbWJveHtsZWZ0IG5vZGUgc2FtcGxlIHNpemV9IFxcIA0KJiYmbl9SID0gXG1ib3h7cmlnaHQgbm9kZSBzYW1wbGUgc2l6ZX0NClxlbmR7YWxpZ24qfQ0KJCQNCg0KIyMjIE1vZGVsIENyZWF0aW9uDQoNCkFzIHRoZSBkYXRhIHdhcyBwcmVlbXB0aXZlbHkgc3BsaXQgaW50byB0cmFpbmluZyBhbmQgdGVzdCBkYXRhLCB0aGUgdHJhaW5pbmcgZGF0YSB3YXMgdXRpbGl6ZWQgZm9yIDEwLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiBhcyBwYXJ0IG9mIHRoZSBpbml0aWFsIGZ1bGwgbW9kZWwgY3JlYXRpb24gcHJvY2Vzcy4gIFRoaXMgY3Jvc3MgdmFsaWRhdGlvbiBlbnN1cmVzIHRoZSBwcm9kdWNlZCBtb2RlbCBpcyB0aGUgYmVzdCBmaXQgZm9yIHRoZSB0cmFpbmluZyBkYXRhIGFzIHByb3ZpZGVkLiAgSW5pdGlhbGx5IHRoZSBoeXBlcnBhcmFtZXRlcnMgd2VyZSBpbml0aWF0ZWQgYXM6DQoNCiAgLSBtaW5pbXVtIHNwbGl0ID0gMjAgDQogICAgLSBtaW5pbXVtIG9ic2VydmF0aW9ucyBpbiBhIGdyb3VwIGJlZm9yZSB0aGUgYWxnb3JpdGhtIHdpbGwgdHJ5IHRvIHNwbGl0DQogIC0gbWluaW11bSBidWNrZXQgPSAxMA0KICAgIC0gbWluaW11bSBvYnNlcnZhdGlvbnMgYWxsb3dlZCBpbiBhIGxlYWYgbm9kZQ0KICAtIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgPSAwLjAwMQ0KICAgIC0gbWluaW11bSBpbXByb3ZlbWVudCBpbiB0YXJnZXQgcGFyYW1ldGVyIHRvIGFsbG93IGZvciBzcGxpdCAoQ0FSVCBDbGFzcyA9IEdpbmkgZ2FpbikNCiAgLSBtYXhpbXVtIGRlcHRoID0gNQ0KICAgIC0gbWF4aW11bSBudW1iZXIgb2YgbGV2ZWxzIGluIHRyZWUgb3V0cHV0DQogIC0gR2luaSBpbXB1cml0eQ0KDQpgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9OX0NCiNjcmVhdGUgQ0FSVCByZWdyZXNzaW9uIHRyZWUgKGluaXRpYWwgZnVsbCBtb2RlbCkNCmNsYXNzLnRyZWUgPC0gcnBhcnQoT2IuUmFua34uLCBkYXRhID0gb2IudHJhaW4xLCBtZXRob2QgPSAiY2xhc3MiLA0KICAgICAgICAgICAgICAgICAgICBwYXJtcyA9IGxpc3Qoc3BsaXQgPSAiZ2luaSIpLA0KICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbCgNCiAgICAgICAgICAgICAgICAgICAgbWluc3BsaXQgPSAyMCwNCiAgICAgICAgICAgICAgICAgICAgbWluYnVja2V0ID0gMTAsDQogICAgICAgICAgICAgICAgICAgIGNwID0gMC4wMDEsICNyZWR1Y3Rpb24gaW4gY29tcGxleGl0eSBwYXJhbSBuZWVkZWQgZm9yIHNwbGl0DQogICAgICAgICAgICAgICAgICAgIG1heGRlcHRoID0gNSwNCiAgICAgICAgICAgICAgICAgICAgeHZhbCA9IDEwDQogICAgICAgICAgICAgICAgICAgKSkNCg0KYGBgDQoNCg0KYGBge3IsIGZpZy5oZWlnaHQ9IDEyLCBmaWcud2lkdGg9MTksIG91dC53aWR0aD0iMTAwJSJ9DQojcGxvdCBmdWxsIG1vZGVsIGNsYXNzIHRyZWUNCmZhbmN5UnBhcnRQbG90KGNsYXNzLnRyZWUsICB0eXBlID0gNSwgdHdlYWsgPSAxLjEsIG1haW4gPSAiSW5pdGlhbCBDQVJUIEdpbmkgQ2xhc3NpZmljYXRpb24gVHJlZSIgLCBjYXB0aW9uID0gIkZpZ3VyZSA4Og0KSW5pdGlhbCBDQVJUIEdpbmkgY2xhc3NpZmljYXRpb24gdHJlZSBtb2RlbCAodW5wcnVuZWQpIikNCmBgYA0KDQojIyMgSHlwZXJwYXJhbWV0ZXIgVHVuaW5nDQoNCldpdGggdGhlIGluaXRpYWwgQ0FSVCBjbGFzc2lmaWNhdGlvbiB0cmVlIG1vZGVsIGNyZWF0ZWQgYWJvdmUgaW4gYEZpZ3VyZSA4YCB0aGUgbW9kZWwgd2FzIHRoZW4gdHVuZWQgdG8gYXNzaWduIGJldHRlciBmaXQgaHlwZXJwYXJhbWV0ZXIgdmFsdWVzIGVuc3VyaW5nIGltcHJvdmVkIGZpdCBhbmQgYW4gb3ZlcmFsbCByZWR1Y3Rpb24gaW4gbW9kZWwgc2l6ZS4gIFRoZSBtb2RlbCdzIENQIHZhbHVlcyB3ZXJlIGNvbXBhcmVkIHdpdGggcmVzcGVjdCB0byB0aGUgY3Jvc3MgdmFsaWRhdGVkIGVycm9yICh4ZXJyb3IpIGFuZCBzdGFuZGFyZCBlcnJvciBvZiB0aGUgY3Jvc3MgdmFsaWRhdGVkIGVycm9yICh4c3RkKSB0byBpZGVudGlmeSBuZXcgQ1AgdmFsdWVzIGZvciB1c2Ugd2l0aCB0aGUgaW5pdGlhbCBtb2RlbC4gIEJvdGggdGhlIENQIGFzc29jaWF0ZWQgd2l0aCB0aGUgbWluaW11bSB4ZXJyb3IgKGtub3duIGFzIHRoZSBtaW4gQ1ApIGFuZCB0aGUgbGFyZ2VzdCBDUCBhc3NvY2lhdGVkIHdpdGggdGhlIHhlcnJvciB3aXRoaW4gMSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWluaW11bSB4ZXJyb3IgdmFsdWUgKGtub3duIGFzIHRoZSAxc2UgQ1AgdmFsdWUpIHdlcmUgY29sbGVjdGVkIGZvciBzdWJzZXF1ZW50IHR1bmluZy4gDQoNCg0KPHRhYmxlPg0KPHRyPg0KPHRkPg0KYGBge3J9DQojcGxvdCBvZiBDUCB2YWx1ZXMgYmFzZWQgb24gdGFibGUgc2l6ZQ0Ka2FibGUoY2xhc3MudHJlZSRjcHRhYmxlLCBjYXB0aW9uID0gIlRhYmxlIDc6DQogICAgICA8YnI+IENQIHZhbHVlcyBmb3IgQ0FSVCBHaW5pIGNsYXNzaWZpY2F0aW9uIHRyZWUgcHJ1bmluZyIpICU+JSANCiAga2FibGVfc3R5bGluZyhmb250X3NpemUgPSAxMikNCmBgYA0KPC90ZD4NCjx0ZD4NCmBgYHtyLCBmaWcuaGVpZ2h0PTh9DQpwYXIob21hID1jKDIsMSw1LDEpKQ0KcGxvdGNwKGNsYXNzLnRyZWUpDQptdGV4dCgiQ1AgQ3V0b2ZmIFRlc3QiLCBzaWRlID0gMywgbGluZSA9IDUsIG91dHNpZGUgPSBULCBmb250ID0gMiwgY2V4ID0gMikNCm10ZXh0KCJGaWd1cmUgOToNCkNQIGh5cGVycGFyYW1ldGVyIHR1bmluZywgYmVzdCBmaXQgdmFsdWUgYXQgY3Jvc3Mgb2YgZG90dGVkIGxpbmUuIiwgc2lkZSA9IDEsIGxpbmUgPSA1LCBvdXRzaWRlID0gVCwgZm9udCA9IDEsIGNleCA9IDAuOSkNCmBgYA0KPC90ZD4NCjwvdHI+DQo8L3RhYmxlPg0KDQojIyMgTW9kZWwgUHJ1bmluZyANCg0KVGhlIGNhbGN1bGF0ZWQgQ1AgdmFsdWVzIGZyb20gdGhlIHByZXZpb3VzIHNlY3Rpb24gd2VyZSB0aGVuIGZlZCBpbiB0byB0aGUgbW9kZWwgcHJ1bmluZyBmdW5jdGlvbiA8Yj5wcnVuZSgpPC9iPiB0byBjcmVhdGUgdHdvIHBydW5lZCBDQVJUIEdpbmkgY2xhc3NpZmljYXRpb24gdHJlZSBtb2RlbHMuICBUaGUgZmlyc3QgdHJlZSBtb2RlbCAoYEZpZ3VyZSAxMGApIHdhcyBjcmVhdGVkIHVzaW5nIHRoZSAxc2UgQ1AgdmFsdWUgd2hpbGUgdGhlIHNlY29uZCAoYEZpZ3VyZSAxMWApIHdhcyBjcmVhdGVkIHVzaW5nIHRoZSBtaW5pbXVtIENQIHZhbHVlLiBBcyBkZW1vbnN0cmF0ZWQgYnkgdGhlIGJlbG93IHJlZ3Jlc3Npb24gdHJlZXMsIHRoZSAxc2UgbWV0aG9kIG9mIHR1bmluZyB0aGUgQ1AgaHlwZXJwYXJhbWV0ZXIgcmVzdWx0cyBpbiBhIG1vcmUgc2lnbmlmaWNhbnRseSByZWR1Y2VkIHJlZ3Jlc3Npb24gdHJlZSBtb2RlbC4gDQoNCmBgYHtyfQ0KY3AuY2xhc3MudGFibGUgPC0gY2xhc3MudHJlZSRjcHRhYmxlDQoNCiNnZXQgdmFsdWVzIGZvciBwcnVuZWQgdHJlZXMNCg0KeGVycm9yLm1pbjIgPC0gbWluKGNwLmNsYXNzLnRhYmxlWywieGVycm9yIl0pICNtaW4geGVycm9yDQoNCiNleHRyYWN0IHJvdyB3aXRoIG1pbmltdW0geGVycm9yIGFzIHN0YW5kYWxvbmUgZGF0YSBmcmFtZSwgdGhlbiBwdWxsIENQIHZhbHVlIG91dCBvZiB0aGF0IHJvdw0KbWluLmNwLnJvdzIgPC0gd2hpY2gubWluKGNwLmNsYXNzLnRhYmxlWywgInhlcnJvciJdKQ0KDQptaW4uY3AyIDwtIGNwLmNsYXNzLnRhYmxlW21pbi5jcC5yb3cyLCAiQ1AiXSAjbWluLmNwIGZvciBtaW5jcCB0cmVlDQoNCiNnZXQgeHN0ZCB2YWx1ZSBmcm9tIG1pbmltdW0geGVycm9yIHJvdw0KeGVycm9yLnhzdGQyIDwtIGNwLmNsYXNzLnRhYmxlW21pbi5jcC5yb3cyLCAieHN0ZCJdDQoNCnRocmVzaG9sZDIgPC0geGVycm9yLm1pbjIgKyB4ZXJyb3IueHN0ZDIgI2FkZCB4c3RkIHZhbHVlIHRvIG1pbiB4ZXJyb3IgdG8gZ2V0IDEgc2UgZnJvbSBtaW4NCg0KYmVzdC5jcC5yb3cyIDwtIHdoaWNoKGNwLmNsYXNzLnRhYmxlWywieGVycm9yIl0gPD0gdGhyZXNob2xkMilbMV0gI3B1bGwgcm93IHdoZXJlIHhlcnJvciBpcyBjbG9zZXN0IHRvIGJ1dCBiZWxvdyBtaW4oeGVycm9yKSArIDFzZSB0aHJlc2hvbGQNCmJlc3QuY3AyIDwtIGNwLmNsYXNzLnRhYmxlW2Jlc3QuY3Aucm93MiwgIkNQIl0gI3B1bGwgQ1AgdmFsdWUgZnJvbSByb3cgd2hlcmUgeGVycm9yIHcuaW4gMXNlDQoNCmNsYXNzLmJlc3RjcCA8LSBwcnVuZShjbGFzcy50cmVlLCBjcCA9IGJlc3QuY3AyKSAjcHJ1bmUgdHJlZSB3aXRoIDFzZSBwYXJhbWV0ZXINCmNsYXNzLm1pbmNwIDwtIHBydW5lKGNsYXNzLnRyZWUsIGNwID0gbWluLmNwMikgI3BydW5lIHRyZWUgd2l0aCBtaW4gY3AgcGFyYW1ldGVyDQpgYGANCg0KYGBge3IsIGZpZy5oZWlnaHQ9IDEyLCBmaWcud2lkdGg9MTIsIG91dC53aWR0aD0iMTAwJSJ9DQpmYW5jeVJwYXJ0UGxvdChjbGFzcy5iZXN0Y3AsICB0eXBlID0gNSwgdHdlYWsgPSAxLjAyLCBtYWluID0gIlBydW5lZCBDQVJUIEdpbmkgY2xhc3NpZmljYXRpb24gVHJlZSANCkNQID0gMXNlIiwgY2FwdGlvbiA9IHBhc3RlKCJGaWd1cmUgMTA6DQpQcnVuZWQgQ0FSVCBSZWdyZXNzaW9uIHRyZWUgKDFzZSBtb2RlbCkuICBDUCA9Iiwgcm91bmQoYmVzdC5jcDIsIDUpKSkNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD0gMTIsIGZpZy53aWR0aD0xNiwgb3V0LndpZHRoPSIxMDAlIn0NCmZhbmN5UnBhcnRQbG90KGNsYXNzLm1pbmNwLCAgdHlwZSA9IDUsIHR3ZWFrID0gMS4wMiwgbWFpbiA9ICJQcnVuZWQgQ0FSVCBHaW5pIGNsYXNzaWZpY2F0aW9uIFRyZWUgDQpDUCA9IG1pbiIsIGNhcHRpb24gPSBwYXN0ZSgiRmlndXJlIDExOg0KUHJ1bmVkIENBUlQgUmVncmVzc2lvbiB0cmVlIChtaW4gbW9kZWwpLiAgQ1AgPSIsIHJvdW5kKG1pbi5jcDIsIDUpKSkNCg0KYGBgDQoNCkFzIGRlbW9uc3RyYXRlZCBpbiB0aGUgYWJvdmUgYEZpZ3VyZSAxMGAsIHRoZSAxc2UgQ0FSVCBHaW5pIGNsYXNzaWZpY2F0aW9uIHRyZWUgcmVzdWx0ZWQgaW4gYSBtb2RlbCB0aGF0IGluY29ycG9yYXRlZCB0aGUgZm9sbG93aW5nIGZlYXR1cmUgdmFyaWFibGVzOg0KDQogIC0gdmVnLmRheQ0KICAtIEZILk92ZXJ3ZWlnaHQNCiAgLSBnZW4uaGVpZ2h0DQogIC0gQWdlDQogIC0gTkNQDQogIC0gQ0FFQw0KICAtIHRyYW5zLmFnZQ0KICAtIEhlaWdodA0KICAtIEZBVkMNCiAgLSBNVFJBTlMNCiAgDQpNZWFud2hpbGUsIGFzIGRlbW9uc3RyYXRlZCBpbiBgRmlndXJlIDExYCB0aGUgbWluaW11bSBDUCBDQVJUIEdpbmkgY2xhc3NpZmljYXRpb24gdHJlZSByZXN1bHRlZCBpbiBhIG1vZGVsIHRoYXQgaW5jb3Jwb3JhdGVkIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzOg0KDQogIC0gdmVnLmRheQ0KICAtIEZILk92ZXJ3ZWlnaHQNCiAgLSBnZW4uaGVpZ2h0DQogIC0gQWdlDQogIC0gTkNQDQogIC0gQ0FFQw0KICAtIHRyYW5zLmFnZQ0KICAtIEhlaWdodA0KICAtIEZBVkMNCiAgLSBNVFJBTlMNCiAgLSBUVUUNCiAgDQpBcyB0aGUgbWluaW11bSBDUCBtb2RlbCB1dGlsaXplZCBhIGxvd2VyIENQIHZhbHVlLCB0aGlzIG1vZGVsIHJlcXVpcmVkIGxlc3Mgb2YgYSBjaGFuZ2UgaW4gdGhlIEdpbmkgZ2FpbiBiZWZvcmUgbm9kZXMgd2VyZSBzcGxpdCwgdGh1cyByZXN1bHRpbmcgaW4gYSBicm9hZGVyIG1vZGVsIHRoYXQgaW5jb3Jwb3JhdGVzIGEgbGFyZ2VyIG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcy4gDQoNCg0KIyMjIE1vZGVsIEV2YWx1YXRpb24NCg0KQm90aCB0aGUgMXNlIGFuZCBtaW5pbXVtIENQIENBUlQgR2luaSBjbGFzc2lmaWNhdGlvbiB0cmVlIG1vZGVscyB3ZXJlIHRoZW4gdXRpbGl6ZWQgdG8gZ2VuZXJhdGUgcHJlZGljdGlvbiB2YWx1ZXMgZm9yIG9iZXNpdHkgcmFua3MgaW4gdXRpbGl6aW5nIHRoZSB0ZXN0IGRhdGEgc2V0LiBUaGVzZSBwcmVkaWN0ZWQgdmFsdWVzIHdlcmUgY29tcGFyZWQgdG8gdGhlIGFjdHVhbCB2YWx1ZXMgaW4gdGhlIHRlc3QgZGF0YSBzZXQgdG8gYXNzZXNzIGZvciBtb2RlbCBhY2N1cmFjeSBhbmQgcmVsaWFiaWxpdHkuICBUaGVzZSBDQVJUIEdpbmkgY2xhc3NpZmljYXRpb24gbW9kZWxzIHdlcmUgYWRkaXRpb25hbGx5IGNvbXBhcmVkIHRvIGEgbXVsdGlub21pYWwgcmVncmVzc2lvbiBtb2RlbCB0byBkZXRlcm1pbmUgdGhlaXIgZWZmaWNhY3kgaW4gcmVsYXRpb24gdG8gb3RoZXIgbW9yZSBlc3RhYmxpc2hlZCBzdGF0aXN0aWNhbCBwcmVkaWN0aW9uIG1ldGhvZHMuICBUaGUgbXVsdGlub21pYWwgbW9kZWwgd2FzIGNvbXByaXNlZCB1dGlsaXppbmcgdGhlIHZhcmlhYmxlcyBhcyBpZGVudGlmaWVkIGluIHRoZSAxc2UgQ0FSVCByZWdyZXNzaW9uIHRyZWUgbW9kZWwuDQoNCmBgYHtyfQ0KI3ByZWRzIGZyb20gdHJlZSBtb2RlbCAoY2xhc3MpDQpvYnJhbmsuY2xhc3MuYmVzdC5sYWIgPC0gcHJlZGljdChjbGFzcy5iZXN0Y3AsIG5ld2RhdGEgPSBvYi50ZXN0MSwgdHlwZSA9ICJjbGFzcyIpDQpvYnJhbmsuY2xhc3MuYmVzdC5wcm9iIDwtIHByZWRpY3QoY2xhc3MuYmVzdGNwLCBuZXdkYXRhID0gb2IudGVzdDEsIHR5cGUgPSAicHJvYiIpWywyXQ0Kb2JyYW5rLmNsYXNzLm1pbi5sYWIgPC0gcHJlZGljdChjbGFzcy5taW5jcCwgbmV3ZGF0YSA9IG9iLnRlc3QxLCB0eXBlID0gImNsYXNzIikNCm9icmFuay5jbGFzcy5taW4ucHJvYiA8LSBwcmVkaWN0KGNsYXNzLm1pbmNwLCBuZXdkYXRhID0gb2IudGVzdDEsIHR5cGUgPSAicHJvYiIpWywyXQ0KDQojbWFrZSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVscw0KI21vZGVsIHVzaW5nIHZhcmlhYmxlcyBmcm9tIDFzZSB0cmVlDQpsb2cuMXNlIDwtIG11bHRpbm9tKE9iLlJhbmsgfiB2ZWcuZGF5ICsgRkguT3ZlcndlaWdodCArIGdlbi5oZWlnaHQgKyBBZ2UgKyBOQ1AgKyBDQUVDICsgdHJhbnMuYWdlICsgSGVpZ2h0ICsgRkFWQyArIE1UUkFOUywgZGF0YSA9IG9iLnRyYWluMSwgdHJhY2U9RikNCg0KDQojbG9nIG1vZGVsIHByZWRpY3Rpb25zDQpsb2cucHJlZC5sYWIgPC0gcHJlZGljdChsb2cuMXNlLCBuZXdkYXRhID0gb2IudGVzdDEsIHR5cGUgPSAiY2xhc3MiKQ0KbG9nLnByZWQucHJvYiA8LSBwcmVkaWN0KGxvZy4xc2UsIG5ld2RhdGEgPSBvYi50ZXN0MSwgdHlwZSA9ICJwcm9iIilbLDJdDQoNCg0KI21ha2UgUk9DIGN1cnZlcw0KUk9DLmNsYXNzLjFzZSA8LSByb2Mob2IudGVzdDEkT2IuUmFuaywgb2JyYW5rLmNsYXNzLmJlc3QucHJvYikNClJPQy5jbGFzcy5taW4gPC0gcm9jKG9iLnRlc3QxJE9iLlJhbmssIG9icmFuay5jbGFzcy5taW4ucHJvYikNClJPQy5sb2cuMXNlIDwtIHJvYyhvYi50ZXN0MSRPYi5SYW5rLCBsb2cucHJlZC5wcm9iKQ0KDQojc2Vuc2l0aXZpdGllcw0Kcm9jLmMxLnNlbiA8LSBST0MuY2xhc3MuMXNlJHNlbnNpdGl2aXRpZXMNCnJvYy5jbS5zZW4gPC0gUk9DLmNsYXNzLm1pbiRzZW5zaXRpdml0aWVzDQpyb2MubG9nLnNlbiA8LSBST0MubG9nLjFzZSRzZW5zaXRpdml0aWVzDQoNCiMxIC0gc3BlY2lmaWNpdGllcw0Kcm9jLmMxLnNwZSA8LSAoMSAtIFJPQy5jbGFzcy4xc2Ukc3BlY2lmaWNpdGllcykNCnJvYy5jbS5zcGUgPC0gKDEgLSBST0MuY2xhc3MubWluJHNwZWNpZmljaXRpZXMpDQpyb2MubG9nLnNwZSA8LSAoMSAtIFJPQy5sb2cuMXNlJHNwZWNpZmljaXRpZXMpDQoNCiNBVUMNCnJvYy5jMS5hdWMgPC0gUk9DLmNsYXNzLjFzZSRhdWMNCnJvYy5jbS5hdWMgPC0gUk9DLmNsYXNzLm1pbiRhdWMNCnJvYy5sb2cuYXVjIDwtIFJPQy5sb2cuMXNlJGF1Yw0KDQojYmVzdCBjdXRvZmZzDQpjMS5jcCA8LSBjb29yZHMoUk9DLmNsYXNzLjFzZSwgImJlc3QiLCByZXQgPSBjKCJ0aHJlc2hvbGQiLCAic2Vuc2l0aXZpdHkiLCAic3BlY2lmaWNpdHkiKSkNCmNtLmNwIDwtIGNvb3JkcyhST0MuY2xhc3MubWluLCAiYmVzdCIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzZW5zaXRpdml0eSIsICJzcGVjaWZpY2l0eSIpKQ0KbDEuY3AgPC0gY29vcmRzKFJPQy5sb2cuMXNlLCAiYmVzdCIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzZW5zaXRpdml0eSIsICJzcGVjaWZpY2l0eSIpKQ0KDQpjdXRwb2Zmcy5jIDwtIE5VTEwNCmN1dG9mZnMuYyA8LSBkYXRhLmZyYW1lKA0KICBtZWFzdXJlID0gYygiVGhyZXNob2xkIiwgIlNlbnNpdGl2aXR5IiwgIlNwZWNpZmljaXR5IiwgIkFVQyIpLA0KICBjMS5jcCA9IGMoYzEuY3BbLDFdLCBjMS5jcFssMl0sIGMxLmNwWywzXSwgcm9jLmMxLmF1YyksDQogIGNtLmNwID0gYyhjbS5jcFssMV0sIGNtLmNwWywyXSwgY20uY3BbLDNdLCByb2MuY20uYXVjKSwNCiAgbDEuY3AgPSBjKGwxLmNwWywxXSwgbDEuY3BbLDJdLCBsMS5jcFssM10sIHJvYy5sb2cuYXVjKQ0KKQ0KDQpgYGANCg0KDQojIyMjIFJPQyBDdXJ2ZSBhbmQgQVVDIEV2YWx1YXRpb24NCg0KSW4gb3JkZXIgdG8gY29tcGFyZSB0aGUgZWZmaWNhY3kgb2YgZWFjaCBDQVJUIEdpbmkgY2xhc3NpZmljYXRpb24gdHJlZSBhbmQgdGhlIG11bHRpbm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwsIGFuIFJPQyBwbG90IHdhcyBjb25zdHJ1Y3RlZCBhbmQgYXJlYSB1bmRlciB0aGUgY3VydmUgKEFVQykgdmFsdWVzIHdlcmUgY2FsY3VsYXRlZCBpbiB0aGUgYmVsb3cgYEZpZ3VyZSAxMmAuIE9wdGltYWwgY3V0b2ZmIHBvaW50cyB0byBtYXhpbWl6ZSB0aHJlc2hvbGQsIHNlbnNpdGl2aXR5LCBhbmQgc3BlY2lmaWNpdHkgd2VyZSBjYWxjdWxhdGVkIGFuZCBwbG90dGVkIGluIHRoZSBiZWxvdyBST0MgcGxvdC4gIFRoZXNlIGN1dG9mZiB2YWx1ZXMgYXJlIGFkZGl0aW9uYWxseSBsaXN0ZWQgaW4gYFRhYmxlIDhgIGZvciBlYXNlIG9mIHJlZmVyZW5jZS4gV2hpbGUgbmVpdGhlciBtb2RlbCBwcm92ZWQgdG8gaW5jcmVkaWJseSByZWxpYWJsZSwgdGhlIENBUlQgR2luaSBjbGFzc2lmaWNhdGlvbiB0cmVlIHdpdGggdGhlIDFzZSBDUCB2YWx1ZSByZXR1cm5lZCB0aGUgYmVzdCBBVUMgdmFsdWUgd2hpbGUgcmV0YWluaW5nIHRoZSBoaWdoZXN0IHBvc3NpYmxlIHNlbnNpdGl2aXR5IGFuZCBzcGVjaWZpY2l0eS4gDQoNCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD04LCBvdXQud2lkdGg9IjEwMCUifQ0KcGFyKG9tYSA9IGMoNiwyLDIsMiksIGNleC5tYWluID0gMikNCnBsb3Qocm9jLmMxLnNwZSwgcm9jLmMxLnNlbiwgdHlwZSA9ICJsIiwgeGxpbSA9IGMoMCwxKSwgeGxhYiA9ICIxIC0gU3BlY2lmaWNpdHkgKEZhbHNlIFBvc2l0aXZlKSIsIHlsaW0gPSBjKDAsMSksIHlsYWIgPSAiU2Vuc2l0aXZpdHkgKFRydWUgUG9zaXRpdmUpIiwgbWFpbiA9ICJST0MgQ3VydmVzIikgKyANCiAgbGluZXMocm9jLmMxLnNwZSwgcm9jLmMxLnNlbiwgY29sID0gImRhcmt2aW9sZXQiKSArDQogIGxpbmVzKHJvYy5sb2cuc3BlLCByb2MubG9nLnNlbiwgY29sID0gImNvcmFsMyIpICsNCiAgbGluZXMocm9jLmNtLnNwZSwgcm9jLmNtLnNlbiwgY29sID0gImJsdWUzIikgKw0KICBsaW5lcyh4ID0gYygwOjEpLCB5PWMoMDoxKSwgY29sID0gImRhcmtyZWQiLCBsdHkgPSA0KQ0KICBwb2ludHMoKDEtYzEuY3BbMSwzXSksIGMxLmNwWzEsMl0sIHBjaCA9IDIyLCBiZyA9IGFkanVzdGNvbG9yKCJkYXJrdmlvbGV0IiwgYWxwaGEgPSAwLjUpLCBjZXggPSAxLjc1KSArDQogIHBvaW50cygoMS1jbS5jcFsxLDNdKSwgY20uY3BbMSwyXSwgcGNoID0gMjMsIGJnID0gYWRqdXN0Y29sb3IoImJsdWUzIiwgYWxwaGEgPSAwLjUpLCBjZXggPSAxLjUpICsNCiAgcG9pbnRzKCgxLWwxLmNwWzEsM10pLCBsMS5jcFsxLDJdLCBwY2ggPSAyMSwgYmcgPSBhZGp1c3Rjb2xvcigiY29yYWwzIiwgYWxwaGEgPSAwLjUpLCBjZXggPSAxLjI1KQ0KICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYyhwYXN0ZSgiVHJlZS4xc2UgIiwgIkFVQyA9Iiwgcm91bmQocm9jLmMxLmF1YywgNCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiVHJlZS5taW4gIiwgIkFVQyA9Iiwgcm91bmQocm9jLmNtLmF1YywgNCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiTG9naS4xc2UgIiwgIkFVQyA9Iiwgcm91bmQocm9jLmxvZy5hdWMsIDQpKSksDQogICAgICAgICBjb2wgPSBjKCJkYXJrdmlvbGV0IiwgImJsdWUzIiwgImNvcmFsMyIpLA0KICAgICAgICAgbHdkID0gMiwNCiAgICAgICAgIGx0eSA9IDEsDQogICAgICAgICBjZXggPSAwLjgsDQogICAgICAgICBidHkgPSAibyIpDQptdGV4dCgiRmlndXJlIDEyOiANClJPQyBDb21wYXJyaXNvbiBvZiBDQVJUICBHaW5pIGNsYXNzaWZpY2F0aW9uIHRyZWVzICYgbXVsdGlub21pYWwgcmVncmVzc2lvbiBtb2RlbCIsIHNpZGUgPSAxLCBsaW5lID0gNiwgb3V0c2lkZSA9IFQpDQpgYGANCg0KDQpgYGB7cn0NCmthYmxlKGN1dG9mZnMuYywgYWxpZ24gPSBjKCJsIiwgImMiLCAiYyIsICJjIiksIGNvbC5uYW1lcyA9IGMoIkN1dG9mZiBEZXRhaWxzIiwgIkNsYXNzIDFzZSBNb2RlbCIsICJDbGFzcyBNaW4gTW9kZWwiLCAiTG9naXN0aWMgTW9kZWwgMXNlIFZhcnMiKSwgY2FwdGlvbiA9ICJUYWJsZSA4Og0KICAgICAgPGJyPkEgc3VtbWFyeSBvZiBjYWxjdWxhdGVkIGJlc3QvbWF4aW11bSBjdXRvZmYgcG9pbnRzIGFuZCBtb2RlbCBBVUMgdmFsdWVzLiIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQojIEJvb3RzdHJhcCBCQUdHSU5HDQoNClRvIGZ1cnRoZXIgaW1wcm92ZSB1cG9uIHRoZSBDQVJUIHJlZ3Jlc3Npb24gYW5kIGNsYXNzaWZpY2F0aW9uIHRyZWVzIGNyZWF0ZWQgdG8gcHJlZGljdCBvYmVzaXR5IHJhbmtpbmcsIGFkZGl0aW9uYWwgQkFHR0lORyBhbGdvcml0aG1zIHdlcmUgZW1wbG95ZWQuIEJvb3RzdHJhcCBhZ2dyZWdhdGlvbiB3aXRoIENBUlQgbW9kZWxzIChCQUdHSU5HKSBpcyBhIHByb2Nlc3MgdGhhdCBjb25zaXN0cyBvZiByZXBlYXRlZGx5IHJlLXNhbXBsaW5nIHRoZSB0ZXN0IGRhdGEgd2l0aCByZXBsYWNlbWVudCBhbmQgY2FsY3VsYXRpbmcgdGhlIG1lYW4gZm9yIGVhY2ggc2FtcGxlIGNvbGxlY3RlZC4gIFRoZSBkaXN0cmlidXRpb24gb2YgdGhlc2UgYm9vdHN0cmFwIG1lYW5zIGFyZSB0aGVuIGFuYWx5c2VkIHRvIGRldGVybWluZSB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgdG8gYnVpbGQgdGhlIHByZWRpY3Rpb24gbW9kZWwsIHdpdGggd2VpZ2h0aW5nIGNvbnNpZGVyZWQgZm9yIHRoZSBmcmVxdWVuY3kgb2YgdGhlc2UgY2FsY3VsYXRlZCBtZWFucy4gIFRocm91Z2ggdGhpcyBhdmVyYWdpbmcgb2YgbXVsdGlwbGUgbW9kZWxzIHRoZSBCQUdHSU5HIHByb2Nlc3MgcmVkdWNlcyBtb2RlbCB2YXJpYW5jZSB3aXRob3V0IGxlYWRpbmcgdG8gaW5jcmVhc2VkIGJpYXMuICBUaGlzIGFwcHJvYWNoIGludHJvZHVjZXMgYSBsYXllciBvZiByYW5kb21uZXNzIHRvIHRoZSBtb2RlbCBidWlsZGluZyBwcm9jZXNzIGZ1cnRoZXIgcHJldmVudGluZyBvdmVyZml0dGluZy4gIFRoZSBpbnRyb2R1Y3Rpb24gb2YgcmFuZG9tbmVzcyBpbiB0aGUgbW9kZWwgYnVpbGRpbmcgcHJvY2VzcyBpcyBlc3NlbnRpYWwgdG8gZW5zdXJpbmcgYSBtb2RlbCdzIGFkYXB0YWJpbGl0eSB0byBuZXcgcG9wdWxhdGlvbiBkYXRhIGZvciBwcmVkaWN0aW9uLiANCg0KIyMgQkFHR0lORyBSZWdyZXNzaW9uDQoNCkFzIHRoZSBkYXRhIHdhcyBwcmVlbXB0aXZlbHkgc3BsaXQgaW50byB0cmFpbmluZyBhbmQgdGVzdCBkYXRhLCB0aGUgdHJhaW5pbmcgZGF0YSB3YXMgdXRpbGl6ZWQgZm9yIDEwLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiBhcyBwYXJ0IG9mIHRoZSBpbml0aWFsIEJBR0dJTkcgcmVncmVzc2lvbiBtb2RlbCBjcmVhdGlvbiBwcm9jZXNzLiAgVGhpcyBjcm9zcyB2YWxpZGF0aW9uIGVuc3VyZXMgdGhlIHByb2R1Y2VkIG1vZGVsIGlzIHRoZSBiZXN0IGZpdCBmb3IgdGhlIHRyYWluaW5nIGRhdGEgYXMgcHJvdmlkZWQuICBJbml0aWFsbHkgdGhlIGh5cGVycGFyYW1ldGVycyB3ZXJlIGluaXRpYXRlZCBhcyBtdWx0aXBsZSB2YWx1ZXMgd2l0aCBhIGZvciBsb29wIHRvIGlkZW50aWZ5IHRoZSBiZXN0IGZpdCBwYXJhbWV0ZXIgdmFsdWVzOg0KDQogIC0gbmJhZ2cgPSAxMCAmIDI1IA0KICAgIC0gbnVtYmVyIG9mIGJhZ2dlZCB0cmVlcw0KICAtIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgPSAwLjAxICYgMC41DQogICAgLSBtaW5pbXVtIGltcHJvdmVtZW50IGluIHRhcmdldCBwYXJhbWV0ZXIgdG8gYWxsb3cgZm9yIHNwbGl0DQogIC0gbWF4aW11bSBkZXB0aCA9IDUgJiAxMA0KICAgIC0gbWF4aW11bSBudW1iZXIgb2YgbGV2ZWxzIGluIHRyZWUgb3V0cHV0DQoNCg0KYGBge3J9DQojQ1YgY29udHJvbA0KY3RybC5iYWcgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDUsIHZlcmJvc2VJdGVyID0gVCkNCg0KI2RlZmluZSBwYXJhbXMgdG8gdGVzdA0KbmJhZ2cudmFsIDwtIGMoMTAsIDI1KQ0KY3AudmFsIDwtIGMoMC4wMSwgMC41KQ0KbWF4ZGVwdGgudmFsIDwtIGMoNSwgMTApDQoNCmJhZyA8LSBkYXRhLmZyYW1lKE5VTEwpDQoNCmZvciAobmJhZ2cgaW4gbmJhZ2cudmFsKSB7DQogIGZvciAoY3AgaW4gY3AudmFsKSB7DQogICAgZm9yIChtYXhkZXB0aCBpbiBtYXhkZXB0aC52YWwpIHsNCiAgICAgIHNldC5zZWVkKDEyNDUpDQogICAgICBtb2RlbCA8LSBiYWdnaW5nKE9iLlJhbmsgfi4sIGRhdGEgPSBvYi50cmFpbiwNCiAgICAgICAgICAgICAgICAgICAgICAgbmJhZ2cgPSBuYmFnZywNCiAgICAgICAgICAgICAgICAgICAgICAgY29vYiA9IFQsDQogICAgICAgICAgICAgICAgICAgICAgIHRyQ29udHJvbCA9IGN0cmwuYmFnLA0KICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbChjcCA9IGNwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IG1heGRlcHRoKQ0KICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICBvb2IuZXJyb3IgPC0gbW9kZWwkZXJyDQogICAgICBiYWcgPC0gcmJpbmQoYmFnLCBkYXRhLmZyYW1lKG5iYWdnID0gbmJhZ2csDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNwID0gY3AsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heGRlcHRoID0gbWF4ZGVwdGgsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9vYi5lcnJvciA9IG9vYi5lcnJvcikpDQogICAgfQ0KICB9DQp9DQpgYGANCg0KYGBge3J9DQojcHVsbCBvdXQgYmVzdCB2YWx1ZXMNCmJlc3QuYmFnZyA8LSBiYWdbd2hpY2gubWluKGJhZyRvb2IuZXJyb3IpLF0NCmthYmxlKGJlc3QuYmFnZywgY2FwdGlvbiA9ICJUYWJsZSA5Og0KICAgICAgPGJyPkEgc3VtbWFyeSBvZiBiZXN0IGh5cGVycGFyYW1ldGVyIHZhbHVlcy4iKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCiMjIyBSTVNFIE1BRSBhbmQgUiBTcXVhcmVkDQoNClV0aWxpemluZyB0aGUgYmVzdCBpZGVudGlmaWVkIGh5cGVycGFyYW1ldGVyIHZhbHVlcyBhcyBsaXN0ZWQgYWJvdmUgaW4gYFRhYmxlIDlgIHRoZSBpbml0aWFsIEJBR0dJTkcgcmVncmVzc2lvbiBtb2RlbCB3YXMgdHJhaW5lZC4gIE9uY2UgdHJhaW5lZCwgdGhlIG1vZGVsIHdhcyB1dGlsaXplZCB0byBvYnRhaW4gbW9kZWwgYXNzZXNzbWVudCBzdGF0aXN0aWNzIHN1Y2ggYXMgUk1TRSwgUiBzcXVhcmVkIGFuZCBNQUUuICBBcyBkZW1vbnN0cmF0ZWQgYmVsb3cgaW4gYFRhYmxlIDlgIHRoZSBCQUdHSU5HIHJlZ3Jlc3Npb24gbW9kZWwgaGFzIHByb3ZlbiB0byBiZSB0aGUgYmVzdCBmaXQgb2YgYWxsIGZpdmUgcmVncmVzc2lvbiBiYXNlZCBtb2RlbHMsIHJldHVybmluZyB0aGUgZ3JlYXRlc3QgciReMiQgdmFsdWUgYW5kIGxvd2VzdCBSTVNFIGFuZCBNU0UgdmFsdWVzIG9mIGFsbCBtb2RlbHMgY29tcGFyZWQuIA0KDQoNCmBgYHtyfQ0KI3RyYWluIGZpbmFsIG1vZGVsDQpiYWcuZmluYWwgPC0gYmFnZ2luZygNCiAgT2IuUmFuayB+LiwNCiAgb2IudHJhaW4sDQogIG5iYWdnID0gYmVzdC5iYWdnJG5iYWdnLA0KICBjb29iID0gVCwNCiAgdHJDb250cm9sID0gY3RybC5iYWcsDQogIGNvbnRyb2wgPSBycGFydC5jb250cm9sKGNwID0gYmVzdC5iYWdnJGNwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtYXhkZXB0aCA9IGJlc3QuYmFnZyRtYXhkZXB0aCksDQogIGltcG9ydGFuY2UgPSBUDQopDQoNCmJhZ2cucHJlZCA8LSBwcmVkaWN0KGJhZy5maW5hbCwgbmV3ZGF0YSA9IG9iLnRlc3QpDQoNCmJhZy5lcnJvciA8LSBwb3N0UmVzYW1wbGUocHJlZCA9IGJhZ2cucHJlZCwgb2JzID0gb2IudGVzdCRPYi5SYW5rKQ0KDQpqb2luLmVycm9yIDwtIHJlZy5lcnJvcnNbLTEsXQ0Kam9pbi5lcnJvciRiYWcucmVnIDwtIGMoYXMubnVtZXJpYyhiYWcuZXJyb3JbMV0pLCBhcy5udW1lcmljKGJhZy5lcnJvclszXSksIGFzLm51bWVyaWMoYmFnLmVycm9yWzJdKSkNCg0Ka2FibGUoam9pbi5lcnJvciwgY29sLm5hbWVzID0gYygiQXNtLiBNZWFzdXJlcyIsICIxc2UgQ0FSVCBUcmVlIiwgIk1pbiBDUCBDQVJUIFRyZWUiLCAiMXNlIExpbmVhciIsICJzdGVwQUlDIExpbmVhciIsICJCQUdHSU5HIFJlZ3Jlc3Npb24iKSAsIGNhcHRpb24gPSAiVGFibGUgOToNCiAgICAgIDxicj5Nb2RlbCBhc3Nlc3NtZW50IHN0YXRpc3RpY3MiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCiMjIyBWYXJpYWJsZSBJbXBvcnRhbmNlDQoNCkEgc3VtbWFyeSBvZiB0aGUgdmFyaWFibGUgaW1wb3J0YW5jZSBmb3IgYm90aCB0aGUgQkFHR0lORyByZWdyZXNzaW9uIGlzIGluY2x1ZGVkIGJlbG93IGluIGBGaWd1cmUgMTNgLiAgVW5saWtlIHdoYXQgd2FzIHNlZW4gd2l0aCB0aGUgQ0FSVCByZWdyZXNzaW9uIG1vZGVscywgdGhlIEJBR0dJTkcgcmVncmVzc2lvbiBtb2RlbCBwbGFjZXMgc2lnbmlmaWNhbnRseSBncmVhdGVyIGltcG9ydGFuY2Ugb24gYWxsIHZhcmlhYmxlcyBpbiB0aGUgZGF0YSBzZXQuDQoNCmBgYHtyfQ0KYmFnLmltcG9ydCA8LSB2YXJJbXAoYmFnLmZpbmFsKQ0KDQpnZXQuYmFnZ2luZy5pbXBvcnRhbmNlIDwtIGZ1bmN0aW9uKG1vZGVsKSB7DQogIHRyZWVzIDwtIG1vZGVsJG10cmVlcw0KICANCiAgaW1wIDwtIG51bWVyaWMobGVuZ3RoKHRyZWVzW1sxXV0kYnRyZWUkdmFyaWFibGUuaW1wb3J0YW5jZSkpDQogIG5hbWVzKGltcCkgPC0gbmFtZXModHJlZXNbWzFdXSRidHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlKQ0KICANCiAgZm9yKHRyZWUgaW4gdHJlZXMpew0KICAgIGltcFtuYW1lcyh0cmVlJGJ0cmVlJHZhcmlhYmxlLmltcG9ydGFuY2UpXSA8LSBpbXBbbmFtZXModHJlZSRidHJlZSR2YXJpYWJsZS5pbXBvcnRhbmNlKV0gKyB0cmVlJGJ0cmVlJHZhcmlhYmxlLmltcG9ydGFuY2UNCiAgfQ0KICANCiAgaW1wIDwtIGltcC9sZW5ndGgodHJlZXMpDQogIHJldHVybihpbXApDQp9DQoNCmJhZy5pbXAuc2MgPC0gc29ydChnZXQuYmFnZ2luZy5pbXBvcnRhbmNlKGJhZy5maW5hbCksIGRlY3JlYXNpbmcgPSBUKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0PTcsIG91dC53aWR0aD0iMTAwJSJ9DQpwYXIob21hID0gYyg1LDIsMywyKSkNCmJhcnBsb3QoYmFnLmltcC5zYywNCiAgICAgICAgaG9yaXogPSBULA0KICAgICAgICBtYWluID0gbGlzdCgiQmFnZ2luZyBSZWdyZXNzaW9uIFRyZWUgTW9kZWwiLCBmb250ID0gMSksDQogICAgICAgIHhsYWIgPSAiSW1wb3J0YW5jZSIsDQogICAgICAgIGxhcyA9IDEpDQptdGV4dCgiVmFyaWFibGUgSW1wb3J0YW5jZSIsIHNpZGUgPSAzLCBsaW5lID0gMSwgb3V0ZXIgPSBULCBjZXggPSAxLjUsIGZvbnQgPSAyKQ0KbXRleHQoIkZpZ3VyZSAxMzoNClZhcmlhYmxlIGltcG9ydGFuY2UgcGxvdHMgZm9yIEJBR0dJTkcgUmVncmVzc2lvbiB0cmVlIG1vZGVsIiwgc2lkZSA9IDEsIGxpbmUgPSAxLCBvdXRlciA9IFQpDQpgYGANCg0KIyMgQkFHR0lORyBDbGFzc2lmaWNhdGlvbg0KDQpBcyB0aGUgZGF0YSB3YXMgcHJlZW1wdGl2ZWx5IHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSwgdGhlIHRyYWluaW5nIGRhdGEgd2FzIHV0aWxpemVkIGZvciAxMC1mb2xkIGNyb3NzIHZhbGlkYXRpb24gYXMgcGFydCBvZiB0aGUgaW5pdGlhbCBCQUdHSU5HIGNsYXNzaWZpY2F0aW9uIG1vZGVsIGNyZWF0aW9uIHByb2Nlc3MuICBUaGlzIGNyb3NzIHZhbGlkYXRpb24gZW5zdXJlcyB0aGUgcHJvZHVjZWQgbW9kZWwgaXMgdGhlIGJlc3QgZml0IGZvciB0aGUgdHJhaW5pbmcgZGF0YSBhcyBwcm92aWRlZC4gIEluaXRpYWxseSB0aGUgaHlwZXJwYXJhbWV0ZXJzIHdlcmUgaW5pdGlhdGVkIGFzIG11bHRpcGxlIHZhbHVlcyB3aXRoIGEgZm9yIGxvb3AgdG8gaWRlbnRpZnkgdGhlIGJlc3QgZml0IHBhcmFtZXRlciB2YWx1ZXM6DQoNCiAgLSBuYmFnZyA9IDEwICYgMjUgDQogICAgLSBudW1iZXIgb2YgYmFnZ2VkIHRyZWVzDQogIC0gbWluaW11bSBzcGxpdCA9IDUgJiAxMA0KICAgIC0gbWluaW11bSBudW1iZXIgb2YgdmFyaWFibGVzIHJlcXVpcmVkIGluIGEgbm9kZSBwcmlvciB0byBzcGxpdCBhdHRlbXB0cw0KICAtIGNvbXBsZXhpdHkgcGFyYW1ldGVyIChjcCkgPSAwLjAxICYgMC4wMDENCiAgICAtIG1pbmltdW0gaW1wcm92ZW1lbnQgaW4gdGFyZ2V0IHBhcmFtZXRlciB0byBhbGxvdyBmb3Igc3BsaXQNCiAgLSBtYXhpbXVtIGRlcHRoID0gNSAmIDEwDQogICAgLSBtYXhpbXVtIG51bWJlciBvZiBsZXZlbHMgaW4gdHJlZSBvdXRwdXQNCg0KYGBge3J9DQpiYWdocC5ncmlkIDwtIGV4cGFuZC5ncmlkKA0KICBuYmFnZyA9IGMoMjUsIDUwKSwNCiAgbWluc3BsaXQgPSBjKDUsIDEwKSwNCiAgbWF4ZGVwdGggPSBjKDUsIDEwKSwNCiAgY3AgPSBjKDAuMDEsIDAuMDAxKQ0KKQ0KDQpiYWcucmVzdWx0cyA8LSBkYXRhLmZyYW1lKE5VTEwpDQpiYWcuYWMgPC0gMA0KYmFnLnBhcmFtIDwtIGxpc3QoTlVMTCkNCg0KZm9yKGkgaW4gMTpucm93KGJhZ2hwLmdyaWQpKSB7DQogIHBhcmFtcyA8LSBiYWdocC5ncmlkW2ksXQ0KICANCiAgcnBhcnQuY29udHJvbCA8LSBycGFydC5jb250cm9sKA0KICAgIG1pbnNwbGl0ID0gcGFyYW1zJG1pbnNwbGl0LA0KICAgIG1heGRlcHRoID0gcGFyYW1zJG1heGRlcHRoLA0KICAgIGNwID0gcGFyYW1zJGNwDQogICkNCiAgDQogIGJhZy5tb2RlbCA8LSBiYWdnaW5nKE9iLlJhbmsgfi4sIG9iLnRyYWluMSwNCiAgICAgICAgICAgICAgICAgICAgICAgbmJhZ2cgPSBwYXJhbXMkbmJhZ2csDQogICAgICAgICAgICAgICAgICAgICAgIGNvb2IgPSBULA0KICAgICAgICAgICAgICAgICAgICAgICBjb250cm9sID0gcnBhcnQuY29udHJvbA0KICAgICAgICAgICAgICAgICAgICAgICApDQogIA0KICBiYWcucHJlZHMgPC0gcHJlZGljdChiYWcubW9kZWwsIG5ld2RhdGEgPSBvYi50ZXN0MSkNCiAgDQogIGJhZy5jb25mIDwtIGNvbmZ1c2lvbk1hdHJpeChiYWcucHJlZHMsIG9iLnRlc3QxJE9iLlJhbmspDQogIGFjY3VyYWN5IDwtIGJhZy5jb25mJG92ZXJhbGxbIkFjY3VyYWN5Il0NCiAgDQogIGJhZy5yZXN1bHRzIDwtIHJiaW5kKGJhZy5yZXN1bHRzLCBkYXRhLmZyYW1lKA0KICAgIG5iYWdnID0gcGFyYW1zJG5iYWdnLA0KICAgIG1pbnNwbGl0ID0gcGFyYW1zJG1pbnNwbGl0LA0KICAgIG1heGRlcHRoID0gcGFyYW1zJG1heGRlcHRoLA0KICAgIGNwID0gcGFyYW1zJGNwLA0KICAgIEFjY3VyYWN5ID0gYWNjdXJhY3kNCiAgKSkNCiAgDQogIGlmKGFjY3VyYWN5ID4gYmFnLmFjKXsNCiAgICBiYWcuYWMgPC0gYWNjdXJhY3kNCiAgICBiYWcucGFyYW0gPC0gcGFyYW1zDQogIH0NCn0NCg0KYGBgDQoNCmBgYHtyfQ0KDQprYWJsZShiYWcucGFyYW0sIGNhcHRpb24gPSAiVGFibGUgMTA6DQogICAgICA8YnI+QSBzdW1tYXJ5IG9mIGJlc3QgaHlwZXJwYXJhbWV0ZXIgdmFsdWVzLiIpICU+JSANCiAga2FibGVfc3R5bGluZw0KYGBgDQoNClRoZSBCQUdHSU5HIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHdhcyB0aGVuIHV0aWxpemVkIHRvIGdlbmVyYXRlIHByZWRpY3Rpb24gdmFsdWVzIGZvciBvYmVzaXR5IHJhbmtzIGluIHV0aWxpemluZyB0aGUgdGVzdCBkYXRhIHNldC4gVGhlc2UgcHJlZGljdGVkIHZhbHVlcyB3ZXJlIGNvbXBhcmVkIHRvIHRoZSBhY3R1YWwgdmFsdWVzIGluIHRoZSB0ZXN0IGRhdGEgc2V0IHRvIGFzc2VzcyBmb3IgbW9kZWwgYWNjdXJhY3kgYW5kIHJlbGlhYmlsaXR5LiAgVGhpcyBCQUdHSU5HIGNsYXNzaWZpY2F0aW9uIG1vZGVsIHdhcyBhZGRpdGlvbmFsbHkgY29tcGFyZWQgdG8gdGhlIHByZXZpb3VzbHkgY29uc3RydWN0ZWQgQ0FSVCBHaW5pIGNsYXNzaWZpY2F0aW9uIG1vZGVsIGFuZCB0aGUgcHJldmlvdXNseSB1dGlsaXplZCBtdWx0aW5vbWlhbCByZWdyZXNzaW9uIG1vZGVsIHRvIGRldGVybWluZSB0aGVpciBlZmZpY2FjeSBpbiByZWxhdGlvbiB0byBvdGhlciBtb3JlIGVzdGFibGlzaGVkIHN0YXRpc3RpY2FsIHByZWRpY3Rpb24gbWV0aG9kcy4gDQoNCkEgY29uZnVzaW9uIG1hdHJpeCBvZiB0aGUgcHJlZGljdGVkIG9iZXNpdHkgcmFuayB2YWx1ZXMgcHJvZHVjZWQgYnkgdGhlIEJBR0dJTkcgY2xhc3NpZmljYXRpb24gbW9kZWwgY29tcGFyZWQgdG8gdGhlIHRlc3QgZGF0YSB2YWx1ZXMgaXMgaW5jbHVkZWQgYmVsb3cgaW4gYFRhYmxlIDExYC4NCg0KYGBge3J9DQpiZXN0LmNvbnRyb2wgPC0gcnBhcnQuY29udHJvbCgNCiAgbWluc3BsaXQgPSBiYWcucGFyYW0kbWluc3BsaXQsDQogIG1heGRlcHRoID0gYmFnLnBhcmFtJG1heGRlcHRoLA0KICBjcCA9IGJhZy5wYXJhbSRjcA0KKQ0KDQpmaW5hbC5iYWcuY2xhcyA8LSBiYWdnaW5nKA0KICBPYi5SYW5rIH4uLCBvYi50cmFpbjEsDQogIG5iYWdnID0gYmFnLnBhcmFtJG5iYWdnLA0KICBjb29iID0gVCwNCiAgY29udHJvbCA9IGJlc3QuY29udHJvbA0KKQ0KDQpiYWcuYy5wcmVkcyA8LSBwcmVkaWN0KGZpbmFsLmJhZy5jbGFzLCBuZXdkYXRhID0gb2IudGVzdDEpDQoNCmZpbmFsLmNvbmYgPC0gY29uZnVzaW9uTWF0cml4KGJhZy5jLnByZWRzLCBvYi50ZXN0MSRPYi5SYW5rKQ0KDQprYWJsZShmaW5hbC5jb25mJHRhYmxlLCBjb2wubmFtZXMgPSBjKCJQcmVkaWN0ZWQgVmFsdWVzIiwgIkFjdHVhbCBJbnNmLiBXZWlnaHQiLCAiQWN0dWFsIE5vcm1hbCBXZWlnaHQiLCAiQWN0dWFsIE9iZXNpdHkgMSIsICJBY3R1YWwgT2Jlc2l0eSAyIiwgIkFjdHVhbCBPYmVzaXR5IDMiLCAiQWN0dWFsIE92ZXJ3ZWlnaHQgMSIsICJBY3R1YWwgT3ZlcndlaWdodCAyIiksIGNhcHRpb24gPSAiVGFibGUgMTE6DQo8YnI+Q29uZnVzaW9uIG1hdHJpeCBvZiBCQUdHSU5HIGNsYXNzaWZpY2F0aW9uIHByZWRpY3RlZCBvYmVzaXR5IHJhbmsgdmFsdWVzLiIpICU+JSANCiAga2FibGVfc3R5bGluZygpICU+JSANCiAgc2Nyb2xsX2JveCgiMTAwJSIpDQpgYGANCg0KDQpgYGB7cn0NCg0KI21ha2UgUk9DIGN1cnZlcw0Kcm9jLmJhZyA8LSByb2Mob2IudGVzdDEkT2IuUmFuaywgYXMubnVtZXJpYyhiYWcuYy5wcmVkcykpDQoNCiNzZW5zaXRpdml0aWVzDQpiYWcuc2VuIDwtIHJvYy5iYWckc2Vuc2l0aXZpdGllcw0KDQojMSAtIHNwZWNpZmljaXRpZXMNCmJhZy5zcGUgPC0gKDEgLSByb2MuYmFnJHNwZWNpZmljaXRpZXMpDQoNCiNBVUMNCmJhZy5hdWMgPC0gUk9DLmNsYXNzLjFzZSRhdWMNCg0KI2Jlc3QgY3V0b2Zmcw0KYmFnLmNwIDwtIGNvb3Jkcyhyb2MuYmFnLCAiYmVzdCIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzZW5zaXRpdml0eSIsICJzcGVjaWZpY2l0eSIpKQ0KDQpjdXRwb2Zmcy5iIDwtIE5VTEwNCmN1dG9mZnMuYiA8LSBkYXRhLmZyYW1lKA0KICBtZWFzdXJlID0gYygiVGhyZXNob2xkIiwgIlNlbnNpdGl2aXR5IiwgIlNwZWNpZmljaXR5IiwgIkFVQyIpLA0KICBiYWcuY3AgPSBjKGJhZy5jcFssMV0sIGJhZy5jcFssMl0sIGJhZy5jcFssM10sIGJhZy5hdWMpDQopDQoNCmBgYA0KDQojIyMgUk9DIEN1cnZlIGFuZCBBVUMgRXZhbHVhdGlvbg0KDQpJbiBvcmRlciB0byBjb21wYXJlIHRoZSBlZmZpY2FjeSBvZiB0aGUgbW9kZWwsIGFuIFJPQyBwbG90IHdhcyBjb25zdHJ1Y3RlZCBhbmQgYXJlYSB1bmRlciB0aGUgY3VydmUgKEFVQykgdmFsdWVzIHdlcmUgY2FsY3VsYXRlZCBpbiB0aGUgYmVsb3cgYEZpZ3VyZSAxM2AuIE9wdGltYWwgY3V0b2ZmIHBvaW50cyB0byBtYXhpbWl6ZSB0aHJlc2hvbGQsIHNlbnNpdGl2aXR5LCBhbmQgc3BlY2lmaWNpdHkgd2VyZSBjYWxjdWxhdGVkIGFuZCBwbG90dGVkIGluIHRoZSBiZWxvdyBST0MgcGxvdC4gIFRoZXNlIGN1dG9mZiB2YWx1ZXMgYXJlIGFkZGl0aW9uYWxseSBsaXN0ZWQgaW4gYFRhYmxlIDEyYCBmb3IgZWFzZSBvZiByZWZlcmVuY2UuIFdoaWxlIG5laXRoZXIgbW9kZWwgcHJvdmVkIHRvIGluY3JlZGlibHkgcmVsaWFibGUsIHRoZSBCQUdHSU5HIGNsYXNzaWZpY2F0aW9uIHJldHVybmVkIHRoZSBiZXN0IEFVQyB2YWx1ZSB3aGlsZSByZXRhaW5pbmcgdGhlIGhpZ2hlc3QgcG9zc2libGUgc2Vuc2l0aXZpdHkgYW5kIHNwZWNpZmljaXR5LiANCg0KDQpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OCwgb3V0LndpZHRoPSIxMDAlIn0NCnBhcihvbWEgPSBjKDYsMiwyLDIpLCBjZXgubWFpbiA9IDIpDQpwbG90KHJvYy5jMS5zcGUsIHJvYy5jMS5zZW4sIHR5cGUgPSAibCIsIHhsaW0gPSBjKDAsMSksIHhsYWIgPSAiMSAtIFNwZWNpZmljaXR5IChGYWxzZSBQb3NpdGl2ZSkiLCB5bGltID0gYygwLDEpLCB5bGFiID0gIlNlbnNpdGl2aXR5IChUcnVlIFBvc2l0aXZlKSIsIG1haW4gPSAiUk9DIEN1cnZlcyIsIGNvbCA9ICJkYXJrdmlvbGV0IikgKw0KICBsaW5lcyhyb2MuY20uc3BlLCByb2MuY20uc2VuLCBjb2wgPSAiYmx1ZTMiKSArDQogIGxpbmVzKHJvYy5sb2cuc3BlLCByb2MubG9nLnNlbiwgY29sID0gImNvcmFsMyIpICsNCiAgbGluZXMoYmFnLnNwZSwgYmFnLnNlbiwgY29sID0gInllbGxvdzQiKSArDQogIGxpbmVzKHggPSBjKDA6MSksIHkgPSBjKDAsMSksIGNvbCA9ICJkYXJrcmVkIiwgbHR5ID0gMykgKw0KICBwb2ludHMoKDEtYzEuY3BbMSwzXSksIGMxLmNwWzEsMl0sIHBjaCA9IDIxLCBiZyA9IGFkanVzdGNvbG9yKCJkYXJrdmlvbGV0IiwgYWxwaGEgPSAwLjUpLCBjZXggPSAxLjc1KSArDQogIHBvaW50cygoMS1jbS5jcFsxLDNdKSwgY20uY3BbMSwyXSwgcGNoID0gMjIsIGJnID0gYWRqdXN0Y29sb3IoImJsdWUzIiwgYWxwaGEgPSAwLjUpLCBjZXggPSAxLjUpICsNCiAgcG9pbnRzKCgxLWwxLmNwWzEsM10pLCBsMS5jcFsxLDJdLCBwY2ggPSAyMywgYmcgPSBhZGp1c3Rjb2xvcigiY29yYWwzIiwgYWxwaGEgPSAwLjUpLCBjZXggPSAxLjI1KSArDQogIHBvaW50cygoMS1iYWcuY3BbMSwzXSksIGJhZy5jcFsxLDJdLCBwY2ggPSAyNCwgYmcgPSBhZGp1c3Rjb2xvcigieWVsbG93NCIsIGFscGhhID0gMC41KSwgY2V4ID0gMS41KQ0KICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYyhwYXN0ZSgiVHJlZS4xc2UgIiwgIkFVQyA9Iiwgcm91bmQocm9jLmMxLmF1YywgNCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiVHJlZS5taW4gIiwgIkFVQyA9Iiwgcm91bmQocm9jLmNtLmF1YywgNCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiTG9naS4xc2UgIiwgIkFVQyA9Iiwgcm91bmQocm9jLmxvZy5hdWMsIDQpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoIkJBR0dJTkciLCAiQVVDID0iLCByb3VuZChiYWcuYXVjLCA0KSkpLA0KICAgICAgICAgY29sID0gYygiZGFya3Zpb2xldCIsICJibHVlMyIsICJjb3JhbDMiLCAieWVsbG93NCIpLA0KICAgICAgICAgbHdkID0gMiwNCiAgICAgICAgIGx0eSA9IDEsDQogICAgICAgICBjZXggPSAwLjgsDQogICAgICAgICBidHkgPSAibyIpDQptdGV4dCgiRmlndXJlIDEzOiANClJPQyBDb21wYXJyaXNvbiBvZiBCQUdHSU5HIGNsYXNzaWZpY2F0aW9uIG1vZGVsLCANCkNBUlQgR2luaSBjbGFzc2lmaWNhdGlvbiB0cmVlcyAmIG11bHRpbm9taWFsIA0KcmVncmVzc2lvbiBtb2RlbCIsIHNpZGUgPSAxLCBsaW5lID0gNy41LCBvdXRzaWRlID0gVCkNCmBgYA0KDQpgYGB7cn0NCmN1dG9mZnMgPC0gY2JpbmQoY3V0b2Zmcy5jLCBjdXRvZmZzLmJbMl0pDQoNCmthYmxlKGN1dG9mZnMsIGFsaWduID0gYygibCIsICJjIiwgImMiLCAiYyIsICJjIiksIGNvbC5uYW1lcyA9IGMoIkN1dG9mZiBEZXRhaWxzIiwgIkNsYXNzIDFzZSBNb2RlbCIsICJDbGFzcyBNaW4gTW9kZWwiLCAiTG9naXN0aWMgTW9kZWwgMXNlIFZhcnMiLCAiQkFHR0lORyBDbGFzcyBNb2RlbCIpLCBjYXB0aW9uID0gIlRhYmxlIDEyOg0KICAgICAgPGJyPkEgc3VtbWFyeSBvZiBjYWxjdWxhdGVkIGJlc3QvbWF4aW11bSBjdXRvZmYgcG9pbnRzICYgQVVDIHZhbHVlcyIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KDQojIENvbmNsdXNpb25zDQoNCk92ZXJhbGwsIHRoZSBCQUdHSU5HIHJlZ3Jlc3Npb24gYW5kIGNsYXNzaWZpY2F0aW9uIG1vZGVscyBwcm9kdWNlZCB0aGUgbW9zdCByZWxpYWJsZSBwcmVkaWN0aW9ucyBmb3IgaW5kaXZpZHVhbHMnIG9iZXNpdHkgcmF0aW5ncyB3aGVuIG9taXR0aW5nIHdlaWdodCBkYXRhLiAgVGhpcyBhcHByb2FjaCBpbnRyb2R1Y2VzIHJhbmRvbW5lc3MgdG8gdGhlIG1vZGVscyBhcyB3ZWxsLCBmdXJ0aGVyIGltcHJvdmluZyB0aGUgbW9kZWxzJyBwZXJmb3JtYW5jZSBhcyBuZXcgdGVzdCBkYXRhIGlzIGludHJvZHVjZWQuICBBZGRpdGlvbmFsbHksIHRyZWF0aW5nIHRoZSBvYmVzaXR5IHJhbmsgdmFsdWVzIGFzIGNhdGVnb3JpY2FsIHByb3ZlZCB0byB5aWVsZCBtb3JlIGFjY3VyYXRlIHByZWRpY3Rpb24gcmVzdWx0cywgdGhlcmVmb3JlIHdoZW4gYnVpbGRpbmcgZnV0dXJlIHByZWRpY3Rpb24gbW9kZWxzIGZvciBvYmVzaXR5IHJhbmsgdGhpcyBhcHByb2FjaCBzaG91bGQgYmUgaW5jb3Jwb3JhdGVkLiANCg0KV2hpbGUgdGhlc2UgbW9kZWxzIGFyZSBmYXIgZnJvbSBwZXJmZWN0IGluIHRoZWlyIHByZWRpY3RpdmUgY2FwYWJpbGl0aWVzLCB0aGV5IHByb3ZpZGUgYSBzdGFydGluZyBwb2ludCB0byBpZGVudGlmeSBib3RoIGluZGl2aWR1YWxzIHRoYXQgYXJlIG92ZXJ3ZWlnaHQgYW5kIG9yIG9iZXNlIHRoYXQgd291bGQgbm90IHRyYWRpdGlvbmFsbHkgc2VlayByb3V0aW5lIG1lZGljYWwgY2FyZSBkdWUgdG8gZmluYW5jaWFsIG9yIHNvY2lldGFsIGJhcnJpZXJzIHRocm91Z2ggdXRpbGl6YXRpb24gb2Ygc2ltcGxlIHdlYiBzdXJ2ZXlzLiAgQWRkaXRpb25hbGx5LCBhcyB3ZWlnaHQgd2FzIHJlbW92ZWQgZnJvbSB0aGVzZSBwcmVkaWN0aW9uIG1vZGVscywgdGhleSBjYW4gYWxzbyBiZSB1dGlsaXplZCB0byBpZGVudGlmeSBpbmRpdmlkdWFscyB0aGF0IG1heSBub3QgeWV0IGJlIG92ZXJ3ZWlnaHQgb3Igb2Jlc2UsIGJ1dCB0aGVpciBiZWhhdmlvciBhbmQgZGlldGFyeSBjaG9pY2VzIGFsaWduIHdpdGggdGhvc2UgdGhhdCBhcmUgb3ZlcndlaWdodCBvciBvYmVzZSwgYWN0aW5nIGVzc2VudGlhbGx5IGFzIGFuIG9iZXNpdHkgcHJlZGljdG9yLiAgQXMgd2VpZ2h0IGlzIGEgc2Vuc2l0aXZlIHN1YmplY3QgZm9yIG1vc3QgYWR1bHRzLCBpdCBpcyBpZGVhbCB0aGF0IHRoZXNlIHByZWRpY3Rpb24gbW9kZWxzIGRvIG5vdCBpbmNsdWRlIHBhcnRpY2lwYW50IHdlaWdodCByZW1vdmluZyB0aGUgcG90ZW50aWFsIGZvciBkZWNlcHRpdmUgYW5zd2VycyBhbmQgZW5jb3VyYWdpbmcgc3VydmV5IGNvbXBsZXRpb24uICBUaHJvdWdoIHRoZXNlIGlkZW50aWZpY2F0aW9uIGFuZCBvYmVzaXR5IHByZWRpY3Rpb24gbWV0aG9kcywgY29tbXVuaXRpZXMgY2FuIGJlIHRhcmdldGVkIGZvciBvdXRyZWFjaCBwcm9ncmFtcyB0byBpbmNyZWFzZSBhY2Nlc3MgdG8gY2FyZSBvciB0YXJnZXRlZCBtYXJrZXRpbmcgdG8gcHJvbW90ZSBoZWFsdGh5IGxpZmVzdHlsZXMgcHJvbW90aW9ucyAoaS5lLiBneW0gbWVtYmVyc2hpcCBkaXNjb3VudHMgZnJvbSBoZWx0aCBpbnN1cmFuY2UgcHJvdmlkZXJzKS4NCg0KIyBDaXRhdGlvbnMNCg0KSW5mb3JtYXRpb24NCg0KWzFdIGh0dHBzOi8vd3d3LmNkYy5nb3Yvb2Jlc2l0eS9hZHVsdC1vYmVzaXR5LWZhY3RzL2luZGV4Lmh0bWwNCg0KWzJdIGh0dHBzOi8vd3d3LmNkYy5nb3Yvb2Jlc2l0eS9jaGlsZGhvb2Qtb2Jlc2l0eS1mYWN0cy9jaGlsZGhvb2Qtb2Jlc2l0eS1mYWN0cy5odG1sDQoNClszXSBodHRwczovL3d3dy53aG8uaW50L25ld3Mtcm9vbS9mYWN0LXNoZWV0cy9kZXRhaWwvb2Jlc2l0eS1hbmQtb3ZlcndlaWdodA0KDQpbNF0gaHR0cHM6Ly91Y29ubnJ1ZGRjZW50ZXIub3JnL3Jlc2VhcmNoL3dlaWdodC1iaWFzLXN0aWdtYS8NCg0KDQo8Zm9udCBjbGFzcz0iaHJlZCI+RGF0YTwvZm9udD4NCg0KWzVdICJFc3RpbWF0aW9uIG9mIE9iZXNpdHkgTGV2ZWxzIEJhc2VkIE9uIEVhdGluZyBIYWJpdHMgYW5kIFBoeXNpY2FsIENvbmRpdGlvbiAuIiBVQ0kgTWFjaGluZSBMZWFybmluZyBSZXBvc2l0b3J5LCAyMDE5LCBodHRwczovL2RvaS5vcmcvMTAuMjQ0MzIvQzVIMzFaLiA8YnI+KG9idGFpbmVkIGZyb206IGh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9kYXRhc2V0LzU0NC9lc3RpbWF0aW9uK29mK29iZXNpdHkrbGV2ZWxzK2Jhc2VkK29uK2VhdGluZytoYWJpdHMrYW5kK3BoeXNpY2FsK2NvbmRpdGlvbikNCg0KaHR0cHM6Ly93d3cuc2NpZW5jZWRpcmVjdC5jb20vc2NpZW5jZS9hcnRpY2xlL3BpaS9TMjM1MjM0MDkxOTMwNjk4NT92aWElM0RpaHViDQo=