Background
We saw last time how to create a
basic package from a simple function, document its contents, and explore
its functionality. In this exercise, we’ll learn how to create formal
tests for our functions, write vignettes, and publish our package to the
web.
Testing
We spent just a bit of time testing out our cats()
function, but we can formalize this process with so-called unit
tests. This basically means we’ll establish some expectations about
what our function should return when passed certain arguments. As Wickham & Bryan point out,
there are four advantages to working with unit tests:
Fewer bugs and errors;
More rigorous code structure;
Easier start/stop points;
Code that’s robust to changes.
Fortunately, the {devtools}
package helps us with this
process via some built-in functions. To begin, we’ll use
use_testthat()
to set up our framework for testing, which
will
add Suggests: testthat
to
DESCRIPTION
;
create the directory tests/testthat/
; and
add the script tests/testthat.R
.
Load the {devtools}
package.
> library(devtools)
Loading required package: usethis
Set the testing framework with use_testthat()
.
## set testing framework
> use_testthat()
✔ Setting active project to '/Users/mark/Documents/GitHub/FISH549/pets'
✔ Adding 'testthat' to Suggests field in DESCRIPTION
✔ Setting Config/testthat/edition field in DESCRIPTION to '3'
✔ Creating 'tests/testthat/'
✔ Writing 'tests/testthat.R'
• Call `use_test()` to initialize a basic test file and open it for editing
Write a unit test
Now that we have our testing framework, we need to write some actual
test scripts. Here again, we see a suggestion like last
time to use use_test()
to initialize a basic test
file.
Use the use_test()
function to create an empty script
for testing our cats()
function.
> use_test("cats")
✓ Writing 'tests/testthat/test-cats.R'
● Modify 'tests/testthat/test-cats.R'
You should now see a script called test-cats.R
with the
following example unit test in it.
test_that("multiplication works", {
expect_equal(2 * 2, 4)
})
Unit tests are hierarchical in nature. So-called
expectations are grouped into tests, which are then
organized into files. Expectations are informal and intended
solely for human eyes—they will never be read or checked by an
R function. The test itself is embedded within
test_that()
, and is based upon the expected result of an
operation that we check with an expect_
function. In the
example test shown above, the expectation is that
"multiplication works"
. The specific test of the
expectation is whether or not 2 * 2 == 4
, which is coded as
expect_equal(2 * 2, 4)
.
You can find a whole battery of {testthat}
functions here.
Let’s go ahead and write some unit tests for cats()
.
Delete the default unit test in test-cats.R
and replace
it with the following code.
test_that("logical operators work", {
## if TRUE
expect_equal(cats(TRUE), "I love cats!")
## if alias for TRUE
expect_equal(cats(1), "I love cats!")
## if FALSE
expect_equal(cats(FALSE), "I am not a cat person.")
## if alias for FALSE
expect_equal(cats(0), "I am not a cat person.")
})
Save your file when you’re done entering the tests.
Run unit tests
Now we’re set to run these tests with the test()
function.
Call test()
and note the output, which may differ
somewhat from below.
> test()
ℹ Testing pets
Attaching package: ‘testthat’
The following object is masked from ‘package:devtools’:
test_file
✔ | F W S OK | Context
⠏ | 0 | cats
[1] "I love cats!"
[1] "I love cats!"
[1] "I am not a cat person."
[1] "I am not a cat person."
✔ | 4 | cats
══ Results ════════════════════════════════════════════════════════════════════════
[ FAIL 0 | WARN 0 | SKIP 0 | PASS 4 ]
What does this all mean?
The report indicates how many total tests were run (4) and the output
of each test. The Results
section at the bottom shows how
many of the tests failed (FAIL
), issued a warning
(WARN
), were skipped (SKIP
), and passed
(PASS
).
All four of our tests passed!
Now is a good time to commit your changes and push them to
GitHub.
Packages on GitHub
The pets
repo we created on GitHub
contains our package skeleton in human-readable form. One of the neat
things about this is that you (or anyone else) can now install the
package directly from GitHub.
You can use either of the following function calls to install a
package diectly from GitHub. To do so, replace
GITHUB_USERNAME
with the username of the repo’s owner and
PACKAGE
with the package’s name.
## original call using {devtools} still works
devtools::install_github("GITHUB_USERNAME/PACKAGE")
## but {remotes} is the newer package with the function
remotes::install_github("GITHUB_USERNAME/PACKAGE")
If you try either of these commands and get the following error, run
Sys.unsetenv("GITHUB_PAT")
and then try again.
Using github PAT from envvar GITHUB_PAT
Error: Failed to install 'pets' from GitHub:
HTTP error 401.
Bad credentials
Editing the README.md
Because your package now lives in a public space on GitHub where
others can find and use it, you should include some helpful information
in the README.md
file to describe the package contents and
how it works.
Use the use_readme_rmd()
function to create a
Markdown file with a skeleton to add information about
your package.
> use_readme_rmd()
✓ Writing 'README.Rmd'
✓ Adding '^README\\.Rmd$' to '.Rbuildignore'
● Modify 'README.Rmd'
✓ Writing '.git/hooks/pre-commit'
The output above indicates that the function
Open up README.Rmd
and inspect its contents.
At the top of README.Rmd
you’ll see a very simply YAML
section with only one entry for the output
format.
---
output: github_document
---
Below that you will see a number of sections with prompts asking for
more information about the package, including a description of the
package, how to install it, and example of its usage.
Comments in Markdown are denoted by
<!-- some comment here -->
.
In the first section # pets
, go ahead and enter some
descriptive text about the package contents (you can delete the comments
about badges).
# pets
The goal of pets is to provide a simple means for people to express their feelings about pets. At present, the package only contains one function: `cats()`.
In the next section on ## Installation
, add some install
instructions like those shown above.
## Installation
You can install the development version of pets like so:
``` r
if(!require("devtools")) {
install.packages("devtools")
}
devtools::install_github("FISH549/pets")
```
In the ## Example
section, fill in some information with
example calls to cats()
.
## Example
Here are two simple examples that allow you to express your feelings about cats.
```{r example}
library(pets)
## if you like cats
cats(TRUE)
## if you don't like cats
cats(FALSE)
```
When you are finished editing README.Rmd
, knit it with
the Knit button in RStudio or use the
build_readme()
function at the command line.
> build_readme()
Installing pets in temporary library
Building /Users/mark/Documents/GitHub/pets/README.Rmd
Now commit both README.Rmd
and
README.md
and then push them to GitHub.
When you are finished, check out your pets
repo on
GitHub to see the new changes to your
README.md
.
Vignettes
Vignettes offer a longer description of a package’s utility. They
aren’t a necessary component of a package, but rather one of the “bells
and whistles”. Vignettes are written in Markdown and
can be accessed via vignette(name)
where name
is the name of the vignette.
You can see a list of all of the vignettes within a particular
package with browseVignettes("packagename")
.
To create a vignette, use the use_vignette()
function by
passing it the name of the vignette file to create. Here use
"example-usage"
.
> use_vignette("example-usage")
✔ Adding 'knitr' to Suggests field in DESCRIPTION
✔ Setting VignetteBuilder field in DESCRIPTION to 'knitr'
✔ Adding 'inst/doc' to '.gitignore'
✔ Creating 'vignettes/'
✔ Adding '*.html', '*.R' to 'vignettes/.gitignore'
✔ Adding 'rmarkdown' to Suggests field in DESCRIPTION
✔ Writing 'vignettes/example-usage.Rmd'
• Modify 'vignettes/example-usage.Rmd'
The call to use_vignette()
did the following
added the necessary dependencies to
DESCRIPTION
added some folders and files to .gitignore
created a vignettes/
directory
created the skeleton file
vignettes/example-usage.Rmd
Inspect the contents of example-usage.Rmd
. At the top
you’ll see the following YAML section.
---
title: "example-usage"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{example-usage}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
Change the title to be a little more informative.
title: "Using the pets package"
Change %\VignetteIndexEntry{example-usage}
to match our
new title.
%\VignetteIndexEntry{Using the pets package}
Scroll down below the YAML and inspect the first code chunk where two
options are being set.
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
Setting collapse = TRUE
will if possible, collapse all
the source code and output blocks from one code chunk into a single
block (by default, they are written to separate blocks).
The comment = "#>
determines the prefix for output
from function calls. You can change this to be any character string or
leave it empty.
Add some more details to the body of the vignette.
# Background
The `{pets}` package was designed to allow people to express their feelings about pets. At present, the package is rather cat-centric, in that it only contains one function: `cats()`.
# Usage
To use `{pets}`, first load the package and then call the function `cats()` with a logical/boolean argument. For example,
```{r setup}
## load pets package
library(pets)
## if you love cats
cats(TRUE)
## if you're not a big fan of cats
cats(FALSE)
```
When you are finished, go ahead and click on the
Knit button in RStudio to preview the
html version of the vignette.
At this point, we’ve created our vignette, but we still need to build
it into the package itself.
Use the build_vignettes()
function to build the
vignette.
> build_vignettes()
ℹ Installing pets in temporary library
ℹ Building vignettes for pets
--- re-building ‘example-usage.Rmd’ using rmarkdown
processing file: example-usage.Rmd
output file: example-usage.knit.md
/Applications/RStudio.app/Contents/MacOS/quarto/bin/tools/pandoc +RTS -K512m -RTS example-usage.knit.md --to html4 --from markdown+autolink_bare_uris+tex_math_single_backslash --output /Users/mark/Documents/GitHub/FISH549/pets/vignettes/example-usage.html --lua-filter /Users/mark/R_libs/rmarkdown/rmarkdown/lua/pagebreak.lua --lua-filter /Users/mark/R_libs/rmarkdown/rmarkdown/lua/latex-div.lua --embed-resources --standalone --section-divs --template /Users/mark/R_libs/rmarkdown/rmd/h/default.html --highlight-style pygments --css /Users/mark/R_libs/rmarkdown/rmarkdown/templates/html_vignette/resources/vignette.css --mathjax --variable 'mathjax-url=https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML' --include-in-header /var/folders/w6/bgxxqxln6nxf9z0wr7fypyfw0000gn/T//Rtmppjt1Se/rmarkdown-stra66d76e727.html
Output created: example-usage.html
--- finished re-building ‘example-usage.Rmd’
ℹ Copying vignettes
ℹ Moving example-usage.html and example-usage.R to doc/
ℹ Copying example-usage.Rmd to doc/
ℹ Building vignette index
> build()
✔ checking for file ‘/Users/mark/Documents/GitHub/FISH549/pets/DESCRIPTION’ ...
─ preparing ‘pets’:
✔ checking DESCRIPTION meta-information
─ installing the package to build vignettes
✔ creating vignettes (1.8s)
─ checking for LF line-endings in source and make files and shell scripts
─ checking for empty or unneeded directories
─ building ‘pets_0.0.0.9000.tar.gz’
[1] "/Users/mark/Documents/GitHub/FISH549/pets_0.0.0.9000.tar.gz"
Re-run your package checks (most of the output below has been
surpressed).
> check()
── R CMD check results ────────────────────────────── pets 0.0.0.9000 ────
Duration: 8.7s
0 errors ✓ | 0 warnings ✓ | 0 notes ✓
It looks like everything is in working order!
Go ahead and commit your changes and push them to
GitHub.
Website
If you’ve spent some time searching the web for information on
packages, there’s a good chance you’ve come across a specific form of
package website. For example, here is the wesbite for the
{usethis}
package that we’ve used to build our
{pets}
package. These websites are created with the {pkgdown}
package, which we can use to create a website for
{pets}
.
Begin by loading the {pkgdown}
package.
> library(pkgdown)
Attaching package: ‘pkgdown’
The following object is masked from ‘package:devtools’:
build_site
Configure your package to use {pkgdown}
.
> use_pkgdown()
✔ Adding '^_pkgdown\\.yml$', '^docs$', '^pkgdown$' to '.Rbuildignore'
✔ Adding 'docs' to '.gitignore'
✔ Writing '_pkgdown.yml'
• Modify '_pkgdown.yml'
use_pkgdown()
will add a new file called
_pkgdown.yml
, which contains some YAML and fields for
url
and template
url: ~
template:
bootstrap: 5
Now we can use pkgdown::build_site()
to create the
actual website for the package. When you call this function, you’ll see
a verbose response from R followed by a preview of your
site opened in your web browser.
Before proceeding, we need to make a small change to our
.gitignore
file. When we run build_site()
, it
creates the necessary html and supporting files in the
/docs
directory, so we need to remove it from
.gitignore
so that we can commit its contents and push the
changes to GitHub.
# R Environment Variables
.Renviron
.Rproj.user
inst/doc
/doc/
/Meta/
# docs
Call build_site()
to create the necessary html and
supporting files in the /docs
directory.
> build_site()
-- Installing package into temporary library -------------------------------------------
== Building pkgdown site =======================================================
Reading from: '/Users/mark/Documents/GitHub/FISH549/pets'
Writing to: '/Users/mark/Documents/GitHub/FISH549/pets/docs'
-- Initialising site -----------------------------------------------------------
-- Building home ---------------------------------------------------------------
Reading 'LICENSE.md'
Writing '404.html'
-- Building function reference -------------------------------------------------
Reading 'man/cats.Rd'
Writing 'reference/cats.html'
-- Building articles -----------------------------------------------------------
Writing 'articles/index.html'
Reading 'vignettes/example-usage.Rmd'
Writing 'articles/example-usage.html'
Writing 'sitemap.xml'
-- Building search index -------------------------------------------------------
== DONE ========================================================================
-- Previewing site ---------------------------------------------------------------------
Commit your changes and push them to GitHub.
Navigate to your pets
repo on
GitHub and click on the Settings
tab.
Under the section on the left titled Code and
automation, click on Pages.
Under Branch select main
and set
the folder to /docs
.
Click Save when you’re done.

