Notebook description: This exercise explores both apps and games on the Google App Store, looking into their rating scores, installs, pricing models, ad-support, categories and content ratings.
Data Cleaning
#clean names and parse dates
df= playstore
df = df %>% clean_names()
df$released = mdy(df$released)
df$last_updated = mdy(df$last_updated)
df$last_updated_year = year(df$last_updated)
df$release_year = year(df$released)
#remove alphabet after number in size
df$size = sub("(\\d)[^0-9]+$", "\\1", df$size)
#remove 'and above' in minimum andriod
df$minimum_android = sub("(\\d)[^0-9]+$", "\\1", df$minimum_android)
summary(df$released)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
"2010-01-28" "2017-02-19" "2018-11-18" "2018-04-22" "2020-01-23" "2020-12-03" "7730"
summary(df$last_updated)
Min. 1st Qu. Median Mean 3rd Qu. Max.
"2008-10-22" "2019-02-04" "2020-03-06" "2019-08-18" "2020-09-11" "2020-12-03"
The dataset contains:
- apps that were released from 2010 to 2020
- earliest: 2010-01-28, latest: 2020-12-03
- 7730 obs without release year
- apps that were last updated in 2008 to 2020
- earliest: 2008-10-22, latest: 2020-12-03
- we can assume that games that did not have an update in the year 2020
- while the earliest release year is 2010, the earliest update year is 2008
length(unique(df$app_id))
[1] 1118136
length(unique(df$app_name))
[1] 1046870
length(unique(df$app_id)) - length(unique(df$app_name))
[1] 71266
length(unique(df$developer_id))
[1] 165594
- 1118136 unique IDs, there are no duplicates in the dataset
- 1046870 unique app names, there are some apps with the same names in the dataset
- 165594 unique developers
df$free = as.integer(as.logical(df$free))
df$in_app_purchases = as.integer(as.logical(df$in_app_purchases))
df$ad_supported = as.integer(as.logical(df$ad_supported))
Hmisc::describe(as.factor(df$free))
as.factor(df$free)
n missing distinct
1118136 0 2
Value 0 1
Frequency 46803 1071333
Proportion 0.042 0.958
Hmisc::describe(as.factor(df$in_app_purchases))
as.factor(df$in_app_purchases)
n missing distinct
1118136 0 2
Value 0 1
Frequency 998025 120111
Proportion 0.893 0.107
Hmisc::describe(as.factor(df$ad_supported))
as.factor(df$ad_supported)
n missing distinct
1118136 0 2
Value 0 1
Frequency 392320 725816
Proportion 0.351 0.649
Attribute Construction
print(unique(df$category))
[1] "Communication" "Strategy" "Tools" "Business"
[5] "Music & Audio" "Maps & Navigation" "Lifestyle" "Educational"
[9] "Productivity" "News & Magazines" "Food & Drink" "Photography"
[13] "Books & Reference" "Shopping" "Board" "Sports"
[17] "Medical" "Health & Fitness" "House & Home" "Education"
[21] "Auto & Vehicles" "Finance" "Parenting" "Travel & Local"
[25] "Personalization" "Entertainment" "Video Players & Editors" "Puzzle"
[29] "Casual" "Events" "Arcade" "Music"
[33] "Trivia" "Action" "Simulation" "Racing"
[37] "Weather" "Role Playing" "Adventure" "Social"
[41] "Word" "Comics" "Art & Design" "Card"
[45] "Libraries & Demo" "Casino" "Beauty" "Dating"
[49] ""
Category There are 49 levels in the category feature, including a NA. The levels are grouped into (1) Apps or (2) Games as described by Google Play Console Help in the following section.
#17 game genres
gamegenres= c("Action","Adventure","Arcade","Board","Casino","Casual","Educational","Music","Puzzle","Racing","Role Playing","Simulation","Sports","Strategy","Trivia","Word")
df = df %>% mutate(is_game = if_else(category %in% gamegenres,"1","0"))
Hmisc::describe(df$is_game)
df$is_game
n missing distinct
1118136 0 2
Value 0 1
Frequency 947111 171025
Proportion 0.847 0.153
- 15.3% of the obs in the dataset are games and 84.7% are apps.
#price model
df = df %>% mutate(price_model = case_when(
(free==1 & in_app_purchases == 1) ~ "freemium",
(free==1 & in_app_purchases == 0) ~ "free",
(free==0 & in_app_purchases == 1) ~ "paymium",
(free==0 & in_app_purchases == 0) ~ "paid",
TRUE ~"other"
))
Hmisc::describe(df$price_model)
df$price_model
n missing distinct
1118136 0 4
Value free freemium paid paymium
Frequency 954156 117177 43869 2934
Proportion 0.853 0.105 0.039 0.003
- Price model attribute combines free and in_app_purchases features to understand the developers pricing strategy
- Majority of the observations in the dataset are free applications and least are paymium.
str(df)
'data.frame': 1118136 obs. of 27 variables:
$ app_name : chr "HTTrack Website Copier" "World War 2: Offline Strategy" "WPSApp" "OfficeSuite - Office, PDF, Word, Excel, PowerPoint" ...
$ app_id : chr "com.httrack.android" "com.skizze.wwii" "com.themausoft.wpsapp" "com.mobisystems.office" ...
$ category : chr "Communication" "Strategy" "Tools" "Business" ...
$ rating : num 3.6 4.3 4.2 4.2 4.2 3.5 4.4 4 4.1 3.3 ...
$ rating_count : int 2848 17297 488639 1224420 665 377 3346 44700 10990 1141 ...
$ installs : chr "100,000+" "1,000,000+" "50,000,000+" "100,000,000+" ...
$ minimum_installs : num 1e+05 1e+06 5e+07 1e+08 5e+04 1e+04 1e+05 1e+07 1e+07 1e+05 ...
$ maximum_installs : num 3.52e+05 2.16e+06 7.93e+07 1.64e+08 7.35e+04 ...
$ free : int 1 1 1 1 1 1 1 1 1 1 ...
$ price : num 0 0 0 0 0 0 0 0 0 0 ...
$ currency : chr "USD" "USD" "USD" "USD" ...
$ size : chr "2.7" "86" "5.8" "59" ...
$ minimum_android : chr "2.3" "5.1" "4.1" "4.4" ...
$ developer_id : chr "Xavier Roche" "Skizze Games" "TheMauSoft" "MobiSystems" ...
$ developer_website: chr "http://www.httrack.com/" "http://stereo7.com/" "http://www.themausoft.com" "http://www.mobisystems.com" ...
$ developer_email : chr "roche+android@httrack.com" "Skizze.Games@gmail.com" "wpsapp.app@gmail.com" "support-officesuite-android@mobisystems.com" ...
$ released : Date, format: "2013-08-12" "2018-07-19" "2016-03-07" "2011-12-22" ...
$ last_updated : Date, format: "2017-05-20" "2020-11-26" "2020-10-21" "2020-11-23" ...
$ content_rating : chr "Everyone" "Everyone 10+" "Everyone" "Everyone" ...
$ privacy_policy : chr "http://android.httrack.com/privacy-policy.html" "https://www.iubenda.com/privacy-policy/8032781" "https://sites.google.com/view/wpsapppolicy/main" "http://www.mobisystems.com/mobile/privacy-policy.html" ...
$ ad_supported : int 0 1 1 1 0 0 0 1 1 1 ...
$ in_app_purchases : int 0 1 0 1 0 0 1 1 1 0 ...
$ editors_choice : chr "False" "False" "False" "False" ...
$ last_updated_year: num 2017 2020 2020 2020 2020 ...
$ release_year : num 2013 2018 2016 2011 2016 ...
$ is_game : chr "0" "1" "0" "0" ...
$ price_model : chr "free" "freemium" "free" "freemium" ...
#age of app (days between collection_date and released_date)
collection_date = '2020-12-03'
df$interval = interval(ymd(df$released),ymd(collection_date))
df$age<- as.period(df$interval)
df$age <- df$age %/% days(1) #convert to days
head(df$age)
[1] 2669 866 1730 3267 1530 668
summary(df$age)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
0 314 745 954 1383 3961 7730
- median of 745 days between data collection date and app release date
#age of update (days between collection date and last_update)
df$interval2 = interval(ymd(df$last_updated),ymd(collection_date))
df$age_update <- as.period(df$interval2)
df$age_update <- df$age_update %/% days(1) #convert to days
head(df$age_update)
[1] 1291 7 42 10 11 27
summary(df$age_update)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0 82.0 270.0 471.1 668.0 4424.0
- median of 270 days between data collection date and app last update date
Data Exploration
Entire dataset
#apps and games released over the years
df %>% group_by(is_game,release_year) %>% tally() %>% ggplot(aes(x=release_year, y= n, fill=is_game)) + geom_area(alpha=0.9) + scale_fill_jama()

#games release year
df %>% group_by(release_year) %>% tally() %>% mutate(prop = round(n/sum(n),3))
#games update year
df %>% group_by(last_updated_year) %>% tally() %>% mutate(prop = round(n/sum(n),3))
- Release year:
- more than 60% of the apps in the dataset are released from 2018, 2019 and 2020.
- 7730 apps in the dataset have no release year indicated
- Last updated year:
- 55.8 % of the apps in the dataset had at least one update in 2020, we will consider these as active applications in this notebook.
#ad supported over years (last_updated_year 2016 to 2020)
df %>% filter(last_updated_year>2015) %>% group_by(is_game,last_updated_year, ad_supported) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=last_updated_year, y=prop, fill=factor(ad_supported))) + geom_col(width=0.6,alpha=0.9) + facet_wrap(~is_game,labeller=label_both) + scale_fill_uchicago() + theme(legend.position="bottom") + labs(y="proportion",fill="ad_supported")

Last updated dates: 2010-01-28 to 2020-12-03 * The ad support feature could help in understanding the developers’ pricing strategy * The above chart used last updated year as a comparision as ad support could change with app updates * Apps: increased use of ads from 2016 to 2019, and a slight decrease in 2020 (Jan to Nov) * Games: increasing porportion of ad support games from 2016 to 2020
#installs levels
length(unique(df$installs))
[1] 23
unique(df$installs)
[1] "100,000+" "1,000,000+" "50,000,000+" "100,000,000+" "50,000+" "10,000+"
[7] "10,000,000+" "5,000,000+" "500,000,000+" "500+" "500,000+" "1,000+"
[13] "100+" "5,000,000,000+" "5,000+" "10+" "50+" "1+"
[19] "" "5+" "1,000,000,000+" "0+" "10,000,000,000+"
#minimum_installs distribution
df %>% filter(!is.na(minimum_installs)) %>% group_by(is_game, minimum_installs) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=factor(minimum_installs), y=prop, fill= is_game)) + geom_col() + facet_wrap(~is_game,labeller=label_both) + theme(legend.position="none") + theme(axis.text.x=element_text(angle=90)) + scale_fill_jama() + labs(y="proportion",x="minimum_installs")

- There are 23 levels of installs categories including one NA
- Similar distribution shape of apps and games installs(minimum installs)
#ratio of app to dev
df %>% group_by(release_year) %>% summarise(devcount=n_distinct(developer_id), appcount=n_distinct(app_id)) %>% mutate(ratio= (appcount/devcount))
#plot release_year, ratio, is_game
df %>% group_by(is_game, release_year) %>% summarise(devcount=n_distinct(developer_id), appcount=n_distinct(app_id)) %>% mutate(ratio= (appcount/devcount)) %>% ggplot(aes(x=release_year, y=ratio, fill=is_game)) + geom_area() + scale_fill_uchicago() + labs(title="Ratio of apps to developers" )

