Netflix Data


             In this assignment we will examine, clean, process and visualize the Netflix data. We will first extract information about the average duration in each genre of the movies. Secondly, we will find out which how many movie and TV-show that have been produced in each year between 2000 and 2019. Lastly, we will see statistical output about TV-shows that shows us the min, max, median, and mean considering season duration.

Information about the Data


             This dataset consists of id, type, title, director, cast, country, the date that has been added to the system, release year, rating, duration, categories that the content is listed in, and lastly the description of the content.

Reading the Data


             In this part, we simply read the CSV file via the link. Also for further details, you can view the structure of the data.

df <- read_csv("https://github.com/ygterl/EDA-Netflix-2020-in-R/raw/master/netflix_titles.csv")
str(df)
spec_tbl_df [6,234 x 12] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ show_id     : num [1:6234] 81145628 80117401 70234439 80058654 80125979 ...
 $ type        : chr [1:6234] "Movie" "Movie" "TV Show" "TV Show" ...
 $ title       : chr [1:6234] "Norm of the North: King Sized Adventure" "Jandino: Whatever it Takes" "Transformers Prime" "Transformers: Robots in Disguise" ...
 $ director    : chr [1:6234] "Richard Finn, Tim Maltby" NA NA NA ...
 $ cast        : chr [1:6234] "Alan Marriott, Andrew Toth, Brian Dobson, Cole Howard, Jennifer Cameron, Jonathan Holmes, Lee Tockar, Lisa Duru"| __truncated__ "Jandino Asporaat" "Peter Cullen, Sumalee Montano, Frank Welker, Jeffrey Combs, Kevin Michael Richardson, Tania Gunadi, Josh Keaton"| __truncated__ "Will Friedle, Darren Criss, Constance Zimmer, Khary Payton, Mitchell Whitfield, Stuart Allan, Ted McGinley, Peter Cullen" ...
 $ country     : chr [1:6234] "United States, India, South Korea, China" "United Kingdom" "United States" "United States" ...
 $ date_added  : chr [1:6234] "September 9, 2019" "September 9, 2016" "September 8, 2018" "September 8, 2018" ...
 $ release_year: num [1:6234] 2019 2016 2013 2016 2017 ...
 $ rating      : chr [1:6234] "TV-PG" "TV-MA" "TV-Y7-FV" "TV-Y7" ...
 $ duration    : chr [1:6234] "90 min" "94 min" "1 Season" "1 Season" ...
 $ listed_in   : chr [1:6234] "Children & Family Movies, Comedies" "Stand-Up Comedy" "Kids' TV" "Kids' TV" ...
 $ description : chr [1:6234] "Before planning an awesome wedding for his grandfather, a polar bear king must take back a stolen artifact from"| __truncated__ "Jandino Asporaat riffs on the challenges of raising kids and serenades the audience with a rousing rendition of"| __truncated__ "With the help of three human allies, the Autobots once again protect Earth from the onslaught of the Decepticon"| __truncated__ "When a prison ship crash unleashes hundreds of Decepticons on Earth, Bumblebee leads a new Autobot force to protect humankind." ...
 - attr(*, "spec")=
  .. cols(
  ..   show_id = col_double(),
  ..   type = col_character(),
  ..   title = col_character(),
  ..   director = col_character(),
  ..   cast = col_character(),
  ..   country = col_character(),
  ..   date_added = col_character(),
  ..   release_year = col_double(),
  ..   rating = col_character(),
  ..   duration = col_character(),
  ..   listed_in = col_character(),
  ..   description = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 


             As seen above, the data consists of 6,234 rows and 12 columns related to tv shows and movies. But also we can see there are some unnecessary variables and improperly formatted data.

Cleaning the Data


             As mentioned above, in this part we will clean and transform the data so we can get rid of the unnecessary variables and improperly formatted data. For starters, we eliminate the show_id column since we will not use it in our analysis.

df$show_id <- NULL


             Then, we convert date_added column to date format instead of character so we can easily filter or reorder the column as we desire.

df$date_added <- mdy(df$date_added)


             For rating, listed_in and type column we have to convert them into categorical values rather than characters. For that, we will use factor function which is used to encode a vector as a factor. Factor returns an object of class “factor”.

df$rating <- as.factor(df$rating)
df$listed_in <- as.factor(df$listed_in)
df$type <- as.factor(df$type)


             Lastly, we will eliminate the duplicated rows depending on the title, country, type, release_year columns.

df <- distinct(df, title, country, type, release_year, .keep_all = TRUE)


             With that last part we concluded our data cleaning process.


Process and Visualization of the Data


             In this part, first, we will use multiple packages to process and filter the data so we can have the necessary rows and columns. Then we will use the ggplot2 package to create some visualization with the processed data.


1-) The Average Duration in each Genre of Movie


             For the first part of our data processing, we will find mean values of duration in minutes in each genre of the movies.

df$duration <- lapply(df$duration, function(a) gsub(" min", "", a))
durations_movie <- df %>%
  filter(type == "Movie") %>%
  mutate(genre = gsub("^(.*?),.*", "\\1", listed_in)) %>%
  group_by(genre) %>%
  summarise(average_duration = round(mean(as.integer(duration)))) %>%
  ungroup()
durations_movie


             As seen in the code we used several functions to get the data in the form we need. What may come as strange is we also implemented RegEx in the second gsub function in order to locate the string types we needed. First, we had to get rid of the ” min” parts of the duration column so we would calculate the mean. Then we have proceeded to filter the data and group by. But since a movie may contain multiple genres we got 249 rows in which the same genre was produced over and over just because the combination of the genres in the same movie was different. That issue was solved by using regular expressions. Since different genres in a movie are separated we used a RegEx which means get anything until a comma. The rest is usual.


             Now we will visualize the data with a bar chart since this data consists of categorical variable

ggplot(durations_movie, aes(x=reorder(genre, average_duration), y=average_duration)) +
  geom_bar(colour="black", size=0.3, fill="#E3363f", stat="identity") +
  ylim(0, 120) +
  coord_flip() +
  geom_text(aes(label = paste(format(average_duration, nsmall=0), "min")),
            position = position_stack(vjust=0.5),
            colour = "black", size = 3) +
  xlab("Netflix Movie Genres") + ylab("Average Duration (min)") +
  ggtitle("Average Duration in each Genre of the Movies")


             As seen above “Action and Adventure” genre has the highest average screen time.

2-) Number of Movies and TV-shows in each year between 2000 and 2019


             In the second analysis, we will find the number of movies and tv-shows in each year on Netflix between 2000 and 2019. We exclude 20th-century content since there is so little amount of them in a year. So let’s start with processing the data.

