Required packages
library(dplyr)
library(readr)
library(tidyr)
library(editrules)
Executive Summary
The ‘Tab Separated Values (tsv)’ files and were imported into R. The “\N” were converted to NA’s and most of the variables were imported into their correct data types.
The two datasets were then joined together by the common variable type ‘tconst’. Unnecessary variables were then removed and the number of observations filtered out to make the size of the dataset more manageable. The ‘titleType’ variable was then converted to a factor and labelled. The ‘startYear’ variable was converted from a character to an integer data type. All the variables in the data were then deemed to be in the correct type.
The dataset was not in a tidy format as the ‘genre’ variable contained multiple values in each cell. The ‘genre’ and ‘tconst’ variables were then selected and a new dataset created which holds the genre types for each title in separate cells. Both datasets were now in a tidy format. A new variable was made called ‘rank’ to determine the top 10 movies in the dataset. The rank variable ranked each title by the number of votes it received multiplied by its average rating.
The data was then scanned for missing values, errors, and inconsistencies. Missing values in the ‘titleType’ variable were deleted as there was a very small percentage while the mean was imputed for the ‘runtimeMinutes’ variable by ‘titleType’. No missing values remained in the dataset. There were no special values or errors found.
The dataset was then scanned for outliers. Outliers were found in ‘numVotes’ and ‘runtimeMinutes’ by titleType however after research these entries were deemed to be valid.
A histogram of ‘averageRating’ showed the data to be left skewed. A square transformation was done which significantly reduced the skewedness and transformed the data into a normal distribution making statistical analysis much simpler.
Data
The datasets were downloaded as ‘Tab Separated Values(tsv)’ files from the International Movie Database (IMDB) website
https://datasets.imdbws.com/.
The IMDB website is a popular website to find out any relevant information regarding thousands of movies, tv shows, documentaries etc. Users can leave reviews of titles, determine the cast, writers, directors of specific titles and much more. It is a very useful site for determining your next movie to watch. The datasets are accessible to customers for personal and non-commercial use and are refreshed daily. The two datasets we chose to join together are ‘basics’ and ‘ratings’.
Basics contains the following information for titles:
- tconst (string): alphanumeric unique identifier of the title
- titleType (string): the type/format of the title (e.g. movie, short, tvseries, tvepisode, video, etc)
- primaryTitle (string): the more popular title / the title used by the filmmakers on promotional materials at the point of release
- originalTitle (string): original title, in the original language
- isAdult (boolean): 0 non-adult title; 1 adult title
- startYear (YYYY): represents the release year of a title. In the case of TV Series, it is the series start year
- endYear (YYYY): TV Series end year. ‘’ for all other title types
- runtimeMinutes: primary runtime of the title, in minutes
- genres (string array): includes up to three genres associated with the title
Ratings contains the IMDb rating and votes information for titles
- tconst (string): alphanumeric unique identifier of the title
- averageRating: weighted average of all the individual user ratings
- numVotes: number of votes the title has received
## Import data
titles <- read_delim("title.basics.tsv", "\t", escape_double = FALSE, na = "\\N", trim_ws = TRUE, quote='',
col_types = cols(
tconst = col_character(),
titleType = col_character(),
primaryTitle = col_character(),
originalTitle = col_character(),
isAdult = col_logical(),
startYear = col_character(),
endYear = col_integer(),
runtimeMinutes = col_integer(),
genres = col_character()))
ratings <- read_delim("title.ratings.tsv", "\t", escape_double = FALSE, na = "\\N", trim_ws = TRUE, quote='')
head(titles)
head(ratings)
## Join datasets
data <- titles %>% left_join(ratings, by = "tconst")
head(data)
- The datasets were imported into R using the ‘read_delim’ function.
- the “\N” were converted to NA’s and most of the variables were converted to their correct data types upon importing the data into R using the col_types argument.
- The Ratings dataset was joined to Basics by the ‘tconst’ variable using a left_join.
Understand
# drop unneeded columns
data <- data %>% select(-originalTitle, -endYear)
# filtered observations
data <- data %>% filter(averageRating >= 2 & numVotes >= 50)
str(data)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 295837 obs. of 9 variables:
$ tconst : chr "tt0000001" "tt0000002" "tt0000003" "tt0000004" ...
$ titleType : chr "short" "short" "short" "short" ...
$ primaryTitle : chr "Carmencita" "Le clown et ses chiens" "Pauvre Pierrot" "Un bon bock" ...
$ isAdult : logi FALSE FALSE FALSE FALSE FALSE FALSE ...
$ startYear : chr "1894" "1892" "1892" "1892" ...
$ runtimeMinutes: int 1 5 4 NA 1 1 1 1 45 1 ...
$ genres : chr "Documentary,Short" "Animation,Short" "Animation,Comedy,Romance" "Animation,Short" ...
$ averageRating : num 5.8 6.3 6.6 6.3 6.2 5.4 5.5 5.6 5.5 6.9 ...
$ numVotes : num 1507 183 1154 112 1854 ...
# convert titlesType to factor
data$titleType <- factor(data$titleType,
levels = c("short", "movie", "tvSeries", "tvShort", "tvMovie", "tvEpisode", "tvMiniSeries", "tvSpecial", "video", "videoGame"),
labels = c("Short", "Movie", "TV_Series", "TV_Short", "TV_Movie", "TV_Episode", "TV_MiniSeries", "TV_Special", "Video", "Video_Game"))
# show all the titleTypes with counts descending
data %>% group_by(titleType) %>% summarise(count = n()) %>% arrange(desc(count))
# converts startYear to an integer data type
data$startYear <- as.integer(data$startYear)
head(data)
- Due to the sheer size of the dataset, we decided to remove what we thought we unnecessary variables and to filter out certain observations to make the dataset more manageable.
- We removed the variables ‘originalTitle’, ‘endYear’.
- The original title variable repeated the same information as the ‘primary title’ variable for a lot of cases.
- A large amount of data was missing for the ‘endYear’ variable.
- We filtered out all observations where the average movie rating was less than 2, and the number of votes cast for the movie was less than 50. This significantly reduced the size of the dataset which made it much easier to work with.
- The structure of the dataset was then examined. Most of the variable datatypes were imported correctly, with the exception of ‘titleType’ which should be a factor, and ‘startYear’ which should be an integer.
- The required data conversions were then carried out.
- The final dataset consists of character, logical, numeric, factor, and integer variables.
5. Tidy & Manipulate Data I
# split genres column (which is a comma seperated list) into a seperate table
data_genres <- data %>% select(tconst, genres) %>% separate_rows(genres, sep=",")
# convert the genres column to a factor
data_genres$genres <- factor(data_genres$genres)
# show all the genres with counts descending
data_genres %>% group_by(genres) %>% summarise(count = n()) %>% arrange(desc(count))
Factor `genres` contains implicit NA, consider using `forcats::fct_explicit_na`
# drop genres from data table
data <- data %>% select(-genres)
head(data_genres)
head(data)
- In order for the dataset to conform to tidy principles;
- Each variable must have its own column.
- Each observation must have its own row.
- Each value must have its own cell.
- At the moment the titles table does not conform to tidy principles since the genre column contains multiple values that are comma seperated.
- To correct this, we split out the ‘genre’ and ‘tconst’ variables into a seperate data table. Separating each comma seperated value into its own row.
- Both the data and data_genres tables are now in tidy format and have correct datatypes for all columns.
6. Tidy & Manipulate Data II
#6 create/mutate a variable
ranks = ratings %>% mutate(rank = dense_rank(desc(averageRating * numVotes))) %>% filter(rank <= 10)
titles %>% inner_join(ranks, by="tconst") %>% arrange(rank) %>% select(primaryTitle, startYear, rank, averageRating, numVotes)
data <- data %>% mutate(rank = dense_rank(desc(averageRating * numVotes)))
head(data)
To answer the Question: What are the top 10 movies in the database by popular vote?
- We are trying to find the top 10 movies with the highest value for averageRating * numVotes.
- Use dense_rank function to assign order the calculation averageRating * numVotes from highest to lowest, then assign a rank.
- Use inner_join to join the titles table to the ranks table.
- inner_join is used because we only want those titles that exist in ranks to be returned.
- The tibble prints out the top 10 movies in the dataset with “The Shawshank Redemption” being the number one movie.
- The rank varaible was then added to the entire dataset.
7. Scan I
colSums(is.na(data))
tconst titleType primaryTitle isAdult
0 0 0 0
startYear runtimeMinutes averageRating numVotes
15 29364 0 0
rank
0
## removed all rows where startYear contained NA's as the amount of missing data was less than 5% (15/295822 < 0.05)
data <- data[!is.na(data$startYear),]
## as the amount of missing values is greater than 5% for runtimeMinutes. We imputed the mean by movie category
data$runtimeMinutes <- with(data, ave(runtimeMinutes, titleType, FUN = function(x) replace(x, is.na(x), mean(x, na.rm = TRUE))))
sapply(data, function(x) sum(is.na(x)))
tconst titleType primaryTitle isAdult
0 0 0 0
startYear runtimeMinutes averageRating numVotes
0 0 0 0
rank
0
## checking for special values.
is.special <- function(x){
if (is.numeric(x))(is.infinite(x) | is.nan(x))
}
sapply(data, function(x) sum(is.special(x)))
tconst titleType primaryTitle isAdult
0 0 0 0
startYear runtimeMinutes averageRating numVotes
0 0 0 0
rank
0
## checking for inconsistencies or errors
(Rule1 <- editset(c("runtimeMinutes >= 0",
"averageRating >= 0", "averageRating <= 10",
"numVotes >= 0",
"startYear > 0", "startYear < 2020")))
Edit set:
num1 : 0 <= runtimeMinutes
num2 : 0 <= averageRating
num3 : averageRating <= 10
num4 : 0 <= numVotes
num5 : 0 < startYear
num6 : startYear < 2020
v <- violatedEdits(Rule1, data)
sum(v)
[1] 0
- The sum of missing values per column was calculated.
- Due to there being such a small percentage of missing values in the startYear column, it was deemed appropritate to simply remove these observations from the data
- As the amount of missing values is greater than 5% for ‘runtimeMinutes’, we imputed the mean value by ‘titleType’
- We then checked to make sure that there were no further missing values in the dataset
- We then checked for the presence of any infinite or nan values in the dataset of which there were none.
- We then checked for any inconsistencies in the dataset. Such as negative values in time varaibles, movie rating being between 0 and 10, and startYear being less than 2020.
8. Scan II
datas <- data %>% group_by(titleType) %>% summarise(
N = n(),
MEAN = mean(runtimeMinutes, na.rm = T),
SD = sd(runtimeMinutes, na.rm = T),
MIN = min(runtimeMinutes, na.rm = T),
Q1 = quantile(runtimeMinutes, .25, na.rm = T),
MEDIAN = quantile(runtimeMinutes, .5, na.rm = T),
Q3 = quantile(runtimeMinutes, .75, na.rm = T),
MAX = max(runtimeMinutes, na.rm = T),
IQR = Q3-Q1,
LF = Q1 - 1.5*IQR,
UF = Q3 + 1.5*IQR,
LOUT = sum(runtimeMinutes < LF, na.rm = T),
UOUT = sum(runtimeMinutes > UF, na.rm = T),
PERC_OUT = round(100*(LOUT+UOUT)/N,2))
datas %>% select(titleType, N, LOUT, UOUT, PERC_OUT) %>% arrange(desc(N))
boxplot(data$runtimeMinutes ~ data$titleType,
main = "Runtime Minutes by Title Type",
xlab = "Title Type",
ylab = "Runtime Minutes")

