Learning Objectives


Upon completing today’s lab activity, students should be able to do the following using R and RStudio:

  1. Use the dplyr package to efficiently subset data frames.

  2. Quantifying the strength of a linear relationship by computing the correlation coefficient of a given data.

  3. Fitting a linear regression model into a given data.

  4. Interpreting and visualizing regression models.

  5. Quantifying errors through the computation and visualization of residuals.


Introduction to dplyr


Data is often messy and difficult to organize or manage. Typically you may need to do the following on your data set:

  • Constraining your options to help manage your data and figure out the things you need/want.

  • Easy and intuitive R commands to filter and subset your data.

  • Efficient and fast subsetting when dealing with large-scale data.

The dplyr package makes the above tasks easier. Note that this package is part of the tidyverse package.


Example Data

Throughout this section, we will use the starwars data set, which is available through the dplyr package.

First, install the dplyr package if you have not done it already.

install.packages("dplyr")

Now, we can begin.

library(dplyr)
starwars
## # A tibble: 87 × 14
##    name    height  mass hair_color  skin_color eye_color birth_year sex   gender
##    <chr>    <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Luke S…    172    77 blond       fair       blue            19   male  mascu…
##  2 C-3PO      167    75 <NA>        gold       yellow         112   none  mascu…
##  3 R2-D2       96    32 <NA>        white, bl… red             33   none  mascu…
##  4 Darth …    202   136 none        white      yellow          41.9 male  mascu…
##  5 Leia O…    150    49 brown       light      brown           19   fema… femin…
##  6 Owen L…    178   120 brown, grey light      blue            52   male  mascu…
##  7 Beru W…    165    75 brown       light      blue            47   fema… femin…
##  8 R5-D4       97    32 <NA>        white, red red             NA   none  mascu…
##  9 Biggs …    183    84 black       light      brown           24   male  mascu…
## 10 Obi-Wa…    182    77 auburn, wh… fair       blue-gray       57   male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>

Notice that the printed data set above is a data frame with 87 rows and 14 columns. In tidyverse terminologies, this is called a “tibble”, a modern reimagining of the data frame. Similar to using the glimpse, function a tibble data frame automatically print a summary shown as a table like above. You can learn more about tibbles at https://tibble.tidyverse.org; in particular you can convert data frames to tibbles with as_tibble().

Verbs

In dplyr, the functions used to subset data are called verbs. Below is a list of verbs and there descriptions.

  • Rows:
    • filter() chooses rows based on column values.
    • slice() chooses rows based on location.
    • arrange() changes the order of the rows.
  • Columns:
    • select() changes whether or not a column is included.
    • rename() changes the name of columns.
    • mutate() changes the values of columns and creates new columns.
    • relocate() changes the order of the columns.
  • Groups of rows:
    • summarise() collapses a group into a single row.

Operating on Rows


  • Filter rows with filter()
sub_fil <- starwars %>% filter(skin_color == "brown", eye_color == "brown")
sub_fil
## # A tibble: 2 × 14
##   name      height  mass hair_color skin_color eye_color birth_year sex   gender
##   <chr>      <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
## 1 Wicket S…     88    20 brown      brown      brown              8 male  mascu…
## 2 Eeth Koth    171    NA black      brown      brown             NA male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>
#Equivalent R base codes
sub_fil_0 <- starwars[starwars$skin_color == "brown" & starwars$eye_color == "brown", ]
sub_fil_1 <- subset(starwars, skin_color == "brown" & eye_color == "brown")


  • Arrange rows with arrange()
sub_arr_asc <- starwars %>% arrange(height, mass) # ascending order
sub_arr_asc
## # A tibble: 87 × 14
##    name     height  mass hair_color skin_color eye_color birth_year sex   gender
##    <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
##  1 Yoda         66    17 white      green      brown            896 male  mascu…
##  2 Ratts T…     79    15 none       grey, blue unknown           NA male  mascu…
##  3 Wicket …     88    20 brown      brown      brown              8 male  mascu…
##  4 Dud Bolt     94    45 none       blue, grey yellow            NA male  mascu…
##  5 R2-D2        96    32 <NA>       white, bl… red               33 none  mascu…
##  6 R4-P17       96    NA none       silver, r… red, blue         NA none  femin…
##  7 R5-D4        97    32 <NA>       white, red red               NA none  mascu…
##  8 Sebulba     112    40 none       grey, red  orange            NA male  mascu…
##  9 Gasgano     122    NA none       white, bl… black             NA male  mascu…
## 10 Watto       137    NA black      blue, grey yellow            NA male  mascu…
## # … with 77 more rows, and 5 more variables: homeworld <chr>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>
# Use desc() to order a column in descending order
sub_arr_dsc <- starwars %>% arrange(desc(height)) # descending order
sub_arr_dsc


  • Choosing rows using slice()
# choose rows with indices 5 to 10
starwars %>% slice(5:10) 
## # A tibble: 6 × 14
##   name     height  mass hair_color  skin_color eye_color birth_year sex   gender
##   <chr>     <int> <dbl> <chr>       <chr>      <chr>          <dbl> <chr> <chr> 
## 1 Leia Or…    150    49 brown       light      brown             19 fema… femin…
## 2 Owen La…    178   120 brown, grey light      blue              52 male  mascu…
## 3 Beru Wh…    165    75 brown       light      blue              47 fema… femin…
## 4 R5-D4        97    32 <NA>        white, red red               NA none  mascu…
## 5 Biggs D…    183    84 black       light      brown             24 male  mascu…
## 6 Obi-Wan…    182    77 auburn, wh… fair       blue-gray         57 male  mascu…
## # … with 5 more variables: homeworld <chr>, species <chr>, films <list>,
## #   vehicles <list>, starships <list>
# choose the first 3 rows
starwars %>% slice_head(n = 3)
# choose the last 3 rows
starwars %>% slice_tail(n = 3)
# randonmly sample 5 rows
starwars %>% slice_sample(n = 5)
# randonmly sample 5 rows
starwars %>% slice_sample(prop = 0.1)


Operating on Columns


  • Selecting columns with select()
starwars %>% select(hair_color, skin_color, eye_color) # select columns by name
## # A tibble: 87 × 3
##    hair_color    skin_color  eye_color
##    <chr>         <chr>       <chr>    
##  1 blond         fair        blue     
##  2 <NA>          gold        yellow   
##  3 <NA>          white, blue red      
##  4 none          white       yellow   
##  5 brown         light       brown    
##  6 brown, grey   light       blue     
##  7 brown         light       blue     
##  8 <NA>          white, red  red      
##  9 black         light       brown    
## 10 auburn, white fair        blue-gray
## # … with 77 more rows
# Select all columns between hair_color and eye_color (inclusive)
starwars %>% select(hair_color:eye_color)
# Select all columns except those from hair_color to eye_color (inclusive)
starwars %>% select(!(hair_color:eye_color))
# Select all columns ending with color
starwars %>% select(ends_with("color"))


  • Add new columns with mutate()


# add a new column using the hight column devided by 100
starwars %>%
  mutate(height_m = height / 100) %>%
  select(height_m, height, everything()) # to see the columns
## # A tibble: 87 × 15
##    height_m height name    mass hair_color skin_color eye_color birth_year sex  
##       <dbl>  <int> <chr>  <dbl> <chr>      <chr>      <chr>          <dbl> <chr>
##  1     1.72    172 Luke …    77 blond      fair       blue            19   male 
##  2     1.67    167 C-3PO     75 <NA>       gold       yellow         112   none 
##  3     0.96     96 R2-D2     32 <NA>       white, bl… red             33   none 
##  4     2.02    202 Darth…   136 none       white      yellow          41.9 male 
##  5     1.5     150 Leia …    49 brown      light      brown           19   fema…
##  6     1.78    178 Owen …   120 brown, gr… light      blue            52   male 
##  7     1.65    165 Beru …    75 brown      light      blue            47   fema…
##  8     0.97     97 R5-D4     32 <NA>       white, red red             NA   none 
##  9     1.83    183 Biggs…    84 black      light      brown           24   male 
## 10     1.82    182 Obi-W…    77 auburn, w… fair       blue-gray       57   male 
## # … with 77 more rows, and 6 more variables: gender <chr>, homeworld <chr>,
## #   species <chr>, films <list>, vehicles <list>, starships <list>
starwars %>%
  mutate(
    height_m = height / 100,
    BMI = mass / (height_m^2)
  ) %>%
  select(BMI, everything())
