Start Date: 2 Sept 2025
Report Date: 30 September 2025
Source: Psi Chi R
To acknowledge National Video Game Day which is September 12th, this
month’s dataset is about video game sales.
The contest will run until September 30th, latest time zone possible.
The entry form is available here and the prompts are below.
knitr::opts_chunk$set(echo = T,message = F,warning = F)
#setwd("C:/Users/alanh/Documents/R/Psi_Chi_R")
rm(list=ls())
setwd("~/R/Psi_Chi_R")
library(tidyverse)
#commenting out code but not for text
if (FALSE) {
}
#total for bottom row
sum_rows = function(x) {
x = as.data.frame(x) # Ensure x is a data frame
sums = sapply(x,function(col) if (is.numeric(col)) sum(col, na.rm = T) else NA)
sums = as.data.frame(t(sums)) # Convert to data frame
names(sums) = names(x) # Retain column names
rbind(x, sums) # Bind the sum row to the original data frame
}
# right column for total
sum_cols = function(x) {
x$Total = rowSums(x[sapply(x, is.numeric)], na.rm = T)
x
}
options(scipen=999) # disable scientific notation
#dollar format function
dollars = function(x) {
paste0("$",format(x,big.mark= ",",scientific=F))
}
desc_stats = function(x){
c(min = min(x,na.rm=T),
median = median(x,na.rm=T),
max = max(x,na.rm=T),
mean = mean(x,na.rm=T),
sd = sd(x,na.rm=T))
}
Clean and EDA
data=read_csv('data.csv',show_col_types = F)
# data = readxl::read_excel('C:/Users/alanh/Downloads/2025Apr_data.xlsx')
names(data) = make.names(colnames(data))
#SmartEDA::ExpData(data,type=2) %>% arrange(desc(Per_of_Missing))
skimr::skim(data)
Data summary
| Name |
data |
| Number of rows |
64016 |
| Number of columns |
13 |
| _______________________ |
|
| Column type frequency: |
|
| character |
7 |
| numeric |
6 |
| ________________________ |
|
| Group variables |
None |
Variable type: character
| title |
0 |
1.00 |
1 |
147 |
0 |
39798 |
0 |
| console |
0 |
1.00 |
2 |
6 |
0 |
81 |
0 |
| genre |
0 |
1.00 |
3 |
16 |
0 |
20 |
0 |
| publisher |
0 |
1.00 |
2 |
49 |
0 |
3383 |
0 |
| developer |
17 |
1.00 |
2 |
68 |
0 |
8862 |
0 |
| release_date |
7051 |
0.89 |
8 |
10 |
0 |
7922 |
0 |
| last_update |
46137 |
0.28 |
8 |
10 |
0 |
1545 |
0 |
Variable type: numeric
| critic_score |
57338 |
0.10 |
7.22 |
1.46 |
1 |
6.40 |
7.50 |
8.30 |
10.00 |
▁▁▃▇▅ |
| total_sales |
45094 |
0.30 |
0.35 |
0.81 |
0 |
0.03 |
0.12 |
0.34 |
20.32 |
▇▁▁▁▁ |
| na_sales |
51379 |
0.20 |
0.26 |
0.49 |
0 |
0.05 |
0.12 |
0.28 |
9.76 |
▇▁▁▁▁ |
| jp_sales |
57290 |
0.11 |
0.10 |
0.17 |
0 |
0.02 |
0.04 |
0.12 |
2.13 |
▇▁▁▁▁ |
| pal_sales |
51192 |
0.20 |
0.15 |
0.39 |
0 |
0.01 |
0.04 |
0.14 |
9.85 |
▇▁▁▁▁ |
| other_sales |
48888 |
0.24 |
0.04 |
0.13 |
0 |
0.00 |
0.01 |
0.03 |
3.12 |
▇▁▁▁▁ |
Level 1: Cleaning and Preparation
Remove rows that are missing a value for
‘critic_score’
Level 2: Descriptives
Create a table that shows the average critic
ratings (‘critic_score’) and total sales (‘total_sales’) for each genre
(‘genre’)
ACR_tab = data1 |>
group_by(genre) |>
summarise(critic_ratings_avg = mean(critic_score,na.rm=T),
total_sales_avg = mean(total_sales,na.rm=T))
print(ACR_tab,print=Inf)
## # A tibble: 20 × 3
## genre critic_ratings_avg total_sales_avg
## <chr> <dbl> <dbl>
## 1 Action 6.83 0.784
## 2 Action-Adventure 7.68 1.00
## 3 Adventure 7.34 0.572
## 4 Board Game 5.3 0.3
## 5 Education 7.75 0.305
## 6 Fighting 7.39 0.632
## 7 MMO 7.78 0.29
## 8 Misc 6.88 0.727
## 9 Music 7.99 0.836
## 10 Party 5.97 0.375
## 11 Platform 7.31 0.500
## 12 Puzzle 7.20 0.283
## 13 Racing 7.19 0.746
## 14 Role-Playing 7.47 0.545
## 15 Sandbox 9.2 1.89
## 16 Shooter 7.28 1.18
## 17 Simulation 6.91 0.659
## 18 Sports 7.29 0.902
## 19 Strategy 7.43 0.268
## 20 Visual Novel 8.5 0.03
Create a table that shows the average total sales
(‘total_sales’) for each developer (‘developer’)
ATS_tab = data1 |>
group_by(developer) |>
summarise(total_sales_avg = mean(total_sales,na.rm=T))
print(ATS_tab)
## # A tibble: 1,576 × 2
## developer total_sales_avg
## <chr> <dbl>
## 1 .theprodukkt NaN
## 2 10tacle Studios AG 0.16
## 3 11 bit studios NaN
## 4 1C Company NaN
## 5 1C: Maddox Games 0.05
## 6 1C:Ino-Co NaN
## 7 1st Playable Productions 3.14
## 8 2015, Inc. 0
## 9 2D Boy 0.03
## 10 2K Australia / 2K Boston 2.83
## # ℹ 1,566 more rows
Level 3: Research Questions
What is each developer’s (‘developer’) most sold
game in North America (‘na_sales’)?
data1 |>
group_by(developer,title) |>
summarise(most_sold_game = max(na_sales,na.rm = T)) |>
arrange(desc(most_sold_game)) |>
distinct(developer,.keep_all = T)
What was the most highly sold game of 2018?
data1 |>
filter(release_date2==2018) |>
group_by(title) |>
summarise(highly_sold_game = max(total_sales,na.rm = T)) |>
arrange(desc(highly_sold_game))
# Red Dead Redemption 2
Is there a relationship between public perception
(‘critic_score’) and the number of video games sold (‘total_sales’)?
rel_list = list('critic_score','total_sales')
for (i in rel_list){
x=sum(is.na(data1[[i]]))
print(paste0(i,":"," ",x))
}
## [1] "critic_score: 0"
## [1] "total_sales: 2552"
data2 = data1 |>
filter(!is.na(total_sales))
for (i in rel_list){
x=shapiro.test(data2[[i]])
print(i)
print(x)
}
## [1] "critic_score"
##
## Shapiro-Wilk normality test
##
## data: data2[[i]]
## W = 0.95407, p-value < 0.00000000000000022
##
## [1] "total_sales"
##
## Shapiro-Wilk normality test
##
## data: data2[[i]]
## W = 0.47624, p-value < 0.00000000000000022
for (i in rel_list){
hist(data2[[i]],main=i,xlab=i,probability = T)
lines(density(data1[[i]],na.rm = T),lwd=2,col='darkred')
}


