I recently posted plots visualizing positional tiers in #SFB9. Here’s a step-by-step guide to replicating my process in R. Hope this helps!

Before we get started in R, save down the SFB9 Data from the Google Sheet created by Josh Hornsby. Save it down as a csv, which should be named “#SFB9 Data - sfb_data.csv” by default. If not, make sure the filename in the second block of code matches yours.


Next, we will load the required packages (‘dplyr’ for manipulating the data, ‘ggplot2’ to handle the plotting). Make sure to install these first, if they are not already.

### Load required packages
library(dplyr)
library(ggplot2)

Now, we will read the data from the csv into a data frame, using ‘colClasses’ to define the column types (this part is not necessary, but is useful to avoid problems that can arise due to a column of numbers being read in as characters). Then, we’ll preview the top of the data frame.

### Read in CSV
sfb9_data <- read.csv(file="#SFB9 Data - sfb_data.csv",
                      colClasses=c("character", "character", "integer", "integer", "integer", 
                                     "character", "character", "character", "integer", "character", 
                                     "character", "character", "integer", "integer"))
  
### Preview the data frame
sfb9_data %>% head
   lg_name div_name rd pk ovr     p_time p_com         fran_at fran_num p_pos p_tm              p_full n_pos
1 Shooters  Doomguy  1  1   1 1562598605  <NA>  @Matt_Gajewski 10733002    RB  DAL     Ezekiel Elliott     1
2 Shooters  Doomguy  1  2   2 1562598605   PDL   @df_patterson 10733008    RB  NYG      Saquon Barkley     2
3 Shooters  Doomguy  1  3   3 1562598927  <NA>      @othmanfcb 10733003    RB  CAR Christian McCaffrey     3
4 Shooters  Doomguy  1  4   4 1562599176  <NA>       @BKenny26 10733009    RB   NO        Alvin Kamara     4
5 Shooters  Doomguy  1  5   5 1562599467  <NA>     @PacingPete 10733011    RB  LAC       Melvin Gordon     5
6 Shooters  Doomguy  1  6   6 1562599467   PDL @DynastyInfidel 10733006    RB  ARI       David Johnson     6
  f_pos
1     1
2     1
3     1
4     1
5     1
6     1

Let’s take a glance at the column names.

### List out column names
names(sfb9_data)
 [1] "lg_name"  "div_name" "rd"       "pk"       "ovr"      "p_time"   "p_com"    "fran_at"  "fran_num"
[10] "p_pos"    "p_tm"     "p_full"   "n_pos"    "f_pos"   

Then, we can edit the column names to be a bit more clear.

### Rename columns
names(sfb9_data) <- c("League", "Division", "Round", "Pick", "Overall", 
                      "Draft_Time", "Predraft", "Team_Name", "Team_Id", "Position", 
                      "NFL_Team", "Player", "League_Pos_Rank", "Team_Pos_Rank")
  
### List out new column names
names(sfb9_data)
 [1] "League"          "Division"        "Round"           "Pick"            "Overall"        
 [6] "Draft_Time"      "Predraft"        "Team_Name"       "Team_Id"         "Position"       
[11] "NFL_Team"        "Player"          "League_Pos_Rank" "Team_Pos_Rank"  

Next, we will calculate ADP. We create a new data frame based off ‘sfb9_data’, which filters out NA values (Timer Expired or similar non-player picks), groups by Player/Position/NFL_Team, calculates a few new columns, and sorts by True_ADP.

Count is the number of times drafted. ADP is the standard calculation, while True_ADP accounts for number of times going undrafted. ADP & True_ADP will match for anyone who was drafted all 100 times, but look at where Eric Tomlinson falls if you sort by one vs. the other for a good example of why I use True_ADP.

### Calculate ADP
sfb9_adp <- sfb9_data %>% 
  filter(!is.na(Player)) %>% 
  group_by(Player, Position, NFL_Team) %>% 
  summarize(Count=n(), ADP=mean(Overall), True_ADP=(mean(Overall)*n() + (100-n())*265)/100) %>% 
  arrange(True_ADP)
  
### Preview the ADP data frame
sfb9_adp %>% head

Now that we have ADP data, we will join that to the granular draft data.

### Join ADP data to granular draft data
sfb9_drafts_adp <- left_join(sfb9_data, sfb9_adp)
Joining, by = c("Position", "NFL_Team", "Player")

