Predicting Dog Breed Rankings and Traits

Preliminaries

library(rmdformats)
library(here)
library(janitor)
library(magrittr)
library(naniar)
library(gtsummary)
library(broom)
library(rsample)
library(yardstick)
library(rms)

library(vcd)
library(patchwork)
library(car)
library(MASS)
library(nnet)
library(conflicted)
library(ggrepel)
library(GGally)

library(tidyverse)

theme_set(theme_bw())

conflict_prefer("select", "dplyr")
conflict_prefer("filter", "dplyr")

1 Background

A major reason dogs are abandoned or brought to shelters is excessive barking. It would be helpful if a potential dog owner could predict a dog’s barking level based on the dog’s drooling level and how good they are around young children, which are two other factors potential owners consider. Perhaps if barking level could be anticipated in advance, potential owners could avoid the heartbreak of abandonment for them and the dog.

The American Kennel Club (AKC) displays a ranking of dog breeds annually. Many factors go into this ranking such as trainability, adaptability, energy level, shedding level, and many more. If I were to rank all the dog breeds, I would rank them by their outgoing nature with other dogs and people. These are traits I regard highly. Therefore, I seek to determine how well the factors ‘openness to strangers’ and ‘good with other dogs’ can predict the AKC 2020 Breed Ranking. I chose the year 2020 because that is the most recent year reported.

2 Research Questions

I will use an ordinal logistic regression model, the Bark Model, to predict a multi-categorical outcome, barking level, in order to answer the question:

1. How effectively can drooling level and goodness with young children predict a dog breed’s barking level?

I will then use an ordinary least squares method of linear regression modeling to fit the Rank Model to predict a quantitative outcome, 2020 AKC Breed Ranking, in order to answer the question:

2. How effectively can openness to strangers and goodness with other dogs predict a dog breed’s 2020 AKC ranking?

3 My Data

I will be combining two data sets. These data are from the Tidy Tuesday archive. Both data sets were put together from the AKC website by a GitHub user named kkakey. The first is a data set of various traits of each breed. The second data set includes the ranking of each breed for each year. I am extracting the 2020 Breed Ranking column from the second data set and adding it to the first to create one tibble that includes dog breed traits and their 2020 ranking. I will narrow down the list of traits to those of interest to my research questions: barking level, drooling level, good with young children, openness to strangers, and good with other dogs.

An advantage of these data is that the AKC is a renowned source of knowledge on dog breeds and I feel I can trust their annual rankings and trait designations. A limitation of these data is that the AKC website has been updated since it was scraped to create the .csv file, and now the website displays different traits than my data set. Some of the traits remained the same but have different names. This will make it difficult to update my models in the future.

Here is a link to the data: https://github.com/kkakey/dog_traits_AKC/tree/main/data

3.1 Data Ingest

First I will ingest the data set of traits into a tibble called dog_traits, then the data set of annual rankings into a tibble called rank_2020, selecting only the breed name and the 2020 ranking.

dog_traits <- read_csv(here("data", "breed_traits.csv")) %>%
  clean_names()

rank_2020 <- read_csv(here("data", "breed_rank_all.csv")) %>%
  clean_names()

rank_2020 <- rank_2020 %>%
  select(breed, x2020_rank)

Now I will combine the two data sets to create my analytic tibble. The breed names have spaces in them, and each sequential space is recognized differently by R. I need to run the mutate function 3 times in each tibble in order to change the spaces to underscores because the maximum numbers of spaces in a breed name is 3. In dog_traits I also need to change an apostrophe to an underscore.

dog_traits <- dog_traits %>%
  mutate(breed = str_replace(breed, " ", "_")) %>%
  mutate(breed = str_replace(breed, " ", "_")) %>%
  mutate(breed = str_replace(breed, " ", "_")) %>%
  mutate(breed = str_replace(breed, "’", "_"))
rank_2020 <- rank_2020 %>%
    mutate(breed = str_replace(breed, " ", "_")) %>%
    mutate(breed = str_replace(breed, " ", "_")) %>%
    mutate(breed = str_replace(breed, " ", "_"))

Now that the breed names in both data sets match each other, I can join the two data sets by breed. I will do so into a tibble called dogs_raw. Then I will check the number of rows (ie breeds) in each data set.

dogs_raw <- left_join(dog_traits, rank_2020, by = "breed")

dim(dog_traits)
[1] 195  17
dim(rank_2020)
[1] 198   2
dim(dogs_raw)
[1] 195  18

The number of rows in each data set do not match. This is because I joined them by breed so the combined tibble only includes those breeds which existed in both data sets. There were 3 breeds in rank_2020 that were not in dog_traits. I have a substantial number of breeds remaining, and I am doing a complete case analysis, so I am not worried about this.

The number of columns is what I expected. I am adding 2 columns into a tibble with 17 columns, but 1 matches, so this creates a total of 19 columns.

I will save my 3 tibbles: the set of traits, the set of 2020 rankings, and the combined set. I will remove the initial 2 data sets now that they are saved, in order to keep my work space clean.

saveRDS(dog_traits, file = here("data", "dog_traits.rds"))
saveRDS(rank_2020, file = here("data", "rank_2020.rds"))
saveRDS(dogs_raw, file = here("data", "dogs_raw.rds"))

rm(dog_traits, rank_2020)

In order to help my work go faster on subsequent sessions, I will include the following code that will ingest my data from the saved, combined .rds file.

dogs_raw <- readRDS(here("data", "dogs_raw.rds"))

3.2 Tidying, Data Cleaning and Data Management

As originally loaded, the dogs_raw data contain 195 rows and 18 columns. I will view my variables of interest to see what their types are so I can start cleaning them up.

dogs_raw %>% 
  select(breed, drooling_level, barking_level, 
         good_with_young_children, openness_to_strangers, 
         good_with_other_dogs, x2020_rank) %>%
  glimpse()
Rows: 195
Columns: 7
$ breed                    <chr> "Retrievers_(Labrador)", "French_Bulldogs", "…
$ drooling_level           <dbl> 2, 3, 2, 2, 3, 1, 1, 3, 2, 2, 1, 1, 1, 3, 4, …
$ barking_level            <dbl> 3, 1, 3, 1, 2, 4, 4, 1, 3, 5, 4, 3, 4, 3, 3, …
$ good_with_young_children <dbl> 5, 5, 5, 5, 3, 5, 5, 3, 5, 3, 3, 5, 5, 5, 3, …
$ openness_to_strangers    <dbl> 5, 5, 3, 5, 4, 5, 3, 3, 4, 4, 4, 3, 5, 4, 3, …
$ good_with_other_dogs     <dbl> 5, 4, 3, 5, 3, 3, 5, 3, 4, 4, 4, 3, 3, 3, 3, …
$ x2020_rank               <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14…

All my variables are ‘dbl’ type. I need most of them to be factors, and some to be characters. I will complete these conversions in the following code. I will also make bark an ordered factor so it works well in my logistic regression model. I will rename my variables and their categories to be more clear and concise. I will need to collapse some of the categories of my categorical variables in order to have each category contain at least 25 observations. I will also relevel my categories because many are ordinal, else they will default to alphabetical order. I will place the cleaned variables in a new tibble called dogs. Then I will remove the tibble of raw data so as to keep my work space clean.

dogs <- dogs_raw %>%
  rename(bark = barking_level,
         drool = drooling_level,
         kids = good_with_young_children,
         dog_friends = good_with_other_dogs,
         strangers = openness_to_strangers,
         rank = x2020_rank) %>%
  mutate(bark = factor(bark, ordered = TRUE),
         bark = fct_recode(bark,
                           "Only to alert" = "1",
                           "Only to alert" = "2",
                           "Quiet" = "3",
                           "Loud" = "4",
                           "Very vocal" = "5"),
         bark = fct_relevel(bark, "Only to alert", "Quiet", "Loud", "Very vocal"),
         drool = factor(drool),
         drool = fct_recode(drool,
                            "Dry" = "1",
                            "Regular" = "2",
                            "Drooler" = "3",
                            "Drooler" = "4",
                            "Drooler" = "5"),
         drool = fct_relevel(drool, "Dry", "Regular", "Drooler"),
         kids = factor(kids),
         kids = fct_recode(kids,
                           "Adults only" = "1",
                           "Adults only" = "3",
                           "Kid friendly" = "4",
                           "Kid friendly" = "5"),
         kids = fct_relevel(kids, "Adults only", "Kid friendly"),
         dog_friends = factor(dog_friends),
         dog_friends = fct_recode(dog_friends,
                           "Lone wolf" = "1",
                           "Lone wolf" = "2",
                           "Lone wolf" = "3",
                           "Pack animal" = "4",
                           "Pack animal" = "5"),
         dog_friends = fct_relevel(dog_friends, "Lone wolf", "Pack animal"),
         strangers = factor(strangers),
         strangers = fct_recode(strangers,
                           "Stranger danger" = "1",
                           "Stranger danger" = "2",
                           "Stranger danger" = "3",
                           "Friendly" = "4",
                           "Outgoing" = "5"),
         strangers = fct_relevel(strangers, "Stranger danger", "Friendly", "Outgoing")) %>%
  select(breed, rank, bark, drool, kids, dog_friends, strangers)

rm(dogs_raw)

3.3 Missingness

I noticed in a data summary that I have an extra level in each of my categorical variables that I did not expect, designated ‘0’. In order to determine which breed is causing this issue, I will arbitrarily choose bark.

dogs %>%
  filter(bark == 0)
# A tibble: 1 × 7
  breed         rank bark  drool kids  dog_friends strangers
  <chr>        <dbl> <ord> <fct> <fct> <fct>       <fct>    
1 Plott_Hounds   167 0     0     0     0           0        

Plott hounds were not given scores for any of the traits in the trait data set. I will treat these as missing data, and therefore remove this row from my tibble for missing outcome data in my complete case analysis.

dogs <- dogs %>%
  filter(bark != "0")

dim(dogs)
[1] 194   7

I am left with 194 rows as expected.

I will check the rest of my data for missingness.

miss_var_summary(dogs)
# A tibble: 7 × 3
  variable    n_miss pct_miss
  <chr>        <int>    <dbl>
1 breed            0        0
2 rank             0        0
3 bark             0        0
4 drool            0        0
5 kids             0        0
6 dog_friends      0        0
7 strangers        0        0

I have no missing data.

I will add row numbers so each breed has a unique identifier. This will not necessarily equal the ranking as I removed Plott Hounds as above. I will drop any empty categories.

dogs <- dogs %>%
  mutate(id = row_number()) %>%
  droplevels()

3.4 Tidied Tibble

My tibble dogs contains 194 rows (breeds) and 8 columns (variables). Each variable is contained in a column, and each row represents a single breed. All variables now have appropriate types, names, and categories.

