1 Introduction

This report analyzes synthetic breast cancer data in order to explore potential underlying patterns in the data. Starting with exploratory data analysis (EDA) to understand the relationship between the variables and their distribution. The there will be principal component analysis (PCA) in the hopes of reducing th dimensionality and identifying key informative features. Then the clustering method with be applied to group similar observations to potentially identify and possible patterns. Following that local outlier factor (LOF) will be utilized to identify and potential abnormal observations. Once all the models have been assesed and compared a final optimal model will be determined and recommended.

2 Materials

2.1 Data Set

According to the World Health Organization (WHO) breast cancer is one of the most common cancers for women worldwide, and is the second leading cause of cancer related deaths in women. This makes identifying breast cancer in women so important because the sooner you start treating a patient the better.

url ="https://chloewinters79.github.io/STA551/Data/syntheticBreastCancerData.csv"
cancer = read.csv(url, header = TRUE)

The data set being used for this assignment is a synthetic breast cancer data set, there are 600 observations in the data set and it consists of 11 variables, 10 of them are numerical and 1 of them is categorical. The 11 variables are detailed below,

  1. Sample_No: (num) unique identifier
  2. Thickness_of_Clump: (num) Benign cells are more likely monolayers and malignant or cancerous cells are multilayer
  3. Cell_Size_Uniformity: (num) Benign cells does not vary in size and malignant or cancer cell vary in size
  4. Cell_Shape_Uniformity: (num) Benign cells do not vary in shape, while malignant or cancerous cells vary in shape
  5. Marginal_Adhesion: (num) Benign cells are more likely to stick together, while malignant or cancerous cells are loose or do not stick together
  6. Single_Epithelial_Cell_Size: (num) In benign cases epithelial cells are normal in size, while in malignant or cancerous cases they are significantly enlarged
  7. Bare_Nuclei: (num) In benign cases the bare nuclei are not surrounded by cytoplasm, while in malignant or cancerous cases they are surrounded by cytoplasm
  8. Bland_Chromatin: (num) Benign cells have uniform or fine chromatin, while malignant or cancerous cells have coarse chromatin
  9. Normal_Nucleoli: (num) In benign cases nucleoli are very small, while in malignant or cancerous cases nucleoli are more prominent
  10. Mitoses: (num) In benign cases cell growth is normal, while in malignant or cancerous cases there is abnormal cell growth
  11. Outcome: (cat) “No” denotes benign breast cancer, “Yes” denotes malignant breast cancer

It should be noted that while numeric, variables 2 - 10 are integers from 1-10.

2.2 Exploratory Data Analysis

First we are going to look at the summary information of the data set. Looking at the variables most of their means seem to be in the 3-5 range, with Mitoses having the lowest mean at 2.093, and Thickness_of_Clump having the largest mean at 5.41. There does seem to be some indication of right skewness with a majority of the variables, however, we will wait to look at some visual representation before making any final conclusions. A table for the categorical variable outcome was also created, there are 380 No observations, which are observations where the cancer was benign and 220 Yes observations, where the cancer was malignant. The class imbalance does not appear to heavily bias the dataset.

summary(cancer)
   Sample_No     Thickness_of_Clump Cell_Size_Uniformity Cell_Shape_Uniformity
 Min.   :  1.0   Min.   : 1.00      Min.   : 1.000       Min.   : 1.000       
 1st Qu.:150.8   1st Qu.: 3.00      1st Qu.: 2.000       1st Qu.: 2.000       
 Median :300.5   Median : 5.00      Median : 3.000       Median : 3.000       
 Mean   :300.5   Mean   : 5.41      Mean   : 4.122       Mean   : 4.195       
 3rd Qu.:450.2   3rd Qu.: 7.25      3rd Qu.: 6.000       3rd Qu.: 6.000       
 Max.   :600.0   Max.   :10.00      Max.   :10.000       Max.   :10.000       
 Marginal_Adhesion Single_Epithelial_Cell_Size  Bare_Nuclei   Bland_Chromatin 
 Min.   : 1.000    Min.   : 1.000              Min.   : 1.0   Min.   : 1.000  
 1st Qu.: 2.000    1st Qu.: 3.000              1st Qu.: 2.0   1st Qu.: 3.000  
 Median : 3.000    Median : 4.000              Median : 3.0   Median : 4.000  
 Mean   : 3.763    Mean   : 4.293              Mean   : 4.5   Mean   : 4.495  
 3rd Qu.: 5.000    3rd Qu.: 5.000              3rd Qu.: 9.0   3rd Qu.: 6.000  
 Max.   :10.000    Max.   :10.000              Max.   :10.0   Max.   :10.000  
 Normal_Nucleoli     Mitoses         Outcome         
 Min.   : 1.000   Min.   : 1.000   Length:600        
 1st Qu.: 2.000   1st Qu.: 1.000   Class :character  
 Median : 3.000   Median : 2.000   Mode  :character  
 Mean   : 3.812   Mean   : 2.093                     
 3rd Qu.: 5.000   3rd Qu.: 2.000                     
 Max.   :10.000   Max.   :10.000                     
table(cancer$Outcome)

 No Yes 
380 220 

Moving onto a visual analysis of the variables, histograms were created for the following variables, Thickness_of_Clump, Cell_Size_Uniformity, Cell_Shape_Uniformity, Marginal_Adhesion, Single_Epithelial_Cell_Size, Bare_Nuclei, Bland_Chromatin, Normal_Nucleoli, and Mitosis. Looking at the output none of the variables appear to have a normal distribution. With the exception of the Thickness_of_Clump variable having a potential uniform distribution, all the other variables have a right skewness, with the variable Mitoses having the most severe skewness. The variables Normal_Nucleoli, Bare_Nuclei, Cell_Shape_Uniformity, Cell_Size_Uniformity, and Marginal_Adhesions having slightly less severe right skewness than Mitosis, but more severe than Single_Epithelial_Cell_Size and Bland_Chromatin, which while the least severe, are still clearly exhibiting a right skewness.

par(mfrow = c(2,2))

hist(cancer$Thickness_of_Clump, main = "Distribution of Clump Thickness")
hist(cancer$Cell_Size_Uniformity, main = "Distribution of Cell Size Uniformity")
hist(cancer$Cell_Shape_Uniformity, main = "Distribution of Cell Shape Uniformity")

hist(cancer$Marginal_Adhesion, main = "Distribution of Marginal Adhesion")

hist(cancer$Single_Epithelial_Cell_Size, main = "Distribution of Single Epithelial Cell Size")
hist(cancer$Bare_Nuclei, main = "Distribution of Bare Nuclei")

hist(cancer$Bland_Chromatin, main = "Distribution of Bland Chromatin")
hist(cancer$Normal_Nucleoli, main = "Distribution of Normal Nucleoli")

hist(cancer$Mitoses, main = "Distribution of Mitoses")

Next box plots were created to look at the relationship between the numerical variables and the outcome variable. Looking at the output it appears that for all the variables the “No” outcome was consistent with the right skewness seen in the histograms above. When looking at the output for all the variables with the “Yes” outcome with the exception of the Mitoses variable all the “Yes” outcomes are showing a left skewness, the Mitoses variable shows right skewness. None of the boxplots show a normal distribution, all boxplots show either right or left skewness.

