Required packages

Let’s install the necessary packages to proceed with this project.


library(readr)
library(tidyr)
library(dplyr)
library(Hmisc)
library(outliers)
library(stringr)
library(lubridate)
library(editrules)
library(jsonlite)

Executive Summary

The purpose of this project is to apply various data pre-processing techniques to process and tidy up our datasets before conducting the analysis. The two datasets are collected from Kaggle: the first set named IMDB 5000 Movie Dataset contains data of around 5000 movies of 2017 or earlier from IMDb and the second set named TMDb 5000 Movie Dataset contains data of around 5000 movies of 2017 or earlier from TMDb.

Since both the datasets have different data which are required for the further analysis, we’ll start by merging these two datasets into a single dataset and keeping just the required variables for the purpose of our further analysis. In all these datasets, each row represents a single movie. In order to keep the dataset simple for achieving our main aim of applying data pre-processing techniques, we’ll just select 5 variables from the first dataset and 9 variables from the second, thus, 13 variables in the merged one.

Then, we’ll try to understand the data by checking its structure and more. Then, we’ll tidy up and manipulate the data to ensure our final dataset follows the rules of tidy data. Then, we’ll scan the data for missing values, errors, and outliers. We’ll try to get info on outliers and solve them or remove them per the dataset. Finally, we’ll apply some data transformation techniques on the dataset as well.

Data

1. IMDB 5000 Movie Dataset

The dataset IMDB 5000 Movie Dataset contains the movie data from IMDb (Internet Movie Database). It contains 4998 observations and 28 variables (columns), which are described below. I’m going to use some of them as detailed later.

Data Source: IMDB 5000 Movie Dataset from Kaggle.


# importing the first dataset
movies_data1 <- read_csv("imdb_5000_movies.csv")

# remove duplicate rows
movies_data1 <- distinct(movies_data1)

# check the dimensions
dim(movies_data1)
[1] 4998   28
# preview the dataset
head(movies_data1)
NA

2. TMDB 5000 Movie Dataset

The dataset TMDB 5000 Movie Dataset contains the movie data from TMDb (The Movie Database). It contains 4803 observations and 20 variables (columns), which are described below. I’m going to use some of them as detailed later.

Data Source: TMDB 5000 Movie Dataset from Kaggle.


# importing the second dataset
movies_data2 <- read_csv("tmdb_5000_movies.csv")

# remove duplicate rows
movies_data2 <- distinct(movies_data2)

# check the dimensions
dim(movies_data2)
[1] 4803   20
# preview the dataset
head(movies_data2)
NA

3. Merged Dataset: movies

First of all, let’s extract and rename the necessary columns in both the datasets before merging them in the final dataset. Then finally, let’s merge both the datasets to create the movies data frame, whose columns (13 in count) are described below.

# Variable Description
1 title Title of the movie
2 country Country of the movie
3 genres Genres of the movie
4 imdb_score IMDb score for the movie
5 fb_likes FB likes of the movie
6 tagline Tagline of the movie
7 release_date Release date of the movie
8 language Language of the movie
9 keywords Keywords for the movie
10 popularity Popularity of the movie
11 kmdb_vote Average vote for the movie
12 budget Budget of the movie (in USD)
13 revenue Gross revenue of the movie (in USD)

# select and re-order the variables

movies_data1 <- movies_data1[, c(12, 21, 10, 26, 28)]

movies_data2 <- movies_data2[, c(18, 17, 12, 6, 5, 9, 19, 1, 13)]

# rename the selected column names

movies_data1 <- rename(movies_data1, title = movie_title, fb_likes = movie_facebook_likes)

movies_data2 <- rename(movies_data2, language = original_language, tmdb_score = vote_average)

# join both of the above datasets
movies <- inner_join(movies_data2, movies_data1, by = "title")

# check the dimensions
dim(movies)
[1]  0 13
# preview the dataset
head(movies)
NA


