Data Visualization Notes

These are my notes for GRD 610A: Data Visualization II in Winter 2022 at the College for Creative Studies. These notes are for my work in the book Data Visualization by Kieran Healy (Princeton University Press, 2019).

Get Started

Everything has a name

Objects in R are created and referred to by their names. Certain names are not allowed because they are reserved words such as TRUE, if, mean(), and NA. Names also cannot start with a number or contain spaces. There are different naming conventions.

Snake Case
my_data
this_is_snake_case

Camel Case
myData
thisIsCamelCase

Pascal Case
MyData
ThisIsPascalCase

Pick one naming convention and stick with it. Be consistent; don’t switch between conventions. I recommend snake case.

# This is a comment (it starts with #)

my_data <- c(1, 2, 3, 4) # Assign using <- ; use ALT + - or OPTION + -

My_Data  
## Error in eval(expr, envir, enclos): object 'My_Data' not found
# Cannot be found because we called it my_data (lowercase)

# Now we can see it
my_data 
## [1] 1 2 3 4

Everything is an object; using functions

Think of functions like a recipe. The arguments of the function are the ingredients and what happens within the function is the sequence of cooking steps.

c(1, 2, 3, 1, 3, 5, 25) # c() is the combine function, it puts things together into a vector/list
## [1]  1  2  3  1  3  5 25
my_numbers <- c(1, 2, 3, 1, 3, 5, 25)
your_numbers <- c(5, 31, 71, 1, 3, 21, 6)

my_numbers
## [1]  1  2  3  1  3  5 25
mean(x = my_numbers)
## [1] 5.714286
mean(my_numbers) # you don't have to specify the argument names, but order matters if you do not specify
## [1] 5.714286
mean(x = your_numbers)
## [1] 19.71429
my_summary <- summary(my_numbers)

my_summary
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   1.500   3.000   5.714   4.000  25.000
table(my_numbers)
## my_numbers
##  1  2  3  5 25 
##  2  1  2  1  1
sd(my_numbers)
## [1] 8.616153
my_numbers * 5
## [1]   5  10  15   5  15  25 125
my_numbers + 1
## [1]  2  3  4  2  4  6 26
my_numbers + my_numbers # How is this different than the last line?
## [1]  2  4  6  2  6 10 50
# If you're not sure what an object is, ask for its class or type

class(my_numbers)
## [1] "numeric"
class(my_summary)
## [1] "summaryDefault" "table"
class(summary)
## [1] "function"
my_new_vector <- c(my_numbers, "Apple") # What happens if we combine a word with numbers?

my_new_vector
## [1] "1"     "2"     "3"     "1"     "3"     "5"     "25"    "Apple"
class(my_new_vector)
## [1] "character"
# Let's look at a new dataset

titanic
##       fate    sex    n percent
## 1 perished   male 1364    62.0
## 2 perished female  126     5.7
## 3 survived   male  367    16.7
## 4 survived female  344    15.6
class(titanic) 
## [1] "data.frame"
# Titanic is a data frame, which is like a table
# The $ operator can be used to access a column of a data frame by name

titanic$percent
## [1] 62.0  5.7 16.7 15.6
# Tibbles are slightly different than data frames. They are also data tables, but they provide more information.

titanic_tb <- as_tibble(titanic)

titanic_tb # How is does this compare to titanic above?
## # A tibble: 4 x 4
##   fate     sex        n percent
##   <fct>    <fct>  <dbl>   <dbl>
## 1 perished male    1364    62  
## 2 perished female   126     5.7
## 3 survived male     367    16.7
## 4 survived female   344    15.6
# To see inside an object, ask for its structure

str(my_numbers)
##  num [1:7] 1 2 3 1 3 5 25
str(my_summary)
##  'summaryDefault' Named num [1:6] 1 1.5 3 5.71 4 ...
##  - attr(*, "names")= chr [1:6] "Min." "1st Qu." "Median" "Mean" ...

Programming in R can be challenging and it takes time to get used to. Be patient and take a break if you get stuck. Make sure parentheses are opened and closed. Complete your commands (look out for the + in the console). Take your time and lookout for typos.

Get Data into R

In this section, we will get data from a URL and make a quick figure.

# Data source
url <- "https://cdn.rawgit.com/kjhealy/viz-organdata/master/organdonation.csv"

# Read the CSV from the URL
organs <- read_csv(file = url)

# Take a quick look at the data
glimpse(organs)
## Rows: 238
## Columns: 21
## $ country          <chr> "Australia", "Australia", "Australia", "Australia", "~
## $ year             <dbl> NA, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1~
## $ donors           <dbl> NA, 12.09, 12.35, 12.51, 10.25, 10.18, 10.59, 10.26, ~
## $ pop              <dbl> 17065, 17284, 17495, 17667, 17855, 18072, 18311, 1851~
## $ pop.dens         <dbl> 0.2204433, 0.2232723, 0.2259980, 0.2282198, 0.2306484~
## $ gdp              <dbl> 16774, 17171, 17914, 18883, 19849, 21079, 21923, 2296~
## $ gdp.lag          <dbl> 16591, 16774, 17171, 17914, 18883, 19849, 21079, 2192~
## $ health           <dbl> 1300, 1379, 1455, 1540, 1626, 1737, 1846, 1948, 2077,~
## $ health.lag       <dbl> 1224, 1300, 1379, 1455, 1540, 1626, 1737, 1846, 1948,~
## $ pubhealth        <dbl> 4.8, 5.4, 5.4, 5.4, 5.4, 5.5, 5.6, 5.7, 5.9, 6.1, 6.2~
## $ roads            <dbl> 136.59537, 122.25179, 112.83224, 110.54508, 107.98096~
## $ cerebvas         <dbl> 682, 647, 630, 611, 631, 592, 576, 525, 516, 493, 474~
## $ assault          <dbl> 21, 19, 17, 18, 17, 16, 17, 17, 16, 15, 16, 15, 14, N~
## $ external         <dbl> 444, 425, 406, 376, 387, 371, 395, 385, 410, 409, 393~
## $ txp.pop          <dbl> 0.9375916, 0.9257116, 0.9145470, 0.9056433, 0.8961075~
## $ world            <chr> "Liberal", "Liberal", "Liberal", "Liberal", "Liberal"~
## $ opt              <chr> "In", "In", "In", "In", "In", "In", "In", "In", "In",~
## $ consent.law      <chr> "Informed", "Informed", "Informed", "Informed", "Info~
## $ consent.practice <chr> "Informed", "Informed", "Informed", "Informed", "Info~
## $ consistent       <chr> "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes", "Yes~
## $ ccode            <chr> "Oz", "Oz", "Oz", "Oz", "Oz", "Oz", "Oz", "Oz", "Oz",~
# View(organs) # Run in RStudio
# Another way to view data 
gapminder
## # A tibble: 1,704 x 6
##    country     continent  year lifeExp      pop gdpPercap
##    <fct>       <fct>     <int>   <dbl>    <int>     <dbl>
##  1 Afghanistan Asia       1952    28.8  8425333      779.
##  2 Afghanistan Asia       1957    30.3  9240934      821.
##  3 Afghanistan Asia       1962    32.0 10267083      853.
##  4 Afghanistan Asia       1967    34.0 11537966      836.
##  5 Afghanistan Asia       1972    36.1 13079460      740.
##  6 Afghanistan Asia       1977    38.4 14880372      786.
##  7 Afghanistan Asia       1982    39.9 12881816      978.
##  8 Afghanistan Asia       1987    40.8 13867957      852.
##  9 Afghanistan Asia       1992    41.7 16317921      649.
## 10 Afghanistan Asia       1997    41.8 22227415      635.
## # ... with 1,694 more rows
# Make a plot object
p <- ggplot(data = gapminder,
            mapping = aes(x = gdpPercap, 
                          y = lifeExp))

# Create a scatterplot
p + geom_point()

Make a Plot

ggplot2 is an R library/package that allows us to map data to visual elements. Using it we can control the way the data appears in the plot and how each element of the plot will be displayed. Aesthetic Mappings make the connection between the data and how it is displayed on the plot (location, size, color, shape, etc.). Geoms define the type of plot (scatterplot, line plot, box plot, bar chart, etc.). Code is added together to make the plot using + the plus sign. More pieces can be added to the plot that define the scales, legend, labels, axes, style or theme of the plot, etc. Each part can be added using different functions with arguments specifying the look of the plot; the plot is built up piece by piece.

Tidy Data

In tidy data:
1. Each variable forms a column.
2. Each observation forms a row.
3. Each type of observational unit forms a table.

From Wickham, H. (2014). Tidy Data. Journal of Statistical Software, 59(10).

Mapping

Build a plot layer by layer, starting with telling ggplot what data to use and how to map or link it to parts of the plot, like the x and y axes. Then add on the type of geom.

p <- ggplot(data = gapminder,
              mapping = aes(x = gdpPercap,
                            y = lifeExp)) 

  p + geom_point()

Layer by Layer

Trying different geom_ functions.

p <- ggplot(data = gapminder,
            mapping = aes(x = gdpPercap,
                          y = lifeExp))

p + geom_smooth()

p + geom_point() + # add the points back into the plot 
  geom_smooth() 

p + geom_point() + 
  geom_smooth(method = "lm") # use a linear model

p + geom_point() +
  geom_smooth(method = "gam") + # generalized additive model
  scale_x_log10() # transform x-axis to log-10 scale

p + geom_point() +
  geom_smooth(method = "gam") +
  scale_x_log10(labels = scales::dollar) # format x-axis in dollars

Mapping Aesthetics

Using the aesthetics mapping, different parts of the data can be encoded in different ways.

p <- ggplot(data = gapminder,
            mapping = aes(x = gdpPercap,
                          y = lifeExp,
                          color = "purple")) # ggplot adds the value "purple" to all rows

p + geom_point() +
  geom_smooth(method = "loess") +
  scale_x_log10()

# To actually turn all of the points purple, we need to set the color property of the geom_ function

p <- ggplot(data = gapminder,
            mapping = aes(x = gdpPercap,
                          y = lifeExp))

p + geom_point(color = "purple") + # set point color to purple
  geom_smooth(method = "loess") +
  scale_x_log10()

p + geom_point(alpha = 0.3) + # make points more transparent
  geom_smooth(color = "orange", # make line orange
              se = FALSE, # remove standard error band
              size = 8, # increase thickness of the line
              method = "lm") +
  scale_x_log10()

p + geom_point(alpha = 0.3) + # make points more transparent
  geom_smooth(method = "gam") +
  scale_x_log10(labels = scales::dollar) +
  # Add title and labels
  labs(x = "GDP per Capita", 
       y = "Life Expectancy in Years",
       title = "Economic Growth and Life Expectancy",
       subtitle = "Data points are country-years",
       caption = "Source: Gapminder")

# Map data by continent
p <- ggplot(data = gapminder,
            mapping = aes(x = gdpPercap,
                          y = lifeExp,
                          color = continent))

p + geom_point() +
  geom_smooth(method = "loess") +
  scale_x_log10()

p <- ggplot(data = gapminder,
            mapping = aes(x = gdpPercap,
                          y = lifeExp,
                          color = continent,
                          fill = continent)) # now the error bands will also have the color

p + geom_point() +
  geom_smooth(method = "loess") +
  scale_x_log10()

Aesthetics by Geom

p <- ggplot(data = gapminder,
            mapping = aes(x = gdpPercap,
                          y = lifeExp))

p + geom_point(mapping = aes(color = continent)) + # points will be colored by continent
  geom_smooth(method = "loess") + # the smoothed line will be for all data
  scale_x_log10()

p + geom_point(mapping = aes(color = log(pop))) + # points will be colored by population
  scale_x_log10()

Saving

Use here() to save plots in the current directory. This function can also be used to reference folders within the current directory. For this class, use .svg to save in vector format and embed in Adobe Illustrator. The function to save a plot is ggsave() which will automatically save the last plot and can also be provided a ggplot object to save.

Where to Go Next

Pick at least two of the questions presented under the Where to Go Next section and answer them.

Show the Right Numbers

“Code almost never works properly the first time you write it.” (p. 73)

Grouping

p <- ggplot(data = gapminder,
            mapping = aes(x = year,
                          y = gdpPercap)) 

p + geom_line() # Something is wrong, we didn't tell it how to group

p + geom_line(aes(group = country)) # Now there is a line per country

Faceting

Facet = small multiple (i.e. a separate graph for each value of the variable)

p <- ggplot(data = gapminder,
            mapping = aes(x = year,
                          y = gdpPercap))

p + geom_line(aes(group = country)) +
  facet_wrap(~continent) # make a separate plot for each continent

# Make it look a little nicer
p + geom_line(color = "gray70",
              aes(group = country)) +
  geom_smooth(size = 1.1, method = "loess", se = FALSE) +
  scale_y_log10(labels = scales::dollar) +
  facet_wrap(~continent, ncol = 5) +
  labs(x = "Year",
       y = "GDP per capita",
       title = "GDP per capita on Five Continents")

# New dataset 2016 General Social Survey with more categorical data
glimpse(gss_sm)
## Rows: 2,867
## Columns: 32
## $ year        <dbl> 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016~
## $ id          <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,~
## $ ballot      <labelled> 1, 2, 3, 1, 3, 2, 1, 3, 1, 3, 2, 1, 2, 3, 2, 3, 3, 2,~
## $ age         <dbl> 47, 61, 72, 43, 55, 53, 50, 23, 45, 71, 33, 86, 32, 60, 76~
## $ childs      <dbl> 3, 0, 2, 4, 2, 2, 2, 3, 3, 4, 5, 4, 3, 5, 7, 2, 6, 5, 0, 2~
## $ sibs        <labelled> 2, 3, 3, 3, 2, 2, 2, 6, 5, 1, 4, 4, 3, 6, 0, 1, 3, 8,~
## $ degree      <fct> Bachelor, High School, Bachelor, High School, Graduate, Ju~
## $ race        <fct> White, White, White, White, White, White, White, Other, Bl~
## $ sex         <fct> Male, Male, Male, Female, Female, Female, Male, Female, Ma~
## $ region      <fct> New England, New England, New England, New England, New En~
## $ income16    <fct> $170000 or over, $50000 to 59999, $75000 to $89999, $17000~
## $ relig       <fct> None, None, Catholic, Catholic, None, None, None, Catholic~
## $ marital     <fct> Married, Never Married, Married, Married, Married, Married~
## $ padeg       <fct> Graduate, Lt High School, High School, NA, Bachelor, NA, H~
## $ madeg       <fct> High School, High School, Lt High School, High School, Hig~
## $ partyid     <fct> "Independent", "Ind,near Dem", "Not Str Republican", "Not ~
## $ polviews    <fct> Moderate, Liberal, Conservative, Moderate, Slightly Libera~
## $ happy       <fct> Pretty Happy, Pretty Happy, Very Happy, Pretty Happy, Very~
## $ partners    <fct> NA, "1 Partner", "1 Partner", NA, "1 Partner", "1 Partner"~
## $ grass       <fct> NA, Legal, Not Legal, NA, Legal, Legal, NA, Not Legal, NA,~
## $ zodiac      <fct> Aquarius, Scorpio, Pisces, Cancer, Scorpio, Scorpio, Capri~
## $ pres12      <labelled> 3, 1, 2, 2, 1, 1, NA, NA, NA, 2, NA, NA, 1, 1, 2, 1, ~
## $ wtssall     <dbl> 0.9569935, 0.4784968, 0.9569935, 1.9139870, 1.4354903, 0.9~
## $ income_rc   <fct> Gt $170000, Gt $50000, Gt $75000, Gt $170000, Gt $170000, ~
## $ agegrp      <fct> Age 45-55, Age 55-65, Age 65+, Age 35-45, Age 45-55, Age 4~
## $ ageq        <fct> Age 34-49, Age 49-62, Age 62+, Age 34-49, Age 49-62, Age 4~
## $ siblings    <fct> 2, 3, 3, 3, 2, 2, 2, 6+, 5, 1, 4, 4, 3, 6+, 0, 1, 3, 6+, 2~
## $ kids        <fct> 3, 0, 2, 4+, 2, 2, 2, 3, 3, 4+, 4+, 4+, 3, 4+, 4+, 2, 4+, ~
## $ religion    <fct> None, None, Catholic, Catholic, None, None, None, Catholic~
## $ bigregion   <fct> Northeast, Northeast, Northeast, Northeast, Northeast, Nor~
## $ partners_rc <fct> NA, 1, 1, NA, 1, 1, NA, 1, NA, 3, 1, NA, 1, NA, 0, 1, 0, N~
## $ obama       <dbl> 0, 1, 0, 0, 1, 1, NA, NA, NA, 0, NA, NA, 1, 1, 0, 1, 0, 1,~
# Practice using facet_grid() to facet between multiple variables
p <- ggplot(data = gss_sm,
            mapping = aes(x = age,
                          y = childs))

p + geom_point(alpha = 0.2) +
  geom_smooth() +
  facet_grid(sex ~ race)
## Warning: Removed 18 rows containing non-finite values (stat_smooth).
## Warning: Removed 18 rows containing missing values (geom_point).

Transforming

Each geom_ function has an associated stat_ function that is used to plot the data. Sometimes this involves transforming the data in some way.

p <- ggplot(data = gss_sm,
            mapping = aes(x = bigregion))

p + geom_bar() # makes a bar graph that counts the number of observations per region; count is computed for us

p + geom_bar(mapping = aes(y = ..prop..)) # the prop statistic can show us proportions

# But this is not right, each shows 100%

# So, we need to fix the automatic grouping that is occurring by region
p + geom_bar(mapping = aes(y = ..prop.., group = 1)) # using group = 1 is basically a placeholder that says all the data is in the same group

# Look at a different variable
table(gss_sm$religion)
## 
## Protestant   Catholic     Jewish       None      Other 
##       1371        649         51        619        159
p <- ggplot(data = gss_sm,
            mapping = aes(x = religion, color = religion))

p + geom_bar() # only the outline has a color - we need to use fill

p <- ggplot(data = gss_sm,
            mapping = aes(x = religion, fill = religion))

p + geom_bar()

# Remove the legend
p + geom_bar() +
  guides(fill = FALSE)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

# How can we look at two variables together
p <- ggplot(data = gss_sm,
            mapping = aes(x = bigregion,
                          fill = religion))

p + geom_bar() # Stacked bar chart of counts

p + geom_bar(position = "fill") # Stacked bar chart of proportions

p + geom_bar(position = "dodge") # Bar chart of counts side by side

p + geom_bar(position = "dodge",
             mapping = aes(y = ..prop..)) # Bar chart of proportions side by side

# Not quite right - all are 100% 

p + geom_bar(position = "dodge",
             mapping = aes(y = ..prop..,
                           group = religion)) # Bar chart of proportions side by side

# The proportions sum to 1 by religion across the regions

p <- ggplot(data = gss_sm,
            mapping = aes(x = religion))

p + geom_bar(position = "dodge",
             mapping = aes(y = ..prop..,
                           group = bigregion)) +
  facet_wrap(~bigregion, ncol = 2)

# Now the proportions sum to 1 by region across religions

Histograms and Density Plots

Histograms create bins of numerical data and display the distribution of the data within those bins.

# A new dataset
glimpse(midwest)
## Rows: 437
## Columns: 28
## $ PID                  <int> 561, 562, 563, 564, 565, 566, 567, 568, 569, 570,~
## $ county               <chr> "ADAMS", "ALEXANDER", "BOND", "BOONE", "BROWN", "~
## $ state                <chr> "IL", "IL", "IL", "IL", "IL", "IL", "IL", "IL", "~
## $ area                 <dbl> 0.052, 0.014, 0.022, 0.017, 0.018, 0.050, 0.017, ~
## $ poptotal             <int> 66090, 10626, 14991, 30806, 5836, 35688, 5322, 16~
## $ popdensity           <dbl> 1270.9615, 759.0000, 681.4091, 1812.1176, 324.222~
## $ popwhite             <int> 63917, 7054, 14477, 29344, 5264, 35157, 5298, 165~
## $ popblack             <int> 1702, 3496, 429, 127, 547, 50, 1, 111, 16, 16559,~
## $ popamerindian        <int> 98, 19, 35, 46, 14, 65, 8, 30, 8, 331, 51, 26, 17~
## $ popasian             <int> 249, 48, 16, 150, 5, 195, 15, 61, 23, 8033, 89, 3~
## $ popother             <int> 124, 9, 34, 1139, 6, 221, 0, 84, 6, 1596, 20, 7, ~
## $ percwhite            <dbl> 96.71206, 66.38434, 96.57128, 95.25417, 90.19877,~
## $ percblack            <dbl> 2.57527614, 32.90043290, 2.86171703, 0.41225735, ~
## $ percamerindan        <dbl> 0.14828264, 0.17880670, 0.23347342, 0.14932156, 0~
## $ percasian            <dbl> 0.37675897, 0.45172219, 0.10673071, 0.48691813, 0~
## $ percother            <dbl> 0.18762294, 0.08469791, 0.22680275, 3.69733169, 0~
## $ popadults            <int> 43298, 6724, 9669, 19272, 3979, 23444, 3583, 1132~
## $ perchsd              <dbl> 75.10740, 59.72635, 69.33499, 75.47219, 68.86152,~
## $ percollege           <dbl> 19.63139, 11.24331, 17.03382, 17.27895, 14.47600,~
## $ percprof             <dbl> 4.355859, 2.870315, 4.488572, 4.197800, 3.367680,~
## $ poppovertyknown      <int> 63628, 10529, 14235, 30337, 4815, 35107, 5241, 16~
## $ percpovertyknown     <dbl> 96.27478, 99.08714, 94.95697, 98.47757, 82.50514,~
## $ percbelowpoverty     <dbl> 13.151443, 32.244278, 12.068844, 7.209019, 13.520~
## $ percchildbelowpovert <dbl> 18.011717, 45.826514, 14.036061, 11.179536, 13.02~
## $ percadultpoverty     <dbl> 11.009776, 27.385647, 10.852090, 5.536013, 11.143~
## $ percelderlypoverty   <dbl> 12.443812, 25.228976, 12.697410, 6.217047, 19.200~
## $ inmetro              <int> 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0~
## $ category             <chr> "AAR", "LHR", "AAR", "ALU", "AAR", "AAR", "LAR", ~
# Show distribution of the size of the counties in the Midwest
p <- ggplot(data = midwest,
            mapping = aes(x = area))

p + geom_histogram() # count is computed automatically by the default stat function

p + geom_histogram(bins = 10) # set 10 bins

# Look at only two states
oh_wi <- c("OH", "WI")

p <- ggplot(data = subset(midwest, subset = state %in% oh_wi),
            mapping = aes(x = percollege,
                          fill = state))

p + geom_histogram(alpha = 0.4, bins = 20) # Overlapping histograms

# Density estimate of the underlying distribution - density plot
p <- ggplot(data = midwest,
            mapping = aes(x = area))

p + geom_density()

# Density by state
p <- ggplot(data = midwest,
            mapping = aes(x = area,
                          fill = state,
                          color = state))

p + geom_density(alpha = 0.3)

# Compare to geom_line(stat = "density")

p + geom_line(stat = "density")

# Scaled density
p <- ggplot(data = subset(midwest, subset = state %in% oh_wi),
            mapping = aes(x = percollege,
                          fill = state,
                          color = state))

p + geom_density(alpha = 0.3,
                 mapping = aes(y = ..scaled..))

### Avoiding Transformation When Necessary

Avoiding transformations - sometimes the data is already aggregated or summarized and we do not need a transformation.

titanic # this data is already summarized
##       fate    sex    n percent
## 1 perished   male 1364    62.0
## 2 perished female  126     5.7
## 3 survived   male  367    16.7
## 4 survived female  344    15.6
p <- ggplot(data = titanic,
            mapping = aes(x = fate,
                          y = percent,
                          fill = sex))

p + geom_bar(position = "dodge",
             stat = "identity") + # plot values as provided, do not summarize/count/etc.
  theme(legend.position = "top") # this puts the legend at the top of the graph

oecd_sum # another dataset that is already summarized
## # A tibble: 57 x 5
## # Groups:   year [57]
##     year other   usa  diff hi_lo
##    <int> <dbl> <dbl> <dbl> <chr>
##  1  1960  68.6  69.9 1.30  Below
##  2  1961  69.2  70.4 1.20  Below
##  3  1962  68.9  70.2 1.30  Below
##  4  1963  69.1  70   0.900 Below
##  5  1964  69.5  70.3 0.800 Below
##  6  1965  69.6  70.3 0.700 Below
##  7  1966  69.9  70.3 0.400 Below
##  8  1967  70.1  70.7 0.600 Below
##  9  1968  70.1  70.4 0.300 Below
## 10  1969  70.1  70.6 0.5   Below
## # ... with 47 more rows
p <- ggplot(data = oecd_sum,
            mapping = aes(x = year, 
                          y = diff,
                          fill = hi_lo))

p + geom_col() + # this is the same as geom_bar with stat = "identity"
  guides(fill = FALSE) + # no legend
  labs(x = NULL, # no x-axis label
       y = "Difference in Years",
       title = "The US Life Expectancy Gap",
       subtitle = "Difference between US and OECD average life expectancies, 1960-2015",
       caption = "Data: OECD. After a chart by Christopher Ingraham, Washington Post, December 27th 2017")
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: Removed 1 rows containing missing values (position_stack).

Where to Go Next

Pick at least two of the questions presented under the Where to Go Next section and answer them.

Graph Tables, Make Labels, Add Notes

Summarizing Data

It is best practice to calculate the summary statistics first and then plot them, rather than using the stat_ functions within geom_ functions. This is because it makes the code easier to understand and read and allows us to double check the data and aggregations more easily.

The pipe operator %>% from dplyr allows us to pass data from one operation or function to another. Usually there is a sequence of steps: group, filter/select, mutate, then summarize.

Within group_by(), grouping levels (left to right) go from outermost to innermost. Functions used to create new variables (such as summarize()) will be applied to the innermost group level first.

# Create a tibble/datat table with the percent of each religion by region
rel_by_region <- gss_sm %>%
  group_by(bigregion, religion) %>%
  summarize(N = n()) %>%
  mutate(freq = N / sum(N),
         pct = round((freq * 100), 0))

# Check the percentages sum to 100 by region
rel_by_region %>% 
  group_by(bigregion) %>%
  summarise(total = sum(pct))
## # A tibble: 4 x 2
##   bigregion total
##   <fct>     <dbl>
## 1 Northeast   100
## 2 Midwest     101
## 3 South       100
## 4 West        101
# Make a plot (note: Healy stops using the argument name)
p <- ggplot(data = rel_by_region,
            mapping = aes(x = bigregion,
                          y = pct,
                          fill = religion))

p + 
  geom_col(position = "dodge2") +
  labs(x = "Region",
       y = "Percent",
       fill = "Reiligion") +
  theme(legend.position = "top")

# Let's rearrange it a little
p <- ggplot(data = rel_by_region,
            mapping = aes(x = religion,
                          y = pct,
                          fill = religion))

p + 
  geom_col(position = "dodge2") +
  labs(x = NULL, # don't put a label on the axis
       y = "Percent",
       fill = "Religion") +
  guides(fill = FALSE) +
  coord_flip() + # switches the x and y axes after the plot is made
  facet_grid(~ bigregion)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

Continuous Variables by Group or Category

In this section, we will learn how to use geom_boxplot()

