Introduction
R Markdown
R Markdown provides an unified authoring framework for data science, combining your code, its results, and your prose commentary. R Markdown documents are fully reproducible and support dozens of output formats, like PDFs, Word files, slideshows, and more.
R Markdown files are designed to be used in three ways:
For communicating to decision makers, who want to focus on the conclusions, not the code behind the analysis.
For collaborating with other data scientists (including future you!), who are interested in both your conclusions, and how you reached them (i.e. the code).
As an environment in which to do data science, as a modern day lab notebook where you can capture not only what you did, but also what you were thinking.
Cheatsheets: R Markdown Reference Sheet
R Markdown Cheat Sheet
R Markdown Basics
library(ggplot2)
library(dplyr)
smaller <- diamonds %>%
filter(carat <= 2.5)
We have data about 53940 diamonds. Only 126 are larger than 2.5 carats. The distribution of the remainder is shown below:

It contains three important types of content:
- An (optional) YAML header surrounded by —s.
- Chunks of R code surrounded by ```.
- Text mixed with simple text formatting like # heading and italics.
R notebook files show the output inside the editor, while hiding the console. R markdown files shows the output inside the console, and does not show output inside the editor. They differ in the value of output in their YAML headers. The YAML header for the R notebook is
ouptut: html_notebook
while the header for the R markdown file is
ouptut: html_document
The other output options in the YAML header: word_document for Word documents, pdf_document for PDF documents, and html_document for HTML documents.
Text Formatting with Markdown
Text formatting
italic or italic bold bold code superscript2 and subscript2
Lists
- Numbered list item 1
- Item 2. The numbers are incremented automatically in the output.
Links and images
http://example.com
linked phrase
[optional caption text]
Tables
| Content Cell |
Content Cell |
| Content Cell |
Content Cell |
Chunk name
Chunks can be given an optional name: ```{r by-name}. This has three advantages:
- You can more easily navigate to specific chunks using the drop-down code navigator in the bottom-left of the script editor:

Graphics produced by the chunks will have useful names that make them easier to use elsewhere. More on that in other important options.
You can set up networks of cached chunks to avoid re-performing expensive computations on every run. More on that below.
There is one chunk name that imbues special behaviour: setup. When you’re in a notebook mode, the chunk named setup will be run automatically once, before any other code is run.
Chunk Options
Chunk output can be customised with options, arguments supplied to chunk header. Knitr provides almost 60 options that you can use to customize your code chunks. Here we’ll cover the most important chunk options that you’ll use frequently. You can see the full list at
http://yihui.name/knitr/options/.
The most important set of options controls if your code block is executed and what results are inserted in the finished report:
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.
error = TRUE causes the render to continue even if code returns an error. This is rarely something you’ll want to include in the final version of your report, but can be very useful if you need to debug exactly what is going on inside your .Rmd. It’s also useful if you’re teaching R and want to deliberately include an error. The default, error = FALSE causes knitting to fail if there is a single error in the document.
Table
By default, R Markdown prints data frames and matrices as you’d see them in the console:
mtcars[1:5, ]
If you prefer that data be displayed with additional formatting you can use the knitr::kable function. The code below generates Table 27.1.
knitr::kable(
mtcars[1:5, ],
caption = "A knitr kable."
)
Read the documentation for ?knitr::kable to see the other ways in which you can customise the table. For even deeper customisation, consider the xtable, stargazer, pander, tables, and ascii packages. Each provides a set of tools for returning formatted tables from R code.
There is also a rich set of options for controlling how figures are embedded. You’ll learn about these in saving your plots.
Caching
Normally, each knit of a document starts from a completely clean slate. This is great for reproducibility, because it ensures that you’ve captured every important computation in code. However, it can be painful if you have some computations that take a long time. The solution is cache = TRUE. When set, this will save the output of the chunk to a specially named file on disk. On subsequent runs, knitr will check to see if the code has changed, and if it hasn’t, it will reuse the cached results.
The caching system must be used with care, because by default it is based on the code only, not its dependencies. For example, here the processed_data chunk depends on the raw_data chunk:
rawdata <- readr::read_csv("test.csv"))
processed_data <- rawdata %>%
filter(!is.na(import_var)) %>%
mutate(new_variable = complicated_transformation(x, y, z))
Caching the processed_data chunk means that it will get re-run if the dplyr pipeline is changed, but it won’t get rerun if the read_csv() call changes. You can avoid that problem with the dependson chunk option:
processed_data <- rawdata %>%
filter(!is.na(import_var)) %>%
mutate(new_variable = complicated_transformation(x, y, z))
dependson should contain a character vector of every chunk that the cached chunk depends on. Knitr will update the results for the cached chunk whenever it detects that one of its dependencies have changed.
Note that the chunks won’t update if a_very_large_file.csv changes, because knitr caching only tracks changes within the .Rmd file. If you want to also track changes to that file you can use the cache.extra option. This is an arbitrary R expression that will invalidate the cache whenever it changes. A good function to use is file.info(): it returns a bunch of information about the file including when it was last modified. Then you can write:
rawdata <- readr::read_csv("a_very_large_file.csv")
As your caching strategies get progressively more complicated, it’s a good idea to regularly clear out all your caches with knitr::clean_cache().
Global Options
As you work more with knitr, you will discover that some of the default chunk options don’t fit your needs and you want to change them. You can do this by calling knitr::opts_chunk$set() in a code chunk. For example, when writing books and tutorials I set:
knitr::opts_chunk$set(
comment = "#>",
collapse = TRUE
)
This uses my preferred comment formatting, and ensures that the code and output are kept closely entwined. On the other hand, if you were preparing a report, you might set:
knitr::opts_chunk$set(
echo = FALSE
)
That will hide the code by default, so only showing the chunks you deliberately choose to show (with echo = TRUE). You might consider setting message = FALSE and warning = FALSE, but that would make it harder to debug problems because you wouldn’t see any messages in the final document.
Inline Code
There is one other way to embed R code into an R Markdown document: directly into the text, with: r. This can be very useful if you mention properties of your data in the text. For example, in the example document I used at the start of the chapter I had:
We have data about 53940 diamonds. Only 126 are larger than 2.5 carats. The distribution of the remainder is shown below:
When inserting numbers into text, format() is your friend. It allows you to set the number of digits so you don’t print to a ridiculous degree of accuracy, and a big.mark to make numbers easier to read. I’ll often combine these into a helper function:
comma <- function(x) format(x, digits = 2, big.mark = ",")
comma(3452345)
Troubleshooting
Troubleshooting R Markdown documents can be challenging because you are no longer in an interactive R environment, and you will need to learn some new tricks. The first thing you should always try is to recreate the problem in an interactive session. Restart R, then “Run all chunks” (either from Code menu, under Run region), or with the keyboard shortcut Ctrl + Alt + R. If you’re lucky, that will recreate the problem, and you can figure out what’s going on interactively.
If that doesn’t help, there must be something different between your interactive environment and the R markdown environment. You’re going to need to systematically explore the options. The most common difference is the working directory: the working directory of an R Markdown is the directory in which it lives. Check the working directory is what you expect by including getwd() in a chunk.
Next, brainstorm all the things that might cause the bug. You’ll need to systematically check that they’re the same in your R session and your R markdown session. The easiest way to do that is to set error = TRUE on the chunk causing the problem, then use print() and str() to check that settings are as you expect.
References
LS0tDQp0aXRsZTogIlIgRm9yIERhdGEgU2NpZW5jZSBDaGFwdGVyIDIzIFIgTWFya2Rvd24iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KIyBJbnRyb2R1Y3Rpb24NCiMgUiBNYXJrZG93bg0KUiBNYXJrZG93biBwcm92aWRlcyBhbiB1bmlmaWVkIGF1dGhvcmluZyBmcmFtZXdvcmsgZm9yIGRhdGEgc2NpZW5jZSwgY29tYmluaW5nIHlvdXIgY29kZSwgaXRzIHJlc3VsdHMsIGFuZCB5b3VyIHByb3NlIGNvbW1lbnRhcnkuIFIgTWFya2Rvd24gZG9jdW1lbnRzIGFyZSBmdWxseSByZXByb2R1Y2libGUgYW5kIHN1cHBvcnQgZG96ZW5zIG9mIG91dHB1dCBmb3JtYXRzLCBsaWtlIFBERnMsIFdvcmQgZmlsZXMsIHNsaWRlc2hvd3MsIGFuZCBtb3JlLg0KDQpSIE1hcmtkb3duIGZpbGVzIGFyZSBkZXNpZ25lZCB0byBiZSB1c2VkIGluIHRocmVlIHdheXM6DQoNCjEuIEZvciBjb21tdW5pY2F0aW5nIHRvIGRlY2lzaW9uIG1ha2Vycywgd2hvIHdhbnQgdG8gZm9jdXMgb24gdGhlIGNvbmNsdXNpb25zLCBub3QgdGhlIGNvZGUgYmVoaW5kIHRoZSBhbmFseXNpcy4NCg0KMi4gRm9yIGNvbGxhYm9yYXRpbmcgd2l0aCBvdGhlciBkYXRhIHNjaWVudGlzdHMgKGluY2x1ZGluZyBmdXR1cmUgeW91ISksIHdobyBhcmUgaW50ZXJlc3RlZCBpbiBib3RoIHlvdXIgY29uY2x1c2lvbnMsIGFuZCBob3cgeW91IHJlYWNoZWQgdGhlbSAoaS5lLiB0aGUgY29kZSkuDQoNCjMuIEFzIGFuIGVudmlyb25tZW50IGluIHdoaWNoIHRvIGRvIGRhdGEgc2NpZW5jZSwgYXMgYSBtb2Rlcm4gZGF5IGxhYiBub3RlYm9vayB3aGVyZSB5b3UgY2FuIGNhcHR1cmUgbm90IG9ubHkgd2hhdCB5b3UgZGlkLCBidXQgYWxzbyB3aGF0IHlvdSB3ZXJlIHRoaW5raW5nLg0KDQpDaGVhdHNoZWV0czogPC9icj4NCjxhIGhyZWY9Imh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAzL3JtYXJrZG93bi1yZWZlcmVuY2UucGRmIj4gUiBNYXJrZG93biBSZWZlcmVuY2UgU2hlZXQgPC9hPg0KDQo8YSBocmVmPSJodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNi8wMy9ybWFya2Rvd24tY2hlYXRzaGVldC0yLjAucGRmIiA+IFIgTWFya2Rvd24gQ2hlYXQgU2hlZXQgPC9hPg0KDQoNCiMgUiBNYXJrZG93biBCYXNpY3MNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQpzbWFsbGVyIDwtIGRpYW1vbmRzICU+JSANCiAgZmlsdGVyKGNhcmF0IDw9IDIuNSkNCmBgYA0KDQoNCldlIGhhdmUgZGF0YSBhYm91dCBgciBucm93KGRpYW1vbmRzKWAgZGlhbW9uZHMuIE9ubHkgDQpgciBucm93KGRpYW1vbmRzKSAtIG5yb3coc21hbGxlcilgIGFyZSBsYXJnZXIgdGhhbg0KMi41IGNhcmF0cy4gVGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmVtYWluZGVyIGlzIHNob3duDQpiZWxvdzoNCg0KYGBge3IsIGVjaG8gPSBGQUxTRX0NCnNtYWxsZXIgJT4lIA0KICBnZ3Bsb3QoYWVzKGNhcmF0KSkgKyANCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDAuMDEpDQpgYGAgDQoNCg0KSXQgY29udGFpbnMgdGhyZWUgaW1wb3J0YW50IHR5cGVzIG9mIGNvbnRlbnQ6IDwvYnI+DQoNCjEuIEFuIChvcHRpb25hbCkgWUFNTCBoZWFkZXIgc3Vycm91bmRlZCBieSAtLS1zLg0KMi4gQ2h1bmtzIG9mIFIgY29kZSBzdXJyb3VuZGVkIGJ5IGBgYC4NCjMuIFRleHQgbWl4ZWQgd2l0aCBzaW1wbGUgdGV4dCBmb3JtYXR0aW5nIGxpa2UgIyBoZWFkaW5nIGFuZCBfaXRhbGljc18uDQoNCg0KDQoNClIgbm90ZWJvb2sgZmlsZXMgc2hvdyB0aGUgb3V0cHV0IGluc2lkZSB0aGUgZWRpdG9yLCB3aGlsZSBoaWRpbmcgdGhlIGNvbnNvbGUuIFIgbWFya2Rvd24gZmlsZXMgc2hvd3MgdGhlIG91dHB1dCBpbnNpZGUgdGhlIGNvbnNvbGUsIGFuZCBkb2VzIG5vdCBzaG93IG91dHB1dCBpbnNpZGUgdGhlIGVkaXRvci4gVGhleSBkaWZmZXIgaW4gdGhlIHZhbHVlIG9mIG91dHB1dCBpbiB0aGVpciBZQU1MIGhlYWRlcnMuIFRoZSBZQU1MIGhlYWRlciBmb3IgdGhlIFIgbm90ZWJvb2sgaXMNCg0KPiBvdXB0dXQ6IGh0bWxfbm90ZWJvb2sNCg0Kd2hpbGUgdGhlIGhlYWRlciBmb3IgdGhlIFIgbWFya2Rvd24gZmlsZSBpcw0KDQo+IG91cHR1dDogaHRtbF9kb2N1bWVudCANCg0KVGhlIG90aGVyIG91dHB1dCBvcHRpb25zIGluIHRoZSBZQU1MIGhlYWRlcjogKndvcmRfZG9jdW1lbnQqIGZvciBXb3JkIGRvY3VtZW50cywgKnBkZl9kb2N1bWVudCogZm9yIFBERiBkb2N1bWVudHMsIGFuZCAqaHRtbF9kb2N1bWVudCogZm9yIEhUTUwgZG9jdW1lbnRzLg0KDQoNCiMgVGV4dCBGb3JtYXR0aW5nIHdpdGggTWFya2Rvd24NCg0KDQoNClRleHQgZm9ybWF0dGluZyANCg0KDQo+ICppdGFsaWMqICBvciBfaXRhbGljXw0KPiAqKmJvbGQqKiAgIF9fYm9sZF9fDQo+IGBjb2RlYA0KPiBzdXBlcnNjcmlwdF4yXiBhbmQgc3Vic2NyaXB0fjJ+DQoNCg0KDQoNCj4gIyAxc3QgTGV2ZWwgSGVhZGVyDQoNCj4gIyMgMm5kIExldmVsIEhlYWRlcg0KDQo+ICMjIyAzcmQgTGV2ZWwgSGVhZGVyDQoNCkxpc3RzDQoNCj4gKiAgIEJ1bGxldGVkIGxpc3QgaXRlbSAxDQoNCj4gKiAgIEl0ZW0gMg0KDQo+ICAgICogSXRlbSAyYQ0KDQo+ICAgICogSXRlbSAyYg0KDQo+IDEuICBOdW1iZXJlZCBsaXN0IGl0ZW0gMQ0KDQo+IDEuICBJdGVtIDIuIFRoZSBudW1iZXJzIGFyZSBpbmNyZW1lbnRlZCBhdXRvbWF0aWNhbGx5IGluIHRoZSBvdXRwdXQuDQoNCkxpbmtzIGFuZCBpbWFnZXMNCg0KDQo+IDxodHRwOi8vZXhhbXBsZS5jb20+DQoNCj4gW2xpbmtlZCBwaHJhc2VdKGh0dHA6Ly9leGFtcGxlLmNvbSkNCg0KPiAhW29wdGlvbmFsIGNhcHRpb24gdGV4dF0NCg0KVGFibGVzIA0KDQoNCj4gRmlyc3QgSGVhZGVyICB8IFNlY29uZCBIZWFkZXINCj4gLS0tLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0tLS0NCj4gQ29udGVudCBDZWxsICB8IENvbnRlbnQgQ2VsbA0KPiBDb250ZW50IENlbGwgIHwgQ29udGVudCBDZWxsDQoNCg0KIyBDaHVuayBuYW1lDQoNCkNodW5rcyBjYW4gYmUgZ2l2ZW4gYW4gb3B0aW9uYWwgbmFtZTogYGBge3IgYnktbmFtZX0uIFRoaXMgaGFzIHRocmVlIGFkdmFudGFnZXM6IDwvYnI+DQoNCjEuIFlvdSBjYW4gbW9yZSBlYXNpbHkgbmF2aWdhdGUgdG8gc3BlY2lmaWMgY2h1bmtzIHVzaW5nIHRoZSBkcm9wLWRvd24gY29kZSBuYXZpZ2F0b3IgaW4gdGhlIGJvdHRvbS1sZWZ0IG9mIHRoZSBzY3JpcHQgZWRpdG9yOiA8L2JyPg0KDQo8aW1nIHNyYz0iaHR0cDovL3I0ZHMuaGFkLmNvLm56L3NjcmVlbnNob3RzL3JtYXJrZG93bi1jaHVuay1uYXYucG5nIiAvPg0KDQoNCjIuIEdyYXBoaWNzIHByb2R1Y2VkIGJ5IHRoZSBjaHVua3Mgd2lsbCBoYXZlIHVzZWZ1bCBuYW1lcyB0aGF0IG1ha2UgdGhlbSBlYXNpZXIgdG8gdXNlIGVsc2V3aGVyZS4gTW9yZSBvbiB0aGF0IGluIG90aGVyIGltcG9ydGFudCBvcHRpb25zLg0KDQozLiBZb3UgY2FuIHNldCB1cCBuZXR3b3JrcyBvZiBjYWNoZWQgY2h1bmtzIHRvIGF2b2lkIHJlLXBlcmZvcm1pbmcgZXhwZW5zaXZlIGNvbXB1dGF0aW9ucyBvbiBldmVyeSBydW4uIE1vcmUgb24gdGhhdCBiZWxvdy4NCg0KVGhlcmUgaXMgb25lIGNodW5rIG5hbWUgdGhhdCBpbWJ1ZXMgc3BlY2lhbCBiZWhhdmlvdXI6IHNldHVwLiBXaGVuIHlvdSdyZSBpbiBhIG5vdGVib29rIG1vZGUsIHRoZSBjaHVuayBuYW1lZCBzZXR1cCB3aWxsIGJlIHJ1biBhdXRvbWF0aWNhbGx5IG9uY2UsIGJlZm9yZSBhbnkgb3RoZXIgY29kZSBpcyBydW4uPC9wPg0KDQojIyBDaHVuayBPcHRpb25zDQoNCkNodW5rIG91dHB1dCBjYW4gYmUgY3VzdG9taXNlZCB3aXRoIG9wdGlvbnMsIGFyZ3VtZW50cyBzdXBwbGllZCB0byBjaHVuayBoZWFkZXIuIEtuaXRyIHByb3ZpZGVzIGFsbW9zdCA2MCBvcHRpb25zIHRoYXQgeW91IGNhbiB1c2UgdG8gY3VzdG9taXplIHlvdXIgY29kZSBjaHVua3MuIEhlcmUgd2UnbGwgY292ZXIgdGhlIG1vc3QgaW1wb3J0YW50IGNodW5rIG9wdGlvbnMgdGhhdCB5b3UnbGwgdXNlIGZyZXF1ZW50bHkuIFlvdSBjYW4gc2VlIHRoZSBmdWxsIGxpc3QgYXQgaHR0cDovL3lpaHVpLm5hbWUva25pdHIvb3B0aW9ucy8uIDwvcD4NCg0KVGhlIG1vc3QgaW1wb3J0YW50IHNldCBvZiBvcHRpb25zIGNvbnRyb2xzIGlmIHlvdXIgY29kZSBibG9jayBpcyBleGVjdXRlZCBhbmQgd2hhdCByZXN1bHRzIGFyZSBpbnNlcnRlZCBpbiB0aGUgZmluaXNoZWQgcmVwb3J0OiA8L3A+DQoNCg0KKiBldmFsID0gRkFMU0UgcHJldmVudHMgY29kZSBmcm9tIGJlaW5nIGV2YWx1YXRlZC4gKEFuZCBvYnZpb3VzbHkgaWYgdGhlIGNvZGUgaXMgbm90IHJ1biwgbm8gcmVzdWx0cyB3aWxsIGJlIGdlbmVyYXRlZCkuIFRoaXMgaXMgdXNlZnVsIGZvciBkaXNwbGF5aW5nIGV4YW1wbGUgY29kZSwgb3IgZm9yIGRpc2FibGluZyBhIGxhcmdlIGJsb2NrIG9mIGNvZGUgd2l0aG91dCBjb21tZW50aW5nIGVhY2ggbGluZS4NCg0KKiBpbmNsdWRlID0gRkFMU0UgcnVucyB0aGUgY29kZSwgYnV0IGRvZXNuJ3Qgc2hvdyB0aGUgY29kZSBvciByZXN1bHRzIGluIHRoZSBmaW5hbCBkb2N1bWVudC4gVXNlIHRoaXMgZm9yIHNldHVwIGNvZGUgdGhhdCB5b3UgZG9uJ3Qgd2FudCBjbHV0dGVyaW5nIHlvdXIgcmVwb3J0Lg0KDQoqIGVjaG8gPSBGQUxTRSBwcmV2ZW50cyBjb2RlLCBidXQgbm90IHRoZSByZXN1bHRzIGZyb20gYXBwZWFyaW5nIGluIHRoZSBmaW5pc2hlZCBmaWxlLiBVc2UgdGhpcyB3aGVuIHdyaXRpbmcgcmVwb3J0cyBhaW1lZCBhdCBwZW9wbGUgd2hvIGRvbid0IHdhbnQgdG8gc2VlIHRoZSB1bmRlcmx5aW5nIFIgY29kZS4NCg0KKiBtZXNzYWdlID0gRkFMU0Ugb3Igd2FybmluZyA9IEZBTFNFIHByZXZlbnRzIG1lc3NhZ2VzIG9yIHdhcm5pbmdzIGZyb20gYXBwZWFyaW5nIGluIHRoZSBmaW5pc2hlZCBmaWxlLg0KDQoqIHJlc3VsdHMgPSAnaGlkZScgaGlkZXMgcHJpbnRlZCBvdXRwdXQ7IGZpZy5zaG93ID0gJ2hpZGUnIGhpZGVzIHBsb3RzLg0KDQoqIGVycm9yID0gVFJVRSBjYXVzZXMgdGhlIHJlbmRlciB0byBjb250aW51ZSBldmVuIGlmIGNvZGUgcmV0dXJucyBhbiBlcnJvci4gVGhpcyBpcyByYXJlbHkgc29tZXRoaW5nIHlvdSdsbCB3YW50IHRvIGluY2x1ZGUgaW4gdGhlIGZpbmFsIHZlcnNpb24gb2YgeW91ciByZXBvcnQsIGJ1dCBjYW4gYmUgdmVyeSB1c2VmdWwgaWYgeW91IG5lZWQgdG8gZGVidWcgZXhhY3RseSB3aGF0IGlzIGdvaW5nIG9uIGluc2lkZSB5b3VyIC5SbWQuIEl0J3MgYWxzbyB1c2VmdWwgaWYgeW91J3JlIHRlYWNoaW5nIFIgYW5kIHdhbnQgdG8gZGVsaWJlcmF0ZWx5IGluY2x1ZGUgYW4gZXJyb3IuIFRoZSBkZWZhdWx0LCBlcnJvciA9IEZBTFNFIGNhdXNlcyBrbml0dGluZyB0byBmYWlsIGlmIHRoZXJlIGlzIGEgc2luZ2xlIGVycm9yIGluIHRoZSBkb2N1bWVudC4NCg0KDQojIFRhYmxlDQoNCkJ5IGRlZmF1bHQsIFIgTWFya2Rvd24gcHJpbnRzIGRhdGEgZnJhbWVzIGFuZCBtYXRyaWNlcyBhcyB5b3UnZCBzZWUgdGhlbSBpbiB0aGUgY29uc29sZToNCg0KYGBge3J9DQptdGNhcnNbMTo1LCBdDQpgYGANCg0KSWYgeW91IHByZWZlciB0aGF0IGRhdGEgYmUgZGlzcGxheWVkIHdpdGggYWRkaXRpb25hbCBmb3JtYXR0aW5nIHlvdSBjYW4gdXNlIHRoZSBrbml0cjo6a2FibGUgZnVuY3Rpb24uIFRoZSBjb2RlIGJlbG93IGdlbmVyYXRlcyBUYWJsZSAyNy4xLg0KDQpgYGB7cn0NCmtuaXRyOjprYWJsZSgNCiAgbXRjYXJzWzE6NSwgXSwgDQogIGNhcHRpb24gPSAiQSBrbml0ciBrYWJsZS4iDQopDQpgYGANCg0KUmVhZCB0aGUgZG9jdW1lbnRhdGlvbiBmb3IgP2tuaXRyOjprYWJsZSB0byBzZWUgdGhlIG90aGVyIHdheXMgaW4gd2hpY2ggeW91IGNhbiBjdXN0b21pc2UgdGhlIHRhYmxlLiBGb3IgZXZlbiBkZWVwZXIgY3VzdG9taXNhdGlvbiwgY29uc2lkZXIgdGhlIHh0YWJsZSwgc3RhcmdhemVyLCBwYW5kZXIsIHRhYmxlcywgYW5kIGFzY2lpIHBhY2thZ2VzLiBFYWNoIHByb3ZpZGVzIGEgc2V0IG9mIHRvb2xzIGZvciByZXR1cm5pbmcgZm9ybWF0dGVkIHRhYmxlcyBmcm9tIFIgY29kZS4gPC9wPg0KDQoNClRoZXJlIGlzIGFsc28gYSByaWNoIHNldCBvZiBvcHRpb25zIGZvciBjb250cm9sbGluZyBob3cgZmlndXJlcyBhcmUgZW1iZWRkZWQuIFlvdSdsbCBsZWFybiBhYm91dCB0aGVzZSBpbiBzYXZpbmcgeW91ciBwbG90cy4gPC9wPg0KDQoNCiMgQ2FjaGluZw0KDQpOb3JtYWxseSwgZWFjaCBrbml0IG9mIGEgZG9jdW1lbnQgc3RhcnRzIGZyb20gYSBjb21wbGV0ZWx5IGNsZWFuIHNsYXRlLiBUaGlzIGlzIGdyZWF0IGZvciByZXByb2R1Y2liaWxpdHksIGJlY2F1c2UgaXQgZW5zdXJlcyB0aGF0IHlvdSd2ZSBjYXB0dXJlZCBldmVyeSBpbXBvcnRhbnQgY29tcHV0YXRpb24gaW4gY29kZS4gSG93ZXZlciwgaXQgY2FuIGJlIHBhaW5mdWwgaWYgeW91IGhhdmUgc29tZSBjb21wdXRhdGlvbnMgdGhhdCB0YWtlIGEgbG9uZyB0aW1lLiBUaGUgc29sdXRpb24gaXMgY2FjaGUgPSBUUlVFLiBXaGVuIHNldCwgdGhpcyB3aWxsIHNhdmUgdGhlIG91dHB1dCBvZiB0aGUgY2h1bmsgdG8gYSBzcGVjaWFsbHkgbmFtZWQgZmlsZSBvbiBkaXNrLiBPbiBzdWJzZXF1ZW50IHJ1bnMsIGtuaXRyIHdpbGwgY2hlY2sgdG8gc2VlIGlmIHRoZSBjb2RlIGhhcyBjaGFuZ2VkLCBhbmQgaWYgaXQgaGFzbid0LCBpdCB3aWxsIHJldXNlIHRoZSBjYWNoZWQgcmVzdWx0cy4gPC9wPg0KDQpUaGUgY2FjaGluZyBzeXN0ZW0gbXVzdCBiZSB1c2VkIHdpdGggY2FyZSwgYmVjYXVzZSBieSBkZWZhdWx0IGl0IGlzIGJhc2VkIG9uIHRoZSBjb2RlIG9ubHksIG5vdCBpdHMgZGVwZW5kZW5jaWVzLiBGb3IgZXhhbXBsZSwgaGVyZSB0aGUgcHJvY2Vzc2VkX2RhdGEgY2h1bmsgZGVwZW5kcyBvbiB0aGUgcmF3X2RhdGEgY2h1bms6DQoNCg0KYGBge3J9DQpyYXdkYXRhIDwtIHJlYWRyOjpyZWFkX2NzdigidGVzdC5jc3YiKSkNCmBgYA0KDQoNCmBgYHtyIHByb2Nlc3NlZF9kYXRhLCBjYWNoZSA9IFRSVUV9DQpwcm9jZXNzZWRfZGF0YSA8LSByYXdkYXRhICU+JSANCiAgZmlsdGVyKCFpcy5uYShpbXBvcnRfdmFyKSkgJT4lIA0KICBtdXRhdGUobmV3X3ZhcmlhYmxlID0gY29tcGxpY2F0ZWRfdHJhbnNmb3JtYXRpb24oeCwgeSwgeikpDQpgYGANCg0KDQpDYWNoaW5nIHRoZSBwcm9jZXNzZWRfZGF0YSBjaHVuayBtZWFucyB0aGF0IGl0IHdpbGwgZ2V0IHJlLXJ1biBpZiB0aGUgZHBseXIgcGlwZWxpbmUgaXMgY2hhbmdlZCwgYnV0IGl0IHdvbid0IGdldCByZXJ1biBpZiB0aGUgcmVhZF9jc3YoKSBjYWxsIGNoYW5nZXMuIFlvdSBjYW4gYXZvaWQgdGhhdCBwcm9ibGVtIHdpdGggdGhlIGRlcGVuZHNvbiBjaHVuayBvcHRpb246DQoNCmBgYHtyIHByb2Nlc3NlZF9kYXRhMSwgY2FjaGUgPSBUUlVFLCBkZXBlbmRzb24gPSAicmF3X2RhdGEifQ0KcHJvY2Vzc2VkX2RhdGEgPC0gcmF3ZGF0YSAlPiUgDQogIGZpbHRlcighaXMubmEoaW1wb3J0X3ZhcikpICU+JSANCiAgbXV0YXRlKG5ld192YXJpYWJsZSA9IGNvbXBsaWNhdGVkX3RyYW5zZm9ybWF0aW9uKHgsIHksIHopKQ0KYGBgDQoNCmRlcGVuZHNvbiBzaG91bGQgY29udGFpbiBhIGNoYXJhY3RlciB2ZWN0b3Igb2YgZXZlcnkgY2h1bmsgdGhhdCB0aGUgY2FjaGVkIGNodW5rIGRlcGVuZHMgb24uIEtuaXRyIHdpbGwgdXBkYXRlIHRoZSByZXN1bHRzIGZvciB0aGUgY2FjaGVkIGNodW5rIHdoZW5ldmVyIGl0IGRldGVjdHMgdGhhdCBvbmUgb2YgaXRzIGRlcGVuZGVuY2llcyBoYXZlIGNoYW5nZWQuPC9wPg0KDQoNCk5vdGUgdGhhdCB0aGUgY2h1bmtzIHdvbid0IHVwZGF0ZSBpZiBhX3ZlcnlfbGFyZ2VfZmlsZS5jc3YgY2hhbmdlcywgYmVjYXVzZSBrbml0ciBjYWNoaW5nIG9ubHkgdHJhY2tzIGNoYW5nZXMgd2l0aGluIHRoZSAuUm1kIGZpbGUuIElmIHlvdSB3YW50IHRvIGFsc28gdHJhY2sgY2hhbmdlcyB0byB0aGF0IGZpbGUgeW91IGNhbiB1c2UgdGhlIGNhY2hlLmV4dHJhIG9wdGlvbi4gVGhpcyBpcyBhbiBhcmJpdHJhcnkgUiBleHByZXNzaW9uIHRoYXQgd2lsbCBpbnZhbGlkYXRlIHRoZSBjYWNoZSB3aGVuZXZlciBpdCBjaGFuZ2VzLiBBIGdvb2QgZnVuY3Rpb24gdG8gdXNlIGlzIGZpbGUuaW5mbygpOiBpdCByZXR1cm5zIGEgYnVuY2ggb2YgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGZpbGUgaW5jbHVkaW5nIHdoZW4gaXQgd2FzIGxhc3QgbW9kaWZpZWQuIFRoZW4geW91IGNhbiB3cml0ZTogPC9wPg0KDQpgYGB7ciByYXdfZGF0YSwgY2FjaGUuZXh0cmEgPSBmaWxlLmluZm8oImFfdmVyeV9sYXJnZV9maWxlLmNzdiIpfQ0KcmF3ZGF0YSA8LSByZWFkcjo6cmVhZF9jc3YoImFfdmVyeV9sYXJnZV9maWxlLmNzdiIpDQpgYGANCg0KQXMgeW91ciBjYWNoaW5nIHN0cmF0ZWdpZXMgZ2V0IHByb2dyZXNzaXZlbHkgbW9yZSBjb21wbGljYXRlZCwgaXQncyBhIGdvb2QgaWRlYSB0byByZWd1bGFybHkgY2xlYXIgb3V0IGFsbCB5b3VyIGNhY2hlcyB3aXRoIGtuaXRyOjpjbGVhbl9jYWNoZSgpLiA8L3A+DQoNCiMgR2xvYmFsIE9wdGlvbnMNCg0KQXMgeW91IHdvcmsgbW9yZSB3aXRoIGtuaXRyLCB5b3Ugd2lsbCBkaXNjb3ZlciB0aGF0IHNvbWUgb2YgdGhlIGRlZmF1bHQgY2h1bmsgb3B0aW9ucyBkb24ndCBmaXQgeW91ciBuZWVkcyBhbmQgeW91IHdhbnQgdG8gY2hhbmdlIHRoZW0uIFlvdSBjYW4gZG8gdGhpcyBieSBjYWxsaW5nIGtuaXRyOjpvcHRzX2NodW5rJHNldCgpIGluIGEgY29kZSBjaHVuay4gRm9yIGV4YW1wbGUsIHdoZW4gd3JpdGluZyBib29rcyBhbmQgdHV0b3JpYWxzIEkgc2V0Og0KDQpgYGB7cn0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgY29tbWVudCA9ICIjPiIsDQogIGNvbGxhcHNlID0gVFJVRQ0KKQ0KYGBgDQoNClRoaXMgdXNlcyBteSBwcmVmZXJyZWQgY29tbWVudCBmb3JtYXR0aW5nLCBhbmQgZW5zdXJlcyB0aGF0IHRoZSBjb2RlIGFuZCBvdXRwdXQgYXJlIGtlcHQgY2xvc2VseSBlbnR3aW5lZC4gT24gdGhlIG90aGVyIGhhbmQsIGlmIHlvdSB3ZXJlIHByZXBhcmluZyBhIHJlcG9ydCwgeW91IG1pZ2h0IHNldDoNCg0KYGBge3J9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGVjaG8gPSBGQUxTRQ0KKQ0KDQpgYGANCg0KVGhhdCB3aWxsIGhpZGUgdGhlIGNvZGUgYnkgZGVmYXVsdCwgc28gb25seSBzaG93aW5nIHRoZSBjaHVua3MgeW91IGRlbGliZXJhdGVseSBjaG9vc2UgdG8gc2hvdyAod2l0aCBlY2hvID0gVFJVRSkuIFlvdSBtaWdodCBjb25zaWRlciBzZXR0aW5nIG1lc3NhZ2UgPSBGQUxTRSBhbmQgd2FybmluZyA9IEZBTFNFLCBidXQgdGhhdCB3b3VsZCBtYWtlIGl0IGhhcmRlciB0byBkZWJ1ZyBwcm9ibGVtcyBiZWNhdXNlIHlvdSB3b3VsZG4ndCBzZWUgYW55IG1lc3NhZ2VzIGluIHRoZSBmaW5hbCBkb2N1bWVudC4NCg0KIyBJbmxpbmUgQ29kZQ0KDQpUaGVyZSBpcyBvbmUgb3RoZXIgd2F5IHRvIGVtYmVkIFIgY29kZSBpbnRvIGFuIFIgTWFya2Rvd24gZG9jdW1lbnQ6IGRpcmVjdGx5IGludG8gdGhlIHRleHQsIHdpdGg6IGByIGAuIFRoaXMgY2FuIGJlIHZlcnkgdXNlZnVsIGlmIHlvdSBtZW50aW9uIHByb3BlcnRpZXMgb2YgeW91ciBkYXRhIGluIHRoZSB0ZXh0LiBGb3IgZXhhbXBsZSwgaW4gdGhlIGV4YW1wbGUgZG9jdW1lbnQgSSB1c2VkIGF0IHRoZSBzdGFydCBvZiB0aGUgY2hhcHRlciBJIGhhZDoNCg0KV2UgaGF2ZSBkYXRhIGFib3V0IGByIG5yb3coZGlhbW9uZHMpYCBkaWFtb25kcy4gT25seSBgciBucm93KGRpYW1vbmRzKSAtIG5yb3coc21hbGxlcilgIGFyZSBsYXJnZXIgdGhhbiAyLjUgY2FyYXRzLiBUaGUgZGlzdHJpYnV0aW9uIG9mIHRoZSByZW1haW5kZXIgaXMgc2hvd24gYmVsb3c6DQoNCg0KV2hlbiBpbnNlcnRpbmcgbnVtYmVycyBpbnRvIHRleHQsIGZvcm1hdCgpIGlzIHlvdXIgZnJpZW5kLiBJdCBhbGxvd3MgeW91IHRvIHNldCB0aGUgbnVtYmVyIG9mIGRpZ2l0cyBzbyB5b3UgZG9uJ3QgcHJpbnQgdG8gYSByaWRpY3Vsb3VzIGRlZ3JlZSBvZiBhY2N1cmFjeSwgYW5kIGEgYmlnLm1hcmsgdG8gbWFrZSBudW1iZXJzIGVhc2llciB0byByZWFkLiBJJ2xsIG9mdGVuIGNvbWJpbmUgdGhlc2UgaW50byBhIGhlbHBlciBmdW5jdGlvbjoNCg0KYGBge3J9DQpjb21tYSA8LSBmdW5jdGlvbih4KSBmb3JtYXQoeCwgZGlnaXRzID0gMiwgYmlnLm1hcmsgPSAiLCIpDQpjb21tYSgzNDUyMzQ1KQ0KYGBgDQoNCiMgVHJvdWJsZXNob290aW5nIA0KDQpUcm91Ymxlc2hvb3RpbmcgUiBNYXJrZG93biBkb2N1bWVudHMgY2FuIGJlIGNoYWxsZW5naW5nIGJlY2F1c2UgeW91IGFyZSBubyBsb25nZXIgaW4gYW4gaW50ZXJhY3RpdmUgUiBlbnZpcm9ubWVudCwgYW5kIHlvdSB3aWxsIG5lZWQgdG8gbGVhcm4gc29tZSBuZXcgdHJpY2tzLiBUaGUgZmlyc3QgdGhpbmcgeW91IHNob3VsZCBhbHdheXMgdHJ5IGlzIHRvIHJlY3JlYXRlIHRoZSBwcm9ibGVtIGluIGFuIGludGVyYWN0aXZlIHNlc3Npb24uIFJlc3RhcnQgUiwgdGhlbiAiUnVuIGFsbCBjaHVua3MiIChlaXRoZXIgZnJvbSBDb2RlIG1lbnUsIHVuZGVyIFJ1biByZWdpb24pLCBvciB3aXRoIHRoZSBrZXlib2FyZCBzaG9ydGN1dCBDdHJsICsgQWx0ICsgUi4gSWYgeW91J3JlIGx1Y2t5LCB0aGF0IHdpbGwgcmVjcmVhdGUgdGhlIHByb2JsZW0sIGFuZCB5b3UgY2FuIGZpZ3VyZSBvdXQgd2hhdCdzIGdvaW5nIG9uIGludGVyYWN0aXZlbHkuIDwvcD4NCg0KSWYgdGhhdCBkb2Vzbid0IGhlbHAsIHRoZXJlIG11c3QgYmUgc29tZXRoaW5nIGRpZmZlcmVudCBiZXR3ZWVuIHlvdXIgaW50ZXJhY3RpdmUgZW52aXJvbm1lbnQgYW5kIHRoZSBSIG1hcmtkb3duIGVudmlyb25tZW50LiBZb3UncmUgZ29pbmcgdG8gbmVlZCB0byBzeXN0ZW1hdGljYWxseSBleHBsb3JlIHRoZSBvcHRpb25zLiBUaGUgbW9zdCBjb21tb24gZGlmZmVyZW5jZSBpcyB0aGUgd29ya2luZyBkaXJlY3Rvcnk6IHRoZSB3b3JraW5nIGRpcmVjdG9yeSBvZiBhbiBSIE1hcmtkb3duIGlzIHRoZSBkaXJlY3RvcnkgaW4gd2hpY2ggaXQgbGl2ZXMuIENoZWNrIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBpcyB3aGF0IHlvdSBleHBlY3QgYnkgaW5jbHVkaW5nIGdldHdkKCkgaW4gYSBjaHVuay4gPC9wPg0KDQpOZXh0LCBicmFpbnN0b3JtIGFsbCB0aGUgdGhpbmdzIHRoYXQgbWlnaHQgY2F1c2UgdGhlIGJ1Zy4gWW91J2xsIG5lZWQgdG8gc3lzdGVtYXRpY2FsbHkgY2hlY2sgdGhhdCB0aGV5J3JlIHRoZSBzYW1lIGluIHlvdXIgUiBzZXNzaW9uIGFuZCB5b3VyIFIgbWFya2Rvd24gc2Vzc2lvbi4gVGhlIGVhc2llc3Qgd2F5IHRvIGRvIHRoYXQgaXMgdG8gc2V0IGVycm9yID0gVFJVRSBvbiB0aGUgY2h1bmsgY2F1c2luZyB0aGUgcHJvYmxlbSwgdGhlbiB1c2UgcHJpbnQoKSBhbmQgc3RyKCkgdG8gY2hlY2sgdGhhdCBzZXR0aW5ncyBhcmUgYXMgeW91IGV4cGVjdC4gPC9wPg0KDQojIFlBTUwgSGVhZGVyDQpZb3UgY2FuIGNvbnRyb2wgbWFueSBvdGhlciAid2hvbGUgZG9jdW1lbnQiIHNldHRpbmdzIGJ5IHR3ZWFraW5nIHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBZQU1MIGhlYWRlci4gWW91IG1pZ2h0IHdvbmRlciB3aGF0IFlBTUwgc3RhbmRzIGZvcjogaXQncyAieWV0IGFub3RoZXIgbWFya3VwIGxhbmd1YWdlIiwgd2hpY2ggaXMgZGVzaWduZWQgZm9yIHJlcHJlc2VudGluZyBoaWVyYXJjaGljYWwgZGF0YSBpbiBhIHdheSB0aGF0J3MgZWFzeSBmb3IgaHVtYW5zIHRvIHJlYWQgYW5kIHdyaXRlLiBSIE1hcmtkb3duIHVzZXMgaXQgdG8gY29udHJvbCBtYW55IGRldGFpbHMgb2YgdGhlIG91dHB1dC4gSGVyZSB3ZSdsbCBkaXNjdXNzIHR3bzogZG9jdW1lbnQgcGFyYW1ldGVycyBhbmQgYmlibGlvZ3JhcGhpZXMuIDwvcD4NCg0KIyMgUGFyYW1ldGVycw0KUiBNYXJrZG93biBkb2N1bWVudHMgY2FuIGluY2x1ZGUgb25lIG9yIG1vcmUgcGFyYW1ldGVycyB3aG9zZSB2YWx1ZXMgY2FuIGJlIHNldCB3aGVuIHlvdSByZW5kZXIgdGhlIHJlcG9ydC4gUGFyYW1ldGVycyBhcmUgdXNlZnVsIHdoZW4geW91IHdhbnQgdG8gcmUtcmVuZGVyIHRoZSBzYW1lIHJlcG9ydCB3aXRoIGRpc3RpbmN0IHZhbHVlcyBmb3IgdmFyaW91cyBrZXkgaW5wdXRzLiBGb3IgZXhhbXBsZSwgeW91IG1pZ2h0IGJlIHByb2R1Y2luZyBzYWxlcyByZXBvcnRzIHBlciBicmFuY2gsIGV4YW0gcmVzdWx0cyBieSBzdHVkZW50LCBvciBkZW1vZ3JhcGhpYyBzdW1tYXJpZXMgYnkgY291bnRyeS4gVG8gZGVjbGFyZSBvbmUgb3IgbW9yZSBwYXJhbWV0ZXJzLCB1c2UgdGhlIHBhcmFtcyBmaWVsZC4gPC9wPg0KDQpUaGlzIGV4YW1wbGUgdXNlIGEgbXlfY2xhc3MgcGFyYW1ldGVyIHRvIGRldGVybWluZXMgd2hpY2ggY2xhc3Mgb2YgY2FycyB0byBkaXNwbGF5Og0KDQoNCg0KQXMgeW91IGNhbiBzZWUsIHBhcmFtZXRlcnMgYXJlIGF2YWlsYWJsZSB3aXRoaW4gdGhlIGNvZGUgY2h1bmtzIGFzIGEgcmVhZC1vbmx5IGxpc3QgbmFtZWQgcGFyYW1zLiA8L3A+DQoNCllvdSBjYW4gd3JpdGUgYXRvbWljIHZlY3RvcnMgZGlyZWN0bHkgaW50byB0aGUgWUFNTCBoZWFkZXIuIFlvdSBjYW4gYWxzbyBydW4gYXJiaXRyYXJ5IFIgZXhwcmVzc2lvbnMgYnkgcHJlZmFjaW5nIHRoZSBwYXJhbWV0ZXIgdmFsdWUgd2l0aCAhci4gVGhpcyBpcyBhIGdvb2Qgd2F5IHRvIHNwZWNpZnkgZGF0ZS90aW1lIHBhcmFtZXRlcnMuIA0KDQojIyBCaWJsaW9ncmFwaGllcyBhbmQgQ2l0YXRpb25zIA0KDQpQYW5kb2MgY2FuIGF1dG9tYXRpY2FsbHkgZ2VuZXJhdGUgY2l0YXRpb25zIGFuZCBhIGJpYmxpb2dyYXBoeSBpbiBhIG51bWJlciBvZiBzdHlsZXMuIFRvIHVzZSB0aGlzIGZlYXR1cmUsIHNwZWNpZnkgYSBiaWJsaW9ncmFwaHkgZmlsZSB1c2luZyB0aGUgYmlibGlvZ3JhcGh5IGZpZWxkIGluIHlvdXIgZmlsZSdzIGhlYWRlci4gVGhlIGZpZWxkIHNob3VsZCBjb250YWluIGEgcGF0aCBmcm9tIHRoZSBkaXJlY3RvcnkgdGhhdCBjb250YWlucyB5b3VyIC5SbWQgZmlsZSB0byB0aGUgZmlsZSB0aGF0IGNvbnRhaW5zIHRoZSBiaWJsaW9ncmFwaHkgZmlsZToNCg0KPiBCaWJsaW9ncmFwaHk6IHJtYXJrZG93bi5iaWINCg0KWW91IGNhbiB1c2UgbWFueSBjb21tb24gYmlibGlvZ3JhcGh5IGZvcm1hdHMgaW5jbHVkaW5nIEJpYkxhVGVYLCBCaWJUZVgsIGVuZG5vdGUsIG1lZGxpbmUuIDwvcD4NCg0KVG8gY3JlYXRlIGEgY2l0YXRpb24gd2l0aGluIHlvdXIgLlJtZCBmaWxlLCB1c2UgYSBrZXkgY29tcG9zZWQgb2YgJ0AnICsgdGhlIGNpdGF0aW9uIGlkZW50aWZpZXIgZnJvbSB0aGUgYmlibGlvZ3JhcGh5IGZpbGUuIFRoZW4gcGxhY2UgdGhlIGNpdGF0aW9uIGluIHNxdWFyZSBicmFja2V0cy4gSGVyZSBhcmUgc29tZSBleGFtcGxlczogPC9icj4NCg0KKiBTZXBhcmF0ZSBtdWx0aXBsZSBjaXRhdGlvbnMgd2l0aCBhIGA7YDogQmxhaCBibGFoIFtAc21pdGgwNDsgQGRvZTk5XS4gDQoNCiogWW91IGNhbiBhZGQgYXJiaXRyYXJ5IGNvbW1lbnRzIGluc2lkZSB0aGUgc3F1YXJlIGJyYWNrZXRzOiANCkJsYWggYmxhaCBbc2VlIEBkb2U5OSwgcHAuIDMzLTM1OyBhbHNvIEBzbWl0aDA0LCBjaC4gMV0uDQoNCiogUmVtb3ZlIHRoZSBzcXVhcmUgYnJhY2tldHMgdG8gY3JlYXRlIGFuIGluLXRleHQgY2l0YXRpb246IEBzbWl0aDA0IA0Kc2F5cyBibGFoLCBvciBAc21pdGgwNCBbcC4gMzNdIHNheXMgYmxhaC4NCg0KKiBBZGQgYSBgLWAgYmVmb3JlIHRoZSBjaXRhdGlvbiB0byBzdXBwcmVzcyB0aGUgYXV0aG9yJ3MgbmFtZTogDQpTbWl0aCBzYXlzIGJsYWggWy1Ac21pdGgwNF0uDQoNCg0KV2hlbiBSIE1hcmtkb3duIHJlbmRlcnMgeW91ciBmaWxlLCBpdCB3aWxsIGJ1aWxkIGFuZCBhcHBlbmQgYSBiaWJsaW9ncmFwaHkgdG8gdGhlIGVuZCBvZiB5b3VyIGRvY3VtZW50LiBUaGUgYmlibGlvZ3JhcGh5IHdpbGwgY29udGFpbiBlYWNoIG9mIHRoZSBjaXRlZCByZWZlcmVuY2VzIGZyb20geW91ciBiaWJsaW9ncmFwaHkgZmlsZSwgYnV0IGl0IHdpbGwgbm90IGNvbnRhaW4gYSBzZWN0aW9uIGhlYWRpbmcuIEFzIGEgcmVzdWx0IGl0IGlzIGNvbW1vbiBwcmFjdGljZSB0byBlbmQgeW91ciBmaWxlIHdpdGggYSBzZWN0aW9uIGhlYWRlciBmb3IgdGhlIGJpYmxpb2dyYXBoeSwgc3VjaCBhcyAjIFJlZmVyZW5jZXMgb3IgIyBCaWJsaW9ncmFwaHkuDQoNCllvdSBjYW4gY2hhbmdlIHRoZSBzdHlsZSBvZiB5b3VyIGNpdGF0aW9ucyBhbmQgYmlibGlvZ3JhcGh5IGJ5IHJlZmVyZW5jaW5nIGEgQ1NMIChjaXRhdGlvbiBzdHlsZSBsYW5ndWFnZSkgZmlsZSBpbiB0aGUgY3NsIGZpZWxkOg0KDQo+IGJpYmxpb2dyYXBoeTogcm1hcmtkb3duLmJpYg0KPiBjc2w6IGFwYS5jc2wNCg0KIyBSZWZlcmVuY2Vz