Why there are 0 rows? Maybe, data is untidy. It requires a fix!

The merged dataset has 0 rows or observations, but the other two datasets have thousands of rows.

So, what’s the reason of this error? The first data frame movies_data from the dataset imdb_5000_movies.csv has data errors. Its title column has some unwanted characters, which was very difficult to find (debug) since R shows it as a blank space, unfortunately!

I found and solved the problem using the below steps:

  1. Print the first title from both the datasets.
  2. movies_data1$title shows a space at last. It shouldn’t be since read_csv() trims spaces.
  3. Nevertheless, replace the space with nothing using str_replace_all on movies_data1$title.

  4. It didn’t work as well - no rows in the merged dataset yet.

  5. So, let’s print the first title as UTF-8 using utf8ToInt().
  6. It shows that the space-looking character has an int value = 160.
  7. However, the space (" ") has an int value = 32, so it’s not a space.
  8. Finally, replace that character with nothing using str_replace_all on movies_data1$title.

  9. And at last, it worked! After merging the two datasets now, the merged dataset shows rows.


# let's print title from both datasets

movies_data1$title[1]
[1] "Avatar "
movies_data2$title[1]
[1] "Avatar"
# movies_data1$title has a space at last, so let's fix it
movies_data1$title <- str_replace_all(movies_data1$title, " $", "")

# join both of the above datasets
movies <- inner_join(movies_data2, movies_data1, by = "title")

# check the dimensions
dim(movies)
[1]  0 13
# preview the dataset
head(movies)

# it doesn't work - still, so is it not a space?

# let's print it in UTF-8 to understand the issue
utf8ToInt(movies_data1$title[1])
[1]  65 118  97 116  97 114 160
# it's not a space, since space != 160
utf8ToInt(" ")
[1] 32
# so, let's fix by replacing it
movies_data1$title <- str_replace_all(movies_data1$title, intToUtf8(160), "")

# join both of the above datasets
movies <- inner_join(movies_data2, movies_data1, by = "title")

# remove duplicate entries
movies <- distinct(movies)

# check the dimensions
dim(movies)
[1] 4602   13
# preview the dataset
head(movies)
NA

Understand

Summarise the types of variables and data structures, check the attributes in the data and apply proper data type conversions. In addition to the R codes and outputs, explain briefly the steps that you have taken. In this section, show that you have fulfilled minimum requirements 2-4.

Let’s understand the data - the data frame movies in this section.

  1. We’ll start with dim() to understand its dimensions.
  2. Then, we’ll check its structure using str().
  3. Lastly, we’ll check the labels for two variables - language and country.

As visualized below, movies has 4602 observations (rows) and 13 variables (columns). Also, it has different types of data including character (chr), numeric (num), and date (Date), as it’s shown below in the output of the str() function. Then, we’ll do these:

  1. We’ll add factors (with labels) to language since its data is two-letter country code.
  2. We’ll add factors (without labels) to country since its data is already labelled.
  3. Finally, we’ll check the levels and the structure of the movies to confirm the changes.

