Required packages
Let’s install the necessary packages to proceed with this project.
library(readr)
library(tidyr)
library(dplyr)
library(Hmisc)
library(outliers)
library(stringr)
library(lubridate)
library(editrules)
library(jsonlite)
Executive Summary
The purpose of this project is to apply various data pre-processing techniques to process and tidy up our datasets before conducting the analysis. The two datasets are collected from Kaggle: the first set named IMDB 5000 Movie Dataset contains data of around 5000 movies of 2017 or earlier from IMDb and the second set named TMDb 5000 Movie Dataset contains data of around 5000 movies of 2017 or earlier from TMDb.
Since both the datasets have different data which are required for the further analysis, we’ll start by merging these two datasets into a single dataset and keeping just the required variables for the purpose of our further analysis. In all these datasets, each row represents a single movie. In order to keep the dataset simple for achieving our main aim of applying data pre-processing techniques, we’ll just select 5 variables from the first dataset and 9 variables from the second, thus, 13 variables in the merged one.
Then, we’ll try to understand the data by checking its structure and more. Then, we’ll tidy up and manipulate the data to ensure our final dataset follows the rules of tidy data. Then, we’ll scan the data for missing values, errors, and outliers. We’ll try to get info on outliers and solve them or remove them per the dataset. Finally, we’ll apply some data transformation techniques on the dataset as well.
Data
1. IMDB 5000 Movie Dataset
The dataset IMDB 5000 Movie Dataset contains the movie data from IMDb (Internet Movie Database). It contains 4998 observations and 28 variables (columns), which are described below. I’m going to use some of them as detailed later.
Data Source: IMDB 5000 Movie Dataset from Kaggle.
# importing the first dataset
movies_data1 <- read_csv("imdb_5000_movies.csv")
# remove duplicate rows
movies_data1 <- distinct(movies_data1)
# check the dimensions
dim(movies_data1)
[1] 4998 28
# preview the dataset
head(movies_data1)
NA
2. TMDB 5000 Movie Dataset
The dataset TMDB 5000 Movie Dataset contains the movie data from TMDb (The Movie Database). It contains 4803 observations and 20 variables (columns), which are described below. I’m going to use some of them as detailed later.
Data Source: TMDB 5000 Movie Dataset from Kaggle.
# importing the second dataset
movies_data2 <- read_csv("tmdb_5000_movies.csv")
# remove duplicate rows
movies_data2 <- distinct(movies_data2)
# check the dimensions
dim(movies_data2)
[1] 4803 20
# preview the dataset
head(movies_data2)
NA
3. Merged Dataset: movies
First of all, let’s extract and rename the necessary columns in both the datasets before merging them in the final dataset. Then finally, let’s merge both the datasets to create the movies data frame, whose columns (13 in count) are described below.
| 1 |
title |
Title of the movie |
| 2 |
country |
Country of the movie |
| 3 |
genres |
Genres of the movie |
| 4 |
imdb_score |
IMDb score for the movie |
| 5 |
fb_likes |
FB likes of the movie |
| 6 |
tagline |
Tagline of the movie |
| 7 |
release_date |
Release date of the movie |
| 8 |
language |
Language of the movie |
| 9 |
keywords |
Keywords for the movie |
| 10 |
popularity |
Popularity of the movie |
| 11 |
kmdb_vote |
Average vote for the movie |
| 12 |
budget |
Budget of the movie (in USD) |
| 13 |
revenue |
Gross revenue of the movie (in USD) |
# select and re-order the variables
movies_data1 <- movies_data1[, c(12, 21, 10, 26, 28)]
movies_data2 <- movies_data2[, c(18, 17, 12, 6, 5, 9, 19, 1, 13)]
# rename the selected column names
movies_data1 <- rename(movies_data1, title = movie_title, fb_likes = movie_facebook_likes)
movies_data2 <- rename(movies_data2, language = original_language, tmdb_score = vote_average)
# join both of the above datasets
movies <- inner_join(movies_data2, movies_data1, by = "title")
# check the dimensions
dim(movies)
[1] 0 13
# preview the dataset
head(movies)
NA
Why there are 0 rows? Maybe, data is untidy. It requires a fix!
The merged dataset has 0 rows or observations, but the other two datasets have thousands of rows.
So, what’s the reason of this error? The first data frame movies_data from the dataset imdb_5000_movies.csv has data errors. Its title column has some unwanted characters, which was very difficult to find (debug) since R shows it as a blank space, unfortunately!
I found and solved the problem using the below steps:
- Print the first
title from both the datasets.
movies_data1$title shows a space at last. It shouldn’t be since read_csv() trims spaces.
Nevertheless, replace the space with nothing using str_replace_all on movies_data1$title.
It didn’t work as well - no rows in the merged dataset yet.
- So, let’s print the first
title as UTF-8 using utf8ToInt().
- It shows that the space-looking character has an int value = 160.
- However, the space (" ") has an int value = 32, so it’s not a space.
Finally, replace that character with nothing using str_replace_all on movies_data1$title.
And at last, it worked! After merging the two datasets now, the merged dataset shows rows.
# let's print title from both datasets
movies_data1$title[1]
[1] "Avatar "
movies_data2$title[1]
[1] "Avatar"
# movies_data1$title has a space at last, so let's fix it
movies_data1$title <- str_replace_all(movies_data1$title, " $", "")
# join both of the above datasets
movies <- inner_join(movies_data2, movies_data1, by = "title")
# check the dimensions
dim(movies)
[1] 0 13
# preview the dataset
head(movies)
# it doesn't work - still, so is it not a space?
# let's print it in UTF-8 to understand the issue
utf8ToInt(movies_data1$title[1])
[1] 65 118 97 116 97 114 160
# it's not a space, since space != 160
utf8ToInt(" ")
[1] 32
# so, let's fix by replacing it
movies_data1$title <- str_replace_all(movies_data1$title, intToUtf8(160), "")
# join both of the above datasets
movies <- inner_join(movies_data2, movies_data1, by = "title")
# remove duplicate entries
movies <- distinct(movies)
# check the dimensions
dim(movies)
[1] 4602 13
# preview the dataset
head(movies)
NA
Understand
Summarise the types of variables and data structures, check the attributes in the data and apply proper data type conversions. In addition to the R codes and outputs, explain briefly the steps that you have taken. In this section, show that you have fulfilled minimum requirements 2-4.
Let’s understand the data - the data frame movies in this section.
- We’ll start with
dim() to understand its dimensions.
- Then, we’ll check its structure using
str().
- Lastly, we’ll check the labels for two variables -
language and country.
As visualized below, movies has 4602 observations (rows) and 13 variables (columns). Also, it has different types of data including character (chr), numeric (num), and date (Date), as it’s shown below in the output of the str() function. Then, we’ll do these:
- We’ll add factors (with labels) to
language since its data is two-letter country code.
- We’ll add factors (without labels) to
country since its data is already labelled.
- Finally, we’ll check the levels and the structure of the
movies to confirm the changes.
# check the dimensions
dim(movies)
[1] 4602 13
# check the structure
str(movies)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 4602 obs. of 13 variables:
$ title : chr "Avatar" "Pirates of the Caribbean: At World's End" "Spectre" "The Dark Knight Rises" ...
$ tagline : chr "Enter the World of Pandora." "At the end of the world, the adventure begins." "A Plan No One Escapes" "The Legend Ends" ...
$ release_date: Date, format: "2009-12-10" "2007-05-19" "2015-10-26" ...
$ language : chr "en" "en" "en" "en" ...
$ keywords : chr "[{\"id\": 1463, \"name\": \"culture clash\"}, {\"id\": 2964, \"name\": \"future\"}, {\"id\": 3386, \"name\": \""| __truncated__ "[{\"id\": 270, \"name\": \"ocean\"}, {\"id\": 726, \"name\": \"drug abuse\"}, {\"id\": 911, \"name\": \"exotic "| __truncated__ "[{\"id\": 470, \"name\": \"spy\"}, {\"id\": 818, \"name\": \"based on novel\"}, {\"id\": 4289, \"name\": \"secr"| __truncated__ "[{\"id\": 849, \"name\": \"dc comics\"}, {\"id\": 853, \"name\": \"crime fighter\"}, {\"id\": 949, \"name\": \""| __truncated__ ...
$ popularity : num 150.4 139.1 107.4 112.3 43.9 ...
$ tmdb_score : num 7.2 6.9 6.3 7.6 6.1 5.9 7.4 7.3 7.4 5.7 ...
$ budget : num 2.37e+08 3.00e+08 2.45e+08 2.50e+08 2.60e+08 2.58e+08 2.60e+08 2.80e+08 2.50e+08 2.50e+08 ...
$ revenue : num 2.79e+09 9.61e+08 8.81e+08 1.08e+09 2.84e+08 ...
$ country : chr "USA" "USA" "UK" "USA" ...
$ genres : chr "Action|Adventure|Fantasy|Sci-Fi" "Action|Adventure|Fantasy" "Action|Adventure|Thriller" "Action|Thriller" ...
$ imdb_score : num 7.9 7.1 6.8 8.5 6.6 6.2 7.8 7.5 7.5 6.9 ...
$ fb_likes : num 33000 0 85000 164000 24000 0 29000 118000 10000 197000 ...
# check the levels
levels(movies$language)
NULL
levels(movies$country)
NULL
# add factors to movies$language
movies <- mutate(movies, language = factor(language,
levels = c("af", "ar", "cn", "cs", "da", "de", "el", "en", "es", "fa", "fr", "he", "hi", "hu",
"id", "is", "it", "ja", "ko", "ky", "nb", "nl", "no", "pl", "ps", "pt", "ro", "ru",
"sl", "sv", "ta", "te", "th", "tr", "vi", "zh"),
labels = c("Afrikaans", "Arabic", "Chinese", "Czech", "Danish", "German", "Greek, Modern",
"English", "Spanish", "Persian", "French", "Hebrew", "Hindi", "Hungarian",
"Indonesian", "Icelandic", "Italian", "Japanese", "Korean", "Kyrgyz", "Norwegian",
"Dutch", "Norwegian", "Polish", "Pashto", "Portuguese", "Romanian", "Russian",
"Slovenian", "Swedish", "Tamil", "Telugu", "Thai", "Turkish", "Vietnamese",
"Chinese")
))
movies <- mutate(movies, country = factor(country))
# check the structure
str(movies)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 4602 obs. of 13 variables:
$ title : chr "Avatar" "Pirates of the Caribbean: At World's End" "Spectre" "The Dark Knight Rises" ...
$ tagline : chr "Enter the World of Pandora." "At the end of the world, the adventure begins." "A Plan No One Escapes" "The Legend Ends" ...
$ release_date: Date, format: "2009-12-10" "2007-05-19" "2015-10-26" ...
$ language : Factor w/ 34 levels "Afrikaans","Arabic",..: 8 8 8 8 8 8 8 8 8 8 ...
$ keywords : chr "[{\"id\": 1463, \"name\": \"culture clash\"}, {\"id\": 2964, \"name\": \"future\"}, {\"id\": 3386, \"name\": \""| __truncated__ "[{\"id\": 270, \"name\": \"ocean\"}, {\"id\": 726, \"name\": \"drug abuse\"}, {\"id\": 911, \"name\": \"exotic "| __truncated__ "[{\"id\": 470, \"name\": \"spy\"}, {\"id\": 818, \"name\": \"based on novel\"}, {\"id\": 4289, \"name\": \"secr"| __truncated__ "[{\"id\": 849, \"name\": \"dc comics\"}, {\"id\": 853, \"name\": \"crime fighter\"}, {\"id\": 949, \"name\": \""| __truncated__ ...
$ popularity : num 150.4 139.1 107.4 112.3 43.9 ...
$ tmdb_score : num 7.2 6.9 6.3 7.6 6.1 5.9 7.4 7.3 7.4 5.7 ...
$ budget : num 2.37e+08 3.00e+08 2.45e+08 2.50e+08 2.60e+08 2.58e+08 2.60e+08 2.80e+08 2.50e+08 2.50e+08 ...
$ revenue : num 2.79e+09 9.61e+08 8.81e+08 1.08e+09 2.84e+08 ...
$ country : Factor w/ 61 levels "Afghanistan",..: 60 60 59 60 60 60 60 60 59 60 ...
$ genres : chr "Action|Adventure|Fantasy|Sci-Fi" "Action|Adventure|Fantasy" "Action|Adventure|Thriller" "Action|Thriller" ...
$ imdb_score : num 7.9 7.1 6.8 8.5 6.6 6.2 7.8 7.5 7.5 6.9 ...
$ fb_likes : num 33000 0 85000 164000 24000 0 29000 118000 10000 197000 ...
# check the levels
levels(movies$language)
[1] "Afrikaans" "Arabic" "Chinese" "Czech" "Danish" "German"
[7] "Greek, Modern" "English" "Spanish" "Persian" "French" "Hebrew"
[13] "Hindi" "Hungarian" "Indonesian" "Icelandic" "Italian" "Japanese"
[19] "Korean" "Kyrgyz" "Norwegian" "Dutch" "Polish" "Pashto"
[25] "Portuguese" "Romanian" "Russian" "Slovenian" "Swedish" "Tamil"
[31] "Telugu" "Thai" "Turkish" "Vietnamese"
levels(movies$country)
[1] "Afghanistan" "Argentina" "Aruba" "Australia" "Bahamas"
[6] "Belgium" "Brazil" "Bulgaria" "Cambodia" "Cameroon"
[11] "Canada" "Chile" "China" "Colombia" "Czech Republic"
[16] "Denmark" "Egypt" "Finland" "France" "Georgia"
[21] "Germany" "Greece" "Hong Kong" "Hungary" "Iceland"
[26] "India" "Iran" "Ireland" "Israel" "Italy"
[31] "Japan" "Kenya" "Kyrgyzstan" "Libya" "Mexico"
[36] "Netherlands" "New Line" "New Zealand" "Nigeria" "Norway"
[41] "Official site" "Pakistan" "Panama" "Peru" "Philippines"
[46] "Poland" "Romania" "Russia" "Slovakia" "South Africa"
[51] "South Korea" "Soviet Union" "Spain" "Sweden" "Switzerland"
[56] "Taiwan" "Thailand" "Turkey" "UK" "USA"
[61] "West Germany"
Tidy & Manipulate Data I
According to The Comprehensive R Archive Network, "tidy data is a standard way of mapping the meaning of a dataset to its structure. A dataset is messy or tidy depending on how rows, columns and tables are matched up with observations, variables and types. In tidy data:
- Each variable forms a column.
- Each observation forms a row.
- Each type of observational unit forms a table."
That said, the data frame movies is not tidy since the two columns - keywords and genres are not tidy.
keywords: It contains data in the JSON format, and again, multiple values in a single variable.
genres: It contains multiple data values in a single variable separated by ‘|’ character.
Hence, we need to tidy up these two columns. I followed the below steps to tidy up these columns:
keywords
keywords is a column with JSON strings, for example, [{“id”: 1463, “name”: “culture clash”}, {“id”: 2964, “name”: “future”}].
- We’ll first convert this JSON into a delimited string, then separate the data into multiple observations (rows) using
unnest().
- We’ll use
fromJSON() to ease the process of parsing or reading JSON objects or strings, else it will become very cumbersome.
- The alternative approach could be to use regular expression to extract the
name from the above JSON to form a delimited string.
- Then, we’ll use
unnest() along with strsplit() on the delimited string to separate the delimited values over multiple rows.
genres
genres is a column with multiple delimited values (genres of the movie), for example, Action|Adventure|Fantasy|Sci-Fi.
- We’ll use
mutate() along with case_when() and grepl() to form a two case formula as given below:
- If
grepl() matches, then TRUE, i.e., genre is found.
- Else
FALSE, i.e., genre is not found.
- Using the above approach, we’ll separate the delimited values over multiple columns - one column for each genre.
# first convert JSON to delimited string
simplifyToString <- function(x) {
fromJSON(x["keywords"])$name %>% paste(collapse = '|')
}
movies <- mutate(movies, keywords = apply(movies, 1, simplifyToString))
# now unnest the `keywords` column into rows
movies <- movies %>% unnest(keywords = strsplit(keywords, "[|]"))
# now unnest the `genres` column into columns
movies <- movies %>% mutate(action = case_when(grepl("Action", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(adventure = case_when(grepl("Adventure", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(animation = case_when(grepl("Animation", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(biography = case_when(grepl("Biography", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(comedy = case_when(grepl("Comedy", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(crime = case_when(grepl("Crime", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(documentary = case_when(grepl("Documentary", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(drama = case_when(grepl("Drama", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(family = case_when(grepl("Family", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(fantasy = case_when(grepl("Fantasy", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(filmnoir = case_when(grepl("Film-Noir", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(history = case_when(grepl("History", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(horror = case_when(grepl("Horror", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(musical = case_when(grepl("Musical", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(mystery = case_when(grepl("Mystery", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(news = case_when(grepl("News", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(romance = case_when(grepl("Romance", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(scifi = case_when(grepl("Sci-Fi", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(short = case_when(grepl("Short", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(sport = case_when(grepl("Sport", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(thriller = case_when(grepl("Thriller", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(war = case_when(grepl("War", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(western = case_when(grepl("Western", genres) ~ TRUE, TRUE ~ FALSE))
# finally delete the `genres` column
movies <- subset(movies, select = -c(genres))
# preview the dataset
head(movies)
NA
Tidy & Manipulate Data II
Now, let’s create one variable from the existing variables. We have budget and revenue variables in our dataset. We can calculate the gross profit of each movie using the difference of these two columns. That said, we can create a profit variable by substracting budget from revenue. We’ll use the mutate() function to create this new variable (column).
# find profit for each movie
movies <- movies %>% mutate(profit = revenue - budget)
# preview the dataset
head(movies)
NA
Scan I
Let’s check the data for missin values (NAs), inconsistencies, and obvious errors (like ratings are more than 10 or less than 0). As we’ve visualized below, there are two columns having NAs. Their names and how we’ll deal with them is below:
tagline
tagline has just 3379 NAs, which is not a small number seeing that movies has 35065 observations = 0.096%.
- Since its percentage is less than 5% and it’s a character value, so it won’t cause any issues in calculations.
- Also, it’s tagline of a movie, which may be unavailable for some movies per its production companies.
- That said, we’ll leave the tagline with NAs as it is, since nothing can be done about it.
language
language has just 5 NAs, which is very small number seeing that movies has 35065 observations = 0.00014%.
- Since its percentage is less than 5%, we can leave it as it’s a character value, so no issues in calculations.
- Or, we can replace the NAs in this column with “English” assuming it’s suggested by the analyst.
- Of course, “English” is the most popular language for the movies, as it’s calcualted below.
# scan for infinites
sum(is.infinite(as.matrix(movies)))
[1] 0
# scan for NANs
sum(is.nan(as.matrix(movies)))
[1] 0
# scan for NAs
sum(is.na(movies))
[1] 3384
# scan for NAs per variable
colSums(is.na(movies))
title tagline release_date language keywords popularity tmdb_score
0 3379 0 5 0 0 0
budget revenue country imdb_score fb_likes action adventure
0 0 0 0 0 0 0
animation biography comedy crime documentary drama family
0 0 0 0 0 0 0
fantasy filmnoir history horror musical mystery news
0 0 0 0 0 0 0
romance scifi short sport thriller war western
0 0 0 0 0 0 0
profit
0
# check the incomplete cases
head(movies[!complete.cases(movies), ])
# check for obvious errors
rules <- editfile("rules.txt", type = "all")
violated <- violatedEdits(rules, movies)
summary(violated)
No violations detected, 0 checks evaluated to NA
NULL
# count the movies per `language`
movies %>% group_by(language) %>% summarise(no_rows = length(language)) %>%
arrange(desc(no_rows)) %>% head()
Factor `language` contains implicit NA, consider using `forcats::fct_explicit_na`
# replace `language` with NA to English
movies$language[is.na(movies$language)] <- "English"
Scan II
We’ll produce boxplots for checking outliers in out dataset. As it’s visualized below, our dataset has probable outliers. So, we calculated the z-scores of the variables and remove the outliered observations (rows) based on the absolute value of more than three.
Of course, it was not necessary to remove the outliers since the outliers are less than 5%. For budget , the outliers count is 698, which is just 0.0199% (which is almost negligible). Also, for revenue , the outliers count is 881, which is just 0.025% (it’s mostly negligible).
Then, after removing the outliers from those variables, we’ll produce the boxplots again. However, we visualized that the outliers are still present. That said, based on our observations, these outliers are not the same case because our dataset is not normally distributed, hence the outliers will be present. The reason being the variables we’re dealing here are budget and revenue, which are not normally distributed in the world. Some movies, like Avengers franchise, are produced with very high budgets, and they earn superb revenues as well. Then, some movies are very low budget movies.
Note: We’re going to do all these scannings on just two variables to keep the document length under 20 pages.
# draw boxplots
movies$budget %>% boxplot(main="Box Plot of Movie's Budget",
ylab="Budget ($)", col = "grey")

movies$revenue %>% boxplot(main="Box Plot of Movie's Revenue",
ylab="Revenue ($)", col = "grey")

# calculate z scores
zscores.budget <- movies$budget %>% scores(type = "z")
zscores.budget %>% summary()
Min. 1st Qu. Median Mean 3rd Qu. Max.
-0.8026 -0.6945 -0.3703 0.0000 0.2782 7.4119
zscores.revenue <- movies$revenue %>% scores(type = "z")
zscores.revenue %>% summary()
Min. 1st Qu. Median Mean 3rd Qu. Max.
-0.5906 -0.5661 -0.3771 0.0000 0.1185 13.2535
# number of outliers per z scores
length( which(abs(zscores.budget) >3 ))
[1] 698
length( which(abs(zscores.revenue) >3 ))
[1] 881
# exclude outliers
movies.budget <- movies[- which(abs(zscores.budget) >3 ),]
movies.revenue <- movies[- which(abs(zscores.revenue) >3 ),]
# boxplot excluding outliers
movies.budget$budget %>% boxplot(main="Box Plot of Movie's Budget (without outliers)",
ylab="Budget ($)", col = "grey")

movies.revenue$revenue %>% boxplot(main="Box Plot of Movie's Revenue (without outliers)",
ylab="Revenue ($)", col = "grey")

LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMiwgMjAxOSIKYXV0aG9yOiAiQXNodXRvc2ggS3VtYXIgU2luZ2ggKHMzNzk3NzY3KSIKc3VidGl0bGU6IEFzc2lnbm1lbnQgMwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgoKCiMjIFJlcXVpcmVkIHBhY2thZ2VzIAoKTGV0J3MgaW5zdGFsbCB0aGUgbmVjZXNzYXJ5IHBhY2thZ2VzIHRvIHByb2NlZWQgd2l0aCB0aGlzIHByb2plY3QuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQoKbGlicmFyeShyZWFkcikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShIbWlzYykKbGlicmFyeShvdXRsaWVycykKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShlZGl0cnVsZXMpCmxpYnJhcnkoanNvbmxpdGUpCgpgYGAKCgoKIyMgRXhlY3V0aXZlIFN1bW1hcnkKClRoZSBwdXJwb3NlIG9mIHRoaXMgcHJvamVjdCBpcyB0byBhcHBseSB2YXJpb3VzIGRhdGEgcHJlLXByb2Nlc3NpbmcgdGVjaG5pcXVlcyB0byBwcm9jZXNzIGFuZCB0aWR5IHVwIG91ciBkYXRhc2V0cyBiZWZvcmUgY29uZHVjdGluZyB0aGUgYW5hbHlzaXMuIFRoZSB0d28gZGF0YXNldHMgYXJlIGNvbGxlY3RlZCBmcm9tIEthZ2dsZTogdGhlIGZpcnN0IHNldCBuYW1lZCAqSU1EQiA1MDAwIE1vdmllIERhdGFzZXQqIGNvbnRhaW5zIGRhdGEgb2YgYXJvdW5kIDUwMDAgbW92aWVzIG9mIDIwMTcgb3IgZWFybGllciBmcm9tIElNRGIgYW5kIHRoZSBzZWNvbmQgc2V0IG5hbWVkICpUTURiIDUwMDAgTW92aWUgRGF0YXNldCogY29udGFpbnMgZGF0YSBvZiBhcm91bmQgNTAwMCBtb3ZpZXMgb2YgMjAxNyBvciBlYXJsaWVyIGZyb20gVE1EYi4KClNpbmNlIGJvdGggdGhlIGRhdGFzZXRzIGhhdmUgZGlmZmVyZW50IGRhdGEgd2hpY2ggYXJlIHJlcXVpcmVkIGZvciB0aGUgZnVydGhlciBhbmFseXNpcywgd2UnbGwgc3RhcnQgYnkgbWVyZ2luZyB0aGVzZSB0d28gZGF0YXNldHMgaW50byBhIHNpbmdsZSBkYXRhc2V0IGFuZCBrZWVwaW5nIGp1c3QgdGhlIHJlcXVpcmVkIHZhcmlhYmxlcyBmb3IgdGhlIHB1cnBvc2Ugb2Ygb3VyIGZ1cnRoZXIgYW5hbHlzaXMuIEluIGFsbCB0aGVzZSBkYXRhc2V0cywgZWFjaCByb3cgcmVwcmVzZW50cyBhIHNpbmdsZSBtb3ZpZS4gSW4gb3JkZXIgdG8ga2VlcCB0aGUgZGF0YXNldCBzaW1wbGUgZm9yIGFjaGlldmluZyBvdXIgbWFpbiBhaW0gb2YgYXBwbHlpbmcgZGF0YSBwcmUtcHJvY2Vzc2luZyB0ZWNobmlxdWVzLCB3ZSdsbCBqdXN0IHNlbGVjdCA1IHZhcmlhYmxlcyBmcm9tIHRoZSBmaXJzdCBkYXRhc2V0IGFuZCA5IHZhcmlhYmxlcyBmcm9tIHRoZSBzZWNvbmQsIHRodXMsIDEzIHZhcmlhYmxlcyBpbiB0aGUgbWVyZ2VkIG9uZS4KClRoZW4sIHdlJ2xsIHRyeSB0byB1bmRlcnN0YW5kIHRoZSBkYXRhIGJ5IGNoZWNraW5nIGl0cyBzdHJ1Y3R1cmUgYW5kIG1vcmUuIFRoZW4sIHdlJ2xsIHRpZHkgdXAgYW5kIG1hbmlwdWxhdGUgdGhlIGRhdGEgdG8gZW5zdXJlIG91ciBmaW5hbCBkYXRhc2V0IGZvbGxvd3MgdGhlIHJ1bGVzIG9mIHRpZHkgZGF0YS4gVGhlbiwgd2UnbGwgc2NhbiB0aGUgZGF0YSBmb3IgbWlzc2luZyB2YWx1ZXMsIGVycm9ycywgYW5kIG91dGxpZXJzLiBXZSdsbCB0cnkgdG8gZ2V0IGluZm8gb24gb3V0bGllcnMgYW5kIHNvbHZlIHRoZW0gb3IgcmVtb3ZlIHRoZW0gcGVyIHRoZSBkYXRhc2V0LiBGaW5hbGx5LCB3ZSdsbCBhcHBseSBzb21lIGRhdGEgdHJhbnNmb3JtYXRpb24gdGVjaG5pcXVlcyBvbiB0aGUgZGF0YXNldCBhcyB3ZWxsLgoKCiMjIERhdGEKCgojIyMgMS4gSU1EQiA1MDAwIE1vdmllIERhdGFzZXQKClRoZSBkYXRhc2V0ICpJTURCIDUwMDAgTW92aWUgRGF0YXNldCogY29udGFpbnMgdGhlIG1vdmllIGRhdGEgZnJvbSBJTURiIChJbnRlcm5ldCBNb3ZpZSBEYXRhYmFzZSkuIEl0IGNvbnRhaW5zIDQ5OTggb2JzZXJ2YXRpb25zIGFuZCAyOCB2YXJpYWJsZXMgKGNvbHVtbnMpLCB3aGljaCBhcmUgZGVzY3JpYmVkIGJlbG93LiBJJ20gZ29pbmcgdG8gdXNlIHNvbWUgb2YgdGhlbSBhcyBkZXRhaWxlZCBsYXRlci4KCioqRGF0YSBTb3VyY2U6KiogW0lNREIgNTAwMCBNb3ZpZSBEYXRhc2V0XShodHRwczovL3d3dy5rYWdnbGUuY29tL2Nhcm9semhhbmdkYy9pbWRiLTUwMDAtbW92aWUtZGF0YXNldCkgZnJvbSBLYWdnbGUuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQoKIyBpbXBvcnRpbmcgdGhlIGZpcnN0IGRhdGFzZXQKbW92aWVzX2RhdGExIDwtIHJlYWRfY3N2KCJpbWRiXzUwMDBfbW92aWVzLmNzdiIpCgojIHJlbW92ZSBkdXBsaWNhdGUgcm93cwptb3ZpZXNfZGF0YTEgPC0gZGlzdGluY3QobW92aWVzX2RhdGExKQoKIyBjaGVjayB0aGUgZGltZW5zaW9ucwpkaW0obW92aWVzX2RhdGExKQoKIyBwcmV2aWV3IHRoZSBkYXRhc2V0CmhlYWQobW92aWVzX2RhdGExKQoKYGBgCgoKIyMjIDIuIFRNREIgNTAwMCBNb3ZpZSBEYXRhc2V0CgpUaGUgZGF0YXNldCAqVE1EQiA1MDAwIE1vdmllIERhdGFzZXQqIGNvbnRhaW5zIHRoZSBtb3ZpZSBkYXRhIGZyb20gVE1EYiAoVGhlIE1vdmllIERhdGFiYXNlKS4gSXQgY29udGFpbnMgNDgwMyBvYnNlcnZhdGlvbnMgYW5kIDIwIHZhcmlhYmxlcyAoY29sdW1ucyksIHdoaWNoIGFyZSBkZXNjcmliZWQgYmVsb3cuIEknbSBnb2luZyB0byB1c2Ugc29tZSBvZiB0aGVtIGFzIGRldGFpbGVkIGxhdGVyLgoKKipEYXRhIFNvdXJjZToqKiBbVE1EQiA1MDAwIE1vdmllIERhdGFzZXRdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vdG1kYi90bWRiLW1vdmllLW1ldGFkYXRhL2RhdGEpIGZyb20gS2FnZ2xlLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KCiMgaW1wb3J0aW5nIHRoZSBzZWNvbmQgZGF0YXNldAptb3ZpZXNfZGF0YTIgPC0gcmVhZF9jc3YoInRtZGJfNTAwMF9tb3ZpZXMuY3N2IikKCiMgcmVtb3ZlIGR1cGxpY2F0ZSByb3dzCm1vdmllc19kYXRhMiA8LSBkaXN0aW5jdChtb3ZpZXNfZGF0YTIpCgojIGNoZWNrIHRoZSBkaW1lbnNpb25zCmRpbShtb3ZpZXNfZGF0YTIpCgojIHByZXZpZXcgdGhlIGRhdGFzZXQKaGVhZChtb3ZpZXNfZGF0YTIpCgpgYGAKCiMjIyAzLiBNZXJnZWQgRGF0YXNldDogYG1vdmllc2AKCkZpcnN0IG9mIGFsbCwgbGV0J3MgZXh0cmFjdCBhbmQgcmVuYW1lIHRoZSBuZWNlc3NhcnkgY29sdW1ucyBpbiBib3RoIHRoZSBkYXRhc2V0cyBiZWZvcmUgbWVyZ2luZyB0aGVtIGluIHRoZSBmaW5hbCBkYXRhc2V0LiBUaGVuIGZpbmFsbHksIGxldCdzIG1lcmdlIGJvdGggdGhlIGRhdGFzZXRzIHRvIGNyZWF0ZSB0aGUgYG1vdmllc2AgZGF0YSBmcmFtZSwgd2hvc2UgY29sdW1ucyAoMTMgaW4gY291bnQpIGFyZSBkZXNjcmliZWQgYmVsb3cuCgp8ICMgfCBWYXJpYWJsZSB8IERlc2NyaXB0aW9uIHwKfDotLXw6LS0tLS0tLS0tfDotLS0tLS0tLS0tLS18CnwxfHRpdGxlfFRpdGxlIG9mIHRoZSBtb3ZpZXwKfDJ8Y291bnRyeXxDb3VudHJ5IG9mIHRoZSBtb3ZpZXwKfDN8Z2VucmVzfEdlbnJlcyBvZiB0aGUgbW92aWV8Cnw0fGltZGJfc2NvcmV8SU1EYiBzY29yZSBmb3IgdGhlIG1vdmllfAp8NXxmYl9saWtlc3xGQiBsaWtlcyBvZiB0aGUgbW92aWV8Cnw2fHRhZ2xpbmV8VGFnbGluZSBvZiB0aGUgbW92aWV8Cnw3fHJlbGVhc2VfZGF0ZXxSZWxlYXNlIGRhdGUgb2YgdGhlIG1vdmllfAp8OHxsYW5ndWFnZXxMYW5ndWFnZSBvZiB0aGUgbW92aWV8Cnw5fGtleXdvcmRzfEtleXdvcmRzIGZvciB0aGUgbW92aWV8CnwxMHxwb3B1bGFyaXR5fFBvcHVsYXJpdHkgb2YgdGhlIG1vdmllfAp8MTF8a21kYl92b3RlfEF2ZXJhZ2Ugdm90ZSBmb3IgdGhlIG1vdmllfAp8MTJ8YnVkZ2V0fEJ1ZGdldCBvZiB0aGUgbW92aWUgKGluIFVTRCl8CnwxM3xyZXZlbnVlfEdyb3NzIHJldmVudWUgb2YgdGhlIG1vdmllIChpbiBVU0QpfAoKYGBge3IgbWVzc2FnZT1GQUxTRX0KCiMgc2VsZWN0IGFuZCByZS1vcmRlciB0aGUgdmFyaWFibGVzCgptb3ZpZXNfZGF0YTEgPC0gbW92aWVzX2RhdGExWywgYygxMiwgMjEsIDEwLCAyNiwgMjgpXQoKbW92aWVzX2RhdGEyIDwtIG1vdmllc19kYXRhMlssIGMoMTgsIDE3LCAxMiwgNiwgNSwgOSwgMTksIDEsIDEzKV0KCiMgcmVuYW1lIHRoZSBzZWxlY3RlZCBjb2x1bW4gbmFtZXMKCm1vdmllc19kYXRhMSA8LSByZW5hbWUobW92aWVzX2RhdGExLCB0aXRsZSA9IG1vdmllX3RpdGxlLCBmYl9saWtlcyA9IG1vdmllX2ZhY2Vib29rX2xpa2VzKQoKbW92aWVzX2RhdGEyIDwtIHJlbmFtZShtb3ZpZXNfZGF0YTIsIGxhbmd1YWdlID0gb3JpZ2luYWxfbGFuZ3VhZ2UsIHRtZGJfc2NvcmUgPSB2b3RlX2F2ZXJhZ2UpCgojIGpvaW4gYm90aCBvZiB0aGUgYWJvdmUgZGF0YXNldHMKbW92aWVzIDwtIGlubmVyX2pvaW4obW92aWVzX2RhdGEyLCBtb3ZpZXNfZGF0YTEsIGJ5ID0gInRpdGxlIikKCiMgY2hlY2sgdGhlIGRpbWVuc2lvbnMKZGltKG1vdmllcykKCiMgcHJldmlldyB0aGUgZGF0YXNldApoZWFkKG1vdmllcykKCmBgYAoKPGJyPgoKIyMjIFdoeSB0aGVyZSBhcmUgKjAgcm93cyo/IE1heWJlLCAqKmRhdGEgaXMgdW50aWR5LiBJdCByZXF1aXJlcyBhIGZpeCEqKgoKVGhlIG1lcmdlZCBkYXRhc2V0IGhhcyAwIHJvd3Mgb3Igb2JzZXJ2YXRpb25zLCBidXQgdGhlIG90aGVyIHR3byBkYXRhc2V0cyBoYXZlIHRob3VzYW5kcyBvZiByb3dzLgoKU28sIHdoYXQncyB0aGUgcmVhc29uIG9mIHRoaXMgZXJyb3I/IFRoZSBmaXJzdCBkYXRhIGZyYW1lIGBtb3ZpZXNfZGF0YWAgZnJvbSB0aGUgZGF0YXNldCAqaW1kYl81MDAwX21vdmllcy5jc3YqIGhhcyBkYXRhIGVycm9ycy4gSXRzIGB0aXRsZWAgY29sdW1uIGhhcyBzb21lIHVud2FudGVkIGNoYXJhY3RlcnMsIHdoaWNoIHdhcyB2ZXJ5IGRpZmZpY3VsdCB0byBmaW5kIChkZWJ1Zykgc2luY2UgUiBzaG93cyBpdCBhcyBhIGJsYW5rIHNwYWNlLCB1bmZvcnR1bmF0ZWx5IQoKSSBmb3VuZCBhbmQgc29sdmVkIHRoZSBwcm9ibGVtIHVzaW5nIHRoZSBiZWxvdyBzdGVwczoKCjEuIFByaW50IHRoZSBmaXJzdCBgdGl0bGVgIGZyb20gYm90aCB0aGUgZGF0YXNldHMuCjIuIGBtb3ZpZXNfZGF0YTEkdGl0bGVgIHNob3dzIGEgc3BhY2UgYXQgbGFzdC4gSXQgc2hvdWxkbid0IGJlIHNpbmNlIGByZWFkX2NzdigpYCB0cmltcyBzcGFjZXMuCjMuIE5ldmVydGhlbGVzcywgcmVwbGFjZSB0aGUgc3BhY2Ugd2l0aCBub3RoaW5nIHVzaW5nIGBzdHJfcmVwbGFjZV9hbGxgIG9uIGBtb3ZpZXNfZGF0YTEkdGl0bGVgLgoKNC4gSXQgZGlkbid0IHdvcmsgYXMgd2VsbCAtIG5vIHJvd3MgaW4gdGhlIG1lcmdlZCBkYXRhc2V0IHlldC4KCjUuIFNvLCBsZXQncyBwcmludCB0aGUgZmlyc3QgYHRpdGxlYCBhcyBVVEYtOCB1c2luZyBgdXRmOFRvSW50KClgLgo2LiBJdCBzaG93cyB0aGF0IHRoZSBzcGFjZS1sb29raW5nIGNoYXJhY3RlciBoYXMgYW4gaW50IHZhbHVlID0gMTYwLgo3LiBIb3dldmVyLCB0aGUgc3BhY2UgKCIgIikgaGFzIGFuIGludCB2YWx1ZSA9IDMyLCBzbyBpdCdzIG5vdCBhIHNwYWNlLgo4LiBGaW5hbGx5LCByZXBsYWNlIHRoYXQgY2hhcmFjdGVyIHdpdGggbm90aGluZyB1c2luZyBgc3RyX3JlcGxhY2VfYWxsYCBvbiBgbW92aWVzX2RhdGExJHRpdGxlYC4KCjkuIEFuZCBhdCBsYXN0LCBpdCB3b3JrZWQhIEFmdGVyIG1lcmdpbmcgdGhlIHR3byBkYXRhc2V0cyBub3csIHRoZSBtZXJnZWQgZGF0YXNldCBzaG93cyByb3dzLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KCiMgbGV0J3MgcHJpbnQgdGl0bGUgZnJvbSBib3RoIGRhdGFzZXRzCgptb3ZpZXNfZGF0YTEkdGl0bGVbMV0KbW92aWVzX2RhdGEyJHRpdGxlWzFdCgojIG1vdmllc19kYXRhMSR0aXRsZSBoYXMgYSBzcGFjZSBhdCBsYXN0LCBzbyBsZXQncyBmaXggaXQKbW92aWVzX2RhdGExJHRpdGxlIDwtIHN0cl9yZXBsYWNlX2FsbChtb3ZpZXNfZGF0YTEkdGl0bGUsICIgJCIsICIiKQoKIyBqb2luIGJvdGggb2YgdGhlIGFib3ZlIGRhdGFzZXRzCm1vdmllcyA8LSBpbm5lcl9qb2luKG1vdmllc19kYXRhMiwgbW92aWVzX2RhdGExLCBieSA9ICJ0aXRsZSIpCgojIGNoZWNrIHRoZSBkaW1lbnNpb25zCmRpbShtb3ZpZXMpCgojIHByZXZpZXcgdGhlIGRhdGFzZXQKaGVhZChtb3ZpZXMpCgojIGl0IGRvZXNuJ3Qgd29yayAtIHN0aWxsLCBzbyBpcyBpdCBub3QgYSBzcGFjZT8KCiMgbGV0J3MgcHJpbnQgaXQgaW4gVVRGLTggdG8gdW5kZXJzdGFuZCB0aGUgaXNzdWUKdXRmOFRvSW50KG1vdmllc19kYXRhMSR0aXRsZVsxXSkKCiMgaXQncyBub3QgYSBzcGFjZSwgc2luY2Ugc3BhY2UgIT0gMTYwCnV0ZjhUb0ludCgiICIpCgojIHNvLCBsZXQncyBmaXggYnkgcmVwbGFjaW5nIGl0Cm1vdmllc19kYXRhMSR0aXRsZSA8LSBzdHJfcmVwbGFjZV9hbGwobW92aWVzX2RhdGExJHRpdGxlLCBpbnRUb1V0ZjgoMTYwKSwgIiIpCgojIGpvaW4gYm90aCBvZiB0aGUgYWJvdmUgZGF0YXNldHMKbW92aWVzIDwtIGlubmVyX2pvaW4obW92aWVzX2RhdGEyLCBtb3ZpZXNfZGF0YTEsIGJ5ID0gInRpdGxlIikKCiMgcmVtb3ZlIGR1cGxpY2F0ZSBlbnRyaWVzCm1vdmllcyA8LSBkaXN0aW5jdChtb3ZpZXMpCgojIGNoZWNrIHRoZSBkaW1lbnNpb25zCmRpbShtb3ZpZXMpCgojIHByZXZpZXcgdGhlIGRhdGFzZXQKaGVhZChtb3ZpZXMpCgpgYGAKCgoKIyMgVW5kZXJzdGFuZCAKClN1bW1hcmlzZSB0aGUgdHlwZXMgb2YgdmFyaWFibGVzIGFuZCBkYXRhIHN0cnVjdHVyZXMsIGNoZWNrIHRoZSBhdHRyaWJ1dGVzIGluIHRoZSBkYXRhIGFuZCBhcHBseSBwcm9wZXIgZGF0YSB0eXBlIGNvbnZlcnNpb25zLiBJbiBhZGRpdGlvbiB0byB0aGUgUiBjb2RlcyBhbmQgb3V0cHV0cywgZXhwbGFpbiBicmllZmx5IHRoZSBzdGVwcyB0aGF0IHlvdSBoYXZlIHRha2VuLiBJbiB0aGlzIHNlY3Rpb24sIHNob3cgdGhhdCB5b3UgaGF2ZSBmdWxmaWxsZWQgbWluaW11bSByZXF1aXJlbWVudHMgMi00LgoKTGV0J3MgdW5kZXJzdGFuZCB0aGUgZGF0YSAtIHRoZSBkYXRhIGZyYW1lIGBtb3ZpZXNgIGluIHRoaXMgc2VjdGlvbi4KCjEuIFdlJ2xsIHN0YXJ0IHdpdGggYGRpbSgpYCB0byB1bmRlcnN0YW5kIGl0cyBkaW1lbnNpb25zLgoyLiBUaGVuLCB3ZSdsbCBjaGVjayBpdHMgc3RydWN0dXJlIHVzaW5nIGBzdHIoKWAuCjMuIExhc3RseSwgd2UnbGwgY2hlY2sgdGhlIGxhYmVscyBmb3IgdHdvIHZhcmlhYmxlcyAtIGBsYW5ndWFnZWAgYW5kIGBjb3VudHJ5YC4KCkFzIHZpc3VhbGl6ZWQgYmVsb3csIGBtb3ZpZXNgIGhhcyA0NjAyIG9ic2VydmF0aW9ucyAocm93cykgYW5kIDEzIHZhcmlhYmxlcyAoY29sdW1ucykuIEFsc28sIGl0IGhhcyBkaWZmZXJlbnQgdHlwZXMgb2YgZGF0YSBpbmNsdWRpbmcgY2hhcmFjdGVyIChjaHIpLCBudW1lcmljIChudW0pLCBhbmQgZGF0ZSAoRGF0ZSksIGFzIGl0J3Mgc2hvd24gYmVsb3cgaW4gdGhlIG91dHB1dCBvZiB0aGUgYHN0cigpYCBmdW5jdGlvbi4gVGhlbiwgd2UnbGwgZG8gdGhlc2U6Cgo0LiBXZSdsbCBhZGQgZmFjdG9ycyAod2l0aCBsYWJlbHMpIHRvIGBsYW5ndWFnZWAgc2luY2UgaXRzIGRhdGEgaXMgdHdvLWxldHRlciBjb3VudHJ5IGNvZGUuCjUuIFdlJ2xsIGFkZCBmYWN0b3JzICh3aXRob3V0IGxhYmVscykgdG8gYGNvdW50cnlgIHNpbmNlIGl0cyBkYXRhIGlzIGFscmVhZHkgbGFiZWxsZWQuCjYuIEZpbmFsbHksIHdlJ2xsIGNoZWNrIHRoZSBsZXZlbHMgYW5kIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGBtb3ZpZXNgIHRvIGNvbmZpcm0gdGhlIGNoYW5nZXMuCgpgYGB7cn0KCiMgY2hlY2sgdGhlIGRpbWVuc2lvbnMKZGltKG1vdmllcykKCiMgY2hlY2sgdGhlIHN0cnVjdHVyZQpzdHIobW92aWVzKQoKIyBjaGVjayB0aGUgbGV2ZWxzCgpsZXZlbHMobW92aWVzJGxhbmd1YWdlKQoKbGV2ZWxzKG1vdmllcyRjb3VudHJ5KQoKIyBhZGQgZmFjdG9ycyB0byBtb3ZpZXMkbGFuZ3VhZ2UKbW92aWVzIDwtIG11dGF0ZShtb3ZpZXMsIGxhbmd1YWdlID0gZmFjdG9yKGxhbmd1YWdlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoImFmIiwgImFyIiwgImNuIiwgImNzIiwgImRhIiwgImRlIiwgImVsIiwgImVuIiwgImVzIiwgImZhIiwgImZyIiwgImhlIiwgImhpIiwgImh1IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJpZCIsICJpcyIsICJpdCIsICJqYSIsICJrbyIsICJreSIsICJuYiIsICJubCIsICJubyIsICJwbCIsICJwcyIsICJwdCIsICJybyIsICJydSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2wiLCAic3YiLCAidGEiLCAidGUiLCAidGgiLCAidHIiLCAidmkiLCAiemgiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJBZnJpa2FhbnMiLCAiQXJhYmljIiwgIkNoaW5lc2UiLCAiQ3plY2giLCAiRGFuaXNoIiwgIkdlcm1hbiIsICJHcmVlaywgTW9kZXJuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFbmdsaXNoIiwgIlNwYW5pc2giLCAiUGVyc2lhbiIsICJGcmVuY2giLCAiSGVicmV3IiwgIkhpbmRpIiwgIkh1bmdhcmlhbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSW5kb25lc2lhbiIsICJJY2VsYW5kaWMiLCAiSXRhbGlhbiIsICJKYXBhbmVzZSIsICJLb3JlYW4iLCAiS3lyZ3l6IiwgIk5vcndlZ2lhbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRHV0Y2giLCAiTm9yd2VnaWFuIiwgIlBvbGlzaCIsICJQYXNodG8iLCAiUG9ydHVndWVzZSIsICJSb21hbmlhbiIsICJSdXNzaWFuIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTbG92ZW5pYW4iLCAiU3dlZGlzaCIsICJUYW1pbCIsICJUZWx1Z3UiLCAiVGhhaSIsICJUdXJraXNoIiwgIlZpZXRuYW1lc2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoaW5lc2UiKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkKCm1vdmllcyA8LSBtdXRhdGUobW92aWVzLCBjb3VudHJ5ID0gZmFjdG9yKGNvdW50cnkpKQoKIyBjaGVjayB0aGUgc3RydWN0dXJlCnN0cihtb3ZpZXMpCgojIGNoZWNrIHRoZSBsZXZlbHMKCmxldmVscyhtb3ZpZXMkbGFuZ3VhZ2UpCgpsZXZlbHMobW92aWVzJGNvdW50cnkpCgpgYGAKCgoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIAoKQWNjb3JkaW5nIHRvIFtUaGUgQ29tcHJlaGVuc2l2ZSBSIEFyY2hpdmUgTmV0d29ya10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3RpZHlyL3ZpZ25ldHRlcy90aWR5LWRhdGEuaHRtbCksICIqdGlkeSBkYXRhIGlzIGEgc3RhbmRhcmQgd2F5IG9mIG1hcHBpbmcgdGhlIG1lYW5pbmcgb2YgYSBkYXRhc2V0IHRvIGl0cyBzdHJ1Y3R1cmUuIEEgZGF0YXNldCBpcyBtZXNzeSBvciB0aWR5IGRlcGVuZGluZyBvbiBob3cgcm93cywgY29sdW1ucyBhbmQgdGFibGVzIGFyZSBtYXRjaGVkIHVwIHdpdGggb2JzZXJ2YXRpb25zLCB2YXJpYWJsZXMgYW5kIHR5cGVzLiBJbiB0aWR5IGRhdGE6KgoKMS4gKkVhY2ggdmFyaWFibGUgZm9ybXMgYSBjb2x1bW4uKgoyLiAqRWFjaCBvYnNlcnZhdGlvbiBmb3JtcyBhIHJvdy4qCjMuICpFYWNoIHR5cGUgb2Ygb2JzZXJ2YXRpb25hbCB1bml0IGZvcm1zIGEgdGFibGUuKiIKClRoYXQgc2FpZCwgdGhlIGRhdGEgZnJhbWUgKipgbW92aWVzYCBpcyBub3QgdGlkeSoqIHNpbmNlIHRoZSB0d28gY29sdW1ucyAtIGBrZXl3b3Jkc2AgYW5kIGBnZW5yZXNgIGFyZSBub3QgdGlkeS4KCiogYGtleXdvcmRzYDogSXQgY29udGFpbnMgZGF0YSBpbiB0aGUgSlNPTiBmb3JtYXQsIGFuZCBhZ2FpbiwgbXVsdGlwbGUgdmFsdWVzIGluIGEgc2luZ2xlIHZhcmlhYmxlLgoqIGBnZW5yZXNgOiBJdCBjb250YWlucyBtdWx0aXBsZSBkYXRhIHZhbHVlcyBpbiBhIHNpbmdsZSB2YXJpYWJsZSBzZXBhcmF0ZWQgYnkgJ3wnIGNoYXJhY3Rlci4KCkhlbmNlLCB3ZSBuZWVkIHRvIHRpZHkgdXAgdGhlc2UgdHdvIGNvbHVtbnMuIEkgZm9sbG93ZWQgdGhlIGJlbG93IHN0ZXBzIHRvIHRpZHkgdXAgdGhlc2UgY29sdW1uczoKCiMjIyMgYGtleXdvcmRzYAoKMS4gYGtleXdvcmRzYCBpcyBhIGNvbHVtbiB3aXRoIEpTT04gc3RyaW5ncywgZm9yIGV4YW1wbGUsICpbeyJpZCI6IDE0NjMsICJuYW1lIjogImN1bHR1cmUgY2xhc2gifSwgeyJpZCI6IDI5NjQsICJuYW1lIjogImZ1dHVyZSJ9XSouCjIuIFdlJ2xsIGZpcnN0IGNvbnZlcnQgdGhpcyBKU09OIGludG8gYSBkZWxpbWl0ZWQgc3RyaW5nLCB0aGVuIHNlcGFyYXRlIHRoZSBkYXRhIGludG8gbXVsdGlwbGUgb2JzZXJ2YXRpb25zIChyb3dzKSB1c2luZyBgdW5uZXN0KClgLgozLiBXZSdsbCB1c2UgYGZyb21KU09OKClgIHRvIGVhc2UgdGhlIHByb2Nlc3Mgb2YgcGFyc2luZyBvciByZWFkaW5nIEpTT04gb2JqZWN0cyBvciBzdHJpbmdzLCBlbHNlIGl0IHdpbGwgYmVjb21lIHZlcnkgY3VtYmVyc29tZS4KNC4gVGhlIGFsdGVybmF0aXZlIGFwcHJvYWNoIGNvdWxkIGJlIHRvIHVzZSByZWd1bGFyIGV4cHJlc3Npb24gdG8gZXh0cmFjdCB0aGUgYG5hbWVgIGZyb20gdGhlIGFib3ZlIEpTT04gdG8gZm9ybSBhIGRlbGltaXRlZCBzdHJpbmcuCjUuIFRoZW4sIHdlJ2xsIHVzZSBgdW5uZXN0KClgIGFsb25nIHdpdGggYHN0cnNwbGl0KClgIG9uIHRoZSBkZWxpbWl0ZWQgc3RyaW5nIHRvIHNlcGFyYXRlIHRoZSBkZWxpbWl0ZWQgdmFsdWVzIG92ZXIgbXVsdGlwbGUgcm93cy4KICAgIAojIyMjIGBnZW5yZXNgCgoxLiBgZ2VucmVzYCBpcyBhIGNvbHVtbiB3aXRoIG11bHRpcGxlIGRlbGltaXRlZCB2YWx1ZXMgKGdlbnJlcyBvZiB0aGUgbW92aWUpLCBmb3IgZXhhbXBsZSwgKkFjdGlvbnxBZHZlbnR1cmV8RmFudGFzeXxTY2ktRmkqLgoyLiBXZSdsbCB1c2UgYG11dGF0ZSgpYCBhbG9uZyB3aXRoIGBjYXNlX3doZW4oKWAgYW5kIGBncmVwbCgpYCB0byBmb3JtIGEgdHdvIGNhc2UgZm9ybXVsYSBhcyBnaXZlbiBiZWxvdzoKICAgICogSWYgYGdyZXBsKClgIG1hdGNoZXMsIHRoZW4gYFRSVUVgLCBpLmUuLCBnZW5yZSBpcyBmb3VuZC4KICAgICogRWxzZSBgRkFMU0VgLCBpLmUuLCBnZW5yZSBpcyBub3QgZm91bmQuCjMuIFVzaW5nIHRoZSBhYm92ZSBhcHByb2FjaCwgd2UnbGwgc2VwYXJhdGUgdGhlIGRlbGltaXRlZCB2YWx1ZXMgb3ZlciBtdWx0aXBsZSBjb2x1bW5zIC0gb25lIGNvbHVtbiBmb3IgZWFjaCBnZW5yZS4KCmBgYHtyIHdhcm5pbmc9RkFMU0V9CgojIGZpcnN0IGNvbnZlcnQgSlNPTiB0byBkZWxpbWl0ZWQgc3RyaW5nCgpzaW1wbGlmeVRvU3RyaW5nIDwtIGZ1bmN0aW9uKHgpIHsKICBmcm9tSlNPTih4WyJrZXl3b3JkcyJdKSRuYW1lICU+JSBwYXN0ZShjb2xsYXBzZSA9ICd8JykKfQoKbW92aWVzIDwtIG11dGF0ZShtb3ZpZXMsIGtleXdvcmRzID0gYXBwbHkobW92aWVzLCAxLCBzaW1wbGlmeVRvU3RyaW5nKSkKCiMgbm93IHVubmVzdCB0aGUgYGtleXdvcmRzYCBjb2x1bW4gaW50byByb3dzCm1vdmllcyA8LSBtb3ZpZXMgJT4lIHVubmVzdChrZXl3b3JkcyA9IHN0cnNwbGl0KGtleXdvcmRzLCAiW3xdIikpCgojIG5vdyB1bm5lc3QgdGhlIGBnZW5yZXNgIGNvbHVtbiBpbnRvIGNvbHVtbnMKCm1vdmllcyA8LSBtb3ZpZXMgJT4lIG11dGF0ZShhY3Rpb24gPSBjYXNlX3doZW4oZ3JlcGwoIkFjdGlvbiIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUoYWR2ZW50dXJlID0gY2FzZV93aGVuKGdyZXBsKCJBZHZlbnR1cmUiLCBnZW5yZXMpIH4gVFJVRSwgVFJVRSB+IEZBTFNFKSkKbW92aWVzIDwtIG1vdmllcyAlPiUgbXV0YXRlKGFuaW1hdGlvbiA9IGNhc2Vfd2hlbihncmVwbCgiQW5pbWF0aW9uIiwgZ2VucmVzKSB+IFRSVUUsIFRSVUUgfiBGQUxTRSkpCm1vdmllcyA8LSBtb3ZpZXMgJT4lIG11dGF0ZShiaW9ncmFwaHkgPSBjYXNlX3doZW4oZ3JlcGwoIkJpb2dyYXBoeSIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUoY29tZWR5ID0gY2FzZV93aGVuKGdyZXBsKCJDb21lZHkiLCBnZW5yZXMpIH4gVFJVRSwgVFJVRSB+IEZBTFNFKSkKbW92aWVzIDwtIG1vdmllcyAlPiUgbXV0YXRlKGNyaW1lID0gY2FzZV93aGVuKGdyZXBsKCJDcmltZSIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUoZG9jdW1lbnRhcnkgPSBjYXNlX3doZW4oZ3JlcGwoIkRvY3VtZW50YXJ5IiwgZ2VucmVzKSB+IFRSVUUsIFRSVUUgfiBGQUxTRSkpCm1vdmllcyA8LSBtb3ZpZXMgJT4lIG11dGF0ZShkcmFtYSA9IGNhc2Vfd2hlbihncmVwbCgiRHJhbWEiLCBnZW5yZXMpIH4gVFJVRSwgVFJVRSB+IEZBTFNFKSkKbW92aWVzIDwtIG1vdmllcyAlPiUgbXV0YXRlKGZhbWlseSA9IGNhc2Vfd2hlbihncmVwbCgiRmFtaWx5IiwgZ2VucmVzKSB+IFRSVUUsIFRSVUUgfiBGQUxTRSkpCm1vdmllcyA8LSBtb3ZpZXMgJT4lIG11dGF0ZShmYW50YXN5ID0gY2FzZV93aGVuKGdyZXBsKCJGYW50YXN5IiwgZ2VucmVzKSB+IFRSVUUsIFRSVUUgfiBGQUxTRSkpCm1vdmllcyA8LSBtb3ZpZXMgJT4lIG11dGF0ZShmaWxtbm9pciA9IGNhc2Vfd2hlbihncmVwbCgiRmlsbS1Ob2lyIiwgZ2VucmVzKSB+IFRSVUUsIFRSVUUgfiBGQUxTRSkpCm1vdmllcyA8LSBtb3ZpZXMgJT4lIG11dGF0ZShoaXN0b3J5ID0gY2FzZV93aGVuKGdyZXBsKCJIaXN0b3J5IiwgZ2VucmVzKSB+IFRSVUUsIFRSVUUgfiBGQUxTRSkpCm1vdmllcyA8LSBtb3ZpZXMgJT4lIG11dGF0ZShob3Jyb3IgPSBjYXNlX3doZW4oZ3JlcGwoIkhvcnJvciIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUobXVzaWNhbCA9IGNhc2Vfd2hlbihncmVwbCgiTXVzaWNhbCIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUobXlzdGVyeSA9IGNhc2Vfd2hlbihncmVwbCgiTXlzdGVyeSIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUobmV3cyA9IGNhc2Vfd2hlbihncmVwbCgiTmV3cyIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUocm9tYW5jZSA9IGNhc2Vfd2hlbihncmVwbCgiUm9tYW5jZSIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUoc2NpZmkgPSBjYXNlX3doZW4oZ3JlcGwoIlNjaS1GaSIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUoc2hvcnQgPSBjYXNlX3doZW4oZ3JlcGwoIlNob3J0IiwgZ2VucmVzKSB+IFRSVUUsIFRSVUUgfiBGQUxTRSkpCm1vdmllcyA8LSBtb3ZpZXMgJT4lIG11dGF0ZShzcG9ydCA9IGNhc2Vfd2hlbihncmVwbCgiU3BvcnQiLCBnZW5yZXMpIH4gVFJVRSwgVFJVRSB+IEZBTFNFKSkKbW92aWVzIDwtIG1vdmllcyAlPiUgbXV0YXRlKHRocmlsbGVyID0gY2FzZV93aGVuKGdyZXBsKCJUaHJpbGxlciIsIGdlbnJlcykgfiBUUlVFLCBUUlVFIH4gRkFMU0UpKQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUod2FyID0gY2FzZV93aGVuKGdyZXBsKCJXYXIiLCBnZW5yZXMpIH4gVFJVRSwgVFJVRSB+IEZBTFNFKSkKbW92aWVzIDwtIG1vdmllcyAlPiUgbXV0YXRlKHdlc3Rlcm4gPSBjYXNlX3doZW4oZ3JlcGwoIldlc3Rlcm4iLCBnZW5yZXMpIH4gVFJVRSwgVFJVRSB+IEZBTFNFKSkKCiMgZmluYWxseSBkZWxldGUgdGhlIGBnZW5yZXNgIGNvbHVtbgptb3ZpZXMgPC0gc3Vic2V0KG1vdmllcywgc2VsZWN0ID0gLWMoZ2VucmVzKSkKCiMgcHJldmlldyB0aGUgZGF0YXNldApoZWFkKG1vdmllcykKCmBgYAoKCgojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIAoKTm93LCBsZXQncyBjcmVhdGUgb25lIHZhcmlhYmxlIGZyb20gdGhlIGV4aXN0aW5nIHZhcmlhYmxlcy4gV2UgaGF2ZSBgYnVkZ2V0YCBhbmQgYHJldmVudWVgIHZhcmlhYmxlcyBpbiBvdXIgZGF0YXNldC4gV2UgY2FuIGNhbGN1bGF0ZSB0aGUgZ3Jvc3MgcHJvZml0IG9mIGVhY2ggbW92aWUgdXNpbmcgdGhlIGRpZmZlcmVuY2Ugb2YgdGhlc2UgdHdvIGNvbHVtbnMuIFRoYXQgc2FpZCwgd2UgY2FuIGNyZWF0ZSBhIGBwcm9maXRgIHZhcmlhYmxlIGJ5IHN1YnN0cmFjdGluZyBgYnVkZ2V0YCBmcm9tIGByZXZlbnVlYC4gV2UnbGwgdXNlIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSB0aGlzIG5ldyB2YXJpYWJsZSAoY29sdW1uKS4KCmBgYHtyfQoKIyBmaW5kIHByb2ZpdCBmb3IgZWFjaCBtb3ZpZQptb3ZpZXMgPC0gbW92aWVzICU+JSBtdXRhdGUocHJvZml0ID0gcmV2ZW51ZSAtIGJ1ZGdldCkKCiMgcHJldmlldyB0aGUgZGF0YXNldApoZWFkKG1vdmllcykKCmBgYAoKCgojIwlTY2FuIEkKCkxldCdzIGNoZWNrIHRoZSBkYXRhIGZvciBtaXNzaW4gdmFsdWVzIChOQXMpLCBpbmNvbnNpc3RlbmNpZXMsIGFuZCBvYnZpb3VzIGVycm9ycyAobGlrZSByYXRpbmdzIGFyZSBtb3JlIHRoYW4gMTAgb3IgbGVzcyB0aGFuIDApLiBBcyB3ZSd2ZSB2aXN1YWxpemVkIGJlbG93LCB0aGVyZSBhcmUgdHdvIGNvbHVtbnMgaGF2aW5nIGBOQWBzLiBUaGVpciBuYW1lcyBhbmQgaG93IHdlJ2xsIGRlYWwgd2l0aCB0aGVtIGlzIGJlbG93OgoKIyMjIyBgdGFnbGluZWAKCjEuIGB0YWdsaW5lYCBoYXMganVzdCAzMzc5IE5Bcywgd2hpY2ggaXMgbm90IGEgc21hbGwgbnVtYmVyIHNlZWluZyB0aGF0IG1vdmllcyBoYXMgMzUwNjUgb2JzZXJ2YXRpb25zID0gMC4wOTYlLgoyLiBTaW5jZSBpdHMgcGVyY2VudGFnZSBpcyBsZXNzIHRoYW4gNSUgYW5kIGl0J3MgYSBjaGFyYWN0ZXIgdmFsdWUsIHNvIGl0IHdvbid0IGNhdXNlIGFueSBpc3N1ZXMgaW4gY2FsY3VsYXRpb25zLgozLiBBbHNvLCBpdCdzIHRhZ2xpbmUgb2YgYSBtb3ZpZSwgd2hpY2ggbWF5IGJlIHVuYXZhaWxhYmxlIGZvciBzb21lIG1vdmllcyBwZXIgaXRzIHByb2R1Y3Rpb24gY29tcGFuaWVzLgo0LiBUaGF0IHNhaWQsIHdlJ2xsIGxlYXZlIHRoZSB0YWdsaW5lIHdpdGggTkFzIGFzIGl0IGlzLCBzaW5jZSBub3RoaW5nIGNhbiBiZSBkb25lIGFib3V0IGl0LgoKIyMjIyBgbGFuZ3VhZ2VgCgoxLiBgbGFuZ3VhZ2VgIGhhcyBqdXN0IDUgTkFzLCB3aGljaCBpcyB2ZXJ5IHNtYWxsIG51bWJlciBzZWVpbmcgdGhhdCBtb3ZpZXMgaGFzIDM1MDY1IG9ic2VydmF0aW9ucyA9IDAuMDAwMTQlLgoyLiBTaW5jZSBpdHMgcGVyY2VudGFnZSBpcyBsZXNzIHRoYW4gNSUsIHdlIGNhbiBsZWF2ZSBpdCBhcyBpdCdzIGEgY2hhcmFjdGVyIHZhbHVlLCBzbyBubyBpc3N1ZXMgaW4gY2FsY3VsYXRpb25zLgozLiBPciwgd2UgY2FuIHJlcGxhY2UgdGhlIE5BcyBpbiB0aGlzIGNvbHVtbiB3aXRoICJFbmdsaXNoIiBhc3N1bWluZyBpdCdzIHN1Z2dlc3RlZCBieSB0aGUgYW5hbHlzdC4KNC4gT2YgY291cnNlLCAiRW5nbGlzaCIgaXMgdGhlIG1vc3QgcG9wdWxhciBsYW5ndWFnZSBmb3IgdGhlIG1vdmllcywgYXMgaXQncyBjYWxjdWFsdGVkIGJlbG93LgoKYGBge3J9CgojIHNjYW4gZm9yIGluZmluaXRlcwpzdW0oaXMuaW5maW5pdGUoYXMubWF0cml4KG1vdmllcykpKQoKIyBzY2FuIGZvciBOQU5zCnN1bShpcy5uYW4oYXMubWF0cml4KG1vdmllcykpKQoKIyBzY2FuIGZvciBOQXMKc3VtKGlzLm5hKG1vdmllcykpCgojIHNjYW4gZm9yIE5BcyBwZXIgdmFyaWFibGUKY29sU3Vtcyhpcy5uYShtb3ZpZXMpKQoKIyBjaGVjayB0aGUgaW5jb21wbGV0ZSBjYXNlcwpoZWFkKG1vdmllc1shY29tcGxldGUuY2FzZXMobW92aWVzKSwgXSkKCiMgY2hlY2sgZm9yIG9idmlvdXMgZXJyb3JzCgpydWxlcyA8LSBlZGl0ZmlsZSgicnVsZXMudHh0IiwgdHlwZSA9ICJhbGwiKQp2aW9sYXRlZCA8LSB2aW9sYXRlZEVkaXRzKHJ1bGVzLCBtb3ZpZXMpCnN1bW1hcnkodmlvbGF0ZWQpCgojIGNvdW50IHRoZSBtb3ZpZXMgcGVyIGBsYW5ndWFnZWAKbW92aWVzICU+JSBncm91cF9ieShsYW5ndWFnZSkgJT4lIHN1bW1hcmlzZShub19yb3dzID0gbGVuZ3RoKGxhbmd1YWdlKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhub19yb3dzKSkgJT4lIGhlYWQoKQoKIyByZXBsYWNlIGBsYW5ndWFnZWAgd2l0aCBOQSB0byBFbmdsaXNoCm1vdmllcyRsYW5ndWFnZVtpcy5uYShtb3ZpZXMkbGFuZ3VhZ2UpXSA8LSAiRW5nbGlzaCIKCmBgYAoKCgojIwlTY2FuIElJCgpXZeKAmWxsIHByb2R1Y2UgYm94cGxvdHMgZm9yIGNoZWNraW5nIG91dGxpZXJzIGluIG91dCBkYXRhc2V0LiBBcyBpdOKAmXMgdmlzdWFsaXplZCBiZWxvdywgb3VyIGRhdGFzZXQgaGFzIHByb2JhYmxlIG91dGxpZXJzLiBTbywgd2UgY2FsY3VsYXRlZCB0aGUgei1zY29yZXMgb2YgdGhlIHZhcmlhYmxlcyBhbmQgcmVtb3ZlIHRoZSBvdXRsaWVyZWQgb2JzZXJ2YXRpb25zIChyb3dzKSBiYXNlZCBvbiB0aGUgYWJzb2x1dGUgdmFsdWUgb2YgbW9yZSB0aGFuIHRocmVlLgoKT2YgY291cnNlLCBpdCB3YXMgbm90IG5lY2Vzc2FyeSB0byByZW1vdmUgdGhlIG91dGxpZXJzIHNpbmNlIHRoZSBvdXRsaWVycyBhcmUgbGVzcyB0aGFuIDUlLiBGb3IgYnVkZ2V0ICwgdGhlCm91dGxpZXJzIGNvdW50IGlzIDY5OCwgd2hpY2ggaXMganVzdCAwLjAxOTklICh3aGljaCBpcyBhbG1vc3QgbmVnbGlnaWJsZSkuIEFsc28sIGZvciByZXZlbnVlICwgdGhlIG91dGxpZXJzIGNvdW50IGlzIDg4MSwgd2hpY2ggaXMganVzdCAwLjAyNSUgKGl0J3MgbW9zdGx5IG5lZ2xpZ2libGUpLgoKVGhlbiwgYWZ0ZXIgcmVtb3ZpbmcgdGhlIG91dGxpZXJzIGZyb20gdGhvc2UgdmFyaWFibGVzLCB3ZeKAmWxsIHByb2R1Y2UgdGhlIGJveHBsb3RzIGFnYWluLiBIb3dldmVyLCB3ZQp2aXN1YWxpemVkIHRoYXQgdGhlIG91dGxpZXJzIGFyZSBzdGlsbCBwcmVzZW50LiBUaGF0IHNhaWQsIGJhc2VkIG9uIG91ciBvYnNlcnZhdGlvbnMsIHRoZXNlIG91dGxpZXJzIGFyZSBub3QgdGhlIHNhbWUgY2FzZSBiZWNhdXNlIG91ciBkYXRhc2V0IGlzIG5vdCBub3JtYWxseSBkaXN0cmlidXRlZCwgaGVuY2UgdGhlIG91dGxpZXJzIHdpbGwgYmUgcHJlc2VudC4gVGhlIHJlYXNvbiBiZWluZyB0aGUgdmFyaWFibGVzIHdlJ3JlIGRlYWxpbmcgaGVyZSBhcmUgYnVkZ2V0IGFuZCByZXZlbnVlLCB3aGljaCBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGluIHRoZSB3b3JsZC4gU29tZSBtb3ZpZXMsIGxpa2UgQXZlbmdlcnMgZnJhbmNoaXNlLCBhcmUgcHJvZHVjZWQgd2l0aCB2ZXJ5IGhpZ2ggYnVkZ2V0cywgYW5kIHRoZXkgZWFybiBzdXBlcmIKcmV2ZW51ZXMgYXMgd2VsbC4gVGhlbiwgc29tZSBtb3ZpZXMgYXJlIHZlcnkgbG93IGJ1ZGdldCBtb3ZpZXMuCgpOb3RlOiBXZeKAmXJlIGdvaW5nIHRvIGRvIGFsbCB0aGVzZSBzY2FubmluZ3Mgb24ganVzdCB0d28gdmFyaWFibGVzIHRvIGtlZXAgdGhlIGRvY3VtZW50IGxlbmd0aCB1bmRlciAyMApwYWdlcy4KCmBgYHtyfQoKIyBkcmF3IGJveHBsb3RzCgptb3ZpZXMkYnVkZ2V0ICU+JSAgYm94cGxvdChtYWluPSJCb3ggUGxvdCBvZiBNb3ZpZSdzIEJ1ZGdldCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWxhYj0iQnVkZ2V0ICgkKSIsIGNvbCA9ICJncmV5IikKCm1vdmllcyRyZXZlbnVlICU+JSAgYm94cGxvdChtYWluPSJCb3ggUGxvdCBvZiBNb3ZpZSdzIFJldmVudWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsYWI9IlJldmVudWUgKCQpIiwgY29sID0gImdyZXkiKQoKIyBjYWxjdWxhdGUgeiBzY29yZXMKCnpzY29yZXMuYnVkZ2V0IDwtIG1vdmllcyRidWRnZXQgJT4lICBzY29yZXModHlwZSA9ICJ6IikKenNjb3Jlcy5idWRnZXQgJT4lIHN1bW1hcnkoKQoKenNjb3Jlcy5yZXZlbnVlIDwtIG1vdmllcyRyZXZlbnVlICU+JSAgc2NvcmVzKHR5cGUgPSAieiIpCnpzY29yZXMucmV2ZW51ZSAlPiUgc3VtbWFyeSgpCgojIG51bWJlciBvZiBvdXRsaWVycyBwZXIgeiBzY29yZXMKCmxlbmd0aCggd2hpY2goYWJzKHpzY29yZXMuYnVkZ2V0KSA+MyApKQoKbGVuZ3RoKCB3aGljaChhYnMoenNjb3Jlcy5yZXZlbnVlKSA+MyApKQoKIyBleGNsdWRlIG91dGxpZXJzCgptb3ZpZXMuYnVkZ2V0IDwtIG1vdmllc1stIHdoaWNoKGFicyh6c2NvcmVzLmJ1ZGdldCkgPjMgKSxdCgptb3ZpZXMucmV2ZW51ZSA8LSBtb3ZpZXNbLSB3aGljaChhYnMoenNjb3Jlcy5yZXZlbnVlKSA+MyApLF0KCiMgYm94cGxvdCBleGNsdWRpbmcgb3V0bGllcnMKCm1vdmllcy5idWRnZXQkYnVkZ2V0ICU+JSAgYm94cGxvdChtYWluPSJCb3ggUGxvdCBvZiBNb3ZpZSdzIEJ1ZGdldCAod2l0aG91dCBvdXRsaWVycykiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsYWI9IkJ1ZGdldCAoJCkiLCBjb2wgPSAiZ3JleSIpCgptb3ZpZXMucmV2ZW51ZSRyZXZlbnVlICU+JSAgYm94cGxvdChtYWluPSJCb3ggUGxvdCBvZiBNb3ZpZSdzIFJldmVudWUgKHdpdGhvdXQgb3V0bGllcnMpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5bGFiPSJSZXZlbnVlICgkKSIsIGNvbCA9ICJncmV5IikKCmBgYAoKCgojIwlUcmFuc2Zvcm0gCgpMZXQncyBwcm9kdWNlIHRoZSBoaXN0b2dyYW0gb2YgdGhlIGdyb3NzIHByb2ZpdCAoYHByb2ZpdGAgdmFyaWFibGUpLCB3aGljaCBzaG93cyB0aGF0IHRoZSBkYXRhIGlzIHJpZ2h0IHNrZXdlZC4gU28sIGxldCdzIHRyYW5zcG9zZSB0aGUgYHByb2ZpdGAgdmFyaWFibGUgdXNpbmcgYG11dGF0ZSgpYCB0byBwZXJmb3JtIGEgcmVjaXByb2NhbCB0cmFuc3BvcmF0aW9uIGZvciBldmVyeSByb3cgKG9ic2VydmF0aW9uKS4gVGhlIG1haW4gaWRlYSBiZWhpbmQgdGhpcyBzY2hlbWUgaXMgdG8gcmVkdWNlIHRoZSByaWdodCBza2V3ZWRuZXNzIG9mIG91ciBkaXN0cmlidXRpb24gYW5kIG1ha2UgaXQgYSBsaXR0bGUgbW9yZSBub3JtYWwsIHdoaWNoIGlzIHZpc2libGUgYWZ0ZXIgdGhlIHRyYW5zZm9ybWF0aW9uLgoKYGBge3J9CgojIHRyYW5zcG9zZSB0aGUgYHByb2ZpdGAgdmFyaWFibGUKCm1vdmllcy50cmFuc3Bvc2UgPC0gCiAgbW92aWVzICU+JSBtdXRhdGUocHJvZml0ID0gbW92aWVzJHByb2ZpdF4oLTEpKQoKIyBkcmF3IGhpc3RvZ3JhbXMgZm9yIGBwcm9maXRgIGFuZCB0cmFuc3Bvc2VkIGBwcm9maXRgCgpoaXN0KG1vdmllcyRwcm9maXQsCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTW92aWUncyBHcm9zcyBQcm9maXQiLCB4bGFiID0gIkdyb3NzIFByb2ZpdCAoJCkiKQoKaGlzdChtb3ZpZXMudHJhbnNwb3NlJHByb2ZpdCwKICAgICBtYWluID0gIkhpc3RvZ3JhbSBvZiBUcmFuc3Bvc2VkIEdyb3NzIFByb2ZpdCIsIHhsYWIgPSAiR3Jvc3MgUHJvZml0IFJlY2lwcm9jYWwiKQoKYGBgCgo8YnI+Cjxicj4K