# New Dataset on Organ Donations by country and year
organdata %>% select(1:6) %>% sample_n(size = 10)
## # A tibble: 10 x 6
##    country       year       donors    pop pop_dens   gdp
##    <chr>         <date>      <dbl>  <int>    <dbl> <int>
##  1 Australia     2000-01-01   10.2  19153    0.247 26545
##  2 Finland       2002-01-01   17.1   5201    1.54  26616
##  3 Ireland       NA           NA     3514    5.00  12917
##  4 Italy         NA           NA       NA   NA        NA
##  5 Switzerland   1995-01-01   13     7041   17.1   26304
##  6 United States 1996-01-01   20.1 269394    2.80  28772
##  7 United States 1992-01-01   17.6 256514    2.66  24411
##  8 Austria       2001-01-01   23.9   8030    9.58  28457
##  9 Sweden        NA           NA     8559    1.90  18660
## 10 Germany       1994-01-01   12.3  81438   22.8   20690
# Graph some of the organ data without really looking at the dataset
p <- ggplot(data = organdata,
            mapping = aes(x = year, y = donors))

p + geom_point() # get a warning about missing data; this graph doesn't make much sense
## Warning: Removed 34 rows containing missing values (geom_point).

# Plot the organ donations by country over time
p <- ggplot(data = organdata,
            mapping = aes(x = year, y = donors))

p + geom_line(mapping = aes(group = country)) +
  facet_wrap(~country) # automatically orders countries alphabetically
## Warning: Removed 34 row(s) containing missing values (geom_path).

# Make boxplots by country (using the data over the years)
p <- ggplot(data = organdata,
            mapping = aes(x = country, y = donors))

p + geom_boxplot() +
  coord_flip() # move country names to the y-axis
## Warning: Removed 34 rows containing non-finite values (stat_boxplot).

# Let's reorder the boxplots by mean donation rate using the reorder function
p <- ggplot(data = organdata,
            mapping = aes(x = reorder(country, donors, na.rm = TRUE),
                          y = donors))

p + geom_boxplot() +
  labs(x = NULL) + # no x-axis title
  coord_flip()
## Warning: Removed 34 rows containing non-finite values (stat_boxplot).

# Add color to the boxplots
p <- ggplot(data = organdata,
            mapping = aes(x = reorder(country, donors, na.rm = TRUE),
                          y = donors,
                          fill = world))

p + geom_boxplot() +
  labs(x = NULL) + # no x-axis title
  coord_flip() +
  theme(legend.position = "top")
## Warning: Removed 34 rows containing non-finite values (stat_boxplot).

# Let's look at this data in point format
p <- ggplot(data = organdata,
            mapping = aes(x = reorder(country, donors, na.rm = TRUE),
                          y = donors,
                          color = world))

p + geom_point() +
  labs(x = NULL) +
  coord_flip() +
  theme(legend.position = "top")
## Warning: Removed 34 rows containing missing values (geom_point).

# Points are on top of each other, so use geom_jitter() to move them around a little
p + geom_jitter() +
  labs(x = NULL) +
  coord_flip() +
  theme(legend.position = "top")
## Warning: Removed 34 rows containing missing values (geom_point).

# Reduce the amount of spread in the points using position_jitter()
p + geom_jitter(position = position_jitter(width = 0.15)) +
  labs(x = NULL) +
  coord_flip() +
  theme(legend.position = "top")
## Warning: Removed 34 rows containing missing values (geom_point).

# Get information about consent laws by country
by_country <- organdata %>%
  group_by(consent_law, country) %>%
  summarize(donors_mean = mean(donors, na.rm = TRUE),
            donors_sd = sd(donors, na.rm = TRUE),
            gdp_mean = mean(gdp, na.rm = TRUE),
            health_mean = mean(health, na.rm = TRUE),
            roads_mean = mean(roads, na.rm = TRUE),
            cerebvas_mean = mean(cerebvas, na.rm = TRUE))

by_country
## # A tibble: 17 x 8
## # Groups:   consent_law [2]
##    consent_law country     donors_mean donors_sd gdp_mean health_mean roads_mean
##    <chr>       <chr>             <dbl>     <dbl>    <dbl>       <dbl>      <dbl>
##  1 Informed    Australia          10.6     1.14    22179.       1958.      105. 
##  2 Informed    Canada             14.0     0.751   23711.       2272.      109. 
##  3 Informed    Denmark            13.1     1.47    23722.       2054.      102. 
##  4 Informed    Germany            13.0     0.611   22163.       2349.      113. 
##  5 Informed    Ireland            19.8     2.48    20824.       1480.      118. 
##  6 Informed    Netherlands        13.7     1.55    23013.       1993.       76.1
##  7 Informed    United Kin~        13.5     0.775   21359.       1561.       67.9
##  8 Informed    United Sta~        20.0     1.33    29212.       3988.      155. 
##  9 Presumed    Austria            23.5     2.42    23876.       1875.      150. 
## 10 Presumed    Belgium            21.9     1.94    22500.       1958.      155. 
## 11 Presumed    Finland            18.4     1.53    21019.       1615.       93.6
## 12 Presumed    France             16.8     1.60    22603.       2160.      156. 
## 13 Presumed    Italy              11.1     4.28    21554.       1757       122. 
## 14 Presumed    Norway             15.4     1.11    26448.       2217.       70.0
## 15 Presumed    Spain              28.1     4.96    16933        1289.      161. 
## 16 Presumed    Sweden             13.1     1.75    22415.       1951.       72.3
## 17 Presumed    Switzerland        14.2     1.71    27233        2776.       96.4
## # ... with 1 more variable: cerebvas_mean <dbl>
# Another way to do this in a shorter step
by_country <- organdata %>%
  group_by(consent_law, country) %>%
  summarize_if(is.numeric, 
               list(mean = mean, sd = sd), # note funs is deprecated
               na.rm = TRUE) %>% 
  ungroup()


# Make a simple plot of our summarized data (Cleaveland Dot Plot)
p <- ggplot(data = by_country,
            mapping = aes(x = donors_mean,
                          y = reorder(country, donors_mean), # this puts the countries in order by donors_mean
                          color = consent_law))

p + 
  geom_point(size = 3) +
  labs(x = "Donor Procurement Rate",
       y = "", # another way of putting no label on an axis
       color = "Consent Law") +
  theme(legend.position = "top")

# Facet into two panels for the Cleaveland Dot Plot
p <- ggplot(data = by_country,
            mapping = aes(x = donors_mean,
                          y = reorder(country, donors_mean)))

p + 
  geom_point(size = 3) +
  facet_wrap(~ consent_law,
             scales = "free_y", # allow the y-axis labels to be different on each facet
             ncol = 1) # orient plot in a single columns

  labs(x = "Donor Procurement Rate",
       y = "") 
## $x
## [1] "Donor Procurement Rate"
## 
## $y
## [1] ""
## 
## attr(,"class")
## [1] "labels"
# Plot the dots (which represent the mean) with the range of standard deviation using geom_pointrange()
p <- ggplot(data = by_country,
            mapping = aes(x = reorder(country, donors_mean),
                          y = donors_mean))

p + 
  geom_pointrange(mapping = aes(ymin = donors_mean - donors_sd,
                                ymax = donors_mean + donors_sd)) +
  labs(x = "",
       y = "Donor Procurement Rate") +
  coord_flip() 

# need to use coord_flip() because geom_pointrange() uses y, ymin, and ymax and we want to show this on the x-axis

Plot Text Directly

geom_text() is used to plot labels on a graph. The argument hjust can be used to left or right justify the text. hjust = 0 will left-justify; hjust = 1 will right-justify.

p <- ggplot(data = by_country,
            mapping = aes(x = roads_mean,
                          y = donors_mean)) 

p + 
  geom_point() + # plot points
  geom_text(mapping = aes(label = country)) # plot the labels

# Text is right on top of the points, use hjust to move it
p + 
  geom_point() +
  geom_text(mapping = aes(label = country),
            hjust = 0)

The ggrepepl package provides two geoms that are more flexible for plotting labels.

library(ggrepel)

# Switch datasets
elections_historic %>% select(2:7)
## # A tibble: 49 x 6
##     year winner                 win_party ec_pct popular_pct popular_margin
##    <int> <chr>                  <chr>      <dbl>       <dbl>          <dbl>
##  1  1824 John Quincy Adams      D.-R.      0.322       0.309        -0.104 
##  2  1828 Andrew Jackson         Dem.       0.682       0.559         0.122 
##  3  1832 Andrew Jackson         Dem.       0.766       0.547         0.178 
##  4  1836 Martin Van Buren       Dem.       0.578       0.508         0.142 
##  5  1840 William Henry Harrison Whig       0.796       0.529         0.0605
##  6  1844 James Polk             Dem.       0.618       0.495         0.0145
##  7  1848 Zachary Taylor         Whig       0.562       0.473         0.0479
##  8  1852 Franklin Pierce        Dem.       0.858       0.508         0.0695
##  9  1856 James Buchanan         Dem.       0.588       0.453         0.122 
## 10  1860 Abraham Lincoln        Rep.       0.594       0.396         0.101 
## # ... with 39 more rows
# Set titles and labels
p_title <- "Presidential Elections: Popular & Electoral College Margins"
p_subtitle <- "1824-2016"
p_caption <- "Data for 2016 are provisional"
x_label <- "Winner's share of Popular Vote"
y_label <- "Winner's share of Electoral College Votes"

p <- ggplot(data = elections_historic,
            mapping = aes(x = popular_pct,
                          y = ec_pct,
                          label = winner_label))

p + 
  geom_hline(yintercept = 0.5,
             size = 1.4,
             color = "gray80") +
  geom_vline(xintercept = 0.5,
             size = 1.4,
             color = "gray80") +
  geom_point() +
  geom_text_repel() +
  scale_x_continuous(labels = scales::percent) +
  scale_y_continuous(labels = scales::percent) +
  labs(x = x_label,
       y = y_label,
       title = p_title,
       subtitle = p_subtitle,
       caption = p_caption)
## Warning: ggrepel: 13 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

Label Outliers

To label specific points, we need to tell the geom which points to label using the subset() function rather than giving the geom the entire dataset.

p <- ggplot(data = by_country,
            mapping = aes(x = gdp_mean,
                          y = health_mean))

p + 
  geom_point() +
  # Only label points with mean GDP greater than 25,000
  geom_text_repel(data = subset(by_country, gdp_mean > 25000),
                  mapping = aes(label = country)) 

p +
  geom_point() +
  # Only label points with mean GDP greater than 25,000 OR 
  # mean health less than 1,500 or Belgium
  geom_text_repel(data = subset(by_country, 
                                gdp_mean > 25000 |
                                  health_mean < 1500 |
                                  country %in% "Belgium"),
                  mapping = aes(label = country))

An alternative to using subset() to filter the data is to add a variable that already has the conditions for labeling to the dataset.

# Add code/indicator variable to organ data
organdata$ind <- organdata$ccode %in% c("Ita", "Spa") & organdata$year > 1998

p <- ggplot(data = organdata,
            mapping = aes(x = roads,
                          y = donors,
                          color = ind))

p + 
  geom_point() +
  geom_text_repel(data = subset(organdata, ind),
                  mapping = aes(label = ccode)) +
  guides(label = FALSE,
         color = FALSE) # removes legend
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: Removed 34 rows containing missing values (geom_point).

Write and Draw in the Plot Area

annotate() can be used to add annotations from different geoms to plots (text, shading, etc.)

p <- ggplot(data = organdata,
            mapping = aes(x = roads,
                          y = donors))

p + 
  geom_point() +
  annotate(geom = "text", # a text annotation
           x = 91, # x position for the annotation
           y = 33, # y position for the annotation
           label = "A surprisingly high \n recovery rate", # label for the annotation
           hjust = 0) # left-align
## Warning: Removed 34 rows containing missing values (geom_point).

p + 
  geom_point() +
  annotate(geom = "rect", # a rectangular annotation
           xmin = 125, xmax = 155, # x position for the annotation
           ymin = 30, ymax = 35, # y position for the annotation
           fill = "red", alpha = 0.2) + # color/fill properties for the annotation
    annotate(geom = "text", # a text annotation
           x = 91, # x position for the annotation
           y = 33, # y position for the annotation
           label = "A surprisingly high \n recovery rate", # label for the annotation
           hjust = 0) 
## Warning: Removed 34 rows containing missing values (geom_point).

Understanding Scales, Guides, and Themes

scale_<MAPPING>_<KIND>() functions can be used to adjust the axes and colors used in plots. The guide() function can be used to adjust the legend. The theme() function can be used to adjust the overall look of a plot.

p <- ggplot(data = organdata,
            mapping = aes(x = roads,
                          y = donors, 
                          color = world))

# Adjust the x and y axes
p + 
  geom_point() +
  scale_x_log10() +
  scale_y_continuous(breaks = c(5, 15, 25),
                     labels = c("Five", "Fifteen", "Twenty Five"))
## Warning: Removed 34 rows containing missing values (geom_point).

# Adjust the color legend
p + 
  geom_point() +
  scale_color_discrete(labels = c("Corporatist", "Liberal", 
                                  "Social Democrat", "Unclassified")) +
  labs(x = "Road Deaths",
       y = "Donor Procurement",
       color = "Welfare State")
## Warning: Removed 34 rows containing missing values (geom_point).

Where to Go Next

Pick at least two of the questions presented under the Where to Go Next section and answer them.

Work with Models

Draw Maps

There are many different ways to represent data on a map; the designer needs to decide how fine the resolution of the representation should be, how to convey the weight of different data points, how to accurately represent spatial data, and the map type to use.

Map U.S. State-Level Data

# Election data - select columns of interest and view a sample of 5 rows
election %>% 
  select(state, total_vote, r_points, pct_trump, party, census) %>%
  sample_n(5) 
## # A tibble: 5 x 6
##   state     total_vote r_points pct_trump party      census   
##   <chr>          <dbl>    <dbl>     <dbl> <chr>      <chr>    
## 1 Minnesota    2945233    -1.51      44.9 Democratic Midwest  
## 2 Delaware      443814   -11.4       41.7 Democratic South    
## 3 Colorado     2780247    -4.91      43.2 Democratic West     
## 4 Oregon       2001336   -11.0       39.1 Democratic West     
## 5 Vermont       315067   -26.4       30.3 Democratic Northeast
# FIPS code is a unique six-digit identifier for every U.S. county
# The first two digits of a FIPS code represent the state

# Set colors for Democratic and Republican parties
party_colors <- c("#2E74C0", "#CB454A")

# Create a plot of the elections data in a faceted dot plot

p0 <- ggplot(data = subset(election, st %nin% "DC"),
             mapping = aes(x = r_points,
                           y = reorder(state, r_points),
                           color = party))

p1 <- p0 +
  geom_vline(xintercept = 0, color = "gray30") +
  geom_point(size = 2)

p2 <- p1 + 
  scale_color_manual(values = party_colors)

p3 <- p2 + 
  scale_x_continuous(breaks = c(-30, -20, -10, 0, 10, 20, 30, 40),
                              labels = c("30\n(Clinton)",
                                         "20", "10", "0", "10", "20", "30",
                                         "40\n(Trump)"))

p3 + 
  facet_wrap(~ census, ncol = 1, scales = "free_y") +
  guides(color = FALSE) +
  labs(x = "Point Margin",
       y = "") +
  theme(axis.text = element_text(size = 8))
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

The maps library provides pre-drawn map data.

library(maps)

# Get data for U.S. States
us_states <- map_data("state")

head(us_states) # it provides latitude and longitude information; region is the state name
##        long      lat group order  region subregion
## 1 -87.46201 30.38968     1     1 alabama      <NA>
## 2 -87.48493 30.37249     1     2 alabama      <NA>
## 3 -87.52503 30.37249     1     3 alabama      <NA>
## 4 -87.53076 30.33239     1     4 alabama      <NA>
## 5 -87.57087 30.32665     1     5 alabama      <NA>
## 6 -87.58806 30.32665     1     6 alabama      <NA>
dim(us_states) # it's a large data frame
## [1] 15537     6
# Make a map

p <- ggplot(data = us_states,
            mapping = aes(x = long, # Use latitude and longitude to plot states
                          y = lat,
                          group = group)) 

p + 
  geom_polygon(fill = "white",
               color = "black") # Outline of a map of U.S. states

p <- ggplot(data = us_states,
            mapping = aes(x = long, 
                          y = lat,
                          group = group,
                          fill = region)) # add color fill to the states

p + 
  geom_polygon(color = "gray90",
                size = 0.1) +
  guides(fill = FALSE)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

Sometimes we will want to alter the map projection so that it looks more accurate. This can be done using the coord_map() function to select an alternate coordinate system (right now it is Cartesian). To use the Albers projection, we have to provide numbers for lat0 and lat1.

p <- ggplot(data = us_states,
            mapping = aes(x = long, 
                          y = lat,
                          group = group,
                          fill = region)) 

p + 
  geom_polygon(color = "gray90",
                size = 0.1) +
  coord_map(projection = "albers",
            lat0 = 39,
            lat1 = 45) +
  guides(fill = FALSE)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

Now it is time to get our data onto the map. We have to merge the two datasets - one has the elections data and one has the data to draw the states on the map. We can use the left_join() function to do this. It is important for a column of data in each dataset to match exactly so that we can put the datasets together.

# First we need to lowercase the state names and put them in a column called "region", to match the mapping data
election$region <- tolower(election$state)

# Now we can join the datasets together using the common region column
us_states_elec <- left_join(us_states, election)

# We are now ready to plot
p <- ggplot(data = us_states_elec,
            mapping = aes(x = long,
                          y = lat,
                          group = group,
                          fill = party))

p + 
  geom_polygon(color = "gray90",
               size = 0.1) +
  coord_map(projection = "albers",
            lat0 = 39,
            lat1 = 45) +
  scale_fill_manual(values = party_colors) + # adjust the colors of the map
  labs(title = "Election Results 2016", fill = NULL)  +
  theme_map() # added in setup chunk

Mapping the data only to states is a little deceptive because there are differences in voting by county and certain areas of the country have larger populations than others.

# Put a continuous variable on the map fill

p <- ggplot(data = us_states_elec,
            mapping = aes(x = long,
                          y = lat,
                          group = group,
                          fill = pct_trump))

p + geom_polygon(color = "gray90",
                 size = 0.1) +
  coord_map(projection = "albers", 
            lat0 = 39,
            lat1 = 45) +
  labs(title = "Trump vote",
       fill = "Percent") +
  theme_map()

# Let's change the color to red and have darker red mean higher percent
p + geom_polygon(color = "gray90",
                 size = 0.1) +
  coord_map(projection = "albers", 
            lat0 = 39,
            lat1 = 45) +
  labs(title = "Trump vote",
       fill = "Percent") +
  scale_fill_gradient(low = "white", 
                      high = "#CB454A") +
  theme_map()

scale_gradient2() is a function that creates a diverging scale from a midpoint.

p <- ggplot(data = us_states_elec,
            mapping = aes(x = long,
                          y = lat,
                          group = group,
                          fill = d_points))

# Create a gradient fill for point margins
p +
  geom_polygon(color = "gray90",
               size = 0.1) +
  coord_map(projection = "albers",
            lat0 = 39,
            lat1 = 45) +
  scale_fill_gradient2(low = "red",
                       mid = scales::muted("purple"),
                       high = "blue",
                       breaks = c(-25, 0, 25, 50, 75)) +
  labs(title = "Winning margins",
       fill = "Percent") +
  theme_map()

# Remove D.C. since it is an outlier; gradient colors are enhanced
# Note: earlier we used st to remove D.C., now we're using region
p <- ggplot(data = subset(us_states_elec, 
                          region %nin% "district of columbia"), 
            mapping = aes(x = long,
                          y = lat,
                          group = group,
                          fill = d_points))
p +
  geom_polygon(color = "gray90",
               size = 0.1) +
  coord_map(projection = "albers",
            lat0 = 39,
            lat1 = 45) +
  scale_fill_gradient2(low = "red",
                       mid = scales::muted("purple"),
                       high = "blue",
                       breaks = c(-25, 0, 25, 50, 75)) +
  labs(title = "Winning margins",
       fill = "Percent") +
  theme_map()

America’s Ur-choropleths

Data can be mapped to counties as well, but it’s important to remember the population distribution in the U.S. Choropleth maps of the U.S. tend to show population density more than anything else because there are concentrations of population in counties in the northeast and the west coast compared to the west. Note that the previous maps did not include Alaska or Hawaii. Now, we are going to add them using a county map dataset.

# Mapping dataset for U.S. counties
county_map %>%
  sample_n(5) # FIPS ID is used to identify the counties
##         long        lat  order  hole piece            group    id
## 1 -1001120.2  -391757.5 166172 FALSE     1 0500000US49011.1 49011
## 2  1341299.7 -1206414.3   3304 FALSE     1 0500000US01111.1 01111
## 3 -1190908.2 -2142158.7   8678 FALSE     1 0500000US02122.1 02122
## 4  1374683.9  -884353.7 153368 FALSE     1 0500000US47129.1 47129
## 5   722217.2  -155738.4  57682 FALSE     1 0500000US19005.1 19005
# County demographic data for U.S. Counties
county_data %>%
  select(id, name, state, pop_dens, pct_black) %>%
  sample_n(5) # FIPS ID is used to identify the counties
##      id                name state      pop_dens   pct_black
## 1 28153        Wayne County    MS [   10,   50) [25.0,50.0)
## 2 31079         Hall County    NE [  100,  500) [ 2.0, 5.0)
## 3 18007       Benton County    IN [   10,   50) [ 0.0, 2.0)
## 4 06013 Contra Costa County    CA [ 1000, 5000) [ 5.0,10.0)
## 5 40073   Kingfisher County    OK [   10,   50) [ 0.0, 2.0)
head(county_data) # ID 0 is for the entire U.S.; IDs with just the first two-digits is for the state
##      id           name state census_region      pop_dens   pop_dens4
## 1     0           <NA>  <NA>          <NA> [   50,  100) [ 45,  118)
## 2 01000              1    AL         South [   50,  100) [ 45,  118)
## 3 01001 Autauga County    AL         South [   50,  100) [ 45,  118)
## 4 01003 Baldwin County    AL         South [  100,  500) [118,71672]
## 5 01005 Barbour County    AL         South [   10,   50) [ 17,   45)
## 6 01007    Bibb County    AL         South [   10,   50) [ 17,   45)
##     pop_dens6   pct_black       pop female white black travel_time  land_area
## 1 [ 82,  215) [10.0,15.0) 318857056   50.8  77.7  13.2        25.5 3531905.43
## 2 [ 82,  215) [25.0,50.0)   4849377   51.5  69.8  26.6        24.2   50645.33
## 3 [ 82,  215) [15.0,25.0)     55395   51.5  78.1  18.4        26.2     594.44
## 4 [ 82,  215) [ 5.0,10.0)    200111   51.2  87.3   9.5        25.9    1589.78
## 5 [ 25,   45) [25.0,50.0)     26887   46.5  50.2  47.6        24.6     884.88
## 6 [ 25,   45) [15.0,25.0)     22506   46.0  76.3  22.1        27.6     622.58
##   hh_income su_gun4 su_gun6 fips votes_dem_2016 votes_gop_2016 total_votes_2016
## 1     53046    <NA>    <NA>    0             NA             NA               NA
## 2     43253    <NA>    <NA> 1000             NA             NA               NA
## 3     53682 [11,54] [10,12) 1001           5908          18110            24661
## 4     50221 [11,54] [10,12) 1003          18409          72780            94090
## 5     32911 [ 5, 8) [ 7, 8) 1005           4848           5431            10390
## 6     36447 [11,54] [10,12) 1007           1874           6733             8748
##   per_dem_2016 per_gop_2016 diff_2016 per_dem_2012 per_gop_2012 diff_2012
## 1           NA           NA        NA           NA           NA        NA
## 2           NA           NA        NA           NA           NA        NA
## 3    0.2395685    0.7343579     12202    0.2657577    0.7263374     11012
## 4    0.1956531    0.7735147     54371    0.2156657    0.7738975     47443
## 5    0.4666025    0.5227141       583    0.5125229    0.4833755       334
## 6    0.2142204    0.7696616      4859    0.2621857    0.7306638      3931
##   winner partywinner16 winner12 partywinner12 flipped
## 1   <NA>          <NA>     <NA>          <NA>    <NA>
## 2   <NA>          <NA>     <NA>          <NA>    <NA>
## 3  Trump    Republican   Romney    Republican      No
## 4  Trump    Republican   Romney    Republican      No
## 5  Trump    Republican    Obama      Democrat     Yes
## 6  Trump    Republican   Romney    Republican      No
# Put  the mapping data and demographic data together
county_full <- left_join(county_map, county_data, by = "id")

# Plot population density by county
p <- ggplot(data = county_full,
            mapping = aes(x = long, y = lat,
                          fill = pop_dens,
                          group = group))
p +
  geom_polygon(color = "gray90", size = 0.05) +
  coord_equal() + # relative scale of map does not change, even if plot dimensions change
  scale_fill_brewer(palette = "Blues",
                    labels = c("0-10", "10-50", "50-100", "100-500",
                               "500-1,000", "1,000-5,000", ">5,000")) +
  labs(fill = "Population per\nsquare mile") +
  theme_map() +
  guides(fill = guide_legend(nrow = 1)) +
  theme(legend.position = "bottom")

# Plot percent of Black population by county

p <- ggplot(data = county_full,
            mapping = aes(x = long, y = lat,
                          fill = pct_black,
                          group = group)) 

p + 
  geom_polygon(color = "gray90",
               size = 0.05) +
  coord_equal() +
  scale_fill_brewer(palette = "Greens") +
  labs(fill = "US Population, Percent Black") +
  theme_map() +
  guides(fill = guide_legend(nrow = 1)) +
  theme(legend.position = "bottom")

The population density and the percent of the population that is Black are confounding variables with many other county-level variables that we might want to examine, so it is important to keep the previous two plots in mind whenever plotting county-level data. To demonstrate this issue, we will make two more plots - one on gun-related suicides and one on binned population density.

# Create the color palette
# brewer.pal() produces evenly spaced color palettes
orange_pal <- RColorBrewer::brewer.pal(n = 6, name = "Oranges")
orange_pal
## [1] "#FEEDDE" "#FDD0A2" "#FDAE6B" "#FD8D3C" "#E6550D" "#A63603"
# Reverse color palette
orange_rev <- rev(orange_pal)
orange_rev
## [1] "#A63603" "#E6550D" "#FD8D3C" "#FDAE6B" "#FDD0A2" "#FEEDDE"
# Recreate a "poorly sourced by widely circulated county map of firearm-related suicide rates" (p. 186)
gun_p <- ggplot(data = county_full,
                mapping = aes(x = long, y = lat,
                              fill = su_gun6,
                              group = group))

gun_p1 <- gun_p +
  geom_polygon(color = "gray90",
               size = 0.05) +
  coord_equal()

gun_p2 <- gun_p1 +
  scale_fill_manual(values = orange_pal)

gun_p2 + 
  labs(title = "Gun-Related Suicides 1999-2015",
       fill = "Rate per 100,000pop.") +
  theme_map() +
  theme(legend.position = "bottom")

# Create the reverse-coded population density map
pop_p <- ggplot(data = county_full,
                mapping = aes(x = long, y = lat,
                              fill = pop_dens6,
                              group = group))

pop_p1 <- pop_p +
  geom_polygon(color = "gray90", size = 0.05) +
  coord_equal()

pop_p2 <- pop_p1 +
  scale_fill_manual(values = orange_rev)

pop_p2 + labs(title = "Reverse-coded Population Density",
              fill = "People per square mile") +
  theme_map() +
  theme(legend.position = "bottom")