# check the dimensions
dim(movies)
[1] 4602   13
# check the structure
str(movies)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   4602 obs. of  13 variables:
 $ title       : chr  "Avatar" "Pirates of the Caribbean: At World's End" "Spectre" "The Dark Knight Rises" ...
 $ tagline     : chr  "Enter the World of Pandora." "At the end of the world, the adventure begins." "A Plan No One Escapes" "The Legend Ends" ...
 $ release_date: Date, format: "2009-12-10" "2007-05-19" "2015-10-26" ...
 $ language    : chr  "en" "en" "en" "en" ...
 $ keywords    : chr  "[{\"id\": 1463, \"name\": \"culture clash\"}, {\"id\": 2964, \"name\": \"future\"}, {\"id\": 3386, \"name\": \""| __truncated__ "[{\"id\": 270, \"name\": \"ocean\"}, {\"id\": 726, \"name\": \"drug abuse\"}, {\"id\": 911, \"name\": \"exotic "| __truncated__ "[{\"id\": 470, \"name\": \"spy\"}, {\"id\": 818, \"name\": \"based on novel\"}, {\"id\": 4289, \"name\": \"secr"| __truncated__ "[{\"id\": 849, \"name\": \"dc comics\"}, {\"id\": 853, \"name\": \"crime fighter\"}, {\"id\": 949, \"name\": \""| __truncated__ ...
 $ popularity  : num  150.4 139.1 107.4 112.3 43.9 ...
 $ tmdb_score  : num  7.2 6.9 6.3 7.6 6.1 5.9 7.4 7.3 7.4 5.7 ...
 $ budget      : num  2.37e+08 3.00e+08 2.45e+08 2.50e+08 2.60e+08 2.58e+08 2.60e+08 2.80e+08 2.50e+08 2.50e+08 ...
 $ revenue     : num  2.79e+09 9.61e+08 8.81e+08 1.08e+09 2.84e+08 ...
 $ country     : chr  "USA" "USA" "UK" "USA" ...
 $ genres      : chr  "Action|Adventure|Fantasy|Sci-Fi" "Action|Adventure|Fantasy" "Action|Adventure|Thriller" "Action|Thriller" ...
 $ imdb_score  : num  7.9 7.1 6.8 8.5 6.6 6.2 7.8 7.5 7.5 6.9 ...
 $ fb_likes    : num  33000 0 85000 164000 24000 0 29000 118000 10000 197000 ...
# check the levels

levels(movies$language)
NULL
levels(movies$country)
NULL
# add factors to movies$language
movies <- mutate(movies, language = factor(language, 
                                           levels = c("af", "ar", "cn", "cs", "da", "de", "el", "en", "es", "fa", "fr", "he", "hi", "hu", 
                                                      "id", "is", "it", "ja", "ko", "ky", "nb", "nl", "no", "pl", "ps", "pt", "ro", "ru", 
                                                      "sl", "sv", "ta", "te", "th", "tr", "vi", "zh"), 
                                           labels = c("Afrikaans", "Arabic", "Chinese", "Czech", "Danish", "German", "Greek, Modern", 
                                                      "English", "Spanish", "Persian", "French", "Hebrew", "Hindi", "Hungarian", 
                                                      "Indonesian", "Icelandic", "Italian", "Japanese", "Korean", "Kyrgyz", "Norwegian", 
                                                      "Dutch", "Norwegian", "Polish", "Pashto", "Portuguese", "Romanian", "Russian", 
                                                      "Slovenian", "Swedish", "Tamil", "Telugu", "Thai", "Turkish", "Vietnamese", 
                                                      "Chinese")
                                           ))

movies <- mutate(movies, country = factor(country))