if (FALSE) {
hist(data1$total_sales,probability = T)
lines(density(data1$total_sales))
hist(data1$critic_score,probability = T)
lines(density(data1$critic_score))
}
cor_test_list = list('s','k')
for (i in cor_test_list){
x= cor.test(data2$critic_score,data2$total_sales,method = i)
print(x)
}
##
## Spearman's rank correlation rho
##
## data: data2$critic_score and data2$total_sales
## S = 7752046553, p-value < 0.00000000000000022
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
## rho
## 0.337814
##
##
## Kendall's rank correlation tau
##
## data: data2$critic_score and data2$total_sales
## z = 22.401, p-value < 0.00000000000000022
## alternative hypothesis: true tau is not equal to 0
## sample estimates:
## tau
## 0.2364818
#yes, the Spearman rank shows a
The Spearman’s showed a significant positive correlation between
critic score and total sales, rho = 0.34, p < .001. This is
consistent with Kendall’s (tau = 0.24, p < .001), indicating that
higher critic scores are correlated with greater sales.
Level 4: Visualization
Graph the relationship between public perception
(‘critic_score’) and the number of video games sold (‘total_sales’)
data2 |>
ggplot(aes(x=critic_score,y=total_sales,col=total_sales))+
geom_point()+
theme_bw()+
geom_jitter()+
#geom_smooth()+
labs(title='In the Eyes of the Beholder')+
theme(plot.title = element_text(hjust = .5))+
guides(col='none')

