1 Goals for Lab 01

  • Get your feet wet!
  • Innoculate you against ggplot2 errors- we all get them!
  • Get exposed to the range of things you can do, before we go deep
  • Develop your own personal preferences for data visualizations!
    • Do you like or hate gridlines?
    • What fonts do you find pleasant to read?
    • What kinds of colors do you like?
    • Are you team theme_gray or theme_bw (or theme_minimal)?

These are important questions, and I want you to develop (well-informed) opinions on these matters!

Most of the things we cover today, we will be re-visiting later in the term; you are not expected to be familiar with all of the R functions or patterns that we are using.

Other things to think about for this lab:

  • Try not to copy-and-paste code:
    • Becoming efficient/proficient with R depends on building muscle memory, and the only way to do that is to type
    • By typing, you will introduce errors, and this is a good chance to get practice at interpreting and fixing them.
  • Take note of steps or functions that involve bits of R that you are unfamiliar with- this lab will help you identify particular areas that you are comfortable with as well as areas you will want to focus on.

2 Nathan’s Hot Dog Eating Contest

This includes a reconstruction of Nathan Yau’s hot dog contest example, as interpreted by Jackie Wirz, ported into R and ggplot2 by Steven Bedrick for a workshop for the OHSU Data Science Institute, and finally adapted, made idiomatic, and improved by Alison Hill for all you intrepid Data-Viz-onauts!

First, we load our packages:

library(tidyverse)
library(extrafont)
library(here)

3 Read in and wrangle data

Next, we load some data. In the Posit Cloud project for this lab, you will see a data directory with the necessary files. We can read it in using read_csv, and along the way use col_factor to tell it how to handle the gender column.

hot_dogs <- read_csv(here::here("data", "hot_dog_contest.csv"), 
    col_types = cols(
      gender = col_factor(levels = NULL)
    ))

3.1 Validation

Check it out, once it is read in, and make sure it looks like this!

glimpse(hot_dogs)
Rows: 59
Columns: 4
$ year      <dbl> 2022, 2022, 2021, 2021, 2020, 2020, 2019, 2019, 2018, 2018, …
$ gender    <fct> male, female, male, female, male, female, male, female, male…
$ name      <chr> "Joey Chestnut", "Miki Sudo", "Joey Chestnut", "Michelle Les…
$ num_eaten <dbl> 63.00, 40.00, 76.00, 30.75, 75.00, 48.50, 71.00, 31.00, 74.0…
hot_dogs
# A tibble: 59 × 4
    year gender name           num_eaten
   <dbl> <fct>  <chr>              <dbl>
 1  2022 male   Joey Chestnut       63  
 2  2022 female Miki Sudo           40  
 3  2021 male   Joey Chestnut       76  
 4  2021 female Michelle Lesco      30.8
 5  2020 male   Joey Chestnut       75  
 6  2020 female Miki Sudo           48.5
 7  2019 male   Joey Chestnut       71  
 8  2019 female Miki Sudo           31  
 9  2018 male   Joey Chestnut       74  
10  2018 female Miki Sudo           37  
# … with 49 more rows

At this point, follow the HLO process and familiarize yourself with the columns and their contents. Questions to ask:

  1. Are the types of the columns what you would expect? Note that this requires that you have some notion of what to expect! Possible things to think about:
  2. What type makes sense for a column representing a year?
  3. What about the num_eaten column?
  4. What about the gender column? What assumptions
  5. What are the minimum and maximum values in the numeric columns? Are the values in the range you would expect?
  6. Are there any missing values?

In addition to glimpse(), try loading (not installing) the skimr package and using its skim() function on the hot_dogs data frame.

3.2 Adding a variable

In addition to the information that is already in the dataset itself, we know that we will also be wanting to somehow include information about whether a given year was before or after the incorporation of the competitive eating league. Let’s add an indicator variable to the data using mutate(). Also, the data’s a little sketchy pre-1981, and for our purposes today we’ll be focusing on males only, so let’s do some filtering, as well:

hot_dogs <- hot_dogs %>% 
  mutate(post_ifoce = year >= 1997) %>% 
  filter(year >= 1981 & gender == 'male')
hot_dogs
# A tibble: 42 × 5
    year gender name           num_eaten post_ifoce
   <dbl> <fct>  <chr>              <dbl> <lgl>     
 1  2022 male   Joey Chestnut         63 TRUE      
 2  2021 male   Joey Chestnut         76 TRUE      
 3  2020 male   Joey Chestnut         75 TRUE      
 4  2019 male   Joey Chestnut         71 TRUE      
 5  2018 male   Joey Chestnut         74 TRUE      
 6  2017 male   Joey Chestnut         72 TRUE      
 7  2016 male   Joey Chestnut         70 TRUE      
 8  2015 male   Matthew Stonie        62 TRUE      
 9  2014 male   Joey Chestnut         61 TRUE      
10  2013 male   Joey Chestnut         69 TRUE      
# … with 32 more rows

4 Plot The Data

Now let’s try making a first crack at a plot:

ggplot(hot_dogs, aes(x = year, y = num_eaten)) + 
  geom_col()

Note that our data is already in “counted” form, so we’re using geom_col() instead of geom_bar().

We will now progressively improve this visualization, one step at a time.

5 Add Axis Labels And Title

ggplot(hot_dogs, aes(x = year, y = num_eaten)) + 
  geom_col() +
  labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
  ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")

6 Play With Colors

Challenge #1:

Make 3 versions of the last plot we just made:

  • In the first, make all the columns outlined in “white”.
  • In the second, make all the columns outlined in “white” and filled in “navyblue”.
  • In the third, make all the columns outlined in “white” and filled in according to whether or not post_ifoce is TRUE or FALSE (use default colors for now).

HINT: color and fill are two of ggplot’s aesthetic mapping variables (i.e., “things about how the plot looks that we get to specify”)

ggplot(hot_dogs, aes(x = year, y = num_eaten)) + 
  geom_col(colour = "white") + 
  labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
  ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")

ggplot(hot_dogs, aes(x = year, y = num_eaten)) + 
  geom_col(colour = "white", fill = "navyblue") + 
  labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
  ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")

ggplot(hot_dogs, aes(x = year, y = num_eaten)) + 
  geom_col(aes(fill = post_ifoce), colour = "white") + 
  labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
  ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")

Challenge #2:

What if you want to change the legend in the last plot you made? Use google to figure out how to do the following:

  • Delete the legend title
  • Make the legend text either “Post-IFOCE” or “Pre-IFOCE”.

HINT: in ggplot, legends are controlled by the relevant scale (color, fill, etc.) that they are mapped to.

ggplot(hot_dogs, aes(x = year, y = num_eaten)) + 
  geom_col(aes(fill = post_ifoce), colour = "white") + 
  labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
  ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)") +
  scale_fill_discrete(name = "",
                      labels=c("Pre-IFOCE", "Post-IFOCE"))

7 Change The Dataset

Now, let’s change the question a little bit. Up to this point, we have looked at HDB performance relative to the creation of the IFOCE. What if what matters is the affiliation of the contestants (i.e., whether or not the contestants are members of the IFOCE or not)? We’ll need some different data for this. Through the Magic Of Data Science™, we have dug that information up and put it into an expanded version of our CSV file, which you can find in the data directory.

Challenge #3:

Let’s work with this new dataset! Do the following:

  • Read in the “hot_dog_contest_with_affiliation.csv” data file, using col_types to read in affiliated and gender as factors.

  • Within a mutate, create a new variable called post_ifoce that is TRUE if year is greater than or equal to 1997.

  • Also filter the new data for only years 1981 and after, and only for male competitors.

hdm_affil <- read_csv(here::here("data", "hot_dog_contest_with_affiliation.csv"), 
    col_types = cols(
      affiliated = col_factor(levels = NULL), 
      gender = col_factor(levels = NULL)
      )) %>% 
  mutate(post_ifoce = year >= 1997) %>% 
  filter(year >= 1981 & gender == "male") 
hdm_affil <- read_csv(here::here("data", "hot_dog_contest_with_affiliation.csv"), 
    col_types = cols(
      affiliated = col_factor(levels = NULL), 
      gender = col_factor(levels = NULL)
      )) %>% 
  mutate(post_ifoce = year >= 1997) %>% 
  filter(year >= 1981 & gender == "male") 
glimpse(hdm_affil)
Rows: 42
Columns: 6
$ year       <dbl> 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013,…
$ gender     <fct> male, male, male, male, male, male, male, male, male, male,…
$ name       <chr> "Joey Chestnut", "Joey Chestnut", "Joey Chestnut", "Joey Ch…
$ num_eaten  <dbl> 63.000, 76.000, 75.000, 71.000, 74.000, 72.000, 70.000, 62.…
$ affiliated <fct> current, current, current, current, current, current, curre…
$ post_ifoce <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,…
Challenge #4:

Let’s do some basic EDA with this new dataset! Do the following:

  • Use dplyr::distinct to figure out how many unique values there are of affiliated.

  • Use dplyr::count to count the number of rows for each unique value of affiliated; use ?count to figure out how to sort the counts in descending order.

hdm_affil %>% 
  distinct(affiliated)
# A tibble: 3 × 1
  affiliated    
  <fct>         
1 current       
2 former        
3 not affiliated
hdm_affil %>% 
  count(affiliated, sort = TRUE)
# A tibble: 3 × 2
  affiliated         n
  <fct>          <int>
1 not affiliated    20
2 current           16
3 former             6

Now let’s plot this new data, and fill the columns according to our new affiliated column.

ggplot(hdm_affil, aes(x = year, y = num_eaten)) + 
  geom_col(aes(fill = affiliated)) + 
  labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
  ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)")

Challenge #5:

Do the following updates to the last plot we just made:

  • Update the colors using hex colors: c('#E9602B','#2277A0','#CCB683').

  • Change the legend title to “IFOCE-affiliation”.

  • Save this plot object as “affil_plot”.

affil_plot <- ggplot(hdm_affil, aes(x = year, y = num_eaten)) + 
  geom_col(aes(fill = affiliated)) + 
  labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
  ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)") +
  scale_fill_manual(values = c('#E9602B','#2277A0','#CCB683'),
                    name = "IFOCE-affiliation")
affil_plot

8 Play With Scales & Coordinates

Now that the bones of the plot are in place, it’s time to tweak the details.

The spacing’s a little funky down near the origin of the plot. The documentation tells us that the defaults are c(0.05, 0) for continuous variables. The first number is multiplicative and the second is additive.

The default was that 2.05 ((2022-1981)*.05+0) was added to the right and left sides of the x-axis as padding, so the effective default limits were c(1979, 2024).

Let’s tighten that up with the expand property for the scale_y_continuous (we’ll also change the breaks for y-axis tick marks here) and scale_x_continuous settings:

affil_plot <- affil_plot + 
  scale_y_continuous(expand = c(0, 0),
                     breaks = seq(0, 70, 10)) +
  scale_x_continuous(expand = c(0, 0))
affil_plot

That is perhaps too tight; note the lack of any space between the bars and the y-axis on hte left.

Let’s loosen things up a bit by updating the plot coordinates.

Challenge #6:

Use coord_cartesian to:

  • Set the x-axis range to 1980-2023

  • Set the y-axis range to 0-80

Using coord_cartesian is the preferred layer here because (from the coord_cartesian documentation): “setting limits on the coordinate system will zoom the plot (like you’re looking at it with a magnifying glass), and will not change the underlying data like setting limits on a scale will.”

In other words, setting limits will actually result in individual data points being included or excluded from the plot based on whether they fall within the specified limits, which could have unanticipated effects (for example, if your plot includes a line fit, that line fit will be done using only the included data rather than all of your data).

Lesson:
Don’t directly change limits unless you really know what you are doing! Most of the time, you want to change the coordinates instead, and do any data point filtering outside of your plotting.
affil_plot <- affil_plot + 
  coord_cartesian(xlim = c(1980, 2023), ylim = c(0, 80)) 
affil_plot

9 Play With Theme Settings

We will talk a lot more about themes and ggplot later in the term, but for now, the important thing to know is that most visual aspects of the plot have a name (e.g. plot.title), and the theme() function lets us tell ggplot what any named part of the plot should look like.

Let’s change some key theme settings:

affil_plot +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme(axis.text = element_text(size = 12)) +
  theme(panel.background = element_blank()) +
  theme(axis.line.x = element_line(color = "gray80", size = 0.5)) +
  theme(axis.ticks = element_line(color = "gray80", size = 0.5))
Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
ℹ Please use the `linewidth` argument instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.

Lesson:
You can change almost anything that your heart desires to change!

By default, plot titles in ggplot2 are left-aligned. For hjust:

  • 0 == left
  • 0.5 == centered
  • 1 == right

We could also save all these as a custom theme. We are not fans of the default font, so we are also going to change this. To do this, you need to install the (extrafont package)[https://github.com/wch/extrafont] and follow its setup instructions before doing this next step.

hot_diggity <- theme(plot.title = element_text(hjust = 0.5),
                     axis.text = element_text(size = 12),
                     panel.background = element_blank(),
                     axis.line.x = element_line(color = "gray80", size = 0.5),
                     axis.ticks = element_line(color = "gray80", size = 0.5),
                     text = element_text(family = "Lato") # need extrafont for this
                     )
affil_plot + hot_diggity 

We could also use someone else’s theme:

library(ggthemes)
affil_plot + theme_fivethirtyeight(base_family = "Lato")

affil_plot + theme_tufte( base_family = "Palatino")

The final thing we have to mess with is the x-axis ticks and labels. We’ll do this in two steps, then override our previous layer scale_x_continuous.

# manually compute a list of years that we want labeled...
years_to_label <- seq(from = 1981, to = 2022, by = 4) 
years_to_label
 [1] 1981 1985 1989 1993 1997 2001 2005 2009 2013 2017 2021
# add a column to the dataframe containing what we want each year's label to be
hd_years <- hdm_affil %>%
  distinct(year) %>% 
  mutate(year_lab = ifelse(year %in% years_to_label, year, ""))
# manually tell ggplot what to use for breaks and labels
affil_plot + 
  hot_diggity +
  scale_x_continuous(expand = c(0, 0), 
                     breaks = hd_years$year,
                     labels = hd_years$year_lab)
Scale for x is already present.
Adding another scale for x, which will replace the existing scale.

10 Final (final, final) version

Don’t name your files “final” :)

All together in one chunk, here is our final (for now) plot! I’m also adding some additional elements here to show you options:

nathan_plot <- ggplot(hdm_affil, aes(x = year, y = num_eaten)) + 
  geom_col(aes(fill = affiliated)) + 
  labs(x = "Year", y = "Hot Dogs and Buns Consumed") +
  ggtitle("Nathan's Hot Dog Eating Contest Results, 1981-2022", subtitle = "(Male contestants only)") +
  scale_fill_manual(values = c('#E9602B','#2277A0','#CCB683'),
                    name = "IFOCE-affiliation") + 
  hot_diggity +
  scale_y_continuous(expand = c(0, 0),
                     breaks = seq(0, 70, 10)) +
  scale_x_continuous(expand = c(0, 0), 
                     breaks = hd_years$year,
                     labels = hd_years$year_lab) + 
  coord_cartesian(xlim = c(1980, 2023), ylim = c(0, 80)) 
nathan_plot

The fill legend is doing its job, here, but we might instead want to use direct annotations on the plot itself, to make it easier and faster to read.

ggplot will let us add annotatations to the plot- i.e., extra text, lines , etc. that are not derived from the data in the plot, but are manually specified - using the annotate() function, which adds additional layers to the plot. The way we are doing it below is a bit tedious, but demonstrates how it works.

nathan_ann <- nathan_plot +
  guides(fill="none") + # turn off the legend/guide for the "fill" aesthetic
  coord_cartesian(xlim = c(1980, 2023), ylim = c(0, 90)) +
  annotate('segment', x=1980.75, xend=2000.25, y= 30, yend=30, linewidth=0.5, color="#CCB683")+
  annotate('segment', x=1980.75, xend=1980.75, y= 30, yend=28, linewidth=0.5, color="#CCB683") +
  annotate('segment', x=2000.25, xend=2000.25, y= 30, yend=28, linewidth=0.5, color="#CCB683") +
  annotate('segment', x=1990, xend=1990, y= 33, yend=30, linewidth=0.5, color="#CCB683") +
  annotate('text', x=1990, y=36, label="No MLE/IFOCE Affiliation", color="#CCB683", family="Lato", hjust=0.5, size = 3) +



  annotate('segment', x=2000.75, xend=2006.25, y= 58, yend=58, linewidth=0.5, color="#2277A0") +
  annotate('segment', x=2000.75, xend=2000.75, y= 58, yend=56, linewidth=0.5, color="#2277A0") +
  annotate('segment', x=2006.25, xend=2006.25, y= 58, yend=56, linewidth=0.5, color="#2277A0") +
  annotate('segment', x=2003.5, xend=2003.5, y= 61, yend=58, linewidth=0.5, color="#2277A0") +
  annotate('text', x=2003.5, y=65, label="MLE/IFOCE\nFormer Member", color="#2277A0", family="Lato", hjust=0.5, size = 3) +


  annotate('segment', x=2006.75, xend=2022.25, y= 79, yend=79, linewidth=0.5, color="#E9602B") +
  annotate('segment', x=2006.75, xend=2006.75, y= 79, yend=77, linewidth=0.5, color="#E9602B") +
  annotate('segment', x=2022.25, xend=2022.25, y= 79, yend=77, linewidth=0.5, color="#E9602B") +
  annotate('segment', x=2015, xend=2015, y= 82, yend=79, linewidth=0.5, color="#E9602B") +
  annotate('text', x=2015, y=86, label="MLE/IFOCE Current Member", color="#E9602B", family="Lato", hjust=0.5, size = 3)
