Our goal in this module is to understand some basic ways of visualizing data with graphics – bar-charts, histograms, box-plots, and so on. We will skip base R commands and instead just work with ggplot2, the most popular visualization package in the {tidyverse} universe as of now.

If you remember MPA 6010, recall the usual options…

  • one qualitative/categorical variables: bar-chart
  • one quantitative/continuous variables: histogram/box-plot/area-chart
  • two quantitative/continuous variables: scatter-plot/hex-bin

We can then ratchet up as we need to.

I will use two data-sets to walk through the initial examples in this module, the first being this IMDB data-set

The internet movie database, http://imdb.com/, is a website devoted to collecting movie data supplied by studios and fans. It claims to be the biggest movie database on the web and is run by amazon. More about information imdb.com can be found online, http://imdb.com/help/show_ leaf?about, including information about the data collection process, http://imdb.com/help/show_leaf?infosource.

library(ggplot2movies)
data(movies)
names(movies)
##  [1] "title"       "year"        "length"      "budget"      "rating"     
##  [6] "votes"       "r1"          "r2"          "r3"          "r4"         
## [11] "r5"          "r6"          "r7"          "r8"          "r9"         
## [16] "r10"         "mpaa"        "Action"      "Animation"   "Comedy"     
## [21] "Drama"       "Documentary" "Romance"     "Short"

A data frame with 28819 rows and 24 variables

Variable Description
title Title of the movie
year Year of release
budget Total budget (if known) in US dollars
length Length in minutes
rating Average IMDB user rating
votes Number of IMDB users who rated this movie
r1-10 Multiplying by ten gives percentile (to nearest 10%) of users who rated this movie a 1
mpaa MPAA rating (missing for a lot of movies)
action, animation, comedy, drama, documentary, romance, short Binary variables representing if movie was classified as belonging to that genre

The second data-set is the Star Wars dataset, a tibble with 87 rows and 13 variables:

library(tidyverse)
data(starwars)
names(starwars)
##  [1] "name"       "height"     "mass"       "hair_color" "skin_color"
##  [6] "eye_color"  "birth_year" "gender"     "homeworld"  "species"   
## [11] "films"      "vehicles"   "starships"
# str(starwars)
Variable Description
name Name of the character
height Height (cm)
mass Weight (kg)
hair_color, skin_color, eye_color Hair, skin, and eye colors
birth_year Year born (BBY = Before Battle of Yavin)
gender male, female, hermaphrodite, or none
homeworld Name of homeworld
species Name of species
films List of films the character appeared in
vehicles List of vehicles the character has piloted
starships List of starships the character has piloted

1 ggplot2 and the grammar of graphics

The {ggplot2} package has a special syntax and I will point out things you should note as we move through this module. First up, the library is called ggplot2 but the command starts with ggplot so don’t let that throw you off-track.

Second, you need to have a data-set to work with. In the code below I start by loading the library and then specifying the data-set to be used.

library(ggplot2)
ggplot(
  data = starwars
  )

Nothing results from these commands because we have not yet specified anything about what should go on the x-axis, what should go on the y-axis. Well, let us do that then by asking for the column eye_color to be put on the x-axis.

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color
    )
  ) 

1.1 geom_bar() … the bar-chart

This results in a gray canvas with the eye colors on the x-axis but nothing else has been drawn since we have not specified the geometry … do you want a bar-chart? histogram? dot-plot? line-chart? This is a categorical variable and hence a bar-chart would be appropriate. We call for a bar-chart with the geom_bar() command.

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color
    )
  ) +
  geom_bar()

1.2 color versus fill

The aes() refers to the aesthetics of the chart, and many other aesthetics can be added, such as group, color, fill, size, alpha, etc. We will see some of these in due course but for now I want to focus on two of these, both involving coloring of the geom_. Specifically, there are two commands for adding colors – (1) color or colour, and (2) fill – to a chart.

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color,
    color = eye_color
    )
  ) +
  geom_bar()

Note what the color = eye_color command did … it drew a colored border for the bars, and an accompanying legend. What if we had used fill = eye_color instead?

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color,
    fill = eye_color
    )
  ) +
  geom_bar()

Aha! Now the bars are filled with colors and an accompanying legend is drawn as well. So fill = and color = behave very differently, bear this in mind.

1.3 Adding labels with labs

One of the nice things about this software environment is that there are plenty of coloring schemes available to us and we will play with some of these shortly, but before we do that, let us look at one more improvement – adding titles, subtitles, captions, and axis labels to our chart. This is done with the labs = () command.

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color,
    fill = eye_color
    )
  ) +
  geom_bar() +
  labs(
    x = "Eye Color",
    y = "Frequency (n)",
    title = "Bar-chart of Eye Colors",
    subtitle = "(of Star Wars characters)",
    caption = "My little work of art!!"
    )

Notice the text that now appears as a result of what has been specified in the labs() command.

1.4 Controlling the chart legend with theme()

In this bar-chart, do we really need the legend? No, because the colors and color names show up in the chart itself. How can we hide the legend? Turns out there is a neat command that will allow you to move the legend around and even to hide it.

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color,
    fill = eye_color
    )
  ) +
  geom_bar() +
  labs(
    x = "Eye Color",
    y = "Frequency (n)",
    title = "Bar-chart of Eye Colors",
    subtitle = "(of Star Wars characters)",
    caption = "My little work of art!!"
    ) +
  theme(legend.position = "none")

Voila! The legend is gone. Instead of β€œnone” you could have specified β€œbottom”, β€œleft”, β€œtop”, β€œright” to place the legend in a particular direction.

1.5 Customizing colors

Of course, it would be good to have the colors match the eye-color so let us do that next. The way we can do this is by calling specific colors by name. I have tried to order the lineup of the colors to match, as closely as I can, the eye colors.

c(
  "black", "blue", "slategray", "brown", "gray34", "gold",
  "greenyellow", "navajowhite1", "orange", "pink", "red",
  "magenta", "thistle3", "white", "yellow"
  ) -> mycolors

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color
    )
  ) + 
  geom_bar(
    fill = mycolors
    ) + 
  labs(
    x = "Eye Color",
    y = "Frequency (n)",
    title = "Bar-chart of Eye Colors",
    subtitle = "(of Star Wars characters)",
    caption = "My little work of art!!"
    ) +
  theme(legend.position = "none")

These colors are from this source but see also this source. Colors can be customized by generating your own palettes via the Color Brewer here. But don’t get carried away: Remember to read the materials on choosing colors wisely, particularly the point about qualitative palettes, divergent palettes, and then palettes that work well even with colorblind audiences.

1.6 Selected color palettes

I had mentioned the existence of a number of color palettes so let us look at a few, but we will do this with a different variable. First up, the Pastel1 palette.

ggplot(
  data = starwars,
  mapping = aes(
    x = gender
    )
  ) + 
  geom_bar(
    aes(fill = gender)
    ) + 
  labs(
    x = "Gender",
    y = "Frequency",
    title = "Bar-chart of Gender",
    subttitle = "(of Star Wars characters)",
    caption = "(Source: The dplyr package)") +
  scale_fill_brewer(
    palette = "Pastel1"
    )

Not bad but doesn’t work too well here. How about trying another palette, Set?

ggplot(
  data = starwars,
  mapping = aes(
    x = gender
    )
  ) + 
  geom_bar(
    aes(fill = gender)
    ) + 
  labs(
    x = "Gender",
    y = "Frequency",
    title = "Bar-chart of Gender",
    subttitle = "(of Star Wars characters)",
    caption = "(Source: The dplyr package)") +
  scale_fill_brewer(
    palette = "Set1"
    )

Nice! But what is also noticeable here is that there are some characters in the data-set whose gender data is missing. These are the NA values. By default, you will see NA values showing up in some types of charts and so it is always good to exclude them from the chart. Here is one way of doing that.

ggplot(
  data = subset(starwars, !is.na(gender)),
  mapping = aes(
    x = gender
    )
  ) + 
  geom_bar(
    aes(fill = gender)
    ) + 
  labs(
    x = "Gender",
    y = "Frequency",
    title = "Bar-chart of Gender",
    subttitle = "(of Star Wars characters)",
    caption = "(Source: The dplyr package)") +
  scale_fill_brewer(
    palette = "Set1"
    )

Notice what is different here: data = subset(starwars, !is.na(gender)) and that this command is effectively saying subset the starwars data to only include those cases where gender is not missing (this is the !is.na() portion of the command).

Another way to do the same thing would have been to use filter() and create a cleaned up copy of the data. If you take this route, be careful not to overwrite the original data-set; note how I am giving a new name (my.data) after filter() to save the results in. Then we lean on this data-set via data = my.data.

starwars %>%
  filter(!is.na(gender)) -> my.data

ggplot(
  data = my.data,
  mapping = aes(
    x = gender
    )
  ) + 
  geom_bar(
    aes(fill = gender)
    ) + 
  labs(
    x = "Gender",
    y = "Frequency",
    title = "Bar-chart of Gender",
    subttitle = "(of Star Wars characters)",
    caption = "(Source: The dplyr package)") +
  scale_fill_brewer(
    palette = "Set1"
    )

There is one color palette you should remember, and this is the {viridis} color scheme that works around varying types of color blindness in the population. Here come the palettes:

ggplot(
  data = my.data,
  mapping = aes(
    x = gender
    )
  ) + 
  geom_bar(
    aes(fill = gender)
    ) + 
  labs(
    x = "Gender",
    y = "Frequency",
    title = "Bar-chart of Gender",
    subttitle = "(of Star Wars characters)",
    caption = "(Source: The dplyr package)") +
  scale_fill_viridis_d(
    option = "viridis"
    )

ggplot(
  data = my.data,
  mapping = aes(
    x = gender
    )
  ) + 
  geom_bar(
    aes(fill = gender)
    ) + 
  labs(
    x = "Gender",
    y = "Frequency",
    title = "Bar-chart of Gender",
    subttitle = "(of Star Wars characters)",
    caption = "(Source: The dplyr package)") +
  scale_fill_viridis_d(
    option = "magma"
    )

ggplot(
  data = my.data,
  mapping = aes(
    x = gender
    )
  ) + 
  geom_bar(
    aes(fill = gender)
    ) + 
  labs(
    x = "Gender",
    y = "Frequency",
    title = "Bar-chart of Gender",
    subttitle = "(of Star Wars characters)",
    caption = "(Source: The dplyr package)") +
  scale_fill_viridis_d(
    option = "plasma"
    )

ggplot(
  data = my.data,
  mapping = aes(
    x = gender
    )
  ) + 
  geom_bar(
    aes(fill = gender)
    ) + 
  labs(
    x = "Gender",
    y = "Frequency",
    title = "Bar-chart of Gender",
    subttitle = "(of Star Wars characters)",
    caption = "(Source: The dplyr package)") +
  scale_fill_viridis_d(
    option = "cividis"
    )

1.7 Themes with {ggthemes}

One can also lean on various plotting themes as shown below. These themes mimic the style of graphics popularized by some data visualization experts (for e.g., Stephen Few, Edward Tufte), news-media houses (Fivethirtyeight, The Economist, The Wall Street Journal), some software packages (Excel, Stata, Google docs), and a few others. Below I show you just a handful.

library(ggthemes)

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color
    )
  ) +
  geom_bar() +
  theme_tufte() 

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color
    )
  ) +
  geom_bar() +
  theme_economist() 

ggplot(
  data = starwars,
  mapping = aes(
    x = eye_color
    )
  ) +
  geom_bar() +
  theme_fivethirtyeight() 

Later on you will learn these & other ways to build advanced visualizations. For now we get to work more with ggplot2.

1.8 More with bar-charts

I want to show a few things with bar-charts now. First, we can specify things a bit differently without altering the result. For example, compare the following two pieces of code.

ggplot(
  data = movies,
  mapping = aes(
    x = mpaa
    )
  ) + 
  geom_bar()

ggplot() + 
  geom_bar(
    data = movies,
    mapping = aes(x = mpaa)
    )

Notice that we switched the data = and the aes() pieces of the code but that made no difference; this is important to bear in mind because it will come in handy down the road when we need to build some advanced visualizations.

The plot is sub-optimal since MPAA ratings are missing for a lot of movies and should be eliminated from the plot via subset(mpa != "") or by running dplyr’s filter() to create another data-set. I will lean on filter().

movies %>% 
  filter(mpaa != "") -> movies2 

ggplot() + 
  geom_bar(
    data = movies2,
    mapping = aes(x = mpaa)
    )

The order of the bars here is fortuitous in that it goes from the smallest frequency to the highest frequency, drawing the reader’s eye. I said fortuitous because {ggplot2} defaults to drawing the bars in an ascending alphabetic/alphanumeric order if the variable is a character. See below for an example.

df = tibble(x = c(rep("A", 2), rep("B", 4), rep("C", 1)))

ggplot() + 
  geom_bar(
    data = df, 
    mapping = aes(x = x)
  )

Notice the bars here do not follow in ascending/descending order of frequencies. Later on we’ll learn how to order the bars with ascending/descending frequencies or by some other logic.

What about plotting relative frequencies on the y-axis rather than the frequencies?

library(scales)

ggplot() + 
  geom_bar(
    data = movies2,
    mapping = aes(
      x = mpaa,
      y = (..count..)/sum(..count..)
      )
    ) + 
  scale_y_continuous(labels = percent) + 
  labs(
    x = "MPAA Rating",
    y  = "Relative Frequency (%)"
    ) 

Note the addition of

  • y = (..count..)/sum(..count..) to change the y-axis to reflect the relative frequency as a proportion, and
  • scale_y_continuous(labels = percent) to then multiply these proportions by 100 to get percentages as the labels rather than 0.2, 0.4, 0.6, etc.

1.9 Disaggregating bar-charts for groups

Let us build a simple bar-chart with the hsb2 data we saw in Module 01. Here we first download it, label the values, save it, and then start charting it.

read.table(
  'https://stats.idre.ucla.edu/stat/data/hsb2.csv',
  header = TRUE,
  sep = ","
  ) -> hsb2

factor(hsb2$female,
       levels = c(0, 1),
       labels = c("Male", "Female")
       ) -> hsb2$female 

factor(hsb2$race,
       levels = c(1:4),
       labels = c("Hispanic", "Asian", "African American", "White")
       ) -> hsb2$race

factor(hsb2$ses,
       levels = c(1:3),
       labels = c("Low", "Middle", "High")
       ) -> hsb2$ses

factor(hsb2$schtyp,
       levels = c(1:2),
       labels = c("Public", "Private")
       ) -> hsb2$schtyp

factor(hsb2$prog,
       levels = c(1:3),
       labels = c("General", "Academic", "Vocational")
       ) -> hsb2$prog

save(
  hsb2, file = here::here("data", "hsb2.RData")
  )
ggplot() + 
  geom_bar(
    data = hsb2,
    mapping = aes(x = ses)) +
  labs(x = "Socioeconomic Status")

Okay, fair enough. But what if I wanted to see how socioeconomic status varies across male and female students?

ggplot() + 
  geom_bar(
    data = hsb2,
    mapping = aes(
      x = ses,
      group = female, 
      fill = female
      )
    ) +
  labs(
    x = "Socioeconomic Status",
    y = "Frequency"
  )

This is not very useful since the viewer has to estimate the relative sizes of the two colors within any given bar. That can be fixed with position = "dodge", juxtaposing the bars for the groups as a result, and the end product is much better. But note: position = "dodge" has to be put outside the aes() but still inside geom_bar() so be careful.

ggplot() + 
  geom_bar(
    data = hsb2,
    mapping = aes(
      x = ses,
      group = female, 
      fill = female
      ),
    position = "dodge"
    ) +
  labs(
    x = "Socioeconomic Status",
    y = "Frequency"
  )

What if you wanted to calculate percentages within each sex? That is, what percent of male students fall within a particular ses category, and the same thing for female students?

ggplot() + 
  geom_bar(
    data = hsb2, 
    aes(
      x = ses, 
      group = female,
      fill = female, 
      y = ..prop..
      ),
    position = "dodge") +
  scale_y_continuous(labels = scales::percent) + 
  labs(
    x = "Socioeconomic Status",
    y = "Relative Frequency (%)"
    )

What about within each ses instead of within gender? That is, what if we wanted percent of Low ses that is Male versus Female, and so on?

ggplot() + 
  geom_bar(
    data = hsb2, 
    aes(
      x = female, 
      group = ses,
      fill = ses, 
      y = ..prop..
      ),
    position = "dodge") +
  scale_y_continuous(labels = scales::percent) + 
  labs(
    x = "Socioeconomic Status",
    y = "Relative Frequency (%)"
    )

