UPDATE (March 25, 2017)

The project outline below was origially drafted in September, 2016. Much has changed since then. Mabye I’ll get around to updating the description at some point. Here are a few of the changes:
- the model
- new features
- feature reduction of highly correlated features
- stepwise feature selection according to AIC
- set of scripts to streamline model testing and evaluation
- monte carlo simulations to explore most profitable betting strategies (developed by a friend and my brother)

Real World Results

Below are real world predictions made prior to each event. You’ll notice the performance between the model and Vegas is pretty similar below. A few things to remember about the model and the results listed below:
- the model doesn’t make prediction where one of the fighters is making a UFC debut
- these are all model predictions at the minimum confidence threshold of 50%
- in a betting situation, these predictions are filtered to a higher confidence threshold, as well as by other means.

Date Bets Moneyline Probability Model.Win Vegas.Win Thresh
2017-03-18 makwan amirkhani -120 0.6285 0 0 60
2017-03-18 brad pickett -138 0.5749 0 0 55
2017-03-18 scott askham -142 0.5857 0 0 55
2017-03-18 daniel omielanczuk 153 0.6032 0 1 60
2017-03-18 darren stewart -176 0.8005 0 0 75
2017-03-18 gunnar nelson -334 0.6366 1 1 60
2017-03-18 corey anderson 125 0.5971 0 1 55
2017-03-18 joe duffy -591 0.7547 1 1 75
2017-03-18 vicente luque -128 0.5163 0 0 50
2017-03-18 marc diakiese -202 0.6297 1 1 60
2017-03-11 beneil dariush 135 0.5341 0 1 50
2017-03-11 francisco trinaldo 160 0.5582 0 1 55
2017-03-11 ray borg -124 0.5128 1 1 50
2017-03-11 mauricio rua -150 0.7802 1 1 75
2017-03-11 michel prazeres -241 0.7483 1 1 70
2017-03-11 rani yahya -211 0.7123 0 0 70
2017-03-11 rony jason 105 0.5065 0 1 50
2017-03-11 tim means -181 0.6000 0 0 60
2017-03-11 kelvin gastelum -340 0.7107 1 1 70
2017-03-04 alistair overeem -131 0.7104 1 1 70
2017-03-04 mirsad bektic -675 0.6382 0 0 60
2017-03-04 luke sanders -135 0.6983 0 0 65
2017-03-04 david teymur 305 0.5320 1 0 50
2017-03-04 marcin tybura -156 0.5285 1 1 50
2017-03-04 paul craig 115 0.5811 0 1 55
2017-03-04 rashad evans -220 0.5955 0 0 55
2017-03-04 stephen thompson -134 0.6237 0 0 60
2017-02-19 elias theodorou 106 0.5708 1 0 55
2017-02-19 gerald meerschaert -279 0.6066 1 1 60
2017-02-19 johny hendricks 120 0.5623 1 0 55
2017-02-19 paul felder -346 0.7766 1 1 75
2017-02-19 santiago ponzinibbio -330 0.6707 1 1 65
2017-02-19 thiago santos -182 0.5703 1 1 55
2017-02-19 travis browne 101 0.5026 0 1 50
2017-02-11 randy brown -143 0.7754 0 0 75
2017-02-11 derek brunson -120 0.6034 0 0 60
2017-02-11 glover teixeira -199 0.5439 1 1 50
2017-02-11 jacare souza -555 0.8839 1 1 75
2017-02-11 dustin poirier -431 0.6404 1 1 60
2017-02-11 islam makhachev -327 0.5128 1 1 50
2017-02-11 rick glenn -176 0.5366 1 1 50
2017-02-11 ryan laflare -296 0.6748 1 1 65
2017-02-11 wilson reis -591 0.8891 1 1 75
2017-02-04 abel trujillo -110 0.5531 0 0 55
2017-02-04 curtis blaydes -300 0.5549 1 1 55
2017-02-04 alex morono -105 0.5609 0 0 55
2017-02-04 chris gruetzemacher 230 0.5427 0 1 50
2017-02-04 khalil rountree jr -165 0.6463 1 1 60
2017-02-04 dennis bermudez -200 0.7212 0 0 70
2017-01-28 raphael assuncao -155 0.5353 1 1 50
2017-01-28 francis ngannou -375 0.7663 1 1 75
2017-01-28 donald cerrone -155 0.7404 0 0 70
2017-01-28 alessio di chirico -110 0.7764 0 0 75
2017-01-28 jc cottrell -145 0.5350 0 0 50
2017-01-28 jason knight -150 0.6837 1 1 65
2017-01-28 sam alvey -155 0.5182 1 1 50
2017-01-15 aleksei oleinik -145 0.5814 1 1 55
2017-01-15 court mcgee -140 0.5932 0 0 55
2017-01-15 joachim christensen -225 0.6326 1 1 60
2017-01-15 dmitrii smoliakov -110 0.6602 0 0 65
2017-01-15 frankie saenz -165 0.5557 0 0 55
2017-01-15 marcin held 100 0.5154 0 1 50
2017-01-15 sergio pettis -150 0.7080 1 1 70
2017-01-15 tony martin -175 0.5132 1 1 50
2017-01-15 walt harris -145 0.6143 1 1 60
2017-01-15 yair rodriguez -445 0.7820 1 1 75
2016-12-30 antonio carlos junior -140 0.5263 1 1 50
2016-12-30 cody garbrandt 160 0.5184 1 0 50
2016-12-30 tarec saffiedine 120 0.5112 0 1 50
2016-12-30 neil magny -165 0.5515 1 1 55
2016-12-30 louis smolka 115 0.6123 0 1 60
2016-12-30 alex garcia -190 0.5155 1 1 50
2016-12-30 tj dillashaw -205 0.5100 1 1 50
2016-12-17 alex morono -115 0.6271 1 1 60
2016-12-17 bojan velickovic -155 0.7095 0 0 70
2016-12-17 colby covington -360 0.5998 1 1 55
2016-12-17 cole miller -110 0.5299 0 0 50
2016-12-17 eddie wineland -225 0.5080 1 1 50
2016-12-17 hector sandoval -130 0.6126 1 1 60
2016-12-17 scott holtzman 150 0.5767 0 1 55
2016-12-17 mike perry -135 0.5634 0 0 55
2016-12-17 mickey gall -120 0.5004 1 1 50
2016-12-17 urijah faber -450 0.6846 1 1 65
2016-12-10 max holloway -195 0.5800 1 1 55
2016-12-10 cub swanson 188 0.5041 1 0 50
2016-12-10 donald cerrone -275 0.7071 1 1 70
2016-12-10 lando vannata -170 0.5478 1 1 50
2016-12-10 kelvin gastelum 110 0.7367 1 0 70
2016-12-10 mitch gagnon -156 0.5630 0 0 55
2016-12-10 nikita krylov 100 0.6733 0 1 65
2016-12-10 olivier aubin mercier -165 0.5002 1 1 50
2016-12-10 rustam khabilov -210 0.7076 1 1 70
2016-12-10 zach makovsky -160 0.5500 0 0 55
2016-12-09 andrew sanchez -205 0.7311 1 1 70
2016-12-09 francis ngannou -550 0.5372 1 1 50
2016-12-09 corey anderson -400 0.7424 1 1 70
2016-12-09 derrick lewis -205 0.7983 1 1 75
2016-12-09 marc diakiese -325 0.6540 1 1 65
2016-12-03 anthony smith -115 0.6544 1 1 65
2016-12-03 brandon moreno -125 0.7252 1 1 70
2016-12-03 brendan oreilly 100 0.5464 0 1 50
2016-12-03 ryan hall -110 0.5732 1 1 55
2016-12-03 ion cutelaba -215 0.6528 0 0 65
2016-12-03 jorge masvidal -250 0.5396 1 1 50
2016-12-03 joseph benavidez -200 0.5385 1 1 50
2016-12-03 devin clark -105 0.5631 1 1 55
2016-11-26 jake matthews -300 0.7287 0 0 70
2016-11-26 omari akhmedov -180 0.5074 1 1 50
2016-11-26 damien brown 115 0.5549 1 0 55
2016-11-26 richard walsh 112 0.6033 0 1 60
2016-11-26 ben nguyen 110 0.5989 1 0 55
2016-11-26 marlon vera 125 0.5636 1 0 55
2016-11-26 derek brunson -135 0.5036 0 0 50
2016-11-26 chris camozzi -245 0.7258 0 0 70
2016-11-26 jason knight 131 0.6187 1 0 60
2016-11-19 gegard mousasi -550 0.7373 1 1 70
2016-11-19 stevie ray -105 0.5643 1 1 55
2016-11-19 teruto ishihara -275 0.6556 0 0 65
2016-11-19 kyoji horiguchi -199 0.7209 1 1 70
2016-11-19 kevin lee -102 0.7378 1 1 70
2016-11-19 zak cummings -170 0.5942 1 1 55
2016-11-19 ryan bader -370 0.7399 1 1 70
2016-11-19 albert morales 235 0.5279 0 1 50
2016-11-19 krzysztof jotko 140 0.5699 1 0 55
2016-11-19 zak ottow 142 0.5038 0 1 50
2016-11-19 manvel gamburyan 140 0.6020 0 1 60
2016-11-19 luis henrique -275 0.6747 1 1 65
2016-11-19 kamaru usman -205 0.7164 1 1 70
2016-11-19 jack hermansson -210 0.5305 0 0 50
2016-11-19 justin scoggins -168 0.7722 0 0 75
2016-11-12 conor mcgregor -137 0.5500 1 1 55
2016-11-12 stephen thompson -191 0.5142 0 0 50
2016-11-12 chris weidman -190 0.7122 0 0 70
2016-11-12 frankie edgar -340 0.5929 1 1 55
2016-11-12 rafael natal -165 0.6559 0 0 65
2016-11-12 vicente luque -105 0.6086 1 1 60
2016-11-12 jim miller 130 0.7162 1 0 70
2016-11-12 khabib nurmagomedov -305 0.6494 1 1 60
2016-11-05 rafael dos anjos -145 0.5486 0 0 50
2016-11-05 charles oliveira -105 0.5347 0 0 50
2016-11-05 beneil dariush -115 0.6033 1 1 60
2016-11-05 marco beltran -125 0.5642 0 0 55
2016-11-05 erick montano -105 0.6659 0 0 65
2016-11-05 douglas silva de andrade -125 0.5554 1 1 55
2016-11-05 alex nicholson 175 0.5269 0 1 50
2016-11-05 enrique barzola -549 0.6046 1 1 60
2016-11-05 felipe arantes 180 0.5206 0 1 50
2016-10-08 Michael Bisping -225 0.8037 1 1 75
2016-10-08 Gegard Mousasi -300 0.9063 1 1 75
2016-10-08 Ovince Saint Preux -155 0.7126 0 0 70
2016-10-08 Stefan Struve -185 0.7629 1 1 75
2016-10-08 Mirsad Bektic -700 0.5751 1 1 55
2016-10-08 Iuri Alcantara -165 0.7111 1 1 70
2016-10-08 Damian Stasiak 145 0.6871 1 0 65
2016-10-08 Albert Tumenov -260 0.5388 0 0 50
2016-10-08 Mike Perry 120 0.6247 1 0 60
2016-10-08 Leonardo Santos 175 0.5480 1 0 50
2016-10-01 John Lineker 105 0.7123 1 0 70
2016-10-01 Will Brooks -270 0.5858 0 0 55
2016-10-01 Andre Fili 150 0.5666 1 0 55
2016-10-01 Shamil Abdurakhimov -120 0.5583 1 1 55
2016-10-01 Elizeu Zaleski dos Santos 130 0.5261 1 0 50
2016-10-01 Nate Marquardt 180 0.5480 1 0 50
2016-10-01 Ion Cutelaba -165 0.6151 1 1 60
2016-10-01 Curtis Blaydes -210 0.5855 1 1 55
2016-09-24 Renan Barao -500 0.7085 1 1 70
2016-09-24 Antonio Silva 350 0.5854 0 1 55
2016-09-24 Fancisco Trinaldo -130 0.7309 1 1 70
2016-09-24 Thiago Santos -620 0.6075 0 0 60
2016-09-24 Mike de la Torre 115 0.5159 0 1 50
2016-09-24 Gilbert Burns -175 0.5683 0 0 55
2016-09-24 Rani Yahya -140 0.6103 1 1 60
2016-09-24 Jussier Formiga -200 0.5980 1 1 55
2016-09-24 Stevie Ray -160 0.6967 0 0 65
2016-09-24 Vicente Luque -500 0.6840 1 1 65
2016-09-17 Dustin Poirier -170 0.6995 0 0 65
2016-09-17 Derek Brunson -190 0.5975 1 1 55
2016-09-17 Roan Carneiro 105 0.5722 1 0 55
2016-09-17 Islam Makhachev -135 0.5811 1 1 55
2016-09-17 Chas Skelly -175 0.5081 1 1 50
2016-09-17 Gabriel Benitez 105 0.5951 1 0 55
2016-09-17 Belal Muhammad -450 0.5708 1 1 55
2016-09-17 Antonio Carlos Junior -255 0.6906 1 1 65
2016-09-17 Jose Quinonez 140 0.5547 1 0 55
2016-09-17 Randy Brown -450 0.6144 1 1 60
2016-09-10 Jimmie Rivera -170 0.5714 1 1 55
2016-09-10 Stipe Miocic -110 0.6259 1 1 60
2016-09-10 Fabricio Werdum -225 0.8458 1 1 75
2016-09-10 Brad Tavares -200 0.6333 1 1 60
2016-09-10 Sean Spencer 105 0.5688 0 1 55
2016-09-03 Andrei Arlovski 120 0.6398 0 1 60
2016-09-03 Alexander Gustaffson -550 0.7330 1 1 70
2016-09-03 Ryan Bader -170 0.7937 1 1 75
2016-09-03 Nick Hein -340 0.7009 1 1 70
2016-09-03 Nick Dalby -190 0.5350 0 0 50
2016-09-03 Taylor Lapilus -139 0.7598 1 1 75
2016-09-03 Rustam Khabilov -560 0.7424 1 1 70

## 
## Attaching package: 'reshape2'
## The following object is masked from 'package:tidyr':
## 
##     smiths

***

Introduction

Ultimate Fighting Championship (UFC) is the world’s premier professional mixed martial arts organization. Fighters engage in one-on-one matches consisting of 3-5 rounds of 5 minutes each. Fighters are allowed to strike with punches, kicks, knees, elbows, or employ submission moves such as chokes or joint locks. Fighting occurs standing up, on the ground, or in a clinch position. Much like other professional sports, there are a variety of structured data gathered on individual fighters and on each fight match up. This project gathers much of that data, learns from it, and then makes predictions about future fight matchups. Specifically, this project seeks to answer the question: Given a specific mathcup, what is each fighter’s probability of winning?


Wrangling

The dataset ultimately developed by and for this project is not only an aggregation of data scraped from multiple sources, but is also comprised of newly generated data derived from the scraped data. Namely, the derivitive data refers to historic career statistics for each fighter at the time of each fight.


Aggregating The Data

This project intially gathered data from four sources:

  • UFC Unofficial API: list of current UFC fighters
  • Wikipedia: list of UFC events and event information
  • FightMetric: detailed fight-by-fight statistics and fighter statistics for every UFC fight
  • FightMatrix: proprietary current and historical fighter ranking system

However, in the end the only sources of data that were used in the final dataset were FightMetric and FightMatrix. The process of scraping data generally followed the pattern below using the Rvest package.

1. Collect and store relevant URLs in a list.

# Collect individual fighter urls.
for (letter in letters) {
  fightMetric_fighter_list_url <- paste("http://www.fightmetric.com/statistics/fighters?char=",
                                        letter, "&page=all", sep = "")
  
  fightMetric_fighter_list_page <- read_html(fightMetric_fighter_list_url)
  
  # Get inidividual FightMetric Fighter links for letter of alphabet from Fighter List page.
  fighter_links_per_letter <- fightMetric_fighter_list_page %>%
    html_nodes(".b-statistics__table-col:nth-child(1) .b-link_style_black") %>%
    html_attr("href")
  
  fightMetric_fighter_links <- c(fightMetric_fighter_links, fighter_links_per_letter)
}

2. Make copies of HTML from the URL list and store in a list.

#### Make copies of each FightMetric fighter page HTML to later scrape data we need. ####
fightMetric_fighter_html <- list()

for (i in 1:length(fightMetric_fighter_links)) {
  fightMetric_fighter_html[[i]] <- read_html(fightMetric_fighter_links[[i]])
}

3. Parse the HTML and put what we want in a dataframe.

#### Populate REVISED FIGHTER DATA ####
for (i in 1:length(fightMetric_fighter_html)) {
  
  # Get fighter name
  name <- fightMetric_fighter_html[[i]] %>%
    html_nodes(".b-content__title-highlight") %>%
    html_text(trim = TRUE)
  
  fightMetric_fighters_df[i, 1] <- name[[1]]
  
  # Get fighter pro record
  record <- fightMetric_fighter_html[[i]] %>%
    html_nodes(".b-content__title-record") %>%
    html_text(trim = TRUE) %>%
    strsplit(" ")
  
  record <- record[[1]][2] %>%
    strsplit("-")
  
  fightMetric_fighters_df[i, 2] <- record[[1]][1]
  
  fightMetric_fighters_df[i, 3] <- record[[1]][2]
  
  fightMetric_fighters_df[i, 4] <- record[[1]][3]
  
  # Get fighter stats
  stats <- fightMetric_fighter_html[[i]] %>%
    html_nodes(".b-list__box-list-item_type_block") %>%
    html_text(trim = TRUE) %>%
    gsub("^.*:(\\n *)*", "", .)
  
  stats <- stats[-10]
  
  for (s in 1:length(stats)) {
    if (s == "") {
      fightMetric_fighters_df[i, (4+s)] <- NA
    } else {
      fightMetric_fighters_df[i, (4+s)] <- stats[s]
    }
  }
}

This process was adapted and repeated to pull data for each of the three primary dataframes:

  • ufc_fight_details_df: fight-by-fight statistics
  • fightMetric_fighters_df: individual fighter statistics
  • figher_ranking_df: current and historical fighter rankings by weightclass

Cleaning The Data

Much of the data that was scraped had to be cleaned to some extent. At the very least values were converted from character to numeric, and in other cases fixes such as removing ‘%’ signs, converting a fighter’s height into number of inches, converting Dates of birth into machine readable Date format, etc… were necessary. Here are a few examples:

Removing ‘%’ sign.

# Remove % sign in several of the columns and convert to numeric.
for (i in c(11,13,15,16)) {
  fightMetric_fighters_df[,i] <- fightMetric_fighters_df[,i] %>%
    gsub("%", "", .) %>%
    as.numeric()
  
  fightMetric_fighters_df[,i] <- fightMetric_fighters_df[,i]/100
}

Converting height into number of inches

# Convert Height to numeric inches.
fightMetric_fighters_df$Height <- fightMetric_fighters_df$Height %>%
  gsub("'|\"", "", .) %>%
  strsplit(split = " ")

for (h in 1:length(fightMetric_fighters_df$Height)) {
  if (fightMetric_fighters_df[h, 5][[1]] == "--") {
    fightMetric_fighters_df[h, 5][[1]] <- NA
  } else {
    fightMetric_fighters_df[h, 5][[1]] <- (as.numeric(fightMetric_fighters_df[h, 5][[1]][1])*12) +
                                     as.numeric(fightMetric_fighters_df[h, 5][[1]][2])
  }
}

fightMetric_fighters_df$Height <- unlist(fightMetric_fighters_df$Height)

There were also instances of UFC fighters with the same name, which was particulary troublesome when merging this fighter data with other dataframes on the fighter’s name. It gave the appearance of a dataset or a merge that was entirely accurate, but for a few inexplicable errors. So after further digging into the datasets and crosschecking with the original webpages, the issue was handled by identifying these fighters by their name in addition to a physical attribute, and then renaming one of them. Here’s an example.

# Handle instances of same name in FIGHTMETRIC_FIGHTERS_DF
fightMetric_fighters_df[fightMetric_fighters_df$Name=="Dong Hyun Kim" & fightMetric_fighters_df$Reach==70, 1] <- "Dong Hyun Kim (Maestro)"
fightMetric_fighters_df[fightMetric_fighters_df$Name=="Michael McDonald" & fightMetric_fighters_df$Weight==205, 1] <- "Michael McDonald (The Black Sniper)"
fightMetric_fighters_df[fightMetric_fighters_df$Name=="Tony Johnson" & fightMetric_fighters_df$Weight==185, 1] <- "Tony Johnson (185lbs.)"

Shaping The Data

After all the data had been collected and stored in dataframes, there was some reshaping required in order to end up with a single dataframe suitable to subset from and present to a logistic regression or random forest function for modeling. At this point there were three dataframes - fight_details, fightMetric_fighters, and fighter_ranking - and the desired end state was to have a single fight_details that contained not only detailed fighter stats for each fight, but also each fighter’s attributes, and each fighter’s ranking. Furthermore, fight_details was currently in the form of each row representing one fighter’s stats for one fight, which meant there were two rows for each fight - one for each fighter’s stats. So, four things needed to happen…

1. Reshape fight_details to contain both fighter’s statistics for a single fight in a single row.

#### Reshape fight_details_df to accomodate logistical regression or random forest ####
colnames(ufc_fight_details_df)[2:3] <- c("Fighter1", "Fighter2")


# Grab even number rows from fight_details to later bind with odd number rows.
fighter2_df <- ufc_fight_details_df %>%
  slice(seq(2,nrow(ufc_fight_details_df),2))

# Rename columns in fighter2_df and original fight_details_df.
colnames(fighter2_df)[17:160] <- paste("F2", colnames(fighter2_df)[17:160], sep = "")
colnames(ufc_fight_details_df)[17:160] <- paste("F1", colnames(ufc_fight_details_df)[17:160], sep = "")

# Delete even number rows from fight_details.
ufc_fight_details_df <- ufc_fight_details_df[-seq(2,nrow(ufc_fight_details_df),2),]

# Combine columns from fight_details and fighter_2, so each row holds each fighter's data for
# a given fight.
ufc_fight_details_df <- bind_cols(ufc_fight_details_df, fighter2_df[17:160])

# Delete fighter_2 data frame.
rm(fighter2_df)

# Then, REMOVE rows with NA for FIGHTER.
na_row_index <- c()
for (n in 1:nrow(ufc_fight_details_df)) {
  if (is.na(ufc_fight_details_df[n,2]) | is.na(ufc_fight_details_df[n,3])) {
    na_row_index <- c(na_row_index, n)
  }
}

ufc_fight_details_df <- ufc_fight_details_df[-c(na_row_index),]
###################################

2. Include a new variable to indicate the winner - simply a 0 or 1 variable indicating if Fighter 1 wins or not.

# Mutate FIGHT_DETAILS to give logical form of result (Fighter1 or Fighter2) ready for logistic
# regression or random forest.
ufc_fight_details_df <- ufc_fight_details_df %>%
  mutate(F1Wins = as.character(Result) == as.character(Fighter1))