# Make individual plots
p1 <- ggplot(cancer, aes(x = Outcome, y = Thickness_of_Clump, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Clump Thickness by Outcome", y = "Clump Thickness") +
  theme_minimal()

p2 <- ggplot(cancer, aes(x = Outcome, y = Cell_Size_Uniformity, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Cell Size Uniformity by Outcome", y = "Cell Size Uniformity") +
  theme_minimal()

p3 <- ggplot(cancer, aes(x = Outcome, y = Cell_Shape_Uniformity, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Cell Shape Uniformity by Outcome", y = "Cell Shape Uniformity") +
  theme_minimal()

p4 <- ggplot(cancer, aes(x = Outcome, y = Marginal_Adhesion, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Marginal Adhesion by Outcome", y = "Marginal Adhesion") +
  theme_minimal()

p5 <- ggplot(cancer, aes(x = Outcome, y = Single_Epithelial_Cell_Size, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Single Epithelial Cell Size by Outcome", y = "Single Epithelial Cell Size") +
  theme_minimal()

p6 <- ggplot(cancer, aes(x = Outcome, y = Bare_Nuclei, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Bare Nuclei by Outcome", y = "Bare Nuclei") +
  theme_minimal()

p7 <- ggplot(cancer, aes(x = Outcome, y = Bland_Chromatin, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Bland Chromatin by Outcome", y = "Bland Chromatin") +
  theme_minimal()

p8 <- ggplot(cancer, aes(x = Outcome, y = Normal_Nucleoli, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Normal Nucleoli by Outcome", y = "Normal Nucleoli") +
  theme_minimal()

p9 <- ggplot(cancer, aes(x = Outcome, y = Mitoses, fill = Outcome)) +
  geom_boxplot(alpha = 0.7) +
  labs(title = "Mitoses by Outcome", y = "Mitoses") +
  theme_minimal()

# Arrange into pages (2x2 layout per page)
grid.arrange(p1, p2, p3, p4, ncol = 2)   # Page 1

grid.arrange(p5, p6, p7, p8, ncol = 2)   # Page 2

grid.arrange(p9, ncol = 1)               # Page 3

Considering that all the numerical variables are integers between 1-10 a traditional pairwise graph does not make sense because it would be quite cluttered and correlation is harder to determine in those kinds of plots when the data is not continuous. To ensure we are still able to look at the correlation between variables, it made sense to do a correlation heat map to analyze the relationship between the numerical variables. Cell_Size_Uniformity and Cell_Shape_Uniformity are the most highly correlated variables with a correlation of 0.84, which is considered a strong correlation. Considering both these variables deal with a type of cell uniformity it does make sense that they would be highly correlated. There are also 16 pairings in the 0.60 - 0.71 range which would be considered strong correlation. Additionally, only 4 pairing are under 0.40 which anything under 0.40 would either be considered weak or very weak, no correlation. Since this assignment is going to be covering Principal Component Analysis (PCA) and Clustering having data with mid to high levels of variable correlation is actually ideal. Since we now know we are working with several numerical variables that are highly correlated we can move into the PCA and Clustering aspects of this assignment.

# Pearson correlation matrix
corr_mat_pearson <- cor(cancer[ ,2:10], method = "pearson")

# Pearson correlation heatmap
ggcorrplot(corr_mat_pearson, 
           hc.order = TRUE,
           type = "lower",
           lab = TRUE,
           lab_size = 3,
           method = "square",
           colors = c("blue", "white", "red"),
           title = "Pearson Correlation Heatmap of Predictors",
           ggtheme = theme_minimal)
Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
ℹ Please use tidy evaluation idioms with `aes()`.
ℹ See also `vignette("ggplot2-in-packages")` for more information.
ℹ The deprecated feature was likely used in the ggcorrplot package.
  Please report the issue at <https://github.com/kassambara/ggcorrplot/issues>.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.

3 Methodolgy & Analysis

This assignment is going to be looking at breast cancer data using principal component analysis and two clustering techniques, k-means and hierarchical. The goal is to help address the multicollinearity seen in the EDA and identify groupings within the data that correlate to patterns in the diagnosis of the tumors. In the end there will hopefully be models to help better understand the patterns in diagnosing these tumors so malignant tumors can be properly identified and treated.

3.1 Principal Component Analysis

A main goal of utilizing principal component analysis is reducing that variable correlation and addressing the multicollinearity we are currently seeing. This happens by transforming the data. In this case we will be undergoing a variable scanning process for all variables besides the ID variable and our outcome variable. After the transformation the PCA will be run on the data and summarized. The PCA run on the data will created a number of new variables based on information from our original variables. Then the decision will be made on how many principal components should be kept for our final analysis.

# Principal Component Analysis


# Remove ID and categorical outcome variable
cancer_num <- cancer[, 2:10]

# Scale numeric variables
cancer_scaled <- scale(cancer_num)

# Run PCA
pca_model <- prcomp(cancer_scaled, center = TRUE, scale. = TRUE)

# Summary of PCA
summary(pca_model)
Importance of components:
                         PC1     PC2    PC3     PC4     PC5    PC6     PC7
Standard deviation     2.362 0.88516 0.7601 0.68262 0.65025 0.6242 0.56456
Proportion of Variance 0.620 0.08706 0.0642 0.05177 0.04698 0.0433 0.03541
Cumulative Proportion  0.620 0.70705 0.7712 0.82302 0.87000 0.9133 0.94872
                           PC8     PC9
Standard deviation     0.55359 0.39380
Proportion of Variance 0.03405 0.01723
Cumulative Proportion  0.98277 1.00000
# Scree plot showing % variance explained
library(factoextra)
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
fviz_eig(pca_model, addlabels = TRUE, ylim = c(0, 60))
Warning in geom_bar(stat = "identity", fill = barfill, color = barcolor, :
Ignoring empty aesthetic: `width`.

# PCA biplot colored by cancer outcome
fviz_pca_biplot(pca_model,
                geom.ind = "point",
                col.ind = cancer$Outcome, 
                palette = c("#00AFBB", "#FC4E07"),
                addEllipses = TRUE,
                label = "var",
                col.var = "black",
                title = "PCA Biplot Colored by Outcome")
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
ℹ The deprecated feature was likely used in the ggpubr package.
  Please report the issue at <https://github.com/kassambara/ggpubr/issues>.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.

# Loadings (to interpret variable contributions)
pca_model$rotation
                                   PC1         PC2         PC3         PC4
Thickness_of_Clump          -0.3070514 -0.05648226 -0.81333514  0.29361318
Cell_Size_Uniformity        -0.3802675 -0.04160008 -0.06690158 -0.15686739
Cell_Shape_Uniformity       -0.3779635 -0.08220087 -0.13319051 -0.13431821
Marginal_Adhesion           -0.3259785 -0.09998840  0.47937227  0.42588885
Single_Epithelial_Cell_Size -0.3336167  0.16844948  0.02952749 -0.65196957
Bare_Nuclei                 -0.3497555 -0.20346938  0.05884037  0.32548805
Bland_Chromatin             -0.3329909 -0.27266333  0.25418507  0.07958854
Normal_Nucleoli             -0.3382363 -0.02299491  0.11012744 -0.29589192
Mitoses                     -0.2303952  0.91305830  0.07348799  0.25469213
                                    PC5         PC6          PC7         PC8
Thickness_of_Clump          -0.02299875  0.10476717 -0.224971906 -0.30438229
Cell_Size_Uniformity        -0.11617544 -0.01274432 -0.131429564  0.53517437
Cell_Shape_Uniformity       -0.07842349 -0.00402826  0.047459472  0.57797555
Marginal_Adhesion           -0.45104284  0.39479083 -0.303383463 -0.12988881
Single_Epithelial_Cell_Size -0.43265173 -0.20545376  0.012390086 -0.45058919
Bare_Nuclei                 -0.05197473 -0.22535066  0.805166225 -0.11924196
Bland_Chromatin              0.46821854 -0.57602286 -0.410456424 -0.14726621
Normal_Nucleoli              0.58462840  0.62757308  0.144263704 -0.16853379
Mitoses                      0.15961384 -0.11975359  0.009344927  0.05169848
                                     PC9
Thickness_of_Clump          -0.006355642
Cell_Size_Uniformity        -0.712206705
Cell_Shape_Uniformity        0.687117836
Marginal_Adhesion            0.065948374
Single_Epithelial_Cell_Size  0.042491971
Bare_Nuclei                 -0.104264303
Bland_Chromatin              0.051328816
Normal_Nucleoli             -0.023094382
Mitoses                      0.019912216

Looking at the output from the PCA there are several conclusions to be made. As to be expected in PCA the first principal component explains a majority of the data with it explaining 62% of the variance. Then moving onto the second principal component it explains 8.706% of the variance for a cumulative total of 70.705% and this gradually increases until the 9th and final principal component is reached. There is also information on how these principal components relate back to the original variables. The first principal component has negative loading for the variables cell size uniformity, cell shape uniformity, bare nuclei, and normal nucleoli. This indicates that this principal component is representative of the variables dealing will cell abnormality and that as those variables increase so does the likelihood of the tumors malignancy. Looking at the second principal component it seems to be mainly representative of the variable mitosis which is associated with the cells division. So the two principal components are representing different aspects of the cells. Considering all this information at this time it seems only utilizing the first two principal components makes the most sense.

3.1.0.1 PCA Classification Models

cancer$Outcome01 <- ifelse(cancer$Outcome == "Yes", 1, 0)
set.seed(123)
idx <- createDataPartition(cancer$Outcome01, p=0.75, list=FALSE)
train <- cancer[idx, ]
test <- cancer[-idx, ]

A logistic regression model using the first two principal components, PC1 and PC2 was fit to the training data set. The model showed that the first principal component PC1 was a significant predictor with p < 2e-16 which means we fail to reject the null at the 0.05 alpha level. However the second principal component PC2 was not significant with a p-value of 0.976. This indicated that a majority of the separation between the malignant and benign tumors is done with the first principal component. The model was evaluated on the testing subset of the data set and it had a high AUC of 0.9885. This shows that the PCA features do not negatively impact the predictive accuracy of the model.

# Use the same scaled data used for PCA
pca_model <- prcomp(cancer_scaled, center=TRUE, scale.=TRUE)

# Keep first two PCs (adjust number if you prefer)
train_pca <- data.frame(
  Outcome01 = train$Outcome01,
  predict(pca_model, newdata=train)[, 1:2]
)

test_pca <- data.frame(
  Outcome01 = test$Outcome01,
  predict(pca_model, newdata=test)[, 1:2]
)

# Logistic Regression with PCA Features
logit_pca <- glm(Outcome01 ~ PC1 + PC2, data=train_pca, family=binomial)

summary(logit_pca)

Call:
glm(formula = Outcome01 ~ PC1 + PC2, family = binomial, data = train_pca)

Coefficients:
              Estimate Std. Error z value Pr(>|z|)    
(Intercept) -10.326696   1.087736  -9.494   <2e-16 ***
PC1          -0.774759   0.086989  -8.906   <2e-16 ***
PC2          -0.005365   0.178155  -0.030    0.976    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 597.658  on 449  degrees of freedom
Residual deviance:  97.593  on 447  degrees of freedom
AIC: 103.59

Number of Fisher Scoring iterations: 7
pca_probs <- predict(logit_pca, newdata=test_pca, type="response")
roc_pca <- roc(test_pca$Outcome01, pca_probs)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
roc_pca$auc
Area under the curve: 0.9885

The random forest model was also built utilizing the first two principal components PC1 and PC2. This model was very strong in terms of predictive performance. The overall accuracy is 95.33%, meaning a majority of the observations in the test date were properly classified. The sensitivity was 1 which means 100% of the malignant observations were properly classified, which considering the real life context of this data, is amazing. The specificity is a little lower but still very high overall at 0.9307. This means 93.07% of the benign observations were correctly classified. Overall, the random forest had incredibly strong results. The model had an AUC of 0.9797 which is only slightly lower than the logistic model. However both models are very strong and are do a great job of properly classifying observations.

# Random Forest with PCA Features
set.seed(123)

rf_pca <- randomForest(
  factor(Outcome01) ~ PC1 + PC2,
  data=train_pca,
  ntree=300
)

rf_pca_probs <- predict(rf_pca, newdata=test_pca, type="prob")[,2]
rf_pca_pred  <- predict(rf_pca, newdata=test_pca, type="class")

confusionMatrix(rf_pca_pred, factor(test_pca$Outcome01), positive="1")
Confusion Matrix and Statistics

          Reference
Prediction  0  1
         0 94  0
         1  7 49
                                         
               Accuracy : 0.9533         
                 95% CI : (0.9062, 0.981)
    No Information Rate : 0.6733         
    P-Value [Acc > NIR] : < 2e-16        
                                         
                  Kappa : 0.8977         
                                         
 Mcnemar's Test P-Value : 0.02334        
                                         
            Sensitivity : 1.0000         
            Specificity : 0.9307         
         Pos Pred Value : 0.8750         
         Neg Pred Value : 1.0000         
             Prevalence : 0.3267         
         Detection Rate : 0.3267         
   Detection Prevalence : 0.3733         
      Balanced Accuracy : 0.9653         
                                         
       'Positive' Class : 1              
                                         
roc_rf_pca <- roc(test_pca$Outcome01, rf_pca_probs)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
roc_rf_pca$auc
Area under the curve: 0.9797

Both the PCA logistic and PCA random forest methods achieve high AUCs showing that they are not negatively impacting the predictive performance. It should be noted that only two PCs were utilized in this analysis. There were 9 PCs and it was my personal decision to use 2 as the optimal amount of PCs for this analysis on this data set. Someone could disagree with the optimal amount of PCs and rerun this analysis with a differnt amount and discover different results.

3.2 Clustering

K-Means and Hierarchical Clustering methods are two methods of unsupervised learning that help identify natural groupings in data. The k-means method focuses on partitioning the data into (k) number of clusters. In this analysis there will be the plotting of a chart known as the “elbow method” that will help advise the optimal number of (k) clusters. After the optimal number of clusters have been determine a graph of the data will be showcased showing the data divided into those clusters. Moving on to the hierarchical clustering this is almost reminiscent of decision tree modeling. This process created nested trees called dendograms and groups together data based on their similarities. Despite both being clustering methods the have differences that make them unique and both beneficial so both methods will be used in the analysis for this assignment.

# K-Means Clustering


# Determine optimal number of clusters (elbow method)
fviz_nbclust(cancer_scaled, kmeans, method = "wss") +
  geom_vline(xintercept = 2, linetype = 2) +
  labs(title = "Elbow Method for Optimal k")

# Run K-means with k = 2
set.seed(123)
kmeans_result <- kmeans(cancer_scaled, centers = 2, nstart = 25)

# Visualize K-means clusters
fviz_cluster(kmeans_result, data = cancer_scaled,
             palette = c("#2E9FDF", "#FC4E07"),
             ellipse.type = "norm",
             geom = "point",
             ggtheme = theme_minimal(),
             main = "K-Means Clustering (k = 2)")

# Compare K-means clusters with true Outcome
table(kmeans_result$cluster, cancer$Outcome)
   
     No Yes
  1  12 207
  2 368  13

Starting with the output from the k-means clustering analysis, the “elbow method” recommends a cutoff at 2 clusters or k=2. Using this cutoff a clustering graph is created where blue is all the data in the first cluster and 2 is all the data in the second cluster. Looking at this graph it is clear the first cluster is wider and larger and captures more spread out data while cluster 2 is smaller and narrower and captures data points that are almost on top of each other. There is also output that indicates how many malignant and non malignant cases were put into each cluster. Cluster 1 has 207 malignant cases and 12 benign cases while in cluster 2 there are 386 benign cases and 13 malignant cases. This shows that the clusters are effectively capturing the separation between the malignant and benign tumors. While there are a few misclassifications considering this is only a 2 cluster method a few misclassifications are to be expected. Overall a majority of the observations are properly classified indicating a strong k-means clustering analysis.

# Hierarchical Clustering

# Compute distance matrix
dist_mat <- dist(cancer_scaled, method = "euclidean")

# Perform hierarchical clustering
hc <- hclust(dist_mat, method = "ward.D2")

# Plot dendrogram
# Hierarchical Clustering 

# Convert scaled data to data frame
scales.hierarch <- as.data.frame(cancer_scaled)

# Compute distance matrix using Euclidean distance
distance <- dist(scales.hierarch, method = "euclidean")

# Perform hierarchical clustering using Complete Linkage
hc1 <- hclust(distance, method = "complete")

# Basic dendrogram with rectangles
plot(hc1,
     cex = 0.6,
     labels = FALSE,
     hang = -1,
     xlab = "",
     main = "Dendrogram: Hierarchical Clustering (Complete Linkage)")

# Draw rectangles for 2 clusters (you can change to 5 if desired)
rect.hclust(hc1, k = 2, border = c("#00AFBB", "#FC4E07"))

hc_clusters <- cutree(hc, k = 2)


# Compare hierarchical clusters to true Outcome
table(hc_clusters, cancer$Outcome)
           
hc_clusters  No Yes
          1 358   5
          2  22 215

Similar to the results in the k-means clustering output the hierarchical clustering also utilizes two groups. However, there is a switch in this cluster compared to the k-means. Instead of the malignant tumors being mainly captured in the first cluster we see the benign tumors being captured in this cluster. The first cluster has 358 benign cases and 5 malignant, while the second cluster has 22 benign cases and 215 malignant cases. Again, there is some misclassification but overall the amount of misclassification is not concerning considering the size of the data set. Overall, this hierarchical clustering model seems to be very strong just like the k-means model.

3.2.0.1 Clustering Classification Models

A logistic model using the K-means clustering method was created. The predictive performance was strong, indicating the two clusters have captured a significant separation between malignant and benign observations. The coefficient ClusterK22 is highly significant with a p-value < 2e-16 which means we would reject the null at the 0.05 alpha level. This is indicative of cluster two having significantly lower odds of being malignant compared to cluster one. Essentially this means most malignant observations are most in cluster one while the majority of benign observations are in cluster 2. The model also has a strong AUC at 0.9446 which is incredibly good. This means without the original data set variables the cluster alone provides significant differntiation of the two observation types, indicating a meaningful pattern,

set.seed(123)
kmeans_model <- kmeans(cancer_scaled, centers=2, nstart=25)

# Add cluster labels
cancer$ClusterK2 <- factor(kmeans_model$cluster)

# Align train/test splits
train$ClusterK2 <- cancer$ClusterK2[idx]
test$ClusterK2  <- cancer$ClusterK2[-idx]

# Logistic Regression using Cluster Label
logit_cluster <- glm(Outcome01 ~ ClusterK2, data=train, family=binomial)

summary(logit_cluster)

Call:
glm(formula = Outcome01 ~ ClusterK2, family = binomial, data = train)

Coefficients:
            Estimate Std. Error z value Pr(>|z|)    
(Intercept)   3.1355     0.3861   8.121 4.62e-16 ***
ClusterK22   -6.4387     0.5027 -12.807  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 597.66  on 449  degrees of freedom
Residual deviance: 144.62  on 448  degrees of freedom
AIC: 148.62

Number of Fisher Scoring iterations: 6
cluster_probs <- predict(logit_cluster, newdata=test, type="response")
roc_cluster <- roc(test$Outcome01, cluster_probs)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
roc_cluster$auc
Area under the curve: 0.9446

Now looking at the random forest trained with the cluster it shows a similarly strong performance. The random forest for clustering has a sensitivity of 0.9388 which means it correctly classifies 93.88% of the malignant observations. Similarly the specificity is 0.9505 which means it correctly classifies 95.05% of the benign cases. This model has the same AUC as the logistic model with 0.9446, again still a very strong AUC. While not as strong as the full model with all the predictors, for only having one feature this model has incredibly strong predictive power and produces meaningful results.

# Random Forest with Clustering Feature
set.seed(123)

rf_cluster <- randomForest(
  factor(Outcome01) ~ ClusterK2,
  data=train,
  ntree=300
)

rf_cluster_probs <- predict(rf_cluster, newdata=test, type="prob")[,2]
rf_cluster_pred <- predict(rf_cluster, newdata=test, type="class")

confusionMatrix(rf_cluster_pred, factor(test$Outcome01), positive="1")
Confusion Matrix and Statistics

          Reference
Prediction  0  1
         0 96  3
         1  5 46
                                          
               Accuracy : 0.9467          
                 95% CI : (0.8976, 0.9767)
    No Information Rate : 0.6733          
    P-Value [Acc > NIR] : 3.123e-16       
                                          
                  Kappa : 0.88            
                                          
 Mcnemar's Test P-Value : 0.7237          
                                          
            Sensitivity : 0.9388          
            Specificity : 0.9505          
         Pos Pred Value : 0.9020          
         Neg Pred Value : 0.9697          
             Prevalence : 0.3267          
         Detection Rate : 0.3067          
   Detection Prevalence : 0.3400          
      Balanced Accuracy : 0.9446          
                                          
       'Positive' Class : 1               
                                          
roc_rf_cluster <- roc(test$Outcome01, rf_cluster_probs)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
roc_rf_cluster$auc
Area under the curve: 0.9446

3.3 LOF

Local Outlier Factor, also known as LOF is a method used to identify anomalies in the data set. This is done by comparing the densities of local data points and if the density of a data point is significantly lower than the data points aware it the point is flagged as an outlier. In order to begin this process a second version of the outcome variable is going to be created. In the original data set the outcome variable is a character variable listed as “Yes” or “No” but in order to utilize the LOF method the all the variables being utilized need to be numeric. The outcome variable will be converted to numeric where 0 = “No” and 1 = “Yes”. Then all the numeric variables are going to be scaled because LOF utilized Euclidean distances so if the variables were unscaled the variables with larger ranges would dominate the computation.

# Convert Outcome to binary numeric: Yes = 1, No = 0



# Identify numeric variables
numeric_vars <- cancer %>% select(where(is.numeric))

# Remove the outcome so LOF is purely unsupervised
numeric_vars <- numeric_vars %>% select(-Outcome01)

# Scale the numeric variables
numeric_scaled <- scale(numeric_vars)

Now that all the numerical variables have been scaled it is time to compute the scores. All the observations will be assigned an outlier score based on how isolated it is compared to local data points. The higher the LOF value the more likely it is to be a potentially anomaly. To visualize these potential outliers the LOF histogram was generated. Looking at the histogram most of the LOF scores appear to be under 1.5 with very few scores surpassing that and even fewer surpassing 2 and none of the scores surpassing 3. Looking at LOF a score of 1 or very close to 1 is ideal, and looking at the summary and histogram is seems like a majority of our observations are meeting this criteria which is what we want to see.

set.seed(123)
k <- 20  

lof_scores <- lof(numeric_scaled, minPts = k)



cancer$LOF_k20 <- lof_scores

train <- cancer[idx, ]
test <- cancer[-idx, ]


summary(cancer$LOF_k20)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.9541  0.9990  1.0294  1.0755  1.0891  2.6311 
hist(cancer$LOF_k20[cancer$LOF_k20 < 5],
     breaks = 40,
     main = paste("LOF Distribution (k =", k, ")"),
     xlab = "LOF Score")

Moving onto the cutoff analysis, this allows us to determine how effectively the LOF identifies malignant cases as the outlier threshold varies. Looking at the first graph it can be seen that when using the lowest cutoff around 1 the LOF only captures around 40% of the malignant cases, and that only drops dramatically to around 0% as we get closer to a cutoff of 2. The second graph provides similar information, showing a negative improvement ratio. Overall, the LOF scores do not have significantly separate the benign cases from the malignant cases. Meaning the malignant cases are not standing out as outliers in a numeric sense, suggesting LOF may not be beneficial for this data set. However, we are going to continue our analysis cause useful information could come from it to help us make our final conclusion.

# Sequence of LOF cutoffs >1
cutoffs <- seq(1, 5, length = 21)

lof_rate <- NULL

for (i in 1:length(cutoffs)) {
  flag <- cancer$LOF_k20 > cutoffs[i]
  malignant <- cancer$Outcome01[flag]
  lof_rate[i] <- sum(malignant) / sum(flag)
}

# Baseline malignant rate
base_rate <- mean(cancer$Outcome01)

relative_improvement <- (lof_rate - base_rate) / base_rate

par(mfrow=c(1,2))

plot(cutoffs, 100*lof_rate, type="l", lwd=2,
     main="LOF malignant-catching rate",
     xlab="LOF cutoff", ylab="Catching rate (%)")

plot(cutoffs, relative_improvement, type="l", lwd=2,
     main="Relative improvement vs baseline",
     xlab="LOF cutoff", ylab="Improvement ratio")

In order to evaluate LOF as a detection tool the ROC curve was generated along with AUC scores. Unfortunately looking at the first graph for LOF Detection, the AUC is 0.5923, considering that an AUC of 0.50 is representative of essentially random guessing a 0.5923 AUC is not that strong. This supports the LOF cutoff results of this possibly not being the best methodology for this data set. When looking at the different potential k-cutoffs there is some improvement, has k increase so does the AUC going from 0.5492 when k=10 to 0.6758 when k=60. Overall, the results are indicating that LOF is not right for this data.

category <- as.character(cancer$Outcome01)

roc_lof <- roc(category, cancer$LOF_k20, levels=c("1","0"), direction=">")

plot(1-roc_lof$specificities, roc_lof$sensitivities, type="l",
     main="ROC Curve: LOF Detection",
     xlab="1 - Specificity", ylab="Sensitivity")
segments(0,0,1,1, lty=2, col="red")

text(0.8, 0.2, paste("AUC =", round(roc_lof$auc, 4)), col="darkred")

k_list <- c(10, 20, 40, 60)

lof_list <- lapply(k_list, function(k) lof(numeric_scaled, minPts=k))

roc_list <- lapply(lof_list, function(x) roc(category, x,
                                            levels=c("1","0"), direction=">"))


colors <- c("darkred", "navy", "purple", "darkgreen")

plot(1-roc_list[[1]]$specificities, roc_list[[1]]$sensitivities, type="l",
     col=colors[1], lwd=2,
     xlab="1 - specificity", ylab="sensitivity",
     main="ROC Comparison for Different k")

for (i in 2:length(k_list)) {
  lines(1-roc_list[[i]]$specificities, roc_list[[i]]$sensitivities,
        col=colors[i], lwd=2)
}

legend("bottomright",
       legend=paste("k =", k_list,
                    "AUC=", round(sapply(roc_list, function(x) x$auc), 4)),
       col=colors, lwd=2, cex=0.8, bty="n")

3.3.1 Binary Classification Models

Despite the LOF results indicating it may not be the best methodology to use on this data set the project is still going to be continued as intended. A binary classification model is going to be utilized to see if pairing LOF with a predictive model is beneficial. Before building this model the data is going to be split into training and testing subsets so the final model can be tested.

The project asks for a binary classification model to be trained without any feature engineering, then train them with feature engineering and compare the results. First we are going to start with the binary logistic regression without any feature engineering, in this case without LOF. Then we are going to also run the model with LOF so the results can be compared. Starting with the model that did not utilize LOF there are several significant variables those being, thickness of clump, cell shape and cell size uniformity, and bare nucleic. The model also had a strong performance with an AUC of 0.9867, which is extremely strong. However, when using the logistic regression model that only utilizes LOF the results were drastically different. The variable LOF_k20 was not statistically significant with a p-value of 0.744, which does not come close to a alpha of 0.05 threshold. This LOF model also has an AUC of 0.6228, a significant drop from the other model. This further confirms previous findings of LOF not being beneficial for this data.

# No LOF

baseline_logit <- glm(
  Outcome01 ~ Thickness_of_Clump + Cell_Size_Uniformity + Cell_Shape_Uniformity + 
    Marginal_Adhesion + Single_Epithelial_Cell_Size + Bare_Nuclei +
    Bland_Chromatin + Normal_Nucleoli + Mitoses,
  data=train, family=binomial
)

summary(baseline_logit)

Call:
glm(formula = Outcome01 ~ Thickness_of_Clump + Cell_Size_Uniformity + 
    Cell_Shape_Uniformity + Marginal_Adhesion + Single_Epithelial_Cell_Size + 
    Bare_Nuclei + Bland_Chromatin + Normal_Nucleoli + Mitoses, 
    family = binomial, data = train)

Coefficients:
                             Estimate Std. Error z value Pr(>|z|)    
(Intercept)                 -11.35988    1.46264  -7.767 8.06e-15 ***
Thickness_of_Clump            0.48872    0.16993   2.876  0.00403 ** 
Cell_Size_Uniformity         -0.01635    0.19903  -0.082  0.93452    
Cell_Shape_Uniformity         0.48740    0.21515   2.265  0.02349 *  
Marginal_Adhesion             0.22057    0.14647   1.506  0.13209    
Single_Epithelial_Cell_Size   0.10343    0.20689   0.500  0.61713    
Bare_Nuclei                   0.56326    0.12784   4.406 1.05e-05 ***
Bland_Chromatin               0.23523    0.17270   1.362  0.17319    
Normal_Nucleoli               0.08490    0.13172   0.645  0.51925    
Mitoses                       0.23509    0.22072   1.065  0.28682    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 597.658  on 449  degrees of freedom
Residual deviance:  80.856  on 440  degrees of freedom
AIC: 100.86

Number of Fisher Scoring iterations: 8
baseline_probs <- predict(baseline_logit, newdata=test, type="response")
roc_baseline <- roc(test$Outcome01, baseline_probs)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
roc_baseline$auc
Area under the curve: 0.9867
# LOF


logit_fit <- glm(Outcome01 ~ LOF_k20, data=train, family=binomial)

summary(logit_fit)

Call:
glm(formula = Outcome01 ~ LOF_k20, family = binomial, data = train)

Coefficients:
            Estimate Std. Error z value Pr(>|z|)
(Intercept)  -0.7085     0.6767  -1.047    0.295
LOF_k20       0.2032     0.6212   0.327    0.744

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 597.66  on 449  degrees of freedom
Residual deviance: 597.55  on 448  degrees of freedom
AIC: 601.55

Number of Fisher Scoring iterations: 4
logit_probs <- predict(logit_fit, newdata=test, type="response")
logit_pred <- ifelse(logit_probs > 0.5, 1, 0)

confusionMatrix(factor(logit_pred), factor(test$Outcome01), positive="1")
Warning in confusionMatrix.default(factor(logit_pred), factor(test$Outcome01),
: Levels are not in the same order for reference and data. Refactoring data to
match.
Confusion Matrix and Statistics

          Reference
Prediction   0   1
         0 101  49
         1   0   0
                                          
               Accuracy : 0.6733          
                 95% CI : (0.5921, 0.7476)
    No Information Rate : 0.6733          
    P-Value [Acc > NIR] : 0.5386          
                                          
                  Kappa : 0               
                                          
 Mcnemar's Test P-Value : 7.025e-12       
                                          
            Sensitivity : 0.0000          
            Specificity : 1.0000          
         Pos Pred Value :    NaN          
         Neg Pred Value : 0.6733          
             Prevalence : 0.3267          
         Detection Rate : 0.0000          
   Detection Prevalence : 0.0000          
      Balanced Accuracy : 0.5000          
                                          
       'Positive' Class : 1               
                                          
roc_logit <- roc(test$Outcome01, logit_probs)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
plot(roc_logit, main="Logistic Regression with LOF")

roc_logit$auc
Area under the curve: 0.6228

Moving onto the random forest model there will also be two versions, one that does not utilize LOF and one that utilizes LOF. Starting with the results from the non LOF model the AUC is 0.9823, which is even better than the logistic model. This model also has a sensitivity of 1, for malignant detection, which means it is correctly detecting the malignant observations every time which is incredible. This is paired with a specificity of 0.96 which means the model correctly detects benign observations 96% of the time. Both of these results are great to see in a model. Switching to the LOF random forest presents a different story. The AUC for this model is only 0.556, which as discussed as before is very weak. Additionally this is paired with a sensitivity of 0.367 which means malignant observations are only being correctly identifies 36.7% of the time. Looking at specificity this is slightly better at 0.713 meaning benign observations are correctly identified 71.3% of the time. While better than the sensitivity, this is lower than the specificity in the non LOF model.

# Not LOF
set.seed(123)

rf_base <- randomForest(
  factor(Outcome01) ~ Thickness_of_Clump + Cell_Size_Uniformity +
    Cell_Shape_Uniformity + Marginal_Adhesion +
    Single_Epithelial_Cell_Size + Bare_Nuclei +
    Bland_Chromatin + Normal_Nucleoli + Mitoses,
  data=train,
  ntree=300,
  importance=TRUE
)

rf_base_probs <- predict(rf_base, newdata=test, type="prob")[,2]
rf_base_pred  <- predict(rf_base, newdata=test, type="class")

confusionMatrix(rf_base_pred, factor(test$Outcome01), positive="1")
Confusion Matrix and Statistics

          Reference
Prediction  0  1
         0 97  0
         1  4 49
                                          
               Accuracy : 0.9733          
                 95% CI : (0.9331, 0.9927)
    No Information Rate : 0.6733          
    P-Value [Acc > NIR] : <2e-16          
                                          
                  Kappa : 0.9406          
                                          
 Mcnemar's Test P-Value : 0.1336          
                                          
            Sensitivity : 1.0000          
            Specificity : 0.9604          
         Pos Pred Value : 0.9245          
         Neg Pred Value : 1.0000          
             Prevalence : 0.3267          
         Detection Rate : 0.3267          
   Detection Prevalence : 0.3533          
      Balanced Accuracy : 0.9802          
                                          
       'Positive' Class : 1               
                                          
roc_rf_base <- roc(test$Outcome01, rf_base_probs)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
roc_rf_base$auc
Area under the curve: 0.9823
# LOF
set.seed(123)

rf_fit <- randomForest(
  factor(Outcome01) ~ LOF_k20,
  data=train,
  ntree=300,
  importance=TRUE
)

rf_probs <- predict(rf_fit, newdata=test, type="prob")[,2]
rf_pred  <- predict(rf_fit, newdata=test, type="class")

confusionMatrix(rf_pred, factor(test$Outcome01), positive="1")
Confusion Matrix and Statistics

          Reference
Prediction  0  1
         0 72 31
         1 29 18
                                         
               Accuracy : 0.6            
                 95% CI : (0.5169, 0.679)
    No Information Rate : 0.6733         
    P-Value [Acc > NIR] : 0.9760         
                                         
                  Kappa : 0.0811         
                                         
 Mcnemar's Test P-Value : 0.8973         
                                         
            Sensitivity : 0.3673         
            Specificity : 0.7129         
         Pos Pred Value : 0.3830         
         Neg Pred Value : 0.6990         
             Prevalence : 0.3267         
         Detection Rate : 0.1200         
   Detection Prevalence : 0.3133         
      Balanced Accuracy : 0.5401         
                                         
       'Positive' Class : 1              
                                         
roc_rf <- roc(test$Outcome01, rf_probs)
Setting levels: control = 0, case = 1
Setting direction: controls < cases
plot(roc_rf, main="Random Forest with LOF")

roc_rf$auc
Area under the curve: 0.5556

4 Results & Conclusions

This project showcases a strong distinction between the benign and malignant tumors in this data set. The PCA analysis that was utilized did a good job of reducing the dimensionality in the data set. Both the classification models were incredibly strong and showed good specificity and sensitivity. The results from the k-means and clustering showcased clear grouping in the data corresponding to tumor outcome status, meaning these numerical measurement indicating a natural separation between the tumor types. The clustering classifications were also incredibly strong especially considering it was one factor. However, in comparison to the PCA classification models they were overall slightly weaker. Indicating that between the two the PCA method should be the recommendation for this specific data set.

When using the LOF methodology it unfortunately was not as effective for this data. While LOF is an incredibly useful tool, it is only useful in certain scenarios with certain data sets and this data set was just not meant to benefit from LOF. The malignant tumors do not appear as outliers in the data set causing the LOFs to have low AUCs and poor sensitivities. The supervised models, being logistic and random forest were very strong. With the random forest being slightly stronger and being a great recommendation for a final model in determining whether or not a tumor is benign or malignant. Especially with a sensitivity of 1, this means patients with malignant tumors would never be misdiagnosed which is incredibly beneficial and life saving. Overall, despite the unfortunate results of LOF not being beneficial, the rest of results provided great results for predicting the whether or not a tumor is benign or malignant, which is the true goal of this project.

LS0tCnRpdGxlOiAiUENBLCBDbHVzdGVyaW5nLCB3aXRoIExPRiBvbiBCaW5hcnkgTW9kZWxzIGZvciBCcmVhc3QgQ2FuY2VyIERhdGEiCmF1dGhvcjogIkNobG/DqSBXaW50ZXJzIgpkYXRlOiAiMjAyNS0xMS0xMyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX3dpZHRoOiA2CiAgICBmaWdfaGVpZ2h0OiA0CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jX2NvbGxhcHNlZDogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgc21vb3RoX3Njcm9sbDogeWVzCiAgICB0aGVtZTogbHVtZW4KICBwZGZfZG9jdW1lbnQ6IAogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNAogICAgZmlnX2NhcHRpb246IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICB3b3JkX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzQnCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KaDEudGl0bGUgewogIGZvbnQtc2l6ZTogMjBweDsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICBjb2xvcjogRGFya1JlZDsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgZm9udC1zaXplOiAxOHB4OwogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogIGNvbG9yOiBEYXJrQmx1ZTsKICB0ZXh0LWFsaWduOiBjZW50ZXI7Cn0KaDEgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAyMnB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogZGFya3JlZDsKICAgIHRleHQtYWxpZ246IGNlbnRlcjsKfQpoMiB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovCiAgICBmb250LXNpemU6IDE4cHg7CiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsKICAgIGNvbG9yOiBuYXZ5OwogICAgdGV4dC1hbGlnbjogbGVmdDsKfQoKaDMgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLwogICAgZm9udC1zaXplOiAxNXB4OwogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7CiAgICBjb2xvcjogbmF2eTsKICAgIHRleHQtYWxpZ246IGxlZnQ7Cn0KCmg0IHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8KICAgIGZvbnQtc2l6ZTogMThweDsKICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOwogICAgY29sb3I6IGRhcmtyZWQ7CiAgICB0ZXh0LWFsaWduOiBsZWZ0Owp9Cjwvc3R5bGU+CgoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIGNvZGUgY2h1bmsgc3BlY2lmaWVzIHdoZXRoZXIgdGhlIFIgY29kZSwgd2FybmluZ3MsIGFuZCBvdXRwdXQgCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLgpsaWJyYXJ5KGtuaXRyKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgICAgICAjIGluY2x1ZGUgY29kZSBjaHVuayBpbiB0aGUgb3V0cHV0IGZpbGUKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmdzID0gRkFMU0UsICAgICAgICMgc29tZXRpbWVzLCB5b3UgY29kZSBtYXkgcHJvZHVjZSB3YXJuaW5nIG1lc3NhZ2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBvdXRwdXQgZmlsZS4gCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSAgICAgICAgICAjIHlvdSBjYW4gYWxzbyBkZWNpZGUgd2hldGhlciB0byBpbmNsdWRlIHRoZSBvdXRwdXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLgogICAgICAgICAgICAgICAgICAgICAgKSAgIAoKCgpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHBhbmRlcikKbGlicmFyeShtbGJlbmNoKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkoZ2djb3JycGxvdCkKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkoZGJzY2FuKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocFJPQykKbGlicmFyeShjYXJldCkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCgoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgIAogICAgICAgICAgICAgICAgICAgICAgd2FybmluZ3MgPSBGQUxTRSwgICAKICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VzID0gRkFMU0UsICAKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BCiAgICAgICAgICAgICAgICAgICAgICApICAgCgpgYGAKCgojIEludHJvZHVjdGlvbgoKICBUaGlzIHJlcG9ydCBhbmFseXplcyBzeW50aGV0aWMgYnJlYXN0IGNhbmNlciBkYXRhIGluIG9yZGVyIHRvIGV4cGxvcmUgcG90ZW50aWFsIHVuZGVybHlpbmcgcGF0dGVybnMgaW4gdGhlIGRhdGEuIFN0YXJ0aW5nIHdpdGggZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyAoRURBKSB0byB1bmRlcnN0YW5kIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaWFibGVzIGFuZCB0aGVpciBkaXN0cmlidXRpb24uIFRoZSB0aGVyZSB3aWxsIGJlIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSkgaW4gdGhlIGhvcGVzIG9mIHJlZHVjaW5nIHRoIGRpbWVuc2lvbmFsaXR5IGFuZCBpZGVudGlmeWluZyBrZXkgaW5mb3JtYXRpdmUgZmVhdHVyZXMuIFRoZW4gdGhlIGNsdXN0ZXJpbmcgbWV0aG9kIHdpdGggYmUgYXBwbGllZCB0byBncm91cCBzaW1pbGFyIG9ic2VydmF0aW9ucyB0byBwb3RlbnRpYWxseSBpZGVudGlmeSBhbmQgcG9zc2libGUgcGF0dGVybnMuIEZvbGxvd2luZyB0aGF0IGxvY2FsIG91dGxpZXIgZmFjdG9yIChMT0YpIHdpbGwgYmUgdXRpbGl6ZWQgdG8gaWRlbnRpZnkgYW5kIHBvdGVudGlhbCBhYm5vcm1hbCBvYnNlcnZhdGlvbnMuIE9uY2UgYWxsIHRoZSBtb2RlbHMgaGF2ZSBiZWVuIGFzc2VzZWQgYW5kIGNvbXBhcmVkIGEgZmluYWwgb3B0aW1hbCBtb2RlbCB3aWxsIGJlIGRldGVybWluZWQgYW5kIHJlY29tbWVuZGVkLiAKCiMgTWF0ZXJpYWxzIAoKIyMgRGF0YSBTZXQKCiAgQWNjb3JkaW5nIHRvIHRoZSBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uIChXSE8pIGJyZWFzdCBjYW5jZXIgaXMgb25lIG9mIHRoZSBtb3N0IGNvbW1vbiBjYW5jZXJzIGZvciB3b21lbiB3b3JsZHdpZGUsIGFuZCBpcyB0aGUgc2Vjb25kIGxlYWRpbmcgY2F1c2Ugb2YgY2FuY2VyIHJlbGF0ZWQgZGVhdGhzIGluIHdvbWVuLiBUaGlzIG1ha2VzIGlkZW50aWZ5aW5nIGJyZWFzdCBjYW5jZXIgaW4gd29tZW4gc28gaW1wb3J0YW50IGJlY2F1c2UgdGhlIHNvb25lciB5b3Ugc3RhcnQgdHJlYXRpbmcgYSBwYXRpZW50IHRoZSBiZXR0ZXIuIAoKYGBge3J9CnVybCA9Imh0dHBzOi8vY2hsb2V3aW50ZXJzNzkuZ2l0aHViLmlvL1NUQTU1MS9EYXRhL3N5bnRoZXRpY0JyZWFzdENhbmNlckRhdGEuY3N2IgpjYW5jZXIgPSByZWFkLmNzdih1cmwsIGhlYWRlciA9IFRSVUUpCmBgYAoKICBUaGUgZGF0YSBzZXQgYmVpbmcgdXNlZCBmb3IgdGhpcyBhc3NpZ25tZW50IGlzIGEgc3ludGhldGljIGJyZWFzdCBjYW5jZXIgZGF0YSBzZXQsIHRoZXJlIGFyZSA2MDAgb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhIHNldCBhbmQgaXQgY29uc2lzdHMgb2YgMTEgdmFyaWFibGVzLCAxMCBvZiB0aGVtIGFyZSBudW1lcmljYWwgYW5kIDEgb2YgdGhlbSBpcyBjYXRlZ29yaWNhbC4gVGhlIDExIHZhcmlhYmxlcyBhcmUgZGV0YWlsZWQgYmVsb3csCgoxLiBTYW1wbGVfTm86IChudW0pIHVuaXF1ZSBpZGVudGlmaWVyCjIuIFRoaWNrbmVzc19vZl9DbHVtcDogKG51bSkgQmVuaWduIGNlbGxzIGFyZSBtb3JlIGxpa2VseSBtb25vbGF5ZXJzIGFuZCBtYWxpZ25hbnQgb3IgY2FuY2Vyb3VzIGNlbGxzIGFyZSBtdWx0aWxheWVyCjMuIENlbGxfU2l6ZV9Vbmlmb3JtaXR5OiAobnVtKSBCZW5pZ24gY2VsbHMgZG9lcyBub3QgdmFyeSBpbiBzaXplIGFuZCBtYWxpZ25hbnQgb3IgY2FuY2VyIGNlbGwgdmFyeSBpbiBzaXplCjQuIENlbGxfU2hhcGVfVW5pZm9ybWl0eTogKG51bSkgQmVuaWduIGNlbGxzIGRvIG5vdCB2YXJ5IGluIHNoYXBlLCB3aGlsZSBtYWxpZ25hbnQgb3IgY2FuY2Vyb3VzIGNlbGxzIHZhcnkgaW4gc2hhcGUKNS4gTWFyZ2luYWxfQWRoZXNpb246IChudW0pIEJlbmlnbiBjZWxscyBhcmUgbW9yZSBsaWtlbHkgdG8gc3RpY2sgdG9nZXRoZXIsIHdoaWxlIG1hbGlnbmFudCBvciBjYW5jZXJvdXMgY2VsbHMgYXJlIGxvb3NlIG9yIGRvIG5vdCBzdGljayB0b2dldGhlcgo2LiBTaW5nbGVfRXBpdGhlbGlhbF9DZWxsX1NpemU6IChudW0pIEluIGJlbmlnbiBjYXNlcyBlcGl0aGVsaWFsIGNlbGxzIGFyZSBub3JtYWwgaW4gc2l6ZSwgd2hpbGUgaW4gbWFsaWduYW50IG9yIGNhbmNlcm91cyBjYXNlcyB0aGV5IGFyZSBzaWduaWZpY2FudGx5IGVubGFyZ2VkCjcuIEJhcmVfTnVjbGVpOiAobnVtKSBJbiBiZW5pZ24gY2FzZXMgdGhlIGJhcmUgbnVjbGVpIGFyZSBub3Qgc3Vycm91bmRlZCBieSBjeXRvcGxhc20sIHdoaWxlIGluIG1hbGlnbmFudCBvciBjYW5jZXJvdXMgY2FzZXMgdGhleSBhcmUgc3Vycm91bmRlZCBieSBjeXRvcGxhc20KOC4gQmxhbmRfQ2hyb21hdGluOiAobnVtKSBCZW5pZ24gY2VsbHMgaGF2ZSB1bmlmb3JtIG9yIGZpbmUgY2hyb21hdGluLCB3aGlsZSBtYWxpZ25hbnQgb3IgY2FuY2Vyb3VzIGNlbGxzIGhhdmUgY29hcnNlIGNocm9tYXRpbgo5LiBOb3JtYWxfTnVjbGVvbGk6IChudW0pIEluIGJlbmlnbiBjYXNlcyBudWNsZW9saSBhcmUgdmVyeSBzbWFsbCwgd2hpbGUgaW4gbWFsaWduYW50IG9yIGNhbmNlcm91cyBjYXNlcyBudWNsZW9saSBhcmUgbW9yZSBwcm9taW5lbnQKMTAuIE1pdG9zZXM6IChudW0pIEluIGJlbmlnbiBjYXNlcyBjZWxsIGdyb3d0aCBpcyBub3JtYWwsIHdoaWxlIGluIG1hbGlnbmFudCBvciBjYW5jZXJvdXMgY2FzZXMgdGhlcmUgaXMgYWJub3JtYWwgY2VsbCBncm93dGgKMTEuIE91dGNvbWU6IChjYXQpICJObyIgZGVub3RlcyBiZW5pZ24gYnJlYXN0IGNhbmNlciwgIlllcyIgZGVub3RlcyBtYWxpZ25hbnQgYnJlYXN0IGNhbmNlcgoKSXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQgd2hpbGUgbnVtZXJpYywgdmFyaWFibGVzIDIgLSAxMCBhcmUgaW50ZWdlcnMgZnJvbSAxLTEwLgoKIyMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcwoKICBGaXJzdCB3ZSBhcmUgZ29pbmcgdG8gbG9vayBhdCB0aGUgc3VtbWFyeSBpbmZvcm1hdGlvbiBvZiB0aGUgZGF0YSBzZXQuIExvb2tpbmcgYXQgdGhlIHZhcmlhYmxlcyBtb3N0IG9mIHRoZWlyIG1lYW5zIHNlZW0gdG8gYmUgaW4gdGhlIDMtNSByYW5nZSwgd2l0aCBNaXRvc2VzIGhhdmluZyB0aGUgbG93ZXN0IG1lYW4gYXQgMi4wOTMsIGFuZCBUaGlja25lc3Nfb2ZfQ2x1bXAgaGF2aW5nIHRoZSBsYXJnZXN0IG1lYW4gYXQgNS40MS4gVGhlcmUgZG9lcyBzZWVtIHRvIGJlIHNvbWUgaW5kaWNhdGlvbiBvZiByaWdodCBza2V3bmVzcyB3aXRoIGEgbWFqb3JpdHkgb2YgdGhlIHZhcmlhYmxlcywgaG93ZXZlciwgd2Ugd2lsbCB3YWl0IHRvIGxvb2sgYXQgc29tZSB2aXN1YWwgcmVwcmVzZW50YXRpb24gYmVmb3JlIG1ha2luZyBhbnkgZmluYWwgY29uY2x1c2lvbnMuIEEgdGFibGUgZm9yIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBvdXRjb21lIHdhcyBhbHNvIGNyZWF0ZWQsIHRoZXJlIGFyZSAzODAgTm8gb2JzZXJ2YXRpb25zLCB3aGljaCBhcmUgb2JzZXJ2YXRpb25zIHdoZXJlIHRoZSBjYW5jZXIgd2FzIGJlbmlnbiBhbmQgMjIwIFllcyBvYnNlcnZhdGlvbnMsIHdoZXJlIHRoZSBjYW5jZXIgd2FzIG1hbGlnbmFudC4gVGhlIGNsYXNzIGltYmFsYW5jZSBkb2VzIG5vdCBhcHBlYXIgdG8gaGVhdmlseSBiaWFzIHRoZSBkYXRhc2V0LiAKCmBgYHtyfQpzdW1tYXJ5KGNhbmNlcikKdGFibGUoY2FuY2VyJE91dGNvbWUpCmBgYAogIAogIE1vdmluZyBvbnRvIGEgdmlzdWFsIGFuYWx5c2lzIG9mIHRoZSB2YXJpYWJsZXMsIGhpc3RvZ3JhbXMgd2VyZSBjcmVhdGVkIGZvciB0aGUgZm9sbG93aW5nIHZhcmlhYmxlcywgVGhpY2tuZXNzX29mX0NsdW1wLCBDZWxsX1NpemVfVW5pZm9ybWl0eSwgQ2VsbF9TaGFwZV9Vbmlmb3JtaXR5LCBNYXJnaW5hbF9BZGhlc2lvbiwgU2luZ2xlX0VwaXRoZWxpYWxfQ2VsbF9TaXplLCBCYXJlX051Y2xlaSwgQmxhbmRfQ2hyb21hdGluLCBOb3JtYWxfTnVjbGVvbGksIGFuZCBNaXRvc2lzLiBMb29raW5nIGF0IHRoZSBvdXRwdXQgbm9uZSBvZiB0aGUgdmFyaWFibGVzIGFwcGVhciB0byBoYXZlIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gV2l0aCB0aGUgZXhjZXB0aW9uIG9mIHRoZSBUaGlja25lc3Nfb2ZfQ2x1bXAgdmFyaWFibGUgaGF2aW5nIGEgcG90ZW50aWFsIHVuaWZvcm0gZGlzdHJpYnV0aW9uLCBhbGwgdGhlIG90aGVyIHZhcmlhYmxlcyBoYXZlIGEgcmlnaHQgc2tld25lc3MsIHdpdGggdGhlIHZhcmlhYmxlIE1pdG9zZXMgaGF2aW5nIHRoZSBtb3N0IHNldmVyZSBza2V3bmVzcy4gVGhlIHZhcmlhYmxlcyBOb3JtYWxfTnVjbGVvbGksIEJhcmVfTnVjbGVpLCBDZWxsX1NoYXBlX1VuaWZvcm1pdHksIENlbGxfU2l6ZV9Vbmlmb3JtaXR5LCBhbmQgTWFyZ2luYWxfQWRoZXNpb25zIGhhdmluZyBzbGlnaHRseSBsZXNzIHNldmVyZSByaWdodCBza2V3bmVzcyB0aGFuIE1pdG9zaXMsIGJ1dCBtb3JlIHNldmVyZSB0aGFuIFNpbmdsZV9FcGl0aGVsaWFsX0NlbGxfU2l6ZSBhbmQgQmxhbmRfQ2hyb21hdGluLCB3aGljaCB3aGlsZSB0aGUgbGVhc3Qgc2V2ZXJlLCBhcmUgc3RpbGwgY2xlYXJseSBleGhpYml0aW5nIGEgcmlnaHQgc2tld25lc3MuIAoKCmBgYHtyfQoKcGFyKG1mcm93ID0gYygyLDIpKQoKaGlzdChjYW5jZXIkVGhpY2tuZXNzX29mX0NsdW1wLCBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBDbHVtcCBUaGlja25lc3MiKQpoaXN0KGNhbmNlciRDZWxsX1NpemVfVW5pZm9ybWl0eSwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgQ2VsbCBTaXplIFVuaWZvcm1pdHkiKQpoaXN0KGNhbmNlciRDZWxsX1NoYXBlX1VuaWZvcm1pdHksIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIENlbGwgU2hhcGUgVW5pZm9ybWl0eSIpCgpoaXN0KGNhbmNlciRNYXJnaW5hbF9BZGhlc2lvbiwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgTWFyZ2luYWwgQWRoZXNpb24iKQpoaXN0KGNhbmNlciRTaW5nbGVfRXBpdGhlbGlhbF9DZWxsX1NpemUsIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIFNpbmdsZSBFcGl0aGVsaWFsIENlbGwgU2l6ZSIpCmhpc3QoY2FuY2VyJEJhcmVfTnVjbGVpLCBtYWluID0gIkRpc3RyaWJ1dGlvbiBvZiBCYXJlIE51Y2xlaSIpCgpoaXN0KGNhbmNlciRCbGFuZF9DaHJvbWF0aW4sIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIEJsYW5kIENocm9tYXRpbiIpCmhpc3QoY2FuY2VyJE5vcm1hbF9OdWNsZW9saSwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgTm9ybWFsIE51Y2xlb2xpIikKaGlzdChjYW5jZXIkTWl0b3NlcywgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgTWl0b3NlcyIpCmBgYAogIE5leHQgYm94IHBsb3RzIHdlcmUgY3JlYXRlZCB0byBsb29rIGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcyBhbmQgdGhlIG91dGNvbWUgdmFyaWFibGUuIExvb2tpbmcgYXQgdGhlIG91dHB1dCBpdCBhcHBlYXJzIHRoYXQgZm9yIGFsbCB0aGUgdmFyaWFibGVzIHRoZSAiTm8iIG91dGNvbWUgd2FzIGNvbnNpc3RlbnQgd2l0aCB0aGUgcmlnaHQgc2tld25lc3Mgc2VlbiBpbiB0aGUgaGlzdG9ncmFtcyBhYm92ZS4gV2hlbiBsb29raW5nIGF0IHRoZSBvdXRwdXQgZm9yIGFsbCB0aGUgdmFyaWFibGVzIHdpdGggdGhlICJZZXMiIG91dGNvbWUgd2l0aCB0aGUgZXhjZXB0aW9uIG9mIHRoZSBNaXRvc2VzIHZhcmlhYmxlIGFsbCB0aGUgIlllcyIgb3V0Y29tZXMgYXJlIHNob3dpbmcgYSBsZWZ0IHNrZXduZXNzLCB0aGUgTWl0b3NlcyB2YXJpYWJsZSBzaG93cyByaWdodCBza2V3bmVzcy4gTm9uZSBvZiB0aGUgYm94cGxvdHMgc2hvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24sIGFsbCBib3hwbG90cyBzaG93IGVpdGhlciByaWdodCBvciBsZWZ0IHNrZXduZXNzLiAKCmBgYHtyfQoKIyBNYWtlIGluZGl2aWR1YWwgcGxvdHMKcDEgPC0gZ2dwbG90KGNhbmNlciwgYWVzKHggPSBPdXRjb21lLCB5ID0gVGhpY2tuZXNzX29mX0NsdW1wLCBmaWxsID0gT3V0Y29tZSkpICsKICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjcpICsKICBsYWJzKHRpdGxlID0gIkNsdW1wIFRoaWNrbmVzcyBieSBPdXRjb21lIiwgeSA9ICJDbHVtcCBUaGlja25lc3MiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwMiA8LSBnZ3Bsb3QoY2FuY2VyLCBhZXMoeCA9IE91dGNvbWUsIHkgPSBDZWxsX1NpemVfVW5pZm9ybWl0eSwgZmlsbCA9IE91dGNvbWUpKSArCiAgZ2VvbV9ib3hwbG90KGFscGhhID0gMC43KSArCiAgbGFicyh0aXRsZSA9ICJDZWxsIFNpemUgVW5pZm9ybWl0eSBieSBPdXRjb21lIiwgeSA9ICJDZWxsIFNpemUgVW5pZm9ybWl0eSIpICsKICB0aGVtZV9taW5pbWFsKCkKCnAzIDwtIGdncGxvdChjYW5jZXIsIGFlcyh4ID0gT3V0Y29tZSwgeSA9IENlbGxfU2hhcGVfVW5pZm9ybWl0eSwgZmlsbCA9IE91dGNvbWUpKSArCiAgZ2VvbV9ib3hwbG90KGFscGhhID0gMC43KSArCiAgbGFicyh0aXRsZSA9ICJDZWxsIFNoYXBlIFVuaWZvcm1pdHkgYnkgT3V0Y29tZSIsIHkgPSAiQ2VsbCBTaGFwZSBVbmlmb3JtaXR5IikgKwogIHRoZW1lX21pbmltYWwoKQoKcDQgPC0gZ2dwbG90KGNhbmNlciwgYWVzKHggPSBPdXRjb21lLCB5ID0gTWFyZ2luYWxfQWRoZXNpb24sIGZpbGwgPSBPdXRjb21lKSkgKwogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNykgKwogIGxhYnModGl0bGUgPSAiTWFyZ2luYWwgQWRoZXNpb24gYnkgT3V0Y29tZSIsIHkgPSAiTWFyZ2luYWwgQWRoZXNpb24iKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwNSA8LSBnZ3Bsb3QoY2FuY2VyLCBhZXMoeCA9IE91dGNvbWUsIHkgPSBTaW5nbGVfRXBpdGhlbGlhbF9DZWxsX1NpemUsIGZpbGwgPSBPdXRjb21lKSkgKwogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNykgKwogIGxhYnModGl0bGUgPSAiU2luZ2xlIEVwaXRoZWxpYWwgQ2VsbCBTaXplIGJ5IE91dGNvbWUiLCB5ID0gIlNpbmdsZSBFcGl0aGVsaWFsIENlbGwgU2l6ZSIpICsKICB0aGVtZV9taW5pbWFsKCkKCnA2IDwtIGdncGxvdChjYW5jZXIsIGFlcyh4ID0gT3V0Y29tZSwgeSA9IEJhcmVfTnVjbGVpLCBmaWxsID0gT3V0Y29tZSkpICsKICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjcpICsKICBsYWJzKHRpdGxlID0gIkJhcmUgTnVjbGVpIGJ5IE91dGNvbWUiLCB5ID0gIkJhcmUgTnVjbGVpIikgKwogIHRoZW1lX21pbmltYWwoKQoKcDcgPC0gZ2dwbG90KGNhbmNlciwgYWVzKHggPSBPdXRjb21lLCB5ID0gQmxhbmRfQ2hyb21hdGluLCBmaWxsID0gT3V0Y29tZSkpICsKICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjcpICsKICBsYWJzKHRpdGxlID0gIkJsYW5kIENocm9tYXRpbiBieSBPdXRjb21lIiwgeSA9ICJCbGFuZCBDaHJvbWF0aW4iKSArCiAgdGhlbWVfbWluaW1hbCgpCgpwOCA8LSBnZ3Bsb3QoY2FuY2VyLCBhZXMoeCA9IE91dGNvbWUsIHkgPSBOb3JtYWxfTnVjbGVvbGksIGZpbGwgPSBPdXRjb21lKSkgKwogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNykgKwogIGxhYnModGl0bGUgPSAiTm9ybWFsIE51Y2xlb2xpIGJ5IE91dGNvbWUiLCB5ID0gIk5vcm1hbCBOdWNsZW9saSIpICsKICB0aGVtZV9taW5pbWFsKCkKCnA5IDwtIGdncGxvdChjYW5jZXIsIGFlcyh4ID0gT3V0Y29tZSwgeSA9IE1pdG9zZXMsIGZpbGwgPSBPdXRjb21lKSkgKwogIGdlb21fYm94cGxvdChhbHBoYSA9IDAuNykgKwogIGxhYnModGl0bGUgPSAiTWl0b3NlcyBieSBPdXRjb21lIiwgeSA9ICJNaXRvc2VzIikgKwogIHRoZW1lX21pbmltYWwoKQoKIyBBcnJhbmdlIGludG8gcGFnZXMgKDJ4MiBsYXlvdXQgcGVyIHBhZ2UpCmdyaWQuYXJyYW5nZShwMSwgcDIsIHAzLCBwNCwgbmNvbCA9IDIpICAgIyBQYWdlIDEKZ3JpZC5hcnJhbmdlKHA1LCBwNiwgcDcsIHA4LCBuY29sID0gMikgICAjIFBhZ2UgMgpncmlkLmFycmFuZ2UocDksIG5jb2wgPSAxKSAgICAgICAgICAgICAgICMgUGFnZSAzCgoKYGBgCiAgQ29uc2lkZXJpbmcgdGhhdCBhbGwgdGhlIG51bWVyaWNhbCB2YXJpYWJsZXMgYXJlIGludGVnZXJzIGJldHdlZW4gMS0xMCBhIHRyYWRpdGlvbmFsIHBhaXJ3aXNlIGdyYXBoIGRvZXMgbm90IG1ha2Ugc2Vuc2UgYmVjYXVzZSBpdCB3b3VsZCBiZSBxdWl0ZSBjbHV0dGVyZWQgYW5kIGNvcnJlbGF0aW9uIGlzIGhhcmRlciB0byBkZXRlcm1pbmUgaW4gdGhvc2Uga2luZHMgb2YgcGxvdHMgd2hlbiB0aGUgZGF0YSBpcyBub3QgY29udGludW91cy4gVG8gZW5zdXJlIHdlIGFyZSBzdGlsbCBhYmxlIHRvIGxvb2sgYXQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdmFyaWFibGVzLCBpdCBtYWRlIHNlbnNlIHRvIGRvIGEgY29ycmVsYXRpb24gaGVhdCBtYXAgdG8gYW5hbHl6ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG51bWVyaWNhbCB2YXJpYWJsZXMuIENlbGxfU2l6ZV9Vbmlmb3JtaXR5IGFuZCBDZWxsX1NoYXBlX1VuaWZvcm1pdHkgYXJlIHRoZSBtb3N0IGhpZ2hseSBjb3JyZWxhdGVkIHZhcmlhYmxlcyB3aXRoIGEgY29ycmVsYXRpb24gb2YgMC44NCwgd2hpY2ggaXMgY29uc2lkZXJlZCBhIHN0cm9uZyBjb3JyZWxhdGlvbi4gQ29uc2lkZXJpbmcgYm90aCB0aGVzZSB2YXJpYWJsZXMgZGVhbCB3aXRoIGEgdHlwZSBvZiBjZWxsIHVuaWZvcm1pdHkgaXQgZG9lcyBtYWtlIHNlbnNlIHRoYXQgdGhleSB3b3VsZCBiZSBoaWdobHkgY29ycmVsYXRlZC4gVGhlcmUgYXJlIGFsc28gMTYgcGFpcmluZ3MgaW4gdGhlIDAuNjAgLSAwLjcxIHJhbmdlIHdoaWNoIHdvdWxkIGJlIGNvbnNpZGVyZWQgc3Ryb25nIGNvcnJlbGF0aW9uLiBBZGRpdGlvbmFsbHksIG9ubHkgNCBwYWlyaW5nIGFyZSB1bmRlciAwLjQwIHdoaWNoIGFueXRoaW5nIHVuZGVyIDAuNDAgd291bGQgZWl0aGVyIGJlIGNvbnNpZGVyZWQgd2VhayBvciB2ZXJ5IHdlYWssIG5vIGNvcnJlbGF0aW9uLiBTaW5jZSB0aGlzIGFzc2lnbm1lbnQgaXMgZ29pbmcgdG8gYmUgY292ZXJpbmcgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAoUENBKSBhbmQgQ2x1c3RlcmluZyBoYXZpbmcgZGF0YSB3aXRoIG1pZCB0byBoaWdoIGxldmVscyBvZiB2YXJpYWJsZSBjb3JyZWxhdGlvbiBpcyBhY3R1YWxseSBpZGVhbC4gU2luY2Ugd2Ugbm93IGtub3cgd2UgYXJlIHdvcmtpbmcgd2l0aCBzZXZlcmFsIG51bWVyaWNhbCB2YXJpYWJsZXMgdGhhdCBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgd2UgY2FuIG1vdmUgaW50byB0aGUgUENBIGFuZCBDbHVzdGVyaW5nIGFzcGVjdHMgb2YgdGhpcyBhc3NpZ25tZW50LiAKCmBgYHtyfQojIFBlYXJzb24gY29ycmVsYXRpb24gbWF0cml4CmNvcnJfbWF0X3BlYXJzb24gPC0gY29yKGNhbmNlclsgLDI6MTBdLCBtZXRob2QgPSAicGVhcnNvbiIpCgojIFBlYXJzb24gY29ycmVsYXRpb24gaGVhdG1hcApnZ2NvcnJwbG90KGNvcnJfbWF0X3BlYXJzb24sIAogICAgICAgICAgIGhjLm9yZGVyID0gVFJVRSwKICAgICAgICAgICB0eXBlID0gImxvd2VyIiwKICAgICAgICAgICBsYWIgPSBUUlVFLAogICAgICAgICAgIGxhYl9zaXplID0gMywKICAgICAgICAgICBtZXRob2QgPSAic3F1YXJlIiwKICAgICAgICAgICBjb2xvcnMgPSBjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpLAogICAgICAgICAgIHRpdGxlID0gIlBlYXJzb24gQ29ycmVsYXRpb24gSGVhdG1hcCBvZiBQcmVkaWN0b3JzIiwKICAgICAgICAgICBnZ3RoZW1lID0gdGhlbWVfbWluaW1hbCkKYGBgCgoKIyBNZXRob2RvbGd5ICYgQW5hbHlzaXMgCgogIFRoaXMgYXNzaWdubWVudCBpcyBnb2luZyB0byBiZSBsb29raW5nIGF0IGJyZWFzdCBjYW5jZXIgZGF0YSB1c2luZyBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIGFuZCB0d28gY2x1c3RlcmluZyB0ZWNobmlxdWVzLCBrLW1lYW5zIGFuZCBoaWVyYXJjaGljYWwuIFRoZSBnb2FsIGlzIHRvIGhlbHAgYWRkcmVzcyB0aGUgbXVsdGljb2xsaW5lYXJpdHkgc2VlbiBpbiB0aGUgRURBIGFuZCBpZGVudGlmeSBncm91cGluZ3Mgd2l0aGluIHRoZSBkYXRhIHRoYXQgY29ycmVsYXRlIHRvIHBhdHRlcm5zIGluIHRoZSBkaWFnbm9zaXMgb2YgdGhlIHR1bW9ycy4gSW4gdGhlIGVuZCB0aGVyZSB3aWxsIGhvcGVmdWxseSBiZSBtb2RlbHMgdG8gaGVscCBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgcGF0dGVybnMgaW4gZGlhZ25vc2luZyB0aGVzZSB0dW1vcnMgc28gbWFsaWduYW50IHR1bW9ycyBjYW4gYmUgcHJvcGVybHkgaWRlbnRpZmllZCBhbmQgdHJlYXRlZC4gCgoKIyMgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyAKCiAgQSBtYWluIGdvYWwgb2YgdXRpbGl6aW5nIHByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgaXMgcmVkdWNpbmcgdGhhdCB2YXJpYWJsZSBjb3JyZWxhdGlvbiBhbmQgYWRkcmVzc2luZyB0aGUgbXVsdGljb2xsaW5lYXJpdHkgd2UgYXJlIGN1cnJlbnRseSBzZWVpbmcuIFRoaXMgaGFwcGVucyBieSB0cmFuc2Zvcm1pbmcgdGhlIGRhdGEuIEluIHRoaXMgY2FzZSB3ZSB3aWxsIGJlIHVuZGVyZ29pbmcgYSB2YXJpYWJsZSBzY2FubmluZyBwcm9jZXNzIGZvciBhbGwgdmFyaWFibGVzIGJlc2lkZXMgdGhlIElEIHZhcmlhYmxlIGFuZCBvdXIgb3V0Y29tZSB2YXJpYWJsZS4gQWZ0ZXIgdGhlIHRyYW5zZm9ybWF0aW9uIHRoZSBQQ0Egd2lsbCBiZSBydW4gb24gdGhlIGRhdGEgYW5kIHN1bW1hcml6ZWQuIFRoZSBQQ0EgcnVuIG9uIHRoZSBkYXRhIHdpbGwgY3JlYXRlZCBhIG51bWJlciBvZiBuZXcgdmFyaWFibGVzIGJhc2VkIG9uIGluZm9ybWF0aW9uIGZyb20gb3VyIG9yaWdpbmFsIHZhcmlhYmxlcy4gVGhlbiB0aGUgZGVjaXNpb24gd2lsbCBiZSBtYWRlIG9uIGhvdyBtYW55IHByaW5jaXBhbCBjb21wb25lbnRzIHNob3VsZCBiZSBrZXB0IGZvciBvdXIgZmluYWwgYW5hbHlzaXMuCgpgYGB7cn0KIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzCgoKIyBSZW1vdmUgSUQgYW5kIGNhdGVnb3JpY2FsIG91dGNvbWUgdmFyaWFibGUKY2FuY2VyX251bSA8LSBjYW5jZXJbLCAyOjEwXQoKIyBTY2FsZSBudW1lcmljIHZhcmlhYmxlcwpjYW5jZXJfc2NhbGVkIDwtIHNjYWxlKGNhbmNlcl9udW0pCgojIFJ1biBQQ0EKcGNhX21vZGVsIDwtIHByY29tcChjYW5jZXJfc2NhbGVkLCBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFKQoKIyBTdW1tYXJ5IG9mIFBDQQpzdW1tYXJ5KHBjYV9tb2RlbCkKCiMgU2NyZWUgcGxvdCBzaG93aW5nICUgdmFyaWFuY2UgZXhwbGFpbmVkCmxpYnJhcnkoZmFjdG9leHRyYSkKZnZpel9laWcocGNhX21vZGVsLCBhZGRsYWJlbHMgPSBUUlVFLCB5bGltID0gYygwLCA2MCkpCgojIFBDQSBiaXBsb3QgY29sb3JlZCBieSBjYW5jZXIgb3V0Y29tZQpmdml6X3BjYV9iaXBsb3QocGNhX21vZGVsLAogICAgICAgICAgICAgICAgZ2VvbS5pbmQgPSAicG9pbnQiLAogICAgICAgICAgICAgICAgY29sLmluZCA9IGNhbmNlciRPdXRjb21lLCAKICAgICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCIjMDBBRkJCIiwgIiNGQzRFMDciKSwKICAgICAgICAgICAgICAgIGFkZEVsbGlwc2VzID0gVFJVRSwKICAgICAgICAgICAgICAgIGxhYmVsID0gInZhciIsCiAgICAgICAgICAgICAgICBjb2wudmFyID0gImJsYWNrIiwKICAgICAgICAgICAgICAgIHRpdGxlID0gIlBDQSBCaXBsb3QgQ29sb3JlZCBieSBPdXRjb21lIikKCiMgTG9hZGluZ3MgKHRvIGludGVycHJldCB2YXJpYWJsZSBjb250cmlidXRpb25zKQpwY2FfbW9kZWwkcm90YXRpb24KCgoKYGBgCiAgTG9va2luZyBhdCB0aGUgb3V0cHV0IGZyb20gdGhlIFBDQSB0aGVyZSBhcmUgc2V2ZXJhbCBjb25jbHVzaW9ucyB0byBiZSBtYWRlLiBBcyB0byBiZSBleHBlY3RlZCBpbiBQQ0EgdGhlIGZpcnN0IHByaW5jaXBhbCBjb21wb25lbnQgZXhwbGFpbnMgYSBtYWpvcml0eSBvZiB0aGUgZGF0YSB3aXRoIGl0IGV4cGxhaW5pbmcgNjIlIG9mIHRoZSB2YXJpYW5jZS4gVGhlbiBtb3Zpbmcgb250byB0aGUgc2Vjb25kIHByaW5jaXBhbCBjb21wb25lbnQgaXQgZXhwbGFpbnMgOC43MDYlIG9mIHRoZSB2YXJpYW5jZSBmb3IgYSBjdW11bGF0aXZlIHRvdGFsIG9mIDcwLjcwNSUgYW5kIHRoaXMgZ3JhZHVhbGx5IGluY3JlYXNlcyB1bnRpbCB0aGUgOXRoIGFuZCBmaW5hbCBwcmluY2lwYWwgY29tcG9uZW50IGlzIHJlYWNoZWQuIFRoZXJlIGlzIGFsc28gaW5mb3JtYXRpb24gb24gaG93IHRoZXNlIHByaW5jaXBhbCBjb21wb25lbnRzIHJlbGF0ZSBiYWNrIHRvIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMuIFRoZSBmaXJzdCBwcmluY2lwYWwgY29tcG9uZW50IGhhcyBuZWdhdGl2ZSBsb2FkaW5nIGZvciB0aGUgdmFyaWFibGVzIGNlbGwgc2l6ZSB1bmlmb3JtaXR5LCBjZWxsIHNoYXBlIHVuaWZvcm1pdHksIGJhcmUgbnVjbGVpLCBhbmQgbm9ybWFsIG51Y2xlb2xpLiBUaGlzIGluZGljYXRlcyB0aGF0IHRoaXMgcHJpbmNpcGFsIGNvbXBvbmVudCBpcyByZXByZXNlbnRhdGl2ZSBvZiB0aGUgdmFyaWFibGVzIGRlYWxpbmcgd2lsbCBjZWxsIGFibm9ybWFsaXR5IGFuZCB0aGF0IGFzIHRob3NlIHZhcmlhYmxlcyBpbmNyZWFzZSBzbyBkb2VzIHRoZSBsaWtlbGlob29kIG9mIHRoZSB0dW1vcnMgbWFsaWduYW5jeS4gTG9va2luZyBhdCB0aGUgc2Vjb25kIHByaW5jaXBhbCBjb21wb25lbnQgaXQgc2VlbXMgdG8gYmUgbWFpbmx5IHJlcHJlc2VudGF0aXZlIG9mIHRoZSB2YXJpYWJsZSBtaXRvc2lzIHdoaWNoIGlzIGFzc29jaWF0ZWQgd2l0aCB0aGUgY2VsbHMgZGl2aXNpb24uIFNvIHRoZSB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgYXJlIHJlcHJlc2VudGluZyBkaWZmZXJlbnQgYXNwZWN0cyBvZiB0aGUgY2VsbHMuIENvbnNpZGVyaW5nIGFsbCB0aGlzIGluZm9ybWF0aW9uIGF0IHRoaXMgdGltZSBpdCBzZWVtcyBvbmx5IHV0aWxpemluZyB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzIG1ha2VzIHRoZSBtb3N0IHNlbnNlLiAKICAKCiMjIyMgUENBIENsYXNzaWZpY2F0aW9uIE1vZGVscwoKYGBge3J9CmNhbmNlciRPdXRjb21lMDEgPC0gaWZlbHNlKGNhbmNlciRPdXRjb21lID09ICJZZXMiLCAxLCAwKQpzZXQuc2VlZCgxMjMpCmlkeCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGNhbmNlciRPdXRjb21lMDEsIHA9MC43NSwgbGlzdD1GQUxTRSkKdHJhaW4gPC0gY2FuY2VyW2lkeCwgXQp0ZXN0IDwtIGNhbmNlclstaWR4LCBdCgpgYGAKCkEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB1c2luZyB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzLCBQQzEgYW5kIFBDMiB3YXMgZml0IHRvIHRoZSB0cmFpbmluZyBkYXRhIHNldC4gVGhlIG1vZGVsIHNob3dlZCB0aGF0IHRoZSBmaXJzdCBwcmluY2lwYWwgY29tcG9uZW50IFBDMSB3YXMgYSBzaWduaWZpY2FudCBwcmVkaWN0b3Igd2l0aCBwIDwgMmUtMTYgd2hpY2ggbWVhbnMgd2UgZmFpbCB0byByZWplY3QgdGhlIG51bGwgYXQgdGhlIDAuMDUgYWxwaGEgbGV2ZWwuIEhvd2V2ZXIgdGhlIHNlY29uZCBwcmluY2lwYWwgY29tcG9uZW50IFBDMiB3YXMgbm90IHNpZ25pZmljYW50IHdpdGggYSBwLXZhbHVlIG9mIDAuOTc2LiBUaGlzIGluZGljYXRlZCB0aGF0IGEgbWFqb3JpdHkgb2YgdGhlIHNlcGFyYXRpb24gYmV0d2VlbiB0aGUgbWFsaWduYW50IGFuZCBiZW5pZ24gdHVtb3JzIGlzIGRvbmUgd2l0aCB0aGUgZmlyc3QgcHJpbmNpcGFsIGNvbXBvbmVudC4gVGhlIG1vZGVsIHdhcyBldmFsdWF0ZWQgb24gdGhlIHRlc3Rpbmcgc3Vic2V0IG9mIHRoZSBkYXRhIHNldCBhbmQgaXQgaGFkIGEgaGlnaCBBVUMgb2YgMC45ODg1LiBUaGlzIHNob3dzIHRoYXQgdGhlIFBDQSBmZWF0dXJlcyBkbyBub3QgbmVnYXRpdmVseSBpbXBhY3QgdGhlIHByZWRpY3RpdmUgYWNjdXJhY3kgb2YgdGhlIG1vZGVsLiAKCmBgYHtyfQoKIyBVc2UgdGhlIHNhbWUgc2NhbGVkIGRhdGEgdXNlZCBmb3IgUENBCnBjYV9tb2RlbCA8LSBwcmNvbXAoY2FuY2VyX3NjYWxlZCwgY2VudGVyPVRSVUUsIHNjYWxlLj1UUlVFKQoKIyBLZWVwIGZpcnN0IHR3byBQQ3MgKGFkanVzdCBudW1iZXIgaWYgeW91IHByZWZlcikKdHJhaW5fcGNhIDwtIGRhdGEuZnJhbWUoCiAgT3V0Y29tZTAxID0gdHJhaW4kT3V0Y29tZTAxLAogIHByZWRpY3QocGNhX21vZGVsLCBuZXdkYXRhPXRyYWluKVssIDE6Ml0KKQoKdGVzdF9wY2EgPC0gZGF0YS5mcmFtZSgKICBPdXRjb21lMDEgPSB0ZXN0JE91dGNvbWUwMSwKICBwcmVkaWN0KHBjYV9tb2RlbCwgbmV3ZGF0YT10ZXN0KVssIDE6Ml0KKQoKIyBMb2dpc3RpYyBSZWdyZXNzaW9uIHdpdGggUENBIEZlYXR1cmVzCmxvZ2l0X3BjYSA8LSBnbG0oT3V0Y29tZTAxIH4gUEMxICsgUEMyLCBkYXRhPXRyYWluX3BjYSwgZmFtaWx5PWJpbm9taWFsKQoKc3VtbWFyeShsb2dpdF9wY2EpCgpwY2FfcHJvYnMgPC0gcHJlZGljdChsb2dpdF9wY2EsIG5ld2RhdGE9dGVzdF9wY2EsIHR5cGU9InJlc3BvbnNlIikKcm9jX3BjYSA8LSByb2ModGVzdF9wY2EkT3V0Y29tZTAxLCBwY2FfcHJvYnMpCnJvY19wY2EkYXVjCgoKYGBgCgoKVGhlIHJhbmRvbSBmb3Jlc3QgbW9kZWwgd2FzIGFsc28gYnVpbHQgdXRpbGl6aW5nIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgUEMxIGFuZCBQQzIuIFRoaXMgbW9kZWwgd2FzIHZlcnkgc3Ryb25nIGluIHRlcm1zIG9mIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuIFRoZSBvdmVyYWxsIGFjY3VyYWN5IGlzIDk1LjMzJSwgbWVhbmluZyBhIG1ham9yaXR5IG9mIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlIHRlc3QgZGF0ZSB3ZXJlIHByb3Blcmx5IGNsYXNzaWZpZWQuIFRoZSBzZW5zaXRpdml0eSB3YXMgMSB3aGljaCBtZWFucyAxMDAlIG9mIHRoZSBtYWxpZ25hbnQgb2JzZXJ2YXRpb25zIHdlcmUgcHJvcGVybHkgY2xhc3NpZmllZCwgd2hpY2ggY29uc2lkZXJpbmcgdGhlIHJlYWwgbGlmZSBjb250ZXh0IG9mIHRoaXMgZGF0YSwgaXMgYW1hemluZy4gVGhlIHNwZWNpZmljaXR5IGlzIGEgbGl0dGxlIGxvd2VyIGJ1dCBzdGlsbCB2ZXJ5IGhpZ2ggb3ZlcmFsbCBhdCAwLjkzMDcuIFRoaXMgbWVhbnMgOTMuMDclIG9mIHRoZSBiZW5pZ24gb2JzZXJ2YXRpb25zIHdlcmUgY29ycmVjdGx5IGNsYXNzaWZpZWQuIE92ZXJhbGwsIHRoZSByYW5kb20gZm9yZXN0IGhhZCBpbmNyZWRpYmx5IHN0cm9uZyByZXN1bHRzLiBUaGUgbW9kZWwgaGFkIGFuIEFVQyBvZiAwLjk3OTcgd2hpY2ggaXMgb25seSBzbGlnaHRseSBsb3dlciB0aGFuIHRoZSBsb2dpc3RpYyBtb2RlbC4gSG93ZXZlciBib3RoIG1vZGVscyBhcmUgdmVyeSBzdHJvbmcgYW5kIGFyZSBkbyBhIGdyZWF0IGpvYiBvZiBwcm9wZXJseSBjbGFzc2lmeWluZyBvYnNlcnZhdGlvbnMuIAoKYGBge3J9CgojIFJhbmRvbSBGb3Jlc3Qgd2l0aCBQQ0EgRmVhdHVyZXMKc2V0LnNlZWQoMTIzKQoKcmZfcGNhIDwtIHJhbmRvbUZvcmVzdCgKICBmYWN0b3IoT3V0Y29tZTAxKSB+IFBDMSArIFBDMiwKICBkYXRhPXRyYWluX3BjYSwKICBudHJlZT0zMDAKKQoKcmZfcGNhX3Byb2JzIDwtIHByZWRpY3QocmZfcGNhLCBuZXdkYXRhPXRlc3RfcGNhLCB0eXBlPSJwcm9iIilbLDJdCnJmX3BjYV9wcmVkICA8LSBwcmVkaWN0KHJmX3BjYSwgbmV3ZGF0YT10ZXN0X3BjYSwgdHlwZT0iY2xhc3MiKQoKY29uZnVzaW9uTWF0cml4KHJmX3BjYV9wcmVkLCBmYWN0b3IodGVzdF9wY2EkT3V0Y29tZTAxKSwgcG9zaXRpdmU9IjEiKQoKcm9jX3JmX3BjYSA8LSByb2ModGVzdF9wY2EkT3V0Y29tZTAxLCByZl9wY2FfcHJvYnMpCnJvY19yZl9wY2EkYXVjCgoKYGBgCkJvdGggdGhlIFBDQSBsb2dpc3RpYyBhbmQgUENBIHJhbmRvbSBmb3Jlc3QgbWV0aG9kcyBhY2hpZXZlIGhpZ2ggQVVDcyBzaG93aW5nIHRoYXQgdGhleSBhcmUgbm90IG5lZ2F0aXZlbHkgaW1wYWN0aW5nIHRoZSBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlLiBJdCBzaG91bGQgYmUgbm90ZWQgdGhhdCBvbmx5IHR3byBQQ3Mgd2VyZSB1dGlsaXplZCBpbiB0aGlzIGFuYWx5c2lzLiBUaGVyZSB3ZXJlIDkgUENzIGFuZCBpdCB3YXMgbXkgcGVyc29uYWwgZGVjaXNpb24gdG8gdXNlIDIgYXMgdGhlIG9wdGltYWwgYW1vdW50IG9mIFBDcyBmb3IgdGhpcyBhbmFseXNpcyBvbiB0aGlzIGRhdGEgc2V0LiBTb21lb25lIGNvdWxkIGRpc2FncmVlIHdpdGggdGhlIG9wdGltYWwgYW1vdW50IG9mIFBDcyBhbmQgcmVydW4gdGhpcyBhbmFseXNpcyB3aXRoIGEgZGlmZmVybnQgYW1vdW50IGFuZCBkaXNjb3ZlciBkaWZmZXJlbnQgcmVzdWx0cy4gCgoKIyMgQ2x1c3RlcmluZyAKCiAgSy1NZWFucyBhbmQgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcgbWV0aG9kcyBhcmUgdHdvIG1ldGhvZHMgb2YgdW5zdXBlcnZpc2VkIGxlYXJuaW5nIHRoYXQgaGVscCBpZGVudGlmeSBuYXR1cmFsIGdyb3VwaW5ncyBpbiBkYXRhLiBUaGUgay1tZWFucyBtZXRob2QgZm9jdXNlcyBvbiBwYXJ0aXRpb25pbmcgdGhlIGRhdGEgaW50byAoaykgbnVtYmVyIG9mIGNsdXN0ZXJzLiBJbiB0aGlzIGFuYWx5c2lzIHRoZXJlIHdpbGwgYmUgdGhlIHBsb3R0aW5nIG9mIGEgY2hhcnQga25vd24gYXMgdGhlICJlbGJvdyBtZXRob2QiIHRoYXQgd2lsbCBoZWxwIGFkdmlzZSB0aGUgb3B0aW1hbCBudW1iZXIgb2YgKGspIGNsdXN0ZXJzLiBBZnRlciB0aGUgb3B0aW1hbCBudW1iZXIgb2YgY2x1c3RlcnMgaGF2ZSBiZWVuIGRldGVybWluZSBhIGdyYXBoIG9mIHRoZSBkYXRhIHdpbGwgYmUgc2hvd2Nhc2VkIHNob3dpbmcgdGhlIGRhdGEgZGl2aWRlZCBpbnRvIHRob3NlIGNsdXN0ZXJzLiBNb3Zpbmcgb24gdG8gdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHRoaXMgaXMgYWxtb3N0IHJlbWluaXNjZW50IG9mIGRlY2lzaW9uIHRyZWUgbW9kZWxpbmcuIFRoaXMgcHJvY2VzcyBjcmVhdGVkIG5lc3RlZCB0cmVlcyBjYWxsZWQgZGVuZG9ncmFtcyBhbmQgZ3JvdXBzIHRvZ2V0aGVyIGRhdGEgYmFzZWQgb24gdGhlaXIgc2ltaWxhcml0aWVzLiBEZXNwaXRlIGJvdGggYmVpbmcgY2x1c3RlcmluZyBtZXRob2RzIHRoZSBoYXZlIGRpZmZlcmVuY2VzIHRoYXQgbWFrZSB0aGVtIHVuaXF1ZSBhbmQgYm90aCBiZW5lZmljaWFsIHNvIGJvdGggbWV0aG9kcyB3aWxsIGJlIHVzZWQgaW4gdGhlIGFuYWx5c2lzIGZvciB0aGlzIGFzc2lnbm1lbnQuIAoKCmBgYHtyfQoKIyBLLU1lYW5zIENsdXN0ZXJpbmcKCgojIERldGVybWluZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycyAoZWxib3cgbWV0aG9kKQpmdml6X25iY2x1c3QoY2FuY2VyX3NjYWxlZCwga21lYW5zLCBtZXRob2QgPSAid3NzIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIsIGxpbmV0eXBlID0gMikgKwogIGxhYnModGl0bGUgPSAiRWxib3cgTWV0aG9kIGZvciBPcHRpbWFsIGsiKQoKIyBSdW4gSy1tZWFucyB3aXRoIGsgPSAyCnNldC5zZWVkKDEyMykKa21lYW5zX3Jlc3VsdCA8LSBrbWVhbnMoY2FuY2VyX3NjYWxlZCwgY2VudGVycyA9IDIsIG5zdGFydCA9IDI1KQoKIyBWaXN1YWxpemUgSy1tZWFucyBjbHVzdGVycwpmdml6X2NsdXN0ZXIoa21lYW5zX3Jlc3VsdCwgZGF0YSA9IGNhbmNlcl9zY2FsZWQsCiAgICAgICAgICAgICBwYWxldHRlID0gYygiIzJFOUZERiIsICIjRkM0RTA3IiksCiAgICAgICAgICAgICBlbGxpcHNlLnR5cGUgPSAibm9ybSIsCiAgICAgICAgICAgICBnZW9tID0gInBvaW50IiwKICAgICAgICAgICAgIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCksCiAgICAgICAgICAgICBtYWluID0gIkstTWVhbnMgQ2x1c3RlcmluZyAoayA9IDIpIikKCiMgQ29tcGFyZSBLLW1lYW5zIGNsdXN0ZXJzIHdpdGggdHJ1ZSBPdXRjb21lCnRhYmxlKGttZWFuc19yZXN1bHQkY2x1c3RlciwgY2FuY2VyJE91dGNvbWUpCmBgYAogIFN0YXJ0aW5nIHdpdGggdGhlIG91dHB1dCBmcm9tIHRoZSBrLW1lYW5zIGNsdXN0ZXJpbmcgYW5hbHlzaXMsIHRoZSAiZWxib3cgbWV0aG9kIiByZWNvbW1lbmRzIGEgY3V0b2ZmIGF0IDIgY2x1c3RlcnMgb3Igaz0yLiBVc2luZyB0aGlzIGN1dG9mZiBhIGNsdXN0ZXJpbmcgZ3JhcGggaXMgY3JlYXRlZCB3aGVyZSBibHVlIGlzIGFsbCB0aGUgZGF0YSBpbiB0aGUgZmlyc3QgY2x1c3RlciBhbmQgMiBpcyBhbGwgdGhlIGRhdGEgaW4gdGhlIHNlY29uZCBjbHVzdGVyLiBMb29raW5nIGF0IHRoaXMgZ3JhcGggaXQgaXMgY2xlYXIgdGhlIGZpcnN0IGNsdXN0ZXIgaXMgd2lkZXIgYW5kIGxhcmdlciBhbmQgY2FwdHVyZXMgbW9yZSBzcHJlYWQgb3V0IGRhdGEgd2hpbGUgY2x1c3RlciAyIGlzIHNtYWxsZXIgYW5kIG5hcnJvd2VyIGFuZCBjYXB0dXJlcyBkYXRhIHBvaW50cyB0aGF0IGFyZSBhbG1vc3Qgb24gdG9wIG9mIGVhY2ggb3RoZXIuIFRoZXJlIGlzIGFsc28gb3V0cHV0IHRoYXQgaW5kaWNhdGVzIGhvdyBtYW55IG1hbGlnbmFudCBhbmQgbm9uIG1hbGlnbmFudCBjYXNlcyB3ZXJlIHB1dCBpbnRvIGVhY2ggY2x1c3Rlci4gQ2x1c3RlciAxIGhhcyAyMDcgbWFsaWduYW50IGNhc2VzIGFuZCAxMiBiZW5pZ24gY2FzZXMgd2hpbGUgaW4gY2x1c3RlciAyIHRoZXJlIGFyZSAzODYgYmVuaWduIGNhc2VzIGFuZCAxMyBtYWxpZ25hbnQgY2FzZXMuIFRoaXMgc2hvd3MgdGhhdCB0aGUgY2x1c3RlcnMgYXJlIGVmZmVjdGl2ZWx5IGNhcHR1cmluZyB0aGUgc2VwYXJhdGlvbiBiZXR3ZWVuIHRoZSBtYWxpZ25hbnQgYW5kIGJlbmlnbiB0dW1vcnMuIFdoaWxlIHRoZXJlIGFyZSBhIGZldyBtaXNjbGFzc2lmaWNhdGlvbnMgY29uc2lkZXJpbmcgdGhpcyBpcyBvbmx5IGEgMiBjbHVzdGVyIG1ldGhvZCBhIGZldyBtaXNjbGFzc2lmaWNhdGlvbnMgYXJlIHRvIGJlIGV4cGVjdGVkLiBPdmVyYWxsIGEgbWFqb3JpdHkgb2YgdGhlIG9ic2VydmF0aW9ucyBhcmUgcHJvcGVybHkgY2xhc3NpZmllZCBpbmRpY2F0aW5nIGEgc3Ryb25nIGstbWVhbnMgY2x1c3RlcmluZyBhbmFseXNpcy4gCgoKYGBge3J9CiMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcKCiMgQ29tcHV0ZSBkaXN0YW5jZSBtYXRyaXgKZGlzdF9tYXQgPC0gZGlzdChjYW5jZXJfc2NhbGVkLCBtZXRob2QgPSAiZXVjbGlkZWFuIikKCiMgUGVyZm9ybSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZwpoYyA8LSBoY2x1c3QoZGlzdF9tYXQsIG1ldGhvZCA9ICJ3YXJkLkQyIikKCiMgUGxvdCBkZW5kcm9ncmFtCiMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcgCgojIENvbnZlcnQgc2NhbGVkIGRhdGEgdG8gZGF0YSBmcmFtZQpzY2FsZXMuaGllcmFyY2ggPC0gYXMuZGF0YS5mcmFtZShjYW5jZXJfc2NhbGVkKQoKIyBDb21wdXRlIGRpc3RhbmNlIG1hdHJpeCB1c2luZyBFdWNsaWRlYW4gZGlzdGFuY2UKZGlzdGFuY2UgPC0gZGlzdChzY2FsZXMuaGllcmFyY2gsIG1ldGhvZCA9ICJldWNsaWRlYW4iKQoKIyBQZXJmb3JtIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHVzaW5nIENvbXBsZXRlIExpbmthZ2UKaGMxIDwtIGhjbHVzdChkaXN0YW5jZSwgbWV0aG9kID0gImNvbXBsZXRlIikKCiMgQmFzaWMgZGVuZHJvZ3JhbSB3aXRoIHJlY3RhbmdsZXMKcGxvdChoYzEsCiAgICAgY2V4ID0gMC42LAogICAgIGxhYmVscyA9IEZBTFNFLAogICAgIGhhbmcgPSAtMSwKICAgICB4bGFiID0gIiIsCiAgICAgbWFpbiA9ICJEZW5kcm9ncmFtOiBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyAoQ29tcGxldGUgTGlua2FnZSkiKQoKIyBEcmF3IHJlY3RhbmdsZXMgZm9yIDIgY2x1c3RlcnMgKHlvdSBjYW4gY2hhbmdlIHRvIDUgaWYgZGVzaXJlZCkKcmVjdC5oY2x1c3QoaGMxLCBrID0gMiwgYm9yZGVyID0gYygiIzAwQUZCQiIsICIjRkM0RTA3IikpCgoKaGNfY2x1c3RlcnMgPC0gY3V0cmVlKGhjLCBrID0gMikKCgojIENvbXBhcmUgaGllcmFyY2hpY2FsIGNsdXN0ZXJzIHRvIHRydWUgT3V0Y29tZQp0YWJsZShoY19jbHVzdGVycywgY2FuY2VyJE91dGNvbWUpCgoKYGBgCgogIFNpbWlsYXIgdG8gdGhlIHJlc3VsdHMgaW4gdGhlIGstbWVhbnMgY2x1c3RlcmluZyBvdXRwdXQgdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGFsc28gdXRpbGl6ZXMgdHdvIGdyb3Vwcy4gSG93ZXZlciwgdGhlcmUgaXMgYSBzd2l0Y2ggaW4gdGhpcyBjbHVzdGVyIGNvbXBhcmVkIHRvIHRoZSBrLW1lYW5zLiBJbnN0ZWFkIG9mIHRoZSBtYWxpZ25hbnQgdHVtb3JzIGJlaW5nIG1haW5seSBjYXB0dXJlZCBpbiB0aGUgZmlyc3QgY2x1c3RlciB3ZSBzZWUgdGhlIGJlbmlnbiB0dW1vcnMgYmVpbmcgY2FwdHVyZWQgaW4gdGhpcyBjbHVzdGVyLiBUaGUgZmlyc3QgY2x1c3RlciBoYXMgMzU4IGJlbmlnbiBjYXNlcyBhbmQgNSBtYWxpZ25hbnQsIHdoaWxlIHRoZSBzZWNvbmQgY2x1c3RlciBoYXMgMjIgYmVuaWduIGNhc2VzIGFuZCAyMTUgbWFsaWduYW50IGNhc2VzLiBBZ2FpbiwgdGhlcmUgaXMgc29tZSBtaXNjbGFzc2lmaWNhdGlvbiBidXQgb3ZlcmFsbCB0aGUgYW1vdW50IG9mIG1pc2NsYXNzaWZpY2F0aW9uIGlzIG5vdCBjb25jZXJuaW5nIGNvbnNpZGVyaW5nIHRoZSBzaXplIG9mIHRoZSBkYXRhIHNldC4gT3ZlcmFsbCwgdGhpcyBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtb2RlbCBzZWVtcyB0byBiZSB2ZXJ5IHN0cm9uZyBqdXN0IGxpa2UgdGhlIGstbWVhbnMgbW9kZWwuIAoKIyMjIyBDbHVzdGVyaW5nIENsYXNzaWZpY2F0aW9uIE1vZGVscwoKQSBsb2dpc3RpYyBtb2RlbCB1c2luZyB0aGUgSy1tZWFucyBjbHVzdGVyaW5nIG1ldGhvZCB3YXMgY3JlYXRlZC4gVGhlIHByZWRpY3RpdmUgcGVyZm9ybWFuY2Ugd2FzIHN0cm9uZywgaW5kaWNhdGluZyB0aGUgdHdvIGNsdXN0ZXJzIGhhdmUgY2FwdHVyZWQgYSBzaWduaWZpY2FudCBzZXBhcmF0aW9uIGJldHdlZW4gbWFsaWduYW50IGFuZCBiZW5pZ24gb2JzZXJ2YXRpb25zLiBUaGUgY29lZmZpY2llbnQgQ2x1c3RlcksyMiBpcyBoaWdobHkgc2lnbmlmaWNhbnQgd2l0aCBhIHAtdmFsdWUgPCAyZS0xNiB3aGljaCBtZWFucyB3ZSB3b3VsZCByZWplY3QgdGhlIG51bGwgYXQgdGhlIDAuMDUgYWxwaGEgbGV2ZWwuIFRoaXMgaXMgaW5kaWNhdGl2ZSBvZiBjbHVzdGVyIHR3byBoYXZpbmcgc2lnbmlmaWNhbnRseSBsb3dlciBvZGRzIG9mIGJlaW5nIG1hbGlnbmFudCBjb21wYXJlZCB0byBjbHVzdGVyIG9uZS4gRXNzZW50aWFsbHkgdGhpcyBtZWFucyBtb3N0IG1hbGlnbmFudCBvYnNlcnZhdGlvbnMgYXJlIG1vc3QgaW4gY2x1c3RlciBvbmUgd2hpbGUgdGhlIG1ham9yaXR5IG9mIGJlbmlnbiBvYnNlcnZhdGlvbnMgYXJlIGluIGNsdXN0ZXIgMi4gVGhlIG1vZGVsIGFsc28gaGFzIGEgc3Ryb25nIEFVQyBhdCAwLjk0NDYgd2hpY2ggaXMgaW5jcmVkaWJseSBnb29kLiBUaGlzIG1lYW5zIHdpdGhvdXQgdGhlIG9yaWdpbmFsIGRhdGEgc2V0IHZhcmlhYmxlcyB0aGUgY2x1c3RlciBhbG9uZSBwcm92aWRlcyBzaWduaWZpY2FudCBkaWZmZXJudGlhdGlvbiBvZiB0aGUgdHdvIG9ic2VydmF0aW9uIHR5cGVzLCBpbmRpY2F0aW5nIGEgbWVhbmluZ2Z1bCBwYXR0ZXJuLCAKCgpgYGB7cn0KCnNldC5zZWVkKDEyMykKa21lYW5zX21vZGVsIDwtIGttZWFucyhjYW5jZXJfc2NhbGVkLCBjZW50ZXJzPTIsIG5zdGFydD0yNSkKCiMgQWRkIGNsdXN0ZXIgbGFiZWxzCmNhbmNlciRDbHVzdGVySzIgPC0gZmFjdG9yKGttZWFuc19tb2RlbCRjbHVzdGVyKQoKIyBBbGlnbiB0cmFpbi90ZXN0IHNwbGl0cwp0cmFpbiRDbHVzdGVySzIgPC0gY2FuY2VyJENsdXN0ZXJLMltpZHhdCnRlc3QkQ2x1c3RlcksyICA8LSBjYW5jZXIkQ2x1c3RlcksyWy1pZHhdCgojIExvZ2lzdGljIFJlZ3Jlc3Npb24gdXNpbmcgQ2x1c3RlciBMYWJlbApsb2dpdF9jbHVzdGVyIDwtIGdsbShPdXRjb21lMDEgfiBDbHVzdGVySzIsIGRhdGE9dHJhaW4sIGZhbWlseT1iaW5vbWlhbCkKCnN1bW1hcnkobG9naXRfY2x1c3RlcikKCmNsdXN0ZXJfcHJvYnMgPC0gcHJlZGljdChsb2dpdF9jbHVzdGVyLCBuZXdkYXRhPXRlc3QsIHR5cGU9InJlc3BvbnNlIikKcm9jX2NsdXN0ZXIgPC0gcm9jKHRlc3QkT3V0Y29tZTAxLCBjbHVzdGVyX3Byb2JzKQpyb2NfY2x1c3RlciRhdWMKCgpgYGAKCgpOb3cgbG9va2luZyBhdCB0aGUgcmFuZG9tIGZvcmVzdCB0cmFpbmVkIHdpdGggdGhlIGNsdXN0ZXIgaXQgc2hvd3MgYSBzaW1pbGFybHkgc3Ryb25nIHBlcmZvcm1hbmNlLiBUaGUgcmFuZG9tIGZvcmVzdCBmb3IgY2x1c3RlcmluZyBoYXMgYSBzZW5zaXRpdml0eSBvZiAwLjkzODggd2hpY2ggbWVhbnMgaXQgY29ycmVjdGx5IGNsYXNzaWZpZXMgOTMuODglIG9mIHRoZSBtYWxpZ25hbnQgb2JzZXJ2YXRpb25zLiBTaW1pbGFybHkgdGhlIHNwZWNpZmljaXR5IGlzIDAuOTUwNSB3aGljaCBtZWFucyBpdCBjb3JyZWN0bHkgY2xhc3NpZmllcyA5NS4wNSUgb2YgdGhlIGJlbmlnbiBjYXNlcy4gVGhpcyBtb2RlbCBoYXMgdGhlIHNhbWUgQVVDIGFzIHRoZSBsb2dpc3RpYyBtb2RlbCB3aXRoIDAuOTQ0NiwgYWdhaW4gc3RpbGwgYSB2ZXJ5IHN0cm9uZyBBVUMuIFdoaWxlIG5vdCBhcyBzdHJvbmcgYXMgdGhlIGZ1bGwgbW9kZWwgd2l0aCBhbGwgdGhlIHByZWRpY3RvcnMsIGZvciBvbmx5IGhhdmluZyBvbmUgZmVhdHVyZSB0aGlzIG1vZGVsIGhhcyBpbmNyZWRpYmx5IHN0cm9uZyBwcmVkaWN0aXZlIHBvd2VyIGFuZCBwcm9kdWNlcyBtZWFuaW5nZnVsIHJlc3VsdHMuCgpgYGB7cn0KCiMgUmFuZG9tIEZvcmVzdCB3aXRoIENsdXN0ZXJpbmcgRmVhdHVyZQpzZXQuc2VlZCgxMjMpCgpyZl9jbHVzdGVyIDwtIHJhbmRvbUZvcmVzdCgKICBmYWN0b3IoT3V0Y29tZTAxKSB+IENsdXN0ZXJLMiwKICBkYXRhPXRyYWluLAogIG50cmVlPTMwMAopCgpyZl9jbHVzdGVyX3Byb2JzIDwtIHByZWRpY3QocmZfY2x1c3RlciwgbmV3ZGF0YT10ZXN0LCB0eXBlPSJwcm9iIilbLDJdCnJmX2NsdXN0ZXJfcHJlZCA8LSBwcmVkaWN0KHJmX2NsdXN0ZXIsIG5ld2RhdGE9dGVzdCwgdHlwZT0iY2xhc3MiKQoKY29uZnVzaW9uTWF0cml4KHJmX2NsdXN0ZXJfcHJlZCwgZmFjdG9yKHRlc3QkT3V0Y29tZTAxKSwgcG9zaXRpdmU9IjEiKQoKcm9jX3JmX2NsdXN0ZXIgPC0gcm9jKHRlc3QkT3V0Y29tZTAxLCByZl9jbHVzdGVyX3Byb2JzKQpyb2NfcmZfY2x1c3RlciRhdWMKCgpgYGAKCgojIyBMT0YKCiAgTG9jYWwgT3V0bGllciBGYWN0b3IsIGFsc28ga25vd24gYXMgTE9GIGlzIGEgbWV0aG9kIHVzZWQgdG8gaWRlbnRpZnkgYW5vbWFsaWVzIGluIHRoZSBkYXRhIHNldC4gVGhpcyBpcyBkb25lIGJ5IGNvbXBhcmluZyB0aGUgZGVuc2l0aWVzIG9mIGxvY2FsIGRhdGEgcG9pbnRzIGFuZCBpZiB0aGUgZGVuc2l0eSBvZiBhIGRhdGEgcG9pbnQgaXMgc2lnbmlmaWNhbnRseSBsb3dlciB0aGFuIHRoZSBkYXRhIHBvaW50cyBhd2FyZSBpdCB0aGUgcG9pbnQgaXMgZmxhZ2dlZCBhcyBhbiBvdXRsaWVyLiBJbiBvcmRlciB0byBiZWdpbiB0aGlzIHByb2Nlc3MgYSBzZWNvbmQgdmVyc2lvbiBvZiB0aGUgb3V0Y29tZSB2YXJpYWJsZSBpcyBnb2luZyB0byBiZSBjcmVhdGVkLiBJbiB0aGUgb3JpZ2luYWwgZGF0YSBzZXQgdGhlIG91dGNvbWUgdmFyaWFibGUgaXMgYSBjaGFyYWN0ZXIgdmFyaWFibGUgbGlzdGVkIGFzICJZZXMiIG9yICJObyIgYnV0IGluIG9yZGVyIHRvIHV0aWxpemUgdGhlIExPRiBtZXRob2QgdGhlIGFsbCB0aGUgdmFyaWFibGVzIGJlaW5nIHV0aWxpemVkIG5lZWQgdG8gYmUgbnVtZXJpYy4gVGhlIG91dGNvbWUgdmFyaWFibGUgd2lsbCBiZSBjb252ZXJ0ZWQgdG8gbnVtZXJpYyB3aGVyZSAwID0gIk5vIiBhbmQgMSA9ICJZZXMiLiBUaGVuIGFsbCB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgYXJlIGdvaW5nIHRvIGJlIHNjYWxlZCBiZWNhdXNlIExPRiB1dGlsaXplZCBFdWNsaWRlYW4gZGlzdGFuY2VzIHNvIGlmIHRoZSB2YXJpYWJsZXMgd2VyZSB1bnNjYWxlZCB0aGUgdmFyaWFibGVzIHdpdGggbGFyZ2VyIHJhbmdlcyB3b3VsZCBkb21pbmF0ZSB0aGUgY29tcHV0YXRpb24uIAoKYGBge3J9CgojIENvbnZlcnQgT3V0Y29tZSB0byBiaW5hcnkgbnVtZXJpYzogWWVzID0gMSwgTm8gPSAwCgoKCiMgSWRlbnRpZnkgbnVtZXJpYyB2YXJpYWJsZXMKbnVtZXJpY192YXJzIDwtIGNhbmNlciAlPiUgc2VsZWN0KHdoZXJlKGlzLm51bWVyaWMpKQoKIyBSZW1vdmUgdGhlIG91dGNvbWUgc28gTE9GIGlzIHB1cmVseSB1bnN1cGVydmlzZWQKbnVtZXJpY192YXJzIDwtIG51bWVyaWNfdmFycyAlPiUgc2VsZWN0KC1PdXRjb21lMDEpCgojIFNjYWxlIHRoZSBudW1lcmljIHZhcmlhYmxlcwpudW1lcmljX3NjYWxlZCA8LSBzY2FsZShudW1lcmljX3ZhcnMpCgoKYGBgCgoKICBOb3cgdGhhdCBhbGwgdGhlIG51bWVyaWNhbCB2YXJpYWJsZXMgaGF2ZSBiZWVuIHNjYWxlZCBpdCBpcyB0aW1lIHRvIGNvbXB1dGUgdGhlIHNjb3Jlcy4gQWxsIHRoZSBvYnNlcnZhdGlvbnMgd2lsbCBiZSBhc3NpZ25lZCBhbiBvdXRsaWVyIHNjb3JlIGJhc2VkIG9uIGhvdyBpc29sYXRlZCBpdCBpcyBjb21wYXJlZCB0byBsb2NhbCBkYXRhIHBvaW50cy4gVGhlIGhpZ2hlciB0aGUgTE9GIHZhbHVlIHRoZSBtb3JlIGxpa2VseSBpdCBpcyB0byBiZSBhIHBvdGVudGlhbGx5IGFub21hbHkuIFRvIHZpc3VhbGl6ZSB0aGVzZSBwb3RlbnRpYWwgb3V0bGllcnMgdGhlIExPRiBoaXN0b2dyYW0gd2FzIGdlbmVyYXRlZC4gTG9va2luZyBhdCB0aGUgaGlzdG9ncmFtIG1vc3Qgb2YgdGhlIExPRiBzY29yZXMgYXBwZWFyIHRvIGJlIHVuZGVyIDEuNSB3aXRoIHZlcnkgZmV3IHNjb3JlcyBzdXJwYXNzaW5nIHRoYXQgYW5kIGV2ZW4gZmV3ZXIgc3VycGFzc2luZyAyIGFuZCBub25lIG9mIHRoZSBzY29yZXMgc3VycGFzc2luZyAzLiBMb29raW5nIGF0IExPRiBhIHNjb3JlIG9mIDEgb3IgdmVyeSBjbG9zZSB0byAxIGlzIGlkZWFsLCBhbmQgbG9va2luZyBhdCB0aGUgc3VtbWFyeSBhbmQgaGlzdG9ncmFtIGlzIHNlZW1zIGxpa2UgYSBtYWpvcml0eSBvZiBvdXIgb2JzZXJ2YXRpb25zIGFyZSBtZWV0aW5nIHRoaXMgY3JpdGVyaWEgd2hpY2ggaXMgd2hhdCB3ZSB3YW50IHRvIHNlZS4gCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQprIDwtIDIwICAKCmxvZl9zY29yZXMgPC0gbG9mKG51bWVyaWNfc2NhbGVkLCBtaW5QdHMgPSBrKQoKCgpjYW5jZXIkTE9GX2syMCA8LSBsb2Zfc2NvcmVzCgp0cmFpbiA8LSBjYW5jZXJbaWR4LCBdCnRlc3QgPC0gY2FuY2VyWy1pZHgsIF0KCgpzdW1tYXJ5KGNhbmNlciRMT0ZfazIwKQoKaGlzdChjYW5jZXIkTE9GX2syMFtjYW5jZXIkTE9GX2syMCA8IDVdLAogICAgIGJyZWFrcyA9IDQwLAogICAgIG1haW4gPSBwYXN0ZSgiTE9GIERpc3RyaWJ1dGlvbiAoayA9IiwgaywgIikiKSwKICAgICB4bGFiID0gIkxPRiBTY29yZSIpCgoKYGBgCgogIE1vdmluZyBvbnRvIHRoZSBjdXRvZmYgYW5hbHlzaXMsIHRoaXMgYWxsb3dzIHVzIHRvIGRldGVybWluZSBob3cgZWZmZWN0aXZlbHkgdGhlIExPRiBpZGVudGlmaWVzIG1hbGlnbmFudCBjYXNlcyBhcyB0aGUgb3V0bGllciB0aHJlc2hvbGQgdmFyaWVzLiBMb29raW5nIGF0IHRoZSBmaXJzdCBncmFwaCBpdCBjYW4gYmUgc2VlbiB0aGF0IHdoZW4gdXNpbmcgdGhlIGxvd2VzdCBjdXRvZmYgYXJvdW5kIDEgdGhlIExPRiBvbmx5IGNhcHR1cmVzIGFyb3VuZCA0MCUgb2YgdGhlIG1hbGlnbmFudCBjYXNlcywgYW5kIHRoYXQgb25seSBkcm9wcyBkcmFtYXRpY2FsbHkgdG8gYXJvdW5kIDAlIGFzIHdlIGdldCBjbG9zZXIgdG8gYSBjdXRvZmYgb2YgMi4gVGhlIHNlY29uZCBncmFwaCBwcm92aWRlcyBzaW1pbGFyIGluZm9ybWF0aW9uLCBzaG93aW5nIGEgbmVnYXRpdmUgaW1wcm92ZW1lbnQgcmF0aW8uIE92ZXJhbGwsIHRoZSBMT0Ygc2NvcmVzIGRvIG5vdCBoYXZlIHNpZ25pZmljYW50bHkgc2VwYXJhdGUgdGhlIGJlbmlnbiBjYXNlcyBmcm9tIHRoZSBtYWxpZ25hbnQgY2FzZXMuIE1lYW5pbmcgdGhlIG1hbGlnbmFudCBjYXNlcyBhcmUgbm90IHN0YW5kaW5nIG91dCBhcyBvdXRsaWVycyBpbiBhIG51bWVyaWMgc2Vuc2UsIHN1Z2dlc3RpbmcgTE9GIG1heSBub3QgYmUgYmVuZWZpY2lhbCBmb3IgdGhpcyBkYXRhIHNldC4gSG93ZXZlciwgd2UgYXJlIGdvaW5nIHRvIGNvbnRpbnVlIG91ciBhbmFseXNpcyBjYXVzZSB1c2VmdWwgaW5mb3JtYXRpb24gY291bGQgY29tZSBmcm9tIGl0IHRvIGhlbHAgdXMgbWFrZSBvdXIgZmluYWwgY29uY2x1c2lvbi4gCgoKYGBge3J9CiMgU2VxdWVuY2Ugb2YgTE9GIGN1dG9mZnMgPjEKY3V0b2ZmcyA8LSBzZXEoMSwgNSwgbGVuZ3RoID0gMjEpCgpsb2ZfcmF0ZSA8LSBOVUxMCgpmb3IgKGkgaW4gMTpsZW5ndGgoY3V0b2ZmcykpIHsKICBmbGFnIDwtIGNhbmNlciRMT0ZfazIwID4gY3V0b2Zmc1tpXQogIG1hbGlnbmFudCA8LSBjYW5jZXIkT3V0Y29tZTAxW2ZsYWddCiAgbG9mX3JhdGVbaV0gPC0gc3VtKG1hbGlnbmFudCkgLyBzdW0oZmxhZykKfQoKIyBCYXNlbGluZSBtYWxpZ25hbnQgcmF0ZQpiYXNlX3JhdGUgPC0gbWVhbihjYW5jZXIkT3V0Y29tZTAxKQoKcmVsYXRpdmVfaW1wcm92ZW1lbnQgPC0gKGxvZl9yYXRlIC0gYmFzZV9yYXRlKSAvIGJhc2VfcmF0ZQoKcGFyKG1mcm93PWMoMSwyKSkKCnBsb3QoY3V0b2ZmcywgMTAwKmxvZl9yYXRlLCB0eXBlPSJsIiwgbHdkPTIsCiAgICAgbWFpbj0iTE9GIG1hbGlnbmFudC1jYXRjaGluZyByYXRlIiwKICAgICB4bGFiPSJMT0YgY3V0b2ZmIiwgeWxhYj0iQ2F0Y2hpbmcgcmF0ZSAoJSkiKQoKcGxvdChjdXRvZmZzLCByZWxhdGl2ZV9pbXByb3ZlbWVudCwgdHlwZT0ibCIsIGx3ZD0yLAogICAgIG1haW49IlJlbGF0aXZlIGltcHJvdmVtZW50IHZzIGJhc2VsaW5lIiwKICAgICB4bGFiPSJMT0YgY3V0b2ZmIiwgeWxhYj0iSW1wcm92ZW1lbnQgcmF0aW8iKQoKCgpgYGAKCiAgSW4gb3JkZXIgdG8gZXZhbHVhdGUgTE9GIGFzIGEgZGV0ZWN0aW9uIHRvb2wgdGhlIFJPQyBjdXJ2ZSB3YXMgZ2VuZXJhdGVkIGFsb25nIHdpdGggQVVDIHNjb3Jlcy4gVW5mb3J0dW5hdGVseSBsb29raW5nIGF0IHRoZSBmaXJzdCBncmFwaCBmb3IgTE9GIERldGVjdGlvbiwgdGhlIEFVQyBpcyAwLjU5MjMsIGNvbnNpZGVyaW5nIHRoYXQgYW4gQVVDIG9mIDAuNTAgaXMgcmVwcmVzZW50YXRpdmUgb2YgZXNzZW50aWFsbHkgcmFuZG9tIGd1ZXNzaW5nIGEgMC41OTIzIEFVQyBpcyBub3QgdGhhdCBzdHJvbmcuIFRoaXMgc3VwcG9ydHMgdGhlIExPRiBjdXRvZmYgcmVzdWx0cyBvZiB0aGlzIHBvc3NpYmx5IG5vdCBiZWluZyB0aGUgYmVzdCBtZXRob2RvbG9neSBmb3IgdGhpcyBkYXRhIHNldC4gV2hlbiBsb29raW5nIGF0IHRoZSBkaWZmZXJlbnQgcG90ZW50aWFsIGstY3V0b2ZmcyB0aGVyZSBpcyBzb21lIGltcHJvdmVtZW50LCBoYXMgayBpbmNyZWFzZSBzbyBkb2VzIHRoZSBBVUMgZ29pbmcgZnJvbSAwLjU0OTIgd2hlbiBrPTEwIHRvIDAuNjc1OCB3aGVuIGs9NjAuIE92ZXJhbGwsIHRoZSByZXN1bHRzIGFyZSBpbmRpY2F0aW5nIHRoYXQgTE9GIGlzIG5vdCByaWdodCBmb3IgdGhpcyBkYXRhLiAKCmBgYHtyfQoKY2F0ZWdvcnkgPC0gYXMuY2hhcmFjdGVyKGNhbmNlciRPdXRjb21lMDEpCgpyb2NfbG9mIDwtIHJvYyhjYXRlZ29yeSwgY2FuY2VyJExPRl9rMjAsIGxldmVscz1jKCIxIiwiMCIpLCBkaXJlY3Rpb249Ij4iKQoKcGxvdCgxLXJvY19sb2Ykc3BlY2lmaWNpdGllcywgcm9jX2xvZiRzZW5zaXRpdml0aWVzLCB0eXBlPSJsIiwKICAgICBtYWluPSJST0MgQ3VydmU6IExPRiBEZXRlY3Rpb24iLAogICAgIHhsYWI9IjEgLSBTcGVjaWZpY2l0eSIsIHlsYWI9IlNlbnNpdGl2aXR5IikKc2VnbWVudHMoMCwwLDEsMSwgbHR5PTIsIGNvbD0icmVkIikKCnRleHQoMC44LCAwLjIsIHBhc3RlKCJBVUMgPSIsIHJvdW5kKHJvY19sb2YkYXVjLCA0KSksIGNvbD0iZGFya3JlZCIpCgoKa19saXN0IDwtIGMoMTAsIDIwLCA0MCwgNjApCgpsb2ZfbGlzdCA8LSBsYXBwbHkoa19saXN0LCBmdW5jdGlvbihrKSBsb2YobnVtZXJpY19zY2FsZWQsIG1pblB0cz1rKSkKCnJvY19saXN0IDwtIGxhcHBseShsb2ZfbGlzdCwgZnVuY3Rpb24oeCkgcm9jKGNhdGVnb3J5LCB4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKCIxIiwiMCIpLCBkaXJlY3Rpb249Ij4iKSkKCgpjb2xvcnMgPC0gYygiZGFya3JlZCIsICJuYXZ5IiwgInB1cnBsZSIsICJkYXJrZ3JlZW4iKQoKcGxvdCgxLXJvY19saXN0W1sxXV0kc3BlY2lmaWNpdGllcywgcm9jX2xpc3RbWzFdXSRzZW5zaXRpdml0aWVzLCB0eXBlPSJsIiwKICAgICBjb2w9Y29sb3JzWzFdLCBsd2Q9MiwKICAgICB4bGFiPSIxIC0gc3BlY2lmaWNpdHkiLCB5bGFiPSJzZW5zaXRpdml0eSIsCiAgICAgbWFpbj0iUk9DIENvbXBhcmlzb24gZm9yIERpZmZlcmVudCBrIikKCmZvciAoaSBpbiAyOmxlbmd0aChrX2xpc3QpKSB7CiAgbGluZXMoMS1yb2NfbGlzdFtbaV1dJHNwZWNpZmljaXRpZXMsIHJvY19saXN0W1tpXV0kc2Vuc2l0aXZpdGllcywKICAgICAgICBjb2w9Y29sb3JzW2ldLCBsd2Q9MikKfQoKbGVnZW5kKCJib3R0b21yaWdodCIsCiAgICAgICBsZWdlbmQ9cGFzdGUoImsgPSIsIGtfbGlzdCwKICAgICAgICAgICAgICAgICAgICAiQVVDPSIsIHJvdW5kKHNhcHBseShyb2NfbGlzdCwgZnVuY3Rpb24oeCkgeCRhdWMpLCA0KSksCiAgICAgICBjb2w9Y29sb3JzLCBsd2Q9MiwgY2V4PTAuOCwgYnR5PSJuIikKCgpgYGAKCgoKIyMjIEJpbmFyeSBDbGFzc2lmaWNhdGlvbiBNb2RlbHMKCiAgRGVzcGl0ZSB0aGUgTE9GIHJlc3VsdHMgaW5kaWNhdGluZyBpdCBtYXkgbm90IGJlIHRoZSBiZXN0IG1ldGhvZG9sb2d5IHRvIHVzZSBvbiB0aGlzIGRhdGEgc2V0IHRoZSBwcm9qZWN0IGlzIHN0aWxsIGdvaW5nIHRvIGJlIGNvbnRpbnVlZCBhcyBpbnRlbmRlZC4gQSBiaW5hcnkgY2xhc3NpZmljYXRpb24gbW9kZWwgaXMgZ29pbmcgdG8gYmUgdXRpbGl6ZWQgdG8gc2VlIGlmIHBhaXJpbmcgTE9GIHdpdGggYSBwcmVkaWN0aXZlIG1vZGVsIGlzIGJlbmVmaWNpYWwuIEJlZm9yZSBidWlsZGluZyB0aGlzIG1vZGVsIHRoZSBkYXRhIGlzIGdvaW5nIHRvIGJlIHNwbGl0IGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc3Vic2V0cyBzbyB0aGUgZmluYWwgbW9kZWwgY2FuIGJlIHRlc3RlZC4gCgogIFRoZSBwcm9qZWN0IGFza3MgZm9yIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIG1vZGVsIHRvIGJlIHRyYWluZWQgd2l0aG91dCBhbnkgZmVhdHVyZSBlbmdpbmVlcmluZywgdGhlbiB0cmFpbiB0aGVtIHdpdGggZmVhdHVyZSBlbmdpbmVlcmluZyBhbmQgY29tcGFyZSB0aGUgcmVzdWx0cy4gRmlyc3Qgd2UgYXJlIGdvaW5nIHRvIHN0YXJ0IHdpdGggdGhlIGJpbmFyeSBsb2dpc3RpYyByZWdyZXNzaW9uIHdpdGhvdXQgYW55IGZlYXR1cmUgZW5naW5lZXJpbmcsIGluIHRoaXMgY2FzZSB3aXRob3V0IExPRi4gVGhlbiB3ZSBhcmUgZ29pbmcgdG8gYWxzbyBydW4gdGhlIG1vZGVsIHdpdGggTE9GIHNvIHRoZSByZXN1bHRzIGNhbiBiZSBjb21wYXJlZC4gU3RhcnRpbmcgd2l0aCB0aGUgbW9kZWwgdGhhdCBkaWQgbm90IHV0aWxpemUgTE9GIHRoZXJlIGFyZSBzZXZlcmFsIHNpZ25pZmljYW50IHZhcmlhYmxlcyB0aG9zZSBiZWluZywgdGhpY2tuZXNzIG9mIGNsdW1wLCBjZWxsIHNoYXBlIGFuZCBjZWxsIHNpemUgdW5pZm9ybWl0eSwgYW5kIGJhcmUgbnVjbGVpYy4gVGhlIG1vZGVsIGFsc28gaGFkIGEgc3Ryb25nIHBlcmZvcm1hbmNlIHdpdGggYW4gQVVDIG9mIDAuOTg2Nywgd2hpY2ggaXMgZXh0cmVtZWx5IHN0cm9uZy4gSG93ZXZlciwgd2hlbiB1c2luZyB0aGUgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCB0aGF0IG9ubHkgdXRpbGl6ZXMgTE9GIHRoZSByZXN1bHRzIHdlcmUgZHJhc3RpY2FsbHkgZGlmZmVyZW50LiBUaGUgdmFyaWFibGUgTE9GX2syMCB3YXMgbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgd2l0aCBhIHAtdmFsdWUgb2YgMC43NDQsIHdoaWNoIGRvZXMgbm90IGNvbWUgY2xvc2UgdG8gYSBhbHBoYSBvZiAwLjA1IHRocmVzaG9sZC4gVGhpcyBMT0YgbW9kZWwgYWxzbyBoYXMgYW4gQVVDICBvZiAwLjYyMjgsIGEgc2lnbmlmaWNhbnQgZHJvcCBmcm9tIHRoZSBvdGhlciBtb2RlbC4gVGhpcyBmdXJ0aGVyIGNvbmZpcm1zIHByZXZpb3VzIGZpbmRpbmdzIG9mIExPRiBub3QgYmVpbmcgYmVuZWZpY2lhbCBmb3IgdGhpcyBkYXRhLiAKCgpgYGB7cn0KCiMgTm8gTE9GCgpiYXNlbGluZV9sb2dpdCA8LSBnbG0oCiAgT3V0Y29tZTAxIH4gVGhpY2tuZXNzX29mX0NsdW1wICsgQ2VsbF9TaXplX1VuaWZvcm1pdHkgKyBDZWxsX1NoYXBlX1VuaWZvcm1pdHkgKyAKICAgIE1hcmdpbmFsX0FkaGVzaW9uICsgU2luZ2xlX0VwaXRoZWxpYWxfQ2VsbF9TaXplICsgQmFyZV9OdWNsZWkgKwogICAgQmxhbmRfQ2hyb21hdGluICsgTm9ybWFsX051Y2xlb2xpICsgTWl0b3NlcywKICBkYXRhPXRyYWluLCBmYW1pbHk9Ymlub21pYWwKKQoKc3VtbWFyeShiYXNlbGluZV9sb2dpdCkKYmFzZWxpbmVfcHJvYnMgPC0gcHJlZGljdChiYXNlbGluZV9sb2dpdCwgbmV3ZGF0YT10ZXN0LCB0eXBlPSJyZXNwb25zZSIpCnJvY19iYXNlbGluZSA8LSByb2ModGVzdCRPdXRjb21lMDEsIGJhc2VsaW5lX3Byb2JzKQpyb2NfYmFzZWxpbmUkYXVjCgoKIyBMT0YKCgpsb2dpdF9maXQgPC0gZ2xtKE91dGNvbWUwMSB+IExPRl9rMjAsIGRhdGE9dHJhaW4sIGZhbWlseT1iaW5vbWlhbCkKCnN1bW1hcnkobG9naXRfZml0KQoKbG9naXRfcHJvYnMgPC0gcHJlZGljdChsb2dpdF9maXQsIG5ld2RhdGE9dGVzdCwgdHlwZT0icmVzcG9uc2UiKQpsb2dpdF9wcmVkIDwtIGlmZWxzZShsb2dpdF9wcm9icyA+IDAuNSwgMSwgMCkKCmNvbmZ1c2lvbk1hdHJpeChmYWN0b3IobG9naXRfcHJlZCksIGZhY3Rvcih0ZXN0JE91dGNvbWUwMSksIHBvc2l0aXZlPSIxIikKcm9jX2xvZ2l0IDwtIHJvYyh0ZXN0JE91dGNvbWUwMSwgbG9naXRfcHJvYnMpCgpwbG90KHJvY19sb2dpdCwgbWFpbj0iTG9naXN0aWMgUmVncmVzc2lvbiB3aXRoIExPRiIpCnJvY19sb2dpdCRhdWMKCgoKYGBgCgogIE1vdmluZyBvbnRvIHRoZSByYW5kb20gZm9yZXN0IG1vZGVsIHRoZXJlIHdpbGwgYWxzbyBiZSB0d28gdmVyc2lvbnMsIG9uZSB0aGF0IGRvZXMgbm90IHV0aWxpemUgTE9GIGFuZCBvbmUgdGhhdCB1dGlsaXplcyBMT0YuIFN0YXJ0aW5nIHdpdGggdGhlIHJlc3VsdHMgZnJvbSB0aGUgbm9uIExPRiBtb2RlbCB0aGUgQVVDIGlzIDAuOTgyMywgd2hpY2ggaXMgZXZlbiBiZXR0ZXIgdGhhbiB0aGUgbG9naXN0aWMgbW9kZWwuIFRoaXMgbW9kZWwgYWxzbyBoYXMgYSBzZW5zaXRpdml0eSBvZiAxLCBmb3IgbWFsaWduYW50IGRldGVjdGlvbiwgd2hpY2ggbWVhbnMgaXQgaXMgY29ycmVjdGx5IGRldGVjdGluZyB0aGUgbWFsaWduYW50IG9ic2VydmF0aW9ucyBldmVyeSB0aW1lIHdoaWNoIGlzIGluY3JlZGlibGUuIFRoaXMgaXMgcGFpcmVkIHdpdGggYSBzcGVjaWZpY2l0eSBvZiAwLjk2IHdoaWNoIG1lYW5zIHRoZSBtb2RlbCBjb3JyZWN0bHkgZGV0ZWN0cyBiZW5pZ24gb2JzZXJ2YXRpb25zIDk2JSBvZiB0aGUgdGltZS4gQm90aCBvZiB0aGVzZSByZXN1bHRzIGFyZSBncmVhdCB0byBzZWUgaW4gYSBtb2RlbC4gU3dpdGNoaW5nIHRvIHRoZSBMT0YgcmFuZG9tIGZvcmVzdCBwcmVzZW50cyBhIGRpZmZlcmVudCBzdG9yeS4gVGhlIEFVQyBmb3IgdGhpcyBtb2RlbCBpcyBvbmx5IDAuNTU2LCB3aGljaCBhcyBkaXNjdXNzZWQgYXMgYmVmb3JlIGlzIHZlcnkgd2Vhay4gQWRkaXRpb25hbGx5IHRoaXMgaXMgcGFpcmVkIHdpdGggYSBzZW5zaXRpdml0eSBvZiAwLjM2NyB3aGljaCBtZWFucyBtYWxpZ25hbnQgb2JzZXJ2YXRpb25zIGFyZSBvbmx5IGJlaW5nIGNvcnJlY3RseSBpZGVudGlmaWVzIDM2LjclIG9mIHRoZSB0aW1lLiBMb29raW5nIGF0IHNwZWNpZmljaXR5IHRoaXMgaXMgc2xpZ2h0bHkgYmV0dGVyIGF0IDAuNzEzIG1lYW5pbmcgYmVuaWduIG9ic2VydmF0aW9ucyBhcmUgY29ycmVjdGx5IGlkZW50aWZpZWQgNzEuMyUgb2YgdGhlIHRpbWUuIFdoaWxlIGJldHRlciB0aGFuIHRoZSBzZW5zaXRpdml0eSwgdGhpcyBpcyBsb3dlciB0aGFuIHRoZSBzcGVjaWZpY2l0eSBpbiB0aGUgbm9uIExPRiBtb2RlbC4gCgoKYGBge3J9CiMgTm90IExPRgpzZXQuc2VlZCgxMjMpCgpyZl9iYXNlIDwtIHJhbmRvbUZvcmVzdCgKICBmYWN0b3IoT3V0Y29tZTAxKSB+IFRoaWNrbmVzc19vZl9DbHVtcCArIENlbGxfU2l6ZV9Vbmlmb3JtaXR5ICsKICAgIENlbGxfU2hhcGVfVW5pZm9ybWl0eSArIE1hcmdpbmFsX0FkaGVzaW9uICsKICAgIFNpbmdsZV9FcGl0aGVsaWFsX0NlbGxfU2l6ZSArIEJhcmVfTnVjbGVpICsKICAgIEJsYW5kX0Nocm9tYXRpbiArIE5vcm1hbF9OdWNsZW9saSArIE1pdG9zZXMsCiAgZGF0YT10cmFpbiwKICBudHJlZT0zMDAsCiAgaW1wb3J0YW5jZT1UUlVFCikKCnJmX2Jhc2VfcHJvYnMgPC0gcHJlZGljdChyZl9iYXNlLCBuZXdkYXRhPXRlc3QsIHR5cGU9InByb2IiKVssMl0KcmZfYmFzZV9wcmVkICA8LSBwcmVkaWN0KHJmX2Jhc2UsIG5ld2RhdGE9dGVzdCwgdHlwZT0iY2xhc3MiKQoKY29uZnVzaW9uTWF0cml4KHJmX2Jhc2VfcHJlZCwgZmFjdG9yKHRlc3QkT3V0Y29tZTAxKSwgcG9zaXRpdmU9IjEiKQoKcm9jX3JmX2Jhc2UgPC0gcm9jKHRlc3QkT3V0Y29tZTAxLCByZl9iYXNlX3Byb2JzKQpyb2NfcmZfYmFzZSRhdWMKCiMgTE9GCnNldC5zZWVkKDEyMykKCnJmX2ZpdCA8LSByYW5kb21Gb3Jlc3QoCiAgZmFjdG9yKE91dGNvbWUwMSkgfiBMT0ZfazIwLAogIGRhdGE9dHJhaW4sCiAgbnRyZWU9MzAwLAogIGltcG9ydGFuY2U9VFJVRQopCgpyZl9wcm9icyA8LSBwcmVkaWN0KHJmX2ZpdCwgbmV3ZGF0YT10ZXN0LCB0eXBlPSJwcm9iIilbLDJdCnJmX3ByZWQgIDwtIHByZWRpY3QocmZfZml0LCBuZXdkYXRhPXRlc3QsIHR5cGU9ImNsYXNzIikKCmNvbmZ1c2lvbk1hdHJpeChyZl9wcmVkLCBmYWN0b3IodGVzdCRPdXRjb21lMDEpLCBwb3NpdGl2ZT0iMSIpCgpyb2NfcmYgPC0gcm9jKHRlc3QkT3V0Y29tZTAxLCByZl9wcm9icykKcGxvdChyb2NfcmYsIG1haW49IlJhbmRvbSBGb3Jlc3Qgd2l0aCBMT0YiKQpyb2NfcmYkYXVjCgoKYGBgCgoKIyBSZXN1bHRzICYgQ29uY2x1c2lvbnMKCiAgVGhpcyBwcm9qZWN0IHNob3djYXNlcyBhIHN0cm9uZyBkaXN0aW5jdGlvbiBiZXR3ZWVuIHRoZSBiZW5pZ24gYW5kIG1hbGlnbmFudCB0dW1vcnMgaW4gdGhpcyBkYXRhIHNldC4gVGhlIFBDQSBhbmFseXNpcyB0aGF0IHdhcyB1dGlsaXplZCBkaWQgYSBnb29kIGpvYiBvZiByZWR1Y2luZyB0aGUgZGltZW5zaW9uYWxpdHkgaW4gdGhlIGRhdGEgc2V0LiBCb3RoIHRoZSBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgd2VyZSBpbmNyZWRpYmx5IHN0cm9uZyBhbmQgc2hvd2VkIGdvb2Qgc3BlY2lmaWNpdHkgYW5kIHNlbnNpdGl2aXR5LiBUaGUgcmVzdWx0cyBmcm9tIHRoZSBrLW1lYW5zIGFuZCBjbHVzdGVyaW5nIHNob3djYXNlZCBjbGVhciBncm91cGluZyBpbiB0aGUgZGF0YSBjb3JyZXNwb25kaW5nIHRvIHR1bW9yIG91dGNvbWUgc3RhdHVzLCBtZWFuaW5nIHRoZXNlIG51bWVyaWNhbCBtZWFzdXJlbWVudCBpbmRpY2F0aW5nIGEgbmF0dXJhbCBzZXBhcmF0aW9uIGJldHdlZW4gdGhlIHR1bW9yIHR5cGVzLiBUaGUgY2x1c3RlcmluZyBjbGFzc2lmaWNhdGlvbnMgd2VyZSBhbHNvIGluY3JlZGlibHkgc3Ryb25nIGVzcGVjaWFsbHkgY29uc2lkZXJpbmcgaXQgd2FzIG9uZSBmYWN0b3IuIEhvd2V2ZXIsIGluIGNvbXBhcmlzb24gdG8gdGhlIFBDQSBjbGFzc2lmaWNhdGlvbiBtb2RlbHMgdGhleSB3ZXJlIG92ZXJhbGwgc2xpZ2h0bHkgd2Vha2VyLiBJbmRpY2F0aW5nIHRoYXQgYmV0d2VlbiB0aGUgdHdvIHRoZSBQQ0EgbWV0aG9kIHNob3VsZCBiZSB0aGUgcmVjb21tZW5kYXRpb24gZm9yIHRoaXMgc3BlY2lmaWMgZGF0YSBzZXQuIAogIAogIFdoZW4gdXNpbmcgdGhlIExPRiBtZXRob2RvbG9neSBpdCB1bmZvcnR1bmF0ZWx5IHdhcyBub3QgYXMgZWZmZWN0aXZlIGZvciB0aGlzIGRhdGEuIFdoaWxlIExPRiBpcyBhbiBpbmNyZWRpYmx5IHVzZWZ1bCB0b29sLCBpdCBpcyBvbmx5IHVzZWZ1bCBpbiBjZXJ0YWluIHNjZW5hcmlvcyB3aXRoIGNlcnRhaW4gZGF0YSBzZXRzIGFuZCB0aGlzIGRhdGEgc2V0IHdhcyBqdXN0IG5vdCBtZWFudCB0byBiZW5lZml0IGZyb20gTE9GLiBUaGUgbWFsaWduYW50IHR1bW9ycyBkbyBub3QgYXBwZWFyIGFzIG91dGxpZXJzIGluIHRoZSBkYXRhIHNldCBjYXVzaW5nIHRoZSBMT0ZzIHRvIGhhdmUgbG93IEFVQ3MgYW5kIHBvb3Igc2Vuc2l0aXZpdGllcy4gVGhlIHN1cGVydmlzZWQgbW9kZWxzLCBiZWluZyBsb2dpc3RpYyBhbmQgcmFuZG9tIGZvcmVzdCB3ZXJlIHZlcnkgc3Ryb25nLiBXaXRoIHRoZSByYW5kb20gZm9yZXN0IGJlaW5nIHNsaWdodGx5IHN0cm9uZ2VyIGFuZCBiZWluZyBhIGdyZWF0IHJlY29tbWVuZGF0aW9uIGZvciBhIGZpbmFsIG1vZGVsIGluIGRldGVybWluaW5nIHdoZXRoZXIgb3Igbm90IGEgdHVtb3IgaXMgYmVuaWduIG9yIG1hbGlnbmFudC4gRXNwZWNpYWxseSB3aXRoIGEgc2Vuc2l0aXZpdHkgb2YgMSwgdGhpcyBtZWFucyBwYXRpZW50cyB3aXRoIG1hbGlnbmFudCB0dW1vcnMgd291bGQgbmV2ZXIgYmUgbWlzZGlhZ25vc2VkIHdoaWNoIGlzIGluY3JlZGlibHkgYmVuZWZpY2lhbCBhbmQgbGlmZSBzYXZpbmcuIE92ZXJhbGwsIGRlc3BpdGUgdGhlIHVuZm9ydHVuYXRlIHJlc3VsdHMgb2YgTE9GIG5vdCBiZWluZyBiZW5lZmljaWFsLCB0aGUgcmVzdCBvZiByZXN1bHRzIHByb3ZpZGVkIGdyZWF0IHJlc3VsdHMgZm9yIHByZWRpY3RpbmcgdGhlIHdoZXRoZXIgb3Igbm90IGEgdHVtb3IgaXMgYmVuaWduIG9yIG1hbGlnbmFudCwgd2hpY2ggaXMgdGhlIHRydWUgZ29hbCBvZiB0aGlzIHByb2plY3QuIAoKCgoKCg==