Today we’re going to learn a whole lot more about R Markdown and how to use it to share your analysis with people that don’t know R.

Homework overview





Link to Published Plot



R Markdown

Markdown is a mark-up language that is used to convert text to HTML. An R Markdown document uses the Markdown language and R code to create a user-friendly page with formatted text and output from R code. You can export an R Markdown document as:

  1. HTML file
  2. PDF
  3. Word doc


R Markdown Resources

Today’s exercises borrow heavily from the R Markdown chapter in R for Data Science. I recommend bookmarking all of these resources, and reading some or all of them if you continue to use R Markdown.


Purpose

Common uses for R Markdown docs are:

  • Interactive display of research
  • Simple way to share your analysis on the web, for free
  • Lessons/Tutorials
  • Technical Books
  • Research Collaboration
  • Research Papers


Today we’re going to use this R Notebook to create an HTML document that can be viewed in a web browser that displays:

  • the output of our educational attainment script from class 10
  • the sources
  • the methods
  • where viewers can download our R scripts to reproduce our research


Create an R Markdown document:

  • File > R Markdown
    • select Document, HTML, and name it r_markdown_test
  • Save it to your class12 folder, name it r_markdown_test
  • Look at the files created in your class 12 folder
  • In R Studio, Knit r_markdown_test
    • See the HTML document that pops up
    • Look at the files created in your class 12 folder
    • Now we’ll look at the document together


Components

There are three basic components of an R Markdown document:

  • metadata: instructions for R Studio on the type of document and how it looks
    • between a pair of three dashes —
    • you can add themes and table of contents
    • it is VERY precisely formatted (indentation matters)
  • text: the text that you want to display in your document
    • written in the Markdown language
  • code: the R code that will create the tables, charts, maps, graphics displayed in the doc
    • A code chunk starts with and ends with three backticks
    • An inline R code expression starts with and ends with one backtick.




R Notebooks

R Notebooks are the most modern (and most common) type of R Markdown document. They are R Markdown documents that default as an HTML file. They also provide the ability to Preview as you work, and the published web page has the option for viewers to download the .Rmd document. This makes them the best choice for collaboration.

Note: The Preview only displays code that has been run. To see all of the R code output, you have to Run the code.

Create an R Notebook document:

  • File > R Notebook
  • Save it to your class12 folder, name it educational_attainment
  • In R Studio, Preview educational_attainment
    • Look at the HTML document that pops up
  • Click the green arrow on the upper right of the first code chunk
  • Preview educational_attainment again
    • Notice that it now displays the the output of the code chunk
    • Click on the the Code button on the upper right of your Preview
    • Download the Rmd, find it in your Downloads and open it



R code in R Markdown

To run code inside an R Markdown document and display the output in your html document you need to add the R code inside a code chunk so that R Studio knows execute it. To insert a chunk:

  • The keyboard shortcut Cmd/Ctrl + Alt + I
  • The “Insert” button icon in the toolbar

Inside the code chunk you can do anything you would in a regular R script - import data, process it, display it. Instead of displaying it in your R Studio window, it displays the output within the R Notebook and creates an HTML document that displays the output.

You’ll want to put code in different chunks, depending on whether you want to display the code or not.

Setup

The first code chunk is always used to define the parameters and you don’t want to display it.

The above code chunk:

  • defines this code chunk as the setup so it runs before any other code chunk, no matter where it is.
    • You can only have one setup.
    • The setup code chunk is never shown in yor output HTML document
  • echo = TRUE sets the default to show the code in your HTML document
  • loads the libraries


Setup your R Notebook:

  • In educational_attainment:
  • Delete everything after the metadata
  • Add setup code chunk like above


Add R code

After you set up your R Notebook, you can add your R code to do the work.


Add R code to your R Notebook:

  • In educational_attainment:
  • Below your setup code, start a new code chunk
  • Insert the code below (or your own code from your homework if you prefer)


acs_vars <- load_variables(2019, "acs1", cache = T)

c15003_vars <- acs_vars %>% 
  filter(grepl("C15003", name)) %>% 
  mutate(label = str_replace(label, "Estimate!!", ""),
         label = str_replace(label, ":!!", "_")) %>% 
  select(name, label) %>% 
  rename(variable = name)

c_education <- get_acs(survey = "acs1", 
                       geography = "state", 
                       state = "NY", 
                       table = "C15003",
                       year = "2019") %>% 
  left_join(c15003_vars, by = "variable")

#### Import census data for all places ####

##### Population data #####
raw_city_pop <- get_acs(survey = "acs1", 
                         geography = "place", 
                         variables = "B01003_001",
                         year = "2019")

##### Educational attainment data #####
raw_city_education <- get_acs(survey = "acs1", 
                              geography = "place", 
                              table = "C15003",
                              year = "2019") %>% 
  left_join(c15003_vars, by = "variable")

##### Per-capita income data #####
raw_city_pc_income <- get_acs(survey = "acs1", 
                              geography = "place", 
                              variables = "B19301_001",
                              year = "2019")

#### Process census data for all places ####

city_pop <- raw_city_pop %>% 
  rename(pop = estimate) %>% 
  select(GEOID, pop)

city_income <-   raw_city_pc_income %>% 
  rename(per_capita_income = estimate) %>% 
  select(GEOID, per_capita_income)

city_ed_attainment <- raw_city_education %>% 
  filter(label == "Total_Bachelor's degree" | 
           label == "Total_Master's degree" | 
           label == "Total_Professional school degree" | 
           label == "Total_Doctorate degree" | 
           label == "Total:") %>% 
  select(GEOID, NAME, estimate, label) %>% 
  pivot_wider(names_from = label, values_from = estimate) %>% 
  mutate(bachelor_and_higher = rowSums(across(`Total_Bachelor's degree`:`Total_Doctorate degree`)),
         # quick test of rows sums, I add up the first row, 11436+7687+950+2223, it worked!
         pct_at_least_bachelors = bachelor_and_higher/`Total:`) 

#### Create data frame for analysis and scatterplot ####
city_analysis <- city_ed_attainment %>% 
  select(GEOID, NAME, pct_at_least_bachelors) %>% 
  separate(NAME, c("city","state"), sep = ",") %>% 
  left_join(city_pop, by = "GEOID") %>% 
  left_join(city_income, by = "GEOID") %>% 
  filter(pop > 65000)