for (i in 1:nrow(ufc_fight_details_df)) {
  if (is.na(ufc_fight_details_df[i,305])) {
    ufc_fight_details_df[i,305] <- NA
  } else if (ufc_fight_details_df[i,305] == TRUE) {
    ufc_fight_details_df[i,305] <- 1
  } else {
    ufc_fight_details_df[i,305] <- 0
  }
}

3. Merge fightMetric_fighters with fight_details on each fighter’s name.

##### Merge FIGHTMETRIC_FIGHTERS_DF with FIGHT_DETAILS_DF ####

#### Merge FIGHTERS_DF to FIGHTER1 in FIGHT_DETAILS ####
fightMetric_fighters_df <- fightMetric_fighters_df %>%
  rename(Fighter1=Name)

ufc_fight_details_df <- ufc_fight_details_df %>%
  left_join(fightMetric_fighters_df, by = "Fighter1")

left_copy_fight_details <- ufc_fight_details_df[,1:306]
right_copy_fighter1_details <- ufc_fight_details_df[,307:323]

left_copy_double_rows <- as.numeric(rownames(left_copy_fight_details[duplicated(left_copy_fight_details),]))

left_copy_fight_details <- left_copy_fight_details[-c(left_copy_double_rows),]
right_copy_fighter1_details <- right_copy_fighter1_details[-c(left_copy_double_rows),]

ufc_fight_details_df <- left_copy_fight_details
########################

#### Merge FIGHTERS_DF to FIGHTER2 in FIGHT_DETAILS ####
fightMetric_fighters_df <- fightMetric_fighters_df %>%
  rename(Fighter2=Fighter1)

ufc_fight_details_df <- ufc_fight_details_df %>%
  left_join(fightMetric_fighters_df, by = "Fighter2")

left_copy_fight_details <- ufc_fight_details_df[,1:306]
right_copy_fighter2_details <- ufc_fight_details_df[,307:323]

left_copy_double_rows <- as.numeric(rownames(left_copy_fight_details[duplicated(left_copy_fight_details),]))

left_copy_fight_details <- left_copy_fight_details[-c(left_copy_double_rows),]
right_copy_fighter2_details <- right_copy_fighter2_details[-c(left_copy_double_rows),]

ufc_fight_details_df <- left_copy_fight_details %>%
  bind_cols(right_copy_fighter1_details) %>%
  bind_cols(right_copy_fighter2_details)

colnames(ufc_fight_details_df)[307:323] <- paste("F1", colnames(fightMetric_fighters_df)[2:18], sep = "")
colnames(ufc_fight_details_df)[324:340] <- paste("F2", colnames(fightMetric_fighters_df)[2:18], sep = "")

fightMetric_fighters_df <- fightMetric_fighters_df %>%
  rename(Name=Fighter2)
###################

4. Merge fighter_ranking with fight_details on each fighter’s name.

################### Assign FIGHTER_RANKING to fighters in NEW_FIGHTS #################

# Reset clean unique FightNumbers 
new_fights_df$FightNumber <- c(1:nrow(new_fights_df))

new_fights_df <- new_fights_df %>%
  mutate(F1Rank = 251, F2Rank = 251)

F1RankCol <- grep("F1Rank", colnames(new_fights_df))
F2RankCol <- grep("F2Rank", colnames(new_fights_df))

quarterly_date <- as.Date("2006-01-01", "%Y-%m-%d")
for (q in 1:(length(fighter_ranking_df)-9)) {
  temp_fighter_ranking <- fighter_ranking_df %>%
    select(c(q,353))
  
  quarterly_fight_details <- new_fights_df %>%
    filter(quarter(Date, with_year = TRUE) == quarter(quarterly_date, with_year = TRUE)) %>%
    select(FightNumber, Date, Fighter1, Fighter2, F1Rank, F2Rank)
  
  for (F1 in 1:nrow(quarterly_fight_details)) {
    if (tolower(quarterly_fight_details[F1, 3]) %in% tolower(na.omit(temp_fighter_ranking[,1]))) {
      quarterly_fight_details[F1,5] <- temp_fighter_ranking[tolower(na.omit(temp_fighter_ranking[,1])) == tolower(quarterly_fight_details[F1,3]), 2][1]
    }
    if (tolower(quarterly_fight_details[F1, 4]) %in% tolower(na.omit(temp_fighter_ranking[,1]))) {
      quarterly_fight_details[F1,6] <- temp_fighter_ranking[tolower(na.omit(temp_fighter_ranking[,1])) == tolower(quarterly_fight_details[F1,4]), 2][1]
    }
  }
  
  for (i in 1:nrow(quarterly_fight_details)) {
    new_fights_df[new_fights_df$FightNumber == quarterly_fight_details[i,1], c(F1RankCol, F2RankCol)] <- quarterly_fight_details[i, 5:6]
  }
  
  quarterly_date <- quarterly_date + months(3)
  if (quarter(quarterly_date, with_year = TRUE) > quarter(today(), with_year = TRUE)) {
    quarterly_date <- as.Date("2006-01-01", "%Y-%m-%d")
  }
}

new_fights_df$FightNumber <- c(1:nrow(new_fights_df))

new_fights_df$F1Rank <- as.numeric(new_fights_df$F1Rank)
new_fights_df$F2Rank <- as.numeric(new_fights_df$F2Rank)

#######################################################################################

Feature Engineering

As mentioned earlier, the dataset for this project is a combination of data scraped from several sources, as well as data derived (or newly created) from the scraped data. Specifically, the derived data includes career statistics for each fighter at the time of each fight. This data was not available anywhere online (to my knowledge), but was critical in developing an accurate description of each fighter at the time of each fight in which to build a model that predicts future outcomes. So, the basic approach to generating this new data was to sort through the entire fight dataset (ufc_fight_details_df) one fighter at a time in chronologically tally up their career stats. Below is an example of some of the code, which can almost certainly be refactored into something more economical, but for the time-being is made up of about 200 lines of nested for loops and if else statements.

4. Example block of code to generate derivitive data.

# Add columns to be populated to FIGHT_DETAILS
ufc_fight_details_df <- ufc_fight_details_df %>%
  mutate(F1CarFightTime = NA, F2CarFightTime = NA, F1UFCWins = NA, F2UFCWins = NA, F1UFCLosses = NA,
         F2UFCLosses = NA, F1UFCNC = NA, F2UFCNC = NA, F1CarSigStr = NA, F2CarSigStr = NA,
         F1CarSigStrAtt = NA, F2CarSigStrAtt = NA, F1CarSigStrAbs = NA, F2CarSigStrAbs = NA,
         F1CarTD = NA, F2CarTD = NA, F1CarTDAtt = NA, F2CarTDAtt = NA, F1CarTDAbs = NA, F2CarTDAbs = NA,
         F1CarOppSigStrAtt = NA, F2CarOppSigStrAtt = NA, F1CarOppTDAtt = NA, F2CarOppTDAtt = NA)

