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")

Conclusions

Overall, I would consider the hypothesis generally confirmed. Assassination clearly holds superior single-target potential to either Sub or Outlaw at the current level of raiding, barring the freak Outlaw parses with multiple 6-buff RtB rolls. Outlaw consistently scales less impressively than either Assassination or Subtlety as ilvl progresses.

LS0tDQp0aXRsZTogIlJvZ3VlIHNwZWMgc2NhbGluZyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogDQogICAgZmlnX2hlaWdodDogNg0KICAgIGZpZ193aWR0aDogMTgNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KSSB3YXMgY3VyaW91cyBhYm91dCB0aGUgcmVsYXRpdmUgc2NhbGluZyBwb3RlbnRpYWwgb2YgdGhlIHRocmVlIHJvZ3VlIHNwZWNzLiBXZSBrbm93IGZyb20gc2ltcyB0aGF0IEFzc2Fzc2luYXRpb24gaXMga2luZyBpbiB0aGUgODUwLTg4MCByYW5nZSwgYW5kIHRoYXQgU3ViIGlzIGV4cGVjdGVkIHRvIHN1cnBhc3MgaXQgYWZ0ZXIgdGhhdC4gT3V0bGF3IGlzIG5vdCBleHBlY3RlZCB0byBzY2FsZSBhcyB3ZWxsIGFzIGVpdGhlciBvZiB0aGUgb3RoZXIgc3BlY3MuIEkgZmlndXJlZCwgd2h5IG5vdCBsb29rIGF0IHRoZSBkYXRhIGFuZCBzZWUgd2hhdCBzaGFrZXMgb3V0Pw0KDQojIyBGZXRjaGluZyB0aGUgZGF0YQ0KDQpEYXRhIGlzIGdyYWJiZWQgZnJvbSBXYXJjcmFmdExvZ3MgdmlhIHRoZWlyIEFQSS4gV2UgZ2V0IHRoZSB0b3AgMWsgcGFyc2VzIHBlciBzcGVjL2VuY291bnRlci9kaWZmaWN1bHR5Lg0KDQpTb3VyY2UgaXMgYXQgaHR0cHM6Ly9naXRodWIuY29tL2NoZWFsZC93YXJjcmFmdGxvZ3MtcGFyc2VzDQoNCmBgYA0KJCBBUElfS0VZPXlvdXJfa2V5X2hlcmUgcnVieSBncmFiYmVyLnJiIHJvZ3VlDQpgYGANCg0KT25jZSB3ZSBoYXZlIHRoZSBKU09OIGZpbGVzLCB3ZSB1c2UgYSBzbWFsbCBzY3JpcHQgdG8gYm9pbCB0aGVtIGRvd24gaW50byBhIENTVjoNCg0KYGBgDQokIEFQSV9LRVk9eW91cl9rZXlfaGVyZSBydWJ5IGRpc3RpbGwucmIgcm9ndWUNCmBgYA0KDQpUaGlzIHJlc3VsdHMgaW4gdGhlIGBjc3Yvcm9ndWUuY3N2YCB0aGF0IHdlIHRoZW4gY29uc3VtZSBpbiBSLg0KDQpPbmNlIGluIFIsIHdlIGxvYWQgaXQgdXA6DQoNCmBgYHtyfQ0KbGlicmFyeShyZXNoYXBlMikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpkYXRhIDwtIHJlYWQuY3N2KCJjc3Yvcm9ndWUuY3N2IikNCmRhdGEkZGF0ZSA8LSBhcy5EYXRlKGRhdGEkZGF0ZSkNCmN1dG9mZl9kYXRlID0gIjIwMTYtMTAtMTUiDQpgYGANCg0KIyMgRmlsdGVyaW5nIGFuZCBwbG90dGluZw0KDQpUaGVuIHdlIGRlZmluZSBhIGZ1bmN0aW9uIHRoYXQgY2FuIGJlIHVzZWQgdG8gZmlsdGVyIHRoZSBkYXRhc2V0IHRvIGEgcGFydGljdWxhciBlbmNvdW50ZXIgYW5kIGRpZmZpY3VsdHkuIEl0IGFsc28gcGVyZm9ybXMgb3V0bGllciBmaWx0ZXJpbmcgLSBmb3IgZWFjaCA1LWlsdmwgYnVja2V0LCBmb3IgZWFjaCBzcGVjLCB3ZSBjb21wdXRlIHRoZSA5NXRoIHBlcmNlbnRpbGUgb2YgRFBTLCB0aGVuIHJlbW92ZSBhbnkgcGFyc2VzIGZyb20gdGhlIGRhdGFzZXQgZm9yIHRoYXQgaWx2bC9zcGVjIGFib3ZlIHRoYXQgY3V0b2ZmLg0KDQpgYGB7cn0NCg0KZ2V0X2VuY291bnRlcl9kYXRhIDwtIGZ1bmN0aW9uKGVuY291bnRlcl9uYW1lLCBkaWZmaWN1bHR5X25hbWUsIGRhdGFzZXQsIGN1dG9mZj1jKDAuMDUsIDAuOTUpLCBidWNrZXRfc2l6ZT0xLCBtaW5faWx2bD04NTUsIG1heF9pbHZsPTg3NSkgew0KICBzcGVjcyA8LSB1bmlxdWUoZGF0YXNldCRzcGVjKQ0KICBkIDwtIGZpbHRlcihkYXRhc2V0LCBpbHZsID49IG1pbl9pbHZsLCBpbHZsIDw9IG1heF9pbHZsLCBlbmNvdW50ZXIgPT0gZW5jb3VudGVyX25hbWUsIGRpZmZpY3VsdHkgPT0gZGlmZmljdWx0eV9uYW1lLCBkYXRlID49IGN1dG9mZl9kYXRlKQ0KICBmb3IoYnVja2V0IGluIHNlcSg4MDAsIDkwMCwgYnk9YnVja2V0X3NpemUpKSB7DQogICAgZm9yKGZfc3BlYyBpbiBzcGVjcykgew0KICAgICAgcSA8LSBxdWFudGlsZShkW2Qkc3BlYyA9PSBmX3NwZWMgJiBkJGlsdmwgPj0gYnVja2V0ICYgZCRpbHZsIDwgYnVja2V0ICsgYnVja2V0X3NpemUsXSRkcHMsIGN1dG9mZikNCiAgICAgIGlmKCFpcy5uYShxWzFdKSkgew0KICAgICAgICBkIDwtIGZpbHRlcihkLCAhKHNwZWMgPT0gZl9zcGVjICYgaWx2bCA+PSBidWNrZXQgJiBpbHZsIDwgYnVja2V0ICsgYnVja2V0X3NpemUgJiAoZHBzID4gcVsyXSB8IGRwcyA8IHFbMV0pKSkNCiAgICAgIH0NCiAgICB9DQogIH0NCiAgcmV0dXJuKGQpDQp9DQpgYGANCg0KUGxvdHRpbmcgdGhlIGRhdGEgaXMgc3RyYWlnaHRmb3J3YXJkLiBIZXJlIHdlIHBsb3QgYm90aCBib3hwbG90cyBmb3IgZWFjaCBzcGVjL2lsdmwgYnVja2V0LCBhcyB3ZWxsIGFzIGEgc21vb3RoZWQgbW92aW5nIGF2ZXJhZ2UgYW5kIGEgbGluZWFyIHRyZW5kbGluZS4NCg0KYGBge3J9DQpwbG90X2VuY291bnRlciA8LSBmdW5jdGlvbihlbmNvdW50ZXJfbmFtZSwgZGlmZmljdWx0eV9uYW1lLCBkYXRhc2V0PWRhdGEsIGJ1Y2tldF9zaXplPTIsIGN1dG9mZj1jKDAuMDUsIDAuOTUpKSB7DQogIGVuY291bnRlcl9kYXRhIDwtIGdldF9lbmNvdW50ZXJfZGF0YShlbmNvdW50ZXJfbmFtZSwgZGlmZmljdWx0eV9uYW1lLCBkYXRhc2V0PWRhdGFzZXQsIGJ1Y2tldF9zaXplPWJ1Y2tldF9zaXplLCBjdXRvZmY9Y3V0b2ZmKQ0KDQogIGxvbmdfZGF0YSA8LSBtZWx0KGVuY291bnRlcl9kYXRhLCBpZC52YXJzPWMoInNwZWMiLCAiaWx2bCIpLCBtZWFzdXJlLnZhcnM9YygiZHBzIikpDQoNCiAgcCA8LSBnZ3Bsb3QoZGF0YT1sb25nX2RhdGEsIGFlcyh4PWlsdmwsIGdyb3VwPXNwZWMsIGNvbG91cj1zcGVjLCBmaWxsPXNwZWMpKSArIGdlb21fYmFyKHBvc2l0aW9uPSJkb2RnZSIpICsNCiAgICBsYWJzKHk9IlBhcnNlcyIsIHg9ImlMZXZlbCIsIHRpdGxlPXBhc3RlKCJQYXJzZXM6IiwgZW5jb3VudGVyX25hbWUsIGRpZmZpY3VsdHlfbmFtZSkpDQogIHByaW50KHApDQogIGNhdCgnXHJcblxyXG4nKQ0KICANCiAgcCA8LSBnZ3Bsb3QoZGF0YT1sb25nX2RhdGEsIGFlcyh4PWlsdmwsIHk9dmFsdWUsIGdyb3VwPXNwZWMsIGNvbG91cj1zcGVjKSkgKw0KICAgIGdlb21fYm94cGxvdChhZXMoZ3JvdXA9aW50ZXJhY3Rpb24oc3BlYywgZmxvb3IoaWx2bC9idWNrZXRfc2l6ZSkgKiBidWNrZXRfc2l6ZSkpKSArDQogICAgZ2VvbV9zbW9vdGgoKSArICMgbG9lc3MgZml0DQogICAgZ2VvbV9zbW9vdGgobGluZXR5cGUgPSAiZGFzaGVkIiwgbWV0aG9kID0gImxtIiwgc2UgPSBGQUxTRSkgKyAjIGxpbmVhciBmaXQNCiAgICBsYWJzKHk9IkRQUyIsIHg9ImlMZXZlbCIsIHRpdGxlPXBhc3RlKCJQZXJmb3JtYW5jZToiLCBlbmNvdW50ZXJfbmFtZSwgZGlmZmljdWx0eV9uYW1lKSkNCiAgcHJpbnQocCkNCiAgY2F0KCdcclxuXHJcbicpDQp9DQpgYGANCg0KIyMgUmVzdWx0cw0KDQpJbiBnZW5lcmFsLCB0aGlzIHNob3dzIHdoYXQgd2UgZ2VuZXJhbGx5IHN1c3BlY3Q6IEFzc2Fzc2luYXRpb24gaXMgdGhlIHN0cm9uZ2VzdCByb2d1ZSBzcGVjIGZvciBwcm9sb25nZWQgc2luZ2xlLXRhcmdldCBmaWdodHMsIHdoaWxlIE91dGxhdyBpcyBvbmx5IHByZWZlcmFibGUgb24gaGVhdnkgbXVsdGktdGFyZ2V0IGZpZ2h0cyBzdWNoIGFzIElsJ2d5bm90aC4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD02fQ0KDQpwbG90X2VuY291bnRlcigibnl0aGVuZHJhIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigibnl0aGVuZHJhIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigibnl0aGVuZHJhIiwgIk15dGhpYyIpDQoNCnBsb3RfZW5jb3VudGVyKCJ1cnNvYyIsICJOb3JtYWwiKQ0KcGxvdF9lbmNvdW50ZXIoInVyc29jIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigidXJzb2MiLCAiTXl0aGljIikNCg0KcGxvdF9lbmNvdW50ZXIoImVsZXJldGhlX3JlbmZlcmFsIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiZWxlcmV0aGVfcmVuZmVyYWwiLCAiSGVyb2ljIikNCnBsb3RfZW5jb3VudGVyKCJlbGVyZXRoZV9yZW5mZXJhbCIsICJNeXRoaWMiKQ0KDQpwbG90X2VuY291bnRlcigiaWxfZ3lub3RoX19oZWFydF9vZl9jb3JydXB0aW9uIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiaWxfZ3lub3RoX19oZWFydF9vZl9jb3JydXB0aW9uIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiaWxfZ3lub3RoX19oZWFydF9vZl9jb3JydXB0aW9uIiwgIk15dGhpYyIpDQoNCnBsb3RfZW5jb3VudGVyKCJkcmFnb25zX29mX25pZ2h0bWFyZSIsICJOb3JtYWwiKQ0KcGxvdF9lbmNvdW50ZXIoImRyYWdvbnNfb2ZfbmlnaHRtYXJlIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigiZHJhZ29uc19vZl9uaWdodG1hcmUiLCAiTXl0aGljIikNCg0KcGxvdF9lbmNvdW50ZXIoImNlbmFyaXVzIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigiY2VuYXJpdXMiLCAiSGVyb2ljIikNCnBsb3RfZW5jb3VudGVyKCJjZW5hcml1cyIsICJNeXRoaWMiKQ0KDQpwbG90X2VuY291bnRlcigieGF2aXVzIiwgIk5vcm1hbCIpDQpwbG90X2VuY291bnRlcigieGF2aXVzIiwgIkhlcm9pYyIpDQpwbG90X2VuY291bnRlcigieGF2aXVzIiwgIk15dGhpYyIpDQpgYGANCg0KIyMjIFNjYWxpbmcNCg0KU28gd2UgaGF2ZSB0aGUgcmF3IChzbW9vdGhlZCkgZGFtYWdlIHByb2dyZXNzaW9ucywgYnV0IGhvdyBhYm91dCBzY2FsaW5nPyBFeWViYWxsaW5nIHRoZSBhYm92ZSwgaXQgbG9va3MgbGlrZSBBc3Nhc3NpbmF0aW9uIHNjYWxlcyBiZXR0ZXIsIGJ1dCB3ZSBzaG91bGQgbG9vayBkaXJlY3RseSBhdCBob3cgZGFtYWdlIGltcHJvdmVzIGFzIGlsdmwgaW5jcmVhc2VzLg0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQpwbG90X2Rwc19nYWluIDwtIGZ1bmN0aW9uKGVuY291bnRlciwgZGlmZmljdWx0eSwgZGF0YXNldD1kYXRhLCBidWNrZXRfc2l6ZT0xLCBjdXRvZmY9YygwLjA1LCAwLjk1KSkgew0KICBkIDwtIGRhdGFzZXRbZGF0YXNldCRlbmNvdW50ZXIgPT0gZW5jb3VudGVyICYgZGF0YXNldCRkaWZmaWN1bHR5ID09IGRpZmZpY3VsdHkgJiBkYXRhc2V0JGlsdmwgPD0gODcwICYgZGF0YXNldCRkYXRlID49IGN1dG9mZl9kYXRlLCBdDQogIGQgPC0gYWdncmVnYXRlKGQkZHBzLCBieT1saXN0KGlsdmw9ZCRpbHZsLCBzcGVjPWQkc3BlYyksIEZVTj1tZWFuKQ0KICBkJGRwcyA8LSBkJHgNCiAgZCA8LSBkW29yZGVyKGQkaWx2bCksXQ0KICBkJGRpZmYgPC0gYXZlKGQkZHBzLCBmYWN0b3IoZCRzcGVjKSwgRlVOPWZ1bmN0aW9uKHgpIGMoTkEsIGRpZmYoeCkpKQ0KICBkIDwtIGZpbHRlcihkLCAhaXMubmEoZGlmZikpDQogIGdncGxvdChkYXRhPWQsIGFlcyh4PWlsdmwsIHk9ZGlmZiwgZ3JvdXA9aW50ZXJhY3Rpb24oc3BlYywgZW5jb3VudGVyKSwgY29sb3VyPXNwZWMpKSArIGdlb21fc21vb3RoKHNlPUZBTFNFLCBmdWxscmFuZ2U9VFJVRSkgKw0KICAgIGxhYnMoeT0iRFBTIGdhaW4gcGVyIHRpZXIiLCB4PSJpTGV2ZWwiLCB0aXRsZT1wYXN0ZShlbmNvdW50ZXIsIGRpZmZpY3VsdHksIHNlcCA9ICIgIikpICsgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDE1MDAwKSkNCn0NCg0KIyBDb21tZW50ZWQgZmlnaHRzIGhhdmUgdG9vIGxpdHRsZSBkYXRhIHRvIHByb2R1Y2UgYW55dGhpbmcgdXNlZnVsDQoNCnBsb3RfZHBzX2dhaW4oIm55dGhlbmRyYSIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2Fpbigibnl0aGVuZHJhIiwgIkhlcm9pYyIpDQpwbG90X2Rwc19nYWluKCJueXRoZW5kcmEiLCAiTXl0aGljIikNCg0KcGxvdF9kcHNfZ2FpbigidXJzb2MiLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oInVyc29jIiwgIkhlcm9pYyIpDQpwbG90X2Rwc19nYWluKCJ1cnNvYyIsICJNeXRoaWMiKQ0KDQpwbG90X2Rwc19nYWluKCJlbGVyZXRoZV9yZW5mZXJhbCIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2FpbigiZWxlcmV0aGVfcmVuZmVyYWwiLCAiSGVyb2ljIikNCnBsb3RfZHBzX2dhaW4oImVsZXJldGhlX3JlbmZlcmFsIiwgIk15dGhpYyIpDQoNCnBsb3RfZHBzX2dhaW4oImlsX2d5bm90aF9faGVhcnRfb2ZfY29ycnVwdGlvbiIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2FpbigiaWxfZ3lub3RoX19oZWFydF9vZl9jb3JydXB0aW9uIiwgIkhlcm9pYyIpDQojIHBsb3RfZHBzX2dhaW4oImlsX2d5bm90aF9faGVhcnRfb2ZfY29ycnVwdGlvbiIsICJNeXRoaWMiKQ0KDQpwbG90X2Rwc19nYWluKCJkcmFnb25zX29mX25pZ2h0bWFyZSIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2FpbigiZHJhZ29uc19vZl9uaWdodG1hcmUiLCAiSGVyb2ljIikNCiMgcGxvdF9kcHNfZ2FpbigiZHJhZ29uc19vZl9uaWdodG1hcmUiLCAiTXl0aGljIikNCg0KcGxvdF9kcHNfZ2FpbigiY2VuYXJpdXMiLCAiTm9ybWFsIikNCnBsb3RfZHBzX2dhaW4oImNlbmFyaXVzIiwgIkhlcm9pYyIpDQojIHBsb3RfZHBzX2dhaW4oImNlbmFyaXVzIiwgIk15dGhpYyIpDQoNCnBsb3RfZHBzX2dhaW4oInhhdml1cyIsICJOb3JtYWwiKQ0KcGxvdF9kcHNfZ2FpbigieGF2aXVzIiwgIkhlcm9pYyIpDQojIHBsb3RfZHBzX2dhaW4oInhhdml1cyIsICJNeXRoaWMiKQ0KDQpgYGANCg0KRmluYWxseSwgaGVyZSdzIGEgcmVhbGx5IGJsdW50IGxpbmVhciByZWdyZXNzaW9uIG9mIGVhY2ggZW5jb3VudGVyLCB3aXRoIHRoZSBzY2FsaW5nIGZhY3RvciAoc2xvcGUgb2YgdGhlIHJlZ3Jlc3Npb24gbGluZSkgcGxvdHRlZDoNCg0KYGBge3IsIGZpZy53aWR0aD0xNn0NCmQgPC0gZGF0YQ0KbSA8LSBkICU+JSBncm91cF9ieShzcGVjLCBlbmNvdW50ZXIpICU+JSBkbyh0aWR5KGxtKGRwcyB+IGlsdmwsIGRhdGE9LikpKQ0Kc2xvcGVzIDwtIG1bbSR0ZXJtID09ICJpbHZsIixdDQpzbG9wZXMkc3BlYyA8LSBmYWN0b3Ioc2xvcGVzJHNwZWMsIGxldmVscyA9IHNsb3BlcyRzcGVjW29yZGVyKHNsb3BlcyRlc3RpbWF0ZSldKQ0KZ2dwbG90KGRhdGE9c2xvcGVzLCBhZXMoeD1lbmNvdW50ZXIsIHk9ZXN0aW1hdGUsIGdyb3VwPWludGVyYWN0aW9uKHNwZWMsIGVuY291bnRlciksIHJlb3JkZXI9ZXN0aW1hdGUsIGZpbGw9c3BlYykpICsgZ2VvbV9iYXIocG9zaXRpb249ImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSArDQogIGxhYnMoeT0iRFBTIGdhaW4vaWx2bCIsIHg9IkVuY291bnRlciIsIHRpdGxlPSJEUFMgc2NhbGluZyBwZXIgaWx2bDogT3ZlcmFsbCIpDQoNCmBgYA0KDQojIyBDb25jbHVzaW9ucw0KDQpPdmVyYWxsLCBJIHdvdWxkIGNvbnNpZGVyIHRoZSBoeXBvdGhlc2lzIGdlbmVyYWxseSBjb25maXJtZWQuIEFzc2Fzc2luYXRpb24gY2xlYXJseSBob2xkcyBzdXBlcmlvciBzaW5nbGUtdGFyZ2V0IHBvdGVudGlhbCB0byBlaXRoZXIgU3ViIG9yIE91dGxhdyBhdCB0aGUgY3VycmVudCBsZXZlbCBvZiByYWlkaW5nLCBiYXJyaW5nIHRoZSBmcmVhayBPdXRsYXcgcGFyc2VzIHdpdGggbXVsdGlwbGUgNi1idWZmIFJ0QiByb2xscy4gT3V0bGF3IGNvbnNpc3RlbnRseSBzY2FsZXMgbGVzcyBpbXByZXNzaXZlbHkgdGhhbiBlaXRoZXIgQXNzYXNzaW5hdGlvbiBvciBTdWJ0bGV0eSBhcyBpbHZsIHByb2dyZXNzZXMu