- The ratio of apps (app and game classes) to developers have increased over the years from 2.08 in 2011 to 3.98 in 2019.
- suggesting that developers are increasingly releasing more apps on the play store per year
- Comparison of app to developer ratio between apps and games classes
- game developers release more games a year as compared to app developers
- the growth in ratio is more significant over the years in the games class compared to apps class
Subset
The following subset is created to try reduce biasness for further exploration, with the following conditions: + are active (have at least one update in 2020) + have a release year + have a rating score + minimum installs equal or above 5000 + rating count above 500
#subset
#last updated in 2020, a rating score, rating count >500, minimum_installs =>5000
active = df %>% filter (last_updated_year== 2020) %>% filter(rating>0) %>% filter(rating_count>500) %>% filter(minimum_installs>1000)
#release year
active %>% group_by(release_year) %>% tally()
#drop obs with no release year
active = active %>% filter (!is.na(release_year))
active$size = as.numeric(active$size)
active$minimum_android = as.numeric(active$minimum_android)
active = active %>% filter(!is.na(size)) %>% filter(!is.na(minimum_android))
dim(active)
[1] 80657 32
- 80657 out of 1118136 observations met the above observations.
#plot release year of active apps
active %>% group_by(release_year) %>% tally() %>% ggplot(aes(x=as.factor(release_year), y=n, fill=n)) + geom_col(width=0.8) + labs(y="","x=release_year") + geom_text(aes(label=n), vjust = -0.3, size = 3.5) + scale_fill_viridis() + theme(legend.position="none")

- As expected, majority of the active apps are released in the recent years
- The oldest active apps are released in 2010 and there are 855 observations.
- There are more apps from 2019 than 2020 in the subset, this could be attributed to conditions of the subset as newer apps released in the recent months could have lower install count and rating count.
#comparing games and apps rating distribution
active %>% ggplot(aes(x=rating, fill=is_game)) + geom_bar() + facet_wrap(~is_game, labeller=label_both) + scale_fill_jama()

- There is a similar distribution shape of rating score between games and apps, both are positively skewed.
#proportion of rating score between free and paid apps
#apps
rf0 = active %>% filter(is_game==0) %>% group_by(free, rating) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=rating,y=prop, fill=factor(free))) + geom_col() + facet_wrap(~free,labeller=label_both) + theme(legend.position="none") + scale_fill_manual(values = c("#dda15e", "#bc6c25")) + labs(y="proportion", title="Apps") + scale_y_continuous(limits=c(0,0.13))
#games
rf1 = active %>% filter(is_game==1) %>% group_by(free, rating) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=rating,y=prop, fill=factor(free))) + geom_col() + facet_wrap(~free,labeller=label_both) + theme(legend.position="none") +
scale_fill_manual(values = c("#606c38", "#283618")) + labs(y="proportion", title="Games") + scale_y_continuous(limits=c(0,0.13))
#plot
ggarrange(rf0,rf1,ncol=1)

- The distribution of rating score between free games and free apps are very similar.
- Free apps have a larger proportion of ratings 2 and below, compared to paid apps.
- Free games have a larger proportion of ratings 2.5 and below compared to paid games.
- In both apps and games, free class has a lower proportion of ratings 4.5 and above, compared to paid class.
- Paid apps have a larger proportion of ratings 4.5 and above compared to paid games.
#maximum_installs, rating_count, rating
pair_vars = active %>% select(maximum_installs, rating_count, rating, age, age_update)
ggpairs(pair_vars)

- As expected there is a positive relationship between
- maximum_installs and rating_count
- rating(score) and maximum_installs
- rating(score) and rating_count
- Age of app has a negative correlation with rating, positive with rating count and maximum_installs
- Age of app updates negative correlation with maximum installs, rating_count and rating
- The distribution of ratings in the subset is negatively skewed, with majority of the observations having rating(score) of 4.0 and above.
#categories
c0 = active %>% filter(is_game==0) %>% group_by(category) %>% tally() %>% ggplot(aes(x=reorder(category,n), y=n)) + geom_col(fill="#52796f") + coord_flip() + labs(y="category",x="")
c1 = active %>% filter(is_game==1) %>% group_by(category) %>% tally() %>% ggplot(aes(x=reorder(category,n), y=n)) + geom_col(fill="#495057") + coord_flip() + labs(y="category",x="")
ggarrange(c0,c1)

- Apps: Tools are the most frequent category while events are the least frequent
- Games: Simulation genre is the most frequent category while music is the least frequent
#avg rating: app
active %>% filter(is_game==0) %>% group_by(category) %>% summarise(avg= mean(rating)) %>% arrange(desc(avg)) %>% as.data.frame()
#avg rating: game
active %>% filter(is_game==1) %>% group_by(category) %>% summarise(avg= mean(rating)) %>% arrange(desc(avg))
Average app ratings for categories:
- In apps: Books & Reference have the highest average rating score, while Dating have the lowest.
- In games: Casino and Word genre have the highest average rating score, while Simulation genre have the lowest.
- interestingly, simulation games are the most common game apps (shown in the previous section)
#active installs levels
active %>% group_by(is_game, minimum_installs) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=factor(minimum_installs), y=prop, fill= is_game)) + geom_col(alpha=0.9) + facet_wrap(~is_game) + theme(legend.position="top") + theme(axis.text.x=element_text(angle=90)) + scale_fill_manual(values=wes_palette("Moonrise2")) + labs(x="minimum_installs",y="proportion",title="Install levels")

- When comparing install levels between apps and games,
- apps have a higher proportion in 100,000 installs and below, compared to games
- apps have a lower proportion in levels 1 million installs and above, compared to games
Most frequent categories in apps and games
#comparing categories in subset and complete dataset
#apps
df %>% filter(is_game==0) %>% group_by(category) %>% tally(sort=T)
active %>% filter(is_game==0) %>% group_by(category) %>% tally(sort=T)
#games
df %>% filter(is_game==1) %>% group_by(category) %>% tally(sort=T)
active %>% filter(is_game==1) %>% group_by(category) %>% tally(sort=T)
- In comparing subset(active apps) and entire dataset, there are some similarities in the most frequent categories in both games and apps.
- The following exploration will use the top 5 categories in the subset as it contains (active) apps that have at least an update in 2020.
- Apps: tools, entertainment, education, personalization, music & audio
- Games: simulation, puzzle, casual, sports, action,
#proportion of top 5 most frequent categories across the years
#apps
top_cats = c("Tools","Entertainment","Education","Personalization","Music & Audio")
df %>% filter(last_updated_year==2020) %>% group_by(release_year, category) %>% tally() %>% mutate(prop=n/sum(n)) %>% filter(category %in% top_cats)%>% ggplot(aes(x=release_year, y=prop, fill=category)) + geom_col(alpha=0.9) + scale_fill_jama() + theme(legend.position="left") + labs(y="proportion", x="", fill="Apps")

#games
top_genres = c("Simulation","Puzzle","Casual","Sports","Action")
df %>% filter(last_updated_year==2020) %>% filter(is_game==1) %>% group_by(release_year, category) %>% tally() %>% mutate(prop=n/sum(n)) %>% filter(category %in% top_genres) %>% ggplot(aes(x=release_year, y=prop, fill=category)) + geom_col(alpha=0.9) + scale_fill_jco() + theme(legend.position="left") + labs(y="proportion", x="", fill="Games")

Looking into the proportion of most frequent categories in active apps (last updated in 2020) to explore the saturation of new releases:
- Apps:
- The identified 5 categories jointly have increased in proportion to other categories over the years.
- Music & Audio category have increased in the past two years (2019 and 2020) while Personalization have decreased in the same time period.
- Games:
- The identified 5 genres jointly make up more than 50% of the games across the years.
- Proportion of Sports games have decreased over the years
- Between the identified 5 genres, Action games make up the lowest proportion from 2015 to 2020.
Pricing strategies
#price model by app type
p1 = active %>% group_by(is_game,price_model) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=is_game, y=prop, fill=price_model)) + geom_col(width=0.5) + coord_flip() + scale_fill_jama() + theme(legend.position="right")
#ad supported by app type
p2 = active %>% group_by(is_game,ad_supported) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=is_game, y=prop, fill=factor(ad_supported))) + geom_col(width=0.5) + coord_flip() + scale_fill_uchicago() + theme(legend.position="right")
ggarrange(p1,p2, nrow=2)

- Price model:
- freemium is the most popular price model among game developer, while free is most popular among app developers
- there is a higher proportion of paid and paymium price model in game when compared to apps
- Add supported:
- games have a higher proportion of ad supported than apps
#price model and ad supported
active %>% group_by(is_game, price_model, ad_supported) %>% tally() %>% mutate(prop = n/sum(n)) %>% ggplot(aes(x=price_model, y=prop, fill=factor(ad_supported))) + geom_col(width=0.6,alpha=0.9) + coord_flip() + facet_wrap(~is_game, labeller=label_both) + scale_fill_uchicago() + theme(legend.position="bottom") + labs(y="proportion",fill="ad_supported")

- Looking at both price model and ad supported:
- across both games and apps, as expected free to download model (free and freemium) have a higher proportion of ad supported than paid and paymium models
- free to download games have a higher proportion of ad support than free to download apps
- paymium apps have a higher proportion of ad supported than paymium games
- freemium games have the most ad support while paid games have the least ad support
#most frequent categories and price_model
active %>% filter (is_game==0) %>% filter(category %in% top_cats) %>% group_by(category, price_model) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=category, y=prop, fill=price_model)) + geom_col(alpha=0.9, width=0.7) + scale_fill_jama() + theme(legend.position="bottom") + labs(y="proportion", x="app category") + coord_flip()

active %>% filter (is_game==1) %>% filter(category %in% top_genres) %>% group_by(category, price_model) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=category, y=prop, fill=price_model)) + geom_col(alpha=0.9, width=0.7) + scale_fill_jama() + theme(legend.position="bottom") + labs(y="proportion", x="game category") + coord_flip()

Looking at developers’ pricing (model) strategy in the identified 5 categories:
- Apps:
- personalization has the most paymium model compared to other categories
- tools have paid model compared to other categories
- Games:
- action genre have the most paymium price model
- sports genre have the most free model and least freemium model
#summary of paid apps by is_game
active_copy = active %>% filter(free==0)
by(active_copy$price, active_copy$is_game, summary)
active_copy$is_game: 0
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.990 1.990 2.990 5.353 4.990 379.990
---------------------------------------------------------------------------------------
active_copy$is_game: 1
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.99 1.49 2.99 3.95 4.99 49.99
#paid games prices of 5 most frequent categories
active %>% filter (is_game==1) %>% filter(free==0) %>% filter(category %in% top_genres) %>% ggplot(aes(x=category, y=price, color=category)) + geom_jitter(show.legend=FALSE, size=2, alpha=0.5) + coord_flip() + scale_color_futurama() + labs(y="price (USD)", title="is_game = 1")

#paid apps prices of 5 most frequent categories
active %>% filter (is_game==0) %>% filter(free==0) %>% filter(category %in% top_cats) %>% ggplot(aes(x=category, y=price, color=category)) + geom_jitter(show.legend=FALSE, size=2, alpha=0.5) + coord_flip() + scale_color_futurama() + labs(y="price (USD)", title="is_game = 0")