dogs
# A tibble: 194 × 8
   breed                      rank bark  drool kids  dog_friends strangers    id
   <chr>                     <dbl> <ord> <fct> <fct> <fct>       <fct>     <int>
 1 Retrievers_(Labrador)         1 Quiet Regu… Kid … Pack animal Outgoing      1
 2 French_Bulldogs               2 Only… Droo… Kid … Pack animal Outgoing      2
 3 German_Shepherd_Dogs          3 Quiet Regu… Kid … Lone wolf   Stranger…     3
 4 Retrievers_(Golden)           4 Only… Regu… Kid … Pack animal Outgoing      4
 5 Bulldogs                      5 Only… Droo… Adul… Lone wolf   Friendly      5
 6 Poodles                       6 Loud  Dry   Kid … Lone wolf   Outgoing      6
 7 Beagles                       7 Loud  Dry   Kid … Pack animal Stranger…     7
 8 Rottweilers                   8 Only… Droo… Adul… Lone wolf   Stranger…     8
 9 Pointers_(German_Shortha…     9 Quiet Regu… Kid … Pack animal Friendly      9
10 Dachshunds                   10 Very… Regu… Adul… Pack animal Friendly     10
# … with 184 more rows

I will save my tidied tibble as an R data set.

saveRDS(dogs, file = here("data", "dogs.rds"))

In order to help my work go faster on subsequent sessions, I will include the following code that will ingest my cleaned data from the saved .rds file.

dogs <- readRDS(here("data", "dogs.rds"))

4 Code Book and Clean Data Summary

The data in my complete dogs sample consist of 194 dog breeds on the American Kennel Club website. The AKC conveys no defined exclusion criteria. Data are excluded from my tibble for outcome missingness only. Of the 194 subjects, 194 have complete data on all variables listed below. My logistic regression outcome is bark, which is a multi-categorical indicator of the breed’s barking level. My ordinary least squares linear regression outcome is rank which is the 2020 AKC Breed Ranking. I have a variable called id which is a unique identifier for each row and the variable breed which identifies the breed the data describe. All other variables listed below will serve as predictors for my models.

4.1 Main Codebook Table

Variables included in my analyses are summarized in the following table. I will omit identifying variables in favor of being concise.

dogs %>% 
  select(-id, -breed) %>%
    tbl_summary(.,
        label = list(
            bark = "Barking level",
            drool = "Drooling level",
            kids = "Good with Young Children",
            strangers = "Openness to Strangers",
            dog_friends = "Good with Other Dogs",
            rank = "2020 American Kennel Club breed ranking"),
        stat = list(all_continuous() ~ 
                "{median} [{min} to {max}]" ))
Characteristic N = 1941
2020 American Kennel Club breed ranking 98 [1 to 195]
Barking level
Only to alert 36 (19%)
Quiet 99 (51%)
Loud 34 (18%)
Very vocal 25 (13%)
Drooling level
Dry 92 (47%)
Regular 64 (33%)
Drooler 38 (20%)
Good with Young Children
Adults only 103 (53%)
Kid friendly 91 (47%)
Good with Other Dogs
Lone wolf 126 (65%)
Pack animal 68 (35%)
Openness to Strangers
Stranger danger 118 (61%)
Friendly 43 (22%)
Outgoing 33 (17%)
1 Median [Range]; n (%)

My data set has 194 subjects with AKC Breed Ranking ranging from 1 to 195. The breakdown of each variable (other than identifiers) is listed above and will be described in deeper detail below.

4.2 Data Distribution Description

This describes the distribution of my data.

dogs %>% describe(.) %>% Hmisc::html()
.

8 Variables   194 Observations

breed
nmissingdistinct
1940194
lowest :Affenpinschers Afghan_Hounds Airedale_Terriers Akitas Alaskan_Malamutes
highest:Whippets Wirehaired_Pointing_GriffonsWirehaired_Vizslas Xoloitzcuintli Yorkshire_Terriers

rank
image
nmissingdistinctInfoMeanGmd.05.10.25.50.75.90.95
1940194197.6465.25 10.65 20.30 49.25 97.50145.75175.70185.35
lowest : 1 2 3 4 5 , highest: 191 192 193 194 195
bark
image
nmissingdistinct
19404
 Value      Only to alert         Quiet          Loud    Very vocal
 Frequency             36            99            34            25
 Proportion         0.186         0.510         0.175         0.129
 

drool
image
nmissingdistinct
19403
 Value          Dry Regular Drooler
 Frequency       92      64      38
 Proportion   0.474   0.330   0.196
 

kids
nmissingdistinct
19402
 Value       Adults only Kid friendly
 Frequency           103           91
 Proportion        0.531        0.469
 

dog_friends
nmissingdistinct
19402
 Value        Lone wolf Pack animal
 Frequency          126          68
 Proportion       0.649       0.351
 

strangers
image
nmissingdistinct
19403
 Value      Stranger danger        Friendly        Outgoing
 Frequency              118              43              33
 Proportion           0.608           0.222           0.170
 

id
image
nmissingdistinctInfoMeanGmd.05.10.25.50.75.90.95
1940194197.565 10.65 20.30 49.25 97.50145.75174.70184.35
lowest : 1 2 3 4 5 , highest: 190 191 192 193 194

5 Analysis 1: Bark Prediction Model

I will use an ordinal logistic regression model called Bark Model to predict barking level, bark, using drooling level, drool, and goodness with young children, kids.

5.1 Exploring the Data

5.1.1 My Outcome: bark

bark is a 4-category variable. I want to visualize what percentage of breeds are in each category.

ggplot(dogs, aes(x = bark, fill = bark)) + 
    geom_bar(aes(y = (..count..)/sum(..count..)), col = "black") +
    geom_text(aes(y = (..count..)/sum(..count..), 
                  label = scales::percent((..count..) / 
                                        sum(..count..))),
              stat = "count", vjust = 1.5, 
              color = "black", size = 5) +
    scale_y_continuous(labels = scales::percent) +
    scale_fill_brewer(palette = "BuPu") +
    guides(fill = "none") + 
    labs(title = "Distribution of Barking Level Across Categories",
         subtitle = "Outcome Distribution for `bark_model`",
         x = "Barking Level",
         y = "Percentage",
         caption = "194 breeds in `dogs`")

Half of the breeds have a barking level of ‘quiet’. The other half is distributed fairly evenly across the other 3 categories: ‘only to alert’, ‘loud’, and ‘very vocal’. The category ‘very vocal’ has the fewest breeds.

5.1.2 My Predictors: drool and kids

I will be using drooling level and goodness with young children in order to predict barking level. I want to visualize the spread of barking level within each category of my predictors.

p1 <- ggplot(dogs, 
       aes(x = drool, fill = bark)) +
  geom_bar(position = "dodge", col = "black") +
  scale_fill_brewer(palette = "GnBu") +
  labs(title = "Drooling Level",
       x = "Drooling Level",
       y = "Proportion",
       fill = "Barking Level")

p2 <- ggplot(dogs, 
       aes(x = kids, fill = bark)) +
  geom_bar(position = "dodge", col = "black") +
  scale_fill_brewer(palette = "OrRd") +
  labs(title = "Goodness with Young Children",
       x = "Good with Young Children",
       y = "Proportion",
       fill = "Barking Level")

p1 / p2 +
  plot_annotation(title = "Spread of Barking Level (Outcome) Within Each Predictor Category",
       caption = "194 breeds in `dogs`")

The ratio of the barking levels visually appears consistent within each category of drooling level. Perhaps drooling level will not be an effective predictor of barking level. For my predictor kids, describing goodness with young children, the breeds who are kid friendly have more ‘very vocal’ breeds and fewer ‘only to alert’ breeds than the breeds who prefer adults only. This predictor may be more effective.

I will view a table of a breakdown of how many breeds are in each category, combining both of my predictors.

dogs %>%
  tabyl(drool, kids) %>%
  adorn_totals(c("row", "col")) %>%
  adorn_percentages("row") %>%
  adorn_pct_formatting(rounding = "half up", digits = 0) %>%
  adorn_ns() %>%
  kable()
drool Adults only Kid friendly Total
Dry 61% (56) 39% (36) 100% (92)
Regular 42% (27) 58% (37) 100% (64)
Drooler 53% (20) 47% (18) 100% (38)
Total 53% (103) 47% (91) 100% (194)

Goodness with young children is about even between the two categories: 53% of breeds prefer adults only and 47% are kid friendly. Within these categories, the spread of drooling level differs. Within the group of breeds who prefer adults only, more than half of the breeds are in the ‘dry’ category for drooling level (56 breeds out of 103 breeds). Within the group of breeds who are kid friendly, there is an even split between ‘dry’ and ‘regular’ drooling level breeds. In both, the minority of breeds are ‘droolers’.

I will check for collinearity between my predictors.

car::vif(polr(bark ~ drool + kids, data = dogs, Hess = TRUE))
          GVIF Df GVIF^(1/(2*Df))
drool 1.039467  2        1.009724
kids  1.039467  1        1.019543

These variance inflation factors are a bit higher than I would like, so I will keep collinearity in mind as I fit and evaluate my model.

5.1.3 Transformation Analysis & Assessment for Nonlinear Terms

I will not transform my outcome because it is categorical. I will not check a Spearman rho2 plot because I have only 1 option of a nonlinear term, an interaction between my 2 categorical predictors, which I will use.

5.2 Fitting the Model: A Proportional-Odds Cumulative-Logit Model

First I will ensure my outcome is an ordered factor.

is.ordered(dogs$bark)
[1] TRUE

Great - it is. Now I will fit my model using the polr engine. I will keep in mind that the distance between each category may not be equal, ie the difference between breeds who bark ‘only to alert’ vs ‘quiet’ breeds is not necessarily the same as the difference between breeds who are ‘very vocal’ vs. ‘loud’.

bark_model <- polr(bark ~ drool * kids, data = dogs, Hess = TRUE)

summary(bark_model)
Call:
polr(formula = bark ~ drool * kids, data = dogs, Hess = TRUE)

Coefficients:
                                 Value Std. Error  t value
droolRegular                  -0.03889     0.4446 -0.08748
droolDrooler                  -0.87278     0.5000 -1.74543
kidsKid friendly               0.93857     0.4085  2.29737
droolRegular:kidsKid friendly -0.45815     0.6277 -0.72988
droolDrooler:kidsKid friendly -0.34710     0.7317 -0.47435

Intercepts:
                    Value   Std. Error t value
Only to alert|Quiet -1.4287  0.2830    -5.0477
Quiet|Loud           0.9993  0.2663     3.7521
Loud|Very vocal      2.1295  0.3094     6.8821

Residual Deviance: 461.5686 
AIC: 477.5686 

My model has 3 intercepts for the 4 bark categories and 5 slopes for our binary predictor and 3-category predictor as well as the interaction between them.

confint(bark_model)
                                   2.5 %    97.5 %
droolRegular                  -0.9153908 0.8307751
droolDrooler                  -1.8612879 0.1060836
kidsKid friendly               0.1403315 1.7452809
droolRegular:kidsKid friendly -1.6917574 0.7727996
droolDrooler:kidsKid friendly -1.7869159 1.0872452

My model predicts that a breed with a ‘regular’ drooling level would not consistently have an impact on the outcome when compared to reference (‘dry’ drooling level) because the 95% confidence interval includes 0. Neither would a ‘drooler’ when compared to the same reference. My model predicts that goodness with young children is a trait which likely consistently impacts the outcome. Both of the interaction term slopes have confidence intervals which include 0, so it is unlikely that these meaningfully improve the predictive ability of my model.

Before I start making predictions, I will display a plot of effect size for my model. In order to do that, I must fit the model with the lrm engine and in order to do that, I must set a datadist.

d <- datadist(dogs)
options(datadist = "d")

bark_model_lrm <- lrm(bark ~ drool * kids, data = dogs, x = T, y = T)

plot(summary(bark_model_lrm))

Similar to what I explained about the confidence intervals, the kids confidence interval is the one that does not include 0 and therefore would be most likely to improve the predictive ability of the model.

5.2.1 Making Predictions

There are 6 possible predictions of my 2 variables, so I will fit the model to 6 new dogs with each of these combinations, show the new data as a tibble, and plot the predicted probabilities.

dogs_new <- data.frame(name = c("Alfalfa", "Bernie", "Clancy", 
                                "Cooper", "Frankie", "Ted"),
                   drool = c("Drooler", "Drooler", "Regular", 
                             "Regular", "Dry", "Dry"),
                   kids = c("Kid friendly", "Adults only", "Kid friendly", 
                            "Adults only", "Kid friendly", "Adults only"))

tibble(dogs_new)
# A tibble: 6 × 3
  name    drool   kids        
  <chr>   <chr>   <chr>       
1 Alfalfa Drooler Kid friendly
2 Bernie  Drooler Adults only 
3 Clancy  Regular Kid friendly
4 Cooper  Regular Adults only 
5 Frankie Dry     Kid friendly
6 Ted     Dry     Adults only 
predict(bark_model, dogs_new, interval = "prediction")
[1] Quiet Quiet Quiet Quiet Quiet Quiet
Levels: Only to alert Quiet Loud Very vocal
plot(predict(bark_model, dogs_new, interval = "prediction"))

Unfortunately, for every possible combination of my 2 variables, my model predicts the barking level will be ‘quiet’. This is probably the worst possible predictive ability for a categorical prediction model. I will move on to validation.

5.3 Model Validation

In order to validate my model, I will have to use the lrm fit. I will use the default number of bootstraps (40).

set.seed(2022)

validate(bark_model_lrm)
          index.orig training    test optimism index.corrected  n
Dxy           0.2310   0.2472  0.2036   0.0436          0.1874 40
R2            0.0750   0.0920  0.0571   0.0349          0.0401 40
Intercept     0.0000   0.0000 -0.1431   0.1431         -0.1431 40
Slope         1.0000   1.0000  0.8014   0.1986          0.8014 40
Emax          0.0000   0.0000  0.0753   0.0753          0.0753 40
D             0.0659   0.0832  0.0485   0.0347          0.0312 40
U            -0.0103  -0.0103 -1.2004   1.1901         -1.2004 40
Q             0.0762   0.0935  1.2489  -1.1554          1.2315 40
B             0.2020   0.1978  0.2072  -0.0095          0.2114 40
g             0.5635   0.6089  0.4721   0.1368          0.4267 40
gp            0.1145   0.1211  0.0961   0.0251          0.0894 40

My R2 changes somewhat in new data, from an estimated R2 of 0.0920 across 40 training samples, to an average R2 of 0.0571, creating an optimism of 0.0349. My R2 corrected for overfitting is then 0.0401, which is quite different than my original R2 of 0.0750.

I will use Somers’ d (Dxy) to calculate the C statistic.

initial <- 0.5 + (0.2310/2)
corrected <- 0.5 + (0.1874/2)

(initial - corrected)
[1] 0.0218

The change in the C-statistic is 0.0218. This is a small change, indicating that if my model were fit to new data, the area under the curve would be only 2 percentage points smaller.

5.4 Testing the Proportional Odds Assumption

In order to test the proportional odds assumption, I will fit my model with the multinom function.

(bark_model_poa <- multinom(bark ~ drool * kids, data = dogs))
# weights:  28 (18 variable)
initial  value 268.941106 
iter  10 value 224.924428
iter  20 value 223.906699
iter  30 value 223.872516
final  value 223.872459 
converged
Call:
multinom(formula = bark ~ drool * kids, data = dogs)

Coefficients:
           (Intercept) droolRegular droolDrooler kidsKid friendly
Quiet       0.96949830  -0.19640480   -0.5175377        0.3520780
Loud        0.08712077  -0.08724537  -15.1870165        0.4722902
Very vocal -1.01143305  -0.08736778   -0.2413670        1.9275416
           droolRegular:kidsKid friendly droolDrooler:kidsKid friendly
Quiet                          0.5331531                     0.1122895
Loud                          -0.2489137                    14.6276306
Very vocal                    -0.2690124                   -17.5208386

Residual Deviance: 447.7449 
AIC: 483.7449 

I will compare the log likelihoods of the multinomial model and the proportional odds logistic regression model. The multinomial logit fits 3 intercepts and 15 slopes for a total of 18 parameters. The proportional odds logit fit 3 intercepts and 5 slopes for a total of 8 parameters. The difference in parameters between the models is 10.

LL_polr <- logLik(bark_model)
LL_multinom <- logLik(bark_model_poa)
(G <- -2 * (LL_polr[1] - LL_multinom[1]))
[1] 13.82372
pchisq(G, 10, lower.tail = FALSE)
[1] 0.181185

This result is not highly significant, so I am comfortable assuming proportional odds.

6 Analysis 2: Rank Prediction Model

I will use an ordinary least squares linear regression model called ‘Rank Model’ to predict the 2020 AKC Breed Ranking, rank, using openness to strangers, strangers, and goodness with other dogs, dog_friends.

6.1 Exploring the Data

My outcome is a ranking, and I have included all the ranked breeds. Therefore, it is impossible for my data to be normally distributed as there is exactly 1 breed at each rank level. Instead of visualizing the distribution of my data in a Normal Q-Q plot or Density Histogram as I normally would, I will visualize the spread of my predictors across the rankings.

ggplot(data = dogs, aes(x = strangers, 
                           y = rank, 
                           col = dog_friends)) + 
  geom_boxplot() +
  guides(fill=guide_legend("title")) +
  labs(title = "Spread of 'Goodness with Other Dogs' and 'Openness to Strangers'",
       subtitle = "Across 194 Ranked Breeds",
       x = "Openness to Strangers",
       y = "Rank",
       col = "Good with Other Dogs",
       shape = "Good with Other Dogs",
       caption = "194 breeds from `dogs`")

The outcome is on the y-axis, with higher ranked breeds lower on the plot. The red color represents breeds that are ‘lone wolves’ or do not get along with other dogs. The blue color represents ‘pack animals’ or breeds who do get along well with other dogs. The x-axis is openness to strangers with the furthest left points being ‘stranger danger’ or lack of openness and the furthest right points being ‘outgoing’.

The only group with outliers is breeds who are outgoing pack animals, and these outliers are 2 breeds ranked in the lowest quartile. Within the ‘stranger danger’ category, the mean rank of pack animals is lower than that of the lone wolves, but both groups range almost the full length of the rankings. Within the ‘friendly’ and ‘outgoing’ categories, the mean rank of lone wolves is lower than that of the pack animals. The range of friendly breeds’ rankings is similar to the range of ‘stranger danger’ breeds’ rankings, but the range of outgoing breeds rankings is smaller.

6.1.1 Visualizing in Groups

I will take a closer look at only the top 10 breeds to see if I can see a pattern there.

dogs_top10 <- dogs %>%
  arrange(rank) %>%
  filter(rank == 1:10)

ggplot(data = dogs_top10, aes(x = strangers, 
                           y = rank, 
                           col = dog_friends)) + 
  geom_point(aes(shape = dog_friends),
    alpha = 0.6,
    size = 5) +
  geom_text_repel(label= dogs_top10$breed,
            col = "black",
            size = 4) +
  guides(fill=guide_legend("title")) +
  labs(title = "Spread of 'Goodness with Other Dogs' and 'Openness to Strangers'",
       subtitle = "Across Top 10 Dog Breeds",
       x = "Openness to Strangers",
       y = "Rank",
       col = "Good with Other Dogs",
       shape = "Good with Other Dogs",
       caption = "breeds ranked 1-10 in `dogs`")

The red circles represent long wolves and the blue triangles represent pack animals. There is really no pattern here. Four of the breeds are ‘lone wolves’ and 6 are ‘pack animals’. There are 4 breeds in the ‘outgoing’ category and 3 breeds in each of the ‘stranger danger’ and ‘friendly’ categories of openness to strangers. This doesn’t answer much for me.

I will now try taking a look at the highest quartile ranked breeds and compare them to the lowest quartile.

194 / 4
[1] 48.5

Each quartile contains 48.5 breeds. I would rather underestimate than overestimate, so I will sample the highest ranked 48 breeds and the lowest ranked 48 breeds.

dogs_top48 <- dogs %>%
  arrange(rank) %>%
  filter(rank == 1:48)

dogs_low48 <- dogs %>%
  arrange(rank) %>%
  top_n(48, rank)
p3 <- ggplot(data = dogs_top48, aes(x = strangers, 
                           y = rank, 
                           col = dog_friends)) + 
  geom_boxplot() +
  guides(col = "none") +
  labs(title = "Highest Ranked Quartile of Breeds",
       x = "Openness to Strangers",
       y = "Rank",
       col = "Good with Other Dogs",
       shape = "Good with Other Dogs")

p4 <- ggplot(data = dogs_low48, aes(x = strangers, 
                           y = rank, 
                           col = dog_friends)) + 
  geom_boxplot() +
  labs(title = "Lowest Ranked Quartile of Breeds",
       x = "Openness to Strangers",
       y = "Rank",
       col = "Good with Other Dogs",
       shape = "Good with Other Dogs")

p3 + p4 +
  plot_annotation(title = "Spread of 'Goodness with Other Dogs' and 'Openness to Strangers'",
                  subtitle = "Across Highest and Lowest Quartiles of Ranked Breeds",
                  caption = "96 breeds ranked 1-48 and 147-195 in `dogs`")

The only group with an outlier is ‘stranger danger’ pack animals, who have an outlier ranked higher than the rest of that group. In the highest quartile of rankings, within the ‘stranger danger’ category, the pack animals are ranked higher than the lone wolves. The opposite is true for friendlt and outgoing dogs in the highest quartile. In the lowest quartile, lone wolves are ranked higher than pack animals in all of the categories of openness to strangers.

6.1.2 My Predictors

I want to take a look at the spread of breeds across each category of my 2 predictors.

dogs %>%
  tabyl(strangers, dog_friends) %>%
  adorn_totals(c("row", "col")) %>%
  adorn_percentages("row") %>%
  adorn_pct_formatting(rounding = "half up", digits = 0) %>%
  adorn_ns() %>%
  kable()
strangers Lone wolf Pack animal Total
Stranger danger 72% (85) 28% (33) 100% (118)
Friendly 58% (25) 42% (18) 100% (43)
Outgoing 48% (16) 52% (17) 100% (33)
Total 65% (126) 35% (68) 100% (194)

More breeds are ‘lone wolves’ (approximately 2/3) than are ‘pack animals’. Within the ‘lone wolf’ category of goodness with other dogs, many more breeds are in the ‘stranger danger’ category of openness to strangers as compared to the ‘friendly’ and ‘outgoing’ categories, which makes sense. Within the ‘pack animal’ category of goodness with other dogs, the breeds are more evenly split, but still most are in the ‘stranger danger’ category of openness to strangers.

I need to check collinearity between my predictors. I have already set a datadist for my data set.

car::vif(ols(rank ~ strangers * dog_friends, data = dogs, x = T, y = T))
                          GVIF Df GVIF^(1/(2*Df))
strangers             14.68864  0             Inf
dog_friends           14.68864  0             Inf
strangers:dog_friends 14.68864  0             Inf

These variance inflation factors are high, so I will keep collinearity in mind as I fit and evaluate my model.

6.1.3 Transformation Analysis & Assessment for Nonlinear Terms

A transformation is unlikely to improve the distribution of my outcome because it is of uniform distribution and I do not think a Box-Cox assessment/plot would change my mind here. I have only 1 option of nonlinear term to add, so I will include an interaction between my 2 categorical predictors.

6.2 Fitting the Model: A Linear Regression Model using Ordinary Least Squares

I will fit my model using the ols engine. I have already set a datadist for my data set.

(rank_model <- ols(data = dogs, rank ~ strangers * dog_friends,
          x = TRUE, y = TRUE))
Linear Regression Model
 
 ols(formula = rank ~ strangers * dog_friends, data = dogs, x = TRUE, 
     y = TRUE)
 
                  Model Likelihood    Discrimination    
                        Ratio Test           Indexes    
 Obs      194    LR chi2     15.92    R2       0.079    
 sigma54.8111    d.f.            5    R2 adj   0.054    
 d.f.     188    Pr(> chi2) 0.0071    g       16.607    
 
 Residuals
 
      Min       1Q   Median       3Q      Max 
 -111.091  -43.818    1.806   39.276  127.118 
 
 
                                              Coef     S.E.    t     Pr(>|t|)
 Intercept                                    103.6941  5.9451 17.44 <0.0001 
 strangers=Friendly                            -9.6941 12.4705 -0.78 0.4379  
 strangers=Outgoing                           -12.8816 14.9369 -0.86 0.3896  
 dog_friends=Pack animal                       14.3968 11.2420  1.28 0.2019  
 strangers=Friendly * dog_friends=Pack animal -30.9523 20.3336 -1.52 0.1296  
 strangers=Outgoing * dog_friends=Pack animal -44.3269 22.1556 -2.00 0.0469  
 

The g statistic is 16.607, which indicates that if I randomly selected 2 breeds, this would be the average difference in their predicted rankings. In a ranked list of 194 breeds, this is not very good.

summary(rank_model)
             Effects              Response : rank 

 Factor                               Low High Diff. Effect   S.E.   Lower 0.95
 strangers - Friendly:Stranger danger 1   2    NA     -9.6941 12.471 -34.2940  
 strangers - Outgoing:Stranger danger 1   3    NA    -12.8820 14.937 -42.3470  
 dog_friends - Pack animal:Lone wolf  1   2    NA     14.3970 11.242  -7.7798  
 Upper 0.95
 14.906    
 16.584    
 36.573    

Adjusted to: strangers=Stranger danger dog_friends=Lone wolf  

This summary tells me that if I had 2 dog breeds, Cooper (a black Labrador) and Ted (a Cavalier King Charles Spaniel) who are both ‘lone wolves’ when it comes to goodness with other dogs, but Cooper is ‘outgoing’ and Ted’s category of openness to strangers is ‘stranger danger’, then my model predicts that Cooper’s 2020 AKC Breed Ranking will be 12.9 points lower than Ted’s. Here is a visualization of these effect sizes.

plot(summary(rank_model))

All of the confidence intervals in this model include 0, showing that these predictors probably have poor predictive ability.

6.3 Residual Diagnostics

I will check which points may be influential, and which predictors might have more influential points.

which.influence(rank_model)
$Intercept
[1] 3

$strangers
 [1]   5   6  13  14  18  32 155 162 163 165 175 180 190

$dog_friends
[1]   7  20  27  28  31  36 191

$`strangers * dog_friends`
[1]   6  13 160 163 164 169 187 189 193

This is interesting. There are about half the amount of influential point in dog_friends as there are in strangers and most of the influential points in ‘strangers’ are in very low ranked breeds. If I were to drop some of these rows, it would impact the estimates of the intercept for strangers.

I will check the residuals for my model and visualize a plot.

temp <- data.frame(area = 1:194)
temp$resid <- residuals(rank_model, type = "ordinary")

ggplot(temp, aes(x = area, y = resid)) +
    geom_point() +
    geom_line()

The residuals range from about -100 to 100. The most accurate predictions occur in the breeds ranked about 130-155. The higher ranked points appear to have larger residuals.

6.4 Model Validation

I will validate my model using the default number of bootstraps.

set.seed(2022)
validate(rank_model)
          index.orig  training      test  optimism index.corrected  n
R-square      0.0788    0.1118    0.0469    0.0650          0.0138 40
MSE        2911.3372 2776.2205 3012.2478 -236.0273       3147.3645 40
g            16.6071   18.3806   14.2807    4.0999         12.5072 40
Intercept     0.0000    0.0000   17.7570  -17.7570         17.7570 40
Slope         1.0000    1.0000    0.8238    0.1762          0.8238 40

My R2 changes substantially in new data, from 0.1118 across 40 training samples to an estimated 0.0469 in a test sample, creating an optimism of 0.065. My R2 corrected for overfitting is 0.0138, which is much lower than my original of 0.0788. My corrected mean squared error is 3147, much higher than 2911.

7 Conclusions

In this data science project, I sought to answer two research questions: 1) “How effectively can drooling level and goodness with young children predict a dog breed’s barking level?”, for which I fit an ordinal logistic regression model to predict this multi-categorical outcome, and 2) “how effectively can openness to strangers and goodness with other dogs predict a dog breed’s 2020 AKC ranking?”, for which I used an ordinary least squares method to fit a linear regression model.

