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

Unify the format in the SoldAsVacant column

Right now we have Y, N, yes, no in the SoldAsVacant column:

```{r creating a second query to look for an especific parcel id where the address was null}
Error: attempt to use zero-length variable name
dbGetQuery(con, SoldAsVacant_query)

I used a case statement to unify the format of the SoldAsVacant column :

dbExecute(
con,
"UPDATE realestate
  SET SoldAsVacant =
case
    when SoldAsVacant = 'Y' then 'Yes'
when SoldAsVacant = 'N' then 'No'
else SoldAsVacant
end;
")
[1] 451

Checking the format is Yes and No:

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

Unify Format in LandUse column.

Similar to what happened with the SoldAsVacant column, in the LandUse column there is VACANT RESIDENTIAL LAND (3450 rows) and VACANT RES LAND(1549 rows) referring to the same category. This can also be fixed with a case statement

dbExecute(
con,
"UPDATE realestate
  SET LandUse =
case
    when LandUse = 'VACANT RES LAND' then 'VACANT RESIDENTIAL LAND'
else LandUse
end;
")
[1] 1549

The same issue is happening with RESIDENTIAL CONDO and CONDO, since there is a separate category for non residential condos CONDOMINIUM OFC OR OTHER COM CONDO. I will fix this with a case statement as well

dbExecute(
con,
"UPDATE realestate
  SET LandUse =
case
    when LandUse = 'CONDO' then 'RESIDENTIAL CONDO'
else LandUse
end;
")
[1] 247

Now the data is clean, I can bring the result to R and store it in a data frame for further analysis, including creating charts.

realestate_clean <- tbl(con, "realestate")
realestate_clean <- collect(realestate_clean)
realestate_clean

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