Student information
Name M_Nr
Malte Gunnar Grube 221200075
Maximilian Runge 220200831
Pascal Pischel 221201473
Truong An Nguyen 221200154

1. Introducing words

1-a) Background and motivation

Mushrooms are widely consumed in various countries due to their culinary appeal. However, the presence of poisonous mushrooms poses a significant risk to human health and safety. Therefore, it is particularly important to be able to distinguish between edible and poisonous mushrooms. This task can be very challenging, as there are many different types of mushrooms species. In this project, our objective is to address this challenge by using a comprehensive dataset to train and employ a machine learning model capable of accurately distinguish between edible and poisonous mushrooms.

1-b) Aim of the project

For this project, we aim to enhance the reliability and efficiency of mushroom classification, ultimately promoting public health and minimizing the risks associated with mushroom consumption. To accomplish this, we will make use of a dataset that contains relevant information about mushrooms. We will ultimately train a model, to effectively analyze and interpret the provided mushroom data, enabling it to classify mushrooms into edible and poisonous.


2. Dataset and data preparation

2-a) Dataset description

The dataset we are going to deal with is the Mushroom Classification dataset from Kaggle. It contains information about mushrooms and whether they are edible or poisonous on 8124 samples. The dataset contains 23 columns, of which 22 are features and 1 is the target variable (edible or poisonous).

2-b) Dataset columns

Introducing, we will take a closer look at the columns of the dataset.

Read dataset

Let’s take a look at how to read the dataset. The dataset is available as a csv file. We can read it with the following command:

mush <- read.csv("mushrooms.csv")

Classification of the columns

To understand what letter stands for what, we will take a look at the classification of the columns:
Attribute information
Attribute Classes
cap-shape bell=b, conical=c, convex=x, flat=f, knobbed=k, sunken=s
cap-surface fibrous=f, grooves=g, scaly=y, smooth=s
cap-color brown=n, buff=b, cinnamon=c, gray=g, green=r, pink=p, purple=u, red=e, white=w, yellow=y
bruises bruises=t, no=f
odor almond=a, anise=l, creosote=c, fishy=y, foul=f, musty=m, none=n, pungent=p, spicy=s
gill-attachment attached=a, descending=d, free=f, notched=n
gill-spacing close=c, crowded=w, distant=d
gill-size broad=b, narrow=n
gill-color black=k, brown=n, buff=b, chocolate=h, gray=g, green=r, orange=o, pink=p, purple=u, red=e, white=w, yellow=y
stalk-shape enlarging=e, tapering=t
stalk-root bulbous=b, club=c, cup=u, equal=e, rhizomorphs=z, rooted=r, missing=?
stalk-surface-above-ring fibrous=f, scaly=y, silky=k, smooth=s
stalk-surface-below-ring fibrous=f, scaly=y, silky=k, smooth=s
stalk-color-above-ring brown=n, buff=b, cinnamon=c, gray=g, orange=o, pink=p, red=e, white=w, yellow=y
stalk-color-below-ring brown=n, buff=b, cinnamon=c, gray=g, orange=o, pink=p, red=e, white=w, yellow=y
veil-type partial=p, universal=u
veil-color brown=n, orange=o, white=w, yellow=y
ring-number none=n, one=o, two=t
ring-type cobwebby=c, evanescent=e, flaring=f, large=l, none=n, pendant=p, sheathing=s, zone=z
spore-print-color black=k, brown=n, buff=b, chocolate=h, green=r, orange=o, purple=u, white=w, yellow=y
population abundant=a, clustered=c, numerous=n, scattered=s, several=v, solitary=y
habitat grasses=g, leaves=l, meadows=m, paths=p, urban=u, waste=w, woods=d

Check null columns

Now we will check if there are any null columns in the dataset:

colSums(is.na(mush))
                   class                cap.shape              cap.surface 
                       0                        0                        0 
               cap.color                  bruises                     odor 
                       0                        0                        0 
         gill.attachment             gill.spacing                gill.size 
                       0                        0                        0 
              gill.color              stalk.shape               stalk.root 
                       0                        0                        0 
stalk.surface.above.ring stalk.surface.below.ring   stalk.color.above.ring 
                       0                        0                        0 
  stalk.color.below.ring                veil.type               veil.color 
                       0                        0                        0 
             ring.number                ring.type        spore.print.color 
                       0                        0                        0 
              population                  habitat 
                       0                        0 

Structure

Let’s check the structure of the dataset:

str(mush)
'data.frame':   8124 obs. of  23 variables:
 $ class                   : chr  "p" "e" "e" "p" ...
 $ cap.shape               : chr  "x" "x" "b" "x" ...
 $ cap.surface             : chr  "s" "s" "s" "y" ...
 $ cap.color               : chr  "n" "y" "w" "w" ...
 $ bruises                 : chr  "t" "t" "t" "t" ...
 $ odor                    : chr  "p" "a" "l" "p" ...
 $ gill.attachment         : chr  "f" "f" "f" "f" ...
 $ gill.spacing            : chr  "c" "c" "c" "c" ...
 $ gill.size               : chr  "n" "b" "b" "n" ...
 $ gill.color              : chr  "k" "k" "n" "n" ...
 $ stalk.shape             : chr  "e" "e" "e" "e" ...
 $ stalk.root              : chr  "e" "c" "c" "e" ...
 $ stalk.surface.above.ring: chr  "s" "s" "s" "s" ...
 $ stalk.surface.below.ring: chr  "s" "s" "s" "s" ...
 $ stalk.color.above.ring  : chr  "w" "w" "w" "w" ...
 $ stalk.color.below.ring  : chr  "w" "w" "w" "w" ...
 $ veil.type               : chr  "p" "p" "p" "p" ...
 $ veil.color              : chr  "w" "w" "w" "w" ...
 $ ring.number             : chr  "o" "o" "o" "o" ...
 $ ring.type               : chr  "p" "p" "p" "p" ...
 $ spore.print.color       : chr  "k" "n" "n" "k" ...
 $ population              : chr  "s" "n" "n" "s" ...
 $ habitat                 : chr  "u" "g" "m" "u" ...

2-c) Data preparation

In this section, we will prepare the data for the model.

Remove veil-type

After this, we will remove the column veil-type, as it only contains one value called p:

mush <- data.frame(mush[, -17])

Convert categorical variables

Finally, we will convert the variables into factors – because all features are categorical. Here is a preview of the converted variables:

mush <- as.data.frame(lapply(mush, as.factor))
  Converted variables
class cap.shape cap.surface cap.color bruises odor gill.attachment gill.spacing gill.size gill.color stalk.shape stalk.root stalk.surface.above.ring stalk.surface.below.ring stalk.color.above.ring stalk.color.below.ring veil.color ring.number ring.type spore.print.color population habitat
p x s n t p f c n k e e s s w w w o p k s u
e x s y t a f c b k e c s s w w w o p n n g
e b s w t l f c b n e c s s w w w o p n n m
p x y w t p f c n n e e s s w w w o p k s u
e x s g f n f w b k t e s s w w w o e n a g
e x y y t a f c b n e c s s w w w o p k n g

2-d) Data visualization

Poisonous-edible distribution

Here you can see the distribution of the both classes:

ggplot(data = mush, aes(x = class, fill = class)) +
  geom_bar(alpha = 0.7) +
  labs(title = "class", x = NULL, y = NULL) +
  scale_fill_manual(values = c("#33cc33", "#4b0092")) +
  theme_bw() +
  theme(
    plot.title = element_text(hjust = 0.5, size = 11, face = "bold"),
    legend.position = "none"
  )


e / p results
class n
edible 4208
poisonous 3916





Feature distributions

Let’s take a look at the distributions of all the features:

feature_names <- names(mush)[-1]

feature_plots <- lapply(feature_names, function(feature) {
  ggplot(data = mush, aes(x = get(feature), fill = class)) +
    geom_bar(alpha = 0.7) +
    labs(title = paste(feature), x = NULL, y = NULL) +
    scale_fill_manual(
      values = c("#33cc33", "#4b0092"),
      labels = c("edible", "poisonous")
    ) +
    theme_bw() +
    theme(
      plot.title = element_text(hjust = 0.5, size = 11, face = "bold"),
      legend.position = "top"
    ) +
    guides(fill = guide_legend(title = "class", nrow = 2))
})

legend <- get_legend(feature_plots[[1]])

for (i in seq_along(feature_plots)) {
  feature_plots[[i]] <- feature_plots[[i]] + theme(legend.position = "none")
}

plot_grid(plotlist = feature_plots, ncol = 2, nrow = 11, legend = legend)


Edible features

In these plots, we can see that certain features are important to classify a mushroom as edible.
If a test mushroom has one of these features, it is more likely to be this class:

Edible features
Attribute Description
odor almond=a, anise=l, none=n
stalk.color.below.ring red=e, orange=o, gray=g
stalk.color.above.ring red=e, orange=o, gray=g
spore.print.color yellow=y, purple=u, orange=o, buff=b
population abundant=a, numerous=n
veil.color orange=o, brown=n
cap.color red=e, purple=u
gill.color gray=g, orange=o
ring.type flaring=f
cap.shape sunken=s

Poisonous features

In these plots, we can see that certain features are important to classify a mushroom as poisonous.
If a test mushroom has one of these features, it is more likely to be this class:

Poisonous features
Attribute Description
odor pungent=p, foul=f, creosote=c, fishy=y, spicy=s, musty=m
stalk.color.below.ring buff=b, yellow=y, cinnamon=c
ring.number none=n
gill.color buff=b, green=r
spore.print.color green=r
cap.shape conical=c
veil.color yellow=y
stalk.color.above.ring buff=b, yellow=y, cinnamon=c
ring.type large=l
cap.surface grooves=g

3. Machine learning

3-a) Model preparation

Now we will split the data into training and test data. We will use the training data to train the model and the test data to test the model.

set.seed(753)
train_index <- createDataPartition(mush$class,
  p = .8, list = FALSE, times = 1
)
train <- mush[train_index, ]
test <- mush[-train_index, ]

3-b) Training models

In this section, we will train several models and compare them – sped up via parallel processing.

GLM

Let’s take a look at the Logistic Regression (glm) model:

cl <- makeCluster(detectCores() - 1)
registerDoParallel(cl)
start_time <- Sys.time()
model_glm <- train(class ~ ., data = train, method = "glm")
stopCluster(cl)
end_time <- Sys.time()
end_time - start_time
Time difference of 17.80068 secs

SVM

Let’s take a look at the Support Vector Machines (linear) model:

cl <- makeCluster(detectCores() - 1)
registerDoParallel(cl)
start_time <- Sys.time()
model_svm <- train(class ~ ., data = train, method = "svmLinear")
stopCluster(cl)
end_time <- Sys.time()
end_time - start_time
Time difference of 8.636677 secs

RF

Let’s take a look at the Random Forest model:

cl <- makeCluster(detectCores() - 1)
registerDoParallel(cl)
start_time <- Sys.time()
model_rf <- train(class ~ ., data = train, method = "rf")
stopCluster(cl)
end_time <- Sys.time()
end_time - start_time
Time difference of 2.940914 mins

KNN

Let’s take a look at the K-Nearest Neighbors model:

cl <- makeCluster(detectCores() - 1)
registerDoParallel(cl)
start_time <- Sys.time()
model_knn <- train(class ~ ., data = train, method = "knn")
stopCluster(cl)
end_time <- Sys.time()
end_time - start_time
Time difference of 49.82261 secs

GBM

Let’s take a look at the Gradient Boosting Machine model:

