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!

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 you are unfamiliar with- we will likely learn more about them all as the term goes on, but 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 RStudio 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)
    ))

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

glimpse(hot_dogs)
Rows: 57
Columns: 4
$ year      <dbl> 2021, 2021, 2020, 2020, 2019, 2019, 2018, 2018, 2017, 2017, 2016, 2016, 2015, 2015, 2014, 2014, 2013, …
$ gender    <fct> male, female, male, female, male, female, male, female, male, female, male, female, male, female, male…
$ name      <chr> "Joey Chestnut", "Michelle Lesco", "Joey Chestnut", "Miki Sudo", "Joey Chestnut", "Miki Sudo", "Joey C…
$ num_eaten <dbl> 76.000, 30.750, 75.000, 48.500, 71.000, 31.000, 74.000, 37.000, 72.000, 41.000, 70.000, 38.000, 62.000…
hot_dogs
# A tibble: 57 × 4
    year gender name           num_eaten
   <dbl> <fct>  <chr>              <dbl>
 1  2021 male   Joey Chestnut       76  
 2  2021 female Michelle Lesco      30.8
 3  2020 male   Joey Chestnut       75  
 4  2020 female Miki Sudo           48.5
 5  2019 male   Joey Chestnut       71  
 6  2019 female Miki Sudo           31  
 7  2018 male   Joey Chestnut       74  
 8  2018 female Miki Sudo           37  
 9  2017 male   Joey Chestnut       72  
10  2017 female Miki Sudo           41  
# … with 47 more rows

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

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: 41 × 5
    year gender name           num_eaten post_ifoce
   <dbl> <fct>  <chr>              <dbl> <lgl>     
 1  2021 male   Joey Chestnut         76 TRUE      
 2  2020 male   Joey Chestnut         75 TRUE      
 3  2019 male   Joey Chestnut         71 TRUE      
 4  2018 male   Joey Chestnut         74 TRUE      
 5  2017 male   Joey Chestnut         72 TRUE      
 6  2016 male   Joey Chestnut         70 TRUE      
 7  2015 male   Matthew Stonie        62 TRUE      
 8  2014 male   Joey Chestnut         61 TRUE      
 9  2013 male   Joey Chestnut         69 TRUE      