ggplot() + 
  geom_bar(
    data = hsb2, 
    aes(
      x = female, 
      group = ses,
      fill = ses, 
      y = ..prop..
      ),
    position = "dodge") +
  scale_y_continuous(labels = scales::percent) + 
  labs(
    x = "Socioeconomic Status",
    y = "Relative Frequency (%)"
    ) 

There is some more we will do with bar-charts but for now let us set them aside and instead look at a few other charts – histograms, box-plots, and line-charts.

1.10 Histograms

If you’ve forgotten what these are, see histogram, or then Yau’s piece here and here. There is a short video available as well.

For histograms in ggplot2, geom_histogram() is the geometry needed but note that the default number of bins is not very useful and can be tweaked, along with other embellishments that are possible as well.

ggplot() + 
  geom_histogram(
    data = hsb2,
    aes(x = read), 
    fill = "cornflowerblue",
    color = "white"
    ) + 
  labs(
    title = "Histogram of Reading Scores",
    x = "Reading Score",
    y = "Frequency"
    )

Note the warning stat_bin() using bins = 30. Pick better value with binwidth. This is because numerical variables need to be grouped in order to have meaningful histograms we can make sense of. How do you define the bins (aka the groups)? We could set bins = 5 and we could also experiment with binwidth =. Let us do bins = 5 which will say give us 5 groups, and go ahead and calculate them yourself.

ggplot() + 
  geom_histogram(
    data = hsb2,
    aes(x = read), 
    fill = "cornflowerblue",
    color = "white",
    bins = 5
    ) + 
  labs(
    title = "Histogram of Reading Scores",
    subtitle = "(with bins = )",
    x = "Reading Score",
    y = "Frequency"
    )

If we wanted more/fewer bins we could tweak it up or down as needed. What about binwidth? This will specify how wide each group must be.

ggplot() + 
  geom_histogram(
    data = hsb2,
    aes(x = read), 
    fill = "cornflowerblue",
    color = "white",
    binwidth = 5
    ) + 
  labs(
    title = "Histogram of Reading Scores",
    subtitle = "(with binwidth = )",
    x = "Reading Score",
    y = "Frequency"
    )

If we wanted to disaggregate the histogram by one or more categorical variables, we could do so quite easily:

ggplot() + 
  geom_histogram(
    data = hsb2,
    aes(x = read), 
    fill = "cornflowerblue",
    color = "white",
    bins = 5
    ) + 
  labs(
    title = "Histogram of Reading Scores",
    subtitle = "(broken out for Male vs. Female students)",
    x = "Reading Score",
    y = "Frequency"
    ) +
  facet_wrap(~ female)

When we do this, it is often useful to organize them so that only one histogram shows up in a row. This is done with the ncol = 1 command.

ggplot() + 
  geom_histogram(
    data = hsb2,
    aes(x = read), 
    fill = "cornflowerblue",
    color = "white",
    bins = 5
    ) + 
  labs(
    title = "Histogram of Reading Scores",
    subtitle = "(broken out for Male vs. Female students)",
    x = "Reading Score",
    y = "Frequency"
    ) +
  facet_wrap(~ female, ncol = 1)

ggplot() + 
  geom_histogram(
    data = hsb2,
    aes(x = read), 
    fill = "cornflowerblue",
    color = "white",
    bins = 5
    ) + 
  labs(
    title = "Histogram of Reading Scores",
    subtitle = "(broken out by Socioeconomic Status)",
    x = "Reading Score",
    y = "Frequency"
    ) +
  facet_wrap(~ ses, ncol = 1)

Now the distributions are stacked above each, easing comparisons; do they have the same average? Do they vary the same? Are they similarly skewed/symmetric?.

For breakouts with two categorical variables we could do

ggplot() + 
  geom_histogram(
    data = hsb2,
    aes(x = read), 
    fill = "cornflowerblue",
    color = "white",
    bins = 5
    ) + 
  labs(
    title = "Histogram of Reading Scores",
    subtitle = "(broken out by Socioeconomic Status and School Type)",
    x = "Reading Score",
    y = "Frequency"
    ) +
  facet_wrap(ses ~ schtyp, ncol = 2)

Note that ses ~ schtyp renders the panels for the first category of ses by all categories of schtyp and then repeats for the other categories in rows 2 and 3. If we did facet_wrap(schtype ~ ses, ncol = 3) we would have a different result:

ggplot() + 
  geom_histogram(
    data = hsb2,
    aes(x = read), 
    fill = "cornflowerblue",
    color = "white",
    bins = 5
    ) + 
  labs(
    title = "Histogram of Reading Scores",
    subtitle = "(broken out by Socioeconomic Status and School Type)",
    x = "Reading Score",
    y = "Frequency"
    ) +
  facet_wrap(schtyp ~ ses, ncol = 3) +
  ylim(c(0, 23))

Notice that here I also add a ylim(c(...)) command to set the minimum and maximum values of the y-axis. This is useful, and I suggest you do not forget to set the y limit to start at 0 or then make a note in the plot for readers so they don’t assume it is at 0 when in fact it has been truncated for ease of data presentation. This misstates the pattern in the data, do not do it or then, again, annotate the plot to that effect so nobody is misled. Bar-charts and histograms will have 0 as the minimum y-limit but this is not true for some other plots.

1.11 Box-plots

Remember these, our friends from MPA 6010? These can be useful for studying the distribution of a continuous variable. See this video. Let us see these in action with the cmhflights data.

load(
  here::here("data", "cmhflights_01092017.RData")
  )

ggplot() + 
  geom_boxplot(
    data = cmhflights,
    mapping = aes(
      y = ArrDelay,
      x = ""
      ),
    fill = "cornflowerblue"
    ) 

Note:

  • the x = "" is in aes() because otherwise with a single group the box-plot will not build up nicely

But, I prefer to see them running horizontally, so how can I do that? With coord_flip() since this just flips the columns.

ggplot() + 
  geom_boxplot(
    data = cmhflights,
    mapping = aes(
      y = ArrDelay,
      x = ""
      ),
    fill = "cornflowerblue"
    ) +
  coord_flip()

And now for a slightly different data-set, one that measures male adults’ hemoglobin concentration for a few populations.

read_csv(
  "http://whitlockschluter.zoology.ubc.ca/wp-content/data/chapter02/chap02e3cHumanHemoglobinElevation.csv"
  ) -> hemoglobin

ggplot() +
  geom_boxplot(
    data = hemoglobin,
    mapping = aes(
      x = population,
      y = hemoglobin,
      fill = population
      )
    ) +
  coord_flip() +
  labs(
    x = "Population",
    y = "Hemoglobin Concentration",
    title = "Hemoglobin Concentration in Adult Males",
    subtitle = "(Andes, Ethiopia, Tibet, USA)"
    ) +
  theme(legend.position = "none")

Notice the need for no legend with fill = population Notice also how fill = is inside aes(...) here because we are asking that each unique value seen in a variable called population be mapped to a unique color.

Could we use our facet_wrap(...) here too? Of course.

ggplot() + 
  geom_boxplot(
    data = cmhflights,
    mapping = aes(
      y = ArrDelay,
      x = Carrier
      ),
    fill = "cornflowerblue"
    ) +
  coord_flip() +
  facet_wrap(~ Month)

1.12 Line-charts

If we have data over time for one or more units, then line-charts work really well to exhibit trends. A classic, current example would be the number of confirmed COVID-19 cases per country per date. For example, say we have data on the unemployment rate for the country. These data are coming from the {plotly} library so we have to make sure it is installed and load it.

library(plotly)
data(economics)
names(economics)
## [1] "date"     "pce"      "pop"      "psavert"  "uempmed"  "unemploy"
ggplot() +
  geom_line(
    data = economics, 
    mapping = aes(
      x = date,
      y = uempmed
      )
    ) + 
  labs(
    x = "Date",
    y = "Unemployment Rate"
  )

They can look very plain and aesthetically unappealing unless you dress them up. See the one below and then the one that follows.

load(
  here::here("data", "gap.df.RData")
  )

ggplot() +
  geom_line(
    data = gap.df, 
    mapping = aes(
      x = year,
      y = LifeExp,
      group = continent,
      color = continent
      )
    ) + 
  geom_point(
    data = gap.df, 
    mapping = aes(
      x = year,
      y = LifeExp,
      group = continent,
      color = continent
      )
    ) + 
  labs(
    x = "Year",
    y = "Median Life Expectancy (in years)",
    color = ""
  ) + 
  theme(legend.position = "bottom") 

1.13 Scatter-plots

These work well if we have two or more continuous variables, and work well to highlight the nature and strength of a relationship between the two variables …. what happens to y as x increases? s

ggplot() + 
  geom_point(
    data = hsb2, 
    mapping = aes(
      x = write,
      y = science
      )
    ) +
  labs(
    x = "Writing Scores", 
    y = "Science Scores"
    ) 

We could highlight the different ses groups, to see if there is any difference in the relationship between writing scores and science scores by the different ses levels.

ggplot() +
  geom_point(
    data = hsb2,
    mapping = aes(
      x = write,
      y = science,
      color = ses
      )
    ) + 
  labs(
    x = "Writing Scores", 
    y = "Science Scores",
    color = ""
    ) + 
  theme(legend.position = "bottom") 

This is not very helpful so why not breakout ses for ease of interpretation?

ggplot() +
  geom_point(
    data = hsb2,
    mapping = aes(
      x = write,
      y = science
      )
    ) + 
  labs(
    x = "Writing Scores", 
    y = "Science Scores"
    ) + 
  facet_wrap(~ ses) 

Could we add another layer, perhaps female?

ggplot() +
  geom_point(
    data = hsb2,
    mapping = aes(
      x = write,
      y = science
      )
    ) + 
  labs(
    x = "Writing Scores", 
    y = "Science Scores"
    ) + 
  facet_wrap(ses ~ female, ncol = 2) 

And finally, a few suggestion about how to build up your visualizations:

  • πŸ” start with pencil and paper, sketch prototypes of desired visualization(s)
  • πŸ˜„ graphics are relatively easy to generate with base R & with ggplot2
  • πŸ‘ common-sense: number & type of variable(s) guide plotting
  • πŸŽ‡ stay color conscious: sensible colors & sensitive to color blindness
  • πŸ”° experiment, experiment, experiment until you are happy
  • use the πŸ†“ learning resources available online
  • πŸ“’ if you learn something new in R, write it down

2 Practice Exercises

2.1 Nobel Prize Winners

Georgios Karamanis gathered and shared data on Nobel prize winners over the years, with a fair amount of detail, and used in the tidytuesday series a while back. These data are to be used for the questions that follow.

readr::read_csv("https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2019/2019-05-14/nobel_winners.csv") -> nobel_winners 
variable class description
prize_year double Year that Nobel Prize was awarded
category character Field of study/category
prize character Prize Name
motivation character Motivation of the award
prize_share character Share eg 1 of 1, 1 of 2, 1 of 4, etc
laureate_id double ID assigned to each winner
laureate_type character Individual or organization
full_name character name of the winner
birth_date double birth date of winner
birth_city character birth city/state of winner
birth_country character birth country of winner
gender character binary gender of the winner
organization_name character organization name
organization_city character organization city
organization_country character organization country
death_date double death date of the winner (if dead)
death_city character death city (if dead)
death_country character death country (if dead)
  1. First create nobel.df that keeps only records starting in the year 1960, and only for the β€œPhysics” category. Now generate an appropriate chart that shows the distribution of winners by birth_country

  2. Now break this distribution out by gender to see how winners by country differs across gender

  3. Now go back to noble_winners, the full data-set, and create a simple plot that shows the distribution of prize winners by death_country, gender, and category

2.2 Water levels in the Great Lakes

Download the monthly Great Lakes water level data-set SPSS format from here and Excel format from here. Note that water level is in meters.

You may use the following command to read in the excel file:

library(readxl)
url <- "https://aniruhil.github.io/avsr/teaching/dataviz/greatlakes.xlsx"
destfile <- "greatlakes.xlsx"
curl::curl_download(url, destfile)
read_excel(destfile, col_types = c("date", 
     "numeric", "numeric", "numeric", "numeric", 
     "numeric")) -> greatlakes 

Now use an appropriate chart to show the water level for Lake Superior.

2.3 County Health Rankings

Download the 2017 County Health Rankings data SPSS format from here, Excel format from here and the accompanying codebook.

These data can be downloaded with the code provided below:

library(readxl)
url <- "https://aniruhil.github.io/avsr/teaching/dataviz/CountyHealthRankings2017.xlsx"
destfile <- "CountyHealthRankings2017.xlsx"
curl::curl_download(url, destfile)
read_excel(destfile) -> chr.df 

Construct appropriate plots that shows the relationship between the following pairs of variables

  1. Adult obesity and High school graduation

  2. Children in poverty and High school graduation

  3. Preventable hospital stays and Unemployment rate

2.4 Unemployment Rates

Use the unemployment data given to you (unemprate.RData) and construct appropriate plots that show the distribution of unemployment rates across years for each of the four educational attainment groups.

load(
  here::here("data", "unemprate.RData")
  ) -> urate 

Be sure to use a unique color for each educational attainment group