cl <- makeCluster(detectCores() - 1)
registerDoParallel(cl)
start_time <- Sys.time()
model_gbm <- train(class ~ ., data = train, method = "gbm", verbose = FALSE)
stopCluster(cl)
end_time <- Sys.time()
end_time - start_time
Time difference of 24.34671 secs

3-c) Confusion matrices

GLM

Let’s take a look at the confusion matrix of the Logistic Regression (glm) model:

confusion_glm <- confusionMatrix(predict(model_glm, test), test$class)
confusion_glm
Confusion Matrix and Statistics

          Reference
Prediction   e   p
         e 841   0
         p   0 783
                                     
               Accuracy : 1          
                 95% CI : (0.9977, 1)
    No Information Rate : 0.5179     
    P-Value [Acc > NIR] : < 2.2e-16  
                                     
                  Kappa : 1          
                                     
 Mcnemar's Test P-Value : NA         
                                     
            Sensitivity : 1.0000     
            Specificity : 1.0000     
         Pos Pred Value : 1.0000     
         Neg Pred Value : 1.0000     
             Prevalence : 0.5179     
         Detection Rate : 0.5179     
   Detection Prevalence : 0.5179     
      Balanced Accuracy : 1.0000     
                                     
       'Positive' Class : e          
                                     

SVM

Let’s take a look at the confusion matrix of the Support Vector Machines (linear) model:

confusion_svm <- confusionMatrix(predict(model_svm, test), test$class)
confusion_svm
Confusion Matrix and Statistics

          Reference
Prediction   e   p
         e 841   0
         p   0 783
                                     
               Accuracy : 1          
                 95% CI : (0.9977, 1)
    No Information Rate : 0.5179     
    P-Value [Acc > NIR] : < 2.2e-16  
                                     
                  Kappa : 1          
                                     
 Mcnemar's Test P-Value : NA         
                                     
            Sensitivity : 1.0000     
            Specificity : 1.0000     
         Pos Pred Value : 1.0000     
         Neg Pred Value : 1.0000     
             Prevalence : 0.5179     
         Detection Rate : 0.5179     
   Detection Prevalence : 0.5179     
      Balanced Accuracy : 1.0000     
                                     
       'Positive' Class : e          
                                     

RF

Let’s take a look at the confusion matrix of the Random Forest model:

confusion_rf <- confusionMatrix(predict(model_rf, test), test$class)
confusion_rf
Confusion Matrix and Statistics

          Reference
Prediction   e   p
         e 841   0
         p   0 783
                                     
               Accuracy : 1          
                 95% CI : (0.9977, 1)
    No Information Rate : 0.5179     
    P-Value [Acc > NIR] : < 2.2e-16  
                                     
                  Kappa : 1          
                                     
 Mcnemar's Test P-Value : NA         
                                     
            Sensitivity : 1.0000     
            Specificity : 1.0000     
         Pos Pred Value : 1.0000     
         Neg Pred Value : 1.0000     
             Prevalence : 0.5179     
         Detection Rate : 0.5179     
   Detection Prevalence : 0.5179     
      Balanced Accuracy : 1.0000     
                                     
       'Positive' Class : e          
                                     

KNN

Let’s take a look at the confusion matrix of the K-Nearest Neighbors model:

confusion_knn <- confusionMatrix(predict(model_knn, test), test$class)
confusion_knn
Confusion Matrix and Statistics

          Reference
Prediction   e   p
         e 841   0
         p   0 783
                                     
               Accuracy : 1          
                 95% CI : (0.9977, 1)
    No Information Rate : 0.5179     
    P-Value [Acc > NIR] : < 2.2e-16  
                                     
                  Kappa : 1          
                                     
 Mcnemar's Test P-Value : NA         
                                     
            Sensitivity : 1.0000     
            Specificity : 1.0000     
         Pos Pred Value : 1.0000     
         Neg Pred Value : 1.0000     
             Prevalence : 0.5179     
         Detection Rate : 0.5179     
   Detection Prevalence : 0.5179     
      Balanced Accuracy : 1.0000     
                                     
       'Positive' Class : e          
                                     

GBM

Let’s take a look at the confusion matrix of the Gradient Boosting Machine model:

confusion_gbm <- confusionMatrix(predict(model_gbm, test), test$class)
confusion_gbm
Confusion Matrix and Statistics

          Reference
Prediction   e   p
         e 841   0
         p   0 783
                                     
               Accuracy : 1          
                 95% CI : (0.9977, 1)
    No Information Rate : 0.5179     
    P-Value [Acc > NIR] : < 2.2e-16  
                                     
                  Kappa : 1          
                                     
 Mcnemar's Test P-Value : NA         
                                     
            Sensitivity : 1.0000     
            Specificity : 1.0000     
         Pos Pred Value : 1.0000     
         Neg Pred Value : 1.0000     
             Prevalence : 0.5179     
         Detection Rate : 0.5179     
   Detection Prevalence : 0.5179     
      Balanced Accuracy : 1.0000     
                                     
       'Positive' Class : e          
                                     

3-d-I) Importance plot

One of the most accurate models is RandomForest. With RandomForest the function varImp() shows the permutation importance. As shown in the importance plot of the model, the odor none is the most significant indicator for an edible mushroom.

var_importance <- varImp(model_rf$finalModel)
var_df <- as.data.frame(var_importance)
sorted_vars <- var_df %>%
  arrange(desc(Overall)) %>%
  head(20)

total <- sum(sorted_vars$Overall)
sorted_vars$Overall <- (sorted_vars$Overall / total) * 100

x_labels <- str_sub(row.names(sorted_vars), end = -2) %>%
  paste0("_", str_sub(row.names(sorted_vars), -1))

ggplot(sorted_vars, aes(
  x = reorder(row.names(sorted_vars), -Overall),
  y = Overall
)) +
  geom_bar(stat = "identity", alpha = 0.6, fill = "#00303F") +
  labs(x = NULL, y = NULL, title = "Variable vs. Importance (in %)") +
  coord_flip() +
  theme_bw() +
  theme(
    legend.position = "none",
    plot.title = element_text(hjust = 0.5, face = "bold")
  ) +
  scale_x_discrete(labels = x_labels)

3-d-II) Relation plots

Here are some plots that show the relationship between some of the variables and the class of the mushrooms.

Gill size vs. Odor

ggplot(mush, aes(x = gill.size, y = odor)) +
  geom_jitter(aes(color = class, alpha = class),
    position = position_jitter(width = 0.2)
  ) +
  scale_color_manual(values = c("e" = "#33cc33", "p" = "#4b0092")) +
  scale_alpha_manual(values = c("e" = 0.4, "p" = 0.6)) +
  scale_size(guide = "none") +
  labs(title = "Gill size vs. Odor") +
  theme_bw() +
  theme(
    plot.title = element_text(
      hjust = 0.5, face = "bold",
      margin = ggplot2::margin(0, 0, 5, 0)
    ),
    axis.title.x = element_text(margin = ggplot2::margin(7, 0, 0, 0)),
    axis.title.y = element_text(margin = ggplot2::margin(0, 15, 0, 0))
  )

Spore print color vs. Gill color

ggplot(mush, aes(x = spore.print.color, y = gill.color)) +
  geom_jitter(aes(color = class, alpha = class),
    position = position_jitter(width = 0.2)
  ) +
  scale_color_manual(values = c("e" = "#33cc33", "p" = "#4b0092")) +
  scale_alpha_manual(values = c("e" = 0.4, "p" = 0.6)) +
  scale_size(guide = "none") +
  labs(title = "Spore print color vs. Gill color") +
  theme_bw() +
  theme(
    plot.title = element_text(
      hjust = 0.5, face = "bold",
      margin = ggplot2::margin(0, 0, 5, 0)
    ),
    axis.title.x = element_text(margin = ggplot2::margin(7, 0, 0, 0)),
    axis.title.y = element_text(margin = ggplot2::margin(0, 15, 0, 0))
  )

Habitat vs. Population

ggplot(mush, aes(x = habitat, y = population)) +
  geom_jitter(aes(color = class, alpha = class),
    position = position_jitter(width = 0.2)
  ) +
  scale_color_manual(values = c("e" = "#33cc33", "p" = "#4b0092")) +
  scale_alpha_manual(values = c("e" = 0.4, "p" = 0.6)) +
  scale_size(guide = "none") +
  labs(title = "Habitat vs. Population") +
  theme_bw() +
  theme(
    plot.title = element_text(
      hjust = 0.5, face = "bold",
      margin = ggplot2::margin(0, 0, 5, 0)
    ),
    axis.title.x = element_text(margin = ggplot2::margin(7, 0, 0, 0)),
    axis.title.y = element_text(margin = ggplot2::margin(0, 15, 0, 0))
  )

Cap shape vs. Stalk color below ring

ggplot(mush, aes(x = cap.shape, y = stalk.color.below.ring)) +
  geom_jitter(aes(color = class, alpha = class),
    position = position_jitter(width = 0.2)
  ) +
  scale_color_manual(values = c("e" = "#33cc33", "p" = "#4b0092")) +
  scale_alpha_manual(values = c("e" = 0.4, "p" = 0.6)) +
  scale_size(guide = "none") +
  labs(title = "Cap shape vs. Stalk color below ring") +
  theme_bw() +
  theme(
    plot.title = element_text(
      hjust = 0.5, face = "bold",
      margin = ggplot2::margin(0, 0, 5, 0)
    ),
    axis.title.x = element_text(margin = ggplot2::margin(7, 0, 0, 0)),
    axis.title.y = element_text(margin = ggplot2::margin(0, 15, 0, 0))
  )

3-e) Decision trees

Finishing the document, we are illustrating various decision trees. The first one is a general tree. Then, we are going to remove the odor variable and see how the tree changes. The next tree does not provide properties that are hard to tell. The last one just provides properties according to the top view of a mushroom.

mush_fac <- as.data.frame(lapply(mush, as.factor))
loss_matrix <- matrix(c(0, 1, 4, 0), byrow = TRUE, nrow = 2)

General tree

tree1 <- rpart(class ~ .,
  minbucket = 1,
  method = "class",
  parms = list(loss = loss_matrix),
  data = mush_fac
)

fancyRpartPlot(tree1,
  palettes = c("Greens", "Purples"),
  caption = NULL, cex = 0.8,
  main = "General tree"
)


Without Odor

mush_fac_no_odor <- mush_fac
mush_fac_no_odor$odor <- NULL

tree2 <- rpart(class ~ .,
  minbucket = 2,
  method = "class",
  parms = list(loss = loss_matrix),
  data = mush_fac_no_odor
)

fancyRpartPlot(tree2,
  palettes = c("Greens", "Purples"),
  caption = NULL, cex = 0.8,
  main = "Without Odor"
)


Without hard to tell properties

mush_fac_hard <- mush_fac %>%
  select(
    -odor, -spore.print.color, -gill.size, -bruises, -ring.type,
    -stalk.surface.above.ring, -stalk.surface.below.ring, -stalk.root
  ) %>%
  mutate_at(vars(
    gill.color, cap.color, stalk.color.above.ring,
    stalk.color.below.ring
  ), ~ if_else(. %in% c("b", "h", "c"), "n", .))

tree3 <- rpart(class ~ .,
  minbucket = 200,
  method = "class",
  parms = list(loss = loss_matrix),
  data = mush_fac_hard
)

fancyRpartPlot(tree3,
  palettes = c("Greens", "Purples"),
  caption = NULL,
  main = "Without hard to tell properties"
)


Top view only

mush_fac_top <- subset(mush_fac, select = -c(
  gill.size,
  gill.color, gill.attachment, gill.spacing,
  stalk.shape, stalk.root, stalk.surface.above.ring,
  stalk.surface.below.ring, stalk.color.above.ring,
  stalk.color.below.ring, spore.print.color
))

