Note: This project involves getting data ready for analysis and doing some preliminary investigations. Project 2 will involve modeling and predictions on the same dataset, and will be released at a later date. Both projects will have equal weightage towards your grade. You may reuse some of the preprocessing/analysis steps from Project 1 in Project 2.

Data

In this project, you will explore a dataset that contains information about movies, including ratings, budget, gross revenue and other attributes. It was prepared by Dr. Guy Lebanon, and here is his description of the dataset:

The file movies_merged contains a dataframe with the same name that has 40K rows and 39 columns. Each row represents a movie title and each column represents a descriptor such as Title, Actors, and Budget. I collected the data by querying IMDb’s API (see www.omdbapi.com) and joining it with a separate dataset of movie budgets and gross earnings (unknown to you). The join key was the movie title. This data is available for personal use, but IMDb’s terms of service do not allow it to be used for commercial purposes or for creating a competing repository.

Objective

Your goal is to investigate the relationship between the movie descriptors and the box office success of movies, as represented by the variable Gross. This task is extremely important as it can help a studio decide which titles to fund for production, how much to bid on produced movies, when to release a title, how much to invest in marketing and PR, etc. This information is most useful before a title is released, but it is still very valuable after the movie is already released to the public (for example it can affect additional marketing spend or how much a studio should negotiate with on-demand streaming companies for “second window” streaming rights).

Instructions

This is an R Markdown Notebook. Open this file in RStudio to get started.

When you execute code within the notebook, the results appear beneath the code. Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter.

x = 1:10
print(x^2)
 [1]   1   4   9  16  25  36  49  64  81 100

Plots appear inline too:

plot(x, x^2, 'o')

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I. Enter some R code and run it.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

Please complete all the tasks below by implementing code chunks that have a TODO comment in them, running all code chunks so that output and plots are displayed, and typing in answers to each question (Q: …) next to/below the corresponding answer prompt (A:). Feel free to add code chunks/show additional output to support any of the answers.

When you are done, you will need to submit the final R markdown file (as pr1.Rmd) with all code chunks implemented and executed, and all text responses written in. You also need to submit a PDF export of the markdown file (as pr1.pdf), which should show your code, output, plots and written responses–this will be your project report. Compress these two files into a single .zip archive and upload it on T-Square.

Setup

Load data

Make sure you’ve downloaded the movies_merged file and it is in the current working directory. Now load it into memory:

load('movies_merged')
cat("Dataset has", dim(movies_merged)[1], "rows and", dim(movies_merged)[2], "columns", end="\n", file="")
Dataset has 40789 rows and 39 columns 

This creates an object of the same name (movies_merged). For convenience, you can copy it to df and start using it:

df = movies_merged
cat("Column names:", end="\n", file="")
Column names: 

Load R packages

Load any R packages that you will need to use. You can come back to this chunk, edit it and re-run to load any additional packages later.

library(ggplot2)
library(GGally)

If you are loading any non-standard packages (ones that have not been discussed in class or explicitly allowed for this project), please mention them below. Include any special instructions if they cannot be installed using the regular install.packages('<pkg name>') command.

Non-standard packages used: None

Tasks

Each task below is worth 10 points, and is meant to be performed sequentially, i.e. do step 2 after you have processed the data as described in step 1. Total points: 100

Complete each task by implementing code chunks as described by TODO comments, and by responding to questions (“Q:”) with written answers (“A:”). If you are unable to find a meaningful or strong relationship in any of the cases when requested, explain why not by referring to appropriate plots/statistics.

It is okay to handle missing values below by omission, but please omit as little as possible. It is worthwhile to invest in reusable and clear code as you may need to use it or modify it in project 2.

1. Remove non-movie rows

The variable Type captures whether the row is a movie, a TV series, or a game. Remove all rows from df that do not correspond to movies.

# TODO: Remove all rows from df that do not correspond to movies
df2 <- df[df$Type == "movie",]
df = df2

Q: How many rows are left after removal? Enter your response below.

A: 40000 rows are left

2. Process Runtime column

The variable Runtime represents the length of the title as a string. Write R code to convert it to a numeric value (in minutes) and replace df$Runtime with the new numeric column.

# TODO: Replace df$Runtime with a numeric column containing the runtime in minutes
rows_num = dim(df)[1]
for (i in seq(1, rows_num)){
  content = unlist(strsplit(df[i, "Runtime"], split=" "))
  if (content == "N/A"){
    df[i,"Runtime"] = 0 
    next()
  }
    
  cur = 0
  for (j in seq(1, length(content) - 1)){
    if (substr(content[j + 1],1, 1) == "h"){
      cur = cur + as.numeric(content[j]) * 60
    }
    
    if (substr(content[j + 1],1, 3) == "min"){
      cur = cur + as.numeric(content[j]) 
    }
  }
  df[i,"Runtime"]= cur
}
df$Runtime =as.numeric((df$Runtime))

Now investigate the distribution of Runtime values and how it changes over years (variable Year, which you can bucket into decades) and in relation to the budget (variable Budget). Include any plots that illustrate.

# TODO: Investigate the distribution of Runtime values and how it varies by Year and Budget
# The distribution of Runtime values
qplot(x = Runtime, data = df, binwidth = 10) 

# Runtime vs. Budget 
qplot(df$Runtime, log(df$Budget))

# Runtime vs. Year
df$Decades = (df$Year %/% 10) * 10
ggplot(df, aes(reorder(Decades, Decades, median), Runtime) ) + geom_boxplot() + scale_x_discrete("") 

Feel free to insert additional code chunks as necessary.

Q: Comment on the distribution as well as relationships. Are there any patterns or trends that you can observe?

A: 1. The Runtime distribution is “Bi model normal distribution”, there are outliers which is much longer than average. 2. Median of Runtime increaded in later years. 3. As Runtime increases, Budget maybe increases, but very scattered. The positive relationship is not very obvious.

3. Encode Genre column

The column Genre represents a list of genres associated with the movie in a string format. Write code to parse each text string into a binary vector with 1s representing the presence of a genre and 0s the absence, and add it to the dataframe as additional columns. Then remove the original Genre column.

For example, if there are a total of 3 genres: Drama, Comedy, and Action, a movie that is both Action and Comedy should be represented by a binary vector <0, 1, 1>. Note that you need to first compile a dictionary of all possible genres and then figure out which movie has which genres (you can use the R tm package to create the dictionary).

# TODO: Replace Genre with a collection of binary columns
library(tm)
myCorpus <- Corpus(VectorSource(df$Genre))
myTDM <- DocumentTermMatrix(myCorpus, control = list(minWordLength = 1))
x = as.matrix(myTDM)
x
# Add processed Genre data to origional df
df = cbind(df, x)
# drop Genre from df
df$Genre <- NULL

Plot the relative proportions of movies having the top 10 most common genres.

# TODO: Select movies from top 10 most common genres and plot their relative proportions
# create a df to record frequency
df.count <- data.frame(matrix(ncol = 2, nrow = 30))
colnames(df.count) = c("genre", "count")
# fill in frequency
totalgenre = colnames(x)
i = 1
for (g in totalgenre){
  cur = sum(df[g])
  df.count[i,] = c(g, cur) 
  i = i + 1
}
df.count$frequency <- as.numeric(df.count$count) / dim(df)[1]
df.count[rev(order(df.count$frequency)),]
# plot relative proportions
df.count = df.count[rev(order(df.count$frequency)),]
qplot(x =df.count$genre[1:10], y =df.count$frequency[1:10])

Examine how the distribution of Runtime changes across genres for the top 10 most common genres.

# TODO: Plot Runtime distribution for top 10 most common genres
totalgenre = df.count$genre[1:10]
print(totalgenre)
 [1] "drama"       "comedy"      "short"       "romance"     "action"      "crime"      
 [7] "thriller"    "documentary" "adventure"   "animation"  
for (i in seq(1, 10)){
  print (ggplot(df[which(df[totalgenre[i]] == 1),], aes("", Runtime)) + geom_boxplot() + coord_flip() + scale_x_discrete("") + ggtitle(totalgenre[i]))
  z = df[which(df[totalgenre[i]] == 1),]
  print (totalgenre[i])
  print (median(z$Runtime))
  print(qplot(x = Runtime, data = z, binwidth = 4, main = totalgenre[i]))
}
[1] "drama"
[1] 96
[1] "comedy"
[1] 90
[1] "short"
[1] 10
[1] "romance"
[1] 96
[1] "action"
[1] 95
[1] "crime"
[1] 95
[1] "thriller"
[1] 95
[1] "documentary"
[1] 72
[1] "adventure"
[1] 95
[1] "animation"
[1] 7

dim(df)
[1] 40000    69
for (i in seq(1, 10)){
  print (ggplot(df[which(df[totalgenre[i]] == 1),], aes("", Runtime)) + geom_boxplot() + coord_flip() + scale_x_discrete("") + ggtitle(totalgenre[i]))
  z = df[which(df[totalgenre[i]] == 1),]
  print (totalgenre[i])
  print (median(z$Runtime))
  print(qplot(x = Runtime, data = z, binwidth = 4, main = totalgenre[i]))
}
[1] "drama"
[1] 96
[1] "comedy"
[1] 90
[1] "short"
[1] 10
[1] "romance"
[1] 96
[1] "action"
[1] 95
[1] "crime"
[1] 95
[1] "thriller"
[1] 95
[1] "documentary"
[1] 72
[1] "adventure"
[1] 95
[1] "animation"
[1] 7

dim(df)
[1] 40000    69

Q: Describe the interesting relationship(s) you observe. Are there any expected or unexpected trends that are evident?

A: As expected, for each genre, the movies run time are generally normally distributed, even though some are skewed, some are biomodeal with smaller bell shape in shorter runtime. The “short” has the lowest runtime, the drama has the longest, which is expected.

Some outliers (600min) is not exprected. They are pretty long movies. Biomodel normal distribution are not expected. I have not think out why there are two peaks.

4. Eliminate mismatched rows

The dataframe was put together by merging two different sources of data and it is possible that the merging process was inaccurate in some cases (the merge was done based on movie title, but there are cases of different movies with the same title). There are 3 columns that contain date information: Year (numeric year), Date (numeric year), and Released (string representation of the release date).

Find and remove all rows where you suspect a merge error occurred based on a mismatch between these variables. To make sure subsequent analysis and modeling work well, avoid removing more than 10% of the rows that have a Gross value present.

Note: Do not remove the rows with Gross == NA at this point, just use this a guideline.

# TODO: Remove rows with Year/Date/Released mismatch
dim(df)
[1] 40000    69
X= data.frame(do.call('rbind', strsplit(as.character(df$Released),'-',fixed=TRUE)))
df$releasedYear = X$X1
# Remove all whose Gross is NA
df_validGross = df[!is.na(df$Gross), ]
dim(df_validGross)
[1] 4558   70
# Keep the ones that are correct
df_validYearDate = df_validGross[abs(df_validGross$Year - df_validGross$Date) <= 1,]
#View(df_validYearDate[, c("Gross", "Year", "Date", "releasedYear")])
df_validYearDate$releasedYear = as.numeric(as.character(df_validYearDate$releasedYear))
df_validYearReleasedYear = df_validYearDate[abs(df_validYearDate$Year - df_validYearDate$releasedYear) <= 1,]
dim(df_validYearDate)
[1] 4431   70
dim(df_validYearReleasedYear)
[1] 4385   70

Q: What is your precise removal logic, and how many rows remain in the resulting dataset?

A: 1.For the same movies, the “Released”, “Date” and “Year” should show less than one year difference. If in any row that their year information in the above three columns are not the same, that indicates a mismatch, and should be removed. 2. There are 3518 rows left after removal, which has gross value.

5. Explore Gross revenue

For the commercial success of a movie, production houses want to maximize Gross revenue. Investigate if Gross revenue is related to Budget, Runtime or Genre in any way.

Note: To get a meaningful relationship, you may have to partition the movies into subsets such as short vs. long duration, or by genre, etc.

# TODO: Investigate if Gross Revenue is related to Budget, Runtime or Genre
# Gross Revenue vs Budget
qplot(log(df_validYearReleasedYear$Budget), log(df_validYearReleasedYear$Gross))

# Gross Revenue vs Runtime
qplot(df_validYearReleasedYear$Runtime, df_validYearReleasedYear$Gross)

# Gross Revenue vs Genre
for (i in seq(1, 10)){
  print (ggplot(df[which(df[totalgenre[i]] == 1),], aes("", Gross)) + geom_boxplot() + coord_flip() + scale_x_discrete("") + ggtitle(totalgenre[i]))
  z = df[which(df[totalgenre[i]] == 1),]
  print (totalgenre[i])
  print (median(z$Runtime))
  #print(qplot(x = Runtime, data = z, binwidth = 4, main = totalgenre[i]))
}
[1] "drama"
[1] 96
[1] "comedy"
[1] 90
[1] "short"
[1] 10
[1] "romance"
[1] 96
[1] "action"
[1] 95
[1] "crime"
[1] 95
[1] "thriller"
[1] 95
[1] "documentary"
[1] 72
[1] "adventure"
[1] 95
[1] "animation"
[1] 7

Q: Did you find any observable relationships or combinations of Budget/Runtime/Genre that result in high Gross revenue? If you divided the movies into different subsets, you may get different answers for them - point out interesting ones.

A: Budget increase as runtime increase The runtime for each Genere are different.Such as short is much shorter than others.

# TODO: Investigate if Gross Revenue is related to Release Month
X= data.frame(do.call('rbind', strsplit(as.character(df_validYearReleasedYear$Released),'-',fixed=TRUE)))
month = X$X2
transformedGross = log(df_validYearReleasedYear$Gross)
df_validYearReleasedYear = cbind(df_validYearReleasedYear, month, transformedGross)
names(df_validYearReleasedYear)
 [1] "Title"             "Year"              "Rated"             "Released"         
 [5] "Runtime"           "Director"          "Writer"            "Actors"           
 [9] "Plot"              "Language"          "Country"           "Awards"           
[13] "Poster"            "Metascore"         "imdbRating"        "imdbVotes"        
[17] "imdbID"            "Type"              "tomatoMeter"       "tomatoImage"      
[21] "tomatoRating"      "tomatoReviews"     "tomatoFresh"       "tomatoRotten"     
[25] "tomatoConsensus"   "tomatoUserMeter"   "tomatoUserRating"  "tomatoUserReviews"
[29] "tomatoURL"         "DVD"               "BoxOffice"         "Production"       
[33] "Website"           "Response"          "Budget"            "Domestic_Gross"   
[37] "Gross"             "Date"              "Decades"           "biography"        
[41] "documentary"       "romance"           "short"             "thriller"         
[45] "drama"             "war"               "comedy"            "horror"           
[49] "sci"               "adventure"         "family"            "history"          
[53] "crime"             "action"            "music"             "mystery"          
[57] "fantasy"           "sport"             "animation"         "musical"          
[61] "show"              "talk"              "adult"             "western"          
[65] "film"              "noir"              "reality"           "news"             
[69] "game"              "releasedYear"      "month"             "transformedGross" 
ggplot(df_validYearReleasedYear, aes(reorder(month, -transformedGross, median), transformedGross) ) + geom_boxplot() + coord_flip() + scale_x_discrete("transformedGross") 

6. Process Awards column

The variable Awards describes nominations and awards in text format. Convert it to 2 numeric columns, the first capturing the number of wins, and the second capturing nominations. Replace the Awards column with these new columns, and then study the relationship of Gross revenue with respect to them.

Note: The format of the Awards column is not standard; you may have to use regular expressions to find the relevant values. Try your best to process them, and you may leave the ones that don’t have enough information as NAs or set them to 0s.

# TODO: Convert Awards to 2 numeric columns: wins and nominations
rows_num = dim(df_validYearReleasedYear)[1]
df_validYearReleasedYear.awards = data.frame(matrix(ncol = 2, nrow = rows_num ))
colnames(df_validYearReleasedYear.awards) = c("wins", "nominations")
for (i in seq(1, rows_num)){
  content = unlist(strsplit(df_validYearReleasedYear[i, "Awards"], split=" "))
  if (length(content) < 2) next
  for (j in seq(1, length(content) - 1)){
    if (substr(content[j + 1], 1, 3) == "win") {
      win_num = as.numeric(content[j])
      df_validYearReleasedYear.awards[i, "wins"] = win_num
    }
    if (substr(content[j + 1], 1, 10) == "nomination") {
      nomination_num = as.numeric(content[j])
      df_validYearReleasedYear.awards[i, "nominations"] = nomination_num
    }
  }
}
df_validYearReleasedYear = cbind(df_validYearReleasedYear, df_validYearReleasedYear.awards)
# number of non N/A in nomination
colSums(!is.na(df_validYearReleasedYear.awards))[2]
nominations 
       3497 

Q: How did you construct your conversion mechanism? How many rows had valid/non-zero wins or nominations?

A: I wrote loops to access each cell in the Award column. Extract the number before " win“, and similarly extract the number before” nomination“. There are 11547 non NA rows in nonmination

# TODO: Plot Gross revenue against wins and nominations
plot(log(df_validYearReleasedYear$nominations),log(df_validYearReleasedYear$Gross))

plot(log(df_validYearReleasedYear$wins),log(df_validYearReleasedYear$Gross))

Q: How does the gross revenue vary by number of awards won and nominations received?

A: As the number of awards won and nominations increases, the gross revenue increase.

7. Movie ratings from IMDb and Rotten Tomatoes

There are several variables that describe ratings, including IMDb ratings (imdbRating represents average user ratings and imdbVotes represents the number of user ratings), and multiple Rotten Tomatoes ratings (represented by several variables pre-fixed by tomato). Read up on such ratings on the web (for example rottentomatoes.com/about and www.imdb.com/help/show_leaf?votestopfaq).

Investigate the pairwise relationships between these different descriptors using graphs.

# TODO: Illustrate how ratings from IMDb and Rotten Tomatoes are related
install.packages('GGally')
Error in install.packages : Updating loaded packages
library(GGally)
names(df)
 [1] "Title"             "Year"              "Rated"             "Released"         
 [5] "Runtime"           "Director"          "Writer"            "Actors"           
 [9] "Plot"              "Language"          "Country"           "Awards"           
[13] "Poster"            "Metascore"         "imdbRating"        "imdbVotes"        
[17] "imdbID"            "Type"              "tomatoMeter"       "tomatoImage"      
[21] "tomatoRating"      "tomatoReviews"     "tomatoFresh"       "tomatoRotten"     
[25] "tomatoConsensus"   "tomatoUserMeter"   "tomatoUserRating"  "tomatoUserReviews"
[29] "tomatoURL"         "DVD"               "BoxOffice"         "Production"       
[33] "Website"           "Response"          "Budget"            "Domestic_Gross"   
[37] "Gross"             "Date"              "Decades"           "biography"        
[41] "documentary"       "romance"           "short"             "thriller"         
[45] "drama"             "war"               "comedy"            "horror"           
[49] "sci"               "adventure"         "family"            "history"          
[53] "crime"             "action"            "music"             "mystery"          
[57] "fantasy"           "sport"             "animation"         "musical"          
[61] "show"              "talk"              "adult"             "western"          
[65] "film"              "noir"              "reality"           "news"             
[69] "game"              "releasedYear"     
rating_parameters = c("tomatoMeter", "tomatoRating", "tomatoReviews", "tomatoFresh", "tomatoRotten", "tomatoUserMeter", "tomatoUserRating", "imdbRating" ,"imdbVotes"  )
ggpairs(df, columns = rating_parameters )

 plot: [1,1] [=------------------------------------------------------------------]  1% est: 0s 
 plot: [1,2] [==-----------------------------------------------------------------]  2% est: 8s 
 plot: [1,3] [==-----------------------------------------------------------------]  4% est: 9s 
 plot: [1,4] [===----------------------------------------------------------------]  5% est:11s 
 plot: [1,5] [====---------------------------------------------------------------]  6% est:10s 
 plot: [1,6] [=====--------------------------------------------------------------]  7% est: 9s 
 plot: [1,7] [======-------------------------------------------------------------]  9% est: 9s 
 plot: [1,8] [=======------------------------------------------------------------] 10% est: 9s 
 plot: [1,9] [=======------------------------------------------------------------] 11% est: 8s 
 plot: [2,1] [========-----------------------------------------------------------] 12% est: 8s 
 plot: [2,2] [=========----------------------------------------------------------] 14% est: 8s 
 plot: [2,3] [==========---------------------------------------------------------] 15% est: 9s 
 plot: [2,4] [===========--------------------------------------------------------] 16% est: 8s 
 plot: [2,5] [============-------------------------------------------------------] 17% est: 8s 
 plot: [2,6] [============-------------------------------------------------------] 19% est: 8s 
 plot: [2,7] [=============------------------------------------------------------] 20% est: 8s 
 plot: [2,8] [==============-----------------------------------------------------] 21% est: 7s 
 plot: [2,9] [===============----------------------------------------------------] 22% est: 7s 
 plot: [3,1] [================---------------------------------------------------] 23% est: 7s 
 plot: [3,2] [=================--------------------------------------------------] 25% est: 7s 
 plot: [3,3] [=================--------------------------------------------------] 26% est: 7s 
 plot: [3,4] [==================-------------------------------------------------] 27% est: 7s 
 plot: [3,5] [===================------------------------------------------------] 28% est: 7s 
 plot: [3,6] [====================-----------------------------------------------] 30% est: 7s 
 plot: [3,7] [=====================----------------------------------------------] 31% est: 6s 
 plot: [3,8] [======================---------------------------------------------] 32% est: 6s 
 plot: [3,9] [======================---------------------------------------------] 33% est: 6s 
 plot: [4,1] [=======================--------------------------------------------] 35% est: 6s 
 plot: [4,2] [========================-------------------------------------------] 36% est: 6s 
 plot: [4,3] [=========================------------------------------------------] 37% est: 6s 
 plot: [4,4] [==========================-----------------------------------------] 38% est: 6s 
 plot: [4,5] [==========================-----------------------------------------] 40% est: 6s 
 plot: [4,6] [===========================----------------------------------------] 41% est: 5s 
 plot: [4,7] [============================---------------------------------------] 42% est: 5s 
 plot: [4,8] [=============================--------------------------------------] 43% est: 5s 
 plot: [4,9] [==============================-------------------------------------] 44% est: 5s 
 plot: [5,1] [===============================------------------------------------] 46% est: 5s 
 plot: [5,2] [===============================------------------------------------] 47% est: 5s 
 plot: [5,3] [================================-----------------------------------] 48% est: 5s 
 plot: [5,4] [=================================----------------------------------] 49% est: 5s 
 plot: [5,5] [==================================---------------------------------] 51% est: 5s 
 plot: [5,6] [===================================--------------------------------] 52% est: 5s 
 plot: [5,7] [====================================-------------------------------] 53% est: 4s 
 plot: [5,8] [====================================-------------------------------] 54% est: 4s 
 plot: [5,9] [=====================================------------------------------] 56% est: 4s 
 plot: [6,1] [======================================-----------------------------] 57% est: 4s 
 plot: [6,2] [=======================================----------------------------] 58% est: 4s 
 plot: [6,3] [========================================---------------------------] 59% est: 4s 
 plot: [6,4] [=========================================--------------------------] 60% est: 4s 
 plot: [6,5] [=========================================--------------------------] 62% est: 4s 
 plot: [6,6] [==========================================-------------------------] 63% est: 4s 
 plot: [6,7] [===========================================------------------------] 64% est: 3s 
 plot: [6,8] [============================================-----------------------] 65% est: 3s 
 plot: [6,9] [=============================================----------------------] 67% est: 3s 
 plot: [7,1] [=============================================----------------------] 68% est: 3s 
 plot: [7,2] [==============================================---------------------] 69% est: 3s 
 plot: [7,3] [===============================================--------------------] 70% est: 3s 
 plot: [7,4] [================================================-------------------] 72% est: 3s 
 plot: [7,5] [=================================================------------------] 73% est: 3s 
 plot: [7,6] [==================================================-----------------] 74% est: 3s 
 plot: [7,7] [==================================================-----------------] 75% est: 2s 
 plot: [7,8] [===================================================----------------] 77% est: 2s 
 plot: [7,9] [====================================================---------------] 78% est: 2s 
 plot: [8,1] [=====================================================--------------] 79% est: 2s 
 plot: [8,2] [======================================================-------------] 80% est: 2s 
 plot: [8,3] [=======================================================------------] 81% est: 2s 
 plot: [8,4] [=======================================================------------] 83% est: 2s 
 plot: [8,5] [========================================================-----------] 84% est: 2s 
 plot: [8,6] [=========================================================----------] 85% est: 2s 
 plot: [8,7] [==========================================================---------] 86% est: 1s 
 plot: [8,8] [===========================================================--------] 88% est: 1s 
 plot: [8,9] [============================================================-------] 89% est: 1s 
 plot: [9,1] [============================================================-------] 90% est: 1s 
 plot: [9,2] [=============================================================------] 91% est: 1s 
 plot: [9,3] [==============================================================-----] 93% est: 1s 
 plot: [9,4] [===============================================================----] 94% est: 1s 
 plot: [9,5] [================================================================---] 95% est: 1s 
 plot: [9,6] [=================================================================--] 96% est: 0s 
 plot: [9,7] [=================================================================--] 98% est: 0s 
 plot: [9,8] [==================================================================-] 99% est: 0s 
 plot: [9,9] [===================================================================]100% est: 0s 
                                                                                               

Q: Comment on the similarities and differences between the user ratings of IMDb and the critics ratings of Rotten Tomatoes.

A: (1). ImbdRatings is high similarities with “tomatoMeter”, “tomatoRating”, “tomatoUserMeter”, “tomatoUserRating”; (2). ImbdRatings has slight similarities with “tomatoReviews”, “tomatoFresh”; (3).ImbdRatings is different with “tomatoRotten”;

8. Ratings and awards

These ratings typically reflect the general appeal of the movie to the public or gather opinions from a larger body of critics. Whereas awards are given by professional societies that may evaluate a movie on specific attributes, such as artistic performance, screenplay, sound design, etc.

Study the relationship between ratings and awards using graphs (awards here refers to wins and/or nominations).

# TODO: Show how ratings and awards are related
df = df2
names(df)
 [1] "Title"             "Year"              "Rated"             "Released"         
 [5] "Runtime"           "Genre"             "Director"          "Writer"           
 [9] "Actors"            "Plot"              "Language"          "Country"          
[13] "Awards"            "Poster"            "Metascore"         "imdbRating"       
[17] "imdbVotes"         "imdbID"            "Type"              "tomatoMeter"      
[21] "tomatoImage"       "tomatoRating"      "tomatoReviews"     "tomatoFresh"      
[25] "tomatoRotten"      "tomatoConsensus"   "tomatoUserMeter"   "tomatoUserRating" 
[29] "tomatoUserReviews" "tomatoURL"         "DVD"               "BoxOffice"        
[33] "Production"        "Website"           "Response"          "Budget"           
[37] "Domestic_Gross"    "Gross"             "Date"             
# add wins and nominations to orgional df
rows_num = dim(df)[1]
df.awards = data.frame(matrix(ncol = 2, nrow = rows_num ))
colnames(df.awards) = c("wins", "nominations")
for (i in seq(1, rows_num)){
  content = unlist(strsplit(df[i, "Awards"], split=" "))
  if (length(content) < 2) next
  for (j in seq(1, length(content) - 1)){
    if (substr(content[j + 1], 1, 3) == "win") {
      win_num = as.numeric(content[j])
      df.awards[i, "wins"] = win_num
    }
    if (substr(content[j + 1], 1, 10) == "nomination") {
      nomination_num = as.numeric(content[j])
      df.awards[i, "nominations"] = nomination_num
    }
  }
}
df = cbind(df, df.awards)
names(df)
 [1] "Title"             "Year"              "Rated"             "Released"         
 [5] "Runtime"           "Genre"             "Director"          "Writer"           
 [9] "Actors"            "Plot"              "Language"          "Country"          
[13] "Awards"            "Poster"            "Metascore"         "imdbRating"       
[17] "imdbVotes"         "imdbID"            "Type"              "tomatoMeter"      
[21] "tomatoImage"       "tomatoRating"      "tomatoReviews"     "tomatoFresh"      
[25] "tomatoRotten"      "tomatoConsensus"   "tomatoUserMeter"   "tomatoUserRating" 
[29] "tomatoUserReviews" "tomatoURL"         "DVD"               "BoxOffice"        
[33] "Production"        "Website"           "Response"          "Budget"           
[37] "Domestic_Gross"    "Gross"             "Date"              "wins"             
[41] "nominations"      
# find relationship
parameters = c("tomatoMeter", "tomatoRating", "tomatoReviews", "tomatoFresh", "tomatoRotten", "tomatoUserMeter", "tomatoUserRating", "imdbRating" ,"imdbVotes", "wins", "nominations" )
ggpairs(df, columns = parameters, title = "fig1" )

 plot: [1,1] [=------------------------------------------------------------------]  1% est: 0s 
 plot: [1,2] [=------------------------------------------------------------------]  2% est: 6s 
 plot: [1,3] [==-----------------------------------------------------------------]  2% est: 8s 
 plot: [1,4] [==-----------------------------------------------------------------]  3% est: 8s 
 plot: [1,5] [===----------------------------------------------------------------]  4% est: 9s 
 plot: [1,6] [===----------------------------------------------------------------]  5% est: 9s 
 plot: [1,7] [====---------------------------------------------------------------]  6% est: 9s 
 plot: [1,8] [====---------------------------------------------------------------]  7% est: 9s 
 plot: [1,9] [=====--------------------------------------------------------------]  7% est: 9s 
 plot: [1,10] [=====-------------------------------------------------------------]  8% est: 9s 
 plot: [1,11] [======------------------------------------------------------------]  9% est: 9s 
 plot: [2,1] [=======------------------------------------------------------------] 10% est: 9s 
 plot: [2,2] [=======------------------------------------------------------------] 11% est: 9s 
 plot: [2,3] [========-----------------------------------------------------------] 12% est: 9s 
 plot: [2,4] [========-----------------------------------------------------------] 12% est: 9s 
 plot: [2,5] [=========----------------------------------------------------------] 13% est: 9s 
 plot: [2,6] [=========----------------------------------------------------------] 14% est: 9s 
 plot: [2,7] [==========---------------------------------------------------------] 15% est: 9s 
 plot: [2,8] [===========--------------------------------------------------------] 16% est: 9s 
 plot: [2,9] [===========--------------------------------------------------------] 17% est: 9s 
 plot: [2,10] [===========-------------------------------------------------------] 17% est: 9s 
 plot: [2,11] [============------------------------------------------------------] 18% est: 9s 
 plot: [3,1] [=============------------------------------------------------------] 19% est: 8s 
 plot: [3,2] [=============------------------------------------------------------] 20% est: 9s 
 plot: [3,3] [==============-----------------------------------------------------] 21% est: 9s 
 plot: [3,4] [==============-----------------------------------------------------] 21% est: 9s 
 plot: [3,5] [===============----------------------------------------------------] 22% est: 9s 
 plot: [3,6] [================---------------------------------------------------] 23% est: 9s 
 plot: [3,7] [================---------------------------------------------------] 24% est: 8s 
 plot: [3,8] [=================--------------------------------------------------] 25% est: 8s 
 plot: [3,9] [=================--------------------------------------------------] 26% est: 8s 
 plot: [3,10] [=================-------------------------------------------------] 26% est: 8s 
 plot: [3,11] [==================------------------------------------------------] 27% est: 8s 
 plot: [4,1] [===================------------------------------------------------] 28% est: 8s 
 plot: [4,2] [===================------------------------------------------------] 29% est: 8s 
 plot: [4,3] [====================-----------------------------------------------] 30% est: 8s 
 plot: [4,4] [====================-----------------------------------------------] 31% est: 8s 
 plot: [4,5] [=====================----------------------------------------------] 31% est: 8s 
 plot: [4,6] [======================---------------------------------------------] 32% est: 8s 
 plot: [4,7] [======================---------------------------------------------] 33% est: 8s 
 plot: [4,8] [=======================--------------------------------------------] 34% est: 8s 
 plot: [4,9] [=======================--------------------------------------------] 35% est: 8s 
 plot: [4,10] [=======================-------------------------------------------] 36% est: 7s 
 plot: [4,11] [========================------------------------------------------] 36% est: 7s 
 plot: [5,1] [=========================------------------------------------------] 37% est: 7s 
 plot: [5,2] [=========================------------------------------------------] 38% est: 7s 
 plot: [5,3] [==========================-----------------------------------------] 39% est: 7s 
 plot: [5,4] [===========================----------------------------------------] 40% est: 7s 
 plot: [5,5] [===========================----------------------------------------] 40% est: 7s 
 plot: [5,6] [============================---------------------------------------] 41% est: 7s 
 plot: [5,7] [============================---------------------------------------] 42% est: 7s 
 plot: [5,8] [=============================--------------------------------------] 43% est: 7s 
 plot: [5,9] [=============================--------------------------------------] 44% est: 7s 
 plot: [5,10] [=============================-------------------------------------] 45% est: 7s 
 plot: [5,11] [==============================------------------------------------] 45% est: 6s 
 plot: [6,1] [===============================------------------------------------] 46% est: 6s 
 plot: [6,2] [================================-----------------------------------] 47% est: 6s 
 plot: [6,3] [================================-----------------------------------] 48% est: 6s 
 plot: [6,4] [=================================----------------------------------] 49% est: 6s 
 plot: [6,5] [=================================----------------------------------] 50% est: 6s 
 plot: [6,6] [==================================---------------------------------] 50% est: 6s 
 plot: [6,7] [==================================---------------------------------] 51% est: 6s 
 plot: [6,8] [===================================--------------------------------] 52% est: 6s 
 plot: [6,9] [===================================--------------------------------] 53% est: 6s 
 plot: [6,10] [===================================-------------------------------] 54% est: 6s 
 plot: [6,11] [====================================------------------------------] 55% est: 6s 
 plot: [7,1] [=====================================------------------------------] 55% est: 6s 
 plot: [7,2] [======================================-----------------------------] 56% est: 6s 
 plot: [7,3] [======================================-----------------------------] 57% est: 5s 
 plot: [7,4] [=======================================----------------------------] 58% est: 5s 
 plot: [7,5] [=======================================----------------------------] 59% est: 5s 
 plot: [7,6] [========================================---------------------------] 60% est: 5s 
 plot: [7,7] [========================================---------------------------] 60% est: 5s 
 plot: [7,8] [=========================================--------------------------] 61% est: 5s 
 plot: [7,9] [==========================================-------------------------] 62% est: 5s 
 plot: [7,10] [=========================================-------------------------] 63% est: 5s 
 plot: [7,11] [==========================================------------------------] 64% est: 5s 
 plot: [8,1] [===========================================------------------------] 64% est: 5s 
 plot: [8,2] [============================================-----------------------] 65% est: 5s 
 plot: [8,3] [============================================-----------------------] 66% est: 4s 
 plot: [8,4] [=============================================----------------------] 67% est: 4s 
 plot: [8,5] [=============================================----------------------] 68% est: 4s 
 plot: [8,6] [==============================================---------------------] 69% est: 4s 
 plot: [8,7] [===============================================--------------------] 69% est: 4s 
 plot: [8,8] [===============================================--------------------] 70% est: 4s 
 plot: [8,9] [================================================-------------------] 71% est: 4s 
 plot: [8,10] [===============================================-------------------] 72% est: 4s 
 plot: [8,11] [================================================------------------] 73% est: 4s 
 plot: [9,1] [=================================================------------------] 74% est: 4s 
 plot: [9,2] [==================================================-----------------] 74% est: 3s 
 plot: [9,3] [==================================================-----------------] 75% est: 3s 
 plot: [9,4] [===================================================----------------] 76% est: 3s 
 plot: [9,5] [===================================================----------------] 77% est: 3s 
 plot: [9,6] [====================================================---------------] 78% est: 3s 
 plot: [9,7] [=====================================================--------------] 79% est: 3s 
 plot: [9,8] [=====================================================--------------] 79% est: 3s 
 plot: [9,9] [======================================================-------------] 80% est: 3s 
 plot: [9,10] [=====================================================-------------] 81% est: 3s 
 plot: [9,11] [======================================================------------] 82% est: 2s 
 plot: [10,1] [=======================================================-----------] 83% est: 2s 
 plot: [10,2] [=======================================================-----------] 83% est: 2s 
 plot: [10,3] [========================================================----------] 84% est: 2s 
 plot: [10,4] [========================================================----------] 85% est: 2s 
 plot: [10,5] [=========================================================---------] 86% est: 2s 
 plot: [10,6] [=========================================================---------] 87% est: 2s 
 plot: [10,7] [==========================================================--------] 88% est: 2s 
 plot: [10,8] [==========================================================--------] 88% est: 2s 
 plot: [10,9] [===========================================================-------] 89% est: 1s 
 plot: [10,10] [===========================================================------] 90% est: 1s 
 plot: [10,11] [===========================================================------] 91% est: 1s 
 plot: [11,1] [=============================================================-----] 92% est: 1s 
 plot: [11,2] [=============================================================-----] 93% est: 1s 
 plot: [11,3] [==============================================================----] 93% est: 1s 
 plot: [11,4] [==============================================================----] 94% est: 1s 
 plot: [11,5] [===============================================================---] 95% est: 1s 
 plot: [11,6] [===============================================================---] 96% est: 1s 
 plot: [11,7] [================================================================--] 97% est: 0s 
 plot: [11,8] [================================================================--] 98% est: 0s 
 plot: [11,9] [=================================================================-] 98% est: 0s 
 plot: [11,10] [================================================================-] 99% est: 0s 
 plot: [11,11] [=================================================================]100% est: 0s 
                                                                                               

Q: How good are these ratings in terms of predicting the success of a movie in winning awards or nominations? Is there a high correlation between two variables?

A: 1. Some of those ratings are highly correlated with the wins and nominations, such as tomatoReviews, tomatoFresh, imbdVotes and imbdVoting. tomatoRotten are not positively related to the awards which is expected. Some of those ratings are not very good predicators, such as tomatoMeters, tomatoRatins, since they have low correlations to awards. 2. Norminations and wins are highly related. Correlation is 0.819 ## 9. Expected insights

Come up with two new insights (backed up by data and graphs) that is expected. Here “new” means insights that are not an immediate consequence of one of the above tasks. You may use any of the columns already explored above or a different one in the dataset, such as Title, Actors, etc.

# TODO: Find and illustrate two expected insights
# Expeact Ingisht # 1
# deliminite
library(tm)
myCorpus <- Corpus(VectorSource(df$Country))
myTDM <- DocumentTermMatrix(myCorpus, control = list(minWordLength = 1))
countries = as.matrix(myTDM)
df = cbind(df, countries)
df.countries <- data.frame(matrix(ncol = 2, nrow = 173))
colnames(df.countries) = c("countryname", "count")
# fill in frequency
totalcountries = colnames(countries)
i = 1
for (c in totalcountries){
  cur = sum(df[c])
  df.countries [i,] = c(c, cur) 
  i = i + 1
}
df.countries $freq <- as.numeric(df.countries $count) / dim(df)[1]
df.countries = df.countries[rev(order(df.countries$freq)),]
df.topcountries = df.countries[0:10 ,]
View(df.topcountries)
# Expected insight # 2
View(df_validYearReleasedYear[, c("Gross", "Decades")])
qplot(df_validYearReleasedYear$Decades, df_validYearReleasedYear$Gross)

ggplot(df_validYearReleasedYear, aes(reorder(Decades, -transformedGross, median), transformedGross) ) + geom_boxplot() + coord_flip() + scale_x_discrete("transformedGross") 

Q: Expected insight #1.

A: USA produced much larger amount of movies than other countries. The following countries are: France, Germany, Canada …

Q: Expected insight #2.

A: Recent years has larger Gross median than those old years.

10. Unexpected insight

Come up with one new insight (backed up by data and graphs) that is unexpected at first glance and do your best to motivate it. Same instructions apply as the previous task.

# TODO: Find and illustrate one unexpected insight
qplot( df_validYearReleasedYear$imdbRating, df_validYearReleasedYear$Gross)

Q: Unexpected insight.

A: The the first thought, I was not expect that the ones with high imbd ratings could have very low gross. But after thinking again, I found it is reasonable to some movies are good, but are not well commercialzied and on the business side, they were not successful.

LS0tCnRpdGxlOiAnUHJvamVjdCAxOiBFeHBsb3JlIGFuZCBQcmVwYXJlIERhdGEnCnN1YnRpdGxlOiB8LQogIENTRTYyNDIgLSBEYXRhIGFuZCBWaXN1YWwgQW5hbHl0aWNzIC0gRmFsbCAyMDE3CiAgRHVlOiBTdW5kYXksIE9jdG9iZXIgMTUsIDIwMTcgYXQgMTE6NTkgUE0gVVRDLTEyOjAwIG9uIFQtU3F1YXJlCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKX05vdGU6IFRoaXMgcHJvamVjdCBpbnZvbHZlcyBnZXR0aW5nIGRhdGEgcmVhZHkgZm9yIGFuYWx5c2lzIGFuZCBkb2luZyBzb21lIHByZWxpbWluYXJ5IGludmVzdGlnYXRpb25zLiBQcm9qZWN0IDIgd2lsbCBpbnZvbHZlIG1vZGVsaW5nIGFuZCBwcmVkaWN0aW9ucyBvbiB0aGUgc2FtZSBkYXRhc2V0LCBhbmQgd2lsbCBiZSByZWxlYXNlZCBhdCBhIGxhdGVyIGRhdGUuIEJvdGggcHJvamVjdHMgd2lsbCBoYXZlIGVxdWFsIHdlaWdodGFnZSB0b3dhcmRzIHlvdXIgZ3JhZGUuIFlvdSBtYXkgcmV1c2Ugc29tZSBvZiB0aGUgcHJlcHJvY2Vzc2luZy9hbmFseXNpcyBzdGVwcyBmcm9tIFByb2plY3QgMSBpbiBQcm9qZWN0IDIuXwoKIyBEYXRhCgpJbiB0aGlzIHByb2plY3QsIHlvdSB3aWxsIGV4cGxvcmUgYSBkYXRhc2V0IHRoYXQgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgbW92aWVzLCBpbmNsdWRpbmcgcmF0aW5ncywgYnVkZ2V0LCBncm9zcyByZXZlbnVlIGFuZCBvdGhlciBhdHRyaWJ1dGVzLiBJdCB3YXMgcHJlcGFyZWQgYnkgRHIuIEd1eSBMZWJhbm9uLCBhbmQgaGVyZSBpcyBoaXMgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGFzZXQ6Cgo+IFRoZSBmaWxlIFtgbW92aWVzX21lcmdlZGBdKGh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS9jb250ZW50LnVkYWNpdHktZGF0YS5jb20vY291cnNlcy9ndC1jczYyNDIvcHJvamVjdC9tb3ZpZXNfbWVyZ2VkKSBjb250YWlucyBhIGRhdGFmcmFtZSB3aXRoIHRoZSBzYW1lIG5hbWUgdGhhdCBoYXMgNDBLIHJvd3MgYW5kIDM5IGNvbHVtbnMuIEVhY2ggcm93IHJlcHJlc2VudHMgYSBtb3ZpZSB0aXRsZSBhbmQgZWFjaCBjb2x1bW4gcmVwcmVzZW50cyBhIGRlc2NyaXB0b3Igc3VjaCBhcyBgVGl0bGVgLCBgQWN0b3JzYCwgYW5kIGBCdWRnZXRgLiBJIGNvbGxlY3RlZCB0aGUgZGF0YSBieSBxdWVyeWluZyBJTURi4oCZcyBBUEkgKHNlZSBbd3d3Lm9tZGJhcGkuY29tXShodHRwOi8vd3d3Lm9tZGJhcGkuY29tLykpIGFuZCBqb2luaW5nIGl0IHdpdGggYSBzZXBhcmF0ZSBkYXRhc2V0IG9mIG1vdmllIGJ1ZGdldHMgYW5kIGdyb3NzIGVhcm5pbmdzICh1bmtub3duIHRvIHlvdSkuIFRoZSBqb2luIGtleSB3YXMgdGhlIG1vdmllIHRpdGxlLiBUaGlzIGRhdGEgaXMgYXZhaWxhYmxlIGZvciBwZXJzb25hbCB1c2UsIGJ1dCBJTURi4oCZcyB0ZXJtcyBvZiBzZXJ2aWNlIGRvIG5vdCBhbGxvdyBpdCB0byBiZSB1c2VkIGZvciBjb21tZXJjaWFsIHB1cnBvc2VzIG9yIGZvciBjcmVhdGluZyBhIGNvbXBldGluZyByZXBvc2l0b3J5LgoKIyBPYmplY3RpdmUKCllvdXIgZ29hbCBpcyB0byBpbnZlc3RpZ2F0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIG1vdmllIGRlc2NyaXB0b3JzIGFuZCB0aGUgYm94IG9mZmljZSBzdWNjZXNzIG9mIG1vdmllcywgYXMgcmVwcmVzZW50ZWQgYnkgdGhlIHZhcmlhYmxlIGBHcm9zc2AuIFRoaXMgdGFzayBpcyBleHRyZW1lbHkgaW1wb3J0YW50IGFzIGl0IGNhbiBoZWxwIGEgc3R1ZGlvIGRlY2lkZSB3aGljaCB0aXRsZXMgdG8gZnVuZCBmb3IgcHJvZHVjdGlvbiwgaG93IG11Y2ggdG8gYmlkIG9uIHByb2R1Y2VkIG1vdmllcywgd2hlbiB0byByZWxlYXNlIGEgdGl0bGUsIGhvdyBtdWNoIHRvIGludmVzdCBpbiBtYXJrZXRpbmcgYW5kIFBSLCBldGMuIFRoaXMgaW5mb3JtYXRpb24gaXMgbW9zdCB1c2VmdWwgYmVmb3JlIGEgdGl0bGUgaXMgcmVsZWFzZWQsIGJ1dCBpdCBpcyBzdGlsbCB2ZXJ5IHZhbHVhYmxlIGFmdGVyIHRoZSBtb3ZpZSBpcyBhbHJlYWR5IHJlbGVhc2VkIHRvIHRoZSBwdWJsaWMgKGZvciBleGFtcGxlIGl0IGNhbiBhZmZlY3QgYWRkaXRpb25hbCBtYXJrZXRpbmcgc3BlbmQgb3IgaG93IG11Y2ggYSBzdHVkaW8gc2hvdWxkIG5lZ290aWF0ZSB3aXRoIG9uLWRlbWFuZCBzdHJlYW1pbmcgY29tcGFuaWVzIGZvciDigJxzZWNvbmQgd2luZG934oCdIHN0cmVhbWluZyByaWdodHMpLgoKIyBJbnN0cnVjdGlvbnMKClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBPcGVuIHRoaXMgZmlsZSBpbiBSU3R1ZGlvIHRvIGdldCBzdGFydGVkLgoKV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiBUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgpgYGB7cn0KeCA9IDE6MTAKcHJpbnQoeF4yKQpgYGAKClBsb3RzIGFwcGVhciBpbmxpbmUgdG9vOgpgYGB7cn0KcGxvdCh4LCB4XjIsICdvJykKYGBgCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4gRW50ZXIgc29tZSBSIGNvZGUgYW5kIHJ1biBpdC4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ21kK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCgpQbGVhc2UgY29tcGxldGUgYWxsIHRoZSB0YXNrcyBiZWxvdyBieSBpbXBsZW1lbnRpbmcgY29kZSBjaHVua3MgdGhhdCBoYXZlIGEgYFRPRE9gIGNvbW1lbnQgaW4gdGhlbSwgcnVubmluZyBhbGwgY29kZSBjaHVua3Mgc28gdGhhdCBvdXRwdXQgYW5kIHBsb3RzIGFyZSBkaXNwbGF5ZWQsIGFuZCB0eXBpbmcgaW4gYW5zd2VycyB0byBlYWNoIHF1ZXN0aW9uICgqKlE6KiogLi4uKSBuZXh0IHRvL2JlbG93IHRoZSBjb3JyZXNwb25kaW5nIGFuc3dlciBwcm9tcHQgKCoqQToqKikuIEZlZWwgZnJlZSB0byBhZGQgY29kZSBjaHVua3Mvc2hvdyBhZGRpdGlvbmFsIG91dHB1dCB0byBzdXBwb3J0IGFueSBvZiB0aGUgYW5zd2Vycy4KCldoZW4geW91IGFyZSBkb25lLCB5b3Ugd2lsbCBuZWVkIHRvIHN1Ym1pdCB0aGUgZmluYWwgUiBtYXJrZG93biBmaWxlIChhcyAqKnByMS5SbWQqKikgd2l0aCBhbGwgY29kZSBjaHVua3MgaW1wbGVtZW50ZWQgYW5kIGV4ZWN1dGVkLCBhbmQgYWxsIHRleHQgcmVzcG9uc2VzIHdyaXR0ZW4gaW4uIFlvdSBhbHNvIG5lZWQgdG8gc3VibWl0IGEgUERGIGV4cG9ydCBvZiB0aGUgbWFya2Rvd24gZmlsZSAoYXMgKipwcjEucGRmKiopLCB3aGljaCBzaG91bGQgc2hvdyB5b3VyIGNvZGUsIG91dHB1dCwgcGxvdHMgYW5kIHdyaXR0ZW4gcmVzcG9uc2VzLS10aGlzIHdpbGwgYmUgeW91ciBwcm9qZWN0IHJlcG9ydC4gQ29tcHJlc3MgdGhlc2UgdHdvIGZpbGVzIGludG8gYSBzaW5nbGUgLnppcCBhcmNoaXZlIGFuZCB1cGxvYWQgaXQgb24gVC1TcXVhcmUuCgojIFNldHVwCgojIyBMb2FkIGRhdGEKCk1ha2Ugc3VyZSB5b3UndmUgZG93bmxvYWRlZCB0aGUgW2Btb3ZpZXNfbWVyZ2VkYF0oaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL2NvbnRlbnQudWRhY2l0eS1kYXRhLmNvbS9jb3Vyc2VzL2d0LWNzNjI0Mi9wcm9qZWN0L21vdmllc19tZXJnZWQpIGZpbGUgYW5kIGl0IGlzIGluIHRoZSBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LiBOb3cgbG9hZCBpdCBpbnRvIG1lbW9yeToKCmBgYHtyfQpsb2FkKCdtb3ZpZXNfbWVyZ2VkJykKY2F0KCJEYXRhc2V0IGhhcyIsIGRpbShtb3ZpZXNfbWVyZ2VkKVsxXSwgInJvd3MgYW5kIiwgZGltKG1vdmllc19tZXJnZWQpWzJdLCAiY29sdW1ucyIsIGVuZD0iXG4iLCBmaWxlPSIiKQpgYGAKClRoaXMgY3JlYXRlcyBhbiBvYmplY3Qgb2YgdGhlIHNhbWUgbmFtZSAoYG1vdmllc19tZXJnZWRgKS4gRm9yIGNvbnZlbmllbmNlLCB5b3UgY2FuIGNvcHkgaXQgdG8gYGRmYCBhbmQgc3RhcnQgdXNpbmcgaXQ6CgpgYGB7cn0KZGYgPSBtb3ZpZXNfbWVyZ2VkCmNhdCgiQ29sdW1uIG5hbWVzOiIsIGVuZD0iXG4iLCBmaWxlPSIiKQpgYGAKCiMjIExvYWQgUiBwYWNrYWdlcwoKTG9hZCBhbnkgUiBwYWNrYWdlcyB0aGF0IHlvdSB3aWxsIG5lZWQgdG8gdXNlLiBZb3UgY2FuIGNvbWUgYmFjayB0byB0aGlzIGNodW5rLCBlZGl0IGl0IGFuZCByZS1ydW4gdG8gbG9hZCBhbnkgYWRkaXRpb25hbCBwYWNrYWdlcyBsYXRlci4KCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoR0dhbGx5KQpgYGAKCklmIHlvdSBhcmUgbG9hZGluZyBhbnkgbm9uLXN0YW5kYXJkIHBhY2thZ2VzIChvbmVzIHRoYXQgaGF2ZSBub3QgYmVlbiBkaXNjdXNzZWQgaW4gY2xhc3Mgb3IgZXhwbGljaXRseSBhbGxvd2VkIGZvciB0aGlzIHByb2plY3QpLCBwbGVhc2UgbWVudGlvbiB0aGVtIGJlbG93LiBJbmNsdWRlIGFueSBzcGVjaWFsIGluc3RydWN0aW9ucyBpZiB0aGV5IGNhbm5vdCBiZSBpbnN0YWxsZWQgdXNpbmcgdGhlIHJlZ3VsYXIgYGluc3RhbGwucGFja2FnZXMoJzxwa2cgbmFtZT4nKWAgY29tbWFuZC4KCioqTm9uLXN0YW5kYXJkIHBhY2thZ2VzIHVzZWQqKjogTm9uZQoKIyBUYXNrcwoKRWFjaCB0YXNrIGJlbG93IGlzIHdvcnRoICoqMTAqKiBwb2ludHMsIGFuZCBpcyBtZWFudCB0byBiZSBwZXJmb3JtZWQgc2VxdWVudGlhbGx5LCBpLmUuIGRvIHN0ZXAgMiBhZnRlciB5b3UgaGF2ZSBwcm9jZXNzZWQgdGhlIGRhdGEgYXMgZGVzY3JpYmVkIGluIHN0ZXAgMS4gVG90YWwgcG9pbnRzOiAqKjEwMCoqCgpDb21wbGV0ZSBlYWNoIHRhc2sgYnkgaW1wbGVtZW50aW5nIGNvZGUgY2h1bmtzIGFzIGRlc2NyaWJlZCBieSBgVE9ET2AgY29tbWVudHMsIGFuZCBieSByZXNwb25kaW5nIHRvIHF1ZXN0aW9ucyAoIioqUSoqOiIpIHdpdGggd3JpdHRlbiBhbnN3ZXJzICgiKipBKio6IikuIElmIHlvdSBhcmUgdW5hYmxlIHRvIGZpbmQgYSBtZWFuaW5nZnVsIG9yIHN0cm9uZyByZWxhdGlvbnNoaXAgaW4gYW55IG9mIHRoZSBjYXNlcyB3aGVuIHJlcXVlc3RlZCwgZXhwbGFpbiB3aHkgbm90IGJ5IHJlZmVycmluZyB0byBhcHByb3ByaWF0ZSBwbG90cy9zdGF0aXN0aWNzLgoKSXQgaXMgb2theSB0byBoYW5kbGUgbWlzc2luZyB2YWx1ZXMgYmVsb3cgYnkgb21pc3Npb24sIGJ1dCBwbGVhc2Ugb21pdCBhcyBsaXR0bGUgYXMgcG9zc2libGUuIEl0IGlzIHdvcnRod2hpbGUgdG8gaW52ZXN0IGluIHJldXNhYmxlIGFuZCBjbGVhciBjb2RlIGFzIHlvdSBtYXkgbmVlZCB0byB1c2UgaXQgb3IgbW9kaWZ5IGl0IGluIHByb2plY3QgMi4KCiMjIDEuIFJlbW92ZSBub24tbW92aWUgcm93cwoKVGhlIHZhcmlhYmxlIGBUeXBlYCBjYXB0dXJlcyB3aGV0aGVyIHRoZSByb3cgaXMgYSBtb3ZpZSwgYSBUViBzZXJpZXMsIG9yIGEgZ2FtZS4gUmVtb3ZlIGFsbCByb3dzIGZyb20gYGRmYCB0aGF0IGRvIG5vdCBjb3JyZXNwb25kIHRvIG1vdmllcy4KCmBgYHtyfQojIFRPRE86IFJlbW92ZSBhbGwgcm93cyBmcm9tIGRmIHRoYXQgZG8gbm90IGNvcnJlc3BvbmQgdG8gbW92aWVzCmRmMiA8LSBkZltkZiRUeXBlID09ICJtb3ZpZSIsXQpkZiA9IGRmMgpgYGAKCioqUSoqOiBIb3cgbWFueSByb3dzIGFyZSBsZWZ0IGFmdGVyIHJlbW92YWw/IF9FbnRlciB5b3VyIHJlc3BvbnNlIGJlbG93Ll8KCioqQSoqOiA0MDAwMCByb3dzIGFyZSBsZWZ0CgojIyAyLiBQcm9jZXNzIGBSdW50aW1lYCBjb2x1bW4KClRoZSB2YXJpYWJsZSBgUnVudGltZWAgcmVwcmVzZW50cyB0aGUgbGVuZ3RoIG9mIHRoZSB0aXRsZSBhcyBhIHN0cmluZy4gV3JpdGUgUiBjb2RlIHRvIGNvbnZlcnQgaXQgdG8gYSBudW1lcmljIHZhbHVlIChpbiBtaW51dGVzKSBhbmQgcmVwbGFjZSBgZGYkUnVudGltZWAgd2l0aCB0aGUgbmV3IG51bWVyaWMgY29sdW1uLgoKYGBge3J9CiMgVE9ETzogUmVwbGFjZSBkZiRSdW50aW1lIHdpdGggYSBudW1lcmljIGNvbHVtbiBjb250YWluaW5nIHRoZSBydW50aW1lIGluIG1pbnV0ZXMKcm93c19udW0gPSBkaW0oZGYpWzFdCmZvciAoaSBpbiBzZXEoMSwgcm93c19udW0pKXsKICBjb250ZW50ID0gdW5saXN0KHN0cnNwbGl0KGRmW2ksICJSdW50aW1lIl0sIHNwbGl0PSIgIikpCiAgaWYgKGNvbnRlbnQgPT0gIk4vQSIpewogICAgZGZbaSwiUnVudGltZSJdID0gMCAKICAgIG5leHQoKQogIH0KICAgIAogIGN1ciA9IDAKICBmb3IgKGogaW4gc2VxKDEsIGxlbmd0aChjb250ZW50KSAtIDEpKXsKICAgIGlmIChzdWJzdHIoY29udGVudFtqICsgMV0sMSwgMSkgPT0gImgiKXsKICAgICAgY3VyID0gY3VyICsgYXMubnVtZXJpYyhjb250ZW50W2pdKSAqIDYwCiAgICB9CiAgICAKICAgIGlmIChzdWJzdHIoY29udGVudFtqICsgMV0sMSwgMykgPT0gIm1pbiIpewogICAgICBjdXIgPSBjdXIgKyBhcy5udW1lcmljKGNvbnRlbnRbal0pIAogICAgfQogIH0KICBkZltpLCJSdW50aW1lIl09IGN1cgp9CmRmJFJ1bnRpbWUgPWFzLm51bWVyaWMoKGRmJFJ1bnRpbWUpKQpgYGAKCk5vdyBpbnZlc3RpZ2F0ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGBSdW50aW1lYCB2YWx1ZXMgYW5kIGhvdyBpdCBjaGFuZ2VzIG92ZXIgeWVhcnMgKHZhcmlhYmxlIGBZZWFyYCwgd2hpY2ggeW91IGNhbiBidWNrZXQgaW50byBkZWNhZGVzKSBhbmQgaW4gcmVsYXRpb24gdG8gdGhlIGJ1ZGdldCAodmFyaWFibGUgYEJ1ZGdldGApLiBJbmNsdWRlIGFueSBwbG90cyB0aGF0IGlsbHVzdHJhdGUuCgpgYGB7cn0KIyBUT0RPOiBJbnZlc3RpZ2F0ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIFJ1bnRpbWUgdmFsdWVzIGFuZCBob3cgaXQgdmFyaWVzIGJ5IFllYXIgYW5kIEJ1ZGdldAoKIyBUaGUgZGlzdHJpYnV0aW9uIG9mIFJ1bnRpbWUgdmFsdWVzCnFwbG90KHggPSBSdW50aW1lLCBkYXRhID0gZGYsIGJpbndpZHRoID0gMTApIAoKIyBSdW50aW1lIHZzLiBCdWRnZXQgCnFwbG90KGRmJFJ1bnRpbWUsIGxvZyhkZiRCdWRnZXQpKQoKIyBSdW50aW1lIHZzLiBZZWFyCmRmJERlY2FkZXMgPSAoZGYkWWVhciAlLyUgMTApICogMTAKZ2dwbG90KGRmLCBhZXMocmVvcmRlcihEZWNhZGVzLCBEZWNhZGVzLCBtZWRpYW4pLCBSdW50aW1lKSApICsgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2Rpc2NyZXRlKCIiKSAKYGBgCgpfRmVlbCBmcmVlIHRvIGluc2VydCBhZGRpdGlvbmFsIGNvZGUgY2h1bmtzIGFzIG5lY2Vzc2FyeS5fCgoqKlEqKjogQ29tbWVudCBvbiB0aGUgZGlzdHJpYnV0aW9uIGFzIHdlbGwgYXMgcmVsYXRpb25zaGlwcy4gQXJlIHRoZXJlIGFueSBwYXR0ZXJucyBvciB0cmVuZHMgdGhhdCB5b3UgY2FuIG9ic2VydmU/CgoqKkEqKjogCjEuIFRoZSBSdW50aW1lIGRpc3RyaWJ1dGlvbiBpcyAiQmkgbW9kZWwgbm9ybWFsIGRpc3RyaWJ1dGlvbiIsIHRoZXJlIGFyZSBvdXRsaWVycyB3aGljaCBpcyBtdWNoIGxvbmdlciB0aGFuIGF2ZXJhZ2UuIAoyLiBNZWRpYW4gb2YgUnVudGltZSBpbmNyZWFkZWQgaW4gbGF0ZXIgeWVhcnMuCjMuIEFzIFJ1bnRpbWUgaW5jcmVhc2VzLCBCdWRnZXQgbWF5YmUgaW5jcmVhc2VzLCBidXQgdmVyeSBzY2F0dGVyZWQuIFRoZSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgaXMgbm90IHZlcnkgb2J2aW91cy4KCiMjIDMuIEVuY29kZSBgR2VucmVgIGNvbHVtbgoKVGhlIGNvbHVtbiBgR2VucmVgIHJlcHJlc2VudHMgYSBsaXN0IG9mIGdlbnJlcyBhc3NvY2lhdGVkIHdpdGggdGhlIG1vdmllIGluIGEgc3RyaW5nIGZvcm1hdC4gV3JpdGUgY29kZSB0byBwYXJzZSBlYWNoIHRleHQgc3RyaW5nIGludG8gYSBiaW5hcnkgdmVjdG9yIHdpdGggMXMgcmVwcmVzZW50aW5nIHRoZSBwcmVzZW5jZSBvZiBhIGdlbnJlIGFuZCAwcyB0aGUgYWJzZW5jZSwgYW5kIGFkZCBpdCB0byB0aGUgZGF0YWZyYW1lIGFzIGFkZGl0aW9uYWwgY29sdW1ucy4gVGhlbiByZW1vdmUgdGhlIG9yaWdpbmFsIGBHZW5yZWAgY29sdW1uLgoKRm9yIGV4YW1wbGUsIGlmIHRoZXJlIGFyZSBhIHRvdGFsIG9mIDMgZ2VucmVzOiBEcmFtYSwgQ29tZWR5LCBhbmQgQWN0aW9uLCBhIG1vdmllIHRoYXQgaXMgYm90aCBBY3Rpb24gYW5kIENvbWVkeSBzaG91bGQgYmUgcmVwcmVzZW50ZWQgYnkgYSBiaW5hcnkgdmVjdG9yIDwwLCAxLCAxPi4gTm90ZSB0aGF0IHlvdSBuZWVkIHRvIGZpcnN0IGNvbXBpbGUgYSBkaWN0aW9uYXJ5IG9mIGFsbCBwb3NzaWJsZSBnZW5yZXMgYW5kIHRoZW4gZmlndXJlIG91dCB3aGljaCBtb3ZpZSBoYXMgd2hpY2ggZ2VucmVzICh5b3UgY2FuIHVzZSB0aGUgUiBgdG1gIHBhY2thZ2UgdG8gY3JlYXRlIHRoZSBkaWN0aW9uYXJ5KS4KCmBgYHtyfQojIFRPRE86IFJlcGxhY2UgR2VucmUgd2l0aCBhIGNvbGxlY3Rpb24gb2YgYmluYXJ5IGNvbHVtbnMKbGlicmFyeSh0bSkKbXlDb3JwdXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShkZiRHZW5yZSkpCm15VERNIDwtIERvY3VtZW50VGVybU1hdHJpeChteUNvcnB1cywgY29udHJvbCA9IGxpc3QobWluV29yZExlbmd0aCA9IDEpKQp4ID0gYXMubWF0cml4KG15VERNKQp4CiMgQWRkIHByb2Nlc3NlZCBHZW5yZSBkYXRhIHRvIG9yaWdpb25hbCBkZgpkZiA9IGNiaW5kKGRmLCB4KQojIGRyb3AgR2VucmUgZnJvbSBkZgpkZiRHZW5yZSA8LSBOVUxMCmBgYAoKUGxvdCB0aGUgcmVsYXRpdmUgcHJvcG9ydGlvbnMgb2YgbW92aWVzIGhhdmluZyB0aGUgdG9wIDEwIG1vc3QgY29tbW9uIGdlbnJlcy4KCmBgYHtyfQojIFRPRE86IFNlbGVjdCBtb3ZpZXMgZnJvbSB0b3AgMTAgbW9zdCBjb21tb24gZ2VucmVzIGFuZCBwbG90IHRoZWlyIHJlbGF0aXZlIHByb3BvcnRpb25zCiMgY3JlYXRlIGEgZGYgdG8gcmVjb3JkIGZyZXF1ZW5jeQpkZi5jb3VudCA8LSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gMiwgbnJvdyA9IDMwKSkKY29sbmFtZXMoZGYuY291bnQpID0gYygiZ2VucmUiLCAiY291bnQiKQojIGZpbGwgaW4gZnJlcXVlbmN5CnRvdGFsZ2VucmUgPSBjb2xuYW1lcyh4KQppID0gMQpmb3IgKGcgaW4gdG90YWxnZW5yZSl7CiAgY3VyID0gc3VtKGRmW2ddKQogIGRmLmNvdW50W2ksXSA9IGMoZywgY3VyKSAKICBpID0gaSArIDEKfQpkZi5jb3VudCRmcmVxdWVuY3kgPC0gYXMubnVtZXJpYyhkZi5jb3VudCRjb3VudCkgLyBkaW0oZGYpWzFdCmRmLmNvdW50W3JldihvcmRlcihkZi5jb3VudCRmcmVxdWVuY3kpKSxdCiMgcGxvdCByZWxhdGl2ZSBwcm9wb3J0aW9ucwpkZi5jb3VudCA9IGRmLmNvdW50W3JldihvcmRlcihkZi5jb3VudCRmcmVxdWVuY3kpKSxdCnFwbG90KHggPWRmLmNvdW50JGdlbnJlWzE6MTBdLCB5ID1kZi5jb3VudCRmcmVxdWVuY3lbMToxMF0pCgpgYGAKCkV4YW1pbmUgaG93IHRoZSBkaXN0cmlidXRpb24gb2YgYFJ1bnRpbWVgIGNoYW5nZXMgYWNyb3NzIGdlbnJlcyBmb3IgdGhlIHRvcCAxMCBtb3N0IGNvbW1vbiBnZW5yZXMuCgpgYGB7cn0KIyBUT0RPOiBQbG90IFJ1bnRpbWUgZGlzdHJpYnV0aW9uIGZvciB0b3AgMTAgbW9zdCBjb21tb24gZ2VucmVzCnRvdGFsZ2VucmUgPSBkZi5jb3VudCRnZW5yZVsxOjEwXQpwcmludCh0b3RhbGdlbnJlKQpmb3IgKGkgaW4gc2VxKDEsIDEwKSl7CiAgcHJpbnQgKGdncGxvdChkZlt3aGljaChkZlt0b3RhbGdlbnJlW2ldXSA9PSAxKSxdLCBhZXMoIiIsIFJ1bnRpbWUpKSArIGdlb21fYm94cGxvdCgpICsgY29vcmRfZmxpcCgpICsgc2NhbGVfeF9kaXNjcmV0ZSgiIikgKyBnZ3RpdGxlKHRvdGFsZ2VucmVbaV0pKQogIHogPSBkZlt3aGljaChkZlt0b3RhbGdlbnJlW2ldXSA9PSAxKSxdCiAgcHJpbnQgKHRvdGFsZ2VucmVbaV0pCiAgcHJpbnQgKG1lZGlhbih6JFJ1bnRpbWUpKQogIHByaW50KHFwbG90KHggPSBSdW50aW1lLCBkYXRhID0geiwgYmlud2lkdGggPSA0LCBtYWluID0gdG90YWxnZW5yZVtpXSkpCn0KZGltKGRmKQoKCmZvciAoaSBpbiBzZXEoMSwgMTApKXsKICBwcmludCAoZ2dwbG90KGRmW3doaWNoKGRmW3RvdGFsZ2VucmVbaV1dID09IDEpLF0sIGFlcygiIiwgUnVudGltZSkpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkgKyBzY2FsZV94X2Rpc2NyZXRlKCIiKSArIGdndGl0bGUodG90YWxnZW5yZVtpXSkpCiAgeiA9IGRmW3doaWNoKGRmW3RvdGFsZ2VucmVbaV1dID09IDEpLF0KICBwcmludCAodG90YWxnZW5yZVtpXSkKICBwcmludCAobWVkaWFuKHokUnVudGltZSkpCiAgcHJpbnQocXBsb3QoeCA9IFJ1bnRpbWUsIGRhdGEgPSB6LCBiaW53aWR0aCA9IDQsIG1haW4gPSB0b3RhbGdlbnJlW2ldKSkKfQpkaW0oZGYpCgpgYGAKCioqUSoqOiBEZXNjcmliZSB0aGUgaW50ZXJlc3RpbmcgcmVsYXRpb25zaGlwKHMpIHlvdSBvYnNlcnZlLiBBcmUgdGhlcmUgYW55IGV4cGVjdGVkIG9yIHVuZXhwZWN0ZWQgdHJlbmRzIHRoYXQgYXJlIGV2aWRlbnQ/CgoqKkEqKjoKQXMgZXhwZWN0ZWQsIGZvciBlYWNoIGdlbnJlLCB0aGUgbW92aWVzIHJ1biB0aW1lIGFyZSBnZW5lcmFsbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIGV2ZW4gdGhvdWdoIHNvbWUgYXJlIHNrZXdlZCwgc29tZSBhcmUgYmlvbW9kZWFsIHdpdGggc21hbGxlciBiZWxsIHNoYXBlIGluIHNob3J0ZXIgcnVudGltZS4gVGhlICJzaG9ydCIgaGFzIHRoZSBsb3dlc3QgcnVudGltZSwgdGhlIGRyYW1hIGhhcyB0aGUgbG9uZ2VzdCwgd2hpY2ggaXMgZXhwZWN0ZWQuCgpTb21lIG91dGxpZXJzICg2MDBtaW4pIGlzIG5vdCBleHByZWN0ZWQuIFRoZXkgYXJlIHByZXR0eSBsb25nIG1vdmllcy4gQmlvbW9kZWwgbm9ybWFsIGRpc3RyaWJ1dGlvbiBhcmUgbm90IGV4cGVjdGVkLiBJIGhhdmUgbm90IHRoaW5rIG91dCB3aHkgdGhlcmUgYXJlIHR3byBwZWFrcy4KCiMjIDQuIEVsaW1pbmF0ZSBtaXNtYXRjaGVkIHJvd3MKClRoZSBkYXRhZnJhbWUgd2FzIHB1dCB0b2dldGhlciBieSBtZXJnaW5nIHR3byBkaWZmZXJlbnQgc291cmNlcyBvZiBkYXRhIGFuZCBpdCBpcyBwb3NzaWJsZSB0aGF0IHRoZSBtZXJnaW5nIHByb2Nlc3Mgd2FzIGluYWNjdXJhdGUgaW4gc29tZSBjYXNlcyAodGhlIG1lcmdlIHdhcyBkb25lIGJhc2VkIG9uIG1vdmllIHRpdGxlLCBidXQgdGhlcmUgYXJlIGNhc2VzIG9mIGRpZmZlcmVudCBtb3ZpZXMgd2l0aCB0aGUgc2FtZSB0aXRsZSkuIFRoZXJlIGFyZSAzIGNvbHVtbnMgdGhhdCBjb250YWluIGRhdGUgaW5mb3JtYXRpb246IGBZZWFyYCAobnVtZXJpYyB5ZWFyKSwgYERhdGVgIChudW1lcmljIHllYXIpLCBhbmQgYFJlbGVhc2VkYCAoc3RyaW5nIHJlcHJlc2VudGF0aW9uIG9mIHRoZSByZWxlYXNlIGRhdGUpLgoKRmluZCBhbmQgcmVtb3ZlIGFsbCByb3dzIHdoZXJlIHlvdSBzdXNwZWN0IGEgbWVyZ2UgZXJyb3Igb2NjdXJyZWQgYmFzZWQgb24gYSBtaXNtYXRjaCBiZXR3ZWVuIHRoZXNlIHZhcmlhYmxlcy4gVG8gbWFrZSBzdXJlIHN1YnNlcXVlbnQgYW5hbHlzaXMgYW5kIG1vZGVsaW5nIHdvcmsgd2VsbCwgYXZvaWQgcmVtb3ZpbmcgbW9yZSB0aGFuIDEwJSBvZiB0aGUgcm93cyB0aGF0IGhhdmUgYSBgR3Jvc3NgIHZhbHVlIHByZXNlbnQuCgpfTm90ZTogRG8gbm90IHJlbW92ZSB0aGUgcm93cyB3aXRoIGBHcm9zcyA9PSBOQWAgYXQgdGhpcyBwb2ludCwganVzdCB1c2UgdGhpcyBhIGd1aWRlbGluZS5fCgpgYGB7cn0KIyBUT0RPOiBSZW1vdmUgcm93cyB3aXRoIFllYXIvRGF0ZS9SZWxlYXNlZCBtaXNtYXRjaApkaW0oZGYpClg9IGRhdGEuZnJhbWUoZG8uY2FsbCgncmJpbmQnLCBzdHJzcGxpdChhcy5jaGFyYWN0ZXIoZGYkUmVsZWFzZWQpLCctJyxmaXhlZD1UUlVFKSkpCmRmJHJlbGVhc2VkWWVhciA9IFgkWDEKCiMgUmVtb3ZlIGFsbCB3aG9zZSBHcm9zcyBpcyBOQQpkZl92YWxpZEdyb3NzID0gZGZbIWlzLm5hKGRmJEdyb3NzKSwgXQpkaW0oZGZfdmFsaWRHcm9zcykKCiMgS2VlcCB0aGUgb25lcyB0aGF0IGFyZSBjb3JyZWN0CmRmX3ZhbGlkWWVhckRhdGUgPSBkZl92YWxpZEdyb3NzW2FicyhkZl92YWxpZEdyb3NzJFllYXIgLSBkZl92YWxpZEdyb3NzJERhdGUpIDw9IDEsXQoKI1ZpZXcoZGZfdmFsaWRZZWFyRGF0ZVssIGMoIkdyb3NzIiwgIlllYXIiLCAiRGF0ZSIsICJyZWxlYXNlZFllYXIiKV0pCmRmX3ZhbGlkWWVhckRhdGUkcmVsZWFzZWRZZWFyID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZGZfdmFsaWRZZWFyRGF0ZSRyZWxlYXNlZFllYXIpKQpkZl92YWxpZFllYXJSZWxlYXNlZFllYXIgPSBkZl92YWxpZFllYXJEYXRlW2FicyhkZl92YWxpZFllYXJEYXRlJFllYXIgLSBkZl92YWxpZFllYXJEYXRlJHJlbGVhc2VkWWVhcikgPD0gMSxdCmRpbShkZl92YWxpZFllYXJEYXRlKQpkaW0oZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyKQpgYGAKCioqUSoqOiBXaGF0IGlzIHlvdXIgcHJlY2lzZSByZW1vdmFsIGxvZ2ljLCBhbmQgaG93IG1hbnkgcm93cyByZW1haW4gaW4gdGhlIHJlc3VsdGluZyBkYXRhc2V0PwoKKipBKio6IAoxLkZvciB0aGUgc2FtZSBtb3ZpZXMsIHRoZSAiUmVsZWFzZWQiLCAiRGF0ZSIgYW5kICJZZWFyIiBzaG91bGQgc2hvdyBsZXNzIHRoYW4gb25lIHllYXIgZGlmZmVyZW5jZS4gSWYgaW4gYW55IHJvdyB0aGF0IHRoZWlyIHllYXIgaW5mb3JtYXRpb24gaW4gdGhlIGFib3ZlIHRocmVlIGNvbHVtbnMgYXJlIG5vdCB0aGUgc2FtZSwgdGhhdCBpbmRpY2F0ZXMgYSBtaXNtYXRjaCwgYW5kIHNob3VsZCBiZSByZW1vdmVkLgoyLiBUaGVyZSBhcmUgMzUxOCByb3dzIGxlZnQgYWZ0ZXIgcmVtb3ZhbCwgd2hpY2ggaGFzIGdyb3NzIHZhbHVlLgoKIyMgNS4gRXhwbG9yZSBgR3Jvc3NgIHJldmVudWUKCkZvciB0aGUgY29tbWVyY2lhbCBzdWNjZXNzIG9mIGEgbW92aWUsIHByb2R1Y3Rpb24gaG91c2VzIHdhbnQgdG8gbWF4aW1pemUgR3Jvc3MgcmV2ZW51ZS4gSW52ZXN0aWdhdGUgaWYgR3Jvc3MgcmV2ZW51ZSBpcyByZWxhdGVkIHRvIEJ1ZGdldCwgUnVudGltZSBvciBHZW5yZSBpbiBhbnkgd2F5LgoKX05vdGU6IFRvIGdldCBhIG1lYW5pbmdmdWwgcmVsYXRpb25zaGlwLCB5b3UgbWF5IGhhdmUgdG8gcGFydGl0aW9uIHRoZSBtb3ZpZXMgaW50byBzdWJzZXRzIHN1Y2ggYXMgc2hvcnQgdnMuIGxvbmcgZHVyYXRpb24sIG9yIGJ5IGdlbnJlLCBldGMuXwoKYGBge3J9CiMgVE9ETzogSW52ZXN0aWdhdGUgaWYgR3Jvc3MgUmV2ZW51ZSBpcyByZWxhdGVkIHRvIEJ1ZGdldCwgUnVudGltZSBvciBHZW5yZQoKIyBHcm9zcyBSZXZlbnVlIHZzIEJ1ZGdldApxcGxvdChsb2coZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyJEJ1ZGdldCksIGxvZyhkZl92YWxpZFllYXJSZWxlYXNlZFllYXIkR3Jvc3MpKQoKIyBHcm9zcyBSZXZlbnVlIHZzIFJ1bnRpbWUKcXBsb3QoZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyJFJ1bnRpbWUsIGRmX3ZhbGlkWWVhclJlbGVhc2VkWWVhciRHcm9zcykKCiMgR3Jvc3MgUmV2ZW51ZSB2cyBHZW5yZQoKZm9yIChpIGluIHNlcSgxLCAxMCkpewogIHByaW50IChnZ3Bsb3QoZGZbd2hpY2goZGZbdG90YWxnZW5yZVtpXV0gPT0gMSksXSwgYWVzKCIiLCBHcm9zcykpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkgKyBzY2FsZV94X2Rpc2NyZXRlKCIiKSArIGdndGl0bGUodG90YWxnZW5yZVtpXSkpCiAgeiA9IGRmW3doaWNoKGRmW3RvdGFsZ2VucmVbaV1dID09IDEpLF0KICBwcmludCAodG90YWxnZW5yZVtpXSkKICBwcmludCAobWVkaWFuKHokUnVudGltZSkpCiAgI3ByaW50KHFwbG90KHggPSBSdW50aW1lLCBkYXRhID0geiwgYmlud2lkdGggPSA0LCBtYWluID0gdG90YWxnZW5yZVtpXSkpCn0KCmBgYAoKKipRKio6IERpZCB5b3UgZmluZCBhbnkgb2JzZXJ2YWJsZSByZWxhdGlvbnNoaXBzIG9yIGNvbWJpbmF0aW9ucyBvZiBCdWRnZXQvUnVudGltZS9HZW5yZSB0aGF0IHJlc3VsdCBpbiBoaWdoIEdyb3NzIHJldmVudWU/IElmIHlvdSBkaXZpZGVkIHRoZSBtb3ZpZXMgaW50byBkaWZmZXJlbnQgc3Vic2V0cywgeW91IG1heSBnZXQgZGlmZmVyZW50IGFuc3dlcnMgZm9yIHRoZW0gLSBwb2ludCBvdXQgaW50ZXJlc3Rpbmcgb25lcy4KCioqQSoqOiBCdWRnZXQgaW5jcmVhc2UgYXMgcnVudGltZSBpbmNyZWFzZQogICAgICAgVGhlIHJ1bnRpbWUgZm9yIGVhY2ggR2VuZXJlIGFyZSBkaWZmZXJlbnQuU3VjaCBhcyBzaG9ydCBpcyBtdWNoIHNob3J0ZXIgdGhhbiBvdGhlcnMuCgoKYGBge3J9CiMgVE9ETzogSW52ZXN0aWdhdGUgaWYgR3Jvc3MgUmV2ZW51ZSBpcyByZWxhdGVkIHRvIFJlbGVhc2UgTW9udGgKWD0gZGF0YS5mcmFtZShkby5jYWxsKCdyYmluZCcsIHN0cnNwbGl0KGFzLmNoYXJhY3RlcihkZl92YWxpZFllYXJSZWxlYXNlZFllYXIkUmVsZWFzZWQpLCctJyxmaXhlZD1UUlVFKSkpCm1vbnRoID0gWCRYMgp0cmFuc2Zvcm1lZEdyb3NzID0gbG9nKGRmX3ZhbGlkWWVhclJlbGVhc2VkWWVhciRHcm9zcykKZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyID0gY2JpbmQoZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyLCBtb250aCwgdHJhbnNmb3JtZWRHcm9zcykKbmFtZXMoZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyKQpnZ3Bsb3QoZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyLCBhZXMocmVvcmRlcihtb250aCwgLXRyYW5zZm9ybWVkR3Jvc3MsIG1lZGlhbiksIHRyYW5zZm9ybWVkR3Jvc3MpICkgKyBnZW9tX2JveHBsb3QoKSArIGNvb3JkX2ZsaXAoKSArIHNjYWxlX3hfZGlzY3JldGUoInRyYW5zZm9ybWVkR3Jvc3MiKSAKCmBgYAoKIyMgNi4gUHJvY2VzcyBgQXdhcmRzYCBjb2x1bW4KClRoZSB2YXJpYWJsZSBgQXdhcmRzYCBkZXNjcmliZXMgbm9taW5hdGlvbnMgYW5kIGF3YXJkcyBpbiB0ZXh0IGZvcm1hdC4gQ29udmVydCBpdCB0byAyIG51bWVyaWMgY29sdW1ucywgdGhlIGZpcnN0IGNhcHR1cmluZyB0aGUgbnVtYmVyIG9mIHdpbnMsIGFuZCB0aGUgc2Vjb25kIGNhcHR1cmluZyBub21pbmF0aW9ucy4gUmVwbGFjZSB0aGUgYEF3YXJkc2AgY29sdW1uIHdpdGggdGhlc2UgbmV3IGNvbHVtbnMsIGFuZCB0aGVuIHN0dWR5IHRoZSByZWxhdGlvbnNoaXAgb2YgYEdyb3NzYCByZXZlbnVlIHdpdGggcmVzcGVjdCB0byB0aGVtLgoKX05vdGU6IFRoZSBmb3JtYXQgb2YgdGhlIGBBd2FyZHNgIGNvbHVtbiBpcyBub3Qgc3RhbmRhcmQ7IHlvdSBtYXkgaGF2ZSB0byB1c2UgcmVndWxhciBleHByZXNzaW9ucyB0byBmaW5kIHRoZSByZWxldmFudCB2YWx1ZXMuIFRyeSB5b3VyIGJlc3QgdG8gcHJvY2VzcyB0aGVtLCBhbmQgeW91IG1heSBsZWF2ZSB0aGUgb25lcyB0aGF0IGRvbid0IGhhdmUgZW5vdWdoIGluZm9ybWF0aW9uIGFzIE5BcyBvciBzZXQgdGhlbSB0byAwcy5fCgpgYGB7cn0KIyBUT0RPOiBDb252ZXJ0IEF3YXJkcyB0byAyIG51bWVyaWMgY29sdW1uczogd2lucyBhbmQgbm9taW5hdGlvbnMKcm93c19udW0gPSBkaW0oZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyKVsxXQpkZl92YWxpZFllYXJSZWxlYXNlZFllYXIuYXdhcmRzID0gZGF0YS5mcmFtZShtYXRyaXgobmNvbCA9IDIsIG5yb3cgPSByb3dzX251bSApKQpjb2xuYW1lcyhkZl92YWxpZFllYXJSZWxlYXNlZFllYXIuYXdhcmRzKSA9IGMoIndpbnMiLCAibm9taW5hdGlvbnMiKQpmb3IgKGkgaW4gc2VxKDEsIHJvd3NfbnVtKSl7CiAgY29udGVudCA9IHVubGlzdChzdHJzcGxpdChkZl92YWxpZFllYXJSZWxlYXNlZFllYXJbaSwgIkF3YXJkcyJdLCBzcGxpdD0iICIpKQogIGlmIChsZW5ndGgoY29udGVudCkgPCAyKSBuZXh0CiAgZm9yIChqIGluIHNlcSgxLCBsZW5ndGgoY29udGVudCkgLSAxKSl7CiAgICBpZiAoc3Vic3RyKGNvbnRlbnRbaiArIDFdLCAxLCAzKSA9PSAid2luIikgewogICAgICB3aW5fbnVtID0gYXMubnVtZXJpYyhjb250ZW50W2pdKQogICAgICBkZl92YWxpZFllYXJSZWxlYXNlZFllYXIuYXdhcmRzW2ksICJ3aW5zIl0gPSB3aW5fbnVtCiAgICB9CiAgICBpZiAoc3Vic3RyKGNvbnRlbnRbaiArIDFdLCAxLCAxMCkgPT0gIm5vbWluYXRpb24iKSB7CiAgICAgIG5vbWluYXRpb25fbnVtID0gYXMubnVtZXJpYyhjb250ZW50W2pdKQogICAgICBkZl92YWxpZFllYXJSZWxlYXNlZFllYXIuYXdhcmRzW2ksICJub21pbmF0aW9ucyJdID0gbm9taW5hdGlvbl9udW0KICAgIH0KICB9Cn0KZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyID0gY2JpbmQoZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyLCBkZl92YWxpZFllYXJSZWxlYXNlZFllYXIuYXdhcmRzKQojIG51bWJlciBvZiBub24gTi9BIGluIG5vbWluYXRpb24KY29sU3VtcyghaXMubmEoZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyLmF3YXJkcykpWzJdCmBgYAoKKipRKio6IEhvdyBkaWQgeW91IGNvbnN0cnVjdCB5b3VyIGNvbnZlcnNpb24gbWVjaGFuaXNtPyBIb3cgbWFueSByb3dzIGhhZCB2YWxpZC9ub24temVybyB3aW5zIG9yIG5vbWluYXRpb25zPwoKKipBKio6IEkgd3JvdGUgbG9vcHMgdG8gYWNjZXNzIGVhY2ggY2VsbCBpbiB0aGUgQXdhcmQgY29sdW1uLiBFeHRyYWN0IHRoZSBudW1iZXIgYmVmb3JlICIgd2luIiwgYW5kIHNpbWlsYXJseSBleHRyYWN0IHRoZSBudW1iZXIgYmVmb3JlICIgbm9taW5hdGlvbiIuIFRoZXJlIGFyZSAxMTU0NyBub24gTkEgcm93cyBpbiBub25taW5hdGlvbgoKYGBge3J9CiMgVE9ETzogUGxvdCBHcm9zcyByZXZlbnVlIGFnYWluc3Qgd2lucyBhbmQgbm9taW5hdGlvbnMKcGxvdChsb2coZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyJG5vbWluYXRpb25zKSxsb2coZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyJEdyb3NzKSkKcGxvdChsb2coZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyJHdpbnMpLGxvZyhkZl92YWxpZFllYXJSZWxlYXNlZFllYXIkR3Jvc3MpKQoKYGBgCgoqKlEqKjogSG93IGRvZXMgdGhlIGdyb3NzIHJldmVudWUgdmFyeSBieSBudW1iZXIgb2YgYXdhcmRzIHdvbiBhbmQgbm9taW5hdGlvbnMgcmVjZWl2ZWQ/CgoqKkEqKjogQXMgdGhlIG51bWJlciBvZiBhd2FyZHMgd29uIGFuZCBub21pbmF0aW9ucyBpbmNyZWFzZXMsIHRoZSBncm9zcyByZXZlbnVlIGluY3JlYXNlLgoKIyMgNy4gTW92aWUgcmF0aW5ncyBmcm9tIElNRGIgYW5kIFJvdHRlbiBUb21hdG9lcwoKVGhlcmUgYXJlIHNldmVyYWwgdmFyaWFibGVzIHRoYXQgZGVzY3JpYmUgcmF0aW5ncywgaW5jbHVkaW5nIElNRGIgcmF0aW5ncyAoYGltZGJSYXRpbmdgIHJlcHJlc2VudHMgYXZlcmFnZSB1c2VyIHJhdGluZ3MgYW5kIGBpbWRiVm90ZXNgIHJlcHJlc2VudHMgdGhlIG51bWJlciBvZiB1c2VyIHJhdGluZ3MpLCBhbmQgbXVsdGlwbGUgUm90dGVuIFRvbWF0b2VzIHJhdGluZ3MgKHJlcHJlc2VudGVkIGJ5IHNldmVyYWwgdmFyaWFibGVzIHByZS1maXhlZCBieSBgdG9tYXRvYCkuIFJlYWQgdXAgb24gc3VjaCByYXRpbmdzIG9uIHRoZSB3ZWIgKGZvciBleGFtcGxlIFtyb3R0ZW50b21hdG9lcy5jb20vYWJvdXRdKGh0dHBzOi8vd3d3LnJvdHRlbnRvbWF0b2VzLmNvbS9hYm91dCkgYW5kIFsgd3d3LmltZGIuY29tL2hlbHAvc2hvd19sZWFmP3ZvdGVzdG9wZmFxXShodHRwOi8vIHd3dy5pbWRiLmNvbS9oZWxwL3Nob3dfbGVhZj92b3Rlc3RvcGZhcSkpLgoKSW52ZXN0aWdhdGUgdGhlIHBhaXJ3aXNlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0aGVzZSBkaWZmZXJlbnQgZGVzY3JpcHRvcnMgdXNpbmcgZ3JhcGhzLgoKYGBge3J9CiMgVE9ETzogSWxsdXN0cmF0ZSBob3cgcmF0aW5ncyBmcm9tIElNRGIgYW5kIFJvdHRlbiBUb21hdG9lcyBhcmUgcmVsYXRlZAppbnN0YWxsLnBhY2thZ2VzKCdHR2FsbHknKQpsaWJyYXJ5KEdHYWxseSkKbmFtZXMoZGYpCnJhdGluZ19wYXJhbWV0ZXJzID0gYygidG9tYXRvTWV0ZXIiLCAidG9tYXRvUmF0aW5nIiwgInRvbWF0b1Jldmlld3MiLCAidG9tYXRvRnJlc2giLCAidG9tYXRvUm90dGVuIiwgInRvbWF0b1VzZXJNZXRlciIsICJ0b21hdG9Vc2VyUmF0aW5nIiwgImltZGJSYXRpbmciICwiaW1kYlZvdGVzIiAgKQpnZ3BhaXJzKGRmLCBjb2x1bW5zID0gcmF0aW5nX3BhcmFtZXRlcnMgKQpgYGAKCioqUSoqOiBDb21tZW50IG9uIHRoZSBzaW1pbGFyaXRpZXMgYW5kIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIHVzZXIgcmF0aW5ncyBvZiBJTURiIGFuZCB0aGUgY3JpdGljcyByYXRpbmdzIG9mIFJvdHRlbiBUb21hdG9lcy4KCioqQSoqOiAKKDEpLiBJbWJkUmF0aW5ncyBpcyBoaWdoIHNpbWlsYXJpdGllcyB3aXRoICJ0b21hdG9NZXRlciIsICJ0b21hdG9SYXRpbmciLCAgInRvbWF0b1VzZXJNZXRlciIsICJ0b21hdG9Vc2VyUmF0aW5nIjsKKDIpLiBJbWJkUmF0aW5ncyBoYXMgc2xpZ2h0IHNpbWlsYXJpdGllcyB3aXRoICJ0b21hdG9SZXZpZXdzIiwgInRvbWF0b0ZyZXNoIjsgCigzKS5JbWJkUmF0aW5ncyBpcyBkaWZmZXJlbnQgd2l0aCAidG9tYXRvUm90dGVuIjsKCiMjIDguIFJhdGluZ3MgYW5kIGF3YXJkcwpUaGVzZSByYXRpbmdzIHR5cGljYWxseSByZWZsZWN0IHRoZSBnZW5lcmFsIGFwcGVhbCBvZiB0aGUgbW92aWUgdG8gdGhlIHB1YmxpYyBvciBnYXRoZXIgb3BpbmlvbnMgZnJvbSBhIGxhcmdlciBib2R5IG9mIGNyaXRpY3MuIFdoZXJlYXMgYXdhcmRzIGFyZSBnaXZlbiBieSBwcm9mZXNzaW9uYWwgc29jaWV0aWVzIHRoYXQgbWF5IGV2YWx1YXRlIGEgbW92aWUgb24gc3BlY2lmaWMgYXR0cmlidXRlcywgc3VjaCBhcyBhcnRpc3RpYyBwZXJmb3JtYW5jZSwgc2NyZWVucGxheSwgc291bmQgZGVzaWduLCBldGMuCgpTdHVkeSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcmF0aW5ncyBhbmQgYXdhcmRzIHVzaW5nIGdyYXBocyAoYXdhcmRzIGhlcmUgcmVmZXJzIHRvIHdpbnMgYW5kL29yIG5vbWluYXRpb25zKS4gCgpgYGB7cn0KIyBUT0RPOiBTaG93IGhvdyByYXRpbmdzIGFuZCBhd2FyZHMgYXJlIHJlbGF0ZWQKZGYgPSBkZjIKbmFtZXMoZGYpCiMgYWRkIHdpbnMgYW5kIG5vbWluYXRpb25zIHRvIG9yZ2lvbmFsIGRmCnJvd3NfbnVtID0gZGltKGRmKVsxXQpkZi5hd2FyZHMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sID0gMiwgbnJvdyA9IHJvd3NfbnVtICkpCmNvbG5hbWVzKGRmLmF3YXJkcykgPSBjKCJ3aW5zIiwgIm5vbWluYXRpb25zIikKZm9yIChpIGluIHNlcSgxLCByb3dzX251bSkpewogIGNvbnRlbnQgPSB1bmxpc3Qoc3Ryc3BsaXQoZGZbaSwgIkF3YXJkcyJdLCBzcGxpdD0iICIpKQogIGlmIChsZW5ndGgoY29udGVudCkgPCAyKSBuZXh0CiAgZm9yIChqIGluIHNlcSgxLCBsZW5ndGgoY29udGVudCkgLSAxKSl7CiAgICBpZiAoc3Vic3RyKGNvbnRlbnRbaiArIDFdLCAxLCAzKSA9PSAid2luIikgewogICAgICB3aW5fbnVtID0gYXMubnVtZXJpYyhjb250ZW50W2pdKQogICAgICBkZi5hd2FyZHNbaSwgIndpbnMiXSA9IHdpbl9udW0KICAgIH0KICAgIGlmIChzdWJzdHIoY29udGVudFtqICsgMV0sIDEsIDEwKSA9PSAibm9taW5hdGlvbiIpIHsKICAgICAgbm9taW5hdGlvbl9udW0gPSBhcy5udW1lcmljKGNvbnRlbnRbal0pCiAgICAgIGRmLmF3YXJkc1tpLCAibm9taW5hdGlvbnMiXSA9IG5vbWluYXRpb25fbnVtCiAgICB9CiAgfQp9CmRmID0gY2JpbmQoZGYsIGRmLmF3YXJkcykKbmFtZXMoZGYpCgojIGZpbmQgcmVsYXRpb25zaGlwCnBhcmFtZXRlcnMgPSBjKCJ0b21hdG9NZXRlciIsICJ0b21hdG9SYXRpbmciLCAidG9tYXRvUmV2aWV3cyIsICJ0b21hdG9GcmVzaCIsICJ0b21hdG9Sb3R0ZW4iLCAidG9tYXRvVXNlck1ldGVyIiwgInRvbWF0b1VzZXJSYXRpbmciLCAiaW1kYlJhdGluZyIgLCJpbWRiVm90ZXMiLCAid2lucyIsICJub21pbmF0aW9ucyIgKQpnZ3BhaXJzKGRmLCBjb2x1bW5zID0gcGFyYW1ldGVycywgdGl0bGUgPSAiZmlnMSIgKQpgYGAKCioqUSoqOiBIb3cgZ29vZCBhcmUgdGhlc2UgcmF0aW5ncyBpbiB0ZXJtcyBvZiBwcmVkaWN0aW5nIHRoZSBzdWNjZXNzIG9mIGEgbW92aWUgaW4gd2lubmluZyBhd2FyZHMgb3Igbm9taW5hdGlvbnM/IElzIHRoZXJlIGEgaGlnaCBjb3JyZWxhdGlvbiBiZXR3ZWVuIHR3byB2YXJpYWJsZXM/CgoqKkEqKjogCjEuIFNvbWUgb2YgdGhvc2UgcmF0aW5ncyBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQgd2l0aCB0aGUgd2lucyBhbmQgbm9taW5hdGlvbnMsIHN1Y2ggYXMgdG9tYXRvUmV2aWV3cywgdG9tYXRvRnJlc2gsIGltYmRWb3RlcyBhbmQgaW1iZFZvdGluZy4KICAgIHRvbWF0b1JvdHRlbiBhcmUgbm90IHBvc2l0aXZlbHkgcmVsYXRlZCB0byB0aGUgYXdhcmRzIHdoaWNoIGlzIGV4cGVjdGVkLgogICAgU29tZSBvZiB0aG9zZSByYXRpbmdzIGFyZSBub3QgdmVyeSBnb29kIHByZWRpY2F0b3JzLCBzdWNoIGFzIHRvbWF0b01ldGVycywgdG9tYXRvUmF0aW5zLCBzaW5jZSB0aGV5IGhhdmUgbG93IGNvcnJlbGF0aW9ucyB0byBhd2FyZHMuCjIuIE5vcm1pbmF0aW9ucyBhbmQgd2lucyBhcmUgaGlnaGx5IHJlbGF0ZWQuIENvcnJlbGF0aW9uIGlzIDAuODE5CiMjIDkuIEV4cGVjdGVkIGluc2lnaHRzCgpDb21lIHVwIHdpdGggdHdvIG5ldyBpbnNpZ2h0cyAoYmFja2VkIHVwIGJ5IGRhdGEgYW5kIGdyYXBocykgdGhhdCBpcyBleHBlY3RlZC4gSGVyZSDigJxuZXfigJ0gbWVhbnMgaW5zaWdodHMgdGhhdCBhcmUgbm90IGFuIGltbWVkaWF0ZSBjb25zZXF1ZW5jZSBvZiBvbmUgb2YgdGhlIGFib3ZlIHRhc2tzLiBZb3UgbWF5IHVzZSBhbnkgb2YgdGhlIGNvbHVtbnMgYWxyZWFkeSBleHBsb3JlZCBhYm92ZSBvciBhIGRpZmZlcmVudCBvbmUgaW4gdGhlIGRhdGFzZXQsIHN1Y2ggYXMgYFRpdGxlYCwgYEFjdG9yc2AsIGV0Yy4KCmBgYHtyfQojIFRPRE86IEZpbmQgYW5kIGlsbHVzdHJhdGUgdHdvIGV4cGVjdGVkIGluc2lnaHRzCiMgRXhwZWFjdCBJbmdpc2h0ICMgMQojIGRlbGltaW5pdGUKbGlicmFyeSh0bSkKbXlDb3JwdXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShkZiRDb3VudHJ5KSkKbXlURE0gPC0gRG9jdW1lbnRUZXJtTWF0cml4KG15Q29ycHVzLCBjb250cm9sID0gbGlzdChtaW5Xb3JkTGVuZ3RoID0gMSkpCmNvdW50cmllcyA9IGFzLm1hdHJpeChteVRETSkKCgpkZiA9IGNiaW5kKGRmLCBjb3VudHJpZXMpCgpkZi5jb3VudHJpZXMgPC0gZGF0YS5mcmFtZShtYXRyaXgobmNvbCA9IDIsIG5yb3cgPSAxNzMpKQpjb2xuYW1lcyhkZi5jb3VudHJpZXMpID0gYygiY291bnRyeW5hbWUiLCAiY291bnQiKQojIGZpbGwgaW4gZnJlcXVlbmN5CnRvdGFsY291bnRyaWVzID0gY29sbmFtZXMoY291bnRyaWVzKQppID0gMQpmb3IgKGMgaW4gdG90YWxjb3VudHJpZXMpewogIGN1ciA9IHN1bShkZltjXSkKICBkZi5jb3VudHJpZXMgW2ksXSA9IGMoYywgY3VyKSAKICBpID0gaSArIDEKfQoKZGYuY291bnRyaWVzICRmcmVxIDwtIGFzLm51bWVyaWMoZGYuY291bnRyaWVzICRjb3VudCkgLyBkaW0oZGYpWzFdCmRmLmNvdW50cmllcyA9IGRmLmNvdW50cmllc1tyZXYob3JkZXIoZGYuY291bnRyaWVzJGZyZXEpKSxdCmRmLnRvcGNvdW50cmllcyA9IGRmLmNvdW50cmllc1swOjEwICxdClZpZXcoZGYudG9wY291bnRyaWVzKQoKIyBFeHBlY3RlZCBpbnNpZ2h0ICMgMgpWaWV3KGRmX3ZhbGlkWWVhclJlbGVhc2VkWWVhclssIGMoIkdyb3NzIiwgIkRlY2FkZXMiKV0pCnFwbG90KGRmX3ZhbGlkWWVhclJlbGVhc2VkWWVhciREZWNhZGVzLCBkZl92YWxpZFllYXJSZWxlYXNlZFllYXIkR3Jvc3MpCgpnZ3Bsb3QoZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyLCBhZXMocmVvcmRlcihEZWNhZGVzLCAtdHJhbnNmb3JtZWRHcm9zcywgbWVkaWFuKSwgdHJhbnNmb3JtZWRHcm9zcykgKSArIGdlb21fYm94cGxvdCgpICsgY29vcmRfZmxpcCgpICsgc2NhbGVfeF9kaXNjcmV0ZSgidHJhbnNmb3JtZWRHcm9zcyIpIAoKCgoKCmBgYAoKKipRKio6IEV4cGVjdGVkIGluc2lnaHQgIzEuCgoqKkEqKjogVVNBIHByb2R1Y2VkIG11Y2ggbGFyZ2VyIGFtb3VudCBvZiBtb3ZpZXMgdGhhbiBvdGhlciBjb3VudHJpZXMuIFRoZSBmb2xsb3dpbmcgY291bnRyaWVzIGFyZTogRnJhbmNlLCBHZXJtYW55LCBDYW5hZGEgLi4uIAoKCioqUSoqOiBFeHBlY3RlZCBpbnNpZ2h0ICMyLgoKKipBKio6IFJlY2VudCB5ZWFycyBoYXMgbGFyZ2VyIEdyb3NzIG1lZGlhbiB0aGFuIHRob3NlIG9sZCB5ZWFycy4KCgoKIyMgMTAuIFVuZXhwZWN0ZWQgaW5zaWdodAoKQ29tZSB1cCB3aXRoIG9uZSBuZXcgaW5zaWdodCAoYmFja2VkIHVwIGJ5IGRhdGEgYW5kIGdyYXBocykgdGhhdCBpcyB1bmV4cGVjdGVkIGF0IGZpcnN0IGdsYW5jZSBhbmQgZG8geW91ciBiZXN0IHRvIG1vdGl2YXRlIGl0LiBTYW1lIGluc3RydWN0aW9ucyBhcHBseSBhcyB0aGUgcHJldmlvdXMgdGFzay4KCmBgYHtyfQojIFRPRE86IEZpbmQgYW5kIGlsbHVzdHJhdGUgb25lIHVuZXhwZWN0ZWQgaW5zaWdodApxcGxvdCggZGZfdmFsaWRZZWFyUmVsZWFzZWRZZWFyJGltZGJSYXRpbmcsIGRmX3ZhbGlkWWVhclJlbGVhc2VkWWVhciRHcm9zcykKYGBgCgoqKlEqKjogVW5leHBlY3RlZCBpbnNpZ2h0LgoKKipBKio6IFRoZSB0aGUgZmlyc3QgdGhvdWdodCwgSSB3YXMgbm90IGV4cGVjdCB0aGF0IHRoZSBvbmVzIHdpdGggaGlnaCBpbWJkIHJhdGluZ3MgY291bGQgaGF2ZSB2ZXJ5IGxvdyBncm9zcy4gQnV0IGFmdGVyIHRoaW5raW5nIGFnYWluLCBJIGZvdW5kIGl0IGlzIHJlYXNvbmFibGUgdG8gc29tZSBtb3ZpZXMgYXJlIGdvb2QsIGJ1dCBhcmUgbm90IHdlbGwgY29tbWVyY2lhbHppZWQgYW5kIG9uIHRoZSBidXNpbmVzcyBzaWRlLCB0aGV5IHdlcmUgbm90IHN1Y2Nlc3NmdWwuCgoK