1 Introduction

Wine, one of the oldest alcoholic beverages known to man, has been theorized to exist as far back in human history as 6,000 BC. Hailing originally from modern day Georgia, extensive anthropological and archaeological research has linked wine to casual, societal, and religious purposes throughout the duration of human history. It is no surprise that the wines of today widely vary from the wines of ancient civilizations, but the core principals have remained the same: fermented grapes (about 600-800 per bottle!) [2].

Jumping to the world of today, wine has remained a staple in human culture and a stronghold in the United States economy. Clocking in at the number 1 consumer of wine globally according to the National Association of American Wineries, the United States’ wine industry reported staggering figures of $275 billion in 2022 alone bringing in over $30 billion in taxes annually [2]. As this industry has proven to be a stronghold of the American economy, there is a significant need felt by restaurateurs and wine purveyors to provide clear and accurate predictions on the quality of new wines.

As wine tasting and ranking is essentially subjective, the researchers at the University of Minho set out to compare expert sommelier rankings to measurable physical characteristics of both red and white wine to mitigate personal bias [1]. This data then allows for creation of prediction models to aid in prediction of a wine’s quality ranking without formal sommelier taste testing and subjectivity. This approach will allow burgeoning wine makers with more limited resources to provide a general quality ranking and assess the potential market value of a newly produced wine.

Additionally, with the increasing number of wine blends and varying inclusion levels of grape skins in the fermentation process, it has become more difficult to cleanly classify wines as white or red. Traditionally red wines are created by fermenting grapes with intact skins while white wines are fermented without skins, meanwhile commonly popular blends such as rose are created by utilizing a mixture of both skin on and skinless grapes [3]. Through chemical analysis of wines currently on the market, the data as collected and prepared by Cortez, Cerdeira, et. al. can be leveraged to create a red vs white classification for wines falling in between the traditional categories. This red vs white prediction can be utilized as a marketing tool by wine makers to provide consumers with a benchmark of wine taste and properties for blend wines, indicating if the flavor and chemical profile is closer to a true red or a true white wine.

1.1 Data Collection and Details

A total of 6497 wines were sampled (1599 red wines and 4898 white wines) for their chemical composition as well as submitted for sommelier evaluation. In order to mitigate potential taster bias, a minimum of three (3) sommeliers evaluated each wine on a score of 0 (awful) to 10 (very excellent) and the median evaluation score was utilized [1]. The chemical composition elements examined are included in the below Table 1 for reference. Additional details regarding the collection of this data can be found at this link.

var.details <- data.frame(
  Variable = c("Fixed Acidity", "Volatile Acidity", "Citric Acid", "Residual Sugar", "Chlorides", "Free Sulfur Dioxide", "Total Sulfur Dioxide", "Density", "pH", "Sulphates", "Alcohol", "Quality", "Wine Type"),
  Unit = c("g/dm^3", "g/dm^3", "g/dm^3", "g/dm^3", "g/dm^3", "g/dm^3", "g/dm^3", "g/dm^3", "pH", "g/dm^3", "% of volume", "", ""),
  Type = c("Continuous", "Continuous", "Continuous", "Continuous", "Continuous", "Continuous", "Continuous", "Continuous", "Continuous", "Continuous", "Continuous", "Continuous", "Binary"),
  Details = c("Concentration of tartaric acid", "Concentration of acetic acid", "Concentration of citric acid", "Concentration of residual sugar after fermentation", "Concentration of sodium chloride", "Concentration of free sulphur dioxide", "Total concentration of sulphur dioxide", "Density of wine", "pH of wine", "Concentration of sulphates", "Alcohol concentration", "Mean wine quality rating (0 to 10)", "Wine type (Red/White)" )
)