# for keeping new variables/columns
starwars %>%
  transmute(
    height_m = height / 100,
    BMI = mass / (height_m^2)
  )


  • Changing column order with relocate()


starwars %>% relocate(sex:homeworld, .before = height)
## # A tibble: 87 × 14
##    name     sex    gender homeworld height  mass hair_color skin_color eye_color
##    <chr>    <chr>  <chr>  <chr>      <int> <dbl> <chr>      <chr>      <chr>    
##  1 Luke Sk… male   mascu… Tatooine     172    77 blond      fair       blue     
##  2 C-3PO    none   mascu… Tatooine     167    75 <NA>       gold       yellow   
##  3 R2-D2    none   mascu… Naboo         96    32 <NA>       white, bl… red      
##  4 Darth V… male   mascu… Tatooine     202   136 none       white      yellow   
##  5 Leia Or… female femin… Alderaan     150    49 brown      light      brown    
##  6 Owen La… male   mascu… Tatooine     178   120 brown, gr… light      blue     
##  7 Beru Wh… female femin… Tatooine     165    75 brown      light      blue     
##  8 R5-D4    none   mascu… Tatooine      97    32 <NA>       white, red red      
##  9 Biggs D… male   mascu… Tatooine     183    84 black      light      brown    
## 10 Obi-Wan… male   mascu… Stewjon      182    77 auburn, w… fair       blue-gray
## # … with 77 more rows, and 5 more variables: birth_year <dbl>, species <chr>,
## #   films <list>, vehicles <list>, starships <list>


  • Summarise values with summarise()
starwars %>% summarise(height = mean(height, na.rm = TRUE))


Combining functions with %>%


starwars %>%
  group_by(sex) %>%
  select(height, mass) %>%
  summarise(
    height = mean(height, na.rm = TRUE),
    mass = mean(mass, na.rm = TRUE)
  )
## Adding missing grouping variables: `sex`
## # A tibble: 5 × 3
##   sex            height   mass
##   <chr>           <dbl>  <dbl>
## 1 female           169.   54.7
## 2 hermaphroditic   175  1358  
## 3 male             179.   81.0
## 4 none             131.   69.8
## 5 <NA>             181.   48

Other Resources

If you want to explore more on using the dplyr package, you can visit the following resources.


Simple Linear Regression (SLR)


Throughout this section we are using the iris data set, which is part of the base R installation.

glimpse(iris)
## Rows: 150
## Columns: 5
## $ Sepal.Length <dbl> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, 5.4, 4.…
## $ Sepal.Width  <dbl> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.…
## $ Petal.Length <dbl> 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.…
## $ Petal.Width  <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.…
## $ Species      <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa, s…


Correlations

First, let’s look at scatter plots of selected variables.

pairs(data = iris, ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width,
      main = "Edgar Anderson's Iris Data",
      pch = 21, 
      bg = c("#1b9e77", "#d95f02", "#7570b3")[unclass(iris$Species)],
      oma=c(3,3,3,15))
par(xpd = TRUE)
legend("bottomright", fill = unique(iris$Species), legend = c( levels(iris$Species)))

We can look at all of the correlations for all relevant variables and save it as a data frame. Note that when we compute the correlations, we ignore the species groupings for now and use all data points.

sub_4 <- iris %>% select(Sepal.Length,Sepal.Width,Petal.Length,Petal.Width)
cor_4 <- data.frame(cor(sub_4))
cor_4
##              Sepal.Length Sepal.Width Petal.Length Petal.Width
## Sepal.Length    1.0000000  -0.1175698    0.8717538   0.8179411
## Sepal.Width    -0.1175698   1.0000000   -0.4284401  -0.3661259
## Petal.Length    0.8717538  -0.4284401    1.0000000   0.9628654
## Petal.Width     0.8179411  -0.3661259    0.9628654   1.0000000

The formula for computing correlation is given by

\[r = \frac{\sum_{i=1}^n{(x_i - \bar{x})(y_i - \bar{y})}}{\sqrt{\sum_{i=1}^n (x_i - \bar{x})^2 \sum_{i=1}^n (y_i - \bar{y})^2}} \]

where the terms \(\bar{x}\) and \(\bar{y}\) are the mean of the \(x\) and \(y\) variables. The mean is computed as

\[ \bar{x} = \frac{\sum_{i=1}^n{x_i}}{n} \]

and

\[ \bar{y} = \frac{\sum_{i=1}^n{y_i}}{n} \]

where \(n\) is the number of observations.

Notice that the results from the previous code snippet show that the the \(r_{Petal.Length,Petal.Width} = 0.9628\) is equal to \(r_{Petal.Width,Petal.Length} = 0.9628\). This means that the correlation equation is symmetric if you switch the \(x\) and \(y\) variables. R computes correlation using this formula behind the scenes.

Note that the correlation coefficient is very sensitive to outliers.

Linear Model

First, let’s take a look at the scatter plot of the Petal.Width vs Petal.Length of the iris data set without coloring by species.

library(ggplot2)
scatter <- ggplot(data=iris, aes(x = Petal.Width, y = Petal.Length)) 
scatter + geom_point(size = 1.5) + # scatter plot
  xlab("Petal Width") +  ylab("Petal Length") + # x and y label
  ggtitle("Edgar Anderson's Iris Data")

Now, we want create a linear model that predicts Petal.Length using Petal.Width as the explanatory variable. The response variable is the Petal.Length. We can write the linear model as follows.

\[\hat{y} = b_0 + b_1 x\]

where \(\hat{y}\) is the response variable (predicted Petal.Length) and \(x\) is the explanatory variable (Petal.Width as predictor). The term \(e\) is the error. Below is a code snippet using the lm function to compute the best fit linear model given the data. That is finding the sample statistics \(b_0\) and \(b_1\) that best fits the data.

lm_mod <- lm(Petal.Length ~ Petal.Width, data = iris)
summary(lm_mod) # details of the model
## 
## Call:
## lm(formula = Petal.Length ~ Petal.Width, data = iris)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.33542 -0.30347 -0.02955  0.25776  1.39453 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.08356    0.07297   14.85   <2e-16 ***
## Petal.Width  2.22994    0.05140   43.39   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4782 on 148 degrees of freedom
## Multiple R-squared:  0.9271, Adjusted R-squared:  0.9266 
## F-statistic:  1882 on 1 and 148 DF,  p-value: < 2.2e-16
lm_mod <- lm(Petal.Length ~ Petal.Width, data = iris)
print(lm_mod) # less details of the model
## 
## Call:
## lm(formula = Petal.Length ~ Petal.Width, data = iris)
## 
## Coefficients:
## (Intercept)  Petal.Width  
##       1.084        2.230

We can write the linear model using the estimated values of \(b_0\) and \(b_1\).

\[\widehat{Petal.Length} = 1.083558 + 2.230 * Petal.Width \]

Now, we can make predictions/estimates using the model.

new.df <- data.frame(Petal.Width=c(0.50, 1, 2)) # using three example widths
predictions_0 <- new.df %>%  mutate(Petal.Length.hat = predict(lm_mod, new.df))
print(predictions_0)
##   Petal.Width Petal.Length.hat
## 1         0.5         2.198528
## 2         1.0         3.313499
## 3         2.0         5.543439
new.df <- data.frame(Petal.Width=iris$Petal.Width) # using the actual widths
predictions_1 <- new.df %>%  mutate(Petal.Length.hat = predict(lm_mod, new.df))
glimpse(predictions_1)
## Rows: 150
## Columns: 2
## $ Petal.Width      <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2…
## $ Petal.Length.hat <dbl> 1.529546, 1.529546, 1.529546, 1.529546, 1.529546, 1.9…

Now, we can make a line to show the linear model into the scatterplot.

library(ggplot2)
scatter <- ggplot(data=iris, aes(x = Petal.Width, y = Petal.Length)) 
scatter + geom_point(size = 1.5) + # scatter plot
  geom_abline(aes(slope = coef(lm_mod)[[2]], intercept = coef(lm_mod)[[1]], colour='Best Fit Linear Model'), size=1) + 
  labs(colour="") +
  xlab("Petal Width") +  ylab("Petal Length") + # x and y label
  ggtitle("Edgar Anderson's Iris Data")

Residuals

The residual for the \(i^{th}\) observation are computed as follows.

\[ e_i = y_i - \hat{y_i} \]

where \(y_i\) is the actual observation and \(\hat{y_i}\) is the estimated/predicted outcome using the linear model.

R can automatically compute the residuals using the resid function.

lm_res <- resid(lm_mod)
lm_res <- data.frame(resid = lm_res)
lm_res <- lm_res %>% mutate(Petal.Width = iris$Petal.Width)