# check the structure
str(movies)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   4602 obs. of  13 variables:
 $ title       : chr  "Avatar" "Pirates of the Caribbean: At World's End" "Spectre" "The Dark Knight Rises" ...
 $ tagline     : chr  "Enter the World of Pandora." "At the end of the world, the adventure begins." "A Plan No One Escapes" "The Legend Ends" ...
 $ release_date: Date, format: "2009-12-10" "2007-05-19" "2015-10-26" ...
 $ language    : Factor w/ 34 levels "Afrikaans","Arabic",..: 8 8 8 8 8 8 8 8 8 8 ...
 $ keywords    : chr  "[{\"id\": 1463, \"name\": \"culture clash\"}, {\"id\": 2964, \"name\": \"future\"}, {\"id\": 3386, \"name\": \""| __truncated__ "[{\"id\": 270, \"name\": \"ocean\"}, {\"id\": 726, \"name\": \"drug abuse\"}, {\"id\": 911, \"name\": \"exotic "| __truncated__ "[{\"id\": 470, \"name\": \"spy\"}, {\"id\": 818, \"name\": \"based on novel\"}, {\"id\": 4289, \"name\": \"secr"| __truncated__ "[{\"id\": 849, \"name\": \"dc comics\"}, {\"id\": 853, \"name\": \"crime fighter\"}, {\"id\": 949, \"name\": \""| __truncated__ ...
 $ popularity  : num  150.4 139.1 107.4 112.3 43.9 ...
 $ tmdb_score  : num  7.2 6.9 6.3 7.6 6.1 5.9 7.4 7.3 7.4 5.7 ...
 $ budget      : num  2.37e+08 3.00e+08 2.45e+08 2.50e+08 2.60e+08 2.58e+08 2.60e+08 2.80e+08 2.50e+08 2.50e+08 ...
 $ revenue     : num  2.79e+09 9.61e+08 8.81e+08 1.08e+09 2.84e+08 ...
 $ country     : Factor w/ 61 levels "Afghanistan",..: 60 60 59 60 60 60 60 60 59 60 ...
 $ genres      : chr  "Action|Adventure|Fantasy|Sci-Fi" "Action|Adventure|Fantasy" "Action|Adventure|Thriller" "Action|Thriller" ...
 $ imdb_score  : num  7.9 7.1 6.8 8.5 6.6 6.2 7.8 7.5 7.5 6.9 ...
 $ fb_likes    : num  33000 0 85000 164000 24000 0 29000 118000 10000 197000 ...
# check the levels

levels(movies$language)
 [1] "Afrikaans"     "Arabic"        "Chinese"       "Czech"         "Danish"        "German"       
 [7] "Greek, Modern" "English"       "Spanish"       "Persian"       "French"        "Hebrew"       
[13] "Hindi"         "Hungarian"     "Indonesian"    "Icelandic"     "Italian"       "Japanese"     
[19] "Korean"        "Kyrgyz"        "Norwegian"     "Dutch"         "Polish"        "Pashto"       
[25] "Portuguese"    "Romanian"      "Russian"       "Slovenian"     "Swedish"       "Tamil"        
[31] "Telugu"        "Thai"          "Turkish"       "Vietnamese"   
levels(movies$country)
 [1] "Afghanistan"    "Argentina"      "Aruba"          "Australia"      "Bahamas"       
 [6] "Belgium"        "Brazil"         "Bulgaria"       "Cambodia"       "Cameroon"      
[11] "Canada"         "Chile"          "China"          "Colombia"       "Czech Republic"
[16] "Denmark"        "Egypt"          "Finland"        "France"         "Georgia"       
[21] "Germany"        "Greece"         "Hong Kong"      "Hungary"        "Iceland"       
[26] "India"          "Iran"           "Ireland"        "Israel"         "Italy"         
[31] "Japan"          "Kenya"          "Kyrgyzstan"     "Libya"          "Mexico"        
[36] "Netherlands"    "New Line"       "New Zealand"    "Nigeria"        "Norway"        
[41] "Official site"  "Pakistan"       "Panama"         "Peru"           "Philippines"   
[46] "Poland"         "Romania"        "Russia"         "Slovakia"       "South Africa"  
[51] "South Korea"    "Soviet Union"   "Spain"          "Sweden"         "Switzerland"   
[56] "Taiwan"         "Thailand"       "Turkey"         "UK"             "USA"           
[61] "West Germany"  

Tidy & Manipulate Data I

According to The Comprehensive R Archive Network, "tidy data is a standard way of mapping the meaning of a dataset to its structure. A dataset is messy or tidy depending on how rows, columns and tables are matched up with observations, variables and types. In tidy data:

  1. Each variable forms a column.
  2. Each observation forms a row.
  3. Each type of observational unit forms a table."

That said, the data frame movies is not tidy since the two columns - keywords and genres are not tidy.

Hence, we need to tidy up these two columns. I followed the below steps to tidy up these columns:

keywords

  1. keywords is a column with JSON strings, for example, [{“id”: 1463, “name”: “culture clash”}, {“id”: 2964, “name”: “future”}].
  2. We’ll first convert this JSON into a delimited string, then separate the data into multiple observations (rows) using unnest().
  3. We’ll use fromJSON() to ease the process of parsing or reading JSON objects or strings, else it will become very cumbersome.
  4. The alternative approach could be to use regular expression to extract the name from the above JSON to form a delimited string.
  5. Then, we’ll use unnest() along with strsplit() on the delimited string to separate the delimited values over multiple rows.

genres

  1. genres is a column with multiple delimited values (genres of the movie), for example, Action|Adventure|Fantasy|Sci-Fi.
  2. We’ll use mutate() along with case_when() and grepl() to form a two case formula as given below:
    • If grepl() matches, then TRUE, i.e., genre is found.
    • Else FALSE, i.e., genre is not found.
  3. Using the above approach, we’ll separate the delimited values over multiple columns - one column for each genre.

# first convert JSON to delimited string

simplifyToString <- function(x) {
  fromJSON(x["keywords"])$name %>% paste(collapse = '|')
}

movies <- mutate(movies, keywords = apply(movies, 1, simplifyToString))

# now unnest the `keywords` column into rows
movies <- movies %>% unnest(keywords = strsplit(keywords, "[|]"))

# now unnest the `genres` column into columns