#### Make it interactive ####
city_plot <- city_analysis %>% 
  ggplot(aes(x = pct_at_least_bachelors, y = per_capita_income, size = pop,
             color = state,
             text = paste0(city,", ", state,
                           "<br>Population : ", scales::comma(pop, accuracy=1L),
                           "<br>Adults with at least a Bachelor's Degree : ", scales::percent(pct_at_least_bachelors, accuracy=1L),
                           "<br>Per-capita income : ", scales::dollar(per_capita_income, accuracy=1L)))) +
  geom_point(alpha = .75) +
  guides(size = "none",
         color = "none") +
  # make sure you have the `scales` package loaded!
  scale_x_continuous(labels = percent_format(accuracy = 1)) +
  scale_y_continuous(labels = dollar_format(accuracy = 1)) + 
  # change legend label formatting
  scale_size_area(labels = comma, max_size = 10) +
  labs(x = "Proportion of Adults with at least a Bachelor's Degree", y = "Per-capita Income",
       title = "Educational Attainment and Per Capita Income",
       caption = "Sources: ACS, 5-yr 2015-19", 
       # add nice label for size element
       size = "Enrollment",
       color = "Urbanicity") +
  theme_bw()  +
  theme(legend.position = 'none')

ggplotly(city_plot, tooltip = "text") %>% 
  layout(margin = list(t = 25))


Hide R code


In some cases, you may want to display your R code, but usually you want to keep it hidden and show the output only. For each code chunk, you can define the chunk options to determine whether the code, warnings, and output are displayed in your html document. The following are the most commonly used options:

  • eval = FALSE prevents code from being evaluated. (And obviously if the code is not run, no results will be generated). This is useful for displaying example code, or for disabling a large block of code without commenting each line.
  • include = FALSE runs the code, but doesn’t show the code or results in the final document. Use this for setup code that you don’t want cluttering your report.
  • echo = FALSE prevents code, but not the results from appearing in the finished file. Use this when writing reports aimed at people who don’t want to see the underlying R code.
  • message = FALSE or warning = FALSE prevents messages or warnings from appearing in the finished file.
  • results = 'hide' hides printed output; fig.show = 'hide' hides plots.

You can also set the options in the YAML header to be able to download the Rmd so that R-curious readers can look at your code. Add code_download: true to the YAML header like this:

{width:200px}


Hide R code in your R Notebook:

  • add the option echo=FALSE, message=F, warning=F so that the beginning of you code chunk looks like this:
  • Add code_download: true to the YAML header



Text Formatting in R Markdown

You can also add spaces with the line break tag - < br >



Add text to your R Notebook:

  • In r_notebook_test:
  • Create citations for the educational attainment data from the assignment 10
    • Make the citations look like below:
      • Data Sources & Headers are 4th level headers
      • Variables are bold
      • Table names are italicized
      • tidycensus lnks to https://walker-data.com/tidycensus/
      • lists of variables use bullet points








Assignment 12

R Assignment

Read through the lesson from today again. Complete the R Notebook of the educational attainment assignment and publish it to RPubs. Make sure that you make your Rmd downloadable!

Submit the link to your RPubs web page on Canvas.