Now, we can make plots of the residuals.

library(ggplot2)
histogram <- ggplot(data=lm_res, aes(x=resid))
histogram + geom_histogram(binwidth=0.2, color="black") + 
  xlab("Residuals") +  ylab("Frequency") + ggtitle("Histogram of Residuals")

library(ggplot2)
scatter <- ggplot(data=lm_res, aes(x = Petal.Width, y =resid))
scatter + geom_point(size = 1.5) + # scatter plot
  xlab("Petal Width") +  ylab("Residual") + # x and y label +
  geom_hline(yintercept=0, linetype='dashed', col = 'red') +
  ggtitle("Petal Width vs Residuals")

The histogram shows a nearly normal residual distribution while the residual scatter plot shows the variation of the residuals vs the explanatory variable.

Next, we can compute the residual sum of squares (rss) or the sum of squared errors (sse).

\[ rss = \sum_{i=1}^n{(y_i - \hat{y}_i)^2} \]

# residual sum of squares (rss) or the sum of squared errors (sse)
sse <- lm_res %>% transmute(resid^2)
sse <- sum(sse)

The sse gives you Left-over variability in the \(y\) values if we know \(x\). It tells you how well does your model fit the actual data. The residual sum of squares is used to help you decide if a statistical model is a good fit for your data. Normally you would compare sse values among linear models. The lower the sse, the better.

Next, we can compute the total sum of squares (tss).

\[ sst = \sum_{i=1}^n{(y_i - \bar{y})^2} \]

sst <- iris %>% transmute((Petal.Length - mean(Petal.Length))^2)
sst <- sum(sst)

Finally, using the sse and sst, we can compute the coefficient of determination.

\[ R^2 = \frac{sst - sse}{sst} = 1 - \frac{sse}{sst} \]

The \(R^2\) value tells you the amount (or percent) of variation explained of you model fit into the data. It is a ratio of a measure of variability around the line divided by a measure of total variability. It tells you how well the model fits. the higher the \(R^2\), the better. This value falls between 0 and 1.

R_squared <- 1 - (sse/sst)
R_squared
## [1] 0.9271098


Lab Exercises


I. Linear Regression on Iris Flowers

  1. Load the iris dataset. Note that this dataset is in the datasets package which is already included in the base R installation. Use dplyr to make subsets (Species: setosa, versicolor, and virginica) of data using variables Sepal.Length and Petal.Length. You should have three subsets. One for each species.

  2. For each species find the best fit linear model to predict Petal.Length using Sepal.Length as the predictor. You should have three Linear models. One for each Species. Include writing the linear model equations with their corresponding sample statistic (intercept and slope). For example, write the equations for \(\hat{y}_{setosa}\), \(\hat{y}_{versicolor}\), and \(\hat{y}_{virginica}\). Interpret the slopes of the models for each species and compare them with each other.

  3. Using your fitted models. Produce a scatterplot which contains the data points colored according to Species. Add a line for each linear model for each species on the same scatterplot. Make sure to put legends and labels correctly.

  4. For each subset you made in problem 1. Compute the correlations. Describe the correlations in this context. Does each correlation reflect what’s on the scatterplot and compare them with each species?

II. Linear Regression on Housing Prices

  1. Load the duke_forest data, which is available using the openintro package. Below is a table of the description of the variables of the duke_forest data set. For each variable, indentify whether they are numerical (discrete or continuous) or categorical (nominal or ordinal).
Variables and their descriptions for the duke_forest dataset.
Variable Description
price Sale price, in USD
bed Number of bedrooms
bath Number of bathrooms
area Area of home, in square feet
year_built Year the home was built
cooling Cooling system: central or other
lot Area of the entire property, in acres
  1. Produce a paired scatterplots using the pairs function in R. You must use the numerical variables.

  2. Compute the correlations of the bed, bath, area, year_built, and lot variables with the price variable. Which explanatory variable has the highest correlation? Does it reflect a consistent pattern in the scatter plot?

  3. Let the response variable (or the outcome) be the price variable. Create a linear model with a predictor chosen with the best coefficient of determination with the price variable.

  4. Produce a scatterplot and residual plot for the linear model with the best coefficient of determination in problem 4. Interpret the slopes and explain the results according to this context. Based on the residuals, identify any outliers and explain why we need to resort to a more complicated model.

LS0tCnRpdGxlOiAiMiAtIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiBhbmQgYGRwbHlyYCIKYXV0aG9yOiAiQWxleCBKb2huIFF1aWphbm8iCmRhdGU6ICIwOS8xNC8yMDIxIgpvdXRwdXQ6IG9wZW5pbnRybzo6bGFiX3JlcG9ydAotLS0KCiMjICoqTGVhcm5pbmcgT2JqZWN0aXZlcyoqCgo8YnI+CgpVcG9uIGNvbXBsZXRpbmcgdG9kYXkncyBsYWIgYWN0aXZpdHksIHN0dWRlbnRzIHNob3VsZCBiZSBhYmxlIHRvIGRvIHRoZSBmb2xsb3dpbmcgdXNpbmcgUiBhbmQgUlN0dWRpbzoKCiAgMS4gVXNlIHRoZSBkcGx5ciBwYWNrYWdlIHRvIGVmZmljaWVudGx5IHN1YnNldCBkYXRhIGZyYW1lcy4KICAKICAyLiBRdWFudGlmeWluZyB0aGUgc3RyZW5ndGggb2YgYSBsaW5lYXIgcmVsYXRpb25zaGlwIGJ5IGNvbXB1dGluZyB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgb2YgYSBnaXZlbiBkYXRhLgogIAogIDMuIEZpdHRpbmcgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBpbnRvIGEgZ2l2ZW4gZGF0YS4KICAKICA0LiBJbnRlcnByZXRpbmcgYW5kIHZpc3VhbGl6aW5nIHJlZ3Jlc3Npb24gbW9kZWxzLgogIAogIDUuIFF1YW50aWZ5aW5nIGVycm9ycyB0aHJvdWdoIHRoZSBjb21wdXRhdGlvbiBhbmQgdmlzdWFsaXphdGlvbiBvZiByZXNpZHVhbHMuCiAgCjxicj4KCiMjICoqSW50cm9kdWN0aW9uIHRvIGBkcGx5cmAqKgoKPGJyPgoKRGF0YSBpcyBvZnRlbiBtZXNzeSBhbmQgZGlmZmljdWx0IHRvIG9yZ2FuaXplIG9yIG1hbmFnZS4gVHlwaWNhbGx5IHlvdSBtYXkgbmVlZCB0byBkbyB0aGUgZm9sbG93aW5nIG9uIHlvdXIgZGF0YSBzZXQ6CgogICogQ29uc3RyYWluaW5nIHlvdXIgb3B0aW9ucyB0byBoZWxwIG1hbmFnZSB5b3VyIGRhdGEgYW5kIGZpZ3VyZSBvdXQgdGhlIHRoaW5ncyB5b3UgbmVlZC93YW50LgogIAogICogRWFzeSBhbmQgaW50dWl0aXZlIFIgY29tbWFuZHMgdG8gZmlsdGVyIGFuZCBzdWJzZXQgeW91ciBkYXRhLgogIAogICogRWZmaWNpZW50IGFuZCBmYXN0IHN1YnNldHRpbmcgd2hlbiBkZWFsaW5nIHdpdGggbGFyZ2Utc2NhbGUgZGF0YS4KICAKVGhlIGBkcGx5cmAgcGFja2FnZSBtYWtlcyB0aGUgYWJvdmUgdGFza3MgZWFzaWVyLiBOb3RlIHRoYXQgdGhpcyBwYWNrYWdlIGlzIHBhcnQgb2YgdGhlIGB0aWR5dmVyc2VgIHBhY2thZ2UuCgo8YnI+CgojIyMgRXhhbXBsZSBEYXRhCgpUaHJvdWdob3V0IHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCB1c2UgdGhlIFtgc3RhcndhcnNgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3N0YXJ3YXJzLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gZGF0YSBzZXQsIHdoaWNoIGlzIGF2YWlsYWJsZSB0aHJvdWdoIHRoZSBgZHBseXJgIHBhY2thZ2UuCgpGaXJzdCwgaW5zdGFsbCB0aGUgYGRwbHlyYCBwYWNrYWdlIGlmIHlvdSBoYXZlIG5vdCBkb25lIGl0IGFscmVhZHkuCgpgYGAKaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQpgYGAKCk5vdywgd2UgY2FuIGJlZ2luLgoKYGBge3IgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpzdGFyd2FycwpgYGAKTm90aWNlIHRoYXQgdGhlIHByaW50ZWQgZGF0YSBzZXQgYWJvdmUgaXMgYSBkYXRhIGZyYW1lIHdpdGggODcgcm93cyBhbmQgMTQgY29sdW1ucy4gSW4gdGlkeXZlcnNlIHRlcm1pbm9sb2dpZXMsIHRoaXMgaXMgY2FsbGVkIGEgInRpYmJsZSIsIGEgbW9kZXJuIHJlaW1hZ2luaW5nIG9mIHRoZSBkYXRhIGZyYW1lLiBTaW1pbGFyIHRvIHVzaW5nIHRoZSBgZ2xpbXBzZWAsIGZ1bmN0aW9uIGEgdGliYmxlIGRhdGEgZnJhbWUgYXV0b21hdGljYWxseSBwcmludCBhIHN1bW1hcnkgc2hvd24gYXMgYSB0YWJsZSBsaWtlIGFib3ZlLiBZb3UgY2FuIGxlYXJuIG1vcmUgYWJvdXQgdGliYmxlcyBhdCBodHRwczovL3RpYmJsZS50aWR5dmVyc2Uub3JnOyBpbiBwYXJ0aWN1bGFyIHlvdSBjYW4gY29udmVydCBkYXRhIGZyYW1lcyB0byB0aWJibGVzIHdpdGggYGFzX3RpYmJsZSgpYC4KCiMjIyBWZXJicwoKSW4gYGRwbHlyYCwgdGhlIGZ1bmN0aW9ucyB1c2VkIHRvIHN1YnNldCBkYXRhIGFyZSBjYWxsZWQgdmVyYnMuIEJlbG93IGlzIGEgbGlzdCBvZiB2ZXJicyBhbmQgdGhlcmUgZGVzY3JpcHRpb25zLgoKICAqIFJvd3M6CiAgICAtIGBmaWx0ZXIoKWAgY2hvb3NlcyByb3dzIGJhc2VkIG9uIGNvbHVtbiB2YWx1ZXMuCiAgICAtIGBzbGljZSgpYCBjaG9vc2VzIHJvd3MgYmFzZWQgb24gbG9jYXRpb24uCiAgICAtIGBhcnJhbmdlKClgIGNoYW5nZXMgdGhlIG9yZGVyIG9mIHRoZSByb3dzLgogICAgCiAgKiBDb2x1bW5zOgogICAgLSBgc2VsZWN0KClgIGNoYW5nZXMgd2hldGhlciBvciBub3QgYSBjb2x1bW4gaXMgaW5jbHVkZWQuCiAgICAtIGByZW5hbWUoKWAgY2hhbmdlcyB0aGUgbmFtZSBvZiBjb2x1bW5zLgogICAgLSBgbXV0YXRlKClgIGNoYW5nZXMgdGhlIHZhbHVlcyBvZiBjb2x1bW5zIGFuZCBjcmVhdGVzIG5ldyBjb2x1bW5zLgogICAgLSBgcmVsb2NhdGUoKWAgY2hhbmdlcyB0aGUgb3JkZXIgb2YgdGhlIGNvbHVtbnMuCgogICogR3JvdXBzIG9mIHJvd3M6CiAgICAtIGBzdW1tYXJpc2UoKWAgY29sbGFwc2VzIGEgZ3JvdXAgaW50byBhIHNpbmdsZSByb3cuCgojIyMgT3BlcmF0aW5nIG9uIFJvd3MKCjxicj4KCiAgKiBGaWx0ZXIgcm93cyB3aXRoICBgZmlsdGVyKClgCgpgYGB7cn0Kc3ViX2ZpbCA8LSBzdGFyd2FycyAlPiUgZmlsdGVyKHNraW5fY29sb3IgPT0gImJyb3duIiwgZXllX2NvbG9yID09ICJicm93biIpCnN1Yl9maWwKYGBgCgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KI0VxdWl2YWxlbnQgUiBiYXNlIGNvZGVzCnN1Yl9maWxfMCA8LSBzdGFyd2Fyc1tzdGFyd2FycyRza2luX2NvbG9yID09ICJicm93biIgJiBzdGFyd2FycyRleWVfY29sb3IgPT0gImJyb3duIiwgXQpzdWJfZmlsXzEgPC0gc3Vic2V0KHN0YXJ3YXJzLCBza2luX2NvbG9yID09ICJicm93biIgJiBleWVfY29sb3IgPT0gImJyb3duIikKYGBgCgo8YnI+CgogICogQXJyYW5nZSByb3dzIHdpdGggYGFycmFuZ2UoKWAKICAKYGBge3J9CnN1Yl9hcnJfYXNjIDwtIHN0YXJ3YXJzICU+JSBhcnJhbmdlKGhlaWdodCwgbWFzcykgIyBhc2NlbmRpbmcgb3JkZXIKc3ViX2Fycl9hc2MKYGBgCgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBVc2UgZGVzYygpIHRvIG9yZGVyIGEgY29sdW1uIGluIGRlc2NlbmRpbmcgb3JkZXIKc3ViX2Fycl9kc2MgPC0gc3RhcndhcnMgJT4lIGFycmFuZ2UoZGVzYyhoZWlnaHQpKSAjIGRlc2NlbmRpbmcgb3JkZXIKc3ViX2Fycl9kc2MKYGBgCgo8YnI+CgogICogQ2hvb3Npbmcgcm93cyB1c2luZyBgc2xpY2UoKWAKCmBgYHtyfQojIGNob29zZSByb3dzIHdpdGggaW5kaWNlcyA1IHRvIDEwCnN0YXJ3YXJzICU+JSBzbGljZSg1OjEwKSAKYGBgCgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBjaG9vc2UgdGhlIGZpcnN0IDMgcm93cwpzdGFyd2FycyAlPiUgc2xpY2VfaGVhZChuID0gMykKYGBgCgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KIyBjaG9vc2UgdGhlIGxhc3QgMyByb3dzCnN0YXJ3YXJzICU+JSBzbGljZV90YWlsKG4gPSAzKQpgYGAKCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIHJhbmRvbm1seSBzYW1wbGUgNSByb3dzCnN0YXJ3YXJzICU+JSBzbGljZV9zYW1wbGUobiA9IDUpCmBgYAoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgcmFuZG9ubWx5IHNhbXBsZSA1IHJvd3MKc3RhcndhcnMgJT4lIHNsaWNlX3NhbXBsZShwcm9wID0gMC4xKQpgYGAKCjxicj4KCiMjIyBPcGVyYXRpbmcgb24gQ29sdW1ucwoKPGJyPgoKICAqIFNlbGVjdGluZyBjb2x1bW5zIHdpdGggYHNlbGVjdCgpYAogCmBgYHtyfQpzdGFyd2FycyAlPiUgc2VsZWN0KGhhaXJfY29sb3IsIHNraW5fY29sb3IsIGV5ZV9jb2xvcikgIyBzZWxlY3QgY29sdW1ucyBieSBuYW1lCmBgYAoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgU2VsZWN0IGFsbCBjb2x1bW5zIGJldHdlZW4gaGFpcl9jb2xvciBhbmQgZXllX2NvbG9yIChpbmNsdXNpdmUpCnN0YXJ3YXJzICU+JSBzZWxlY3QoaGFpcl9jb2xvcjpleWVfY29sb3IpCmBgYAoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgU2VsZWN0IGFsbCBjb2x1bW5zIGV4Y2VwdCB0aG9zZSBmcm9tIGhhaXJfY29sb3IgdG8gZXllX2NvbG9yIChpbmNsdXNpdmUpCnN0YXJ3YXJzICU+JSBzZWxlY3QoIShoYWlyX2NvbG9yOmV5ZV9jb2xvcikpCmBgYAoKYGBge3IgcmVzdWx0cz0naGlkZSd9CiMgU2VsZWN0IGFsbCBjb2x1bW5zIGVuZGluZyB3aXRoIGNvbG9yCnN0YXJ3YXJzICU+JSBzZWxlY3QoZW5kc193aXRoKCJjb2xvciIpKQpgYGAKCjxicj4KCiAgKiBBZGQgbmV3IGNvbHVtbnMgd2l0aCBgbXV0YXRlKClgCiAgCjxicj4KCmBgYHtyfQojIGFkZCBhIG5ldyBjb2x1bW4gdXNpbmcgdGhlIGhpZ2h0IGNvbHVtbiBkZXZpZGVkIGJ5IDEwMApzdGFyd2FycyAlPiUKICBtdXRhdGUoaGVpZ2h0X20gPSBoZWlnaHQgLyAxMDApICU+JQogIHNlbGVjdChoZWlnaHRfbSwgaGVpZ2h0LCBldmVyeXRoaW5nKCkpICMgdG8gc2VlIHRoZSBjb2x1bW5zCmBgYAoKYGBge3IgcmVzdWx0cz0naGlkZSd9CnN0YXJ3YXJzICU+JQogIG11dGF0ZSgKICAgIGhlaWdodF9tID0gaGVpZ2h0IC8gMTAwLAogICAgQk1JID0gbWFzcyAvIChoZWlnaHRfbV4yKQogICkgJT4lCiAgc2VsZWN0KEJNSSwgZXZlcnl0aGluZygpKQpgYGAKCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIGZvciBrZWVwaW5nIG5ldyB2YXJpYWJsZXMvY29sdW1ucwpzdGFyd2FycyAlPiUKICB0cmFuc211dGUoCiAgICBoZWlnaHRfbSA9IGhlaWdodCAvIDEwMCwKICAgIEJNSSA9IG1hc3MgLyAoaGVpZ2h0X21eMikKICApCmBgYAoKPGJyPgoKICAqIENoYW5naW5nIGNvbHVtbiBvcmRlciB3aXRoIGByZWxvY2F0ZSgpYAogIAo8YnI+CgpgYGB7cn0Kc3RhcndhcnMgJT4lIHJlbG9jYXRlKHNleDpob21ld29ybGQsIC5iZWZvcmUgPSBoZWlnaHQpCmBgYAoKPGJyPgoKICAqIFN1bW1hcmlzZSB2YWx1ZXMgd2l0aCBgc3VtbWFyaXNlKClgCgpgYGB7ciByZXN1bHRzPSdoaWRlJ30Kc3RhcndhcnMgJT4lIHN1bW1hcmlzZShoZWlnaHQgPSBtZWFuKGhlaWdodCwgbmEucm0gPSBUUlVFKSkKYGBgCgo8YnI+CgojIyMgQ29tYmluaW5nIGZ1bmN0aW9ucyB3aXRoICU+JQoKPGJyPgoKYGBge3J9CnN0YXJ3YXJzICU+JQogIGdyb3VwX2J5KHNleCkgJT4lCiAgc2VsZWN0KGhlaWdodCwgbWFzcykgJT4lCiAgc3VtbWFyaXNlKAogICAgaGVpZ2h0ID0gbWVhbihoZWlnaHQsIG5hLnJtID0gVFJVRSksCiAgICBtYXNzID0gbWVhbihtYXNzLCBuYS5ybSA9IFRSVUUpCiAgKQpgYGAKCiMjIyBPdGhlciBSZXNvdXJjZXMKCklmIHlvdSB3YW50IHRvIGV4cGxvcmUgbW9yZSBvbiB1c2luZyB0aGUgYGRwbHlyYCBwYWNrYWdlLCB5b3UgY2FuIHZpc2l0IHRoZSBmb2xsb3dpbmcgcmVzb3VyY2VzLgoKICAqIFtJbnRyb2R1Y3Rpb24gdG8gYGRwbHlyYCBieSBjcmFuLnItcHJvamVjdF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2RwbHlyL3ZpZ25ldHRlcy9kcGx5ci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9CiAgCiAgKiBbSW50cm9kdWN0aW9uIHRvIGBkcGx5cmAgYnkgZGF0YWNhbXBdKGh0dHBzOi8vY2FtcHVzLmRhdGFjYW1wLmNvbS9jb3Vyc2VzL3Jib290Y2FtcC9pbnRyb2R1Y3Rpb24tdG8tZHBseXI/ZXg9MSl7dGFyZ2V0PSJfYmxhbmsifQogIAogICogW0ludHJvZHVjdGlvbiB0byBgZHBseXJgIGJ5IHItYmxvZ2dlcnNdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLzIwMjAvMDYvaW50cm9kdWN0aW9uLXRvLWRwbHlyLyl7dGFyZ2V0PSJfYmxhbmsifQoKICAqIFtgZHBseXJgOiBwYXJ0IG9mIGB0aWR5dmVyc2VgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9Cgo8YnI+CgojIyAqKlNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiAoU0xSKSoqCgo8YnI+CgpUaHJvdWdob3V0IHRoaXMgc2VjdGlvbiB3ZSBhcmUgdXNpbmcgdGhlIGBpcmlzYCBkYXRhIHNldCwgd2hpY2ggaXMgcGFydCBvZiB0aGUgYmFzZSBSIGluc3RhbGxhdGlvbi4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmdsaW1wc2UoaXJpcykKYGBgCgo8YnI+CgojIyMgQ29ycmVsYXRpb25zCgpGaXJzdCwgbGV0J3MgbG9vayBhdCBzY2F0dGVyIHBsb3RzIG9mIHNlbGVjdGVkIHZhcmlhYmxlcy4KCmBgYHtyfQpwYWlycyhkYXRhID0gaXJpcywgfiBTZXBhbC5MZW5ndGggKyBTZXBhbC5XaWR0aCArIFBldGFsLkxlbmd0aCArIFBldGFsLldpZHRoLAogICAgICBtYWluID0gIkVkZ2FyIEFuZGVyc29uJ3MgSXJpcyBEYXRhIiwKICAgICAgcGNoID0gMjEsIAogICAgICBiZyA9IGMoIiMxYjllNzciLCAiI2Q5NWYwMiIsICIjNzU3MGIzIilbdW5jbGFzcyhpcmlzJFNwZWNpZXMpXSwKICAgICAgb21hPWMoMywzLDMsMTUpKQpwYXIoeHBkID0gVFJVRSkKbGVnZW5kKCJib3R0b21yaWdodCIsIGZpbGwgPSB1bmlxdWUoaXJpcyRTcGVjaWVzKSwgbGVnZW5kID0gYyggbGV2ZWxzKGlyaXMkU3BlY2llcykpKQpgYGAKCldlIGNhbiBsb29rIGF0IGFsbCBvZiB0aGUgY29ycmVsYXRpb25zIGZvciBhbGwgcmVsZXZhbnQgdmFyaWFibGVzIGFuZCBzYXZlIGl0IGFzIGEgZGF0YSBmcmFtZS4gTm90ZSB0aGF0IHdoZW4gd2UgY29tcHV0ZSB0aGUgY29ycmVsYXRpb25zLCB3ZSBpZ25vcmUgdGhlIHNwZWNpZXMgZ3JvdXBpbmdzIGZvciBub3cgYW5kIHVzZSBhbGwgZGF0YSBwb2ludHMuCgpgYGB7cn0Kc3ViXzQgPC0gaXJpcyAlPiUgc2VsZWN0KFNlcGFsLkxlbmd0aCxTZXBhbC5XaWR0aCxQZXRhbC5MZW5ndGgsUGV0YWwuV2lkdGgpCmNvcl80IDwtIGRhdGEuZnJhbWUoY29yKHN1Yl80KSkKY29yXzQKYGBgCgpUaGUgZm9ybXVsYSBmb3IgY29tcHV0aW5nIGNvcnJlbGF0aW9uIGlzIGdpdmVuIGJ5CgpcW3IgPSBcZnJhY3tcc3VtX3tpPTF9Xm57KHhfaSAtIFxiYXJ7eH0pKHlfaSAtIFxiYXJ7eX0pfX17XHNxcnR7XHN1bV97aT0xfV5uICh4X2kgLSBcYmFye3h9KV4yIFxzdW1fe2k9MX1ebiAoeV9pIC0gXGJhcnt5fSleMn19IFxdCgp3aGVyZSB0aGUgdGVybXMgJFxiYXJ7eH0kIGFuZCAkXGJhcnt5fSQgYXJlIHRoZSBtZWFuIG9mIHRoZSAkeCQgYW5kICR5JCB2YXJpYWJsZXMuIFRoZSBtZWFuIGlzIGNvbXB1dGVkIGFzCgpcWyBcYmFye3h9ID0gXGZyYWN7XHN1bV97aT0xfV5ue3hfaX19e259IFxdCgphbmQKClxbIFxiYXJ7eX0gPSBcZnJhY3tcc3VtX3tpPTF9Xm57eV9pfX17bn0gXF0KCndoZXJlICRuJCBpcyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucy4KCk5vdGljZSB0aGF0IHRoZSByZXN1bHRzIGZyb20gdGhlIHByZXZpb3VzIGNvZGUgc25pcHBldCBzaG93IHRoYXQgdGhlIHRoZSAkcl97UGV0YWwuTGVuZ3RoLFBldGFsLldpZHRofSA9IDAuOTYyOCQgaXMgZXF1YWwgdG8gJHJfe1BldGFsLldpZHRoLFBldGFsLkxlbmd0aH0gPSAwLjk2MjgkLiBUaGlzIG1lYW5zIHRoYXQgdGhlIGNvcnJlbGF0aW9uIGVxdWF0aW9uIGlzIHN5bW1ldHJpYyBpZiB5b3Ugc3dpdGNoIHRoZSAkeCQgYW5kICR5JCB2YXJpYWJsZXMuIFIgY29tcHV0ZXMgY29ycmVsYXRpb24gdXNpbmcgdGhpcyBmb3JtdWxhIGJlaGluZCB0aGUgc2NlbmVzLgoKTm90ZSB0aGF0IHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBpcyB2ZXJ5IHNlbnNpdGl2ZSB0byBvdXRsaWVycy4KCiMjIyBMaW5lYXIgTW9kZWwKCkZpcnN0LCBsZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgc2NhdHRlciBwbG90IG9mIHRoZSBgUGV0YWwuV2lkdGhgIHZzIGBQZXRhbC5MZW5ndGhgIG9mIHRoZSBgaXJpc2AgZGF0YSBzZXQgd2l0aG91dCBjb2xvcmluZyBieSBzcGVjaWVzLgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKc2NhdHRlciA8LSBnZ3Bsb3QoZGF0YT1pcmlzLCBhZXMoeCA9IFBldGFsLldpZHRoLCB5ID0gUGV0YWwuTGVuZ3RoKSkgCnNjYXR0ZXIgKyBnZW9tX3BvaW50KHNpemUgPSAxLjUpICsgIyBzY2F0dGVyIHBsb3QKICB4bGFiKCJQZXRhbCBXaWR0aCIpICsgIHlsYWIoIlBldGFsIExlbmd0aCIpICsgIyB4IGFuZCB5IGxhYmVsCiAgZ2d0aXRsZSgiRWRnYXIgQW5kZXJzb24ncyBJcmlzIERhdGEiKQpgYGAKCk5vdywgd2Ugd2FudCBjcmVhdGUgYSBsaW5lYXIgbW9kZWwgdGhhdCBwcmVkaWN0cyBgUGV0YWwuTGVuZ3RoYCB1c2luZyBgUGV0YWwuV2lkdGhgIGFzIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZS4gVGhlIHJlc3BvbnNlIHZhcmlhYmxlIGlzIHRoZSBgUGV0YWwuTGVuZ3RoYC4gV2UgY2FuIHdyaXRlIHRoZSBsaW5lYXIgbW9kZWwgYXMgZm9sbG93cy4KClxbXGhhdHt5fSA9IGJfMCArIGJfMSB4XF0KCndoZXJlICRcaGF0e3l9JCBpcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUgKHByZWRpY3RlZCBgUGV0YWwuTGVuZ3RoYCkgYW5kICR4JCBpcyB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUgKGBQZXRhbC5XaWR0aGAgYXMgcHJlZGljdG9yKS4gVGhlIHRlcm0gJGUkIGlzIHRoZSBlcnJvci4gQmVsb3cgaXMgYSBjb2RlIHNuaXBwZXQgdXNpbmcgdGhlIGBsbWAgZnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgYmVzdCBmaXQgbGluZWFyIG1vZGVsIGdpdmVuIHRoZSBkYXRhLiBUaGF0IGlzIGZpbmRpbmcgdGhlIHNhbXBsZSBzdGF0aXN0aWNzICRiXzAkIGFuZCAkYl8xJCB0aGF0IGJlc3QgZml0cyB0aGUgZGF0YS4KCmBgYHtyfQpsbV9tb2QgPC0gbG0oUGV0YWwuTGVuZ3RoIH4gUGV0YWwuV2lkdGgsIGRhdGEgPSBpcmlzKQpzdW1tYXJ5KGxtX21vZCkgIyBkZXRhaWxzIG9mIHRoZSBtb2RlbApgYGAKCmBgYHtyfQpsbV9tb2QgPC0gbG0oUGV0YWwuTGVuZ3RoIH4gUGV0YWwuV2lkdGgsIGRhdGEgPSBpcmlzKQpwcmludChsbV9tb2QpICMgbGVzcyBkZXRhaWxzIG9mIHRoZSBtb2RlbApgYGAKCldlIGNhbiB3cml0ZSB0aGUgbGluZWFyIG1vZGVsIHVzaW5nIHRoZSBlc3RpbWF0ZWQgdmFsdWVzIG9mICRiXzAkIGFuZCAkYl8xJC4KClxbXHdpZGVoYXR7UGV0YWwuTGVuZ3RofSA9IDEuMDgzNTU4ICsgMi4yMzAgKiBQZXRhbC5XaWR0aCBcXQoKTm93LCB3ZSBjYW4gbWFrZSBwcmVkaWN0aW9ucy9lc3RpbWF0ZXMgdXNpbmcgdGhlIG1vZGVsLgoKYGBge3J9Cm5ldy5kZiA8LSBkYXRhLmZyYW1lKFBldGFsLldpZHRoPWMoMC41MCwgMSwgMikpICMgdXNpbmcgdGhyZWUgZXhhbXBsZSB3aWR0aHMKcHJlZGljdGlvbnNfMCA8LSBuZXcuZGYgJT4lICBtdXRhdGUoUGV0YWwuTGVuZ3RoLmhhdCA9IHByZWRpY3QobG1fbW9kLCBuZXcuZGYpKQpwcmludChwcmVkaWN0aW9uc18wKQpgYGAKCmBgYHtyfQpuZXcuZGYgPC0gZGF0YS5mcmFtZShQZXRhbC5XaWR0aD1pcmlzJFBldGFsLldpZHRoKSAjIHVzaW5nIHRoZSBhY3R1YWwgd2lkdGhzCnByZWRpY3Rpb25zXzEgPC0gbmV3LmRmICU+JSAgbXV0YXRlKFBldGFsLkxlbmd0aC5oYXQgPSBwcmVkaWN0KGxtX21vZCwgbmV3LmRmKSkKZ2xpbXBzZShwcmVkaWN0aW9uc18xKQpgYGAKCk5vdywgd2UgY2FuIG1ha2UgYSBsaW5lIHRvIHNob3cgdGhlIGxpbmVhciBtb2RlbCBpbnRvIHRoZSBzY2F0dGVycGxvdC4KCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCnNjYXR0ZXIgPC0gZ2dwbG90KGRhdGE9aXJpcywgYWVzKHggPSBQZXRhbC5XaWR0aCwgeSA9IFBldGFsLkxlbmd0aCkpIApzY2F0dGVyICsgZ2VvbV9wb2ludChzaXplID0gMS41KSArICMgc2NhdHRlciBwbG90CiAgZ2VvbV9hYmxpbmUoYWVzKHNsb3BlID0gY29lZihsbV9tb2QpW1syXV0sIGludGVyY2VwdCA9IGNvZWYobG1fbW9kKVtbMV1dLCBjb2xvdXI9J0Jlc3QgRml0IExpbmVhciBNb2RlbCcpLCBzaXplPTEpICsgCiAgbGFicyhjb2xvdXI9IiIpICsKICB4bGFiKCJQZXRhbCBXaWR0aCIpICsgIHlsYWIoIlBldGFsIExlbmd0aCIpICsgIyB4IGFuZCB5IGxhYmVsCiAgZ2d0aXRsZSgiRWRnYXIgQW5kZXJzb24ncyBJcmlzIERhdGEiKQpgYGAKCiMjIyBSZXNpZHVhbHMKClRoZSByZXNpZHVhbCBmb3IgdGhlICRpXnt0aH0kIG9ic2VydmF0aW9uIGFyZSBjb21wdXRlZCBhcyBmb2xsb3dzLgoKXFsgZV9pID0geV9pIC0gXGhhdHt5X2l9ICBcXQoKd2hlcmUgJHlfaSQgaXMgdGhlIGFjdHVhbCBvYnNlcnZhdGlvbiBhbmQgJFxoYXR7eV9pfSQgaXMgdGhlIGVzdGltYXRlZC9wcmVkaWN0ZWQgb3V0Y29tZSB1c2luZyB0aGUgbGluZWFyIG1vZGVsLgoKUiBjYW4gYXV0b21hdGljYWxseSBjb21wdXRlIHRoZSByZXNpZHVhbHMgdXNpbmcgdGhlIGByZXNpZGAgZnVuY3Rpb24uCgpgYGB7cn0KbG1fcmVzIDwtIHJlc2lkKGxtX21vZCkKbG1fcmVzIDwtIGRhdGEuZnJhbWUocmVzaWQgPSBsbV9yZXMpCmxtX3JlcyA8LSBsbV9yZXMgJT4lIG11dGF0ZShQZXRhbC5XaWR0aCA9IGlyaXMkUGV0YWwuV2lkdGgpCmBgYAoKTm93LCB3ZSBjYW4gbWFrZSBwbG90cyBvZiB0aGUgcmVzaWR1YWxzLgoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKaGlzdG9ncmFtIDwtIGdncGxvdChkYXRhPWxtX3JlcywgYWVzKHg9cmVzaWQpKQpoaXN0b2dyYW0gKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD0wLjIsIGNvbG9yPSJibGFjayIpICsgCiAgeGxhYigiUmVzaWR1YWxzIikgKyAgeWxhYigiRnJlcXVlbmN5IikgKyBnZ3RpdGxlKCJIaXN0b2dyYW0gb2YgUmVzaWR1YWxzIikKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpzY2F0dGVyIDwtIGdncGxvdChkYXRhPWxtX3JlcywgYWVzKHggPSBQZXRhbC5XaWR0aCwgeSA9cmVzaWQpKQpzY2F0dGVyICsgZ2VvbV9wb2ludChzaXplID0gMS41KSArICMgc2NhdHRlciBwbG90CiAgeGxhYigiUGV0YWwgV2lkdGgiKSArICB5bGFiKCJSZXNpZHVhbCIpICsgIyB4IGFuZCB5IGxhYmVsICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MCwgbGluZXR5cGU9J2Rhc2hlZCcsIGNvbCA9ICdyZWQnKSArCiAgZ2d0aXRsZSgiUGV0YWwgV2lkdGggdnMgUmVzaWR1YWxzIikKYGBgCgpUaGUgaGlzdG9ncmFtIHNob3dzIGEgbmVhcmx5IG5vcm1hbCByZXNpZHVhbCBkaXN0cmlidXRpb24gd2hpbGUgdGhlIHJlc2lkdWFsIHNjYXR0ZXIgcGxvdCBzaG93cyB0aGUgdmFyaWF0aW9uIG9mIHRoZSByZXNpZHVhbHMgdnMgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlLgoKTmV4dCwgd2UgY2FuIGNvbXB1dGUgdGhlIHJlc2lkdWFsIHN1bSBvZiBzcXVhcmVzIChyc3MpIG9yIHRoZSBzdW0gb2Ygc3F1YXJlZCBlcnJvcnMgKHNzZSkuCgpcWyByc3MgPSBcc3VtX3tpPTF9Xm57KHlfaSAtIFxoYXR7eX1faSleMn0gXF0KCmBgYHtyfQojIHJlc2lkdWFsIHN1bSBvZiBzcXVhcmVzIChyc3MpIG9yIHRoZSBzdW0gb2Ygc3F1YXJlZCBlcnJvcnMgKHNzZSkKc3NlIDwtIGxtX3JlcyAlPiUgdHJhbnNtdXRlKHJlc2lkXjIpCnNzZSA8LSBzdW0oc3NlKQpgYGAKClRoZSBzc2UgZ2l2ZXMgeW91IExlZnQtb3ZlciB2YXJpYWJpbGl0eSBpbiB0aGUgJHkkIHZhbHVlcyBpZiB3ZSBrbm93ICR4JC4gSXQgdGVsbHMgeW91IGhvdyB3ZWxsIGRvZXMgeW91ciBtb2RlbCBmaXQgdGhlIGFjdHVhbCBkYXRhLiBUaGUgcmVzaWR1YWwgc3VtIG9mIHNxdWFyZXMgaXMgdXNlZCB0byBoZWxwIHlvdSBkZWNpZGUgaWYgYSBzdGF0aXN0aWNhbCBtb2RlbCBpcyBhIGdvb2QgZml0IGZvciB5b3VyIGRhdGEuIE5vcm1hbGx5IHlvdSB3b3VsZCBjb21wYXJlIHNzZSB2YWx1ZXMgYW1vbmcgbGluZWFyIG1vZGVscy4gVGhlIGxvd2VyIHRoZSBzc2UsIHRoZSBiZXR0ZXIuCgpOZXh0LCB3ZSBjYW4gY29tcHV0ZSB0aGUgdG90YWwgc3VtIG9mIHNxdWFyZXMgKHRzcykuCgpcWyBzc3QgPSBcc3VtX3tpPTF9Xm57KHlfaSAtIFxiYXJ7eX0pXjJ9IFxdCgpgYGB7cn0Kc3N0IDwtIGlyaXMgJT4lIHRyYW5zbXV0ZSgoUGV0YWwuTGVuZ3RoIC0gbWVhbihQZXRhbC5MZW5ndGgpKV4yKQpzc3QgPC0gc3VtKHNzdCkKYGBgCgpGaW5hbGx5LCB1c2luZyB0aGUgc3NlIGFuZCBzc3QsIHdlIGNhbiBjb21wdXRlIHRoZSBjb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uLgoKXFsgUl4yID0gXGZyYWN7c3N0IC0gc3NlfXtzc3R9ID0gMSAtIFxmcmFje3NzZX17c3N0fSBcXQoKVGhlICRSXjIkIHZhbHVlIHRlbGxzIHlvdSB0aGUgYW1vdW50IChvciBwZXJjZW50KSBvZiB2YXJpYXRpb24gZXhwbGFpbmVkIG9mIHlvdSBtb2RlbCBmaXQgaW50byB0aGUgZGF0YS4gSXQgaXMgYSByYXRpbyBvZiBhIG1lYXN1cmUgb2YgdmFyaWFiaWxpdHkgYXJvdW5kIHRoZSBsaW5lIGRpdmlkZWQgYnkgYSBtZWFzdXJlIG9mIHRvdGFsIHZhcmlhYmlsaXR5LiBJdCB0ZWxscyB5b3UgaG93IHdlbGwgdGhlIG1vZGVsIGZpdHMuIHRoZSBoaWdoZXIgdGhlICRSXjIkLCB0aGUgYmV0dGVyLiBUaGlzIHZhbHVlIGZhbGxzIGJldHdlZW4gMCBhbmQgMS4KCmBgYHtyfQpSX3NxdWFyZWQgPC0gMSAtIChzc2Uvc3N0KQpSX3NxdWFyZWQKYGBgCgo8YnI+CgojIyAqKkxhYiBFeGVyY2lzZXMqKgoKPGJyPgoKIyMjIEkuIExpbmVhciBSZWdyZXNzaW9uIG9uIElyaXMgRmxvd2VycwoKICAxLiBMb2FkIHRoZSBgaXJpc2AgZGF0YXNldC4gTm90ZSB0aGF0IHRoaXMgZGF0YXNldCBpcyBpbiB0aGUgYGRhdGFzZXRzYCBwYWNrYWdlIHdoaWNoIGlzIGFscmVhZHkgaW5jbHVkZWQgaW4gdGhlIGJhc2UgUiBpbnN0YWxsYXRpb24uIFVzZSBgZHBseXJgIHRvIG1ha2Ugc3Vic2V0cyAoU3BlY2llczogc2V0b3NhLCB2ZXJzaWNvbG9yLCBhbmQgdmlyZ2luaWNhKSBvZiBkYXRhIHVzaW5nIHZhcmlhYmxlcyBgU2VwYWwuTGVuZ3RoYCBhbmQgYFBldGFsLkxlbmd0aGAuIFlvdSBzaG91bGQgaGF2ZSB0aHJlZSBzdWJzZXRzLiBPbmUgZm9yIGVhY2ggc3BlY2llcy4KICAKICAyLiBGb3IgZWFjaCBzcGVjaWVzIGZpbmQgdGhlIGJlc3QgZml0IGxpbmVhciBtb2RlbCB0byBwcmVkaWN0IGBQZXRhbC5MZW5ndGhgIHVzaW5nIGBTZXBhbC5MZW5ndGhgIGFzIHRoZSBwcmVkaWN0b3IuIFlvdSBzaG91bGQgaGF2ZSB0aHJlZSBMaW5lYXIgbW9kZWxzLiBPbmUgZm9yIGVhY2ggU3BlY2llcy4gSW5jbHVkZSB3cml0aW5nIHRoZSBsaW5lYXIgbW9kZWwgZXF1YXRpb25zIHdpdGggdGhlaXIgY29ycmVzcG9uZGluZyBzYW1wbGUgc3RhdGlzdGljIChpbnRlcmNlcHQgYW5kIHNsb3BlKS4gRm9yIGV4YW1wbGUsIHdyaXRlIHRoZSBlcXVhdGlvbnMgZm9yICRcaGF0e3l9X3tzZXRvc2F9JCwgJFxoYXR7eX1fe3ZlcnNpY29sb3J9JCwgYW5kICRcaGF0e3l9X3t2aXJnaW5pY2F9JC4gSW50ZXJwcmV0IHRoZSBzbG9wZXMgb2YgdGhlIG1vZGVscyBmb3IgZWFjaCBzcGVjaWVzIGFuZCBjb21wYXJlIHRoZW0gd2l0aCBlYWNoIG90aGVyLgogIAogIDMuIFVzaW5nIHlvdXIgZml0dGVkIG1vZGVscy4gUHJvZHVjZSBhIHNjYXR0ZXJwbG90IHdoaWNoIGNvbnRhaW5zIHRoZSBkYXRhIHBvaW50cyBjb2xvcmVkIGFjY29yZGluZyB0byBTcGVjaWVzLiBBZGQgYSBsaW5lIGZvciBlYWNoIGxpbmVhciBtb2RlbCBmb3IgZWFjaCBzcGVjaWVzIG9uIHRoZSBzYW1lIHNjYXR0ZXJwbG90LiBNYWtlIHN1cmUgdG8gcHV0IGxlZ2VuZHMgYW5kIGxhYmVscyBjb3JyZWN0bHkuCiAgCiAgNC4gRm9yIGVhY2ggc3Vic2V0IHlvdSBtYWRlIGluIHByb2JsZW0gMS4gQ29tcHV0ZSB0aGUgY29ycmVsYXRpb25zLiBEZXNjcmliZSB0aGUgY29ycmVsYXRpb25zIGluIHRoaXMgY29udGV4dC4gRG9lcyBlYWNoIGNvcnJlbGF0aW9uIHJlZmxlY3Qgd2hhdCdzIG9uIHRoZSBzY2F0dGVycGxvdCBhbmQgY29tcGFyZSB0aGVtIHdpdGggZWFjaCBzcGVjaWVzPwoKIyMjIElJLiBMaW5lYXIgUmVncmVzc2lvbiBvbiBIb3VzaW5nIFByaWNlcwoKICAxLiBMb2FkIHRoZSBbYGR1a2VfZm9yZXN0YF0oaHR0cHM6Ly93d3cub3BlbmludHJvLm9yZy9kYXRhL2luZGV4LnBocD9kYXRhPWR1a2VfZm9yZXN0KXt0YXJnZXQ9Il9ibGFuayJ9IGRhdGEsIHdoaWNoIGlzIGF2YWlsYWJsZSB1c2luZyB0aGUgYG9wZW5pbnRyb2AgcGFja2FnZS4gQmVsb3cgaXMgYSB0YWJsZSBvZiB0aGUgZGVzY3JpcHRpb24gb2YgdGhlIHZhcmlhYmxlcyBvZiB0aGUgYGR1a2VfZm9yZXN0YCBkYXRhIHNldC4gRm9yIGVhY2ggdmFyaWFibGUsIGluZGVudGlmeSB3aGV0aGVyIHRoZXkgYXJlIG51bWVyaWNhbCAoZGlzY3JldGUgb3IgY29udGludW91cykgb3IgY2F0ZWdvcmljYWwgKG5vbWluYWwgb3Igb3JkaW5hbCkuCiAgCmBgYHtyIGR1a2UtdmFyaWFibGVzLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpkdWtlX2ZvcmVzdF92YXJfZGVmIDwtIHRyaWJibGUoCiAgfnZhcmlhYmxlLCB+ZGVzY3JpcHRpb24sCiAgInByaWNlIiwgIlNhbGUgcHJpY2UsIGluIFVTRCIsCiAgImJlZCIsICJOdW1iZXIgb2YgYmVkcm9vbXMiLAogICJiYXRoIiwgIk51bWJlciBvZiBiYXRocm9vbXMiLAogICJhcmVhIiwgIkFyZWEgb2YgaG9tZSwgaW4gc3F1YXJlIGZlZXQiLAogICJ5ZWFyX2J1aWx0IiwgIlllYXIgdGhlIGhvbWUgd2FzIGJ1aWx0IiwKICAiY29vbGluZyIsICJDb29saW5nIHN5c3RlbTogY2VudHJhbCBvciBvdGhlciIsCiAgImxvdCIsICJBcmVhIG9mIHRoZSBlbnRpcmUgcHJvcGVydHksIGluIGFjcmVzIgopCgpsaWJyYXJ5KGthYmxlRXh0cmEpCmR1a2VfZm9yZXN0X3Zhcl9kZWYgJT4lCiAga2JsKGxpbmVzZXAgPSAiIiwgYm9va3RhYnMgPSBUUlVFLCBjYXB0aW9uID0gIlZhcmlhYmxlcyBhbmQgdGhlaXIgZGVzY3JpcHRpb25zIGZvciB0aGUgYGR1a2VfZm9yZXN0YCBkYXRhc2V0LiIsCiAgICAgIGNvbC5uYW1lcyA9IGMoIlZhcmlhYmxlIiwgIkRlc2NyaXB0aW9uIikpICU+JQogIGthYmxlX3N0eWxpbmcoCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiY29uZGVuc2VkIiksCiAgICBsYXRleF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob2xkX3Bvc2l0aW9uIiksCiAgICBmdWxsX3dpZHRoID0gRkFMU0UKICApICU+JQogIGNvbHVtbl9zcGVjKDEsIHdpZHRoID0gIjE1ZW0iLCBtb25vc3BhY2UgPSBUUlVFKSAlPiUKICBjb2x1bW5fc3BlYygyLCB3aWR0aCA9ICIyNWVtIikKYGBgCgogIDIuIFByb2R1Y2UgYSBwYWlyZWQgc2NhdHRlcnBsb3RzIHVzaW5nIHRoZSBgcGFpcnNgIGZ1bmN0aW9uIGluIFIuIFlvdSBtdXN0IHVzZSB0aGUgbnVtZXJpY2FsIHZhcmlhYmxlcy4KCiAgMy4gQ29tcHV0ZSB0aGUgY29ycmVsYXRpb25zIG9mIHRoZSBgYmVkYCwgYGJhdGhgLCBgYXJlYWAsIGB5ZWFyX2J1aWx0YCwgYW5kIGBsb3RgIHZhcmlhYmxlcyB3aXRoIHRoZSBgcHJpY2VgIHZhcmlhYmxlLiBXaGljaCBleHBsYW5hdG9yeSB2YXJpYWJsZSBoYXMgdGhlIGhpZ2hlc3QgY29ycmVsYXRpb24/IERvZXMgaXQgcmVmbGVjdCBhIGNvbnNpc3RlbnQgcGF0dGVybiBpbiB0aGUgc2NhdHRlciBwbG90PwoKICA0LiBMZXQgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIChvciB0aGUgb3V0Y29tZSkgYmUgdGhlIGBwcmljZWAgdmFyaWFibGUuIENyZWF0ZSBhIGxpbmVhciBtb2RlbCB3aXRoIGEgcHJlZGljdG9yIGNob3NlbiB3aXRoIHRoZSBiZXN0IGNvZWZmaWNpZW50IG9mIGRldGVybWluYXRpb24gd2l0aCB0aGUgYHByaWNlYCB2YXJpYWJsZS4KICAKICA1LiBQcm9kdWNlIGEgc2NhdHRlcnBsb3QgYW5kIHJlc2lkdWFsIHBsb3QgZm9yIHRoZSBsaW5lYXIgbW9kZWwgd2l0aCB0aGUgYmVzdCBjb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uIGluIHByb2JsZW0gNC4gSW50ZXJwcmV0IHRoZSBzbG9wZXMgYW5kIGV4cGxhaW4gdGhlIHJlc3VsdHMgYWNjb3JkaW5nIHRvIHRoaXMgY29udGV4dC4gQmFzZWQgb24gdGhlIHJlc2lkdWFscywgaWRlbnRpZnkgYW55IG91dGxpZXJzIGFuZCBleHBsYWluIHdoeSB3ZSBuZWVkIHRvIHJlc29ydCB0byBhIG1vcmUgY29tcGxpY2F0ZWQgbW9kZWwuCiAg