R Markdown for everyday use: a mini tutorial

Narrative, code and visualizations


last updated: 17 Sept. 2021 - changelog

Prologue

Hi and welcome to this streamlined, fast-paced, crash course on basic R Markdown. I never intended to write a Prologue but it serves well as the only unnumbered ({.unnumbered}) or ({-}) section of the Table of Contents (TOC) to the left. Once you download the original rmd file from the dropdown menu at the top-right of this document, you’ll notice that in my YAML there is an element instructing document’s sections to be numbered (number_section: true). Nevertheless, this specific setting in Prologue, ({.unnumbered}), takes precedence and overrides general YAML settings.

Prologue 2

Next section, Prologue 2, not only it is unnumbered but it is also unlisted despite the fact that YAML again instructs otherwise. If you look to the left, Prologue 2 is not included in the TOC. Have in mind that to be unlisted, it has also to be unnumbered. You can’t unlist a heading that is numbered. So the syntax for this is {.unnumbered .unlisted}.

This is a nice opportunity to say that my YAML also instructs all of my code chunks, like the one below this paragraph, to be hidden by default, courtesy of this syntax: (code_folding: hide), so that they can only appear by clicking on the Code button to the right corner, above the chunk. Again, any “contra legem” instruction inside the code chunk takes precedence, allowing me to show the chunk on load, despite the general rule of hiding them all by default. The syntax for that is {r, class.source = 'fold-show'}.

> #Prologue 2 {.unnumbered .unlisted}

1 Introduction

And now let’s move on to the official introductions! You can find more about me if you look down to the lower left pane of this (quite modified) theme from the rmdformats package by Julien Barnier. The document you’re reading is a ‘living’ and ‘breathing’ HTML document. I can write this narrative in it, then spice it up with some executable code that you can hide/show and run, I can prepare visualizations that you can interact with and all in all, I can express myself with the absolute freedom of explaining my ideas and providing evidence outside the inherent restrictions of all known (to me) editing platforms. It’s minimal, it’s reproducible, it’s highly effective. As Donald Knuth1 eloquently put it:

“Let us change our traditional attitude to the construction of programs. Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to human beings what we want a computer to do.”

This file is meant to be my perpetual educational playground in R and I thought it could prove useful to share it and possibly help beginners to quickly overcome the hurdle of setting up their own reporting environment. This document summarizes all hints, tips, tricks and perks I come across my readings from various scattered and dispersed sources.

(If you clicked the previous link you just learned about internal links and how you can easily let your readers navigate through any section of your site).

I would like this file to end up being a very elegant and meticulous example of what can be done by using R Markdown for data analysis through the production of HTML outputs. This means that I won’t discuss PDF/DOC output environment or respective options like \newpage (page break) since in HTML (if not printed on paper) page break has no practical meaning. My main focus is to gradually standardize the reporting process and all necessary tools (packages, coding tips, best practices, etc) and then use this file as an open template for the projects to come.

p.s.: don’t forget to download the original rmd file from the menu at the top of the document to accompany you along the study of these lines. (How did I > do this? Just by adding the code_download = TRUE element in YAML). If you want to embed arbitrary files in the HTML output file for download (i.e., source > data files, etc), use these instructions.

Until you do so, here is a quick fix from xfun package that gives you a link to download a file:

> my_file <- here::here('empty_excel.xlsx')
> xfun::embed_file(my_file)

Download empty_excel.xlsx

And here you have another one, downloadthis which adds a nifty button:

> library(downloadthis)
> list(mtcars, iris) %>%
+   download_this(
+     output_name = "mtcars and iris datasets",
+     output_extension = ".xlsx",
+     button_label = "Download 'mtcars' and 'iris' datasets as xlsx",
+     button_type = "warning",
+     has_icon = TRUE,
+     icon = "fa fa-save"
+   )

2 Basic document structure

R Markdown is all about literate and reproducible programming, so every markdown document could really benefit from following a more or less standard structure, consisting of four top chunks right after the YAML section: setup, libraries, functions, reads.

  • setup: set the options you want to define globally (echo, eval, include, cache, figs, etc.), i.e.:

```r
> knitr::opts_chunk$set(message = FALSE, warning = FALSE, collapse = TRUE, prompt = TRUE) 
```
  • library: put all your library calls.

```r
> library(tidyverse)
> library(reticulate)
> library(here)
> (...)
```
  • functions: all your functions in one place.
  • reads: read the data you are going to be using in the document, i.e.:

```r
> data <- read_csv(data/my_dataset.csv)
```

3 Formatting

3.1 A few words about the formatting of this document

How did I make this section collapsible? 😉

You can use this handy code in case you need collapsible elements in your report. There are of course obvious formatting problems that need adjustment but one can take care of it according to needs and time available. Pro tip: add this summary h6 { display: inline-block; } into your styles.css and tweak accordingly if you want the arrow to appear next to the header (thank me later!)

So, the document you read is an R Markdown document exported to HTML. Its format is based on a modified version of the readthedown theme that can be found in the rmdformats package. This is a very handy theme for my needs but I felt like an aesthetic intervention was necessary to personalize some formatting options. To do that, I located the main css file of the theme (styles.css), brought it up to R’s working directory, called it in YAML section (css: custom.css) and made in there all necessary changes. From that point on, one should experiment to see what fits to her/his needs and what doesn’t (for more information visit the ‘How to’ section of this page). The winking face emoji was directly copied and pasted from emojipedia).

So, with R Markdown, one can write a strikeout bold sentence and italicize it. Also, one can write subscripts, like chemistry formulas (H2O) or superscripts (210 is 1024) and this is something I would like to underline. I can also choose the color of some words, with good old plain HTML.

Except the really basic formatting that has already been introduced above and you can explore by downloading the rmd file, one can use an array of options to beautify their reports. These options are not always aligned with R Markdown’s ‘canon’ though but they are clever and more importantly, do the job.

The ‘canon’ by the way, formulated by Markdown creator, John Gruber2, goes like this:

Markdown is intended to be as easy-to-read and easy-to-write as is feasible. Readability, however, is emphasized above all else. A Markdown-formatted document should be publishable as-is, as plain text, without looking like it’s been marked up with tags or formatting instructions. While Markdown’s syntax has been influenced by several existing text-to-HTML filters, including Setext, atx, Textile, reStructuredText, Grutatext, and EtText, the single biggest source of inspiration for Markdown’s syntax is the format of plain text email. To this end, Markdown’s syntax is comprised entirely of punctuation characters, which punctuation characters have been carefully chosen so as to look like what they mean. E.g., asterisks around a word actually look like emphasis. Markdown lists look like, well, lists. Even blockquotes look like quoted passages of text, assuming you’ve ever used email.

3.2 Line blocks

A nice way to preserve the division of lines in the output as well as any leading spaces is the line blocks syntax. Let’s write some poetry!

  April is the cruellest month, breeding
Lilacs out of the dead land, mixing
Memory and desire, stirring
Dull roots with spring rain.
Winter kept us warm, covering
Earth in forgetful snow, feeding
A little life with dried tubers. (…)

excerpt from The Waste Land by T. S. Eliot.

Although Pandoc documentation says that Inline formatting (such as emphasis) is allowed in the content, but not block-level formatting (such as block quotes or lists), I apparently used block quotes syntax and it worked…

3.3 Lists

What follows is a collection of some list types.

3.3.1 Bullet lists

Use either * or + or -.

  • one syntax error
  • two syntax errors
  • three syntax errors

3.3.2 Nested lists

  • objects

    • chair
    • table
    • spoon
  • colors

    • blue
    • red
    • white

3.3.3 Task lists

  • an unchecked task list item
  • checked item

3.3.4 Definition lists

In a data analytics project the use of definition lists would be quite useful for stakeholders or/and team members and this is a handy way of making it happen:

Aggregation

The process of collecting or gathering many separate pieces into a whole.

Analytical skills in programming

Qualities and characteristics as well as computational tools associated with using facts to solve problems. E.g.,

R markdown, R packages, etc. (this box was created with just 4 tabs)
Area chart

A data visualization that uses individual data points for a changing variable connected by a continuous line with a field in area underneath.

3.3.5 Numbered example lists

There are cases where a concise way of referring to your examples is necessary. To achieve this, you can use the @ special marker before the example sentence, followed by a space. The numbering will continue automagically throughout the document.

  1. For example, this will be my first example.

After that I will continue my report but all of a sudden the need of providing more examples is born. What is it going to happen if I use the same syntax, (@), once again?

  1. This is my second example and apparently the numbering continues.

Finally, to ‘cut off’ lists, you can insert some non-indented content like an HTML comment, which won’t produce visible output in any format, like: <!-- end of list --> or similar. By ‘breaking’ the list, normal formatting can go on.

3.4 Boxes and chunks

3.4.1 A simple box

A nice div to wrap-up conclusions:

  • This is my first conclusion
  • This is my second conclusion

3.4.2 Scrollable chunks

Some code for fixing chunk’s maximum height:

<style type="text/css">
pre {
  max-height: 300px;
  overflow-y: auto;
}

pre[class] {
  max-height: 200px;
}
</style>

Above we defined some CSS rules to limit the height of code blocks. Now we can test if these rules work on code blocks and text output by looking for the presence of a vertical scroll bar:

> # pretend that we have a lot of code in this chunk
> if (1 + 1 == 2) {
+   # of course that is true
+   print(mtcars)
+   # we just printed a lengthy data set
+ }
##                      mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
## Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
## Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
## Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
## Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
## Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
## Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
## Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
## Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
## Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
## Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
## Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
## Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
## Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
## AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
## Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
## Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
## Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
## Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

Next we add rules for a new class, scroll-100, to limit the height to 100px, and add the class to the output of a code chunk via the chunk option class.output. This way, we can manually define each chunk’s height:

> print(mtcars)
##                      mpg cyl  disp  hp drat    wt  qsec vs am gear carb
## Mazda RX4           21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
## Mazda RX4 Wag       21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
## Datsun 710          22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
## Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
## Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
## Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
## Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
## Merc 240D           24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
## Merc 230            22.8   4 140.8  95 3.92 3.150 22.90  1  0    4    2
## Merc 280            19.2   6 167.6 123 3.92 3.440 18.30  1  0    4    4
## Merc 280C           17.8   6 167.6 123 3.92 3.440 18.90  1  0    4    4
## Merc 450SE          16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
## Merc 450SL          17.3   8 275.8 180 3.07 3.730 17.60  0  0    3    3
## Merc 450SLC         15.2   8 275.8 180 3.07 3.780 18.00  0  0    3    3
## Cadillac Fleetwood  10.4   8 472.0 205 2.93 5.250 17.98  0  0    3    4
## Lincoln Continental 10.4   8 460.0 215 3.00 5.424 17.82  0  0    3    4
## Chrysler Imperial   14.7   8 440.0 230 3.23 5.345 17.42  0  0    3    4
## Fiat 128            32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
## Honda Civic         30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
## Toyota Corolla      33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
## Toyota Corona       21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1
## Dodge Challenger    15.5   8 318.0 150 2.76 3.520 16.87  0  0    3    2
## AMC Javelin         15.2   8 304.0 150 3.15 3.435 17.30  0  0    3    2
## Camaro Z28          13.3   8 350.0 245 3.73 3.840 15.41  0  0    3    4
## Pontiac Firebird    19.2   8 400.0 175 3.08 3.845 17.05  0  0    3    2
## Fiat X1-9           27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
## Porsche 914-2       26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
## Lotus Europa        30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2
## Ford Pantera L      15.8   8 351.0 264 4.22 3.170 14.50  0  1    5    4
## Ferrari Dino        19.7   6 145.0 175 3.62 2.770 15.50  0  1    5    6
## Maserati Bora       15.0   8 301.0 335 3.54 3.570 14.60  0  1    5    8
## Volvo 142E          21.4   4 121.0 109 4.11 2.780 18.60  1  1    4    2

4 Visualizations

4.1 Interactive graphics

> library(ggplot2)
> library(plotly)
> library(gapminder)
>  
> p <- gapminder %>%
+   filter(year==1977) %>%
+   ggplot( aes(gdpPercap, lifeExp, size = pop, color=continent)) +
+   geom_point() +
+   scale_x_log10() +
+   theme_bw()
>  
> ggplotly(p)

4.2 Tables & Figures

4.2.1 Simple tables

The most simple table is the one that is formatted with plain markdown syntax:

So, a syntax like this:

    |  | 2020 | 2021 | both |
    |---|---|---|---|
    | files | 160 | 80 | 1 |
    | folders | 53 | 23 | 0 |

ends up like this:

2020 2021 both
files 160 80 1
folders 53 23 0

For a convenient table generator, take a look here.

4.2.1.1 A simple kable table

> require(knitr)
> require(kableExtra)
> mtcars %>%
+   head() %>%
+   kable(digits = 1, caption = 'example of kable table') %>%
+   kable_styling(full_width = FALSE, position = 'left') %>%
+   row_spec(0,
+            bold = T,
+            color = 'white',
+            background = 'black')
Table 4.1: example of kable table
mpg cyl disp hp drat wt qsec vs am gear carb
Mazda RX4 21.0 6 160 110 3.9 2.6 16.5 0 1 4 4
Mazda RX4 Wag 21.0 6 160 110 3.9 2.9 17.0 0 1 4 4
Datsun 710 22.8 4 108 93 3.9 2.3 18.6 1 1 4 1
Hornet 4 Drive 21.4 6 258 110 3.1 3.2 19.4 1 0 3 1
Hornet Sportabout 18.7 8 360 175 3.1 3.4 17.0 0 0 3 2
Valiant 18.1 6 225 105 2.8 3.5 20.2 1 0 3 1

4.2.1.2 A less simple kable table

A table with custom column names, custom alignment and a caption:

> iris2 <- head(iris)
> knitr::kable(iris2, col.names = c('We', 'Need', 'Five', 'Names', 'Here'), align = "lccrr", caption = "An example table caption.")
Table 4.2: An example table caption.
We Need Five Names Here
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa

4.2.1.3 An example of kableExtra table

Smaller fonts, kableExtra package required, documentation here:

> kable(head(iris, 5), booktabs = TRUE) %>%
+   kable_styling(font_size = 8)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa

The following example takes a portion of a dataset and turns it into a table via kableExtra package:

> data <- faithful[1:4, ]
> knitr::kable(data,
+  caption = "Table with kable")
Table 4.3: Table with kable
eruptions waiting
3.600 79
1.800 54
3.333 74
2.283 62

This one filters the content of the table:

> summary(cars$dist)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    2.00   26.00   36.00   42.98   56.00  120.00
> summary(cars$speed)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     4.0    12.0    15.0    15.4    19.0    25.0

4.2.1.4 A row/column kableExtra table formatting

Various formatting options:

> kable(head(iris, 5), align = 'c', booktabs = TRUE) %>%
+   row_spec(1, bold = TRUE, italic = TRUE) %>% 
+   row_spec(2:3, color = 'white', background = 'black') %>%
+   row_spec(4, underline = TRUE, monospace = TRUE) %>% 
+   row_spec(5, angle = 45) %>% 
+   column_spec(5, strikeout = TRUE)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa

4.2.1.5 A kableExtra column grouping

for row grouping, here:

> iris2 <- iris[1:5, c(1, 3, 2, 4, 5)]
> names(iris2) <- gsub('[.].+', '', names(iris2))
> kable(iris2, booktabs = TRUE) %>%
+   add_header_above(c("Length" = 2, "Width" = 2, " " = 1)) %>% 
+   add_header_above(c("Measurements" = 4, "More attributes" = 1))
Measurements
More attributes
Length
Width
Sepal Petal Sepal Petal Species
5.1 1.4 3.5 0.2 setosa
4.9 1.4 3.0 0.2 setosa
4.7 1.3 3.2 0.2 setosa
4.6 1.5 3.1 0.2 setosa
5.0 1.4 3.6 0.2 setosa

4.2.1.6 More kableExtra options

Examples taken directly from the author’s website:

> library(kableExtra)
> dt <- mtcars[1:5, 1:6]
> dt %>%
+   kbl(caption = "Recreating booktabs style table") %>%
+   kable_classic_2(bootstrap_options = "striped", "hover", "condensed", full_width=F, position="float_right", html_font = "Cambria", font_size=16) %>% 
+   column_spec(5:7, bold = T) %>%
+   row_spec(3:5, bold = T, color = "white", background = "#D7261E")  %>%
+   add_header_above(c(" ", "Group 1" = 2, "Group 2" = 2, "Group 3" = 2)) %>%
+   add_header_above(c(" ", "Group 4" = 4, "Group 5" = 2))
Table 4.4: Recreating booktabs style table
Group 4
Group 5
Group 1
Group 2
Group 3
mpg cyl disp hp drat wt
Mazda RX4 21.0 6 160 110 3.90 2.620
Mazda RX4 Wag 21.0 6 160 110 3.90 2.875
Datsun 710 22.8 4 108 93 3.85 2.320
Hornet 4 Drive 21.4 6 258 110 3.08 3.215
Hornet Sportabout 18.7 8 360 175 3.15 3.440

4.2.2 Table with filtering slider

This is a very fancy DT package table with a slider for filtering!

> library(DT)
> datatable(mtcars, rownames = FALSE, filter="top", options = list(pageLength = 5, scrollX=T) )

4.2.3 Tabbed/pillset navigation

You can turn parallel sections to tabs or ‘pillsets’:

4.2.3.1 Results plot

4.2.3.1.1 Plots

We show a scatter plot in this section.

> par(mar = c(4, 4, .5, .1))
> plot(mpg ~ hp, data = mtcars, pch = 19)