#### Begin loop to generate additional fighter data ####
for (fighter in unique_fighters$.) {
  fighter_career_data <- ufc_fight_details_df %>%
    filter((Fighter1 == as.character(fighter)) | (Fighter2 == as.character(fighter))) %>%
    arrange(Date)
  
  for (i in 1:nrow(fighter_career_data)) {
    
  #### Adding up total career time in ring for each fighter going into each fight - Fighter1 spot. ####
    if (fighter == fighter_career_data[i,2]) {
      
      if (i == 1) {
        # F1CarFightTime when F1
        fighter_career_data[i, 342] <- 0
        
        # F1CarSigStr when F1
        fighter_career_data[i, 350] <- 0
        
        # F1CarSigStrAtt when F1
        fighter_career_data[i, 352] <- 0
        
        # F1CarSigStrAbs when F1
        fighter_career_data[i, 354] <- 0
        
        # F1CarTD when F1
        fighter_career_data[i, 356] <- 0
        
        # F1CarTDAtt when F1
        fighter_career_data[i, 358] <- 0
        
        # F1CarTDAbs when F1
        fighter_career_data[i, 360] <- 0
        
        # F1CarOppSigStrAtt when F1
        fighter_career_data[i, 362] <- 0
        
        # F1CarOppTDAtt when F1
        fighter_career_data[i, 364] <- 0
      } else {
        # F1 was F1 last fight
        if (fighter == fighter_career_data[(i-1),2]) {
         
          # F1CarFightTime when F1 this fight was F1 last fight
          fighter_career_data[i, 342] <- fighter_career_data[(i-1), 342] + fighter_career_data[(i-1), 341]
          
          # Get fight data from last fight when F1 this fight was F1 last fight.
          temp_fight_data <- c(fighter_career_data[(i-1), 18], fighter_career_data[(i-1), 19],
                               fighter_career_data[(i-1), 162], fighter_career_data[(i-1), 23],
                               fighter_career_data[(i-1), 24], fighter_career_data[(i-1), 167],
                               fighter_career_data[(i-1), 163], fighter_career_data[(i-1), 168])
          
          # F1CarSigStr through F1CarOppTDAtt when F1 this fight was F1 last fight
          for (n in 1:8) {
            if (is.na(temp_fight_data[n])) {
              fighter_career_data[i, ((174+n)*2)] <- 0
            } else {
              fighter_career_data[i, ((174+n)*2)] <- fighter_career_data[(i-1), ((174+n)*2)] + temp_fight_data[n]
            }
          }
        }

Some widdling down of the features occured in going from the 382 features of ufc_fight_details_df to the subset of 48 features ultimately used in the logisitic regression model. This process was part logic, part domain expertise. Perhaps more on the logic side were features of physical description, like age, height, weight, reach, weight class, and stance. Career statistics features are the product of both logic and domain expertise. You don’t need to know anything about Mixed Martial Arts or UFC to think it’s a good idea to consider each fighter’s career statistics when making a prediction, but, for example, data for round-by-round statistics were not included because not all fights have the same number of rounds, and there would have been a considerable amount of additional data wrangling to present this data to the model in a useful way. The decision to include each fighter’s rank might also seem natural, but on the other hand an excellent data scientist without as much domain expertise might think that statistical descriptions of each fighter’s performance should be enough, or even superior than a somewhat subjective or circumstantial indicator like rank. But the ranking variable takes into consideration factors such as: the quality of a fighter’s past opponenents, and the chronology of past performance (e.g. a fighter with a long successful career that is now fighting past his prime).


Prediction Methods

After collecting and wrangling the data, we have a dataset of over 5,000 MMA fights ready to train a statistical model to make a predictions. Given we’re trying to predict a binary outcome - Win or Lose - for each fighter in a given fight, there are a few methods that would be suitable. For this project I tested two different methods - Logistic Regression and Random Forest, and ended up going with Logistic Regression because it performed better on test data.


Logistic Regression


Testing

# Variable for the date ten years ago today.
ten_years_ago <- ymd(today()) - years(10)
# This gives us the subset of fights (rows) we'll use for the model.
row_subset_fights <- ufc_fight_details_df %>%
  filter(Date > ten_years_ago) %>%
  filter(Gender == "Men") %>%
  filter(WeightClass != "Strawweight") %>%
  filter(WeightClass != "Atomweight") %>%
  filter(WeightClass != "Catch Weight") %>%
  filter(WeightClass != "Open Weight") %>%
  filter(WeightClass != "Other") %>%
  filter(WeightClass != "Super Heavyweight") %>%
  filter((F1UFCWins>0 | F1UFCLosses>0 | F1UFCNC>0) & (F2UFCWins>0 | F2UFCLosses>0 | F2UFCNC>0)) %>%
  arrange(Date)
# Subset of columns to be used in the model subset.
model_dataset_columns <- read.csv(paste(getwd(), "/FreshModelColumns.csv", sep = ""))
model_dataset_columns <- as.character(model_dataset_columns$ColumnNames)
# Get subset fight_details dataset - all rows; only columns from selected_features
fresh_model_dataset <- row_subset_fights %>%
  subset(select = model_dataset_columns)
# Factor Weightclass.
fresh_model_dataset$WeightClass <- as.factor(fresh_model_dataset$WeightClass)
# Factor F1Stance
fresh_model_dataset$F1Stance <- fresh_model_dataset$F1Stance %>%
  as.factor()
# Factor F2Stance
fresh_model_dataset$F2Stance <- fresh_model_dataset$F2Stance %>%
  as.factor()
##########!!!!!!!!! SUBSETTING !!!!!!!!!!###########
# Split fight_details_df into training and testing sets.
set.seed(109) 
split_ufc_data <- fresh_model_dataset$F1Wins %>%
  sample.split(SplitRatio = 0.70)
train_ufc_subset <- fresh_model_dataset %>%
  subset(split_ufc_data == TRUE)
test_ufc_subset <- fresh_model_dataset %>%
  subset(split_ufc_data == FALSE)
# Remove and save columns we don't need, but may need later... just in case.
saved_column_names <- c("FightNumber","Fighter1","Fighter2","Date")
column_pos <- c()
for (column in saved_column_names) {
  column_pos <- c(column_pos, grep(column, colnames(fresh_model_dataset)))
}
saved_columns <- fresh_model_dataset %>%
  select(column_pos)
train_ufc_subset <- train_ufc_subset %>% select(-column_pos)
test_ufc_subset <- test_ufc_subset %>% select(-column_pos)
####################################
######### LOGIT MODEL ##############
ufc_fresh_logit_model <- glm(F1Wins ~ ., data = na.omit(train_ufc_subset), family = binomial)
# Prediction - probability that F1 wins
test_ufc_subset$F1WinsProb <- round(predict(ufc_fresh_logit_model, newdata = test_ufc_subset, type = "response"), 4)
#########################################
########## THRESHOLD MODELS #############
WinThreshold <- 0.5
# Filter out fights to get only those where F1WinsProb > 0.6 or < 0.4
test_threshold_subset <- test_ufc_subset %>%
  filter(F1WinsProb > WinThreshold | F1WinsProb < (1-WinThreshold))
test_threshold_subset$PredF1Wins <- as.numeric(test_threshold_subset$F1WinsProb > WinThreshold)
#######################################
######### LOGIT CONFUSION MATRIX #######
logitPerformance <- confusionMatrix(as.factor(test_threshold_subset$PredF1Wins), as.factor(test_threshold_subset$F1Wins),
                                    dnn = c("PredF1Wins", "F1Wins"))
logitPerformance
Confusion Matrix and Statistics

          F1Wins
PredF1Wins   0   1
         0 119  99
         1 237 479
                                          
               Accuracy : 0.6403          
                 95% CI : (0.6085, 0.6711)
    No Information Rate : 0.6188          
    P-Value [Acc > NIR] : 0.09412         
                                          
                  Kappa : 0.1761          
 Mcnemar's Test P-Value : 7.782e-14       
                                          
            Sensitivity : 0.3343          
            Specificity : 0.8287          
         Pos Pred Value : 0.5459          
         Neg Pred Value : 0.6690          
             Prevalence : 0.3812          
         Detection Rate : 0.1274          
   Detection Prevalence : 0.2334          
      Balanced Accuracy : 0.5815          
                                          
       'Positive' Class : 0               
                                          
roc(F1Wins ~ F1WinsProb, test_threshold_subset, auc = TRUE, plot = TRUE)

Call:
roc.formula(formula = F1Wins ~ F1WinsProb, data = test_threshold_subset,     auc = TRUE, plot = TRUE)

Data: F1WinsProb in 356 controls (F1Wins 0) < 578 cases (F1Wins 1).
Area under the curve: 0.6701


Random Forest


Testing

############################ RANDOM FOREST MODEL ###################################
#set.seed(388)
ufc_fresh_randomForest_model <- randomForest(as.factor(F1Wins) ~ ., data = na.omit(train_ufc_subset),
                                            importance=TRUE)
test_ufc_subset$F1WinsProb <- NULL
test_ufc_subset$RFF1WinsProb <- predict(ufc_fresh_randomForest_model, test_ufc_subset, type = "prob")[,2]
test_ufc_subset$RFPredF1Wins <- as.numeric(test_ufc_subset$RFF1WinsProb > 0.5)
randomForestPerformance <- confusionMatrix(as.factor(test_ufc_subset$RFPredF1Wins), as.factor(test_ufc_subset$F1Wins), dnn = c("F1Wins", "PredF1Wins"))
randomForestPerformance
Confusion Matrix and Statistics

      PredF1Wins
F1Wins   0   1
     0  91  77
     1 265 501
                                         
               Accuracy : 0.6338         
                 95% CI : (0.602, 0.6648)
    No Information Rate : 0.6188         
    P-Value [Acc > NIR] : 0.1817         
                                         
                  Kappa : 0.1362         
 Mcnemar's Test P-Value : <2e-16         
                                         
            Sensitivity : 0.25562        
            Specificity : 0.86678        
         Pos Pred Value : 0.54167        
         Neg Pred Value : 0.65405        
             Prevalence : 0.38116        
         Detection Rate : 0.09743        
   Detection Prevalence : 0.17987        
      Balanced Accuracy : 0.56120        
                                         
       'Positive' Class : 0              
                                         
roc(F1Wins ~ RFF1WinsProb, test_ufc_subset, auc = TRUE, plot = TRUE)

Call:
roc.formula(formula = F1Wins ~ RFF1WinsProb, data = test_ufc_subset,     auc = TRUE, plot = TRUE)

Data: RFF1WinsProb in 356 controls (F1Wins 0) < 578 cases (F1Wins 1).
Area under the curve: 0.6282

varImpPlot(ufc_fresh_randomForest_model, n.var = 10)


UFC Project Workflow

The practical application of this project is to generate predictions for UFC fights on a weekly basis in order to wager on the fights. There are four main steps in this workflow.

  1. Update FightMetric_fighters_df: After each fight event, existing fighter data changes and sometimes new fighters are introduced to UFC. So, we want to pull this new information into our dataset of fighters (FightMetric_fighters_df). Specifically, we do this by running two blocks of code labeled for FightMetric_fighters_df that can be found in the UFC.R script beginning at line ~250 and ~1022, respectively.

  2. Update fighter_ranking_df: Of course, after each event fighter’s win and lose, and so the fighter rankings change. There are actually two compenents to our fighter_ranking_df - we have historical rankings and current rankings. After each event we need to update the current rankings of each fighter. But the historical rankings will not change. These are recorded in the dataset on a quarterly basis. In other words, we have historical rankings for each weightclass for each quarter going back to Q1 of 2006. So updating of the historical rankings only occurs once a quarter when they are released. Theoretically this means our model could have a few cases of rankings that are slightly off, but as a practical matter quarterly rankings are probably pretty accurate as the vast majority of fighters do not fight more than once per quarter. We update the current rankings by running the block of code beginning at line ~1079 of UFCUpdater.R, and we update the historical rankings once a quarter by running the block of code at line ~1030 of UFCUpdater.R.

  3. Update ufc_fight_details_df: After each event there are new fights and detailed statistics to be added to our ufc_fight_details_df. While this dataset was originally built using the UFC.R script, it can be a lengthy and somewhat hands-on process to scrape nearly 10,000 web pages once, but to do so every week would be overly burdonsome. So, there is another script, UFCUpdater.R, that scrapes only the recently completed fights and data, computes their derivitive data, and then merges them back into ufc_fight_details_df. We simply run the UFCUpdator.R script to accomplish this.

  4. Generate predictions: Once we have updated our dataset, we are ready to generate predictions for the set of upcoming fights. To accomplish this we run the UFCPredictor.R script. This script does a few things. First, it scrapes the upcoming fight matchups. Then it computes and gathers the relevant variables for each fighter from our dataset. Then it refits the model with the most recent dataset. Finally, it applies our model to the set of data for the upcoming fights to produce a probability that Fighter 1 will win.


Roadmap For Improvement

The status of this project is both functional and practical, and it accomplishes what it sets out to do. Nonetheless, there is room for improvement. The following is a working list of items to explore for improving the performance of the model.

Feature Engineering
1. A feature that incorporates how a fight ends: For example, does a fighter win by KO/TKO, Submission, Decision, etc…
2. A feature that incorporates whether or not this is a fighter’s first fight in UFC.
3. A feature to incorporate a fighter’s current winning or losing streak.
4. Further testing of different variable combinations (removing variables that appear insignificant, for example)

Predictive Modeling
1. Explore implementing and Ensemble model of both Logistic Regression and Random Forest.
2. Explore clustering on sets of fights predicted incorrectly to see if we can develop rules to improve overall predictive accuracy with a multi-stage predictive approach… or a multi-faceted model (i.e. generate multiple fits for subsets of fights that meet certain criteria).
3. Explore Specificity, Precision, and other model performance measures as a way to weight or hone model predictions.

Usability
1. Automate dataset updates and prediction generation.
2. Build a user-friendly dashboard or interactive interface with Flexdashboard or Shiny, respectively.

Code Base
1. Implement testing suite to help avoid “breaking” an increasingly complex code base during future code maintenance and feature additions.
2. Plenty of room for refactoring, which will simplfy readability and collaboration, and may present some gains in execution speed.


Appendix

Data

# Creating table to explain the data.
data_table <- data.frame(colnames(ufc_fight_details_df),
                         rep(NA,length(ufc_fight_details_df)),
                         as.character(ufc_fight_details_df[3449,]),
                         rep("FightMetric",length(ufc_fight_details_df)),
                         stringsAsFactors = FALSE)
# Assign column names.
colnames(data_table) <- c("Variables", "Unabreviated", "Example", "Source")
# Generate unabreviated variable names.
data_table$Unabreviated <- data_table$Variables %>%
  gsub("F1", "Fighter1 ", .) %>%
  gsub("F2", "Fighter2 ", .) %>%
  gsub("Str", "Strike ", .) %>%
  gsub("Att", "Attempt ", .) %>%
  gsub("Car", "Career ", .) %>%
  gsub("Sig", "Significant ", .) %>%
  gsub("Opp", "Opponent ", .) %>%
  gsub("TD", "Take Down ", .) %>%
  gsub("Abs", "Absorbed ", .) %>%
  gsub("pM", "per Minute ", .) %>%
  gsub("Acc", "Accuracy ", .) %>%
  gsub("Def", "Defense ", .) %>%
  gsub("Avg", "Average ", .) %>%
  gsub("Dist", "Distance ", .) %>%
  gsub("KD", "Knock Down ", .) %>%
  gsub("Pass", "Guard Pass ", .) %>%
  gsub("NC", "No Contest ", .) %>%
  gsub("R1", "Round 1 ", .) %>%
  gsub("R2", "Round 2 ", .) %>%
  gsub("R3", "Round 3 ", .) %>%
  gsub("R4", "Round 4 ", .) %>%
  gsub("R5", "Round 5 ", .) %>%
  gsub("Tot", "Total ", .) %>%
  gsub("Rev", "Reversal", .) %>%
  gsub("SA", "Strikes Absorbed ", .) %>%
  gsub("Sub", "Submission ", .) %>%
  gsub("Ref", "Referee ", .) %>%
  gsub("SL", "Strikes Landed ", .)
# Insert data sources
self_derived <- grep(".*Car.*", data_table$Variables)
fightmatrix <- grep("F1Rank|F2Rank", data_table$Variables)
data_table$Source[self_derived] <- "Derived"
data_table$Source[fightmatrix] <- "FightMatrix"
kable(data_table, caption = "Composition of the primary datset, [ufc_fight_details_df]", align = 'l')
Variables Unabreviated Example Source
Date Date 2009-03-07 FightMetric
Detail Detail Kick to Head At Distance FightMetric
F1Age Fighter1 Age 32 FightMetric
F1BodyStr Fighter1 BodyStrike 0 FightMetric
F1BodyStrAtt Fighter1 BodyStrike Attempt 0 FightMetric
F1CarFightTime Fighter1 Career FightTime 3790 Derived
F1CarOppSigStrAtt Fighter1 Career Opponent Significant Strike Attempt 540 Derived
F1CarOppTDAtt Fighter1 Career Opponent Take Down Attempt 5 Derived
F1CarSigStr Fighter1 Career Significant Strike 216 Derived
F1CarSigStrAbs Fighter1 Career Significant Strike Absorbed 215 Derived
F1CarSigStrAtt Fighter1 Career Significant Strike Attempt 505 Derived
F1CarStrAbspM Fighter1 Career Strike Absorbed per Minute 3.4 Derived
F1CarStrAcc Fighter1 Career Strike Accuracy 0.43 Derived
F1CarStrDef Fighter1 Career Strike Defense 0.4 Derived
F1CarStrLpM Fighter1 Career Strike Lper Minute 3.42 Derived
F1CarTD Fighter1 Career Take Down 17 Derived
F1CarTDAbs Fighter1 Career Take Down Absorbed 0 Derived
F1CarTDAcc Fighter1 Career Take Down Accuracy 0.52 Derived
F1CarTDAtt Fighter1 Career Take Down Attempt 33 Derived
F1CarTDAvg Fighter1 Career Take Down Average 2.43 Derived
F1CarTDDef Fighter1 Career Take Down Defense 0 Derived
F1ClinchStr Fighter1 ClinchStrike 2 FightMetric
F1ClinchStrAtt Fighter1 ClinchStrike Attempt 4 FightMetric
F1DistStr Fighter1 Distance Strike 20 FightMetric
F1DistStrAtt Fighter1 Distance Strike Attempt 42 FightMetric
F1DOB Fighter1 DOB 1976-10-05 FightMetric
F1GroundStr Fighter1 GroundStrike 1 FightMetric
F1GroundStrAtt Fighter1 GroundStrike Attempt 1 FightMetric
F1HeadStr Fighter1 HeadStrike 18 FightMetric
F1HeadStrAtt Fighter1 HeadStrike Attempt 42 FightMetric
F1Height Fighter1 Height 73 FightMetric
F1KD Fighter1 Knock Down 1 FightMetric
F1LegStr Fighter1 LegStrike 5 FightMetric
F1LegStrAtt Fighter1 LegStrike Attempt 5 FightMetric
F1Pass Fighter1 Guard Pass 0 FightMetric
F1ProLosses Fighter1 ProLosses 5 FightMetric
F1ProNC Fighter1 ProNo Contest 0 FightMetric
F1ProWins Fighter1 ProWins 12 FightMetric
F1R1BodyStr Fighter1 Round 1 BodyStrike 0 FightMetric
F1R1BodyStrAtt Fighter1 Round 1 BodyStrike Attempt 0 FightMetric
F1R1ClinchStr Fighter1 Round 1 ClinchStrike 2 FightMetric
F1R1ClinchStrAtt Fighter1 Round 1 ClinchStrike Attempt 4 FightMetric
F1R1DistStr Fighter1 Round 1 Distance Strike 20 FightMetric
F1R1DistStrAtt Fighter1 Round 1 Distance Strike Attempt 42 FightMetric
F1R1GroundStr Fighter1 Round 1 GroundStrike 1 FightMetric
F1R1GroundStrAtt Fighter1 Round 1 GroundStrike Attempt 1 FightMetric
F1R1HeadStr Fighter1 Round 1 HeadStrike 18 FightMetric
F1R1HeadStrAtt Fighter1 Round 1 HeadStrike Attempt 42 FightMetric
F1R1KD Fighter1 Round 1 Knock Down 1 FightMetric
F1R1LegStr Fighter1 Round 1 LegStrike 5 FightMetric
F1R1LegStrAtt Fighter1 Round 1 LegStrike Attempt 5 FightMetric
F1R1Pass Fighter1 Round 1 Guard Pass 0 FightMetric
F1R1Rev Fighter1 Round 1 Reversal 0 FightMetric
F1R1SigStr Fighter1 Round 1 Significant Strike 23 FightMetric
F1R1SigStrAtt Fighter1 Round 1 Significant Strike Attempt 47 FightMetric
F1R1SigStrPercent Fighter1 Round 1 Significant Strike Percent 0.48 FightMetric
F1R1SubAtt Fighter1 Round 1 Submission Attempt 0 FightMetric
F1R1TD Fighter1 Round 1 Take Down 0 FightMetric
F1R1TDAtt Fighter1 Round 1 Take Down Attempt 0 FightMetric
F1R1TDPercent Fighter1 Round 1 Take Down Percent 0 FightMetric
F1R1TotStr Fighter1 Round 1 Total Strike 40 FightMetric
F1R1TotStrAtt Fighter1 Round 1 Total Strike Attempt 64 FightMetric
F1R2BodyStr Fighter1 Round 2 BodyStrike NA FightMetric
F1R2BodyStrAtt Fighter1 Round 2 BodyStrike Attempt NA FightMetric
F1R2ClinchStr Fighter1 Round 2 ClinchStrike NA FightMetric
F1R2ClinchStrAtt Fighter1 Round 2 ClinchStrike Attempt NA FightMetric
F1R2DistStr Fighter1 Round 2 Distance Strike NA FightMetric
F1R2DistStrAtt Fighter1 Round 2 Distance Strike Attempt NA FightMetric
F1R2GroundStr Fighter1 Round 2 GroundStrike NA FightMetric
F1R2GroundStrAtt Fighter1 Round 2 GroundStrike Attempt NA FightMetric
F1R2HeadStr Fighter1 Round 2 HeadStrike NA FightMetric
F1R2HeadStrAtt Fighter1 Round 2 HeadStrike Attempt NA FightMetric
F1R2KD Fighter1 Round 2 Knock Down NA FightMetric
F1R2LegStr Fighter1 Round 2 LegStrike NA FightMetric
F1R2LegStrAtt Fighter1 Round 2 LegStrike Attempt NA FightMetric
F1R2Pass Fighter1 Round 2 Guard Pass NA FightMetric
F1R2Rev Fighter1 Round 2 Reversal NA FightMetric
F1R2SigStr Fighter1 Round 2 Significant Strike NA FightMetric
F1R2SigStrAtt Fighter1 Round 2 Significant Strike Attempt NA FightMetric
F1R2SigStrPercent Fighter1 Round 2 Significant Strike Percent NA FightMetric
F1R2SubAtt Fighter1 Round 2 Submission Attempt NA FightMetric
F1R2TD Fighter1 Round 2 Take Down NA FightMetric
F1R2TDAtt Fighter1 Round 2 Take Down Attempt NA FightMetric
F1R2TDPercent Fighter1 Round 2 Take Down Percent NA FightMetric
F1R2TotStr Fighter1 Round 2 Total Strike NA FightMetric
F1R2TotStrAtt Fighter1 Round 2 Total Strike Attempt NA FightMetric
F1R3BodyStr Fighter1 Round 3 BodyStrike NA FightMetric
F1R3BodyStrAtt Fighter1 Round 3 BodyStrike Attempt NA FightMetric
F1R3ClinchStr Fighter1 Round 3 ClinchStrike NA FightMetric
F1R3ClinchStrAtt Fighter1 Round 3 ClinchStrike Attempt NA FightMetric
F1R3DistStr Fighter1 Round 3 Distance Strike NA FightMetric
F1R3DistStrAtt Fighter1 Round 3 Distance Strike Attempt NA FightMetric
F1R3GroundStr Fighter1 Round 3 GroundStrike NA FightMetric
F1R3GroundStrAtt Fighter1 Round 3 GroundStrike Attempt NA FightMetric
F1R3HeadStr Fighter1 Round 3 HeadStrike NA FightMetric
F1R3HeadStrAtt Fighter1 Round 3 HeadStrike Attempt NA FightMetric
F1R3KD Fighter1 Round 3 Knock Down NA FightMetric
F1R3LegStr Fighter1 Round 3 LegStrike NA FightMetric
F1R3LegStrAtt Fighter1 Round 3 LegStrike Attempt NA FightMetric
F1R3Pass Fighter1 Round 3 Guard Pass NA FightMetric
F1R3Rev Fighter1 Round 3 Reversal NA FightMetric
F1R3SigStr Fighter1 Round 3 Significant Strike NA FightMetric
F1R3SigStrAtt Fighter1 Round 3 Significant Strike Attempt NA FightMetric
F1R3SigStrPercent Fighter1 Round 3 Significant Strike Percent NA FightMetric
F1R3SubAtt Fighter1 Round 3 Submission Attempt NA FightMetric
F1R3TD Fighter1 Round 3 Take Down NA FightMetric
F1R3TDAtt Fighter1 Round 3 Take Down Attempt NA FightMetric
F1R3TDPercent Fighter1 Round 3 Take Down Percent NA FightMetric
F1R3TotStr Fighter1 Round 3 Total Strike NA FightMetric
F1R3TotStrAtt Fighter1 Round 3 Total Strike Attempt NA FightMetric
F1R4BodyStr Fighter1 Round 4 BodyStrike NA FightMetric
F1R4BodyStrAtt Fighter1 Round 4 BodyStrike Attempt NA FightMetric
F1R4ClinchStr Fighter1 Round 4 ClinchStrike NA FightMetric
F1R4ClinchStrAtt Fighter1 Round 4 ClinchStrike Attempt NA FightMetric
F1R4DistStr Fighter1 Round 4 Distance Strike NA FightMetric
F1R4DistStrAtt Fighter1 Round 4 Distance Strike Attempt NA FightMetric
F1R4GroundStr Fighter1 Round 4 GroundStrike NA FightMetric
F1R4GroundStrAtt Fighter1 Round 4 GroundStrike Attempt NA FightMetric
F1R4HeadStr Fighter1 Round 4 HeadStrike NA FightMetric
F1R4HeadStrAtt Fighter1 Round 4 HeadStrike Attempt NA FightMetric
F1R4KD Fighter1 Round 4 Knock Down NA FightMetric
F1R4LegStr Fighter1 Round 4 LegStrike NA FightMetric
F1R4LegStrAtt Fighter1 Round 4 LegStrike Attempt NA FightMetric
F1R4Pass Fighter1 Round 4 Guard Pass NA FightMetric
F1R4Rev Fighter1 Round 4 Reversal NA FightMetric
F1R4SigStr Fighter1 Round 4 Significant Strike NA FightMetric
F1R4SigStrAtt Fighter1 Round 4 Significant Strike Attempt NA FightMetric
F1R4SigStrPercent Fighter1 Round 4 Significant Strike Percent NA FightMetric
F1R4SubAtt Fighter1 Round 4 Submission Attempt NA FightMetric
F1R4TD Fighter1 Round 4 Take Down NA FightMetric
F1R4TDAtt Fighter1 Round 4 Take Down Attempt NA FightMetric
F1R4TDPercent Fighter1 Round 4 Take Down Percent NA FightMetric
F1R4TotStr Fighter1 Round 4 Total Strike NA FightMetric
F1R4TotStrAtt Fighter1 Round 4 Total Strike Attempt NA FightMetric
F1R5BodyStr Fighter1 Round 5 BodyStrike NA FightMetric
F1R5BodyStrAtt Fighter1 Round 5 BodyStrike Attempt NA FightMetric
F1R5ClinchStr Fighter1 Round 5 ClinchStrike NA FightMetric
F1R5ClinchStrAtt Fighter1 Round 5 ClinchStrike Attempt NA FightMetric
F1R5DistStr Fighter1 Round 5 Distance Strike NA FightMetric
F1R5DistStrAtt Fighter1 Round 5 Distance Strike Attempt NA FightMetric
F1R5GroundStr Fighter1 Round 5 GroundStrike NA FightMetric
F1R5GroundStrAtt Fighter1 Round 5 GroundStrike Attempt NA FightMetric
F1R5HeadStr Fighter1 Round 5 HeadStrike NA FightMetric
F1R5HeadStrAtt Fighter1 Round 5 HeadStrike Attempt NA FightMetric
F1R5KD Fighter1 Round 5 Knock Down NA FightMetric
F1R5LegStr Fighter1 Round 5 LegStrike NA FightMetric
F1R5LegStrAtt Fighter1 Round 5 LegStrike Attempt NA FightMetric
F1R5Pass Fighter1 Round 5 Guard Pass NA FightMetric
F1R5Rev Fighter1 Round 5 Reversal NA FightMetric
F1R5SigStr Fighter1 Round 5 Significant Strike NA FightMetric
F1R5SigStrAtt Fighter1 Round 5 Significant Strike Attempt NA FightMetric
F1R5SigStrPercent Fighter1 Round 5 Significant Strike Percent NA FightMetric
F1R5SubAtt Fighter1 Round 5 Submission Attempt NA FightMetric
F1R5TD Fighter1 Round 5 Take Down NA FightMetric
F1R5TDAtt Fighter1 Round 5 Take Down Attempt NA FightMetric
F1R5TDPercent Fighter1 Round 5 Take Down Percent NA FightMetric
F1R5TotStr Fighter1 Round 5 Total Strike NA FightMetric
F1R5TotStrAtt Fighter1 Round 5 Total Strike Attempt NA FightMetric
F1Rank Fighter1 Rank 20 FightMatrix
F1Reach Fighter1 Reach 76 FightMetric
F1Rev Fighter1 Reversal 0 FightMetric
F1SApM Fighter1 Strikes Absorbed per Minute 3.8 FightMetric
F1SigStr Fighter1 Significant Strike 23 FightMetric
F1SigStrAtt Fighter1 Significant Strike Attempt 47 FightMetric
F1SigStrPercent Fighter1 Significant Strike Percent 0.48 FightMetric
F1SLpM Fighter1 Strikes Landed per Minute 3.69 FightMetric
F1Stance Fighter1 Stance Orthodox FightMetric
F1StrAcc Fighter1 Strike Accuracy 0.42 FightMetric
F1StrDef Fighter1 Strike Defense 0.61 FightMetric
F1SubAtt Fighter1 Submission Attempt 0 FightMetric
F1SubAttAvg Fighter1 Submission Attempt Average 0 FightMetric
F1TD Fighter1 Take Down 0 FightMetric
F1TDAcc Fighter1 Take Down Accuracy 0.34 FightMetric
F1TDAtt Fighter1 Take Down Attempt 0 FightMetric
F1TDAvg Fighter1 Take Down Average 3 FightMetric
F1TDDef Fighter1 Take Down Defense 0.91 FightMetric
F1TDPercent Fighter1 Take Down Percent 0 FightMetric
F1TotStr Fighter1 Total Strike 40 FightMetric
F1TotStrAtt Fighter1 Total Strike Attempt 64 FightMetric
F1UFCLosses Fighter1 UFCLosses 2 FightMetric
F1UFCNC Fighter1 UFCNo Contest 0 FightMetric
F1UFCWins Fighter1 UFCWins 5 FightMetric
F1Weight Fighter1 Weight 205 FightMetric
F1Wins Fighter1 Wins 1 FightMetric
F2Age Fighter2 Age 31 FightMetric
F2BodyStr Fighter2 BodyStrike 3 FightMetric
F2BodyStrAtt Fighter2 BodyStrike Attempt 5 FightMetric
F2CarFightTime Fighter2 Career FightTime 401 Derived
F2CarOppSigStrAtt Fighter2 Career Opponent Significant Strike Attempt 20 Derived
F2CarOppTDAtt Fighter2 Career Opponent Take Down Attempt 1 Derived
F2CarSigStr Fighter2 Career Significant Strike 34 Derived
F2CarSigStrAbs Fighter2 Career Significant Strike Absorbed 3 Derived
F2CarSigStrAtt Fighter2 Career Significant Strike Attempt 55 Derived
F2CarStrAbspM Fighter2 Career Strike Absorbed per Minute 0.45 Derived
F2CarStrAcc Fighter2 Career Strike Accuracy 0.62 Derived
F2CarStrDef Fighter2 Career Strike Defense 0.15 Derived
F2CarStrLpM Fighter2 Career Strike Lper Minute 5.09 Derived
F2CarTD Fighter2 Career Take Down 1 Derived
F2CarTDAbs Fighter2 Career Take Down Absorbed 0 Derived
F2CarTDAcc Fighter2 Career Take Down Accuracy 0.2 Derived
F2CarTDAtt Fighter2 Career Take Down Attempt 5 Derived
F2CarTDAvg Fighter2 Career Take Down Average 0.5 Derived
F2CarTDDef Fighter2 Career Take Down Defense 0 Derived
F2ClinchStr Fighter2 ClinchStrike 4 FightMetric
F2ClinchStrAtt Fighter2 ClinchStrike Attempt 6 FightMetric
F2DistStr Fighter2 Distance Strike 10 FightMetric
F2DistStrAtt Fighter2 Distance Strike Attempt 31 FightMetric
F2DOB Fighter2 DOB 1978-02-09 FightMetric
F2GroundStr Fighter2 GroundStrike 0 FightMetric
F2GroundStrAtt Fighter2 GroundStrike Attempt 0 FightMetric
F2HeadStr Fighter2 HeadStrike 11 FightMetric
F2HeadStrAtt Fighter2 HeadStrike Attempt 32 FightMetric
F2Height Fighter2 Height 72 FightMetric
F2KD Fighter2 Knock Down 0 FightMetric
F2LegStr Fighter2 LegStrike 0 FightMetric
F2LegStrAtt Fighter2 LegStrike Attempt 0 FightMetric
F2Pass Fighter2 Guard Pass 0 FightMetric
F2ProLosses Fighter2 ProLosses 6 FightMetric
F2ProNC Fighter2 ProNo Contest 0 FightMetric
F2ProWins Fighter2 ProWins 14 FightMetric
F2R1BodyStr Fighter2 Round 1 BodyStrike 3 FightMetric
F2R1BodyStrAtt Fighter2 Round 1 BodyStrike Attempt 5 FightMetric
F2R1ClinchStr Fighter2 Round 1 ClinchStrike 4 FightMetric
F2R1ClinchStrAtt Fighter2 Round 1 ClinchStrike Attempt 6 FightMetric
F2R1DistStr Fighter2 Round 1 Distance Strike 10 FightMetric
F2R1DistStrAtt Fighter2 Round 1 Distance Strike Attempt 31 FightMetric
F2R1GroundStr Fighter2 Round 1 GroundStrike 0 FightMetric
F2R1GroundStrAtt Fighter2 Round 1 GroundStrike Attempt 0 FightMetric
F2R1HeadStr Fighter2 Round 1 HeadStrike 11 FightMetric
F2R1HeadStrAtt Fighter2 Round 1 HeadStrike Attempt 32 FightMetric
F2R1KD Fighter2 Round 1 Knock Down 0 FightMetric
F2R1LegStr Fighter2 Round 1 LegStrike 0 FightMetric
F2R1LegStrAtt Fighter2 Round 1 LegStrike Attempt 0 FightMetric
F2R1Pass Fighter2 Round 1 Guard Pass 0 FightMetric
F2R1Rev Fighter2 Round 1 Reversal 0 FightMetric
F2R1SigStr Fighter2 Round 1 Significant Strike 14 FightMetric
F2R1SigStrAtt Fighter2 Round 1 Significant Strike Attempt 37 FightMetric
F2R1SigStrPercent Fighter2 Round 1 Significant Strike Percent 0.37 FightMetric
F2R1SubAtt Fighter2 Round 1 Submission Attempt 0 FightMetric
F2R1TD Fighter2 Round 1 Take Down 0 FightMetric
F2R1TDAtt Fighter2 Round 1 Take Down Attempt 4 FightMetric
F2R1TDPercent Fighter2 Round 1 Take Down Percent 0 FightMetric
F2R1TotStr Fighter2 Round 1 Total Strike 14 FightMetric
F2R1TotStrAtt Fighter2 Round 1 Total Strike Attempt 37 FightMetric
F2R2BodyStr Fighter2 Round 2 BodyStrike NA FightMetric
F2R2BodyStrAtt Fighter2 Round 2 BodyStrike Attempt NA FightMetric
F2R2ClinchStr Fighter2 Round 2 ClinchStrike NA FightMetric
F2R2ClinchStrAtt Fighter2 Round 2 ClinchStrike Attempt NA FightMetric
F2R2DistStr Fighter2 Round 2 Distance Strike NA FightMetric
F2R2DistStrAtt Fighter2 Round 2 Distance Strike Attempt NA FightMetric
F2R2GroundStr Fighter2 Round 2 GroundStrike NA FightMetric
F2R2GroundStrAtt Fighter2 Round 2 GroundStrike Attempt NA FightMetric
F2R2HeadStr Fighter2 Round 2 HeadStrike NA FightMetric
F2R2HeadStrAtt Fighter2 Round 2 HeadStrike Attempt NA FightMetric
F2R2KD Fighter2 Round 2 Knock Down NA FightMetric
F2R2LegStr Fighter2 Round 2 LegStrike NA FightMetric
F2R2LegStrAtt Fighter2 Round 2 LegStrike Attempt NA FightMetric
F2R2Pass Fighter2 Round 2 Guard Pass NA FightMetric
F2R2Rev Fighter2 Round 2 Reversal NA FightMetric
F2R2SigStr Fighter2 Round 2 Significant Strike NA FightMetric
F2R2SigStrAtt Fighter2 Round 2 Significant Strike Attempt NA FightMetric
F2R2SigStrPercent Fighter2 Round 2 Significant Strike Percent NA FightMetric
F2R2SubAtt Fighter2 Round 2 Submission Attempt NA FightMetric
F2R2TD Fighter2 Round 2 Take Down NA FightMetric
F2R2TDAtt Fighter2 Round 2 Take Down Attempt NA FightMetric
F2R2TDPercent Fighter2 Round 2 Take Down Percent NA FightMetric
F2R2TotStr Fighter2 Round 2 Total Strike NA FightMetric
F2R2TotStrAtt Fighter2 Round 2 Total Strike Attempt NA FightMetric
F2R3BodyStr Fighter2 Round 3 BodyStrike NA FightMetric
F2R3BodyStrAtt Fighter2 Round 3 BodyStrike Attempt NA FightMetric
F2R3ClinchStr Fighter2 Round 3 ClinchStrike NA FightMetric
F2R3ClinchStrAtt Fighter2 Round 3 ClinchStrike Attempt NA FightMetric
F2R3DistStr Fighter2 Round 3 Distance Strike NA FightMetric
F2R3DistStrAtt Fighter2 Round 3 Distance Strike Attempt NA FightMetric
F2R3GroundStr Fighter2 Round 3 GroundStrike NA FightMetric
F2R3GroundStrAtt Fighter2 Round 3 GroundStrike Attempt NA FightMetric
F2R3HeadStr Fighter2 Round 3 HeadStrike NA FightMetric
F2R3HeadStrAtt Fighter2 Round 3 HeadStrike Attempt NA FightMetric
F2R3KD Fighter2 Round 3 Knock Down NA FightMetric
F2R3LegStr Fighter2 Round 3 LegStrike NA FightMetric
F2R3LegStrAtt Fighter2 Round 3 LegStrike Attempt NA FightMetric
F2R3Pass Fighter2 Round 3 Guard Pass NA FightMetric
F2R3Rev Fighter2 Round 3 Reversal NA FightMetric
F2R3SigStr Fighter2 Round 3 Significant Strike NA FightMetric
F2R3SigStrAtt Fighter2 Round 3 Significant Strike Attempt NA FightMetric
F2R3SigStrPercent Fighter2 Round 3 Significant Strike Percent NA FightMetric
F2R3SubAtt Fighter2 Round 3 Submission Attempt NA FightMetric
F2R3TD Fighter2 Round 3 Take Down NA FightMetric
F2R3TDAtt Fighter2 Round 3 Take Down Attempt NA FightMetric
F2R3TDPercent Fighter2 Round 3 Take Down Percent NA FightMetric
F2R3TotStr Fighter2 Round 3 Total Strike NA FightMetric
F2R3TotStrAtt Fighter2 Round 3 Total Strike Attempt NA FightMetric
F2R4BodyStr Fighter2 Round 4 BodyStrike NA FightMetric
F2R4BodyStrAtt Fighter2 Round 4 BodyStrike Attempt NA FightMetric
F2R4ClinchStr Fighter2 Round 4 ClinchStrike NA FightMetric
F2R4ClinchStrAtt Fighter2 Round 4 ClinchStrike Attempt NA FightMetric
F2R4DistStr Fighter2 Round 4 Distance Strike NA FightMetric
F2R4DistStrAtt Fighter2 Round 4 Distance Strike Attempt NA FightMetric
F2R4GroundStr Fighter2 Round 4 GroundStrike NA FightMetric
F2R4GroundStrAtt Fighter2 Round 4 GroundStrike Attempt NA FightMetric
F2R4HeadStr Fighter2 Round 4 HeadStrike NA FightMetric
F2R4HeadStrAtt Fighter2 Round 4 HeadStrike Attempt NA FightMetric
F2R4KD Fighter2 Round 4 Knock Down NA FightMetric
F2R4LegStr Fighter2 Round 4 LegStrike NA FightMetric
F2R4LegStrAtt Fighter2 Round 4 LegStrike Attempt NA FightMetric
F2R4Pass Fighter2 Round 4 Guard Pass NA FightMetric
F2R4Rev Fighter2 Round 4 Reversal NA FightMetric
F2R4SigStr Fighter2 Round 4 Significant Strike NA FightMetric
F2R4SigStrAtt Fighter2 Round 4 Significant Strike Attempt NA FightMetric
F2R4SigStrPercent Fighter2 Round 4 Significant Strike Percent NA FightMetric
F2R4SubAtt Fighter2 Round 4 Submission Attempt NA FightMetric
F2R4TD Fighter2 Round 4 Take Down NA FightMetric
F2R4TDAtt Fighter2 Round 4 Take Down Attempt NA FightMetric
F2R4TDPercent Fighter2 Round 4 Take Down Percent NA FightMetric
F2R4TotStr Fighter2 Round 4 Total Strike NA FightMetric
F2R4TotStrAtt Fighter2 Round 4 Total Strike Attempt NA FightMetric
F2R5BodyStr Fighter2 Round 5 BodyStrike NA FightMetric
F2R5BodyStrAtt Fighter2 Round 5 BodyStrike Attempt NA FightMetric
F2R5ClinchStr Fighter2 Round 5 ClinchStrike NA FightMetric
F2R5ClinchStrAtt Fighter2 Round 5 ClinchStrike Attempt NA FightMetric
F2R5DistStr Fighter2 Round 5 Distance Strike NA FightMetric
F2R5DistStrAtt Fighter2 Round 5 Distance Strike Attempt NA FightMetric
F2R5GroundStr Fighter2 Round 5 GroundStrike NA FightMetric
F2R5GroundStrAtt Fighter2 Round 5 GroundStrike Attempt NA FightMetric
F2R5HeadStr Fighter2 Round 5 HeadStrike NA FightMetric
F2R5HeadStrAtt Fighter2 Round 5 HeadStrike Attempt NA FightMetric
F2R5KD Fighter2 Round 5 Knock Down NA FightMetric
F2R5LegStr Fighter2 Round 5 LegStrike NA FightMetric
F2R5LegStrAtt Fighter2 Round 5 LegStrike Attempt NA FightMetric
F2R5Pass Fighter2 Round 5 Guard Pass NA FightMetric
F2R5Rev Fighter2 Round 5 Reversal NA FightMetric
F2R5SigStr Fighter2 Round 5 Significant Strike NA FightMetric
F2R5SigStrAtt Fighter2 Round 5 Significant Strike Attempt NA FightMetric
F2R5SigStrPercent Fighter2 Round 5 Significant Strike Percent NA FightMetric
F2R5SubAtt Fighter2 Round 5 Submission Attempt NA FightMetric
F2R5TD Fighter2 Round 5 Take Down NA FightMetric
F2R5TDAtt Fighter2 Round 5 Take Down Attempt NA FightMetric
F2R5TDPercent Fighter2 Round 5 Take Down Percent NA FightMetric
F2R5TotStr Fighter2 Round 5 Total Strike NA FightMetric
F2R5TotStrAtt Fighter2 Round 5 Total Strike Attempt NA FightMetric
F2Rank Fighter2 Rank 51 FightMatrix
F2Reach Fighter2 Reach 72 FightMetric
F2Rev Fighter2 Reversal 0 FightMetric
F2SApM Fighter2 Strikes Absorbed per Minute 2.26 FightMetric
F2SigStr Fighter2 Significant Strike 14 FightMetric
F2SigStrAtt Fighter2 Significant Strike Attempt 37 FightMetric
F2SigStrPercent Fighter2 Significant Strike Percent 0.37 FightMetric
F2SLpM Fighter2 Strikes Landed per Minute 3.12 FightMetric
F2Stance Fighter2 Stance Orthodox FightMetric
F2StrAcc Fighter2 Strike Accuracy 0.51 FightMetric
F2StrDef Fighter2 Strike Defense 0.57 FightMetric
F2SubAtt Fighter2 Submission Attempt 0 FightMetric
F2SubAttAvg Fighter2 Submission Attempt Average 0.6 FightMetric
F2TD Fighter2 Take Down 0 FightMetric
F2TDAcc Fighter2 Take Down Accuracy 0.29 FightMetric
F2TDAtt Fighter2 Take Down Attempt 4 FightMetric
F2TDAvg Fighter2 Take Down Average 3.17 FightMetric
F2TDDef Fighter2 Take Down Defense 0.57 FightMetric
F2TDPercent Fighter2 Take Down Percent 0 FightMetric
F2TotStr Fighter2 Total Strike 14 FightMetric
F2TotStrAtt Fighter2 Total Strike Attempt 37 FightMetric
F2UFCLosses Fighter2 UFCLosses 0 FightMetric
F2UFCNC Fighter2 UFCNo Contest 0 FightMetric
F2UFCWins Fighter2 UFCWins 2 FightMetric
F2Weight Fighter2 Weight 185 FightMetric
Fighter1 Fighter1 Matt Hamill FightMetric
Fighter2 Fighter2 Mark Munoz FightMetric
FightNumber FightNumber 3449 FightMetric
Gender Gender Men FightMetric
Judge1 Judge1 NA FightMetric
Judge1Score Judge1Score NA FightMetric
Judge2 Judge2 NA FightMetric
Judge2Score Judge2Score NA FightMetric
Judge3 Judge3 NA FightMetric
Judge3Score Judge3Score NA FightMetric
Method Method KO/TKO FightMetric
Ref Referee Dan Miragliotta FightMetric
Result Result Matt Hamill FightMetric
Round Round 1 FightMetric
Time Time 3:53 FightMetric
TotFightTime Total FightTime 233 FightMetric
WeightClass WeightClass Light Heavyweight FightMetric
# Creating table to explain the data.
data_table_fighters <- data.frame(colnames(fightMetric_fighters_df),
                         rep(NA,length(fightMetric_fighters_df)),
                         as.character(fightMetric_fighters_df[fightMetric_fighters_df$Name=="Gegard Mousasi",]),
                         rep("FightMetric",length(fightMetric_fighters_df)),
                         stringsAsFactors = FALSE)
# Assign column names.
colnames(data_table_fighters) <- c("Variables", "Unabreviated", "Example", "Source")
# Generate unabreviated variable names.
data_table_fighters$Unabreviated <- data_table_fighters$Variables %>%
  gsub("Str", "Strike ", .) %>%
  gsub("SL", "Strikes Landed ", .) %>%
  gsub("pM", "per Minute ", .) %>%
  gsub("Acc", "Accuracy ", .) %>%
  gsub("Def", "Defense ", .) %>%
  gsub("Avg", "Average ", .) %>%
  gsub("SA", "Strikes Absorbed", .) %>%
  gsub("Sub", "Submission ", .) %>%
  gsub("Att", "Attempt ", .) %>%
  gsub("TD", "Take Down ", .)
kable(data_table_fighters, caption = "Composition of the primary datset, [ufc_fight_details_df]", align = 'l')
Variables Unabreviated Example Source
Name Name Gegard Mousasi FightMetric
ProWins ProWins 39 FightMetric
ProLosses ProLosses 6 FightMetric
ProNC ProNC 2 FightMetric
Height Height 74 FightMetric
Weight Weight 185 FightMetric
Reach Reach 76 FightMetric
Stance Stance Orthodox FightMetric
DOB DOB 1985-08-01 FightMetric
SLpM Strikes Landed per Minute 3.56 FightMetric
StrAcc Strike Accuracy 0.5 FightMetric
SApM Strikes Absorbedper Minute 1.18 FightMetric
StrDef Strike Defense 0.69 FightMetric
TDAvg Take Down Average 1.6 FightMetric
TDAcc Take Down Accuracy 0.65 FightMetric
TDDef Take Down Defense 0.61 FightMetric
SubAttAvg Submission Attempt Average 1.2 FightMetric
Age Age 31 FightMetric
LS0tCnRpdGxlOiAiUHJlZGljdGluZyBVRkMgT3V0Y29tZXMiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgb3V0cHV0X2RpcjogL1VzZXJzL2x3MjEzNC9EZXNrdG9wL0RhdGFTY2kvVUZDCiAgICBvdXRwdXRfZmlsZTogaW5kZXguaHRtbAogICAgc21hcnQ6IHllcwogICAgdGhlbWU6IHVuaXRlZAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gRkFMU0UsIHRpZHkgPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpCmxpYnJhcnkoanNvbmxpdGUpCmxpYnJhcnkoaHR0cikKbGlicmFyeShkcGx5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShrbml0cikKbGlicmFyeShydmVzdCkKbGlicmFyeShjYVRvb2xzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShycGFydCkKbGlicmFyeShwYXJ0eSkKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkobWxvZ2l0KQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoc3VydmV5KQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoYW9kKQpgYGAKCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQojIEltcG9ydCBkYXRhc2V0cwojIHVmY19maWdodF9kZXRhaWxzX2RmCnVmY19maWdodF9kZXRhaWxzX2RmIDwtIHJlYWQuY3N2KHBhc3RlKGdldHdkKCksICIvdWZjX2ZpZ2h0X2RldGFpbHMuY3N2Iiwgc2VwID0gIiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgp1ZmNfZmlnaHRfZGV0YWlsc19kZiRYIDwtIE5VTEwKCiMgZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYKZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYgPC0gcmVhZC5jc3YocGFzdGUoZ2V0d2QoKSwgIi9maWdodE1ldHJpY19maWdodGVycy5jc3YiLCBzZXAgPSAiIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCmZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmJFggPC0gTlVMTAoKIyBmaWdodGVyX3JhbmtpbmdfZGYKZmlnaHRlcl9yYW5raW5nX2RmIDwtIHJlYWQuY3N2KHBhc3RlKGdldHdkKCksICIvZmlnaHRlcl9yYW5raW5nLmNzdiIsIHNlcCA9ICIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKZmlnaHRlcl9yYW5raW5nX2RmJFggPC0gTlVMTApgYGAKCgojIyBJbnRyb2R1Y3Rpb24KVWx0aW1hdGUgRmlnaHRpbmcgQ2hhbXBpb25zaGlwIChVRkMpIGlzIHRoZSB3b3JsZOKAmXMgcHJlbWllciBwcm9mZXNzaW9uYWwgbWl4ZWQgbWFydGlhbCBhcnRzCm9yZ2FuaXphdGlvbi4gRmlnaHRlcnMgZW5nYWdlIGluIG9uZS1vbi1vbmUgbWF0Y2hlcyBjb25zaXN0aW5nIG9mIDMtNSByb3VuZHMgb2YgNSBtaW51dGVzIGVhY2guCkZpZ2h0ZXJzIGFyZSBhbGxvd2VkIHRvIHN0cmlrZSB3aXRoIHB1bmNoZXMsIGtpY2tzLCBrbmVlcywgZWxib3dzLCBvciBlbXBsb3kgc3VibWlzc2lvbiBtb3ZlcyBzdWNoIGFzCmNob2tlcyBvciBqb2ludCBsb2Nrcy4gRmlnaHRpbmcgb2NjdXJzIHN0YW5kaW5nIHVwLCBvbiB0aGUgZ3JvdW5kLCBvciBpbiBhIGNsaW5jaCBwb3NpdGlvbi4gTXVjaCBsaWtlCm90aGVyIHByb2Zlc3Npb25hbCBzcG9ydHMsIHRoZXJlIGFyZSBhIHZhcmlldHkgb2Ygc3RydWN0dXJlZCBkYXRhIGdhdGhlcmVkIG9uIGluZGl2aWR1YWwgZmlnaHRlcnMgYW5kIG9uCmVhY2ggZmlnaHQgbWF0Y2ggdXAuIFRoaXMgcHJvamVjdCBnYXRoZXJzIG11Y2ggb2YgdGhhdCBkYXRhLCBsZWFybnMgZnJvbSBpdCwgYW5kIHRoZW4gbWFrZXMgcHJlZGljdGlvbnMKYWJvdXQgZnV0dXJlIGZpZ2h0IG1hdGNodXBzLiBTcGVjaWZpY2FsbHksIHRoaXMgcHJvamVjdCBzZWVrcyB0byBhbnN3ZXIgdGhlIHF1ZXN0aW9uOiBHaXZlbiBhIHNwZWNpZmljCm1hdGhjdXAsIHdoYXQgaXMgZWFjaCBmaWdodGVyJ3MgcHJvYmFiaWxpdHkgb2Ygd2lubmluZz8KClRoZSBwcmFjdGljYWwgYXBwbGljYXRpb24gb2YgdGhpcyBwcm9qZWN0ICh0aG91Z2ggdGhlcmUgYXJlIHBvdGVudGlhbGx5IG90aGVycykgaXMgdG8gd2FnZXIgb24gZmlnaHRzCmFuZCBtYWtlIG1vbmV5LiBJbiBmYWN0LCB0aGUgbW9kZWwncyBwcmVkaWN0aW9ucyBoYXZlIGJlZW4gYXBwbGllZCB0byByZWFsIHdvcmxkIGZ1dHVyZSBmaWdodHMgZm9yIDUgd2Vla3Mgbm93LCB0b3RhbGxpbmcgNDAgZmlnaHRzLCBhbmQgaGFzIGEgNzUlIGFjY3VyYWN5IGF0IGEgcHJvYmFiaWxpdHkgdGhyZXNob2xkIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byA1MCUuIFRoZSBhY2N1cmFjeSBpbmNyZWFzZXMgYWNjb3JkaW5nbHkgd2l0aCB0aGUgcHJvYmFiaWxpdHkgdGhyZXNob2xkLiBUaGUgd2FnZXJpbmcgdHJhY2sgcmVjb3JkIG92ZXIgdGhlc2UgcGFzdCA1IHdlZWtzIGF0IHRoZSA1MCUgdGhyZXNob2xkIGhhcyBiZWVuIH4xMjAlIGdyb3d0aC4gSW4gb3RoZXIgd29yZHMgaWYgeW91IHN0YXJ0ZWQgd2l0aCBhIHBvb2wgb2YgJDg2LDAwMCAoYW4gYW1vdW50IHRoYXQgd291bGQgbGV0IHlvdSBwdXQgJDEwSyBvbiBlYWNoIGZpZ2h0KSBvbiBTZXB0ZW1iZXIgMXN0LCB0aGVuIGJ5IE9jdG9iZXIgMm5kIHlvdSB3b3VsZCBoYXZlICQxNzUsMDAwLiBUaGUgUk9JIGlzIGRldGVybWluZWQgYnkgdGhlIG9kZHMgZm9yIHRoZSBpbmRpdml1YWwgZmlnaHRzLCBzbyBpdCBpcyBzb21ld2hhdCBpbmRlcGVuZGFudCBvZiB0aGUgb3ZlcmFsbCBwcmVkaWNpdmUgYWNjdXJhY3kuIFRoZSByZXR1cm4gaXMgYmFzZWQgb24gJDEwSyB3YWdlcnMgb24gNDAgZmlnaHRzIG92ZXIgYSA1IHdlZWsgcGVyaW9kLgoKCioqKgojIyBXcmFuZ2xpbmcKVGhleSBzYXkgZGF0YSB3cmFuZ2xpbmcgbWFrZXMgdXAgODAlLTkwJSBvZiB0aGUgdGltZSBzcGVudCBvbiBhIGRhdGEgc2NpZW5jZSBwcm9qZWN0LCBhbmQgdGhhdCBleHBlY3RhdGlvbiBjb250aW51ZWQgdG8gaG9sZCB0cnVlIGZvciB0aGlzIG9uZS4gIFRoZSBkYXRhc2V0IHVsdGltYXRlbHkgZGV2ZWxvcGVkIGJ5IGFuZCBmb3IgdGhpcyBwcm9qZWN0IGlzIG5vdCBvbmx5IGFuIGFnZ3JlZ2F0aW9uIG9mIGRhdGEgc2NyYXBlZCBmcm9tIG11bHRpcGxlIHNvdXJjZXMsIGJ1dCBpcyBhbHNvIGNvbXByaXNlZCBvZiBuZXdseSBnZW5lcmF0ZWQgZGF0YSBkZXJpdmVkIGZyb20gdGhlIHNjcmFwZWQgZGF0YS4gTmFtZWx5LCB0aGUgZGVyaXZpdGl2ZSBkYXRhIHJlZmVycyB0byBoaXN0b3JpYyBjYXJlZXIgc3RhdGlzdGljcyBmb3IgZWFjaCBmaWdodGVyIGF0IHRoZSB0aW1lIG9mIGVhY2ggZmlnaHQuCgoqKioKCiMjIyBBZ2dyZWdhdGluZyBUaGUgRGF0YQpUaGlzIHByb2plY3QgaW50aWFsbHkgZ2F0aGVyZWQgZGF0YSBmcm9tIGZvdXIgc291cmNlczogIAoKLSBbVUZDIFVub2ZmaWNpYWwgQVBJXShodHRwOi8vdWZjLWRhdGEtYXBpLnVmYy5jb20vYXBpL3YxL3VzL2ZpZ2h0ZXJzKTogbGlzdCBvZiBjdXJyZW50IFVGQyBmaWdodGVycwotIFtXaWtpcGVkaWFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpc3Rfb2ZfVUZDX2V2ZW50cyk6IGxpc3Qgb2YgVUZDIGV2ZW50cyBhbmQgZXZlbnQgaW5mb3JtYXRpb24gIAotIFtGaWdodE1ldHJpY10oaHR0cDovL3d3dy5maWdodG1ldHJpYy5jb20vKTogZGV0YWlsZWQgZmlnaHQtYnktZmlnaHQgc3RhdGlzdGljcyBhbmQgZmlnaHRlciBzdGF0aXN0aWNzIGZvciBldmVyeSBVRkMgZmlnaHQgIAotIFtGaWdodE1hdHJpeF0oaHR0cDovL3d3dy5maWdodG1hdHJpeC5jb20vKTogcHJvcHJpZXRhcnkgY3VycmVudCBhbmQgaGlzdG9yaWNhbCBmaWdodGVyIHJhbmtpbmcgc3lzdGVtCgoKSG93ZXZlciwgaW4gdGhlIGVuZCB0aGUgb25seSBzb3VyY2VzIG9mIGRhdGEgdGhhdCB3ZXJlIHVzZWQgaW4gdGhlIGZpbmFsIGRhdGFzZXQgd2VyZSBGaWdodE1ldHJpYyBhbmQgRmlnaHRNYXRyaXguICBUaGUgcHJvY2VzcyBvZiBzY3JhcGluZyBkYXRhIGdlbmVyYWxseSBmb2xsb3dlZCB0aGUgcGF0dGVybiBiZWxvdyB1c2luZyB0aGUgUnZlc3QgcGFja2FnZS4gIAoKCgoqKjEuIENvbGxlY3QgYW5kIHN0b3JlIHJlbGV2YW50IFVSTHMgaW4gYSBsaXN0LioqCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9CiMgQ29sbGVjdCBpbmRpdmlkdWFsIGZpZ2h0ZXIgdXJscy4KZm9yIChsZXR0ZXIgaW4gbGV0dGVycykgewogIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJfbGlzdF91cmwgPC0gcGFzdGUoImh0dHA6Ly93d3cuZmlnaHRtZXRyaWMuY29tL3N0YXRpc3RpY3MvZmlnaHRlcnM/Y2hhcj0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV0dGVyLCAiJnBhZ2U9YWxsIiwgc2VwID0gIiIpCiAgCiAgZmlnaHRNZXRyaWNfZmlnaHRlcl9saXN0X3BhZ2UgPC0gcmVhZF9odG1sKGZpZ2h0TWV0cmljX2ZpZ2h0ZXJfbGlzdF91cmwpCiAgCiAgIyBHZXQgaW5pZGl2aWR1YWwgRmlnaHRNZXRyaWMgRmlnaHRlciBsaW5rcyBmb3IgbGV0dGVyIG9mIGFscGhhYmV0IGZyb20gRmlnaHRlciBMaXN0IHBhZ2UuCiAgZmlnaHRlcl9saW5rc19wZXJfbGV0dGVyIDwtIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJfbGlzdF9wYWdlICU+JQogICAgaHRtbF9ub2RlcygiLmItc3RhdGlzdGljc19fdGFibGUtY29sOm50aC1jaGlsZCgxKSAuYi1saW5rX3N0eWxlX2JsYWNrIikgJT4lCiAgICBodG1sX2F0dHIoImhyZWYiKQogIAogIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJfbGlua3MgPC0gYyhmaWdodE1ldHJpY19maWdodGVyX2xpbmtzLCBmaWdodGVyX2xpbmtzX3Blcl9sZXR0ZXIpCn0KYGBgCgoKCioqMi4gTWFrZSBjb3BpZXMgb2YgSFRNTCBmcm9tIHRoZSBVUkwgbGlzdCBhbmQgc3RvcmUgaW4gYSBsaXN0LioqCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9CiMjIyMgTWFrZSBjb3BpZXMgb2YgZWFjaCBGaWdodE1ldHJpYyBmaWdodGVyIHBhZ2UgSFRNTCB0byBsYXRlciBzY3JhcGUgZGF0YSB3ZSBuZWVkLiAjIyMjCmZpZ2h0TWV0cmljX2ZpZ2h0ZXJfaHRtbCA8LSBsaXN0KCkKCmZvciAoaSBpbiAxOmxlbmd0aChmaWdodE1ldHJpY19maWdodGVyX2xpbmtzKSkgewogIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJfaHRtbFtbaV1dIDwtIHJlYWRfaHRtbChmaWdodE1ldHJpY19maWdodGVyX2xpbmtzW1tpXV0pCn0KYGBgCgoKCioqMy4gUGFyc2UgdGhlIEhUTUwgYW5kIHB1dCB3aGF0IHdlIHdhbnQgaW4gYSBkYXRhZnJhbWUuKioKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KIyMjIyBQb3B1bGF0ZSBSRVZJU0VEIEZJR0hURVIgREFUQSAjIyMjCmZvciAoaSBpbiAxOmxlbmd0aChmaWdodE1ldHJpY19maWdodGVyX2h0bWwpKSB7CiAgCiAgIyBHZXQgZmlnaHRlciBuYW1lCiAgbmFtZSA8LSBmaWdodE1ldHJpY19maWdodGVyX2h0bWxbW2ldXSAlPiUKICAgIGh0bWxfbm9kZXMoIi5iLWNvbnRlbnRfX3RpdGxlLWhpZ2hsaWdodCIpICU+JQogICAgaHRtbF90ZXh0KHRyaW0gPSBUUlVFKQogIAogIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmW2ksIDFdIDwtIG5hbWVbWzFdXQogIAogICMgR2V0IGZpZ2h0ZXIgcHJvIHJlY29yZAogIHJlY29yZCA8LSBmaWdodE1ldHJpY19maWdodGVyX2h0bWxbW2ldXSAlPiUKICAgIGh0bWxfbm9kZXMoIi5iLWNvbnRlbnRfX3RpdGxlLXJlY29yZCIpICU+JQogICAgaHRtbF90ZXh0KHRyaW0gPSBUUlVFKSAlPiUKICAgIHN0cnNwbGl0KCIgIikKICAKICByZWNvcmQgPC0gcmVjb3JkW1sxXV1bMl0gJT4lCiAgICBzdHJzcGxpdCgiLSIpCiAgCiAgZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGZbaSwgMl0gPC0gcmVjb3JkW1sxXV1bMV0KICAKICBmaWdodE1ldHJpY19maWdodGVyc19kZltpLCAzXSA8LSByZWNvcmRbWzFdXVsyXQogIAogIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmW2ksIDRdIDwtIHJlY29yZFtbMV1dWzNdCiAgCiAgIyBHZXQgZmlnaHRlciBzdGF0cwogIHN0YXRzIDwtIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJfaHRtbFtbaV1dICU+JQogICAgaHRtbF9ub2RlcygiLmItbGlzdF9fYm94LWxpc3QtaXRlbV90eXBlX2Jsb2NrIikgJT4lCiAgICBodG1sX3RleHQodHJpbSA9IFRSVUUpICU+JQogICAgZ3N1YigiXi4qOihcXG4gKikqIiwgIiIsIC4pCiAgCiAgc3RhdHMgPC0gc3RhdHNbLTEwXQogIAogIGZvciAocyBpbiAxOmxlbmd0aChzdGF0cykpIHsKICAgIGlmIChzID09ICIiKSB7CiAgICAgIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmW2ksICg0K3MpXSA8LSBOQQogICAgfSBlbHNlIHsKICAgICAgZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGZbaSwgKDQrcyldIDwtIHN0YXRzW3NdCiAgICB9CiAgfQp9CmBgYAoKCgpUaGlzIHByb2Nlc3Mgd2FzIGFkYXB0ZWQgYW5kIHJlcGVhdGVkIHRvIHB1bGwgZGF0YSBmb3IgZWFjaCBvZiB0aGUgdGhyZWUgcHJpbWFyeSBkYXRhZnJhbWVzOiAgCgotIGB1ZmNfZmlnaHRfZGV0YWlsc19kZmA6IGZpZ2h0LWJ5LWZpZ2h0IHN0YXRpc3RpY3MgIAotIGBmaWdodE1ldHJpY19maWdodGVyc19kZmA6IGluZGl2aWR1YWwgZmlnaHRlciBzdGF0aXN0aWNzICAKLSBgZmlnaGVyX3JhbmtpbmdfZGZgOiBjdXJyZW50IGFuZCBoaXN0b3JpY2FsIGZpZ2h0ZXIgcmFua2luZ3MgYnkgd2VpZ2h0Y2xhc3MKCioqKgojIyMgQ2xlYW5pbmcgVGhlIERhdGEKTXVjaCBvZiB0aGUgZGF0YSB0aGF0IHdhcyBzY3JhcGVkIGhhZCB0byBiZSBjbGVhbmVkIHRvIHNvbWUgZXh0ZW50LiAgQXQgdGhlIHZlcnkgbGVhc3QgdmFsdWVzIHdlcmUgY29udmVydGVkIGZyb20gY2hhcmFjdGVyIHRvIG51bWVyaWMsIGFuZCBpbiBvdGhlciBjYXNlcyBmaXhlcyBzdWNoIGFzIHJlbW92aW5nICclJyBzaWducywgY29udmVydGluZyBhIGZpZ2h0ZXIncyBoZWlnaHQgaW50byBudW1iZXIgb2YgaW5jaGVzLCBjb252ZXJ0aW5nIERhdGVzIG9mIGJpcnRoIGludG8gbWFjaGluZSByZWFkYWJsZSBEYXRlIGZvcm1hdCwgZXRjLi4uIHdlcmUgbmVjZXNzYXJ5LiBIZXJlIGFyZSBhIGZldyBleGFtcGxlczoKCgoKKipSZW1vdmluZyAnJScgc2lnbi4qKgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQojIFJlbW92ZSAlIHNpZ24gaW4gc2V2ZXJhbCBvZiB0aGUgY29sdW1ucyBhbmQgY29udmVydCB0byBudW1lcmljLgpmb3IgKGkgaW4gYygxMSwxMywxNSwxNikpIHsKICBmaWdodE1ldHJpY19maWdodGVyc19kZlssaV0gPC0gZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGZbLGldICU+JQogICAgZ3N1YigiJSIsICIiLCAuKSAlPiUKICAgIGFzLm51bWVyaWMoKQogIAogIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmWyxpXSA8LSBmaWdodE1ldHJpY19maWdodGVyc19kZlssaV0vMTAwCn0KYGBgCgoKCioqQ29udmVydGluZyBoZWlnaHQgaW50byBudW1iZXIgb2YgaW5jaGVzKioKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KIyBDb252ZXJ0IEhlaWdodCB0byBudW1lcmljIGluY2hlcy4KZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYkSGVpZ2h0IDwtIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmJEhlaWdodCAlPiUKICBnc3ViKCInfFwiIiwgIiIsIC4pICU+JQogIHN0cnNwbGl0KHNwbGl0ID0gIiAiKQoKZm9yIChoIGluIDE6bGVuZ3RoKGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmJEhlaWdodCkpIHsKICBpZiAoZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGZbaCwgNV1bWzFdXSA9PSAiLS0iKSB7CiAgICBmaWdodE1ldHJpY19maWdodGVyc19kZltoLCA1XVtbMV1dIDwtIE5BCiAgfSBlbHNlIHsKICAgIGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmW2gsIDVdW1sxXV0gPC0gKGFzLm51bWVyaWMoZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGZbaCwgNV1bWzFdXVsxXSkqMTIpICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGZbaCwgNV1bWzFdXVsyXSkKICB9Cn0KCmZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmJEhlaWdodCA8LSB1bmxpc3QoZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYkSGVpZ2h0KQpgYGAKCgoKVGhlcmUgd2VyZSBhbHNvIGluc3RhbmNlcyBvZiBVRkMgZmlnaHRlcnMgd2l0aCB0aGUgc2FtZSBuYW1lLCB3aGljaCB3YXMgcGFydGljdWxhcnkgdHJvdWJsZXNvbWUgd2hlbiBtZXJnaW5nIHRoaXMgZmlnaHRlciBkYXRhIHdpdGggb3RoZXIgZGF0YWZyYW1lcyBvbiB0aGUgZmlnaHRlcidzIG5hbWUuICBJdCBnYXZlIHRoZSBhcHBlYXJhbmNlIG9mIGEgZGF0YXNldCBvciBhIG1lcmdlIHRoYXQgd2FzIGVudGlyZWx5IGFjY3VyYXRlLCBidXQgZm9yIGEgZmV3IGluZXhwbGljYWJsZSBlcnJvcnMuICBTbyBhZnRlciBmdXJ0aGVyIGRpZ2dpbmcgaW50byB0aGUgZGF0YXNldHMgYW5kIGNyb3NzY2hlY2tpbmcgd2l0aCB0aGUgb3JpZ2luYWwgd2VicGFnZXMsIHRoZSBpc3N1ZSB3YXMgaGFuZGxlZCBieSBpZGVudGlmeWluZyB0aGVzZSBmaWdodGVycyBieSB0aGVpciBuYW1lIGluIGFkZGl0aW9uIHRvIGEgcGh5c2ljYWwgYXR0cmlidXRlLCBhbmQgdGhlbiByZW5hbWluZyBvbmUgb2YgdGhlbS4gIEhlcmUncyBhbiBleGFtcGxlLgoKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KIyBIYW5kbGUgaW5zdGFuY2VzIG9mIHNhbWUgbmFtZSBpbiBGSUdIVE1FVFJJQ19GSUdIVEVSU19ERgpmaWdodE1ldHJpY19maWdodGVyc19kZltmaWdodE1ldHJpY19maWdodGVyc19kZiROYW1lPT0iRG9uZyBIeXVuIEtpbSIgJiBmaWdodE1ldHJpY19maWdodGVyc19kZiRSZWFjaD09NzAsIDFdIDwtICJEb25nIEh5dW4gS2ltIChNYWVzdHJvKSIKZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGZbZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYkTmFtZT09Ik1pY2hhZWwgTWNEb25hbGQiICYgZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYkV2VpZ2h0PT0yMDUsIDFdIDwtICJNaWNoYWVsIE1jRG9uYWxkIChUaGUgQmxhY2sgU25pcGVyKSIKZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGZbZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYkTmFtZT09IlRvbnkgSm9obnNvbiIgJiBmaWdodE1ldHJpY19maWdodGVyc19kZiRXZWlnaHQ9PTE4NSwgMV0gPC0gIlRvbnkgSm9obnNvbiAoMTg1bGJzLikiCgpgYGAKCgoqKioKIyMjIFNoYXBpbmcgVGhlIERhdGEKQWZ0ZXIgYWxsIHRoZSBkYXRhIGhhZCBiZWVuIGNvbGxlY3RlZCBhbmQgc3RvcmVkIGluIGRhdGFmcmFtZXMsIHRoZXJlIHdhcyBzb21lIHJlc2hhcGluZyByZXF1aXJlZCBpbiBvcmRlciB0byBlbmQgdXAgd2l0aCBhIHNpbmdsZSBkYXRhZnJhbWUgc3VpdGFibGUgdG8gc3Vic2V0IGZyb20gYW5kIHByZXNlbnQgdG8gYSBsb2dpc3RpYyByZWdyZXNzaW9uIG9yIHJhbmRvbSBmb3Jlc3QgZnVuY3Rpb24gZm9yIG1vZGVsaW5nLiAgQXQgdGhpcyBwb2ludCB0aGVyZSB3ZXJlIHRocmVlIGRhdGFmcmFtZXMgLSBgZmlnaHRfZGV0YWlsc2AsIGBmaWdodE1ldHJpY19maWdodGVyc2AsIGFuZCBgZmlnaHRlcl9yYW5raW5nYCAtIGFuZCB0aGUgZGVzaXJlZCBlbmQgc3RhdGUgd2FzIHRvIGhhdmUgYSBzaW5nbGUgYGZpZ2h0X2RldGFpbHNgIHRoYXQgY29udGFpbmVkIG5vdCBvbmx5IGRldGFpbGVkIGZpZ2h0ZXIgc3RhdHMgZm9yIGVhY2ggZmlnaHQsIGJ1dCBhbHNvIGVhY2ggZmlnaHRlcidzIGF0dHJpYnV0ZXMsIGFuZCBlYWNoIGZpZ2h0ZXIncyByYW5raW5nLiBGdXJ0aGVybW9yZSwgYGZpZ2h0X2RldGFpbHNgIHdhcyBjdXJyZW50bHkgaW4gdGhlIGZvcm0gb2YgZWFjaCByb3cgcmVwcmVzZW50aW5nIG9uZSBmaWdodGVyJ3Mgc3RhdHMgZm9yIG9uZSBmaWdodCwgd2hpY2ggbWVhbnQgdGhlcmUgd2VyZSB0d28gcm93cyBmb3IgZWFjaCBmaWdodCAtIG9uZSBmb3IgZWFjaCBmaWdodGVyJ3Mgc3RhdHMuIFNvLCBmb3VyIHRoaW5ncyBuZWVkZWQgdG8gaGFwcGVuLi4uCgoKCioqMS4gUmVzaGFwZSBgZmlnaHRfZGV0YWlsc2AgdG8gY29udGFpbiBib3RoIGZpZ2h0ZXIncyBzdGF0aXN0aWNzIGZvciBhIHNpbmdsZSBmaWdodCBpbiBhIHNpbmdsZSByb3cuKioKYGBge3IsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KIyMjIyBSZXNoYXBlIGZpZ2h0X2RldGFpbHNfZGYgdG8gYWNjb21vZGF0ZSBsb2dpc3RpY2FsIHJlZ3Jlc3Npb24gb3IgcmFuZG9tIGZvcmVzdCAjIyMjCmNvbG5hbWVzKHVmY19maWdodF9kZXRhaWxzX2RmKVsyOjNdIDwtIGMoIkZpZ2h0ZXIxIiwgIkZpZ2h0ZXIyIikKCgojIEdyYWIgZXZlbiBudW1iZXIgcm93cyBmcm9tIGZpZ2h0X2RldGFpbHMgdG8gbGF0ZXIgYmluZCB3aXRoIG9kZCBudW1iZXIgcm93cy4KZmlnaHRlcjJfZGYgPC0gdWZjX2ZpZ2h0X2RldGFpbHNfZGYgJT4lCiAgc2xpY2Uoc2VxKDIsbnJvdyh1ZmNfZmlnaHRfZGV0YWlsc19kZiksMikpCgojIFJlbmFtZSBjb2x1bW5zIGluIGZpZ2h0ZXIyX2RmIGFuZCBvcmlnaW5hbCBmaWdodF9kZXRhaWxzX2RmLgpjb2xuYW1lcyhmaWdodGVyMl9kZilbMTc6MTYwXSA8LSBwYXN0ZSgiRjIiLCBjb2xuYW1lcyhmaWdodGVyMl9kZilbMTc6MTYwXSwgc2VwID0gIiIpCmNvbG5hbWVzKHVmY19maWdodF9kZXRhaWxzX2RmKVsxNzoxNjBdIDwtIHBhc3RlKCJGMSIsIGNvbG5hbWVzKHVmY19maWdodF9kZXRhaWxzX2RmKVsxNzoxNjBdLCBzZXAgPSAiIikKCiMgRGVsZXRlIGV2ZW4gbnVtYmVyIHJvd3MgZnJvbSBmaWdodF9kZXRhaWxzLgp1ZmNfZmlnaHRfZGV0YWlsc19kZiA8LSB1ZmNfZmlnaHRfZGV0YWlsc19kZlstc2VxKDIsbnJvdyh1ZmNfZmlnaHRfZGV0YWlsc19kZiksMiksXQoKIyBDb21iaW5lIGNvbHVtbnMgZnJvbSBmaWdodF9kZXRhaWxzIGFuZCBmaWdodGVyXzIsIHNvIGVhY2ggcm93IGhvbGRzIGVhY2ggZmlnaHRlcidzIGRhdGEgZm9yCiMgYSBnaXZlbiBmaWdodC4KdWZjX2ZpZ2h0X2RldGFpbHNfZGYgPC0gYmluZF9jb2xzKHVmY19maWdodF9kZXRhaWxzX2RmLCBmaWdodGVyMl9kZlsxNzoxNjBdKQoKIyBEZWxldGUgZmlnaHRlcl8yIGRhdGEgZnJhbWUuCnJtKGZpZ2h0ZXIyX2RmKQoKIyBUaGVuLCBSRU1PVkUgcm93cyB3aXRoIE5BIGZvciBGSUdIVEVSLgpuYV9yb3dfaW5kZXggPC0gYygpCmZvciAobiBpbiAxOm5yb3codWZjX2ZpZ2h0X2RldGFpbHNfZGYpKSB7CiAgaWYgKGlzLm5hKHVmY19maWdodF9kZXRhaWxzX2RmW24sMl0pIHwgaXMubmEodWZjX2ZpZ2h0X2RldGFpbHNfZGZbbiwzXSkpIHsKICAgIG5hX3Jvd19pbmRleCA8LSBjKG5hX3Jvd19pbmRleCwgbikKICB9Cn0KCnVmY19maWdodF9kZXRhaWxzX2RmIDwtIHVmY19maWdodF9kZXRhaWxzX2RmWy1jKG5hX3Jvd19pbmRleCksXQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwpgYGAKCgoKKioyLiBJbmNsdWRlIGEgbmV3IHZhcmlhYmxlIHRvIGluZGljYXRlIHRoZSB3aW5uZXIgLSBzaW1wbHkgYSAwIG9yIDEgdmFyaWFibGUgaW5kaWNhdGluZyBpZiBGaWdodGVyIDEgd2lucyBvciBub3QuKioKCmBgYHtyLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9CiMgTXV0YXRlIEZJR0hUX0RFVEFJTFMgdG8gZ2l2ZSBsb2dpY2FsIGZvcm0gb2YgcmVzdWx0IChGaWdodGVyMSBvciBGaWdodGVyMikgcmVhZHkgZm9yIGxvZ2lzdGljCiMgcmVncmVzc2lvbiBvciByYW5kb20gZm9yZXN0Lgp1ZmNfZmlnaHRfZGV0YWlsc19kZiA8LSB1ZmNfZmlnaHRfZGV0YWlsc19kZiAlPiUKICBtdXRhdGUoRjFXaW5zID0gYXMuY2hhcmFjdGVyKFJlc3VsdCkgPT0gYXMuY2hhcmFjdGVyKEZpZ2h0ZXIxKSkKCmZvciAoaSBpbiAxOm5yb3codWZjX2ZpZ2h0X2RldGFpbHNfZGYpKSB7CiAgaWYgKGlzLm5hKHVmY19maWdodF9kZXRhaWxzX2RmW2ksMzA1XSkpIHsKICAgIHVmY19maWdodF9kZXRhaWxzX2RmW2ksMzA1XSA8LSBOQQogIH0gZWxzZSBpZiAodWZjX2ZpZ2h0X2RldGFpbHNfZGZbaSwzMDVdID09IFRSVUUpIHsKICAgIHVmY19maWdodF9kZXRhaWxzX2RmW2ksMzA1XSA8LSAxCiAgfSBlbHNlIHsKICAgIHVmY19maWdodF9kZXRhaWxzX2RmW2ksMzA1XSA8LSAwCiAgfQp9CmBgYAoKCgoqKjMuIE1lcmdlIGBmaWdodE1ldHJpY19maWdodGVyc2Agd2l0aCBgZmlnaHRfZGV0YWlsc2Agb24gZWFjaCBmaWdodGVyJ3MgbmFtZS4qKgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQojIyMjIyBNZXJnZSBGSUdIVE1FVFJJQ19GSUdIVEVSU19ERiB3aXRoIEZJR0hUX0RFVEFJTFNfREYgIyMjIwoKIyMjIyBNZXJnZSBGSUdIVEVSU19ERiB0byBGSUdIVEVSMSBpbiBGSUdIVF9ERVRBSUxTICMjIyMKZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYgPC0gZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYgJT4lCiAgcmVuYW1lKEZpZ2h0ZXIxPU5hbWUpCgp1ZmNfZmlnaHRfZGV0YWlsc19kZiA8LSB1ZmNfZmlnaHRfZGV0YWlsc19kZiAlPiUKICBsZWZ0X2pvaW4oZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYsIGJ5ID0gIkZpZ2h0ZXIxIikKCmxlZnRfY29weV9maWdodF9kZXRhaWxzIDwtIHVmY19maWdodF9kZXRhaWxzX2RmWywxOjMwNl0KcmlnaHRfY29weV9maWdodGVyMV9kZXRhaWxzIDwtIHVmY19maWdodF9kZXRhaWxzX2RmWywzMDc6MzIzXQoKbGVmdF9jb3B5X2RvdWJsZV9yb3dzIDwtIGFzLm51bWVyaWMocm93bmFtZXMobGVmdF9jb3B5X2ZpZ2h0X2RldGFpbHNbZHVwbGljYXRlZChsZWZ0X2NvcHlfZmlnaHRfZGV0YWlscyksXSkpCgpsZWZ0X2NvcHlfZmlnaHRfZGV0YWlscyA8LSBsZWZ0X2NvcHlfZmlnaHRfZGV0YWlsc1stYyhsZWZ0X2NvcHlfZG91YmxlX3Jvd3MpLF0KcmlnaHRfY29weV9maWdodGVyMV9kZXRhaWxzIDwtIHJpZ2h0X2NvcHlfZmlnaHRlcjFfZGV0YWlsc1stYyhsZWZ0X2NvcHlfZG91YmxlX3Jvd3MpLF0KCnVmY19maWdodF9kZXRhaWxzX2RmIDwtIGxlZnRfY29weV9maWdodF9kZXRhaWxzCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyMjIyBNZXJnZSBGSUdIVEVSU19ERiB0byBGSUdIVEVSMiBpbiBGSUdIVF9ERVRBSUxTICMjIyMKZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYgPC0gZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYgJT4lCiAgcmVuYW1lKEZpZ2h0ZXIyPUZpZ2h0ZXIxKQoKdWZjX2ZpZ2h0X2RldGFpbHNfZGYgPC0gdWZjX2ZpZ2h0X2RldGFpbHNfZGYgJT4lCiAgbGVmdF9qb2luKGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmLCBieSA9ICJGaWdodGVyMiIpCgpsZWZ0X2NvcHlfZmlnaHRfZGV0YWlscyA8LSB1ZmNfZmlnaHRfZGV0YWlsc19kZlssMTozMDZdCnJpZ2h0X2NvcHlfZmlnaHRlcjJfZGV0YWlscyA8LSB1ZmNfZmlnaHRfZGV0YWlsc19kZlssMzA3OjMyM10KCmxlZnRfY29weV9kb3VibGVfcm93cyA8LSBhcy5udW1lcmljKHJvd25hbWVzKGxlZnRfY29weV9maWdodF9kZXRhaWxzW2R1cGxpY2F0ZWQobGVmdF9jb3B5X2ZpZ2h0X2RldGFpbHMpLF0pKQoKbGVmdF9jb3B5X2ZpZ2h0X2RldGFpbHMgPC0gbGVmdF9jb3B5X2ZpZ2h0X2RldGFpbHNbLWMobGVmdF9jb3B5X2RvdWJsZV9yb3dzKSxdCnJpZ2h0X2NvcHlfZmlnaHRlcjJfZGV0YWlscyA8LSByaWdodF9jb3B5X2ZpZ2h0ZXIyX2RldGFpbHNbLWMobGVmdF9jb3B5X2RvdWJsZV9yb3dzKSxdCgp1ZmNfZmlnaHRfZGV0YWlsc19kZiA8LSBsZWZ0X2NvcHlfZmlnaHRfZGV0YWlscyAlPiUKICBiaW5kX2NvbHMocmlnaHRfY29weV9maWdodGVyMV9kZXRhaWxzKSAlPiUKICBiaW5kX2NvbHMocmlnaHRfY29weV9maWdodGVyMl9kZXRhaWxzKQoKY29sbmFtZXModWZjX2ZpZ2h0X2RldGFpbHNfZGYpWzMwNzozMjNdIDwtIHBhc3RlKCJGMSIsIGNvbG5hbWVzKGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmKVsyOjE4XSwgc2VwID0gIiIpCmNvbG5hbWVzKHVmY19maWdodF9kZXRhaWxzX2RmKVszMjQ6MzQwXSA8LSBwYXN0ZSgiRjIiLCBjb2xuYW1lcyhmaWdodE1ldHJpY19maWdodGVyc19kZilbMjoxOF0sIHNlcCA9ICIiKQoKZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYgPC0gZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYgJT4lCiAgcmVuYW1lKE5hbWU9RmlnaHRlcjIpCiMjIyMjIyMjIyMjIyMjIyMjIyMKYGBgCgoKCioqNC4gTWVyZ2UgYGZpZ2h0ZXJfcmFua2luZ2Agd2l0aCBgZmlnaHRfZGV0YWlsc2Agb24gZWFjaCBmaWdodGVyJ3MgbmFtZS4qKgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQojIyMjIyMjIyMjIyMjIyMjIyMjIEFzc2lnbiBGSUdIVEVSX1JBTktJTkcgdG8gZmlnaHRlcnMgaW4gTkVXX0ZJR0hUUyAjIyMjIyMjIyMjIyMjIyMjIwoKIyBSZXNldCBjbGVhbiB1bmlxdWUgRmlnaHROdW1iZXJzIApuZXdfZmlnaHRzX2RmJEZpZ2h0TnVtYmVyIDwtIGMoMTpucm93KG5ld19maWdodHNfZGYpKQoKbmV3X2ZpZ2h0c19kZiA8LSBuZXdfZmlnaHRzX2RmICU+JQogIG11dGF0ZShGMVJhbmsgPSAyNTEsIEYyUmFuayA9IDI1MSkKCkYxUmFua0NvbCA8LSBncmVwKCJGMVJhbmsiLCBjb2xuYW1lcyhuZXdfZmlnaHRzX2RmKSkKRjJSYW5rQ29sIDwtIGdyZXAoIkYyUmFuayIsIGNvbG5hbWVzKG5ld19maWdodHNfZGYpKQoKcXVhcnRlcmx5X2RhdGUgPC0gYXMuRGF0ZSgiMjAwNi0wMS0wMSIsICIlWS0lbS0lZCIpCmZvciAocSBpbiAxOihsZW5ndGgoZmlnaHRlcl9yYW5raW5nX2RmKS05KSkgewogIHRlbXBfZmlnaHRlcl9yYW5raW5nIDwtIGZpZ2h0ZXJfcmFua2luZ19kZiAlPiUKICAgIHNlbGVjdChjKHEsMzUzKSkKICAKICBxdWFydGVybHlfZmlnaHRfZGV0YWlscyA8LSBuZXdfZmlnaHRzX2RmICU+JQogICAgZmlsdGVyKHF1YXJ0ZXIoRGF0ZSwgd2l0aF95ZWFyID0gVFJVRSkgPT0gcXVhcnRlcihxdWFydGVybHlfZGF0ZSwgd2l0aF95ZWFyID0gVFJVRSkpICU+JQogICAgc2VsZWN0KEZpZ2h0TnVtYmVyLCBEYXRlLCBGaWdodGVyMSwgRmlnaHRlcjIsIEYxUmFuaywgRjJSYW5rKQogIAogIGZvciAoRjEgaW4gMTpucm93KHF1YXJ0ZXJseV9maWdodF9kZXRhaWxzKSkgewogICAgaWYgKHRvbG93ZXIocXVhcnRlcmx5X2ZpZ2h0X2RldGFpbHNbRjEsIDNdKSAlaW4lIHRvbG93ZXIobmEub21pdCh0ZW1wX2ZpZ2h0ZXJfcmFua2luZ1ssMV0pKSkgewogICAgICBxdWFydGVybHlfZmlnaHRfZGV0YWlsc1tGMSw1XSA8LSB0ZW1wX2ZpZ2h0ZXJfcmFua2luZ1t0b2xvd2VyKG5hLm9taXQodGVtcF9maWdodGVyX3JhbmtpbmdbLDFdKSkgPT0gdG9sb3dlcihxdWFydGVybHlfZmlnaHRfZGV0YWlsc1tGMSwzXSksIDJdWzFdCiAgICB9CiAgICBpZiAodG9sb3dlcihxdWFydGVybHlfZmlnaHRfZGV0YWlsc1tGMSwgNF0pICVpbiUgdG9sb3dlcihuYS5vbWl0KHRlbXBfZmlnaHRlcl9yYW5raW5nWywxXSkpKSB7CiAgICAgIHF1YXJ0ZXJseV9maWdodF9kZXRhaWxzW0YxLDZdIDwtIHRlbXBfZmlnaHRlcl9yYW5raW5nW3RvbG93ZXIobmEub21pdCh0ZW1wX2ZpZ2h0ZXJfcmFua2luZ1ssMV0pKSA9PSB0b2xvd2VyKHF1YXJ0ZXJseV9maWdodF9kZXRhaWxzW0YxLDRdKSwgMl1bMV0KICAgIH0KICB9CiAgCiAgZm9yIChpIGluIDE6bnJvdyhxdWFydGVybHlfZmlnaHRfZGV0YWlscykpIHsKICAgIG5ld19maWdodHNfZGZbbmV3X2ZpZ2h0c19kZiRGaWdodE51bWJlciA9PSBxdWFydGVybHlfZmlnaHRfZGV0YWlsc1tpLDFdLCBjKEYxUmFua0NvbCwgRjJSYW5rQ29sKV0gPC0gcXVhcnRlcmx5X2ZpZ2h0X2RldGFpbHNbaSwgNTo2XQogIH0KICAKICBxdWFydGVybHlfZGF0ZSA8LSBxdWFydGVybHlfZGF0ZSArIG1vbnRocygzKQogIGlmIChxdWFydGVyKHF1YXJ0ZXJseV9kYXRlLCB3aXRoX3llYXIgPSBUUlVFKSA+IHF1YXJ0ZXIodG9kYXkoKSwgd2l0aF95ZWFyID0gVFJVRSkpIHsKICAgIHF1YXJ0ZXJseV9kYXRlIDwtIGFzLkRhdGUoIjIwMDYtMDEtMDEiLCAiJVktJW0tJWQiKQogIH0KfQoKbmV3X2ZpZ2h0c19kZiRGaWdodE51bWJlciA8LSBjKDE6bnJvdyhuZXdfZmlnaHRzX2RmKSkKCm5ld19maWdodHNfZGYkRjFSYW5rIDwtIGFzLm51bWVyaWMobmV3X2ZpZ2h0c19kZiRGMVJhbmspCm5ld19maWdodHNfZGYkRjJSYW5rIDwtIGFzLm51bWVyaWMobmV3X2ZpZ2h0c19kZiRGMlJhbmspCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKYGBgCgoKKioqCiMjIyBGZWF0dXJlIEVuZ2luZWVyaW5nCkFzIG1lbnRpb25lZCBlYXJsaWVyLCB0aGUgZGF0YXNldCBmb3IgdGhpcyBwcm9qZWN0IGlzIGEgY29tYmluYXRpb24gb2YgZGF0YSBzY3JhcGVkIGZyb20gc2V2ZXJhbCBzb3VyY2VzLCBhcyB3ZWxsIGFzIGRhdGEgZGVyaXZlZCAob3IgbmV3bHkgY3JlYXRlZCkgZnJvbSB0aGUgc2NyYXBlZCBkYXRhLiAgU3BlY2lmaWNhbGx5LCB0aGUgZGVyaXZlZCBkYXRhIGluY2x1ZGVzIGNhcmVlciBzdGF0aXN0aWNzIGZvciBlYWNoIGZpZ2h0ZXIgYXQgdGhlIHRpbWUgb2YgZWFjaCBmaWdodC4gIFRoaXMgZGF0YSB3YXMgbm90IGF2YWlsYWJsZSBhbnl3aGVyZSBvbmxpbmUgKHRvIG15IGtub3dsZWRnZSksIGJ1dCB3YXMgY3JpdGljYWwgaW4gZGV2ZWxvcGluZyBhbiBhY2N1cmF0ZSBkZXNjcmlwdGlvbiBvZiBlYWNoIGZpZ2h0ZXIgYXQgdGhlIHRpbWUgb2YgZWFjaCBmaWdodCBpbiB3aGljaCB0byBidWlsZCBhIG1vZGVsIHRoYXQgcHJlZGljdHMgZnV0dXJlIG91dGNvbWVzLiAgU28sIHRoZSBiYXNpYyBhcHByb2FjaCB0byBnZW5lcmF0aW5nIHRoaXMgbmV3IGRhdGEgd2FzIHRvIHNvcnQgdGhyb3VnaCB0aGUgZW50aXJlIGZpZ2h0IGRhdGFzZXQgKGB1ZmNfZmlnaHRfZGV0YWlsc19kZmApIG9uZSBmaWdodGVyIGF0IGEgdGltZSBpbiBjaHJvbm9sb2dpY2FsbHkgdGFsbHkgdXAgdGhlaXIgY2FyZWVyIHN0YXRzLiBCZWxvdyBpcyBhbiBleGFtcGxlIG9mIHNvbWUgb2YgdGhlIGNvZGUsIHdoaWNoIGNhbiBhbG1vc3QgY2VydGFpbmx5IGJlIHJlZmFjdG9yZWQgaW50byBzb21ldGhpbmcgbW9yZSBlY29ub21pY2FsLCBidXQgZm9yIHRoZSB0aW1lLWJlaW5nIGlzIG1hZGUgdXAgb2YgYWJvdXQgMjAwIGxpbmVzIG9mIG5lc3RlZCBgZm9yYCBsb29wcyBhbmQgYGlmYCBgZWxzZWAgc3RhdGVtZW50cy4KCioqNC4gRXhhbXBsZSBibG9jayBvZiBjb2RlIHRvIGdlbmVyYXRlIGRlcml2aXRpdmUgZGF0YS4qKgpgYGB7ciwgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQojIEFkZCBjb2x1bW5zIHRvIGJlIHBvcHVsYXRlZCB0byBGSUdIVF9ERVRBSUxTCnVmY19maWdodF9kZXRhaWxzX2RmIDwtIHVmY19maWdodF9kZXRhaWxzX2RmICU+JQogIG11dGF0ZShGMUNhckZpZ2h0VGltZSA9IE5BLCBGMkNhckZpZ2h0VGltZSA9IE5BLCBGMVVGQ1dpbnMgPSBOQSwgRjJVRkNXaW5zID0gTkEsIEYxVUZDTG9zc2VzID0gTkEsCiAgICAgICAgIEYyVUZDTG9zc2VzID0gTkEsIEYxVUZDTkMgPSBOQSwgRjJVRkNOQyA9IE5BLCBGMUNhclNpZ1N0ciA9IE5BLCBGMkNhclNpZ1N0ciA9IE5BLAogICAgICAgICBGMUNhclNpZ1N0ckF0dCA9IE5BLCBGMkNhclNpZ1N0ckF0dCA9IE5BLCBGMUNhclNpZ1N0ckFicyA9IE5BLCBGMkNhclNpZ1N0ckFicyA9IE5BLAogICAgICAgICBGMUNhclREID0gTkEsIEYyQ2FyVEQgPSBOQSwgRjFDYXJUREF0dCA9IE5BLCBGMkNhclREQXR0ID0gTkEsIEYxQ2FyVERBYnMgPSBOQSwgRjJDYXJUREFicyA9IE5BLAogICAgICAgICBGMUNhck9wcFNpZ1N0ckF0dCA9IE5BLCBGMkNhck9wcFNpZ1N0ckF0dCA9IE5BLCBGMUNhck9wcFREQXR0ID0gTkEsIEYyQ2FyT3BwVERBdHQgPSBOQSkKCiMjIyMgQmVnaW4gbG9vcCB0byBnZW5lcmF0ZSBhZGRpdGlvbmFsIGZpZ2h0ZXIgZGF0YSAjIyMjCmZvciAoZmlnaHRlciBpbiB1bmlxdWVfZmlnaHRlcnMkLikgewogIGZpZ2h0ZXJfY2FyZWVyX2RhdGEgPC0gdWZjX2ZpZ2h0X2RldGFpbHNfZGYgJT4lCiAgICBmaWx0ZXIoKEZpZ2h0ZXIxID09IGFzLmNoYXJhY3RlcihmaWdodGVyKSkgfCAoRmlnaHRlcjIgPT0gYXMuY2hhcmFjdGVyKGZpZ2h0ZXIpKSkgJT4lCiAgICBhcnJhbmdlKERhdGUpCiAgCiAgZm9yIChpIGluIDE6bnJvdyhmaWdodGVyX2NhcmVlcl9kYXRhKSkgewogICAgCiAgIyMjIyBBZGRpbmcgdXAgdG90YWwgY2FyZWVyIHRpbWUgaW4gcmluZyBmb3IgZWFjaCBmaWdodGVyIGdvaW5nIGludG8gZWFjaCBmaWdodCAtIEZpZ2h0ZXIxIHNwb3QuICMjIyMKICAgIGlmIChmaWdodGVyID09IGZpZ2h0ZXJfY2FyZWVyX2RhdGFbaSwyXSkgewogICAgICAKICAgICAgaWYgKGkgPT0gMSkgewogICAgICAgICMgRjFDYXJGaWdodFRpbWUgd2hlbiBGMQogICAgICAgIGZpZ2h0ZXJfY2FyZWVyX2RhdGFbaSwgMzQyXSA8LSAwCiAgICAgICAgCiAgICAgICAgIyBGMUNhclNpZ1N0ciB3aGVuIEYxCiAgICAgICAgZmlnaHRlcl9jYXJlZXJfZGF0YVtpLCAzNTBdIDwtIDAKICAgICAgICAKICAgICAgICAjIEYxQ2FyU2lnU3RyQXR0IHdoZW4gRjEKICAgICAgICBmaWdodGVyX2NhcmVlcl9kYXRhW2ksIDM1Ml0gPC0gMAogICAgICAgIAogICAgICAgICMgRjFDYXJTaWdTdHJBYnMgd2hlbiBGMQogICAgICAgIGZpZ2h0ZXJfY2FyZWVyX2RhdGFbaSwgMzU0XSA8LSAwCiAgICAgICAgCiAgICAgICAgIyBGMUNhclREIHdoZW4gRjEKICAgICAgICBmaWdodGVyX2NhcmVlcl9kYXRhW2ksIDM1Nl0gPC0gMAogICAgICAgIAogICAgICAgICMgRjFDYXJUREF0dCB3aGVuIEYxCiAgICAgICAgZmlnaHRlcl9jYXJlZXJfZGF0YVtpLCAzNThdIDwtIDAKICAgICAgICAKICAgICAgICAjIEYxQ2FyVERBYnMgd2hlbiBGMQogICAgICAgIGZpZ2h0ZXJfY2FyZWVyX2RhdGFbaSwgMzYwXSA8LSAwCiAgICAgICAgCiAgICAgICAgIyBGMUNhck9wcFNpZ1N0ckF0dCB3aGVuIEYxCiAgICAgICAgZmlnaHRlcl9jYXJlZXJfZGF0YVtpLCAzNjJdIDwtIDAKICAgICAgICAKICAgICAgICAjIEYxQ2FyT3BwVERBdHQgd2hlbiBGMQogICAgICAgIGZpZ2h0ZXJfY2FyZWVyX2RhdGFbaSwgMzY0XSA8LSAwCiAgICAgIH0gZWxzZSB7CiAgICAgICAgIyBGMSB3YXMgRjEgbGFzdCBmaWdodAogICAgICAgIGlmIChmaWdodGVyID09IGZpZ2h0ZXJfY2FyZWVyX2RhdGFbKGktMSksMl0pIHsKICAgICAgICAgCiAgICAgICAgICAjIEYxQ2FyRmlnaHRUaW1lIHdoZW4gRjEgdGhpcyBmaWdodCB3YXMgRjEgbGFzdCBmaWdodAogICAgICAgICAgZmlnaHRlcl9jYXJlZXJfZGF0YVtpLCAzNDJdIDwtIGZpZ2h0ZXJfY2FyZWVyX2RhdGFbKGktMSksIDM0Ml0gKyBmaWdodGVyX2NhcmVlcl9kYXRhWyhpLTEpLCAzNDFdCiAgICAgICAgICAKICAgICAgICAgICMgR2V0IGZpZ2h0IGRhdGEgZnJvbSBsYXN0IGZpZ2h0IHdoZW4gRjEgdGhpcyBmaWdodCB3YXMgRjEgbGFzdCBmaWdodC4KICAgICAgICAgIHRlbXBfZmlnaHRfZGF0YSA8LSBjKGZpZ2h0ZXJfY2FyZWVyX2RhdGFbKGktMSksIDE4XSwgZmlnaHRlcl9jYXJlZXJfZGF0YVsoaS0xKSwgMTldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlnaHRlcl9jYXJlZXJfZGF0YVsoaS0xKSwgMTYyXSwgZmlnaHRlcl9jYXJlZXJfZGF0YVsoaS0xKSwgMjNdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlnaHRlcl9jYXJlZXJfZGF0YVsoaS0xKSwgMjRdLCBmaWdodGVyX2NhcmVlcl9kYXRhWyhpLTEpLCAxNjddLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlnaHRlcl9jYXJlZXJfZGF0YVsoaS0xKSwgMTYzXSwgZmlnaHRlcl9jYXJlZXJfZGF0YVsoaS0xKSwgMTY4XSkKICAgICAgICAgIAogICAgICAgICAgIyBGMUNhclNpZ1N0ciB0aHJvdWdoIEYxQ2FyT3BwVERBdHQgd2hlbiBGMSB0aGlzIGZpZ2h0IHdhcyBGMSBsYXN0IGZpZ2h0CiAgICAgICAgICBmb3IgKG4gaW4gMTo4KSB7CiAgICAgICAgICAgIGlmIChpcy5uYSh0ZW1wX2ZpZ2h0X2RhdGFbbl0pKSB7CiAgICAgICAgICAgICAgZmlnaHRlcl9jYXJlZXJfZGF0YVtpLCAoKDE3NCtuKSoyKV0gPC0gMAogICAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICAgIGZpZ2h0ZXJfY2FyZWVyX2RhdGFbaSwgKCgxNzQrbikqMildIDwtIGZpZ2h0ZXJfY2FyZWVyX2RhdGFbKGktMSksICgoMTc0K24pKjIpXSArIHRlbXBfZmlnaHRfZGF0YVtuXQogICAgICAgICAgICB9CiAgICAgICAgICB9CiAgICAgICAgfQpgYGAKCgpTb21lIHdpZGRsaW5nIGRvd24gb2YgdGhlIGZlYXR1cmVzIG9jY3VyZWQgaW4gZ29pbmcgZnJvbSB0aGUgMzgyIGZlYXR1cmVzIG9mIGB1ZmNfZmlnaHRfZGV0YWlsc19kZmAgdG8gdGhlIHN1YnNldCBvZiA0OCBmZWF0dXJlcyB1bHRpbWF0ZWx5IHVzZWQgaW4gdGhlIGxvZ2lzaXRpYyByZWdyZXNzaW9uIG1vZGVsLiAgVGhpcyBwcm9jZXNzIHdhcyBwYXJ0IGxvZ2ljLCBwYXJ0IGRvbWFpbiBleHBlcnRpc2UuIFBlcmhhcHMgbW9yZSBvbiB0aGUgbG9naWMgc2lkZSB3ZXJlIGZlYXR1cmVzIG9mIHBoeXNpY2FsIGRlc2NyaXB0aW9uLCBsaWtlIGFnZSwgaGVpZ2h0LCB3ZWlnaHQsIHJlYWNoLCB3ZWlnaHQgY2xhc3MsIGFuZCBzdGFuY2UuIENhcmVlciBzdGF0aXN0aWNzIGZlYXR1cmVzIGFyZSB0aGUgcHJvZHVjdCBvZiBib3RoIGxvZ2ljIGFuZCBkb21haW4gZXhwZXJ0aXNlLiAgWW91IGRvbid0IG5lZWQgdG8ga25vdyBhbnl0aGluZyBhYm91dCBNaXhlZCBNYXJ0aWFsIEFydHMgb3IgVUZDIHRvIHRoaW5rIGl0J3MgYSBnb29kIGlkZWEgdG8gY29uc2lkZXIgZWFjaCBmaWdodGVyJ3MgY2FyZWVyIHN0YXRpc3RpY3Mgd2hlbiBtYWtpbmcgYSBwcmVkaWN0aW9uLCBidXQsIGZvciBleGFtcGxlLCBkYXRhIGZvciByb3VuZC1ieS1yb3VuZCBzdGF0aXN0aWNzIHdlcmUgbm90IGluY2x1ZGVkIGJlY2F1c2Ugbm90IGFsbCBmaWdodHMgaGF2ZSB0aGUgc2FtZSBudW1iZXIgb2Ygcm91bmRzLCBhbmQgdGhlcmUgd291bGQgaGF2ZSBiZWVuIGEgY29uc2lkZXJhYmxlIGFtb3VudCBvZiBhZGRpdGlvbmFsIGRhdGEgd3JhbmdsaW5nIHRvIHByZXNlbnQgdGhpcyBkYXRhIHRvIHRoZSBtb2RlbCBpbiBhIHVzZWZ1bCB3YXkuICBUaGUgZGVjaXNpb24gdG8gaW5jbHVkZSBlYWNoIGZpZ2h0ZXIncyByYW5rIG1pZ2h0IGFsc28gc2VlbSBuYXR1cmFsLCBidXQgb24gdGhlIG90aGVyIGhhbmQgYW4gZXhjZWxsZW50IGRhdGEgc2NpZW50aXN0IHdpdGhvdXQgYXMgbXVjaCBkb21haW4gZXhwZXJ0aXNlIG1pZ2h0IHRoaW5rIHRoYXQgc3RhdGlzdGljYWwgZGVzY3JpcHRpb25zIG9mIGVhY2ggZmlnaHRlcidzIHBlcmZvcm1hbmNlIHNob3VsZCBiZSBlbm91Z2gsIG9yIGV2ZW4gc3VwZXJpb3IgdGhhbiBhIHNvbWV3aGF0IHN1YmplY3RpdmUgb3IgY2lyY3Vtc3RhbnRpYWwgaW5kaWNhdG9yIGxpa2UgcmFuay4gQnV0IHRoZSByYW5raW5nIHZhcmlhYmxlIHRha2VzIGludG8gY29uc2lkZXJhdGlvbiBmYWN0b3JzIHN1Y2ggYXM6IHRoZSBxdWFsaXR5IG9mIGEgZmlnaHRlcidzIHBhc3Qgb3Bwb25lbmVudHMsIGFuZCB0aGUgY2hyb25vbG9neSBvZiBwYXN0IHBlcmZvcm1hbmNlIChlLmcuIGEgZmlnaHRlciB3aXRoIGEgbG9uZyBzdWNjZXNzZnVsIGNhcmVlciB0aGF0IGlzIG5vdyBmaWdodGluZyBwYXN0IGhpcyBwcmltZSkuCgoqKioKPCEtLSAjIyMgTWFpbnRhaW5pbmcgQSBEeW5hbWljIERhdGFzZXQgLS0+CjwhLS0gQWZ0ZXIgY3JlYXRpbmcgdGhlIGluaXRhbCBmb3VuZGF0aW9uYWwgZGF0YXNldCBhbmQgYnVpbGRpbmcgdGhlIG1vZGVsLCB0aGUgd29yayBpcyBub3QgZmluaXNoZWQgaWYgaXQncyBpbnRlbmRlZCB0byBtYWtlIHByZWRpY3Rpb25zIG9uIGEgcmVndWxhciBiYXNpcy4gIFVGQyBob2xkcyBldmVudHMgYWxtb3N0IGV2ZXJ5IHdlZWssIHNvIGZvciB0aGlzIHByb2plY3QgdG8gYmUgcHJhY3RpY2FsIHRoZSBkYXRhc2V0IG11c3QgYmUgdXBkYXRlZCBhZnRlciBldmVyeSBldmVudCwgd2hpY2ggaXMganVzdCBhYm91dCBldmVyeSB3ZWVrLiAtLT4KCjwhLS0gKioqIC0tPgojIyBQcmVkaWN0aW9uIE1ldGhvZHMKQWZ0ZXIgY29sbGVjdGluZyBhbmQgd3JhbmdsaW5nIHRoZSBkYXRhLCB3ZSBoYXZlIGEgZGF0YXNldCBvZiBvdmVyIDUsMDAwIE1NQSBmaWdodHMgcmVhZHkgdG8gdHJhaW4gYSBzdGF0aXN0aWNhbCBtb2RlbCB0byBtYWtlIGEgcHJlZGljdGlvbnMuICBHaXZlbiB3ZSdyZSB0cnlpbmcgdG8gcHJlZGljdCBhIGJpbmFyeSBvdXRjb21lIC0gV2luIG9yIExvc2UgLSBmb3IgZWFjaCBmaWdodGVyIGluIGEgZ2l2ZW4gZmlnaHQsIHRoZXJlIGFyZSBhIGZldyBtZXRob2RzIHRoYXQgd291bGQgYmUgc3VpdGFibGUuICBGb3IgdGhpcyBwcm9qZWN0IEkgdGVzdGVkIHR3byBkaWZmZXJlbnQgbWV0aG9kcyAtIExvZ2lzdGljIFJlZ3Jlc3Npb24gYW5kIFJhbmRvbSBGb3Jlc3QsIGFuZCBlbmRlZCB1cCBnb2luZyB3aXRoIExvZ2lzdGljIFJlZ3Jlc3Npb24gYmVjYXVzZSBpdCBwZXJmb3JtZWQgYmV0dGVyIG9uIHRlc3QgZGF0YS4KCioqKgojIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKKioqCiMjIyMgVGVzdGluZwoKYGBge3J9CiMgVmFyaWFibGUgZm9yIHRoZSBkYXRlIHRlbiB5ZWFycyBhZ28gdG9kYXkuCnRlbl95ZWFyc19hZ28gPC0geW1kKHRvZGF5KCkpIC0geWVhcnMoMTApCgojIFRoaXMgZ2l2ZXMgdXMgdGhlIHN1YnNldCBvZiBmaWdodHMgKHJvd3MpIHdlJ2xsIHVzZSBmb3IgdGhlIG1vZGVsLgpyb3dfc3Vic2V0X2ZpZ2h0cyA8LSB1ZmNfZmlnaHRfZGV0YWlsc19kZiAlPiUKICBmaWx0ZXIoRGF0ZSA+IHRlbl95ZWFyc19hZ28pICU+JQogIGZpbHRlcihHZW5kZXIgPT0gIk1lbiIpICU+JQogIGZpbHRlcihXZWlnaHRDbGFzcyAhPSAiU3RyYXd3ZWlnaHQiKSAlPiUKICBmaWx0ZXIoV2VpZ2h0Q2xhc3MgIT0gIkF0b213ZWlnaHQiKSAlPiUKICBmaWx0ZXIoV2VpZ2h0Q2xhc3MgIT0gIkNhdGNoIFdlaWdodCIpICU+JQogIGZpbHRlcihXZWlnaHRDbGFzcyAhPSAiT3BlbiBXZWlnaHQiKSAlPiUKICBmaWx0ZXIoV2VpZ2h0Q2xhc3MgIT0gIk90aGVyIikgJT4lCiAgZmlsdGVyKFdlaWdodENsYXNzICE9ICJTdXBlciBIZWF2eXdlaWdodCIpICU+JQogIGZpbHRlcigoRjFVRkNXaW5zPjAgfCBGMVVGQ0xvc3Nlcz4wIHwgRjFVRkNOQz4wKSAmIChGMlVGQ1dpbnM+MCB8IEYyVUZDTG9zc2VzPjAgfCBGMlVGQ05DPjApKSAlPiUKICBhcnJhbmdlKERhdGUpCgoKIyBTdWJzZXQgb2YgY29sdW1ucyB0byBiZSB1c2VkIGluIHRoZSBtb2RlbCBzdWJzZXQuCm1vZGVsX2RhdGFzZXRfY29sdW1ucyA8LSByZWFkLmNzdihwYXN0ZShnZXR3ZCgpLCAiL0ZyZXNoTW9kZWxDb2x1bW5zLmNzdiIsIHNlcCA9ICIiKSkKbW9kZWxfZGF0YXNldF9jb2x1bW5zIDwtIGFzLmNoYXJhY3Rlcihtb2RlbF9kYXRhc2V0X2NvbHVtbnMkQ29sdW1uTmFtZXMpCgojIEdldCBzdWJzZXQgZmlnaHRfZGV0YWlscyBkYXRhc2V0IC0gYWxsIHJvd3M7IG9ubHkgY29sdW1ucyBmcm9tIHNlbGVjdGVkX2ZlYXR1cmVzCmZyZXNoX21vZGVsX2RhdGFzZXQgPC0gcm93X3N1YnNldF9maWdodHMgJT4lCiAgc3Vic2V0KHNlbGVjdCA9IG1vZGVsX2RhdGFzZXRfY29sdW1ucykKCiMgRmFjdG9yIFdlaWdodGNsYXNzLgpmcmVzaF9tb2RlbF9kYXRhc2V0JFdlaWdodENsYXNzIDwtIGFzLmZhY3RvcihmcmVzaF9tb2RlbF9kYXRhc2V0JFdlaWdodENsYXNzKQoKIyBGYWN0b3IgRjFTdGFuY2UKZnJlc2hfbW9kZWxfZGF0YXNldCRGMVN0YW5jZSA8LSBmcmVzaF9tb2RlbF9kYXRhc2V0JEYxU3RhbmNlICU+JQogIGFzLmZhY3RvcigpCgojIEZhY3RvciBGMlN0YW5jZQpmcmVzaF9tb2RlbF9kYXRhc2V0JEYyU3RhbmNlIDwtIGZyZXNoX21vZGVsX2RhdGFzZXQkRjJTdGFuY2UgJT4lCiAgYXMuZmFjdG9yKCkKCiMjIyMjIyMjIyMhISEhISEhISEgU1VCU0VUVElORyAhISEhISEhISEhIyMjIyMjIyMjIyMKIyBTcGxpdCBmaWdodF9kZXRhaWxzX2RmIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cy4KCnNldC5zZWVkKDEwOSkgCgpzcGxpdF91ZmNfZGF0YSA8LSBmcmVzaF9tb2RlbF9kYXRhc2V0JEYxV2lucyAlPiUKICBzYW1wbGUuc3BsaXQoU3BsaXRSYXRpbyA9IDAuNzApCgp0cmFpbl91ZmNfc3Vic2V0IDwtIGZyZXNoX21vZGVsX2RhdGFzZXQgJT4lCiAgc3Vic2V0KHNwbGl0X3VmY19kYXRhID09IFRSVUUpCgp0ZXN0X3VmY19zdWJzZXQgPC0gZnJlc2hfbW9kZWxfZGF0YXNldCAlPiUKICBzdWJzZXQoc3BsaXRfdWZjX2RhdGEgPT0gRkFMU0UpCgoKIyBSZW1vdmUgYW5kIHNhdmUgY29sdW1ucyB3ZSBkb24ndCBuZWVkLCBidXQgbWF5IG5lZWQgbGF0ZXIuLi4ganVzdCBpbiBjYXNlLgpzYXZlZF9jb2x1bW5fbmFtZXMgPC0gYygiRmlnaHROdW1iZXIiLCJGaWdodGVyMSIsIkZpZ2h0ZXIyIiwiRGF0ZSIpCgpjb2x1bW5fcG9zIDwtIGMoKQpmb3IgKGNvbHVtbiBpbiBzYXZlZF9jb2x1bW5fbmFtZXMpIHsKICBjb2x1bW5fcG9zIDwtIGMoY29sdW1uX3BvcywgZ3JlcChjb2x1bW4sIGNvbG5hbWVzKGZyZXNoX21vZGVsX2RhdGFzZXQpKSkKfQoKc2F2ZWRfY29sdW1ucyA8LSBmcmVzaF9tb2RlbF9kYXRhc2V0ICU+JQogIHNlbGVjdChjb2x1bW5fcG9zKQoKdHJhaW5fdWZjX3N1YnNldCA8LSB0cmFpbl91ZmNfc3Vic2V0ICU+JSBzZWxlY3QoLWNvbHVtbl9wb3MpCnRlc3RfdWZjX3N1YnNldCA8LSB0ZXN0X3VmY19zdWJzZXQgJT4lIHNlbGVjdCgtY29sdW1uX3BvcykKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMjIyMjIExPR0lUIE1PREVMICMjIyMjIyMjIyMjIyMjCgp1ZmNfZnJlc2hfbG9naXRfbW9kZWwgPC0gZ2xtKEYxV2lucyB+IC4sIGRhdGEgPSBuYS5vbWl0KHRyYWluX3VmY19zdWJzZXQpLCBmYW1pbHkgPSBiaW5vbWlhbCkKCiMgUHJlZGljdGlvbiAtIHByb2JhYmlsaXR5IHRoYXQgRjEgd2lucwp0ZXN0X3VmY19zdWJzZXQkRjFXaW5zUHJvYiA8LSByb3VuZChwcmVkaWN0KHVmY19mcmVzaF9sb2dpdF9tb2RlbCwgbmV3ZGF0YSA9IHRlc3RfdWZjX3N1YnNldCwgdHlwZSA9ICJyZXNwb25zZSIpLCA0KQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMjIyMjIyBUSFJFU0hPTEQgTU9ERUxTICMjIyMjIyMjIyMjIyMKV2luVGhyZXNob2xkIDwtIDAuNQoKIyBGaWx0ZXIgb3V0IGZpZ2h0cyB0byBnZXQgb25seSB0aG9zZSB3aGVyZSBGMVdpbnNQcm9iID4gMC42IG9yIDwgMC40CnRlc3RfdGhyZXNob2xkX3N1YnNldCA8LSB0ZXN0X3VmY19zdWJzZXQgJT4lCiAgZmlsdGVyKEYxV2luc1Byb2IgPiBXaW5UaHJlc2hvbGQgfCBGMVdpbnNQcm9iIDwgKDEtV2luVGhyZXNob2xkKSkKCnRlc3RfdGhyZXNob2xkX3N1YnNldCRQcmVkRjFXaW5zIDwtIGFzLm51bWVyaWModGVzdF90aHJlc2hvbGRfc3Vic2V0JEYxV2luc1Byb2IgPiBXaW5UaHJlc2hvbGQpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIyMjIyMjIExPR0lUIENPTkZVU0lPTiBNQVRSSVggIyMjIyMjIwoKbG9naXRQZXJmb3JtYW5jZSA8LSBjb25mdXNpb25NYXRyaXgoYXMuZmFjdG9yKHRlc3RfdGhyZXNob2xkX3N1YnNldCRQcmVkRjFXaW5zKSwgYXMuZmFjdG9yKHRlc3RfdGhyZXNob2xkX3N1YnNldCRGMVdpbnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkbm4gPSBjKCJQcmVkRjFXaW5zIiwgIkYxV2lucyIpKQoKbG9naXRQZXJmb3JtYW5jZQpgYGAKCmBgYHtyLCBlY2hvPVRSVUV9CnJvYyhGMVdpbnMgfiBGMVdpbnNQcm9iLCB0ZXN0X3RocmVzaG9sZF9zdWJzZXQsIGF1YyA9IFRSVUUsIHBsb3QgPSBUUlVFKQpgYGAKCioqKgojIyMgUmFuZG9tIEZvcmVzdAoKKioqCiMjIyMgVGVzdGluZwoKYGBge3J9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgUkFORE9NIEZPUkVTVCBNT0RFTCAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojc2V0LnNlZWQoMzg4KQoKdWZjX2ZyZXNoX3JhbmRvbUZvcmVzdF9tb2RlbCA8LSByYW5kb21Gb3Jlc3QoYXMuZmFjdG9yKEYxV2lucykgfiAuLCBkYXRhID0gbmEub21pdCh0cmFpbl91ZmNfc3Vic2V0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbXBvcnRhbmNlPVRSVUUpCgp0ZXN0X3VmY19zdWJzZXQkRjFXaW5zUHJvYiA8LSBOVUxMCgp0ZXN0X3VmY19zdWJzZXQkUkZGMVdpbnNQcm9iIDwtIHByZWRpY3QodWZjX2ZyZXNoX3JhbmRvbUZvcmVzdF9tb2RlbCwgdGVzdF91ZmNfc3Vic2V0LCB0eXBlID0gInByb2IiKVssMl0KCnRlc3RfdWZjX3N1YnNldCRSRlByZWRGMVdpbnMgPC0gYXMubnVtZXJpYyh0ZXN0X3VmY19zdWJzZXQkUkZGMVdpbnNQcm9iID4gMC41KQoKcmFuZG9tRm9yZXN0UGVyZm9ybWFuY2UgPC0gY29uZnVzaW9uTWF0cml4KGFzLmZhY3Rvcih0ZXN0X3VmY19zdWJzZXQkUkZQcmVkRjFXaW5zKSwgYXMuZmFjdG9yKHRlc3RfdWZjX3N1YnNldCRGMVdpbnMpLCBkbm4gPSBjKCJGMVdpbnMiLCAiUHJlZEYxV2lucyIpKQoKcmFuZG9tRm9yZXN0UGVyZm9ybWFuY2UKYGBgCgpgYGB7ciwgZWNobz1UUlVFfQpyb2MoRjFXaW5zIH4gUkZGMVdpbnNQcm9iLCB0ZXN0X3VmY19zdWJzZXQsIGF1YyA9IFRSVUUsIHBsb3QgPSBUUlVFKQpgYGAKCmBgYHtyfQp2YXJJbXBQbG90KHVmY19mcmVzaF9yYW5kb21Gb3Jlc3RfbW9kZWwsIG4udmFyID0gMTApCmBgYAoKCioqKgojIyBVRkMgUHJvamVjdCBXb3JrZmxvdwoKVGhlIHByYWN0aWNhbCBhcHBsaWNhdGlvbiBvZiB0aGlzIHByb2plY3QgaXMgdG8gZ2VuZXJhdGUgcHJlZGljdGlvbnMgZm9yIFVGQyBmaWdodHMgb24gYSB3ZWVrbHkgYmFzaXMgaW4gb3JkZXIgdG8gd2FnZXIgb24gdGhlIGZpZ2h0cy4gIFRoZXJlIGFyZSBmb3VyIG1haW4gc3RlcHMgaW4gdGhpcyB3b3JrZmxvdy4gIAoKMS4gVXBkYXRlIGBGaWdodE1ldHJpY19maWdodGVyc19kZmA6ICBBZnRlciBlYWNoIGZpZ2h0IGV2ZW50LCBleGlzdGluZyBmaWdodGVyIGRhdGEgY2hhbmdlcyBhbmQgc29tZXRpbWVzIG5ldyBmaWdodGVycyBhcmUgaW50cm9kdWNlZCB0byBVRkMuICBTbywgd2Ugd2FudCB0byBwdWxsIHRoaXMgbmV3IGluZm9ybWF0aW9uIGludG8gb3VyIGRhdGFzZXQgb2YgZmlnaHRlcnMgKGBGaWdodE1ldHJpY19maWdodGVyc19kZmApLiAgU3BlY2lmaWNhbGx5LCB3ZSBkbyB0aGlzIGJ5IHJ1bm5pbmcgdHdvIGJsb2NrcyBvZiBjb2RlIGxhYmVsZWQgZm9yIEZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmIHRoYXQgY2FuIGJlIGZvdW5kIGluIHRoZSBVRkMuUiBzY3JpcHQgYmVnaW5uaW5nIGF0IGxpbmUgfjI1MCBhbmQgfjEwMjIsIHJlc3BlY3RpdmVseS4gIAoKMi4gVXBkYXRlIGBmaWdodGVyX3JhbmtpbmdfZGZgOiAgT2YgY291cnNlLCBhZnRlciBlYWNoIGV2ZW50IGZpZ2h0ZXIncyB3aW4gYW5kIGxvc2UsIGFuZCBzbyB0aGUgZmlnaHRlciByYW5raW5ncyBjaGFuZ2UuICBUaGVyZSBhcmUgYWN0dWFsbHkgdHdvIGNvbXBlbmVudHMgdG8gb3VyIGBmaWdodGVyX3JhbmtpbmdfZGZgIC0gd2UgaGF2ZSBoaXN0b3JpY2FsIHJhbmtpbmdzIGFuZCBjdXJyZW50IHJhbmtpbmdzLiAgQWZ0ZXIgZWFjaCBldmVudCB3ZSBuZWVkIHRvIHVwZGF0ZSB0aGUgY3VycmVudCByYW5raW5ncyBvZiBlYWNoIGZpZ2h0ZXIuIEJ1dCB0aGUgaGlzdG9yaWNhbCByYW5raW5ncyB3aWxsIG5vdCBjaGFuZ2UuICBUaGVzZSBhcmUgcmVjb3JkZWQgaW4gdGhlIGRhdGFzZXQgb24gYSBxdWFydGVybHkgYmFzaXMuICBJbiBvdGhlciB3b3Jkcywgd2UgaGF2ZSBoaXN0b3JpY2FsIHJhbmtpbmdzIGZvciBlYWNoIHdlaWdodGNsYXNzIGZvciBlYWNoIHF1YXJ0ZXIgZ29pbmcgYmFjayB0byBRMSBvZiAyMDA2LiBTbyB1cGRhdGluZyBvZiB0aGUgaGlzdG9yaWNhbCByYW5raW5ncyBvbmx5IG9jY3VycyBvbmNlIGEgcXVhcnRlciB3aGVuIHRoZXkgYXJlIHJlbGVhc2VkLiAgVGhlb3JldGljYWxseSB0aGlzIG1lYW5zIG91ciBtb2RlbCBjb3VsZCBoYXZlIGEgZmV3IGNhc2VzIG9mIHJhbmtpbmdzIHRoYXQgYXJlIHNsaWdodGx5IG9mZiwgYnV0IGFzIGEgcHJhY3RpY2FsIG1hdHRlciBxdWFydGVybHkgcmFua2luZ3MgYXJlIHByb2JhYmx5IHByZXR0eSBhY2N1cmF0ZSBhcyB0aGUgdmFzdCBtYWpvcml0eSBvZiBmaWdodGVycyBkbyBub3QgZmlnaHQgbW9yZSB0aGFuIG9uY2UgcGVyIHF1YXJ0ZXIuICBXZSB1cGRhdGUgdGhlIGN1cnJlbnQgcmFua2luZ3MgYnkgcnVubmluZyB0aGUgYmxvY2sgb2YgY29kZSBiZWdpbm5pbmcgYXQgbGluZSB+MTA3OSBvZiBVRkNVcGRhdGVyLlIsIGFuZCB3ZSB1cGRhdGUgdGhlIGhpc3RvcmljYWwgcmFua2luZ3Mgb25jZSBhIHF1YXJ0ZXIgYnkgcnVubmluZyB0aGUgYmxvY2sgb2YgY29kZSBhdCBsaW5lIH4xMDMwIG9mIFVGQ1VwZGF0ZXIuUi4gIAoKMy4gVXBkYXRlIGB1ZmNfZmlnaHRfZGV0YWlsc19kZmA6ICBBZnRlciBlYWNoIGV2ZW50IHRoZXJlIGFyZSBuZXcgZmlnaHRzIGFuZCBkZXRhaWxlZCBzdGF0aXN0aWNzIHRvIGJlIGFkZGVkIHRvIG91ciBgdWZjX2ZpZ2h0X2RldGFpbHNfZGZgLiAgV2hpbGUgdGhpcyBkYXRhc2V0IHdhcyBvcmlnaW5hbGx5IGJ1aWx0IHVzaW5nIHRoZSBVRkMuUiBzY3JpcHQsIGl0IGNhbiBiZSBhIGxlbmd0aHkgYW5kIHNvbWV3aGF0IGhhbmRzLW9uIHByb2Nlc3MgdG8gc2NyYXBlIG5lYXJseSAxMCwwMDAgd2ViIHBhZ2VzIG9uY2UsIGJ1dCB0byBkbyBzbyBldmVyeSB3ZWVrIHdvdWxkIGJlIG92ZXJseSBidXJkb25zb21lLiAgU28sIHRoZXJlIGlzIGFub3RoZXIgc2NyaXB0LCBVRkNVcGRhdGVyLlIsIHRoYXQgc2NyYXBlcyBvbmx5IHRoZSByZWNlbnRseSBjb21wbGV0ZWQgZmlnaHRzIGFuZCBkYXRhLCBjb21wdXRlcyB0aGVpciBkZXJpdml0aXZlIGRhdGEsIGFuZCB0aGVuIG1lcmdlcyB0aGVtIGJhY2sgaW50byBgdWZjX2ZpZ2h0X2RldGFpbHNfZGZgLiAgV2Ugc2ltcGx5IHJ1biB0aGUgVUZDVXBkYXRvci5SIHNjcmlwdCB0byBhY2NvbXBsaXNoIHRoaXMuICAKCjQuIEdlbmVyYXRlIHByZWRpY3Rpb25zOiAgT25jZSB3ZSBoYXZlIHVwZGF0ZWQgb3VyIGRhdGFzZXQsIHdlIGFyZSByZWFkeSB0byBnZW5lcmF0ZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHNldCBvZiB1cGNvbWluZyBmaWdodHMuICBUbyBhY2NvbXBsaXNoIHRoaXMgd2UgcnVuIHRoZSBVRkNQcmVkaWN0b3IuUiBzY3JpcHQuICBUaGlzIHNjcmlwdCBkb2VzIGEgZmV3IHRoaW5ncy4gIEZpcnN0LCBpdCBzY3JhcGVzIHRoZSB1cGNvbWluZyBmaWdodCBtYXRjaHVwcy4gIFRoZW4gaXQgY29tcHV0ZXMgYW5kIGdhdGhlcnMgdGhlIHJlbGV2YW50IHZhcmlhYmxlcyBmb3IgZWFjaCBmaWdodGVyIGZyb20gb3VyIGRhdGFzZXQuICBUaGVuIGl0IHJlZml0cyB0aGUgbW9kZWwgd2l0aCB0aGUgbW9zdCByZWNlbnQgZGF0YXNldC4gIEZpbmFsbHksIGl0IGFwcGxpZXMgb3VyIG1vZGVsIHRvIHRoZSBzZXQgb2YgZGF0YSBmb3IgdGhlIHVwY29taW5nIGZpZ2h0cyB0byBwcm9kdWNlIGEgcHJvYmFiaWxpdHkgdGhhdCBGaWdodGVyIDEgd2lsbCB3aW4uCgoqKioKIyMgUmVhbCBXb3JsZCBSZXN1bHRzCgpgYGB7cn0KcGFzdF9wcmVkaWN0aW9ucyA8LSByZWFkLmNzdigiL1VzZXJzL2x3MjEzNC9EZXNrdG9wL0RhdGFTY2kvVUZDU2l0ZS9QYXN0UHJlZGljdGlvbnMuY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKcGFzdF9wcmVkaWN0aW9ucyRYIDwtIE5VTEwKCnBhc3RfcHJlZGljdGlvbnMkRGF0ZSA8LSBwYXN0X3ByZWRpY3Rpb25zJERhdGUgJT4lCiAgYXMuRGF0ZSgiJVktJW0tJWQiKQoKcGFzdF9wcmVkaWN0aW9ucyA8LSBwYXN0X3ByZWRpY3Rpb25zICU+JQogIGFycmFuZ2UoZGVzYyhEYXRlKSkKCnBhc3RfcHJlZGljdGlvbnMkYFZlZ2FzIE91dGNvbWVgIDwtIE5BCmZvcihpIGluIDE6bnJvdyhwYXN0X3ByZWRpY3Rpb25zKSkgewogIGlmICgocGFzdF9wcmVkaWN0aW9ucyRNb25leWxpbmVbaV0gPiAwKSAmIChwYXN0X3ByZWRpY3Rpb25zJE91dGNvbWVbaV09PSJXSU4iKSkgewogICAgcGFzdF9wcmVkaWN0aW9ucyRgVmVnYXMgT3V0Y29tZWBbaV0gPC0gIkxPU0UiCiAgfSBlbHNlIGlmICgocGFzdF9wcmVkaWN0aW9ucyRNb25leWxpbmVbaV0gPCAwKSAmIChwYXN0X3ByZWRpY3Rpb25zJE91dGNvbWVbaV09PSJMT1NFIikpIHsKICAgIHBhc3RfcHJlZGljdGlvbnMkYFZlZ2FzIE91dGNvbWVgW2ldIDwtICJMT1NFIgogIH0gZWxzZSB7CiAgICBwYXN0X3ByZWRpY3Rpb25zJGBWZWdhcyBPdXRjb21lYFtpXSA8LSAiV0lOIgogIH0KfQoKY29sbmFtZXMocGFzdF9wcmVkaWN0aW9ucylbYygyLDMsOCldIDwtIGMoIkZpZ2h0ZXIgMSIsICJGaWdodGVyIDIiLCAiTW9kZWwgT3V0Y29tZSIpCgprYWJsZShwYXN0X3ByZWRpY3Rpb25zLCBhbGlnbiA9ICdsJykKYGBgCgpgYGB7cn0KcGFzdF9wcmVkaWN0aW9uc19wbG90IDwtIHBhc3RfcHJlZGljdGlvbnMkRGF0ZSAlPiUKICB1bmlxdWUoKSAlPiUKICBhcy5kYXRhLmZyYW1lKCkKCnBhc3RfcHJlZGljdGlvbnNfd2lucyA8LSBwYXN0X3ByZWRpY3Rpb25zICU+JQogIGFycmFuZ2UoZGVzYyhEYXRlKSkgJT4lCiAgZ3JvdXBfYnkoRGF0ZSkgJT4lCiAgZmlsdGVyKGBNb2RlbCBPdXRjb21lYCA9PSJXSU4iKSAlPiUKICBjb3VudChgTW9kZWwgT3V0Y29tZWApCgpwYXN0X3ByZWRpY3Rpb25zX3Bsb3QkTW9kZWxXaW5zIDwtIHBhc3RfcHJlZGljdGlvbnNfd2lucyRuCgpybShwYXN0X3ByZWRpY3Rpb25zX3dpbnMpCgpwYXN0X3ByZWRpY3Rpb25zX3ZlZ2Fzd2lucyA8LSBwYXN0X3ByZWRpY3Rpb25zICU+JQogIGFycmFuZ2UoZGVzYyhEYXRlKSkgJT4lCiAgZ3JvdXBfYnkoRGF0ZSkgJT4lCiAgZmlsdGVyKGBWZWdhcyBPdXRjb21lYCA9PSJXSU4iKSAlPiUKICBjb3VudChgVmVnYXMgT3V0Y29tZWApCgpwYXN0X3ByZWRpY3Rpb25zX3Bsb3QkVmVnYXNXaW5zIDwtIHBhc3RfcHJlZGljdGlvbnNfdmVnYXN3aW5zJG4KCnJtKHBhc3RfcHJlZGljdGlvbnNfdmVnYXN3aW5zKQoKcGFzdF9wcmVkaWN0aW9uc19wbG90JC4gPC0gcmV2KHBhc3RfcHJlZGljdGlvbnNfcGxvdCQuKQoKY29sbmFtZXMocGFzdF9wcmVkaWN0aW9uc19wbG90KVsxXSA8LSAiRGF0ZSIKCmdncGxvdChwYXN0X3ByZWRpY3Rpb25zX3Bsb3QsIGFlcyhEYXRlKSkgKyBnZW9tX2xpbmUoYWVzKHkgPSBNb2RlbFdpbnMsIGNvbG91ciA9ICJNb2RlbFdpbnMiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IFZlZ2FzV2lucywgY29sb3VyID0gIlZlZ2FzV2lucyIpKSArIHlsYWIoIldpbiBDb3VudCIpICsgc2NhbGVfeF9kYXRlKCkKYGBgCgpgYGB7cn0KcGFzdF9wcmVkaWN0aW9uc190b3RhbHMgPC0gZGF0YS5mcmFtZSgxLCAxOihzdW0ocGFzdF9wcmVkaWN0aW9uc19wbG90JE1vZGVsV2lucykgKyBzdW0ocGFzdF9wcmVkaWN0aW9uc19wbG90JFZlZ2FzV2lucykpKQoKcGFzdF9wcmVkaWN0aW9uc190b3RhbHMkV2lucyA8LSBjKHJlcCgiTW9kZWwiLCBzdW0ocGFzdF9wcmVkaWN0aW9uc19wbG90JE1vZGVsV2lucykpLCByZXAoIlZlZ2FzIiwgc3VtKHBhc3RfcHJlZGljdGlvbnNfcGxvdCRWZWdhc1dpbnMpKSkKCgpxcGxvdChXaW5zLCBkYXRhID0gcGFzdF9wcmVkaWN0aW9uc190b3RhbHMsIGdlb20gPSAiYmFyIiwgZmlsbCA9IFdpbnMpCmBgYAoKKioqCiMjIFJvYWRtYXAgRm9yIEltcHJvdmVtZW50IAoKVGhlIHN0YXR1cyBvZiB0aGlzIHByb2plY3QgaXMgYm90aCBmdW5jdGlvbmFsIGFuZCBwcmFjdGljYWwsIGFuZCBpdCBhY2NvbXBsaXNoZXMgd2hhdCBpdCBzZXRzIG91dCB0byBkby4gTm9uZXRoZWxlc3MsIHRoZXJlIGlzIHJvb20gZm9yIGltcHJvdmVtZW50LiAgVGhlIGZvbGxvd2luZyBpcyBhIHdvcmtpbmcgbGlzdCBvZiBpdGVtcyB0byBleHBsb3JlIGZvciBpbXByb3ZpbmcgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBtb2RlbC4gIAogIAoqKkZlYXR1cmUgRW5naW5lZXJpbmcqKiAgCjEuIEEgZmVhdHVyZSB0aGF0IGluY29ycG9yYXRlcyBob3cgYSBmaWdodCBlbmRzOiBGb3IgZXhhbXBsZSwgZG9lcyBhIGZpZ2h0ZXIgd2luIGJ5IEtPL1RLTywgU3VibWlzc2lvbiwgRGVjaXNpb24sIGV0Yy4uLiAgCjIuIEEgZmVhdHVyZSB0aGF0IGluY29ycG9yYXRlcyB3aGV0aGVyIG9yIG5vdCB0aGlzIGlzIGEgZmlnaHRlcidzIGZpcnN0IGZpZ2h0IGluIFVGQy4gIAozLiBBIGZlYXR1cmUgdG8gaW5jb3Jwb3JhdGUgYSBmaWdodGVyJ3MgY3VycmVudCB3aW5uaW5nIG9yIGxvc2luZyBzdHJlYWsuICAKNC4gRnVydGhlciB0ZXN0aW5nIG9mIGRpZmZlcmVudCB2YXJpYWJsZSBjb21iaW5hdGlvbnMgKHJlbW92aW5nIHZhcmlhYmxlcyB0aGF0IGFwcGVhciBpbnNpZ25pZmljYW50LCBmb3IgZXhhbXBsZSkKCioqUHJlZGljdGl2ZSBNb2RlbGluZyoqICAKMS4gRXhwbG9yZSBpbXBsZW1lbnRpbmcgYW5kIEVuc2VtYmxlIG1vZGVsIG9mIGJvdGggTG9naXN0aWMgUmVncmVzc2lvbiBhbmQgUmFuZG9tIEZvcmVzdC4gIAoyLiBFeHBsb3JlIGNsdXN0ZXJpbmcgb24gc2V0cyBvZiBmaWdodHMgcHJlZGljdGVkIGluY29ycmVjdGx5IHRvIHNlZSBpZiB3ZSBjYW4gZGV2ZWxvcCBydWxlcyB0byBpbXByb3ZlIG92ZXJhbGwgcHJlZGljdGl2ZSBhY2N1cmFjeSB3aXRoIGEgbXVsdGktc3RhZ2UgcHJlZGljdGl2ZSBhcHByb2FjaC4uLiBvciBhIG11bHRpLWZhY2V0ZWQgbW9kZWwgKGkuZS4gZ2VuZXJhdGUgbXVsdGlwbGUgZml0cyBmb3Igc3Vic2V0cyBvZiBmaWdodHMgdGhhdCBtZWV0IGNlcnRhaW4gY3JpdGVyaWEpLiAgCjMuIEV4cGxvcmUgU3BlY2lmaWNpdHksIFByZWNpc2lvbiwgYW5kIG90aGVyIG1vZGVsIHBlcmZvcm1hbmNlIG1lYXN1cmVzIGFzIGEgd2F5IHRvIHdlaWdodCBvciBob25lIG1vZGVsIHByZWRpY3Rpb25zLiAgCgoqKlVzYWJpbGl0eSoqICAKMS4gQXV0b21hdGUgZGF0YXNldCB1cGRhdGVzIGFuZCBwcmVkaWN0aW9uIGdlbmVyYXRpb24uICAKMi4gQnVpbGQgYSB1c2VyLWZyaWVuZGx5IGRhc2hib2FyZCBvciBpbnRlcmFjdGl2ZSBpbnRlcmZhY2Ugd2l0aCBGbGV4ZGFzaGJvYXJkIG9yIFNoaW55LCByZXNwZWN0aXZlbHkuICAKCioqQ29kZSBCYXNlKiogIAoxLiBJbXBsZW1lbnQgdGVzdGluZyBzdWl0ZSB0byBoZWxwIGF2b2lkICJicmVha2luZyIgYW4gaW5jcmVhc2luZ2x5IGNvbXBsZXggY29kZSBiYXNlIGR1cmluZyBmdXR1cmUgY29kZSBtYWludGVuYW5jZSBhbmQgZmVhdHVyZSBhZGRpdGlvbnMuICAKMi4gUGxlbnR5IG9mIHJvb20gZm9yIHJlZmFjdG9yaW5nLCB3aGljaCB3aWxsIHNpbXBsZnkgcmVhZGFiaWxpdHkgYW5kIGNvbGxhYm9yYXRpb24sIGFuZCBtYXkgcHJlc2VudCBzb21lIGdhaW5zIGluIGV4ZWN1dGlvbiBzcGVlZC4KCioqKgojIyBBcHBlbmRpeCAgCgojIyMgRGF0YQpgYGB7cn0KIyBDcmVhdGluZyB0YWJsZSB0byBleHBsYWluIHRoZSBkYXRhLgpkYXRhX3RhYmxlIDwtIGRhdGEuZnJhbWUoY29sbmFtZXModWZjX2ZpZ2h0X2RldGFpbHNfZGYpLAogICAgICAgICAgICAgICAgICAgICAgICAgcmVwKE5BLGxlbmd0aCh1ZmNfZmlnaHRfZGV0YWlsc19kZikpLAogICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHVmY19maWdodF9kZXRhaWxzX2RmWzM0NDksXSksCiAgICAgICAgICAgICAgICAgICAgICAgICByZXAoIkZpZ2h0TWV0cmljIixsZW5ndGgodWZjX2ZpZ2h0X2RldGFpbHNfZGYpKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKCiMgQXNzaWduIGNvbHVtbiBuYW1lcy4KY29sbmFtZXMoZGF0YV90YWJsZSkgPC0gYygiVmFyaWFibGVzIiwgIlVuYWJyZXZpYXRlZCIsICJFeGFtcGxlIiwgIlNvdXJjZSIpCgojIEdlbmVyYXRlIHVuYWJyZXZpYXRlZCB2YXJpYWJsZSBuYW1lcy4KZGF0YV90YWJsZSRVbmFicmV2aWF0ZWQgPC0gZGF0YV90YWJsZSRWYXJpYWJsZXMgJT4lCiAgZ3N1YigiRjEiLCAiRmlnaHRlcjEgIiwgLikgJT4lCiAgZ3N1YigiRjIiLCAiRmlnaHRlcjIgIiwgLikgJT4lCiAgZ3N1YigiU3RyIiwgIlN0cmlrZSAiLCAuKSAlPiUKICBnc3ViKCJBdHQiLCAiQXR0ZW1wdCAiLCAuKSAlPiUKICBnc3ViKCJDYXIiLCAiQ2FyZWVyICIsIC4pICU+JQogIGdzdWIoIlNpZyIsICJTaWduaWZpY2FudCAiLCAuKSAlPiUKICBnc3ViKCJPcHAiLCAiT3Bwb25lbnQgIiwgLikgJT4lCiAgZ3N1YigiVEQiLCAiVGFrZSBEb3duICIsIC4pICU+JQogIGdzdWIoIkFicyIsICJBYnNvcmJlZCAiLCAuKSAlPiUKICBnc3ViKCJwTSIsICJwZXIgTWludXRlICIsIC4pICU+JQogIGdzdWIoIkFjYyIsICJBY2N1cmFjeSAiLCAuKSAlPiUKICBnc3ViKCJEZWYiLCAiRGVmZW5zZSAiLCAuKSAlPiUKICBnc3ViKCJBdmciLCAiQXZlcmFnZSAiLCAuKSAlPiUKICBnc3ViKCJEaXN0IiwgIkRpc3RhbmNlICIsIC4pICU+JQogIGdzdWIoIktEIiwgIktub2NrIERvd24gIiwgLikgJT4lCiAgZ3N1YigiUGFzcyIsICJHdWFyZCBQYXNzICIsIC4pICU+JQogIGdzdWIoIk5DIiwgIk5vIENvbnRlc3QgIiwgLikgJT4lCiAgZ3N1YigiUjEiLCAiUm91bmQgMSAiLCAuKSAlPiUKICBnc3ViKCJSMiIsICJSb3VuZCAyICIsIC4pICU+JQogIGdzdWIoIlIzIiwgIlJvdW5kIDMgIiwgLikgJT4lCiAgZ3N1YigiUjQiLCAiUm91bmQgNCAiLCAuKSAlPiUKICBnc3ViKCJSNSIsICJSb3VuZCA1ICIsIC4pICU+JQogIGdzdWIoIlRvdCIsICJUb3RhbCAiLCAuKSAlPiUKICBnc3ViKCJSZXYiLCAiUmV2ZXJzYWwiLCAuKSAlPiUKICBnc3ViKCJTQSIsICJTdHJpa2VzIEFic29yYmVkICIsIC4pICU+JQogIGdzdWIoIlN1YiIsICJTdWJtaXNzaW9uICIsIC4pICU+JQogIGdzdWIoIlJlZiIsICJSZWZlcmVlICIsIC4pICU+JQogIGdzdWIoIlNMIiwgIlN0cmlrZXMgTGFuZGVkICIsIC4pCgojIEluc2VydCBkYXRhIHNvdXJjZXMKc2VsZl9kZXJpdmVkIDwtIGdyZXAoIi4qQ2FyLioiLCBkYXRhX3RhYmxlJFZhcmlhYmxlcykKZmlnaHRtYXRyaXggPC0gZ3JlcCgiRjFSYW5rfEYyUmFuayIsIGRhdGFfdGFibGUkVmFyaWFibGVzKQoKZGF0YV90YWJsZSRTb3VyY2Vbc2VsZl9kZXJpdmVkXSA8LSAiRGVyaXZlZCIKZGF0YV90YWJsZSRTb3VyY2VbZmlnaHRtYXRyaXhdIDwtICJGaWdodE1hdHJpeCIKCmthYmxlKGRhdGFfdGFibGUsIGNhcHRpb24gPSAiQ29tcG9zaXRpb24gb2YgdGhlIHByaW1hcnkgZGF0c2V0LCBbdWZjX2ZpZ2h0X2RldGFpbHNfZGZdIiwgYWxpZ24gPSAnbCcpCmBgYAoKYGBge3J9CiMgQ3JlYXRpbmcgdGFibGUgdG8gZXhwbGFpbiB0aGUgZGF0YS4KZGF0YV90YWJsZV9maWdodGVycyA8LSBkYXRhLmZyYW1lKGNvbG5hbWVzKGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHJlcChOQSxsZW5ndGgoZmlnaHRNZXRyaWNfZmlnaHRlcnNfZGYpKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihmaWdodE1ldHJpY19maWdodGVyc19kZltmaWdodE1ldHJpY19maWdodGVyc19kZiROYW1lPT0iR2VnYXJkIE1vdXNhc2kiLF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgcmVwKCJGaWdodE1ldHJpYyIsbGVuZ3RoKGZpZ2h0TWV0cmljX2ZpZ2h0ZXJzX2RmKSksCiAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgojIEFzc2lnbiBjb2x1bW4gbmFtZXMuCmNvbG5hbWVzKGRhdGFfdGFibGVfZmlnaHRlcnMpIDwtIGMoIlZhcmlhYmxlcyIsICJVbmFicmV2aWF0ZWQiLCAiRXhhbXBsZSIsICJTb3VyY2UiKQoKIyBHZW5lcmF0ZSB1bmFicmV2aWF0ZWQgdmFyaWFibGUgbmFtZXMuCmRhdGFfdGFibGVfZmlnaHRlcnMkVW5hYnJldmlhdGVkIDwtIGRhdGFfdGFibGVfZmlnaHRlcnMkVmFyaWFibGVzICU+JQogIGdzdWIoIlN0ciIsICJTdHJpa2UgIiwgLikgJT4lCiAgZ3N1YigiU0wiLCAiU3RyaWtlcyBMYW5kZWQgIiwgLikgJT4lCiAgZ3N1YigicE0iLCAicGVyIE1pbnV0ZSAiLCAuKSAlPiUKICBnc3ViKCJBY2MiLCAiQWNjdXJhY3kgIiwgLikgJT4lCiAgZ3N1YigiRGVmIiwgIkRlZmVuc2UgIiwgLikgJT4lCiAgZ3N1YigiQXZnIiwgIkF2ZXJhZ2UgIiwgLikgJT4lCiAgZ3N1YigiU0EiLCAiU3RyaWtlcyBBYnNvcmJlZCIsIC4pICU+JQogIGdzdWIoIlN1YiIsICJTdWJtaXNzaW9uICIsIC4pICU+JQogIGdzdWIoIkF0dCIsICJBdHRlbXB0ICIsIC4pICU+JQogIGdzdWIoIlREIiwgIlRha2UgRG93biAiLCAuKQoKa2FibGUoZGF0YV90YWJsZV9maWdodGVycywgY2FwdGlvbiA9ICJDb21wb3NpdGlvbiBvZiB0aGUgcHJpbWFyeSBkYXRzZXQsIFt1ZmNfZmlnaHRfZGV0YWlsc19kZl0iLCBhbGlnbiA9ICdsJykKYGBgCgoKCgoK