Object:

By the end of 2019, the mobile gaming sector reaches $68.5 billion, covering 45% of the global game market. To exploit tremendous opportunities from this market, game developers need to have an overall view of the market.
The main object of this project is to conduct an Exploratory Data Analysis to expose the factors that might affect a game revenue, which can determine by the two main metrics: the average user rating and the rating count.

Data Cleaning Process:

Games without Average User Rating values will be dropped
Genre tag “Entertainment” and “Games” are ignored from string as it does not provide meaningful insight
Games less than 30 User Rating will be dropped to prevent biased ratting from developers
The remaining string are extracted and grouped in genres by their characteristics:
* Puzzle = Puzzle/Board
* Adventure = Adventure/Role/Role Playing
* Action = Action
* Family = Family/Education/Trivia
* Simulation = Similation
* Other = Casual/Card

#Cleaning: Drops games without Average User Rating:
app_game.df <- app_game.df %>% 
  filter(`Primary Genre` == "Games" | `Primary Genre` == "Entertainment") %>%
  filter(!is.na(`Average User Rating`))
#Extract Genres variables into separate columns:
app_game.df <- app_game.df %>% 
  extract(Genres,into = "Puzzle",regex = "(Puzzle|Board)",remove = F) %>%
  extract(Genres,into = "Adventure",regex = "(Adventure|Role|Role Playing)",remove = F) %>%
  extract(Genres,into = "Action",regex = "(Action)",remove = F) %>%
  extract(Genres,into = "Family",regex = "(Family|Education|Trivia)",remove = F)%>%
  extract(Genres,into = "Simulation",regex = "(Simulation)",remove = F)%>%
  extract(Genres,into = "Others",regex = "(Casual|Card)")
#Drop Games with less than 30 User Rating and group them into 6 main genres:
app_game.df <- app_game.df %>% 
  gather("Puzzle","Adventure","Action","Family","Simulation","Others",key="Genres",value= "cases",na.rm = T)%>%
  filter(`User Rating Count`> 30)
app_game.df$cases <- NULL
app_game.df$`Icon URL` <- NULL
app_game.df$Subtitle <- NULL
app_game.df$Description <- NULL
app_game.df$URL <- NULL
app_game.df$Languages <- NULL
#Drop game with 60$ in price:
app_game.df <- filter(app_game.df, Price < 30)
#Convert Size Variable from Byte to Megabyte:
app_game.df$Size <- app_game.df$Size/1000000

1. The Landscape of Games on Appstore:

1.1 Overall Rating, Price, and Genres:

#Price, Genres and Avarage Rating
app_game.df$Genres <- factor(app_game.df$Genres, 
                             level = c("Action", "Adventure", "Simulation", "Puzzle", "Family", "Others"))
app_game.df$Games <- ifelse(app_game.df$Price == 0, "Free","Paid")
genres <- app_game.df %>% group_by(Genres, Games) %>% 
  dplyr:: summarise(count = n(), rating = mean(`Average User Rating`))
percent.price <- genres %>% group_by(Genres) %>% 
  dplyr::mutate(free = round(count/sum(count),2),
                total = sum(count)/2,
                rating = round(sum(rating*count/total)/4,2))

ggplot(percent.price) + geom_col(aes(x = Genres, y = total)) + 
  geom_col(aes(x = Genres, y = count, fill=Games)) + 
  geom_text(aes(x = Genres, y = count/2, label = paste(free*100,"%")), color = "white") +
  geom_point(aes(x = Genres, y = rating*600),color = "grey38") +
  geom_line(aes(x = Genres, y = rating*600, group = 1), color = "grey38") + 
  geom_text(aes(x = Genres, y = rating*600 + 50, label = rating*2)) + 
  scale_y_continuous(sec.axis = sec_axis(~./300, name = "Average Rating")) +
  ylab("Number of Games") + scale_fill_manual(values = c("steelblue","grey38")) +
  ggtitle("Genres, Number of Games, and Average User Rating")

Puzzle seems to be the most popular genre with more than 1000 games on Appstore, following by Simulation and Adventure games. The least popular game genre is Family game with only around 300 games. Despite being the most popular game genre, Puzzle has the lowest average user rating with just only 4.06/5. The highest average rating belongs to the three Action, Adventure and Family games with 4.22/5.

#Paid vs Free games:
paid <- app_game.df %>% group_by(Games) %>% dplyr::summarise(Count = n()) %>%
  mutate(Percent = paste(round(Count/sum(Count),2)*100,"%"))
price <- app_game.df %>% dplyr::mutate(Price = ifelse(Price == 0, NA, Price)) %>% filter(!is.na(Price))
price$Price <- ifelse(price$Price < 5, "Under $5", 
                      ifelse(price$Price < 10, "$5-$10",
                             ifelse(price$Price < 15, "$10-$15","Over $15")))
price$Price <- factor(price$Price, levels = c("Under $5","$5-$10","$10-$15","Over $15"))
price <- price %>% group_by(Price) %>% dplyr::summarise(Count = n()) %>% 
  mutate(Percent = paste(round(Count/sum(Count),2)*100,"%"))
app_game.df$In_app <- ifelse(is.na(app_game.df$`In-app Purchases`), "No","Yes")
in_app <- app_game.df %>% group_by(Games, In_app) %>% dplyr::summarise(Count = n()) %>% group_by(Games) %>%
  dplyr::mutate(Percent = paste(round(Count/sum(Count),2)*100,"%"))
in_app <- in_app[,-3]
#table
grid.arrange(
  tableGrob(paid),
  tableGrob(price),
  tableGrob(in_app),
  nrow = 1)

Price Analysis:
81% of the games on the market are free to download and play. In the paid game sector, 86% of the games just cost under $5 for the user to download and play. This percent drops significantly as the price goes up. Free game developers might gain revenue based on advertising and in-app purchases. Accordingly, 77% of the free games have in app purchases service and the rest of them might earn revenue from in-app advertising, compared with 38% and 62% of “yes” and “no” in-app purchases in the paid game sector.

1.2 Size Trend and Genres Trend:

#Overall
ggplot(data = app_game.df, aes(x=Size)) + 
  geom_histogram(binwidth = 50, fill = "steelblue") + ggtitle("Game Size Distribution")
# Game size by Genres
ggplot(data = app_game.df, aes(x=Size)) +
  geom_histogram(fill = "steelblue", binwidth = 100) + facet_wrap(~Genres) + 
  coord_cartesian(xlim = c(0,1000)) + ggtitle("Game Size by Genres")

The game sizes are mostly under one gigabyte. The Action, Adventure, and Simulation game have higher average size than other genres, assuming the higher demand for graphical and strategical features of the player. The other three genres have similar distribution shape with size highly concentrate in the under 300mb zone.

app_game.df$Year <- format(app_game.df$`Original Release Date`,"%Y")
app_game.df %>% group_by(Year, Genres) %>% dplyr::summarise(mean = mean(Size)) %>% 
  ggplot(aes(x = Year, y = mean, group = Genres, color = Genres)) + geom_line() + geom_point() +
  ylab("Average Size") + ggtitle("Average Size Change over Time by Genres")

Game sizes increase substantially from 2008 to 2019 (about 12 times on average). Action, Adventure, and Simulation games have the highest average size with around 435mb, 430mb, and 260mb respectively, whereas Family games have a modest average size with around 200mb.

year.rating <- app_game.df %>% 
  group_by(Year,Genres) %>% 
  dplyr::summarise(mean.size = mean(`Average User Rating`))
names(year.rating) <- c("Year","Genres","Rating")
ggplot(year.rating, aes(x = Year, y = Rating, fill= Genres)) + geom_line(aes(color = Genres),size =1,group = 1) +
  facet_wrap(.~Genres, ncol = 3) + ylab("Average Rating")+ ggtitle("Rating Change over Time by Genres") +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

The average user ratings of all genres increase over time. Simulation games have the highest average user rating in 2019 (almost nearly 4.5). Following Simulation games, Adventure and Puzzle games seem to have a stable increase since 2011 with around 4.3 in average rating. On the other hand, Action seems to not have any improvement in terms of customer satisfaction since 2012.

# Genres Trend
year.rating <- app_game.df %>% 
  group_by(floor_date(`Original Release Date`,"%Y"), Genres) %>%
  dplyr::summarise(Count = n())
names(year.rating) <- c("Year","Genres","Count")
year.rating <- year.rating[-c(66:71),]
ggplot(year.rating, aes(x = Year, y = Count, color= Genres)) + geom_line(size = 1) +
  facet_wrap(.~Genres, ncol = 3) + ylab("Number of Games") + ggtitle("Genres Trend")+
  theme(axis.text.x = element_text(angle = 40, hjust = 1))

