Myanimelist collects the ratings of several million users, and uses those to make an overall anime rank. The problem with this method is that some people systematically give higher ratings than others, and those people might be more likely to watch certain shows. Conversely, there is the same issue on the user side, where some people might deliberately search out shows with higher ratings than others.

Normally this issue would be solved with regression, which cannot be done here because there are 10,000 different titles and 1,000,000 different users, making it impossible to run the algorithm due to memory constraints. Instead, I used an interated method where I observed which raters gave higher ratings than others controlling for the anime they watched, and what anime received higher ratings than others controlling for the users who rated them. Because the confounding goes both ways, so I had to repeat the method to get roughly reliable results.

Loading and cleaning data

setwd('~')
Warning: The working directory was changed to C:/Users/micha/OneDrive/Documents inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
setwd('rfolder/anime')
animeids <- read.csv('data/anime.csv', sep='\t')
#original anime ranks
ogranks <- read.csv('data2/aranks_v1.csv', sep=',')
#first iteration
aranks2 <- read.csv('data2/aranks_v2.csv', sep=',')
#fifth iteration
aranks5 <- read.csv('data2/aranks_v6.csv', sep=',')
sequel <- read.csv('data2/sequels.csv', sep=',') %>% select(anime_id, Sequel)
#tenth iteration
aranks10 <- read.csv('data2/aranks_v11.csv', sep=',')
animeids <- read.csv('data/anime.csv', sep='\t')
names <- animeids %>% select(anime_id, title)

original_ranks <- left_join(ogranks, names, by='anime_id') %>% arrange(-animescore)
first_iteration <- left_join(aranks2, names, by='anime_id') %>% arrange(-animescore)
fifth_iteration <- left_join(aranks5, names, by='anime_id') %>% arrange(-animescore)
tenth_iteration <- left_join(aranks10, names, by='anime_id') %>% arrange(-animescore)

mean(original_ranks$animescore, na.rm=T)
[1] 6.284712
sd(original_ranks$animescore, na.rm=T)
[1] 1.052224
first_iteration$animescore = normalise(first_iteration$animescore)*1.052224+6.284712
fifth_iteration$animescore = normalise(fifth_iteration$animescore)*1.052224+6.284712
tenth_iteration$animescore = normalise(tenth_iteration$animescore)*1.052224+6.284712

first_iteration$animescore1 = first_iteration$animescore
fifth_iteration$animescore5 = fifth_iteration$animescore
tenth_iteration$animescore10 = tenth_iteration$animescore

newdata <- left_join(original_ranks, first_iteration %>% select(anime_id, animescore1), by='anime_id') %>% arrange(-animescore)
newdata <- left_join(newdata, fifth_iteration %>% select(anime_id, animescore5), by='anime_id') %>% arrange(-animescore)
newdata <- left_join(newdata, tenth_iteration %>% select(anime_id, animescore10), by='anime_id') %>% arrange(-animescore)
newdata <- left_join(newdata, sequel %>% select(anime_id, Sequel), by='anime_id') %>% arrange(-animescore)
newdata$adv <- newdata$animescore10 - newdata$animescore

Correlation matrix. Correlation between fifth iteration ratings and tenth iteration is about .996.

correlation_matrix(newdata %>% select(animescore, animescore1, animescore5, animescore10))
             animescore  animescore1 animescore5 animescore10
animescore   "NA"        "0.912 ***" "0.952 ***" "0.952 ***" 
animescore1  "0.912 ***" "NA"        "0.967 ***" "0.959 ***" 
animescore5  "0.952 ***" "0.967 ***" "NA"        "1 ***"     
animescore10 "0.952 ***" "0.959 ***" "1 ***"     "NA"        

The rankings

Kicking out anime with less than 5k ratings

newdata$originalrank <- ranker(newdata$animescore)
newdata$newrank <- ranker(newdata$animescore10)
newdata2 <- newdata %>% filter(an > 4999)

Top 20 for each category. animescore – original rating, animescore10 – user adjusted rating.

original <- newdata2 %>% filter(Sequel==0)
sequel <- newdata2 %>% filter(Sequel==1)

head(newdata2 %>% filter(Sequel==1) %>% select(title, animescore) %>% arrange(-animescore), n=20)
head(newdata2 %>% filter(Sequel==1) %>% select(title, animescore10) %>% arrange(-animescore10), n=20)

head(newdata2 %>% filter(Sequel==0) %>% select(title, animescore) %>% arrange(-animescore), n=20)
head(newdata2 %>% filter(Sequel==0) %>% select(title, animescore10) %>% arrange(-animescore10), n=20)

head(newdata2 %>% filter(Sequel==0) %>% select(title, adv) %>% arrange(-adv), n=20)
head(newdata2 %>% filter(Sequel==0) %>% select(title, adv) %>% arrange(adv), n=20)

head(newdata2 %>% filter(Sequel==1) %>% select(title, adv) %>% arrange(-adv), n=20)
head(newdata2 %>% filter(Sequel==1) %>% select(title, adv) %>% arrange(adv), n=20)

Some notes: - Traditionally “elitist” anime like Ashita no Joe, Kaiba, and Legend of the Galactic Heroes gain the most from the user-adjusted method, as the kind of people who are predisposed to watching them are harsher raters. - Anime watched by men tend to gain more from user-adjustment, and vice versa. - Legend of the Galactic Heroes goes from top 4 to indisputably number 1 after adjusting for user ratings.

This is the code snippet I used to calculate the user-adjusted ratings. I had to run 10 iterations separately (no loops) of it due to memory limitations.

```r
setwd('~')
setwd('Documents/rstuff/anime')
animeids <- read.csv('data/anime.csv', sep='\t')
test <- read.csv('data2/fullfilev1.csv', sep=',')
ranksv3 <- read.csv('data2/ranks_v3.csv', sep=',')

names <- animeids %>% select(anime_id, title)

########3naive means
m <- test %>% group_by(anime_id) %>% summarise(animescore=mean(score, na.rm=T), n=n())
m2 <- test %>% group_by(user_id) %>% summarise(userscore=mean(score, na.rm=T), n=n())

ranks <- left_join(m, names, by='anime_id') %>% arrange(-animescore)
uranks <- m2 %>% arrange(-userscore)

##############
test2 <- test
test2$ascore2 <- test2$score
test2$uscore2 <- test2$score
test2$score2 <- test2$score

########3
uranks <- test2 %>% group_by(user_id) %>% summarise(userscore=mean(uscore2, na.rm=T), un=n()) %>% arrange(-userscore)
aranks <- test2 %>% group_by(anime_id) %>% summarise(animescore=mean(ascore2, na.rm=T), an=n()) %>% arrange(-animescore)
write.csv(aranks, paste0('data2/aranks_v', 1, '.csv'))
write.csv(uranks, paste0('data2/uranks_v', 1, '.csv'))

test2 <- left_join(test2, uranks %>% select(user_id, userscore, un), by='user_id')
test2 <- left_join(test2, aranks %>% select(anime_id, animescore, an), by='anime_id')
test2$adiff <- test2$score2-test2$userscore
test2$udiff <- test2$score2-test2$animescore

test2$ascore2 <- test2$adiff+mean(aranks$animescore, na.rm=T)
test2$uscore2 <- test2$udiff+mean(uranks$userscore, na.rm=T)

aranks2 <- test2 %>% group_by(anime_id) %>% summarise(animescore=mean(ascore2, na.rm=T), an=n()) %>% arrange(-animescore)
uranks2 <- test2 %>% group_by(user_id) %>% summarise(userscore=mean(uscore2, na.rm=T), un=n()) %>% arrange(-userscore)
aranks2 <- left_join(names, aranks2 %>% select(anime_id, animescore, an), by='anime_id')

write.csv(aranks2, paste0('data2/aranks_v', 2, '.csv'))
write.csv(uranks2, paste0('data2/uranks_v', 2, '.csv'))

```