kable(var.details, caption = "Table 1:
      <br>Wine data variable units, types, and general description") %>% 
  kable_styling()
Table 1:
Wine data variable units, types, and general description
Variable Unit Type Details
Fixed Acidity g/dm^3 Continuous Concentration of tartaric acid
Volatile Acidity g/dm^3 Continuous Concentration of acetic acid
Citric Acid g/dm^3 Continuous Concentration of citric acid
Residual Sugar g/dm^3 Continuous Concentration of residual sugar after fermentation
Chlorides g/dm^3 Continuous Concentration of sodium chloride
Free Sulfur Dioxide g/dm^3 Continuous Concentration of free sulphur dioxide
Total Sulfur Dioxide g/dm^3 Continuous Total concentration of sulphur dioxide
Density g/dm^3 Continuous Density of wine
pH pH Continuous pH of wine
Sulphates g/dm^3 Continuous Concentration of sulphates
Alcohol % of volume Continuous Alcohol concentration
Quality Continuous Mean wine quality rating (0 to 10)
Wine Type Binary Wine type (Red/White)

2 Data Preparation

Initially the wine data was stored in separate white wine and red wine data sets. In order to prepare and utilize this data for analysis the white and red wine data sets were merged, with the creation of a categorical value to capture the type of wine (Red: Type = 0 | White: Type = 1). A cursory review of the merged wine data indicates there are no missing values, therefore imputation is not required for this prediction analysis (Table 2).

#read in data
redwine <- read.csv("https://nlepera.github.io/sta552/HW04/data/winequality-red.csv")
whitewine <- read.csv("https://nlepera.github.io/sta552/HW04/data/winequality-white.csv")

#set red = 1 / white = 0 pre-merge
redwine$typef <- as.factor("Red")
whitewine$typef <- as.factor("White")

#merge red and white wine
wine <- bind_rows(redwine, whitewine)

#summary table
kable(summary(wine), align = "l", caption = "Table 2:
      <br>Summary statistics of wine data.
      <br>Type: Red = 1 | White = 0") %>% 
  kable_styling() %>% 
  scroll_box(width = "100%")
Table 2:
Summary statistics of wine data.
Type: Red = 1 | White = 0
fixed.acidity volatile.acidity citric.acid residual.sugar chlorides free.sulfur.dioxide total.sulfur.dioxide density pH sulphates alcohol quality typef
Min. : 3.800 Min. :0.0800 Min. :0.0000 Min. : 0.600 Min. :0.00900 Min. : 1.00 Min. : 6.0 Min. :0.9871 Min. :2.720 Min. :0.2200 Min. : 8.00 Min. :3.000 Red :1599
1st Qu.: 6.400 1st Qu.:0.2300 1st Qu.:0.2500 1st Qu.: 1.800 1st Qu.:0.03800 1st Qu.: 17.00 1st Qu.: 77.0 1st Qu.:0.9923 1st Qu.:3.110 1st Qu.:0.4300 1st Qu.: 9.50 1st Qu.:5.000 White:4898
Median : 7.000 Median :0.2900 Median :0.3100 Median : 3.000 Median :0.04700 Median : 29.00 Median :118.0 Median :0.9949 Median :3.210 Median :0.5100 Median :10.30 Median :6.000 NA
Mean : 7.215 Mean :0.3397 Mean :0.3186 Mean : 5.443 Mean :0.05603 Mean : 30.53 Mean :115.7 Mean :0.9947 Mean :3.219 Mean :0.5313 Mean :10.49 Mean :5.818 NA
3rd Qu.: 7.700 3rd Qu.:0.4000 3rd Qu.:0.3900 3rd Qu.: 8.100 3rd Qu.:0.06500 3rd Qu.: 41.00 3rd Qu.:156.0 3rd Qu.:0.9970 3rd Qu.:3.320 3rd Qu.:0.6000 3rd Qu.:11.30 3rd Qu.:6.000 NA
Max. :15.900 Max. :1.5800 Max. :1.6600 Max. :65.800 Max. :0.61100 Max. :289.00 Max. :440.0 Max. :1.0390 Max. :4.010 Max. :2.0000 Max. :14.90 Max. :9.000 NA

2.1 Exploratory Data Analysis (EDA)

The merged wine data set was then examined to determine the potential for correlation between feature variables. Figure 1 below provides a visual demonstration of the strength of each correlation between variables, with the circle size and color corresponding to the correlation’s magnitude and direction (positive/negative). Calculated pearson correlation coefficient (r) values with an absolute value greater than 0.6 are considered to indicate moderate to strong correlations; a total of four (4) correlations between six (6) unique variables were identified as at least moderate, as captured below in Figure 2 and Table 3.

wine$type <- as.numeric(wine$typef)
wine$type[wine$type == 2] <- 0

#correlation check
#create correlation matrix
cor <- cor(wine[-13])

# plot correlation matrix
corrplot(cor, type ="upper", order = "hclust", tl.col = "black", tl.srt = 65, diag = T, mar = c(2,1,4,1))
title(main = "Variable Correlation", sub = "Figure 1:
Correlation analysis of wine data variables")

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

#filter for moderate to strong correlation, ignoring correlation = 1 (same variable match)
corr <- filter(cor2, abs(cor) >= 0.6) %>% filter(abs(cor) != 1) 
corr$cor <- round(corr$cor, 3)
corr <- corr[order(corr$cor), ]

cor.al.den <- ggplot(wine, aes(x = alcohol, y = density)) +
  geom_point(alpha = 0.5) +
  geom_smooth(method = "lm", color = "red", alpha = 0.75) +
  theme_bw()

cor.tso2.fso2 <- ggplot(wine, aes(x = total.sulfur.dioxide, y = free.sulfur.dioxide)) +
  geom_point(alpha = 0.5) +
  geom_smooth(method = "lm", color = "red", alpha = 0.75) +
  xlab("total sulfur dioxide (t.SO2)") +
  ylab("free sulfur dioxide (f.SO2)") +
  theme_bw()

cor.ty.vac <- ggplot(wine, aes(x = typef, y = volatile.acidity)) +
  geom_boxplot(fill = c("red4", "yellow3"), alpha = 0.5) +
  xlab("type") +
  ylab("volatile acidity") +
  theme_bw()

cor.ty.tso2 <- ggplot(wine, aes(x = typef, y = total.sulfur.dioxide)) +
  geom_boxplot(fill = c("red4", "yellow3"), alpha = 0.5) +
  xlab("type") +
  ylab("total sulfur dioxide (t.SO2)") +
  theme_bw()
grid.arrange(
  arrangeGrob(cor.al.den, cor.tso2.fso2, cor.ty.vac, cor.ty.tso2, ncol = 2, nrow = 2),
  top = textGrob("Pairwise Correlation Plots
                 ", gp = gpar(fontsize = 18, fontface = "bold")),
  bottom = textGrob("Figure 2
  Visualzations of wine data variable correlations where | r | > 0.6"))

#summary table - removed duplicate entries (swapped from v1 to v2)
kable(corr[c(1,3,5,7),], align = "l", row.names = F, col.names = c("Variable 1 (x)", "Variable 2 (y)", "Correlation"), caption = "Table 3:
      <br>Moderate to strong variable correlations identified in the merged wine data. Duplicate entries have been removed.") %>% 
  kable_styling()
Table 3:
Moderate to strong variable correlations identified in the merged wine data. Duplicate entries have been removed.
Variable 1 (x) Variable 2 (y) Correlation
type total.sulfur.dioxide -0.700
alcohol density -0.687
type volatile.acidity 0.653
total.sulfur.dioxide free.sulfur.dioxide 0.721

The correlated variables as outlined in Figure 2 andTable 3 above prove logical and do not require further investigation.

  • Alcohol is known to have a lighter density than water, therefore the negative correlation coefficient between alcohol and density is logical.
  • Volatile acidity (concentration of acetic acid) is known to have a higher allowable levels in red wines than white wines per the federal Tax and Trade Bureau [4]. As the wine type was coded as 1 = Red and 0 = white this observed negative correlation coefficient is logical.
  • Red wines are known to have lower total sulfur dioxide concentrations as the tannin present in grape skins assist in stabilization during fermentation, preventing SO2 byproduct formation [5]. As the wine type was coded as 1 = Red and 0 = white this observed positive correlation coefficient is logical.
  • As total sulfur dioxide measures both the free and reacted sulfur dioxide, the positive correlation coefficient between total and free sulfur dioxide concentrations is logical.

2.2 Feature Engineering

In order to provide additional dimensions to the regression and SVM analysis feature engineering was performed to create two additional variables: headache and add. The headache variable was created by taking the product of sulphates concentration and alcohol content as these are the two most common contributers to wine based hangovers. Secondly the add variable was created by summing the sulphates concentration, chlorides concentration, and the citric acid concentration to get a total additives concentration. Lastly, a variable was created to calculate the ratio of Free Sulphur Dioxide to Total Sulphur Dioxide to omit the correlation between these variables.

#combining alcohol and sulfates for headache potential
wine$headache <- wine$sulphates * wine$alcohol

#additives 
wine$add <- wine$sulphates + wine$chlorides + wine$citric.acid

#SO2 ratio
wine$so2.ratio <- wine$free.sulfur.dioxide / wine$total.sulfur.dioxide

kable(summary(wine[c(14:16)]), align = "c", col.names = c("Headache", "Additives", "Sulfur Dioxide Ratio"), caption = "Table 4:
      <br>Summary statistics of engineered feature variables.") %>% 
  kable_styling()
Table 4:
Summary statistics of engineered feature variables.
Headache Additives Sulfur Dioxide Ratio
Min. :0.0000 Min. : 2.625 Min. :0.3440
1st Qu.:0.0000 1st Qu.: 4.400 1st Qu.:0.7540
Median :0.0000 Median : 5.208 Median :0.8690
Mean :0.2461 Mean : 5.573 Mean :0.9059
3rd Qu.:0.0000 3rd Qu.: 6.348 3rd Qu.:1.0100
Max. :1.0000 Max. :19.404 Max. :3.6100
headache <- c("Headache", "", "Continuous", "Calculated variable for headache potential (product Sulphates & Alcohol")
add <- c("Total Additives", "", "Continuous", "Calculated variable for total wine addatives (sum Sulphates, Chlorides, & Citric Acid")
so2.ratio <- c("Sulpur Dioxide Ratio", "", "Continuous", "Calculated variable for ratio of Free Sulpur Dioxide : Total Sulphur Dioxide")

var.details <- rbind(var.details, headache) %>% rbind(add) %>% rbind(so2.ratio)

3 Regularized Linear Regression

In order to predict the median sommelier quality score of wine based off of the chemical properties of a sample, multiple regularized linear regression models were created with quality as the response variable. Before any prediction models were created, the data was split into 80:20 train:test groups to reduce the potential for over-fitting and to allow for improved hyperparameter tuning. Additionally, once the data was split into training and test groups, the feature variables were standardized to ensure a comparable scale between variables and remove potential over-weighting of variables.

#regression prep work
#set seed
set.seed(129)

#split feature and response variables
x.linear <- wine[-12]
y.linear <- wine$quality

#data split for CV (80:20)
split.linear <- createDataPartition(y.linear, p=0.8, list=F)

#training data
x.linear.train <- x.linear[split.linear,]
y.linear.train <- y.linear[split.linear]

#test data
x.linear.test <- x.linear[-split.linear,]
y.linear.test <- y.linear[-split.linear]

#standardize values
std.linear <- preProcess(x.linear.train, method = c("center", "scale"))
x.linear.train.std <- predict(std.linear, x.linear.train) 
x.linear.test.std <- predict(std.linear, x.linear.test) 

#drop factor type (typef) as numeric type already included (type)
x.linear.train.std <- x.linear.train.std[-12]
x.linear.test.std <- x.linear.test.std[-12]

3.1 Linear Regression Model Builds (LASSO, Ridge, & Elastic Net)

Regularized regression was employed to introduce constraints on the regression coefficients and subsequently reduce over-fitting risk. The loss function was employed through the glmnet package to ensure appropriate regularized regression with various approaches (LASSO, Ridge, & Elastic Net), utilizing the below equations to complete the model creation. The LASSO model automatically penalizes variables ultimately resulting in feature selection and a reduced model for use while the Ridge model penalizes variables by reducing impact without feature selection. Meanwhile the Elastic Net model combines these approaches and results in penalized variables with reduced impact and more limited feature selection.

lasso <- glmnet(x.linear.train.std, y.linear.train, alpha = 1)
ridge <- glmnet(x.linear.train.std, y.linear.train, alpha = 0)
enet <- glmnet(x.linear.train.std, y.linear.train, alpha = 0.5)

\[ \begin{align*} \underline{\mbox{LOSS Function Equations}} \\ \\ LOSS_{linear} = \ \sum_{i=1}^n (y_i - \hat y_i) \\ LOSS_{regularized} = \ LOSS_{linear} + \lambda \left[ \frac{1-\alpha}{2} \| \beta \|_{2}^2 + \alpha \| \beta \|_1\right] \ \ &\mbox{where} \ \ \left\{ \begin{array}{rl} \alpha = 1: &\mbox{LASSO} \\ \alpha = 0: &\mbox{Ridge} \\ 0 \leq a \leq 1: &\mbox{Elastic Net} \end{array}\right. \\ \end{align*} \]

\[ \underline{\mbox{Regularized Regression Equations}}\\ \begin{array}{l} \\ L1_{LASSO} &= \ LOSS_{reguarized} &+ \ \ \lambda \sum_{j=1}^k |a_j| \ \ \\ L2_{ridge} &= \ LOSS_{regularized} &+ \ \ \lambda \sum_{j=1}^k |a_j|^2 \ \ \\ L3_{Elastic} &= LOSS_{regulatised} &+ \ \ \lambda \sum_{j=1}^k |a_j| + \ \ \lambda \sum_{j=1}^k |a_j|^2 \ \end{array} \ \mbox{where} \ \ \ \left\{ \begin{array}{rl} \alpha = &\mbox{Regression Coeff.} \\ k = &\mbox{# of Feature Vars.}\\ \lambda = &\mbox{Hyperparameter} \end{array} \right. \]

Hyperparameter \(\lambda\) values were subsequently tuned through cross validation as part of the model selection process. More information on \(\lambda\) value selection is included below in section 3.2.1 Coefficient Path Analysis & Cross Validation

3.2 Linear Model Analysis & Selection

Regression models were created utilizing the predicted regression equation coefficients obtained from the regularized regression equations. These models were then cross validated and assessed for the best fit \(\lambda\) hyperparameter (Lagrange multiplier) before ultimately comparing the models’ efficacy and accuracy.

3.2.1 Coefficient Path Analysis & λ Selection Through Cross Validation

To determine the best fit number of feature variables for each regression model, 10-fold cross validation was though cv.glmnet() was employed for each model to calculate the range of best fit log(λ) values for use. The best fit value always lies between the log(λ) that provides minimum cross validated error and the log(λ) that returns error within 1 standard error of the minimum. Both values were plotted in each coefficient path analysis and cross validation RMSE vs log(λ) plot below in Figure 3. The log(λ) within 1 standard error of the minimum (lambda.1se) was chosen to improve model usability and reduce the overall number of feature variables included.

lasso.cv <- cv.glmnet(as.matrix(x.linear.train.std), y.linear.train, alpha = 1, main = "LASSO")
ridge.cv <- cv.glmnet(as.matrix(x.linear.train.std), y.linear.train, alpha = 0, main = "Ridge")
enet.cv <- cv.glmnet(as.matrix(x.linear.train.std), y.linear.train, alpha = 0.5, main = "Elastic Net")


par(mfrow = c(3,2), oma=c(6,0,3,0))
lasso.coef <- plot(lasso, xvar = "lambda", label = T, col = rainbow(15), xlab = "Log(λ)") +
  abline(v = log(lasso.cv$lambda.min), lty = 4) + 
  abline(v = log(lasso.cv$lambda.1se), lty = 4, col = "darkred")
lasso.lam <- plot(lasso.cv)

ridge.coef <- plot(ridge, xvar = "lambda", label = T, col = rainbow(15), xlab = "Log(λ)") + 
  abline(v = log(ridge.cv$lambda.min), lty = 4) + 
  abline(v = log(ridge.cv$lambda.1se), lty = 4, col = "darkred")
ridge.lam <- plot(ridge.cv)

enet.coef <- plot(enet, xvar = "lambda", label = T, col = rainbow(15), xlab = "Log(λ)") + 
  abline(v = log(enet.cv$lambda.min), lty = 4) + 
  abline(v = log(enet.cv$lambda.1se), lty = 4, col = "darkred")
enet.lam <- plot(enet.cv)

mtext("Coefficient Path Analysis & RSME λ Tuning", line = 1, side = 3, outer = T, cex = 1.5, font = 2)
mtext("LASSO", line = -1.5, side = 3, outer = T, font = 4)
mtext("Ridge", line = -28.5, side = 3, outer = T, font = 4)
mtext("Elastic Net", line = -55.5, side = 3, outer = T, font = 4)
mtext("Figure 3:
Coefficient path analysis & Root Mean Square Error (RMSE) analysis for λ tuning of each regularized linear regression model.
Black dotted lines represent the λ min & red dotted lines capture λ 1SE values.
Top horizonal axis indicates the number of feature variables present with associated log(λ)", line = 3, side = 1, outer = T, cex = 0.8, weight = 1)

These visualized cut points (log(λ)) from above Figure 3 are summarized below in Table 5, along with raw λ values. The λ value that retains all error within one standard error of the minimum value (se1_λ) was chosen for utilization in final model creation to allow for a more flexible model with any new potential data. This will support the wine classification model in retaining adaptivity to new incoming data. This will reduce the need for more frequent model rebuilds and re-tunes.

coef.lin <- data.frame(
  lin.gamma = c("Min_λ", "se1_λ", "Min_logλ", "se1_logλ"),
  lasso = c(lasso.cv$lambda.min, lasso.cv$lambda.1se, log(lasso.cv$lambda.min), log(lasso.cv$lambda.1se)),
  ridge = c(ridge.cv$lambda.min, ridge.cv$lambda.1se, log(ridge.cv$lambda.min), log(ridge.cv$lambda.1se)),
  enet = c(enet.cv$lambda.min, enet.cv$lambda.1se, log(enet.cv$lambda.min), log(enet.cv$lambda.1se))
  )

kable(coef.lin, col.names = c("λ Value Type", "LASSO", "Ridge", "Elastic Net"), caption = "Table 5:
      <br>Cross validated λ & log(λ) values for each regularized logistic regression model. Min_λ & se1_λ represent the λ value with minimal cross validated error & λ value with error within 1 standard error of the minimum respectively.  Min_logλ & se1_logλ are the log() of Min_λ & se1_λ respectively.", align = c("l", "c", "c", "c")) %>% 
  kable_styling()
Table 5:
Cross validated λ & log(λ) values for each regularized logistic regression model. Min_λ & se1_λ represent the λ value with minimal cross validated error & λ value with error within 1 standard error of the minimum respectively. Min_logλ & se1_logλ are the log() of Min_λ & se1_λ respectively.
λ Value Type LASSO Ridge Elastic Net
Min_λ 0.0017555 0.0387118 0.0026560
se1_λ 0.0344618 0.2267356 0.0756436
Min_logλ -6.3449836 -3.2516117 -5.9309377
se1_logλ -3.3679039 -1.4839706 -2.5817230

3.3 Model Selection

With the best fit log(λ) chosen as the lambda.1se values, the regression models were re-run on the test data with a fixed log(λ) value to assess the model performance. As illustrated below in Table 6 the elastic net model produced the lowest RMSE and the lowest RMSE % of the range of y test values for quality indicating this is the best fit model. While all models’ RMSE values were within 10-20% of the range of the y test values for quality, choosing the lowest RMSE and RMSE % of range of y test will result in the most accurate prediction models.

#model predict test data using model coefficients & lambda.1se from CV tuning & RMSE calculation
#lasso
lasso.model <- glmnet(x.linear.train.std, y.linear.train, alpha = 1, lambda = lasso.cv$lambda.1se)
lasso.pred <- round(predict(lasso.model, s = lasso.cv$lambda.1se, newx = as.matrix(x.linear.test.std)),0)
lasso.rmse <- rmse(y.linear.test, lasso.pred)
lasso.rmse.norm <- round((lasso.rmse/(max(y.linear.test)-min(y.linear.test)))*100,2)

#ridge
ridge.model <- glmnet(x.linear.train.std, y.linear.train, alpha = 0, lambda = ridge.cv$lambda.1se)
ridge.pred <- round(predict(ridge.model, s = ridge.cv$lambda.1se, newx = as.matrix(x.linear.test.std)),0)
ridge.rmse <- rmse(y.linear.test, ridge.pred)
ridge.rmse.norm <- round((ridge.rmse/(max(y.linear.test)-min(y.linear.test)))*100,2)

#elastic net
enet.model <- glmnet(x.linear.train.std, y.linear.train, alpha = 0.5, lambda = enet.cv$lambda.1se)
enet.pred <- round(predict(enet.model, s = enet.cv$lambda.1se, newx = as.matrix(x.linear.test.std)),0)
enet.rmse <- rmse(y.linear.test, enet.pred)
enet.rmse.norm <- round((enet.rmse/(max(y.linear.test)-min(y.linear.test)))*100,2)

#join RMSE vals for kable display
RMSE <- data.frame(
  Model = c("LASSO", "Ridge", "ElasticNet"),
  RMSE = c(lasso.rmse, ridge.rmse, enet.rmse),
  RMSE.norm = c(lasso.rmse.norm, ridge.rmse.norm, enet.rmse.norm),
  eval = c("Good", "Good", "Good") #manually calculated for this but can be sent through for loop to auto calc
)

kable(RMSE, align = c("l","c","c","c"), col.names = c("Model", "RMSE", "RMSE % of Range of Test Quality Values", "Intepretation"), caption = "Table 6:
      <br>Root Mean Square Error (RMSE) values, calculated percent of target range captured by RMSE, and the model evaluation for regularized linear regression models to utilize for model selection.") %>% 
  kable_styling()
Table 6:
Root Mean Square Error (RMSE) values, calculated percent of target range captured by RMSE, and the model evaluation for regularized linear regression models to utilize for model selection.
Model RMSE RMSE % of Range of Test Quality Values Intepretation
LASSO 0.8034977 16.07 Good
Ridge 0.8120813 16.24 Good
ElasticNet 0.8058913 16.12 Good

Once selected, the elastic net regression equation coefficients were obtained (Table 7 below) to create the final regression equation as presented below.

#extracting regression equation coefficients
enet.coef <- as.data.frame(as.matrix(coef(enet.cv, enet.cv$lambda.1se))) %>% subset(s1 != 0)

kable(enet.coef, align = "c", col.names = c("Variable", "Regression Coefficient"), caption = "Table 7:
      <br>Regression coefficients for selected regularized regression model: Elastic Net") %>% 
  kable_styling()
Table 7:
Regression coefficients for selected regularized regression model: Elastic Net
Variable Regression Coefficient
(Intercept) 5.8178496
volatile.acidity -0.1884719
residual.sugar 0.0276036
chlorides -0.0030770
alcohol 0.3157441
headache 0.0448596
so2.ratio 0.0629408

\[ \begin{align*} \hat{y}_{Elastic Net} = &5.818 - 0.200 * \mbox{Volatile Acidity} + 0.039 * \mbox{Residual Sugar} - \\ &0.003 * \mbox{Chlorides} + 0.336 * \mbox{Alcohol} + 0.046 * Headache + \\ &0.070 * \mbox{Sulphur Dioxide Ratio} \end{align*} \]

4 Regularized Logistic Regression

In order to predict the type (red/white) of wine based off of the chemical properties of a sample, multiple regularized logistic regression models were created with type as the response variable. Before any prediction models were created, the data was split into 80:20 train:test groups to reduce the potential for over-fitting and to allow for improved hyperparameter tuning. Additionally, once the data was split into training and test groups, the feature variables were standardized to ensure a comparable scale between variables and remove potential over-weighting of variables.

#regression prep work
#set seed
set.seed(12935)

#split feature and response variables
x.log <- wine[-c(13:14)]
y.log <- wine$type


#data split for CV (80:20)
split.log <- createDataPartition(y.linear, p=0.8, list=F)

#training data
x.log.train <- x.log[split.log,]
y.log.train <- y.log[split.log]

#test data
x.log.test <- x.log[-split.log,]
y.log.test <- y.log[-split.log]

#standardize values
std.log <- preProcess(x.log.train, method = c("center", "scale"))
x.log.train.std <- predict(std.log, x.log.train) 
x.log.test.std <- predict(std.log, x.log.test) 

4.1 Logistic Regression Model Builds

Regularized regression was employed to introduce constraints on the regression coefficients and subsequently reduce over-fitting risk. The loss function was employed through the glmnet package to ensure appropriate regularized regression with various approaches (LASSO, Ridge, & Elastic Net), utilizing the below equations to complete the model creation. The LASSO model automatically penalizes variables ultimately resulting in feature selection and a reduced model for use while the Ridge model penalizes variables by reducing impact without feature selection. Meanwhile the Elastic Net model combines these approaches and results in penalized variables with reduced impact and more limited feature selection.

lasso.log <- glmnet(x.log.train.std, y.log.train, alpha = 1, family = "binomial")
ridge.log <- glmnet(x.log.train.std, y.log.train, alpha = 0, family = "binomial")
enet.log <- glmnet(x.log.train.std, y.log.train, alpha = 0.5, family = "binomial")

\[ \begin{align*} \underline{\mbox{LOSS Function Equations}} \\ \\ LOSS_{logistic} = \ -\frac{1}{n} \sum_{i=1}^n \ [\ y_i\log(\hat{p}_i) + (1-y_{i})\log(1-\hat p_i)\ ] \\ LOSS_{regularized} = \ LOSS_{logistic} + \lambda \left[ \frac{1-\alpha}{2} \| \beta \|_{2}^2 + \alpha \| \beta \|_1\right] \ \ &\mbox{where} \ \ \left\{ \begin{array}{rl} \alpha = 1: &\mbox{LASSO} \\ \alpha = 0: &\mbox{Ridge} \\ 0 \leq a \leq 1: &\mbox{Elastic Net} \end{array}\right. \\ \end{align*} \]

\[ \underline{\mbox{Regularized Regression Equations}}\\ \begin{array}{l} \\ L1_{LASSO} &= \ LOSS_{reguarized} &+ \ \ \gamma \sum_{j=1}^k |\beta_j| \ \ \\ L2_{ridge} &= \ LOSS_{regularized} &+ \ \ \gamma \sum_{j=1}^k |\beta_j|^2 \ \ \\ L3_{Elastic} &= LOSS_{regulatised} &+ \ \ \gamma \sum_{j=1}^k |\beta_j| + \ \ \gamma \sum_{j=1}^k |\beta_j|^2 \ \end{array} \ \mbox{where} \ \ \ \left\{ \begin{array}{rl} \beta = &\mbox{Regression Coeff.} \\ k = &\mbox{# of Feature Vars.}\\ \gamma = &\mbox{Hyperparameter} \end{array} \right. \]

Hyperparameter \(\gamma\) values were subsequently tuned through cross validation as part of the model selection process. More information on \(\gamma\) value selection is included below in section 4.2.1 Coefficient Path Analysis & Cross Validation

4.2 Logistic Model Analysis & Parameter Value Selection

Regression models were created utilizing the predicted regression equation coefficients obtained from the regularized regression equations. These models were then cross validated and assessed for the best fit \(\gamma\) hyperparameter (Lagrange multiplier) before ultimately comparing the models’ efficacy and accuracy.

4.2.1 Coefficient Path Analysis & γ Selection Through Cross Validation

To determine the best fit number of feature variables for each regression model, 10-fold cross validation was though cv.glmnet() was employed for each model to calculate the range of best fit log(\(\gamma\)) values for use. The best fit value always lies between the log(\(\gamma\)) that provides minimum cross validated error and the log(\(\gamma\)) that returns error within 1 standard error of the minimum. Both values were plotted in each coefficient path analysis and cross validation RMSE vs log(\(\gamma\)) plot below in Figure 4. The log(\(\gamma\)) within 1 standard error of the minimum (gamma.1se) was chosen to improve model usability and reduce the overall number of feature variables included.

lasso.log.cv <- cv.glmnet(as.matrix(x.log.train.std), y.log.train, alpha = 1, family = "binomial", main = "LASSO")
ridge.log.cv <- cv.glmnet(as.matrix(x.log.train.std), y.log.train, alpha = 0, family = "binomial", main = "Ridge")
enet.log.cv <- cv.glmnet(as.matrix(x.log.train.std), y.log.train, alpha = 0.5, family = "binomial", main = "Elastic Net")
par(mfrow = c(3,2), oma=c(6,0,3,0))
lasso.log.coef <- plot(lasso.log, xvar = "lambda", label = T, col = rainbow(15), xlab = "Log(γ)") +
  abline(v = log(lasso.log.cv$lambda.min), lty = 4) + 
  abline(v = log(lasso.log.cv$lambda.1se), lty = 4, col = "darkred")
lasso.log.lam <- plot(lasso.log.cv, xlab = "Log(γ)")

ridge.log.coef <- plot(ridge.log, xvar = "lambda", label = T, col = rainbow(15), xlab = "Log(γ)") + 
  abline(v = log(ridge.log.cv$lambda.min), lty = 4) + 
  abline(v = log(ridge.log.cv$lambda.1se), lty = 4, col = "darkred")
ridge.log.lam <- plot(ridge.log.cv, xlab = "Log(γ)")

enet.log.coef <- plot(enet.log, xvar = "lambda", label = T, col = rainbow(15), xlab = "Log(γ)") + 
  abline(v = log(enet.log.cv$lambda.min), lty = 4) + 
  abline(v = log(enet.log.cv$lambda.1se), lty = 4, col = "darkred")
enet.log.lam <- plot(enet.log.cv, xlab = "Log(γ)")

mtext("Coefficient Path Analysis & Model Fit γ Tuning", line = 1, side = 3, outer = T, cex = 1.5, font = 2)
mtext("LASSO", line = -1.5, side = 3, outer = T, font = 4)
mtext("Ridge", line = -28.5, side = 3, outer = T, font = 4)
mtext("Elastic Net", line = -55.5, side = 3, outer = T, font = 4)
mtext("Figure 4:
Coefficient path analysis & binomial deviance analysis for γ tuning of each regularized logarithmic regression model.
Black dotted lines represent the γ min & red dotted lines capture γ 1SE values.
Top horizonal axis indicates the number of feature variables present with associated log(γ)", line = 3, side = 1, outer = T, cex = 0.8, weight = 1)

These visualized cut points (log(γ)) from above Figure 4 are summarized below in Table 8, along with raw γ values. The γ value that retains all error within one standard error of the minimum value (se1_γ) was chosen for utilization in final model creation to allow for a more flexible model with any new potential data. This will support the wine classification model in retaining adaptivity to new incoming data. This will reduce the need for more frequent model rebuilds and re-tunes.

coef.log <- data.frame(
  log.gamma = c("Min_γ", "se1_γ", "Min_logγ", "se1_logγ"),
  lasso = c(lasso.log.cv$lambda.min, lasso.log.cv$lambda.1se, log(lasso.log.cv$lambda.min), log(lasso.log.cv$lambda.1se)),
  ridge = c(ridge.log.cv$lambda.min, ridge.log.cv$lambda.1se, log(ridge.log.cv$lambda.min), log(ridge.log.cv$lambda.1se)),
  enet = c(enet.log.cv$lambda.min, enet.log.cv$lambda.1se, log(enet.log.cv$lambda.min), log(enet.log.cv$lambda.1se))
  )

kable(coef.log, col.names = c("γ Value Type", "LASSO", "Ridge", "Elastic Net"),align = c("l", "c", "c", "c"), caption = "Table 8:
      <br>Cross validated γ & log(γ) values for each regularized logistic regression model. Min_γ & se1_γ represent the γ value with minimal cross validated error & γ value with error within 1 standard error of the minimum respectively.  Min_logγ & se1_logγ are the log() of Min_γ & se1_γ respectively.") %>% 
  kable_styling()
Table 8:
Cross validated γ & log(γ) values for each regularized logistic regression model. Min_γ & se1_γ represent the γ value with minimal cross validated error & γ value with error within 1 standard error of the minimum respectively. Min_logγ & se1_logγ are the log() of Min_γ & se1_γ respectively.
γ Value Type LASSO Ridge Elastic Net
Min_γ 0.0000837 0.0300662 0.0000601
se1_γ 0.0019782 0.0329976 0.0017126
Min_logγ -9.3887375 -3.5043534 -9.7189615
se1_logγ -6.2255903 -3.4113197 -6.3697468

4.2.2 Cut-off Probability Value Selection

To properly fine tune the regularized logistic regression prediction model, the optimal probability cutoff must be selected in addition to the Lagrange multiplier (γ). This value serves as the cut off point for when the prediction model will predict a wine type of red. In order to get the most accurate predictions, this cutoff value must be carefully selected. All three models were held at a standard probability of \(\alpha = 0.5\) and predicted probabilities were collected. Then these predicted probabilities were classified as white or red repeatedly based on 100 different cut off probabilities from 0 to 1 (increments of 0.01). The accuracy rates (rate at which the cut off point & prediction model successfully classified a red wine as a red and a white wine as a white) for each model were calculated at each of these 100 points.

The below Figure 5 provides an interactive insight into these 100 probability cut off value and accuracy rate pairings:

#model predict test data using model coefficients & lambda.1se from CV tuning & RMSE calculation
#lasso
lasso.log.model <- glmnet(x.log.train.std, y.log.train, alpha = 1, family = "binomial", lambda = lasso.log.cv$lambda.1se)
lasso.log.pred <- predict(lasso.log.model, s = lasso.log.cv$lambda.1se, newx = as.matrix(x.log.test.std), type = "response")


#ridge
ridge.log.model <- glmnet(x.log.train.std, y.log.train, alpha = 0, family = "binomial", lambda = ridge.log.cv$lambda.1se)
ridge.log.pred <- predict(ridge.log.model, s = ridge.log.cv$lambda.1se, newx = as.matrix(x.log.test.std), type = "response")


#elastic net
enet.log.model <- glmnet(x.log.train.std, y.log.train, alpha = 0.5, family = "binomial", lambda = enet.log.cv$lambda.1se)
enet.log.pred <- predict(enet.log.model, s = enet.log.cv$lambda.1se, newx = as.matrix(x.log.test.std), type = "response")
##### running through with probability cutoff of 0.5 (α = 0.5) to test accuracy to then to select better α value
#set sequence
seq.ac <- seq(0,1, length = 100)
ac.lasso <- NULL
ac.ridge <- NULL
ac.enet <- NULL

#for loop to run through all threshold levels and transform probs into outcomes (Red/White) based on threshold
for (i in 1:length(seq.ac)){
  y.p.lasso <- ifelse(lasso.log.pred > seq.ac[i], 1 , 0)
  y.p.ridge <- ifelse(ridge.log.pred > seq.ac[i], 1 , 0)
  y.p.enet <- ifelse(lasso.log.pred > seq.ac[i], 1 , 0)
  ac.lasso[i] <- mean(y.log.test == y.p.lasso)
  ac.ridge[i] <- mean(y.log.test == y.p.ridge)
  ac.enet[i] <- mean(y.log.test == y.p.enet)
}

#create max accuracy cutoffs, mean included in the event more than one cutoff with max accuracy
cut.lasso <- mean(seq.ac[which(ac.lasso==max(ac.lasso))])
cut.ridge <- mean(seq.ac[which(ac.ridge==max(ac.ridge))])
cut.enet <- mean(seq.ac[which(ac.enet==max(ac.enet))])

#merge for plotting
ac.log.all <- data.frame(
  prob = rep(seq.ac,3),
  accuracy = c(ac.lasso, ac.ridge, ac.enet),
  group = c(rep("lasso", 100), rep("ridge", 100), rep("elastic", 100))
)
#ac.log.el <- ac.log.all %>% subset(group == "elastic")
  
ac.log.plot <- ggplot(ac.log.all, aes(x = prob, y = accuracy, color = group, lty = group)) +
  geom_line() +
  labs(title = "Model Prediction Accuracy vs. Cut-off Probability", x= "Cut-off Probability", y="Accuracy", color = "Model", lty = "")

ggplotly(ac.log.plot)

Figure 5: An interactive visualization of the three logistic models’ prediction accuracy when utilizing cut-off probabilities from 0 to 1 (increments of 0.01). Hover over any point in the graph for accuracy and α values.

These 100 pairings for each model were then analyzed to identify the associated probability cut off that resulted in the maximum accuracy. The below Table 9 summarizes these maximum accuracy & associated probability cut off pairings for the LASSO, Ridge, and Elastic Net regularized logistic regression prediction models. The below outlined cut-off probabilities will be utilized for all subsequent analysis of the LASSO, Ridge, and Elastic Net logistic regression models in this report.

cut.max <- data.frame(
  val = c("Cut_Off_Prob", "Max_Accuracy"),
  lasso = c(cut.lasso, max(ac.lasso)),
  ridge = c(cut.ridge, max(ac.ridge)),
  enet = c(cut.enet, max(ac.enet))
)

kable(cut.max , col.names = c("", "LASSO", "Ridge", "Elastic Net"), align = c("l", "c","c","c"), digits = 3, caption ="Table 9:
      <br>Cut-off probabilities to use with logistic regression model to maximize prediction accuracy of wine type.") %>% 
  kable_styling()
Table 9:
Cut-off probabilities to use with logistic regression model to maximize prediction accuracy of wine type.
LASSO Ridge Elastic Net
Cut_Off_Prob 0.551 0.401 0.551
Max_Accuracy 0.997 0.995 0.997

4.3 Model Selection

Utilizing the calculated best cut-off probability values the Receiver Operating Characteristics (ROC) curve was plotted along with the predicted best fit threshold points as calculated above in Table 9. Due to the high level of model tuning with calculating the best fit γ and the best fit cut-off probability, the models appear to be over-fitted despite incorporating the LOSS functions. As demonstrated below in Figure 6, the ROC curves are near perfect right angles, with Area Under the Curve (AUC) values incredibly close to 1. This indicates the model is likely over fitted and there is a potential for this model to not adapt well to future data for accurate predictions.

#ROC creation roc(response, predictor, calculated best probability cutoffs)
ROC.lasso <- roc(y.log.test, lasso.log.pred)
ROC.ridge <- roc(y.log.test, ridge.log.pred)
ROC.enet <- roc(y.log.test, enet.log.pred)

#pull sensitivites and 1-specificity for easy ref
lasso.sn <- ROC.lasso$sensitivities
lasso.sp <- 1-ROC.lasso$specificities
ridge.sn <- ROC.ridge$sensitivities
ridge.sp <- 1-ROC.ridge$specificities
enet.sn <- ROC.enet$sensitivities
enet.sp <- 1-ROC.enet$specificities

#get ROC plot coordinates for best fit cut off probability values prev calculated (threshold)
lasso.cp <- coords(ROC.lasso, x = cut.lasso, input = "threshold", ret = c("threshold", "sensitivity", "specificity"))
ridge.cp <- coords(ROC.ridge, x = cut.ridge, input = "threshold", ret = c("threshold", "sensitivity", "specificity"))
enet.cp <- coords(ROC.enet, x = cut.enet, input = "threshold", ret = c("threshold", "sensitivity", "specificity"))

#plot ROC curves and threshold values
par(oma = c(6,2,2,2),  cex.main = 2)
plot(lasso.sp, lasso.sn, type = "l", xlim=c(0,1), xlab="1 - Specificity (False Positive)" , ylim=c(0,1), ylab = "Sensitivity (True Positive)", main = "ROC Curve Comparrisons", col = "darkviolet", lwd=2) +
  abline(0,1, lty=2, col = "darkred", lwd=1) +
  lines(ridge.sp, ridge.sn, col = "blue3", lty = 2, lwd =2) +
  lines(enet.sp, enet.sn, col = "coral3", lty = 3, lwd=2) +
  points((1-lasso.cp[1,3]), lasso.cp[1,2], pch = 23, col = "darkviolet", cex =2) +
  points((1-ridge.cp[1,3]), ridge.cp[1,2], pch = 21, col = "blue3", cex = 2) +
  points((1-enet.cp[1,3]), enet.cp[1,2], pch = 22, col = "coral3", cex = 2) 
  legend("bottomright", c(paste("LASSO  AUC =", round(ROC.lasso$auc,4)),
                          paste("Ridge   AUC =", round(ROC.ridge$auc, 4)),
                          paste("E.Net    AUC =", round(ROC.enet$auc, 4))),
         col = c("darkviolet", "blue3", "coral3"),
         lwd = 2, lty=1, cex = 0.8, bty="o")
  title(sub = "Figure 6:
  Comparrison of ROC and AUC values for each logistic regression prediction model
  Calculated best fit thresholds (γ) indicated on the plot.", outer = T, line=2)

While the models appear to be over fitted, based on the analysis outputs as captured below in Table 10 the LASSO model proves to be the best fit regularized logistic regression prediction model for accurately predicting a wine’s type (Red/White) based on chemical composition. This model was selected as while all models returned an AUC value of near 1, the LASSO model returned the lowest false positive rate (1 - Sensitivity). In order to reduce the likely overfitting, the cut-off probability and/or the Lagrange multiplier.

#table of ROC curve stats for each model
cutoffs <- data.frame(
  Model = c("LASSO", "Ridge", "E.Net"),
  CutOff = c(cut.lasso, cut.ridge, cut.enet),
  Sens = c(lasso.cp[1,2], ridge.cp[1,2], enet.cp[1,2]),
  Spec = c((1-lasso.cp[1,3]), (1-ridge.cp[1,3]), (1-enet.cp[1,3])),
  AUC = c(ROC.lasso$auc, ROC.ridge$auc, ROC.enet$auc)
) 

kable(cutoffs, digits = 5, align = c("l","c","c","c","c"), col.names = c("Model", "Cut Off Probability", "Sensitivity (True Postivie)", "1-Specificity (False Positive)", "Model AUC"), caption = "Table 10:
      <br>Final regularized regression model analysis outputs.") %>% 
  kable_styling()
Table 10:
Final regularized regression model analysis outputs.
Model Cut Off Probability Sensitivity (True Postivie) 1-Specificity (False Positive) Model AUC
LASSO 0.55051 1.00000 0.00405 0.99986
Ridge 0.40115 0.99678 0.00507 0.99960
E.Net 0.55051 0.99357 0.00507 0.99984
pred.lasso.gamma <- ifelse(lasso.log.pred > cut.lasso, 1, 0)
pred.lasso.gamma <- as.factor(pred.lasso.gamma)
y.log.tst.f <- as.factor(y.log.test)

lasso.conf <- confusionMatrix(pred.lasso.gamma, y.log.tst.f)

log.ac <- (lasso.conf$table[1,1] + lasso.conf$table[2,2])/sum(lasso.conf$table)
log.prec <- (lasso.conf$table[1,1])/(lasso.conf$table[1,1]+lasso.conf$table[1,2])
log.rec <- (lasso.conf$table[1,1])/(lasso.conf$table[1,1]+lasso.conf$table[2,1])

log.eval <- data.frame(
  Accuracy = log.ac,
  Precision = log.prec,
  Recall = log.rec
)

kable(log.eval, align = "c", caption = "Table 11:
      <br>Evaluation of of regularized logistic regression LASSO model (red vs white)") %>% 
  kable_styling()
Table 11:
Evaluation of of regularized logistic regression LASSO model (red vs white)
Accuracy Precision Recall
0.9969183 1 0.9959473

Once selected, the LASSO regression equation coefficients were obtained (Table 12 below) to create the final regularized logistic regression equation.

#extracting regression equation coefficients
lasso.log.coef <- as.data.frame(as.matrix(coef(enet.log.cv, enet.log.cv$lambda.1se))) %>% subset(s1 != 0)

kable(lasso.log.coef, align = "c", col.names = c("Variable", "Regression Coefficient"), caption = "Table 12:
      <br>Regression coefficients for selected regularized regression model: Elastic Net") %>% 
  kable_styling()
Table 12:
Regression coefficients for selected regularized regression model: Elastic Net
Variable Regression Coefficient
(Intercept) -3.1048553
fixed.acidity 0.5379201
volatile.acidity 1.2574066
citric.acid -0.1231985
residual.sugar -1.6070025
chlorides 0.7114934
free.sulfur.dioxide -0.4647124
total.sulfur.dioxide -1.4656181
density 1.9210576
pH 0.5269191
sulphates 0.3018866
alcohol 0.1959809
quality 0.0011875
headache 0.4761542
so2.ratio 0.8529268

5 Support Vector Machine (SVM) Analysis

In order to create more future proof prediction models, support vector machine (SVM) classification and regression models were created in addition to the regularized logistic and linear regression models previously explored.

Rather than relying on a traditional regression model to predict a binary outcome (ex: is a wine red or white?), SVM classification sets out to identify a hyperplane that maximizes the margins between the two classes (red/white). The SVM C-classification model was run on an 80:20 train:test split. To note the 80:20 split data did not undergo standardization, as standardization is not required for SVM based classification. Cross validation was tuned to utilize 5-fold validation with 0 repetitions. Repeated cross-validation can be employed should the available database grow, but with the current size of the data set available, introducing repeated cross validation will likely lead to overfitting.

5.1 SVM Classification vs Regularized Logistic Regression

As the SVM C-classification model was employed with the RBF kernel (radial), both the cost (C) and curvature (\(\gamma\)) hyperparameters required tuning before proper use. A grid search was employed to identify the best fit hyperparameter values utilizing the tune() package. These best fit values for C and \(\gamma\) were then fed back into the SVM C-classification as fixed C and \(\gamma\) values to create the final SVM C-classification model. Predictions were then generated (Table 13) and utilized to calculate the accuracy, precision, and recall (sensitivity) of the model, as captured below in Table 14.

#split data
wine.svm <- wine[-14]
set.seed(1236895)
svm.split <- sample(1:nrow(wine.svm), 0.8*nrow(wine.svm), replace = F)
train.svm <- wine.svm[svm.split,]
test.svm <- wine.svm[-svm.split,]
#set up CV control
cv.cont.class <- tune.control(cross = 5, nrepeat = 1)

hp.class <- tune(svm, typef ~., data = train.svm, 
                 kernel = "radial", 
                 ranges = list(cost = 10^(-1:2), gamma = c(0.1, 0.5, 1, 2)),
                 tunecontrol = cv.cont.class)
class.model <- hp.class$best.model
class.cost <- class.model$cost
class.gamma <- class.model$gamma

class.train <- svm(typef ~ ., train.svm, kernel = "radial", cost = class.cost, gamma = class.gamma)

class.pred <- predict(class.train, test.svm, type = "class")

class.conf <- table(Predicted = class.pred, Actual = test.svm$type)

kable(class.conf, col.names = c("Predicted Type", "Actual Red", "Actual White"), align = c("c", "c", "c"), caption="Table 13:
      <br>Confusion matrix for SVM classification of wine (red/white)") %>% 
  kable_styling()
Table 13:
Confusion matrix for SVM classification of wine (red/white)
Predicted Type Actual Red Actual White
Red 319 2
White 3 976
class.ac <- (class.conf[1,1] + class.conf[2,2])/sum(class.conf)
class.prec <- (class.conf[1,1])/(class.conf[1,1]+class.conf[1,2])
class.rec <- (class.conf[1,1])/(class.conf[1,1]+class.conf[2,1])

classlog.eval <- data.frame(
  Model = c("LASSO", "SVM C-classification"),
  Accuracy = c(log.ac ,class.ac),
  Precision = c(log.prec, class.prec),
  Recall = c(log.rec, class.rec)
)

kable(classlog.eval, align = "c", caption = "Table 14:
      <br>Evaluation of of SVM C-classifciation model (red vs white) compared to the regularized linear regression LASSO model") %>% 
  kable_styling()
Table 14:
Evaluation of of SVM C-classifciation model (red vs white) compared to the regularized linear regression LASSO model
Model Accuracy Precision Recall
LASSO 0.9969183 1.0000000 0.9959473
SVM C-classification 0.9961538 0.9937695 0.9906832

While the above Table 14 demonstrates the LASSO model had slightly better values for all three evaluation parameters, the SVM C-classification prediction model is likely a better fit for predicting wine type (red/white) based on chemical composition due to the nature of the model creation process. As the SVM C-classification model can be rapidly tuned on the current data, this prediction model will allow for increased longevity in prediction capabilities. The SVM model will require fewer re-visits to ensure the model remains the best fit to the current data parameters, with slightly less potential over fitting. As wines change and evolve, this SVM C-classification prediction model will evolve in the same way.

#transform to numerif from factor and fix white = 0 bc as.factor puts white = 2
test.svm$typef <- as.numeric(test.svm$typef)
test.svm$typef[test.svm$typef == 2] <- 0

class.pred1 <- as.numeric(class.pred)
class.pred1[class.pred == 2] <- 0

#transform from list to data frame and ensure column name is match
class.pred1 <- as.data.frame(class.pred1) %>% rename(typef = class.pred1)
#create ROC 
roc.svm <- roc(test.svm$typef, class.pred1$typef)

#pull sens-spes
svm.sn <- roc.svm$sensitivities
svm.sp <- 1-roc.svm$specificities

#optimal cut point
svm.cp <- coords(roc.svm, "best", ret = c("threshold", "sensitivity", "specificity"))

#plot ROC curves and threshold values
par(oma = c(6,2,2,2),  cex.main = 2)
plot(svm.sp, svm.sn, type = "l", xlim=c(0,1), xlab="1 - Specificity (False Positive)" , ylim=c(0,1), ylab = "Sensitivity (True Positive)", main = "ROC Curve Comparrisons", col = "darkviolet", lwd=2) +
  lines(lasso.sp, lasso.sn, col = "blue3", lwd = 2, lty = 3) +
  points((1-svm.cp[1,3]), svm.cp[1,2], pch = 23, col = "darkviolet", cex =2) +
  points((1-ridge.cp[1,3]), ridge.cp[1,2], pch = 21, col = "blue3", cex = 2) 
  legend("bottomright", c(paste("SVM       AUC =", round(roc.svm$auc,4)),
                          paste("LASSO   AUC =", round(ROC.lasso$auc, 4))),
         col = c("darkviolet", "blue3"),
         lwd = 2, lty=1, cex = 0.8, bty="o")
  title(sub = "Figure 7:
  Comparrison of ROC and AUC values for the SVM C-classification 
  prediction model and the regularized logistic regression prediction model
  Calculated best fit thresholds (γ) indicated on the plot.", outer = T, line=2)

Similar to the LASSO model, the SVM C-classification prediction model appears to demonstrate over-fitting based on the near perfect square shape of the ROC curve in Figure 7 and AUC value incredibly close to 1. In contrast to the LASSO model this appearance of over-fitting is less concerning for model longevity in the SVM C-classification model due to the model building process. As the SVM C-class model will continuously re-tune the selection hyperarameters and identify new hyperplanes to accurately classify wine types based on newly available data, the regression coefficients will require manual re-tuning after the introduction of enough new data. Therefore the SVM C-classification model will prove to be a more cost effective prediction model when seeking to determine wine type (red/white) based on wine chemical composition.

5.2 SVM Regression (SVR) vs Regularized Linear Regression

In order to allow for increased future data flexibility in the prediction model, an SVM regression model was created to allow for sommelier quality prediction while minimizing model complexity and error tolerance. This increased error tolerance as captured by \(\epsilon\) allows for improved prediction output after introduction of new wine data without requiring re-tuning of the regression coefficients resulting in improved longevity and decreased cost.

As the SVR model was employed with the RBF kernel (radial), the cost (C), curvature (\(\gamma\)), and the control error (\(\epsilon\)) hyperparameters required tuning before proper use. A grid search was employed to identify the best fit hyperparameter values utilizing the tune() package. These best fit values for C, \(\gamma\). and \(\epsilon\) were then fed back into the SVR model as fixed C, \(\gamma\), and \(\epsilon\) values to create the final SVR model. Predictions were then generated (Table 15) and utilized to calculate the mean square error (MSE) and mean absolute error (MAE) for comparison against the regularized linear regression prediction model, as captured below in Table 15.

wine.svr <- wine[-13]
svr.x <- wine.svr[, -12]
svr.y <- wine.svr[,12]

set.seed(123856)
train.svr <- sample(1:nrow(wine.svr), 0.8*nrow(wine.svr))
x.train <- svr.x[train.svr, ]
x.test <- svr.x[-train.svr, ]
y.train <- svr.y[train.svr]
y.test <- svr.y[-train.svr]

tune.svr <- tune.control(cross = 5, nrepeat = 1)

hp.svr <- tune(svm, x.train, y.train,
               ranges = list(epsilon = seq(0.1, 0.5, 1.0),
                             cost = c(1, 10, 100),
                             gamma = c(0.01, 0.1, 1)),
               tunecontrol = tune.svr)
final.svr <- svm(x.train, y.train,
                 type = "eps-regression",
                 kernel = "radial",
                 epsilon = hp.svr$best.parameters$epsilon,
                 cost = hp.svr$best.parameters$cost,
                 gamma = hp.svr$best.parameters$gamma)

svr.pred <- round(predict(final.svr, x.test),0)
svr.conf <- table(Predicted = svr.pred, Actual = y.test)

mse.svr <- mean((y.test - svr.pred)**2)
mae.svr <- mean(abs(y.test - svr.pred))
rsqr.svr <- (cor(y.test, svr.pred))**2

mse.enet <- mean((y.linear.test - enet.pred)**2)
mae.enet <- mean(abs(y.linear.test - enet.pred))
rsqr.enet <- (cor(y.linear.test, enet.pred))**2

mse.mae <- data.frame(
  Statistic = c("MSE", "MAE", "R.Squared"),
  SVR = c(mse.svr, mae.svr, rsqr.svr),
  E.Net = c(mse.enet, mae.enet, rsqr.enet)
)

kable(svr.conf, col.names = c("Predicted Quality Rank", "Actual 3", "Actual 4", "Actual 5", "Actual 6", "Actual 7", "Actual 8", "Actual 9"), align = "c", caption="Table 15:
      <br>Confusion matrix for SVR prediction of median sommelier quality ranking (0 to 10 whole numbers only)") %>% 
  kable_styling()
Table 15:
Confusion matrix for SVR prediction of median sommelier quality ranking (0 to 10 whole numbers only)
Predicted Quality Rank Actual 3 Actual 4 Actual 5 Actual 6 Actual 7 Actual 8 Actual 9
4 0 4 1 0 0 0 0
5 1 12 274 67 6 1 0
6 5 30 143 456 101 17 0
7 1 0 4 29 127 10 1
8 0 0 0 1 1 8 0
kable(mse.mae, align = c("l", "c" , "c"), caption = "Table 16:
      <br>Comparrison of Mean Square Error (MSE) and Mean Absolute Error (MAE) values from SVR prediction model and regularized linear regression prediction model.", col.names = c("Statistic", "SVM Regression", "Elasic Net Linear Regression")) %>% 
  kable_styling()
Table 16:
Comparrison of Mean Square Error (MSE) and Mean Absolute Error (MAE) values from SVR prediction model and regularized linear regression prediction model.
Statistic SVM Regression Elasic Net Linear Regression
MSE 0.5184615 0.6494607
MAE 0.3892308 0.5246533
R.Squared 0.3609760 0.1768766

As demonstrated in Table 15 by the lower MSE and MAE values and higher R\(^2\) value, the SVR prediction model for mean sommelier quality rating more successfully produces predictions than the regularized linear regression prediction model.

In addition to providing more accurate predictions, as the SVR model can be rapidly tuned on the current data, this prediction model will allow for increased longevity in prediction capabilities. The SVR model will require fewer re-visits to ensure the model remains the best fit to the current data parameters, with slightly less potential over fitting. As wines change and evolve, this SVR prediction model will evolve in the same way.

6 Conclusions

Overall the prediction models created by the support vector machine algorithms resulted in improved prediction capabilities for both variables of interest (median sommelier quality ranking and wine type [red / white]). For median sommelier quality ranking the SVR model produced predictions with an R\(^2\) nearly double that seen in the regularized linear regression prediction model, indicating that the increased flexibility of this model aids in the accurate prediction of wine quality. Additionally while the prediction of white vs red models all appear to be over-fitted, the SVR C-classification model demonstrated less overfitting with incredibly high accuracy and less data preparation prior to model utilization.

Due to the increased flexibility to new data added to the model over time and demonstrated better predictive outputs both the SVM C-classification and SVR models should be implemented to allow for wine quality and wine type predictions based on chemical composition alone. Through utilizing these predictive models, wine makers can better predict how well various batches will be rated on their quality by sampling a small quantity of wine prior to bottling and aging. This will allow for more dynamic pricing and prevent wine makers from investing in long term storage and aging of bad batches. Additionally the SVM C-classification model will allow wine makers to better classify their wine blends to either white or red based on a chemical composition sample. This will be useful as blends utilize less and less grape skin in the fermentation process and produce clearer wines that may still meet the composition qualifications of a “Red”.

7 Citations

Data
[1] P. Cortez, A. Cerdeira, F. Almeida, T. Matos and J. Reis. Modeling wine preferences by data mining from physicochemical properties. In Decision Support Systems, Elsevier, 47(4):547-553. ISSN: 0167-9236. (obtained from: https://archive.ics.uci.edu/dataset/186/wine+quality)

Wine Facts
[2] Dunham, J. (2025). Wine Facts. The National Association of American Wineries. https://wineamerica.org/the-magic-of-wine/wine-facts/

[3] Sussman, Z. (2022). What’s the Difference Between Red and White Wine? Retrieved 2025, from https://www.foodandwine.com/wine/whats-difference-between-red-and-white-wine

[4] Gardener, D., & Kelly, M. (2025). Volatile Acidity in Wine. https://extension.psu.edu/volatile-acidity-in-wine

[5] Stockley, Creina et al. “So2 and Wine: A Review.” OIV Collective Expertise Document, 1 ed., International Organisation of Vine and Wine (OIV), March 2021 2021, pp. 1 - 30. general editor, OIV Publications, 2025. (obtained from: https://www.oiv.int/public/medias/7840/oiv-collective-expertise-document-so2-and-wine-a-review.pdf)

LS0tDQp0aXRsZTogIkEgQmxpbmQgVGFzdGUgVGVzdCA8aW1nIHNyYz1cImh0dHBzOi8vbmxlcGVyYS5naXRodWIuaW8vc3RhNTUxL0hXMDEvaW1nL3Blbmd1aW5fY3V0ZS5wbmdcIiBzdHlsZT1cImZsb2F0OiByaWdodDsgd2lkdGg6IDEyJVwiLz4iDQpzdWJ0aXRsZTogIlByZWRpY3Rpbmcgc29tbWVsaWVyIHF1YWxpdHkgcmFua2luZ3Mgb2Ygd2luZSBhbmQgd2luZSB0eXBlIChyZWQgLyB3aGl0ZSkgdXRpbGl6aW5nIGNoZW1pY2FsIGNvbXBvc2l0aW9uIGFsb25lLiINCmF1dGhvcjoNCi0gbmFtZTogTmF0YWxpZSBMZVBlcmENCiAgYWZmaWxpYXRpb246IFdlc3QgQ2hlc3RlciBVbml2ZXJzaXR5IHwgU1RBNTUyIC0gSFcgMDQNCmRhdGU6ICIwMiBBcHIgMjAyNSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRvY19jb2xsYXBzZTogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgZmlnX2FsaWduOiBjZW50ZXINCiAgICBkZl9wcmludDoga2FibGUNCi0tLQ0KDQpgYGB7Y3NzLCBlY2hvID0gRkFMU0V9DQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXdlaWdodDpib2xkOw0KICBjb2xvcjogZGFya21hZ2VudGEgOw0KICBmb250LWZhbWlseTogdmVyZGFuYTsNCn0NCmgxLnN1YnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtd2VpZ2h0OmJvbGQ7DQogIGNvbG9yOiBkYXJrbWFnZW50YSA7DQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LWZhbWlseTogdmVyZGFuYTsNCiAgY29sb3I6IG5hdnk7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciB0aGUgZGF0ZSAgKi8NCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQogIGNvbG9yOiBuYXZ5Ow0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtd2VpZ2h0OmJvbGQ7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCiAgICBmb250LWZhbWlseTogdmVyZGFuYTsNCn0NCg0KLmhyZWQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQogICAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmJvZHkgew0KICBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOw0KICBmb250LWZhbWlseTogdmVyZGFuYTsNCn0NCg0KLmhpZ2hsaWdodG1lIHsgDQogIGJhY2tncm91bmQtY29sb3I6eWVsbG93OyANCn0NCg0KcCB7IA0KICBiYWNrZ3JvdW5kLWNvbG9yOndoaXRlOyANCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCmg1IHsNCiAgY29sb3I6IG5hdnk7DQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KfQ0KDQouaWZyYW1lIHsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KDQphOmxpbmsgew0KICBjb2xvcjogZGFya21hZ2VudGE7DQogIGZvbnQtZmFtaWx5OiB2ZXJkYW5hOw0KfQ0KDQouZmlnbGFiZWwgew0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGNvbG9yOiBzbGF0ZWdyYXk7DQogIGZvbnQtc3R5bGU6IGl0YWxpYzsNCiAgZm9udC1zaXplOiAxODsNCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCi50ZDEgew0KICBmb250LXdlaWdodDogYm9sZDsNCiAgZm9udC1mYW1pbHk6IHZlcmRhbmE7DQp9DQoNCnRoLCB0ZCB7DQogIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZGRkOw0KICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQp0cjpob3ZlciB7YmFja2dyb3VuZC1jb2xvcjogY29yYWw7fQ0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KaWYgKCFyZXF1aXJlKCJkcGx5ciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZHBseXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtaXN0eSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibWlzdHkiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibWlzdHkiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInBseXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBseXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicGx5ciIpDQp9DQoNCmlmICghcmVxdWlyZSgic3RyaW5nciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygic3RyaW5nciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJzdHJpbmdyIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwbG90bHkiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInBhbmRvYyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZG9jIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBhbmRvYyIpDQp9DQoNCmlmICghcmVxdWlyZSgiZ3JpZEV4dHJhIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJncmlkRXh0cmEiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ3JpZEV4dHJhIikNCn0NCmlmICghcmVxdWlyZSgicGxvdHJpeCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdHJpeCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwbG90cml4IikNCn0NCg0KaWYgKCFyZXF1aXJlKCJncmlkIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJncmlkIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdyaWQiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJyYXN0ZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInJhc3RlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJyYXN0ZXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJkYnNjYW4iKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImRic2NhbiIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJkYnNjYW4iKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwUk9DIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwUk9DIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBST0MiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3JpZGdlcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dyaWRnZXMiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dyaWRnZXMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgia25pdHIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJHR2FsbHkiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIkdHYWxseSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJHR2FsbHkiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdnbHBvdDIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjbHVzdGVyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJjbHVzdGVyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImNsdXN0ZXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJrYWJsZUV4dHJhIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJrYWJsZUV4dHJhIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImthYmxlRXh0cmEiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJmb3JjYXRzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJmb3JjYXRzIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImZvcmNhdHMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJycGFydCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicnBhcnQiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicnBhcnQiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJycGFydC5wbG90IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInJwYXJ0LnBsb3QiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtZXRhbiIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibWV0YW4iLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibWV0YW4iKQ0KfQ0KIGlmICghcmVxdWlyZSgiTW9kZWxNZXRyaWNzIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIk1vZGVsTWV0cmljcyIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJNb2RlbE1ldHJpY3MiKQ0KIH0NCiBpZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgicGFuZGVyIikNCiB9DQoNCiBpZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogICBsaWJyYXJ5KCJnZ3Bsb3QyIikNCiB9DQoNCiBpZiAoIXJlcXVpcmUoImUxMDcxIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImUxMDcxIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoImUxMDcxIikNCiB9DQogaWYgKCFyZXF1aXJlKCJwcmVzZW50ZXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicHJlc2VudGVyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoInByZXNlbnRlciIpDQogfQ0KIGlmICghcmVxdWlyZSgibWljZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJtaWNlIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCiAgIGxpYnJhcnkoIm1pY2UiKQ0KIH0NCiBpZiAoIXJlcXVpcmUoImdnbWljZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJnZ21pY2UiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICAgbGlicmFyeSgiZ2dtaWNlIikNCiB9DQppZiAoIXJlcXVpcmUoImNvcnJwbG90IikpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygiY29ycnBsb3QiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBsaWJyYXJ5KCJjb3JycGxvdCIpDQp9DQoNCmlmICghcmVxdWlyZSgiY2FyZXQiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIGxpYnJhcnkoImNhcmV0IikNCn0NCg0KaWYgKCFyZXF1aXJlKCJnZ3B1YnIiKSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3B1YnIiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KICBsaWJyYXJ5KCJnZ3B1YnIiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImdsbW5ldCIpKSB7DQogIGluc3RhbGwucGFja2FnZXMoImdsbW5ldCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQogIGxpYnJhcnkoImdsbW5ldCIpDQp9DQoNCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsICAgDQogICAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gVFJVRSwgICANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbnNvbGUgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAnY2VudGVyJykNCg0Kb3B0aW9ucyhEVC5vcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gNSwgc2Nyb2xsWCA9IFRSVUUpKQ0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNCldpbmUsIG9uZSBvZiB0aGUgb2xkZXN0IGFsY29ob2xpYyBiZXZlcmFnZXMga25vd24gdG8gbWFuLCBoYXMgYmVlbiB0aGVvcml6ZWQgdG8gZXhpc3QgYXMgZmFyIGJhY2sgaW4gaHVtYW4gaGlzdG9yeSBhcyA2LDAwMCBCQy4gIEhhaWxpbmcgb3JpZ2luYWxseSBmcm9tIG1vZGVybiBkYXkgR2VvcmdpYSwgZXh0ZW5zaXZlIGFudGhyb3BvbG9naWNhbCBhbmQgYXJjaGFlb2xvZ2ljYWwgcmVzZWFyY2ggaGFzIGxpbmtlZCB3aW5lIHRvIGNhc3VhbCwgc29jaWV0YWwsIGFuZCByZWxpZ2lvdXMgcHVycG9zZXMgdGhyb3VnaG91dCB0aGUgZHVyYXRpb24gb2YgaHVtYW4gaGlzdG9yeS4gIEl0IGlzIG5vIHN1cnByaXNlIHRoYXQgdGhlIHdpbmVzIG9mIHRvZGF5IHdpZGVseSB2YXJ5IGZyb20gdGhlIHdpbmVzIG9mIGFuY2llbnQgY2l2aWxpemF0aW9ucywgYnV0IHRoZSBjb3JlIHByaW5jaXBhbHMgaGF2ZSByZW1haW5lZCB0aGUgc2FtZTogZmVybWVudGVkIGdyYXBlcyAoYWJvdXQgNjAwLTgwMCA8aT5wZXIgYm90dGxlITwvaT4pIFsyXS4gIA0KDQpKdW1waW5nIHRvIHRoZSB3b3JsZCBvZiB0b2RheSwgd2luZSBoYXMgcmVtYWluZWQgYSBzdGFwbGUgaW4gaHVtYW4gY3VsdHVyZSBhbmQgYSBzdHJvbmdob2xkIGluIHRoZSBVbml0ZWQgU3RhdGVzIGVjb25vbXkuICBDbG9ja2luZyBpbiBhdCB0aGUgbnVtYmVyIDEgY29uc3VtZXIgb2Ygd2luZSBnbG9iYWxseSBhY2NvcmRpbmcgdG8gdGhlIE5hdGlvbmFsIEFzc29jaWF0aW9uIG9mIEFtZXJpY2FuIFdpbmVyaWVzLCB0aGUgVW5pdGVkIFN0YXRlcycgd2luZSBpbmR1c3RyeSByZXBvcnRlZCBzdGFnZ2VyaW5nIGZpZ3VyZXMgb2YgXCQyNzUgYmlsbGlvbiBpbiAyMDIyIGFsb25lIGJyaW5naW5nIGluIG92ZXIgXCQzMCBiaWxsaW9uIGluIHRheGVzIGFubnVhbGx5IFsyXS4gIEFzIHRoaXMgaW5kdXN0cnkgaGFzIHByb3ZlbiB0byBiZSBhIHN0cm9uZ2hvbGQgb2YgdGhlIEFtZXJpY2FuIGVjb25vbXksIHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgbmVlZCBmZWx0IGJ5IHJlc3RhdXJhdGV1cnMgYW5kIHdpbmUgcHVydmV5b3JzIHRvIHByb3ZpZGUgY2xlYXIgYW5kIGFjY3VyYXRlIHByZWRpY3Rpb25zIG9uIHRoZSBxdWFsaXR5IG9mIG5ldyB3aW5lcy4gIA0KDQpBcyB3aW5lIHRhc3RpbmcgYW5kIHJhbmtpbmcgaXMgZXNzZW50aWFsbHkgc3ViamVjdGl2ZSwgdGhlIHJlc2VhcmNoZXJzIGF0IHRoZSBVbml2ZXJzaXR5IG9mIE1pbmhvIHNldCBvdXQgdG8gY29tcGFyZSBleHBlcnQgc29tbWVsaWVyIHJhbmtpbmdzIHRvIG1lYXN1cmFibGUgcGh5c2ljYWwgY2hhcmFjdGVyaXN0aWNzIG9mIGJvdGggcmVkIGFuZCB3aGl0ZSB3aW5lIHRvIG1pdGlnYXRlIHBlcnNvbmFsIGJpYXMgWzFdLiAgVGhpcyBkYXRhIHRoZW4gYWxsb3dzIGZvciBjcmVhdGlvbiBvZiBwcmVkaWN0aW9uIG1vZGVscyB0byBhaWQgaW4gcHJlZGljdGlvbiBvZiBhIHdpbmUncyBxdWFsaXR5IHJhbmtpbmcgd2l0aG91dCBmb3JtYWwgc29tbWVsaWVyIHRhc3RlIHRlc3RpbmcgYW5kIHN1YmplY3Rpdml0eS4gIFRoaXMgYXBwcm9hY2ggd2lsbCBhbGxvdyBidXJnZW9uaW5nIHdpbmUgbWFrZXJzIHdpdGggbW9yZSBsaW1pdGVkIHJlc291cmNlcyB0byBwcm92aWRlIGEgZ2VuZXJhbCBxdWFsaXR5IHJhbmtpbmcgYW5kIGFzc2VzcyB0aGUgcG90ZW50aWFsIG1hcmtldCB2YWx1ZSBvZiBhIG5ld2x5IHByb2R1Y2VkIHdpbmUuICANCg0KQWRkaXRpb25hbGx5LCB3aXRoIHRoZSBpbmNyZWFzaW5nIG51bWJlciBvZiB3aW5lIGJsZW5kcyBhbmQgdmFyeWluZyBpbmNsdXNpb24gbGV2ZWxzIG9mIGdyYXBlIHNraW5zIGluIHRoZSBmZXJtZW50YXRpb24gcHJvY2VzcywgaXQgaGFzIGJlY29tZSBtb3JlIGRpZmZpY3VsdCB0byBjbGVhbmx5IGNsYXNzaWZ5IHdpbmVzIGFzIHdoaXRlIG9yIHJlZC4gIFRyYWRpdGlvbmFsbHkgcmVkIHdpbmVzIGFyZSBjcmVhdGVkIGJ5IGZlcm1lbnRpbmcgZ3JhcGVzIHdpdGggaW50YWN0IHNraW5zIHdoaWxlIHdoaXRlIHdpbmVzIGFyZSBmZXJtZW50ZWQgd2l0aG91dCBza2lucywgbWVhbndoaWxlIGNvbW1vbmx5IHBvcHVsYXIgYmxlbmRzIHN1Y2ggYXMgcm9zZSBhcmUgY3JlYXRlZCBieSB1dGlsaXppbmcgYSBtaXh0dXJlIG9mIGJvdGggc2tpbiBvbiBhbmQgc2tpbmxlc3MgZ3JhcGVzIFszXS4gIFRocm91Z2ggY2hlbWljYWwgYW5hbHlzaXMgb2Ygd2luZXMgY3VycmVudGx5IG9uIHRoZSBtYXJrZXQsIHRoZSBkYXRhIGFzIGNvbGxlY3RlZCBhbmQgcHJlcGFyZWQgYnkgQ29ydGV6LCBDZXJkZWlyYSwgZXQuIGFsLiBjYW4gYmUgbGV2ZXJhZ2VkIHRvIGNyZWF0ZSBhIHJlZCB2cyB3aGl0ZSBjbGFzc2lmaWNhdGlvbiBmb3Igd2luZXMgZmFsbGluZyBpbiBiZXR3ZWVuIHRoZSB0cmFkaXRpb25hbCBjYXRlZ29yaWVzLiAgVGhpcyByZWQgdnMgd2hpdGUgcHJlZGljdGlvbiBjYW4gYmUgdXRpbGl6ZWQgYXMgYSBtYXJrZXRpbmcgdG9vbCBieSB3aW5lIG1ha2VycyB0byBwcm92aWRlIGNvbnN1bWVycyB3aXRoIGEgYmVuY2htYXJrIG9mIHdpbmUgdGFzdGUgYW5kIHByb3BlcnRpZXMgZm9yIGJsZW5kIHdpbmVzLCBpbmRpY2F0aW5nIGlmIHRoZSBmbGF2b3IgYW5kIGNoZW1pY2FsIHByb2ZpbGUgaXMgY2xvc2VyIHRvIGEgdHJ1ZSByZWQgb3IgYSB0cnVlIHdoaXRlIHdpbmUuIA0KDQojIyBEYXRhIENvbGxlY3Rpb24gYW5kIERldGFpbHMNCg0KQSB0b3RhbCBvZiA2NDk3IHdpbmVzIHdlcmUgc2FtcGxlZCAoMTU5OSByZWQgd2luZXMgYW5kIDQ4OTggd2hpdGUgd2luZXMpIGZvciB0aGVpciBjaGVtaWNhbCBjb21wb3NpdGlvbiBhcyB3ZWxsIGFzIHN1Ym1pdHRlZCBmb3Igc29tbWVsaWVyIGV2YWx1YXRpb24uICBJbiBvcmRlciB0byBtaXRpZ2F0ZSBwb3RlbnRpYWwgdGFzdGVyIGJpYXMsIGEgbWluaW11bSBvZiB0aHJlZSAoMykgc29tbWVsaWVycyBldmFsdWF0ZWQgZWFjaCB3aW5lIG9uIGEgc2NvcmUgb2YgMCAoYXdmdWwpIHRvIDEwICh2ZXJ5IGV4Y2VsbGVudCkgYW5kIHRoZSBtZWRpYW4gZXZhbHVhdGlvbiBzY29yZSB3YXMgdXRpbGl6ZWQgWzFdLiAgVGhlIGNoZW1pY2FsIGNvbXBvc2l0aW9uIGVsZW1lbnRzIGV4YW1pbmVkIGFyZSBpbmNsdWRlZCBpbiB0aGUgYmVsb3cgYFRhYmxlIDFgIGZvciByZWZlcmVuY2UuICBBZGRpdGlvbmFsIGRldGFpbHMgcmVnYXJkaW5nIHRoZSBjb2xsZWN0aW9uIG9mIHRoaXMgZGF0YSBjYW4gYmUgZm91bmQgYXQgdGhpcyA8YSBocmVmID0gImh0dHBzOi8vbmxlcGVyYS5naXRodWIuaW8vc3RhNTUyL0hXMDQvZGF0YS93aW5lcXVhbGl0eV9uYW1lcy5ydGYiPmxpbms8L2E+Lg0KDQoNCmBgYHtyfQ0KdmFyLmRldGFpbHMgPC0gZGF0YS5mcmFtZSgNCiAgVmFyaWFibGUgPSBjKCJGaXhlZCBBY2lkaXR5IiwgIlZvbGF0aWxlIEFjaWRpdHkiLCAiQ2l0cmljIEFjaWQiLCAiUmVzaWR1YWwgU3VnYXIiLCAiQ2hsb3JpZGVzIiwgIkZyZWUgU3VsZnVyIERpb3hpZGUiLCAiVG90YWwgU3VsZnVyIERpb3hpZGUiLCAiRGVuc2l0eSIsICJwSCIsICJTdWxwaGF0ZXMiLCAiQWxjb2hvbCIsICJRdWFsaXR5IiwgIldpbmUgVHlwZSIpLA0KICBVbml0ID0gYygiZy9kbV4zIiwgImcvZG1eMyIsICJnL2RtXjMiLCAiZy9kbV4zIiwgImcvZG1eMyIsICJnL2RtXjMiLCAiZy9kbV4zIiwgImcvZG1eMyIsICJwSCIsICJnL2RtXjMiLCAiJSBvZiB2b2x1bWUiLCAiIiwgIiIpLA0KICBUeXBlID0gYygiQ29udGludW91cyIsICJDb250aW51b3VzIiwgIkNvbnRpbnVvdXMiLCAiQ29udGludW91cyIsICJDb250aW51b3VzIiwgIkNvbnRpbnVvdXMiLCAiQ29udGludW91cyIsICJDb250aW51b3VzIiwgIkNvbnRpbnVvdXMiLCAiQ29udGludW91cyIsICJDb250aW51b3VzIiwgIkNvbnRpbnVvdXMiLCAiQmluYXJ5IiksDQogIERldGFpbHMgPSBjKCJDb25jZW50cmF0aW9uIG9mIHRhcnRhcmljIGFjaWQiLCAiQ29uY2VudHJhdGlvbiBvZiBhY2V0aWMgYWNpZCIsICJDb25jZW50cmF0aW9uIG9mIGNpdHJpYyBhY2lkIiwgIkNvbmNlbnRyYXRpb24gb2YgcmVzaWR1YWwgc3VnYXIgYWZ0ZXIgZmVybWVudGF0aW9uIiwgIkNvbmNlbnRyYXRpb24gb2Ygc29kaXVtIGNobG9yaWRlIiwgIkNvbmNlbnRyYXRpb24gb2YgZnJlZSBzdWxwaHVyIGRpb3hpZGUiLCAiVG90YWwgY29uY2VudHJhdGlvbiBvZiBzdWxwaHVyIGRpb3hpZGUiLCAiRGVuc2l0eSBvZiB3aW5lIiwgInBIIG9mIHdpbmUiLCAiQ29uY2VudHJhdGlvbiBvZiBzdWxwaGF0ZXMiLCAiQWxjb2hvbCBjb25jZW50cmF0aW9uIiwgIk1lYW4gd2luZSBxdWFsaXR5IHJhdGluZyAoMCB0byAxMCkiLCAiV2luZSB0eXBlIChSZWQvV2hpdGUpIiApDQopDQoNCmthYmxlKHZhci5kZXRhaWxzLCBjYXB0aW9uID0gIlRhYmxlIDE6DQogICAgICA8YnI+V2luZSBkYXRhIHZhcmlhYmxlIHVuaXRzLCB0eXBlcywgYW5kIGdlbmVyYWwgZGVzY3JpcHRpb24iKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCg0KIyBEYXRhIFByZXBhcmF0aW9uDQoNCkluaXRpYWxseSB0aGUgd2luZSBkYXRhIHdhcyBzdG9yZWQgaW4gc2VwYXJhdGUgd2hpdGUgd2luZSBhbmQgcmVkIHdpbmUgZGF0YSBzZXRzLiAgSW4gb3JkZXIgdG8gcHJlcGFyZSBhbmQgdXRpbGl6ZSB0aGlzIGRhdGEgZm9yIGFuYWx5c2lzIHRoZSB3aGl0ZSBhbmQgcmVkIHdpbmUgZGF0YSBzZXRzIHdlcmUgbWVyZ2VkLCB3aXRoIHRoZSBjcmVhdGlvbiBvZiBhIGNhdGVnb3JpY2FsIHZhbHVlIHRvIGNhcHR1cmUgdGhlIHR5cGUgb2Ygd2luZSAoUmVkOiBUeXBlID0gMCB8IFdoaXRlOiBUeXBlID0gMSkuIEEgY3Vyc29yeSByZXZpZXcgb2YgdGhlIG1lcmdlZCB3aW5lIGRhdGEgaW5kaWNhdGVzIHRoZXJlIGFyZSBubyBtaXNzaW5nIHZhbHVlcywgdGhlcmVmb3JlIGltcHV0YXRpb24gaXMgbm90IHJlcXVpcmVkIGZvciB0aGlzIHByZWRpY3Rpb24gYW5hbHlzaXMgKGBUYWJsZSAyYCkuDQoNCmBgYHtyfQ0KI3JlYWQgaW4gZGF0YQ0KcmVkd2luZSA8LSByZWFkLmNzdigiaHR0cHM6Ly9ubGVwZXJhLmdpdGh1Yi5pby9zdGE1NTIvSFcwNC9kYXRhL3dpbmVxdWFsaXR5LXJlZC5jc3YiKQ0Kd2hpdGV3aW5lIDwtIHJlYWQuY3N2KCJodHRwczovL25sZXBlcmEuZ2l0aHViLmlvL3N0YTU1Mi9IVzA0L2RhdGEvd2luZXF1YWxpdHktd2hpdGUuY3N2IikNCg0KI3NldCByZWQgPSAxIC8gd2hpdGUgPSAwIHByZS1tZXJnZQ0KcmVkd2luZSR0eXBlZiA8LSBhcy5mYWN0b3IoIlJlZCIpDQp3aGl0ZXdpbmUkdHlwZWYgPC0gYXMuZmFjdG9yKCJXaGl0ZSIpDQoNCiNtZXJnZSByZWQgYW5kIHdoaXRlIHdpbmUNCndpbmUgPC0gYmluZF9yb3dzKHJlZHdpbmUsIHdoaXRld2luZSkNCg0KI3N1bW1hcnkgdGFibGUNCmthYmxlKHN1bW1hcnkod2luZSksIGFsaWduID0gImwiLCBjYXB0aW9uID0gIlRhYmxlIDI6DQogICAgICA8YnI+U3VtbWFyeSBzdGF0aXN0aWNzIG9mIHdpbmUgZGF0YS4NCiAgICAgIDxicj5UeXBlOiBSZWQgPSAxIHwgV2hpdGUgPSAwIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkgJT4lIA0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiKQ0KYGBgDQoNCiMjIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkNCg0KVGhlIG1lcmdlZCB3aW5lIGRhdGEgc2V0IHdhcyB0aGVuIGV4YW1pbmVkIHRvIGRldGVybWluZSB0aGUgcG90ZW50aWFsIGZvciBjb3JyZWxhdGlvbiBiZXR3ZWVuIGZlYXR1cmUgdmFyaWFibGVzLiBgRmlndXJlIDFgIGJlbG93IHByb3ZpZGVzIGEgdmlzdWFsIGRlbW9uc3RyYXRpb24gb2YgdGhlIHN0cmVuZ3RoIG9mIGVhY2ggY29ycmVsYXRpb24gYmV0d2VlbiB2YXJpYWJsZXMsIHdpdGggdGhlIGNpcmNsZSBzaXplIGFuZCBjb2xvciBjb3JyZXNwb25kaW5nIHRvIHRoZSBjb3JyZWxhdGlvbidzIG1hZ25pdHVkZSBhbmQgZGlyZWN0aW9uIChwb3NpdGl2ZS9uZWdhdGl2ZSkuIENhbGN1bGF0ZWQgcGVhcnNvbiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCAocikgdmFsdWVzIHdpdGggYW4gYWJzb2x1dGUgdmFsdWUgZ3JlYXRlciB0aGFuIDAuNiBhcmUgY29uc2lkZXJlZCB0byBpbmRpY2F0ZSBtb2RlcmF0ZSB0byBzdHJvbmcgY29ycmVsYXRpb25zOyBhIHRvdGFsIG9mIGZvdXIgKDQpIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHNpeCAoNikgdW5pcXVlIHZhcmlhYmxlcyB3ZXJlIGlkZW50aWZpZWQgYXMgYXQgbGVhc3QgbW9kZXJhdGUsIGFzIGNhcHR1cmVkIGJlbG93IGluIGBGaWd1cmUgMmAgYW5kIGBUYWJsZSAzYC4gDQoNCmBgYHtyLCBmaWcuaGVpZ2h0ID0gOH0NCndpbmUkdHlwZSA8LSBhcy5udW1lcmljKHdpbmUkdHlwZWYpDQp3aW5lJHR5cGVbd2luZSR0eXBlID09IDJdIDwtIDANCg0KI2NvcnJlbGF0aW9uIGNoZWNrDQojY3JlYXRlIGNvcnJlbGF0aW9uIG1hdHJpeA0KY29yIDwtIGNvcih3aW5lWy0xM10pDQoNCiMgcGxvdCBjb3JyZWxhdGlvbiBtYXRyaXgNCmNvcnJwbG90KGNvciwgdHlwZSA9InVwcGVyIiwgb3JkZXIgPSAiaGNsdXN0IiwgdGwuY29sID0gImJsYWNrIiwgdGwuc3J0ID0gNjUsIGRpYWcgPSBULCBtYXIgPSBjKDIsMSw0LDEpKQ0KdGl0bGUobWFpbiA9ICJWYXJpYWJsZSBDb3JyZWxhdGlvbiIsIHN1YiA9ICJGaWd1cmUgMToNCkNvcnJlbGF0aW9uIGFuYWx5c2lzIG9mIHdpbmUgZGF0YSB2YXJpYWJsZXMiKQ0KYGBgDQoNCmBgYHtyfQ0KI2NvcnJlbGF0aW9uIGRhdGEgZnJhbWUgZm9yIGZpbHRlcmluZw0KY29yMiA8LSBkYXRhLmZyYW1lKA0KICByb3cgPSByb3duYW1lcyhjb3IpW3Jvdyhjb3IpXSwNCiAgY29sID0gY29sbmFtZXMoY29yKVtjb2woY29yKV0sDQogIGNvciA9IGMoY29yKQ0KICApDQoNCiNmaWx0ZXIgZm9yIG1vZGVyYXRlIHRvIHN0cm9uZyBjb3JyZWxhdGlvbiwgaWdub3JpbmcgY29ycmVsYXRpb24gPSAxIChzYW1lIHZhcmlhYmxlIG1hdGNoKQ0KY29yciA8LSBmaWx0ZXIoY29yMiwgYWJzKGNvcikgPj0gMC42KSAlPiUgZmlsdGVyKGFicyhjb3IpICE9IDEpIA0KY29yciRjb3IgPC0gcm91bmQoY29yciRjb3IsIDMpDQpjb3JyIDwtIGNvcnJbb3JkZXIoY29yciRjb3IpLCBdDQoNCmNvci5hbC5kZW4gPC0gZ2dwbG90KHdpbmUsIGFlcyh4ID0gYWxjb2hvbCwgeSA9IGRlbnNpdHkpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjc1KSArDQogIHRoZW1lX2J3KCkNCg0KY29yLnRzbzIuZnNvMiA8LSBnZ3Bsb3Qod2luZSwgYWVzKHggPSB0b3RhbC5zdWxmdXIuZGlveGlkZSwgeSA9IGZyZWUuc3VsZnVyLmRpb3hpZGUpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIiwgYWxwaGEgPSAwLjc1KSArDQogIHhsYWIoInRvdGFsIHN1bGZ1ciBkaW94aWRlICh0LlNPMikiKSArDQogIHlsYWIoImZyZWUgc3VsZnVyIGRpb3hpZGUgKGYuU08yKSIpICsNCiAgdGhlbWVfYncoKQ0KDQpjb3IudHkudmFjIDwtIGdncGxvdCh3aW5lLCBhZXMoeCA9IHR5cGVmLCB5ID0gdm9sYXRpbGUuYWNpZGl0eSkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGwgPSBjKCJyZWQ0IiwgInllbGxvdzMiKSwgYWxwaGEgPSAwLjUpICsNCiAgeGxhYigidHlwZSIpICsNCiAgeWxhYigidm9sYXRpbGUgYWNpZGl0eSIpICsNCiAgdGhlbWVfYncoKQ0KDQpjb3IudHkudHNvMiA8LSBnZ3Bsb3Qod2luZSwgYWVzKHggPSB0eXBlZiwgeSA9IHRvdGFsLnN1bGZ1ci5kaW94aWRlKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbCA9IGMoInJlZDQiLCAieWVsbG93MyIpLCBhbHBoYSA9IDAuNSkgKw0KICB4bGFiKCJ0eXBlIikgKw0KICB5bGFiKCJ0b3RhbCBzdWxmdXIgZGlveGlkZSAodC5TTzIpIikgKw0KICB0aGVtZV9idygpDQogIA0KYGBgDQoNCg0KDQpgYGB7ciwgZmlnLmhlaWdodD02LCBvdXQud2lkdGg9IjEwMCUifQ0KZ3JpZC5hcnJhbmdlKA0KICBhcnJhbmdlR3JvYihjb3IuYWwuZGVuLCBjb3IudHNvMi5mc28yLCBjb3IudHkudmFjLCBjb3IudHkudHNvMiwgbmNvbCA9IDIsIG5yb3cgPSAyKSwNCiAgdG9wID0gdGV4dEdyb2IoIlBhaXJ3aXNlIENvcnJlbGF0aW9uIFBsb3RzDQogICAgICAgICAgICAgICAgICIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE4LCBmb250ZmFjZSA9ICJib2xkIikpLA0KICBib3R0b20gPSB0ZXh0R3JvYigiRmlndXJlIDINCiAgVmlzdWFsemF0aW9ucyBvZiB3aW5lIGRhdGEgdmFyaWFibGUgY29ycmVsYXRpb25zIHdoZXJlIHwgciB8ID4gMC42IikpDQoNCiNzdW1tYXJ5IHRhYmxlIC0gcmVtb3ZlZCBkdXBsaWNhdGUgZW50cmllcyAoc3dhcHBlZCBmcm9tIHYxIHRvIHYyKQ0Ka2FibGUoY29ycltjKDEsMyw1LDcpLF0sIGFsaWduID0gImwiLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBjKCJWYXJpYWJsZSAxICh4KSIsICJWYXJpYWJsZSAyICh5KSIsICJDb3JyZWxhdGlvbiIpLCBjYXB0aW9uID0gIlRhYmxlIDM6DQogICAgICA8YnI+TW9kZXJhdGUgdG8gc3Ryb25nIHZhcmlhYmxlIGNvcnJlbGF0aW9ucyBpZGVudGlmaWVkIGluIHRoZSBtZXJnZWQgd2luZSBkYXRhLiBEdXBsaWNhdGUgZW50cmllcyBoYXZlIGJlZW4gcmVtb3ZlZC4iKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNClRoZSBjb3JyZWxhdGVkIHZhcmlhYmxlcyBhcyBvdXRsaW5lZCBpbiBgRmlndXJlIDJgIGFuZGBUYWJsZSAzYCBhYm92ZSBwcm92ZSBsb2dpY2FsIGFuZCBkbyBub3QgcmVxdWlyZSBmdXJ0aGVyIGludmVzdGlnYXRpb24uDQoNCiAgLSBBbGNvaG9sIGlzIGtub3duIHRvIGhhdmUgYSBsaWdodGVyIGRlbnNpdHkgdGhhbiB3YXRlciwgdGhlcmVmb3JlIHRoZSBuZWdhdGl2ZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBiZXR3ZWVuIGFsY29ob2wgYW5kIGRlbnNpdHkgaXMgbG9naWNhbC4NCiAgLSBWb2xhdGlsZSBhY2lkaXR5IChjb25jZW50cmF0aW9uIG9mIGFjZXRpYyBhY2lkKSBpcyBrbm93biB0byBoYXZlIGEgaGlnaGVyIGFsbG93YWJsZSBsZXZlbHMgaW4gcmVkIHdpbmVzIHRoYW4gd2hpdGUgd2luZXMgcGVyIHRoZSBmZWRlcmFsIFRheCBhbmQgVHJhZGUgQnVyZWF1IFs0XS4gIEFzIHRoZSB3aW5lIHR5cGUgd2FzIGNvZGVkIGFzIDEgPSBSZWQgYW5kIDAgPSB3aGl0ZSB0aGlzIG9ic2VydmVkIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGlzIGxvZ2ljYWwuDQogIC0gUmVkIHdpbmVzIGFyZSBrbm93biB0byBoYXZlIGxvd2VyIHRvdGFsIHN1bGZ1ciBkaW94aWRlIGNvbmNlbnRyYXRpb25zIGFzIHRoZSB0YW5uaW4gcHJlc2VudCBpbiBncmFwZSBza2lucyBhc3Npc3QgaW4gc3RhYmlsaXphdGlvbiBkdXJpbmcgZmVybWVudGF0aW9uLCBwcmV2ZW50aW5nIFNPMiBieXByb2R1Y3QgZm9ybWF0aW9uIFs1XS4gQXMgdGhlIHdpbmUgdHlwZSB3YXMgY29kZWQgYXMgMSA9IFJlZCBhbmQgMCA9IHdoaXRlIHRoaXMgb2JzZXJ2ZWQgcG9zaXRpdmUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgaXMgbG9naWNhbC4NCiAgLSBBcyB0b3RhbCBzdWxmdXIgZGlveGlkZSBtZWFzdXJlcyBib3RoIHRoZSBmcmVlIGFuZCByZWFjdGVkIHN1bGZ1ciBkaW94aWRlLCB0aGUgcG9zaXRpdmUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYmV0d2VlbiB0b3RhbCBhbmQgZnJlZSBzdWxmdXIgZGlveGlkZSBjb25jZW50cmF0aW9ucyBpcyBsb2dpY2FsLiANCg0KIyMgRmVhdHVyZSBFbmdpbmVlcmluZw0KDQpJbiBvcmRlciB0byBwcm92aWRlIGFkZGl0aW9uYWwgZGltZW5zaW9ucyB0byB0aGUgcmVncmVzc2lvbiBhbmQgU1ZNIGFuYWx5c2lzIGZlYXR1cmUgZW5naW5lZXJpbmcgd2FzIHBlcmZvcm1lZCB0byBjcmVhdGUgdHdvIGFkZGl0aW9uYWwgdmFyaWFibGVzOiBoZWFkYWNoZSBhbmQgYWRkLiAgVGhlIGhlYWRhY2hlIHZhcmlhYmxlIHdhcyBjcmVhdGVkIGJ5IHRha2luZyB0aGUgcHJvZHVjdCBvZiBzdWxwaGF0ZXMgY29uY2VudHJhdGlvbiBhbmQgYWxjb2hvbCBjb250ZW50IGFzIHRoZXNlIGFyZSB0aGUgdHdvIG1vc3QgY29tbW9uIGNvbnRyaWJ1dGVycyB0byB3aW5lIGJhc2VkIGhhbmdvdmVycy4gIFNlY29uZGx5IHRoZSBhZGQgdmFyaWFibGUgd2FzIGNyZWF0ZWQgYnkgc3VtbWluZyB0aGUgc3VscGhhdGVzIGNvbmNlbnRyYXRpb24sIGNobG9yaWRlcyBjb25jZW50cmF0aW9uLCBhbmQgdGhlIGNpdHJpYyBhY2lkIGNvbmNlbnRyYXRpb24gdG8gZ2V0IGEgdG90YWwgYWRkaXRpdmVzIGNvbmNlbnRyYXRpb24uIExhc3RseSwgYSB2YXJpYWJsZSB3YXMgY3JlYXRlZCB0byBjYWxjdWxhdGUgdGhlIHJhdGlvIG9mIEZyZWUgU3VscGh1ciBEaW94aWRlIHRvIFRvdGFsIFN1bHBodXIgRGlveGlkZSB0byBvbWl0IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZXNlIHZhcmlhYmxlcy4NCg0KYGBge3J9DQojY29tYmluaW5nIGFsY29ob2wgYW5kIHN1bGZhdGVzIGZvciBoZWFkYWNoZSBwb3RlbnRpYWwNCndpbmUkaGVhZGFjaGUgPC0gd2luZSRzdWxwaGF0ZXMgKiB3aW5lJGFsY29ob2wNCg0KI2FkZGl0aXZlcyANCndpbmUkYWRkIDwtIHdpbmUkc3VscGhhdGVzICsgd2luZSRjaGxvcmlkZXMgKyB3aW5lJGNpdHJpYy5hY2lkDQoNCiNTTzIgcmF0aW8NCndpbmUkc28yLnJhdGlvIDwtIHdpbmUkZnJlZS5zdWxmdXIuZGlveGlkZSAvIHdpbmUkdG90YWwuc3VsZnVyLmRpb3hpZGUNCg0Ka2FibGUoc3VtbWFyeSh3aW5lW2MoMTQ6MTYpXSksIGFsaWduID0gImMiLCBjb2wubmFtZXMgPSBjKCJIZWFkYWNoZSIsICJBZGRpdGl2ZXMiLCAiU3VsZnVyIERpb3hpZGUgUmF0aW8iKSwgY2FwdGlvbiA9ICJUYWJsZSA0Og0KICAgICAgPGJyPlN1bW1hcnkgc3RhdGlzdGljcyBvZiBlbmdpbmVlcmVkIGZlYXR1cmUgdmFyaWFibGVzLiIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCg0KaGVhZGFjaGUgPC0gYygiSGVhZGFjaGUiLCAiIiwgIkNvbnRpbnVvdXMiLCAiQ2FsY3VsYXRlZCB2YXJpYWJsZSBmb3IgaGVhZGFjaGUgcG90ZW50aWFsIChwcm9kdWN0IFN1bHBoYXRlcyAmIEFsY29ob2wiKQ0KYWRkIDwtIGMoIlRvdGFsIEFkZGl0aXZlcyIsICIiLCAiQ29udGludW91cyIsICJDYWxjdWxhdGVkIHZhcmlhYmxlIGZvciB0b3RhbCB3aW5lIGFkZGF0aXZlcyAoc3VtIFN1bHBoYXRlcywgQ2hsb3JpZGVzLCAmIENpdHJpYyBBY2lkIikNCnNvMi5yYXRpbyA8LSBjKCJTdWxwdXIgRGlveGlkZSBSYXRpbyIsICIiLCAiQ29udGludW91cyIsICJDYWxjdWxhdGVkIHZhcmlhYmxlIGZvciByYXRpbyBvZiBGcmVlIFN1bHB1ciBEaW94aWRlIDogVG90YWwgU3VscGh1ciBEaW94aWRlIikNCg0KdmFyLmRldGFpbHMgPC0gcmJpbmQodmFyLmRldGFpbHMsIGhlYWRhY2hlKSAlPiUgcmJpbmQoYWRkKSAlPiUgcmJpbmQoc28yLnJhdGlvKQ0KYGBgDQoNCiMgUmVndWxhcml6ZWQgTGluZWFyIFJlZ3Jlc3Npb24NCg0KSW4gb3JkZXIgdG8gcHJlZGljdCB0aGUgbWVkaWFuIHNvbW1lbGllciBxdWFsaXR5IHNjb3JlIG9mIHdpbmUgYmFzZWQgb2ZmIG9mIHRoZSBjaGVtaWNhbCBwcm9wZXJ0aWVzIG9mIGEgc2FtcGxlLCBtdWx0aXBsZSByZWd1bGFyaXplZCBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbHMgd2VyZSBjcmVhdGVkIHdpdGggcXVhbGl0eSBhcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUuICBCZWZvcmUgYW55IHByZWRpY3Rpb24gbW9kZWxzIHdlcmUgY3JlYXRlZCwgdGhlIGRhdGEgd2FzIHNwbGl0IGludG8gODA6MjAgdHJhaW46dGVzdCBncm91cHMgdG8gcmVkdWNlIHRoZSBwb3RlbnRpYWwgZm9yIG92ZXItZml0dGluZyBhbmQgdG8gYWxsb3cgZm9yIGltcHJvdmVkIGh5cGVycGFyYW1ldGVyIHR1bmluZy4gQWRkaXRpb25hbGx5LCBvbmNlIHRoZSBkYXRhIHdhcyBzcGxpdCBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IGdyb3VwcywgdGhlIGZlYXR1cmUgdmFyaWFibGVzIHdlcmUgc3RhbmRhcmRpemVkIHRvIGVuc3VyZSBhIGNvbXBhcmFibGUgc2NhbGUgYmV0d2VlbiB2YXJpYWJsZXMgYW5kIHJlbW92ZSBwb3RlbnRpYWwgb3Zlci13ZWlnaHRpbmcgb2YgdmFyaWFibGVzLiANCg0KYGBge3J9DQojcmVncmVzc2lvbiBwcmVwIHdvcmsNCiNzZXQgc2VlZA0Kc2V0LnNlZWQoMTI5KQ0KDQojc3BsaXQgZmVhdHVyZSBhbmQgcmVzcG9uc2UgdmFyaWFibGVzDQp4LmxpbmVhciA8LSB3aW5lWy0xMl0NCnkubGluZWFyIDwtIHdpbmUkcXVhbGl0eQ0KDQojZGF0YSBzcGxpdCBmb3IgQ1YgKDgwOjIwKQ0Kc3BsaXQubGluZWFyIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oeS5saW5lYXIsIHA9MC44LCBsaXN0PUYpDQoNCiN0cmFpbmluZyBkYXRhDQp4LmxpbmVhci50cmFpbiA8LSB4LmxpbmVhcltzcGxpdC5saW5lYXIsXQ0KeS5saW5lYXIudHJhaW4gPC0geS5saW5lYXJbc3BsaXQubGluZWFyXQ0KDQojdGVzdCBkYXRhDQp4LmxpbmVhci50ZXN0IDwtIHgubGluZWFyWy1zcGxpdC5saW5lYXIsXQ0KeS5saW5lYXIudGVzdCA8LSB5LmxpbmVhclstc3BsaXQubGluZWFyXQ0KDQojc3RhbmRhcmRpemUgdmFsdWVzDQpzdGQubGluZWFyIDwtIHByZVByb2Nlc3MoeC5saW5lYXIudHJhaW4sIG1ldGhvZCA9IGMoImNlbnRlciIsICJzY2FsZSIpKQ0KeC5saW5lYXIudHJhaW4uc3RkIDwtIHByZWRpY3Qoc3RkLmxpbmVhciwgeC5saW5lYXIudHJhaW4pIA0KeC5saW5lYXIudGVzdC5zdGQgPC0gcHJlZGljdChzdGQubGluZWFyLCB4LmxpbmVhci50ZXN0KSANCg0KI2Ryb3AgZmFjdG9yIHR5cGUgKHR5cGVmKSBhcyBudW1lcmljIHR5cGUgYWxyZWFkeSBpbmNsdWRlZCAodHlwZSkNCngubGluZWFyLnRyYWluLnN0ZCA8LSB4LmxpbmVhci50cmFpbi5zdGRbLTEyXQ0KeC5saW5lYXIudGVzdC5zdGQgPC0geC5saW5lYXIudGVzdC5zdGRbLTEyXQ0KYGBgDQoNCiMjIExpbmVhciBSZWdyZXNzaW9uIE1vZGVsIEJ1aWxkcyAoTEFTU08sIFJpZGdlLCAmIEVsYXN0aWMgTmV0KQ0KDQpSZWd1bGFyaXplZCByZWdyZXNzaW9uIHdhcyBlbXBsb3llZCB0byBpbnRyb2R1Y2UgY29uc3RyYWludHMgb24gdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGFuZCBzdWJzZXF1ZW50bHkgcmVkdWNlIG92ZXItZml0dGluZyByaXNrLiAgVGhlIGxvc3MgZnVuY3Rpb24gd2FzIGVtcGxveWVkIHRocm91Z2ggdGhlIDxiPmdsbW5ldDwvYj4gcGFja2FnZSB0byBlbnN1cmUgYXBwcm9wcmlhdGUgcmVndWxhcml6ZWQgcmVncmVzc2lvbiB3aXRoIHZhcmlvdXMgYXBwcm9hY2hlcyAoTEFTU08sIFJpZGdlLCAmIEVsYXN0aWMgTmV0KSwgdXRpbGl6aW5nIHRoZSBiZWxvdyBlcXVhdGlvbnMgdG8gY29tcGxldGUgdGhlIG1vZGVsIGNyZWF0aW9uLiAgVGhlIExBU1NPIG1vZGVsIGF1dG9tYXRpY2FsbHkgcGVuYWxpemVzIHZhcmlhYmxlcyB1bHRpbWF0ZWx5IHJlc3VsdGluZyBpbiBmZWF0dXJlIHNlbGVjdGlvbiBhbmQgYSByZWR1Y2VkIG1vZGVsIGZvciB1c2Ugd2hpbGUgdGhlIFJpZGdlIG1vZGVsIHBlbmFsaXplcyB2YXJpYWJsZXMgYnkgcmVkdWNpbmcgaW1wYWN0IHdpdGhvdXQgZmVhdHVyZSBzZWxlY3Rpb24uIE1lYW53aGlsZSB0aGUgRWxhc3RpYyBOZXQgbW9kZWwgY29tYmluZXMgdGhlc2UgYXBwcm9hY2hlcyBhbmQgcmVzdWx0cyBpbiBwZW5hbGl6ZWQgdmFyaWFibGVzIHdpdGggcmVkdWNlZCBpbXBhY3QgYW5kIG1vcmUgbGltaXRlZCBmZWF0dXJlIHNlbGVjdGlvbi4gDQoNCmBgYHtyfQ0KbGFzc28gPC0gZ2xtbmV0KHgubGluZWFyLnRyYWluLnN0ZCwgeS5saW5lYXIudHJhaW4sIGFscGhhID0gMSkNCnJpZGdlIDwtIGdsbW5ldCh4LmxpbmVhci50cmFpbi5zdGQsIHkubGluZWFyLnRyYWluLCBhbHBoYSA9IDApDQplbmV0IDwtIGdsbW5ldCh4LmxpbmVhci50cmFpbi5zdGQsIHkubGluZWFyLnRyYWluLCBhbHBoYSA9IDAuNSkNCmBgYA0KDQoNCiQkDQpcYmVnaW57YWxpZ24qfQ0KXHVuZGVybGluZXtcbWJveHtMT1NTIEZ1bmN0aW9uIEVxdWF0aW9uc319IFxcIFxcDQpMT1NTX3tsaW5lYXJ9ID0gXCBcc3VtX3tpPTF9Xm4gKHlfaSAtIFxoYXQgeV9pKSBcXA0KTE9TU197cmVndWxhcml6ZWR9ID0gXCBMT1NTX3tsaW5lYXJ9ICsgIFxsYW1iZGEgIFxsZWZ0WyBcZnJhY3sxLVxhbHBoYX17Mn0gXHwgXGJldGEgXHxfezJ9XjIgKyBcYWxwaGEgXHwgXGJldGEgXHxfMVxyaWdodF0gXCBcICZcbWJveHt3aGVyZX0gXCBcICBcbGVmdFx7IFxiZWdpbnthcnJheX17cmx9IFxhbHBoYSA9IDE6ICZcbWJveHtMQVNTT30gXFwgXGFscGhhID0gMDogJlxtYm94e1JpZGdlfSBcXCAwIFxsZXEgYSBcbGVxIDE6ICZcbWJveHtFbGFzdGljIE5ldH0gXGVuZHthcnJheX1ccmlnaHQuIFxcDQpcZW5ke2FsaWduKn0NCiQkDQoNCiQkDQpcdW5kZXJsaW5le1xtYm94e1JlZ3VsYXJpemVkIFJlZ3Jlc3Npb24gRXF1YXRpb25zfX1cXA0KXGJlZ2lue2FycmF5fXtsfSBcXA0KTDFfe0xBU1NPfSAmPSBcIExPU1Nfe3JlZ3Vhcml6ZWR9ICYrIFwgXCBcbGFtYmRhIFxzdW1fe2o9MX1eayB8YV9qfCBcIFwgXFwNCkwyX3tyaWRnZX0gJj0gXCBMT1NTX3tyZWd1bGFyaXplZH0gJisgXCBcIFxsYW1iZGEgXHN1bV97aj0xfV5rIHxhX2p8XjIgXCBcICBcXA0KTDNfe0VsYXN0aWN9ICY9IExPU1Nfe3JlZ3VsYXRpc2VkfSAmKyAgXCBcIFxsYW1iZGEgXHN1bV97aj0xfV5rIHxhX2p8ICsgXCBcIFxsYW1iZGEgXHN1bV97aj0xfV5rIHxhX2p8XjIgXCANClxlbmR7YXJyYXl9IFwgIFxtYm94e3doZXJlfSBcIFwgXCAgXGxlZnRceyBcYmVnaW57YXJyYXl9e3JsfSBcYWxwaGEgPSAmXG1ib3h7UmVncmVzc2lvbiBDb2VmZi59IFxcIGsgPSAmXG1ib3h7IyBvZiBGZWF0dXJlIFZhcnMufVxcIFxsYW1iZGEgPSAmXG1ib3h7SHlwZXJwYXJhbWV0ZXJ9IFxlbmR7YXJyYXl9IFxyaWdodC4NCiQkDQo8YnI+DQoNCkh5cGVycGFyYW1ldGVyICRcbGFtYmRhJCB2YWx1ZXMgd2VyZSBzdWJzZXF1ZW50bHkgdHVuZWQgdGhyb3VnaCBjcm9zcyB2YWxpZGF0aW9uIGFzIHBhcnQgb2YgdGhlIG1vZGVsIHNlbGVjdGlvbiBwcm9jZXNzLiBNb3JlIGluZm9ybWF0aW9uIG9uICRcbGFtYmRhJCB2YWx1ZSBzZWxlY3Rpb24gaXMgaW5jbHVkZWQgYmVsb3cgaW4gc2VjdGlvbiA8Yj4zLjIuMSBDb2VmZmljaWVudCBQYXRoIEFuYWx5c2lzICYgQ3Jvc3MgVmFsaWRhdGlvbjwvYj4NCg0KIyMgTGluZWFyIE1vZGVsIEFuYWx5c2lzICYgU2VsZWN0aW9uDQoNClJlZ3Jlc3Npb24gbW9kZWxzIHdlcmUgY3JlYXRlZCB1dGlsaXppbmcgdGhlIHByZWRpY3RlZCByZWdyZXNzaW9uIGVxdWF0aW9uIGNvZWZmaWNpZW50cyBvYnRhaW5lZCBmcm9tIHRoZSByZWd1bGFyaXplZCByZWdyZXNzaW9uIGVxdWF0aW9ucy4gIFRoZXNlIG1vZGVscyB3ZXJlIHRoZW4gY3Jvc3MgdmFsaWRhdGVkIGFuZCBhc3Nlc3NlZCBmb3IgdGhlIGJlc3QgZml0ICRcbGFtYmRhJCBoeXBlcnBhcmFtZXRlciAoTGFncmFuZ2UgbXVsdGlwbGllcikgYmVmb3JlIHVsdGltYXRlbHkgY29tcGFyaW5nIHRoZSBtb2RlbHMnIGVmZmljYWN5IGFuZCBhY2N1cmFjeS4gDQoNCiMjIyBDb2VmZmljaWVudCBQYXRoIEFuYWx5c2lzICYgzrsgU2VsZWN0aW9uIFRocm91Z2ggQ3Jvc3MgVmFsaWRhdGlvbg0KDQpUbyBkZXRlcm1pbmUgdGhlIGJlc3QgZml0IG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcyBmb3IgZWFjaCByZWdyZXNzaW9uIG1vZGVsLCAxMC1mb2xkIGNyb3NzIHZhbGlkYXRpb24gd2FzIHRob3VnaCA8Yj5jdi5nbG1uZXQoKTwvYj4gd2FzIGVtcGxveWVkIGZvciBlYWNoIG1vZGVsIHRvIGNhbGN1bGF0ZSB0aGUgcmFuZ2Ugb2YgYmVzdCBmaXQgbG9nKM67KSB2YWx1ZXMgZm9yIHVzZS4gIFRoZSBiZXN0IGZpdCB2YWx1ZSBhbHdheXMgbGllcyBiZXR3ZWVuIHRoZSBsb2cozrspIHRoYXQgcHJvdmlkZXMgbWluaW11bSBjcm9zcyB2YWxpZGF0ZWQgZXJyb3IgYW5kIHRoZSBsb2cozrspIHRoYXQgcmV0dXJucyBlcnJvciB3aXRoaW4gMSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWluaW11bS4gIEJvdGggdmFsdWVzIHdlcmUgcGxvdHRlZCBpbiBlYWNoIGNvZWZmaWNpZW50IHBhdGggYW5hbHlzaXMgYW5kIGNyb3NzIHZhbGlkYXRpb24gUk1TRSB2cyBsb2cozrspIHBsb3QgYmVsb3cgaW4gYEZpZ3VyZSAzYC4gVGhlIGxvZyjOuykgd2l0aGluIDEgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1pbmltdW0gKGxhbWJkYS4xc2UpIHdhcyBjaG9zZW4gdG8gaW1wcm92ZSBtb2RlbCB1c2FiaWxpdHkgYW5kIHJlZHVjZSB0aGUgb3ZlcmFsbCBudW1iZXIgb2YgZmVhdHVyZSB2YXJpYWJsZXMgaW5jbHVkZWQuIA0KDQpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMn0NCg0KbGFzc28uY3YgPC0gY3YuZ2xtbmV0KGFzLm1hdHJpeCh4LmxpbmVhci50cmFpbi5zdGQpLCB5LmxpbmVhci50cmFpbiwgYWxwaGEgPSAxLCBtYWluID0gIkxBU1NPIikNCnJpZGdlLmN2IDwtIGN2LmdsbW5ldChhcy5tYXRyaXgoeC5saW5lYXIudHJhaW4uc3RkKSwgeS5saW5lYXIudHJhaW4sIGFscGhhID0gMCwgbWFpbiA9ICJSaWRnZSIpDQplbmV0LmN2IDwtIGN2LmdsbW5ldChhcy5tYXRyaXgoeC5saW5lYXIudHJhaW4uc3RkKSwgeS5saW5lYXIudHJhaW4sIGFscGhhID0gMC41LCBtYWluID0gIkVsYXN0aWMgTmV0IikNCg0KDQpwYXIobWZyb3cgPSBjKDMsMiksIG9tYT1jKDYsMCwzLDApKQ0KbGFzc28uY29lZiA8LSBwbG90KGxhc3NvLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVCwgY29sID0gcmFpbmJvdygxNSksIHhsYWIgPSAiTG9nKM67KSIpICsNCiAgYWJsaW5lKHYgPSBsb2cobGFzc28uY3YkbGFtYmRhLm1pbiksIGx0eSA9IDQpICsgDQogIGFibGluZSh2ID0gbG9nKGxhc3NvLmN2JGxhbWJkYS4xc2UpLCBsdHkgPSA0LCBjb2wgPSAiZGFya3JlZCIpDQpsYXNzby5sYW0gPC0gcGxvdChsYXNzby5jdikNCg0KcmlkZ2UuY29lZiA8LSBwbG90KHJpZGdlLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVCwgY29sID0gcmFpbmJvdygxNSksIHhsYWIgPSAiTG9nKM67KSIpICsgDQogIGFibGluZSh2ID0gbG9nKHJpZGdlLmN2JGxhbWJkYS5taW4pLCBsdHkgPSA0KSArIA0KICBhYmxpbmUodiA9IGxvZyhyaWRnZS5jdiRsYW1iZGEuMXNlKSwgbHR5ID0gNCwgY29sID0gImRhcmtyZWQiKQ0KcmlkZ2UubGFtIDwtIHBsb3QocmlkZ2UuY3YpDQoNCmVuZXQuY29lZiA8LSBwbG90KGVuZXQsIHh2YXIgPSAibGFtYmRhIiwgbGFiZWwgPSBULCBjb2wgPSByYWluYm93KDE1KSwgeGxhYiA9ICJMb2cozrspIikgKyANCiAgYWJsaW5lKHYgPSBsb2coZW5ldC5jdiRsYW1iZGEubWluKSwgbHR5ID0gNCkgKyANCiAgYWJsaW5lKHYgPSBsb2coZW5ldC5jdiRsYW1iZGEuMXNlKSwgbHR5ID0gNCwgY29sID0gImRhcmtyZWQiKQ0KZW5ldC5sYW0gPC0gcGxvdChlbmV0LmN2KQ0KDQptdGV4dCgiQ29lZmZpY2llbnQgUGF0aCBBbmFseXNpcyAmIFJTTUUgzrsgVHVuaW5nIiwgbGluZSA9IDEsIHNpZGUgPSAzLCBvdXRlciA9IFQsIGNleCA9IDEuNSwgZm9udCA9IDIpDQptdGV4dCgiTEFTU08iLCBsaW5lID0gLTEuNSwgc2lkZSA9IDMsIG91dGVyID0gVCwgZm9udCA9IDQpDQptdGV4dCgiUmlkZ2UiLCBsaW5lID0gLTI4LjUsIHNpZGUgPSAzLCBvdXRlciA9IFQsIGZvbnQgPSA0KQ0KbXRleHQoIkVsYXN0aWMgTmV0IiwgbGluZSA9IC01NS41LCBzaWRlID0gMywgb3V0ZXIgPSBULCBmb250ID0gNCkNCm10ZXh0KCJGaWd1cmUgMzoNCkNvZWZmaWNpZW50IHBhdGggYW5hbHlzaXMgJiBSb290IE1lYW4gU3F1YXJlIEVycm9yIChSTVNFKSBhbmFseXNpcyBmb3IgzrsgdHVuaW5nIG9mIGVhY2ggcmVndWxhcml6ZWQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwuDQpCbGFjayBkb3R0ZWQgbGluZXMgcmVwcmVzZW50IHRoZSDOuyBtaW4gJiByZWQgZG90dGVkIGxpbmVzIGNhcHR1cmUgzrsgMVNFIHZhbHVlcy4NClRvcCBob3Jpem9uYWwgYXhpcyBpbmRpY2F0ZXMgdGhlIG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcyBwcmVzZW50IHdpdGggYXNzb2NpYXRlZCBsb2cozrspIiwgbGluZSA9IDMsIHNpZGUgPSAxLCBvdXRlciA9IFQsIGNleCA9IDAuOCwgd2VpZ2h0ID0gMSkNCg0KYGBgDQoNClRoZXNlIHZpc3VhbGl6ZWQgY3V0IHBvaW50cyAobG9nKM67KSkgZnJvbSBhYm92ZSBgRmlndXJlIDNgIGFyZSBzdW1tYXJpemVkIGJlbG93IGluIGBUYWJsZSA1YCwgYWxvbmcgd2l0aCByYXcgzrsgdmFsdWVzLiBUaGUgzrsgdmFsdWUgdGhhdCByZXRhaW5zIGFsbCBlcnJvciB3aXRoaW4gb25lIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBtaW5pbXVtIHZhbHVlIChzZTFfzrspIHdhcyBjaG9zZW4gZm9yIHV0aWxpemF0aW9uIGluIGZpbmFsIG1vZGVsIGNyZWF0aW9uIHRvIGFsbG93IGZvciBhIG1vcmUgZmxleGlibGUgbW9kZWwgd2l0aCBhbnkgbmV3IHBvdGVudGlhbCBkYXRhLiAgVGhpcyB3aWxsIHN1cHBvcnQgdGhlIHdpbmUgY2xhc3NpZmljYXRpb24gbW9kZWwgaW4gcmV0YWluaW5nIGFkYXB0aXZpdHkgdG8gbmV3IGluY29taW5nIGRhdGEuICBUaGlzIHdpbGwgcmVkdWNlIHRoZSBuZWVkIGZvciBtb3JlIGZyZXF1ZW50IG1vZGVsIHJlYnVpbGRzIGFuZCByZS10dW5lcy4gDQoNCg0KYGBge3J9DQpjb2VmLmxpbiA8LSBkYXRhLmZyYW1lKA0KICBsaW4uZ2FtbWEgPSBjKCJNaW5fzrsiLCAic2UxX867IiwgIk1pbl9sb2fOuyIsICJzZTFfbG9nzrsiKSwNCiAgbGFzc28gPSBjKGxhc3NvLmN2JGxhbWJkYS5taW4sIGxhc3NvLmN2JGxhbWJkYS4xc2UsIGxvZyhsYXNzby5jdiRsYW1iZGEubWluKSwgbG9nKGxhc3NvLmN2JGxhbWJkYS4xc2UpKSwNCiAgcmlkZ2UgPSBjKHJpZGdlLmN2JGxhbWJkYS5taW4sIHJpZGdlLmN2JGxhbWJkYS4xc2UsIGxvZyhyaWRnZS5jdiRsYW1iZGEubWluKSwgbG9nKHJpZGdlLmN2JGxhbWJkYS4xc2UpKSwNCiAgZW5ldCA9IGMoZW5ldC5jdiRsYW1iZGEubWluLCBlbmV0LmN2JGxhbWJkYS4xc2UsIGxvZyhlbmV0LmN2JGxhbWJkYS5taW4pLCBsb2coZW5ldC5jdiRsYW1iZGEuMXNlKSkNCiAgKQ0KDQprYWJsZShjb2VmLmxpbiwgY29sLm5hbWVzID0gYygizrsgVmFsdWUgVHlwZSIsICJMQVNTTyIsICJSaWRnZSIsICJFbGFzdGljIE5ldCIpLCBjYXB0aW9uID0gIlRhYmxlIDU6DQogICAgICA8YnI+Q3Jvc3MgdmFsaWRhdGVkIM67ICYgbG9nKM67KSB2YWx1ZXMgZm9yIGVhY2ggcmVndWxhcml6ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4gTWluX867ICYgc2UxX867IHJlcHJlc2VudCB0aGUgzrsgdmFsdWUgd2l0aCBtaW5pbWFsIGNyb3NzIHZhbGlkYXRlZCBlcnJvciAmIM67IHZhbHVlIHdpdGggZXJyb3Igd2l0aGluIDEgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1pbmltdW0gcmVzcGVjdGl2ZWx5LiAgTWluX2xvZ867ICYgc2UxX2xvZ867IGFyZSB0aGUgbG9nKCkgb2YgTWluX867ICYgc2UxX867IHJlc3BlY3RpdmVseS4iLCBhbGlnbiA9IGMoImwiLCAiYyIsICJjIiwgImMiKSkgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KIyMgTW9kZWwgU2VsZWN0aW9uDQoNCldpdGggdGhlIGJlc3QgZml0IGxvZyjOuykgY2hvc2VuIGFzIHRoZSBsYW1iZGEuMXNlIHZhbHVlcywgdGhlIHJlZ3Jlc3Npb24gbW9kZWxzIHdlcmUgcmUtcnVuIG9uIHRoZSB0ZXN0IGRhdGEgd2l0aCBhIGZpeGVkIGxvZyjOuykgdmFsdWUgdG8gYXNzZXNzIHRoZSBtb2RlbCBwZXJmb3JtYW5jZS4gIEFzIGlsbHVzdHJhdGVkIGJlbG93IGluIGBUYWJsZSA2YCB0aGUgZWxhc3RpYyBuZXQgbW9kZWwgcHJvZHVjZWQgdGhlIGxvd2VzdCBSTVNFIGFuZCB0aGUgbG93ZXN0IFJNU0UgJSBvZiB0aGUgcmFuZ2Ugb2YgeSB0ZXN0IHZhbHVlcyBmb3IgcXVhbGl0eSBpbmRpY2F0aW5nIHRoaXMgaXMgdGhlIGJlc3QgZml0IG1vZGVsLiAgV2hpbGUgYWxsIG1vZGVscycgUk1TRSB2YWx1ZXMgd2VyZSB3aXRoaW4gMTAtMjAlIG9mIHRoZSByYW5nZSBvZiB0aGUgeSB0ZXN0IHZhbHVlcyBmb3IgcXVhbGl0eSwgY2hvb3NpbmcgdGhlIGxvd2VzdCBSTVNFIGFuZCBSTVNFICUgb2YgcmFuZ2Ugb2YgeSB0ZXN0IHdpbGwgcmVzdWx0IGluIHRoZSBtb3N0IGFjY3VyYXRlIHByZWRpY3Rpb24gbW9kZWxzLiAgIA0KDQpgYGB7cn0NCiNtb2RlbCBwcmVkaWN0IHRlc3QgZGF0YSB1c2luZyBtb2RlbCBjb2VmZmljaWVudHMgJiBsYW1iZGEuMXNlIGZyb20gQ1YgdHVuaW5nICYgUk1TRSBjYWxjdWxhdGlvbg0KI2xhc3NvDQpsYXNzby5tb2RlbCA8LSBnbG1uZXQoeC5saW5lYXIudHJhaW4uc3RkLCB5LmxpbmVhci50cmFpbiwgYWxwaGEgPSAxLCBsYW1iZGEgPSBsYXNzby5jdiRsYW1iZGEuMXNlKQ0KbGFzc28ucHJlZCA8LSByb3VuZChwcmVkaWN0KGxhc3NvLm1vZGVsLCBzID0gbGFzc28uY3YkbGFtYmRhLjFzZSwgbmV3eCA9IGFzLm1hdHJpeCh4LmxpbmVhci50ZXN0LnN0ZCkpLDApDQpsYXNzby5ybXNlIDwtIHJtc2UoeS5saW5lYXIudGVzdCwgbGFzc28ucHJlZCkNCmxhc3NvLnJtc2Uubm9ybSA8LSByb3VuZCgobGFzc28ucm1zZS8obWF4KHkubGluZWFyLnRlc3QpLW1pbih5LmxpbmVhci50ZXN0KSkpKjEwMCwyKQ0KDQojcmlkZ2UNCnJpZGdlLm1vZGVsIDwtIGdsbW5ldCh4LmxpbmVhci50cmFpbi5zdGQsIHkubGluZWFyLnRyYWluLCBhbHBoYSA9IDAsIGxhbWJkYSA9IHJpZGdlLmN2JGxhbWJkYS4xc2UpDQpyaWRnZS5wcmVkIDwtIHJvdW5kKHByZWRpY3QocmlkZ2UubW9kZWwsIHMgPSByaWRnZS5jdiRsYW1iZGEuMXNlLCBuZXd4ID0gYXMubWF0cml4KHgubGluZWFyLnRlc3Quc3RkKSksMCkNCnJpZGdlLnJtc2UgPC0gcm1zZSh5LmxpbmVhci50ZXN0LCByaWRnZS5wcmVkKQ0KcmlkZ2Uucm1zZS5ub3JtIDwtIHJvdW5kKChyaWRnZS5ybXNlLyhtYXgoeS5saW5lYXIudGVzdCktbWluKHkubGluZWFyLnRlc3QpKSkqMTAwLDIpDQoNCiNlbGFzdGljIG5ldA0KZW5ldC5tb2RlbCA8LSBnbG1uZXQoeC5saW5lYXIudHJhaW4uc3RkLCB5LmxpbmVhci50cmFpbiwgYWxwaGEgPSAwLjUsIGxhbWJkYSA9IGVuZXQuY3YkbGFtYmRhLjFzZSkNCmVuZXQucHJlZCA8LSByb3VuZChwcmVkaWN0KGVuZXQubW9kZWwsIHMgPSBlbmV0LmN2JGxhbWJkYS4xc2UsIG5ld3ggPSBhcy5tYXRyaXgoeC5saW5lYXIudGVzdC5zdGQpKSwwKQ0KZW5ldC5ybXNlIDwtIHJtc2UoeS5saW5lYXIudGVzdCwgZW5ldC5wcmVkKQ0KZW5ldC5ybXNlLm5vcm0gPC0gcm91bmQoKGVuZXQucm1zZS8obWF4KHkubGluZWFyLnRlc3QpLW1pbih5LmxpbmVhci50ZXN0KSkpKjEwMCwyKQ0KDQojam9pbiBSTVNFIHZhbHMgZm9yIGthYmxlIGRpc3BsYXkNClJNU0UgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJMQVNTTyIsICJSaWRnZSIsICJFbGFzdGljTmV0IiksDQogIFJNU0UgPSBjKGxhc3NvLnJtc2UsIHJpZGdlLnJtc2UsIGVuZXQucm1zZSksDQogIFJNU0Uubm9ybSA9IGMobGFzc28ucm1zZS5ub3JtLCByaWRnZS5ybXNlLm5vcm0sIGVuZXQucm1zZS5ub3JtKSwNCiAgZXZhbCA9IGMoIkdvb2QiLCAiR29vZCIsICJHb29kIikgI21hbnVhbGx5IGNhbGN1bGF0ZWQgZm9yIHRoaXMgYnV0IGNhbiBiZSBzZW50IHRocm91Z2ggZm9yIGxvb3AgdG8gYXV0byBjYWxjDQopDQoNCmthYmxlKFJNU0UsIGFsaWduID0gYygibCIsImMiLCJjIiwiYyIpLCBjb2wubmFtZXMgPSBjKCJNb2RlbCIsICJSTVNFIiwgIlJNU0UgJSBvZiBSYW5nZSBvZiBUZXN0IFF1YWxpdHkgVmFsdWVzIiwgIkludGVwcmV0YXRpb24iKSwgY2FwdGlvbiA9ICJUYWJsZSA2Og0KICAgICAgPGJyPlJvb3QgTWVhbiBTcXVhcmUgRXJyb3IgKFJNU0UpIHZhbHVlcywgY2FsY3VsYXRlZCBwZXJjZW50IG9mIHRhcmdldCByYW5nZSBjYXB0dXJlZCBieSBSTVNFLCBhbmQgdGhlIG1vZGVsIGV2YWx1YXRpb24gZm9yIHJlZ3VsYXJpemVkIGxpbmVhciByZWdyZXNzaW9uIG1vZGVscyB0byB1dGlsaXplIGZvciBtb2RlbCBzZWxlY3Rpb24uIikgJT4lIA0KICBrYWJsZV9zdHlsaW5nKCkNCmBgYA0KDQpPbmNlIHNlbGVjdGVkLCB0aGUgZWxhc3RpYyBuZXQgcmVncmVzc2lvbiBlcXVhdGlvbiBjb2VmZmljaWVudHMgd2VyZSBvYnRhaW5lZCAoYFRhYmxlIDdgIGJlbG93KSB0byBjcmVhdGUgdGhlIGZpbmFsIHJlZ3Jlc3Npb24gZXF1YXRpb24gYXMgcHJlc2VudGVkIGJlbG93Lg0KDQpgYGB7cn0NCiNleHRyYWN0aW5nIHJlZ3Jlc3Npb24gZXF1YXRpb24gY29lZmZpY2llbnRzDQplbmV0LmNvZWYgPC0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoY29lZihlbmV0LmN2LCBlbmV0LmN2JGxhbWJkYS4xc2UpKSkgJT4lIHN1YnNldChzMSAhPSAwKQ0KDQprYWJsZShlbmV0LmNvZWYsIGFsaWduID0gImMiLCBjb2wubmFtZXMgPSBjKCJWYXJpYWJsZSIsICJSZWdyZXNzaW9uIENvZWZmaWNpZW50IiksIGNhcHRpb24gPSAiVGFibGUgNzoNCiAgICAgIDxicj5SZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBmb3Igc2VsZWN0ZWQgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtb2RlbDogRWxhc3RpYyBOZXQiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KDQpgYGANCg0KJCQNClxiZWdpbnthbGlnbip9DQpcaGF0e3l9X3tFbGFzdGljIE5ldH0gPSAmNS44MTggLSAwLjIwMCAqIFxtYm94e1ZvbGF0aWxlIEFjaWRpdHl9ICsgMC4wMzkgKiBcbWJveHtSZXNpZHVhbCBTdWdhcn0gLSBcXA0KJjAuMDAzICogXG1ib3h7Q2hsb3JpZGVzfSArIDAuMzM2ICogXG1ib3h7QWxjb2hvbH0gKyAwLjA0NiAqIEhlYWRhY2hlICsgXFwNCiYwLjA3MCAqIFxtYm94e1N1bHBodXIgRGlveGlkZSBSYXRpb30NClxlbmR7YWxpZ24qfQ0KJCQNCg0KIyBSZWd1bGFyaXplZCBMb2dpc3RpYyBSZWdyZXNzaW9uDQoNCkluIG9yZGVyIHRvIHByZWRpY3QgdGhlIHR5cGUgKHJlZC93aGl0ZSkgb2Ygd2luZSBiYXNlZCBvZmYgb2YgdGhlIGNoZW1pY2FsIHByb3BlcnRpZXMgb2YgYSBzYW1wbGUsIG11bHRpcGxlIHJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIHdlcmUgY3JlYXRlZCB3aXRoIHR5cGUgYXMgdGhlIHJlc3BvbnNlIHZhcmlhYmxlLiAgQmVmb3JlIGFueSBwcmVkaWN0aW9uIG1vZGVscyB3ZXJlIGNyZWF0ZWQsIHRoZSBkYXRhIHdhcyBzcGxpdCBpbnRvIDgwOjIwIHRyYWluOnRlc3QgZ3JvdXBzIHRvIHJlZHVjZSB0aGUgcG90ZW50aWFsIGZvciBvdmVyLWZpdHRpbmcgYW5kIHRvIGFsbG93IGZvciBpbXByb3ZlZCBoeXBlcnBhcmFtZXRlciB0dW5pbmcuIEFkZGl0aW9uYWxseSwgb25jZSB0aGUgZGF0YSB3YXMgc3BsaXQgaW50byB0cmFpbmluZyBhbmQgdGVzdCBncm91cHMsIHRoZSBmZWF0dXJlIHZhcmlhYmxlcyB3ZXJlIHN0YW5kYXJkaXplZCB0byBlbnN1cmUgYSBjb21wYXJhYmxlIHNjYWxlIGJldHdlZW4gdmFyaWFibGVzIGFuZCByZW1vdmUgcG90ZW50aWFsIG92ZXItd2VpZ2h0aW5nIG9mIHZhcmlhYmxlcy4gDQoNCmBgYHtyfQ0KI3JlZ3Jlc3Npb24gcHJlcCB3b3JrDQojc2V0IHNlZWQNCnNldC5zZWVkKDEyOTM1KQ0KDQojc3BsaXQgZmVhdHVyZSBhbmQgcmVzcG9uc2UgdmFyaWFibGVzDQp4LmxvZyA8LSB3aW5lWy1jKDEzOjE0KV0NCnkubG9nIDwtIHdpbmUkdHlwZQ0KDQoNCiNkYXRhIHNwbGl0IGZvciBDViAoODA6MjApDQpzcGxpdC5sb2cgPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih5LmxpbmVhciwgcD0wLjgsIGxpc3Q9RikNCg0KI3RyYWluaW5nIGRhdGENCngubG9nLnRyYWluIDwtIHgubG9nW3NwbGl0LmxvZyxdDQp5LmxvZy50cmFpbiA8LSB5LmxvZ1tzcGxpdC5sb2ddDQoNCiN0ZXN0IGRhdGENCngubG9nLnRlc3QgPC0geC5sb2dbLXNwbGl0LmxvZyxdDQp5LmxvZy50ZXN0IDwtIHkubG9nWy1zcGxpdC5sb2ddDQoNCiNzdGFuZGFyZGl6ZSB2YWx1ZXMNCnN0ZC5sb2cgPC0gcHJlUHJvY2Vzcyh4LmxvZy50cmFpbiwgbWV0aG9kID0gYygiY2VudGVyIiwgInNjYWxlIikpDQp4LmxvZy50cmFpbi5zdGQgPC0gcHJlZGljdChzdGQubG9nLCB4LmxvZy50cmFpbikgDQp4LmxvZy50ZXN0LnN0ZCA8LSBwcmVkaWN0KHN0ZC5sb2csIHgubG9nLnRlc3QpIA0KDQpgYGANCg0KIyMgTG9naXN0aWMgUmVncmVzc2lvbiBNb2RlbCBCdWlsZHMNCg0KUmVndWxhcml6ZWQgcmVncmVzc2lvbiB3YXMgZW1wbG95ZWQgdG8gaW50cm9kdWNlIGNvbnN0cmFpbnRzIG9uIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyBhbmQgc3Vic2VxdWVudGx5IHJlZHVjZSBvdmVyLWZpdHRpbmcgcmlzay4gIFRoZSBsb3NzIGZ1bmN0aW9uIHdhcyBlbXBsb3llZCB0aHJvdWdoIHRoZSA8Yj5nbG1uZXQ8L2I+IHBhY2thZ2UgdG8gZW5zdXJlIGFwcHJvcHJpYXRlIHJlZ3VsYXJpemVkIHJlZ3Jlc3Npb24gd2l0aCB2YXJpb3VzIGFwcHJvYWNoZXMgKExBU1NPLCBSaWRnZSwgJiBFbGFzdGljIE5ldCksIHV0aWxpemluZyB0aGUgYmVsb3cgZXF1YXRpb25zIHRvIGNvbXBsZXRlIHRoZSBtb2RlbCBjcmVhdGlvbi4gIFRoZSBMQVNTTyBtb2RlbCBhdXRvbWF0aWNhbGx5IHBlbmFsaXplcyB2YXJpYWJsZXMgdWx0aW1hdGVseSByZXN1bHRpbmcgaW4gZmVhdHVyZSBzZWxlY3Rpb24gYW5kIGEgcmVkdWNlZCBtb2RlbCBmb3IgdXNlIHdoaWxlIHRoZSBSaWRnZSBtb2RlbCBwZW5hbGl6ZXMgdmFyaWFibGVzIGJ5IHJlZHVjaW5nIGltcGFjdCB3aXRob3V0IGZlYXR1cmUgc2VsZWN0aW9uLiBNZWFud2hpbGUgdGhlIEVsYXN0aWMgTmV0IG1vZGVsIGNvbWJpbmVzIHRoZXNlIGFwcHJvYWNoZXMgYW5kIHJlc3VsdHMgaW4gcGVuYWxpemVkIHZhcmlhYmxlcyB3aXRoIHJlZHVjZWQgaW1wYWN0IGFuZCBtb3JlIGxpbWl0ZWQgZmVhdHVyZSBzZWxlY3Rpb24uIA0KDQpgYGB7cn0NCmxhc3NvLmxvZyA8LSBnbG1uZXQoeC5sb2cudHJhaW4uc3RkLCB5LmxvZy50cmFpbiwgYWxwaGEgPSAxLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0KcmlkZ2UubG9nIDwtIGdsbW5ldCh4LmxvZy50cmFpbi5zdGQsIHkubG9nLnRyYWluLCBhbHBoYSA9IDAsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQplbmV0LmxvZyA8LSBnbG1uZXQoeC5sb2cudHJhaW4uc3RkLCB5LmxvZy50cmFpbiwgYWxwaGEgPSAwLjUsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQpgYGANCg0KDQokJA0KXGJlZ2lue2FsaWduKn0NClx1bmRlcmxpbmV7XG1ib3h7TE9TUyBGdW5jdGlvbiBFcXVhdGlvbnN9fSBcXCBcXA0KTE9TU197bG9naXN0aWN9ID0gXCAtXGZyYWN7MX17bn0gXHN1bV97aT0xfV5uIFwgW1wgeV9pXGxvZyhcaGF0e3B9X2kpICsgKDEteV97aX0pXGxvZygxLVxoYXQgcF9pKVwgXSBcXA0KTE9TU197cmVndWxhcml6ZWR9ID0gXCBMT1NTX3tsb2dpc3RpY30gKyAgXGxhbWJkYSAgXGxlZnRbIFxmcmFjezEtXGFscGhhfXsyfSBcfCBcYmV0YSBcfF97Mn1eMiArIFxhbHBoYSBcfCBcYmV0YSBcfF8xXHJpZ2h0XSBcIFwgJlxtYm94e3doZXJlfSBcIFwgIFxsZWZ0XHsgXGJlZ2lue2FycmF5fXtybH0gXGFscGhhID0gMTogJlxtYm94e0xBU1NPfSBcXCBcYWxwaGEgPSAwOiAmXG1ib3h7UmlkZ2V9IFxcIDAgXGxlcSBhIFxsZXEgMTogJlxtYm94e0VsYXN0aWMgTmV0fSBcZW5ke2FycmF5fVxyaWdodC4gXFwNClxlbmR7YWxpZ24qfQ0KJCQNCg0KJCQNClx1bmRlcmxpbmV7XG1ib3h7UmVndWxhcml6ZWQgUmVncmVzc2lvbiBFcXVhdGlvbnN9fVxcDQpcYmVnaW57YXJyYXl9e2x9IFxcDQpMMV97TEFTU099ICY9IFwgTE9TU197cmVndWFyaXplZH0gJisgXCBcIFxnYW1tYSBcc3VtX3tqPTF9XmsgfFxiZXRhX2p8IFwgXCBcXA0KTDJfe3JpZGdlfSAmPSBcIExPU1Nfe3JlZ3VsYXJpemVkfSAmKyBcIFwgXGdhbW1hIFxzdW1fe2o9MX1eayB8XGJldGFfanxeMiBcIFwgIFxcDQpMM197RWxhc3RpY30gJj0gTE9TU197cmVndWxhdGlzZWR9ICYrICBcIFwgXGdhbW1hIFxzdW1fe2o9MX1eayB8XGJldGFfanwgKyBcIFwgXGdhbW1hIFxzdW1fe2o9MX1eayB8XGJldGFfanxeMiBcIA0KXGVuZHthcnJheX0gXCAgXG1ib3h7d2hlcmV9IFwgXCBcICBcbGVmdFx7IFxiZWdpbnthcnJheX17cmx9IFxiZXRhID0gJlxtYm94e1JlZ3Jlc3Npb24gQ29lZmYufSBcXCBrID0gJlxtYm94eyMgb2YgRmVhdHVyZSBWYXJzLn1cXCBcZ2FtbWEgPSAmXG1ib3h7SHlwZXJwYXJhbWV0ZXJ9IFxlbmR7YXJyYXl9IFxyaWdodC4NCiQkDQo8YnI+DQoNCkh5cGVycGFyYW1ldGVyICRcZ2FtbWEkIHZhbHVlcyB3ZXJlIHN1YnNlcXVlbnRseSB0dW5lZCB0aHJvdWdoIGNyb3NzIHZhbGlkYXRpb24gYXMgcGFydCBvZiB0aGUgbW9kZWwgc2VsZWN0aW9uIHByb2Nlc3MuIE1vcmUgaW5mb3JtYXRpb24gb24gJFxnYW1tYSQgdmFsdWUgc2VsZWN0aW9uIGlzIGluY2x1ZGVkIGJlbG93IGluIHNlY3Rpb24gPGI+NC4yLjEgQ29lZmZpY2llbnQgUGF0aCBBbmFseXNpcyAmIENyb3NzIFZhbGlkYXRpb248L2I+DQoNCiMjIExvZ2lzdGljIE1vZGVsIEFuYWx5c2lzICYgUGFyYW1ldGVyIFZhbHVlIFNlbGVjdGlvbg0KDQpSZWdyZXNzaW9uIG1vZGVscyB3ZXJlIGNyZWF0ZWQgdXRpbGl6aW5nIHRoZSBwcmVkaWN0ZWQgcmVncmVzc2lvbiBlcXVhdGlvbiBjb2VmZmljaWVudHMgb2J0YWluZWQgZnJvbSB0aGUgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBlcXVhdGlvbnMuICBUaGVzZSBtb2RlbHMgd2VyZSB0aGVuIGNyb3NzIHZhbGlkYXRlZCBhbmQgYXNzZXNzZWQgZm9yIHRoZSBiZXN0IGZpdCAkXGdhbW1hJCBoeXBlcnBhcmFtZXRlciAoTGFncmFuZ2UgbXVsdGlwbGllcikgYmVmb3JlIHVsdGltYXRlbHkgY29tcGFyaW5nIHRoZSBtb2RlbHMnIGVmZmljYWN5IGFuZCBhY2N1cmFjeS4gDQoNCiMjIyBDb2VmZmljaWVudCBQYXRoIEFuYWx5c2lzICYgzrMgU2VsZWN0aW9uIFRocm91Z2ggQ3Jvc3MgVmFsaWRhdGlvbg0KDQpUbyBkZXRlcm1pbmUgdGhlIGJlc3QgZml0IG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcyBmb3IgZWFjaCByZWdyZXNzaW9uIG1vZGVsLCAxMC1mb2xkIGNyb3NzIHZhbGlkYXRpb24gd2FzIHRob3VnaCA8Yj5jdi5nbG1uZXQoKTwvYj4gd2FzIGVtcGxveWVkIGZvciBlYWNoIG1vZGVsIHRvIGNhbGN1bGF0ZSB0aGUgcmFuZ2Ugb2YgYmVzdCBmaXQgbG9nKCRcZ2FtbWEkKSB2YWx1ZXMgZm9yIHVzZS4gIFRoZSBiZXN0IGZpdCB2YWx1ZSBhbHdheXMgbGllcyBiZXR3ZWVuIHRoZSBsb2coJFxnYW1tYSQpIHRoYXQgcHJvdmlkZXMgbWluaW11bSBjcm9zcyB2YWxpZGF0ZWQgZXJyb3IgYW5kIHRoZSBsb2coJFxnYW1tYSQpIHRoYXQgcmV0dXJucyBlcnJvciB3aXRoaW4gMSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWluaW11bS4gIEJvdGggdmFsdWVzIHdlcmUgcGxvdHRlZCBpbiBlYWNoIGNvZWZmaWNpZW50IHBhdGggYW5hbHlzaXMgYW5kIGNyb3NzIHZhbGlkYXRpb24gUk1TRSB2cyBsb2coJFxnYW1tYSQpIHBsb3QgYmVsb3cgaW4gYEZpZ3VyZSA0YC4gVGhlIGxvZygkXGdhbW1hJCkgd2l0aGluIDEgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1pbmltdW0gKGdhbW1hLjFzZSkgd2FzIGNob3NlbiB0byBpbXByb3ZlIG1vZGVsIHVzYWJpbGl0eSBhbmQgcmVkdWNlIHRoZSBvdmVyYWxsIG51bWJlciBvZiBmZWF0dXJlIHZhcmlhYmxlcyBpbmNsdWRlZC4gDQoNCmBgYHtyfQ0KDQpsYXNzby5sb2cuY3YgPC0gY3YuZ2xtbmV0KGFzLm1hdHJpeCh4LmxvZy50cmFpbi5zdGQpLCB5LmxvZy50cmFpbiwgYWxwaGEgPSAxLCBmYW1pbHkgPSAiYmlub21pYWwiLCBtYWluID0gIkxBU1NPIikNCnJpZGdlLmxvZy5jdiA8LSBjdi5nbG1uZXQoYXMubWF0cml4KHgubG9nLnRyYWluLnN0ZCksIHkubG9nLnRyYWluLCBhbHBoYSA9IDAsIGZhbWlseSA9ICJiaW5vbWlhbCIsIG1haW4gPSAiUmlkZ2UiKQ0KZW5ldC5sb2cuY3YgPC0gY3YuZ2xtbmV0KGFzLm1hdHJpeCh4LmxvZy50cmFpbi5zdGQpLCB5LmxvZy50cmFpbiwgYWxwaGEgPSAwLjUsIGZhbWlseSA9ICJiaW5vbWlhbCIsIG1haW4gPSAiRWxhc3RpYyBOZXQiKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEyfQ0KcGFyKG1mcm93ID0gYygzLDIpLCBvbWE9Yyg2LDAsMywwKSkNCmxhc3NvLmxvZy5jb2VmIDwtIHBsb3QobGFzc28ubG9nLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVCwgY29sID0gcmFpbmJvdygxNSksIHhsYWIgPSAiTG9nKM6zKSIpICsNCiAgYWJsaW5lKHYgPSBsb2cobGFzc28ubG9nLmN2JGxhbWJkYS5taW4pLCBsdHkgPSA0KSArIA0KICBhYmxpbmUodiA9IGxvZyhsYXNzby5sb2cuY3YkbGFtYmRhLjFzZSksIGx0eSA9IDQsIGNvbCA9ICJkYXJrcmVkIikNCmxhc3NvLmxvZy5sYW0gPC0gcGxvdChsYXNzby5sb2cuY3YsIHhsYWIgPSAiTG9nKM6zKSIpDQoNCnJpZGdlLmxvZy5jb2VmIDwtIHBsb3QocmlkZ2UubG9nLCB4dmFyID0gImxhbWJkYSIsIGxhYmVsID0gVCwgY29sID0gcmFpbmJvdygxNSksIHhsYWIgPSAiTG9nKM6zKSIpICsgDQogIGFibGluZSh2ID0gbG9nKHJpZGdlLmxvZy5jdiRsYW1iZGEubWluKSwgbHR5ID0gNCkgKyANCiAgYWJsaW5lKHYgPSBsb2cocmlkZ2UubG9nLmN2JGxhbWJkYS4xc2UpLCBsdHkgPSA0LCBjb2wgPSAiZGFya3JlZCIpDQpyaWRnZS5sb2cubGFtIDwtIHBsb3QocmlkZ2UubG9nLmN2LCB4bGFiID0gIkxvZyjOsykiKQ0KDQplbmV0LmxvZy5jb2VmIDwtIHBsb3QoZW5ldC5sb2csIHh2YXIgPSAibGFtYmRhIiwgbGFiZWwgPSBULCBjb2wgPSByYWluYm93KDE1KSwgeGxhYiA9ICJMb2cozrMpIikgKyANCiAgYWJsaW5lKHYgPSBsb2coZW5ldC5sb2cuY3YkbGFtYmRhLm1pbiksIGx0eSA9IDQpICsgDQogIGFibGluZSh2ID0gbG9nKGVuZXQubG9nLmN2JGxhbWJkYS4xc2UpLCBsdHkgPSA0LCBjb2wgPSAiZGFya3JlZCIpDQplbmV0LmxvZy5sYW0gPC0gcGxvdChlbmV0LmxvZy5jdiwgeGxhYiA9ICJMb2cozrMpIikNCg0KbXRleHQoIkNvZWZmaWNpZW50IFBhdGggQW5hbHlzaXMgJiBNb2RlbCBGaXQgzrMgVHVuaW5nIiwgbGluZSA9IDEsIHNpZGUgPSAzLCBvdXRlciA9IFQsIGNleCA9IDEuNSwgZm9udCA9IDIpDQptdGV4dCgiTEFTU08iLCBsaW5lID0gLTEuNSwgc2lkZSA9IDMsIG91dGVyID0gVCwgZm9udCA9IDQpDQptdGV4dCgiUmlkZ2UiLCBsaW5lID0gLTI4LjUsIHNpZGUgPSAzLCBvdXRlciA9IFQsIGZvbnQgPSA0KQ0KbXRleHQoIkVsYXN0aWMgTmV0IiwgbGluZSA9IC01NS41LCBzaWRlID0gMywgb3V0ZXIgPSBULCBmb250ID0gNCkNCm10ZXh0KCJGaWd1cmUgNDoNCkNvZWZmaWNpZW50IHBhdGggYW5hbHlzaXMgJiBiaW5vbWlhbCBkZXZpYW5jZSBhbmFseXNpcyBmb3IgzrMgdHVuaW5nIG9mIGVhY2ggcmVndWxhcml6ZWQgbG9nYXJpdGhtaWMgcmVncmVzc2lvbiBtb2RlbC4NCkJsYWNrIGRvdHRlZCBsaW5lcyByZXByZXNlbnQgdGhlIM6zIG1pbiAmIHJlZCBkb3R0ZWQgbGluZXMgY2FwdHVyZSDOsyAxU0UgdmFsdWVzLg0KVG9wIGhvcml6b25hbCBheGlzIGluZGljYXRlcyB0aGUgbnVtYmVyIG9mIGZlYXR1cmUgdmFyaWFibGVzIHByZXNlbnQgd2l0aCBhc3NvY2lhdGVkIGxvZyjOsykiLCBsaW5lID0gMywgc2lkZSA9IDEsIG91dGVyID0gVCwgY2V4ID0gMC44LCB3ZWlnaHQgPSAxKQ0KDQpgYGANCg0KVGhlc2UgdmlzdWFsaXplZCBjdXQgcG9pbnRzIChsb2cozrMpKSBmcm9tIGFib3ZlIGBGaWd1cmUgNGAgYXJlIHN1bW1hcml6ZWQgYmVsb3cgaW4gYFRhYmxlIDhgLCBhbG9uZyB3aXRoIHJhdyDOsyB2YWx1ZXMuIFRoZSDOsyB2YWx1ZSB0aGF0IHJldGFpbnMgYWxsIGVycm9yIHdpdGhpbiBvbmUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIG1pbmltdW0gdmFsdWUgKHNlMV/Osykgd2FzIGNob3NlbiBmb3IgdXRpbGl6YXRpb24gaW4gZmluYWwgbW9kZWwgY3JlYXRpb24gdG8gYWxsb3cgZm9yIGEgbW9yZSBmbGV4aWJsZSBtb2RlbCB3aXRoIGFueSBuZXcgcG90ZW50aWFsIGRhdGEuICBUaGlzIHdpbGwgc3VwcG9ydCB0aGUgd2luZSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBpbiByZXRhaW5pbmcgYWRhcHRpdml0eSB0byBuZXcgaW5jb21pbmcgZGF0YS4gIFRoaXMgd2lsbCByZWR1Y2UgdGhlIG5lZWQgZm9yIG1vcmUgZnJlcXVlbnQgbW9kZWwgcmVidWlsZHMgYW5kIHJlLXR1bmVzLiANCg0KDQpgYGB7cn0NCmNvZWYubG9nIDwtIGRhdGEuZnJhbWUoDQogIGxvZy5nYW1tYSA9IGMoIk1pbl/OsyIsICJzZTFfzrMiLCAiTWluX2xvZ86zIiwgInNlMV9sb2fOsyIpLA0KICBsYXNzbyA9IGMobGFzc28ubG9nLmN2JGxhbWJkYS5taW4sIGxhc3NvLmxvZy5jdiRsYW1iZGEuMXNlLCBsb2cobGFzc28ubG9nLmN2JGxhbWJkYS5taW4pLCBsb2cobGFzc28ubG9nLmN2JGxhbWJkYS4xc2UpKSwNCiAgcmlkZ2UgPSBjKHJpZGdlLmxvZy5jdiRsYW1iZGEubWluLCByaWRnZS5sb2cuY3YkbGFtYmRhLjFzZSwgbG9nKHJpZGdlLmxvZy5jdiRsYW1iZGEubWluKSwgbG9nKHJpZGdlLmxvZy5jdiRsYW1iZGEuMXNlKSksDQogIGVuZXQgPSBjKGVuZXQubG9nLmN2JGxhbWJkYS5taW4sIGVuZXQubG9nLmN2JGxhbWJkYS4xc2UsIGxvZyhlbmV0LmxvZy5jdiRsYW1iZGEubWluKSwgbG9nKGVuZXQubG9nLmN2JGxhbWJkYS4xc2UpKQ0KICApDQoNCmthYmxlKGNvZWYubG9nLCBjb2wubmFtZXMgPSBjKCLOsyBWYWx1ZSBUeXBlIiwgIkxBU1NPIiwgIlJpZGdlIiwgIkVsYXN0aWMgTmV0IiksYWxpZ24gPSBjKCJsIiwgImMiLCAiYyIsICJjIiksIGNhcHRpb24gPSAiVGFibGUgODoNCiAgICAgIDxicj5Dcm9zcyB2YWxpZGF0ZWQgzrMgJiBsb2cozrMpIHZhbHVlcyBmb3IgZWFjaCByZWd1bGFyaXplZCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLiBNaW5fzrMgJiBzZTFfzrMgcmVwcmVzZW50IHRoZSDOsyB2YWx1ZSB3aXRoIG1pbmltYWwgY3Jvc3MgdmFsaWRhdGVkIGVycm9yICYgzrMgdmFsdWUgd2l0aCBlcnJvciB3aXRoaW4gMSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgbWluaW11bSByZXNwZWN0aXZlbHkuICBNaW5fbG9nzrMgJiBzZTFfbG9nzrMgYXJlIHRoZSBsb2coKSBvZiBNaW5fzrMgJiBzZTFfzrMgcmVzcGVjdGl2ZWx5LiIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KIyMjIEN1dC1vZmYgUHJvYmFiaWxpdHkgVmFsdWUgU2VsZWN0aW9uDQoNClRvIHByb3Blcmx5IGZpbmUgdHVuZSB0aGUgcmVndWxhcml6ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsLCB0aGUgb3B0aW1hbCBwcm9iYWJpbGl0eSBjdXRvZmYgbXVzdCBiZSBzZWxlY3RlZCBpbiBhZGRpdGlvbiB0byB0aGUgTGFncmFuZ2UgbXVsdGlwbGllciAozrMpLiBUaGlzIHZhbHVlIHNlcnZlcyBhcyB0aGUgY3V0IG9mZiBwb2ludCBmb3Igd2hlbiB0aGUgcHJlZGljdGlvbiBtb2RlbCB3aWxsIHByZWRpY3QgYSB3aW5lIHR5cGUgb2YgcmVkLiAgSW4gb3JkZXIgdG8gZ2V0IHRoZSBtb3N0IGFjY3VyYXRlIHByZWRpY3Rpb25zLCB0aGlzIGN1dG9mZiB2YWx1ZSBtdXN0IGJlIGNhcmVmdWxseSBzZWxlY3RlZC4gQWxsIHRocmVlIG1vZGVscyB3ZXJlIGhlbGQgYXQgYSBzdGFuZGFyZCBwcm9iYWJpbGl0eSBvZiAkXGFscGhhID0gMC41JCBhbmQgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgd2VyZSBjb2xsZWN0ZWQuIFRoZW4gdGhlc2UgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgd2VyZSBjbGFzc2lmaWVkIGFzIHdoaXRlIG9yIHJlZCByZXBlYXRlZGx5IGJhc2VkIG9uIDEwMCBkaWZmZXJlbnQgY3V0IG9mZiBwcm9iYWJpbGl0aWVzIGZyb20gMCB0byAxIChpbmNyZW1lbnRzIG9mIDAuMDEpLiAgVGhlIGFjY3VyYWN5IHJhdGVzIChyYXRlIGF0IHdoaWNoIHRoZSBjdXQgb2ZmIHBvaW50ICYgcHJlZGljdGlvbiBtb2RlbCBzdWNjZXNzZnVsbHkgY2xhc3NpZmllZCBhIHJlZCB3aW5lIGFzIGEgcmVkIGFuZCBhIHdoaXRlIHdpbmUgYXMgYSB3aGl0ZSkgZm9yIGVhY2ggbW9kZWwgd2VyZSBjYWxjdWxhdGVkIGF0IGVhY2ggb2YgdGhlc2UgMTAwIHBvaW50cy4gDQoNClRoZSBiZWxvdyBgRmlndXJlIDVgIHByb3ZpZGVzIGFuIGludGVyYWN0aXZlIGluc2lnaHQgaW50byB0aGVzZSAxMDAgcHJvYmFiaWxpdHkgY3V0IG9mZiB2YWx1ZSBhbmQgYWNjdXJhY3kgcmF0ZSBwYWlyaW5nczogIA0KDQpgYGB7cn0NCiNtb2RlbCBwcmVkaWN0IHRlc3QgZGF0YSB1c2luZyBtb2RlbCBjb2VmZmljaWVudHMgJiBsYW1iZGEuMXNlIGZyb20gQ1YgdHVuaW5nICYgUk1TRSBjYWxjdWxhdGlvbg0KI2xhc3NvDQpsYXNzby5sb2cubW9kZWwgPC0gZ2xtbmV0KHgubG9nLnRyYWluLnN0ZCwgeS5sb2cudHJhaW4sIGFscGhhID0gMSwgZmFtaWx5ID0gImJpbm9taWFsIiwgbGFtYmRhID0gbGFzc28ubG9nLmN2JGxhbWJkYS4xc2UpDQpsYXNzby5sb2cucHJlZCA8LSBwcmVkaWN0KGxhc3NvLmxvZy5tb2RlbCwgcyA9IGxhc3NvLmxvZy5jdiRsYW1iZGEuMXNlLCBuZXd4ID0gYXMubWF0cml4KHgubG9nLnRlc3Quc3RkKSwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCg0KI3JpZGdlDQpyaWRnZS5sb2cubW9kZWwgPC0gZ2xtbmV0KHgubG9nLnRyYWluLnN0ZCwgeS5sb2cudHJhaW4sIGFscGhhID0gMCwgZmFtaWx5ID0gImJpbm9taWFsIiwgbGFtYmRhID0gcmlkZ2UubG9nLmN2JGxhbWJkYS4xc2UpDQpyaWRnZS5sb2cucHJlZCA8LSBwcmVkaWN0KHJpZGdlLmxvZy5tb2RlbCwgcyA9IHJpZGdlLmxvZy5jdiRsYW1iZGEuMXNlLCBuZXd4ID0gYXMubWF0cml4KHgubG9nLnRlc3Quc3RkKSwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCg0KI2VsYXN0aWMgbmV0DQplbmV0LmxvZy5tb2RlbCA8LSBnbG1uZXQoeC5sb2cudHJhaW4uc3RkLCB5LmxvZy50cmFpbiwgYWxwaGEgPSAwLjUsIGZhbWlseSA9ICJiaW5vbWlhbCIsIGxhbWJkYSA9IGVuZXQubG9nLmN2JGxhbWJkYS4xc2UpDQplbmV0LmxvZy5wcmVkIDwtIHByZWRpY3QoZW5ldC5sb2cubW9kZWwsIHMgPSBlbmV0LmxvZy5jdiRsYW1iZGEuMXNlLCBuZXd4ID0gYXMubWF0cml4KHgubG9nLnRlc3Quc3RkKSwgdHlwZSA9ICJyZXNwb25zZSIpDQoNCmBgYA0KDQpgYGB7cn0NCiMjIyMjIHJ1bm5pbmcgdGhyb3VnaCB3aXRoIHByb2JhYmlsaXR5IGN1dG9mZiBvZiAwLjUgKM6xID0gMC41KSB0byB0ZXN0IGFjY3VyYWN5IHRvIHRoZW4gdG8gc2VsZWN0IGJldHRlciDOsSB2YWx1ZQ0KI3NldCBzZXF1ZW5jZQ0Kc2VxLmFjIDwtIHNlcSgwLDEsIGxlbmd0aCA9IDEwMCkNCmFjLmxhc3NvIDwtIE5VTEwNCmFjLnJpZGdlIDwtIE5VTEwNCmFjLmVuZXQgPC0gTlVMTA0KDQojZm9yIGxvb3AgdG8gcnVuIHRocm91Z2ggYWxsIHRocmVzaG9sZCBsZXZlbHMgYW5kIHRyYW5zZm9ybSBwcm9icyBpbnRvIG91dGNvbWVzIChSZWQvV2hpdGUpIGJhc2VkIG9uIHRocmVzaG9sZA0KZm9yIChpIGluIDE6bGVuZ3RoKHNlcS5hYykpew0KICB5LnAubGFzc28gPC0gaWZlbHNlKGxhc3NvLmxvZy5wcmVkID4gc2VxLmFjW2ldLCAxICwgMCkNCiAgeS5wLnJpZGdlIDwtIGlmZWxzZShyaWRnZS5sb2cucHJlZCA+IHNlcS5hY1tpXSwgMSAsIDApDQogIHkucC5lbmV0IDwtIGlmZWxzZShsYXNzby5sb2cucHJlZCA+IHNlcS5hY1tpXSwgMSAsIDApDQogIGFjLmxhc3NvW2ldIDwtIG1lYW4oeS5sb2cudGVzdCA9PSB5LnAubGFzc28pDQogIGFjLnJpZGdlW2ldIDwtIG1lYW4oeS5sb2cudGVzdCA9PSB5LnAucmlkZ2UpDQogIGFjLmVuZXRbaV0gPC0gbWVhbih5LmxvZy50ZXN0ID09IHkucC5lbmV0KQ0KfQ0KDQojY3JlYXRlIG1heCBhY2N1cmFjeSBjdXRvZmZzLCBtZWFuIGluY2x1ZGVkIGluIHRoZSBldmVudCBtb3JlIHRoYW4gb25lIGN1dG9mZiB3aXRoIG1heCBhY2N1cmFjeQ0KY3V0Lmxhc3NvIDwtIG1lYW4oc2VxLmFjW3doaWNoKGFjLmxhc3NvPT1tYXgoYWMubGFzc28pKV0pDQpjdXQucmlkZ2UgPC0gbWVhbihzZXEuYWNbd2hpY2goYWMucmlkZ2U9PW1heChhYy5yaWRnZSkpXSkNCmN1dC5lbmV0IDwtIG1lYW4oc2VxLmFjW3doaWNoKGFjLmVuZXQ9PW1heChhYy5lbmV0KSldKQ0KDQojbWVyZ2UgZm9yIHBsb3R0aW5nDQphYy5sb2cuYWxsIDwtIGRhdGEuZnJhbWUoDQogIHByb2IgPSByZXAoc2VxLmFjLDMpLA0KICBhY2N1cmFjeSA9IGMoYWMubGFzc28sIGFjLnJpZGdlLCBhYy5lbmV0KSwNCiAgZ3JvdXAgPSBjKHJlcCgibGFzc28iLCAxMDApLCByZXAoInJpZGdlIiwgMTAwKSwgcmVwKCJlbGFzdGljIiwgMTAwKSkNCikNCmBgYA0KDQpgYGB7cn0NCiNhYy5sb2cuZWwgPC0gYWMubG9nLmFsbCAlPiUgc3Vic2V0KGdyb3VwID09ICJlbGFzdGljIikNCiAgDQphYy5sb2cucGxvdCA8LSBnZ3Bsb3QoYWMubG9nLmFsbCwgYWVzKHggPSBwcm9iLCB5ID0gYWNjdXJhY3ksIGNvbG9yID0gZ3JvdXAsIGx0eSA9IGdyb3VwKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiTW9kZWwgUHJlZGljdGlvbiBBY2N1cmFjeSB2cy4gQ3V0LW9mZiBQcm9iYWJpbGl0eSIsIHg9ICJDdXQtb2ZmIFByb2JhYmlsaXR5IiwgeT0iQWNjdXJhY3kiLCBjb2xvciA9ICJNb2RlbCIsIGx0eSA9ICIiKQ0KDQpnZ3Bsb3RseShhYy5sb2cucGxvdCkNCmBgYA0KPGZvbnQgY2xhc3MgPSAiZmlnbGFiZWwiPiBGaWd1cmUgNToNCkFuIGludGVyYWN0aXZlIHZpc3VhbGl6YXRpb24gb2YgdGhlIHRocmVlIGxvZ2lzdGljIG1vZGVscycgcHJlZGljdGlvbiBhY2N1cmFjeSB3aGVuIHV0aWxpemluZyBjdXQtb2ZmIHByb2JhYmlsaXRpZXMgZnJvbSAwIHRvIDEgKGluY3JlbWVudHMgb2YgMC4wMSkuIEhvdmVyIG92ZXIgYW55IHBvaW50IGluIHRoZSBncmFwaCBmb3IgYWNjdXJhY3kgYW5kIM6xIHZhbHVlcy48L2ZvbnQ+DQoNCg0KVGhlc2UgMTAwIHBhaXJpbmdzIGZvciBlYWNoIG1vZGVsIHdlcmUgdGhlbiBhbmFseXplZCB0byBpZGVudGlmeSB0aGUgYXNzb2NpYXRlZCBwcm9iYWJpbGl0eSBjdXQgb2ZmIHRoYXQgcmVzdWx0ZWQgaW4gdGhlIG1heGltdW0gYWNjdXJhY3kuICBUaGUgYmVsb3cgYFRhYmxlIDlgIHN1bW1hcml6ZXMgdGhlc2UgbWF4aW11bSBhY2N1cmFjeSAmIGFzc29jaWF0ZWQgcHJvYmFiaWxpdHkgY3V0IG9mZiBwYWlyaW5ncyBmb3IgdGhlIExBU1NPLCBSaWRnZSwgYW5kIEVsYXN0aWMgTmV0IHJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gcHJlZGljdGlvbiBtb2RlbHMuIFRoZSBiZWxvdyBvdXRsaW5lZCBjdXQtb2ZmIHByb2JhYmlsaXRpZXMgd2lsbCBiZSB1dGlsaXplZCBmb3IgYWxsIHN1YnNlcXVlbnQgYW5hbHlzaXMgb2YgdGhlIExBU1NPLCBSaWRnZSwgYW5kIEVsYXN0aWMgTmV0IGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWxzIGluIHRoaXMgcmVwb3J0Lg0KDQpgYGB7cn0NCmN1dC5tYXggPC0gZGF0YS5mcmFtZSgNCiAgdmFsID0gYygiQ3V0X09mZl9Qcm9iIiwgIk1heF9BY2N1cmFjeSIpLA0KICBsYXNzbyA9IGMoY3V0Lmxhc3NvLCBtYXgoYWMubGFzc28pKSwNCiAgcmlkZ2UgPSBjKGN1dC5yaWRnZSwgbWF4KGFjLnJpZGdlKSksDQogIGVuZXQgPSBjKGN1dC5lbmV0LCBtYXgoYWMuZW5ldCkpDQopDQoNCmthYmxlKGN1dC5tYXggLCBjb2wubmFtZXMgPSBjKCIiLCAiTEFTU08iLCAiUmlkZ2UiLCAiRWxhc3RpYyBOZXQiKSwgYWxpZ24gPSBjKCJsIiwgImMiLCJjIiwiYyIpLCBkaWdpdHMgPSAzLCBjYXB0aW9uID0iVGFibGUgOToNCiAgICAgIDxicj5DdXQtb2ZmIHByb2JhYmlsaXRpZXMgdG8gdXNlIHdpdGggbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0byBtYXhpbWl6ZSBwcmVkaWN0aW9uIGFjY3VyYWN5IG9mIHdpbmUgdHlwZS4iKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCiMjIE1vZGVsIFNlbGVjdGlvbg0KDQpVdGlsaXppbmcgdGhlIGNhbGN1bGF0ZWQgYmVzdCBjdXQtb2ZmIHByb2JhYmlsaXR5IHZhbHVlcyB0aGUgUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljcyAoUk9DKSBjdXJ2ZSB3YXMgcGxvdHRlZCBhbG9uZyB3aXRoIHRoZSBwcmVkaWN0ZWQgYmVzdCBmaXQgdGhyZXNob2xkIHBvaW50cyBhcyBjYWxjdWxhdGVkIGFib3ZlIGluIGBUYWJsZSA5YC4gIER1ZSB0byB0aGUgaGlnaCBsZXZlbCBvZiBtb2RlbCB0dW5pbmcgd2l0aCBjYWxjdWxhdGluZyB0aGUgYmVzdCBmaXQgzrMgYW5kIHRoZSBiZXN0IGZpdCBjdXQtb2ZmIHByb2JhYmlsaXR5LCB0aGUgbW9kZWxzIGFwcGVhciB0byBiZSBvdmVyLWZpdHRlZCBkZXNwaXRlIGluY29ycG9yYXRpbmcgdGhlIExPU1MgZnVuY3Rpb25zLiAgQXMgZGVtb25zdHJhdGVkIGJlbG93IGluIGBGaWd1cmUgNmAsIHRoZSBST0MgY3VydmVzIGFyZSBuZWFyIHBlcmZlY3QgcmlnaHQgYW5nbGVzLCB3aXRoIEFyZWEgVW5kZXIgdGhlIEN1cnZlIChBVUMpIHZhbHVlcyBpbmNyZWRpYmx5IGNsb3NlIHRvIDEuICBUaGlzIGluZGljYXRlcyB0aGUgbW9kZWwgaXMgbGlrZWx5IG92ZXIgZml0dGVkIGFuZCB0aGVyZSBpcyBhIHBvdGVudGlhbCBmb3IgdGhpcyBtb2RlbCB0byBub3QgYWRhcHQgd2VsbCB0byBmdXR1cmUgZGF0YSBmb3IgYWNjdXJhdGUgcHJlZGljdGlvbnMuDQoNCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOCwgb3V0LndpZHRoPSIxMDAlIix3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsZXJyb3I9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KDQojUk9DIGNyZWF0aW9uIHJvYyhyZXNwb25zZSwgcHJlZGljdG9yLCBjYWxjdWxhdGVkIGJlc3QgcHJvYmFiaWxpdHkgY3V0b2ZmcykNClJPQy5sYXNzbyA8LSByb2MoeS5sb2cudGVzdCwgbGFzc28ubG9nLnByZWQpDQpST0MucmlkZ2UgPC0gcm9jKHkubG9nLnRlc3QsIHJpZGdlLmxvZy5wcmVkKQ0KUk9DLmVuZXQgPC0gcm9jKHkubG9nLnRlc3QsIGVuZXQubG9nLnByZWQpDQoNCiNwdWxsIHNlbnNpdGl2aXRlcyBhbmQgMS1zcGVjaWZpY2l0eSBmb3IgZWFzeSByZWYNCmxhc3NvLnNuIDwtIFJPQy5sYXNzbyRzZW5zaXRpdml0aWVzDQpsYXNzby5zcCA8LSAxLVJPQy5sYXNzbyRzcGVjaWZpY2l0aWVzDQpyaWRnZS5zbiA8LSBST0MucmlkZ2Ukc2Vuc2l0aXZpdGllcw0KcmlkZ2Uuc3AgPC0gMS1ST0MucmlkZ2Ukc3BlY2lmaWNpdGllcw0KZW5ldC5zbiA8LSBST0MuZW5ldCRzZW5zaXRpdml0aWVzDQplbmV0LnNwIDwtIDEtUk9DLmVuZXQkc3BlY2lmaWNpdGllcw0KDQojZ2V0IFJPQyBwbG90IGNvb3JkaW5hdGVzIGZvciBiZXN0IGZpdCBjdXQgb2ZmIHByb2JhYmlsaXR5IHZhbHVlcyBwcmV2IGNhbGN1bGF0ZWQgKHRocmVzaG9sZCkNCmxhc3NvLmNwIDwtIGNvb3JkcyhST0MubGFzc28sIHggPSBjdXQubGFzc28sIGlucHV0ID0gInRocmVzaG9sZCIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzZW5zaXRpdml0eSIsICJzcGVjaWZpY2l0eSIpKQ0KcmlkZ2UuY3AgPC0gY29vcmRzKFJPQy5yaWRnZSwgeCA9IGN1dC5yaWRnZSwgaW5wdXQgPSAidGhyZXNob2xkIiwgcmV0ID0gYygidGhyZXNob2xkIiwgInNlbnNpdGl2aXR5IiwgInNwZWNpZmljaXR5IikpDQplbmV0LmNwIDwtIGNvb3JkcyhST0MuZW5ldCwgeCA9IGN1dC5lbmV0LCBpbnB1dCA9ICJ0aHJlc2hvbGQiLCByZXQgPSBjKCJ0aHJlc2hvbGQiLCAic2Vuc2l0aXZpdHkiLCAic3BlY2lmaWNpdHkiKSkNCg0KI3Bsb3QgUk9DIGN1cnZlcyBhbmQgdGhyZXNob2xkIHZhbHVlcw0KcGFyKG9tYSA9IGMoNiwyLDIsMiksICBjZXgubWFpbiA9IDIpDQpwbG90KGxhc3NvLnNwLCBsYXNzby5zbiwgdHlwZSA9ICJsIiwgeGxpbT1jKDAsMSksIHhsYWI9IjEgLSBTcGVjaWZpY2l0eSAoRmFsc2UgUG9zaXRpdmUpIiAsIHlsaW09YygwLDEpLCB5bGFiID0gIlNlbnNpdGl2aXR5IChUcnVlIFBvc2l0aXZlKSIsIG1haW4gPSAiUk9DIEN1cnZlIENvbXBhcnJpc29ucyIsIGNvbCA9ICJkYXJrdmlvbGV0IiwgbHdkPTIpICsNCiAgYWJsaW5lKDAsMSwgbHR5PTIsIGNvbCA9ICJkYXJrcmVkIiwgbHdkPTEpICsNCiAgbGluZXMocmlkZ2Uuc3AsIHJpZGdlLnNuLCBjb2wgPSAiYmx1ZTMiLCBsdHkgPSAyLCBsd2QgPTIpICsNCiAgbGluZXMoZW5ldC5zcCwgZW5ldC5zbiwgY29sID0gImNvcmFsMyIsIGx0eSA9IDMsIGx3ZD0yKSArDQogIHBvaW50cygoMS1sYXNzby5jcFsxLDNdKSwgbGFzc28uY3BbMSwyXSwgcGNoID0gMjMsIGNvbCA9ICJkYXJrdmlvbGV0IiwgY2V4ID0yKSArDQogIHBvaW50cygoMS1yaWRnZS5jcFsxLDNdKSwgcmlkZ2UuY3BbMSwyXSwgcGNoID0gMjEsIGNvbCA9ICJibHVlMyIsIGNleCA9IDIpICsNCiAgcG9pbnRzKCgxLWVuZXQuY3BbMSwzXSksIGVuZXQuY3BbMSwyXSwgcGNoID0gMjIsIGNvbCA9ICJjb3JhbDMiLCBjZXggPSAyKSANCiAgbGVnZW5kKCJib3R0b21yaWdodCIsIGMocGFzdGUoIkxBU1NPICBBVUMgPSIsIHJvdW5kKFJPQy5sYXNzbyRhdWMsNCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiUmlkZ2UgICBBVUMgPSIsIHJvdW5kKFJPQy5yaWRnZSRhdWMsIDQpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoIkUuTmV0ICAgIEFVQyA9Iiwgcm91bmQoUk9DLmVuZXQkYXVjLCA0KSkpLA0KICAgICAgICAgY29sID0gYygiZGFya3Zpb2xldCIsICJibHVlMyIsICJjb3JhbDMiKSwNCiAgICAgICAgIGx3ZCA9IDIsIGx0eT0xLCBjZXggPSAwLjgsIGJ0eT0ibyIpDQogIHRpdGxlKHN1YiA9ICJGaWd1cmUgNjoNCiAgQ29tcGFycmlzb24gb2YgUk9DIGFuZCBBVUMgdmFsdWVzIGZvciBlYWNoIGxvZ2lzdGljIHJlZ3Jlc3Npb24gcHJlZGljdGlvbiBtb2RlbA0KICBDYWxjdWxhdGVkIGJlc3QgZml0IHRocmVzaG9sZHMgKM6zKSBpbmRpY2F0ZWQgb24gdGhlIHBsb3QuIiwgb3V0ZXIgPSBULCBsaW5lPTIpDQoNCmBgYA0KDQpXaGlsZSB0aGUgbW9kZWxzIGFwcGVhciB0byBiZSBvdmVyIGZpdHRlZCwgYmFzZWQgb24gdGhlIGFuYWx5c2lzIG91dHB1dHMgYXMgY2FwdHVyZWQgYmVsb3cgaW4gYFRhYmxlIDEwYCB0aGUgPGI+TEFTU088L2I+IG1vZGVsIHByb3ZlcyB0byBiZSB0aGUgYmVzdCBmaXQgcmVndWxhcml6ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsIGZvciBhY2N1cmF0ZWx5IHByZWRpY3RpbmcgYSB3aW5lJ3MgdHlwZSAoUmVkL1doaXRlKSBiYXNlZCBvbiBjaGVtaWNhbCBjb21wb3NpdGlvbi4gIFRoaXMgbW9kZWwgd2FzIHNlbGVjdGVkIGFzIHdoaWxlIGFsbCBtb2RlbHMgcmV0dXJuZWQgYW4gQVVDIHZhbHVlIG9mIG5lYXIgMSwgdGhlIExBU1NPIG1vZGVsIHJldHVybmVkIHRoZSBsb3dlc3QgZmFsc2UgcG9zaXRpdmUgcmF0ZSAoMSAtIFNlbnNpdGl2aXR5KS4gIEluIG9yZGVyIHRvIHJlZHVjZSB0aGUgbGlrZWx5IG92ZXJmaXR0aW5nLCB0aGUgY3V0LW9mZiBwcm9iYWJpbGl0eSBhbmQvb3IgdGhlIExhZ3JhbmdlIG11bHRpcGxpZXIuIA0KDQpgYGB7cn0NCiN0YWJsZSBvZiBST0MgY3VydmUgc3RhdHMgZm9yIGVhY2ggbW9kZWwNCmN1dG9mZnMgPC0gZGF0YS5mcmFtZSgNCiAgTW9kZWwgPSBjKCJMQVNTTyIsICJSaWRnZSIsICJFLk5ldCIpLA0KICBDdXRPZmYgPSBjKGN1dC5sYXNzbywgY3V0LnJpZGdlLCBjdXQuZW5ldCksDQogIFNlbnMgPSBjKGxhc3NvLmNwWzEsMl0sIHJpZGdlLmNwWzEsMl0sIGVuZXQuY3BbMSwyXSksDQogIFNwZWMgPSBjKCgxLWxhc3NvLmNwWzEsM10pLCAoMS1yaWRnZS5jcFsxLDNdKSwgKDEtZW5ldC5jcFsxLDNdKSksDQogIEFVQyA9IGMoUk9DLmxhc3NvJGF1YywgUk9DLnJpZGdlJGF1YywgUk9DLmVuZXQkYXVjKQ0KKSANCg0Ka2FibGUoY3V0b2ZmcywgZGlnaXRzID0gNSwgYWxpZ24gPSBjKCJsIiwiYyIsImMiLCJjIiwiYyIpLCBjb2wubmFtZXMgPSBjKCJNb2RlbCIsICJDdXQgT2ZmIFByb2JhYmlsaXR5IiwgIlNlbnNpdGl2aXR5IChUcnVlIFBvc3RpdmllKSIsICIxLVNwZWNpZmljaXR5IChGYWxzZSBQb3NpdGl2ZSkiLCAiTW9kZWwgQVVDIiksIGNhcHRpb24gPSAiVGFibGUgMTA6DQogICAgICA8YnI+RmluYWwgcmVndWxhcml6ZWQgcmVncmVzc2lvbiBtb2RlbCBhbmFseXNpcyBvdXRwdXRzLiIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCg0KDQpwcmVkLmxhc3NvLmdhbW1hIDwtIGlmZWxzZShsYXNzby5sb2cucHJlZCA+IGN1dC5sYXNzbywgMSwgMCkNCnByZWQubGFzc28uZ2FtbWEgPC0gYXMuZmFjdG9yKHByZWQubGFzc28uZ2FtbWEpDQp5LmxvZy50c3QuZiA8LSBhcy5mYWN0b3IoeS5sb2cudGVzdCkNCg0KbGFzc28uY29uZiA8LSBjb25mdXNpb25NYXRyaXgocHJlZC5sYXNzby5nYW1tYSwgeS5sb2cudHN0LmYpDQoNCmxvZy5hYyA8LSAobGFzc28uY29uZiR0YWJsZVsxLDFdICsgbGFzc28uY29uZiR0YWJsZVsyLDJdKS9zdW0obGFzc28uY29uZiR0YWJsZSkNCmxvZy5wcmVjIDwtIChsYXNzby5jb25mJHRhYmxlWzEsMV0pLyhsYXNzby5jb25mJHRhYmxlWzEsMV0rbGFzc28uY29uZiR0YWJsZVsxLDJdKQ0KbG9nLnJlYyA8LSAobGFzc28uY29uZiR0YWJsZVsxLDFdKS8obGFzc28uY29uZiR0YWJsZVsxLDFdK2xhc3NvLmNvbmYkdGFibGVbMiwxXSkNCg0KbG9nLmV2YWwgPC0gZGF0YS5mcmFtZSgNCiAgQWNjdXJhY3kgPSBsb2cuYWMsDQogIFByZWNpc2lvbiA9IGxvZy5wcmVjLA0KICBSZWNhbGwgPSBsb2cucmVjDQopDQoNCmthYmxlKGxvZy5ldmFsLCBhbGlnbiA9ICJjIiwgY2FwdGlvbiA9ICJUYWJsZSAxMToNCiAgICAgIDxicj5FdmFsdWF0aW9uIG9mIG9mIHJlZ3VsYXJpemVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gTEFTU08gbW9kZWwgKHJlZCB2cyB3aGl0ZSkiKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCg0KT25jZSBzZWxlY3RlZCwgdGhlIExBU1NPIHJlZ3Jlc3Npb24gZXF1YXRpb24gY29lZmZpY2llbnRzIHdlcmUgb2J0YWluZWQgKGBUYWJsZSAxMmAgYmVsb3cpIHRvIGNyZWF0ZSB0aGUgZmluYWwgcmVndWxhcml6ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBlcXVhdGlvbi4NCg0KYGBge3J9DQojZXh0cmFjdGluZyByZWdyZXNzaW9uIGVxdWF0aW9uIGNvZWZmaWNpZW50cw0KbGFzc28ubG9nLmNvZWYgPC0gYXMuZGF0YS5mcmFtZShhcy5tYXRyaXgoY29lZihlbmV0LmxvZy5jdiwgZW5ldC5sb2cuY3YkbGFtYmRhLjFzZSkpKSAlPiUgc3Vic2V0KHMxICE9IDApDQoNCmthYmxlKGxhc3NvLmxvZy5jb2VmLCBhbGlnbiA9ICJjIiwgY29sLm5hbWVzID0gYygiVmFyaWFibGUiLCAiUmVncmVzc2lvbiBDb2VmZmljaWVudCIpLCBjYXB0aW9uID0gIlRhYmxlIDEyOg0KICAgICAgPGJyPlJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGZvciBzZWxlY3RlZCByZWd1bGFyaXplZCByZWdyZXNzaW9uIG1vZGVsOiBFbGFzdGljIE5ldCIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCmBgYA0KDQoNCg0KIyBTdXBwb3J0IFZlY3RvciBNYWNoaW5lIChTVk0pIEFuYWx5c2lzDQoNCkluIG9yZGVyIHRvIGNyZWF0ZSBtb3JlIGZ1dHVyZSBwcm9vZiBwcmVkaWN0aW9uIG1vZGVscywgc3VwcG9ydCB2ZWN0b3IgbWFjaGluZSAoU1ZNKSBjbGFzc2lmaWNhdGlvbiBhbmQgcmVncmVzc2lvbiBtb2RlbHMgd2VyZSBjcmVhdGVkIGluIGFkZGl0aW9uIHRvIHRoZSByZWd1bGFyaXplZCBsb2dpc3RpYyBhbmQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzIHByZXZpb3VzbHkgZXhwbG9yZWQuIA0KDQpSYXRoZXIgdGhhbiByZWx5aW5nIG9uIGEgdHJhZGl0aW9uYWwgcmVncmVzc2lvbiBtb2RlbCB0byBwcmVkaWN0IGEgYmluYXJ5IG91dGNvbWUgKGV4OiBpcyBhIHdpbmUgcmVkIG9yIHdoaXRlPyksIFNWTSBjbGFzc2lmaWNhdGlvbiBzZXRzIG91dCB0byBpZGVudGlmeSBhIGh5cGVycGxhbmUgdGhhdCBtYXhpbWl6ZXMgdGhlIG1hcmdpbnMgYmV0d2VlbiB0aGUgdHdvIGNsYXNzZXMgKHJlZC93aGl0ZSkuIFRoZSBTVk0gQy1jbGFzc2lmaWNhdGlvbiBtb2RlbCB3YXMgcnVuIG9uIGFuIDgwOjIwIHRyYWluOnRlc3Qgc3BsaXQuIFRvIG5vdGUgdGhlIDgwOjIwIHNwbGl0IGRhdGEgZGlkIG5vdCB1bmRlcmdvIHN0YW5kYXJkaXphdGlvbiwgYXMgc3RhbmRhcmRpemF0aW9uIGlzIG5vdCByZXF1aXJlZCBmb3IgU1ZNIGJhc2VkIGNsYXNzaWZpY2F0aW9uLiBDcm9zcyB2YWxpZGF0aW9uIHdhcyB0dW5lZCB0byB1dGlsaXplIDUtZm9sZCB2YWxpZGF0aW9uIHdpdGggMCByZXBldGl0aW9ucy4gIFJlcGVhdGVkIGNyb3NzLXZhbGlkYXRpb24gY2FuIGJlIGVtcGxveWVkIHNob3VsZCB0aGUgYXZhaWxhYmxlIGRhdGFiYXNlIGdyb3csIGJ1dCB3aXRoIHRoZSBjdXJyZW50IHNpemUgb2YgdGhlIGRhdGEgc2V0IGF2YWlsYWJsZSwgaW50cm9kdWNpbmcgcmVwZWF0ZWQgY3Jvc3MgdmFsaWRhdGlvbiB3aWxsIGxpa2VseSBsZWFkIHRvIG92ZXJmaXR0aW5nLg0KDQoNCiMjIFNWTSBDbGFzc2lmaWNhdGlvbiB2cyBSZWd1bGFyaXplZCBMb2dpc3RpYyBSZWdyZXNzaW9uDQoNCkFzIHRoZSBTVk0gQy1jbGFzc2lmaWNhdGlvbiBtb2RlbCB3YXMgZW1wbG95ZWQgd2l0aCB0aGUgUkJGIGtlcm5lbCAocmFkaWFsKSwgYm90aCB0aGUgY29zdCAoQykgYW5kIGN1cnZhdHVyZSAoJFxnYW1tYSQpIGh5cGVycGFyYW1ldGVycyByZXF1aXJlZCB0dW5pbmcgYmVmb3JlIHByb3BlciB1c2UuICBBIGdyaWQgc2VhcmNoIHdhcyBlbXBsb3llZCB0byBpZGVudGlmeSB0aGUgYmVzdCBmaXQgaHlwZXJwYXJhbWV0ZXIgdmFsdWVzIHV0aWxpemluZyB0aGUgPGI+dHVuZSgpPC9iPiBwYWNrYWdlLiAgVGhlc2UgYmVzdCBmaXQgdmFsdWVzIGZvciBDIGFuZCAkXGdhbW1hJCB3ZXJlIHRoZW4gZmVkIGJhY2sgaW50byB0aGUgU1ZNIEMtY2xhc3NpZmljYXRpb24gYXMgZml4ZWQgQyBhbmQgJFxnYW1tYSQgdmFsdWVzIHRvIGNyZWF0ZSB0aGUgZmluYWwgU1ZNIEMtY2xhc3NpZmljYXRpb24gbW9kZWwuICBQcmVkaWN0aW9ucyB3ZXJlIHRoZW4gZ2VuZXJhdGVkIChgVGFibGUgMTNgKSBhbmQgdXRpbGl6ZWQgdG8gY2FsY3VsYXRlIHRoZSBhY2N1cmFjeSwgcHJlY2lzaW9uLCBhbmQgcmVjYWxsIChzZW5zaXRpdml0eSkgb2YgdGhlIG1vZGVsLCBhcyBjYXB0dXJlZCBiZWxvdyBpbiBgVGFibGUgMTRgLg0KDQoNCmBgYHtyfQ0KI3NwbGl0IGRhdGENCndpbmUuc3ZtIDwtIHdpbmVbLTE0XQ0Kc2V0LnNlZWQoMTIzNjg5NSkNCnN2bS5zcGxpdCA8LSBzYW1wbGUoMTpucm93KHdpbmUuc3ZtKSwgMC44Km5yb3cod2luZS5zdm0pLCByZXBsYWNlID0gRikNCnRyYWluLnN2bSA8LSB3aW5lLnN2bVtzdm0uc3BsaXQsXQ0KdGVzdC5zdm0gPC0gd2luZS5zdm1bLXN2bS5zcGxpdCxdDQpgYGANCg0KYGBge3J9DQojc2V0IHVwIENWIGNvbnRyb2wNCmN2LmNvbnQuY2xhc3MgPC0gdHVuZS5jb250cm9sKGNyb3NzID0gNSwgbnJlcGVhdCA9IDEpDQoNCmhwLmNsYXNzIDwtIHR1bmUoc3ZtLCB0eXBlZiB+LiwgZGF0YSA9IHRyYWluLnN2bSwgDQogICAgICAgICAgICAgICAgIGtlcm5lbCA9ICJyYWRpYWwiLCANCiAgICAgICAgICAgICAgICAgcmFuZ2VzID0gbGlzdChjb3N0ID0gMTBeKC0xOjIpLCBnYW1tYSA9IGMoMC4xLCAwLjUsIDEsIDIpKSwNCiAgICAgICAgICAgICAgICAgdHVuZWNvbnRyb2wgPSBjdi5jb250LmNsYXNzKQ0KYGBgDQoNCmBgYHtyfQ0KY2xhc3MubW9kZWwgPC0gaHAuY2xhc3MkYmVzdC5tb2RlbA0KY2xhc3MuY29zdCA8LSBjbGFzcy5tb2RlbCRjb3N0DQpjbGFzcy5nYW1tYSA8LSBjbGFzcy5tb2RlbCRnYW1tYQ0KDQpjbGFzcy50cmFpbiA8LSBzdm0odHlwZWYgfiAuLCB0cmFpbi5zdm0sIGtlcm5lbCA9ICJyYWRpYWwiLCBjb3N0ID0gY2xhc3MuY29zdCwgZ2FtbWEgPSBjbGFzcy5nYW1tYSkNCg0KY2xhc3MucHJlZCA8LSBwcmVkaWN0KGNsYXNzLnRyYWluLCB0ZXN0LnN2bSwgdHlwZSA9ICJjbGFzcyIpDQoNCmNsYXNzLmNvbmYgPC0gdGFibGUoUHJlZGljdGVkID0gY2xhc3MucHJlZCwgQWN0dWFsID0gdGVzdC5zdm0kdHlwZSkNCg0Ka2FibGUoY2xhc3MuY29uZiwgY29sLm5hbWVzID0gYygiUHJlZGljdGVkIFR5cGUiLCAiQWN0dWFsIFJlZCIsICJBY3R1YWwgV2hpdGUiKSwgYWxpZ24gPSBjKCJjIiwgImMiLCAiYyIpLCBjYXB0aW9uPSJUYWJsZSAxMzoNCiAgICAgIDxicj5Db25mdXNpb24gbWF0cml4IGZvciBTVk0gY2xhc3NpZmljYXRpb24gb2Ygd2luZSAocmVkL3doaXRlKSIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCmNsYXNzLmFjIDwtIChjbGFzcy5jb25mWzEsMV0gKyBjbGFzcy5jb25mWzIsMl0pL3N1bShjbGFzcy5jb25mKQ0KY2xhc3MucHJlYyA8LSAoY2xhc3MuY29uZlsxLDFdKS8oY2xhc3MuY29uZlsxLDFdK2NsYXNzLmNvbmZbMSwyXSkNCmNsYXNzLnJlYyA8LSAoY2xhc3MuY29uZlsxLDFdKS8oY2xhc3MuY29uZlsxLDFdK2NsYXNzLmNvbmZbMiwxXSkNCg0KY2xhc3Nsb2cuZXZhbCA8LSBkYXRhLmZyYW1lKA0KICBNb2RlbCA9IGMoIkxBU1NPIiwgIlNWTSBDLWNsYXNzaWZpY2F0aW9uIiksDQogIEFjY3VyYWN5ID0gYyhsb2cuYWMgLGNsYXNzLmFjKSwNCiAgUHJlY2lzaW9uID0gYyhsb2cucHJlYywgY2xhc3MucHJlYyksDQogIFJlY2FsbCA9IGMobG9nLnJlYywgY2xhc3MucmVjKQ0KKQ0KDQprYWJsZShjbGFzc2xvZy5ldmFsLCBhbGlnbiA9ICJjIiwgY2FwdGlvbiA9ICJUYWJsZSAxNDoNCiAgICAgIDxicj5FdmFsdWF0aW9uIG9mIG9mIFNWTSBDLWNsYXNzaWZjaWF0aW9uIG1vZGVsIChyZWQgdnMgd2hpdGUpIGNvbXBhcmVkIHRvIHRoZSByZWd1bGFyaXplZCBsaW5lYXIgcmVncmVzc2lvbiBMQVNTTyBtb2RlbCIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQpgYGANCg0KV2hpbGUgdGhlIGFib3ZlIGBUYWJsZSAxNGAgZGVtb25zdHJhdGVzIHRoZSBMQVNTTyBtb2RlbCBoYWQgc2xpZ2h0bHkgYmV0dGVyIHZhbHVlcyBmb3IgYWxsIHRocmVlIGV2YWx1YXRpb24gcGFyYW1ldGVycywgdGhlIFNWTSBDLWNsYXNzaWZpY2F0aW9uIHByZWRpY3Rpb24gbW9kZWwgaXMgbGlrZWx5IGEgYmV0dGVyIGZpdCBmb3IgcHJlZGljdGluZyB3aW5lIHR5cGUgKHJlZC93aGl0ZSkgYmFzZWQgb24gY2hlbWljYWwgY29tcG9zaXRpb24gZHVlIHRvIHRoZSBuYXR1cmUgb2YgdGhlIG1vZGVsIGNyZWF0aW9uIHByb2Nlc3MuICBBcyB0aGUgU1ZNIEMtY2xhc3NpZmljYXRpb24gbW9kZWwgY2FuIGJlIHJhcGlkbHkgdHVuZWQgb24gdGhlIGN1cnJlbnQgZGF0YSwgdGhpcyBwcmVkaWN0aW9uIG1vZGVsIHdpbGwgYWxsb3cgZm9yIGluY3JlYXNlZCBsb25nZXZpdHkgaW4gcHJlZGljdGlvbiBjYXBhYmlsaXRpZXMuICBUaGUgU1ZNIG1vZGVsIHdpbGwgcmVxdWlyZSBmZXdlciByZS12aXNpdHMgdG8gZW5zdXJlIHRoZSBtb2RlbCByZW1haW5zIHRoZSBiZXN0IGZpdCB0byB0aGUgY3VycmVudCBkYXRhIHBhcmFtZXRlcnMsIHdpdGggc2xpZ2h0bHkgbGVzcyBwb3RlbnRpYWwgb3ZlciBmaXR0aW5nLiAgQXMgd2luZXMgY2hhbmdlIGFuZCBldm9sdmUsIHRoaXMgU1ZNIEMtY2xhc3NpZmljYXRpb24gcHJlZGljdGlvbiBtb2RlbCB3aWxsIGV2b2x2ZSBpbiB0aGUgc2FtZSB3YXkuDQoNCmBgYHtyfQ0KI3RyYW5zZm9ybSB0byBudW1lcmlmIGZyb20gZmFjdG9yIGFuZCBmaXggd2hpdGUgPSAwIGJjIGFzLmZhY3RvciBwdXRzIHdoaXRlID0gMg0KdGVzdC5zdm0kdHlwZWYgPC0gYXMubnVtZXJpYyh0ZXN0LnN2bSR0eXBlZikNCnRlc3Quc3ZtJHR5cGVmW3Rlc3Quc3ZtJHR5cGVmID09IDJdIDwtIDANCg0KY2xhc3MucHJlZDEgPC0gYXMubnVtZXJpYyhjbGFzcy5wcmVkKQ0KY2xhc3MucHJlZDFbY2xhc3MucHJlZCA9PSAyXSA8LSAwDQoNCiN0cmFuc2Zvcm0gZnJvbSBsaXN0IHRvIGRhdGEgZnJhbWUgYW5kIGVuc3VyZSBjb2x1bW4gbmFtZSBpcyBtYXRjaA0KY2xhc3MucHJlZDEgPC0gYXMuZGF0YS5mcmFtZShjbGFzcy5wcmVkMSkgJT4lIHJlbmFtZSh0eXBlZiA9IGNsYXNzLnByZWQxKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gOCwgb3V0LndpZHRoPSIxMDAlIix3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsZXJyb3I9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0KI2NyZWF0ZSBST0MgDQpyb2Muc3ZtIDwtIHJvYyh0ZXN0LnN2bSR0eXBlZiwgY2xhc3MucHJlZDEkdHlwZWYpDQoNCiNwdWxsIHNlbnMtc3Blcw0Kc3ZtLnNuIDwtIHJvYy5zdm0kc2Vuc2l0aXZpdGllcw0Kc3ZtLnNwIDwtIDEtcm9jLnN2bSRzcGVjaWZpY2l0aWVzDQoNCiNvcHRpbWFsIGN1dCBwb2ludA0Kc3ZtLmNwIDwtIGNvb3Jkcyhyb2Muc3ZtLCAiYmVzdCIsIHJldCA9IGMoInRocmVzaG9sZCIsICJzZW5zaXRpdml0eSIsICJzcGVjaWZpY2l0eSIpKQ0KDQojcGxvdCBST0MgY3VydmVzIGFuZCB0aHJlc2hvbGQgdmFsdWVzDQpwYXIob21hID0gYyg2LDIsMiwyKSwgIGNleC5tYWluID0gMikNCnBsb3Qoc3ZtLnNwLCBzdm0uc24sIHR5cGUgPSAibCIsIHhsaW09YygwLDEpLCB4bGFiPSIxIC0gU3BlY2lmaWNpdHkgKEZhbHNlIFBvc2l0aXZlKSIgLCB5bGltPWMoMCwxKSwgeWxhYiA9ICJTZW5zaXRpdml0eSAoVHJ1ZSBQb3NpdGl2ZSkiLCBtYWluID0gIlJPQyBDdXJ2ZSBDb21wYXJyaXNvbnMiLCBjb2wgPSAiZGFya3Zpb2xldCIsIGx3ZD0yKSArDQogIGxpbmVzKGxhc3NvLnNwLCBsYXNzby5zbiwgY29sID0gImJsdWUzIiwgbHdkID0gMiwgbHR5ID0gMykgKw0KICBwb2ludHMoKDEtc3ZtLmNwWzEsM10pLCBzdm0uY3BbMSwyXSwgcGNoID0gMjMsIGNvbCA9ICJkYXJrdmlvbGV0IiwgY2V4ID0yKSArDQogIHBvaW50cygoMS1yaWRnZS5jcFsxLDNdKSwgcmlkZ2UuY3BbMSwyXSwgcGNoID0gMjEsIGNvbCA9ICJibHVlMyIsIGNleCA9IDIpIA0KICBsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYyhwYXN0ZSgiU1ZNICAgICAgIEFVQyA9Iiwgcm91bmQocm9jLnN2bSRhdWMsNCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiTEFTU08gICBBVUMgPSIsIHJvdW5kKFJPQy5sYXNzbyRhdWMsIDQpKSksDQogICAgICAgICBjb2wgPSBjKCJkYXJrdmlvbGV0IiwgImJsdWUzIiksDQogICAgICAgICBsd2QgPSAyLCBsdHk9MSwgY2V4ID0gMC44LCBidHk9Im8iKQ0KICB0aXRsZShzdWIgPSAiRmlndXJlIDc6DQogIENvbXBhcnJpc29uIG9mIFJPQyBhbmQgQVVDIHZhbHVlcyBmb3IgdGhlIFNWTSBDLWNsYXNzaWZpY2F0aW9uIA0KICBwcmVkaWN0aW9uIG1vZGVsIGFuZCB0aGUgcmVndWxhcml6ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsDQogIENhbGN1bGF0ZWQgYmVzdCBmaXQgdGhyZXNob2xkcyAozrMpIGluZGljYXRlZCBvbiB0aGUgcGxvdC4iLCBvdXRlciA9IFQsIGxpbmU9MikNCg0KDQpgYGANCg0KU2ltaWxhciB0byB0aGUgTEFTU08gbW9kZWwsIHRoZSBTVk0gQy1jbGFzc2lmaWNhdGlvbiBwcmVkaWN0aW9uIG1vZGVsIGFwcGVhcnMgdG8gZGVtb25zdHJhdGUgb3Zlci1maXR0aW5nIGJhc2VkIG9uIHRoZSBuZWFyIHBlcmZlY3Qgc3F1YXJlIHNoYXBlIG9mIHRoZSBST0MgY3VydmUgaW4gYEZpZ3VyZSA3YCBhbmQgQVVDIHZhbHVlIGluY3JlZGlibHkgY2xvc2UgdG8gMS4gIEluIGNvbnRyYXN0IHRvIHRoZSBMQVNTTyBtb2RlbCB0aGlzIGFwcGVhcmFuY2Ugb2Ygb3Zlci1maXR0aW5nIGlzIGxlc3MgY29uY2VybmluZyBmb3IgbW9kZWwgbG9uZ2V2aXR5IGluIHRoZSBTVk0gQy1jbGFzc2lmaWNhdGlvbiBtb2RlbCBkdWUgdG8gdGhlIG1vZGVsIGJ1aWxkaW5nIHByb2Nlc3MuICBBcyB0aGUgU1ZNIEMtY2xhc3MgbW9kZWwgd2lsbCBjb250aW51b3VzbHkgcmUtdHVuZSB0aGUgc2VsZWN0aW9uIGh5cGVyYXJhbWV0ZXJzIGFuZCBpZGVudGlmeSBuZXcgaHlwZXJwbGFuZXMgdG8gYWNjdXJhdGVseSBjbGFzc2lmeSB3aW5lIHR5cGVzIGJhc2VkIG9uIG5ld2x5IGF2YWlsYWJsZSBkYXRhLCB0aGUgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgd2lsbCByZXF1aXJlIG1hbnVhbCByZS10dW5pbmcgYWZ0ZXIgdGhlIGludHJvZHVjdGlvbiBvZiBlbm91Z2ggbmV3IGRhdGEuIFRoZXJlZm9yZSB0aGUgU1ZNIEMtY2xhc3NpZmljYXRpb24gbW9kZWwgd2lsbCBwcm92ZSB0byBiZSBhIG1vcmUgY29zdCBlZmZlY3RpdmUgcHJlZGljdGlvbiBtb2RlbCB3aGVuIHNlZWtpbmcgdG8gZGV0ZXJtaW5lIHdpbmUgdHlwZSAocmVkL3doaXRlKSBiYXNlZCBvbiB3aW5lIGNoZW1pY2FsIGNvbXBvc2l0aW9uLiANCg0KIyMgU1ZNIFJlZ3Jlc3Npb24gKFNWUikgdnMgUmVndWxhcml6ZWQgTGluZWFyIFJlZ3Jlc3Npb24NCg0KSW4gb3JkZXIgdG8gYWxsb3cgZm9yIGluY3JlYXNlZCBmdXR1cmUgZGF0YSBmbGV4aWJpbGl0eSBpbiB0aGUgcHJlZGljdGlvbiBtb2RlbCwgYW4gU1ZNIHJlZ3Jlc3Npb24gbW9kZWwgd2FzIGNyZWF0ZWQgdG8gYWxsb3cgZm9yIHNvbW1lbGllciBxdWFsaXR5IHByZWRpY3Rpb24gd2hpbGUgbWluaW1pemluZyBtb2RlbCBjb21wbGV4aXR5IGFuZCBlcnJvciB0b2xlcmFuY2UuIFRoaXMgaW5jcmVhc2VkIGVycm9yIHRvbGVyYW5jZSBhcyBjYXB0dXJlZCBieSAkXGVwc2lsb24kIGFsbG93cyBmb3IgaW1wcm92ZWQgcHJlZGljdGlvbiBvdXRwdXQgYWZ0ZXIgaW50cm9kdWN0aW9uIG9mIG5ldyB3aW5lIGRhdGEgd2l0aG91dCByZXF1aXJpbmcgcmUtdHVuaW5nIG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50cyByZXN1bHRpbmcgaW4gaW1wcm92ZWQgbG9uZ2V2aXR5IGFuZCBkZWNyZWFzZWQgY29zdC4gDQoNCkFzIHRoZSBTVlIgbW9kZWwgd2FzIGVtcGxveWVkIHdpdGggdGhlIFJCRiBrZXJuZWwgKHJhZGlhbCksIHRoZSBjb3N0IChDKSwgY3VydmF0dXJlICgkXGdhbW1hJCksIGFuZCB0aGUgY29udHJvbCBlcnJvciAoJFxlcHNpbG9uJCkgaHlwZXJwYXJhbWV0ZXJzIHJlcXVpcmVkIHR1bmluZyBiZWZvcmUgcHJvcGVyIHVzZS4gIEEgZ3JpZCBzZWFyY2ggd2FzIGVtcGxveWVkIHRvIGlkZW50aWZ5IHRoZSBiZXN0IGZpdCBoeXBlcnBhcmFtZXRlciB2YWx1ZXMgdXRpbGl6aW5nIHRoZSA8Yj50dW5lKCk8L2I+IHBhY2thZ2UuICBUaGVzZSBiZXN0IGZpdCB2YWx1ZXMgZm9yIEMsICRcZ2FtbWEkLiBhbmQgJFxlcHNpbG9uJCB3ZXJlIHRoZW4gZmVkIGJhY2sgaW50byB0aGUgU1ZSIG1vZGVsIGFzIGZpeGVkIEMsICRcZ2FtbWEkLCBhbmQgJFxlcHNpbG9uJCB2YWx1ZXMgdG8gY3JlYXRlIHRoZSBmaW5hbCBTVlIgbW9kZWwuICBQcmVkaWN0aW9ucyB3ZXJlIHRoZW4gZ2VuZXJhdGVkIChgVGFibGUgMTVgKSBhbmQgdXRpbGl6ZWQgdG8gY2FsY3VsYXRlIHRoZSBtZWFuIHNxdWFyZSBlcnJvciAoTVNFKSBhbmQgbWVhbiBhYnNvbHV0ZSBlcnJvciAoTUFFKSBmb3IgY29tcGFyaXNvbiBhZ2FpbnN0IHRoZSByZWd1bGFyaXplZCBsaW5lYXIgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsLCBhcyBjYXB0dXJlZCBiZWxvdyBpbiBgVGFibGUgMTVgLg0KDQoNCmBgYHtyfQ0Kd2luZS5zdnIgPC0gd2luZVstMTNdDQpzdnIueCA8LSB3aW5lLnN2clssIC0xMl0NCnN2ci55IDwtIHdpbmUuc3ZyWywxMl0NCg0Kc2V0LnNlZWQoMTIzODU2KQ0KdHJhaW4uc3ZyIDwtIHNhbXBsZSgxOm5yb3cod2luZS5zdnIpLCAwLjgqbnJvdyh3aW5lLnN2cikpDQp4LnRyYWluIDwtIHN2ci54W3RyYWluLnN2ciwgXQ0KeC50ZXN0IDwtIHN2ci54Wy10cmFpbi5zdnIsIF0NCnkudHJhaW4gPC0gc3ZyLnlbdHJhaW4uc3ZyXQ0KeS50ZXN0IDwtIHN2ci55Wy10cmFpbi5zdnJdDQoNCnR1bmUuc3ZyIDwtIHR1bmUuY29udHJvbChjcm9zcyA9IDUsIG5yZXBlYXQgPSAxKQ0KDQpocC5zdnIgPC0gdHVuZShzdm0sIHgudHJhaW4sIHkudHJhaW4sDQogICAgICAgICAgICAgICByYW5nZXMgPSBsaXN0KGVwc2lsb24gPSBzZXEoMC4xLCAwLjUsIDEuMCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvc3QgPSBjKDEsIDEwLCAxMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnYW1tYSA9IGMoMC4wMSwgMC4xLCAxKSksDQogICAgICAgICAgICAgICB0dW5lY29udHJvbCA9IHR1bmUuc3ZyKQ0KYGBgDQoNCmBgYHtyfQ0KZmluYWwuc3ZyIDwtIHN2bSh4LnRyYWluLCB5LnRyYWluLA0KICAgICAgICAgICAgICAgICB0eXBlID0gImVwcy1yZWdyZXNzaW9uIiwNCiAgICAgICAgICAgICAgICAga2VybmVsID0gInJhZGlhbCIsDQogICAgICAgICAgICAgICAgIGVwc2lsb24gPSBocC5zdnIkYmVzdC5wYXJhbWV0ZXJzJGVwc2lsb24sDQogICAgICAgICAgICAgICAgIGNvc3QgPSBocC5zdnIkYmVzdC5wYXJhbWV0ZXJzJGNvc3QsDQogICAgICAgICAgICAgICAgIGdhbW1hID0gaHAuc3ZyJGJlc3QucGFyYW1ldGVycyRnYW1tYSkNCg0Kc3ZyLnByZWQgPC0gcm91bmQocHJlZGljdChmaW5hbC5zdnIsIHgudGVzdCksMCkNCnN2ci5jb25mIDwtIHRhYmxlKFByZWRpY3RlZCA9IHN2ci5wcmVkLCBBY3R1YWwgPSB5LnRlc3QpDQoNCm1zZS5zdnIgPC0gbWVhbigoeS50ZXN0IC0gc3ZyLnByZWQpKioyKQ0KbWFlLnN2ciA8LSBtZWFuKGFicyh5LnRlc3QgLSBzdnIucHJlZCkpDQpyc3FyLnN2ciA8LSAoY29yKHkudGVzdCwgc3ZyLnByZWQpKSoqMg0KDQptc2UuZW5ldCA8LSBtZWFuKCh5LmxpbmVhci50ZXN0IC0gZW5ldC5wcmVkKSoqMikNCm1hZS5lbmV0IDwtIG1lYW4oYWJzKHkubGluZWFyLnRlc3QgLSBlbmV0LnByZWQpKQ0KcnNxci5lbmV0IDwtIChjb3IoeS5saW5lYXIudGVzdCwgZW5ldC5wcmVkKSkqKjINCg0KbXNlLm1hZSA8LSBkYXRhLmZyYW1lKA0KICBTdGF0aXN0aWMgPSBjKCJNU0UiLCAiTUFFIiwgIlIuU3F1YXJlZCIpLA0KICBTVlIgPSBjKG1zZS5zdnIsIG1hZS5zdnIsIHJzcXIuc3ZyKSwNCiAgRS5OZXQgPSBjKG1zZS5lbmV0LCBtYWUuZW5ldCwgcnNxci5lbmV0KQ0KKQ0KDQprYWJsZShzdnIuY29uZiwgY29sLm5hbWVzID0gYygiUHJlZGljdGVkIFF1YWxpdHkgUmFuayIsICJBY3R1YWwgMyIsICJBY3R1YWwgNCIsICJBY3R1YWwgNSIsICJBY3R1YWwgNiIsICJBY3R1YWwgNyIsICJBY3R1YWwgOCIsICJBY3R1YWwgOSIpLCBhbGlnbiA9ICJjIiwgY2FwdGlvbj0iVGFibGUgMTU6DQogICAgICA8YnI+Q29uZnVzaW9uIG1hdHJpeCBmb3IgU1ZSIHByZWRpY3Rpb24gb2YgbWVkaWFuIHNvbW1lbGllciBxdWFsaXR5IHJhbmtpbmcgKDAgdG8gMTAgd2hvbGUgbnVtYmVycyBvbmx5KSIpICU+JSANCiAga2FibGVfc3R5bGluZygpDQoNCg0Ka2FibGUobXNlLm1hZSwgYWxpZ24gPSBjKCJsIiwgImMiICwgImMiKSwgY2FwdGlvbiA9ICJUYWJsZSAxNjoNCiAgICAgIDxicj5Db21wYXJyaXNvbiBvZiBNZWFuIFNxdWFyZSBFcnJvciAoTVNFKSBhbmQgTWVhbiBBYnNvbHV0ZSBFcnJvciAoTUFFKSB2YWx1ZXMgZnJvbSBTVlIgcHJlZGljdGlvbiBtb2RlbCBhbmQgcmVndWxhcml6ZWQgbGluZWFyIHJlZ3Jlc3Npb24gcHJlZGljdGlvbiBtb2RlbC4iLCBjb2wubmFtZXMgPSBjKCJTdGF0aXN0aWMiLCAiU1ZNIFJlZ3Jlc3Npb24iLCAiRWxhc2ljIE5ldCBMaW5lYXIgUmVncmVzc2lvbiIpKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KYGBgDQoNCkFzIGRlbW9uc3RyYXRlZCBpbiBgVGFibGUgMTVgIGJ5IHRoZSBsb3dlciBNU0UgYW5kIE1BRSB2YWx1ZXMgYW5kIGhpZ2hlciBSJF4yJCB2YWx1ZSwgdGhlIFNWUiBwcmVkaWN0aW9uIG1vZGVsIGZvciBtZWFuIHNvbW1lbGllciBxdWFsaXR5IHJhdGluZyBtb3JlIHN1Y2Nlc3NmdWxseSBwcm9kdWNlcyBwcmVkaWN0aW9ucyB0aGFuIHRoZSByZWd1bGFyaXplZCBsaW5lYXIgcmVncmVzc2lvbiBwcmVkaWN0aW9uIG1vZGVsLg0KDQpJbiBhZGRpdGlvbiB0byBwcm92aWRpbmcgbW9yZSBhY2N1cmF0ZSBwcmVkaWN0aW9ucywgYXMgdGhlIFNWUiBtb2RlbCBjYW4gYmUgcmFwaWRseSB0dW5lZCBvbiB0aGUgY3VycmVudCBkYXRhLCB0aGlzIHByZWRpY3Rpb24gbW9kZWwgd2lsbCBhbGxvdyBmb3IgaW5jcmVhc2VkIGxvbmdldml0eSBpbiBwcmVkaWN0aW9uIGNhcGFiaWxpdGllcy4gIFRoZSBTVlIgbW9kZWwgd2lsbCByZXF1aXJlIGZld2VyIHJlLXZpc2l0cyB0byBlbnN1cmUgdGhlIG1vZGVsIHJlbWFpbnMgdGhlIGJlc3QgZml0IHRvIHRoZSBjdXJyZW50IGRhdGEgcGFyYW1ldGVycywgd2l0aCBzbGlnaHRseSBsZXNzIHBvdGVudGlhbCBvdmVyIGZpdHRpbmcuICBBcyB3aW5lcyBjaGFuZ2UgYW5kIGV2b2x2ZSwgdGhpcyBTVlIgcHJlZGljdGlvbiBtb2RlbCB3aWxsIGV2b2x2ZSBpbiB0aGUgc2FtZSB3YXkuDQoNCiMgQ29uY2x1c2lvbnMNCg0KT3ZlcmFsbCB0aGUgcHJlZGljdGlvbiBtb2RlbHMgY3JlYXRlZCBieSB0aGUgc3VwcG9ydCB2ZWN0b3IgbWFjaGluZSBhbGdvcml0aG1zIHJlc3VsdGVkIGluIGltcHJvdmVkIHByZWRpY3Rpb24gY2FwYWJpbGl0aWVzIGZvciBib3RoIHZhcmlhYmxlcyBvZiBpbnRlcmVzdCAobWVkaWFuIHNvbW1lbGllciBxdWFsaXR5IHJhbmtpbmcgYW5kIHdpbmUgdHlwZSBbcmVkIC8gd2hpdGVdKS4gIEZvciBtZWRpYW4gc29tbWVsaWVyIHF1YWxpdHkgcmFua2luZyB0aGUgU1ZSIG1vZGVsIHByb2R1Y2VkIHByZWRpY3Rpb25zIHdpdGggYW4gUiReMiQgbmVhcmx5IGRvdWJsZSB0aGF0IHNlZW4gaW4gdGhlIHJlZ3VsYXJpemVkIGxpbmVhciByZWdyZXNzaW9uIHByZWRpY3Rpb24gbW9kZWwsIGluZGljYXRpbmcgdGhhdCB0aGUgaW5jcmVhc2VkIGZsZXhpYmlsaXR5IG9mIHRoaXMgbW9kZWwgYWlkcyBpbiB0aGUgYWNjdXJhdGUgcHJlZGljdGlvbiBvZiB3aW5lIHF1YWxpdHkuICBBZGRpdGlvbmFsbHkgd2hpbGUgdGhlIHByZWRpY3Rpb24gb2Ygd2hpdGUgdnMgcmVkIG1vZGVscyBhbGwgYXBwZWFyIHRvIGJlIG92ZXItZml0dGVkLCB0aGUgU1ZSIEMtY2xhc3NpZmljYXRpb24gbW9kZWwgZGVtb25zdHJhdGVkIGxlc3Mgb3ZlcmZpdHRpbmcgd2l0aCBpbmNyZWRpYmx5IGhpZ2ggYWNjdXJhY3kgYW5kIGxlc3MgZGF0YSBwcmVwYXJhdGlvbiBwcmlvciB0byBtb2RlbCB1dGlsaXphdGlvbi4NCg0KRHVlIHRvIHRoZSBpbmNyZWFzZWQgZmxleGliaWxpdHkgdG8gbmV3IGRhdGEgYWRkZWQgdG8gdGhlIG1vZGVsIG92ZXIgdGltZSBhbmQgZGVtb25zdHJhdGVkIGJldHRlciBwcmVkaWN0aXZlIG91dHB1dHMgYm90aCB0aGUgU1ZNIEMtY2xhc3NpZmljYXRpb24gYW5kIFNWUiBtb2RlbHMgc2hvdWxkIGJlIGltcGxlbWVudGVkIHRvIGFsbG93IGZvciB3aW5lIHF1YWxpdHkgYW5kIHdpbmUgdHlwZSBwcmVkaWN0aW9ucyBiYXNlZCBvbiBjaGVtaWNhbCBjb21wb3NpdGlvbiBhbG9uZS4gIFRocm91Z2ggdXRpbGl6aW5nIHRoZXNlIHByZWRpY3RpdmUgbW9kZWxzLCB3aW5lIG1ha2VycyBjYW4gYmV0dGVyIHByZWRpY3QgaG93IHdlbGwgdmFyaW91cyBiYXRjaGVzIHdpbGwgYmUgcmF0ZWQgb24gdGhlaXIgcXVhbGl0eSBieSBzYW1wbGluZyBhIHNtYWxsIHF1YW50aXR5IG9mIHdpbmUgcHJpb3IgdG8gYm90dGxpbmcgYW5kIGFnaW5nLiAgVGhpcyB3aWxsIGFsbG93IGZvciBtb3JlIGR5bmFtaWMgcHJpY2luZyBhbmQgcHJldmVudCB3aW5lIG1ha2VycyBmcm9tIGludmVzdGluZyBpbiBsb25nIHRlcm0gc3RvcmFnZSBhbmQgYWdpbmcgb2YgYmFkIGJhdGNoZXMuICBBZGRpdGlvbmFsbHkgdGhlIFNWTSBDLWNsYXNzaWZpY2F0aW9uIG1vZGVsIHdpbGwgYWxsb3cgd2luZSBtYWtlcnMgdG8gYmV0dGVyIGNsYXNzaWZ5IHRoZWlyIHdpbmUgYmxlbmRzIHRvIGVpdGhlciB3aGl0ZSBvciByZWQgYmFzZWQgb24gYSBjaGVtaWNhbCBjb21wb3NpdGlvbiBzYW1wbGUuIFRoaXMgd2lsbCBiZSB1c2VmdWwgYXMgYmxlbmRzIHV0aWxpemUgbGVzcyBhbmQgbGVzcyBncmFwZSBza2luIGluIHRoZSBmZXJtZW50YXRpb24gcHJvY2VzcyBhbmQgcHJvZHVjZSBjbGVhcmVyIHdpbmVzIHRoYXQgbWF5IHN0aWxsIG1lZXQgdGhlIGNvbXBvc2l0aW9uIHF1YWxpZmljYXRpb25zIG9mIGEgIlJlZCIuDQoNCiMgQ2l0YXRpb25zDQoNCjxmb250IGNsYXNzPSJocmVkIj5EYXRhPC9mb250Pg0KPGJyPg0KWzFdIFAuIENvcnRleiwgQS4gQ2VyZGVpcmEsIEYuIEFsbWVpZGEsIFQuIE1hdG9zIGFuZCBKLiBSZWlzLiANCiAgTW9kZWxpbmcgd2luZSBwcmVmZXJlbmNlcyBieSBkYXRhIG1pbmluZyBmcm9tIHBoeXNpY29jaGVtaWNhbCBwcm9wZXJ0aWVzLg0KICBJbiBEZWNpc2lvbiBTdXBwb3J0IFN5c3RlbXMsIEVsc2V2aWVyLCA0Nyg0KTo1NDctNTUzLiBJU1NOOiAwMTY3LTkyMzYuDQogIChvYnRhaW5lZCBmcm9tOiBodHRwczovL2FyY2hpdmUuaWNzLnVjaS5lZHUvZGF0YXNldC8xODYvd2luZStxdWFsaXR5KQ0KDQoNCjxmb250IGNsYXNzPSJocmVkIj5XaW5lIEZhY3RzPC9mb250Pg0KPGJyPg0KWzJdIER1bmhhbSwgSi4gKDIwMjUpLiBXaW5lIEZhY3RzLiBUaGUgTmF0aW9uYWwgQXNzb2NpYXRpb24gb2YgQW1lcmljYW4gV2luZXJpZXMuIA0KICBodHRwczovL3dpbmVhbWVyaWNhLm9yZy90aGUtbWFnaWMtb2Ytd2luZS93aW5lLWZhY3RzLw0KDQoNClszXSAgU3Vzc21hbiwgWi4gKDIwMjIpLiBXaGF0J3MgdGhlIERpZmZlcmVuY2UgQmV0d2VlbiBSZWQgYW5kIFdoaXRlIFdpbmU/IFJldHJpZXZlZCAyMDI1LCBmcm9tIGh0dHBzOi8vd3d3LmZvb2RhbmR3aW5lLmNvbS93aW5lL3doYXRzLWRpZmZlcmVuY2UtYmV0d2Vlbi1yZWQtYW5kLXdoaXRlLXdpbmUgDQoNCls0XSBHYXJkZW5lciwgRC4sICYgS2VsbHksIE0uICgyMDI1KS4gVm9sYXRpbGUgQWNpZGl0eSBpbiBXaW5lLiBodHRwczovL2V4dGVuc2lvbi5wc3UuZWR1L3ZvbGF0aWxlLWFjaWRpdHktaW4td2luZQ0KDQpbNV0gIFN0b2NrbGV5LCBDcmVpbmEgZXQgYWwuICJTbzIgYW5kIFdpbmU6IEEgUmV2aWV3LiIgT0lWIENvbGxlY3RpdmUgRXhwZXJ0aXNlIERvY3VtZW50LCAxIGVkLiwgSW50ZXJuYXRpb25hbCBPcmdhbmlzYXRpb24gb2YgVmluZSBhbmQgV2luZSAoT0lWKSwgTWFyY2ggMjAyMSAyMDIxLCBwcC4gMSAtIDMwLiBnZW5lcmFsIGVkaXRvciwgT0lWIFB1YmxpY2F0aW9ucywgMjAyNS4NCihvYnRhaW5lZCBmcm9tOiBodHRwczovL3d3dy5vaXYuaW50L3B1YmxpYy9tZWRpYXMvNzg0MC9vaXYtY29sbGVjdGl2ZS1leHBlcnRpc2UtZG9jdW1lbnQtc28yLWFuZC13aW5lLWEtcmV2aWV3LnBkZik=