Graph the total number of video games sold by EA
Sports (‘publisher’) over time
data3 = data2 |>
filter(grepl('ea sports',publisher,ignore.case = T)) |>
mutate(PUBLISHER_2 = case_when(publisher == 'EA Sports BIG' ~ 'EA Sports',TRUE ~ publisher))
data3 |>
group_by(release_date2) |>
summarise(HEADCOUNT =n()) |>
ggplot(aes(x=release_date2,y=HEADCOUNT))+
geom_line(col='darkgreen',lwd=1)+
theme_bw()+
labs(x='Release Year',y='Games Sold',title='The EA Way by EA Games',subtitle='(the way of microtransactions to sell, sell, sell!)') +
geom_text(aes(label = HEADCOUNT,vjust = -.5,hjust = 2),size = 3.5,col='darkred')+
theme(plot.subtitle = element_text(hjust = .5),plot.title = element_text(hjust = .5))

data3 |>
ggplot(aes(x=release_date2))+
geom_bar(fill='darkgreen',col='gold')+
theme_bw()+
labs(x='Release Year',y='Games Sold',title='The EA Way by EA Games',subtitle='(the way of microtransactions to sell, sell, sell!)') +
geom_text(stat='count',aes(label = after_stat(count)),vjust = -0.5,size = 3,col='darkred')+
theme(plot.subtitle = element_text(hjust = .5),plot.title = element_text(hjust = .5))

data4 = data3 |>
select(contains('sale')) |>
na.omit()
cor_frame = cor(data4) |> as.data.frame()
cor_frame
cor_frame2 = cor_frame |>
rownames_to_column('var1') |>
pivot_longer(-var1,names_to = 'var2',values_to = 'corr')
cor_frame2 |>
ggplot(aes(x=var1,y=var2,fill=corr))+
geom_tile()+scale_fill_gradient2(
high = "darkred", low = "lightblue", mid = "gray",midpoint = 0, limit = c(-1, 1), space = "Lab",name = "Correlation") +
theme_bw() +
labs(title='Sales Matrix',y='',x='')+
theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1),
plot.title = element_text(hjust = .5))+
geom_text(aes(label = round(corr, 2)))