LS0tCnRpdGxlOiAiTVBBIDU4MzAgLSBNb2R1bGUgMDUiCnN1YnRpdGxlOiAiU3ByaW5nIDIwMjAiCmF1dGhvcjogIlByb2Zlc3NvciBSdWhpbCIKZGF0ZTogIlVwZGF0ZWQgb24gYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGhpZ2hsaWdodDogemVuYnVybgogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKYm9keXsgLyogTm9ybWFsICAqLwovKiAgICBmb250LWZhbWlseTogTGF0bywgc2Fucy1zZXJpZjsgIAogICAgICBmb250LWZhbWlseTogTXVrdGEsIHNhbnMtc2VyaWY7IAogICAgICBmb250LWZhbWlseTogJ051bml0byBTYW5zJywgc2Fucy1zZXJpZjsKICAgICAgZm9udC1mYW1pbHk6IEthcmxhLCBzYW5zLXNlcmlmOyAgKi8KICAgICAgZm9udC1mYW1pbHk6ICdNZXJyaXdlYXRoZXIgU2FucycsIHNhbnMtc2VyaWY7IAogICAgICBmb250LXNpemU6IDE4cHg7CiAgfQoKaDEudGl0bGUgewogIGZvbnQtc2l6ZTogMzhweDsKICBjb2xvcjogRGFya1JlZDsKfQoKaDEgeyAvKiBIZWFkZXIgMSAqLwogIGZvbnQtc2l6ZTogMjhweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KCmgyIHsgLyogSGVhZGVyIDIgKi8KICAgIGZvbnQtc2l6ZTogMjJweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KCmgzIHsgLyogSGVhZGVyIDMgKi8KICBmb250LXNpemU6IDE4cHg7CiAgY29sb3I6IERhcmtCbHVlOwp9Cgpjb2RlLnJ7IC8qIENvZGUgYmxvY2sgKi8KICAgIGZvbnQtZmFtaWx5OiBNdWt0YSwgc2Fucy1zZXJpZjsgCiAgICBmb250LXdlaWdodDogNjAwOyAgCiAgICBmb250LXNpemU6IDE2cHg7Cn0KCi8qIHByZSB7IC8qIENvZGUgYmxvY2sgLSBkZXRlcm1pbmVzIGNvZGUgc3BhY2luZyBiZXR3ZWVuIGxpbmVzICovCiAgICBmb250LXNpemU6IDE2cHg7Cn0gKi8KPC9zdHlsZT4KCgpgYGB7ciBrbGlwcHksIGVjaG8gPSBGQUxTRSwgaW5jbHVkZSA9IFRSVUV9CmtsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnLCBjb2xvciA9ICdjb3JuZmxvd2VyYmx1ZScsIHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpCmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGRwaSA9IDMwMCwgY2FjaGUgPSBUUlVFLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDUsIG91dC53aWR0aCA9ICIxMDAlIiwgaGlnaGxpZ2h0ID0gVFJVRSkgCmBgYAoKT3VyIGdvYWwgaW4gdGhpcyBtb2R1bGUgaXMgdG8gdW5kZXJzdGFuZCBzb21lIGJhc2ljIHdheXMgb2YgdmlzdWFsaXppbmcgZGF0YSB3aXRoIGdyYXBoaWNzIC0tIGJhci1jaGFydHMsIGhpc3RvZ3JhbXMsIGJveC1wbG90cywgYW5kIHNvIG9uLiBXZSB3aWxsIHNraXAgYGJhc2UgUmAgY29tbWFuZHMgYW5kIGluc3RlYWQganVzdCB3b3JrIHdpdGggYGdncGxvdDJgLCB0aGUgbW9zdCBwb3B1bGFyIHZpc3VhbGl6YXRpb24gcGFja2FnZSBpbiB0aGUgYHt0aWR5dmVyc2V9YCB1bml2ZXJzZSBhcyBvZiBub3cuIAoKSWYgeW91IHJlbWVtYmVyIE1QQSA2MDEwLCByZWNhbGwgdGhlIHVzdWFsIG9wdGlvbnMuLi4gCgotIG9uZSBxdWFsaXRhdGl2ZS9jYXRlZ29yaWNhbCB2YXJpYWJsZXM6IGBiYXItY2hhcnRgIAotIG9uZSBxdWFudGl0YXRpdmUvY29udGludW91cyB2YXJpYWJsZXM6IGBoaXN0b2dyYW0vYm94LXBsb3QvYXJlYS1jaGFydGAgIAotIHR3byBxdWFudGl0YXRpdmUvY29udGludW91cyB2YXJpYWJsZXM6IGBzY2F0dGVyLXBsb3QvaGV4LWJpbmAgCgpXZSBjYW4gdGhlbiByYXRjaGV0IHVwIGFzIHdlIG5lZWQgdG8uIAoKSSB3aWxsIHVzZSB0d28gZGF0YS1zZXRzIHRvIHdhbGsgdGhyb3VnaCB0aGUgaW5pdGlhbCBleGFtcGxlcyBpbiB0aGlzIG1vZHVsZSwgdGhlIGZpcnN0IGJlaW5nIHRoaXMgW0lNREIgZGF0YS1zZXRdKGh0dHA6Ly9pbWRiLmNvbS8pCgo+IFRoZSBpbnRlcm5ldCBtb3ZpZSBkYXRhYmFzZSwgaHR0cDovL2ltZGIuY29tLywgaXMgYSB3ZWJzaXRlIGRldm90ZWQgdG8gY29sbGVjdGluZyBtb3ZpZSBkYXRhIHN1cHBsaWVkIGJ5IHN0dWRpb3MgYW5kIGZhbnMuIEl0IGNsYWltcyB0byBiZSB0aGUgYmlnZ2VzdCBtb3ZpZSBkYXRhYmFzZSBvbiB0aGUgd2ViIGFuZCBpcyBydW4gYnkgYW1hem9uLiBNb3JlIGFib3V0IGluZm9ybWF0aW9uIGltZGIuY29tIGNhbiBiZSBmb3VuZCBvbmxpbmUsIGh0dHA6Ly9pbWRiLmNvbS9oZWxwL3Nob3dfIGxlYWY/YWJvdXQsIGluY2x1ZGluZyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSBjb2xsZWN0aW9uIHByb2Nlc3MsIGh0dHA6Ly9pbWRiLmNvbS9oZWxwL3Nob3dfbGVhZj9pbmZvc291cmNlLgoKYGBge3IgbW92aWVzfQpsaWJyYXJ5KGdncGxvdDJtb3ZpZXMpCmRhdGEobW92aWVzKQpuYW1lcyhtb3ZpZXMpCmBgYAoKQSBkYXRhIGZyYW1lIHdpdGggMjg4MTkgcm93cyBhbmQgMjQgdmFyaWFibGVzIAoKfCBWYXJpYWJsZSB8IERlc2NyaXB0aW9uIHwKfCA6LS0gfCA6LS0gfAp8IHRpdGxlIHwgVGl0bGUgb2YgdGhlIG1vdmllIHwKfCB5ZWFyIHwgWWVhciBvZiByZWxlYXNlIHwKfCBidWRnZXQgfCBUb3RhbCBidWRnZXQgKGlmIGtub3duKSBpbiBVUyBkb2xsYXJzIHwKfCBsZW5ndGggfCBMZW5ndGggaW4gbWludXRlcyB8CnwgcmF0aW5nIHwgQXZlcmFnZSBJTURCIHVzZXIgcmF0aW5nIHwKfCB2b3RlcyB8IE51bWJlciBvZiBJTURCIHVzZXJzIHdobyByYXRlZCB0aGlzIG1vdmllIHwKfCByMS0xMCB8IE11bHRpcGx5aW5nIGJ5IHRlbiBnaXZlcyBwZXJjZW50aWxlICh0byBuZWFyZXN0IDEwJSkgb2YgdXNlcnMgd2hvIHJhdGVkIHRoaXMgbW92aWUgYSAxIHwKfCBtcGFhIHwgTVBBQSByYXRpbmcgKG1pc3NpbmcgZm9yIGEgbG90IG9mIG1vdmllcykgfAp8IGFjdGlvbiwgYW5pbWF0aW9uLCBjb21lZHksIGRyYW1hLCBkb2N1bWVudGFyeSwgcm9tYW5jZSwgc2hvcnQgfCBCaW5hcnkgdmFyaWFibGVzIHJlcHJlc2VudGluZyBpZiBtb3ZpZSB3YXMgY2xhc3NpZmllZCBhcyBiZWxvbmdpbmcgdG8gdGhhdCBnZW5yZSB8CgoKVGhlIHNlY29uZCBkYXRhLXNldCBpcyB0aGUgW1N0YXIgV2FycyBkYXRhc2V0XShodHRwczovL3N3YXBpLmNvKSwgYSBgdGliYmxlYCB3aXRoIDg3IHJvd3MgYW5kIDEzIHZhcmlhYmxlczoKCmBgYHtyIHN0YXJ3YXJzfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKZGF0YShzdGFyd2FycykKbmFtZXMoc3RhcndhcnMpCiMgc3RyKHN0YXJ3YXJzKQpgYGAKCnwgVmFyaWFibGUgfCBEZXNjcmlwdGlvbiB8CnwgOi0tIHwgOi0tIHwKfCBuYW1lIHwgTmFtZSBvZiB0aGUgY2hhcmFjdGVyIHwKfCBoZWlnaHQgfCBIZWlnaHQgKGNtKSB8CnwgbWFzcyB8IFdlaWdodCAoa2cpIHwKfCBoYWlyX2NvbG9yLCBza2luX2NvbG9yLCBleWVfY29sb3IgfCBIYWlyLCBza2luLCBhbmQgZXllIGNvbG9ycyB8CnwgYmlydGhfeWVhciB8IFllYXIgYm9ybiAoQkJZID0gQmVmb3JlIEJhdHRsZSBvZiBZYXZpbikgfAp8IGdlbmRlciB8IG1hbGUsIGZlbWFsZSwgaGVybWFwaHJvZGl0ZSwgb3Igbm9uZSB8CnwgaG9tZXdvcmxkIHwgTmFtZSBvZiBob21ld29ybGQgfAp8IHNwZWNpZXMgfCBOYW1lIG9mIHNwZWNpZXMgfAp8IGZpbG1zIHwgTGlzdCBvZiBmaWxtcyB0aGUgY2hhcmFjdGVyIGFwcGVhcmVkIGluIHwKfCB2ZWhpY2xlcyB8IExpc3Qgb2YgdmVoaWNsZXMgdGhlIGNoYXJhY3RlciBoYXMgcGlsb3RlZCB8Cnwgc3RhcnNoaXBzIHwgTGlzdCBvZiBzdGFyc2hpcHMgdGhlIGNoYXJhY3RlciBoYXMgcGlsb3RlZCB8CgoKIyBgZ2dwbG90MmAgYW5kIHRoZSBbZ3JhbW1hciBvZiBncmFwaGljc10oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9sYXllcmVkLWdyYW1tYXIuaHRtbCkKClRoZSBge2dncGxvdDJ9YCBwYWNrYWdlIGhhcyBhIHNwZWNpYWwgc3ludGF4IGFuZCBJIHdpbGwgcG9pbnQgb3V0IHRoaW5ncyB5b3Ugc2hvdWxkIG5vdGUgYXMgd2UgbW92ZSB0aHJvdWdoIHRoaXMgbW9kdWxlLiBGaXJzdCB1cCwgdGhlIGxpYnJhcnkgaXMgY2FsbGVkIGBnZ3Bsb3QyYCBidXQgdGhlIGNvbW1hbmQgc3RhcnRzIHdpdGggYGdncGxvdGAgc28gZG9uJ3QgbGV0IHRoYXQgdGhyb3cgeW91IG9mZi10cmFjay4gCgpTZWNvbmQsIHlvdSBuZWVkIHRvIGhhdmUgYSBkYXRhLXNldCB0byB3b3JrIHdpdGguIEluIHRoZSBjb2RlIGJlbG93IEkgc3RhcnQgYnkgbG9hZGluZyB0aGUgbGlicmFyeSBhbmQgdGhlbiBzcGVjaWZ5aW5nIHRoZSBkYXRhLXNldCB0byBiZSB1c2VkLiAKCmBgYHtyIGdnMDAwfQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdCgKICBkYXRhID0gc3RhcndhcnMKICApCmBgYAoKTm90aGluZyByZXN1bHRzIGZyb20gdGhlc2UgY29tbWFuZHMgYmVjYXVzZSB3ZSBoYXZlIG5vdCB5ZXQgc3BlY2lmaWVkIGFueXRoaW5nIGFib3V0IHdoYXQgc2hvdWxkIGdvIG9uIHRoZSB4LWF4aXMsIHdoYXQgc2hvdWxkIGdvIG9uIHRoZSB5LWF4aXMuIFdlbGwsIGxldCB1cyBkbyB0aGF0IHRoZW4gYnkgYXNraW5nIGZvciB0aGUgY29sdW1uIGBleWVfY29sb3JgIHRvIGJlIHB1dCBvbiB0aGUgeC1heGlzLiAKCmBgYHtyIGdnMDAxfQpnZ3Bsb3QoCiAgZGF0YSA9IHN0YXJ3YXJzLAogIG1hcHBpbmcgPSBhZXMoCiAgICB4ID0gZXllX2NvbG9yCiAgICApCiAgKSAKYGBgCgojIyBgZ2VvbV9iYXIoKWAgLi4uIHRoZSBiYXItY2hhcnQgClRoaXMgcmVzdWx0cyBpbiBhIGdyYXkgY2FudmFzIHdpdGggdGhlIGV5ZSBjb2xvcnMgb24gdGhlIHgtYXhpcyBidXQgbm90aGluZyBlbHNlIGhhcyBiZWVuIGRyYXduIHNpbmNlIHdlIGhhdmUgbm90IHNwZWNpZmllZCB0aGUgYGdlb21ldHJ5YCAuLi4gZG8geW91IHdhbnQgYSBiYXItY2hhcnQ/IGhpc3RvZ3JhbT8gZG90LXBsb3Q/IGxpbmUtY2hhcnQ/IFRoaXMgaXMgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBhbmQgaGVuY2UgYSBiYXItY2hhcnQgd291bGQgYmUgYXBwcm9wcmlhdGUuIFdlIGNhbGwgZm9yIGEgYmFyLWNoYXJ0IHdpdGggdGhlIGBnZW9tX2JhcigpYCBjb21tYW5kLiAKCmBgYHtyIGdnMDAyYX0KZ2dwbG90KAogIGRhdGEgPSBzdGFyd2FycywKICBtYXBwaW5nID0gYWVzKAogICAgeCA9IGV5ZV9jb2xvcgogICAgKQogICkgKwogIGdlb21fYmFyKCkKYGBgCgojIyBgY29sb3JgIHZlcnN1cyBgZmlsbGAgClRoZSBgYWVzKClgIHJlZmVycyB0byB0aGUgKiphZXN0aGV0aWNzKiogb2YgdGhlIGNoYXJ0LCBhbmQgbWFueSBvdGhlciBgYWVzdGhldGljc2AgY2FuIGJlIGFkZGVkLCBzdWNoIGFzIGBncm91cGAsIGBjb2xvcmAsIGBmaWxsYCwgYHNpemVgLCBgYWxwaGFgLCBldGMuIFdlIHdpbGwgc2VlIHNvbWUgb2YgdGhlc2UgaW4gZHVlIGNvdXJzZSBidXQgZm9yIG5vdyBJIHdhbnQgdG8gZm9jdXMgb24gdHdvIG9mIHRoZXNlLCBib3RoIGludm9sdmluZyBjb2xvcmluZyBvZiB0aGUgYGdlb21fYC4gU3BlY2lmaWNhbGx5LCB0aGVyZSBhcmUgdHdvIGNvbW1hbmRzIGZvciBhZGRpbmcgY29sb3JzIC0tICgxKSBgY29sb3JgIG9yIGBjb2xvdXJgLCBhbmQgKDIpIGBmaWxsYCAtLSB0byBhIGNoYXJ0LiAKCmBgYHtyIGNvbDFhfQpnZ3Bsb3QoCiAgZGF0YSA9IHN0YXJ3YXJzLAogIG1hcHBpbmcgPSBhZXMoCiAgICB4ID0gZXllX2NvbG9yLAogICAgY29sb3IgPSBleWVfY29sb3IKICAgICkKICApICsKICBnZW9tX2JhcigpCmBgYAoKTm90ZSB3aGF0IHRoZSBgY29sb3IgPSBleWVfY29sb3JgIGNvbW1hbmQgZGlkIC4uLiBpdCBkcmV3IGEgY29sb3JlZCBib3JkZXIgZm9yIHRoZSBiYXJzLCBhbmQgYW4gYWNjb21wYW55aW5nIGxlZ2VuZC4gV2hhdCBpZiB3ZSBoYWQgdXNlZCBgZmlsbCA9IGV5ZV9jb2xvcmAgaW5zdGVhZD8gIAoKYGBge3IgY29sMWJ9CmdncGxvdCgKICBkYXRhID0gc3RhcndhcnMsCiAgbWFwcGluZyA9IGFlcygKICAgIHggPSBleWVfY29sb3IsCiAgICBmaWxsID0gZXllX2NvbG9yCiAgICApCiAgKSArCiAgZ2VvbV9iYXIoKQpgYGAKCkFoYSEgTm93IHRoZSBiYXJzIGFyZSBmaWxsZWQgd2l0aCBjb2xvcnMgYW5kIGFuIGFjY29tcGFueWluZyBsZWdlbmQgaXMgZHJhd24gYXMgd2VsbC4gU28gYGZpbGwgPWAgYW5kIGBjb2xvciA9YCBiZWhhdmUgdmVyeSBkaWZmZXJlbnRseSwgYmVhciB0aGlzIGluIG1pbmQuIAoKIyMgQWRkaW5nIGxhYmVscyB3aXRoIGBsYWJzYCAKT25lIG9mIHRoZSBuaWNlIHRoaW5ncyBhYm91dCB0aGlzIHNvZnR3YXJlIGVudmlyb25tZW50IGlzIHRoYXQgdGhlcmUgYXJlIHBsZW50eSBvZiBjb2xvcmluZyBzY2hlbWVzIGF2YWlsYWJsZSB0byB1cyBhbmQgd2Ugd2lsbCBwbGF5IHdpdGggc29tZSBvZiB0aGVzZSBzaG9ydGx5LCBidXQgYmVmb3JlIHdlIGRvIHRoYXQsIGxldCB1cyBsb29rIGF0IG9uZSBtb3JlIGltcHJvdmVtZW50IC0tIGFkZGluZyB0aXRsZXMsIHN1YnRpdGxlcywgY2FwdGlvbnMsIGFuZCBheGlzIGxhYmVscyB0byBvdXIgY2hhcnQuIFRoaXMgaXMgZG9uZSB3aXRoIHRoZSBgbGFicyA9ICgpYCBjb21tYW5kLgoKYGBge3IgY29sMWIyfQpnZ3Bsb3QoCiAgZGF0YSA9IHN0YXJ3YXJzLAogIG1hcHBpbmcgPSBhZXMoCiAgICB4ID0gZXllX2NvbG9yLAogICAgZmlsbCA9IGV5ZV9jb2xvcgogICAgKQogICkgKwogIGdlb21fYmFyKCkgKwogIGxhYnMoCiAgICB4ID0gIkV5ZSBDb2xvciIsCiAgICB5ID0gIkZyZXF1ZW5jeSAobikiLAogICAgdGl0bGUgPSAiQmFyLWNoYXJ0IG9mIEV5ZSBDb2xvcnMiLAogICAgc3VidGl0bGUgPSAiKG9mIFN0YXIgV2FycyBjaGFyYWN0ZXJzKSIsCiAgICBjYXB0aW9uID0gIk15IGxpdHRsZSB3b3JrIG9mIGFydCEhIgogICAgKQpgYGAKCk5vdGljZSB0aGUgdGV4dCB0aGF0IG5vdyBhcHBlYXJzIGFzIGEgcmVzdWx0IG9mIHdoYXQgaGFzIGJlZW4gc3BlY2lmaWVkIGluIHRoZSBgbGFicygpYCBjb21tYW5kLiAKCiMjIENvbnRyb2xsaW5nIHRoZSBjaGFydCBsZWdlbmQgd2l0aCBgdGhlbWUoKWAKSW4gdGhpcyBiYXItY2hhcnQsIGRvIHdlIHJlYWxseSBuZWVkIHRoZSBsZWdlbmQ/IE5vLCBiZWNhdXNlIHRoZSBjb2xvcnMgYW5kIGNvbG9yIG5hbWVzIHNob3cgdXAgaW4gdGhlIGNoYXJ0IGl0c2VsZi4gSG93IGNhbiB3ZSBoaWRlIHRoZSBsZWdlbmQ/IFR1cm5zIG91dCB0aGVyZSBpcyBhIG5lYXQgY29tbWFuZCB0aGF0IHdpbGwgYWxsb3cgeW91IHRvIG1vdmUgdGhlIGxlZ2VuZCBhcm91bmQgYW5kIGV2ZW4gdG8gaGlkZSBpdC4gCgpgYGB7ciBjb2wxY30KZ2dwbG90KAogIGRhdGEgPSBzdGFyd2FycywKICBtYXBwaW5nID0gYWVzKAogICAgeCA9IGV5ZV9jb2xvciwKICAgIGZpbGwgPSBleWVfY29sb3IKICAgICkKICApICsKICBnZW9tX2JhcigpICsKICBsYWJzKAogICAgeCA9ICJFeWUgQ29sb3IiLAogICAgeSA9ICJGcmVxdWVuY3kgKG4pIiwKICAgIHRpdGxlID0gIkJhci1jaGFydCBvZiBFeWUgQ29sb3JzIiwKICAgIHN1YnRpdGxlID0gIihvZiBTdGFyIFdhcnMgY2hhcmFjdGVycykiLAogICAgY2FwdGlvbiA9ICJNeSBsaXR0bGUgd29yayBvZiBhcnQhISIKICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpWb2lsYSEgVGhlIGxlZ2VuZCBpcyBnb25lLiBJbnN0ZWFkIG9mICJub25lIiB5b3UgY291bGQgaGF2ZSBzcGVjaWZpZWQgImJvdHRvbSIsICJsZWZ0IiwgInRvcCIsICJyaWdodCIgdG8gcGxhY2UgdGhlIGxlZ2VuZCBpbiBhIHBhcnRpY3VsYXIgZGlyZWN0aW9uLgoKIyMgQ3VzdG9taXppbmcgY29sb3JzIApPZiBjb3Vyc2UsIGl0IHdvdWxkIGJlIGdvb2QgdG8gaGF2ZSB0aGUgY29sb3JzIG1hdGNoIHRoZSBleWUtY29sb3Igc28gbGV0IHVzIGRvIHRoYXQgbmV4dC4gVGhlIHdheSB3ZSBjYW4gZG8gdGhpcyBpcyBieSBjYWxsaW5nIHNwZWNpZmljIGNvbG9ycyBieSBuYW1lLiBJIGhhdmUgdHJpZWQgdG8gb3JkZXIgdGhlIGxpbmV1cCBvZiB0aGUgY29sb3JzIHRvIG1hdGNoLCBhcyBjbG9zZWx5IGFzIEkgY2FuLCB0aGUgZXllIGNvbG9ycy4gCgpgYGB7ciBjb2wzfQpjKAogICJibGFjayIsICJibHVlIiwgInNsYXRlZ3JheSIsICJicm93biIsICJncmF5MzQiLCAiZ29sZCIsCiAgImdyZWVueWVsbG93IiwgIm5hdmFqb3doaXRlMSIsICJvcmFuZ2UiLCAicGluayIsICJyZWQiLAogICJtYWdlbnRhIiwgInRoaXN0bGUzIiwgIndoaXRlIiwgInllbGxvdyIKICApIC0+IG15Y29sb3JzCgpnZ3Bsb3QoCiAgZGF0YSA9IHN0YXJ3YXJzLAogIG1hcHBpbmcgPSBhZXMoCiAgICB4ID0gZXllX2NvbG9yCiAgICApCiAgKSArIAogIGdlb21fYmFyKAogICAgZmlsbCA9IG15Y29sb3JzCiAgICApICsgCiAgbGFicygKICAgIHggPSAiRXllIENvbG9yIiwKICAgIHkgPSAiRnJlcXVlbmN5IChuKSIsCiAgICB0aXRsZSA9ICJCYXItY2hhcnQgb2YgRXllIENvbG9ycyIsCiAgICBzdWJ0aXRsZSA9ICIob2YgU3RhciBXYXJzIGNoYXJhY3RlcnMpIiwKICAgIGNhcHRpb24gPSAiTXkgbGl0dGxlIHdvcmsgb2YgYXJ0ISEiCiAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKVGhlc2UgY29sb3JzIGFyZSBmcm9tIFt0aGlzIHNvdXJjZV0oaHR0cDovL3d3dy5zdGF0LmNvbHVtYmlhLmVkdS9+dHpoZW5nL2ZpbGVzL1Jjb2xvci5wZGYpIGJ1dCBzZWUgYWxzbyBbdGhpcyBzb3VyY2VdKGh0dHBzOi8vd3d3Lm5jZWFzLnVjc2IuZWR1L35mcmF6aWVyL1JTcGF0aWFsR3VpZGVzL2NvbG9yUGFsZXR0ZUNoZWF0c2hlZXQucGRmKS4gQ29sb3JzIGNhbiBiZSBjdXN0b21pemVkIGJ5IGdlbmVyYXRpbmcgeW91ciBvd24gcGFsZXR0ZXMgdmlhIHRoZSBbQ29sb3IgQnJld2VyIGhlcmVdKGh0dHA6Ly9jb2xvcmJyZXdlcjIub3JnLyN0eXBlPXNlcXVlbnRpYWwmc2NoZW1lPVlsR25CdSZuPTMpLiBCdXQgZG9uJ3QgZ2V0IGNhcnJpZWQgYXdheTogUmVtZW1iZXIgdG8gcmVhZCB0aGUgbWF0ZXJpYWxzIG9uIGNob29zaW5nIGNvbG9ycyB3aXNlbHksIHBhcnRpY3VsYXJseSB0aGUgcG9pbnQgYWJvdXQgcXVhbGl0YXRpdmUgcGFsZXR0ZXMsIGRpdmVyZ2VudCBwYWxldHRlcywgYW5kIHRoZW4gcGFsZXR0ZXMgdGhhdCB3b3JrIHdlbGwgZXZlbiB3aXRoIGNvbG9yYmxpbmQgYXVkaWVuY2VzLgoKIyMgU2VsZWN0ZWQgY29sb3IgcGFsZXR0ZXMKSSBoYWQgbWVudGlvbmVkIHRoZSBleGlzdGVuY2Ugb2YgYSBudW1iZXIgb2YgY29sb3IgcGFsZXR0ZXMgc28gbGV0IHVzIGxvb2sgYXQgYSBmZXcsIGJ1dCB3ZSB3aWxsIGRvIHRoaXMgd2l0aCBhIGRpZmZlcmVudCB2YXJpYWJsZS4gRmlyc3QgdXAsIHRoZSBgUGFzdGVsMWAgcGFsZXR0ZS4gCgpgYGB7ciBjb2xmaWxsYjF9CmdncGxvdCgKICBkYXRhID0gc3RhcndhcnMsCiAgbWFwcGluZyA9IGFlcygKICAgIHggPSBnZW5kZXIKICAgICkKICApICsgCiAgZ2VvbV9iYXIoCiAgICBhZXMoZmlsbCA9IGdlbmRlcikKICAgICkgKyAKICBsYWJzKAogICAgeCA9ICJHZW5kZXIiLAogICAgeSA9ICJGcmVxdWVuY3kiLAogICAgdGl0bGUgPSAiQmFyLWNoYXJ0IG9mIEdlbmRlciIsCiAgICBzdWJ0dGl0bGUgPSAiKG9mIFN0YXIgV2FycyBjaGFyYWN0ZXJzKSIsCiAgICBjYXB0aW9uID0gIihTb3VyY2U6IFRoZSBkcGx5ciBwYWNrYWdlKSIpICsKICBzY2FsZV9maWxsX2JyZXdlcigKICAgIHBhbGV0dGUgPSAiUGFzdGVsMSIKICAgICkKYGBgCgpOb3QgYmFkIGJ1dCBkb2Vzbid0IHdvcmsgdG9vIHdlbGwgaGVyZS4gSG93IGFib3V0IHRyeWluZyBhbm90aGVyIHBhbGV0dGUsIGBTZXRgPwoKYGBge3IgY29sZmlsbGIyfQpnZ3Bsb3QoCiAgZGF0YSA9IHN0YXJ3YXJzLAogIG1hcHBpbmcgPSBhZXMoCiAgICB4ID0gZ2VuZGVyCiAgICApCiAgKSArIAogIGdlb21fYmFyKAogICAgYWVzKGZpbGwgPSBnZW5kZXIpCiAgICApICsgCiAgbGFicygKICAgIHggPSAiR2VuZGVyIiwKICAgIHkgPSAiRnJlcXVlbmN5IiwKICAgIHRpdGxlID0gIkJhci1jaGFydCBvZiBHZW5kZXIiLAogICAgc3VidHRpdGxlID0gIihvZiBTdGFyIFdhcnMgY2hhcmFjdGVycykiLAogICAgY2FwdGlvbiA9ICIoU291cmNlOiBUaGUgZHBseXIgcGFja2FnZSkiKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIoCiAgICBwYWxldHRlID0gIlNldDEiCiAgICApCmBgYAoKTmljZSEgQnV0IHdoYXQgaXMgYWxzbyBub3RpY2VhYmxlIGhlcmUgaXMgdGhhdCB0aGVyZSBhcmUgc29tZSBjaGFyYWN0ZXJzIGluIHRoZSBkYXRhLXNldCB3aG9zZSBnZW5kZXIgZGF0YSBpcyBtaXNzaW5nLiBUaGVzZSBhcmUgdGhlIGBOQWAgdmFsdWVzLiBCeSBkZWZhdWx0LCB5b3Ugd2lsbCBzZWUgYE5BYCB2YWx1ZXMgc2hvd2luZyB1cCBpbiBzb21lIHR5cGVzIG9mIGNoYXJ0cyBhbmQgc28gaXQgaXMgYWx3YXlzIGdvb2QgdG8gZXhjbHVkZSB0aGVtIGZyb20gdGhlIGNoYXJ0LiBIZXJlIGlzIG9uZSB3YXkgb2YgZG9pbmcgdGhhdC4gCgpgYGB7ciBjb2xmaWxsYjN9CmdncGxvdCgKICBkYXRhID0gc3Vic2V0KHN0YXJ3YXJzLCAhaXMubmEoZ2VuZGVyKSksCiAgbWFwcGluZyA9IGFlcygKICAgIHggPSBnZW5kZXIKICAgICkKICApICsgCiAgZ2VvbV9iYXIoCiAgICBhZXMoZmlsbCA9IGdlbmRlcikKICAgICkgKyAKICBsYWJzKAogICAgeCA9ICJHZW5kZXIiLAogICAgeSA9ICJGcmVxdWVuY3kiLAogICAgdGl0bGUgPSAiQmFyLWNoYXJ0IG9mIEdlbmRlciIsCiAgICBzdWJ0dGl0bGUgPSAiKG9mIFN0YXIgV2FycyBjaGFyYWN0ZXJzKSIsCiAgICBjYXB0aW9uID0gIihTb3VyY2U6IFRoZSBkcGx5ciBwYWNrYWdlKSIpICsKICBzY2FsZV9maWxsX2JyZXdlcigKICAgIHBhbGV0dGUgPSAiU2V0MSIKICAgICkKYGBgCgpOb3RpY2Ugd2hhdCBpcyBkaWZmZXJlbnQgaGVyZTogYGRhdGEgPSBzdWJzZXQoc3RhcndhcnMsICFpcy5uYShnZW5kZXIpKWAgYW5kIHRoYXQgdGhpcyBjb21tYW5kIGlzIGVmZmVjdGl2ZWx5IHNheWluZyBzdWJzZXQgdGhlIHN0YXJ3YXJzIGRhdGEgdG8gb25seSBpbmNsdWRlIHRob3NlIGNhc2VzIHdoZXJlIGdlbmRlciBpcyBub3QgbWlzc2luZyAodGhpcyBpcyB0aGUgYCFpcy5uYSgpYCBwb3J0aW9uIG9mIHRoZSBjb21tYW5kKS4gCgpBbm90aGVyIHdheSB0byBkbyB0aGUgc2FtZSB0aGluZyB3b3VsZCBoYXZlIGJlZW4gdG8gdXNlIGBmaWx0ZXIoKWAgYW5kIGNyZWF0ZSBhIGNsZWFuZWQgdXAgY29weSBvZiB0aGUgZGF0YS4gSWYgeW91IHRha2UgdGhpcyByb3V0ZSwgYmUgY2FyZWZ1bCBub3QgdG8gb3ZlcndyaXRlIHRoZSBvcmlnaW5hbCBkYXRhLXNldDsgbm90ZSBob3cgSSBhbSBnaXZpbmcgYSBuZXcgbmFtZSBgKG15LmRhdGEpYCBhZnRlciBgZmlsdGVyKClgIHRvIHNhdmUgdGhlIHJlc3VsdHMgaW4uIFRoZW4gd2UgbGVhbiBvbiB0aGlzIGRhdGEtc2V0IHZpYSBgZGF0YSA9IG15LmRhdGFgLgoKYGBge3IgY29sZmlsbGI0fQpzdGFyd2FycyAlPiUKICBmaWx0ZXIoIWlzLm5hKGdlbmRlcikpIC0+IG15LmRhdGEKCmdncGxvdCgKICBkYXRhID0gbXkuZGF0YSwKICBtYXBwaW5nID0gYWVzKAogICAgeCA9IGdlbmRlcgogICAgKQogICkgKyAKICBnZW9tX2JhcigKICAgIGFlcyhmaWxsID0gZ2VuZGVyKQogICAgKSArIAogIGxhYnMoCiAgICB4ID0gIkdlbmRlciIsCiAgICB5ID0gIkZyZXF1ZW5jeSIsCiAgICB0aXRsZSA9ICJCYXItY2hhcnQgb2YgR2VuZGVyIiwKICAgIHN1YnR0aXRsZSA9ICIob2YgU3RhciBXYXJzIGNoYXJhY3RlcnMpIiwKICAgIGNhcHRpb24gPSAiKFNvdXJjZTogVGhlIGRwbHlyIHBhY2thZ2UpIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKAogICAgcGFsZXR0ZSA9ICJTZXQxIgogICAgKQpgYGAKClRoZXJlIGlzIG9uZSBjb2xvciBwYWxldHRlIHlvdSBzaG91bGQgcmVtZW1iZXIsIGFuZCB0aGlzIGlzIHRoZSBge3ZpcmlkaXN9YCBjb2xvciBzY2hlbWUgdGhhdCB3b3JrcyBhcm91bmQgdmFyeWluZyB0eXBlcyBvZiBjb2xvciBibGluZG5lc3MgaW4gdGhlIHBvcHVsYXRpb24uIEhlcmUgY29tZSB0aGUgcGFsZXR0ZXM6CgpgYGB7ciBjb2xmaWxsdjF9CmdncGxvdCgKICBkYXRhID0gbXkuZGF0YSwKICBtYXBwaW5nID0gYWVzKAogICAgeCA9IGdlbmRlcgogICAgKQogICkgKyAKICBnZW9tX2JhcigKICAgIGFlcyhmaWxsID0gZ2VuZGVyKQogICAgKSArIAogIGxhYnMoCiAgICB4ID0gIkdlbmRlciIsCiAgICB5ID0gIkZyZXF1ZW5jeSIsCiAgICB0aXRsZSA9ICJCYXItY2hhcnQgb2YgR2VuZGVyIiwKICAgIHN1YnR0aXRsZSA9ICIob2YgU3RhciBXYXJzIGNoYXJhY3RlcnMpIiwKICAgIGNhcHRpb24gPSAiKFNvdXJjZTogVGhlIGRwbHlyIHBhY2thZ2UpIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKAogICAgb3B0aW9uID0gInZpcmlkaXMiCiAgICApCmBgYAoKYGBge3IgY29sZmlsbHYyfQpnZ3Bsb3QoCiAgZGF0YSA9IG15LmRhdGEsCiAgbWFwcGluZyA9IGFlcygKICAgIHggPSBnZW5kZXIKICAgICkKICApICsgCiAgZ2VvbV9iYXIoCiAgICBhZXMoZmlsbCA9IGdlbmRlcikKICAgICkgKyAKICBsYWJzKAogICAgeCA9ICJHZW5kZXIiLAogICAgeSA9ICJGcmVxdWVuY3kiLAogICAgdGl0bGUgPSAiQmFyLWNoYXJ0IG9mIEdlbmRlciIsCiAgICBzdWJ0dGl0bGUgPSAiKG9mIFN0YXIgV2FycyBjaGFyYWN0ZXJzKSIsCiAgICBjYXB0aW9uID0gIihTb3VyY2U6IFRoZSBkcGx5ciBwYWNrYWdlKSIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgKICAgIG9wdGlvbiA9ICJtYWdtYSIKICAgICkKYGBgCgpgYGB7ciBjb2xmaWxsdjN9CmdncGxvdCgKICBkYXRhID0gbXkuZGF0YSwKICBtYXBwaW5nID0gYWVzKAogICAgeCA9IGdlbmRlcgogICAgKQogICkgKyAKICBnZW9tX2JhcigKICAgIGFlcyhmaWxsID0gZ2VuZGVyKQogICAgKSArIAogIGxhYnMoCiAgICB4ID0gIkdlbmRlciIsCiAgICB5ID0gIkZyZXF1ZW5jeSIsCiAgICB0aXRsZSA9ICJCYXItY2hhcnQgb2YgR2VuZGVyIiwKICAgIHN1YnR0aXRsZSA9ICIob2YgU3RhciBXYXJzIGNoYXJhY3RlcnMpIiwKICAgIGNhcHRpb24gPSAiKFNvdXJjZTogVGhlIGRwbHlyIHBhY2thZ2UpIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKAogICAgb3B0aW9uID0gInBsYXNtYSIKICAgICkKYGBgCgpgYGB7ciBjb2xmaWxsdjR9CmdncGxvdCgKICBkYXRhID0gbXkuZGF0YSwKICBtYXBwaW5nID0gYWVzKAogICAgeCA9IGdlbmRlcgogICAgKQogICkgKyAKICBnZW9tX2JhcigKICAgIGFlcyhmaWxsID0gZ2VuZGVyKQogICAgKSArIAogIGxhYnMoCiAgICB4ID0gIkdlbmRlciIsCiAgICB5ID0gIkZyZXF1ZW5jeSIsCiAgICB0aXRsZSA9ICJCYXItY2hhcnQgb2YgR2VuZGVyIiwKICAgIHN1YnR0aXRsZSA9ICIob2YgU3RhciBXYXJzIGNoYXJhY3RlcnMpIiwKICAgIGNhcHRpb24gPSAiKFNvdXJjZTogVGhlIGRwbHlyIHBhY2thZ2UpIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKAogICAgb3B0aW9uID0gImNpdmlkaXMiCiAgICApCmBgYAoKIyMgVGhlbWVzIHdpdGggYHtnZ3RoZW1lc31gCk9uZSBjYW4gYWxzbyBsZWFuIG9uIHZhcmlvdXMgcGxvdHRpbmcgdGhlbWVzIGFzIHNob3duIGJlbG93LiBUaGVzZSB0aGVtZXMgbWltaWMgdGhlIHN0eWxlIG9mIGdyYXBoaWNzIHBvcHVsYXJpemVkIGJ5IHNvbWUgZGF0YSB2aXN1YWxpemF0aW9uIGV4cGVydHMgKGZvciBlLmcuLCBTdGVwaGVuIEZldywgRWR3YXJkIFR1ZnRlKSwgbmV3cy1tZWRpYSBob3VzZXMgKEZpdmV0aGlydHllaWdodCwgVGhlIEVjb25vbWlzdCwgVGhlIFdhbGwgU3RyZWV0IEpvdXJuYWwpLCBzb21lIHNvZnR3YXJlIHBhY2thZ2VzIChFeGNlbCwgU3RhdGEsIEdvb2dsZSBkb2NzKSwgYW5kIGEgZmV3IG90aGVycy4gQmVsb3cgSSBzaG93IHlvdSBqdXN0IGEgaGFuZGZ1bC4gIAoKYGBge3IgZ2d0MX0KbGlicmFyeShnZ3RoZW1lcykKCmdncGxvdCgKICBkYXRhID0gc3RhcndhcnMsCiAgbWFwcGluZyA9IGFlcygKICAgIHggPSBleWVfY29sb3IKICAgICkKICApICsKICBnZW9tX2JhcigpICsKICB0aGVtZV90dWZ0ZSgpIApgYGAKCmBgYHtyIGdndDJ9CmdncGxvdCgKICBkYXRhID0gc3RhcndhcnMsCiAgbWFwcGluZyA9IGFlcygKICAgIHggPSBleWVfY29sb3IKICAgICkKICApICsKICBnZW9tX2JhcigpICsKICB0aGVtZV9lY29ub21pc3QoKSAKYGBgCgpgYGB7ciBnZ3QzfQpnZ3Bsb3QoCiAgZGF0YSA9IHN0YXJ3YXJzLAogIG1hcHBpbmcgPSBhZXMoCiAgICB4ID0gZXllX2NvbG9yCiAgICApCiAgKSArCiAgZ2VvbV9iYXIoKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgCmBgYAoKTGF0ZXIgb24geW91IHdpbGwgbGVhcm4gdGhlc2UgJiBvdGhlciB3YXlzIHRvIGJ1aWxkIGFkdmFuY2VkIHZpc3VhbGl6YXRpb25zLiBGb3Igbm93IHdlIGdldCB0byB3b3JrIG1vcmUgd2l0aCBgZ2dwbG90MmAuIAoKIyMgTW9yZSB3aXRoIGJhci1jaGFydHMKCkkgd2FudCB0byBzaG93IGEgZmV3IHRoaW5ncyB3aXRoIGJhci1jaGFydHMgbm93LiBGaXJzdCwgd2UgY2FuIHNwZWNpZnkgdGhpbmdzIGEgYml0IGRpZmZlcmVudGx5IHdpdGhvdXQgYWx0ZXJpbmcgdGhlIHJlc3VsdC4gRm9yIGV4YW1wbGUsIGNvbXBhcmUgdGhlIGZvbGxvd2luZyB0d28gcGllY2VzIG9mIGNvZGUuIAoKYGBge3IgYmFyMDF9CmdncGxvdCgKICBkYXRhID0gbW92aWVzLAogIG1hcHBpbmcgPSBhZXMoCiAgICB4ID0gbXBhYQogICAgKQogICkgKyAKICBnZW9tX2JhcigpCmBgYAoKYGBge3IgYmFyMDJ9CmdncGxvdCgpICsgCiAgZ2VvbV9iYXIoCiAgICBkYXRhID0gbW92aWVzLAogICAgbWFwcGluZyA9IGFlcyh4ID0gbXBhYSkKICAgICkKYGBgCgpOb3RpY2UgdGhhdCB3ZSBzd2l0Y2hlZCB0aGUgYGRhdGEgPWAgYW5kIHRoZSBgYWVzKClgIHBpZWNlcyBvZiB0aGUgY29kZSBidXQgdGhhdCBtYWRlIG5vIGRpZmZlcmVuY2U7IHRoaXMgaXMgaW1wb3J0YW50IHRvIGJlYXIgaW4gbWluZCBiZWNhdXNlIGl0IHdpbGwgY29tZSBpbiBoYW5keSBkb3duIHRoZSByb2FkIHdoZW4gd2UgbmVlZCB0byBidWlsZCBzb21lIGFkdmFuY2VkIHZpc3VhbGl6YXRpb25zLiAKClRoZSBwbG90IGlzIHN1Yi1vcHRpbWFsIHNpbmNlIE1QQUEgcmF0aW5ncyBhcmUgbWlzc2luZyBmb3IgYSBsb3Qgb2YgbW92aWVzIGFuZCBzaG91bGQgYmUgIGVsaW1pbmF0ZWQgZnJvbSB0aGUgcGxvdCB2aWEgYHN1YnNldChtcGEgIT0gIiIpYCAgb3IgYnkgcnVubmluZyBkcGx5cidzIGBmaWx0ZXIoKWAgIHRvIGNyZWF0ZSBhbm90aGVyIGRhdGEtc2V0LiBJIHdpbGwgbGVhbiBvbiBgZmlsdGVyKClgLiAKCmBgYHtyIGJhcjJ9Cm1vdmllcyAlPiUgCiAgZmlsdGVyKG1wYWEgIT0gIiIpIC0+IG1vdmllczIgCgpnZ3Bsb3QoKSArIAogIGdlb21fYmFyKAogICAgZGF0YSA9IG1vdmllczIsCiAgICBtYXBwaW5nID0gYWVzKHggPSBtcGFhKQogICAgKQpgYGAKClRoZSBvcmRlciBvZiB0aGUgYmFycyBoZXJlIGlzIGZvcnR1aXRvdXMgaW4gdGhhdCBpdCBnb2VzIGZyb20gdGhlIHNtYWxsZXN0IGZyZXF1ZW5jeSB0byB0aGUgaGlnaGVzdCBmcmVxdWVuY3ksIGRyYXdpbmcgdGhlIHJlYWRlcidzIGV5ZS4gSSBzYWlkIGZvcnR1aXRvdXMgYmVjYXVzZSBge2dncGxvdDJ9YCBkZWZhdWx0cyB0byBkcmF3aW5nIHRoZSBiYXJzIGluIGFuIGFzY2VuZGluZyBhbHBoYWJldGljL2FscGhhbnVtZXJpYyBvcmRlciBpZiB0aGUgdmFyaWFibGUgaXMgYSAqKmNoYXJhY3RlcioqLiBTZWUgYmVsb3cgZm9yIGFuIGV4YW1wbGUuIAoKYGBge3IgYmFyM30KZGYgPSB0aWJibGUoeCA9IGMocmVwKCJBIiwgMiksIHJlcCgiQiIsIDQpLCByZXAoIkMiLCAxKSkpCgpnZ3Bsb3QoKSArIAogIGdlb21fYmFyKAogICAgZGF0YSA9IGRmLCAKICAgIG1hcHBpbmcgPSBhZXMoeCA9IHgpCiAgKQpgYGAKCk5vdGljZSB0aGUgYmFycyBoZXJlIGRvIG5vdCBmb2xsb3cgaW4gYXNjZW5kaW5nL2Rlc2NlbmRpbmcgb3JkZXIgb2YgZnJlcXVlbmNpZXMuIExhdGVyIG9uIHdlJ2xsIGxlYXJuIGhvdyB0byBvcmRlciB0aGUgYmFycyB3aXRoIGFzY2VuZGluZy9kZXNjZW5kaW5nIGZyZXF1ZW5jaWVzIG9yIGJ5IHNvbWUgb3RoZXIgbG9naWMuIAoKV2hhdCBhYm91dCBwbG90dGluZyBgcmVsYXRpdmUgZnJlcXVlbmNpZXNgIG9uIHRoZSB5LWF4aXMgcmF0aGVyIHRoYW4gdGhlIGZyZXF1ZW5jaWVzPyAKCmBgYHtyIGJhcjR9CmxpYnJhcnkoc2NhbGVzKQoKZ2dwbG90KCkgKyAKICBnZW9tX2JhcigKICAgIGRhdGEgPSBtb3ZpZXMyLAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IG1wYWEsCiAgICAgIHkgPSAoLi5jb3VudC4uKS9zdW0oLi5jb3VudC4uKQogICAgICApCiAgICApICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnQpICsgCiAgbGFicygKICAgIHggPSAiTVBBQSBSYXRpbmciLAogICAgeSAgPSAiUmVsYXRpdmUgRnJlcXVlbmN5ICglKSIKICAgICkgCmBgYAoKTm90ZSB0aGUgYWRkaXRpb24gb2YgCgorIGB5ID0gKC4uY291bnQuLikvc3VtKC4uY291bnQuLilgIHRvIGNoYW5nZSB0aGUgeS1heGlzIHRvIHJlZmxlY3QgdGhlIHJlbGF0aXZlIGZyZXF1ZW5jeSBhcyBhIHByb3BvcnRpb24sIGFuZCAgCisgYHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50KWAgdG8gdGhlbiBtdWx0aXBseSB0aGVzZSBwcm9wb3J0aW9ucyBieSAxMDAgdG8gZ2V0IHBlcmNlbnRhZ2VzIGFzIHRoZSBsYWJlbHMgcmF0aGVyIHRoYW4gMC4yLCAwLjQsIDAuNiwgZXRjLiAKCiMjIERpc2FnZ3JlZ2F0aW5nIGJhci1jaGFydHMgZm9yIGdyb3VwcyAKTGV0IHVzIGJ1aWxkIGEgc2ltcGxlIGJhci1jaGFydCB3aXRoIHRoZSBgaHNiMmAgZGF0YSB3ZSBzYXcgaW4gTW9kdWxlIDAxLiBIZXJlIHdlIGZpcnN0IGRvd25sb2FkIGl0LCBsYWJlbCB0aGUgdmFsdWVzLCBzYXZlIGl0LCBhbmQgdGhlbiBzdGFydCBjaGFydGluZyBpdC4gIAoKYGBge3IgaHNiMi1kYXRhaW59CnJlYWQudGFibGUoCiAgJ2h0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9zdGF0L2RhdGEvaHNiMi5jc3YnLAogIGhlYWRlciA9IFRSVUUsCiAgc2VwID0gIiwiCiAgKSAtPiBoc2IyCgpmYWN0b3IoaHNiMiRmZW1hbGUsCiAgICAgICBsZXZlbHMgPSBjKDAsIDEpLAogICAgICAgbGFiZWxzID0gYygiTWFsZSIsICJGZW1hbGUiKQogICAgICAgKSAtPiBoc2IyJGZlbWFsZSAKCmZhY3Rvcihoc2IyJHJhY2UsCiAgICAgICBsZXZlbHMgPSBjKDE6NCksCiAgICAgICBsYWJlbHMgPSBjKCJIaXNwYW5pYyIsICJBc2lhbiIsICJBZnJpY2FuIEFtZXJpY2FuIiwgIldoaXRlIikKICAgICAgICkgLT4gaHNiMiRyYWNlCgpmYWN0b3IoaHNiMiRzZXMsCiAgICAgICBsZXZlbHMgPSBjKDE6MyksCiAgICAgICBsYWJlbHMgPSBjKCJMb3ciLCAiTWlkZGxlIiwgIkhpZ2giKQogICAgICAgKSAtPiBoc2IyJHNlcwoKZmFjdG9yKGhzYjIkc2NodHlwLAogICAgICAgbGV2ZWxzID0gYygxOjIpLAogICAgICAgbGFiZWxzID0gYygiUHVibGljIiwgIlByaXZhdGUiKQogICAgICAgKSAtPiBoc2IyJHNjaHR5cAoKZmFjdG9yKGhzYjIkcHJvZywKICAgICAgIGxldmVscyA9IGMoMTozKSwKICAgICAgIGxhYmVscyA9IGMoIkdlbmVyYWwiLCAiQWNhZGVtaWMiLCAiVm9jYXRpb25hbCIpCiAgICAgICApIC0+IGhzYjIkcHJvZwoKc2F2ZSgKICBoc2IyLCBmaWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICJoc2IyLlJEYXRhIikKICApCmBgYAoKYGBge3IgYmFyNS1iYXNlfQpnZ3Bsb3QoKSArIAogIGdlb21fYmFyKAogICAgZGF0YSA9IGhzYjIsCiAgICBtYXBwaW5nID0gYWVzKHggPSBzZXMpKSArCiAgbGFicyh4ID0gIlNvY2lvZWNvbm9taWMgU3RhdHVzIikKYGBgCgpPa2F5LCBmYWlyIGVub3VnaC4gQnV0IHdoYXQgaWYgSSB3YW50ZWQgdG8gc2VlIGhvdyBzb2Npb2Vjb25vbWljIHN0YXR1cyB2YXJpZXMgYWNyb3NzIG1hbGUgYW5kIGZlbWFsZSBzdHVkZW50cz8gIAoKYGBge3IgYmFyNX0KZ2dwbG90KCkgKyAKICBnZW9tX2JhcigKICAgIGRhdGEgPSBoc2IyLAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHNlcywKICAgICAgZ3JvdXAgPSBmZW1hbGUsIAogICAgICBmaWxsID0gZmVtYWxlCiAgICAgICkKICAgICkgKwogIGxhYnMoCiAgICB4ID0gIlNvY2lvZWNvbm9taWMgU3RhdHVzIiwKICAgIHkgPSAiRnJlcXVlbmN5IgogICkKYGBgCgpUaGlzIGlzIG5vdCB2ZXJ5IHVzZWZ1bCBzaW5jZSB0aGUgdmlld2VyIGhhcyB0byBlc3RpbWF0ZSB0aGUgcmVsYXRpdmUgc2l6ZXMgb2YgdGhlIHR3byBjb2xvcnMgd2l0aGluIGFueSBnaXZlbiBiYXIuIFRoYXQgY2FuIGJlIGZpeGVkIHdpdGggYHBvc2l0aW9uID0gImRvZGdlImAsIGp1eHRhcG9zaW5nIHRoZSBiYXJzIGZvciB0aGUgZ3JvdXBzIGFzIGEgcmVzdWx0LCBhbmQgdGhlIGVuZCBwcm9kdWN0IGlzIG11Y2ggYmV0dGVyLiBCdXQgbm90ZTogYHBvc2l0aW9uID0gImRvZGdlImAgaGFzIHRvIGJlIHB1dCBvdXRzaWRlIHRoZSBgYWVzKClgIGJ1dCBzdGlsbCBpbnNpZGUgYGdlb21fYmFyKClgIHNvIGJlIGNhcmVmdWwuIAoKYGBge3IgYmFyNn0KZ2dwbG90KCkgKyAKICBnZW9tX2JhcigKICAgIGRhdGEgPSBoc2IyLAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHNlcywKICAgICAgZ3JvdXAgPSBmZW1hbGUsIAogICAgICBmaWxsID0gZmVtYWxlCiAgICAgICksCiAgICBwb3NpdGlvbiA9ICJkb2RnZSIKICAgICkgKwogIGxhYnMoCiAgICB4ID0gIlNvY2lvZWNvbm9taWMgU3RhdHVzIiwKICAgIHkgPSAiRnJlcXVlbmN5IgogICkKYGBgCgpXaGF0IGlmIHlvdSB3YW50ZWQgdG8gY2FsY3VsYXRlIHBlcmNlbnRhZ2VzIHdpdGhpbiBlYWNoIHNleD8gVGhhdCBpcywgd2hhdCBwZXJjZW50IG9mIG1hbGUgc3R1ZGVudHMgZmFsbCB3aXRoaW4gYSBwYXJ0aWN1bGFyIHNlcyBjYXRlZ29yeSwgYW5kIHRoZSBzYW1lIHRoaW5nIGZvciBmZW1hbGUgc3R1ZGVudHM/IAoKYGBge3IgYmFyN30KZ2dwbG90KCkgKyAKICBnZW9tX2JhcigKICAgIGRhdGEgPSBoc2IyLCAKICAgIGFlcygKICAgICAgeCA9IHNlcywgCiAgICAgIGdyb3VwID0gZmVtYWxlLAogICAgICBmaWxsID0gZmVtYWxlLCAKICAgICAgeSA9IC4ucHJvcC4uCiAgICAgICksCiAgICBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIAogIGxhYnMoCiAgICB4ID0gIlNvY2lvZWNvbm9taWMgU3RhdHVzIiwKICAgIHkgPSAiUmVsYXRpdmUgRnJlcXVlbmN5ICglKSIKICAgICkKYGBgCgpXaGF0IGFib3V0IHdpdGhpbiBlYWNoIHNlcyBpbnN0ZWFkIG9mIHdpdGhpbiBnZW5kZXI/IFRoYXQgaXMsIHdoYXQgaWYgd2Ugd2FudGVkIHBlcmNlbnQgb2YgTG93IHNlcyB0aGF0IGlzIE1hbGUgdmVyc3VzIEZlbWFsZSwgYW5kIHNvIG9uPwoKYGBge3IgYmFyOH0KZ2dwbG90KCkgKyAKICBnZW9tX2JhcigKICAgIGRhdGEgPSBoc2IyLCAKICAgIGFlcygKICAgICAgeCA9IGZlbWFsZSwgCiAgICAgIGdyb3VwID0gc2VzLAogICAgICBmaWxsID0gc2VzLCAKICAgICAgeSA9IC4ucHJvcC4uCiAgICAgICksCiAgICBwb3NpdGlvbiA9ICJkb2RnZSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArIAogIGxhYnMoCiAgICB4ID0gIlNvY2lvZWNvbm9taWMgU3RhdHVzIiwKICAgIHkgPSAiUmVsYXRpdmUgRnJlcXVlbmN5ICglKSIKICAgICkKYGBgCgpgYGB7ciBiYXI5fQpnZ3Bsb3QoKSArIAogIGdlb21fYmFyKAogICAgZGF0YSA9IGhzYjIsIAogICAgYWVzKAogICAgICB4ID0gZmVtYWxlLCAKICAgICAgZ3JvdXAgPSBzZXMsCiAgICAgIGZpbGwgPSBzZXMsIAogICAgICB5ID0gLi5wcm9wLi4KICAgICAgKSwKICAgIHBvc2l0aW9uID0gImRvZGdlIikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsgCiAgbGFicygKICAgIHggPSAiU29jaW9lY29ub21pYyBTdGF0dXMiLAogICAgeSA9ICJSZWxhdGl2ZSBGcmVxdWVuY3kgKCUpIgogICAgKSAKYGBgCgpUaGVyZSBpcyBzb21lIG1vcmUgd2Ugd2lsbCBkbyB3aXRoIGJhci1jaGFydHMgYnV0IGZvciBub3cgbGV0IHVzIHNldCB0aGVtIGFzaWRlIGFuZCBpbnN0ZWFkIGxvb2sgYXQgYSBmZXcgb3RoZXIgY2hhcnRzIC0tIGhpc3RvZ3JhbXMsIGJveC1wbG90cywgYW5kIGxpbmUtY2hhcnRzLiAKCiMjIEhpc3RvZ3JhbXMgCklmIHlvdSd2ZSBmb3Jnb3R0ZW4gd2hhdCB0aGVzZSBhcmUsIHNlZSBbaGlzdG9ncmFtXShodHRwOi8vdGlubGl6emllLm9yZy9oaXN0b2dyYW1zLyksIG9yIHRoZW4gW1lhdSdzIHBpZWNlIGhlcmVdKGh0dHBzOi8vZmxvd2luZ2RhdGEuY29tLzIwMTQvMDIvMjcvaG93LXRvLXJlYWQtaGlzdG9ncmFtcy1hbmQtdXNlLXRoZW0taW4tci8pIGFuZCBbaGVyZV0oaHR0cHM6Ly9mbG93aW5nZGF0YS5jb20vMjAxNy8wNi8wNy9ob3ctaGlzdG9ncmFtcy13b3JrLykuIFtUaGVyZSBpcyBhIHNob3J0IHZpZGVvIGF2YWlsYWJsZSBhcyB3ZWxsXShodHRwczovL3ZpbWVvLmNvbS8yMjE2MDczNDEpLiAKCkZvciBoaXN0b2dyYW1zIGluIGdncGxvdDIsIGBnZW9tX2hpc3RvZ3JhbSgpYCBpcyB0aGUgZ2VvbWV0cnkgbmVlZGVkIGJ1dCBub3RlIHRoYXQgdGhlIGRlZmF1bHQgbnVtYmVyIG9mIGJpbnMgaXMgbm90IHZlcnkgdXNlZnVsIGFuZCBjYW4gYmUgdHdlYWtlZCwgYWxvbmcgd2l0aCBvdGhlciBlbWJlbGxpc2htZW50cyB0aGF0IGFyZSBwb3NzaWJsZSBhcyB3ZWxsLiAKCmBgYHtyIGdnMmF9CmdncGxvdCgpICsgCiAgZ2VvbV9oaXN0b2dyYW0oCiAgICBkYXRhID0gaHNiMiwKICAgIGFlcyh4ID0gcmVhZCksIAogICAgZmlsbCA9ICJjb3JuZmxvd2VyYmx1ZSIsCiAgICBjb2xvciA9ICJ3aGl0ZSIKICAgICkgKyAKICBsYWJzKAogICAgdGl0bGUgPSAiSGlzdG9ncmFtIG9mIFJlYWRpbmcgU2NvcmVzIiwKICAgIHggPSAiUmVhZGluZyBTY29yZSIsCiAgICB5ID0gIkZyZXF1ZW5jeSIKICAgICkKYGBgCgpOb3RlIHRoZSB3YXJuaW5nIGBzdGF0X2JpbigpYCB1c2luZyBgYmlucyA9IDMwYC4gUGljayBiZXR0ZXIgdmFsdWUgd2l0aCBgYmlud2lkdGhgLiBUaGlzIGlzIGJlY2F1c2UgbnVtZXJpY2FsIHZhcmlhYmxlcyBuZWVkIHRvIGJlIGdyb3VwZWQgaW4gb3JkZXIgdG8gaGF2ZSBtZWFuaW5nZnVsIGhpc3RvZ3JhbXMgd2UgY2FuIG1ha2Ugc2Vuc2Ugb2YuIEhvdyBkbyB5b3UgZGVmaW5lIHRoZSBiaW5zIChha2EgdGhlIGdyb3Vwcyk/IFdlIGNvdWxkIHNldCBgYmlucyA9IDVgIGFuZCB3ZSBjb3VsZCBhbHNvIGV4cGVyaW1lbnQgd2l0aCBgYmlud2lkdGggPWAuIExldCB1cyBkbyBgYmlucyA9IDVgIHdoaWNoIHdpbGwgc2F5IGdpdmUgdXMgNSBncm91cHMsIGFuZCBnbyBhaGVhZCBhbmQgY2FsY3VsYXRlIHRoZW0geW91cnNlbGYuIAoKYGBge3IgZ2cyYn0KZ2dwbG90KCkgKyAKICBnZW9tX2hpc3RvZ3JhbSgKICAgIGRhdGEgPSBoc2IyLAogICAgYWVzKHggPSByZWFkKSwgCiAgICBmaWxsID0gImNvcm5mbG93ZXJibHVlIiwKICAgIGNvbG9yID0gIndoaXRlIiwKICAgIGJpbnMgPSA1CiAgICApICsgCiAgbGFicygKICAgIHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBSZWFkaW5nIFNjb3JlcyIsCiAgICBzdWJ0aXRsZSA9ICIod2l0aCBiaW5zID0gKSIsCiAgICB4ID0gIlJlYWRpbmcgU2NvcmUiLAogICAgeSA9ICJGcmVxdWVuY3kiCiAgICApCmBgYAoKSWYgd2Ugd2FudGVkIG1vcmUvZmV3ZXIgYmlucyB3ZSBjb3VsZCB0d2VhayBpdCB1cCBvciBkb3duIGFzIG5lZWRlZC4gCldoYXQgYWJvdXQgYmlud2lkdGg/IFRoaXMgd2lsbCBzcGVjaWZ5IGhvdyB3aWRlIGVhY2ggZ3JvdXAgbXVzdCBiZS4gCgpgYGB7ciBnZzJjfQpnZ3Bsb3QoKSArIAogIGdlb21faGlzdG9ncmFtKAogICAgZGF0YSA9IGhzYjIsCiAgICBhZXMoeCA9IHJlYWQpLCAKICAgIGZpbGwgPSAiY29ybmZsb3dlcmJsdWUiLAogICAgY29sb3IgPSAid2hpdGUiLAogICAgYmlud2lkdGggPSA1CiAgICApICsgCiAgbGFicygKICAgIHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBSZWFkaW5nIFNjb3JlcyIsCiAgICBzdWJ0aXRsZSA9ICIod2l0aCBiaW53aWR0aCA9ICkiLAogICAgeCA9ICJSZWFkaW5nIFNjb3JlIiwKICAgIHkgPSAiRnJlcXVlbmN5IgogICAgKQpgYGAKCklmIHdlIHdhbnRlZCB0byBkaXNhZ2dyZWdhdGUgdGhlIGhpc3RvZ3JhbSBieSBvbmUgb3IgbW9yZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMsIHdlIGNvdWxkIGRvIHNvIHF1aXRlIGVhc2lseTogCgpgYGB7ciBnZzN9CmdncGxvdCgpICsgCiAgZ2VvbV9oaXN0b2dyYW0oCiAgICBkYXRhID0gaHNiMiwKICAgIGFlcyh4ID0gcmVhZCksIAogICAgZmlsbCA9ICJjb3JuZmxvd2VyYmx1ZSIsCiAgICBjb2xvciA9ICJ3aGl0ZSIsCiAgICBiaW5zID0gNQogICAgKSArIAogIGxhYnMoCiAgICB0aXRsZSA9ICJIaXN0b2dyYW0gb2YgUmVhZGluZyBTY29yZXMiLAogICAgc3VidGl0bGUgPSAiKGJyb2tlbiBvdXQgZm9yIE1hbGUgdnMuIEZlbWFsZSBzdHVkZW50cykiLAogICAgeCA9ICJSZWFkaW5nIFNjb3JlIiwKICAgIHkgPSAiRnJlcXVlbmN5IgogICAgKSArCiAgZmFjZXRfd3JhcCh+IGZlbWFsZSkKYGBgCgpXaGVuIHdlIGRvIHRoaXMsIGl0IGlzIG9mdGVuIHVzZWZ1bCB0byBvcmdhbml6ZSB0aGVtIHNvIHRoYXQgb25seSBvbmUgaGlzdG9ncmFtIHNob3dzIHVwIGluIGEgcm93LiBUaGlzIGlzIGRvbmUgd2l0aCB0aGUgYG5jb2wgPSAxYCBjb21tYW5kLiAKCmBgYHtyIGdnNGF9CmdncGxvdCgpICsgCiAgZ2VvbV9oaXN0b2dyYW0oCiAgICBkYXRhID0gaHNiMiwKICAgIGFlcyh4ID0gcmVhZCksIAogICAgZmlsbCA9ICJjb3JuZmxvd2VyYmx1ZSIsCiAgICBjb2xvciA9ICJ3aGl0ZSIsCiAgICBiaW5zID0gNQogICAgKSArIAogIGxhYnMoCiAgICB0aXRsZSA9ICJIaXN0b2dyYW0gb2YgUmVhZGluZyBTY29yZXMiLAogICAgc3VidGl0bGUgPSAiKGJyb2tlbiBvdXQgZm9yIE1hbGUgdnMuIEZlbWFsZSBzdHVkZW50cykiLAogICAgeCA9ICJSZWFkaW5nIFNjb3JlIiwKICAgIHkgPSAiRnJlcXVlbmN5IgogICAgKSArCiAgZmFjZXRfd3JhcCh+IGZlbWFsZSwgbmNvbCA9IDEpCmBgYAoKYGBge3IgZ2c0Yn0KZ2dwbG90KCkgKyAKICBnZW9tX2hpc3RvZ3JhbSgKICAgIGRhdGEgPSBoc2IyLAogICAgYWVzKHggPSByZWFkKSwgCiAgICBmaWxsID0gImNvcm5mbG93ZXJibHVlIiwKICAgIGNvbG9yID0gIndoaXRlIiwKICAgIGJpbnMgPSA1CiAgICApICsgCiAgbGFicygKICAgIHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBSZWFkaW5nIFNjb3JlcyIsCiAgICBzdWJ0aXRsZSA9ICIoYnJva2VuIG91dCBieSBTb2Npb2Vjb25vbWljIFN0YXR1cykiLAogICAgeCA9ICJSZWFkaW5nIFNjb3JlIiwKICAgIHkgPSAiRnJlcXVlbmN5IgogICAgKSArCiAgZmFjZXRfd3JhcCh+IHNlcywgbmNvbCA9IDEpCmBgYAoKTm93IHRoZSBkaXN0cmlidXRpb25zIGFyZSBzdGFja2VkIGFib3ZlIGVhY2gsIGVhc2luZyBjb21wYXJpc29uczsgZG8gdGhleSBoYXZlIHRoZSBzYW1lIGF2ZXJhZ2U/IERvIHRoZXkgdmFyeSB0aGUgc2FtZT8gQXJlIHRoZXkgc2ltaWxhcmx5IHNrZXdlZC9zeW1tZXRyaWM/LiAKCkZvciBicmVha291dHMgd2l0aCB0d28gY2F0ZWdvcmljYWwgdmFyaWFibGVzIHdlIGNvdWxkIGRvIAoKYGBge3IgZ2c1YX0KZ2dwbG90KCkgKyAKICBnZW9tX2hpc3RvZ3JhbSgKICAgIGRhdGEgPSBoc2IyLAogICAgYWVzKHggPSByZWFkKSwgCiAgICBmaWxsID0gImNvcm5mbG93ZXJibHVlIiwKICAgIGNvbG9yID0gIndoaXRlIiwKICAgIGJpbnMgPSA1CiAgICApICsgCiAgbGFicygKICAgIHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBSZWFkaW5nIFNjb3JlcyIsCiAgICBzdWJ0aXRsZSA9ICIoYnJva2VuIG91dCBieSBTb2Npb2Vjb25vbWljIFN0YXR1cyBhbmQgU2Nob29sIFR5cGUpIiwKICAgIHggPSAiUmVhZGluZyBTY29yZSIsCiAgICB5ID0gIkZyZXF1ZW5jeSIKICAgICkgKwogIGZhY2V0X3dyYXAoc2VzIH4gc2NodHlwLCBuY29sID0gMikKYGBgCgpOb3RlIHRoYXQgYHNlcyB+IHNjaHR5cGAgcmVuZGVycyB0aGUgcGFuZWxzIGZvciB0aGUgZmlyc3QgY2F0ZWdvcnkgb2YgYHNlc2AgYnkgYWxsIGNhdGVnb3JpZXMgb2Ygc2NodHlwIGFuZCB0aGVuIHJlcGVhdHMgZm9yIHRoZSBvdGhlciBjYXRlZ29yaWVzIGluIHJvd3MgMiBhbmQgMy4gSWYgd2UgZGlkIGBmYWNldF93cmFwKHNjaHR5cGUgfiBzZXMsIG5jb2wgPSAzKWAgd2Ugd291bGQgaGF2ZSBhIGRpZmZlcmVudCByZXN1bHQ6CgpgYGB7ciBnZzVifQpnZ3Bsb3QoKSArIAogIGdlb21faGlzdG9ncmFtKAogICAgZGF0YSA9IGhzYjIsCiAgICBhZXMoeCA9IHJlYWQpLCAKICAgIGZpbGwgPSAiY29ybmZsb3dlcmJsdWUiLAogICAgY29sb3IgPSAid2hpdGUiLAogICAgYmlucyA9IDUKICAgICkgKyAKICBsYWJzKAogICAgdGl0bGUgPSAiSGlzdG9ncmFtIG9mIFJlYWRpbmcgU2NvcmVzIiwKICAgIHN1YnRpdGxlID0gIihicm9rZW4gb3V0IGJ5IFNvY2lvZWNvbm9taWMgU3RhdHVzIGFuZCBTY2hvb2wgVHlwZSkiLAogICAgeCA9ICJSZWFkaW5nIFNjb3JlIiwKICAgIHkgPSAiRnJlcXVlbmN5IgogICAgKSArCiAgZmFjZXRfd3JhcChzY2h0eXAgfiBzZXMsIG5jb2wgPSAzKSArCiAgeWxpbShjKDAsIDIzKSkKYGBgCgpOb3RpY2UgdGhhdCBoZXJlIEkgYWxzbyBhZGQgYSBgeWxpbShjKC4uLikpYCBjb21tYW5kIHRvIHNldCB0aGUgbWluaW11bSBhbmQgbWF4aW11bSB2YWx1ZXMgb2YgdGhlIHktYXhpcy4gVGhpcyBpcyB1c2VmdWwsIGFuZCBJIHN1Z2dlc3QgeW91IGRvIG5vdCBmb3JnZXQgdG8gc2V0IHRoZSB5IGxpbWl0IHRvIHN0YXJ0IGF0IDAgb3IgdGhlbiBtYWtlIGEgbm90ZSBpbiB0aGUgcGxvdCBmb3IgcmVhZGVycyBzbyB0aGV5IGRvbid0IGFzc3VtZSBpdCBpcyBhdCAwIHdoZW4gaW4gZmFjdCBpdCBoYXMgYmVlbiB0cnVuY2F0ZWQgZm9yIGVhc2Ugb2YgZGF0YSBwcmVzZW50YXRpb24uIFRoaXMgbWlzc3RhdGVzIHRoZSBwYXR0ZXJuIGluIHRoZSBkYXRhLCBkbyBub3QgZG8gaXQgb3IgdGhlbiwgYWdhaW4sIGFubm90YXRlIHRoZSBwbG90IHRvIHRoYXQgZWZmZWN0IHNvIG5vYm9keSBpcyBtaXNsZWQuIEJhci1jaGFydHMgYW5kIGhpc3RvZ3JhbXMgd2lsbCBoYXZlIDAgYXMgdGhlIG1pbmltdW0geS1saW1pdCBidXQgdGhpcyBpcyBub3QgdHJ1ZSBmb3Igc29tZSBvdGhlciBwbG90cy4gCgoKIyMgQm94LXBsb3RzIApSZW1lbWJlciB0aGVzZSwgb3VyIGZyaWVuZHMgZnJvbSBNUEEgNjAxMD8gVGhlc2UgY2FuIGJlIHVzZWZ1bCBmb3Igc3R1ZHlpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIGNvbnRpbnVvdXMgdmFyaWFibGUuIFtTZWUgdGhpcyB2aWRlb10oaHR0cHM6Ly92aW1lby5jb20vMjIyMzU4MDM0KS4gTGV0IHVzIHNlZSB0aGVzZSBpbiBhY3Rpb24gd2l0aCB0aGUgYGNtaGZsaWdodHNgIGRhdGEuIAoKYGBge3IgYm94MWF9CmxvYWQoCiAgaGVyZTo6aGVyZSgiZGF0YSIsICJjbWhmbGlnaHRzXzAxMDkyMDE3LlJEYXRhIikKICApCgpnZ3Bsb3QoKSArIAogIGdlb21fYm94cGxvdCgKICAgIGRhdGEgPSBjbWhmbGlnaHRzLAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeSA9IEFyckRlbGF5LAogICAgICB4ID0gIiIKICAgICAgKSwKICAgIGZpbGwgPSAiY29ybmZsb3dlcmJsdWUiCiAgICApIApgYGAKCk5vdGU6IAoKKyB0aGUgYHggPSAiImAgaXMgaW4gYGFlcygpYCBiZWNhdXNlIG90aGVyd2lzZSB3aXRoIGEgc2luZ2xlIGdyb3VwIHRoZSBib3gtcGxvdCB3aWxsIG5vdCBidWlsZCB1cCBuaWNlbHkKCkJ1dCwgSSBwcmVmZXIgdG8gc2VlIHRoZW0gcnVubmluZyBob3Jpem9udGFsbHksIHNvIGhvdyBjYW4gSSBkbyB0aGF0PyBXaXRoIGBjb29yZF9mbGlwKClgIHNpbmNlIHRoaXMganVzdCBmbGlwcyB0aGUgY29sdW1ucy4gCgpgYGB7ciBib3gxYn0KZ2dwbG90KCkgKyAKICBnZW9tX2JveHBsb3QoCiAgICBkYXRhID0gY21oZmxpZ2h0cywKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHkgPSBBcnJEZWxheSwKICAgICAgeCA9ICIiCiAgICAgICksCiAgICBmaWxsID0gImNvcm5mbG93ZXJibHVlIgogICAgKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKQW5kIG5vdyBmb3IgYSBzbGlnaHRseSBkaWZmZXJlbnQgZGF0YS1zZXQsIG9uZSB0aGF0IG1lYXN1cmVzIG1hbGUgYWR1bHRzJyBoZW1vZ2xvYmluIGNvbmNlbnRyYXRpb24gZm9yIGEgZmV3IHBvcHVsYXRpb25zLiAKCmBgYHtyIGJveDJ9CnJlYWRfY3N2KAogICJodHRwOi8vd2hpdGxvY2tzY2hsdXRlci56b29sb2d5LnViYy5jYS93cC1jb250ZW50L2RhdGEvY2hhcHRlcjAyL2NoYXAwMmUzY0h1bWFuSGVtb2dsb2JpbkVsZXZhdGlvbi5jc3YiCiAgKSAtPiBoZW1vZ2xvYmluCgpnZ3Bsb3QoKSArCiAgZ2VvbV9ib3hwbG90KAogICAgZGF0YSA9IGhlbW9nbG9iaW4sCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gcG9wdWxhdGlvbiwKICAgICAgeSA9IGhlbW9nbG9iaW4sCiAgICAgIGZpbGwgPSBwb3B1bGF0aW9uCiAgICAgICkKICAgICkgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicygKICAgIHggPSAiUG9wdWxhdGlvbiIsCiAgICB5ID0gIkhlbW9nbG9iaW4gQ29uY2VudHJhdGlvbiIsCiAgICB0aXRsZSA9ICJIZW1vZ2xvYmluIENvbmNlbnRyYXRpb24gaW4gQWR1bHQgTWFsZXMiLAogICAgc3VidGl0bGUgPSAiKEFuZGVzLCBFdGhpb3BpYSwgVGliZXQsIFVTQSkiCiAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKTm90aWNlIHRoZSBuZWVkIGZvciBubyBsZWdlbmQgd2l0aCBgZmlsbCA9IHBvcHVsYXRpb25gIE5vdGljZSBhbHNvIGhvdyBgZmlsbCA9IGAgaXMgaW5zaWRlIGBhZXMoLi4uKWAgaGVyZSBiZWNhdXNlIHdlIGFyZSBhc2tpbmcgdGhhdCBlYWNoIHVuaXF1ZSB2YWx1ZSBzZWVuIGluIGEgdmFyaWFibGUgY2FsbGVkIGBwb3B1bGF0aW9uYCBiZSBtYXBwZWQgdG8gYSB1bmlxdWUgY29sb3IuIAoKQ291bGQgd2UgdXNlIG91ciBgZmFjZXRfd3JhcCguLi4pYCBoZXJlIHRvbz8gT2YgY291cnNlLiAKCmBgYHtyIGJveDFjfQpnZ3Bsb3QoKSArIAogIGdlb21fYm94cGxvdCgKICAgIGRhdGEgPSBjbWhmbGlnaHRzLAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeSA9IEFyckRlbGF5LAogICAgICB4ID0gQ2FycmllcgogICAgICApLAogICAgZmlsbCA9ICJjb3JuZmxvd2VyYmx1ZSIKICAgICkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZmFjZXRfd3JhcCh+IE1vbnRoKQpgYGAKCgojIyBMaW5lLWNoYXJ0cyAKSWYgd2UgaGF2ZSBkYXRhIG92ZXIgdGltZSBmb3Igb25lIG9yIG1vcmUgdW5pdHMsIHRoZW4gbGluZS1jaGFydHMgd29yayByZWFsbHkgd2VsbCB0byBleGhpYml0IHRyZW5kcy4gQSBjbGFzc2ljLCBjdXJyZW50IGV4YW1wbGUgd291bGQgYmUgdGhlIG51bWJlciBvZiBjb25maXJtZWQgQ09WSUQtMTkgY2FzZXMgcGVyIGNvdW50cnkgcGVyIGRhdGUuIEZvciBleGFtcGxlLCBzYXkgd2UgaGF2ZSBkYXRhIG9uIHRoZSB1bmVtcGxveW1lbnQgcmF0ZSBmb3IgdGhlIGNvdW50cnkuIFRoZXNlIGRhdGEgYXJlIGNvbWluZyBmcm9tIHRoZSBge3Bsb3RseX1gIGxpYnJhcnkgc28gd2UgaGF2ZSB0byBtYWtlIHN1cmUgaXQgaXMgaW5zdGFsbGVkIGFuZCBsb2FkIGl0LgoKYGBge3IgbGluZTF9CmxpYnJhcnkocGxvdGx5KQpkYXRhKGVjb25vbWljcykKbmFtZXMoZWNvbm9taWNzKQpnZ3Bsb3QoKSArCiAgZ2VvbV9saW5lKAogICAgZGF0YSA9IGVjb25vbWljcywgCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gZGF0ZSwKICAgICAgeSA9IHVlbXBtZWQKICAgICAgKQogICAgKSArIAogIGxhYnMoCiAgICB4ID0gIkRhdGUiLAogICAgeSA9ICJVbmVtcGxveW1lbnQgUmF0ZSIKICApCmBgYAoKVGhleSBjYW4gbG9vayB2ZXJ5IHBsYWluIGFuZCBhZXN0aGV0aWNhbGx5IHVuYXBwZWFsaW5nIHVubGVzcyB5b3UgZHJlc3MgdGhlbSB1cC4gU2VlIHRoZSBvbmUgYmVsb3cgYW5kIHRoZW4gdGhlIG9uZSB0aGF0IGZvbGxvd3MuIAoKYGBge3IgbGluZTJ9CmxvYWQoCiAgaGVyZTo6aGVyZSgiZGF0YSIsICJnYXAuZGYuUkRhdGEiKQogICkKCmdncGxvdCgpICsKICBnZW9tX2xpbmUoCiAgICBkYXRhID0gZ2FwLmRmLCAKICAgIG1hcHBpbmcgPSBhZXMoCiAgICAgIHggPSB5ZWFyLAogICAgICB5ID0gTGlmZUV4cCwKICAgICAgZ3JvdXAgPSBjb250aW5lbnQsCiAgICAgIGNvbG9yID0gY29udGluZW50CiAgICAgICkKICAgICkgKyAKICBnZW9tX3BvaW50KAogICAgZGF0YSA9IGdhcC5kZiwgCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0geWVhciwKICAgICAgeSA9IExpZmVFeHAsCiAgICAgIGdyb3VwID0gY29udGluZW50LAogICAgICBjb2xvciA9IGNvbnRpbmVudAogICAgICApCiAgICApICsgCiAgbGFicygKICAgIHggPSAiWWVhciIsCiAgICB5ID0gIk1lZGlhbiBMaWZlIEV4cGVjdGFuY3kgKGluIHllYXJzKSIsCiAgICBjb2xvciA9ICIiCiAgKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSAKYGBgCgojIyBTY2F0dGVyLXBsb3RzIApUaGVzZSB3b3JrIHdlbGwgaWYgd2UgaGF2ZSB0d28gb3IgbW9yZSBjb250aW51b3VzIHZhcmlhYmxlcywgYW5kIHdvcmsgd2VsbCB0byBoaWdobGlnaHQgdGhlIG5hdHVyZSBhbmQgc3RyZW5ndGggb2YgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcyAuLi4uIHdoYXQgaGFwcGVucyB0byBgeWAgYXMgYHhgIGluY3JlYXNlcz8gcwoKYGBge3Igc2MxfQpnZ3Bsb3QoKSArIAogIGdlb21fcG9pbnQoCiAgICBkYXRhID0gaHNiMiwgCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gd3JpdGUsCiAgICAgIHkgPSBzY2llbmNlCiAgICAgICkKICAgICkgKwogIGxhYnMoCiAgICB4ID0gIldyaXRpbmcgU2NvcmVzIiwgCiAgICB5ID0gIlNjaWVuY2UgU2NvcmVzIgogICAgKSAKYGBgCgpXZSBjb3VsZCBoaWdobGlnaHQgdGhlIGRpZmZlcmVudCBgc2VzYCBncm91cHMsIHRvIHNlZSBpZiB0aGVyZSBpcyBhbnkgZGlmZmVyZW5jZSBpbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gd3JpdGluZyBzY29yZXMgYW5kIHNjaWVuY2Ugc2NvcmVzIGJ5IHRoZSBkaWZmZXJlbnQgc2VzIGxldmVscy4gCgpgYGB7ciBzYzJ9CmdncGxvdCgpICsKICBnZW9tX3BvaW50KAogICAgZGF0YSA9IGhzYjIsCiAgICBtYXBwaW5nID0gYWVzKAogICAgICB4ID0gd3JpdGUsCiAgICAgIHkgPSBzY2llbmNlLAogICAgICBjb2xvciA9IHNlcwogICAgICApCiAgICApICsgCiAgbGFicygKICAgIHggPSAiV3JpdGluZyBTY29yZXMiLCAKICAgIHkgPSAiU2NpZW5jZSBTY29yZXMiLAogICAgY29sb3IgPSAiIgogICAgKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSAKYGBgCgpUaGlzIGlzIG5vdCB2ZXJ5IGhlbHBmdWwgc28gd2h5IG5vdCBicmVha291dCBzZXMgZm9yIGVhc2Ugb2YgaW50ZXJwcmV0YXRpb24/IAoKYGBge3Igc2MzfQpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSBoc2IyLAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHdyaXRlLAogICAgICB5ID0gc2NpZW5jZQogICAgICApCiAgICApICsgCiAgbGFicygKICAgIHggPSAiV3JpdGluZyBTY29yZXMiLCAKICAgIHkgPSAiU2NpZW5jZSBTY29yZXMiCiAgICApICsgCiAgZmFjZXRfd3JhcCh+IHNlcykgCmBgYAoKQ291bGQgd2UgYWRkIGFub3RoZXIgbGF5ZXIsIHBlcmhhcHMgYGZlbWFsZWA/IAoKYGBge3Igc2M0fQpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSBoc2IyLAogICAgbWFwcGluZyA9IGFlcygKICAgICAgeCA9IHdyaXRlLAogICAgICB5ID0gc2NpZW5jZQogICAgICApCiAgICApICsgCiAgbGFicygKICAgIHggPSAiV3JpdGluZyBTY29yZXMiLCAKICAgIHkgPSAiU2NpZW5jZSBTY29yZXMiCiAgICApICsgCiAgZmFjZXRfd3JhcChzZXMgfiBmZW1hbGUsIG5jb2wgPSAyKSAKYGBgCgpBbmQgZmluYWxseSwgYSBmZXcgc3VnZ2VzdGlvbiBhYm91dCBob3cgdG8gYnVpbGQgdXAgeW91ciB2aXN1YWxpemF0aW9uczogCgotIGByIGVtb2ppZm9udDo6ZW1vamkoJ3JlcGVhdCcpYCBzdGFydCB3aXRoIHBlbmNpbCBhbmQgcGFwZXIsIHNrZXRjaCBwcm90b3R5cGVzIG9mIGRlc2lyZWQgdmlzdWFsaXphdGlvbihzKQotIGByIGVtb2ppZm9udDo6ZW1vamkoJ3NtaWxlJylgIGdyYXBoaWNzIGFyZSByZWxhdGl2ZWx5IGVhc3kgdG8gZ2VuZXJhdGUgd2l0aCBiYXNlIFIgJiB3aXRoIGBnZ3Bsb3QyYCAKLSBgciBlbW9qaWZvbnQ6OmVtb2ppKCdjbGFwJylgIGNvbW1vbi1zZW5zZTogYG51bWJlcmAgJiBgdHlwZWAgb2YgdmFyaWFibGUocykgZ3VpZGUgcGxvdHRpbmcgCi0gYHIgZW1vamlmb250OjplbW9qaSgnc3BhcmtsZXInKWAgc3RheSBgY29sb3IgY29uc2Npb3VzYDogc2Vuc2libGUgY29sb3JzICYgc2Vuc2l0aXZlIHRvIGNvbG9yIGJsaW5kbmVzcwotIGByIGVtb2ppZm9udDo6ZW1vamkoJ2JlZ2lubmVyJylgIGV4cGVyaW1lbnQsIGV4cGVyaW1lbnQsIGV4cGVyaW1lbnQgdW50aWwgeW91IGFyZSBoYXBweSAKLSB1c2UgdGhlIGByIGVtb2ppZm9udDo6ZW1vamkoJ2ZyZWUnKWAgbGVhcm5pbmcgcmVzb3VyY2VzIGF2YWlsYWJsZSBvbmxpbmUgCi0gYHIgZW1vamlmb250OjplbW9qaSgnbGVkZ2VyJylgIGlmIHlvdSBsZWFybiBzb21ldGhpbmcgbmV3IGluIFIsIHdyaXRlIGl0IGRvd24gCgotLS0tLS0tLS0tCgojIFByYWN0aWNlIEV4ZXJjaXNlcyAKCiMjIE5vYmVsIFByaXplIFdpbm5lcnMgCkdlb3JnaW9zIEthcmFtYW5pcyBnYXRoZXJlZCBhbmQgc2hhcmVkIGRhdGEgb24gTm9iZWwgcHJpemUgd2lubmVycyBvdmVyIHRoZSB5ZWFycywgd2l0aCBhIGZhaXIgYW1vdW50IG9mIGRldGFpbCwgYW5kIHVzZWQgaW4gdGhlIGB0aWR5dHVlc2RheWAgc2VyaWVzIGEgd2hpbGUgYmFjay4gVGhlc2UgZGF0YSBhcmUgdG8gYmUgdXNlZCBmb3IgdGhlIHF1ZXN0aW9ucyB0aGF0IGZvbGxvdy4gCgpgYGB7ciBub2JlbC13aW5uZXJzfQpyZWFkcjo6cmVhZF9jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAxOS8yMDE5LTA1LTE0L25vYmVsX3dpbm5lcnMuY3N2IikgLT4gbm9iZWxfd2lubmVycyAKYGBgCgp8dmFyaWFibGUgICAgICAgICAgICAgfGNsYXNzICAgICB8ZGVzY3JpcHRpb24gfAp8Oi0tLXw6LS0tfDotLS0tLS0tLS0tLXwKfHByaXplX3llYXIgICAgICAgICAgIHxkb3VibGUgICAgfCBZZWFyIHRoYXQgTm9iZWwgUHJpemUgd2FzIGF3YXJkZWR8CnxjYXRlZ29yeSAgICAgICAgICAgICB8Y2hhcmFjdGVyIHwgRmllbGQgb2Ygc3R1ZHkvY2F0ZWdvcnl8Cnxwcml6ZSAgICAgICAgICAgICAgICB8Y2hhcmFjdGVyIHwgUHJpemUgTmFtZSB8Cnxtb3RpdmF0aW9uICAgICAgICAgICB8Y2hhcmFjdGVyIHwgTW90aXZhdGlvbiBvZiB0aGUgYXdhcmQgfAp8cHJpemVfc2hhcmUgICAgICAgICAgfGNoYXJhY3RlciB8IFNoYXJlIGVnIDEgb2YgMSwgMSBvZiAyLCAxIG9mIDQsIGV0YyB8CnxsYXVyZWF0ZV9pZCAgICAgICAgICB8ZG91YmxlICAgIHwgSUQgYXNzaWduZWQgdG8gZWFjaCB3aW5uZXIgfAp8bGF1cmVhdGVfdHlwZSAgICAgICAgfGNoYXJhY3RlciB8IEluZGl2aWR1YWwgb3Igb3JnYW5pemF0aW9uICB8CnxmdWxsX25hbWUgICAgICAgICAgICB8Y2hhcmFjdGVyIHwgbmFtZSBvZiB0aGUgd2lubmVyfAp8YmlydGhfZGF0ZSAgICAgICAgICAgfGRvdWJsZSAgICB8IGJpcnRoIGRhdGUgb2Ygd2lubmVyIHwKfGJpcnRoX2NpdHkgICAgICAgICAgIHxjaGFyYWN0ZXIgfCBiaXJ0aCBjaXR5L3N0YXRlIG9mIHdpbm5lciB8CnxiaXJ0aF9jb3VudHJ5ICAgICAgICB8Y2hhcmFjdGVyIHwgYmlydGggY291bnRyeSBvZiB3aW5uZXIgfAp8Z2VuZGVyICAgICAgICAgICAgICAgfGNoYXJhY3RlciB8IGJpbmFyeSBnZW5kZXIgb2YgdGhlIHdpbm5lciB8Cnxvcmdhbml6YXRpb25fbmFtZSAgICB8Y2hhcmFjdGVyIHwgb3JnYW5pemF0aW9uIG5hbWUgfAp8b3JnYW5pemF0aW9uX2NpdHkgICAgfGNoYXJhY3RlciB8IG9yZ2FuaXphdGlvbiBjaXR5IHwKfG9yZ2FuaXphdGlvbl9jb3VudHJ5IHxjaGFyYWN0ZXIgfCBvcmdhbml6YXRpb24gY291bnRyeSB8CnxkZWF0aF9kYXRlICAgICAgICAgICB8ZG91YmxlICAgIHwgZGVhdGggZGF0ZSBvZiB0aGUgd2lubmVyIChpZiBkZWFkKSB8CnxkZWF0aF9jaXR5ICAgICAgICAgICB8Y2hhcmFjdGVyIHwgZGVhdGggY2l0eSAoaWYgZGVhZCkgfAp8ZGVhdGhfY291bnRyeSAgICAgICAgfGNoYXJhY3RlciB8IGRlYXRoIGNvdW50cnkgKGlmIGRlYWQpIHwKCgooYSkgRmlyc3QgY3JlYXRlIGBub2JlbC5kZmAgdGhhdCBrZWVwcyBvbmx5IHJlY29yZHMgc3RhcnRpbmcgaW4gdGhlIHllYXIgMTk2MCwgYW5kIG9ubHkgZm9yIHRoZSAiUGh5c2ljcyIgY2F0ZWdvcnkuIE5vdyBnZW5lcmF0ZSBhbiBhcHByb3ByaWF0ZSBjaGFydCB0aGF0IHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2Ygd2lubmVycyBieSBgYmlydGhfY291bnRyeWAgCgooYikgTm93IGJyZWFrIHRoaXMgZGlzdHJpYnV0aW9uIG91dCBieSBgZ2VuZGVyYCB0byBzZWUgaG93IHdpbm5lcnMgYnkgY291bnRyeSBkaWZmZXJzIGFjcm9zcyBnZW5kZXIgCgooYykgTm93IGdvIGJhY2sgdG8gYG5vYmxlX3dpbm5lcnNgLCB0aGUgZnVsbCBkYXRhLXNldCwgYW5kIGNyZWF0ZSBhIHNpbXBsZSBwbG90IHRoYXQgc2hvd3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwcml6ZSB3aW5uZXJzIGJ5IGBkZWF0aF9jb3VudHJ5YCwgYGdlbmRlcmAsIGFuZCBgY2F0ZWdvcnlgICAKCgojIyBXYXRlciBsZXZlbHMgaW4gdGhlIEdyZWF0IExha2VzCgpEb3dubG9hZCB0aGUgbW9udGhseSBHcmVhdCBMYWtlcyB3YXRlciBsZXZlbCBkYXRhLXNldCBbU1BTUyBmb3JtYXQgZnJvbSBoZXJlXShodHRwczovL2FuaXJ1aGlsLmdpdGh1Yi5pby9hdnNyL3RlYWNoaW5nL2RhdGF2aXovZ3JlYXRsYWtlcy5zYXYpIGFuZCBbRXhjZWwgZm9ybWF0IGZyb20gaGVyZV0oaHR0cHM6Ly9hbmlydWhpbC5naXRodWIuaW8vYXZzci90ZWFjaGluZy9kYXRhdml6L2dyZWF0bGFrZXMueGxzeCkuICpOb3RlIHRoYXQgd2F0ZXIgbGV2ZWwgaXMgaW4gbWV0ZXJzLiogCgpZb3UgbWF5IHVzZSB0aGUgZm9sbG93aW5nIGNvbW1hbmQgdG8gcmVhZCBpbiB0aGUgZXhjZWwgZmlsZTogCgpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShyZWFkeGwpCnVybCA8LSAiaHR0cHM6Ly9hbmlydWhpbC5naXRodWIuaW8vYXZzci90ZWFjaGluZy9kYXRhdml6L2dyZWF0bGFrZXMueGxzeCIKZGVzdGZpbGUgPC0gImdyZWF0bGFrZXMueGxzeCIKY3VybDo6Y3VybF9kb3dubG9hZCh1cmwsIGRlc3RmaWxlKQpyZWFkX2V4Y2VsKGRlc3RmaWxlLCBjb2xfdHlwZXMgPSBjKCJkYXRlIiwgCiAgICAgIm51bWVyaWMiLCAibnVtZXJpYyIsICJudW1lcmljIiwgIm51bWVyaWMiLCAKICAgICAibnVtZXJpYyIpKSAtPiBncmVhdGxha2VzIApgYGAKCk5vdyB1c2UgYW4gYXBwcm9wcmlhdGUgY2hhcnQgdG8gc2hvdyB0aGUgd2F0ZXIgbGV2ZWwgZm9yIExha2UgU3VwZXJpb3IuIAoKIyMgQ291bnR5IEhlYWx0aCBSYW5raW5ncwpEb3dubG9hZCB0aGUgMjAxNyBDb3VudHkgSGVhbHRoIFJhbmtpbmdzIGRhdGEgW1NQU1MgZm9ybWF0IGZyb20gaGVyZV0oaHR0cHM6Ly9hbmlydWhpbC5naXRodWIuaW8vYXZzci90ZWFjaGluZy9kYXRhdml6L0NvdW50eUhlYWx0aFJhbmtpbmdzMjAxNy5zYXYpLCBbRXhjZWwgZm9ybWF0IGZyb20gaGVyZV0oaHR0cHM6Ly9hbmlydWhpbC5naXRodWIuaW8vYXZzci90ZWFjaGluZy9kYXRhdml6L0NvdW50eUhlYWx0aFJhbmtpbmdzMjAxNy54bHN4KSBhbmQgdGhlIFthY2NvbXBhbnlpbmcgY29kZWJvb2tdKGh0dHA6Ly93d3cuY291bnR5aGVhbHRocmFua2luZ3Mub3JnL3NpdGVzL2RlZmF1bHQvZmlsZXMvMjAxN1RyZW5kc0RvY3VtZW50YXRpb24ucGRmKS4gCgpUaGVzZSBkYXRhIGNhbiBiZSBkb3dubG9hZGVkIHdpdGggdGhlIGNvZGUgcHJvdmlkZWQgYmVsb3c6IAoKYGBge3IgZ3JlYXQtbGFrZXN9CmxpYnJhcnkocmVhZHhsKQp1cmwgPC0gImh0dHBzOi8vYW5pcnVoaWwuZ2l0aHViLmlvL2F2c3IvdGVhY2hpbmcvZGF0YXZpei9Db3VudHlIZWFsdGhSYW5raW5nczIwMTcueGxzeCIKZGVzdGZpbGUgPC0gIkNvdW50eUhlYWx0aFJhbmtpbmdzMjAxNy54bHN4IgpjdXJsOjpjdXJsX2Rvd25sb2FkKHVybCwgZGVzdGZpbGUpCnJlYWRfZXhjZWwoZGVzdGZpbGUpIC0+IGNoci5kZiAKYGBgCgpDb25zdHJ1Y3QgYXBwcm9wcmlhdGUgcGxvdHMgdGhhdCBzaG93cyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGZvbGxvd2luZyBwYWlycyBvZiB2YXJpYWJsZXMgCgooYSkgQWR1bHQgb2Jlc2l0eSBhbmQgSGlnaCBzY2hvb2wgZ3JhZHVhdGlvbiAKCihiKSBDaGlsZHJlbiBpbiBwb3ZlcnR5IGFuZCBIaWdoIHNjaG9vbCBncmFkdWF0aW9uIAoKKGMpIFByZXZlbnRhYmxlIGhvc3BpdGFsIHN0YXlzIGFuZCBVbmVtcGxveW1lbnQgcmF0ZSAKCgojIyBVbmVtcGxveW1lbnQgUmF0ZXMKVXNlIHRoZSB1bmVtcGxveW1lbnQgZGF0YSBnaXZlbiB0byB5b3UgYCh1bmVtcHJhdGUuUkRhdGEpYCBhbmQgY29uc3RydWN0IGFwcHJvcHJpYXRlIHBsb3RzIHRoYXQgc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIHVuZW1wbG95bWVudCByYXRlcyBhY3Jvc3MgeWVhcnMgZm9yIGVhY2ggb2YgdGhlIGZvdXIgZWR1Y2F0aW9uYWwgYXR0YWlubWVudCBncm91cHMuIAoKYGBge3J9CmxvYWQoCiAgaGVyZTo6aGVyZSgiZGF0YSIsICJ1bmVtcHJhdGUuUkRhdGEiKQogICkgLT4gdXJhdGUgCmBgYAoKQmUgc3VyZSB0byB1c2UgYSB1bmlxdWUgY29sb3IgZm9yIGVhY2ggZWR1Y2F0aW9uYWwgYXR0YWlubWVudCBncm91cAoK