“R
will always be arcane to those who do not make a
serious effort to learn it. It is not meant to be intuitive and easy for
casual users to just plunge into. It is far too complex and powerful for
that. But the rewards are great for serious data analysts who put in the
effort. ”
— Berton
Gunter R-help August 2007 (archived at https://perma.cc/KY9N-2FTT )
“Evelyn Hall: I would like to know how (if) I can extract some of the
information from the summary of my nlme.
Simon Blomberg: This is R
. There is no if. Only
how.”
— Evelyn
Hall and Simon ‘Yoda’ Blomberg, R-help April 2005 (archived at https://perma.cc/KY9N-2FTT )
Learning
R
Base R
Here are a slew of resources for learning base R
(in
addition to the documents in the lab’s Primer
Articles folder ):
Statistics (using
R
)
The following are resources for learning statistics using
R
.
tidyverse
The following are resources for learning tidyverse
,
which is a collection of R
packages for data
management:
Questions
If you have questions:
Initial Set Up
Note: many of these initial setup steps described below are not
necessary for general use; many of these steps are necessary only for
using lab-related repositories (e.g., to gain API access to export data
from REDCap
, to use absolute paths rather than relative
paths so repos can communicate with each other, etc.).
Make sure you are logged onto a computer that can access the lab
server (either a lab computer, or a computer you can VPN into the lab
server), and that you have admin access to install and uninstall
software
Install R
(https://www.r-project.org/ ) into a directory that
contains no spaces; On PC, change the location from the default
C:/Program Files/R/[R-VERSION]
(which contains a space) to
C:/R/[R-VERSION]
(which
does not contain any spaces ; archived at https://perma.cc/6VMX-3LYX---this is because some
packages that require compilation to install cannot read filepaths with
spaces ; archived at https://perma.cc/XA3V-JTPY ); may have to right click and
“Run As Administrator”
If R
was already installed in a directory that contains
spaces (e.g., C:/Program Files/R/[R-VERSION]
), uninstall
R
before installing it in a directory that doesn’t contain
spaces
Install RStudio
Desktop (https://www.rstudio.com/products/rstudio/download/ ) in
the main program files directory; may have to right click and “Run As
Administrator”. RStudio
is the best available graphical
user interface for R.
Set the executables for R
and RStudio
to
always run with administrator permissions.
If on Windows, open File Explorer and find the main executable of
R
(C:/R/[R-VERSION]/bin/R.exe
) and
RStudio
(C:\Program Files\RStudio\bin\RStudio.exe
). Right-click it
to open the contextual menu. Then, click or tap on “Properties”. In the
Properties window, go to the Compatibility tab. At the bottom of the
window, check the box next to the “Run this program as an administrator”
option, and then click or tap on Apply or OK.
Install tools to allow you to compile R
packages so you
can install packages from source, if necessary (i.e., if package
binaries are not available):
Set up git
, GitLab
, and the
GitHub Desktop
App in the main program files directory; may
have to right click and “Run As Administrator”; For instructions setting
up and using GitLab
, see here: https://devpsylab.github.io/DataAnalysis/git.html#toBegin
The Rprofile.site
file in the etc
folder
of the R
installation directory is the code that is run for
every user at the beginning each time you load
R. We will update the default Rprofile.site
file with the
lab’s Rprofile.site
file so R
installs
packages in the correct location, sets the default package repository,
updates packages, and gives you a fortune cookie. To do this, perform
the following steps:
Rename the Rprofile.site
file in the R
installation directory
(C:/R/R-[InsertVersionNumber]/etc/Rprofile.site
) to be
Rprofile_BACKUP.site
Download the lab’s Rprofile.site
file located in this
repository at the following location (https://research-git.uiowa.edu/PetersenLab/R-InitialSetup/-/blob/master/R%20Setup%20Files/Rprofile.site ),
and paste it into the R
installation directory (PC:
C:/R/R-[InsertVersionNumber]/etc/Rprofile.site
; Mac:
/Library/Frameworks/R.framework/Versions/[InsertVersionNumber]/Resources/etc/Rprofile.site
)
The .Rprofile
file in the user’s Documents
folder is the code that is run for the particular
user at the beginning each time you load R. We will update
the default .Rprofile
file (if there is one) with the lab’s
.Rprofile
file so R
knows which computer you
are using and which path to use (relative to where your R
projects are located). To do this, perform the following steps:
Download the lab’s .Rprofile
template file in this
repository at the following location, and make sure to remove anything
besides .Rprofile
in the filename: https://research-git.uiowa.edu/PetersenLab/R-InitialSetup/-/blob/master/R%20Setup%20Files/.Rprofile
Open the lab’s .Rprofile
file, and revise it with your
HawkID
Revise the lab’s .Rprofile
file with the local path to
the Documents
folder for each of the computers you will use
to access R
(e.g., home computer, work computer, laptop).
Make sure to use forward slashes (/
), not back slashes
(\
) in the path.
You will save the file in your HOME
directory. To find
the HOME
directory, open R
and type the
following command: Sys.getenv("HOME")
—the output of the
command is the location of your HOME
directory; If this is
a lab computer, it may be located here:
//home.iowa.uiowa.edu/[user]/Documents
. If this is your
personal computer, it may be located here: PC:
C:/Users/[user]/Documents
; Mac: /Users/[user]
.
Then close R.
If your HOME
directory is in a OneDrive folder (or
another cloud-based sync folder), you will want to change the directory
of your HOME
path so that it is not in a OneDrive folder.
To do that, open Environment Variables
(archived at https://perma.cc/A2E5-B5VA ) in Windows. Then, add/edit
HOME
as the “variable name” with the intended location as
the “variable value” (e.g., C:/Users/[user]/Documents
,
where you replace “user” with your HawkID).
You may also solve this issue by placing the following command in
the Rprofile.site
from the previous step
Sys.setenv("HOME" = "C:/Users/[specific user ID])/Documents")
Move the revised .Rprofile
file to the
HOME
directory and overwrite the original
.Rprofile
file (if it exists). You may have to show hidden
files in order to see the file (PC: see Windows Explorer settings; Mac:
Command+Shift+Dot).
Make sure to show filename extensions in your file explorer window,
and make sure the file is named .Rprofile
(not
.Rprofile.Rprofile
). Make sure there is a period at the
beginning of the filename.
Run RStudio
. If the Rprofile.site
and
.Rprofile
files are correctly set up, they should
pre-populate your path
location when you open R. If the
contents of the Global Environment
in RStudio
are empty, your Rprofile.site
and/or .Rprofile
files are not set up correctly.
If you get this error
(Error: could not find function "install.packages"
), run
the following line manually and then restart RStudio
after
the package finishes installing:
install.packages("fortunes")
For reproducibility purposes ,
prevent R
/RStudio
from saving your workspaces
automatically using the following steps:
With RStudio running, choose Tools → Global Options
from the menus.
In the Options dialog, change the value for
Save workspace to .RData on exit
to
Never
.
Click OK
.
Install the petersenlab
R
package using
the following steps:
Install the remotes
package using the following
command: install.packages("remotes")
Install the petersenlab
package using the following
command:
remotes::install_github("DevPsyLab/petersenlab")
Request an API
token for the following REDCap project(s); note: please check with
Dr. P before requesting an API token. In general, RAs should not have an
API token.
When your API token has been approved for these projects, open the
Encrypt REDCap Token.R
script: https://research-git.uiowa.edu/petersenlab/R-InitialSetup/blob/master/REDCap%20Credentials/Encrypt%20REDCap%20Token.R
Revise the API tokens to reflect yours, then run the script to save
your encrypted credentials on the lab server and your encryption key on
your local computer
Verify that the Encryption Key
(REDCap Encryption Key.RData
) was saved where you intended
it to be saved on your local computer
Verify that a file named with your HawkID was saved here:
//lc-rs-store24.hpc.uiowa.edu/lss_itpetersen/Lab/Studies/School Readiness Study/Data Management/REDCap/Tokens/
Copy the Encryption Key (REDCap Encryption Key.RData
)
to the comparable location of any other computers you own that you plan
to access the data from
The file has to be in the comparable location (relative to the
path
variable you set in Rprofile.site
) of
every computer in order for it to be found by the
Export Data.R
script. The default location is:
file.path(path, "GitHub/R/Data/REDCap Encryption Key.RData")
,
so if path
is set as "C:/User/YourName"
, the
file would be saved in:
C:/User/YourName/GitHub/R/Data/REDCap Encryption Key.RData
.
The recommended location for GitHub
repos is to create a
folder titled GitHub
in your Documents
folder,
and to put repos in the GitHub
folder; it is NOT
recommended to put git
repos in a OneDrive folder because
git
files tend not to play nice with syncing services (archived at https://perma.cc/XZ6F-43G3 ; e.g., OneDrive,
Dropbox)
Add the SRS Data Processing repo from the lab drive to your
GitHub Desktop
App
(//lc-rs-store24.hpc.uiowa.edu/lss_itpetersen/Lab/Studies/School Readiness Study/Data Processing
)
Open RStudio
by using “Run as Administrator” (always
open RStudio
as an administrator so it has write access to
the program files directory);
Open the Export Data.R
script in R: https://research-git.uiowa.edu/petersenlab/srs/SRS-DataProcessing/blob/master/1.%20Export%20Data/Export%20Data.R
\\lc-rs-store24.hpc.uiowa.edu\lss_itpetersen\Lab\Studies\School Readiness Study\Data Processing\1. Export Data\Export Data.R
Ensure your HawkID and location of your encryption key in the script
are correct, and then run the script to verify that you can export data
from REDCap
For antialiased plots in RStudio
, change the Graphics
backend to Cairo
:
Tools → Global Options → Graphics
Install Packages
To install and load R
packages, see the instructions here .
Update Packages
To update packages, use the following code:
update.packages(checkBuilt = TRUE)
One indication that the packages might not be updating to the latest
version is seeing the same packages showing as needing an update after
having run the update.packages()
function. If this does not
update the package(s) to the latest version, you may need to install the
latest version of the package(s) from source (see the section on “Initial Set Up ” of R
for the software
needed to install R packages from source):
update.packages(checkBuilt = TRUE, type = "source")
Update
R
Instructions adapted from: https://mirror.las.iastate.edu/CRAN/bin/windows/base/rw-FAQ.html#What_0027s-the-best-way-to-upgrade_003f
(archived at https://perma.cc/W5QW-MA6Q )
Uninstall R
Install the new R
version into a directory that
contains no spaces (see Step 2 in the Initial Set
Up section above)
[You only need to do this step if you installed packages in the
R-version-specific “Library” folder rather than the common/shared
“Packages” folder—that is, you don’t need to do this step if you used
the lab’s Rprofile.site
file, as described above, which
installs packages to the common/shared “Packages” folder]:
Copy installed packages in the “Library” folder to the “Library”
folder in the new installation
In new R
version folder, copy the current
Rprofile.site
file as a backup
(Rprofile_BACKUP.site
) and overwrite the original file with
the lab’s version of Rprofile.site
from here: https://research-git.uiowa.edu/PetersenLab/R-InitialSetup/-/blob/master/R%20Setup%20Files/Rprofile.site
R
will run the file named Rprofile.site
at
initial runtime.
Set the executables for R
and RStudio
to
always run with administrator permissions.
If on Windows, open File Explorer and find the main executable of
R
(C:\R\R-VERSION\bin\R.exe
) and
RStudio
(C:\Program Files\RStudio\bin\RStudio.exe
). Right-click it
to open the contextual menu. Then, click or tap on “Properties”. In the
Properties window, go to the Compatibility tab. At the bottom of the
window, check the box next to the “Run this program as an administrator”
option, and then click or tap on Apply or OK.
Make sure you have the latest version of the tools necessary to
compile packages from source (i.e., Rtools for Windows or R
Compiler Tools for Rcpp on MacOS; see the instructions in the section on
initial set up )
Open the new R
version and run
update.packages(checkBuilt = TRUE, ask = FALSE)
, and
install any necessary packages
Close R
Delete anything left of the old installation
Style Guide and Best
Practices
Create
Rstudio Project
For each data analysis project (i.e., each GitLab
/GitHub
repo), create an
RStudio Project. This helps keep your project files organized.
Use R
Notebooks for “Computational Notebooks”
Using R
Notebooks for “Computational Notebooks” is
helpful for reproducible code that can be shared with others. To create
computational notebooks see the Markdown
section on computational notebooks
in the Data Analysis guides.
Separate sections in
code
In R
scripts, use sections.
To insert a section in RStudio
, use
CTRL-Shift-R
or “Code” - “Insert Section”
In R
Notebooks/Markdown, use Headers and code chunks.
Headers: 1, 2, or 3 pound signs
Code Chunks: Ctrl+Alt+I
; or click “Insert” button then
“R”
Naming variables
Use meaningful variable names; we want to know what a variable
represents without having to consult an external codebook for every
variable
Variable names should include the prefix for the measure followed by
an underscore
e.g., cbcl_
for the Child Behavior Checklist
variables
Use lower camel case for variable naming
e.g., prefix_thisIsTheVariableName
Do not include spaces in variable names
Don’t save your
workspace image
For reproducibility purposes, it is important not
to save your workspace image (archived at https://perma.cc/9SCZ-L4DE ). It is best practices to
begin R
each session with a clean workspace. If there is a
.Rdata
file in the same folder as the
Rstudio Project
, Rstudio will automatically load the
objects into the workspace at the beginning of the session. This is
problematic because those objects can interact/interfere with the code
and can lead to problems with replicability for others who are running
the code without those objects in the workspace. When you exit
RStudio
, RStudio
asks if you want to “Save
workspace image to [filepath]/.Rdata
?” Make sure to select
“Don’t Save”! However, do make sure to save your R
scripts
before exiting Rstudio.
Saving Plots
png(); dev.off()
Shortcuts
Run selected line(s) of code: Ctrl + Enter
Comment/uncomment code: Ctrl + Shift + C
Pipe: Ctrl + Shift + M
Insert Code Chunk: Ctrl + Alt + I
Assignment operator: Alt + - (alt-dash)
Select multiple lines: Ctrl + Alt, up or down; or Alt + drag
mouse
Search: Ctrl + Shift + F
Show all keyboard shortcuts: Alt + Shift + K
Running Scripts
Automatically with Windows
https://www.spsanderson.com/steveondata/posts/2023-06-29/index.html
(archived at https://perma.cc/9EXK-W99Y )
R
scripts can be run automatically. For example, it can
be helpful to have an R
markdown report run automatically
before the day begins.
Open the Notepad
app and create a file with the
following syntax.
Location of R executable file\R CMD BATCH "Path location of script that should be automatically run"
Example:
C:\R\4.1.3\bin\R CMD BATCH "R:\Lab\Studies\School Readiness Study\Data Processing\5. Reports\automatic_reports\Run_Reports_auto.R"
Save the file as a .bat
file in the desired
location
Once the .bat
file has been created, search
Windows Task Scheduler
in the search bar
In the Actions
selection bar, select
Create basic Task...
Name the task and provide a description
Next, set the trigger for the new task (i.e., how often the task
should run)
Set the action for the task by selecting
Start a program
Under, Program/script
browse to the .bat
file that was created in step 1 and select Next
Click Finish
and the script is now configured to run
automatically
Note: When R
is updated, the path to the
bin
folder within R
needs to be updated to
reflect an accurate absolute path to R.
Example: C:\R\4.1.3\bin\R CMD BATCH
changed to
C:\R\4.3.0\bin\R CMD BATCH
Sending slacks with
R
Occasionally, it can be helpful to send a Slack message using
R
. For example, if a script does not run, a Slack message
can be sent to inform the appropriate team members. These
instructions (archived at https://perma.cc/9CWJ-J5ZT ) can largely be followed to
set up R
to send Slack messages. However, there are some
differences:
When setting up the configuration file, use the below template. The
slack API token should be placed in the token
category.
Note the token will need to be updated every 30 days. You can
generate a new token by navigating to the Slack API and selecting
Oauth & Permissions
slack picture
token: YOUR_FULL_API_TOKEN
channel: #general
username: slackr
incoming_webhook_url: https://hooks.slack.com/services/XXXXX/XXXXX/XXXXX
Once the configuration is complete, it is possible to send messages.
For now, we have found it helpful to embed the slacks in the
tryCatch
function.
tryCatch(
CODE YOU WANT TO RUN,
error = function(e)
{
#message to send if the code doesn't run
my_message <- paste( "example message")
slackr_msg(my_message, channel = "#recruitment")
})
Replacing
//n
with a space
Many notes in projects that are exported from REDCap come with spaces
denotes as //n
. Use the below code to make these fields
more readable in the future.
gsub('\\n', ', ', df$notesField)
Working with
R
on a Network Drive
When working with R
on a network drive, it may be
helpful to configure the project to store .Rproj.user
on
the local C:/
drive rather than on the network drive, which
results in slow execution times.
For more info:
Package
Development
Working with
renv
for Package Management
renv
is used for reproducibility, by helping with
package management (tracking package versions, etc.):
https://rstudio.github.io/renv/articles/renv.html
Before updating a package locally, make sure that it is available in
the Posit Package Manager (so it can be available to GitHub
Actions):
https://packagemanager.posit.co
Updating the
Package
To update the package, run the following in R
:
# 1. Update packages in package environment
renv::upgrade()
renv::update()
renv::snapshot()
# 2. Add/edit code
# 3. Update documentation
roxygen2::roxygenise()
# 4. Update package version
usethis::use_version()
Then, build the package: Ctrl-Shift-B
Then, install the package:
renv::install("C:/R/Packages/petersenlab") #PC
renv::install("/Library/Frameworks/R.framework/Packages/petersenlab") #Mac
Installing
Packages
To install new packages in the package environment, run the following
in R
:
renv::install("NAME_OF_PACKAGE")
or:
install.packages("NAME_OF_PACKAGE)
R CMD check
Build the source package
click on the “Build” tab in the top-right pane of RStudio, and then
click “Build Source Package”
Open terminal in RStudio
After the package is built, open a terminal window directly in
RStudio by clicking on the “Terminal” tab at the bottom of RStudio
Run R CMD check --as-cran
In the terminal window, navigate to the directory where your package
source is located. Then, run R CMD check --as-cran
followed
by the name of your package tarball. For example:
Build .tar.gz
file:
devtools::build(pkg = "D:/Documents/GitHub/petersenlab")
R CMD check --as-cran petersenlab_1.0.0.tar.gz
If errors compiling the PDF manual:
R CMD Rd2pdf . --output=man/figures/manual.pdf --force --no-preview --no-clean
Troubleshooting
no visible binding for global variable
;
Undefined global functions or variables
For example:
no visible binding for global variable
'moderatorVal_centered'
Undefined global functions or variables:
moderatorVal_centered predictorVal_centered
Solution: set each variable to NULL
in the package
function before it is mentioned. For example:
predictorVal_centered <- moderatorVal_centered <- NULL
R CMD check
via GitHub Actions
usethis::use_github_action("check-standard")
Useful keyboard
shortcuts for package authoring:
Install Package: ‘Ctrl + Shift + B’
Check Package: ‘Ctrl + Shift + E’
Test Package: ‘Ctrl + Shift + T’
renv::install("C:/R/Packages/petersenlab")
renv::snapshot()
renv::install()
pkgdown
Run once to configure your package to use pkgdown:
usethis::use_pkgdown()
Then use pkgdown
to build your website:
pkgdown::build_site()
Steps to Add
Functions
Add .R
file with the function
Add the function to the _pkgdown.yml
file
Update version number
renv::upgrade()
renv::update()
renv::snapshot()
roxygen2::roxygenise()
Install Package: ‘Ctrl + Shift + B’
Check Package: ‘Ctrl + Shift + E’
R CMD check
Commit and push changes
Update release version in GitHub
Add
sub-packages
devtools::build(pkg = "D:/Documents/GitHub/petersenlab/inst/extdata/testpackage1")
devtools::build(pkg = "D:/Documents/GitHub/petersenlab/inst/extdata/testpackage2")
install.packages("D:/Documents/GitHub/petersenlab/inst/extdata/testpackage1_0.1.0.tar.gz", repos = NULL, source = TRUE)
install.packages("D:/Documents/GitHub/petersenlab/inst/extdata/testpackage2_0.1.0.tar.gz", repos = NULL, source = TRUE)
remotes::install_local("D:/Documents/GitHub/petersenlab/inst/extdata/testpackage2_0.1.0.tar.gz")
remotes::install_local("D:/Documents/GitHub/petersenlab/inst/extdata/testpackage2_0.1.0.tar.gz")
Resources
Official documentation for CRAN:
Unofficial documentation:
For Package
Development Tasks
LS0tCnRpdGxlOiAiUiIKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsCiAgZXJyb3IgPSBUUlVFLAogIGNvbW1lbnQgPSAiIiwKICBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IikKYGBgCgogPiAiKmBSYCB3aWxsIGFsd2F5cyBiZSBhcmNhbmUgdG8gdGhvc2Ugd2hvIGRvIG5vdCBtYWtlIGEgc2VyaW91cyBlZmZvcnQgdG8gbGVhcm4gaXQuIEl0IGlzIG5vdCBtZWFudCB0byBiZSBpbnR1aXRpdmUgYW5kIGVhc3kgZm9yIGNhc3VhbCB1c2VycyB0byBqdXN0IHBsdW5nZSBpbnRvLiBJdCBpcyBmYXIgdG9vIGNvbXBsZXggYW5kIHBvd2VyZnVsIGZvciB0aGF0LiBCdXQgdGhlIHJld2FyZHMgYXJlIGdyZWF0IGZvciBzZXJpb3VzIGRhdGEgYW5hbHlzdHMgd2hvIHB1dCBpbiB0aGUgZWZmb3J0LioiCgrigJQgW0JlcnRvbiBHdW50ZXIgUi1oZWxwIEF1Z3VzdCAyMDA3XShodHRwczovL3d3dy5icm9kcmlndWVzLmNvL2Jsb2cvMjAyMi0wNi0wMi1hcmNhbmUvKSAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9LWTlOLTJGVFQpCgo+ICJFdmVseW4gSGFsbDogSSB3b3VsZCBsaWtlIHRvIGtub3cgaG93IChpZikgSSBjYW4gZXh0cmFjdCBzb21lIG9mIHRoZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBzdW1tYXJ5IG9mIG15IG5sbWUuCj4gCj4gU2ltb24gQmxvbWJlcmc6IFRoaXMgaXMgYFJgLiBUaGVyZSBpcyBubyBpZi4gT25seSBob3cuIgoK4oCUIFtFdmVseW4gSGFsbCBhbmQgU2ltb24gJ1lvZGEnIEJsb21iZXJnLCBSLWhlbHAgQXByaWwgMjAwNV0oaHR0cHM6Ly93d3cuYnJvZHJpZ3Vlcy5jby9ibG9nLzIwMjItMDYtMDItYXJjYW5lLykgKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvS1k5Ti0yRlRUKQoKIyBMZWFybmluZyBgUmAgeyNsZWFybn0KCiMjIEJhc2UgUgoKSGVyZSBhcmUgYSBzbGV3IG9mIHJlc291cmNlcyBmb3IgbGVhcm5pbmcgYmFzZSBgUmAgKGluIGFkZGl0aW9uIHRvIHRoZSBkb2N1bWVudHMgaW4gdGhlIGxhYidzIFtQcmltZXIgQXJ0aWNsZXMgZm9sZGVyXShodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZHJpdmUvdS8wL2ZvbGRlcnMvMVUwcE5yakYyX3RJUFU2Qml5Q3YwSHFiT1VBNVA1MTZyKSk6CgotIFVzZSB0aGlzIEludHJvIHRvIGBSYDogaHR0cHM6Ly93d3cuc3RhdG1ldGhvZHMubmV0LyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9TUFEyLURKS00pCiAgICAtIEkgdXNlZCB0aGlzIHJlc291cmNlIGNvbnNpZGVyYWJseSB3aGVuIEkgd2FzIGxlYXJuaW5nIGBSYAotIExlYXJuIGhvdyB0byBiZWNvbWUgYSBiZXR0ZXIgY29kZXI6IGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLzEwLXRvcC10aXBzLWZvci1iZWNvbWluZy1hLWJldHRlci1jb2Rlci8gKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvSDNXNy1RREFNKQotIEhvdyB0byBiZWNvbWUgZmx1ZW50IGluIGBSYDogaHR0cHM6Ly93d3cuc2hhcnBzaWdodGxhYnMuY29tL2Jsb2cvYXJlLXlvdS1mbHVlbnQtci8gKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvUldMNS1HVFdWKQotIFZpZGVvIHRyYWluaW5nIGNvdXJzZXMgaW4gYFJgIHNraWxsczogaHR0cHM6Ly93d3cucGx1cmFsc2lnaHQuY29tL3NlYXJjaD9xPVIgKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvVTRaWi1VRTRYKQotIEJyb3dzZSB0aGUgYENvb2tib29rIGZvciBSYCB0byBmaW5kIHNvbHV0aW9ucyB0byBjb21tb24gdGFza3MgYW5kIHByb2JsZW1zOiBodHRwOi8vd3d3LmNvb2tib29rLXIuY29tLyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy81RVJKLVZSRlIpCi0gQnJvd3NlIHRoZSBgUiBHcmFwaCBHYWxsZXJ5YCB0byBmaW5kIGV4YW1wbGVzIG9mIHZhcmlvdXMgZ3JhcGhzOiA8aHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tPgotIEZyZWUgYENvZGVhY2FkZW15YCBjb3Vyc2Ugb24gYFJgOiBodHRwczovL3d3dy5jb2RlY2FkZW15LmNvbS9sZWFybi9sZWFybi1yIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL0pIUzItRVlVVSkKLSBGcmVlIGBDb3Vyc2VyYWAgY291cnNlcyBvbiBgUmA6IGh0dHBzOi8vcGFpcmFjaC5jb20vMjAxMi8xMi8yMi9sZWFybi10by11c2Utci1mb3ItZnJlZS13aXRoLWNvdXJzZXJhLyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9YSjVVLTVFNFcpCi0gTU9PQ3MgYW5kIGNvdXJzZXMgdG8gbGVhcm4gYFJgOiBodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS9tb29jcy1hbmQtY291cnNlcy10by1sZWFybi1yLyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9NUTI1LUUzTUUpCi0gV2F0Y2ggdGhlc2UgdmlkZW9zIGZyb20gYENvdXJzZXJhYDogaHR0cHM6Ly9ibG9nLnJldm9sdXRpb25hbmFseXRpY3MuY29tLzIwMTIvMTIvY291cnNlcmEtdmlkZW9zLmh0bWwgKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvNkZVNC1QQVFXKQotIGBSU3R1ZGlvYCBXZWJpbmFyczogaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL3dlYmluYXJzLyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy82Uk5SLTk4SkIpCi0gVUNMQSBTdGF0cyBXZWJzaXRlOiBodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvci8gKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvTTNONi05NlJZKQotIFRha2UgdGhpcyBJbnRyb2R1Y3Rpb24gdG8gYFJgIGNvdXJzZTogaHR0cHM6Ly93d3cuZGF0YWNhbXAuY29tL2NvdXJzZXMvZnJlZS1pbnRyb2R1Y3Rpb24tdG8tciAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy82Wk05LTM3TDkpCi0gVGVhY2hpbmcgYFJgIGluIGEgS2luZGVyLCBHZW50bGVyLCBNb3JlIEVmZmVjdGl2ZSBNYW5uZXI6IGh0dHBzOi8vZ2l0aHViLmNvbS9tYXRsb2ZmL1RpZHl2ZXJzZVNrZXB0aWMgKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvUVU5Ri1GQlM4KQotIExlYXJuIGBSYCBpbnRlcmFjdGl2ZWx5IHdpdGggYHN3aXJsYDogaHR0cHM6Ly9zd2lybHN0YXRzLmNvbS8gKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvVFQ5Vi1VNjYzKQotIFVzZSB0aGUgYGxlYXJucmAgcGFja2FnZTogaHR0cHM6Ly9ibG9nLnJzdHVkaW8uY29tLzIwMTcvMDcvMTEvaW50cm9kdWNpbmctbGVhcm5yLyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9YR0o4LUVYU04pCi0gWW91IHdpbGwgc29tZXRpbWVzIGZpbmQgcmVsZXZhbnQgYXJ0aWNsZXMgb24gYFItYmxvZ2dlcnNgOiBodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8gKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvRUwzWC1aWEJCKQoKIyMgU3RhdGlzdGljcyAodXNpbmcgYFJgKQoKVGhlIGZvbGxvd2luZyBhcmUgcmVzb3VyY2VzIGZvciBsZWFybmluZyBzdGF0aXN0aWNzIHVzaW5nIGBSYC4KCi0gVUNMQSBTdGF0cyBXZWJzaXRlOiBodHRwczovL3N0YXRzLm9hcmMudWNsYS5lZHUvb3RoZXIvZGFlLyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9GVkc1LTVYODIpCi0gRnJlZSB0ZXh0Ym9vayBvbiBMZWFybmluZyBTdGF0aXN0aWNzIHdpdGggYFJgOiBodHRwczovL2xlYXJuaW5nc3RhdGlzdGljc3dpdGhyLmNvbSAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9TWTg3LTVHQ1gpCi0gQW4gZXhjZWxsZW50IGludHJvZHVjdG9yeSB0ZXh0Ym9vayBvbiBEaXNjb3ZlcmluZyBTdGF0aXN0aWNzIHVzaW5nIGBSYDogaHR0cHM6Ly93d3cuYW1hem9uLmNvbS9EaXNjb3ZlcmluZy1TdGF0aXN0aWNzLVVzaW5nLUFuZHktRmllbGQvZHAvMTQ0NjIwMDQ2OSAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy85TEdVLTdaTUUpCgojIyBgdGlkeXZlcnNlYAoKVGhlIGZvbGxvd2luZyBhcmUgcmVzb3VyY2VzIGZvciBsZWFybmluZyBgdGlkeXZlcnNlYCwgd2hpY2ggaXMgYSBjb2xsZWN0aW9uIG9mIGBSYCBwYWNrYWdlcyBmb3IgZGF0YSBtYW5hZ2VtZW50OgoKLSBodHRwczovL3d3dy50aWR5dmVyc2Uub3JnL2xlYXJuLyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy81WlVNLVhHRVMpCgotIGh0dHBzOi8vd3d3LmxpbmtlZGluLmNvbS9sZWFybmluZy9sZWFybmluZy10aGUtci10aWR5dmVyc2Uvd2VsY29tZT91PTQyNDU5MDIwIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL1RENTYtRlg4UikKCiMjIFF1ZXN0aW9ucwoKSWYgeW91IGhhdmUgcXVlc3Rpb25zOgoKLSBQb3N0IHRvIHRoZSBgUmAgbWFpbGluZyBsaXN0OiBodHRwczovL3N0YXQuZXRoei5jaC9tYWlsbWFuL2xpc3RpbmZvL3ItaGVscAotIFBvc3QgdG8gZm9ydW1zOiBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy90YWdnZWQvcgotIE1vcmUgaW5mbzogaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vd2hlcmUtdG8tZ2V0LWhlbHAtd2l0aC15b3VyLXItcXVlc3Rpb24vIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL1A1VFctRTdIUCkKCiMgSW5pdGlhbCBTZXQgVXAgeyNzZXR1cH0KCk5vdGU6IG1hbnkgb2YgdGhlc2UgaW5pdGlhbCBzZXR1cCBzdGVwcyBkZXNjcmliZWQgYmVsb3cgYXJlIG5vdCBuZWNlc3NhcnkgZm9yIGdlbmVyYWwgdXNlOyBtYW55IG9mIHRoZXNlIHN0ZXBzIGFyZSBuZWNlc3Nhcnkgb25seSBmb3IgdXNpbmcgbGFiLXJlbGF0ZWQgcmVwb3NpdG9yaWVzIChlLmcuLCB0byBnYWluIEFQSSBhY2Nlc3MgdG8gZXhwb3J0IGRhdGEgZnJvbSBgUkVEQ2FwYCwgdG8gdXNlIGFic29sdXRlIHBhdGhzIHJhdGhlciB0aGFuIHJlbGF0aXZlIHBhdGhzIHNvIHJlcG9zIGNhbiBjb21tdW5pY2F0ZSB3aXRoIGVhY2ggb3RoZXIsIGV0Yy4pLgoKMS4gTWFrZSBzdXJlIHlvdSBhcmUgbG9nZ2VkIG9udG8gYSBjb21wdXRlciB0aGF0IGNhbiBhY2Nlc3MgdGhlIGxhYiBzZXJ2ZXIgKGVpdGhlciBhIGxhYiBjb21wdXRlciwgb3IgYSBjb21wdXRlciB5b3UgY2FuIFZQTiBpbnRvIHRoZSBsYWIgc2VydmVyKSwgYW5kIHRoYXQgeW91IGhhdmUgYWRtaW4gYWNjZXNzIHRvIGluc3RhbGwgYW5kIHVuaW5zdGFsbCBzb2Z0d2FyZQoxLiBJbnN0YWxsIGBSYCAoaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pIGludG8gYSBkaXJlY3RvcnkgdGhhdCBjb250YWlucyBubyBzcGFjZXM7IE9uIFBDLCBjaGFuZ2UgdGhlIGxvY2F0aW9uIGZyb20gdGhlIGRlZmF1bHQgYEM6L1Byb2dyYW0gRmlsZXMvUi9bUi1WRVJTSU9OXWAgKHdoaWNoIGNvbnRhaW5zIGEgc3BhY2UpIHRvIGBDOi9SL1tSLVZFUlNJT05dYCAoW3doaWNoIGRvZXMgbm90IGNvbnRhaW4gYW55IHNwYWNlc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvZG9jL21hbnVhbHMvci1yZWxlYXNlL1ItYWRtaW4uaHRtbCNJbnN0YWxsaW5nLVItdW5kZXItV2luZG93cyk7IGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvNlZNWC0zTFlYLS0tdGhpcyBpcyBiZWNhdXNlIFtzb21lIHBhY2thZ2VzIHRoYXQgcmVxdWlyZSBjb21waWxhdGlvbiB0byBpbnN0YWxsIGNhbm5vdCByZWFkIGZpbGVwYXRocyB3aXRoIHNwYWNlc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvZG9jL21hbnVhbHMvci1yZWxlYXNlL1ItYWRtaW4uaHRtbCNJbnN0YWxsYXRpb24pOyBhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL1hBM1YtSlRQWSk7IG1heSBoYXZlIHRvIHJpZ2h0IGNsaWNrIGFuZCAiUnVuIEFzIEFkbWluaXN0cmF0b3IiCiAgICAtIElmIGBSYCB3YXMgYWxyZWFkeSBpbnN0YWxsZWQgaW4gYSBkaXJlY3RvcnkgdGhhdCBjb250YWlucyBzcGFjZXMgKGUuZy4sIGBDOi9Qcm9ncmFtIEZpbGVzL1IvW1ItVkVSU0lPTl1gKSwgdW5pbnN0YWxsIGBSYCBiZWZvcmUgaW5zdGFsbGluZyBpdCBpbiBhIGRpcmVjdG9yeSB0aGF0IGRvZXNuJ3QgY29udGFpbiBzcGFjZXMKMS4gSW5zdGFsbCBgUlN0dWRpb2AgRGVza3RvcCAoaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9kb3dubG9hZC8pIGluIHRoZSBtYWluIHByb2dyYW0gZmlsZXMgZGlyZWN0b3J5OyBtYXkgaGF2ZSB0byByaWdodCBjbGljayBhbmQgIlJ1biBBcyBBZG1pbmlzdHJhdG9yIi4KYFJTdHVkaW9gIGlzIHRoZSBiZXN0IGF2YWlsYWJsZSBncmFwaGljYWwgdXNlciBpbnRlcmZhY2UgZm9yIFIuCjEuIFNldCB0aGUgZXhlY3V0YWJsZXMgZm9yIGBSYCBhbmQgYFJTdHVkaW9gIHRvIGFsd2F5cyBydW4gd2l0aCBhZG1pbmlzdHJhdG9yIHBlcm1pc3Npb25zLgogICAgLSBJZiBvbiBXaW5kb3dzLCBvcGVuIEZpbGUgRXhwbG9yZXIgYW5kIGZpbmQgdGhlIG1haW4gZXhlY3V0YWJsZSBvZiBgUmAgKGBDOi9SL1tSLVZFUlNJT05dL2Jpbi9SLmV4ZWApIGFuZCBgUlN0dWRpb2AgKGBDOlxQcm9ncmFtIEZpbGVzXFJTdHVkaW9cYmluXFJTdHVkaW8uZXhlYCkuCiAgICBSaWdodC1jbGljayBpdCB0byBvcGVuIHRoZSBjb250ZXh0dWFsIG1lbnUuCiAgICBUaGVuLCBjbGljayBvciB0YXAgb24gIlByb3BlcnRpZXMiLgogICAgSW4gdGhlIFByb3BlcnRpZXMgd2luZG93LCBnbyB0byB0aGUgQ29tcGF0aWJpbGl0eSB0YWIuCiAgICBBdCB0aGUgYm90dG9tIG9mIHRoZSB3aW5kb3csIGNoZWNrIHRoZSBib3ggbmV4dCB0byB0aGUgIlJ1biB0aGlzIHByb2dyYW0gYXMgYW4gYWRtaW5pc3RyYXRvciIgb3B0aW9uLCBhbmQgdGhlbiBjbGljayBvciB0YXAgb24gQXBwbHkgb3IgT0suCjEuIEluc3RhbGwgdG9vbHMgdG8gYWxsb3cgeW91IHRvIGNvbXBpbGUgYFJgIHBhY2thZ2VzIHNvIHlvdSBjYW4gaW5zdGFsbCBwYWNrYWdlcyBmcm9tIHNvdXJjZSwgaWYgbmVjZXNzYXJ5IChpLmUuLCBpZiBwYWNrYWdlIGJpbmFyaWVzIGFyZSBub3QgYXZhaWxhYmxlKToKICAgIC0gSWYgb24gV2luZG93cywgaW5zdGFsbCBbUnRvb2xzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9iaW4vd2luZG93cy9SdG9vbHMvKTsgbWF5IGhhdmUgdG8gcmlnaHQgY2xpY2sgYW5kICJSdW4gQXMgQWRtaW5pc3RyYXRvciIKICAgIC0gSWYgb24gTWFjLCBpbnN0YWxsIFtSIENvbXBpbGVyIFRvb2xzIGZvciBSY3BwIG9uIE1hY09TXShodHRwczovL3RoZWNvYXRsZXNzcHJvZmVzc29yLmNvbS9wcm9ncmFtbWluZy9jcHAvci1jb21waWxlci10b29scy1mb3ItcmNwcC1vbi1tYWNvcy8pIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL0IzNUotUjIyWCkKMS4gU2V0IHVwIGBnaXRgLCBgR2l0TGFiYCwgYW5kIHRoZSBgR2l0SHViIERlc2t0b3BgIEFwcCBpbiB0aGUgbWFpbiBwcm9ncmFtIGZpbGVzIGRpcmVjdG9yeTsgbWF5IGhhdmUgdG8gcmlnaHQgY2xpY2sgYW5kICJSdW4gQXMgQWRtaW5pc3RyYXRvciI7IEZvciBpbnN0cnVjdGlvbnMgc2V0dGluZyB1cCBhbmQgdXNpbmcgYEdpdExhYmAsIHNlZSBoZXJlOiBodHRwczovL2RldnBzeWxhYi5naXRodWIuaW8vRGF0YUFuYWx5c2lzL2dpdC5odG1sI3RvQmVnaW4KMS4gVGhlIGBScHJvZmlsZS5zaXRlYCBmaWxlIGluIHRoZSBgZXRjYCBmb2xkZXIgb2YgdGhlIGBSYCBpbnN0YWxsYXRpb24gZGlyZWN0b3J5IGlzIHRoZSBjb2RlIHRoYXQgaXMgcnVuIGZvciAqKipldmVyeSB1c2VyKioqIGF0IHRoZSBiZWdpbm5pbmcgZWFjaCB0aW1lIHlvdSBsb2FkIFIuICBXZSB3aWxsIHVwZGF0ZSB0aGUgZGVmYXVsdCBgUnByb2ZpbGUuc2l0ZWAgZmlsZSB3aXRoIHRoZSBsYWIncyBgUnByb2ZpbGUuc2l0ZWAgZmlsZSBzbyBgUmAgaW5zdGFsbHMgcGFja2FnZXMgaW4gdGhlIGNvcnJlY3QgbG9jYXRpb24sIHNldHMgdGhlIGRlZmF1bHQgcGFja2FnZSByZXBvc2l0b3J5LCB1cGRhdGVzIHBhY2thZ2VzLCBhbmQgZ2l2ZXMgeW91IGEgZm9ydHVuZSBjb29raWUuClRvIGRvIHRoaXMsIHBlcmZvcm0gdGhlIGZvbGxvd2luZyBzdGVwczoKICAgIC0gUmVuYW1lIHRoZSBgUnByb2ZpbGUuc2l0ZWAgZmlsZSBpbiB0aGUgYFJgIGluc3RhbGxhdGlvbiBkaXJlY3RvcnkgKGBDOi9SL1ItW0luc2VydFZlcnNpb25OdW1iZXJdL2V0Yy9ScHJvZmlsZS5zaXRlYCkgdG8gYmUgYFJwcm9maWxlX0JBQ0tVUC5zaXRlYAogICAgLSBEb3dubG9hZCB0aGUgbGFiJ3MgYFJwcm9maWxlLnNpdGVgIGZpbGUgbG9jYXRlZCBpbiB0aGlzIHJlcG9zaXRvcnkgYXQgdGhlIGZvbGxvd2luZyBsb2NhdGlvbiAoaHR0cHM6Ly9yZXNlYXJjaC1naXQudWlvd2EuZWR1L1BldGVyc2VuTGFiL1ItSW5pdGlhbFNldHVwLy0vYmxvYi9tYXN0ZXIvUiUyMFNldHVwJTIwRmlsZXMvUnByb2ZpbGUuc2l0ZSksIGFuZCBwYXN0ZSBpdCBpbnRvIHRoZSBgUmAgaW5zdGFsbGF0aW9uIGRpcmVjdG9yeSAoUEM6IGBDOi9SL1ItW0luc2VydFZlcnNpb25OdW1iZXJdL2V0Yy9ScHJvZmlsZS5zaXRlYDsgTWFjOiBgL0xpYnJhcnkvRnJhbWV3b3Jrcy9SLmZyYW1ld29yay9WZXJzaW9ucy9bSW5zZXJ0VmVyc2lvbk51bWJlcl0vUmVzb3VyY2VzL2V0Yy9ScHJvZmlsZS5zaXRlYCkKMS4gVGhlIGAuUnByb2ZpbGVgIGZpbGUgaW4gdGhlIHVzZXIncyBgRG9jdW1lbnRzYCBmb2xkZXIgaXMgdGhlIGNvZGUgdGhhdCBpcyBydW4gZm9yICoqKnRoZSBwYXJ0aWN1bGFyIHVzZXIqKiogYXQgdGhlIGJlZ2lubmluZyBlYWNoIHRpbWUgeW91IGxvYWQgUi4KV2Ugd2lsbCB1cGRhdGUgdGhlIGRlZmF1bHQgYC5ScHJvZmlsZWAgZmlsZSAoaWYgdGhlcmUgaXMgb25lKSB3aXRoIHRoZSBsYWIncyBgLlJwcm9maWxlYCBmaWxlIHNvIGBSYCBrbm93cyB3aGljaCBjb21wdXRlciB5b3UgYXJlIHVzaW5nIGFuZCB3aGljaCBwYXRoIHRvIHVzZSAocmVsYXRpdmUgdG8gd2hlcmUgeW91ciBgUmAgcHJvamVjdHMgYXJlIGxvY2F0ZWQpLgpUbyBkbyB0aGlzLCBwZXJmb3JtIHRoZSBmb2xsb3dpbmcgc3RlcHM6CiAgICAtIERvd25sb2FkIHRoZSBsYWIncyBgLlJwcm9maWxlYCB0ZW1wbGF0ZSBmaWxlIGluIHRoaXMgcmVwb3NpdG9yeSBhdCB0aGUgZm9sbG93aW5nIGxvY2F0aW9uLCBhbmQgbWFrZSBzdXJlIHRvIHJlbW92ZSBhbnl0aGluZyBiZXNpZGVzIGAuUnByb2ZpbGVgIGluIHRoZSBmaWxlbmFtZTogaHR0cHM6Ly9yZXNlYXJjaC1naXQudWlvd2EuZWR1L1BldGVyc2VuTGFiL1ItSW5pdGlhbFNldHVwLy0vYmxvYi9tYXN0ZXIvUiUyMFNldHVwJTIwRmlsZXMvLlJwcm9maWxlCiAgICAtIE9wZW4gdGhlIGxhYidzIGAuUnByb2ZpbGVgIGZpbGUsIGFuZCByZXZpc2UgaXQgd2l0aCB5b3VyIEhhd2tJRAogICAgLSBSZXZpc2UgdGhlIGxhYidzIGAuUnByb2ZpbGVgIGZpbGUgd2l0aCB0aGUgbG9jYWwgcGF0aCB0byB0aGUgYERvY3VtZW50c2AgZm9sZGVyIGZvciBlYWNoIG9mIHRoZSBjb21wdXRlcnMgeW91IHdpbGwgdXNlIHRvIGFjY2VzcyBgUmAgKGUuZy4sIGhvbWUgY29tcHV0ZXIsIHdvcmsgY29tcHV0ZXIsIGxhcHRvcCkuCiAgICBNYWtlIHN1cmUgdG8gdXNlIGZvcndhcmQgc2xhc2hlcyAoYC9gKSwgbm90IGJhY2sgc2xhc2hlcyAoYFxgKSBpbiB0aGUgcGF0aC4KICAgIC0gWW91IHdpbGwgc2F2ZSB0aGUgZmlsZSBpbiB5b3VyIGBIT01FYCBkaXJlY3RvcnkuCiAgICBUbyBmaW5kIHRoZSBgSE9NRWAgZGlyZWN0b3J5LCBvcGVuIGBSYCBhbmQgdHlwZSB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6IGBTeXMuZ2V0ZW52KCJIT01FIilg4oCUdGhlIG91dHB1dCBvZiB0aGUgY29tbWFuZCBpcyB0aGUgbG9jYXRpb24gb2YgeW91ciBgSE9NRWAgZGlyZWN0b3J5OyBJZiB0aGlzIGlzIGEgbGFiIGNvbXB1dGVyLCBpdCBtYXkgYmUgbG9jYXRlZCBoZXJlOiBgLy9ob21lLmlvd2EudWlvd2EuZWR1L1t1c2VyXS9Eb2N1bWVudHNgLgogICAgSWYgdGhpcyBpcyB5b3VyIHBlcnNvbmFsIGNvbXB1dGVyLCBpdCBtYXkgYmUgbG9jYXRlZCBoZXJlOiBQQzogYEM6L1VzZXJzL1t1c2VyXS9Eb2N1bWVudHNgOyBNYWM6IGAvVXNlcnMvW3VzZXJdYC4KICAgIFRoZW4gY2xvc2UgUi4KICAgIC0gSWYgeW91ciBgSE9NRWAgZGlyZWN0b3J5IGlzIGluIGEgT25lRHJpdmUgZm9sZGVyIChvciBhbm90aGVyIGNsb3VkLWJhc2VkIHN5bmMgZm9sZGVyKSwgeW91IHdpbGwgd2FudCB0byBjaGFuZ2UgdGhlIGRpcmVjdG9yeSBvZiB5b3VyIGBIT01FYCBwYXRoIHNvIHRoYXQgaXQgaXMgbm90IGluIGEgT25lRHJpdmUgZm9sZGVyLgogICAgVG8gZG8gdGhhdCwgb3BlbiBbYEVudmlyb25tZW50IFZhcmlhYmxlc2BdKGh0dHBzOi8vc3VwZXJ1c2VyLmNvbS9xdWVzdGlvbnMvOTQ5NTYwL2hvdy1kby1pLXNldC1zeXN0ZW0tZW52aXJvbm1lbnQtdmFyaWFibGVzLWluLXdpbmRvd3MtMTApIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL0EyRTUtQjVWQSkgaW4gV2luZG93cy4KICAgIFRoZW4sIGFkZC9lZGl0IGBIT01FYCBhcyB0aGUgInZhcmlhYmxlIG5hbWUiIHdpdGggdGhlIGludGVuZGVkIGxvY2F0aW9uIGFzIHRoZSAidmFyaWFibGUgdmFsdWUiIChlLmcuLCBgQzovVXNlcnMvW3VzZXJdL0RvY3VtZW50c2AsIHdoZXJlIHlvdSByZXBsYWNlICJ1c2VyIiB3aXRoIHlvdXIgSGF3a0lEKS4KICAgICAgICAtIFlvdSBtYXkgYWxzbyBzb2x2ZSB0aGlzIGlzc3VlIGJ5IHBsYWNpbmcgdGhlIGZvbGxvd2luZyBjb21tYW5kIGluIHRoZSBgUnByb2ZpbGUuc2l0ZWAgZnJvbSB0aGUgcHJldmlvdXMgc3RlcAogICAgICAgICAgICAtIGBTeXMuc2V0ZW52KCJIT01FIiA9ICJDOi9Vc2Vycy9bc3BlY2lmaWMgdXNlciBJRF0pL0RvY3VtZW50cyIpYAogICAgLSBNb3ZlIHRoZSByZXZpc2VkIGAuUnByb2ZpbGVgIGZpbGUgdG8gdGhlIGBIT01FYCBkaXJlY3RvcnkgYW5kIG92ZXJ3cml0ZSB0aGUgb3JpZ2luYWwgYC5ScHJvZmlsZWAgZmlsZSAoaWYgaXQgZXhpc3RzKS4KICAgIFlvdSBtYXkgaGF2ZSB0byBzaG93IGhpZGRlbiBmaWxlcyBpbiBvcmRlciB0byBzZWUgdGhlIGZpbGUgKFBDOiBzZWUgV2luZG93cyBFeHBsb3JlciBzZXR0aW5nczsgTWFjOiBDb21tYW5kK1NoaWZ0K0RvdCkuCiAgICAtIE1ha2Ugc3VyZSB0byBzaG93IGZpbGVuYW1lIGV4dGVuc2lvbnMgaW4geW91ciBmaWxlIGV4cGxvcmVyIHdpbmRvdywgYW5kIG1ha2Ugc3VyZSB0aGUgZmlsZSBpcyBuYW1lZCBgLlJwcm9maWxlYCAobm90IGAuUnByb2ZpbGUuUnByb2ZpbGVgKS4KICAgIE1ha2Ugc3VyZSB0aGVyZSBpcyBhIHBlcmlvZCBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBmaWxlbmFtZS4KMS4gUnVuIGBSU3R1ZGlvYC4KSWYgdGhlIGBScHJvZmlsZS5zaXRlYCBhbmQgYC5ScHJvZmlsZWAgZmlsZXMgYXJlIGNvcnJlY3RseSBzZXQgdXAsIHRoZXkgc2hvdWxkIHByZS1wb3B1bGF0ZSB5b3VyIGBwYXRoYCBsb2NhdGlvbiB3aGVuIHlvdSBvcGVuIFIuCklmIHRoZSBjb250ZW50cyBvZiB0aGUgYEdsb2JhbCBFbnZpcm9ubWVudGAgaW4gYFJTdHVkaW9gIGFyZSBlbXB0eSwgeW91ciBgUnByb2ZpbGUuc2l0ZWAgYW5kL29yIGAuUnByb2ZpbGVgIGZpbGVzIGFyZSBub3Qgc2V0IHVwIGNvcnJlY3RseS4KICAgIC0gSWYgeW91IGdldCB0aGlzIGVycm9yIChgRXJyb3I6IGNvdWxkIG5vdCBmaW5kIGZ1bmN0aW9uICJpbnN0YWxsLnBhY2thZ2VzImApLCBydW4gdGhlIGZvbGxvd2luZyBsaW5lIG1hbnVhbGx5IGFuZCB0aGVuIHJlc3RhcnQgYFJTdHVkaW9gIGFmdGVyIHRoZSBwYWNrYWdlIGZpbmlzaGVzIGluc3RhbGxpbmc6IGBpbnN0YWxsLnBhY2thZ2VzKCJmb3J0dW5lcyIpYAoxLiBGb3IgW3JlcHJvZHVjaWJpbGl0eSBwdXJwb3Nlc10oI2RvbnRTYXZlV29ya3NwYWNlKSwgcHJldmVudCBgUmAvYFJTdHVkaW9gIGZyb20gc2F2aW5nIHlvdXIgd29ya3NwYWNlcyBhdXRvbWF0aWNhbGx5IHVzaW5nIHRoZSBmb2xsb3dpbmcgc3RlcHM6CiAgICAtIFdpdGggUlN0dWRpbyBydW5uaW5nLCBjaG9vc2UgYFRvb2xzIOKGkiBHbG9iYWwgT3B0aW9uc2AgZnJvbSB0aGUgbWVudXMuCiAgICAtIEluIHRoZSBPcHRpb25zIGRpYWxvZywgY2hhbmdlIHRoZSB2YWx1ZSBmb3IgYFNhdmUgd29ya3NwYWNlIHRvIC5SRGF0YSBvbiBleGl0YCB0byBgTmV2ZXJgLgogICAgLSBDbGljayBgT0tgLgoxLiBJbnN0YWxsIHRoZSBgcGV0ZXJzZW5sYWJgIGBSYCBwYWNrYWdlIHVzaW5nIHRoZSBmb2xsb3dpbmcgc3RlcHM6CiAgICAtIEluc3RhbGwgdGhlIGByZW1vdGVzYCBwYWNrYWdlIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZDogYGluc3RhbGwucGFja2FnZXMoInJlbW90ZXMiKWAKICAgIC0gSW5zdGFsbCB0aGUgYHBldGVyc2VubGFiYCBwYWNrYWdlIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZDogYHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJEZXZQc3lMYWIvcGV0ZXJzZW5sYWIiKWAKMS4gUmVxdWVzdCBhbiBbQVBJIHRva2VuXShodHRwczovL3JlZGNhcC5pY3RzLnVpb3dhLmVkdS9yZWRjYXAvYXBpL2hlbHAvP2NvbnRlbnQ9dG9rZW5zKSBmb3IgdGhlIGZvbGxvd2luZyBSRURDYXAgcHJvamVjdChzKTsgbm90ZTogcGxlYXNlIGNoZWNrIHdpdGggRHIuIFAgYmVmb3JlIHJlcXVlc3RpbmcgYW4gQVBJIHRva2VuLgpJbiBnZW5lcmFsLCBSQXMgc2hvdWxkIG5vdCBoYXZlIGFuIEFQSSB0b2tlbi4KICAgIC0gW1NjaG9vbCBSZWFkaW5lc3MgU3R1ZHldKGh0dHBzOi8vcmVkY2FwLmljdHMudWlvd2EuZWR1L3JlZGNhcC9yZWRjYXBfdjEyLjQuMS9pbmRleC5waHA/cGlkPTQ5NDEpCiAgICAtIFtTY2hvb2wgUmVhZGluZXNzIFN0dWR5IFNjcmVlbmluZ10oaHR0cHM6Ly9yZWRjYXAuaWN0cy51aW93YS5lZHUvcmVkY2FwL3JlZGNhcF92MTIuNC4xL2luZGV4LnBocD9waWQ9NDk1OCkKICAgIC0gWzIwMTcwMTgzNyAtIE1lY2hhbmlzbXMgaW4gdGhlIERldmVsb3BtZW50IG9mIFNlbGYtUmVndWxhdGlvbiwgU2Nob29sIFJlYWRpbmVzcywgYW5kIEJlaGF2aW9yIFByb2JsZW1zIFtEYXRhIEV4dHJhY3Rpb25dXShodHRwczovL3JlZGNhcC5pY3RzLnVpb3dhLmVkdS9yZWRjYXAvcmVkY2FwX3YxMi40LjEvaW5kZXgucGhwP3BpZD0xMTIzMykKICAgIC0gW1NjaG9vbCBSZWFkaW5lc3MgU3R1ZHkgLSBQcm9zcGVjdGl2ZSBQYXJ0aWNpcGFudHNdKGh0dHBzOi8vcmVkY2FwLmljdHMudWlvd2EuZWR1L3JlZGNhcC9yZWRjYXBfdjEyLjQuMS9pbmRleC5waHA/cGlkPTEwNDQwKQoxLiBXaGVuIHlvdXIgQVBJIHRva2VuIGhhcyBiZWVuIGFwcHJvdmVkIGZvciB0aGVzZSBwcm9qZWN0cywgb3BlbiB0aGUgYEVuY3J5cHQgUkVEQ2FwIFRva2VuLlJgIHNjcmlwdDoKaHR0cHM6Ly9yZXNlYXJjaC1naXQudWlvd2EuZWR1L3BldGVyc2VubGFiL1ItSW5pdGlhbFNldHVwL2Jsb2IvbWFzdGVyL1JFRENhcCUyMENyZWRlbnRpYWxzL0VuY3J5cHQlMjBSRURDYXAlMjBUb2tlbi5SCjEuIFJldmlzZSB0aGUgQVBJIHRva2VucyB0byByZWZsZWN0IHlvdXJzLCB0aGVuIHJ1biB0aGUgc2NyaXB0IHRvIHNhdmUgeW91ciBlbmNyeXB0ZWQgY3JlZGVudGlhbHMgb24gdGhlIGxhYiBzZXJ2ZXIgYW5kIHlvdXIgZW5jcnlwdGlvbiBrZXkgb24geW91ciBsb2NhbCBjb21wdXRlcgogICAgLSBWZXJpZnkgdGhhdCB0aGUgRW5jcnlwdGlvbiBLZXkgKGBSRURDYXAgRW5jcnlwdGlvbiBLZXkuUkRhdGFgKSB3YXMgc2F2ZWQgd2hlcmUgeW91IGludGVuZGVkIGl0IHRvIGJlIHNhdmVkIG9uIHlvdXIgbG9jYWwgY29tcHV0ZXIKICAgIC0gVmVyaWZ5IHRoYXQgYSBmaWxlIG5hbWVkIHdpdGggeW91ciBIYXdrSUQgd2FzIHNhdmVkIGhlcmU6IGAvL2xjLXJzLXN0b3JlMjQuaHBjLnVpb3dhLmVkdS9sc3NfaXRwZXRlcnNlbi9MYWIvU3R1ZGllcy9TY2hvb2wgUmVhZGluZXNzIFN0dWR5L0RhdGEgTWFuYWdlbWVudC9SRURDYXAvVG9rZW5zL2AKMS4gQ29weSB0aGUgRW5jcnlwdGlvbiBLZXkgKGBSRURDYXAgRW5jcnlwdGlvbiBLZXkuUkRhdGFgKSB0byB0aGUgY29tcGFyYWJsZSBsb2NhdGlvbiBvZiBhbnkgb3RoZXIgY29tcHV0ZXJzIHlvdSBvd24gdGhhdCB5b3UgcGxhbiB0byBhY2Nlc3MgdGhlIGRhdGEgZnJvbQogICAgLSBUaGUgZmlsZSBoYXMgdG8gYmUgaW4gdGhlIGNvbXBhcmFibGUgbG9jYXRpb24gKHJlbGF0aXZlIHRvIHRoZSBgcGF0aGAgdmFyaWFibGUgeW91IHNldCBpbiBgUnByb2ZpbGUuc2l0ZWApIG9mIGV2ZXJ5IGNvbXB1dGVyIGluIG9yZGVyIGZvciBpdCB0byBiZSBmb3VuZCBieSB0aGUgYEV4cG9ydCBEYXRhLlJgIHNjcmlwdC4KICAgIFRoZSBkZWZhdWx0IGxvY2F0aW9uIGlzOiBgZmlsZS5wYXRoKHBhdGgsICJHaXRIdWIvUi9EYXRhL1JFRENhcCBFbmNyeXB0aW9uIEtleS5SRGF0YSIpYCwgc28gaWYgYHBhdGhgIGlzIHNldCBhcyBgIkM6L1VzZXIvWW91ck5hbWUiYCwgdGhlIGZpbGUgd291bGQgYmUgc2F2ZWQgaW46IGBDOi9Vc2VyL1lvdXJOYW1lL0dpdEh1Yi9SL0RhdGEvUkVEQ2FwIEVuY3J5cHRpb24gS2V5LlJEYXRhYC4KICAgIFRoZSByZWNvbW1lbmRlZCBsb2NhdGlvbiBmb3IgYEdpdEh1YmAgcmVwb3MgaXMgdG8gY3JlYXRlIGEgZm9sZGVyIHRpdGxlZCBgR2l0SHViYCBpbiB5b3VyIGBEb2N1bWVudHNgIGZvbGRlciwgYW5kIHRvIHB1dCByZXBvcyBpbiB0aGUgYEdpdEh1YmAgZm9sZGVyOyBpdCBpcyAqTk9UKiByZWNvbW1lbmRlZCB0byBwdXQgYGdpdGAgcmVwb3MgaW4gYSBPbmVEcml2ZSBmb2xkZXIgYmVjYXVzZSBbYGdpdGAgZmlsZXMgdGVuZCBub3QgdG8gcGxheSBuaWNlIHdpdGggc3luY2luZyBzZXJ2aWNlc10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTkzMDUwMzMvd2h5LWlzLXB1dHRpbmctZ2l0LXJlcG9zaXRvcmllcy1pbnNpZGUtb2YtYS1kcm9wYm94LWZvbGRlci1ub3QtcmVjb21tZW5kZWQpIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjL1haNkYtNDNHMzsgZS5nLiwgT25lRHJpdmUsIERyb3Bib3gpCjEuIEFkZCB0aGUgU1JTIERhdGEgUHJvY2Vzc2luZyByZXBvIGZyb20gdGhlIGxhYiBkcml2ZSB0byB5b3VyIGBHaXRIdWIgRGVza3RvcGAgQXBwIChgLy9sYy1ycy1zdG9yZTI0LmhwYy51aW93YS5lZHUvbHNzX2l0cGV0ZXJzZW4vTGFiL1N0dWRpZXMvU2Nob29sIFJlYWRpbmVzcyBTdHVkeS9EYXRhIFByb2Nlc3NpbmdgKQogICAgLSBGb3IgaW5zdHJ1Y3Rpb25zLCBzZWUgdGhlIHNlY3Rpb24gb24gIltIb3cgdG8gYWRkIGEgcHJlLWV4aXN0aW5nIHJlcG8gZnJvbSB0aGUgbGFiIGRyaXZlIChSRFNTL25ldHdvcmsgc2hhcmUpIHRvIHlvdXIgY29tcHV0ZXJdKGh0dHBzOi8vcmVzZWFyY2gtZ2l0LnVpb3dhLmVkdS9QZXRlcnNlbkxhYi9SLUluaXRpYWxTZXR1cC8tL2Jsb2IvbWFzdGVyL0dpdExhYi9HaXRMYWIlMjBJbnN0cnVjdGlvbnMubWQjaG93LXRvLWFkZC1hLXByZS1leGlzdGluZy1yZXBvLWZyb20tdGhlLWxhYi1kcml2ZS1yZHNzbmV0d29yay1zaGFyZS10by15b3VyLWNvbXB1dGVyKSIgaGVyZTogaHR0cHM6Ly9yZXNlYXJjaC1naXQudWlvd2EuZWR1L3BldGVyc2VubGFiL1ItSW5pdGlhbFNldHVwL2Jsb2IvbWFzdGVyL0dpdExhYi9HaXRMYWIlMjBJbnN0cnVjdGlvbnMubWQKMS4gT3BlbiBgUlN0dWRpb2AgYnkgdXNpbmcgIlJ1biBhcyBBZG1pbmlzdHJhdG9yIiAoYWx3YXlzIG9wZW4gYFJTdHVkaW9gIGFzIGFuIGFkbWluaXN0cmF0b3Igc28gaXQgaGFzIHdyaXRlIGFjY2VzcyB0byB0aGUgcHJvZ3JhbSBmaWxlcyBkaXJlY3RvcnkpOwoxLiBPcGVuIHRoZSBgRXhwb3J0IERhdGEuUmAgc2NyaXB0IGluIFI6Cmh0dHBzOi8vcmVzZWFyY2gtZ2l0LnVpb3dhLmVkdS9wZXRlcnNlbmxhYi9zcnMvU1JTLURhdGFQcm9jZXNzaW5nL2Jsb2IvbWFzdGVyLzEuJTIwRXhwb3J0JTIwRGF0YS9FeHBvcnQlMjBEYXRhLlIKYFxcbGMtcnMtc3RvcmUyNC5ocGMudWlvd2EuZWR1XGxzc19pdHBldGVyc2VuXExhYlxTdHVkaWVzXFNjaG9vbCBSZWFkaW5lc3MgU3R1ZHlcRGF0YSBQcm9jZXNzaW5nXDEuIEV4cG9ydCBEYXRhXEV4cG9ydCBEYXRhLlJgCjEuIEVuc3VyZSB5b3VyIEhhd2tJRCBhbmQgbG9jYXRpb24gb2YgeW91ciBlbmNyeXB0aW9uIGtleSBpbiB0aGUgc2NyaXB0IGFyZSBjb3JyZWN0LCBhbmQgdGhlbiBydW4gdGhlIHNjcmlwdCB0byB2ZXJpZnkgdGhhdCB5b3UgY2FuIGV4cG9ydCBkYXRhIGZyb20gUkVEQ2FwCjEuIEZvciBhbnRpYWxpYXNlZCBwbG90cyBpbiBgUlN0dWRpb2AsIGNoYW5nZSB0aGUgR3JhcGhpY3MgYmFja2VuZCB0byBgQ2Fpcm9gOgpgVG9vbHMg4oaSIEdsb2JhbCBPcHRpb25zIOKGkiBHcmFwaGljc2AKCiMgTGFiIFBhY2thZ2UgeyNwZXRlcnNlbmxhYn0KClRoZSBbYHBldGVyc2VubGFiYCBwYWNrYWdlXShodHRwczovL2RldnBzeWxhYi5naXRodWIuaW8vcGV0ZXJzZW5sYWIpIGlzIGhlcmU6IGh0dHBzOi8vZGV2cHN5bGFiLmdpdGh1Yi5pby9wZXRlcnNlbmxhYi4KVG8gaW5zdGFsbCB0aGUgW2BwZXRlcnNlbmxhYmAgcGFja2FnZV0oaHR0cHM6Ly9kZXZwc3lsYWIuZ2l0aHViLmlvL3BldGVyc2VubGFiKSwgc2VlIGluc3RydWN0aW9ucyBbaGVyZV0oZGF0YU1hbmFnZW1lbnQuaHRtbCNsYWJGdW5jdGlvbnMpLgoKIyBJbnN0YWxsIFBhY2thZ2VzIHsjaW5zdGFsbFBhY2thZ2VzfQoKVG8gaW5zdGFsbCBhbmQgbG9hZCBgUmAgcGFja2FnZXMsIHNlZSB0aGUgaW5zdHJ1Y3Rpb25zIFtoZXJlXShkYXRhTWFuYWdlbWVudC5odG1sI2xvYWRJbnN0YWxsUGFja2FnZXMpLgoKIyBVcGRhdGUgUGFja2FnZXMgeyN1cGRhdGVQYWNrYWdlc30KClRvIHVwZGF0ZSBwYWNrYWdlcywgdXNlIHRoZSBmb2xsb3dpbmcgY29kZToKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEZBTFNFfQp1cGRhdGUucGFja2FnZXMoY2hlY2tCdWlsdCA9IFRSVUUpCmBgYAoKT25lIGluZGljYXRpb24gdGhhdCB0aGUgcGFja2FnZXMgbWlnaHQgbm90IGJlIHVwZGF0aW5nIHRvIHRoZSBsYXRlc3QgdmVyc2lvbiBpcyBzZWVpbmcgdGhlIHNhbWUgcGFja2FnZXMgc2hvd2luZyBhcyBuZWVkaW5nIGFuIHVwZGF0ZSBhZnRlciBoYXZpbmcgcnVuIHRoZSBgdXBkYXRlLnBhY2thZ2VzKClgIGZ1bmN0aW9uLgpJZiB0aGlzIGRvZXMgbm90IHVwZGF0ZSB0aGUgcGFja2FnZShzKSB0byB0aGUgbGF0ZXN0IHZlcnNpb24sIHlvdSBtYXkgbmVlZCB0byBpbnN0YWxsIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiB0aGUgcGFja2FnZShzKSBmcm9tIHNvdXJjZSAoc2VlIHRoZSBzZWN0aW9uIG9uICJbSW5pdGlhbCBTZXQgVXBdKCNzZXR1cCkiIG9mIGBSYCBmb3IgdGhlIHNvZnR3YXJlIG5lZWRlZCB0byBpbnN0YWxsIFIgcGFja2FnZXMgZnJvbSBzb3VyY2UpOgoKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRkFMU0V9CnVwZGF0ZS5wYWNrYWdlcyhjaGVja0J1aWx0ID0gVFJVRSwgdHlwZSA9ICJzb3VyY2UiKQpgYGAKCiMgVXBkYXRlIGBSYCB7I3VwZGF0ZVJ9CgpJbnN0cnVjdGlvbnMgYWRhcHRlZCBmcm9tOiBodHRwczovL21pcnJvci5sYXMuaWFzdGF0ZS5lZHUvQ1JBTi9iaW4vd2luZG93cy9iYXNlL3J3LUZBUS5odG1sI1doYXRfMDAyN3MtdGhlLWJlc3Qtd2F5LXRvLXVwZ3JhZGVfMDAzZiAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9XNVFXLU1BNlEpCgoxLiBVbmluc3RhbGwgYFJgCjEuIEluc3RhbGwgdGhlIG5ldyBgUmAgdmVyc2lvbiBpbnRvIGEgZGlyZWN0b3J5IHRoYXQgY29udGFpbnMgbm8gc3BhY2VzIChzZWUgU3RlcCAyIGluIHRoZSBbSW5pdGlhbCBTZXQgVXBdKCNzZXR1cCkgc2VjdGlvbiBhYm92ZSkKMS4gW1lvdSBvbmx5IG5lZWQgdG8gZG8gdGhpcyBzdGVwIGlmIHlvdSBpbnN0YWxsZWQgcGFja2FnZXMgaW4gdGhlIFItdmVyc2lvbi1zcGVjaWZpYyAiTGlicmFyeSIgZm9sZGVyIHJhdGhlciB0aGFuIHRoZSBjb21tb24vc2hhcmVkICJQYWNrYWdlcyIgZm9sZGVy4oCUdGhhdCBpcywgeW91IGRvbid0IG5lZWQgdG8gZG8gdGhpcyBzdGVwIGlmIHlvdSB1c2VkIHRoZSBsYWIncyBgUnByb2ZpbGUuc2l0ZWAgZmlsZSwgYXMgZGVzY3JpYmVkIGFib3ZlLCB3aGljaCBpbnN0YWxscyBwYWNrYWdlcyB0byB0aGUgY29tbW9uL3NoYXJlZCAiUGFja2FnZXMiIGZvbGRlcl06CiAgICAtIENvcHkgaW5zdGFsbGVkIHBhY2thZ2VzIGluIHRoZSAiTGlicmFyeSIgZm9sZGVyIHRvIHRoZSAiTGlicmFyeSIgZm9sZGVyIGluIHRoZSBuZXcgaW5zdGFsbGF0aW9uCjEuIEluIG5ldyBgUmAgdmVyc2lvbiBmb2xkZXIsIGNvcHkgdGhlIGN1cnJlbnQgYFJwcm9maWxlLnNpdGVgIGZpbGUgYXMgYSBiYWNrdXAgKGBScHJvZmlsZV9CQUNLVVAuc2l0ZWApIGFuZCBvdmVyd3JpdGUgdGhlIG9yaWdpbmFsIGZpbGUgd2l0aCB0aGUgbGFiJ3MgdmVyc2lvbiBvZiBgUnByb2ZpbGUuc2l0ZWAgZnJvbSBoZXJlOiBodHRwczovL3Jlc2VhcmNoLWdpdC51aW93YS5lZHUvUGV0ZXJzZW5MYWIvUi1Jbml0aWFsU2V0dXAvLS9ibG9iL21hc3Rlci9SJTIwU2V0dXAlMjBGaWxlcy9ScHJvZmlsZS5zaXRlCiAgICAtIGBSYCB3aWxsIHJ1biB0aGUgZmlsZSBuYW1lZCBgUnByb2ZpbGUuc2l0ZWAgYXQgaW5pdGlhbCBydW50aW1lLgoxLiBTZXQgdGhlIGV4ZWN1dGFibGVzIGZvciBgUmAgYW5kIGBSU3R1ZGlvYCB0byBhbHdheXMgcnVuIHdpdGggYWRtaW5pc3RyYXRvciBwZXJtaXNzaW9ucy4KICAgIC0gSWYgb24gV2luZG93cywgb3BlbiBGaWxlIEV4cGxvcmVyIGFuZCBmaW5kIHRoZSBtYWluIGV4ZWN1dGFibGUgb2YgYFJgIChgQzpcUlxSLVZFUlNJT05cYmluXFIuZXhlYCkgYW5kIGBSU3R1ZGlvYCAoYEM6XFByb2dyYW0gRmlsZXNcUlN0dWRpb1xiaW5cUlN0dWRpby5leGVgKS4KICAgIFJpZ2h0LWNsaWNrIGl0IHRvIG9wZW4gdGhlIGNvbnRleHR1YWwgbWVudS4KICAgIFRoZW4sIGNsaWNrIG9yIHRhcCBvbiAiUHJvcGVydGllcyIuCiAgICBJbiB0aGUgUHJvcGVydGllcyB3aW5kb3csIGdvIHRvIHRoZSBDb21wYXRpYmlsaXR5IHRhYi4KICAgIEF0IHRoZSBib3R0b20gb2YgdGhlIHdpbmRvdywgY2hlY2sgdGhlIGJveCBuZXh0IHRvIHRoZSAiUnVuIHRoaXMgcHJvZ3JhbSBhcyBhbiBhZG1pbmlzdHJhdG9yIiBvcHRpb24sIGFuZCB0aGVuIGNsaWNrIG9yIHRhcCBvbiBBcHBseSBvciBPSy4KMS4gTWFrZSBzdXJlIHlvdSBoYXZlIHRoZSBsYXRlc3QgdmVyc2lvbiBvZiB0aGUgdG9vbHMgbmVjZXNzYXJ5IHRvIGNvbXBpbGUgcGFja2FnZXMgZnJvbSBzb3VyY2UgKGkuZS4sIFJ0b29scyBmb3IgV2luZG93cyBvciBgUmAgQ29tcGlsZXIgVG9vbHMgZm9yIFJjcHAgb24gTWFjT1M7IHNlZSB0aGUgaW5zdHJ1Y3Rpb25zIGluIHRoZSBzZWN0aW9uIG9uIFtpbml0aWFsIHNldCB1cF0oI3NldHVwKSkKMS4gT3BlbiB0aGUgbmV3IGBSYCB2ZXJzaW9uIGFuZCBydW4gYHVwZGF0ZS5wYWNrYWdlcyhjaGVja0J1aWx0ID0gVFJVRSwgYXNrID0gRkFMU0UpYCwgYW5kIGluc3RhbGwgYW55IG5lY2Vzc2FyeSBwYWNrYWdlcwoxLiBDbG9zZSBSCjEuIERlbGV0ZSBhbnl0aGluZyBsZWZ0IG9mIHRoZSBvbGQgaW5zdGFsbGF0aW9uCgojIFN0eWxlIEd1aWRlIGFuZCBCZXN0IFByYWN0aWNlcyB7I2Jlc3RQcmFjdGljZXN9CgojIyBDcmVhdGUgYFJzdHVkaW8gUHJvamVjdGAKCkZvciBlYWNoIGRhdGEgYW5hbHlzaXMgcHJvamVjdCAoaS5lLiwgZWFjaCBbYEdpdExhYmAvYEdpdEh1YmBdKCNnaXQpIHJlcG8pLCBjcmVhdGUgYW4gUlN0dWRpbyBQcm9qZWN0LgpUaGlzIGhlbHBzIGtlZXAgeW91ciBwcm9qZWN0IGZpbGVzIG9yZ2FuaXplZC4KCiMjIFVzZSBgUmAgTm90ZWJvb2tzIGZvciAiQ29tcHV0YXRpb25hbCBOb3RlYm9va3MiCgpVc2luZyBgUmAgTm90ZWJvb2tzIGZvciAiQ29tcHV0YXRpb25hbCBOb3RlYm9va3MiIGlzIGhlbHBmdWwgZm9yIHJlcHJvZHVjaWJsZSBjb2RlIHRoYXQgY2FuIGJlIHNoYXJlZCB3aXRoIG90aGVycy4KVG8gY3JlYXRlIGNvbXB1dGF0aW9uYWwgbm90ZWJvb2tzIHNlZSB0aGUgYE1hcmtkb3duYCBzZWN0aW9uIG9uIFtjb21wdXRhdGlvbmFsIG5vdGVib29rc10obWFya2Rvd24uaHRtbCNjb21wdXRhdGlvbmFsTm90ZWJvb2spIGluIHRoZSBEYXRhIEFuYWx5c2lzIGd1aWRlcy4KCiMjIFNlcGFyYXRlIHNlY3Rpb25zIGluIGNvZGUKCi0gSW4gYFJgIHNjcmlwdHMsIHVzZSBzZWN0aW9ucy4KICAgIC0gVG8gaW5zZXJ0IGEgc2VjdGlvbiBpbiBgUlN0dWRpb2AsIHVzZSBgQ1RSTC1TaGlmdC1SYCBvciAiQ29kZSIgLSAiSW5zZXJ0IFNlY3Rpb24iCi0gSW4gYFJgIE5vdGVib29rcy9NYXJrZG93biwgdXNlIEhlYWRlcnMgYW5kIGNvZGUgY2h1bmtzLgogICAgLSBIZWFkZXJzOiAxLCAyLCBvciAzIHBvdW5kIHNpZ25zCiAgICAtIENvZGUgQ2h1bmtzOiBgQ3RybCtBbHQrSWA7IG9yIGNsaWNrICJJbnNlcnQiIGJ1dHRvbiB0aGVuICJSIgoKIyMgTmFtaW5nIHZhcmlhYmxlcwoKLSBVc2UgbWVhbmluZ2Z1bCB2YXJpYWJsZSBuYW1lczsgd2Ugd2FudCB0byBrbm93IHdoYXQgYSB2YXJpYWJsZSByZXByZXNlbnRzIHdpdGhvdXQgaGF2aW5nIHRvIGNvbnN1bHQgYW4gZXh0ZXJuYWwgY29kZWJvb2sgZm9yIGV2ZXJ5IHZhcmlhYmxlCi0gVmFyaWFibGUgbmFtZXMgc2hvdWxkIGluY2x1ZGUgdGhlIHByZWZpeCBmb3IgdGhlIG1lYXN1cmUgZm9sbG93ZWQgYnkgYW4gdW5kZXJzY29yZQogICAgLSBlLmcuLCBgY2JjbF9gIGZvciB0aGUgQ2hpbGQgQmVoYXZpb3IgQ2hlY2tsaXN0IHZhcmlhYmxlcwotIFVzZSBsb3dlciBjYW1lbCBjYXNlIGZvciB2YXJpYWJsZSBuYW1pbmcKICAgIC0gZS5nLiwgYHByZWZpeF90aGlzSXNUaGVWYXJpYWJsZU5hbWVgCi0gRG8gKipub3QqKiBpbmNsdWRlIHNwYWNlcyBpbiB2YXJpYWJsZSBuYW1lcwoKIyMgQ29tbWVudCBjb2RlIGZyZXF1ZW50bHkgYW5kIGNsZWFybHkhCgpJdCBpcyBpbXBvcnRhbnQgdG8gY29tbWVudCBjb2RlIGZyZXF1ZW50bHkgYW5kIGNsZWFybHkuCllvdSB3YW50IHlvdSAoYW5kIG90aGVycykgdG8gZWFzaWx5IGJlIGFibGUgdG8gdW5kZXJzdGFuZCB5b3VyIGNvZGUgaWYgeW91IGNvbWUgYmFjayB0byBpdCBzZXZlcmFsIHllYXJzIGxhdGVyIQoKIyMgRG9uJ3Qgc2F2ZSB5b3VyIHdvcmtzcGFjZSBpbWFnZSB7I2RvbnRTYXZlV29ya3NwYWNlfQoKRm9yIHJlcHJvZHVjaWJpbGl0eSBwdXJwb3NlcywgaXQgaXMgaW1wb3J0YW50IFsqKm5vdCoqIHRvIHNhdmUgeW91ciB3b3Jrc3BhY2UgaW1hZ2VdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLzIwMTcvMDQvdXNpbmctci1kb250LXNhdmUteW91ci13b3Jrc3BhY2UvKSAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy85U0NaLUw0REUpLgpJdCBpcyBiZXN0IHByYWN0aWNlcyB0byBiZWdpbiBgUmAgZWFjaCBzZXNzaW9uIHdpdGggYSBjbGVhbiB3b3Jrc3BhY2UuCklmIHRoZXJlIGlzIGEgYC5SZGF0YWAgZmlsZSBpbiB0aGUgc2FtZSBmb2xkZXIgYXMgdGhlIGBSc3R1ZGlvIFByb2plY3RgLCBSc3R1ZGlvIHdpbGwgYXV0b21hdGljYWxseSBsb2FkIHRoZSBvYmplY3RzIGludG8gdGhlIHdvcmtzcGFjZSBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBzZXNzaW9uLgpUaGlzIGlzIHByb2JsZW1hdGljIGJlY2F1c2UgdGhvc2Ugb2JqZWN0cyBjYW4gaW50ZXJhY3QvaW50ZXJmZXJlIHdpdGggdGhlIGNvZGUgYW5kIGNhbiBsZWFkIHRvIHByb2JsZW1zIHdpdGggcmVwbGljYWJpbGl0eSBmb3Igb3RoZXJzIHdobyBhcmUgcnVubmluZyB0aGUgY29kZSB3aXRob3V0IHRob3NlIG9iamVjdHMgaW4gdGhlIHdvcmtzcGFjZS4KV2hlbiB5b3UgZXhpdCBgUlN0dWRpb2AsIGBSU3R1ZGlvYCBhc2tzIGlmIHlvdSB3YW50IHRvICJTYXZlIHdvcmtzcGFjZSBpbWFnZSB0byBgW2ZpbGVwYXRoXS8uUmRhdGFgPyIKTWFrZSBzdXJlIHRvIHNlbGVjdCAiRG9uJ3QgU2F2ZSIhCkhvd2V2ZXIsIGRvIG1ha2Ugc3VyZSB0byBzYXZlIHlvdXIgYFJgIHNjcmlwdHMgYmVmb3JlIGV4aXRpbmcgUnN0dWRpby4KCiMgRGF0YSBNYW5hZ2VtZW50CgotIFRoZSBsYWIncyBbRGF0YSBBbmFseXNpcyBHdWlkZXMgb24gRGF0YSBNYW5hZ2VtZW50XShkYXRhTWFuYWdlbWVudC5odG1sKQotIFRpZHl2ZXJzZTogaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZwotIFVuaXZlcnNpdHkgb2YgSW93YSBXb3Jrc2hvcHMKCiMgU2F2aW5nIFBsb3RzCgpgcG5nKCk7IGRldi5vZmYoKWAKCi0gVGhlIGxhYidzIFtEYXRhIEFuYWx5c2lzIEd1aWRlcyBvbiBGaWd1cmVzXShmaWd1cmVzLmh0bWwpCgojIFNhdmluZyBPdXRwdXQKCmBSIE1hcmtkb3duYAoKLSBUaGUgbGFiJ3MgW0RhdGEgQW5hbHlzaXMgR3VpZGVzIG9uIE1hcmtkb3duXShtYXJrZG93bi5odG1sKQotIGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2xlc3Nvbi0xLmh0bWwgKGFyY2hpdmVkIGF0IGh0dHBzOi8vcGVybWEuY2MvOFNRSC1ONjhYKQotIGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL3JtYXJrZG93bi1jaGVhdHNoZWV0LnBkZiAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy8zTk1ULTRMMjUpCi0gaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLyAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9VSkM4LVpaVkMpCgojIFNob3J0Y3V0cwoKLSBSdW4gc2VsZWN0ZWQgbGluZShzKSBvZiBjb2RlOiBDdHJsICsgRW50ZXIKLSBDb21tZW50L3VuY29tbWVudCBjb2RlOiBDdHJsICsgU2hpZnQgKyBDCi0gUGlwZTogQ3RybCArIFNoaWZ0ICsgTQotIEluc2VydCBDb2RlIENodW5rOiBDdHJsICsgQWx0ICsgSQotIEFzc2lnbm1lbnQgb3BlcmF0b3I6IEFsdCArIC0gKGFsdC1kYXNoKQotIFNlbGVjdCBtdWx0aXBsZSBsaW5lczogQ3RybCArIEFsdCwgdXAgb3IgZG93bjsgb3IgQWx0ICsgZHJhZyBtb3VzZQotIFNlYXJjaDogQ3RybCArIFNoaWZ0ICsgRgotIFNob3cgYWxsIGtleWJvYXJkIHNob3J0Y3V0czogQWx0ICsgU2hpZnQgKyBLCgojIFN0YXRpc3RpY3MgRXhhbXBsZXMKCi0gW0JheWVzaWFuIEFuYWx5c2lzXShiYXllc2lhbi5odG1sKQotIFtEYXRhIE1hbmFnZW1lbnRdKGRhdGFNYW5hZ2VtZW50Lmh0bWwpCi0gW0RldmVsb3BtZW50YWwgU2NhbGluZ10oZGV2ZWxvcG1lbnRhbFNjYWxpbmcuaHRtbCkKLSBbRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpc10oZWRhLmh0bWwpCi0gW0ZhY3RvciBBbmFseXNpc10oZmFjdG9yQW5hbHlzaXMuaHRtbCkKLSBbSGllcmFyY2hpY2FsIExpbmVhciBNb2RlbGluZ10oaGxtLmh0bWwpCi0gW0l0ZW0gUmVzcG9uc2UgVGhlb3J5XShpcnQuaHRtbCkKLSBbTG9uZ2l0dWRpbmFsIERhdGEgQW5hbHlzaXNdKGxkYS5odG1sKQotIFtNZWRpYXRpb25dKHNlbS5odG1sI21lZGlhdGlvbikKLSBbTW9kZXJhdGlvbi9JbnRlcmFjdGlvbl0ocmVncmVzc2lvbi5odG1sI21vZGVyYXRpb24pCi0gW011bHRpcGxlIEltcHV0YXRpb25dKG11bHRpcGxlSW1wdXRhdGlvbi5odG1sKQotIFtQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzXShwY2EuaHRtbCkKLSBbUmVncmVzc2lvbl0ocmVncmVzc2lvbi5odG1sKQotIFtTdHJ1Y3R1cmFsIEVxdWF0aW9uIE1vZGVsaW5nXShzZW0uaHRtbCkKCiMgUnVubmluZyBTY3JpcHRzIEF1dG9tYXRpY2FsbHkgd2l0aCBXaW5kb3dzCgpodHRwczovL3d3dy5zcHNhbmRlcnNvbi5jb20vc3RldmVvbmRhdGEvcG9zdHMvMjAyMy0wNi0yOS9pbmRleC5odG1sIChhcmNoaXZlZCBhdCBodHRwczovL3Blcm1hLmNjLzlFWEstVzk5WSkKCmBSYCBzY3JpcHRzIGNhbiBiZSBydW4gYXV0b21hdGljYWxseS4KRm9yIGV4YW1wbGUsIGl0IGNhbiBiZSBoZWxwZnVsIHRvIGhhdmUgYW4gYFJgIG1hcmtkb3duIHJlcG9ydCBydW4gYXV0b21hdGljYWxseSBiZWZvcmUgdGhlIGRheSBiZWdpbnMuCgoxLiBPcGVuIHRoZSBgTm90ZXBhZGAgYXBwIGFuZCBjcmVhdGUgYSBmaWxlIHdpdGggdGhlIGZvbGxvd2luZyBzeW50YXguCiAgIC0gYExvY2F0aW9uIG9mIFIgZXhlY3V0YWJsZSBmaWxlXFIgQ01EIEJBVENIICJQYXRoIGxvY2F0aW9uIG9mIHNjcmlwdCB0aGF0IHNob3VsZCBiZSBhdXRvbWF0aWNhbGx5IHJ1biJgCiAgIC0gKipFeGFtcGxlOioqIAogICAtIGBDOlxSXDQuMS4zXGJpblxSIENNRCBCQVRDSCAiUjpcTGFiXFN0dWRpZXNcU2Nob29sIFJlYWRpbmVzcyBTdHVkeVxEYXRhIFByb2Nlc3NpbmdcNS4gUmVwb3J0c1xhdXRvbWF0aWNfcmVwb3J0c1xSdW5fUmVwb3J0c19hdXRvLlIiYAoxLiBTYXZlIHRoZSBmaWxlIGFzIGEgYC5iYXRgIGZpbGUgaW4gdGhlIGRlc2lyZWQgbG9jYXRpb24KMS4gT25jZSB0aGUgYC5iYXRgIGZpbGUgaGFzIGJlZW4gY3JlYXRlZCwgc2VhcmNoIGBXaW5kb3dzIFRhc2sgU2NoZWR1bGVyYCBpbiB0aGUgc2VhcmNoIGJhcgohW3Rhc2sgc2NoZWR1bGVyXShpbWFnZXMvdGFzay1zY2hlZHVsZXIucG5nKQoxLiBJbiB0aGUgYEFjdGlvbnNgIHNlbGVjdGlvbiBiYXIsIHNlbGVjdCBgQ3JlYXRlIGJhc2ljIFRhc2suLi5gCjEuIE5hbWUgdGhlIHRhc2sgYW5kIHByb3ZpZGUgYSBkZXNjcmlwdGlvbgoxLiBOZXh0LCBzZXQgdGhlIHRyaWdnZXIgZm9yIHRoZSBuZXcgdGFzayAoaS5lLiwgaG93IG9mdGVuIHRoZSB0YXNrIHNob3VsZCBydW4pCjEuIFNldCB0aGUgYWN0aW9uIGZvciB0aGUgdGFzayBieSBzZWxlY3RpbmcgYFN0YXJ0IGEgcHJvZ3JhbWAKMS4gVW5kZXIsIGBQcm9ncmFtL3NjcmlwdGAgYnJvd3NlIHRvIHRoZSBgLmJhdGAgZmlsZSB0aGF0IHdhcyBjcmVhdGVkIGluIHN0ZXAgMSBhbmQgc2VsZWN0IGBOZXh0YAoxLiBDbGljayBgRmluaXNoYCBhbmQgdGhlIHNjcmlwdCBpcyBub3cgY29uZmlndXJlZCB0byBydW4gYXV0b21hdGljYWxseQoxLiAqKk5vdGU6IFdoZW4gYFJgIGlzIHVwZGF0ZWQsIHRoZSBwYXRoIHRvIHRoZSBgYmluYCBmb2xkZXIgd2l0aGluIGBSYCBuZWVkcyB0byBiZSB1cGRhdGVkIHRvIHJlZmxlY3QgYW4gYWNjdXJhdGUgYWJzb2x1dGUgcGF0aCB0byBSLioqCiAgICAtIEV4YW1wbGU6IGBDOlxSXDQuMS4zXGJpblxSIENNRCBCQVRDSGAgY2hhbmdlZCB0byBgQzpcUlw0LjMuMFxiaW5cUiBDTUQgQkFUQ0hgCgojIyBUcm91Ymxlc2hvb3RpbmcKCiMjIyBQYW5kb2MgZXJyb3IKClRoaXMgZXJyb3IgbWF5IGFwcGVhciBpZiB5b3UgYXJlIGF0dGVtcHRpbmcgdG8gcmVuZGVyIGEgbWFya2Rvd24gZmlsZQoKYGBgCnBhbmRvYyB2ZXJzaW9uIDEuMTIuMyBvciBoaWdoZXIgaXMgcmVxdWlyZWQgYW5kIHdhcyBub3QgZm91bmQuCmBgYAoKVGhlIHNvbHV0aW9uIHRvIHRoaXMgcHJvYmxlbSBbY2FuIGJlIGZvdW5kIGF0IHRoaXMgbGlua10oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjg0MzI2MDcvcGFuZG9jLXZlcnNpb24tMS0xMi0zLW9yLWhpZ2hlci1pcy1yZXF1aXJlZC1hbmQtd2FzLW5vdC1mb3VuZC1yLXNoaW55KSAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9ZWDU3LUJQUlMpCgojIFJlYWRpbmcgUGFzc3dvcmQgUHJvdGVjdGVkIEV4Y2VsIERhdGFiYXNlcwoKQSBoZWxwZnVsIHBvc3QgY2FuIGJlIGZvdW5kIGhlcmU6IDxicj4KCmh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzM1ODUyNzIyL2hvdy1kby15b3UtcmVhZC1hLXBhc3N3b3JkLXByb3RlY3RlZC1leGNlbC1maWxlLWludG8tciAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy9VMzJaLTIyVkUpCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJleGNlbC5saW5rIikKCmxpYnJhcnkoImV4Y2VsLmxpbmsiKQoKcGFzc3dvcmRQcm90ZWN0ZWRCb29rIDwtIHhsLnJlYWQuZmlsZShmaWxlLnBhdGgoImZ1bGwgcGF0aCB0byB3b3JrYm9vayIpLCAjRnVsbCBwYXRoIHRvIHdvcmtib29rCnBhc3N3b3JkID0gInBhc3MiLCAjcGFzc3dvcmQKd3JpdGUucmVzLnBhc3N3b3JkPSJwYXNzIikgI3dyaXRpbmcgdGhlIHJlc2V0IHBhc3N3b3JkCmBgYAoKIyBTZW5kaW5nIHNsYWNrcyB3aXRoIGBSYAoKT2NjYXNpb25hbGx5LCBpdCBjYW4gYmUgaGVscGZ1bCB0byBzZW5kIGEgU2xhY2sgbWVzc2FnZSB1c2luZyBgUmAuCkZvciBleGFtcGxlLCBpZiBhIHNjcmlwdCBkb2VzIG5vdCBydW4sIGEgU2xhY2sgbWVzc2FnZSBjYW4gYmUgc2VudCB0byBpbmZvcm0gdGhlIGFwcHJvcHJpYXRlIHRlYW0gbWVtYmVycy4KW1RoZXNlIGluc3RydWN0aW9uc10oaHR0cHM6Ly93d3cuaW5mb3dvcmxkLmNvbS9hcnRpY2xlLzM0MDI2NTcvaG93LXRvLXNsYWNrLWZyb20tci5odG1sKSAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy85Q1dKLUo1WlQpIGNhbiBsYXJnZWx5IGJlIGZvbGxvd2VkIHRvIHNldCB1cCBgUmAgdG8gc2VuZCBTbGFjayBtZXNzYWdlcy4KSG93ZXZlciwgdGhlcmUgYXJlIHNvbWUgZGlmZmVyZW5jZXM6CgoxLiBXaGVuIHNldHRpbmcgdXAgdGhlIGNvbmZpZ3VyYXRpb24gZmlsZSwgdXNlIHRoZSBiZWxvdyB0ZW1wbGF0ZS4KVGhlIHNsYWNrIEFQSSB0b2tlbiBzaG91bGQgYmUgcGxhY2VkIGluIHRoZSBgdG9rZW5gIGNhdGVnb3J5LgogICAgLSBOb3RlIHRoZSB0b2tlbiB3aWxsIG5lZWQgdG8gYmUgdXBkYXRlZCBldmVyeSAzMCBkYXlzLgogICAgWW91IGNhbiBnZW5lcmF0ZSBhIG5ldyB0b2tlbiBieSBuYXZpZ2F0aW5nIHRvIHRoZSBbU2xhY2sgQVBJXShodHRwczovL2FwaS5zbGFjay5jb20vYXBwcykgYW5kIHNlbGVjdGluZyBgT2F1dGggJiBQZXJtaXNzaW9uc2AKICAgIC0gIVtzbGFjayBwaWN0dXJlXShpbWFnZXMvc2xhY2tBUEkucG5nKQoKYGBgCnRva2VuOiBZT1VSX0ZVTExfQVBJX1RPS0VOCmNoYW5uZWw6ICNnZW5lcmFsCnVzZXJuYW1lOiBzbGFja3IKaW5jb21pbmdfd2ViaG9va191cmw6IGh0dHBzOi8vaG9va3Muc2xhY2suY29tL3NlcnZpY2VzL1hYWFhYL1hYWFhYL1hYWFhYCmBgYAoKT25jZSB0aGUgY29uZmlndXJhdGlvbiBpcyBjb21wbGV0ZSwgaXQgaXMgcG9zc2libGUgdG8gc2VuZCBtZXNzYWdlcy4gCkZvciBub3csIHdlIGhhdmUgZm91bmQgaXQgaGVscGZ1bCB0byBlbWJlZCB0aGUgc2xhY2tzIGluIHRoZSBgdHJ5Q2F0Y2hgIGZ1bmN0aW9uLiAKCmBgYHtyLCBldmFsID0gRkFMU0V9CnRyeUNhdGNoKApDT0RFIFlPVSBXQU5UIFRPIFJVTiwKZXJyb3IgPSBmdW5jdGlvbihlKQp7CiAgI21lc3NhZ2UgdG8gc2VuZCBpZiB0aGUgY29kZSBkb2Vzbid0IHJ1bgogIG15X21lc3NhZ2UgPC0gcGFzdGUoICJleGFtcGxlIG1lc3NhZ2UiKQogIHNsYWNrcl9tc2cobXlfbWVzc2FnZSwgY2hhbm5lbCA9ICIjcmVjcnVpdG1lbnQiKQp9KQpgYGAKCiMjIFNsYWNraW5nIFNwZWNpZmljIFVzZXJzCgpJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHNsYWNrIHNwZWNpZmljIHVzZXJzIHdpdGggaW5zdHJ1Y3Rpb25zIGZvdW5kIFthdCB0aGlzIGxpbmtdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzMyNDE5NzU2L2hvdy1kby15b3UtdGFnLXBlb3BsZS13aXRoLWEtc2xhY2stYm90KSAoYXJjaGl2ZWQgYXQgaHR0cHM6Ly9wZXJtYS5jYy81OVU1LVY0R1EpLgoKIyBSZXBsYWNpbmcgYC8vbmAgd2l0aCBhIHNwYWNlCgpNYW55IG5vdGVzIGluIHByb2plY3RzIHRoYXQgYXJlIGV4cG9ydGVkIGZyb20gUkVEQ2FwIGNvbWUgd2l0aCBzcGFjZXMgZGVub3RlcyBhcyBgLy9uYC4KVXNlIHRoZSBiZWxvdyBjb2RlIHRvIG1ha2UgdGhlc2UgZmllbGRzIG1vcmUgcmVhZGFibGUgaW4gdGhlIGZ1dHVyZS4KCmBgYHtyLCBldmFsID0gRkFMU0V9CmdzdWIoJ1xcbicsICcsICcsIGRmJG5vdGVzRmllbGQpCmBgYAoKIyBXb3JraW5nIHdpdGggYFJgIG9uIGEgTmV0d29yayBEcml2ZQoKV2hlbiB3b3JraW5nIHdpdGggYFJgIG9uIGEgbmV0d29yayBkcml2ZSwgaXQgbWF5IGJlIGhlbHBmdWwgdG8gY29uZmlndXJlIHRoZSBwcm9qZWN0IHRvIHN0b3JlIGAuUnByb2oudXNlcmAgb24gdGhlIGxvY2FsIGBDOi9gIGRyaXZlIHJhdGhlciB0aGFuIG9uIHRoZSBuZXR3b3JrIGRyaXZlLCB3aGljaCByZXN1bHRzIGluIHNsb3cgZXhlY3V0aW9uIHRpbWVzLgoKRm9yIG1vcmUgaW5mbzoKCi0gaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcnN0dWRpby9wdWxsLzE0ODc1Ci0gaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vcnN0dWRpby9pc3N1ZXMvMTQ2MTkjaXNzdWVjb21tZW50LTIxODk4NTU1OTgKLSBodHRwczovL2dpdGh1Yi5jb20vcnN0dWRpby9yc3R1ZGlvL2lzc3Vlcy8xMDQxNyNpc3N1ZWNvbW1lbnQtMjE5OTkzMDM4NQoKIyBQYWNrYWdlIERldmVsb3BtZW50CgojIyBXb3JraW5nIHdpdGggYHJlbnZgIGZvciBQYWNrYWdlIE1hbmFnZW1lbnQKCmByZW52YCBpcyB1c2VkIGZvciByZXByb2R1Y2liaWxpdHksIGJ5IGhlbHBpbmcgd2l0aCBwYWNrYWdlIG1hbmFnZW1lbnQgKHRyYWNraW5nIHBhY2thZ2UgdmVyc2lvbnMsIGV0Yy4pOgoKaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9yZW52L2FydGljbGVzL3JlbnYuaHRtbAoKQmVmb3JlIHVwZGF0aW5nIGEgcGFja2FnZSBsb2NhbGx5LCBtYWtlIHN1cmUgdGhhdCBpdCBpcyBhdmFpbGFibGUgaW4gdGhlIFBvc2l0IFBhY2thZ2UgTWFuYWdlciAoc28gaXQgY2FuIGJlIGF2YWlsYWJsZSB0byBHaXRIdWIgQWN0aW9ucyk6CgpodHRwczovL3BhY2thZ2VtYW5hZ2VyLnBvc2l0LmNvCgojIyMgVXBkYXRpbmcgdGhlIFBhY2thZ2UKClRvIHVwZGF0ZSB0aGUgcGFja2FnZSwgcnVuIHRoZSBmb2xsb3dpbmcgaW4gYFJgOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyAxLiBVcGRhdGUgcGFja2FnZXMgaW4gcGFja2FnZSBlbnZpcm9ubWVudApyZW52Ojp1cGdyYWRlKCkKcmVudjo6dXBkYXRlKCkKcmVudjo6c25hcHNob3QoKQoKIyAyLiBBZGQvZWRpdCBjb2RlCgojIDMuIFVwZGF0ZSBkb2N1bWVudGF0aW9uCnJveHlnZW4yOjpyb3h5Z2VuaXNlKCkKCiMgNC4gVXBkYXRlIHBhY2thZ2UgdmVyc2lvbgp1c2V0aGlzOjp1c2VfdmVyc2lvbigpCmBgYAoKVGhlbiwgYnVpbGQgdGhlIHBhY2thZ2U6IEN0cmwtU2hpZnQtQgoKVGhlbiwgaW5zdGFsbCB0aGUgcGFja2FnZToKCmBgYHtyLCBldmFsID0gRkFMU0V9CnJlbnY6Omluc3RhbGwoIkM6L1IvUGFja2FnZXMvcGV0ZXJzZW5sYWIiKSAjUEMKcmVudjo6aW5zdGFsbCgiL0xpYnJhcnkvRnJhbWV3b3Jrcy9SLmZyYW1ld29yay9QYWNrYWdlcy9wZXRlcnNlbmxhYiIpICNNYWMKYGBgCgojIyMgSW5zdGFsbGluZyBQYWNrYWdlcwoKVG8gaW5zdGFsbCBuZXcgcGFja2FnZXMgaW4gdGhlIHBhY2thZ2UgZW52aXJvbm1lbnQsIHJ1biB0aGUgZm9sbG93aW5nIGluIGBSYDoKCmBgYHtyLCBldmFsID0gRkFMU0V9CnJlbnY6Omluc3RhbGwoIk5BTUVfT0ZfUEFDS0FHRSIpCmBgYApvcjoKYGBge3IsIGV2YWwgPSBGQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygiTkFNRV9PRl9QQUNLQUdFKQpgYGAKCiMjIGBSIENNRCBjaGVja2AgeyNyQ21kQ2hlY2t9CgoxLiBCdWlsZCB0aGUgc291cmNlIHBhY2thZ2UKICAgIC0gY2xpY2sgb24gdGhlICJCdWlsZCIgdGFiIGluIHRoZSB0b3AtcmlnaHQgcGFuZSBvZiBSU3R1ZGlvLCBhbmQgdGhlbiBjbGljayAiQnVpbGQgU291cmNlIFBhY2thZ2UiCjEuIE9wZW4gdGVybWluYWwgaW4gUlN0dWRpbwogICAgLSBBZnRlciB0aGUgcGFja2FnZSBpcyBidWlsdCwgb3BlbiBhIHRlcm1pbmFsIHdpbmRvdyBkaXJlY3RseSBpbiBSU3R1ZGlvIGJ5IGNsaWNraW5nIG9uIHRoZSAiVGVybWluYWwiIHRhYiBhdCB0aGUgYm90dG9tIG9mIFJTdHVkaW8KMS4gUnVuIGBSIENNRCBjaGVjayAtLWFzLWNyYW5gCiAgICAtIEluIHRoZSB0ZXJtaW5hbCB3aW5kb3csIG5hdmlnYXRlIHRvIHRoZSBkaXJlY3Rvcnkgd2hlcmUgeW91ciBwYWNrYWdlIHNvdXJjZSBpcyBsb2NhdGVkLgogICAgVGhlbiwgcnVuIGBSIENNRCBjaGVjayAtLWFzLWNyYW5gIGZvbGxvd2VkIGJ5IHRoZSBuYW1lIG9mIHlvdXIgcGFja2FnZSB0YXJiYWxsLiBGb3IgZXhhbXBsZToKCkJ1aWxkIGAudGFyLmd6YCBmaWxlOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZGV2dG9vbHM6OmJ1aWxkKHBrZyA9ICJEOi9Eb2N1bWVudHMvR2l0SHViL3BldGVyc2VubGFiIikKYGBgCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpSIENNRCBjaGVjayAtLWFzLWNyYW4gcGV0ZXJzZW5sYWJfMS4wLjAudGFyLmd6CmBgYAoKSWYgZXJyb3JzIGNvbXBpbGluZyB0aGUgUERGIG1hbnVhbDoKCmBgYHtyLCBldmFsID0gRkFMU0V9ClIgQ01EIFJkMnBkZiAuIC0tb3V0cHV0PW1hbi9maWd1cmVzL21hbnVhbC5wZGYgLS1mb3JjZSAtLW5vLXByZXZpZXcgLS1uby1jbGVhbgpgYGAKCiMjIyBUcm91Ymxlc2hvb3RpbmcKCiMjIyMgYG5vIHZpc2libGUgYmluZGluZyBmb3IgZ2xvYmFsIHZhcmlhYmxlYDsgYFVuZGVmaW5lZCBnbG9iYWwgZnVuY3Rpb25zIG9yIHZhcmlhYmxlc2AKCkZvciBleGFtcGxlOgpgYGByCm5vIHZpc2libGUgYmluZGluZyBmb3IgZ2xvYmFsIHZhcmlhYmxlCiAgICAnbW9kZXJhdG9yVmFsX2NlbnRlcmVkJwogIFVuZGVmaW5lZCBnbG9iYWwgZnVuY3Rpb25zIG9yIHZhcmlhYmxlczoKICAgIG1vZGVyYXRvclZhbF9jZW50ZXJlZCBwcmVkaWN0b3JWYWxfY2VudGVyZWQKYGBgCgpTb2x1dGlvbjogc2V0IGVhY2ggdmFyaWFibGUgdG8gYE5VTExgIGluIHRoZSBwYWNrYWdlIGZ1bmN0aW9uIGJlZm9yZSBpdCBpcyBtZW50aW9uZWQuCkZvciBleGFtcGxlOgoKYGBgcgpwcmVkaWN0b3JWYWxfY2VudGVyZWQgPC0gbW9kZXJhdG9yVmFsX2NlbnRlcmVkIDwtIE5VTEwKYGBgCgojIyBgUiBDTUQgY2hlY2tgIHZpYSBHaXRIdWIgQWN0aW9ucwoKYGBge3IsIGV2YWwgPSBGQUxTRX0KdXNldGhpczo6dXNlX2dpdGh1Yl9hY3Rpb24oImNoZWNrLXN0YW5kYXJkIikKYGBgCgojIyBVc2VmdWwga2V5Ym9hcmQgc2hvcnRjdXRzIGZvciBwYWNrYWdlIGF1dGhvcmluZzoKCiBJbnN0YWxsIFBhY2thZ2U6ICAgICAgICAgICAnQ3RybCArIFNoaWZ0ICsgQicKIAogQ2hlY2sgUGFja2FnZTogICAgICAgICAgICAgJ0N0cmwgKyBTaGlmdCArIEUnCiAKIFRlc3QgUGFja2FnZTogICAgICAgICAgICAgICdDdHJsICsgU2hpZnQgKyBUJwoKYGBge3IsIGV2YWwgPSBGQUxTRX0KcmVudjo6aW5zdGFsbCgiQzovUi9QYWNrYWdlcy9wZXRlcnNlbmxhYiIpCnJlbnY6OnNuYXBzaG90KCkKcmVudjo6aW5zdGFsbCgpCmBgYAoKIyMgYHBrZ2Rvd25gCgpSdW4gb25jZSB0byBjb25maWd1cmUgeW91ciBwYWNrYWdlIHRvIHVzZSBwa2dkb3duOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KdXNldGhpczo6dXNlX3BrZ2Rvd24oKQpgYGAKClRoZW4gdXNlIGBwa2dkb3duYCB0byBidWlsZCB5b3VyIHdlYnNpdGU6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwa2dkb3duOjpidWlsZF9zaXRlKCkKYGBgCgojIyBTdGVwcyB0byBBZGQgRnVuY3Rpb25zCgoxLiBBZGQgYC5SYCBmaWxlIHdpdGggdGhlIGZ1bmN0aW9uCjEuIEFkZCB0aGUgZnVuY3Rpb24gdG8gdGhlIGBfcGtnZG93bi55bWxgIGZpbGUKMS4gVXBkYXRlIHZlcnNpb24gbnVtYmVyCjEuIGByZW52Ojp1cGdyYWRlKClgCjEuIGByZW52Ojp1cGRhdGUoKWAKMS4gYHJlbnY6OnNuYXBzaG90KClgCjEuIGByb3h5Z2VuMjo6cm94eWdlbmlzZSgpYAoxLiBJbnN0YWxsIFBhY2thZ2U6ICdDdHJsICsgU2hpZnQgKyBCJwoxLiBDaGVjayBQYWNrYWdlOiAnQ3RybCArIFNoaWZ0ICsgRScKMS4gYFIgQ01EIGNoZWNrYAoxLiBDb21taXQgYW5kIHB1c2ggY2hhbmdlcwoxLiBVcGRhdGUgcmVsZWFzZSB2ZXJzaW9uIGluIEdpdEh1YgoKIyMgQWRkIHN1Yi1wYWNrYWdlcwoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZGV2dG9vbHM6OmJ1aWxkKHBrZyA9ICJEOi9Eb2N1bWVudHMvR2l0SHViL3BldGVyc2VubGFiL2luc3QvZXh0ZGF0YS90ZXN0cGFja2FnZTEiKQpkZXZ0b29sczo6YnVpbGQocGtnID0gIkQ6L0RvY3VtZW50cy9HaXRIdWIvcGV0ZXJzZW5sYWIvaW5zdC9leHRkYXRhL3Rlc3RwYWNrYWdlMiIpCgppbnN0YWxsLnBhY2thZ2VzKCJEOi9Eb2N1bWVudHMvR2l0SHViL3BldGVyc2VubGFiL2luc3QvZXh0ZGF0YS90ZXN0cGFja2FnZTFfMC4xLjAudGFyLmd6IiwgcmVwb3MgPSBOVUxMLCBzb3VyY2UgPSBUUlVFKQppbnN0YWxsLnBhY2thZ2VzKCJEOi9Eb2N1bWVudHMvR2l0SHViL3BldGVyc2VubGFiL2luc3QvZXh0ZGF0YS90ZXN0cGFja2FnZTJfMC4xLjAudGFyLmd6IiwgcmVwb3MgPSBOVUxMLCBzb3VyY2UgPSBUUlVFKQoKcmVtb3Rlczo6aW5zdGFsbF9sb2NhbCgiRDovRG9jdW1lbnRzL0dpdEh1Yi9wZXRlcnNlbmxhYi9pbnN0L2V4dGRhdGEvdGVzdHBhY2thZ2UyXzAuMS4wLnRhci5neiIpCnJlbW90ZXM6Omluc3RhbGxfbG9jYWwoIkQ6L0RvY3VtZW50cy9HaXRIdWIvcGV0ZXJzZW5sYWIvaW5zdC9leHRkYXRhL3Rlc3RwYWNrYWdlMl8wLjEuMC50YXIuZ3oiKQpgYGAKCiMjIFN1Ym1pdCBQYWNrYWdlIHRvIENSQU4KCmh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3N1Ym1pdC5odG1sCgojIyBSZXNvdXJjZXMKCk9mZmljaWFsIGRvY3VtZW50YXRpb24gZm9yIENSQU46CgotIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2RvYy9tYW51YWxzL1ItZXh0cy5odG1sCgpVbm9mZmljaWFsIGRvY3VtZW50YXRpb246CgotIGh0dHBzOi8vcnB1YnMuY29tL1lhUnJyL3JwYWNrYWdlaW50cm8KLSBodHRwczovL3ItcGtncy5vcmcKLSBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9kb2MvY29udHJpYi9MZWlzY2gtQ3JlYXRpbmdQYWNrYWdlcy5wZGYKLSBodHRwczovL3BvcnRhbC5zdGF0cy5veC5hYy51ay91c2VyZGF0YS9ydXRoL0FQVFMyMDEyL1Jjb3Vyc2UxMC5wZGYKLSBodHRwczovL3dlYi5taXQuZWR1L2luc29uZy93d3cvcGRmL3JwYWNrYWdlX2luc3RydWN0aW9ucy5wZGYKLSBodHRwczovL2hpbGFyeXBhcmtlci5jb20vMjAxNC8wNC8yOS93cml0aW5nLWFuLXItcGFja2FnZS1mcm9tLXNjcmF0Y2gvCgojIyMgRm9yIFBhY2thZ2UgRGV2ZWxvcG1lbnQgVGFza3MKCi0gaHR0cHM6Ly91c2V0aGlzLnItbGliLm9yZy9pbmRleC5odG1sCgojIyMgRm9yIERvY3VtZW50YXRpb24KCi0gaHR0cHM6Ly9naXRodWIuY29tL3ItbGliL3JveHlnZW4yI3JveHlnZW4yCi0gaHR0cHM6Ly9oaWxhcnlwYXJrZXIuY29tLzIwMTQvMDQvMjkvd3JpdGluZy1hbi1yLXBhY2thZ2UtZnJvbS1zY3JhdGNoLwoKIyMjIENyZWF0aW5nIFZpZ25ldHRlcwoKLSBodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24tY29va2Jvb2svcGFja2FnZS12aWduZXR0ZS5odG1sCgojIyMgRm9yIExpY2Vuc2luZwoKLSBodHRwczovL3VzZXRoaXMuci1saWIub3JnL3JlZmVyZW5jZS9saWNlbnNlcy5odG1sCg==
7.5 Comment code frequently and clearly!
It is important to comment code frequently and clearly. You want you (and others) to easily be able to understand your code if you come back to it several years later!