LS0tCnRpdGxlOiAiTWV0aG9kcyAxIC0gQ2xhc3MgMTIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzQnCiAgICB0b2NfZmxvYXQ6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKPGJyPjxicj48YnI+PGhyPgoKVG9kYXkgd2UncmUgZ29pbmcgdG8gbGVhcm4gYSB3aG9sZSBsb3QgbW9yZSBhYm91dCBSIE1hcmtkb3duIGFuZCBob3cgdG8gdXNlIGl0IHRvIHNoYXJlIHlvdXIgYW5hbHlzaXMgd2l0aCBwZW9wbGUgdGhhdCBkb24ndCBrbm93IFIuCgojIyMgSG9tZXdvcmsgb3ZlcnZpZXcKCjxicj48aHI+PGJyPjxicj4KCmBgYHtyIGVjaG89RiwgbWVzc2FnZT1GLCB3YXJuaW5nPUYsIGZpZy53aWR0aD03fQpsaWJyYXJ5KHRpZHljZW5zdXMpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShzY2FsZXMpCgpual91bmVtcGxveW1lbnQgPC0gcmVhZF9jc3YoImRhdGEvb3V0cHV0L25qX3VuZW1wbG95bWVudC5jc3YiKQoKbmpfdW5lbXBsb3ltZW50X3Bsb3QgPC0gZ2dwbG90KGRhdGE9bmpfdW5lbXBsb3ltZW50LCBhZXMoeD1yZW9yZGVyKGdyb3VwLHVuZW1wbG95bWVudF9yYXRlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9dW5lbXBsb3ltZW50X3JhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZTAoIlRoZSB1bmVtcGxveW1lbnQgcmF0ZSBmb3IgcGVvcGxlIHRoYXQgaWRlbnRpZnkgYXMgPGJyPiIsIGdyb3VwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIGluIHRoZSBDZW5zdXMgaXMgIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZXM6OnBlcmNlbnQodW5lbXBsb3ltZW50X3JhdGUsIGFjY3VyYWN5PTEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIgKCsvLSAiLCBzY2FsZXM6OmNvbW1hKG1vZSwgYWNjdXJhY3k9LjEpLCAiKS4iKSkpICsKICBnZW9tX2NvbChmaWxsID0gIiNjNWM2ZDAiKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDAuMDU1KSxzaXplID0gMSwgY29sb3VyID0gIiMzMmE5OTEiLCBsaW5ldHlwZT0iZGFzaGVkIikgKyAKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9MjUsaGp1c3Q9MSksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbD1zY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMUwpKSArCiAgYW5ub3RhdGUoInRleHQiLCB4PTEuNzUsIHk9MC4wNiwgbGFiZWw9ICJTdGF0ZS13aWRlIFVuZW1wbG95bWVudCBSYXRlIiwgY29sb3IgPSAiIzMyYTk5MSIpICsKICBsYWJzKHRpdGxlID0gIk5ldyBKZXJzZXkgVW5lbXBsb3ltZW50IFJhdGUgYnkgUmFjZSBhbmQgRXRobmljaXR5IikgKwogIHRoZW1lKHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIikpCgpnZ3Bsb3RseShual91bmVtcGxveW1lbnRfcGxvdCwgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lIAogIGxheW91dChtYXJnaW4gPSBsaXN0KHQgPSA3NSkpCgpgYGAKCgpbTGluayB0byBQdWJsaXNoZWQgUGxvdF0oaHR0cHM6Ly9ycHVicy5jb20vRFVFLW1ldGhvZHMxL25qLXVuZW1wbG95bWVudCkKCjxicj48YnI+CgojIyMgUiBNYXJrZG93bgoKTWFya2Rvd24gaXMgYSBtYXJrLXVwIGxhbmd1YWdlIHRoYXQgaXMgdXNlZCB0byBjb252ZXJ0IHRleHQgdG8gSFRNTC4gIEFuIFIgTWFya2Rvd24gZG9jdW1lbnQgdXNlcyB0aGUgTWFya2Rvd24gbGFuZ3VhZ2UgYW5kIFIgY29kZSB0byBjcmVhdGUgYSB1c2VyLWZyaWVuZGx5IHBhZ2Ugd2l0aCBmb3JtYXR0ZWQgdGV4dCBhbmQgb3V0cHV0IGZyb20gUiBjb2RlLiBZb3UgY2FuIGV4cG9ydCBhbiBSIE1hcmtkb3duIGRvY3VtZW50IGFzOgoKMS4gSFRNTCBmaWxlCjIuIFBERgozLiBXb3JkIGRvYwoKPGJyPgoKIyMjIyBSIE1hcmtkb3duIFJlc291cmNlcwoKKiBbUiBmb3IgRGF0YSBTY2llbmNlOiBSIE1hcmtkb3duIGNoYXB0ZXJdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovci1tYXJrZG93bi5odG1sKQoqIFtSIE1hcmtkb3duOiBUaGUgRGVmaW5pdGl2ZSBHdWlkZV0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duL2Jhc2ljcy5odG1sKQoqIFtSIE1hcmtkb3duIENvb2tib29rXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24tY29va2Jvb2svaG93LXRvLXJlYWQtdGhpcy1ib29rLmh0bWwpCiogW1IgTWFya2Rvd24gQ2hlYXRzaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDIvcm1hcmtkb3duLWNoZWF0c2hlZXQucGRmKQoKVG9kYXkncyBleGVyY2lzZXMgYm9ycm93IGhlYXZpbHkgZnJvbSB0aGUgUiBNYXJrZG93biBjaGFwdGVyIGluIFIgZm9yIERhdGEgU2NpZW5jZS4gSSByZWNvbW1lbmQgYm9va21hcmtpbmcgYWxsIG9mIHRoZXNlIHJlc291cmNlcywgYW5kIHJlYWRpbmcgc29tZSBvciBhbGwgb2YgdGhlbSBpZiB5b3UgY29udGludWUgdG8gdXNlIFIgTWFya2Rvd24uCgo8YnI+CgojIyMjIFB1cnBvc2UKCkNvbW1vbiB1c2VzIGZvciBSIE1hcmtkb3duIGRvY3MgYXJlOgoKKiBJbnRlcmFjdGl2ZSBkaXNwbGF5IG9mIHJlc2VhcmNoCiogU2ltcGxlIHdheSB0byBzaGFyZSB5b3VyIGFuYWx5c2lzIG9uIHRoZSB3ZWIsIGZvciBmcmVlCiogTGVzc29ucy9UdXRvcmlhbHMKKiBUZWNobmljYWwgQm9va3MKKiBSZXNlYXJjaCBDb2xsYWJvcmF0aW9uCiogUmVzZWFyY2ggUGFwZXJzCgo8YnI+CgoqKlRvZGF5IHdlJ3JlIGdvaW5nIHRvIHVzZSB0aGlzIFIgTm90ZWJvb2sgdG8gY3JlYXRlIGFuIEhUTUwgZG9jdW1lbnQgdGhhdCBjYW4gYmUgdmlld2VkIGluIGEgd2ViIGJyb3dzZXIgdGhhdCBkaXNwbGF5czogKioKCiogKip0aGUgb3V0cHV0IG9mIG91ciBlZHVjYXRpb25hbCBhdHRhaW5tZW50IHNjcmlwdCBmcm9tIGNsYXNzIDEwKioKKiAqKnRoZSBzb3VyY2VzKioKKiAqKnRoZSBtZXRob2RzKioKKiAqKndoZXJlIHZpZXdlcnMgY2FuIGRvd25sb2FkIG91ciBSIHNjcmlwdHMgdG8gcmVwcm9kdWNlIG91ciByZXNlYXJjaCoqCgo8YnI+CgojIyMjICoqPGZvbnQgY29sb3I9IiM5NTM0NDUiPkNyZWF0ZSBhbiBSIE1hcmtkb3duIGRvY3VtZW50OioqCgo+ICogRmlsZSA+IFIgTWFya2Rvd24gCj4gICArIHNlbGVjdCBEb2N1bWVudCwgSFRNTCwgYW5kIG5hbWUgaXQgcl9tYXJrZG93bl90ZXN0Cj4gKiBTYXZlIGl0IHRvIHlvdXIgY2xhc3MxMiBmb2xkZXIsIG5hbWUgaXQgcl9tYXJrZG93bl90ZXN0Cj4gKiBMb29rIGF0IHRoZSBmaWxlcyBjcmVhdGVkIGluIHlvdXIgY2xhc3MgMTIgZm9sZGVyCj4gKiBJbiBSIFN0dWRpbywgYEtuaXRgIHJfbWFya2Rvd25fdGVzdAo+ICAgKyBTZWUgdGhlIEhUTUwgZG9jdW1lbnQgdGhhdCBwb3BzIHVwCj4gICArIExvb2sgYXQgdGhlIGZpbGVzIGNyZWF0ZWQgaW4geW91ciBjbGFzcyAxMiBmb2xkZXIKPiAgICsgTm93IHdlJ2xsIGxvb2sgYXQgdGhlIGRvY3VtZW50IHRvZ2V0aGVyCgo8L2ZvbnQ+Cgo8YnI+CgojIyMjIENvbXBvbmVudHMKClRoZXJlIGFyZSB0aHJlZSBiYXNpYyBjb21wb25lbnRzIG9mIGFuIFIgTWFya2Rvd24gZG9jdW1lbnQ6CgoqICoqbWV0YWRhdGEqKjogaW5zdHJ1Y3Rpb25zIGZvciBSIFN0dWRpbyBvbiB0aGUgdHlwZSBvZiBkb2N1bWVudCBhbmQgaG93IGl0IGxvb2tzCiAgICsgYmV0d2VlbiBhIHBhaXIgb2YgdGhyZWUgZGFzaGVzICAtLS0KICAgKyB5b3UgY2FuIGFkZCB0aGVtZXMgYW5kIHRhYmxlIG9mIGNvbnRlbnRzCiAgICsgaXQgaXMgVkVSWSBwcmVjaXNlbHkgZm9ybWF0dGVkIChpbmRlbnRhdGlvbiBtYXR0ZXJzKQoqICoqdGV4dCoqOiB0aGUgdGV4dCB0aGF0IHlvdSB3YW50IHRvIGRpc3BsYXkgaW4geW91ciBkb2N1bWVudAogICArIHdyaXR0ZW4gaW4gdGhlIE1hcmtkb3duIGxhbmd1YWdlCiogKipjb2RlKio6IHRoZSBSIGNvZGUgdGhhdCB3aWxsIGNyZWF0ZSB0aGUgdGFibGVzLCBjaGFydHMsIG1hcHMsIGdyYXBoaWNzIGRpc3BsYXllZCBpbiB0aGUgZG9jCiAgICsgQSBjb2RlIGNodW5rIHN0YXJ0cyB3aXRoIGFuZCBlbmRzIHdpdGggKnRocmVlKiBiYWNrdGlja3MgCiAgICsgQW4gaW5saW5lIFIgY29kZSBleHByZXNzaW9uIHN0YXJ0cyB3aXRoIGFuZCBlbmRzIHdpdGggKm9uZSogYmFja3RpY2suCgo8YnI+PGhyPgoKIVtdKGltZy9ybm90ZWJvb2tfYW5hdG9teV9yb3RhdGVkLnBuZyl7d2lkdGg9OTAwcHh9Cgo8YnI+PGhyPgoKIyMjIFIgTm90ZWJvb2tzCgpSIE5vdGVib29rcyBhcmUgdGhlIG1vc3QgbW9kZXJuIChhbmQgbW9zdCBjb21tb24pIHR5cGUgb2YgUiBNYXJrZG93biBkb2N1bWVudC4gVGhleSBhcmUgUiBNYXJrZG93biBkb2N1bWVudHMgdGhhdCBkZWZhdWx0IGFzIGFuIEhUTUwgZmlsZS4gVGhleSBhbHNvIHByb3ZpZGUgdGhlIGFiaWxpdHkgdG8gUHJldmlldyBhcyB5b3Ugd29yaywgYW5kIHRoZSBwdWJsaXNoZWQgd2ViIHBhZ2UgaGFzIHRoZSBvcHRpb24gZm9yIHZpZXdlcnMgdG8gZG93bmxvYWQgdGhlIC5SbWQgZG9jdW1lbnQuIFRoaXMgbWFrZXMgdGhlbSB0aGUgYmVzdCBjaG9pY2UgZm9yIGNvbGxhYm9yYXRpb24uCgoqTm90ZToqIFRoZSBgUHJldmlld2Agb25seSBkaXNwbGF5cyBjb2RlIHRoYXQgaGFzIGJlZW4gcnVuLiAgVG8gc2VlIGFsbCBvZiB0aGUgUiBjb2RlIG91dHB1dCwgeW91IGhhdmUgdG8gYFJ1bmAgdGhlIGNvZGUuCgojIyMjICoqPGZvbnQgY29sb3I9IiM5NTM0NDUiPkNyZWF0ZSBhbiBSIE5vdGVib29rIGRvY3VtZW50OioqCgo+ICogRmlsZSA+IFIgTm90ZWJvb2sgCj4gKiBTYXZlIGl0IHRvIHlvdXIgY2xhc3MxMiBmb2xkZXIsIG5hbWUgaXQgZWR1Y2F0aW9uYWxfYXR0YWlubWVudAo+ICogSW4gUiBTdHVkaW8sIGBQcmV2aWV3YCBlZHVjYXRpb25hbF9hdHRhaW5tZW50Cj4gICArIExvb2sgYXQgIHRoZSBIVE1MIGRvY3VtZW50IHRoYXQgcG9wcyB1cAo+ICogQ2xpY2sgdGhlIGdyZWVuIGFycm93IG9uIHRoZSB1cHBlciByaWdodCBvZiB0aGUgZmlyc3QgY29kZSBjaHVuawo+ICogYFByZXZpZXdgIGVkdWNhdGlvbmFsX2F0dGFpbm1lbnQgYWdhaW4KPiAgICsgTm90aWNlIHRoYXQgaXQgbm93IGRpc3BsYXlzIHRoZSB0aGUgb3V0cHV0IG9mIHRoZSBjb2RlIGNodW5rCj4gICArIENsaWNrIG9uIHRoZSB0aGUgKkNvZGUqICBidXR0b24gb24gdGhlIHVwcGVyIHJpZ2h0IG9mIHlvdXIgUHJldmlldwo+ICAgKyBEb3dubG9hZCB0aGUgUm1kLCBmaW5kIGl0IGluIHlvdXIgRG93bmxvYWRzIGFuZCBvcGVuIGl0Cgo8YnI+Cgo8L2ZvbnQ+Cgo8YnI+CgojIyMgUiBjb2RlIGluIFIgTWFya2Rvd24KClRvIHJ1biBjb2RlIGluc2lkZSBhbiBSIE1hcmtkb3duIGRvY3VtZW50IGFuZCBkaXNwbGF5IHRoZSBvdXRwdXQgaW4geW91ciBodG1sIGRvY3VtZW50IHlvdSBuZWVkIHRvIGFkZCB0aGUgUiBjb2RlIGluc2lkZSBhIGNvZGUgY2h1bmsgc28gdGhhdCBSIFN0dWRpbyBrbm93cyBleGVjdXRlIGl0LiBUbyBpbnNlcnQgYSBjaHVuazoKCiogVGhlIGtleWJvYXJkIHNob3J0Y3V0IENtZC9DdHJsICsgQWx0ICsgSQoqIFRoZSDigJxJbnNlcnTigJ0gYnV0dG9uIGljb24gaW4gdGhlIHRvb2xiYXIKCkluc2lkZSB0aGUgY29kZSBjaHVuayB5b3UgY2FuIGRvIGFueXRoaW5nIHlvdSB3b3VsZCBpbiBhIHJlZ3VsYXIgUiBzY3JpcHQgLSBpbXBvcnQgZGF0YSwgcHJvY2VzcyBpdCwgZGlzcGxheSBpdC4gIEluc3RlYWQgb2YgZGlzcGxheWluZyBpdCBpbiB5b3VyIFIgU3R1ZGlvIHdpbmRvdywgaXQgZGlzcGxheXMgdGhlIG91dHB1dCB3aXRoaW4gdGhlIFIgTm90ZWJvb2sgYW5kIGNyZWF0ZXMgYW4gSFRNTCBkb2N1bWVudCB0aGF0IGRpc3BsYXlzIHRoZSBvdXRwdXQuICAKCllvdSdsbCB3YW50IHRvIHB1dCBjb2RlIGluIGRpZmZlcmVudCBjaHVua3MsIGRlcGVuZGluZyBvbiB3aGV0aGVyIHlvdSB3YW50IHRvIGRpc3BsYXkgdGhlIGNvZGUgb3Igbm90LiAgCgoKIyMjIyBTZXR1cCAKClRoZSBmaXJzdCBjb2RlIGNodW5rIGlzIGFsd2F5cyB1c2VkIHRvIGRlZmluZSB0aGUgcGFyYW1ldGVycyBhbmQgeW91IGRvbid0IHdhbnQgdG8gZGlzcGxheSBpdC4KCiFbXShpbWcvc2V0dXAucG5nKXt3aWR0aD05MDBweH0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5Y2Vuc3VzKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShzY2FsZXMpCgpgYGAKCgpUaGUgYWJvdmUgY29kZSBjaHVuazoKCiogZGVmaW5lcyB0aGlzIGNvZGUgY2h1bmsgYXMgdGhlIGBzZXR1cGAgc28gaXQgcnVucyBiZWZvcmUgYW55IG90aGVyIGNvZGUgY2h1bmssIG5vIG1hdHRlciB3aGVyZSBpdCBpcy4gIAogICAgKyAqKllvdSBjYW4gb25seSBoYXZlIG9uZSBzZXR1cCoqLiAKICAgICsgVGhlIHNldHVwIGNvZGUgY2h1bmsgaXMgbmV2ZXIgc2hvd24gaW4geW9yIG91dHB1dCBIVE1MIGRvY3VtZW50CiogYGVjaG8gPSBUUlVFYCBzZXRzIHRoZSBkZWZhdWx0IHRvIHNob3cgdGhlIGNvZGUgaW4geW91ciBIVE1MIGRvY3VtZW50CiogbG9hZHMgdGhlIGxpYnJhcmllcwoKPGJyPgoKIyMjIyAqKjxmb250IGNvbG9yPSIjOTUzNDQ1Ij5TZXR1cCB5b3VyIFIgTm90ZWJvb2s6KioKCj4gKiBJbiBlZHVjYXRpb25hbF9hdHRhaW5tZW50Ogo+ICogRGVsZXRlIGV2ZXJ5dGhpbmcgYWZ0ZXIgdGhlIG1ldGFkYXRhCj4gKiBBZGQgc2V0dXAgY29kZSBjaHVuayBsaWtlIGFib3ZlCjwvZm9udD4KCjxicj4KCiMjIyMgQWRkIFIgY29kZQoKQWZ0ZXIgeW91IHNldCB1cCB5b3VyIFIgTm90ZWJvb2ssIHlvdSBjYW4gYWRkIHlvdXIgUiBjb2RlIHRvIGRvIHRoZSB3b3JrLiAKCjxicj4KCiMjIyMgKio8Zm9udCBjb2xvcj0iIzk1MzQ0NSI+QWRkIFIgY29kZSB0byB5b3VyIFIgTm90ZWJvb2s6KioKCj4gKiBJbiBlZHVjYXRpb25hbF9hdHRhaW5tZW50Ogo+ICogQmVsb3cgeW91ciBzZXR1cCBjb2RlLCBzdGFydCBhIG5ldyBjb2RlIGNodW5rCj4gKiBJbnNlcnQgdGhlIGNvZGUgYmVsb3cgKG9yIHlvdXIgb3duIGNvZGUgZnJvbSB5b3VyIGhvbWV3b3JrIGlmIHlvdSBwcmVmZXIpCjwvZm9udD4KCjxicj4KCmBgYHtyLCBldmFsPUZBTFNFfQphY3NfdmFycyA8LSBsb2FkX3ZhcmlhYmxlcygyMDE5LCAiYWNzMSIsIGNhY2hlID0gVCkKCmMxNTAwM192YXJzIDwtIGFjc192YXJzICU+JSAKICBmaWx0ZXIoZ3JlcGwoIkMxNTAwMyIsIG5hbWUpKSAlPiUgCiAgbXV0YXRlKGxhYmVsID0gc3RyX3JlcGxhY2UobGFiZWwsICJFc3RpbWF0ZSEhIiwgIiIpLAogICAgICAgICBsYWJlbCA9IHN0cl9yZXBsYWNlKGxhYmVsLCAiOiEhIiwgIl8iKSkgJT4lIAogIHNlbGVjdChuYW1lLCBsYWJlbCkgJT4lIAogIHJlbmFtZSh2YXJpYWJsZSA9IG5hbWUpCgpjX2VkdWNhdGlvbiA8LSBnZXRfYWNzKHN1cnZleSA9ICJhY3MxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgZ2VvZ3JhcGh5ID0gInN0YXRlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgc3RhdGUgPSAiTlkiLCAKICAgICAgICAgICAgICAgICAgICAgICB0YWJsZSA9ICJDMTUwMDMiLAogICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAiMjAxOSIpICU+JSAKICBsZWZ0X2pvaW4oYzE1MDAzX3ZhcnMsIGJ5ID0gInZhcmlhYmxlIikKCiMjIyMgSW1wb3J0IGNlbnN1cyBkYXRhIGZvciBhbGwgcGxhY2VzICMjIyMKCiMjIyMjIFBvcHVsYXRpb24gZGF0YSAjIyMjIwpyYXdfY2l0eV9wb3AgPC0gZ2V0X2FjcyhzdXJ2ZXkgPSAiYWNzMSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgZ2VvZ3JhcGh5ID0gInBsYWNlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gIjIwMTkiKQoKIyMjIyMgRWR1Y2F0aW9uYWwgYXR0YWlubWVudCBkYXRhICMjIyMjCnJhd19jaXR5X2VkdWNhdGlvbiA8LSBnZXRfYWNzKHN1cnZleSA9ICJhY3MxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb2dyYXBoeSA9ICJwbGFjZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YWJsZSA9ICJDMTUwMDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gIjIwMTkiKSAlPiUgCiAgbGVmdF9qb2luKGMxNTAwM192YXJzLCBieSA9ICJ2YXJpYWJsZSIpCgojIyMjIyBQZXItY2FwaXRhIGluY29tZSBkYXRhICMjIyMjCnJhd19jaXR5X3BjX2luY29tZSA8LSBnZXRfYWNzKHN1cnZleSA9ICJhY3MxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlb2dyYXBoeSA9ICJwbGFjZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSAiQjE5MzAxXzAwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSAiMjAxOSIpCgojIyMjIFByb2Nlc3MgY2Vuc3VzIGRhdGEgZm9yIGFsbCBwbGFjZXMgIyMjIwoKY2l0eV9wb3AgPC0gcmF3X2NpdHlfcG9wICU+JSAKICByZW5hbWUocG9wID0gZXN0aW1hdGUpICU+JSAKICBzZWxlY3QoR0VPSUQsIHBvcCkKCmNpdHlfaW5jb21lIDwtICAgcmF3X2NpdHlfcGNfaW5jb21lICU+JSAKICByZW5hbWUocGVyX2NhcGl0YV9pbmNvbWUgPSBlc3RpbWF0ZSkgJT4lIAogIHNlbGVjdChHRU9JRCwgcGVyX2NhcGl0YV9pbmNvbWUpCgpjaXR5X2VkX2F0dGFpbm1lbnQgPC0gcmF3X2NpdHlfZWR1Y2F0aW9uICU+JSAKICBmaWx0ZXIobGFiZWwgPT0gIlRvdGFsX0JhY2hlbG9yJ3MgZGVncmVlIiB8IAogICAgICAgICAgIGxhYmVsID09ICJUb3RhbF9NYXN0ZXIncyBkZWdyZWUiIHwgCiAgICAgICAgICAgbGFiZWwgPT0gIlRvdGFsX1Byb2Zlc3Npb25hbCBzY2hvb2wgZGVncmVlIiB8IAogICAgICAgICAgIGxhYmVsID09ICJUb3RhbF9Eb2N0b3JhdGUgZGVncmVlIiB8IAogICAgICAgICAgIGxhYmVsID09ICJUb3RhbDoiKSAlPiUgCiAgc2VsZWN0KEdFT0lELCBOQU1FLCBlc3RpbWF0ZSwgbGFiZWwpICU+JSAKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbGFiZWwsIHZhbHVlc19mcm9tID0gZXN0aW1hdGUpICU+JSAKICBtdXRhdGUoYmFjaGVsb3JfYW5kX2hpZ2hlciA9IHJvd1N1bXMoYWNyb3NzKGBUb3RhbF9CYWNoZWxvcidzIGRlZ3JlZWA6YFRvdGFsX0RvY3RvcmF0ZSBkZWdyZWVgKSksCiAgICAgICAgICMgcXVpY2sgdGVzdCBvZiByb3dzIHN1bXMsIEkgYWRkIHVwIHRoZSBmaXJzdCByb3csIDExNDM2Kzc2ODcrOTUwKzIyMjMsIGl0IHdvcmtlZCEKICAgICAgICAgcGN0X2F0X2xlYXN0X2JhY2hlbG9ycyA9IGJhY2hlbG9yX2FuZF9oaWdoZXIvYFRvdGFsOmApIAoKIyMjIyBDcmVhdGUgZGF0YSBmcmFtZSBmb3IgYW5hbHlzaXMgYW5kIHNjYXR0ZXJwbG90ICMjIyMKY2l0eV9hbmFseXNpcyA8LSBjaXR5X2VkX2F0dGFpbm1lbnQgJT4lIAogIHNlbGVjdChHRU9JRCwgTkFNRSwgcGN0X2F0X2xlYXN0X2JhY2hlbG9ycykgJT4lIAogIHNlcGFyYXRlKE5BTUUsIGMoImNpdHkiLCJzdGF0ZSIpLCBzZXAgPSAiLCIpICU+JSAKICBsZWZ0X2pvaW4oY2l0eV9wb3AsIGJ5ID0gIkdFT0lEIikgJT4lIAogIGxlZnRfam9pbihjaXR5X2luY29tZSwgYnkgPSAiR0VPSUQiKSAlPiUgCiAgZmlsdGVyKHBvcCA+IDY1MDAwKQoKIyMjIyBNYWtlIGl0IGludGVyYWN0aXZlICMjIyMKY2l0eV9wbG90IDwtIGNpdHlfYW5hbHlzaXMgJT4lIAogIGdncGxvdChhZXMoeCA9IHBjdF9hdF9sZWFzdF9iYWNoZWxvcnMsIHkgPSBwZXJfY2FwaXRhX2luY29tZSwgc2l6ZSA9IHBvcCwKICAgICAgICAgICAgIGNvbG9yID0gc3RhdGUsCiAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUwKGNpdHksIiwgIiwgc3RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+UG9wdWxhdGlvbiA6ICIsIHNjYWxlczo6Y29tbWEocG9wLCBhY2N1cmFjeT0xTCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+QWR1bHRzIHdpdGggYXQgbGVhc3QgYSBCYWNoZWxvcidzIERlZ3JlZSA6ICIsIHNjYWxlczo6cGVyY2VudChwY3RfYXRfbGVhc3RfYmFjaGVsb3JzLCBhY2N1cmFjeT0xTCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+UGVyLWNhcGl0YSBpbmNvbWUgOiAiLCBzY2FsZXM6OmRvbGxhcihwZXJfY2FwaXRhX2luY29tZSwgYWNjdXJhY3k9MUwpKSkpICsKICBnZW9tX3BvaW50KGFscGhhID0gLjc1KSArCiAgZ3VpZGVzKHNpemUgPSAibm9uZSIsCiAgICAgICAgIGNvbG9yID0gIm5vbmUiKSArCiAgIyBtYWtlIHN1cmUgeW91IGhhdmUgdGhlIGBzY2FsZXNgIHBhY2thZ2UgbG9hZGVkIQogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGRvbGxhcl9mb3JtYXQoYWNjdXJhY3kgPSAxKSkgKyAKICAjIGNoYW5nZSBsZWdlbmQgbGFiZWwgZm9ybWF0dGluZwogIHNjYWxlX3NpemVfYXJlYShsYWJlbHMgPSBjb21tYSwgbWF4X3NpemUgPSAxMCkgKwogIGxhYnMoeCA9ICJQcm9wb3J0aW9uIG9mIEFkdWx0cyB3aXRoIGF0IGxlYXN0IGEgQmFjaGVsb3IncyBEZWdyZWUiLCB5ID0gIlBlci1jYXBpdGEgSW5jb21lIiwKICAgICAgIHRpdGxlID0gIkVkdWNhdGlvbmFsIEF0dGFpbm1lbnQgYW5kIFBlciBDYXBpdGEgSW5jb21lIiwKICAgICAgIGNhcHRpb24gPSAiU291cmNlczogQUNTLCA1LXlyIDIwMTUtMTkiLCAKICAgICAgICMgYWRkIG5pY2UgbGFiZWwgZm9yIHNpemUgZWxlbWVudAogICAgICAgc2l6ZSA9ICJFbnJvbGxtZW50IiwKICAgICAgIGNvbG9yID0gIlVyYmFuaWNpdHkiKSArCiAgdGhlbWVfYncoKSAgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykKCmdncGxvdGx5KGNpdHlfcGxvdCwgdG9vbHRpcCA9ICJ0ZXh0IikgJT4lIAogIGxheW91dChtYXJnaW4gPSBsaXN0KHQgPSAyNSkpCmBgYAoKCjxicj4KCiMjIyMgSGlkZSBSIGNvZGUKPGJyPgoKSW4gc29tZSBjYXNlcywgeW91IG1heSB3YW50IHRvIGRpc3BsYXkgeW91ciBSIGNvZGUsIGJ1dCB1c3VhbGx5IHlvdSB3YW50IHRvIGtlZXAgaXQgaGlkZGVuIGFuZCBzaG93IHRoZSBvdXRwdXQgb25seS4gIEZvciBlYWNoIGNvZGUgY2h1bmssIHlvdSBjYW4gZGVmaW5lIHRoZSBjaHVuayBvcHRpb25zIHRvIGRldGVybWluZSB3aGV0aGVyIHRoZSBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCBhcmUgZGlzcGxheWVkIGluIHlvdXIgaHRtbCBkb2N1bWVudC4gIFRoZSBmb2xsb3dpbmcgYXJlIHRoZSBtb3N0IGNvbW1vbmx5IHVzZWQgb3B0aW9uczoKCiogYGV2YWwgPSBGQUxTRWAgcHJldmVudHMgY29kZSBmcm9tIGJlaW5nIGV2YWx1YXRlZC4gKEFuZCBvYnZpb3VzbHkgaWYgdGhlIGNvZGUgaXMgbm90IHJ1biwgbm8gcmVzdWx0cyB3aWxsIGJlIGdlbmVyYXRlZCkuIFRoaXMgaXMgdXNlZnVsIGZvciBkaXNwbGF5aW5nIGV4YW1wbGUgY29kZSwgb3IgZm9yIGRpc2FibGluZyBhIGxhcmdlIGJsb2NrIG9mIGNvZGUgd2l0aG91dCBjb21tZW50aW5nIGVhY2ggbGluZS4KKiBgaW5jbHVkZSA9IEZBTFNFYCBydW5zIHRoZSBjb2RlLCBidXQgZG9lc27igJl0IHNob3cgdGhlIGNvZGUgb3IgcmVzdWx0cyBpbiB0aGUgZmluYWwgZG9jdW1lbnQuIFVzZSB0aGlzIGZvciBzZXR1cCBjb2RlIHRoYXQgeW91IGRvbuKAmXQgd2FudCBjbHV0dGVyaW5nIHlvdXIgcmVwb3J0LgoqIGBlY2hvID0gRkFMU0VgIHByZXZlbnRzIGNvZGUsIGJ1dCBub3QgdGhlIHJlc3VsdHMgZnJvbSBhcHBlYXJpbmcgaW4gdGhlIGZpbmlzaGVkIGZpbGUuIFVzZSB0aGlzIHdoZW4gd3JpdGluZyByZXBvcnRzIGFpbWVkIGF0IHBlb3BsZSB3aG8gZG9u4oCZdCB3YW50IHRvIHNlZSB0aGUgdW5kZXJseWluZyBSIGNvZGUuCiogYG1lc3NhZ2UgPSBGQUxTRWAgb3Igd2FybmluZyA9IEZBTFNFIHByZXZlbnRzIG1lc3NhZ2VzIG9yIHdhcm5pbmdzIGZyb20gYXBwZWFyaW5nIGluIHRoZSBmaW5pc2hlZCBmaWxlLgoqIGByZXN1bHRzID0gJ2hpZGUnYCBoaWRlcyBwcmludGVkIG91dHB1dDsgYGZpZy5zaG93ID0gJ2hpZGUnYCBoaWRlcyBwbG90cy4KCllvdSBjYW4gYWxzbyBzZXQgdGhlIG9wdGlvbnMgaW4gdGhlIFlBTUwgaGVhZGVyIHRvIGJlIGFibGUgdG8gZG93bmxvYWQgdGhlIFJtZCBzbyB0aGF0IFItY3VyaW91cyByZWFkZXJzIGNhbiBsb29rIGF0IHlvdXIgY29kZS4gIEFkZCBgY29kZV9kb3dubG9hZDogdHJ1ZWAgdG8gdGhlIFlBTUwgaGVhZGVyIGxpa2UgdGhpczoKCiFbXShpbWcveWFtbC5wbmcpe3dpZHRoOjIwMHB4fQoKPGJyPgoKIyMjIyAqKjxmb250IGNvbG9yPSIjOTUzNDQ1Ij5IaWRlIFIgY29kZSBpbiB5b3VyIFIgTm90ZWJvb2s6KioKCj4gKiBhZGQgdGhlIG9wdGlvbiBgZWNobz1GQUxTRSwgbWVzc2FnZT1GLCB3YXJuaW5nPUZgIHNvIHRoYXQgdGhlIGJlZ2lubmluZyBvZiB5b3UgY29kZSBjaHVuayBsb29rcyBsaWtlIHRoaXM6Cj4gIVtdKGltZy9lY2hvLnBuZyl7d2lkdGg9MjUwcHh9Cj4gKiAgQWRkIGBjb2RlX2Rvd25sb2FkOiB0cnVlYCB0byB0aGUgWUFNTCBoZWFkZXIKPC9mb250PgoKPGJyPjxicj4KCiMjIyBUZXh0IEZvcm1hdHRpbmcgaW4gUiBNYXJrZG93bgoKIVtdKGltZy9tYXJrZG93bl90ZXh0LnBuZyl7d2lkdGg9MzAwcHh9CgpZb3UgY2FuIGFsc28gYWRkIHNwYWNlcyB3aXRoIHRoZSBsaW5lIGJyZWFrIHRhZyAtIFs8IGJyID5dKGh0dHBzOi8vd3d3Lnczc2Nob29scy5jb20vdGFncy90YWdfYnIuYXNwKQoKPGJyPjxicj4KCiMjIyMgKio8Zm9udCBjb2xvcj0iIzk1MzQ0NSI+QWRkIHRleHQgdG8geW91ciBSIE5vdGVib29rOioqCgo+ICogSW4gcl9ub3RlYm9va190ZXN0Ogo+ICogQ3JlYXRlIGNpdGF0aW9ucyBmb3IgdGhlIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgZGF0YSBmcm9tIHRoZSBhc3NpZ25tZW50IDEwIAo+ICAgKyBNYWtlIHRoZSBjaXRhdGlvbnMgbG9vayBsaWtlIGJlbG93Ogo+ICAgICAgICsgRGF0YSBTb3VyY2VzICYgSGVhZGVycyBhcmUgNHRoIGxldmVsIGhlYWRlcnMKPiAgICAgICArIFZhcmlhYmxlcyBhcmUgKipib2xkKioKPiAgICAgICArIFRhYmxlIG5hbWVzIGFyZSAqaXRhbGljaXplZCoKPiAgICAgICArIHRpZHljZW5zdXMgbG5rcyB0byBodHRwczovL3dhbGtlci1kYXRhLmNvbS90aWR5Y2Vuc3VzLwo+ICAgICAgICsgbGlzdHMgb2YgdmFyaWFibGVzIHVzZSBidWxsZXQgcG9pbnRzCgo8L2ZvbnQ+CgohW10oaW1nL2NpdGF0aW9ucy5wbmcpe3dpZHRoPTYwMHB4fQo8YnI+PGJyPjxicj48aHI+PGJyPjxicj48YnI+CgoKIyMjIEFzc2lnbm1lbnQgMTIKCiMjIyMjIFIgQXNzaWdubWVudAoKUmVhZCB0aHJvdWdoIHRoZSBsZXNzb24gZnJvbSB0b2RheSBhZ2Fpbi4gQ29tcGxldGUgdGhlIFIgTm90ZWJvb2sgb2YgdGhlIGVkdWNhdGlvbmFsIGF0dGFpbm1lbnQgYXNzaWdubWVudCBhbmQgcHVibGlzaCBpdCB0byBSUHVicy4gTWFrZSBzdXJlIHRoYXQgeW91IG1ha2UgeW91ciBSbWQgZG93bmxvYWRhYmxlIQoKU3VibWl0IHRoZSBsaW5rIHRvIHlvdXIgUlB1YnMgd2ViIHBhZ2Ugb24gQ2FudmFzLgoKIyMjIyMgUmVhZGluZ3M6IAoKUmVhZCBbVW5pdGVkLCBDaGFwdGVyIE9uZSBvZiBNaW5vciBGZWVsaW5ncyBieSBDYXRoeSBQYXJrIEhvbmddKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9maWxlL2QvMUF2cTE1S0JHUk5Wd29ubVdvQ0JzQTVkdkRmU1hjM2lqL3ZpZXc/dXNwPXNoYXJpbmcpCgpMb29rIHRocm91Z2ggYWxsIG9mIHRoZSBSIE1hcmtkb3duIHJlc291cmNlczoKCiogW1IgZm9yIERhdGEgU2NpZW5jZTogUiBNYXJrZG93biBjaGFwdGVyXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3ItbWFya2Rvd24uaHRtbCkKKiBbUiBNYXJrZG93bjogVGhlIERlZmluaXRpdmUgR3VpZGVdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi9iYXNpY3MuaHRtbCkKKiBbUiBNYXJrZG93biBDb29rYm9va10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLWNvb2tib29rL2hvdy10by1yZWFkLXRoaXMtYm9vay5odG1sKQoqIFtSIE1hcmtkb3duIENoZWF0c2hlZXRdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL3JtYXJrZG93bi1jaGVhdHNoZWV0LnBkZikKCgo8YnI+PGJyPjxicj48aHI+PGJyPjxicj48YnI+