4.2.3.1.2 Tables

We show the data in this tab.

> head(mtcars)

With an unnumbered, unlisted and empty section header, ## {- .unlisted}, we can end/‘cut off’ the tabset/pillset above and continue to write more paragraphs. This is the only way to escape this object.

And now that Tables section is over, I’d like to show you how to refer to any table or figure (or even equation, although I don’t deal with those yet) inside your document. The steps are:

  1. In your YAML, include use_bookdown = TRUE beneath your output: element. Beware, use proper indentation or else this won’t work.
  2. The go into your table/figure/equation code chunk and label it, i.e. 

{r my-labeled-chunk}

  1. Then write the reference, i.e., Please see Table \@ref(tab:my-labeled-chunk)

Live example: Refer to Table 4.1 above.

All set.

4.2.4 More on formatting of tables and figures

Moving on, in this figure, we play with out.width, alignment and captions (check original rmd!):

    {r, out.width = '30%', fig.align='center', fig.cap='A beautiful plot from a newbie R coder!'}
> plot(cars, pch = 18)
A beautiful plot from a newbie R coder!

Figure 4.1: A beautiful plot from a newbie R coder!

And a test in figure’s dimensions (width and height):

    {r, fig.dim=c(5,3.2)}
> plot(cars, pch = 16)

Now, we’ll place multiple figures side-by-side from the same code chunk:

    {r, fig.show='hold', out.width='50%'}
> par(mar = c(4, 4, .2, .1))
> plot(cars, pch = 19)
> plot(pressure, pch = 17)

And now, tables (the table can break across pages):

> knitr::kable(iris[1:15, ], caption = 'A caption')
Table 4.5: A caption
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
5.1 3.5 1.4 0.2 setosa
4.9 3.0 1.4 0.2 setosa
4.7 3.2 1.3 0.2 setosa
4.6 3.1 1.5 0.2 setosa
5.0 3.6 1.4 0.2 setosa
5.4 3.9 1.7 0.4 setosa
4.6 3.4 1.4 0.3 setosa
5.0 3.4 1.5 0.2 setosa
4.4 2.9 1.4 0.2 setosa
4.9 3.1 1.5 0.1 setosa
5.4 3.7 1.5 0.2 setosa
4.8 3.4 1.6 0.2 setosa
4.8 3.0 1.4 0.1 setosa
4.3 3.0 1.1 0.1 setosa
5.8 4.0 1.2 0.2 setosa

The following pagination is due to YAML element, df_print:paged

    {r cols.print=3, rows.print=3}
> mtcars

4.3 Several columns



Since R Markdown use the bootstrap framework under the hood! It is possible to benefit its powerful grid system. Basically, you can consider that your row is divided in 12 subunits of same width. You can then choose to use only a few of this subunits.



Here, I use 3 subunits of size 4 (4x3=12). The last column is used for a plot. You can read more about the grid system here. I got this result showing the following code in my R Markdown document.



> # annual flow on Nile River
> Nile %>% as.data.frame() %>% mutate(year=1871:1970) %>% 
+   rename(flow=x) %>% 
+   ggplot(.)+ geom_line(aes(x=year, y=flow), 
+                        color="darkblue", lwd=2) +
+   theme_minimal() + labs(x="", y="Flow", 
+                          title="Annual river flow on Nile River",
+                          subtitle="(1871-1970)")

4.4 Diagrams

A diagram based on DiagrammeR package:

> DiagrammeR::grViz("digraph {
+   graph [layout = dot, rankdir = TB]
+   
+   node [shape = rectangle]        
+   rec1 [label = 'Step 1. Wake up']
+   rec2 [label = 'Step 2. Write code']
+   rec3 [label =  'Step 3. ???']
+   rec4 [label = 'Step 4. PROFIT']
+   
+   # edge definitions with the node IDs
+   rec1 -> rec2 -> rec3 -> rec4
+   }",
+   height = 500)  

In the code chunk above, a special background formatting is used to change the appearance of the element. The same goes for this next example as well, which is one more diagram, this time with added parameters:

> #can also use bg-success for green
> 
> DiagrammeR::grViz("
+   digraph graph2 {
+   
+   graph [layout = dot, rankdir = LR]
+   
+   # node definitions with substituted label text
+   node [shape = oval]
+   a [label = '@@1']
+   b [label = '@@2']
+   c [label = '@@3']
+   d [label = '@@4']
+   
+   a -> b -> c -> d
+   }
+   
+   [1]: names(iris)[1]
+   [2]: names(iris)[2]
+   [3]: names(iris)[3]
+   [4]: names(iris)[4]
+   ",
+   height = 100)

Chunk background formatting could be useful when presenting good/bad approaches in solving a problem or when you want to showcase worst/optimal coding choices.

5 Images

an image of myself

And below another method (a better method) of inserting images that allows alignment and size handling, by using the knitr package:

    {r, echo=TRUE, fig.align='center', out.width='30%'}
> knitr::include_graphics("images/chess.jpg")

6 Maps

> buellton <- st_sfc(st_point(c(-120.1927, 34.6136)), crs=4326)
> m1 <- mapview(buellton, col.regions="orange") # make the map
> m1@map %>% leaflet::addMeasure(primaryLengthUnit = "meters")

7 Use of other languages

7.1 Python

Yes, you can run Python in RStudio! (more here) (reticulate package needed):

> x = 'hello, python world!'
+ print(x.split(' '))
## ['hello,', 'python', 'world!']

7.2 SQL

(more info):

> #create an in-memory RSQLite database of the mtcars dataset
> library(RSQLite)
> con <- dbConnect(RSQLite::SQLite(), dbname = ':memory:')
> 
> dbListTables(con)
## character(0)
> dbWriteTable(con, "mtcars", mtcars)
> dbListTables(con)
## [1] "mtcars"
> 
> dbListFields(con, "mtcars")
##  [1] "mpg"  "cyl"  "disp" "hp"   "drat" "wt"   "qsec" "vs"   "am"   "gear"
## [11] "carb"
> dbReadTable(con, "mtcars")
> SELECT *
+ FROM mtcars
+ WHERE (`cyl` = 4.0)
> mt_cars_df

And now we take the SQL results which are now saved as an R dataframe and we throw them into ggplot (!!!):

> library(ggplot2)
> ggplot(data = mt_cars_df, 
+        aes(x = disp, y = mpg)) +
+   geom_point() +
+   xlab("Engine Size") +
+   ylab("Miles Per Gallon") +
+   ggtitle("Fuel Efficiency Generally Decreases as Engine Size Increases")

8 Interactive documents

8.1 Via htmlwidgets package

There are two types of interactive R Markdown documents: you can use the HTML Widgets framework, or the Shiny framework (or both). The HTML Widgets framework is implemented in the R package htmlwidgets (Vaidyanathan et al. 2020), interfacing JavaScript libraries that create interactive applications, such as interactive graphics and tables. Several widget packages have been developed based on this framework, such as DT (Xie, Cheng, and Tan 2021), leaflet (Cheng, Karambelkar, and Xie 2021), and dygraphs (Vanderkam et al. 2018). Visit https://www.htmlwidgets.org to know more about widget packages as well as how to develop a widget package by yourself.

Below is a map that shows the location of the Department of Statistics, Iowa State University, following this procedure. For more HTML widgets, experiment with that collection.

> library(leaflet)
> leaflet() %>% addTiles() %>%
+   setView(-93.65, 42.0285, zoom = 17) %>%
+   addPopups(
+     -93.65, 42.0285,
+     'Here is the <b>Department of Statistics</b>, ISU'
+   )

8.2 Via shiny package

A standard R plot can be made interactive by wrapping it in the Shiny renderPlot() function. The selectInput() function creates the input widget to drive the plot.