tree4 <- rpart(class ~ .,
  minbucket = 1,
  method = "class",
  parms = list(loss = loss_matrix),
  data = mush_fac_top
)

fancyRpartPlot(tree4,
  palettes = c("Greens", "Purples"),
  caption = NULL, cex = 0.8,
  main = "Top view only"
)


4. Real mushrooms

In this section, we are going to show some real mushrooms. In addition to that, we are going to provide trees for classfying them.

Shield Dapperling [1]

fancyRpartPlot(tree3,
  palettes = c("Greens", "Purples"),
  caption = NULL, main = NULL
)


Deadly Dapperling [2]

fancyRpartPlot(tree3,
  palettes = c("Greens", "Purples"),
  caption = NULL, main = NULL
)


Medusa Mushroom [3]

fancyRpartPlot(tree3,
  palettes = c("Greens", "Purples"),
  caption = NULL, main = NULL
)


Inky Mushroom [4]

fancyRpartPlot(tree3,
  palettes = c("Greens", "Purples"),
  caption = NULL, main = NULL
)


5. Conclusion

As we delved into the analysis of the mushroom dataset, exploring its various features and their importance, we were able to train several models and compare them. With that we gained valuable insights into the characteristics that differentiate edible and poisonous mushrooms. Through our efforts, we successfully developed decision trees that proved effective in classifying real mushrooms based on the knowledge obtained from the dataset. In conclusion, this project serves as a foundation for future mushroom classification and related studies. By utilizing the power of machine learning, we can continue to make strides in understanding and utilizing the world of mushrooms for the betterment of society.


References

[1] iNaturalist: Shield Dapperling. URL: https://www.inaturalist.org/observations/156623219. Accessed: July 20, 2023
[2] iNaturalist: Deadly Dapperling. URL: https://www.inaturalist.org/observations/70759242. Accessed: July 20, 2023.
[3] iNaturalist: Medusa Mushroom. URL: https://www.inaturalist.org/observations/91868779. Accessed: July 20, 2023.
[4] iNaturalist: Inky Mushroom. URL: https://www.inaturalist.org/observations/170844438. Accessed: July 20, 2023.