Navigate to https://USERNAME.github.io/pets/
to view the
website for your package (where USERNAME
is your GitHub
user name).
If youget a 404 error or your site doesn’t display properly, wait a
few minutes and then force-refresh your browser.
Hex sticker
Let’s be honest—hex
stickers are all the rage when it comes to R
packages. We can make our own hex sticker for our {pets}
package with the {hexSticker}
package. The function sticker()
will do all of the work for
us.
Here we’ll include an image of a silhouette cata and dog on our
sticker.
Load {hexSticker}
and make the following call to
sticker()
(you can type ?sticker
to see all of
the function arguments).
> library(hexSticker)
## create sticker image
sticker("cat_and_dog.png",
## package name
package = "pets",
## heights & widths
p_size = 18, s_x = 0.95, s_y = 0.9, asp = 0.85,
s_width = 0.65, s_height = 0.65, p_y = 1.6,
## text color
p_color = "#4b2e83",
## fill color
h_fill = "white",
## border color
h_color = "#85754d",
## filename to save
filename = "logo.png")
Tell R to use our new image with
usethis::use_logo()
.
> usethis::use_logo("logo.png")
✓ Creating 'man/figures/'
✓ Resized 'logo.png' to 240x278
● Add logo to your README with the following html:
Warning: pkgdown config does not specify the site's url, which is optional but recommended
# pets <img src='man/figures/logo.png' align="right" height="139" />
[Copied to clipboard]
The last thing we need to do is edit our README.Rmd
file
to include a reference to the sticker.
Add the following line of code to the right of the
# pets
heading.
# pets <img src="man/figures/logo.png" align="right" alt="" width="120" />
When you are finished, click the Knit button in
RStudio to preview your new readme file.
Lastly, we need to rebuild the website with
build_site()
.
> build_site()
-- Installing package into temporary library -------------------------------------------
== Building pkgdown site =======================================================
Reading from: '/Users/mark/Documents/GitHub/FISH549/pets'
Writing to: '/Users/mark/Documents/GitHub/FISH549/pets/docs'
-- Initialising site -----------------------------------------------------------
-- Building favicons -----------------------------------------------------------
Building favicons with realfavicongenerator.net...
Copying 'pkgdown/favicon/apple-touch-icon-120x120.png' to 'apple-touch-icon-120x120.png'
Copying 'pkgdown/favicon/apple-touch-icon-152x152.png' to 'apple-touch-icon-152x152.png'
Copying 'pkgdown/favicon/apple-touch-icon-180x180.png' to 'apple-touch-icon-180x180.png'
Copying 'pkgdown/favicon/apple-touch-icon-60x60.png' to 'apple-touch-icon-60x60.png'
Copying 'pkgdown/favicon/apple-touch-icon-76x76.png' to 'apple-touch-icon-76x76.png'
Copying 'pkgdown/favicon/apple-touch-icon.png' to 'apple-touch-icon.png'
Copying 'pkgdown/favicon/favicon-16x16.png' to 'favicon-16x16.png'
Copying 'pkgdown/favicon/favicon-32x32.png' to 'favicon-32x32.png'
Copying 'pkgdown/favicon/favicon.ico' to 'favicon.ico'
Copying 'logo.png' to 'logo.png'
-- Building home ---------------------------------------------------------------
Writing 'authors.html'
Reading 'LICENSE.md'
Writing 'LICENSE.html'
Writing 'LICENSE-text.html'
Copying 'man/figures/logo.png' to 'reference/figures/logo.png'
Writing '404.html'
-- Building function reference -------------------------------------------------
Writing 'reference/index.html'
Reading 'man/cats.Rd'
Writing 'reference/cats.html'
-- Building articles -----------------------------------------------------------
Writing 'articles/index.html'
Reading 'vignettes/example-usage.Rmd'
Writing 'articles/example-usage.html'
Writing 'sitemap.xml'
-- Building search index -------------------------------------------------------
== DONE ========================================================================
-- Previewing site ---------------------------------------------------------------------
When it finishes, go ahead and commit your changes and push them to
GitHub.
Finally, navigate back to your pets
repo on
GitHub and preview your changes.
LS0tCnRpdGxlOiAiQ3JlYXRpbmcgUiBwYWNrYWdlcyIKc3VidGl0bGU6ICJQYXJ0IDIiCmRhdGU6ICI8YnI+MjEgRmVicnVhcnkgMjAyNSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogc3BhY2VsYWIKICAgIGhpZ2hsaWdodDogdGV4dG1hdGUKICAgIGNzczogLi4vbGVjdHVyZV9pbnN0LmNzcwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwotLS0KCmBgYHtyIHNldF9kZWZhdWx0cywgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBldmFsID0gRkFMU0UpCmBgYAoKKioqCgojIEJhY2tncm91bmQKCldlIHNhdyBbbGFzdCB0aW1lXShsZWNfMjNfcGFja2FnZXNfMS5odG1sKSBob3cgdG8gY3JlYXRlIGEgYmFzaWMgcGFja2FnZSBmcm9tIGEgc2ltcGxlIGZ1bmN0aW9uLCBkb2N1bWVudCBpdHMgY29udGVudHMsIGFuZCBleHBsb3JlIGl0cyBmdW5jdGlvbmFsaXR5LiBJbiB0aGlzIGV4ZXJjaXNlLCB3ZSdsbCBsZWFybiBob3cgdG8gY3JlYXRlIGZvcm1hbCB0ZXN0cyBmb3Igb3VyIGZ1bmN0aW9ucywgd3JpdGUgdmlnbmV0dGVzLCBhbmQgcHVibGlzaCBvdXIgcGFja2FnZSB0byB0aGUgd2ViLgoKKioqCgojIFRlc3RpbmcKCldlIHNwZW50IGp1c3QgYSBiaXQgb2YgdGltZSB0ZXN0aW5nIG91dCBvdXIgYGNhdHMoKWAgZnVuY3Rpb24sIGJ1dCB3ZSBjYW4gZm9ybWFsaXplIHRoaXMgcHJvY2VzcyB3aXRoIHNvLWNhbGxlZCAqdW5pdCB0ZXN0cyouIFRoaXMgYmFzaWNhbGx5IG1lYW5zIHdlJ2xsIGVzdGFibGlzaCBzb21lIGV4cGVjdGF0aW9ucyBhYm91dCB3aGF0IG91ciBmdW5jdGlvbiBzaG91bGQgcmV0dXJuIHdoZW4gcGFzc2VkIGNlcnRhaW4gYXJndW1lbnRzLiBBcyBbV2lja2hhbSAmIEJyeWFuXShodHRwczovL3ItcGtncy5vcmcvdGVzdHMuaHRtbCkgcG9pbnQgb3V0LCB0aGVyZSBhcmUgZm91ciBhZHZhbnRhZ2VzIHRvIHdvcmtpbmcgd2l0aCB1bml0IHRlc3RzOgoKMSkgRmV3ZXIgYnVncyBhbmQgZXJyb3JzOwoKMikgTW9yZSByaWdvcm91cyBjb2RlIHN0cnVjdHVyZTsKCjMpIEVhc2llciBzdGFydC9zdG9wIHBvaW50czsKCjQpIENvZGUgdGhhdCdzIHJvYnVzdCB0byBjaGFuZ2VzLgoKRm9ydHVuYXRlbHksIHRoZSBge2RldnRvb2xzfWAgcGFja2FnZSBoZWxwcyB1cyB3aXRoIHRoaXMgcHJvY2VzcyB2aWEgc29tZSBidWlsdC1pbiBmdW5jdGlvbnMuIFRvIGJlZ2luLCB3ZSdsbCB1c2UgYHVzZV90ZXN0dGhhdCgpYCB0byBzZXQgdXAgb3VyIGZyYW1ld29yayBmb3IgdGVzdGluZywgd2hpY2ggd2lsbAoKKiBhZGQgYFN1Z2dlc3RzOiB0ZXN0dGhhdGAgdG8gYERFU0NSSVBUSU9OYDsKCiogY3JlYXRlIHRoZSBkaXJlY3RvcnkgYHRlc3RzL3Rlc3R0aGF0L2A7IGFuZAoKKiBhZGQgdGhlIHNjcmlwdCBgdGVzdHMvdGVzdHRoYXQuUmAuCgo6OjogdGFzawoKTG9hZCB0aGUgYHtkZXZ0b29sc31gIHBhY2thZ2UuCgo6OjoKCmBgYHtyIGxvYWRfZGV2dG9vbHN9Cj4gbGlicmFyeShkZXZ0b29scykKTG9hZGluZyByZXF1aXJlZCBwYWNrYWdlOiB1c2V0aGlzCmBgYAoKOjo6IHRhc2sKClNldCB0aGUgdGVzdGluZyBmcmFtZXdvcmsgd2l0aCBgdXNlX3Rlc3R0aGF0KClgLgoKOjo6CgpgYGB7ciB1c2VfdGVzdHRoYXR9CiMjIHNldCB0ZXN0aW5nIGZyYW1ld29yawo+IHVzZV90ZXN0dGhhdCgpCuKclCBTZXR0aW5nIGFjdGl2ZSBwcm9qZWN0IHRvICcvVXNlcnMvbWFyay9Eb2N1bWVudHMvR2l0SHViL0ZJU0g1NDkvcGV0cycK4pyUIEFkZGluZyAndGVzdHRoYXQnIHRvIFN1Z2dlc3RzIGZpZWxkIGluIERFU0NSSVBUSU9OCuKclCBTZXR0aW5nIENvbmZpZy90ZXN0dGhhdC9lZGl0aW9uIGZpZWxkIGluIERFU0NSSVBUSU9OIHRvICczJwrinJQgQ3JlYXRpbmcgJ3Rlc3RzL3Rlc3R0aGF0LycK4pyUIFdyaXRpbmcgJ3Rlc3RzL3Rlc3R0aGF0LlInCuKAoiBDYWxsIGB1c2VfdGVzdCgpYCB0byBpbml0aWFsaXplIGEgYmFzaWMgdGVzdCBmaWxlIGFuZCBvcGVuIGl0IGZvciBlZGl0aW5nCmBgYAoKIyMgV3JpdGUgYSB1bml0IHRlc3QKCk5vdyB0aGF0IHdlIGhhdmUgb3VyIHRlc3RpbmcgZnJhbWV3b3JrLCB3ZSBuZWVkIHRvIHdyaXRlIHNvbWUgYWN0dWFsIHRlc3Qgc2NyaXB0cy4gSGVyZSBhZ2Fpbiwgd2Ugc2VlIGEgc3VnZ2VzdGlvbiBsaWtlIFtsYXN0IHRpbWVdKGh0dHBzOi8vZmlzaDU0OS5naXRodWIuaW8vd2Vic2l0ZS9sZWN0dXJlcy93ZWVrXzA4L2xlY18yM19wYWNrYWdlc18xLmh0bWwjV3JpdGVfYV9mdW5jdGlvbikgdG8gdXNlIGB1c2VfdGVzdCgpYCB0byBpbml0aWFsaXplIGEgYmFzaWMgdGVzdCBmaWxlLgoKOjo6IHRhc2sKClVzZSB0aGUgYHVzZV90ZXN0KClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhbiBlbXB0eSBzY3JpcHQgZm9yIHRlc3Rpbmcgb3VyIGBjYXRzKClgIGZ1bmN0aW9uLgoKOjo6CgpgYGB7ciB1c2VfdGVzdH0KPiB1c2VfdGVzdCgiY2F0cyIpCuKckyBXcml0aW5nICd0ZXN0cy90ZXN0dGhhdC90ZXN0LWNhdHMuUicK4pePIE1vZGlmeSAndGVzdHMvdGVzdHRoYXQvdGVzdC1jYXRzLlInCmBgYAoKOjo6IHRpcAoKWW91IHNob3VsZCBub3cgc2VlIGEgc2NyaXB0IGNhbGxlZCBgdGVzdC1jYXRzLlJgIHdpdGggdGhlIGZvbGxvd2luZyBleGFtcGxlIHVuaXQgdGVzdCBpbiBpdC4KCjo6OgoKYGBge3IgdGVzdF90aGF0X2RlZmF1bHR9CnRlc3RfdGhhdCgibXVsdGlwbGljYXRpb24gd29ya3MiLCB7CiAgZXhwZWN0X2VxdWFsKDIgKiAyLCA0KQp9KQpgYGAKClVuaXQgdGVzdHMgYXJlIGhpZXJhcmNoaWNhbCBpbiBuYXR1cmUuIFNvLWNhbGxlZCAqZXhwZWN0YXRpb25zKiBhcmUgZ3JvdXBlZCBpbnRvICp0ZXN0cyosIHdoaWNoIGFyZSB0aGVuIG9yZ2FuaXplZCBpbnRvICpmaWxlcyouIEV4cGVjdGF0aW9ucyBhcmUgaW5mb3JtYWwgYW5kIGludGVuZGVkIHNvbGVseSBmb3IgaHVtYW4gZXllcy0tLXRoZXkgd2lsbCBuZXZlciBiZSByZWFkIG9yIGNoZWNrZWQgYnkgYW4gKipSKiogZnVuY3Rpb24uIFRoZSB0ZXN0IGl0c2VsZiBpcyBlbWJlZGRlZCB3aXRoaW4gYHRlc3RfdGhhdCgpYCwgYW5kIGlzIGJhc2VkIHVwb24gdGhlIGV4cGVjdGVkIHJlc3VsdCBvZiBhbiBvcGVyYXRpb24gdGhhdCB3ZSBjaGVjayB3aXRoIGFuIGBleHBlY3RfYCBmdW5jdGlvbi4gSW4gdGhlIGV4YW1wbGUgdGVzdCBzaG93biBhYm92ZSwgdGhlIGV4cGVjdGF0aW9uIGlzIHRoYXQgYCJtdWx0aXBsaWNhdGlvbiB3b3JrcyJgLiBUaGUgc3BlY2lmaWMgdGVzdCBvZiB0aGUgZXhwZWN0YXRpb24gaXMgd2hldGhlciBvciBub3QgYDIgKiAyID09IDRgLCB3aGljaCBpcyBjb2RlZCBhcyBgZXhwZWN0X2VxdWFsKDIgKiAyLCA0KWAuIAoKOjo6IHRpcAoKWW91IGNhbiBmaW5kIGEgd2hvbGUgYmF0dGVyeSBvZiBge3Rlc3R0aGF0fWAgZnVuY3Rpb25zIFtoZXJlXShodHRwczovL3Rlc3R0aGF0LnItbGliLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCkuCgo6OjoKCkxldCdzIGdvIGFoZWFkIGFuZCB3cml0ZSBzb21lIHVuaXQgdGVzdHMgZm9yIGBjYXRzKClgLgoKOjo6IHRhc2sKCkRlbGV0ZSB0aGUgZGVmYXVsdCB1bml0IHRlc3QgaW4gYHRlc3QtY2F0cy5SYCBhbmQgcmVwbGFjZSBpdCB3aXRoIHRoZSBmb2xsb3dpbmcgY29kZS4KCjo6OgoKYGBge3IgdW5pdF90ZXN0XzF9CnRlc3RfdGhhdCgibG9naWNhbCBvcGVyYXRvcnMgd29yayIsIHsKICAjIyBpZiBUUlVFCiAgZXhwZWN0X2VxdWFsKGNhdHMoVFJVRSksICJJIGxvdmUgY2F0cyEiKQogICMjIGlmIGFsaWFzIGZvciBUUlVFCiAgZXhwZWN0X2VxdWFsKGNhdHMoMSksICJJIGxvdmUgY2F0cyEiKQogICMjIGlmIEZBTFNFCiAgZXhwZWN0X2VxdWFsKGNhdHMoRkFMU0UpLCAiSSBhbSBub3QgYSBjYXQgcGVyc29uLiIpCiAgIyMgaWYgYWxpYXMgZm9yIEZBTFNFCiAgZXhwZWN0X2VxdWFsKGNhdHMoMCksICJJIGFtIG5vdCBhIGNhdCBwZXJzb24uIikKfSkKYGBgCgo6OjogdGFzawoKU2F2ZSB5b3VyIGZpbGUgd2hlbiB5b3UncmUgZG9uZSBlbnRlcmluZyB0aGUgdGVzdHMuCgo6OjoKCiMjIFJ1biB1bml0IHRlc3RzCgpOb3cgd2UncmUgc2V0IHRvIHJ1biB0aGVzZSB0ZXN0cyB3aXRoIHRoZSBgdGVzdCgpYCBmdW5jdGlvbi4KCjo6OiB0YXNrCgpDYWxsIGB0ZXN0KClgIGFuZCBub3RlIHRoZSBvdXRwdXQsIHdoaWNoIG1heSBkaWZmZXIgc29tZXdoYXQgZnJvbSBiZWxvdy4KCjo6OgoKYGBge3IgcnVuX3Rlc3R9Cj4gdGVzdCgpCuKEuSBUZXN0aW5nIHBldHMKCkF0dGFjaGluZyBwYWNrYWdlOiDigJh0ZXN0dGhhdOKAmQoKVGhlIGZvbGxvd2luZyBvYmplY3QgaXMgbWFza2VkIGZyb20g4oCYcGFja2FnZTpkZXZ0b29sc+KAmToKCiAgICB0ZXN0X2ZpbGUKCuKclCB8IEYgVyBTICBPSyB8IENvbnRleHQK4qCPIHwgICAgICAgICAwIHwgY2F0cyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIApbMV0gIkkgbG92ZSBjYXRzISIKWzFdICJJIGxvdmUgY2F0cyEiClsxXSAiSSBhbSBub3QgYSBjYXQgcGVyc29uLiIKWzFdICJJIGFtIG5vdCBhIGNhdCBwZXJzb24uIgrinJQgfCAgICAgICAgIDQgfCBjYXRzCgrilZDilZAgUmVzdWx0cyDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZDilZAKWyBGQUlMIDAgfCBXQVJOIDAgfCBTS0lQIDAgfCBQQVNTIDQgXQpgYGAKCjxicj4KCldoYXQgZG9lcyB0aGlzIGFsbCBtZWFuPwoKVGhlIHJlcG9ydCBpbmRpY2F0ZXMgaG93IG1hbnkgdG90YWwgdGVzdHMgd2VyZSBydW4gKDQpIGFuZCB0aGUgb3V0cHV0IG9mIGVhY2ggdGVzdC4gVGhlIGBSZXN1bHRzYCBzZWN0aW9uIGF0IHRoZSBib3R0b20gc2hvd3MgaG93IG1hbnkgb2YgdGhlIHRlc3RzIGZhaWxlZCAoYEZBSUxgKSwgaXNzdWVkIGEgd2FybmluZyAoYFdBUk5gKSwgd2VyZSBza2lwcGVkIChgU0tJUGApLCBhbmQgcGFzc2VkIChgUEFTU2ApLgoKOjo6IHN1Y2Nlc3MKCkFsbCBmb3VyIG9mIG91ciB0ZXN0cyBwYXNzZWQhCgo6OjoKCgo6OjogdGlwCgpOb3cgaXMgYSBnb29kIHRpbWUgdG8gY29tbWl0IHlvdXIgY2hhbmdlcyBhbmQgcHVzaCB0aGVtIHRvICoqR2l0SHViKiouCgo6OjoKCioqKgoKIyBQYWNrYWdlcyBvbiBHaXRIdWIKClRoZSBgcGV0c2AgcmVwbyB3ZSBjcmVhdGVkIG9uICoqR2l0SHViKiogY29udGFpbnMgb3VyIHBhY2thZ2Ugc2tlbGV0b24gaW4gaHVtYW4tcmVhZGFibGUgZm9ybS4gT25lIG9mIHRoZSBuZWF0IHRoaW5ncyBhYm91dCB0aGlzIGlzIHRoYXQgeW91IChvciBhbnlvbmUgZWxzZSkgY2FuIG5vdyBpbnN0YWxsIHRoZSBwYWNrYWdlIGRpcmVjdGx5IGZyb20gKipHaXRIdWIqKi4KCjo6OiB0aXAKCllvdSBjYW4gdXNlIGVpdGhlciBvZiB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uIGNhbGxzIHRvIGluc3RhbGwgYSBwYWNrYWdlIGRpZWN0bHkgZnJvbSBHaXRIdWIuIFRvIGRvIHNvLCByZXBsYWNlIGBHSVRIVUJfVVNFUk5BTUVgIHdpdGggdGhlIHVzZXJuYW1lIG9mIHRoZSByZXBvJ3Mgb3duZXIgYW5kIGBQQUNLQUdFYCB3aXRoIHRoZSBwYWNrYWdlJ3MgbmFtZS4KCjo6OgoKYGBge3IgaW5zdGFsbF9naH0KIyMgb3JpZ2luYWwgY2FsbCB1c2luZyB7ZGV2dG9vbHN9IHN0aWxsIHdvcmtzCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiR0lUSFVCX1VTRVJOQU1FL1BBQ0tBR0UiKQoKIyMgYnV0IHtyZW1vdGVzfSBpcyB0aGUgbmV3ZXIgcGFja2FnZSB3aXRoIHRoZSBmdW5jdGlvbgpyZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiR0lUSFVCX1VTRVJOQU1FL1BBQ0tBR0UiKQpgYGAKCjo6OiB0aXAKCklmIHlvdSB0cnkgZWl0aGVyIG9mIHRoZXNlIGNvbW1hbmRzIGFuZCBnZXQgdGhlIGZvbGxvd2luZyBlcnJvciwgcnVuIGBTeXMudW5zZXRlbnYoIkdJVEhVQl9QQVQiKWAgYW5kIHRoZW4gdHJ5IGFnYWluLiAKCjo6OgoKYGBgClVzaW5nIGdpdGh1YiBQQVQgZnJvbSBlbnZ2YXIgR0lUSFVCX1BBVApFcnJvcjogRmFpbGVkIHRvIGluc3RhbGwgJ3BldHMnIGZyb20gR2l0SHViOgogIEhUVFAgZXJyb3IgNDAxLgogIEJhZCBjcmVkZW50aWFscwpgYGAKCiMjIEVkaXRpbmcgdGhlIGBSRUFETUUubWRgCgpCZWNhdXNlIHlvdXIgcGFja2FnZSBub3cgbGl2ZXMgaW4gYSBwdWJsaWMgc3BhY2Ugb24gR2l0SHViIHdoZXJlIG90aGVycyBjYW4gZmluZCBhbmQgdXNlIGl0LCB5b3Ugc2hvdWxkIGluY2x1ZGUgc29tZSBoZWxwZnVsIGluZm9ybWF0aW9uIGluIHRoZSBgUkVBRE1FLm1kYCBmaWxlIHRvIGRlc2NyaWJlIHRoZSBwYWNrYWdlIGNvbnRlbnRzIGFuZCBob3cgaXQgd29ya3MuCgo6OjogdGFzawoKVXNlIHRoZSBgdXNlX3JlYWRtZV9ybWQoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIGEgKipNYXJrZG93bioqIGZpbGUgd2l0aCBhIHNrZWxldG9uIHRvIGFkZCBpbmZvcm1hdGlvbiBhYm91dCB5b3VyIHBhY2thZ2UuCgo6OjoKCmBgYHtyIHVzZV9yZWFkbWV9Cj4gdXNlX3JlYWRtZV9ybWQoKQrinJMgV3JpdGluZyAnUkVBRE1FLlJtZCcK4pyTIEFkZGluZyAnXlJFQURNRVxcLlJtZCQnIHRvICcuUmJ1aWxkaWdub3JlJwril48gTW9kaWZ5ICdSRUFETUUuUm1kJwrinJMgV3JpdGluZyAnLmdpdC9ob29rcy9wcmUtY29tbWl0JwpgYGAKClRoZSBvdXRwdXQgYWJvdmUgaW5kaWNhdGVzIHRoYXQgdGhlIGZ1bmN0aW9uCgoqIGNyZWF0ZWQgYFJFQURNRS5SbWRgCgoqIGFkZGVkIHNvbWUgbGluZXMgdG8gYC5SYnVpbGRpZ25vcmVgCgoqIGNyZWF0ZWQgYSAqKkdpdCoqICpwcmUtY29tbWl0IGhvb2sqIHRvIGtlZXAgYFJFQURNRS5SbWRgIGFuZCBgUkVBRE1FLm1kYCBzeW5jZWQgd2l0aCBvbmUgYW5vdGhlcgoKOjo6IHRhc2sKCk9wZW4gdXAgYFJFQURNRS5SbWRgIGFuZCBpbnNwZWN0IGl0cyBjb250ZW50cy4gCgo6OjoKCkF0IHRoZSB0b3Agb2YgYFJFQURNRS5SbWRgIHlvdSdsbCBzZWUgYSB2ZXJ5IHNpbXBseSBZQU1MIHNlY3Rpb24gd2l0aCBvbmx5IG9uZSBlbnRyeSBmb3IgdGhlIGBvdXRwdXRgIGZvcm1hdC4KCmBgYAotLS0Kb3V0cHV0OiBnaXRodWJfZG9jdW1lbnQKLS0tCmBgYAoKQmVsb3cgdGhhdCB5b3Ugd2lsbCBzZWUgYSBudW1iZXIgb2Ygc2VjdGlvbnMgd2l0aCBwcm9tcHRzIGFza2luZyBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgcGFja2FnZSwgaW5jbHVkaW5nIGEgZGVzY3JpcHRpb24gb2YgdGhlIHBhY2thZ2UsIGhvdyB0byBpbnN0YWxsIGl0LCBhbmQgZXhhbXBsZSBvZiBpdHMgdXNhZ2UuIAoKOjo6IHRpcAoKQ29tbWVudHMgaW4gKipNYXJrZG93bioqIGFyZSBkZW5vdGVkIGJ5IGA8IS0tIHNvbWUgY29tbWVudCBoZXJlIC0tPmAuCgo6OjoKCjo6OiB0YXNrCgpJbiB0aGUgZmlyc3Qgc2VjdGlvbiBgIyBwZXRzYCwgZ28gYWhlYWQgYW5kIGVudGVyIHNvbWUgZGVzY3JpcHRpdmUgdGV4dCBhYm91dCB0aGUgcGFja2FnZSBjb250ZW50cyAoeW91IGNhbiBkZWxldGUgdGhlIGNvbW1lbnRzIGFib3V0IGJhZGdlcykuCgo6OjoKCmBgYAojIHBldHMKClRoZSBnb2FsIG9mIHBldHMgaXMgdG8gcHJvdmlkZSBhIHNpbXBsZSBtZWFucyBmb3IgcGVvcGxlIHRvIGV4cHJlc3MgdGhlaXIgZmVlbGluZ3MgYWJvdXQgcGV0cy4gQXQgcHJlc2VudCwgdGhlIHBhY2thZ2Ugb25seSBjb250YWlucyBvbmUgZnVuY3Rpb246IGBjYXRzKClgLgpgYGAKCjo6OiB0YXNrCgpJbiB0aGUgbmV4dCBzZWN0aW9uIG9uIGAjIyBJbnN0YWxsYXRpb25gLCBhZGQgc29tZSBpbnN0YWxsIGluc3RydWN0aW9ucyBsaWtlIHRob3NlIHNob3duIGFib3ZlLgoKOjo6CgpgYGBgCiMjIEluc3RhbGxhdGlvbgoKWW91IGNhbiBpbnN0YWxsIHRoZSBkZXZlbG9wbWVudCB2ZXJzaW9uIG9mIHBldHMgbGlrZSBzbzoKCmBgYCByCmlmKCFyZXF1aXJlKCJkZXZ0b29scyIpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQp9CmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRklTSDU0OS9wZXRzIikKYGBgCmBgYGAKCjo6OiB0YXNrCgpJbiB0aGUgYCMjIEV4YW1wbGVgIHNlY3Rpb24sIGZpbGwgaW4gc29tZSBpbmZvcm1hdGlvbiB3aXRoIGV4YW1wbGUgY2FsbHMgdG8gYGNhdHMoKWAuCgo6OjoKCmBgYGAKIyMgRXhhbXBsZQoKSGVyZSBhcmUgdHdvIHNpbXBsZSBleGFtcGxlcyB0aGF0IGFsbG93IHlvdSB0byBleHByZXNzIHlvdXIgZmVlbGluZ3MgYWJvdXQgY2F0cy4KCmByICcnYGBgYHtyIGV4YW1wbGV9CmxpYnJhcnkocGV0cykKCiMjIGlmIHlvdSBsaWtlIGNhdHMKY2F0cyhUUlVFKQoKIyMgaWYgeW91IGRvbid0IGxpa2UgY2F0cwpjYXRzKEZBTFNFKQpgYGAKYGBgYAoKOjo6IHRhc2sKCldoZW4geW91IGFyZSBmaW5pc2hlZCBlZGl0aW5nIGBSRUFETUUuUm1kYCwga25pdCBpdCB3aXRoIHRoZSAqKktuaXQqKiBidXR0b24gaW4gKipSU3R1ZGlvKiogb3IgdXNlIHRoZSBgYnVpbGRfcmVhZG1lKClgIGZ1bmN0aW9uIGF0IHRoZSBjb21tYW5kIGxpbmUuCgo6OjoKCmBgYHtyIGJ1aWxkXyBfcmVhZG1lfQo+IGJ1aWxkX3JlYWRtZSgpCkluc3RhbGxpbmcgcGV0cyBpbiB0ZW1wb3JhcnkgbGlicmFyeQpCdWlsZGluZyAvVXNlcnMvbWFyay9Eb2N1bWVudHMvR2l0SHViL3BldHMvUkVBRE1FLlJtZApgYGAKCjo6OiB0YXNrCgpOb3cgY29tbWl0ICoqYm90aCoqIGBSRUFETUUuUm1kYCBhbmQgYFJFQURNRS5tZGAgYW5kIHRoZW4gcHVzaCB0aGVtIHRvICoqR2l0SHViKiouIFdoZW4geW91IGFyZSBmaW5pc2hlZCwgY2hlY2sgb3V0IHlvdXIgYHBldHNgIHJlcG8gb24gKipHaXRIdWIqKiB0byBzZWUgdGhlIG5ldyBjaGFuZ2VzIHRvIHlvdXIgYFJFQURNRS5tZGAuCgo6OjoKCgoqKioKCiMgVmlnbmV0dGVzCgpWaWduZXR0ZXMgb2ZmZXIgYSBsb25nZXIgZGVzY3JpcHRpb24gb2YgYSBwYWNrYWdlJ3MgdXRpbGl0eS4gVGhleSBhcmVuJ3QgYSBuZWNlc3NhcnkgY29tcG9uZW50IG9mIGEgcGFja2FnZSwgYnV0IHJhdGhlciBvbmUgb2YgdGhlICJiZWxscyBhbmQgd2hpc3RsZXMiLiBWaWduZXR0ZXMgYXJlIHdyaXR0ZW4gaW4gKipNYXJrZG93bioqIGFuZCBjYW4gYmUgYWNjZXNzZWQgdmlhIGB2aWduZXR0ZShuYW1lKWAgd2hlcmUgYG5hbWVgIGlzIHRoZSBuYW1lIG9mIHRoZSB2aWduZXR0ZS4KCjo6OiB0aXAKCllvdSBjYW4gc2VlIGEgbGlzdCBvZiBhbGwgb2YgdGhlIHZpZ25ldHRlcyB3aXRoaW4gYSBwYXJ0aWN1bGFyIHBhY2thZ2Ugd2l0aCBgYnJvd3NlVmlnbmV0dGVzKCJwYWNrYWdlbmFtZSIpYC4KCjo6OgoKOjo6IHRhc2sKClRvIGNyZWF0ZSBhIHZpZ25ldHRlLCB1c2UgdGhlIGB1c2VfdmlnbmV0dGUoKWAgZnVuY3Rpb24gYnkgcGFzc2luZyBpdCB0aGUgbmFtZSBvZiB0aGUgdmlnbmV0dGUgZmlsZSB0byBjcmVhdGUuIEhlcmUgdXNlIGAiZXhhbXBsZS11c2FnZSJgLgoKOjo6CgpgYGB7ciB2aWduZXR0ZX0KPiB1c2VfdmlnbmV0dGUoImV4YW1wbGUtdXNhZ2UiKQrinJQgQWRkaW5nICdrbml0cicgdG8gU3VnZ2VzdHMgZmllbGQgaW4gREVTQ1JJUFRJT04K4pyUIFNldHRpbmcgVmlnbmV0dGVCdWlsZGVyIGZpZWxkIGluIERFU0NSSVBUSU9OIHRvICdrbml0cicK4pyUIEFkZGluZyAnaW5zdC9kb2MnIHRvICcuZ2l0aWdub3JlJwrinJQgQ3JlYXRpbmcgJ3ZpZ25ldHRlcy8nCuKclCBBZGRpbmcgJyouaHRtbCcsICcqLlInIHRvICd2aWduZXR0ZXMvLmdpdGlnbm9yZScK4pyUIEFkZGluZyAncm1hcmtkb3duJyB0byBTdWdnZXN0cyBmaWVsZCBpbiBERVNDUklQVElPTgrinJQgV3JpdGluZyAndmlnbmV0dGVzL2V4YW1wbGUtdXNhZ2UuUm1kJwrigKIgTW9kaWZ5ICd2aWduZXR0ZXMvZXhhbXBsZS11c2FnZS5SbWQnCmBgYAoKVGhlIGNhbGwgdG8gYHVzZV92aWduZXR0ZSgpYCBkaWQgdGhlIGZvbGxvd2luZwoKKiBhZGRlZCB0aGUgbmVjZXNzYXJ5IGRlcGVuZGVuY2llcyB0byBgREVTQ1JJUFRJT05gCgoqIGFkZGVkIHNvbWUgZm9sZGVycyBhbmQgZmlsZXMgdG8gYC5naXRpZ25vcmVgCgoqIGNyZWF0ZWQgYSBgdmlnbmV0dGVzL2AgZGlyZWN0b3J5CgoqIGNyZWF0ZWQgdGhlIHNrZWxldG9uIGZpbGUgYHZpZ25ldHRlcy9leGFtcGxlLXVzYWdlLlJtZGAKCjo6OiB0YXNrCgpJbnNwZWN0IHRoZSBjb250ZW50cyBvZiBgZXhhbXBsZS11c2FnZS5SbWRgLiBBdCB0aGUgdG9wIHlvdSdsbCBzZWUgdGhlIGZvbGxvd2luZyBZQU1MIHNlY3Rpb24uCgo6OjoKCmBgYAotLS0KdGl0bGU6ICJleGFtcGxlLXVzYWdlIgpvdXRwdXQ6IHJtYXJrZG93bjo6aHRtbF92aWduZXR0ZQp2aWduZXR0ZTogPgogICVcVmlnbmV0dGVJbmRleEVudHJ5e2V4YW1wbGUtdXNhZ2V9CiAgJVxWaWduZXR0ZUVuZ2luZXtrbml0cjo6cm1hcmtkb3dufQogICVcVmlnbmV0dGVFbmNvZGluZ3tVVEYtOH0KLS0tCmBgYAoKOjo6IHRhc2sKCkNoYW5nZSB0aGUgdGl0bGUgdG8gYmUgYSBsaXR0bGUgbW9yZSBpbmZvcm1hdGl2ZS4KCjo6OgoKYGBgCnRpdGxlOiAiVXNpbmcgdGhlIHBldHMgcGFja2FnZSIKYGBgCgo6OjogdGFzawoKQ2hhbmdlIGAlXFZpZ25ldHRlSW5kZXhFbnRyeXtleGFtcGxlLXVzYWdlfWAgdG8gbWF0Y2ggb3VyIG5ldyB0aXRsZS4KCjo6OgoKYGBgCiVcVmlnbmV0dGVJbmRleEVudHJ5e1VzaW5nIHRoZSBwZXRzIHBhY2thZ2V9CmBgYAoKOjo6IHRhc2sKClNjcm9sbCBkb3duIGJlbG93IHRoZSBZQU1MIGFuZCBpbnNwZWN0IHRoZSBmaXJzdCBjb2RlIGNodW5rIHdoZXJlIHR3byBvcHRpb25zIGFyZSBiZWluZyBzZXQuIAoKOjo6CgpgYGB7ciwgZWNobyA9IFRSVUV9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBjb2xsYXBzZSA9IFRSVUUsCiAgY29tbWVudCA9ICIjPiIKKQpgYGAKCjo6OiB0aXAKClNldHRpbmcgYGNvbGxhcHNlID0gVFJVRWAgd2lsbCBpZiBwb3NzaWJsZSwgY29sbGFwc2UgYWxsIHRoZSBzb3VyY2UgY29kZSBhbmQgb3V0cHV0IGJsb2NrcyBmcm9tIG9uZSBjb2RlIGNodW5rIGludG8gYSBzaW5nbGUgYmxvY2sgKGJ5IGRlZmF1bHQsIHRoZXkgYXJlIHdyaXR0ZW4gdG8gc2VwYXJhdGUgYmxvY2tzKS4KCjo6OgoKOjo6IHRpcAoKVGhlIGBjb21tZW50ID0gIiM+YCBkZXRlcm1pbmVzIHRoZSBwcmVmaXggZm9yIG91dHB1dCBmcm9tIGZ1bmN0aW9uIGNhbGxzLiBZb3UgY2FuIGNoYW5nZSB0aGlzIHRvIGJlIGFueSBjaGFyYWN0ZXIgc3RyaW5nIG9yIGxlYXZlIGl0IGVtcHR5LgoKOjo6Cgo6OjogdGFzawoKQWRkIHNvbWUgbW9yZSBkZXRhaWxzIHRvIHRoZSBib2R5IG9mIHRoZSB2aWduZXR0ZS4KCjo6OgoKYGBgYAojIEJhY2tncm91bmQKClRoZSBge3BldHN9YCBwYWNrYWdlIHdhcyBkZXNpZ25lZCB0byBhbGxvdyBwZW9wbGUgdG8gZXhwcmVzcyB0aGVpciBmZWVsaW5ncyBhYm91dCBwZXRzLiBBdCBwcmVzZW50LCB0aGUgcGFja2FnZSBpcyByYXRoZXIgY2F0LWNlbnRyaWMsIGluIHRoYXQgaXQgb25seSBjb250YWlucyBvbmUgZnVuY3Rpb246IGBjYXRzKClgLgoKIyBVc2FnZQoKVG8gdXNlIGB7cGV0c31gLCBmaXJzdCBsb2FkIHRoZSBwYWNrYWdlIGFuZCB0aGVuIGNhbGwgdGhlIGZ1bmN0aW9uIGBjYXRzKClgIHdpdGggYSBsb2dpY2FsL2Jvb2xlYW4gYXJndW1lbnQuIEZvciBleGFtcGxlLCAKCmByICcnYGBgYHtyIHNldHVwfQojIyBsb2FkIHBldHMgcGFja2FnZQpsaWJyYXJ5KHBldHMpCgojIyBpZiB5b3UgbG92ZSBjYXRzCmNhdHMoVFJVRSkKCiMjIGlmIHlvdSdyZSBub3QgYSBiaWcgZmFuIG9mIGNhdHMKY2F0cyhGQUxTRSkKYGBgCmBgYGAKCjo6OiB0YXNrCgpXaGVuIHlvdSBhcmUgZmluaXNoZWQsIGdvIGFoZWFkIGFuZCBjbGljayBvbiB0aGUgKipLbml0KiogYnV0dG9uIGluICoqUlN0dWRpbyoqIHRvIHByZXZpZXcgdGhlIGh0bWwgdmVyc2lvbiBvZiB0aGUgdmlnbmV0dGUuCgo6OjoKCkF0IHRoaXMgcG9pbnQsIHdlJ3ZlIGNyZWF0ZWQgb3VyIHZpZ25ldHRlLCBidXQgd2Ugc3RpbGwgbmVlZCB0byBidWlsZCBpdCBpbnRvIHRoZSBwYWNrYWdlIGl0c2VsZi4KCjo6OiB0YXNrCgpVc2UgdGhlIGBidWlsZF92aWduZXR0ZXMoKWAgZnVuY3Rpb24gdG8gYnVpbGQgdGhlIHZpZ25ldHRlLgoKOjo6CgpgYGB7ciBidWlsZF92aWduZXR0ZX0KPiBidWlsZF92aWduZXR0ZXMoKQrihLkgSW5zdGFsbGluZyBwZXRzIGluIHRlbXBvcmFyeSBsaWJyYXJ5CuKEuSBCdWlsZGluZyB2aWduZXR0ZXMgZm9yIHBldHMKLS0tIHJlLWJ1aWxkaW5nIOKAmGV4YW1wbGUtdXNhZ2UuUm1k4oCZIHVzaW5nIHJtYXJrZG93bgoKCnByb2Nlc3NpbmcgZmlsZTogZXhhbXBsZS11c2FnZS5SbWQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCm91dHB1dCBmaWxlOiBleGFtcGxlLXVzYWdlLmtuaXQubWQKCi9BcHBsaWNhdGlvbnMvUlN0dWRpby5hcHAvQ29udGVudHMvTWFjT1MvcXVhcnRvL2Jpbi90b29scy9wYW5kb2MgK1JUUyAtSzUxMm0gLVJUUyBleGFtcGxlLXVzYWdlLmtuaXQubWQgLS10byBodG1sNCAtLWZyb20gbWFya2Rvd24rYXV0b2xpbmtfYmFyZV91cmlzK3RleF9tYXRoX3NpbmdsZV9iYWNrc2xhc2ggLS1vdXRwdXQgL1VzZXJzL21hcmsvRG9jdW1lbnRzL0dpdEh1Yi9GSVNINTQ5L3BldHMvdmlnbmV0dGVzL2V4YW1wbGUtdXNhZ2UuaHRtbCAtLWx1YS1maWx0ZXIgL1VzZXJzL21hcmsvUl9saWJzL3JtYXJrZG93bi9ybWFya2Rvd24vbHVhL3BhZ2VicmVhay5sdWEgLS1sdWEtZmlsdGVyIC9Vc2Vycy9tYXJrL1JfbGlicy9ybWFya2Rvd24vcm1hcmtkb3duL2x1YS9sYXRleC1kaXYubHVhIC0tZW1iZWQtcmVzb3VyY2VzIC0tc3RhbmRhbG9uZSAtLXNlY3Rpb24tZGl2cyAtLXRlbXBsYXRlIC9Vc2Vycy9tYXJrL1JfbGlicy9ybWFya2Rvd24vcm1kL2gvZGVmYXVsdC5odG1sIC0taGlnaGxpZ2h0LXN0eWxlIHB5Z21lbnRzIC0tY3NzIC9Vc2Vycy9tYXJrL1JfbGlicy9ybWFya2Rvd24vcm1hcmtkb3duL3RlbXBsYXRlcy9odG1sX3ZpZ25ldHRlL3Jlc291cmNlcy92aWduZXR0ZS5jc3MgLS1tYXRoamF4IC0tdmFyaWFibGUgJ21hdGhqYXgtdXJsPWh0dHBzOi8vbWF0aGpheC5yc3R1ZGlvLmNvbS9sYXRlc3QvTWF0aEpheC5qcz9jb25maWc9VGVYLUFNUy1NTUxfSFRNTG9yTU1MJyAtLWluY2x1ZGUtaW4taGVhZGVyIC92YXIvZm9sZGVycy93Ni9iZ3h4cXhsbjZueGY5ejB3cjdmeXB5ZncwMDAwZ24vVC8vUnRtcHBqdDFTZS9ybWFya2Rvd24tc3RyYTY2ZDc2ZTcyNy5odG1sIAoKT3V0cHV0IGNyZWF0ZWQ6IGV4YW1wbGUtdXNhZ2UuaHRtbAotLS0gZmluaXNoZWQgcmUtYnVpbGRpbmcg4oCYZXhhbXBsZS11c2FnZS5SbWTigJkKCuKEuSBDb3B5aW5nIHZpZ25ldHRlcwrihLkgTW92aW5nIGV4YW1wbGUtdXNhZ2UuaHRtbCBhbmQgZXhhbXBsZS11c2FnZS5SIHRvIGRvYy8K4oS5IENvcHlpbmcgZXhhbXBsZS11c2FnZS5SbWQgdG8gZG9jLwrihLkgQnVpbGRpbmcgdmlnbmV0dGUgaW5kZXgKYGBgCgo6OjogdGFzawoKUmVidWlsZCB5b3VyIHBhY2thZ2UuCgo6OjoKCmBgYHtyIHJlYnVpbGR9Cj4gYnVpbGQoKQrinJQgIGNoZWNraW5nIGZvciBmaWxlIOKAmC9Vc2Vycy9tYXJrL0RvY3VtZW50cy9HaXRIdWIvRklTSDU0OS9wZXRzL0RFU0NSSVBUSU9O4oCZIC4uLgrilIAgIHByZXBhcmluZyDigJhwZXRz4oCZOgrinJQgIGNoZWNraW5nIERFU0NSSVBUSU9OIG1ldGEtaW5mb3JtYXRpb24K4pSAICBpbnN0YWxsaW5nIHRoZSBwYWNrYWdlIHRvIGJ1aWxkIHZpZ25ldHRlcwrinJQgIGNyZWF0aW5nIHZpZ25ldHRlcyAoMS44cykK4pSAICBjaGVja2luZyBmb3IgTEYgbGluZS1lbmRpbmdzIGluIHNvdXJjZSBhbmQgbWFrZSBmaWxlcyBhbmQgc2hlbGwgc2NyaXB0cwrilIAgIGNoZWNraW5nIGZvciBlbXB0eSBvciB1bm5lZWRlZCBkaXJlY3RvcmllcwrilIAgIGJ1aWxkaW5nIOKAmHBldHNfMC4wLjAuOTAwMC50YXIuZ3rigJkKICAgClsxXSAiL1VzZXJzL21hcmsvRG9jdW1lbnRzL0dpdEh1Yi9GSVNINTQ5L3BldHNfMC4wLjAuOTAwMC50YXIuZ3oiCmBgYAoKOjo6IHRhc2sKClJlLXJ1biB5b3VyIHBhY2thZ2UgY2hlY2tzIChtb3N0IG9mIHRoZSBvdXRwdXQgYmVsb3cgaGFzIGJlZW4gc3VycHJlc3NlZCkuCgo6OjoKCmBgYHtyIHJ1bl9jaGVja3N9Cj4gY2hlY2soKQrilIDilIAgUiBDTUQgY2hlY2sgcmVzdWx0cyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAgcGV0cyAwLjAuMC45MDAwIOKUgOKUgOKUgOKUgApEdXJhdGlvbjogOC43cwoKMCBlcnJvcnMg4pyTIHwgMCB3YXJuaW5ncyDinJMgfCAwIG5vdGVzIOKckwpgYGAKCjo6OiBzdWNjZXNzCgpJdCBsb29rcyBsaWtlIGV2ZXJ5dGhpbmcgaXMgaW4gd29ya2luZyBvcmRlciEKCjo6OgoKOjo6IHRhc2sKCkdvIGFoZWFkIGFuZCBjb21taXQgeW91ciBjaGFuZ2VzIGFuZCBwdXNoIHRoZW0gdG8gKipHaXRIdWIqKi4KCjo6OgoKCioqKgoKIyBXZWJzaXRlCgpJZiB5b3UndmUgc3BlbnQgc29tZSB0aW1lIHNlYXJjaGluZyB0aGUgd2ViIGZvciBpbmZvcm1hdGlvbiBvbiBwYWNrYWdlcywgdGhlcmUncyBhIGdvb2QgY2hhbmNlIHlvdSd2ZSBjb21lIGFjcm9zcyBhIHNwZWNpZmljIGZvcm0gb2YgcGFja2FnZSB3ZWJzaXRlLiBGb3IgZXhhbXBsZSwgW2hlcmVdKGh0dHBzOi8vdXNldGhpcy5yLWxpYi5vcmcvKSBpcyB0aGUgd2VzYml0ZSBmb3IgdGhlIGB7dXNldGhpc31gIHBhY2thZ2UgdGhhdCB3ZSd2ZSB1c2VkIHRvIGJ1aWxkIG91ciBge3BldHN9YCBwYWNrYWdlLiBUaGVzZSB3ZWJzaXRlcyBhcmUgY3JlYXRlZCB3aXRoIHRoZSBbYHtwa2dkb3dufWBdKGh0dHBzOi8vcGtnZG93bi5yLWxpYi5vcmcvaW5kZXguaHRtbCkgcGFja2FnZSwgd2hpY2ggd2UgY2FuIHVzZSB0byBjcmVhdGUgYSB3ZWJzaXRlIGZvciBge3BldHN9YC4KCjo6OiB0YXNrCgpCZWdpbiBieSBsb2FkaW5nIHRoZSBge3BrZ2Rvd259YCBwYWNrYWdlLgoKOjo6CgpgYGB7ciBsb2FkX3BrZ2Rvd259Cj4gbGlicmFyeShwa2dkb3duKQoKQXR0YWNoaW5nIHBhY2thZ2U6IOKAmHBrZ2Rvd27igJkKClRoZSBmb2xsb3dpbmcgb2JqZWN0IGlzIG1hc2tlZCBmcm9tIOKAmHBhY2thZ2U6ZGV2dG9vbHPigJk6CgogICAgYnVpbGRfc2l0ZQpgYGAKCjo6OiB0YXNrCgpDb25maWd1cmUgeW91ciBwYWNrYWdlIHRvIHVzZSBge3BrZ2Rvd259YC4KCjo6OgoKYGBge3IgaW5pdF9wa2dkb3dufQo+IHVzZV9wa2dkb3duKCkK4pyUIEFkZGluZyAnXl9wa2dkb3duXFwueW1sJCcsICdeZG9jcyQnLCAnXnBrZ2Rvd24kJyB0byAnLlJidWlsZGlnbm9yZScK4pyUIEFkZGluZyAnZG9jcycgdG8gJy5naXRpZ25vcmUnCuKclCBXcml0aW5nICdfcGtnZG93bi55bWwnCuKAoiBNb2RpZnkgJ19wa2dkb3duLnltbCcKYGBgCgpgdXNlX3BrZ2Rvd24oKWAgd2lsbCBhZGQgYSBuZXcgZmlsZSBjYWxsZWQgYF9wa2dkb3duLnltbGAsIHdoaWNoIGNvbnRhaW5zIHNvbWUgWUFNTCBhbmQgZmllbGRzIGZvciBgdXJsYCBhbmQgYHRlbXBsYXRlYAoKYGBgCnVybDogfgp0ZW1wbGF0ZToKICBib290c3RyYXA6IDUKYGBgCgpOb3cgd2UgY2FuIHVzZSBgcGtnZG93bjo6YnVpbGRfc2l0ZSgpYCB0byBjcmVhdGUgdGhlIGFjdHVhbCB3ZWJzaXRlIGZvciB0aGUgcGFja2FnZS4gV2hlbiB5b3UgY2FsbCB0aGlzIGZ1bmN0aW9uLCB5b3UnbGwgc2VlIGEgdmVyYm9zZSByZXNwb25zZSBmcm9tICoqUioqIGZvbGxvd2VkIGJ5IGEgcHJldmlldyBvZiB5b3VyIHNpdGUgb3BlbmVkIGluIHlvdXIgd2ViIGJyb3dzZXIuCgo6Ojogbm90ZQoKQmVmb3JlIHByb2NlZWRpbmcsIHdlIG5lZWQgdG8gbWFrZSBhIHNtYWxsIGNoYW5nZSB0byBvdXIgYC5naXRpZ25vcmVgIGZpbGUuIFdoZW4gd2UgcnVuIGBidWlsZF9zaXRlKClgLCBpdCBjcmVhdGVzIHRoZSBuZWNlc3NhcnkgaHRtbCBhbmQgc3VwcG9ydGluZyBmaWxlcyBpbiB0aGUgYC9kb2NzYCBkaXJlY3RvcnksIHNvIHdlIG5lZWQgdG8gcmVtb3ZlIGl0IGZyb20gYC5naXRpZ25vcmVgIHNvIHRoYXQgd2UgY2FuIGNvbW1pdCBpdHMgY29udGVudHMgYW5kIHB1c2ggdGhlIGNoYW5nZXMgdG8gKipHaXRIdWIqKi4KCjo6OgoKOjo6IHRhc2sKCiogT3BlbiB5b3VyIGAuZ2l0aWdub3JlYCBmaWxlIGFuZCBsb29rIGZvciBgZG9jc2AgKE1hcmsncyBpcyBpbiBhIGJsb2NrIGNhbGxlZCBgIyBSIEVudmlyb25tZW50IFZhcmlhYmxlc2ApLgoKKiBDb21tZW50IGl0IG91dC4KCjo6OgoKYGBgCiMgUiBFbnZpcm9ubWVudCBWYXJpYWJsZXMKLlJlbnZpcm9uCi5ScHJvai51c2VyCmluc3QvZG9jCi9kb2MvCi9NZXRhLwojIGRvY3MKYGBgCgo6OjogdGFzawoKQ2FsbCBgYnVpbGRfc2l0ZSgpYCB0byBjcmVhdGUgdGhlIG5lY2Vzc2FyeSBodG1sIGFuZCBzdXBwb3J0aW5nIGZpbGVzIGluIHRoZSBgL2RvY3NgIGRpcmVjdG9yeS4KCjo6OgoKYGBge3IgYnVpbGRfc2l0ZX0KPiBidWlsZF9zaXRlKCkKLS0gSW5zdGFsbGluZyBwYWNrYWdlIGludG8gdGVtcG9yYXJ5IGxpYnJhcnkgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQo9PSBCdWlsZGluZyBwa2dkb3duIHNpdGUgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQpSZWFkaW5nIGZyb206ICcvVXNlcnMvbWFyay9Eb2N1bWVudHMvR2l0SHViL0ZJU0g1NDkvcGV0cycKV3JpdGluZyB0bzogICAnL1VzZXJzL21hcmsvRG9jdW1lbnRzL0dpdEh1Yi9GSVNINTQ5L3BldHMvZG9jcycKLS0gSW5pdGlhbGlzaW5nIHNpdGUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLS0gQnVpbGRpbmcgaG9tZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KUmVhZGluZyAnTElDRU5TRS5tZCcKV3JpdGluZyAnNDA0Lmh0bWwnCi0tIEJ1aWxkaW5nIGZ1bmN0aW9uIHJlZmVyZW5jZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tClJlYWRpbmcgJ21hbi9jYXRzLlJkJwpXcml0aW5nICdyZWZlcmVuY2UvY2F0cy5odG1sJwotLSBCdWlsZGluZyBhcnRpY2xlcyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpXcml0aW5nICdhcnRpY2xlcy9pbmRleC5odG1sJwpSZWFkaW5nICd2aWduZXR0ZXMvZXhhbXBsZS11c2FnZS5SbWQnCldyaXRpbmcgJ2FydGljbGVzL2V4YW1wbGUtdXNhZ2UuaHRtbCcKV3JpdGluZyAnc2l0ZW1hcC54bWwnCi0tIEJ1aWxkaW5nIHNlYXJjaCBpbmRleCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCj09IERPTkUgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Ci0tIFByZXZpZXdpbmcgc2l0ZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBgCgo6OjogdGFzawoKQ29tbWl0IHlvdXIgY2hhbmdlcyBhbmQgcHVzaCB0aGVtIHRvICoqR2l0SHViKiouCgo6OjoKCjo6OiB0YXNrCgoqIE5hdmlnYXRlIHRvIHlvdXIgYHBldHNgIHJlcG8gb24gKipHaXRIdWIqKiBhbmQgY2xpY2sgb24gdGhlICoqU2V0dGluZ3MqKiB0YWIuCgoqIFVuZGVyIHRoZSBzZWN0aW9uIG9uIHRoZSBsZWZ0IHRpdGxlZCAqKkNvZGUgYW5kIGF1dG9tYXRpb24qKiwgY2xpY2sgb24gKipQYWdlcyoqLgoKKiBVbmRlciAqKkJyYW5jaCoqIHNlbGVjdCBgbWFpbmAgYW5kIHNldCB0aGUgZm9sZGVyIHRvIGAvZG9jc2AuCgoqIENsaWNrICoqU2F2ZSoqIHdoZW4geW91J3JlIGRvbmUuCgo6OjoKCjxpbWcgc3JjPSJpbWcvZ2hfcGFnZXNfc2V0LnBuZyIgY2xhc3M9Im5vYm9yZGVyIj4KCjo6OiB0YXNrCgpOYXZpZ2F0ZSB0byBgaHR0cHM6Ly9VU0VSTkFNRS5naXRodWIuaW8vcGV0cy9gIHRvIHZpZXcgdGhlIHdlYnNpdGUgZm9yIHlvdXIgcGFja2FnZSAod2hlcmUgYFVTRVJOQU1FYCBpcyB5b3VyIEdpdEh1YiB1c2VyIG5hbWUpLgoKOjo6Cgo6OjogdGlwCgpJZiB5b3VnZXQgYSA0MDQgZXJyb3Igb3IgeW91ciBzaXRlIGRvZXNuJ3QgZGlzcGxheSBwcm9wZXJseSwgd2FpdCBhIGZldyBtaW51dGVzIGFuZCB0aGVuIGZvcmNlLXJlZnJlc2ggeW91ciBicm93c2VyLgoKOjo6CgoKKioqCgojIEhleCBzdGlja2VyCgpMZXQncyBiZSBob25lc3QtLS1baGV4IHN0aWNrZXJzXShodHRwczovL3N3YWcucnN0dWRpby5jb20vcy9zaG9wKSBhcmUgYWxsIHRoZSByYWdlIHdoZW4gaXQgY29tZXMgdG8gKipSKiogcGFja2FnZXMuIFdlIGNhbiBtYWtlIG91ciBvd24gaGV4IHN0aWNrZXIgZm9yIG91ciBge3BldHN9YCBwYWNrYWdlIHdpdGggdGhlIFtge2hleFN0aWNrZXJ9YF0oaHR0cHM6Ly9naXRodWIuY29tL0d1YW5nY2h1YW5nWXUvaGV4U3RpY2tlcikgcGFja2FnZS4gVGhlIGZ1bmN0aW9uIGBzdGlja2VyKClgIHdpbGwgZG8gYWxsIG9mIHRoZSB3b3JrIGZvciB1cy4KCkhlcmUgd2UnbGwgaW5jbHVkZSBhbiBpbWFnZSBvZiBhIHNpbGhvdWV0dGUgY2F0YSBhbmQgZG9nIG9uIG91ciBzdGlja2VyLgoKOjo6IHRhc2sKCiogRG93bmxvYWQgdGhlIGBjYXRfYW5kX2RvZy5wbmdgIGltYWdlIGJ5IG5hdmlnYXRpbmcgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9GSVNINTQ5L3dlYnNpdGUvYmxvYi9tYWluL2xlY3R1cmVzL3dlZWtfMDgvY2F0X2FuZF9kb2cucG5nKSBhbmQgcmlnaHQtY2xpY2tpbmcgb24gdGhlIGltYWdlLiAKCiogQ2hvb3NlICoqU2F2ZSBJbWFnZSBBcy4uLioqIGFuZCBzYXZlIGl0IGluIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBmb3IgeW91ciBge3BldHN9YCBwYWNrYWdlLgoKOjo6Cgo6OjogdGFzawoKTG9hZCBge2hleFN0aWNrZXJ9YCBhbmQgbWFrZSB0aGUgZm9sbG93aW5nIGNhbGwgdG8gYHN0aWNrZXIoKWAgKHlvdSBjYW4gdHlwZSBgP3N0aWNrZXJgIHRvIHNlZSBhbGwgb2YgdGhlIGZ1bmN0aW9uIGFyZ3VtZW50cykuCgo6OjoKCmBgYHtyfQo+IGxpYnJhcnkoaGV4U3RpY2tlcikKYGBgCgpgYGB7ciBtYWtlX3N0aWNrZXJ9CiMjIGNyZWF0ZSBzdGlja2VyIGltYWdlCnN0aWNrZXIoImNhdF9hbmRfZG9nLnBuZyIsCiAgICAgICAgIyMgcGFja2FnZSBuYW1lCiAgICAgICAgcGFja2FnZSA9ICJwZXRzIiwKICAgICAgICAjIyBoZWlnaHRzICYgd2lkdGhzCiAgICAgICAgcF9zaXplID0gMTgsIHNfeCA9IDAuOTUsIHNfeSA9IDAuOSwgYXNwID0gMC44NSwKICAgICAgICBzX3dpZHRoID0gMC42NSwgc19oZWlnaHQgPSAwLjY1LCBwX3kgPSAxLjYsCiAgICAgICAgIyMgdGV4dCBjb2xvcgogICAgICAgIHBfY29sb3IgPSAiIzRiMmU4MyIsCiAgICAgICAgIyMgZmlsbCBjb2xvcgogICAgICAgIGhfZmlsbCA9ICJ3aGl0ZSIsCiAgICAgICAgIyMgYm9yZGVyIGNvbG9yCiAgICAgICAgaF9jb2xvciA9ICIjODU3NTRkIiwKICAgICAgICAjIyBmaWxlbmFtZSB0byBzYXZlCiAgICAgICAgZmlsZW5hbWUgPSAibG9nby5wbmciKQpgYGAKCjo6OiB0YXNrCgpUZWxsICoqUioqIHRvIHVzZSBvdXIgbmV3IGltYWdlIHdpdGggYHVzZXRoaXM6OnVzZV9sb2dvKClgLgoKOjo6CgpgYGB7ciB1c2VfbG9nb30KPiB1c2V0aGlzOjp1c2VfbG9nbygibG9nby5wbmciKQrinJMgQ3JlYXRpbmcgJ21hbi9maWd1cmVzLycK4pyTIFJlc2l6ZWQgJ2xvZ28ucG5nJyB0byAyNDB4Mjc4CuKXjyBBZGQgbG9nbyB0byB5b3VyIFJFQURNRSB3aXRoIHRoZSBmb2xsb3dpbmcgaHRtbDoKV2FybmluZzogcGtnZG93biBjb25maWcgZG9lcyBub3Qgc3BlY2lmeSB0aGUgc2l0ZSdzIHVybCwgd2hpY2ggaXMgb3B0aW9uYWwgYnV0IHJlY29tbWVuZGVkCiAgIyBwZXRzIDxpbWcgc3JjPSdtYW4vZmlndXJlcy9sb2dvLnBuZycgYWxpZ249InJpZ2h0IiBoZWlnaHQ9IjEzOSIgLz4KICBbQ29waWVkIHRvIGNsaXBib2FyZF0KYGBgCgpUaGUgbGFzdCB0aGluZyB3ZSBuZWVkIHRvIGRvIGlzIGVkaXQgb3VyIGBSRUFETUUuUm1kYCBmaWxlIHRvIGluY2x1ZGUgYSByZWZlcmVuY2UgdG8gdGhlIHN0aWNrZXIuCgo6OjogdGFzawoKQWRkIHRoZSBmb2xsb3dpbmcgbGluZSBvZiBjb2RlIHRvIHRoZSByaWdodCBvZiB0aGUgYCMgcGV0c2AgaGVhZGluZy4KCjo6OgoKYGBgCiMgcGV0cyA8aW1nIHNyYz0ibWFuL2ZpZ3VyZXMvbG9nby5wbmciIGFsaWduPSJyaWdodCIgYWx0PSIiIHdpZHRoPSIxMjAiIC8+CmBgYAoKOjo6IHRhc2sKCldoZW4geW91IGFyZSBmaW5pc2hlZCwgY2xpY2sgdGhlICoqS25pdCoqIGJ1dHRvbiBpbiAqKlJTdHVkaW8qKiB0byBwcmV2aWV3IHlvdXIgbmV3IHJlYWRtZSBmaWxlLgoKOjo6Cgo6OjogdGFzawoKTGFzdGx5LCB3ZSBuZWVkIHRvIHJlYnVpbGQgdGhlIHdlYnNpdGUgd2l0aCBgYnVpbGRfc2l0ZSgpYC4KCjo6OgoKYGBge3J9Cj4gYnVpbGRfc2l0ZSgpCi0tIEluc3RhbGxpbmcgcGFja2FnZSBpbnRvIHRlbXBvcmFyeSBsaWJyYXJ5IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KPT0gQnVpbGRpbmcgcGtnZG93biBzaXRlID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KUmVhZGluZyBmcm9tOiAnL1VzZXJzL21hcmsvRG9jdW1lbnRzL0dpdEh1Yi9GSVNINTQ5L3BldHMnCldyaXRpbmcgdG86ICAgJy9Vc2Vycy9tYXJrL0RvY3VtZW50cy9HaXRIdWIvRklTSDU0OS9wZXRzL2RvY3MnCi0tIEluaXRpYWxpc2luZyBzaXRlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi0tIEJ1aWxkaW5nIGZhdmljb25zIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCkJ1aWxkaW5nIGZhdmljb25zIHdpdGggcmVhbGZhdmljb25nZW5lcmF0b3IubmV0Li4uCkNvcHlpbmcgJ3BrZ2Rvd24vZmF2aWNvbi9hcHBsZS10b3VjaC1pY29uLTEyMHgxMjAucG5nJyB0byAnYXBwbGUtdG91Y2gtaWNvbi0xMjB4MTIwLnBuZycKQ29weWluZyAncGtnZG93bi9mYXZpY29uL2FwcGxlLXRvdWNoLWljb24tMTUyeDE1Mi5wbmcnIHRvICdhcHBsZS10b3VjaC1pY29uLTE1MngxNTIucG5nJwpDb3B5aW5nICdwa2dkb3duL2Zhdmljb24vYXBwbGUtdG91Y2gtaWNvbi0xODB4MTgwLnBuZycgdG8gJ2FwcGxlLXRvdWNoLWljb24tMTgweDE4MC5wbmcnCkNvcHlpbmcgJ3BrZ2Rvd24vZmF2aWNvbi9hcHBsZS10b3VjaC1pY29uLTYweDYwLnBuZycgdG8gJ2FwcGxlLXRvdWNoLWljb24tNjB4NjAucG5nJwpDb3B5aW5nICdwa2dkb3duL2Zhdmljb24vYXBwbGUtdG91Y2gtaWNvbi03Nng3Ni5wbmcnIHRvICdhcHBsZS10b3VjaC1pY29uLTc2eDc2LnBuZycKQ29weWluZyAncGtnZG93bi9mYXZpY29uL2FwcGxlLXRvdWNoLWljb24ucG5nJyB0byAnYXBwbGUtdG91Y2gtaWNvbi5wbmcnCkNvcHlpbmcgJ3BrZ2Rvd24vZmF2aWNvbi9mYXZpY29uLTE2eDE2LnBuZycgdG8gJ2Zhdmljb24tMTZ4MTYucG5nJwpDb3B5aW5nICdwa2dkb3duL2Zhdmljb24vZmF2aWNvbi0zMngzMi5wbmcnIHRvICdmYXZpY29uLTMyeDMyLnBuZycKQ29weWluZyAncGtnZG93bi9mYXZpY29uL2Zhdmljb24uaWNvJyB0byAnZmF2aWNvbi5pY28nCkNvcHlpbmcgJ2xvZ28ucG5nJyB0byAnbG9nby5wbmcnCi0tIEJ1aWxkaW5nIGhvbWUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCldyaXRpbmcgJ2F1dGhvcnMuaHRtbCcKUmVhZGluZyAnTElDRU5TRS5tZCcKV3JpdGluZyAnTElDRU5TRS5odG1sJwpXcml0aW5nICdMSUNFTlNFLXRleHQuaHRtbCcKQ29weWluZyAnbWFuL2ZpZ3VyZXMvbG9nby5wbmcnIHRvICdyZWZlcmVuY2UvZmlndXJlcy9sb2dvLnBuZycKV3JpdGluZyAnNDA0Lmh0bWwnCi0tIEJ1aWxkaW5nIGZ1bmN0aW9uIHJlZmVyZW5jZSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCldyaXRpbmcgJ3JlZmVyZW5jZS9pbmRleC5odG1sJwpSZWFkaW5nICdtYW4vY2F0cy5SZCcKV3JpdGluZyAncmVmZXJlbmNlL2NhdHMuaHRtbCcKLS0gQnVpbGRpbmcgYXJ0aWNsZXMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KV3JpdGluZyAnYXJ0aWNsZXMvaW5kZXguaHRtbCcKUmVhZGluZyAndmlnbmV0dGVzL2V4YW1wbGUtdXNhZ2UuUm1kJwpXcml0aW5nICdhcnRpY2xlcy9leGFtcGxlLXVzYWdlLmh0bWwnCldyaXRpbmcgJ3NpdGVtYXAueG1sJwotLSBCdWlsZGluZyBzZWFyY2ggaW5kZXggLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQo9PSBET05FID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQotLSBQcmV2aWV3aW5nIHNpdGUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYAoKOjo6IHRhc2sKCldoZW4gaXQgZmluaXNoZXMsIGdvIGFoZWFkIGFuZCBjb21taXQgeW91ciBjaGFuZ2VzIGFuZCBwdXNoIHRoZW0gdG8gKipHaXRIdWIqKi4KCjo6OgoKOjo6IHRhc2sKCkZpbmFsbHksIG5hdmlnYXRlIGJhY2sgdG8geW91ciBgcGV0c2AgcmVwbyBvbiAqKkdpdEh1YioqIGFuZCBwcmV2aWV3IHlvdXIgY2hhbmdlcy4KCjo6Ogo=