I was curious about the relative scaling potential of the three rogue specs. We know from sims that Assassination is king in the 850-880 range, and that Sub is expected to surpass it after that. Outlaw is not expected to scale as well as either of the other specs. I figured, why not look at the data and see what shakes out?
Fetching the data
Data is grabbed from WarcraftLogs via their API. We get the top 1k parses per spec/encounter/difficulty.
Source is at https://github.com/cheald/warcraftlogs-parses
$ API_KEY=your_key_here ruby grabber.rb rogue
Once we have the JSON files, we use a small script to boil them down into a CSV:
$ API_KEY=your_key_here ruby distill.rb rogue
This results in the csv/rogue.csv
that we then consume in R.
Once in R, we load it up:
library(reshape2)
library(ggplot2)
library(dplyr)
package dplyr was built under R version 3.3.2
Attaching package: dplyr
The following objects are masked from package:stats:
filter, lag
The following objects are masked from package:base:
intersect, setdiff, setequal, union
data <- read.csv("csv/rogue.csv")
data$date <- as.Date(data$date)
cutoff_date = "2016-10-15"
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, 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')
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')
}
Results
In general, this shows what we generally suspect: Assassination is the strongest rogue spec for prolonged single-target fights, while Outlaw is only preferable on heavy multi-target fights such as Il’gynoth.
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? Eyeballing the above, it looks like Assassination scales better, but we should look directly at how damage improves as ilvl increases.
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",]
slopes$spec <- factor(slopes$spec, levels = slopes$spec[order(slopes$estimate)])
duplicated levels in factors are deprecated
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")
LS0tDQp0aXRsZTogIlJvZ3VlIHNwZWMgc2NhbGluZyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogDQogICAgZmlnX2hlaWdodDogNg0KICAgIGZpZ193aWR0aDogMTgNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KSSB3YXMgY3VyaW91cyBhYm91dCB0aGUgcmVsYXRpdmUgc2NhbGluZyBwb3RlbnRpYWwgb2YgdGhlIHRocmVlIHJvZ3VlIHNwZWNzLiBXZSBrbm93IGZyb20gc2ltcyB0aGF0IEFzc2Fzc2luYXRpb24gaXMga2luZyBpbiB0aGUgODUwLTg4MCByYW5nZSwgYW5kIHRoYXQgU3ViIGlzIGV4cGVjdGVkIHRvIHN1cnBhc3MgaXQgYWZ0ZXIgdGhhdC4gT3V0bGF3IGlzIG5vdCBleHBlY3RlZCB0byBzY2FsZSBhcyB3ZWxsIGFzIGVpdGhlciBvZiB0aGUgb3RoZXIgc3BlY3MuIEkgZmlndXJlZCwgd2h5IG5vdCBsb29rIGF0IHRoZSBkYXRhIGFuZCBzZWUgd2hhdCBzaGFrZXMgb3V0Pw0KDQojIyBGZXRjaGluZyB0aGUgZGF0YQ0KDQpEYXRhIGlzIGdyYWJiZWQgZnJvbSBXYXJjcmFmdExvZ3MgdmlhIHRoZWlyIEFQSS4gV2UgZ2V0IHRoZSB0b3AgMWsgcGFyc2VzIHBlciBzcGVjL2VuY291bnRlci9kaWZmaWN1bHR5Lg0KDQpTb3VyY2UgaXMgYXQgaHR0cHM6Ly9naXRodWIuY29tL2NoZWFsZC93YXJjcmFmdGxvZ3MtcGFyc2VzDQoNCmBgYA0KJCBBUElfS0VZPXlvdXJfa2V5X2hlcmUgcnVieSBncmFiYmVyLnJiIHJvZ3VlDQpgYGANCg0KT25jZSB3ZSBoYXZlIHRoZSBKU09OIGZpbGVzLCB3ZSB1c2UgYSBzbWFsbCBzY3JpcHQgdG8gYm9pbCB0aGVtIGRvd24gaW50byBhIENTVjoNCg0KYGBgDQokIEFQSV9LRVk9eW91cl9rZXlfaGVyZSBydWJ5IGRpc3RpbGwucmIgcm9ndWUNCmBgYA0KDQpUaGlzIHJlc3VsdHMgaW4gdGhlIGBjc3Yvcm9ndWUuY3N2YCB0aGF0IHdlIHRoZW4gY29uc3VtZSBpbiBSLg0KDQpPbmNlIGluIFIsIHdlIGxvYWQgaXQgdXA6DQoNCmBgYHtyfQ0KbGlicmFyeShyZXNoYXBlMikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpkYXRhIDwtIHJlYWQuY3N2KCJjc3Yvcm9ndWUuY3N2IikNCmRhdGEkZGF0ZSA8LSBhcy5EYXRlKGRhdGEkZGF0ZSkNCmN1dG9mZl9kYXRlID0gIjIwMTYtMTAtMTUiDQpgYGANCg0KIyMgRmlsdGVyaW5nIGFuZCBwbG90dGluZw0KDQpUaGVuIHdlIGRlZmluZSBhIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIHVzZWQgdG8gZmlsdGVyIHRoZSBkYXRhc2V0IHRvIGEgcGFydGljdWxhciBlbmNvdW50ZXIgYW5kIGRpZmZpY3VsdHkuIEl0IGFsc28gcGVyZm9ybXMgb3V0bGllciBmaWx0ZXJpbmcgLSBmb3IgZWFjaCA1LWlsdmwgYnVja2V0LCBmb3IgZWFjaCBzcGVjLCB3ZSBjb21wdXRlIHRoZSA5NXRoIHBlcmNlbnRpbGUgb2YgRFBTLCB0aGVuIHJlbW92ZSBhbnkgcGFyc2VzIGZyb20gdGhlIGRhdGFzZXQgZm9yIHRoYXQgaWx2bC9zcGVjIGFib3ZlIHRoYXQgY3V0b2ZmLg0KDQpgYGB7cn0NCg0KZ2V0X2VuY291bnRlcl9kYXRhIDwtIGZ1bmN0aW9uKGVuY291bnRlcl9uYW1lLCBkaWZmaWN1bHR5X25hbWUsIGRhdGFzZXQsIGN1dG9mZj1jKDAuMDUsIDAuOTUpLCBidWNrZXRfc2l6ZT0xLCBtaW5faWx2bD04NTUsIG1heF9pbHZsPTg3NSkgew0KICBzcGVjcyA8LSB1bmlxdWUoZGF0YXNldCRzcGVjKQ0KICBkIDwtIGZpbHRlcihkYXRhc2V0LCBpbHZsID49IG1pbl9pbHZsLCBpbHZsIDw9IG1heF9pbHZsLCBlbmNvdW50ZXIgPT0gZW5jb3VudGVyX25hbWUsIGRpZmZpY3VsdHkgPT0gZGlmZmljdWx0eV9uYW1lLCBkYXRlID49IGN1dG9mZl9kYXRlKQ0KICBmb3IoYnVja2V0IGluIHNlcSg4MDAsIDkwMCwgYnk9YnVja2V0X3NpemUpKSB7DQogICAgZm9yKGZfc3BlYyBpbiBzcGVjcykgew0KICAgICAgcSA8LSBxdWFudGlsZShkW2Qkc3BlYyA9PSBmX3NwZWMgJiBkJGlsdmwgPj0gYnVja2V0ICYgZCRpbHZsIDwgYnVja2V0ICsgYnVja2V0X3NpemUsXSRkcHMsIGN1dG9mZikNCiAgICAgIGlmKCFpcy5uYShxWzFdKSkgew0KICAgICAgICBkIDwtIGZpbHRlcihkLCAhKHNwZWMgPT0gZl9zcGVjICYgaWx2bCA+PSBidWNrZXQgJiBpbHZsIDwgYnVja2V0ICsgYnVja2V0X3NpemUgJiAoZHBzID4gcVsyXSB8IGRwcyA8IHFbMV0pKSkNCiAgICAgIH0NCiAgICB9DQogIH0NCiAgcmV0dXJuKGQpDQp9DQpgYGANCg0KUGxvdHRpbmcgdGhlIGRhdGEgaXMgc3RyYWlnaHRmb3J3YXJkLiBIZXJlIHdlIHBsb3QgYm90aCBib3hwbG90cyBmb3IgZWFjaCBzcGVjL2lsdmwgYnVja2V0LCBhcyB3ZWxsIGFzIGEgc21vb3RoZWQgbW92aW5nIGF2ZXJhZ2UgYW5kIGEgbGluZWFyIHRyZW5kbGluZS4NCg0KYGBge3J9DQpwbG90X2VuY291bnRlciA8LSBmdW5jdGlvbihlbmNvdW50ZXJfbmFtZSwgZGlmZmljdWx0eV9uYW1lLCBkYXRhc2V0PWRhdGEsIGJ1Y2tldF9zaXplPTIsIGN1dG9mZj1jKDAuMDUsIDAuOTUpKSB7DQogIGVuY291bnRlcl9kYXRhIDwtIGdldF9lbmNvdW50ZXJfZGF0YShlbmNvdW50ZXJfbmFtZSwgZGlmZmljdWx0eV9uYW1lLCBkYXRhc2V0PWRhdGFzZXQsIGJ1Y2tldF9zaXplPWJ1Y2tldF9zaXplLCBjdXRvZmY9Y3V0b2ZmKQ0KDQogIGxvbmdfZGF0YSA8LSBtZWx0KGVuY291bnRlcl9kYXRhLCBpZC52YXJzPWMoInNwZWMiLCAiaWx2bCIpLCBtZWFzdXJlLnZhcnM9YygiZHBzIikpDQoNCiAgcCA8LSBnZ3Bsb3QoZGF0YT1sb25nX2RhdGEsIGFlcyh4PWlsdmwsIGdyb3VwPXNwZWMsIGNvbG91cj1zcGVjLCBmaWxsPXNwZWMpKSArIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIpICsNCiAgICBsYWJzKHk9IlBhcnNlcyIsIHg9ImlMZXZlbCIsIHRpdGxlPXBhc3RlKCJQYXJzZXM6IiwgZW5jb3VudGVyX25hbWUsIGRpZmZpY3VsdHlfbmFtZSkpDQogIHByaW50KHApDQogIGNhdCgnXHJcblxyXG4nKQ0KICANCiAgcCA8LSBnZ3Bsb3QoZGF0YT1sb25nX2RhdGEsIGFlcyh4PWlsdmwsIHk9dmFsdWUsIGdyb3VwPXNwZWMsIGNvbG91cj1zcGVjKSkgKw0KICAgIGdlb21fYm94cGxvdChhZXMoZ3JvdXA9aW50ZXJhY3Rpb24oc3BlYywgZmxvb3IoaWx2bC9idWNrZXRfc2l6ZSkgKiBidWNrZXRfc2l6ZSkpKSArDQogICAgZ2VvbV9zbW9vdGgoKSArICMgbG9lc3MgZml0DQogICAgZ2VvbV9zbW9vdGgobGluZXR5cGUgPSAiZGFzaGVkIiwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKyAjIGxpbmVhciBmaXQNCiAgICBsYWJzKHk9IkRQUyIsIHg9ImlMZXZlbCIsIHRpdGxlPXBhc3RlKCJQZXJmb3JtYW5jZToiLCBlbmNvdW50ZXJfbmFtZSwgZGlmZmljdWx0eV9uYW1lKSkNCiAgcHJpbnQocCkNCiAgY2F0KCdcclxuXHJcbicpDQp9DQpgYGANCg0KIyMgUmVzdWx0cw0KDQpJbiBnZW5lcmFsLCB0aGlzIHNob3dzIHdoYXQgd2UgZ2VuZXJhbGx5IHN1c3BlY3Q6IEFzc2Fzc2luYXRpb24gaXMgdGhlIHN0cm9uZ2VzdCByb2d1ZSBzcGVjIGZvciBwcm9sb25nZWQgc2luZ2xlLXRhcmdldCBmaWdodHMsIHdoaWxlIE91dGxhdyBpcyBvbmx5IHByZWZlcmFibGUgb24gaGVhdnkgbXVsdGktdGFyZ2V0IGZpZ2h0cyBzdWNoIGFzIElsJ2d5bm90aC4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KDQpwbG90X2VuY291bnRlcigibnl0aGVuZHJhIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigibnl0aGVuZHJhIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigibnl0aGVuZHJhIiwgIk15dGhpYyIpDQoNCnBsb3RfZW5jb3VudGVyKCJ1cnNvYyIsICJOb3JtYWwiKQ0KcGxvdF9lbmNvdW50ZXIoInVyc29jIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigidXJzb2MiLCAiTXl0aGljIikNCg0KcGxvdF9lbmNvdW50ZXIoImVsZXJldGhlX3JlbmZlcmFsIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiZWxlcmV0aGVfcmVuZmVyYWwiLCAiSGVyb2ljIikNCnBsb3RfZW5jb3VudGVyKCJlbGVyZXRoZV9yZW5mZXJhbCIsICJNeXRoaWMiKQ0KDQpwbG90X2VuY291bnRlcigiaWxfZ3lub3RoX19oZWFydF9vZl9jb3JydXB0aW9uIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiaWxfZ3lub3RoX19oZWFydF9vZl9jb3JydXB0aW9uIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiaWxfZ3lub3RoX19oZWFydF9vZl9jb3JydXB0aW9uIiwgIk15dGhpYyIpDQoNCnBsb3RfZW5jb3VudGVyKCJkcmFnb25zX29mX25pZ2h0bWFyZSIsICJOb3JtYWwiKQ0KcGxvdF9lbmNvdW50ZXIoImRyYWdvbnNfb2ZfbmlnaHRtYXJlIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiZHJhZ29uc19vZl9uaWdodG1hcmUiLCAiTXl0aGljIikNCg0KcGxvdF9lbmNvdW50ZXIoImNlbmFyaXVzIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiY2VuYXJpdXMiLCAiSGVyb2ljIikNCnBsb3RfZW5jb3VudGVyKCJjZW5hcml1cyIsICJNeXRoaWMiKQ0KDQpwbG90X2VuY291bnRlcigieGF2aXVzIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigieGF2aXVzIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigieGF2aXVzIiwgIk15dGhpYyIpDQpgYGANCg0KIyMjIFNjYWxpbmcNCg0KU28gd2UgaGF2ZSB0aGUgcmF3IChzbW9vdGhlZCkgZGFtYWdlIHByb2dyZXNzaW9ucywgYnV0IGhvdyBhYm91dCBzY2FsaW5nPyBFeWViYWxsaW5nIHRoZSBhYm92ZSwgaXQgbG9va3MgbGlrZSBBc3Nhc3NpbmF0aW9uIHNjYWxlcyBiZXR0ZXIsIGJ1dCB3ZSBzaG91bGQgbG9vayBkaXJlY3RseSBhdCBob3cgZGFtYWdlIGltcHJvdmVzIGFzIGlsdmwgaW5jcmVhc2VzLg0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQpwbG90X2Rwc19nYWluIDwtIGZ1bmN0aW9uKGVuY291bnRlciwgZGlmZmljdWx0eSwgZGF0YXNldD1kYXRhLCBidWNrZXRfc2l6ZT0xLCBjdXRvZmY9YygwLjA1LCAwLjk1KSkgew0KICBkIDwtIGRhdGFzZXRbZGF0YXNldCRlbmNvdW50ZXIgPT0gZW5jb3VudGVyICYgZGF0YXNldCRkaWZmaWN1bHR5ID09IGRpZmZpY3VsdHkgJiBkYXRhc2V0JGlsdmwgPD0gODcwICYgZGF0YXNldCRkYXRlID49IGN1dG9mZl9kYXRlLCBdDQogIGQgPC0gYWdncmVnYXRlKGQkZHBzLCBieT1saXN0KGlsdmw9ZCRpbHZsLCBzcGVjPWQkc3BlYyksIEZVTj1tZWFuKQ0KICBkJGRwcyA8LSBkJHgNCiAgZCA8LSBkW29yZGVyKGQkaWx2bCksXQ0KICBkJGRpZmYgPC0gYXZlKGQkZHBzLCBmYWN0b3IoZCRzcGVjKSwgRlVOPWZ1bmN0aW9uKHgpIGMoTkEsIGRpZmYoeCkpKQ0KICBkIDwtIGZpbHRlcihkLCAhaXMubmEoZGlmZikpDQogIGdncGxvdChkYXRhPWQsIGFlcyh4PWlsdmwsIHk9ZGlmZiwgZ3JvdXA9aW50ZXJhY3Rpb24oc3BlYywgZW5jb3VudGVyKSwgY29sb3VyPXNwZWMpKSArIGdlb21fc21vb3RoKHNlPUZBTFNFLCBmdWxscmFuZ2U9VFJVRSkgKw0KICAgIGxhYnMoeT0iRFBTIGdhaW4gcGVyIHRpZXIiLCB4PSJpTGV2ZWwiLCB0aXRsZT1wYXN0ZShlbmNvdW50ZXIsIGRpZmZpY3VsdHksIHNlcCA9ICIgIikpICsgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDE1MDAwKSkNCn0NCg0KIyBDb21tZW50ZWQgZmlnaHRzIGhhdmUgdG9vIGxpdHRsZSBkYXRhIHRvIHByb2R1Y2UgYW55dGhpbmcgdXNlZnVsDQoNCnBsb3RfZHBzX2dhaW4oIm55dGhlbmRyYSIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2Fpbigibnl0aGVuZHJhIiwgIkhlcm9pYyIpDQpwbG90X2Rwc19nYWluKCJueXRoZW5kcmEiLCAiTXl0aGljIikNCg0KcGxvdF9kcHNfZ2FpbigidXJzb2MiLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oInVyc29jIiwgIkhlcm9pYyIpDQpwbG90X2Rwc19nYWluKCJ1cnNvYyIsICJNeXRoaWMiKQ0KDQpwbG90X2Rwc19nYWluKCJlbGVyZXRoZV9yZW5mZXJhbCIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2FpbigiZWxlcmV0aGVfcmVuZmVyYWwiLCAiSGVyb2ljIikNCnBsb3RfZHBzX2dhaW4oImVsZXJldGhlX3JlbmZlcmFsIiwgIk15dGhpYyIpDQoNCnBsb3RfZHBzX2dhaW4oImlsX2d5bm90aF9faGVhcnRfb2ZfY29ycnVwdGlvbiIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2FpbigiaWxfZ3lub3RoX19oZWFydF9vZl9jb3JydXB0aW9uIiwgIkhlcm9pYyIpDQojIHBsb3RfZHBzX2dhaW4oImlsX2d5bm90aF9faGVhcnRfb2ZfY29ycnVwdGlvbiIsICJNeXRoaWMiKQ0KDQpwbG90X2Rwc19nYWluKCJkcmFnb25zX29mX25pZ2h0bWFyZSIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2FpbigiZHJhZ29uc19vZl9uaWdodG1hcmUiLCAiSGVyb2ljIikNCiMgcGxvdF9kcHNfZ2FpbigiZHJhZ29uc19vZl9uaWdodG1hcmUiLCAiTXl0aGljIikNCg0KcGxvdF9kcHNfZ2FpbigiY2VuYXJpdXMiLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oImNlbmFyaXVzIiwgIkhlcm9pYyIpDQojIHBsb3RfZHBzX2dhaW4oImNlbmFyaXVzIiwgIk15dGhpYyIpDQoNCnBsb3RfZHBzX2dhaW4oInhhdml1cyIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2FpbigieGF2aXVzIiwgIkhlcm9pYyIpDQojIHBsb3RfZHBzX2dhaW4oInhhdml1cyIsICJNeXRoaWMiKQ0KDQpgYGANCg0KRmluYWxseSwgaGVyZSdzIGEgcmVhbGx5IGJsdW50IGxpbmVhciByZWdyZXNzaW9uIG9mIGVhY2ggZW5jb3VudGVyLCB3aXRoIHRoZSBzY2FsaW5nIGZhY3RvciAoc2xvcGUgb2YgdGhlIHJlZ3Jlc3Npb24gbGluZSkgcGxvdHRlZDoNCg0KYGBge3IsIGZpZy53aWR0aD0xNn0NCmQgPC0gZGF0YQ0KbSA8LSBkICU+JSBncm91cF9ieShzcGVjLCBlbmNvdW50ZXIpICU+JSBkbyh0aWR5KGxtKGRwcyB+IGlsdmwsIGRhdGE9LikpKQ0Kc2xvcGVzIDwtIG1bbSR0ZXJtID09ICJpbHZsIixdDQpzbG9wZXMkc3BlYyA8LSBmYWN0b3Ioc2xvcGVzJHNwZWMsIGxldmVscyA9IHNsb3BlcyRzcGVjW29yZGVyKHNsb3BlcyRlc3RpbWF0ZSldKQ0KZ2dwbG90KGRhdGE9c2xvcGVzLCBhZXMoeD1lbmNvdW50ZXIsIHk9ZXN0aW1hdGUsIGdyb3VwPWludGVyYWN0aW9uKHNwZWMsIGVuY291bnRlciksIHJlb3JkZXI9ZXN0aW1hdGUsIGZpbGw9c3BlYykpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnMoeT0iRFBTIGdhaW4vaWx2bCIsIHg9IkVuY291bnRlciIsIHRpdGxlPSJEUFMgc2NhbGluZyBwZXIgaWx2bDogT3ZlcmFsbCIpDQoNCmBgYA0KDQojIyBDb25jbHVzaW9ucw0KDQpPdmVyYWxsLCBJIHdvdWxkIGNvbnNpZGVyIHRoZSBoeXBvdGhlc2lzIGdlbmVyYWxseSBjb25maXJtZWQuIEFzc2Fzc2luYXRpb24gY2xlYXJseSBob2xkcyBzdXBlcmlvciBzaW5nbGUtdGFyZ2V0IHBvdGVudGlhbCB0byBlaXRoZXIgU3ViIG9yIE91dGxhdyBhdCB0aGUgY3VycmVudCBsZXZlbCBvZiByYWlkaW5nLCBiYXJyaW5nIHRoZSBmcmVhayBPdXRsYXcgcGFyc2VzIHdpdGggbXVsdGlwbGUgNi1idWZmIFJ0QiByb2xscy4gT3V0bGF3IGNvbnNpc3RlbnRseSBzY2FsZXMgbGVzcyBpbXByZXNzaXZlbHkgdGhhbiBlaXRoZXIgQXNzYXNzaW5hdGlvbiBvciBTdWJ0bGV0eSBhcyBpbHZsIHByb2dyZXNzZXMu