LS0tDQp0aXRsZTogU2VtaW5hciBSIC0tIE11c2hyb29tIENsYXNzaWZpY2F0aW9uDQpkYXRlOiAiSnVseSAyMSwgMjAyMyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogdHJ1ZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCi0tLQ0KDQpgYGB7ciBwcmVwYXJhdGlvbiwgc2V0dXAsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRSwgcmVzdWx0cz0iaGlkZSJ9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGNvbW1lbnQgPSAiIiwgZGV2ID0gInBuZyIsDQogIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFDQopDQoNCiMgbWFrZSBzdXJlIHBhbmRvYyBpcyBpbnN0YWxsZWQgYmVmb3JlIGluc3RhbGxpbmcgcGFja2FnZXMNCm9wdGlvbnMocmVwb3MgPSBjKENSQU4gPSAiaHR0cHM6Ly9mdHAuZ3dkZy5kZS9wdWIvbWlzYy9jcmFuLyIpKQ0KDQojIGluc3RhbGwga25pdHIgYW5kIGthYmxlRXh0cmEgaWYgbm90IGFscmVhZHkgaW5zdGFsbGVkDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCmlmICghcmVxdWlyZSgia2FibGVFeHRyYSIpKSBpbnN0YWxsLnBhY2thZ2VzKCJrYWJsZUV4dHJhIikNCg0KIyBsb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KYGBgDQoNCmBgYHtyIGF1dGhvcnMsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9VFJVRX0NCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgTmFtZSA9IGMoDQogICAgIk1hbHRlIEd1bm5hciBHcnViZSIsICJNYXhpbWlsaWFuIFJ1bmdlIiwgIlBhc2NhbCBQaXNjaGVsIiwNCiAgICAiVHJ1b25nIEFuIE5ndXllbiINCiAgKSwNCiAgTV9OciA9IGMoMjIxMjAwMDc1LCAyMjAyMDA4MzEsIDIyMTIwMTQ3MywgMjIxMjAwMTU0KQ0KKQ0KDQprbml0cjo6a2FibGUoZGF0YSwgZm9ybWF0ID0gImh0bWwiLCBjYXB0aW9uID0gIlN0dWRlbnQgaW5mb3JtYXRpb24iKSAlPiUNCiAga2FibGVfc3R5bGluZygNCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEZBTFNFLA0KICAgIHBvc2l0aW9uID0gImxlZnQiDQogICkNCmBgYA0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMC40cHggc29saWQgI0NDQzsgbWFyZ2luLXRvcDogMjVweDsiPg0KDQojIyAxLiBJbnRyb2R1Y2luZyB3b3Jkcw0KDQojIyMgMS1hKSBCYWNrZ3JvdW5kIGFuZCBtb3RpdmF0aW9uDQpNdXNocm9vbXMgYXJlIHdpZGVseSBjb25zdW1lZCBpbiB2YXJpb3VzIGNvdW50cmllcyBkdWUgdG8gdGhlaXIgY3VsaW5hcnkgYXBwZWFsLiBIb3dldmVyLCB0aGUgcHJlc2VuY2Ugb2YgcG9pc29ub3VzIG11c2hyb29tcyBwb3NlcyBhIHNpZ25pZmljYW50IHJpc2sgdG8gaHVtYW4gaGVhbHRoIGFuZCBzYWZldHkuIFRoZXJlZm9yZSwgaXQgaXMgcGFydGljdWxhcmx5IGltcG9ydGFudCB0byBiZSBhYmxlIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gZWRpYmxlIGFuZCBwb2lzb25vdXMgbXVzaHJvb21zLiBUaGlzIHRhc2sgY2FuIGJlIHZlcnkgY2hhbGxlbmdpbmcsIGFzIHRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCB0eXBlcyBvZiBtdXNocm9vbXMgc3BlY2llcy4gSW4gdGhpcyBwcm9qZWN0LCBvdXIgb2JqZWN0aXZlIGlzIHRvIGFkZHJlc3MgdGhpcyBjaGFsbGVuZ2UgYnkgdXNpbmcgYSBjb21wcmVoZW5zaXZlIGRhdGFzZXQgdG8gdHJhaW4gYW5kIGVtcGxveSBhIG1hY2hpbmUgbGVhcm5pbmcgbW9kZWwgY2FwYWJsZSBvZiBhY2N1cmF0ZWx5IGRpc3Rpbmd1aXNoIGJldHdlZW4gZWRpYmxlIGFuZCBwb2lzb25vdXMgbXVzaHJvb21zLg0KDQojIyMgMS1iKSBBaW0gb2YgdGhlIHByb2plY3QNCkZvciB0aGlzIHByb2plY3QsIHdlIGFpbSB0byBlbmhhbmNlIHRoZSByZWxpYWJpbGl0eSBhbmQgZWZmaWNpZW5jeSBvZiBtdXNocm9vbSBjbGFzc2lmaWNhdGlvbiwgdWx0aW1hdGVseSBwcm9tb3RpbmcgcHVibGljIGhlYWx0aCBhbmQgbWluaW1pemluZyB0aGUgcmlza3MgYXNzb2NpYXRlZCB3aXRoIG11c2hyb29tIGNvbnN1bXB0aW9uLiBUbyBhY2NvbXBsaXNoIHRoaXMsIHdlIHdpbGwgbWFrZSB1c2Ugb2YgYSBkYXRhc2V0IHRoYXQgY29udGFpbnMgcmVsZXZhbnQgaW5mb3JtYXRpb24gYWJvdXQgbXVzaHJvb21zLiBXZSB3aWxsIHVsdGltYXRlbHkgdHJhaW4gYSBtb2RlbCwgdG8gZWZmZWN0aXZlbHkgYW5hbHl6ZSBhbmQgaW50ZXJwcmV0IHRoZSBwcm92aWRlZCBtdXNocm9vbSBkYXRhLCBlbmFibGluZyBpdCB0byBjbGFzc2lmeSBtdXNocm9vbXMgaW50byBlZGlibGUgYW5kIHBvaXNvbm91cy4NCg0KPGhyIHN0eWxlPSJib3JkZXI6IDAuNHB4IHNvbGlkICNDQ0M7IG1hcmdpbi10b3A6IDI1cHg7Ij4NCg0KIyMgMi4gRGF0YXNldCBhbmQgZGF0YSBwcmVwYXJhdGlvbg0KDQojIyMgMi1hKSBEYXRhc2V0IGRlc2NyaXB0aW9uDQpUaGUgZGF0YXNldCB3ZSBhcmUgZ29pbmcgdG8gZGVhbCB3aXRoIGlzIHRoZSBbTXVzaHJvb20gQ2xhc3NpZmljYXRpb25dKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vdWNpbWwvbXVzaHJvb20tY2xhc3NpZmljYXRpb24pIGRhdGFzZXQgZnJvbSBLYWdnbGUuIEl0IGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IG11c2hyb29tcyBhbmQgd2hldGhlciB0aGV5IGFyZSBlZGlibGUgb3IgcG9pc29ub3VzIG9uICoqODEyNCoqIHNhbXBsZXMuIFRoZSBkYXRhc2V0IGNvbnRhaW5zICoqMjMqKiBjb2x1bW5zLCBvZiB3aGljaCAqKjIyKiogYXJlIGZlYXR1cmVzIGFuZCAqKjEqKiBpcyB0aGUgdGFyZ2V0IHZhcmlhYmxlIChlZGlibGUgb3IgcG9pc29ub3VzKS4NCg0KIyMjIDItYikgRGF0YXNldCBjb2x1bW5zIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCkludHJvZHVjaW5nLCB3ZSB3aWxsIHRha2UgYSBjbG9zZXIgbG9vayBhdCB0aGUgY29sdW1ucyBvZiB0aGUgZGF0YXNldC4NCg0KIyMjIyBSZWFkIGRhdGFzZXQNCkxldCdzIHRha2UgYSBsb29rIGF0IGhvdyB0byByZWFkIHRoZSBkYXRhc2V0LiBUaGUgZGF0YXNldCBpcyBhdmFpbGFibGUgYXMgYSBjc3YgZmlsZS4gV2UgY2FuIHJlYWQgaXQgd2l0aCB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6DQpgYGB7ciByZWFkLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfSANCm11c2ggPC0gcmVhZC5jc3YoIm11c2hyb29tcy5jc3YiKQ0KYGBgDQoNCiMjIyMgQ2xhc3NpZmljYXRpb24gb2YgdGhlIGNvbHVtbnMNClRvIHVuZGVyc3RhbmQgd2hhdCBsZXR0ZXIgc3RhbmRzIGZvciB3aGF0LCB3ZSB3aWxsIHRha2UgYSBsb29rIGF0IHRoZSBjbGFzc2lmaWNhdGlvbiBvZiB0aGUgY29sdW1uczoNCmBgYHtyIGluZm8sIGVjaG89RkFMU0V9DQphdHRyaWJ1dGVfaW5mbyA8LSBkYXRhLmZyYW1lKA0KICBBdHRyaWJ1dGUgPSBjKA0KICAgICJjYXAtc2hhcGUiLCAiY2FwLXN1cmZhY2UiLCAiY2FwLWNvbG9yIiwgImJydWlzZXMiLCAib2RvciIsDQogICAgImdpbGwtYXR0YWNobWVudCIsICJnaWxsLXNwYWNpbmciLCAiZ2lsbC1zaXplIiwgImdpbGwtY29sb3IiLCAic3RhbGstc2hhcGUiLA0KICAgICJzdGFsay1yb290IiwgInN0YWxrLXN1cmZhY2UtYWJvdmUtcmluZyIsICJzdGFsay1zdXJmYWNlLWJlbG93LXJpbmciLA0KICAgICJzdGFsay1jb2xvci1hYm92ZS1yaW5nIiwgInN0YWxrLWNvbG9yLWJlbG93LXJpbmciLCAidmVpbC10eXBlIiwNCiAgICAidmVpbC1jb2xvciIsICJyaW5nLW51bWJlciIsICJyaW5nLXR5cGUiLCAic3BvcmUtcHJpbnQtY29sb3IiLA0KICAgICJwb3B1bGF0aW9uIiwgImhhYml0YXQiDQogICksDQogIENsYXNzZXMgPSBjKA0KICAgICJiZWxsPWIsIGNvbmljYWw9YywgY29udmV4PXgsIGZsYXQ9Ziwga25vYmJlZD1rLCBzdW5rZW49cyIsDQogICAgImZpYnJvdXM9ZiwgZ3Jvb3Zlcz1nLCBzY2FseT15LCBzbW9vdGg9cyIsDQogICAgImJyb3duPW4sIGJ1ZmY9YiwgY2lubmFtb249YywgZ3JheT1nLCBncmVlbj1yLCBwaW5rPXAsIHB1cnBsZT11LCByZWQ9ZSwNCiAgICB3aGl0ZT13LCB5ZWxsb3c9eSIsICJicnVpc2VzPXQsIG5vPWYiLCAiYWxtb25kPWEsIGFuaXNlPWwsIGNyZW9zb3RlPWMsDQogICAgZmlzaHk9eSwgZm91bD1mLCBtdXN0eT1tLCBub25lPW4sIHB1bmdlbnQ9cCwgc3BpY3k9cyIsDQogICAgImF0dGFjaGVkPWEsIGRlc2NlbmRpbmc9ZCwgZnJlZT1mLCBub3RjaGVkPW4iLA0KICAgICJjbG9zZT1jLCBjcm93ZGVkPXcsIGRpc3RhbnQ9ZCIsDQogICAgImJyb2FkPWIsIG5hcnJvdz1uIiwNCiAgICAiYmxhY2s9aywgYnJvd249biwgYnVmZj1iLCBjaG9jb2xhdGU9aCwgZ3JheT1nLCBncmVlbj1yLCBvcmFuZ2U9bywgcGluaz1wLA0KICAgIHB1cnBsZT11LCByZWQ9ZSwgd2hpdGU9dywgeWVsbG93PXkiLA0KICAgICJlbmxhcmdpbmc9ZSwgdGFwZXJpbmc9dCIsDQogICAgImJ1bGJvdXM9YiwgY2x1Yj1jLCBjdXA9dSwgZXF1YWw9ZSwgcmhpem9tb3JwaHM9eiwgcm9vdGVkPXIsIG1pc3Npbmc9PyIsDQogICAgImZpYnJvdXM9Ziwgc2NhbHk9eSwgc2lsa3k9aywgc21vb3RoPXMiLA0KICAgICJmaWJyb3VzPWYsIHNjYWx5PXksIHNpbGt5PWssIHNtb290aD1zIiwNCiAgICAiYnJvd249biwgYnVmZj1iLCBjaW5uYW1vbj1jLCBncmF5PWcsIG9yYW5nZT1vLCBwaW5rPXAsIHJlZD1lLCB3aGl0ZT13LA0KICAgIHllbGxvdz15IiwgImJyb3duPW4sIGJ1ZmY9YiwgY2lubmFtb249YywgZ3JheT1nLCBvcmFuZ2U9bywgcGluaz1wLCByZWQ9ZSwNCiAgICB3aGl0ZT13LCB5ZWxsb3c9eSIsICJwYXJ0aWFsPXAsIHVuaXZlcnNhbD11IiwNCiAgICAiYnJvd249biwgb3JhbmdlPW8sIHdoaXRlPXcsIHllbGxvdz15IiwNCiAgICAibm9uZT1uLCBvbmU9bywgdHdvPXQiLA0KICAgICJjb2J3ZWJieT1jLCBldmFuZXNjZW50PWUsIGZsYXJpbmc9ZiwgbGFyZ2U9bCwgbm9uZT1uLCBwZW5kYW50PXAsDQogICAgc2hlYXRoaW5nPXMsIHpvbmU9eiIsICJibGFjaz1rLCBicm93bj1uLCBidWZmPWIsIGNob2NvbGF0ZT1oLCBncmVlbj1yLA0KICAgIG9yYW5nZT1vLCBwdXJwbGU9dSwgd2hpdGU9dywgeWVsbG93PXkiLA0KICAgICJhYnVuZGFudD1hLCBjbHVzdGVyZWQ9YywgbnVtZXJvdXM9biwgc2NhdHRlcmVkPXMsIHNldmVyYWw9diwgc29saXRhcnk9eSIsDQogICAgImdyYXNzZXM9ZywgbGVhdmVzPWwsIG1lYWRvd3M9bSwgcGF0aHM9cCwgdXJiYW49dSwgd2FzdGU9dywgd29vZHM9ZCINCiAgKQ0KKQ0Ka25pdHI6OmthYmxlKGF0dHJpYnV0ZV9pbmZvLCBjYXB0aW9uID0gIkF0dHJpYnV0ZSBpbmZvcm1hdGlvbiIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCiMjIyMgQ2hlY2sgbnVsbCBjb2x1bW5zDQpOb3cgd2Ugd2lsbCBjaGVjayBpZiB0aGVyZSBhcmUgYW55IG51bGwgY29sdW1ucyBpbiB0aGUgZGF0YXNldDoNCmBgYHtyIGlzbmF9DQpjb2xTdW1zKGlzLm5hKG11c2gpKQ0KYGBgDQoNCiMjIyMgU3RydWN0dXJlDQpMZXQncyBjaGVjayB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0Og0KYGBge3Igc3RyfQ0Kc3RyKG11c2gpDQpgYGANCg0KIyMjIDItYykgRGF0YSBwcmVwYXJhdGlvbiB7LnRhYnNldCAudGFic2V0LWZhZGV9DQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgcHJlcGFyZSB0aGUgZGF0YSBmb3IgdGhlIG1vZGVsLg0KDQpgYGB7ciBpbnN0YWxsMSwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPUZBTFNFLCBldmFsPVRSVUUsIHJlc3VsdHM9ImhpZGUifQ0KIyBJbnN0YWxsIHBhY2thZ2VzIGlmIHRoZXkgYXJlIG5vdCBhbHJlYWR5IGluc3RhbGxlZA0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KaWYgKCFyZXF1aXJlKCJjb3dwbG90IikpIGluc3RhbGwucGFja2FnZXMoImNvd3Bsb3QiKQ0KaWYgKCFyZXF1aXJlKCJjYXJldCIpKSBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQppZiAoIXJlcXVpcmUoImRwbHlyIikpIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCmlmICghcmVxdWlyZSgiZ3JpZEV4dHJhIikpIGluc3RhbGwucGFja2FnZXMoImdyaWRFeHRyYSIpDQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KYGBgDQoNCmBgYHtyIGFwcGx5MSwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPUZBTFNFLCBldmFsPVRSVUV9DQojIExvYWQgcGFja2FnZXMNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoY293cGxvdCkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQojIyMjIFJlbW92ZSB2ZWlsLXR5cGUNCkFmdGVyIHRoaXMsIHdlIHdpbGwgcmVtb3ZlIHRoZSBjb2x1bW4gYHZlaWwtdHlwZWAsIGFzIGl0IG9ubHkgY29udGFpbnMgb25lIHZhbHVlIGNhbGxlZCBgcGA6DQpgYGB7ciB2ZWlsLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KbXVzaCA8LSBkYXRhLmZyYW1lKG11c2hbLCAtMTddKQ0KYGBgDQoNCiMjIyMgQ29udmVydCBjYXRlZ29yaWNhbCB2YXJpYWJsZXMNCkZpbmFsbHksIHdlIHdpbGwgY29udmVydCB0aGUgdmFyaWFibGVzIGludG8gZmFjdG9ycyAtLSBiZWNhdXNlIGFsbCBmZWF0dXJlcyBhcmUgY2F0ZWdvcmljYWwuIEhlcmUgaXMgYSBwcmV2aWV3IG9mIHRoZSBjb252ZXJ0ZWQgdmFyaWFibGVzOg0KYGBge3IgZmFjdG9yLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KbXVzaCA8LSBhcy5kYXRhLmZyYW1lKGxhcHBseShtdXNoLCBhcy5mYWN0b3IpKQ0KYGBgDQoNCmBgYHtyIHByZXZpZXcsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQ0Ka25pdHI6OmthYmxlKGhlYWQobXVzaCksDQogIGZvcm1hdCA9ICJodG1sIiwNCiAgY2FwdGlvbiA9ICImbmJzcDsmbmJzcDtDb252ZXJ0ZWQgdmFyaWFibGVzIiwNCiAgZXNjYXBlID0gRkFMU0UNCikgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQ0KICBzY3JvbGxfYm94KHdpZHRoID0gIjEwMCUiLCBoZWlnaHQgPSAiOTAlIikNCmBgYA0KDQojIyMgMi1kKSBEYXRhIHZpc3VhbGl6YXRpb24gey50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQojIyMjIFBvaXNvbm91cy1lZGlibGUgZGlzdHJpYnV0aW9uDQpIZXJlIHlvdSBjYW4gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGJvdGggY2xhc3NlczoNCmBgYHtyIGVkcG9pcGxvdCwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPVRSVUUsIGV2YWw9VFJVRSwgZmlnLmhlaWdodCA9IDMsIGZpZy53aWR0aCA9IDUsIG91dC5leHRyYT0nc3R5bGU9ImZsb2F0OmxlZnQiJ30NCmdncGxvdChkYXRhID0gbXVzaCwgYWVzKHggPSBjbGFzcywgZmlsbCA9IGNsYXNzKSkgKw0KICBnZW9tX2JhcihhbHBoYSA9IDAuNykgKw0KICBsYWJzKHRpdGxlID0gImNsYXNzIiwgeCA9IE5VTEwsIHkgPSBOVUxMKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMzM2NjMzMiLCAiIzRiMDA5MiIpKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTEsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIg0KICApDQpgYGANClwNClwNCmBgYHtyIGVkcG9pdGFibGUsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1GQUxTRSwgZXZhbD1UUlVFLCBmaWcuaGVpZ2h0ID0gNSwgZmlnLndpZHRoID0gNSwgb3V0LmV4dHJhPSdzdHlsZT0iZmxvYXQ6bGVmdCInfQ0KbXVzaF9jb3VudCA8LSBtdXNoICU+JQ0KICBjb3VudChjbGFzcykgJT4lDQogIG11dGF0ZShjbGFzcyA9IGlmZWxzZShjbGFzcyA9PSAiZSIsICJlZGlibGUiLCAicG9pc29ub3VzIikpDQoNCmtuaXRyOjprYWJsZShtdXNoX2NvdW50LCBjYXB0aW9uID0gImUgLyBwIHJlc3VsdHMiKSAlPiUNCiAga2FibGVfc3R5bGluZygic3RyaXBlZCIsICJob3ZlciIsDQogICAgZnVsbF93aWR0aCA9IEZBTFNFLA0KICAgIHBvc2l0aW9uID0gImNlbnRlciINCiAgKQ0KYGBgDQoNClwNClwNClwNCg0KPGhyIHN0eWxlPSJib3JkZXI6IDAuNHB4IHNvbGlkICNDQ0M7IG1hcmdpbi10b3A6IDI1cHg7Ij4NCg0KIyMjIyBGZWF0dXJlIGRpc3RyaWJ1dGlvbnMNCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBkaXN0cmlidXRpb25zIG9mIGFsbCB0aGUgZmVhdHVyZXM6DQpgYGB7ciBmZWF0ZGlzdCwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPVRSVUUsIGV2YWw9VFJVRSwgZmlnLmhlaWdodCA9IDI0LCBmaWcud2lkdGggPSA3fQ0KZmVhdHVyZV9uYW1lcyA8LSBuYW1lcyhtdXNoKVstMV0NCg0KZmVhdHVyZV9wbG90cyA8LSBsYXBwbHkoZmVhdHVyZV9uYW1lcywgZnVuY3Rpb24oZmVhdHVyZSkgew0KICBnZ3Bsb3QoZGF0YSA9IG11c2gsIGFlcyh4ID0gZ2V0KGZlYXR1cmUpLCBmaWxsID0gY2xhc3MpKSArDQogICAgZ2VvbV9iYXIoYWxwaGEgPSAwLjcpICsNCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoZmVhdHVyZSksIHggPSBOVUxMLCB5ID0gTlVMTCkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgICAgdmFsdWVzID0gYygiIzMzY2MzMyIsICIjNGIwMDkyIiksDQogICAgICBsYWJlbHMgPSBjKCJlZGlibGUiLCAicG9pc29ub3VzIikNCiAgICApICsNCiAgICB0aGVtZV9idygpICsNCiAgICB0aGVtZSgNCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksDQogICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIg0KICAgICkgKw0KICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gImNsYXNzIiwgbnJvdyA9IDIpKQ0KfSkNCg0KbGVnZW5kIDwtIGdldF9sZWdlbmQoZmVhdHVyZV9wbG90c1tbMV1dKQ0KDQpmb3IgKGkgaW4gc2VxX2Fsb25nKGZlYXR1cmVfcGxvdHMpKSB7DQogIGZlYXR1cmVfcGxvdHNbW2ldXSA8LSBmZWF0dXJlX3Bsb3RzW1tpXV0gKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQp9DQoNCnBsb3RfZ3JpZChwbG90bGlzdCA9IGZlYXR1cmVfcGxvdHMsIG5jb2wgPSAyLCBucm93ID0gMTEsIGxlZ2VuZCA9IGxlZ2VuZCkNCmBgYA0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMC40cHggc29saWQgI0NDQzsgbWFyZ2luLXRvcDogMjVweDsiPg0KDQojIyMjIEVkaWJsZSBmZWF0dXJlcw0KSW4gdGhlc2UgcGxvdHMsIHdlIGNhbiBzZWUgdGhhdCBjZXJ0YWluIGZlYXR1cmVzIGFyZSBpbXBvcnRhbnQgdG8gY2xhc3NpZnkgYSBtdXNocm9vbSBhcyAqZWRpYmxlKi4gXA0KSWYgYSB0ZXN0IG11c2hyb29tIGhhcyBvbmUgb2YgdGhlc2UgZmVhdHVyZXMsIGl0IGlzIG1vcmUgbGlrZWx5IHRvIGJlIHRoaXMgY2xhc3M6DQpgYGB7ciBlZGZlYXQsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1GQUxTRSwgZXZhbD1UUlVFLCBmaWcuaGVpZ2h0ID0gMTV9DQpkYXRhIDwtIGRhdGEuZnJhbWUoDQogIEF0dHJpYnV0ZSA9IGMoDQogICAgIm9kb3IiLCAic3RhbGsuY29sb3IuYmVsb3cucmluZyIsICJzdGFsay5jb2xvci5hYm92ZS5yaW5nIiwNCiAgICAic3BvcmUucHJpbnQuY29sb3IiLCAicG9wdWxhdGlvbiIsICJ2ZWlsLmNvbG9yIiwgImNhcC5jb2xvciIsDQogICAgImdpbGwuY29sb3IiLCAicmluZy50eXBlIiwgImNhcC5zaGFwZSINCiAgKSwNCiAgRGVzY3JpcHRpb24gPSBjKA0KICAgICJhbG1vbmQ9YSwgYW5pc2U9bCwgbm9uZT1uIiwgInJlZD1lLCBvcmFuZ2U9bywgZ3JheT1nIiwNCiAgICAicmVkPWUsIG9yYW5nZT1vLCBncmF5PWciLA0KICAgICJ5ZWxsb3c9eSwgcHVycGxlPXUsIG9yYW5nZT1vLCBidWZmPWIiLCAiYWJ1bmRhbnQ9YSwgbnVtZXJvdXM9biIsDQogICAgIm9yYW5nZT1vLCBicm93bj1uIiwgInJlZD1lLCBwdXJwbGU9dSIsICJncmF5PWcsIG9yYW5nZT1vIiwNCiAgICAiZmxhcmluZz1mIiwgInN1bmtlbj1zIg0KICApDQopDQpgYGANCg0KYGBge3IgZWRmZWF0dGFibGUsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQ0Ka25pdHI6OmthYmxlKGRhdGEsDQogIGZvcm1hdCA9ICJodG1sIiwNCiAgY2FwdGlvbiA9ICJFZGlibGUgZmVhdHVyZXMiLA0KICBjb2wubmFtZXMgPSBjKCJBdHRyaWJ1dGUiLCAiRGVzY3JpcHRpb24iKQ0KKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkNCmBgYA0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMC40cHggc29saWQgI0NDQzsgbWFyZ2luLXRvcDogMjVweDsiPg0KDQojIyMjIFBvaXNvbm91cyBmZWF0dXJlcw0KSW4gdGhlc2UgcGxvdHMsIHdlIGNhbiBzZWUgdGhhdCBjZXJ0YWluIGZlYXR1cmVzIGFyZSBpbXBvcnRhbnQgdG8gY2xhc3NpZnkgYSBtdXNocm9vbSBhcyAqcG9pc29ub3VzKi4gXA0KSWYgYSB0ZXN0IG11c2hyb29tIGhhcyBvbmUgb2YgdGhlc2UgZmVhdHVyZXMsIGl0IGlzIG1vcmUgbGlrZWx5IHRvIGJlIHRoaXMgY2xhc3M6DQpgYGB7ciBwb2lzZmVhdCwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPUZBTFNFLCBldmFsPVRSVUUsIGZpZy5oZWlnaHQgPSAxNX0NCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgQXR0cmlidXRlID0gYygNCiAgICAib2RvciIsICJzdGFsay5jb2xvci5iZWxvdy5yaW5nIiwgInJpbmcubnVtYmVyIiwgImdpbGwuY29sb3IiLA0KICAgICJzcG9yZS5wcmludC5jb2xvciIsICJjYXAuc2hhcGUiLCAidmVpbC5jb2xvciIsICJzdGFsay5jb2xvci5hYm92ZS5yaW5nIiwNCiAgICAicmluZy50eXBlIiwgImNhcC5zdXJmYWNlIg0KICApLA0KICBEZXNjcmlwdGlvbiA9IGMoDQogICAgInB1bmdlbnQ9cCwgZm91bD1mLCBjcmVvc290ZT1jLCBmaXNoeT15LCBzcGljeT1zLCBtdXN0eT1tIiwNCiAgICAiYnVmZj1iLCB5ZWxsb3c9eSwgY2lubmFtb249YyIsICJub25lPW4iLCAiYnVmZj1iLCBncmVlbj1yIiwNCiAgICAiZ3JlZW49ciIsICJjb25pY2FsPWMiLCAieWVsbG93PXkiLCAiYnVmZj1iLCB5ZWxsb3c9eSwgY2lubmFtb249YyIsDQogICAgImxhcmdlPWwiLCAiZ3Jvb3Zlcz1nIg0KICApDQopDQpgYGANCg0KYGBge3IgcG9pc2ZlYXR0YWJsZSwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPUZBTFNFLCBldmFsPVRSVUV9DQprbml0cjo6a2FibGUoZGF0YSwNCiAgZm9ybWF0ID0gImh0bWwiLA0KICBjYXB0aW9uID0gIlBvaXNvbm91cyBmZWF0dXJlcyIsDQogIGNvbC5uYW1lcyA9IGMoIkF0dHJpYnV0ZSIsICJEZXNjcmlwdGlvbiIpDQopICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCjxociBzdHlsZT0iYm9yZGVyOiAwLjRweCBzb2xpZCAjQ0NDOyBtYXJnaW4tdG9wOiAyNXB4OyI+DQoNCiMjIDMuIE1hY2hpbmUgbGVhcm5pbmcNCg0KIyMjIDMtYSkgTW9kZWwgcHJlcGFyYXRpb24gey50YWJzZXQgLnRhYnNldC1mYWRlfQ0KYGBge3IgaW5zdGFsbDIsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1GQUxTRSwgZXZhbD1UUlVFLCByZXN1bHRzPSJoaWRlIn0NCiMgSW5zdGFsbCBwYWNrYWdlcyBpZiB0aGV5IGFyZSBub3QgaW5zdGFsbGVkDQppZiAoIXJlcXVpcmUoImNhcmV0IikpIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmlmICghcmVxdWlyZSgia2VybmxhYiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJrZXJubGFiIikNCmlmICghcmVxdWlyZSgiZ2JtIikpIGluc3RhbGwucGFja2FnZXMoImdibSIpDQoNCiMgLi4uIG1ha2Ugc3VyZSBnY2MtZm9ydHJhbiBvciBzb21ldGhpbmcgc2ltaWxhciBpcyBpbnN0YWxsZWQNCmlmICghcmVxdWlyZSgicmFuZG9tRm9yZXN0IikpIGluc3RhbGwucGFja2FnZXMoInJhbmRvbUZvcmVzdCIpDQoNCmlmICghcmVxdWlyZSgiRk5OIikpIGluc3RhbGwucGFja2FnZXMoIkZOTiIpDQppZiAoIXJlcXVpcmUoImRvUGFyYWxsZWwiKSkgaW5zdGFsbC5wYWNrYWdlcygiZG9QYXJhbGxlbCIpDQppZiAoIXJlcXVpcmUoInN0cmluZ3IiKSkgaW5zdGFsbC5wYWNrYWdlcygic3RyaW5nciIpDQppZiAoIXJlcXVpcmUoInJwYXJ0IikpIGluc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCmlmICghcmVxdWlyZSgicnBhcnQucGxvdCIpKSBpbnN0YWxsLnBhY2thZ2VzKCJycGFydC5wbG90IikNCmlmICghcmVxdWlyZSgicmF0dGxlIikpIGluc3RhbGwucGFja2FnZXMoInJhdHRsZSIpDQppZiAoIXJlcXVpcmUoIlJDb2xvckJyZXdlciIpKSBpbnN0YWxsLnBhY2thZ2VzKCJSQ29sb3JCcmV3ZXIiKQ0KYGBgDQoNCmBgYHtyIGFwcGx5MiwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPUZBTFNFLCBldmFsPVRSVUV9DQojIExvYWQgcGFja2FnZXMNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGtlcm5sYWIpDQpsaWJyYXJ5KGdibSkNCmxpYnJhcnkocmFuZG9tRm9yZXN0KQ0KbGlicmFyeShGTk4pDQpsaWJyYXJ5KGRvUGFyYWxsZWwpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHJwYXJ0KQ0KbGlicmFyeShycGFydC5wbG90KQ0KbGlicmFyeShyYXR0bGUpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmBgYA0KDQpOb3cgd2Ugd2lsbCBzcGxpdCB0aGUgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0IGRhdGEuIFdlIHdpbGwgdXNlIHRoZSB0cmFpbmluZyBkYXRhIHRvIHRyYWluIHRoZSBtb2RlbCBhbmQgdGhlIHRlc3QgZGF0YSB0byB0ZXN0IHRoZSBtb2RlbC4NCmBgYHtyIGRhdGFwYXJ0LCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0Kc2V0LnNlZWQoNzUzKQ0KdHJhaW5faW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihtdXNoJGNsYXNzLA0KICBwID0gLjgsIGxpc3QgPSBGQUxTRSwgdGltZXMgPSAxDQopDQp0cmFpbiA8LSBtdXNoW3RyYWluX2luZGV4LCBdDQp0ZXN0IDwtIG11c2hbLXRyYWluX2luZGV4LCBdDQpgYGANCg0KIyMjIDMtYikgVHJhaW5pbmcgbW9kZWxzIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCB0cmFpbiBzZXZlcmFsIG1vZGVscyBhbmQgY29tcGFyZSB0aGVtIC0tIHNwZWQgdXAgdmlhIHBhcmFsbGVsIHByb2Nlc3NpbmcuDQoNCiMjIyMgR0xNDQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgKipMb2dpc3RpYyBSZWdyZXNzaW9uKiogKGdsbSkgbW9kZWw6DQpgYGB7ciB0cmFpbmdsbSwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0NCmNsIDwtIG1ha2VDbHVzdGVyKGRldGVjdENvcmVzKCkgLSAxKQ0KcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQ0Kc3RhcnRfdGltZSA8LSBTeXMudGltZSgpDQptb2RlbF9nbG0gPC0gdHJhaW4oY2xhc3MgfiAuLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJnbG0iKQ0Kc3RvcENsdXN0ZXIoY2wpDQplbmRfdGltZSA8LSBTeXMudGltZSgpDQplbmRfdGltZSAtIHN0YXJ0X3RpbWUNCmBgYA0KDQojIyMjIFNWTQ0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlICoqU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMqKiAobGluZWFyKSBtb2RlbDoNCmBgYHtyIHRyYWluc3ZtLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KY2wgPC0gbWFrZUNsdXN0ZXIoZGV0ZWN0Q29yZXMoKSAtIDEpDQpyZWdpc3RlckRvUGFyYWxsZWwoY2wpDQpzdGFydF90aW1lIDwtIFN5cy50aW1lKCkNCm1vZGVsX3N2bSA8LSB0cmFpbihjbGFzcyB+IC4sIGRhdGEgPSB0cmFpbiwgbWV0aG9kID0gInN2bUxpbmVhciIpDQpzdG9wQ2x1c3RlcihjbCkNCmVuZF90aW1lIDwtIFN5cy50aW1lKCkNCmVuZF90aW1lIC0gc3RhcnRfdGltZQ0KYGBgDQoNCiMjIyMgUkYNCkxldCdzIHRha2UgYSBsb29rIGF0IHRoZSAqKlJhbmRvbSBGb3Jlc3QqKiBtb2RlbDoNCmBgYHtyIHRyYWlucmYsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1UUlVFLCBldmFsPVRSVUV9DQpjbCA8LSBtYWtlQ2x1c3RlcihkZXRlY3RDb3JlcygpIC0gMSkNCnJlZ2lzdGVyRG9QYXJhbGxlbChjbCkNCnN0YXJ0X3RpbWUgPC0gU3lzLnRpbWUoKQ0KbW9kZWxfcmYgPC0gdHJhaW4oY2xhc3MgfiAuLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJyZiIpDQpzdG9wQ2x1c3RlcihjbCkNCmVuZF90aW1lIDwtIFN5cy50aW1lKCkNCmVuZF90aW1lIC0gc3RhcnRfdGltZQ0KYGBgDQoNCiMjIyMgS05ODQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgKipLLU5lYXJlc3QgTmVpZ2hib3JzKiogbW9kZWw6DQpgYGB7ciB0cmFpbmtubiwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0NCmNsIDwtIG1ha2VDbHVzdGVyKGRldGVjdENvcmVzKCkgLSAxKQ0KcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQ0Kc3RhcnRfdGltZSA8LSBTeXMudGltZSgpDQptb2RlbF9rbm4gPC0gdHJhaW4oY2xhc3MgfiAuLCBkYXRhID0gdHJhaW4sIG1ldGhvZCA9ICJrbm4iKQ0Kc3RvcENsdXN0ZXIoY2wpDQplbmRfdGltZSA8LSBTeXMudGltZSgpDQplbmRfdGltZSAtIHN0YXJ0X3RpbWUNCmBgYA0KDQojIyMjIEdCTQ0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlICoqR3JhZGllbnQgQm9vc3RpbmcgTWFjaGluZSoqIG1vZGVsOg0KYGBge3IgdHJhaW5nYm0sIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1UUlVFLCBldmFsPVRSVUV9DQpjbCA8LSBtYWtlQ2x1c3RlcihkZXRlY3RDb3JlcygpIC0gMSkNCnJlZ2lzdGVyRG9QYXJhbGxlbChjbCkNCnN0YXJ0X3RpbWUgPC0gU3lzLnRpbWUoKQ0KbW9kZWxfZ2JtIDwtIHRyYWluKGNsYXNzIH4gLiwgZGF0YSA9IHRyYWluLCBtZXRob2QgPSAiZ2JtIiwgdmVyYm9zZSA9IEZBTFNFKQ0Kc3RvcENsdXN0ZXIoY2wpDQplbmRfdGltZSA8LSBTeXMudGltZSgpDQplbmRfdGltZSAtIHN0YXJ0X3RpbWUNCmBgYA0KDQojIyMgMy1jKSBDb25mdXNpb24gbWF0cmljZXMgey50YWJzZXQgLnRhYnNldC1mYWRlfQ0KDQojIyMjIEdMTQ0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGNvbmZ1c2lvbiBtYXRyaXggb2YgdGhlICoqTG9naXN0aWMgUmVncmVzc2lvbioqIChnbG0pIG1vZGVsOg0KYGBge3IgY29uZmdsbSwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0NCmNvbmZ1c2lvbl9nbG0gPC0gY29uZnVzaW9uTWF0cml4KHByZWRpY3QobW9kZWxfZ2xtLCB0ZXN0KSwgdGVzdCRjbGFzcykNCmNvbmZ1c2lvbl9nbG0NCmBgYA0KDQojIyMjIFNWTQ0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGNvbmZ1c2lvbiBtYXRyaXggb2YgdGhlICoqU3VwcG9ydCBWZWN0b3IgTWFjaGluZXMqKiAobGluZWFyKSBtb2RlbDoNCmBgYHtyIGNvbmZzdm0sIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1UUlVFLCBldmFsPVRSVUV9DQpjb25mdXNpb25fc3ZtIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0KG1vZGVsX3N2bSwgdGVzdCksIHRlc3QkY2xhc3MpDQpjb25mdXNpb25fc3ZtDQpgYGANCg0KIyMjIyBSRg0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGNvbmZ1c2lvbiBtYXRyaXggb2YgdGhlICoqUmFuZG9tIEZvcmVzdCoqIG1vZGVsOg0KYGBge3IgY29uZnJmLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KY29uZnVzaW9uX3JmIDwtIGNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0KG1vZGVsX3JmLCB0ZXN0KSwgdGVzdCRjbGFzcykNCmNvbmZ1c2lvbl9yZg0KYGBgDQoNCiMjIyMgS05ODQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgY29uZnVzaW9uIG1hdHJpeCBvZiB0aGUgKipLLU5lYXJlc3QgTmVpZ2hib3JzKiogbW9kZWw6DQpgYGB7ciBjb25ma25uLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KY29uZnVzaW9uX2tubiA8LSBjb25mdXNpb25NYXRyaXgocHJlZGljdChtb2RlbF9rbm4sIHRlc3QpLCB0ZXN0JGNsYXNzKQ0KY29uZnVzaW9uX2tubg0KYGBgDQoNCiMjIyMgR0JNDQpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgY29uZnVzaW9uIG1hdHJpeCBvZiB0aGUgKipHcmFkaWVudCBCb29zdGluZyBNYWNoaW5lKiogbW9kZWw6DQpgYGB7ciBjb25mZ2JtLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KY29uZnVzaW9uX2dibSA8LSBjb25mdXNpb25NYXRyaXgocHJlZGljdChtb2RlbF9nYm0sIHRlc3QpLCB0ZXN0JGNsYXNzKQ0KY29uZnVzaW9uX2dibQ0KYGBgDQoNCiMjIyAzLWQtSSkgSW1wb3J0YW5jZSBwbG90IHsudGFic2V0IC50YWJzZXQtZmFkZX0NCk9uZSBvZiB0aGUgbW9zdCBhY2N1cmF0ZSBtb2RlbHMgaXMgYFJhbmRvbUZvcmVzdGAuIFdpdGggYFJhbmRvbUZvcmVzdGAgdGhlIGZ1bmN0aW9uIGB2YXJJbXAoKWAgc2hvd3MgdGhlIHBlcm11dGF0aW9uIGltcG9ydGFuY2UuIEFzIHNob3duIGluIHRoZSBpbXBvcnRhbmNlIHBsb3Qgb2YgdGhlIG1vZGVsLCB0aGUgb2RvciAqKm5vbmUqKiBpcyB0aGUgbW9zdCBzaWduaWZpY2FudCBpbmRpY2F0b3IgZm9yIGFuICplZGlibGUqICBtdXNocm9vbS4NCmBgYHtyIGltcHBsb3QsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1UUlVFLCBldmFsPVRSVUV9DQp2YXJfaW1wb3J0YW5jZSA8LSB2YXJJbXAobW9kZWxfcmYkZmluYWxNb2RlbCkNCnZhcl9kZiA8LSBhcy5kYXRhLmZyYW1lKHZhcl9pbXBvcnRhbmNlKQ0Kc29ydGVkX3ZhcnMgPC0gdmFyX2RmICU+JQ0KICBhcnJhbmdlKGRlc2MoT3ZlcmFsbCkpICU+JQ0KICBoZWFkKDIwKQ0KDQp0b3RhbCA8LSBzdW0oc29ydGVkX3ZhcnMkT3ZlcmFsbCkNCnNvcnRlZF92YXJzJE92ZXJhbGwgPC0gKHNvcnRlZF92YXJzJE92ZXJhbGwgLyB0b3RhbCkgKiAxMDANCg0KeF9sYWJlbHMgPC0gc3RyX3N1Yihyb3cubmFtZXMoc29ydGVkX3ZhcnMpLCBlbmQgPSAtMikgJT4lDQogIHBhc3RlMCgiXyIsIHN0cl9zdWIocm93Lm5hbWVzKHNvcnRlZF92YXJzKSwgLTEpKQ0KDQpnZ3Bsb3Qoc29ydGVkX3ZhcnMsIGFlcygNCiAgeCA9IHJlb3JkZXIocm93Lm5hbWVzKHNvcnRlZF92YXJzKSwgLU92ZXJhbGwpLA0KICB5ID0gT3ZlcmFsbA0KKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgYWxwaGEgPSAwLjYsIGZpbGwgPSAiIzAwMzAzRiIpICsNCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwsIHRpdGxlID0gIlZhcmlhYmxlIHZzLiBJbXBvcnRhbmNlIChpbiAlKSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKQ0KICApICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSB4X2xhYmVscykNCmBgYA0KDQojIyMgMy1kLUlJKSBSZWxhdGlvbiBwbG90cyB7LnRhYnNldCAudGFic2V0LWZhZGV9DQpIZXJlIGFyZSBzb21lIHBsb3RzIHRoYXQgc2hvdyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gc29tZSBvZiB0aGUgdmFyaWFibGVzIGFuZCB0aGUgY2xhc3Mgb2YgdGhlIG11c2hyb29tcy4NCg0KIyMjIyBHaWxsIHNpemUgdnMuIE9kb3INCmBgYHtyIHBsb3QxLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KZ2dwbG90KG11c2gsIGFlcyh4ID0gZ2lsbC5zaXplLCB5ID0gb2RvcikpICsNCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gY2xhc3MsIGFscGhhID0gY2xhc3MpLA0KICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4yKQ0KICApICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImUiID0gIiMzM2NjMzMiLCAicCIgPSAiIzRiMDA5MiIpKSArDQogIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBjKCJlIiA9IDAuNCwgInAiID0gMC42KSkgKw0KICBzY2FsZV9zaXplKGd1aWRlID0gIm5vbmUiKSArDQogIGxhYnModGl0bGUgPSAiR2lsbCBzaXplIHZzLiBPZG9yIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCgNCiAgICAgIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLA0KICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDAsIDAsIDUsIDApDQogICAgKSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDcsIDAsIDAsIDApKSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQobWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDAsIDE1LCAwLCAwKSkNCiAgKQ0KYGBgDQoNCiMjIyMgU3BvcmUgcHJpbnQgY29sb3IgdnMuIEdpbGwgY29sb3INCmBgYHtyIHBsb3QyLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KZ2dwbG90KG11c2gsIGFlcyh4ID0gc3BvcmUucHJpbnQuY29sb3IsIHkgPSBnaWxsLmNvbG9yKSkgKw0KICBnZW9tX2ppdHRlcihhZXMoY29sb3IgPSBjbGFzcywgYWxwaGEgPSBjbGFzcyksDQogICAgcG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjIpDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZSIgPSAiIzMzY2MzMyIsICJwIiA9ICIjNGIwMDkyIikpICsNCiAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoImUiID0gMC40LCAicCIgPSAwLjYpKSArDQogIHNjYWxlX3NpemUoZ3VpZGUgPSAibm9uZSIpICsNCiAgbGFicyh0aXRsZSA9ICJTcG9yZSBwcmludCBjb2xvciB2cy4gR2lsbCBjb2xvciIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoDQogICAgICBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiwNCiAgICAgIG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigwLCAwLCA1LCAwKQ0KICAgICksDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbig3LCAwLCAwLCAwKSksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigwLCAxNSwgMCwgMCkpDQogICkNCmBgYA0KDQojIyMjIEhhYml0YXQgdnMuIFBvcHVsYXRpb24NCmBgYHtyIHBsb3QzLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KZ2dwbG90KG11c2gsIGFlcyh4ID0gaGFiaXRhdCwgeSA9IHBvcHVsYXRpb24pKSArDQogIGdlb21faml0dGVyKGFlcyhjb2xvciA9IGNsYXNzLCBhbHBoYSA9IGNsYXNzKSwNCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMikNCiAgKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJlIiA9ICIjMzNjYzMzIiwgInAiID0gIiM0YjAwOTIiKSkgKw0KICBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzID0gYygiZSIgPSAwLjQsICJwIiA9IDAuNikpICsNCiAgc2NhbGVfc2l6ZShndWlkZSA9ICJub25lIikgKw0KICBsYWJzKHRpdGxlID0gIkhhYml0YXQgdnMuIFBvcHVsYXRpb24iKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KA0KICAgICAgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIsDQogICAgICBtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMCwgMCwgNSwgMCkNCiAgICApLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oNywgMCwgMCwgMCkpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBnZ3Bsb3QyOjptYXJnaW4oMCwgMTUsIDAsIDApKQ0KICApDQpgYGANCg0KIyMjIyBDYXAgc2hhcGUgdnMuIFN0YWxrIGNvbG9yIGJlbG93IHJpbmcNCmBgYHtyIHBsb3Q0LCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KZ2dwbG90KG11c2gsIGFlcyh4ID0gY2FwLnNoYXBlLCB5ID0gc3RhbGsuY29sb3IuYmVsb3cucmluZykpICsNCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gY2xhc3MsIGFscGhhID0gY2xhc3MpLA0KICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4yKQ0KICApICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImUiID0gIiMzM2NjMzMiLCAicCIgPSAiIzRiMDA5MiIpKSArDQogIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXMgPSBjKCJlIiA9IDAuNCwgInAiID0gMC42KSkgKw0KICBzY2FsZV9zaXplKGd1aWRlID0gIm5vbmUiKSArDQogIGxhYnModGl0bGUgPSAiQ2FwIHNoYXBlIHZzLiBTdGFsayBjb2xvciBiZWxvdyByaW5nIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dCgNCiAgICAgIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLA0KICAgICAgbWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDAsIDAsIDUsIDApDQogICAgKSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDcsIDAsIDAsIDApKSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQobWFyZ2luID0gZ2dwbG90Mjo6bWFyZ2luKDAsIDE1LCAwLCAwKSkNCiAgKQ0KYGBgDQoNCiMjIyAzLWUpIERlY2lzaW9uIHRyZWVzIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCkZpbmlzaGluZyB0aGUgZG9jdW1lbnQsIHdlIGFyZSBpbGx1c3RyYXRpbmcgdmFyaW91cyBkZWNpc2lvbiB0cmVlcy4gVGhlIGZpcnN0IG9uZSBpcyBhIGdlbmVyYWwgdHJlZS4gVGhlbiwgd2UgYXJlIGdvaW5nIHRvIHJlbW92ZSB0aGUgYG9kb3JgIHZhcmlhYmxlIGFuZCBzZWUgaG93IHRoZSB0cmVlIGNoYW5nZXMuIFRoZSBuZXh0IHRyZWUgZG9lcyBub3QgcHJvdmlkZSBwcm9wZXJ0aWVzIHRoYXQgYXJlIGhhcmQgdG8gdGVsbC4gVGhlIGxhc3Qgb25lIGp1c3QgcHJvdmlkZXMgcHJvcGVydGllcyBhY2NvcmRpbmcgdG8gdGhlIHRvcCB2aWV3IG9mIGEgbXVzaHJvb20uDQoNCmBgYHtyIHRyZWVzLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFfQ0KbXVzaF9mYWMgPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkobXVzaCwgYXMuZmFjdG9yKSkNCmxvc3NfbWF0cml4IDwtIG1hdHJpeChjKDAsIDEsIDQsIDApLCBieXJvdyA9IFRSVUUsIG5yb3cgPSAyKQ0KYGBgCQ0KDQojIyMjIEdlbmVyYWwgdHJlZQ0KYGBge3IgZ2VudHJlZSwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPVRSVUUsIGV2YWw9VFJVRSwgZmlnLndpZHRoPTl9DQp0cmVlMSA8LSBycGFydChjbGFzcyB+IC4sDQogIG1pbmJ1Y2tldCA9IDEsDQogIG1ldGhvZCA9ICJjbGFzcyIsDQogIHBhcm1zID0gbGlzdChsb3NzID0gbG9zc19tYXRyaXgpLA0KICBkYXRhID0gbXVzaF9mYWMNCikNCg0KZmFuY3lScGFydFBsb3QodHJlZTEsDQogIHBhbGV0dGVzID0gYygiR3JlZW5zIiwgIlB1cnBsZXMiKSwNCiAgY2FwdGlvbiA9IE5VTEwsIGNleCA9IDAuOCwNCiAgbWFpbiA9ICJHZW5lcmFsIHRyZWUiDQopDQpgYGANCg0KPGhyIHN0eWxlPSJib3JkZXI6IDAuNHB4IHNvbGlkICNDQ0M7IG1hcmdpbi10b3A6IDI1cHg7Ij4NCg0KIyMjIyBXaXRob3V0IE9kb3INCmBgYHtyIG5vb2RvciwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPVRSVUUsIGV2YWw9VFJVRSwgZmlnLndpZHRoPTl9DQptdXNoX2ZhY19ub19vZG9yIDwtIG11c2hfZmFjDQptdXNoX2ZhY19ub19vZG9yJG9kb3IgPC0gTlVMTA0KDQp0cmVlMiA8LSBycGFydChjbGFzcyB+IC4sDQogIG1pbmJ1Y2tldCA9IDIsDQogIG1ldGhvZCA9ICJjbGFzcyIsDQogIHBhcm1zID0gbGlzdChsb3NzID0gbG9zc19tYXRyaXgpLA0KICBkYXRhID0gbXVzaF9mYWNfbm9fb2Rvcg0KKQ0KDQpmYW5jeVJwYXJ0UGxvdCh0cmVlMiwNCiAgcGFsZXR0ZXMgPSBjKCJHcmVlbnMiLCAiUHVycGxlcyIpLA0KICBjYXB0aW9uID0gTlVMTCwgY2V4ID0gMC44LA0KICBtYWluID0gIldpdGhvdXQgT2RvciINCikNCmBgYA0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMC40cHggc29saWQgI0NDQzsgbWFyZ2luLXRvcDogMjVweDsiPg0KDQojIyMjIFdpdGhvdXQgaGFyZCB0byB0ZWxsIHByb3BlcnRpZXMNCmBgYHtyIG5vaGFyZHByb3BzLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFLCBmaWcud2lkdGg9OX0NCm11c2hfZmFjX2hhcmQgPC0gbXVzaF9mYWMgJT4lDQogIHNlbGVjdCgNCiAgICAtb2RvciwgLXNwb3JlLnByaW50LmNvbG9yLCAtZ2lsbC5zaXplLCAtYnJ1aXNlcywgLXJpbmcudHlwZSwNCiAgICAtc3RhbGsuc3VyZmFjZS5hYm92ZS5yaW5nLCAtc3RhbGsuc3VyZmFjZS5iZWxvdy5yaW5nLCAtc3RhbGsucm9vdA0KICApICU+JQ0KICBtdXRhdGVfYXQodmFycygNCiAgICBnaWxsLmNvbG9yLCBjYXAuY29sb3IsIHN0YWxrLmNvbG9yLmFib3ZlLnJpbmcsDQogICAgc3RhbGsuY29sb3IuYmVsb3cucmluZw0KICApLCB+IGlmX2Vsc2UoLiAlaW4lIGMoImIiLCAiaCIsICJjIiksICJuIiwgLikpDQoNCnRyZWUzIDwtIHJwYXJ0KGNsYXNzIH4gLiwNCiAgbWluYnVja2V0ID0gMjAwLA0KICBtZXRob2QgPSAiY2xhc3MiLA0KICBwYXJtcyA9IGxpc3QobG9zcyA9IGxvc3NfbWF0cml4KSwNCiAgZGF0YSA9IG11c2hfZmFjX2hhcmQNCikNCg0KZmFuY3lScGFydFBsb3QodHJlZTMsDQogIHBhbGV0dGVzID0gYygiR3JlZW5zIiwgIlB1cnBsZXMiKSwNCiAgY2FwdGlvbiA9IE5VTEwsDQogIG1haW4gPSAiV2l0aG91dCBoYXJkIHRvIHRlbGwgcHJvcGVydGllcyINCikNCmBgYA0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMC40cHggc29saWQgI0NDQzsgbWFyZ2luLXRvcDogMjVweDsiPg0KDQojIyMjIFRvcCB2aWV3IG9ubHkNCmBgYHtyIHRvcGcsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1UUlVFLCBldmFsPVRSVUUsIGZpZy53aWR0aD05fQ0KbXVzaF9mYWNfdG9wIDwtIHN1YnNldChtdXNoX2ZhYywgc2VsZWN0ID0gLWMoDQogIGdpbGwuc2l6ZSwNCiAgZ2lsbC5jb2xvciwgZ2lsbC5hdHRhY2htZW50LCBnaWxsLnNwYWNpbmcsDQogIHN0YWxrLnNoYXBlLCBzdGFsay5yb290LCBzdGFsay5zdXJmYWNlLmFib3ZlLnJpbmcsDQogIHN0YWxrLnN1cmZhY2UuYmVsb3cucmluZywgc3RhbGsuY29sb3IuYWJvdmUucmluZywNCiAgc3RhbGsuY29sb3IuYmVsb3cucmluZywgc3BvcmUucHJpbnQuY29sb3INCikpDQoNCnRyZWU0IDwtIHJwYXJ0KGNsYXNzIH4gLiwNCiAgbWluYnVja2V0ID0gMSwNCiAgbWV0aG9kID0gImNsYXNzIiwNCiAgcGFybXMgPSBsaXN0KGxvc3MgPSBsb3NzX21hdHJpeCksDQogIGRhdGEgPSBtdXNoX2ZhY190b3ANCikNCg0KZmFuY3lScGFydFBsb3QodHJlZTQsDQogIHBhbGV0dGVzID0gYygiR3JlZW5zIiwgIlB1cnBsZXMiKSwNCiAgY2FwdGlvbiA9IE5VTEwsIGNleCA9IDAuOCwNCiAgbWFpbiA9ICJUb3AgdmlldyBvbmx5Ig0KKQ0KYGBgDQoNCjxociBzdHlsZT0iYm9yZGVyOiAwLjRweCBzb2xpZCAjQ0NDOyBtYXJnaW4tdG9wOiAyNXB4OyI+DQoNCiMjIDQuIFJlYWwgbXVzaHJvb21zIHsudGFic2V0IC50YWJzZXQtZmFkZX0NCkluIHRoaXMgc2VjdGlvbiwgd2UgYXJlIGdvaW5nIHRvIHNob3cgc29tZSByZWFsIG11c2hyb29tcy4gSW4gYWRkaXRpb24gdG8gdGhhdCwgd2UgYXJlIGdvaW5nIHRvIHByb3ZpZGUgdHJlZXMgZm9yIGNsYXNzZnlpbmcgdGhlbS4NCg0KIyMjIFNoaWVsZCBEYXBwZXJsaW5nIFsxXQ0KDQo8Y2VudGVyPg0KDQpgYGB7ciBzaGllbGQsIGVjaG89RkFMU0UsIGZpZy5zaG93PSdob2xkJywgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9JzM1JScsIG91dC5leHRyYT0nc3R5bGU9Im1hcmdpbjoxMHB4Iid9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiYXNzZXRzL3NoaWVsZDEuanBnIikNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJhc3NldHMvc2hpZWxkMi5qcGciKQ0KYGBgDQoNCjwvY2VudGVyPg0KDQpgYGB7ciB0cmVlMSwgZmlnLmFsaWduPSdjZW50ZXInLCBlY2hvPVRSVUUsIGV2YWw9VFJVRSwgZmlnLndpZHRoPTl9DQpmYW5jeVJwYXJ0UGxvdCh0cmVlMywNCiAgcGFsZXR0ZXMgPSBjKCJHcmVlbnMiLCAiUHVycGxlcyIpLA0KICBjYXB0aW9uID0gTlVMTCwgbWFpbiA9IE5VTEwNCikNCmBgYA0KDQo8aHIgc3R5bGU9ImJvcmRlcjogMC40cHggc29saWQgI0NDQzsgbWFyZ2luLXRvcDogMjVweDsiPg0KDQojIyMgRGVhZGx5IERhcHBlcmxpbmcgWzJdDQoNCjxjZW50ZXI+DQoNCmBgYHtyIGRlYWRseSwgZWNobz1GQUxTRSwgZmlnLnNob3c9J2hvbGQnLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0nNDclJywgb3V0LmV4dHJhPSdzdHlsZT0ibWFyZ2luOjEwcHgiJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJhc3NldHMvZGVhZGx5MS5qcGciKQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImFzc2V0cy9kZWFkbHkyLmpwZyIpDQpgYGANCg0KPC9jZW50ZXI+DQoNCmBgYHtyIHRyZWUyLCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFLCBmaWcud2lkdGg9OX0NCmZhbmN5UnBhcnRQbG90KHRyZWUzLA0KICBwYWxldHRlcyA9IGMoIkdyZWVucyIsICJQdXJwbGVzIiksDQogIGNhcHRpb24gPSBOVUxMLCBtYWluID0gTlVMTA0KKQ0KYGBgDQoNCjxociBzdHlsZT0iYm9yZGVyOiAwLjRweCBzb2xpZCAjQ0NDOyBtYXJnaW4tdG9wOiAyNXB4OyI+DQoNCiMjIyBNZWR1c2EgTXVzaHJvb20gWzNdDQoNCjxjZW50ZXI+DQoNCmBgYHtyIG1lZHVzYSwgZWNobz1GQUxTRSwgZmlnLnNob3c9J2hvbGQnLCBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aD0nNDclJywgb3V0LmV4dHJhPSdzdHlsZT0ibWFyZ2luOjEwcHgiJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJhc3NldHMvbWVkdXNhMS5qcGVnIikNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJhc3NldHMvbWVkdXNhMi5qcGVnIikNCmBgYA0KDQo8L2NlbnRlcj4NCg0KYGBge3IgdHJlZTMsIGZpZy5hbGlnbj0nY2VudGVyJywgZWNobz1UUlVFLCBldmFsPVRSVUUsIGZpZy53aWR0aD05fQ0KZmFuY3lScGFydFBsb3QodHJlZTMsDQogIHBhbGV0dGVzID0gYygiR3JlZW5zIiwgIlB1cnBsZXMiKSwNCiAgY2FwdGlvbiA9IE5VTEwsIG1haW4gPSBOVUxMDQopDQpgYGANCg0KPGhyIHN0eWxlPSJib3JkZXI6IDAuNHB4IHNvbGlkICNDQ0M7IG1hcmdpbi10b3A6IDI1cHg7Ij4NCg0KIyMjIElua3kgTXVzaHJvb20gWzRdDQo8Y2VudGVyPg0KDQpgYGB7ciBpbmt5LCBlY2hvPUZBTFNFLCBmaWcuc2hvdz0naG9sZCcsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSczNSUnLCBvdXQuZXh0cmE9J3N0eWxlPSJtYXJnaW46MTBweCInfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImFzc2V0cy9pbmt5MS5qcGVnIikNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJhc3NldHMvaW5reTIuanBlZyIpDQpgYGANCg0KPC9jZW50ZXI+DQoNCmBgYHtyIHRyZWU0LCBmaWcuYWxpZ249J2NlbnRlcicsIGVjaG89VFJVRSwgZXZhbD1UUlVFLCBmaWcud2lkdGg9OX0NCmZhbmN5UnBhcnRQbG90KHRyZWUzLA0KICBwYWxldHRlcyA9IGMoIkdyZWVucyIsICJQdXJwbGVzIiksDQogIGNhcHRpb24gPSBOVUxMLCBtYWluID0gTlVMTA0KKQ0KYGBgDQoNCjxociBzdHlsZT0iYm9yZGVyOiAwLjRweCBzb2xpZCAjQ0NDOyBtYXJnaW4tdG9wOiAyNXB4OyI+DQoNCiMjIDUuIENvbmNsdXNpb24NCkFzIHdlIGRlbHZlZCBpbnRvIHRoZSBhbmFseXNpcyBvZiB0aGUgbXVzaHJvb20gZGF0YXNldCwgZXhwbG9yaW5nIGl0cyB2YXJpb3VzIGZlYXR1cmVzIGFuZCB0aGVpciBpbXBvcnRhbmNlLCB3ZSB3ZXJlIGFibGUgdG8gdHJhaW4gc2V2ZXJhbCBtb2RlbHMgYW5kIGNvbXBhcmUgdGhlbS4gV2l0aCB0aGF0IHdlIGdhaW5lZCB2YWx1YWJsZSBpbnNpZ2h0cyBpbnRvIHRoZSBjaGFyYWN0ZXJpc3RpY3MgdGhhdCBkaWZmZXJlbnRpYXRlIGVkaWJsZSBhbmQgcG9pc29ub3VzIG11c2hyb29tcy4NClRocm91Z2ggb3VyIGVmZm9ydHMsIHdlIHN1Y2Nlc3NmdWxseSBkZXZlbG9wZWQgZGVjaXNpb24gdHJlZXMgdGhhdCBwcm92ZWQgZWZmZWN0aXZlIGluIGNsYXNzaWZ5aW5nIHJlYWwgbXVzaHJvb21zIGJhc2VkIG9uIHRoZSBrbm93bGVkZ2Ugb2J0YWluZWQgZnJvbSB0aGUgZGF0YXNldC4NCkluIGNvbmNsdXNpb24sIHRoaXMgcHJvamVjdCBzZXJ2ZXMgYXMgYSBmb3VuZGF0aW9uIGZvciBmdXR1cmUgbXVzaHJvb20gY2xhc3NpZmljYXRpb24gYW5kIHJlbGF0ZWQgc3R1ZGllcy4NCkJ5IHV0aWxpemluZyB0aGUgcG93ZXIgb2YgbWFjaGluZSBsZWFybmluZywgd2UgY2FuIGNvbnRpbnVlIHRvIG1ha2Ugc3RyaWRlcyBpbiB1bmRlcnN0YW5kaW5nIGFuZCB1dGlsaXppbmcgdGhlIHdvcmxkIG9mIG11c2hyb29tcyBmb3IgdGhlIGJldHRlcm1lbnQgb2Ygc29jaWV0eS4NCg0KPGhyIHN0eWxlPSJib3JkZXI6IDAuNHB4IHNvbGlkICNDQ0M7IG1hcmdpbi10b3A6IDI1cHg7Ij4NCg0KIyMgUmVmZXJlbmNlcw0KWzFdIGlOYXR1cmFsaXN0OiBTaGllbGQgRGFwcGVybGluZy4gVVJMOiBodHRwczovL3d3dy5pbmF0dXJhbGlzdC5vcmcvb2JzZXJ2YXRpb25zLzE1NjYyMzIxOS4gQWNjZXNzZWQ6IEp1bHkgMjAsIDIwMjMgXA0KWzJdIGlOYXR1cmFsaXN0OiBEZWFkbHkgRGFwcGVybGluZy4gVVJMOiBodHRwczovL3d3dy5pbmF0dXJhbGlzdC5vcmcvb2JzZXJ2YXRpb25zLzcwNzU5MjQyLiBBY2Nlc3NlZDogSnVseSAyMCwgMjAyMy4gXA0KWzNdIGlOYXR1cmFsaXN0OiBNZWR1c2EgTXVzaHJvb20uIFVSTDogaHR0cHM6Ly93d3cuaW5hdHVyYWxpc3Qub3JnL29ic2VydmF0aW9ucy85MTg2ODc3OS4gQWNjZXNzZWQ6IEp1bHkgMjAsIDIwMjMuIFwNCls0XSBpTmF0dXJhbGlzdDogSW5reSBNdXNocm9vbS4gVVJMOiBodHRwczovL3d3dy5pbmF0dXJhbGlzdC5vcmcvb2JzZXJ2YXRpb25zLzE3MDg0NDQzOC4gQWNjZXNzZWQ6IEp1bHkgMjAsIDIwMjMuDQo=