Because this dataset was collect in August 2019, the number of games of each genre for 2019 is incomplete. Therefore, this genre trend analysis will focus on the 2008-2018 period. The Y-axis indicates the number of games introduced on Appstore in a specific year.
From the graph, we can see that this is the age of simulation game. Being one of the genres that have the lowest number of games introduced on Apple Appstore in 2008, Simulation becomes the dominant genre with around 120 games introduced in 2018. Action and Adventure games have a similar trend with a substantial increase from 2008 to 2016 and a slight decrease from then. Puzzle genres, being dominant from 2008 to 2015, stepped down its throne and had consistently decreased since 2015.

2. Factor Affect User Rating and Rating Count:

2.1 Average User Rating:

This analysis divides the size of games into three main categories:
1. Under 100mb
2. Between 100mb and 300mb
3. Over 300mb
The size categories can give the developers some ideas about the segments. The “under 100mb” games are the ones aiming to mass market, which just serve the basic needs of entertainment with a modest number of features. The advantage of this category is the ability to reach a large number of users with minimum hardware requirements. The “100mb-300mb” category will focus more on users who are more interested in games than the mass market and require higher technical and graphical gameplays. The last category focus on “game geeks” who demand the extensive graphics aspect and the complexity of the gameplays.

#Subgroup 1: under 100mb
app_game.df %>% filter(Sizegroup == "Under 100Mb") %>%
  ggplot(aes(x=rating, y=Size)) + 
  geom_boxplot(fill = "steelblue", outlier.color = "firebrick2") +
  #facet_wrap(.~Genres, nrow = 2) +
  xlab("Average User Rating") + ylab("Size") +
  ggtitle("User Rating vs Size (<100mb)")

Although more data are required to generate a more accurate statement about the relationship between the size of games and user ratings, we can have a general idea about this relationship. The advantage of this category is its modest requirement and easy to play. But where is the line between simplicity and under the requirements?
From the boxplot graph, games with 4-point and 5-point for rating have higher average size than those with a lower rate. Too small in size might be insufficient for the minimum requirements of the users. In this categorie, games should be 50mb or higher to cover the basic needs of users.

#Subgroup 2: 100mb - 300mb 
app_game.df %>% filter(Sizegroup == "100Mb - 300Mb") %>%
  ggplot(aes(x=rating, y=Size)) + 
  geom_boxplot(fill = "steelblue", outlier.color = "firebrick2") + 
  xlab("Average User Rating") +
  ylab("Size")+
  ggtitle("User Rating vs Size (100Mb - 300Mb)")

With the data in hand, we can not generate a clear statement about the relationship between size and rating for 100mb - 300mb category. The games with 1 rating point seem to be bigger in size. However, the number of games with 1 rating point is very low comparing to other rating categories, so concluding games with a bigger size of this category get a lower rate would be a wrong conclusion. More data need to be collected and analyzed to have a clear and accurate statement.

#Subgroup 3: over 300mb
app_game.df %>% filter(Sizegroup == "Over 300Mb") %>%
  ggplot(aes(x=rating, y=Size)) + 
  geom_boxplot(fill = "steelblue", outlier.color = "firebrick2") +
  scale_y_continuous(limits=c(0,1000)) + 
  xlab("Average User Rating") + ylab("Size")+
  ggtitle("User Rating vs Size (Over 300Mb)")

Overall, games with 4-point and 5-point seem to have bigger sizes. However, there is a big overlap of the size between the rating of a good game (4 and 5 rating point) and a bad game (below 4 rating point), so we can not generate any conclusion about the effect of size on this size category. More information about features is needed to get a clear view of the effect of size on 100-300mb and over 300mb size categories.

#Create update variables:
app_game.df$update <- as.numeric(Sys.Date() - app_game.df$`Current Version Release Date`) 

ggplot(app_game.df,aes(x=rating, y = update)) + 
  geom_boxplot(fill = "steelblue", outlier.color = "firebrick2") +
  xlab("Average User Rating") + ylab("Number of Days Since Last Update") +
  ggtitle("Days Since Last Update and User Rating")

The number of days since the last update and the average user rating have a negative relationship. The shorter the period among updates demonstrate the ability of a developer to adapt and modify its games to satisfy the users and market trends. The boxplot shows that “days since last update” of games with 4-point and 5-point are significantly lower (around 750 and 400 days as median respectively) than that of games with 3-point and below.

2.2 User Rating Count:

Besides Average User Rating, User Rating Count can be used to determine the number of customers who downloaded the games and actually experienced it (actual users). The following analysis will focus on two main questions:
1. Which type of game and size group is performing better in terms of User Rating Count?
2. Which genres are good for developers to focus on?

#bubble chart: Free/Paid, User rating, size = user rating count
ggplot(app_game.df) + 
  geom_jitter(aes(x = Games, y = `User Rating Count`/1000, color = Games), alpha =0.3) +
  scale_y_continuous(limits=c(0,500)) + 
  ylab("Rating Count in Thousand") + facet_grid(.~Sizegroup) + ggtitle("User Rating Count and Game types")

On average, Free games have a higher user rating count than paid games. Among size categories, the “100-300mb” category seems to be the best category for free games with the highest average user rating count than any other category. For paid games, “under 100mb” seem to be the best size categories for developers to focus on.

ggplot(app_game.df) + 
  geom_jitter(aes(x = Genres, y = `User Rating Count`/1000, color = Genres), alpha =0.4) +
  scale_y_continuous(limits=c(0,500)) + 
  ylab("Rating Count in Thousand") + ggtitle("User Rating Count and Genres")

In terms of user rating count, Action, Adventure, and Simulation are potential genres for developers to focus on with higher average user rating count than others. Family games seem to be the least potential genre with the lowest number of rating count among the six genres.

3. Key Takeaways and Recommendations:

3.1 Key Takeaways:

  1. Puzzle games are dominating the Appstore game market with the highest number of games (more than 1000 games).
  2. On average from 2008-2019, Action, Adventure, and Family genres have the highest average user rating with 4.22 point.
  3. Simulation is the rising star with a consistent increase in both the average user rating point and the number of game introduced per year in the last decade.
  4. Overall, the sizes of games on the market increase dramatically since 2008 (12 times on average).
  5. For the type of games, Free game seems to dominate the market with 81% coverage.

3.2 Recommendation:

  1. Action, Adventure, and Simulation are good genres for developers to focus on in terms of user rating count and customer trend, especially Simulation with the highest increase in bot average user rating and the number of new games introduced.
  2. For the type of game, Free game seems to be the better category to focus on with a significantly higher average user rating count. The revenue for free games can be generated by advertising and in-app purchases.
  3. For the size category, in term of user rating count, “100-300mb” seem to be the best category for free games and “under-100mb” category is good for developers who want to focus on paid games.
LS0tCnRpdGxlOiAiTW9iaWxlIFN0cmF0ZWd5IEdhbWUgQXBwbGUiCmF1dGhvcjogIkxvYyBOZ3V5ZW4gLSBUaGUgVW5pdmVyc2l0eSBvZiBUZXhhcyBhdCBEYWxsYXMiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdGhlbWU6IGNlcnVsZWFuCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnNCcKICAgIHRvY19mbG9hdDogeWVzCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzQnCi0tLQoKIyMgT2JqZWN0OgoKQnkgdGhlIGVuZCBvZiAyMDE5LCB0aGUgbW9iaWxlIGdhbWluZyBzZWN0b3IgcmVhY2hlcyBcJDY4LjUgYmlsbGlvbiwgY292ZXJpbmcgNDUlIG9mIHRoZSBnbG9iYWwgZ2FtZSBtYXJrZXQuIFRvIGV4cGxvaXQgdHJlbWVuZG91cyBvcHBvcnR1bml0aWVzIGZyb20gdGhpcyBtYXJrZXQsIGdhbWUgZGV2ZWxvcGVycyBuZWVkIHRvIGhhdmUgYW4gb3ZlcmFsbCB2aWV3IG9mIHRoZSBtYXJrZXQuXApUaGUgbWFpbiBvYmplY3Qgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGNvbmR1Y3QgYW4gRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyB0byBleHBvc2UgdGhlIGZhY3RvcnMgdGhhdCBtaWdodCBhZmZlY3QgYSBnYW1lIHJldmVudWUsIHdoaWNoIGNhbiBkZXRlcm1pbmUgYnkgdGhlIHR3byBtYWluIG1ldHJpY3M6IHRoZSBhdmVyYWdlIHVzZXIgcmF0aW5nIGFuZCB0aGUgcmF0aW5nIGNvdW50LgoKYGBge3IgbG9hZFBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30KaWYoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQpwYWNtYW46OnBfbG9hZCh0aWR5dmVyc2UsIHBseXIsIGZhc3REdW1taWVzLCByZXNoYXBlMiwgUkNvbG9yQnJld2VyLGx1YnJpZGF0ZSwgZ3JpZCwgZ3JpZEV4dHJhLCBjYXJldCxNQVNTKQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYygpKQpgYGAKCmBgYHtyIHJlYWREYXRhLCBpbmNsdWRlPUZBTFNFfQojIyBSZWFkIEFwcHN0b3JlX2dhbWVzIGRhdGEKYXBwX2dhbWUuZGYgPC0gcmVhZF9jc3YoZmlsZS5jaG9vc2UoKSxjb2xfdHlwZXMgPSBjb2xzKCJVc2VyIFJhdGluZyBDb3VudCIgPSBjb2xfaW50ZWdlcigpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiT3JpZ2luYWwgUmVsZWFzZSBEYXRlIiA9IGNvbF9kYXRlKCIlZC8lbS8lWSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDdXJyZW50IFZlcnNpb24gUmVsZWFzZSBEYXRlIiA9IGNvbF9kYXRlKCIlZC8lbS8lWSIpKSkKYGBgCgojIyMgRGF0YSBDbGVhbmluZyBQcm9jZXNzOgoKR2FtZXMgd2l0aG91dCBBdmVyYWdlIFVzZXIgUmF0aW5nIHZhbHVlcyB3aWxsIGJlIGRyb3BwZWRcCkdlbnJlIHRhZyAiRW50ZXJ0YWlubWVudCIgYW5kICJHYW1lcyIgYXJlIGlnbm9yZWQgZnJvbSBzdHJpbmcgYXMgaXQgZG9lcyBub3QgcHJvdmlkZSBtZWFuaW5nZnVsIGluc2lnaHRcCkdhbWVzIGxlc3MgdGhhbiAzMCBVc2VyIFJhdGluZyB3aWxsIGJlIGRyb3BwZWQgdG8gcHJldmVudCBiaWFzZWQgcmF0dGluZyBmcm9tIGRldmVsb3BlcnNcClRoZSByZW1haW5pbmcgc3RyaW5nIGFyZSBleHRyYWN0ZWQgYW5kIGdyb3VwZWQgaW4gZ2VucmVzIGJ5IHRoZWlyIGNoYXJhY3RlcmlzdGljczpcClwqIFB1enpsZSA9IFB1enpsZS9Cb2FyZFwKXCogQWR2ZW50dXJlID0gQWR2ZW50dXJlL1JvbGUvUm9sZSBQbGF5aW5nXApcKiBBY3Rpb24gPSBBY3Rpb25cClwqIEZhbWlseSA9IEZhbWlseS9FZHVjYXRpb24vVHJpdmlhXApcKiBTaW11bGF0aW9uID0gU2ltaWxhdGlvblwKXCogT3RoZXIgPSBDYXN1YWwvQ2FyZAoKYGBge3IgVGlkeSBEYXRhfQojQ2xlYW5pbmc6IERyb3BzIGdhbWVzIHdpdGhvdXQgQXZlcmFnZSBVc2VyIFJhdGluZzoKYXBwX2dhbWUuZGYgPC0gYXBwX2dhbWUuZGYgJT4lIAogIGZpbHRlcihgUHJpbWFyeSBHZW5yZWAgPT0gIkdhbWVzIiB8IGBQcmltYXJ5IEdlbnJlYCA9PSAiRW50ZXJ0YWlubWVudCIpICU+JQogIGZpbHRlcighaXMubmEoYEF2ZXJhZ2UgVXNlciBSYXRpbmdgKSkKI0V4dHJhY3QgR2VucmVzIHZhcmlhYmxlcyBpbnRvIHNlcGFyYXRlIGNvbHVtbnM6CmFwcF9nYW1lLmRmIDwtIGFwcF9nYW1lLmRmICU+JSAKICBleHRyYWN0KEdlbnJlcyxpbnRvID0gIlB1enpsZSIscmVnZXggPSAiKFB1enpsZXxCb2FyZCkiLHJlbW92ZSA9IEYpICU+JQogIGV4dHJhY3QoR2VucmVzLGludG8gPSAiQWR2ZW50dXJlIixyZWdleCA9ICIoQWR2ZW50dXJlfFJvbGV8Um9sZSBQbGF5aW5nKSIscmVtb3ZlID0gRikgJT4lCiAgZXh0cmFjdChHZW5yZXMsaW50byA9ICJBY3Rpb24iLHJlZ2V4ID0gIihBY3Rpb24pIixyZW1vdmUgPSBGKSAlPiUKICBleHRyYWN0KEdlbnJlcyxpbnRvID0gIkZhbWlseSIscmVnZXggPSAiKEZhbWlseXxFZHVjYXRpb258VHJpdmlhKSIscmVtb3ZlID0gRiklPiUKICBleHRyYWN0KEdlbnJlcyxpbnRvID0gIlNpbXVsYXRpb24iLHJlZ2V4ID0gIihTaW11bGF0aW9uKSIscmVtb3ZlID0gRiklPiUKICBleHRyYWN0KEdlbnJlcyxpbnRvID0gIk90aGVycyIscmVnZXggPSAiKENhc3VhbHxDYXJkKSIpCiNEcm9wIEdhbWVzIHdpdGggbGVzcyB0aGFuIDMwIFVzZXIgUmF0aW5nIGFuZCBncm91cCB0aGVtIGludG8gNiBtYWluIGdlbnJlczoKYXBwX2dhbWUuZGYgPC0gYXBwX2dhbWUuZGYgJT4lIAogIGdhdGhlcigiUHV6emxlIiwiQWR2ZW50dXJlIiwiQWN0aW9uIiwiRmFtaWx5IiwiU2ltdWxhdGlvbiIsIk90aGVycyIsa2V5PSJHZW5yZXMiLHZhbHVlPSAiY2FzZXMiLG5hLnJtID0gVCklPiUKICBmaWx0ZXIoYFVzZXIgUmF0aW5nIENvdW50YD4gMzApCmFwcF9nYW1lLmRmJGNhc2VzIDwtIE5VTEwKYXBwX2dhbWUuZGYkYEljb24gVVJMYCA8LSBOVUxMCmFwcF9nYW1lLmRmJFN1YnRpdGxlIDwtIE5VTEwKYXBwX2dhbWUuZGYkRGVzY3JpcHRpb24gPC0gTlVMTAphcHBfZ2FtZS5kZiRVUkwgPC0gTlVMTAphcHBfZ2FtZS5kZiRMYW5ndWFnZXMgPC0gTlVMTAojRHJvcCBnYW1lIHdpdGggNjAkIGluIHByaWNlOgphcHBfZ2FtZS5kZiA8LSBmaWx0ZXIoYXBwX2dhbWUuZGYsIFByaWNlIDwgMzApCiNDb252ZXJ0IFNpemUgVmFyaWFibGUgZnJvbSBCeXRlIHRvIE1lZ2FieXRlOgphcHBfZ2FtZS5kZiRTaXplIDwtIGFwcF9nYW1lLmRmJFNpemUvMTAwMDAwMApgYGAKCiMjIDEuIFRoZSBMYW5kc2NhcGUgb2YgR2FtZXMgb24gQXBwc3RvcmU6CgojIyMgMS4xIE92ZXJhbGwgUmF0aW5nLCBQcmljZSwgYW5kIEdlbnJlczoKCmBgYHtyIEdlbnJlcyBEaXN0cmlidXRpb259CiNQcmljZSwgR2VucmVzIGFuZCBBdmFyYWdlIFJhdGluZwphcHBfZ2FtZS5kZiRHZW5yZXMgPC0gZmFjdG9yKGFwcF9nYW1lLmRmJEdlbnJlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgPSBjKCJBY3Rpb24iLCAiQWR2ZW50dXJlIiwgIlNpbXVsYXRpb24iLCAiUHV6emxlIiwgIkZhbWlseSIsICJPdGhlcnMiKSkKYXBwX2dhbWUuZGYkR2FtZXMgPC0gaWZlbHNlKGFwcF9nYW1lLmRmJFByaWNlID09IDAsICJGcmVlIiwiUGFpZCIpCmdlbnJlcyA8LSBhcHBfZ2FtZS5kZiAlPiUgZ3JvdXBfYnkoR2VucmVzLCBHYW1lcykgJT4lIAogIGRwbHlyOjogc3VtbWFyaXNlKGNvdW50ID0gbigpLCByYXRpbmcgPSBtZWFuKGBBdmVyYWdlIFVzZXIgUmF0aW5nYCkpCnBlcmNlbnQucHJpY2UgPC0gZ2VucmVzICU+JSBncm91cF9ieShHZW5yZXMpICU+JSAKICBkcGx5cjo6bXV0YXRlKGZyZWUgPSByb3VuZChjb3VudC9zdW0oY291bnQpLDIpLAogICAgICAgICAgICAgICAgdG90YWwgPSBzdW0oY291bnQpLzIsCiAgICAgICAgICAgICAgICByYXRpbmcgPSByb3VuZChzdW0ocmF0aW5nKmNvdW50L3RvdGFsKS80LDIpKQoKZ2dwbG90KHBlcmNlbnQucHJpY2UpICsgZ2VvbV9jb2woYWVzKHggPSBHZW5yZXMsIHkgPSB0b3RhbCkpICsgCiAgZ2VvbV9jb2woYWVzKHggPSBHZW5yZXMsIHkgPSBjb3VudCwgZmlsbD1HYW1lcykpICsgCiAgZ2VvbV90ZXh0KGFlcyh4ID0gR2VucmVzLCB5ID0gY291bnQvMiwgbGFiZWwgPSBwYXN0ZShmcmVlKjEwMCwiJSIpKSwgY29sb3IgPSAid2hpdGUiKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IEdlbnJlcywgeSA9IHJhdGluZyo2MDApLGNvbG9yID0gImdyZXkzOCIpICsKICBnZW9tX2xpbmUoYWVzKHggPSBHZW5yZXMsIHkgPSByYXRpbmcqNjAwLCBncm91cCA9IDEpLCBjb2xvciA9ICJncmV5MzgiKSArIAogIGdlb21fdGV4dChhZXMoeCA9IEdlbnJlcywgeSA9IHJhdGluZyo2MDAgKyA1MCwgbGFiZWwgPSByYXRpbmcqMikpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKHNlYy5heGlzID0gc2VjX2F4aXMofi4vMzAwLCBuYW1lID0gIkF2ZXJhZ2UgUmF0aW5nIikpICsKICB5bGFiKCJOdW1iZXIgb2YgR2FtZXMiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInN0ZWVsYmx1ZSIsImdyZXkzOCIpKSArCiAgZ2d0aXRsZSgiR2VucmVzLCBOdW1iZXIgb2YgR2FtZXMsIGFuZCBBdmVyYWdlIFVzZXIgUmF0aW5nIikKYGBgCgpQdXp6bGUgc2VlbXMgdG8gYmUgdGhlIG1vc3QgcG9wdWxhciBnZW5yZSB3aXRoIG1vcmUgdGhhbiAxMDAwIGdhbWVzIG9uIEFwcHN0b3JlLCBmb2xsb3dpbmcgYnkgU2ltdWxhdGlvbiBhbmQgQWR2ZW50dXJlIGdhbWVzLiBUaGUgbGVhc3QgcG9wdWxhciBnYW1lIGdlbnJlIGlzIEZhbWlseSBnYW1lIHdpdGggb25seSBhcm91bmQgMzAwIGdhbWVzLiBEZXNwaXRlIGJlaW5nIHRoZSBtb3N0IHBvcHVsYXIgZ2FtZSBnZW5yZSwgUHV6emxlIGhhcyB0aGUgbG93ZXN0IGF2ZXJhZ2UgdXNlciByYXRpbmcgd2l0aCBqdXN0IG9ubHkgNC4wNi81LiBUaGUgaGlnaGVzdCBhdmVyYWdlIHJhdGluZyBiZWxvbmdzIHRvIHRoZSB0aHJlZSBBY3Rpb24sIEFkdmVudHVyZSBhbmQgRmFtaWx5IGdhbWVzIHdpdGggNC4yMi81LgoKYGBge3J9CiNQYWlkIHZzIEZyZWUgZ2FtZXM6CnBhaWQgPC0gYXBwX2dhbWUuZGYgJT4lIGdyb3VwX2J5KEdhbWVzKSAlPiUgZHBseXI6OnN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lCiAgbXV0YXRlKFBlcmNlbnQgPSBwYXN0ZShyb3VuZChDb3VudC9zdW0oQ291bnQpLDIpKjEwMCwiJSIpKQpwcmljZSA8LSBhcHBfZ2FtZS5kZiAlPiUgZHBseXI6Om11dGF0ZShQcmljZSA9IGlmZWxzZShQcmljZSA9PSAwLCBOQSwgUHJpY2UpKSAlPiUgZmlsdGVyKCFpcy5uYShQcmljZSkpCnByaWNlJFByaWNlIDwtIGlmZWxzZShwcmljZSRQcmljZSA8IDUsICJVbmRlciAkNSIsIAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHByaWNlJFByaWNlIDwgMTAsICIkNS0kMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShwcmljZSRQcmljZSA8IDE1LCAiJDEwLSQxNSIsIk92ZXIgJDE1IikpKQpwcmljZSRQcmljZSA8LSBmYWN0b3IocHJpY2UkUHJpY2UsIGxldmVscyA9IGMoIlVuZGVyICQ1IiwiJDUtJDEwIiwiJDEwLSQxNSIsIk92ZXIgJDE1IikpCnByaWNlIDwtIHByaWNlICU+JSBncm91cF9ieShQcmljZSkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQgPSBuKCkpICU+JSAKICBtdXRhdGUoUGVyY2VudCA9IHBhc3RlKHJvdW5kKENvdW50L3N1bShDb3VudCksMikqMTAwLCIlIikpCmFwcF9nYW1lLmRmJEluX2FwcCA8LSBpZmVsc2UoaXMubmEoYXBwX2dhbWUuZGYkYEluLWFwcCBQdXJjaGFzZXNgKSwgIk5vIiwiWWVzIikKaW5fYXBwIDwtIGFwcF9nYW1lLmRmICU+JSBncm91cF9ieShHYW1lcywgSW5fYXBwKSAlPiUgZHBseXI6OnN1bW1hcmlzZShDb3VudCA9IG4oKSkgJT4lIGdyb3VwX2J5KEdhbWVzKSAlPiUKICBkcGx5cjo6bXV0YXRlKFBlcmNlbnQgPSBwYXN0ZShyb3VuZChDb3VudC9zdW0oQ291bnQpLDIpKjEwMCwiJSIpKQppbl9hcHAgPC0gaW5fYXBwWywtM10KI3RhYmxlCmdyaWQuYXJyYW5nZSgKICB0YWJsZUdyb2IocGFpZCksCiAgdGFibGVHcm9iKHByaWNlKSwKICB0YWJsZUdyb2IoaW5fYXBwKSwKICBucm93ID0gMSkKYGBgCgpQcmljZSBBbmFseXNpczpcCjgxJSBvZiB0aGUgZ2FtZXMgb24gdGhlIG1hcmtldCBhcmUgZnJlZSB0byBkb3dubG9hZCBhbmQgcGxheS4gSW4gdGhlIHBhaWQgZ2FtZSBzZWN0b3IsIDg2JSBvZiB0aGUgZ2FtZXMganVzdCBjb3N0IHVuZGVyIFwkNSBmb3IgdGhlIHVzZXIgdG8gZG93bmxvYWQgYW5kIHBsYXkuIFRoaXMgcGVyY2VudCBkcm9wcyBzaWduaWZpY2FudGx5IGFzIHRoZSBwcmljZSBnb2VzIHVwLiBGcmVlIGdhbWUgZGV2ZWxvcGVycyBtaWdodCBnYWluIHJldmVudWUgYmFzZWQgb24gYWR2ZXJ0aXNpbmcgYW5kIGluLWFwcCBwdXJjaGFzZXMuIEFjY29yZGluZ2x5LCA3NyUgb2YgdGhlIGZyZWUgZ2FtZXMgaGF2ZSBpbiBhcHAgcHVyY2hhc2VzIHNlcnZpY2UgYW5kIHRoZSByZXN0IG9mIHRoZW0gbWlnaHQgZWFybiByZXZlbnVlIGZyb20gaW4tYXBwIGFkdmVydGlzaW5nLCBjb21wYXJlZCB3aXRoIDM4JSBhbmQgNjIlIG9mICJ5ZXMiIGFuZCAibm8iIGluLWFwcCBwdXJjaGFzZXMgaW4gdGhlIHBhaWQgZ2FtZSBzZWN0b3IuCgojIyMgMS4yIFNpemUgVHJlbmQgYW5kIEdlbnJlcyBUcmVuZDoKCmBgYHtyIEdhbWUgU2l6ZSBEaXN0cmlidXRpb259CiNPdmVyYWxsCmdncGxvdChkYXRhID0gYXBwX2dhbWUuZGYsIGFlcyh4PVNpemUpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNTAsIGZpbGwgPSAic3RlZWxibHVlIikgKyBnZ3RpdGxlKCJHYW1lIFNpemUgRGlzdHJpYnV0aW9uIikKIyBHYW1lIHNpemUgYnkgR2VucmVzCmdncGxvdChkYXRhID0gYXBwX2dhbWUuZGYsIGFlcyh4PVNpemUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJzdGVlbGJsdWUiLCBiaW53aWR0aCA9IDEwMCkgKyBmYWNldF93cmFwKH5HZW5yZXMpICsgCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsMTAwMCkpICsgZ2d0aXRsZSgiR2FtZSBTaXplIGJ5IEdlbnJlcyIpCmBgYAoKVGhlIGdhbWUgc2l6ZXMgYXJlIG1vc3RseSB1bmRlciBvbmUgZ2lnYWJ5dGUuIFRoZSBBY3Rpb24sIEFkdmVudHVyZSwgYW5kIFNpbXVsYXRpb24gZ2FtZSBoYXZlIGhpZ2hlciBhdmVyYWdlIHNpemUgdGhhbiBvdGhlciBnZW5yZXMsIGFzc3VtaW5nIHRoZSBoaWdoZXIgZGVtYW5kIGZvciBncmFwaGljYWwgYW5kIHN0cmF0ZWdpY2FsIGZlYXR1cmVzIG9mIHRoZSBwbGF5ZXIuIFRoZSBvdGhlciB0aHJlZSBnZW5yZXMgaGF2ZSBzaW1pbGFyIGRpc3RyaWJ1dGlvbiBzaGFwZSB3aXRoIHNpemUgaGlnaGx5IGNvbmNlbnRyYXRlIGluIHRoZSB1bmRlciAzMDBtYiB6b25lLgoKYGBge3IgU2l6ZSBDaGFuZ2UgT3ZlciBUaW1lIGJ5IEdlbnJlcywgbWVzc2FnZT1GQUxTRX0KYXBwX2dhbWUuZGYkWWVhciA8LSBmb3JtYXQoYXBwX2dhbWUuZGYkYE9yaWdpbmFsIFJlbGVhc2UgRGF0ZWAsIiVZIikKYXBwX2dhbWUuZGYgJT4lIGdyb3VwX2J5KFllYXIsIEdlbnJlcykgJT4lIGRwbHlyOjpzdW1tYXJpc2UobWVhbiA9IG1lYW4oU2l6ZSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBZZWFyLCB5ID0gbWVhbiwgZ3JvdXAgPSBHZW5yZXMsIGNvbG9yID0gR2VucmVzKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoKSArCiAgeWxhYigiQXZlcmFnZSBTaXplIikgKyBnZ3RpdGxlKCJBdmVyYWdlIFNpemUgQ2hhbmdlIG92ZXIgVGltZSBieSBHZW5yZXMiKQpgYGAKCkdhbWUgc2l6ZXMgaW5jcmVhc2Ugc3Vic3RhbnRpYWxseSBmcm9tIDIwMDggdG8gMjAxOSAoYWJvdXQgMTIgdGltZXMgb24gYXZlcmFnZSkuIEFjdGlvbiwgQWR2ZW50dXJlLCBhbmQgU2ltdWxhdGlvbiBnYW1lcyBoYXZlIHRoZSBoaWdoZXN0IGF2ZXJhZ2Ugc2l6ZSB3aXRoIGFyb3VuZCA0MzVtYiwgNDMwbWIsIGFuZCAyNjBtYiByZXNwZWN0aXZlbHksIHdoZXJlYXMgRmFtaWx5IGdhbWVzIGhhdmUgYSBtb2Rlc3QgYXZlcmFnZSBzaXplIHdpdGggYXJvdW5kIDIwMG1iLgoKYGBge3IgUmF0aW5nIENoYW5nZSBvdmVyIFRpbWUgYnkgR2VucmVzfQp5ZWFyLnJhdGluZyA8LSBhcHBfZ2FtZS5kZiAlPiUgCiAgZ3JvdXBfYnkoWWVhcixHZW5yZXMpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKG1lYW4uc2l6ZSA9IG1lYW4oYEF2ZXJhZ2UgVXNlciBSYXRpbmdgKSkKbmFtZXMoeWVhci5yYXRpbmcpIDwtIGMoIlllYXIiLCJHZW5yZXMiLCJSYXRpbmciKQpnZ3Bsb3QoeWVhci5yYXRpbmcsIGFlcyh4ID0gWWVhciwgeSA9IFJhdGluZywgZmlsbD0gR2VucmVzKSkgKyBnZW9tX2xpbmUoYWVzKGNvbG9yID0gR2VucmVzKSxzaXplID0xLGdyb3VwID0gMSkgKwogIGZhY2V0X3dyYXAoLn5HZW5yZXMsIG5jb2wgPSAzKSArIHlsYWIoIkF2ZXJhZ2UgUmF0aW5nIikrIGdndGl0bGUoIlJhdGluZyBDaGFuZ2Ugb3ZlciBUaW1lIGJ5IEdlbnJlcyIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpgYGAKClRoZSBhdmVyYWdlIHVzZXIgcmF0aW5ncyBvZiBhbGwgZ2VucmVzIGluY3JlYXNlIG92ZXIgdGltZS4gU2ltdWxhdGlvbiBnYW1lcyBoYXZlIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgdXNlciByYXRpbmcgaW4gMjAxOSAoYWxtb3N0IG5lYXJseSA0LjUpLiBGb2xsb3dpbmcgU2ltdWxhdGlvbiBnYW1lcywgQWR2ZW50dXJlIGFuZCBQdXp6bGUgZ2FtZXMgc2VlbSB0byBoYXZlIGEgc3RhYmxlIGluY3JlYXNlIHNpbmNlIDIwMTEgd2l0aCBhcm91bmQgNC4zIGluIGF2ZXJhZ2UgcmF0aW5nLiBPbiB0aGUgb3RoZXIgaGFuZCwgQWN0aW9uIHNlZW1zIHRvIG5vdCBoYXZlIGFueSBpbXByb3ZlbWVudCBpbiB0ZXJtcyBvZiBjdXN0b21lciBzYXRpc2ZhY3Rpb24gc2luY2UgMjAxMi4KCmBgYHtyIEdlbnJlcyBUcmVuZH0KIyBHZW5yZXMgVHJlbmQKeWVhci5yYXRpbmcgPC0gYXBwX2dhbWUuZGYgJT4lIAogIGdyb3VwX2J5KGZsb29yX2RhdGUoYE9yaWdpbmFsIFJlbGVhc2UgRGF0ZWAsIiVZIiksIEdlbnJlcykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudCA9IG4oKSkKbmFtZXMoeWVhci5yYXRpbmcpIDwtIGMoIlllYXIiLCJHZW5yZXMiLCJDb3VudCIpCnllYXIucmF0aW5nIDwtIHllYXIucmF0aW5nWy1jKDY2OjcxKSxdCmdncGxvdCh5ZWFyLnJhdGluZywgYWVzKHggPSBZZWFyLCB5ID0gQ291bnQsIGNvbG9yPSBHZW5yZXMpKSArIGdlb21fbGluZShzaXplID0gMSkgKwogIGZhY2V0X3dyYXAoLn5HZW5yZXMsIG5jb2wgPSAzKSArIHlsYWIoIk51bWJlciBvZiBHYW1lcyIpICsgZ2d0aXRsZSgiR2VucmVzIFRyZW5kIikrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0MCwgaGp1c3QgPSAxKSkKYGBgCgpCZWNhdXNlIHRoaXMgZGF0YXNldCB3YXMgY29sbGVjdCBpbiBBdWd1c3QgMjAxOSwgdGhlIG51bWJlciBvZiBnYW1lcyBvZiBlYWNoIGdlbnJlIGZvciAyMDE5IGlzIGluY29tcGxldGUuIFRoZXJlZm9yZSwgdGhpcyBnZW5yZSB0cmVuZCBhbmFseXNpcyB3aWxsIGZvY3VzIG9uIHRoZSAyMDA4LTIwMTggcGVyaW9kLiBUaGUgWS1heGlzIGluZGljYXRlcyB0aGUgbnVtYmVyIG9mIGdhbWVzIGludHJvZHVjZWQgb24gQXBwc3RvcmUgaW4gYSBzcGVjaWZpYyB5ZWFyLlwKRnJvbSB0aGUgZ3JhcGgsIHdlIGNhbiBzZWUgdGhhdCB0aGlzIGlzIHRoZSBhZ2Ugb2Ygc2ltdWxhdGlvbiBnYW1lLiBCZWluZyBvbmUgb2YgdGhlIGdlbnJlcyB0aGF0IGhhdmUgdGhlIGxvd2VzdCBudW1iZXIgb2YgZ2FtZXMgaW50cm9kdWNlZCBvbiBBcHBsZSBBcHBzdG9yZSBpbiAyMDA4LCBTaW11bGF0aW9uIGJlY29tZXMgdGhlIGRvbWluYW50IGdlbnJlIHdpdGggYXJvdW5kIDEyMCBnYW1lcyBpbnRyb2R1Y2VkIGluIDIwMTguIEFjdGlvbiBhbmQgQWR2ZW50dXJlIGdhbWVzIGhhdmUgYSBzaW1pbGFyIHRyZW5kIHdpdGggYSBzdWJzdGFudGlhbCBpbmNyZWFzZSBmcm9tIDIwMDggdG8gMjAxNiBhbmQgYSBzbGlnaHQgZGVjcmVhc2UgZnJvbSB0aGVuLiBQdXp6bGUgZ2VucmVzLCBiZWluZyBkb21pbmFudCBmcm9tIDIwMDggdG8gMjAxNSwgc3RlcHBlZCBkb3duIGl0cyB0aHJvbmUgYW5kIGhhZCBjb25zaXN0ZW50bHkgZGVjcmVhc2VkIHNpbmNlIDIwMTUuCgojIyAyLiBGYWN0b3IgQWZmZWN0IFVzZXIgUmF0aW5nIGFuZCBSYXRpbmcgQ291bnQ6CgojIyMgMi4xIEF2ZXJhZ2UgVXNlciBSYXRpbmc6CgpUaGlzIGFuYWx5c2lzIGRpdmlkZXMgdGhlIHNpemUgb2YgZ2FtZXMgaW50byB0aHJlZSBtYWluIGNhdGVnb3JpZXM6XAoxLiBVbmRlciAxMDBtYlwKMi4gQmV0d2VlbiAxMDBtYiBhbmQgMzAwbWJcCjMuIE92ZXIgMzAwbWJcClRoZSBzaXplIGNhdGVnb3JpZXMgY2FuIGdpdmUgdGhlIGRldmVsb3BlcnMgc29tZSBpZGVhcyBhYm91dCB0aGUgc2VnbWVudHMuIFRoZSAidW5kZXIgMTAwbWIiIGdhbWVzIGFyZSB0aGUgb25lcyBhaW1pbmcgdG8gbWFzcyBtYXJrZXQsIHdoaWNoIGp1c3Qgc2VydmUgdGhlIGJhc2ljIG5lZWRzIG9mIGVudGVydGFpbm1lbnQgd2l0aCBhIG1vZGVzdCBudW1iZXIgb2YgZmVhdHVyZXMuIFRoZSBhZHZhbnRhZ2Ugb2YgdGhpcyBjYXRlZ29yeSBpcyB0aGUgYWJpbGl0eSB0byByZWFjaCBhIGxhcmdlIG51bWJlciBvZiB1c2VycyB3aXRoIG1pbmltdW0gaGFyZHdhcmUgcmVxdWlyZW1lbnRzLiBUaGUgIjEwMG1iLTMwMG1iIiBjYXRlZ29yeSB3aWxsIGZvY3VzIG1vcmUgb24gdXNlcnMgd2hvIGFyZSBtb3JlIGludGVyZXN0ZWQgaW4gZ2FtZXMgdGhhbiB0aGUgbWFzcyBtYXJrZXQgYW5kIHJlcXVpcmUgaGlnaGVyIHRlY2huaWNhbCBhbmQgZ3JhcGhpY2FsIGdhbWVwbGF5cy4gVGhlIGxhc3QgY2F0ZWdvcnkgZm9jdXMgb24gImdhbWUgZ2Vla3MiIHdobyBkZW1hbmQgdGhlIGV4dGVuc2l2ZSBncmFwaGljcyBhc3BlY3QgYW5kIHRoZSBjb21wbGV4aXR5IG9mIHRoZSBnYW1lcGxheXMuCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQphcHBfZ2FtZS5kZiRTaXplZ3JvdXAgPC0gaWZlbHNlKGFwcF9nYW1lLmRmJFNpemUgPCAxMDAsICJVbmRlciAxMDBNYiIsIAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGFwcF9nYW1lLmRmJFNpemUgPCAzMDAsICIxMDBNYiAtIDMwME1iIiwiT3ZlciAzMDBNYiIpKQphcHBfZ2FtZS5kZiRTaXplZ3JvdXAgPC0gZmFjdG9yKGFwcF9nYW1lLmRmJFNpemVncm91cCwgbGV2ZWxzID0gYygiVW5kZXIgMTAwTWIiLCIxMDBNYiAtIDMwME1iIiwiT3ZlciAzMDBNYiIpKQphcHBfZ2FtZS5kZiRyYXRpbmcgPC0gaWZlbHNlKGFwcF9nYW1lLmRmJGBBdmVyYWdlIFVzZXIgUmF0aW5nYCA8IDIsICIxIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGFwcF9nYW1lLmRmJGBBdmVyYWdlIFVzZXIgUmF0aW5nYCA8IDMsICIyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGFwcF9nYW1lLmRmJGBBdmVyYWdlIFVzZXIgUmF0aW5nYCA8IDQsIjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGFwcF9nYW1lLmRmJGBBdmVyYWdlIFVzZXIgUmF0aW5nYCA8IDUsIjQiLCI1IikpKSkKYGBgCgpgYGB7ciBVc2VyIFJhdGluZyB2cyBTaXplfQojU3ViZ3JvdXAgMTogdW5kZXIgMTAwbWIKYXBwX2dhbWUuZGYgJT4lIGZpbHRlcihTaXplZ3JvdXAgPT0gIlVuZGVyIDEwME1iIikgJT4lCiAgZ2dwbG90KGFlcyh4PXJhdGluZywgeT1TaXplKSkgKyAKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJzdGVlbGJsdWUiLCBvdXRsaWVyLmNvbG9yID0gImZpcmVicmljazIiKSArCiAgI2ZhY2V0X3dyYXAoLn5HZW5yZXMsIG5yb3cgPSAyKSArCiAgeGxhYigiQXZlcmFnZSBVc2VyIFJhdGluZyIpICsgeWxhYigiU2l6ZSIpICsKICBnZ3RpdGxlKCJVc2VyIFJhdGluZyB2cyBTaXplICg8MTAwbWIpIikKYGBgCgpBbHRob3VnaCBtb3JlIGRhdGEgYXJlIHJlcXVpcmVkIHRvIGdlbmVyYXRlIGEgbW9yZSBhY2N1cmF0ZSBzdGF0ZW1lbnQgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBzaXplIG9mIGdhbWVzIGFuZCB1c2VyIHJhdGluZ3MsIHdlIGNhbiBoYXZlIGEgZ2VuZXJhbCBpZGVhIGFib3V0IHRoaXMgcmVsYXRpb25zaGlwLiBUaGUgYWR2YW50YWdlIG9mIHRoaXMgY2F0ZWdvcnkgaXMgaXRzIG1vZGVzdCByZXF1aXJlbWVudCBhbmQgZWFzeSB0byBwbGF5LiBCdXQgd2hlcmUgaXMgdGhlIGxpbmUgYmV0d2VlbiBzaW1wbGljaXR5IGFuZCB1bmRlciB0aGUgcmVxdWlyZW1lbnRzP1wKRnJvbSB0aGUgYm94cGxvdCBncmFwaCwgZ2FtZXMgd2l0aCA0LXBvaW50IGFuZCA1LXBvaW50IGZvciByYXRpbmcgaGF2ZSBoaWdoZXIgYXZlcmFnZSBzaXplIHRoYW4gdGhvc2Ugd2l0aCBhIGxvd2VyIHJhdGUuIFRvbyBzbWFsbCBpbiBzaXplIG1pZ2h0IGJlIGluc3VmZmljaWVudCBmb3IgdGhlIG1pbmltdW0gcmVxdWlyZW1lbnRzIG9mIHRoZSB1c2Vycy4gSW4gdGhpcyBjYXRlZ29yaWUsIGdhbWVzIHNob3VsZCBiZSA1MG1iIG9yIGhpZ2hlciB0byBjb3ZlciB0aGUgYmFzaWMgbmVlZHMgb2YgdXNlcnMuCgpgYGB7cn0KI1N1Ymdyb3VwIDI6IDEwMG1iIC0gMzAwbWIgCmFwcF9nYW1lLmRmICU+JSBmaWx0ZXIoU2l6ZWdyb3VwID09ICIxMDBNYiAtIDMwME1iIikgJT4lCiAgZ2dwbG90KGFlcyh4PXJhdGluZywgeT1TaXplKSkgKyAKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJzdGVlbGJsdWUiLCBvdXRsaWVyLmNvbG9yID0gImZpcmVicmljazIiKSArIAogIHhsYWIoIkF2ZXJhZ2UgVXNlciBSYXRpbmciKSArCiAgeWxhYigiU2l6ZSIpKwogIGdndGl0bGUoIlVzZXIgUmF0aW5nIHZzIFNpemUgKDEwME1iIC0gMzAwTWIpIikKYGBgCgpXaXRoIHRoZSBkYXRhIGluIGhhbmQsIHdlIGNhbiBub3QgZ2VuZXJhdGUgYSBjbGVhciBzdGF0ZW1lbnQgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNpemUgYW5kIHJhdGluZyBmb3IgMTAwbWIgLSAzMDBtYiBjYXRlZ29yeS4gVGhlIGdhbWVzIHdpdGggMSByYXRpbmcgcG9pbnQgc2VlbSB0byBiZSBiaWdnZXIgaW4gc2l6ZS4gSG93ZXZlciwgdGhlIG51bWJlciBvZiBnYW1lcyB3aXRoIDEgcmF0aW5nIHBvaW50IGlzIHZlcnkgbG93IGNvbXBhcmluZyB0byBvdGhlciByYXRpbmcgY2F0ZWdvcmllcywgc28gY29uY2x1ZGluZyBnYW1lcyB3aXRoIGEgYmlnZ2VyIHNpemUgb2YgdGhpcyBjYXRlZ29yeSBnZXQgYSBsb3dlciByYXRlIHdvdWxkIGJlIGEgd3JvbmcgY29uY2x1c2lvbi4gTW9yZSBkYXRhIG5lZWQgdG8gYmUgY29sbGVjdGVkIGFuZCBhbmFseXplZCB0byBoYXZlIGEgY2xlYXIgYW5kIGFjY3VyYXRlIHN0YXRlbWVudC4KCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiNTdWJncm91cCAzOiBvdmVyIDMwMG1iCmFwcF9nYW1lLmRmICU+JSBmaWx0ZXIoU2l6ZWdyb3VwID09ICJPdmVyIDMwME1iIikgJT4lCiAgZ2dwbG90KGFlcyh4PXJhdGluZywgeT1TaXplKSkgKyAKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJzdGVlbGJsdWUiLCBvdXRsaWVyLmNvbG9yID0gImZpcmVicmljazIiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsMTAwMCkpICsgCiAgeGxhYigiQXZlcmFnZSBVc2VyIFJhdGluZyIpICsgeWxhYigiU2l6ZSIpKwogIGdndGl0bGUoIlVzZXIgUmF0aW5nIHZzIFNpemUgKE92ZXIgMzAwTWIpIikKYGBgCgpPdmVyYWxsLCBnYW1lcyB3aXRoIDQtcG9pbnQgYW5kIDUtcG9pbnQgc2VlbSB0byBoYXZlIGJpZ2dlciBzaXplcy4gSG93ZXZlciwgdGhlcmUgaXMgYSBiaWcgb3ZlcmxhcCBvZiB0aGUgc2l6ZSBiZXR3ZWVuIHRoZSByYXRpbmcgb2YgYSBnb29kIGdhbWUgKDQgYW5kIDUgcmF0aW5nIHBvaW50KSBhbmQgYSBiYWQgZ2FtZSAoYmVsb3cgNCByYXRpbmcgcG9pbnQpLCBzbyB3ZSBjYW4gbm90IGdlbmVyYXRlIGFueSBjb25jbHVzaW9uIGFib3V0IHRoZSBlZmZlY3Qgb2Ygc2l6ZSBvbiB0aGlzIHNpemUgY2F0ZWdvcnkuIE1vcmUgaW5mb3JtYXRpb24gYWJvdXQgZmVhdHVyZXMgaXMgbmVlZGVkIHRvIGdldCBhIGNsZWFyIHZpZXcgb2YgdGhlIGVmZmVjdCBvZiBzaXplIG9uIDEwMC0zMDBtYiBhbmQgb3ZlciAzMDBtYiBzaXplIGNhdGVnb3JpZXMuCgpgYGB7ciBEYXlzIFNpbmNlIExhc3QgVXBkYXRlIGFuZCBSYXRpbmd9CiNDcmVhdGUgdXBkYXRlIHZhcmlhYmxlczoKYXBwX2dhbWUuZGYkdXBkYXRlIDwtIGFzLm51bWVyaWMoU3lzLkRhdGUoKSAtIGFwcF9nYW1lLmRmJGBDdXJyZW50IFZlcnNpb24gUmVsZWFzZSBEYXRlYCkgCgpnZ3Bsb3QoYXBwX2dhbWUuZGYsYWVzKHg9cmF0aW5nLCB5ID0gdXBkYXRlKSkgKyAKICBnZW9tX2JveHBsb3QoZmlsbCA9ICJzdGVlbGJsdWUiLCBvdXRsaWVyLmNvbG9yID0gImZpcmVicmljazIiKSArCiAgeGxhYigiQXZlcmFnZSBVc2VyIFJhdGluZyIpICsgeWxhYigiTnVtYmVyIG9mIERheXMgU2luY2UgTGFzdCBVcGRhdGUiKSArCiAgZ2d0aXRsZSgiRGF5cyBTaW5jZSBMYXN0IFVwZGF0ZSBhbmQgVXNlciBSYXRpbmciKQpgYGAKClRoZSBudW1iZXIgb2YgZGF5cyBzaW5jZSB0aGUgbGFzdCB1cGRhdGUgYW5kIHRoZSBhdmVyYWdlIHVzZXIgcmF0aW5nIGhhdmUgYSBuZWdhdGl2ZSByZWxhdGlvbnNoaXAuIFRoZSBzaG9ydGVyIHRoZSBwZXJpb2QgYW1vbmcgdXBkYXRlcyBkZW1vbnN0cmF0ZSB0aGUgYWJpbGl0eSBvZiBhIGRldmVsb3BlciB0byBhZGFwdCBhbmQgbW9kaWZ5IGl0cyBnYW1lcyB0byBzYXRpc2Z5IHRoZSB1c2VycyBhbmQgbWFya2V0IHRyZW5kcy4gVGhlIGJveHBsb3Qgc2hvd3MgdGhhdCAiZGF5cyBzaW5jZSBsYXN0IHVwZGF0ZSIgb2YgZ2FtZXMgd2l0aCA0LXBvaW50IGFuZCA1LXBvaW50IGFyZSBzaWduaWZpY2FudGx5IGxvd2VyIChhcm91bmQgNzUwIGFuZCA0MDAgZGF5cyBhcyBtZWRpYW4gcmVzcGVjdGl2ZWx5KSB0aGFuIHRoYXQgb2YgZ2FtZXMgd2l0aCAzLXBvaW50IGFuZCBiZWxvdy4KCiMjIyAyLjIgVXNlciBSYXRpbmcgQ291bnQ6CgpCZXNpZGVzIEF2ZXJhZ2UgVXNlciBSYXRpbmcsIFVzZXIgUmF0aW5nIENvdW50IGNhbiBiZSB1c2VkIHRvIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIGN1c3RvbWVycyB3aG8gZG93bmxvYWRlZCB0aGUgZ2FtZXMgYW5kIGFjdHVhbGx5IGV4cGVyaWVuY2VkIGl0IChhY3R1YWwgdXNlcnMpLiBUaGUgZm9sbG93aW5nIGFuYWx5c2lzIHdpbGwgZm9jdXMgb24gdHdvIG1haW4gcXVlc3Rpb25zOlwKMS4gV2hpY2ggdHlwZSBvZiBnYW1lIGFuZCBzaXplIGdyb3VwIGlzIHBlcmZvcm1pbmcgYmV0dGVyIGluIHRlcm1zIG9mIFVzZXIgUmF0aW5nIENvdW50P1wKMi4gV2hpY2ggZ2VucmVzIGFyZSBnb29kIGZvciBkZXZlbG9wZXJzIHRvIGZvY3VzIG9uPwoKYGBge3IgVXNlciBSYXRpbmcgQ291bnQgYW5kIEdhbWUgdHlwZXMsIHdhcm5pbmc9RkFMU0V9CiNidWJibGUgY2hhcnQ6IEZyZWUvUGFpZCwgVXNlciByYXRpbmcsIHNpemUgPSB1c2VyIHJhdGluZyBjb3VudApnZ3Bsb3QoYXBwX2dhbWUuZGYpICsgCiAgZ2VvbV9qaXR0ZXIoYWVzKHggPSBHYW1lcywgeSA9IGBVc2VyIFJhdGluZyBDb3VudGAvMTAwMCwgY29sb3IgPSBHYW1lcyksIGFscGhhID0wLjMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMCw1MDApKSArIAogIHlsYWIoIlJhdGluZyBDb3VudCBpbiBUaG91c2FuZCIpICsgZmFjZXRfZ3JpZCguflNpemVncm91cCkgKyBnZ3RpdGxlKCJVc2VyIFJhdGluZyBDb3VudCBhbmQgR2FtZSB0eXBlcyIpCmBgYAoKT24gYXZlcmFnZSwgRnJlZSBnYW1lcyBoYXZlIGEgaGlnaGVyIHVzZXIgcmF0aW5nIGNvdW50IHRoYW4gcGFpZCBnYW1lcy4gQW1vbmcgc2l6ZSBjYXRlZ29yaWVzLCB0aGUgIjEwMC0zMDBtYiIgY2F0ZWdvcnkgc2VlbXMgdG8gYmUgdGhlIGJlc3QgY2F0ZWdvcnkgZm9yIGZyZWUgZ2FtZXMgd2l0aCB0aGUgaGlnaGVzdCBhdmVyYWdlIHVzZXIgcmF0aW5nIGNvdW50IHRoYW4gYW55IG90aGVyIGNhdGVnb3J5LiBGb3IgcGFpZCBnYW1lcywgInVuZGVyIDEwMG1iIiBzZWVtIHRvIGJlIHRoZSBiZXN0IHNpemUgY2F0ZWdvcmllcyBmb3IgZGV2ZWxvcGVycyB0byBmb2N1cyBvbi4KCmBgYHtyIFVzZXIgUmF0aW5nIENvdW50IHZzIEdlbnJlcywgd2FybmluZz1GQUxTRX0KZ2dwbG90KGFwcF9nYW1lLmRmKSArIAogIGdlb21faml0dGVyKGFlcyh4ID0gR2VucmVzLCB5ID0gYFVzZXIgUmF0aW5nIENvdW50YC8xMDAwLCBjb2xvciA9IEdlbnJlcyksIGFscGhhID0wLjQpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMCw1MDApKSArIAogIHlsYWIoIlJhdGluZyBDb3VudCBpbiBUaG91c2FuZCIpICsgZ2d0aXRsZSgiVXNlciBSYXRpbmcgQ291bnQgYW5kIEdlbnJlcyIpCmBgYAoKSW4gdGVybXMgb2YgdXNlciByYXRpbmcgY291bnQsIEFjdGlvbiwgQWR2ZW50dXJlLCBhbmQgU2ltdWxhdGlvbiBhcmUgcG90ZW50aWFsIGdlbnJlcyBmb3IgZGV2ZWxvcGVycyB0byBmb2N1cyBvbiB3aXRoIGhpZ2hlciBhdmVyYWdlIHVzZXIgcmF0aW5nIGNvdW50IHRoYW4gb3RoZXJzLiBGYW1pbHkgZ2FtZXMgc2VlbSB0byBiZSB0aGUgbGVhc3QgcG90ZW50aWFsIGdlbnJlIHdpdGggdGhlIGxvd2VzdCBudW1iZXIgb2YgcmF0aW5nIGNvdW50IGFtb25nIHRoZSBzaXggZ2VucmVzLgoKIyMgMy4gS2V5IFRha2Vhd2F5cyBhbmQgUmVjb21tZW5kYXRpb25zOgoKIyMjIDMuMSBLZXkgVGFrZWF3YXlzOgoKMS4gIFB1enpsZSBnYW1lcyBhcmUgZG9taW5hdGluZyB0aGUgQXBwc3RvcmUgZ2FtZSBtYXJrZXQgd2l0aCB0aGUgaGlnaGVzdCBudW1iZXIgb2YgZ2FtZXMgKG1vcmUgdGhhbiAxMDAwIGdhbWVzKS5cCjIuICBPbiBhdmVyYWdlIGZyb20gMjAwOC0yMDE5LCBBY3Rpb24sIEFkdmVudHVyZSwgYW5kIEZhbWlseSBnZW5yZXMgaGF2ZSB0aGUgaGlnaGVzdCBhdmVyYWdlIHVzZXIgcmF0aW5nIHdpdGggNC4yMiBwb2ludC5cCjMuICBTaW11bGF0aW9uIGlzIHRoZSByaXNpbmcgc3RhciB3aXRoIGEgY29uc2lzdGVudCBpbmNyZWFzZSBpbiBib3RoIHRoZSBhdmVyYWdlIHVzZXIgcmF0aW5nIHBvaW50IGFuZCB0aGUgbnVtYmVyIG9mIGdhbWUgaW50cm9kdWNlZCBwZXIgeWVhciBpbiB0aGUgbGFzdCBkZWNhZGUuXAo0LiAgT3ZlcmFsbCwgdGhlIHNpemVzIG9mIGdhbWVzIG9uIHRoZSBtYXJrZXQgaW5jcmVhc2UgZHJhbWF0aWNhbGx5IHNpbmNlIDIwMDggKDEyIHRpbWVzIG9uIGF2ZXJhZ2UpLlwKNS4gIEZvciB0aGUgdHlwZSBvZiBnYW1lcywgRnJlZSBnYW1lIHNlZW1zIHRvIGRvbWluYXRlIHRoZSBtYXJrZXQgd2l0aCA4MSUgY292ZXJhZ2UuCgojIyMgMy4yIFJlY29tbWVuZGF0aW9uOgoKMS4gIEFjdGlvbiwgQWR2ZW50dXJlLCBhbmQgU2ltdWxhdGlvbiBhcmUgZ29vZCBnZW5yZXMgZm9yIGRldmVsb3BlcnMgdG8gZm9jdXMgb24gaW4gdGVybXMgb2YgdXNlciByYXRpbmcgY291bnQgYW5kIGN1c3RvbWVyIHRyZW5kLCBlc3BlY2lhbGx5IFNpbXVsYXRpb24gd2l0aCB0aGUgaGlnaGVzdCBpbmNyZWFzZSBpbiBib3QgYXZlcmFnZSB1c2VyIHJhdGluZyBhbmQgdGhlIG51bWJlciBvZiBuZXcgZ2FtZXMgaW50cm9kdWNlZC5cCjIuICBGb3IgdGhlIHR5cGUgb2YgZ2FtZSwgRnJlZSBnYW1lIHNlZW1zIHRvIGJlIHRoZSBiZXR0ZXIgY2F0ZWdvcnkgdG8gZm9jdXMgb24gd2l0aCBhIHNpZ25pZmljYW50bHkgaGlnaGVyIGF2ZXJhZ2UgdXNlciByYXRpbmcgY291bnQuIFRoZSByZXZlbnVlIGZvciBmcmVlIGdhbWVzIGNhbiBiZSBnZW5lcmF0ZWQgYnkgYWR2ZXJ0aXNpbmcgYW5kIGluLWFwcCBwdXJjaGFzZXMuXAozLiAgRm9yIHRoZSBzaXplIGNhdGVnb3J5LCBpbiB0ZXJtIG9mIHVzZXIgcmF0aW5nIGNvdW50LCAiMTAwLTMwMG1iIiBzZWVtIHRvIGJlIHRoZSBiZXN0IGNhdGVnb3J5IGZvciBmcmVlIGdhbWVzIGFuZCAidW5kZXItMTAwbWIiIGNhdGVnb3J5IGlzIGdvb2QgZm9yIGRldmVsb3BlcnMgd2hvIHdhbnQgdG8gZm9jdXMgb24gcGFpZCBnYW1lcy4K