In exploring my data, fitting the models, making predictions, visualizing effect sizes, and interpreting summary statistics, I have learned valuable insight into the predictive ability of my models. Firstly, drooling level and goodness with young children are not very effective predictors of barking level within my data set nor within new data. Secondly, openness to strangers and goodness with other dogs were not very effective predictors of 2020 AKC Breed Ranking within my data set nor within new data. I have learned why the AKC takes so many other traits into consideration when ranking dog breeds, because the traits I have investigated here do not correlate well with each other.

A major limitation is that the AKC website has updated their traits search algorithm and no longer uses the same phrasing nor the same measures as when this data was scraped. Therefore, I am not able to easily update my models with each annual ranking. Another limitation is that many of the variables in the raw data set had very high variance inflation factors when I added more than 2 variables to the model. Some were predictably collinear, such as coat length and grooming frequency, or energy level and mental stimulation needs. However, most were surprising to me, such as grooming frequency and energy level, or trainability and coat length. I felt that this limited me in predictor choice, but I was still able to answer my research questions.

A next step would be to scrape the AKC website again to obtain the more updated measures, fit prediction models, and compare those models to my models fit here. This would help determine if the changes to the website affected the predictive ability of these variables. Another interesting next step would be to adjust for age or life stage of the dogs. Puppies, young dogs, middle aged dogs, and elderly dogs all behave differently, regardless of their breed. For instance, puppies usually sleep more than young or middle aged dogs but eat less. Puppies go through ‘fear phases’ where their openness to strangers or goodness with young children or other dogs may be much lower than an older dog of the same breed who has more experience.

The impact of this data science project helps me understand which predictors do (or do not, rather) weigh strongly in the AKC’s annual breed rankings, and taught me that breeds who drool more do not necessarily bark more, which is something I used to think. For someone contemplating getting a dog, learning about these traits and how they interact could be vital to the lifelong success and happiness of the both the owner and their new pup.


As a result of this data science project, I have learned about data selection and variable selection. In choosing a data set to analyze, I wish I had run some simple toy models in a subset to better appreciate possible collinearity or high correlations between predictors. If I knew how closely the variables in the data sets I used correlated, I might have combined a third data set with some different measures, like perhaps breeds’ life expectancies or inclinations toward illness. Furthermore in my variable selection, I would have liked to add a third or fourth predictor to one or both of my models, but collinearity concerns prevented me from doing so. This has driven the point home that data selection and cleaning is the vast majority of the workload when it comes to fitting models to answer research questions.

8 References and Acknowledgments

Here is a link to the data: https://github.com/kkakey/dog_traits_AKC/tree/main/data

Thank you to Dr. Love and the CRSP 432 Teaching Assistants for the conveying the knowledge and insight necessary to complete this project and answer my research questions.

9 Session Information

xfun::session_info()
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur 10.16

Locale: en_US.UTF-8 / en_US.UTF-8 / en_US.UTF-8 / C / en_US.UTF-8 / en_US.UTF-8

Package version:
  abind_1.4-5         askpass_1.1         assertthat_0.2.1   
  backports_1.4.1     base64enc_0.1-3     bit_4.0.4          
  bit64_4.0.5         bitops_1.0.7        blob_1.2.3         
  bookdown_0.26       boot_1.3.28         brio_1.1.3         
  broom_0.8.0         broom.helpers_1.7.0 bslib_0.3.1        
  cachem_1.0.6        callr_3.7.0         car_3.0-12         
  carData_3.0-5       cellranger_1.1.0    checkmate_2.1.0    
  cli_3.2.0           clipr_0.8.0         cluster_2.1.3      
  codetools_0.2-18    colorspace_2.0-3    commonmark_1.8.0   
  compiler_4.1.2      conflicted_1.1.0    cpp11_0.4.2        
  crayon_1.5.1        curl_4.3.2          data.table_1.14.2  
  DBI_1.1.2           dbplyr_2.1.1        desc_1.4.1         
  diffobj_0.3.5       digest_0.6.29       dplyr_1.0.8        
  dtplyr_1.2.1        ellipsis_0.3.2      evaluate_0.15      
  fansi_1.0.3         farver_2.1.0        fastmap_1.1.0      
  forcats_0.5.1       foreign_0.8-82      Formula_1.2-4      
  fs_1.5.2            furrr_0.2.3         future_1.25.0      
  gargle_1.2.0        generics_0.1.2      GGally_2.1.2       
  ggplot2_3.3.5       ggrepel_0.9.1       globals_0.14.0     
  glue_1.6.2          googledrive_2.0.0   googlesheets4_1.0.0
  graphics_4.1.2      grDevices_4.1.2     grid_4.1.2         
  gridExtra_2.3       gt_0.5.0            gtable_0.3.0       
  gtsummary_1.5.2     haven_2.5.0         here_1.0.1         
  highr_0.9           Hmisc_4.7-0         hms_1.1.1          
  htmlTable_2.4.0     htmltools_0.5.2     htmlwidgets_1.5.4  
  httr_1.4.2          ids_1.0.1           isoband_0.2.5      
  janitor_2.1.0       jpeg_0.1-9          jquerylib_0.1.4    
  jsonlite_1.8.0      knitr_1.38          labeling_0.4.2     
  labelled_2.9.0      lattice_0.20-45     latticeExtra_0.6-29
  lifecycle_1.0.1     listenv_0.8.0       lme4_1.1.29        
  lmtest_0.9-40       lubridate_1.8.0     magrittr_2.0.3     
  maptools_1.1.4      MASS_7.3-57         Matrix_1.4-1       
  MatrixModels_0.5-0  memoise_2.0.1       methods_4.1.2      
  mgcv_1.8.40         mime_0.12           minqa_1.2.4        
  modelr_0.1.8        multcomp_1.4-18     munsell_0.5.0      
  mvtnorm_1.1-3       naniar_0.6.1        nlme_3.1-157       
  nloptr_2.0.0        nnet_7.3-17         norm_1.0.10.0      
  numDeriv_2016.8.1.1 openssl_2.0.0       parallel_4.1.2     
  parallelly_1.31.1   patchwork_1.1.1     pbkrtest_0.5.1     
  pillar_1.7.0        pkgconfig_2.0.3     pkgload_1.2.4      
  plyr_1.8.7          png_0.1-7           polspline_1.1.19   
  praise_1.0.0        prettyunits_1.1.1   pROC_1.18.0        
  processx_3.5.3      progress_1.2.2      ps_1.7.0           
  purrr_0.3.4         quantreg_5.88       R6_2.5.1           
  rappdirs_0.3.3      RColorBrewer_1.1-3  Rcpp_1.0.8.3       
  RcppEigen_0.3.3.9.2 readr_2.1.2         readxl_1.4.0       
  rematch_1.0.1       rematch2_2.1.2      reprex_2.0.1       
  reshape_0.8.9       rlang_1.0.2         rmarkdown_2.14     
  rmdformats_1.0.3    rms_6.3-0           rpart_4.1.16       
  rprojroot_2.0.3     rsample_0.1.1       rstudioapi_0.13    
  rvest_1.0.2         sandwich_3.0-1      sass_0.4.1         
  scales_1.2.0        selectr_0.4.2       slider_0.2.2       
  snakecase_0.11.0    sp_1.4.7            SparseM_1.81       
  splines_4.1.2       stats_4.1.2         stringi_1.7.6      
  stringr_1.4.0       survival_3.3-1      sys_3.4            
  testthat_3.1.3      TH.data_1.1-0       tibble_3.1.6       
  tidyr_1.2.0         tidyselect_1.1.2    tidyverse_1.3.1    
  tinytex_0.38        tools_4.1.2         tzdb_0.3.0         
  UpSetR_1.4.0        utf8_1.2.2          utils_4.1.2        
  uuid_1.1.0          vcd_1.4-9           vctrs_0.4.1        
  viridis_0.6.2       viridisLite_0.4.0   visdat_0.5.3       
  vroom_1.5.7         waldo_0.4.0         warp_0.2.0         
  withr_2.5.0         xfun_0.30           xml2_1.3.3         
  yaml_2.3.5          yardstick_0.0.9     zoo_1.8-10         