LS0tDQp0aXRsZTogIlJlY29uc2lkZXJpbmcgYW5pbWUgcmFua2luZ3MiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpNeWFuaW1lbGlzdCBjb2xsZWN0cyB0aGUgcmF0aW5ncyBvZiBzZXZlcmFsIG1pbGxpb24gdXNlcnMsIGFuZCB1c2VzIHRob3NlIHRvIG1ha2UNCmFuIG92ZXJhbGwgYW5pbWUgcmFuay4gVGhlIHByb2JsZW0gd2l0aCB0aGlzIG1ldGhvZCBpcyB0aGF0IHNvbWUgcGVvcGxlIHN5c3RlbWF0aWNhbGx5DQpnaXZlIGhpZ2hlciByYXRpbmdzIHRoYW4gb3RoZXJzLCBhbmQgdGhvc2UgcGVvcGxlIG1pZ2h0IGJlIG1vcmUgbGlrZWx5IHRvIHdhdGNoIGNlcnRhaW4NCnNob3dzLiBDb252ZXJzZWx5LCB0aGVyZSBpcyB0aGUgc2FtZSBpc3N1ZSBvbiB0aGUgdXNlciBzaWRlLCB3aGVyZSBzb21lIHBlb3BsZSBtaWdodA0KZGVsaWJlcmF0ZWx5IHNlYXJjaCBvdXQgc2hvd3Mgd2l0aCBoaWdoZXIgcmF0aW5ncyB0aGFuIG90aGVycy4NCg0KTm9ybWFsbHkgdGhpcyBpc3N1ZSB3b3VsZCBiZSBzb2x2ZWQgd2l0aCByZWdyZXNzaW9uLCB3aGljaCBjYW5ub3QgYmUgZG9uZSBoZXJlDQpiZWNhdXNlIHRoZXJlIGFyZSAxMCwwMDAgZGlmZmVyZW50IHRpdGxlcyBhbmQgMSwwMDAsMDAwIGRpZmZlcmVudCB1c2VycywgbWFraW5nDQppdCBpbXBvc3NpYmxlIHRvIHJ1biB0aGUgYWxnb3JpdGhtIGR1ZSB0byBtZW1vcnkgY29uc3RyYWludHMuIEluc3RlYWQsIEkgdXNlZCBhbiBpbnRlcmF0ZWQNCm1ldGhvZCB3aGVyZSBJIG9ic2VydmVkIHdoaWNoIHJhdGVycyBnYXZlIGhpZ2hlciByYXRpbmdzIHRoYW4gb3RoZXJzIGNvbnRyb2xsaW5nDQpmb3IgdGhlIGFuaW1lIHRoZXkgd2F0Y2hlZCwgYW5kIHdoYXQgYW5pbWUgcmVjZWl2ZWQgaGlnaGVyIHJhdGluZ3MgdGhhbiBvdGhlcnMNCmNvbnRyb2xsaW5nIGZvciB0aGUgdXNlcnMgd2hvIHJhdGVkIHRoZW0uIEJlY2F1c2UgdGhlIGNvbmZvdW5kaW5nIGdvZXMgYm90aCB3YXlzLA0Kc28gSSBoYWQgdG8gcmVwZWF0IHRoZSBtZXRob2QgdG8gZ2V0IHJvdWdobHkgcmVsaWFibGUgcmVzdWx0cy4NCg0KIyMgTG9hZGluZyBhbmQgY2xlYW5pbmcgZGF0YQ0KDQoNCmBgYHtyfQ0Kc2V0d2QoJ34nKQ0Kc2V0d2QoJ3Jmb2xkZXIvYW5pbWUnKQ0KYW5pbWVpZHMgPC0gcmVhZC5jc3YoJ2RhdGEvYW5pbWUuY3N2Jywgc2VwPSdcdCcpDQojb3JpZ2luYWwgYW5pbWUgcmFua3MNCm9ncmFua3MgPC0gcmVhZC5jc3YoJ2RhdGEyL2FyYW5rc192MS5jc3YnLCBzZXA9JywnKQ0KI2ZpcnN0IGl0ZXJhdGlvbg0KYXJhbmtzMiA8LSByZWFkLmNzdignZGF0YTIvYXJhbmtzX3YyLmNzdicsIHNlcD0nLCcpDQojZmlmdGggaXRlcmF0aW9uDQphcmFua3M1IDwtIHJlYWQuY3N2KCdkYXRhMi9hcmFua3NfdjYuY3N2Jywgc2VwPScsJykNCnNlcXVlbCA8LSByZWFkLmNzdignZGF0YTIvc2VxdWVscy5jc3YnLCBzZXA9JywnKSAlPiUgc2VsZWN0KGFuaW1lX2lkLCBTZXF1ZWwpDQojdGVudGggaXRlcmF0aW9uDQphcmFua3MxMCA8LSByZWFkLmNzdignZGF0YTIvYXJhbmtzX3YxMS5jc3YnLCBzZXA9JywnKQ0KYW5pbWVpZHMgPC0gcmVhZC5jc3YoJ2RhdGEvYW5pbWUuY3N2Jywgc2VwPSdcdCcpDQpgYGANCg0KYGBge3J9DQpuYW1lcyA8LSBhbmltZWlkcyAlPiUgc2VsZWN0KGFuaW1lX2lkLCB0aXRsZSkNCg0Kb3JpZ2luYWxfcmFua3MgPC0gbGVmdF9qb2luKG9ncmFua3MsIG5hbWVzLCBieT0nYW5pbWVfaWQnKSAlPiUgYXJyYW5nZSgtYW5pbWVzY29yZSkNCmZpcnN0X2l0ZXJhdGlvbiA8LSBsZWZ0X2pvaW4oYXJhbmtzMiwgbmFtZXMsIGJ5PSdhbmltZV9pZCcpICU+JSBhcnJhbmdlKC1hbmltZXNjb3JlKQ0KZmlmdGhfaXRlcmF0aW9uIDwtIGxlZnRfam9pbihhcmFua3M1LCBuYW1lcywgYnk9J2FuaW1lX2lkJykgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpDQp0ZW50aF9pdGVyYXRpb24gPC0gbGVmdF9qb2luKGFyYW5rczEwLCBuYW1lcywgYnk9J2FuaW1lX2lkJykgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpDQoNCm1lYW4ob3JpZ2luYWxfcmFua3MkYW5pbWVzY29yZSwgbmEucm09VCkNCnNkKG9yaWdpbmFsX3JhbmtzJGFuaW1lc2NvcmUsIG5hLnJtPVQpDQoNCmZpcnN0X2l0ZXJhdGlvbiRhbmltZXNjb3JlID0gbm9ybWFsaXNlKGZpcnN0X2l0ZXJhdGlvbiRhbmltZXNjb3JlKSoxLjA1MjIyNCs2LjI4NDcxMg0KZmlmdGhfaXRlcmF0aW9uJGFuaW1lc2NvcmUgPSBub3JtYWxpc2UoZmlmdGhfaXRlcmF0aW9uJGFuaW1lc2NvcmUpKjEuMDUyMjI0KzYuMjg0NzEyDQp0ZW50aF9pdGVyYXRpb24kYW5pbWVzY29yZSA9IG5vcm1hbGlzZSh0ZW50aF9pdGVyYXRpb24kYW5pbWVzY29yZSkqMS4wNTIyMjQrNi4yODQ3MTINCg0KZmlyc3RfaXRlcmF0aW9uJGFuaW1lc2NvcmUxID0gZmlyc3RfaXRlcmF0aW9uJGFuaW1lc2NvcmUNCmZpZnRoX2l0ZXJhdGlvbiRhbmltZXNjb3JlNSA9IGZpZnRoX2l0ZXJhdGlvbiRhbmltZXNjb3JlDQp0ZW50aF9pdGVyYXRpb24kYW5pbWVzY29yZTEwID0gdGVudGhfaXRlcmF0aW9uJGFuaW1lc2NvcmUNCg0KbmV3ZGF0YSA8LSBsZWZ0X2pvaW4ob3JpZ2luYWxfcmFua3MsIGZpcnN0X2l0ZXJhdGlvbiAlPiUgc2VsZWN0KGFuaW1lX2lkLCBhbmltZXNjb3JlMSksIGJ5PSdhbmltZV9pZCcpICU+JSBhcnJhbmdlKC1hbmltZXNjb3JlKQ0KbmV3ZGF0YSA8LSBsZWZ0X2pvaW4obmV3ZGF0YSwgZmlmdGhfaXRlcmF0aW9uICU+JSBzZWxlY3QoYW5pbWVfaWQsIGFuaW1lc2NvcmU1KSwgYnk9J2FuaW1lX2lkJykgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpDQpuZXdkYXRhIDwtIGxlZnRfam9pbihuZXdkYXRhLCB0ZW50aF9pdGVyYXRpb24gJT4lIHNlbGVjdChhbmltZV9pZCwgYW5pbWVzY29yZTEwKSwgYnk9J2FuaW1lX2lkJykgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpDQpuZXdkYXRhIDwtIGxlZnRfam9pbihuZXdkYXRhLCBzZXF1ZWwgJT4lIHNlbGVjdChhbmltZV9pZCwgU2VxdWVsKSwgYnk9J2FuaW1lX2lkJykgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpDQpuZXdkYXRhJGFkdiA8LSBuZXdkYXRhJGFuaW1lc2NvcmUxMCAtIG5ld2RhdGEkYW5pbWVzY29yZQ0KYGBgDQoNCkNvcnJlbGF0aW9uIG1hdHJpeC4gQ29ycmVsYXRpb24gYmV0d2VlbiBmaWZ0aCBpdGVyYXRpb24gcmF0aW5ncyBhbmQgdGVudGggaXRlcmF0aW9uIGlzDQphYm91dCAuOTk2Lg0KDQpgYGB7cn0NCmNvcnJlbGF0aW9uX21hdHJpeChuZXdkYXRhICU+JSBzZWxlY3QoYW5pbWVzY29yZSwgYW5pbWVzY29yZTEsIGFuaW1lc2NvcmU1LCBhbmltZXNjb3JlMTApKQ0KYGBgDQoNCiMjIFRoZSByYW5raW5ncw0KDQpLaWNraW5nIG91dCBhbmltZSB3aXRoIGxlc3MgdGhhbiA1ayByYXRpbmdzDQpgYGB7cn0NCm5ld2RhdGEkb3JpZ2luYWxyYW5rIDwtIHJhbmtlcihuZXdkYXRhJGFuaW1lc2NvcmUpDQpuZXdkYXRhJG5ld3JhbmsgPC0gcmFua2VyKG5ld2RhdGEkYW5pbWVzY29yZTEwKQ0KbmV3ZGF0YTIgPC0gbmV3ZGF0YSAlPiUgZmlsdGVyKGFuID4gNDk5OSkNCmBgYA0KDQpUb3AgMjAgZm9yIGVhY2ggY2F0ZWdvcnkuIGFuaW1lc2NvcmUgLS0gb3JpZ2luYWwgcmF0aW5nLCBhbmltZXNjb3JlMTAgLS0gdXNlciBhZGp1c3RlZCByYXRpbmcuIA0KYGBge3J9DQpvcmlnaW5hbCA8LSBuZXdkYXRhMiAlPiUgZmlsdGVyKFNlcXVlbD09MCkNCnNlcXVlbCA8LSBuZXdkYXRhMiAlPiUgZmlsdGVyKFNlcXVlbD09MSkNCg0KaGVhZChuZXdkYXRhMiAlPiUgZmlsdGVyKFNlcXVlbD09MSkgJT4lIHNlbGVjdCh0aXRsZSwgYW5pbWVzY29yZSkgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpLCBuPTIwKQ0KaGVhZChuZXdkYXRhMiAlPiUgZmlsdGVyKFNlcXVlbD09MSkgJT4lIHNlbGVjdCh0aXRsZSwgYW5pbWVzY29yZTEwKSAlPiUgYXJyYW5nZSgtYW5pbWVzY29yZTEwKSwgbj0yMCkNCg0KaGVhZChuZXdkYXRhMiAlPiUgZmlsdGVyKFNlcXVlbD09MCkgJT4lIHNlbGVjdCh0aXRsZSwgYW5pbWVzY29yZSkgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpLCBuPTIwKQ0KaGVhZChuZXdkYXRhMiAlPiUgZmlsdGVyKFNlcXVlbD09MCkgJT4lIHNlbGVjdCh0aXRsZSwgYW5pbWVzY29yZTEwKSAlPiUgYXJyYW5nZSgtYW5pbWVzY29yZTEwKSwgbj0yMCkNCg0KaGVhZChuZXdkYXRhMiAlPiUgZmlsdGVyKFNlcXVlbD09MCkgJT4lIHNlbGVjdCh0aXRsZSwgYWR2KSAlPiUgYXJyYW5nZSgtYWR2KSwgbj0yMCkNCmhlYWQobmV3ZGF0YTIgJT4lIGZpbHRlcihTZXF1ZWw9PTApICU+JSBzZWxlY3QodGl0bGUsIGFkdikgJT4lIGFycmFuZ2UoYWR2KSwgbj0yMCkNCg0KaGVhZChuZXdkYXRhMiAlPiUgZmlsdGVyKFNlcXVlbD09MSkgJT4lIHNlbGVjdCh0aXRsZSwgYWR2KSAlPiUgYXJyYW5nZSgtYWR2KSwgbj0yMCkNCmhlYWQobmV3ZGF0YTIgJT4lIGZpbHRlcihTZXF1ZWw9PTEpICU+JSBzZWxlY3QodGl0bGUsIGFkdikgJT4lIGFycmFuZ2UoYWR2KSwgbj0yMCkNCmBgYA0KDQpTb21lIG5vdGVzOg0KLSBUcmFkaXRpb25hbGx5ICJlbGl0aXN0IiBhbmltZSBsaWtlIEFzaGl0YSBubyBKb2UsIEthaWJhLCBhbmQgTGVnZW5kIG9mIHRoZSBHYWxhY3RpYw0KSGVyb2VzIGdhaW4gdGhlIG1vc3QgZnJvbSB0aGUgdXNlci1hZGp1c3RlZCBtZXRob2QsIGFzIHRoZSBraW5kIG9mIHBlb3BsZSB3aG8NCmFyZSBwcmVkaXNwb3NlZCB0byB3YXRjaGluZyB0aGVtIGFyZSBoYXJzaGVyIHJhdGVycy4NCi0gQW5pbWUgd2F0Y2hlZCBieSBtZW4gdGVuZCB0byBnYWluIG1vcmUgZnJvbSB1c2VyLWFkanVzdG1lbnQsIGFuZCB2aWNlIHZlcnNhLg0KLSBMZWdlbmQgb2YgdGhlIEdhbGFjdGljIEhlcm9lcyBnb2VzIGZyb20gdG9wIDQgdG8gaW5kaXNwdXRhYmx5IG51bWJlciAxIGFmdGVyDQphZGp1c3RpbmcgZm9yIHVzZXIgcmF0aW5ncy4NCg0KDQpUaGlzIGlzIHRoZSBjb2RlIHNuaXBwZXQgSSB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgdXNlci1hZGp1c3RlZCByYXRpbmdzLg0KSSBoYWQgdG8gcnVuIDEwIGl0ZXJhdGlvbnMgc2VwYXJhdGVseSAobm8gbG9vcHMpIG9mIGl0IGR1ZSB0byBtZW1vcnkgbGltaXRhdGlvbnMuDQoNCmBgYHtyfQ0Kc2V0d2QoJ34nKQ0Kc2V0d2QoJ0RvY3VtZW50cy9yc3R1ZmYvYW5pbWUnKQ0KYW5pbWVpZHMgPC0gcmVhZC5jc3YoJ2RhdGEvYW5pbWUuY3N2Jywgc2VwPSdcdCcpDQp0ZXN0IDwtIHJlYWQuY3N2KCdkYXRhMi9mdWxsZmlsZXYxLmNzdicsIHNlcD0nLCcpDQpyYW5rc3YzIDwtIHJlYWQuY3N2KCdkYXRhMi9yYW5rc192My5jc3YnLCBzZXA9JywnKQ0KDQpuYW1lcyA8LSBhbmltZWlkcyAlPiUgc2VsZWN0KGFuaW1lX2lkLCB0aXRsZSkNCg0KIyMjIyMjIyMzbmFpdmUgbWVhbnMNCm0gPC0gdGVzdCAlPiUgZ3JvdXBfYnkoYW5pbWVfaWQpICU+JSBzdW1tYXJpc2UoYW5pbWVzY29yZT1tZWFuKHNjb3JlLCBuYS5ybT1UKSwgbj1uKCkpDQptMiA8LSB0ZXN0ICU+JSBncm91cF9ieSh1c2VyX2lkKSAlPiUgc3VtbWFyaXNlKHVzZXJzY29yZT1tZWFuKHNjb3JlLCBuYS5ybT1UKSwgbj1uKCkpDQoNCnJhbmtzIDwtIGxlZnRfam9pbihtLCBuYW1lcywgYnk9J2FuaW1lX2lkJykgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpDQp1cmFua3MgPC0gbTIgJT4lIGFycmFuZ2UoLXVzZXJzY29yZSkNCg0KIyMjIyMjIyMjIyMjIyMNCnRlc3QyIDwtIHRlc3QNCnRlc3QyJGFzY29yZTIgPC0gdGVzdDIkc2NvcmUNCnRlc3QyJHVzY29yZTIgPC0gdGVzdDIkc2NvcmUNCnRlc3QyJHNjb3JlMiA8LSB0ZXN0MiRzY29yZQ0KDQojIyMjIyMjIzMNCnVyYW5rcyA8LSB0ZXN0MiAlPiUgZ3JvdXBfYnkodXNlcl9pZCkgJT4lIHN1bW1hcmlzZSh1c2Vyc2NvcmU9bWVhbih1c2NvcmUyLCBuYS5ybT1UKSwgdW49bigpKSAlPiUgYXJyYW5nZSgtdXNlcnNjb3JlKQ0KYXJhbmtzIDwtIHRlc3QyICU+JSBncm91cF9ieShhbmltZV9pZCkgJT4lIHN1bW1hcmlzZShhbmltZXNjb3JlPW1lYW4oYXNjb3JlMiwgbmEucm09VCksIGFuPW4oKSkgJT4lIGFycmFuZ2UoLWFuaW1lc2NvcmUpDQp3cml0ZS5jc3YoYXJhbmtzLCBwYXN0ZTAoJ2RhdGEyL2FyYW5rc192JywgMSwgJy5jc3YnKSkNCndyaXRlLmNzdih1cmFua3MsIHBhc3RlMCgnZGF0YTIvdXJhbmtzX3YnLCAxLCAnLmNzdicpKQ0KDQp0ZXN0MiA8LSBsZWZ0X2pvaW4odGVzdDIsIHVyYW5rcyAlPiUgc2VsZWN0KHVzZXJfaWQsIHVzZXJzY29yZSwgdW4pLCBieT0ndXNlcl9pZCcpDQp0ZXN0MiA8LSBsZWZ0X2pvaW4odGVzdDIsIGFyYW5rcyAlPiUgc2VsZWN0KGFuaW1lX2lkLCBhbmltZXNjb3JlLCBhbiksIGJ5PSdhbmltZV9pZCcpDQp0ZXN0MiRhZGlmZiA8LSB0ZXN0MiRzY29yZTItdGVzdDIkdXNlcnNjb3JlDQp0ZXN0MiR1ZGlmZiA8LSB0ZXN0MiRzY29yZTItdGVzdDIkYW5pbWVzY29yZQ0KDQp0ZXN0MiRhc2NvcmUyIDwtIHRlc3QyJGFkaWZmK21lYW4oYXJhbmtzJGFuaW1lc2NvcmUsIG5hLnJtPVQpDQp0ZXN0MiR1c2NvcmUyIDwtIHRlc3QyJHVkaWZmK21lYW4odXJhbmtzJHVzZXJzY29yZSwgbmEucm09VCkNCg0KYXJhbmtzMiA8LSB0ZXN0MiAlPiUgZ3JvdXBfYnkoYW5pbWVfaWQpICU+JSBzdW1tYXJpc2UoYW5pbWVzY29yZT1tZWFuKGFzY29yZTIsIG5hLnJtPVQpLCBhbj1uKCkpICU+JSBhcnJhbmdlKC1hbmltZXNjb3JlKQ0KdXJhbmtzMiA8LSB0ZXN0MiAlPiUgZ3JvdXBfYnkodXNlcl9pZCkgJT4lIHN1bW1hcmlzZSh1c2Vyc2NvcmU9bWVhbih1c2NvcmUyLCBuYS5ybT1UKSwgdW49bigpKSAlPiUgYXJyYW5nZSgtdXNlcnNjb3JlKQ0KYXJhbmtzMiA8LSBsZWZ0X2pvaW4obmFtZXMsIGFyYW5rczIgJT4lIHNlbGVjdChhbmltZV9pZCwgYW5pbWVzY29yZSwgYW4pLCBieT0nYW5pbWVfaWQnKQ0KDQp3cml0ZS5jc3YoYXJhbmtzMiwgcGFzdGUwKCdkYXRhMi9hcmFua3NfdicsIDIsICcuY3N2JykpDQp3cml0ZS5jc3YodXJhbmtzMiwgcGFzdGUwKCdkYXRhMi91cmFua3NfdicsIDIsICcuY3N2JykpDQpgYGANCg0KDQoNCg==