“Small differences in reporting, combined with coarse binning and miscoding, will produce spatially misleading and substantively mistaken results. It might seem that focusing on on the details of variable coding in this particular case is a little too much in the weeds for a genreal introduction. But it is exactly these details that can dramatically alter the appearance of any graph, and especially maps, in a way that can be hard to detect after the fact.” (p. 189)

Statebins

The statebins package is an alternative way to develop U.S. maps. The syntax is slightly different than ggplot; the statebins package has been updated since the writing of the book, so the code below is different than the code in the book.

# install.packages("statebins") # note statebins has not been previously installed

library(statebins)

# The statebins package has changed since this book was written

# Continuous Data

statebins(state_data = election,
          state_col = "state",
          value_col = "pct_trump",
          round = FALSE) +
  labs(fill = "Percent Trump") +
  scale_fill_gradient(low = "#FEE5D9", 
                      high = "#A50F15") +
  theme_statebins() +
  theme(legend.position = "top") 

statebins(state_data = subset(election, st %nin% "DC"),
          state_col = "state",
          value_col = "pct_clinton",
          round = FALSE) +
  scale_fill_gradient(low = "#EFF3FF", 
                      high = "#08519C") +
  labs(fill = "Percent Clinton") +
  theme_statebins() +
  theme(legend.position = "top") 

# Categorical Data

ggplot(data = election, 
       mapping = aes(state = st,
                     fill = party)) +
  geom_statebins() +
  scale_fill_manual(values = c("royalblue", "darkred"),
                    labels = c("Clinton", "Trump")) +
  labs(fill = "Winner") +
  coord_equal() +
  theme_statebins() +
  theme(legend.position = "right") 

# Binned Data

ggplot(data = election, 
       mapping = aes(state = st,
                     fill = cut(pct_trump, 4))) +
  geom_statebins() +
  scale_fill_brewer(palette = "Reds",
                    labels = c("4-21", "21-37", "37-53", "53-70")) +
  labs(fill = "Percent Trump") +
  coord_equal() +
  theme_statebins() +
  theme(legend.position = "top") 

Small-Multiple Maps

Use small-multiple maps to show geographic data over time. We will also use the viridis package to get good color palettes that are vibrant on both ends.

opiates # state-level death rate from optiate-related causes 1999-2014
## # A tibble: 800 x 11
##     year state    fips deaths population crude adjusted adjusted_se region abbr 
##    <int> <chr>   <int>  <int>      <int> <dbl>    <dbl>       <dbl> <ord>  <chr>
##  1  1999 Alabama     1     37    4430141   0.8      0.8         0.1 South  AL   
##  2  1999 Alaska      2     27     624779   4.3      4           0.8 West   AK   
##  3  1999 Arizona     4    229    5023823   4.6      4.7         0.3 West   AZ   
##  4  1999 Arkans~     5     28    2651860   1.1      1.1         0.2 South  AR   
##  5  1999 Califo~     6   1474   33499204   4.4      4.5         0.1 West   CA   
##  6  1999 Colora~     8    164    4226018   3.9      3.7         0.3 West   CO   
##  7  1999 Connec~     9    151    3386401   4.5      4.4         0.4 North~ CT   
##  8  1999 Delawa~    10     32     774990   4.1      4.1         0.7 South  DE   
##  9  1999 Distri~    11     28     570213   4.9      4.9         0.9 South  DC   
## 10  1999 Florida    12    402   15759421   2.6      2.6         0.1 South  FL   
## # ... with 790 more rows, and 1 more variable: division_name <chr>
# lower-case the state name to match the us_states data (from the maps package)
opiates$region <- tolower(opiates$state)

# join with the us_states data
opiates_map <- left_join(us_states, opiates)

library(viridis)

p0 <- ggplot(data = subset(opiates_map, year > 1999),
             mapping = aes(x = long, y = lat,
                           group = group,
                           fill = adjusted))

p1 <- p0 +
  geom_polygon(color = "gray90",
               size = 0.05) +
  coord_map(projection = "albers",
            lat0 = 39,
            lat1 = 45)

p2 <- p1 +
  scale_fill_viridis_c(option = "plasma")

p2 + 
  theme_map() +
  facet_wrap(~ year, ncol = 3) +
  theme(legend.position = "bottom",
        strip.background = element_blank()) +
  labs(title = "Opiate Related Deaths by State, 2000-2014",
       fill = "Death rate per 100,000 population")

But this might not be the best way to view this data due to the issues with population, demographics, and the difficulty in comparing things spatially.

Is Your Data Really Spatial?

We can make line plots of the opiates data over time, which may make it easier to make comparisons and draw conclusions.

p <- ggplot(data = opiates,
            mapping = aes(x = year,
                          y = adjusted, 
                          group = state))

p + geom_line(color = "gray70") # But this is a little difficult to see since there are so many lines
## Warning: Removed 17 row(s) containing missing values (geom_path).

# Divide it up by census division

p0 <- ggplot(data = drop_na(opiates, division_name), # remove rows with NA for division_name to leave out D.C.
            mapping = aes(x = year, 
                          y = adjusted))
p1 <- p0 +
  geom_line(color = "gray70",
            mapping = aes(group = state)) # make line chart, one line per state

p2 <- p1 + 
  geom_smooth(mapping = aes(group = division_name),
              se = FALSE) # make trend line by census division

p3 <- p2 + # label only the end of the line with the state
  geom_text_repel(data = subset(opiates, year == max(year) & abbr != "DC"),
                  mapping = aes(x = year,
                                y = adjusted,
                                label = abbr),
                  size = 1.8,
                  segment.color = NA, # no line segment linking text to point
                  nudge_x = 30) + # shift the label text to the right
  # shift coordinate system so there's room for the labels
  coord_cartesian(c(min(opiates$year), max(opiates$year))) 

p3 + 
  labs(x = "",
       y = "Rate per 100,000 population",
       title = "State-Level Opiate Death Rates by Census Division, 1999-2014") +
  facet_wrap(~ reorder(division_name, -adjusted, na.rm = TRUE),
              # put the divisions with highest rates first
             nrow = 3)
## Warning: Removed 27 rows containing non-finite values (stat_smooth).

## Warning: Removed 17 row(s) containing missing values (geom_path).

Where to Go Next

Pick at least two of the questions presented under the Where to Go Next section and answer them.

Refine your Plots

ggplot allows us to make lots of customization and refinement to our plots once we have finalized the data we want to show. This could include annotations, highlights, theme changes, and different colors.

# New dataset - membership and income data by section of the American Sociological Association
head(asasec)
##                                Section         Sname Beginning Revenues
## 1      Aging and the Life Course (018)         Aging     12752    12104
## 2     Alcohol, Drugs and Tobacco (030) Alcohol/Drugs     11933     1144
## 3 Altruism and Social Solidarity (047)      Altruism      1139     1862
## 4            Animals and Society (042)       Animals       473      820
## 5             Asia/Asian America (024)          Asia      9056     2116
## 6            Body and Embodiment (048)          Body      3408     1618
##   Expenses Ending Journal Year Members
## 1    12007  12849      No 2005     598
## 2      400  12677      No 2005     301
## 3     1875   1126      No 2005      NA
## 4     1116    177      No 2005     209
## 5     1710   9462      No 2005     365
## 6     1920   3106      No 2005      NA
# Plot membership vs. revenue for 2014
p <- ggplot(data = subset(asasec, Year == 2014),
            mapping = aes(x = Members,
                          y = Revenues,
                          label = Sname))  

# Use a linear function to estimate relationship
# Label the outliers
# Add titles and axis labels
# Make y-axis labels currency
# Move legend to the bottom
p4 <- p +
  geom_point(mapping = aes(color = Journal)) +
  geom_smooth(method = "lm", se = FALSE, color = "gray80") +
  geom_text_repel(data = subset(asasec, Year == 2014 & Revenues > 7000), 
                  size = 2) +
  labs(x = "Membership",
       y = "Revenues",
       color = "Section has own Journal",
       title = "ASA Sections",
       subtitle = "2014 Calendar Year.",
       caption = "Source: ASA annual report") +
  scale_y_continuous(labels = scales::dollar) +
  theme(legend.position = "bottom")

p4

Use Color to Your Advantage

When deciding on a color palette, think about whether the variable values are unordered or ordered; categorical or numeric. RColorBrewer offers many types of palettes and can be accessed within ggplot using scale_color_brewer() and scale_fill_brewer(). Color palettes can also be set manually by providing the hex codes and using scale_color_manual() and scale_fill_manual(). R also knows some color names, these can be accessed using demo('colors').

# Test different color palettes with the organdata
p <- ggplot(data = organdata,
            mapping = aes(x = roads,
                          y = donors,
                          color = world))

# Set2
p + geom_point(size = 2) +
  scale_color_brewer(palette = "Set2") +
  theme(legend.position = "bottom")
## Warning: Removed 46 rows containing missing values (geom_point).

# Pastel2
p + geom_point(size = 2) +
  scale_color_brewer(palette = "Pastel2") +
  theme(legend.position = "bottom")
## Warning: Removed 46 rows containing missing values (geom_point).

# Dark2
p + geom_point(size = 2) +
  scale_color_brewer(palette = "Dark2") +
  theme(legend.position = "bottom")
## Warning: Removed 46 rows containing missing values (geom_point).

# Use a manual palette

# Set the color values
cb_palette <- c("#999999", "#E69F00", "#56B4E9", "#009E73",
                "#F0E442", "#0072B2", "#D55E00", "#CC79A7")

# Use scale_color_manual() to set colors to the color blind palette
p4 +
  scale_color_manual(values = cb_palette)

# Note: only the first two colors are used because there are only two values in the color variable

# The dichromat package can be used to transform colors into color-blind safe colors
library(dichromat) # note: need to install

# Get list of colors - 5 colors from Set2
default <- RColorBrewer::brewer.pal(5, "Set2")

# List types of color-blindness
types <- c("deutan", "protan", "tritan")
names(types) <- c("Deuteronopia", "Protanopia", "Tritanopia")

# Create a table of colors that will transform the Set2 colors into what is seen under different color-blindness
color_table <- types %>%
    purrr::map(~ dichromat(default, .x)) %>%
    as_tibble() %>%
    add_column(default, .before = TRUE)

color_table
## # A tibble: 5 x 4
##   default Deuteronopia Protanopia Tritanopia
##   <chr>   <chr>        <chr>      <chr>     
## 1 #66C2A5 #AEAEA7      #BABAA5    #82BDBD   
## 2 #FC8D62 #B6B661      #9E9E63    #F29494   
## 3 #8DA0CB #9C9CCB      #9E9ECB    #92ABAB   
## 4 #E78AC3 #ACACC1      #9898C3    #DA9C9C   
## 5 #A6D854 #CACA5E      #D3D355    #B6C8C8
# view the colors
color_comp(color_table)

Layer Color and Text Together

We can use color and text to highlight certain parts of a plot. county_data contains data on the 2016 U.S. general election.

# Democrat Blue and Republican Red - create color palette
party_colors <- c("#2E74C0", "#CB454A")

# Initial plot - points for each county that did not flip
# x-axis is the population
# y-axis is the percent of the population that is Black
p0 <- ggplot(data = subset(county_data,
                           flipped == "No"),
             mapping = aes(x = pop,
                           y = black / 100))

p1 <- p0 +
  geom_point(alpha = 0.15,
             color = "gray50") +
  scale_x_log10(labels = scales::comma) # log-scale for population

p1

# Add a second layer of points for the counties that did flip
# color by party that won
p2 <- p1 +
  geom_point(data = subset(county_data,
                           flipped == "Yes"),
             mapping = aes(x = pop,
                           y = black / 100,
                           color = partywinner16)) +
  scale_color_manual(values = party_colors)

p2

# Set the y-axis scale and labels
p3 <- p2 +
  scale_y_continuous(labels = scales::percent) +
  labs(color = "County flipped to...",
       x = "County Population (log scale)",
       y = "Percent Black Population",
       title = "Flipped counties, 2016",
       caption = "Counties in gray did not flip.")

p3

# Label counties with high percentage of Black residents that flipped
p4 <- p3 +
  geom_text_repel(data = subset(county_data,
                                flipped == "Yes" & black > 25),
                  mapping = aes(x = pop,
                                y = black / 100,
                                label = state),
                  size = 2)

# Add a minimal theme and put the legend at the top
p4 + theme_minimal() +
  theme(legend.position = "top")

Change the Appearance of Plots with Themes

The theme of a plot can change the overall appearance. Using theme_set() with the name of a theme as the argument of the function, we can set the theme for all plots in a session. We can also use + with a theme function to add a theme to a specific plot. The theme() function can be used to fine-tune different aspects of the plot (like the legend position). ggplot contains some default themes; the ggthemes package contains more themes. There are other pre-made themes available in the cowplot and hrbrthemes packages. We can also manually set theme elements within the theme() function to make our own custom themes.

# theme_bw()
theme_set(theme_bw())
p4 + theme(legend.position = "top")

# theme_dark
theme_set(theme_dark())
p4 + theme(legend.position = "top")

library(ggthemes)

# Economist Theme
theme_set(theme_economist())
p4 + theme(legend.position="top")

# Wall Street Journal Theme
theme_set(theme_wsj())

# Text sizes are quite large on this theme, so set them in the theme() function to be smaller
p4 + theme(plot.title = element_text(size = rel(0.6)),
           legend.title = element_text(size = rel(0.35)),
           plot.caption = element_text(size = rel(0.35)),
           legend.position = "top")

# Clear out theme setting
theme_set(theme())

# Make a custom theme
# Warnings - font family not found in Windows font database
# Needed to update the family names
# Added hjust to left-align title
p4 + theme(legend.position = "top",
           plot.title = element_text(size = rel(2),
                                     lineheight = .5,
                                     family = "serif",
                                     face = "bold.italic",
                                     color = "orange",
                                     hjust = 0),
           axis.text.x = element_text(size = rel(1.1),
                                      family = "mono",
                                      face = "bold",
                                      color = "purple"),
           panel.background = element_blank(),
           panel.grid.major = element_line(color = "gray90"),
           legend.key = element_blank())

Use Theme Elements in a Substantive Way

The theme() in ggplot can be used to fix design elements and create custom figures.

# Dataset: age of each GSS respondent for all years since 1972

# Aggregation - mean age of respondents for years of interest
table(gss_lon$year)
## 
## 1972 1973 1974 1975 1976 1977 1978 1980 1982 1983 1984 1985 1986 1987 1988 1989 
## 1613 1504 1484 1490 1499 1530 1532 1468 1860 1599 1473 1534 1470 1819 1481 1537 
## 1990 1991 1993 1994 1996 1998 2000 2002 2004 2006 2008 2010 2012 2014 2016 
## 1372 1517 1606 2992 2904 2832 2817 2765 2812 4510 2023 2044 1974 2538 2867
yrs <- c(seq(1972, 1988, 4), # survey occurred every 4 years, 1972 - 1988
         1993, # survey occurred in 1993
         seq(1996, 2016, 4) # survey occurred every 4 years, 1996 - 2016
         ) 

# Compute mean age
mean_age <- gss_lon %>%
  filter(age %nin% NA && year %in% yrs) %>%
  group_by(year) %>%
  summarise(xbar = round(mean(age, na.rm = TRUE), 0))

# Add y-axis coordinate for text label
mean_age$y <- 0.3

# Create location and label for years
yr_labs <- data.frame(x = 85,
                      y = 0.8,
                      year = yrs)

# Make plot
p <- ggplot(data = subset(gss_lon, year %in% yrs),
            mapping = aes(x = age))

p1 <- p + 
  geom_density(fill = "gray20", 
               color = FALSE,
               alpha = 0.9, 
               mapping = aes(y = ..scaled..)) +
  geom_vline(data = subset(mean_age, year %in% yrs),
             mapping = aes(xintercept = xbar), 
             color = "white", 
             size = 0.5) +
  geom_text(data = subset(mean_age, year %in% yrs),
            mapping = aes(x = xbar, y = y, label = xbar), 
            nudge_x = 7.5,
            color = "white", 
            size = 3.5, 
            hjust = 1) +
  geom_text(data = subset(yr_labs, year %in% yrs),
            mapping = aes(x = x, y = y, label = year)) +
  facet_grid(year ~ ., 
             switch = "y") # put the year labels for the facets on the left

p1 
## Warning: Removed 83 rows containing non-finite values (stat_density).

# Add theme
# Remove theme_book()
# p1 + theme_book(base_size = 10,  
#                 plot_title_size = 10,
#                 strip_text_size = 32, 
#                 panel_spacing = unit(0.1, "lines")) +
p1 +
  theme_minimal() +
  theme(plot.title = element_text(size = 16),
        axis.text.x= element_text(size = 12),
        axis.title.y = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank(),
        strip.background = element_blank(),
        strip.text.y = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank()) +
  labs(x = "Age",
       y = NULL,
       title = "Age Distribution of\nGSS Respondents")
## Warning: Removed 83 rows containing non-finite values (stat_density).

# Make this plot with ggridges
library(ggridges)

# Plot all years; ordered by year (factor())
p <- ggplot(data = gss_lon,
            mapping = aes(x = age, 
                          y = factor(year, 
                                     levels = rev(unique(year)),
                                     ordered = TRUE)))

p + geom_density_ridges(alpha = 0.6, 
                        fill = "lightblue", 
                        scale = 1.5) +
    scale_x_continuous(breaks = c(25, 50, 75)) +
    scale_y_discrete(expand = c(0.01, 0)) + 
    labs(x = "Age",
         y = NULL,
         title = "Age Distribution of\nGSS Respondents") +
    theme_ridges() +
    theme(title = element_text(size = 16, face = "bold"))
## Warning: Removed 221 rows containing non-finite values (stat_density_ridges).

Case Studies

Will be covered in Week 11

Where to Go Next

Pick at least two of the questions presented under the Where to Go Next section and answer them.

Heat Maps

This section is not in the book as is taken from Time Based Heatmaps in R. geom_tile() can be used to create heat maps.

# Data - 911 calls in Seattle
incidents <- read_csv("https://raw.githubusercontent.com/lgellis/MiscTutorial/master/ggmap/i2Sample.csv") 

# Assign color variables
col1 = "#d8e1cf" 
col2 = "#438484"

# Peek at the data set 
head(incidents)
## # A tibble: 6 x 20
##      ...1 CAD.CDW.ID CAD.Event.Number General.Offense.Number Event.Clearance.Co~
##     <dbl>      <dbl>            <dbl>                  <dbl> <chr>              
## 1  450024     813558      12000168676             2012168676 064                
## 2 1028516    1002946      12000438147             2012438147 100                
## 3 1319344    1036985      15000080173              201580173 161                
## 4 1334148    1053917      15000105067             2015105067 100                
## 5   32080      48491      10000294206             2010294206 281                
## 6  326478     681155      11000387576             2011387576 250                
## # ... with 15 more variables: Event.Clearance.Description <chr>,
## #   Event.Clearance.SubGroup <chr>, Event.Clearance.Group <chr>,
## #   Event.Clearance.Date <chr>, Hundred.Block.Location <chr>,
## #   District.Sector <chr>, Zone.Beat <chr>, Census.Tract <chr>,
## #   Longitude <dbl>, Latitude <dbl>, Incident.Location <chr>,
## #   Initial.Type.Description <chr>, Initial.Type.Subgroup <chr>,
## #   Initial.Type.Group <chr>, At.Scene.Time <chr>
str(incidents)
## spec_tbl_df [50,000 x 20] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ ...1                       : num [1:50000] 450024 1028516 1319344 1334148 32080 ...
##  $ CAD.CDW.ID                 : num [1:50000] 813558 1002946 1036985 1053917 48491 ...
##  $ CAD.Event.Number           : num [1:50000] 1.2e+10 1.2e+10 1.5e+10 1.5e+10 1.0e+10 ...
##  $ General.Offense.Number     : num [1:50000] 2.01e+09 2.01e+09 2.02e+08 2.02e+09 2.01e+09 ...
##  $ Event.Clearance.Code       : chr [1:50000] "064" "100" "161" "100" ...
##  $ Event.Clearance.Description: chr [1:50000] "SHOPLIFT" "FRAUD (INCLUDING IDENTITY THEFT)" "TRESPASS" "FRAUD (INCLUDING IDENTITY THEFT)" ...
##  $ Event.Clearance.SubGroup   : chr [1:50000] "THEFT" "FRAUD CALLS" "TRESPASS" "FRAUD CALLS" ...
##  $ Event.Clearance.Group      : chr [1:50000] "SHOPLIFTING" "FRAUD CALLS" "TRESPASS" "FRAUD CALLS" ...
##  $ Event.Clearance.Date       : chr [1:50000] "05/31/2012 06:00:00 PM" "12/24/2012 11:14:00 AM" "03/11/2015 12:45:00 PM" "03/31/2015 04:56:00 PM" ...
##  $ Hundred.Block.Location     : chr [1:50000] "39XX BLOCK OF S OTHELLO ST" "27XX BLOCK OF ALKI AVE SW" "6XX BLOCK OF NW MARKET ST" "77XX BLOCK OF RAINIER AV S" ...
##  $ District.Sector            : chr [1:50000] "S" "W" "B" "S" ...
##  $ Zone.Beat                  : chr [1:50000] "S1" "W1" "B2" "S2" ...
##  $ Census.Tract               : chr [1:50000] "11000.1011" "9701.2000" "4800.4000" "11102.4008" ...
##  $ Longitude                  : num [1:50000] -122 -122 -122 -122 -122 ...
##  $ Latitude                   : num [1:50000] 47.5 47.6 47.7 47.5 47.6 ...
##  $ Incident.Location          : chr [1:50000] "(47.537044021, -122.282344886)" "(47.579317217, -122.409989598)" "(47.668651602, -122.364558421)" "(47.533143434, -122.269986901)" ...
##  $ Initial.Type.Description   : chr [1:50000] NA "TRU - FORGERY/CHKS/BUNCO/SCAMS/ID THEFT" "BURG - RES (INCL UNOCC STRUCTURES ON PROP)" "FRAUD - FORGERY,BUNCO, SCAMS, ID THEFT, ETC" ...
##  $ Initial.Type.Subgroup      : chr [1:50000] NA "FRAUD CALLS" "BURGLARY" "FRAUD CALLS" ...
##  $ Initial.Type.Group         : chr [1:50000] NA "FRAUD CALLS" "RESIDENTIAL BURGLARIES" "FRAUD CALLS" ...
##  $ At.Scene.Time              : chr [1:50000] NA "12/24/2012 10:33:00 AM" NA NA ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   ...1 = col_double(),
##   ..   CAD.CDW.ID = col_double(),
##   ..   CAD.Event.Number = col_double(),
##   ..   General.Offense.Number = col_double(),
##   ..   Event.Clearance.Code = col_character(),
##   ..   Event.Clearance.Description = col_character(),
##   ..   Event.Clearance.SubGroup = col_character(),
##   ..   Event.Clearance.Group = col_character(),
##   ..   Event.Clearance.Date = col_character(),
##   ..   Hundred.Block.Location = col_character(),
##   ..   District.Sector = col_character(),
##   ..   Zone.Beat = col_character(),
##   ..   Census.Tract = col_character(),
##   ..   Longitude = col_double(),
##   ..   Latitude = col_double(),
##   ..   Incident.Location = col_character(),
##   ..   Initial.Type.Description = col_character(),
##   ..   Initial.Type.Subgroup = col_character(),
##   ..   Initial.Type.Group = col_character(),
##   ..   At.Scene.Time = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
# Prepare data - get month/year/day/etc. using lubridate package
incidents$ymd <-lubridate::mdy_hms(incidents$Event.Clearance.Date)
incidents$month <- lubridate::month(incidents$ymd, label = TRUE)
incidents$year <- lubridate::year(incidents$ymd)
incidents$wday <- lubridate::wday(incidents$ymd, label = TRUE)
incidents$hour <- lubridate::hour(incidents$ymd)

# Create a summary table that has the number of incidents by hour and day
dayHour <- incidents %>%
  group_by(hour, wday) %>%
  summarise(N = n())

# Order by weekday in reverse
dayHour$wday <- factor(dayHour$wday, levels = rev(levels(dayHour$wday)))

# Remove NAs
dayHour <- dayHour %>% na.omit()

# Use geom_tile() to create a heat map
ggplot(data = dayHour, 
       mapping = aes(x = hour, 
                     y = wday)) + 
  geom_tile(aes(fill = N), 
            colour = "white") + # white outline
  scale_fill_gradient(low = col1, 
                      high = col2) +  
  theme_minimal() + 
  labs(title = "Heat Map of Seattle Incidents by Day of Week and Hour",
       x = "Incidents Per Hour", 
       y = "Day of Week",
       fill = "Total Incidents") +
  # Remove the gridlines
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank())

# Create summary table for the number of incidents by group and hour
groupSummary <- incidents %>%
  group_by(Event.Clearance.Group, hour) %>%
  summarise(N = n()) %>%
  filter(!is.na(Event.Clearance.Group)) # remove NA event

# Make heat map
ggplot(data = groupSummary, 
       mapping = aes(x = hour,
                     y = Event.Clearance.Group)) + 
  geom_tile(aes(fill = N), 
            colour = "white") +
  scale_fill_gradient(low = col1, 
                      high = col2) +  
  labs(title = "Heat Map of Seattle Incidents by Event and Hour",
       x = "Hour", 
       y = "Event",
       fill = "Total Incidents") +
  theme_minimal() + 
  # Remove gridlines
  theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank())
## Warning: Removed 4 rows containing missing values (geom_tile).

LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiBOb3RlcyINCmF1dGhvcjogIkplbm4gU2NoaWxsaW5nIg0KZGF0ZTogIjIwMjItMDMtMzAiDQpvdXRwdXQ6DQogaHRtbF9kb2N1bWVudDoNCiAgdGhlbWU6ICJmbGF0bHkiICMgVGhlbWUgR2FsbGVyeTogaHR0cHM6Ly93d3cuZGF0YWRyZWFtaW5nLm9yZy9wb3N0L3ItbWFya2Rvd24tdGhlbWUtZ2FsbGVyeS8NCiAgdG9jOiBUUlVFDQogIHRvY19mbG9hdDogVFJVRQ0KICBjb2RlX2Rvd25sb2FkOiBUUlVFDQotLS0NCiAgIA0KIyMgRGF0YSBWaXN1YWxpemF0aW9uIE5vdGVzDQoNClRoZXNlIGFyZSBteSBub3RlcyBmb3IgKipHUkQgNjEwQTogRGF0YSBWaXN1YWxpemF0aW9uIElJKiogaW4gV2ludGVyIDIwMjIgYXQgdGhlIENvbGxlZ2UgZm9yIENyZWF0aXZlIFN0dWRpZXMuIFRoZXNlIG5vdGVzIGFyZSBmb3IgbXkgd29yayBpbiB0aGUgYm9vayAqRGF0YSBWaXN1YWxpemF0aW9uKiBieSBLaWVyYW4gSGVhbHkgKFByaW5jZXRvbiBVbml2ZXJzaXR5IFByZXNzLCAyMDE5KS4gDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KDQojIyBCeSBkZWZhdWx0LCBzaG93IGNvZGUgZm9yIGFsbCBjaHVua3MgaW4gdGhlIGtuaXR0ZWQgZG9jdW1lbnQsDQojIyBhcyB3ZWxsIGFzIHRoZSBvdXRwdXQuIFRvIG92ZXJyaWRlIGZvciBhIHBhcnRpY3VsYXIgY2h1bmsNCiMjIHVzZSBlY2hvID0gRkFMU0UgaW4gaXRzIG9wdGlvbnMuDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSkgDQoNCiMjIFNldCB0aGUgZGVmYXVsdCBzaXplIG9mIGZpZ3VyZXMNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9OCwgZmlnLmhlaWdodD01KSAgDQoNCiMjIExvYWQgdGhlIGxpYnJhcmllcyB3ZSB3aWxsIGJlIHVzaW5nDQpsaWJyYXJ5KGdhcG1pbmRlcikNCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkoc29jdml6KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmxpYnJhcnkoZ2dyZXBlbCkNCg0KIyMgTGlicmFyaWVzOiBJbnN0YWxsIG9uY2UgcGVyIG1hY2hpbmUsIGxvYWQgb25jZSBwZXIgUiBzZXNzaW9uDQoNCiMgVGhlbWUgTWFwDQp0aGVtZV9tYXAgPC0gZnVuY3Rpb24oYmFzZV9zaXplPTksIGJhc2VfZmFtaWx5PSIiKSB7DQogICAgcmVxdWlyZShncmlkKQ0KICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZT1iYXNlX3NpemUsIGJhc2VfZmFtaWx5PWJhc2VfZmFtaWx5KSANCiAgICAgICAgdGhlbWUoYXhpcy5saW5lPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgYXhpcy50aWNrcz1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgcGFuZWwuYm9yZGVyPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgcGFuZWwuZ3JpZD1lbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgIHBhbmVsLnNwYWNpbmc9dW5pdCgwLCAibGluZXMiKSwNCiAgICAgICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kPWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSBjKDAsMCksDQogICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMCwwKQ0KICAgICAgICAgICAgICApDQp9DQoNCmBgYA0KDQoNCiMjIEdldCBTdGFydGVkDQoNCiMjIyBFdmVyeXRoaW5nIGhhcyBhIG5hbWUNCk9iamVjdHMgaW4gUiBhcmUgY3JlYXRlZCBhbmQgcmVmZXJyZWQgdG8gYnkgdGhlaXIgbmFtZXMuIENlcnRhaW4gbmFtZXMgYXJlIG5vdCBhbGxvd2VkIGJlY2F1c2UgdGhleSBhcmUgcmVzZXJ2ZWQgd29yZHMgc3VjaCBhcyBgYFRSVUVgYCwgYGBpZmBgLCBgYG1lYW4oKWBgLCBhbmQgYGBOQWBgLiBOYW1lcyBhbHNvIGNhbm5vdCBzdGFydCB3aXRoIGEgbnVtYmVyIG9yIGNvbnRhaW4gc3BhY2VzLiBUaGVyZSBhcmUgZGlmZmVyZW50IG5hbWluZyBjb252ZW50aW9ucy4gIA0KDQoqKlNuYWtlIENhc2UqKiAgDQpgYG15X2RhdGFgYCAgDQpgYHRoaXNfaXNfc25ha2VfY2FzZWBgICANCg0KKipDYW1lbCBDYXNlKiogIA0KYGBteURhdGFgYCAgDQpgYHRoaXNJc0NhbWVsQ2FzZWBgDQoNCioqUGFzY2FsIENhc2UqKiAgDQpgYE15RGF0YWBgICANCmBgVGhpc0lzUGFzY2FsQ2FzZWBgDQoNClBpY2sgb25lIG5hbWluZyBjb252ZW50aW9uIGFuZCBzdGljayB3aXRoIGl0LiBCZSBjb25zaXN0ZW50OyBkb24ndCBzd2l0Y2ggYmV0d2VlbiBjb252ZW50aW9ucy4gSSByZWNvbW1lbmQgc25ha2UgY2FzZS4NCg0KYGBge3IgbmFtaW5nLCBlcnJvcj1UUlVFfQ0KDQojIFRoaXMgaXMgYSBjb21tZW50IChpdCBzdGFydHMgd2l0aCAjKQ0KDQpteV9kYXRhIDwtIGMoMSwgMiwgMywgNCkgIyBBc3NpZ24gdXNpbmcgPC0gOyB1c2UgQUxUICsgLSBvciBPUFRJT04gKyAtDQoNCk15X0RhdGEgIA0KIyBDYW5ub3QgYmUgZm91bmQgYmVjYXVzZSB3ZSBjYWxsZWQgaXQgbXlfZGF0YSAobG93ZXJjYXNlKQ0KDQojIE5vdyB3ZSBjYW4gc2VlIGl0DQpteV9kYXRhIA0KDQpgYGANCg0KIyMjIEV2ZXJ5dGhpbmcgaXMgYW4gb2JqZWN0OyB1c2luZyBmdW5jdGlvbnMNClRoaW5rIG9mIGZ1bmN0aW9ucyBsaWtlIGEgcmVjaXBlLiBUaGUgYXJndW1lbnRzIG9mIHRoZSBmdW5jdGlvbiBhcmUgdGhlIGluZ3JlZGllbnRzIGFuZCB3aGF0IGhhcHBlbnMgd2l0aGluIHRoZSBmdW5jdGlvbiBpcyB0aGUgc2VxdWVuY2Ugb2YgY29va2luZyBzdGVwcy4gDQpgYGB7ciBvYmplY3RzLWZ1bmN0aW9uc30NCg0KYygxLCAyLCAzLCAxLCAzLCA1LCAyNSkgIyBjKCkgaXMgdGhlIGNvbWJpbmUgZnVuY3Rpb24sIGl0IHB1dHMgdGhpbmdzIHRvZ2V0aGVyIGludG8gYSB2ZWN0b3IvbGlzdA0KDQpteV9udW1iZXJzIDwtIGMoMSwgMiwgMywgMSwgMywgNSwgMjUpDQp5b3VyX251bWJlcnMgPC0gYyg1LCAzMSwgNzEsIDEsIDMsIDIxLCA2KQ0KDQpteV9udW1iZXJzDQoNCm1lYW4oeCA9IG15X251bWJlcnMpDQptZWFuKG15X251bWJlcnMpICMgeW91IGRvbid0IGhhdmUgdG8gc3BlY2lmeSB0aGUgYXJndW1lbnQgbmFtZXMsIGJ1dCBvcmRlciBtYXR0ZXJzIGlmIHlvdSBkbyBub3Qgc3BlY2lmeQ0KDQptZWFuKHggPSB5b3VyX251bWJlcnMpDQoNCm15X3N1bW1hcnkgPC0gc3VtbWFyeShteV9udW1iZXJzKQ0KDQpteV9zdW1tYXJ5DQoNCnRhYmxlKG15X251bWJlcnMpDQoNCnNkKG15X251bWJlcnMpDQoNCm15X251bWJlcnMgKiA1DQoNCm15X251bWJlcnMgKyAxDQoNCm15X251bWJlcnMgKyBteV9udW1iZXJzICMgSG93IGlzIHRoaXMgZGlmZmVyZW50IHRoYW4gdGhlIGxhc3QgbGluZT8NCg0KIyBJZiB5b3UncmUgbm90IHN1cmUgd2hhdCBhbiBvYmplY3QgaXMsIGFzayBmb3IgaXRzIGNsYXNzIG9yIHR5cGUNCg0KY2xhc3MobXlfbnVtYmVycykNCg0KY2xhc3MobXlfc3VtbWFyeSkNCg0KY2xhc3Moc3VtbWFyeSkNCg0KbXlfbmV3X3ZlY3RvciA8LSBjKG15X251bWJlcnMsICJBcHBsZSIpICMgV2hhdCBoYXBwZW5zIGlmIHdlIGNvbWJpbmUgYSB3b3JkIHdpdGggbnVtYmVycz8NCg0KbXlfbmV3X3ZlY3Rvcg0KDQpjbGFzcyhteV9uZXdfdmVjdG9yKQ0KDQojIExldCdzIGxvb2sgYXQgYSBuZXcgZGF0YXNldA0KDQp0aXRhbmljDQoNCmNsYXNzKHRpdGFuaWMpIA0KDQojIFRpdGFuaWMgaXMgYSBkYXRhIGZyYW1lLCB3aGljaCBpcyBsaWtlIGEgdGFibGUNCiMgVGhlICQgb3BlcmF0b3IgY2FuIGJlIHVzZWQgdG8gYWNjZXNzIGEgY29sdW1uIG9mIGEgZGF0YSBmcmFtZSBieSBuYW1lDQoNCnRpdGFuaWMkcGVyY2VudA0KDQojIFRpYmJsZXMgYXJlIHNsaWdodGx5IGRpZmZlcmVudCB0aGFuIGRhdGEgZnJhbWVzLiBUaGV5IGFyZSBhbHNvIGRhdGEgdGFibGVzLCBidXQgdGhleSBwcm92aWRlIG1vcmUgaW5mb3JtYXRpb24uDQoNCnRpdGFuaWNfdGIgPC0gYXNfdGliYmxlKHRpdGFuaWMpDQoNCnRpdGFuaWNfdGIgIyBIb3cgaXMgZG9lcyB0aGlzIGNvbXBhcmUgdG8gdGl0YW5pYyBhYm92ZT8NCg0KIyBUbyBzZWUgaW5zaWRlIGFuIG9iamVjdCwgYXNrIGZvciBpdHMgc3RydWN0dXJlDQoNCnN0cihteV9udW1iZXJzKQ0KDQpzdHIobXlfc3VtbWFyeSkNCg0KYGBgDQpQcm9ncmFtbWluZyBpbiBSIGNhbiBiZSBjaGFsbGVuZ2luZyBhbmQgaXQgdGFrZXMgdGltZSB0byBnZXQgdXNlZCB0by4gQmUgcGF0aWVudCBhbmQgdGFrZSBhIGJyZWFrIGlmIHlvdSBnZXQgc3R1Y2suIE1ha2Ugc3VyZSBwYXJlbnRoZXNlcyBhcmUgb3BlbmVkIGFuZCBjbG9zZWQuIENvbXBsZXRlIHlvdXIgY29tbWFuZHMgKGxvb2sgb3V0IGZvciB0aGUgKyBpbiB0aGUgY29uc29sZSkuIFRha2UgeW91ciB0aW1lIGFuZCBsb29rb3V0IGZvciB0eXBvcy4gDQoNCiMjIyBHZXQgRGF0YSBpbnRvIFINCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBnZXQgZGF0YSBmcm9tIGEgVVJMIGFuZCBtYWtlIGEgcXVpY2sgZmlndXJlLg0KDQpgYGB7ciBnZXQtZGF0YX0NCg0KIyBEYXRhIHNvdXJjZQ0KdXJsIDwtICJodHRwczovL2Nkbi5yYXdnaXQuY29tL2tqaGVhbHkvdml6LW9yZ2FuZGF0YS9tYXN0ZXIvb3JnYW5kb25hdGlvbi5jc3YiDQoNCiMgUmVhZCB0aGUgQ1NWIGZyb20gdGhlIFVSTA0Kb3JnYW5zIDwtIHJlYWRfY3N2KGZpbGUgPSB1cmwpDQoNCiMgVGFrZSBhIHF1aWNrIGxvb2sgYXQgdGhlIGRhdGENCmdsaW1wc2Uob3JnYW5zKQ0KDQojIFZpZXcob3JnYW5zKSAjIFJ1biBpbiBSU3R1ZGlvDQoNCmBgYA0KDQpgYGB7ciBtYWtlLWZpZ3VyZX0NCg0KIyBBbm90aGVyIHdheSB0byB2aWV3IGRhdGEgDQpnYXBtaW5kZXINCg0KIyBNYWtlIGEgcGxvdCBvYmplY3QNCnAgPC0gZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbGlmZUV4cCkpDQoNCiMgQ3JlYXRlIGEgc2NhdHRlcnBsb3QNCnAgKyBnZW9tX3BvaW50KCkNCg0KYGBgDQoNCg0KIyMgTWFrZSBhIFBsb3QNCg0KYGBnZ3Bsb3QyYGAgaXMgYW4gYGBSYGAgbGlicmFyeS9wYWNrYWdlIHRoYXQgYWxsb3dzIHVzIHRvIG1hcCBkYXRhIHRvIHZpc3VhbCBlbGVtZW50cy4gVXNpbmcgaXQgd2UgY2FuIGNvbnRyb2wgdGhlIHdheSB0aGUgZGF0YSBhcHBlYXJzIGluIHRoZSBwbG90IGFuZCBob3cgZWFjaCBlbGVtZW50IG9mIHRoZSBwbG90IHdpbGwgYmUgZGlzcGxheWVkLiAqKkFlc3RoZXRpYyBNYXBwaW5ncyoqIG1ha2UgdGhlIGNvbm5lY3Rpb24gYmV0d2VlbiB0aGUgZGF0YSBhbmQgaG93IGl0IGlzIGRpc3BsYXllZCBvbiB0aGUgcGxvdCAobG9jYXRpb24sIHNpemUsIGNvbG9yLCBzaGFwZSwgZXRjLikuICoqR2VvbXMqKiBkZWZpbmUgdGhlIHR5cGUgb2YgcGxvdCAoc2NhdHRlcnBsb3QsIGxpbmUgcGxvdCwgYm94IHBsb3QsIGJhciBjaGFydCwgZXRjLikuIENvZGUgaXMgYWRkZWQgdG9nZXRoZXIgdG8gbWFrZSB0aGUgcGxvdCB1c2luZyBgYCtgYCB0aGUgcGx1cyBzaWduLiBNb3JlIHBpZWNlcyBjYW4gYmUgYWRkZWQgdG8gdGhlIHBsb3QgdGhhdCBkZWZpbmUgdGhlIHNjYWxlcywgbGVnZW5kLCBsYWJlbHMsIGF4ZXMsIHN0eWxlIG9yIHRoZW1lIG9mIHRoZSBwbG90LCBldGMuIEVhY2ggcGFydCBjYW4gYmUgYWRkZWQgdXNpbmcgZGlmZmVyZW50IGZ1bmN0aW9ucyB3aXRoIGFyZ3VtZW50cyBzcGVjaWZ5aW5nIHRoZSBsb29rIG9mIHRoZSBwbG90OyB0aGUgcGxvdCBpcyBidWlsdCB1cCBwaWVjZSBieSBwaWVjZS4gDQoNCiMjIyBUaWR5IERhdGENCkluIHRpZHkgZGF0YTogIA0KMS4gRWFjaCB2YXJpYWJsZSBmb3JtcyBhIGNvbHVtbi4gIA0KMi4gRWFjaCBvYnNlcnZhdGlvbiBmb3JtcyBhIHJvdy4gIA0KMy4gRWFjaCB0eXBlIG9mIG9ic2VydmF0aW9uYWwgdW5pdCBmb3JtcyBhIHRhYmxlLiAgDQoNCkZyb20gV2lja2hhbSwgSC4gKDIwMTQpLiBUaWR5IERhdGEuICpKb3VybmFsIG9mIFN0YXRpc3RpY2FsIFNvZnR3YXJlKiwgNTkoMTApLg0KDQojIyMgTWFwcGluZw0KQnVpbGQgYSBwbG90IGxheWVyIGJ5IGxheWVyLCBzdGFydGluZyB3aXRoIHRlbGxpbmcgZ2dwbG90IHdoYXQgZGF0YSB0byB1c2UgYW5kIGhvdyB0byBtYXAgb3IgbGluayBpdCAgdG8gcGFydHMgb2YgdGhlIHBsb3QsIGxpa2UgdGhlIHggYW5kIHkgYXhlcy4gVGhlbiBhZGQgb24gdGhlIHR5cGUgb2YgYGBnZW9tYGAuIA0KDQpgYGB7ciBjaC0zLWZpZy0xfQ0KDQpwIDwtIGdncGxvdChkYXRhID0gZ2FwbWluZGVyLA0KICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxpZmVFeHApKSANCg0KICBwICsgZ2VvbV9wb2ludCgpDQoNCmBgYA0KDQojIyMgTGF5ZXIgYnkgTGF5ZXINCg0KVHJ5aW5nIGRpZmZlcmVudCBgYGdlb21fYGAgZnVuY3Rpb25zLg0KDQpgYGB7ciBjaC0zLWZpZy0yfQ0KDQpwIDwtIGdncGxvdChkYXRhID0gZ2FwbWluZGVyLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbGlmZUV4cCkpDQoNCnAgKyBnZW9tX3Ntb290aCgpDQoNCnAgKyBnZW9tX3BvaW50KCkgKyAjIGFkZCB0aGUgcG9pbnRzIGJhY2sgaW50byB0aGUgcGxvdCANCiAgZ2VvbV9zbW9vdGgoKSANCg0KcCArIGdlb21fcG9pbnQoKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSAjIHVzZSBhIGxpbmVhciBtb2RlbA0KDQpwICsgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImdhbSIpICsgIyBnZW5lcmFsaXplZCBhZGRpdGl2ZSBtb2RlbA0KICBzY2FsZV94X2xvZzEwKCkgIyB0cmFuc2Zvcm0geC1heGlzIHRvIGxvZy0xMCBzY2FsZQ0KDQpwICsgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImdhbSIpICsNCiAgc2NhbGVfeF9sb2cxMChsYWJlbHMgPSBzY2FsZXM6OmRvbGxhcikgIyBmb3JtYXQgeC1heGlzIGluIGRvbGxhcnMNCg0KYGBgDQoNCiMjIyBNYXBwaW5nIEFlc3RoZXRpY3MNCg0KVXNpbmcgdGhlIGFlc3RoZXRpY3MgbWFwcGluZywgZGlmZmVyZW50IHBhcnRzIG9mIHRoZSBkYXRhIGNhbiBiZSBlbmNvZGVkIGluIGRpZmZlcmVudCB3YXlzLiAgDQoNCmBgYHtyIGNoMy1maWctM30NCg0KDQpwIDwtIGdncGxvdChkYXRhID0gZ2FwbWluZGVyLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbGlmZUV4cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAicHVycGxlIikpICMgZ2dwbG90IGFkZHMgdGhlIHZhbHVlICJwdXJwbGUiIHRvIGFsbCByb3dzDQoNCnAgKyBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiKSArDQogIHNjYWxlX3hfbG9nMTAoKQ0KDQojIFRvIGFjdHVhbGx5IHR1cm4gYWxsIG9mIHRoZSBwb2ludHMgcHVycGxlLCB3ZSBuZWVkIHRvIHNldCB0aGUgY29sb3IgcHJvcGVydHkgb2YgdGhlIGdlb21fIGZ1bmN0aW9uDQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsaWZlRXhwKSkNCg0KcCArIGdlb21fcG9pbnQoY29sb3IgPSAicHVycGxlIikgKyAjIHNldCBwb2ludCBjb2xvciB0byBwdXJwbGUNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIikgKw0KICBzY2FsZV94X2xvZzEwKCkNCg0KcCArIGdlb21fcG9pbnQoYWxwaGEgPSAwLjMpICsgIyBtYWtlIHBvaW50cyBtb3JlIHRyYW5zcGFyZW50DQogIGdlb21fc21vb3RoKGNvbG9yID0gIm9yYW5nZSIsICMgbWFrZSBsaW5lIG9yYW5nZQ0KICAgICAgICAgICAgICBzZSA9IEZBTFNFLCAjIHJlbW92ZSBzdGFuZGFyZCBlcnJvciBiYW5kDQogICAgICAgICAgICAgIHNpemUgPSA4LCAjIGluY3JlYXNlIHRoaWNrbmVzcyBvZiB0aGUgbGluZQ0KICAgICAgICAgICAgICBtZXRob2QgPSAibG0iKSArDQogIHNjYWxlX3hfbG9nMTAoKQ0KDQoNCnAgKyBnZW9tX3BvaW50KGFscGhhID0gMC4zKSArICMgbWFrZSBwb2ludHMgbW9yZSB0cmFuc3BhcmVudA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2FtIikgKw0KICBzY2FsZV94X2xvZzEwKGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyKSArDQogICMgQWRkIHRpdGxlIGFuZCBsYWJlbHMNCiAgbGFicyh4ID0gIkdEUCBwZXIgQ2FwaXRhIiwgDQogICAgICAgeSA9ICJMaWZlIEV4cGVjdGFuY3kgaW4gWWVhcnMiLA0KICAgICAgIHRpdGxlID0gIkVjb25vbWljIEdyb3d0aCBhbmQgTGlmZSBFeHBlY3RhbmN5IiwNCiAgICAgICBzdWJ0aXRsZSA9ICJEYXRhIHBvaW50cyBhcmUgY291bnRyeS15ZWFycyIsDQogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IEdhcG1pbmRlciIpDQoNCiMgTWFwIGRhdGEgYnkgY29udGluZW50DQpwIDwtIGdncGxvdChkYXRhID0gZ2FwbWluZGVyLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbGlmZUV4cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb250aW5lbnQpKQ0KDQpwICsgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIikgKw0KICBzY2FsZV94X2xvZzEwKCkNCg0KDQpwIDwtIGdncGxvdChkYXRhID0gZ2FwbWluZGVyLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gZ2RwUGVyY2FwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbGlmZUV4cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb250aW5lbnQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjb250aW5lbnQpKSAjIG5vdyB0aGUgZXJyb3IgYmFuZHMgd2lsbCBhbHNvIGhhdmUgdGhlIGNvbG9yDQoNCnAgKyBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiKSArDQogIHNjYWxlX3hfbG9nMTAoKQ0KDQpgYGANCg0KIyMjIEFlc3RoZXRpY3MgYnkgR2VvbQ0KDQpgYGB7ciBjaDMtZmlnLTR9DQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBnZHBQZXJjYXAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsaWZlRXhwKSkNCg0KcCArIGdlb21fcG9pbnQobWFwcGluZyA9IGFlcyhjb2xvciA9IGNvbnRpbmVudCkpICsgIyBwb2ludHMgd2lsbCBiZSBjb2xvcmVkIGJ5IGNvbnRpbmVudA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG9lc3MiKSArICMgdGhlIHNtb290aGVkIGxpbmUgd2lsbCBiZSBmb3IgYWxsIGRhdGENCiAgc2NhbGVfeF9sb2cxMCgpDQoNCnAgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoY29sb3IgPSBsb2cocG9wKSkpICsgIyBwb2ludHMgd2lsbCBiZSBjb2xvcmVkIGJ5IHBvcHVsYXRpb24NCiAgc2NhbGVfeF9sb2cxMCgpDQoNCg0KYGBgDQoNCiMjIyBTYXZpbmcgIA0KVXNlIGBgaGVyZSgpYGAgdG8gc2F2ZSBwbG90cyBpbiB0aGUgY3VycmVudCBkaXJlY3RvcnkuIFRoaXMgZnVuY3Rpb24gY2FuIGFsc28gYmUgdXNlZCB0byByZWZlcmVuY2UgZm9sZGVycyB3aXRoaW4gdGhlIGN1cnJlbnQgZGlyZWN0b3J5LiBGb3IgdGhpcyBjbGFzcywgdXNlIGBgLnN2Z2BgIHRvIHNhdmUgaW4gdmVjdG9yIGZvcm1hdCBhbmQgZW1iZWQgaW4gQWRvYmUgSWxsdXN0cmF0b3IuIFRoZSBmdW5jdGlvbiB0byBzYXZlIGEgcGxvdCBpcyBgYGdnc2F2ZSgpYGAgd2hpY2ggd2lsbCBhdXRvbWF0aWNhbGx5IHNhdmUgdGhlIGxhc3QgcGxvdCBhbmQgY2FuIGFsc28gYmUgcHJvdmlkZWQgYSBgYGdncGxvdGBgIG9iamVjdCB0byBzYXZlLiANCg0KIyMjIFdoZXJlIHRvIEdvIE5leHQgIA0KUGljayBhdCBsZWFzdCB0d28gb2YgdGhlIHF1ZXN0aW9ucyBwcmVzZW50ZWQgdW5kZXIgdGhlICpXaGVyZSB0byBHbyBOZXh0KiBzZWN0aW9uIGFuZCBhbnN3ZXIgdGhlbS4NCg0KIyMgU2hvdyB0aGUgUmlnaHQgTnVtYmVycyAgDQoNCioqIkNvZGUgYWxtb3N0IG5ldmVyIHdvcmtzIHByb3Blcmx5IHRoZSBmaXJzdCB0aW1lIHlvdSB3cml0ZSBpdC4iKiogKHAuIDczKSAgDQoNCiMjIyBHcm91cGluZyAgDQoNCmBgYHtyIGdyb3VwLXBsb3R9DQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSB5ZWFyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZ2RwUGVyY2FwKSkgDQoNCnAgKyBnZW9tX2xpbmUoKSAjIFNvbWV0aGluZyBpcyB3cm9uZywgd2UgZGlkbid0IHRlbGwgaXQgaG93IHRvIGdyb3VwDQoNCnAgKyBnZW9tX2xpbmUoYWVzKGdyb3VwID0gY291bnRyeSkpICMgTm93IHRoZXJlIGlzIGEgbGluZSBwZXIgY291bnRyeQ0KDQpgYGANCg0KDQojIyMgRmFjZXRpbmcgIA0KDQpGYWNldCA9IHNtYWxsIG11bHRpcGxlIChpLmUuIGEgc2VwYXJhdGUgZ3JhcGggZm9yIGVhY2ggdmFsdWUgb2YgdGhlIHZhcmlhYmxlKSAgDQoNCmBgYHtyIGZhY2V0LXBsb3RzfQ0KDQpwIDwtIGdncGxvdChkYXRhID0gZ2FwbWluZGVyLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0geWVhciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGdkcFBlcmNhcCkpDQoNCnAgKyBnZW9tX2xpbmUoYWVzKGdyb3VwID0gY291bnRyeSkpICsNCiAgZmFjZXRfd3JhcCh+Y29udGluZW50KSAjIG1ha2UgYSBzZXBhcmF0ZSBwbG90IGZvciBlYWNoIGNvbnRpbmVudA0KDQojIE1ha2UgaXQgbG9vayBhIGxpdHRsZSBuaWNlcg0KcCArIGdlb21fbGluZShjb2xvciA9ICJncmF5NzAiLA0KICAgICAgICAgICAgICBhZXMoZ3JvdXAgPSBjb3VudHJ5KSkgKw0KICBnZW9tX3Ntb290aChzaXplID0gMS4xLCBtZXRob2QgPSAibG9lc3MiLCBzZSA9IEZBTFNFKSArDQogIHNjYWxlX3lfbG9nMTAobGFiZWxzID0gc2NhbGVzOjpkb2xsYXIpICsNCiAgZmFjZXRfd3JhcCh+Y29udGluZW50LCBuY29sID0gNSkgKw0KICBsYWJzKHggPSAiWWVhciIsDQogICAgICAgeSA9ICJHRFAgcGVyIGNhcGl0YSIsDQogICAgICAgdGl0bGUgPSAiR0RQIHBlciBjYXBpdGEgb24gRml2ZSBDb250aW5lbnRzIikNCg0KIyBOZXcgZGF0YXNldCAyMDE2IEdlbmVyYWwgU29jaWFsIFN1cnZleSB3aXRoIG1vcmUgY2F0ZWdvcmljYWwgZGF0YQ0KZ2xpbXBzZShnc3Nfc20pDQoNCiMgUHJhY3RpY2UgdXNpbmcgZmFjZXRfZ3JpZCgpIHRvIGZhY2V0IGJldHdlZW4gbXVsdGlwbGUgdmFyaWFibGVzDQpwIDwtIGdncGxvdChkYXRhID0gZ3NzX3NtLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gYWdlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gY2hpbGRzKSkNCg0KcCArIGdlb21fcG9pbnQoYWxwaGEgPSAwLjIpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGZhY2V0X2dyaWQoc2V4IH4gcmFjZSkNCg0KYGBgDQoNCg0KIyMjIFRyYW5zZm9ybWluZyAgDQoNCkVhY2ggYGBnZW9tX2BgIGZ1bmN0aW9uIGhhcyBhbiBhc3NvY2lhdGVkIGBgc3RhdF9gYCBmdW5jdGlvbiB0aGF0IGlzIHVzZWQgdG8gcGxvdCB0aGUgZGF0YS4gU29tZXRpbWVzIHRoaXMgaW52b2x2ZXMgdHJhbnNmb3JtaW5nIHRoZSBkYXRhIGluIHNvbWUgd2F5LiANCg0KYGBge3IgdHJhbnNmb3JtLXBsb3R9DQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBnc3Nfc20sDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBiaWdyZWdpb24pKQ0KDQpwICsgZ2VvbV9iYXIoKSAjIG1ha2VzIGEgYmFyIGdyYXBoIHRoYXQgY291bnRzIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIHBlciByZWdpb247IGNvdW50IGlzIGNvbXB1dGVkIGZvciB1cw0KDQpwICsgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh5ID0gLi5wcm9wLi4pKSAjIHRoZSBwcm9wIHN0YXRpc3RpYyBjYW4gc2hvdyB1cyBwcm9wb3J0aW9ucw0KDQojIEJ1dCB0aGlzIGlzIG5vdCByaWdodCwgZWFjaCBzaG93cyAxMDAlDQoNCiMgU28sIHdlIG5lZWQgdG8gZml4IHRoZSBhdXRvbWF0aWMgZ3JvdXBpbmcgdGhhdCBpcyBvY2N1cnJpbmcgYnkgcmVnaW9uDQpwICsgZ2VvbV9iYXIobWFwcGluZyA9IGFlcyh5ID0gLi5wcm9wLi4sIGdyb3VwID0gMSkpICMgdXNpbmcgZ3JvdXAgPSAxIGlzIGJhc2ljYWxseSBhIHBsYWNlaG9sZGVyIHRoYXQgc2F5cyBhbGwgdGhlIGRhdGEgaXMgaW4gdGhlIHNhbWUgZ3JvdXANCg0KIyBMb29rIGF0IGEgZGlmZmVyZW50IHZhcmlhYmxlDQp0YWJsZShnc3Nfc20kcmVsaWdpb24pDQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBnc3Nfc20sDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSByZWxpZ2lvbiwgY29sb3IgPSByZWxpZ2lvbikpDQoNCnAgKyBnZW9tX2JhcigpICMgb25seSB0aGUgb3V0bGluZSBoYXMgYSBjb2xvciAtIHdlIG5lZWQgdG8gdXNlIGZpbGwNCg0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGdzc19zbSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHJlbGlnaW9uLCBmaWxsID0gcmVsaWdpb24pKQ0KDQpwICsgZ2VvbV9iYXIoKQ0KDQojIFJlbW92ZSB0aGUgbGVnZW5kDQpwICsgZ2VvbV9iYXIoKSArDQogIGd1aWRlcyhmaWxsID0gRkFMU0UpDQoNCmBgYA0KYGBge3IgZnJlcXVlbmN5LXBsb3RzfQ0KDQojIEhvdyBjYW4gd2UgbG9vayBhdCB0d28gdmFyaWFibGVzIHRvZ2V0aGVyDQpwIDwtIGdncGxvdChkYXRhID0gZ3NzX3NtLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gYmlncmVnaW9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gcmVsaWdpb24pKQ0KDQpwICsgZ2VvbV9iYXIoKSAjIFN0YWNrZWQgYmFyIGNoYXJ0IG9mIGNvdW50cw0KDQpwICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICMgU3RhY2tlZCBiYXIgY2hhcnQgb2YgcHJvcG9ydGlvbnMNCg0KcCArIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIikgIyBCYXIgY2hhcnQgb2YgY291bnRzIHNpZGUgYnkgc2lkZQ0KDQpwICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLA0KICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeSA9IC4ucHJvcC4uKSkgIyBCYXIgY2hhcnQgb2YgcHJvcG9ydGlvbnMgc2lkZSBieSBzaWRlDQoNCiMgTm90IHF1aXRlIHJpZ2h0IC0gYWxsIGFyZSAxMDAlIA0KDQpwICsgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLA0KICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeSA9IC4ucHJvcC4uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSByZWxpZ2lvbikpICMgQmFyIGNoYXJ0IG9mIHByb3BvcnRpb25zIHNpZGUgYnkgc2lkZQ0KDQojIFRoZSBwcm9wb3J0aW9ucyBzdW0gdG8gMSBieSByZWxpZ2lvbiBhY3Jvc3MgdGhlIHJlZ2lvbnMNCg0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGdzc19zbSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHJlbGlnaW9uKSkNCg0KcCArIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwNCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHkgPSAuLnByb3AuLiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gYmlncmVnaW9uKSkgKw0KICBmYWNldF93cmFwKH5iaWdyZWdpb24sIG5jb2wgPSAyKQ0KDQojIE5vdyB0aGUgcHJvcG9ydGlvbnMgc3VtIHRvIDEgYnkgcmVnaW9uIGFjcm9zcyByZWxpZ2lvbnMNCg0KDQpgYGANCg0KIyMjIEhpc3RvZ3JhbXMgYW5kIERlbnNpdHkgUGxvdHMNCg0KSGlzdG9ncmFtcyBjcmVhdGUgYmlucyBvZiBudW1lcmljYWwgZGF0YSBhbmQgZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBkYXRhIHdpdGhpbiB0aG9zZSBiaW5zLiAgDQoNCmBgYHtyIGhpc3RvZ3JhbS1kZW5zaXR5LXBsb3RzfQ0KDQojIEEgbmV3IGRhdGFzZXQNCmdsaW1wc2UobWlkd2VzdCkNCg0KIyBTaG93IGRpc3RyaWJ1dGlvbiBvZiB0aGUgc2l6ZSBvZiB0aGUgY291bnRpZXMgaW4gdGhlIE1pZHdlc3QNCnAgPC0gZ2dwbG90KGRhdGEgPSBtaWR3ZXN0LA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gYXJlYSkpDQoNCnAgKyBnZW9tX2hpc3RvZ3JhbSgpICMgY291bnQgaXMgY29tcHV0ZWQgYXV0b21hdGljYWxseSBieSB0aGUgZGVmYXVsdCBzdGF0IGZ1bmN0aW9uDQoNCnAgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTApICMgc2V0IDEwIGJpbnMNCg0KIyBMb29rIGF0IG9ubHkgdHdvIHN0YXRlcw0Kb2hfd2kgPC0gYygiT0giLCAiV0kiKQ0KDQpwIDwtIGdncGxvdChkYXRhID0gc3Vic2V0KG1pZHdlc3QsIHN1YnNldCA9IHN0YXRlICVpbiUgb2hfd2kpLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcGVyY29sbGVnZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHN0YXRlKSkNCg0KcCArIGdlb21faGlzdG9ncmFtKGFscGhhID0gMC40LCBiaW5zID0gMjApICMgT3ZlcmxhcHBpbmcgaGlzdG9ncmFtcw0KDQojIERlbnNpdHkgZXN0aW1hdGUgb2YgdGhlIHVuZGVybHlpbmcgZGlzdHJpYnV0aW9uIC0gZGVuc2l0eSBwbG90DQpwIDwtIGdncGxvdChkYXRhID0gbWlkd2VzdCwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGFyZWEpKQ0KDQpwICsgZ2VvbV9kZW5zaXR5KCkNCg0KIyBEZW5zaXR5IGJ5IHN0YXRlDQpwIDwtIGdncGxvdChkYXRhID0gbWlkd2VzdCwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGFyZWEsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBzdGF0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBzdGF0ZSkpDQoNCnAgKyBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpDQoNCiMgQ29tcGFyZSB0byBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IikNCg0KcCArIGdlb21fbGluZShzdGF0ID0gImRlbnNpdHkiKQ0KDQojIFNjYWxlZCBkZW5zaXR5DQpwIDwtIGdncGxvdChkYXRhID0gc3Vic2V0KG1pZHdlc3QsIHN1YnNldCA9IHN0YXRlICVpbiUgb2hfd2kpLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcGVyY29sbGVnZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHN0YXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHN0YXRlKSkNCg0KcCArIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMywNCiAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh5ID0gLi5zY2FsZWQuLikpDQoNCmBgYA0KIyMjIEF2b2lkaW5nIFRyYW5zZm9ybWF0aW9uIFdoZW4gTmVjZXNzYXJ5DQoNCkF2b2lkaW5nIHRyYW5zZm9ybWF0aW9ucyAtIHNvbWV0aW1lcyB0aGUgZGF0YSBpcyBhbHJlYWR5IGFnZ3JlZ2F0ZWQgb3Igc3VtbWFyaXplZCBhbmQgd2UgZG8gbm90IG5lZWQgYSB0cmFuc2Zvcm1hdGlvbi4NCg0KYGBge3Igbm8tdHJhbnNmb3JtLXBsb3RzfQ0KDQp0aXRhbmljICMgdGhpcyBkYXRhIGlzIGFscmVhZHkgc3VtbWFyaXplZA0KDQpwIDwtIGdncGxvdChkYXRhID0gdGl0YW5pYywNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGZhdGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBwZXJjZW50LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gc2V4KSkNCg0KcCArIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwNCiAgICAgICAgICAgICBzdGF0ID0gImlkZW50aXR5IikgKyAjIHBsb3QgdmFsdWVzIGFzIHByb3ZpZGVkLCBkbyBub3Qgc3VtbWFyaXplL2NvdW50L2V0Yy4NCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICMgdGhpcyBwdXRzIHRoZSBsZWdlbmQgYXQgdGhlIHRvcCBvZiB0aGUgZ3JhcGgNCg0Kb2VjZF9zdW0gIyBhbm90aGVyIGRhdGFzZXQgdGhhdCBpcyBhbHJlYWR5IHN1bW1hcml6ZWQNCg0KcCA8LSBnZ3Bsb3QoZGF0YSA9IG9lY2Rfc3VtLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0geWVhciwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBkaWZmLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gaGlfbG8pKQ0KDQpwICsgZ2VvbV9jb2woKSArICMgdGhpcyBpcyB0aGUgc2FtZSBhcyBnZW9tX2JhciB3aXRoIHN0YXQgPSAiaWRlbnRpdHkiDQogIGd1aWRlcyhmaWxsID0gRkFMU0UpICsgIyBubyBsZWdlbmQNCiAgbGFicyh4ID0gTlVMTCwgIyBubyB4LWF4aXMgbGFiZWwNCiAgICAgICB5ID0gIkRpZmZlcmVuY2UgaW4gWWVhcnMiLA0KICAgICAgIHRpdGxlID0gIlRoZSBVUyBMaWZlIEV4cGVjdGFuY3kgR2FwIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJEaWZmZXJlbmNlIGJldHdlZW4gVVMgYW5kIE9FQ0QgYXZlcmFnZSBsaWZlIGV4cGVjdGFuY2llcywgMTk2MC0yMDE1IiwNCiAgICAgICBjYXB0aW9uID0gIkRhdGE6IE9FQ0QuIEFmdGVyIGEgY2hhcnQgYnkgQ2hyaXN0b3BoZXIgSW5ncmFoYW0sIFdhc2hpbmd0b24gUG9zdCwgRGVjZW1iZXIgMjd0aCAyMDE3IikNCg0KYGBgDQoNCiMjIyBXaGVyZSB0byBHbyBOZXh0ICANClBpY2sgYXQgbGVhc3QgdHdvIG9mIHRoZSBxdWVzdGlvbnMgcHJlc2VudGVkIHVuZGVyIHRoZSAqV2hlcmUgdG8gR28gTmV4dCogc2VjdGlvbiBhbmQgYW5zd2VyIHRoZW0uDQoNCg0KIyMgR3JhcGggVGFibGVzLCBNYWtlIExhYmVscywgQWRkIE5vdGVzDQoNCiMjIyBTdW1tYXJpemluZyBEYXRhDQoNCkl0IGlzIGJlc3QgcHJhY3RpY2UgdG8gY2FsY3VsYXRlIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZmlyc3QgYW5kIHRoZW4gcGxvdCB0aGVtLCByYXRoZXIgdGhhbiB1c2luZyB0aGUgYHN0YXRfYCBmdW5jdGlvbnMgd2l0aGluIGBnZW9tX2AgZnVuY3Rpb25zLiBUaGlzIGlzIGJlY2F1c2UgaXQgbWFrZXMgdGhlIGNvZGUgZWFzaWVyIHRvIHVuZGVyc3RhbmQgYW5kIHJlYWQgYW5kIGFsbG93cyB1cyB0byBkb3VibGUgY2hlY2sgdGhlIGRhdGEgYW5kIGFnZ3JlZ2F0aW9ucyBtb3JlIGVhc2lseS4NCg0KVGhlIHBpcGUgb3BlcmF0b3IgYCU+JWAgZnJvbSBgZHBseXJgIGFsbG93cyB1cyB0byBwYXNzIGRhdGEgZnJvbSBvbmUgb3BlcmF0aW9uIG9yIGZ1bmN0aW9uIHRvIGFub3RoZXIuIFVzdWFsbHkgdGhlcmUgaXMgYSBzZXF1ZW5jZSBvZiBzdGVwczogZ3JvdXAsIGZpbHRlci9zZWxlY3QsIG11dGF0ZSwgdGhlbiBzdW1tYXJpemUuIA0KDQpXaXRoaW4gYGdyb3VwX2J5KClgLCBncm91cGluZyBsZXZlbHMgKGxlZnQgdG8gcmlnaHQpIGdvIGZyb20gb3V0ZXJtb3N0IHRvIGlubmVybW9zdC4gRnVuY3Rpb25zIHVzZWQgdG8gY3JlYXRlIG5ldyB2YXJpYWJsZXMgKHN1Y2ggYXMgYHN1bW1hcml6ZSgpYCkgd2lsbCBiZSBhcHBsaWVkIHRvIHRoZSBpbm5lcm1vc3QgZ3JvdXAgbGV2ZWwgZmlyc3QuIA0KDQpgYGB7ciBzdW1tYXJpemV9DQoNCiMgQ3JlYXRlIGEgdGliYmxlL2RhdGF0IHRhYmxlIHdpdGggdGhlIHBlcmNlbnQgb2YgZWFjaCByZWxpZ2lvbiBieSByZWdpb24NCnJlbF9ieV9yZWdpb24gPC0gZ3NzX3NtICU+JQ0KICBncm91cF9ieShiaWdyZWdpb24sIHJlbGlnaW9uKSAlPiUNCiAgc3VtbWFyaXplKE4gPSBuKCkpICU+JQ0KICBtdXRhdGUoZnJlcSA9IE4gLyBzdW0oTiksDQogICAgICAgICBwY3QgPSByb3VuZCgoZnJlcSAqIDEwMCksIDApKQ0KDQojIENoZWNrIHRoZSBwZXJjZW50YWdlcyBzdW0gdG8gMTAwIGJ5IHJlZ2lvbg0KcmVsX2J5X3JlZ2lvbiAlPiUgDQogIGdyb3VwX2J5KGJpZ3JlZ2lvbikgJT4lDQogIHN1bW1hcmlzZSh0b3RhbCA9IHN1bShwY3QpKQ0KDQojIE1ha2UgYSBwbG90IChub3RlOiBIZWFseSBzdG9wcyB1c2luZyB0aGUgYXJndW1lbnQgbmFtZSkNCnAgPC0gZ2dwbG90KGRhdGEgPSByZWxfYnlfcmVnaW9uLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gYmlncmVnaW9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcGN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gcmVsaWdpb24pKQ0KDQpwICsgDQogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlMiIpICsNCiAgbGFicyh4ID0gIlJlZ2lvbiIsDQogICAgICAgeSA9ICJQZXJjZW50IiwNCiAgICAgICBmaWxsID0gIlJlaWxpZ2lvbiIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQoNCiMgTGV0J3MgcmVhcnJhbmdlIGl0IGEgbGl0dGxlDQpwIDwtIGdncGxvdChkYXRhID0gcmVsX2J5X3JlZ2lvbiwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHJlbGlnaW9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcGN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gcmVsaWdpb24pKQ0KDQpwICsgDQogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlMiIpICsNCiAgbGFicyh4ID0gTlVMTCwgIyBkb24ndCBwdXQgYSBsYWJlbCBvbiB0aGUgYXhpcw0KICAgICAgIHkgPSAiUGVyY2VudCIsDQogICAgICAgZmlsbCA9ICJSZWxpZ2lvbiIpICsNCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKw0KICBjb29yZF9mbGlwKCkgKyAjIHN3aXRjaGVzIHRoZSB4IGFuZCB5IGF4ZXMgYWZ0ZXIgdGhlIHBsb3QgaXMgbWFkZQ0KICBmYWNldF9ncmlkKH4gYmlncmVnaW9uKQ0KDQpgYGANCg0KIyMjIENvbnRpbnVvdXMgVmFyaWFibGVzIGJ5IEdyb3VwIG9yIENhdGVnb3J5DQoNCkluIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBsZWFybiBob3cgdG8gdXNlIGBnZW9tX2JveHBsb3QoKWANCg0KYGBge3IgYm94LXBsb3RzfQ0KDQojIE5ldyBEYXRhc2V0IG9uIE9yZ2FuIERvbmF0aW9ucyBieSBjb3VudHJ5IGFuZCB5ZWFyDQpvcmdhbmRhdGEgJT4lIHNlbGVjdCgxOjYpICU+JSBzYW1wbGVfbihzaXplID0gMTApDQoNCiMgR3JhcGggc29tZSBvZiB0aGUgb3JnYW4gZGF0YSB3aXRob3V0IHJlYWxseSBsb29raW5nIGF0IHRoZSBkYXRhc2V0DQpwIDwtIGdncGxvdChkYXRhID0gb3JnYW5kYXRhLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0geWVhciwgeSA9IGRvbm9ycykpDQoNCnAgKyBnZW9tX3BvaW50KCkgIyBnZXQgYSB3YXJuaW5nIGFib3V0IG1pc3NpbmcgZGF0YTsgdGhpcyBncmFwaCBkb2Vzbid0IG1ha2UgbXVjaCBzZW5zZQ0KDQojIFBsb3QgdGhlIG9yZ2FuIGRvbmF0aW9ucyBieSBjb3VudHJ5IG92ZXIgdGltZQ0KcCA8LSBnZ3Bsb3QoZGF0YSA9IG9yZ2FuZGF0YSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHllYXIsIHkgPSBkb25vcnMpKQ0KDQpwICsgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoZ3JvdXAgPSBjb3VudHJ5KSkgKw0KICBmYWNldF93cmFwKH5jb3VudHJ5KSAjIGF1dG9tYXRpY2FsbHkgb3JkZXJzIGNvdW50cmllcyBhbHBoYWJldGljYWxseQ0KDQojIE1ha2UgYm94cGxvdHMgYnkgY291bnRyeSAodXNpbmcgdGhlIGRhdGEgb3ZlciB0aGUgeWVhcnMpDQpwIDwtIGdncGxvdChkYXRhID0gb3JnYW5kYXRhLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gY291bnRyeSwgeSA9IGRvbm9ycykpDQoNCnAgKyBnZW9tX2JveHBsb3QoKSArDQogIGNvb3JkX2ZsaXAoKSAjIG1vdmUgY291bnRyeSBuYW1lcyB0byB0aGUgeS1heGlzDQoNCiMgTGV0J3MgcmVvcmRlciB0aGUgYm94cGxvdHMgYnkgbWVhbiBkb25hdGlvbiByYXRlIHVzaW5nIHRoZSByZW9yZGVyIGZ1bmN0aW9uDQpwIDwtIGdncGxvdChkYXRhID0gb3JnYW5kYXRhLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcmVvcmRlcihjb3VudHJ5LCBkb25vcnMsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBkb25vcnMpKQ0KDQpwICsgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHggPSBOVUxMKSArICMgbm8geC1heGlzIHRpdGxlDQogIGNvb3JkX2ZsaXAoKQ0KDQojIEFkZCBjb2xvciB0byB0aGUgYm94cGxvdHMNCnAgPC0gZ2dwbG90KGRhdGEgPSBvcmdhbmRhdGEsDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGRvbm9ycywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGRvbm9ycywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHdvcmxkKSkNCg0KcCArIGdlb21fYm94cGxvdCgpICsNCiAgbGFicyh4ID0gTlVMTCkgKyAjIG5vIHgtYXhpcyB0aXRsZQ0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCg0KIyBMZXQncyBsb29rIGF0IHRoaXMgZGF0YSBpbiBwb2ludCBmb3JtYXQNCnAgPC0gZ2dwbG90KGRhdGEgPSBvcmdhbmRhdGEsDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGRvbm9ycywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGRvbm9ycywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB3b3JsZCkpDQoNCnAgKyBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHggPSBOVUxMKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KDQojIFBvaW50cyBhcmUgb24gdG9wIG9mIGVhY2ggb3RoZXIsIHNvIHVzZSBnZW9tX2ppdHRlcigpIHRvIG1vdmUgdGhlbSBhcm91bmQgYSBsaXR0bGUNCnAgKyBnZW9tX2ppdHRlcigpICsNCiAgbGFicyh4ID0gTlVMTCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCg0KIyBSZWR1Y2UgdGhlIGFtb3VudCBvZiBzcHJlYWQgaW4gdGhlIHBvaW50cyB1c2luZyBwb3NpdGlvbl9qaXR0ZXIoKQ0KcCArIGdlb21faml0dGVyKHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xNSkpICsNCiAgbGFicyh4ID0gTlVMTCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCg0KDQojIEdldCBpbmZvcm1hdGlvbiBhYm91dCBjb25zZW50IGxhd3MgYnkgY291bnRyeQ0KYnlfY291bnRyeSA8LSBvcmdhbmRhdGEgJT4lDQogIGdyb3VwX2J5KGNvbnNlbnRfbGF3LCBjb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXplKGRvbm9yc19tZWFuID0gbWVhbihkb25vcnMsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBkb25vcnNfc2QgPSBzZChkb25vcnMsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICBnZHBfbWVhbiA9IG1lYW4oZ2RwLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgaGVhbHRoX21lYW4gPSBtZWFuKGhlYWx0aCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIHJvYWRzX21lYW4gPSBtZWFuKHJvYWRzLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgY2VyZWJ2YXNfbWVhbiA9IG1lYW4oY2VyZWJ2YXMsIG5hLnJtID0gVFJVRSkpDQoNCmJ5X2NvdW50cnkNCg0KIyBBbm90aGVyIHdheSB0byBkbyB0aGlzIGluIGEgc2hvcnRlciBzdGVwDQpieV9jb3VudHJ5IDwtIG9yZ2FuZGF0YSAlPiUNCiAgZ3JvdXBfYnkoY29uc2VudF9sYXcsIGNvdW50cnkpICU+JQ0KICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgDQogICAgICAgICAgICAgICBsaXN0KG1lYW4gPSBtZWFuLCBzZCA9IHNkKSwgIyBub3RlIGZ1bnMgaXMgZGVwcmVjYXRlZA0KICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSAlPiUgDQogIHVuZ3JvdXAoKQ0KDQoNCiMgTWFrZSBhIHNpbXBsZSBwbG90IG9mIG91ciBzdW1tYXJpemVkIGRhdGEgKENsZWF2ZWxhbmQgRG90IFBsb3QpDQpwIDwtIGdncGxvdChkYXRhID0gYnlfY291bnRyeSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGRvbm9yc19tZWFuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcmVvcmRlcihjb3VudHJ5LCBkb25vcnNfbWVhbiksICMgdGhpcyBwdXRzIHRoZSBjb3VudHJpZXMgaW4gb3JkZXIgYnkgZG9ub3JzX21lYW4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb25zZW50X2xhdykpDQoNCnAgKyANCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICBsYWJzKHggPSAiRG9ub3IgUHJvY3VyZW1lbnQgUmF0ZSIsDQogICAgICAgeSA9ICIiLCAjIGFub3RoZXIgd2F5IG9mIHB1dHRpbmcgbm8gbGFiZWwgb24gYW4gYXhpcw0KICAgICAgIGNvbG9yID0gIkNvbnNlbnQgTGF3IikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCg0KIyBGYWNldCBpbnRvIHR3byBwYW5lbHMgZm9yIHRoZSBDbGVhdmVsYW5kIERvdCBQbG90DQpwIDwtIGdncGxvdChkYXRhID0gYnlfY291bnRyeSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGRvbm9yc19tZWFuLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcmVvcmRlcihjb3VudHJ5LCBkb25vcnNfbWVhbikpKQ0KDQpwICsgDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgZmFjZXRfd3JhcCh+IGNvbnNlbnRfbGF3LA0KICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiLCAjIGFsbG93IHRoZSB5LWF4aXMgbGFiZWxzIHRvIGJlIGRpZmZlcmVudCBvbiBlYWNoIGZhY2V0DQogICAgICAgICAgICAgbmNvbCA9IDEpICMgb3JpZW50IHBsb3QgaW4gYSBzaW5nbGUgY29sdW1ucw0KICBsYWJzKHggPSAiRG9ub3IgUHJvY3VyZW1lbnQgUmF0ZSIsDQogICAgICAgeSA9ICIiKSANCg0KIyBQbG90IHRoZSBkb3RzICh3aGljaCByZXByZXNlbnQgdGhlIG1lYW4pIHdpdGggdGhlIHJhbmdlIG9mIHN0YW5kYXJkIGRldmlhdGlvbiB1c2luZyBnZW9tX3BvaW50cmFuZ2UoKQ0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGJ5X2NvdW50cnksDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGNvdW50cnksIGRvbm9yc19tZWFuKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGRvbm9yc19tZWFuKSkNCg0KcCArIA0KICBnZW9tX3BvaW50cmFuZ2UobWFwcGluZyA9IGFlcyh5bWluID0gZG9ub3JzX21lYW4gLSBkb25vcnNfc2QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHltYXggPSBkb25vcnNfbWVhbiArIGRvbm9yc19zZCkpICsNCiAgbGFicyh4ID0gIiIsDQogICAgICAgeSA9ICJEb25vciBQcm9jdXJlbWVudCBSYXRlIikgKw0KICBjb29yZF9mbGlwKCkgDQojIG5lZWQgdG8gdXNlIGNvb3JkX2ZsaXAoKSBiZWNhdXNlIGdlb21fcG9pbnRyYW5nZSgpIHVzZXMgeSwgeW1pbiwgYW5kIHltYXggYW5kIHdlIHdhbnQgdG8gc2hvdyB0aGlzIG9uIHRoZSB4LWF4aXMNCg0KICANCmBgYA0KDQojIyMgUGxvdCBUZXh0IERpcmVjdGx5DQoNCmBnZW9tX3RleHQoKWAgaXMgdXNlZCB0byBwbG90IGxhYmVscyBvbiBhIGdyYXBoLiBUaGUgYXJndW1lbnQgYGhqdXN0YCBjYW4gYmUgdXNlZCB0byBsZWZ0IG9yIHJpZ2h0IGp1c3RpZnkgdGhlIHRleHQuIGBoanVzdCA9IDBgIHdpbGwgbGVmdC1qdXN0aWZ5OyBgaGp1c3QgPSAxYCB3aWxsIHJpZ2h0LWp1c3RpZnkuICANCg0KYGBge3IgcGxvdC10ZXh0fQ0KDQpwIDwtIGdncGxvdChkYXRhID0gYnlfY291bnRyeSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHJvYWRzX21lYW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBkb25vcnNfbWVhbikpIA0KDQpwICsgDQogIGdlb21fcG9pbnQoKSArICMgcGxvdCBwb2ludHMNCiAgZ2VvbV90ZXh0KG1hcHBpbmcgPSBhZXMobGFiZWwgPSBjb3VudHJ5KSkgIyBwbG90IHRoZSBsYWJlbHMNCg0KIyBUZXh0IGlzIHJpZ2h0IG9uIHRvcCBvZiB0aGUgcG9pbnRzLCB1c2UgaGp1c3QgdG8gbW92ZSBpdA0KcCArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3RleHQobWFwcGluZyA9IGFlcyhsYWJlbCA9IGNvdW50cnkpLA0KICAgICAgICAgICAgaGp1c3QgPSAwKQ0KDQoNCmBgYA0KVGhlIGBnZ3JlcGVwbGAgcGFja2FnZSBwcm92aWRlcyB0d28gZ2VvbXMgdGhhdCBhcmUgbW9yZSBmbGV4aWJsZSBmb3IgcGxvdHRpbmcgbGFiZWxzLg0KDQpgYGB7ciBnZ3JlcGVsfQ0KDQpsaWJyYXJ5KGdncmVwZWwpDQoNCiMgU3dpdGNoIGRhdGFzZXRzDQplbGVjdGlvbnNfaGlzdG9yaWMgJT4lIHNlbGVjdCgyOjcpDQoNCiMgU2V0IHRpdGxlcyBhbmQgbGFiZWxzDQpwX3RpdGxlIDwtICJQcmVzaWRlbnRpYWwgRWxlY3Rpb25zOiBQb3B1bGFyICYgRWxlY3RvcmFsIENvbGxlZ2UgTWFyZ2lucyINCnBfc3VidGl0bGUgPC0gIjE4MjQtMjAxNiINCnBfY2FwdGlvbiA8LSAiRGF0YSBmb3IgMjAxNiBhcmUgcHJvdmlzaW9uYWwiDQp4X2xhYmVsIDwtICJXaW5uZXIncyBzaGFyZSBvZiBQb3B1bGFyIFZvdGUiDQp5X2xhYmVsIDwtICJXaW5uZXIncyBzaGFyZSBvZiBFbGVjdG9yYWwgQ29sbGVnZSBWb3RlcyINCg0KcCA8LSBnZ3Bsb3QoZGF0YSA9IGVsZWN0aW9uc19oaXN0b3JpYywNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHBvcHVsYXJfcGN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZWNfcGN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHdpbm5lcl9sYWJlbCkpDQoNCnAgKyANCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMC41LA0KICAgICAgICAgICAgIHNpemUgPSAxLjQsDQogICAgICAgICAgICAgY29sb3IgPSAiZ3JheTgwIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAwLjUsDQogICAgICAgICAgICAgc2l6ZSA9IDEuNCwNCiAgICAgICAgICAgICBjb2xvciA9ICJncmF5ODAiKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fdGV4dF9yZXBlbCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIGxhYnMoeCA9IHhfbGFiZWwsDQogICAgICAgeSA9IHlfbGFiZWwsDQogICAgICAgdGl0bGUgPSBwX3RpdGxlLA0KICAgICAgIHN1YnRpdGxlID0gcF9zdWJ0aXRsZSwNCiAgICAgICBjYXB0aW9uID0gcF9jYXB0aW9uKQ0KDQoNCmBgYA0KDQojIyMgTGFiZWwgT3V0bGllcnMNCg0KVG8gbGFiZWwgc3BlY2lmaWMgcG9pbnRzLCB3ZSBuZWVkIHRvIHRlbGwgdGhlIGdlb20gd2hpY2ggcG9pbnRzIHRvIGxhYmVsIHVzaW5nIHRoZSBgc3Vic2V0KClgIGZ1bmN0aW9uIHJhdGhlciB0aGFuIGdpdmluZyB0aGUgZ2VvbSB0aGUgZW50aXJlIGRhdGFzZXQuDQoNCmBgYHtyIGxhYmVsLW91dGxpZXJzLTF9DQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBieV9jb3VudHJ5LA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gZ2RwX21lYW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBoZWFsdGhfbWVhbikpDQoNCnAgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBPbmx5IGxhYmVsIHBvaW50cyB3aXRoIG1lYW4gR0RQIGdyZWF0ZXIgdGhhbiAyNSwwMDANCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQoYnlfY291bnRyeSwgZ2RwX21lYW4gPiAyNTAwMCksDQogICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKGxhYmVsID0gY291bnRyeSkpIA0KDQpwICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgIyBPbmx5IGxhYmVsIHBvaW50cyB3aXRoIG1lYW4gR0RQIGdyZWF0ZXIgdGhhbiAyNSwwMDAgT1IgDQogICMgbWVhbiBoZWFsdGggbGVzcyB0aGFuIDEsNTAwIG9yIEJlbGdpdW0NCiAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQoYnlfY291bnRyeSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdkcF9tZWFuID4gMjUwMDAgfA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWx0aF9tZWFuIDwgMTUwMCB8DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnRyeSAlaW4lICJCZWxnaXVtIiksDQogICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKGxhYmVsID0gY291bnRyeSkpDQoNCg0KDQoNCg0KYGBgDQoNCkFuIGFsdGVybmF0aXZlIHRvIHVzaW5nIGBzdWJzZXQoKWAgdG8gZmlsdGVyIHRoZSBkYXRhIGlzIHRvIGFkZCBhIHZhcmlhYmxlIHRoYXQgYWxyZWFkeSBoYXMgdGhlIGNvbmRpdGlvbnMgZm9yIGxhYmVsaW5nIHRvIHRoZSBkYXRhc2V0Lg0KDQpgYGB7ciBsYWJlbC1vdXRsaWVycy0yfQ0KDQojIEFkZCBjb2RlL2luZGljYXRvciB2YXJpYWJsZSB0byBvcmdhbiBkYXRhDQpvcmdhbmRhdGEkaW5kIDwtIG9yZ2FuZGF0YSRjY29kZSAlaW4lIGMoIkl0YSIsICJTcGEiKSAmIG9yZ2FuZGF0YSR5ZWFyID4gMTk5OA0KDQpwIDwtIGdncGxvdChkYXRhID0gb3JnYW5kYXRhLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcm9hZHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBkb25vcnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gaW5kKSkNCg0KcCArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChvcmdhbmRhdGEsIGluZCksDQogICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKGxhYmVsID0gY2NvZGUpKSArDQogIGd1aWRlcyhsYWJlbCA9IEZBTFNFLA0KICAgICAgICAgY29sb3IgPSBGQUxTRSkgIyByZW1vdmVzIGxlZ2VuZA0KDQpgYGANCg0KDQojIyMgV3JpdGUgYW5kIERyYXcgaW4gdGhlIFBsb3QgQXJlYQ0KDQpgYW5ub3RhdGUoKWAgY2FuIGJlIHVzZWQgdG8gYWRkIGFubm90YXRpb25zIGZyb20gZGlmZmVyZW50IGdlb21zIHRvIHBsb3RzICh0ZXh0LCBzaGFkaW5nLCBldGMuKQ0KDQpgYGB7ciBhbm5vdGF0ZX0NCg0KcCA8LSBnZ3Bsb3QoZGF0YSA9IG9yZ2FuZGF0YSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHJvYWRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gZG9ub3JzKSkNCg0KcCArIA0KICBnZW9tX3BvaW50KCkgKw0KICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCAjIGEgdGV4dCBhbm5vdGF0aW9uDQogICAgICAgICAgIHggPSA5MSwgIyB4IHBvc2l0aW9uIGZvciB0aGUgYW5ub3RhdGlvbg0KICAgICAgICAgICB5ID0gMzMsICMgeSBwb3NpdGlvbiBmb3IgdGhlIGFubm90YXRpb24NCiAgICAgICAgICAgbGFiZWwgPSAiQSBzdXJwcmlzaW5nbHkgaGlnaCBcbiByZWNvdmVyeSByYXRlIiwgIyBsYWJlbCBmb3IgdGhlIGFubm90YXRpb24NCiAgICAgICAgICAgaGp1c3QgPSAwKSAjIGxlZnQtYWxpZ24NCg0KcCArIA0KICBnZW9tX3BvaW50KCkgKw0KICBhbm5vdGF0ZShnZW9tID0gInJlY3QiLCAjIGEgcmVjdGFuZ3VsYXIgYW5ub3RhdGlvbg0KICAgICAgICAgICB4bWluID0gMTI1LCB4bWF4ID0gMTU1LCAjIHggcG9zaXRpb24gZm9yIHRoZSBhbm5vdGF0aW9uDQogICAgICAgICAgIHltaW4gPSAzMCwgeW1heCA9IDM1LCAjIHkgcG9zaXRpb24gZm9yIHRoZSBhbm5vdGF0aW9uDQogICAgICAgICAgIGZpbGwgPSAicmVkIiwgYWxwaGEgPSAwLjIpICsgIyBjb2xvci9maWxsIHByb3BlcnRpZXMgZm9yIHRoZSBhbm5vdGF0aW9uDQogICAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgIyBhIHRleHQgYW5ub3RhdGlvbg0KICAgICAgICAgICB4ID0gOTEsICMgeCBwb3NpdGlvbiBmb3IgdGhlIGFubm90YXRpb24NCiAgICAgICAgICAgeSA9IDMzLCAjIHkgcG9zaXRpb24gZm9yIHRoZSBhbm5vdGF0aW9uDQogICAgICAgICAgIGxhYmVsID0gIkEgc3VycHJpc2luZ2x5IGhpZ2ggXG4gcmVjb3ZlcnkgcmF0ZSIsICMgbGFiZWwgZm9yIHRoZSBhbm5vdGF0aW9uDQogICAgICAgICAgIGhqdXN0ID0gMCkgDQoNCmBgYA0KDQoNCiMjIyBVbmRlcnN0YW5kaW5nIFNjYWxlcywgR3VpZGVzLCBhbmQgVGhlbWVzDQoNCmBzY2FsZV88TUFQUElORz5fPEtJTkQ+KClgIGZ1bmN0aW9ucyBjYW4gYmUgdXNlZCB0byBhZGp1c3QgdGhlIGF4ZXMgYW5kIGNvbG9ycyB1c2VkIGluIHBsb3RzLiBUaGUgYGd1aWRlKClgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGFkanVzdCB0aGUgbGVnZW5kLiBUaGUgYHRoZW1lKClgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIHRvIGFkanVzdCB0aGUgb3ZlcmFsbCBsb29rIG9mIGEgcGxvdC4NCg0KYGBge3Igc2NhbGVzfQ0KDQpwIDwtIGdncGxvdChkYXRhID0gb3JnYW5kYXRhLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcm9hZHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBkb25vcnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHdvcmxkKSkNCg0KIyBBZGp1c3QgdGhlIHggYW5kIHkgYXhlcw0KcCArIA0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV94X2xvZzEwKCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYyg1LCAxNSwgMjUpLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiRml2ZSIsICJGaWZ0ZWVuIiwgIlR3ZW50eSBGaXZlIikpDQoNCiMgQWRqdXN0IHRoZSBjb2xvciBsZWdlbmQNCnAgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobGFiZWxzID0gYygiQ29ycG9yYXRpc3QiLCAiTGliZXJhbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTb2NpYWwgRGVtb2NyYXQiLCAiVW5jbGFzc2lmaWVkIikpICsNCiAgbGFicyh4ID0gIlJvYWQgRGVhdGhzIiwNCiAgICAgICB5ID0gIkRvbm9yIFByb2N1cmVtZW50IiwNCiAgICAgICBjb2xvciA9ICJXZWxmYXJlIFN0YXRlIikNCg0KYGBgDQoNCg0KDQojIyMgV2hlcmUgdG8gR28gTmV4dCAgDQpQaWNrIGF0IGxlYXN0IHR3byBvZiB0aGUgcXVlc3Rpb25zIHByZXNlbnRlZCB1bmRlciB0aGUgKldoZXJlIHRvIEdvIE5leHQqIHNlY3Rpb24gYW5kIGFuc3dlciB0aGVtLg0KDQojIyBXb3JrIHdpdGggTW9kZWxzDQoNCmBgYHtyfQ0KDQpgYGANCg0KIyMgRHJhdyBNYXBzDQoNClRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCB3YXlzIHRvIHJlcHJlc2VudCBkYXRhIG9uIGEgbWFwOyB0aGUgZGVzaWduZXIgbmVlZHMgdG8gZGVjaWRlIGhvdyBmaW5lIHRoZSByZXNvbHV0aW9uIG9mIHRoZSByZXByZXNlbnRhdGlvbiBzaG91bGQgYmUsIGhvdyB0byBjb252ZXkgdGhlIHdlaWdodCBvZiBkaWZmZXJlbnQgZGF0YSBwb2ludHMsIGhvdyB0byBhY2N1cmF0ZWx5IHJlcHJlc2VudCBzcGF0aWFsIGRhdGEsIGFuZCB0aGUgbWFwIHR5cGUgdG8gdXNlLiANCg0KIyMjIE1hcCBVLlMuIFN0YXRlLUxldmVsIERhdGENCg0KYGBge3IgdXMtZWxlY3Rpb25zLTF9DQoNCiMgRWxlY3Rpb24gZGF0YSAtIHNlbGVjdCBjb2x1bW5zIG9mIGludGVyZXN0IGFuZCB2aWV3IGEgc2FtcGxlIG9mIDUgcm93cw0KZWxlY3Rpb24gJT4lIA0KICBzZWxlY3Qoc3RhdGUsIHRvdGFsX3ZvdGUsIHJfcG9pbnRzLCBwY3RfdHJ1bXAsIHBhcnR5LCBjZW5zdXMpICU+JQ0KICBzYW1wbGVfbig1KSANCg0KIyBGSVBTIGNvZGUgaXMgYSB1bmlxdWUgc2l4LWRpZ2l0IGlkZW50aWZpZXIgZm9yIGV2ZXJ5IFUuUy4gY291bnR5DQojIFRoZSBmaXJzdCB0d28gZGlnaXRzIG9mIGEgRklQUyBjb2RlIHJlcHJlc2VudCB0aGUgc3RhdGUNCg0KIyBTZXQgY29sb3JzIGZvciBEZW1vY3JhdGljIGFuZCBSZXB1YmxpY2FuIHBhcnRpZXMNCnBhcnR5X2NvbG9ycyA8LSBjKCIjMkU3NEMwIiwgIiNDQjQ1NEEiKQ0KDQojIENyZWF0ZSBhIHBsb3Qgb2YgdGhlIGVsZWN0aW9ucyBkYXRhIGluIGEgZmFjZXRlZCBkb3QgcGxvdA0KDQpwMCA8LSBnZ3Bsb3QoZGF0YSA9IHN1YnNldChlbGVjdGlvbiwgc3QgJW5pbiUgIkRDIiksDQogICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcl9wb2ludHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcmVvcmRlcihzdGF0ZSwgcl9wb2ludHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBwYXJ0eSkpDQoNCnAxIDwtIHAwICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgY29sb3IgPSAiZ3JheTMwIikgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKQ0KDQpwMiA8LSBwMSArIA0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcGFydHlfY29sb3JzKQ0KDQpwMyA8LSBwMiArIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygtMzAsIC0yMCwgLTEwLCAwLCAxMCwgMjAsIDMwLCA0MCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCIzMFxuKENsaW50b24pIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIwIiwgIjEwIiwgIjAiLCAiMTAiLCAiMjAiLCAiMzAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNDBcbihUcnVtcCkiKSkNCg0KcDMgKyANCiAgZmFjZXRfd3JhcCh+IGNlbnN1cywgbmNvbCA9IDEsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIGd1aWRlcyhjb2xvciA9IEZBTFNFKSArDQogIGxhYnMoeCA9ICJQb2ludCBNYXJnaW4iLA0KICAgICAgIHkgPSAiIikgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQ0KDQpgYGANCg0KVGhlIGBtYXBzYCBsaWJyYXJ5IHByb3ZpZGVzIHByZS1kcmF3biBtYXAgZGF0YS4NCg0KYGBge3IgdXMtbWFwLTF9DQoNCmxpYnJhcnkobWFwcykNCg0KIyBHZXQgZGF0YSBmb3IgVS5TLiBTdGF0ZXMNCnVzX3N0YXRlcyA8LSBtYXBfZGF0YSgic3RhdGUiKQ0KDQpoZWFkKHVzX3N0YXRlcykgIyBpdCBwcm92aWRlcyBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGluZm9ybWF0aW9uOyByZWdpb24gaXMgdGhlIHN0YXRlIG5hbWUNCg0KZGltKHVzX3N0YXRlcykgIyBpdCdzIGEgbGFyZ2UgZGF0YSBmcmFtZQ0KDQojIE1ha2UgYSBtYXANCg0KcCA8LSBnZ3Bsb3QoZGF0YSA9IHVzX3N0YXRlcywNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGxvbmcsICMgVXNlIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdG8gcGxvdCBzdGF0ZXMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxhdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cCkpIA0KDQpwICsgDQogIGdlb21fcG9seWdvbihmaWxsID0gIndoaXRlIiwNCiAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgIyBPdXRsaW5lIG9mIGEgbWFwIG9mIFUuUy4gc3RhdGVzDQoNCg0KcCA8LSBnZ3Bsb3QoZGF0YSA9IHVzX3N0YXRlcywNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGxvbmcsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbGF0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGdyb3VwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gcmVnaW9uKSkgIyBhZGQgY29sb3IgZmlsbCB0byB0aGUgc3RhdGVzDQoNCnAgKyANCiAgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImdyYXk5MCIsDQogICAgICAgICAgICAgICAgc2l6ZSA9IDAuMSkgKw0KICBndWlkZXMoZmlsbCA9IEZBTFNFKQ0KDQpgYGANCg0KU29tZXRpbWVzIHdlIHdpbGwgd2FudCB0byBhbHRlciB0aGUgbWFwIHByb2plY3Rpb24gc28gdGhhdCBpdCBsb29rcyBtb3JlIGFjY3VyYXRlLiBUaGlzIGNhbiBiZSBkb25lIHVzaW5nIHRoZSBgY29vcmRfbWFwKClgIGZ1bmN0aW9uIHRvIHNlbGVjdCBhbiBhbHRlcm5hdGUgY29vcmRpbmF0ZSBzeXN0ZW0gKHJpZ2h0IG5vdyBpdCBpcyBDYXJ0ZXNpYW4pLiBUbyB1c2UgdGhlIEFsYmVycyBwcm9qZWN0aW9uLCB3ZSBoYXZlIHRvIHByb3ZpZGUgbnVtYmVycyBmb3IgYGxhdDBgIGFuZCBgbGF0MWAuDQoNCmBgYHtyIHVzLW1hcC0yfQ0KDQpwIDwtIGdncGxvdChkYXRhID0gdXNfc3RhdGVzLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gbG9uZywgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsYXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSByZWdpb24pKSANCg0KcCArIA0KICBnZW9tX3BvbHlnb24oY29sb3IgPSAiZ3JheTkwIiwNCiAgICAgICAgICAgICAgICBzaXplID0gMC4xKSArDQogIGNvb3JkX21hcChwcm9qZWN0aW9uID0gImFsYmVycyIsDQogICAgICAgICAgICBsYXQwID0gMzksDQogICAgICAgICAgICBsYXQxID0gNDUpICsNCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkNCg0KYGBgDQoNCk5vdyBpdCBpcyB0aW1lIHRvIGdldCBvdXIgZGF0YSBvbnRvIHRoZSBtYXAuIFdlIGhhdmUgdG8gbWVyZ2UgdGhlIHR3byBkYXRhc2V0cyAtIG9uZSBoYXMgdGhlIGVsZWN0aW9ucyBkYXRhIGFuZCBvbmUgaGFzIHRoZSBkYXRhIHRvIGRyYXcgdGhlIHN0YXRlcyBvbiB0aGUgbWFwLiBXZSBjYW4gdXNlIHRoZSBgbGVmdF9qb2luKClgIGZ1bmN0aW9uIHRvIGRvIHRoaXMuIEl0IGlzIGltcG9ydGFudCBmb3IgYSBjb2x1bW4gb2YgZGF0YSBpbiBlYWNoIGRhdGFzZXQgdG8gbWF0Y2ggZXhhY3RseSBzbyB0aGF0IHdlIGNhbiBwdXQgdGhlIGRhdGFzZXRzIHRvZ2V0aGVyLg0KDQpgYGB7ciB1cy1tYXAtM30NCg0KIyBGaXJzdCB3ZSBuZWVkIHRvIGxvd2VyY2FzZSB0aGUgc3RhdGUgbmFtZXMgYW5kIHB1dCB0aGVtIGluIGEgY29sdW1uIGNhbGxlZCAicmVnaW9uIiwgdG8gbWF0Y2ggdGhlIG1hcHBpbmcgZGF0YQ0KZWxlY3Rpb24kcmVnaW9uIDwtIHRvbG93ZXIoZWxlY3Rpb24kc3RhdGUpDQoNCiMgTm93IHdlIGNhbiBqb2luIHRoZSBkYXRhc2V0cyB0b2dldGhlciB1c2luZyB0aGUgY29tbW9uIHJlZ2lvbiBjb2x1bW4NCnVzX3N0YXRlc19lbGVjIDwtIGxlZnRfam9pbih1c19zdGF0ZXMsIGVsZWN0aW9uKQ0KDQojIFdlIGFyZSBub3cgcmVhZHkgdG8gcGxvdA0KcCA8LSBnZ3Bsb3QoZGF0YSA9IHVzX3N0YXRlc19lbGVjLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gbG9uZywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxhdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHBhcnR5KSkNCg0KcCArIA0KICBnZW9tX3BvbHlnb24oY29sb3IgPSAiZ3JheTkwIiwNCiAgICAgICAgICAgICAgIHNpemUgPSAwLjEpICsNCiAgY29vcmRfbWFwKHByb2plY3Rpb24gPSAiYWxiZXJzIiwNCiAgICAgICAgICAgIGxhdDAgPSAzOSwNCiAgICAgICAgICAgIGxhdDEgPSA0NSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBwYXJ0eV9jb2xvcnMpICsgIyBhZGp1c3QgdGhlIGNvbG9ycyBvZiB0aGUgbWFwDQogIGxhYnModGl0bGUgPSAiRWxlY3Rpb24gUmVzdWx0cyAyMDE2IiwgZmlsbCA9IE5VTEwpICArDQogIHRoZW1lX21hcCgpICMgYWRkZWQgaW4gc2V0dXAgY2h1bmsNCg0KYGBgDQpNYXBwaW5nIHRoZSBkYXRhIG9ubHkgdG8gc3RhdGVzIGlzIGEgbGl0dGxlIGRlY2VwdGl2ZSBiZWNhdXNlIHRoZXJlIGFyZSBkaWZmZXJlbmNlcyBpbiB2b3RpbmcgYnkgY291bnR5IGFuZCBjZXJ0YWluIGFyZWFzIG9mIHRoZSBjb3VudHJ5IGhhdmUgbGFyZ2VyIHBvcHVsYXRpb25zIHRoYW4gb3RoZXJzLg0KDQpgYGB7ciB1cy1tYXAtNH0NCg0KIyBQdXQgYSBjb250aW51b3VzIHZhcmlhYmxlIG9uIHRoZSBtYXAgZmlsbA0KDQpwIDwtIGdncGxvdChkYXRhID0gdXNfc3RhdGVzX2VsZWMsDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBsb25nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbGF0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGdyb3VwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gcGN0X3RydW1wKSkNCg0KcCArIGdlb21fcG9seWdvbihjb2xvciA9ICJncmF5OTAiLA0KICAgICAgICAgICAgICAgICBzaXplID0gMC4xKSArDQogIGNvb3JkX21hcChwcm9qZWN0aW9uID0gImFsYmVycyIsIA0KICAgICAgICAgICAgbGF0MCA9IDM5LA0KICAgICAgICAgICAgbGF0MSA9IDQ1KSArDQogIGxhYnModGl0bGUgPSAiVHJ1bXAgdm90ZSIsDQogICAgICAgZmlsbCA9ICJQZXJjZW50IikgKw0KICB0aGVtZV9tYXAoKQ0KDQojIExldCdzIGNoYW5nZSB0aGUgY29sb3IgdG8gcmVkIGFuZCBoYXZlIGRhcmtlciByZWQgbWVhbiBoaWdoZXIgcGVyY2VudA0KcCArIGdlb21fcG9seWdvbihjb2xvciA9ICJncmF5OTAiLA0KICAgICAgICAgICAgICAgICBzaXplID0gMC4xKSArDQogIGNvb3JkX21hcChwcm9qZWN0aW9uID0gImFsYmVycyIsIA0KICAgICAgICAgICAgbGF0MCA9IDM5LA0KICAgICAgICAgICAgbGF0MSA9IDQ1KSArDQogIGxhYnModGl0bGUgPSAiVHJ1bXAgdm90ZSIsDQogICAgICAgZmlsbCA9ICJQZXJjZW50IikgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiI0NCNDU0QSIpICsNCiAgdGhlbWVfbWFwKCkNCg0KYGBgDQoNCmBzY2FsZV9ncmFkaWVudDIoKWAgaXMgYSBmdW5jdGlvbiB0aGF0IGNyZWF0ZXMgYSBkaXZlcmdpbmcgc2NhbGUgZnJvbSBhIG1pZHBvaW50LiANCg0KYGBge3IgdXMtbWFwLTV9DQoNCnAgPC0gZ2dwbG90KGRhdGEgPSB1c19zdGF0ZXNfZWxlYywNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGxvbmcsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsYXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBkX3BvaW50cykpDQoNCiMgQ3JlYXRlIGEgZ3JhZGllbnQgZmlsbCBmb3IgcG9pbnQgbWFyZ2lucw0KcCArDQogIGdlb21fcG9seWdvbihjb2xvciA9ICJncmF5OTAiLA0KICAgICAgICAgICAgICAgc2l6ZSA9IDAuMSkgKw0KICBjb29yZF9tYXAocHJvamVjdGlvbiA9ICJhbGJlcnMiLA0KICAgICAgICAgICAgbGF0MCA9IDM5LA0KICAgICAgICAgICAgbGF0MSA9IDQ1KSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICBtaWQgPSBzY2FsZXM6Om11dGVkKCJwdXJwbGUiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygtMjUsIDAsIDI1LCA1MCwgNzUpKSArDQogIGxhYnModGl0bGUgPSAiV2lubmluZyBtYXJnaW5zIiwNCiAgICAgICBmaWxsID0gIlBlcmNlbnQiKSArDQogIHRoZW1lX21hcCgpDQoNCiMgUmVtb3ZlIEQuQy4gc2luY2UgaXQgaXMgYW4gb3V0bGllcjsgZ3JhZGllbnQgY29sb3JzIGFyZSBlbmhhbmNlZA0KIyBOb3RlOiBlYXJsaWVyIHdlIHVzZWQgc3QgdG8gcmVtb3ZlIEQuQy4sIG5vdyB3ZSdyZSB1c2luZyByZWdpb24NCnAgPC0gZ2dwbG90KGRhdGEgPSBzdWJzZXQodXNfc3RhdGVzX2VsZWMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb24gJW5pbiUgImRpc3RyaWN0IG9mIGNvbHVtYmlhIiksIA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gbG9uZywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGxhdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGRfcG9pbnRzKSkNCnAgKw0KICBnZW9tX3BvbHlnb24oY29sb3IgPSAiZ3JheTkwIiwNCiAgICAgICAgICAgICAgIHNpemUgPSAwLjEpICsNCiAgY29vcmRfbWFwKHByb2plY3Rpb24gPSAiYWxiZXJzIiwNCiAgICAgICAgICAgIGxhdDAgPSAzOSwNCiAgICAgICAgICAgIGxhdDEgPSA0NSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAicmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gc2NhbGVzOjptdXRlZCgicHVycGxlIiksDQogICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoLTI1LCAwLCAyNSwgNTAsIDc1KSkgKw0KICBsYWJzKHRpdGxlID0gIldpbm5pbmcgbWFyZ2lucyIsDQogICAgICAgZmlsbCA9ICJQZXJjZW50IikgKw0KICB0aGVtZV9tYXAoKQ0KDQpgYGANCg0KIyMjIEFtZXJpY2EncyBVci1jaG9yb3BsZXRocyANCg0KRGF0YSBjYW4gYmUgbWFwcGVkIHRvIGNvdW50aWVzIGFzIHdlbGwsIGJ1dCBpdCdzIGltcG9ydGFudCB0byByZW1lbWJlciB0aGUgcG9wdWxhdGlvbiBkaXN0cmlidXRpb24gaW4gdGhlIFUuUy4gQ2hvcm9wbGV0aCBtYXBzIG9mIHRoZSBVLlMuIHRlbmQgdG8gc2hvdyBwb3B1bGF0aW9uIGRlbnNpdHkgbW9yZSB0aGFuIGFueXRoaW5nIGVsc2UgYmVjYXVzZSB0aGVyZSBhcmUgY29uY2VudHJhdGlvbnMgb2YgcG9wdWxhdGlvbiBpbiBjb3VudGllcyBpbiB0aGUgbm9ydGhlYXN0IGFuZCB0aGUgd2VzdCBjb2FzdCBjb21wYXJlZCB0byB0aGUgd2VzdC4gTm90ZSB0aGF0IHRoZSBwcmV2aW91cyBtYXBzIGRpZCBub3QgaW5jbHVkZSBBbGFza2Egb3IgSGF3YWlpLiBOb3csIHdlIGFyZSBnb2luZyB0byBhZGQgdGhlbSB1c2luZyBhIGNvdW50eSBtYXAgZGF0YXNldC4NCg0KYGBge3IgdXMtbWFwLTZ9DQoNCiMgTWFwcGluZyBkYXRhc2V0IGZvciBVLlMuIGNvdW50aWVzDQpjb3VudHlfbWFwICU+JQ0KICBzYW1wbGVfbig1KSAjIEZJUFMgSUQgaXMgdXNlZCB0byBpZGVudGlmeSB0aGUgY291bnRpZXMNCg0KIyBDb3VudHkgZGVtb2dyYXBoaWMgZGF0YSBmb3IgVS5TLiBDb3VudGllcw0KY291bnR5X2RhdGEgJT4lDQogIHNlbGVjdChpZCwgbmFtZSwgc3RhdGUsIHBvcF9kZW5zLCBwY3RfYmxhY2spICU+JQ0KICBzYW1wbGVfbig1KSAjIEZJUFMgSUQgaXMgdXNlZCB0byBpZGVudGlmeSB0aGUgY291bnRpZXMNCg0KaGVhZChjb3VudHlfZGF0YSkgIyBJRCAwIGlzIGZvciB0aGUgZW50aXJlIFUuUy47IElEcyB3aXRoIGp1c3QgdGhlIGZpcnN0IHR3by1kaWdpdHMgaXMgZm9yIHRoZSBzdGF0ZQ0KDQojIFB1dCAgdGhlIG1hcHBpbmcgZGF0YSBhbmQgZGVtb2dyYXBoaWMgZGF0YSB0b2dldGhlcg0KY291bnR5X2Z1bGwgPC0gbGVmdF9qb2luKGNvdW50eV9tYXAsIGNvdW50eV9kYXRhLCBieSA9ICJpZCIpDQoNCiMgUGxvdCBwb3B1bGF0aW9uIGRlbnNpdHkgYnkgY291bnR5DQpwIDwtIGdncGxvdChkYXRhID0gY291bnR5X2Z1bGwsDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBsb25nLCB5ID0gbGF0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gcG9wX2RlbnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXApKQ0KcCArDQogIGdlb21fcG9seWdvbihjb2xvciA9ICJncmF5OTAiLCBzaXplID0gMC4wNSkgKw0KICBjb29yZF9lcXVhbCgpICsgIyByZWxhdGl2ZSBzY2FsZSBvZiBtYXAgZG9lcyBub3QgY2hhbmdlLCBldmVuIGlmIHBsb3QgZGltZW5zaW9ucyBjaGFuZ2UNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJCbHVlcyIsDQogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAtMTAiLCAiMTAtNTAiLCAiNTAtMTAwIiwgIjEwMC01MDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI1MDAtMSwwMDAiLCAiMSwwMDAtNSwwMDAiLCAiPjUsMDAwIikpICsNCiAgbGFicyhmaWxsID0gIlBvcHVsYXRpb24gcGVyXG5zcXVhcmUgbWlsZSIpICsNCiAgdGhlbWVfbWFwKCkgKw0KICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMSkpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCiMgUGxvdCBwZXJjZW50IG9mIEJsYWNrIHBvcHVsYXRpb24gYnkgY291bnR5DQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBjb3VudHlfZnVsbCwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBwY3RfYmxhY2ssDQogICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXApKSANCg0KcCArIA0KICBnZW9tX3BvbHlnb24oY29sb3IgPSAiZ3JheTkwIiwNCiAgICAgICAgICAgICAgIHNpemUgPSAwLjA1KSArDQogIGNvb3JkX2VxdWFsKCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkdyZWVucyIpICsNCiAgbGFicyhmaWxsID0gIlVTIFBvcHVsYXRpb24sIFBlcmNlbnQgQmxhY2siKSArDQogIHRoZW1lX21hcCgpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobnJvdyA9IDEpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQpgYGANCg0KVGhlIHBvcHVsYXRpb24gZGVuc2l0eSBhbmQgdGhlIHBlcmNlbnQgb2YgdGhlIHBvcHVsYXRpb24gdGhhdCBpcyBCbGFjayBhcmUgY29uZm91bmRpbmcgdmFyaWFibGVzIHdpdGggbWFueSBvdGhlciBjb3VudHktbGV2ZWwgdmFyaWFibGVzIHRoYXQgd2UgbWlnaHQgd2FudCB0byBleGFtaW5lLCBzbyBpdCBpcyBpbXBvcnRhbnQgdG8ga2VlcCB0aGUgcHJldmlvdXMgdHdvIHBsb3RzIGluIG1pbmQgd2hlbmV2ZXIgcGxvdHRpbmcgY291bnR5LWxldmVsIGRhdGEuIFRvIGRlbW9uc3RyYXRlIHRoaXMgaXNzdWUsIHdlIHdpbGwgbWFrZSB0d28gbW9yZSBwbG90cyAtIG9uZSBvbiBndW4tcmVsYXRlZCBzdWljaWRlcyBhbmQgb25lIG9uIGJpbm5lZCBwb3B1bGF0aW9uIGRlbnNpdHkuIA0KDQpgYGB7ciB1cy1tYXAtN30NCg0KIyBDcmVhdGUgdGhlIGNvbG9yIHBhbGV0dGUNCiMgYnJld2VyLnBhbCgpIHByb2R1Y2VzIGV2ZW5seSBzcGFjZWQgY29sb3IgcGFsZXR0ZXMNCm9yYW5nZV9wYWwgPC0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKG4gPSA2LCBuYW1lID0gIk9yYW5nZXMiKQ0Kb3JhbmdlX3BhbA0KDQojIFJldmVyc2UgY29sb3IgcGFsZXR0ZQ0Kb3JhbmdlX3JldiA8LSByZXYob3JhbmdlX3BhbCkNCm9yYW5nZV9yZXYNCg0KIyBSZWNyZWF0ZSBhICJwb29ybHkgc291cmNlZCBieSB3aWRlbHkgY2lyY3VsYXRlZCBjb3VudHkgbWFwIG9mIGZpcmVhcm0tcmVsYXRlZCBzdWljaWRlIHJhdGVzIiAocC4gMTg2KQ0KZ3VuX3AgPC0gZ2dwbG90KGRhdGEgPSBjb3VudHlfZnVsbCwNCiAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBsb25nLCB5ID0gbGF0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IHN1X2d1bjYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGdyb3VwKSkNCg0KZ3VuX3AxIDwtIGd1bl9wICsNCiAgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImdyYXk5MCIsDQogICAgICAgICAgICAgICBzaXplID0gMC4wNSkgKw0KICBjb29yZF9lcXVhbCgpDQoNCmd1bl9wMiA8LSBndW5fcDEgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBvcmFuZ2VfcGFsKQ0KDQpndW5fcDIgKyANCiAgbGFicyh0aXRsZSA9ICJHdW4tUmVsYXRlZCBTdWljaWRlcyAxOTk5LTIwMTUiLA0KICAgICAgIGZpbGwgPSAiUmF0ZSBwZXIgMTAwLDAwMHBvcC4iKSArDQogIHRoZW1lX21hcCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCiMgQ3JlYXRlIHRoZSByZXZlcnNlLWNvZGVkIHBvcHVsYXRpb24gZGVuc2l0eSBtYXANCnBvcF9wIDwtIGdncGxvdChkYXRhID0gY291bnR5X2Z1bGwsDQogICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gbG9uZywgeSA9IGxhdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBwb3BfZGVuczYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IGdyb3VwKSkNCg0KcG9wX3AxIDwtIHBvcF9wICsNCiAgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImdyYXk5MCIsIHNpemUgPSAwLjA1KSArDQogIGNvb3JkX2VxdWFsKCkNCg0KcG9wX3AyIDwtIHBvcF9wMSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IG9yYW5nZV9yZXYpDQoNCnBvcF9wMiArIGxhYnModGl0bGUgPSAiUmV2ZXJzZS1jb2RlZCBQb3B1bGF0aW9uIERlbnNpdHkiLA0KICAgICAgICAgICAgICBmaWxsID0gIlBlb3BsZSBwZXIgc3F1YXJlIG1pbGUiKSArDQogIHRoZW1lX21hcCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCmBgYA0KIlNtYWxsIGRpZmZlcmVuY2VzIGluIHJlcG9ydGluZywgY29tYmluZWQgd2l0aCBjb2Fyc2UgYmlubmluZyBhbmQgbWlzY29kaW5nLCB3aWxsIHByb2R1Y2Ugc3BhdGlhbGx5IG1pc2xlYWRpbmcgYW5kIHN1YnN0YW50aXZlbHkgbWlzdGFrZW4gcmVzdWx0cy4gSXQgbWlnaHQgc2VlbSB0aGF0IGZvY3VzaW5nIG9uIG9uIHRoZSBkZXRhaWxzIG9mIHZhcmlhYmxlIGNvZGluZyBpbiB0aGlzIHBhcnRpY3VsYXIgY2FzZSBpcyBhIGxpdHRsZSB0b28gbXVjaCBpbiB0aGUgd2VlZHMgZm9yIGEgZ2VucmVhbCBpbnRyb2R1Y3Rpb24uIEJ1dCBpdCBpcyBleGFjdGx5IHRoZXNlIGRldGFpbHMgdGhhdCBjYW4gZHJhbWF0aWNhbGx5IGFsdGVyIHRoZSBhcHBlYXJhbmNlIG9mIGFueSBncmFwaCwgYW5kIGVzcGVjaWFsbHkgbWFwcywgaW4gYSB3YXkgdGhhdCBjYW4gYmUgaGFyZCB0byBkZXRlY3QgYWZ0ZXIgdGhlIGZhY3QuIiAocC4gMTg5KQ0KDQojIyMgU3RhdGViaW5zDQoNClRoZSBgc3RhdGViaW5zYCBwYWNrYWdlIGlzIGFuIGFsdGVybmF0aXZlIHdheSB0byBkZXZlbG9wIFUuUy4gbWFwcy4gVGhlIHN5bnRheCBpcyBzbGlnaHRseSBkaWZmZXJlbnQgdGhhbiBgZ2dwbG90YDsgdGhlIGBzdGF0ZWJpbnNgIHBhY2thZ2UgaGFzIGJlZW4gdXBkYXRlZCBzaW5jZSB0aGUgd3JpdGluZyBvZiB0aGUgYm9vaywgc28gdGhlIGNvZGUgYmVsb3cgaXMgZGlmZmVyZW50IHRoYW4gdGhlIGNvZGUgaW4gdGhlIGJvb2suDQoNCmBgYHtyIHN0YXRlYmluc30NCg0KIyBpbnN0YWxsLnBhY2thZ2VzKCJzdGF0ZWJpbnMiKSAjIG5vdGUgc3RhdGViaW5zIGhhcyBub3QgYmVlbiBwcmV2aW91c2x5IGluc3RhbGxlZA0KDQpsaWJyYXJ5KHN0YXRlYmlucykNCg0KIyBUaGUgc3RhdGViaW5zIHBhY2thZ2UgaGFzIGNoYW5nZWQgc2luY2UgdGhpcyBib29rIHdhcyB3cml0dGVuDQoNCiMgQ29udGludW91cyBEYXRhDQoNCnN0YXRlYmlucyhzdGF0ZV9kYXRhID0gZWxlY3Rpb24sDQogICAgICAgICAgc3RhdGVfY29sID0gInN0YXRlIiwNCiAgICAgICAgICB2YWx1ZV9jb2wgPSAicGN0X3RydW1wIiwNCiAgICAgICAgICByb3VuZCA9IEZBTFNFKSArDQogIGxhYnMoZmlsbCA9ICJQZXJjZW50IFRydW1wIikgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICIjRkVFNUQ5IiwgDQogICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICIjQTUwRjE1IikgKw0KICB0aGVtZV9zdGF0ZWJpbnMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSANCg0Kc3RhdGViaW5zKHN0YXRlX2RhdGEgPSBzdWJzZXQoZWxlY3Rpb24sIHN0ICVuaW4lICJEQyIpLA0KICAgICAgICAgIHN0YXRlX2NvbCA9ICJzdGF0ZSIsDQogICAgICAgICAgdmFsdWVfY29sID0gInBjdF9jbGludG9uIiwNCiAgICAgICAgICByb3VuZCA9IEZBTFNFKSArDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIiNFRkYzRkYiLCANCiAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gIiMwODUxOUMiKSArDQogIGxhYnMoZmlsbCA9ICJQZXJjZW50IENsaW50b24iKSArDQogIHRoZW1lX3N0YXRlYmlucygpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpIA0KDQogIA0KIyBDYXRlZ29yaWNhbCBEYXRhDQoNCmdncGxvdChkYXRhID0gZWxlY3Rpb24sIA0KICAgICAgIG1hcHBpbmcgPSBhZXMoc3RhdGUgPSBzdCwNCiAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBwYXJ0eSkpICsNCiAgZ2VvbV9zdGF0ZWJpbnMoKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInJveWFsYmx1ZSIsICJkYXJrcmVkIiksDQogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkNsaW50b24iLCAiVHJ1bXAiKSkgKw0KICBsYWJzKGZpbGwgPSAiV2lubmVyIikgKw0KICBjb29yZF9lcXVhbCgpICsNCiAgdGhlbWVfc3RhdGViaW5zKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSANCg0KIyBCaW5uZWQgRGF0YQ0KDQpnZ3Bsb3QoZGF0YSA9IGVsZWN0aW9uLCANCiAgICAgICBtYXBwaW5nID0gYWVzKHN0YXRlID0gc3QsDQogICAgICAgICAgICAgICAgICAgICBmaWxsID0gY3V0KHBjdF90cnVtcCwgNCkpKSArDQogIGdlb21fc3RhdGViaW5zKCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlJlZHMiLA0KICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCI0LTIxIiwgIjIxLTM3IiwgIjM3LTUzIiwgIjUzLTcwIikpICsNCiAgbGFicyhmaWxsID0gIlBlcmNlbnQgVHJ1bXAiKSArDQogIGNvb3JkX2VxdWFsKCkgKw0KICB0aGVtZV9zdGF0ZWJpbnMoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSANCg0KDQoNCmBgYA0KDQojIyMgU21hbGwtTXVsdGlwbGUgTWFwcw0KDQpVc2Ugc21hbGwtbXVsdGlwbGUgbWFwcyB0byBzaG93IGdlb2dyYXBoaWMgZGF0YSBvdmVyIHRpbWUuIFdlIHdpbGwgYWxzbyB1c2UgdGhlIGB2aXJpZGlzYCBwYWNrYWdlIHRvIGdldCBnb29kIGNvbG9yIHBhbGV0dGVzIHRoYXQgYXJlIHZpYnJhbnQgb24gYm90aCBlbmRzLg0KDQpgYGB7ciBtYXAtc21hbGwtbXVsdGlwbGV9DQoNCm9waWF0ZXMgIyBzdGF0ZS1sZXZlbCBkZWF0aCByYXRlIGZyb20gb3B0aWF0ZS1yZWxhdGVkIGNhdXNlcyAxOTk5LTIwMTQNCg0KIyBsb3dlci1jYXNlIHRoZSBzdGF0ZSBuYW1lIHRvIG1hdGNoIHRoZSB1c19zdGF0ZXMgZGF0YSAoZnJvbSB0aGUgbWFwcyBwYWNrYWdlKQ0Kb3BpYXRlcyRyZWdpb24gPC0gdG9sb3dlcihvcGlhdGVzJHN0YXRlKQ0KDQojIGpvaW4gd2l0aCB0aGUgdXNfc3RhdGVzIGRhdGENCm9waWF0ZXNfbWFwIDwtIGxlZnRfam9pbih1c19zdGF0ZXMsIG9waWF0ZXMpDQoNCmxpYnJhcnkodmlyaWRpcykNCg0KcDAgPC0gZ2dwbG90KGRhdGEgPSBzdWJzZXQob3BpYXRlc19tYXAsIHllYXIgPiAxOTk5KSwNCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBsb25nLCB5ID0gbGF0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBhZGp1c3RlZCkpDQoNCnAxIDwtIHAwICsNCiAgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImdyYXk5MCIsDQogICAgICAgICAgICAgICBzaXplID0gMC4wNSkgKw0KICBjb29yZF9tYXAocHJvamVjdGlvbiA9ICJhbGJlcnMiLA0KICAgICAgICAgICAgbGF0MCA9IDM5LA0KICAgICAgICAgICAgbGF0MSA9IDQ1KQ0KDQpwMiA8LSBwMSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiKQ0KDQpwMiArIA0KICB0aGVtZV9tYXAoKSArDQogIGZhY2V0X3dyYXAofiB5ZWFyLCBuY29sID0gMykgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBsYWJzKHRpdGxlID0gIk9waWF0ZSBSZWxhdGVkIERlYXRocyBieSBTdGF0ZSwgMjAwMC0yMDE0IiwNCiAgICAgICBmaWxsID0gIkRlYXRoIHJhdGUgcGVyIDEwMCwwMDAgcG9wdWxhdGlvbiIpDQoNCg0KYGBgDQoNCkJ1dCB0aGlzIG1pZ2h0IG5vdCBiZSB0aGUgYmVzdCB3YXkgdG8gdmlldyB0aGlzIGRhdGEgZHVlIHRvIHRoZSBpc3N1ZXMgd2l0aCBwb3B1bGF0aW9uLCBkZW1vZ3JhcGhpY3MsIGFuZCB0aGUgZGlmZmljdWx0eSBpbiBjb21wYXJpbmcgdGhpbmdzIHNwYXRpYWxseS4gDQoNCiMjIyBJcyBZb3VyIERhdGEgUmVhbGx5IFNwYXRpYWw/DQoNCldlIGNhbiBtYWtlIGxpbmUgcGxvdHMgb2YgdGhlIG9waWF0ZXMgZGF0YSBvdmVyIHRpbWUsIHdoaWNoIG1heSBtYWtlIGl0IGVhc2llciB0byBtYWtlIGNvbXBhcmlzb25zIGFuZCBkcmF3IGNvbmNsdXNpb25zLg0KDQpgYGB7ciBsaW5lLXBsb3RzLW9waWF0ZXN9DQoNCnAgPC0gZ2dwbG90KGRhdGEgPSBvcGlhdGVzLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0geWVhciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGFkanVzdGVkLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBzdGF0ZSkpDQoNCnAgKyBnZW9tX2xpbmUoY29sb3IgPSAiZ3JheTcwIikgIyBCdXQgdGhpcyBpcyBhIGxpdHRsZSBkaWZmaWN1bHQgdG8gc2VlIHNpbmNlIHRoZXJlIGFyZSBzbyBtYW55IGxpbmVzDQoNCiMgRGl2aWRlIGl0IHVwIGJ5IGNlbnN1cyBkaXZpc2lvbg0KDQpwMCA8LSBnZ3Bsb3QoZGF0YSA9IGRyb3BfbmEob3BpYXRlcywgZGl2aXNpb25fbmFtZSksICMgcmVtb3ZlIHJvd3Mgd2l0aCBOQSBmb3IgZGl2aXNpb25fbmFtZSB0byBsZWF2ZSBvdXQgRC5DLg0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0geWVhciwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBhZGp1c3RlZCkpDQpwMSA8LSBwMCArDQogIGdlb21fbGluZShjb2xvciA9ICJncmF5NzAiLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyhncm91cCA9IHN0YXRlKSkgIyBtYWtlIGxpbmUgY2hhcnQsIG9uZSBsaW5lIHBlciBzdGF0ZQ0KDQpwMiA8LSBwMSArIA0KICBnZW9tX3Ntb290aChtYXBwaW5nID0gYWVzKGdyb3VwID0gZGl2aXNpb25fbmFtZSksDQogICAgICAgICAgICAgIHNlID0gRkFMU0UpICMgbWFrZSB0cmVuZCBsaW5lIGJ5IGNlbnN1cyBkaXZpc2lvbg0KDQpwMyA8LSBwMiArICMgbGFiZWwgb25seSB0aGUgZW5kIG9mIHRoZSBsaW5lIHdpdGggdGhlIHN0YXRlDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KG9waWF0ZXMsIHllYXIgPT0gbWF4KHllYXIpICYgYWJiciAhPSAiREMiKSwNCiAgICAgICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHllYXIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBhZGp1c3RlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBhYmJyKSwNCiAgICAgICAgICAgICAgICAgIHNpemUgPSAxLjgsDQogICAgICAgICAgICAgICAgICBzZWdtZW50LmNvbG9yID0gTkEsICMgbm8gbGluZSBzZWdtZW50IGxpbmtpbmcgdGV4dCB0byBwb2ludA0KICAgICAgICAgICAgICAgICAgbnVkZ2VfeCA9IDMwKSArICMgc2hpZnQgdGhlIGxhYmVsIHRleHQgdG8gdGhlIHJpZ2h0DQogICMgc2hpZnQgY29vcmRpbmF0ZSBzeXN0ZW0gc28gdGhlcmUncyByb29tIGZvciB0aGUgbGFiZWxzDQogIGNvb3JkX2NhcnRlc2lhbihjKG1pbihvcGlhdGVzJHllYXIpLCBtYXgob3BpYXRlcyR5ZWFyKSkpIA0KDQpwMyArIA0KICBsYWJzKHggPSAiIiwNCiAgICAgICB5ID0gIlJhdGUgcGVyIDEwMCwwMDAgcG9wdWxhdGlvbiIsDQogICAgICAgdGl0bGUgPSAiU3RhdGUtTGV2ZWwgT3BpYXRlIERlYXRoIFJhdGVzIGJ5IENlbnN1cyBEaXZpc2lvbiwgMTk5OS0yMDE0IikgKw0KICBmYWNldF93cmFwKH4gcmVvcmRlcihkaXZpc2lvbl9uYW1lLCAtYWRqdXN0ZWQsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICMgcHV0IHRoZSBkaXZpc2lvbnMgd2l0aCBoaWdoZXN0IHJhdGVzIGZpcnN0DQogICAgICAgICAgICAgbnJvdyA9IDMpDQoNCg0KYGBgDQoNCiMjIyBXaGVyZSB0byBHbyBOZXh0ICANClBpY2sgYXQgbGVhc3QgdHdvIG9mIHRoZSBxdWVzdGlvbnMgcHJlc2VudGVkIHVuZGVyIHRoZSAqV2hlcmUgdG8gR28gTmV4dCogc2VjdGlvbiBhbmQgYW5zd2VyIHRoZW0uDQoNCg0KIyMgUmVmaW5lIHlvdXIgUGxvdHMNCg0KYGBnZ3Bsb3RgYCBhbGxvd3MgdXMgdG8gbWFrZSBsb3RzIG9mIGN1c3RvbWl6YXRpb24gYW5kIHJlZmluZW1lbnQgdG8gb3VyIHBsb3RzIG9uY2Ugd2UgaGF2ZSBmaW5hbGl6ZWQgdGhlIGRhdGEgd2Ugd2FudCB0byBzaG93LiBUaGlzIGNvdWxkIGluY2x1ZGUgYW5ub3RhdGlvbnMsIGhpZ2hsaWdodHMsIHRoZW1lIGNoYW5nZXMsIGFuZCBkaWZmZXJlbnQgY29sb3JzLg0KDQpgYGB7ciBiYXNpYy1yZWZpbmVtZW50fQ0KDQojIE5ldyBkYXRhc2V0IC0gbWVtYmVyc2hpcCBhbmQgaW5jb21lIGRhdGEgYnkgc2VjdGlvbiBvZiB0aGUgQW1lcmljYW4gU29jaW9sb2dpY2FsIEFzc29jaWF0aW9uDQpoZWFkKGFzYXNlYykNCg0KIyBQbG90IG1lbWJlcnNoaXAgdnMuIHJldmVudWUgZm9yIDIwMTQNCnAgPC0gZ2dwbG90KGRhdGEgPSBzdWJzZXQoYXNhc2VjLCBZZWFyID09IDIwMTQpLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gTWVtYmVycywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFJldmVudWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IFNuYW1lKSkgIA0KDQojIFVzZSBhIGxpbmVhciBmdW5jdGlvbiB0byBlc3RpbWF0ZSByZWxhdGlvbnNoaXANCiMgTGFiZWwgdGhlIG91dGxpZXJzDQojIEFkZCB0aXRsZXMgYW5kIGF4aXMgbGFiZWxzDQojIE1ha2UgeS1heGlzIGxhYmVscyBjdXJyZW5jeQ0KIyBNb3ZlIGxlZ2VuZCB0byB0aGUgYm90dG9tDQpwNCA8LSBwICsNCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGNvbG9yID0gSm91cm5hbCkpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSwgY29sb3IgPSAiZ3JheTgwIikgKw0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChhc2FzZWMsIFllYXIgPT0gMjAxNCAmIFJldmVudWVzID4gNzAwMCksIA0KICAgICAgICAgICAgICAgICAgc2l6ZSA9IDIpICsNCiAgbGFicyh4ID0gIk1lbWJlcnNoaXAiLA0KICAgICAgIHkgPSAiUmV2ZW51ZXMiLA0KICAgICAgIGNvbG9yID0gIlNlY3Rpb24gaGFzIG93biBKb3VybmFsIiwNCiAgICAgICB0aXRsZSA9ICJBU0EgU2VjdGlvbnMiLA0KICAgICAgIHN1YnRpdGxlID0gIjIwMTQgQ2FsZW5kYXIgWWVhci4iLA0KICAgICAgIGNhcHRpb24gPSAiU291cmNlOiBBU0EgYW5udWFsIHJlcG9ydCIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6ZG9sbGFyKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQpwNA0KDQpgYGANCg0KIyMjIFVzZSBDb2xvciB0byBZb3VyIEFkdmFudGFnZQ0KDQpXaGVuIGRlY2lkaW5nIG9uIGEgY29sb3IgcGFsZXR0ZSwgdGhpbmsgYWJvdXQgd2hldGhlciB0aGUgdmFyaWFibGUgdmFsdWVzIGFyZSB1bm9yZGVyZWQgb3Igb3JkZXJlZDsgY2F0ZWdvcmljYWwgb3IgbnVtZXJpYy4gYFJDb2xvckJyZXdlcmAgb2ZmZXJzIG1hbnkgdHlwZXMgb2YgcGFsZXR0ZXMgYW5kIGNhbiBiZSBhY2Nlc3NlZCB3aXRoaW4gYGdncGxvdGAgdXNpbmcgYHNjYWxlX2NvbG9yX2JyZXdlcigpYCBhbmQgYHNjYWxlX2ZpbGxfYnJld2VyKClgLiBDb2xvciBwYWxldHRlcyBjYW4gYWxzbyBiZSBzZXQgbWFudWFsbHkgYnkgcHJvdmlkaW5nIHRoZSBoZXggY29kZXMgYW5kIHVzaW5nIGBzY2FsZV9jb2xvcl9tYW51YWwoKWAgYW5kIGBzY2FsZV9maWxsX21hbnVhbCgpYC4gUiBhbHNvIGtub3dzIHNvbWUgY29sb3IgbmFtZXMsIHRoZXNlIGNhbiBiZSBhY2Nlc3NlZCB1c2luZyBgZGVtbygnY29sb3JzJylgLg0KDQpgYGB7ciB1c2UtY29sb3J9DQoNCiMgVGVzdCBkaWZmZXJlbnQgY29sb3IgcGFsZXR0ZXMgd2l0aCB0aGUgb3JnYW5kYXRhDQpwIDwtIGdncGxvdChkYXRhID0gb3JnYW5kYXRhLA0KICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcm9hZHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBkb25vcnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gd29ybGQpKQ0KDQojIFNldDINCnAgKyBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQojIFBhc3RlbDINCnAgKyBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlBhc3RlbDIiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQojIERhcmsyDQpwICsgZ2VvbV9wb2ludChzaXplID0gMikgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCg0KIyBVc2UgYSBtYW51YWwgcGFsZXR0ZQ0KDQojIFNldCB0aGUgY29sb3IgdmFsdWVzDQpjYl9wYWxldHRlIDwtIGMoIiM5OTk5OTkiLCAiI0U2OUYwMCIsICIjNTZCNEU5IiwgIiMwMDlFNzMiLA0KICAgICAgICAgICAgICAgICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIsICIjQ0M3OUE3IikNCg0KIyBVc2Ugc2NhbGVfY29sb3JfbWFudWFsKCkgdG8gc2V0IGNvbG9ycyB0byB0aGUgY29sb3IgYmxpbmQgcGFsZXR0ZQ0KcDQgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2JfcGFsZXR0ZSkNCg0KIyBOb3RlOiBvbmx5IHRoZSBmaXJzdCB0d28gY29sb3JzIGFyZSB1c2VkIGJlY2F1c2UgdGhlcmUgYXJlIG9ubHkgdHdvIHZhbHVlcyBpbiB0aGUgY29sb3IgdmFyaWFibGUNCg0KIyBUaGUgZGljaHJvbWF0IHBhY2thZ2UgY2FuIGJlIHVzZWQgdG8gdHJhbnNmb3JtIGNvbG9ycyBpbnRvIGNvbG9yLWJsaW5kIHNhZmUgY29sb3JzDQpsaWJyYXJ5KGRpY2hyb21hdCkgIyBub3RlOiBuZWVkIHRvIGluc3RhbGwNCg0KIyBHZXQgbGlzdCBvZiBjb2xvcnMgLSA1IGNvbG9ycyBmcm9tIFNldDINCmRlZmF1bHQgPC0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDUsICJTZXQyIikNCg0KIyBMaXN0IHR5cGVzIG9mIGNvbG9yLWJsaW5kbmVzcw0KdHlwZXMgPC0gYygiZGV1dGFuIiwgInByb3RhbiIsICJ0cml0YW4iKQ0KbmFtZXModHlwZXMpIDwtIGMoIkRldXRlcm9ub3BpYSIsICJQcm90YW5vcGlhIiwgIlRyaXRhbm9waWEiKQ0KDQojIENyZWF0ZSBhIHRhYmxlIG9mIGNvbG9ycyB0aGF0IHdpbGwgdHJhbnNmb3JtIHRoZSBTZXQyIGNvbG9ycyBpbnRvIHdoYXQgaXMgc2VlbiB1bmRlciBkaWZmZXJlbnQgY29sb3ItYmxpbmRuZXNzDQpjb2xvcl90YWJsZSA8LSB0eXBlcyAlPiUNCiAgICBwdXJycjo6bWFwKH4gZGljaHJvbWF0KGRlZmF1bHQsIC54KSkgJT4lDQogICAgYXNfdGliYmxlKCkgJT4lDQogICAgYWRkX2NvbHVtbihkZWZhdWx0LCAuYmVmb3JlID0gVFJVRSkNCg0KY29sb3JfdGFibGUNCg0KIyB2aWV3IHRoZSBjb2xvcnMNCmNvbG9yX2NvbXAoY29sb3JfdGFibGUpDQoNCmBgYA0KDQojIyMgTGF5ZXIgQ29sb3IgYW5kIFRleHQgVG9nZXRoZXINCg0KV2UgY2FuIHVzZSBjb2xvciBhbmQgdGV4dCB0byBoaWdobGlnaHQgY2VydGFpbiBwYXJ0cyBvZiBhIHBsb3QuIGBjb3VudHlfZGF0YWAgY29udGFpbnMgZGF0YSBvbiB0aGUgMjAxNiBVLlMuIGdlbmVyYWwgZWxlY3Rpb24uDQoNCmBgYHtyIGxheWVyLWNvbG9yfQ0KDQojIERlbW9jcmF0IEJsdWUgYW5kIFJlcHVibGljYW4gUmVkIC0gY3JlYXRlIGNvbG9yIHBhbGV0dGUNCnBhcnR5X2NvbG9ycyA8LSBjKCIjMkU3NEMwIiwgIiNDQjQ1NEEiKQ0KDQojIEluaXRpYWwgcGxvdCAtIHBvaW50cyBmb3IgZWFjaCBjb3VudHkgdGhhdCBkaWQgbm90IGZsaXANCiMgeC1heGlzIGlzIHRoZSBwb3B1bGF0aW9uDQojIHktYXhpcyBpcyB0aGUgcGVyY2VudCBvZiB0aGUgcG9wdWxhdGlvbiB0aGF0IGlzIEJsYWNrDQpwMCA8LSBnZ3Bsb3QoZGF0YSA9IHN1YnNldChjb3VudHlfZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZsaXBwZWQgPT0gIk5vIiksDQogICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcG9wLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGJsYWNrIC8gMTAwKSkNCg0KcDEgPC0gcDAgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC4xNSwNCiAgICAgICAgICAgICBjb2xvciA9ICJncmF5NTAiKSArDQogIHNjYWxlX3hfbG9nMTAobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgIyBsb2ctc2NhbGUgZm9yIHBvcHVsYXRpb24NCg0KcDENCg0KIyBBZGQgYSBzZWNvbmQgbGF5ZXIgb2YgcG9pbnRzIGZvciB0aGUgY291bnRpZXMgdGhhdCBkaWQgZmxpcA0KIyBjb2xvciBieSBwYXJ0eSB0aGF0IHdvbg0KcDIgPC0gcDEgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoY291bnR5X2RhdGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmbGlwcGVkID09ICJZZXMiKSwNCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBwb3AsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYmxhY2sgLyAxMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHBhcnR5d2lubmVyMTYpKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBwYXJ0eV9jb2xvcnMpDQoNCnAyDQoNCiMgU2V0IHRoZSB5LWF4aXMgc2NhbGUgYW5kIGxhYmVscw0KcDMgPC0gcDIgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIGxhYnMoY29sb3IgPSAiQ291bnR5IGZsaXBwZWQgdG8uLi4iLA0KICAgICAgIHggPSAiQ291bnR5IFBvcHVsYXRpb24gKGxvZyBzY2FsZSkiLA0KICAgICAgIHkgPSAiUGVyY2VudCBCbGFjayBQb3B1bGF0aW9uIiwNCiAgICAgICB0aXRsZSA9ICJGbGlwcGVkIGNvdW50aWVzLCAyMDE2IiwNCiAgICAgICBjYXB0aW9uID0gIkNvdW50aWVzIGluIGdyYXkgZGlkIG5vdCBmbGlwLiIpDQoNCnAzDQoNCiMgTGFiZWwgY291bnRpZXMgd2l0aCBoaWdoIHBlcmNlbnRhZ2Ugb2YgQmxhY2sgcmVzaWRlbnRzIHRoYXQgZmxpcHBlZA0KcDQgPC0gcDMgKw0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChjb3VudHlfZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmxpcHBlZCA9PSAiWWVzIiAmIGJsYWNrID4gMjUpLA0KICAgICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh4ID0gcG9wLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYmxhY2sgLyAxMDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gc3RhdGUpLA0KICAgICAgICAgICAgICAgICAgc2l6ZSA9IDIpDQoNCiMgQWRkIGEgbWluaW1hbCB0aGVtZSBhbmQgcHV0IHRoZSBsZWdlbmQgYXQgdGhlIHRvcA0KcDQgKyB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCg0KYGBgDQoNCiMjIyBDaGFuZ2UgdGhlIEFwcGVhcmFuY2Ugb2YgUGxvdHMgd2l0aCBUaGVtZXMNCg0KVGhlIHRoZW1lIG9mIGEgcGxvdCBjYW4gY2hhbmdlIHRoZSBvdmVyYWxsIGFwcGVhcmFuY2UuIFVzaW5nIGB0aGVtZV9zZXQoKWAgd2l0aCB0aGUgbmFtZSBvZiBhIHRoZW1lIGFzIHRoZSBhcmd1bWVudCBvZiB0aGUgZnVuY3Rpb24sIHdlIGNhbiBzZXQgdGhlIHRoZW1lIGZvciBhbGwgcGxvdHMgaW4gYSBzZXNzaW9uLiBXZSBjYW4gYWxzbyB1c2UgYCtgIHdpdGggYSB0aGVtZSBmdW5jdGlvbiB0byBhZGQgYSB0aGVtZSB0byBhIHNwZWNpZmljIHBsb3QuIFRoZSBgdGhlbWUoKWAgZnVuY3Rpb24gY2FuIGJlIHVzZWQgdG8gZmluZS10dW5lIGRpZmZlcmVudCBhc3BlY3RzIG9mIHRoZSBwbG90IChsaWtlICB0aGUgbGVnZW5kIHBvc2l0aW9uKS4gYGdncGxvdGAgY29udGFpbnMgc29tZSBkZWZhdWx0IHRoZW1lczsgdGhlIGBnZ3RoZW1lc2AgcGFja2FnZSBjb250YWlucyBtb3JlIHRoZW1lcy4gVGhlcmUgYXJlIG90aGVyIHByZS1tYWRlIHRoZW1lcyBhdmFpbGFibGUgaW4gdGhlIGBjb3dwbG90YCBhbmQgYGhyYnJ0aGVtZXNgIHBhY2thZ2VzLiBXZSBjYW4gYWxzbyBtYW51YWxseSBzZXQgdGhlbWUgZWxlbWVudHMgd2l0aGluIHRoZSBgdGhlbWUoKWAgZnVuY3Rpb24gdG8gbWFrZSBvdXIgb3duIGN1c3RvbSB0aGVtZXMuDQoNCmBgYHtyIGNoYW5nZS10aGVtZX0NCg0KIyB0aGVtZV9idygpDQp0aGVtZV9zZXQodGhlbWVfYncoKSkNCnA0ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQoNCiMgdGhlbWVfZGFyaw0KdGhlbWVfc2V0KHRoZW1lX2RhcmsoKSkNCnA0ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQoNCmxpYnJhcnkoZ2d0aGVtZXMpDQoNCiMgRWNvbm9taXN0IFRoZW1lDQp0aGVtZV9zZXQodGhlbWVfZWNvbm9taXN0KCkpDQpwNCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikNCg0KIyBXYWxsIFN0cmVldCBKb3VybmFsIFRoZW1lDQp0aGVtZV9zZXQodGhlbWVfd3NqKCkpDQoNCiMgVGV4dCBzaXplcyBhcmUgcXVpdGUgbGFyZ2Ugb24gdGhpcyB0aGVtZSwgc28gc2V0IHRoZW0gaW4gdGhlIHRoZW1lKCkgZnVuY3Rpb24gdG8gYmUgc21hbGxlcg0KcDQgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC42KSksDQogICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuMzUpKSwNCiAgICAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMC4zNSkpLA0KICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIikNCg0KIyBDbGVhciBvdXQgdGhlbWUgc2V0dGluZw0KdGhlbWVfc2V0KHRoZW1lKCkpDQoNCiMgTWFrZSBhIGN1c3RvbSB0aGVtZQ0KIyBXYXJuaW5ncyAtIGZvbnQgZmFtaWx5IG5vdCBmb3VuZCBpbiBXaW5kb3dzIGZvbnQgZGF0YWJhc2UNCiMgTmVlZGVkIHRvIHVwZGF0ZSB0aGUgZmFtaWx5IG5hbWVzDQojIEFkZGVkIGhqdXN0IHRvIGxlZnQtYWxpZ24gdGl0bGUNCnA0ICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsDQogICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHJlbCgyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5laGVpZ2h0ID0gLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gInNlcmlmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQuaXRhbGljIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJvcmFuZ2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMCksDQogICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSByZWwoMS4xKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIm1vbm8iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJwdXJwbGUiKSwNCiAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5OTAiKSwNCiAgICAgICAgICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KDQpgYGANCg0KIyMjIFVzZSBUaGVtZSBFbGVtZW50cyBpbiBhIFN1YnN0YW50aXZlIFdheQ0KDQpUaGUgYHRoZW1lKClgIGluIGBnZ3Bsb3RgIGNhbiBiZSB1c2VkIHRvIGZpeCBkZXNpZ24gZWxlbWVudHMgYW5kIGNyZWF0ZSBjdXN0b20gZmlndXJlcy4NCg0KYGBge3IgdGhlbWUtc3Vic3RhbnRpdmV9DQoNCiMgRGF0YXNldDogYWdlIG9mIGVhY2ggR1NTIHJlc3BvbmRlbnQgZm9yIGFsbCB5ZWFycyBzaW5jZSAxOTcyDQoNCiMgQWdncmVnYXRpb24gLSBtZWFuIGFnZSBvZiByZXNwb25kZW50cyBmb3IgeWVhcnMgb2YgaW50ZXJlc3QNCnRhYmxlKGdzc19sb24keWVhcikNCg0KeXJzIDwtIGMoc2VxKDE5NzIsIDE5ODgsIDQpLCAjIHN1cnZleSBvY2N1cnJlZCBldmVyeSA0IHllYXJzLCAxOTcyIC0gMTk4OA0KICAgICAgICAgMTk5MywgIyBzdXJ2ZXkgb2NjdXJyZWQgaW4gMTk5Mw0KICAgICAgICAgc2VxKDE5OTYsIDIwMTYsIDQpICMgc3VydmV5IG9jY3VycmVkIGV2ZXJ5IDQgeWVhcnMsIDE5OTYgLSAyMDE2DQogICAgICAgICApIA0KDQojIENvbXB1dGUgbWVhbiBhZ2UNCm1lYW5fYWdlIDwtIGdzc19sb24gJT4lDQogIGZpbHRlcihhZ2UgJW5pbiUgTkEgJiYgeWVhciAlaW4lIHlycykgJT4lDQogIGdyb3VwX2J5KHllYXIpICU+JQ0KICBzdW1tYXJpc2UoeGJhciA9IHJvdW5kKG1lYW4oYWdlLCBuYS5ybSA9IFRSVUUpLCAwKSkNCg0KIyBBZGQgeS1heGlzIGNvb3JkaW5hdGUgZm9yIHRleHQgbGFiZWwNCm1lYW5fYWdlJHkgPC0gMC4zDQoNCiMgQ3JlYXRlIGxvY2F0aW9uIGFuZCBsYWJlbCBmb3IgeWVhcnMNCnlyX2xhYnMgPC0gZGF0YS5mcmFtZSh4ID0gODUsDQogICAgICAgICAgICAgICAgICAgICAgeSA9IDAuOCwNCiAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0geXJzKQ0KDQojIE1ha2UgcGxvdA0KcCA8LSBnZ3Bsb3QoZGF0YSA9IHN1YnNldChnc3NfbG9uLCB5ZWFyICVpbiUgeXJzKSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGFnZSkpDQoNCnAxIDwtIHAgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiZ3JheTIwIiwgDQogICAgICAgICAgICAgICBjb2xvciA9IEZBTFNFLA0KICAgICAgICAgICAgICAgYWxwaGEgPSAwLjksIA0KICAgICAgICAgICAgICAgbWFwcGluZyA9IGFlcyh5ID0gLi5zY2FsZWQuLikpICsNCiAgZ2VvbV92bGluZShkYXRhID0gc3Vic2V0KG1lYW5fYWdlLCB5ZWFyICVpbiUgeXJzKSwNCiAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHhpbnRlcmNlcHQgPSB4YmFyKSwgDQogICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLCANCiAgICAgICAgICAgICBzaXplID0gMC41KSArDQogIGdlb21fdGV4dChkYXRhID0gc3Vic2V0KG1lYW5fYWdlLCB5ZWFyICVpbiUgeXJzKSwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHhiYXIsIHkgPSB5LCBsYWJlbCA9IHhiYXIpLCANCiAgICAgICAgICAgIG51ZGdlX3ggPSA3LjUsDQogICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsIA0KICAgICAgICAgICAgc2l6ZSA9IDMuNSwgDQogICAgICAgICAgICBoanVzdCA9IDEpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBzdWJzZXQoeXJfbGFicywgeWVhciAlaW4lIHlycyksDQogICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSwgbGFiZWwgPSB5ZWFyKSkgKw0KICBmYWNldF9ncmlkKHllYXIgfiAuLCANCiAgICAgICAgICAgICBzd2l0Y2ggPSAieSIpICMgcHV0IHRoZSB5ZWFyIGxhYmVscyBmb3IgdGhlIGZhY2V0cyBvbiB0aGUgbGVmdA0KDQpwMSANCg0KIyBBZGQgdGhlbWUNCiMgUmVtb3ZlIHRoZW1lX2Jvb2soKQ0KIyBwMSArIHRoZW1lX2Jvb2soYmFzZV9zaXplID0gMTAsICANCiMgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGVfc2l6ZSA9IDEwLA0KIyAgICAgICAgICAgICAgICAgc3RyaXBfdGV4dF9zaXplID0gMzIsIA0KIyAgICAgICAgICAgICAgICAgcGFuZWxfc3BhY2luZyA9IHVuaXQoMC4xLCAibGluZXMiKSkgKw0KcDEgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksDQogICAgICAgIGF4aXMudGV4dC54PSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgbGFicyh4ID0gIkFnZSIsDQogICAgICAgeSA9IE5VTEwsDQogICAgICAgdGl0bGUgPSAiQWdlIERpc3RyaWJ1dGlvbiBvZlxuR1NTIFJlc3BvbmRlbnRzIikNCg0KDQojIE1ha2UgdGhpcyBwbG90IHdpdGggZ2dyaWRnZXMNCmxpYnJhcnkoZ2dyaWRnZXMpDQoNCiMgUGxvdCBhbGwgeWVhcnM7IG9yZGVyZWQgYnkgeWVhciAoZmFjdG9yKCkpDQpwIDwtIGdncGxvdChkYXRhID0gZ3NzX2xvbiwNCiAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGFnZSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBmYWN0b3IoeWVhciwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gcmV2KHVuaXF1ZSh5ZWFyKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUpKSkNCg0KcCArIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAwLjYsIA0KICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gMS41KSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMjUsIDUwLCA3NSkpICsNCiAgICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMC4wMSwgMCkpICsgDQogICAgbGFicyh4ID0gIkFnZSIsDQogICAgICAgICB5ID0gTlVMTCwNCiAgICAgICAgIHRpdGxlID0gIkFnZSBEaXN0cmlidXRpb24gb2ZcbkdTUyBSZXNwb25kZW50cyIpICsNCiAgICB0aGVtZV9yaWRnZXMoKSArDQogICAgdGhlbWUodGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSkNCg0KYGBgDQoNCiMjIyBDYXNlIFN0dWRpZXMNCldpbGwgYmUgY292ZXJlZCBpbiBXZWVrIDExDQoNCiMjIyBXaGVyZSB0byBHbyBOZXh0ICANClBpY2sgYXQgbGVhc3QgdHdvIG9mIHRoZSBxdWVzdGlvbnMgcHJlc2VudGVkIHVuZGVyIHRoZSAqV2hlcmUgdG8gR28gTmV4dCogc2VjdGlvbiBhbmQgYW5zd2VyIHRoZW0uDQoNCiMjIEhlYXQgTWFwcw0KDQpUaGlzIHNlY3Rpb24gaXMgbm90IGluIHRoZSBib29rIGFzIGlzIHRha2VuIGZyb20gW1RpbWUgQmFzZWQgSGVhdG1hcHMgaW4gUl0oaHR0cHM6Ly93d3cubGl0dGxlbWlzc2RhdGEuY29tL2Jsb2cvaGVhdG1hcHMpLiBgZ2VvbV90aWxlKClgIGNhbiBiZSB1c2VkIHRvIGNyZWF0ZSBoZWF0IG1hcHMuDQoNCmBgYHtyIGhlYXRtYXBzfQ0KDQojIERhdGEgLSA5MTEgY2FsbHMgaW4gU2VhdHRsZQ0KaW5jaWRlbnRzIDwtIHJlYWRfY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbGdlbGxpcy9NaXNjVHV0b3JpYWwvbWFzdGVyL2dnbWFwL2kyU2FtcGxlLmNzdiIpIA0KDQojIEFzc2lnbiBjb2xvciB2YXJpYWJsZXMNCmNvbDEgPSAiI2Q4ZTFjZiIgDQpjb2wyID0gIiM0Mzg0ODQiDQoNCiMgUGVlayBhdCB0aGUgZGF0YSBzZXQgDQpoZWFkKGluY2lkZW50cykNCnN0cihpbmNpZGVudHMpDQoNCiMgUHJlcGFyZSBkYXRhIC0gZ2V0IG1vbnRoL3llYXIvZGF5L2V0Yy4gdXNpbmcgbHVicmlkYXRlIHBhY2thZ2UNCmluY2lkZW50cyR5bWQgPC1sdWJyaWRhdGU6Om1keV9obXMoaW5jaWRlbnRzJEV2ZW50LkNsZWFyYW5jZS5EYXRlKQ0KaW5jaWRlbnRzJG1vbnRoIDwtIGx1YnJpZGF0ZTo6bW9udGgoaW5jaWRlbnRzJHltZCwgbGFiZWwgPSBUUlVFKQ0KaW5jaWRlbnRzJHllYXIgPC0gbHVicmlkYXRlOjp5ZWFyKGluY2lkZW50cyR5bWQpDQppbmNpZGVudHMkd2RheSA8LSBsdWJyaWRhdGU6OndkYXkoaW5jaWRlbnRzJHltZCwgbGFiZWwgPSBUUlVFKQ0KaW5jaWRlbnRzJGhvdXIgPC0gbHVicmlkYXRlOjpob3VyKGluY2lkZW50cyR5bWQpDQoNCiMgQ3JlYXRlIGEgc3VtbWFyeSB0YWJsZSB0aGF0IGhhcyB0aGUgbnVtYmVyIG9mIGluY2lkZW50cyBieSBob3VyIGFuZCBkYXkNCmRheUhvdXIgPC0gaW5jaWRlbnRzICU+JQ0KICBncm91cF9ieShob3VyLCB3ZGF5KSAlPiUNCiAgc3VtbWFyaXNlKE4gPSBuKCkpDQoNCiMgT3JkZXIgYnkgd2Vla2RheSBpbiByZXZlcnNlDQpkYXlIb3VyJHdkYXkgPC0gZmFjdG9yKGRheUhvdXIkd2RheSwgbGV2ZWxzID0gcmV2KGxldmVscyhkYXlIb3VyJHdkYXkpKSkNCg0KIyBSZW1vdmUgTkFzDQpkYXlIb3VyIDwtIGRheUhvdXIgJT4lIG5hLm9taXQoKQ0KDQojIFVzZSBnZW9tX3RpbGUoKSB0byBjcmVhdGUgYSBoZWF0IG1hcA0KZ2dwbG90KGRhdGEgPSBkYXlIb3VyLCANCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBob3VyLCANCiAgICAgICAgICAgICAgICAgICAgIHkgPSB3ZGF5KSkgKyANCiAgZ2VvbV90aWxlKGFlcyhmaWxsID0gTiksIA0KICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIikgKyAjIHdoaXRlIG91dGxpbmUNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSBjb2wxLCANCiAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gY29sMikgKyAgDQogIHRoZW1lX21pbmltYWwoKSArIA0KICBsYWJzKHRpdGxlID0gIkhlYXQgTWFwIG9mIFNlYXR0bGUgSW5jaWRlbnRzIGJ5IERheSBvZiBXZWVrIGFuZCBIb3VyIiwNCiAgICAgICB4ID0gIkluY2lkZW50cyBQZXIgSG91ciIsIA0KICAgICAgIHkgPSAiRGF5IG9mIFdlZWsiLA0KICAgICAgIGZpbGwgPSAiVG90YWwgSW5jaWRlbnRzIikgKw0KICAjIFJlbW92ZSB0aGUgZ3JpZGxpbmVzDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQ0KDQojIENyZWF0ZSBzdW1tYXJ5IHRhYmxlIGZvciB0aGUgbnVtYmVyIG9mIGluY2lkZW50cyBieSBncm91cCBhbmQgaG91cg0KZ3JvdXBTdW1tYXJ5IDwtIGluY2lkZW50cyAlPiUNCiAgZ3JvdXBfYnkoRXZlbnQuQ2xlYXJhbmNlLkdyb3VwLCBob3VyKSAlPiUNCiAgc3VtbWFyaXNlKE4gPSBuKCkpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEV2ZW50LkNsZWFyYW5jZS5Hcm91cCkpICMgcmVtb3ZlIE5BIGV2ZW50DQoNCiMgTWFrZSBoZWF0IG1hcA0KZ2dwbG90KGRhdGEgPSBncm91cFN1bW1hcnksIA0KICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGhvdXIsDQogICAgICAgICAgICAgICAgICAgICB5ID0gRXZlbnQuQ2xlYXJhbmNlLkdyb3VwKSkgKyANCiAgZ2VvbV90aWxlKGFlcyhmaWxsID0gTiksIA0KICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIikgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9IGNvbDEsIA0KICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSBjb2wyKSArICANCiAgbGFicyh0aXRsZSA9ICJIZWF0IE1hcCBvZiBTZWF0dGxlIEluY2lkZW50cyBieSBFdmVudCBhbmQgSG91ciIsDQogICAgICAgeCA9ICJIb3VyIiwgDQogICAgICAgeSA9ICJFdmVudCIsDQogICAgICAgZmlsbCA9ICJUb3RhbCBJbmNpZGVudHMiKSArDQogIHRoZW1lX21pbmltYWwoKSArIA0KICAjIFJlbW92ZSBncmlkbGluZXMNCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpDQoNCg0KYGBgDQoNCg==