Executive Summary

This assignment demonstrates the key steps in data pro-processing as studied in this subject. Data processing techniques are applied to Movie data from the IMDB data set is used to demonstrate these steps. Firstly, the required packages are loaded into and then the data is read into R as data frames from CSV files based on the local computer. This data is then checked and with data pre processing methods and then selected features across a number tables are merged to apply further pre-processing and reading the data for further analysis. Outliers are identified and addressed using Tukeys univariate methods. Following this, a range of transformations are applied to the selected columns in the new data set to further improve and stabilise the data for further analysis.

Required packages

The following section loads the required packages for this assignment.

# Load the required packages.
library(readr)
library(tidyr)
library(dplyr)
library(Hmisc)
library(outliers)
library(editrules)
library(deductive)
library(validate)
library(forecast)
# Clear any environmnet variables. 
rm(list = ls())

Data

The data sets used in the this assignment is an extract from the IMDB movie data base was sourced from the Relational Data Set Repository website (https://relational.fit.cvut.cz/dataset/IMDb). It consists of a number of tables that form a relational database. As had some issues connecting directly to the database from R (slowness etc) for the purposes of obtaining the data for my assignment, I connected to the source repository with a SQL client (MySQL Workbench) and extracted the tables as CSV files to my local machine and then imported them into R. The files are mentioned in the section below. Whilst the dataset is generally of good quality and the required checks for data quality and consistency was still applied.

Read the data

The following codes reads in the initial dataset from CSV files.

# Read in the IBMDB dataset tables as CSV files to Dataframes
actors_df <- data.frame(read.csv("actors.csv", sep = ';'))
directors_df <- data.frame(read.csv("directors.csv", sep = ';'))
movies_df <- data.frame(read.csv("movies.csv", sep = ';'))
movies2actors_df <- data.frame(read.csv("movies2actors.csv", sep = ';'))
movies2directors_df <- data.frame(read.csv("movies2directors.csv", sep = ';'))
ratings_df <- data.frame(read.csv("ratings.csv", sep = ';'))
runningtimes_df <- data.frame(read.csv("runningtimes.csv", sep = ';'))
movies_genres_df <- data.frame(read.csv("movie_genres.csv", sep = ';'))
# Create a list of dataframes consisting of the tables read to data frames in the above code.
IMDB_Dataframes <- list(actors_df, directors_df, movies_df, movies2actors_df, movies2directors_df, ratings_df,runningtimes_df)

Check the data.

This section performs some rudimentary checks of initial data set to ensure that it they are readable and useable by R. A for loop is used here to be efficient with code and addressing repetitive task with respected to variables that are repeatedly used.

# Loop through data sets and display head obserrvations in each. 
for (data_set in IMDB_Dataframes){
  print(head(data_set))
}

The following section of code checks that each table has been read in as a data frame.

# Check if individual data sets are of class data frame. 
for (data_set_name in IMDB_Dataframes){
  #print("Start of  =========================", data_set_name)
  print(class(data_set_name))
}
[1] "data.frame"
[1] "data.frame"
[1] "data.frame"
[1] "data.frame"
[1] "data.frame"
[1] "data.frame"
[1] "data.frame"

Understand

This section summarises the types of variables and data structures, checks the attributes in the data set and applies data type conversions as required. As I am creating a new, merged data set, only the variables in this new data set are subject type conversion, cleaning etc.

# Loop through data sets and check the structure, indicating the variable names and types. 
for (data_set in IMDB_Dataframes){
  print(str(data_set))
}
'data.frame':   98690 obs. of  3 variables:
 $ actorid: int  4 16 28 566 580 636 769 797 802 808 ...
 $ name   : Factor w/ 98686 levels "'Bear'Boyd, Steven",..: 3 1 2 6 7 9 18 19 20 21 ...
 $ sex    : Factor w/ 2 levels "F","M": 2 2 2 2 2 2 2 2 2 2 ...
NULL
'data.frame':   2201 obs. of  5 variables:
 $ directorid: int  67 92 284 708 746 836 947 1151 1756 1836 ...
 $ name      : Factor w/ 2201 levels "Aalmuhammed, Jefri",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ rate      : num  7 4.3 6.8 6.8 26.2 3 6 6.1 4.1 5 ...
 $ gross     : num  4.30e+04 1.28e+07 0.00 3.30e+04 2.13e+08 ...
 $ num       : int  1 1 1 1 4 1 1 1 1 1 ...
NULL
'data.frame':   3832 obs. of  3 variables:
 $ movieid: int  1672052 1672111 1672580 1672716 1672946 1673647 1673658 1673848 1674388 1674737 ...
 $ title  : Factor w/ 3832 levels "¡Three Amigos! (1986)",..: 3 4 6 5 2 7 8 9 10 11 ...
 $ year   : int  1980 1988 1997 1986 2014 1961 1996 1999 1996 1957 ...
NULL
'data.frame':   138349 obs. of  4 variables:
 $ movieid     : int  1672052 1672052 1672052 1672052 1672052 1672052 1672052 1672052 1672052 1672052 ...
 $ actorid     : int  88796 88848 121878 134348 198682 254523 291369 344412 417547 501765 ...
 $ as_character: Factor w/ 123599 levels "","(also archive footage)  [Babe' Hanson]  <5>",..: 36628 34625 73808 70087 73796 73114 25299 7897 29587 73710 ...
 $ leading     : int  20 7 21 41 4 13 22 23 6 11 ...
NULL
'data.frame':   4141 obs. of  3 variables:
 $ movieid   : int  1672052 1672111 1672580 1672716 1672946 1673647 1673658 1673848 1674388 1674737 ...
 $ directorid: int  22397 54934 297253 188926 188940 302682 155385 133605 115990 164962 ...
 $ genre     : Factor w/ 24 levels "","Action","Adult",..: 10 2 7 10 2 10 7 7 4 10 ...
NULL
'data.frame':   3773 obs. of  4 variables:
 $ movieid     : int  1672052 1672111 1672580 1672716 1673647 1673658 1673848 1674388 1674737 1677011 ...
 $ rank        : num  7.8 5.3 4.4 7 6.5 3.9 7 5.5 8.9 6.4 ...
 $ votes       : int  8111 32183 1894 1255 128 20 137748 47380 245366 12576 ...
 $ distribution: Factor w/ 369 levels "","....211..5",..: 100 222 230 97 171 349 136 222 85 151 ...
NULL
'data.frame':   5086 obs. of  4 variables:
 $ movieid : int  1672052 1672111 1672580 1672716 1673647 1673647 1673658 1673848 1674388 1674737 ...
 $ time    : Factor w/ 836 levels "(None):111","(None):236",..: 12 13 18 177 47 496 179 178 8 177 ...
 $ addition: Factor w/ 219 levels "","(10th Anniversary edition)",..: 1 1 1 1 1 89 1 1 1 1 ...
 $ time1   : int  107 108 113 96 140 125 98 97 103 96 ...
NULL

Create new merged data set

The IMDB custom data set (IMDB_Custom) is created from merging a number of data frames, collating the relevant information for this assignment. The common variable between these merged dataset is column movieid. This operation is a right join and is not prone to resulting in missing values and preserves existing information. The data frames that are amalgamated for my analysis is movies2driectors, movies, ratings and running times. Once this final set was constructed, unwanted variables and duplicates were removed to form the final data set IMDB_Custom for pre-processing.

The following code renames the movie_id variable to be consistent with the other tables to support merger and analysis with other tables.

## use dplyer to align the movie id variable name to support merging with other tables. 
#Renaming columns with dplyr::rename()
movies_genres_df <- movies_genres_df %>%  rename(movieid = movie_id)

The following code mergers a number movies_genres_df and movies_df to the movies data frame.

# Join movies and directors
moviesNdirectors <- merge(movies2directors_df, movies_df,by="movieid")
# Now add more data that will be useful for dataset.
moviesNdirectorsNratings <- merge(moviesNdirectors, ratings_df,by="movieid")
# add data from running times
moviesNdirectorsNratingsNTimes <- merge(moviesNdirectorsNratings, runningtimes_df,by="movieid")
# Rename to a customised table.
IMDB_Custom <-moviesNdirectorsNratingsNTimes
# Remove unwanted columns. 
IMDB_Custom$time <- NULL
IMDB_Custom$directorid <- NULL
IMDB_Custom$addition <- NULL
# Inspect the final data set. 
head(IMDB_Custom)

Inspect and Understand

This section inspects the data frame and variables using R functions. The following code chunk checks the data types (i.e., character, numeric, integer, factor, and logical) of the variables in the data set. The str() function is used which provides the detailed structure within the data set. It also provides details with regards to the number of variables and observations. Data types are also changed to support the pre-processing and analysis.

# Check the dimensions fo the DF
dim(IMDB_Custom)
[1] 5544    8
# list the column names and change where appropriate.
str(IMDB_Custom)
'data.frame':   5544 obs. of  8 variables:
 $ movieid     : int  1672052 1672111 1672580 1672716 1673647 1673647 1673658 1673848 1674388 1674737 ...
 $ genre       : Factor w/ 24 levels "","Action","Adult",..: 10 2 7 10 10 10 7 7 4 10 ...
 $ title       : Factor w/ 3832 levels "¡Three Amigos! (1986)",..: 3 4 6 5 7 7 8 9 10 11 ...
 $ year        : int  1980 1988 1997 1986 1961 1961 1996 1999 1996 1957 ...
 $ rank        : num  7.8 5.3 4.4 7 6.5 6.5 3.9 7 5.5 8.9 ...
 $ votes       : int  8111 32183 1894 1255 128 128 20 137748 47380 245366 ...
 $ distribution: Factor w/ 369 levels "","....211..5",..: 100 222 230 97 171 171 349 136 222 85 ...
 $ time1       : int  107 108 113 96 140 125 98 97 103 96 ...

The following section of code isnspect and Correct varibales

#Rename columns with dplyr::rename() to provide some context to the dataset. 
# Rename the variable names to meaningful ones
# rank to movie_ranking
IMDB_Custom<- IMDB_Custom%>%rename(movie_ranking=rank)
# votes to movie_votes
IMDB_Custom<- IMDB_Custom%>%rename(movie_votes=votes)
# name to time1 to running time. 
IMDB_Custom<- IMDB_Custom%>%rename(running_time=time1)
# rate to director_rating
# The following variable types were corrected and changed to support the analysis. 
# Chnage the rank variable from nuemrical to ordinal
IMDB_Custom$movie_ranking <- as.numeric(IMDB_Custom$movie_ranking)
# Change title to string
IMDB_Custom$title <-as.character(IMDB_Custom$title)
# Change distributio from factor to ordinal
IMDB_Custom$distribution<-as.ordered(IMDB_Custom$distribution)

Recheck the structure and confirm the changes have been applied.

str(IMDB_Custom)
'data.frame':   5544 obs. of  8 variables:
 $ movieid      : int  1672052 1672111 1672580 1672716 1673647 1673647 1673658 1673848 1674388 1674737 ...
 $ genre        : Factor w/ 24 levels "","Action","Adult",..: 10 2 7 10 10 10 7 7 4 10 ...
 $ title        : chr  "'Breaker' Morant (1980)" "'Crocodile' Dundee II (1988)" "'Til There Was You (1997)" "'night, Mother (1986)" ...
 $ year         : int  1980 1988 1997 1986 1961 1961 1996 1999 1996 1957 ...
 $ movie_ranking: num  7.8 5.3 4.4 7 6.5 6.5 3.9 7 5.5 8.9 ...
 $ movie_votes  : int  8111 32183 1894 1255 128 128 20 137748 47380 245366 ...
 $ distribution : Ord.factor w/ 352 levels ""<"....211..5"<..: 90 211 219 87 160 160 332 126 211 75 ...
 $ running_time : int  107 108 113 96 140 125 98 97 103 96 ...

As the initial mergers may have resulted in some duplicate values, remove these values and finalise the data set.

# Finally remove any duplicate rows
IMDB_Custom <- distinct(IMDB_Custom)
dim((IMDB_Custom))
[1] 4916    8

Tidy & Manipulate Data I

The data set I have chosen and the merger of relevant tables resulting in the final table, IMDB_Custom already conforms the tidy data principle and no reshaping of my data into a tidy format is required.

Tidy & Manipulate Data II

The following code uses the mutate function Create/mutate the running time variable to add the column running_time_seconds.

# Create a new variable for running time in seconds b
IMDB_Custom <- IMDB_Custom %>% mutate(running_time_seconds = running_time*60)
head(IMDB_Custom)

Scan I

The following sections of code scan the data for missing values (NaNs and NAs) and any inconsistencies and obvious errors.

# 
sum(is.na(IMDB_Custom$movieid))
[1] 0
sum(is.na(IMDB_Custom$genre))
[1] 0
sum(is.na(IMDB_Custom$title))
[1] 0
sum(is.na(IMDB_Custom$year))
[1] 0
sum(is.na(IMDB_Custom$movie_ranking))
[1] 0
sum(is.na(IMDB_Custom$movie_votes))
[1] 0
sum(is.na(IMDB_Custom$distribution))      
[1] 0
sum(is.na(IMDB_Custom$running_time))
[1] 0
sum(is.nan(IMDB_Custom$movieid))
[1] 0
sum(is.nan(IMDB_Custom$genre))
[1] 0
sum(is.nan(IMDB_Custom$title))
[1] 0
sum(is.nan(IMDB_Custom$year))
[1] 0
sum(is.nan(IMDB_Custom$movie_ranking))
[1] 0
sum(is.nan(IMDB_Custom$movie_votes))
[1] 0
sum(is.nan(IMDB_Custom$distribution))
[1] 0
sum(is.nan(IMDB_Custom$running_time))
[1] 0

Scan II

The following sections of code scan and address outliers and uses Tukey’s univariate method of outlier detection is used to detect outliers in the box plots.

Display Box Plots - Pre Outlier resolution

First a box plot is created to provide any visualisation of outliers. As seen in the boxplots below there are a number of outliers for every variable and this may be addressed and improved with Tukeys method.

# create a collection of boxplots of the selected variables showing the outliers. 
par(mfrow=c(2,2))
boxplot(IMDB_Custom$year, main="\nMovie Year", ylab="Year", col="blue", pars=list(outcol="red"))
boxplot(IMDB_Custom$running_time, main="\nRunning Time", ylab="Minutes", col="blue", pars=list(outcol="red"))
boxplot(IMDB_Custom$movie_votes, main="Movie Running Votes", ylab="Minutes", col="blue", pars=list(outcol="red"))
boxplot(IMDB_Custom$running_time, main="Movie Running Ranking", ylab="Minutes", col="blue", pars=list(outcol="red"))
mtext("Box Plots for Variables pre Transformation", side = 3, line = -1,outer = TRUE)

Check outliers using z-score and respective adjustments

Use Tukeys method for each of the variables to detect and address the outliers. ### Movie Year Variable

# Check for outliers using the z-score
z.scores <-  IMDB_Custom$year %>% scores(type = "z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-4.0320 -0.2399  0.4644  0.0000  0.6811  1.3853 

There are 86 values that can be considered outliers

# Check the number of values that fall outside the range. .
length (which( abs(z.scores) >3 ))
[1] 86
# Replace the outliers with median values
IMDB_Custom$year[which( abs(z.scores) >3 )] <- median(IMDB_Custom$year)

The values outside the range have now been reduced to 74

# Recheck the z scores after the imputations to confirm the outlier have been addressed. 
z.scores <-  IMDB_Custom$year %>% scores(type = "z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-3.3330 -0.2768  0.4423  0.0000  0.6820  1.4610 
length (which( abs(z.scores) >3 ))
[1] 74

Movie Running Time Variable

# Check for outliers using the z-score
z.scores <-  IMDB_Custom$running_time %>% scores(type = "z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-4.1359 -0.5594 -0.1530  0.0000  0.3753  9.1132 

There are 109 values that can be considered outliers.

# Check the number of values that fall outside the range. There are 109 values that can be considered outliers. 
length (which( abs(z.scores) >3 ))
[1] 109
# Replace the outliers with median values
IMDB_Custom$running_time[which( abs(z.scores) >3 )] <- median(IMDB_Custom$running_time)

The values outside the range have now been reduced to 71.

# Recheck the z scores after the imputations to confirm the outlier have been addressed. The values outside the range have now been reduced to 71.
z.scores <-  IMDB_Custom$running_time %>% scores(type = "z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-3.4406 -0.6590 -0.1241  0.0000  0.5178  3.9947 
length (which( abs(z.scores) >3 ))
[1] 71

Movie Votes Variable

# Check for outliers using the z-score
z.scores <-  IMDB_Custom$movie_votes %>% scores(type = "z")
z.scores %>% summary()
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.44796 -0.41869 -0.33531  0.00000 -0.03218 14.98511 

There are 99 values that can be considered outliers.

# Check the number of values that fall outside the range. There are 99 values that can be considered outliers.
length (which( abs(z.scores) >3 ))
[1] 99
# Replace the outliers with median values
IMDB_Custom$movie_votes[which( abs(z.scores) >3 )] <- median(IMDB_Custom$movie_votes)

The values outside the range have now been reduced to 152

# Recheck the z scores after the imputations to confirm the outlier have been addressed. There are 86 values that can be considered outliers. The values outside the range have now been reduced to 152
z.scores <-  IMDB_Custom$movie_votes %>% scores(type = "z")
z.scores %>% summary()
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-0.61902 -0.56426 -0.40830  0.00000  0.07881  5.78189 
length (which( abs(z.scores) >3 ))
[1] 152

Movie ranking

# Check for outliers using the z-score
z.scores <-  IMDB_Custom$movie_ranking %>% scores(type = "z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-4.0239 -0.5589  0.1340  0.0000  0.7404  2.4729 

There are 40 values that can be considered outliers.

# Check the number of values that fall outside the range. There are 40 values that can be considered outliers. 
length (which( abs(z.scores) >3 ))
[1] 40
# Replace the outliers with median values
IMDB_Custom$movie_ranking[which( abs(z.scores) >3 )] <- median(IMDB_Custom$movie_ranking)

The values outside the range have now been reduced to 13.

# Recheck the z scores after the imputations to confirm the outlier have been addressed. The values outside the range have now been reduced to 13.
z.scores <-  IMDB_Custom$movie_ranking %>% scores(type = "z")
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-3.1610 -0.6161  0.1110  0.0000  0.7473  2.5650 
length (which( abs(z.scores) >3 ))
[1] 13
Display Box Plots - Post Oulier resolution

The section below displays the variables after Tukeys univariate outlier detection and resolution methods have been applied. As can be seen there are considerable improvements from the initial set of plots.

par(mfrow=c(2,2))
boxplot(IMDB_Custom$year, main="\nMovie Year", ylab="Year", col="blue", pars=list(outcol="red"))
boxplot(IMDB_Custom$running_time, main="\nRunning Time", ylab="Minutes", col="blue", pars=list(outcol="red"))
boxplot(IMDB_Custom$movie_votes, main="Movie Running Votes", ylab="Minutes", col="blue", pars=list(outcol="red"))
boxplot(IMDB_Custom$running_time, main="Movie Running Ranking", ylab="Minutes", col="blue", pars=list(outcol="red"))
# mtext("My 'Collection of Box Plots for Variables", side = 3, line = -21, outer = TRUE)
mtext("Box Plots for Variables Post Outlier Resolution", side = 3, line = -1,outer = TRUE)

Transform

This section applies transformation to the selected variables by visualy analysing hte skew of the distribution and applying a relevant transoframtion. Please note I have applied a range of transformation to view the effefcts but have specified wich ones are relevant to based on the skew. The plots below display respective histograms of the variables of interest.

# Plot histograms on untransformed variables. 
par(mfrow=c(2,2))
hist(IMDB_Custom$year,main="Movie Year", xlab="Year", col="blue")
hist(IMDB_Custom$running_time,main="\nMovie Running Time", xlab="Running Time", col="blue")
hist(IMDB_Custom$movie_votes, main="\nMovie Votes",xlab="Movie Votes", col="blue")
hist(IMDB_Custom$movie_ranking,main="Movie Ranking",xlab="Movie Ranking", col="blue")
mtext("Histograms for Variables Pre Transformation", side = 3, line = -1,outer = TRUE)

Movie Year

Data for movie year time is strongley skewed to the left. Whilst we apply all transforamion to se the effect the most applicable transformation to left skewed data is square root and reciprocal transformation, other transformations including Box Cox with a lambda value of auto is applied. In this case the transformations had little effect in producing a symmetrical distribution.

# Movie Year Transformations - Left Skewed
# Square Transforamton
year_sq1_trn <- IMDB_Custom$year^2
# Box Cox
year_BC_trn <- BoxCox(IMDB_Custom$year, lambda = "auto")
#log transformation
year_log_trn <- log10(IMDB_Custom$year)
year_ln_trn <- log(IMDB_Custom$year)
# Reciprocal Transformation
year_recip_trn <- 1/IMDB_Custom$year
# Plot histograms on untransformed variables. 
par(mfrow=c(1,5))
hist(year_sq1_trn,main="\nSquare Root", xlab="Year", col="blue")
hist(year_BC_trn,main="\nBox Cox", xlab="Year", col="blue")
hist(year_log_trn,main="\nLog 10", xlab="Year", col="blue")
hist(year_ln_trn,main="\nNatural Log", xlab="Year", col="blue")
hist(year_recip_trn,main="\nReciprocal Log", xlab="Year", col="blue")
mtext("Histograms for Year Variables Post Transformation", side = 3, line = -1,outer = TRUE)

Movie Running Time

Data for movie running times apper to be reasonably symmetrical, lets see if it can be improved. Again, all transforamion are applied to see the effect. Again none of the transformations applied had any strong impact on improving the distrubution.

# Movie Year Transformations - Left Skewed
# Square Transforamton
running_time_sq1_trn <- IMDB_Custom$running_time^2
# Box Cox
running_time_BC_trn <- BoxCox(IMDB_Custom$running_time, lambda = "auto")
#log transformation
running_time_log_trn <- log10(IMDB_Custom$running_time)
running_time_ln_trn <- log(IMDB_Custom$running_time)
# Reciprocal Transformation
running_time_recip_trn <- 1/IMDB_Custom$running_time
# Plot histograms on untransformed variables. 
par(mfrow=c(1,5))
hist(running_time_sq1_trn,main="\nSquare Root", xlab="Running Time", col="blue")
hist(running_time_BC_trn,main="\nBox Cox", xlab="Running Time", col="blue")
hist(running_time_log_trn,main="\nLog 10", xlab="Running Time", col="blue")
hist(running_time_ln_trn,main="\nNatural Log", xlab="Running Time", col="blue")
hist(running_time_recip_trn,main="\nReciprocal", xlab="Running Time", col="blue")
mtext("Histograms for Running Times Variables Post Transformation", side = 3, line = -1,outer = TRUE)

Movie Votes

Data for movie votes is very strongly skewed to the right. Whilst I have applied all transformation to see the effect the most applicable transformation to right skewed data is log, natural log and square root transformation. Here the both the log transformations applied appear to have a reasonable impact on improving the symmetry of distribution.

# Movie Votes Transformations - Left Skewed
# Square Transforamton
movie_votes_sq1_trn <- IMDB_Custom$movie_votes^2
# Box Cox
movie_votes_BC_trn <- BoxCox(IMDB_Custom$movie_votes, lambda = "auto")
#log transformation
movie_votes_log_trn <- log10(IMDB_Custom$movie_votes)
movie_votes_ln_trn <- log(IMDB_Custom$movie_votes)
# Reciprocal Transformation
movie_votes_recip_trn <- 1/IMDB_Custom$movie_votes
# Plot histograms on untransformed variables. 
par(mfrow=c(1,5))
hist(movie_votes_sq1_trn,main="\nSquare Root", xlab="Movie Votes", col="blue")
hist(movie_votes_BC_trn,main="\nBox Cox", xlab="Movie Votes", col="blue")
hist(movie_votes_log_trn,main="\nLog 10", xlab="Movie Votes", col="blue")
hist(movie_votes_ln_trn,main="\nNatural Log", xlab="Movie Votes", col="blue")
hist(movie_votes_recip_trn,main="\nReciprocal", xlab="Movie Votes", col="blue")
mtext("Histograms for Movie Votess Variables Post Transformation", side = 3, line = -1,outer = TRUE)

Movie Ranking

Data for movie running times apper to be reasonably symmetrical, lets see if it can be improved. Again, all transforamion are applied to see the effect. As per the plots below Square root and Box Cox transformations appear to have a moderate impact on improving the symmetry of the distrubution.

# Movie Votes Transformations - Left Skewed
# Square Transforamton
movie_ranking_sq1_trn <- IMDB_Custom$movie_ranking^2
# Box Cox
movie_ranking_BC_trn <- BoxCox(IMDB_Custom$movie_ranking, lambda = "auto")
#log transformation
movie_ranking_log_trn <- log10(IMDB_Custom$movie_ranking)
movie_ranking_ln_trn <- log(IMDB_Custom$movie_ranking)
# Reciprocal Transformation
movie_ranking_recip_trn <- 1/IMDB_Custom$movie_ranking
# Plot histograms on untransformed variables. 
par(mfrow=c(1,5))
hist(movie_ranking_sq1_trn,main="\nSquare Root", xlab="Movie Votes", col="blue")
hist(movie_ranking_BC_trn,main="\nBox Cox", xlab="Movie Votes", col="blue")
hist(movie_ranking_log_trn,main="\nLog 10", xlab="Movie Votes", col="blue")
hist(movie_ranking_ln_trn,main="\nNatural Log", xlab="Movie Votes", col="blue")
hist(movie_ranking_recip_trn,main="\nReciprocal", xlab="Movie Votes", col="blue")
mtext("Histograms for Movie Votess Variables Post Transformation", side = 3, line = -1,outer = TRUE)

LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMSwgMjAxOTogUHJlLXByb2Nlc3Npbmcgb2YgSU1EQiBEYXRhIgphdXRob3I6ICJLRVZJTiBKT0hOU09OIChJTkRJVklEVUFMIEFTU0lHTk1FTlQpLCBTMzIwNzM0MyIKc3VidGl0bGU6IEFzc2lnbm1lbnQgMwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCgoKCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IAoKVGhpcyBhc3NpZ25tZW50IGRlbW9uc3RyYXRlcyB0aGUga2V5IHN0ZXBzIGluIGRhdGEgcHJvLXByb2Nlc3NpbmcgYXMgc3R1ZGllZCBpbiB0aGlzIHN1YmplY3QuIERhdGEgcHJvY2Vzc2luZyB0ZWNobmlxdWVzIGFyZSBhcHBsaWVkIHRvIE1vdmllIGRhdGEgZnJvbSB0aGUgSU1EQiBkYXRhIHNldCBpcyB1c2VkIHRvIGRlbW9uc3RyYXRlIHRoZXNlIHN0ZXBzLiBGaXJzdGx5LCB0aGUgcmVxdWlyZWQgcGFja2FnZXMgYXJlIGxvYWRlZCBpbnRvIGFuZCB0aGVuIHRoZSBkYXRhIGlzIHJlYWQgaW50byBSIGFzIGRhdGEgZnJhbWVzIGZyb20gQ1NWIGZpbGVzIGJhc2VkIG9uIHRoZSBsb2NhbCBjb21wdXRlci4gVGhpcyBkYXRhIGlzIHRoZW4gY2hlY2tlZCBhbmQgd2l0aCBkYXRhIHByZSBwcm9jZXNzaW5nIG1ldGhvZHMgYW5kIHRoZW4gc2VsZWN0ZWQgZmVhdHVyZXMgYWNyb3NzIGEgbnVtYmVyIHRhYmxlcyBhcmUgbWVyZ2VkIHRvIGFwcGx5IGZ1cnRoZXIgcHJlLXByb2Nlc3NpbmcgYW5kIHJlYWRpbmcgdGhlIGRhdGEgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuIE91dGxpZXJzIGFyZSBpZGVudGlmaWVkIGFuZCBhZGRyZXNzZWQgdXNpbmcgVHVrZXlzIHVuaXZhcmlhdGUgbWV0aG9kcy4gRm9sbG93aW5nIHRoaXMsIGEgcmFuZ2Ugb2YgdHJhbnNmb3JtYXRpb25zIGFyZSBhcHBsaWVkIHRvIHRoZSBzZWxlY3RlZCBjb2x1bW5zIGluIHRoZSBuZXcgZGF0YSBzZXQgdG8gZnVydGhlciBpbXByb3ZlIGFuZCBzdGFiaWxpc2UgdGhlIGRhdGEgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuIAoKIyMgUmVxdWlyZWQgcGFja2FnZXMgCgpUaGUgZm9sbG93aW5nIHNlY3Rpb24gbG9hZHMgdGhlIHJlcXVpcmVkIHBhY2thZ2VzIGZvciB0aGlzIGFzc2lnbm1lbnQuCgpgYGB7cn0KIyBMb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcy4KbGlicmFyeShyZWFkcikKbGlicmFyeSh0aWR5cikKbGlicmFyeShkcGx5cikKbGlicmFyeShIbWlzYykKbGlicmFyeShvdXRsaWVycykKbGlicmFyeShlZGl0cnVsZXMpCmxpYnJhcnkoZGVkdWN0aXZlKQpsaWJyYXJ5KHZhbGlkYXRlKQpsaWJyYXJ5KGZvcmVjYXN0KQpgYGAKCmBgYHtyfQojIENsZWFyIGFueSBlbnZpcm9ubW5ldCB2YXJpYWJsZXMuIApybShsaXN0ID0gbHMoKSkKYGBgCgoKIyMgRGF0YSAKCgpUaGUgZGF0YSBzZXRzIHVzZWQgaW4gdGhlIHRoaXMgYXNzaWdubWVudCBpcyBhbiBleHRyYWN0IGZyb20gdGhlIElNREIgbW92aWUgZGF0YSBiYXNlIHdhcyBzb3VyY2VkIGZyb20gdGhlIFJlbGF0aW9uYWwgRGF0YSBTZXQgUmVwb3NpdG9yeSB3ZWJzaXRlIChodHRwczovL3JlbGF0aW9uYWwuZml0LmN2dXQuY3ovZGF0YXNldC9JTURiKS4gSXQgY29uc2lzdHMgb2YgYSBudW1iZXIgb2YgdGFibGVzIHRoYXQgZm9ybSBhIHJlbGF0aW9uYWwgZGF0YWJhc2UuIEFzIGhhZCBzb21lIGlzc3VlcyBjb25uZWN0aW5nIGRpcmVjdGx5IHRvIHRoZSBkYXRhYmFzZSBmcm9tIFIgKHNsb3duZXNzIGV0YykgZm9yIHRoZSBwdXJwb3NlcyBvZiBvYnRhaW5pbmcgdGhlIGRhdGEgZm9yIG15IGFzc2lnbm1lbnQsIEkgY29ubmVjdGVkIHRvIHRoZSBzb3VyY2UgcmVwb3NpdG9yeSB3aXRoIGEgU1FMIGNsaWVudCAoTXlTUUwgV29ya2JlbmNoKSBhbmQgZXh0cmFjdGVkIHRoZSB0YWJsZXMgYXMgQ1NWIGZpbGVzIHRvIG15IGxvY2FsIG1hY2hpbmUgYW5kIHRoZW4gaW1wb3J0ZWQgdGhlbSBpbnRvIFIuIFRoZSBmaWxlcyBhcmUgbWVudGlvbmVkIGluIHRoZSBzZWN0aW9uIGJlbG93LiBXaGlsc3QgdGhlIGRhdGFzZXQgaXMgZ2VuZXJhbGx5IG9mIGdvb2QgcXVhbGl0eSBhbmQgdGhlIHJlcXVpcmVkIGNoZWNrcyBmb3IgZGF0YSBxdWFsaXR5IGFuZCBjb25zaXN0ZW5jeSB3YXMgc3RpbGwgYXBwbGllZC4gCgojIyMgUmVhZCB0aGUgZGF0YQpUaGUgZm9sbG93aW5nIGNvZGVzIHJlYWRzIGluIHRoZSBpbml0aWFsIGRhdGFzZXQgZnJvbSBDU1YgZmlsZXMuCmBgYHtyfQojIFJlYWQgaW4gdGhlIElCTURCIGRhdGFzZXQgdGFibGVzIGFzIENTViBmaWxlcyB0byBEYXRhZnJhbWVzCmFjdG9yc19kZiA8LSBkYXRhLmZyYW1lKHJlYWQuY3N2KCJhY3RvcnMuY3N2Iiwgc2VwID0gJzsnKSkKZGlyZWN0b3JzX2RmIDwtIGRhdGEuZnJhbWUocmVhZC5jc3YoImRpcmVjdG9ycy5jc3YiLCBzZXAgPSAnOycpKQptb3ZpZXNfZGYgPC0gZGF0YS5mcmFtZShyZWFkLmNzdigibW92aWVzLmNzdiIsIHNlcCA9ICc7JykpCm1vdmllczJhY3RvcnNfZGYgPC0gZGF0YS5mcmFtZShyZWFkLmNzdigibW92aWVzMmFjdG9ycy5jc3YiLCBzZXAgPSAnOycpKQptb3ZpZXMyZGlyZWN0b3JzX2RmIDwtIGRhdGEuZnJhbWUocmVhZC5jc3YoIm1vdmllczJkaXJlY3RvcnMuY3N2Iiwgc2VwID0gJzsnKSkKcmF0aW5nc19kZiA8LSBkYXRhLmZyYW1lKHJlYWQuY3N2KCJyYXRpbmdzLmNzdiIsIHNlcCA9ICc7JykpCnJ1bm5pbmd0aW1lc19kZiA8LSBkYXRhLmZyYW1lKHJlYWQuY3N2KCJydW5uaW5ndGltZXMuY3N2Iiwgc2VwID0gJzsnKSkKbW92aWVzX2dlbnJlc19kZiA8LSBkYXRhLmZyYW1lKHJlYWQuY3N2KCJtb3ZpZV9nZW5yZXMuY3N2Iiwgc2VwID0gJzsnKSkKYGBgCgpgYGB7cn0KIyBDcmVhdGUgYSBsaXN0IG9mIGRhdGFmcmFtZXMgY29uc2lzdGluZyBvZiB0aGUgdGFibGVzIHJlYWQgdG8gZGF0YSBmcmFtZXMgaW4gdGhlIGFib3ZlIGNvZGUuCklNREJfRGF0YWZyYW1lcyA8LSBsaXN0KGFjdG9yc19kZiwgZGlyZWN0b3JzX2RmLCBtb3ZpZXNfZGYsIG1vdmllczJhY3RvcnNfZGYsIG1vdmllczJkaXJlY3RvcnNfZGYsIHJhdGluZ3NfZGYscnVubmluZ3RpbWVzX2RmKQpgYGAKCiMjIyBDaGVjayB0aGUgZGF0YS4gClRoaXMgc2VjdGlvbiBwZXJmb3JtcyBzb21lIHJ1ZGltZW50YXJ5IGNoZWNrcyBvZiBpbml0aWFsIGRhdGEgc2V0IHRvIGVuc3VyZSB0aGF0IGl0IHRoZXkgYXJlIHJlYWRhYmxlIGFuZCB1c2VhYmxlIGJ5IFIuIEEgZm9yIGxvb3AgaXMgdXNlZCBoZXJlIHRvIGJlIGVmZmljaWVudCB3aXRoIGNvZGUgYW5kIGFkZHJlc3NpbmcgcmVwZXRpdGl2ZSB0YXNrIHdpdGggcmVzcGVjdGVkIHRvIHZhcmlhYmxlcyB0aGF0IGFyZSByZXBlYXRlZGx5IHVzZWQuCgpgYGB7cn0KIyBMb29wIHRocm91Z2ggZGF0YSBzZXRzIGFuZCBkaXNwbGF5IGhlYWQgb2JzZXJydmF0aW9ucyBpbiBlYWNoLiAKZm9yIChkYXRhX3NldCBpbiBJTURCX0RhdGFmcmFtZXMpewogIHByaW50KGhlYWQoZGF0YV9zZXQpKQp9CmBgYAoKVGhlIGZvbGxvd2luZyBzZWN0aW9uIG9mIGNvZGUgY2hlY2tzIHRoYXQgZWFjaCB0YWJsZSBoYXMgYmVlbiByZWFkIGluIGFzIGEgZGF0YSBmcmFtZS4gCmBgYHtyfQojIENoZWNrIGlmIGluZGl2aWR1YWwgZGF0YSBzZXRzIGFyZSBvZiBjbGFzcyBkYXRhIGZyYW1lLiAKZm9yIChkYXRhX3NldF9uYW1lIGluIElNREJfRGF0YWZyYW1lcyl7CiAgI3ByaW50KCJTdGFydCBvZiAgPT09PT09PT09PT09PT09PT09PT09PT09PSIsIGRhdGFfc2V0X25hbWUpCiAgcHJpbnQoY2xhc3MoZGF0YV9zZXRfbmFtZSkpCn0KYGBgCgojIyBVbmRlcnN0YW5kIApUaGlzIHNlY3Rpb24gc3VtbWFyaXNlcyB0aGUgdHlwZXMgb2YgdmFyaWFibGVzIGFuZCBkYXRhIHN0cnVjdHVyZXMsIGNoZWNrcyB0aGUgYXR0cmlidXRlcyBpbiB0aGUgZGF0YSBzZXQgYW5kIGFwcGxpZXMgZGF0YSB0eXBlIGNvbnZlcnNpb25zIGFzIHJlcXVpcmVkLiBBcyBJIGFtIGNyZWF0aW5nIGEgbmV3LCBtZXJnZWQgZGF0YSBzZXQsIG9ubHkgdGhlIHZhcmlhYmxlcyBpbiB0aGlzIG5ldyBkYXRhIHNldCBhcmUgc3ViamVjdCB0eXBlIGNvbnZlcnNpb24sIGNsZWFuaW5nIGV0Yy4gCgpgYGB7cn0KIyBMb29wIHRocm91Z2ggZGF0YSBzZXRzIGFuZCBjaGVjayB0aGUgc3RydWN0dXJlLCBpbmRpY2F0aW5nIHRoZSB2YXJpYWJsZSBuYW1lcyBhbmQgdHlwZXMuIApmb3IgKGRhdGFfc2V0IGluIElNREJfRGF0YWZyYW1lcyl7CiAgcHJpbnQoc3RyKGRhdGFfc2V0KSkKfQpgYGAKIyMjIENyZWF0ZSBuZXcgbWVyZ2VkIGRhdGEgc2V0ClRoZSBJTURCIGN1c3RvbSBkYXRhIHNldCAgKElNREJfQ3VzdG9tKSBpcyBjcmVhdGVkIGZyb20gbWVyZ2luZyBhIG51bWJlciBvZiBkYXRhIGZyYW1lcywgY29sbGF0aW5nIHRoZSByZWxldmFudCBpbmZvcm1hdGlvbiBmb3IgdGhpcyBhc3NpZ25tZW50LiBUaGUgY29tbW9uIHZhcmlhYmxlIGJldHdlZW4gdGhlc2UgbWVyZ2VkIGRhdGFzZXQgaXMgY29sdW1uIG1vdmllaWQuIFRoaXMgb3BlcmF0aW9uIGlzIGEgcmlnaHQgam9pbiBhbmQgaXMgbm90IHByb25lIHRvIHJlc3VsdGluZyBpbiBtaXNzaW5nIHZhbHVlcyBhbmQgcHJlc2VydmVzIGV4aXN0aW5nIGluZm9ybWF0aW9uLiBUaGUgZGF0YSBmcmFtZXMgdGhhdCBhcmUgYW1hbGdhbWF0ZWQgZm9yIG15IGFuYWx5c2lzIGlzIG1vdmllczJkcmllY3RvcnMsIG1vdmllcywgcmF0aW5ncyBhbmQgcnVubmluZyB0aW1lcy4gT25jZSB0aGlzIGZpbmFsIHNldCB3YXMgY29uc3RydWN0ZWQsIHVud2FudGVkIHZhcmlhYmxlcyBhbmQgZHVwbGljYXRlcyB3ZXJlIHJlbW92ZWQgdG8gZm9ybSB0aGUgZmluYWwgZGF0YSBzZXQgSU1EQl9DdXN0b20gZm9yIHByZS1wcm9jZXNzaW5nLgoKVGhlIGZvbGxvd2luZyBjb2RlIHJlbmFtZXMgdGhlIG1vdmllX2lkIHZhcmlhYmxlIHRvIGJlIGNvbnNpc3RlbnQgd2l0aCB0aGUgb3RoZXIgdGFibGVzIHRvIHN1cHBvcnQgbWVyZ2VyIGFuZCBhbmFseXNpcyB3aXRoIG90aGVyIHRhYmxlcy4KYGBge3J9CiMjIHVzZSBkcGx5ZXIgdG8gYWxpZ24gdGhlIG1vdmllIGlkIHZhcmlhYmxlIG5hbWUgdG8gc3VwcG9ydCBtZXJnaW5nIHdpdGggb3RoZXIgdGFibGVzLiAKI1JlbmFtaW5nIGNvbHVtbnMgd2l0aCBkcGx5cjo6cmVuYW1lKCkKbW92aWVzX2dlbnJlc19kZiA8LSBtb3ZpZXNfZ2VucmVzX2RmICU+JSAgcmVuYW1lKG1vdmllaWQgPSBtb3ZpZV9pZCkKYGBgCgpUaGUgZm9sbG93aW5nIGNvZGUgbWVyZ2VycyBhIG51bWJlciBtb3ZpZXNfZ2VucmVzX2RmIGFuZCBtb3ZpZXNfZGYgdG8gdGhlIG1vdmllcyBkYXRhIGZyYW1lLgpgYGB7cn0KIyBKb2luIG1vdmllcyBhbmQgZGlyZWN0b3JzCm1vdmllc05kaXJlY3RvcnMgPC0gbWVyZ2UobW92aWVzMmRpcmVjdG9yc19kZiwgbW92aWVzX2RmLGJ5PSJtb3ZpZWlkIikKIyBOb3cgYWRkIG1vcmUgZGF0YSB0aGF0IHdpbGwgYmUgdXNlZnVsIGZvciBkYXRhc2V0Lgptb3ZpZXNOZGlyZWN0b3JzTnJhdGluZ3MgPC0gbWVyZ2UobW92aWVzTmRpcmVjdG9ycywgcmF0aW5nc19kZixieT0ibW92aWVpZCIpCiMgYWRkIGRhdGEgZnJvbSBydW5uaW5nIHRpbWVzCm1vdmllc05kaXJlY3RvcnNOcmF0aW5nc05UaW1lcyA8LSBtZXJnZShtb3ZpZXNOZGlyZWN0b3JzTnJhdGluZ3MsIHJ1bm5pbmd0aW1lc19kZixieT0ibW92aWVpZCIpCiMgUmVuYW1lIHRvIGEgY3VzdG9taXNlZCB0YWJsZS4KSU1EQl9DdXN0b20gPC1tb3ZpZXNOZGlyZWN0b3JzTnJhdGluZ3NOVGltZXMKYGBgCgpgYGB7cn0KIyBSZW1vdmUgdW53YW50ZWQgY29sdW1ucy4gCklNREJfQ3VzdG9tJHRpbWUgPC0gTlVMTApJTURCX0N1c3RvbSRkaXJlY3RvcmlkIDwtIE5VTEwKSU1EQl9DdXN0b20kYWRkaXRpb24gPC0gTlVMTApgYGAKCmBgYHtyfQojIEluc3BlY3QgdGhlIGZpbmFsIGRhdGEgc2V0LiAKaGVhZChJTURCX0N1c3RvbSkKYGBgCgojIyBJbnNwZWN0IGFuZCBVbmRlcnN0YW5kClRoaXMgc2VjdGlvbiBpbnNwZWN0cyB0aGUgZGF0YSBmcmFtZSBhbmQgdmFyaWFibGVzIHVzaW5nIFIgZnVuY3Rpb25zLiBUaGUgZm9sbG93aW5nIGNvZGUgY2h1bmsgY2hlY2tzIHRoZSBkYXRhIHR5cGVzIChpLmUuLCBjaGFyYWN0ZXIsIG51bWVyaWMsIGludGVnZXIsIGZhY3RvciwgYW5kIGxvZ2ljYWwpIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgc2V0LiBUaGUgc3RyKCkgZnVuY3Rpb24gaXMgdXNlZCB3aGljaCBwcm92aWRlcyB0aGUgZGV0YWlsZWQgc3RydWN0dXJlIHdpdGhpbiB0aGUgZGF0YSBzZXQuIEl0IGFsc28gcHJvdmlkZXMgZGV0YWlscyB3aXRoIHJlZ2FyZHMgdG8gdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgYW5kIG9ic2VydmF0aW9ucy4gRGF0YSB0eXBlcyBhcmUgYWxzbyBjaGFuZ2VkIHRvIHN1cHBvcnQgdGhlIHByZS1wcm9jZXNzaW5nIGFuZCBhbmFseXNpcy4gCgoKYGBge3J9CiMgQ2hlY2sgdGhlIGRpbWVuc2lvbnMgZm8gdGhlIERGCmRpbShJTURCX0N1c3RvbSkKYGBgCgpgYGB7cn0KIyBsaXN0IHRoZSBjb2x1bW4gbmFtZXMgYW5kIGNoYW5nZSB3aGVyZSBhcHByb3ByaWF0ZS4Kc3RyKElNREJfQ3VzdG9tKQpgYGAKClRoZSBmb2xsb3dpbmcgc2VjdGlvbiBvZiBjb2RlIGlzbnNwZWN0IGFuZCBDb3JyZWN0IHZhcmliYWxlcwpgYGB7cn0KI1JlbmFtZSBjb2x1bW5zIHdpdGggZHBseXI6OnJlbmFtZSgpIHRvIHByb3ZpZGUgc29tZSBjb250ZXh0IHRvIHRoZSBkYXRhc2V0LiAKIyBSZW5hbWUgdGhlIHZhcmlhYmxlIG5hbWVzIHRvIG1lYW5pbmdmdWwgb25lcwojIHJhbmsgdG8gbW92aWVfcmFua2luZwpJTURCX0N1c3RvbTwtIElNREJfQ3VzdG9tJT4lcmVuYW1lKG1vdmllX3Jhbmtpbmc9cmFuaykKIyB2b3RlcyB0byBtb3ZpZV92b3RlcwpJTURCX0N1c3RvbTwtIElNREJfQ3VzdG9tJT4lcmVuYW1lKG1vdmllX3ZvdGVzPXZvdGVzKQojIG5hbWUgdG8gdGltZTEgdG8gcnVubmluZyB0aW1lLiAKSU1EQl9DdXN0b208LSBJTURCX0N1c3RvbSU+JXJlbmFtZShydW5uaW5nX3RpbWU9dGltZTEpCiMgcmF0ZSB0byBkaXJlY3Rvcl9yYXRpbmcKYGBgCgpgYGB7cn0KIyBUaGUgZm9sbG93aW5nIHZhcmlhYmxlIHR5cGVzIHdlcmUgY29ycmVjdGVkIGFuZCBjaGFuZ2VkIHRvIHN1cHBvcnQgdGhlIGFuYWx5c2lzLiAKCiMgQ2huYWdlIHRoZSByYW5rIHZhcmlhYmxlIGZyb20gbnVlbXJpY2FsIHRvIG9yZGluYWwKSU1EQl9DdXN0b20kbW92aWVfcmFua2luZyA8LSBhcy5udW1lcmljKElNREJfQ3VzdG9tJG1vdmllX3JhbmtpbmcpCiMgQ2hhbmdlIHRpdGxlIHRvIHN0cmluZwpJTURCX0N1c3RvbSR0aXRsZSA8LWFzLmNoYXJhY3RlcihJTURCX0N1c3RvbSR0aXRsZSkKIyBDaGFuZ2UgZGlzdHJpYnV0aW8gZnJvbSBmYWN0b3IgdG8gb3JkaW5hbApJTURCX0N1c3RvbSRkaXN0cmlidXRpb248LWFzLm9yZGVyZWQoSU1EQl9DdXN0b20kZGlzdHJpYnV0aW9uKQpgYGAKCgpSZWNoZWNrIHRoZSBzdHJ1Y3R1cmUgYW5kIGNvbmZpcm0gdGhlIGNoYW5nZXMgaGF2ZSBiZWVuIGFwcGxpZWQuIApgYGB7cn0Kc3RyKElNREJfQ3VzdG9tKQpgYGAKCkFzIHRoZSBpbml0aWFsIG1lcmdlcnMgbWF5IGhhdmUgcmVzdWx0ZWQgaW4gc29tZSBkdXBsaWNhdGUgdmFsdWVzLCByZW1vdmUgdGhlc2UgdmFsdWVzIGFuZCBmaW5hbGlzZSB0aGUgZGF0YSBzZXQuICAKCmBgYHtyfQojIEZpbmFsbHkgcmVtb3ZlIGFueSBkdXBsaWNhdGUgcm93cwpJTURCX0N1c3RvbSA8LSBkaXN0aW5jdChJTURCX0N1c3RvbSkKYGBgCgpgYGB7cn0KZGltKChJTURCX0N1c3RvbSkpCmBgYAoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIApUaGUgZGF0YSBzZXQgSSBoYXZlIGNob3NlbiBhbmQgdGhlIG1lcmdlciBvZiByZWxldmFudCB0YWJsZXMgcmVzdWx0aW5nIGluIHRoZSBmaW5hbCB0YWJsZSwgSU1EQl9DdXN0b20gYWxyZWFkeSBjb25mb3JtcyB0aGUgdGlkeSBkYXRhIHByaW5jaXBsZSBhbmQgbm8gcmVzaGFwaW5nIG9mIG15IGRhdGEgaW50byBhIHRpZHkgZm9ybWF0IGlzIHJlcXVpcmVkLiAKCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSUkgClRoZSBmb2xsb3dpbmcgY29kZSB1c2VzIHRoZSBtdXRhdGUgZnVuY3Rpb24gQ3JlYXRlL211dGF0ZSB0aGUgcnVubmluZyB0aW1lIHZhcmlhYmxlIHRvIGFkZCB0aGUgY29sdW1uIHJ1bm5pbmdfdGltZV9zZWNvbmRzLiAKCmBgYHtyfQojIENyZWF0ZSBhIG5ldyB2YXJpYWJsZSBmb3IgcnVubmluZyB0aW1lIGluIHNlY29uZHMgYgpJTURCX0N1c3RvbSA8LSBJTURCX0N1c3RvbSAlPiUgbXV0YXRlKHJ1bm5pbmdfdGltZV9zZWNvbmRzID0gcnVubmluZ190aW1lKjYwKQpoZWFkKElNREJfQ3VzdG9tKQpgYGAKCiMjCVNjYW4gSSAKClRoZSBmb2xsb3dpbmcgc2VjdGlvbnMgb2YgY29kZSBzY2FuIHRoZSBkYXRhIGZvciBtaXNzaW5nIHZhbHVlcyAoTmFOcyBhbmQgTkFzKSBhbmQgYW55IGluY29uc2lzdGVuY2llcyBhbmQgb2J2aW91cyBlcnJvcnMuIApgYGB7cn0KIyAKc3VtKGlzLm5hKElNREJfQ3VzdG9tJG1vdmllaWQpKQpzdW0oaXMubmEoSU1EQl9DdXN0b20kZ2VucmUpKQpzdW0oaXMubmEoSU1EQl9DdXN0b20kdGl0bGUpKQpzdW0oaXMubmEoSU1EQl9DdXN0b20keWVhcikpCnN1bShpcy5uYShJTURCX0N1c3RvbSRtb3ZpZV9yYW5raW5nKSkKc3VtKGlzLm5hKElNREJfQ3VzdG9tJG1vdmllX3ZvdGVzKSkKc3VtKGlzLm5hKElNREJfQ3VzdG9tJGRpc3RyaWJ1dGlvbikpICAgICAgCnN1bShpcy5uYShJTURCX0N1c3RvbSRydW5uaW5nX3RpbWUpKQpgYGAKCmBgYHtyfQpzdW0oaXMubmFuKElNREJfQ3VzdG9tJG1vdmllaWQpKQpzdW0oaXMubmFuKElNREJfQ3VzdG9tJGdlbnJlKSkKc3VtKGlzLm5hbihJTURCX0N1c3RvbSR0aXRsZSkpCnN1bShpcy5uYW4oSU1EQl9DdXN0b20keWVhcikpCnN1bShpcy5uYW4oSU1EQl9DdXN0b20kbW92aWVfcmFua2luZykpCnN1bShpcy5uYW4oSU1EQl9DdXN0b20kbW92aWVfdm90ZXMpKQpzdW0oaXMubmFuKElNREJfQ3VzdG9tJGRpc3RyaWJ1dGlvbikpCnN1bShpcy5uYW4oSU1EQl9DdXN0b20kcnVubmluZ190aW1lKSkKYGBgCgojIwlTY2FuIElJClRoZSBmb2xsb3dpbmcgc2VjdGlvbnMgb2YgY29kZSBzY2FuIGFuZCBhZGRyZXNzIG91dGxpZXJzIGFuZCB1c2VzIFR1a2V54oCZcyB1bml2YXJpYXRlIG1ldGhvZCBvZiBvdXRsaWVyIGRldGVjdGlvbiBpcyB1c2VkIHRvIGRldGVjdCBvdXRsaWVycyBpbiB0aGUgYm94IHBsb3RzLiAKCiMjIyBEaXNwbGF5IEJveCBQbG90cyAtIFByZSBPdXRsaWVyIHJlc29sdXRpb24KRmlyc3QgYSBib3ggcGxvdCBpcyBjcmVhdGVkIHRvIHByb3ZpZGUgYW55IHZpc3VhbGlzYXRpb24gb2Ygb3V0bGllcnMuIEFzIHNlZW4gaW4gdGhlIGJveHBsb3RzIGJlbG93IHRoZXJlIGFyZSBhIG51bWJlciBvZiBvdXRsaWVycyBmb3IgZXZlcnkgdmFyaWFibGUgYW5kIHRoaXMgbWF5IGJlIGFkZHJlc3NlZCBhbmQgaW1wcm92ZWQgd2l0aCBUdWtleXMgbWV0aG9kLiAgCgpgYGB7cn0KIyBjcmVhdGUgYSBjb2xsZWN0aW9uIG9mIGJveHBsb3RzIG9mIHRoZSBzZWxlY3RlZCB2YXJpYWJsZXMgc2hvd2luZyB0aGUgb3V0bGllcnMuIApwYXIobWZyb3c9YygyLDIpKQpib3hwbG90KElNREJfQ3VzdG9tJHllYXIsIG1haW49IlxuTW92aWUgWWVhciIsIHlsYWI9IlllYXIiLCBjb2w9ImJsdWUiLCBwYXJzPWxpc3Qob3V0Y29sPSJyZWQiKSkKYm94cGxvdChJTURCX0N1c3RvbSRydW5uaW5nX3RpbWUsIG1haW49IlxuUnVubmluZyBUaW1lIiwgeWxhYj0iTWludXRlcyIsIGNvbD0iYmx1ZSIsIHBhcnM9bGlzdChvdXRjb2w9InJlZCIpKQpib3hwbG90KElNREJfQ3VzdG9tJG1vdmllX3ZvdGVzLCBtYWluPSJNb3ZpZSBSdW5uaW5nIFZvdGVzIiwgeWxhYj0iTWludXRlcyIsIGNvbD0iYmx1ZSIsIHBhcnM9bGlzdChvdXRjb2w9InJlZCIpKQpib3hwbG90KElNREJfQ3VzdG9tJHJ1bm5pbmdfdGltZSwgbWFpbj0iTW92aWUgUnVubmluZyBSYW5raW5nIiwgeWxhYj0iTWludXRlcyIsIGNvbD0iYmx1ZSIsIHBhcnM9bGlzdChvdXRjb2w9InJlZCIpKQptdGV4dCgiQm94IFBsb3RzIGZvciBWYXJpYWJsZXMgcHJlIFRyYW5zZm9ybWF0aW9uIiwgc2lkZSA9IDMsIGxpbmUgPSAtMSxvdXRlciA9IFRSVUUpCmBgYAoKIyMjIENoZWNrIG91dGxpZXJzIHVzaW5nIHotc2NvcmUgYW5kIHJlc3BlY3RpdmUgYWRqdXN0bWVudHMKVXNlIFR1a2V5cyBtZXRob2QgZm9yIGVhY2ggb2YgdGhlIHZhcmlhYmxlcyB0byBkZXRlY3QgYW5kIGFkZHJlc3MgdGhlIG91dGxpZXJzLiAKIyMjIE1vdmllIFllYXIgVmFyaWFibGUKYGBge3J9CiMgQ2hlY2sgZm9yIG91dGxpZXJzIHVzaW5nIHRoZSB6LXNjb3JlCnouc2NvcmVzIDwtICBJTURCX0N1c3RvbSR5ZWFyICU+JSBzY29yZXModHlwZSA9ICJ6IikKei5zY29yZXMgJT4lIHN1bW1hcnkoKQpgYGAKVGhlcmUgYXJlIDg2IHZhbHVlcyB0aGF0IGNhbiBiZSBjb25zaWRlcmVkIG91dGxpZXJzCmBgYHtyfQojIENoZWNrIHRoZSBudW1iZXIgb2YgdmFsdWVzIHRoYXQgZmFsbCBvdXRzaWRlIHRoZSByYW5nZS4gLgpsZW5ndGggKHdoaWNoKCBhYnMoei5zY29yZXMpID4zICkpCmBgYApgYGB7cn0KIyBSZXBsYWNlIHRoZSBvdXRsaWVycyB3aXRoIG1lZGlhbiB2YWx1ZXMKSU1EQl9DdXN0b20keWVhclt3aGljaCggYWJzKHouc2NvcmVzKSA+MyApXSA8LSBtZWRpYW4oSU1EQl9DdXN0b20keWVhcikKYGBgClRoZSB2YWx1ZXMgb3V0c2lkZSB0aGUgcmFuZ2UgaGF2ZSBub3cgYmVlbiByZWR1Y2VkIHRvIDc0CmBgYHtyfQojIFJlY2hlY2sgdGhlIHogc2NvcmVzIGFmdGVyIHRoZSBpbXB1dGF0aW9ucyB0byBjb25maXJtIHRoZSBvdXRsaWVyIGhhdmUgYmVlbiBhZGRyZXNzZWQuIAp6LnNjb3JlcyA8LSAgSU1EQl9DdXN0b20keWVhciAlPiUgc2NvcmVzKHR5cGUgPSAieiIpCnouc2NvcmVzICU+JSBzdW1tYXJ5KCkKbGVuZ3RoICh3aGljaCggYWJzKHouc2NvcmVzKSA+MyApKQpgYGAKCiMjIyBNb3ZpZSBSdW5uaW5nIFRpbWUgIFZhcmlhYmxlCmBgYHtyfQojIENoZWNrIGZvciBvdXRsaWVycyB1c2luZyB0aGUgei1zY29yZQp6LnNjb3JlcyA8LSAgSU1EQl9DdXN0b20kcnVubmluZ190aW1lICU+JSBzY29yZXModHlwZSA9ICJ6IikKei5zY29yZXMgJT4lIHN1bW1hcnkoKQpgYGAKVGhlcmUgYXJlIDEwOSB2YWx1ZXMgdGhhdCBjYW4gYmUgY29uc2lkZXJlZCBvdXRsaWVycy4gCmBgYHtyfQojIENoZWNrIHRoZSBudW1iZXIgb2YgdmFsdWVzIHRoYXQgZmFsbCBvdXRzaWRlIHRoZSByYW5nZS4gVGhlcmUgYXJlIDEwOSB2YWx1ZXMgdGhhdCBjYW4gYmUgY29uc2lkZXJlZCBvdXRsaWVycy4gCmxlbmd0aCAod2hpY2goIGFicyh6LnNjb3JlcykgPjMgKSkKYGBgCmBgYHtyfQojIFJlcGxhY2UgdGhlIG91dGxpZXJzIHdpdGggbWVkaWFuIHZhbHVlcwpJTURCX0N1c3RvbSRydW5uaW5nX3RpbWVbd2hpY2goIGFicyh6LnNjb3JlcykgPjMgKV0gPC0gbWVkaWFuKElNREJfQ3VzdG9tJHJ1bm5pbmdfdGltZSkKYGBgClRoZSB2YWx1ZXMgb3V0c2lkZSB0aGUgcmFuZ2UgaGF2ZSBub3cgYmVlbiByZWR1Y2VkIHRvIDcxLgpgYGB7cn0KIyBSZWNoZWNrIHRoZSB6IHNjb3JlcyBhZnRlciB0aGUgaW1wdXRhdGlvbnMgdG8gY29uZmlybSB0aGUgb3V0bGllciBoYXZlIGJlZW4gYWRkcmVzc2VkLiBUaGUgdmFsdWVzIG91dHNpZGUgdGhlIHJhbmdlIGhhdmUgbm93IGJlZW4gcmVkdWNlZCB0byA3MS4Kei5zY29yZXMgPC0gIElNREJfQ3VzdG9tJHJ1bm5pbmdfdGltZSAlPiUgc2NvcmVzKHR5cGUgPSAieiIpCnouc2NvcmVzICU+JSBzdW1tYXJ5KCkKbGVuZ3RoICh3aGljaCggYWJzKHouc2NvcmVzKSA+MyApKQpgYGAKCiMjIyMgTW92aWUgVm90ZXMgIFZhcmlhYmxlCmBgYHtyfQojIENoZWNrIGZvciBvdXRsaWVycyB1c2luZyB0aGUgei1zY29yZQp6LnNjb3JlcyA8LSAgSU1EQl9DdXN0b20kbW92aWVfdm90ZXMgJT4lIHNjb3Jlcyh0eXBlID0gInoiKQp6LnNjb3JlcyAlPiUgc3VtbWFyeSgpCmBgYApUaGVyZSBhcmUgOTkgdmFsdWVzIHRoYXQgY2FuIGJlIGNvbnNpZGVyZWQgb3V0bGllcnMuCmBgYHtyfQojIENoZWNrIHRoZSBudW1iZXIgb2YgdmFsdWVzIHRoYXQgZmFsbCBvdXRzaWRlIHRoZSByYW5nZS4gVGhlcmUgYXJlIDk5IHZhbHVlcyB0aGF0IGNhbiBiZSBjb25zaWRlcmVkIG91dGxpZXJzLgpsZW5ndGggKHdoaWNoKCBhYnMoei5zY29yZXMpID4zICkpCmBgYAoKYGBge3J9CiMgUmVwbGFjZSB0aGUgb3V0bGllcnMgd2l0aCBtZWRpYW4gdmFsdWVzCklNREJfQ3VzdG9tJG1vdmllX3ZvdGVzW3doaWNoKCBhYnMoei5zY29yZXMpID4zICldIDwtIG1lZGlhbihJTURCX0N1c3RvbSRtb3ZpZV92b3RlcykKYGBgClRoZSB2YWx1ZXMgb3V0c2lkZSB0aGUgcmFuZ2UgaGF2ZSBub3cgYmVlbiByZWR1Y2VkIHRvIDE1MgpgYGB7cn0KIyBSZWNoZWNrIHRoZSB6IHNjb3JlcyBhZnRlciB0aGUgaW1wdXRhdGlvbnMgdG8gY29uZmlybSB0aGUgb3V0bGllciBoYXZlIGJlZW4gYWRkcmVzc2VkLiBUaGVyZSBhcmUgODYgdmFsdWVzIHRoYXQgY2FuIGJlIGNvbnNpZGVyZWQgb3V0bGllcnMuIFRoZSB2YWx1ZXMgb3V0c2lkZSB0aGUgcmFuZ2UgaGF2ZSBub3cgYmVlbiByZWR1Y2VkIHRvIDE1Mgp6LnNjb3JlcyA8LSAgSU1EQl9DdXN0b20kbW92aWVfdm90ZXMgJT4lIHNjb3Jlcyh0eXBlID0gInoiKQp6LnNjb3JlcyAlPiUgc3VtbWFyeSgpCmxlbmd0aCAod2hpY2goIGFicyh6LnNjb3JlcykgPjMgKSkKYGBgCgojIE1vdmllIHJhbmtpbmcKYGBge3J9CiMgQ2hlY2sgZm9yIG91dGxpZXJzIHVzaW5nIHRoZSB6LXNjb3JlCnouc2NvcmVzIDwtICBJTURCX0N1c3RvbSRtb3ZpZV9yYW5raW5nICU+JSBzY29yZXModHlwZSA9ICJ6IikKei5zY29yZXMgJT4lIHN1bW1hcnkoKQpgYGAKVGhlcmUgYXJlIDQwIHZhbHVlcyB0aGF0IGNhbiBiZSBjb25zaWRlcmVkIG91dGxpZXJzLgpgYGB7cn0KIyBDaGVjayB0aGUgbnVtYmVyIG9mIHZhbHVlcyB0aGF0IGZhbGwgb3V0c2lkZSB0aGUgcmFuZ2UuIFRoZXJlIGFyZSA0MCB2YWx1ZXMgdGhhdCBjYW4gYmUgY29uc2lkZXJlZCBvdXRsaWVycy4gCmxlbmd0aCAod2hpY2goIGFicyh6LnNjb3JlcykgPjMgKSkKYGBgCgpgYGB7cn0KIyBSZXBsYWNlIHRoZSBvdXRsaWVycyB3aXRoIG1lZGlhbiB2YWx1ZXMKSU1EQl9DdXN0b20kbW92aWVfcmFua2luZ1t3aGljaCggYWJzKHouc2NvcmVzKSA+MyApXSA8LSBtZWRpYW4oSU1EQl9DdXN0b20kbW92aWVfcmFua2luZykKYGBgClRoZSB2YWx1ZXMgb3V0c2lkZSB0aGUgcmFuZ2UgaGF2ZSBub3cgYmVlbiByZWR1Y2VkIHRvIDEzLgpgYGB7cn0KIyBSZWNoZWNrIHRoZSB6IHNjb3JlcyBhZnRlciB0aGUgaW1wdXRhdGlvbnMgdG8gY29uZmlybSB0aGUgb3V0bGllciBoYXZlIGJlZW4gYWRkcmVzc2VkLiBUaGUgdmFsdWVzIG91dHNpZGUgdGhlIHJhbmdlIGhhdmUgbm93IGJlZW4gcmVkdWNlZCB0byAxMy4Kei5zY29yZXMgPC0gIElNREJfQ3VzdG9tJG1vdmllX3JhbmtpbmcgJT4lIHNjb3Jlcyh0eXBlID0gInoiKQp6LnNjb3JlcyAlPiUgc3VtbWFyeSgpCmxlbmd0aCAod2hpY2goIGFicyh6LnNjb3JlcykgPjMgKSkKYGBgCgoKIyMjIyMgRGlzcGxheSBCb3ggUGxvdHMgLSBQb3N0IE91bGllciByZXNvbHV0aW9uClRoZSBzZWN0aW9uIGJlbG93IGRpc3BsYXlzIHRoZSB2YXJpYWJsZXMgYWZ0ZXIgVHVrZXlzIHVuaXZhcmlhdGUgb3V0bGllciBkZXRlY3Rpb24gYW5kIHJlc29sdXRpb24gbWV0aG9kcyBoYXZlIGJlZW4gYXBwbGllZC4gQXMgY2FuIGJlIHNlZW4gdGhlcmUgYXJlIGNvbnNpZGVyYWJsZSBpbXByb3ZlbWVudHMgZnJvbSB0aGUgaW5pdGlhbCBzZXQgb2YgcGxvdHMuIAoKYGBge3J9CnBhcihtZnJvdz1jKDIsMikpCmJveHBsb3QoSU1EQl9DdXN0b20keWVhciwgbWFpbj0iXG5Nb3ZpZSBZZWFyIiwgeWxhYj0iWWVhciIsIGNvbD0iYmx1ZSIsIHBhcnM9bGlzdChvdXRjb2w9InJlZCIpKQpib3hwbG90KElNREJfQ3VzdG9tJHJ1bm5pbmdfdGltZSwgbWFpbj0iXG5SdW5uaW5nIFRpbWUiLCB5bGFiPSJNaW51dGVzIiwgY29sPSJibHVlIiwgcGFycz1saXN0KG91dGNvbD0icmVkIikpCmJveHBsb3QoSU1EQl9DdXN0b20kbW92aWVfdm90ZXMsIG1haW49Ik1vdmllIFJ1bm5pbmcgVm90ZXMiLCB5bGFiPSJNaW51dGVzIiwgY29sPSJibHVlIiwgcGFycz1saXN0KG91dGNvbD0icmVkIikpCmJveHBsb3QoSU1EQl9DdXN0b20kcnVubmluZ190aW1lLCBtYWluPSJNb3ZpZSBSdW5uaW5nIFJhbmtpbmciLCB5bGFiPSJNaW51dGVzIiwgY29sPSJibHVlIiwgcGFycz1saXN0KG91dGNvbD0icmVkIikpCiMgbXRleHQoIk15ICdDb2xsZWN0aW9uIG9mIEJveCBQbG90cyBmb3IgVmFyaWFibGVzIiwgc2lkZSA9IDMsIGxpbmUgPSAtMjEsIG91dGVyID0gVFJVRSkKbXRleHQoIkJveCBQbG90cyBmb3IgVmFyaWFibGVzIFBvc3QgT3V0bGllciBSZXNvbHV0aW9uIiwgc2lkZSA9IDMsIGxpbmUgPSAtMSxvdXRlciA9IFRSVUUpCmBgYAojIwlUcmFuc2Zvcm0gCgpUaGlzIHNlY3Rpb24gYXBwbGllcyB0cmFuc2Zvcm1hdGlvbiB0byB0aGUgc2VsZWN0ZWQgdmFyaWFibGVzIGJ5IHZpc3VhbHkgYW5hbHlzaW5nIGh0ZSBza2V3IG9mIHRoZSBkaXN0cmlidXRpb24gYW5kIGFwcGx5aW5nIGEgcmVsZXZhbnQgdHJhbnNvZnJhbXRpb24uIFBsZWFzZSBub3RlIEkgaGF2ZSBhcHBsaWVkIGEgcmFuZ2Ugb2YgdHJhbnNmb3JtYXRpb24gdG8gdmlldyB0aGUgZWZmZWZjdHMgYnV0IGhhdmUgc3BlY2lmaWVkIHdpY2ggb25lcyBhcmUgcmVsZXZhbnQgdG8gYmFzZWQgb24gdGhlIHNrZXcuICBUaGUgcGxvdHMgYmVsb3cgZGlzcGxheSByZXNwZWN0aXZlIGhpc3RvZ3JhbXMgb2YgdGhlIHZhcmlhYmxlcyBvZiBpbnRlcmVzdC4gCgpgYGB7cn0KIyBQbG90IGhpc3RvZ3JhbXMgb24gdW50cmFuc2Zvcm1lZCB2YXJpYWJsZXMuIApwYXIobWZyb3c9YygyLDIpKQpoaXN0KElNREJfQ3VzdG9tJHllYXIsbWFpbj0iTW92aWUgWWVhciIsIHhsYWI9IlllYXIiLCBjb2w9ImJsdWUiKQpoaXN0KElNREJfQ3VzdG9tJHJ1bm5pbmdfdGltZSxtYWluPSJcbk1vdmllIFJ1bm5pbmcgVGltZSIsIHhsYWI9IlJ1bm5pbmcgVGltZSIsIGNvbD0iYmx1ZSIpCmhpc3QoSU1EQl9DdXN0b20kbW92aWVfdm90ZXMsIG1haW49IlxuTW92aWUgVm90ZXMiLHhsYWI9Ik1vdmllIFZvdGVzIiwgY29sPSJibHVlIikKaGlzdChJTURCX0N1c3RvbSRtb3ZpZV9yYW5raW5nLG1haW49Ik1vdmllIFJhbmtpbmciLHhsYWI9Ik1vdmllIFJhbmtpbmciLCBjb2w9ImJsdWUiKQptdGV4dCgiSGlzdG9ncmFtcyBmb3IgVmFyaWFibGVzIFByZSBUcmFuc2Zvcm1hdGlvbiIsIHNpZGUgPSAzLCBsaW5lID0gLTEsb3V0ZXIgPSBUUlVFKQpgYGAKIyMjIyBNb3ZpZSBZZWFyCkRhdGEgZm9yIG1vdmllIHllYXIgdGltZSBpcyBzdHJvbmdsZXkgc2tld2VkIHRvIHRoZSBsZWZ0LiAgV2hpbHN0IHdlIGFwcGx5IGFsbCB0cmFuc2ZvcmFtaW9uIHRvIHNlIHRoZSBlZmZlY3QgdGhlIG1vc3QgYXBwbGljYWJsZSB0cmFuc2Zvcm1hdGlvbiB0byBsZWZ0IHNrZXdlZCBkYXRhIGlzIHNxdWFyZSByb290IGFuZCByZWNpcHJvY2FsIHRyYW5zZm9ybWF0aW9uLCBvdGhlciB0cmFuc2Zvcm1hdGlvbnMgaW5jbHVkaW5nIEJveCBDb3ggd2l0aCBhIGxhbWJkYSB2YWx1ZSBvZiBhdXRvIGlzIGFwcGxpZWQuIEluIHRoaXMgY2FzZSB0aGUgdHJhbnNmb3JtYXRpb25zIGhhZCBsaXR0bGUgZWZmZWN0IGluIHByb2R1Y2luZyBhIHN5bW1ldHJpY2FsIGRpc3RyaWJ1dGlvbi4gCgpgYGB7cn0KIyBNb3ZpZSBZZWFyIFRyYW5zZm9ybWF0aW9ucyAtIExlZnQgU2tld2VkCiMgU3F1YXJlIFRyYW5zZm9yYW10b24KeWVhcl9zcTFfdHJuIDwtIElNREJfQ3VzdG9tJHllYXJeMgojIEJveCBDb3gKeWVhcl9CQ190cm4gPC0gQm94Q294KElNREJfQ3VzdG9tJHllYXIsIGxhbWJkYSA9ICJhdXRvIikKI2xvZyB0cmFuc2Zvcm1hdGlvbgp5ZWFyX2xvZ190cm4gPC0gbG9nMTAoSU1EQl9DdXN0b20keWVhcikKeWVhcl9sbl90cm4gPC0gbG9nKElNREJfQ3VzdG9tJHllYXIpCiMgUmVjaXByb2NhbCBUcmFuc2Zvcm1hdGlvbgp5ZWFyX3JlY2lwX3RybiA8LSAxL0lNREJfQ3VzdG9tJHllYXIKYGBgCgpgYGB7cn0KIyBQbG90IGhpc3RvZ3JhbXMgb24gdW50cmFuc2Zvcm1lZCB2YXJpYWJsZXMuIApwYXIobWZyb3c9YygxLDUpKQpoaXN0KHllYXJfc3ExX3RybixtYWluPSJcblNxdWFyZSBSb290IiwgeGxhYj0iWWVhciIsIGNvbD0iYmx1ZSIpCmhpc3QoeWVhcl9CQ190cm4sbWFpbj0iXG5Cb3ggQ294IiwgeGxhYj0iWWVhciIsIGNvbD0iYmx1ZSIpCmhpc3QoeWVhcl9sb2dfdHJuLG1haW49IlxuTG9nIDEwIiwgeGxhYj0iWWVhciIsIGNvbD0iYmx1ZSIpCmhpc3QoeWVhcl9sbl90cm4sbWFpbj0iXG5OYXR1cmFsIExvZyIsIHhsYWI9IlllYXIiLCBjb2w9ImJsdWUiKQpoaXN0KHllYXJfcmVjaXBfdHJuLG1haW49IlxuUmVjaXByb2NhbCBMb2ciLCB4bGFiPSJZZWFyIiwgY29sPSJibHVlIikKbXRleHQoIkhpc3RvZ3JhbXMgZm9yIFllYXIgVmFyaWFibGVzIFBvc3QgVHJhbnNmb3JtYXRpb24iLCBzaWRlID0gMywgbGluZSA9IC0xLG91dGVyID0gVFJVRSkKYGBgCgojIyMjIE1vdmllIFJ1bm5pbmcgVGltZQpEYXRhIGZvciBtb3ZpZSBydW5uaW5nIHRpbWVzIGFwcGVyIHRvIGJlIHJlYXNvbmFibHkgc3ltbWV0cmljYWwsIGxldHMgc2VlIGlmIGl0IGNhbiBiZSBpbXByb3ZlZC4gQWdhaW4sIGFsbCB0cmFuc2ZvcmFtaW9uIGFyZSBhcHBsaWVkIHRvIHNlZSB0aGUgZWZmZWN0LiBBZ2FpbiBub25lIG9mIHRoZSB0cmFuc2Zvcm1hdGlvbnMgYXBwbGllZCBoYWQgYW55IHN0cm9uZyBpbXBhY3Qgb24gaW1wcm92aW5nIHRoZSBkaXN0cnVidXRpb24uIAoKYGBge3J9CiMgTW92aWUgWWVhciBUcmFuc2Zvcm1hdGlvbnMgLSBMZWZ0IFNrZXdlZAojIFNxdWFyZSBUcmFuc2ZvcmFtdG9uCnJ1bm5pbmdfdGltZV9zcTFfdHJuIDwtIElNREJfQ3VzdG9tJHJ1bm5pbmdfdGltZV4yCiMgQm94IENveApydW5uaW5nX3RpbWVfQkNfdHJuIDwtIEJveENveChJTURCX0N1c3RvbSRydW5uaW5nX3RpbWUsIGxhbWJkYSA9ICJhdXRvIikKI2xvZyB0cmFuc2Zvcm1hdGlvbgpydW5uaW5nX3RpbWVfbG9nX3RybiA8LSBsb2cxMChJTURCX0N1c3RvbSRydW5uaW5nX3RpbWUpCnJ1bm5pbmdfdGltZV9sbl90cm4gPC0gbG9nKElNREJfQ3VzdG9tJHJ1bm5pbmdfdGltZSkKIyBSZWNpcHJvY2FsIFRyYW5zZm9ybWF0aW9uCnJ1bm5pbmdfdGltZV9yZWNpcF90cm4gPC0gMS9JTURCX0N1c3RvbSRydW5uaW5nX3RpbWUKYGBgCgpgYGB7cn0KIyBQbG90IGhpc3RvZ3JhbXMgb24gdW50cmFuc2Zvcm1lZCB2YXJpYWJsZXMuIApwYXIobWZyb3c9YygxLDUpKQpoaXN0KHJ1bm5pbmdfdGltZV9zcTFfdHJuLG1haW49IlxuU3F1YXJlIFJvb3QiLCB4bGFiPSJSdW5uaW5nIFRpbWUiLCBjb2w9ImJsdWUiKQpoaXN0KHJ1bm5pbmdfdGltZV9CQ190cm4sbWFpbj0iXG5Cb3ggQ294IiwgeGxhYj0iUnVubmluZyBUaW1lIiwgY29sPSJibHVlIikKaGlzdChydW5uaW5nX3RpbWVfbG9nX3RybixtYWluPSJcbkxvZyAxMCIsIHhsYWI9IlJ1bm5pbmcgVGltZSIsIGNvbD0iYmx1ZSIpCmhpc3QocnVubmluZ190aW1lX2xuX3RybixtYWluPSJcbk5hdHVyYWwgTG9nIiwgeGxhYj0iUnVubmluZyBUaW1lIiwgY29sPSJibHVlIikKaGlzdChydW5uaW5nX3RpbWVfcmVjaXBfdHJuLG1haW49IlxuUmVjaXByb2NhbCIsIHhsYWI9IlJ1bm5pbmcgVGltZSIsIGNvbD0iYmx1ZSIpCm10ZXh0KCJIaXN0b2dyYW1zIGZvciBSdW5uaW5nIFRpbWVzIFZhcmlhYmxlcyBQb3N0IFRyYW5zZm9ybWF0aW9uIiwgc2lkZSA9IDMsIGxpbmUgPSAtMSxvdXRlciA9IFRSVUUpCmBgYAoKIyMjIyBNb3ZpZSBWb3RlcwpEYXRhIGZvciBtb3ZpZSB2b3RlcyBpcyB2ZXJ5IHN0cm9uZ2x5ICBza2V3ZWQgdG8gdGhlIHJpZ2h0LiBXaGlsc3QgSSBoYXZlIGFwcGxpZWQgYWxsIHRyYW5zZm9ybWF0aW9uIHRvIHNlZSB0aGUgZWZmZWN0IHRoZSBtb3N0IGFwcGxpY2FibGUgdHJhbnNmb3JtYXRpb24gdG8gcmlnaHQgc2tld2VkIGRhdGEgaXMgbG9nLCBuYXR1cmFsIGxvZyBhbmQgc3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24uIEhlcmUgdGhlIGJvdGggdGhlIGxvZyB0cmFuc2Zvcm1hdGlvbnMgYXBwbGllZCBhcHBlYXIgdG8gaGF2ZSBhIHJlYXNvbmFibGUgaW1wYWN0IG9uIGltcHJvdmluZyB0aGUgc3ltbWV0cnkgb2YgZGlzdHJpYnV0aW9uLiAKCmBgYHtyfQojIE1vdmllIFZvdGVzIFRyYW5zZm9ybWF0aW9ucyAtIExlZnQgU2tld2VkCiMgU3F1YXJlIFRyYW5zZm9yYW10b24KbW92aWVfdm90ZXNfc3ExX3RybiA8LSBJTURCX0N1c3RvbSRtb3ZpZV92b3Rlc14yCiMgQm94IENveAptb3ZpZV92b3Rlc19CQ190cm4gPC0gQm94Q294KElNREJfQ3VzdG9tJG1vdmllX3ZvdGVzLCBsYW1iZGEgPSAiYXV0byIpCiNsb2cgdHJhbnNmb3JtYXRpb24KbW92aWVfdm90ZXNfbG9nX3RybiA8LSBsb2cxMChJTURCX0N1c3RvbSRtb3ZpZV92b3RlcykKbW92aWVfdm90ZXNfbG5fdHJuIDwtIGxvZyhJTURCX0N1c3RvbSRtb3ZpZV92b3RlcykKIyBSZWNpcHJvY2FsIFRyYW5zZm9ybWF0aW9uCm1vdmllX3ZvdGVzX3JlY2lwX3RybiA8LSAxL0lNREJfQ3VzdG9tJG1vdmllX3ZvdGVzCmBgYAoKYGBge3J9CiMgUGxvdCBoaXN0b2dyYW1zIG9uIHVudHJhbnNmb3JtZWQgdmFyaWFibGVzLiAKcGFyKG1mcm93PWMoMSw1KSkKaGlzdChtb3ZpZV92b3Rlc19zcTFfdHJuLG1haW49IlxuU3F1YXJlIFJvb3QiLCB4bGFiPSJNb3ZpZSBWb3RlcyIsIGNvbD0iYmx1ZSIpCmhpc3QobW92aWVfdm90ZXNfQkNfdHJuLG1haW49IlxuQm94IENveCIsIHhsYWI9Ik1vdmllIFZvdGVzIiwgY29sPSJibHVlIikKaGlzdChtb3ZpZV92b3Rlc19sb2dfdHJuLG1haW49IlxuTG9nIDEwIiwgeGxhYj0iTW92aWUgVm90ZXMiLCBjb2w9ImJsdWUiKQpoaXN0KG1vdmllX3ZvdGVzX2xuX3RybixtYWluPSJcbk5hdHVyYWwgTG9nIiwgeGxhYj0iTW92aWUgVm90ZXMiLCBjb2w9ImJsdWUiKQpoaXN0KG1vdmllX3ZvdGVzX3JlY2lwX3RybixtYWluPSJcblJlY2lwcm9jYWwiLCB4bGFiPSJNb3ZpZSBWb3RlcyIsIGNvbD0iYmx1ZSIpCm10ZXh0KCJIaXN0b2dyYW1zIGZvciBNb3ZpZSBWb3Rlc3MgVmFyaWFibGVzIFBvc3QgVHJhbnNmb3JtYXRpb24iLCBzaWRlID0gMywgbGluZSA9IC0xLG91dGVyID0gVFJVRSkKYGBgCgojIyMjIE1vdmllIFJhbmtpbmcKRGF0YSBmb3IgbW92aWUgcnVubmluZyB0aW1lcyBhcHBlciB0byBiZSByZWFzb25hYmx5IHN5bW1ldHJpY2FsLCBsZXRzIHNlZSBpZiBpdCBjYW4gYmUgaW1wcm92ZWQuIEFnYWluLCBhbGwgdHJhbnNmb3JhbWlvbiBhcmUgYXBwbGllZCB0byBzZWUgdGhlIGVmZmVjdC4gQXMgcGVyIHRoZSBwbG90cyBiZWxvdyBTcXVhcmUgcm9vdCBhbmQgQm94IENveCB0cmFuc2Zvcm1hdGlvbnMgYXBwZWFyIHRvIGhhdmUgYSBtb2RlcmF0ZSBpbXBhY3Qgb24gaW1wcm92aW5nIHRoZSBzeW1tZXRyeSBvZiB0aGUgZGlzdHJ1YnV0aW9uLiAKCmBgYHtyfQojIE1vdmllIFZvdGVzIFRyYW5zZm9ybWF0aW9ucyAtIExlZnQgU2tld2VkCiMgU3F1YXJlIFRyYW5zZm9yYW10b24KbW92aWVfcmFua2luZ19zcTFfdHJuIDwtIElNREJfQ3VzdG9tJG1vdmllX3JhbmtpbmdeMgojIEJveCBDb3gKbW92aWVfcmFua2luZ19CQ190cm4gPC0gQm94Q294KElNREJfQ3VzdG9tJG1vdmllX3JhbmtpbmcsIGxhbWJkYSA9ICJhdXRvIikKI2xvZyB0cmFuc2Zvcm1hdGlvbgptb3ZpZV9yYW5raW5nX2xvZ190cm4gPC0gbG9nMTAoSU1EQl9DdXN0b20kbW92aWVfcmFua2luZykKbW92aWVfcmFua2luZ19sbl90cm4gPC0gbG9nKElNREJfQ3VzdG9tJG1vdmllX3JhbmtpbmcpCiMgUmVjaXByb2NhbCBUcmFuc2Zvcm1hdGlvbgptb3ZpZV9yYW5raW5nX3JlY2lwX3RybiA8LSAxL0lNREJfQ3VzdG9tJG1vdmllX3JhbmtpbmcKYGBgCgpgYGB7cn0KIyBQbG90IGhpc3RvZ3JhbXMgb24gdW50cmFuc2Zvcm1lZCB2YXJpYWJsZXMuIApwYXIobWZyb3c9YygxLDUpKQpoaXN0KG1vdmllX3Jhbmtpbmdfc3ExX3RybixtYWluPSJcblNxdWFyZSBSb290IiwgeGxhYj0iTW92aWUgVm90ZXMiLCBjb2w9ImJsdWUiKQpoaXN0KG1vdmllX3JhbmtpbmdfQkNfdHJuLG1haW49IlxuQm94IENveCIsIHhsYWI9Ik1vdmllIFZvdGVzIiwgY29sPSJibHVlIikKaGlzdChtb3ZpZV9yYW5raW5nX2xvZ190cm4sbWFpbj0iXG5Mb2cgMTAiLCB4bGFiPSJNb3ZpZSBWb3RlcyIsIGNvbD0iYmx1ZSIpCmhpc3QobW92aWVfcmFua2luZ19sbl90cm4sbWFpbj0iXG5OYXR1cmFsIExvZyIsIHhsYWI9Ik1vdmllIFZvdGVzIiwgY29sPSJibHVlIikKaGlzdChtb3ZpZV9yYW5raW5nX3JlY2lwX3RybixtYWluPSJcblJlY2lwcm9jYWwiLCB4bGFiPSJNb3ZpZSBWb3RlcyIsIGNvbD0iYmx1ZSIpCm10ZXh0KCJIaXN0b2dyYW1zIGZvciBNb3ZpZSBWb3Rlc3MgVmFyaWFibGVzIFBvc3QgVHJhbnNmb3JtYXRpb24iLCBzaWRlID0gMywgbGluZSA9IC0xLG91dGVyID0gVFJVRSkKYGBgCgoK