LS0tCnRpdGxlOiAiUHJlZGljdGluZyBEb2cgQnJlZWQgUmFua2luZ3MgYW5kIFRyYWl0cyIKYXV0aG9yOiAiQ2F0aGVyaW5lIEhlaW56aW5nZXIiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOgogICAgaGlnaGxpZ2h0OiBrYXRlCiAgICBudW1iZXJfc2VjdGlvbnM6IFRSVUUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogVFJVRQotLS0KCiMgUHJlbGltaW5hcmllcyB7LX0KCmBgYHtyIHNldHVwLCBlY2hvID0gRkFMU0UsIGNhY2hlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQpvcHRpb25zKG1heC5wcmludD0iMjUwIikKb3B0c19jaHVuayRzZXQoZWNobz1UUlVFLAogICAgICAgICAgICAgICBjYWNoZT1GQUxTRSwKICAgICAgICAgICAgICAgcHJvbXB0PUZBTFNFLAogICAgICAgICAgICAgICB0aWR5PUZBTFNFLAogICAgICAgICAgICAgICBjb21tZW50PU5BLAogICAgICAgICAgICAgICBtZXNzYWdlPUZBTFNFLAogICAgICAgICAgICAgICB3YXJuaW5nPUZBTFNFKQpvcHRzX2tuaXQkc2V0KHdpZHRoPTc1KQpgYGAKCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkocm1kZm9ybWF0cykKbGlicmFyeShoZXJlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkobmFuaWFyKQpsaWJyYXJ5KGd0c3VtbWFyeSkKbGlicmFyeShicm9vbSkKbGlicmFyeShyc2FtcGxlKQpsaWJyYXJ5KHlhcmRzdGljaykKbGlicmFyeShybXMpCgpsaWJyYXJ5KHZjZCkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoY2FyKQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkobm5ldCkKbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoR0dhbGx5KQoKbGlicmFyeSh0aWR5dmVyc2UpCgp0aGVtZV9zZXQodGhlbWVfYncoKSkKCmNvbmZsaWN0X3ByZWZlcigic2VsZWN0IiwgImRwbHlyIikKY29uZmxpY3RfcHJlZmVyKCJmaWx0ZXIiLCAiZHBseXIiKQpgYGAKCiMgQmFja2dyb3VuZAoKQSBtYWpvciByZWFzb24gZG9ncyBhcmUgYWJhbmRvbmVkIG9yIGJyb3VnaHQgdG8gc2hlbHRlcnMgaXMgZXhjZXNzaXZlIGJhcmtpbmcuIEl0IHdvdWxkIGJlIGhlbHBmdWwgaWYgYSBwb3RlbnRpYWwgZG9nIG93bmVyIGNvdWxkIHByZWRpY3QgYSBkb2cncyBiYXJraW5nIGxldmVsIGJhc2VkIG9uIHRoZSBkb2cncyBkcm9vbGluZyBsZXZlbCBhbmQgaG93IGdvb2QgdGhleSBhcmUgYXJvdW5kIHlvdW5nIGNoaWxkcmVuLCB3aGljaCBhcmUgdHdvIG90aGVyIGZhY3RvcnMgcG90ZW50aWFsIG93bmVycyBjb25zaWRlci4gUGVyaGFwcyBpZiBiYXJraW5nIGxldmVsIGNvdWxkIGJlIGFudGljaXBhdGVkIGluIGFkdmFuY2UsIHBvdGVudGlhbCBvd25lcnMgY291bGQgYXZvaWQgdGhlIGhlYXJ0YnJlYWsgb2YgYWJhbmRvbm1lbnQgZm9yIHRoZW0gYW5kIHRoZSBkb2cuCgpUaGUgQW1lcmljYW4gS2VubmVsIENsdWIgKEFLQykgZGlzcGxheXMgYSByYW5raW5nIG9mIGRvZyBicmVlZHMgYW5udWFsbHkuIE1hbnkgZmFjdG9ycyBnbyBpbnRvIHRoaXMgcmFua2luZyBzdWNoIGFzIHRyYWluYWJpbGl0eSwgYWRhcHRhYmlsaXR5LCBlbmVyZ3kgbGV2ZWwsIHNoZWRkaW5nIGxldmVsLCBhbmQgbWFueSBtb3JlLiBJZiBJIHdlcmUgdG8gcmFuayBhbGwgdGhlIGRvZyBicmVlZHMsIEkgd291bGQgcmFuayB0aGVtIGJ5IHRoZWlyIG91dGdvaW5nIG5hdHVyZSB3aXRoIG90aGVyIGRvZ3MgYW5kIHBlb3BsZS4gVGhlc2UgYXJlIHRyYWl0cyBJIHJlZ2FyZCBoaWdobHkuIFRoZXJlZm9yZSwgSSBzZWVrIHRvIGRldGVybWluZSBob3cgd2VsbCB0aGUgZmFjdG9ycyAnb3Blbm5lc3MgdG8gc3RyYW5nZXJzJyBhbmQgJ2dvb2Qgd2l0aCBvdGhlciBkb2dzJyBjYW4gcHJlZGljdCB0aGUgQUtDIDIwMjAgQnJlZWQgUmFua2luZy4gSSBjaG9zZSB0aGUgeWVhciAyMDIwIGJlY2F1c2UgdGhhdCBpcyB0aGUgbW9zdCByZWNlbnQgeWVhciByZXBvcnRlZC4KCiMgUmVzZWFyY2ggUXVlc3Rpb25zCgpJIHdpbGwgdXNlIGFuIG9yZGluYWwgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbCwgdGhlIEJhcmsgTW9kZWwsIHRvIHByZWRpY3QgYSBtdWx0aS1jYXRlZ29yaWNhbCBvdXRjb21lLCBiYXJraW5nIGxldmVsLCBpbiBvcmRlciB0byBhbnN3ZXIgdGhlIHF1ZXN0aW9uOgoKKioxLiBIb3cgZWZmZWN0aXZlbHkgY2FuIGRyb29saW5nIGxldmVsIGFuZCBnb29kbmVzcyB3aXRoIHlvdW5nIGNoaWxkcmVuIHByZWRpY3QgYSBkb2cgYnJlZWQncyBiYXJraW5nIGxldmVsPyoqCgpJIHdpbGwgdGhlbiB1c2UgYW4gb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcyBtZXRob2Qgb2YgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxpbmcgdG8gZml0IHRoZSBSYW5rIE1vZGVsIHRvIHByZWRpY3QgYSBxdWFudGl0YXRpdmUgb3V0Y29tZSwgMjAyMCBBS0MgQnJlZWQgUmFua2luZywgaW4gb3JkZXIgdG8gYW5zd2VyIHRoZSBxdWVzdGlvbjoKCioqMi4gSG93IGVmZmVjdGl2ZWx5IGNhbiBvcGVubmVzcyB0byBzdHJhbmdlcnMgYW5kIGdvb2RuZXNzIHdpdGggb3RoZXIgZG9ncyBwcmVkaWN0IGEgZG9nIGJyZWVkJ3MgMjAyMCBBS0MgcmFua2luZz8qKgoKIyBNeSBEYXRhCgpJIHdpbGwgYmUgY29tYmluaW5nIHR3byBkYXRhIHNldHMuIFRoZXNlIGRhdGEgYXJlIGZyb20gdGhlIFRpZHkgVHVlc2RheSBhcmNoaXZlLiBCb3RoIGRhdGEgc2V0cyB3ZXJlIHB1dCB0b2dldGhlciBmcm9tIHRoZSBBS0Mgd2Vic2l0ZSBieSBhIEdpdEh1YiB1c2VyIG5hbWVkIGtrYWtleS4gVGhlIGZpcnN0IGlzIGEgZGF0YSBzZXQgb2YgdmFyaW91cyB0cmFpdHMgb2YgZWFjaCBicmVlZC4gVGhlIHNlY29uZCBkYXRhIHNldCBpbmNsdWRlcyB0aGUgcmFua2luZyBvZiBlYWNoIGJyZWVkIGZvciBlYWNoIHllYXIuIEkgYW0gZXh0cmFjdGluZyB0aGUgMjAyMCBCcmVlZCBSYW5raW5nIGNvbHVtbiBmcm9tIHRoZSBzZWNvbmQgZGF0YSBzZXQgYW5kIGFkZGluZyBpdCB0byB0aGUgZmlyc3QgdG8gY3JlYXRlIG9uZSB0aWJibGUgdGhhdCBpbmNsdWRlcyBkb2cgYnJlZWQgdHJhaXRzIGFuZCB0aGVpciAyMDIwIHJhbmtpbmcuIEkgd2lsbCBuYXJyb3cgZG93biB0aGUgbGlzdCBvZiB0cmFpdHMgdG8gdGhvc2Ugb2YgaW50ZXJlc3QgdG8gbXkgcmVzZWFyY2ggcXVlc3Rpb25zOiBiYXJraW5nIGxldmVsLCBkcm9vbGluZyBsZXZlbCwgZ29vZCB3aXRoIHlvdW5nIGNoaWxkcmVuLCBvcGVubmVzcyB0byBzdHJhbmdlcnMsIGFuZCBnb29kIHdpdGggb3RoZXIgZG9ncy4KCkFuIGFkdmFudGFnZSBvZiB0aGVzZSBkYXRhIGlzIHRoYXQgdGhlIEFLQyBpcyBhIHJlbm93bmVkIHNvdXJjZSBvZiBrbm93bGVkZ2Ugb24gZG9nIGJyZWVkcyBhbmQgSSBmZWVsIEkgY2FuIHRydXN0IHRoZWlyIGFubnVhbCByYW5raW5ncyBhbmQgdHJhaXQgZGVzaWduYXRpb25zLiBBIGxpbWl0YXRpb24gb2YgdGhlc2UgZGF0YSBpcyB0aGF0IHRoZSBBS0Mgd2Vic2l0ZSBoYXMgYmVlbiB1cGRhdGVkIHNpbmNlIGl0IHdhcyBzY3JhcGVkIHRvIGNyZWF0ZSB0aGUgLmNzdiBmaWxlLCBhbmQgbm93IHRoZSB3ZWJzaXRlIGRpc3BsYXlzIGRpZmZlcmVudCB0cmFpdHMgdGhhbiBteSBkYXRhIHNldC4gU29tZSBvZiB0aGUgdHJhaXRzIHJlbWFpbmVkIHRoZSBzYW1lIGJ1dCBoYXZlIGRpZmZlcmVudCBuYW1lcy4gVGhpcyB3aWxsIG1ha2UgaXQgZGlmZmljdWx0IHRvIHVwZGF0ZSBteSBtb2RlbHMgaW4gdGhlIGZ1dHVyZS4gCgpIZXJlIGlzIGEgbGluayB0byB0aGUgZGF0YTogaHR0cHM6Ly9naXRodWIuY29tL2trYWtleS9kb2dfdHJhaXRzX0FLQy90cmVlL21haW4vZGF0YSAKCiMjIERhdGEgSW5nZXN0CgpGaXJzdCBJIHdpbGwgaW5nZXN0IHRoZSBkYXRhIHNldCBvZiB0cmFpdHMgaW50byBhIHRpYmJsZSBjYWxsZWQgYGRvZ190cmFpdHNgLCB0aGVuIHRoZSBkYXRhIHNldCBvZiBhbm51YWwgcmFua2luZ3MgaW50byBhIHRpYmJsZSBjYWxsZWQgYHJhbmtfMjAyMGAsIHNlbGVjdGluZyBvbmx5IHRoZSBicmVlZCBuYW1lIGFuZCB0aGUgMjAyMCByYW5raW5nLiAKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHNob3dfY29sX3R5cGVzID0gRkFMU0V9CmRvZ190cmFpdHMgPC0gcmVhZF9jc3YoaGVyZSgiZGF0YSIsICJicmVlZF90cmFpdHMuY3N2IikpICU+JQogIGNsZWFuX25hbWVzKCkKCnJhbmtfMjAyMCA8LSByZWFkX2NzdihoZXJlKCJkYXRhIiwgImJyZWVkX3JhbmtfYWxsLmNzdiIpKSAlPiUKICBjbGVhbl9uYW1lcygpCgpyYW5rXzIwMjAgPC0gcmFua18yMDIwICU+JQogIHNlbGVjdChicmVlZCwgeDIwMjBfcmFuaykKYGBgCgpOb3cgSSB3aWxsIGNvbWJpbmUgdGhlIHR3byBkYXRhIHNldHMgdG8gY3JlYXRlIG15IGFuYWx5dGljIHRpYmJsZS4gVGhlIGJyZWVkIG5hbWVzIGhhdmUgc3BhY2VzIGluIHRoZW0sIGFuZCBlYWNoIHNlcXVlbnRpYWwgc3BhY2UgaXMgcmVjb2duaXplZCBkaWZmZXJlbnRseSBieSBSLiBJIG5lZWQgdG8gcnVuIHRoZSBgbXV0YXRlYCBmdW5jdGlvbiAzIHRpbWVzIGluIGVhY2ggdGliYmxlIGluIG9yZGVyIHRvIGNoYW5nZSB0aGUgc3BhY2VzIHRvIHVuZGVyc2NvcmVzIGJlY2F1c2UgdGhlIG1heGltdW0gbnVtYmVycyBvZiBzcGFjZXMgaW4gYSBicmVlZCBuYW1lIGlzIDMuIEluIGBkb2dfdHJhaXRzYCBJIGFsc28gbmVlZCB0byBjaGFuZ2UgYW4gYXBvc3Ryb3BoZSB0byBhbiB1bmRlcnNjb3JlLiAKCmBgYHtyfQpkb2dfdHJhaXRzIDwtIGRvZ190cmFpdHMgJT4lCiAgbXV0YXRlKGJyZWVkID0gc3RyX3JlcGxhY2UoYnJlZWQsICIgIiwgIl8iKSkgJT4lCiAgbXV0YXRlKGJyZWVkID0gc3RyX3JlcGxhY2UoYnJlZWQsICIgIiwgIl8iKSkgJT4lCiAgbXV0YXRlKGJyZWVkID0gc3RyX3JlcGxhY2UoYnJlZWQsICIgIiwgIl8iKSkgJT4lCiAgbXV0YXRlKGJyZWVkID0gc3RyX3JlcGxhY2UoYnJlZWQsICLigJkiLCAiXyIpKQpgYGAKCmBgYHtyfQpyYW5rXzIwMjAgPC0gcmFua18yMDIwICU+JQogICAgbXV0YXRlKGJyZWVkID0gc3RyX3JlcGxhY2UoYnJlZWQsICIgIiwgIl8iKSkgJT4lCiAgICBtdXRhdGUoYnJlZWQgPSBzdHJfcmVwbGFjZShicmVlZCwgIiAiLCAiXyIpKSAlPiUKICAgIG11dGF0ZShicmVlZCA9IHN0cl9yZXBsYWNlKGJyZWVkLCAiICIsICJfIikpCmBgYAoKTm93IHRoYXQgdGhlIGJyZWVkIG5hbWVzIGluIGJvdGggZGF0YSBzZXRzIG1hdGNoIGVhY2ggb3RoZXIsIEkgY2FuIGpvaW4gdGhlIHR3byBkYXRhIHNldHMgYnkgYGJyZWVkYC4gSSB3aWxsIGRvIHNvIGludG8gYSB0aWJibGUgY2FsbGVkIGBkb2dzX3Jhd2AuIFRoZW4gSSB3aWxsIGNoZWNrIHRoZSBudW1iZXIgb2Ygcm93cyAoaWUgYnJlZWRzKSBpbiBlYWNoIGRhdGEgc2V0LiAKCmBgYHtyfQpkb2dzX3JhdyA8LSBsZWZ0X2pvaW4oZG9nX3RyYWl0cywgcmFua18yMDIwLCBieSA9ICJicmVlZCIpCgpkaW0oZG9nX3RyYWl0cykKZGltKHJhbmtfMjAyMCkKZGltKGRvZ3NfcmF3KQpgYGAKClRoZSBudW1iZXIgb2Ygcm93cyBpbiBlYWNoIGRhdGEgc2V0IGRvIG5vdCBtYXRjaC4gVGhpcyBpcyBiZWNhdXNlIEkgam9pbmVkIHRoZW0gYnkgYGJyZWVkYCBzbyB0aGUgY29tYmluZWQgdGliYmxlIG9ubHkgaW5jbHVkZXMgdGhvc2UgYnJlZWRzIHdoaWNoIGV4aXN0ZWQgaW4gYm90aCBkYXRhIHNldHMuIFRoZXJlIHdlcmUgMyBicmVlZHMgaW4gYHJhbmtfMjAyMGAgdGhhdCB3ZXJlIG5vdCBpbiBgZG9nX3RyYWl0c2AuIEkgaGF2ZSBhIHN1YnN0YW50aWFsIG51bWJlciBvZiBicmVlZHMgcmVtYWluaW5nLCBhbmQgSSBhbSBkb2luZyBhIGNvbXBsZXRlIGNhc2UgYW5hbHlzaXMsIHNvIEkgYW0gbm90IHdvcnJpZWQgYWJvdXQgdGhpcy4KClRoZSBudW1iZXIgb2YgY29sdW1ucyBpcyB3aGF0IEkgZXhwZWN0ZWQuIEkgYW0gYWRkaW5nIDIgY29sdW1ucyBpbnRvIGEgdGliYmxlIHdpdGggMTcgY29sdW1ucywgYnV0IDEgbWF0Y2hlcywgc28gdGhpcyBjcmVhdGVzIGEgdG90YWwgb2YgMTkgY29sdW1ucy4KCkkgd2lsbCBzYXZlIG15IDMgdGliYmxlczogdGhlIHNldCBvZiB0cmFpdHMsIHRoZSBzZXQgb2YgMjAyMCByYW5raW5ncywgYW5kIHRoZSBjb21iaW5lZCBzZXQuIEkgd2lsbCByZW1vdmUgdGhlIGluaXRpYWwgMiBkYXRhIHNldHMgbm93IHRoYXQgdGhleSBhcmUgc2F2ZWQsIGluIG9yZGVyIHRvIGtlZXAgbXkgd29yayBzcGFjZSBjbGVhbi4KCmBgYHtyfQpzYXZlUkRTKGRvZ190cmFpdHMsIGZpbGUgPSBoZXJlKCJkYXRhIiwgImRvZ190cmFpdHMucmRzIikpCnNhdmVSRFMocmFua18yMDIwLCBmaWxlID0gaGVyZSgiZGF0YSIsICJyYW5rXzIwMjAucmRzIikpCnNhdmVSRFMoZG9nc19yYXcsIGZpbGUgPSBoZXJlKCJkYXRhIiwgImRvZ3NfcmF3LnJkcyIpKQoKcm0oZG9nX3RyYWl0cywgcmFua18yMDIwKQpgYGAKCkluIG9yZGVyIHRvIGhlbHAgbXkgd29yayBnbyBmYXN0ZXIgb24gc3Vic2VxdWVudCBzZXNzaW9ucywgSSB3aWxsIGluY2x1ZGUgdGhlIGZvbGxvd2luZyBjb2RlIHRoYXQgd2lsbCBpbmdlc3QgbXkgZGF0YSBmcm9tIHRoZSBzYXZlZCwgY29tYmluZWQgLnJkcyBmaWxlLgoKYGBge3J9CmRvZ3NfcmF3IDwtIHJlYWRSRFMoaGVyZSgiZGF0YSIsICJkb2dzX3Jhdy5yZHMiKSkKYGBgCgojIyBUaWR5aW5nLCBEYXRhIENsZWFuaW5nIGFuZCBEYXRhIE1hbmFnZW1lbnQKCkFzIG9yaWdpbmFsbHkgbG9hZGVkLCB0aGUgYGRvZ3NfcmF3YCBkYXRhIGNvbnRhaW4gYHIgbnJvdyhkb2dzX3JhdylgIHJvd3MgYW5kIGByIG5jb2woZG9nc19yYXcpYCBjb2x1bW5zLiBJIHdpbGwgdmlldyBteSB2YXJpYWJsZXMgb2YgaW50ZXJlc3QgdG8gc2VlIHdoYXQgdGhlaXIgdHlwZXMgYXJlIHNvIEkgY2FuIHN0YXJ0IGNsZWFuaW5nIHRoZW0gdXAuCgpgYGB7cn0KZG9nc19yYXcgJT4lIAogIHNlbGVjdChicmVlZCwgZHJvb2xpbmdfbGV2ZWwsIGJhcmtpbmdfbGV2ZWwsIAogICAgICAgICBnb29kX3dpdGhfeW91bmdfY2hpbGRyZW4sIG9wZW5uZXNzX3RvX3N0cmFuZ2VycywgCiAgICAgICAgIGdvb2Rfd2l0aF9vdGhlcl9kb2dzLCB4MjAyMF9yYW5rKSAlPiUKICBnbGltcHNlKCkKYGBgCgpBbGwgbXkgdmFyaWFibGVzIGFyZSAnZGJsJyB0eXBlLiBJIG5lZWQgbW9zdCBvZiB0aGVtIHRvIGJlIGZhY3RvcnMsIGFuZCBzb21lIHRvIGJlIGNoYXJhY3RlcnMuIEkgd2lsbCBjb21wbGV0ZSB0aGVzZSBjb252ZXJzaW9ucyBpbiB0aGUgZm9sbG93aW5nIGNvZGUuIEkgd2lsbCBhbHNvIG1ha2UgYGJhcmtgIGFuIG9yZGVyZWQgZmFjdG9yIHNvIGl0IHdvcmtzIHdlbGwgaW4gbXkgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4gSSB3aWxsIHJlbmFtZSBteSB2YXJpYWJsZXMgYW5kIHRoZWlyIGNhdGVnb3JpZXMgdG8gYmUgbW9yZSBjbGVhciBhbmQgY29uY2lzZS4gSSB3aWxsIG5lZWQgdG8gY29sbGFwc2Ugc29tZSBvZiB0aGUgY2F0ZWdvcmllcyBvZiBteSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgaW4gb3JkZXIgdG8gaGF2ZSBlYWNoIGNhdGVnb3J5IGNvbnRhaW4gYXQgbGVhc3QgMjUgb2JzZXJ2YXRpb25zLiBJIHdpbGwgYWxzbyByZWxldmVsIG15IGNhdGVnb3JpZXMgYmVjYXVzZSBtYW55IGFyZSBvcmRpbmFsLCBlbHNlIHRoZXkgd2lsbCBkZWZhdWx0IHRvIGFscGhhYmV0aWNhbCBvcmRlci4gSSB3aWxsIHBsYWNlIHRoZSBjbGVhbmVkIHZhcmlhYmxlcyBpbiBhIG5ldyB0aWJibGUgY2FsbGVkIGBkb2dzYC4gVGhlbiBJIHdpbGwgcmVtb3ZlIHRoZSB0aWJibGUgb2YgcmF3IGRhdGEgc28gYXMgdG8ga2VlcCBteSB3b3JrIHNwYWNlIGNsZWFuLgoKYGBge3J9CmRvZ3MgPC0gZG9nc19yYXcgJT4lCiAgcmVuYW1lKGJhcmsgPSBiYXJraW5nX2xldmVsLAogICAgICAgICBkcm9vbCA9IGRyb29saW5nX2xldmVsLAogICAgICAgICBraWRzID0gZ29vZF93aXRoX3lvdW5nX2NoaWxkcmVuLAogICAgICAgICBkb2dfZnJpZW5kcyA9IGdvb2Rfd2l0aF9vdGhlcl9kb2dzLAogICAgICAgICBzdHJhbmdlcnMgPSBvcGVubmVzc190b19zdHJhbmdlcnMsCiAgICAgICAgIHJhbmsgPSB4MjAyMF9yYW5rKSAlPiUKICBtdXRhdGUoYmFyayA9IGZhY3RvcihiYXJrLCBvcmRlcmVkID0gVFJVRSksCiAgICAgICAgIGJhcmsgPSBmY3RfcmVjb2RlKGJhcmssCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJPbmx5IHRvIGFsZXJ0IiA9ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9ubHkgdG8gYWxlcnQiID0gIjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiUXVpZXQiID0gIjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTG91ZCIgPSAiNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJWZXJ5IHZvY2FsIiA9ICI1IiksCiAgICAgICAgIGJhcmsgPSBmY3RfcmVsZXZlbChiYXJrLCAiT25seSB0byBhbGVydCIsICJRdWlldCIsICJMb3VkIiwgIlZlcnkgdm9jYWwiKSwKICAgICAgICAgZHJvb2wgPSBmYWN0b3IoZHJvb2wpLAogICAgICAgICBkcm9vbCA9IGZjdF9yZWNvZGUoZHJvb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRHJ5IiA9ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWd1bGFyIiA9ICIyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEcm9vbGVyIiA9ICIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEcm9vbGVyIiA9ICI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEcm9vbGVyIiA9ICI1IiksCiAgICAgICAgIGRyb29sID0gZmN0X3JlbGV2ZWwoZHJvb2wsICJEcnkiLCAiUmVndWxhciIsICJEcm9vbGVyIiksCiAgICAgICAgIGtpZHMgPSBmYWN0b3Ioa2lkcyksCiAgICAgICAgIGtpZHMgPSBmY3RfcmVjb2RlKGtpZHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJBZHVsdHMgb25seSIgPSAiMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJBZHVsdHMgb25seSIgPSAiMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJLaWQgZnJpZW5kbHkiID0gIjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiS2lkIGZyaWVuZGx5IiA9ICI1IiksCiAgICAgICAgIGtpZHMgPSBmY3RfcmVsZXZlbChraWRzLCAiQWR1bHRzIG9ubHkiLCAiS2lkIGZyaWVuZGx5IiksCiAgICAgICAgIGRvZ19mcmllbmRzID0gZmFjdG9yKGRvZ19mcmllbmRzKSwKICAgICAgICAgZG9nX2ZyaWVuZHMgPSBmY3RfcmVjb2RlKGRvZ19mcmllbmRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiTG9uZSB3b2xmIiA9ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxvbmUgd29sZiIgPSAiMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJMb25lIHdvbGYiID0gIjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiUGFjayBhbmltYWwiID0gIjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiUGFjayBhbmltYWwiID0gIjUiKSwKICAgICAgICAgZG9nX2ZyaWVuZHMgPSBmY3RfcmVsZXZlbChkb2dfZnJpZW5kcywgIkxvbmUgd29sZiIsICJQYWNrIGFuaW1hbCIpLAogICAgICAgICBzdHJhbmdlcnMgPSBmYWN0b3Ioc3RyYW5nZXJzKSwKICAgICAgICAgc3RyYW5nZXJzID0gZmN0X3JlY29kZShzdHJhbmdlcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJTdHJhbmdlciBkYW5nZXIiID0gIjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiU3RyYW5nZXIgZGFuZ2VyIiA9ICIyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIlN0cmFuZ2VyIGRhbmdlciIgPSAiMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJGcmllbmRseSIgPSAiNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdXRnb2luZyIgPSAiNSIpLAogICAgICAgICBzdHJhbmdlcnMgPSBmY3RfcmVsZXZlbChzdHJhbmdlcnMsICJTdHJhbmdlciBkYW5nZXIiLCAiRnJpZW5kbHkiLCAiT3V0Z29pbmciKSkgJT4lCiAgc2VsZWN0KGJyZWVkLCByYW5rLCBiYXJrLCBkcm9vbCwga2lkcywgZG9nX2ZyaWVuZHMsIHN0cmFuZ2VycykKCnJtKGRvZ3NfcmF3KQpgYGAKCiMjIE1pc3NpbmduZXNzCgpJIG5vdGljZWQgaW4gYSBkYXRhIHN1bW1hcnkgdGhhdCBJIGhhdmUgYW4gZXh0cmEgbGV2ZWwgaW4gZWFjaCBvZiBteSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgdGhhdCBJIGRpZCBub3QgZXhwZWN0LCBkZXNpZ25hdGVkICcwJy4gSW4gb3JkZXIgdG8gZGV0ZXJtaW5lIHdoaWNoIGJyZWVkIGlzIGNhdXNpbmcgdGhpcyBpc3N1ZSwgSSB3aWxsIGFyYml0cmFyaWx5IGNob29zZSBgYmFya2AuCgpgYGB7cn0KZG9ncyAlPiUKICBmaWx0ZXIoYmFyayA9PSAwKQpgYGAKClBsb3R0IGhvdW5kcyB3ZXJlIG5vdCBnaXZlbiBzY29yZXMgZm9yIGFueSBvZiB0aGUgdHJhaXRzIGluIHRoZSB0cmFpdCBkYXRhIHNldC4gSSB3aWxsIHRyZWF0IHRoZXNlIGFzIG1pc3NpbmcgZGF0YSwgYW5kIHRoZXJlZm9yZSByZW1vdmUgdGhpcyByb3cgZnJvbSBteSB0aWJibGUgZm9yIG1pc3Npbmcgb3V0Y29tZSBkYXRhIGluIG15IGNvbXBsZXRlIGNhc2UgYW5hbHlzaXMuCgpgYGB7cn0KZG9ncyA8LSBkb2dzICU+JQogIGZpbHRlcihiYXJrICE9ICIwIikKCmRpbShkb2dzKQpgYGAKCkkgYW0gbGVmdCB3aXRoIDE5NCByb3dzIGFzIGV4cGVjdGVkLiAKCkkgd2lsbCBjaGVjayB0aGUgcmVzdCBvZiBteSBkYXRhIGZvciBtaXNzaW5nbmVzcy4KCmBgYHtyIG1pc3NpbmduZXNzfQptaXNzX3Zhcl9zdW1tYXJ5KGRvZ3MpCmBgYAoKSSBoYXZlIG5vIG1pc3NpbmcgZGF0YS4KCkkgd2lsbCBhZGQgcm93IG51bWJlcnMgc28gZWFjaCBicmVlZCBoYXMgYSB1bmlxdWUgaWRlbnRpZmllci4gVGhpcyB3aWxsIG5vdCBuZWNlc3NhcmlseSBlcXVhbCB0aGUgcmFua2luZyBhcyBJIHJlbW92ZWQgUGxvdHQgSG91bmRzIGFzIGFib3ZlLiBJIHdpbGwgZHJvcCBhbnkgZW1wdHkgY2F0ZWdvcmllcy4KCmBgYHtyfQpkb2dzIDwtIGRvZ3MgJT4lCiAgbXV0YXRlKGlkID0gcm93X251bWJlcigpKSAlPiUKICBkcm9wbGV2ZWxzKCkKYGBgCgojIyBUaWRpZWQgVGliYmxlCgpNeSB0aWJibGUgYGRvZ3NgIGNvbnRhaW5zIGByIG5yb3coZG9ncylgIHJvd3MgKGJyZWVkcykgYW5kIGByIG5jb2woZG9ncylgIGNvbHVtbnMgKHZhcmlhYmxlcykuIEVhY2ggdmFyaWFibGUgaXMgY29udGFpbmVkIGluIGEgY29sdW1uLCBhbmQgZWFjaCByb3cgcmVwcmVzZW50cyBhIHNpbmdsZSBicmVlZC4gQWxsIHZhcmlhYmxlcyBub3cgaGF2ZSBhcHByb3ByaWF0ZSB0eXBlcywgbmFtZXMsIGFuZCBjYXRlZ29yaWVzLgoKYGBge3IgbGlzdF90aGVfdGliYmxlfQpkb2dzCmBgYAoKSSB3aWxsIHNhdmUgbXkgdGlkaWVkIHRpYmJsZSBhcyBhbiBSIGRhdGEgc2V0LgoKYGBge3J9CnNhdmVSRFMoZG9ncywgZmlsZSA9IGhlcmUoImRhdGEiLCAiZG9ncy5yZHMiKSkKYGBgCgpJbiBvcmRlciB0byBoZWxwIG15IHdvcmsgZ28gZmFzdGVyIG9uIHN1YnNlcXVlbnQgc2Vzc2lvbnMsIEkgd2lsbCBpbmNsdWRlIHRoZSBmb2xsb3dpbmcgY29kZSB0aGF0IHdpbGwgaW5nZXN0IG15IGNsZWFuZWQgZGF0YSBmcm9tIHRoZSBzYXZlZCAucmRzIGZpbGUuCgpgYGB7cn0KZG9ncyA8LSByZWFkUkRTKGhlcmUoImRhdGEiLCAiZG9ncy5yZHMiKSkKYGBgCgojIENvZGUgQm9vayBhbmQgQ2xlYW4gRGF0YSBTdW1tYXJ5CgpUaGUgZGF0YSBpbiBteSBjb21wbGV0ZSBgZG9nc2Agc2FtcGxlIGNvbnNpc3Qgb2YgYHIgbnJvdyhkb2dzKWAgZG9nIGJyZWVkcyBvbiB0aGUgQW1lcmljYW4gS2VubmVsIENsdWIgd2Vic2l0ZS4gVGhlIEFLQyBjb252ZXlzIG5vIGRlZmluZWQgZXhjbHVzaW9uIGNyaXRlcmlhLiBEYXRhIGFyZSBleGNsdWRlZCBmcm9tIG15IHRpYmJsZSBmb3Igb3V0Y29tZSBtaXNzaW5nbmVzcyBvbmx5LiBPZiB0aGUgYHIgbnJvdyhkb2dzKWAgc3ViamVjdHMsIGByIG5fY2FzZV9jb21wbGV0ZShkb2dzKWAgaGF2ZSBjb21wbGV0ZSBkYXRhIG9uIGFsbCB2YXJpYWJsZXMgbGlzdGVkIGJlbG93LiBNeSBsb2dpc3RpYyByZWdyZXNzaW9uICoqb3V0Y29tZSoqIGlzIGBiYXJrYCwgd2hpY2ggaXMgYSBtdWx0aS1jYXRlZ29yaWNhbCBpbmRpY2F0b3Igb2YgdGhlIGJyZWVkJ3MgYmFya2luZyBsZXZlbC4gTXkgb3JkaW5hcnkgbGVhc3Qgc3F1YXJlcyBsaW5lYXIgcmVncmVzc2lvbiAqKm91dGNvbWUqKiBpcyBgcmFua2Agd2hpY2ggaXMgdGhlIDIwMjAgQUtDIEJyZWVkIFJhbmtpbmcuIEkgaGF2ZSBhIHZhcmlhYmxlIGNhbGxlZCBgaWRgIHdoaWNoIGlzIGEgdW5pcXVlIGlkZW50aWZpZXIgZm9yIGVhY2ggcm93IGFuZCB0aGUgdmFyaWFibGUgYGJyZWVkYCB3aGljaCBpZGVudGlmaWVzIHRoZSBicmVlZCB0aGUgZGF0YSBkZXNjcmliZS4gQWxsIG90aGVyIHZhcmlhYmxlcyBsaXN0ZWQgYmVsb3cgd2lsbCBzZXJ2ZSBhcyAqKnByZWRpY3RvcnMqKiBmb3IgbXkgbW9kZWxzLgoKIyMgTWFpbiBDb2RlYm9vayBUYWJsZQoKVmFyaWFibGVzIGluY2x1ZGVkIGluIG15IGFuYWx5c2VzIGFyZSBzdW1tYXJpemVkIGluIHRoZSBmb2xsb3dpbmcgdGFibGUuIEkgd2lsbCBvbWl0IGlkZW50aWZ5aW5nIHZhcmlhYmxlcyBpbiBmYXZvciBvZiBiZWluZyBjb25jaXNlLgoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0KZG9ncyAlPiUgCiAgc2VsZWN0KC1pZCwgLWJyZWVkKSAlPiUKICAgIHRibF9zdW1tYXJ5KC4sCiAgICAgICAgbGFiZWwgPSBsaXN0KAogICAgICAgICAgICBiYXJrID0gIkJhcmtpbmcgbGV2ZWwiLAogICAgICAgICAgICBkcm9vbCA9ICJEcm9vbGluZyBsZXZlbCIsCiAgICAgICAgICAgIGtpZHMgPSAiR29vZCB3aXRoIFlvdW5nIENoaWxkcmVuIiwKICAgICAgICAgICAgc3RyYW5nZXJzID0gIk9wZW5uZXNzIHRvIFN0cmFuZ2VycyIsCiAgICAgICAgICAgIGRvZ19mcmllbmRzID0gIkdvb2Qgd2l0aCBPdGhlciBEb2dzIiwKICAgICAgICAgICAgcmFuayA9ICIyMDIwIEFtZXJpY2FuIEtlbm5lbCBDbHViIGJyZWVkIHJhbmtpbmciKSwKICAgICAgICBzdGF0ID0gbGlzdChhbGxfY29udGludW91cygpIH4gCiAgICAgICAgICAgICAgICAie21lZGlhbn0gW3ttaW59IHRvIHttYXh9XSIgKSkKYGBgCgpNeSBkYXRhIHNldCBoYXMgMTk0IHN1YmplY3RzIHdpdGggQUtDIEJyZWVkIFJhbmtpbmcgcmFuZ2luZyBmcm9tIDEgdG8gMTk1LiBUaGUgYnJlYWtkb3duIG9mIGVhY2ggdmFyaWFibGUgKG90aGVyIHRoYW4gaWRlbnRpZmllcnMpIGlzIGxpc3RlZCBhYm92ZSBhbmQgd2lsbCBiZSBkZXNjcmliZWQgaW4gZGVlcGVyIGRldGFpbCBiZWxvdy4KCiMjIERhdGEgRGlzdHJpYnV0aW9uIERlc2NyaXB0aW9uCgpUaGlzIGRlc2NyaWJlcyB0aGUgZGlzdHJpYnV0aW9uIG9mIG15IGRhdGEuCgpgYGB7cn0KZG9ncyAlPiUgZGVzY3JpYmUoLikgJT4lIEhtaXNjOjpodG1sKCkKYGBgCgojIEFuYWx5c2lzIDE6IEJhcmsgUHJlZGljdGlvbiBNb2RlbAoKSSB3aWxsIHVzZSBhbiBvcmRpbmFsIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwgY2FsbGVkIGBCYXJrIE1vZGVsYCB0byBwcmVkaWN0IGJhcmtpbmcgbGV2ZWwsIGBiYXJrYCwgdXNpbmcgZHJvb2xpbmcgbGV2ZWwsIGBkcm9vbGAsIGFuZCBnb29kbmVzcyB3aXRoIHlvdW5nIGNoaWxkcmVuLCBga2lkc2AuCgojIyBFeHBsb3JpbmcgdGhlIERhdGEKCiMjIyBNeSBPdXRjb21lOiBgYmFya2AKCmBiYXJrYCBpcyBhIDQtY2F0ZWdvcnkgdmFyaWFibGUuIEkgd2FudCB0byB2aXN1YWxpemUgd2hhdCBwZXJjZW50YWdlIG9mIGJyZWVkcyBhcmUgaW4gZWFjaCBjYXRlZ29yeS4gCgpgYGB7cn0KZ2dwbG90KGRvZ3MsIGFlcyh4ID0gYmFyaywgZmlsbCA9IGJhcmspKSArIAogICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKS9zdW0oLi5jb3VudC4uKSksIGNvbCA9ICJibGFjayIpICsKICAgIGdlb21fdGV4dChhZXMoeSA9ICguLmNvdW50Li4pL3N1bSguLmNvdW50Li4pLCAKICAgICAgICAgICAgICAgICAgbGFiZWwgPSBzY2FsZXM6OnBlcmNlbnQoKC4uY291bnQuLikgLyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bSguLmNvdW50Li4pKSksCiAgICAgICAgICAgICAgc3RhdCA9ICJjb3VudCIsIHZqdXN0ID0gMS41LCAKICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSA1KSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJ1UHUiKSArCiAgICBndWlkZXMoZmlsbCA9ICJub25lIikgKyAKICAgIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIEJhcmtpbmcgTGV2ZWwgQWNyb3NzIENhdGVnb3JpZXMiLAogICAgICAgICBzdWJ0aXRsZSA9ICJPdXRjb21lIERpc3RyaWJ1dGlvbiBmb3IgYGJhcmtfbW9kZWxgIiwKICAgICAgICAgeCA9ICJCYXJraW5nIExldmVsIiwKICAgICAgICAgeSA9ICJQZXJjZW50YWdlIiwKICAgICAgICAgY2FwdGlvbiA9ICIxOTQgYnJlZWRzIGluIGBkb2dzYCIpCmBgYAoKSGFsZiBvZiB0aGUgYnJlZWRzIGhhdmUgYSBiYXJraW5nIGxldmVsIG9mICdxdWlldCcuIFRoZSBvdGhlciBoYWxmIGlzIGRpc3RyaWJ1dGVkIGZhaXJseSBldmVubHkgYWNyb3NzIHRoZSBvdGhlciAzIGNhdGVnb3JpZXM6ICdvbmx5IHRvIGFsZXJ0JywgICdsb3VkJywgYW5kICd2ZXJ5IHZvY2FsJy4gVGhlIGNhdGVnb3J5ICd2ZXJ5IHZvY2FsJyBoYXMgdGhlIGZld2VzdCBicmVlZHMuCgojIyMgTXkgUHJlZGljdG9yczogYGRyb29sYCBhbmQgYGtpZHNgCgpJIHdpbGwgYmUgdXNpbmcgZHJvb2xpbmcgbGV2ZWwgYW5kIGdvb2RuZXNzIHdpdGggeW91bmcgY2hpbGRyZW4gaW4gb3JkZXIgdG8gcHJlZGljdCBiYXJraW5nIGxldmVsLiBJIHdhbnQgdG8gdmlzdWFsaXplIHRoZSBzcHJlYWQgb2YgYmFya2luZyBsZXZlbCB3aXRoaW4gZWFjaCBjYXRlZ29yeSBvZiBteSBwcmVkaWN0b3JzLiAKCmBgYHtyIGZpZy5oZWlnaHQ9MTB9CnAxIDwtIGdncGxvdChkb2dzLCAKICAgICAgIGFlcyh4ID0gZHJvb2wsIGZpbGwgPSBiYXJrKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgY29sID0gImJsYWNrIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiR25CdSIpICsKICBsYWJzKHRpdGxlID0gIkRyb29saW5nIExldmVsIiwKICAgICAgIHggPSAiRHJvb2xpbmcgTGV2ZWwiLAogICAgICAgeSA9ICJQcm9wb3J0aW9uIiwKICAgICAgIGZpbGwgPSAiQmFya2luZyBMZXZlbCIpCgpwMiA8LSBnZ3Bsb3QoZG9ncywgCiAgICAgICBhZXMoeCA9IGtpZHMsIGZpbGwgPSBiYXJrKSkgKwogIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgY29sID0gImJsYWNrIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiT3JSZCIpICsKICBsYWJzKHRpdGxlID0gIkdvb2RuZXNzIHdpdGggWW91bmcgQ2hpbGRyZW4iLAogICAgICAgeCA9ICJHb29kIHdpdGggWW91bmcgQ2hpbGRyZW4iLAogICAgICAgeSA9ICJQcm9wb3J0aW9uIiwKICAgICAgIGZpbGwgPSAiQmFya2luZyBMZXZlbCIpCgpwMSAvIHAyICsKICBwbG90X2Fubm90YXRpb24odGl0bGUgPSAiU3ByZWFkIG9mIEJhcmtpbmcgTGV2ZWwgKE91dGNvbWUpIFdpdGhpbiBFYWNoIFByZWRpY3RvciBDYXRlZ29yeSIsCiAgICAgICBjYXB0aW9uID0gIjE5NCBicmVlZHMgaW4gYGRvZ3NgIikKYGBgCgpUaGUgcmF0aW8gb2YgdGhlIGJhcmtpbmcgbGV2ZWxzIHZpc3VhbGx5IGFwcGVhcnMgY29uc2lzdGVudCB3aXRoaW4gZWFjaCBjYXRlZ29yeSBvZiBkcm9vbGluZyBsZXZlbC4gUGVyaGFwcyBkcm9vbGluZyBsZXZlbCB3aWxsIG5vdCBiZSBhbiBlZmZlY3RpdmUgcHJlZGljdG9yIG9mIGJhcmtpbmcgbGV2ZWwuIEZvciBteSBwcmVkaWN0b3IgYGtpZHNgLCBkZXNjcmliaW5nIGdvb2RuZXNzIHdpdGggeW91bmcgY2hpbGRyZW4sIHRoZSBicmVlZHMgd2hvIGFyZSBraWQgZnJpZW5kbHkgaGF2ZSBtb3JlICd2ZXJ5IHZvY2FsJyBicmVlZHMgYW5kIGZld2VyICdvbmx5IHRvIGFsZXJ0JyBicmVlZHMgdGhhbiB0aGUgYnJlZWRzIHdobyBwcmVmZXIgYWR1bHRzIG9ubHkuIFRoaXMgcHJlZGljdG9yIG1heSBiZSBtb3JlIGVmZmVjdGl2ZS4gCgpJIHdpbGwgdmlldyBhIHRhYmxlIG9mIGEgYnJlYWtkb3duIG9mIGhvdyBtYW55IGJyZWVkcyBhcmUgaW4gZWFjaCBjYXRlZ29yeSwgY29tYmluaW5nIGJvdGggb2YgbXkgcHJlZGljdG9ycy4KCmBgYHtyfQpkb2dzICU+JQogIHRhYnlsKGRyb29sLCBraWRzKSAlPiUKICBhZG9ybl90b3RhbHMoYygicm93IiwgImNvbCIpKSAlPiUKICBhZG9ybl9wZXJjZW50YWdlcygicm93IikgJT4lCiAgYWRvcm5fcGN0X2Zvcm1hdHRpbmcocm91bmRpbmcgPSAiaGFsZiB1cCIsIGRpZ2l0cyA9IDApICU+JQogIGFkb3JuX25zKCkgJT4lCiAga2FibGUoKQpgYGAKCkdvb2RuZXNzIHdpdGggeW91bmcgY2hpbGRyZW4gaXMgYWJvdXQgZXZlbiBiZXR3ZWVuIHRoZSB0d28gY2F0ZWdvcmllczogNTMlIG9mIGJyZWVkcyBwcmVmZXIgYWR1bHRzIG9ubHkgYW5kIDQ3JSBhcmUga2lkIGZyaWVuZGx5LiBXaXRoaW4gdGhlc2UgY2F0ZWdvcmllcywgdGhlIHNwcmVhZCBvZiBkcm9vbGluZyBsZXZlbCBkaWZmZXJzLiBXaXRoaW4gdGhlIGdyb3VwIG9mIGJyZWVkcyB3aG8gcHJlZmVyIGFkdWx0cyBvbmx5LCBtb3JlIHRoYW4gaGFsZiBvZiB0aGUgYnJlZWRzIGFyZSBpbiB0aGUgJ2RyeScgY2F0ZWdvcnkgZm9yIGRyb29saW5nIGxldmVsICg1NiBicmVlZHMgb3V0IG9mIDEwMyBicmVlZHMpLiBXaXRoaW4gdGhlIGdyb3VwIG9mIGJyZWVkcyB3aG8gYXJlIGtpZCBmcmllbmRseSwgdGhlcmUgaXMgYW4gZXZlbiBzcGxpdCBiZXR3ZWVuICdkcnknIGFuZCAncmVndWxhcicgZHJvb2xpbmcgbGV2ZWwgYnJlZWRzLiBJbiBib3RoLCB0aGUgbWlub3JpdHkgb2YgYnJlZWRzIGFyZSAnZHJvb2xlcnMnLgoKSSB3aWxsIGNoZWNrIGZvciBjb2xsaW5lYXJpdHkgYmV0d2VlbiBteSBwcmVkaWN0b3JzLiAKCmBgYHtyfQpjYXI6OnZpZihwb2xyKGJhcmsgfiBkcm9vbCArIGtpZHMsIGRhdGEgPSBkb2dzLCBIZXNzID0gVFJVRSkpCmBgYAoKVGhlc2UgdmFyaWFuY2UgaW5mbGF0aW9uIGZhY3RvcnMgYXJlIGEgYml0IGhpZ2hlciB0aGFuIEkgd291bGQgbGlrZSwgc28gSSB3aWxsIGtlZXAgY29sbGluZWFyaXR5IGluIG1pbmQgYXMgSSBmaXQgYW5kIGV2YWx1YXRlIG15IG1vZGVsLgoKIyMjIFRyYW5zZm9ybWF0aW9uIEFuYWx5c2lzICYgQXNzZXNzbWVudCBmb3IgTm9ubGluZWFyIFRlcm1zCgpJIHdpbGwgbm90IHRyYW5zZm9ybSBteSBvdXRjb21lIGJlY2F1c2UgaXQgaXMgY2F0ZWdvcmljYWwuIEkgd2lsbCBub3QgY2hlY2sgYSBTcGVhcm1hbiByaG9eMl4gcGxvdCBiZWNhdXNlIEkgaGF2ZSBvbmx5IDEgb3B0aW9uIG9mIGEgbm9ubGluZWFyIHRlcm0sIGFuIGludGVyYWN0aW9uIGJldHdlZW4gbXkgMiBjYXRlZ29yaWNhbCBwcmVkaWN0b3JzLCB3aGljaCBJIHdpbGwgdXNlLgoKIyMgRml0dGluZyB0aGUgTW9kZWw6IEEgUHJvcG9ydGlvbmFsLU9kZHMgQ3VtdWxhdGl2ZS1Mb2dpdCBNb2RlbAoKRmlyc3QgSSB3aWxsIGVuc3VyZSBteSBvdXRjb21lIGlzIGFuIG9yZGVyZWQgZmFjdG9yLgoKYGBge3IgRW5zdXJlIG15IG91dGNvbWUgaXMgYW4gb3JkZXJlZCBmYWN0b3J9CmlzLm9yZGVyZWQoZG9ncyRiYXJrKQpgYGAKCkdyZWF0IC0gaXQgaXMuICBOb3cgSSB3aWxsIGZpdCBteSBtb2RlbCB1c2luZyB0aGUgYHBvbHJgIGVuZ2luZS4gSSB3aWxsIGtlZXAgaW4gbWluZCB0aGF0IHRoZSBkaXN0YW5jZSBiZXR3ZWVuIGVhY2ggY2F0ZWdvcnkgbWF5IG5vdCBiZSBlcXVhbCwgaWUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBicmVlZHMgd2hvIGJhcmsgJ29ubHkgdG8gYWxlcnQnIHZzICdxdWlldCcgYnJlZWRzIGlzIG5vdCBuZWNlc3NhcmlseSB0aGUgc2FtZSBhcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGJyZWVkcyB3aG8gYXJlICd2ZXJ5IHZvY2FsJyB2cy4gJ2xvdWQnLgoKYGBge3J9CmJhcmtfbW9kZWwgPC0gcG9scihiYXJrIH4gZHJvb2wgKiBraWRzLCBkYXRhID0gZG9ncywgSGVzcyA9IFRSVUUpCgpzdW1tYXJ5KGJhcmtfbW9kZWwpCmBgYAoKTXkgbW9kZWwgaGFzIDMgaW50ZXJjZXB0cyBmb3IgdGhlIDQgYGJhcmtgIGNhdGVnb3JpZXMgYW5kIDUgc2xvcGVzIGZvciBvdXIgYmluYXJ5IHByZWRpY3RvciBhbmQgMy1jYXRlZ29yeSBwcmVkaWN0b3IgYXMgd2VsbCBhcyB0aGUgaW50ZXJhY3Rpb24gYmV0d2VlbiB0aGVtLgoKYGBge3J9CmNvbmZpbnQoYmFya19tb2RlbCkKYGBgCgpNeSBtb2RlbCBwcmVkaWN0cyB0aGF0IGEgYnJlZWQgd2l0aCBhICdyZWd1bGFyJyBkcm9vbGluZyBsZXZlbCB3b3VsZCBub3QgY29uc2lzdGVudGx5IGhhdmUgYW4gaW1wYWN0IG9uIHRoZSBvdXRjb21lIHdoZW4gY29tcGFyZWQgdG8gcmVmZXJlbmNlICgnZHJ5JyBkcm9vbGluZyBsZXZlbCkgYmVjYXVzZSB0aGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgaW5jbHVkZXMgMC4gTmVpdGhlciB3b3VsZCBhICdkcm9vbGVyJyB3aGVuIGNvbXBhcmVkIHRvIHRoZSBzYW1lIHJlZmVyZW5jZS4gTXkgbW9kZWwgcHJlZGljdHMgdGhhdCBnb29kbmVzcyB3aXRoIHlvdW5nIGNoaWxkcmVuIGlzIGEgdHJhaXQgd2hpY2ggbGlrZWx5IGNvbnNpc3RlbnRseSBpbXBhY3RzIHRoZSBvdXRjb21lLiBCb3RoIG9mIHRoZSBpbnRlcmFjdGlvbiB0ZXJtIHNsb3BlcyBoYXZlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIHdoaWNoIGluY2x1ZGUgMCwgc28gaXQgaXMgdW5saWtlbHkgdGhhdCB0aGVzZSBtZWFuaW5nZnVsbHkgaW1wcm92ZSB0aGUgcHJlZGljdGl2ZSBhYmlsaXR5IG9mIG15IG1vZGVsLgoKQmVmb3JlIEkgc3RhcnQgbWFraW5nIHByZWRpY3Rpb25zLCBJIHdpbGwgZGlzcGxheSBhIHBsb3Qgb2YgZWZmZWN0IHNpemUgZm9yIG15IG1vZGVsLiBJbiBvcmRlciB0byBkbyB0aGF0LCBJIG11c3QgZml0IHRoZSBtb2RlbCB3aXRoIHRoZSBgbHJtYCBlbmdpbmUgYW5kIGluIG9yZGVyIHRvIGRvIHRoYXQsIEkgbXVzdCBzZXQgYSBkYXRhZGlzdC4KCmBgYHtyfQpkIDwtIGRhdGFkaXN0KGRvZ3MpCm9wdGlvbnMoZGF0YWRpc3QgPSAiZCIpCgpiYXJrX21vZGVsX2xybSA8LSBscm0oYmFyayB+IGRyb29sICoga2lkcywgZGF0YSA9IGRvZ3MsIHggPSBULCB5ID0gVCkKCnBsb3Qoc3VtbWFyeShiYXJrX21vZGVsX2xybSkpCmBgYAoKU2ltaWxhciB0byB3aGF0IEkgZXhwbGFpbmVkIGFib3V0IHRoZSBjb25maWRlbmNlIGludGVydmFscywgdGhlIGBraWRzYCBjb25maWRlbmNlIGludGVydmFsIGlzIHRoZSBvbmUgdGhhdCBkb2VzIG5vdCBpbmNsdWRlIDAgYW5kIHRoZXJlZm9yZSB3b3VsZCBiZSBtb3N0IGxpa2VseSB0byBpbXByb3ZlIHRoZSBwcmVkaWN0aXZlIGFiaWxpdHkgb2YgdGhlIG1vZGVsLgoKIyMjIE1ha2luZyBQcmVkaWN0aW9ucwoKVGhlcmUgYXJlIDYgcG9zc2libGUgcHJlZGljdGlvbnMgb2YgbXkgMiB2YXJpYWJsZXMsIHNvIEkgd2lsbCBmaXQgdGhlIG1vZGVsIHRvIDYgbmV3IGRvZ3Mgd2l0aCBlYWNoIG9mIHRoZXNlIGNvbWJpbmF0aW9ucywgc2hvdyB0aGUgbmV3IGRhdGEgYXMgYSB0aWJibGUsIGFuZCBwbG90IHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcy4gCgpgYGB7cn0KZG9nc19uZXcgPC0gZGF0YS5mcmFtZShuYW1lID0gYygiQWxmYWxmYSIsICJCZXJuaWUiLCAiQ2xhbmN5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvb3BlciIsICJGcmFua2llIiwgIlRlZCIpLAogICAgICAgICAgICAgICAgICAgZHJvb2wgPSBjKCJEcm9vbGVyIiwgIkRyb29sZXIiLCAiUmVndWxhciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSZWd1bGFyIiwgIkRyeSIsICJEcnkiKSwKICAgICAgICAgICAgICAgICAgIGtpZHMgPSBjKCJLaWQgZnJpZW5kbHkiLCAiQWR1bHRzIG9ubHkiLCAiS2lkIGZyaWVuZGx5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWR1bHRzIG9ubHkiLCAiS2lkIGZyaWVuZGx5IiwgIkFkdWx0cyBvbmx5IikpCgp0aWJibGUoZG9nc19uZXcpCgpwcmVkaWN0KGJhcmtfbW9kZWwsIGRvZ3NfbmV3LCBpbnRlcnZhbCA9ICJwcmVkaWN0aW9uIikKCnBsb3QocHJlZGljdChiYXJrX21vZGVsLCBkb2dzX25ldywgaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIpKQpgYGAKClVuZm9ydHVuYXRlbHksIGZvciBldmVyeSBwb3NzaWJsZSBjb21iaW5hdGlvbiBvZiBteSAyIHZhcmlhYmxlcywgbXkgbW9kZWwgcHJlZGljdHMgdGhlIGJhcmtpbmcgbGV2ZWwgd2lsbCBiZSAncXVpZXQnLiBUaGlzIGlzIHByb2JhYmx5IHRoZSB3b3JzdCBwb3NzaWJsZSBwcmVkaWN0aXZlIGFiaWxpdHkgZm9yIGEgY2F0ZWdvcmljYWwgcHJlZGljdGlvbiBtb2RlbC4gSSB3aWxsIG1vdmUgb24gdG8gdmFsaWRhdGlvbi4KCiMjIE1vZGVsIFZhbGlkYXRpb24KCkluIG9yZGVyIHRvIHZhbGlkYXRlIG15IG1vZGVsLCBJIHdpbGwgaGF2ZSB0byB1c2UgdGhlIGBscm1gIGZpdC4gSSB3aWxsIHVzZSB0aGUgZGVmYXVsdCBudW1iZXIgb2YgYm9vdHN0cmFwcyAoNDApLgoKYGBge3J9CnNldC5zZWVkKDIwMjIpCgp2YWxpZGF0ZShiYXJrX21vZGVsX2xybSkKYGBgCgpNeSBSXjJeIGNoYW5nZXMgc29tZXdoYXQgaW4gbmV3IGRhdGEsIGZyb20gYW4gZXN0aW1hdGVkIFJeMl4gb2YgMC4wOTIwIGFjcm9zcyA0MCB0cmFpbmluZyBzYW1wbGVzLCB0byBhbiBhdmVyYWdlIFJeMl4gb2YgMC4wNTcxLCBjcmVhdGluZyBhbiBvcHRpbWlzbSBvZiAwLjAzNDkuIE15IFJeMl4gY29ycmVjdGVkIGZvciBvdmVyZml0dGluZyBpcyB0aGVuIDAuMDQwMSwgd2hpY2ggaXMgcXVpdGUgZGlmZmVyZW50IHRoYW4gbXkgb3JpZ2luYWwgUl4yXiBvZiAwLjA3NTAuCgpJIHdpbGwgdXNlIFNvbWVycycgZCAoRHh5KSB0byBjYWxjdWxhdGUgdGhlIEMgc3RhdGlzdGljLiAKCmBgYHtyfQppbml0aWFsIDwtIDAuNSArICgwLjIzMTAvMikKY29ycmVjdGVkIDwtIDAuNSArICgwLjE4NzQvMikKCihpbml0aWFsIC0gY29ycmVjdGVkKQpgYGAKClRoZSBjaGFuZ2UgaW4gdGhlIEMtc3RhdGlzdGljIGlzIDAuMDIxOC4gVGhpcyBpcyBhIHNtYWxsIGNoYW5nZSwgaW5kaWNhdGluZyB0aGF0IGlmIG15IG1vZGVsIHdlcmUgZml0IHRvIG5ldyBkYXRhLCB0aGUgYXJlYSB1bmRlciB0aGUgY3VydmUgd291bGQgYmUgb25seSAyIHBlcmNlbnRhZ2UgcG9pbnRzIHNtYWxsZXIuCgojIyBUZXN0aW5nIHRoZSBQcm9wb3J0aW9uYWwgT2RkcyBBc3N1bXB0aW9uCgpJbiBvcmRlciB0byB0ZXN0IHRoZSBwcm9wb3J0aW9uYWwgb2RkcyBhc3N1bXB0aW9uLCBJIHdpbGwgZml0IG15IG1vZGVsIHdpdGggdGhlIGBtdWx0aW5vbWAgZnVuY3Rpb24uCgpgYGB7cn0KKGJhcmtfbW9kZWxfcG9hIDwtIG11bHRpbm9tKGJhcmsgfiBkcm9vbCAqIGtpZHMsIGRhdGEgPSBkb2dzKSkKYGBgCgpJIHdpbGwgY29tcGFyZSB0aGUgbG9nIGxpa2VsaWhvb2RzIG9mIHRoZSBtdWx0aW5vbWlhbCBtb2RlbCBhbmQgdGhlIHByb3BvcnRpb25hbCBvZGRzIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwuIFRoZSBtdWx0aW5vbWlhbCBsb2dpdCBmaXRzIDMgaW50ZXJjZXB0cyBhbmQgMTUgc2xvcGVzIGZvciBhIHRvdGFsIG9mIDE4IHBhcmFtZXRlcnMuIFRoZSBwcm9wb3J0aW9uYWwgb2RkcyBsb2dpdCBmaXQgMyBpbnRlcmNlcHRzIGFuZCA1IHNsb3BlcyBmb3IgYSB0b3RhbCBvZiA4IHBhcmFtZXRlcnMuIFRoZSBkaWZmZXJlbmNlIGluIHBhcmFtZXRlcnMgYmV0d2VlbiB0aGUgbW9kZWxzIGlzIDEwLgoKYGBge3J9CkxMX3BvbHIgPC0gbG9nTGlrKGJhcmtfbW9kZWwpCkxMX211bHRpbm9tIDwtIGxvZ0xpayhiYXJrX21vZGVsX3BvYSkKKEcgPC0gLTIgKiAoTExfcG9sclsxXSAtIExMX211bHRpbm9tWzFdKSkKcGNoaXNxKEcsIDEwLCBsb3dlci50YWlsID0gRkFMU0UpCmBgYAoKVGhpcyByZXN1bHQgaXMgbm90IGhpZ2hseSBzaWduaWZpY2FudCwgc28gSSBhbSBjb21mb3J0YWJsZSBhc3N1bWluZyBwcm9wb3J0aW9uYWwgb2Rkcy4KCiMgQW5hbHlzaXMgMjogUmFuayBQcmVkaWN0aW9uIE1vZGVsCgpJIHdpbGwgdXNlIGFuIG9yZGluYXJ5IGxlYXN0IHNxdWFyZXMgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgY2FsbGVkICdSYW5rIE1vZGVsJyB0byBwcmVkaWN0IHRoZSAyMDIwIEFLQyBCcmVlZCBSYW5raW5nLCBgcmFua2AsIHVzaW5nIG9wZW5uZXNzIHRvIHN0cmFuZ2VycywgYHN0cmFuZ2Vyc2AsIGFuZCBnb29kbmVzcyB3aXRoIG90aGVyIGRvZ3MsIGBkb2dfZnJpZW5kc2AuCgojIyBFeHBsb3JpbmcgdGhlIERhdGEKCk15IG91dGNvbWUgaXMgYSByYW5raW5nLCBhbmQgSSBoYXZlIGluY2x1ZGVkIGFsbCB0aGUgcmFua2VkIGJyZWVkcy4gVGhlcmVmb3JlLCBpdCBpcyBpbXBvc3NpYmxlIGZvciBteSBkYXRhIHRvIGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFzIHRoZXJlIGlzIGV4YWN0bHkgMSBicmVlZCBhdCBlYWNoIHJhbmsgbGV2ZWwuIEluc3RlYWQgb2YgdmlzdWFsaXppbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBteSBkYXRhIGluIGEgTm9ybWFsIFEtUSBwbG90IG9yIERlbnNpdHkgSGlzdG9ncmFtIGFzIEkgbm9ybWFsbHkgd291bGQsIEkgd2lsbCB2aXN1YWxpemUgdGhlIHNwcmVhZCBvZiBteSBwcmVkaWN0b3JzIGFjcm9zcyB0aGUgcmFua2luZ3MuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkb2dzLCBhZXMoeCA9IHN0cmFuZ2VycywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByYW5rLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gZG9nX2ZyaWVuZHMpKSArIAogIGdlb21fYm94cGxvdCgpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQoInRpdGxlIikpICsKICBsYWJzKHRpdGxlID0gIlNwcmVhZCBvZiAnR29vZG5lc3Mgd2l0aCBPdGhlciBEb2dzJyBhbmQgJ09wZW5uZXNzIHRvIFN0cmFuZ2VycyciLAogICAgICAgc3VidGl0bGUgPSAiQWNyb3NzIDE5NCBSYW5rZWQgQnJlZWRzIiwKICAgICAgIHggPSAiT3Blbm5lc3MgdG8gU3RyYW5nZXJzIiwKICAgICAgIHkgPSAiUmFuayIsCiAgICAgICBjb2wgPSAiR29vZCB3aXRoIE90aGVyIERvZ3MiLAogICAgICAgc2hhcGUgPSAiR29vZCB3aXRoIE90aGVyIERvZ3MiLAogICAgICAgY2FwdGlvbiA9ICIxOTQgYnJlZWRzIGZyb20gYGRvZ3NgIikKYGBgCgpUaGUgb3V0Y29tZSBpcyBvbiB0aGUgeS1heGlzLCB3aXRoIGhpZ2hlciByYW5rZWQgYnJlZWRzIGxvd2VyIG9uIHRoZSBwbG90LiBUaGUgcmVkIGNvbG9yIHJlcHJlc2VudHMgYnJlZWRzIHRoYXQgYXJlICdsb25lIHdvbHZlcycgb3IgZG8gbm90IGdldCBhbG9uZyB3aXRoIG90aGVyIGRvZ3MuIFRoZSBibHVlIGNvbG9yIHJlcHJlc2VudHMgJ3BhY2sgYW5pbWFscycgb3IgYnJlZWRzIHdobyBkbyBnZXQgYWxvbmcgd2VsbCB3aXRoIG90aGVyIGRvZ3MuIFRoZSB4LWF4aXMgaXMgb3Blbm5lc3MgdG8gc3RyYW5nZXJzIHdpdGggdGhlIGZ1cnRoZXN0IGxlZnQgcG9pbnRzIGJlaW5nICdzdHJhbmdlciBkYW5nZXInIG9yIGxhY2sgb2Ygb3Blbm5lc3MgYW5kIHRoZSBmdXJ0aGVzdCByaWdodCBwb2ludHMgYmVpbmcgJ291dGdvaW5nJy4gCgpUaGUgb25seSBncm91cCB3aXRoIG91dGxpZXJzIGlzIGJyZWVkcyB3aG8gYXJlIG91dGdvaW5nIHBhY2sgYW5pbWFscywgYW5kIHRoZXNlIG91dGxpZXJzIGFyZSAyIGJyZWVkcyByYW5rZWQgaW4gdGhlIGxvd2VzdCBxdWFydGlsZS4gV2l0aGluIHRoZSAnc3RyYW5nZXIgZGFuZ2VyJyBjYXRlZ29yeSwgdGhlIG1lYW4gcmFuayBvZiBwYWNrIGFuaW1hbHMgaXMgbG93ZXIgdGhhbiB0aGF0IG9mIHRoZSBsb25lIHdvbHZlcywgYnV0IGJvdGggZ3JvdXBzIHJhbmdlIGFsbW9zdCB0aGUgZnVsbCBsZW5ndGggb2YgdGhlIHJhbmtpbmdzLiBXaXRoaW4gdGhlICdmcmllbmRseScgYW5kICdvdXRnb2luZycgY2F0ZWdvcmllcywgdGhlIG1lYW4gcmFuayBvZiBsb25lIHdvbHZlcyBpcyBsb3dlciB0aGFuIHRoYXQgb2YgdGhlIHBhY2sgYW5pbWFscy4gVGhlIHJhbmdlIG9mIGZyaWVuZGx5IGJyZWVkcycgcmFua2luZ3MgaXMgc2ltaWxhciB0byB0aGUgcmFuZ2Ugb2YgJ3N0cmFuZ2VyIGRhbmdlcicgYnJlZWRzJyByYW5raW5ncywgYnV0IHRoZSByYW5nZSBvZiBvdXRnb2luZyBicmVlZHMgcmFua2luZ3MgaXMgc21hbGxlci4KCiMjIyBWaXN1YWxpemluZyBpbiBHcm91cHMKCkkgd2lsbCB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgb25seSB0aGUgdG9wIDEwIGJyZWVkcyB0byBzZWUgaWYgSSBjYW4gc2VlIGEgcGF0dGVybiB0aGVyZS4KCmBgYHtyfQpkb2dzX3RvcDEwIDwtIGRvZ3MgJT4lCiAgYXJyYW5nZShyYW5rKSAlPiUKICBmaWx0ZXIocmFuayA9PSAxOjEwKQoKZ2dwbG90KGRhdGEgPSBkb2dzX3RvcDEwLCBhZXMoeCA9IHN0cmFuZ2VycywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSByYW5rLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gZG9nX2ZyaWVuZHMpKSArIAogIGdlb21fcG9pbnQoYWVzKHNoYXBlID0gZG9nX2ZyaWVuZHMpLAogICAgYWxwaGEgPSAwLjYsCiAgICBzaXplID0gNSkgKwogIGdlb21fdGV4dF9yZXBlbChsYWJlbD0gZG9nc190b3AxMCRicmVlZCwKICAgICAgICAgICAgY29sID0gImJsYWNrIiwKICAgICAgICAgICAgc2l6ZSA9IDQpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQoInRpdGxlIikpICsKICBsYWJzKHRpdGxlID0gIlNwcmVhZCBvZiAnR29vZG5lc3Mgd2l0aCBPdGhlciBEb2dzJyBhbmQgJ09wZW5uZXNzIHRvIFN0cmFuZ2VycyciLAogICAgICAgc3VidGl0bGUgPSAiQWNyb3NzIFRvcCAxMCBEb2cgQnJlZWRzIiwKICAgICAgIHggPSAiT3Blbm5lc3MgdG8gU3RyYW5nZXJzIiwKICAgICAgIHkgPSAiUmFuayIsCiAgICAgICBjb2wgPSAiR29vZCB3aXRoIE90aGVyIERvZ3MiLAogICAgICAgc2hhcGUgPSAiR29vZCB3aXRoIE90aGVyIERvZ3MiLAogICAgICAgY2FwdGlvbiA9ICJicmVlZHMgcmFua2VkIDEtMTAgaW4gYGRvZ3NgIikKYGBgCgpUaGUgcmVkIGNpcmNsZXMgcmVwcmVzZW50IGxvbmcgd29sdmVzIGFuZCB0aGUgYmx1ZSB0cmlhbmdsZXMgcmVwcmVzZW50IHBhY2sgYW5pbWFscy4gVGhlcmUgaXMgcmVhbGx5IG5vIHBhdHRlcm4gaGVyZS4gRm91ciBvZiB0aGUgYnJlZWRzIGFyZSAnbG9uZSB3b2x2ZXMnIGFuZCA2IGFyZSAncGFjayBhbmltYWxzJy4gVGhlcmUgYXJlIDQgYnJlZWRzIGluIHRoZSAnb3V0Z29pbmcnIGNhdGVnb3J5IGFuZCAzIGJyZWVkcyBpbiBlYWNoIG9mIHRoZSAnc3RyYW5nZXIgZGFuZ2VyJyBhbmQgJ2ZyaWVuZGx5JyBjYXRlZ29yaWVzIG9mIG9wZW5uZXNzIHRvIHN0cmFuZ2Vycy4gVGhpcyBkb2Vzbid0IGFuc3dlciBtdWNoIGZvciBtZS4KCkkgd2lsbCBub3cgdHJ5IHRha2luZyBhIGxvb2sgYXQgdGhlIGhpZ2hlc3QgcXVhcnRpbGUgcmFua2VkIGJyZWVkcyBhbmQgY29tcGFyZSB0aGVtIHRvIHRoZSBsb3dlc3QgcXVhcnRpbGUuCgpgYGB7cn0KMTk0IC8gNApgYGAKCkVhY2ggcXVhcnRpbGUgY29udGFpbnMgNDguNSBicmVlZHMuIEkgd291bGQgcmF0aGVyIHVuZGVyZXN0aW1hdGUgdGhhbiBvdmVyZXN0aW1hdGUsIHNvIEkgd2lsbCBzYW1wbGUgdGhlIGhpZ2hlc3QgcmFua2VkIDQ4IGJyZWVkcyBhbmQgdGhlIGxvd2VzdCByYW5rZWQgNDggYnJlZWRzLgoKYGBge3IgcXVhcnRpbGVzfQpkb2dzX3RvcDQ4IDwtIGRvZ3MgJT4lCiAgYXJyYW5nZShyYW5rKSAlPiUKICBmaWx0ZXIocmFuayA9PSAxOjQ4KQoKZG9nc19sb3c0OCA8LSBkb2dzICU+JQogIGFycmFuZ2UocmFuaykgJT4lCiAgdG9wX24oNDgsIHJhbmspCmBgYAoKYGBge3IgZmlnLmhlaWdodD00fQpwMyA8LSBnZ3Bsb3QoZGF0YSA9IGRvZ3NfdG9wNDgsIGFlcyh4ID0gc3RyYW5nZXJzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHJhbmssIAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBkb2dfZnJpZW5kcykpICsgCiAgZ2VvbV9ib3hwbG90KCkgKwogIGd1aWRlcyhjb2wgPSAibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIkhpZ2hlc3QgUmFua2VkIFF1YXJ0aWxlIG9mIEJyZWVkcyIsCiAgICAgICB4ID0gIk9wZW5uZXNzIHRvIFN0cmFuZ2VycyIsCiAgICAgICB5ID0gIlJhbmsiLAogICAgICAgY29sID0gIkdvb2Qgd2l0aCBPdGhlciBEb2dzIiwKICAgICAgIHNoYXBlID0gIkdvb2Qgd2l0aCBPdGhlciBEb2dzIikKCnA0IDwtIGdncGxvdChkYXRhID0gZG9nc19sb3c0OCwgYWVzKHggPSBzdHJhbmdlcnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcmFuaywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGRvZ19mcmllbmRzKSkgKyAKICBnZW9tX2JveHBsb3QoKSArCiAgbGFicyh0aXRsZSA9ICJMb3dlc3QgUmFua2VkIFF1YXJ0aWxlIG9mIEJyZWVkcyIsCiAgICAgICB4ID0gIk9wZW5uZXNzIHRvIFN0cmFuZ2VycyIsCiAgICAgICB5ID0gIlJhbmsiLAogICAgICAgY29sID0gIkdvb2Qgd2l0aCBPdGhlciBEb2dzIiwKICAgICAgIHNoYXBlID0gIkdvb2Qgd2l0aCBPdGhlciBEb2dzIikKCnAzICsgcDQgKwogIHBsb3RfYW5ub3RhdGlvbih0aXRsZSA9ICJTcHJlYWQgb2YgJ0dvb2RuZXNzIHdpdGggT3RoZXIgRG9ncycgYW5kICdPcGVubmVzcyB0byBTdHJhbmdlcnMnIiwKICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiQWNyb3NzIEhpZ2hlc3QgYW5kIExvd2VzdCBRdWFydGlsZXMgb2YgUmFua2VkIEJyZWVkcyIsCiAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSAiOTYgYnJlZWRzIHJhbmtlZCAxLTQ4IGFuZCAxNDctMTk1IGluIGBkb2dzYCIpCmBgYAoKVGhlIG9ubHkgZ3JvdXAgd2l0aCBhbiBvdXRsaWVyIGlzICdzdHJhbmdlciBkYW5nZXInIHBhY2sgYW5pbWFscywgd2hvIGhhdmUgYW4gb3V0bGllciByYW5rZWQgaGlnaGVyIHRoYW4gdGhlIHJlc3Qgb2YgdGhhdCBncm91cC4gSW4gdGhlIGhpZ2hlc3QgcXVhcnRpbGUgb2YgcmFua2luZ3MsIHdpdGhpbiB0aGUgJ3N0cmFuZ2VyIGRhbmdlcicgY2F0ZWdvcnksIHRoZSBwYWNrIGFuaW1hbHMgYXJlIHJhbmtlZCBoaWdoZXIgdGhhbiB0aGUgbG9uZSB3b2x2ZXMuIFRoZSBvcHBvc2l0ZSBpcyB0cnVlIGZvciBmcmllbmRsdCBhbmQgb3V0Z29pbmcgZG9ncyBpbiB0aGUgaGlnaGVzdCBxdWFydGlsZS4gSW4gdGhlIGxvd2VzdCBxdWFydGlsZSwgbG9uZSB3b2x2ZXMgYXJlIHJhbmtlZCBoaWdoZXIgdGhhbiBwYWNrIGFuaW1hbHMgaW4gYWxsIG9mIHRoZSBjYXRlZ29yaWVzIG9mIGBvcGVubmVzcyB0byBzdHJhbmdlcnNgLiAgCgojIyMgTXkgUHJlZGljdG9ycwoKSSB3YW50IHRvIHRha2UgYSBsb29rIGF0IHRoZSBzcHJlYWQgb2YgYnJlZWRzIGFjcm9zcyBlYWNoIGNhdGVnb3J5IG9mIG15IDIgcHJlZGljdG9ycy4KCmBgYHtyfQpkb2dzICU+JQogIHRhYnlsKHN0cmFuZ2VycywgZG9nX2ZyaWVuZHMpICU+JQogIGFkb3JuX3RvdGFscyhjKCJyb3ciLCAiY29sIikpICU+JQogIGFkb3JuX3BlcmNlbnRhZ2VzKCJyb3ciKSAlPiUKICBhZG9ybl9wY3RfZm9ybWF0dGluZyhyb3VuZGluZyA9ICJoYWxmIHVwIiwgZGlnaXRzID0gMCkgJT4lCiAgYWRvcm5fbnMoKSAlPiUKICBrYWJsZSgpCmBgYAoKTW9yZSBicmVlZHMgYXJlICdsb25lIHdvbHZlcycgKGFwcHJveGltYXRlbHkgMi8zKSB0aGFuIGFyZSAncGFjayBhbmltYWxzJy4gV2l0aGluIHRoZSAnbG9uZSB3b2xmJyBjYXRlZ29yeSBvZiBnb29kbmVzcyB3aXRoIG90aGVyIGRvZ3MsIG1hbnkgbW9yZSBicmVlZHMgYXJlIGluIHRoZSAnc3RyYW5nZXIgZGFuZ2VyJyBjYXRlZ29yeSBvZiBvcGVubmVzcyB0byBzdHJhbmdlcnMgYXMgY29tcGFyZWQgdG8gdGhlICdmcmllbmRseScgYW5kICdvdXRnb2luZycgY2F0ZWdvcmllcywgd2hpY2ggbWFrZXMgc2Vuc2UuIFdpdGhpbiB0aGUgJ3BhY2sgYW5pbWFsJyBjYXRlZ29yeSBvZiBnb29kbmVzcyB3aXRoIG90aGVyIGRvZ3MsIHRoZSBicmVlZHMgYXJlIG1vcmUgZXZlbmx5IHNwbGl0LCBidXQgc3RpbGwgbW9zdCBhcmUgaW4gdGhlICdzdHJhbmdlciBkYW5nZXInIGNhdGVnb3J5IG9mIG9wZW5uZXNzIHRvIHN0cmFuZ2Vycy4KCkkgbmVlZCB0byBjaGVjayBjb2xsaW5lYXJpdHkgYmV0d2VlbiBteSBwcmVkaWN0b3JzLiBJIGhhdmUgYWxyZWFkeSBzZXQgYSBkYXRhZGlzdCBmb3IgbXkgZGF0YSBzZXQuCgpgYGB7cn0KY2FyOjp2aWYob2xzKHJhbmsgfiBzdHJhbmdlcnMgKiBkb2dfZnJpZW5kcywgZGF0YSA9IGRvZ3MsIHggPSBULCB5ID0gVCkpCmBgYAoKVGhlc2UgdmFyaWFuY2UgaW5mbGF0aW9uIGZhY3RvcnMgYXJlIGhpZ2gsIHNvIEkgd2lsbCBrZWVwIGNvbGxpbmVhcml0eSBpbiBtaW5kIGFzIEkgZml0IGFuZCBldmFsdWF0ZSBteSBtb2RlbC4KCiMjIyBUcmFuc2Zvcm1hdGlvbiBBbmFseXNpcyAmIEFzc2Vzc21lbnQgZm9yIE5vbmxpbmVhciBUZXJtcwoKQSB0cmFuc2Zvcm1hdGlvbiBpcyB1bmxpa2VseSB0byBpbXByb3ZlIHRoZSBkaXN0cmlidXRpb24gb2YgbXkgb3V0Y29tZSBiZWNhdXNlIGl0IGlzIG9mIHVuaWZvcm0gZGlzdHJpYnV0aW9uIGFuZCBJIGRvIG5vdCB0aGluayBhIEJveC1Db3ggYXNzZXNzbWVudC9wbG90IHdvdWxkIGNoYW5nZSBteSBtaW5kIGhlcmUuIEkgaGF2ZSBvbmx5IDEgb3B0aW9uIG9mIG5vbmxpbmVhciB0ZXJtIHRvIGFkZCwgc28gSSB3aWxsIGluY2x1ZGUgYW4gaW50ZXJhY3Rpb24gYmV0d2VlbiBteSAyIGNhdGVnb3JpY2FsIHByZWRpY3RvcnMuCgojIyBGaXR0aW5nIHRoZSBNb2RlbDogQSBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCB1c2luZyBPcmRpbmFyeSBMZWFzdCBTcXVhcmVzCgpJIHdpbGwgZml0IG15IG1vZGVsIHVzaW5nIHRoZSBgb2xzYCBlbmdpbmUuIEkgaGF2ZSBhbHJlYWR5IHNldCBhIGRhdGFkaXN0IGZvciBteSBkYXRhIHNldC4KCmBgYHtyfQoocmFua19tb2RlbCA8LSBvbHMoZGF0YSA9IGRvZ3MsIHJhbmsgfiBzdHJhbmdlcnMgKiBkb2dfZnJpZW5kcywKICAgICAgICAgIHggPSBUUlVFLCB5ID0gVFJVRSkpCmBgYAoKVGhlIGcgc3RhdGlzdGljIGlzIDE2LjYwNywgd2hpY2ggaW5kaWNhdGVzIHRoYXQgaWYgSSByYW5kb21seSBzZWxlY3RlZCAyIGJyZWVkcywgdGhpcyB3b3VsZCBiZSB0aGUgYXZlcmFnZSBkaWZmZXJlbmNlIGluIHRoZWlyIHByZWRpY3RlZCByYW5raW5ncy4gSW4gYSByYW5rZWQgbGlzdCBvZiAxOTQgYnJlZWRzLCB0aGlzIGlzIG5vdCB2ZXJ5IGdvb2QuCgpgYGB7cn0Kc3VtbWFyeShyYW5rX21vZGVsKQpgYGAKClRoaXMgc3VtbWFyeSB0ZWxscyBtZSB0aGF0IGlmIEkgaGFkIDIgZG9nIGJyZWVkcywgQ29vcGVyIChhIGJsYWNrIExhYnJhZG9yKSBhbmQgVGVkIChhIENhdmFsaWVyIEtpbmcgQ2hhcmxlcyBTcGFuaWVsKSB3aG8gYXJlIGJvdGggJ2xvbmUgd29sdmVzJyB3aGVuIGl0IGNvbWVzIHRvIGdvb2RuZXNzIHdpdGggb3RoZXIgZG9ncywgYnV0IENvb3BlciBpcyAnb3V0Z29pbmcnIGFuZCBUZWQncyBjYXRlZ29yeSBvZiBvcGVubmVzcyB0byBzdHJhbmdlcnMgaXMgJ3N0cmFuZ2VyIGRhbmdlcicsIHRoZW4gbXkgbW9kZWwgcHJlZGljdHMgdGhhdCBDb29wZXIncyAyMDIwIEFLQyBCcmVlZCBSYW5raW5nIHdpbGwgYmUgMTIuOSBwb2ludHMgbG93ZXIgdGhhbiBUZWQncy4gSGVyZSBpcyBhIHZpc3VhbGl6YXRpb24gb2YgdGhlc2UgZWZmZWN0IHNpemVzLgoKYGBge3J9CnBsb3Qoc3VtbWFyeShyYW5rX21vZGVsKSkKYGBgCgpBbGwgb2YgdGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGluIHRoaXMgbW9kZWwgaW5jbHVkZSAwLCBzaG93aW5nIHRoYXQgdGhlc2UgcHJlZGljdG9ycyBwcm9iYWJseSBoYXZlIHBvb3IgcHJlZGljdGl2ZSBhYmlsaXR5LiAKCiMjIFJlc2lkdWFsIERpYWdub3N0aWNzCgpJIHdpbGwgY2hlY2sgd2hpY2ggcG9pbnRzIG1heSBiZSBpbmZsdWVudGlhbCwgYW5kIHdoaWNoIHByZWRpY3RvcnMgbWlnaHQgaGF2ZSBtb3JlIGluZmx1ZW50aWFsIHBvaW50cy4KCmBgYHtyfQp3aGljaC5pbmZsdWVuY2UocmFua19tb2RlbCkKYGBgCgpUaGlzIGlzIGludGVyZXN0aW5nLiBUaGVyZSBhcmUgYWJvdXQgaGFsZiB0aGUgYW1vdW50IG9mIGluZmx1ZW50aWFsIHBvaW50IGluIGBkb2dfZnJpZW5kc2AgYXMgdGhlcmUgYXJlIGluIGBzdHJhbmdlcnNgIGFuZCBtb3N0IG9mIHRoZSBpbmZsdWVudGlhbCBwb2ludHMgaW4gJ3N0cmFuZ2VycycgYXJlIGluIHZlcnkgbG93IHJhbmtlZCBicmVlZHMuIElmIEkgd2VyZSB0byBkcm9wIHNvbWUgb2YgdGhlc2Ugcm93cywgaXQgd291bGQgaW1wYWN0IHRoZSBlc3RpbWF0ZXMgb2YgdGhlIGludGVyY2VwdCBmb3IgYHN0cmFuZ2Vyc2AuIAoKSSB3aWxsIGNoZWNrIHRoZSByZXNpZHVhbHMgZm9yIG15IG1vZGVsIGFuZCB2aXN1YWxpemUgYSBwbG90LgoKYGBge3J9CnRlbXAgPC0gZGF0YS5mcmFtZShhcmVhID0gMToxOTQpCnRlbXAkcmVzaWQgPC0gcmVzaWR1YWxzKHJhbmtfbW9kZWwsIHR5cGUgPSAib3JkaW5hcnkiKQoKZ2dwbG90KHRlbXAsIGFlcyh4ID0gYXJlYSwgeSA9IHJlc2lkKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGdlb21fbGluZSgpCmBgYAoKVGhlIHJlc2lkdWFscyByYW5nZSBmcm9tIGFib3V0IC0xMDAgdG8gMTAwLiBUaGUgbW9zdCBhY2N1cmF0ZSBwcmVkaWN0aW9ucyBvY2N1ciBpbiB0aGUgYnJlZWRzIHJhbmtlZCBhYm91dCAxMzAtMTU1LiBUaGUgaGlnaGVyIHJhbmtlZCBwb2ludHMgYXBwZWFyIHRvIGhhdmUgbGFyZ2VyIHJlc2lkdWFscy4KCiMjIE1vZGVsIFZhbGlkYXRpb24KCkkgd2lsbCB2YWxpZGF0ZSBteSBtb2RlbCB1c2luZyB0aGUgZGVmYXVsdCBudW1iZXIgb2YgYm9vdHN0cmFwcy4KCmBgYHtyfQpzZXQuc2VlZCgyMDIyKQp2YWxpZGF0ZShyYW5rX21vZGVsKQpgYGAKCk15IFJeMl4gY2hhbmdlcyBzdWJzdGFudGlhbGx5IGluIG5ldyBkYXRhLCBmcm9tIDAuMTExOCBhY3Jvc3MgNDAgdHJhaW5pbmcgc2FtcGxlcyB0byBhbiBlc3RpbWF0ZWQgMC4wNDY5IGluIGEgdGVzdCBzYW1wbGUsIGNyZWF0aW5nIGFuIG9wdGltaXNtIG9mIDAuMDY1LiBNeSBSXjJeIGNvcnJlY3RlZCBmb3Igb3ZlcmZpdHRpbmcgaXMgMC4wMTM4LCB3aGljaCBpcyBtdWNoIGxvd2VyIHRoYW4gbXkgb3JpZ2luYWwgb2YgMC4wNzg4LiBNeSBjb3JyZWN0ZWQgbWVhbiBzcXVhcmVkIGVycm9yIGlzIDMxNDcsIG11Y2ggaGlnaGVyIHRoYW4gMjkxMS4KCiMgQ29uY2x1c2lvbnMKICAgCkluIHRoaXMgZGF0YSBzY2llbmNlIHByb2plY3QsIEkgc291Z2h0IHRvIGFuc3dlciB0d28gcmVzZWFyY2ggcXVlc3Rpb25zOiAxKSAiSG93IGVmZmVjdGl2ZWx5IGNhbiBkcm9vbGluZyBsZXZlbCBhbmQgZ29vZG5lc3Mgd2l0aCB5b3VuZyBjaGlsZHJlbiBwcmVkaWN0IGEgZG9nIGJyZWVkJ3MgYmFya2luZyBsZXZlbD8iLCBmb3Igd2hpY2ggSSBmaXQgYW4gb3JkaW5hbCBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsIHRvIHByZWRpY3QgdGhpcyBtdWx0aS1jYXRlZ29yaWNhbCBvdXRjb21lLCBhbmQgMikgImhvdyBlZmZlY3RpdmVseSBjYW4gb3Blbm5lc3MgdG8gc3RyYW5nZXJzIGFuZCBnb29kbmVzcyB3aXRoIG90aGVyIGRvZ3MgcHJlZGljdCBhIGRvZyBicmVlZCdzIDIwMjAgQUtDIHJhbmtpbmc/IiwgZm9yIHdoaWNoIEkgdXNlZCBhbiBvcmRpbmFyeSBsZWFzdCBzcXVhcmVzIG1ldGhvZCB0byBmaXQgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbC4KCkluIGV4cGxvcmluZyBteSBkYXRhLCBmaXR0aW5nIHRoZSBtb2RlbHMsIG1ha2luZyBwcmVkaWN0aW9ucywgdmlzdWFsaXppbmcgZWZmZWN0IHNpemVzLCBhbmQgaW50ZXJwcmV0aW5nIHN1bW1hcnkgc3RhdGlzdGljcywgSSBoYXZlIGxlYXJuZWQgdmFsdWFibGUgaW5zaWdodCBpbnRvIHRoZSBwcmVkaWN0aXZlIGFiaWxpdHkgb2YgbXkgbW9kZWxzLiBGaXJzdGx5LCBkcm9vbGluZyBsZXZlbCBhbmQgZ29vZG5lc3Mgd2l0aCB5b3VuZyBjaGlsZHJlbiBhcmUgbm90IHZlcnkgZWZmZWN0aXZlIHByZWRpY3RvcnMgb2YgYmFya2luZyBsZXZlbCB3aXRoaW4gbXkgZGF0YSBzZXQgbm9yIHdpdGhpbiBuZXcgZGF0YS4gU2Vjb25kbHksIG9wZW5uZXNzIHRvIHN0cmFuZ2VycyBhbmQgZ29vZG5lc3Mgd2l0aCBvdGhlciBkb2dzIHdlcmUgbm90IHZlcnkgZWZmZWN0aXZlIHByZWRpY3RvcnMgb2YgMjAyMCBBS0MgQnJlZWQgUmFua2luZyB3aXRoaW4gbXkgZGF0YSBzZXQgbm9yIHdpdGhpbiBuZXcgZGF0YS4gSSBoYXZlIGxlYXJuZWQgd2h5IHRoZSBBS0MgdGFrZXMgc28gbWFueSBvdGhlciB0cmFpdHMgaW50byBjb25zaWRlcmF0aW9uIHdoZW4gcmFua2luZyBkb2cgYnJlZWRzLCBiZWNhdXNlIHRoZSB0cmFpdHMgSSBoYXZlIGludmVzdGlnYXRlZCBoZXJlIGRvIG5vdCBjb3JyZWxhdGUgd2VsbCB3aXRoIGVhY2ggb3RoZXIuIAoKQSBtYWpvciBsaW1pdGF0aW9uIGlzIHRoYXQgdGhlIEFLQyB3ZWJzaXRlIGhhcyB1cGRhdGVkIHRoZWlyIHRyYWl0cyBzZWFyY2ggYWxnb3JpdGhtIGFuZCBubyBsb25nZXIgdXNlcyB0aGUgc2FtZSBwaHJhc2luZyBub3IgdGhlIHNhbWUgbWVhc3VyZXMgYXMgd2hlbiB0aGlzIGRhdGEgd2FzIHNjcmFwZWQuIFRoZXJlZm9yZSwgSSBhbSBub3QgYWJsZSB0byBlYXNpbHkgdXBkYXRlIG15IG1vZGVscyB3aXRoIGVhY2ggYW5udWFsIHJhbmtpbmcuIEFub3RoZXIgbGltaXRhdGlvbiBpcyB0aGF0IG1hbnkgb2YgdGhlIHZhcmlhYmxlcyBpbiB0aGUgcmF3IGRhdGEgc2V0IGhhZCB2ZXJ5IGhpZ2ggdmFyaWFuY2UgaW5mbGF0aW9uIGZhY3RvcnMgd2hlbiBJIGFkZGVkIG1vcmUgdGhhbiAyIHZhcmlhYmxlcyB0byB0aGUgbW9kZWwuIFNvbWUgd2VyZSBwcmVkaWN0YWJseSBjb2xsaW5lYXIsIHN1Y2ggYXMgY29hdCBsZW5ndGggYW5kIGdyb29taW5nIGZyZXF1ZW5jeSwgb3IgZW5lcmd5IGxldmVsIGFuZCBtZW50YWwgc3RpbXVsYXRpb24gbmVlZHMuIEhvd2V2ZXIsIG1vc3Qgd2VyZSBzdXJwcmlzaW5nIHRvIG1lLCBzdWNoIGFzIGdyb29taW5nIGZyZXF1ZW5jeSBhbmQgZW5lcmd5IGxldmVsLCBvciB0cmFpbmFiaWxpdHkgYW5kIGNvYXQgbGVuZ3RoLiBJIGZlbHQgdGhhdCB0aGlzIGxpbWl0ZWQgbWUgaW4gcHJlZGljdG9yIGNob2ljZSwgYnV0IEkgd2FzIHN0aWxsIGFibGUgdG8gYW5zd2VyIG15IHJlc2VhcmNoIHF1ZXN0aW9ucy4KCkEgbmV4dCBzdGVwIHdvdWxkIGJlIHRvIHNjcmFwZSB0aGUgQUtDIHdlYnNpdGUgYWdhaW4gdG8gb2J0YWluIHRoZSBtb3JlIHVwZGF0ZWQgbWVhc3VyZXMsIGZpdCBwcmVkaWN0aW9uIG1vZGVscywgYW5kIGNvbXBhcmUgdGhvc2UgbW9kZWxzIHRvIG15IG1vZGVscyBmaXQgaGVyZS4gVGhpcyB3b3VsZCBoZWxwIGRldGVybWluZSBpZiB0aGUgY2hhbmdlcyB0byB0aGUgd2Vic2l0ZSBhZmZlY3RlZCB0aGUgcHJlZGljdGl2ZSBhYmlsaXR5IG9mIHRoZXNlIHZhcmlhYmxlcy4gQW5vdGhlciBpbnRlcmVzdGluZyBuZXh0IHN0ZXAgd291bGQgYmUgdG8gYWRqdXN0IGZvciBhZ2Ugb3IgbGlmZSBzdGFnZSBvZiB0aGUgZG9ncy4gUHVwcGllcywgeW91bmcgZG9ncywgbWlkZGxlIGFnZWQgZG9ncywgYW5kIGVsZGVybHkgZG9ncyBhbGwgYmVoYXZlIGRpZmZlcmVudGx5LCByZWdhcmRsZXNzIG9mIHRoZWlyIGJyZWVkLiBGb3IgaW5zdGFuY2UsIHB1cHBpZXMgdXN1YWxseSBzbGVlcCBtb3JlIHRoYW4geW91bmcgb3IgbWlkZGxlIGFnZWQgZG9ncyBidXQgZWF0IGxlc3MuIFB1cHBpZXMgZ28gdGhyb3VnaCAnZmVhciBwaGFzZXMnIHdoZXJlIHRoZWlyIG9wZW5uZXNzIHRvIHN0cmFuZ2VycyBvciBnb29kbmVzcyB3aXRoIHlvdW5nIGNoaWxkcmVuIG9yIG90aGVyIGRvZ3MgbWF5IGJlIG11Y2ggbG93ZXIgdGhhbiBhbiBvbGRlciBkb2cgb2YgdGhlIHNhbWUgYnJlZWQgd2hvIGhhcyBtb3JlIGV4cGVyaWVuY2UuCgpUaGUgaW1wYWN0IG9mIHRoaXMgZGF0YSBzY2llbmNlIHByb2plY3QgaGVscHMgbWUgdW5kZXJzdGFuZCB3aGljaCBwcmVkaWN0b3JzIGRvIChvciBkbyBub3QsIHJhdGhlcikgd2VpZ2ggc3Ryb25nbHkgaW4gdGhlIEFLQydzIGFubnVhbCBicmVlZCByYW5raW5ncywgYW5kIHRhdWdodCBtZSB0aGF0IGJyZWVkcyB3aG8gZHJvb2wgbW9yZSBkbyBub3QgbmVjZXNzYXJpbHkgYmFyayBtb3JlLCB3aGljaCBpcyBzb21ldGhpbmcgSSB1c2VkIHRvIHRoaW5rLiBGb3Igc29tZW9uZSBjb250ZW1wbGF0aW5nIGdldHRpbmcgYSBkb2csIGxlYXJuaW5nIGFib3V0IHRoZXNlIHRyYWl0cyBhbmQgaG93IHRoZXkgaW50ZXJhY3QgY291bGQgYmUgdml0YWwgdG8gdGhlIGxpZmVsb25nIHN1Y2Nlc3MgYW5kIGhhcHBpbmVzcyBvZiB0aGUgYm90aCB0aGUgb3duZXIgYW5kIHRoZWlyIG5ldyBwdXAuCgotLS0KCkFzIGEgcmVzdWx0IG9mIHRoaXMgZGF0YSBzY2llbmNlIHByb2plY3QsIEkgaGF2ZSBsZWFybmVkIGFib3V0IGRhdGEgc2VsZWN0aW9uIGFuZCB2YXJpYWJsZSBzZWxlY3Rpb24uIEluIGNob29zaW5nIGEgZGF0YSBzZXQgdG8gYW5hbHl6ZSwgSSB3aXNoIEkgaGFkIHJ1biBzb21lIHNpbXBsZSB0b3kgbW9kZWxzIGluIGEgc3Vic2V0IHRvIGJldHRlciBhcHByZWNpYXRlIHBvc3NpYmxlIGNvbGxpbmVhcml0eSBvciBoaWdoIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHByZWRpY3RvcnMuIElmIEkga25ldyBob3cgY2xvc2VseSB0aGUgdmFyaWFibGVzIGluIHRoZSBkYXRhIHNldHMgSSB1c2VkIGNvcnJlbGF0ZWQsIEkgbWlnaHQgaGF2ZSBjb21iaW5lZCBhIHRoaXJkIGRhdGEgc2V0IHdpdGggc29tZSBkaWZmZXJlbnQgbWVhc3VyZXMsIGxpa2UgcGVyaGFwcyBicmVlZHMnIGxpZmUgZXhwZWN0YW5jaWVzIG9yIGluY2xpbmF0aW9ucyB0b3dhcmQgaWxsbmVzcy4gRnVydGhlcm1vcmUgaW4gbXkgdmFyaWFibGUgc2VsZWN0aW9uLCBJIHdvdWxkIGhhdmUgbGlrZWQgdG8gYWRkIGEgdGhpcmQgb3IgZm91cnRoIHByZWRpY3RvciB0byBvbmUgb3IgYm90aCBvZiBteSBtb2RlbHMsIGJ1dCBjb2xsaW5lYXJpdHkgY29uY2VybnMgcHJldmVudGVkIG1lIGZyb20gZG9pbmcgc28uIFRoaXMgaGFzIGRyaXZlbiB0aGUgcG9pbnQgaG9tZSB0aGF0IGRhdGEgc2VsZWN0aW9uIGFuZCBjbGVhbmluZyBpcyB0aGUgdmFzdCBtYWpvcml0eSBvZiB0aGUgd29ya2xvYWQgd2hlbiBpdCBjb21lcyB0byBmaXR0aW5nIG1vZGVscyB0byBhbnN3ZXIgcmVzZWFyY2ggcXVlc3Rpb25zLgoKIyBSZWZlcmVuY2VzIGFuZCBBY2tub3dsZWRnbWVudHMKCkhlcmUgaXMgYSBsaW5rIHRvIHRoZSBkYXRhOiBodHRwczovL2dpdGh1Yi5jb20va2tha2V5L2RvZ190cmFpdHNfQUtDL3RyZWUvbWFpbi9kYXRhIAoKVGhhbmsgeW91IHRvIERyLiBMb3ZlIGFuZCB0aGUgQ1JTUCA0MzIgVGVhY2hpbmcgQXNzaXN0YW50cyBmb3IgdGhlIGNvbnZleWluZyB0aGUga25vd2xlZGdlIGFuZCBpbnNpZ2h0IG5lY2Vzc2FyeSB0byBjb21wbGV0ZSB0aGlzIHByb2plY3QgYW5kIGFuc3dlciBteSByZXNlYXJjaCBxdWVzdGlvbnMuCiAgICAKIyBTZXNzaW9uIEluZm9ybWF0aW9uCgpgYGB7cn0KeGZ1bjo6c2Vzc2lvbl9pbmZvKCkKYGBgCg==