- For pay to download models,
- the lowest price is 0.99USD for both games and apps
- highest priced pay to download game is 49.99USD and app is 379.99
- median of 2.99 USD for both games and apps
- Looking at prices between 0.99 and 50.00USD
- games: sports and simulation have games more than 10USD but puzzle, casual and action do not
- apps: tools and education have apps that are more than 20USD, but personalization, music & audio, and entertainment categories do not.
Content rating
#content rating
active %>% group_by(is_game,content_rating) %>% tally() %>% mutate(prop=round(n/sum(n),2))
active %>% group_by(is_game,content_rating) %>% tally() %>% mutate(prop=round(n/sum(n),2)) %>% ggplot(aes(x=is_game, y=prop, fill=factor(content_rating))) + geom_col(width=0.5) + coord_flip() + scale_fill_uchicago() + theme(legend.position="bottom") + labs(y="proportion",fill="content_rating")

- in both games and apps types, ‘Everyone’ rating is the most common and "Adults only 18+’ is the least common
- compared to apps, games have a higher proportion of ‘Teen’ and ‘Everyone 10+’ content but lower proportion of ‘Mature 17+’ and ‘Everyone’ rating.
#content_rating of most frequent categories
active %>% filter (is_game==1) %>% filter(category %in% top_genres) %>% group_by(category, content_rating) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=category, y=prop, fill=content_rating)) + geom_col(alpha=0.9, width=0.7) + scale_fill_uchicago() + theme(legend.position="bottom") + labs(y="proportion", x="game category") + coord_flip()

active %>% filter (is_game==0) %>% filter(category %in% top_cats) %>% group_by(category, content_rating) %>% tally() %>% mutate(prop=n/sum(n)) %>% ggplot(aes(x=category, y=prop, fill=content_rating)) + geom_col(width=0.7) + scale_fill_jco() + theme(legend.position="bottom") + labs(y="proportion", x="app category") + coord_flip()