Coordinate system already present. Adding new coordinate system, which will
replace the existing one.
nathan_ann

Finally, adding in another layer of data, including information about female contestants:

hdm_females <- read_csv(here::here("data", "hot_dog_contest_with_affiliation.csv"), 
    col_types = cols(
      affiliated = col_factor(levels = NULL), 
      gender = col_factor(levels = NULL)
      )) %>% 
  mutate(post_ifoce = year >= 1997) %>% 
  filter(year >= 1981 & gender == "female") 
glimpse(hdm_females)
Rows: 12
Columns: 6
$ year       <dbl> 2022, 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013,…
$ gender     <fct> female, female, female, female, female, female, female, fem…
$ name       <chr> "Miki Sudo", "Michelle Lesco", "Miki Sudo", "Miki Sudo", "M…
$ num_eaten  <dbl> 40.00, 30.75, 48.50, 31.00, 37.00, 41.00, 38.00, 38.00, 34.…
$ affiliated <fct> current, current, current, current, current, current, curre…
$ post_ifoce <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,…
nathan_w_females <- nathan_ann +
  # add in the female data, and manually set a fill color
  geom_col(data = hdm_females, 
           width = 0.75, 
           fill = "#F68A39") +
  labs(subtitle = NULL) # no longer need the subtitle warning about male-only data!
nathan_w_females

And adding a final caption:

caption <- paste(strwrap("* From 2011 on, separate Men's and Women's prizes have been awarded. All female champions to date have been MLE/IFOCE-affiliated.", 70), collapse="\n")

nathan_w_females +
  # now an asterisk to set off the female scores, and a caption
  annotate('text', x = 2011, y = 36, label="*", family = "Lato", size = 8) +
  labs(caption = caption) +
  theme(plot.caption = element_text(family = "Lato", size=8, hjust=0, margin=margin(t=15)))

