UFC 208 took place in February 2017, with 9 out of 10 fights going to a decision. I wanted to look at that number in the context of UFC history.

library(ggplot2)
library(dplyr)
library(tidyr)
fighters = read.csv("C://Users//Jason//Downloads//UFC-Fight-Card-Analysis-master//UFC-Fight-Card-Analysis-master//ALL UFC FIGHTERS 2%2F23%2F2016 SHERDOG.COM - Sheet1.csv", header=T)
fights = read.csv("C://Users//Jason//Downloads//UFC-Fight-Card-Analysis-master//UFC-Fight-Card-Analysis-master////ALL UFC FIGHTS 2%2F23%2F2016 SHERDOG.COM - Sheet1.csv", header=T)

Some quick cleanup to remove any URLs and change the date from a character to date object.

fights= fights[,-c(1,8,9)]
fighters = fighters[, -1]

fights$event_date = as.Date(fights$event_date, "%m/%d/%Y")

The fights lists several different methods of victory, many of which are abnormalities (no contests, DQ, etc.). We need to separate only the fights that ended using the 4 main methods.

#Main methods of victory
methods=c("Submission", "TKO", "KO", "Decision")
fights_method_subset = fights %>% filter(method %in% methods)

An issue with the early UFCs were that they were constantly changing rules to try to appease different stakeholders. It wasn’t until Zuffa bought the company that they really moved towards a consistent, (mostly) unified rules system. Let’s subset the data starting from UFC 30 (2/23/2001), which was the first event of the Zuffa ownership.

fights_method_subset = fights_method_subset %>% filter(event_date >= "2001-02-23")

We will attempt to solve the question of how likely a card with 90% decisions was to occur by using hacker statistics. To do this, we will simulate a UFC card thousands of times to create a probability distribution of outcomes.

Start by creating vectors to store the number of results of each individual method of victory on each simulated card.

#initialize vectors for simulation
dec_sim=c()
sub_sim=c()
ko_sim =c()
tko_sim = c()

Setup the simulation parameters, including the number of iterations, the seed, and the number of fights on each simulated card (UFC 208 had 10 fights on it, so we’ll use 10).

(Note: My computer seems to churn about 1000 iterations or so per minute. If you’re impatient, then maybe consider reducing 20,000 iterations down to a couple thousand. The more trials, the more central the results though.)

iterations=20000
set.seed(1)
# Set number of fights on the card
n_fights = 10

Create a monte carlo simulation, which randomly selects 10 fights in the data, and then appends the number of different methods of finishes to the vectors we created.

for (i in 1:iterations){
  
  # create sample n-fight card  
  sample = sample(fights_method_subset$method, n_fights, replace=T)
  
  # assign no. of finishes of each type to a vector  
  for(method in methods){  
    
    #Loop through the 4 methods, get the sum of the number of finishes on the card, and assign it to the appropriate vector.
    x = sum(sample == method)
    
    if(method == methods[2]){
      tko_sim[i] = x
    } else if(method == methods[1]){
      sub_sim[i] = x
    } else if(method == methods[3]){
      ko_sim[i] = x
    } else{
      dec_sim[i] = x
    }
    
  }
}

Move all of the simulation results into a data frame, sim_df. Then, run the summary stats. The average 10-fight card has about 4.5 decisions and 5.5 finishes.

sim_df = data.frame(dec = dec_sim, tko = tko_sim, ko = ko_sim, sub = sub_sim)
summary(sim_df)
      dec              tko              ko             sub       
 Min.   : 0.000   Min.   :0.000   Min.   :0.000   Min.   :0.000  
 1st Qu.: 3.000   1st Qu.:1.000   1st Qu.:0.000   1st Qu.:1.000  
 Median : 4.000   Median :2.000   Median :1.000   Median :2.000  
 Mean   : 4.492   Mean   :2.212   Mean   :1.225   Mean   :2.071  
 3rd Qu.: 6.000   3rd Qu.:3.000   3rd Qu.:2.000   3rd Qu.:3.000  
 Max.   :10.000   Max.   :8.000   Max.   :7.000   Max.   :8.000  

The 4 finishes are really just one variable, but they are dispersed among 4 columns. So this is a wide-data frame that needs to be converted to long for graphical purposes, which can be done easily using Tidyr’s gather function.

sim_wide = gather(sim_df, method, n)
head(sim_wide)

View the frequency of each type of finish in the simulation. Looks like the average show typically clusters around 3-6 decisions, 0-2 knockouts, and 1-3 submissions and TKOs.

ggplot(sim_wide, aes(x=n, fill=as.factor(n)) ) + 
  geom_bar(aes(y = (..count..)), show.legend = F) +
  scale_x_continuous(breaks=0:10) + 
  facet_wrap(~method, ncol = 1)

Now let’s look at it as a cross-table.

sim_table = table(sim_wide$method, sim_wide$n)
sim_table
     
         0    1    2    3    4    5    6    7    8    9   10
  dec   54  391 1559 3275 4833 4738 3183 1454  423   79   11
  ko  5466 7492 4743 1781  427   78    9    4    0    0    0
  sub 2027 5039 6070 4148 1949  619  129   17    2    0    0
  tko 1634 4730 5903 4467 2282  766  183   34    1    0    0

So the probability of a 10-fight UFC card having 9 decisions is approximately 79/20000 (and 90/20000 if you include the 11 times all 10 fights went to decision). To put this in decimal form comes out to…

#decimal probability
sum(sim_table["dec", c("9", "10")]) / iterations
[1] 0.0045
#One out of every x events
iterations / sum(sim_table["dec", c("9", "10")]) 
[1] 222.2222

So UFC 208 probabilisticly was a one-in-222 show event. For reference, UFC 208 was the 359th show in the Zuffa era of UFC, which means that statistically speaking it should have only occurred approximately 1.6 times during the entire 15-year sample. To visualize that outlier…

ggplot(subset(sim_wide, method=="dec"), aes(x=n, y= ..density..)) + 
  geom_freqpoly(binwidth=1, lwd=2) + 
  scale_x_continuous(breaks=0:10) + 
  geom_vline(xintercept=9, col="red", lwd=1.5) + 
  geom_text(aes(x=9, y=.2, label= paste("UFC 208 \n", 
                                        sum(sim_table["dec", c("9", "10")])/iterations )), 
            hjust=-.25,  color="red", size=6)+
  ggtitle(paste("Probability of x Decisions on a", n_fights,  "Fight UFC Card")) +
  theme(plot.title = element_text(size = 16, face = "bold")) +
  xlab(paste("Decisions (out of ", n_fights, ")", sep=""))

Those results were in the simulated world we created. How does 9/10 decisions fare in the actual course of UFC history? We need to get the number of finishes on each actual show to find out.

#Get a list of all event names sampled
event_names = unique(fights_method_subset$event_name)
#Create a new DF that simply has each show in UFC history, and then a count of all the different finishes on that show (we'll populate those counts in the next loop)
events = data.frame(event = event_names, submissions = 0, decisions = 0, ko = 0, tko = 0, other = 0)
head(events)

Loop through the original fights DF (the subset we created got rid of all the “other” finishes, which we need to count again). In the loop, we’ll keep a running count of the number of different finishes on each show.

(the loop only takes about 30 seconds)

#loop through each event in the sample to count number of finishes per card
for(ev in event_names){
  
  #Since we threw out all the "other" finishes in the initial subset, we'll use the original fights DF to count the number of different results
  for(row in 1:nrow(fights)){
    
    #look for the proper row that corresponds to the looping event
    if(fights$event_name[row] == ev){
      
      #if the fight ended by submission, then add 1 to the new events DF we created under the subm. column
      if(fights$method[row] == "Submission"){
        events[events[,1] == ev, 2] = events[events[,1] == ev, 2] +1
        
      } else if(fights$method[row] == "Decision"){
        events[events[,1] == ev, 3] = events[events[,1] == ev, 3] +1
        
      } else if(fights$method[row] == "KO"){
        events[events[,1] == ev, 4] = events[events[,1] == ev, 4] +1
        
      } else if(fights$method[row] == "TKO"){
        events[events[,1] == ev, 5] = events[events[,1] == ev, 5] +1
        
      } else{
        events[events[,1] == ev, 6] = events[events[,1] == ev, 6] +1
      }
    } 
    
  }
}
head(events)

Now we can calculate the percentage of each finish on each card. First, we’ll count the number of fights on each card, and then use that to get the finish percentages.

#Fetch the sum of each row (removes only the first column, the event name)
events$n_fights = rowSums(events[-1])
#Calculate the percentages of finishes on each card
events = events %>%
  mutate(sub_pct = submissions/n_fights,
         dec_pct = decisions/n_fights,
         ko_pct = ko/n_fights,
         tko_pct = tko/n_fights
  ) %>%
  arrange(desc(dec_pct))

Plot the top 10 cards in UFC history with the highest percentages of decisions. Note: R makes you refactor a categorical variable in order to plot on it in a different order other than the default, so I created a function that does that.

#Function to refactor a categorical variable by the order of a numeric variable
refactor = function(df, col_factor, col_sort){
  df[, col_factor] = factor(df[, col_factor], 
                            levels = df[,col_factor][order(-df[, col_sort])])   
  
  return(df)
}
#refactor the event list by highest decision percentage
events = refactor(events, "event", "dec_pct") %>% arrange(desc(dec_pct))
ggplot(events[1:10,], aes(x=event, y=dec_pct)) + 
  geom_col() + 
  theme(axis.text.x = element_text(face="italic", color="#993333", 
                                   size=8, angle=90)) +
  coord_flip(expand = T) +
  ggtitle("UFC Shows with Highest Percentage of Decisions") +
  scale_y_continuous(breaks = seq(0,.9,.1))

Only 3 of the 346 other shows analyzed had ever cleared 80% decisions, with the 83.3% of UFC 169 and UFC FN 36 being the highest. That really puts the 90% decisions of UFC 208 into perspective!

Here is the list of all shows with at least 70% decisions. Only 16 shows have even done that (which is only 4.6% of all shows)!

Now that we have devoted all this time to analyzing the anomaly that was UFC 208, one of the most boring UFC cards of all time, let’s finish on a high note by creating a watch list for UFC Fight Pass of UFC shows with the highest knockout percentage!

#refactor the event list by highest decision percentage
events = refactor(events, "event", "ko_pct") %>% arrange(desc(ko_pct))
ggplot(events[1:10,], aes(x=event, y=ko_pct)) + 
  geom_col() + 
  theme(axis.text.x = element_text(face="italic", color="#993333", 
                           size=8, angle=90)) +
  coord_flip(expand = T) +
  ggtitle("UFC Shows with Highest Percentage of Knockouts") +
  scale_y_continuous(breaks = seq(0,.5,.05))

NA
LS0tDQp0aXRsZTogIlVGQyBGaWdodCBDYXJkIFByb2JhYmlsaXR5IEFuYWx5c2lzIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciBnbG9iYWxfb3B0aW9ucywgaW5jbHVkZT1GQUxTRSwgZWNobz1UUlVFfQ0KYGBgDQoNClVGQyAyMDggdG9vayBwbGFjZSBpbiBGZWJydWFyeSAyMDE3LCB3aXRoIDkgb3V0IG9mIDEwIGZpZ2h0cyBnb2luZyB0byBhIGRlY2lzaW9uLiBJIHdhbnRlZCB0byBsb29rIGF0IHRoYXQgbnVtYmVyIGluIHRoZSBjb250ZXh0IG9mIFVGQyBoaXN0b3J5Lg0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQoNCmZpZ2h0ZXJzID0gcmVhZC5jc3YoIkM6Ly9Vc2Vycy8vSmFzb24vL0Rvd25sb2Fkcy8vVUZDLUZpZ2h0LUNhcmQtQW5hbHlzaXMtbWFzdGVyLy9VRkMtRmlnaHQtQ2FyZC1BbmFseXNpcy1tYXN0ZXIvL0FMTCBVRkMgRklHSFRFUlMgMiUyRjIzJTJGMjAxNiBTSEVSRE9HLkNPTSAtIFNoZWV0MS5jc3YiLCBoZWFkZXI9VCkNCmZpZ2h0cyA9IHJlYWQuY3N2KCJDOi8vVXNlcnMvL0phc29uLy9Eb3dubG9hZHMvL1VGQy1GaWdodC1DYXJkLUFuYWx5c2lzLW1hc3Rlci8vVUZDLUZpZ2h0LUNhcmQtQW5hbHlzaXMtbWFzdGVyLy8vL0FMTCBVRkMgRklHSFRTIDIlMkYyMyUyRjIwMTYgU0hFUkRPRy5DT00gLSBTaGVldDEuY3N2IiwgaGVhZGVyPVQpDQoNCmBgYA0KDQpTb21lIHF1aWNrIGNsZWFudXAgdG8gcmVtb3ZlIGFueSBVUkxzIGFuZCBjaGFuZ2UgdGhlIGRhdGUgZnJvbSBhIGNoYXJhY3RlciB0byBkYXRlIG9iamVjdC4NCmBgYHtyfQ0KZmlnaHRzPSBmaWdodHNbLC1jKDEsOCw5KV0NCmZpZ2h0ZXJzID0gZmlnaHRlcnNbLCAtMV0NCg0KZmlnaHRzJGV2ZW50X2RhdGUgPSBhcy5EYXRlKGZpZ2h0cyRldmVudF9kYXRlLCAiJW0vJWQvJVkiKQ0KYGBgDQoNClRoZSBmaWdodHMgbGlzdHMgc2V2ZXJhbCBkaWZmZXJlbnQgbWV0aG9kcyBvZiB2aWN0b3J5LCBtYW55IG9mIHdoaWNoIGFyZSBhYm5vcm1hbGl0aWVzIChubyBjb250ZXN0cywgRFEsIGV0Yy4pLiBXZSBuZWVkIHRvIHNlcGFyYXRlIG9ubHkgdGhlIGZpZ2h0cyB0aGF0IGVuZGVkIHVzaW5nIHRoZSA0IG1haW4gbWV0aG9kcy4NCmBgYHtyfQ0KbWV0aG9kcz1jKCJTdWJtaXNzaW9uIiwgIlRLTyIsICJLTyIsICJEZWNpc2lvbiIpDQoNCmZpZ2h0c19tZXRob2Rfc3Vic2V0ID0gZmlnaHRzICU+JSBmaWx0ZXIobWV0aG9kICVpbiUgbWV0aG9kcykNCmBgYA0KDQpBbiBpc3N1ZSB3aXRoIHRoZSBlYXJseSBVRkNzIHdlcmUgdGhhdCB0aGV5IHdlcmUgY29uc3RhbnRseSBjaGFuZ2luZyBydWxlcyB0byB0cnkgdG8gYXBwZWFzZSBkaWZmZXJlbnQgc3Rha2Vob2xkZXJzLiBJdCB3YXNuJ3QgdW50aWwgWnVmZmEgYm91Z2h0IHRoZSBjb21wYW55IHRoYXQgdGhleSByZWFsbHkgbW92ZWQgdG93YXJkcyBhIGNvbnNpc3RlbnQsIChtb3N0bHkpIHVuaWZpZWQgcnVsZXMgc3lzdGVtLiBMZXQncyBzdWJzZXQgdGhlIGRhdGEgc3RhcnRpbmcgZnJvbSBVRkMgMzAgKDIvMjMvMjAwMSksIHdoaWNoIHdhcyB0aGUgZmlyc3QgZXZlbnQgb2YgdGhlIFp1ZmZhIG93bmVyc2hpcC4NCmBgYHtyfQ0KZmlnaHRzX21ldGhvZF9zdWJzZXQgPSBmaWdodHNfbWV0aG9kX3N1YnNldCAlPiUgZmlsdGVyKGV2ZW50X2RhdGUgPj0gIjIwMDEtMDItMjMiKQ0KYGBgDQoNCldlIHdpbGwgYXR0ZW1wdCB0byBzb2x2ZSB0aGUgcXVlc3Rpb24gb2YgaG93IGxpa2VseSBhIGNhcmQgd2l0aCA5MCUgZGVjaXNpb25zIHdhcyB0byBvY2N1ciBieSB1c2luZyBoYWNrZXIgc3RhdGlzdGljcy4gVG8gZG8gdGhpcywgd2Ugd2lsbCBzaW11bGF0ZSBhIFVGQyBjYXJkIHRob3VzYW5kcyBvZiB0aW1lcyB0byBjcmVhdGUgYSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24gb2Ygb3V0Y29tZXMuDQoNClN0YXJ0IGJ5IGNyZWF0aW5nIHZlY3RvcnMgdG8gc3RvcmUgdGhlIG51bWJlciBvZiByZXN1bHRzIG9mIGVhY2ggaW5kaXZpZHVhbCBtZXRob2Qgb2YgdmljdG9yeSBvbiBlYWNoIHNpbXVsYXRlZCBjYXJkLg0KYGBge3IsIGVjaG89VFJVRX0NCmRlY19zaW09YygpDQpzdWJfc2ltPWMoKQ0Ka29fc2ltID1jKCkNCnRrb19zaW0gPSBjKCkNCmBgYA0KDQpTZXR1cCB0aGUgc2ltdWxhdGlvbiBwYXJhbWV0ZXJzLCBpbmNsdWRpbmcgdGhlIG51bWJlciBvZiBpdGVyYXRpb25zLCB0aGUgc2VlZCwgYW5kIHRoZSBudW1iZXIgb2YgZmlnaHRzIG9uIGVhY2ggc2ltdWxhdGVkIGNhcmQgKFVGQyAyMDggaGFkIDEwIGZpZ2h0cyBvbiBpdCwgc28gd2UnbGwgdXNlIDEwKS4NCg0KKE5vdGU6IE15IGNvbXB1dGVyIHNlZW1zIHRvIGNodXJuIGFib3V0IDEwMDAgaXRlcmF0aW9ucyBvciBzbyBwZXIgbWludXRlLiBJZiB5b3UncmUgaW1wYXRpZW50LCB0aGVuIG1heWJlIGNvbnNpZGVyIHJlZHVjaW5nIDIwLDAwMCBpdGVyYXRpb25zIGRvd24gdG8gYSBjb3VwbGUgdGhvdXNhbmQuIFRoZSBtb3JlIHRyaWFscywgdGhlIG1vcmUgY2VudHJhbCB0aGUgcmVzdWx0cyB0aG91Z2guKQ0KYGBge3J9DQppdGVyYXRpb25zPTIwMDAwDQpzZXQuc2VlZCgxKQ0Kbl9maWdodHMgPSAxMA0KYGBgDQoNCkNyZWF0ZSBhIG1vbnRlIGNhcmxvIHNpbXVsYXRpb24sIHdoaWNoIHJhbmRvbWx5IHNlbGVjdHMgMTAgZmlnaHRzIGluIHRoZSBkYXRhLCBhbmQgdGhlbiBhcHBlbmRzIHRoZSBudW1iZXIgb2YgZGlmZmVyZW50IG1ldGhvZHMgb2YgZmluaXNoZXMgdG8gdGhlIHZlY3RvcnMgd2UgY3JlYXRlZC4NCmBgYHtyfQ0KZm9yIChpIGluIDE6aXRlcmF0aW9ucyl7DQogIA0KICAjIGNyZWF0ZSBzYW1wbGUgbi1maWdodCBjYXJkICANCiAgc2FtcGxlID0gc2FtcGxlKGZpZ2h0c19tZXRob2Rfc3Vic2V0JG1ldGhvZCwgbl9maWdodHMsIHJlcGxhY2U9VCkNCiAgDQogICMgYXNzaWduIG5vLiBvZiBmaW5pc2hlcyBvZiBlYWNoIHR5cGUgdG8gYSB2ZWN0b3IgIA0KICBmb3IobWV0aG9kIGluIG1ldGhvZHMpeyAgDQogICAgDQogICAgI0xvb3AgdGhyb3VnaCB0aGUgNCBtZXRob2RzLCBnZXQgdGhlIHN1bSBvZiB0aGUgbnVtYmVyIG9mIGZpbmlzaGVzIG9uIHRoZSBjYXJkLCBhbmQgYXNzaWduIGl0IHRvIHRoZSBhcHByb3ByaWF0ZSB2ZWN0b3IuDQogICAgeCA9IHN1bShzYW1wbGUgPT0gbWV0aG9kKQ0KICAgIA0KICAgIGlmKG1ldGhvZCA9PSBtZXRob2RzWzJdKXsNCiAgICAgIHRrb19zaW1baV0gPSB4DQogICAgfSBlbHNlIGlmKG1ldGhvZCA9PSBtZXRob2RzWzFdKXsNCiAgICAgIHN1Yl9zaW1baV0gPSB4DQogICAgfSBlbHNlIGlmKG1ldGhvZCA9PSBtZXRob2RzWzNdKXsNCiAgICAgIGtvX3NpbVtpXSA9IHgNCiAgICB9IGVsc2V7DQogICAgICBkZWNfc2ltW2ldID0geA0KICAgIH0NCiAgICANCiAgfQ0KfQ0KYGBgDQoNCk1vdmUgYWxsIG9mIHRoZSBzaW11bGF0aW9uIHJlc3VsdHMgaW50byBhIGRhdGEgZnJhbWUsIHNpbV9kZi4gVGhlbiwgcnVuIHRoZSBzdW1tYXJ5IHN0YXRzLiBUaGUgYXZlcmFnZSAxMC1maWdodCBjYXJkIGhhcyBhYm91dCA0LjUgZGVjaXNpb25zIGFuZCA1LjUgZmluaXNoZXMuDQpgYGB7cn0NCnNpbV9kZiA9IGRhdGEuZnJhbWUoZGVjID0gZGVjX3NpbSwgdGtvID0gdGtvX3NpbSwga28gPSBrb19zaW0sIHN1YiA9IHN1Yl9zaW0pDQoNCnN1bW1hcnkoc2ltX2RmKQ0KYGBgDQoNClRoZSA0IGZpbmlzaGVzIGFyZSByZWFsbHkganVzdCBvbmUgdmFyaWFibGUsIGJ1dCB0aGV5IGFyZSBkaXNwZXJzZWQgYW1vbmcgNCBjb2x1bW5zLiBTbyB0aGlzIGlzIGEgd2lkZS1kYXRhIGZyYW1lIHRoYXQgbmVlZHMgdG8gYmUgY29udmVydGVkIHRvIGxvbmcgZm9yIGdyYXBoaWNhbCBwdXJwb3Nlcywgd2hpY2ggY2FuIGJlIGRvbmUgZWFzaWx5IHVzaW5nIFRpZHlyJ3MgZ2F0aGVyIGZ1bmN0aW9uLg0KYGBge3J9DQpzaW1fd2lkZSA9IGdhdGhlcihzaW1fZGYsIG1ldGhvZCwgbikNCmhlYWQoc2ltX3dpZGUpDQpgYGANCg0KVmlldyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggdHlwZSBvZiBmaW5pc2ggaW4gdGhlIHNpbXVsYXRpb24uIExvb2tzIGxpa2UgdGhlIGF2ZXJhZ2Ugc2hvdyB0eXBpY2FsbHkgY2x1c3RlcnMgYXJvdW5kIDMtNiBkZWNpc2lvbnMsIDAtMiBrbm9ja291dHMsIGFuZCAxLTMgc3VibWlzc2lvbnMgYW5kIFRLT3MuDQpgYGB7cn0NCmdncGxvdChzaW1fd2lkZSwgYWVzKHg9biwgZmlsbD1hcy5mYWN0b3IobikpICkgKyANCiAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSksIHNob3cubGVnZW5kID0gRikgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPTA6MTApICsgDQogIGZhY2V0X3dyYXAofm1ldGhvZCwgbmNvbCA9IDEpDQoNCmBgYA0KDQpOb3cgbGV0J3MgbG9vayBhdCBpdCBhcyBhIGNyb3NzLXRhYmxlLg0KYGBge3J9DQpzaW1fdGFibGUgPSB0YWJsZShzaW1fd2lkZSRtZXRob2QsIHNpbV93aWRlJG4pDQpzaW1fdGFibGUNCmBgYA0KDQpTbyB0aGUgcHJvYmFiaWxpdHkgb2YgYSAxMC1maWdodCBVRkMgY2FyZCBoYXZpbmcgOSBkZWNpc2lvbnMgaXMgYXBwcm94aW1hdGVseSA3OS8yMDAwMCAoYW5kIDkwLzIwMDAwIGlmIHlvdSBpbmNsdWRlIHRoZSAxMSB0aW1lcyBhbGwgMTAgZmlnaHRzIHdlbnQgdG8gZGVjaXNpb24pLiBUbyBwdXQgdGhpcyBpbiBkZWNpbWFsIGZvcm0gY29tZXMgb3V0IHRvLi4uDQpgYGB7cn0NCiNkZWNpbWFsIHByb2JhYmlsaXR5DQpzdW0oc2ltX3RhYmxlWyJkZWMiLCBjKCI5IiwgIjEwIildKSAvIGl0ZXJhdGlvbnMNCg0KI09uZSBvdXQgb2YgZXZlcnkgeCBldmVudHMNCml0ZXJhdGlvbnMgLyBzdW0oc2ltX3RhYmxlWyJkZWMiLCBjKCI5IiwgIjEwIildKSANCg0KYGBgDQoNClNvIFVGQyAyMDggcHJvYmFiaWxpc3RpY2x5IHdhcyBhIG9uZS1pbi0yMjIgc2hvdyBldmVudC4gRm9yIHJlZmVyZW5jZSwgVUZDIDIwOCB3YXMgdGhlIDM1OXRoIHNob3cgaW4gdGhlIFp1ZmZhIGVyYSBvZiBVRkMsIHdoaWNoIG1lYW5zIHRoYXQgc3RhdGlzdGljYWxseSBzcGVha2luZyBpdCBzaG91bGQgaGF2ZSBvbmx5IG9jY3VycmVkIGFwcHJveGltYXRlbHkgMS42IHRpbWVzIGR1cmluZyB0aGUgZW50aXJlIDE1LXllYXIgc2FtcGxlLiAgVG8gdmlzdWFsaXplIHRoYXQgb3V0bGllci4uLg0KDQpgYGB7cn0NCmdncGxvdChzdWJzZXQoc2ltX3dpZGUsIG1ldGhvZD09ImRlYyIpLCBhZXMoeD1uLCB5PSAuLmRlbnNpdHkuLikpICsgDQogIGdlb21fZnJlcXBvbHkoYmlud2lkdGg9MSwgbHdkPTIpICsgDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9MDoxMCkgKyANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTksIGNvbD0icmVkIiwgbHdkPTEuNSkgKyANCiAgZ2VvbV90ZXh0KGFlcyh4PTksIHk9LjIsIGxhYmVsPSBwYXN0ZSgiVUZDIDIwOCBcbiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShzaW1fdGFibGVbImRlYyIsIGMoIjkiLCAiMTAiKV0pL2l0ZXJhdGlvbnMgKSksIA0KICAgICAgICAgICAgaGp1c3Q9LS4yNSwgIGNvbG9yPSJyZWQiLCBzaXplPTYpKw0KICBnZ3RpdGxlKHBhc3RlKCJQcm9iYWJpbGl0eSBvZiB4IERlY2lzaW9ucyBvbiBhIiwgbl9maWdodHMsICAiRmlnaHQgVUZDIENhcmQiKSkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIikpICsNCiAgeGxhYihwYXN0ZSgiRGVjaXNpb25zIChvdXQgb2YgIiwgbl9maWdodHMsICIpIiwgc2VwPSIiKSkNCg0KYGBgDQoNClRob3NlIHJlc3VsdHMgd2VyZSBpbiB0aGUgc2ltdWxhdGVkIHdvcmxkIHdlIGNyZWF0ZWQuIEhvdyBkb2VzIDkvMTAgZGVjaXNpb25zIGZhcmUgaW4gdGhlIGFjdHVhbCBjb3Vyc2Ugb2YgVUZDIGhpc3Rvcnk/IFdlIG5lZWQgdG8gZ2V0IHRoZSBudW1iZXIgb2YgZmluaXNoZXMgb24gZWFjaCBhY3R1YWwgc2hvdyB0byBmaW5kIG91dC4NCg0KYGBge3J9DQojR2V0IGEgbGlzdCBvZiBhbGwgZXZlbnQgbmFtZXMgc2FtcGxlZA0KZXZlbnRfbmFtZXMgPSB1bmlxdWUoZmlnaHRzX21ldGhvZF9zdWJzZXQkZXZlbnRfbmFtZSkNCg0KI0NyZWF0ZSBhIG5ldyBERiB0aGF0IHNpbXBseSBoYXMgZWFjaCBzaG93IGluIFVGQyBoaXN0b3J5LCBhbmQgdGhlbiBhIGNvdW50IG9mIGFsbCB0aGUgZGlmZmVyZW50IGZpbmlzaGVzIG9uIHRoYXQgc2hvdyAod2UnbGwgcG9wdWxhdGUgdGhvc2UgY291bnRzIGluIHRoZSBuZXh0IGxvb3ApDQpldmVudHMgPSBkYXRhLmZyYW1lKGV2ZW50ID0gZXZlbnRfbmFtZXMsIHN1Ym1pc3Npb25zID0gMCwgZGVjaXNpb25zID0gMCwga28gPSAwLCB0a28gPSAwLCBvdGhlciA9IDApDQpoZWFkKGV2ZW50cykNCmBgYA0KDQpMb29wIHRocm91Z2ggdGhlIG9yaWdpbmFsIGZpZ2h0cyBERiAodGhlIHN1YnNldCB3ZSBjcmVhdGVkIGdvdCByaWQgb2YgYWxsIHRoZSAib3RoZXIiIGZpbmlzaGVzLCB3aGljaCB3ZSBuZWVkIHRvIGNvdW50IGFnYWluKS4gSW4gdGhlIGxvb3AsIHdlJ2xsIGtlZXAgYSBydW5uaW5nIGNvdW50IG9mIHRoZSBudW1iZXIgb2YgZGlmZmVyZW50IGZpbmlzaGVzIG9uIGVhY2ggc2hvdy4NCg0KKHRoZSBsb29wIG9ubHkgdGFrZXMgYWJvdXQgMzAgc2Vjb25kcykNCg0KYGBge3J9DQojbG9vcCB0aHJvdWdoIGVhY2ggZXZlbnQgaW4gdGhlIHNhbXBsZSB0byBjb3VudCBudW1iZXIgb2YgZmluaXNoZXMgcGVyIGNhcmQNCmZvcihldiBpbiBldmVudF9uYW1lcyl7DQogIA0KICAjU2luY2Ugd2UgdGhyZXcgb3V0IGFsbCB0aGUgIm90aGVyIiBmaW5pc2hlcyBpbiB0aGUgaW5pdGlhbCBzdWJzZXQsIHdlJ2xsIHVzZSB0aGUgb3JpZ2luYWwgZmlnaHRzIERGIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgZGlmZmVyZW50IHJlc3VsdHMNCiAgZm9yKHJvdyBpbiAxOm5yb3coZmlnaHRzKSl7DQogICAgDQogICAgI2xvb2sgZm9yIHRoZSBwcm9wZXIgcm93IHRoYXQgY29ycmVzcG9uZHMgdG8gdGhlIGxvb3BpbmcgZXZlbnQNCiAgICBpZihmaWdodHMkZXZlbnRfbmFtZVtyb3ddID09IGV2KXsNCiAgICAgIA0KICAgICAgI2lmIHRoZSBmaWdodCBlbmRlZCBieSBzdWJtaXNzaW9uLCB0aGVuIGFkZCAxIHRvIHRoZSBuZXcgZXZlbnRzIERGIHdlIGNyZWF0ZWQgdW5kZXIgdGhlIHN1Ym0uIGNvbHVtbg0KICAgICAgaWYoZmlnaHRzJG1ldGhvZFtyb3ddID09ICJTdWJtaXNzaW9uIil7DQogICAgICAgIGV2ZW50c1tldmVudHNbLDFdID09IGV2LCAyXSA9IGV2ZW50c1tldmVudHNbLDFdID09IGV2LCAyXSArMQ0KICAgICAgICANCiAgICAgIH0gZWxzZSBpZihmaWdodHMkbWV0aG9kW3Jvd10gPT0gIkRlY2lzaW9uIil7DQogICAgICAgIGV2ZW50c1tldmVudHNbLDFdID09IGV2LCAzXSA9IGV2ZW50c1tldmVudHNbLDFdID09IGV2LCAzXSArMQ0KICAgICAgICANCiAgICAgIH0gZWxzZSBpZihmaWdodHMkbWV0aG9kW3Jvd10gPT0gIktPIil7DQogICAgICAgIGV2ZW50c1tldmVudHNbLDFdID09IGV2LCA0XSA9IGV2ZW50c1tldmVudHNbLDFdID09IGV2LCA0XSArMQ0KICAgICAgICANCiAgICAgIH0gZWxzZSBpZihmaWdodHMkbWV0aG9kW3Jvd10gPT0gIlRLTyIpew0KICAgICAgICBldmVudHNbZXZlbnRzWywxXSA9PSBldiwgNV0gPSBldmVudHNbZXZlbnRzWywxXSA9PSBldiwgNV0gKzENCiAgICAgICAgDQogICAgICB9IGVsc2V7DQogICAgICAgIGV2ZW50c1tldmVudHNbLDFdID09IGV2LCA2XSA9IGV2ZW50c1tldmVudHNbLDFdID09IGV2LCA2XSArMQ0KICAgICAgfQ0KICAgIH0gDQogICAgDQogIH0NCn0NCg0KaGVhZChldmVudHMpDQpgYGANCg0KTm93IHdlIGNhbiBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgZWFjaCBmaW5pc2ggb24gZWFjaCBjYXJkLiBGaXJzdCwgd2UnbGwgY291bnQgdGhlIG51bWJlciBvZiBmaWdodHMgb24gZWFjaCBjYXJkLCBhbmQgdGhlbiB1c2UgdGhhdCB0byBnZXQgdGhlIGZpbmlzaCBwZXJjZW50YWdlcy4NCg0KYGBge3J9DQojRmV0Y2ggdGhlIHN1bSBvZiBlYWNoIHJvdyAocmVtb3ZlcyBvbmx5IHRoZSBmaXJzdCBjb2x1bW4sIHRoZSBldmVudCBuYW1lKQ0KZXZlbnRzJG5fZmlnaHRzID0gcm93U3VtcyhldmVudHNbLTFdKQ0KDQoNCiNDYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2VzIG9mIGZpbmlzaGVzIG9uIGVhY2ggY2FyZA0KZXZlbnRzID0gZXZlbnRzICU+JQ0KICBtdXRhdGUoc3ViX3BjdCA9IHN1Ym1pc3Npb25zL25fZmlnaHRzLA0KICAgICAgICAgZGVjX3BjdCA9IGRlY2lzaW9ucy9uX2ZpZ2h0cywNCiAgICAgICAgIGtvX3BjdCA9IGtvL25fZmlnaHRzLA0KICAgICAgICAgdGtvX3BjdCA9IHRrby9uX2ZpZ2h0cw0KICApICU+JQ0KICBhcnJhbmdlKGRlc2MoZGVjX3BjdCkpDQoNCmBgYA0KDQpQbG90IHRoZSB0b3AgMTAgY2FyZHMgaW4gVUZDIGhpc3Rvcnkgd2l0aCB0aGUgaGlnaGVzdCBwZXJjZW50YWdlcyBvZiBkZWNpc2lvbnMuIE5vdGU6IFIgbWFrZXMgeW91IHJlZmFjdG9yIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgaW4gb3JkZXIgdG8gcGxvdCBvbiBpdCBpbiBhIGRpZmZlcmVudCBvcmRlciBvdGhlciB0aGFuIHRoZSBkZWZhdWx0LCBzbyBJIGNyZWF0ZWQgYSBmdW5jdGlvbiB0aGF0IGRvZXMgdGhhdC4NCg0KYGBge3J9DQojRnVuY3Rpb24gdG8gcmVmYWN0b3IgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSBieSB0aGUgb3JkZXIgb2YgYSBudW1lcmljIHZhcmlhYmxlDQpyZWZhY3RvciA9IGZ1bmN0aW9uKGRmLCBjb2xfZmFjdG9yLCBjb2xfc29ydCl7DQogIGRmWywgY29sX2ZhY3Rvcl0gPSBmYWN0b3IoZGZbLCBjb2xfZmFjdG9yXSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gZGZbLGNvbF9mYWN0b3JdW29yZGVyKC1kZlssIGNvbF9zb3J0XSldKSAgIA0KICANCiAgcmV0dXJuKGRmKQ0KfQ0KDQojcmVmYWN0b3IgdGhlIGV2ZW50IGxpc3QgYnkgaGlnaGVzdCBkZWNpc2lvbiBwZXJjZW50YWdlDQpldmVudHMgPSByZWZhY3RvcihldmVudHMsICJldmVudCIsICJkZWNfcGN0IikgJT4lIGFycmFuZ2UoZGVzYyhkZWNfcGN0KSkNCg0KDQpnZ3Bsb3QoZXZlbnRzWzE6MTAsXSwgYWVzKHg9ZXZlbnQsIHk9ZGVjX3BjdCkpICsgDQogIGdlb21fY29sKCkgKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZT0iaXRhbGljIiwgY29sb3I9IiM5OTMzMzMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT04LCBhbmdsZT05MCkpICsNCiAgY29vcmRfZmxpcChleHBhbmQgPSBUKSArDQogIGdndGl0bGUoIlVGQyBTaG93cyB3aXRoIEhpZ2hlc3QgUGVyY2VudGFnZSBvZiBEZWNpc2lvbnMiKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwuOSwuMSkpDQoNCg0KYGBgDQoNCk9ubHkgMyBvZiB0aGUgMzQ2IG90aGVyIHNob3dzIGFuYWx5emVkIGhhZCBldmVyIGNsZWFyZWQgODAlIGRlY2lzaW9ucywgd2l0aCB0aGUgODMuMyUgb2YgVUZDIDE2OSBhbmQgVUZDIEZOIDM2IGJlaW5nIHRoZSBoaWdoZXN0LiBUaGF0IHJlYWxseSBwdXRzIHRoZSA5MCUgZGVjaXNpb25zIG9mIFVGQyAyMDggaW50byBwZXJzcGVjdGl2ZSENCg0KSGVyZSBpcyB0aGUgbGlzdCBvZiBhbGwgc2hvd3Mgd2l0aCBhdCBsZWFzdCA3MCUgZGVjaXNpb25zLiBPbmx5IDE2IHNob3dzIGhhdmUgZXZlbiBkb25lIHRoYXQgKHdoaWNoIGlzIG9ubHkgNC42JSBvZiBhbGwgc2hvd3MpIQ0KYGBge3J9DQpldmVudHMgJT4lIGZpbHRlcihkZWNfcGN0ID49IC43KSAlPiUNCiAgc2VsZWN0KCJldmVudCIsICJkZWNfcGN0IiwgImRlY2lzaW9ucyIsICJuX2ZpZ2h0cyIpICU+JQ0KICBhcnJhbmdlKGRlc2MoZGVjX3BjdCksIGRlc2MoZGVjaXNpb25zKSkNCmBgYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIGRldm90ZWQgYWxsIHRoaXMgdGltZSB0byBhbmFseXppbmcgdGhlIGFub21hbHkgdGhhdCB3YXMgVUZDIDIwOCwgb25lIG9mIHRoZSBtb3N0IGJvcmluZyBVRkMgY2FyZHMgb2YgYWxsIHRpbWUsIGxldCdzIGZpbmlzaCBvbiBhIGhpZ2ggbm90ZSBieSBjcmVhdGluZyBhIHdhdGNoIGxpc3QgZm9yIFVGQyBGaWdodCBQYXNzIG9mIFVGQyBzaG93cyB3aXRoIHRoZSBoaWdoZXN0IGtub2Nrb3V0IHBlcmNlbnRhZ2UhDQpgYGB7cn0NCiNyZWZhY3RvciB0aGUgZXZlbnQgbGlzdCBieSBoaWdoZXN0IGRlY2lzaW9uIHBlcmNlbnRhZ2UNCmV2ZW50cyA9IHJlZmFjdG9yKGV2ZW50cywgImV2ZW50IiwgImtvX3BjdCIpICU+JSBhcnJhbmdlKGRlc2Moa29fcGN0KSkNCg0KDQpnZ3Bsb3QoZXZlbnRzWzE6MTAsXSwgYWVzKHg9ZXZlbnQsIHk9a29fcGN0KSkgKyANCiAgZ2VvbV9jb2woKSArIA0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChmYWNlPSJpdGFsaWMiLCBjb2xvcj0iIzk5MzMzMyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT04LCBhbmdsZT05MCkpICsNCiAgY29vcmRfZmxpcChleHBhbmQgPSBUKSArDQogIGdndGl0bGUoIlVGQyBTaG93cyB3aXRoIEhpZ2hlc3QgUGVyY2VudGFnZSBvZiBLbm9ja291dHMiKSArDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwuNSwuMDUpKQ0KICANCmBgYA0KDQoNCg==