(section commented out due to conflicts between Shiny and other packages (need to revisit this section)

8.3 Dashboards

8.3.1 flexdashboard package

You can use flexdashboard to publish groups of related data visualizations as a dashboard. A flexdashboard can either be static (a standard web page) or dynamic (a Shiny interactive document). A wide variety of components can be included in flexdashboard layouts, including:

  1. Interactive JavaScript data visualizations based on htmlwidgets.
  2. R graphical output including base, lattice, and grid graphics.
  3. Tabular data (with optional sorting, filtering, and paging).
  4. Value boxes for highlighting important summary data.
  5. Gauges for displaying values on a meter within a specified range.
  6. Text annotations of various kinds.

8.3.2 A usage example

The following examples, while working well, are not aesthetically pleasing because the output format of this file does not come from the flexdashboard package. So the idea behind this package is to use it along with the main report, via links from main to flexdashboard report.

8.3.2.1 Contact Rate

> gauge(91, min = 0, max = 100, symbol = '%', gaugeSectors(
+   success = c(80, 100), warning = c(40, 79), danger = c(0, 39)
+ ))

8.3.2.2 Average Rating

> gauge(37.4, min = 0, max = 50, gaugeSectors(
+   success = c(41, 50), warning = c(21, 40), danger = c(0, 20)
+ ))

8.3.2.3 Cancellations

> gauge(7, min = 0, max = 10, gaugeSectors(
+   success = c(0, 2), warning = c(3, 6), danger = c(7, 10)
+ ))

9 Secrets for effective coding

  1. When starting a new project, start an R Studio ‘New Project’ and keep the file path clean and tidy!!

    • Use here package to make the file really portable.
  2. Your working directory is where your *.rmd lives.

  3. Finish your R Markdown with a session-info chunk.

  4. Document your packages and include code for optional installation, when sharing the *.rmd.

  5. Fundamental mindset when using R Markdown:

    • Reproducible research (recommended Coursera lesson)
    • Literate programming (Donald Knuth)

The idea of literate programming shines some light on this dark area of science. This is an idea from Donald Knuth where you combine your text with your code output to create a document. This is a blend of your literature (text), and your programming (code), to create something that you can read from top to bottom. Imagine your paper - the introduction, methods, results, discussion, and conclusion, and all the bits of code that make each section. With rmarkdown, you can see the pieces of your data analysis all together (Nicolas Tierney, here).

  1. Name (label) thy chunks!

  2. Run code in chunks, run locally, knit, see how they work, move on.

  3. Include TODO placeholders as notes for future revisits.

  4. Avoid hard-coded info(numbers, text) if the use of inline R code is applicable and viable, i.e., There were 50 cars studied.

     There were 50 cars studied.
  5. Invest some quality time in reading packages’ documentation.

10 Books and packages

In order to prepare this file I’ve read and taken notes from the following web references:

  1. Yihui Xie, (2021). R Markdown: The Definitive Guide
  2. Yihui Xie, Christophe Dervieux, Emily Riederer, (2021). R Markdown Cookbook
  3. John MacFarlane, (2021). PANDOC official documentation (yes, I went through all of it (but only once!!))
  4. Holtz Yan, (2018).Pimp my RMD
  • To knit and run the rmd file, the installation of the following packages (please excuse me if something’s missing!) is necessary: htmltools, leaflet, flexdashboard, reticulate, knitr, shiny, pander, rmdformats, DiagrammeR, kableExtra, tidyverse, plotly, gapminder, DT, bookdown and RSQLite.

11 How to…?

To customize the postamble (lower-left corner) section of this theme (readthedown theme of rmdformats package) in order to add more YAML elements beyond the default author and date ones, you need to:

  • define in YAML the element you want, i.e., website, telephone, etc.
  • tweak template.html [‘readthedown’ section], found in rmdformats package installation folder.
  • tweak the style.css for the ‘readthedown’ theme [various postamble sections]
  • find the proper name for your glyphicon and include it in the template.html [‘readthedown’ section].

To knit with parameters:

  • Updated: 07 Sept. 2021 (this was done with a custom parameter in my YAML)

12 Session Info

Make your readers’ life easier. Provide them with what’s necessary to understand and reproduce your work!

> devtools::session_info()
## - Session info ---------------------------------------------------------------
##  setting  value                       
##  version  R version 4.1.0 (2021-05-18)
##  os       Windows 10 x64              
##  system   x86_64, mingw32             
##  ui       RTerm                       
##  language (EN)                        
##  collate  Greek_Greece.1253           
##  ctype    Greek_Greece.1253           
##  tz       Europe/Istanbul             
##  date     2021-09-17                  
## 
## - Packages -------------------------------------------------------------------
##  package           * version date       lib source        
##  assertthat          0.2.1   2019-03-21 [1] CRAN (R 4.1.0)
##  backports           1.2.1   2020-12-09 [1] CRAN (R 4.1.0)
##  base64enc           0.1-3   2015-07-28 [1] CRAN (R 4.1.0)
##  bit                 4.0.4   2020-08-04 [1] CRAN (R 4.1.0)
##  bit64               4.0.5   2020-08-30 [1] CRAN (R 4.1.0)
##  blob                1.2.2   2021-07-23 [1] CRAN (R 4.1.0)
##  bookdown            0.23    2021-08-13 [1] CRAN (R 4.1.1)
##  broom               0.7.8   2021-06-24 [1] CRAN (R 4.1.0)
##  bslib               0.3.0   2021-09-02 [1] CRAN (R 4.1.1)
##  bsplus              0.1.2   2020-06-25 [1] CRAN (R 4.1.1)
##  cachem              1.0.5   2021-05-15 [1] CRAN (R 4.1.0)
##  callr               3.7.0   2021-04-20 [1] CRAN (R 4.1.0)
##  cellranger          1.1.0   2016-07-27 [1] CRAN (R 4.1.0)
##  class               7.3-19  2021-05-03 [2] CRAN (R 4.1.0)
##  classInt            0.4-3   2020-04-07 [1] CRAN (R 4.1.1)
##  cli                 3.0.1   2021-07-17 [1] CRAN (R 4.1.0)
##  codetools           0.2-18  2020-11-04 [2] CRAN (R 4.1.0)
##  colorspace          2.0-2   2021-06-24 [1] CRAN (R 4.1.0)
##  crayon              1.4.1   2021-02-08 [1] CRAN (R 4.1.0)
##  crosstalk           1.1.1   2021-01-12 [1] CRAN (R 4.1.1)
##  data.table          1.14.0  2021-02-21 [1] CRAN (R 4.1.0)
##  DBI                 1.1.1   2021-01-15 [1] CRAN (R 4.1.1)
##  dbplyr              2.1.1   2021-04-06 [1] CRAN (R 4.1.0)
##  desc                1.3.0   2021-03-05 [1] CRAN (R 4.1.0)
##  devtools            2.4.2   2021-06-07 [1] CRAN (R 4.1.0)
##  DiagrammeR        * 1.0.6.1 2020-05-08 [1] CRAN (R 4.1.1)
##  digest              0.6.27  2020-10-24 [1] CRAN (R 4.1.0)
##  downloadthis      * 0.2.1   2020-09-17 [1] CRAN (R 4.1.1)
##  dplyr             * 1.0.7   2021-06-18 [1] CRAN (R 4.1.0)
##  DT                * 0.19    2021-09-02 [1] CRAN (R 4.1.1)
##  e1071               1.7-8   2021-07-28 [1] CRAN (R 4.1.1)
##  ellipsis            0.3.2   2021-04-29 [1] CRAN (R 4.1.0)
##  evaluate            0.14    2019-05-28 [1] CRAN (R 4.1.0)
##  fansi               0.5.0   2021-05-25 [1] CRAN (R 4.1.0)
##  farver              2.1.0   2021-02-28 [1] CRAN (R 4.1.0)
##  fastmap             1.1.0   2021-01-25 [1] CRAN (R 4.1.0)
##  flexdashboard     * 0.5.2   2020-06-24 [1] CRAN (R 4.1.1)
##  forcats           * 0.5.1   2021-01-27 [1] CRAN (R 4.1.0)
##  fs                  1.5.0   2020-07-31 [1] CRAN (R 4.1.0)
##  gapminder         * 0.3.0   2017-10-31 [1] CRAN (R 4.1.1)
##  generics            0.1.0   2020-10-31 [1] CRAN (R 4.1.0)
##  ggplot2           * 3.3.5   2021-06-25 [1] CRAN (R 4.1.0)
##  glue                1.4.2   2020-08-27 [1] CRAN (R 4.1.0)
##  gtable              0.3.0   2019-03-25 [1] CRAN (R 4.1.0)
##  haven               2.4.1   2021-04-23 [1] CRAN (R 4.1.0)
##  here                1.0.1   2020-12-13 [1] CRAN (R 4.1.1)
##  highr               0.9     2021-04-16 [1] CRAN (R 4.1.0)
##  hms                 1.1.0   2021-05-17 [1] CRAN (R 4.1.0)
##  htmltools         * 0.5.2   2021-08-25 [1] CRAN (R 4.1.1)
##  htmlwidgets         1.5.3   2020-12-10 [1] CRAN (R 4.1.1)
##  httr                1.4.2   2020-07-20 [1] CRAN (R 4.1.0)
##  jquerylib           0.1.4   2021-04-26 [1] CRAN (R 4.1.0)
##  jsonlite            1.7.2   2020-12-09 [1] CRAN (R 4.1.0)
##  kableExtra        * 1.3.4   2021-02-20 [1] CRAN (R 4.1.1)
##  KernSmooth          2.23-20 2021-05-03 [2] CRAN (R 4.1.0)
##  knitr             * 1.33    2021-04-24 [1] CRAN (R 4.1.1)
##  labeling            0.4.2   2020-10-20 [1] CRAN (R 4.1.0)
##  lattice             0.20-44 2021-05-02 [2] CRAN (R 4.1.0)
##  lazyeval            0.2.2   2019-03-15 [1] CRAN (R 4.1.0)
##  leafem              0.1.6   2021-05-24 [1] CRAN (R 4.1.1)
##  leaflet           * 2.0.4.1 2021-01-07 [1] CRAN (R 4.1.1)
##  leaflet.providers   1.9.0   2019-11-09 [1] CRAN (R 4.1.1)
##  lifecycle           1.0.0   2021-02-15 [1] CRAN (R 4.1.0)
##  lubridate           1.7.10  2021-02-26 [1] CRAN (R 4.1.0)
##  magrittr            2.0.1   2020-11-17 [1] CRAN (R 4.1.0)
##  mapview           * 2.10.0  2021-06-05 [1] CRAN (R 4.1.1)
##  Matrix              1.3-3   2021-05-04 [2] CRAN (R 4.1.0)
##  memoise             2.0.0   2021-01-26 [1] CRAN (R 4.1.0)
##  mime                0.11    2021-06-23 [1] CRAN (R 4.1.0)
##  modelr              0.1.8   2020-05-19 [1] CRAN (R 4.1.0)
##  munsell             0.5.0   2018-06-12 [1] CRAN (R 4.1.0)
##  pander            * 0.6.4   2021-06-13 [1] CRAN (R 4.1.1)
##  pillar              1.6.2   2021-07-29 [1] CRAN (R 4.1.0)
##  pkgbuild            1.2.0   2020-12-15 [1] CRAN (R 4.1.0)
##  pkgconfig           2.0.3   2019-09-22 [1] CRAN (R 4.1.0)
##  pkgload             1.2.1   2021-04-06 [1] CRAN (R 4.1.0)
##  plotly            * 4.9.4.1 2021-06-18 [1] CRAN (R 4.1.1)
##  png                 0.1-7   2013-12-03 [1] CRAN (R 4.1.0)
##  prettyunits         1.1.1   2020-01-24 [1] CRAN (R 4.1.0)
##  processx            3.5.2   2021-04-30 [1] CRAN (R 4.1.0)
##  proxy               0.4-26  2021-06-07 [1] CRAN (R 4.1.1)
##  ps                  1.6.0   2021-02-28 [1] CRAN (R 4.1.0)
##  purrr             * 0.3.4   2020-04-17 [1] CRAN (R 4.1.0)
##  R6                  2.5.1   2021-08-19 [1] CRAN (R 4.1.0)
##  rappdirs            0.3.3   2021-01-31 [1] CRAN (R 4.1.0)
##  raster              3.4-13  2021-06-18 [1] CRAN (R 4.1.1)
##  RColorBrewer        1.1-2   2014-12-07 [1] CRAN (R 4.1.0)
##  Rcpp                1.0.7   2021-07-07 [1] CRAN (R 4.1.0)
##  readr             * 2.0.1   2021-08-10 [1] CRAN (R 4.1.1)
##  readxl              1.3.1   2019-03-13 [1] CRAN (R 4.1.0)
##  remotes             2.4.0   2021-06-02 [1] CRAN (R 4.1.0)
##  reprex              2.0.0   2021-04-02 [1] CRAN (R 4.1.0)
##  reticulate        * 1.20    2021-05-03 [1] CRAN (R 4.1.1)
##  rlang               0.4.11  2021-04-30 [1] CRAN (R 4.1.0)
##  rmarkdown           2.10    2021-08-06 [1] CRAN (R 4.1.0)
##  rmdformats        * 1.0.2   2021-04-19 [1] CRAN (R 4.1.1)
##  rprojroot           2.0.2   2020-11-15 [1] CRAN (R 4.1.0)
##  RSQLite           * 2.2.8   2021-08-21 [1] CRAN (R 4.1.1)
##  rstudioapi          0.13    2020-11-12 [1] CRAN (R 4.1.0)
##  rvest               1.0.0   2021-03-09 [1] CRAN (R 4.1.0)
##  sass                0.4.0   2021-05-12 [1] CRAN (R 4.1.1)
##  satellite           1.0.2   2019-12-09 [1] CRAN (R 4.1.1)
##  scales              1.1.1   2020-05-11 [1] CRAN (R 4.1.0)
##  sessioninfo         1.1.1   2018-11-05 [1] CRAN (R 4.1.0)
##  sf                * 1.0-2   2021-07-26 [1] CRAN (R 4.1.1)
##  sp                  1.4-5   2021-01-10 [1] CRAN (R 4.1.1)
##  stringi             1.6.2   2021-05-17 [1] CRAN (R 4.1.0)
##  stringr           * 1.4.0   2019-02-10 [1] CRAN (R 4.1.0)
##  svglite             2.0.0   2021-02-20 [1] CRAN (R 4.1.1)
##  systemfonts         1.0.2   2021-05-11 [1] CRAN (R 4.1.1)
##  testthat            3.0.4   2021-07-01 [1] CRAN (R 4.1.0)
##  tibble            * 3.1.3   2021-07-23 [1] CRAN (R 4.1.0)
##  tidyr             * 1.1.3   2021-03-03 [1] CRAN (R 4.1.0)
##  tidyselect          1.1.1   2021-04-30 [1] CRAN (R 4.1.0)
##  tidyverse         * 1.3.1   2021-04-15 [1] CRAN (R 4.1.1)
##  tzdb                0.1.2   2021-07-20 [1] CRAN (R 4.1.0)
##  units               0.7-2   2021-06-08 [1] CRAN (R 4.1.1)
##  usethis             2.0.1   2021-02-10 [1] CRAN (R 4.1.0)
##  utf8                1.2.2   2021-07-24 [1] CRAN (R 4.1.0)
##  vctrs               0.3.8   2021-04-29 [1] CRAN (R 4.1.0)
##  viridisLite         0.4.0   2021-04-13 [1] CRAN (R 4.1.0)
##  visNetwork          2.0.9   2019-12-06 [1] CRAN (R 4.1.1)
##  webshot             0.5.2   2019-11-22 [1] CRAN (R 4.1.1)
##  withr               2.4.2   2021-04-18 [1] CRAN (R 4.1.0)
##  writexl             1.4.0   2021-04-20 [1] CRAN (R 4.1.1)
##  xfun                0.25    2021-08-06 [1] CRAN (R 4.1.1)
##  xml2                1.3.2   2020-04-23 [1] CRAN (R 4.1.0)
##  yaml                2.2.1   2020-02-01 [1] CRAN (R 4.1.0)
## 
## [1] C:/Users/George/Documents/R/win-library/4.1
## [2] C:/Program Files/R/R-4.1.0/library

13 Changelog

170921 Added a link to the ‘Tables Generator’ website.


  1. (1984) download his ‘Literate Programming’ paper↩︎

  2. John Gruber, ‘Daring Fireball’ blog.↩︎

LS0tDQp0aXRsZTogIlIgTWFya2Rvd24gZm9yIGV2ZXJ5ZGF5IHVzZTogYSBtaW5pIHR1dG9yaWFsIg0Kc3VidGl0bGU6ICJOYXJyYXRpdmUsIGNvZGUgYW5kIHZpc3VhbGl6YXRpb25zIg0KZGF0ZTogImByIGZvcm1hdChTeXMuRGF0ZSgpKWAiDQphdXRob3I6IEdlb3JnZSBNYXRzYXJpZGlzLCBNLlNjLg0KZW1haWw6IGdlbWF0c0BnbWFpbC5jb20NCndlYnNpdGU6IGh0dHBzOi8vd3d3LmNpdmlsaXRhcy5nci9wb3J0Zm9saW8NCm51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KY3NzOiBjdXN0b20uY3NzDQpwYXJhbXM6DQogIGRhdGU6IDA3IFNlcHQuIDIwMjENCg0Kb3V0cHV0Og0KICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAga2VlcF9tZDogdHJ1ZQ0KICAgIGRmX3ByaW50OiBwYWdlZCAjc2VlIHRoaXM6IGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi9odG1sLWRvY3VtZW50Lmh0bWwjdGFiOmRmLXByaW50DQogICAgbGlnaHRib3g6IFRSVUUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBhbmNob3Jfc2VjdGlvbnM6IHRydWUNCiAgICB1c2VfYm9va2Rvd246IFRSVUUNCiAgICANCi0tLQ0KDQoNCi0tLQ0KDQojIyMjIyMgbGFzdCB1cGRhdGVkOiAxNyBTZXB0LiAyMDIxIC0gW2NoYW5nZWxvZ10oI2NoYW5nZWxvZykgey51bm51bWJlcmVkIC51bmxpc3RlZH0NCg0KPCEtLSBzZXR1cCBjaHVuayAtLT4NCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY29sbGFwc2UgPSBUUlVFLCBwcm9tcHQgPSBUUlVFKSAjIFRoZXNlIGFyZSBHbG9iYWwgT3B0aW9ucyANCmBgYA0KDQo8IS0tIGxpYnJhcmllcyBjaHVuayAtLT4NCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGh0bWx0b29scykNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkoZmxleGRhc2hib2FyZCkNCmxpYnJhcnkocmV0aWN1bGF0ZSkNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KHBhbmRlcikNCmxpYnJhcnkocm1kZm9ybWF0cykNCmxpYnJhcnkoRGlhZ3JhbW1lUikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnYXBtaW5kZXIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoUlNRTGl0ZSkNCmxpYnJhcnkobWFwdmlldykNCmxpYnJhcnkoc2YpDQpgYGANCg0KDQojIFByb2xvZ3VlIHsudW5udW1iZXJlZH0NCg0KDQoNCkhpIGFuZCB3ZWxjb21lIHRvIHRoaXMgc3RyZWFtbGluZWQsIGZhc3QtcGFjZWQsIGNyYXNoIGNvdXJzZSBvbiBiYXNpYyBSIE1hcmtkb3duLiBJIG5ldmVyIGludGVuZGVkIHRvIHdyaXRlIGEgKipQcm9sb2d1ZSoqIGJ1dCBpdCBzZXJ2ZXMgd2VsbCBhcyB0aGUgb25seSB1bm51bWJlcmVkIChgey51bm51bWJlcmVkfWApIG9yIChgey19YCkgc2VjdGlvbiBvZiB0aGUgVGFibGUgb2YgQ29udGVudHMgKFRPQykgdG8gdGhlIGxlZnQuIE9uY2UgeW91IGRvd25sb2FkIHRoZSBvcmlnaW5hbCBgcm1kYCBmaWxlIGZyb20gdGhlIGRyb3Bkb3duIG1lbnUgYXQgdGhlIHRvcC1yaWdodCBvZiB0aGlzIGRvY3VtZW50LCB5b3UnbGwgbm90aWNlIHRoYXQgaW4gbXkgW1lBTUxdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy95bWx0aGlzL3ZpZ25ldHRlcy95YW1sLWZpZWxkZ3VpZGUuaHRtbCkgdGhlcmUgaXMgYW4gZWxlbWVudCBpbnN0cnVjdGluZyBkb2N1bWVudCdzIHNlY3Rpb25zIHRvIGJlIG51bWJlcmVkIChgbnVtYmVyX3NlY3Rpb246IHRydWVgKS4gTmV2ZXJ0aGVsZXNzLCB0aGlzIHNwZWNpZmljIHNldHRpbmcgaW4gUHJvbG9ndWUsIChgey51bm51bWJlcmVkfWApLCB0YWtlcyBwcmVjZWRlbmNlIGFuZCBvdmVycmlkZXMgZ2VuZXJhbCBZQU1MIHNldHRpbmdzLg0KDQojIFByb2xvZ3VlIDIgey51bm51bWJlcmVkIC51bmxpc3RlZH0NCg0KTmV4dCBzZWN0aW9uLCAqUHJvbG9ndWUgMiosIG5vdCBvbmx5IGl0IGlzIHVubnVtYmVyZWQgYnV0IGl0IGlzIGFsc28gdW5saXN0ZWQgZGVzcGl0ZSB0aGUgZmFjdCB0aGF0IFlBTUwgYWdhaW4gaW5zdHJ1Y3RzIG90aGVyd2lzZS4gSWYgeW91IGxvb2sgdG8gdGhlIGxlZnQsICoqUHJvbG9ndWUgMioqIGlzIG5vdCBpbmNsdWRlZCBpbiB0aGUgVE9DLiBIYXZlIGluIG1pbmQgdGhhdCB0byBiZSB1bmxpc3RlZCwgaXQgaGFzIGFsc28gdG8gYmUgdW5udW1iZXJlZC4gWW91IGNhbid0IHVubGlzdCBhIGhlYWRpbmcgdGhhdCBpcyBudW1iZXJlZC4gU28gdGhlIHN5bnRheCBmb3IgdGhpcyBpcyBgey51bm51bWJlcmVkIC51bmxpc3RlZH1gLg0KDQpUaGlzIGlzIGEgbmljZSBvcHBvcnR1bml0eSB0byBzYXkgdGhhdCBteSBZQU1MIGFsc28gaW5zdHJ1Y3RzIGFsbCBvZiBteSBjb2RlIGNodW5rcywgbGlrZSB0aGUgb25lIGJlbG93IHRoaXMgcGFyYWdyYXBoLCB0byBiZSBoaWRkZW4gYnkgZGVmYXVsdCwgY291cnRlc3kgb2YgdGhpcyBzeW50YXg6IChgY29kZV9mb2xkaW5nOiBoaWRlYCksIHNvIHRoYXQgdGhleSBjYW4gb25seSBhcHBlYXIgYnkgY2xpY2tpbmcgb24gdGhlIGBDb2RlYCBidXR0b24gdG8gdGhlIHJpZ2h0IGNvcm5lciwgYWJvdmUgdGhlIGNodW5rLiBBZ2FpbiwgYW55ICoiY29udHJhIGxlZ2VtIiogaW5zdHJ1Y3Rpb24gaW5zaWRlIHRoZSBjb2RlIGNodW5rIHRha2VzIHByZWNlZGVuY2UsIGFsbG93aW5nIG1lIHRvIHNob3cgdGhlIGNodW5rIG9uIGxvYWQsIGRlc3BpdGUgdGhlIGdlbmVyYWwgcnVsZSBvZiBoaWRpbmcgdGhlbSBhbGwgYnkgZGVmYXVsdC4gVGhlIHN5bnRheCBmb3IgdGhhdCBpcyBge3IsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfWAuDQoNCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30NCiNQcm9sb2d1ZSAyIHsudW5udW1iZXJlZCAudW5saXN0ZWR9DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgSW50cm9kdWN0aW9uDQoNCkFuZCBub3cgbGV0J3MgbW92ZSBvbiB0byB0aGUgb2ZmaWNpYWwgaW50cm9kdWN0aW9ucyEgWW91IGNhbiBmaW5kIG1vcmUgYWJvdXQgbWUgaWYgeW91IGxvb2sgZG93biB0byB0aGUgbG93ZXIgbGVmdCBwYW5lIG9mIHRoaXMgKHF1aXRlIG1vZGlmaWVkKSB0aGVtZSBmcm9tIHRoZSBgcm1kZm9ybWF0c2AgW3BhY2thZ2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9qdWJhL3JtZGZvcm1hdHMpIGJ5IEp1bGllbiBCYXJuaWVyLiBUaGUgZG9jdW1lbnQgeW91J3JlIHJlYWRpbmcgaXMgYSAnbGl2aW5nJyBhbmQgJ2JyZWF0aGluZycgSFRNTCBkb2N1bWVudC4gSSBjYW4gd3JpdGUgdGhpcyBuYXJyYXRpdmUgaW4gaXQsIHRoZW4gc3BpY2UgaXQgdXAgd2l0aCBzb21lIGV4ZWN1dGFibGUgY29kZSB0aGF0ICoqeW91KiogY2FuIGhpZGUvc2hvdyBhbmQgcnVuLCBJIGNhbiBwcmVwYXJlIHZpc3VhbGl6YXRpb25zIHRoYXQgeW91IGNhbiBpbnRlcmFjdCB3aXRoIGFuZCBhbGwgaW4gYWxsLCBJIGNhbiBleHByZXNzIG15c2VsZiB3aXRoIHRoZSBhYnNvbHV0ZSBmcmVlZG9tIG9mIGV4cGxhaW5pbmcgbXkgaWRlYXMgYW5kIHByb3ZpZGluZyBldmlkZW5jZSBvdXRzaWRlIHRoZSBpbmhlcmVudCByZXN0cmljdGlvbnMgb2YgYWxsIGtub3duICh0byBtZSkgZWRpdGluZyBwbGF0Zm9ybXMuIEl0J3MgbWluaW1hbCwgaXQncyByZXByb2R1Y2libGUsIGl0J3MgaGlnaGx5IGVmZmVjdGl2ZS4gQXMgRG9uYWxkIEtudXRoXlsoMTk4NCkgW2Rvd25sb2FkXShodHRwczovL3dlYi5hcmNoaXZlLm9yZy93ZWIvMjAxOTA4MTkyMTE4MTUvaHR0cDovL3d3dy5saXRlcmF0ZXByb2dyYW1taW5nLmNvbS9rbnV0aHdlYi5wZGYpIGhpcyAnTGl0ZXJhdGUgUHJvZ3JhbW1pbmcnIHBhcGVyXSBlbG9xdWVudGx5IHB1dCBpdDoNCg0KPiAiTGV0IHVzIGNoYW5nZSBvdXIgdHJhZGl0aW9uYWwgYXR0aXR1ZGUgdG8gdGhlIGNvbnN0cnVjdGlvbiBvZiBwcm9ncmFtcy4gSW5zdGVhZCBvZiBpbWFnaW5pbmcgdGhhdCBvdXIgbWFpbiB0YXNrIGlzIHRvIGluc3RydWN0IGEgY29tcHV0ZXIgd2hhdCB0byBkbywgbGV0IHVzIGNvbmNlbnRyYXRlIHJhdGhlciBvbiBleHBsYWluaW5nIHRvIGh1bWFuIGJlaW5ncyB3aGF0IHdlIHdhbnQgYSBjb21wdXRlciB0byBkby4iDQoNClRoaXMgZmlsZSBpcyBtZWFudCB0byBiZSBteSAqcGVycGV0dWFsIGVkdWNhdGlvbmFsIHBsYXlncm91bmQgaW4gUiogYW5kIEkgdGhvdWdodCBpdCBjb3VsZCBwcm92ZSB1c2VmdWwgdG8gc2hhcmUgaXQgYW5kIHBvc3NpYmx5IGhlbHAgYmVnaW5uZXJzIHRvIHF1aWNrbHkgb3ZlcmNvbWUgdGhlIGh1cmRsZSBvZiBzZXR0aW5nIHVwIHRoZWlyIG93biByZXBvcnRpbmcgZW52aXJvbm1lbnQuIFRoaXMgZG9jdW1lbnQgc3VtbWFyaXplcyBhbGwgaGludHMsIHRpcHMsIHRyaWNrcyBhbmQgcGVya3MgSSBjb21lIGFjcm9zcyBteSByZWFkaW5ncyBmcm9tIHZhcmlvdXMgW3NjYXR0ZXJlZCBhbmQgZGlzcGVyc2VkIHNvdXJjZXNdKCNib29rcykuDQoNCigqSWYgeW91IGNsaWNrZWQgdGhlIHByZXZpb3VzIGxpbmsgeW91IGp1c3QgbGVhcm5lZCBhYm91dCBbaW50ZXJuYWwgbGlua3Ndey51bH0gYW5kIGhvdyB5b3UgY2FuIGVhc2lseSBsZXQgeW91ciByZWFkZXJzIG5hdmlnYXRlIHRocm91Z2ggYW55IHNlY3Rpb24gb2YgeW91ciBzaXRlKikuDQoNCkkgd291bGQgbGlrZSB0aGlzIGZpbGUgdG8gZW5kIHVwIGJlaW5nIGEgdmVyeSBlbGVnYW50IGFuZCBtZXRpY3Vsb3VzIGV4YW1wbGUgKipvZiB3aGF0IGNhbiBiZSBkb25lKiogYnkgdXNpbmcgUiBNYXJrZG93biBmb3IgZGF0YSBhbmFseXNpcyB0aHJvdWdoIHRoZSBwcm9kdWN0aW9uIG9mICoqSFRNTCBvdXRwdXRzKiouIFRoaXMgbWVhbnMgdGhhdCBJIHdvbid0IGRpc2N1c3MgUERGL0RPQyBvdXRwdXQgZW52aXJvbm1lbnQgb3IgcmVzcGVjdGl2ZSBvcHRpb25zIGxpa2UgYFxuZXdwYWdlYCAocGFnZSBicmVhaykgc2luY2UgaW4gSFRNTCAoaWYgbm90IHByaW50ZWQgb24gcGFwZXIpIHBhZ2UgYnJlYWsgaGFzIG5vIHByYWN0aWNhbCBtZWFuaW5nLiBNeSBtYWluIGZvY3VzIGlzIHRvIGdyYWR1YWxseSBzdGFuZGFyZGl6ZSB0aGUgcmVwb3J0aW5nIHByb2Nlc3MgYW5kIGFsbCBuZWNlc3NhcnkgdG9vbHMgKHBhY2thZ2VzLCBjb2RpbmcgdGlwcywgYmVzdCBwcmFjdGljZXMsIGV0YykgYW5kIHRoZW4gdXNlIHRoaXMgZmlsZSBhcyBhbiBvcGVuIHRlbXBsYXRlIGZvciB0aGUgcHJvamVjdHMgdG8gY29tZS4NCg0KPiAqcC5zLjogZG9uJ3QgZm9yZ2V0IHRvIGRvd25sb2FkIHRoZSBvcmlnaW5hbCBgcm1kYCBmaWxlIGZyb20gdGhlIG1lbnUgYXQgdGhlIHRvcCBvZiB0aGUgZG9jdW1lbnQgdG8gYWNjb21wYW55IHlvdSBhbG9uZyB0aGUgc3R1ZHkgb2YgdGhlc2UgbGluZXMuIChIb3cgZGlkIEkgPiBkbyB0aGlzPyBKdXN0IGJ5IGFkZGluZyB0aGUgYGNvZGVfZG93bmxvYWQgPSBUUlVFYCBlbGVtZW50IGluIFlBTUwpLiBJZiB5b3Ugd2FudCB0byBlbWJlZCBhcmJpdHJhcnkgZmlsZXMgaW4gdGhlIEhUTUwgb3V0cHV0IGZpbGUgZm9yIGRvd25sb2FkIChpLmUuLCBzb3VyY2UgPiBkYXRhIGZpbGVzLCBldGMpLCB1c2UgdGhlc2UgW2luc3RydWN0aW9uc10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLWNvb2tib29rL2VtYmVkLWZpbGUuaHRtbCkqLg0KDQpVbnRpbCB5b3UgZG8gc28sIGhlcmUgaXMgYSBxdWljayBmaXggZnJvbSBgeGZ1bmAgcGFja2FnZSB0aGF0IGdpdmVzIHlvdSBhIGxpbmsgdG8gZG93bmxvYWQgYSBmaWxlOg0KDQoNCmBgYHtyIGVjaG99DQpteV9maWxlIDwtIGhlcmU6OmhlcmUoJ2VtcHR5X2V4Y2VsLnhsc3gnKQ0KeGZ1bjo6ZW1iZWRfZmlsZShteV9maWxlKQ0KYGBgDQoNCkFuZCBoZXJlIHlvdSBoYXZlIGFub3RoZXIgb25lLCBgZG93bmxvYWR0aGlzYCB3aGljaCBhZGRzIGEgbmlmdHkgYnV0dG9uOg0KDQpgYGB7cn0NCmxpYnJhcnkoZG93bmxvYWR0aGlzKQ0KbGlzdChtdGNhcnMsIGlyaXMpICU+JQ0KICBkb3dubG9hZF90aGlzKA0KICAgIG91dHB1dF9uYW1lID0gIm10Y2FycyBhbmQgaXJpcyBkYXRhc2V0cyIsDQogICAgb3V0cHV0X2V4dGVuc2lvbiA9ICIueGxzeCIsDQogICAgYnV0dG9uX2xhYmVsID0gIkRvd25sb2FkICdtdGNhcnMnIGFuZCAnaXJpcycgZGF0YXNldHMgYXMgeGxzeCIsDQogICAgYnV0dG9uX3R5cGUgPSAid2FybmluZyIsDQogICAgaGFzX2ljb24gPSBUUlVFLA0KICAgIGljb24gPSAiZmEgZmEtc2F2ZSINCiAgKQ0KYGBgDQoNCg0KDQojIEJhc2ljIGRvY3VtZW50IHN0cnVjdHVyZQ0KDQpSIE1hcmtkb3duIGlzIGFsbCBhYm91dCBsaXRlcmF0ZSBhbmQgcmVwcm9kdWNpYmxlIHByb2dyYW1taW5nLCBzbyBldmVyeSBtYXJrZG93biBkb2N1bWVudCBjb3VsZCByZWFsbHkgYmVuZWZpdCBmcm9tIGZvbGxvd2luZyBhIG1vcmUgb3IgbGVzcyAqKnN0YW5kYXJkKiogc3RydWN0dXJlLCBjb25zaXN0aW5nIG9mIGZvdXIgKip0b3AqKiBjaHVua3MgcmlnaHQgYWZ0ZXIgdGhlIFlBTUwgc2VjdGlvbjogc2V0dXAsIGxpYnJhcmllcywgZnVuY3Rpb25zLCByZWFkcy4NCg0KKyAqKnNldHVwKio6IFtzZXQgdGhlIG9wdGlvbnNdKGh0dHBzOi8veWlodWkub3JnL2tuaXRyL29wdGlvbnMvI2NodW5rLW9wdGlvbnMpIHlvdSB3YW50IHRvIGRlZmluZSBnbG9iYWxseSAoZWNobywgZXZhbCwgaW5jbHVkZSwgY2FjaGUsIGZpZ3MsIGV0Yy4pLCBpLmUuOg0KICAgIA0KYGBgYA0KYGBge3IgZXZhbD1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY29sbGFwc2UgPSBUUlVFLCBwcm9tcHQgPSBUUlVFKSANCmBgYA0KYGBgYA0KDQorICoqbGlicmFyeSoqOiBwdXQgYWxsIHlvdXIgbGlicmFyeSBjYWxscy4NCmBgYGANCmBgYHtyIGV2YWw9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocmV0aWN1bGF0ZSkNCmxpYnJhcnkoaGVyZSkNCiguLi4pDQpgYGANCmBgYGANCg0KKyAqKmZ1bmN0aW9ucyoqOiBhbGwgeW91ciBmdW5jdGlvbnMgaW4gb25lIHBsYWNlLg0KKyAqKnJlYWRzKio6IHJlYWQgdGhlIGRhdGEgeW91IGFyZSBnb2luZyB0byBiZSB1c2luZyBpbiB0aGUgZG9jdW1lbnQsIGkuZS46DQoNCmBgYGANCmBgYHtyIGV2YWw9RkFMU0V9DQpkYXRhIDwtIHJlYWRfY3N2KGRhdGEvbXlfZGF0YXNldC5jc3YpDQpgYGANCmBgYGANCg0KIyBGb3JtYXR0aW5nDQoNCiMjIEEgZmV3IHdvcmRzIGFib3V0IHRoZSBmb3JtYXR0aW5nIG9mIHRoaXMgZG9jdW1lbnQNCg0KPGRldGFpbHM+DQo8c3VtbWFyeT4NCg0KPGg2Pg0KDQpIb3cgZGlkIEkgbWFrZSB0aGlzIHNlY3Rpb24gY29sbGFwc2libGU/IPCfmIkNCg0KPC9oNj4NCg0KPC9zdW1tYXJ5Pg0KDQoqWW91IGNhbiB1c2UgdGhpcyBoYW5keSBjb2RlIGluIGNhc2UgeW91IG5lZWQgY29sbGFwc2libGUgZWxlbWVudHMgaW4geW91ciByZXBvcnQuIFRoZXJlIGFyZSBvZiBjb3Vyc2Ugb2J2aW91cyBmb3JtYXR0aW5nIHByb2JsZW1zIHRoYXQgbmVlZCBhZGp1c3RtZW50IGJ1dCBvbmUgY2FuIHRha2UgY2FyZSBvZiBpdCBhY2NvcmRpbmcgdG8gbmVlZHMgYW5kIHRpbWUgYXZhaWxhYmxlLiBQcm8gdGlwOiBhZGQgdGhpcyBgc3VtbWFyeSBoNiB7IGRpc3BsYXk6IGlubGluZS1ibG9jazsgfWAgaW50byB5b3VyIHN0eWxlcy5jc3MgYW5kIHR3ZWFrIGFjY29yZGluZ2x5IGlmIHlvdSB3YW50IHRoZSBhcnJvdyB0byBhcHBlYXIgbmV4dCB0byB0aGUgaGVhZGVyICh0aGFuayBtZSBsYXRlciEpKg0KDQo8L2RldGFpbHM+DQoNCg0KU28sIHRoZSBkb2N1bWVudCB5b3UgcmVhZCBpcyBhbiBSIE1hcmtkb3duIGRvY3VtZW50IGV4cG9ydGVkIHRvIEhUTUwuIEl0cyBmb3JtYXQgaXMgYmFzZWQgb24gYSBtb2RpZmllZCB2ZXJzaW9uIG9mIHRoZSAqKnJlYWR0aGVkb3duKiogdGhlbWUgdGhhdCBjYW4gYmUgZm91bmQgaW4gdGhlIGBybWRmb3JtYXRzYCBwYWNrYWdlLiBUaGlzIGlzIGEgdmVyeSBoYW5keSB0aGVtZSBmb3IgbXkgbmVlZHMgYnV0IEkgZmVsdCBsaWtlIGFuIGFlc3RoZXRpYyBpbnRlcnZlbnRpb24gd2FzIG5lY2Vzc2FyeSB0byBwZXJzb25hbGl6ZSBzb21lIGZvcm1hdHRpbmcgb3B0aW9ucy4gVG8gZG8gdGhhdCwgSSBsb2NhdGVkIHRoZSBtYWluIGNzcyBmaWxlIG9mIHRoZSB0aGVtZSAoc3R5bGVzLmNzcyksIGJyb3VnaHQgaXQgdXAgdG8gUidzIHdvcmtpbmcgZGlyZWN0b3J5LCBjYWxsZWQgaXQgaW4gWUFNTCBzZWN0aW9uIChjc3M6IGN1c3RvbS5jc3MpIGFuZCBtYWRlIGluIHRoZXJlIGFsbCBuZWNlc3NhcnkgY2hhbmdlcy4gRnJvbSB0aGF0IHBvaW50IG9uLCBvbmUgc2hvdWxkIGV4cGVyaW1lbnQgdG8gc2VlIHdoYXQgZml0cyB0byBoZXIvaGlzIG5lZWRzIGFuZCB3aGF0IGRvZXNuJ3QgKGZvciBtb3JlIGluZm9ybWF0aW9uIHZpc2l0IHRoZSBbJ0hvdyB0byddKCNob3d0bykgc2VjdGlvbiBvZiB0aGlzIHBhZ2UpLiBUaGUgYHdpbmtpbmcgZmFjZWAgZW1vamkgd2FzICoqZGlyZWN0bHkqKiBjb3BpZWQgYW5kIHBhc3RlZCBmcm9tIFtlbW9qaXBlZGlhXShodHRwczovL2Vtb2ppcGVkaWEub3JnLykpLg0KDQpTbywgd2l0aCBSIE1hcmtkb3duLCBvbmUgY2FuIHdyaXRlIGEgfn5zdHJpa2VvdXR+fiAqKmJvbGQgc2VudGVuY2UqKiBhbmQgKml0YWxpY2l6ZSogaXQuIEFsc28sIG9uZSBjYW4gd3JpdGUgc3Vic2NyaXB0cywgbGlrZSBjaGVtaXN0cnkgZm9ybXVsYXMgKEh+Mn5PKSBvciBzdXBlcnNjcmlwdHMgKDJeMTBeIGlzIDEwMjQpIGFuZCB0aGlzIGlzIHNvbWV0aGluZyBJIHdvdWxkIGxpa2UgdG8gW3VuZGVybGluZV17LnVsfS4gSSBjYW4gYWxzbyBjaG9vc2UgdGhlIFtjb2xvcl17c3R5bGU9ImNvbG9yOiByZWQ7In0gb2Ygc29tZSB3b3Jkcywgd2l0aCBnb29kIG9sZCBwbGFpbiBIVE1MLg0KDQpFeGNlcHQgdGhlIHJlYWxseSBiYXNpYyBmb3JtYXR0aW5nIHRoYXQgaGFzIGFscmVhZHkgYmVlbiBpbnRyb2R1Y2VkIGFib3ZlIGFuZCB5b3UgY2FuIGV4cGxvcmUgYnkgZG93bmxvYWRpbmcgdGhlIGBybWRgIGZpbGUsIG9uZSBjYW4gdXNlIGFuIGFycmF5IG9mIG9wdGlvbnMgdG8gYmVhdXRpZnkgdGhlaXIgcmVwb3J0cy4gVGhlc2Ugb3B0aW9ucyBhcmUgbm90IGFsd2F5cyBhbGlnbmVkIHdpdGggUiBNYXJrZG93bidzICoqJ2Nhbm9uJyoqIHRob3VnaCBidXQgdGhleSBhcmUgY2xldmVyIGFuZCBtb3JlIGltcG9ydGFudGx5LCBkbyB0aGUgam9iLg0KDQpUaGUgJ2Nhbm9uJyBieSB0aGUgd2F5LCBmb3JtdWxhdGVkIGJ5IE1hcmtkb3duIGNyZWF0b3IsICoqSm9obiBHcnViZXIqKl5bSm9obiBHcnViZXIsIFsnRGFyaW5nIEZpcmViYWxsJ10oaHR0cHM6Ly9kYXJpbmdmaXJlYmFsbC5uZXQvcHJvamVjdHMvbWFya2Rvd24vc3ludGF4I3BoaWxvc29waHkpIGJsb2cuXSwgZ29lcyBsaWtlIHRoaXM6DQoNCj4gTWFya2Rvd24gaXMgaW50ZW5kZWQgdG8gYmUgYXMgZWFzeS10by1yZWFkIGFuZCBlYXN5LXRvLXdyaXRlIGFzIGlzIGZlYXNpYmxlLiBSZWFkYWJpbGl0eSwgaG93ZXZlciwgaXMgZW1waGFzaXplZCBhYm92ZSBhbGwgZWxzZS4gQSBNYXJrZG93bi1mb3JtYXR0ZWQgZG9jdW1lbnQgc2hvdWxkIGJlIHB1Ymxpc2hhYmxlIGFzLWlzLCBhcyBwbGFpbiB0ZXh0LCB3aXRob3V0IGxvb2tpbmcgbGlrZSBpdOKAmXMgYmVlbiBtYXJrZWQgdXAgd2l0aCB0YWdzIG9yIGZvcm1hdHRpbmcgaW5zdHJ1Y3Rpb25zLiBXaGlsZSBNYXJrZG93buKAmXMgc3ludGF4IGhhcyBiZWVuIGluZmx1ZW5jZWQgYnkgc2V2ZXJhbCBleGlzdGluZyB0ZXh0LXRvLUhUTUwgZmlsdGVycywgaW5jbHVkaW5nIFNldGV4dCwgYXR4LCBUZXh0aWxlLCByZVN0cnVjdHVyZWRUZXh0LCBHcnV0YXRleHQsIGFuZCBFdFRleHQsIHRoZSBzaW5nbGUgYmlnZ2VzdCBzb3VyY2Ugb2YgaW5zcGlyYXRpb24gZm9yIE1hcmtkb3du4oCZcyBzeW50YXggaXMgdGhlIGZvcm1hdCBvZiBwbGFpbiB0ZXh0IGVtYWlsLiBUbyB0aGlzIGVuZCwgTWFya2Rvd27igJlzIHN5bnRheCBpcyBjb21wcmlzZWQgZW50aXJlbHkgb2YgcHVuY3R1YXRpb24gY2hhcmFjdGVycywgd2hpY2ggcHVuY3R1YXRpb24gY2hhcmFjdGVycyBoYXZlIGJlZW4gY2FyZWZ1bGx5IGNob3NlbiBzbyBhcyB0byBsb29rIGxpa2Ugd2hhdCB0aGV5IG1lYW4uIEUuZy4sIGFzdGVyaXNrcyBhcm91bmQgYSB3b3JkIGFjdHVhbGx5IGxvb2sgbGlrZSAqKmVtcGhhc2lzKiouIE1hcmtkb3duIGxpc3RzIGxvb2sgbGlrZSwgd2VsbCwgbGlzdHMuIEV2ZW4gYmxvY2txdW90ZXMgbG9vayBsaWtlIHF1b3RlZCBwYXNzYWdlcyBvZiB0ZXh0LCBhc3N1bWluZyB5b3XigJl2ZSBldmVyIHVzZWQgZW1haWwuDQoNCg0KIyMgTGluZSBibG9ja3MNCg0KQSBuaWNlIHdheSB0byBwcmVzZXJ2ZSB0aGUgZGl2aXNpb24gb2YgbGluZXMgaW4gdGhlIG91dHB1dCBhcyB3ZWxsIGFzIGFueSBsZWFkaW5nIHNwYWNlcyBpcyB0aGUgbGluZSBibG9ja3Mgc3ludGF4LiBMZXQncyB3cml0ZSBzb21lIHBvZXRyeSENCg0KPiB8ICAgQXByaWwgaXMgdGhlIGNydWVsbGVzdCBtb250aCwgYnJlZWRpbmcNCj4gfCBMaWxhY3Mgb3V0IG9mIHRoZSBkZWFkIGxhbmQsIG1peGluZw0KPiB8IE1lbW9yeSBhbmQgZGVzaXJlLCBzdGlycmluZw0KPiB8IER1bGwgcm9vdHMgd2l0aCBzcHJpbmcgcmFpbi4NCj4gfCBXaW50ZXIga2VwdCB1cyB3YXJtLCBjb3ZlcmluZw0KPiB8IEVhcnRoIGluIGZvcmdldGZ1bCBzbm93LCBmZWVkaW5nDQo+IHwgQSBsaXR0bGUgbGlmZSB3aXRoIGRyaWVkIHR1YmVycy4gKC4uLikNCj4gfCANCj4gfCAqZXhjZXJwdCogZnJvbSAqKlRoZSBXYXN0ZSBMYW5kKiogYnkgVC4gUy4gRWxpb3QuDQoNCkFsdGhvdWdoIFBhbmRvYyBkb2N1bWVudGF0aW9uIHNheXMgdGhhdCAqSW5saW5lIGZvcm1hdHRpbmcgKHN1Y2ggYXMgZW1waGFzaXMpIGlzIGFsbG93ZWQgaW4gdGhlIGNvbnRlbnQsIGJ1dCBub3QgYmxvY2stbGV2ZWwgZm9ybWF0dGluZyAoc3VjaCBhcyBibG9jayBxdW90ZXMgb3IgbGlzdHMpKiwgSSBhcHBhcmVudGx5IHVzZWQgYmxvY2sgcXVvdGVzIHN5bnRheCBhbmQgaXQgd29ya2VkLi4uDQoNCiMjIExpc3RzDQoNCldoYXQgZm9sbG93cyBpcyBhIGNvbGxlY3Rpb24gb2Ygc29tZSBsaXN0IHR5cGVzLg0KDQojIyMgQnVsbGV0IGxpc3RzDQoNClVzZSBlaXRoZXIgYCpgIG9yIGArYCBvciBgLWAuDQoNCi0gICBvbmUgc3ludGF4IGVycm9yDQotICAgdHdvIHN5bnRheCBlcnJvcnMNCi0gICB0aHJlZSBzeW50YXggZXJyb3JzDQoNCiMjIyBOZXN0ZWQgbGlzdHMNCg0KLSAgIG9iamVjdHMNCg0KICAgIC0gICBjaGFpcg0KICAgIC0gICB0YWJsZQ0KICAgIC0gICBzcG9vbg0KDQotICAgY29sb3JzDQoNCiAgICAtICAgYmx1ZQ0KICAgIC0gICByZWQNCiAgICAtICAgd2hpdGUNCg0KIyMjIFRhc2sgbGlzdHMNCg0KLSAgIFsgXSBhbiB1bmNoZWNrZWQgdGFzayBsaXN0IGl0ZW0NCi0gICBbeF0gY2hlY2tlZCBpdGVtDQoNCiMjIyBEZWZpbml0aW9uIGxpc3RzDQpJbiBhIGRhdGEgYW5hbHl0aWNzIHByb2plY3QgdGhlIHVzZSBvZiBkZWZpbml0aW9uIGxpc3RzIHdvdWxkIGJlIHF1aXRlIHVzZWZ1bCBmb3Igc3Rha2Vob2xkZXJzIG9yL2FuZCB0ZWFtIG1lbWJlcnMgYW5kIHRoaXMgaXMgYSBoYW5keSB3YXkgb2YgbWFraW5nIGl0IGhhcHBlbjoNCg0KQWdncmVnYXRpb24NCg0KOiAgIFRoZSBwcm9jZXNzIG9mIGNvbGxlY3Rpbmcgb3IgZ2F0aGVyaW5nIG1hbnkgc2VwYXJhdGUgcGllY2VzIGludG8gYSB3aG9sZS4NCg0KQW5hbHl0aWNhbCBza2lsbHMgKmluIHByb2dyYW1taW5nKg0KDQo6ICAgUXVhbGl0aWVzIGFuZCBjaGFyYWN0ZXJpc3RpY3MgYXMgd2VsbCBhcyBjb21wdXRhdGlvbmFsIHRvb2xzIGFzc29jaWF0ZWQgd2l0aCB1c2luZyBmYWN0cyB0byBzb2x2ZSBwcm9ibGVtcy4gRS5nLiwNCg0KICAgICAgICBSIG1hcmtkb3duLCBSIHBhY2thZ2VzLCBldGMuICh0aGlzIGJveCB3YXMgY3JlYXRlZCB3aXRoIGp1c3QgNCB0YWJzKQ0KICAgICAgICANCkFyZWEgY2hhcnQNCg0KOiAgIEEgZGF0YSB2aXN1YWxpemF0aW9uIHRoYXQgdXNlcyBpbmRpdmlkdWFsIGRhdGEgcG9pbnRzIGZvciBhIGNoYW5naW5nIHZhcmlhYmxlIGNvbm5lY3RlZCBieSBhIGNvbnRpbnVvdXMgbGluZSB3aXRoIGEgZmllbGQgaW4gYXJlYSB1bmRlcm5lYXRoLg0KDQojIyMgTnVtYmVyZWQgZXhhbXBsZSBsaXN0cw0KDQpUaGVyZSBhcmUgY2FzZXMgd2hlcmUgYSBjb25jaXNlIHdheSBvZiByZWZlcnJpbmcgdG8geW91ciBleGFtcGxlcyBpcyBuZWNlc3NhcnkuIFRvIGFjaGlldmUgdGhpcywgeW91IGNhbiB1c2UgdGhlIGBAYCBzcGVjaWFsIG1hcmtlciBiZWZvcmUgdGhlIGV4YW1wbGUgc2VudGVuY2UsIGZvbGxvd2VkIGJ5IGEgYHNwYWNlYC4gVGhlIG51bWJlcmluZyB3aWxsIGNvbnRpbnVlICphdXRvbWFnaWNhbGx5KiB0aHJvdWdob3V0IHRoZSBkb2N1bWVudC4NCg0KKEApIEZvciBleGFtcGxlLCAqdGhpcyB3aWxsIGJlIG15IGZpcnN0IGV4YW1wbGUqLg0KDQpBZnRlciB0aGF0IEkgd2lsbCBjb250aW51ZSBteSByZXBvcnQgYnV0IGFsbCBvZiBhIHN1ZGRlbiB0aGUgbmVlZCBvZiBwcm92aWRpbmcgbW9yZSBleGFtcGxlcyBpcyBib3JuLiBXaGF0IGlzIGl0IGdvaW5nIHRvIGhhcHBlbiBpZiBJIHVzZSB0aGUgc2FtZSBzeW50YXgsIGAoQClgLCBvbmNlIGFnYWluPw0KDQooQCkgVGhpcyBpcyBteSBzZWNvbmQgZXhhbXBsZSBhbmQgYXBwYXJlbnRseSB0aGUgbnVtYmVyaW5nIGNvbnRpbnVlcy4NCg0KPCEtLSBlbmQgb2YgbGlzdCAtLT4NCg0KRmluYWxseSwgdG8gJ2N1dCBvZmYnIGxpc3RzLCB5b3UgY2FuIGluc2VydCBzb21lIG5vbi1pbmRlbnRlZCBjb250ZW50IGxpa2UgYW4gSFRNTCBjb21tZW50LCB3aGljaCB3b24ndCBwcm9kdWNlIHZpc2libGUgb3V0cHV0IGluIGFueSBmb3JtYXQsIGxpa2U6IGA8IS0tIGVuZCBvZiBsaXN0IC0tPmAgb3Igc2ltaWxhci4gQnkgJ2JyZWFraW5nJyB0aGUgbGlzdCwgbm9ybWFsIGZvcm1hdHRpbmcgY2FuIGdvIG9uLg0KDQoNCiMjIEJveGVzIGFuZCBjaHVua3MNCg0KIyMjIEEgc2ltcGxlIGJveCANCkEgbmljZSBgZGl2YCB0byB3cmFwLXVwIGNvbmNsdXNpb25zOg0KYGBgez1odG1sfQ0KPHN0eWxlPg0KZGl2LmJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiNlNmYwZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDt9DQo8L3N0eWxlPg0KYGBgDQo8ZGl2IGNsYXNzID0gImJsdWUiPg0KDQotIFRoaXMgaXMgbXkgZmlyc3QgY29uY2x1c2lvbg0KLSBUaGlzIGlzIG15IHNlY29uZCBjb25jbHVzaW9uDQoNCjwvZGl2Pg0KDQojIyMgU2Nyb2xsYWJsZSBjaHVua3MNCg0KU29tZSBjb2RlIGZvciBmaXhpbmcgY2h1bmsncyBtYXhpbXVtIGhlaWdodDoNCg0KPCEtLSBDb21tZW50aW5nIChhbmQgcHJvamVjdGluZykgdGhlIGNvZGUgLS0+DQpgYGBgDQpgYGB7Y3NzLCBlY2hvPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30NCnByZSB7DQogIG1heC1oZWlnaHQ6IDMwMHB4Ow0KICBvdmVyZmxvdy15OiBhdXRvOw0KfQ0KDQpwcmVbY2xhc3NdIHsNCiAgbWF4LWhlaWdodDogMjAwcHg7DQp9DQpgYGANCmBgYGANCg0KPCEtLSBSdW5uaW5nIHRoZSBjb2RlIC0tPg0KYGBge2NzcywgZWNobz1GQUxTRSwgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9DQpwcmUgew0KICBtYXgtaGVpZ2h0OiAzMDBweDsNCiAgb3ZlcmZsb3cteTogYXV0bzsNCn0NCg0KcHJlW2NsYXNzXSB7DQogIG1heC1oZWlnaHQ6IDIwMHB4Ow0KfQ0KYGBgDQoNCkFib3ZlIHdlIGRlZmluZWQgc29tZSBDU1MgcnVsZXMgdG8gbGltaXQgdGhlIGhlaWdodCBvZiBjb2RlIGJsb2Nrcy4gTm93IHdlIGNhbiB0ZXN0IGlmIHRoZXNlIHJ1bGVzIHdvcmsgb24gY29kZSBibG9ja3MgYW5kIHRleHQgb3V0cHV0IGJ5IGxvb2tpbmcgZm9yIHRoZSBwcmVzZW5jZSBvZiAqKmEgdmVydGljYWwgc2Nyb2xsIGJhcioqOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30NCiMgcHJldGVuZCB0aGF0IHdlIGhhdmUgYSBsb3Qgb2YgY29kZSBpbiB0aGlzIGNodW5rDQppZiAoMSArIDEgPT0gMikgew0KICAjIG9mIGNvdXJzZSB0aGF0IGlzIHRydWUNCiAgcHJpbnQobXRjYXJzKQ0KICAjIHdlIGp1c3QgcHJpbnRlZCBhIGxlbmd0aHkgZGF0YSBzZXQNCn0NCmBgYA0KDQpOZXh0IHdlIGFkZCBydWxlcyBmb3IgYSBuZXcgY2xhc3MsIGBzY3JvbGwtMTAwYCwgdG8gbGltaXQgdGhlIGhlaWdodCB0byAxMDBweCwgYW5kIGFkZCB0aGUgY2xhc3MgdG8gdGhlIG91dHB1dCBvZiBhIGNvZGUgY2h1bmsgdmlhIHRoZSBjaHVuayBvcHRpb24gYGNsYXNzLm91dHB1dGAuIFRoaXMgd2F5LCB3ZSBjYW4gbWFudWFsbHkgZGVmaW5lIGVhY2ggY2h1bmsncyBoZWlnaHQ6DQoNCjwhLS0gQ29tbWVudGluZyBhbmQgcHJvamVjdGluZyB0aGUgY29kZSAtLT4NCmBgYHtjc3MsIGVjaG89RkFMU0UsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQ0KLnNjcm9sbC0xMDAgew0KICBtYXgtaGVpZ2h0OiAxMDBweDsNCiAgb3ZlcmZsb3cteTogYXV0bzsNCiAgYmFja2dyb3VuZC1jb2xvcjogaW5oZXJpdDsNCn0NCg0KDQo8IS0tIFJ1bm5pbmcgdGhlIGNvZGUgLS0+DQpgYGB7Y3NzLCBlY2hvPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30NCi5zY3JvbGwtMTAwIHsNCiAgbWF4LWhlaWdodDogMTAwcHg7DQogIG92ZXJmbG93LXk6IGF1dG87DQogIGJhY2tncm91bmQtY29sb3I6IGluaGVyaXQ7DQp9DQpgYGANCg0KYGBge3IsIGNsYXNzLm91dHB1dD0ic2Nyb2xsLTEwMCIsIGNsYXNzLnNvdXJjZSA9ICdmb2xkLXNob3cnfQ0KcHJpbnQobXRjYXJzKQ0KYGBgDQoNCiMgVmlzdWFsaXphdGlvbnMNCg0KIyMgSW50ZXJhY3RpdmUgZ3JhcGhpY3MNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoZ2FwbWluZGVyKQ0KIA0KcCA8LSBnYXBtaW5kZXIgJT4lDQogIGZpbHRlcih5ZWFyPT0xOTc3KSAlPiUNCiAgZ2dwbG90KCBhZXMoZ2RwUGVyY2FwLCBsaWZlRXhwLCBzaXplID0gcG9wLCBjb2xvcj1jb250aW5lbnQpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3hfbG9nMTAoKSArDQogIHRoZW1lX2J3KCkNCiANCmdncGxvdGx5KHApDQpgYGANCg0KIyMgVGFibGVzICYgRmlndXJlcw0KDQojIyMgU2ltcGxlIHRhYmxlcw0KDQpUaGUgbW9zdCBzaW1wbGUgdGFibGUgaXMgdGhlIG9uZSB0aGF0IGlzIGZvcm1hdHRlZCB3aXRoIHBsYWluIG1hcmtkb3duIHN5bnRheDoNCg0KU28sIGEgc3ludGF4IGxpa2UgdGhpczoNCg0KICAgICAgICB8ICB8IDIwMjAgfCAyMDIxIHwgYm90aCB8DQogICAgICAgIHwtLS18LS0tfC0tLXwtLS18DQogICAgICAgIHwgZmlsZXMgfCAxNjAgfCA4MCB8IDEgfA0KICAgICAgICB8IGZvbGRlcnMgfCA1MyB8IDIzIHwgMCB8DQoNCmVuZHMgdXAgbGlrZSB0aGlzOg0KDQp8ICB8IDIwMjAgfCAyMDIxIHwgYm90aCB8DQp8LS0tfC0tLXwtLS18LS0tfA0KfCBmaWxlcyB8IDE2MCB8IDgwIHwgMSB8DQp8IGZvbGRlcnMgfCA1MyB8IDIzIHwgMCB8DQoNCkZvciBhIGNvbnZlbmllbnQgdGFibGUgZ2VuZXJhdG9yLCBbdGFrZSBhIGxvb2sgaGVyZV0oaHR0cHM6Ly93d3cudGFibGVzZ2VuZXJhdG9yLmNvbS9tYXJrZG93bl90YWJsZXMpLg0KDQoNCiMjIyMgQSBzaW1wbGUgYGthYmxlYCB0YWJsZQ0KDQpgYGB7ciBkZW1vLXRhYmxlfQ0KcmVxdWlyZShrbml0cikNCnJlcXVpcmUoa2FibGVFeHRyYSkNCm10Y2FycyAlPiUNCiAgaGVhZCgpICU+JQ0KICBrYWJsZShkaWdpdHMgPSAxLCBjYXB0aW9uID0gJ2V4YW1wbGUgb2Yga2FibGUgdGFibGUnKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gJ2xlZnQnKSAlPiUNCiAgcm93X3NwZWMoMCwNCiAgICAgICAgICAgYm9sZCA9IFQsDQogICAgICAgICAgIGNvbG9yID0gJ3doaXRlJywNCiAgICAgICAgICAgYmFja2dyb3VuZCA9ICdibGFjaycpDQpgYGANCg0KIyMjIyBBIGxlc3Mgc2ltcGxlIGBrYWJsZWAgdGFibGUNCg0KQSB0YWJsZSB3aXRoIGN1c3RvbSBjb2x1bW4gbmFtZXMsIGN1c3RvbSBhbGlnbm1lbnQgYW5kIGEgY2FwdGlvbjoNCmBgYHtyfQ0KaXJpczIgPC0gaGVhZChpcmlzKQ0Ka25pdHI6OmthYmxlKGlyaXMyLCBjb2wubmFtZXMgPSBjKCdXZScsICdOZWVkJywgJ0ZpdmUnLCAnTmFtZXMnLCAnSGVyZScpLCBhbGlnbiA9ICJsY2NyciIsIGNhcHRpb24gPSAiQW4gZXhhbXBsZSB0YWJsZSBjYXB0aW9uLiIpDQoNCmBgYA0KDQoNCiMjIyMgQW4gZXhhbXBsZSBvZiBga2FibGVFeHRyYWAgdGFibGUNCg0KU21hbGxlciBmb250cywgYGthYmxlRXh0cmFgIHBhY2thZ2UgcmVxdWlyZWQsIFtkb2N1bWVudGF0aW9uIGhlcmVdKCNodHRwczovL2hhb3podTIzMy5naXRodWIuaW8va2FibGVFeHRyYS9hd2Vzb21lX3RhYmxlX2luX2h0bWwuaHRtbCk6DQoNCmBgYHtyfQ0Ka2FibGUoaGVhZChpcmlzLCA1KSwgYm9va3RhYnMgPSBUUlVFKSAlPiUNCiAga2FibGVfc3R5bGluZyhmb250X3NpemUgPSA4KQ0KYGBgDQoNClRoZSBmb2xsb3dpbmcgZXhhbXBsZSB0YWtlcyBhIHBvcnRpb24gb2YgYSBkYXRhc2V0IGFuZCB0dXJucyBpdCBpbnRvIGEgdGFibGUgdmlhIGBrYWJsZUV4dHJhYCBwYWNrYWdlOg0KDQpgYGB7cn0NCmRhdGEgPC0gZmFpdGhmdWxbMTo0LCBdDQprbml0cjo6a2FibGUoZGF0YSwNCiBjYXB0aW9uID0gIlRhYmxlIHdpdGgga2FibGUiKQ0KYGBgDQoNClRoaXMgb25lIGZpbHRlcnMgdGhlIGNvbnRlbnQgb2YgdGhlIHRhYmxlOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30NCnN1bW1hcnkoY2FycyRkaXN0KQ0Kc3VtbWFyeShjYXJzJHNwZWVkKQ0KYGBgDQoNCg0KIyMjIyBBIHJvdy9jb2x1bW4gYGthYmxlRXh0cmFgIHRhYmxlIGZvcm1hdHRpbmcNCg0KVmFyaW91cyBmb3JtYXR0aW5nIG9wdGlvbnM6DQoNCmBgYHtyfQ0Ka2FibGUoaGVhZChpcmlzLCA1KSwgYWxpZ24gPSAnYycsIGJvb2t0YWJzID0gVFJVRSkgJT4lDQogIHJvd19zcGVjKDEsIGJvbGQgPSBUUlVFLCBpdGFsaWMgPSBUUlVFKSAlPiUgDQogIHJvd19zcGVjKDI6MywgY29sb3IgPSAnd2hpdGUnLCBiYWNrZ3JvdW5kID0gJ2JsYWNrJykgJT4lDQogIHJvd19zcGVjKDQsIHVuZGVybGluZSA9IFRSVUUsIG1vbm9zcGFjZSA9IFRSVUUpICU+JSANCiAgcm93X3NwZWMoNSwgYW5nbGUgPSA0NSkgJT4lIA0KICBjb2x1bW5fc3BlYyg1LCBzdHJpa2VvdXQgPSBUUlVFKQ0KYGBgDQoNCiMjIyMgQSBga2FibGVFeHRyYWAgY29sdW1uIGdyb3VwaW5nDQoNCmZvciByb3cgZ3JvdXBpbmcsIFtoZXJlXSgjaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLWNvb2tib29rL2thYmxlZXh0cmEuaHRtbCk6DQoNCmBgYHtyfQ0KaXJpczIgPC0gaXJpc1sxOjUsIGMoMSwgMywgMiwgNCwgNSldDQpuYW1lcyhpcmlzMikgPC0gZ3N1YignWy5dLisnLCAnJywgbmFtZXMoaXJpczIpKQ0Ka2FibGUoaXJpczIsIGJvb2t0YWJzID0gVFJVRSkgJT4lDQogIGFkZF9oZWFkZXJfYWJvdmUoYygiTGVuZ3RoIiA9IDIsICJXaWR0aCIgPSAyLCAiICIgPSAxKSkgJT4lIA0KICBhZGRfaGVhZGVyX2Fib3ZlKGMoIk1lYXN1cmVtZW50cyIgPSA0LCAiTW9yZSBhdHRyaWJ1dGVzIiA9IDEpKQ0KYGBgDQoNCiMjIyMgTW9yZSBga2FibGVFeHRyYWAgb3B0aW9ucw0KDQpFeGFtcGxlcyB0YWtlbiBkaXJlY3RseSBmcm9tIHRoZSBbYXV0aG9yJ3Mgd2Vic2l0ZV0oI2h0dHBzOi8vaGFvemh1MjMzLmdpdGh1Yi5pby9rYWJsZUV4dHJhL2F3ZXNvbWVfdGFibGVfaW5faHRtbC5odG1sKToNCg0KYGBge3IgYWR2YW5jZWQtdGFibGVzfQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KZHQgPC0gbXRjYXJzWzE6NSwgMTo2XQ0KZHQgJT4lDQogIGtibChjYXB0aW9uID0gIlJlY3JlYXRpbmcgYm9va3RhYnMgc3R5bGUgdGFibGUiKSAlPiUNCiAga2FibGVfY2xhc3NpY18yKGJvb3RzdHJhcF9vcHRpb25zID0gInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgZnVsbF93aWR0aD1GLCBwb3NpdGlvbj0iZmxvYXRfcmlnaHQiLCBodG1sX2ZvbnQgPSAiQ2FtYnJpYSIsIGZvbnRfc2l6ZT0xNikgJT4lIA0KICBjb2x1bW5fc3BlYyg1OjcsIGJvbGQgPSBUKSAlPiUNCiAgcm93X3NwZWMoMzo1LCBib2xkID0gVCwgY29sb3IgPSAid2hpdGUiLCBiYWNrZ3JvdW5kID0gIiNENzI2MUUiKSAgJT4lDQogIGFkZF9oZWFkZXJfYWJvdmUoYygiICIsICJHcm91cCAxIiA9IDIsICJHcm91cCAyIiA9IDIsICJHcm91cCAzIiA9IDIpKSAlPiUNCiAgYWRkX2hlYWRlcl9hYm92ZShjKCIgIiwgIkdyb3VwIDQiID0gNCwgIkdyb3VwIDUiID0gMikpDQogIA0KYGBgDQoNCiMjIyBUYWJsZSB3aXRoIGZpbHRlcmluZyBzbGlkZXINClRoaXMgaXMgYSB2ZXJ5IGZhbmN5IGBEVGAgcGFja2FnZSB0YWJsZSB3aXRoIGEgc2xpZGVyIGZvciBmaWx0ZXJpbmchDQoNCmBgYHtyfQ0KbGlicmFyeShEVCkNCmRhdGF0YWJsZShtdGNhcnMsIHJvd25hbWVzID0gRkFMU0UsIGZpbHRlcj0idG9wIiwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDUsIHNjcm9sbFg9VCkgKQ0KYGBgDQojIyMgVGFiYmVkL3BpbGxzZXQgbmF2aWdhdGlvbg0KWW91IGNhbiB0dXJuIHBhcmFsbGVsIHNlY3Rpb25zIHRvIHRhYnMgb3IgJ3BpbGxzZXRzJzoNCg0KIyMjIyBSZXN1bHRzIHBsb3Qgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KIyMjIyMgUGxvdHMNCg0KV2Ugc2hvdyBhIHNjYXR0ZXIgcGxvdCBpbiB0aGlzIHNlY3Rpb24uDQoNCmBgYHtyLCBmaWcuZGltPWMoNSwgMyl9DQpwYXIobWFyID0gYyg0LCA0LCAuNSwgLjEpKQ0KcGxvdChtcGcgfiBocCwgZGF0YSA9IG10Y2FycywgcGNoID0gMTkpDQpgYGANCg0KIyMjIyMgVGFibGVzDQoNCldlIHNob3cgdGhlIGRhdGEgaW4gdGhpcyB0YWIuDQoNCmBgYHtyfQ0KaGVhZChtdGNhcnMpDQpgYGANCg0KIyMgey0gLnVubGlzdGVkfQ0KDQpXaXRoIGFuIHVubnVtYmVyZWQsIHVubGlzdGVkIGFuZCBlbXB0eSBzZWN0aW9uIGhlYWRlciwgYCMjIHstIC51bmxpc3RlZH1gLCB3ZSBjYW4gZW5kLydjdXQgb2ZmJyB0aGUgdGFic2V0L3BpbGxzZXQgYWJvdmUgYW5kIGNvbnRpbnVlIHRvIHdyaXRlIG1vcmUgcGFyYWdyYXBocy4gVGhpcyBpcyB0aGUgb25seSB3YXkgdG8gZXNjYXBlIHRoaXMgb2JqZWN0Lg0KDQpBbmQgbm93IHRoYXQgKipUYWJsZXMqKiBzZWN0aW9uIGlzIG92ZXIsIEknZCBsaWtlIHRvIHNob3cgeW91IGhvdyB0byByZWZlciB0byBhbnkgdGFibGUgb3IgZmlndXJlIChvciBldmVuIGVxdWF0aW9uLCBhbHRob3VnaCBJIGRvbid0IGRlYWwgd2l0aCB0aG9zZSB5ZXQpIGluc2lkZSB5b3VyIGRvY3VtZW50LiBUaGUgc3RlcHMgYXJlOg0KDQoxLiBJbiB5b3VyIFlBTUwsIGluY2x1ZGUgYHVzZV9ib29rZG93biA9IFRSVUVgIGJlbmVhdGggeW91ciBgb3V0cHV0OmAgZWxlbWVudC4gQmV3YXJlLCB1c2UgcHJvcGVyIGluZGVudGF0aW9uIG9yIGVsc2UgdGhpcyB3b24ndCB3b3JrLg0KMi4gVGhlIGdvIGludG8geW91ciB0YWJsZS9maWd1cmUvZXF1YXRpb24gY29kZSBjaHVuayBhbmQgKipsYWJlbCoqIGl0LCBpLmUuIA0KDQoNCmB7ciBteS1sYWJlbGVkLWNodW5rfWANCg0KMy4gVGhlbiB3cml0ZSB0aGUgcmVmZXJlbmNlLCBpLmUuLCBQbGVhc2Ugc2VlIFRhYmxlIGBcQHJlZih0YWI6bXktbGFiZWxlZC1jaHVuaylgDQoNCkxpdmUgZXhhbXBsZTogUmVmZXIgdG8gVGFibGUgXEByZWYodGFiOmRlbW8tdGFibGUpIGFib3ZlLg0KDQpBbGwgc2V0Lg0KDQojIyMgTW9yZSBvbiBmb3JtYXR0aW5nIG9mIHRhYmxlcyBhbmQgZmlndXJlcw0KDQpNb3Zpbmcgb24sIGluIHRoaXMgZmlndXJlLCB3ZSBwbGF5IHdpdGggb3V0LndpZHRoLCBhbGlnbm1lbnQgYW5kIGNhcHRpb25zIChjaGVjayBvcmlnaW5hbCBybWQhKToNCg0KDQogICAgICAgIHtyLCBvdXQud2lkdGggPSAnMzAlJywgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuY2FwPSdBIGJlYXV0aWZ1bCBwbG90IGZyb20gYSBuZXdiaWUgUiBjb2RlciEnfQ0KDQoNCmBgYHtyLCBvdXQud2lkdGggPSAnMzAlJywgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuY2FwPSdBIGJlYXV0aWZ1bCBwbG90IGZyb20gYSBuZXdiaWUgUiBjb2RlciEnfQ0KcGxvdChjYXJzLCBwY2ggPSAxOCkNCmBgYA0KDQpBbmQgYSB0ZXN0IGluIGZpZ3VyZSdzIGRpbWVuc2lvbnMgKHdpZHRoIGFuZCBoZWlnaHQpOg0KDQogICAgICAgIHtyLCBmaWcuZGltPWMoNSwzLjIpfQ0KDQpgYGB7ciwgZmlnLmRpbT1jKDUsMy4yKX0NCnBsb3QoY2FycywgcGNoID0gMTYpDQpgYGANCg0KTm93LCB3ZSdsbCBwbGFjZSBtdWx0aXBsZSBmaWd1cmVzIHNpZGUtYnktc2lkZSBmcm9tIHRoZSBzYW1lIGNvZGUgY2h1bms6DQoNCiAgICAgICAge3IsIGZpZy5zaG93PSdob2xkJywgb3V0LndpZHRoPSc1MCUnfQ0KDQpgYGB7ciwgZmlnLnNob3c9J2hvbGQnLCBvdXQud2lkdGg9JzUwJSd9DQpwYXIobWFyID0gYyg0LCA0LCAuMiwgLjEpKQ0KcGxvdChjYXJzLCBwY2ggPSAxOSkNCnBsb3QocHJlc3N1cmUsIHBjaCA9IDE3KQ0KYGBgDQoNCkFuZCBub3csIHRhYmxlcyAodGhlIHRhYmxlIGNhbiBicmVhayBhY3Jvc3MgcGFnZXMpOg0KDQpgYGB7ciB0YWJsZXMtbXRjYXJzfQ0Ka25pdHI6OmthYmxlKGlyaXNbMToxNSwgXSwgY2FwdGlvbiA9ICdBIGNhcHRpb24nKQ0KYGBgDQpUaGUgZm9sbG93aW5nIHBhZ2luYXRpb24gaXMgZHVlIHRvIFlBTUwgZWxlbWVudCwgYGRmX3ByaW50OnBhZ2VkYA0KDQogICAgICAgIHtyIGNvbHMucHJpbnQ9Mywgcm93cy5wcmludD0zfQ0KDQpgYGB7ciBjb2xzLnByaW50PTMsIHJvd3MucHJpbnQ9M30NCm10Y2Fycw0KYGBgDQojIyBTZXZlcmFsIGNvbHVtbnMNCjo6OiB7LnJvd30NCjo6OiB7LmNvbC1tZC00fQ0KPGJyPjxicj5TaW5jZSBSIE1hcmtkb3duIHVzZSB0aGUgW2Jvb3RzdHJhcCBmcmFtZXdvcmtdKGh0dHBzOi8vZ2V0Ym9vdHN0cmFwLmNvbS9kb2NzLzQuMC9sYXlvdXQvZ3JpZC8pIHVuZGVyIHRoZSBob29kISBJdCBpcyBwb3NzaWJsZSB0byBiZW5lZml0IGl0cyBwb3dlcmZ1bCBncmlkIHN5c3RlbS4gQmFzaWNhbGx5LCB5b3UgY2FuIGNvbnNpZGVyIHRoYXQgeW91ciByb3cgaXMgZGl2aWRlZCBpbiAxMiBzdWJ1bml0cyBvZiBzYW1lIHdpZHRoLiBZb3UgY2FuIHRoZW4gY2hvb3NlIHRvIHVzZSBvbmx5IGEgZmV3IG9mIHRoaXMgc3VidW5pdHMuDQo6OjoNCg0KOjo6IHsuY29sLW1kLTR9DQo8YnI+PGJyPkhlcmUsIEkgdXNlIDMgc3VidW5pdHMgb2Ygc2l6ZSA0ICg0eDM9MTIpLiBUaGUgbGFzdCBjb2x1bW4gaXMgdXNlZCBmb3IgYSBwbG90LiBZb3UgY2FuIHJlYWQgbW9yZSBhYm91dCB0aGUgZ3JpZCBzeXN0ZW0gW2hlcmVdKGJvb3RzdHJhcCUyMGdyaWQlMjBzeXN0ZW0pLiBJIGdvdCB0aGlzIHJlc3VsdCBzaG93aW5nIHRoZSBmb2xsb3dpbmcgY29kZSBpbiBteSBSIE1hcmtkb3duIGRvY3VtZW50Lg0KOjo6DQoNCjo6OiB7LmNvbC1tZC00fQ0KPGJyPjxicj4NCg0KYGBge3J9DQojIGFubnVhbCBmbG93IG9uIE5pbGUgUml2ZXINCk5pbGUgJT4lIGFzLmRhdGEuZnJhbWUoKSAlPiUgbXV0YXRlKHllYXI9MTg3MToxOTcwKSAlPiUgDQogIHJlbmFtZShmbG93PXgpICU+JSANCiAgZ2dwbG90KC4pKyBnZW9tX2xpbmUoYWVzKHg9eWVhciwgeT1mbG93KSwgDQogICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJkYXJrYmx1ZSIsIGx3ZD0yKSArDQogIHRoZW1lX21pbmltYWwoKSArIGxhYnMoeD0iIiwgeT0iRmxvdyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPSJBbm51YWwgcml2ZXIgZmxvdyBvbiBOaWxlIFJpdmVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZT0iKDE4NzEtMTk3MCkiKQ0KYGBgDQo6OjoNCjo6Og0KDQoNCiMjIERpYWdyYW1zDQoNCkEgZGlhZ3JhbSBiYXNlZCBvbiBgRGlhZ3JhbW1lUmAgcGFja2FnZToNCg0KYGBge3IgY2xhc3Muc291cmNlPSJiZy1kYW5nZXIiLCBjbGFzcy5vdXRwdXQ9ImJnLXdhcm5pbmcifQ0KRGlhZ3JhbW1lUjo6Z3JWaXooImRpZ3JhcGggew0KICBncmFwaCBbbGF5b3V0ID0gZG90LCByYW5rZGlyID0gVEJdDQogIA0KICBub2RlIFtzaGFwZSA9IHJlY3RhbmdsZV0gICAgICAgIA0KICByZWMxIFtsYWJlbCA9ICdTdGVwIDEuIFdha2UgdXAnXQ0KICByZWMyIFtsYWJlbCA9ICdTdGVwIDIuIFdyaXRlIGNvZGUnXQ0KICByZWMzIFtsYWJlbCA9ICAnU3RlcCAzLiA/Pz8nXQ0KICByZWM0IFtsYWJlbCA9ICdTdGVwIDQuIFBST0ZJVCddDQogIA0KICAjIGVkZ2UgZGVmaW5pdGlvbnMgd2l0aCB0aGUgbm9kZSBJRHMNCiAgcmVjMSAtPiByZWMyIC0+IHJlYzMgLT4gcmVjNA0KICB9IiwNCiAgaGVpZ2h0ID0gNTAwKSAgDQpgYGANCg0KSW4gdGhlIGNvZGUgY2h1bmsgYWJvdmUsIGEgc3BlY2lhbCBiYWNrZ3JvdW5kIGZvcm1hdHRpbmcgaXMgdXNlZCB0byBjaGFuZ2UgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIGVsZW1lbnQuIFRoZSBzYW1lIGdvZXMgZm9yIHRoaXMgbmV4dCBleGFtcGxlIGFzIHdlbGwsIHdoaWNoIGlzIG9uZSBtb3JlIGRpYWdyYW0sIHRoaXMgdGltZSB3aXRoIGFkZGVkIHBhcmFtZXRlcnM6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZT0iYmctaW5mbyJ9DQojY2FuIGFsc28gdXNlIGJnLXN1Y2Nlc3MgZm9yIGdyZWVuDQoNCkRpYWdyYW1tZVI6OmdyVml6KCINCiAgZGlncmFwaCBncmFwaDIgew0KICANCiAgZ3JhcGggW2xheW91dCA9IGRvdCwgcmFua2RpciA9IExSXQ0KICANCiAgIyBub2RlIGRlZmluaXRpb25zIHdpdGggc3Vic3RpdHV0ZWQgbGFiZWwgdGV4dA0KICBub2RlIFtzaGFwZSA9IG92YWxdDQogIGEgW2xhYmVsID0gJ0BAMSddDQogIGIgW2xhYmVsID0gJ0BAMiddDQogIGMgW2xhYmVsID0gJ0BAMyddDQogIGQgW2xhYmVsID0gJ0BANCddDQogIA0KICBhIC0+IGIgLT4gYyAtPiBkDQogIH0NCiAgDQogIFsxXTogbmFtZXMoaXJpcylbMV0NCiAgWzJdOiBuYW1lcyhpcmlzKVsyXQ0KICBbM106IG5hbWVzKGlyaXMpWzNdDQogIFs0XTogbmFtZXMoaXJpcylbNF0NCiAgIiwNCiAgaGVpZ2h0ID0gMTAwKQ0KICANCmBgYA0KDQpDaHVuayBiYWNrZ3JvdW5kIGZvcm1hdHRpbmcgY291bGQgYmUgdXNlZnVsIHdoZW4gcHJlc2VudGluZyBnb29kL2JhZCBhcHByb2FjaGVzIGluIHNvbHZpbmcgYSBwcm9ibGVtIG9yIHdoZW4geW91IHdhbnQgdG8gc2hvd2Nhc2Ugd29yc3Qvb3B0aW1hbCBjb2RpbmcgY2hvaWNlcy4NCg0KIyBJbWFnZXMNCg0KIVthbiBpbWFnZSBvZiBteXNlbGZdKGltYWdlcy9teXNlbGYuanBnKXt3aWR0aD0iMTI4LCIgaGVpZ2h0PSIxMjgifQ0KDQpBbmQgYmVsb3cgYW5vdGhlciBtZXRob2QgKGEgYmV0dGVyIG1ldGhvZCkgb2YgaW5zZXJ0aW5nIGltYWdlcyB0aGF0IGFsbG93cyBhbGlnbm1lbnQgYW5kIHNpemUgaGFuZGxpbmcsIGJ5IHVzaW5nIHRoZSBga25pdHJgIHBhY2thZ2U6DQoNCiAgICAgICAge3IsIGVjaG89VFJVRSwgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9JzMwJSd9DQoNCmBgYHtyLCBlY2hvPVRSVUUsIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSczMCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImltYWdlcy9jaGVzcy5qcGciKQ0KYGBgDQoNCjwhLS0gcG9zc2libGUgdXNlIG9mIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfSBiZWxvdywgYnJlYWtzIFRhYmxlIG9mIENvbnRlbnQgLS0+DQoNCg0KIyBNYXBzDQoNCmBgYHtyfQ0KYnVlbGx0b24gPC0gc3Rfc2ZjKHN0X3BvaW50KGMoLTEyMC4xOTI3LCAzNC42MTM2KSksIGNycz00MzI2KQ0KbTEgPC0gbWFwdmlldyhidWVsbHRvbiwgY29sLnJlZ2lvbnM9Im9yYW5nZSIpICMgbWFrZSB0aGUgbWFwDQptMUBtYXAgJT4lIGxlYWZsZXQ6OmFkZE1lYXN1cmUocHJpbWFyeUxlbmd0aFVuaXQgPSAibWV0ZXJzIikNCmBgYA0KDQoNCiMgVXNlIG9mIG90aGVyIGxhbmd1YWdlcw0KDQojIyBQeXRob24NCg0KWWVzLCB5b3UgY2FuIHJ1biBQeXRob24gaW4gUlN0dWRpbyEgWyhtb3JlIGhlcmUpXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vbGFuZ3VhZ2UtZW5naW5lcy5odG1sI3B5dGhvbikgKGByZXRpY3VsYXRlYCBwYWNrYWdlIG5lZWRlZCk6DQoNCmBgYHtweXRob24gY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9DQp4ID0gJ2hlbGxvLCBweXRob24gd29ybGQhJw0KcHJpbnQoeC5zcGxpdCgnICcpKQ0KYGBgDQoNCiMjIFNRTA0KDQooW21vcmUgaW5mb10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duL2xhbmd1YWdlLWVuZ2luZXMuaHRtbCNzcWwpKToNCg0KYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9DQojY3JlYXRlIGFuIGluLW1lbW9yeSBSU1FMaXRlIGRhdGFiYXNlIG9mIHRoZSBtdGNhcnMgZGF0YXNldA0KbGlicmFyeShSU1FMaXRlKQ0KY29uIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgZGJuYW1lID0gJzptZW1vcnk6JykNCg0KZGJMaXN0VGFibGVzKGNvbikNCmRiV3JpdGVUYWJsZShjb24sICJtdGNhcnMiLCBtdGNhcnMpDQpkYkxpc3RUYWJsZXMoY29uKQ0KDQpkYkxpc3RGaWVsZHMoY29uLCAibXRjYXJzIikNCmRiUmVhZFRhYmxlKGNvbiwgIm10Y2FycyIpDQpgYGANCg0KYGBge3NxbCwgY29ubmVjdGlvbj1jb24sIG91dHB1dC52YXIgPSAibXRfY2Fyc19kZiJ9DQpTRUxFQ1QgKg0KRlJPTSBtdGNhcnMNCldIRVJFIChgY3lsYCA9IDQuMCkNCmBgYA0KDQpgYGB7cn0NCm10X2NhcnNfZGYNCmBgYA0KDQpBbmQgbm93ICoqd2UgdGFrZSB0aGUgU1FMIHJlc3VsdHMgd2hpY2ggYXJlIG5vdyBzYXZlZCBhcyBhbiBSIGRhdGFmcmFtZSoqIGFuZCB3ZSB0aHJvdyB0aGVtIGludG8gZ2dwbG90ICghISEpOg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChkYXRhID0gbXRfY2Fyc19kZiwgDQogICAgICAgYWVzKHggPSBkaXNwLCB5ID0gbXBnKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICB4bGFiKCJFbmdpbmUgU2l6ZSIpICsNCiAgeWxhYigiTWlsZXMgUGVyIEdhbGxvbiIpICsNCiAgZ2d0aXRsZSgiRnVlbCBFZmZpY2llbmN5IEdlbmVyYWxseSBEZWNyZWFzZXMgYXMgRW5naW5lIFNpemUgSW5jcmVhc2VzIikNCmBgYA0KDQojIEludGVyYWN0aXZlIGRvY3VtZW50cw0KDQojIyBWaWEgYGh0bWx3aWRnZXRzYCBwYWNrYWdlDQoNClRoZXJlIGFyZSB0d28gdHlwZXMgb2YgW2ludGVyYWN0aXZlXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9sZXNzb24tMTQuaHRtbCkgUiBNYXJrZG93biBkb2N1bWVudHM6IHlvdSBjYW4gdXNlIHRoZSBIVE1MIFdpZGdldHMgZnJhbWV3b3JrLCBvciB0aGUgU2hpbnkgZnJhbWV3b3JrIChvciBib3RoKS4gVGhlIEhUTUwgV2lkZ2V0cyBmcmFtZXdvcmsgaXMgaW1wbGVtZW50ZWQgaW4gdGhlIFIgcGFja2FnZSBodG1sd2lkZ2V0cyAoVmFpZHlhbmF0aGFuIGV0IGFsLiAyMDIwKSwgaW50ZXJmYWNpbmcgSmF2YVNjcmlwdCBsaWJyYXJpZXMgdGhhdCBjcmVhdGUgaW50ZXJhY3RpdmUgYXBwbGljYXRpb25zLCBzdWNoIGFzIGludGVyYWN0aXZlIGdyYXBoaWNzIGFuZCB0YWJsZXMuIFNldmVyYWwgd2lkZ2V0IHBhY2thZ2VzIGhhdmUgYmVlbiBkZXZlbG9wZWQgYmFzZWQgb24gdGhpcyBmcmFtZXdvcmssIHN1Y2ggYXMgRFQgKFhpZSwgQ2hlbmcsIGFuZCBUYW4gMjAyMSksIGxlYWZsZXQgKENoZW5nLCBLYXJhbWJlbGthciwgYW5kIFhpZSAyMDIxKSwgYW5kIGR5Z3JhcGhzIChWYW5kZXJrYW0gZXQgYWwuIDIwMTgpLiBWaXNpdCA8aHR0cHM6Ly93d3cuaHRtbHdpZGdldHMub3JnPiB0byBrbm93IG1vcmUgYWJvdXQgd2lkZ2V0IHBhY2thZ2VzIGFzIHdlbGwgYXMgaG93IHRvIGRldmVsb3AgYSB3aWRnZXQgcGFja2FnZSBieSB5b3Vyc2VsZi4NCg0KQmVsb3cgaXMgYSBtYXAgdGhhdCBzaG93cyB0aGUgbG9jYXRpb24gb2YgdGhlIERlcGFydG1lbnQgb2YgU3RhdGlzdGljcywgSW93YSBTdGF0ZSBVbml2ZXJzaXR5LCBbZm9sbG93aW5nIHRoaXMgcHJvY2VkdXJlXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vaW50ZXJhY3RpdmUtZG9jdW1lbnRzLmh0bWwpLiBGb3IgbW9yZSBIVE1MIHdpZGdldHMsIFtleHBlcmltZW50IHdpdGggdGhhdF0oaHR0cHM6Ly93d3cuaHRtbHdpZGdldHMub3JnL2luZGV4Lmh0bWwpIGNvbGxlY3Rpb24uDQoNCmBgYHtyIG91dC53aWR0aD0nMTAwJScsIGVjaG89VFJVRX0NCmxpYnJhcnkobGVhZmxldCkNCmxlYWZsZXQoKSAlPiUgYWRkVGlsZXMoKSAlPiUNCiAgc2V0VmlldygtOTMuNjUsIDQyLjAyODUsIHpvb20gPSAxNykgJT4lDQogIGFkZFBvcHVwcygNCiAgICAtOTMuNjUsIDQyLjAyODUsDQogICAgJ0hlcmUgaXMgdGhlIDxiPkRlcGFydG1lbnQgb2YgU3RhdGlzdGljczwvYj4sIElTVScNCiAgKQ0KYGBgDQoNCiMjIFZpYSBgc2hpbnlgIHBhY2thZ2UNCg0KQSBzdGFuZGFyZCBSIHBsb3QgY2FuIGJlIG1hZGUgaW50ZXJhY3RpdmUgYnkgd3JhcHBpbmcgaXQgaW4gdGhlIFNoaW55IGByZW5kZXJQbG90KClgIGZ1bmN0aW9uLiBUaGUgYHNlbGVjdElucHV0KClgIGZ1bmN0aW9uIGNyZWF0ZXMgdGhlIGlucHV0IHdpZGdldCB0byBkcml2ZSB0aGUgcGxvdC4NCg0KKihzZWN0aW9uIGNvbW1lbnRlZCBvdXQgZHVlIHRvIGNvbmZsaWN0cyBiZXR3ZWVuIFNoaW55IGFuZCBvdGhlciBwYWNrYWdlcyAobmVlZCB0byByZXZpc2l0IHRoaXMgc2VjdGlvbikqDQoNCjwhLS0gYGBgez1odG1sfSAtLT4NCg0KPCEtLSA8IS0tYGBge3IgZXJ1cHRpb25zLCBjYWNoZT1GQUxTRX0gLS0+DQo8IS0tIGxpYnJhcnkoc2hpbnkpIC0tPg0KDQo8IS0tIHNlbGVjdElucHV0KCAtLT4NCg0KPCEtLSAgICdicmVha3MnLCBsYWJlbCA9ICdOdW1iZXIgb2YgYmluczonLCAtLT4NCg0KPCEtLSAgIGNob2ljZXMgPSBjKDEwLCAyMCwgMzUsIDUwKSwgc2VsZWN0ZWQgPSAyMCAtLT4NCg0KPCEtLSApIC0tPg0KDQo8IS0tIHNsaWRlcklucHV0KCJid19hZGp1c3QiLCBsYWJlbCA9ICJCYW5kd2lkdGggYWRqdXN0bWVudDoiLCAtLT4NCg0KPCEtLSAgICAgICAgICAgICAgIG1pbiA9IDAuMiwgbWF4ID0gMiwgdmFsdWUgPSAxLCBzdGVwID0gMC4yKSAtLT4NCg0KPCEtLSByZW5kZXJQbG90KHsgLS0+DQoNCjwhLS0gICBwYXIobWFyID0gYyg0LCA0LCAuMSwgLjUpKSAtLT4NCg0KPCEtLSAgIGhpc3QoIC0tPg0KDQo8IS0tICAgICBmYWl0aGZ1bCRlcnVwdGlvbnMsIGFzLm51bWVyaWMoaW5wdXQkYnJlYWtzKSwgLS0+DQoNCjwhLS0gICAgIGNvbCA9ICdncmF5JywgYm9yZGVyID0gJ3doaXRlJywgLS0+DQoNCjwhLS0gICAgIHhsYWIgPSAnRHVyYXRpb24gKG1pbnV0ZXMpJywgbWFpbiA9ICcnIC0tPg0KDQo8IS0tICAgKSAtLT4NCg0KPCEtLSB9KSAtLT4NCg0KPCEtLSBgYGAtLT4NCg0KPCEtLSBgYGAgLS0+DQoNCg0KDQojIyBEYXNoYm9hcmRzDQoNCiMjIyBgZmxleGRhc2hib2FyZGAgcGFja2FnZQ0KDQpZb3UgY2FuIHVzZSBgZmxleGRhc2hib2FyZGAgdG8gcHVibGlzaCBncm91cHMgb2YgcmVsYXRlZCBkYXRhIHZpc3VhbGl6YXRpb25zIGFzIGEgZGFzaGJvYXJkLiBBIGBmbGV4ZGFzaGJvYXJkYCBjYW4gZWl0aGVyIGJlIHN0YXRpYyAoYSBzdGFuZGFyZCB3ZWIgcGFnZSkgb3IgZHluYW1pYyAoYSBTaGlueSBpbnRlcmFjdGl2ZSBkb2N1bWVudCkuIEEgd2lkZSB2YXJpZXR5IG9mIGNvbXBvbmVudHMgY2FuIGJlIGluY2x1ZGVkIGluIGBmbGV4ZGFzaGJvYXJkYCBsYXlvdXRzLCBpbmNsdWRpbmc6DQoNCjEuICBJbnRlcmFjdGl2ZSBKYXZhU2NyaXB0IGRhdGEgdmlzdWFsaXphdGlvbnMgYmFzZWQgb24gaHRtbHdpZGdldHMuDQoyLiAgUiBncmFwaGljYWwgb3V0cHV0IGluY2x1ZGluZyBiYXNlLCBsYXR0aWNlLCBhbmQgZ3JpZCBncmFwaGljcy4NCjMuICBUYWJ1bGFyIGRhdGEgKHdpdGggb3B0aW9uYWwgc29ydGluZywgZmlsdGVyaW5nLCBhbmQgcGFnaW5nKS4NCjQuICBWYWx1ZSBib3hlcyBmb3IgaGlnaGxpZ2h0aW5nIGltcG9ydGFudCBzdW1tYXJ5IGRhdGEuDQo1LiAgR2F1Z2VzIGZvciBkaXNwbGF5aW5nIHZhbHVlcyBvbiBhIG1ldGVyIHdpdGhpbiBhIHNwZWNpZmllZCByYW5nZS4NCjYuICBUZXh0IGFubm90YXRpb25zIG9mIHZhcmlvdXMga2luZHMuDQoNCiMjIyBBIHVzYWdlIGV4YW1wbGUNCg0KVGhlIGZvbGxvd2luZyBleGFtcGxlcywgd2hpbGUgd29ya2luZyB3ZWxsLCBhcmUgbm90IGFlc3RoZXRpY2FsbHkgcGxlYXNpbmcgYmVjYXVzZSB0aGUgb3V0cHV0IGZvcm1hdCBvZiB0aGlzIGZpbGUgZG9lcyAqKm5vdCoqIGNvbWUgZnJvbSB0aGUgYGZsZXhkYXNoYm9hcmRgIHBhY2thZ2UuIFNvIHRoZSBpZGVhIGJlaGluZCB0aGlzIHBhY2thZ2UgaXMgdG8gdXNlIGl0IGFsb25nIHdpdGggdGhlIG1haW4gcmVwb3J0LCB2aWEgbGlua3MgZnJvbSBtYWluIHRvIGBmbGV4ZGFzaGJvYXJkYCByZXBvcnQuDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShmbGV4ZGFzaGJvYXJkKQ0KYGBgDQoNCiMjIyMgQ29udGFjdCBSYXRlDQoNCmBgYHtyfQ0KZ2F1Z2UoOTEsIG1pbiA9IDAsIG1heCA9IDEwMCwgc3ltYm9sID0gJyUnLCBnYXVnZVNlY3RvcnMoDQogIHN1Y2Nlc3MgPSBjKDgwLCAxMDApLCB3YXJuaW5nID0gYyg0MCwgNzkpLCBkYW5nZXIgPSBjKDAsIDM5KQ0KKSkNCmBgYA0KDQojIyMjIEF2ZXJhZ2UgUmF0aW5nDQoNCmBgYHtyfQ0KZ2F1Z2UoMzcuNCwgbWluID0gMCwgbWF4ID0gNTAsIGdhdWdlU2VjdG9ycygNCiAgc3VjY2VzcyA9IGMoNDEsIDUwKSwgd2FybmluZyA9IGMoMjEsIDQwKSwgZGFuZ2VyID0gYygwLCAyMCkNCikpDQpgYGANCg0KIyMjIyBDYW5jZWxsYXRpb25zDQoNCmBgYHtyfQ0KZ2F1Z2UoNywgbWluID0gMCwgbWF4ID0gMTAsIGdhdWdlU2VjdG9ycygNCiAgc3VjY2VzcyA9IGMoMCwgMiksIHdhcm5pbmcgPSBjKDMsIDYpLCBkYW5nZXIgPSBjKDcsIDEwKQ0KKSkNCmBgYA0KDQoNCiMgU2VjcmV0cyBmb3IgZWZmZWN0aXZlIGNvZGluZw0KDQoxLiAgV2hlbiBzdGFydGluZyBhIG5ldyBwcm9qZWN0LCBzdGFydCBhbiBSIFN0dWRpbyAnTmV3IFByb2plY3QnIGFuZCBrZWVwIHRoZSBmaWxlIHBhdGggY2xlYW4gYW5kIHRpZHkhIQ0KDQogICAgLSAgIFVzZSBgaGVyZWAgcGFja2FnZSB0byBtYWtlIHRoZSBmaWxlIHJlYWxseSBwb3J0YWJsZS4NCg0KMi4gIFlvdXIgd29ya2luZyBkaXJlY3RvcnkgaXMgd2hlcmUgeW91ciAqLnJtZCBsaXZlcy4NCg0KMy4gIEZpbmlzaCB5b3VyIFIgTWFya2Rvd24gd2l0aCBhIGBzZXNzaW9uLWluZm9gIGNodW5rLg0KDQo0LiAgRG9jdW1lbnQgeW91ciBwYWNrYWdlcyBhbmQgaW5jbHVkZSBjb2RlIGZvciBvcHRpb25hbCBpbnN0YWxsYXRpb24sIHdoZW4gc2hhcmluZyB0aGUgKi5ybWQuDQoNCjUuICBGdW5kYW1lbnRhbCBtaW5kc2V0IHdoZW4gdXNpbmcgUiBNYXJrZG93bjoNCg0KICAgIC0gICBSZXByb2R1Y2libGUgcmVzZWFyY2ggKHJlY29tbWVuZGVkIFtDb3Vyc2VyYSBsZXNzb25dKGh0dHBzOi8vd3d3LmNvdXJzZXJhLm9yZy9sZWFybi9yZXByb2R1Y2libGUtcmVzZWFyY2gpKQ0KICAgIC0gICBMaXRlcmF0ZSBwcm9ncmFtbWluZyAoRG9uYWxkIEtudXRoKQ0KDQo+IFRoZSBpZGVhIG9mIGxpdGVyYXRlIHByb2dyYW1taW5nIHNoaW5lcyBzb21lIGxpZ2h0IG9uIHRoaXMgZGFyayBhcmVhIG9mIHNjaWVuY2UuIFRoaXMgaXMgYW4gaWRlYSBmcm9tIERvbmFsZCBLbnV0aCB3aGVyZSB5b3UgY29tYmluZSB5b3VyIHRleHQgd2l0aCB5b3VyIGNvZGUgb3V0cHV0IHRvIGNyZWF0ZSBhIGRvY3VtZW50LiBUaGlzIGlzIGEgYmxlbmQgb2YgeW91ciBsaXRlcmF0dXJlICh0ZXh0KSwgYW5kIHlvdXIgcHJvZ3JhbW1pbmcgKGNvZGUpLCB0byBjcmVhdGUgc29tZXRoaW5nIHRoYXQgeW91IGNhbiByZWFkIGZyb20gdG9wIHRvIGJvdHRvbS4gSW1hZ2luZSB5b3VyIHBhcGVyIC0gdGhlIGludHJvZHVjdGlvbiwgbWV0aG9kcywgcmVzdWx0cywgZGlzY3Vzc2lvbiwgYW5kIGNvbmNsdXNpb24sIGFuZCBhbGwgdGhlIGJpdHMgb2YgY29kZSB0aGF0IG1ha2UgZWFjaCBzZWN0aW9uLiBXaXRoIGBybWFya2Rvd25gLCB5b3UgY2FuIHNlZSB0aGUgcGllY2VzIG9mIHlvdXIgZGF0YSBhbmFseXNpcyBhbGwgdG9nZXRoZXIgKE5pY29sYXMgVGllcm5leSwgW2hlcmVdKGh0dHBzOi8vcm1kNHNjaS5uanRpZXJuZXkuY29tL3doeS1ybWFya2Rvd24uaHRtbCkpLg0KDQo2LiAgTmFtZSAobGFiZWwpIHRoeSBjaHVua3MhDQoNCjcuICBSdW4gY29kZSBpbiBjaHVua3MsIHJ1biBsb2NhbGx5LCBrbml0LCBzZWUgaG93IHRoZXkgd29yaywgbW92ZSBvbi4NCg0KOC4gIEluY2x1ZGUgVE9ETyBwbGFjZWhvbGRlcnMgYXMgbm90ZXMgZm9yIGZ1dHVyZSByZXZpc2l0cy4NCg0KOS4gQXZvaWQgaGFyZC1jb2RlZCBpbmZvKG51bWJlcnMsIHRleHQpIGlmIHRoZSB1c2Ugb2YgaW5saW5lIFIgY29kZSBpcyBhcHBsaWNhYmxlIGFuZCB2aWFibGUsIGkuZS4sIFRoZXJlIHdlcmUgYHIgbnJvdyhjYXJzKWAgY2FycyBzdHVkaWVkLg0KDQogICAgICAgIFRoZXJlIHdlcmUgYHIgbnJvdyhjYXJzKWAgY2FycyBzdHVkaWVkLg0KDQoxMC4gKipJbnZlc3QqKiBzb21lIHF1YWxpdHkgdGltZSBpbiByZWFkaW5nIHBhY2thZ2VzJyBkb2N1bWVudGF0aW9uLg0KDQojIEJvb2tzIGFuZCBwYWNrYWdlcyB7I2Jvb2tzfQ0KDQpJbiBvcmRlciB0byBwcmVwYXJlIHRoaXMgZmlsZSBJJ3ZlIHJlYWQgYW5kIHRha2VuIG5vdGVzIGZyb20gdGhlIGZvbGxvd2luZyB3ZWIgcmVmZXJlbmNlczoNCg0KMS4gIFlpaHVpIFhpZSwgKDIwMjEpLiBbUiBNYXJrZG93bjogVGhlIERlZmluaXRpdmUgR3VpZGVdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi8pDQoyLiAgWWlodWkgWGllLCBDaHJpc3RvcGhlIERlcnZpZXV4LCBFbWlseSBSaWVkZXJlciwgKDIwMjEpLiBbUiBNYXJrZG93biBDb29rYm9va10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLWNvb2tib29rLykNCjMuICBKb2huIE1hY0ZhcmxhbmUsICgyMDIxKS4gW1BBTkRPQyBvZmZpY2lhbCBkb2N1bWVudGF0aW9uXShodHRwczovL3BhbmRvYy5vcmcvTUFOVUFMLmh0bWwpICh5ZXMsIEkgd2VudCB0aHJvdWdoIGFsbCBvZiBpdCAoYnV0IG9ubHkgb25jZSEhKSkNCjQuICBIb2x0eiBZYW4sICgyMDE4KS5bUGltcCBteSBSTURdKCNodHRwczovL2hvbHR6eS5naXRodWIuaW8vUGltcC1teS1ybWQvKQ0KDQorIFRvIGtuaXQgYW5kIHJ1biB0aGUgcm1kIGZpbGUsIHRoZSBpbnN0YWxsYXRpb24gb2YgdGhlIGZvbGxvd2luZyBwYWNrYWdlcyAocGxlYXNlIGV4Y3VzZSBtZSBpZiBzb21ldGhpbmcncyBtaXNzaW5nISkgaXMgbmVjZXNzYXJ5OiBgaHRtbHRvb2xzYCwgYGxlYWZsZXRgLCBgZmxleGRhc2hib2FyZGAsIGByZXRpY3VsYXRlYCwgYGtuaXRyYCwgYHNoaW55YCwgYHBhbmRlcmAsIGBybWRmb3JtYXRzYCwgYERpYWdyYW1tZVJgLCBga2FibGVFeHRyYWAsIGB0aWR5dmVyc2VgLCBgcGxvdGx5YCwgYGdhcG1pbmRlcmAsIGBEVGAsIGBib29rZG93bmAgYW5kIGBSU1FMaXRlYC4NCg0KDQojIEhvdyB0by4uLj8geyNob3d0b30NCg0KKipUbyBjdXN0b21pemUgdGhlIHBvc3RhbWJsZSoqIChsb3dlci1sZWZ0IGNvcm5lcikgc2VjdGlvbiBvZiB0aGlzIHRoZW1lICgqKnJlYWR0aGVkb3duKiogdGhlbWUgb2YgYHJtZGZvcm1hdHNgIHBhY2thZ2UpIGluIG9yZGVyIHRvIGFkZCBtb3JlIFlBTUwgZWxlbWVudHMgYmV5b25kIHRoZSBkZWZhdWx0IGBhdXRob3JgIGFuZCBgZGF0ZWAgb25lcywgeW91IG5lZWQgdG86IA0KDQorIGRlZmluZSBpbiBZQU1MIHRoZSBlbGVtZW50IHlvdSB3YW50LCBpLmUuLCB3ZWJzaXRlLCB0ZWxlcGhvbmUsIGV0Yy4gDQorIHR3ZWFrIHRlbXBsYXRlLmh0bWwgWydyZWFkdGhlZG93bicgc2VjdGlvbl0sIGZvdW5kIGluIGBybWRmb3JtYXRzYCBwYWNrYWdlIGluc3RhbGxhdGlvbiBmb2xkZXIuIA0KKyB0d2VhayB0aGUgc3R5bGUuY3NzIGZvciB0aGUgJ3JlYWR0aGVkb3duJyB0aGVtZSBbdmFyaW91cyBwb3N0YW1ibGUgc2VjdGlvbnNdIA0KKyBmaW5kIHRoZSBwcm9wZXIgbmFtZSBmb3IgeW91ciBbZ2x5cGhpY29uXShodHRwczovL2dldGJvb3RzdHJhcC5jb20vZG9jcy8zLjMvY29tcG9uZW50cy8pIGFuZCBpbmNsdWRlIGl0IGluIHRoZSB0ZW1wbGF0ZS5odG1sIFsncmVhZHRoZWRvd24nIHNlY3Rpb25dLg0KDQoqKlRvIGtuaXQgd2l0aCBwYXJhbWV0ZXJzKio6DQoNCisgKlVwZGF0ZWQ6IGByIHBhcmFtcyRkYXRlYCogKHRoaXMgd2FzIGRvbmUgd2l0aCBhIGN1c3RvbSBwYXJhbWV0ZXIgaW4gbXkgWUFNTCkgDQogICAgLSAoW2NoZWNrIGhlcmVdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi9wYXJhbWV0ZXJpemVkLXJlcG9ydHMuaHRtbCkgZm9yIGEgZmFudGFzdGljIHdheSBvZiBrbml0dGluZyB3aXRoIHBhcmFtZXRlcnMhKQ0KICAgIC0gW2Fsc28gaGVyZV0oaHR0cHM6Ly93d3cucmljaGFyZHNoYW5uYS5jb20vdHV0b3JpYWwvcm1hcmtkb3duX3R1dG9yaWFsXzEvI2ludHJvZHVjaW5nLXBhcmFtZXRlcnMpICFba25pdHRpbmcgd2l0aCBwYXJhbWV0ZXJzLCBieSBLZWl0aCBNY051bHR5XShpbWFnZXMva25pdHBhcmFtLmpwZWcpe3dpZHRoPSIyNTYiIGhlaWdodD0iMjU2In0NCg0KIyBTZXNzaW9uIEluZm8geyNzZXNzaW9ufQ0KTWFrZSB5b3VyIHJlYWRlcnMnIGxpZmUgZWFzaWVyLiBQcm92aWRlIHRoZW0gd2l0aCB3aGF0J3MgbmVjZXNzYXJ5IHRvIHVuZGVyc3RhbmQgYW5kIHJlcHJvZHVjZSB5b3VyIHdvcmshDQpgYGB7ciBzZXNzaW9uLWluZm8sIGluY2x1ZGU9VFJVRSwgZWNobz1UUlVFLCByZXN1bHRzPSdtYXJrdXAnLCBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30NCmRldnRvb2xzOjpzZXNzaW9uX2luZm8oKQ0KYGBgDQoNCiMgQ2hhbmdlbG9nIHsjY2hhbmdlbG9nfQ0KDQoqKjE3MDkyMSoqIEFkZGVkIGEgbGluayB0byB0aGUgWydUYWJsZXMgR2VuZXJhdG9yJ10oaHR0cHM6Ly93d3cudGFibGVzZ2VuZXJhdG9yLmNvbS9tYXJrZG93bl90YWJsZXMpIHdlYnNpdGUuDQoNCg0KPCEtLSB0b2RvOiBsaXN0IG9mIGZpZ3VyZXMvdGFibGVzIC0tPg0KPCEtLSB0b2RvOiBmaXggKHdoaXRlIHNwYWNlcykgYW5kIG9wdGltaXplIGNzcyAoaS5lLiwgdGhlIGxpZ2h0IGJsdWUgY29sb3Igb2YgYW5jaG9yZWQgaGVhZGluZ3MpIC0tPg0KPCEtLSB0b2RvOiBmaXggdGhlIGFidW5kYW50IHdoaXRlIHNwYWNlIGJldHdlZW4gSDEgYW5kIHBhcmFncmFwaCwgbW9zdCBwcm9iYWJseSBpcyBhIGNzcyBpc3N1ZSAtLT4=