number_movies_tvshows <- df %>%
  filter(release_year >= 2000 & release_year < 2020) %>%
  group_by(type, release_year) %>%
  summarise(count = n()) %>%
  arrange(desc(release_year)) %>%
  ungroup()
number_movies_tvshows


             As you can see 2017, 2018, and 2016 there were hundreds of movies whereas the year that most tv-shows produced was 2019.


             Now it’s time to visualize the data on a line chart.

ggplot(number_movies_tvshows, aes(release_year, count)) +
  geom_line(aes(colour = type), size = 1.5, linetype = "dotted") +
  geom_point(size = 2.5) +
  ggtitle("Number of Movies and TV-shows in each year") +
  theme(legend.position=c(0.2, 0.8),
        legend.key.size = unit(1, 'cm'),
        legend.title = element_text(size=14),
        legend.text = element_text(size=10),
        legend.key=element_blank(),
        legend.background=element_rect(fill = "lightgrey"),
        axis.text.x = element_text(angle = 90, vjust = 1)) +
  labs(color="Type of the Content") +
  scale_x_continuous(name="Year", breaks=seq(2000, 2019)) +
  scale_y_continuous(name="Number of Movies and TV-Shows", breaks=seq(0, 700, 50))


             The line chart shows us there was only one year, 2019, that amount of the tv-shows surpassed the number of movies. But the decrease in the number of movies started in 2018 so we can expect to see more tv-shows than movies, in the future.

3-) Statistical Summary of the Season Length of TV-shows


             Lastly, we will process the data so we get a statistical summary about the season length of TV-shows. Even though we could find a way to implement summary function which gives us all the values we need, minimum, 1st quartile, median, mean, 3rd quartile and max values, we will find them seperately by using their own functions in order to demonstrate how diverse and practical R is.


             Well, we all can guess what is the minimum number of seasons for a TV show, 1 season. But what about the longest tv show in our data. Let’s find out.

df$duration <- lapply(df$duration, function(b) as.numeric(gsub("([0-9]+).*$", "\\1", b)))
maximum_season <- df %>%
  filter(type == "TV Show") %>%
  slice(which.max(as.numeric(duration))) %>%
  summarise(title, longest_season = as.numeric(duration)) %>%
  ungroup()
maximum_season


             We inferred that “Grey’s Anatomy” was the longest tv show in our data with 15 seasons.


             Now let’s find the median season length and its corresponding title.

median_season <- df %>%
  filter(type == "TV Show") %>%
  slice(median(as.numeric(duration))) %>%
  summarise(title, median_season = as.numeric(duration)) %>%
  ungroup()
median_season


             Since there is a lot of tv shows with 1 season our median is 1 season with the “Transformers Prime” tv show. So far with the information, we have, minimum, maximum, and median, we can assume that this is a right-skewed distribution.


             Let’s not just assume and visualize the data we already have.

season_number <- df %>%
  filter(type == "TV Show")
ggplot(season_number, aes(x=as.numeric(duration))) +
  geom_histogram(fill="#E3363f", binwidth=1) +
  geom_vline(aes(xintercept=mean(as.numeric(duration))), color="#366de3", linetype="dashed", size=0.9) +
  annotate(
    "text",
    x = 2.5, y = 1200,
    label = "The\naverage\nnumber\nof\nseasons",
    vjust = 1, size = 3, color = "#366de3"
  ) +
  geom_vline(aes(xintercept=median(as.numeric(duration))), color="grey40", linetype="dashed", size=0.9) +
  annotate(
    "text",
    x = .5, y = 1200,
    label = "The\nmedian\nnumber\nof\nseasons",
    vjust = 1, size = 3, color = "grey40"
  ) +
  xlab("Number of Season") +
  ylab("Count of Number of Season") +
  ggtitle("Distribution of Season Length") +
  theme(legend.title = element_blank())


             As expected, this is a right-skewed distribution. For better understanding, I also included the other statistics we have obtained.


             In this part we will calculate the average season length of each genre of tv shows.

season_length_tvshows <- df %>%
  filter(type == "TV Show") %>%
  mutate(genre = gsub("^(.*?),.*", "\\1", listed_in)) %>%
  group_by(genre) %>%
  summarise(average_season_length = round(mean(as.integer(duration), na.rm = TRUE), 1)) %>%
  ungroup()
season_length_tvshows


             For this part we will visualize the data on horizontal bar chart.

ggplot(season_length_tvshows, aes(x=reorder(genre, average_season_length), y=average_season_length)) +
  geom_bar(colour="black", size=0.3, fill="#E3363f", stat="identity") +
  ylim(0, 6) +
  coord_flip() +
  geom_text(aes(label = paste(format(average_season_length, nsmall=0), " Season")),
            position = position_stack(vjust=0.5),
            colour = "black", size = 3) +
  xlab("Netflix TV Show Genres") + ylab("Average Length (season)") +
  ggtitle("Average Season Length in each Genre of the TV Shows")


             It is clear that Classical & Cult TV Shows have the highest average season length with 5.8 Season, followed by Romantic TV Shows.


             Well, as we head to end this report, we will include the way to locate 1st and 3rd quartiles, just to demonstrate the proper functions, not like they provide us any insights.

duration_quartiles <- df %>%
  filter(type == "TV Show") %>%
  group_by(type) %>%
  summarise(Q1 = quantile(as.numeric(duration), 0.25),
            Q3 = quantile(as.numeric(duration), 0.75)) %>%
  ungroup()
duration_quartiles


Summary


             Today we have dealt with Netflix Data. Nowadays people tend to get lost in daily problems, career goals, and social media, but sometimes it is necessary to just retreat from all and chill. Well, that brings us to Netflix. In conclusion to our analyses, we can say movie production statistically tends to diminish whereas tv shows are rising, especially with the help of streaming platforms. There is a high chance that we will see more and more tv shows. We also concluded that watching Grey’s Anatomy from the beginning would take so much time in the nowadays competitive world. But thankfully seeing from our histogram tv shows tend to be as short as 1 season, which is the perfect amount to get the gist of the story and finish it in one go. Even though tv shows evolve to be shorter we also see the number of seasons of Romantic TV Shows is still high with 3.6 seasons, averagely. But being a documentary and docuseries lover saves me so much time to work more on data analytics, as seen in our outputs.




Disclaimer: First part of this report is overly inspired by Yigit Erol’s Medium post and Github repository. All the excerpts and analyses are done for educational purposes.

                                               Thanks for reading…



R

