diff --git a/2-Regression/4-Logistic/solution/R/lesson_4.Rmd b/2-Regression/4-Logistic/solution/R/lesson_4.Rmd
index 971fc56ba0..2199fb34f4 100644
--- a/2-Regression/4-Logistic/solution/R/lesson_4.Rmd
+++ b/2-Regression/4-Logistic/solution/R/lesson_4.Rmd
@@ -12,21 +12,21 @@ output:
## Build a logistic regression model - Lesson 4
-![Logistic vs. linear regression infographic](https://github.com/microsoft/ML-For-Beginners/blob/main/2-Regression/4-Logistic/images/linear-vs-logistic.png)
+![Logistic vs. linear regression infographic](../../images/linear-vs-logistic.png)
#### **[Pre-lecture quiz](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/15/)**
#### Introduction
-In this final lesson on Regression, one of the basic *classic* ML techniques, we will take a look at Logistic Regression. You would use this technique to discover patterns to predict `binary` `categories`. Is this candy chocolate or not? Is this disease contagious or not? Will this customer choose this product or not?
+In this final lesson on Regression, one of the basic *classic* ML techniques, we will take a look at Logistic Regression. You would use this technique to discover patterns to predict binary categories. Is this candy chocolate or not? Is this disease contagious or not? Will this customer choose this product or not?
In this lesson, you will learn:
- Techniques for logistic regression
-✅ Deepen your understanding of working with this type of regression in this [Learn module](https://docs.microsoft.com/learn/modules/train-evaluate-classification-models?WT.mc_id=academic-77952-leestott)
+✅ Deepen your understanding of working with this type of regression in this [Learn module](https://learn.microsoft.com/training/modules/introduction-classification-models/?WT.mc_id=academic-77952-leestott)
-#### **Prerequisite**
+## Prerequisite
Having worked with the pumpkin data, we are now familiar enough with it to realize that there's one binary category that we can work with: `Color`.
@@ -70,7 +70,17 @@ Logistic regression differs from linear regression, which you learned about prev
Logistic regression does not offer the same features as linear regression. The former offers a prediction about a `binary category` ("orange or not orange") whereas the latter is capable of predicting `continual values`, for example given the origin of a pumpkin and the time of harvest, *how much its price will rise*.
-![Infographic by Dasani Madipalli](../../images/pumpkin-classifier.png){width="600"}
+![Infographic by Dasani Madipalli](../../images/pumpkin-classifier.png)
+
+### Other classifications
+
+There are other types of logistic regression, including multinomial and ordinal:
+
+- **Multinomial**, which involves having more than one category - "Orange, White, and Striped".
+
+- **Ordinal**, which involves ordered categories, useful if we wanted to order our outcomes logically, like our pumpkins that are ordered by a finite number of sizes (mini,sm,med,lg,xl,xxl).
+
+![Multinomial vs ordinal regression](../../images/multinomial-vs-ordinal.png)
#### **Variables DO NOT have to correlate**
@@ -82,9 +92,11 @@ Logistic regression will give more accurate results if you use more data; our sm
✅ Think about the types of data that would lend themselves well to logistic regression
-## 1. Tidy the data
+## Exercise - tidy the data
+
+First, clean the data a bit, dropping null values and selecting only some of the columns:
-Now, the fun begins! Let's start by importing the data, cleaning the data a bit, dropping rows containing missing values and selecting only some of the columns:
+1. Add the following code:
```{r, tidyr, message=F, warning=F}
# Load the core tidyverse packages
@@ -106,34 +118,50 @@ pumpkins_select <- pumpkins_select %>%
# View the first few rows
pumpkins_select %>%
slice_head(n = 5)
-
```
-Sometimes, we may want some little more information on our data. We can have a look at the `data`, `its structure` and the `data type` of its features by using the [*glimpse()*](https://pillar.r-lib.org/reference/glimpse.html) function as below:
+You can always take a peek at your new dataframe, by using the [*glimpse()*](https://pillar.r-lib.org/reference/glimpse.html) function as below:
+
```{r glimpse}
pumpkins_select %>%
glimpse()
```
-Wow! Seems that all our columns are all of type *character*, further alluding that they are all categorical.
-
Let's confirm that we will actually be doing a binary classification problem:
```{r distinct color}
# Subset distinct observations in outcome column
pumpkins_select %>%
distinct(color)
+```
+
+### Visualization - categorical plot
+By now you have loaded up the pumpkin data once again and cleaned it so as to preserve a dataset containing a few variables, including Color. Let's visualize the dataframe in the notebook using ggplot library.
+
+The ggplot library offers some neat ways to visualize your data. For example, you can compare distributions of the data for each Variety and Color in a categorical plot.
+
+1. Create such a plot by using the geombar function, using our pumpkin data, and specifying a color mapping for each pumpkin category (orange or white):
+```{r}
+# Specify colors for each value of the hue variable
+palette <- c(ORANGE = "orange", WHITE = "wheat")
+
+# Create the bar plot
+ggplot(pumpkins_select, aes(y = variety, fill = color)) +
+ geom_bar(position = "dodge") +
+ scale_fill_manual(values = palette) +
+ labs(y = "Variety", fill = "Color") +
+ theme_minimal()
```
-🥳🥳 That went down well!
+By observing the data, you can see how the Color data relates to Variety.
-## 2. Explore the data
+✅ Given this categorical plot, what are some interesting explorations you can envision?
-The goal of data exploration is to try to understand the `relationships` between its attributes; in particular, any apparent correlation between the *features* and the *label* your model will try to predict. One way of doing this is by using data visualization.
+### Data pre-processing: feature encoding
-Given our the data types of our columns, we can `encode` them and be on our way to making some visualizations. This simply involves `translating` a column with `categorical values` for example our columns of type *char*, into one or more `numeric columns` that take the place of the original. - Something we did in our [last lesson](https://github.com/microsoft/ML-For-Beginners/blob/main/2-Regression/3-Linear/solution/lesson_3.html).
+Our pumpkins dataset contains string values for all its columns. Working with categorical data is intuitive for humans but not for machines. Machine learning algorithms work well with numbers. That's why encoding is a very important step in the data pre-processing phase, since it enables us to turn categorical data into numerical data, without losing any information. Good encoding leads to building a good model.
For feature encoding there are two main types of encoders:
@@ -164,26 +192,14 @@ baked_pumpkins %>%
slice_head(n = 5)
```
-Now, let's make a categorical plot showing the distribution of the predictors with respect to the outcome color!
+✅ What are the advantages of using an ordinal encoder for the Item Size column?
-```{r cat plot pumpkins-colors-variety}
-# Specify colors for each value of the hue variable
-palette <- c(ORANGE = "orange", WHITE = "wheat")
+### Analyse relationships between variables
-# Create the bar plot
-ggplot(pumpkins_select, aes(y = variety, fill = color)) +
- geom_bar(position = "dodge") +
- scale_fill_manual(values = palette) +
- labs(y = "Variety", fill = "Color") +
- theme_minimal()
-```
-
-Amazing🤩! For some of the features, there's a noticeable difference in the distribution for each color label. For instance, it seems the white pumpkins can be found in smaller packages and in some particular varieties of pumpkins. The *item_size* category also seems to make a difference in the color distribution. These features may help predict the color of a pumpkin.
-
-### **Analysing relationships between features and label**
-
-```{r}
+Now that we have pre-processed our data, we can analyse the relationships between the features and the label to grasp an idea of how well the model will be able to predict the label given the features. The best way to perform this kind of analysis is plotting the data.
+We'll be using again the ggplot geom_boxplot_ function, to visualize the relationships between Item Size, Variety and Color in a categorical plot. To better plot the data we'll be using the encoded Item Size column and the unencoded Variety column.
+```{r boxplot}
# Define the color palette
palette <- c(ORANGE = "orange", WHITE = "wheat")
@@ -206,14 +222,11 @@ ggplot(pumpkins_select_plot, aes(x = `item_size`, y = color, fill = color)) +
guides(fill = guide_legend(title = "Color")) +
theme(panel.spacing = unit(0.5, "lines"))+
theme(strip.text.y = element_text(size = 4, hjust = 0))
-
```
-Let's now focus on a specific relationship: Item Size and Color!
-
-#### **Use a swarm plot**
+#### Use a swarm plot
-Color is a binary category (Orange or Not), it's called `categorical data`. There are other various ways of [visualizing categorical data](https://seaborn.pydata.org/tutorial/categorical.html?highlight=bar).
+Since Color is a binary category (White or Not), it needs 'a [specialized approach](https://github.com/rstudio/cheatsheets/blob/main/data-visualization.pdf) to visualization'.
Try a `swarm plot` to show the distribution of color with respect to the item_size.
@@ -228,13 +241,12 @@ baked_pumpkins %>%
scale_color_brewer(palette = "Dark2", direction = -1) +
theme(legend.position = "none")
```
-Now that we have an idea of the relationship between the binary categories of color and the larger group of sizes, let's explore logistic regression to determine a given pumpkin's likely color.
-## 3. Build your model
+Now that we have an idea of the relationship between the binary categories of color and the larger group of sizes, let's explore logistic regression to determine a given pumpkin's likely color.
-Let's begin by splitting the data into `training` and `test` sets. The training set is used to train a classifier so that it finds a statistical relationship between the features and the label value.
+## Build your model
-It is best practice to hold out some of your data for **testing** in order to get a better estimate of how your models will perform on new data by comparing the predicted labels with the already known labels in the test set. [rsample](https://rsample.tidymodels.org/), a package in Tidymodels, provides infrastructure for efficient data splitting and resampling:
+Select the variables you want to use in your classification model and split the data into training and test sets. [rsample](https://rsample.tidymodels.org/), a package in Tidymodels, provides infrastructure for efficient data splitting and resampling:
```{r split_data}
# Split data into 80% for training and 20% for testing
@@ -268,7 +280,6 @@ pumpkins_recipe <- recipe(color ~ ., data = pumpkins_train) %>%
log_reg <- logistic_reg() %>%
set_engine("glm") %>%
set_mode("classification")
-
```
Now that we have a recipe and a model specification, we need to find a way of bundling them together into an object that will first preprocess the data (prep+bake behind the scenes), fit the model on the preprocessed data and also allow for potential post-processing activities.
@@ -283,7 +294,6 @@ log_reg_wf <- workflow() %>%
# Print out the workflow
log_reg_wf
-
```
After a workflow has been *specified*, a model can be `trained` using the [`fit()`](https://tidymodels.github.io/parsnip/reference/fit.html) function. The workflow will estimate a recipe and preprocess the data before training, so we won't have to manually do that using prep and bake.
@@ -296,8 +306,6 @@ wf_fit <- log_reg_wf %>%
# Print the trained workflow
wf_fit
-
-
```
The model print out shows the coefficients learned during training.
@@ -315,11 +323,12 @@ results <- pumpkins_test %>% select(color) %>%
# Compare predictions
results %>%
slice_head(n = 10)
-
```
Very nice! This provides some more insights into how logistic regression works.
+### Better comprehension via a confusion matrix
+
Comparing each prediction with its corresponding "ground truth" actual value isn't a very efficient way to determine how well the model is predicting. Fortunately, Tidymodels has a few more tricks up its sleeve: [`yardstick`](https://yardstick.tidymodels.org/) - a package used to measure the effectiveness of models using performance metrics.
One performance metric associated with classification problems is the [`confusion matrix`](https://wikipedia.org/wiki/Confusion_matrix). A confusion matrix describes how well a classification model performs. A confusion matrix tabulates how many examples in each class were correctly classified by a model. In our case, it will show you how many orange pumpkins were classified as orange and how many white pumpkins were classified as white; the confusion matrix also shows you how many were classified into the **wrong** categories.
@@ -373,18 +382,15 @@ eval_metrics <- metric_set(ppv, recall, spec, f_meas, accuracy)
eval_metrics(data = results, truth = color, estimate = .pred_class)
```
-#### **Visualize the ROC curve of this model**
-
-For a start, this is not a bad model; its precision, recall, F measure and accuracy are in the 90% range so ideally you could use it to predict the color of a pumpkin given a set of variables. It also seems that our model was not really able to identify the white pumpkins 🧐. Could you guess why? One reason could be because of the high prevalence of ORANGE pumpkins in our training set making our model more inclined to predict the majority class.
+## Visualize the ROC curve of this model
-Let's do one more visualization to see the so-called [`ROC score`](https://en.wikipedia.org/wiki/Receiver_operating_characteristic):
+Let's do one more visualization to see the so-called [`ROC curve`](https://en.wikipedia.org/wiki/Receiver_operating_characteristic):
```{r roc_curve}
# Make a roc_curve
results %>%
roc_curve(color, .pred_ORANGE) %>%
autoplot()
-
```
ROC curves are often used to get a view of the output of a classifier in terms of its true vs. false positives. ROC curves typically feature `True Positive Rate`/Sensitivity on the Y axis, and `False Positive Rate`/1-Specificity on the X axis. Thus, the steepness of the curve and the space between the midpoint line and the curve matter: you want a curve that quickly heads up and over the line. In our case, there are false positives to start with, and then the line heads up and over properly.
@@ -395,15 +401,17 @@ Finally, let's use `yardstick::roc_auc()` to calculate the actual Area Under the
# Calculate area under curve
results %>%
roc_auc(color, .pred_ORANGE)
-
```
-The result is around `0.947`. Given that the AUC ranges from 0 to 1, you want a big score, since a model that is 100% correct in its predictions will have an AUC of 1; in this case, the model is *pretty good*.
+The result is around `0.975`. Given that the AUC ranges from 0 to 1, you want a big score, since a model that is 100% correct in its predictions will have an AUC of 1; in this case, the model is *pretty good*.
In future lessons on classifications, you will learn how to improve your model's scores (such as dealing with imbalanced data in this case).
-But for now, congratulations 🎉🎉🎉! You've completed these regression lessons!
+## 🚀Challenge
+
+There's a lot more to unpack regarding logistic regression! But the best way to learn is to experiment. Find a dataset that lends itself to this type of analysis and build a model with it. What do you learn? tip: try [Kaggle](https://www.kaggle.com/search?q=logistic+regression+datasets) for interesting datasets.
+
+## Review & Self Study
-You R awesome!
+Read the first few pages of [this paper from Stanford](https://web.stanford.edu/~jurafsky/slp3/5.pdf) on some practical uses for logistic regression. Think about tasks that are better suited for one or the other type of regression tasks that we have studied up to this point. What would work best?
-![Artwork by \@allison_horst](../../images/r_learners_sm.jpeg)
diff --git a/2-Regression/4-Logistic/solution/R/lesson_4.html b/2-Regression/4-Logistic/solution/R/lesson_4.html
index f62b50c3ae..c44a56d2b0 100644
--- a/2-Regression/4-Logistic/solution/R/lesson_4.html
+++ b/2-Regression/4-Logistic/solution/R/lesson_4.html
@@ -2955,7 +2955,7 @@
Build a regression model: logistic
Build a logistic regression model - Lesson 4
-
+
Logistic vs. linear regression infographic
@@ -2966,19 +2966,20 @@
In this final lesson on Regression, one of the basic classic
ML techniques, we will take a look at Logistic Regression. You would use
-this technique to discover patterns to predict binary
-categories
. Is this candy chocolate or not? Is this disease
-contagious or not? Will this customer choose this product or not?
+this technique to discover patterns to predict binary categories. Is
+this candy chocolate or not? Is this disease contagious or not? Will
+this customer choose this product or not?
In this lesson, you will learn:
- Techniques for logistic regression
✅ Deepen your understanding of working with this type of regression
-in this Learn
+in this Learn
module
-
-
Prerequisite
+
+
+
Prerequisite
Having worked with the pumpkin data, we are now familiar enough with
it to realize that there’s one binary category that we can work with:
Color
.
@@ -3012,7 +3013,6 @@
Prerequisite
pacman::p_load(tidyverse, tidymodels, janitor, ggbeeswarm)
-
Define the question
For our purposes, we will express this as a binary: ‘White’ or ‘Not
@@ -3039,10 +3039,26 @@
Binary classification
given the origin of a pumpkin and the time of harvest,
how much its
price will rise.
-
+
Infographic by Dasani Madipalli
+
+
Other classifications
+
There are other types of logistic regression, including multinomial
+and ordinal:
+
+Multinomial, which involves having more than one
+category - “Orange, White, and Striped”.
+Ordinal, which involves ordered categories,
+useful if we wanted to order our outcomes logically, like our pumpkins
+that are ordered by a finite number of sizes
+(mini,sm,med,lg,xl,xxl).
+
+
+
+
Multinomial vs ordinal regression
+
Variables DO NOT have to correlate
Remember how linear regression worked better with more correlated
@@ -3059,11 +3075,14 @@
You need a lot of clean data
logistic regression
-
-
1. Tidy the data
-
Now, the fun begins! Let’s start by importing the data, cleaning the
-data a bit, dropping rows containing missing values and selecting only
+
+
+
Exercise - tidy the data
+
First, clean the data a bit, dropping null values and selecting only
some of the columns:
+
+- Add the following code:
+
# Load the core tidyverse packages
library(tidyverse)
@@ -3088,9 +3107,7 @@ 1. Tidy the data
{"columns":[{"label":["city_name"],"name":[1],"type":["chr"],"align":["left"]},{"label":["package"],"name":[2],"type":["chr"],"align":["left"]},{"label":["variety"],"name":[3],"type":["chr"],"align":["left"]},{"label":["origin"],"name":[4],"type":["chr"],"align":["left"]},{"label":["item_size"],"name":[5],"type":["chr"],"align":["left"]},{"label":["color"],"name":[6],"type":["fct"],"align":["left"]}],"data":[{"1":"BALTIMORE","2":"24 inch bins","3":"HOWDEN TYPE","4":"DELAWARE","5":"med","6":"ORANGE"},{"1":"BALTIMORE","2":"24 inch bins","3":"HOWDEN TYPE","4":"VIRGINIA","5":"med","6":"ORANGE"},{"1":"BALTIMORE","2":"24 inch bins","3":"HOWDEN TYPE","4":"MARYLAND","5":"lge","6":"ORANGE"},{"1":"BALTIMORE","2":"24 inch bins","3":"HOWDEN TYPE","4":"MARYLAND","5":"lge","6":"ORANGE"},{"1":"BALTIMORE","2":"36 inch bins","3":"HOWDEN TYPE","4":"MARYLAND","5":"med","6":"ORANGE"}],"options":{"columns":{"min":{},"max":[10]},"rows":{"min":[10],"max":[10]},"pages":{}}}
-
Sometimes, we may want some little more information on our data. We
-can have a look at the data
, its structure
and
-the data type
of its features by using the glimpse()
+
You can always take a peek at your new dataframe, by using the glimpse()
function as below:
pumpkins_select %>%
glimpse()
@@ -3102,8 +3119,6 @@
1. Tidy the data
## $ origin <chr> "DELAWARE", "VIRGINIA", "MARYLAND", "MARYLAND", "MARYLAND", …
## $ item_size <chr> "med", "med", "lge", "lge", "med", "lge", "med", "lge", "med…
## $ color <fct> ORANGE, ORANGE, ORANGE, ORANGE, ORANGE, ORANGE, ORANGE, ORAN…
-
Wow! Seems that all our columns are all of type character,
-further alluding that they are all categorical.
Let’s confirm that we will actually be doing a binary classification
problem:
# Subset distinct observations in outcome column
@@ -3114,22 +3129,43 @@ 1. Tidy the data
{"columns":[{"label":["color"],"name":[1],"type":["fct"],"align":["left"]}],"data":[{"1":"ORANGE"},{"1":"WHITE"}],"options":{"columns":{"min":{},"max":[10]},"rows":{"min":[10],"max":[10]},"pages":{}}}
-
🥳🥳 That went down well!
+
+
Visualization - categorical plot
+
By now you have loaded up the pumpkin data once again and cleaned it
+so as to preserve a dataset containing a few variables, including Color.
+Let’s visualize the dataframe in the notebook using ggplot library.
+
The ggplot library offers some neat ways to visualize your data. For
+example, you can compare distributions of the data for each Variety and
+Color in a categorical plot.
+
+- Create such a plot by using the geombar function, using our pumpkin
+data, and specifying a color mapping for each pumpkin category (orange
+or white):
+
+
# Specify colors for each value of the hue variable
+palette <- c(ORANGE = "orange", WHITE = "wheat")
+
+# Create the bar plot
+ggplot(pumpkins_select, aes(y = variety, fill = color)) +
+ geom_bar(position = "dodge") +
+ scale_fill_manual(values = palette) +
+ labs(y = "Variety", fill = "Color") +
+ theme_minimal()
+
+
By observing the data, you can see how the Color data relates to
+Variety.
+
✅ Given this categorical plot, what are some interesting
+explorations you can envision?
-
-
2. Explore the data
-
The goal of data exploration is to try to understand the
-relationships
between its attributes; in particular, any
-apparent correlation between the features and the
-label your model will try to predict. One way of doing this is
-by using data visualization.
-
Given our the data types of our columns, we can encode
-them and be on our way to making some visualizations. This simply
-involves translating
a column with
-categorical values
for example our columns of type
-char, into one or more numeric columns
that take
-the place of the original. - Something we did in our last
-lesson.
+
+
Data pre-processing: feature encoding
+
Our pumpkins dataset contains string values for all its columns.
+Working with categorical data is intuitive for humans but not for
+machines. Machine learning algorithms work well with numbers. That’s why
+encoding is a very important step in the data pre-processing phase,
+since it enables us to turn categorical data into numerical data,
+without losing any information. Good encoding leads to building a good
+model.
For feature encoding there are two main types of encoders:
Ordinal encoder: it suits well for ordinal variables, which are
@@ -3162,46 +3198,38 @@
2. Explore the data
analysis and then extract the preprocessed data with the steps
applied.
-# Preprocess and extract data to allow some data analysis
-baked_pumpkins <- recipe(color ~ ., data = pumpkins_select) %>%
- # Define ordering for item_size column
- step_mutate(item_size = ordered(item_size, levels = c('sml', 'med', 'med-lge', 'lge', 'xlge', 'jbo', 'exjbo'))) %>%
- # Convert factors to numbers using the order defined above (Ordinal encoding)
- step_integer(item_size, zero_based = F) %>%
- # Encode all other predictors using one hot encoding
- step_dummy(all_nominal(), -all_outcomes(), one_hot = TRUE) %>%
- prep(data = pumpkin_select) %>%
- bake(new_data = NULL)
-
-# Display the first few rows of preprocessed data
-baked_pumpkins %>%
- slice_head(n = 5)
+# Preprocess and extract data to allow some data analysis
+baked_pumpkins <- recipe(color ~ ., data = pumpkins_select) %>%
+ # Define ordering for item_size column
+ step_mutate(item_size = ordered(item_size, levels = c('sml', 'med', 'med-lge', 'lge', 'xlge', 'jbo', 'exjbo'))) %>%
+ # Convert factors to numbers using the order defined above (Ordinal encoding)
+ step_integer(item_size, zero_based = F) %>%
+ # Encode all other predictors using one hot encoding
+ step_dummy(all_nominal(), -all_outcomes(), one_hot = TRUE) %>%
+ prep(data = pumpkin_select) %>%
+ bake(new_data = NULL)
+
+# Display the first few rows of preprocessed data
+baked_pumpkins %>%
+ slice_head(n = 5)
-Now, let’s make a categorical plot showing the distribution of the
-predictors with respect to the outcome color!
-# Specify colors for each value of the hue variable
-palette <- c(ORANGE = "orange", WHITE = "wheat")
-
-# Create the bar plot
-ggplot(pumpkins_select, aes(y = variety, fill = color)) +
- geom_bar(position = "dodge") +
- scale_fill_manual(values = palette) +
- labs(y = "Variety", fill = "Color") +
- theme_minimal()
-
-Amazing🤩! For some of the features, there’s a noticeable difference
-in the distribution for each color label. For instance, it seems the
-white pumpkins can be found in smaller packages and in some particular
-varieties of pumpkins. The item_size category also seems to
-make a difference in the color distribution. These features may help
-predict the color of a pumpkin.
-
-
Analysing relationships between features and
-label
+
✅ What are the advantages of using an ordinal encoder for the Item
+Size column?
+
+
+
Analyse relationships between variables
+
Now that we have pre-processed our data, we can analyse the
+relationships between the features and the label to grasp an idea of how
+well the model will be able to predict the label given the features. The
+best way to perform this kind of analysis is plotting the data. We’ll be
+using again the ggplot geom_boxplot_ function, to visualize the
+relationships between Item Size, Variety and Color in a categorical
+plot. To better plot the data we’ll be using the encoded Item Size
+column and the unencoded Variety column.
# Define the color palette
palette <- c(ORANGE = "orange", WHITE = "wheat")
@@ -3225,12 +3253,10 @@ Analysing relationships between features and
theme(panel.spacing = unit(0.5, "lines"))+
theme(strip.text.y = element_text(size = 4, hjust = 0))
-
Let’s now focus on a specific relationship: Item Size and Color!
-
Use a swarm plot
-
Color is a binary category (Orange or Not), it’s called
-categorical data
. There are other various ways of visualizing
-categorical data.
+
Use a swarm plot
+
Since Color is a binary category (White or Not), it needs ‘a specialized
+approach to visualization’.
Try a swarm plot
to show the distribution of color with
respect to the item_size.
We’ll use the ggbeeswarm package
@@ -3244,23 +3270,17 @@
Use a swarm plot
geom_quasirandom() +
scale_color_brewer(palette = "Dark2", direction = -1) +
theme(legend.position = "none")
-
-Now that we have an idea of the relationship between the binary
+
+
Now that we have an idea of the relationship between the binary
categories of color and the larger group of sizes, let’s explore
logistic regression to determine a given pumpkin’s likely color.
-
3. Build your model
-
Let’s begin by splitting the data into training
and
-test
sets. The training set is used to train a classifier
-so that it finds a statistical relationship between the features and the
-label value.
-
It is best practice to hold out some of your data for
-testing in order to get a better estimate of how your
-models will perform on new data by comparing the predicted labels with
-the already known labels in the test set. rsample, a package in
+
Build your model
+
Select the variables you want to use in your classification model and
+split the data into training and test sets. rsample, a package in
Tidymodels, provides infrastructure for efficient data splitting and
resampling:
# Split data into 80% for training and 20% for testing
@@ -3437,6 +3457,8 @@ 3. Build your model
Very nice! This provides some more insights into how logistic
regression works.
+
+
Better comprehension via a confusion matrix
Comparing each prediction with its corresponding “ground truth”
actual value isn’t a very efficient way to determine how well the model
is predicting. Fortunately, Tidymodels has a few more tricks up its
@@ -3532,16 +3554,11 @@
3. Build your model
{"columns":[{"label":[".metric"],"name":[1],"type":["chr"],"align":["left"]},{"label":[".estimator"],"name":[2],"type":["chr"],"align":["left"]},{"label":[".estimate"],"name":[3],"type":["dbl"],"align":["right"]}],"data":[{"1":"ppv","2":"binary","3":"1.0000000"},{"1":"recall","2":"binary","3":"0.8947368"},{"1":"spec","2":"binary","3":"1.0000000"},{"1":"f_meas","2":"binary","3":"0.9444444"},{"1":"accuracy","2":"binary","3":"0.9095477"}],"options":{"columns":{"min":{},"max":[10]},"rows":{"min":[10],"max":[10]},"pages":{}}}
-
-
Visualize the ROC curve of this model
-
For a start, this is not a bad model; its precision, recall, F
-measure and accuracy are in the 90% range so ideally you could use it to
-predict the color of a pumpkin given a set of variables. It also seems
-that our model was not really able to identify the white pumpkins 🧐.
-Could you guess why? One reason could be because of the high prevalence
-of ORANGE pumpkins in our training set making our model more inclined to
-predict the majority class.
-
Let’s do one more visualization to see the so-called ROC score
:
+
+
+
+
Visualize the ROC curve of this model
+
Let’s do one more visualization to see the so-called ROC curve
:
# Make a roc_curve
results %>%
roc_curve(color, .pred_ORANGE) %>%
@@ -3567,24 +3584,31 @@ Visualize the ROC curve of this model
{"columns":[{"label":[".metric"],"name":[1],"type":["chr"],"align":["left"]},{"label":[".estimator"],"name":[2],"type":["chr"],"align":["left"]},{"label":[".estimate"],"name":[3],"type":["dbl"],"align":["right"]}],"data":[{"1":"roc_auc","2":"binary","3":"0.9473684"}],"options":{"columns":{"min":{},"max":[10]},"rows":{"min":[10],"max":[10]},"pages":{}}}
-
The result is around 0.947
. Given that the AUC ranges
+
The result is around 0.975
. Given that the AUC ranges
from 0 to 1, you want a big score, since a model that is 100% correct in
its predictions will have an AUC of 1; in this case, the model is
pretty good.
In future lessons on classifications, you will learn how to improve
your model’s scores (such as dealing with imbalanced data in this
case).
-
But for now, congratulations 🎉🎉🎉! You’ve completed these
-regression lessons!
-
You R awesome!
-
-
-
Artwork by @allison_horst
+
+
🚀Challenge
+
There’s a lot more to unpack regarding logistic regression! But the
+best way to learn is to experiment. Find a dataset that lends itself to
+this type of analysis and build a model with it. What do you learn? tip:
+try Kaggle
+for interesting datasets.
+
+
Review & Self Study
+
Read the first few pages of this paper from
+Stanford on some practical uses for logistic regression. Think about
+tasks that are better suited for one or the other type of regression
+tasks that we have studied up to this point. What would work best?
-
LS0tCnRpdGxlOiAnQnVpbGQgYSByZWdyZXNzaW9uIG1vZGVsOiBsb2dpc3RpYyByZWdyZXNzaW9uJwpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdGhlbWU6IGZsYXRseQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHllcwotLS0KCiMjIEJ1aWxkIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCAtIExlc3NvbiA0CgohW0xvZ2lzdGljIHZzLiBsaW5lYXIgcmVncmVzc2lvbiBpbmZvZ3JhcGhpY10oaHR0cHM6Ly9naXRodWIuY29tL21pY3Jvc29mdC9NTC1Gb3ItQmVnaW5uZXJzL2Jsb2IvbWFpbi8yLVJlZ3Jlc3Npb24vNC1Mb2dpc3RpYy9pbWFnZXMvbGluZWFyLXZzLWxvZ2lzdGljLnBuZykKCiMjIyMgKipbUHJlLWxlY3R1cmUgcXVpel0oaHR0cHM6Ly9ncmF5LXNhbmQtMDdhMTBmNDAzLjEuYXp1cmVzdGF0aWNhcHBzLm5ldC9xdWl6LzE1LykqKgoKIyMjIyAgSW50cm9kdWN0aW9uCgpJbiB0aGlzIGZpbmFsIGxlc3NvbiBvbiBSZWdyZXNzaW9uLCBvbmUgb2YgdGhlIGJhc2ljICpjbGFzc2ljKiBNTCB0ZWNobmlxdWVzLCB3ZSB3aWxsIHRha2UgYSBsb29rIGF0IExvZ2lzdGljIFJlZ3Jlc3Npb24uIFlvdSB3b3VsZCB1c2UgdGhpcyB0ZWNobmlxdWUgdG8gZGlzY292ZXIgcGF0dGVybnMgdG8gcHJlZGljdCBgYmluYXJ5YCBgY2F0ZWdvcmllc2AuIElzIHRoaXMgY2FuZHkgY2hvY29sYXRlIG9yIG5vdD8gSXMgdGhpcyBkaXNlYXNlIGNvbnRhZ2lvdXMgb3Igbm90PyBXaWxsIHRoaXMgY3VzdG9tZXIgY2hvb3NlIHRoaXMgcHJvZHVjdCBvciBub3Q/CgpJbiB0aGlzIGxlc3NvbiwgeW91IHdpbGwgbGVhcm46CgotICAgVGVjaG5pcXVlcyBmb3IgbG9naXN0aWMgcmVncmVzc2lvbgoK4pyFIERlZXBlbiB5b3VyIHVuZGVyc3RhbmRpbmcgb2Ygd29ya2luZyB3aXRoIHRoaXMgdHlwZSBvZiByZWdyZXNzaW9uIGluIHRoaXMgW0xlYXJuIG1vZHVsZV0oaHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vbGVhcm4vbW9kdWxlcy90cmFpbi1ldmFsdWF0ZS1jbGFzc2lmaWNhdGlvbi1tb2RlbHM/V1QubWNfaWQ9YWNhZGVtaWMtNzc5NTItbGVlc3RvdHQpCgojIyMjICoqUHJlcmVxdWlzaXRlKioKCkhhdmluZyB3b3JrZWQgd2l0aCB0aGUgcHVtcGtpbiBkYXRhLCB3ZSBhcmUgbm93IGZhbWlsaWFyIGVub3VnaCB3aXRoIGl0IHRvIHJlYWxpemUgdGhhdCB0aGVyZSdzIG9uZSBiaW5hcnkgY2F0ZWdvcnkgdGhhdCB3ZSBjYW4gd29yayB3aXRoOiBgQ29sb3JgLgoKTGV0J3MgYnVpbGQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgdGhhdCwgZ2l2ZW4gc29tZSB2YXJpYWJsZXMsICp3aGF0IGNvbG9yIGEgZ2l2ZW4gcHVtcGtpbiBpcyBsaWtlbHkgdG8gYmUqIChvcmFuZ2Ug8J+OgyBvciB3aGl0ZSDwn5G7KS4KCj4gV2h5IGFyZSB3ZSB0YWxraW5nIGFib3V0IGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBpbiBhIGxlc3NvbiBncm91cGluZyBhYm91dCByZWdyZXNzaW9uPyBPbmx5IGZvciBsaW5ndWlzdGljIGNvbnZlbmllbmNlLCBhcyBsb2dpc3RpYyByZWdyZXNzaW9uIGlzIFtyZWFsbHkgYSBjbGFzc2lmaWNhdGlvbiBtZXRob2RdKGh0dHBzOi8vc2Npa2l0LWxlYXJuLm9yZy9zdGFibGUvbW9kdWxlcy9saW5lYXJfbW9kZWwuaHRtbCNsb2dpc3RpYy1yZWdyZXNzaW9uKSwgYWxiZWl0IGEgbGluZWFyLWJhc2VkIG9uZS4gTGVhcm4gYWJvdXQgb3RoZXIgd2F5cyB0byBjbGFzc2lmeSBkYXRhIGluIHRoZSBuZXh0IGxlc3NvbiBncm91cC4KCkZvciB0aGlzIGxlc3Nvbiwgd2UnbGwgcmVxdWlyZSB0aGUgZm9sbG93aW5nIHBhY2thZ2VzOgoKLSAgIGB0aWR5dmVyc2VgOiBUaGUgW3RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pIGlzIGEgW2NvbGxlY3Rpb24gb2YgUiBwYWNrYWdlc10oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy9wYWNrYWdlcykgZGVzaWduZWQgdG8gbWFrZXMgZGF0YSBzY2llbmNlIGZhc3RlciwgZWFzaWVyIGFuZCBtb3JlIGZ1biEKCi0gICBgdGlkeW1vZGVsc2A6IFRoZSBbdGlkeW1vZGVsc10oaHR0cHM6Ly93d3cudGlkeW1vZGVscy5vcmcvKSBmcmFtZXdvcmsgaXMgYSBbY29sbGVjdGlvbiBvZiBwYWNrYWdlc10oaHR0cHM6Ly93d3cudGlkeW1vZGVscy5vcmcvcGFja2FnZXMvKSBmb3IgbW9kZWxpbmcgYW5kIG1hY2hpbmUgbGVhcm5pbmcuCgotICAgYGphbml0b3JgOiBUaGUgW2phbml0b3IgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL3NmaXJrZS9qYW5pdG9yKSBwcm92aWRlcyBzaW1wbGUgbGl0dGxlIHRvb2xzIGZvciBleGFtaW5pbmcgYW5kIGNsZWFuaW5nIGRpcnR5IGRhdGEuCgotICAgYGdnYmVlc3dhcm1gOiBUaGUgW2dnYmVlc3dhcm0gcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2VjbGFya2UvZ2diZWVzd2FybSkgcHJvdmlkZXMgbWV0aG9kcyB0byBjcmVhdGUgYmVlc3dhcm0tc3R5bGUgcGxvdHMgdXNpbmcgZ2dwbG90Mi4KCllvdSBjYW4gaGF2ZSB0aGVtIGluc3RhbGxlZCBhczoKCmBpbnN0YWxsLnBhY2thZ2VzKGMoInRpZHl2ZXJzZSIsICJ0aWR5bW9kZWxzIiwgImphbml0b3IiLCAiZ2diZWVzd2FybSIpKWAKCkFsdGVybmF0ZWx5LCB0aGUgc2NyaXB0IGJlbG93IGNoZWNrcyB3aGV0aGVyIHlvdSBoYXZlIHRoZSBwYWNrYWdlcyByZXF1aXJlZCB0byBjb21wbGV0ZSB0aGlzIG1vZHVsZSBhbmQgaW5zdGFsbHMgdGhlbSBmb3IgeW91IGluIGNhc2UgdGhleSBhcmUgbWlzc2luZy4KCmBgYHtyLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0Kc3VwcHJlc3NXYXJuaW5ncyhpZiAoIXJlcXVpcmUoInBhY21hbiIpKWluc3RhbGwucGFja2FnZXMoInBhY21hbiIpKQoKcGFjbWFuOjpwX2xvYWQodGlkeXZlcnNlLCB0aWR5bW9kZWxzLCBqYW5pdG9yLCBnZ2JlZXN3YXJtKQpgYGAKCiMjICoqRGVmaW5lIHRoZSBxdWVzdGlvbioqCgpGb3Igb3VyIHB1cnBvc2VzLCB3ZSB3aWxsIGV4cHJlc3MgdGhpcyBhcyBhIGJpbmFyeTogJ1doaXRlJyBvciAnTm90IFdoaXRlJy4gVGhlcmUgaXMgYWxzbyBhICdzdHJpcGVkJyBjYXRlZ29yeSBpbiBvdXIgZGF0YXNldCBidXQgdGhlcmUgYXJlIGZldyBpbnN0YW5jZXMgb2YgaXQsIHNvIHdlIHdpbGwgbm90IHVzZSBpdC4gSXQgZGlzYXBwZWFycyBvbmNlIHdlIHJlbW92ZSBudWxsIHZhbHVlcyBmcm9tIHRoZSBkYXRhc2V0LCBhbnl3YXkuCgo+IPCfjoMgRnVuIGZhY3QsIHdlIHNvbWV0aW1lcyBjYWxsIHdoaXRlIHB1bXBraW5zICdnaG9zdCcgcHVtcGtpbnMuIFRoZXkgYXJlbid0IHZlcnkgZWFzeSB0byBjYXJ2ZSwgc28gdGhleSBhcmVuJ3QgYXMgcG9wdWxhciBhcyB0aGUgb3JhbmdlIG9uZXMgYnV0IHRoZXkgYXJlIGNvb2wgbG9va2luZyEgU28gd2UgY291bGQgYWxzbyByZWZvcm11bGF0ZSBvdXIgcXVlc3Rpb24gYXM6ICdHaG9zdCcgb3IgJ05vdCBHaG9zdCcuIPCfkbsKCiMjICoqQWJvdXQgbG9naXN0aWMgcmVncmVzc2lvbioqCgpMb2dpc3RpYyByZWdyZXNzaW9uIGRpZmZlcnMgZnJvbSBsaW5lYXIgcmVncmVzc2lvbiwgd2hpY2ggeW91IGxlYXJuZWQgYWJvdXQgcHJldmlvdXNseSwgaW4gYSBmZXcgaW1wb3J0YW50IHdheXMuCgojIyMjICoqQmluYXJ5IGNsYXNzaWZpY2F0aW9uKioKCkxvZ2lzdGljIHJlZ3Jlc3Npb24gZG9lcyBub3Qgb2ZmZXIgdGhlIHNhbWUgZmVhdHVyZXMgYXMgbGluZWFyIHJlZ3Jlc3Npb24uIFRoZSBmb3JtZXIgb2ZmZXJzIGEgcHJlZGljdGlvbiBhYm91dCBhIGBiaW5hcnkgY2F0ZWdvcnlgICgib3JhbmdlIG9yIG5vdCBvcmFuZ2UiKSB3aGVyZWFzIHRoZSBsYXR0ZXIgaXMgY2FwYWJsZSBvZiBwcmVkaWN0aW5nIGBjb250aW51YWwgdmFsdWVzYCwgZm9yIGV4YW1wbGUgZ2l2ZW4gdGhlIG9yaWdpbiBvZiBhIHB1bXBraW4gYW5kIHRoZSB0aW1lIG9mIGhhcnZlc3QsICpob3cgbXVjaCBpdHMgcHJpY2Ugd2lsbCByaXNlKi4KCiFbSW5mb2dyYXBoaWMgYnkgRGFzYW5pIE1hZGlwYWxsaV0oLi4vLi4vaW1hZ2VzL3B1bXBraW4tY2xhc3NpZmllci5wbmcpe3dpZHRoPSI2MDAifQoKIyMjIyAqKlZhcmlhYmxlcyBETyBOT1QgaGF2ZSB0byBjb3JyZWxhdGUqKgoKUmVtZW1iZXIgaG93IGxpbmVhciByZWdyZXNzaW9uIHdvcmtlZCBiZXR0ZXIgd2l0aCBtb3JlIGNvcnJlbGF0ZWQgdmFyaWFibGVzPyBMb2dpc3RpYyByZWdyZXNzaW9uIGlzIHRoZSBvcHBvc2l0ZSAtIHRoZSB2YXJpYWJsZXMgZG9uJ3QgaGF2ZSB0byBhbGlnbi4gVGhhdCB3b3JrcyBmb3IgdGhpcyBkYXRhIHdoaWNoIGhhcyBzb21ld2hhdCB3ZWFrIGNvcnJlbGF0aW9ucy4KCiMjIyMgKipZb3UgbmVlZCBhIGxvdCBvZiBjbGVhbiBkYXRhKioKCkxvZ2lzdGljIHJlZ3Jlc3Npb24gd2lsbCBnaXZlIG1vcmUgYWNjdXJhdGUgcmVzdWx0cyBpZiB5b3UgdXNlIG1vcmUgZGF0YTsgb3VyIHNtYWxsIGRhdGFzZXQgaXMgbm90IG9wdGltYWwgZm9yIHRoaXMgdGFzaywgc28ga2VlcCB0aGF0IGluIG1pbmQuCgrinIUgVGhpbmsgYWJvdXQgdGhlIHR5cGVzIG9mIGRhdGEgdGhhdCB3b3VsZCBsZW5kIHRoZW1zZWx2ZXMgd2VsbCB0byBsb2dpc3RpYyByZWdyZXNzaW9uCgojIyAxLiBUaWR5IHRoZSBkYXRhCgpOb3csIHRoZSBmdW4gYmVnaW5zISBMZXQncyBzdGFydCBieSBpbXBvcnRpbmcgdGhlIGRhdGEsIGNsZWFuaW5nIHRoZSBkYXRhIGEgYml0LCBkcm9wcGluZyByb3dzIGNvbnRhaW5pbmcgbWlzc2luZyB2YWx1ZXMgYW5kIHNlbGVjdGluZyBvbmx5IHNvbWUgb2YgdGhlIGNvbHVtbnM6CgpgYGB7ciwgdGlkeXIsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQojIExvYWQgdGhlIGNvcmUgdGlkeXZlcnNlIHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKQoKIyBJbXBvcnQgdGhlIGRhdGEgYW5kIGNsZWFuIGNvbHVtbiBuYW1lcwpwdW1wa2lucyA8LSByZWFkX2NzdihmaWxlID0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9taWNyb3NvZnQvTUwtRm9yLUJlZ2lubmVycy9tYWluLzItUmVncmVzc2lvbi9kYXRhL1VTLXB1bXBraW5zLmNzdiIpICU+JSAKICBjbGVhbl9uYW1lcygpCgojIFNlbGVjdCBkZXNpcmVkIGNvbHVtbnMKcHVtcGtpbnNfc2VsZWN0IDwtIHB1bXBraW5zICU+JSAKICBzZWxlY3QoYyhjaXR5X25hbWUsIHBhY2thZ2UsIHZhcmlldHksIG9yaWdpbiwgaXRlbV9zaXplLCBjb2xvcikpIAoKIyBEcm9wIHJvd3MgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyBhbmQgZW5jb2RlIGNvbG9yIGFzIGZhY3RvciAoY2F0ZWdvcnkpCnB1bXBraW5zX3NlbGVjdCA8LSBwdW1wa2luc19zZWxlY3QgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKGNvbG9yID0gZmFjdG9yKGNvbG9yKSkKCiMgVmlldyB0aGUgZmlyc3QgZmV3IHJvd3MKcHVtcGtpbnNfc2VsZWN0ICU+JSAKICBzbGljZV9oZWFkKG4gPSA1KQoKYGBgCgpTb21ldGltZXMsIHdlIG1heSB3YW50IHNvbWUgbGl0dGxlIG1vcmUgaW5mb3JtYXRpb24gb24gb3VyIGRhdGEuIFdlIGNhbiBoYXZlIGEgbG9vayBhdCB0aGUgYGRhdGFgLCBgaXRzIHN0cnVjdHVyZWAgYW5kIHRoZSBgZGF0YSB0eXBlYCBvZiBpdHMgZmVhdHVyZXMgYnkgdXNpbmcgdGhlIFsqZ2xpbXBzZSgpKl0oaHR0cHM6Ly9waWxsYXIuci1saWIub3JnL3JlZmVyZW5jZS9nbGltcHNlLmh0bWwpIGZ1bmN0aW9uIGFzIGJlbG93OgoKYGBge3IgZ2xpbXBzZX0KcHVtcGtpbnNfc2VsZWN0ICU+JSAKICBnbGltcHNlKCkKYGBgCgpXb3chIFNlZW1zIHRoYXQgYWxsIG91ciBjb2x1bW5zIGFyZSBhbGwgb2YgdHlwZSAqY2hhcmFjdGVyKiwgZnVydGhlciBhbGx1ZGluZyB0aGF0IHRoZXkgYXJlIGFsbCBjYXRlZ29yaWNhbC4KCkxldCdzIGNvbmZpcm0gdGhhdCB3ZSB3aWxsIGFjdHVhbGx5IGJlIGRvaW5nIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHByb2JsZW06CgpgYGB7ciBkaXN0aW5jdCBjb2xvcn0KIyBTdWJzZXQgZGlzdGluY3Qgb2JzZXJ2YXRpb25zIGluIG91dGNvbWUgY29sdW1uCnB1bXBraW5zX3NlbGVjdCAlPiUgCiAgZGlzdGluY3QoY29sb3IpCgpgYGAKCvCfpbPwn6WzIFRoYXQgd2VudCBkb3duIHdlbGwhCgojIyAyLiBFeHBsb3JlIHRoZSBkYXRhCgpUaGUgZ29hbCBvZiBkYXRhIGV4cGxvcmF0aW9uIGlzIHRvIHRyeSB0byB1bmRlcnN0YW5kIHRoZSBgcmVsYXRpb25zaGlwc2AgYmV0d2VlbiBpdHMgYXR0cmlidXRlczsgaW4gcGFydGljdWxhciwgYW55IGFwcGFyZW50IGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlICpmZWF0dXJlcyogYW5kIHRoZSAqbGFiZWwqIHlvdXIgbW9kZWwgd2lsbCB0cnkgdG8gcHJlZGljdC4gT25lIHdheSBvZiBkb2luZyB0aGlzIGlzIGJ5IHVzaW5nIGRhdGEgdmlzdWFsaXphdGlvbi4KCkdpdmVuIG91ciB0aGUgZGF0YSB0eXBlcyBvZiBvdXIgY29sdW1ucywgd2UgY2FuIGBlbmNvZGVgIHRoZW0gYW5kIGJlIG9uIG91ciB3YXkgdG8gbWFraW5nIHNvbWUgdmlzdWFsaXphdGlvbnMuIFRoaXMgc2ltcGx5IGludm9sdmVzIGB0cmFuc2xhdGluZ2AgYSBjb2x1bW4gd2l0aCBgY2F0ZWdvcmljYWwgdmFsdWVzYCBmb3IgZXhhbXBsZSBvdXIgY29sdW1ucyBvZiB0eXBlICpjaGFyKiwgaW50byBvbmUgb3IgbW9yZSBgbnVtZXJpYyBjb2x1bW5zYCB0aGF0IHRha2UgdGhlIHBsYWNlIG9mIHRoZSBvcmlnaW5hbC4gLSBTb21ldGhpbmcgd2UgZGlkIGluIG91ciBbbGFzdCBsZXNzb25dKGh0dHBzOi8vZ2l0aHViLmNvbS9taWNyb3NvZnQvTUwtRm9yLUJlZ2lubmVycy9ibG9iL21haW4vMi1SZWdyZXNzaW9uLzMtTGluZWFyL3NvbHV0aW9uL2xlc3Nvbl8zLmh0bWwpLgoKRm9yIGZlYXR1cmUgZW5jb2RpbmcgdGhlcmUgYXJlIHR3byBtYWluIHR5cGVzIG9mIGVuY29kZXJzOgoKMS4gT3JkaW5hbCBlbmNvZGVyOiBpdCBzdWl0cyB3ZWxsIGZvciBvcmRpbmFsIHZhcmlhYmxlcywgd2hpY2ggYXJlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB3aGVyZSB0aGVpciBkYXRhIGZvbGxvd3MgYSBsb2dpY2FsIG9yZGVyaW5nLCBsaWtlIHRoZSBgaXRlbV9zaXplYCBjb2x1bW4gaW4gb3VyIGRhdGFzZXQuIEl0IGNyZWF0ZXMgYSBtYXBwaW5nIHN1Y2ggdGhhdCBlYWNoIGNhdGVnb3J5IGlzIHJlcHJlc2VudGVkIGJ5IGEgbnVtYmVyLCB3aGljaCBpcyB0aGUgb3JkZXIgb2YgdGhlIGNhdGVnb3J5IGluIHRoZSBjb2x1bW4uCgoyLiBDYXRlZ29yaWNhbCBlbmNvZGVyOiBpdCBzdWl0cyB3ZWxsIGZvciBub21pbmFsIHZhcmlhYmxlcywgd2hpY2ggYXJlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB3aGVyZSB0aGVpciBkYXRhIGRvZXMgbm90IGZvbGxvdyBhIGxvZ2ljYWwgb3JkZXJpbmcsIGxpa2UgYWxsIHRoZSBmZWF0dXJlcyBkaWZmZXJlbnQgZnJvbSBgaXRlbV9zaXplYCBpbiBvdXIgZGF0YXNldC4gSXQgaXMgYSBvbmUtaG90IGVuY29kaW5nLCB3aGljaCBtZWFucyB0aGF0IGVhY2ggY2F0ZWdvcnkgaXMgcmVwcmVzZW50ZWQgYnkgYSBiaW5hcnkgY29sdW1uOiB0aGUgZW5jb2RlZCB2YXJpYWJsZSBpcyBlcXVhbCB0byAxIGlmIHRoZSBwdW1wa2luIGJlbG9uZ3MgdG8gdGhhdCBWYXJpZXR5IGFuZCAwIG90aGVyd2lzZS4KClRpZHltb2RlbHMgcHJvdmlkZXMgeWV0IGFub3RoZXIgbmVhdCBwYWNrYWdlOiBbcmVjaXBlc10oaHR0cHM6Ly9yZWNpcGVzLnRpZHltb2RlbHMub3JnLyktIGEgcGFja2FnZSBmb3IgcHJlcHJvY2Vzc2luZyBkYXRhLiBXZSdsbCBkZWZpbmUgYSBgcmVjaXBlYCB0aGF0IHNwZWNpZmllcyB0aGF0IGFsbCBwcmVkaWN0b3IgY29sdW1ucyBzaG91bGQgYmUgZW5jb2RlZCBpbnRvIGEgc2V0IG9mIGludGVnZXJzICwgYHByZXBgIGl0IHRvIGVzdGltYXRlcyB0aGUgcmVxdWlyZWQgcXVhbnRpdGllcyBhbmQgc3RhdGlzdGljcyBuZWVkZWQgYnkgYW55IG9wZXJhdGlvbnMgYW5kIGZpbmFsbHkgYGJha2VgIHRvIGFwcGx5IHRoZSBjb21wdXRhdGlvbnMgdG8gbmV3IGRhdGEuCgo+IE5vcm1hbGx5LCByZWNpcGVzIGlzIHVzdWFsbHkgdXNlZCBhcyBhIHByZXByb2Nlc3NvciBmb3IgbW9kZWxsaW5nIHdoZXJlIGl0IGRlZmluZXMgd2hhdCBzdGVwcyBzaG91bGQgYmUgYXBwbGllZCB0byBhIGRhdGEgc2V0IGluIG9yZGVyIHRvIGdldCBpdCByZWFkeSBmb3IgbW9kZWxsaW5nLiBJbiB0aGF0IGNhc2UgaXQgaXMgKipoaWdobHkgcmVjb21tZW5kKiogdGhhdCB5b3UgdXNlIGEgYHdvcmtmbG93KClgIGluc3RlYWQgb2YgbWFudWFsbHkgZXN0aW1hdGluZyBhIHJlY2lwZSB1c2luZyBwcmVwIGFuZCBiYWtlLiBXZSdsbCBzZWUgYWxsIHRoaXMgaW4ganVzdCBhIG1vbWVudC4KPgo+IEhvd2V2ZXIgZm9yIG5vdywgd2UgYXJlIHVzaW5nIHJlY2lwZXMgKyBwcmVwICsgYmFrZSB0byBzcGVjaWZ5IHdoYXQgc3RlcHMgc2hvdWxkIGJlIGFwcGxpZWQgdG8gYSBkYXRhIHNldCBpbiBvcmRlciB0byBnZXQgaXQgcmVhZHkgZm9yIGRhdGEgYW5hbHlzaXMgYW5kIHRoZW4gZXh0cmFjdCB0aGUgcHJlcHJvY2Vzc2VkIGRhdGEgd2l0aCB0aGUgc3RlcHMgYXBwbGllZC4KCmBgYHtyIHJlY2lwZV9wcmVwX2Jha2V9CiMgUHJlcHJvY2VzcyBhbmQgZXh0cmFjdCBkYXRhIHRvIGFsbG93IHNvbWUgZGF0YSBhbmFseXNpcwpiYWtlZF9wdW1wa2lucyA8LSByZWNpcGUoY29sb3IgfiAuLCBkYXRhID0gcHVtcGtpbnNfc2VsZWN0KSAlPiUKICAjIERlZmluZSBvcmRlcmluZyBmb3IgaXRlbV9zaXplIGNvbHVtbgogIHN0ZXBfbXV0YXRlKGl0ZW1fc2l6ZSA9IG9yZGVyZWQoaXRlbV9zaXplLCBsZXZlbHMgPSBjKCdzbWwnLCAnbWVkJywgJ21lZC1sZ2UnLCAnbGdlJywgJ3hsZ2UnLCAnamJvJywgJ2V4amJvJykpKSAlPiUKICAjIENvbnZlcnQgZmFjdG9ycyB0byBudW1iZXJzIHVzaW5nIHRoZSBvcmRlciBkZWZpbmVkIGFib3ZlIChPcmRpbmFsIGVuY29kaW5nKQogIHN0ZXBfaW50ZWdlcihpdGVtX3NpemUsIHplcm9fYmFzZWQgPSBGKSAlPiUKICAjIEVuY29kZSBhbGwgb3RoZXIgcHJlZGljdG9ycyB1c2luZyBvbmUgaG90IGVuY29kaW5nCiAgc3RlcF9kdW1teShhbGxfbm9taW5hbCgpLCAtYWxsX291dGNvbWVzKCksIG9uZV9ob3QgPSBUUlVFKSAlPiUKICBwcmVwKGRhdGEgPSBwdW1wa2luX3NlbGVjdCkgJT4lCiAgYmFrZShuZXdfZGF0YSA9IE5VTEwpCgojIERpc3BsYXkgdGhlIGZpcnN0IGZldyByb3dzIG9mIHByZXByb2Nlc3NlZCBkYXRhCmJha2VkX3B1bXBraW5zICU+JSAKICBzbGljZV9oZWFkKG4gPSA1KQpgYGAKCk5vdywgbGV0J3MgbWFrZSBhIGNhdGVnb3JpY2FsIHBsb3Qgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBwcmVkaWN0b3JzIHdpdGggcmVzcGVjdCB0byB0aGUgb3V0Y29tZSBjb2xvciEKCmBgYHtyIGNhdCBwbG90IHB1bXBraW5zLWNvbG9ycy12YXJpZXR5fQojIFNwZWNpZnkgY29sb3JzIGZvciBlYWNoIHZhbHVlIG9mIHRoZSBodWUgdmFyaWFibGUKcGFsZXR0ZSA8LSBjKE9SQU5HRSA9ICJvcmFuZ2UiLCBXSElURSA9ICJ3aGVhdCIpCgojIENyZWF0ZSB0aGUgYmFyIHBsb3QKZ2dwbG90KHB1bXBraW5zX3NlbGVjdCwgYWVzKHkgPSB2YXJpZXR5LCBmaWxsID0gY29sb3IpKSArCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsZXR0ZSkgKwogIGxhYnMoeSA9ICJWYXJpZXR5IiwgZmlsbCA9ICJDb2xvciIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpBbWF6aW5n8J+kqSEgRm9yIHNvbWUgb2YgdGhlIGZlYXR1cmVzLCB0aGVyZSdzIGEgbm90aWNlYWJsZSBkaWZmZXJlbmNlIGluIHRoZSBkaXN0cmlidXRpb24gZm9yIGVhY2ggY29sb3IgbGFiZWwuIEZvciBpbnN0YW5jZSwgaXQgc2VlbXMgdGhlIHdoaXRlIHB1bXBraW5zIGNhbiBiZSBmb3VuZCBpbiBzbWFsbGVyIHBhY2thZ2VzIGFuZCBpbiBzb21lIHBhcnRpY3VsYXIgdmFyaWV0aWVzIG9mIHB1bXBraW5zLiBUaGUgKml0ZW1fc2l6ZSogY2F0ZWdvcnkgYWxzbyBzZWVtcyB0byBtYWtlIGEgZGlmZmVyZW5jZSBpbiB0aGUgY29sb3IgZGlzdHJpYnV0aW9uLiBUaGVzZSBmZWF0dXJlcyBtYXkgaGVscCBwcmVkaWN0IHRoZSBjb2xvciBvZiBhIHB1bXBraW4uCgojIyMgKipBbmFseXNpbmcgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGZlYXR1cmVzIGFuZCBsYWJlbCoqCgpgYGB7cn0KCiMgRGVmaW5lIHRoZSBjb2xvciBwYWxldHRlCnBhbGV0dGUgPC0gYyhPUkFOR0UgPSAib3JhbmdlIiwgV0hJVEUgPSAid2hlYXQiKQoKIyBXZSBuZWVkIHRoZSBlbmNvZGVkIEl0ZW0gU2l6ZSBjb2x1bW4gdG8gdXNlIGl0IGFzIHRoZSB4LWF4aXMgdmFsdWVzIGluIHRoZSBwbG90CnB1bXBraW5zX3NlbGVjdF9wbG90PC1wdW1wa2luc19zZWxlY3QKcHVtcGtpbnNfc2VsZWN0X3Bsb3QkaXRlbV9zaXplIDwtIGJha2VkX3B1bXBraW5zJGl0ZW1fc2l6ZQoKIyBDcmVhdGUgdGhlIGdyb3VwZWQgYm94IHBsb3QKZ2dwbG90KHB1bXBraW5zX3NlbGVjdF9wbG90LCBhZXMoeCA9IGBpdGVtX3NpemVgLCB5ID0gY29sb3IsIGZpbGwgPSBjb2xvcikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZmFjZXRfZ3JpZCh2YXJpZXR5IH4gLiwgc2NhbGVzID0gImZyZWVfeCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlKSArCiAgbGFicyh4ID0gIkl0ZW0gU2l6ZSIsIHkgPSAiIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJDb2xvciIpKSArCiAgdGhlbWUocGFuZWwuc3BhY2luZyA9IHVuaXQoMC41LCAibGluZXMiKSkrCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA0LCBoanVzdCA9IDApKSAKCmBgYAoKTGV0J3Mgbm93IGZvY3VzIG9uIGEgc3BlY2lmaWMgcmVsYXRpb25zaGlwOiBJdGVtIFNpemUgYW5kIENvbG9yIQoKIyMjIyAqKlVzZSBhIHN3YXJtIHBsb3QqKgoKQ29sb3IgaXMgYSBiaW5hcnkgY2F0ZWdvcnkgKE9yYW5nZSBvciBOb3QpLCBpdCdzIGNhbGxlZCBgY2F0ZWdvcmljYWwgZGF0YWAuIFRoZXJlIGFyZSBvdGhlciB2YXJpb3VzIHdheXMgb2YgW3Zpc3VhbGl6aW5nIGNhdGVnb3JpY2FsIGRhdGFdKGh0dHBzOi8vc2VhYm9ybi5weWRhdGEub3JnL3R1dG9yaWFsL2NhdGVnb3JpY2FsLmh0bWw/aGlnaGxpZ2h0PWJhcikuCgpUcnkgYSBgc3dhcm0gcGxvdGAgdG8gc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIGNvbG9yIHdpdGggcmVzcGVjdCB0byB0aGUgaXRlbV9zaXplLgoKV2UnbGwgdXNlIHRoZSBbZ2diZWVzd2FybSBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vZWNsYXJrZS9nZ2JlZXN3YXJtKSB3aGljaCBwcm92aWRlcyBtZXRob2RzIHRvIGNyZWF0ZSBiZWVzd2FybS1zdHlsZSBwbG90cyB1c2luZyBnZ3Bsb3QyLiBCZWVzd2FybSBwbG90cyBhcmUgYSB3YXkgb2YgcGxvdHRpbmcgcG9pbnRzIHRoYXQgd291bGQgb3JkaW5hcmlseSBvdmVybGFwIHNvIHRoYXQgdGhleSBmYWxsIG5leHQgdG8gZWFjaCBvdGhlciBpbnN0ZWFkLgoKYGBge3IgYmVlX3N3YXJtIHBsb3R9CiMgQ3JlYXRlIGJlZXN3YXJtIHBsb3RzIG9mIGNvbG9yIGFuZCBpdGVtX3NpemUKYmFrZWRfcHVtcGtpbnMgJT4lIAogIG11dGF0ZShjb2xvciA9IGZhY3Rvcihjb2xvcikpICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gY29sb3IsIHkgPSBpdGVtX3NpemUsIGNvbG9yID0gY29sb3IpKSArCiAgZ2VvbV9xdWFzaXJhbmRvbSgpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIsIGRpcmVjdGlvbiA9IC0xKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKTm93IHRoYXQgd2UgaGF2ZSBhbiBpZGVhIG9mIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgYmluYXJ5IGNhdGVnb3JpZXMgb2YgY29sb3IgYW5kIHRoZSBsYXJnZXIgZ3JvdXAgb2Ygc2l6ZXMsIGxldCdzIGV4cGxvcmUgbG9naXN0aWMgcmVncmVzc2lvbiB0byBkZXRlcm1pbmUgYSBnaXZlbiBwdW1wa2luJ3MgbGlrZWx5IGNvbG9yLgoKIyMgMy4gQnVpbGQgeW91ciBtb2RlbAoKTGV0J3MgYmVnaW4gYnkgc3BsaXR0aW5nIHRoZSBkYXRhIGludG8gYHRyYWluaW5nYCBhbmQgYHRlc3RgIHNldHMuIFRoZSB0cmFpbmluZyBzZXQgaXMgdXNlZCB0byB0cmFpbiBhIGNsYXNzaWZpZXIgc28gdGhhdCBpdCBmaW5kcyBhIHN0YXRpc3RpY2FsIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBmZWF0dXJlcyBhbmQgdGhlIGxhYmVsIHZhbHVlLgoKSXQgaXMgYmVzdCBwcmFjdGljZSB0byBob2xkIG91dCBzb21lIG9mIHlvdXIgZGF0YSBmb3IgKip0ZXN0aW5nKiogaW4gb3JkZXIgdG8gZ2V0IGEgYmV0dGVyIGVzdGltYXRlIG9mIGhvdyB5b3VyIG1vZGVscyB3aWxsIHBlcmZvcm0gb24gbmV3IGRhdGEgYnkgY29tcGFyaW5nIHRoZSBwcmVkaWN0ZWQgbGFiZWxzIHdpdGggdGhlIGFscmVhZHkga25vd24gbGFiZWxzIGluIHRoZSB0ZXN0IHNldC4gW3JzYW1wbGVdKGh0dHBzOi8vcnNhbXBsZS50aWR5bW9kZWxzLm9yZy8pLCBhIHBhY2thZ2UgaW4gVGlkeW1vZGVscywgcHJvdmlkZXMgaW5mcmFzdHJ1Y3R1cmUgZm9yIGVmZmljaWVudCBkYXRhIHNwbGl0dGluZyBhbmQgcmVzYW1wbGluZzoKCmBgYHtyIHNwbGl0X2RhdGF9CiMgU3BsaXQgZGF0YSBpbnRvIDgwJSBmb3IgdHJhaW5pbmcgYW5kIDIwJSBmb3IgdGVzdGluZwpzZXQuc2VlZCgyMDU2KQpwdW1wa2luc19zcGxpdCA8LSBwdW1wa2luc19zZWxlY3QgJT4lIAogIGluaXRpYWxfc3BsaXQocHJvcCA9IDAuOCkKCiMgRXh0cmFjdCB0aGUgZGF0YSBpbiBlYWNoIHNwbGl0CnB1bXBraW5zX3RyYWluIDwtIHRyYWluaW5nKHB1bXBraW5zX3NwbGl0KQpwdW1wa2luc190ZXN0IDwtIHRlc3RpbmcocHVtcGtpbnNfc3BsaXQpCgojIFByaW50IG91dCB0aGUgZmlyc3QgNSByb3dzIG9mIHRoZSB0cmFpbmluZyBzZXQKcHVtcGtpbnNfdHJhaW4gJT4lIAogIHNsaWNlX2hlYWQobiA9IDUpCmBgYAoK8J+ZjCBXZSBhcmUgbm93IHJlYWR5IHRvIHRyYWluIGEgbW9kZWwgYnkgZml0dGluZyB0aGUgdHJhaW5pbmcgZmVhdHVyZXMgdG8gdGhlIHRyYWluaW5nIGxhYmVsIChjb2xvcikuCgpXZSdsbCBiZWdpbiBieSBjcmVhdGluZyBhIHJlY2lwZSB0aGF0IHNwZWNpZmllcyB0aGUgcHJlcHJvY2Vzc2luZyBzdGVwcyB0aGF0IHNob3VsZCBiZSBjYXJyaWVkIG91dCBvbiBvdXIgZGF0YSB0byBnZXQgaXQgcmVhZHkgZm9yIG1vZGVsbGluZyBpLmU6IGVuY29kaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBpbnRvIGEgc2V0IG9mIGludGVnZXJzLiBKdXN0IGxpa2UgYGJha2VkX3B1bXBraW5zYCwgd2UgY3JlYXRlIGEgYHB1bXBraW5zX3JlY2lwZWAgYnV0IGRvIG5vdCBgcHJlcGAgYW5kIGBiYWtlYCBzaW5jZSBpdCB3b3VsZCBiZSBidW5kbGVkIGludG8gYSB3b3JrZmxvdywgd2hpY2ggeW91IHdpbGwgc2VlIGluIGp1c3QgYSBmZXcgc3RlcHMgZnJvbSBub3cuIAoKVGhlcmUgYXJlIHF1aXRlIGEgbnVtYmVyIG9mIHdheXMgdG8gc3BlY2lmeSBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgaW4gVGlkeW1vZGVscy4gU2VlIGA/bG9naXN0aWNfcmVnKClgIEZvciBub3csIHdlJ2xsIHNwZWNpZnkgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHZpYSB0aGUgZGVmYXVsdCBgc3RhdHM6OmdsbSgpYCBlbmdpbmUuCgpgYGB7ciBsb2dfcmVnfQojIENyZWF0ZSBhIHJlY2lwZSB0aGF0IHNwZWNpZmllcyBwcmVwcm9jZXNzaW5nIHN0ZXBzIGZvciBtb2RlbGxpbmcKcHVtcGtpbnNfcmVjaXBlIDwtIHJlY2lwZShjb2xvciB+IC4sIGRhdGEgPSBwdW1wa2luc190cmFpbikgJT4lIAogIHN0ZXBfbXV0YXRlKGl0ZW1fc2l6ZSA9IG9yZGVyZWQoaXRlbV9zaXplLCBsZXZlbHMgPSBjKCdzbWwnLCAnbWVkJywgJ21lZC1sZ2UnLCAnbGdlJywgJ3hsZ2UnLCAnamJvJywgJ2V4amJvJykpKSAlPiUKICBzdGVwX2ludGVnZXIoaXRlbV9zaXplLCB6ZXJvX2Jhc2VkID0gRikgJT4lICAKICBzdGVwX2R1bW15KGFsbF9ub21pbmFsKCksIC1hbGxfb3V0Y29tZXMoKSwgb25lX2hvdCA9IFRSVUUpCgojIENyZWF0ZSBhIGxvZ2lzdGljIG1vZGVsIHNwZWNpZmljYXRpb24KbG9nX3JlZyA8LSBsb2dpc3RpY19yZWcoKSAlPiUgCiAgc2V0X2VuZ2luZSgiZ2xtIikgJT4lIAogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpCgpgYGAKCk5vdyB0aGF0IHdlIGhhdmUgYSByZWNpcGUgYW5kIGEgbW9kZWwgc3BlY2lmaWNhdGlvbiwgd2UgbmVlZCB0byBmaW5kIGEgd2F5IG9mIGJ1bmRsaW5nIHRoZW0gdG9nZXRoZXIgaW50byBhbiBvYmplY3QgdGhhdCB3aWxsIGZpcnN0IHByZXByb2Nlc3MgdGhlIGRhdGEgKHByZXArYmFrZSBiZWhpbmQgdGhlIHNjZW5lcyksIGZpdCB0aGUgbW9kZWwgb24gdGhlIHByZXByb2Nlc3NlZCBkYXRhIGFuZCBhbHNvIGFsbG93IGZvciBwb3RlbnRpYWwgcG9zdC1wcm9jZXNzaW5nIGFjdGl2aXRpZXMuCgpJbiBUaWR5bW9kZWxzLCB0aGlzIGNvbnZlbmllbnQgb2JqZWN0IGlzIGNhbGxlZCBhIFtgd29ya2Zsb3dgXShodHRwczovL3dvcmtmbG93cy50aWR5bW9kZWxzLm9yZy8pIGFuZCBjb252ZW5pZW50bHkgaG9sZHMgeW91ciBtb2RlbGluZyBjb21wb25lbnRzLgoKYGBge3Igd29ya2Zsb3d9CiMgQnVuZGxlIG1vZGVsbGluZyBjb21wb25lbnRzIGluIGEgd29ya2Zsb3cKbG9nX3JlZ193ZiA8LSB3b3JrZmxvdygpICU+JSAKICBhZGRfcmVjaXBlKHB1bXBraW5zX3JlY2lwZSkgJT4lIAogIGFkZF9tb2RlbChsb2dfcmVnKQoKIyBQcmludCBvdXQgdGhlIHdvcmtmbG93CmxvZ19yZWdfd2YKCmBgYAoKQWZ0ZXIgYSB3b3JrZmxvdyBoYXMgYmVlbiAqc3BlY2lmaWVkKiwgYSBtb2RlbCBjYW4gYmUgYHRyYWluZWRgIHVzaW5nIHRoZSBbYGZpdCgpYF0oaHR0cHM6Ly90aWR5bW9kZWxzLmdpdGh1Yi5pby9wYXJzbmlwL3JlZmVyZW5jZS9maXQuaHRtbCkgZnVuY3Rpb24uIFRoZSB3b3JrZmxvdyB3aWxsIGVzdGltYXRlIGEgcmVjaXBlIGFuZCBwcmVwcm9jZXNzIHRoZSBkYXRhIGJlZm9yZSB0cmFpbmluZywgc28gd2Ugd29uJ3QgaGF2ZSB0byBtYW51YWxseSBkbyB0aGF0IHVzaW5nIHByZXAgYW5kIGJha2UuCgoKYGBge3IgdHJhaW59CiMgVHJhaW4gdGhlIG1vZGVsCndmX2ZpdCA8LSBsb2dfcmVnX3dmICU+JSAKICBmaXQoZGF0YSA9IHB1bXBraW5zX3RyYWluKQoKIyBQcmludCB0aGUgdHJhaW5lZCB3b3JrZmxvdwp3Zl9maXQKCgpgYGAKClRoZSBtb2RlbCBwcmludCBvdXQgc2hvd3MgdGhlIGNvZWZmaWNpZW50cyBsZWFybmVkIGR1cmluZyB0cmFpbmluZy4KCk5vdyB3ZSd2ZSB0cmFpbmVkIHRoZSBtb2RlbCB1c2luZyB0aGUgdHJhaW5pbmcgZGF0YSwgd2UgY2FuIG1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3QgZGF0YSB1c2luZyBbcGFyc25pcDo6cHJlZGljdCgpXShodHRwczovL3BhcnNuaXAudGlkeW1vZGVscy5vcmcvcmVmZXJlbmNlL3ByZWRpY3QubW9kZWxfZml0Lmh0bWwpLiBMZXQncyBzdGFydCBieSB1c2luZyB0aGUgbW9kZWwgdG8gcHJlZGljdCBsYWJlbHMgZm9yIG91ciB0ZXN0IHNldCBhbmQgdGhlIHByb2JhYmlsaXRpZXMgZm9yIGVhY2ggbGFiZWwuIFdoZW4gdGhlIHByb2JhYmlsaXR5IGlzIG1vcmUgdGhhbiAwLjUsIHRoZSBwcmVkaWN0IGNsYXNzIGlzIGBXSElURWAgZWxzZSBgT1JBTkdFYC4KCmBgYHtyIHRlc3RfcHJlZH0KIyBNYWtlIHByZWRpY3Rpb25zIGZvciBjb2xvciBhbmQgY29ycmVzcG9uZGluZyBwcm9iYWJpbGl0aWVzCnJlc3VsdHMgPC0gcHVtcGtpbnNfdGVzdCAlPiUgc2VsZWN0KGNvbG9yKSAlPiUgCiAgYmluZF9jb2xzKHdmX2ZpdCAlPiUgCiAgICAgICAgICAgICAgcHJlZGljdChuZXdfZGF0YSA9IHB1bXBraW5zX3Rlc3QpKSAlPiUKICBiaW5kX2NvbHMod2ZfZml0ICU+JQogICAgICAgICAgICAgIHByZWRpY3QobmV3X2RhdGEgPSBwdW1wa2luc190ZXN0LCB0eXBlID0gInByb2IiKSkKCiMgQ29tcGFyZSBwcmVkaWN0aW9ucwpyZXN1bHRzICU+JSAKICBzbGljZV9oZWFkKG4gPSAxMCkKCmBgYAoKVmVyeSBuaWNlISBUaGlzIHByb3ZpZGVzIHNvbWUgbW9yZSBpbnNpZ2h0cyBpbnRvIGhvdyBsb2dpc3RpYyByZWdyZXNzaW9uIHdvcmtzLgoKQ29tcGFyaW5nIGVhY2ggcHJlZGljdGlvbiB3aXRoIGl0cyBjb3JyZXNwb25kaW5nICJncm91bmQgdHJ1dGgiIGFjdHVhbCB2YWx1ZSBpc24ndCBhIHZlcnkgZWZmaWNpZW50IHdheSB0byBkZXRlcm1pbmUgaG93IHdlbGwgdGhlIG1vZGVsIGlzIHByZWRpY3RpbmcuIEZvcnR1bmF0ZWx5LCBUaWR5bW9kZWxzIGhhcyBhIGZldyBtb3JlIHRyaWNrcyB1cCBpdHMgc2xlZXZlOiBbYHlhcmRzdGlja2BdKGh0dHBzOi8veWFyZHN0aWNrLnRpZHltb2RlbHMub3JnLykgLSBhIHBhY2thZ2UgdXNlZCB0byBtZWFzdXJlIHRoZSBlZmZlY3RpdmVuZXNzIG9mIG1vZGVscyB1c2luZyBwZXJmb3JtYW5jZSBtZXRyaWNzLgoKT25lIHBlcmZvcm1hbmNlIG1ldHJpYyBhc3NvY2lhdGVkIHdpdGggY2xhc3NpZmljYXRpb24gcHJvYmxlbXMgaXMgdGhlIFtgY29uZnVzaW9uIG1hdHJpeGBdKGh0dHBzOi8vd2lraXBlZGlhLm9yZy93aWtpL0NvbmZ1c2lvbl9tYXRyaXgpLiBBIGNvbmZ1c2lvbiBtYXRyaXggZGVzY3JpYmVzIGhvdyB3ZWxsIGEgY2xhc3NpZmljYXRpb24gbW9kZWwgcGVyZm9ybXMuIEEgY29uZnVzaW9uIG1hdHJpeCB0YWJ1bGF0ZXMgaG93IG1hbnkgZXhhbXBsZXMgaW4gZWFjaCBjbGFzcyB3ZXJlIGNvcnJlY3RseSBjbGFzc2lmaWVkIGJ5IGEgbW9kZWwuIEluIG91ciBjYXNlLCBpdCB3aWxsIHNob3cgeW91IGhvdyBtYW55IG9yYW5nZSBwdW1wa2lucyB3ZXJlIGNsYXNzaWZpZWQgYXMgb3JhbmdlIGFuZCBob3cgbWFueSB3aGl0ZSBwdW1wa2lucyB3ZXJlIGNsYXNzaWZpZWQgYXMgd2hpdGU7IHRoZSBjb25mdXNpb24gbWF0cml4IGFsc28gc2hvd3MgeW91IGhvdyBtYW55IHdlcmUgY2xhc3NpZmllZCBpbnRvIHRoZSAqKndyb25nKiogY2F0ZWdvcmllcy4KClRoZSBbKipgY29uZl9tYXQoKWAqKl0oaHR0cHM6Ly90aWR5bW9kZWxzLmdpdGh1Yi5pby95YXJkc3RpY2svcmVmZXJlbmNlL2NvbmZfbWF0Lmh0bWwpIGZ1bmN0aW9uIGZyb20geWFyZHN0aWNrIGNhbGN1bGF0ZXMgdGhpcyBjcm9zcy10YWJ1bGF0aW9uIG9mIG9ic2VydmVkIGFuZCBwcmVkaWN0ZWQgY2xhc3Nlcy4KCmBgYHtyIGNvbmZfbWF0fQojIENvbmZ1c2lvbiBtYXRyaXggZm9yIHByZWRpY3Rpb24gcmVzdWx0cwpjb25mX21hdChkYXRhID0gcmVzdWx0cywgdHJ1dGggPSBjb2xvciwgZXN0aW1hdGUgPSAucHJlZF9jbGFzcykKYGBgCgpMZXQncyBpbnRlcnByZXQgdGhlIGNvbmZ1c2lvbiBtYXRyaXguIE91ciBtb2RlbCBpcyBhc2tlZCB0byBjbGFzc2lmeSBwdW1wa2lucyBiZXR3ZWVuIHR3byBiaW5hcnkgY2F0ZWdvcmllcywgY2F0ZWdvcnkgYHdoaXRlYCBhbmQgY2F0ZWdvcnkgYG5vdC13aGl0ZWAKCi0gICBJZiB5b3VyIG1vZGVsIHByZWRpY3RzIGEgcHVtcGtpbiBhcyB3aGl0ZSBhbmQgaXQgYmVsb25ncyB0byBjYXRlZ29yeSAnd2hpdGUnIGluIHJlYWxpdHkgd2UgY2FsbCBpdCBhIGB0cnVlIHBvc2l0aXZlYCwgc2hvd24gYnkgdGhlIHRvcCBsZWZ0IG51bWJlci4KCi0gICBJZiB5b3VyIG1vZGVsIHByZWRpY3RzIGEgcHVtcGtpbiBhcyBub3Qgd2hpdGUgYW5kIGl0IGJlbG9uZ3MgdG8gY2F0ZWdvcnkgJ3doaXRlJyBpbiByZWFsaXR5IHdlIGNhbGwgaXQgYSBgZmFsc2UgbmVnYXRpdmVgLCBzaG93biBieSB0aGUgYm90dG9tIGxlZnQgbnVtYmVyLgoKLSAgIElmIHlvdXIgbW9kZWwgcHJlZGljdHMgYSBwdW1wa2luIGFzIHdoaXRlIGFuZCBpdCBiZWxvbmdzIHRvIGNhdGVnb3J5ICdub3Qtd2hpdGUnIGluIHJlYWxpdHkgd2UgY2FsbCBpdCBhIGBmYWxzZSBwb3NpdGl2ZWAsIHNob3duIGJ5IHRoZSB0b3AgcmlnaHQgbnVtYmVyLgoKLSAgIElmIHlvdXIgbW9kZWwgcHJlZGljdHMgYSBwdW1wa2luIGFzIG5vdCB3aGl0ZSBhbmQgaXQgYmVsb25ncyB0byBjYXRlZ29yeSAnbm90LXdoaXRlJyBpbiByZWFsaXR5IHdlIGNhbGwgaXQgYSBgdHJ1ZSBuZWdhdGl2ZWAsIHNob3duIGJ5IHRoZSBib3R0b20gcmlnaHQgbnVtYmVyLgoKfCBUcnV0aCB8Cnw6LS0tLS06fAoKCnwgICAgICAgICAgICAgICB8ICAgICAgICB8ICAgICAgIHwKfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tfAp8ICoqUHJlZGljdGVkKiogfCBXSElURSB8IE9SQU5HRSB8CnwgV0hJVEUgICAgICAgIHwgVFAgICAgIHwgRlAgICAgfAp8IE9SQU5HRSAgICAgICAgIHwgRk4gICAgIHwgVE4gICAgfAoKQXMgeW91IG1pZ2h0IGhhdmUgZ3Vlc3NlZCBpdCdzIHByZWZlcmFibGUgdG8gaGF2ZSBhIGxhcmdlciBudW1iZXIgb2YgdHJ1ZSBwb3NpdGl2ZXMgYW5kIHRydWUgbmVnYXRpdmVzIGFuZCBhIGxvd2VyIG51bWJlciBvZiBmYWxzZSBwb3NpdGl2ZXMgYW5kIGZhbHNlIG5lZ2F0aXZlcywgd2hpY2ggaW1wbGllcyB0aGF0IHRoZSBtb2RlbCBwZXJmb3JtcyBiZXR0ZXIuCgpUaGUgY29uZnVzaW9uIG1hdHJpeCBpcyBoZWxwZnVsIHNpbmNlIGl0IGdpdmVzIHJpc2UgdG8gb3RoZXIgbWV0cmljcyB0aGF0IGNhbiBoZWxwIHVzIGJldHRlciBldmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbC4gTGV0J3MgZ28gdGhyb3VnaCBzb21lIG9mIHRoZW06Cgrwn46TIFByZWNpc2lvbjogYFRQLyhUUCArIEZQKWAgZGVmaW5lZCBhcyB0aGUgcHJvcG9ydGlvbiBvZiBwcmVkaWN0ZWQgcG9zaXRpdmVzIHRoYXQgYXJlIGFjdHVhbGx5IHBvc2l0aXZlLiBBbHNvIGNhbGxlZCBbcG9zaXRpdmUgcHJlZGljdGl2ZSB2YWx1ZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUG9zaXRpdmVfcHJlZGljdGl2ZV92YWx1ZSAiUG9zaXRpdmUgcHJlZGljdGl2ZSB2YWx1ZSIpCgrwn46TIFJlY2FsbDogYFRQLyhUUCArIEZOKWAgZGVmaW5lZCBhcyB0aGUgcHJvcG9ydGlvbiBvZiBwb3NpdGl2ZSByZXN1bHRzIG91dCBvZiB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgd2hpY2ggd2VyZSBhY3R1YWxseSBwb3NpdGl2ZS4gQWxzbyBrbm93biBhcyBgc2Vuc2l0aXZpdHlgLgoK8J+OkyBTcGVjaWZpY2l0eTogYFROLyhUTiArIEZQKWAgZGVmaW5lZCBhcyB0aGUgcHJvcG9ydGlvbiBvZiBuZWdhdGl2ZSByZXN1bHRzIG91dCBvZiB0aGUgbnVtYmVyIG9mIHNhbXBsZXMgd2hpY2ggd2VyZSBhY3R1YWxseSBuZWdhdGl2ZS4KCvCfjpMgQWNjdXJhY3k6IGBUUCArIFROLyhUUCArIFROICsgRlAgKyBGTilgIFRoZSBwZXJjZW50YWdlIG9mIGxhYmVscyBwcmVkaWN0ZWQgYWNjdXJhdGVseSBmb3IgYSBzYW1wbGUuCgrwn46TIEYgTWVhc3VyZTogQSB3ZWlnaHRlZCBhdmVyYWdlIG9mIHRoZSBwcmVjaXNpb24gYW5kIHJlY2FsbCwgd2l0aCBiZXN0IGJlaW5nIDEgYW5kIHdvcnN0IGJlaW5nIDAuCgpMZXQncyBjYWxjdWxhdGUgdGhlc2UgbWV0cmljcyEKCmBgYHtyIG1ldHJpY19zZXR9CiMgQ29tYmluZSBtZXRyaWMgZnVuY3Rpb25zIGFuZCBjYWxjdWxhdGUgdGhlbSBhbGwgYXQgb25jZQpldmFsX21ldHJpY3MgPC0gbWV0cmljX3NldChwcHYsIHJlY2FsbCwgc3BlYywgZl9tZWFzLCBhY2N1cmFjeSkKZXZhbF9tZXRyaWNzKGRhdGEgPSByZXN1bHRzLCB0cnV0aCA9IGNvbG9yLCBlc3RpbWF0ZSA9IC5wcmVkX2NsYXNzKQpgYGAKCiMjIyMgKipWaXN1YWxpemUgdGhlIFJPQyBjdXJ2ZSBvZiB0aGlzIG1vZGVsKioKCkZvciBhIHN0YXJ0LCB0aGlzIGlzIG5vdCBhIGJhZCBtb2RlbDsgaXRzIHByZWNpc2lvbiwgcmVjYWxsLCBGIG1lYXN1cmUgYW5kIGFjY3VyYWN5IGFyZSBpbiB0aGUgOTAlIHJhbmdlIHNvIGlkZWFsbHkgeW91IGNvdWxkIHVzZSBpdCB0byBwcmVkaWN0IHRoZSBjb2xvciBvZiBhIHB1bXBraW4gZ2l2ZW4gYSBzZXQgb2YgdmFyaWFibGVzLiBJdCBhbHNvIHNlZW1zIHRoYXQgb3VyIG1vZGVsIHdhcyBub3QgcmVhbGx5IGFibGUgdG8gaWRlbnRpZnkgdGhlIHdoaXRlIHB1bXBraW5zIPCfp5AuIENvdWxkIHlvdSBndWVzcyB3aHk/IE9uZSByZWFzb24gY291bGQgYmUgYmVjYXVzZSBvZiB0aGUgaGlnaCBwcmV2YWxlbmNlIG9mIE9SQU5HRSBwdW1wa2lucyBpbiBvdXIgdHJhaW5pbmcgc2V0IG1ha2luZyBvdXIgbW9kZWwgbW9yZSBpbmNsaW5lZCB0byBwcmVkaWN0IHRoZSBtYWpvcml0eSBjbGFzcy4KCkxldCdzIGRvIG9uZSBtb3JlIHZpc3VhbGl6YXRpb24gdG8gc2VlIHRoZSBzby1jYWxsZWQgW2BST0Mgc2NvcmVgXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SZWNlaXZlcl9vcGVyYXRpbmdfY2hhcmFjdGVyaXN0aWMpOgoKYGBge3Igcm9jX2N1cnZlfQojIE1ha2UgYSByb2NfY3VydmUKcmVzdWx0cyAlPiUgCiAgcm9jX2N1cnZlKGNvbG9yLCAucHJlZF9PUkFOR0UpICU+JSAKICBhdXRvcGxvdCgpCgpgYGAKClJPQyBjdXJ2ZXMgYXJlIG9mdGVuIHVzZWQgdG8gZ2V0IGEgdmlldyBvZiB0aGUgb3V0cHV0IG9mIGEgY2xhc3NpZmllciBpbiB0ZXJtcyBvZiBpdHMgdHJ1ZSB2cy4gZmFsc2UgcG9zaXRpdmVzLiBST0MgY3VydmVzIHR5cGljYWxseSBmZWF0dXJlIGBUcnVlIFBvc2l0aXZlIFJhdGVgL1NlbnNpdGl2aXR5IG9uIHRoZSBZIGF4aXMsIGFuZCBgRmFsc2UgUG9zaXRpdmUgUmF0ZWAvMS1TcGVjaWZpY2l0eSBvbiB0aGUgWCBheGlzLiBUaHVzLCB0aGUgc3RlZXBuZXNzIG9mIHRoZSBjdXJ2ZSBhbmQgdGhlIHNwYWNlIGJldHdlZW4gdGhlIG1pZHBvaW50IGxpbmUgYW5kIHRoZSBjdXJ2ZSBtYXR0ZXI6IHlvdSB3YW50IGEgY3VydmUgdGhhdCBxdWlja2x5IGhlYWRzIHVwIGFuZCBvdmVyIHRoZSBsaW5lLiBJbiBvdXIgY2FzZSwgdGhlcmUgYXJlIGZhbHNlIHBvc2l0aXZlcyB0byBzdGFydCB3aXRoLCBhbmQgdGhlbiB0aGUgbGluZSBoZWFkcyB1cCBhbmQgb3ZlciBwcm9wZXJseS4KCkZpbmFsbHksIGxldCdzIHVzZSBgeWFyZHN0aWNrOjpyb2NfYXVjKClgIHRvIGNhbGN1bGF0ZSB0aGUgYWN0dWFsIEFyZWEgVW5kZXIgdGhlIEN1cnZlLiBPbmUgd2F5IG9mIGludGVycHJldGluZyBBVUMgaXMgYXMgdGhlIHByb2JhYmlsaXR5IHRoYXQgdGhlIG1vZGVsIHJhbmtzIGEgcmFuZG9tIHBvc2l0aXZlIGV4YW1wbGUgbW9yZSBoaWdobHkgdGhhbiBhIHJhbmRvbSBuZWdhdGl2ZSBleGFtcGxlLgoKYGBge3Igcm9jX2FvY30KIyBDYWxjdWxhdGUgYXJlYSB1bmRlciBjdXJ2ZQpyZXN1bHRzICU+JSAKICByb2NfYXVjKGNvbG9yLCAucHJlZF9PUkFOR0UpCgpgYGAKClRoZSByZXN1bHQgaXMgYXJvdW5kIGAwLjk0N2AuIEdpdmVuIHRoYXQgdGhlIEFVQyByYW5nZXMgZnJvbSAwIHRvIDEsIHlvdSB3YW50IGEgYmlnIHNjb3JlLCBzaW5jZSBhIG1vZGVsIHRoYXQgaXMgMTAwJSBjb3JyZWN0IGluIGl0cyBwcmVkaWN0aW9ucyB3aWxsIGhhdmUgYW4gQVVDIG9mIDE7IGluIHRoaXMgY2FzZSwgdGhlIG1vZGVsIGlzICpwcmV0dHkgZ29vZCouCgpJbiBmdXR1cmUgbGVzc29ucyBvbiBjbGFzc2lmaWNhdGlvbnMsIHlvdSB3aWxsIGxlYXJuIGhvdyB0byBpbXByb3ZlIHlvdXIgbW9kZWwncyBzY29yZXMgKHN1Y2ggYXMgZGVhbGluZyB3aXRoIGltYmFsYW5jZWQgZGF0YSBpbiB0aGlzIGNhc2UpLgoKQnV0IGZvciBub3csIGNvbmdyYXR1bGF0aW9ucyDwn46J8J+OifCfjokhIFlvdSd2ZSBjb21wbGV0ZWQgdGhlc2UgcmVncmVzc2lvbiBsZXNzb25zIQoKWW91IFIgYXdlc29tZSEKCiFbQXJ0d29yayBieSBcQGFsbGlzb25faG9yc3RdKC4uLy4uL2ltYWdlcy9yX2xlYXJuZXJzX3NtLmpwZWcpCg==
+
LS0tCnRpdGxlOiAnQnVpbGQgYSByZWdyZXNzaW9uIG1vZGVsOiBsb2dpc3RpYyByZWdyZXNzaW9uJwpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdGhlbWU6IGZsYXRseQogICAgaGlnaGxpZ2h0OiBicmVlemVkYXJrCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHllcwotLS0KCiMjIEJ1aWxkIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCAtIExlc3NvbiA0CgohW0xvZ2lzdGljIHZzLiBsaW5lYXIgcmVncmVzc2lvbiBpbmZvZ3JhcGhpY10oLi4vLi4vaW1hZ2VzL2xpbmVhci12cy1sb2dpc3RpYy5wbmcpCgojIyMjICoqW1ByZS1sZWN0dXJlIHF1aXpdKGh0dHBzOi8vZ3JheS1zYW5kLTA3YTEwZjQwMy4xLmF6dXJlc3RhdGljYXBwcy5uZXQvcXVpei8xNS8pKioKCiMjIyMgIEludHJvZHVjdGlvbgoKSW4gdGhpcyBmaW5hbCBsZXNzb24gb24gUmVncmVzc2lvbiwgb25lIG9mIHRoZSBiYXNpYyAqY2xhc3NpYyogTUwgdGVjaG5pcXVlcywgd2Ugd2lsbCB0YWtlIGEgbG9vayBhdCBMb2dpc3RpYyBSZWdyZXNzaW9uLiBZb3Ugd291bGQgdXNlIHRoaXMgdGVjaG5pcXVlIHRvIGRpc2NvdmVyIHBhdHRlcm5zIHRvIHByZWRpY3QgYmluYXJ5IGNhdGVnb3JpZXMuIElzIHRoaXMgY2FuZHkgY2hvY29sYXRlIG9yIG5vdD8gSXMgdGhpcyBkaXNlYXNlIGNvbnRhZ2lvdXMgb3Igbm90PyBXaWxsIHRoaXMgY3VzdG9tZXIgY2hvb3NlIHRoaXMgcHJvZHVjdCBvciBub3Q/CgpJbiB0aGlzIGxlc3NvbiwgeW91IHdpbGwgbGVhcm46CgotICAgVGVjaG5pcXVlcyBmb3IgbG9naXN0aWMgcmVncmVzc2lvbgoK4pyFIERlZXBlbiB5b3VyIHVuZGVyc3RhbmRpbmcgb2Ygd29ya2luZyB3aXRoIHRoaXMgdHlwZSBvZiByZWdyZXNzaW9uIGluIHRoaXMgW0xlYXJuIG1vZHVsZV0oaHR0cHM6Ly9sZWFybi5taWNyb3NvZnQuY29tL3RyYWluaW5nL21vZHVsZXMvaW50cm9kdWN0aW9uLWNsYXNzaWZpY2F0aW9uLW1vZGVscy8/V1QubWNfaWQ9YWNhZGVtaWMtNzc5NTItbGVlc3RvdHQpCgojIyBQcmVyZXF1aXNpdGUKCkhhdmluZyB3b3JrZWQgd2l0aCB0aGUgcHVtcGtpbiBkYXRhLCB3ZSBhcmUgbm93IGZhbWlsaWFyIGVub3VnaCB3aXRoIGl0IHRvIHJlYWxpemUgdGhhdCB0aGVyZSdzIG9uZSBiaW5hcnkgY2F0ZWdvcnkgdGhhdCB3ZSBjYW4gd29yayB3aXRoOiBgQ29sb3JgLgoKTGV0J3MgYnVpbGQgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgdGhhdCwgZ2l2ZW4gc29tZSB2YXJpYWJsZXMsICp3aGF0IGNvbG9yIGEgZ2l2ZW4gcHVtcGtpbiBpcyBsaWtlbHkgdG8gYmUqIChvcmFuZ2Ug8J+OgyBvciB3aGl0ZSDwn5G7KS4KCj4gV2h5IGFyZSB3ZSB0YWxraW5nIGFib3V0IGJpbmFyeSBjbGFzc2lmaWNhdGlvbiBpbiBhIGxlc3NvbiBncm91cGluZyBhYm91dCByZWdyZXNzaW9uPyBPbmx5IGZvciBsaW5ndWlzdGljIGNvbnZlbmllbmNlLCBhcyBsb2dpc3RpYyByZWdyZXNzaW9uIGlzIFtyZWFsbHkgYSBjbGFzc2lmaWNhdGlvbiBtZXRob2RdKGh0dHBzOi8vc2Npa2l0LWxlYXJuLm9yZy9zdGFibGUvbW9kdWxlcy9saW5lYXJfbW9kZWwuaHRtbCNsb2dpc3RpYy1yZWdyZXNzaW9uKSwgYWxiZWl0IGEgbGluZWFyLWJhc2VkIG9uZS4gTGVhcm4gYWJvdXQgb3RoZXIgd2F5cyB0byBjbGFzc2lmeSBkYXRhIGluIHRoZSBuZXh0IGxlc3NvbiBncm91cC4KCkZvciB0aGlzIGxlc3Nvbiwgd2UnbGwgcmVxdWlyZSB0aGUgZm9sbG93aW5nIHBhY2thZ2VzOgoKLSAgIGB0aWR5dmVyc2VgOiBUaGUgW3RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pIGlzIGEgW2NvbGxlY3Rpb24gb2YgUiBwYWNrYWdlc10oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy9wYWNrYWdlcykgZGVzaWduZWQgdG8gbWFrZXMgZGF0YSBzY2llbmNlIGZhc3RlciwgZWFzaWVyIGFuZCBtb3JlIGZ1biEKCi0gICBgdGlkeW1vZGVsc2A6IFRoZSBbdGlkeW1vZGVsc10oaHR0cHM6Ly93d3cudGlkeW1vZGVscy5vcmcvKSBmcmFtZXdvcmsgaXMgYSBbY29sbGVjdGlvbiBvZiBwYWNrYWdlc10oaHR0cHM6Ly93d3cudGlkeW1vZGVscy5vcmcvcGFja2FnZXMvKSBmb3IgbW9kZWxpbmcgYW5kIG1hY2hpbmUgbGVhcm5pbmcuCgotICAgYGphbml0b3JgOiBUaGUgW2phbml0b3IgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL3NmaXJrZS9qYW5pdG9yKSBwcm92aWRlcyBzaW1wbGUgbGl0dGxlIHRvb2xzIGZvciBleGFtaW5pbmcgYW5kIGNsZWFuaW5nIGRpcnR5IGRhdGEuCgotICAgYGdnYmVlc3dhcm1gOiBUaGUgW2dnYmVlc3dhcm0gcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2VjbGFya2UvZ2diZWVzd2FybSkgcHJvdmlkZXMgbWV0aG9kcyB0byBjcmVhdGUgYmVlc3dhcm0tc3R5bGUgcGxvdHMgdXNpbmcgZ2dwbG90Mi4KCllvdSBjYW4gaGF2ZSB0aGVtIGluc3RhbGxlZCBhczoKCmBpbnN0YWxsLnBhY2thZ2VzKGMoInRpZHl2ZXJzZSIsICJ0aWR5bW9kZWxzIiwgImphbml0b3IiLCAiZ2diZWVzd2FybSIpKWAKCkFsdGVybmF0ZWx5LCB0aGUgc2NyaXB0IGJlbG93IGNoZWNrcyB3aGV0aGVyIHlvdSBoYXZlIHRoZSBwYWNrYWdlcyByZXF1aXJlZCB0byBjb21wbGV0ZSB0aGlzIG1vZHVsZSBhbmQgaW5zdGFsbHMgdGhlbSBmb3IgeW91IGluIGNhc2UgdGhleSBhcmUgbWlzc2luZy4KCmBgYHtyLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0Kc3VwcHJlc3NXYXJuaW5ncyhpZiAoIXJlcXVpcmUoInBhY21hbiIpKWluc3RhbGwucGFja2FnZXMoInBhY21hbiIpKQoKcGFjbWFuOjpwX2xvYWQodGlkeXZlcnNlLCB0aWR5bW9kZWxzLCBqYW5pdG9yLCBnZ2JlZXN3YXJtKQpgYGAKCiMjICoqRGVmaW5lIHRoZSBxdWVzdGlvbioqCgpGb3Igb3VyIHB1cnBvc2VzLCB3ZSB3aWxsIGV4cHJlc3MgdGhpcyBhcyBhIGJpbmFyeTogJ1doaXRlJyBvciAnTm90IFdoaXRlJy4gVGhlcmUgaXMgYWxzbyBhICdzdHJpcGVkJyBjYXRlZ29yeSBpbiBvdXIgZGF0YXNldCBidXQgdGhlcmUgYXJlIGZldyBpbnN0YW5jZXMgb2YgaXQsIHNvIHdlIHdpbGwgbm90IHVzZSBpdC4gSXQgZGlzYXBwZWFycyBvbmNlIHdlIHJlbW92ZSBudWxsIHZhbHVlcyBmcm9tIHRoZSBkYXRhc2V0LCBhbnl3YXkuCgo+IPCfjoMgRnVuIGZhY3QsIHdlIHNvbWV0aW1lcyBjYWxsIHdoaXRlIHB1bXBraW5zICdnaG9zdCcgcHVtcGtpbnMuIFRoZXkgYXJlbid0IHZlcnkgZWFzeSB0byBjYXJ2ZSwgc28gdGhleSBhcmVuJ3QgYXMgcG9wdWxhciBhcyB0aGUgb3JhbmdlIG9uZXMgYnV0IHRoZXkgYXJlIGNvb2wgbG9va2luZyEgU28gd2UgY291bGQgYWxzbyByZWZvcm11bGF0ZSBvdXIgcXVlc3Rpb24gYXM6ICdHaG9zdCcgb3IgJ05vdCBHaG9zdCcuIPCfkbsKCiMjICoqQWJvdXQgbG9naXN0aWMgcmVncmVzc2lvbioqCgpMb2dpc3RpYyByZWdyZXNzaW9uIGRpZmZlcnMgZnJvbSBsaW5lYXIgcmVncmVzc2lvbiwgd2hpY2ggeW91IGxlYXJuZWQgYWJvdXQgcHJldmlvdXNseSwgaW4gYSBmZXcgaW1wb3J0YW50IHdheXMuCgojIyMjICoqQmluYXJ5IGNsYXNzaWZpY2F0aW9uKioKCkxvZ2lzdGljIHJlZ3Jlc3Npb24gZG9lcyBub3Qgb2ZmZXIgdGhlIHNhbWUgZmVhdHVyZXMgYXMgbGluZWFyIHJlZ3Jlc3Npb24uIFRoZSBmb3JtZXIgb2ZmZXJzIGEgcHJlZGljdGlvbiBhYm91dCBhIGBiaW5hcnkgY2F0ZWdvcnlgICgib3JhbmdlIG9yIG5vdCBvcmFuZ2UiKSB3aGVyZWFzIHRoZSBsYXR0ZXIgaXMgY2FwYWJsZSBvZiBwcmVkaWN0aW5nIGBjb250aW51YWwgdmFsdWVzYCwgZm9yIGV4YW1wbGUgZ2l2ZW4gdGhlIG9yaWdpbiBvZiBhIHB1bXBraW4gYW5kIHRoZSB0aW1lIG9mIGhhcnZlc3QsICpob3cgbXVjaCBpdHMgcHJpY2Ugd2lsbCByaXNlKi4KCiFbSW5mb2dyYXBoaWMgYnkgRGFzYW5pIE1hZGlwYWxsaV0oLi4vLi4vaW1hZ2VzL3B1bXBraW4tY2xhc3NpZmllci5wbmcpCgojIyMgT3RoZXIgY2xhc3NpZmljYXRpb25zCgpUaGVyZSBhcmUgb3RoZXIgdHlwZXMgb2YgbG9naXN0aWMgcmVncmVzc2lvbiwgaW5jbHVkaW5nIG11bHRpbm9taWFsIGFuZCBvcmRpbmFsOgoKLSAqKk11bHRpbm9taWFsKiosIHdoaWNoIGludm9sdmVzIGhhdmluZyBtb3JlIHRoYW4gb25lIGNhdGVnb3J5IC0gIk9yYW5nZSwgV2hpdGUsIGFuZCBTdHJpcGVkIi4KCi0gKipPcmRpbmFsKiosIHdoaWNoIGludm9sdmVzIG9yZGVyZWQgY2F0ZWdvcmllcywgdXNlZnVsIGlmIHdlIHdhbnRlZCB0byBvcmRlciBvdXIgb3V0Y29tZXMgbG9naWNhbGx5LCBsaWtlIG91ciBwdW1wa2lucyB0aGF0IGFyZSBvcmRlcmVkIGJ5IGEgZmluaXRlIG51bWJlciBvZiBzaXplcyAobWluaSxzbSxtZWQsbGcseGwseHhsKS4KCiFbTXVsdGlub21pYWwgdnMgb3JkaW5hbCByZWdyZXNzaW9uXSguLi8uLi9pbWFnZXMvbXVsdGlub21pYWwtdnMtb3JkaW5hbC5wbmcpCgojIyMjICoqVmFyaWFibGVzIERPIE5PVCBoYXZlIHRvIGNvcnJlbGF0ZSoqCgpSZW1lbWJlciBob3cgbGluZWFyIHJlZ3Jlc3Npb24gd29ya2VkIGJldHRlciB3aXRoIG1vcmUgY29ycmVsYXRlZCB2YXJpYWJsZXM/IExvZ2lzdGljIHJlZ3Jlc3Npb24gaXMgdGhlIG9wcG9zaXRlIC0gdGhlIHZhcmlhYmxlcyBkb24ndCBoYXZlIHRvIGFsaWduLiBUaGF0IHdvcmtzIGZvciB0aGlzIGRhdGEgd2hpY2ggaGFzIHNvbWV3aGF0IHdlYWsgY29ycmVsYXRpb25zLgoKIyMjIyAqKllvdSBuZWVkIGEgbG90IG9mIGNsZWFuIGRhdGEqKgoKTG9naXN0aWMgcmVncmVzc2lvbiB3aWxsIGdpdmUgbW9yZSBhY2N1cmF0ZSByZXN1bHRzIGlmIHlvdSB1c2UgbW9yZSBkYXRhOyBvdXIgc21hbGwgZGF0YXNldCBpcyBub3Qgb3B0aW1hbCBmb3IgdGhpcyB0YXNrLCBzbyBrZWVwIHRoYXQgaW4gbWluZC4KCuKchSBUaGluayBhYm91dCB0aGUgdHlwZXMgb2YgZGF0YSB0aGF0IHdvdWxkIGxlbmQgdGhlbXNlbHZlcyB3ZWxsIHRvIGxvZ2lzdGljIHJlZ3Jlc3Npb24KCiMjIEV4ZXJjaXNlIC0gdGlkeSB0aGUgZGF0YQoKRmlyc3QsIGNsZWFuIHRoZSBkYXRhIGEgYml0LCBkcm9wcGluZyBudWxsIHZhbHVlcyBhbmQgc2VsZWN0aW5nIG9ubHkgc29tZSBvZiB0aGUgY29sdW1uczoKCjEuIEFkZCB0aGUgZm9sbG93aW5nIGNvZGU6CgpgYGB7ciwgdGlkeXIsIG1lc3NhZ2U9Riwgd2FybmluZz1GfQojIExvYWQgdGhlIGNvcmUgdGlkeXZlcnNlIHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKQoKIyBJbXBvcnQgdGhlIGRhdGEgYW5kIGNsZWFuIGNvbHVtbiBuYW1lcwpwdW1wa2lucyA8LSByZWFkX2NzdihmaWxlID0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9taWNyb3NvZnQvTUwtRm9yLUJlZ2lubmVycy9tYWluLzItUmVncmVzc2lvbi9kYXRhL1VTLXB1bXBraW5zLmNzdiIpICU+JSAKICBjbGVhbl9uYW1lcygpCgojIFNlbGVjdCBkZXNpcmVkIGNvbHVtbnMKcHVtcGtpbnNfc2VsZWN0IDwtIHB1bXBraW5zICU+JSAKICBzZWxlY3QoYyhjaXR5X25hbWUsIHBhY2thZ2UsIHZhcmlldHksIG9yaWdpbiwgaXRlbV9zaXplLCBjb2xvcikpIAoKIyBEcm9wIHJvd3MgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyBhbmQgZW5jb2RlIGNvbG9yIGFzIGZhY3RvciAoY2F0ZWdvcnkpCnB1bXBraW5zX3NlbGVjdCA8LSBwdW1wa2luc19zZWxlY3QgJT4lIAogIGRyb3BfbmEoKSAlPiUgCiAgbXV0YXRlKGNvbG9yID0gZmFjdG9yKGNvbG9yKSkKCiMgVmlldyB0aGUgZmlyc3QgZmV3IHJvd3MKcHVtcGtpbnNfc2VsZWN0ICU+JSAKICBzbGljZV9oZWFkKG4gPSA1KQpgYGAKCllvdSBjYW4gYWx3YXlzIHRha2UgYSBwZWVrIGF0IHlvdXIgbmV3IGRhdGFmcmFtZSwgYnkgdXNpbmcgdGhlIFsqZ2xpbXBzZSgpKl0oaHR0cHM6Ly9waWxsYXIuci1saWIub3JnL3JlZmVyZW5jZS9nbGltcHNlLmh0bWwpIGZ1bmN0aW9uIGFzIGJlbG93OgoKCmBgYHtyIGdsaW1wc2V9CnB1bXBraW5zX3NlbGVjdCAlPiUgCiAgZ2xpbXBzZSgpCmBgYAoKTGV0J3MgY29uZmlybSB0aGF0IHdlIHdpbGwgYWN0dWFsbHkgYmUgZG9pbmcgYSBiaW5hcnkgY2xhc3NpZmljYXRpb24gcHJvYmxlbToKCmBgYHtyIGRpc3RpbmN0IGNvbG9yfQojIFN1YnNldCBkaXN0aW5jdCBvYnNlcnZhdGlvbnMgaW4gb3V0Y29tZSBjb2x1bW4KcHVtcGtpbnNfc2VsZWN0ICU+JSAKICBkaXN0aW5jdChjb2xvcikKYGBgCgojIyMgVmlzdWFsaXphdGlvbiAtIGNhdGVnb3JpY2FsIHBsb3QKQnkgbm93IHlvdSBoYXZlIGxvYWRlZCB1cCB0aGUgcHVtcGtpbiBkYXRhIG9uY2UgYWdhaW4gYW5kIGNsZWFuZWQgaXQgc28gYXMgdG8gcHJlc2VydmUgYSBkYXRhc2V0IGNvbnRhaW5pbmcgYSBmZXcgdmFyaWFibGVzLCBpbmNsdWRpbmcgQ29sb3IuIExldCdzIHZpc3VhbGl6ZSB0aGUgZGF0YWZyYW1lIGluIHRoZSBub3RlYm9vayB1c2luZyBnZ3Bsb3QgbGlicmFyeS4KClRoZSBnZ3Bsb3QgbGlicmFyeSBvZmZlcnMgc29tZSBuZWF0IHdheXMgdG8gdmlzdWFsaXplIHlvdXIgZGF0YS4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gY29tcGFyZSBkaXN0cmlidXRpb25zIG9mIHRoZSBkYXRhIGZvciBlYWNoIFZhcmlldHkgYW5kIENvbG9yIGluIGEgY2F0ZWdvcmljYWwgcGxvdC4KCjEuIENyZWF0ZSBzdWNoIGEgcGxvdCBieSB1c2luZyB0aGUgZ2VvbWJhciBmdW5jdGlvbiwgdXNpbmcgb3VyIHB1bXBraW4gZGF0YSwgYW5kIHNwZWNpZnlpbmcgYSBjb2xvciBtYXBwaW5nIGZvciBlYWNoIHB1bXBraW4gY2F0ZWdvcnkgKG9yYW5nZSBvciB3aGl0ZSk6CgpgYGB7cn0KIyBTcGVjaWZ5IGNvbG9ycyBmb3IgZWFjaCB2YWx1ZSBvZiB0aGUgaHVlIHZhcmlhYmxlCnBhbGV0dGUgPC0gYyhPUkFOR0UgPSAib3JhbmdlIiwgV0hJVEUgPSAid2hlYXQiKQoKIyBDcmVhdGUgdGhlIGJhciBwbG90CmdncGxvdChwdW1wa2luc19zZWxlY3QsIGFlcyh5ID0gdmFyaWV0eSwgZmlsbCA9IGNvbG9yKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHBhbGV0dGUpICsKICBsYWJzKHkgPSAiVmFyaWV0eSIsIGZpbGwgPSAiQ29sb3IiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKQnkgb2JzZXJ2aW5nIHRoZSBkYXRhLCB5b3UgY2FuIHNlZSBob3cgdGhlIENvbG9yIGRhdGEgcmVsYXRlcyB0byBWYXJpZXR5LgoK4pyFIEdpdmVuIHRoaXMgY2F0ZWdvcmljYWwgcGxvdCwgd2hhdCBhcmUgc29tZSBpbnRlcmVzdGluZyBleHBsb3JhdGlvbnMgeW91IGNhbiBlbnZpc2lvbj8KCiMjIyBEYXRhIHByZS1wcm9jZXNzaW5nOiBmZWF0dXJlIGVuY29kaW5nCgpPdXIgcHVtcGtpbnMgZGF0YXNldCBjb250YWlucyBzdHJpbmcgdmFsdWVzIGZvciBhbGwgaXRzIGNvbHVtbnMuIFdvcmtpbmcgd2l0aCBjYXRlZ29yaWNhbCBkYXRhIGlzIGludHVpdGl2ZSBmb3IgaHVtYW5zIGJ1dCBub3QgZm9yIG1hY2hpbmVzLiBNYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobXMgd29yayB3ZWxsIHdpdGggbnVtYmVycy4gVGhhdCdzIHdoeSBlbmNvZGluZyBpcyBhIHZlcnkgaW1wb3J0YW50IHN0ZXAgaW4gdGhlIGRhdGEgcHJlLXByb2Nlc3NpbmcgcGhhc2UsIHNpbmNlIGl0IGVuYWJsZXMgdXMgdG8gdHVybiBjYXRlZ29yaWNhbCBkYXRhIGludG8gbnVtZXJpY2FsIGRhdGEsIHdpdGhvdXQgbG9zaW5nIGFueSBpbmZvcm1hdGlvbi4gR29vZCBlbmNvZGluZyBsZWFkcyB0byBidWlsZGluZyBhIGdvb2QgbW9kZWwuCgpGb3IgZmVhdHVyZSBlbmNvZGluZyB0aGVyZSBhcmUgdHdvIG1haW4gdHlwZXMgb2YgZW5jb2RlcnM6CgoxLiBPcmRpbmFsIGVuY29kZXI6IGl0IHN1aXRzIHdlbGwgZm9yIG9yZGluYWwgdmFyaWFibGVzLCB3aGljaCBhcmUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdoZXJlIHRoZWlyIGRhdGEgZm9sbG93cyBhIGxvZ2ljYWwgb3JkZXJpbmcsIGxpa2UgdGhlIGBpdGVtX3NpemVgIGNvbHVtbiBpbiBvdXIgZGF0YXNldC4gSXQgY3JlYXRlcyBhIG1hcHBpbmcgc3VjaCB0aGF0IGVhY2ggY2F0ZWdvcnkgaXMgcmVwcmVzZW50ZWQgYnkgYSBudW1iZXIsIHdoaWNoIGlzIHRoZSBvcmRlciBvZiB0aGUgY2F0ZWdvcnkgaW4gdGhlIGNvbHVtbi4KCjIuIENhdGVnb3JpY2FsIGVuY29kZXI6IGl0IHN1aXRzIHdlbGwgZm9yIG5vbWluYWwgdmFyaWFibGVzLCB3aGljaCBhcmUgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdoZXJlIHRoZWlyIGRhdGEgZG9lcyBub3QgZm9sbG93IGEgbG9naWNhbCBvcmRlcmluZywgbGlrZSBhbGwgdGhlIGZlYXR1cmVzIGRpZmZlcmVudCBmcm9tIGBpdGVtX3NpemVgIGluIG91ciBkYXRhc2V0LiBJdCBpcyBhIG9uZS1ob3QgZW5jb2RpbmcsIHdoaWNoIG1lYW5zIHRoYXQgZWFjaCBjYXRlZ29yeSBpcyByZXByZXNlbnRlZCBieSBhIGJpbmFyeSBjb2x1bW46IHRoZSBlbmNvZGVkIHZhcmlhYmxlIGlzIGVxdWFsIHRvIDEgaWYgdGhlIHB1bXBraW4gYmVsb25ncyB0byB0aGF0IFZhcmlldHkgYW5kIDAgb3RoZXJ3aXNlLgoKVGlkeW1vZGVscyBwcm92aWRlcyB5ZXQgYW5vdGhlciBuZWF0IHBhY2thZ2U6IFtyZWNpcGVzXShodHRwczovL3JlY2lwZXMudGlkeW1vZGVscy5vcmcvKS0gYSBwYWNrYWdlIGZvciBwcmVwcm9jZXNzaW5nIGRhdGEuIFdlJ2xsIGRlZmluZSBhIGByZWNpcGVgIHRoYXQgc3BlY2lmaWVzIHRoYXQgYWxsIHByZWRpY3RvciBjb2x1bW5zIHNob3VsZCBiZSBlbmNvZGVkIGludG8gYSBzZXQgb2YgaW50ZWdlcnMgLCBgcHJlcGAgaXQgdG8gZXN0aW1hdGVzIHRoZSByZXF1aXJlZCBxdWFudGl0aWVzIGFuZCBzdGF0aXN0aWNzIG5lZWRlZCBieSBhbnkgb3BlcmF0aW9ucyBhbmQgZmluYWxseSBgYmFrZWAgdG8gYXBwbHkgdGhlIGNvbXB1dGF0aW9ucyB0byBuZXcgZGF0YS4KCj4gTm9ybWFsbHksIHJlY2lwZXMgaXMgdXN1YWxseSB1c2VkIGFzIGEgcHJlcHJvY2Vzc29yIGZvciBtb2RlbGxpbmcgd2hlcmUgaXQgZGVmaW5lcyB3aGF0IHN0ZXBzIHNob3VsZCBiZSBhcHBsaWVkIHRvIGEgZGF0YSBzZXQgaW4gb3JkZXIgdG8gZ2V0IGl0IHJlYWR5IGZvciBtb2RlbGxpbmcuIEluIHRoYXQgY2FzZSBpdCBpcyAqKmhpZ2hseSByZWNvbW1lbmQqKiB0aGF0IHlvdSB1c2UgYSBgd29ya2Zsb3coKWAgaW5zdGVhZCBvZiBtYW51YWxseSBlc3RpbWF0aW5nIGEgcmVjaXBlIHVzaW5nIHByZXAgYW5kIGJha2UuIFdlJ2xsIHNlZSBhbGwgdGhpcyBpbiBqdXN0IGEgbW9tZW50Lgo+Cj4gSG93ZXZlciBmb3Igbm93LCB3ZSBhcmUgdXNpbmcgcmVjaXBlcyArIHByZXAgKyBiYWtlIHRvIHNwZWNpZnkgd2hhdCBzdGVwcyBzaG91bGQgYmUgYXBwbGllZCB0byBhIGRhdGEgc2V0IGluIG9yZGVyIHRvIGdldCBpdCByZWFkeSBmb3IgZGF0YSBhbmFseXNpcyBhbmQgdGhlbiBleHRyYWN0IHRoZSBwcmVwcm9jZXNzZWQgZGF0YSB3aXRoIHRoZSBzdGVwcyBhcHBsaWVkLgoKYGBge3IgcmVjaXBlX3ByZXBfYmFrZX0KIyBQcmVwcm9jZXNzIGFuZCBleHRyYWN0IGRhdGEgdG8gYWxsb3cgc29tZSBkYXRhIGFuYWx5c2lzCmJha2VkX3B1bXBraW5zIDwtIHJlY2lwZShjb2xvciB+IC4sIGRhdGEgPSBwdW1wa2luc19zZWxlY3QpICU+JQogICMgRGVmaW5lIG9yZGVyaW5nIGZvciBpdGVtX3NpemUgY29sdW1uCiAgc3RlcF9tdXRhdGUoaXRlbV9zaXplID0gb3JkZXJlZChpdGVtX3NpemUsIGxldmVscyA9IGMoJ3NtbCcsICdtZWQnLCAnbWVkLWxnZScsICdsZ2UnLCAneGxnZScsICdqYm8nLCAnZXhqYm8nKSkpICU+JQogICMgQ29udmVydCBmYWN0b3JzIHRvIG51bWJlcnMgdXNpbmcgdGhlIG9yZGVyIGRlZmluZWQgYWJvdmUgKE9yZGluYWwgZW5jb2RpbmcpCiAgc3RlcF9pbnRlZ2VyKGl0ZW1fc2l6ZSwgemVyb19iYXNlZCA9IEYpICU+JQogICMgRW5jb2RlIGFsbCBvdGhlciBwcmVkaWN0b3JzIHVzaW5nIG9uZSBob3QgZW5jb2RpbmcKICBzdGVwX2R1bW15KGFsbF9ub21pbmFsKCksIC1hbGxfb3V0Y29tZXMoKSwgb25lX2hvdCA9IFRSVUUpICU+JQogIHByZXAoZGF0YSA9IHB1bXBraW5fc2VsZWN0KSAlPiUKICBiYWtlKG5ld19kYXRhID0gTlVMTCkKCiMgRGlzcGxheSB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgcHJlcHJvY2Vzc2VkIGRhdGEKYmFrZWRfcHVtcGtpbnMgJT4lIAogIHNsaWNlX2hlYWQobiA9IDUpCmBgYAoK4pyFIFdoYXQgYXJlIHRoZSBhZHZhbnRhZ2VzIG9mIHVzaW5nIGFuIG9yZGluYWwgZW5jb2RlciBmb3IgdGhlIEl0ZW0gU2l6ZSBjb2x1bW4/CgojIyMgQW5hbHlzZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzCgpOb3cgdGhhdCB3ZSBoYXZlIHByZS1wcm9jZXNzZWQgb3VyIGRhdGEsIHdlIGNhbiBhbmFseXNlIHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlIGZlYXR1cmVzIGFuZCB0aGUgbGFiZWwgdG8gZ3Jhc3AgYW4gaWRlYSBvZiBob3cgd2VsbCB0aGUgbW9kZWwgd2lsbCBiZSBhYmxlIHRvIHByZWRpY3QgdGhlIGxhYmVsIGdpdmVuIHRoZSBmZWF0dXJlcy4gVGhlIGJlc3Qgd2F5IHRvIHBlcmZvcm0gdGhpcyBraW5kIG9mIGFuYWx5c2lzIGlzIHBsb3R0aW5nIHRoZSBkYXRhLiAKV2UnbGwgYmUgdXNpbmcgYWdhaW4gdGhlIGdncGxvdCBnZW9tX2JveHBsb3RfIGZ1bmN0aW9uLCB0byB2aXN1YWxpemUgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBJdGVtIFNpemUsIFZhcmlldHkgYW5kIENvbG9yIGluIGEgY2F0ZWdvcmljYWwgcGxvdC4gVG8gYmV0dGVyIHBsb3QgdGhlIGRhdGEgd2UnbGwgYmUgdXNpbmcgdGhlIGVuY29kZWQgSXRlbSBTaXplIGNvbHVtbiBhbmQgdGhlIHVuZW5jb2RlZCBWYXJpZXR5IGNvbHVtbi4KCmBgYHtyIGJveHBsb3R9CiMgRGVmaW5lIHRoZSBjb2xvciBwYWxldHRlCnBhbGV0dGUgPC0gYyhPUkFOR0UgPSAib3JhbmdlIiwgV0hJVEUgPSAid2hlYXQiKQoKIyBXZSBuZWVkIHRoZSBlbmNvZGVkIEl0ZW0gU2l6ZSBjb2x1bW4gdG8gdXNlIGl0IGFzIHRoZSB4LWF4aXMgdmFsdWVzIGluIHRoZSBwbG90CnB1bXBraW5zX3NlbGVjdF9wbG90PC1wdW1wa2luc19zZWxlY3QKcHVtcGtpbnNfc2VsZWN0X3Bsb3QkaXRlbV9zaXplIDwtIGJha2VkX3B1bXBraW5zJGl0ZW1fc2l6ZQoKIyBDcmVhdGUgdGhlIGdyb3VwZWQgYm94IHBsb3QKZ2dwbG90KHB1bXBraW5zX3NlbGVjdF9wbG90LCBhZXMoeCA9IGBpdGVtX3NpemVgLCB5ID0gY29sb3IsIGZpbGwgPSBjb2xvcikpICsKICBnZW9tX2JveHBsb3QoKSArCiAgZmFjZXRfZ3JpZCh2YXJpZXR5IH4gLiwgc2NhbGVzID0gImZyZWVfeCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYWxldHRlKSArCiAgbGFicyh4ID0gIkl0ZW0gU2l6ZSIsIHkgPSAiIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJDb2xvciIpKSArCiAgdGhlbWUocGFuZWwuc3BhY2luZyA9IHVuaXQoMC41LCAibGluZXMiKSkrCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA0LCBoanVzdCA9IDApKSAKYGBgCgojIyMjIFVzZSBhIHN3YXJtIHBsb3QKClNpbmNlIENvbG9yIGlzIGEgYmluYXJ5IGNhdGVnb3J5IChXaGl0ZSBvciBOb3QpLCBpdCBuZWVkcyAnYSBbc3BlY2lhbGl6ZWQgYXBwcm9hY2hdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL2Jsb2IvbWFpbi9kYXRhLXZpc3VhbGl6YXRpb24ucGRmKSB0byB2aXN1YWxpemF0aW9uJy4KClRyeSBhIGBzd2FybSBwbG90YCB0byBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgY29sb3Igd2l0aCByZXNwZWN0IHRvIHRoZSBpdGVtX3NpemUuCgpXZSdsbCB1c2UgdGhlIFtnZ2JlZXN3YXJtIHBhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9lY2xhcmtlL2dnYmVlc3dhcm0pIHdoaWNoIHByb3ZpZGVzIG1ldGhvZHMgdG8gY3JlYXRlIGJlZXN3YXJtLXN0eWxlIHBsb3RzIHVzaW5nIGdncGxvdDIuIEJlZXN3YXJtIHBsb3RzIGFyZSBhIHdheSBvZiBwbG90dGluZyBwb2ludHMgdGhhdCB3b3VsZCBvcmRpbmFyaWx5IG92ZXJsYXAgc28gdGhhdCB0aGV5IGZhbGwgbmV4dCB0byBlYWNoIG90aGVyIGluc3RlYWQuCgpgYGB7ciBiZWVfc3dhcm0gcGxvdH0KIyBDcmVhdGUgYmVlc3dhcm0gcGxvdHMgb2YgY29sb3IgYW5kIGl0ZW1fc2l6ZQpiYWtlZF9wdW1wa2lucyAlPiUgCiAgbXV0YXRlKGNvbG9yID0gZmFjdG9yKGNvbG9yKSkgJT4lIAogIGdncGxvdChtYXBwaW5nID0gYWVzKHggPSBjb2xvciwgeSA9IGl0ZW1fc2l6ZSwgY29sb3IgPSBjb2xvcikpICsKICBnZW9tX3F1YXNpcmFuZG9tKCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIiwgZGlyZWN0aW9uID0gLTEpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBhbiBpZGVhIG9mIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgYmluYXJ5IGNhdGVnb3JpZXMgb2YgY29sb3IgYW5kIHRoZSBsYXJnZXIgZ3JvdXAgb2Ygc2l6ZXMsIGxldCdzIGV4cGxvcmUgbG9naXN0aWMgcmVncmVzc2lvbiB0byBkZXRlcm1pbmUgYSBnaXZlbiBwdW1wa2luJ3MgbGlrZWx5IGNvbG9yLgoKIyMgQnVpbGQgeW91ciBtb2RlbAoKU2VsZWN0IHRoZSB2YXJpYWJsZXMgeW91IHdhbnQgdG8gdXNlIGluIHlvdXIgY2xhc3NpZmljYXRpb24gbW9kZWwgYW5kIHNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Qgc2V0cy4gW3JzYW1wbGVdKGh0dHBzOi8vcnNhbXBsZS50aWR5bW9kZWxzLm9yZy8pLCBhIHBhY2thZ2UgaW4gVGlkeW1vZGVscywgcHJvdmlkZXMgaW5mcmFzdHJ1Y3R1cmUgZm9yIGVmZmljaWVudCBkYXRhIHNwbGl0dGluZyBhbmQgcmVzYW1wbGluZzoKCmBgYHtyIHNwbGl0X2RhdGF9CiMgU3BsaXQgZGF0YSBpbnRvIDgwJSBmb3IgdHJhaW5pbmcgYW5kIDIwJSBmb3IgdGVzdGluZwpzZXQuc2VlZCgyMDU2KQpwdW1wa2luc19zcGxpdCA8LSBwdW1wa2luc19zZWxlY3QgJT4lIAogIGluaXRpYWxfc3BsaXQocHJvcCA9IDAuOCkKCiMgRXh0cmFjdCB0aGUgZGF0YSBpbiBlYWNoIHNwbGl0CnB1bXBraW5zX3RyYWluIDwtIHRyYWluaW5nKHB1bXBraW5zX3NwbGl0KQpwdW1wa2luc190ZXN0IDwtIHRlc3RpbmcocHVtcGtpbnNfc3BsaXQpCgojIFByaW50IG91dCB0aGUgZmlyc3QgNSByb3dzIG9mIHRoZSB0cmFpbmluZyBzZXQKcHVtcGtpbnNfdHJhaW4gJT4lIAogIHNsaWNlX2hlYWQobiA9IDUpCmBgYAoK8J+ZjCBXZSBhcmUgbm93IHJlYWR5IHRvIHRyYWluIGEgbW9kZWwgYnkgZml0dGluZyB0aGUgdHJhaW5pbmcgZmVhdHVyZXMgdG8gdGhlIHRyYWluaW5nIGxhYmVsIChjb2xvcikuCgpXZSdsbCBiZWdpbiBieSBjcmVhdGluZyBhIHJlY2lwZSB0aGF0IHNwZWNpZmllcyB0aGUgcHJlcHJvY2Vzc2luZyBzdGVwcyB0aGF0IHNob3VsZCBiZSBjYXJyaWVkIG91dCBvbiBvdXIgZGF0YSB0byBnZXQgaXQgcmVhZHkgZm9yIG1vZGVsbGluZyBpLmU6IGVuY29kaW5nIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBpbnRvIGEgc2V0IG9mIGludGVnZXJzLiBKdXN0IGxpa2UgYGJha2VkX3B1bXBraW5zYCwgd2UgY3JlYXRlIGEgYHB1bXBraW5zX3JlY2lwZWAgYnV0IGRvIG5vdCBgcHJlcGAgYW5kIGBiYWtlYCBzaW5jZSBpdCB3b3VsZCBiZSBidW5kbGVkIGludG8gYSB3b3JrZmxvdywgd2hpY2ggeW91IHdpbGwgc2VlIGluIGp1c3QgYSBmZXcgc3RlcHMgZnJvbSBub3cuIAoKVGhlcmUgYXJlIHF1aXRlIGEgbnVtYmVyIG9mIHdheXMgdG8gc3BlY2lmeSBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgaW4gVGlkeW1vZGVscy4gU2VlIGA/bG9naXN0aWNfcmVnKClgIEZvciBub3csIHdlJ2xsIHNwZWNpZnkgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHZpYSB0aGUgZGVmYXVsdCBgc3RhdHM6OmdsbSgpYCBlbmdpbmUuCgpgYGB7ciBsb2dfcmVnfQojIENyZWF0ZSBhIHJlY2lwZSB0aGF0IHNwZWNpZmllcyBwcmVwcm9jZXNzaW5nIHN0ZXBzIGZvciBtb2RlbGxpbmcKcHVtcGtpbnNfcmVjaXBlIDwtIHJlY2lwZShjb2xvciB+IC4sIGRhdGEgPSBwdW1wa2luc190cmFpbikgJT4lIAogIHN0ZXBfbXV0YXRlKGl0ZW1fc2l6ZSA9IG9yZGVyZWQoaXRlbV9zaXplLCBsZXZlbHMgPSBjKCdzbWwnLCAnbWVkJywgJ21lZC1sZ2UnLCAnbGdlJywgJ3hsZ2UnLCAnamJvJywgJ2V4amJvJykpKSAlPiUKICBzdGVwX2ludGVnZXIoaXRlbV9zaXplLCB6ZXJvX2Jhc2VkID0gRikgJT4lICAKICBzdGVwX2R1bW15KGFsbF9ub21pbmFsKCksIC1hbGxfb3V0Y29tZXMoKSwgb25lX2hvdCA9IFRSVUUpCgojIENyZWF0ZSBhIGxvZ2lzdGljIG1vZGVsIHNwZWNpZmljYXRpb24KbG9nX3JlZyA8LSBsb2dpc3RpY19yZWcoKSAlPiUgCiAgc2V0X2VuZ2luZSgiZ2xtIikgJT4lIAogIHNldF9tb2RlKCJjbGFzc2lmaWNhdGlvbiIpCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBhIHJlY2lwZSBhbmQgYSBtb2RlbCBzcGVjaWZpY2F0aW9uLCB3ZSBuZWVkIHRvIGZpbmQgYSB3YXkgb2YgYnVuZGxpbmcgdGhlbSB0b2dldGhlciBpbnRvIGFuIG9iamVjdCB0aGF0IHdpbGwgZmlyc3QgcHJlcHJvY2VzcyB0aGUgZGF0YSAocHJlcCtiYWtlIGJlaGluZCB0aGUgc2NlbmVzKSwgZml0IHRoZSBtb2RlbCBvbiB0aGUgcHJlcHJvY2Vzc2VkIGRhdGEgYW5kIGFsc28gYWxsb3cgZm9yIHBvdGVudGlhbCBwb3N0LXByb2Nlc3NpbmcgYWN0aXZpdGllcy4KCkluIFRpZHltb2RlbHMsIHRoaXMgY29udmVuaWVudCBvYmplY3QgaXMgY2FsbGVkIGEgW2B3b3JrZmxvd2BdKGh0dHBzOi8vd29ya2Zsb3dzLnRpZHltb2RlbHMub3JnLykgYW5kIGNvbnZlbmllbnRseSBob2xkcyB5b3VyIG1vZGVsaW5nIGNvbXBvbmVudHMuCgpgYGB7ciB3b3JrZmxvd30KIyBCdW5kbGUgbW9kZWxsaW5nIGNvbXBvbmVudHMgaW4gYSB3b3JrZmxvdwpsb2dfcmVnX3dmIDwtIHdvcmtmbG93KCkgJT4lIAogIGFkZF9yZWNpcGUocHVtcGtpbnNfcmVjaXBlKSAlPiUgCiAgYWRkX21vZGVsKGxvZ19yZWcpCgojIFByaW50IG91dCB0aGUgd29ya2Zsb3cKbG9nX3JlZ193ZgpgYGAKCkFmdGVyIGEgd29ya2Zsb3cgaGFzIGJlZW4gKnNwZWNpZmllZCosIGEgbW9kZWwgY2FuIGJlIGB0cmFpbmVkYCB1c2luZyB0aGUgW2BmaXQoKWBdKGh0dHBzOi8vdGlkeW1vZGVscy5naXRodWIuaW8vcGFyc25pcC9yZWZlcmVuY2UvZml0Lmh0bWwpIGZ1bmN0aW9uLiBUaGUgd29ya2Zsb3cgd2lsbCBlc3RpbWF0ZSBhIHJlY2lwZSBhbmQgcHJlcHJvY2VzcyB0aGUgZGF0YSBiZWZvcmUgdHJhaW5pbmcsIHNvIHdlIHdvbid0IGhhdmUgdG8gbWFudWFsbHkgZG8gdGhhdCB1c2luZyBwcmVwIGFuZCBiYWtlLgoKCmBgYHtyIHRyYWlufQojIFRyYWluIHRoZSBtb2RlbAp3Zl9maXQgPC0gbG9nX3JlZ193ZiAlPiUgCiAgZml0KGRhdGEgPSBwdW1wa2luc190cmFpbikKCiMgUHJpbnQgdGhlIHRyYWluZWQgd29ya2Zsb3cKd2ZfZml0CmBgYAoKVGhlIG1vZGVsIHByaW50IG91dCBzaG93cyB0aGUgY29lZmZpY2llbnRzIGxlYXJuZWQgZHVyaW5nIHRyYWluaW5nLgoKTm93IHdlJ3ZlIHRyYWluZWQgdGhlIG1vZGVsIHVzaW5nIHRoZSB0cmFpbmluZyBkYXRhLCB3ZSBjYW4gbWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBkYXRhIHVzaW5nIFtwYXJzbmlwOjpwcmVkaWN0KCldKGh0dHBzOi8vcGFyc25pcC50aWR5bW9kZWxzLm9yZy9yZWZlcmVuY2UvcHJlZGljdC5tb2RlbF9maXQuaHRtbCkuIExldCdzIHN0YXJ0IGJ5IHVzaW5nIHRoZSBtb2RlbCB0byBwcmVkaWN0IGxhYmVscyBmb3Igb3VyIHRlc3Qgc2V0IGFuZCB0aGUgcHJvYmFiaWxpdGllcyBmb3IgZWFjaCBsYWJlbC4gV2hlbiB0aGUgcHJvYmFiaWxpdHkgaXMgbW9yZSB0aGFuIDAuNSwgdGhlIHByZWRpY3QgY2xhc3MgaXMgYFdISVRFYCBlbHNlIGBPUkFOR0VgLgoKYGBge3IgdGVzdF9wcmVkfQojIE1ha2UgcHJlZGljdGlvbnMgZm9yIGNvbG9yIGFuZCBjb3JyZXNwb25kaW5nIHByb2JhYmlsaXRpZXMKcmVzdWx0cyA8LSBwdW1wa2luc190ZXN0ICU+JSBzZWxlY3QoY29sb3IpICU+JSAKICBiaW5kX2NvbHMod2ZfZml0ICU+JSAKICAgICAgICAgICAgICBwcmVkaWN0KG5ld19kYXRhID0gcHVtcGtpbnNfdGVzdCkpICU+JQogIGJpbmRfY29scyh3Zl9maXQgJT4lCiAgICAgICAgICAgICAgcHJlZGljdChuZXdfZGF0YSA9IHB1bXBraW5zX3Rlc3QsIHR5cGUgPSAicHJvYiIpKQoKIyBDb21wYXJlIHByZWRpY3Rpb25zCnJlc3VsdHMgJT4lIAogIHNsaWNlX2hlYWQobiA9IDEwKQpgYGAKClZlcnkgbmljZSEgVGhpcyBwcm92aWRlcyBzb21lIG1vcmUgaW5zaWdodHMgaW50byBob3cgbG9naXN0aWMgcmVncmVzc2lvbiB3b3Jrcy4KCiMjIyBCZXR0ZXIgY29tcHJlaGVuc2lvbiB2aWEgYSBjb25mdXNpb24gbWF0cml4CgpDb21wYXJpbmcgZWFjaCBwcmVkaWN0aW9uIHdpdGggaXRzIGNvcnJlc3BvbmRpbmcgImdyb3VuZCB0cnV0aCIgYWN0dWFsIHZhbHVlIGlzbid0IGEgdmVyeSBlZmZpY2llbnQgd2F5IHRvIGRldGVybWluZSBob3cgd2VsbCB0aGUgbW9kZWwgaXMgcHJlZGljdGluZy4gRm9ydHVuYXRlbHksIFRpZHltb2RlbHMgaGFzIGEgZmV3IG1vcmUgdHJpY2tzIHVwIGl0cyBzbGVldmU6IFtgeWFyZHN0aWNrYF0oaHR0cHM6Ly95YXJkc3RpY2sudGlkeW1vZGVscy5vcmcvKSAtIGEgcGFja2FnZSB1c2VkIHRvIG1lYXN1cmUgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgbW9kZWxzIHVzaW5nIHBlcmZvcm1hbmNlIG1ldHJpY3MuCgpPbmUgcGVyZm9ybWFuY2UgbWV0cmljIGFzc29jaWF0ZWQgd2l0aCBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtcyBpcyB0aGUgW2Bjb25mdXNpb24gbWF0cml4YF0oaHR0cHM6Ly93aWtpcGVkaWEub3JnL3dpa2kvQ29uZnVzaW9uX21hdHJpeCkuIEEgY29uZnVzaW9uIG1hdHJpeCBkZXNjcmliZXMgaG93IHdlbGwgYSBjbGFzc2lmaWNhdGlvbiBtb2RlbCBwZXJmb3Jtcy4gQSBjb25mdXNpb24gbWF0cml4IHRhYnVsYXRlcyBob3cgbWFueSBleGFtcGxlcyBpbiBlYWNoIGNsYXNzIHdlcmUgY29ycmVjdGx5IGNsYXNzaWZpZWQgYnkgYSBtb2RlbC4gSW4gb3VyIGNhc2UsIGl0IHdpbGwgc2hvdyB5b3UgaG93IG1hbnkgb3JhbmdlIHB1bXBraW5zIHdlcmUgY2xhc3NpZmllZCBhcyBvcmFuZ2UgYW5kIGhvdyBtYW55IHdoaXRlIHB1bXBraW5zIHdlcmUgY2xhc3NpZmllZCBhcyB3aGl0ZTsgdGhlIGNvbmZ1c2lvbiBtYXRyaXggYWxzbyBzaG93cyB5b3UgaG93IG1hbnkgd2VyZSBjbGFzc2lmaWVkIGludG8gdGhlICoqd3JvbmcqKiBjYXRlZ29yaWVzLgoKVGhlIFsqKmBjb25mX21hdCgpYCoqXShodHRwczovL3RpZHltb2RlbHMuZ2l0aHViLmlvL3lhcmRzdGljay9yZWZlcmVuY2UvY29uZl9tYXQuaHRtbCkgZnVuY3Rpb24gZnJvbSB5YXJkc3RpY2sgY2FsY3VsYXRlcyB0aGlzIGNyb3NzLXRhYnVsYXRpb24gb2Ygb2JzZXJ2ZWQgYW5kIHByZWRpY3RlZCBjbGFzc2VzLgoKYGBge3IgY29uZl9tYXR9CiMgQ29uZnVzaW9uIG1hdHJpeCBmb3IgcHJlZGljdGlvbiByZXN1bHRzCmNvbmZfbWF0KGRhdGEgPSByZXN1bHRzLCB0cnV0aCA9IGNvbG9yLCBlc3RpbWF0ZSA9IC5wcmVkX2NsYXNzKQpgYGAKCkxldCdzIGludGVycHJldCB0aGUgY29uZnVzaW9uIG1hdHJpeC4gT3VyIG1vZGVsIGlzIGFza2VkIHRvIGNsYXNzaWZ5IHB1bXBraW5zIGJldHdlZW4gdHdvIGJpbmFyeSBjYXRlZ29yaWVzLCBjYXRlZ29yeSBgd2hpdGVgIGFuZCBjYXRlZ29yeSBgbm90LXdoaXRlYAoKLSAgIElmIHlvdXIgbW9kZWwgcHJlZGljdHMgYSBwdW1wa2luIGFzIHdoaXRlIGFuZCBpdCBiZWxvbmdzIHRvIGNhdGVnb3J5ICd3aGl0ZScgaW4gcmVhbGl0eSB3ZSBjYWxsIGl0IGEgYHRydWUgcG9zaXRpdmVgLCBzaG93biBieSB0aGUgdG9wIGxlZnQgbnVtYmVyLgoKLSAgIElmIHlvdXIgbW9kZWwgcHJlZGljdHMgYSBwdW1wa2luIGFzIG5vdCB3aGl0ZSBhbmQgaXQgYmVsb25ncyB0byBjYXRlZ29yeSAnd2hpdGUnIGluIHJlYWxpdHkgd2UgY2FsbCBpdCBhIGBmYWxzZSBuZWdhdGl2ZWAsIHNob3duIGJ5IHRoZSBib3R0b20gbGVmdCBudW1iZXIuCgotICAgSWYgeW91ciBtb2RlbCBwcmVkaWN0cyBhIHB1bXBraW4gYXMgd2hpdGUgYW5kIGl0IGJlbG9uZ3MgdG8gY2F0ZWdvcnkgJ25vdC13aGl0ZScgaW4gcmVhbGl0eSB3ZSBjYWxsIGl0IGEgYGZhbHNlIHBvc2l0aXZlYCwgc2hvd24gYnkgdGhlIHRvcCByaWdodCBudW1iZXIuCgotICAgSWYgeW91ciBtb2RlbCBwcmVkaWN0cyBhIHB1bXBraW4gYXMgbm90IHdoaXRlIGFuZCBpdCBiZWxvbmdzIHRvIGNhdGVnb3J5ICdub3Qtd2hpdGUnIGluIHJlYWxpdHkgd2UgY2FsbCBpdCBhIGB0cnVlIG5lZ2F0aXZlYCwgc2hvd24gYnkgdGhlIGJvdHRvbSByaWdodCBudW1iZXIuCgp8IFRydXRoIHwKfDotLS0tLTp8CgoKfCAgICAgICAgICAgICAgIHwgICAgICAgIHwgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tfC0tLS0tLS18CnwgKipQcmVkaWN0ZWQqKiB8IFdISVRFIHwgT1JBTkdFIHwKfCBXSElURSAgICAgICAgfCBUUCAgICAgfCBGUCAgICB8CnwgT1JBTkdFICAgICAgICAgfCBGTiAgICAgfCBUTiAgICB8CgpBcyB5b3UgbWlnaHQgaGF2ZSBndWVzc2VkIGl0J3MgcHJlZmVyYWJsZSB0byBoYXZlIGEgbGFyZ2VyIG51bWJlciBvZiB0cnVlIHBvc2l0aXZlcyBhbmQgdHJ1ZSBuZWdhdGl2ZXMgYW5kIGEgbG93ZXIgbnVtYmVyIG9mIGZhbHNlIHBvc2l0aXZlcyBhbmQgZmFsc2UgbmVnYXRpdmVzLCB3aGljaCBpbXBsaWVzIHRoYXQgdGhlIG1vZGVsIHBlcmZvcm1zIGJldHRlci4KClRoZSBjb25mdXNpb24gbWF0cml4IGlzIGhlbHBmdWwgc2luY2UgaXQgZ2l2ZXMgcmlzZSB0byBvdGhlciBtZXRyaWNzIHRoYXQgY2FuIGhlbHAgdXMgYmV0dGVyIGV2YWx1YXRlIHRoZSBwZXJmb3JtYW5jZSBvZiBhIGNsYXNzaWZpY2F0aW9uIG1vZGVsLiBMZXQncyBnbyB0aHJvdWdoIHNvbWUgb2YgdGhlbToKCvCfjpMgUHJlY2lzaW9uOiBgVFAvKFRQICsgRlApYCBkZWZpbmVkIGFzIHRoZSBwcm9wb3J0aW9uIG9mIHByZWRpY3RlZCBwb3NpdGl2ZXMgdGhhdCBhcmUgYWN0dWFsbHkgcG9zaXRpdmUuIEFsc28gY2FsbGVkIFtwb3NpdGl2ZSBwcmVkaWN0aXZlIHZhbHVlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Qb3NpdGl2ZV9wcmVkaWN0aXZlX3ZhbHVlICJQb3NpdGl2ZSBwcmVkaWN0aXZlIHZhbHVlIikKCvCfjpMgUmVjYWxsOiBgVFAvKFRQICsgRk4pYCBkZWZpbmVkIGFzIHRoZSBwcm9wb3J0aW9uIG9mIHBvc2l0aXZlIHJlc3VsdHMgb3V0IG9mIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyB3aGljaCB3ZXJlIGFjdHVhbGx5IHBvc2l0aXZlLiBBbHNvIGtub3duIGFzIGBzZW5zaXRpdml0eWAuCgrwn46TIFNwZWNpZmljaXR5OiBgVE4vKFROICsgRlApYCBkZWZpbmVkIGFzIHRoZSBwcm9wb3J0aW9uIG9mIG5lZ2F0aXZlIHJlc3VsdHMgb3V0IG9mIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyB3aGljaCB3ZXJlIGFjdHVhbGx5IG5lZ2F0aXZlLgoK8J+OkyBBY2N1cmFjeTogYFRQICsgVE4vKFRQICsgVE4gKyBGUCArIEZOKWAgVGhlIHBlcmNlbnRhZ2Ugb2YgbGFiZWxzIHByZWRpY3RlZCBhY2N1cmF0ZWx5IGZvciBhIHNhbXBsZS4KCvCfjpMgRiBNZWFzdXJlOiBBIHdlaWdodGVkIGF2ZXJhZ2Ugb2YgdGhlIHByZWNpc2lvbiBhbmQgcmVjYWxsLCB3aXRoIGJlc3QgYmVpbmcgMSBhbmQgd29yc3QgYmVpbmcgMC4KCkxldCdzIGNhbGN1bGF0ZSB0aGVzZSBtZXRyaWNzIQoKYGBge3IgbWV0cmljX3NldH0KIyBDb21iaW5lIG1ldHJpYyBmdW5jdGlvbnMgYW5kIGNhbGN1bGF0ZSB0aGVtIGFsbCBhdCBvbmNlCmV2YWxfbWV0cmljcyA8LSBtZXRyaWNfc2V0KHBwdiwgcmVjYWxsLCBzcGVjLCBmX21lYXMsIGFjY3VyYWN5KQpldmFsX21ldHJpY3MoZGF0YSA9IHJlc3VsdHMsIHRydXRoID0gY29sb3IsIGVzdGltYXRlID0gLnByZWRfY2xhc3MpCmBgYAoKIyMgVmlzdWFsaXplIHRoZSBST0MgY3VydmUgb2YgdGhpcyBtb2RlbAoKTGV0J3MgZG8gb25lIG1vcmUgdmlzdWFsaXphdGlvbiB0byBzZWUgdGhlIHNvLWNhbGxlZCBbYFJPQyBjdXJ2ZWBdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1JlY2VpdmVyX29wZXJhdGluZ19jaGFyYWN0ZXJpc3RpYyk6CgpgYGB7ciByb2NfY3VydmV9CiMgTWFrZSBhIHJvY19jdXJ2ZQpyZXN1bHRzICU+JSAKICByb2NfY3VydmUoY29sb3IsIC5wcmVkX09SQU5HRSkgJT4lIAogIGF1dG9wbG90KCkKYGBgCgpST0MgY3VydmVzIGFyZSBvZnRlbiB1c2VkIHRvIGdldCBhIHZpZXcgb2YgdGhlIG91dHB1dCBvZiBhIGNsYXNzaWZpZXIgaW4gdGVybXMgb2YgaXRzIHRydWUgdnMuIGZhbHNlIHBvc2l0aXZlcy4gUk9DIGN1cnZlcyB0eXBpY2FsbHkgZmVhdHVyZSBgVHJ1ZSBQb3NpdGl2ZSBSYXRlYC9TZW5zaXRpdml0eSBvbiB0aGUgWSBheGlzLCBhbmQgYEZhbHNlIFBvc2l0aXZlIFJhdGVgLzEtU3BlY2lmaWNpdHkgb24gdGhlIFggYXhpcy4gVGh1cywgdGhlIHN0ZWVwbmVzcyBvZiB0aGUgY3VydmUgYW5kIHRoZSBzcGFjZSBiZXR3ZWVuIHRoZSBtaWRwb2ludCBsaW5lIGFuZCB0aGUgY3VydmUgbWF0dGVyOiB5b3Ugd2FudCBhIGN1cnZlIHRoYXQgcXVpY2tseSBoZWFkcyB1cCBhbmQgb3ZlciB0aGUgbGluZS4gSW4gb3VyIGNhc2UsIHRoZXJlIGFyZSBmYWxzZSBwb3NpdGl2ZXMgdG8gc3RhcnQgd2l0aCwgYW5kIHRoZW4gdGhlIGxpbmUgaGVhZHMgdXAgYW5kIG92ZXIgcHJvcGVybHkuCgpGaW5hbGx5LCBsZXQncyB1c2UgYHlhcmRzdGljazo6cm9jX2F1YygpYCB0byBjYWxjdWxhdGUgdGhlIGFjdHVhbCBBcmVhIFVuZGVyIHRoZSBDdXJ2ZS4gT25lIHdheSBvZiBpbnRlcnByZXRpbmcgQVVDIGlzIGFzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBtb2RlbCByYW5rcyBhIHJhbmRvbSBwb3NpdGl2ZSBleGFtcGxlIG1vcmUgaGlnaGx5IHRoYW4gYSByYW5kb20gbmVnYXRpdmUgZXhhbXBsZS4KCmBgYHtyIHJvY19hb2N9CiMgQ2FsY3VsYXRlIGFyZWEgdW5kZXIgY3VydmUKcmVzdWx0cyAlPiUgCiAgcm9jX2F1Yyhjb2xvciwgLnByZWRfT1JBTkdFKQpgYGAKClRoZSByZXN1bHQgaXMgYXJvdW5kIGAwLjk3NWAuIEdpdmVuIHRoYXQgdGhlIEFVQyByYW5nZXMgZnJvbSAwIHRvIDEsIHlvdSB3YW50IGEgYmlnIHNjb3JlLCBzaW5jZSBhIG1vZGVsIHRoYXQgaXMgMTAwJSBjb3JyZWN0IGluIGl0cyBwcmVkaWN0aW9ucyB3aWxsIGhhdmUgYW4gQVVDIG9mIDE7IGluIHRoaXMgY2FzZSwgdGhlIG1vZGVsIGlzICpwcmV0dHkgZ29vZCouCgpJbiBmdXR1cmUgbGVzc29ucyBvbiBjbGFzc2lmaWNhdGlvbnMsIHlvdSB3aWxsIGxlYXJuIGhvdyB0byBpbXByb3ZlIHlvdXIgbW9kZWwncyBzY29yZXMgKHN1Y2ggYXMgZGVhbGluZyB3aXRoIGltYmFsYW5jZWQgZGF0YSBpbiB0aGlzIGNhc2UpLgoKIyMg8J+agENoYWxsZW5nZQoKVGhlcmUncyBhIGxvdCBtb3JlIHRvIHVucGFjayByZWdhcmRpbmcgbG9naXN0aWMgcmVncmVzc2lvbiEgQnV0IHRoZSBiZXN0IHdheSB0byBsZWFybiBpcyB0byBleHBlcmltZW50LiBGaW5kIGEgZGF0YXNldCB0aGF0IGxlbmRzIGl0c2VsZiB0byB0aGlzIHR5cGUgb2YgYW5hbHlzaXMgYW5kIGJ1aWxkIGEgbW9kZWwgd2l0aCBpdC4gV2hhdCBkbyB5b3UgbGVhcm4/IHRpcDogdHJ5IFtLYWdnbGVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vc2VhcmNoP3E9bG9naXN0aWMrcmVncmVzc2lvbitkYXRhc2V0cykgZm9yIGludGVyZXN0aW5nIGRhdGFzZXRzLgoKIyMgUmV2aWV3ICYgU2VsZiBTdHVkeQoKUmVhZCB0aGUgZmlyc3QgZmV3IHBhZ2VzIG9mIFt0aGlzIHBhcGVyIGZyb20gU3RhbmZvcmRdKGh0dHBzOi8vd2ViLnN0YW5mb3JkLmVkdS9+anVyYWZza3kvc2xwMy81LnBkZikgb24gc29tZSBwcmFjdGljYWwgdXNlcyBmb3IgbG9naXN0aWMgcmVncmVzc2lvbi4gVGhpbmsgYWJvdXQgdGFza3MgdGhhdCBhcmUgYmV0dGVyIHN1aXRlZCBmb3Igb25lIG9yIHRoZSBvdGhlciB0eXBlIG9mIHJlZ3Jlc3Npb24gdGFza3MgdGhhdCB3ZSBoYXZlIHN0dWRpZWQgdXAgdG8gdGhpcyBwb2ludC4gV2hhdCB3b3VsZCB3b3JrIGJlc3Q/Cgo=