Here, we are breaking out the overall draft data into separate data frames for each position.

### Create position-specific data frames
qb_adp <- sfb9_drafts_adp %>% filter(Position=="QB")
rb_adp <- sfb9_drafts_adp %>% filter(Position=="RB")
wr_adp <- sfb9_drafts_adp %>% filter(Position=="WR")
te_adp <- sfb9_drafts_adp %>% filter(Position=="TE")
  
### Preview the QB data frame
qb_adp %>% head
    League Division Round Pick Overall Draft_Time Predraft       Team_Name  Team_Id Position NFL_Team
1 Shooters  Doomguy     1   10      10 1562604664     <NA>        @sberg82 10733010       QB       KC
2 Shooters  Doomguy     2    6      18 1562607574     <NA>     @Eliotcrist 10733005       QB      HOU
3 Shooters  Doomguy     3    9      33 1562627564     <NA>     @JeneBramel 10733001       QB      IND
4 Shooters  Doomguy     3   11      35 1562633371     <NA>       @IDP_Dude 10733007       QB       GB
5 Shooters  Doomguy     4    7      43 1562676015     <NA> @DynastyInfidel 10733006       QB      CLE
6 Shooters  Doomguy     4   10      46 1562689928     <NA>      @othmanfcb 10733003       QB      ATL
           Player League_Pos_Rank Team_Pos_Rank Count   ADP True_ADP
1 Patrick Mahomes               1             1   100 10.75    10.75
2  Deshaun Watson               2             1   100 22.01    22.01
3     Andrew Luck               3             1   100 25.77    25.77
4   Aaron Rodgers               4             1   100 29.35    29.35
5  Baker Mayfield               5             1   100 35.29    35.29
6       Matt Ryan               6             1   100 40.61    40.61

Here we plot Overall draft pick vs. ADP for the qb_adp data frame, using geom_point to produce a scatter with our specified color & alpha (transparency).

### Define QB Plot
qb_plot <- ggplot(qb_adp, aes(x=True_ADP, y=Overall)) + 
  geom_point(alpha=.5, color="#C77CFF")
  
### Show QB Plot
qb_plot


Now, let’s add a title & edit the axis labels (which will make more sense after our next block of code).

### Add title & edit axis labels
qb_plot <- ggplot(qb_adp, aes(x=True_ADP, y=Overall)) + 
  geom_point(alpha=.5, color="#C77CFF") + 
  
  labs(title="\nVisualizing QB Tiers in #SFB9", subtitle="") + 
  xlab("\nADP (Round)\n") + 
  ylab("\nDraft Round\n")
  
### Show Plot
qb_plot


To match our axis labels & see where in the draft we are more easily, we’re going to edit the axis scales to be based on draft round instead of overall pick. In the scale, we are defining the breaks (where to have text) to be a sequence of the middle of each round, and defining the labels as the numbers 1 through 22.

### Edit axis ticks
qb_plot <- ggplot(qb_adp, aes(x=True_ADP, y=Overall)) + 
  geom_point(alpha=.5, color="#C77CFF") + 
  labs(title="\nVisualizing QB Tiers in #SFB9", subtitle="") + 
  xlab("\nADP (Round)\n") + 
  ylab("\nDraft Round\n") + 
  
  scale_x_continuous(breaks=seq(6, 260, 12), labels=seq(1, 22, 1)) + 
  scale_y_continuous(breaks=seq(6, 260, 12), labels=seq(1, 22, 1))
  
### Show QB Plot
qb_plot


Now, we’ll use ‘coord_cartesian’ to help us zoom into the range we’d like to focus on. This can be used to look at just the first x rounds, but here we’ll just trim the excess space on the ends. (You have to use a bit of trial & error with this function as I had to specify limits of 12-252 to actually get 0-264, as it always adds a little onto each end.)

### Edit display settings to zoom/focus on range
qb_plot <- ggplot(qb_adp, aes(x=True_ADP, y=Overall)) + 
  geom_point(alpha=.5, color="#C77CFF") + 
  labs(title="\nVisualizing QB Tiers in #SFB9", subtitle="") + 
  xlab("\nADP (Round)\n") + 
  ylab("\nDraft Round\n") + 
  scale_x_continuous(breaks=seq(6, 260, 12), labels=seq(1, 22, 1)) + 
  scale_y_continuous(breaks=seq(6, 260, 12), labels=seq(1, 22, 1)) + 
  
  coord_cartesian(xlim=c(12, 252), ylim=c(12, 252))
  
### Show QB Plot
qb_plot


Finally, we’ll edit the theme settings. There are a ton of options here, but we’re just going to change some font sizes, remove the legend, and edit the gridlines. [Note on the gridlines: because we defined the axis scale breaks to be at the middle of the round, we need to show the minor gridlines (and hide the major gridlines) if we want to segment the plot into rounds.]

### Edit theme settings to change text sizes, remove the legend, and edit gridlines
qb_plot <- ggplot(qb_adp, aes(x=True_ADP, y=Overall)) + 
  geom_point(alpha=.5, color="#C77CFF") + 
  labs(title="\nVisualizing QB Tiers in #SFB9", subtitle="") + 
  xlab("\nADP (Round)\n") + 
  ylab("\nDraft Round\n") + 
  scale_x_continuous(breaks=seq(6, 260, 12), labels=seq(1, 22, 1), limits=c(0, 265)) + 
  scale_y_continuous(breaks=seq(6, 260, 12), labels=seq(1, 22, 1), limits=c(0, 265)) + 
  coord_cartesian(xlim=c(12, 252), ylim=c(12, 252)) + 
  
  theme(plot.title=element_text(hjust=0.5, size=18), plot.subtitle=element_text(hjust=0.5, size=14), 
        axis.title.x=element_text(size=12), axis.title.y=element_text(size=12), 
        axis.text.x=element_text(size=10), axis.text.y=element_text(size=10), 
        panel.grid.major.x=element_blank(), panel.grid.minor.x=element_line(color="#0f0f0f", size=.25), 
        panel.grid.major.y=element_blank(), panel.grid.minor.y=element_line(color="#0f0f0f", size=.25), 
        legend.position="none")
  
### Show QB Plot
qb_plot


To tie up our plot, we can add an annotation to call out Mahomes. In general, calling out data points is best done with the ‘geom_label’ or ‘geom_text’ functions. But the way I labeled the tiers called for a manual method.

### Show QB Plot (w/ 1st annotation)
qb_plot + 
  annotate("label", label="Mahomes", x=10.5, y=42, color="#7CAE00", size=2)


I hope you find this useful. If you run into issues, have questions on how to apply this to other tasks, or need any help with R, feel free to DM me on twitter.

LS0tCnRpdGxlOiAiVmlzdWFsaXppbmcgUG9zaXRpb25hbCBUaWVycyBpbiAjU0ZCOSIKb3V0cHV0OiBwZGZfZG9jdW1lbnQKLS0tCgotLS0KCiMjIyMjIEkgcmVjZW50bHkgcG9zdGVkIHBsb3RzIHZpc3VhbGl6aW5nIHBvc2l0aW9uYWwgdGllcnMgaW4gI1NGQjkuIEhlcmUncyBhIHN0ZXAtYnktc3RlcCBndWlkZSB0byByZXBsaWNhdGluZyBteSBwcm9jZXNzIGluIFIuIEhvcGUgdGhpcyBoZWxwcyEgIAoKLS0tCgpCZWZvcmUgd2UgZ2V0IHN0YXJ0ZWQgaW4gUiwgc2F2ZSBkb3duIHRoZSBTRkI5IERhdGEgZnJvbSB0aGUgW0dvb2dsZSBTaGVldF0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMXZnZXhpRF8tbWNaNFFRS0s3Zlpzd25fV1pFeWpxN3dTTEd5eDMxbjF6QVEvZWRpdCNnaWQ9MTcwNzQxMTU5KSBjcmVhdGVkIGJ5IFtKb3NoIEhvcm5zYnldKGh0dHBzOi8vdHdpdHRlci5jb20vSm9zaF9BREhEKS4gU2F2ZSBpdCBkb3duIGFzIGEgY3N2LCB3aGljaCBzaG91bGQgYmUgbmFtZWQgIiNTRkI5IERhdGEgLSBzZmJfZGF0YS5jc3YiIGJ5IGRlZmF1bHQuIElmIG5vdCwgbWFrZSBzdXJlIHRoZSBmaWxlbmFtZSBpbiB0aGUgc2Vjb25kIGJsb2NrIG9mIGNvZGUgbWF0Y2hlcyB5b3Vycy4KCi0tLQoKTmV4dCwgd2Ugd2lsbCBsb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcyAoJ2RwbHlyJyBmb3IgbWFuaXB1bGF0aW5nIHRoZSBkYXRhLCAnZ2dwbG90MicgdG8gaGFuZGxlIHRoZSBwbG90dGluZykuIE1ha2Ugc3VyZSB0byBpbnN0YWxsIHRoZXNlIGZpcnN0LCBpZiB0aGV5IGFyZSBub3QgYWxyZWFkeS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CiMjIyBMb2FkIHJlcXVpcmVkIHBhY2thZ2VzCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgotLS0KCk5vdywgd2Ugd2lsbCByZWFkIHRoZSBkYXRhIGZyb20gdGhlIGNzdiBpbnRvIGEgZGF0YSBmcmFtZSwgdXNpbmcgJ2NvbENsYXNzZXMnIHRvIGRlZmluZSB0aGUgY29sdW1uIHR5cGVzICh0aGlzIHBhcnQgaXMgbm90IG5lY2Vzc2FyeSwgYnV0IGlzIHVzZWZ1bCB0byBhdm9pZCBwcm9ibGVtcyB0aGF0IGNhbiBhcmlzZSBkdWUgdG8gYSBjb2x1bW4gb2YgbnVtYmVycyBiZWluZyByZWFkIGluIGFzIGNoYXJhY3RlcnMpLiBUaGVuLCB3ZSdsbCBwcmV2aWV3IHRoZSB0b3Agb2YgdGhlIGRhdGEgZnJhbWUuCgpgYGB7cn0KIyMjIFJlYWQgaW4gQ1NWCnNmYjlfZGF0YSA8LSByZWFkLmNzdihmaWxlPSIjU0ZCOSBEYXRhIC0gc2ZiX2RhdGEuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgIGNvbENsYXNzZXM9YygiY2hhcmFjdGVyIiwgImNoYXJhY3RlciIsICJpbnRlZ2VyIiwgImludGVnZXIiLCAiaW50ZWdlciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNoYXJhY3RlciIsICJjaGFyYWN0ZXIiLCAiY2hhcmFjdGVyIiwgImludGVnZXIiLCAiY2hhcmFjdGVyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2hhcmFjdGVyIiwgImNoYXJhY3RlciIsICJpbnRlZ2VyIiwgImludGVnZXIiKSkKICAKIyMjIFByZXZpZXcgdGhlIGRhdGEgZnJhbWUKc2ZiOV9kYXRhICU+JSBoZWFkCmBgYAoKLS0tCgpMZXQncyB0YWtlIGEgZ2xhbmNlIGF0IHRoZSBjb2x1bW4gbmFtZXMuCgpgYGB7cn0KIyMjIExpc3Qgb3V0IGNvbHVtbiBuYW1lcwpuYW1lcyhzZmI5X2RhdGEpCmBgYAoKLS0tCgpUaGVuLCB3ZSBjYW4gZWRpdCB0aGUgY29sdW1uIG5hbWVzIHRvIGJlIGEgYml0IG1vcmUgY2xlYXIuCgpgYGB7cn0KIyMjIFJlbmFtZSBjb2x1bW5zCm5hbWVzKHNmYjlfZGF0YSkgPC0gYygiTGVhZ3VlIiwgIkRpdmlzaW9uIiwgIlJvdW5kIiwgIlBpY2siLCAiT3ZlcmFsbCIsIAogICAgICAgICAgICAgICAgICAgICAgIkRyYWZ0X1RpbWUiLCAiUHJlZHJhZnQiLCAiVGVhbV9OYW1lIiwgIlRlYW1fSWQiLCAiUG9zaXRpb24iLCAKICAgICAgICAgICAgICAgICAgICAgICJORkxfVGVhbSIsICJQbGF5ZXIiLCAiTGVhZ3VlX1Bvc19SYW5rIiwgIlRlYW1fUG9zX1JhbmsiKQogIAojIyMgTGlzdCBvdXQgbmV3IGNvbHVtbiBuYW1lcwpuYW1lcyhzZmI5X2RhdGEpCmBgYAoKLS0tCgpOZXh0LCB3ZSB3aWxsIGNhbGN1bGF0ZSBBRFAuIFdlIGNyZWF0ZSBhIG5ldyBkYXRhIGZyYW1lIGJhc2VkIG9mZiAnc2ZiOV9kYXRhJywgd2hpY2ggZmlsdGVycyBvdXQgTkEgdmFsdWVzIChUaW1lciBFeHBpcmVkIG9yIHNpbWlsYXIgbm9uLXBsYXllciBwaWNrcyksIGdyb3VwcyBieSBQbGF5ZXIvUG9zaXRpb24vTkZMX1RlYW0sIGNhbGN1bGF0ZXMgYSBmZXcgbmV3IGNvbHVtbnMsIGFuZCBzb3J0cyBieSBUcnVlX0FEUC4KCkNvdW50IGlzIHRoZSBudW1iZXIgb2YgdGltZXMgZHJhZnRlZC4gQURQIGlzIHRoZSBzdGFuZGFyZCBjYWxjdWxhdGlvbiwgd2hpbGUgVHJ1ZV9BRFAgYWNjb3VudHMgZm9yIG51bWJlciBvZiB0aW1lcyBnb2luZyB1bmRyYWZ0ZWQuIEFEUCAmIFRydWVfQURQIHdpbGwgbWF0Y2ggZm9yIGFueW9uZSB3aG8gd2FzIGRyYWZ0ZWQgYWxsIDEwMCB0aW1lcywgYnV0IGxvb2sgYXQgd2hlcmUgRXJpYyBUb21saW5zb24gZmFsbHMgaWYgeW91IHNvcnQgYnkgb25lIHZzLiB0aGUgb3RoZXIgZm9yIGEgZ29vZCBleGFtcGxlIG9mIHdoeSBJIHVzZSBUcnVlX0FEUC4KCmBgYHtyfQojIyMgQ2FsY3VsYXRlIEFEUApzZmI5X2FkcCA8LSBzZmI5X2RhdGEgJT4lIAogIGZpbHRlcighaXMubmEoUGxheWVyKSkgJT4lIAogIGdyb3VwX2J5KFBsYXllciwgUG9zaXRpb24sIE5GTF9UZWFtKSAlPiUgCiAgc3VtbWFyaXplKENvdW50PW4oKSwgQURQPW1lYW4oT3ZlcmFsbCksIFRydWVfQURQPShtZWFuKE92ZXJhbGwpKm4oKSArICgxMDAtbigpKSoyNjUpLzEwMCkgJT4lIAogIGFycmFuZ2UoVHJ1ZV9BRFApCiAgCiMjIyBQcmV2aWV3IHRoZSBBRFAgZGF0YSBmcmFtZQpzZmI5X2FkcCAlPiUgaGVhZApgYGAKCi0tLQoKTm93IHRoYXQgd2UgaGF2ZSBBRFAgZGF0YSwgd2Ugd2lsbCBqb2luIHRoYXQgdG8gdGhlIGdyYW51bGFyIGRyYWZ0IGRhdGEuCgpgYGB7cn0KIyMjIEpvaW4gQURQIGRhdGEgdG8gZ3JhbnVsYXIgZHJhZnQgZGF0YQpzZmI5X2RyYWZ0c19hZHAgPC0gbGVmdF9qb2luKHNmYjlfZGF0YSwgc2ZiOV9hZHApCmBgYAoKLS0tCgpIZXJlLCB3ZSBhcmUgYnJlYWtpbmcgb3V0IHRoZSBvdmVyYWxsIGRyYWZ0IGRhdGEgaW50byBzZXBhcmF0ZSBkYXRhIGZyYW1lcyBmb3IgZWFjaCBwb3NpdGlvbi4KCmBgYHtyfQojIyMgQ3JlYXRlIHBvc2l0aW9uLXNwZWNpZmljIGRhdGEgZnJhbWVzCnFiX2FkcCA8LSBzZmI5X2RyYWZ0c19hZHAgJT4lIGZpbHRlcihQb3NpdGlvbj09IlFCIikKcmJfYWRwIDwtIHNmYjlfZHJhZnRzX2FkcCAlPiUgZmlsdGVyKFBvc2l0aW9uPT0iUkIiKQp3cl9hZHAgPC0gc2ZiOV9kcmFmdHNfYWRwICU+JSBmaWx0ZXIoUG9zaXRpb249PSJXUiIpCnRlX2FkcCA8LSBzZmI5X2RyYWZ0c19hZHAgJT4lIGZpbHRlcihQb3NpdGlvbj09IlRFIikKICAKIyMjIFByZXZpZXcgdGhlIFFCIGRhdGEgZnJhbWUKcWJfYWRwICU+JSBoZWFkCmBgYAoKLS0tCgpIZXJlIHdlIHBsb3QgT3ZlcmFsbCBkcmFmdCBwaWNrIHZzLiBBRFAgZm9yIHRoZSBxYl9hZHAgZGF0YSBmcmFtZSwgdXNpbmcgZ2VvbV9wb2ludCB0byBwcm9kdWNlIGEgc2NhdHRlciB3aXRoIG91ciBzcGVjaWZpZWQgY29sb3IgJiBhbHBoYSAodHJhbnNwYXJlbmN5KS4gCgpgYGB7cn0KIyMjIERlZmluZSBRQiBQbG90CnFiX3Bsb3QgPC0gZ2dwbG90KHFiX2FkcCwgYWVzKHg9VHJ1ZV9BRFAsIHk9T3ZlcmFsbCkpICsgCiAgZ2VvbV9wb2ludChhbHBoYT0uNSwgY29sb3I9IiNDNzdDRkYiKQogIAojIyMgU2hvdyBRQiBQbG90CnFiX3Bsb3QKYGBgCgotLS0KCk5vdywgbGV0J3MgYWRkIGEgdGl0bGUgJiBlZGl0IHRoZSBheGlzIGxhYmVscyAod2hpY2ggd2lsbCBtYWtlIG1vcmUgc2Vuc2UgYWZ0ZXIgb3VyIG5leHQgYmxvY2sgb2YgY29kZSkuCgpgYGB7cn0KIyMjIEFkZCB0aXRsZSAmIGVkaXQgYXhpcyBsYWJlbHMKcWJfcGxvdCA8LSBnZ3Bsb3QocWJfYWRwLCBhZXMoeD1UcnVlX0FEUCwgeT1PdmVyYWxsKSkgKyAKICBnZW9tX3BvaW50KGFscGhhPS41LCBjb2xvcj0iI0M3N0NGRiIpICsgCiAgCiAgbGFicyh0aXRsZT0iXG5WaXN1YWxpemluZyBRQiBUaWVycyBpbiAjU0ZCOSIsIHN1YnRpdGxlPSIiKSArIAogIHhsYWIoIlxuQURQIChSb3VuZClcbiIpICsgCiAgeWxhYigiXG5EcmFmdCBSb3VuZFxuIikKICAKIyMjIFNob3cgUGxvdApxYl9wbG90CmBgYAoKLS0tCgpUbyBtYXRjaCBvdXIgYXhpcyBsYWJlbHMgJiBzZWUgd2hlcmUgaW4gdGhlIGRyYWZ0IHdlIGFyZSBtb3JlIGVhc2lseSwgd2UncmUgZ29pbmcgdG8gZWRpdCB0aGUgYXhpcyBzY2FsZXMgdG8gYmUgYmFzZWQgb24gZHJhZnQgcm91bmQgaW5zdGVhZCBvZiBvdmVyYWxsIHBpY2suIEluIHRoZSBzY2FsZSwgd2UgYXJlIGRlZmluaW5nIHRoZSBicmVha3MgKHdoZXJlIHRvIGhhdmUgdGV4dCkgdG8gYmUgYSBzZXF1ZW5jZSBvZiB0aGUgbWlkZGxlIG9mIGVhY2ggcm91bmQsIGFuZCBkZWZpbmluZyB0aGUgbGFiZWxzIGFzIHRoZSBudW1iZXJzIDEgdGhyb3VnaCAyMi4KCmBgYHtyfQojIyMgRWRpdCBheGlzIHRpY2tzCnFiX3Bsb3QgPC0gZ2dwbG90KHFiX2FkcCwgYWVzKHg9VHJ1ZV9BRFAsIHk9T3ZlcmFsbCkpICsgCiAgZ2VvbV9wb2ludChhbHBoYT0uNSwgY29sb3I9IiNDNzdDRkYiKSArIAogIGxhYnModGl0bGU9IlxuVmlzdWFsaXppbmcgUUIgVGllcnMgaW4gI1NGQjkiLCBzdWJ0aXRsZT0iIikgKyAKICB4bGFiKCJcbkFEUCAoUm91bmQpXG4iKSArIAogIHlsYWIoIlxuRHJhZnQgUm91bmRcbiIpICsgCiAgCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoNiwgMjYwLCAxMiksIGxhYmVscz1zZXEoMSwgMjIsIDEpKSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDYsIDI2MCwgMTIpLCBsYWJlbHM9c2VxKDEsIDIyLCAxKSkKICAKIyMjIFNob3cgUUIgUGxvdApxYl9wbG90CmBgYAoKLS0tCgpOb3csIHdlJ2xsIHVzZSAnY29vcmRfY2FydGVzaWFuJyB0byBoZWxwIHVzIHpvb20gaW50byB0aGUgcmFuZ2Ugd2UnZCBsaWtlIHRvIGZvY3VzIG9uLiBUaGlzIGNhbiBiZSB1c2VkIHRvIGxvb2sgYXQganVzdCB0aGUgZmlyc3QgeCByb3VuZHMsIGJ1dCBoZXJlIHdlJ2xsIGp1c3QgdHJpbSB0aGUgZXhjZXNzIHNwYWNlIG9uIHRoZSBlbmRzLiAoWW91IGhhdmUgdG8gdXNlIGEgYml0IG9mIHRyaWFsICYgZXJyb3Igd2l0aCB0aGlzIGZ1bmN0aW9uIGFzIEkgaGFkIHRvIHNwZWNpZnkgbGltaXRzIG9mIDEyLTI1MiB0byBhY3R1YWxseSBnZXQgMC0yNjQsIGFzIGl0IGFsd2F5cyBhZGRzIGEgbGl0dGxlIG9udG8gZWFjaCBlbmQuKQoKYGBge3J9CiMjIyBFZGl0IGRpc3BsYXkgc2V0dGluZ3MgdG8gem9vbS9mb2N1cyBvbiByYW5nZQpxYl9wbG90IDwtIGdncGxvdChxYl9hZHAsIGFlcyh4PVRydWVfQURQLCB5PU92ZXJhbGwpKSArIAogIGdlb21fcG9pbnQoYWxwaGE9LjUsIGNvbG9yPSIjQzc3Q0ZGIikgKyAKICBsYWJzKHRpdGxlPSJcblZpc3VhbGl6aW5nIFFCIFRpZXJzIGluICNTRkI5Iiwgc3VidGl0bGU9IiIpICsgCiAgeGxhYigiXG5BRFAgKFJvdW5kKVxuIikgKyAKICB5bGFiKCJcbkRyYWZ0IFJvdW5kXG4iKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDYsIDI2MCwgMTIpLCBsYWJlbHM9c2VxKDEsIDIyLCAxKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSg2LCAyNjAsIDEyKSwgbGFiZWxzPXNlcSgxLCAyMiwgMSkpICsgCiAgCiAgY29vcmRfY2FydGVzaWFuKHhsaW09YygxMiwgMjUyKSwgeWxpbT1jKDEyLCAyNTIpKQogIAojIyMgU2hvdyBRQiBQbG90CnFiX3Bsb3QKYGBgCgotLS0KCkZpbmFsbHksIHdlJ2xsIGVkaXQgdGhlIHRoZW1lIHNldHRpbmdzLiBUaGVyZSBhcmUgYSB0b24gb2Ygb3B0aW9ucyBoZXJlLCBidXQgd2UncmUganVzdCBnb2luZyB0byBjaGFuZ2Ugc29tZSBmb250IHNpemVzLCByZW1vdmUgdGhlIGxlZ2VuZCwgYW5kIGVkaXQgdGhlIGdyaWRsaW5lcy4gW05vdGUgb24gdGhlIGdyaWRsaW5lczogYmVjYXVzZSB3ZSBkZWZpbmVkIHRoZSBheGlzIHNjYWxlIGJyZWFrcyB0byBiZSBhdCB0aGUgbWlkZGxlIG9mIHRoZSByb3VuZCwgd2UgbmVlZCB0byBzaG93IHRoZSBtaW5vciBncmlkbGluZXMgKGFuZCBoaWRlIHRoZSBtYWpvciBncmlkbGluZXMpIGlmIHdlIHdhbnQgdG8gc2VnbWVudCB0aGUgcGxvdCBpbnRvIHJvdW5kcy5dCgpgYGB7cn0KIyMjIEVkaXQgdGhlbWUgc2V0dGluZ3MgdG8gY2hhbmdlIHRleHQgc2l6ZXMsIHJlbW92ZSB0aGUgbGVnZW5kLCBhbmQgZWRpdCBncmlkbGluZXMKcWJfcGxvdCA8LSBnZ3Bsb3QocWJfYWRwLCBhZXMoeD1UcnVlX0FEUCwgeT1PdmVyYWxsKSkgKyAKICBnZW9tX3BvaW50KGFscGhhPS41LCBjb2xvcj0iI0M3N0NGRiIpICsgCiAgbGFicyh0aXRsZT0iXG5WaXN1YWxpemluZyBRQiBUaWVycyBpbiAjU0ZCOSIsIHN1YnRpdGxlPSIiKSArIAogIHhsYWIoIlxuQURQIChSb3VuZClcbiIpICsgCiAgeWxhYigiXG5EcmFmdCBSb3VuZFxuIikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSg2LCAyNjAsIDEyKSwgbGFiZWxzPXNlcSgxLCAyMiwgMSksIGxpbWl0cz1jKDAsIDI2NSkpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz1zZXEoNiwgMjYwLCAxMiksIGxhYmVscz1zZXEoMSwgMjIsIDEpLCBsaW1pdHM9YygwLCAyNjUpKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMTIsIDI1MiksIHlsaW09YygxMiwgMjUyKSkgKyAKICAKICB0aGVtZShwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9MTgpLCBwbG90LnN1YnRpdGxlPWVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHNpemU9MTQpLCAKICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF90ZXh0KHNpemU9MTIpLCBheGlzLnRpdGxlLnk9ZWxlbWVudF90ZXh0KHNpemU9MTIpLCAKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT0xMCksIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci54PWVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vci54PWVsZW1lbnRfbGluZShjb2xvcj0iIzBmMGYwZiIsIHNpemU9LjI1KSwgCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55PWVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vci55PWVsZW1lbnRfbGluZShjb2xvcj0iIzBmMGYwZiIsIHNpemU9LjI1KSwgCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIikKICAKIyMjIFNob3cgUUIgUGxvdApxYl9wbG90CmBgYAoKLS0tCgpUbyB0aWUgdXAgb3VyIHBsb3QsIHdlIGNhbiBhZGQgYW4gYW5ub3RhdGlvbiB0byBjYWxsIG91dCBNYWhvbWVzLiBJbiBnZW5lcmFsLCBjYWxsaW5nIG91dCBkYXRhIHBvaW50cyBpcyBiZXN0IGRvbmUgd2l0aCB0aGUgJ2dlb21fbGFiZWwnIG9yICdnZW9tX3RleHQnIGZ1bmN0aW9ucy4gQnV0IHRoZSB3YXkgSSBsYWJlbGVkIHRoZSB0aWVycyBjYWxsZWQgZm9yIGEgbWFudWFsIG1ldGhvZC4KCmBgYHtyfQojIyMgU2hvdyBRQiBQbG90ICh3LyAxc3QgYW5ub3RhdGlvbikKcWJfcGxvdCArIAogIGFubm90YXRlKCJsYWJlbCIsIGxhYmVsPSJNYWhvbWVzIiwgeD0xMC41LCB5PTQyLCBjb2xvcj0iIzdDQUUwMCIsIHNpemU9MikKYGBgCgotLS0KCkkgaG9wZSB5b3UgZmluZCB0aGlzIHVzZWZ1bC4gSWYgeW91IHJ1biBpbnRvIGlzc3VlcywgaGF2ZSBxdWVzdGlvbnMgb24gaG93IHRvIGFwcGx5IHRoaXMgdG8gb3RoZXIgdGFza3MsIG9yIG5lZWQgYW55IGhlbHAgd2l0aCBSLCBmZWVsIGZyZWUgdG8gRE0gbWUgb24gW3R3aXR0ZXJdKGh0dHBzOi8vdHdpdHRlci5jb20vU2xhdmluMjIpLgo=