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:

Ratings contains the IMDb rating and votes information for titles

## 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)

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)

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)

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?

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

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")

Transform

hist(data$averageRating  , main = "Fig1 Histogram of Average Rating", xlab = "Average Rating")

hist(data$averageRating^2, main = "Fig2 Histogram of Average Rating Squared", xlab = "Average Rating Squared")

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