LS0tDQp0aXRsZTogIioqQkRBLTUwMyBXZWVrIDcgLSBOZXRmbGl4IFRhc2sqKiINCmF1dGhvcjogIl9FbWlyaGFuIMWeYWhpbl8iDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiDQpvdXRwdXQ6DQogICAgaHRtbF9ub3RlYm9vazoNCiAgICAgICAgdG9jOiB0cnVlDQogICAgICAgIHRvY19kZXB0aDogMg0KICAgICAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KPGJyPg0KPHN0eWxlPg0KI1RPQyB7DQogICAgY29sb3I6ICM0MDQwNDA7DQogICAgZm9udC1zaXplOiAxM3B4Ow0KICAgIGJvcmRlci1jb2xvcjogIzAwMDAwMDsNCiAgICBmb250LWZhbWlseTogLWFwcGxlLXN5c3RlbSwgc3lzdGVtLXVpLCBCbGlua01hY1N5c3RlbUZvbnQsICJTZWdvZSBVSSIsIFJvYm90bywgVWJ1bnR1Ow0KfQ0KaDEudGl0bGUgew0KICAgIGNvbG9yOiAjNDA0MDQwOw0KICAgIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBzeXN0ZW0tdWksIEJsaW5rTWFjU3lzdGVtRm9udCwgIlNlZ29lIFVJIiwgUm9ib3RvLCBVYnVudHU7DQp9DQpoNC5hdXRob3Igew0KICAgIGNvbG9yOiAjNDA0MDQwOw0KICAgIGZvbnQtc2l6ZTogMTJweDsNCiAgICBmb250LWZhbWlseTogLWFwcGxlLXN5c3RlbSwgc3lzdGVtLXVpLCBCbGlua01hY1N5c3RlbUZvbnQsICJTZWdvZSBVSSIsIFJvYm90bywgVWJ1bnR1Ow0KfQ0KaDQuZGF0ZSB7DQogICAgY29sb3I6ICM0MDQwNDA7DQogICAgZm9udC1zaXplOiAxMnB4Ow0KICAgIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBzeXN0ZW0tdWksIEJsaW5rTWFjU3lzdGVtRm9udCwgIlNlZ29lIFVJIiwgUm9ib3RvLCBVYnVudHU7DQp9DQpib2R5IHsNCiAgICBjb2xvcjogIzY5Njk2OTsNCiAgICBmb250LWZhbWlseTogLWFwcGxlLXN5c3RlbSwgc3lzdGVtLXVpLCBCbGlua01hY1N5c3RlbUZvbnQsICJTZWdvZSBVSSIsIFJvYm90bywgVWJ1bnR1Ow0KICAgIGJhY2tncm91bmQtY29sb3I6ICNGNUY1RjU7DQogICAgZm9udC1zaXplOiAxNHB4Ow0KPC9zdHlsZT4NCg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IDxzcGFuIHN0eWxlPSJjb2xvcjojNDA0MDQwO2ZvbnQtc2l6ZTo1MHB4Ij4qKk5ldGZsaXggRGF0YSoqPC9zcGFuPg0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgSW4gdGhpcyBhc3NpZ25tZW50IHdlIHdpbGwgZXhhbWluZSwgY2xlYW4sIHByb2Nlc3MgYW5kIHZpc3VhbGl6ZSB0aGUgW05ldGZsaXggZGF0YV0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3lndGVybC9FREEtTmV0ZmxpeC0yMDIwLWluLVIvbWFzdGVyL25ldGZsaXhfdGl0bGVzLmNzdikuIFdlIHdpbGwgZmlyc3QgZXh0cmFjdCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgYXZlcmFnZSBkdXJhdGlvbiBpbiBlYWNoIGdlbnJlIG9mIHRoZSBtb3ZpZXMuIFNlY29uZGx5LCB3ZSB3aWxsIGZpbmQgb3V0IHdoaWNoIGhvdyBtYW55IG1vdmllIGFuZCBUVi1zaG93IHRoYXQgaGF2ZSBiZWVuIHByb2R1Y2VkIGluIGVhY2ggeWVhciBiZXR3ZWVuIDIwMDAgYW5kIDIwMTkuIExhc3RseSwgd2Ugd2lsbCBzZWUgc3RhdGlzdGljYWwgb3V0cHV0IGFib3V0IFRWLXNob3dzIHRoYXQgc2hvd3MgdXMgdGhlIG1pbiwgbWF4LCBtZWRpYW4sIGFuZCBtZWFuIGNvbnNpZGVyaW5nIHNlYXNvbiBkdXJhdGlvbi4NCjxicj4NCg0KIyAqKkluZm9ybWF0aW9uIGFib3V0IHRoZSBEYXRhKioNCg0KPGJyPg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IFRoaXMgZGF0YXNldCBjb25zaXN0cyBvZiBpZCwgdHlwZSwgdGl0bGUsIGRpcmVjdG9yLCBjYXN0LCBjb3VudHJ5LCB0aGUgZGF0ZSB0aGF0IGhhcyBiZWVuIGFkZGVkIHRvIHRoZSBzeXN0ZW0sIHJlbGVhc2UgeWVhciwgcmF0aW5nLCBkdXJhdGlvbiwgY2F0ZWdvcmllcyB0aGF0IHRoZSBjb250ZW50IGlzIGxpc3RlZCBpbiwgYW5kIGxhc3RseSB0aGUgZGVzY3JpcHRpb24gb2YgdGhlIGNvbnRlbnQuDQoNCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmBgYA0KDQojICoqUmVhZGluZyB0aGUgRGF0YSoqDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBJbiB0aGlzIHBhcnQsIHdlIHNpbXBseSByZWFkIHRoZSBDU1YgZmlsZSB2aWEgdGhlIGxpbmsuIEFsc28gZm9yIGZ1cnRoZXIgZGV0YWlscywgeW91IGNhbiB2aWV3IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEuDQoNCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9DQpkZiA8LSByZWFkX2NzdigiaHR0cHM6Ly9naXRodWIuY29tL3lndGVybC9FREEtTmV0ZmxpeC0yMDIwLWluLVIvcmF3L21hc3Rlci9uZXRmbGl4X3RpdGxlcy5jc3YiKQ0Kc3RyKGRmKQ0KYGBgDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBBcyBzZWVuIGFib3ZlLCB0aGUgZGF0YSBjb25zaXN0cyBvZiA2LDIzNCByb3dzIGFuZCAxMiBjb2x1bW5zIHJlbGF0ZWQgdG8gdHYgc2hvd3MgYW5kIG1vdmllcy4gQnV0IGFsc28gd2UgY2FuIHNlZSB0aGVyZSBhcmUgc29tZSB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMgYW5kIGltcHJvcGVybHkgZm9ybWF0dGVkIGRhdGEuDQoNCg0KIyAqKkNsZWFuaW5nIHRoZSBEYXRhKioNCg0KPGJyPg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IEFzIG1lbnRpb25lZCBhYm92ZSwgaW4gdGhpcyBwYXJ0IHdlIHdpbGwgY2xlYW4gYW5kIHRyYW5zZm9ybSB0aGUgZGF0YSBzbyB3ZSBjYW4gZ2V0IHJpZCBvZiB0aGUgdW5uZWNlc3NhcnkgdmFyaWFibGVzIGFuZCBpbXByb3Blcmx5IGZvcm1hdHRlZCBkYXRhLiBGb3Igc3RhcnRlcnMsIHdlIGVsaW1pbmF0ZSB0aGUgc2hvd19pZCBjb2x1bW4gc2luY2Ugd2Ugd2lsbCBub3QgdXNlIGl0IGluIG91ciBhbmFseXNpcy4NCg0KYGBge3J9DQpkZiRzaG93X2lkIDwtIE5VTEwNCmBgYA0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgVGhlbiwgd2UgY29udmVydCBkYXRlX2FkZGVkIGNvbHVtbiB0byBkYXRlIGZvcm1hdCBpbnN0ZWFkIG9mIGNoYXJhY3RlciBzbyB3ZSBjYW4gZWFzaWx5IGZpbHRlciBvciByZW9yZGVyIHRoZSBjb2x1bW4gYXMgd2UgZGVzaXJlLg0KDQpgYGB7cn0NCmRmJGRhdGVfYWRkZWQgPC0gbWR5KGRmJGRhdGVfYWRkZWQpDQpgYGANCg0KPGJyPg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IEZvciByYXRpbmcsIGxpc3RlZF9pbiBhbmQgdHlwZSBjb2x1bW4gd2UgaGF2ZSB0byBjb252ZXJ0IHRoZW0gaW50byBjYXRlZ29yaWNhbCB2YWx1ZXMgcmF0aGVyIHRoYW4gY2hhcmFjdGVycy4gRm9yIHRoYXQsIHdlIHdpbGwgdXNlIFtmYWN0b3IgZnVuY3Rpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlL3ZlcnNpb25zLzMuNi4yL3RvcGljcy9mYWN0b3IpIHdoaWNoIGlzIHVzZWQgdG8gZW5jb2RlIGEgdmVjdG9yIGFzIGEgZmFjdG9yLiBGYWN0b3IgcmV0dXJucyBhbiBvYmplY3Qgb2YgY2xhc3MgImZhY3RvciIuDQoNCmBgYHtyfQ0KZGYkcmF0aW5nIDwtIGFzLmZhY3RvcihkZiRyYXRpbmcpDQpkZiRsaXN0ZWRfaW4gPC0gYXMuZmFjdG9yKGRmJGxpc3RlZF9pbikNCmRmJHR5cGUgPC0gYXMuZmFjdG9yKGRmJHR5cGUpDQpgYGANCg0KPGJyPg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IExhc3RseSwgd2Ugd2lsbCBlbGltaW5hdGUgdGhlIGR1cGxpY2F0ZWQgcm93cyBkZXBlbmRpbmcgb24gdGhlIHRpdGxlLCBjb3VudHJ5LCB0eXBlLCByZWxlYXNlX3llYXIgY29sdW1ucy4NCg0KYGBge3J9DQpkZiA8LSBkaXN0aW5jdChkZiwgdGl0bGUsIGNvdW50cnksIHR5cGUsIHJlbGVhc2VfeWVhciwgLmtlZXBfYWxsID0gVFJVRSkNCmBgYA0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgV2l0aCB0aGF0IGxhc3QgcGFydCB3ZSBjb25jbHVkZWQgb3VyIGRhdGEgY2xlYW5pbmcgcHJvY2Vzcy4NCg0KPGJyPg0KDQojICoqUHJvY2VzcyBhbmQgVmlzdWFsaXphdGlvbiBvZiB0aGUgRGF0YSoqDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBJbiB0aGlzIHBhcnQsIGZpcnN0LCB3ZSB3aWxsIHVzZSBtdWx0aXBsZSBwYWNrYWdlcyB0byBwcm9jZXNzIGFuZCBmaWx0ZXIgdGhlIGRhdGEgc28gd2UgY2FuIGhhdmUgdGhlIG5lY2Vzc2FyeSByb3dzIGFuZCBjb2x1bW5zLiBUaGVuIHdlIHdpbGwgdXNlIHRoZSBnZ3Bsb3QyIHBhY2thZ2UgdG8gY3JlYXRlIHNvbWUgdmlzdWFsaXphdGlvbiB3aXRoIHRoZSBwcm9jZXNzZWQgZGF0YS4NCg0KPGJyPg0KDQojIyAgKioxLSkgVGhlIEF2ZXJhZ2UgRHVyYXRpb24gaW4gZWFjaCBHZW5yZSBvZiBNb3ZpZSoqDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBGb3IgdGhlIGZpcnN0IHBhcnQgb2Ygb3VyIGRhdGEgcHJvY2Vzc2luZywgd2Ugd2lsbCBmaW5kIG1lYW4gdmFsdWVzIG9mIGR1cmF0aW9uIGluIG1pbnV0ZXMgaW4gZWFjaCBnZW5yZSBvZiB0aGUgbW92aWVzLg0KDQpgYGB7cn0NCmRmJGR1cmF0aW9uIDwtIGxhcHBseShkZiRkdXJhdGlvbiwgZnVuY3Rpb24oYSkgZ3N1YigiIG1pbiIsICIiLCBhKSkNCmR1cmF0aW9uc19tb3ZpZSA8LSBkZiAlPiUNCiAgZmlsdGVyKHR5cGUgPT0gIk1vdmllIikgJT4lDQogIG11dGF0ZShnZW5yZSA9IGdzdWIoIl4oLio/KSwuKiIsICJcXDEiLCBsaXN0ZWRfaW4pKSAlPiUNCiAgZ3JvdXBfYnkoZ2VucmUpICU+JQ0KICBzdW1tYXJpc2UoYXZlcmFnZV9kdXJhdGlvbiA9IHJvdW5kKG1lYW4oYXMuaW50ZWdlcihkdXJhdGlvbikpKSkgJT4lDQogIHVuZ3JvdXAoKQ0KZHVyYXRpb25zX21vdmllDQpgYGANCg0KPGJyPg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IEFzIHNlZW4gaW4gdGhlIGNvZGUgd2UgdXNlZCBzZXZlcmFsIGZ1bmN0aW9ucyB0byBnZXQgdGhlIGRhdGEgaW4gdGhlIGZvcm0gd2UgbmVlZC4gV2hhdCBtYXkgY29tZSBhcyBzdHJhbmdlIGlzIHdlIGFsc28gaW1wbGVtZW50ZWQgW1JlZ0V4XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc3RyaW5nci92aWduZXR0ZXMvcmVndWxhci1leHByZXNzaW9ucy5odG1sKSBpbiB0aGUgc2Vjb25kIFtnc3ViXShodHRwczovL3d3dy5qb3VybmFsZGV2LmNvbS80MzY5MC9zdWItYW5kLWdzdWItZnVuY3Rpb24tcikgZnVuY3Rpb24gaW4gb3JkZXIgdG8gbG9jYXRlIHRoZSBzdHJpbmcgdHlwZXMgd2UgbmVlZGVkLiBGaXJzdCwgd2UgaGFkIHRvIGdldCByaWQgb2YgdGhlICIgbWluIiBwYXJ0cyBvZiB0aGUgZHVyYXRpb24gY29sdW1uIHNvIHdlIHdvdWxkIGNhbGN1bGF0ZSB0aGUgbWVhbi4gVGhlbiB3ZSBoYXZlIHByb2NlZWRlZCB0byBmaWx0ZXIgdGhlIGRhdGEgYW5kIGdyb3VwIGJ5LiBCdXQgc2luY2UgYSBtb3ZpZSBtYXkgY29udGFpbiBtdWx0aXBsZSBnZW5yZXMgd2UgZ290ICoqMjQ5IHJvd3MqKiBpbiB3aGljaCB0aGUgc2FtZSBnZW5yZSB3YXMgcHJvZHVjZWQgb3ZlciBhbmQgb3ZlciBqdXN0IGJlY2F1c2UgdGhlIGNvbWJpbmF0aW9uIG9mIHRoZSBnZW5yZXMgaW4gdGhlIHNhbWUgbW92aWUgd2FzIGRpZmZlcmVudC4gVGhhdCBpc3N1ZSB3YXMgc29sdmVkIGJ5IHVzaW5nIFtyZWd1bGFyIGV4cHJlc3Npb25zXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc3RyaW5nci92aWduZXR0ZXMvcmVndWxhci1leHByZXNzaW9ucy5odG1sKS4gU2luY2UgZGlmZmVyZW50IGdlbnJlcyBpbiBhIG1vdmllIGFyZSBzZXBhcmF0ZWQgd2UgdXNlZCBhIFtSZWdFeF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3N0cmluZ3IvdmlnbmV0dGVzL3JlZ3VsYXItZXhwcmVzc2lvbnMuaHRtbCkgd2hpY2ggbWVhbnMgZ2V0IGFueXRoaW5nIHVudGlsIGEgY29tbWEuIFRoZSByZXN0IGlzIHVzdWFsLg0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgTm93IHdlIHdpbGwgdmlzdWFsaXplIHRoZSBkYXRhIHdpdGggYSBiYXIgY2hhcnQgc2luY2UgdGhpcyBkYXRhIGNvbnNpc3RzIG9mIGNhdGVnb3JpY2FsIHZhcmlhYmxlDQoNCmBgYHtyfQ0KZ2dwbG90KGR1cmF0aW9uc19tb3ZpZSwgYWVzKHg9cmVvcmRlcihnZW5yZSwgYXZlcmFnZV9kdXJhdGlvbiksIHk9YXZlcmFnZV9kdXJhdGlvbikpICsNCiAgZ2VvbV9iYXIoY29sb3VyPSJibGFjayIsIHNpemU9MC4zLCBmaWxsPSIjRTMzNjNmIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIHlsaW0oMCwgMTIwKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZShmb3JtYXQoYXZlcmFnZV9kdXJhdGlvbiwgbnNtYWxsPTApLCAibWluIikpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLA0KICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDMpICsNCiAgeGxhYigiTmV0ZmxpeCBNb3ZpZSBHZW5yZXMiKSArIHlsYWIoIkF2ZXJhZ2UgRHVyYXRpb24gKG1pbikiKSArDQogIGdndGl0bGUoIkF2ZXJhZ2UgRHVyYXRpb24gaW4gZWFjaCBHZW5yZSBvZiB0aGUgTW92aWVzIikNCmBgYA0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgQXMgc2VlbiBhYm92ZSAiQWN0aW9uIGFuZCBBZHZlbnR1cmUiIGdlbnJlIGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIHNjcmVlbiB0aW1lLg0KDQojIyAgKioyLSkgTnVtYmVyIG9mIE1vdmllcyBhbmQgVFYtc2hvd3MgaW4gZWFjaCB5ZWFyIGJldHdlZW4gMjAwMCBhbmQgMjAxOSoqDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBJbiB0aGUgc2Vjb25kIGFuYWx5c2lzLCB3ZSB3aWxsIGZpbmQgdGhlIG51bWJlciBvZiBtb3ZpZXMgYW5kIHR2LXNob3dzIGluIGVhY2ggeWVhciBvbiBOZXRmbGl4IGJldHdlZW4gMjAwMCBhbmQgMjAxOS4gV2UgZXhjbHVkZSAyMHRoLWNlbnR1cnkgY29udGVudCBzaW5jZSB0aGVyZSBpcyBzbyBsaXR0bGUgYW1vdW50IG9mIHRoZW0gaW4gYSB5ZWFyLiBTbyBsZXQncyBzdGFydCB3aXRoIHByb2Nlc3NpbmcgdGhlIGRhdGEuDQoNCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9DQpudW1iZXJfbW92aWVzX3R2c2hvd3MgPC0gZGYgJT4lDQogIGZpbHRlcihyZWxlYXNlX3llYXIgPj0gMjAwMCAmIHJlbGVhc2VfeWVhciA8IDIwMjApICU+JQ0KICBncm91cF9ieSh0eXBlLCByZWxlYXNlX3llYXIpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MocmVsZWFzZV95ZWFyKSkgJT4lDQogIHVuZ3JvdXAoKQ0KbnVtYmVyX21vdmllc190dnNob3dzDQpgYGANCg0KPGJyPg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IEFzIHlvdSBjYW4gc2VlIDIwMTcsIDIwMTgsIGFuZCAyMDE2IHRoZXJlIHdlcmUgaHVuZHJlZHMgb2YgbW92aWVzIHdoZXJlYXMgdGhlIHllYXIgdGhhdCBtb3N0IHR2LXNob3dzIHByb2R1Y2VkIHdhcyAyMDE5Lg0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgTm93IGl0J3MgdGltZSB0byB2aXN1YWxpemUgdGhlIGRhdGEgb24gYSBsaW5lIGNoYXJ0Lg0KDQpgYGB7cn0NCmdncGxvdChudW1iZXJfbW92aWVzX3R2c2hvd3MsIGFlcyhyZWxlYXNlX3llYXIsIGNvdW50KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG91ciA9IHR5cGUpLCBzaXplID0gMS41LCBsaW5ldHlwZSA9ICJkb3R0ZWQiKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIuNSkgKw0KICBnZ3RpdGxlKCJOdW1iZXIgb2YgTW92aWVzIGFuZCBUVi1zaG93cyBpbiBlYWNoIHllYXIiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj1jKDAuMiwgMC44KSwNCiAgICAgICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgxLCAnY20nKSwNCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTQpLA0KICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwNCiAgICAgICAgbGVnZW5kLmtleT1lbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gImxpZ2h0Z3JleSIpLA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDEpKSArDQogIGxhYnMoY29sb3I9IlR5cGUgb2YgdGhlIENvbnRlbnQiKSArDQogIHNjYWxlX3hfY29udGludW91cyhuYW1lPSJZZWFyIiwgYnJlYWtzPXNlcSgyMDAwLCAyMDE5KSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobmFtZT0iTnVtYmVyIG9mIE1vdmllcyBhbmQgVFYtU2hvd3MiLCBicmVha3M9c2VxKDAsIDcwMCwgNTApKQ0KYGBgDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBUaGUgbGluZSBjaGFydCBzaG93cyB1cyB0aGVyZSB3YXMgb25seSBvbmUgeWVhciwgMjAxOSwgdGhhdCBhbW91bnQgb2YgdGhlIHR2LXNob3dzIHN1cnBhc3NlZCB0aGUgbnVtYmVyIG9mIG1vdmllcy4gQnV0IHRoZSBkZWNyZWFzZSBpbiB0aGUgbnVtYmVyIG9mIG1vdmllcyBzdGFydGVkIGluIDIwMTggc28gd2UgY2FuIGV4cGVjdCB0byBzZWUgbW9yZSB0di1zaG93cyB0aGFuIG1vdmllcywgaW4gdGhlIGZ1dHVyZS4NCg0KIyMgICoqMy0pIFN0YXRpc3RpY2FsIFN1bW1hcnkgb2YgdGhlIFNlYXNvbiBMZW5ndGggb2YgVFYtc2hvd3MqKg0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgTGFzdGx5LCB3ZSB3aWxsIHByb2Nlc3MgdGhlIGRhdGEgc28gd2UgZ2V0IGEgc3RhdGlzdGljYWwgc3VtbWFyeSBhYm91dCB0aGUgc2Vhc29uIGxlbmd0aCBvZiBUVi1zaG93cy4gRXZlbiB0aG91Z2ggd2UgY291bGQgZmluZCBhIHdheSB0byBpbXBsZW1lbnQgW3N1bW1hcnkgZnVuY3Rpb25dKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlL3ZlcnNpb25zLzMuNi4yL3RvcGljcy9zdW1tYXJ5KSB3aGljaCBnaXZlcyB1cyBhbGwgdGhlIHZhbHVlcyB3ZSBuZWVkLCBtaW5pbXVtLCAxc3QgcXVhcnRpbGUsIG1lZGlhbiwgbWVhbiwgM3JkIHF1YXJ0aWxlIGFuZCBtYXggdmFsdWVzLCB3ZSB3aWxsIGZpbmQgdGhlbSBzZXBlcmF0ZWx5IGJ5IHVzaW5nIHRoZWlyIG93biBmdW5jdGlvbnMgaW4gb3JkZXIgdG8gZGVtb25zdHJhdGUgaG93IGRpdmVyc2UgYW5kIHByYWN0aWNhbCBSIGlzLg0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgV2VsbCwgd2UgYWxsIGNhbiBndWVzcyB3aGF0IGlzIHRoZSBtaW5pbXVtIG51bWJlciBvZiBzZWFzb25zIGZvciBhIFRWIHNob3csIDEgc2Vhc29uLiBCdXQgd2hhdCBhYm91dCB0aGUgbG9uZ2VzdCB0diBzaG93IGluIG91ciBkYXRhLiBMZXQncyBmaW5kIG91dC4NCg0KYGBge3J9DQpkZiRkdXJhdGlvbiA8LSBsYXBwbHkoZGYkZHVyYXRpb24sIGZ1bmN0aW9uKGIpIGFzLm51bWVyaWMoZ3N1YigiKFswLTldKykuKiQiLCAiXFwxIiwgYikpKQ0KbWF4aW11bV9zZWFzb24gPC0gZGYgJT4lDQogIGZpbHRlcih0eXBlID09ICJUViBTaG93IikgJT4lDQogIHNsaWNlKHdoaWNoLm1heChhcy5udW1lcmljKGR1cmF0aW9uKSkpICU+JQ0KICBzdW1tYXJpc2UodGl0bGUsIGxvbmdlc3Rfc2Vhc29uID0gYXMubnVtZXJpYyhkdXJhdGlvbikpICU+JQ0KICB1bmdyb3VwKCkNCm1heGltdW1fc2Vhc29uDQpgYGANCg0KPGJyPg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IFdlIGluZmVycmVkIHRoYXQgIkdyZXkncyBBbmF0b215IiB3YXMgdGhlIGxvbmdlc3QgdHYgc2hvdyBpbiBvdXIgZGF0YSB3aXRoIDE1IHNlYXNvbnMuDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBOb3cgbGV0J3MgZmluZCB0aGUgbWVkaWFuIHNlYXNvbiBsZW5ndGggYW5kIGl0cyBjb3JyZXNwb25kaW5nIHRpdGxlLg0KDQpgYGB7cn0NCm1lZGlhbl9zZWFzb24gPC0gZGYgJT4lDQogIGZpbHRlcih0eXBlID09ICJUViBTaG93IikgJT4lDQogIHNsaWNlKG1lZGlhbihhcy5udW1lcmljKGR1cmF0aW9uKSkpICU+JQ0KICBzdW1tYXJpc2UodGl0bGUsIG1lZGlhbl9zZWFzb24gPSBhcy5udW1lcmljKGR1cmF0aW9uKSkgJT4lDQogIHVuZ3JvdXAoKQ0KbWVkaWFuX3NlYXNvbg0KYGBgDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBTaW5jZSB0aGVyZSBpcyBhIGxvdCBvZiB0diBzaG93cyB3aXRoIDEgc2Vhc29uIG91ciBtZWRpYW4gaXMgMSBzZWFzb24gd2l0aCB0aGUgIlRyYW5zZm9ybWVycyBQcmltZSIgdHYgc2hvdy4gU28gZmFyIHdpdGggdGhlIGluZm9ybWF0aW9uLCB3ZSBoYXZlLCBtaW5pbXVtLCBtYXhpbXVtLCBhbmQgbWVkaWFuLCB3ZSBjYW4gYXNzdW1lIHRoYXQgdGhpcyBpcyBhIHJpZ2h0LXNrZXdlZCBkaXN0cmlidXRpb24uDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBMZXQncyBub3QganVzdCBhc3N1bWUgYW5kIHZpc3VhbGl6ZSB0aGUgZGF0YSB3ZSBhbHJlYWR5IGhhdmUuDQoNCmBgYHtyfQ0Kc2Vhc29uX251bWJlciA8LSBkZiAlPiUNCiAgZmlsdGVyKHR5cGUgPT0gIlRWIFNob3ciKQ0KZ2dwbG90KHNlYXNvbl9udW1iZXIsIGFlcyh4PWFzLm51bWVyaWMoZHVyYXRpb24pKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsPSIjRTMzNjNmIiwgYmlud2lkdGg9MSkgKw0KICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PW1lYW4oYXMubnVtZXJpYyhkdXJhdGlvbikpKSwgY29sb3I9IiMzNjZkZTMiLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0wLjkpICsNCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSAyLjUsIHkgPSAxMjAwLA0KICAgIGxhYmVsID0gIlRoZVxuYXZlcmFnZVxubnVtYmVyXG5vZlxuc2Vhc29ucyIsDQogICAgdmp1c3QgPSAxLCBzaXplID0gMywgY29sb3IgPSAiIzM2NmRlMyINCiAgKSArDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9bWVkaWFuKGFzLm51bWVyaWMoZHVyYXRpb24pKSksIGNvbG9yPSJncmV5NDAiLCBsaW5ldHlwZT0iZGFzaGVkIiwgc2l6ZT0wLjkpICsNCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSAuNSwgeSA9IDEyMDAsDQogICAgbGFiZWwgPSAiVGhlXG5tZWRpYW5cbm51bWJlclxub2ZcbnNlYXNvbnMiLA0KICAgIHZqdXN0ID0gMSwgc2l6ZSA9IDMsIGNvbG9yID0gImdyZXk0MCINCiAgKSArDQogIHhsYWIoIk51bWJlciBvZiBTZWFzb24iKSArDQogIHlsYWIoIkNvdW50IG9mIE51bWJlciBvZiBTZWFzb24iKSArDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBTZWFzb24gTGVuZ3RoIikgKw0KICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpDQoNCmBgYA0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgQXMgZXhwZWN0ZWQsIHRoaXMgaXMgYSByaWdodC1za2V3ZWQgZGlzdHJpYnV0aW9uLiBGb3IgYmV0dGVyIHVuZGVyc3RhbmRpbmcsIEkgYWxzbyBpbmNsdWRlZCB0aGUgb3RoZXIgc3RhdGlzdGljcyB3ZSBoYXZlIG9idGFpbmVkLg0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgSW4gdGhpcyBwYXJ0IHdlIHdpbGwgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIHNlYXNvbiBsZW5ndGggb2YgZWFjaCBnZW5yZSBvZiB0diBzaG93cy4NCg0KYGBge3J9DQpzZWFzb25fbGVuZ3RoX3R2c2hvd3MgPC0gZGYgJT4lDQogIGZpbHRlcih0eXBlID09ICJUViBTaG93IikgJT4lDQogIG11dGF0ZShnZW5yZSA9IGdzdWIoIl4oLio/KSwuKiIsICJcXDEiLCBsaXN0ZWRfaW4pKSAlPiUNCiAgZ3JvdXBfYnkoZ2VucmUpICU+JQ0KICBzdW1tYXJpc2UoYXZlcmFnZV9zZWFzb25fbGVuZ3RoID0gcm91bmQobWVhbihhcy5pbnRlZ2VyKGR1cmF0aW9uKSwgbmEucm0gPSBUUlVFKSwgMSkpICU+JQ0KICB1bmdyb3VwKCkNCnNlYXNvbl9sZW5ndGhfdHZzaG93cw0KYGBgDQoNCjxicj4NCiZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyZuYnNwOyBGb3IgdGhpcyBwYXJ0IHdlIHdpbGwgdmlzdWFsaXplIHRoZSBkYXRhIG9uIGhvcml6b250YWwgYmFyIGNoYXJ0Lg0KDQpgYGB7cn0NCmdncGxvdChzZWFzb25fbGVuZ3RoX3R2c2hvd3MsIGFlcyh4PXJlb3JkZXIoZ2VucmUsIGF2ZXJhZ2Vfc2Vhc29uX2xlbmd0aCksIHk9YXZlcmFnZV9zZWFzb25fbGVuZ3RoKSkgKw0KICBnZW9tX2Jhcihjb2xvdXI9ImJsYWNrIiwgc2l6ZT0wLjMsIGZpbGw9IiNFMzM2M2YiLCBzdGF0PSJpZGVudGl0eSIpICsNCiAgeWxpbSgwLCA2KSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZShmb3JtYXQoYXZlcmFnZV9zZWFzb25fbGVuZ3RoLCBuc21hbGw9MCksICIgU2Vhc29uIikpLA0KICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdD0wLjUpLA0KICAgICAgICAgICAgY29sb3VyID0gImJsYWNrIiwgc2l6ZSA9IDMpICsNCiAgeGxhYigiTmV0ZmxpeCBUViBTaG93IEdlbnJlcyIpICsgeWxhYigiQXZlcmFnZSBMZW5ndGggKHNlYXNvbikiKSArDQogIGdndGl0bGUoIkF2ZXJhZ2UgU2Vhc29uIExlbmd0aCBpbiBlYWNoIEdlbnJlIG9mIHRoZSBUViBTaG93cyIpDQpgYGANCg0KPGJyPg0KJm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7Jm5ic3A7IEl0IGlzIGNsZWFyIHRoYXQgQ2xhc3NpY2FsICYgQ3VsdCBUViBTaG93cyBoYXZlIHRoZSBoaWdoZXN0IGF2ZXJhZ2Ugc2Vhc29uIGxlbmd0aCB3aXRoIDUuOCBTZWFzb24sIGZvbGxvd2VkIGJ5IFJvbWFudGljIFRWIFNob3dzLg0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgV2VsbCwgYXMgd2UgaGVhZCB0byBlbmQgdGhpcyByZXBvcnQsIHdlIHdpbGwgaW5jbHVkZSB0aGUgd2F5IHRvIGxvY2F0ZSAxc3QgYW5kIDNyZCBxdWFydGlsZXMsIGp1c3QgdG8gZGVtb25zdHJhdGUgdGhlIHByb3BlciBmdW5jdGlvbnMsIG5vdCBsaWtlIHRoZXkgcHJvdmlkZSB1cyBhbnkgaW5zaWdodHMuDQoNCmBgYHtyfQ0KZHVyYXRpb25fcXVhcnRpbGVzIDwtIGRmICU+JQ0KICBmaWx0ZXIodHlwZSA9PSAiVFYgU2hvdyIpICU+JQ0KICBncm91cF9ieSh0eXBlKSAlPiUNCiAgc3VtbWFyaXNlKFExID0gcXVhbnRpbGUoYXMubnVtZXJpYyhkdXJhdGlvbiksIDAuMjUpLA0KICAgICAgICAgICAgUTMgPSBxdWFudGlsZShhcy5udW1lcmljKGR1cmF0aW9uKSwgMC43NSkpICU+JQ0KICB1bmdyb3VwKCkNCmR1cmF0aW9uX3F1YXJ0aWxlcw0KYGBgDQoNCjxicj4NCg0KIyAqKlN1bW1hcnkqKg0KDQo8YnI+DQombmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsgVG9kYXkgd2UgaGF2ZSBkZWFsdCB3aXRoIE5ldGZsaXggRGF0YS4gTm93YWRheXMgcGVvcGxlIHRlbmQgdG8gZ2V0IGxvc3QgaW4gZGFpbHkgcHJvYmxlbXMsIGNhcmVlciBnb2FscywgYW5kIHNvY2lhbCBtZWRpYSwgYnV0IHNvbWV0aW1lcyBpdCBpcyBuZWNlc3NhcnkgdG8ganVzdCByZXRyZWF0IGZyb20gYWxsIGFuZCBjaGlsbC4gV2VsbCwgdGhhdCBicmluZ3MgdXMgdG8gTmV0ZmxpeC4gSW4gY29uY2x1c2lvbiB0byBvdXIgYW5hbHlzZXMsIHdlIGNhbiBzYXkgbW92aWUgcHJvZHVjdGlvbiBzdGF0aXN0aWNhbGx5IHRlbmRzIHRvIGRpbWluaXNoIHdoZXJlYXMgdHYgc2hvd3MgYXJlIHJpc2luZywgZXNwZWNpYWxseSB3aXRoIHRoZSBoZWxwIG9mIHN0cmVhbWluZyBwbGF0Zm9ybXMuIFRoZXJlIGlzIGEgaGlnaCBjaGFuY2UgdGhhdCB3ZSB3aWxsIHNlZSBtb3JlIGFuZCBtb3JlIHR2IHNob3dzLiBXZSBhbHNvIGNvbmNsdWRlZCB0aGF0IHdhdGNoaW5nIEdyZXkncyBBbmF0b215IGZyb20gdGhlIGJlZ2lubmluZyB3b3VsZCB0YWtlIHNvIG11Y2ggdGltZSBpbiB0aGUgbm93YWRheXMgY29tcGV0aXRpdmUgd29ybGQuIEJ1dCB0aGFua2Z1bGx5IHNlZWluZyBmcm9tIG91ciBoaXN0b2dyYW0gdHYgc2hvd3MgdGVuZCB0byBiZSBhcyBzaG9ydCBhcyAxIHNlYXNvbiwgd2hpY2ggaXMgdGhlIHBlcmZlY3QgYW1vdW50IHRvIGdldCB0aGUgZ2lzdCBvZiB0aGUgc3RvcnkgYW5kIGZpbmlzaCBpdCBpbiBvbmUgZ28uIEV2ZW4gdGhvdWdoIHR2IHNob3dzIGV2b2x2ZSB0byBiZSBzaG9ydGVyIHdlIGFsc28gc2VlIHRoZSBudW1iZXIgb2Ygc2Vhc29ucyBvZiBSb21hbnRpYyBUViBTaG93cyBpcyBzdGlsbCBoaWdoIHdpdGggMy42IHNlYXNvbnMsIGF2ZXJhZ2VseS4gQnV0IGJlaW5nIGEgZG9jdW1lbnRhcnkgYW5kIGRvY3VzZXJpZXMgbG92ZXIgc2F2ZXMgbWUgc28gbXVjaCB0aW1lIHRvIHdvcmsgbW9yZSBvbiBkYXRhIGFuYWx5dGljcywgYXMgc2VlbiBpbiBvdXIgb3V0cHV0cy4NCg0KPGJyPg0KPGJyPg0KPGJyPg0KX0Rpc2NsYWltZXI6IEZpcnN0IHBhcnQgb2YgdGhpcyByZXBvcnQgaXMgb3Zlcmx5IGluc3BpcmVkIGJ5IFlpZ2l0IEVyb2wncyBbTWVkaXVtIHBvc3RdKGh0dHBzOi8vbWVkaXVtLmNvbS9kZWVwLWxlYXJuaW5nLXR1cmtpeWUvZXhwbG9yYXRpb24tb2YtbmV0ZmxpeC0yMDIwLWRhdGFzZXQtaW4tci1tYXJrZG93bi1lZGEtYjIwMmJiYWVjNGEpIGFuZCBbR2l0aHViIHJlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS95Z3RlcmwvRURBLU5ldGZsaXgtMjAyMC1pbi1SLykuIEFsbCB0aGUgZXhjZXJwdHMgYW5kIGFuYWx5c2VzIGFyZSBkb25lIGZvciBlZHVjYXRpb25hbCBwdXJwb3Nlcy5fDQoNCiMjIyAmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDsmbmJzcDtfX19UaGFua3MgZm9yIHJlYWRpbmcuLi5fX18NCjxicj4NCjxicj4NCiFbUl0oaHR0cHM6Ly91cGxvYWQud2lraW1lZGlhLm9yZy93aWtpcGVkaWEvY29tbW9ucy90aHVtYi8xLzFiL1JfbG9nby5zdmcvMTIwMHB4LVJfbG9nby5zdmcucG5nKVw=