- According to the Tukey’s method of outlier detection, outliers are defined as the values in the data set that fall beyond the range of −1.5×IQR to 1.5×IQR.
- We determined the number of outliers in the ‘averageRating’ variable to be 5215 from the above table.
As the percentage of outliers is 1.7%, we deemed it appropriate to simply remove these observations from the dataset.
- A Multivariate boxplot was plotted to determine possible outliers in runtimeMinutes by titleType.
- Although the plot does show outliers in the variable, after conducting research the data was deemed to be valid.
There is a movie which goes for 10 days (14400 hours!)
The same can be said for the number of votes and average rating numeric variables. Conducting a boxplot shows the presence of many outliers however these are not in fact outliers as the most popular movies receive a huge number of votes in relation to other not so successful movies, and all ratings fell within 0 to 10 as checked above making them valid data.
LS0tCnRpdGxlOiAiRGF0YSBQcmVwcm9jZXNzaW5nIG9mIElNREIgRGF0YSIKYXV0aG9yOiAiQXNobGV5IE1hbGxpYSBzMzc3MzcxNiBhbmQgRGFuaWVsIEV2YW5zIHMzNzY2NjU0IgpzdWJ0aXRsZTogQXNzaWdubWVudCAzCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKIyMgUmVxdWlyZWQgcGFja2FnZXMgCgpgYGB7ciwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGVkaXRydWxlcykKYGBgCgojIyBFeGVjdXRpdmUgU3VtbWFyeSAKVGhlIOKAmFRhYiBTZXBhcmF0ZWQgVmFsdWVzICh0c3Yp4oCZIGZpbGVzIGFuZCB3ZXJlIGltcG9ydGVkIGludG8gUi4gVGhlIOKAnFxcTuKAnSB3ZXJlIGNvbnZlcnRlZCB0byBOQSdzIGFuZCBtb3N0IG9mIHRoZSB2YXJpYWJsZXMgd2VyZSBpbXBvcnRlZCBpbnRvIHRoZWlyIGNvcnJlY3QgZGF0YSB0eXBlcy4KClRoZSB0d28gZGF0YXNldHMgd2VyZSB0aGVuIGpvaW5lZCB0b2dldGhlciBieSB0aGUgY29tbW9uIHZhcmlhYmxlIHR5cGUg4oCYdGNvbnN04oCZLiBVbm5lY2Vzc2FyeSB2YXJpYWJsZXMgd2VyZSB0aGVuIHJlbW92ZWQgYW5kIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGZpbHRlcmVkIG91dCB0byBtYWtlIHRoZSBzaXplIG9mIHRoZSBkYXRhc2V0IG1vcmUgbWFuYWdlYWJsZS4gVGhlIOKAmHRpdGxlVHlwZeKAmSB2YXJpYWJsZSB3YXMgdGhlbiBjb252ZXJ0ZWQgdG8gYSBmYWN0b3IgYW5kIGxhYmVsbGVkLiBUaGUg4oCYc3RhcnRZZWFy4oCZIHZhcmlhYmxlIHdhcyBjb252ZXJ0ZWQgZnJvbSBhIGNoYXJhY3RlciB0byBhbiBpbnRlZ2VyIGRhdGEgdHlwZS4gQWxsIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgd2VyZSB0aGVuIGRlZW1lZCB0byBiZSBpbiB0aGUgY29ycmVjdCB0eXBlLgoKVGhlIGRhdGFzZXQgd2FzIG5vdCBpbiBhIHRpZHkgZm9ybWF0IGFzIHRoZSDigJhnZW5yZeKAmSB2YXJpYWJsZSBjb250YWluZWQgbXVsdGlwbGUgdmFsdWVzIGluIGVhY2ggY2VsbC4gVGhlIOKAmGdlbnJl4oCZIGFuZCDigJh0Y29uc3TigJkgdmFyaWFibGVzIHdlcmUgdGhlbiBzZWxlY3RlZCBhbmQgYSBuZXcgZGF0YXNldCBjcmVhdGVkIHdoaWNoIGhvbGRzIHRoZSBnZW5yZSB0eXBlcyBmb3IgZWFjaCB0aXRsZSBpbiBzZXBhcmF0ZSBjZWxscy4gQm90aCBkYXRhc2V0cyB3ZXJlIG5vdyBpbiBhIHRpZHkgZm9ybWF0LiBBIG5ldyB2YXJpYWJsZSB3YXMgbWFkZSBjYWxsZWQg4oCYcmFua+KAmSB0byBkZXRlcm1pbmUgdGhlIHRvcCAxMCBtb3ZpZXMgaW4gdGhlIGRhdGFzZXQuIFRoZSByYW5rIHZhcmlhYmxlIHJhbmtlZCBlYWNoIHRpdGxlIGJ5IHRoZSBudW1iZXIgb2Ygdm90ZXMgaXQgcmVjZWl2ZWQgbXVsdGlwbGllZCBieSBpdHMgYXZlcmFnZSByYXRpbmcuCgpUaGUgZGF0YSB3YXMgdGhlbiBzY2FubmVkIGZvciBtaXNzaW5nIHZhbHVlcywgZXJyb3JzLCBhbmQgaW5jb25zaXN0ZW5jaWVzLiBNaXNzaW5nIHZhbHVlcyBpbiB0aGUg4oCYdGl0bGVUeXBl4oCZIHZhcmlhYmxlIHdlcmUgZGVsZXRlZCBhcyB0aGVyZSB3YXMgYSB2ZXJ5IHNtYWxsIHBlcmNlbnRhZ2Ugd2hpbGUgdGhlIG1lYW4gd2FzIGltcHV0ZWQgZm9yIHRoZSDigJhydW50aW1lTWludXRlc+KAmSB2YXJpYWJsZSBieSDigJh0aXRsZVR5cGXigJkuIE5vIG1pc3NpbmcgdmFsdWVzIHJlbWFpbmVkIGluIHRoZSBkYXRhc2V0LiBUaGVyZSB3ZXJlIG5vIHNwZWNpYWwgdmFsdWVzIG9yIGVycm9ycyBmb3VuZC4KClRoZSBkYXRhc2V0IHdhcyB0aGVuIHNjYW5uZWQgZm9yIG91dGxpZXJzLiBPdXRsaWVycyB3ZXJlIGZvdW5kIGluIOKAmG51bVZvdGVz4oCZIGFuZCDigJhydW50aW1lTWludXRlc+KAmSBieSB0aXRsZVR5cGUgaG93ZXZlciBhZnRlciByZXNlYXJjaCB0aGVzZSBlbnRyaWVzIHdlcmUgZGVlbWVkIHRvIGJlIHZhbGlkLiAKCkEgaGlzdG9ncmFtIG9mIOKAmGF2ZXJhZ2VSYXRpbmfigJkgc2hvd2VkIHRoZSBkYXRhIHRvIGJlIGxlZnQgc2tld2VkLiBBIHNxdWFyZSB0cmFuc2Zvcm1hdGlvbiB3YXMgZG9uZSB3aGljaCBzaWduaWZpY2FudGx5IHJlZHVjZWQgdGhlIHNrZXdlZG5lc3MgYW5kIHRyYW5zZm9ybWVkIHRoZSBkYXRhIGludG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uIG1ha2luZyBzdGF0aXN0aWNhbCBhbmFseXNpcyBtdWNoIHNpbXBsZXIuCgojIyBEYXRhIApUaGUgZGF0YXNldHMgd2VyZSBkb3dubG9hZGVkIGFzICdUYWIgU2VwYXJhdGVkIFZhbHVlcyh0c3YpJyBmaWxlcyBmcm9tIHRoZSBJbnRlcm5hdGlvbmFsIE1vdmllIERhdGFiYXNlIChJTURCKSB3ZWJzaXRlIAoKaHR0cHM6Ly9kYXRhc2V0cy5pbWRid3MuY29tLy4gIAoKVGhlIElNREIgd2Vic2l0ZSBpcyBhIHBvcHVsYXIgd2Vic2l0ZSB0byBmaW5kIG91dCBhbnkgcmVsZXZhbnQgaW5mb3JtYXRpb24gcmVnYXJkaW5nIHRob3VzYW5kcyBvZiBtb3ZpZXMsIHR2IHNob3dzLCBkb2N1bWVudGFyaWVzIGV0Yy4gVXNlcnMgY2FuIGxlYXZlIHJldmlld3Mgb2YgdGl0bGVzLCBkZXRlcm1pbmUgdGhlIGNhc3QsIHdyaXRlcnMsIGRpcmVjdG9ycyBvZiBzcGVjaWZpYyB0aXRsZXMgYW5kIG11Y2ggbW9yZS4gSXQgaXMgYSB2ZXJ5IHVzZWZ1bCBzaXRlIGZvciBkZXRlcm1pbmluZyB5b3VyIG5leHQgbW92aWUgdG8gd2F0Y2guClRoZSBkYXRhc2V0cyBhcmUgYWNjZXNzaWJsZSB0byBjdXN0b21lcnMgZm9yIHBlcnNvbmFsIGFuZCBub24tY29tbWVyY2lhbCB1c2UgYW5kIGFyZSByZWZyZXNoZWQgZGFpbHkuIApUaGUgdHdvIGRhdGFzZXRzIHdlIGNob3NlIHRvIGpvaW4gdG9nZXRoZXIgYXJlIOKAmGJhc2ljc+KAmSBhbmQg4oCYcmF0aW5nc+KAmS4gCgoqKkJhc2ljcyoqIGNvbnRhaW5zIHRoZSBmb2xsb3dpbmcgaW5mb3JtYXRpb24gZm9yIHRpdGxlczoKCi0gdGNvbnN0IChzdHJpbmcpOiBhbHBoYW51bWVyaWMgdW5pcXVlIGlkZW50aWZpZXIgb2YgdGhlIHRpdGxlCi0gdGl0bGVUeXBlIChzdHJpbmcpOiB0aGUgdHlwZS9mb3JtYXQgb2YgdGhlIHRpdGxlIChlLmcuIG1vdmllLCBzaG9ydCwgdHZzZXJpZXMsIHR2ZXBpc29kZSwgdmlkZW8sIGV0YykKLSBwcmltYXJ5VGl0bGUgKHN0cmluZyk6IHRoZSBtb3JlIHBvcHVsYXIgdGl0bGUgLyB0aGUgdGl0bGUgdXNlZCBieSB0aGUgZmlsbW1ha2VycyBvbiBwcm9tb3Rpb25hbCBtYXRlcmlhbHMgYXQgdGhlIHBvaW50IG9mIHJlbGVhc2UKLSBvcmlnaW5hbFRpdGxlIChzdHJpbmcpOiBvcmlnaW5hbCB0aXRsZSwgaW4gdGhlIG9yaWdpbmFsIGxhbmd1YWdlCi0gaXNBZHVsdCAoYm9vbGVhbik6ICAwIG5vbi1hZHVsdCB0aXRsZTsgMSBhZHVsdCB0aXRsZQotIHN0YXJ0WWVhciAoWVlZWSk6IHJlcHJlc2VudHMgdGhlIHJlbGVhc2UgeWVhciBvZiBhIHRpdGxlLiBJbiB0aGUgY2FzZSBvZiBUViBTZXJpZXMsIGl0IGlzIHRoZSBzZXJpZXMgc3RhcnQgeWVhcgotIGVuZFllYXIgKFlZWVkpOiBUViBTZXJpZXMgZW5kIHllYXIuIOKAmFxO4oCZIGZvciBhbGwgb3RoZXIgdGl0bGUgdHlwZXMKLSBydW50aW1lTWludXRlczogcHJpbWFyeSBydW50aW1lIG9mIHRoZSB0aXRsZSwgaW4gbWludXRlcwotIGdlbnJlcyAoc3RyaW5nIGFycmF5KTogaW5jbHVkZXMgdXAgdG8gdGhyZWUgZ2VucmVzIGFzc29jaWF0ZWQgd2l0aCB0aGUgdGl0bGUKCioqUmF0aW5ncyoqIGNvbnRhaW5zIHRoZSBJTURiIHJhdGluZyBhbmQgdm90ZXMgaW5mb3JtYXRpb24gZm9yIHRpdGxlcwoKLSB0Y29uc3QgKHN0cmluZyk6IGFscGhhbnVtZXJpYyB1bmlxdWUgaWRlbnRpZmllciBvZiB0aGUgdGl0bGUKLSBhdmVyYWdlUmF0aW5nOiB3ZWlnaHRlZCBhdmVyYWdlIG9mIGFsbCB0aGUgaW5kaXZpZHVhbCB1c2VyIHJhdGluZ3MKLQludW1Wb3RlczogbnVtYmVyIG9mIHZvdGVzIHRoZSB0aXRsZSBoYXMgcmVjZWl2ZWQKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIGVjaG8gPSBULCByZXN1bHRzID0naGlkZSd9CiMjIEltcG9ydCBkYXRhCnRpdGxlcyA8LSByZWFkX2RlbGltKCJ0aXRsZS5iYXNpY3MudHN2IiwgIlx0IiwgZXNjYXBlX2RvdWJsZSA9IEZBTFNFLCBuYSA9ICJcXE4iLCB0cmltX3dzID0gVFJVRSwgcXVvdGU9JycsCiAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoCiAgICAgICAgICAgICAgICAgICAgICAgdGNvbnN0ID0gY29sX2NoYXJhY3RlcigpLCAKICAgICAgICAgICAgICAgICAgICAgICB0aXRsZVR5cGUgPSBjb2xfY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgcHJpbWFyeVRpdGxlID0gY29sX2NoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgIG9yaWdpbmFsVGl0bGUgPSBjb2xfY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgaXNBZHVsdCA9IGNvbF9sb2dpY2FsKCksCiAgICAgICAgICAgICAgICAgICAgICAgc3RhcnRZZWFyID0gY29sX2NoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgIGVuZFllYXIgPSBjb2xfaW50ZWdlcigpLCAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgcnVudGltZU1pbnV0ZXMgPSBjb2xfaW50ZWdlcigpLCAKICAgICAgICAgICAgICAgICAgICAgICBnZW5yZXMgPSBjb2xfY2hhcmFjdGVyKCkpKQpyYXRpbmdzIDwtIHJlYWRfZGVsaW0oInRpdGxlLnJhdGluZ3MudHN2IiwgIlx0IiwgZXNjYXBlX2RvdWJsZSA9IEZBTFNFLCBuYSA9ICJcXE4iLCB0cmltX3dzID0gVFJVRSwgcXVvdGU9JycpCmBgYApgYGB7ciBtZXNzYWdlPUZBTFNFLCBjb2xzLnByaW50PTExfQpoZWFkKHRpdGxlcykKaGVhZChyYXRpbmdzKQoKIyMgSm9pbiBkYXRhc2V0cwpkYXRhIDwtIHRpdGxlcyAlPiUgbGVmdF9qb2luKHJhdGluZ3MsIGJ5ID0gInRjb25zdCIpCmhlYWQoZGF0YSkKYGBgCi0gVGhlIGRhdGFzZXRzIHdlcmUgaW1wb3J0ZWQgaW50byBSIHVzaW5nIHRoZSDigJhyZWFkX2RlbGlt4oCZIGZ1bmN0aW9uLiAKLSB0aGUgIlxcTiIgd2VyZSBjb252ZXJ0ZWQgdG8gTkHigJlzIGFuZCBtb3N0IG9mIHRoZSB2YXJpYWJsZXMgd2VyZSBjb252ZXJ0ZWQgdG8gdGhlaXIgY29ycmVjdCBkYXRhIHR5cGVzIHVwb24gaW1wb3J0aW5nIHRoZSBkYXRhIGludG8gUiB1c2luZyB0aGUgY29sX3R5cGVzIGFyZ3VtZW50LiAKLSBUaGUgUmF0aW5ncyBkYXRhc2V0IHdhcyBqb2luZWQgdG8gQmFzaWNzIGJ5IHRoZSDigJh0Y29uc3TigJkgdmFyaWFibGUgdXNpbmcgYSBsZWZ0X2pvaW4uIAoKIyMgVW5kZXJzdGFuZCAKYGBge3IsIGNvbHMucHJpbnQgPSAxMX0KIyBkcm9wIHVubmVlZGVkIGNvbHVtbnMKZGF0YSA8LSBkYXRhICU+JSBzZWxlY3QoLW9yaWdpbmFsVGl0bGUsIC1lbmRZZWFyKQojIGZpbHRlcmVkIG9ic2VydmF0aW9ucwpkYXRhIDwtIGRhdGEgJT4lIGZpbHRlcihhdmVyYWdlUmF0aW5nID49IDIgJiBudW1Wb3RlcyA+PSA1MCkKc3RyKGRhdGEpCiMgY29udmVydCB0aXRsZXNUeXBlIHRvIGZhY3RvcgpkYXRhJHRpdGxlVHlwZSA8LSBmYWN0b3IoZGF0YSR0aXRsZVR5cGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygic2hvcnQiLCAibW92aWUiLCAidHZTZXJpZXMiLCAidHZTaG9ydCIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHZNb3ZpZSIsICJ0dkVwaXNvZGUiLCAidHZNaW5pU2VyaWVzIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHZTcGVjaWFsIiwgInZpZGVvIiwgInZpZGVvR2FtZSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlNob3J0IiwgIk1vdmllIiwgIlRWX1NlcmllcyIsICJUVl9TaG9ydCIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUVl9Nb3ZpZSIsICJUVl9FcGlzb2RlIiwgIlRWX01pbmlTZXJpZXMiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVFZfU3BlY2lhbCIsICJWaWRlbyIsICJWaWRlb19HYW1lIikpCiMgc2hvdyBhbGwgdGhlIHRpdGxlVHlwZXMgd2l0aCBjb3VudHMgZGVzY2VuZGluZwpkYXRhICU+JSBncm91cF9ieSh0aXRsZVR5cGUpICU+JSBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSBhcnJhbmdlKGRlc2MoY291bnQpKQojIGNvbnZlcnRzIHN0YXJ0WWVhciB0byBhbiBpbnRlZ2VyIGRhdGEgdHlwZQpkYXRhJHN0YXJ0WWVhciA8LSBhcy5pbnRlZ2VyKGRhdGEkc3RhcnRZZWFyKQpoZWFkKGRhdGEpCmBgYAotCUR1ZSB0byB0aGUgc2hlZXIgc2l6ZSBvZiB0aGUgZGF0YXNldCwgd2UgZGVjaWRlZCB0byByZW1vdmUgd2hhdCB3ZSB0aG91Z2h0IHdlIHVubmVjZXNzYXJ5IHZhcmlhYmxlcyBhbmQgdG8gZmlsdGVyIG91dCBjZXJ0YWluIG9ic2VydmF0aW9ucyB0byBtYWtlIHRoZSBkYXRhc2V0IG1vcmUgbWFuYWdlYWJsZS4gCi0JV2UgcmVtb3ZlZCB0aGUgdmFyaWFibGVzIOKAmG9yaWdpbmFsVGl0bGXigJksIOKAmGVuZFllYXLigJkuCiAgICAgIC0gVGhlIG9yaWdpbmFsIHRpdGxlIHZhcmlhYmxlIHJlcGVhdGVkIHRoZSBzYW1lIGluZm9ybWF0aW9uIGFzIHRoZSAgICAgICAgICAgICAg4oCYcHJpbWFyeSB0aXRsZeKAmSB2YXJpYWJsZSBmb3IgYSBsb3Qgb2YgY2FzZXMuICAKICAgICAgLSBBIGxhcmdlIGFtb3VudCBvZiBkYXRhIHdhcyBtaXNzaW5nIGZvciB0aGUg4oCYZW5kWWVhcuKAmSB2YXJpYWJsZS4gCi0gV2UgZmlsdGVyZWQgb3V0IGFsbCBvYnNlcnZhdGlvbnMgd2hlcmUgdGhlIGF2ZXJhZ2UgbW92aWUgcmF0aW5nIHdhcyBsZXNzIHRoYW4gMiwgYW5kIHRoZSBudW1iZXIgb2Ygdm90ZXMgY2FzdCBmb3IgdGhlIG1vdmllIHdhcyBsZXNzIHRoYW4gNTAuIFRoaXMgc2lnbmlmaWNhbnRseSByZWR1Y2VkIHRoZSBzaXplIG9mIHRoZSBkYXRhc2V0IHdoaWNoIG1hZGUgaXQgbXVjaCBlYXNpZXIgdG8gd29yayB3aXRoLiAKLSBUaGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0IHdhcyB0aGVuIGV4YW1pbmVkLiBNb3N0IG9mIHRoZSB2YXJpYWJsZSBkYXRhdHlwZXMgd2VyZSBpbXBvcnRlZCBjb3JyZWN0bHksIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiDigJh0aXRsZVR5cGXigJkgd2hpY2ggc2hvdWxkIGJlIGEgZmFjdG9yLCBhbmQg4oCYc3RhcnRZZWFy4oCZIHdoaWNoIHNob3VsZCBiZSBhbiBpbnRlZ2VyLiAKLSBUaGUgcmVxdWlyZWQgZGF0YSBjb252ZXJzaW9ucyB3ZXJlIHRoZW4gY2FycmllZCBvdXQuIAotIFRoZSBmaW5hbCBkYXRhc2V0IGNvbnNpc3RzIG9mIGNoYXJhY3RlciwgbG9naWNhbCwgbnVtZXJpYywgZmFjdG9yLCBhbmQgaW50ZWdlciB2YXJpYWJsZXMuIAoKIyMJNS4gVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIApgYGB7ciwgY29scy5wcmludCA9IDExfQojIHNwbGl0IGdlbnJlcyBjb2x1bW4gKHdoaWNoIGlzIGEgY29tbWEgc2VwZXJhdGVkIGxpc3QpIGludG8gYSBzZXBlcmF0ZSB0YWJsZQpkYXRhX2dlbnJlcyA8LSBkYXRhICU+JSBzZWxlY3QodGNvbnN0LCBnZW5yZXMpICU+JSBzZXBhcmF0ZV9yb3dzKGdlbnJlcywgc2VwPSIsIikKIyBjb252ZXJ0IHRoZSBnZW5yZXMgY29sdW1uIHRvIGEgZmFjdG9yCmRhdGFfZ2VucmVzJGdlbnJlcyA8LSBmYWN0b3IoZGF0YV9nZW5yZXMkZ2VucmVzKQojIHNob3cgYWxsIHRoZSBnZW5yZXMgd2l0aCBjb3VudHMgZGVzY2VuZGluZwpkYXRhX2dlbnJlcyAlPiUgZ3JvdXBfYnkoZ2VucmVzKSAlPiUgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKGNvdW50KSkKIyBkcm9wIGdlbnJlcyBmcm9tIGRhdGEgdGFibGUKZGF0YSA8LSBkYXRhICU+JSBzZWxlY3QoLWdlbnJlcykKaGVhZChkYXRhX2dlbnJlcykKaGVhZChkYXRhKQpgYGAKLSBJbiBvcmRlciBmb3IgdGhlIGRhdGFzZXQgdG8gY29uZm9ybSB0byB0aWR5IHByaW5jaXBsZXM7CiAgICAtIEVhY2ggdmFyaWFibGUgbXVzdCBoYXZlIGl0cyBvd24gY29sdW1uLgogICAgLQlFYWNoIG9ic2VydmF0aW9uIG11c3QgaGF2ZSBpdHMgb3duIHJvdy4KICAgIC0gRWFjaCB2YWx1ZSBtdXN0IGhhdmUgaXRzIG93biBjZWxsLgotIEF0IHRoZSBtb21lbnQgdGhlIHRpdGxlcyB0YWJsZSBkb2VzIG5vdCBjb25mb3JtIHRvIHRpZHkgcHJpbmNpcGxlcyBzaW5jZSB0aGUgZ2VucmUgY29sdW1uIGNvbnRhaW5zIG11bHRpcGxlIHZhbHVlcyB0aGF0IGFyZSBjb21tYSBzZXBlcmF0ZWQuCi0gVG8gY29ycmVjdCB0aGlzLCB3ZSBzcGxpdCBvdXQgdGhlIOKAmGdlbnJl4oCZIGFuZCDigJh0Y29uc3TigJkgdmFyaWFibGVzIGludG8gYSBzZXBlcmF0ZSBkYXRhIHRhYmxlLiBTZXBhcmF0aW5nIGVhY2ggY29tbWEgc2VwZXJhdGVkIHZhbHVlIGludG8gaXRzIG93biByb3cuCi0gQm90aCB0aGUgZGF0YSBhbmQgZGF0YV9nZW5yZXMgdGFibGVzIGFyZSBub3cgaW4gdGlkeSBmb3JtYXQgYW5kIGhhdmUgY29ycmVjdCBkYXRhdHlwZXMgZm9yIGFsbCBjb2x1bW5zLiAKCiMjCTYuIFRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkKYGBge3IsIGNvbHMucHJpbnQgPSAxMX0KIzYgY3JlYXRlL211dGF0ZSBhIHZhcmlhYmxlCnJhbmtzID0gcmF0aW5ncyAlPiUgbXV0YXRlKHJhbmsgPSBkZW5zZV9yYW5rKGRlc2MoYXZlcmFnZVJhdGluZyAqIG51bVZvdGVzKSkpICU+JSBmaWx0ZXIocmFuayA8PSAxMCkKCnRpdGxlcyAlPiUgaW5uZXJfam9pbihyYW5rcywgYnk9InRjb25zdCIpICU+JSBhcnJhbmdlKHJhbmspICU+JSBzZWxlY3QocHJpbWFyeVRpdGxlLCBzdGFydFllYXIsIHJhbmssIGF2ZXJhZ2VSYXRpbmcsIG51bVZvdGVzKQoKZGF0YSA8LSBkYXRhICU+JSBtdXRhdGUocmFuayA9IGRlbnNlX3JhbmsoZGVzYyhhdmVyYWdlUmF0aW5nICogbnVtVm90ZXMpKSkgCmhlYWQoZGF0YSkKYGBgClRvIGFuc3dlciB0aGUgUXVlc3Rpb246ICoqV2hhdCBhcmUgdGhlIHRvcCAxMCBtb3ZpZXMgaW4gdGhlIGRhdGFiYXNlIGJ5IHBvcHVsYXIgdm90ZT8qKgoKLSBXZSBhcmUgdHJ5aW5nIHRvIGZpbmQgdGhlIHRvcCAxMCBtb3ZpZXMgd2l0aCB0aGUgaGlnaGVzdCB2YWx1ZSBmb3IgYXZlcmFnZVJhdGluZyAqIG51bVZvdGVzLgotIFVzZSBkZW5zZV9yYW5rIGZ1bmN0aW9uIHRvIGFzc2lnbiBvcmRlciB0aGUgY2FsY3VsYXRpb24gYXZlcmFnZVJhdGluZyAqIG51bVZvdGVzIGZyb20gaGlnaGVzdCB0byBsb3dlc3QsIHRoZW4gYXNzaWduIGEgcmFuay4KLSBVc2UgaW5uZXJfam9pbiB0byBqb2luIHRoZSB0aXRsZXMgdGFibGUgdG8gdGhlIHJhbmtzIHRhYmxlLgogICAgLSBpbm5lcl9qb2luIGlzIHVzZWQgYmVjYXVzZSB3ZSBvbmx5IHdhbnQgdGhvc2UgdGl0bGVzIHRoYXQgZXhpc3QgaW4gcmFua3MgICAgIHRvIGJlIHJldHVybmVkLgotIFRoZSB0aWJibGUgcHJpbnRzIG91dCB0aGUgdG9wIDEwIG1vdmllcyBpbiB0aGUgZGF0YXNldCB3aXRoICJUaGUgU2hhd3NoYW5rIFJlZGVtcHRpb24iIGJlaW5nIHRoZSBudW1iZXIgb25lIG1vdmllLiAKLSBUaGUgcmFuayB2YXJhaWJsZSB3YXMgdGhlbiBhZGRlZCB0byB0aGUgZW50aXJlIGRhdGFzZXQuIAoKIyMJNy4gU2NhbiBJIApgYGB7cn0KY29sU3Vtcyhpcy5uYShkYXRhKSkKIyMgcmVtb3ZlZCBhbGwgcm93cyB3aGVyZSBzdGFydFllYXIgY29udGFpbmVkIE5BJ3MgYXMgdGhlIGFtb3VudCBvZiBtaXNzaW5nIGRhdGEgd2FzIGxlc3MgdGhhbiA1JSAoMTUvMjk1ODIyIDwgMC4wNSkKZGF0YSA8LSBkYXRhWyFpcy5uYShkYXRhJHN0YXJ0WWVhciksXQoKIyMgYXMgdGhlIGFtb3VudCBvZiBtaXNzaW5nIHZhbHVlcyBpcyBncmVhdGVyIHRoYW4gNSUgZm9yIHJ1bnRpbWVNaW51dGVzLiBXZSBpbXB1dGVkIHRoZSBtZWFuIGJ5IG1vdmllIGNhdGVnb3J5CmRhdGEkcnVudGltZU1pbnV0ZXMgPC0gIHdpdGgoZGF0YSwgYXZlKHJ1bnRpbWVNaW51dGVzLCB0aXRsZVR5cGUsIEZVTiA9IGZ1bmN0aW9uKHgpIHJlcGxhY2UoeCwgaXMubmEoeCksIG1lYW4oeCwgbmEucm0gPSBUUlVFKSkpKQpzYXBwbHkoZGF0YSwgZnVuY3Rpb24oeCkgc3VtKGlzLm5hKHgpKSkKCiMjIGNoZWNraW5nIGZvciBzcGVjaWFsIHZhbHVlcy4KaXMuc3BlY2lhbCA8LSBmdW5jdGlvbih4KXsKICAgaWYgKGlzLm51bWVyaWMoeCkpKGlzLmluZmluaXRlKHgpIHwgaXMubmFuKHgpKQp9CnNhcHBseShkYXRhLCBmdW5jdGlvbih4KSBzdW0oaXMuc3BlY2lhbCh4KSkpCgojIyBjaGVja2luZyBmb3IgaW5jb25zaXN0ZW5jaWVzIG9yIGVycm9ycwooUnVsZTEgPC0gZWRpdHNldChjKCJydW50aW1lTWludXRlcyA+PSAwIiwKICAgICAgICAgICAgICAgICAgICAiYXZlcmFnZVJhdGluZyA+PSAwIiwgImF2ZXJhZ2VSYXRpbmcgPD0gMTAiLAogICAgICAgICAgICAgICAgICAgICJudW1Wb3RlcyA+PSAwIiwgCiAgICAgICAgICAgICAgICAgICAgInN0YXJ0WWVhciA+IDAiLCAic3RhcnRZZWFyIDwgMjAyMCIpKSkKdiA8LSB2aW9sYXRlZEVkaXRzKFJ1bGUxLCBkYXRhKQpzdW0odikKYGBgCi0gVGhlIHN1bSBvZiBtaXNzaW5nIHZhbHVlcyBwZXIgY29sdW1uIHdhcyBjYWxjdWxhdGVkLiAKLSBEdWUgdG8gdGhlcmUgYmVpbmcgc3VjaCBhIHNtYWxsIHBlcmNlbnRhZ2Ugb2YgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIHN0YXJ0WWVhciBjb2x1bW4sIGl0IHdhcyBkZWVtZWQgYXBwcm9wcml0YXRlIHRvIHNpbXBseSByZW1vdmUgdGhlc2Ugb2JzZXJ2YXRpb25zIGZyb20gdGhlIGRhdGEKLSBBcyB0aGUgYW1vdW50IG9mIG1pc3NpbmcgdmFsdWVzIGlzIGdyZWF0ZXIgdGhhbiA1JSBmb3Ig4oCYcnVudGltZU1pbnV0ZXPigJksIHdlIGltcHV0ZWQgdGhlIG1lYW4gdmFsdWUgYnkg4oCYdGl0bGVUeXBl4oCZIAotIFdlIHRoZW4gY2hlY2tlZCB0byBtYWtlIHN1cmUgdGhhdCB0aGVyZSB3ZXJlIG5vIGZ1cnRoZXIgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQKLSBXZSB0aGVuIGNoZWNrZWQgZm9yIHRoZSBwcmVzZW5jZSBvZiBhbnkgaW5maW5pdGUgb3IgbmFuIHZhbHVlcyBpbiB0aGUgZGF0YXNldCBvZiB3aGljaCB0aGVyZSB3ZXJlIG5vbmUuIAotIFdlIHRoZW4gY2hlY2tlZCBmb3IgYW55IGluY29uc2lzdGVuY2llcyBpbiB0aGUgZGF0YXNldC4gU3VjaCBhcyBuZWdhdGl2ZSB2YWx1ZXMgaW4gdGltZSB2YXJhaWJsZXMsIG1vdmllIHJhdGluZyBiZWluZyBiZXR3ZWVuIDAgYW5kIDEwLCBhbmQgc3RhcnRZZWFyIGJlaW5nIGxlc3MgdGhhbiAyMDIwLiAgCgojIyAgOC4gU2NhbiBJSQpgYGB7ciwgY29scy5wcmludCA9IDE0fQpkYXRhcyA8LSBkYXRhICU+JSBncm91cF9ieSh0aXRsZVR5cGUpICU+JSBzdW1tYXJpc2UoCiAgTiAgID0gbigpLAogIE1FQU4gPSBtZWFuKHJ1bnRpbWVNaW51dGVzLCBuYS5ybSA9IFQpLCAKICBTRCAgPSBzZChydW50aW1lTWludXRlcywgbmEucm0gPSBUKSwKICBNSU4gPSBtaW4ocnVudGltZU1pbnV0ZXMsIG5hLnJtID0gVCksIAogIFExICA9IHF1YW50aWxlKHJ1bnRpbWVNaW51dGVzLCAuMjUsIG5hLnJtID0gVCksIAogIE1FRElBTiA9IHF1YW50aWxlKHJ1bnRpbWVNaW51dGVzLCAuNSwgbmEucm0gPSBUKSwgCiAgUTMgICA9IHF1YW50aWxlKHJ1bnRpbWVNaW51dGVzLCAuNzUsIG5hLnJtID0gVCksIAogIE1BWCAgPSBtYXgocnVudGltZU1pbnV0ZXMsIG5hLnJtID0gVCksIAogIElRUiAgPSBRMy1RMSwgCiAgTEYgPSBRMSAtIDEuNSpJUVIsCiAgVUYgPSBRMyArIDEuNSpJUVIsCiAgTE9VVCA9IHN1bShydW50aW1lTWludXRlcyA8IExGLCBuYS5ybSA9IFQpLAogIFVPVVQgPSBzdW0ocnVudGltZU1pbnV0ZXMgPiBVRiwgbmEucm0gPSBUKSwKICBQRVJDX09VVCA9IHJvdW5kKDEwMCooTE9VVCtVT1VUKS9OLDIpKQpkYXRhcyAlPiUgc2VsZWN0KHRpdGxlVHlwZSwgTiwgTE9VVCwgVU9VVCwgUEVSQ19PVVQpICU+JSBhcnJhbmdlKGRlc2MoTikpCgpib3hwbG90KGRhdGEkcnVudGltZU1pbnV0ZXMgfiBkYXRhJHRpdGxlVHlwZSwgCiAgICAgICAgbWFpbiA9ICJSdW50aW1lIE1pbnV0ZXMgYnkgVGl0bGUgVHlwZSIsCiAgICAgICAgeGxhYiA9ICJUaXRsZSBUeXBlIiwKICAgICAgICB5bGFiID0gIlJ1bnRpbWUgTWludXRlcyIpCmBgYAotIEFjY29yZGluZyB0byB0aGUgVHVrZXnigJlzIG1ldGhvZCBvZiBvdXRsaWVyIGRldGVjdGlvbiwgb3V0bGllcnMgYXJlIGRlZmluZWQgYXMgdGhlIHZhbHVlcyBpbiB0aGUgZGF0YSBzZXQgdGhhdCBmYWxsIGJleW9uZCB0aGUgcmFuZ2Ugb2Yg4oiSMS41w5dJUVIgdG8gMS41w5dJUVIuIAotIFdlIGRldGVybWluZWQgdGhlIG51bWJlciBvZiBvdXRsaWVycyBpbiB0aGUg4oCYYXZlcmFnZVJhdGluZ+KAmSB2YXJpYWJsZSB0byBiZSA1MjE1IGZyb20gdGhlIGFib3ZlIHRhYmxlLgotIEFzIHRoZSBwZXJjZW50YWdlIG9mIG91dGxpZXJzIGlzIDEuNyUsIHdlIGRlZW1lZCBpdCBhcHByb3ByaWF0ZSB0byBzaW1wbHkgcmVtb3ZlIHRoZXNlIG9ic2VydmF0aW9ucyBmcm9tIHRoZSBkYXRhc2V0LiAKCi0gQSBNdWx0aXZhcmlhdGUgYm94cGxvdCB3YXMgcGxvdHRlZCB0byBkZXRlcm1pbmUgcG9zc2libGUgb3V0bGllcnMgaW4gcnVudGltZU1pbnV0ZXMgYnkgdGl0bGVUeXBlLiAKLSBBbHRob3VnaCB0aGUgcGxvdCBkb2VzIHNob3cgb3V0bGllcnMgaW4gdGhlIHZhcmlhYmxlLCBhZnRlciBjb25kdWN0aW5nIHJlc2VhcmNoIHRoZSBkYXRhIHdhcyBkZWVtZWQgdG8gYmUgdmFsaWQuIAotIFRoZXJlIGlzIGEgbW92aWUgd2hpY2ggZ29lcyBmb3IgMTAgZGF5cyAoMTQ0MDAgaG91cnMhKQoKLSBUaGUgc2FtZSBjYW4gYmUgc2FpZCBmb3IgdGhlIG51bWJlciBvZiB2b3RlcyBhbmQgYXZlcmFnZSByYXRpbmcgbnVtZXJpYyB2YXJpYWJsZXMuIENvbmR1Y3RpbmcgYSBib3hwbG90IHNob3dzIHRoZSBwcmVzZW5jZSBvZiBtYW55IG91dGxpZXJzIGhvd2V2ZXIgdGhlc2UgYXJlIG5vdCBpbiBmYWN0IG91dGxpZXJzIGFzIHRoZSBtb3N0IHBvcHVsYXIgbW92aWVzIHJlY2VpdmUgYSBodWdlIG51bWJlciBvZiB2b3RlcyBpbiByZWxhdGlvbiB0byBvdGhlciBub3Qgc28gc3VjY2Vzc2Z1bCBtb3ZpZXMsIGFuZCBhbGwgcmF0aW5ncyBmZWxsIHdpdGhpbiAwIHRvIDEwIGFzIGNoZWNrZWQgYWJvdmUgbWFraW5nIHRoZW0gdmFsaWQgZGF0YS4gICAKCgojIwlUcmFuc2Zvcm0gCmBgYHtyfQpoaXN0KGRhdGEkYXZlcmFnZVJhdGluZyAgLCBtYWluID0gIkZpZzEgSGlzdG9ncmFtIG9mIEF2ZXJhZ2UgUmF0aW5nIiwgeGxhYiA9ICJBdmVyYWdlIFJhdGluZyIpCmhpc3QoZGF0YSRhdmVyYWdlUmF0aW5nXjIsIG1haW4gPSAiRmlnMiBIaXN0b2dyYW0gb2YgQXZlcmFnZSBSYXRpbmcgU3F1YXJlZCIsIHhsYWIgPSAiQXZlcmFnZSBSYXRpbmcgU3F1YXJlZCIpCmBgYAoKLSBJbiBGaWcxIHdlIHNob3cgYSBoaXN0b2dyYW0gb2YgYXZlcmFnZSByYXRpbmdzIGZyb20gdGhlIGRhdGFzZXQuIFRoZSBkYXRhIGlzIGxlZnQgc2tld2VkLgotIEZpZzIgc2hvd3Mgd2hlbiBhIHNxdWFyaW5nIHRyYW5zZm9ybWF0aW9uIGlzIGFwcGxpZWQsIHRoZSBoaXN0b2dyYW0gbG9va3MgbW9yZSBsaWtlIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiAK