Filtering and plotting
Then we define a function that can be used to filter the dataset to a particular encounter and difficulty. It also performs outlier filtering - for each 5-ilvl bucket, for each spec, we compute the 95th percentile of DPS, then remove any parses from the dataset for that ilvl/spec above that cutoff.
get_encounter_data <- function(encounter_name, difficulty_name, dataset, cutoff=c(0.05, 0.95), bucket_size=1, min_ilvl=855, max_ilvl=875) {
specs <- unique(dataset$spec)
d <- filter(dataset, ilvl >= min_ilvl, ilvl <= max_ilvl, encounter == encounter_name, difficulty == difficulty_name, date >= cutoff_date)
for(bucket in seq(800, 900, by=bucket_size)) {
for(f_spec in specs) {
q <- quantile(d[d$spec == f_spec & d$ilvl >= bucket & d$ilvl < bucket + bucket_size,]$dps, cutoff)
if(!is.na(q[1])) {
d <- filter(d, !(spec == f_spec & ilvl >= bucket & ilvl < bucket + bucket_size & (dps > q[2] | dps < q[1])))
}
}
}
return(d)
}
Plotting the data is straightforward. Here we plot both boxplots for each spec/ilvl bucket, as well as a smoothed moving average and a linear trendline.
plot_encounter <- function(encounter_name, difficulty_name, dataset=data, bucket_size=2, cutoff=c(0.05, 0.95)) {
encounter_data <- get_encounter_data(encounter_name, difficulty_name, dataset=dataset, bucket_size=bucket_size, cutoff=cutoff)
long_data <- melt(encounter_data, id.vars=c("spec", "ilvl"), measure.vars=c("dps"))
p <- ggplot(data=long_data, aes(x=ilvl, y=value, group=spec, colour=spec)) +
geom_boxplot(aes(group=interaction(spec, floor(ilvl/bucket_size) * bucket_size))) +
geom_smooth() + # loess fit
geom_smooth(linetype = "dashed", method = "lm", se = FALSE) + # linear fit
labs(y="DPS", x="iLevel", title=paste("Performance:", encounter_name, difficulty_name))
print(p)
cat('\r\n\r\n')
p <- ggplot(data=long_data, aes(x=ilvl, group=spec, colour=spec, fill=spec)) + geom_bar(position="dodge") +
labs(y="Parses", x="iLevel", title=paste("Parses:", encounter_name, difficulty_name))
print(p)
cat('\r\n\r\n')
}
Results
plot_encounter("Nythendra", "Normal")
plot_encounter("Nythendra", "Heroic")
plot_encounter("Nythendra", "Mythic")
plot_encounter("Ursoc", "Normal")
plot_encounter("Ursoc", "Heroic")
plot_encounter("Ursoc", "Mythic")
plot_encounter("Elerethe_Renferal", "Normal")
plot_encounter("Elerethe_Renferal", "Heroic")
plot_encounter("Elerethe_Renferal", "Mythic")
plot_encounter("Il_gynoth__Heart_of_Corruption", "Normal")
plot_encounter("Il_gynoth__Heart_of_Corruption", "Heroic")
plot_encounter("Il_gynoth__Heart_of_Corruption", "Mythic")
plot_encounter("Dragons_of_Nightmare", "Normal")
plot_encounter("Dragons_of_Nightmare", "Heroic")
plot_encounter("Dragons_of_Nightmare", "Mythic")
plot_encounter("Cenarius", "Normal")
plot_encounter("Cenarius", "Heroic")
plot_encounter("Cenarius", "Mythic")
plot_encounter("Xavius", "Normal")
plot_encounter("Xavius", "Heroic")
plot_encounter("Xavius", "Mythic")
Scaling
So we have the raw (smoothed) damage progressions, but how about scaling?
plot_dps_gain <- function(encounter, difficulty, dataset=data, bucket_size=1, cutoff=c(0.05, 0.95)) {
d <- dataset[dataset$encounter == encounter & dataset$difficulty == difficulty & dataset$ilvl <= 870 & dataset$date >= cutoff_date, ]
d <- aggregate(d$dps, by=list(ilvl=d$ilvl, spec=d$spec), FUN=mean)
d$dps <- d$x
d <- d[order(d$ilvl),]
d$diff <- ave(d$dps, factor(d$spec), FUN=function(x) c(NA, diff(x)))
d <- filter(d, !is.na(diff))
ggplot(data=d, aes(x=ilvl, y=diff, group=interaction(spec, encounter), colour=spec)) + geom_smooth(se=FALSE, fullrange=TRUE) +
labs(y="DPS gain per tier", x="iLevel", title=paste(encounter, difficulty, sep = " ")) + coord_cartesian(ylim = c(0, 15000))
}
# Commented fights have too little data to produce anything useful
plot_dps_gain("Nythendra", "Normal")
plot_dps_gain("Nythendra", "Heroic")
plot_dps_gain("Nythendra", "Mythic")
plot_dps_gain("Ursoc", "Normal")
plot_dps_gain("Ursoc", "Heroic")
plot_dps_gain("Ursoc", "Mythic")
plot_dps_gain("Elerethe_Renferal", "Normal")
plot_dps_gain("Elerethe_Renferal", "Heroic")
plot_dps_gain("Elerethe_Renferal", "Mythic")
plot_dps_gain("Il_gynoth__Heart_of_Corruption", "Normal")
plot_dps_gain("Il_gynoth__Heart_of_Corruption", "Heroic")
# plot_dps_gain("il_gynoth__heart_of_corruption", "Mythic")
plot_dps_gain("Dragons_of_Nightmare", "Normal")
plot_dps_gain("Dragons_of_Nightmare", "Heroic")
# plot_dps_gain("Dragons_of_Nightmare", "Mythic")
plot_dps_gain("Cenarius", "Normal")
plot_dps_gain("Cenarius", "Heroic")
# plot_dps_gain("cenarius", "Mythic")
plot_dps_gain("Xavius", "Normal")
plot_dps_gain("Xavius", "Heroic")
# plot_dps_gain("xavius", "Mythic")
Finally, here’s a really blunt linear regression of each encounter, with the scaling factor (slope of the regression line) plotted:
d <- data
m <- d %>% group_by(spec, encounter) %>% do(tidy(lm(dps ~ ilvl, data=.)))
slopes <- m[m$term == "ilvl",]
ggplot(data=slopes, aes(x=encounter, y=estimate, group=interaction(spec, encounter), reorder=estimate, fill=spec)) + geom_bar(position="dodge", stat="identity") +
labs(y="DPS gain/ilvl", x="Encounter", title="DPS scaling per ilvl: Overall")
LS0tDQp0aXRsZTogIkRydWlkIHNwZWMgc2NhbGluZyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogDQogICAgZmlnX2hlaWdodDogNg0KICAgIGZpZ193aWR0aDogMTgNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KIyMgRmV0Y2hpbmcgdGhlIGRhdGENCg0KRGF0YSBpcyBncmFiYmVkIGZyb20gV2FyY3JhZnRMb2dzIHZpYSB0aGVpciBBUEkuIFdlIGdldCB0aGUgdG9wIDFrIHBhcnNlcyBwZXIgc3BlYy9lbmNvdW50ZXIvZGlmZmljdWx0eS4NCg0KU291cmNlIGlzIGF0IGh0dHBzOi8vZ2l0aHViLmNvbS9jaGVhbGQvd2FyY3JhZnRsb2dzLXBhcnNlcw0KDQpgYGANCiQgQVBJX0tFWT15b3VyX2tleV9oZXJlIHJ1YnkgZ3JhYmJlci5yYiBkcnVpZA0KYGBgDQoNCk9uY2Ugd2UgaGF2ZSB0aGUgSlNPTiBmaWxlcywgd2UgdXNlIGEgc21hbGwgc2NyaXB0IHRvIGJvaWwgdGhlbSBkb3duIGludG8gYSBDU1Y6DQoNCmBgYA0KJCBBUElfS0VZPXlvdXJfa2V5X2hlcmUgcnVieSBkaXN0aWxsLnJiIGRydWlkDQpgYGANCg0KVGhpcyByZXN1bHRzIGluIHRoZSBgY3N2L2RydWlkLmNzdmAgdGhhdCB3ZSB0aGVuIGNvbnN1bWUgaW4gUi4NCg0KT25jZSBpbiBSLCB3ZSBsb2FkIGl0IHVwOg0KDQpgYGB7cn0NCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShicm9vbSkNCmRhdGEgPC0gcmVhZC5jc3YoImNzdi9kcnVpZC5jc3YiKQ0KZGF0YSRkYXRlIDwtIGFzLkRhdGUoZGF0YSRkYXRlKQ0KY3V0b2ZmX2RhdGUgPSAiMjAxNi0xMC0xNSINCmBgYA0KDQojIyBGaWx0ZXJpbmcgYW5kIHBsb3R0aW5nDQoNClRoZW4gd2UgZGVmaW5lIGEgZnVuY3Rpb24gdGhhdCBjYW4gYmUgdXNlZCB0byBmaWx0ZXIgdGhlIGRhdGFzZXQgdG8gYSBwYXJ0aWN1bGFyIGVuY291bnRlciBhbmQgZGlmZmljdWx0eS4gSXQgYWxzbyBwZXJmb3JtcyBvdXRsaWVyIGZpbHRlcmluZyAtIGZvciBlYWNoIDUtaWx2bCBidWNrZXQsIGZvciBlYWNoIHNwZWMsIHdlIGNvbXB1dGUgdGhlIDk1dGggcGVyY2VudGlsZSBvZiBEUFMsIHRoZW4gcmVtb3ZlIGFueSBwYXJzZXMgZnJvbSB0aGUgZGF0YXNldCBmb3IgdGhhdCBpbHZsL3NwZWMgYWJvdmUgdGhhdCBjdXRvZmYuDQoNCmBgYHtyfQ0KDQpnZXRfZW5jb3VudGVyX2RhdGEgPC0gZnVuY3Rpb24oZW5jb3VudGVyX25hbWUsIGRpZmZpY3VsdHlfbmFtZSwgZGF0YXNldCwgY3V0b2ZmPWMoMC4wNSwgMC45NSksIGJ1Y2tldF9zaXplPTEsIG1pbl9pbHZsPTg1NSwgbWF4X2lsdmw9ODc1KSB7DQogIHNwZWNzIDwtIHVuaXF1ZShkYXRhc2V0JHNwZWMpDQogIGQgPC0gZmlsdGVyKGRhdGFzZXQsIGlsdmwgPj0gbWluX2lsdmwsIGlsdmwgPD0gbWF4X2lsdmwsIGVuY291bnRlciA9PSBlbmNvdW50ZXJfbmFtZSwgZGlmZmljdWx0eSA9PSBkaWZmaWN1bHR5X25hbWUsIGRhdGUgPj0gY3V0b2ZmX2RhdGUpDQogIGZvcihidWNrZXQgaW4gc2VxKDgwMCwgOTAwLCBieT1idWNrZXRfc2l6ZSkpIHsNCiAgICBmb3IoZl9zcGVjIGluIHNwZWNzKSB7DQogICAgICBxIDwtIHF1YW50aWxlKGRbZCRzcGVjID09IGZfc3BlYyAmIGQkaWx2bCA+PSBidWNrZXQgJiBkJGlsdmwgPCBidWNrZXQgKyBidWNrZXRfc2l6ZSxdJGRwcywgY3V0b2ZmKQ0KICAgICAgaWYoIWlzLm5hKHFbMV0pKSB7DQogICAgICAgIGQgPC0gZmlsdGVyKGQsICEoc3BlYyA9PSBmX3NwZWMgJiBpbHZsID49IGJ1Y2tldCAmIGlsdmwgPCBidWNrZXQgKyBidWNrZXRfc2l6ZSAmIChkcHMgPiBxWzJdIHwgZHBzIDwgcVsxXSkpKQ0KICAgICAgfQ0KICAgIH0NCiAgfQ0KICByZXR1cm4oZCkNCn0NCmBgYA0KDQpQbG90dGluZyB0aGUgZGF0YSBpcyBzdHJhaWdodGZvcndhcmQuIEhlcmUgd2UgcGxvdCBib3RoIGJveHBsb3RzIGZvciBlYWNoIHNwZWMvaWx2bCBidWNrZXQsIGFzIHdlbGwgYXMgYSBzbW9vdGhlZCBtb3ZpbmcgYXZlcmFnZSBhbmQgYSBsaW5lYXIgdHJlbmRsaW5lLg0KDQpgYGB7cn0NCnBsb3RfZW5jb3VudGVyIDwtIGZ1bmN0aW9uKGVuY291bnRlcl9uYW1lLCBkaWZmaWN1bHR5X25hbWUsIGRhdGFzZXQ9ZGF0YSwgYnVja2V0X3NpemU9MiwgY3V0b2ZmPWMoMC4wNSwgMC45NSkpIHsNCiAgZW5jb3VudGVyX2RhdGEgPC0gZ2V0X2VuY291bnRlcl9kYXRhKGVuY291bnRlcl9uYW1lLCBkaWZmaWN1bHR5X25hbWUsIGRhdGFzZXQ9ZGF0YXNldCwgYnVja2V0X3NpemU9YnVja2V0X3NpemUsIGN1dG9mZj1jdXRvZmYpDQoNCiAgbG9uZ19kYXRhIDwtIG1lbHQoZW5jb3VudGVyX2RhdGEsIGlkLnZhcnM9Yygic3BlYyIsICJpbHZsIiksIG1lYXN1cmUudmFycz1jKCJkcHMiKSkNCg0KICBwIDwtIGdncGxvdChkYXRhPWxvbmdfZGF0YSwgYWVzKHg9aWx2bCwgeT12YWx1ZSwgZ3JvdXA9c3BlYywgY29sb3VyPXNwZWMpKSArDQogICAgZ2VvbV9ib3hwbG90KGFlcyhncm91cD1pbnRlcmFjdGlvbihzcGVjLCBmbG9vcihpbHZsL2J1Y2tldF9zaXplKSAqIGJ1Y2tldF9zaXplKSkpICsNCiAgICBnZW9tX3Ntb290aCgpICsgIyBsb2VzcyBmaXQNCiAgICBnZW9tX3Ntb290aChsaW5ldHlwZSA9ICJkYXNoZWQiLCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKSArICMgbGluZWFyIGZpdA0KICAgIGxhYnMoeT0iRFBTIiwgeD0iaUxldmVsIiwgdGl0bGU9cGFzdGUoIlBlcmZvcm1hbmNlOiIsIGVuY291bnRlcl9uYW1lLCBkaWZmaWN1bHR5X25hbWUpKQ0KICBwcmludChwKQ0KICBjYXQoJ1xyXG5cclxuJykNCiAgDQogIHAgPC0gZ2dwbG90KGRhdGE9bG9uZ19kYXRhLCBhZXMoeD1pbHZsLCBncm91cD1zcGVjLCBjb2xvdXI9c3BlYywgZmlsbD1zcGVjKSkgKyBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiKSArDQogICAgbGFicyh5PSJQYXJzZXMiLCB4PSJpTGV2ZWwiLCB0aXRsZT1wYXN0ZSgiUGFyc2VzOiIsIGVuY291bnRlcl9uYW1lLCBkaWZmaWN1bHR5X25hbWUpKQ0KICBwcmludChwKQ0KICBjYXQoJ1xyXG5cclxuJykNCn0NCmBgYA0KDQojIyBSZXN1bHRzDQoNCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KDQpwbG90X2VuY291bnRlcigiTnl0aGVuZHJhIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiTnl0aGVuZHJhIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiTnl0aGVuZHJhIiwgIk15dGhpYyIpDQoNCnBsb3RfZW5jb3VudGVyKCJVcnNvYyIsICJOb3JtYWwiKQ0KcGxvdF9lbmNvdW50ZXIoIlVyc29jIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiVXJzb2MiLCAiTXl0aGljIikNCg0KcGxvdF9lbmNvdW50ZXIoIkVsZXJldGhlX1JlbmZlcmFsIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiRWxlcmV0aGVfUmVuZmVyYWwiLCAiSGVyb2ljIikNCnBsb3RfZW5jb3VudGVyKCJFbGVyZXRoZV9SZW5mZXJhbCIsICJNeXRoaWMiKQ0KDQpwbG90X2VuY291bnRlcigiSWxfZ3lub3RoX19IZWFydF9vZl9Db3JydXB0aW9uIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiSWxfZ3lub3RoX19IZWFydF9vZl9Db3JydXB0aW9uIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiSWxfZ3lub3RoX19IZWFydF9vZl9Db3JydXB0aW9uIiwgIk15dGhpYyIpDQoNCnBsb3RfZW5jb3VudGVyKCJEcmFnb25zX29mX05pZ2h0bWFyZSIsICJOb3JtYWwiKQ0KcGxvdF9lbmNvdW50ZXIoIkRyYWdvbnNfb2ZfTmlnaHRtYXJlIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiRHJhZ29uc19vZl9OaWdodG1hcmUiLCAiTXl0aGljIikNCg0KcGxvdF9lbmNvdW50ZXIoIkNlbmFyaXVzIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiQ2VuYXJpdXMiLCAiSGVyb2ljIikNCnBsb3RfZW5jb3VudGVyKCJDZW5hcml1cyIsICJNeXRoaWMiKQ0KDQpwbG90X2VuY291bnRlcigiWGF2aXVzIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiWGF2aXVzIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiWGF2aXVzIiwgIk15dGhpYyIpDQpgYGANCg0KIyMjIFNjYWxpbmcNCg0KU28gd2UgaGF2ZSB0aGUgcmF3IChzbW9vdGhlZCkgZGFtYWdlIHByb2dyZXNzaW9ucywgYnV0IGhvdyBhYm91dCBzY2FsaW5nPyANCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KcGxvdF9kcHNfZ2FpbiA8LSBmdW5jdGlvbihlbmNvdW50ZXIsIGRpZmZpY3VsdHksIGRhdGFzZXQ9ZGF0YSwgYnVja2V0X3NpemU9MSwgY3V0b2ZmPWMoMC4wNSwgMC45NSkpIHsNCiAgZCA8LSBkYXRhc2V0W2RhdGFzZXQkZW5jb3VudGVyID09IGVuY291bnRlciAmIGRhdGFzZXQkZGlmZmljdWx0eSA9PSBkaWZmaWN1bHR5ICYgZGF0YXNldCRpbHZsIDw9IDg3MCAmIGRhdGFzZXQkZGF0ZSA+PSBjdXRvZmZfZGF0ZSwgXQ0KICBkIDwtIGFnZ3JlZ2F0ZShkJGRwcywgYnk9bGlzdChpbHZsPWQkaWx2bCwgc3BlYz1kJHNwZWMpLCBGVU49bWVhbikNCiAgZCRkcHMgPC0gZCR4DQogIGQgPC0gZFtvcmRlcihkJGlsdmwpLF0NCiAgZCRkaWZmIDwtIGF2ZShkJGRwcywgZmFjdG9yKGQkc3BlYyksIEZVTj1mdW5jdGlvbih4KSBjKE5BLCBkaWZmKHgpKSkNCiAgZCA8LSBmaWx0ZXIoZCwgIWlzLm5hKGRpZmYpKQ0KICBnZ3Bsb3QoZGF0YT1kLCBhZXMoeD1pbHZsLCB5PWRpZmYsIGdyb3VwPWludGVyYWN0aW9uKHNwZWMsIGVuY291bnRlciksIGNvbG91cj1zcGVjKSkgKyBnZW9tX3Ntb290aChzZT1GQUxTRSwgZnVsbHJhbmdlPVRSVUUpICsNCiAgICBsYWJzKHk9IkRQUyBnYWluIHBlciB0aWVyIiwgeD0iaUxldmVsIiwgdGl0bGU9cGFzdGUoZW5jb3VudGVyLCBkaWZmaWN1bHR5LCBzZXAgPSAiICIpKSArIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAxNTAwMCkpDQp9DQoNCiMgQ29tbWVudGVkIGZpZ2h0cyBoYXZlIHRvbyBsaXR0bGUgZGF0YSB0byBwcm9kdWNlIGFueXRoaW5nIHVzZWZ1bA0KDQpwbG90X2Rwc19nYWluKCJOeXRoZW5kcmEiLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oIk55dGhlbmRyYSIsICJIZXJvaWMiKQ0KcGxvdF9kcHNfZ2FpbigiTnl0aGVuZHJhIiwgIk15dGhpYyIpDQoNCnBsb3RfZHBzX2dhaW4oIlVyc29jIiwgIk5vcm1hbCIpDQpwbG90X2Rwc19nYWluKCJVcnNvYyIsICJIZXJvaWMiKQ0KcGxvdF9kcHNfZ2FpbigiVXJzb2MiLCAiTXl0aGljIikNCg0KcGxvdF9kcHNfZ2FpbigiRWxlcmV0aGVfUmVuZmVyYWwiLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oIkVsZXJldGhlX1JlbmZlcmFsIiwgIkhlcm9pYyIpDQpwbG90X2Rwc19nYWluKCJFbGVyZXRoZV9SZW5mZXJhbCIsICJNeXRoaWMiKQ0KDQpwbG90X2Rwc19nYWluKCJJbF9neW5vdGhfX0hlYXJ0X29mX0NvcnJ1cHRpb24iLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oIklsX2d5bm90aF9fSGVhcnRfb2ZfQ29ycnVwdGlvbiIsICJIZXJvaWMiKQ0KIyBwbG90X2Rwc19nYWluKCJpbF9neW5vdGhfX2hlYXJ0X29mX2NvcnJ1cHRpb24iLCAiTXl0aGljIikNCg0KcGxvdF9kcHNfZ2FpbigiRHJhZ29uc19vZl9OaWdodG1hcmUiLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oIkRyYWdvbnNfb2ZfTmlnaHRtYXJlIiwgIkhlcm9pYyIpDQojIHBsb3RfZHBzX2dhaW4oIkRyYWdvbnNfb2ZfTmlnaHRtYXJlIiwgIk15dGhpYyIpDQoNCnBsb3RfZHBzX2dhaW4oIkNlbmFyaXVzIiwgIk5vcm1hbCIpDQpwbG90X2Rwc19nYWluKCJDZW5hcml1cyIsICJIZXJvaWMiKQ0KIyBwbG90X2Rwc19nYWluKCJjZW5hcml1cyIsICJNeXRoaWMiKQ0KDQpwbG90X2Rwc19nYWluKCJYYXZpdXMiLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oIlhhdml1cyIsICJIZXJvaWMiKQ0KIyBwbG90X2Rwc19nYWluKCJ4YXZpdXMiLCAiTXl0aGljIikNCg0KYGBgDQoNCkZpbmFsbHksIGhlcmUncyBhIHJlYWxseSBibHVudCBsaW5lYXIgcmVncmVzc2lvbiBvZiBlYWNoIGVuY291bnRlciwgd2l0aCB0aGUgc2NhbGluZyBmYWN0b3IgKHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIGxpbmUpIHBsb3R0ZWQ6DQoNCmBgYHtyLCBmaWcud2lkdGg9MTZ9DQpkIDwtIGRhdGENCm0gPC0gZCAlPiUgZ3JvdXBfYnkoc3BlYywgZW5jb3VudGVyKSAlPiUgZG8odGlkeShsbShkcHMgfiBpbHZsLCBkYXRhPS4pKSkNCnNsb3BlcyA8LSBtW20kdGVybSA9PSAiaWx2bCIsXQ0KZ2dwbG90KGRhdGE9c2xvcGVzLCBhZXMoeD1lbmNvdW50ZXIsIHk9ZXN0aW1hdGUsIGdyb3VwPWludGVyYWN0aW9uKHNwZWMsIGVuY291bnRlciksIHJlb3JkZXI9ZXN0aW1hdGUsIGZpbGw9c3BlYykpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnMoeT0iRFBTIGdhaW4vaWx2bCIsIHg9IkVuY291bnRlciIsIHRpdGxlPSJEUFMgc2NhbGluZyBwZXIgaWx2bDogT3ZlcmFsbCIpDQpgYGANCg==