movies <- movies %>% mutate(action = case_when(grepl("Action", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(adventure = case_when(grepl("Adventure", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(animation = case_when(grepl("Animation", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(biography = case_when(grepl("Biography", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(comedy = case_when(grepl("Comedy", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(crime = case_when(grepl("Crime", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(documentary = case_when(grepl("Documentary", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(drama = case_when(grepl("Drama", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(family = case_when(grepl("Family", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(fantasy = case_when(grepl("Fantasy", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(filmnoir = case_when(grepl("Film-Noir", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(history = case_when(grepl("History", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(horror = case_when(grepl("Horror", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(musical = case_when(grepl("Musical", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(mystery = case_when(grepl("Mystery", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(news = case_when(grepl("News", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(romance = case_when(grepl("Romance", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(scifi = case_when(grepl("Sci-Fi", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(short = case_when(grepl("Short", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(sport = case_when(grepl("Sport", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(thriller = case_when(grepl("Thriller", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(war = case_when(grepl("War", genres) ~ TRUE, TRUE ~ FALSE))
movies <- movies %>% mutate(western = case_when(grepl("Western", genres) ~ TRUE, TRUE ~ FALSE))

# finally delete the `genres` column
movies <- subset(movies, select = -c(genres))

# preview the dataset
head(movies)
NA

Tidy & Manipulate Data II

Now, let’s create one variable from the existing variables. We have budget and revenue variables in our dataset. We can calculate the gross profit of each movie using the difference of these two columns. That said, we can create a profit variable by substracting budget from revenue. We’ll use the mutate() function to create this new variable (column).


# find profit for each movie
movies <- movies %>% mutate(profit = revenue - budget)

# preview the dataset
head(movies)
NA

Scan I

Let’s check the data for missin values (NAs), inconsistencies, and obvious errors (like ratings are more than 10 or less than 0). As we’ve visualized below, there are two columns having NAs. Their names and how we’ll deal with them is below:

tagline

  1. tagline has just 3379 NAs, which is not a small number seeing that movies has 35065 observations = 0.096%.
  2. Since its percentage is less than 5% and it’s a character value, so it won’t cause any issues in calculations.
  3. Also, it’s tagline of a movie, which may be unavailable for some movies per its production companies.
  4. That said, we’ll leave the tagline with NAs as it is, since nothing can be done about it.

language

  1. language has just 5 NAs, which is very small number seeing that movies has 35065 observations = 0.00014%.
  2. Since its percentage is less than 5%, we can leave it as it’s a character value, so no issues in calculations.
  3. Or, we can replace the NAs in this column with “English” assuming it’s suggested by the analyst.
  4. Of course, “English” is the most popular language for the movies, as it’s calcualted below.

# scan for infinites
sum(is.infinite(as.matrix(movies)))
[1] 0
# scan for NANs
sum(is.nan(as.matrix(movies)))
[1] 0
# scan for NAs
sum(is.na(movies))
[1] 3384
# scan for NAs per variable
colSums(is.na(movies))
       title      tagline release_date     language     keywords   popularity   tmdb_score 
           0         3379            0            5            0            0            0 
      budget      revenue      country   imdb_score     fb_likes       action    adventure 
           0            0            0            0            0            0            0 
   animation    biography       comedy        crime  documentary        drama       family 
           0            0            0            0            0            0            0 
     fantasy     filmnoir      history       horror      musical      mystery         news 
           0            0            0            0            0            0            0 
     romance        scifi        short        sport     thriller          war      western 
           0            0            0            0            0            0            0 
      profit 
           0 
# check the incomplete cases
head(movies[!complete.cases(movies), ])

# check for obvious errors

rules <- editfile("rules.txt", type = "all")
violated <- violatedEdits(rules, movies)
summary(violated)
No violations detected, 0 checks evaluated to NA
NULL
# count the movies per `language`
movies %>% group_by(language) %>% summarise(no_rows = length(language)) %>% 
  arrange(desc(no_rows)) %>% head()
Factor `language` contains implicit NA, consider using `forcats::fct_explicit_na`
# replace `language` with NA to English
movies$language[is.na(movies$language)] <- "English"

Scan II

We’ll produce boxplots for checking outliers in out dataset. As it’s visualized below, our dataset has probable outliers. So, we calculated the z-scores of the variables and remove the outliered observations (rows) based on the absolute value of more than three.

Of course, it was not necessary to remove the outliers since the outliers are less than 5%. For budget , the outliers count is 698, which is just 0.0199% (which is almost negligible). Also, for revenue , the outliers count is 881, which is just 0.025% (it’s mostly negligible).

Then, after removing the outliers from those variables, we’ll produce the boxplots again. However, we visualized that the outliers are still present. That said, based on our observations, these outliers are not the same case because our dataset is not normally distributed, hence the outliers will be present. The reason being the variables we’re dealing here are budget and revenue, which are not normally distributed in the world. Some movies, like Avengers franchise, are produced with very high budgets, and they earn superb revenues as well. Then, some movies are very low budget movies.

Note: We’re going to do all these scannings on just two variables to keep the document length under 20 pages.


# draw boxplots

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


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


# calculate z scores

zscores.budget <- movies$budget %>%  scores(type = "z")
zscores.budget %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-0.8026 -0.6945 -0.3703  0.0000  0.2782  7.4119 
zscores.revenue <- movies$revenue %>%  scores(type = "z")
zscores.revenue %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-0.5906 -0.5661 -0.3771  0.0000  0.1185 13.2535 
# number of outliers per z scores

length( which(abs(zscores.budget) >3 ))
[1] 698
length( which(abs(zscores.revenue) >3 ))
[1] 881
# exclude outliers

movies.budget <- movies[- which(abs(zscores.budget) >3 ),]

movies.revenue <- movies[- which(abs(zscores.revenue) >3 ),]

# boxplot excluding outliers

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


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

Transform

Let’s produce the histogram of the gross profit (profit variable), which shows that the data is right skewed. So, let’s transpose the profit variable using mutate() to perform a reciprocal transporation for every row (observation). The main idea behind this scheme is to reduce the right skewedness of our distribution and make it a little more normal, which is visible after the transformation.


# transpose the `profit` variable

movies.transpose <- 
  movies %>% mutate(profit = movies$profit^(-1))

# draw histograms for `profit` and transposed `profit`

hist(movies$profit,
     main = "Histogram of Movie's Gross Profit", xlab = "Gross Profit ($)")


hist(movies.transpose$profit,
     main = "Histogram of Transposed Gross Profit", xlab = "Gross Profit Reciprocal")



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