- Games:
- majority of sports, simulation, puzzle and casual genres are rated ‘Everyone’, but majority of action is rated ‘Teen’
- action games have the highest proportion of ‘Mature 17+’ content compared to the other identified genres
- Apps:
- across the five identified genres, majority of the apps are rated ‘Everyone’, least are ‘Everyone 10+’, and none are rated ‘Adults only 18+’
- entertainment apps have the highest proportion of ‘Teen’ and ‘Mature 17+’ content compared to the other identified app categories
Export dataframe
#shortlisted variables for modeling
games1 = active %>% filter(is_game == 1) %>% select(app_id, category, rating, rating_count, minimum_installs, maximum_installs, free, price, size, content_rating, ad_supported, in_app_purchases, last_updated_year, release_year, age, age_update)
dim(games1)
[1] 22591 16
apps1 = active %>% filter(is_game == 0) %>% select(app_id, category, rating, rating_count, minimum_installs, maximum_installs, free, price, size, content_rating, ad_supported, in_app_purchases, last_updated_year, release_year, age, age_update)
dim(apps1)
[1] 58066 16
#export df
write.csv(games1,"games1.csv",row.names = FALSE)
write.csv(apps1,"apps1.csv",row.names = FALSE)
LS0tCnRpdGxlOiAiUGxheSBTdG9yZSBFREEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkRhdGFzZXQ6IFtHb29nbGUgUGxheSBTdG9yZSBBcHBzXShodHRwczovL2dpdGh1Yi5jb20vZ2F1dGhhbXAxMC9Hb29nbGUtUGxheXN0b3JlLURhdGFzZXQpIGJ5IEdhdXRoYW4gUHJha2FzaCwgY29udGFpbmluZyAxLjEgbWlsbGlvbiBhcHBsaWNhdGlvbnMgd2l0aCAyMyBhdHRyaWJ1dGVzIChjb2xsZWN0ZWQgaW4gRGVjZW1iZXIgMjAyMCkuIAoKTm90ZWJvb2sgZGVzY3JpcHRpb246IFRoaXMgZXhlcmNpc2UgZXhwbG9yZXMgYm90aCBhcHBzIGFuZCBnYW1lcyBvbiB0aGUgR29vZ2xlIEFwcCBTdG9yZSwgbG9va2luZyBpbnRvIHRoZWlyIHJhdGluZyBzY29yZXMsIGluc3RhbGxzLCBwcmljaW5nIG1vZGVscywgYWQtc3VwcG9ydCwgY2F0ZWdvcmllcyBhbmQgY29udGVudCByYXRpbmdzLgoKTm90ZWJvb2sgQ29udGVudHM6CgoqIERhdGEgQ2xlYW5pbmcgCiogQXR0cmlidXRlIENvbnN0cnVjdGlvbgoqIERhdGEgRXhwbG9yYXRpb24KCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShIbWlzYykKbGlicmFyeShza2ltcikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KHdlc2FuZGVyc29uKQpsaWJyYXJ5KGdnc2NpKQpsaWJyYXJ5KHZpcmlkaXMpCnRoZW1lX3NldCh0aGVtZV9idygpKQpgYGAKCiMjIyBJbXBvcnQgRGF0YQpgYGB7cn0KcGxheXN0b3JlID0gcmVhZC5jc3YoIkdvb2dsZS1QbGF5c3RvcmUuY3N2IixoZWFkZXI9VFJVRSkKZGltKHBsYXlzdG9yZSkKYGBgCiogMTExODEzNiByb3dzIGFuZCAyMyBjb2xzCgojIyMgRGF0YSBDbGVhbmluZwpgYGB7cn0KI2NsZWFuIG5hbWVzIGFuZCBwYXJzZSBkYXRlcwpkZj0gcGxheXN0b3JlCmRmID0gZGYgJT4lIGNsZWFuX25hbWVzKCkKZGYkcmVsZWFzZWQgPSBtZHkoZGYkcmVsZWFzZWQpCmRmJGxhc3RfdXBkYXRlZCA9IG1keShkZiRsYXN0X3VwZGF0ZWQpCmRmJGxhc3RfdXBkYXRlZF95ZWFyID0geWVhcihkZiRsYXN0X3VwZGF0ZWQpCmRmJHJlbGVhc2VfeWVhciA9IHllYXIoZGYkcmVsZWFzZWQpCgojcmVtb3ZlIGFscGhhYmV0IGFmdGVyIG51bWJlciBpbiBzaXplCmRmJHNpemUgPSBzdWIoIihcXGQpW14wLTldKyQiLCAiXFwxIiwgZGYkc2l6ZSkKI3JlbW92ZSAnYW5kIGFib3ZlJyBpbiBtaW5pbXVtIGFuZHJpb2QKZGYkbWluaW11bV9hbmRyb2lkID0gc3ViKCIoXFxkKVteMC05XSskIiwgIlxcMSIsIGRmJG1pbmltdW1fYW5kcm9pZCkKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRX0Kc3VtbWFyeShkZiRyZWxlYXNlZCkKc3VtbWFyeShkZiRsYXN0X3VwZGF0ZWQpCmBgYAoKVGhlIGRhdGFzZXQgY29udGFpbnM6CgoqIGFwcHMgdGhhdCB3ZXJlIHJlbGVhc2VkIGZyb20gMjAxMCB0byAyMDIwCiAgKyBlYXJsaWVzdDogMjAxMC0wMS0yOCwgbGF0ZXN0OiAyMDIwLTEyLTAzCiAgKyA3NzMwIG9icyB3aXRob3V0IHJlbGVhc2UgeWVhcgoqIGFwcHMgdGhhdCB3ZXJlIGxhc3QgdXBkYXRlZCBpbiAyMDA4IHRvIDIwMjAKICArIGVhcmxpZXN0OiAyMDA4LTEwLTIyLCBsYXRlc3Q6IDIwMjAtMTItMDMKICArIHdlIGNhbiBhc3N1bWUgdGhhdCBnYW1lcyB0aGF0IGRpZCBub3QgaGF2ZSBhbiB1cGRhdGUgaW4gdGhlIHllYXIgMjAyMCAKKiB3aGlsZSB0aGUgZWFybGllc3QgcmVsZWFzZSB5ZWFyIGlzIDIwMTAsIHRoZSBlYXJsaWVzdCB1cGRhdGUgeWVhciBpcyAyMDA4CgpgYGB7cn0KbGVuZ3RoKHVuaXF1ZShkZiRhcHBfaWQpKSAKbGVuZ3RoKHVuaXF1ZShkZiRhcHBfbmFtZSkpIApsZW5ndGgodW5pcXVlKGRmJGFwcF9pZCkpIC0gbGVuZ3RoKHVuaXF1ZShkZiRhcHBfbmFtZSkpIApsZW5ndGgodW5pcXVlKGRmJGRldmVsb3Blcl9pZCkpIApgYGAKKiAxMTE4MTM2IHVuaXF1ZSBJRHMsIHRoZXJlIGFyZSBubyBkdXBsaWNhdGVzIGluIHRoZSBkYXRhc2V0CiogMTA0Njg3MCB1bmlxdWUgYXBwIG5hbWVzLCB0aGVyZSBhcmUgc29tZSBhcHBzIHdpdGggdGhlIHNhbWUgbmFtZXMgaW4gdGhlIGRhdGFzZXQKKiAxNjU1OTQgdW5pcXVlIGRldmVsb3BlcnMgCgpgYGB7cn0KZGYkZnJlZSA9IGFzLmludGVnZXIoYXMubG9naWNhbChkZiRmcmVlKSkKZGYkaW5fYXBwX3B1cmNoYXNlcyA9IGFzLmludGVnZXIoYXMubG9naWNhbChkZiRpbl9hcHBfcHVyY2hhc2VzKSkKZGYkYWRfc3VwcG9ydGVkID0gYXMuaW50ZWdlcihhcy5sb2dpY2FsKGRmJGFkX3N1cHBvcnRlZCkpCgpIbWlzYzo6ZGVzY3JpYmUoYXMuZmFjdG9yKGRmJGZyZWUpKQpIbWlzYzo6ZGVzY3JpYmUoYXMuZmFjdG9yKGRmJGluX2FwcF9wdXJjaGFzZXMpKQpIbWlzYzo6ZGVzY3JpYmUoYXMuZmFjdG9yKGRmJGFkX3N1cHBvcnRlZCkpCmBgYAoKIyMjIEF0dHJpYnV0ZSBDb25zdHJ1Y3Rpb24gCgpgYGB7cn0KcHJpbnQodW5pcXVlKGRmJGNhdGVnb3J5KSkKYGBgCgoqKkNhdGVnb3J5KioKVGhlcmUgYXJlIDQ5IGxldmVscyBpbiB0aGUgY2F0ZWdvcnkgZmVhdHVyZSwgaW5jbHVkaW5nIGEgTkEuIFRoZSBsZXZlbHMgYXJlIGdyb3VwZWQgaW50byAoMSkgQXBwcyBvciAoMikgR2FtZXMgYXMgZGVzY3JpYmVkIGJ5IFtHb29nbGUgUGxheSBDb25zb2xlIEhlbHBdKGh0dHBzOi8vc3VwcG9ydC5nb29nbGUuY29tL2dvb2dsZXBsYXkvYW5kcm9pZC1kZXZlbG9wZXIvYW5zd2VyLzk4NTk2NzM/aGw9ZW4pIGluIHRoZSBmb2xsb3dpbmcgc2VjdGlvbi4gCgpgYGB7cn0KIzE3IGdhbWUgZ2VucmVzCmdhbWVnZW5yZXM9IGMoIkFjdGlvbiIsIkFkdmVudHVyZSIsIkFyY2FkZSIsIkJvYXJkIiwiQ2FzaW5vIiwiQ2FzdWFsIiwiRWR1Y2F0aW9uYWwiLCJNdXNpYyIsIlB1enpsZSIsIlJhY2luZyIsIlJvbGUgUGxheWluZyIsIlNpbXVsYXRpb24iLCJTcG9ydHMiLCJTdHJhdGVneSIsIlRyaXZpYSIsIldvcmQiKQpkZiA9IGRmICU+JSBtdXRhdGUoaXNfZ2FtZSA9IGlmX2Vsc2UoY2F0ZWdvcnkgJWluJSBnYW1lZ2VucmVzLCIxIiwiMCIpKQpIbWlzYzo6ZGVzY3JpYmUoZGYkaXNfZ2FtZSkKYGBgCgoqIDE1LjMlIG9mIHRoZSBvYnMgaW4gdGhlIGRhdGFzZXQgYXJlIGdhbWVzIGFuZCA4NC43JSBhcmUgYXBwcy4gIAoKYGBge3J9CiNwcmljZSBtb2RlbApkZiA9IGRmICU+JSBtdXRhdGUocHJpY2VfbW9kZWwgPSBjYXNlX3doZW4oCgkoZnJlZT09MSAmIGluX2FwcF9wdXJjaGFzZXMgPT0gMSkgfiAiZnJlZW1pdW0iLAoJKGZyZWU9PTEgJiBpbl9hcHBfcHVyY2hhc2VzID09IDApIH4gImZyZWUiLAoJKGZyZWU9PTAgJiBpbl9hcHBfcHVyY2hhc2VzID09IDEpIH4gInBheW1pdW0iLAoJKGZyZWU9PTAgJiBpbl9hcHBfcHVyY2hhc2VzID09IDApIH4gInBhaWQiLCAKCVRSVUUgfiJvdGhlciIKCSkpCkhtaXNjOjpkZXNjcmliZShkZiRwcmljZV9tb2RlbCkKYGBgCgoqIFByaWNlIG1vZGVsIGF0dHJpYnV0ZSBjb21iaW5lcyBmcmVlIGFuZCBpbl9hcHBfcHVyY2hhc2VzIGZlYXR1cmVzIHRvIHVuZGVyc3RhbmQgdGhlIGRldmVsb3BlcnMgcHJpY2luZyBzdHJhdGVneQoqIE1ham9yaXR5IG9mIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlIGRhdGFzZXQgYXJlIGZyZWUgYXBwbGljYXRpb25zIGFuZCBsZWFzdCBhcmUgcGF5bWl1bS4gCgoKYGBge3J9CnN0cihkZikKYGBgCgoKCmBgYHtyfQojYWdlIG9mIGFwcCAoZGF5cyBiZXR3ZWVuIGNvbGxlY3Rpb25fZGF0ZSBhbmQgcmVsZWFzZWQpIApjb2xsZWN0aW9uX2RhdGUgPSAnMjAyMC0xMi0wMycKZGYkaW50ZXJ2YWwgPSBpbnRlcnZhbCh5bWQoZGYkcmVsZWFzZWQpLHltZChjb2xsZWN0aW9uX2RhdGUpKQpkZiRhZ2U8LSBhcy5wZXJpb2QoZGYkaW50ZXJ2YWwpCmRmJGFnZSA8LSBkZiRhZ2UgJS8lIGRheXMoMSkgI2NvbnZlcnQgdG8gZGF5cwpoZWFkKGRmJGFnZSkKc3VtbWFyeShkZiRhZ2UpCmBgYAoqIG1lZGlhbiBvZiA3NDUgZGF5cyBiZXR3ZWVuIGRhdGEgY29sbGVjdGlvbiBkYXRlIGFuZCBhcHAgcmVsZWFzZSBkYXRlCgpgYGB7cn0KI2FnZSBvZiB1cGRhdGUgKGRheXMgYmV0d2VlbiBjb2xsZWN0aW9uIGRhdGUgYW5kIGxhc3RfdXBkYXRlKQpkZiRpbnRlcnZhbDIgPSBpbnRlcnZhbCh5bWQoZGYkbGFzdF91cGRhdGVkKSx5bWQoY29sbGVjdGlvbl9kYXRlKSkKZGYkYWdlX3VwZGF0ZSA8LSBhcy5wZXJpb2QoZGYkaW50ZXJ2YWwyKQpkZiRhZ2VfdXBkYXRlIDwtIGRmJGFnZV91cGRhdGUgJS8lIGRheXMoMSkgI2NvbnZlcnQgdG8gZGF5cwpoZWFkKGRmJGFnZV91cGRhdGUpCnN1bW1hcnkoZGYkYWdlX3VwZGF0ZSkKYGBgCiogbWVkaWFuIG9mIDI3MCBkYXlzIGJldHdlZW4gZGF0YSBjb2xsZWN0aW9uIGRhdGUgYW5kIGFwcCBsYXN0IHVwZGF0ZSBkYXRlCgojIyMgRGF0YSBFeHBsb3JhdGlvbiAKCiMjIyMgRW50aXJlIGRhdGFzZXQKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojYXBwcyBhbmQgZ2FtZXMgcmVsZWFzZWQgb3ZlciB0aGUgeWVhcnMgCmRmICU+JSBncm91cF9ieShpc19nYW1lLHJlbGVhc2VfeWVhcikgJT4lIHRhbGx5KCkgJT4lIGdncGxvdChhZXMoeD1yZWxlYXNlX3llYXIsIHk9IG4sIGZpbGw9aXNfZ2FtZSkpICsgZ2VvbV9hcmVhKGFscGhhPTAuOSkgKyBzY2FsZV9maWxsX2phbWEoKQpgYGAKCgpgYGB7cn0KI2dhbWVzIHJlbGVhc2UgeWVhciAKZGYgJT4lIGdyb3VwX2J5KHJlbGVhc2VfeWVhcikgJT4lIHRhbGx5KCkgJT4lIG11dGF0ZShwcm9wID0gcm91bmQobi9zdW0obiksMykpIAojZ2FtZXMgdXBkYXRlIHllYXIKZGYgJT4lIGdyb3VwX2J5KGxhc3RfdXBkYXRlZF95ZWFyKSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlKHByb3AgPSByb3VuZChuL3N1bShuKSwzKSkKYGBgCgoqIFJlbGVhc2UgeWVhcjogCiAgKyBtb3JlIHRoYW4gNjAlIG9mIHRoZSBhcHBzIGluIHRoZSBkYXRhc2V0IGFyZSByZWxlYXNlZCBmcm9tIDIwMTgsIDIwMTkgYW5kIDIwMjAuIAogICsgNzczMCBhcHBzIGluIHRoZSBkYXRhc2V0IGhhdmUgbm8gcmVsZWFzZSB5ZWFyIGluZGljYXRlZAoqIExhc3QgdXBkYXRlZCB5ZWFyOiAKICArIDU1LjggJSBvZiB0aGUgYXBwcyBpbiB0aGUgZGF0YXNldCBoYWQgYXQgbGVhc3Qgb25lIHVwZGF0ZSBpbiAyMDIwLCB3ZSB3aWxsIGNvbnNpZGVyIHRoZXNlIGFzIGFjdGl2ZSBhcHBsaWNhdGlvbnMgaW4gICAgIHRoaXMgbm90ZWJvb2suIAoKCmBgYHtyfQojcHJvcG9ydGlvbiBvZiBhZCBzdXBwb3J0ZWQgb3ZlciB5ZWFycyAobGFzdF91cGRhdGVkX3llYXIgMjAxNiB0byAyMDIwKQpkZiAlPiUgZmlsdGVyKGxhc3RfdXBkYXRlZF95ZWFyPjIwMTUpICU+JSBncm91cF9ieShpc19nYW1lLGxhc3RfdXBkYXRlZF95ZWFyLCBhZF9zdXBwb3J0ZWQpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lIGdncGxvdChhZXMoeD1sYXN0X3VwZGF0ZWRfeWVhciwgeT1wcm9wLCBmaWxsPWZhY3RvcihhZF9zdXBwb3J0ZWQpKSkgKyBnZW9tX2NvbCh3aWR0aD0wLjYsYWxwaGE9MC45KSArIGZhY2V0X3dyYXAofmlzX2dhbWUsbGFiZWxsZXI9bGFiZWxfYm90aCkgKyBzY2FsZV9maWxsX3VjaGljYWdvKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsgbGFicyh5PSJwcm9wb3J0aW9uIixmaWxsPSJhZF9zdXBwb3J0ZWQiKQpgYGAKTGFzdCB1cGRhdGVkIGRhdGVzOiAyMDEwLTAxLTI4IHRvIDIwMjAtMTItMDMKKiBUaGUgYWQgc3VwcG9ydCBmZWF0dXJlIGNvdWxkIGhlbHAgaW4gdW5kZXJzdGFuZGluZyB0aGUgZGV2ZWxvcGVycycgcHJpY2luZyBzdHJhdGVneSAKKiBUaGUgYWJvdmUgY2hhcnQgdXNlZCBsYXN0IHVwZGF0ZWQgeWVhciBhcyBhIGNvbXBhcmlzaW9uIGFzIGFkIHN1cHBvcnQgY291bGQgY2hhbmdlIHdpdGggYXBwIHVwZGF0ZXMKKiBBcHBzOiBpbmNyZWFzZWQgdXNlIG9mIGFkcyBmcm9tIDIwMTYgdG8gMjAxOSwgYW5kIGEgc2xpZ2h0IGRlY3JlYXNlIGluIDIwMjAgKEphbiB0byBOb3YpCiogR2FtZXM6IGluY3JlYXNpbmcgcG9ycG9ydGlvbiBvZiBhZCBzdXBwb3J0IGdhbWVzIGZyb20gMjAxNiB0byAyMDIwIAoKYGBge3J9CiNpbnN0YWxscyBsZXZlbHMKbGVuZ3RoKHVuaXF1ZShkZiRpbnN0YWxscykpCnVuaXF1ZShkZiRpbnN0YWxscykKCiNtaW5pbXVtX2luc3RhbGxzIGRpc3RyaWJ1dGlvbgpkZiAlPiUgZmlsdGVyKCFpcy5uYShtaW5pbXVtX2luc3RhbGxzKSkgJT4lIGdyb3VwX2J5KGlzX2dhbWUsIG1pbmltdW1faW5zdGFsbHMpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lIGdncGxvdChhZXMoeD1mYWN0b3IobWluaW11bV9pbnN0YWxscyksIHk9cHJvcCwgZmlsbD0gaXNfZ2FtZSkpICsgZ2VvbV9jb2woKSArIGZhY2V0X3dyYXAofmlzX2dhbWUsbGFiZWxsZXI9bGFiZWxfYm90aCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCkpICsgc2NhbGVfZmlsbF9qYW1hKCkgKyBsYWJzKHk9InByb3BvcnRpb24iLHg9Im1pbmltdW1faW5zdGFsbHMiKQpgYGAKCiogVGhlcmUgYXJlIDIzIGxldmVscyBvZiBpbnN0YWxscyBjYXRlZ29yaWVzIGluY2x1ZGluZyBvbmUgTkEKKiBTaW1pbGFyIGRpc3RyaWJ1dGlvbiBzaGFwZSBvZiBhcHBzIGFuZCBnYW1lcyBpbnN0YWxscyhtaW5pbXVtIGluc3RhbGxzKQoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNyYXRpbyBvZiBhcHAgdG8gZGV2CmRmICU+JSBncm91cF9ieShyZWxlYXNlX3llYXIpICU+JSBzdW1tYXJpc2UoZGV2Y291bnQ9bl9kaXN0aW5jdChkZXZlbG9wZXJfaWQpLCBhcHBjb3VudD1uX2Rpc3RpbmN0KGFwcF9pZCkpICU+JSBtdXRhdGUocmF0aW89IChhcHBjb3VudC9kZXZjb3VudCkpCgojcGxvdCByZWxlYXNlX3llYXIsIHJhdGlvLCBpc19nYW1lCmRmICU+JSBncm91cF9ieShpc19nYW1lLCByZWxlYXNlX3llYXIpICU+JSBzdW1tYXJpc2UoZGV2Y291bnQ9bl9kaXN0aW5jdChkZXZlbG9wZXJfaWQpLCBhcHBjb3VudD1uX2Rpc3RpbmN0KGFwcF9pZCkpICU+JSBtdXRhdGUocmF0aW89IChhcHBjb3VudC9kZXZjb3VudCkpICU+JSBnZ3Bsb3QoYWVzKHg9cmVsZWFzZV95ZWFyLCB5PXJhdGlvLCBmaWxsPWlzX2dhbWUpKSArIGdlb21fYXJlYSgpICsgc2NhbGVfZmlsbF91Y2hpY2FnbygpICsgbGFicyh0aXRsZT0iUmF0aW8gb2YgYXBwcyB0byBkZXZlbG9wZXJzIiApCmBgYAoKKiBUaGUgcmF0aW8gb2YgYXBwcyAoYXBwIGFuZCBnYW1lIGNsYXNzZXMpIHRvIGRldmVsb3BlcnMgaGF2ZSBpbmNyZWFzZWQgb3ZlciB0aGUgeWVhcnMgZnJvbSAyLjA4IGluIDIwMTEgdG8gMy45OCBpbiAyMDE5LgogICsgc3VnZ2VzdGluZyB0aGF0IGRldmVsb3BlcnMgYXJlIGluY3JlYXNpbmdseSByZWxlYXNpbmcgbW9yZSBhcHBzIG9uIHRoZSBwbGF5IHN0b3JlIHBlciB5ZWFyCiogQ29tcGFyaXNvbiBvZiBhcHAgdG8gZGV2ZWxvcGVyIHJhdGlvIGJldHdlZW4gYXBwcyBhbmQgZ2FtZXMgY2xhc3NlcwogICsgZ2FtZSBkZXZlbG9wZXJzIHJlbGVhc2UgbW9yZSBnYW1lcyBhIHllYXIgYXMgY29tcGFyZWQgdG8gYXBwIGRldmVsb3BlcnMKICArIHRoZSBncm93dGggaW4gcmF0aW8gaXMgbW9yZSBzaWduaWZpY2FudCBvdmVyIHRoZSB5ZWFycyBpbiB0aGUgZ2FtZXMgY2xhc3MgY29tcGFyZWQgdG8gYXBwcyBjbGFzcwoKCgoKIyMjIyBTdWJzZXQKVGhlIGZvbGxvd2luZyBzdWJzZXQgaXMgY3JlYXRlZCB0byB0cnkgcmVkdWNlIGJpYXNuZXNzIGZvciBmdXJ0aGVyIGV4cGxvcmF0aW9uLCB3aXRoIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczogCiAgKyBhcmUgYWN0aXZlIChoYXZlIGF0IGxlYXN0IG9uZSB1cGRhdGUgaW4gMjAyMCkgCiAgKyBoYXZlIGEgcmVsZWFzZSB5ZWFyIAogICsgaGF2ZSBhIHJhdGluZyBzY29yZQogICsgbWluaW11bSBpbnN0YWxscyBlcXVhbCBvciBhYm92ZSA1MDAwCiAgKyByYXRpbmcgY291bnQgYWJvdmUgNTAwCiAgCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojc3Vic2V0IAojbGFzdCB1cGRhdGVkIGluIDIwMjAsIGEgcmF0aW5nIHNjb3JlLCByYXRpbmcgY291bnQgPjUwMCwgbWluaW11bV9pbnN0YWxscyA9PjUwMDAKYWN0aXZlID0gZGYgJT4lIGZpbHRlciAobGFzdF91cGRhdGVkX3llYXI9PSAyMDIwKSAlPiUgZmlsdGVyKHJhdGluZz4wKSAlPiUgZmlsdGVyKHJhdGluZ19jb3VudD41MDApICU+JSBmaWx0ZXIobWluaW11bV9pbnN0YWxscz4xMDAwKQoKI3JlbGVhc2UgeWVhcgphY3RpdmUgJT4lIGdyb3VwX2J5KHJlbGVhc2VfeWVhcikgJT4lIHRhbGx5KCkgCiNkcm9wIG9icyB3aXRoIG5vIHJlbGVhc2UgeWVhciAKYWN0aXZlID0gYWN0aXZlICU+JSBmaWx0ZXIgKCFpcy5uYShyZWxlYXNlX3llYXIpKSAKCmFjdGl2ZSRzaXplID0gYXMubnVtZXJpYyhhY3RpdmUkc2l6ZSkKYWN0aXZlJG1pbmltdW1fYW5kcm9pZCA9IGFzLm51bWVyaWMoYWN0aXZlJG1pbmltdW1fYW5kcm9pZCkKYWN0aXZlID0gYWN0aXZlICU+JSBmaWx0ZXIoIWlzLm5hKHNpemUpKSAlPiUgZmlsdGVyKCFpcy5uYShtaW5pbXVtX2FuZHJvaWQpKSAKZGltKGFjdGl2ZSkKYGBgCgoqIDgwNjU3IG91dCBvZiAxMTE4MTM2IG9ic2VydmF0aW9ucyBtZXQgdGhlIGFib3ZlIG9ic2VydmF0aW9ucy4gCgoKYGBge3J9CiNwbG90IHJlbGVhc2UgeWVhciBvZiBhY3RpdmUgYXBwcwphY3RpdmUgJT4lIGdyb3VwX2J5KHJlbGVhc2VfeWVhcikgJT4lIHRhbGx5KCkgJT4lIGdncGxvdChhZXMoeD1hcy5mYWN0b3IocmVsZWFzZV95ZWFyKSwgeT1uLCBmaWxsPW4pKSArIGdlb21fY29sKHdpZHRoPTAuOCkgKyBsYWJzKHk9IiIsIng9cmVsZWFzZV95ZWFyIikgKyBnZW9tX3RleHQoYWVzKGxhYmVsPW4pLCB2anVzdCA9IC0wLjMsIHNpemUgPSAzLjUpICsgc2NhbGVfZmlsbF92aXJpZGlzKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKQpgYGAKCiogQXMgZXhwZWN0ZWQsIG1ham9yaXR5IG9mIHRoZSBhY3RpdmUgYXBwcyBhcmUgcmVsZWFzZWQgaW4gdGhlIHJlY2VudCB5ZWFycwoqIFRoZSBvbGRlc3QgYWN0aXZlIGFwcHMgYXJlIHJlbGVhc2VkIGluIDIwMTAgYW5kIHRoZXJlIGFyZSA4NTUgb2JzZXJ2YXRpb25zLiAKKiBUaGVyZSBhcmUgbW9yZSBhcHBzIGZyb20gMjAxOSB0aGFuIDIwMjAgaW4gdGhlIHN1YnNldCwgdGhpcyBjb3VsZCBiZSBhdHRyaWJ1dGVkIHRvIGNvbmRpdGlvbnMgb2YgdGhlIHN1YnNldCBhcyBuZXdlciBhcHBzIHJlbGVhc2VkIGluIHRoZSByZWNlbnQgbW9udGhzIGNvdWxkIGhhdmUgbG93ZXIgaW5zdGFsbCBjb3VudCBhbmQgcmF0aW5nIGNvdW50LiAKCmBgYHtyfQojY29tcGFyaW5nIGdhbWVzIGFuZCBhcHBzIHJhdGluZyBkaXN0cmlidXRpb24gCmFjdGl2ZSAlPiUgZ2dwbG90KGFlcyh4PXJhdGluZywgZmlsbD1pc19nYW1lKSkgKyBnZW9tX2JhcigpICsgZmFjZXRfd3JhcCh+aXNfZ2FtZSwgbGFiZWxsZXI9bGFiZWxfYm90aCkgKyBzY2FsZV9maWxsX2phbWEoKQpgYGAKCiogVGhlcmUgaXMgYSBzaW1pbGFyIGRpc3RyaWJ1dGlvbiBzaGFwZSBvZiByYXRpbmcgc2NvcmUgYmV0d2VlbiBnYW1lcyBhbmQgYXBwcywgYm90aCBhcmUgcG9zaXRpdmVseSBza2V3ZWQuIAoKYGBge3J9CiNwcm9wb3J0aW9uIG9mIHJhdGluZyBzY29yZSBiZXR3ZWVuIGZyZWUgYW5kIHBhaWQgYXBwcwojYXBwcwpyZjAgPSBhY3RpdmUgJT4lIGZpbHRlcihpc19nYW1lPT0wKSAlPiUgZ3JvdXBfYnkoZnJlZSwgcmF0aW5nKSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlKHByb3A9bi9zdW0obikpICU+JSBnZ3Bsb3QoYWVzKHg9cmF0aW5nLHk9cHJvcCwgZmlsbD1mYWN0b3IoZnJlZSkpKSArIGdlb21fY29sKCkgKyBmYWNldF93cmFwKH5mcmVlLGxhYmVsbGVyPWxhYmVsX2JvdGgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjZGRhMTVlIiwgIiNiYzZjMjUiKSkgKyBsYWJzKHk9InByb3BvcnRpb24iLCB0aXRsZT0iQXBwcyIpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsMC4xMykpCiNnYW1lcwpyZjEgPSBhY3RpdmUgJT4lIGZpbHRlcihpc19nYW1lPT0xKSAlPiUgZ3JvdXBfYnkoZnJlZSwgcmF0aW5nKSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlKHByb3A9bi9zdW0obikpICU+JSBnZ3Bsb3QoYWVzKHg9cmF0aW5nLHk9cHJvcCwgZmlsbD1mYWN0b3IoZnJlZSkpKSArIGdlb21fY29sKCkgKyBmYWNldF93cmFwKH5mcmVlLGxhYmVsbGVyPWxhYmVsX2JvdGgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyAKc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzYwNmMzOCIsICIjMjgzNjE4IikpICsgbGFicyh5PSJwcm9wb3J0aW9uIiwgdGl0bGU9IkdhbWVzIikgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMCwwLjEzKSkKI3Bsb3QKZ2dhcnJhbmdlKHJmMCxyZjEsbmNvbD0xKQpgYGAKCiogVGhlIGRpc3RyaWJ1dGlvbiBvZiByYXRpbmcgc2NvcmUgYmV0d2VlbiBmcmVlIGdhbWVzIGFuZCBmcmVlIGFwcHMgYXJlIHZlcnkgc2ltaWxhci4KKiBGcmVlIGFwcHMgaGF2ZSBhIGxhcmdlciBwcm9wb3J0aW9uIG9mIHJhdGluZ3MgMiBhbmQgYmVsb3csIGNvbXBhcmVkIHRvIHBhaWQgYXBwcy4KKiBGcmVlIGdhbWVzIGhhdmUgYSBsYXJnZXIgcHJvcG9ydGlvbiBvZiByYXRpbmdzIDIuNSBhbmQgYmVsb3cgY29tcGFyZWQgdG8gcGFpZCBnYW1lcy4gCiogSW4gYm90aCBhcHBzIGFuZCBnYW1lcywgZnJlZSBjbGFzcyBoYXMgYSBsb3dlciBwcm9wb3J0aW9uIG9mIHJhdGluZ3MgNC41IGFuZCBhYm92ZSwgY29tcGFyZWQgdG8gcGFpZCBjbGFzcy4gCiogUGFpZCBhcHBzIGhhdmUgYSBsYXJnZXIgcHJvcG9ydGlvbiBvZiByYXRpbmdzIDQuNSBhbmQgYWJvdmUgY29tcGFyZWQgdG8gcGFpZCBnYW1lcy4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojbWF4aW11bV9pbnN0YWxscywgcmF0aW5nX2NvdW50LCByYXRpbmcKcGFpcl92YXJzID0gYWN0aXZlICU+JSBzZWxlY3QobWF4aW11bV9pbnN0YWxscywgcmF0aW5nX2NvdW50LCByYXRpbmcsIGFnZSwgYWdlX3VwZGF0ZSkKZ2dwYWlycyhwYWlyX3ZhcnMpCmBgYAoKKiBBcyBleHBlY3RlZCB0aGVyZSBpcyBhIHBvc2l0aXZlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuCiAgKyBtYXhpbXVtX2luc3RhbGxzIGFuZCByYXRpbmdfY291bnQKICArIHJhdGluZyhzY29yZSkgYW5kIG1heGltdW1faW5zdGFsbHMKICArIHJhdGluZyhzY29yZSkgYW5kIHJhdGluZ19jb3VudCAKKiBBZ2Ugb2YgYXBwIGhhcyBhIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIHdpdGggcmF0aW5nLCBwb3NpdGl2ZSB3aXRoIHJhdGluZyBjb3VudCBhbmQgbWF4aW11bV9pbnN0YWxscwoqIEFnZSBvZiBhcHAgdXBkYXRlcyBuZWdhdGl2ZSBjb3JyZWxhdGlvbiB3aXRoIG1heGltdW0gaW5zdGFsbHMsIHJhdGluZ19jb3VudCBhbmQgcmF0aW5nCiogVGhlIGRpc3RyaWJ1dGlvbiBvZiByYXRpbmdzIGluIHRoZSBzdWJzZXQgaXMgbmVnYXRpdmVseSBza2V3ZWQsIHdpdGggbWFqb3JpdHkgb2YgdGhlIG9ic2VydmF0aW9ucyBoYXZpbmcgcmF0aW5nKHNjb3JlKSBvZiA0LjAgICAgYW5kIGFib3ZlLiAKCmBgYHtyfQojY2F0ZWdvcmllcwpjMCA9IGFjdGl2ZSAlPiUgZmlsdGVyKGlzX2dhbWU9PTApICU+JSBncm91cF9ieShjYXRlZ29yeSkgJT4lIHRhbGx5KCkgJT4lIGdncGxvdChhZXMoeD1yZW9yZGVyKGNhdGVnb3J5LG4pLCB5PW4pKSArIGdlb21fY29sKGZpbGw9IiM1Mjc5NmYiKSArIGNvb3JkX2ZsaXAoKSArIGxhYnMoeT0iY2F0ZWdvcnkiLHg9IiIpCmMxID0gYWN0aXZlICU+JSBmaWx0ZXIoaXNfZ2FtZT09MSkgJT4lIGdyb3VwX2J5KGNhdGVnb3J5KSAlPiUgdGFsbHkoKSAlPiUgZ2dwbG90KGFlcyh4PXJlb3JkZXIoY2F0ZWdvcnksbiksIHk9bikpICsgZ2VvbV9jb2woZmlsbD0iIzQ5NTA1NyIpICsgY29vcmRfZmxpcCgpICsgbGFicyh5PSJjYXRlZ29yeSIseD0iIikKZ2dhcnJhbmdlKGMwLGMxKQpgYGAKCiogQXBwczogVG9vbHMgYXJlIHRoZSBtb3N0IGZyZXF1ZW50IGNhdGVnb3J5IHdoaWxlIGV2ZW50cyBhcmUgdGhlIGxlYXN0IGZyZXF1ZW50CiogR2FtZXM6IFNpbXVsYXRpb24gZ2VucmUgaXMgdGhlIG1vc3QgZnJlcXVlbnQgY2F0ZWdvcnkgd2hpbGUgbXVzaWMgaXMgdGhlIGxlYXN0IGZyZXF1ZW50CgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KI2F2ZyByYXRpbmc6IGFwcAphY3RpdmUgJT4lIGZpbHRlcihpc19nYW1lPT0wKSAlPiUgZ3JvdXBfYnkoY2F0ZWdvcnkpICU+JSBzdW1tYXJpc2UoYXZnPSBtZWFuKHJhdGluZykpICU+JSBhcnJhbmdlKGRlc2MoYXZnKSkgJT4lIGFzLmRhdGEuZnJhbWUoKQojYXZnIHJhdGluZzogZ2FtZQphY3RpdmUgJT4lIGZpbHRlcihpc19nYW1lPT0xKSAlPiUgZ3JvdXBfYnkoY2F0ZWdvcnkpICU+JSBzdW1tYXJpc2UoYXZnPSBtZWFuKHJhdGluZykpICU+JSBhcnJhbmdlKGRlc2MoYXZnKSkKYGBgCgpBdmVyYWdlIGFwcCByYXRpbmdzIGZvciBjYXRlZ29yaWVzOiAgCgoqIEluIGFwcHM6ICpCb29rcyAmIFJlZmVyZW5jZSogaGF2ZSB0aGUgaGlnaGVzdCBhdmVyYWdlIHJhdGluZyBzY29yZSwgd2hpbGUgKkRhdGluZyogaGF2ZSB0aGUgbG93ZXN0LgoqIEluIGdhbWVzOiAqQ2FzaW5vKiBhbmQgKldvcmQqIGdlbnJlIGhhdmUgdGhlIGhpZ2hlc3QgYXZlcmFnZSByYXRpbmcgc2NvcmUsIHdoaWxlICpTaW11bGF0aW9uKiBnZW5yZSBoYXZlIHRoZSBsb3dlc3QuCiAgKyBpbnRlcmVzdGluZ2x5LCBzaW11bGF0aW9uIGdhbWVzIGFyZSB0aGUgbW9zdCBjb21tb24gZ2FtZSBhcHBzIChzaG93biBpbiB0aGUgcHJldmlvdXMgc2VjdGlvbikKCmBgYHtyfQojYWN0aXZlIGluc3RhbGxzIGxldmVscwphY3RpdmUgJT4lIGdyb3VwX2J5KGlzX2dhbWUsIG1pbmltdW1faW5zdGFsbHMpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lIGdncGxvdChhZXMoeD1mYWN0b3IobWluaW11bV9pbnN0YWxscyksIHk9cHJvcCwgZmlsbD0gaXNfZ2FtZSkpICsgZ2VvbV9jb2woYWxwaGE9MC45KSArIGZhY2V0X3dyYXAofmlzX2dhbWUpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKSArIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT05MCkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPXdlc19wYWxldHRlKCJNb29ucmlzZTIiKSkgKyBsYWJzKHg9Im1pbmltdW1faW5zdGFsbHMiLHk9InByb3BvcnRpb24iLHRpdGxlPSJJbnN0YWxsIGxldmVscyIpCmBgYAoKKiBXaGVuIGNvbXBhcmluZyBpbnN0YWxsIGxldmVscyBiZXR3ZWVuIGFwcHMgYW5kIGdhbWVzLCAKICArIGFwcHMgaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIGluIDEwMCwwMDAgaW5zdGFsbHMgYW5kIGJlbG93LCBjb21wYXJlZCB0byBnYW1lcyAKICArIGFwcHMgaGF2ZSBhIGxvd2VyIHByb3BvcnRpb24gaW4gbGV2ZWxzIDEgbWlsbGlvbiBpbnN0YWxscyBhbmQgYWJvdmUsIGNvbXBhcmVkIHRvIGdhbWVzIAoKCgojIyMjIE1vc3QgZnJlcXVlbnQgY2F0ZWdvcmllcyBpbiBhcHBzIGFuZCBnYW1lcwoKYGBge3J9CiNjb21wYXJpbmcgY2F0ZWdvcmllcyBpbiBzdWJzZXQgYW5kIGNvbXBsZXRlIGRhdGFzZXQKI2FwcHMKZGYgJT4lIGZpbHRlcihpc19nYW1lPT0wKSAlPiUgZ3JvdXBfYnkoY2F0ZWdvcnkpICU+JSB0YWxseShzb3J0PVQpCmFjdGl2ZSAlPiUgZmlsdGVyKGlzX2dhbWU9PTApICU+JSBncm91cF9ieShjYXRlZ29yeSkgJT4lIHRhbGx5KHNvcnQ9VCkKI2dhbWVzCmRmICU+JSBmaWx0ZXIoaXNfZ2FtZT09MSkgJT4lIGdyb3VwX2J5KGNhdGVnb3J5KSAlPiUgdGFsbHkoc29ydD1UKQphY3RpdmUgJT4lIGZpbHRlcihpc19nYW1lPT0xKSAlPiUgZ3JvdXBfYnkoY2F0ZWdvcnkpICU+JSB0YWxseShzb3J0PVQpCmBgYAoKKiBJbiBjb21wYXJpbmcgc3Vic2V0KGFjdGl2ZSBhcHBzKSBhbmQgZW50aXJlIGRhdGFzZXQsIHRoZXJlIGFyZSBzb21lIHNpbWlsYXJpdGllcyBpbiB0aGUgbW9zdCBmcmVxdWVudCBjYXRlZ29yaWVzIGluIGJvdGggZ2FtZXMgYW5kIGFwcHMuIAoqIFRoZSBmb2xsb3dpbmcgZXhwbG9yYXRpb24gd2lsbCB1c2UgdGhlIHRvcCA1IGNhdGVnb3JpZXMgaW4gdGhlIHN1YnNldCBhcyBpdCBjb250YWlucyAoYWN0aXZlKSBhcHBzIHRoYXQgaGF2ZSBhdCBsZWFzdCBhbiB1cGRhdGUgaW4gMjAyMC4gCiAgKyBBcHBzOiB0b29scywgZW50ZXJ0YWlubWVudCwgZWR1Y2F0aW9uLCBwZXJzb25hbGl6YXRpb24sIG11c2ljICYgYXVkaW8KICArIEdhbWVzOiBzaW11bGF0aW9uLCBwdXp6bGUsIGNhc3VhbCwgc3BvcnRzLCBhY3Rpb24sIAogIAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CiNwcm9wb3J0aW9uIG9mIHRvcCA1IG1vc3QgZnJlcXVlbnQgY2F0ZWdvcmllcyBhY3Jvc3MgdGhlIHllYXJzIAojYXBwcwp0b3BfY2F0cyA9IGMoIlRvb2xzIiwiRW50ZXJ0YWlubWVudCIsIkVkdWNhdGlvbiIsIlBlcnNvbmFsaXphdGlvbiIsIk11c2ljICYgQXVkaW8iKQpkZiAlPiUgZmlsdGVyKGxhc3RfdXBkYXRlZF95ZWFyPT0yMDIwKSAlPiUgZ3JvdXBfYnkocmVsZWFzZV95ZWFyLCBjYXRlZ29yeSkgJT4lIHRhbGx5KCkgJT4lIG11dGF0ZShwcm9wPW4vc3VtKG4pKSAlPiUgZmlsdGVyKGNhdGVnb3J5ICVpbiUgdG9wX2NhdHMpJT4lIGdncGxvdChhZXMoeD1yZWxlYXNlX3llYXIsIHk9cHJvcCwgZmlsbD1jYXRlZ29yeSkpICsgZ2VvbV9jb2woYWxwaGE9MC45KSArIHNjYWxlX2ZpbGxfamFtYSgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJsZWZ0IikgKyBsYWJzKHk9InByb3BvcnRpb24iLCB4PSIiLCBmaWxsPSJBcHBzIikKI2dhbWVzCnRvcF9nZW5yZXMgPSBjKCJTaW11bGF0aW9uIiwiUHV6emxlIiwiQ2FzdWFsIiwiU3BvcnRzIiwiQWN0aW9uIikKZGYgJT4lIGZpbHRlcihsYXN0X3VwZGF0ZWRfeWVhcj09MjAyMCkgJT4lIGZpbHRlcihpc19nYW1lPT0xKSAlPiUgZ3JvdXBfYnkocmVsZWFzZV95ZWFyLCBjYXRlZ29yeSkgJT4lIHRhbGx5KCkgJT4lIG11dGF0ZShwcm9wPW4vc3VtKG4pKSAlPiUgZmlsdGVyKGNhdGVnb3J5ICVpbiUgdG9wX2dlbnJlcykgJT4lIGdncGxvdChhZXMoeD1yZWxlYXNlX3llYXIsIHk9cHJvcCwgZmlsbD1jYXRlZ29yeSkpICsgZ2VvbV9jb2woYWxwaGE9MC45KSArIHNjYWxlX2ZpbGxfamNvKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImxlZnQiKSArIGxhYnMoeT0icHJvcG9ydGlvbiIsIHg9IiIsIGZpbGw9IkdhbWVzIikKYGBgCgpMb29raW5nIGludG8gdGhlIHByb3BvcnRpb24gb2YgbW9zdCBmcmVxdWVudCBjYXRlZ29yaWVzIGluIGFjdGl2ZSBhcHBzIChsYXN0IHVwZGF0ZWQgaW4gMjAyMCkgdG8gZXhwbG9yZSB0aGUgc2F0dXJhdGlvbiBvZiBuZXcgcmVsZWFzZXM6CgoqIEFwcHM6CiAgKyBUaGUgaWRlbnRpZmllZCA1IGNhdGVnb3JpZXMgam9pbnRseSBoYXZlIGluY3JlYXNlZCBpbiBwcm9wb3J0aW9uIHRvIG90aGVyIGNhdGVnb3JpZXMgb3ZlciB0aGUgeWVhcnMuIAogICsgTXVzaWMgJiBBdWRpbyBjYXRlZ29yeSBoYXZlIGluY3JlYXNlZCBpbiB0aGUgcGFzdCB0d28geWVhcnMgKDIwMTkgYW5kIDIwMjApIHdoaWxlIFBlcnNvbmFsaXphdGlvbiBoYXZlIGRlY3JlYXNlZCBpbiB0aGUgc2FtZSAgICAgdGltZSBwZXJpb2QuIAoqIEdhbWVzOgogICsgVGhlIGlkZW50aWZpZWQgNSBnZW5yZXMgam9pbnRseSBtYWtlIHVwIG1vcmUgdGhhbiA1MCUgb2YgdGhlIGdhbWVzIGFjcm9zcyB0aGUgeWVhcnMuCiAgKyBQcm9wb3J0aW9uIG9mIFNwb3J0cyBnYW1lcyBoYXZlICBkZWNyZWFzZWQgb3ZlciB0aGUgeWVhcnMKICArIEJldHdlZW4gdGhlIGlkZW50aWZpZWQgNSBnZW5yZXMsIEFjdGlvbiBnYW1lcyBtYWtlIHVwIHRoZSBsb3dlc3QgcHJvcG9ydGlvbiBmcm9tIDIwMTUgdG8gMjAyMC4gCgoKCgojIyMjIFByaWNpbmcgc3RyYXRlZ2llcyAKCmBgYHtyfQojcHJpY2UgbW9kZWwgYnkgYXBwIHR5cGUKcDEgPSBhY3RpdmUgJT4lIGdyb3VwX2J5KGlzX2dhbWUscHJpY2VfbW9kZWwpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lIGdncGxvdChhZXMoeD1pc19nYW1lLCB5PXByb3AsIGZpbGw9cHJpY2VfbW9kZWwpKSArIGdlb21fY29sKHdpZHRoPTAuNSkgKyBjb29yZF9mbGlwKCkgKyBzY2FsZV9maWxsX2phbWEoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0icmlnaHQiKQoKI2FkIHN1cHBvcnRlZCBieSBhcHAgdHlwZQpwMiA9IGFjdGl2ZSAlPiUgZ3JvdXBfYnkoaXNfZ2FtZSxhZF9zdXBwb3J0ZWQpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lIGdncGxvdChhZXMoeD1pc19nYW1lLCB5PXByb3AsIGZpbGw9ZmFjdG9yKGFkX3N1cHBvcnRlZCkpKSArIGdlb21fY29sKHdpZHRoPTAuNSkgKyBjb29yZF9mbGlwKCkgKyBzY2FsZV9maWxsX3VjaGljYWdvKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikKCmdnYXJyYW5nZShwMSxwMiwgbnJvdz0yKQpgYGAKKiBQcmljZSBtb2RlbDoKICArIGZyZWVtaXVtIGlzIHRoZSBtb3N0IHBvcHVsYXIgcHJpY2UgbW9kZWwgYW1vbmcgZ2FtZSBkZXZlbG9wZXIsIHdoaWxlIGZyZWUgaXMgbW9zdCBwb3B1bGFyIGFtb25nIGFwcCBkZXZlbG9wZXJzCiAgKyB0aGVyZSBpcyBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIHBhaWQgYW5kIHBheW1pdW0gcHJpY2UgbW9kZWwgaW4gZ2FtZSB3aGVuIGNvbXBhcmVkIHRvIGFwcHMKKiBBZGQgc3VwcG9ydGVkOgogICsgZ2FtZXMgaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIGFkIHN1cHBvcnRlZCB0aGFuIGFwcHMKCgpgYGB7cn0KI3ByaWNlIG1vZGVsIGFuZCBhZCBzdXBwb3J0ZWQgCmFjdGl2ZSAlPiUgZ3JvdXBfYnkoaXNfZ2FtZSwgcHJpY2VfbW9kZWwsIGFkX3N1cHBvcnRlZCkgJT4lIHRhbGx5KCkgJT4lIG11dGF0ZShwcm9wID0gbi9zdW0obikpICU+JSBnZ3Bsb3QoYWVzKHg9cHJpY2VfbW9kZWwsIHk9cHJvcCwgZmlsbD1mYWN0b3IoYWRfc3VwcG9ydGVkKSkpICsgZ2VvbV9jb2wod2lkdGg9MC42LGFscGhhPTAuOSkgKyBjb29yZF9mbGlwKCkgKyBmYWNldF93cmFwKH5pc19nYW1lLCBsYWJlbGxlcj1sYWJlbF9ib3RoKSArIHNjYWxlX2ZpbGxfdWNoaWNhZ28oKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKyBsYWJzKHk9InByb3BvcnRpb24iLGZpbGw9ImFkX3N1cHBvcnRlZCIpCmBgYAoKKiBMb29raW5nIGF0IGJvdGggcHJpY2UgbW9kZWwgYW5kIGFkIHN1cHBvcnRlZDoKICArIGFjcm9zcyBib3RoIGdhbWVzIGFuZCBhcHBzLCBhcyBleHBlY3RlZCBmcmVlIHRvIGRvd25sb2FkIG1vZGVsIChmcmVlIGFuZCBmcmVlbWl1bSkgaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIGFkIHN1cHBvcnRlZCAgICAgICAgdGhhbiBwYWlkIGFuZCBwYXltaXVtIG1vZGVscwogICsgZnJlZSB0byBkb3dubG9hZCBnYW1lcyBoYXZlIGEgaGlnaGVyIHByb3BvcnRpb24gb2YgYWQgc3VwcG9ydCB0aGFuIGZyZWUgdG8gZG93bmxvYWQgYXBwcyAKICArIHBheW1pdW0gYXBwcyBoYXZlIGEgaGlnaGVyIHByb3BvcnRpb24gb2YgYWQgc3VwcG9ydGVkIHRoYW4gcGF5bWl1bSBnYW1lcyAKICArIGZyZWVtaXVtIGdhbWVzIGhhdmUgdGhlIG1vc3QgYWQgc3VwcG9ydCB3aGlsZSBwYWlkIGdhbWVzIGhhdmUgdGhlIGxlYXN0IGFkIHN1cHBvcnQKIAoKYGBge3J9CiNtb3N0IGZyZXF1ZW50IGNhdGVnb3JpZXMgYW5kIHByaWNlX21vZGVsCmFjdGl2ZSAlPiUgZmlsdGVyIChpc19nYW1lPT0wKSAlPiUgZmlsdGVyKGNhdGVnb3J5ICVpbiUgdG9wX2NhdHMpICU+JSBncm91cF9ieShjYXRlZ29yeSwgcHJpY2VfbW9kZWwpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lIGdncGxvdChhZXMoeD1jYXRlZ29yeSwgeT1wcm9wLCBmaWxsPXByaWNlX21vZGVsKSkgKyBnZW9tX2NvbChhbHBoYT0wLjksIHdpZHRoPTAuNykgKyBzY2FsZV9maWxsX2phbWEoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKyBsYWJzKHk9InByb3BvcnRpb24iLCB4PSJhcHAgY2F0ZWdvcnkiKSArIGNvb3JkX2ZsaXAoKQoKYWN0aXZlICU+JSBmaWx0ZXIgKGlzX2dhbWU9PTEpICU+JSBmaWx0ZXIoY2F0ZWdvcnkgJWluJSB0b3BfZ2VucmVzKSAlPiUgZ3JvdXBfYnkoY2F0ZWdvcnksIHByaWNlX21vZGVsKSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlKHByb3A9bi9zdW0obikpICU+JSBnZ3Bsb3QoYWVzKHg9Y2F0ZWdvcnksIHk9cHJvcCwgZmlsbD1wcmljZV9tb2RlbCkpICsgZ2VvbV9jb2woYWxwaGE9MC45LCB3aWR0aD0wLjcpICsgc2NhbGVfZmlsbF9qYW1hKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsgbGFicyh5PSJwcm9wb3J0aW9uIiwgeD0iZ2FtZSBjYXRlZ29yeSIpICsgY29vcmRfZmxpcCgpCmBgYAoKTG9va2luZyBhdCBkZXZlbG9wZXJzJyBwcmljaW5nIChtb2RlbCkgc3RyYXRlZ3kgaW4gdGhlIGlkZW50aWZpZWQgNSBjYXRlZ29yaWVzOgoKKiBBcHBzOiAKICArIHBlcnNvbmFsaXphdGlvbiBoYXMgdGhlIG1vc3QgcGF5bWl1bSBtb2RlbCBjb21wYXJlZCB0byBvdGhlciBjYXRlZ29yaWVzCiAgKyB0b29scyBoYXZlIHBhaWQgbW9kZWwgY29tcGFyZWQgdG8gb3RoZXIgY2F0ZWdvcmllcwoqIEdhbWVzOgogICsgYWN0aW9uIGdlbnJlIGhhdmUgdGhlIG1vc3QgcGF5bWl1bSBwcmljZSBtb2RlbCAKICArIHNwb3J0cyBnZW5yZSBoYXZlIHRoZSBtb3N0IGZyZWUgbW9kZWwgYW5kIGxlYXN0IGZyZWVtaXVtIG1vZGVsIAoKCgpgYGB7cn0KI3N1bW1hcnkgb2YgcGFpZCBhcHBzIGJ5IGlzX2dhbWUKYWN0aXZlX2NvcHkgPSBhY3RpdmUgJT4lIGZpbHRlcihmcmVlPT0wKSAKYnkoYWN0aXZlX2NvcHkkcHJpY2UsIGFjdGl2ZV9jb3B5JGlzX2dhbWUsIHN1bW1hcnkpCgojcGFpZCBnYW1lcyBwcmljZXMgb2YgNSBtb3N0IGZyZXF1ZW50IGNhdGVnb3JpZXMKYWN0aXZlICU+JSBmaWx0ZXIgKGlzX2dhbWU9PTEpICU+JSBmaWx0ZXIoZnJlZT09MCkgJT4lIGZpbHRlcihjYXRlZ29yeSAlaW4lIHRvcF9nZW5yZXMpICU+JSBnZ3Bsb3QoYWVzKHg9Y2F0ZWdvcnksIHk9cHJpY2UsIGNvbG9yPWNhdGVnb3J5KSkgKyBnZW9tX2ppdHRlcihzaG93LmxlZ2VuZD1GQUxTRSwgc2l6ZT0yLCBhbHBoYT0wLjUpICsgY29vcmRfZmxpcCgpICsgc2NhbGVfY29sb3JfZnV0dXJhbWEoKSArIGxhYnMoeT0icHJpY2UgKFVTRCkiLCB0aXRsZT0iaXNfZ2FtZSA9IDEiKQoKI3BhaWQgYXBwcyBwcmljZXMgb2YgNSBtb3N0IGZyZXF1ZW50IGNhdGVnb3JpZXMKYWN0aXZlICU+JSBmaWx0ZXIgKGlzX2dhbWU9PTApICU+JSBmaWx0ZXIoZnJlZT09MCkgJT4lIGZpbHRlcihjYXRlZ29yeSAlaW4lIHRvcF9jYXRzKSAlPiUgZ2dwbG90KGFlcyh4PWNhdGVnb3J5LCB5PXByaWNlLCBjb2xvcj1jYXRlZ29yeSkpICsgZ2VvbV9qaXR0ZXIoc2hvdy5sZWdlbmQ9RkFMU0UsIHNpemU9MiwgYWxwaGE9MC41KSArIGNvb3JkX2ZsaXAoKSArIHNjYWxlX2NvbG9yX2Z1dHVyYW1hKCkgKyBsYWJzKHk9InByaWNlIChVU0QpIiwgdGl0bGU9ImlzX2dhbWUgPSAwIikKYGBgCiogRm9yIHBheSB0byBkb3dubG9hZCBtb2RlbHMsIAogICsgdGhlIGxvd2VzdCBwcmljZSBpcyAwLjk5VVNEIGZvciBib3RoIGdhbWVzIGFuZCBhcHBzCiAgKyBoaWdoZXN0IHByaWNlZCBwYXkgdG8gZG93bmxvYWQgZ2FtZSBpcyA0OS45OVVTRCBhbmQgYXBwIGlzIDM3OS45OQogICsgbWVkaWFuIG9mIDIuOTkgVVNEIGZvciBib3RoIGdhbWVzIGFuZCBhcHBzCgoqIExvb2tpbmcgYXQgcHJpY2VzIGJldHdlZW4gMC45OSBhbmQgNTAuMDBVU0QKICArIGdhbWVzOiBzcG9ydHMgYW5kIHNpbXVsYXRpb24gaGF2ZSBnYW1lcyBtb3JlIHRoYW4gMTBVU0QgYnV0IHB1enpsZSwgY2FzdWFsIGFuZCBhY3Rpb24gZG8gbm90CiAgKyBhcHBzOiB0b29scyBhbmQgZWR1Y2F0aW9uIGhhdmUgYXBwcyB0aGF0IGFyZSBtb3JlIHRoYW4gMjBVU0QsIGJ1dCBwZXJzb25hbGl6YXRpb24sIG11c2ljICYgYXVkaW8sIGFuZCBlbnRlcnRhaW5tZW50IGNhdGVnb3JpZXMgICAgIGRvIG5vdC4gCgoKIyMjIyBDb250ZW50IHJhdGluZwpgYGB7cn0KI2NvbnRlbnQgcmF0aW5nCmFjdGl2ZSAlPiUgZ3JvdXBfYnkoaXNfZ2FtZSxjb250ZW50X3JhdGluZykgJT4lIHRhbGx5KCkgJT4lIG11dGF0ZShwcm9wPXJvdW5kKG4vc3VtKG4pLDIpKQoKYWN0aXZlICU+JSBncm91cF9ieShpc19nYW1lLGNvbnRlbnRfcmF0aW5nKSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlKHByb3A9cm91bmQobi9zdW0obiksMikpICU+JSBnZ3Bsb3QoYWVzKHg9aXNfZ2FtZSwgeT1wcm9wLCBmaWxsPWZhY3Rvcihjb250ZW50X3JhdGluZykpKSArIGdlb21fY29sKHdpZHRoPTAuNSkgKyBjb29yZF9mbGlwKCkgKyBzY2FsZV9maWxsX3VjaGljYWdvKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsgbGFicyh5PSJwcm9wb3J0aW9uIixmaWxsPSJjb250ZW50X3JhdGluZyIpCmBgYAoKKiBpbiBib3RoIGdhbWVzIGFuZCBhcHBzIHR5cGVzLCAnRXZlcnlvbmUnIHJhdGluZyBpcyB0aGUgbW9zdCBjb21tb24gIGFuZCAiQWR1bHRzIG9ubHkgMTgrJyBpcyB0aGUgbGVhc3QgY29tbW9uCiogY29tcGFyZWQgdG8gYXBwcywgZ2FtZXMgaGF2ZSBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mICdUZWVuJyBhbmQgJ0V2ZXJ5b25lIDEwKycgY29udGVudCBidXQgbG93ZXIgcHJvcG9ydGlvbiBvZiAnTWF0dXJlIDE3KycgYW5kICAgICAnRXZlcnlvbmUnIHJhdGluZy4gIAoKCmBgYHtyfQojY29udGVudF9yYXRpbmcgb2YgbW9zdCBmcmVxdWVudCBjYXRlZ29yaWVzIAphY3RpdmUgJT4lIGZpbHRlciAoaXNfZ2FtZT09MSkgJT4lIGZpbHRlcihjYXRlZ29yeSAlaW4lIHRvcF9nZW5yZXMpICU+JSBncm91cF9ieShjYXRlZ29yeSwgY29udGVudF9yYXRpbmcpICU+JSB0YWxseSgpICU+JSBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lIGdncGxvdChhZXMoeD1jYXRlZ29yeSwgeT1wcm9wLCBmaWxsPWNvbnRlbnRfcmF0aW5nKSkgKyBnZW9tX2NvbChhbHBoYT0wLjksIHdpZHRoPTAuNykgKyBzY2FsZV9maWxsX3VjaGljYWdvKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsgbGFicyh5PSJwcm9wb3J0aW9uIiwgeD0iZ2FtZSBjYXRlZ29yeSIpICsgY29vcmRfZmxpcCgpCgphY3RpdmUgJT4lIGZpbHRlciAoaXNfZ2FtZT09MCkgJT4lIGZpbHRlcihjYXRlZ29yeSAlaW4lIHRvcF9jYXRzKSAlPiUgZ3JvdXBfYnkoY2F0ZWdvcnksIGNvbnRlbnRfcmF0aW5nKSAlPiUgdGFsbHkoKSAlPiUgbXV0YXRlKHByb3A9bi9zdW0obikpICU+JSBnZ3Bsb3QoYWVzKHg9Y2F0ZWdvcnksIHk9cHJvcCwgZmlsbD1jb250ZW50X3JhdGluZykpICsgZ2VvbV9jb2wod2lkdGg9MC43KSArIHNjYWxlX2ZpbGxfamNvKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsgbGFicyh5PSJwcm9wb3J0aW9uIiwgeD0iYXBwIGNhdGVnb3J5IikgKyBjb29yZF9mbGlwKCkKYGBgCgoqIEdhbWVzOiAKICArIG1ham9yaXR5IG9mIHNwb3J0cywgc2ltdWxhdGlvbiwgcHV6emxlIGFuZCBjYXN1YWwgZ2VucmVzIGFyZSByYXRlZCAnRXZlcnlvbmUnLCBidXQgbWFqb3JpdHkgb2YgYWN0aW9uIGlzIHJhdGVkICdUZWVuJwogICsgYWN0aW9uIGdhbWVzIGhhdmUgdGhlIGhpZ2hlc3QgcHJvcG9ydGlvbiBvZiAnTWF0dXJlIDE3KycgY29udGVudCBjb21wYXJlZCB0byB0aGUgb3RoZXIgaWRlbnRpZmllZCBnZW5yZXMKKiBBcHBzOiAKICArIGFjcm9zcyB0aGUgZml2ZSBpZGVudGlmaWVkIGdlbnJlcywgbWFqb3JpdHkgb2YgdGhlIGFwcHMgYXJlIHJhdGVkICdFdmVyeW9uZScsIGxlYXN0IGFyZSAnRXZlcnlvbmUgMTArJywgYW5kIG5vbmUgYXJlIHJhdGVkICAgICAgICAgJ0FkdWx0cyBvbmx5IDE4KycKICArIGVudGVydGFpbm1lbnQgYXBwcyBoYXZlIHRoZSBoaWdoZXN0IHByb3BvcnRpb24gb2YgJ1RlZW4nIGFuZCAnTWF0dXJlIDE3KycgY29udGVudCBjb21wYXJlZCB0byB0aGUgb3RoZXIgaWRlbnRpZmllZCBhcHAgICAgICAgICAgICAgY2F0ZWdvcmllcwoKCiMjIyBFeHBvcnQgZGF0YWZyYW1lIAoKYGBge3J9CiNzaG9ydGxpc3RlZCB2YXJpYWJsZXMgZm9yIG1vZGVsaW5nIApnYW1lczEgPSBhY3RpdmUgJT4lIGZpbHRlcihpc19nYW1lID09IDEpICU+JSBzZWxlY3QoYXBwX2lkLCBjYXRlZ29yeSwgcmF0aW5nLCByYXRpbmdfY291bnQsIG1pbmltdW1faW5zdGFsbHMsIG1heGltdW1faW5zdGFsbHMsIGZyZWUsIHByaWNlLCBzaXplLCBjb250ZW50X3JhdGluZywgYWRfc3VwcG9ydGVkLCBpbl9hcHBfcHVyY2hhc2VzLCBsYXN0X3VwZGF0ZWRfeWVhciwgcmVsZWFzZV95ZWFyLCBhZ2UsIGFnZV91cGRhdGUpIApkaW0oZ2FtZXMxKQoKYXBwczEgPSBhY3RpdmUgJT4lIGZpbHRlcihpc19nYW1lID09IDApICU+JSBzZWxlY3QoYXBwX2lkLCBjYXRlZ29yeSwgcmF0aW5nLCByYXRpbmdfY291bnQsIG1pbmltdW1faW5zdGFsbHMsIG1heGltdW1faW5zdGFsbHMsIGZyZWUsIHByaWNlLCBzaXplLCBjb250ZW50X3JhdGluZywgYWRfc3VwcG9ydGVkLCBpbl9hcHBfcHVyY2hhc2VzLCBsYXN0X3VwZGF0ZWRfeWVhciwgcmVsZWFzZV95ZWFyLCBhZ2UsIGFnZV91cGRhdGUpCmRpbShhcHBzMSkKYGBgCgpgYGB7cn0KI2V4cG9ydCBkZgp3cml0ZS5jc3YoZ2FtZXMxLCJnYW1lczEuY3N2Iixyb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KGFwcHMxLCJhcHBzMS5jc3YiLHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCgo=