Required packages
library(readr)
library(tidyr)
library(dplyr)
library(outliers)
library(lubridate)
library(stringr)
library(knitr)
library(MVN)
library(forecast)
Executive Summary
In this assignment, four data sets will be taken from an open data source and inputted into RStudio. Each of them will be described in details in regard with their source and variables. At the beginning, they will be combined into two data sets, and the data types of all variables within these two data sets will be checked and converted into correct types if necassary. Then after tidying up them according to Hadley Wickham’s “Tidy Data” principles, they will be combined again into a final-form data set for the rest of this assignment. Straight after this, the new single data set will be scanned to seek for the existence of any missing values, special values or inconsistencies, which will in turn be solved by appropriate approaches. Hereafter, some new variables created based on calculation over existing variables will be added to the right of the data set. Finally, each numeric variable within this data set will be scanned again to seek for the existence of outliers, which will also be handled by proper methodologies. Transformation will then be performed on certain variables in the last part. All these steps metioned above are considered as major tasks in data preprocessing, after which further procedures in data analysis, such as exploring and modelling, can be implemented on this data set.
Data
The following four data sets are acquired from Kaggle (https://www.kaggle.com/stefanoleone992/imdb-extensive-dataset). They are orginally scraped from publicly available website - International Movie Database a.k.a. IMDb (https://www.imdb.com) and uploaded by Kaggle user.
movie <- read_csv("IMDb movies.csv")
people <- read_csv("IMDb names.csv")
rate <- read_csv("IMDb ratings.csv")
principal <- read_csv("IMDb title_principals.csv")
These four data sets are in turn inputted into R using the “read_csv” function from “readr” package as data frames, each of them has been assigned a new name as well.
Among them, the “movie” data frame includes 81,273 movies with 22 variables such as movie description, average rating, number of votes, genre, etc.
The “people” data frame includes 175,719 cast members with 20 variables such as birth details, death details, height, spouses, children, etc.
The “rate” data frame includes 81,273 rating details from demographic perspective with 49 variables.
Lastly, the “principal” data frame includes 377,848 cast members’ roles in movies with 6 variables such as IMDb title id, IMDb name id, order of importance in the movie, role, and characters played.
Since these four data frames contain nearly 100 variables (coloumns) in total, they will be in turn subsetted into smaller data frames so that only those variables (columns) useful for this assignment will be kept.
movie <- movie %>% select(imdb_title_id, title, date_published:duration, production_company, budget, worlwide_gross_income)
head(movie)
The “select” function is applied to subset “movie” data frame, leaving 8 variables as shown above. Their descriptions are as follows:
*imdb_title_id: identifier of each movie title assigned by IMDb website.
*title: title of each movie.
*date_published: date of release of each movie.
*genre: genres of each movie, a movie can have at most three genres according to IMDb’s rule.
*duration: length of each movie, recorded in minutes.
*production_company: production company (or studio) of each movie.
*budget: budget of each movie, mostly recorded in American Dollars (USD). Some are recorded in other currencies which are no longer available nowadays, especially those movices produced in late 19th/early 20th centuries.
*worlwide_gross_income: World-wide gross income of each movie, mostly recorded in American Dollars (USD). Some are recorded in other currencies which are no longer available nowadays, especially those movices produced in late 19th/early 20th centuries.
people <- people %>% select(imdb_name_id, name)
head(people)
The “select” function is applied to subset “people” data frame, leaving 2 variables as shown above. Their descriptions are as follows:
*imdb_name_id: identifier of each cast member assigned by IMDb website.
*name: full name of each cast member.
rate <- rate %>% select(imdb_title_id, total_votes, mean_vote)
head(rate)
The “select” function is applied to subset “rate” data frame, leaving 3 variables as shown above. Their descriptions are as follows:
*imdb_title_id: identifier of each movie title assigned by IMDb website.
*total_votes: total number of votes of each movie received by IMDb.
*mean_vote: average rating (on a 0-10 scale) determined by all the votes for each movie.
principal <- principal %>% select(imdb_title_id:category)
head(principal)
The “select” function is applied to subset “principal” data frame, leaving 4 variables as shown above. Their descriptions are as follows:
*imdb_title_id: identifier of each movie title assigned by IMDb website.
*ordering: Order of importance of each cast member in the movie. Notice that at most 10 most important cast members of each movie are recorded in this data frame.
*imdb_name_id: identifier of each cast member assigned by IMDb website.
*category: Category of job (role) done by the cast member.
The data frames “movie” and “rate” will be combined into a new data frame called “movie_rate”:
movie_rate <- movie %>% left_join(rate, by = "imdb_title_id")
head(movie_rate)
The function “left_join” is applied to conduct such combination so that all the original information in “movie” can be preserved, whereas only ratings that have a match in “movie” will be added to the right. Since “movie” and “rate” are connected via variable “imdb_title_id”, it is chosen as the key variable in “by” argument.
The “principal” data frame seems to be understandable for computers, but they will certainly raise confusion among human beings. To make it more informative for people, movie titles and cast members’ full names should be added into this data frame. Hence, the data frames “movie”, “principal” and “people” will be combined into a new data frame called “movie_principal_people”:
movie_principal_people <- principal %>% left_join(movie %>% select(imdb_title_id, title), by = "imdb_title_id") %>%
left_join(people %>% select(imdb_name_id, name), by = "imdb_name_id") %>% select(imdb_title_id, title, ordering, imdb_name_id, name, category)
head(movie_principal_people)
Notice that joining three data frames requires two steps of combination. Again, function “left_join” is applied throughout these two combinations so that all the original information in “principal” can be preserved, whereas only movie titles and cast members’ names that have a match in “principal” will be added to the right. Since “principal” and “movie” are connected via variable “imdb_title_id”, “principal” and “people” are connected via variable “imdb_name_id”, these two variables are chosen as the keys for the “by” argument in each step of combinations. To make the new data frame “movie_principal_people” easier to read, its columns are arranged in such way that movie title is straight after “imdb_title_id”, and cast members’ name is straight after “imdb_name_id”.
“movie_rate” and “movie_principal_people” will be joined together later after tidying up.
Understand
str(movie_rate)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 81273 obs. of 10 variables:
$ imdb_title_id : chr "tt0000574" "tt0001892" "tt0002101" "tt0002130" ...
$ title : chr "The Story of the Kelly Gang" "Den sorte drøm" "Cleopatra" "L'Inferno" ...
$ date_published : chr "1906-12-26" "1911-08-19" "1912-11-13" "1911-03-06" ...
$ genre : chr "Biography, Crime, Drama" "Drama" "Drama, History" "Adventure, Drama, Fantasy" ...
$ duration : num 70 53 100 68 60 85 120 120 55 121 ...
$ production_company : chr "J. and N. Tait" "Fotorama" "Helen Gardner Picture Players" "Milano Film" ...
$ budget : chr "$ 2250" NA "$ 45000" NA ...
$ worlwide_gross_income: chr NA NA NA NA ...
$ total_votes : num 537 171 420 2019 438 ...
$ mean_vote : num 6.3 6.1 5.2 6.9 5.8 6.8 6.2 7.1 5.4 6.6 ...
By applying “str” function, 10 variables are found in data frame “movie_rate”. Data types of each column are automatically “guessed” by this function, as shown above.
*Column “imdb_title_id” is of correct data type, because identifier of each movie is a string of characters and numbers.
*Column “title” is of correct data type, because movie titles mainly consist of characters, sometimes numbers are involved as well.
*Column “date_published” should be of date type instead of character. However, for some movies, only the number of year is recorded on IMDb, so this column cannot be directly converted into date type by “as.Date” function. Such conversion will be conducted later in the part “Scan I” after these input inconsistencies are solved.
*Column “genre” is of correct data type under current condition, where each movie can have more than one genre. However, if in some other cases where one movie can only have one genre, then this column can be considered as factor type as well.
*Column “duration” is of correct data type, since lengths of movies are expressed as integers.
*Column “production_company” is of correct data type, since names of the production companies (or studios) consist of characters.
*Column “budget” is of correct data type, because budget of each movie is recorded in number values together with corresponding currencies labelled in front of the number.
*Column “worlwide_gross_income” is of correct data type, since gross income is recorded in number values together with corresponding currencies labelled in front of the number.
*Column “total_votes” is of correct data type, because total number of votes of each movie is denoted by integers.
*Column “mean_vote” is of correct data type, since the average rating of each movie is expressed as a floating number over a scale of 0-10 with 2 significant numbers, where 0 indicates the movie is least favoured and 10 indicates the movie is most favoured.
str(movie_principal_people)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 377848 obs. of 6 variables:
$ imdb_title_id: chr "tt0000574" "tt0000574" "tt0000574" "tt0000574" ...
$ title : chr "The Story of the Kelly Gang" "The Story of the Kelly Gang" "The Story of the Kelly Gang" "The Story of the Kelly Gang" ...
$ ordering : num 1 2 3 4 5 6 7 8 9 10 ...
$ imdb_name_id : chr "nm0846887" "nm0846894" "nm3002376" "nm0170118" ...
$ name : chr "Elizabeth Tait" "John Tait" "Norman Campbell" "Bella Cola" ...
$ category : chr "actress" "actor" "actor" "actress" ...
movie_principal_people$ordering <- factor(movie_principal_people$ordering, levels = c("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"), ordered=TRUE)
movie_principal_people$category <- as.factor(movie_principal_people$category)
By applying “str” function, 6 variables are found in data frame “movie_principal_people”. Data types of each column are automatically “guessed” by this function, as shown above.
*Both “imdb_title_id” and “title” are of correct data types, as discussed before.
*Column “ordering” should be of factor type, because each movie can have at most 10 most important cast members, so values under this column can only be one of the 10 integers (from 1 to 10). Moreover, these 10 factors are ordinal and they should be ordered as 1<2<3<4<5<6>7<8<9<10, where 1 means the most important cast member and 10 means the least important cast member.
*Column “imdb_name_id” is of correct data type, since identifier of each cast member is a string of characters and numbers.
*Column “name” is of correct data type, because each cast member’s full name consists of only characters.
*Column “category” should be of factor type, since there are only 12 distinct values under this column: “actor”, “actress”, “archive_footage”, “archive_sound”, “cinematographer”, “composer”, “director”, “editor”, “producer”, “production_designer”, “self” and “writer”. Notice that there is no order between these 12 factors.
str(movie_principal_people)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 377848 obs. of 6 variables:
$ imdb_title_id: chr "tt0000574" "tt0000574" "tt0000574" "tt0000574" ...
$ title : chr "The Story of the Kelly Gang" "The Story of the Kelly Gang" "The Story of the Kelly Gang" "The Story of the Kelly Gang" ...
$ ordering : Ord.factor w/ 10 levels "1"<"2"<"3"<"4"<..: 1 2 3 4 5 6 7 8 9 10 ...
$ imdb_name_id : chr "nm0846887" "nm0846894" "nm3002376" "nm0170118" ...
$ name : chr "Elizabeth Tait" "John Tait" "Norman Campbell" "Bella Cola" ...
$ category : Factor w/ 12 levels "actor","actress",..: 2 1 1 2 7 9 9 9 6 5 ...
Now every column in “movie_principal_people” is of correct data types.
Tidy & Manipulate Data I
Although the data frame “movie_principal_people” seems to be tidy on its own, it is considered as untidy when people need to join it with “movie_rate”. Suppose that we want to figure out the 3 most important cast members of each movie, combining “movie_rate” and “movie_principal_people” directly will result in information of the same movie being repeated several times in different rows, namely information redundancy. Hence, before joining these two data frames, “movie_principal_people” should first be “spreaded” into wide format:
movie_principal_people <- movie_principal_people %>% unite(name_category, "name", "category", sep = " (")
movie_principal_people$name_category <- paste0(movie_principal_people$name_category, ")")
In order to employ the “spread” function correctly, the two columns, “name” and “category”, should be combined together to prevent any errors. This is done by the assistance from “unite” function, and the new column is named as “name_category”. The separator being used here is " (“, so that after pasting a”)" to each row of the new column “name_category”, the role of each cast member will be nicely demostrated inside a bracket next to their full names, as shown below.
head(movie_principal_people)
From now on, the “spread” function can be smoothly operated over the data frame “movie_principal_people”.
important_cast_3 <- movie_principal_people %>% filter(ordering <= 3) %>% select(imdb_title_id, ordering, name_category) %>% spread(key = ordering, value = name_category)
colnames(important_cast_3) <- c("imdb_title_id", "first", "second", "third")
Since only the 3 most important cast members are going to be added to “movie_rate” data frame, “filter” function has been applied to select the rows with ordering equals to either 1, 2 or 3 from “movie_principal_people”. Also, since titles of each movie already exist in the “movie_rate” data frame, only the columns “imdb_title_id”, “ordering” and “name_category” are chosen by “select” function. To “spread” the data frame, the key column should be “ordering” as it contains variable names, and the value column is “name_category” as it contains values forming multiple variables. The new data frame after spreading is temporarily called “important_cast_3”, and its columns are renamed as “imdb_title_id”, “first”, “second” and “third”. The first few rows are shown below:
head(important_cast_3)
Now that each movie only appears in one row, it can be joined to “movie_rate”.
movie_rate_3principals <- movie_rate %>% left_join(important_cast_3, by = "imdb_title_id")
movie_rate_principals <- movie_rate_3principals %>% unite(principal_casts, "first", "second", "third", sep = ", ", na.rm = TRUE)
“movie_rate” and “important_cast_3” are combined together through their common column, “imdb_title_id”. Here function “left_join” is applied so that all the original information from “movie_rate” can be reserved. To make the whole data frame more compendious, we compress the three columns containing the 3 most important cast members into a single column called “principal_casts”, by using “unite” function and skipping all the NA’s (NA’s exist since some movies have less then 3 most important cast members). The new data frame is named as “movie_rate_principals”, and its first few rows are shown below:
head(movie_rate_principals)
movie_rate_principals$principal_casts[movie_rate_principals$principal_casts == ""] <- "Unknown"
It is observed that some rows under the column “principal_casts” don’t contain any information. This is because not all movies are recorded in the data frame “important_cast_3” (which originally came from the data frame “principal”) in regard with information about their most important cast members. To make this column more straightforward (and less confusing), replace all the empty values by “Unknown”. The final form of data frame “movie_rate_principals” is shown below:
head(movie_rate_principals)
Notice that at this stage, all the combinations of data frames have been completed. “movie_rate_principals” will be used throughout the rest of this assignment.
Scan I
In this part, the presence of any missing value, special value and input inconsistency will be investigated throughout data frame “movie_rate_principals”.
colSums(is.na(movie_rate_principals))
imdb_title_id title date_published genre duration
0 0 0 0 0
production_company budget worlwide_gross_income total_votes mean_vote
4325 58469 51381 0 0
principal_casts
0
By checking number of NA’s of each column in “movie_rate_principals”, it is found that only “production_company”, “budget” and “worlwide_gross_income” contain missing values. There are several reasons that the rows having missing values should not be excluded from the data frame. First of all, all three columns mentioned previously possess considerably large propotion of NA’s (way larger than 5% - the recommended proportion for excluding missing data), especially for “budget” and “worlwide_gross_income” in which the number of missing values is more than half of number of observations (rows). Removing these rows will certainly result in losing too much significant information.
On the other hand, it is rather rational that these columns containing such large number of missing values. In the U.S., movie production has been industrialised & standardised for nearly a century, thus most movies originated in America are produced by specific production companies. However, in some other countries, movie production industry has only been established for a very short time, it might be relatively difficult for websites such as IMDb to trace back on the information about those production companies. This is why the column “production_company” possesses so many missing values.
For the other two columns, “budget” & “worlwide_gross_income”, the reason is pretty similar as well. Immature movie production industry makes it troublesome for people to record these values. In other scenarios, some production companies might be unwilling to publish their budget and gross income of certain movies, since a lot of “trading secrets” are involved in such information.
Nevertheless, missing values in “production_company” can be handled by replacing the NA’s with “Unknown”, so that people can understand that they are not recorded:
movie_rate_principals$production_company[is.na(movie_rate_principals$production_company)] <- "Unknown"
Function “is.na” is used to locate the positions of NA’s.
At this stage, missing values in “budget” and “worlwide_gross_income” are still left there, these two columns have too many NA’s so substituting column mean/median will be unmeaningful. Moreover, there also exist some input inconsistancies among them which will be discussed later.
sapply(movie_rate_principals, function(x) sum(is.infinite(x)))
imdb_title_id title date_published genre duration
0 0 0 0 0
production_company budget worlwide_gross_income total_votes mean_vote
0 0 0 0 0
principal_casts
0
sapply(movie_rate_principals, function(x) sum(is.nan(x)))
imdb_title_id title date_published genre duration
0 0 0 0 0
production_company budget worlwide_gross_income total_votes mean_vote
0 0 0 0 0
principal_casts
0
By applying “is.infinite” and “is.nan” functions, no special value is found throughout data frame “movie_rate_principals”.
However, there still exist some input inconsistancies in several columns. In “date_published”, most values are recorded in th form “YYYY-MM-DD”, whereas some other rows only contain the value of year. This situation is solved as shown below:
movie_rate_principals <- movie_rate_principals %>% separate(date_published, into = c("year_published", "month_published", "day_published"), sep = "-")
movie_rate_principals$month_published[is.na(movie_rate_principals$month_published)] <- 01
movie_rate_principals$day_published[is.na(movie_rate_principals$day_published)] <- 01
movie_rate_principals <- movie_rate_principals %>% unite(date_published, "year_published", "month_published", "day_published", sep = "-")
movie_rate_principals$date_published <- as.Date(movie_rate_principals$date_published)
For the first step, “date_published” is temporarily seperated into three columns, each contains the value of year, month and day respectively. By doing so, those rows that miss the value of month and day can be located by “is.na” function. To make it easier to convert column “date_published” into date data type, a not-so-justifiable assumption has been made on those movies (rows) only contatining year value, that is they are all released on the 1st of January. I chose to do so because in the succeeding parts of this assignment, only the value of year will be utilised for analysis, and it will be easier to extract year value from a date-type column by using “lubridate” package. However, in other scenarios where values of month and day are also required for analysis, such assumption should never be made.
To implememnt the above assumption, replace NA’s in month and day columns by “01”, then combine the temporary columns “year_published”, “month_published” and “day_published” back into “date_published” by using “unite” function. After this, “date_published” can be converted to date type through “as.Date” function. Now that every vairable in data frame “movie_rate_principals” is of correct data types, as shown below:
str(movie_rate_principals)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 81273 obs. of 11 variables:
$ imdb_title_id : chr "tt0000574" "tt0001892" "tt0002101" "tt0002130" ...
$ title : chr "The Story of the Kelly Gang" "Den sorte drøm" "Cleopatra" "L'Inferno" ...
$ date_published : Date, format: "1906-12-26" "1911-08-19" "1912-11-13" "1911-03-06" ...
$ genre : chr "Biography, Crime, Drama" "Drama" "Drama, History" "Adventure, Drama, Fantasy" ...
$ duration : num 70 53 100 68 60 85 120 120 55 121 ...
$ production_company : chr "J. and N. Tait" "Fotorama" "Helen Gardner Picture Players" "Milano Film" ...
$ budget : chr "$ 2250" NA "$ 45000" NA ...
$ worlwide_gross_income: chr NA NA NA NA ...
$ total_votes : num 537 171 420 2019 438 ...
$ mean_vote : num 6.3 6.1 5.2 6.9 5.8 6.8 6.2 7.1 5.4 6.6 ...
$ principal_casts : chr "Elizabeth Tait (actress), John Tait (actor), Norman Campbell (actor)" "Unknown" "Unknown" "Salvatore Papa (actor), Arturo Pirovano (actor), Giuseppe de Liguoro (actor)" ...
Inconsistancies also exist in the columns “budget” and “worlwide_gross_income”, where the strings consist of currency labels followed by the actual numeric values. Among them, most currencies are denoted by three capital letters, with the exception that U.S. Dollars is denoted by the symbol “$”. Such inconsistancy is handled as follows:
movie_rate_principals <- movie_rate_principals %>% separate(budget, into = c("budget_unit", "budget_value"), sep = " ")
movie_rate_principals <- movie_rate_principals %>% separate(worlwide_gross_income, into = c("gross_income_unit", "gross_income_value"), sep = " ")
movie_rate_principals$budget_unit[movie_rate_principals$budget_unit == "$"] <- "USD"
movie_rate_principals$gross_income_unit[movie_rate_principals$gross_income_unit == "$"] <- "USD"
Notice that there is always a space (" “) between currency labels and numeric values. Based on this special format created by IMDb, we can separate currency labels and numeric values into two columns simply by using”separate" function. During this step, currency columns are named as "%_unit“, and numeric columns are named as”%_value“. Now that all the”$" symbols in currency columns can be replaced by a more formal format “USD”, just like all the other currencies. Another benefit of separating currency labels and numeric values is that we can now perform mathematical calculations over budget and gross income for each movie. The current data types of columns in the data frame “movie_rate_principals” is shown below:
str(movie_rate_principals)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 81273 obs. of 13 variables:
$ imdb_title_id : chr "tt0000574" "tt0001892" "tt0002101" "tt0002130" ...
$ title : chr "The Story of the Kelly Gang" "Den sorte drøm" "Cleopatra" "L'Inferno" ...
$ date_published : Date, format: "1906-12-26" "1911-08-19" "1912-11-13" "1911-03-06" ...
$ genre : chr "Biography, Crime, Drama" "Drama" "Drama, History" "Adventure, Drama, Fantasy" ...
$ duration : num 70 53 100 68 60 85 120 120 55 121 ...
$ production_company: chr "J. and N. Tait" "Fotorama" "Helen Gardner Picture Players" "Milano Film" ...
$ budget_unit : chr "USD" NA "USD" NA ...
$ budget_value : chr "2250" NA "45000" NA ...
$ gross_income_unit : chr NA NA NA NA ...
$ gross_income_value: chr NA NA NA NA ...
$ total_votes : num 537 171 420 2019 438 ...
$ mean_vote : num 6.3 6.1 5.2 6.9 5.8 6.8 6.2 7.1 5.4 6.6 ...
$ principal_casts : chr "Elizabeth Tait (actress), John Tait (actor), Norman Campbell (actor)" "Unknown" "Unknown" "Salvatore Papa (actor), Arturo Pirovano (actor), Giuseppe de Liguoro (actor)" ...
Tidy & Manipulate Data II
In real life, people usually reference to the box office of a movie to determine its comprehensive performance. Although the actual quality of a movie is not always related to its box office, it worths the effort to investigate on how much profit a movie can produce. In this part, profit and profit rate of each movie will be calculated and added to the right of data frame “movie_rate_principals”, as follows:
movie_rate_principals$budget_value <- as.numeric(movie_rate_principals$budget_value)
movie_rate_principals$gross_income_value <- as.numeric(movie_rate_principals$gross_income_value)
movie_rate_principals_profit <- movie_rate_principals %>% filter(budget_unit == gross_income_unit) %>%
mutate(profit = gross_income_value - budget_value, profit_rate = profit/budget_value)
Before any calculation, the two columns “budget_value” and “gross_income_value” shoud first be converted into numeric types. Besides, it is only meaningful to calculate profit and profit rate when a movie’s budget and gross income are recorded in the same currency. The “filter” function is applied to filter out those rows that qualify such condition. Profit is calculated by budget subtracted from gross income, and profit rate equals to quotient of profit over budget. Notice that a negative profit/rate indicates the movie has lost money, while a positive profit/rate indicates the movie has actually made profit. These two new variables can be added to the right of data frame “movie_rate_principals” by using “mutate” function. The new data frame after mutation is renamed as “movie_rate_principals_profit”, and its first few rows are shown below:
head(movie_rate_principals_profit)
Scan II
In this part, each column from data frame “movie_rate_principals_profit” which contains numeric values will be checked whether outliers exist or not, with the assistance of Box Plot (a.k.a. Tukey’s method of outlier detection), Histogram and their corresponding z-scores (only when the values are approximately normally distributed, which is implied by a nearly symmetric histogram).
par(mfrow=c(1,2))
movie_rate_principals_profit$duration %>% boxplot(main="Box Plot of Movies' Duration", ylab = "Duration (in minutes)")
hist(movie_rate_principals_profit$duration, main = "Histogram of Movies' Duration")

According to Box Plot, there exist a considerable amount of outliers under column “duration”, all of them are located above the maximum calculated by Tukey’s Method. This is because, although most movies are maintained between 90-120 minutes for the favour of audience’s patience, there is no rule to limit a movie’s length and some directors may have a lot more content to be presented in their movies than others, which is pretty reasonable. On the other hand, as the shortest movie last more than 50 minutes, no outlier is found under the minimum. Since the histogram is dramatically skewed to the right, z-score test is not appropriate here.
par(mfrow=c(1,2))
movie_rate_principals_profit$budget_value %>% boxplot(main="Box Plot of Movies' Budget", ylab = "Budget")
hist(movie_rate_principals_profit$budget_value, main = "Histogram of Movies' Budget")

According to Box Plot, there exist a considerable amount of outliers under column “budget_value”, all of them are located above the maximum calculated by Tukey’s Method. This is because no rules are made upon maximum amount of budget a production company can put in their movies. In fact, it is pretty common that Sci-Fi movies cost way more budget than other genres. Besides, due to economic inflation, money value nowadays is way different than in the past, which will also result in budget value keeping increasing as time goes by. On the other hand, as budget must be a positive value, a default lower bound is set above zero, thus no outlier is found under the minimum. Since the histogram is completely unsymmetric, z-score test is not appropriate here.
par(mfrow=c(1,2))
movie_rate_principals_profit$gross_income_value %>% boxplot(main="Box Plot of Movies' Gross Income", ylab = "Gross Income")
hist(movie_rate_principals_profit$gross_income_value, main = "Histogram of Movies' Gross Income")

According to Box Plot, there exist a considerable amount of outliers under column “gross_income_value”, all of them are located above the maximum calculated by Tukey’s Method. Again, this is because no upper bound is predefined on how much gross income a movie can make. If a particular movie is outstandingly favoured by the audience, then its gross income will upsurprisingly reach a new peak. Besides, due to economic inflation, money value nowadays is way different than in the past, which will also result in gross income value keeping increasing as time goes by. On the other hand, as gross income must be a positive value, a default lower bound is set above zero, thus no outlier is found under the minimum. Since the histogram is nowhere close to be symmetric, z-score test is not appropriate here.
par(mfrow=c(1,2))
movie_rate_principals_profit$total_votes %>% boxplot(main="Box Plot of Movies' Number of Votes", ylab = "Number of Votes")
hist(movie_rate_principals_profit$total_votes, main = "Histogram of Movies' Number of Votes")

According to Box Plot, there exist a considerable amount of outliers under column “total_votes”, all of them are located above the maximum calculated by Tukey’s Method. This is because there is no upper bound to prevent audience from voting. If a particular movie is outstandingly favoured, or detested, by the audience, then it will be undoubtfully voted way more times than other movies. On the other hand, as number of votes cannot be negative, a default lower bound is set at zero, thus no outlier is found under the minimum. Since the histogram is dramatically skewed to the right, z-score test is not appropriate here.
par(mfrow=c(1,2))
movie_rate_principals_profit$mean_vote %>% boxplot(main="Box Plot of Movies' Average Rating", ylab = "Average Rating")
hist(movie_rate_principals_profit$mean_vote, main = "Histogram of Movies' Average Rating")

z.scores <- movie_rate_principals_profit$mean_vote %>% scores(type = "z")
z.scores %>% summary()
Min. 1st Qu. Median Mean 3rd Qu. Max.
-4.98290 -0.60703 0.07366 0.00000 0.68142 2.89367
which( abs(z.scores) >3 )
[1] 328 606 670 730 1024 1035 1038 1855 2161 2214 2297 2477 2901 2975 3046 3253 3467 3628 3747 3899 4026 4095 4131 4208 4331
[26] 4722 4852 4898 5029 5164 5191 5224 5501 5741 5747 5769 5785 5818 5976 6068 6143 6160 6168 6229 6357 6382 6559 6580 6645 6703
[51] 7000 7255 7468 7572 7582 7638 7662 7667 7688 7739 7773 7825 7904 7911 8148 8204 8216 8229 8286 8299 8355 8388 8617 8658 8666
length(which( abs(z.scores) >3 ))
[1] 75
According to Box Plot, there exist a considerable amount of outliers under column “mean_vote”, most of them are located under the minimum calculated by Tukey’s Method, while some are above the maximum as well. This turns out to be very different from previous cases. The reason behind is that rating of a movie is set over the scale of 0-10, i.e. there exist a predifined lower bound at 0 and upper bound at 10. While most movies receive average ratings between 4 and 9, outliers under the minimum are apparently more than those above the maxium, because it is reasonably harder for a movie to gain audience’s praise than disapproval. It is observed that the histogram is slightly skewed to the left, but still pretty close to be symmetric. Thus z-score test can be performed in this case. Based on the result, 75 outliers are identified by z-score test.
par(mfrow=c(1,2))
movie_rate_principals_profit$profit %>% boxplot(main="Box Plot of Movies' Profit", ylab = "Profit")
hist(movie_rate_principals_profit$profit, main = "Histogram of Movies' Profit")

According to Box Plot, there exist a considerable amount of outliers under column “profit”, most of them are located above the maximum calculated by Tukey’s Method, while some are below the minimum as well. Notice that there is neither predefined upper bound on how much profit a movie can make, nor predefined lower bound on how much money a movie can lose. Outliers above the maximum are apparently more than those under the minimum, because as a well-known recreational product, it is reasonably common that a movie is able to make certain amount of profit, unless it is extremely unpopular or with extremly low quality. Since the histogram is completely unsymmetric, z-score test is not appropriate here.
par(mfrow=c(1,2))
movie_rate_principals_profit$profit_rate %>% boxplot(main="Box Plot of Movies' Profit Rate", ylab = "Profit Rate")
hist(movie_rate_principals_profit$profit_rate, main = "Histogram of Movies' Profit Rate")

z.scores2 <- movie_rate_principals_profit$profit_rate %>% scores(type = "z")
z.scores2 %>% summary()
Min. 1st Qu. Median Mean 3rd Qu. Max.
-0.01447 -0.01447 -0.01445 0.00000 -0.01441 84.50993
which( abs(z.scores2) >3 )
[1] 618 7045
length(which( abs(z.scores2) >3 ))
[1] 2
According to Box Plot, there only exist a small number of outliers under column “profit_rate”, a few of them are located above the maximum calculated by Tukey’s Method and others are below the minimum. Similarly to “profit”, there is neither predefined upper bound nor predefined lower bound on possible profit rate. Except those values in upper tail, the histogram turns out to be nearly symmetric, thus the z-score test can be performed in this case. Based on its result, only 2 outliers are found.
In summary, outliers in columns “duration”, “budget_value”, “gross_income_value”, “total_votes”, “mean_vote” and “profit” should not be excluded, since their numbers are considerably large compared to the number of observations, i.e. they have taken up a large proportion of samples. They shoud neither be handled by imputation or capping, since none of these outliers mentioned above is caused by data entry/processing errors. Instead, these outliers can be “eliminated” by transformation so that their original values are “maintained” (which means people can achieve their original values by simply inversing the transformation) and at the same time, further statistical analysis can be applied over these variables. This will be discussed in the next part “Transform”.
However, outliers in the column “profit_rate” might be excluded upon demand, especially those 2 found by z-score test as they are relatively small in numbers compared to the total number of oberservations (which is 8908). Such elimination can be down as follows:
profit_rate_clean <- movie_rate_principals_profit$profit_rate[-which(abs(z.scores2) >3)]
Aside from univariate outliers, bivariate outliers may also exist in the data frame “movie_rate_principals_profit”. Suppose that people are interested in the relationship between budget and gross income of movies released between 2000 and 2010, then scatter plot and the Mahalanobis distance can be used to detect outliers based on budget gross income, as shown below:
par(mfrow=c(1,2))
gross_income_budget <- movie_rate_principals_profit %>% filter(year(date_published)<=2010, year(date_published)>=2000) %>% select(gross_income_value, budget_value)
gross_income_budget %>% plot(gross_income_value ~ budget_value, data = ., ylab = "Gross Income", xlab = "Budget", main = "Gross Income VS Budget")
results <- mvn(data = gross_income_budget, multivariateOutlierMethod = "quan", showOutliers = TRUE)

Before plotting, movies released between 2000 and 2010 should first be filtered out by applying “filter” function. Next, the columns “gross_income_value” and “budget_value” which are useful for plotting can be extracted and saved into a new data frame called “gross_income_budget”. To get the scatter plot, “plot” function is applied where y-axis represents gross income and x-axis represents budget. To acquire Mahalanobis distance, “mvn” function from the MVN package is applied and the method being used here is chi-square distribution critical value, as mentioned in the argument by “quan”.
In the scatter plot, it is observed that for most movies, small gross income is associated with small budget and large gross income is associated with large budget. However, there do exist some outliers located in bottom-right and top-right corners. In Mahalanobis distance, it is found that outliers have taken up nearly half of the number of observations. Thus they should not be excluded. Besides, they shoud neither be handled by imputation or capping, since none of them is caused by data entry/processing errors.
Transform
As mentioned in previous part, most of the variables in data frame “movie_rate_principals_profit” have their distributions skewed to the right, which is not preferred by statistical inference. In order to achieve a symmetric (normal) distribution, we can seek assistance from transformation. In the following, the coulmn “budget_value” will be used as an exmample:
par(mfrow=c(1,2))
hist(log(movie_rate_principals_profit$budget_value), main = "Histogram of log(budget value)")
bc_budget <- BoxCox(movie_rate_principals_profit$budget_value, lambda = "auto")
hist(bc_budget, main = "Histogram of Box-Cox Transformed Budget Value")

Since all values under the column “budget_value” are positive, logarithm transformation can be applied to reduce the right skewness. This can be simply implemented by using “log” function. Another approach to achieve a symmetric (normal) distribution is the BoxCox Transformation. This is done by using “BoxCox” function from the “forecast” package. As shown above, both transformations have behaved pretty well in turning the distribution into a nearly normal one. Notice that logarithm transformation can only be applied when all the values are positive. If there exist any zero or negative values (such as in “profit” and “profit_rate”), then people should turn to reciprocal (or BoxCox) transformation to reduce right skewness.
Across all the numeric variables in data frame “movie_rate_principals_profit”, it is also observed that they are set on completely different ranges to each other. Such condition might have serious impact on statistical anlysis or machine learning prediction accuracy. In order to reduce such impact, all these numeric variables should be normalised into similar scales, which can be done by z-score standardisation as follows:
z_movie_rate_principals_profit <- movie_rate_principals_profit %>% select(duration, budget_value, gross_income_value, total_votes, mean_vote, profit, profit_rate) %>% scale(center = TRUE, scale = TRUE)
attributes(z_movie_rate_principals_profit)
$dim
[1] 8908 7
$dimnames
$dimnames[[1]]
NULL
$dimnames[[2]]
[1] "duration" "budget_value" "gross_income_value" "total_votes" "mean_vote" "profit"
[7] "profit_rate"
$`scaled:center`
duration budget_value gross_income_value total_votes mean_vote profit
1.062924e+02 2.470251e+07 6.569612e+07 6.893791e+04 6.424248e+00 4.099361e+07
profit_rate
6.594215e+02
$`scaled:scale`
duration budget_value gross_income_value total_votes mean_vote profit
1.891083e+01 3.564445e+07 1.529234e+08 1.416507e+05 1.028367e+00 1.282126e+08
profit_rate
4.563320e+04
Firsly, all 7 numeric variables should be extracted from data frame “movie_rate_principals_profit”. Then z-score standardisation can be implemented by the “scale” fucntion with both arguments “center” and “scale” setting as TRUE.
LS0tCnRpdGxlOiAiTUFUSDIzNDkgRGF0YSBXcmFuZ2xpbmcgU2VtZXN0ZXIgMSwgMjAyMCIKYXV0aG9yOiAiV2VpcHUgWHVlIFMzODQ4NjUzIgpzdWJ0aXRsZTogQXNzaWdubWVudCAyCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQojIyBSZXF1aXJlZCBwYWNrYWdlcyAKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkob3V0bGllcnMpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoTVZOKQpsaWJyYXJ5KGZvcmVjYXN0KQpgYGAKCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IApJbiB0aGlzIGFzc2lnbm1lbnQsIGZvdXIgZGF0YSBzZXRzIHdpbGwgYmUgdGFrZW4gZnJvbSBhbiBvcGVuIGRhdGEgc291cmNlIGFuZCBpbnB1dHRlZCBpbnRvIFJTdHVkaW8uIEVhY2ggb2YgdGhlbSB3aWxsIGJlIGRlc2NyaWJlZCBpbiBkZXRhaWxzIGluIHJlZ2FyZCB3aXRoIHRoZWlyIHNvdXJjZSBhbmQgdmFyaWFibGVzLiBBdCB0aGUgYmVnaW5uaW5nLCB0aGV5IHdpbGwgYmUgY29tYmluZWQgaW50byB0d28gZGF0YSBzZXRzLCBhbmQgdGhlIGRhdGEgdHlwZXMgb2YgYWxsIHZhcmlhYmxlcyB3aXRoaW4gdGhlc2UgdHdvIGRhdGEgc2V0cyB3aWxsIGJlIGNoZWNrZWQgYW5kIGNvbnZlcnRlZCBpbnRvIGNvcnJlY3QgdHlwZXMgaWYgbmVjYXNzYXJ5LiBUaGVuIGFmdGVyIHRpZHlpbmcgdXAgdGhlbSBhY2NvcmRpbmcgdG8gSGFkbGV5IFdpY2toYW3igJlzIOKAnFRpZHkgRGF0YeKAnSBwcmluY2lwbGVzLCB0aGV5IHdpbGwgYmUgY29tYmluZWQgYWdhaW4gaW50byBhIGZpbmFsLWZvcm0gZGF0YSBzZXQgZm9yIHRoZSByZXN0IG9mIHRoaXMgYXNzaWdubWVudC4gU3RyYWlnaHQgYWZ0ZXIgdGhpcywgdGhlIG5ldyBzaW5nbGUgZGF0YSBzZXQgd2lsbCBiZSBzY2FubmVkIHRvIHNlZWsgZm9yIHRoZSBleGlzdGVuY2Ugb2YgYW55IG1pc3NpbmcgdmFsdWVzLCBzcGVjaWFsIHZhbHVlcyBvciBpbmNvbnNpc3RlbmNpZXMsIHdoaWNoIHdpbGwgaW4gdHVybiBiZSBzb2x2ZWQgYnkgYXBwcm9wcmlhdGUgYXBwcm9hY2hlcy4gSGVyZWFmdGVyLCBzb21lIG5ldyB2YXJpYWJsZXMgY3JlYXRlZCBiYXNlZCBvbiBjYWxjdWxhdGlvbiBvdmVyIGV4aXN0aW5nIHZhcmlhYmxlcyB3aWxsIGJlIGFkZGVkIHRvIHRoZSByaWdodCBvZiB0aGUgZGF0YSBzZXQuIEZpbmFsbHksIGVhY2ggbnVtZXJpYyB2YXJpYWJsZSB3aXRoaW4gdGhpcyBkYXRhIHNldCB3aWxsIGJlIHNjYW5uZWQgYWdhaW4gdG8gc2VlayBmb3IgdGhlIGV4aXN0ZW5jZSBvZiBvdXRsaWVycywgd2hpY2ggd2lsbCBhbHNvIGJlIGhhbmRsZWQgYnkgcHJvcGVyIG1ldGhvZG9sb2dpZXMuIFRyYW5zZm9ybWF0aW9uIHdpbGwgdGhlbiBiZSBwZXJmb3JtZWQgb24gY2VydGFpbiB2YXJpYWJsZXMgaW4gdGhlIGxhc3QgcGFydC4gQWxsIHRoZXNlIHN0ZXBzIG1ldGlvbmVkIGFib3ZlIGFyZSBjb25zaWRlcmVkIGFzIG1ham9yIHRhc2tzIGluIGRhdGEgcHJlcHJvY2Vzc2luZywgYWZ0ZXIgd2hpY2ggZnVydGhlciBwcm9jZWR1cmVzIGluIGRhdGEgYW5hbHlzaXMsIHN1Y2ggYXMgZXhwbG9yaW5nIGFuZCBtb2RlbGxpbmcsIGNhbiBiZSBpbXBsZW1lbnRlZCBvbiB0aGlzIGRhdGEgc2V0LgoKCiMjIERhdGEgClRoZSBmb2xsb3dpbmcgZm91ciBkYXRhIHNldHMgYXJlIGFjcXVpcmVkIGZyb20gS2FnZ2xlIChodHRwczovL3d3dy5rYWdnbGUuY29tL3N0ZWZhbm9sZW9uZTk5Mi9pbWRiLWV4dGVuc2l2ZS1kYXRhc2V0KS4gVGhleSBhcmUgb3JnaW5hbGx5IHNjcmFwZWQgZnJvbSBwdWJsaWNseSBhdmFpbGFibGUgd2Vic2l0ZSAtIEludGVybmF0aW9uYWwgTW92aWUgRGF0YWJhc2UgYS5rLmEuIElNRGIgKGh0dHBzOi8vd3d3LmltZGIuY29tKSBhbmQgdXBsb2FkZWQgYnkgS2FnZ2xlIHVzZXIuIApgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW92aWUgPC0gcmVhZF9jc3YoIklNRGIgbW92aWVzLmNzdiIpCnBlb3BsZSA8LSByZWFkX2NzdigiSU1EYiBuYW1lcy5jc3YiKQpyYXRlIDwtIHJlYWRfY3N2KCJJTURiIHJhdGluZ3MuY3N2IikKcHJpbmNpcGFsIDwtIHJlYWRfY3N2KCJJTURiIHRpdGxlX3ByaW5jaXBhbHMuY3N2IikKYGBgClRoZXNlIGZvdXIgZGF0YSBzZXRzIGFyZSBpbiB0dXJuIGlucHV0dGVkIGludG8gUiB1c2luZyB0aGUgInJlYWRfY3N2IiBmdW5jdGlvbiBmcm9tICJyZWFkciIgcGFja2FnZSBhcyBkYXRhIGZyYW1lcywgZWFjaCBvZiB0aGVtIGhhcyBiZWVuIGFzc2lnbmVkIGEgbmV3IG5hbWUgYXMgd2VsbC4gCgpBbW9uZyB0aGVtLCB0aGUgIm1vdmllIiBkYXRhIGZyYW1lIGluY2x1ZGVzIDgxLDI3MyBtb3ZpZXMgd2l0aCAyMiB2YXJpYWJsZXMgc3VjaCBhcyBtb3ZpZSBkZXNjcmlwdGlvbiwgYXZlcmFnZSByYXRpbmcsIG51bWJlciBvZiB2b3RlcywgZ2VucmUsIGV0Yy4gCgpUaGUgInBlb3BsZSIgZGF0YSBmcmFtZSBpbmNsdWRlcyAxNzUsNzE5IGNhc3QgbWVtYmVycyB3aXRoIDIwIHZhcmlhYmxlcyBzdWNoIGFzIGJpcnRoIGRldGFpbHMsIGRlYXRoIGRldGFpbHMsIGhlaWdodCwgc3BvdXNlcywgY2hpbGRyZW4sIGV0Yy4gCgpUaGUgInJhdGUiIGRhdGEgZnJhbWUgaW5jbHVkZXMgODEsMjczIHJhdGluZyBkZXRhaWxzIGZyb20gZGVtb2dyYXBoaWMgcGVyc3BlY3RpdmUgd2l0aCA0OSB2YXJpYWJsZXMuCgpMYXN0bHksIHRoZSAicHJpbmNpcGFsIiBkYXRhIGZyYW1lIGluY2x1ZGVzIDM3Nyw4NDggY2FzdCBtZW1iZXJzJyByb2xlcyBpbiBtb3ZpZXMgd2l0aCA2IHZhcmlhYmxlcyBzdWNoIGFzIElNRGIgdGl0bGUgaWQsIElNRGIgbmFtZSBpZCwgb3JkZXIgb2YgaW1wb3J0YW5jZSBpbiB0aGUgbW92aWUsIHJvbGUsIGFuZCBjaGFyYWN0ZXJzIHBsYXllZC4KClNpbmNlIHRoZXNlIGZvdXIgZGF0YSBmcmFtZXMgY29udGFpbiBuZWFybHkgMTAwIHZhcmlhYmxlcyAoY29sb3VtbnMpIGluIHRvdGFsLCB0aGV5IHdpbGwgYmUgaW4gdHVybiBzdWJzZXR0ZWQgaW50byBzbWFsbGVyIGRhdGEgZnJhbWVzIHNvIHRoYXQgb25seSB0aG9zZSB2YXJpYWJsZXMgKGNvbHVtbnMpIHVzZWZ1bCBmb3IgdGhpcyBhc3NpZ25tZW50IHdpbGwgYmUga2VwdC4KYGBge3J9Cm1vdmllIDwtIG1vdmllICU+JSBzZWxlY3QoaW1kYl90aXRsZV9pZCwgdGl0bGUsIGRhdGVfcHVibGlzaGVkOmR1cmF0aW9uLCBwcm9kdWN0aW9uX2NvbXBhbnksIGJ1ZGdldCwgd29ybHdpZGVfZ3Jvc3NfaW5jb21lKQpoZWFkKG1vdmllKQpgYGAKVGhlICJzZWxlY3QiIGZ1bmN0aW9uIGlzIGFwcGxpZWQgdG8gc3Vic2V0ICJtb3ZpZSIgZGF0YSBmcmFtZSwgbGVhdmluZyA4IHZhcmlhYmxlcyBhcyBzaG93biBhYm92ZS4gVGhlaXIgZGVzY3JpcHRpb25zIGFyZSBhcyBmb2xsb3dzOgoKKmltZGJfdGl0bGVfaWQ6IGlkZW50aWZpZXIgb2YgZWFjaCBtb3ZpZSB0aXRsZSBhc3NpZ25lZCBieSBJTURiIHdlYnNpdGUuCgoqdGl0bGU6IHRpdGxlIG9mIGVhY2ggbW92aWUuCgoqZGF0ZV9wdWJsaXNoZWQ6IGRhdGUgb2YgcmVsZWFzZSBvZiBlYWNoIG1vdmllLgoKKmdlbnJlOiBnZW5yZXMgb2YgZWFjaCBtb3ZpZSwgYSBtb3ZpZSBjYW4gaGF2ZSBhdCBtb3N0IHRocmVlIGdlbnJlcyBhY2NvcmRpbmcgdG8gSU1EYidzIHJ1bGUuCgoqZHVyYXRpb246IGxlbmd0aCBvZiBlYWNoIG1vdmllLCByZWNvcmRlZCBpbiBtaW51dGVzLgoKKnByb2R1Y3Rpb25fY29tcGFueTogcHJvZHVjdGlvbiBjb21wYW55IChvciBzdHVkaW8pIG9mIGVhY2ggbW92aWUuCgoqYnVkZ2V0OiBidWRnZXQgb2YgZWFjaCBtb3ZpZSwgbW9zdGx5IHJlY29yZGVkIGluIEFtZXJpY2FuIERvbGxhcnMgKFVTRCkuIFNvbWUgYXJlIHJlY29yZGVkIGluIG90aGVyIGN1cnJlbmNpZXMgd2hpY2ggYXJlIG5vIGxvbmdlciBhdmFpbGFibGUgbm93YWRheXMsIGVzcGVjaWFsbHkgdGhvc2UgbW92aWNlcyBwcm9kdWNlZCBpbiBsYXRlIDE5dGgvZWFybHkgMjB0aCBjZW50dXJpZXMuCgoqd29ybHdpZGVfZ3Jvc3NfaW5jb21lOiBXb3JsZC13aWRlIGdyb3NzIGluY29tZSBvZiBlYWNoIG1vdmllLCBtb3N0bHkgcmVjb3JkZWQgaW4gQW1lcmljYW4gRG9sbGFycyAoVVNEKS4gU29tZSBhcmUgcmVjb3JkZWQgaW4gb3RoZXIgY3VycmVuY2llcyB3aGljaCBhcmUgbm8gbG9uZ2VyIGF2YWlsYWJsZSBub3dhZGF5cywgZXNwZWNpYWxseSB0aG9zZSBtb3ZpY2VzIHByb2R1Y2VkIGluIGxhdGUgMTl0aC9lYXJseSAyMHRoIGNlbnR1cmllcy4KCmBgYHtyfQpwZW9wbGUgPC0gcGVvcGxlICU+JSBzZWxlY3QoaW1kYl9uYW1lX2lkLCBuYW1lKQpoZWFkKHBlb3BsZSkKYGBgClRoZSAic2VsZWN0IiBmdW5jdGlvbiBpcyBhcHBsaWVkIHRvIHN1YnNldCAicGVvcGxlIiBkYXRhIGZyYW1lLCBsZWF2aW5nIDIgdmFyaWFibGVzIGFzIHNob3duIGFib3ZlLiBUaGVpciBkZXNjcmlwdGlvbnMgYXJlIGFzIGZvbGxvd3M6CgoqaW1kYl9uYW1lX2lkOiBpZGVudGlmaWVyIG9mIGVhY2ggY2FzdCBtZW1iZXIgYXNzaWduZWQgYnkgSU1EYiB3ZWJzaXRlLgoKKm5hbWU6IGZ1bGwgbmFtZSBvZiBlYWNoIGNhc3QgbWVtYmVyLgoKYGBge3J9CnJhdGUgPC0gcmF0ZSAlPiUgc2VsZWN0KGltZGJfdGl0bGVfaWQsIHRvdGFsX3ZvdGVzLCBtZWFuX3ZvdGUpCmhlYWQocmF0ZSkKYGBgClRoZSAic2VsZWN0IiBmdW5jdGlvbiBpcyBhcHBsaWVkIHRvIHN1YnNldCAicmF0ZSIgZGF0YSBmcmFtZSwgbGVhdmluZyAzIHZhcmlhYmxlcyBhcyBzaG93biBhYm92ZS4gVGhlaXIgZGVzY3JpcHRpb25zIGFyZSBhcyBmb2xsb3dzOgoKKmltZGJfdGl0bGVfaWQ6IGlkZW50aWZpZXIgb2YgZWFjaCBtb3ZpZSB0aXRsZSBhc3NpZ25lZCBieSBJTURiIHdlYnNpdGUuCgoqdG90YWxfdm90ZXM6IHRvdGFsIG51bWJlciBvZiB2b3RlcyBvZiBlYWNoIG1vdmllIHJlY2VpdmVkIGJ5IElNRGIuCgoqbWVhbl92b3RlOiBhdmVyYWdlIHJhdGluZyAob24gYSAwLTEwIHNjYWxlKSBkZXRlcm1pbmVkIGJ5IGFsbCB0aGUgdm90ZXMgZm9yIGVhY2ggbW92aWUuCgpgYGB7cn0KcHJpbmNpcGFsIDwtIHByaW5jaXBhbCAlPiUgc2VsZWN0KGltZGJfdGl0bGVfaWQ6Y2F0ZWdvcnkpCmhlYWQocHJpbmNpcGFsKQpgYGAKVGhlICJzZWxlY3QiIGZ1bmN0aW9uIGlzIGFwcGxpZWQgdG8gc3Vic2V0ICJwcmluY2lwYWwiIGRhdGEgZnJhbWUsIGxlYXZpbmcgNCB2YXJpYWJsZXMgYXMgc2hvd24gYWJvdmUuIFRoZWlyIGRlc2NyaXB0aW9ucyBhcmUgYXMgZm9sbG93czoKCippbWRiX3RpdGxlX2lkOiBpZGVudGlmaWVyIG9mIGVhY2ggbW92aWUgdGl0bGUgYXNzaWduZWQgYnkgSU1EYiB3ZWJzaXRlLgoKKm9yZGVyaW5nOiBPcmRlciBvZiBpbXBvcnRhbmNlIG9mIGVhY2ggY2FzdCBtZW1iZXIgaW4gdGhlIG1vdmllLiBOb3RpY2UgdGhhdCBhdCBtb3N0IDEwIG1vc3QgaW1wb3J0YW50IGNhc3QgbWVtYmVycyBvZiBlYWNoIG1vdmllIGFyZSByZWNvcmRlZCBpbiB0aGlzIGRhdGEgZnJhbWUuCgoqaW1kYl9uYW1lX2lkOiBpZGVudGlmaWVyIG9mIGVhY2ggY2FzdCBtZW1iZXIgYXNzaWduZWQgYnkgSU1EYiB3ZWJzaXRlLgoKKmNhdGVnb3J5OiBDYXRlZ29yeSBvZiBqb2IgKHJvbGUpIGRvbmUgYnkgdGhlIGNhc3QgbWVtYmVyLgoKVGhlIGRhdGEgZnJhbWVzICJtb3ZpZSIgYW5kICJyYXRlIiB3aWxsIGJlIGNvbWJpbmVkIGludG8gYSBuZXcgZGF0YSBmcmFtZSBjYWxsZWQgIm1vdmllX3JhdGUiOgpgYGB7cn0KbW92aWVfcmF0ZSA8LSBtb3ZpZSAlPiUgbGVmdF9qb2luKHJhdGUsIGJ5ID0gImltZGJfdGl0bGVfaWQiKQpoZWFkKG1vdmllX3JhdGUpCmBgYApUaGUgZnVuY3Rpb24gImxlZnRfam9pbiIgaXMgYXBwbGllZCB0byBjb25kdWN0IHN1Y2ggY29tYmluYXRpb24gc28gdGhhdCBhbGwgdGhlIG9yaWdpbmFsIGluZm9ybWF0aW9uIGluICJtb3ZpZSIgY2FuIGJlIHByZXNlcnZlZCwgd2hlcmVhcyBvbmx5IHJhdGluZ3MgdGhhdCBoYXZlIGEgbWF0Y2ggaW4gIm1vdmllIiB3aWxsIGJlIGFkZGVkIHRvIHRoZSByaWdodC4gU2luY2UgIm1vdmllIiBhbmQgInJhdGUiIGFyZSBjb25uZWN0ZWQgdmlhIHZhcmlhYmxlICJpbWRiX3RpdGxlX2lkIiwgaXQgaXMgY2hvc2VuIGFzIHRoZSBrZXkgdmFyaWFibGUgaW4gImJ5IiBhcmd1bWVudC4KClRoZSAicHJpbmNpcGFsIiBkYXRhIGZyYW1lIHNlZW1zIHRvIGJlIHVuZGVyc3RhbmRhYmxlIGZvciBjb21wdXRlcnMsIGJ1dCB0aGV5IHdpbGwgY2VydGFpbmx5IHJhaXNlIGNvbmZ1c2lvbiBhbW9uZyBodW1hbiBiZWluZ3MuIFRvIG1ha2UgaXQgbW9yZSBpbmZvcm1hdGl2ZSBmb3IgcGVvcGxlLCBtb3ZpZSB0aXRsZXMgYW5kIGNhc3QgbWVtYmVycycgZnVsbCBuYW1lcyBzaG91bGQgYmUgYWRkZWQgaW50byB0aGlzIGRhdGEgZnJhbWUuIEhlbmNlLCB0aGUgZGF0YSBmcmFtZXMgIm1vdmllIiwgInByaW5jaXBhbCIgYW5kICJwZW9wbGUiIHdpbGwgYmUgY29tYmluZWQgaW50byBhIG5ldyBkYXRhIGZyYW1lIGNhbGxlZCAibW92aWVfcHJpbmNpcGFsX3Blb3BsZSI6CmBgYHtyfQptb3ZpZV9wcmluY2lwYWxfcGVvcGxlIDwtIHByaW5jaXBhbCAlPiUgbGVmdF9qb2luKG1vdmllICU+JSBzZWxlY3QoaW1kYl90aXRsZV9pZCwgdGl0bGUpLCBieSA9ICJpbWRiX3RpdGxlX2lkIikgJT4lIAogIGxlZnRfam9pbihwZW9wbGUgJT4lIHNlbGVjdChpbWRiX25hbWVfaWQsIG5hbWUpLCBieSA9ICJpbWRiX25hbWVfaWQiKSAlPiUgc2VsZWN0KGltZGJfdGl0bGVfaWQsIHRpdGxlLCBvcmRlcmluZywgaW1kYl9uYW1lX2lkLCBuYW1lLCBjYXRlZ29yeSkKaGVhZChtb3ZpZV9wcmluY2lwYWxfcGVvcGxlKQpgYGAKTm90aWNlIHRoYXQgam9pbmluZyB0aHJlZSBkYXRhIGZyYW1lcyByZXF1aXJlcyB0d28gc3RlcHMgb2YgY29tYmluYXRpb24uIEFnYWluLCBmdW5jdGlvbiAibGVmdF9qb2luIiBpcyBhcHBsaWVkIHRocm91Z2hvdXQgdGhlc2UgdHdvIGNvbWJpbmF0aW9ucyBzbyB0aGF0IGFsbCB0aGUgb3JpZ2luYWwgaW5mb3JtYXRpb24gaW4gInByaW5jaXBhbCIgY2FuIGJlIHByZXNlcnZlZCwgd2hlcmVhcyBvbmx5IG1vdmllIHRpdGxlcyBhbmQgY2FzdCBtZW1iZXJzJyBuYW1lcyB0aGF0IGhhdmUgYSBtYXRjaCBpbiAicHJpbmNpcGFsIiB3aWxsIGJlIGFkZGVkIHRvIHRoZSByaWdodC4gU2luY2UgInByaW5jaXBhbCIgYW5kICJtb3ZpZSIgYXJlIGNvbm5lY3RlZCB2aWEgdmFyaWFibGUgImltZGJfdGl0bGVfaWQiLCAicHJpbmNpcGFsIiBhbmQgInBlb3BsZSIgYXJlIGNvbm5lY3RlZCB2aWEgdmFyaWFibGUgImltZGJfbmFtZV9pZCIsIHRoZXNlIHR3byB2YXJpYWJsZXMgYXJlIGNob3NlbiBhcyB0aGUga2V5cyBmb3IgdGhlICJieSIgYXJndW1lbnQgaW4gZWFjaCBzdGVwIG9mIGNvbWJpbmF0aW9ucy4gVG8gbWFrZSB0aGUgbmV3IGRhdGEgZnJhbWUgIm1vdmllX3ByaW5jaXBhbF9wZW9wbGUiIGVhc2llciB0byByZWFkLCBpdHMgY29sdW1ucyBhcmUgYXJyYW5nZWQgaW4gc3VjaCB3YXkgdGhhdCBtb3ZpZSB0aXRsZSBpcyBzdHJhaWdodCBhZnRlciAiaW1kYl90aXRsZV9pZCIsIGFuZCBjYXN0IG1lbWJlcnMnIG5hbWUgaXMgc3RyYWlnaHQgYWZ0ZXIgImltZGJfbmFtZV9pZCIuCgoibW92aWVfcmF0ZSIgYW5kICJtb3ZpZV9wcmluY2lwYWxfcGVvcGxlIiB3aWxsIGJlIGpvaW5lZCB0b2dldGhlciBsYXRlciBhZnRlciB0aWR5aW5nIHVwLgoKCiMjIFVuZGVyc3RhbmQgCmBgYHtyfQpzdHIobW92aWVfcmF0ZSkKYGBgCkJ5IGFwcGx5aW5nICJzdHIiIGZ1bmN0aW9uLCAxMCB2YXJpYWJsZXMgYXJlIGZvdW5kIGluIGRhdGEgZnJhbWUgIm1vdmllX3JhdGUiLiBEYXRhIHR5cGVzIG9mIGVhY2ggY29sdW1uIGFyZSBhdXRvbWF0aWNhbGx5ICJndWVzc2VkIiBieSB0aGlzIGZ1bmN0aW9uLCBhcyBzaG93biBhYm92ZS4gCgoqQ29sdW1uICJpbWRiX3RpdGxlX2lkIiBpcyBvZiBjb3JyZWN0IGRhdGEgdHlwZSwgYmVjYXVzZSBpZGVudGlmaWVyIG9mIGVhY2ggbW92aWUgaXMgYSBzdHJpbmcgb2YgY2hhcmFjdGVycyBhbmQgbnVtYmVycy4KCipDb2x1bW4gInRpdGxlIiBpcyBvZiBjb3JyZWN0IGRhdGEgdHlwZSwgYmVjYXVzZSBtb3ZpZSB0aXRsZXMgbWFpbmx5IGNvbnNpc3Qgb2YgY2hhcmFjdGVycywgc29tZXRpbWVzIG51bWJlcnMgYXJlIGludm9sdmVkIGFzIHdlbGwuCgoqQ29sdW1uICJkYXRlX3B1Ymxpc2hlZCIgc2hvdWxkIGJlIG9mIGRhdGUgdHlwZSBpbnN0ZWFkIG9mIGNoYXJhY3Rlci4gSG93ZXZlciwgZm9yIHNvbWUgbW92aWVzLCBvbmx5IHRoZSBudW1iZXIgb2YgeWVhciBpcyByZWNvcmRlZCBvbiBJTURiLCBzbyB0aGlzIGNvbHVtbiBjYW5ub3QgYmUgZGlyZWN0bHkgY29udmVydGVkIGludG8gZGF0ZSB0eXBlIGJ5ICJhcy5EYXRlIiBmdW5jdGlvbi4gU3VjaCBjb252ZXJzaW9uIHdpbGwgYmUgY29uZHVjdGVkIGxhdGVyIGluIHRoZSBwYXJ0ICJTY2FuIEkiIGFmdGVyIHRoZXNlIGlucHV0IGluY29uc2lzdGVuY2llcyBhcmUgc29sdmVkLiAKCipDb2x1bW4gImdlbnJlIiBpcyBvZiBjb3JyZWN0IGRhdGEgdHlwZSB1bmRlciBjdXJyZW50IGNvbmRpdGlvbiwgd2hlcmUgZWFjaCBtb3ZpZSBjYW4gaGF2ZSBtb3JlIHRoYW4gb25lIGdlbnJlLiBIb3dldmVyLCBpZiBpbiBzb21lIG90aGVyIGNhc2VzIHdoZXJlIG9uZSBtb3ZpZSBjYW4gb25seSBoYXZlIG9uZSBnZW5yZSwgdGhlbiB0aGlzIGNvbHVtbiBjYW4gYmUgY29uc2lkZXJlZCBhcyBmYWN0b3IgdHlwZSBhcyB3ZWxsLgoKKkNvbHVtbiAiZHVyYXRpb24iIGlzIG9mIGNvcnJlY3QgZGF0YSB0eXBlLCBzaW5jZSBsZW5ndGhzIG9mIG1vdmllcyBhcmUgZXhwcmVzc2VkIGFzIGludGVnZXJzLgoKKkNvbHVtbiAicHJvZHVjdGlvbl9jb21wYW55IiBpcyBvZiBjb3JyZWN0IGRhdGEgdHlwZSwgc2luY2UgbmFtZXMgb2YgdGhlIHByb2R1Y3Rpb24gY29tcGFuaWVzIChvciBzdHVkaW9zKSBjb25zaXN0IG9mIGNoYXJhY3RlcnMuCgoqQ29sdW1uICJidWRnZXQiIGlzIG9mIGNvcnJlY3QgZGF0YSB0eXBlLCBiZWNhdXNlIGJ1ZGdldCBvZiBlYWNoIG1vdmllIGlzIHJlY29yZGVkIGluIG51bWJlciB2YWx1ZXMgdG9nZXRoZXIgd2l0aCBjb3JyZXNwb25kaW5nIGN1cnJlbmNpZXMgbGFiZWxsZWQgaW4gZnJvbnQgb2YgdGhlIG51bWJlci4gCgoqQ29sdW1uICJ3b3Jsd2lkZV9ncm9zc19pbmNvbWUiIGlzIG9mIGNvcnJlY3QgZGF0YSB0eXBlLCBzaW5jZSBncm9zcyBpbmNvbWUgaXMgcmVjb3JkZWQgaW4gbnVtYmVyIHZhbHVlcyB0b2dldGhlciB3aXRoIGNvcnJlc3BvbmRpbmcgY3VycmVuY2llcyBsYWJlbGxlZCBpbiBmcm9udCBvZiB0aGUgbnVtYmVyLgoKKkNvbHVtbiAidG90YWxfdm90ZXMiIGlzIG9mIGNvcnJlY3QgZGF0YSB0eXBlLCBiZWNhdXNlIHRvdGFsIG51bWJlciBvZiB2b3RlcyBvZiBlYWNoIG1vdmllIGlzIGRlbm90ZWQgYnkgaW50ZWdlcnMuCgoqQ29sdW1uICJtZWFuX3ZvdGUiIGlzIG9mIGNvcnJlY3QgZGF0YSB0eXBlLCBzaW5jZSB0aGUgYXZlcmFnZSByYXRpbmcgb2YgZWFjaCBtb3ZpZSBpcyBleHByZXNzZWQgYXMgYSBmbG9hdGluZyBudW1iZXIgb3ZlciBhIHNjYWxlIG9mIDAtMTAgd2l0aCAyIHNpZ25pZmljYW50IG51bWJlcnMsIHdoZXJlIDAgaW5kaWNhdGVzIHRoZSBtb3ZpZSBpcyBsZWFzdCBmYXZvdXJlZCBhbmQgMTAgaW5kaWNhdGVzIHRoZSBtb3ZpZSBpcyBtb3N0IGZhdm91cmVkLgoKYGBge3J9CnN0cihtb3ZpZV9wcmluY2lwYWxfcGVvcGxlKQptb3ZpZV9wcmluY2lwYWxfcGVvcGxlJG9yZGVyaW5nIDwtIGZhY3Rvcihtb3ZpZV9wcmluY2lwYWxfcGVvcGxlJG9yZGVyaW5nLCBsZXZlbHMgPSBjKCIxIiwgIjIiLCAiMyIsICI0IiwgIjUiLCAiNiIsICI3IiwgIjgiLCAiOSIsICIxMCIpLCBvcmRlcmVkPVRSVUUpCm1vdmllX3ByaW5jaXBhbF9wZW9wbGUkY2F0ZWdvcnkgPC0gYXMuZmFjdG9yKG1vdmllX3ByaW5jaXBhbF9wZW9wbGUkY2F0ZWdvcnkpCmBgYApCeSBhcHBseWluZyAic3RyIiBmdW5jdGlvbiwgNiB2YXJpYWJsZXMgYXJlIGZvdW5kIGluIGRhdGEgZnJhbWUgIm1vdmllX3ByaW5jaXBhbF9wZW9wbGUiLiBEYXRhIHR5cGVzIG9mIGVhY2ggY29sdW1uIGFyZSBhdXRvbWF0aWNhbGx5ICJndWVzc2VkIiBieSB0aGlzIGZ1bmN0aW9uLCBhcyBzaG93biBhYm92ZS4KCipCb3RoICJpbWRiX3RpdGxlX2lkIiBhbmQgInRpdGxlIiBhcmUgb2YgY29ycmVjdCBkYXRhIHR5cGVzLCBhcyBkaXNjdXNzZWQgYmVmb3JlLgoKKkNvbHVtbiAib3JkZXJpbmciIHNob3VsZCBiZSBvZiBmYWN0b3IgdHlwZSwgYmVjYXVzZSBlYWNoIG1vdmllIGNhbiBoYXZlIGF0IG1vc3QgMTAgbW9zdCBpbXBvcnRhbnQgY2FzdCBtZW1iZXJzLCBzbyB2YWx1ZXMgdW5kZXIgdGhpcyBjb2x1bW4gY2FuIG9ubHkgYmUgb25lIG9mIHRoZSAxMCBpbnRlZ2VycyAoZnJvbSAxIHRvIDEwKS4gTW9yZW92ZXIsIHRoZXNlIDEwIGZhY3RvcnMgYXJlIG9yZGluYWwgYW5kIHRoZXkgc2hvdWxkIGJlIG9yZGVyZWQgYXMgMTwyPDM8NDw1PDY+Nzw4PDk8MTAsIHdoZXJlIDEgbWVhbnMgdGhlIG1vc3QgaW1wb3J0YW50IGNhc3QgbWVtYmVyIGFuZCAxMCBtZWFucyB0aGUgbGVhc3QgaW1wb3J0YW50IGNhc3QgbWVtYmVyLgoKKkNvbHVtbiAiaW1kYl9uYW1lX2lkIiBpcyBvZiBjb3JyZWN0IGRhdGEgdHlwZSwgc2luY2UgaWRlbnRpZmllciBvZiBlYWNoIGNhc3QgbWVtYmVyIGlzIGEgc3RyaW5nIG9mIGNoYXJhY3RlcnMgYW5kIG51bWJlcnMuCgoqQ29sdW1uICJuYW1lIiBpcyBvZiBjb3JyZWN0IGRhdGEgdHlwZSwgYmVjYXVzZSBlYWNoIGNhc3QgbWVtYmVyJ3MgZnVsbCBuYW1lIGNvbnNpc3RzIG9mIG9ubHkgY2hhcmFjdGVycy4KCipDb2x1bW4gImNhdGVnb3J5IiBzaG91bGQgYmUgb2YgZmFjdG9yIHR5cGUsIHNpbmNlIHRoZXJlIGFyZSBvbmx5IDEyIGRpc3RpbmN0IHZhbHVlcyB1bmRlciB0aGlzIGNvbHVtbjogImFjdG9yIiwgImFjdHJlc3MiLCAiYXJjaGl2ZV9mb290YWdlIiwgImFyY2hpdmVfc291bmQiLCAiY2luZW1hdG9ncmFwaGVyIiwgImNvbXBvc2VyIiwgImRpcmVjdG9yIiwgImVkaXRvciIsICJwcm9kdWNlciIsICJwcm9kdWN0aW9uX2Rlc2lnbmVyIiwgInNlbGYiIGFuZCAid3JpdGVyIi4gTm90aWNlIHRoYXQgdGhlcmUgaXMgbm8gb3JkZXIgYmV0d2VlbiB0aGVzZSAxMiBmYWN0b3JzLgpgYGB7cn0Kc3RyKG1vdmllX3ByaW5jaXBhbF9wZW9wbGUpCmBgYApOb3cgZXZlcnkgY29sdW1uIGluICJtb3ZpZV9wcmluY2lwYWxfcGVvcGxlIiBpcyBvZiBjb3JyZWN0IGRhdGEgdHlwZXMuCgoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIApBbHRob3VnaCB0aGUgZGF0YSBmcmFtZSAibW92aWVfcHJpbmNpcGFsX3Blb3BsZSIgc2VlbXMgdG8gYmUgdGlkeSBvbiBpdHMgb3duLCBpdCBpcyBjb25zaWRlcmVkIGFzIHVudGlkeSB3aGVuIHBlb3BsZSBuZWVkIHRvIGpvaW4gaXQgd2l0aCAibW92aWVfcmF0ZSIuIFN1cHBvc2UgdGhhdCB3ZSB3YW50IHRvIGZpZ3VyZSBvdXQgdGhlIDMgbW9zdCBpbXBvcnRhbnQgY2FzdCBtZW1iZXJzIG9mIGVhY2ggbW92aWUsIGNvbWJpbmluZyAibW92aWVfcmF0ZSIgYW5kICJtb3ZpZV9wcmluY2lwYWxfcGVvcGxlIiBkaXJlY3RseSB3aWxsIHJlc3VsdCBpbiBpbmZvcm1hdGlvbiBvZiB0aGUgc2FtZSBtb3ZpZSBiZWluZyByZXBlYXRlZCBzZXZlcmFsIHRpbWVzIGluIGRpZmZlcmVudCByb3dzLCBuYW1lbHkgaW5mb3JtYXRpb24gcmVkdW5kYW5jeS4gSGVuY2UsIGJlZm9yZSBqb2luaW5nIHRoZXNlIHR3byBkYXRhIGZyYW1lcywgIm1vdmllX3ByaW5jaXBhbF9wZW9wbGUiIHNob3VsZCBmaXJzdCBiZSAic3ByZWFkZWQiIGludG8gd2lkZSBmb3JtYXQ6CmBgYHtyfQptb3ZpZV9wcmluY2lwYWxfcGVvcGxlIDwtIG1vdmllX3ByaW5jaXBhbF9wZW9wbGUgJT4lIHVuaXRlKG5hbWVfY2F0ZWdvcnksICJuYW1lIiwgImNhdGVnb3J5Iiwgc2VwID0gIiAoIikKbW92aWVfcHJpbmNpcGFsX3Blb3BsZSRuYW1lX2NhdGVnb3J5IDwtIHBhc3RlMChtb3ZpZV9wcmluY2lwYWxfcGVvcGxlJG5hbWVfY2F0ZWdvcnksICIpIikKYGBgCkluIG9yZGVyIHRvIGVtcGxveSB0aGUgInNwcmVhZCIgZnVuY3Rpb24gY29ycmVjdGx5LCB0aGUgdHdvIGNvbHVtbnMsICJuYW1lIiBhbmQgImNhdGVnb3J5Iiwgc2hvdWxkIGJlIGNvbWJpbmVkIHRvZ2V0aGVyIHRvIHByZXZlbnQgYW55IGVycm9ycy4gVGhpcyBpcyBkb25lIGJ5IHRoZSBhc3Npc3RhbmNlIGZyb20gInVuaXRlIiBmdW5jdGlvbiwgYW5kIHRoZSBuZXcgY29sdW1uIGlzIG5hbWVkIGFzICJuYW1lX2NhdGVnb3J5Ii4gVGhlIHNlcGFyYXRvciBiZWluZyB1c2VkIGhlcmUgaXMgIiAoIiwgc28gdGhhdCBhZnRlciBwYXN0aW5nIGEgIikiIHRvIGVhY2ggcm93IG9mIHRoZSBuZXcgY29sdW1uICJuYW1lX2NhdGVnb3J5IiwgdGhlIHJvbGUgb2YgZWFjaCBjYXN0IG1lbWJlciB3aWxsIGJlIG5pY2VseSBkZW1vc3RyYXRlZCBpbnNpZGUgYSBicmFja2V0IG5leHQgdG8gdGhlaXIgZnVsbCBuYW1lcywgYXMgc2hvd24gYmVsb3cuCmBgYHtyfQpoZWFkKG1vdmllX3ByaW5jaXBhbF9wZW9wbGUpCmBgYApGcm9tIG5vdyBvbiwgdGhlICJzcHJlYWQiIGZ1bmN0aW9uIGNhbiBiZSBzbW9vdGhseSBvcGVyYXRlZCBvdmVyIHRoZSBkYXRhIGZyYW1lICJtb3ZpZV9wcmluY2lwYWxfcGVvcGxlIi4KYGBge3J9CmltcG9ydGFudF9jYXN0XzMgPC0gbW92aWVfcHJpbmNpcGFsX3Blb3BsZSAlPiUgZmlsdGVyKG9yZGVyaW5nIDw9IDMpICU+JSBzZWxlY3QoaW1kYl90aXRsZV9pZCwgb3JkZXJpbmcsIG5hbWVfY2F0ZWdvcnkpICU+JSBzcHJlYWQoa2V5ID0gb3JkZXJpbmcsIHZhbHVlID0gbmFtZV9jYXRlZ29yeSkKY29sbmFtZXMoaW1wb3J0YW50X2Nhc3RfMykgPC0gYygiaW1kYl90aXRsZV9pZCIsICJmaXJzdCIsICJzZWNvbmQiLCAidGhpcmQiKQpgYGAKU2luY2Ugb25seSB0aGUgMyBtb3N0IGltcG9ydGFudCBjYXN0IG1lbWJlcnMgYXJlIGdvaW5nIHRvIGJlIGFkZGVkIHRvICJtb3ZpZV9yYXRlIiBkYXRhIGZyYW1lLCAiZmlsdGVyIiBmdW5jdGlvbiBoYXMgYmVlbiBhcHBsaWVkIHRvIHNlbGVjdCB0aGUgcm93cyB3aXRoIG9yZGVyaW5nIGVxdWFscyB0byBlaXRoZXIgMSwgMiBvciAzIGZyb20gIm1vdmllX3ByaW5jaXBhbF9wZW9wbGUiLiBBbHNvLCBzaW5jZSB0aXRsZXMgb2YgZWFjaCBtb3ZpZSBhbHJlYWR5IGV4aXN0IGluIHRoZSAibW92aWVfcmF0ZSIgZGF0YSBmcmFtZSwgb25seSB0aGUgY29sdW1ucyAiaW1kYl90aXRsZV9pZCIsICJvcmRlcmluZyIgYW5kICJuYW1lX2NhdGVnb3J5IiBhcmUgY2hvc2VuIGJ5ICJzZWxlY3QiIGZ1bmN0aW9uLiBUbyAic3ByZWFkIiB0aGUgZGF0YSBmcmFtZSwgdGhlIGtleSBjb2x1bW4gc2hvdWxkIGJlICJvcmRlcmluZyIgYXMgaXQgY29udGFpbnMgdmFyaWFibGUgbmFtZXMsIGFuZCB0aGUgdmFsdWUgY29sdW1uIGlzICJuYW1lX2NhdGVnb3J5IiBhcyBpdCBjb250YWlucyB2YWx1ZXMgZm9ybWluZyBtdWx0aXBsZSB2YXJpYWJsZXMuIFRoZSBuZXcgZGF0YSBmcmFtZSBhZnRlciBzcHJlYWRpbmcgaXMgdGVtcG9yYXJpbHkgY2FsbGVkICJpbXBvcnRhbnRfY2FzdF8zIiwgYW5kIGl0cyBjb2x1bW5zIGFyZSByZW5hbWVkIGFzICJpbWRiX3RpdGxlX2lkIiwgImZpcnN0IiwgInNlY29uZCIgYW5kICJ0aGlyZCIuIFRoZSBmaXJzdCBmZXcgcm93cyBhcmUgc2hvd24gYmVsb3c6CmBgYHtyfQpoZWFkKGltcG9ydGFudF9jYXN0XzMpCmBgYApOb3cgdGhhdCBlYWNoIG1vdmllIG9ubHkgYXBwZWFycyBpbiBvbmUgcm93LCBpdCBjYW4gYmUgam9pbmVkIHRvICJtb3ZpZV9yYXRlIi4KYGBge3J9Cm1vdmllX3JhdGVfM3ByaW5jaXBhbHMgPC0gbW92aWVfcmF0ZSAlPiUgbGVmdF9qb2luKGltcG9ydGFudF9jYXN0XzMsIGJ5ID0gImltZGJfdGl0bGVfaWQiKQptb3ZpZV9yYXRlX3ByaW5jaXBhbHMgPC0gbW92aWVfcmF0ZV8zcHJpbmNpcGFscyAlPiUgdW5pdGUocHJpbmNpcGFsX2Nhc3RzLCAiZmlyc3QiLCAic2Vjb25kIiwgInRoaXJkIiwgc2VwID0gIiwgIiwgbmEucm0gPSBUUlVFKQpgYGAKIm1vdmllX3JhdGUiIGFuZCAiaW1wb3J0YW50X2Nhc3RfMyIgYXJlIGNvbWJpbmVkIHRvZ2V0aGVyIHRocm91Z2ggdGhlaXIgY29tbW9uIGNvbHVtbiwgImltZGJfdGl0bGVfaWQiLiBIZXJlIGZ1bmN0aW9uICJsZWZ0X2pvaW4iIGlzIGFwcGxpZWQgc28gdGhhdCBhbGwgdGhlIG9yaWdpbmFsIGluZm9ybWF0aW9uIGZyb20gIm1vdmllX3JhdGUiIGNhbiBiZSByZXNlcnZlZC4gVG8gbWFrZSB0aGUgd2hvbGUgZGF0YSBmcmFtZSBtb3JlIGNvbXBlbmRpb3VzLCB3ZSBjb21wcmVzcyB0aGUgdGhyZWUgY29sdW1ucyBjb250YWluaW5nIHRoZSAzIG1vc3QgaW1wb3J0YW50IGNhc3QgbWVtYmVycyBpbnRvIGEgc2luZ2xlIGNvbHVtbiBjYWxsZWQgInByaW5jaXBhbF9jYXN0cyIsIGJ5IHVzaW5nICJ1bml0ZSIgZnVuY3Rpb24gYW5kIHNraXBwaW5nIGFsbCB0aGUgTkEncyAoTkEncyBleGlzdCBzaW5jZSBzb21lIG1vdmllcyBoYXZlIGxlc3MgdGhlbiAzIG1vc3QgaW1wb3J0YW50IGNhc3QgbWVtYmVycykuIFRoZSBuZXcgZGF0YSBmcmFtZSBpcyBuYW1lZCBhcyAibW92aWVfcmF0ZV9wcmluY2lwYWxzIiwgYW5kIGl0cyBmaXJzdCBmZXcgcm93cyBhcmUgc2hvd24gYmVsb3c6CmBgYHtyfQpoZWFkKG1vdmllX3JhdGVfcHJpbmNpcGFscykKbW92aWVfcmF0ZV9wcmluY2lwYWxzJHByaW5jaXBhbF9jYXN0c1ttb3ZpZV9yYXRlX3ByaW5jaXBhbHMkcHJpbmNpcGFsX2Nhc3RzID09ICIiXSA8LSAiVW5rbm93biIKYGBgCkl0IGlzIG9ic2VydmVkIHRoYXQgc29tZSByb3dzIHVuZGVyIHRoZSBjb2x1bW4gInByaW5jaXBhbF9jYXN0cyIgZG9uJ3QgY29udGFpbiBhbnkgaW5mb3JtYXRpb24uIFRoaXMgaXMgYmVjYXVzZSBub3QgYWxsIG1vdmllcyBhcmUgcmVjb3JkZWQgaW4gdGhlIGRhdGEgZnJhbWUgImltcG9ydGFudF9jYXN0XzMiICh3aGljaCBvcmlnaW5hbGx5IGNhbWUgZnJvbSB0aGUgZGF0YSBmcmFtZSAicHJpbmNpcGFsIikgaW4gcmVnYXJkIHdpdGggaW5mb3JtYXRpb24gYWJvdXQgdGhlaXIgbW9zdCBpbXBvcnRhbnQgY2FzdCBtZW1iZXJzLiBUbyBtYWtlIHRoaXMgY29sdW1uIG1vcmUgc3RyYWlnaHRmb3J3YXJkIChhbmQgbGVzcyBjb25mdXNpbmcpLCByZXBsYWNlIGFsbCB0aGUgZW1wdHkgdmFsdWVzIGJ5ICJVbmtub3duIi4gVGhlIGZpbmFsIGZvcm0gb2YgZGF0YSBmcmFtZSAibW92aWVfcmF0ZV9wcmluY2lwYWxzIiBpcyBzaG93biBiZWxvdzoKYGBge3J9CmhlYWQobW92aWVfcmF0ZV9wcmluY2lwYWxzKQpgYGAKTm90aWNlIHRoYXQgYXQgdGhpcyBzdGFnZSwgYWxsIHRoZSBjb21iaW5hdGlvbnMgb2YgZGF0YSBmcmFtZXMgaGF2ZSBiZWVuIGNvbXBsZXRlZC4gIm1vdmllX3JhdGVfcHJpbmNpcGFscyIgd2lsbCBiZSB1c2VkIHRocm91Z2hvdXQgdGhlIHJlc3Qgb2YgdGhpcyBhc3NpZ25tZW50LgoKCiMjCVNjYW4gSSAKSW4gdGhpcyBwYXJ0LCB0aGUgcHJlc2VuY2Ugb2YgYW55IG1pc3NpbmcgdmFsdWUsIHNwZWNpYWwgdmFsdWUgYW5kIGlucHV0IGluY29uc2lzdGVuY3kgd2lsbCBiZSBpbnZlc3RpZ2F0ZWQgdGhyb3VnaG91dCBkYXRhIGZyYW1lICJtb3ZpZV9yYXRlX3ByaW5jaXBhbHMiLgpgYGB7cn0KY29sU3Vtcyhpcy5uYShtb3ZpZV9yYXRlX3ByaW5jaXBhbHMpKQpgYGAKQnkgY2hlY2tpbmcgbnVtYmVyIG9mIE5BJ3Mgb2YgZWFjaCBjb2x1bW4gaW4gIm1vdmllX3JhdGVfcHJpbmNpcGFscyIsIGl0IGlzIGZvdW5kIHRoYXQgb25seSAicHJvZHVjdGlvbl9jb21wYW55IiwgImJ1ZGdldCIgYW5kICJ3b3Jsd2lkZV9ncm9zc19pbmNvbWUiIGNvbnRhaW4gbWlzc2luZyB2YWx1ZXMuIFRoZXJlIGFyZSBzZXZlcmFsIHJlYXNvbnMgdGhhdCB0aGUgcm93cyBoYXZpbmcgbWlzc2luZyB2YWx1ZXMgc2hvdWxkIG5vdCBiZSBleGNsdWRlZCBmcm9tIHRoZSBkYXRhIGZyYW1lLiBGaXJzdCBvZiBhbGwsIGFsbCB0aHJlZSBjb2x1bW5zIG1lbnRpb25lZCBwcmV2aW91c2x5IHBvc3Nlc3MgY29uc2lkZXJhYmx5IGxhcmdlIHByb3BvdGlvbiBvZiBOQSdzICh3YXkgbGFyZ2VyIHRoYW4gNSUgLSB0aGUgcmVjb21tZW5kZWQgcHJvcG9ydGlvbiBmb3IgZXhjbHVkaW5nIG1pc3NpbmcgZGF0YSksIGVzcGVjaWFsbHkgZm9yICJidWRnZXQiIGFuZCAid29ybHdpZGVfZ3Jvc3NfaW5jb21lIiBpbiB3aGljaCB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGlzIG1vcmUgdGhhbiBoYWxmIG9mIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgKHJvd3MpLiBSZW1vdmluZyB0aGVzZSByb3dzIHdpbGwgY2VydGFpbmx5IHJlc3VsdCBpbiBsb3NpbmcgdG9vIG11Y2ggc2lnbmlmaWNhbnQgaW5mb3JtYXRpb24uIAoKT24gdGhlIG90aGVyIGhhbmQsIGl0IGlzIHJhdGhlciByYXRpb25hbCB0aGF0IHRoZXNlIGNvbHVtbnMgY29udGFpbmluZyBzdWNoIGxhcmdlIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcy4gSW4gdGhlIFUuUy4sIG1vdmllIHByb2R1Y3Rpb24gaGFzIGJlZW4gaW5kdXN0cmlhbGlzZWQgJiBzdGFuZGFyZGlzZWQgZm9yIG5lYXJseSBhIGNlbnR1cnksIHRodXMgbW9zdCBtb3ZpZXMgb3JpZ2luYXRlZCBpbiBBbWVyaWNhIGFyZSBwcm9kdWNlZCBieSBzcGVjaWZpYyBwcm9kdWN0aW9uIGNvbXBhbmllcy4gSG93ZXZlciwgaW4gc29tZSBvdGhlciBjb3VudHJpZXMsIG1vdmllIHByb2R1Y3Rpb24gaW5kdXN0cnkgaGFzIG9ubHkgYmVlbiBlc3RhYmxpc2hlZCBmb3IgYSB2ZXJ5IHNob3J0IHRpbWUsIGl0IG1pZ2h0IGJlIHJlbGF0aXZlbHkgZGlmZmljdWx0IGZvciB3ZWJzaXRlcyBzdWNoIGFzIElNRGIgdG8gdHJhY2UgYmFjayBvbiB0aGUgaW5mb3JtYXRpb24gYWJvdXQgdGhvc2UgcHJvZHVjdGlvbiBjb21wYW5pZXMuIFRoaXMgaXMgd2h5IHRoZSBjb2x1bW4gInByb2R1Y3Rpb25fY29tcGFueSIgcG9zc2Vzc2VzIHNvIG1hbnkgbWlzc2luZyB2YWx1ZXMuCgpGb3IgdGhlIG90aGVyIHR3byBjb2x1bW5zLCAiYnVkZ2V0IiAmICJ3b3Jsd2lkZV9ncm9zc19pbmNvbWUiLCB0aGUgcmVhc29uIGlzIHByZXR0eSBzaW1pbGFyIGFzIHdlbGwuIEltbWF0dXJlIG1vdmllIHByb2R1Y3Rpb24gaW5kdXN0cnkgbWFrZXMgaXQgdHJvdWJsZXNvbWUgZm9yIHBlb3BsZSB0byByZWNvcmQgdGhlc2UgdmFsdWVzLiBJbiBvdGhlciBzY2VuYXJpb3MsIHNvbWUgcHJvZHVjdGlvbiBjb21wYW5pZXMgbWlnaHQgYmUgdW53aWxsaW5nIHRvIHB1Ymxpc2ggdGhlaXIgYnVkZ2V0IGFuZCBncm9zcyBpbmNvbWUgb2YgY2VydGFpbiBtb3ZpZXMsIHNpbmNlIGEgbG90IG9mICJ0cmFkaW5nIHNlY3JldHMiIGFyZSBpbnZvbHZlZCBpbiBzdWNoIGluZm9ybWF0aW9uLgoKTmV2ZXJ0aGVsZXNzLCBtaXNzaW5nIHZhbHVlcyBpbiAicHJvZHVjdGlvbl9jb21wYW55IiBjYW4gYmUgaGFuZGxlZCBieSByZXBsYWNpbmcgdGhlIE5BJ3Mgd2l0aCAiVW5rbm93biIsIHNvIHRoYXQgcGVvcGxlIGNhbiB1bmRlcnN0YW5kIHRoYXQgdGhleSBhcmUgbm90IHJlY29yZGVkOgpgYGB7cn0KbW92aWVfcmF0ZV9wcmluY2lwYWxzJHByb2R1Y3Rpb25fY29tcGFueVtpcy5uYShtb3ZpZV9yYXRlX3ByaW5jaXBhbHMkcHJvZHVjdGlvbl9jb21wYW55KV0gPC0gIlVua25vd24iCmBgYApGdW5jdGlvbiAiaXMubmEiIGlzIHVzZWQgdG8gbG9jYXRlIHRoZSBwb3NpdGlvbnMgb2YgTkEncy4KCkF0IHRoaXMgc3RhZ2UsIG1pc3NpbmcgdmFsdWVzIGluICJidWRnZXQiIGFuZCAid29ybHdpZGVfZ3Jvc3NfaW5jb21lIiBhcmUgc3RpbGwgbGVmdCB0aGVyZSwgdGhlc2UgdHdvIGNvbHVtbnMgaGF2ZSB0b28gbWFueSBOQSdzIHNvIHN1YnN0aXR1dGluZyBjb2x1bW4gbWVhbi9tZWRpYW4gd2lsbCBiZSB1bm1lYW5pbmdmdWwuIE1vcmVvdmVyLCB0aGVyZSBhbHNvIGV4aXN0IHNvbWUgaW5wdXQgaW5jb25zaXN0YW5jaWVzIGFtb25nIHRoZW0gd2hpY2ggd2lsbCBiZSBkaXNjdXNzZWQgbGF0ZXIuCgpgYGB7cn0Kc2FwcGx5KG1vdmllX3JhdGVfcHJpbmNpcGFscywgZnVuY3Rpb24oeCkgc3VtKGlzLmluZmluaXRlKHgpKSkKc2FwcGx5KG1vdmllX3JhdGVfcHJpbmNpcGFscywgZnVuY3Rpb24oeCkgc3VtKGlzLm5hbih4KSkpCmBgYApCeSBhcHBseWluZyAiaXMuaW5maW5pdGUiIGFuZCAiaXMubmFuIiBmdW5jdGlvbnMsIG5vIHNwZWNpYWwgdmFsdWUgaXMgZm91bmQgdGhyb3VnaG91dCBkYXRhIGZyYW1lICJtb3ZpZV9yYXRlX3ByaW5jaXBhbHMiLgoKSG93ZXZlciwgdGhlcmUgc3RpbGwgZXhpc3Qgc29tZSBpbnB1dCBpbmNvbnNpc3RhbmNpZXMgaW4gc2V2ZXJhbCBjb2x1bW5zLiBJbiAiZGF0ZV9wdWJsaXNoZWQiLCBtb3N0IHZhbHVlcyBhcmUgcmVjb3JkZWQgaW4gdGggZm9ybSAiWVlZWS1NTS1ERCIsIHdoZXJlYXMgc29tZSBvdGhlciByb3dzIG9ubHkgY29udGFpbiB0aGUgdmFsdWUgb2YgeWVhci4gVGhpcyBzaXR1YXRpb24gaXMgc29sdmVkIGFzIHNob3duIGJlbG93OgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRX0KbW92aWVfcmF0ZV9wcmluY2lwYWxzIDwtIG1vdmllX3JhdGVfcHJpbmNpcGFscyAlPiUgc2VwYXJhdGUoZGF0ZV9wdWJsaXNoZWQsIGludG8gPSBjKCJ5ZWFyX3B1Ymxpc2hlZCIsICJtb250aF9wdWJsaXNoZWQiLCAiZGF5X3B1Ymxpc2hlZCIpLCBzZXAgPSAiLSIpCm1vdmllX3JhdGVfcHJpbmNpcGFscyRtb250aF9wdWJsaXNoZWRbaXMubmEobW92aWVfcmF0ZV9wcmluY2lwYWxzJG1vbnRoX3B1Ymxpc2hlZCldIDwtIDAxCm1vdmllX3JhdGVfcHJpbmNpcGFscyRkYXlfcHVibGlzaGVkW2lzLm5hKG1vdmllX3JhdGVfcHJpbmNpcGFscyRkYXlfcHVibGlzaGVkKV0gPC0gMDEKbW92aWVfcmF0ZV9wcmluY2lwYWxzIDwtIG1vdmllX3JhdGVfcHJpbmNpcGFscyAlPiUgdW5pdGUoZGF0ZV9wdWJsaXNoZWQsICJ5ZWFyX3B1Ymxpc2hlZCIsICJtb250aF9wdWJsaXNoZWQiLCAiZGF5X3B1Ymxpc2hlZCIsIHNlcCA9ICItIikKbW92aWVfcmF0ZV9wcmluY2lwYWxzJGRhdGVfcHVibGlzaGVkIDwtIGFzLkRhdGUobW92aWVfcmF0ZV9wcmluY2lwYWxzJGRhdGVfcHVibGlzaGVkKQpgYGAKRm9yIHRoZSBmaXJzdCBzdGVwLCAiZGF0ZV9wdWJsaXNoZWQiIGlzIHRlbXBvcmFyaWx5IHNlcGVyYXRlZCBpbnRvIHRocmVlIGNvbHVtbnMsIGVhY2ggY29udGFpbnMgdGhlIHZhbHVlIG9mIHllYXIsIG1vbnRoIGFuZCBkYXkgcmVzcGVjdGl2ZWx5LiBCeSBkb2luZyBzbywgdGhvc2Ugcm93cyB0aGF0IG1pc3MgdGhlIHZhbHVlIG9mIG1vbnRoIGFuZCBkYXkgY2FuIGJlIGxvY2F0ZWQgYnkgImlzLm5hIiBmdW5jdGlvbi4gVG8gbWFrZSBpdCBlYXNpZXIgdG8gY29udmVydCBjb2x1bW4gImRhdGVfcHVibGlzaGVkIiBpbnRvIGRhdGUgZGF0YSB0eXBlLCBhIG5vdC1zby1qdXN0aWZpYWJsZSBhc3N1bXB0aW9uIGhhcyBiZWVuIG1hZGUgb24gdGhvc2UgbW92aWVzIChyb3dzKSBvbmx5IGNvbnRhdGluaW5nIHllYXIgdmFsdWUsIHRoYXQgaXMgdGhleSBhcmUgYWxsIHJlbGVhc2VkIG9uIHRoZSAxc3Qgb2YgSmFudWFyeS4gSSBjaG9zZSB0byBkbyBzbyBiZWNhdXNlIGluIHRoZSBzdWNjZWVkaW5nIHBhcnRzIG9mIHRoaXMgYXNzaWdubWVudCwgb25seSB0aGUgdmFsdWUgb2YgeWVhciB3aWxsIGJlIHV0aWxpc2VkIGZvciBhbmFseXNpcywgYW5kIGl0IHdpbGwgYmUgZWFzaWVyIHRvIGV4dHJhY3QgeWVhciB2YWx1ZSBmcm9tIGEgZGF0ZS10eXBlIGNvbHVtbiBieSB1c2luZyAibHVicmlkYXRlIiBwYWNrYWdlLiBIb3dldmVyLCBpbiBvdGhlciBzY2VuYXJpb3Mgd2hlcmUgdmFsdWVzIG9mIG1vbnRoIGFuZCBkYXkgYXJlIGFsc28gcmVxdWlyZWQgZm9yIGFuYWx5c2lzLCBzdWNoIGFzc3VtcHRpb24gc2hvdWxkIG5ldmVyIGJlIG1hZGUuCgpUbyBpbXBsZW1lbW50IHRoZSBhYm92ZSBhc3N1bXB0aW9uLCByZXBsYWNlIE5BJ3MgaW4gbW9udGggYW5kIGRheSBjb2x1bW5zIGJ5ICIwMSIsIHRoZW4gY29tYmluZSB0aGUgdGVtcG9yYXJ5IGNvbHVtbnMgInllYXJfcHVibGlzaGVkIiwgIm1vbnRoX3B1Ymxpc2hlZCIgYW5kICJkYXlfcHVibGlzaGVkIiBiYWNrIGludG8gImRhdGVfcHVibGlzaGVkIiBieSB1c2luZyAidW5pdGUiIGZ1bmN0aW9uLiBBZnRlciB0aGlzLCAiZGF0ZV9wdWJsaXNoZWQiIGNhbiBiZSBjb252ZXJ0ZWQgdG8gZGF0ZSB0eXBlIHRocm91Z2ggImFzLkRhdGUiIGZ1bmN0aW9uLiBOb3cgdGhhdCBldmVyeSB2YWlyYWJsZSBpbiBkYXRhIGZyYW1lICJtb3ZpZV9yYXRlX3ByaW5jaXBhbHMiIGlzIG9mIGNvcnJlY3QgZGF0YSB0eXBlcywgYXMgc2hvd24gYmVsb3c6CmBgYHtyfQpzdHIobW92aWVfcmF0ZV9wcmluY2lwYWxzKQpgYGAKCkluY29uc2lzdGFuY2llcyBhbHNvIGV4aXN0IGluIHRoZSBjb2x1bW5zICJidWRnZXQiIGFuZCAid29ybHdpZGVfZ3Jvc3NfaW5jb21lIiwgd2hlcmUgdGhlIHN0cmluZ3MgY29uc2lzdCBvZiBjdXJyZW5jeSBsYWJlbHMgZm9sbG93ZWQgYnkgdGhlIGFjdHVhbCBudW1lcmljIHZhbHVlcy4gQW1vbmcgdGhlbSwgbW9zdCBjdXJyZW5jaWVzIGFyZSBkZW5vdGVkIGJ5IHRocmVlIGNhcGl0YWwgbGV0dGVycywgd2l0aCB0aGUgZXhjZXB0aW9uIHRoYXQgVS5TLiBEb2xsYXJzIGlzIGRlbm90ZWQgYnkgdGhlIHN5bWJvbCAiJCIuIFN1Y2ggaW5jb25zaXN0YW5jeSBpcyBoYW5kbGVkIGFzIGZvbGxvd3M6IApgYGB7cn0KbW92aWVfcmF0ZV9wcmluY2lwYWxzIDwtIG1vdmllX3JhdGVfcHJpbmNpcGFscyAlPiUgc2VwYXJhdGUoYnVkZ2V0LCBpbnRvID0gYygiYnVkZ2V0X3VuaXQiLCAiYnVkZ2V0X3ZhbHVlIiksIHNlcCA9ICIgIikKbW92aWVfcmF0ZV9wcmluY2lwYWxzIDwtIG1vdmllX3JhdGVfcHJpbmNpcGFscyAlPiUgc2VwYXJhdGUod29ybHdpZGVfZ3Jvc3NfaW5jb21lLCBpbnRvID0gYygiZ3Jvc3NfaW5jb21lX3VuaXQiLCAiZ3Jvc3NfaW5jb21lX3ZhbHVlIiksIHNlcCA9ICIgIikKbW92aWVfcmF0ZV9wcmluY2lwYWxzJGJ1ZGdldF91bml0W21vdmllX3JhdGVfcHJpbmNpcGFscyRidWRnZXRfdW5pdCA9PSAiJCJdIDwtICJVU0QiCm1vdmllX3JhdGVfcHJpbmNpcGFscyRncm9zc19pbmNvbWVfdW5pdFttb3ZpZV9yYXRlX3ByaW5jaXBhbHMkZ3Jvc3NfaW5jb21lX3VuaXQgPT0gIiQiXSA8LSAiVVNEIgpgYGAKTm90aWNlIHRoYXQgdGhlcmUgaXMgYWx3YXlzIGEgc3BhY2UgKCIgIikgYmV0d2VlbiBjdXJyZW5jeSBsYWJlbHMgYW5kIG51bWVyaWMgdmFsdWVzLiBCYXNlZCBvbiB0aGlzIHNwZWNpYWwgZm9ybWF0IGNyZWF0ZWQgYnkgSU1EYiwgd2UgY2FuIHNlcGFyYXRlIGN1cnJlbmN5IGxhYmVscyBhbmQgbnVtZXJpYyB2YWx1ZXMgaW50byB0d28gY29sdW1ucyBzaW1wbHkgYnkgdXNpbmcgInNlcGFyYXRlIiBmdW5jdGlvbi4gRHVyaW5nIHRoaXMgc3RlcCwgY3VycmVuY3kgY29sdW1ucyBhcmUgbmFtZWQgYXMgIiVfdW5pdCIsIGFuZCBudW1lcmljIGNvbHVtbnMgYXJlIG5hbWVkIGFzICIlX3ZhbHVlIi4gTm93IHRoYXQgYWxsIHRoZSAiJCIgc3ltYm9scyBpbiBjdXJyZW5jeSBjb2x1bW5zIGNhbiBiZSByZXBsYWNlZCBieSBhIG1vcmUgZm9ybWFsIGZvcm1hdCAiVVNEIiwganVzdCBsaWtlIGFsbCB0aGUgb3RoZXIgY3VycmVuY2llcy4gQW5vdGhlciBiZW5lZml0IG9mIHNlcGFyYXRpbmcgY3VycmVuY3kgbGFiZWxzIGFuZCBudW1lcmljIHZhbHVlcyBpcyB0aGF0IHdlIGNhbiBub3cgcGVyZm9ybSBtYXRoZW1hdGljYWwgY2FsY3VsYXRpb25zIG92ZXIgYnVkZ2V0IGFuZCBncm9zcyBpbmNvbWUgZm9yIGVhY2ggbW92aWUuIFRoZSBjdXJyZW50IGRhdGEgdHlwZXMgb2YgY29sdW1ucyBpbiB0aGUgZGF0YSBmcmFtZSAibW92aWVfcmF0ZV9wcmluY2lwYWxzIiBpcyBzaG93biBiZWxvdzoKYGBge3J9CnN0cihtb3ZpZV9yYXRlX3ByaW5jaXBhbHMpCmBgYAoKCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgCkluIHJlYWwgbGlmZSwgcGVvcGxlIHVzdWFsbHkgcmVmZXJlbmNlIHRvIHRoZSBib3ggb2ZmaWNlIG9mIGEgbW92aWUgdG8gZGV0ZXJtaW5lIGl0cyBjb21wcmVoZW5zaXZlIHBlcmZvcm1hbmNlLiBBbHRob3VnaCB0aGUgYWN0dWFsIHF1YWxpdHkgb2YgYSBtb3ZpZSBpcyBub3QgYWx3YXlzIHJlbGF0ZWQgdG8gaXRzIGJveCBvZmZpY2UsIGl0IHdvcnRocyB0aGUgZWZmb3J0IHRvIGludmVzdGlnYXRlIG9uIGhvdyBtdWNoIHByb2ZpdCBhIG1vdmllIGNhbiBwcm9kdWNlLiBJbiB0aGlzIHBhcnQsIHByb2ZpdCBhbmQgcHJvZml0IHJhdGUgb2YgZWFjaCBtb3ZpZSB3aWxsIGJlIGNhbGN1bGF0ZWQgYW5kIGFkZGVkIHRvIHRoZSByaWdodCBvZiBkYXRhIGZyYW1lICJtb3ZpZV9yYXRlX3ByaW5jaXBhbHMiLCBhcyBmb2xsb3dzOgpgYGB7cn0KbW92aWVfcmF0ZV9wcmluY2lwYWxzJGJ1ZGdldF92YWx1ZSA8LSBhcy5udW1lcmljKG1vdmllX3JhdGVfcHJpbmNpcGFscyRidWRnZXRfdmFsdWUpCm1vdmllX3JhdGVfcHJpbmNpcGFscyRncm9zc19pbmNvbWVfdmFsdWUgPC0gYXMubnVtZXJpYyhtb3ZpZV9yYXRlX3ByaW5jaXBhbHMkZ3Jvc3NfaW5jb21lX3ZhbHVlKQptb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0IDwtIG1vdmllX3JhdGVfcHJpbmNpcGFscyAlPiUgZmlsdGVyKGJ1ZGdldF91bml0ID09IGdyb3NzX2luY29tZV91bml0KSAlPiUgCiAgbXV0YXRlKHByb2ZpdCA9IGdyb3NzX2luY29tZV92YWx1ZSAtIGJ1ZGdldF92YWx1ZSwgcHJvZml0X3JhdGUgPSBwcm9maXQvYnVkZ2V0X3ZhbHVlKQpgYGAKQmVmb3JlIGFueSBjYWxjdWxhdGlvbiwgdGhlIHR3byBjb2x1bW5zICJidWRnZXRfdmFsdWUiIGFuZCAiZ3Jvc3NfaW5jb21lX3ZhbHVlIiBzaG91ZCBmaXJzdCBiZSBjb252ZXJ0ZWQgaW50byBudW1lcmljIHR5cGVzLiBCZXNpZGVzLCBpdCBpcyBvbmx5IG1lYW5pbmdmdWwgdG8gY2FsY3VsYXRlIHByb2ZpdCBhbmQgcHJvZml0IHJhdGUgd2hlbiBhIG1vdmllJ3MgYnVkZ2V0IGFuZCBncm9zcyBpbmNvbWUgYXJlIHJlY29yZGVkIGluIHRoZSBzYW1lIGN1cnJlbmN5LiBUaGUgImZpbHRlciIgZnVuY3Rpb24gaXMgYXBwbGllZCB0byBmaWx0ZXIgb3V0IHRob3NlIHJvd3MgdGhhdCBxdWFsaWZ5IHN1Y2ggY29uZGl0aW9uLiBQcm9maXQgaXMgY2FsY3VsYXRlZCBieSBidWRnZXQgc3VidHJhY3RlZCBmcm9tIGdyb3NzIGluY29tZSwgYW5kIHByb2ZpdCByYXRlIGVxdWFscyB0byBxdW90aWVudCBvZiBwcm9maXQgb3ZlciBidWRnZXQuIE5vdGljZSB0aGF0IGEgbmVnYXRpdmUgcHJvZml0L3JhdGUgaW5kaWNhdGVzIHRoZSBtb3ZpZSBoYXMgbG9zdCBtb25leSwgd2hpbGUgYSBwb3NpdGl2ZSBwcm9maXQvcmF0ZSBpbmRpY2F0ZXMgdGhlIG1vdmllIGhhcyBhY3R1YWxseSBtYWRlIHByb2ZpdC4gVGhlc2UgdHdvIG5ldyB2YXJpYWJsZXMgY2FuIGJlIGFkZGVkIHRvIHRoZSByaWdodCBvZiBkYXRhIGZyYW1lICJtb3ZpZV9yYXRlX3ByaW5jaXBhbHMiIGJ5IHVzaW5nICJtdXRhdGUiIGZ1bmN0aW9uLiBUaGUgbmV3IGRhdGEgZnJhbWUgYWZ0ZXIgbXV0YXRpb24gaXMgcmVuYW1lZCBhcyAibW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCIsIGFuZCBpdHMgZmlyc3QgZmV3IHJvd3MgYXJlIHNob3duIGJlbG93OiAKYGBge3J9CmhlYWQobW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCkKYGBgCgoKIyMJU2NhbiBJSQpJbiB0aGlzIHBhcnQsIGVhY2ggY29sdW1uIGZyb20gZGF0YSBmcmFtZSAibW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCIgd2hpY2ggY29udGFpbnMgbnVtZXJpYyB2YWx1ZXMgd2lsbCBiZSBjaGVja2VkIHdoZXRoZXIgb3V0bGllcnMgZXhpc3Qgb3Igbm90LCB3aXRoIHRoZSBhc3Npc3RhbmNlIG9mIEJveCBQbG90IChhLmsuYS4gVHVrZXnigJlzIG1ldGhvZCBvZiBvdXRsaWVyIGRldGVjdGlvbiksIEhpc3RvZ3JhbSBhbmQgdGhlaXIgY29ycmVzcG9uZGluZyB6LXNjb3JlcyAob25seSB3aGVuIHRoZSB2YWx1ZXMgYXJlIGFwcHJveGltYXRlbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIHdoaWNoIGlzIGltcGxpZWQgYnkgYSBuZWFybHkgc3ltbWV0cmljIGhpc3RvZ3JhbSkuCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwYXIobWZyb3c9YygxLDIpKQptb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JGR1cmF0aW9uICU+JSBib3hwbG90KG1haW49IkJveCBQbG90IG9mIE1vdmllcycgRHVyYXRpb24iLCB5bGFiID0gIkR1cmF0aW9uIChpbiBtaW51dGVzKSIpCmhpc3QobW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCRkdXJhdGlvbiwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTW92aWVzJyBEdXJhdGlvbiIpCmBgYApBY2NvcmRpbmcgdG8gQm94IFBsb3QsIHRoZXJlIGV4aXN0IGEgY29uc2lkZXJhYmxlIGFtb3VudCBvZiBvdXRsaWVycyB1bmRlciBjb2x1bW4gImR1cmF0aW9uIiwgYWxsIG9mIHRoZW0gYXJlIGxvY2F0ZWQgYWJvdmUgdGhlIG1heGltdW0gY2FsY3VsYXRlZCBieSBUdWtleSdzIE1ldGhvZC4gVGhpcyBpcyBiZWNhdXNlLCBhbHRob3VnaCBtb3N0IG1vdmllcyBhcmUgbWFpbnRhaW5lZCBiZXR3ZWVuIDkwLTEyMCBtaW51dGVzIGZvciB0aGUgZmF2b3VyIG9mIGF1ZGllbmNlJ3MgcGF0aWVuY2UsIHRoZXJlIGlzIG5vIHJ1bGUgdG8gbGltaXQgYSBtb3ZpZSdzIGxlbmd0aCBhbmQgc29tZSBkaXJlY3RvcnMgbWF5IGhhdmUgYSBsb3QgbW9yZSBjb250ZW50IHRvIGJlIHByZXNlbnRlZCBpbiB0aGVpciBtb3ZpZXMgdGhhbiBvdGhlcnMsIHdoaWNoIGlzIHByZXR0eSByZWFzb25hYmxlLiBPbiB0aGUgb3RoZXIgaGFuZCwgYXMgdGhlIHNob3J0ZXN0IG1vdmllIGxhc3QgbW9yZSB0aGFuIDUwIG1pbnV0ZXMsIG5vIG91dGxpZXIgaXMgZm91bmQgdW5kZXIgdGhlIG1pbmltdW0uIFNpbmNlIHRoZSBoaXN0b2dyYW0gaXMgZHJhbWF0aWNhbGx5IHNrZXdlZCB0byB0aGUgcmlnaHQsIHotc2NvcmUgdGVzdCBpcyBub3QgYXBwcm9wcmlhdGUgaGVyZS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwYXIobWZyb3c9YygxLDIpKQptb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JGJ1ZGdldF92YWx1ZSAlPiUgYm94cGxvdChtYWluPSJCb3ggUGxvdCBvZiBNb3ZpZXMnIEJ1ZGdldCIsIHlsYWIgPSAiQnVkZ2V0IikKaGlzdChtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JGJ1ZGdldF92YWx1ZSwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgTW92aWVzJyBCdWRnZXQiKQpgYGAKQWNjb3JkaW5nIHRvIEJveCBQbG90LCB0aGVyZSBleGlzdCBhIGNvbnNpZGVyYWJsZSBhbW91bnQgb2Ygb3V0bGllcnMgdW5kZXIgY29sdW1uICJidWRnZXRfdmFsdWUiLCBhbGwgb2YgdGhlbSBhcmUgbG9jYXRlZCBhYm92ZSB0aGUgbWF4aW11bSBjYWxjdWxhdGVkIGJ5IFR1a2V5J3MgTWV0aG9kLiBUaGlzIGlzIGJlY2F1c2Ugbm8gcnVsZXMgYXJlIG1hZGUgdXBvbiBtYXhpbXVtIGFtb3VudCBvZiBidWRnZXQgYSBwcm9kdWN0aW9uIGNvbXBhbnkgY2FuIHB1dCBpbiB0aGVpciBtb3ZpZXMuIEluIGZhY3QsIGl0IGlzIHByZXR0eSBjb21tb24gdGhhdCBTY2ktRmkgbW92aWVzIGNvc3Qgd2F5IG1vcmUgYnVkZ2V0IHRoYW4gb3RoZXIgZ2VucmVzLiBCZXNpZGVzLCBkdWUgdG8gZWNvbm9taWMgaW5mbGF0aW9uLCBtb25leSB2YWx1ZSBub3dhZGF5cyBpcyB3YXkgZGlmZmVyZW50IHRoYW4gaW4gdGhlIHBhc3QsIHdoaWNoIHdpbGwgYWxzbyByZXN1bHQgaW4gYnVkZ2V0IHZhbHVlIGtlZXBpbmcgaW5jcmVhc2luZyBhcyB0aW1lIGdvZXMgYnkuIE9uIHRoZSBvdGhlciBoYW5kLCBhcyBidWRnZXQgbXVzdCBiZSBhIHBvc2l0aXZlIHZhbHVlLCBhIGRlZmF1bHQgbG93ZXIgYm91bmQgaXMgc2V0IGFib3ZlIHplcm8sIHRodXMgbm8gb3V0bGllciBpcyBmb3VuZCB1bmRlciB0aGUgbWluaW11bS4gU2luY2UgdGhlIGhpc3RvZ3JhbSBpcyBjb21wbGV0ZWx5IHVuc3ltbWV0cmljLCB6LXNjb3JlIHRlc3QgaXMgbm90IGFwcHJvcHJpYXRlIGhlcmUuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGFyKG1mcm93PWMoMSwyKSkKbW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCRncm9zc19pbmNvbWVfdmFsdWUgJT4lIGJveHBsb3QobWFpbj0iQm94IFBsb3Qgb2YgTW92aWVzJyBHcm9zcyBJbmNvbWUiLCB5bGFiID0gIkdyb3NzIEluY29tZSIpCmhpc3QobW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCRncm9zc19pbmNvbWVfdmFsdWUsIG1haW4gPSAiSGlzdG9ncmFtIG9mIE1vdmllcycgR3Jvc3MgSW5jb21lIikKYGBgCkFjY29yZGluZyB0byBCb3ggUGxvdCwgdGhlcmUgZXhpc3QgYSBjb25zaWRlcmFibGUgYW1vdW50IG9mIG91dGxpZXJzIHVuZGVyIGNvbHVtbiAiZ3Jvc3NfaW5jb21lX3ZhbHVlIiwgYWxsIG9mIHRoZW0gYXJlIGxvY2F0ZWQgYWJvdmUgdGhlIG1heGltdW0gY2FsY3VsYXRlZCBieSBUdWtleSdzIE1ldGhvZC4gQWdhaW4sIHRoaXMgaXMgYmVjYXVzZSBubyB1cHBlciBib3VuZCBpcyBwcmVkZWZpbmVkIG9uIGhvdyBtdWNoIGdyb3NzIGluY29tZSBhIG1vdmllIGNhbiBtYWtlLiBJZiBhIHBhcnRpY3VsYXIgbW92aWUgaXMgb3V0c3RhbmRpbmdseSBmYXZvdXJlZCBieSB0aGUgYXVkaWVuY2UsIHRoZW4gaXRzIGdyb3NzIGluY29tZSB3aWxsIHVwc3VycHJpc2luZ2x5IHJlYWNoIGEgbmV3IHBlYWsuIEJlc2lkZXMsIGR1ZSB0byBlY29ub21pYyBpbmZsYXRpb24sIG1vbmV5IHZhbHVlIG5vd2FkYXlzIGlzIHdheSBkaWZmZXJlbnQgdGhhbiBpbiB0aGUgcGFzdCwgd2hpY2ggd2lsbCBhbHNvIHJlc3VsdCBpbiBncm9zcyBpbmNvbWUgdmFsdWUga2VlcGluZyBpbmNyZWFzaW5nIGFzIHRpbWUgZ29lcyBieS4gT24gdGhlIG90aGVyIGhhbmQsIGFzIGdyb3NzIGluY29tZSBtdXN0IGJlIGEgcG9zaXRpdmUgdmFsdWUsIGEgZGVmYXVsdCBsb3dlciBib3VuZCBpcyBzZXQgYWJvdmUgemVybywgdGh1cyBubyBvdXRsaWVyIGlzIGZvdW5kIHVuZGVyIHRoZSBtaW5pbXVtLiBTaW5jZSB0aGUgaGlzdG9ncmFtIGlzIG5vd2hlcmUgY2xvc2UgdG8gYmUgc3ltbWV0cmljLCB6LXNjb3JlIHRlc3QgaXMgbm90IGFwcHJvcHJpYXRlIGhlcmUuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGFyKG1mcm93PWMoMSwyKSkKbW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCR0b3RhbF92b3RlcyAlPiUgYm94cGxvdChtYWluPSJCb3ggUGxvdCBvZiBNb3ZpZXMnIE51bWJlciBvZiBWb3RlcyIsIHlsYWIgPSAiTnVtYmVyIG9mIFZvdGVzIikKaGlzdChtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JHRvdGFsX3ZvdGVzLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBNb3ZpZXMnIE51bWJlciBvZiBWb3RlcyIpCmBgYApBY2NvcmRpbmcgdG8gQm94IFBsb3QsIHRoZXJlIGV4aXN0IGEgY29uc2lkZXJhYmxlIGFtb3VudCBvZiBvdXRsaWVycyB1bmRlciBjb2x1bW4gInRvdGFsX3ZvdGVzIiwgYWxsIG9mIHRoZW0gYXJlIGxvY2F0ZWQgYWJvdmUgdGhlIG1heGltdW0gY2FsY3VsYXRlZCBieSBUdWtleSdzIE1ldGhvZC4gVGhpcyBpcyBiZWNhdXNlIHRoZXJlIGlzIG5vIHVwcGVyIGJvdW5kIHRvIHByZXZlbnQgYXVkaWVuY2UgZnJvbSB2b3RpbmcuIElmIGEgcGFydGljdWxhciBtb3ZpZSBpcyBvdXRzdGFuZGluZ2x5IGZhdm91cmVkLCBvciBkZXRlc3RlZCwgYnkgdGhlIGF1ZGllbmNlLCB0aGVuIGl0IHdpbGwgYmUgdW5kb3VidGZ1bGx5IHZvdGVkIHdheSBtb3JlIHRpbWVzIHRoYW4gb3RoZXIgbW92aWVzLiBPbiB0aGUgb3RoZXIgaGFuZCwgYXMgbnVtYmVyIG9mIHZvdGVzIGNhbm5vdCBiZSBuZWdhdGl2ZSwgYSBkZWZhdWx0IGxvd2VyIGJvdW5kIGlzIHNldCBhdCB6ZXJvLCB0aHVzIG5vIG91dGxpZXIgaXMgZm91bmQgdW5kZXIgdGhlIG1pbmltdW0uIFNpbmNlIHRoZSBoaXN0b2dyYW0gaXMgZHJhbWF0aWNhbGx5IHNrZXdlZCB0byB0aGUgcmlnaHQsIHotc2NvcmUgdGVzdCBpcyBub3QgYXBwcm9wcmlhdGUgaGVyZS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwYXIobWZyb3c9YygxLDIpKQptb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JG1lYW5fdm90ZSAlPiUgYm94cGxvdChtYWluPSJCb3ggUGxvdCBvZiBNb3ZpZXMnIEF2ZXJhZ2UgUmF0aW5nIiwgeWxhYiA9ICJBdmVyYWdlIFJhdGluZyIpCmhpc3QobW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCRtZWFuX3ZvdGUsIG1haW4gPSAiSGlzdG9ncmFtIG9mIE1vdmllcycgQXZlcmFnZSBSYXRpbmciKQp6LnNjb3JlcyA8LSBtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JG1lYW5fdm90ZSAlPiUgIHNjb3Jlcyh0eXBlID0gInoiKQp6LnNjb3JlcyAlPiUgc3VtbWFyeSgpCndoaWNoKCBhYnMoei5zY29yZXMpID4zICkKbGVuZ3RoKHdoaWNoKCBhYnMoei5zY29yZXMpID4zICkpCmBgYApBY2NvcmRpbmcgdG8gQm94IFBsb3QsIHRoZXJlIGV4aXN0IGEgY29uc2lkZXJhYmxlIGFtb3VudCBvZiBvdXRsaWVycyB1bmRlciBjb2x1bW4gIm1lYW5fdm90ZSIsIG1vc3Qgb2YgdGhlbSBhcmUgbG9jYXRlZCB1bmRlciB0aGUgbWluaW11bSBjYWxjdWxhdGVkIGJ5IFR1a2V5J3MgTWV0aG9kLCB3aGlsZSBzb21lIGFyZSBhYm92ZSB0aGUgbWF4aW11bSBhcyB3ZWxsLiBUaGlzIHR1cm5zIG91dCB0byBiZSB2ZXJ5IGRpZmZlcmVudCBmcm9tIHByZXZpb3VzIGNhc2VzLiBUaGUgcmVhc29uIGJlaGluZCBpcyB0aGF0IHJhdGluZyBvZiBhIG1vdmllIGlzIHNldCBvdmVyIHRoZSBzY2FsZSBvZiAwLTEwLCBpLmUuIHRoZXJlIGV4aXN0IGEgcHJlZGlmaW5lZCBsb3dlciBib3VuZCBhdCAwIGFuZCB1cHBlciBib3VuZCBhdCAxMC4gV2hpbGUgbW9zdCBtb3ZpZXMgcmVjZWl2ZSBhdmVyYWdlIHJhdGluZ3MgYmV0d2VlbiA0IGFuZCA5LCBvdXRsaWVycyB1bmRlciB0aGUgbWluaW11bSBhcmUgYXBwYXJlbnRseSBtb3JlIHRoYW4gdGhvc2UgYWJvdmUgdGhlIG1heGl1bSwgYmVjYXVzZSBpdCBpcyByZWFzb25hYmx5IGhhcmRlciBmb3IgYSBtb3ZpZSB0byBnYWluIGF1ZGllbmNlJ3MgcHJhaXNlIHRoYW4gZGlzYXBwcm92YWwuIEl0IGlzIG9ic2VydmVkIHRoYXQgdGhlIGhpc3RvZ3JhbSBpcyBzbGlnaHRseSBza2V3ZWQgdG8gdGhlIGxlZnQsIGJ1dCBzdGlsbCBwcmV0dHkgY2xvc2UgdG8gYmUgc3ltbWV0cmljLiBUaHVzIHotc2NvcmUgdGVzdCBjYW4gYmUgcGVyZm9ybWVkIGluIHRoaXMgY2FzZS4gQmFzZWQgb24gdGhlIHJlc3VsdCwgNzUgb3V0bGllcnMgYXJlIGlkZW50aWZpZWQgYnkgei1zY29yZSB0ZXN0LgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBhcihtZnJvdz1jKDEsMikpCm1vdmllX3JhdGVfcHJpbmNpcGFsc19wcm9maXQkcHJvZml0ICU+JSBib3hwbG90KG1haW49IkJveCBQbG90IG9mIE1vdmllcycgUHJvZml0IiwgeWxhYiA9ICJQcm9maXQiKQpoaXN0KG1vdmllX3JhdGVfcHJpbmNpcGFsc19wcm9maXQkcHJvZml0LCBtYWluID0gIkhpc3RvZ3JhbSBvZiBNb3ZpZXMnIFByb2ZpdCIpCmBgYApBY2NvcmRpbmcgdG8gQm94IFBsb3QsIHRoZXJlIGV4aXN0IGEgY29uc2lkZXJhYmxlIGFtb3VudCBvZiBvdXRsaWVycyB1bmRlciBjb2x1bW4gInByb2ZpdCIsIG1vc3Qgb2YgdGhlbSBhcmUgbG9jYXRlZCBhYm92ZSB0aGUgbWF4aW11bSBjYWxjdWxhdGVkIGJ5IFR1a2V5J3MgTWV0aG9kLCB3aGlsZSBzb21lIGFyZSBiZWxvdyB0aGUgbWluaW11bSBhcyB3ZWxsLiBOb3RpY2UgdGhhdCB0aGVyZSBpcyBuZWl0aGVyIHByZWRlZmluZWQgdXBwZXIgYm91bmQgb24gaG93IG11Y2ggcHJvZml0IGEgbW92aWUgY2FuIG1ha2UsIG5vciBwcmVkZWZpbmVkIGxvd2VyIGJvdW5kIG9uIGhvdyBtdWNoIG1vbmV5IGEgbW92aWUgY2FuIGxvc2UuIE91dGxpZXJzIGFib3ZlIHRoZSBtYXhpbXVtIGFyZSBhcHBhcmVudGx5IG1vcmUgdGhhbiB0aG9zZSB1bmRlciB0aGUgbWluaW11bSwgYmVjYXVzZSBhcyBhIHdlbGwta25vd24gcmVjcmVhdGlvbmFsIHByb2R1Y3QsIGl0IGlzIHJlYXNvbmFibHkgY29tbW9uIHRoYXQgYSBtb3ZpZSBpcyBhYmxlIHRvIG1ha2UgY2VydGFpbiBhbW91bnQgb2YgcHJvZml0LCB1bmxlc3MgaXQgaXMgZXh0cmVtZWx5IHVucG9wdWxhciBvciB3aXRoIGV4dHJlbWx5IGxvdyBxdWFsaXR5LiBTaW5jZSB0aGUgaGlzdG9ncmFtIGlzIGNvbXBsZXRlbHkgdW5zeW1tZXRyaWMsIHotc2NvcmUgdGVzdCBpcyBub3QgYXBwcm9wcmlhdGUgaGVyZS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGVycm9yPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwYXIobWZyb3c9YygxLDIpKQptb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JHByb2ZpdF9yYXRlICU+JSBib3hwbG90KG1haW49IkJveCBQbG90IG9mIE1vdmllcycgUHJvZml0IFJhdGUiLCB5bGFiID0gIlByb2ZpdCBSYXRlIikKaGlzdChtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JHByb2ZpdF9yYXRlLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBNb3ZpZXMnIFByb2ZpdCBSYXRlIikKei5zY29yZXMyIDwtIG1vdmllX3JhdGVfcHJpbmNpcGFsc19wcm9maXQkcHJvZml0X3JhdGUgJT4lICBzY29yZXModHlwZSA9ICJ6IikKei5zY29yZXMyICU+JSBzdW1tYXJ5KCkKd2hpY2goIGFicyh6LnNjb3JlczIpID4zICkKbGVuZ3RoKHdoaWNoKCBhYnMoei5zY29yZXMyKSA+MyApKQpgYGAKQWNjb3JkaW5nIHRvIEJveCBQbG90LCB0aGVyZSBvbmx5IGV4aXN0IGEgc21hbGwgbnVtYmVyIG9mIG91dGxpZXJzIHVuZGVyIGNvbHVtbiAicHJvZml0X3JhdGUiLCBhIGZldyBvZiB0aGVtIGFyZSBsb2NhdGVkIGFib3ZlIHRoZSBtYXhpbXVtIGNhbGN1bGF0ZWQgYnkgVHVrZXkncyBNZXRob2QgYW5kIG90aGVycyBhcmUgYmVsb3cgdGhlIG1pbmltdW0uIFNpbWlsYXJseSB0byAicHJvZml0IiwgdGhlcmUgaXMgbmVpdGhlciBwcmVkZWZpbmVkIHVwcGVyIGJvdW5kIG5vciBwcmVkZWZpbmVkIGxvd2VyIGJvdW5kIG9uIHBvc3NpYmxlIHByb2ZpdCByYXRlLiBFeGNlcHQgdGhvc2UgdmFsdWVzIGluIHVwcGVyIHRhaWwsIHRoZSBoaXN0b2dyYW0gdHVybnMgb3V0IHRvIGJlIG5lYXJseSBzeW1tZXRyaWMsIHRodXMgdGhlIHotc2NvcmUgdGVzdCBjYW4gYmUgcGVyZm9ybWVkIGluIHRoaXMgY2FzZS4gQmFzZWQgb24gaXRzIHJlc3VsdCwgb25seSAyIG91dGxpZXJzIGFyZSBmb3VuZC4KCkluIHN1bW1hcnksIG91dGxpZXJzIGluIGNvbHVtbnMgImR1cmF0aW9uIiwgImJ1ZGdldF92YWx1ZSIsICAiZ3Jvc3NfaW5jb21lX3ZhbHVlIiwgInRvdGFsX3ZvdGVzIiwgIm1lYW5fdm90ZSIgYW5kICJwcm9maXQiIHNob3VsZCBub3QgYmUgZXhjbHVkZWQsIHNpbmNlIHRoZWlyIG51bWJlcnMgYXJlIGNvbnNpZGVyYWJseSBsYXJnZSBjb21wYXJlZCB0byB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucywgaS5lLiB0aGV5IGhhdmUgdGFrZW4gdXAgYSBsYXJnZSBwcm9wb3J0aW9uIG9mIHNhbXBsZXMuIFRoZXkgc2hvdWQgbmVpdGhlciBiZSBoYW5kbGVkIGJ5IGltcHV0YXRpb24gb3IgY2FwcGluZywgc2luY2Ugbm9uZSBvZiB0aGVzZSBvdXRsaWVycyBtZW50aW9uZWQgYWJvdmUgaXMgY2F1c2VkIGJ5IGRhdGEgZW50cnkvcHJvY2Vzc2luZyBlcnJvcnMuIEluc3RlYWQsIHRoZXNlIG91dGxpZXJzIGNhbiBiZSAiZWxpbWluYXRlZCIgYnkgdHJhbnNmb3JtYXRpb24gc28gdGhhdCB0aGVpciBvcmlnaW5hbCB2YWx1ZXMgYXJlICJtYWludGFpbmVkIiAod2hpY2ggbWVhbnMgcGVvcGxlIGNhbiBhY2hpZXZlIHRoZWlyIG9yaWdpbmFsIHZhbHVlcyBieSBzaW1wbHkgaW52ZXJzaW5nIHRoZSB0cmFuc2Zvcm1hdGlvbikgYW5kIGF0IHRoZSBzYW1lIHRpbWUsIGZ1cnRoZXIgc3RhdGlzdGljYWwgYW5hbHlzaXMgY2FuIGJlIGFwcGxpZWQgb3ZlciB0aGVzZSB2YXJpYWJsZXMuIFRoaXMgd2lsbCBiZSBkaXNjdXNzZWQgaW4gdGhlIG5leHQgcGFydCAiVHJhbnNmb3JtIi4KCkhvd2V2ZXIsIG91dGxpZXJzIGluIHRoZSBjb2x1bW4gInByb2ZpdF9yYXRlIiBtaWdodCBiZSBleGNsdWRlZCB1cG9uIGRlbWFuZCwgZXNwZWNpYWxseSB0aG9zZSAyIGZvdW5kIGJ5IHotc2NvcmUgdGVzdCBhcyB0aGV5IGFyZSByZWxhdGl2ZWx5IHNtYWxsIGluIG51bWJlcnMgY29tcGFyZWQgdG8gdGhlIHRvdGFsIG51bWJlciBvZiBvYmVyc2VydmF0aW9ucyAod2hpY2ggaXMgODkwOCkuIFN1Y2ggZWxpbWluYXRpb24gY2FuIGJlIGRvd24gYXMgZm9sbG93czoKYGBge3J9CnByb2ZpdF9yYXRlX2NsZWFuIDwtIG1vdmllX3JhdGVfcHJpbmNpcGFsc19wcm9maXQkcHJvZml0X3JhdGVbLXdoaWNoKGFicyh6LnNjb3JlczIpID4zKV0KYGBgCgpBc2lkZSBmcm9tIHVuaXZhcmlhdGUgb3V0bGllcnMsIGJpdmFyaWF0ZSBvdXRsaWVycyBtYXkgYWxzbyBleGlzdCBpbiB0aGUgZGF0YSBmcmFtZSAibW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCIuIFN1cHBvc2UgdGhhdCBwZW9wbGUgYXJlIGludGVyZXN0ZWQgaW4gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGJ1ZGdldCBhbmQgZ3Jvc3MgaW5jb21lIG9mIG1vdmllcyByZWxlYXNlZCBiZXR3ZWVuIDIwMDAgYW5kIDIwMTAsIHRoZW4gc2NhdHRlciBwbG90IGFuZCB0aGUgTWFoYWxhbm9iaXMgZGlzdGFuY2UgY2FuIGJlIHVzZWQgdG8gZGV0ZWN0IG91dGxpZXJzIGJhc2VkIG9uIGJ1ZGdldCBncm9zcyBpbmNvbWUsIGFzIHNob3duIGJlbG93OgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCBlcnJvcj1GQUxTRSwgd2FybmluZz1GQUxTRX0KcGFyKG1mcm93PWMoMSwyKSkKZ3Jvc3NfaW5jb21lX2J1ZGdldCA8LSBtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0ICU+JSBmaWx0ZXIoeWVhcihkYXRlX3B1Ymxpc2hlZCk8PTIwMTAsIHllYXIoZGF0ZV9wdWJsaXNoZWQpPj0yMDAwKSAlPiUgc2VsZWN0KGdyb3NzX2luY29tZV92YWx1ZSwgYnVkZ2V0X3ZhbHVlKQpncm9zc19pbmNvbWVfYnVkZ2V0ICU+JSBwbG90KGdyb3NzX2luY29tZV92YWx1ZSB+IGJ1ZGdldF92YWx1ZSwgZGF0YSA9IC4sIHlsYWIgPSAiR3Jvc3MgSW5jb21lIiwgeGxhYiA9ICJCdWRnZXQiLCBtYWluID0gIkdyb3NzIEluY29tZSBWUyBCdWRnZXQiKQpyZXN1bHRzIDwtIG12bihkYXRhID0gZ3Jvc3NfaW5jb21lX2J1ZGdldCwgbXVsdGl2YXJpYXRlT3V0bGllck1ldGhvZCA9ICJxdWFuIiwgc2hvd091dGxpZXJzID0gVFJVRSkKYGBgCkJlZm9yZSBwbG90dGluZywgbW92aWVzIHJlbGVhc2VkIGJldHdlZW4gMjAwMCBhbmQgMjAxMCBzaG91bGQgZmlyc3QgYmUgZmlsdGVyZWQgb3V0IGJ5IGFwcGx5aW5nICJmaWx0ZXIiIGZ1bmN0aW9uLiBOZXh0LCB0aGUgY29sdW1ucyAiZ3Jvc3NfaW5jb21lX3ZhbHVlIiBhbmQgICJidWRnZXRfdmFsdWUiIHdoaWNoIGFyZSB1c2VmdWwgZm9yIHBsb3R0aW5nIGNhbiBiZSBleHRyYWN0ZWQgYW5kIHNhdmVkIGludG8gYSBuZXcgZGF0YSBmcmFtZSBjYWxsZWQgImdyb3NzX2luY29tZV9idWRnZXQiLiBUbyBnZXQgdGhlIHNjYXR0ZXIgcGxvdCwgInBsb3QiIGZ1bmN0aW9uIGlzIGFwcGxpZWQgd2hlcmUgeS1heGlzIHJlcHJlc2VudHMgZ3Jvc3MgaW5jb21lIGFuZCB4LWF4aXMgcmVwcmVzZW50cyBidWRnZXQuIFRvIGFjcXVpcmUgTWFoYWxhbm9iaXMgZGlzdGFuY2UsICJtdm4iIGZ1bmN0aW9uIGZyb20gdGhlIE1WTiBwYWNrYWdlIGlzIGFwcGxpZWQgYW5kIHRoZSBtZXRob2QgYmVpbmcgdXNlZCBoZXJlIGlzIGNoaS1zcXVhcmUgZGlzdHJpYnV0aW9uIGNyaXRpY2FsIHZhbHVlLCBhcyBtZW50aW9uZWQgaW4gdGhlIGFyZ3VtZW50IGJ5ICJxdWFuIi4KCkluIHRoZSBzY2F0dGVyIHBsb3QsIGl0IGlzIG9ic2VydmVkIHRoYXQgZm9yIG1vc3QgbW92aWVzLCBzbWFsbCBncm9zcyBpbmNvbWUgaXMgYXNzb2NpYXRlZCB3aXRoIHNtYWxsIGJ1ZGdldCBhbmQgbGFyZ2UgZ3Jvc3MgaW5jb21lIGlzIGFzc29jaWF0ZWQgd2l0aCBsYXJnZSBidWRnZXQuIEhvd2V2ZXIsIHRoZXJlIGRvIGV4aXN0IHNvbWUgb3V0bGllcnMgbG9jYXRlZCBpbiBib3R0b20tcmlnaHQgYW5kIHRvcC1yaWdodCBjb3JuZXJzLiBJbiBNYWhhbGFub2JpcyBkaXN0YW5jZSwgaXQgaXMgZm91bmQgdGhhdCBvdXRsaWVycyBoYXZlIHRha2VuIHVwIG5lYXJseSBoYWxmIG9mIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zLiBUaHVzIHRoZXkgc2hvdWxkIG5vdCBiZSBleGNsdWRlZC4gQmVzaWRlcywgdGhleSBzaG91ZCBuZWl0aGVyIGJlIGhhbmRsZWQgYnkgaW1wdXRhdGlvbiBvciBjYXBwaW5nLCBzaW5jZSBub25lIG9mIHRoZW0gaXMgY2F1c2VkIGJ5IGRhdGEgZW50cnkvcHJvY2Vzc2luZyBlcnJvcnMuCgoKIyMJVHJhbnNmb3JtIApBcyBtZW50aW9uZWQgaW4gcHJldmlvdXMgcGFydCwgbW9zdCBvZiB0aGUgdmFyaWFibGVzIGluIGRhdGEgZnJhbWUgIm1vdmllX3JhdGVfcHJpbmNpcGFsc19wcm9maXQiIGhhdmUgdGhlaXIgZGlzdHJpYnV0aW9ucyBza2V3ZWQgdG8gdGhlIHJpZ2h0LCB3aGljaCBpcyBub3QgcHJlZmVycmVkIGJ5IHN0YXRpc3RpY2FsIGluZmVyZW5jZS4gSW4gb3JkZXIgdG8gYWNoaWV2ZSBhIHN5bW1ldHJpYyAobm9ybWFsKSBkaXN0cmlidXRpb24sIHdlIGNhbiBzZWVrIGFzc2lzdGFuY2UgZnJvbSB0cmFuc2Zvcm1hdGlvbi4gSW4gdGhlIGZvbGxvd2luZywgdGhlIGNvdWxtbiAiYnVkZ2V0X3ZhbHVlIiB3aWxsIGJlIHVzZWQgYXMgYW4gZXhtYW1wbGU6CmBgYHtyfQpwYXIobWZyb3c9YygxLDIpKQpoaXN0KGxvZyhtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JGJ1ZGdldF92YWx1ZSksIG1haW4gPSAiSGlzdG9ncmFtIG9mIGxvZyhidWRnZXQgdmFsdWUpIikKYmNfYnVkZ2V0IDwtIEJveENveChtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0JGJ1ZGdldF92YWx1ZSwgbGFtYmRhID0gImF1dG8iKQpoaXN0KGJjX2J1ZGdldCwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgQm94LUNveCBUcmFuc2Zvcm1lZCBCdWRnZXQgVmFsdWUiKQpgYGAKU2luY2UgYWxsIHZhbHVlcyB1bmRlciB0aGUgY29sdW1uICJidWRnZXRfdmFsdWUiIGFyZSBwb3NpdGl2ZSwgbG9nYXJpdGhtIHRyYW5zZm9ybWF0aW9uIGNhbiBiZSBhcHBsaWVkIHRvIHJlZHVjZSB0aGUgcmlnaHQgc2tld25lc3MuIFRoaXMgY2FuIGJlIHNpbXBseSBpbXBsZW1lbnRlZCBieSB1c2luZyAibG9nIiBmdW5jdGlvbi4gQW5vdGhlciBhcHByb2FjaCB0byBhY2hpZXZlIGEgc3ltbWV0cmljIChub3JtYWwpIGRpc3RyaWJ1dGlvbiBpcyB0aGUgQm94Q294IFRyYW5zZm9ybWF0aW9uLiBUaGlzIGlzIGRvbmUgYnkgdXNpbmcgIkJveENveCIgZnVuY3Rpb24gZnJvbSB0aGUgImZvcmVjYXN0IiBwYWNrYWdlLiBBcyBzaG93biBhYm92ZSwgYm90aCB0cmFuc2Zvcm1hdGlvbnMgaGF2ZSBiZWhhdmVkIHByZXR0eSB3ZWxsIGluIHR1cm5pbmcgdGhlIGRpc3RyaWJ1dGlvbiBpbnRvIGEgbmVhcmx5IG5vcm1hbCBvbmUuIE5vdGljZSB0aGF0IGxvZ2FyaXRobSB0cmFuc2Zvcm1hdGlvbiBjYW4gb25seSBiZSBhcHBsaWVkIHdoZW4gYWxsIHRoZSB2YWx1ZXMgYXJlIHBvc2l0aXZlLiBJZiB0aGVyZSBleGlzdCBhbnkgemVybyBvciBuZWdhdGl2ZSB2YWx1ZXMgKHN1Y2ggYXMgaW4gInByb2ZpdCIgYW5kICJwcm9maXRfcmF0ZSIpLCB0aGVuIHBlb3BsZSBzaG91bGQgdHVybiB0byByZWNpcHJvY2FsIChvciBCb3hDb3gpIHRyYW5zZm9ybWF0aW9uIHRvIHJlZHVjZSByaWdodCBza2V3bmVzcy4gCgpBY3Jvc3MgYWxsIHRoZSBudW1lcmljIHZhcmlhYmxlcyBpbiBkYXRhIGZyYW1lICJtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0IiwgaXQgaXMgYWxzbyBvYnNlcnZlZCB0aGF0IHRoZXkgYXJlIHNldCBvbiBjb21wbGV0ZWx5IGRpZmZlcmVudCByYW5nZXMgdG8gZWFjaCBvdGhlci4gU3VjaCBjb25kaXRpb24gbWlnaHQgaGF2ZSBzZXJpb3VzIGltcGFjdCBvbiBzdGF0aXN0aWNhbCBhbmx5c2lzIG9yIG1hY2hpbmUgbGVhcm5pbmcgcHJlZGljdGlvbiBhY2N1cmFjeS4gSW4gb3JkZXIgdG8gcmVkdWNlIHN1Y2ggaW1wYWN0LCBhbGwgdGhlc2UgbnVtZXJpYyB2YXJpYWJsZXMgc2hvdWxkIGJlIG5vcm1hbGlzZWQgaW50byBzaW1pbGFyIHNjYWxlcywgd2hpY2ggY2FuIGJlIGRvbmUgYnkgei1zY29yZSBzdGFuZGFyZGlzYXRpb24gYXMgZm9sbG93czoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgZXJyb3I9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnpfbW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCA8LSBtb3ZpZV9yYXRlX3ByaW5jaXBhbHNfcHJvZml0ICU+JSBzZWxlY3QoZHVyYXRpb24sIGJ1ZGdldF92YWx1ZSwgZ3Jvc3NfaW5jb21lX3ZhbHVlLCB0b3RhbF92b3RlcywgbWVhbl92b3RlLCBwcm9maXQsIHByb2ZpdF9yYXRlKSAlPiUgc2NhbGUoY2VudGVyID0gVFJVRSwgc2NhbGUgPSBUUlVFKQphdHRyaWJ1dGVzKHpfbW92aWVfcmF0ZV9wcmluY2lwYWxzX3Byb2ZpdCkKYGBgCkZpcnNseSwgYWxsIDcgbnVtZXJpYyB2YXJpYWJsZXMgc2hvdWxkIGJlIGV4dHJhY3RlZCBmcm9tIGRhdGEgZnJhbWUgIm1vdmllX3JhdGVfcHJpbmNpcGFsc19wcm9maXQiLiBUaGVuIHotc2NvcmUgc3RhbmRhcmRpc2F0aW9uIGNhbiBiZSBpbXBsZW1lbnRlZCBieSB0aGUgInNjYWxlIiBmdWNudGlvbiB3aXRoIGJvdGggYXJndW1lbnRzICJjZW50ZXIiIGFuZCAic2NhbGUiIHNldHRpbmcgYXMgVFJVRS4KPGJyPgo8YnI+Cg==