10  2012 male   Joey Chestnut         68 TRUE      
# … with 31 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-2021", 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-2021", 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-2021", 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-2021", 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-2021", 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 available at http://bit.ly/cs631-hotdog-affiliated.

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: 41
Columns: 6
$ year       <dbl> 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012, 2011, 2010, 2009, 2008, 2007, 2006, 2005,…
$ gender     <fct> male, male, male, male, male, male, male, male, male, male, male, male, male, male, male, male, male,…
$ name       <chr> "Joey Chestnut", "Joey Chestnut", "Joey Chestnut", "Joey Chestnut", "Joey Chestnut", "Joey Chestnut",…
$ num_eaten  <dbl> 76.000, 75.000, 71.000, 74.000, 72.000, 70.000, 62.000, 61.000, 69.000, 68.000, 62.000, 54.000, 68.00…
$ affiliated <fct> current, current, current, current, current, current, current, current, current, current, current, cu…
$ post_ifoce <lgl> TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, 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           15
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-2021", 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-2021", 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 1.8 ((2017-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, 2019).

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

But now the plot looks like it is wearing tight pants.

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-2018

  • Set the y-axis range to 0-80

Using coord_cartesian is the preferred layer here because “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.”

Lesson:
Don’t change limits unless you really know what you are doing! Most of the time, you want to change the coordinates instead.
affil_plot <- affil_plot + 
  coord_cartesian(xlim = c(1980, 2018), 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))

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 = 2021, 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-2021", 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, 2022), 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 = FALSE) +
  coord_cartesian(xlim = c(1980, 2022), ylim = c(0, 90)) +
  annotate('segment', x=1980.75, xend=2000.25, y= 30, yend=30, size=0.5, color="#CCB683")+
  annotate('segment', x=1980.75, xend=1980.75, y= 30, yend=28, size=0.5, color="#CCB683") +
  annotate('segment', x=2000.25, xend=2000.25, y= 30, yend=28, size=0.5, color="#CCB683") +
  annotate('segment', x=1990, xend=1990, y= 33, yend=30, size=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, size=0.5, color="#2277A0") +
  annotate('segment', x=2000.75, xend=2000.75, y= 58, yend=56, size=0.5, color="#2277A0") +
  annotate('segment', x=2006.25, xend=2006.25, y= 58, yend=56, size=0.5, color="#2277A0") +
  annotate('segment', x=2003.5, xend=2003.5, y= 61, yend=58, size=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=2021.25, y= 79, yend=79, size=0.5, color="#E9602B") +
  annotate('segment', x=2006.75, xend=2006.75, y= 79, yend=77, size=0.5, color="#E9602B") +
  annotate('segment', x=2021.25, xend=2021.25, y= 79, yend=77, size=0.5, color="#E9602B") +
  annotate('segment', x=2012, xend=2012, y= 82, yend=79, size=0.5, color="#E9602B") +
  annotate('text', x=2012, y=86, label="MLE/IFOCE Current Member", color="#E9602B", family="Lato", hjust=0.5, size = 3)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
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: 11
Columns: 6
$ year       <dbl> 2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012, 2011
$ gender     <fct> female, female, female, female, female, female, female, female, female, female, female
$ name       <chr> "Michelle Lesco", "Miki Sudo", "Miki Sudo", "Miki Sudo", "Miki Sudo", "Miki Sudo", "Miki Sudo", "Miki…
$ num_eaten  <dbl> 30.75, 48.50, 31.00, 37.00, 41.00, 38.00, 38.00, 34.00, 36.75, 45.00, 40.00
$ affiliated <fct> current, current, current, current, current, current, current, current, current, current, current
$ post_ifoce <lgl> TRUE, 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 = 2018.5, y = 39, 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/CiAgICAKVGhlc2UgYXJlIGltcG9ydGFudCBxdWVzdGlvbnMsIGFuZCBJIHdhbnQgeW91IHRvIGRldmVsb3AgKHdlbGwtaW5mb3JtZWQpIG9waW5pb25zIG9uIHRoZXNlIG1hdHRlcnMhCiFbXShpbWFnZXMvdGhlbWUtdGVhbS10d2VldHMucG5nKQoKT3RoZXIgdGhpbmdzIHRvIHRoaW5rIGFib3V0IGZvciB0aGlzIGxhYjoKCi0gVHJ5IG5vdCB0byBjb3B5LWFuZC1wYXN0ZSBjb2RlOgogIC0gQmVjb21pbmcgZWZmaWNpZW50L3Byb2ZpY2llbnQgd2l0aCBSIGRlcGVuZHMgb24gYnVpbGRpbmcgbXVzY2xlIG1lbW9yeSwgYW5kIHRoZSBvbmx5IHdheSB0byBkbyB0aGF0IGlzIHRvIHR5cGUKICAtIEJ5IHR5cGluZywgeW91IF93aWxsXyBpbnRyb2R1Y2UgZXJyb3JzLCBhbmQgdGhpcyBpcyBhIGdvb2QgY2hhbmNlIHRvIGdldCBwcmFjdGljZSBhdCBpbnRlcnByZXRpbmcgYW5kIGZpeGluZyB0aGVtLgotIFRha2Ugbm90ZSBvZiBzdGVwcyBvciBmdW5jdGlvbnMgdGhhdCB5b3UgYXJlIHVuZmFtaWxpYXIgd2l0aC0gd2Ugd2lsbCBsaWtlbHkgbGVhcm4gbW9yZSBhYm91dCB0aGVtIGFsbCBhcyB0aGUgdGVybSBnb2VzIG9uLCBidXQgdGhpcyBsYWIgd2lsbCBoZWxwIHlvdSBpZGVudGlmeSBwYXJ0aWN1bGFyIGFyZWFzIHRoYXQgeW91IGFyZSBjb21mb3J0YWJsZSB3aXRoIGFzIHdlbGwgYXMgYXJlYXMgeW91IHdpbGwgd2FudCB0byBmb2N1cyBvbi4KCgojIE5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QKCiFbXShodHRwczovL2kwLndwLmNvbS9mbG93aW5nZGF0YS5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMDkvMDYvaG90LWRvZ3MxLmdpZj96b29tPTImZml0PTkwMCUyQzQyMykKClRoaXMgaW5jbHVkZXMgYSByZWNvbnN0cnVjdGlvbiBvZiBbTmF0aGFuIFlhdSdzIGhvdCBkb2cgY29udGVzdCBleGFtcGxlXShodHRwOi8vZmxvd2luZ2RhdGEuY29tLzIwMDkvMDcvMDIvd2hvcy1nb2luZy10by13aW4tbmF0aGFucy1ob3QtZG9nLWVhdGluZy1jb250ZXN0L2hvdC1kb2dzLTIvKSwgYXMgaW50ZXJwcmV0ZWQgYnkgSmFja2llIFdpcnosIHBvcnRlZCBpbnRvIFIgYW5kIGBnZ3Bsb3QyYCBieSBTdGV2ZW4gQmVkcmljayBmb3IgYSB3b3Jrc2hvcCBmb3IgdGhlIFtPSFNVIERhdGEgU2NpZW5jZSBJbnN0aXR1dGVdKGh0dHBzOi8vb2hzdWxpYnJhcnktZGF0YXNjaWVuY2VpbnN0aXR1dGUuZ2l0aHViLmlvKSwgYW5kIGZpbmFsbHkgYWRhcHRlZCwgbWFkZSBpZGlvbWF0aWMsIGFuZCBpbXByb3ZlZCBieSBBbGlzb24gSGlsbCBmb3IgYWxsIHlvdSBpbnRyZXBpZCBEYXRhLVZpei1vbmF1dHMhCgoKRmlyc3QsIHdlIGxvYWQgb3VyIHBhY2thZ2VzOiAKCgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZXh0cmFmb250KQpsaWJyYXJ5KGhlcmUpCmBgYAoKCiMgUmVhZCBpbiBhbmQgd3JhbmdsZSBkYXRhCgpOZXh0LCB3ZSBsb2FkIHNvbWUgZGF0YS4gSW4gdGhlIFJTdHVkaW8gQ2xvdWQgcHJvamVjdCBmb3IgdGhpcyBsYWIsIHlvdSB3aWxsIHNlZSBhIGBkYXRhYCBkaXJlY3Rvcnkgd2l0aCB0aGUgbmVjZXNzYXJ5IGZpbGVzLiBXZSBjYW4gcmVhZCBpdCBpbiB1c2luZyBgcmVhZF9jc3ZgLCBhbmQgYWxvbmcgdGhlIHdheSB1c2UgYGNvbF9mYWN0b3JgIHRvIHRlbGwgaXQgaG93IHRvIGhhbmRsZSB0aGUgZ2VuZGVyIGNvbHVtbi4KCmBgYHtyfQpob3RfZG9ncyA8LSByZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgImhvdF9kb2dfY29udGVzdC5jc3YiKSwgCiAgICBjb2xfdHlwZXMgPSBjb2xzKAogICAgICBnZW5kZXIgPSBjb2xfZmFjdG9yKGxldmVscyA9IE5VTEwpCiAgICApKQpgYGAKCkNoZWNrIGl0IG91dCwgb25jZSBpdCBpcyByZWFkIGluLCBhbmQgbWFrZSBzdXJlIGl0IGxvb2tzIGxpa2UgdGhpcyEKCmBgYHtyfQpnbGltcHNlKGhvdF9kb2dzKQpob3RfZG9ncwpgYGAKCkF0IHRoaXMgcG9pbnQsIGZvbGxvdyB0aGUgSExPIHByb2Nlc3MgYW5kIGZhbWlsaWFyaXplIHlvdXJzZWxmIHdpdGggdGhlIGNvbHVtbnMgYW5kIHRoZWlyIGNvbnRlbnRzLgoKSW4gYWRkaXRpb24gdG8gdGhlIGluZm9ybWF0aW9uIHRoYXQgaXMgYWxyZWFkeSBfaW5fIHRoZSBkYXRhc2V0IGl0c2VsZiwgd2Uga25vdyB0aGF0IHdlIHdpbGwgYWxzbyBiZSB3YW50aW5nIHRvIHNvbWVob3cgaW5jbHVkZSBpbmZvcm1hdGlvbiBhYm91dCB3aGV0aGVyIGEgZ2l2ZW4geWVhciB3YXMgYmVmb3JlIG9yIGFmdGVyIHRoZSBpbmNvcnBvcmF0aW9uIG9mIHRoZSBjb21wZXRpdGl2ZSBlYXRpbmcgbGVhZ3VlLiBMZXQncyBhZGQgYW4gX2luZGljYXRvciB2YXJpYWJsZV8gdG8gdGhlIGRhdGEgdXNpbmcgYG11dGF0ZSgpYC4gQWxzbywgdGhlIGRhdGEncyBhIGxpdHRsZSBza2V0Y2h5IHByZS0xOTgxLCBhbmQgZm9yIG91ciBwdXJwb3NlcyB0b2RheSB3ZSdsbCBiZSBmb2N1c2luZyBvbiBtYWxlcyBvbmx5LCBzbyBsZXQncyBkbyBzb21lIGBmaWx0ZXJgaW5nLCBhcyB3ZWxsOgoKYGBge3J9CmhvdF9kb2dzIDwtIGhvdF9kb2dzICU+JSAKICBtdXRhdGUocG9zdF9pZm9jZSA9IHllYXIgPj0gMTk5NykgJT4lIAogIGZpbHRlcih5ZWFyID49IDE5ODEgJiBnZW5kZXIgPT0gJ21hbGUnKQpob3RfZG9ncwpgYGAKCgoKIyBQbG90IFRoZSBEYXRhCgpOb3cgbGV0J3MgdHJ5IG1ha2luZyBhIGZpcnN0IGNyYWNrIGF0IGEgcGxvdDoKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woKQpgYGAKCk5vdGUgdGhhdCBvdXIgZGF0YSBpcyBhbHJlYWR5IGluICJjb3VudGVkIiBmb3JtLCBzbyB3ZSdyZSB1c2luZyBgZ2VvbV9jb2woKWAgaW5zdGVhZCBvZiBgZ2VvbV9iYXIoKWAuCgpXZSB3aWxsIG5vdyBwcm9ncmVzc2l2ZWx5IGltcHJvdmUgdGhpcyB2aXN1YWxpemF0aW9uLCBvbmUgc3RlcCBhdCBhIHRpbWUuCgojIEFkZCBBeGlzIExhYmVscyBBbmQgVGl0bGUKCgpgYGB7cn0KZ2dwbG90KGhvdF9kb2dzLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKCkgKwogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMSIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikKYGBgCgojIFBsYXkgV2l0aCBDb2xvcnMKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXN1Y2Nlc3MiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPkNoYWxsZW5nZSAjMTo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KTWFrZSAzIHZlcnNpb25zIG9mIHRoZSBsYXN0IHBsb3Qgd2UganVzdCBtYWRlOgoKKiBfX0luIHRoZSBmaXJzdCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiLgoqIF9fSW4gdGhlIHNlY29uZCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiIGFuZCBmaWxsZWQgaW4gIm5hdnlibHVlIi4KKiBfX0luIHRoZSB0aGlyZCxfXyBtYWtlIGFsbCB0aGUgY29sdW1ucyBvdXRsaW5lZCBpbiAid2hpdGUiIGFuZCBmaWxsZWQgaW4gYWNjb3JkaW5nIHRvIHdoZXRoZXIgb3Igbm90IGBwb3N0X2lmb2NlYCBpcyBUUlVFIG9yIEZBTFNFICh1c2UgZGVmYXVsdCBjb2xvcnMgZm9yIG5vdykuCgpfSElOVDpfIGBjb2xvcmAgYW5kIGBmaWxsYCBhcmUgdHdvIG9mIGBnZ3Bsb3RgJ3MgYWVzdGhldGljIG1hcHBpbmcgdmFyaWFibGVzIChpLmUuLCAidGhpbmdzIGFib3V0IGhvdyB0aGUgcGxvdCBsb29rcyB0aGF0IHdlIGdldCB0byBzcGVjaWZ5IikKCiAgPC9kaXY+CjwvZGl2PgoKCgpgYGB7cn0KZ2dwbG90KGhvdF9kb2dzLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKGNvbG91ciA9ICJ3aGl0ZSIpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIxIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woY29sb3VyID0gIndoaXRlIiwgZmlsbCA9ICJuYXZ5Ymx1ZSIpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIxIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoaG90X2RvZ3MsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBwb3N0X2lmb2NlKSwgY29sb3VyID0gIndoaXRlIikgKyAKICBsYWJzKHggPSAiWWVhciIsIHkgPSAiSG90IERvZ3MgYW5kIEJ1bnMgQ29uc3VtZWQiKSArCiAgZ2d0aXRsZSgiTmF0aGFuJ3MgSG90IERvZyBFYXRpbmcgQ29udGVzdCBSZXN1bHRzLCAxOTgxLTIwMjEiLCBzdWJ0aXRsZSA9ICIoTWFsZSBjb250ZXN0YW50cyBvbmx5KSIpCmBgYAoKCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzI6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CldoYXQgaWYgeW91IHdhbnQgdG8gY2hhbmdlIHRoZSBsZWdlbmQgaW4gdGhlIGxhc3QgcGxvdCB5b3UgbWFkZT8gVXNlIGdvb2dsZSB0byBmaWd1cmUgb3V0IGhvdyB0byBkbyB0aGUgZm9sbG93aW5nOgoKKiBEZWxldGUgdGhlIGxlZ2VuZCB0aXRsZQoqIE1ha2UgdGhlIGxlZ2VuZCB0ZXh0IGVpdGhlciAiUG9zdC1JRk9DRSIgb3IgIlByZS1JRk9DRSIuCgpISU5UOiBpbiBgZ2dwbG90YCwgbGVnZW5kcyBhcmUgY29udHJvbGxlZCBieSB0aGUgcmVsZXZhbnQgc2NhbGUgKGNvbG9yLCBmaWxsLCBldGMuKSB0aGF0IHRoZXkgYXJlIG1hcHBlZCB0by4KCiAgPC9kaXY+CjwvZGl2PgoKYGBge3J9CmdncGxvdChob3RfZG9ncywgYWVzKHggPSB5ZWFyLCB5ID0gbnVtX2VhdGVuKSkgKyAKICBnZW9tX2NvbChhZXMoZmlsbCA9IHBvc3RfaWZvY2UpLCBjb2xvdXIgPSAid2hpdGUiKSArIAogIGxhYnMoeCA9ICJZZWFyIiwgeSA9ICJIb3QgRG9ncyBhbmQgQnVucyBDb25zdW1lZCIpICsKICBnZ3RpdGxlKCJOYXRoYW4ncyBIb3QgRG9nIEVhdGluZyBDb250ZXN0IFJlc3VsdHMsIDE5ODEtMjAyMSIsIHN1YnRpdGxlID0gIihNYWxlIGNvbnRlc3RhbnRzIG9ubHkpIikgKwogIHNjYWxlX2ZpbGxfZGlzY3JldGUobmFtZSA9ICIiLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIlByZS1JRk9DRSIsICJQb3N0LUlGT0NFIikpCmBgYAoKIyBDaGFuZ2UgVGhlIERhdGFzZXQKCk5vdywgbGV0J3MgY2hhbmdlIHRoZSBxdWVzdGlvbiBhIGxpdHRsZSBiaXQuIFVwIHRvIHRoaXMgcG9pbnQsIHdlIGhhdmUgbG9va2VkIGF0IEhEQiBwZXJmb3JtYW5jZSByZWxhdGl2ZSB0byAgdGhlIF9jcmVhdGlvbl8gb2YgdGhlIElGT0NFLiBXaGF0IGlmIHdoYXQgbWF0dGVycyBpcyB0aGUgX2FmZmlsaWF0aW9uXyBvZiB0aGUgY29udGVzdGFudHMgKGkuZS4sIHdoZXRoZXIgb3Igbm90IHRoZSBjb250ZXN0YW50cyBhcmUgbWVtYmVycyBvZiB0aGUgSUZPQ0Ugb3Igbm90KT8gV2UnbGwgbmVlZCBzb21lIGRpZmZlcmVudCBkYXRhIGZvciB0aGlzLiBUaHJvdWdoIHRoZSBfTWFnaWMgT2YgRGF0YSBTY2llbmNl4oSiXywgd2UgaGF2ZSBkdWcgdGhhdCBpbmZvcm1hdGlvbiB1cCBhbmQgcHV0IGl0IGludG8gYW4gZXhwYW5kZWQgdmVyc2lvbiBvZiBvdXIgQ1NWIGZpbGUgYXZhaWxhYmxlIGF0IFtodHRwOi8vYml0Lmx5L2NzNjMxLWhvdGRvZy1hZmZpbGlhdGVkXShodHRwOi8vYml0Lmx5L2NzNjMxLWhvdGRvZy1hZmZpbGlhdGVkKS4KCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzM6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CkxldCdzIHdvcmsgd2l0aCB0aGlzIG5ldyBkYXRhc2V0ISBEbyB0aGUgZm9sbG93aW5nOgoKKiBSZWFkIGluIHRoZSAiaG90X2RvZ19jb250ZXN0X3dpdGhfYWZmaWxpYXRpb24uY3N2IiBkYXRhIGZpbGUsIHVzaW5nIGBjb2xfdHlwZXNgIHRvIHJlYWQgaW4gYGFmZmlsaWF0ZWRgIGFuZCBgZ2VuZGVyYCBhcyBmYWN0b3JzLiAKKiBXaXRoaW4gYSBgbXV0YXRlYCwgY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCBgcG9zdF9pZm9jZWAgdGhhdCBpcyBUUlVFIGlmIGB5ZWFyYCBpcyBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gMTk5Ny4gCiogQWxzbyBgZmlsdGVyYCB0aGUgbmV3IGRhdGEgZm9yIG9ubHkgeWVhcnMgMTk4MSBhbmQgYWZ0ZXIsIGFuZCBvbmx5IGZvciBtYWxlIGNvbXBldGl0b3JzLgogIDwvZGl2Pgo8L2Rpdj4KCmBgYHtyIGV2YWwgPSBGQUxTRX0KaGRtX2FmZmlsIDwtIHJlYWRfY3N2KGhlcmU6OmhlcmUoImRhdGEiLCAiaG90X2RvZ19jb250ZXN0X3dpdGhfYWZmaWxpYXRpb24uY3N2IiksIAogICAgY29sX3R5cGVzID0gY29scygKICAgICAgYWZmaWxpYXRlZCA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCksIAogICAgICBnZW5kZXIgPSBjb2xfZmFjdG9yKGxldmVscyA9IE5VTEwpCiAgICAgICkpICU+JSAKICBtdXRhdGUocG9zdF9pZm9jZSA9IHllYXIgPj0gMTk5NykgJT4lIAogIGZpbHRlcih5ZWFyID49IDE5ODEgJiBnZW5kZXIgPT0gIm1hbGUiKSAKYGBgCgoKYGBge3J9CmhkbV9hZmZpbCA8LSByZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgImhvdF9kb2dfY29udGVzdF93aXRoX2FmZmlsaWF0aW9uLmNzdiIpLCAKICAgIGNvbF90eXBlcyA9IGNvbHMoCiAgICAgIGFmZmlsaWF0ZWQgPSBjb2xfZmFjdG9yKGxldmVscyA9IE5VTEwpLCAKICAgICAgZ2VuZGVyID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKQogICAgICApKSAlPiUgCiAgbXV0YXRlKHBvc3RfaWZvY2UgPSB5ZWFyID49IDE5OTcpICU+JSAKICBmaWx0ZXIoeWVhciA+PSAxOTgxICYgZ2VuZGVyID09ICJtYWxlIikgCmdsaW1wc2UoaGRtX2FmZmlsKQpgYGAKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXN1Y2Nlc3MiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPkNoYWxsZW5nZSAjNDo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KTGV0J3MgZG8gc29tZSBiYXNpYyBFREEgd2l0aCB0aGlzIG5ldyBkYXRhc2V0ISBEbyB0aGUgZm9sbG93aW5nOgoKKiBVc2UgYGRwbHlyOjpkaXN0aW5jdGAgdG8gZmlndXJlIG91dCBob3cgbWFueSB1bmlxdWUgdmFsdWVzIHRoZXJlIGFyZSBvZiBgYWZmaWxpYXRlZGAuCiogVXNlIGBkcGx5cjo6Y291bnRgIHRvIGNvdW50IHRoZSBudW1iZXIgb2Ygcm93cyBmb3IgZWFjaCB1bmlxdWUgdmFsdWUgb2YgYGFmZmlsaWF0ZWRgOyB1c2UgYD9jb3VudGAgdG8gZmlndXJlIG91dCBob3cgdG8gc29ydCB0aGUgY291bnRzIGluIGRlc2NlbmRpbmcgb3JkZXIuCiAgPC9kaXY+CjwvZGl2PgoKYGBge3J9CmhkbV9hZmZpbCAlPiUgCiAgZGlzdGluY3QoYWZmaWxpYXRlZCkKaGRtX2FmZmlsICU+JSAKICBjb3VudChhZmZpbGlhdGVkLCBzb3J0ID0gVFJVRSkKYGBgCgoKTm93IGxldCdzIHBsb3QgdGhpcyBuZXcgZGF0YSwgYW5kIGZpbGwgdGhlIGNvbHVtbnMgYWNjb3JkaW5nIHRvIG91ciBuZXcgYGFmZmlsaWF0ZWRgIGNvbHVtbi4KCmBgYHtyfQpnZ3Bsb3QoaGRtX2FmZmlsLCBhZXMoeCA9IHllYXIsIHkgPSBudW1fZWF0ZW4pKSArIAogIGdlb21fY29sKGFlcyhmaWxsID0gYWZmaWxpYXRlZCkpICsgCiAgbGFicyh4ID0gIlllYXIiLCB5ID0gIkhvdCBEb2dzIGFuZCBCdW5zIENvbnN1bWVkIikgKwogIGdndGl0bGUoIk5hdGhhbidzIEhvdCBEb2cgRWF0aW5nIENvbnRlc3QgUmVzdWx0cywgMTk4MS0yMDIxIiwgc3VidGl0bGUgPSAiKE1hbGUgY29udGVzdGFudHMgb25seSkiKQpgYGAKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXN1Y2Nlc3MiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPkNoYWxsZW5nZSAjNTo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KRG8gdGhlIGZvbGxvd2luZyB1cGRhdGVzIHRvIHRoZSBsYXN0IHBsb3Qgd2UganVzdCBtYWRlOgoKKiBVcGRhdGUgdGhlIGNvbG9ycyB1c2luZyBoZXggY29sb3JzOiBgYygnI0U5NjAyQicsJyMyMjc3QTAnLCcjQ0NCNjgzJylgLiAKKiBDaGFuZ2UgdGhlIGxlZ2VuZCB0aXRsZSB0byAiSUZPQ0UtYWZmaWxpYXRpb24iLiAKKiBTYXZlIHRoaXMgcGxvdCBvYmplY3QgYXMgImFmZmlsX3Bsb3QiLgogIDwvZGl2Pgo8L2Rpdj4KCmBgYHtyfQphZmZpbF9wbG90IDwtIGdncGxvdChoZG1fYWZmaWwsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBhZmZpbGlhdGVkKSkgKyAKICBsYWJzKHggPSAiWWVhciIsIHkgPSAiSG90IERvZ3MgYW5kIEJ1bnMgQ29uc3VtZWQiKSArCiAgZ2d0aXRsZSgiTmF0aGFuJ3MgSG90IERvZyBFYXRpbmcgQ29udGVzdCBSZXN1bHRzLCAxOTgxLTIwMjEiLCBzdWJ0aXRsZSA9ICIoTWFsZSBjb250ZXN0YW50cyBvbmx5KSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCcjRTk2MDJCJywnIzIyNzdBMCcsJyNDQ0I2ODMnKSwKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIklGT0NFLWFmZmlsaWF0aW9uIikKYWZmaWxfcGxvdApgYGAKCiMgUGxheSBXaXRoIFNjYWxlcyAmIENvb3JkaW5hdGVzCgpOb3cgdGhhdCB0aGUgYm9uZXMgb2YgdGhlIHBsb3QgYXJlIGluIHBsYWNlLCBpdCdzIHRpbWUgdG8gdHdlYWsgdGhlIGRldGFpbHMuCgpUaGUgc3BhY2luZydzIGEgbGl0dGxlIGZ1bmt5IGRvd24gbmVhciB0aGUgb3JpZ2luIG9mIHRoZSBwbG90LiBUaGUgW2RvY3VtZW50YXRpb25dKGh0dHA6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3NjYWxlX2NvbnRpbnVvdXMuaHRtbCkgdGVsbHMgdXMgdGhhdCB0aGUgZGVmYXVsdHMgYXJlIGBjKDAuMDUsIDApYCBmb3IgY29udGludW91cyB2YXJpYWJsZXMuIFRoZSBmaXJzdCBudW1iZXIgaXMgbXVsdGlwbGljYXRpdmUgYW5kIHRoZSBzZWNvbmQgaXMgYWRkaXRpdmUuIAoKVGhlIGRlZmF1bHQgd2FzIHRoYXQgMS44ICgoMjAxNy0xOTgxKSouMDUrMCkgd2FzIGFkZGVkIHRvIHRoZSByaWdodCBhbmQgbGVmdCBzaWRlcyBvZiB0aGUgeC1heGlzIGFzIHBhZGRpbmcsIHNvIHRoZSBlZmZlY3RpdmUgZGVmYXVsdCBsaW1pdHMgd2VyZSBgYygxOTc5LCAyMDE5KWAuCgpMZXQncyB0aWdodGVuIHRoYXQgdXAgd2l0aCB0aGUgYGV4cGFuZGAgcHJvcGVydHkgZm9yIHRoZSBgc2NhbGVfeV9jb250aW51b3VzYCAod2UnbGwgYWxzbyBjaGFuZ2UgdGhlIGJyZWFrcyBmb3IgeS1heGlzIHRpY2sgbWFya3MgaGVyZSkgYW5kIGBzY2FsZV94X2NvbnRpbnVvdXNgIHNldHRpbmdzOgoKYGBge3J9CmFmZmlsX3Bsb3QgPC0gYWZmaWxfcGxvdCArIAogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgNzAsIDEwKSkgKwogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBjKDAsIDApKQphZmZpbF9wbG90CmBgYAoKCgpCdXQgbm93IHRoZSBwbG90IGxvb2tzIGxpa2UgaXQgaXMgd2VhcmluZyB0aWdodCBwYW50cy4KCjwhLS0gIVtdKGh0dHBzOi8vbWVkaWEuZ2lwaHkuY29tL21lZGlhL3hUMVhIMDdubzJ3WlNxNG1ubS9naXBoeS5naWYpIC0tPgoKTGV0J3MgbG9vc2VuIHRoaW5ncyB1cCBhIGJpdCBieSB1cGRhdGluZyB0aGUgcGxvdCBjb29yZGluYXRlcy4gCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5DaGFsbGVuZ2UgIzY6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+ClVzZSBgY29vcmRfY2FydGVzaWFuYCB0bzoKCiogU2V0IHRoZSB4LWF4aXMgcmFuZ2UgdG8gMTk4MC0yMDE4CiogU2V0IHRoZSB5LWF4aXMgcmFuZ2UgdG8gMC04MAogIDwvZGl2Pgo8L2Rpdj4KClVzaW5nIGBjb29yZF9jYXJ0ZXNpYW5gIGlzIHRoZSBwcmVmZXJyZWQgbGF5ZXIgaGVyZSBiZWNhdXNlICJzZXR0aW5nIGxpbWl0cyBvbiB0aGUgY29vcmRpbmF0ZSBzeXN0ZW0gd2lsbCB6b29tIHRoZSBwbG90IChsaWtlIHlvdSdyZSBsb29raW5nIGF0IGl0IHdpdGggYSBtYWduaWZ5aW5nIGdsYXNzKSwgYW5kIHdpbGwgbm90IGNoYW5nZSB0aGUgdW5kZXJseWluZyBkYXRhIGxpa2Ugc2V0dGluZyBgbGltaXRzYCBvbiBhIHNjYWxlIHdpbGwuIiAgCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1pbmZvIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5MZXNzb246PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CkRvbid0IGNoYW5nZSBgbGltaXRzYCB1bmxlc3MgeW91IHJlYWxseSBrbm93IHdoYXQgeW91IGFyZSBkb2luZyEgTW9zdCBvZiB0aGUgdGltZSwgeW91IHdhbnQgdG8gY2hhbmdlIHRoZSBjb29yZGluYXRlcyBpbnN0ZWFkLgogIDwvZGl2Pgo8L2Rpdj4KCmBgYHtyfQphZmZpbF9wbG90IDwtIGFmZmlsX3Bsb3QgKyAKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMTk4MCwgMjAxOCksIHlsaW0gPSBjKDAsIDgwKSkgCmFmZmlsX3Bsb3QKYGBgCgoKIyBQbGF5IFdpdGggVGhlbWUgU2V0dGluZ3MKCldlIHdpbGwgdGFsayBhIGxvdCBtb3JlIGFib3V0IHRoZW1lcyBhbmQgYGdncGxvdGAgbGF0ZXIgaW4gdGhlIHRlcm0sIGJ1dCBmb3Igbm93LCB0aGUgaW1wb3J0YW50IHRoaW5nIHRvIGtub3cgaXMgdGhhdCBtb3N0IHZpc3VhbCBhc3BlY3RzIG9mIHRoZSBwbG90IGhhdmUgYSBfbmFtZV8gKGUuZy4gYHBsb3QudGl0bGVgKSwgYW5kIHRoZSBgdGhlbWUoKWAgZnVuY3Rpb24gbGV0cyB1cyB0ZWxsIGBnZ3Bsb3RgIHdoYXQgYW55IFtuYW1lZCBwYXJ0XShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvdGhlbWUuaHRtbCkgb2YgdGhlIHBsb3Qgc2hvdWxkIGxvb2sgbGlrZS4KCkxldCdzIGNoYW5nZSBzb21lIGtleSB0aGVtZSBzZXR0aW5nczoKCmBgYHtyfQphZmZpbF9wbG90ICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgKwogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHRoZW1lKGF4aXMubGluZS54ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXk4MCIsIHNpemUgPSAwLjUpKSArCiAgdGhlbWUoYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5ODAiLCBzaXplID0gMC41KSkKYGBgCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1pbmZvIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5MZXNzb246PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+CllvdSBjYW4gY2hhbmdlICphbG1vc3QgYW55dGhpbmcqIHRoYXQgeW91ciBoZWFydCBkZXNpcmVzIHRvIGNoYW5nZSEgCiAgPC9kaXY+CjwvZGl2PgoKCkJ5IGRlZmF1bHQsIHBsb3QgdGl0bGVzIGluIGBnZ3Bsb3QyYCBhcmUgbGVmdC1hbGlnbmVkLiBGb3IgYGhqdXN0YDoKCi0gYDBgID09IGxlZnQKLSBgMC41YCA9PSBjZW50ZXJlZAotIGAxYCA9PSByaWdodAoKV2UgY291bGQgYWxzbyBzYXZlIGFsbCB0aGVzZSBhcyBhIGN1c3RvbSB0aGVtZS4gV2UgYXJlIG5vdCBmYW5zIG9mIHRoZSBkZWZhdWx0IGZvbnQsIHNvIHdlIGFyZSBhbHNvIGdvaW5nIHRvIGNoYW5nZSB0aGlzLiBUbyBkbyB0aGlzLCB5b3UgbmVlZCB0byBpbnN0YWxsIHRoZSAoYGV4dHJhZm9udGAgcGFja2FnZSlbaHR0cHM6Ly9naXRodWIuY29tL3djaC9leHRyYWZvbnRdIGFuZCBmb2xsb3cgaXRzIHNldHVwIGluc3RydWN0aW9ucyBiZWZvcmUgZG9pbmcgdGhpcyBuZXh0IHN0ZXAuCgpgYGB7cn0KaG90X2RpZ2dpdHkgPC0gdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5ODAiLCBzaXplID0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5ODAiLCBzaXplID0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiTGF0byIpICMgbmVlZCBleHRyYWZvbnQgZm9yIHRoaXMKICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCgoKYGBge3J9CmFmZmlsX3Bsb3QgKyBob3RfZGlnZ2l0eSAKYGBgCgpXZSBjb3VsZCBhbHNvIHVzZSBzb21lb25lIGVsc2UncyB0aGVtZToKCmBgYHtyfQpsaWJyYXJ5KGdndGhlbWVzKQphZmZpbF9wbG90ICsgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KGJhc2VfZmFtaWx5ID0gIkxhdG8iKQphZmZpbF9wbG90ICsgdGhlbWVfdHVmdGUoIGJhc2VfZmFtaWx5ID0gIlBhbGF0aW5vIikKYGBgCgoKCgpUaGUgZmluYWwgdGhpbmcgd2UgaGF2ZSB0byBtZXNzIHdpdGggaXMgdGhlIHgtYXhpcyB0aWNrcyBhbmQgbGFiZWxzLiBXZSdsbCBkbyB0aGlzIGluIHR3byBzdGVwcywgdGhlbiBvdmVycmlkZSBvdXIgcHJldmlvdXMgbGF5ZXIgYHNjYWxlX3hfY29udGludW91c2AuCgpgYGB7cn0KIyBtYW51YWxseSBjb21wdXRlIGEgbGlzdCBvZiB5ZWFycyB0aGF0IHdlIHdhbnQgbGFiZWxlZC4uLgp5ZWFyc190b19sYWJlbCA8LSBzZXEoZnJvbSA9IDE5ODEsIHRvID0gMjAyMSwgYnkgPSA0KSAKeWVhcnNfdG9fbGFiZWwKCiMgYWRkIGEgY29sdW1uIHRvIHRoZSBkYXRhZnJhbWUgY29udGFpbmluZyB3aGF0IHdlIHdhbnQgZWFjaCB5ZWFyJ3MgbGFiZWwgdG8gYmUKaGRfeWVhcnMgPC0gaGRtX2FmZmlsICU+JQogIGRpc3RpbmN0KHllYXIpICU+JSAKICBtdXRhdGUoeWVhcl9sYWIgPSBpZmVsc2UoeWVhciAlaW4lIHllYXJzX3RvX2xhYmVsLCB5ZWFyLCAiIikpCmBgYAoKYGBge3J9CiMgbWFudWFsbHkgdGVsbCBnZ3Bsb3Qgd2hhdCB0byB1c2UgZm9yIGJyZWFrcyBhbmQgbGFiZWxzCmFmZmlsX3Bsb3QgKyAKICBob3RfZGlnZ2l0eSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBoZF95ZWFycyR5ZWFyLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBoZF95ZWFycyR5ZWFyX2xhYikKYGBgCgojIEZpbmFsIChmaW5hbCwgZmluYWwpIHZlcnNpb24KCkRvbid0IG5hbWUgeW91ciBmaWxlcyAiZmluYWwiIDopCgohW10oaHR0cDovL3d3dy5waGRjb21pY3MuY29tL2NvbWljcy9hcmNoaXZlL3BoZDEwMTIxMnMuZ2lmKQoKQWxsIHRvZ2V0aGVyIGluIG9uZSBjaHVuaywgaGVyZSBpcyBvdXIgZmluYWwgKGZvciBub3cpIHBsb3QhIEknbSBhbHNvIGFkZGluZyBzb21lIGFkZGl0aW9uYWwgZWxlbWVudHMgaGVyZSB0byBzaG93IHlvdSBvcHRpb25zOgoKYGBge3J9Cm5hdGhhbl9wbG90IDwtIGdncGxvdChoZG1fYWZmaWwsIGFlcyh4ID0geWVhciwgeSA9IG51bV9lYXRlbikpICsgCiAgZ2VvbV9jb2woYWVzKGZpbGwgPSBhZmZpbGlhdGVkKSkgKyAKICBsYWJzKHggPSAiWWVhciIsIHkgPSAiSG90IERvZ3MgYW5kIEJ1bnMgQ29uc3VtZWQiKSArCiAgZ2d0aXRsZSgiTmF0aGFuJ3MgSG90IERvZyBFYXRpbmcgQ29udGVzdCBSZXN1bHRzLCAxOTgxLTIwMjEiLCBzdWJ0aXRsZSA9ICIoTWFsZSBjb250ZXN0YW50cyBvbmx5KSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCcjRTk2MDJCJywnIzIyNzdBMCcsJyNDQ0I2ODMnKSwKICAgICAgICAgICAgICAgICAgICBuYW1lID0gIklGT0NFLWFmZmlsaWF0aW9uIikgKyAKICBob3RfZGlnZ2l0eSArCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCA3MCwgMTApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBoZF95ZWFycyR5ZWFyLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBoZF95ZWFycyR5ZWFyX2xhYikgKyAKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMTk4MCwgMjAyMiksIHlsaW0gPSBjKDAsIDgwKSkgCm5hdGhhbl9wbG90CmBgYAoKCgpUaGUgZmlsbCBsZWdlbmQgaXMgZG9pbmcgaXRzIGpvYiwgaGVyZSwgYnV0IHdlIG1pZ2h0IGluc3RlYWQgd2FudCB0byB1c2UgZGlyZWN0IGFubm90YXRpb25zIG9uIHRoZSBwbG90IGl0c2VsZiwgdG8gbWFrZSBpdCBlYXNpZXIgYW5kIGZhc3RlciB0byByZWFkLiAKCmBnZ3Bsb3RgIHdpbGwgbGV0IHVzIGFkZCBfYW5ub3RhdGF0aW9uc18gdG8gdGhlIHBsb3QtIGkuZS4sIGV4dHJhIHRleHQsIGxpbmVzICwgZXRjLiB0aGF0IGFyZSBub3QgZGVyaXZlZCBmcm9tIHRoZSBkYXRhIGluIHRoZSBwbG90LCBidXQgYXJlIG1hbnVhbGx5IHNwZWNpZmllZCAtIHVzaW5nIHRoZSBgYW5ub3RhdGUoKWAgZnVuY3Rpb24sIHdoaWNoIGFkZHMgYWRkaXRpb25hbCBsYXllcnMgdG8gdGhlIHBsb3QuIFRoZSB3YXkgd2UgYXJlIGRvaW5nIGl0IGJlbG93IGlzIGEgYml0IHRlZGlvdXMgYnV0IGRlbW9uc3RyYXRlcyBob3cgaXQgd29ya3MuCgpgYGB7cn0KbmF0aGFuX2FubiA8LSBuYXRoYW5fcGxvdCArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygxOTgwLCAyMDIyKSwgeWxpbSA9IGMoMCwgOTApKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTE5ODAuNzUsIHhlbmQ9MjAwMC4yNSwgeT0gMzAsIHllbmQ9MzAsIHNpemU9MC41LCBjb2xvcj0iI0NDQjY4MyIpKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0xOTgwLjc1LCB4ZW5kPTE5ODAuNzUsIHk9IDMwLCB5ZW5kPTI4LCBzaXplPTAuNSwgY29sb3I9IiNDQ0I2ODMiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDAuMjUsIHhlbmQ9MjAwMC4yNSwgeT0gMzAsIHllbmQ9MjgsIHNpemU9MC41LCBjb2xvcj0iI0NDQjY4MyIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MTk5MCwgeGVuZD0xOTkwLCB5PSAzMywgeWVuZD0zMCwgc2l6ZT0wLjUsIGNvbG9yPSIjQ0NCNjgzIikgKwogIGFubm90YXRlKCd0ZXh0JywgeD0xOTkwLCB5PTM2LCBsYWJlbD0iTm8gTUxFL0lGT0NFIEFmZmlsaWF0aW9uIiwgY29sb3I9IiNDQ0I2ODMiLCBmYW1pbHk9IkxhdG8iLCBoanVzdD0wLjUsIHNpemUgPSAzKSArCgoKCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDAuNzUsIHhlbmQ9MjAwNi4yNSwgeT0gNTgsIHllbmQ9NTgsIHNpemU9MC41LCBjb2xvcj0iIzIyNzdBMCIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAwMC43NSwgeGVuZD0yMDAwLjc1LCB5PSA1OCwgeWVuZD01Niwgc2l6ZT0wLjUsIGNvbG9yPSIjMjI3N0EwIikgKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0yMDA2LjI1LCB4ZW5kPTIwMDYuMjUsIHk9IDU4LCB5ZW5kPTU2LCBzaXplPTAuNSwgY29sb3I9IiMyMjc3QTAiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDMuNSwgeGVuZD0yMDAzLjUsIHk9IDYxLCB5ZW5kPTU4LCBzaXplPTAuNSwgY29sb3I9IiMyMjc3QTAiKSArCiAgYW5ub3RhdGUoJ3RleHQnLCB4PTIwMDMuNSwgeT02NSwgbGFiZWw9Ik1MRS9JRk9DRVxuRm9ybWVyIE1lbWJlciIsIGNvbG9yPSIjMjI3N0EwIiwgZmFtaWx5PSJMYXRvIiwgaGp1c3Q9MC41LCBzaXplID0gMykgKwoKCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMDYuNzUsIHhlbmQ9MjAyMS4yNSwgeT0gNzksIHllbmQ9NzksIHNpemU9MC41LCBjb2xvcj0iI0U5NjAyQiIpICsKICBhbm5vdGF0ZSgnc2VnbWVudCcsIHg9MjAwNi43NSwgeGVuZD0yMDA2Ljc1LCB5PSA3OSwgeWVuZD03Nywgc2l6ZT0wLjUsIGNvbG9yPSIjRTk2MDJCIikgKwogIGFubm90YXRlKCdzZWdtZW50JywgeD0yMDIxLjI1LCB4ZW5kPTIwMjEuMjUsIHk9IDc5LCB5ZW5kPTc3LCBzaXplPTAuNSwgY29sb3I9IiNFOTYwMkIiKSArCiAgYW5ub3RhdGUoJ3NlZ21lbnQnLCB4PTIwMTIsIHhlbmQ9MjAxMiwgeT0gODIsIHllbmQ9NzksIHNpemU9MC41LCBjb2xvcj0iI0U5NjAyQiIpICsKICBhbm5vdGF0ZSgndGV4dCcsIHg9MjAxMiwgeT04NiwgbGFiZWw9Ik1MRS9JRk9DRSBDdXJyZW50IE1lbWJlciIsIGNvbG9yPSIjRTk2MDJCIiwgZmFtaWx5PSJMYXRvIiwgaGp1c3Q9MC41LCBzaXplID0gMykKbmF0aGFuX2FubgpgYGAKCkZpbmFsbHksIGFkZGluZyBpbiBhbm90aGVyIGxheWVyIG9mIGRhdGEsIGluY2x1ZGluZyBpbmZvcm1hdGlvbiBhYm91dCBmZW1hbGUgY29udGVzdGFudHM6CgpgYGB7cn0KaGRtX2ZlbWFsZXMgPC0gcmVhZF9jc3YoaGVyZTo6aGVyZSgiZGF0YSIsICJob3RfZG9nX2NvbnRlc3Rfd2l0aF9hZmZpbGlhdGlvbi5jc3YiKSwgCiAgICBjb2xfdHlwZXMgPSBjb2xzKAogICAgICBhZmZpbGlhdGVkID0gY29sX2ZhY3RvcihsZXZlbHMgPSBOVUxMKSwgCiAgICAgIGdlbmRlciA9IGNvbF9mYWN0b3IobGV2ZWxzID0gTlVMTCkKICAgICAgKSkgJT4lIAogIG11dGF0ZShwb3N0X2lmb2NlID0geWVhciA+PSAxOTk3KSAlPiUgCiAgZmlsdGVyKHllYXIgPj0gMTk4MSAmIGdlbmRlciA9PSAiZmVtYWxlIikgCmdsaW1wc2UoaGRtX2ZlbWFsZXMpCmBgYAoKYGBge3J9Cm5hdGhhbl93X2ZlbWFsZXMgPC0gbmF0aGFuX2FubiArCiAgIyBhZGQgaW4gdGhlIGZlbWFsZSBkYXRhLCBhbmQgbWFudWFsbHkgc2V0IGEgZmlsbCBjb2xvcgogIGdlb21fY29sKGRhdGEgPSBoZG1fZmVtYWxlcywgCiAgICAgICAgICAgd2lkdGggPSAwLjc1LCAKICAgICAgICAgICBmaWxsID0gIiNGNjhBMzkiKSArCiAgbGFicyhzdWJ0aXRsZSA9IE5VTEwpICMgbm8gbG9uZ2VyIG5lZWQgdGhlIHN1YnRpdGxlIHdhcm5pbmcgYWJvdXQgbWFsZS1vbmx5IGRhdGEhCm5hdGhhbl93X2ZlbWFsZXMKYGBgCgpBbmQgYWRkaW5nIGEgZmluYWwgY2FwdGlvbjoKCmBgYHtyfQpjYXB0aW9uIDwtIHBhc3RlKHN0cndyYXAoIiogRnJvbSAyMDExIG9uLCBzZXBhcmF0ZSBNZW4ncyBhbmQgV29tZW4ncyBwcml6ZXMgaGF2ZSBiZWVuIGF3YXJkZWQuIEFsbCBmZW1hbGUgY2hhbXBpb25zIHRvIGRhdGUgaGF2ZSBiZWVuIE1MRS9JRk9DRS1hZmZpbGlhdGVkLiIsIDcwKSwgY29sbGFwc2U9IlxuIikKCm5hdGhhbl93X2ZlbWFsZXMgKwogICMgbm93IGFuIGFzdGVyaXNrIHRvIHNldCBvZmYgdGhlIGZlbWFsZSBzY29yZXMsIGFuZCBhIGNhcHRpb24KICBhbm5vdGF0ZSgndGV4dCcsIHggPSAyMDE4LjUsIHkgPSAzOSwgbGFiZWw9IioiLCBmYW1pbHkgPSAiTGF0byIsIHNpemUgPSA4KSArCiAgbGFicyhjYXB0aW9uID0gY2FwdGlvbikgKwogIHRoZW1lKHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiTGF0byIsIHNpemU9OCwgaGp1c3Q9MCwgbWFyZ2luPW1hcmdpbih0PTE1KSkpCmBgYAoK

Creative Commons License