Introduction

For my final project I will be gathering movie ratings from different sources and normalizing the different ratings. I will be gathering the information from different movie rating sites like IMDB, Rotten Tomato, and Metacritics. Each of these sites have different rating systems and different rating scales, which will be normalized in this project in order to create one single rating. I will also be gathering information by scrapping websites that do not have a open source RESTFUL API to use. For this project I will be creating a normalized list of ratings for all Micheal Bay Movies.

Prerequisite Before Gathering Data

We will be needing to create a list of all Micheal Bay movies in which he directed. This list will be used to pass through to the RESTFUL API in order to get the ratings of these movies.

movie_list = c('Bad Boys', 'The Rock', 'Armageddon', 'Pearl Harbor', 'Bad Boys II', 'The Island', 'Transformers', 'Transformers: Revenge of the Fallen', 'Transformers: Dark of the Moon', 'Pain & Gain', 'Transformers: Age of Extinction', 'Transformers: The Last Knight')

OMDB API

IMDB, Rotten Tomato, and Metacritics currently does not have a open source RESTFUL API for the public to gather data. Luckily there is a open source project called OMDb which provides an open source API to gather all details for a movie and also the ratings from the different sites. We will also need to implement checks to make sure that we are getting the right movie with the API by checking to make sure that the directors of all the movies is Micheal Bay.

library(dotenv)
library(httr)
library(jsonlite)
library(tidyverse)
base = 'http://www.omdbapi.com'
api = Sys.getenv('API_KEY')

response = GET(base, query = list('apikey' = api, 'type' = 'movie', 't' = 'bad boy'))
response_text = content(response, 'text')

json_file = fromJSON(response_text)

get_movie_info = function(movie_title)
{
  base = 'http://www.omdbapi.com'
  api = Sys.getenv('API_KEY')
  
  response = GET(base, query = list('apikey' = api, 'type' = 'movie', 't' = movie_title))
  response_text = content(response, 'text')
  
  json_file = fromJSON(response_text)
  
  json_file = json_file$Ratings
  
  json_file = json_file %>%
    mutate(Movie = movie_title)
  
  return(json_file)
}
columns = c('Source', 'Value', 'Movie')
rating = data.frame(matrix(nrow = 0, ncol = length(columns)))
colnames(rating) = columns

for (movie in movie_list)
{
  rating = rbind(rating, get_movie_info(movie))
}

We are now able to get all the ratings using OMDB API from the biggest rating sites IMDB, Rotten Tomato, and Metacritics.

Roger Ebert Reviews

I am also going to be getting the ratings from RogerEbret.com which has a specific section showings the ratings for all Michael Bay movies. This website does not have a API so I will be putting all the movies into a excel spreadsheet and exporting them into a csv. Then I will be loading the csv file and combining the data that I got with the Restful API.

file = read_csv('Roger Ebert Michael Bay.csv', col_names=c('Movie', 'Value'))

── Column specification ────────────────────────────────────────────────────────────────────────────────────────
cols(
  Movie = col_character(),
  Value = col_character()
)
file = file %>%
  mutate(Source = 'Robertebert.com')

rating = rbind(rating, file)

Unpivoting the Data and Normalizing the Data

I am going to unpiviot the data so that it will be easier to compare the rating of each of the movies

rating %>%
  pivot_wider(names_from=Source, values_from=Value)

From a quick look it is difficult to see which movie is highly rating as each rating website has their own rating systems and and weights. I am going normalize all of the different rates by using the the Min-Max Feature Scaling \((X - X_{min})/(X_{max} - X_{min})\). This will normalize all the scores from the different from the different sources. Also we will not be using the Z score method of standardizing the scores as we do not know if the data follows a normal distribution, and using the Z score method there is no bounds to the range.

source_rating = data.frame('Source' = c('Internet Movie Database', 'Rotten Tomatoes', 'Metacritic', 'Robertebert.com'),
                           'Min' = c(1, 1, 1, 1),
                           'Max' = c(10, 100, 100, 4)
                           )
pattern = '^\\D*(\\d+(?:\\.\\d+)?)'

rating = rating %>%
  mutate(fix_value = str_extract(Value, pattern))

rating$fix_value = as.numeric(rating$fix_value)
rating = rating %>%
  left_join(source_rating, by='Source')
rating = rating %>%
  mutate(normal_value = ((fix_value-Min)/(Max - Min)))

Now that we have normalized all the ratings lets see which are the highest rated movies. It is still difficult to see which one is truly the highest rated movie since there is so many different ratings for each movie. Some sources may rate a movie high while others may rate it low. I am going to now take all the normalized ratings and aggregate them into one final rating to see which one is truly the highest rated Michael Bay Movie.

rating %>%
  select(Source, Movie, normal_value) %>%
  pivot_wider(names_from=Source, values_from=normal_value)

Graphics and Statistical Analysis

Now that we have normalized all the different rates from the different sources we can take the average of all the scores in order to get one final score. This will allow users to easily determine which movie is the best without needing to look at all the data.

rating %>%
  filter(!is.na(normal_value)) %>%
  group_by(Movie) %>%
  summarize(avg_norm_value = mean(normal_value)) %>%
  arrange(desc(avg_norm_value))

As we can see The Rock is the highest rated Michael Bay movie with Transformers coming at a close second.

Graphics

Plotting out all the movies and comparing how each source rates the movie in order to see if a certain source has bias against Micheal Bay movies

rating %>%
  ggplot(aes(x=Movie, y=normal_value, fill=Source)) + geom_bar(stat='identity', position=position_dodge()) + coord_flip()

We can see that IMDB (Internet Movie Database) rates movies a lot higher compared to the other sources. When the other sources rate a Micheal Bay movie very low IMDB usually rates it really high. If we look at the raw data we can easily see this with many movies where Rotten Tomato rates a movie 20/100 but IMDB would rate the movie 6/10. But as long as IMDB rates is rating the movies consistently without any bias, then it should not have an effect on the normalization of the rating.

rating %>%
  filter(!is.na(normal_value)) %>%
  group_by(Movie) %>%
  summarize(avg_norm_value = mean(normal_value)) %>%
  ggplot(aes(x = reorder(Movie, avg_norm_value), y = avg_norm_value)) + geom_bar(stat='identity') + coord_flip()

Conclusion

We can see that the highest rated movie is The Rock and we can confirm that by looking at the individual ratings from the different sources. While there are multiple methods of normalizing scores, the Min-Max Feature Scaling was the easiest to implement especially since I did not know the population mean, standard deviation, and if the scores were normal. The downside of the Min-Max Feature Scaling is that depending on the scaling of the rating system. For example Robertebert.com which only rates out of 4 which means when the score is normalized there will also be only 4 different scores. Something new that I tried was creating my presentation using R Markdown instead of powerpoint. While it is a lot easier not needing to go back and forth between different programs, it requires learning of specific commands in order to create the presentation.

LS0tCnRpdGxlOiAiRGF0YSA2MDcgRmluYWwgUHJvamVjdCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBJbnRyb2R1Y3Rpb24KCkZvciBteSBmaW5hbCBwcm9qZWN0IEkgd2lsbCBiZSBnYXRoZXJpbmcgbW92aWUgcmF0aW5ncyBmcm9tIGRpZmZlcmVudCBzb3VyY2VzIGFuZCBub3JtYWxpemluZyB0aGUgZGlmZmVyZW50IHJhdGluZ3MuIEkgd2lsbCBiZSBnYXRoZXJpbmcgdGhlIGluZm9ybWF0aW9uIGZyb20gZGlmZmVyZW50IG1vdmllIHJhdGluZyBzaXRlcyBsaWtlIElNREIsIFJvdHRlbiBUb21hdG8sIGFuZCBNZXRhY3JpdGljcy4gRWFjaCBvZiB0aGVzZSBzaXRlcyBoYXZlIGRpZmZlcmVudCByYXRpbmcgc3lzdGVtcyBhbmQgZGlmZmVyZW50IHJhdGluZyBzY2FsZXMsIHdoaWNoIHdpbGwgYmUgbm9ybWFsaXplZCBpbiB0aGlzIHByb2plY3QgaW4gb3JkZXIgdG8gY3JlYXRlIG9uZSBzaW5nbGUgcmF0aW5nLiBJIHdpbGwgYWxzbyBiZSBnYXRoZXJpbmcgaW5mb3JtYXRpb24gYnkgc2NyYXBwaW5nIHdlYnNpdGVzIHRoYXQgZG8gbm90IGhhdmUgYSBvcGVuIHNvdXJjZSBSRVNURlVMIEFQSSB0byB1c2UuIEZvciB0aGlzIHByb2plY3QgSSB3aWxsIGJlIGNyZWF0aW5nIGEgbm9ybWFsaXplZCBsaXN0IG9mIHJhdGluZ3MgZm9yIGFsbCBNaWNoZWFsIEJheSBNb3ZpZXMuCgoKIyBQcmVyZXF1aXNpdGUgQmVmb3JlIEdhdGhlcmluZyBEYXRhCgpXZSB3aWxsIGJlIG5lZWRpbmcgdG8gY3JlYXRlIGEgbGlzdCBvZiBhbGwgTWljaGVhbCBCYXkgbW92aWVzIGluIHdoaWNoIGhlIGRpcmVjdGVkLiBUaGlzIGxpc3Qgd2lsbCBiZSB1c2VkIHRvIHBhc3MgdGhyb3VnaCB0byB0aGUgUkVTVEZVTCBBUEkgaW4gb3JkZXIgdG8gZ2V0IHRoZSByYXRpbmdzIG9mIHRoZXNlIG1vdmllcy4KYGBge3J9Cm1vdmllX2xpc3QgPSBjKCdCYWQgQm95cycsICdUaGUgUm9jaycsICdBcm1hZ2VkZG9uJywgJ1BlYXJsIEhhcmJvcicsICdCYWQgQm95cyBJSScsICdUaGUgSXNsYW5kJywgJ1RyYW5zZm9ybWVycycsICdUcmFuc2Zvcm1lcnM6IFJldmVuZ2Ugb2YgdGhlIEZhbGxlbicsICdUcmFuc2Zvcm1lcnM6IERhcmsgb2YgdGhlIE1vb24nLCAnUGFpbiAmIEdhaW4nLCAnVHJhbnNmb3JtZXJzOiBBZ2Ugb2YgRXh0aW5jdGlvbicsICdUcmFuc2Zvcm1lcnM6IFRoZSBMYXN0IEtuaWdodCcpCmBgYAoKCiMgT01EQiBBUEkKCklNREIsIFJvdHRlbiBUb21hdG8sIGFuZCBNZXRhY3JpdGljcyBjdXJyZW50bHkgZG9lcyBub3QgaGF2ZSBhIG9wZW4gc291cmNlIFJFU1RGVUwgQVBJIGZvciB0aGUgcHVibGljIHRvIGdhdGhlciBkYXRhLiBMdWNraWx5IHRoZXJlIGlzIGEgb3BlbiBzb3VyY2UgcHJvamVjdCBjYWxsZWQgW09NRGJdKCdodHRwOi8vd3d3Lm9tZGJhcGkuY29tLycpIHdoaWNoIHByb3ZpZGVzIGFuIG9wZW4gc291cmNlIEFQSSB0byBnYXRoZXIgYWxsIGRldGFpbHMgZm9yIGEgbW92aWUgYW5kIGFsc28gdGhlIHJhdGluZ3MgZnJvbSB0aGUgZGlmZmVyZW50IHNpdGVzLiBXZSB3aWxsIGFsc28gbmVlZCB0byBpbXBsZW1lbnQgY2hlY2tzIHRvIG1ha2Ugc3VyZSB0aGF0IHdlIGFyZSBnZXR0aW5nIHRoZSByaWdodCBtb3ZpZSB3aXRoIHRoZSBBUEkgYnkgY2hlY2tpbmcgdG8gbWFrZSBzdXJlIHRoYXQgdGhlIGRpcmVjdG9ycyBvZiBhbGwgdGhlIG1vdmllcyBpcyBNaWNoZWFsIEJheS4KCmBgYHtyfQpsaWJyYXJ5KGRvdGVudikKbGlicmFyeShodHRyKQpsaWJyYXJ5KGpzb25saXRlKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpgYGB7ciBUZXN0aW5nIG9mIHRoZSBBUEl9CmJhc2UgPSAnaHR0cDovL3d3dy5vbWRiYXBpLmNvbScKYXBpID0gU3lzLmdldGVudignQVBJX0tFWScpCgpyZXNwb25zZSA9IEdFVChiYXNlLCBxdWVyeSA9IGxpc3QoJ2FwaWtleScgPSBhcGksICd0eXBlJyA9ICdtb3ZpZScsICd0JyA9ICdiYWQgYm95JykpCnJlc3BvbnNlX3RleHQgPSBjb250ZW50KHJlc3BvbnNlLCAndGV4dCcpCgpqc29uX2ZpbGUgPSBmcm9tSlNPTihyZXNwb25zZV90ZXh0KQpgYGAKCmBgYHtyIGZ1bmN0aW9uIHRvIGdldCB0aGUgcmF0aW5ncyBtb3ZpZX0KCmdldF9tb3ZpZV9pbmZvID0gZnVuY3Rpb24obW92aWVfdGl0bGUpCnsKICBiYXNlID0gJ2h0dHA6Ly93d3cub21kYmFwaS5jb20nCiAgYXBpID0gU3lzLmdldGVudignQVBJX0tFWScpCiAgCiAgcmVzcG9uc2UgPSBHRVQoYmFzZSwgcXVlcnkgPSBsaXN0KCdhcGlrZXknID0gYXBpLCAndHlwZScgPSAnbW92aWUnLCAndCcgPSBtb3ZpZV90aXRsZSkpCiAgcmVzcG9uc2VfdGV4dCA9IGNvbnRlbnQocmVzcG9uc2UsICd0ZXh0JykKICAKICBqc29uX2ZpbGUgPSBmcm9tSlNPTihyZXNwb25zZV90ZXh0KQogIAogIGpzb25fZmlsZSA9IGpzb25fZmlsZSRSYXRpbmdzCiAgCiAganNvbl9maWxlID0ganNvbl9maWxlICU+JQogICAgbXV0YXRlKE1vdmllID0gbW92aWVfdGl0bGUpCiAgCiAgcmV0dXJuKGpzb25fZmlsZSkKfQpgYGAKCmBgYHtyIGxvb3BpbmcgdGhyb3VnaCB0aGUgbW92aWVzIGFuZCBnZXR0aW5nIGFsbCB0aGUgcmF0aW5nc30KY29sdW1ucyA9IGMoJ1NvdXJjZScsICdWYWx1ZScsICdNb3ZpZScpCnJhdGluZyA9IGRhdGEuZnJhbWUobWF0cml4KG5yb3cgPSAwLCBuY29sID0gbGVuZ3RoKGNvbHVtbnMpKSkKY29sbmFtZXMocmF0aW5nKSA9IGNvbHVtbnMKCmZvciAobW92aWUgaW4gbW92aWVfbGlzdCkKewogIHJhdGluZyA9IHJiaW5kKHJhdGluZywgZ2V0X21vdmllX2luZm8obW92aWUpKQp9CmBgYAoKV2UgYXJlIG5vdyBhYmxlIHRvIGdldCBhbGwgdGhlIHJhdGluZ3MgdXNpbmcgT01EQiBBUEkgZnJvbSB0aGUgYmlnZ2VzdCByYXRpbmcgc2l0ZXMgSU1EQiwgUm90dGVuIFRvbWF0bywgYW5kIE1ldGFjcml0aWNzLiAKCiMgUm9nZXIgRWJlcnQgUmV2aWV3cwoKSSBhbSBhbHNvIGdvaW5nIHRvIGJlIGdldHRpbmcgdGhlIHJhdGluZ3MgZnJvbSBbUm9nZXJFYnJldC5jb21dKGh0dHBzOi8vd3d3LnJvZ2VyZWJlcnQuY29tL2Nhc3QtYW5kLWNyZXcvbWljaGFlbC1iYXkpIHdoaWNoIGhhcyBhIHNwZWNpZmljIHNlY3Rpb24gc2hvd2luZ3MgdGhlIHJhdGluZ3MgZm9yIGFsbCBNaWNoYWVsIEJheSBtb3ZpZXMuIFRoaXMgd2Vic2l0ZSBkb2VzIG5vdCBoYXZlIGEgQVBJIHNvIEkgd2lsbCBiZSBwdXR0aW5nIGFsbCB0aGUgbW92aWVzIGludG8gYSBleGNlbCBzcHJlYWRzaGVldCBhbmQgZXhwb3J0aW5nIHRoZW0gaW50byBhIGNzdi4gVGhlbiBJIHdpbGwgYmUgbG9hZGluZyB0aGUgY3N2IGZpbGUgYW5kIGNvbWJpbmluZyB0aGUgZGF0YSB0aGF0IEkgZ290IHdpdGggdGhlIFJlc3RmdWwgQVBJLgoKYGBge3J9CmZpbGUgPSByZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3h2aWN4cHgvRGF0YTYwNy9tYWluL0ZpbmFsJTIwUHJvamVjdC9Sb2dlciUyMEViZXJ0JTIwTWljaGFlbCUyMEJheS5jc3YnLCBjb2xfbmFtZXM9YygnTW92aWUnLCAnVmFsdWUnKSkKCmZpbGUgPSBmaWxlICU+JQogIG11dGF0ZShTb3VyY2UgPSAnUm9iZXJ0ZWJlcnQuY29tJykKCnJhdGluZyA9IHJiaW5kKHJhdGluZywgZmlsZSkKYGBgCgojIFVucGl2b3RpbmcgdGhlIERhdGEgYW5kIE5vcm1hbGl6aW5nIHRoZSBEYXRhCgpJIGFtIGdvaW5nIHRvIHVucGl2aW90IHRoZSBkYXRhIHNvIHRoYXQgaXQgd2lsbCBiZSBlYXNpZXIgdG8gY29tcGFyZSB0aGUgcmF0aW5nIG9mIGVhY2ggb2YgdGhlIG1vdmllcwpgYGB7cn0KcmF0aW5nICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb209U291cmNlLCB2YWx1ZXNfZnJvbT1WYWx1ZSkKYGBgCgpGcm9tIGEgcXVpY2sgbG9vayBpdCBpcyBkaWZmaWN1bHQgdG8gc2VlIHdoaWNoIG1vdmllIGlzIGhpZ2hseSByYXRpbmcgYXMgZWFjaCByYXRpbmcgd2Vic2l0ZSBoYXMgdGhlaXIgb3duIHJhdGluZyBzeXN0ZW1zIGFuZCBhbmQgd2VpZ2h0cy4gSSBhbSBnb2luZyBub3JtYWxpemUgYWxsIG9mIHRoZSBkaWZmZXJlbnQgcmF0ZXMgYnkgdXNpbmcgdGhlIHRoZSBNaW4tTWF4IEZlYXR1cmUgU2NhbGluZyAkKFggLSBYX3ttaW59KS8oWF97bWF4fSAtIFhfe21pbn0pJC4gVGhpcyB3aWxsIG5vcm1hbGl6ZSBhbGwgdGhlIHNjb3JlcyBmcm9tIHRoZSBkaWZmZXJlbnQgZnJvbSB0aGUgZGlmZmVyZW50IHNvdXJjZXMuIEFsc28gd2Ugd2lsbCBub3QgYmUgdXNpbmcgdGhlIFogc2NvcmUgbWV0aG9kIG9mIHN0YW5kYXJkaXppbmcgdGhlIHNjb3JlcyBhcyB3ZSBkbyBub3Qga25vdyBpZiB0aGUgZGF0YSBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgYW5kIHVzaW5nIHRoZSBaIHNjb3JlIG1ldGhvZCB0aGVyZSBpcyBubyBib3VuZHMgdG8gdGhlIHJhbmdlLiAKCgpgYGB7cn0Kc291cmNlX3JhdGluZyA9IGRhdGEuZnJhbWUoJ1NvdXJjZScgPSBjKCdJbnRlcm5ldCBNb3ZpZSBEYXRhYmFzZScsICdSb3R0ZW4gVG9tYXRvZXMnLCAnTWV0YWNyaXRpYycsICdSb2JlcnRlYmVydC5jb20nKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgJ01pbicgPSBjKDEsIDEsIDEsIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAnTWF4JyA9IGMoMTAsIDEwMCwgMTAwLCA0KQogICAgICAgICAgICAgICAgICAgICAgICAgICApCmBgYAoKYGBge3IgRml4aW5nIFJhdGVzfQpwYXR0ZXJuID0gJ15cXEQqKFxcZCsoPzpcXC5cXGQrKT8pJwoKcmF0aW5nID0gcmF0aW5nICU+JQogIG11dGF0ZShmaXhfdmFsdWUgPSBzdHJfZXh0cmFjdChWYWx1ZSwgcGF0dGVybikpCgpyYXRpbmckZml4X3ZhbHVlID0gYXMubnVtZXJpYyhyYXRpbmckZml4X3ZhbHVlKQpgYGAKCmBgYHtyIGpvaW5uaW5nIG9mIHRoZSBtaW4gYW5kIG1heCByYXRpbmd9CnJhdGluZyA9IHJhdGluZyAlPiUKICBsZWZ0X2pvaW4oc291cmNlX3JhdGluZywgYnk9J1NvdXJjZScpCmBgYAoKYGBge3IgZm9ybXVsYSB0byBub3JtYWxpemUgdGhlIHJhdGluZ3N9CnJhdGluZyA9IHJhdGluZyAlPiUKICBtdXRhdGUobm9ybWFsX3ZhbHVlID0gKChmaXhfdmFsdWUtTWluKS8oTWF4IC0gTWluKSkpCmBgYAoKTm93IHRoYXQgd2UgaGF2ZSBub3JtYWxpemVkIGFsbCB0aGUgcmF0aW5ncyBsZXRzIHNlZSB3aGljaCBhcmUgdGhlIGhpZ2hlc3QgcmF0ZWQgbW92aWVzLiBJdCBpcyBzdGlsbCBkaWZmaWN1bHQgdG8gc2VlIHdoaWNoIG9uZSBpcyB0cnVseSB0aGUgaGlnaGVzdCByYXRlZCBtb3ZpZSBzaW5jZSB0aGVyZSBpcyBzbyBtYW55IGRpZmZlcmVudCByYXRpbmdzIGZvciBlYWNoIG1vdmllLiBTb21lIHNvdXJjZXMgbWF5IHJhdGUgYSBtb3ZpZSBoaWdoIHdoaWxlIG90aGVycyBtYXkgcmF0ZSBpdCBsb3cuIEkgYW0gZ29pbmcgdG8gbm93IHRha2UgYWxsIHRoZSBub3JtYWxpemVkIHJhdGluZ3MgYW5kIGFnZ3JlZ2F0ZSB0aGVtIGludG8gb25lIGZpbmFsIHJhdGluZyB0byBzZWUgd2hpY2ggb25lIGlzIHRydWx5IHRoZSBoaWdoZXN0IHJhdGVkIE1pY2hhZWwgQmF5IE1vdmllLgoKYGBge3J9CnJhdGluZyAlPiUKICBzZWxlY3QoU291cmNlLCBNb3ZpZSwgbm9ybWFsX3ZhbHVlKSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tPVNvdXJjZSwgdmFsdWVzX2Zyb209bm9ybWFsX3ZhbHVlKQpgYGAKIyBHcmFwaGljcyBhbmQgU3RhdGlzdGljYWwgQW5hbHlzaXMKCk5vdyB0aGF0IHdlIGhhdmUgbm9ybWFsaXplZCBhbGwgdGhlIGRpZmZlcmVudCByYXRlcyBmcm9tIHRoZSBkaWZmZXJlbnQgc291cmNlcyB3ZSBjYW4gdGFrZSB0aGUgYXZlcmFnZSBvZiBhbGwgdGhlIHNjb3JlcyBpbiBvcmRlciB0byBnZXQgb25lIGZpbmFsIHNjb3JlLiBUaGlzIHdpbGwgYWxsb3cgdXNlcnMgdG8gZWFzaWx5IGRldGVybWluZSB3aGljaCBtb3ZpZSBpcyB0aGUgYmVzdCB3aXRob3V0IG5lZWRpbmcgdG8gbG9vayBhdCBhbGwgdGhlIGRhdGEuCgpgYGB7cn0KcmF0aW5nICU+JQogIGZpbHRlcighaXMubmEobm9ybWFsX3ZhbHVlKSkgJT4lCiAgZ3JvdXBfYnkoTW92aWUpICU+JQogIHN1bW1hcml6ZShhdmdfbm9ybV92YWx1ZSA9IG1lYW4obm9ybWFsX3ZhbHVlKSkgJT4lCiAgYXJyYW5nZShkZXNjKGF2Z19ub3JtX3ZhbHVlKSkKYGBgCkFzIHdlIGNhbiBzZWUgVGhlIFJvY2sgaXMgdGhlIGhpZ2hlc3QgcmF0ZWQgTWljaGFlbCBCYXkgbW92aWUgd2l0aCBUcmFuc2Zvcm1lcnMgY29taW5nIGF0IGEgY2xvc2Ugc2Vjb25kLiAKCiMjIEdyYXBoaWNzCgpQbG90dGluZyBvdXQgYWxsIHRoZSBtb3ZpZXMgYW5kIGNvbXBhcmluZyBob3cgZWFjaCBzb3VyY2UgcmF0ZXMgdGhlIG1vdmllIGluIG9yZGVyIHRvIHNlZSBpZiBhIGNlcnRhaW4gc291cmNlIGhhcyBiaWFzIGFnYWluc3QgTWljaGVhbCBCYXkgbW92aWVzCgpgYGB7cn0KcmF0aW5nICU+JQogIGdncGxvdChhZXMoeD1Nb3ZpZSwgeT1ub3JtYWxfdmFsdWUsIGZpbGw9U291cmNlKSkgKyBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpICsgY29vcmRfZmxpcCgpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IElNREIgKEludGVybmV0IE1vdmllIERhdGFiYXNlKSByYXRlcyBtb3ZpZXMgYSBsb3QgaGlnaGVyIGNvbXBhcmVkIHRvIHRoZSBvdGhlciBzb3VyY2VzLiBXaGVuIHRoZSBvdGhlciBzb3VyY2VzIHJhdGUgYSBNaWNoZWFsIEJheSBtb3ZpZSB2ZXJ5IGxvdyBJTURCIHVzdWFsbHkgcmF0ZXMgaXQgcmVhbGx5IGhpZ2guIElmIHdlIGxvb2sgYXQgdGhlIHJhdyBkYXRhIHdlIGNhbiBlYXNpbHkgc2VlIHRoaXMgd2l0aCBtYW55IG1vdmllcyB3aGVyZSBSb3R0ZW4gVG9tYXRvIHJhdGVzIGEgbW92aWUgMjAvMTAwIGJ1dCBJTURCIHdvdWxkIHJhdGUgdGhlIG1vdmllIDYvMTAuIEJ1dCBhcyBsb25nIGFzIElNREIgcmF0ZXMgaXMgcmF0aW5nIHRoZSBtb3ZpZXMgY29uc2lzdGVudGx5IHdpdGhvdXQgYW55IGJpYXMsIHRoZW4gaXQgc2hvdWxkIG5vdCBoYXZlIGFuIGVmZmVjdCBvbiB0aGUgbm9ybWFsaXphdGlvbiBvZiB0aGUgcmF0aW5nLiAKCmBgYHtyfQpyYXRpbmcgJT4lCiAgZmlsdGVyKCFpcy5uYShub3JtYWxfdmFsdWUpKSAlPiUKICBncm91cF9ieShNb3ZpZSkgJT4lCiAgc3VtbWFyaXplKGF2Z19ub3JtX3ZhbHVlID0gbWVhbihub3JtYWxfdmFsdWUpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKE1vdmllLCBhdmdfbm9ybV92YWx1ZSksIHkgPSBhdmdfbm9ybV92YWx1ZSkpICsgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArIGNvb3JkX2ZsaXAoKQpgYGAKCgojIENvbmNsdXNpb24KV2UgY2FuIHNlZSB0aGF0IHRoZSBoaWdoZXN0IHJhdGVkIG1vdmllIGlzIFRoZSBSb2NrIGFuZCB3ZSBjYW4gY29uZmlybSB0aGF0IGJ5IGxvb2tpbmcgYXQgdGhlIGluZGl2aWR1YWwgcmF0aW5ncyBmcm9tIHRoZSBkaWZmZXJlbnQgc291cmNlcy4gV2hpbGUgdGhlcmUgYXJlIG11bHRpcGxlIG1ldGhvZHMgb2Ygbm9ybWFsaXppbmcgc2NvcmVzLCB0aGUgTWluLU1heCBGZWF0dXJlIFNjYWxpbmcgd2FzIHRoZSBlYXNpZXN0IHRvIGltcGxlbWVudCBlc3BlY2lhbGx5IHNpbmNlIEkgZGlkIG5vdCBrbm93IHRoZSBwb3B1bGF0aW9uIG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIGlmIHRoZSBzY29yZXMgd2VyZSBub3JtYWwuIFRoZSBkb3duc2lkZSBvZiB0aGUgTWluLU1heCBGZWF0dXJlIFNjYWxpbmcgaXMgdGhhdCBkZXBlbmRpbmcgb24gdGhlIHNjYWxpbmcgb2YgdGhlIHJhdGluZyBzeXN0ZW0uIEZvciBleGFtcGxlIFJvYmVydGViZXJ0LmNvbSB3aGljaCBvbmx5IHJhdGVzIG91dCBvZiA0IHdoaWNoIG1lYW5zIHdoZW4gdGhlIHNjb3JlIGlzIG5vcm1hbGl6ZWQgdGhlcmUgd2lsbCBhbHNvIGJlIG9ubHkgNCBkaWZmZXJlbnQgc2NvcmVzLiBTb21ldGhpbmcgbmV3IHRoYXQgSSB0cmllZCB3YXMgY3JlYXRpbmcgbXkgcHJlc2VudGF0aW9uIHVzaW5nIFIgTWFya2Rvd24gaW5zdGVhZCBvZiBwb3dlcnBvaW50LiBXaGlsZSBpdCBpcyBhIGxvdCBlYXNpZXIgbm90IG5lZWRpbmcgdG8gZ28gYmFjayBhbmQgZm9ydGggYmV0d2VlbiBkaWZmZXJlbnQgcHJvZ3JhbXMsIGl0IHJlcXVpcmVzIGxlYXJuaW5nIG9mIHNwZWNpZmljIGNvbW1hbmRzIGluIG9yZGVyIHRvIGNyZWF0ZSB0aGUgcHJlc2VudGF0aW9uLgoK