LS0tDQp0aXRsZTogIlBzaSBDaGkgUiAtIFNlcCAyMDI1Ig0KI2RhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBsaWdodGJveDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIGFsd2F5c19hbGxvd19odG1sOiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIGFuY2hvcl9zZWN0aW9uczogVFJVRQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCi0tLQ0KDQpTdGFydCBEYXRlOiAyIFNlcHQgMjAyNQ0KDQpSZXBvcnQgRGF0ZTogYHIgZm9ybWF0KFN5cy5EYXRlKCksICclZCAlQiAlWScpYA0KDQpbU291cmNlOiBQc2kgQ2hpIFJdKGh0dHBzOi8vb3NmLmlvL2N2OWJ0L3dpa2kvaG9tZS8pDQoNClRvIGFja25vd2xlZGdlIE5hdGlvbmFsIFZpZGVvIEdhbWUgRGF5IHdoaWNoIGlzIFNlcHRlbWJlciAxMnRoLCB0aGlzIG1vbnRoJ3MgZGF0YXNldCBpcyBhYm91dCB2aWRlbyBnYW1lIHNhbGVzLg0KDQpUaGUgY29udGVzdCB3aWxsIHJ1biB1bnRpbCBTZXB0ZW1iZXIgMzB0aCwgbGF0ZXN0IHRpbWUgem9uZSBwb3NzaWJsZS4gVGhlIGVudHJ5IGZvcm0gaXMgYXZhaWxhYmxlIGhlcmUgYW5kIHRoZSBwcm9tcHRzIGFyZSBiZWxvdy4NCg0KDQpgYGB7ciB3YXJuaW5nPUYsbWVzc2FnZT1GfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBULG1lc3NhZ2UgPSBGLHdhcm5pbmcgPSBGKQ0KDQojc2V0d2QoIkM6L1VzZXJzL2FsYW5oL0RvY3VtZW50cy9SL1BzaV9DaGlfUiIpDQoNCnJtKGxpc3Q9bHMoKSkNCg0Kc2V0d2QoIn4vUi9Qc2lfQ2hpX1IiKQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KI2NvbW1lbnRpbmcgb3V0IGNvZGUgYnV0IG5vdCBmb3IgdGV4dA0KaWYgKEZBTFNFKSB7DQoNCn0NCg0KI3RvdGFsIGZvciBib3R0b20gcm93DQoNCnN1bV9yb3dzID0gZnVuY3Rpb24oeCkgew0KICB4ID0gYXMuZGF0YS5mcmFtZSh4KSAjIEVuc3VyZSB4IGlzIGEgZGF0YSBmcmFtZQ0KICBzdW1zID0gc2FwcGx5KHgsZnVuY3Rpb24oY29sKSBpZiAoaXMubnVtZXJpYyhjb2wpKSBzdW0oY29sLCBuYS5ybSA9IFQpIGVsc2UgTkEpDQogIHN1bXMgPSBhcy5kYXRhLmZyYW1lKHQoc3VtcykpICMgQ29udmVydCB0byBkYXRhIGZyYW1lDQogIG5hbWVzKHN1bXMpID0gbmFtZXMoeCkgIyBSZXRhaW4gY29sdW1uIG5hbWVzDQogIHJiaW5kKHgsIHN1bXMpICMgQmluZCB0aGUgc3VtIHJvdyB0byB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZQ0KfQ0KDQojIHJpZ2h0IGNvbHVtbiBmb3IgdG90YWwNCnN1bV9jb2xzID0gZnVuY3Rpb24oeCkgew0KICB4JFRvdGFsID0gcm93U3Vtcyh4W3NhcHBseSh4LCBpcy5udW1lcmljKV0sIG5hLnJtID0gVCkNCiAgeA0KfQ0KDQpvcHRpb25zKHNjaXBlbj05OTkpICMgZGlzYWJsZSBzY2llbnRpZmljIG5vdGF0aW9uDQoNCiNkb2xsYXIgZm9ybWF0IGZ1bmN0aW9uDQpkb2xsYXJzID0gZnVuY3Rpb24oeCkgew0KICBwYXN0ZTAoIiQiLGZvcm1hdCh4LGJpZy5tYXJrPSAiLCIsc2NpZW50aWZpYz1GKSkNCn0NCg0KZGVzY19zdGF0cyA9IGZ1bmN0aW9uKHgpew0KICBjKG1pbiA9IG1pbih4LG5hLnJtPVQpLA0KICAgIG1lZGlhbiA9IG1lZGlhbih4LG5hLnJtPVQpLA0KICAgIG1heCA9IG1heCh4LG5hLnJtPVQpLA0KICAgIG1lYW4gPSBtZWFuKHgsbmEucm09VCksDQogICAgc2QgPSBzZCh4LG5hLnJtPVQpKQ0KfQ0KYGBgDQoNCiMjIENsZWFuIGFuZCBFREENCg0KYGBge3J9DQpkYXRhPXJlYWRfY3N2KCdkYXRhLmNzdicsc2hvd19jb2xfdHlwZXMgPSBGKQ0KDQojIGRhdGEgPSByZWFkeGw6OnJlYWRfZXhjZWwoJ0M6L1VzZXJzL2FsYW5oL0Rvd25sb2Fkcy8yMDI1QXByX2RhdGEueGxzeCcpDQoNCm5hbWVzKGRhdGEpID0gbWFrZS5uYW1lcyhjb2xuYW1lcyhkYXRhKSkNCg0KI1NtYXJ0RURBOjpFeHBEYXRhKGRhdGEsdHlwZT0yKSAlPiUgYXJyYW5nZShkZXNjKFBlcl9vZl9NaXNzaW5nKSkNCg0Kc2tpbXI6OnNraW0oZGF0YSkNCmBgYA0KDQojIyMgRGF0YSBzZXQ6DQoNCmBgYHtyIGVjaG89Rn0NCmRvd25sb2FkdGhpczo6ZG93bmxvYWRfdGhpcyhkYXRhLG91dHB1dF9uYW1lID0gJ2RhdGFzZXQnLG91dHB1dF9leHRlbnNpb24gPSAnLnhsc3gnKQ0KYGBgDQoNCg0KIyMgTGV2ZWwgMTogQ2xlYW5pbmcgYW5kIFByZXBhcmF0aW9uDQoNCiMjIyBSZW1vdmUgcm93cyB0aGF0IGFyZSBtaXNzaW5nIGEgdmFsdWUgZm9yIOKAmGNyaXRpY19zY29yZeKAmQ0KDQojIyMgUmUtZm9ybWF0IHRoZSByZWxlYXNlX2RhdGUgYW5kIGxhc3RfdXBkYXRlIGNvbHVtbnMgdG8gb25seSBjb250YWluIHRoZSB5ZWFyDQoNCmBgYHtyfQ0KZGF0YTEgPSBkYXRhIHw+IA0KICBmaWx0ZXIoIWlzLm5hKGNyaXRpY19zY29yZSkpIHw+IA0KICBtdXRhdGUocmVsZWFzZV9kYXRlMiA9IHllYXIobWR5KHJlbGVhc2VfZGF0ZSkpLA0KICAgICAgICAgbGFzdF91cGRhdGUyID0geWVhcihtZHkobGFzdF91cGRhdGUpKSkgfD4gDQogIHNlbGVjdChyZWxlYXNlX2RhdGUyLGxhc3RfdXBkYXRlMixjcml0aWNfc2NvcmUsZXZlcnl0aGluZygpKQ0KYGBgDQoNCiMjIExldmVsIDI6IERlc2NyaXB0aXZlcw0KDQojIyMgQ3JlYXRlIGEgdGFibGUgdGhhdCBzaG93cyB0aGUgYXZlcmFnZSBjcml0aWMgcmF0aW5ncyAo4oCYY3JpdGljX3Njb3Jl4oCZKSBhbmQgdG90YWwgc2FsZXMgKOKAmHRvdGFsX3NhbGVz4oCZKSBmb3IgZWFjaCBnZW5yZSAo4oCYZ2VucmXigJkpDQoNCmBgYHtyfQ0KQUNSX3RhYiA9IGRhdGExIHw+IA0KICBncm91cF9ieShnZW5yZSkgfD4gDQogIHN1bW1hcmlzZShjcml0aWNfcmF0aW5nc19hdmcgPSBtZWFuKGNyaXRpY19zY29yZSxuYS5ybT1UKSwNCiAgICAgICAgICAgIHRvdGFsX3NhbGVzX2F2ZyA9IG1lYW4odG90YWxfc2FsZXMsbmEucm09VCkpDQoNCnByaW50KEFDUl90YWIscHJpbnQ9SW5mKQ0KYGBgDQoNCiMjIENyZWF0ZSBhIHRhYmxlIHRoYXQgc2hvd3MgdGhlIGF2ZXJhZ2UgdG90YWwgc2FsZXMgKOKAmHRvdGFsX3NhbGVz4oCZKSBmb3IgZWFjaCBkZXZlbG9wZXIgKOKAmGRldmVsb3BlcuKAmSkNCg0KYGBge3J9DQpBVFNfdGFiID0gZGF0YTEgfD4gDQogIGdyb3VwX2J5KGRldmVsb3BlcikgfD4gDQogIHN1bW1hcmlzZSh0b3RhbF9zYWxlc19hdmcgPSBtZWFuKHRvdGFsX3NhbGVzLG5hLnJtPVQpKQ0KDQpwcmludChBVFNfdGFiKQ0KYGBgDQoNCg0KIyMgTGV2ZWwgMzogUmVzZWFyY2ggUXVlc3Rpb25zDQoNCiMjIyBXaGF0IGlzIGVhY2ggZGV2ZWxvcGVy4oCZcyAo4oCYZGV2ZWxvcGVy4oCZKSBtb3N0IHNvbGQgZ2FtZSBpbiBOb3J0aCBBbWVyaWNhICjigJhuYV9zYWxlc+KAmSk/DQoNCmBgYHtyfQ0KZGF0YTEgfD4gDQogIGdyb3VwX2J5KGRldmVsb3Blcix0aXRsZSkgfD4gDQogIHN1bW1hcmlzZShtb3N0X3NvbGRfZ2FtZSA9IG1heChuYV9zYWxlcyxuYS5ybSA9IFQpKSB8PiANCiAgYXJyYW5nZShkZXNjKG1vc3Rfc29sZF9nYW1lKSkgfD4gDQogIGRpc3RpbmN0KGRldmVsb3Blciwua2VlcF9hbGwgPSBUKQ0KYGBgDQoNCiMjIyBXaGF0IHdhcyB0aGUgbW9zdCBoaWdobHkgc29sZCBnYW1lIG9mIDIwMTg/DQoNCmBgYHtyfQ0KZGF0YTEgfD4gDQogIGZpbHRlcihyZWxlYXNlX2RhdGUyPT0yMDE4KSB8PiANCiAgZ3JvdXBfYnkodGl0bGUpIHw+IA0KICBzdW1tYXJpc2UoaGlnaGx5X3NvbGRfZ2FtZSA9IG1heCh0b3RhbF9zYWxlcyxuYS5ybSA9IFQpKSB8PiANCiAgYXJyYW5nZShkZXNjKGhpZ2hseV9zb2xkX2dhbWUpKQ0KDQojIFJlZCBEZWFkIFJlZGVtcHRpb24gMg0KYGBgDQoNCiMjIyBJcyB0aGVyZSBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHB1YmxpYyBwZXJjZXB0aW9uICjigJhjcml0aWNfc2NvcmXigJkpIGFuZCB0aGUgbnVtYmVyIG9mIHZpZGVvIGdhbWVzIHNvbGQgKOKAmHRvdGFsX3NhbGVz4oCZKT8NCg0KYGBge3J9DQpyZWxfbGlzdCA9IGxpc3QoJ2NyaXRpY19zY29yZScsJ3RvdGFsX3NhbGVzJykNCg0KZm9yIChpIGluIHJlbF9saXN0KXsNCiAgeD1zdW0oaXMubmEoZGF0YTFbW2ldXSkpDQogIHByaW50KHBhc3RlMChpLCI6IiwiICIseCkpDQp9DQpgYGANCg0KYGBge3J9DQpkYXRhMiA9IGRhdGExIHw+IA0KICBmaWx0ZXIoIWlzLm5hKHRvdGFsX3NhbGVzKSkNCmBgYA0KDQpgYGB7cn0NCmZvciAoaSBpbiByZWxfbGlzdCl7DQogIHg9c2hhcGlyby50ZXN0KGRhdGEyW1tpXV0pDQogIHByaW50KGkpDQogIHByaW50KHgpDQp9DQpgYGANCg0KYGBge3J9DQpmb3IgKGkgaW4gcmVsX2xpc3Qpew0KICBoaXN0KGRhdGEyW1tpXV0sbWFpbj1pLHhsYWI9aSxwcm9iYWJpbGl0eSA9IFQpDQogIGxpbmVzKGRlbnNpdHkoZGF0YTFbW2ldXSxuYS5ybSA9IFQpLGx3ZD0yLGNvbD0nZGFya3JlZCcpDQp9DQoNCg0KaWYgKEZBTFNFKSB7DQpoaXN0KGRhdGExJHRvdGFsX3NhbGVzLHByb2JhYmlsaXR5ID0gVCkNCmxpbmVzKGRlbnNpdHkoZGF0YTEkdG90YWxfc2FsZXMpKQ0KDQpoaXN0KGRhdGExJGNyaXRpY19zY29yZSxwcm9iYWJpbGl0eSA9IFQpDQpsaW5lcyhkZW5zaXR5KGRhdGExJGNyaXRpY19zY29yZSkpDQp9DQpgYGANCg0KYGBge3J9DQpjb3JfdGVzdF9saXN0ID0gbGlzdCgncycsJ2snKQ0KDQpmb3IgKGkgaW4gY29yX3Rlc3RfbGlzdCl7DQogIHg9IGNvci50ZXN0KGRhdGEyJGNyaXRpY19zY29yZSxkYXRhMiR0b3RhbF9zYWxlcyxtZXRob2QgPSBpKQ0KICBwcmludCh4KQ0KfQ0KDQojeWVzLCB0aGUgU3BlYXJtYW4gcmFuayBzaG93cyBhIA0KYGBgDQoNClRoZSBTcGVhcm1hbidzIHNob3dlZCBhIHNpZ25pZmljYW50IHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gY3JpdGljIHNjb3JlIGFuZCB0b3RhbCBzYWxlcywgcmhvID0gMC4zNCwgcCA8IC4wMDEuIFRoaXMgaXMgY29uc2lzdGVudCB3aXRoIEtlbmRhbGzigJlzICh0YXUgPSAwLjI0LCBwIDwgLjAwMSksIGluZGljYXRpbmcgdGhhdCBoaWdoZXIgY3JpdGljIHNjb3JlcyBhcmUgY29ycmVsYXRlZCB3aXRoIGdyZWF0ZXIgc2FsZXMuDQoNCiMjIExldmVsIDQ6IFZpc3VhbGl6YXRpb24NCg0KIyMjIEdyYXBoIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBwdWJsaWMgcGVyY2VwdGlvbiAo4oCYY3JpdGljX3Njb3Jl4oCZKSBhbmQgdGhlIG51bWJlciBvZiB2aWRlbyBnYW1lcyBzb2xkICjigJh0b3RhbF9zYWxlc+KAmSkNCg0KYGBge3J9DQpkYXRhMiB8PiANCiAgZ2dwbG90KGFlcyh4PWNyaXRpY19zY29yZSx5PXRvdGFsX3NhbGVzLGNvbD10b3RhbF9zYWxlcykpKw0KICBnZW9tX3BvaW50KCkrDQogIHRoZW1lX2J3KCkrDQogIGdlb21faml0dGVyKCkrDQogICNnZW9tX3Ntb290aCgpKw0KICBsYWJzKHRpdGxlPSdJbiB0aGUgRXllcyBvZiB0aGUgQmVob2xkZXInKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSkrDQogIGd1aWRlcyhjb2w9J25vbmUnKQ0KYGBgDQoNCiMjIyBHcmFwaCB0aGUgdG90YWwgbnVtYmVyIG9mIHZpZGVvIGdhbWVzIHNvbGQgYnkgRUEgU3BvcnRzICjigJhwdWJsaXNoZXLigJkpIG92ZXIgdGltZQ0KDQpgYGB7cn0NCmRhdGEzID0gZGF0YTIgfD4gDQogIGZpbHRlcihncmVwbCgnZWEgc3BvcnRzJyxwdWJsaXNoZXIsaWdub3JlLmNhc2UgPSBUKSkgfD4gDQogIG11dGF0ZShQVUJMSVNIRVJfMiA9IGNhc2Vfd2hlbihwdWJsaXNoZXIgPT0gJ0VBIFNwb3J0cyBCSUcnIH4gJ0VBIFNwb3J0cycsVFJVRSB+IHB1Ymxpc2hlcikpDQoNCmRhdGEzIHw+IA0KICBncm91cF9ieShyZWxlYXNlX2RhdGUyKSB8PiANCiAgc3VtbWFyaXNlKEhFQURDT1VOVCA9bigpKSB8PiANCiAgZ2dwbG90KGFlcyh4PXJlbGVhc2VfZGF0ZTIseT1IRUFEQ09VTlQpKSsNCiAgZ2VvbV9saW5lKGNvbD0nZGFya2dyZWVuJyxsd2Q9MSkrDQogIHRoZW1lX2J3KCkrDQogIGxhYnMoeD0nUmVsZWFzZSBZZWFyJyx5PSdHYW1lcyBTb2xkJyx0aXRsZT0nVGhlIEVBIFdheSBieSBFQSBHYW1lcycsc3VidGl0bGU9Jyh0aGUgd2F5IG9mIG1pY3JvdHJhbnNhY3Rpb25zIHRvIHNlbGwsIHNlbGwsIHNlbGwhKScpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEhFQURDT1VOVCx2anVzdCA9IC0uNSxoanVzdCA9IDIpLHNpemUgPSAzLjUsY29sPSdkYXJrcmVkJykrDQogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSkscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSkNCmBgYA0KDQpgYGB7cn0NCmRhdGEzIHw+IA0KICBnZ3Bsb3QoYWVzKHg9cmVsZWFzZV9kYXRlMikpKw0KICBnZW9tX2JhcihmaWxsPSdkYXJrZ3JlZW4nLGNvbD0nZ29sZCcpKw0KICB0aGVtZV9idygpKw0KICBsYWJzKHg9J1JlbGVhc2UgWWVhcicseT0nR2FtZXMgU29sZCcsdGl0bGU9J1RoZSBFQSBXYXkgYnkgRUEgR2FtZXMnLHN1YnRpdGxlPScodGhlIHdheSBvZiBtaWNyb3RyYW5zYWN0aW9ucyB0byBzZWxsLCBzZWxsLCBzZWxsISknKSArDQogIGdlb21fdGV4dChzdGF0PSdjb3VudCcsYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLHZqdXN0ID0gLTAuNSxzaXplID0gMyxjb2w9J2RhcmtyZWQnKSsNCiAgdGhlbWUocGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IC41KSxwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gLjUpKQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YTQgPSBkYXRhMyB8PiANCiAgc2VsZWN0KGNvbnRhaW5zKCdzYWxlJykpIHw+IA0KICBuYS5vbWl0KCkNCg0KY29yX2ZyYW1lID0gY29yKGRhdGE0KSB8PiBhcy5kYXRhLmZyYW1lKCkNCg0KY29yX2ZyYW1lDQpgYGANCg0KYGBge3J9DQpjb3JfZnJhbWUyID0gY29yX2ZyYW1lIHw+IA0KICByb3duYW1lc190b19jb2x1bW4oJ3ZhcjEnKSB8PiANCiAgcGl2b3RfbG9uZ2VyKC12YXIxLG5hbWVzX3RvID0gJ3ZhcjInLHZhbHVlc190byA9ICdjb3JyJykNCg0KY29yX2ZyYW1lMiB8PiANCiAgZ2dwbG90KGFlcyh4PXZhcjEseT12YXIyLGZpbGw9Y29ycikpKw0KICBnZW9tX3RpbGUoKStzY2FsZV9maWxsX2dyYWRpZW50MigNCiAgICBoaWdoID0gImRhcmtyZWQiLCBsb3cgPSAibGlnaHRibHVlIiwgbWlkID0gImdyYXkiLG1pZHBvaW50ID0gMCwgbGltaXQgPSBjKC0xLCAxKSwgc3BhY2UgPSAiTGFiIixuYW1lID0gIkNvcnJlbGF0aW9uIikgKw0KICB0aGVtZV9idygpICsNCiAgbGFicyh0aXRsZT0nU2FsZXMgTWF0cml4Jyx5PScnLHg9JycpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0ID0gMSksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAuNSkpKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoY29yciwgMikpKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9Rn0NCiNiZWVwIHdoZW4gZG9uZQ0KaWYgKHJlcXVpcmUoImJlZXByIikpDQogIGJlZXByOjpiZWVwKDIpDQpgYGA=