In this project I will be cleaning the Nashville Housing dataset,
that was shared by AlexTheAnalyst trough GitHub using MySQL queries with
R. The dataset is available here
This particular project could have been done completely with R
because I have the .csv file available. However, I decided to make this
project out of the personal interest of learning and illustrating how to
use this amazing tools, MySQL and R, together to query our databases,
clean data, and use the results immediately for analysis and charts. I
believe this concepts can be very useful for me in my future career and
for others.
Notes on importing data to MySQl database
There are multiple ways to import a cvs file into a database. I
believe one of the easiest ways is to convert the cvs file into an sql
file using websites like convertcsv.com,
provided the dataset is not too big (like this one that has 56477 rows),
for different reasons.
First of all, you can customize pretty much everything, including the
data type of the fields, and very especially the date format, which can
save you a lot of time after importing your file. The date in a csv file
usually doesn’t match the format used by MySQL of YYYY-MM-DD and this
creates problems very often during the importation or need to be fixed
after importation.
Second it is very easy to use and fast. It creates the CREATE TABLE
and INSERT INTO statements. After downloading the .sql file, I opened it
with VS Code to checked everything was correct. In this case, the last
three rows were all blanks, so I removed them. Then I just created the
table by simply executing the sql statements from a file using the
source command via the command line like mysql> source file_name like
this:
mysql> source /Users/alexandravelez/Desktop.realestate.sql
Setting up the connection with MySQL and R
There are two options for connecting R to a MySQL database: 1. Using
the odbc package with a database driver 2. Using the RMariaDB
package
I used the first method. I installed MySQL community server on my
mac, MySQL Workbench, and iODBC driver manager. Credits to this video that shows
an easy and quick way to set everything up without even needing the
terminal.
Then I installed the R packages:
The DBI package helps connecting R to database management systems
(DBMS). The odbc package provides a DBI-compliant interface to Open
Database Connectivity (ODBC) drivers. It allows for an efficient, easy
way to setup connection to any database using an ODBC driver, including
SQL Server, Oracle, MySQL, PostgreSQL, SQLite and others. Tidyverse to
use dplyr, dbplyr and ggplot2. Dbplyr is included in the tidyverse and
converts dplyr into SQL code.
Connecting R to the database:
library("DBI")
con <- dbConnect(odbc(), Driver = "/usr/local/mysql-connector-odbc-8.2.0-macos13-arm64/lib/libmyodbc8a.so",
Server = "localhost", Database = "sys", UID = "root", PWD = "testDB_123",
Port = 3306, Password = "password")
The connection was stored in an object named con, and I can see under
connections all my SQL databases.
I can call dbListTables(con) to see all the tables in my sys
database. The name of the table I will work with is ‘realestate’.
I can create a direct link with a table using tbl() from dplyr. This
table is not in R yet, it is just a connection to a table that is in the
database
realestate_db <- tbl(con, "realestate")
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
realestate_db
R displays also Source: table [?? x 19] Database: mysql
[root@:/sys] because R does not know how many rows are in this table, it
just take a look to the first rows of the table that is in the
database.
To bring the realestate table to R I have to use the collect ().
Now I have a data frame exactly like if I used read.csv()
Checking for nulls
Creating a query to look for nulls in UniqueID. In this case I stored
the query in an object to pass it to a dbGetQuery function, but
alternatively I can pass the query directly into the function.
null_UniqueID_query <- "SELECT *
FROM realestate
WHERE UniqueID IS NULL"
Now I will use the dbGetQuery function from de DBI package to pass my
query. The result is a a data frame that can be stored to work with in
the future.
dbGetQuery(con, null_UniqueID_query)
Creating a query to look for nulls in ParcelID
null_ParcelID_query <- "SELECT *
FROM realestate
WHERE ParcelID IS NULL"
Passing the query into the dbGetQuery function:
dbGetQuery(con, null_ParcelID_query)
Creating a query to look for nulls in LandUse:
null_LandUse_query <- "SELECT *
FROM realestate
WHERE LandUse IS NULL"
Passing the query into the dbGetQuery function:
dbGetQuery(con, null_LandUse_query)
I got 0 nulls for UniqueID, Parcel ID and LandUse.
Creating a query to look for nulls in PropertyAddress:
null_PropertyAddress_query <- "SELECT *
FROM realestate
WHERE PropertyAddress IS NULL"
dbGetQuery(con, null_PropertyAddress_query)
I got 29 nulls on the PropertyAddress column.
Populating the nulls in the PropertyAddress.
Taking a closer look to the parcel ID for some of the property
address nulls:
ParceID_query <- "SELECT *
FROM realestate
WHERE ParcelID = '052 01 0 296.00'"
dbGetQuery(con, ParceID_query)
ParcelID_query2 <- "SELECT *
FROM realestate
WHERE ParcelID = '093 08 0 054.00'"
dbGetQuery(con, ParcelID_query2)
When inspecting the nulls in PropertyAddress we can see that there
are several properties with the same ParcelID but one of them without a
Property address. In this case this information can be completed. To do
this we can create a joint to the realestate table to itself and obtain
the values where the parcel id is the same but the propertyaddress is
complete.
With the IFNULL function we want to replace the null value with the
property address of our match in the joint
joint_query <- "SELECT a.ParcelID, a.PropertyAddress, b.ParcelID, b.PropertyAddress, IFNULL(a.PropertyAddress, b.PropertyAddress) as populate_address
FROM sys.realestate a
JOIN sys.realestate b
on a.ParcelID = b.ParcelID
AND a.UniqueID <> b.UniqueID
WHERE
a.PropertyAddress is NULL;"
joint <- dbGetQuery(con, joint_query)
joint
Now I want to populate the nulls with the results that we got. For
this I have to pass an update statement using the dbExecute function.
For SQL queries that affect the underlying database, such as UPDATE,
DELETE, INSERT INTO, and DROP TABLE, DBI provides two functions.
dbExecute() passes the SQL statement to the DBMS for execution and
returns the number of rows affected. dbSendStatement() performs in the
same manner, but returns a result object. Call dbGetRowsAffected() with
the result object to get the count of the affected rows. You then need
to call dbClearResult() with the result object afterwards to release
resources.
dbExecute() is a convenience function that calls dbSendStatement(),
dbGetRowsAffected(), and dbClearResult().
dbExecute(
con,
"UPDATE sys.realestate a
JOIN sys.realestate b
on a.ParcelID = b.ParcelID
AND a.UniqueID <> b.UniqueID
SET a.PropertyAddress = IFNULL(a.PropertyAddress, b.PropertyAddress)
WHERE
a.PropertyAddress is NULL;"
)
[1] 29
The console returned [1] 29 which means the 29 rows affected. Now
passing again the query for nulls in the PropertyAddress column, I get 0
nulls.
dbGetQuery(con, null_PropertyAddress_query)
Breaking the PropertyAddress columns in two separate columns.
The second issue with property address contains the address itself
and the city in a single string. It is ideal to have them in separate
columns. To do this we can simply use the substring index function which
returns a substring from a string before a specified number of
occurrences of the delimiter. In this case the delimiter is a ','.
dbExecute(
con,
"ALTER TABLE realestate
ADD AddressProperty VARCHAR(60),
ADD PropertyCity VARCHAR(50);
")
dbExecute(
con,
"UPDATE realestate
SET AddressProperty = SUBSTRING_INDEX(PropertyAddress, ',', 1),
PropertyCity = SUBSTRING_INDEX(PropertyAddress, ',', -1) ;
")
[1] 56477
Checking the property address information was splitted correctly:
realestate_db <- tbl(con, "realestate")
realestate_db
Breaking the Owner Address columns in three separate columns.
The Property address had the address and the city whereas the owner
address has the address, city and estate in the same string. I extracted
the information with the substring_index function, and in the case of
the information between the two commas, I nested a substring_index
function.
dbExecute(
con,
"ALTER TABLE realestate
ADD AddressOwner VARCHAR(60),
ADD OwnerCity VARCHAR(50),
ADD OwnerEstate VARCHAR(50);
")
dbExecute(
con,
"UPDATE realestate
SET AddressOwner = SUBSTRING_INDEX(OwnerAddress, ',', 1),
OwnerCity = SUBSTRING_INDEX(SUBSTRING_INDEX(OwnerAddress, ',', 2), ',',-1),
OwnerEstate = SUBSTRING_INDEX(OwnerAddress, ',', -1);
")
[1] 26015
Search and remove duplicates
It is safe to assume that if two or more entries share the same
parcel ID, address, legal reference, sale date and sale price then there
is a duplicated entry.
To look for entry I will use the row_number window function, that
will assign a row number to each element in a partition. The partition
will be over the parcel ID, address, legal reference, sale date and sale
price to identify how many elements share exactly the same information
in these columns.
There are 104 duplicates. To remove the duplicates I will use the
dbExdecute function with the same query I got the duplicates
dbExecute(
con,
"WITH DuplicatesCTE as (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY PropertyAddress,
SalePrice,
SaleDate,
LegalReference,
ParcelID
order by UniqueID
) row_num
FROM sys.realestate
)
DELETE FROM sys.realestate
WHERE UniqueID IN (
SELECT UniqueID
FROM DuplicatesCTE
WHERE row_num > 1
);
")
[1] 104
Descriptive analysis on land use
We can store the entire table or the results of our queries in data
frames in R, and this makes it very easy to create a chart with it in
R.
land_use <- realestate_clean%>%
count(LandUse, sort=TRUE)
land_use
Then organizing the data to display in the next chart, where I will
display the 5 more common land uses, and the remaining 32 categories of
land uses summarized as “Others” since their numbers are very low. This
information will be stored in the land_use_p1 object, that I will pass
into ggplot() later.
land_use_p1 <- land_use[land_use$n >=247, ]
Land_use_others <- sum(land_use[which(land_use$n<1047), 2])
land_use_p1[nrow(land_use_p1) + 1,] = list("OTHERS", Land_use_others)
land_use_p1
Creating a bar char for land use:
I created and stored a theme to apply to the next charts to keep
consistency.
mytheme <- theme(
plot.title = element_text(family = "Arial", face = "bold", size = (15), colour = "#5A5A5A"),
axis.title = element_text(family = "Arial", size = (10), colour = "#808080", hjust=c(1), vjust=c(0)),
axis.text = element_text(family = "Arial", size = (10), colour = "#808080"),
legend.title = element_text(colour = "#808080", face = "bold", family = "Arial"),
legend.text = element_text(colour = "#808080", family = "Arial"),
plot.subtitle = element_text(colour = "#5A5A5A", family = "Arial"),
plot.caption = element_text(colour = "#5A5A5A", family = "Arial")
)
Creating a bar char with ggplot2:
p1 <- ggplot(land_use_p1, aes(x = LandUse, y = n, fill = LandUse)) +
geom_col(width = 0.4) +
scale_x_discrete(labels = c("Duplex", "Others", "Residential\nCondo", "Single\nFamily", "Vacant\nResidential\nLand", "Zero Lot\nLine"))+
scale_fill_manual(values = c("#b3cde0", "#005b96", "#011F4B","#8a0303", "#03396c","#6497b1" ))+
labs(y= "Number of properties", x = "Land Use", title = "Land use of the Different Properties Sold\nIn Nahsville, Tennessee from 2018 to 2021", fill = "LandUse", caption = "Properties sold in Nashville, Tennessee from 2018 to 2021", substitle = "Single family is by far the most common land use of the properties sold during the period, followed by residential condo ")+
mytheme
p1

Descriptive analysis on sale date
To take a quick look about the property sales per year I will group
the information by year and then count the sales:
sale_date_p2 <- realestate_clean %>%
mutate(year = year(SaleDate)) %>%
group_by(year) %>%
count()
sale_date_p2
Creating a bar char for sales per year:
p2 <- ggplot(sale_date_p2, aes(x = year, y = n, fill = year)) +
geom_col(width = 0.4)+
labs(y= "Year of Sale", x = "Number of Sales", title = "Number of Sales Per year in Nahsville, Tennessee,\nfrom 2018 to 2021", fill = "year", caption = "Properties sold in Nashville, Tennessee from 2018 to 2021", subtitle = "The properties sales increased steadily from 2018 to 2020, declining slightly again in 2021 ")+
mytheme+
theme(legend.position="none")
p2

To wrap up, the ODBC, DBI, dplyr and dbplyr used together, allow us
to connect our databases to R and execute queries directly in R. The
results of our queries can be visualized as they are on the database
with the tbl function or we can bring the results to R and store them
into objects with dbGetQuery to analyze the information and create
charts without the need of importing or exporting the information as a
separate file.
LS0tCnRpdGxlOiAiTmFzaHZpbGxlIEhvdXNpbmcgLSBNeVNRTCBEYXRhYmFzZSBRdWVyaWVzIFdpdGggUiAiCm91dHB1dDogaHRtbF9ub3RlYm9vawplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCkluIHRoaXMgcHJvamVjdCBJIHdpbGwgYmUgY2xlYW5pbmcgdGhlIE5hc2h2aWxsZSBIb3VzaW5nIGRhdGFzZXQsIHRoYXQgd2FzIHNoYXJlZCBieSBBbGV4VGhlQW5hbHlzdCB0cm91Z2ggR2l0SHViIHVzaW5nIE15U1FMIHF1ZXJpZXMgd2l0aCBSLgpUaGUgZGF0YXNldCBpcyBhdmFpbGFibGUgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4VGhlQW5hbHlzdC9Qb3J0Zm9saW9Qcm9qZWN0cy9ibG9iL21haW4vTmFzaHZpbGxlJTIwSG91c2luZyUyMERhdGElMjBmb3IlMjBEYXRhJTIwQ2xlYW5pbmclMjAocmV1cGxvYWRlZCkueGxzeCkKClRoaXMgcGFydGljdWxhciBwcm9qZWN0IGNvdWxkIGhhdmUgYmVlbiBkb25lIGNvbXBsZXRlbHkgd2l0aCBSIGJlY2F1c2UgSSBoYXZlIHRoZSAuY3N2IGZpbGUgYXZhaWxhYmxlLiBIb3dldmVyLCBJIGRlY2lkZWQgdG8gbWFrZSB0aGlzIHByb2plY3Qgb3V0IG9mIHRoZSBwZXJzb25hbCBpbnRlcmVzdCBvZiBsZWFybmluZyBhbmQgaWxsdXN0cmF0aW5nIGhvdyB0byB1c2UgdGhpcyBhbWF6aW5nIHRvb2xzLCBNeVNRTCBhbmQgUiwgdG9nZXRoZXIgdG8gcXVlcnkgb3VyIGRhdGFiYXNlcywgY2xlYW4gZGF0YSwgYW5kIHVzZSB0aGUgcmVzdWx0cyBpbW1lZGlhdGVseSBmb3IgYW5hbHlzaXMgYW5kIGNoYXJ0cy4gSSBiZWxpZXZlIHRoaXMgY29uY2VwdHMgY2FuIGJlIHZlcnkgdXNlZnVsIGZvciBtZSBpbiBteSBmdXR1cmUgY2FyZWVyIGFuZCBmb3Igb3RoZXJzLiAKCiMjIE5vdGVzIG9uIGltcG9ydGluZyBkYXRhIHRvIE15U1FsIGRhdGFiYXNlCgpUaGVyZSBhcmUgbXVsdGlwbGUgd2F5cyB0byBpbXBvcnQgYSBjdnMgZmlsZSBpbnRvIGEgZGF0YWJhc2UuIEkgYmVsaWV2ZSBvbmUgb2YgdGhlIGVhc2llc3Qgd2F5cyBpcyB0byBjb252ZXJ0IHRoZSBjdnMgZmlsZSBpbnRvIGFuIHNxbCBmaWxlIHVzaW5nIHdlYnNpdGVzIGxpa2UgW2NvbnZlcnRjc3YuY29tXShodHRwczovL3d3dy5jb252ZXJ0Y3N2LmNvbS9jc3YtdG8tc3FsLmh0bSksIHByb3ZpZGVkIHRoZSBkYXRhc2V0IGlzIG5vdCB0b28gYmlnIChsaWtlIHRoaXMgb25lIHRoYXQgaGFzIDU2NDc3IHJvd3MpLCBmb3IgZGlmZmVyZW50IHJlYXNvbnMuIAoKRmlyc3Qgb2YgYWxsLCB5b3UgY2FuIGN1c3RvbWl6ZSBwcmV0dHkgbXVjaCBldmVyeXRoaW5nLCBpbmNsdWRpbmcgdGhlIGRhdGEgdHlwZSBvZiB0aGUgZmllbGRzLCBhbmQgdmVyeSBlc3BlY2lhbGx5IHRoZSBkYXRlIGZvcm1hdCwgd2hpY2ggY2FuIHNhdmUgeW91IGEgbG90IG9mIHRpbWUgYWZ0ZXIgaW1wb3J0aW5nIHlvdXIgZmlsZS4gVGhlIGRhdGUgaW4gYSBjc3YgZmlsZSB1c3VhbGx5IGRvZXNuJ3QgbWF0Y2ggdGhlIGZvcm1hdCB1c2VkIGJ5IE15U1FMIG9mIFlZWVktTU0tREQgYW5kIHRoaXMgY3JlYXRlcyBwcm9ibGVtcyB2ZXJ5IG9mdGVuIGR1cmluZyB0aGUgaW1wb3J0YXRpb24gb3IgbmVlZCB0byBiZSBmaXhlZCBhZnRlciBpbXBvcnRhdGlvbi4gCgpTZWNvbmQgaXQgaXMgdmVyeSBlYXN5IHRvIHVzZSBhbmQgZmFzdC4gSXQgY3JlYXRlcyB0aGUgQ1JFQVRFIFRBQkxFIGFuZCBJTlNFUlQgSU5UTyBzdGF0ZW1lbnRzLiBBZnRlciBkb3dubG9hZGluZyB0aGUgLnNxbCBmaWxlLCBJIG9wZW5lZCBpdCB3aXRoIFZTIENvZGUgdG8gY2hlY2tlZCBldmVyeXRoaW5nIHdhcyBjb3JyZWN0LiBJbiB0aGlzIGNhc2UsIHRoZSBsYXN0IHRocmVlIHJvd3Mgd2VyZSBhbGwgYmxhbmtzLCBzbyBJIHJlbW92ZWQgdGhlbS4gVGhlbiBJIGp1c3QgY3JlYXRlZCB0aGUgdGFibGUgYnkgc2ltcGx5IGV4ZWN1dGluZyB0aGUgc3FsIHN0YXRlbWVudHMgZnJvbSBhIGZpbGUgdXNpbmcgdGhlIHNvdXJjZSBjb21tYW5kIHZpYSB0aGUgY29tbWFuZCBsaW5lIGxpa2UgbXlzcWw+IHNvdXJjZSBmaWxlX25hbWUgbGlrZSB0aGlzOiAKCm15c3FsPiBzb3VyY2UgL1VzZXJzL2FsZXhhbmRyYXZlbGV6L0Rlc2t0b3AucmVhbGVzdGF0ZS5zcWwKCiMjIFNldHRpbmcgdXAgdGhlIGNvbm5lY3Rpb24gd2l0aCBNeVNRTCBhbmQgUgoKVGhlcmUgYXJlIHR3byBvcHRpb25zIGZvciBjb25uZWN0aW5nIFIgdG8gYSBNeVNRTCBkYXRhYmFzZToKMS4gVXNpbmcgdGhlIG9kYmMgcGFja2FnZSB3aXRoIGEgZGF0YWJhc2UgZHJpdmVyCjIuIFVzaW5nIHRoZSBSTWFyaWFEQiBwYWNrYWdlCgpJIHVzZWQgdGhlIGZpcnN0IG1ldGhvZC4gSSBpbnN0YWxsZWQgTXlTUUwgY29tbXVuaXR5IHNlcnZlciBvbiBteSBtYWMsIE15U1FMIFdvcmtiZW5jaCwgYW5kIGlPREJDIGRyaXZlciBtYW5hZ2VyLiBDcmVkaXRzIHRvIHRoaXMgW3ZpZGVvXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PXVSa3Z2T0RXQTFRKSB0aGF0IHNob3dzIGFuIGVhc3kgYW5kIHF1aWNrIHdheSB0byBzZXQgZXZlcnl0aGluZyB1cCB3aXRob3V0IGV2ZW4gbmVlZGluZyB0aGUgdGVybWluYWwuCgpUaGVuIEkgaW5zdGFsbGVkIHRoZSBSIHBhY2thZ2VzOgoKVGhlIERCSSBwYWNrYWdlIGhlbHBzIGNvbm5lY3RpbmcgUiB0byBkYXRhYmFzZSBtYW5hZ2VtZW50IHN5c3RlbXMgKERCTVMpLiAKVGhlIG9kYmMgcGFja2FnZSBwcm92aWRlcyBhIERCSS1jb21wbGlhbnQgaW50ZXJmYWNlIHRvIE9wZW4gRGF0YWJhc2UgQ29ubmVjdGl2aXR5IChPREJDKSBkcml2ZXJzLiBJdCBhbGxvd3MgZm9yIGFuIGVmZmljaWVudCwgZWFzeSB3YXkgdG8gc2V0dXAgY29ubmVjdGlvbiB0byBhbnkgZGF0YWJhc2UgdXNpbmcgYW4gT0RCQyBkcml2ZXIsIGluY2x1ZGluZyBTUUwgU2VydmVyLCBPcmFjbGUsIE15U1FMLCBQb3N0Z3JlU1FMLCBTUUxpdGUgYW5kIG90aGVycy4gClRpZHl2ZXJzZSB0byB1c2UgZHBseXIsIGRicGx5ciBhbmQgZ2dwbG90Mi4KRGJwbHlyIGlzIGluY2x1ZGVkIGluIHRoZSB0aWR5dmVyc2UgYW5kIGNvbnZlcnRzIGRwbHlyIGludG8gU1FMIGNvZGUuCgpgYGB7ciBJbnN0YWxsaW5nIHBhY2thZ2VzLCByZXN1bHRzPSdoaWRlJ30KaW5zdGFsbC5wYWNrYWdlcygiREJJIikKbGlicmFyeShEQkkpCmluc3RhbGwucGFja2FnZXMoIm9kYmMiKQpsaWJyYXJ5KG9kYmMpCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKQoKYGBgCgpDb25uZWN0aW5nIFIgdG8gdGhlIGRhdGFiYXNlOgoKYGBge3IgY29ubmVjdGluZyBSIHRvIE15U1FMLCBldmFsPUZBTFNFfQpsaWJyYXJ5KCJEQkkiKQpjb24gPC0gZGJDb25uZWN0KG9kYmMoKSwgRHJpdmVyID0gIi91c3IvbG9jYWwvbXlzcWwtY29ubmVjdG9yLW9kYmMtOC4yLjAtbWFjb3MxMy1hcm02NC9saWIvbGlibXlvZGJjOGEuc28iLCAKICAgICAgICAgICAgICAgICBTZXJ2ZXIgPSAibG9jYWxob3N0IiwgRGF0YWJhc2UgPSAic3lzIiwgVUlEID0gInJvb3QiLCBQV0QgPSAidGVzdERCXzEyMyIsIAogICAgICAgICAgICAgICAgIFBvcnQgPSAzMzA2LCBQYXNzd29yZCA9ICJwYXNzd29yZCIpCmBgYAoKYGBge3IgZWNobz1GQUxTRX0KbGlicmFyeSgiREJJIikKY29uIDwtIGRiQ29ubmVjdChvZGJjKCksIERyaXZlciA9ICIvdXNyL2xvY2FsL215c3FsLWNvbm5lY3Rvci1vZGJjLTguMi4wLW1hY29zMTMtYXJtNjQvbGliL2xpYm15b2RiYzhhLnNvIiwgCiAgICAgICAgICAgICAgICAgU2VydmVyID0gImxvY2FsaG9zdCIsIERhdGFiYXNlID0gInN5cyIsIFVJRCA9ICJyb290IiwgUFdEID0gInRlc3REQl8xMjMiLCAKICAgICAgICAgICAgICAgICBQb3J0ID0gMzMwNiwgUGFzc3dvcmQgPSAiIUF2cjMxMzg2NjciKQpgYGAKClRoZSBjb25uZWN0aW9uIHdhcyBzdG9yZWQgaW4gYW4gb2JqZWN0IG5hbWVkIGNvbiwgYW5kIEkgY2FuIHNlZSB1bmRlciBjb25uZWN0aW9ucyBhbGwgbXkgU1FMIGRhdGFiYXNlcy4KCkkgY2FuIGNhbGwgZGJMaXN0VGFibGVzKGNvbikgdG8gc2VlIGFsbCB0aGUgdGFibGVzIGluIG15IHN5cyBkYXRhYmFzZS4gVGhlIG5hbWUgb2YgdGhlIHRhYmxlIEkgd2lsbCB3b3JrIHdpdGggaXMgJ3JlYWxlc3RhdGUnLgoKSSBjYW4gY3JlYXRlIGEgZGlyZWN0IGxpbmsgd2l0aCBhIHRhYmxlIHVzaW5nIHRibCgpIGZyb20gZHBseXIuIFRoaXMgdGFibGUgaXMgbm90IGluIFIgeWV0LCBpdCBpcyBqdXN0IGEgY29ubmVjdGlvbiB0byBhIHRhYmxlIHRoYXQgaXMgaW4gdGhlIGRhdGFiYXNlCgpgYGB7ciBjb25uZWN0aW5nIHRvIG15IHRhYmxlfQpyZWFsZXN0YXRlX2RiIDwtIHRibChjb24sICJyZWFsZXN0YXRlIikKcmVhbGVzdGF0ZV9kYgpgYGAKClIgZGlzcGxheXMgYWxzbyBTb3VyY2U6ICAgdGFibGU8bXl0YWJsZT4gWz8/IHggMTldIERhdGFiYXNlOiBteXNxbCAgW3Jvb3RAOi9zeXNdIGJlY2F1c2UgUiBkb2VzIG5vdCBrbm93IGhvdyBtYW55IHJvd3MgYXJlIGluIHRoaXMgdGFibGUsIGl0IGp1c3QgdGFrZSBhIGxvb2sgdG8gdGhlIGZpcnN0IHJvd3Mgb2YgdGhlIHRhYmxlIHRoYXQgaXMgaW4gdGhlIGRhdGFiYXNlLgoKVG8gYnJpbmcgdGhlIHJlYWxlc3RhdGUgdGFibGUgdG8gUiBJIGhhdmUgdG8gdXNlIHRoZSBjb2xsZWN0ICgpLiAKCmBgYHtyIHN0b3JpbmcgdGhlIHRhYmxlIGluIGEgZGF0YSBmcmFtZSwgZWNobz1UUlVFfQpyZWFsZXN0YXRlX2RmIDwtIGNvbGxlY3QocmVhbGVzdGF0ZV9kYikKcmVhbGVzdGF0ZV9kZgpgYGAKCk5vdyBJIGhhdmUgYSBkYXRhIGZyYW1lIGV4YWN0bHkgbGlrZSBpZiBJIHVzZWQgcmVhZC5jc3YoKQoKIyMgQ2hlY2tpbmcgZm9yIG51bGxzCgpDcmVhdGluZyBhIHF1ZXJ5IHRvIGxvb2sgZm9yIG51bGxzIGluIFVuaXF1ZUlELiBJbiB0aGlzIGNhc2UgSSBzdG9yZWQgdGhlIHF1ZXJ5IGluIGFuIG9iamVjdCB0byBwYXNzIGl0IHRvIGEgZGJHZXRRdWVyeSBmdW5jdGlvbiwgYnV0IGFsdGVybmF0aXZlbHkgSSBjYW4gcGFzcyB0aGUgcXVlcnkgZGlyZWN0bHkgaW50byB0aGUgZnVuY3Rpb24uCgpgYGB7ciBxdWVyeSBudWxscyBVbmlxdWVJRH0KbnVsbF9VbmlxdWVJRF9xdWVyeSA8LSAiU0VMRUNUICoKICBGUk9NIHJlYWxlc3RhdGUKICBXSEVSRSBVbmlxdWVJRCBJUyBOVUxMIgpgYGAKICAKTm93IEkgd2lsbCB1c2UgdGhlIGRiR2V0UXVlcnkgZnVuY3Rpb24gZnJvbSBkZSBEQkkgcGFja2FnZSB0byBwYXNzIG15IHF1ZXJ5LiBUaGUgcmVzdWx0IGlzIGEgYSBkYXRhIGZyYW1lIHRoYXQgY2FuIGJlIHN0b3JlZCB0byB3b3JrIHdpdGggaW4gdGhlIGZ1dHVyZS4KCmBgYHtzcWx9CmRiR2V0UXVlcnkoY29uLCBudWxsX1VuaXF1ZUlEX3F1ZXJ5KQpgYGAKCmBgYHtyIHBhc3NpbmcgdGhlIHF1ZXJ5IHRvIGxvb2sgZm9yIG51bGwgaW4gdGhlIFVuaXF1ZUlEIGNvbHVtbiwgZWNobz1UUlVFIH0KZGJHZXRRdWVyeShjb24sIG51bGxfVW5pcXVlSURfcXVlcnkpCmBgYAoKQ3JlYXRpbmcgYSBxdWVyeSB0byBsb29rIGZvciBudWxscyBpbiBQYXJjZWxJRAoKYGBge3IgcXVlcnkgbnVsbHMgUGFyY2VsSUR9Cm51bGxfUGFyY2VsSURfcXVlcnkgPC0gIlNFTEVDVCAqCiAgRlJPTSByZWFsZXN0YXRlCiAgV0hFUkUgUGFyY2VsSUQgSVMgTlVMTCIKYGBgCgpQYXNzaW5nIHRoZSBxdWVyeSBpbnRvIHRoZSBkYkdldFF1ZXJ5IGZ1bmN0aW9uOgoKYGBge3NxbCB9CmRiR2V0UXVlcnkoY29uLCBudWxsX1BhcmNlbElEX3F1ZXJ5KQpgYGAKCmBgYHtyIHBhc3NpbmcgdGhlIHF1ZXJ5IHRvIGxvb2sgZm9yIG51bGwgaW4gdGhlIFBhcmNlbElEIGNvbHVtbiB9CmRiR2V0UXVlcnkoY29uLCBudWxsX1BhcmNlbElEX3F1ZXJ5KQpgYGAKQ3JlYXRpbmcgYSBxdWVyeSB0byBsb29rIGZvciBudWxscyBpbiBMYW5kVXNlOgoKYGBge3IgcXVlcnkgbnVsbHMgTGFuZFVzZX0KbnVsbF9MYW5kVXNlX3F1ZXJ5IDwtICJTRUxFQ1QgKgogIEZST00gcmVhbGVzdGF0ZQogIFdIRVJFIExhbmRVc2UgSVMgTlVMTCIKYGBgCgpQYXNzaW5nIHRoZSBxdWVyeSBpbnRvIHRoZSBkYkdldFF1ZXJ5IGZ1bmN0aW9uOgoKYGBge3NxbH0KZGJHZXRRdWVyeShjb24sIG51bGxfTGFuZFVzZV9xdWVyeSkKYGBgCgpgYGB7ciBwYXNzaW5nIHRoZSBxdWVyeSB0byBsb29rIGZvciBudWxsIGluIHRoZSBMYW5kVXNlIGNvbHVtbiB9CmRiR2V0UXVlcnkoY29uLCBudWxsX0xhbmRVc2VfcXVlcnkpCmBgYAoKSSBnb3QgMCBudWxscyBmb3IgVW5pcXVlSUQsIFBhcmNlbCBJRCBhbmQgTGFuZFVzZS4KCkNyZWF0aW5nIGEgcXVlcnkgdG8gbG9vayBmb3IgbnVsbHMgaW4gUHJvcGVydHlBZGRyZXNzOgoKYGBge3IgcXVlcnkgbnVsbHMgUHJvcGVydHlBZGRyZXNzfQpudWxsX1Byb3BlcnR5QWRkcmVzc19xdWVyeSA8LSAiU0VMRUNUICoKICBGUk9NIHJlYWxlc3RhdGUKICBXSEVSRSBQcm9wZXJ0eUFkZHJlc3MgSVMgTlVMTCIKYGBgCgpgYGB7c3FsIH0KZGJHZXRRdWVyeShjb24sIG51bGxfUHJvcGVydHlBZGRyZXNzX3F1ZXJ5KQpgYGAKCmBgYHtyIHBhc3NpbmcgdGhlIHF1ZXJ5IHRvIGxvb2sgZm9yIG51bGwgaW4gdGhlIFByb3BlcnR5QWRkcmVzcyBjb2x1bW4gfQpkYkdldFF1ZXJ5KGNvbiwgbnVsbF9Qcm9wZXJ0eUFkZHJlc3NfcXVlcnkpCmBgYAoKSSBnb3QgMjkgbnVsbHMgb24gdGhlIFByb3BlcnR5QWRkcmVzcyBjb2x1bW4uIAoKIyMgUG9wdWxhdGluZyB0aGUgbnVsbHMgaW4gdGhlIFByb3BlcnR5QWRkcmVzcy4KClRha2luZyBhIGNsb3NlciBsb29rIHRvIHRoZSBwYXJjZWwgSUQgZm9yIHNvbWUgb2YgdGhlIHByb3BlcnR5IGFkZHJlc3MgbnVsbHM6CgpgYGB7ciBjcmVhdGluZyB0aGUgcXVlcnkgdG8gbG9vayBmb3IgYW4gZXNwZWNpZmljIHBhcmNlbCBpZCB3aGVyZSB0aGUgYWRkcmVzcyB3YXMgbnVsbH0KUGFyY2VJRF9xdWVyeSA8LSAiU0VMRUNUICoKICAgICAgICAgICAgICAgICAgRlJPTSByZWFsZXN0YXRlCiAgICAgICAgICAgICAgICAgIFdIRVJFIFBhcmNlbElEID0gJzA1MiAwMSAwIDI5Ni4wMCciIApgYGAKCmBgYHtzcWx9CmRiR2V0UXVlcnkoY29uLCBQYXJjZUlEX3F1ZXJ5KQpgYGAKCmBgYHtyIHBhc3NpbmcgdGhlIHF1ZXJ5fQpkYkdldFF1ZXJ5KGNvbiwgUGFyY2VJRF9xdWVyeSkKYGBgCmBgYHtyIGNyZWF0aW5nIGEgc2Vjb25kIHF1ZXJ5IHRvIGxvb2sgZm9yIGFuIGVzcGVjaWZpYyBwYXJjZWwgaWQgd2hlcmUgdGhlIGFkZHJlc3Mgd2FzIG51bGx9ClBhcmNlbElEX3F1ZXJ5MiA8LSAiU0VMRUNUICoKICAgICAgICAgICAgICAgICAgRlJPTSByZWFsZXN0YXRlCiAgICAgICAgICAgICAgICAgIFdIRVJFIFBhcmNlbElEID0gJzA5MyAwOCAwIDA1NC4wMCciIApgYGAKCmBgYHtzcWwgfQpkYkdldFF1ZXJ5KGNvbiwgUGFyY2VsSURfcXVlcnkyKQpgYGAKCmBgYHtyIHBhc3NpbmcgdGhlIHNlY29uZCBxdWVyeSB9CmRiR2V0UXVlcnkoY29uLCBQYXJjZWxJRF9xdWVyeTIpCmBgYApXaGVuIGluc3BlY3RpbmcgdGhlIG51bGxzIGluIFByb3BlcnR5QWRkcmVzcyB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIHNldmVyYWwgcHJvcGVydGllcyB3aXRoIHRoZSBzYW1lIFBhcmNlbElEIGJ1dCBvbmUgb2YgdGhlbSB3aXRob3V0IGEgUHJvcGVydHkgYWRkcmVzcy4gSW4gdGhpcyBjYXNlIHRoaXMgaW5mb3JtYXRpb24gY2FuIGJlIGNvbXBsZXRlZC4gVG8gZG8gdGhpcyB3ZSBjYW4gY3JlYXRlIGEgam9pbnQgdG8gdGhlIHJlYWxlc3RhdGUgdGFibGUgdG8gaXRzZWxmIGFuZCBvYnRhaW4gdGhlIHZhbHVlcyB3aGVyZSB0aGUgcGFyY2VsIGlkIGlzIHRoZSBzYW1lIGJ1dCB0aGUgcHJvcGVydHlhZGRyZXNzIGlzIGNvbXBsZXRlLiAKCldpdGggdGhlIElGTlVMTCBmdW5jdGlvbiB3ZSB3YW50IHRvIHJlcGxhY2UgdGhlIG51bGwgdmFsdWUgd2l0aCB0aGUgcHJvcGVydHkgYWRkcmVzcyBvZiBvdXIgbWF0Y2ggaW4gdGhlIGpvaW50CmBgYHtyIGpvaW50IHF1ZXJ5fQpqb2ludF9xdWVyeSA8LSAiU0VMRUNUIGEuUGFyY2VsSUQsIGEuUHJvcGVydHlBZGRyZXNzLCBiLlBhcmNlbElELCBiLlByb3BlcnR5QWRkcmVzcywgSUZOVUxMKGEuUHJvcGVydHlBZGRyZXNzLCBiLlByb3BlcnR5QWRkcmVzcykgYXMgcG9wdWxhdGVfYWRkcmVzcwpGUk9NIHN5cy5yZWFsZXN0YXRlIGEKSk9JTiBzeXMucmVhbGVzdGF0ZSBiCiAgIG9uIGEuUGFyY2VsSUQgPSBiLlBhcmNlbElECiAgIEFORCBhLlVuaXF1ZUlEIDw+IGIuVW5pcXVlSUQKV0hFUkUgCmEuUHJvcGVydHlBZGRyZXNzIGlzIE5VTEw7IgpgYGAKCmBgYHtzcWx9CmpvaW50IDwtIGRiR2V0UXVlcnkoY29uLCBqb2ludF9xdWVyeSkKam9pbnQKYGBgCgpgYGB7ciBwYXNzaW5nIHRoZSBqb2ludCBxdWVyeX0Kam9pbnQgPC0gZGJHZXRRdWVyeShjb24sIGpvaW50X3F1ZXJ5KQpqb2ludApgYGAKCk5vdyBJIHdhbnQgdG8gcG9wdWxhdGUgdGhlIG51bGxzIHdpdGggdGhlIHJlc3VsdHMgdGhhdCB3ZSBnb3QuIEZvciB0aGlzIEkgaGF2ZSB0byBwYXNzIGFuIHVwZGF0ZSBzdGF0ZW1lbnQgdXNpbmcgdGhlIGRiRXhlY3V0ZSBmdW5jdGlvbi4gCkZvciBTUUwgcXVlcmllcyB0aGF0IGFmZmVjdCB0aGUgdW5kZXJseWluZyBkYXRhYmFzZSwgc3VjaCBhcyBVUERBVEUsIERFTEVURSwgSU5TRVJUIElOVE8sIGFuZCBEUk9QIFRBQkxFLCBEQkkgcHJvdmlkZXMgdHdvIGZ1bmN0aW9ucy4gZGJFeGVjdXRlKCkgcGFzc2VzIHRoZSBTUUwgc3RhdGVtZW50IHRvIHRoZSBEQk1TIGZvciBleGVjdXRpb24gYW5kIHJldHVybnMgdGhlIG51bWJlciBvZiByb3dzIGFmZmVjdGVkLiBkYlNlbmRTdGF0ZW1lbnQoKSBwZXJmb3JtcyBpbiB0aGUgc2FtZSBtYW5uZXIsIGJ1dCByZXR1cm5zIGEgcmVzdWx0IG9iamVjdC4gQ2FsbCBkYkdldFJvd3NBZmZlY3RlZCgpIHdpdGggdGhlIHJlc3VsdCBvYmplY3QgdG8gZ2V0IHRoZSBjb3VudCBvZiB0aGUgYWZmZWN0ZWQgcm93cy4gWW91IHRoZW4gbmVlZCB0byBjYWxsIGRiQ2xlYXJSZXN1bHQoKSB3aXRoIHRoZSByZXN1bHQgb2JqZWN0IGFmdGVyd2FyZHMgdG8gcmVsZWFzZSByZXNvdXJjZXMuCgpkYkV4ZWN1dGUoKSBpcyBhIGNvbnZlbmllbmNlIGZ1bmN0aW9uIHRoYXQgY2FsbHMgZGJTZW5kU3RhdGVtZW50KCksIGRiR2V0Um93c0FmZmVjdGVkKCksIGFuZCBkYkNsZWFyUmVzdWx0KCkuCgpgYGB7ciBwb3B1bGF0aW5nIHRoZSBudWxsIHZhbHVlcyBpbiB0aGUgUHJvcGVydHlBZGRyZXNzIGNvbHVtbn0KZGJFeGVjdXRlKApjb24sCiJVUERBVEUgc3lzLnJlYWxlc3RhdGUgYQpKT0lOIHN5cy5yZWFsZXN0YXRlIGIKICAgb24gYS5QYXJjZWxJRCA9IGIuUGFyY2VsSUQKICAgQU5EIGEuVW5pcXVlSUQgPD4gYi5VbmlxdWVJRApTRVQgYS5Qcm9wZXJ0eUFkZHJlc3MgPSBJRk5VTEwoYS5Qcm9wZXJ0eUFkZHJlc3MsIGIuUHJvcGVydHlBZGRyZXNzKQpXSEVSRSAKYS5Qcm9wZXJ0eUFkZHJlc3MgaXMgTlVMTDsiCikKYGBgCgpUaGUgY29uc29sZSByZXR1cm5lZCBbMV0gMjkgd2hpY2ggbWVhbnMgdGhlIDI5IHJvd3MgYWZmZWN0ZWQuIApOb3cgcGFzc2luZyBhZ2FpbiB0aGUgcXVlcnkgZm9yIG51bGxzIGluIHRoZSBQcm9wZXJ0eUFkZHJlc3MgY29sdW1uLCBJIGdldCAwIG51bGxzLiAKCmBgYHtzcWx9CmRiR2V0UXVlcnkoY29uLCBudWxsX1Byb3BlcnR5QWRkcmVzc19xdWVyeSkKYGBgCgpgYGB7ciBjaGVja2luZyBhbGwgbnVsbHMgaW4gcHJvcGVydHkgYWRkcmVzcyB3ZXJlIGZpeGVkfQpkYkdldFF1ZXJ5KGNvbiwgbnVsbF9Qcm9wZXJ0eUFkZHJlc3NfcXVlcnkpCmBgYAoKCiMjIEJyZWFraW5nIHRoZSBQcm9wZXJ0eUFkZHJlc3MgY29sdW1ucyBpbiB0d28gc2VwYXJhdGUgY29sdW1ucy4KClRoZSBzZWNvbmQgaXNzdWUgd2l0aCBwcm9wZXJ0eSBhZGRyZXNzIGNvbnRhaW5zIHRoZSBhZGRyZXNzIGl0c2VsZiBhbmQgdGhlIGNpdHkgaW4gYSBzaW5nbGUgc3RyaW5nLiBJdCBpcyBpZGVhbCB0byBoYXZlIHRoZW0gaW4gc2VwYXJhdGUgY29sdW1ucy4gVG8gZG8gdGhpcyB3ZSBjYW4gc2ltcGx5IHVzZSB0aGUgc3Vic3RyaW5nIGluZGV4IGZ1bmN0aW9uIHdoaWNoIHJldHVybnMgYSBzdWJzdHJpbmcgZnJvbSBhIHN0cmluZyBiZWZvcmUgYSBzcGVjaWZpZWQgbnVtYmVyIG9mIG9jY3VycmVuY2VzIG9mIHRoZSBkZWxpbWl0ZXIuIEluIHRoaXMgY2FzZSB0aGUgZGVsaW1pdGVyIGlzIGEgXCcsXCcuCgpgYGB7ciBDcmVhdGluZyB0d28gbmV3IGNvbHVtbnMsIEFkZHJlc3NQcm9wZXJ0eSBhbmQgUHJvcGVydHlDaXR5fQpkYkV4ZWN1dGUoCmNvbiwKIkFMVEVSIFRBQkxFIHJlYWxlc3RhdGUKICBBREQgQWRkcmVzc1Byb3BlcnR5IFZBUkNIQVIoNjApLAogIEFERCBQcm9wZXJ0eUNpdHkgVkFSQ0hBUig1MCk7CiIpCmBgYAoKYGBge3IgU3BsaXRpbmcgdGhlIHByb3BlcnR5IGFkZHJlc3MgaW50byB0d28gc2VwYXJhdGUgY29sdW1uc30KZGJFeGVjdXRlKApjb24sCiJVUERBVEUgcmVhbGVzdGF0ZQogIFNFVCBBZGRyZXNzUHJvcGVydHkgPSBTVUJTVFJJTkdfSU5ERVgoUHJvcGVydHlBZGRyZXNzLCAnLCcsIDEpLAogICAgICBQcm9wZXJ0eUNpdHkgPSBTVUJTVFJJTkdfSU5ERVgoUHJvcGVydHlBZGRyZXNzLCAnLCcsIC0xKSA7CiIpCmBgYAoKQ2hlY2tpbmcgdGhlIHByb3BlcnR5IGFkZHJlc3MgaW5mb3JtYXRpb24gd2FzIHNwbGl0dGVkIGNvcnJlY3RseToKYGBge3NxbH0KcmVhbGVzdGF0ZV9kYiA8LSB0YmwoY29uLCAicmVhbGVzdGF0ZSIpCnJlYWxlc3RhdGVfZGIKYGBgCgpgYGB7ciBDaGVja2luZyB0aGUgcHJvcGVydHkgYWRkcmVzcyBpbmZvcm1hdGlvbiB3YXMgc3BsaXR0ZWQgY29ycmVjdGx5fQpyZWFsZXN0YXRlX2RiIDwtIHRibChjb24sICJyZWFsZXN0YXRlIikKcmVhbGVzdGF0ZV9kYgpgYGAKCiMjIEJyZWFraW5nIHRoZSBPd25lciBBZGRyZXNzIGNvbHVtbnMgaW4gdGhyZWUgc2VwYXJhdGUgY29sdW1ucy4KVGhlIFByb3BlcnR5IGFkZHJlc3MgaGFkIHRoZSBhZGRyZXNzIGFuZCB0aGUgY2l0eSB3aGVyZWFzIHRoZSBvd25lciBhZGRyZXNzIGhhcyB0aGUgYWRkcmVzcywgY2l0eSBhbmQgZXN0YXRlIGluIHRoZSBzYW1lIHN0cmluZy4gSSBleHRyYWN0ZWQgdGhlIGluZm9ybWF0aW9uIHdpdGggdGhlIHN1YnN0cmluZ19pbmRleCBmdW5jdGlvbiwgYW5kIGluIHRoZSBjYXNlIG9mIHRoZSBpbmZvcm1hdGlvbiBiZXR3ZWVuIHRoZSB0d28gY29tbWFzLCBJIG5lc3RlZCBhIHN1YnN0cmluZ19pbmRleCBmdW5jdGlvbi4gCgpgYGB7ciBDcmVhdGluZyB0aGUgdGhyZWVlIG5ldyBjb2x1bW5zIGZvciBvd25lciBhZGRyZXNzLCBvd25lciBjaXR5IGFuZCBvd25lciBlc3RhdGV9CmRiRXhlY3V0ZSgKY29uLAoiQUxURVIgVEFCTEUgcmVhbGVzdGF0ZQogIEFERCBBZGRyZXNzT3duZXIgVkFSQ0hBUig2MCksCiAgQUREIE93bmVyQ2l0eSBWQVJDSEFSKDUwKSwKICBBREQgT3duZXJFc3RhdGUgVkFSQ0hBUig1MCk7CiIpCmBgYAoKYGBge3IgU3BsaXR0aW5nIE93bmVyIGFkZHJlc3MgaW5mb3JtYXRpb24gaW50byB0aGUgdGhyZWUgbmV3IGNvbHVtbnN9CmRiRXhlY3V0ZSgKY29uLAoiVVBEQVRFIHJlYWxlc3RhdGUKICBTRVQgQWRkcmVzc093bmVyID0gU1VCU1RSSU5HX0lOREVYKE93bmVyQWRkcmVzcywgJywnLCAxKSwKICAgICAgT3duZXJDaXR5ID0gU1VCU1RSSU5HX0lOREVYKFNVQlNUUklOR19JTkRFWChPd25lckFkZHJlc3MsICcsJywgMiksICcsJywtMSksCiAgICAgIE93bmVyRXN0YXRlID0gU1VCU1RSSU5HX0lOREVYKE93bmVyQWRkcmVzcywgJywnLCAtMSk7CiIpCmBgYAoKCmBgYHtyIENoZWNraW5nIHRoZSBvd25lciBhZGRyZXNzIGluZm9ybWF0aW9uIHdhcyBzcGxpdHRlZCBjb3JyZWN0bHl9CnJlYWxlc3RhdGVfZGIgPC0gdGJsKGNvbiwgInJlYWxlc3RhdGUiKQpyZWFsZXN0YXRlX2RiCmBgYAoKIyMgVW5pZnkgdGhlIGZvcm1hdCBpbiB0aGUgU29sZEFzVmFjYW50IGNvbHVtbgoKUmlnaHQgbm93IHdlIGhhdmUgWSwgTiwgeWVzLCBubyBpbiB0aGUgU29sZEFzVmFjYW50IGNvbHVtbjoKCmBgYHtyIGNyZWF0aW5nIGEgcXVlcnkgdG8gbG9vayBmb3IgdGVoIGRpZmZlcmVudCB2YWx1ZXMgaW4gdGhlIFNvbGRBc1ZhY2FudCBjb2x1bW59ClNvbGRBc1ZhY2FudF9xdWVyeSA8LSAiU0VMRUNUIERJU1RJTkNUKFNvbGRBc1ZhY2FudCksIENPVU5UKFNvbGRBc1ZhY2FudCkKRlJPTSBzeXMucmVhbGVzdGF0ZQpHUk9VUCBCWSBTb2xkQXNWYWNhbnQ7IgoKYGBgCgpgYGB7c3FsfQpkYkdldFF1ZXJ5KGNvbiwgU29sZEFzVmFjYW50X3F1ZXJ5KQpgYGAKCmBgYHtyIHBhc3NpbmcgdGhlIFNvbGRBc1ZhY2FudCBxdWVyeX0KZGJHZXRRdWVyeShjb24sIFNvbGRBc1ZhY2FudF9xdWVyeSkKYGBgCkkgdXNlZCBhIGNhc2Ugc3RhdGVtZW50IHRvIHVuaWZ5IHRoZSBmb3JtYXQgb2YgdGhlIFNvbGRBc1ZhY2FudCBjb2x1bW4gOiAKYGBge3IgY2FzZSBlc3RhdGVtZW50IHRvIHVuaWZ5IGZvcm1hdCBvZiB0aGUgU29sZEFzVmFjYW50IGNvbHVtbn0KZGJFeGVjdXRlKApjb24sCiJVUERBVEUgcmVhbGVzdGF0ZQogIFNFVCBTb2xkQXNWYWNhbnQgPQpjYXNlCiAgICB3aGVuIFNvbGRBc1ZhY2FudCA9ICdZJyB0aGVuICdZZXMnCndoZW4gU29sZEFzVmFjYW50ID0gJ04nIHRoZW4gJ05vJwplbHNlIFNvbGRBc1ZhY2FudAplbmQ7CiIpCmBgYApDaGVja2luZyB0aGUgZm9ybWF0IGlzIFllcyBhbmQgTm86CgpgYGB7ciBjaGVja2luZyB0aGUgU29sZEFzVmFjYW50IGZvcm1hdCBpcyB1bmlmb3JtIH0KZGJHZXRRdWVyeShjb24sIFNvbGRBc1ZhY2FudF9xdWVyeSkKYGBgCgojIyBTZWFyY2ggYW5kIHJlbW92ZSBkdXBsaWNhdGVzCiBJdCBpcyBzYWZlIHRvIGFzc3VtZSB0aGF0IGlmIHR3byBvciBtb3JlIGVudHJpZXMgc2hhcmUgdGhlIHNhbWUgcGFyY2VsIElELCBhZGRyZXNzLCBsZWdhbCByZWZlcmVuY2UsIHNhbGUgZGF0ZSBhbmQgc2FsZSBwcmljZSB0aGVuIHRoZXJlIGlzIGEgZHVwbGljYXRlZCBlbnRyeS4KIApUbyBsb29rIGZvciBlbnRyeSBJIHdpbGwgdXNlIHRoZSByb3dfbnVtYmVyIHdpbmRvdyBmdW5jdGlvbiwgdGhhdCB3aWxsIGFzc2lnbiBhIHJvdyBudW1iZXIgdG8gZWFjaCBlbGVtZW50IGluIGEgcGFydGl0aW9uLiBUaGUgcGFydGl0aW9uIHdpbGwgYmUgb3ZlciB0aGUgcGFyY2VsIElELCBhZGRyZXNzLCBsZWdhbCByZWZlcmVuY2UsIHNhbGUgZGF0ZSBhbmQgc2FsZSBwcmljZSB0byBpZGVudGlmeSBob3cgbWFueSBlbGVtZW50cyBzaGFyZSBleGFjdGx5IHRoZSBzYW1lIGluZm9ybWF0aW9uIGluIHRoZXNlIGNvbHVtbnMuIAoKYGBge3IgbG9va2luZyBmb3IgZHVwbGljYXRlcyB3aXRoIHRoZSByb3dfbnVtYmVyIGZ1bmN0aW9ufQpkYkdldFF1ZXJ5KApjb24sCiJXSVRIIER1cGxpY2F0ZXNDVEUgYXMgKAogICAgU0VMRUNUICosCiAgICAgICAgICAgUk9XX05VTUJFUigpIE9WRVIoCiAgICAgICAgICAgICAgIFBBUlRJVElPTiBCWSBQcm9wZXJ0eUFkZHJlc3MsCiAgICAgICAgICAgICAgICAgICBTYWxlUHJpY2UsCiAgICAgICAgICAgICAgICAgICBTYWxlRGF0ZSwKICAgICAgICAgICAgICAgICAgIExlZ2FsUmVmZXJlbmNlLAogICAgICAgICAgICAgICAgICAgUGFyY2VsSUQKICAgICAgICAgICAgICAgT1JERVIgQlkgVW5pcXVlSUQKICAgICAgICAgICAgICAgKSByb3dfbnVtCiAgICBGUk9NIHN5cy5yZWFsZXN0YXRlCikKICAgIFNFTEVDVCAqCiAgICBGUk9NIER1cGxpY2F0ZXNDVEUKICAgIFdIRVJFIHJvd19udW0gPiAxOwoiKQpgYGAKClRoZXJlIGFyZSAxMDQgZHVwbGljYXRlcy4KVG8gcmVtb3ZlIHRoZSBkdXBsaWNhdGVzIEkgd2lsbCB1c2UgdGhlIGRiRXhkZWN1dGUgZnVuY3Rpb24gd2l0aCB0aGUgc2FtZSBxdWVyeSBJIGdvdCB0aGUgZHVwbGljYXRlcwpgYGB7ciBSZW1vdmluZyB0aGUgZHVwbGljYXRlc30KZGJFeGVjdXRlKApjb24sCiJXSVRIIER1cGxpY2F0ZXNDVEUgYXMgKAogICAgU0VMRUNUICosCiAgICAgICAgICAgUk9XX05VTUJFUigpIE9WRVIgKAogICAgICAgICAgICAgICBQQVJUSVRJT04gQlkgUHJvcGVydHlBZGRyZXNzLAogICAgICAgICAgICAgICAgICAgU2FsZVByaWNlLAogICAgICAgICAgICAgICAgICAgU2FsZURhdGUsCiAgICAgICAgICAgICAgICAgICBMZWdhbFJlZmVyZW5jZSwKICAgICAgICAgICAgICAgICAgIFBhcmNlbElECiAgICAgICAgICAgICAgIG9yZGVyIGJ5IFVuaXF1ZUlECiAgICAgICAgICAgICAgICkgcm93X251bQogICAgRlJPTSBzeXMucmVhbGVzdGF0ZQopCkRFTEVURSBGUk9NIHN5cy5yZWFsZXN0YXRlCldIRVJFIFVuaXF1ZUlEIElOICgKICAgIFNFTEVDVCBVbmlxdWVJRAogICAgRlJPTSBEdXBsaWNhdGVzQ1RFCiAgICBXSEVSRSByb3dfbnVtID4gMQogICAgKTsKIikKYGBgCgoKIyMgVW5pZnkgRm9ybWF0IGluIExhbmRVc2UgY29sdW1uLiAKClNpbWlsYXIgdG8gd2hhdCBoYXBwZW5lZCB3aXRoIHRoZSBTb2xkQXNWYWNhbnQgY29sdW1uLCBpbiB0aGUgTGFuZFVzZSBjb2x1bW4gdGhlcmUgaXMgVkFDQU5UIFJFU0lERU5USUFMIExBTkQgKDM0NTAgcm93cykgYW5kIFZBQ0FOVCBSRVMgTEFORCgxNTQ5IHJvd3MpIHJlZmVycmluZyB0byB0aGUgc2FtZSBjYXRlZ29yeS4gVGhpcyBjYW4gYWxzbyBiZSBmaXhlZCB3aXRoIGEgY2FzZSBzdGF0ZW1lbnQKCmBgYHtyIGNhc2UgZXN0YXRlbWVudCB0byB1bmlmeSBmb3JtYXQgb2YgdGhlIExhbmRVc2UgY29sdW1uIGZvciB2YWNhbnQgcmVzaWRlbnRpYWwgbGFuZH0KZGJFeGVjdXRlKApjb24sCiJVUERBVEUgcmVhbGVzdGF0ZQogIFNFVCBMYW5kVXNlID0KY2FzZQogICAgd2hlbiBMYW5kVXNlID0gJ1ZBQ0FOVCBSRVMgTEFORCcgdGhlbiAnVkFDQU5UIFJFU0lERU5USUFMIExBTkQnCmVsc2UgTGFuZFVzZQplbmQ7CiIpCmBgYAoKVGhlIHNhbWUgaXNzdWUgaXMgaGFwcGVuaW5nIHdpdGggUkVTSURFTlRJQUwgQ09ORE8gYW5kIENPTkRPLCBzaW5jZSB0aGVyZSBpcyBhIHNlcGFyYXRlIGNhdGVnb3J5IGZvciBub24gcmVzaWRlbnRpYWwgY29uZG9zIENPTkRPTUlOSVVNIE9GQyAgT1IgT1RIRVIgQ09NIENPTkRPLiBJIHdpbGwgZml4IHRoaXMgd2l0aCBhIGNhc2Ugc3RhdGVtZW50IGFzIHdlbGwKYGBge3IgY2FzZSBlc3RhdGVtZW50IHRvIHVuaWZ5IGZvcm1hdCBvZiB0aGUgTGFuZFVzZSBjb2x1bW4gZm9yIGNvbmRvc30KZGJFeGVjdXRlKApjb24sCiJVUERBVEUgcmVhbGVzdGF0ZQogIFNFVCBMYW5kVXNlID0KY2FzZQogICAgd2hlbiBMYW5kVXNlID0gJ0NPTkRPJyB0aGVuICdSRVNJREVOVElBTCBDT05ETycKZWxzZSBMYW5kVXNlCmVuZDsKIikKYGBgCgpOb3cgdGhlIGRhdGEgaXMgY2xlYW4sIEkgY2FuIGJyaW5nIHRoZSByZXN1bHQgdG8gUiBhbmQgc3RvcmUgaXQgaW4gYSBkYXRhIGZyYW1lIGZvciBmdXJ0aGVyIGFuYWx5c2lzLCBpbmNsdWRpbmcgY3JlYXRpbmcgY2hhcnRzLgpgYGB7c3FsfQpyZWFsZXN0YXRlX2NsZWFuIDwtIHRibChjb24sICJyZWFsZXN0YXRlIikKcmVhbGVzdGF0ZV9jbGVhbiA8LSBjb2xsZWN0KHJlYWxlc3RhdGVfY2xlYW4pCnJlYWxlc3RhdGVfY2xlYW4KYGBgCgpgYGB7ciBzdG9yaW5nIGNsZWFuIGRhdGEgaW4gYSBkYXRhIGZyYW1lfQpyZWFsZXN0YXRlX2NsZWFuIDwtIHRibChjb24sICJyZWFsZXN0YXRlIikKcmVhbGVzdGF0ZV9jbGVhbiA8LSBjb2xsZWN0KHJlYWxlc3RhdGVfY2xlYW4pCnJlYWxlc3RhdGVfY2xlYW4KYGBgCgojIyBEZXNjcmlwdGl2ZSBhbmFseXNpcyBvbiBsYW5kIHVzZSAKCldlIGNhbiBzdG9yZSB0aGUgZW50aXJlIHRhYmxlIG9yIHRoZSByZXN1bHRzIG9mIG91ciBxdWVyaWVzIGluIGRhdGEgZnJhbWVzIGluIFIsIGFuZCB0aGlzIG1ha2VzIGl0IHZlcnkgZWFzeSB0byBjcmVhdGUgYSBjaGFydCB3aXRoIGl0IGluIFIuCmBgYHtzcWx9CmxhbmRfdXNlIDwtIHJlYWxlc3RhdGVfY2xlYW4lPiUKY291bnQoTGFuZFVzZSwgc29ydD1UUlVFKQpsYW5kX3VzZQpgYGAKCmBgYHtyIEluc3BlY3RpbmcgdGhlIGRpZmZlcmVudCB0eXBlcyBvZiBsYW5kIHVzZSBpbiB0aGUgZGF0YSBzZXR9CmxhbmRfdXNlIDwtIHJlYWxlc3RhdGVfY2xlYW4lPiUKY291bnQoTGFuZFVzZSwgc29ydD1UUlVFKQpsYW5kX3VzZQpgYGAKClRoZW4gb3JnYW5pemluZyB0aGUgZGF0YSB0byBkaXNwbGF5IGluIHRoZSBuZXh0IGNoYXJ0LCB3aGVyZSBJIHdpbGwgZGlzcGxheSB0aGUgNSBtb3JlIGNvbW1vbiBsYW5kIHVzZXMsIGFuZCB0aGUgcmVtYWluaW5nIDMyIGNhdGVnb3JpZXMgb2YgbGFuZCB1c2VzIHN1bW1hcml6ZWQgYXMgIk90aGVycyIgc2luY2UgdGhlaXIgbnVtYmVycyBhcmUgdmVyeSBsb3cuIFRoaXMgaW5mb3JtYXRpb24gd2lsbCBiZSBzdG9yZWQgaW4gdGhlIGxhbmRfdXNlX3AxIG9iamVjdCwgdGhhdCBJIHdpbGwgcGFzcyBpbnRvIGdncGxvdCgpIGxhdGVyLiAKCmBgYHtyfQpsYW5kX3VzZV9wMSA8LSBsYW5kX3VzZVtsYW5kX3VzZSRuID49MjQ3LCBdCkxhbmRfdXNlX290aGVycyA8LSBzdW0obGFuZF91c2Vbd2hpY2gobGFuZF91c2UkbjwxMDQ3KSwgMl0pCmxhbmRfdXNlX3AxW25yb3cobGFuZF91c2VfcDEpICsgMSxdID0gbGlzdCgiT1RIRVJTIiwgTGFuZF91c2Vfb3RoZXJzKQpsYW5kX3VzZV9wMSAKYGBgCgoKQ3JlYXRpbmcgYSBiYXIgY2hhciBmb3IgbGFuZCB1c2U6CgpJIGNyZWF0ZWQgYW5kIHN0b3JlZCBhIHRoZW1lIHRvIGFwcGx5IHRvIHRoZSBuZXh0IGNoYXJ0cyB0byBrZWVwIGNvbnNpc3RlbmN5LgpgYGB7ciBDcmVhdGluZyBhIGNvbnNpc3RlbnQgdGhlbWUgdG8gYXBwbHkgYWNyb3NzIGFsbCB0aGUgY2hhcnRzfQpteXRoZW1lIDwtIHRoZW1lKAogIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkFyaWFsIiwgZmFjZSA9ICJib2xkIiwgc2l6ZSA9ICgxNSksIGNvbG91ciA9ICIjNUE1QTVBIiksCiAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiQXJpYWwiLCBzaXplID0gKDEwKSwgY29sb3VyID0gIiM4MDgwODAiLCAgaGp1c3Q9YygxKSwgdmp1c3Q9YygwKSksCiAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJBcmlhbCIsIHNpemUgPSAoMTApLCBjb2xvdXIgPSAiIzgwODA4MCIpLAogIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiIzgwODA4MCIsIGZhY2UgPSAiYm9sZCIsIGZhbWlseSA9ICJBcmlhbCIpLAogIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICIjODA4MDgwIiwgZmFtaWx5ID0gIkFyaWFsIiksCiAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiIzVBNUE1QSIsIGZhbWlseSA9ICJBcmlhbCIpLAogIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiIzVBNUE1QSIsIGZhbWlseSA9ICJBcmlhbCIpCikKCmBgYAoKQ3JlYXRpbmcgYSBiYXIgY2hhciB3aXRoIGdncGxvdDI6CgpgYGB7c3FsfQpwMSA8LSBnZ3Bsb3QobGFuZF91c2VfcDEsIGFlcyh4ID0gTGFuZFVzZSwgeSA9IG4sIGZpbGwgPSBMYW5kVXNlKSkgKyAKICBnZW9tX2NvbCh3aWR0aCA9IDAuNCkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiRHVwbGV4IiwgIk90aGVycyIsICJSZXNpZGVudGlhbFxuQ29uZG8iLCAiU2luZ2xlXG5GYW1pbHkiLCAiVmFjYW50XG5SZXNpZGVudGlhbFxuTGFuZCIsICJaZXJvIExvdFxuTGluZSIpKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjYjNjZGUwIiwgIiMwMDViOTYiLCAiIzAxMUY0QiIsIiM4YTAzMDMiLCAiIzAzMzk2YyIsIiM2NDk3YjEiICkpKwogIGxhYnMoeT0gIk51bWJlciBvZiBwcm9wZXJ0aWVzIiwgeCA9ICJMYW5kIFVzZSIsIHRpdGxlID0gIkxhbmQgdXNlIG9mIHRoZSBEaWZmZXJlbnQgUHJvcGVydGllcyBTb2xkXG5JbiBOYWhzdmlsbGUsIFRlbm5lc3NlZSBmcm9tIDIwMTggdG8gMjAyMSIsIGZpbGwgPSAiTGFuZFVzZSIsIGNhcHRpb24gPSAiUHJvcGVydGllcyBzb2xkIGluIE5hc2h2aWxsZSwgVGVubmVzc2VlIGZyb20gMjAxOCB0byAyMDIxIiwgc3Vic3RpdGxlID0gIlNpbmdsZSBmYW1pbHkgaXMgYnkgZmFyIHRoZSBtb3N0IGNvbW1vbiBsYW5kIHVzZSBvZiB0aGUgcHJvcGVydGllcyBzb2xkIGR1cmluZyB0aGUgcGVyaW9kLCBmb2xsb3dlZCBieSByZXNpZGVudGlhbCBjb25kbyAiKSsKICBteXRoZW1lCnAxCmBgYAoKYGBge3IgQ3JlYXRpbmcgYSBiYXIgY2hhciBvZiBsYW5kIHVzZSwgZWNobz1UUlVFfQpwMSA8LSBnZ3Bsb3QobGFuZF91c2VfcDEsIGFlcyh4ID0gTGFuZFVzZSwgeSA9IG4sIGZpbGwgPSBMYW5kVXNlKSkgKyAKICBnZW9tX2NvbCh3aWR0aCA9IDAuNCkgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gYygiRHVwbGV4IiwgIk90aGVycyIsICJSZXNpZGVudGlhbFxuQ29uZG8iLCAiU2luZ2xlXG5GYW1pbHkiLCAiVmFjYW50XG5SZXNpZGVudGlhbFxuTGFuZCIsICJaZXJvIExvdFxuTGluZSIpKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjYjNjZGUwIiwgIiMwMDViOTYiLCAiIzAxMUY0QiIsIiM4YTAzMDMiLCAiIzAzMzk2YyIsIiM2NDk3YjEiICkpKwogIGxhYnMoeT0gIk51bWJlciBvZiBwcm9wZXJ0aWVzIiwgeCA9ICJMYW5kIFVzZSIsIHRpdGxlID0gIkxhbmQgdXNlIG9mIHRoZSBEaWZmZXJlbnQgUHJvcGVydGllcyBTb2xkXG5JbiBOYWhzdmlsbGUsIFRlbm5lc3NlZSBmcm9tIDIwMTggdG8gMjAyMSIsIGZpbGwgPSAiTGFuZFVzZSIsIGNhcHRpb24gPSAiUHJvcGVydGllcyBzb2xkIGluIE5hc2h2aWxsZSwgVGVubmVzc2VlIGZyb20gMjAxOCB0byAyMDIxIiwgc3Vic3RpdGxlID0gIlNpbmdsZSBmYW1pbHkgaXMgYnkgZmFyIHRoZSBtb3N0IGNvbW1vbiBsYW5kIHVzZSBvZiB0aGUgcHJvcGVydGllcyBzb2xkIGR1cmluZyB0aGUgcGVyaW9kLCBmb2xsb3dlZCBieSByZXNpZGVudGlhbCBjb25kbyAiKSsKICBteXRoZW1lCnAxCmBgYAojIyBEZXNjcmlwdGl2ZSBhbmFseXNpcyBvbiBzYWxlIGRhdGUKClRvIHRha2UgYSBxdWljayBsb29rIGFib3V0IHRoZSBwcm9wZXJ0eSBzYWxlcyBwZXIgeWVhciBJIHdpbGwgZ3JvdXAgdGhlIGluZm9ybWF0aW9uIGJ5IHllYXIgYW5kIHRoZW4gY291bnQgdGhlIHNhbGVzOgoKYGBge3NxbH0Kc2FsZV9kYXRlX3AyIDwtIHJlYWxlc3RhdGVfY2xlYW4gJT4lCm11dGF0ZSh5ZWFyID0geWVhcihTYWxlRGF0ZSkpICU+JQpncm91cF9ieSh5ZWFyKSAlPiUKY291bnQoKQpzYWxlX2RhdGVfcDIKYGBgCgpgYGB7ciBHcm91cGluZyBpbmZvcm1hdGlvbiBieSBzYWxlIGRhdGV9CnNhbGVfZGF0ZV9wMiA8LSByZWFsZXN0YXRlX2NsZWFuICU+JQptdXRhdGUoeWVhciA9IHllYXIoU2FsZURhdGUpKSAlPiUKZ3JvdXBfYnkoeWVhcikgJT4lCmNvdW50KCkKc2FsZV9kYXRlX3AyCmBgYAoKQ3JlYXRpbmcgYSBiYXIgY2hhciBmb3Igc2FsZXMgcGVyIHllYXI6CmBgYHtzcWx9CnAyIDwtIGdncGxvdChzYWxlX2RhdGVfcDIsIGFlcyh4ID0geWVhciwgeSA9IG4sIGZpbGwgPSB5ZWFyKSkgKyAKICBnZW9tX2NvbCh3aWR0aCA9IDAuNCkrCiAgbGFicyh5PSAiWWVhciBvZiBTYWxlIiwgeCA9ICJOdW1iZXIgb2YgU2FsZXMiLCB0aXRsZSA9ICJOdW1iZXIgb2YgU2FsZXMgUGVyIHllYXIgaW4gTmFoc3ZpbGxlLCBUZW5uZXNzZWUsXG5mcm9tIDIwMTggdG8gMjAyMSIsIGZpbGwgPSAieWVhciIsIGNhcHRpb24gPSAiUHJvcGVydGllcyBzb2xkIGluIE5hc2h2aWxsZSwgVGVubmVzc2VlIGZyb20gMjAxOCB0byAyMDIxIiwgc3VidGl0bGUgPSAiVGhlIHByb3BlcnRpZXMgc2FsZXMgaW5jcmVhc2VkIHN0ZWFkaWx5IGZyb20gMjAxOCB0byAyMDIwLCBkZWNsaW5pbmcgc2xpZ2h0bHkgYWdhaW4gaW4gMjAyMSAiKSsKICAgbXl0aGVtZSsKICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKcDIKYGBgCgpgYGB7ciBDcmVhdGluZyBhIGJhciBjaGFyIG9mIHJpZGUgbGVuZ3RocyBieSBjdXN0b21lciB0eXBlLCBlY2hvPVRSVUV9CnAyIDwtIGdncGxvdChzYWxlX2RhdGVfcDIsIGFlcyh4ID0geWVhciwgeSA9IG4sIGZpbGwgPSB5ZWFyKSkgKyAKICBnZW9tX2NvbCh3aWR0aCA9IDAuNCkrCiAgbGFicyh5PSAiWWVhciBvZiBTYWxlIiwgeCA9ICJOdW1iZXIgb2YgU2FsZXMiLCB0aXRsZSA9ICJOdW1iZXIgb2YgU2FsZXMgUGVyIHllYXIgaW4gTmFoc3ZpbGxlLCBUZW5uZXNzZWUsXG5mcm9tIDIwMTggdG8gMjAyMSIsIGZpbGwgPSAieWVhciIsIGNhcHRpb24gPSAiUHJvcGVydGllcyBzb2xkIGluIE5hc2h2aWxsZSwgVGVubmVzc2VlIGZyb20gMjAxOCB0byAyMDIxIiwgc3VidGl0bGUgPSAiVGhlIHByb3BlcnRpZXMgc2FsZXMgaW5jcmVhc2VkIHN0ZWFkaWx5IGZyb20gMjAxOCB0byAyMDIwLCBkZWNsaW5pbmcgc2xpZ2h0bHkgYWdhaW4gaW4gMjAyMSAiKSsKICAgbXl0aGVtZSsKICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikKcDIKYGBgClRvIHdyYXAgdXAsIHRoZSBPREJDLCBEQkksIGRwbHlyIGFuZCBkYnBseXIgdXNlZCB0b2dldGhlciwgYWxsb3cgdXMgdG8gY29ubmVjdCBvdXIgZGF0YWJhc2VzIHRvIFIgYW5kIGV4ZWN1dGUgcXVlcmllcyBkaXJlY3RseSBpbiBSLiBUaGUgcmVzdWx0cyBvZiBvdXIgcXVlcmllcyBjYW4gYmUgdmlzdWFsaXplZCBhcyB0aGV5IGFyZSBvbiB0aGUgZGF0YWJhc2Ugd2l0aCB0aGUgdGJsIGZ1bmN0aW9uIG9yIHdlIGNhbiBicmluZyB0aGUgcmVzdWx0cyB0byBSIGFuZCBzdG9yZSB0aGVtIGludG8gb2JqZWN0cyB3aXRoIGRiR2V0UXVlcnkgdG8gYW5hbHl6ZSB0aGUgaW5mb3JtYXRpb24gYW5kIGNyZWF0ZSBjaGFydHMgd2l0aG91dCB0aGUgbmVlZCBvZiBpbXBvcnRpbmcgb3IgZXhwb3J0aW5nIHRoZSBpbmZvcm1hdGlvbiBhcyBhIHNlcGFyYXRlIGZpbGUuIAoK