LS0tCnRpdGxlOiAiTGFiIDAxOiBOYXRoYW4ncyBIb3QtRG9nIEVhdGluZyBDb250ZXN0IgpzdWJ0aXRsZTogIkJNSSA1LzYyNSIKYXV0aG9yOiAiQWxpc29uIEhpbGwgKHdpdGggbWlub3IgbW9kaWZpY2F0aW9ucyBieSBTdGV2ZW4gQmVkcmljaykiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKICAgIHRvY19kZXB0aDogMgogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFLCBjYWNoZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3IgPSBUUlVFLCBjb21tZW50ID0gTkEsIHdhcm5pbmdzID0gRkFMU0UsIGVycm9ycyA9IEZBTFNFLCBtZXNzYWdlcyA9IEZBTFNFLCB0aWR5ID0gRkFMU0UsIGNhY2hlID0gVFJVRSkKYGBgCgpgYGB7ciBsb2FkLXBhY2thZ2VzLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGV4dHJhZm9udCkKYGBgCgojIEdvYWxzIGZvciBMYWIgMDEKCi0gR2V0IHlvdXIgZmVldCB3ZXQhCi0gSW5ub2N1bGF0ZSB5b3UgYWdhaW5zdCBgZ2dwbG90MmAgZXJyb3JzLSB3ZSBhbGwgZ2V0IHRoZW0hCi0gR2V0IGV4cG9zZWQgdG8gdGhlICpyYW5nZSogb2YgdGhpbmdzIHlvdSBjYW4gZG8sIGJlZm9yZSB3ZSBnbyAqKmRlZXAqKi4uLgotIERldmVsb3AgeW91ciAqb3duKiAqKnBlcnNvbmFsKiogcHJlZmVyZW5jZXMgZm9yIGRhdGEgdmlzdWFsaXphdGlvbnMhCiAgICAtIERvIHlvdSBsaWtlIG9yIGhhdGUgZ3JpZGxpbmVzPwogICAgLSBXaGF0IGZvbnRzIGRvIHlvdSBmaW5kIHBsZWFzYW50IHRvIHJlYWQ/CiAgICAtIFdoYXQga2luZHMgb2YgY29sb3JzIGRvIHlvdSBsaWtlPwogICAgLSBBcmUgeW91IHRlYW0gYHRoZW1lX2dyYXlgIG9yIGB0aGVtZV9id2AgKG9yIGB0aGVtZV9taW5pbWFsYCk/CiAgICAKVGhlc2UgYXJlIGltcG9ydGFudCBxdWVzdGlvbnMsIGFuZCBJIHdhbnQgeW91IHRvIGRldmVsb3AgKHdlbGwtaW5mb3JtZWQpIG9waW5pb25zIG9uIHRoZXNlIG1hdHRlcnMhCiFbXShpbWFnZXMvdGhlbWUtdGVhbS10d2VldHMucG5nKQoKTW9zdCBvZiB0aGUgdGhpbmdzIHdlIGNvdmVyIHRvZGF5LCB3ZSB3aWxsIGJlIHJlLXZpc2l0aW5nIGxhdGVyIGluIHRoZSB0ZXJtOyB5b3UgYXJlIG5vdCBleHBlY3RlZCB0byBiZSBmYW1pbGlhciB3aXRoIGFsbCBvZiB0aGUgUiBmdW5jdGlvbnMgb3IgcGF0dGVybnMgdGhhdCB3ZSBhcmUgdXNpbmcuCgpPdGhlciB0aGluZ3MgdG8gdGhpbmsgYWJvdXQgZm9yIHRoaXMgbGFiOgoKLSBUcnkgbm90IHRvIGNvcHktYW5kLXBhc3RlIGNvZGU6CiAgLSBCZWNvbWluZyBlZmZpY2llbnQvcHJvZmljaWVudCB3aXRoIFIgZGVwZW5kcyBvbiBidWlsZGluZyBtdXNjbGUgbWVtb3J5LCBhbmQgdGhlIG9ubHkgd2F5IHRvIGRvIHRoYXQgaXMgdG8gdHlwZQogIC0gQnkgdHlwaW5nLCB5b3UgX3dpbGxfIGludHJvZHVjZSBlcnJvcnMsIGFuZCB0aGlzIGlzIGEgZ29vZCBjaGFuY2UgdG8gZ2V0IHByYWN0aWNlIGF0IGludGVycHJldGluZyBhbmQgZml4aW5nIHRoZW0uCi0gVGFrZSBub3RlIG9mIHN0ZXBzIG9yIGZ1bmN0aW9ucyB0aGF0IGludm9sdmUgYml0cyBvZiBSIHRoYXQgeW91IGFyZSB1bmZhbWlsaWFyIHdpdGgtIHRoaXMgbGFiIHdpbGwgaGVscCB5b3UgaWRlbnRpZnkgcGFydGljdWxhciBhcmVhcyB0aGF0IHlvdSBhcmUgY29tZm9ydGFibGUgd2l0aCBhcyB3ZWxsIGFzIGFyZWFzIHlvdSB3aWxsIHdhbnQgdG8gZm9jdXMgb24uCgojIE5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QKCiFbXShodHRwczovL2kwLndwLmNvbS9mbG93aW5nZGF0YS5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMDkvMDYvaG90LWRvZ3MxLmdpZj96b29tPTImZml0PTkwMCUyQzQyMykKClRoaXMgaW5jbHVkZXMgYSByZWNvbnN0cnVjdGlvbiBvZiBbTmF0aGFuIFlhdSdzIGhvdCBkb2cgY29udGVzdCBleGFtcGxlXShodHRwOi8vZmxvd2luZ2RhdGEuY29tLzIwMDkvMDcvMDIvd2hvcy1nb2luZy10by13aW4tbmF0aGFucy1ob3QtZG9nLWVhdGluZy1jb250ZXN0L2hvdC1kb2dzLTIvKSwgYXMgaW50ZXJwcmV0ZWQgYnkgSmFja2llIFdpcnosIHBvcnRlZCBpbnRvIFIgYW5kIGBnZ3Bsb3QyYCBieSBTdGV2ZW4gQmVkcmljayBmb3IgYSB3b3Jrc2hvcCBmb3IgdGhlIFtPSFNVIERhdGEgU2NpZW5jZSBJbnN0aXR1dGVdKGh0dHBzOi8vb2hzdWxpYnJhcnktZGF0YXNjaWVuY2VpbnN0aXR1dGUuZ2l0aHViLmlvKSwgYW5kIGZpbmFsbHkgYWRhcHRlZCwgbWFkZSBpZGlvbWF0aWMsIGFuZCBpbXByb3ZlZCBieSBBbGlzb24gSGlsbCBmb3IgYWxsIHlvdSBpbnRyZXBpZCBEYXRhLVZpei1vbmF1dHMhCgoKRmlyc3QsIHdlIGxvYWQgb3VyIHBhY2thZ2VzOiAKCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZXh0cmFmb250KQpsaWJyYXJ5KGhlcmUpCmBgYAoKCiMgUmVhZCBpbiBhbmQgd3JhbmdsZSBkYXRhCgpOZXh0LCB3ZSBsb2FkIHNvbWUgZGF0YS4gSW4gdGhlIFBvc2l0IENsb3VkIHByb2plY3QgZm9yIHRoaXMgbGFiLCB5b3Ugd2lsbCBzZWUgYSBgZGF0YWAgZGlyZWN0b3J5IHdpdGggdGhlIG5lY2Vzc2FyeSBmaWxlcy4gV2UgY2FuIHJlYWQgaXQgaW4gdXNpbmcgYHJlYWRfY3N2YCwgYW5kIGFsb25nIHRoZSB3YXkgdXNlIGBjb2xfZmFjdG9yYCB0byB0ZWxsIGl0IGhvdyB0byBoYW5kbGUgdGhlIGdlbmRlciBjb2x1bW4uCgpgYGB7cn0KaG90X2RvZ3MgPC0gcmVhZF9jc3YoaGVyZTo6aGVyZSgiZGF0YSIsICJob3RfZG9nX2NvbnRlc3QuY3N2IiksIAogICAgY29sX3R5cGVzID0gY29scygKICAgICAgZ2VuZGVyID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKQogICAgKSkKYGBgCgojIyBWYWxpZGF0aW9uCgpDaGVjayBpdCBvdXQsIG9uY2UgaXQgaXMgcmVhZCBpbiwgYW5kIG1ha2Ugc3VyZSBpdCBsb29rcyBsaWtlIHRoaXMhCgpgYGB7cn0KZ2xpbXBzZShob3RfZG9ncykKaG90X2RvZ3MKYGBgCgpBdCB0aGlzIHBvaW50LCBmb2xsb3cgdGhlIEhMTyBwcm9jZXNzIGFuZCBmYW1pbGlhcml6ZSB5b3Vyc2VsZiB3aXRoIHRoZSBjb2x1bW5zIGFuZCB0aGVpciBjb250ZW50cy4gUXVlc3Rpb25zIHRvIGFzazoKCjEuIEFyZSB0aGUgdHlwZXMgb2YgdGhlIGNvbHVtbnMgd2hhdCB5b3Ugd291bGQgZXhwZWN0PyBOb3RlIHRoYXQgdGhpcyByZXF1aXJlcyB0aGF0IHlvdSBoYXZlIHNvbWUgbm90aW9uIG9mIHdoYXQgdG8gZXhwZWN0ISBQb3NzaWJsZSB0aGluZ3MgdG8gdGhpbmsgYWJvdXQ6CiAgMS4gV2hhdCB0eXBlIG1ha2VzIHNlbnNlIGZvciBhIGNvbHVtbiByZXByZXNlbnRpbmcgYSB5ZWFyPwogIDIuIFdoYXQgYWJvdXQgdGhlIGBudW1fZWF0ZW5gIGNvbHVtbj8KICAzLiBXaGF0IGFib3V0IHRoZSBgZ2VuZGVyYCBjb2x1bW4/IFdoYXQgYXNzdW1wdGlvbnMKMi4gV2hhdCBhcmUgdGhlIG1pbmltdW0gYW5kIG1heGltdW0gdmFsdWVzIGluIHRoZSBudW1lcmljIGNvbHVtbnM/IEFyZSB0aGUgdmFsdWVzIGluIHRoZSByYW5nZSB5b3Ugd291bGQgZXhwZWN0PwozLiBBcmUgdGhlcmUgYW55IG1pc3NpbmcgdmFsdWVzPwoKSW4gYWRkaXRpb24gdG8gYGdsaW1wc2UoKWAsIHRyeSBsb2FkaW5nIChub3QgaW5zdGFsbGluZykgdGhlIGBza2ltcmAgcGFja2FnZSBhbmQgdXNpbmcgaXRzIGBza2ltKClgIGZ1bmN0aW9uIG9uIHRoZSBgaG90X2RvZ3NgIGRhdGEgZnJhbWUuCgojIyBBZGRpbmcgYSB2YXJpYWJsZQoKSW4gYWRkaXRpb24gdG8gdGhlIGluZm9ybWF0aW9uIHRoYXQgaXMgYWxyZWFkeSBfaW5fIHRoZSBkYXRhc2V0IGl0c2VsZiwgd2Uga25vdyB0aGF0IHdlIHdpbGwgYWxzbyBiZSB3YW50aW5nIHRvIHNvbWVob3cgaW5jbHVkZSBpbmZvcm1hdGlvbiBhYm91dCB3aGV0aGVyIGEgZ2l2ZW4geWVhciB3YXMgYmVmb3JlIG9yIGFmdGVyIHRoZSBpbmNvcnBvcmF0aW9uIG9mIHRoZSBjb21wZXRpdGl2ZSBlYXRpbmcgbGVhZ3VlLiBMZXQncyBhZGQgYW4gX2luZGljYXRvciB2YXJpYWJsZV8gdG8gdGhlIGRhdGEgdXNpbmcgYG11dGF0ZSgpYC4gQWxzbywgdGhlIGRhdGEncyBhIGxpdHRsZSBza2V0Y2h5IHByZS0xOTgxLCBhbmQgZm9yIG91ciBwdXJwb3NlcyB0b2RheSB3ZSdsbCBiZSBmb2N1c2luZyBvbiBtYWxlcyBvbmx5LCBzbyBsZXQncyBkbyBzb21lIGBmaWx0ZXJgaW5nLCBhcyB3ZWxsOgoKYGBge3J9CmhvdF9kb2dzIDwtIGhvdF9kb2dzICU+JSAKICBtdXRhdGUocG9zdF9pZm9jZSA9IHllYXIgPj0gMTk5NykgJT4lIAogIGZpbHRlcih5ZWFyID49IDE5ODEgJiBnZW5kZXIgPT0gJ21hbGUnKQpob3RfZG9ncwpgYGAKCgoKIyBQbG90IFRoZSBEYXRhCgpOb3cgbGV0J3MgdHJ5IG1ha2luZyBhIGZpcnN0IGNyYWNrIGF0IGEgcGxvdDoKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woKQpgYGAKCk5vdGUgdGhhdCBvdXIgZGF0YSBpcyBhbHJlYWR5IGluICJjb3VudGVkIiBmb3JtLCBzbyB3ZSdyZSB1c2luZyBgZ2VvbV9jb2woKWAgaW5zdGVhZCBvZiBgZ2VvbV9iYXIoKWAuCgpXZSB3aWxsIG5vdyBwcm9ncmVzc2l2ZWx5IGltcHJvdmUgdGhpcyB2aXN1YWxpemF0aW9uLCBvbmUgc3RlcCBhdCBhIHRpbWUuCgojIEFkZCBBeGlzIExhYmVscyBBbmQgVGl0bGUKCgpgYGB7cn0KZ2dwbG90KGhvdF9kb2dzLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKCkgKwogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMiIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikKYGBgCgojIFBsYXkgV2l0aCBDb2xvcnMKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXN1Y2Nlc3MiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPkNoYWxsZW5nZSAjMTo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KTWFrZSAzIHZlcnNpb25zIG9mIHRoZSBsYXN0IHBsb3Qgd2UganVzdCBtYWRlOgoKKiBfX0luIHRoZSBmaXJzdCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiLgoqIF9fSW4gdGhlIHNlY29uZCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiIGFuZCBmaWxsZWQgaW4gIm5hdnlibHVlIi4KKiBfX0luIHRoZSB0aGlyZCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiIGFuZCBmaWxsZWQgaW4gYWNjb3JkaW5nIHRvIHdoZXRoZXIgb3Igbm90IGBwb3N0X2lmb2NlYCBpcyBUUlVFIG9yIEZBTFNFICh1c2UgZGVmYXVsdCBjb2xvcnMgZm9yIG5vdykuCgpfSElOVDpfIGBjb2xvcmAgYW5kIGBmaWxsYCBhcmUgdHdvIG9mIGBnZ3Bsb3RgJ3MgYWVzdGhldGljIG1hcHBpbmcgdmFyaWFibGVzIChpLmUuLCAidGhpbmdzIGFib3V0IGhvdyB0aGUgcGxvdCBsb29rcyB0aGF0IHdlIGdldCB0byBzcGVjaWZ5IikKCiAgPC9kaXY+CjwvZGl2PgoKCgpgYGB7cn0KZ2dwbG90KGhvdF9kb2dzLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKGNvbG91ciA9ICJ3aGl0ZSIpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIyIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woY29sb3VyID0gIndoaXRlIiwgZmlsbCA9ICJuYXZ5Ymx1ZSIpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIyIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBwb3N0X2lmb2NlKSwgY29sb3VyID0gIndoaXRlIikgKyAKICBsYWJzKHggPSAiWWVhciIsIHkgPSAiSG90IERvZ3MgYW5kIEJ1bnMgQ29uc3VtZWQiKSArCiAgZ2d0aXRsZSgiTmF0aGFuJ3MgSG90IERvZyBFYXRpbmcgQ29udGVzdCBSZXN1bHRzLCAxOTgxLTIwMjIiLCBzdWJ0aXRsZSA9ICIoTWFsZSBjb250ZXN0YW50cyBvbmx5KSIpCmBgYAoKCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzI6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CldoYXQgaWYgeW91IHdhbnQgdG8gY2hhbmdlIHRoZSBsZWdlbmQgaW4gdGhlIGxhc3QgcGxvdCB5b3UgbWFkZT8gVXNlIGdvb2dsZSB0byBmaWd1cmUgb3V0IGhvdyB0byBkbyB0aGUgZm9sbG93aW5nOgoKKiBEZWxldGUgdGhlIGxlZ2VuZCB0aXRsZQoqIE1ha2UgdGhlIGxlZ2VuZCB0ZXh0IGVpdGhlciAiUG9zdC1JRk9DRSIgb3IgIlByZS1JRk9DRSIuCgpISU5UOiBpbiBgZ2dwbG90YCwgbGVnZW5kcyBhcmUgY29udHJvbGxlZCBieSB0aGUgcmVsZXZhbnQgc2NhbGUgKGNvbG9yLCBmaWxsLCBldGMuKSB0aGF0IHRoZXkgYXJlIG1hcHBlZCB0by4KCiAgPC9kaXY+CjwvZGl2PgoKYGBge3J9CmdncGxvdChob3RfZG9ncywgYWVzKHggPSB5ZWFyLCB5ID0gbnVtX2VhdGVuKSkgKyAKICBnZW9tX2NvbChhZXMoZmlsbCA9IHBvc3RfaWZvY2UpLCBjb2xvdXIgPSAid2hpdGUiKSArIAogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMiIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikgKwogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICIiLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlByZS1JRk9DRSIsICJQb3N0LUlGT0NFIikpCmBgYAoKIyBDaGFuZ2UgVGhlIERhdGFzZXQKCk5vdywgbGV0J3MgY2hhbmdlIHRoZSBxdWVzdGlvbiBhIGxpdHRsZSBiaXQuIFVwIHRvIHRoaXMgcG9pbnQsIHdlIGhhdmUgbG9va2VkIGF0IEhEQiBwZXJmb3JtYW5jZSByZWxhdGl2ZSB0byAgdGhlIF9jcmVhdGlvbl8gb2YgdGhlIElGT0NFLiBXaGF0IGlmIHdoYXQgbWF0dGVycyBpcyB0aGUgX2FmZmlsaWF0aW9uXyBvZiB0aGUgY29udGVzdGFudHMgKGkuZS4sIHdoZXRoZXIgb3Igbm90IHRoZSBjb250ZXN0YW50cyBhcmUgbWVtYmVycyBvZiB0aGUgSUZPQ0Ugb3Igbm90KT8gV2UnbGwgbmVlZCBzb21lIGRpZmZlcmVudCBkYXRhIGZvciB0aGlzLiBUaHJvdWdoIHRoZSBfTWFnaWMgT2YgRGF0YSBTY2llbmNl4oSiXywgd2UgaGF2ZSBkdWcgdGhhdCBpbmZvcm1hdGlvbiB1cCBhbmQgcHV0IGl0IGludG8gYW4gZXhwYW5kZWQgdmVyc2lvbiBvZiBvdXIgQ1NWIGZpbGUsIHdoaWNoIHlvdSBjYW4gZmluZCBpbiB0aGUgZGF0YSBkaXJlY3RvcnkuCgoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICMzOjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpMZXQncyB3b3JrIHdpdGggdGhpcyBuZXcgZGF0YXNldCEgRG8gdGhlIGZvbGxvd2luZzoKCiogUmVhZCBpbiB0aGUgImhvdF9kb2dfY29udGVzdF93aXRoX2FmZmlsaWF0aW9uLmNzdiIgZGF0YSBmaWxlLCB1c2luZyBgY29sX3R5cGVzYCB0byByZWFkIGluIGBhZmZpbGlhdGVkYCBhbmQgYGdlbmRlcmAgYXMgZmFjdG9ycy4gCiogV2l0aGluIGEgYG11dGF0ZWAsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYHBvc3RfaWZvY2VgIHRoYXQgaXMgVFJVRSBpZiBgeWVhcmAgaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIDE5OTcuIAoqIEFsc28gYGZpbHRlcmAgdGhlIG5ldyBkYXRhIGZvciBvbmx5IHllYXJzIDE5ODEgYW5kIGFmdGVyLCBhbmQgb25seSBmb3IgbWFsZSBjb21wZXRpdG9ycy4KICA8L2Rpdj4KPC9kaXY+CgpgYGB7ciBldmFsID0gRkFMU0V9CmhkbV9hZmZpbCA8LSByZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgImhvdF9kb2dfY29udGVzdF93aXRoX2FmZmlsaWF0aW9uLmNzdiIpLCAKICAgIGNvbF90eXBlcyA9IGNvbHMoCiAgICAgIGFmZmlsaWF0ZWQgPSBjb2xfZmFjdG9yKGxldmVscyA9IE5VTEwpLCAKICAgICAgZ2VuZGVyID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKQogICAgICApKSAlPiUgCiAgbXV0YXRlKHBvc3RfaWZvY2UgPSB5ZWFyID49IDE5OTcpICU+JSAKICBmaWx0ZXIoeWVhciA+PSAxOTgxICYgZ2VuZGVyID09ICJtYWxlIikgCmBgYAoKCmBgYHtyfQpoZG1fYWZmaWwgPC0gcmVhZF9jc3YoaGVyZTo6aGVyZSgiZGF0YSIsICJob3RfZG9nX2NvbnRlc3Rfd2l0aF9hZmZpbGlhdGlvbi5jc3YiKSwgCiAgICBjb2xfdHlwZXMgPSBjb2xzKAogICAgICBhZmZpbGlhdGVkID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKSwgCiAgICAgIGdlbmRlciA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCkKICAgICAgKSkgJT4lIAogIG11dGF0ZShwb3N0X2lmb2NlID0geWVhciA+PSAxOTk3KSAlPiUgCiAgZmlsdGVyKHllYXIgPj0gMTk4MSAmIGdlbmRlciA9PSAibWFsZSIpIApnbGltcHNlKGhkbV9hZmZpbCkKYGBgCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzQ6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CkxldCdzIGRvIHNvbWUgYmFzaWMgRURBIHdpdGggdGhpcyBuZXcgZGF0YXNldCEgRG8gdGhlIGZvbGxvd2luZzoKCiogVXNlIGBkcGx5cjo6ZGlzdGluY3RgIHRvIGZpZ3VyZSBvdXQgaG93IG1hbnkgdW5pcXVlIHZhbHVlcyB0aGVyZSBhcmUgb2YgYGFmZmlsaWF0ZWRgLgoqIFVzZSBgZHBseXI6OmNvdW50YCB0byBjb3VudCB0aGUgbnVtYmVyIG9mIHJvd3MgZm9yIGVhY2ggdW5pcXVlIHZhbHVlIG9mIGBhZmZpbGlhdGVkYDsgdXNlIGA/Y291bnRgIHRvIGZpZ3VyZSBvdXQgaG93IHRvIHNvcnQgdGhlIGNvdW50cyBpbiBkZXNjZW5kaW5nIG9yZGVyLgogIDwvZGl2Pgo8L2Rpdj4KCmBgYHtyfQpoZG1fYWZmaWwgJT4lIAogIGRpc3RpbmN0KGFmZmlsaWF0ZWQpCmhkbV9hZmZpbCAlPiUgCiAgY291bnQoYWZmaWxpYXRlZCwgc29ydCA9IFRSVUUpCmBgYAoKCk5vdyBsZXQncyBwbG90IHRoaXMgbmV3IGRhdGEsIGFuZCBmaWxsIHRoZSBjb2x1bW5zIGFjY29yZGluZyB0byBvdXIgbmV3IGBhZmZpbGlhdGVkYCBjb2x1bW4uCgpgYGB7cn0KZ2dwbG90KGhkbV9hZmZpbCwgYWVzKHggPSB5ZWFyLCB5ID0gbnVtX2VhdGVuKSkgKyAKICBnZW9tX2NvbChhZXMoZmlsbCA9IGFmZmlsaWF0ZWQpKSArIAogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMiIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikKYGBgCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzU6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CkRvIHRoZSBmb2xsb3dpbmcgdXBkYXRlcyB0byB0aGUgbGFzdCBwbG90IHdlIGp1c3QgbWFkZToKCiogVXBkYXRlIHRoZSBjb2xvcnMgdXNpbmcgaGV4IGNvbG9yczogYGMoJyNFOTYwMkInLCcjMjI3N0EwJywnI0NDQjY4MycpYC4gCiogQ2hhbmdlIHRoZSBsZWdlbmQgdGl0bGUgdG8gIklGT0NFLWFmZmlsaWF0aW9uIi4gCiogU2F2ZSB0aGlzIHBsb3Qgb2JqZWN0IGFzICJhZmZpbF9wbG90Ii4KICA8L2Rpdj4KPC9kaXY+CgpgYGB7cn0KYWZmaWxfcGxvdCA8LSBnZ3Bsb3QoaGRtX2FmZmlsLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKGFlcyhmaWxsID0gYWZmaWxpYXRlZCkpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIyIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnI0U5NjAyQicsJyMyMjc3QTAnLCcjQ0NCNjgzJyksCiAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJJRk9DRS1hZmZpbGlhdGlvbiIpCmFmZmlsX3Bsb3QKYGBgCgojIFBsYXkgV2l0aCBTY2FsZXMgJiBDb29yZGluYXRlcwoKTm93IHRoYXQgdGhlIGJvbmVzIG9mIHRoZSBwbG90IGFyZSBpbiBwbGFjZSwgaXQncyB0aW1lIHRvIHR3ZWFrIHRoZSBkZXRhaWxzLgoKVGhlIHNwYWNpbmcncyBhIGxpdHRsZSBmdW5reSBkb3duIG5lYXIgdGhlIG9yaWdpbiBvZiB0aGUgcGxvdC4gVGhlIFtkb2N1bWVudGF0aW9uXShodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zY2FsZV9jb250aW51b3VzLmh0bWwpIHRlbGxzIHVzIHRoYXQgdGhlIGRlZmF1bHRzIGFyZSBgYygwLjA1LCAwKWAgZm9yIGNvbnRpbnVvdXMgdmFyaWFibGVzLiBUaGUgZmlyc3QgbnVtYmVyIGlzIG11bHRpcGxpY2F0aXZlIGFuZCB0aGUgc2Vjb25kIGlzIGFkZGl0aXZlLiAKClRoZSBkZWZhdWx0IHdhcyB0aGF0IDIuMDUgKCgyMDIyLTE5ODEpKi4wNSswKSB3YXMgYWRkZWQgdG8gdGhlIHJpZ2h0IGFuZCBsZWZ0IHNpZGVzIG9mIHRoZSB4LWF4aXMgYXMgcGFkZGluZywgc28gdGhlIGVmZmVjdGl2ZSBkZWZhdWx0IGxpbWl0cyB3ZXJlIGBjKDE5NzksIDIwMjQpYC4KCkxldCdzIHRpZ2h0ZW4gdGhhdCB1cCB3aXRoIHRoZSBgZXhwYW5kYCBwcm9wZXJ0eSBmb3IgdGhlIGBzY2FsZV95X2NvbnRpbnVvdXNgICh3ZSdsbCBhbHNvIGNoYW5nZSB0aGUgYnJlYWtzIGZvciB5LWF4aXMgdGljayBtYXJrcyBoZXJlKSBhbmQgYHNjYWxlX3hfY29udGludW91c2Agc2V0dGluZ3M6CgpgYGB7cn0KYWZmaWxfcGxvdCA8LSBhZmZpbF9wbG90ICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCA3MCwgMTApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCkpCmFmZmlsX3Bsb3QKYGBgCgoKClRoYXQgaXMgcGVyaGFwcyBfdG9vXyB0aWdodDsgbm90ZSB0aGUgbGFjayBvZiBhbnkgc3BhY2UgYmV0d2VlbiB0aGUgYmFycyBhbmQgdGhlIHktYXhpcyBvbiBodGUgbGVmdC4KCkxldCdzIGxvb3NlbiB0aGluZ3MgdXAgYSBiaXQgYnkgdXBkYXRpbmcgdGhlIHBsb3QgY29vcmRpbmF0ZXMuIAoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+Q2hhbGxlbmdlICM2OjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpVc2UgYGNvb3JkX2NhcnRlc2lhbmAgdG86CgoqIFNldCB0aGUgeC1heGlzIHJhbmdlIHRvIDE5ODAtMjAyMwoqIFNldCB0aGUgeS1heGlzIHJhbmdlIHRvIDAtODAKICA8L2Rpdj4KPC9kaXY+CgpVc2luZyBgY29vcmRfY2FydGVzaWFuYCBpcyB0aGUgcHJlZmVycmVkIGxheWVyIGhlcmUgYmVjYXVzZSAoZnJvbSB0aGUgYGNvb3JkX2NhcnRlc2lhbmAgZG9jdW1lbnRhdGlvbik6ICJzZXR0aW5nIGxpbWl0cyBvbiB0aGUgY29vcmRpbmF0ZSBzeXN0ZW0gd2lsbCB6b29tIHRoZSBwbG90IChsaWtlIHlvdSdyZSBsb29raW5nIGF0IGl0IHdpdGggYSBtYWduaWZ5aW5nIGdsYXNzKSwgYW5kIHdpbGwgbm90IGNoYW5nZSB0aGUgdW5kZXJseWluZyBkYXRhIGxpa2Ugc2V0dGluZyBgbGltaXRzYCBvbiBhIHNjYWxlIHdpbGwuIiAgCgpJbiBvdGhlciB3b3Jkcywgc2V0dGluZyBgbGltaXRzYCB3aWxsIGFjdHVhbGx5IHJlc3VsdCBpbiBpbmRpdmlkdWFsIGRhdGEgcG9pbnRzIGJlaW5nIGluY2x1ZGVkIG9yIGV4Y2x1ZGVkIGZyb20gdGhlIHBsb3QgYmFzZWQgb24gd2hldGhlciB0aGV5IGZhbGwgd2l0aGluIHRoZSBzcGVjaWZpZWQgbGltaXRzLCB3aGljaCBjb3VsZCBoYXZlIHVuYW50aWNpcGF0ZWQgZWZmZWN0cyAoZm9yIGV4YW1wbGUsIGlmIHlvdXIgcGxvdCBpbmNsdWRlcyBhIGxpbmUgZml0LCB0aGF0IGxpbmUgZml0IHdpbGwgYmUgZG9uZSB1c2luZyBvbmx5IHRoZSBpbmNsdWRlZCBkYXRhIHJhdGhlciB0aGFuIGFsbCBvZiB5b3VyIGRhdGEpLgoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtaW5mbyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+TGVzc29uOjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpEb24ndCBkaXJlY3RseSBjaGFuZ2UgYGxpbWl0c2AgdW5sZXNzIHlvdSByZWFsbHkga25vdyB3aGF0IHlvdSBhcmUgZG9pbmchIE1vc3Qgb2YgdGhlIHRpbWUsIHlvdSB3YW50IHRvIGNoYW5nZSB0aGUgY29vcmRpbmF0ZXMgaW5zdGVhZCwgYW5kIGRvIGFueSBkYXRhIHBvaW50IGZpbHRlcmluZyBvdXRzaWRlIG9mIHlvdXIgcGxvdHRpbmcuCiAgPC9kaXY+CjwvZGl2PgoKYGBge3J9CmFmZmlsX3Bsb3QgPC0gYWZmaWxfcGxvdCArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxOTgwLCAyMDIzKSwgeWxpbSA9IGMoMCwgODApKSAKYWZmaWxfcGxvdApgYGAKCgojIFBsYXkgV2l0aCBUaGVtZSBTZXR0aW5ncwoKV2Ugd2lsbCB0YWxrIGEgbG90IG1vcmUgYWJvdXQgdGhlbWVzIGFuZCBgZ2dwbG90YCBsYXRlciBpbiB0aGUgdGVybSwgYnV0IGZvciBub3csIHRoZSBpbXBvcnRhbnQgdGhpbmcgdG8ga25vdyBpcyB0aGF0IG1vc3QgdmlzdWFsIGFzcGVjdHMgb2YgdGhlIHBsb3QgaGF2ZSBhIF9uYW1lXyAoZS5nLiBgcGxvdC50aXRsZWApLCBhbmQgdGhlIGB0aGVtZSgpYCBmdW5jdGlvbiBsZXRzIHVzIHRlbGwgYGdncGxvdGAgd2hhdCBhbnkgW25hbWVkIHBhcnRdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS90aGVtZS5odG1sKSBvZiB0aGUgcGxvdCBzaG91bGQgbG9vayBsaWtlLgoKTGV0J3MgY2hhbmdlIHNvbWUga2V5IHRoZW1lIHNldHRpbmdzOgoKYGBge3J9CmFmZmlsX3Bsb3QgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWUoYXhpcy5saW5lLnggPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheTgwIiwgc2l6ZSA9IDAuNSkpICsKICB0aGVtZShheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk4MCIsIHNpemUgPSAwLjUpKQpgYGAKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLWluZm8iPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPkxlc3Nvbjo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KWW91IGNhbiBjaGFuZ2UgKmFsbW9zdCBhbnl0aGluZyogdGhhdCB5b3VyIGhlYXJ0IGRlc2lyZXMgdG8gY2hhbmdlISAKICA8L2Rpdj4KPC9kaXY+CgoKQnkgZGVmYXVsdCwgcGxvdCB0aXRsZXMgaW4gYGdncGxvdDJgIGFyZSBsZWZ0LWFsaWduZWQuIEZvciBgaGp1c3RgOgoKLSBgMGAgPT0gbGVmdAotIGAwLjVgID09IGNlbnRlcmVkCi0gYDFgID09IHJpZ2h0CgpXZSBjb3VsZCBhbHNvIHNhdmUgYWxsIHRoZXNlIGFzIGEgY3VzdG9tIHRoZW1lLiBXZSBhcmUgbm90IGZhbnMgb2YgdGhlIGRlZmF1bHQgZm9udCwgc28gd2UgYXJlIGFsc28gZ29pbmcgdG8gY2hhbmdlIHRoaXMuIFRvIGRvIHRoaXMsIHlvdSBuZWVkIHRvIGluc3RhbGwgdGhlIChgZXh0cmFmb250YCBwYWNrYWdlKVtodHRwczovL2dpdGh1Yi5jb20vd2NoL2V4dHJhZm9udF0gYW5kIGZvbGxvdyBpdHMgc2V0dXAgaW5zdHJ1Y3Rpb25zIGJlZm9yZSBkb2luZyB0aGlzIG5leHQgc3RlcC4KCmBgYHtyfQpob3RfZGlnZ2l0eSA8LSB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk4MCIsIHNpemUgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk4MCIsIHNpemUgPSAwLjUpLAogICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJMYXRvIikgIyBuZWVkIGV4dHJhZm9udCBmb3IgdGhpcwogICAgICAgICAgICAgICAgICAgICApCmBgYAoKCgpgYGB7cn0KYWZmaWxfcGxvdCArIGhvdF9kaWdnaXR5IApgYGAKCldlIGNvdWxkIGFsc28gdXNlIHNvbWVvbmUgZWxzZSdzIHRoZW1lOgoKYGBge3J9CmxpYnJhcnkoZ2d0aGVtZXMpCmFmZmlsX3Bsb3QgKyB0aGVtZV9maXZldGhpcnR5ZWlnaHQoYmFzZV9mYW1pbHkgPSAiTGF0byIpCmFmZmlsX3Bsb3QgKyB0aGVtZV90dWZ0ZSggYmFzZV9mYW1pbHkgPSAiUGFsYXRpbm8iKQpgYGAKCgoKClRoZSBmaW5hbCB0aGluZyB3ZSBoYXZlIHRvIG1lc3Mgd2l0aCBpcyB0aGUgeC1heGlzIHRpY2tzIGFuZCBsYWJlbHMuIFdlJ2xsIGRvIHRoaXMgaW4gdHdvIHN0ZXBzLCB0aGVuIG92ZXJyaWRlIG91ciBwcmV2aW91cyBsYXllciBgc2NhbGVfeF9jb250aW51b3VzYC4KCmBgYHtyfQojIG1hbnVhbGx5IGNvbXB1dGUgYSBsaXN0IG9mIHllYXJzIHRoYXQgd2Ugd2FudCBsYWJlbGVkLi4uCnllYXJzX3RvX2xhYmVsIDwtIHNlcShmcm9tID0gMTk4MSwgdG8gPSAyMDIyLCBieSA9IDQpIAp5ZWFyc190b19sYWJlbAoKIyBhZGQgYSBjb2x1bW4gdG8gdGhlIGRhdGFmcmFtZSBjb250YWluaW5nIHdoYXQgd2Ugd2FudCBlYWNoIHllYXIncyBsYWJlbCB0byBiZQpoZF95ZWFycyA8LSBoZG1fYWZmaWwgJT4lCiAgZGlzdGluY3QoeWVhcikgJT4lIAogIG11dGF0ZSh5ZWFyX2xhYiA9IGlmZWxzZSh5ZWFyICVpbiUgeWVhcnNfdG9fbGFiZWwsIHllYXIsICIiKSkKYGBgCgpgYGB7cn0KIyBtYW51YWxseSB0ZWxsIGdncGxvdCB3aGF0IHRvIHVzZSBmb3IgYnJlYWtzIGFuZCBsYWJlbHMKYWZmaWxfcGxvdCArIAogIGhvdF9kaWdnaXR5ICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGhkX3llYXJzJHllYXIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGhkX3llYXJzJHllYXJfbGFiKQpgYGAKCiMgRmluYWwgKGZpbmFsLCBmaW5hbCkgdmVyc2lvbgoKRG9uJ3QgbmFtZSB5b3VyIGZpbGVzICJmaW5hbCIgOikKCiFbXShodHRwOi8vd3d3LnBoZGNvbWljcy5jb20vY29taWNzL2FyY2hpdmUvcGhkMTAxMjEycy5naWYpCgpBbGwgdG9nZXRoZXIgaW4gb25lIGNodW5rLCBoZXJlIGlzIG91ciBmaW5hbCAoZm9yIG5vdykgcGxvdCEgSSdtIGFsc28gYWRkaW5nIHNvbWUgYWRkaXRpb25hbCBlbGVtZW50cyBoZXJlIHRvIHNob3cgeW91IG9wdGlvbnM6CgpgYGB7cn0KbmF0aGFuX3Bsb3QgPC0gZ2dwbG90KGhkbV9hZmZpbCwgYWVzKHggPSB5ZWFyLCB5ID0gbnVtX2VhdGVuKSkgKyAKICBnZW9tX2NvbChhZXMoZmlsbCA9IGFmZmlsaWF0ZWQpKSArIAogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMiIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoJyNFOTYwMkInLCcjMjI3N0EwJywnI0NDQjY4MycpLAogICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiSUZPQ0UtYWZmaWxpYXRpb24iKSArIAogIGhvdF9kaWdnaXR5ICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDcwLCAxMCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSwgCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGhkX3llYXJzJHllYXIsCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGhkX3llYXJzJHllYXJfbGFiKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxOTgwLCAyMDIzKSwgeWxpbSA9IGMoMCwgODApKSAKbmF0aGFuX3Bsb3QKYGBgCgoKClRoZSBmaWxsIGxlZ2VuZCBpcyBkb2luZyBpdHMgam9iLCBoZXJlLCBidXQgd2UgbWlnaHQgaW5zdGVhZCB3YW50IHRvIHVzZSBkaXJlY3QgYW5ub3RhdGlvbnMgb24gdGhlIHBsb3QgaXRzZWxmLCB0byBtYWtlIGl0IGVhc2llciBhbmQgZmFzdGVyIHRvIHJlYWQuIAoKYGdncGxvdGAgd2lsbCBsZXQgdXMgYWRkIF9hbm5vdGF0YXRpb25zXyB0byB0aGUgcGxvdC0gaS5lLiwgZXh0cmEgdGV4dCwgbGluZXMgLCBldGMuIHRoYXQgYXJlIG5vdCBkZXJpdmVkIGZyb20gdGhlIGRhdGEgaW4gdGhlIHBsb3QsIGJ1dCBhcmUgbWFudWFsbHkgc3BlY2lmaWVkIC0gdXNpbmcgdGhlIGBhbm5vdGF0ZSgpYCBmdW5jdGlvbiwgd2hpY2ggYWRkcyBhZGRpdGlvbmFsIGxheWVycyB0byB0aGUgcGxvdC4gVGhlIHdheSB3ZSBhcmUgZG9pbmcgaXQgYmVsb3cgaXMgYSBiaXQgdGVkaW91cywgYnV0IGRlbW9uc3RyYXRlcyBob3cgaXQgd29ya3MuCgpgYGB7cn0KbmF0aGFuX2FubiA8LSBuYXRoYW5fcGxvdCArCiAgZ3VpZGVzKGZpbGw9Im5vbmUiKSArICMgdHVybiBvZmYgdGhlIGxlZ2VuZC9ndWlkZSBmb3IgdGhlICJmaWxsIiBhZXN0aGV0aWMKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMTk4MCwgMjAyMyksIHlsaW0gPSBjKDAsIDkwKSkgKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0xOTgwLjc1LCB4ZW5kPTIwMDAuMjUsIHk9IDMwLCB5ZW5kPTMwLCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iI0NDQjY4MyIpKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0xOTgwLjc1LCB4ZW5kPTE5ODAuNzUsIHk9IDMwLCB5ZW5kPTI4LCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iI0NDQjY4MyIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAwMC4yNSwgeGVuZD0yMDAwLjI1LCB5PSAzMCwgeWVuZD0yOCwgbGluZXdpZHRoPTAuNSwgY29sb3I9IiNDQ0I2ODMiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTE5OTAsIHhlbmQ9MTk5MCwgeT0gMzMsIHllbmQ9MzAsIGxpbmV3aWR0aD0wLjUsIGNvbG9yPSIjQ0NCNjgzIikgKwogIGFubm90YXRlKCd0ZXh0JywgeD0xOTkwLCB5PTM2LCBsYWJlbD0iTm8gTUxFL0lGT0NFIEFmZmlsaWF0aW9uIiwgY29sb3I9IiNDQ0I2ODMiLCBmYW1pbHk9IkxhdG8iLCBoanVzdD0wLjUsIHNpemUgPSAzKSArCgoKCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDAuNzUsIHhlbmQ9MjAwNi4yNSwgeT0gNTgsIHllbmQ9NTgsIGxpbmV3aWR0aD0wLjUsIGNvbG9yPSIjMjI3N0EwIikgKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0yMDAwLjc1LCB4ZW5kPTIwMDAuNzUsIHk9IDU4LCB5ZW5kPTU2LCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iIzIyNzdBMCIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAwNi4yNSwgeGVuZD0yMDA2LjI1LCB5PSA1OCwgeWVuZD01NiwgbGluZXdpZHRoPTAuNSwgY29sb3I9IiMyMjc3QTAiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDMuNSwgeGVuZD0yMDAzLjUsIHk9IDYxLCB5ZW5kPTU4LCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iIzIyNzdBMCIpICsKICBhbm5vdGF0ZSgndGV4dCcsIHg9MjAwMy41LCB5PTY1LCBsYWJlbD0iTUxFL0lGT0NFXG5Gb3JtZXIgTWVtYmVyIiwgY29sb3I9IiMyMjc3QTAiLCBmYW1pbHk9IkxhdG8iLCBoanVzdD0wLjUsIHNpemUgPSAzKSArCgoKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAwNi43NSwgeGVuZD0yMDIyLjI1LCB5PSA3OSwgeWVuZD03OSwgbGluZXdpZHRoPTAuNSwgY29sb3I9IiNFOTYwMkIiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDYuNzUsIHhlbmQ9MjAwNi43NSwgeT0gNzksIHllbmQ9NzcsIGxpbmV3aWR0aD0wLjUsIGNvbG9yPSIjRTk2MDJCIikgKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0yMDIyLjI1LCB4ZW5kPTIwMjIuMjUsIHk9IDc5LCB5ZW5kPTc3LCBsaW5ld2lkdGg9MC41LCBjb2xvcj0iI0U5NjAyQiIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAxNSwgeGVuZD0yMDE1LCB5PSA4MiwgeWVuZD03OSwgbGluZXdpZHRoPTAuNSwgY29sb3I9IiNFOTYwMkIiKSArCiAgYW5ub3RhdGUoJ3RleHQnLCB4PTIwMTUsIHk9ODYsIGxhYmVsPSJNTEUvSUZPQ0UgQ3VycmVudCBNZW1iZXIiLCBjb2xvcj0iI0U5NjAyQiIsIGZhbWlseT0iTGF0byIsIGhqdXN0PTAuNSwgc2l6ZSA9IDMpCm5hdGhhbl9hbm4KYGBgCgpGaW5hbGx5LCBhZGRpbmcgaW4gYW5vdGhlciBsYXllciBvZiBkYXRhLCBpbmNsdWRpbmcgaW5mb3JtYXRpb24gYWJvdXQgZmVtYWxlIGNvbnRlc3RhbnRzOgoKYGBge3J9CmhkbV9mZW1hbGVzIDwtIHJlYWRfY3N2KGhlcmU6OmhlcmUoImRhdGEiLCAiaG90X2RvZ19jb250ZXN0X3dpdGhfYWZmaWxpYXRpb24uY3N2IiksIAogICAgY29sX3R5cGVzID0gY29scygKICAgICAgYWZmaWxpYXRlZCA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCksIAogICAgICBnZW5kZXIgPSBjb2xfZmFjdG9yKGxldmVscyA9IE5VTEwpCiAgICAgICkpICU+JSAKICBtdXRhdGUocG9zdF9pZm9jZSA9IHllYXIgPj0gMTk5NykgJT4lIAogIGZpbHRlcih5ZWFyID49IDE5ODEgJiBnZW5kZXIgPT0gImZlbWFsZSIpIApnbGltcHNlKGhkbV9mZW1hbGVzKQpgYGAKCmBgYHtyfQpuYXRoYW5fd19mZW1hbGVzIDwtIG5hdGhhbl9hbm4gKwogICMgYWRkIGluIHRoZSBmZW1hbGUgZGF0YSwgYW5kIG1hbnVhbGx5IHNldCBhIGZpbGwgY29sb3IKICBnZW9tX2NvbChkYXRhID0gaGRtX2ZlbWFsZXMsIAogICAgICAgICAgIHdpZHRoID0gMC43NSwgCiAgICAgICAgICAgZmlsbCA9ICIjRjY4QTM5IikgKwogIGxhYnMoc3VidGl0bGUgPSBOVUxMKSAjIG5vIGxvbmdlciBuZWVkIHRoZSBzdWJ0aXRsZSB3YXJuaW5nIGFib3V0IG1hbGUtb25seSBkYXRhIQpuYXRoYW5fd19mZW1hbGVzCmBgYAoKQW5kIGFkZGluZyBhIGZpbmFsIGNhcHRpb246CgpgYGB7cn0KY2FwdGlvbiA8LSBwYXN0ZShzdHJ3cmFwKCIqIEZyb20gMjAxMSBvbiwgc2VwYXJhdGUgTWVuJ3MgYW5kIFdvbWVuJ3MgcHJpemVzIGhhdmUgYmVlbiBhd2FyZGVkLiBBbGwgZmVtYWxlIGNoYW1waW9ucyB0byBkYXRlIGhhdmUgYmVlbiBNTEUvSUZPQ0UtYWZmaWxpYXRlZC4iLCA3MCksIGNvbGxhcHNlPSJcbiIpCgpuYXRoYW5fd19mZW1hbGVzICsKICAjIG5vdyBhbiBhc3RlcmlzayB0byBzZXQgb2ZmIHRoZSBmZW1hbGUgc2NvcmVzLCBhbmQgYSBjYXB0aW9uCiAgYW5ub3RhdGUoJ3RleHQnLCB4ID0gMjAxMSwgeSA9IDM2LCBsYWJlbD0iKiIsIGZhbWlseSA9ICJMYXRvIiwgc2l6ZSA9IDgpICsKICBsYWJzKGNhcHRpb24gPSBjYXB0aW9uKSArCiAgdGhlbWUocGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJMYXRvIiwgc2l6ZT04LCBoanVzdD0wLCBtYXJnaW49bWFyZ2luKHQ9MTUpKSkKYGBgCgo=

Creative Commons License