1. This assignment is my/our own original work, except where I/we have appropriately cited the original source (appropriate citation of original work will vary from discipline to discipline).
  2. This assignment has not previously been submitted in any form for this or any other unit, degree or diploma at any university or other institute of tertiary education.
  3. I/we acknowledge that it is my responsibility to check that the file I/we have submitted is: a) readable, b) the correct file and c) fully complete.

Introduction

The AFL (Australian Football League) is the highest league that exists for a popular sport within Australia, called football (sometimes ‘footy’ or ‘ozzie rules’). Like most sports that are popular around the globe, a ludicrous betting industry exists around the AFL where you’re rewarded for picking the winning team(s), and not-so-rewarded if you happen to pick the losing team(s). The AFL, and this betting ethos, is somewhat ingrained into Australian culture to the point that it’s common place for groups of friends/colleagues to all pitch in and run a ‘tipping competition.’ A competition where each participant is ranked by the amount of wins that they can successfully predict during a season of football, and is rewarded according to their individual ranks. Of course, where there are rewards to be reaped there’s more often than not a group of determined people, using all means possible, in order to grab the biggest piece of the pie. We decided that, armed with a semester’s knowledge of Data Science as our tools, it would be in our best interest to join this group and test our luck at grabbing a piece of the pie.

This report outlines of all the necessary steps we took to try ‘secure this piece of the pie;’ that is, predict that when any given AFL match is played which team would be more likely to win.

During the report we’ll explore:


Description of Data and Methods

The Data

  • Presents a clear, concise description of the data, sources, and statistical methods used in the report
  • Important aspects of data set are clearly explained
  • Summary tables (if presented) are understandable and self- contained

The data that we used to go about this prediction is a data set that was retrieved from Kaggle (AFL Dataset). Which is actually just an aggregated collection (as a .csv file) of all the player data available from the AFL Tables website; from AFL seasons 2012 - 2016 (inclusive). The data set has been analysed once before by the author/uploader of the data, (MichealStone. He explored his data and modeled it accordingly in an effort to try find the ‘most influential’ player in the AFL from seasons 2012 - 2016.

AFLstats <- read.csv("stats.csv")

Of course when we are analyzing this data set we are making the assumption that MichealStone, the author of the data set, has made no errors whilst collecting all this data and merging it together. Seeing as the data has no missing values (seen in the plot(s) below) and during our analysis we were lead to reasonable, logical outcomes; it seems like this is a fair assumption to make.

require(Hmisc)
plot(describe(AFLstats))
$Categorical

$Continuous

The data set has 45496 rows and 32 columns. Each row represents a player’s performance within an AFL match between 2012 and 2016; including information about which round the game in question was, if the team one or lost and a bunch of numerical statistics relating to the individual performance; giving us a total of potentially 32 different variables to work with for each individual player.

Here’s a quick peek at the first six rows of our data set. Here we can see that we’re looking at Adelaide’s first game in the 2016 season, where they played against North Melbourne at the Docklands stadium and lost. The majority of the information left in these rows just assigns an integer number to a specific game statistic, indicating how many times a player managed to meet the condition of that statistic. This is useful in giving us an indication of how well one player might have been playing in a certain game with respect to another playing (comparing only the variables we have to work with of course).

AFLstats[1:5,1:8]

Below is an outline of these different variables. As you can see we have seven ‘categorical variables’ (some should probably just be character variables like ‘Player’), and the rest are just quantitative variables (all in integer form).

str(AFLstats)
'data.frame':   45496 obs. of  32 variables:
 $ Team                  : Factor w/ 18 levels "Adelaide","Brisbane Lions",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Player                : Factor w/ 1048 levels "Ablett, Gary",..: 26 55 98 136 184 185 236 449 455 508 ...
 $ Season                : int  2016 2016 2016 2016 2016 2016 2016 2016 2016 2016 ...
 $ Round                 : Factor w/ 28 levels "EF","GF","PF",..: 5 5 5 5 5 5 5 5 5 5 ...
 $ Date                  : Factor w/ 427 levels "2012-03-24","2012-03-29",..: 345 345 345 345 345 345 345 345 345 345 ...
 $ Score                 : int  97 97 97 97 97 97 97 97 97 97 ...
 $ Margin                : int  -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 ...
 $ WinLoss               : Factor w/ 3 levels "D","L","W": 2 2 2 2 2 2 2 2 2 2 ...
 $ Opposition            : Factor w/ 18 levels "Adelaide","Brisbane Lions",..: 12 12 12 12 12 12 12 12 12 12 ...
 $ Venue                 : Factor w/ 19 levels "Adelaide Oval",..: 6 6 6 6 6 6 6 6 6 6 ...
 $ Disposals             : int  18 15 13 11 20 23 20 10 10 24 ...
 $ Kicks                 : int  9 11 9 5 12 14 14 4 8 14 ...
 $ Marks                 : int  3 3 4 1 2 3 2 3 5 9 ...
 $ Handballs             : int  9 4 4 6 8 9 6 6 2 10 ...
 $ Goals                 : int  0 1 0 0 0 1 1 1 5 0 ...
 $ Behinds               : int  0 3 0 0 0 0 0 0 0 0 ...
 $ Hitouts               : int  0 0 0 0 0 0 0 35 5 0 ...
 $ Tackles               : int  2 8 2 2 9 2 3 1 1 1 ...
 $ Rebound50s            : int  2 3 3 4 2 2 2 0 0 2 ...
 $ Inside50s             : int  4 3 1 0 1 5 6 1 2 2 ...
 $ Clearances            : int  3 3 0 0 3 10 2 2 0 1 ...
 $ Clangers              : int  2 3 1 0 7 1 1 1 1 4 ...
 $ FreesFor              : int  0 3 0 2 2 2 1 0 1 0 ...
 $ FreesAgainst          : int  1 1 0 0 3 1 0 0 1 1 ...
 $ ContendedPossessions  : int  10 8 3 9 9 15 5 4 4 7 ...
 $ UncontendedPossessions: int  10 7 10 3 13 9 14 6 5 17 ...
 $ ContestedMarks        : int  0 0 0 1 0 0 0 1 1 0 ...
 $ MarksInside50         : int  0 1 0 0 0 0 0 1 2 0 ...
 $ OnePercenters         : int  2 0 0 7 1 1 3 0 2 1 ...
 $ Bounces               : int  1 0 0 0 0 0 0 0 0 0 ...
 $ GoalAssists           : int  0 0 0 0 1 0 1 0 1 1 ...
 $ PercentPlayed         : int  77 91 83 84 77 67 76 93 90 82 ...

It’s import to understand all our variables before we actually get into manipulating the data; to make sure we’re not destroying the integrity of our data as we carry out our analysis. In saying that, most of the variables are pretty self explanatory; even to someone who only has a very basic knowledge of AFL. However, there’s certainly some ‘slang’ terminology which might need to be cleared up:

  • Behinds - when the ball is touched either by another player or by the goal post but still passes over the goal line; or simply the ball passes over the behind line (more information here)
  • Hitouts - when a player successfully knocks the ball out of a ruck
  • Ruck - a ruck occurs generally after a goal is scored, play is ceased after a static tackle or the ball exits the field lines; to reset the play a referee will either bounce the ball up, or throw the ball in from the sideline and the opposing teams with contest to hit the ball into play
  • Rebound50s - when the ball exits the teams defensive 50 area (their own 50 area)
  • Clangers - a general turn given to a player’s small error which results in a turn-over of the ball (more information here)
  • OnePercenters - as the name alludes to, this is a term given to collection of rare actions within AFL which benefit the teams play; including knock-ons, spoils, smothers and shepards (more information here)

Our methods

When taking a closer look at the problem we are out to solve, predicting which team is more likely to win in any given AFL match, it seems like quite a simple classification problem. We want to be able to classify one team as winning and one team as losing, based upon an assigned chance of winning. The chance winning would optimally be determined by a subset of relevant variables picked from the 32 we have available within this data set. With this in mind it seems that predictive models like Logistic Regression, K-Nearest Neighbours and Decision Tree/Many Forests would be perfect candidates for helping us solve our problem, as they either designed for (or just good for) assigning predictive probabilities for simple, binary classification variables. However, we will explore how helpful these tools actually are in helping us achieve our goal in the later parts of this report.

A surprisingly more effective method for solving our prediction problem is simply wrangling parts of the data set that we want to look at into a workable form, and then visualizing these parts through various plots. The advantage that this method has over the more complex predictive models is that plots make it much easier to comprehend a simple relationship between a select few variables. This makes it more efficient for us to understand (and communicate) the strong (or weak) correlations that we have come across within our data set. When comparing this to the more complicated predictive models it’s clear that they both have their place within any analysis. However, as we discover later in our process it’s clear that one ‘set’ of methods worked better than the other.


Methodology

As what we’re interested in is a team’s overall performance and the data set that we’re looking at is structured in terms of each individual player’s performance, our first hurdle we had to jump over was getting the data set into a more workable form for what we’ve set out to achieve.

The first thing we did was deleted columns we deemed would be necessary going forward in our analysis; the obvious candidates to go were ‘Player’ and ‘Percent Played’ as they’d be useless once we combined everything to be in terms of any teams not players. However, slightly less obvious variables that we removed were ‘FreesAgainst’ and ‘Uncontested Possessions.’ We did this is because our once one teams statistics are put against another teams statistics for comparison one team’s ‘FreesFor’ would be exactly the same as the opposing team, and vise versa (same thing for Contested vs Uncontested Possessions). So, to prevent future problems and clutter we deleted these four columns completely from our data set.

AFL <- AFLstats
AFL <- AFL[c(1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,25,27,28,29,30,31)]
#colnames(AFL)

The next logical step in our methodology was to actually split our data set into the different teams total games as that’s what we’re most interested in, how well one team would perform against any other.

all_teams <- unique(as.character(AFL$Team))
ad <- AFL[AFL$Team == all_teams[1],]
bl <- AFL[AFL$Team == all_teams[2],]
clt <- AFL[AFL$Team == all_teams[3],]
clwd <- AFL[AFL$Team == all_teams[4],]
esdn <- AFL[AFL$Team == all_teams[5],]
fd <- AFL[AFL$Team == all_teams[6],]
glng <- AFL[AFL$Team == all_teams[7],]
gc <- AFL[AFL$Team == all_teams[8],]
gws <- AFL[AFL$Team == all_teams[9],]
ht <- AFL[AFL$Team == all_teams[10],]
mlb <- AFL[AFL$Team == all_teams[11],]
nmlb <- AFL[AFL$Team == all_teams[12],]
pad <- AFL[AFL$Team == all_teams[13],]
rmd <- AFL[AFL$Team == all_teams[14],]
sk <- AFL[AFL$Team == all_teams[15],]
syd <- AFL[AFL$Team == all_teams[16],]
wc <- AFL[AFL$Team == all_teams[17],]
wb <- AFL[AFL$Team == all_teams[18],]

After this had been done we still faced the problem that our data is in terms of rows of player statistics, and not rows of total game statistics. Through rigorous trial and error we created a nice, neat function which cleans all the data by wrangling it into the form we want it to be in (total statistics for any given game). It is also important to note that only five of the total games in the data set resulted in a draw (shown below), so in an effort so simplify our the data we decided it would be best to just remove these cases (this occurs within the cleaning function).

# players which were part of a tie game / number of players in team / number of teams in a match
nrow(AFL[AFL$WinLoss == "D",]) / 22 / 2
[1] 5

This is the function which ‘cleans’ our data. It essential takes the first nine columns and takes one copy of the information there as it’s unique to each match. The remaining columns are player statistics so they are just summed together with all the other similar variables within any match to get the data into the form we want it to be in. As mentioned before, the games that were tied were removed from the data set in this step.

cleanAFLdata <- function(team){
  
  library(plyr)
  team_stats <- ddply(team, .(Date), numcolwise(sum))
  team_stats <- team_stats[c(1,5:23)]
  team_keep <- team[match(unique(team$Date), team$Date),]
  team_keep <- team_keep[1:9]
  team_clean <- merge(team_keep, team_stats)
  team_clean <- team_clean[team_clean$WinLoss != "D",]
  team_clean$WinLoss <- droplevels(team_clean$WinLoss)
  levels(team_clean$WinLoss)[levels(team_clean$WinLoss)=="L"] <- 0
  levels(team_clean$WinLoss)[levels(team_clean$WinLoss)=="W"] <- 1
  team_clean$WinLoss <- factor(team_clean$WinLoss)
  
  return(team_clean)
  
}
ad_clean <- cleanAFLdata(ad)
bl_clean <- cleanAFLdata(bl)
clt_clean <- cleanAFLdata(clt)
clwd_clean <- cleanAFLdata(clwd)
esdn_clean <- cleanAFLdata(esdn)
fd_clean <- cleanAFLdata(fd)
glng_clean <- cleanAFLdata(glng)
gc_clean <- cleanAFLdata(gc)
gws_clean <- cleanAFLdata(gws)
ht_clean <- cleanAFLdata(ht)
mlb_clean <- cleanAFLdata(mlb)
nmlb_clean <- cleanAFLdata(nmlb)
pad_clean <- cleanAFLdata(pad)
rmd_clean <- cleanAFLdata(rmd)
sk_clean <- cleanAFLdata(sk)
syd_clean <- cleanAFLdata(syd)
wc_clean <- cleanAFLdata(wc)
wb_clean <- cleanAFLdata(wb)

After going through all this effort to split the data to be in terms of separate teams it actually meant that for each team we only had a very small set of data points. This made any prediction models for each individual team impossible as the training set (2012 - 2015) only had roughly 100 rows and our test set (2016) only had about 25 rows; which R didn’t think was enough data for the predictive models we wanted to do. So we ended up just combining everything back into one nice big data set (after being cleaned).

AFL_clean <- rbind(ad_clean, bl_clean, clt_clean, clwd_clean, esdn_clean, fd_clean, glng_clean, 
                   gc_clean, gws_clean, ht_clean, mlb_clean, nmlb_clean, pad_clean, rmd_clean,
                   sk_clean, syd_clean, wc_clean, wb_clean)
#length(unique(AFL_clean$Team))

Once our data was once again in one big, combined data frame we decided (and R accepted our decision) it was finally time to successfully split our data into a separate training and test set. It made sense for our training set to be all the games in 2012 - 2015 (inclusive) and then our test set to be just the matches played in the latest (in our data set) 2016 season. This made sense as our goal is to predict the current (2017) matches so having our test set being the closest to that as possible meant our predictive models would (in theory) ‘tune’ our model to be as accurate for the current season as we could hope for.

AFL.test <- AFL_clean[AFL_clean$Season == 2016,]
#head(AFL.test)
AFL.train <- AFL_clean[AFL_clean$Season != 2016,]
#head(AFL.train)

Next we decided to do a little exploratory analysis of our training set, this was to make sure everything makes logical sense (check we haven’t made an error during our cleaning process), and just to explore the data and see if we can find anything interesting.

plot(AFL.train$WinLoss, AFL.train$Disposals)

plot(AFL.train$WinLoss, AFL.train$Inside50s)

plot(AFL.train$WinLoss, AFL.train$Clearances)

From these few plots we can see that our data looks to be intact, as we actually got given functional box plots as outputs. And more importantly it seems that at least some of these variables have some sort of a correlation with teams who’ve won (1) and teams who’ve lost (0). This meant that some of our variables might be strong candidates in being able to assistance us in predicting the outcomes of future games.

As a second measure to this process we created a correlation coefficient histogram matrix with all of our variables to see if anything had a particularly strong correlation with our ‘WinLoss’ variable.

source("PairsWithHist.R")
pairs(AFL_clean, lower.panel = panel.pts, upper.panel = panel.cor, diag.panel = panel.hist)

Our next step was to actually begin preparing to make some predictive models; which wasn’t just as simple as fitting a model with all the variables and then fitting another model with none, and then running forward selection between the two (although that was one step within the process). After some trial and error and thought into what we were actually doing some things became clear; we had too many variables and some variables just didn’t make sense being in our model(s).

To reduce our total amount of variables we first removed all the categorical explanatory variables. Our reason for doing this is that each level of a factor variable within a predictive model becomes its own variable, not just the one factor variable itself. For example, by including the factor variable ‘Venue’ we are introducing is 19 new explanatory variables into our model, not simply just venue.

The next problem was that the ‘Margin’ variable was a perfect predictor of whether a team would win or lose. If the margin for a game was greater than zero the team had won, if the margin was negative then the team had lost. By including this in our model it would have had such a strong correlation that all the other variables, which are arguably more useful for our overall goal, would be lost as noise; so we had to remove it.

Variables like ‘Score’ and ‘Goals’ were in a similar sort of boat because teams who win are always going to have a higher score, because that’s how you win the game. This is such a basic premise that it really made no sense to include in our model as it was just adding noise. And the last set of variables we didn’t include in our model were ‘Handballs’ and ‘Kicks’ as they are just subsets of the ‘Disposal’ variable, making it pointless to include all three within our model.

After all of these ‘types’ of variables were excluded from our model we were left with what we with the majority of the integer match statistics like ‘Disposals,’ ‘Inside50s,’ ‘Clangers,’ etc.

# just 'numerical' variables
AFL.glm.num <- glm(WinLoss ~  Disposals + Marks + Behinds + Inside50s + Clearances + Clangers + ContendedPossessions + ContestedMarks + MarksInside50, 
                  data = AFL.train, family = binomial(logit))

Then we set up a model with no explanatory variables.

AFL.glm.0 <- glm(WinLoss ~ 1, data = AFL.train, family = binomial(logit))

And finally we compared these two models to find the optimal set of variables we should be including in our final model. Which by looking at the summary below you might have noticed left us with seven explanatory variables in our final model; removing ‘ContestedMarks’ and ‘Behinds’ as forward selection didn’t find them relevant.

All the variables left in the model also have very small p-values which is always a good sign that they could be highly correlated to the response variable (winning a match).

AFL.glm.fwd <- step(AFL.glm.0, scope = formula(AFL.glm.num), direction = "forward", trace = 0)
summary(AFL.glm.fwd)

Call:
glm(formula = WinLoss ~ Inside50s + Marks + ContendedPossessions + 
    MarksInside50 + Clangers + Clearances + Disposals, family = binomial(logit), 
    data = AFL.train)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-2.69869  -0.70594  -0.01347   0.69685   2.59537  

Coefficients:
                       Estimate Std. Error z value Pr(>|z|)    
(Intercept)          -17.993850   1.172142 -15.351  < 2e-16 ***
Inside50s              0.067637   0.010162   6.656 2.82e-11 ***
Marks                  0.040351   0.005638   7.157 8.24e-13 ***
ContendedPossessions   0.047621   0.006556   7.264 3.77e-13 ***
MarksInside50          0.158085   0.019926   7.933 2.13e-15 ***
Clangers              -0.031204   0.009413  -3.315 0.000917 ***
Clearances             0.034660   0.012002   2.888 0.003878 ** 
Disposals              0.007527   0.002734   2.753 0.005898 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 2279.1  on 1643  degrees of freedom
Residual deviance: 1463.4  on 1636  degrees of freedom
AIC: 1479.4

Number of Fisher Scoring iterations: 5

Analysis

Logistic Regression

The first predictive model that we decided to implement was a Logistic Regression model, with ‘WinLoss’ being our response variable, obviously with a ‘1’ (winning game) being the preferred outcome.

AFL.all.pred <- predict(AFL.glm.fwd, newdata = AFL.test, type = "response")
#head(AFL.all.pred)

After the model had made its predictions using our test data to ‘tune’ it, we had to compare these predictions to the actual true, predicted outcomes for 2016. The most efficient way of going about this was to create a confusion matrix of our predicted results against the true results, this gives us an idea of how accurate our model actually is.

AFL.all.pred.class <- rep(0, length(AFL.all.pred))
AFL.all.pred.class[AFL.all.pred > 0.75] <- 1
table(AFL.all.pred.class, AFL.test$WinLoss)
                  
AFL.all.pred.class   0   1
                 0 188  75
                 1  19 132
(188+132)/(188+75+19+132)
[1] 0.7729469

From this confusion matrix we can see that about 77% of our predictions were correct when we used a cut-off value of 0.75. This means that any game predicted by our model that has above a 75% chance of winning will only then be classified as a ‘sure win.’ This cut-off seems like a fair assumption because, assuming the model actually worked in the way we wanted it to, every four games you placed a bet on you’d only lose one of those four. And when you factor in even some average odds you’d almost always come out profitable by a long shot if that was the case.

This model has a pretty reasonable success rate even when this high cut-off is put in place however, it suffers from the same ‘problem’ that all our other predictive models do (discussed after all the predictive models have been ‘showcased’).

K-Nearest Neighbours

After our Logistic Regression model came out as such a success we decided that the next best step would be to try a K-Nearest Neighbors model to see if it’s more or less accurate when compared to our previous predictive model.

library(class)
AFL.knn.pred <- knn(AFL.train[,c("Disposals", "Marks", "Inside50s", "Clearances", "Clangers", "ContendedPossessions", "MarksInside50")], AFL.test[,c("Disposals", "Marks", "Inside50s", "Clearances", "Clangers", "ContendedPossessions", "MarksInside50")], AFL.train$WinLoss, 
    k = 3)
table(AFL.knn.pred, AFL.test$WinLoss)
            
AFL.knn.pred   0   1
           0 139  39
           1  68 168
(139+167)/(139+40+68+167)
[1] 0.7391304

And, perhaps unsurprisingly, the K-Nearest Neighbors model resulted in a predicted probability which is only roughly 4% behind the Logistic Regression model. This shows again that our models are consistently predicting at a much better rate then a 50/50 guess. However, this model uses the same set of variables and thus suffers from the same problem as the previous.

Decision Tree(s)

We’d done a Logistic Regression and a K-Nearest Neighbors model, so it really only made sense to finish the trio by attempting to create a Decision Tree or Many Forests model.

library(rpart)
AFL.num.tree <- rpart(WinLoss ~  Disposals + Marks + Behinds + Inside50s + Clearances + Clangers + ContendedPossessions + ContestedMarks + MarksInside50, 
                  AFL_clean)
AFL.num.tree.pred = predict(AFL.num.tree, AFL.test)
AFL.num.tree.pred <- AFL.num.tree.pred[,2]
#head(AFL.num.tree.pred)
AFL.num.tree.class <- rep(0, length(AFL.num.tree.pred))
AFL.num.tree.class[AFL.num.tree.pred > 0.75] <- 1
table(AFL.num.tree.class, AFL.test$WinLoss)
                  
AFL.num.tree.class   0   1
                 0 172  70
                 1  35 137
(172 + 137)/(172+70+35+137)
[1] 0.7463768

To our surprise the Many Forests model actually predicted with an almost identical success rate as the K-Nearest Neighbors model did. I guess this just shows that our variables must have quite even splits with respect to wins and losses. Again, this model suffers from the same problem as the previous two.

However, this model wasn’t as useless as the previous two models because we can actually visualize the different splits that the Decision Tree uses which gives us valuable insight into how different combinations of variables interact to either improve your chances of winning or hurt them.

library(rpart.plot)
library(randomForest)
library(partykit)
plot(as.party(AFL.num.tree))

The Decision Tree diagram is one of the more valuable things to come out of the analysis with respect to our goal to predict the outcome of future games. By just following the right most path of the diagram we can see that more Inside50s paired with more MarksInside50 is a ‘recipe’ for a very high chance of winning; which seems quite obvious. By following the left most path we can see the complete opposite, fewer Inside50s paired with fewer MarksInside50 results more often then not in a lower chance of winning; which again seems quite obvious.

This diagram only really starts to get interesting when you look at the combinations of the many different variables. For example if a team isn’t getting a lot of Inside50s but is getting a lot of MarksInside50, and then also the team has a decent amount of Marks overall then they suddenly have a much better chance at winning.

This diagram might be especially useful for something like half-time betting. If two teams are almost neck to neck at half-time but then one team is following one of these ‘paths’ that lead to a higher proportion of wins then it might be in your best interest to bet on that team over their opposition. In no way is it a perfect set of paths that lead to victory for a team however, using it is certainly a little more useful then blindly picking one team or the other.

The Problem

Perhaps a little foresight into what our models are actually doing would’ve been useful and saved us a lot of sort of wasted time. The problem with all our ‘predictive’ models is that they’re not actually really predicting anything useful. All they are doing is classifying a game as either a win or a loss using given the post-game statistics for a team. Statistics which can only be collected once a game has finished, not before a game has been played which was the whole ‘problem’ we set out to solve.

This makes our models sort of useless compared to the way we’d imaged them but not completely useless. The fact that they could predict three out of every four games correct with just the variables we gave them to work with reinforces the fact that some sort of correlation exists between those variables and a team winning a game. To actually solve our problem it might have been a lot more useful to be looking at quarter-time or half-time teams statistics and then seeing how those relate to a win or a loss.

Plotting

After our ‘predictive’ models really didn’t help us with what we wanted to achieve we had to take a step back and think about a more specific way of analyzing the data which actually somewhat helps in predicting the outcomes of future matches. We thought of two main things that we were curious to look into:

  1. How often are individual teams winning & losing within our data set, and how does this compare to their performance in the current 2017 season?

The first thing we needed to do was separate the total wins and loses with respect to each team throughout the whole data set. Then we put them into descending order because that’s how we intended them to be shown within our plots we planned on making.

table_all <- table(AFL_clean$Team, AFL_clean$WinLoss)
all_freq <- as.data.frame.matrix(table_all)
all_freq <- all_freq[order(all_freq$`1`, decreasing = TRUE),]
#all_freq

This wasn’t the end of the story though. Simply graphing each team’s total frequency of wins and losses and then ordering them by number of wins would be completely unfair. Some teams have just played more games than other teams which means they’d by default probably have more wins because they’ve simply played more games. Thus, to show them fairly next to each other we have to get them in terms of their win/loss percentage or proportion. That is, out of all the games they’ve played what fraction of them did they win, and what fraction did they lose?

win_perc <- all_freq$`1` / (all_freq$`0` + all_freq$`1`)
loss_perc <- 1 - win_perc
all_perc <- cbind(all_freq, loss_perc, win_perc)
all_perc <- all_perc[3:4]
colnames(all_perc) <- c("Loss Percentage", "Win Percentage")
#all_perc
all_perc_decr <- all_perc[order(all_perc$`Win Percentage`, decreasing = TRUE),]
#all_perc_decr
mosaicplot(all_perc_decr, col=c(rgb(215/255,25/255,28/255),rgb(166/255,217/255,106/255)), main = "Total Win vs Loss Percentage for each team (2012-2016) [Descending Order]", cex.axis = 1.2)

Here we’ve just split the above plot in half so it’s easier to see and what we’ve done is compared the rank of the teams in our descending win percentage plot from the 2012 - 2016 data against the current 2017 ladder positions (as of 31-05-2017). A green dot next to a team represents them being in the same half of both the 2017 ladder and our 2012 - 2016 descending win percentage rankings. As you might have noticed we by doing this we only managed to place just over half of the teams in their correct halves, 56% to be exact. However, quite a few of the teams are only off by a one or two positions, and the seasons not finished yet so there’s always room for the teams to do the natural thing and converge on their relative historical rankings.

Not to mention this season of AFL has just been particularly unpredictable; teams who’ve generally been on top have fallen and teams who’ve generally been at the bottom have started to rise. Just look at GWS; from 2012 - 2016 they have the fourth lowest win percentage yet they’re currently second on the ladder. This probably comes down to them being a young team and each year their players are getting exponentially more experienced and playing better as a result, but it’s interesting nonetheless.

Team Win Percentage vs 2017 Ladder

Team Win Percentage vs 2017 Ladder

  1. Are certain teams winning a lot more often at certain venues, and does this have anything to do with how much experience they have playing at these venues?

To answer these questions we first had to determine which venues all of the teams have played at so we have a decent sample size of matches to work with, and to make sure each team is represented within each venue’s analysis. To do this we plotted our score variable for each team filtered by each different venue we have in our data set.

library(lattice)
barchart(AFL_clean$Score ~ AFL_clean$Team | AFL_clean$Venue)

Using this plot and comparing the total amount of games played at each venue we narrowed the venues we wanted to look at down to Subiaco, Adelaide Oval, M.C.G, S.C.G and The Gabba. These locations were also picked as they’re the most popular venues for Perth, Adelaide, Melbourne, Sydney and Brisbane respectively; meaning if there’s any correlation between the teams playing on their home ground we should be able to see it across all the main venues.

To do this we had to extract the wins and losses that each team has had when only looking at a specific venue.

venueFilter <- function(venue){
  
  AFL_venue <- AFL_clean[AFL_clean$Venue == venue,]
  venue_table <- table(AFL_venue$Team, AFL_venue$WinLoss)
  venue_df <- as.data.frame.matrix(venue_table)
  venue_freq <- venue_df[order(venue_df$`1`, decreasing = TRUE),]
  
  win_perc <- venue_freq$`1` / (venue_freq$`0` + venue_freq$`1`)
  loss_perc <- 1 - win_perc
  all_perc <- cbind(venue_freq, loss_perc, win_perc)
  all_perc <- all_perc[3:4]
  colnames(all_perc) <- c("Loss Percentage", "Win Percentage")
  #all_perc
  
  team_total <- (venue_freq$`0` + venue_freq$`1`)
  game_totals <- sum(team_total)
  team_proportion <- team_total / game_totals
  venue_freq <- cbind(venue_freq, team_proportion)
  #venue_freq
  
  all_venue <- cbind(venue_freq, all_perc)
  all_venue <- all_venue[order(all_venue$`Win Percentage`, decreasing = TRUE),]
  return(all_venue)
}
subiaco <- venueFilter("Subiaco")
syd.showground <- venueFilter("Sydney Showground")
gabba <- venueFilter("Gabba")
mcg <- venueFilter("M.C.G.")
scg <- venueFilter("S.C.G.")
adl.oval <- venueFilter("Adelaide Oval")
carrara <- venueFilter("Carrara")
docklands <- venueFilter("Docklands")
ftbl.park <- venueFilter("Football Park")

I’ve just included the code for only looking at The Gabba but every single plot was made using the same code and just replacing the venue with whichever one you were interested in.

library(ggplot2)
gabba.tprop <- gabba[3]
bp <- ggplot(gabba.tprop, aes(x="", y=team_proportion, fill=row.names(gabba.tprop)) ) + geom_bar(width = 1, stat = "identity") + scale_fill_manual(values=c("#999999", "#E69F00", "#56B4E9", "#8fc0a0", "#cc9b5b", "#71529b", "#7bf68d", "#96270f", "#1e46be", "#b60edc", "#678dbd", "#4eb20b", "#5f37e5", "#02339b", "#a7a4df", "#2b681e", "#2c3f7c", "#d9ed8f"))
bp

pie <- bp + coord_polar("y", start=0)
pie

gabba.wperc <- gabba[4:5]
#subiaco.wperc
mosaicplot(gabba.wperc, col=c(rgb(215/255,25/255,28/255),rgb(166/255,217/255,106/255)), main = "Total Win vs Loss Percentage for each team at Subiaco Oval (2012-2016) [Descending Order]", cex.axis = 1.2)

So you don’t have to look at the same code ten times and squint trying to look at the downsized R-output I’ve just included the slides I made from the power point, but like I said before it was all made using the exact same code above.

Subiaco

Subiaco is the home ground of Fremantle and West Coast and as seen on the first plot they’re also in the top two places for win percentage when just looking at games played at Subiaco. The second plot shows the fraction of games that each individual team has played at Subiaco and by no surprise Fremantle and West Coast together make up about 60% of the games. This leaves the other 16 teams to share the remaining 40% of games between themselves, which equates to about six games each.

After looking at these plots it makes a lot more sense that Fremantle and West Coast are topping the win percentage at Subiaco because they’re so disproportionately represented at the venue. Not to mention that all the other teams have to fly quite a distance to get to Subiaco, this probably contributes heavily to the fact that the majority of the teams have less than a 50% win rate.

Relating back to our goal of predicting future outcomes of games, this gives us some very useful information. When Brisbane, Collingwood, Melbourne or St Kilda are playing at Subiaco it’s probably a good idea to bet against them, especially if they’re up against Fremantle or West Coast.

Adelaide Oval

Looking at Adelaide Oval we see a similar trend with the home teams, Adelaide and Port Adelaide, together making up about 60% of the total games played on their home turf. Similarily, this leaves the other 16 teams to share the remaining 40% of games, but this time that only equates to about four games each (as less games have been played at Adelaide Oval compared to Subiaco).

When we look at the win percentages there’s definitely some improvement as Port Adelaide have moved from overall being ranked 10th to being ranked 6th however, Adelaide has stayed stagnant in the same position. Interestingly we also have a couple different teams dominating the top positions and a couple more teams having not ever one a game at the venue.

Using this information we could say that if Port Adelaide is playing a team that’s on par with them, but they’re playing at Adelaide Oval, then they might just have that extra edge from the home advantage and come out on top. We see Brisbane, Collingwood and St Kilda being repeated on the bottom of the list with no wins what so ever, again, making your chances of winning when betting against them (if they’re playing at Subiaco or Adelaide Oval) that much better.

M.C.G.

The M.C.G is a much more popular venue with a total of 500 games played at it, which is more games than Subiaco and Adelaide Oval combined. It’s also in Melbourne which is within a couple hours drive of over half the AFL teams. These factors contribute the the M.C.G having a much more even spread of team’s total games played at the venue, as seen on the second plot each close-by team holds about 5% - 10% of the games instead of 30%.

However, even with the spread being a lot more even and no team(s) being extremelly disproportionately represented, we still see this trend of the surrounding, close-by teams playing better. Every single on of the close-by teams has either held their position or improving dramatically, with the Western Bulldogs climbing nearly half of the ranks. However, we still see Brisbane in the bottom few ranks adding to the pile of evidence that you should probably always bet against them if they’re playing an away game.

S.C.G.

The S.C.G has the smallest amount of games played at is compared to all the other venues we’re looking at, only 90 games it total which is less than 1/5th of the total games played at the S.C.G. This sort of explains the extreme dispoportionality that we see at the venue, with Sydney having played about half of all the games played there. Interestingly though GWS plays just about as often as an away team despite them being situated quite close by to the venue.

The win percentages are quite skewed here due to the small amount of games that have been played there by away teams, each team has only played about two or three games. This makes it hard to get any useful information from the win percentage plot however, it has to be noted that Brisbane has no wins again despite actually being roughly one of the closest away teams to the venue.

The Gabba

Looking at The Gabba we can fully confirm that things really aren’t looking too great for Brisbane. Despite it being their home ground and then having played about 50% of the total games at the venue they are still very close to the bottom of the ranks. Again it is strange how Gold Coast is within an hours drive of The Gabba yet they still play there as often as an away team.

The Gabba doesn’t have many more games played at it than the S.C.G however, it’s interesting that the skew is sort of in the completely opposite direction, with nearly half the teams having a (or close to a) 100% win rate. This makes sense when you consider that most of the teams would be facing up against the home team, Brisbane, who we’ve already established probably aren’t the best team to be putting your money on.


Conclusions

We set out to achieve quite an ambitious goal; create some sort of really intelligent statistical model for predicting who might win any given AFL match. Unfortunately, we didn’t really come close to creating this perfect model we dreamed of however, our analysis as a whole was no where near useless, even considering what we set out to achieve.

Our predictive models themselves were pretty useless as they require a game to have already finished to actually classify the game with some success. However, they’ve shown that the explanatory variables they contain must have some sort of significant impact on whether a team will win or lose. We also managed to get that quite insightful tree diagram out of one of our predictive models which is useful for determining what combinations of ‘behaviors’ within an AFL game are more likely to lead to a team winning.

Ultimately, the many, many plots that we created based around the various different venues are some of the most useful parts of the analysis with respect to our actual goal. Whenever two teams are playing at any one of those venues we can glance over at our plots and have quite a good overview of how well each team plays relative to one another. It’s nothing close to predicting any team will win with 95% confidence but it’s definitely useful information, and provides at least a little bit of an advantage compared to going into the bet completely blind.

I think that we’re held back a little bit by our data, we only have the statistics for after a game has been played. If we had access to some sort of ‘real-time’ data, where we could see a list of these statistics at certain times within the game I think it would be much more valuable to what we wanted to achieve (I suspect that’s how big betting companies must do it). Maybe there’s some sort of a trend right before a team is about to make a comeback that you could pick out? However, it would only really be plausible if you had the match data over time.

I think that to improve our project we should have definitely had a more in depth planning process and approached the data set after having a very clear, concise goal of what needs to get done in mind.

Overall, we’ve come out with some useful resources for assisting in predicting a winning team and climbed a mountain of R-code along the way - so I’d say it was a success.



Appendix

AFLstats <- read.csv("stats.csv")
AFL <- AFLstats
AFL <- AFL[c(1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,25,27,28,29,30,31)]
colnames(AFL)
sum_names <- colnames(AFL)[10:28]
sum_names
all_teams <- unique(as.character(AFL$Team))
ad <- AFL[AFL$Team == all_teams[1],]
bl <- AFL[AFL$Team == all_teams[2],]
clt <- AFL[AFL$Team == all_teams[3],]
clwd <- AFL[AFL$Team == all_teams[4],]
esdn <- AFL[AFL$Team == all_teams[5],]
fd <- AFL[AFL$Team == all_teams[6],]
glng <- AFL[AFL$Team == all_teams[7],]
gc <- AFL[AFL$Team == all_teams[8],]
gws <- AFL[AFL$Team == all_teams[9],]
ht <- AFL[AFL$Team == all_teams[10],]
mlb <- AFL[AFL$Team == all_teams[11],]
nmlb <- AFL[AFL$Team == all_teams[12],]
pad <- AFL[AFL$Team == all_teams[13],]
rmd <- AFL[AFL$Team == all_teams[14],]
sk <- AFL[AFL$Team == all_teams[15],]
syd <- AFL[AFL$Team == all_teams[16],]
wc <- AFL[AFL$Team == all_teams[17],]
wb <- AFL[AFL$Team == all_teams[18],]
cleanAFLdata <- function(team){
  
  library(plyr)
  team_stats <- ddply(team, .(Date), numcolwise(sum))
  team_stats <- team_stats[c(1,5:23)]
  team_keep <- team[match(unique(team$Date), team$Date),]
  team_keep <- team_keep[1:9]
  team_clean <- merge(team_keep, team_stats)
  team_clean <- team_clean[team_clean$WinLoss != "D",]
  team_clean$WinLoss <- droplevels(team_clean$WinLoss)
  levels(team_clean$WinLoss)[levels(team_clean$WinLoss)=="L"] <- 0
  levels(team_clean$WinLoss)[levels(team_clean$WinLoss)=="W"] <- 1
  team_clean$WinLoss <- factor(team_clean$WinLoss)
  
  return(team_clean)
  
}
syd_clean <- cleanAFLdata(syd)
head(syd_clean)
ad_clean <- cleanAFLdata(ad)
bl_clean <- cleanAFLdata(bl)
clt_clean <- cleanAFLdata(clt)
clwd_clean <- cleanAFLdata(clwd)
esdn_clean <- cleanAFLdata(esdn)
fd_clean <- cleanAFLdata(fd)
glng_clean <- cleanAFLdata(glng)
gc_clean <- cleanAFLdata(gc)
gws_clean <- cleanAFLdata(gws)
ht_clean <- cleanAFLdata(ht)
mlb_clean <- cleanAFLdata(mlb)
nmlb_clean <- cleanAFLdata(nmlb)
pad_clean <- cleanAFLdata(pad)
rmd_clean <- cleanAFLdata(rmd)
sk_clean <- cleanAFLdata(sk)
syd_clean <- cleanAFLdata(syd)
wc_clean <- cleanAFLdata(wc)
wb_clean <- cleanAFLdata(wb)
AFL_clean <- rbind(ad_clean, bl_clean, clt_clean, clwd_clean, esdn_clean, fd_clean, glng_clean, 
                   gc_clean, gws_clean, ht_clean, mlb_clean, nmlb_clean, pad_clean, rmd_clean,
                   sk_clean, syd_clean, wc_clean, wb_clean)
length(unique(AFL_clean$Team))
AFL.test <- AFL_clean[AFL_clean$Season == 2016,]
head(AFL.test)
AFL.train <- AFL_clean[AFL_clean$Season != 2016,]
head(AFL.train)
plot(AFL.train$WinLoss, AFL.train$Disposals)
library(ggplot2)
qplot(WinLoss, Disposals, data = AFL.train)
source("PairsWithHist.R")
pairs(AFL_clean, lower.panel = panel.pts, upper.panel = panel.cor, diag.panel = panel.hist)
# just 'numerical' variables
AFL.glm.num <- glm(WinLoss ~  Disposals + Marks + Behinds + Inside50s + Clearances + Clangers + ContendedPossessions + ContestedMarks + MarksInside50, 
                  data = AFL.train, family = binomial(logit))
# 'all' variables (Margin excluded because too perfect)
AFL.glm.all <- glm(WinLoss ~  Score + Opposition + Venue + Disposals + Marks + Goals + Behinds + Hitouts + Tackles + Rebound50s + Inside50s + Clearances + Clangers + FreesFor + ContendedPossessions + ContestedMarks + MarksInside50 + OnePercenters + Bounces+GoalAssists, 
                  data = AFL.train, family = binomial)
#summary(AFL.glm.all)
AFL.glm.0 <- glm(WinLoss ~ 1, data = AFL.train, family = binomial(logit))
AFL.glm.fwd <- step(AFL.glm.0, scope = formula(AFL.glm.num), direction = "forward", trace = 0)
summary(AFL.glm.fwd)
AFL.all.pred <- predict(AFL.glm.fwd, newdata = AFL.test, type = "response")
head(AFL.all.pred)
AFL.all.pred.class <- rep(0, length(AFL.all.pred))
AFL.all.pred.class[AFL.all.pred > 0.75] <- 1
table(AFL.all.pred.class, AFL.test$WinLoss)
(188+132)/(188+75+19+132)
library(rpart)
AFL.tree <- rpart(WinLoss ~  Opposition + Venue + Disposals + Marks + Goals + Behinds + Hitouts + Tackles + Rebound50s + Inside50s + Clearances + Clangers + FreesFor + ContendedPossessions + ContestedMarks + MarksInside50 + OnePercenters + Bounces + GoalAssists, 
                  AFL_clean)
#summary(AFL.tree)
library(rpart)
AFL.num.tree <- rpart(WinLoss ~  Disposals + Marks + Behinds + Inside50s + Clearances + Clangers + ContendedPossessions + ContestedMarks + MarksInside50, 
                  AFL_clean)
AFL.num.tree.pred = predict(AFL.num.tree, AFL.test)
AFL.num.tree.pred <- AFL.num.tree.pred[,2]
head(AFL.num.tree.pred)
AFL.num.tree.class <- rep(0, length(AFL.num.tree.pred))
AFL.num.tree.class[AFL.num.tree.pred > 0.75] <- 1
table(AFL.num.tree.class, AFL.test$WinLoss)
(172 + 137)/(172+70+35+137)
library(rpart.plot)
library(randomForest)
library(partykit)
plot(as.party(AFL.tree))
library(rpart)
AFL.teams <- rpart(WinLoss ~  Team + Opposition,
                  AFL_clean)
library(rpart.plot)
library(randomForest)
library(partykit)
plot(as.party(AFL.teams))
library(rpart)
AFL.venue <- rpart(WinLoss ~  Team + Venue,
                  AFL_clean)
library(rpart.plot)
library(randomForest)
library(partykit)
plot(as.party(AFL.venue))
library(rpart.plot)
library(randomForest)
library(partykit)
plot(as.party(AFL.num.tree))
library(class)
AFL.knn.pred <- knn(AFL.train[,c("Disposals", "Marks", "Behinds", "Inside50s", "Clearances", "Clangers", "ContendedPossessions", "ContestedMarks", "MarksInside50")], AFL.test[,c("Disposals", "Marks", "Behinds", "Inside50s", "Clearances", "Clangers", "ContendedPossessions", "ContestedMarks", "MarksInside50")], AFL.train$WinLoss, 
    k = 3)
table(AFL.knn.pred, AFL.test$WinLoss)
(143+168)/(143+39+64+168)
unique(AFL_clean$Venue)
nrow(AFL_clean[AFL_clean$Venue == "Traeger Park",])
table(AFL_clean$Venue)
table_all <- table(AFL_clean$Team, AFL_clean$WinLoss)  # wins and losses for each team

# creating tables of each team and thier wins/losses against opposition
table_ad <- table(ad_clean$Opposition, ad_clean$WinLoss)
table_wc <- table(wc_clean$Opposition, wc_clean$WinLoss)
table_bl <- table(bl_clean$Opposition, bl_clean$WinLoss)
table_clt <- table(clt_clean$Opposition, clt_clean$WinLoss)
table_clwd <- table(clwd_clean$Opposition, clwd_clean$WinLoss)
table_esdn <- table(esdn_clean$Opposition, esdn_clean$WinLoss)
table_fd <- table(fd_clean$Opposition, fd_clean$WinLoss)
table_gc <- table(gc_clean$Opposition, gc_clean$WinLoss)
table_glng <- table(glng_clean$Opposition, glng_clean$WinLoss)
table_gws <- table(gws_clean$Opposition, gws_clean$WinLoss)
# Plotting Mosaics

library(vcd)

mosaicplot(table_all, col=c("red","darkgreen"), main = "Wins&Losses against each team (2012-2016)", xlab = "Team", ylab = "Win or loss")

mosaicplot(table_wc, col=c("red","darkgreen"),main = "West Coast and their wins and losses against each team (2012-2016)",  xlab = "Team", ylab = "Win or loss")

mosaicplot(table_ad, col=c("red","darkgreen"), main = "Adelaide and their wins and losses against each team (2012-2016)",  xlab = "Team", ylab = "Win or loss")

mosaicplot(table_bl, col=c("red","darkgreen"),main = "Brisbane Lions and their wins and losses against each team (2012-2016)",  xlab = "Team", ylab = "Win or loss")

mosaicplot(table_clt, col=c("red","darkgreen"),main = "Carlton and their wins and losses against each team (2012-2016)",  xlab = "Team", ylab = "Win or loss")

mosaicplot(table_clwd, col=c("red","darkgreen"),main = "Collingwood and their wins and losses against each team (2012-2016)",  xlab = "Team", ylab = "Win or loss")
all_freq <- as.data.frame.matrix(table_all)
all_freq <- all_freq[order(all_freq$`1`, decreasing = TRUE),]
all_freq
mosaicplot(all_freq, col=c("red","darkgreen"), main = "Win vs Loss Frequency for each team (2012-2016)", cex.axis = 1.2)
win_perc <- all_freq$`1` / (all_freq$`0` + all_freq$`1`)
loss_perc <- 1 - win_perc
all_perc <- cbind(all_freq, loss_perc, win_perc)
all_perc <- all_perc[3:4]
colnames(all_perc) <- c("Loss Percentage", "Win Percentage")
all_perc
mosaicplot(all_perc, col=c("red","darkgreen"), main = "Win vs Loss Percentage for each team (2012-2016)", cex.axis = 1.2)
all_perc_decr <- all_perc[order(all_perc$`Win Percentage`, decreasing = TRUE),]
all_perc_decr
mosaicplot(all_perc_decr, col=c(rgb(215/255,25/255,28/255),rgb(166/255,217/255,106/255)), main = "Total Win vs Loss Percentage for each team (2012-2016) [Descending Order]", cex.axis = 1.2)
library(lattice)
barchart(AFL_clean$Score ~ AFL_clean$Team | AFL_clean$Venue)
venueFilter <- function(venue){
  
  AFL_venue <- AFL_clean[AFL_clean$Venue == venue,]
  venue_table <- table(AFL_venue$Team, AFL_venue$WinLoss)
  venue_df <- as.data.frame.matrix(venue_table)
  venue_freq <- venue_df[order(venue_df$`1`, decreasing = TRUE),]
  
  win_perc <- venue_freq$`1` / (venue_freq$`0` + venue_freq$`1`)
  loss_perc <- 1 - win_perc
  all_perc <- cbind(venue_freq, loss_perc, win_perc)
  all_perc <- all_perc[3:4]
  colnames(all_perc) <- c("Loss Percentage", "Win Percentage")
  #all_perc
  
  team_total <- (venue_freq$`0` + venue_freq$`1`)
  game_totals <- sum(team_total)
  team_proportion <- team_total / game_totals
  venue_freq <- cbind(venue_freq, team_proportion)
  #venue_freq
  
  all_venue <- cbind(venue_freq, all_perc)
  all_venue <- all_venue[order(all_venue$`Win Percentage`, decreasing = TRUE),]
  return(all_venue)

}
subiaco <- venueFilter("Subiaco")
syd.showground <- venueFilter("Sydney Showground")
gabba <- venueFilter("Gabba")
mcg <- venueFilter("M.C.G.")
scg <- venueFilter("S.C.G.")
adl.oval <- venueFilter("Adelaide Oval")
carrara <- venueFilter("Carrara")
docklands <- venueFilter("Docklands")
ftbl.park <- venueFilter("Football Park")
sum(gabba[1] + gabba[2])
gabba
library(ggplot2)

gabba.tprop <- gabba[3]
bp <- ggplot(gabba.tprop, aes(x="", y=team_proportion, fill=row.names(gabba.tprop)) ) + geom_bar(width = 1, stat = "identity") + scale_fill_manual(values=c("#999999", "#E69F00", "#56B4E9", "#8fc0a0", "#cc9b5b", "#71529b", "#7bf68d", "#96270f", "#1e46be", "#b60edc", "#678dbd", "#4eb20b", "#5f37e5", "#02339b", "#a7a4df", "#2b681e", "#2c3f7c", "#d9ed8f"))
bp

pie <- bp + coord_polar("y", start=0)
pie
gabba.wperc <- gabba[4:5]
#subiaco.wperc

mosaicplot(gabba.wperc, col=c(rgb(215/255,25/255,28/255),rgb(166/255,217/255,106/255)), main = "Total Win vs Loss Percentage for each team at Subiaco Oval (2012-2016) [Descending Order]", cex.axis = 1.2)

LS0tCnRpdGxlOiAiQUZMIFByb2plY3QgUmVwb3J0IgphdXRob3I6ICJUcm95IEVuZ2VsaGFyZHQgJiBBZGFtIEhhcnRtYW5uIgpvdXRwdXQ6CiAgd29yZF9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCioqKgoKMS4gVGhpcyBhc3NpZ25tZW50IGlzIG15L291ciBvd24gb3JpZ2luYWwgd29yaywgZXhjZXB0IHdoZXJlIEkvd2UgaGF2ZSBhcHByb3ByaWF0ZWx5IGNpdGVkIHRoZSBvcmlnaW5hbCBzb3VyY2UgKGFwcHJvcHJpYXRlIGNpdGF0aW9uIG9mIG9yaWdpbmFsIHdvcmsgd2lsbCB2YXJ5IGZyb20gZGlzY2lwbGluZSB0byBkaXNjaXBsaW5lKS4KMi4gVGhpcyBhc3NpZ25tZW50IGhhcyBub3QgcHJldmlvdXNseSBiZWVuIHN1Ym1pdHRlZCBpbiBhbnkgZm9ybSBmb3IgdGhpcyBvciBhbnkgb3RoZXIgdW5pdCwgZGVncmVlIG9yIGRpcGxvbWEgYXQgYW55IHVuaXZlcnNpdHkgb3Igb3RoZXIgaW5zdGl0dXRlIG9mIHRlcnRpYXJ5IGVkdWNhdGlvbi4KMy4gSS93ZSBhY2tub3dsZWRnZSB0aGF0IGl0IGlzIG15IHJlc3BvbnNpYmlsaXR5IHRvIGNoZWNrIHRoYXQgdGhlIGZpbGUgSS93ZSBoYXZlIHN1Ym1pdHRlZCBpczogYSkgcmVhZGFibGUsIGIpIHRoZSBjb3JyZWN0IGZpbGUgYW5kIGMpIGZ1bGx5IGNvbXBsZXRlLgoKKioqCgojIyBfX0ludHJvZHVjdGlvbl9fCgpUaGUgQUZMIChBdXN0cmFsaWFuIEZvb3RiYWxsIExlYWd1ZSkgaXMgdGhlIGhpZ2hlc3QgbGVhZ3VlIHRoYXQgZXhpc3RzIGZvciBhIHBvcHVsYXIgc3BvcnQgd2l0aGluIEF1c3RyYWxpYSwgY2FsbGVkIGZvb3RiYWxsIChzb21ldGltZXMgJ2Zvb3R5JyBvciAnb3p6aWUgcnVsZXMnKS4gTGlrZSBtb3N0IHNwb3J0cyB0aGF0IGFyZSBwb3B1bGFyIGFyb3VuZCB0aGUgZ2xvYmUsIGEgbHVkaWNyb3VzIGJldHRpbmcgaW5kdXN0cnkgZXhpc3RzIGFyb3VuZCB0aGUgQUZMIHdoZXJlIHlvdSdyZSByZXdhcmRlZCBmb3IgcGlja2luZyB0aGUgd2lubmluZyB0ZWFtKHMpLCBhbmQgbm90LXNvLXJld2FyZGVkIGlmIHlvdSBoYXBwZW4gdG8gcGljayB0aGUgbG9zaW5nIHRlYW0ocykuIFRoZSBBRkwsIGFuZCB0aGlzIGJldHRpbmcgZXRob3MsIGlzIHNvbWV3aGF0IGluZ3JhaW5lZCBpbnRvIEF1c3RyYWxpYW4gY3VsdHVyZSB0byB0aGUgcG9pbnQgdGhhdCBpdCdzIGNvbW1vbiBwbGFjZSBmb3IgZ3JvdXBzIG9mIGZyaWVuZHMvY29sbGVhZ3VlcyB0byBhbGwgcGl0Y2ggaW4gYW5kIHJ1biBhICd0aXBwaW5nIGNvbXBldGl0aW9uLicgQSBjb21wZXRpdGlvbiB3aGVyZSBlYWNoIHBhcnRpY2lwYW50IGlzIHJhbmtlZCBieSB0aGUgYW1vdW50IG9mIHdpbnMgdGhhdCB0aGV5IGNhbiBzdWNjZXNzZnVsbHkgcHJlZGljdCBkdXJpbmcgYSBzZWFzb24gb2YgZm9vdGJhbGwsIGFuZCBpcyByZXdhcmRlZCBhY2NvcmRpbmcgdG8gdGhlaXIgaW5kaXZpZHVhbCByYW5rcy4gT2YgY291cnNlLCB3aGVyZSB0aGVyZSBhcmUgcmV3YXJkcyB0byBiZSByZWFwZWQgdGhlcmUncyBtb3JlIG9mdGVuIHRoYW4gbm90IGEgZ3JvdXAgb2YgZGV0ZXJtaW5lZCBwZW9wbGUsIHVzaW5nIGFsbCBtZWFucyBwb3NzaWJsZSwgaW4gb3JkZXIgdG8gZ3JhYiB0aGUgYmlnZ2VzdCBwaWVjZSBvZiB0aGUgcGllLiBXZSBkZWNpZGVkIHRoYXQsIGFybWVkIHdpdGggYSBzZW1lc3RlcidzIGtub3dsZWRnZSBvZiBEYXRhIFNjaWVuY2UgYXMgb3VyIHRvb2xzLCBpdCB3b3VsZCBiZSBpbiBvdXIgYmVzdCBpbnRlcmVzdCB0byBqb2luIHRoaXMgZ3JvdXAgYW5kIHRlc3Qgb3VyIGx1Y2sgYXQgZ3JhYmJpbmcgYSBwaWVjZSBvZiB0aGUgcGllLgoKVGhpcyByZXBvcnQgb3V0bGluZXMgb2YgYWxsIHRoZSBuZWNlc3Nhcnkgc3RlcHMgd2UgdG9vayB0byB0cnkgJ3NlY3VyZSB0aGlzIHBpZWNlIG9mIHRoZSBwaWU7JyB0aGF0IGlzLCBwcmVkaWN0IHRoYXQgd2hlbiBhbnkgZ2l2ZW4gQUZMIG1hdGNoIGlzIHBsYXllZCB3aGljaCB0ZWFtIHdvdWxkIGJlIG1vcmUgbGlrZWx5IHRvIHdpbi4KCkR1cmluZyB0aGUgcmVwb3J0IHdlJ2xsIGV4cGxvcmU6ICAKCiogV2hlcmUgdGhlIGRhdGEgY29tZXMgZnJvbSwgYW5kIHdoYXQgaXQgYWN0dWFsbHkgcmVwcmVzZW50cwoqIFRoZSBzdGVwcyB3ZSB0b29rIGluIGNsZWFuaW5nIG91ciBkYXRhLCB3cmFuZ2xpbmcgaXQgaW50byAncHJvcGVyJyBmb3JtLCBhbmQgZGVhbGluZyB3aXRoIGFueSBwb3NzaWJsZSB1bmRlcmx5aW5nIGFzc3VtcHRpb25zCiogVGhlIG1hbnkgZGlmZmVyZW50IHdheXMgd2Ugd2VudCBhYm91dCBhbmFseXppbmcgb3VyIGRhdGEgdG8gdHJ5IGdldCB0aGUgbW9zdCBhY2N1cmF0ZSBwcmVkaWN0aW9uIGZvciB0aGUgd2lubmluZyB0ZWFtCiogQSBzdW1tYXJ5IG9mIGhvdyB3ZWxsIG91ciBhbmFseXNpcyBoZWxwZWQgdXMgaW4gcmVhY2hpbmcgdGhlIGdvYWwgd2Ugc2V0IG91dCB0byBhY2hpZXZlLCBwbHVzIGEgZGlzY3Vzc2lvbiBvbiB0aGUgbWFueSBkcmF3YmFja3Mgd2UgbWV0IGFuZCBpbXByb3ZlbWVudHMgd2UgY291bGQgbWFrZQoKKioqCgojIyBfX0Rlc2NyaXB0aW9uIG9mIERhdGEgYW5kIE1ldGhvZHNfXwojIyMgVGhlIERhdGEKCi0gUHJlc2VudHMgYSBjbGVhciwgY29uY2lzZSBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YSwgc291cmNlcywgYW5kIHN0YXRpc3RpY2FsIG1ldGhvZHMgdXNlZCBpbiB0aGUgcmVwb3J0ICAKLSBJbXBvcnRhbnQgYXNwZWN0cyBvZiBkYXRhIHNldCBhcmUgY2xlYXJseSBleHBsYWluZWQgIAotIFN1bW1hcnkgdGFibGVzIChpZiBwcmVzZW50ZWQpIGFyZSB1bmRlcnN0YW5kYWJsZSBhbmQgc2VsZi0gY29udGFpbmVkICAKClRoZSBkYXRhIHRoYXQgd2UgdXNlZCB0byBnbyBhYm91dCB0aGlzIHByZWRpY3Rpb24gaXMgYSBkYXRhIHNldCB0aGF0IHdhcyByZXRyaWV2ZWQgZnJvbSBbS2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tKSAoW0FGTCBEYXRhc2V0XShodHRwOi8vd3d3LmthZ2dsZS5jb20vc3RvbmV5NzEvYWZsc3RhdHMvdmVyc2lvbi8xKSkuIFdoaWNoIGlzIGFjdHVhbGx5IGp1c3QgYW4gYWdncmVnYXRlZCBjb2xsZWN0aW9uIChhcyBhIC5jc3YgZmlsZSkgb2YgYWxsIHRoZSBwbGF5ZXIgZGF0YSBhdmFpbGFibGUgZnJvbSB0aGUgW0FGTCBUYWJsZXNdKGh0dHA6Ly9hZmx0YWJsZXMuY29tL2FmbC9hZmxfaW5kZXguaHRtbCkgd2Vic2l0ZTsgZnJvbSBBRkwgc2Vhc29ucyAyMDEyIC0gMjAxNiAoaW5jbHVzaXZlKS4gVGhlIGRhdGEgc2V0IGhhcyBiZWVuIGFuYWx5c2VkIG9uY2UgYmVmb3JlIGJ5IHRoZSBhdXRob3IvdXBsb2FkZXIgb2YgdGhlIGRhdGEsIChbTWljaGVhbFN0b25lXShodHRwczovL3d3dy5rYWdnbGUuY29tL3N0b25leTcxL21vc3QtaW5mbHVlbnRpYWwtcGxheWVycy1pbi10aGUtYWZsL2NvZGUpLiBIZSBleHBsb3JlZCBoaXMgZGF0YSBhbmQgbW9kZWxlZCBpdCBhY2NvcmRpbmdseSBpbiBhbiBlZmZvcnQgdG8gdHJ5IGZpbmQgdGhlICdtb3N0IGluZmx1ZW50aWFsJyBwbGF5ZXIgaW4gdGhlIEFGTCBmcm9tIHNlYXNvbnMgMjAxMiAtIDIwMTYuCgpgYGB7cn0KQUZMc3RhdHMgPC0gcmVhZC5jc3YoInN0YXRzLmNzdiIpCmBgYAoKT2YgY291cnNlIHdoZW4gd2UgYXJlIGFuYWx5emluZyB0aGlzIGRhdGEgc2V0IHdlIGFyZSBtYWtpbmcgdGhlIGFzc3VtcHRpb24gdGhhdCBNaWNoZWFsU3RvbmUsIHRoZSBhdXRob3Igb2YgdGhlIGRhdGEgc2V0LCBoYXMgbWFkZSBubyBlcnJvcnMgd2hpbHN0IGNvbGxlY3RpbmcgYWxsIHRoaXMgZGF0YSBhbmQgbWVyZ2luZyBpdCB0b2dldGhlci4gU2VlaW5nIGFzIHRoZSBkYXRhIGhhcyBubyBtaXNzaW5nIHZhbHVlcyAoc2VlbiBpbiB0aGUgcGxvdChzKSBiZWxvdykgYW5kIGR1cmluZyBvdXIgYW5hbHlzaXMgd2Ugd2VyZSBsZWFkIHRvIHJlYXNvbmFibGUsIGxvZ2ljYWwgb3V0Y29tZXM7IGl0IHNlZW1zIGxpa2UgdGhpcyBpcyBhIGZhaXIgYXNzdW1wdGlvbiB0byBtYWtlLgoKYGBge3J9CnJlcXVpcmUoSG1pc2MpCnBsb3QoZGVzY3JpYmUoQUZMc3RhdHMpKQpgYGAKClRoZSBkYXRhIHNldCBoYXMgKio0NTQ5NiByb3dzKiogYW5kICoqMzIgY29sdW1ucyoqLiBFYWNoIHJvdyByZXByZXNlbnRzIGEgcGxheWVyJ3MgcGVyZm9ybWFuY2Ugd2l0aGluIGFuIEFGTCBtYXRjaCBiZXR3ZWVuIDIwMTIgYW5kIDIwMTY7IGluY2x1ZGluZyBpbmZvcm1hdGlvbiBhYm91dCB3aGljaCByb3VuZCB0aGUgZ2FtZSBpbiBxdWVzdGlvbiB3YXMsIGlmIHRoZSB0ZWFtIG9uZSBvciBsb3N0IGFuZCBhIGJ1bmNoIG9mIG51bWVyaWNhbCBzdGF0aXN0aWNzIHJlbGF0aW5nIHRvIHRoZSBpbmRpdmlkdWFsIHBlcmZvcm1hbmNlOyBnaXZpbmcgdXMgYSB0b3RhbCBvZiBwb3RlbnRpYWxseSAzMiBkaWZmZXJlbnQgdmFyaWFibGVzIHRvIHdvcmsgd2l0aCBmb3IgZWFjaCBpbmRpdmlkdWFsIHBsYXllci4gIAoKSGVyZSdzIGEgcXVpY2sgcGVlayBhdCB0aGUgZmlyc3Qgc2l4IHJvd3Mgb2Ygb3VyIGRhdGEgc2V0LiBIZXJlIHdlIGNhbiBzZWUgdGhhdCB3ZSdyZSBsb29raW5nIGF0IEFkZWxhaWRlJ3MgZmlyc3QgZ2FtZSBpbiB0aGUgMjAxNiBzZWFzb24sIHdoZXJlIHRoZXkgcGxheWVkIGFnYWluc3QgTm9ydGggTWVsYm91cm5lIGF0IHRoZSBEb2NrbGFuZHMgc3RhZGl1bSBhbmQgbG9zdC4gVGhlIG1ham9yaXR5IG9mIHRoZSBpbmZvcm1hdGlvbiBsZWZ0IGluIHRoZXNlIHJvd3MganVzdCBhc3NpZ25zIGFuIGludGVnZXIgbnVtYmVyIHRvIGEgc3BlY2lmaWMgZ2FtZSBzdGF0aXN0aWMsIGluZGljYXRpbmcgaG93IG1hbnkgdGltZXMgYSBwbGF5ZXIgbWFuYWdlZCB0byBtZWV0IHRoZSBjb25kaXRpb24gb2YgdGhhdCBzdGF0aXN0aWMuIFRoaXMgaXMgdXNlZnVsIGluIGdpdmluZyB1cyBhbiBpbmRpY2F0aW9uIG9mIGhvdyB3ZWxsIG9uZSBwbGF5ZXIgbWlnaHQgaGF2ZSBiZWVuIHBsYXlpbmcgaW4gYSBjZXJ0YWluIGdhbWUgd2l0aCByZXNwZWN0IHRvIGFub3RoZXIgcGxheWluZyAoY29tcGFyaW5nIG9ubHkgdGhlIHZhcmlhYmxlcyB3ZSBoYXZlIHRvIHdvcmsgd2l0aCBvZiBjb3Vyc2UpLgoKYGBge3J9CkFGTHN0YXRzWzE6NSwxOjhdCmBgYAoKCkJlbG93IGlzIGFuIG91dGxpbmUgb2YgdGhlc2UgZGlmZmVyZW50IHZhcmlhYmxlcy4gQXMgeW91IGNhbiBzZWUgd2UgaGF2ZSBzZXZlbiAnY2F0ZWdvcmljYWwgdmFyaWFibGVzJyAoc29tZSBzaG91bGQgcHJvYmFibHkganVzdCBiZSBjaGFyYWN0ZXIgdmFyaWFibGVzIGxpa2UgJ1BsYXllcicpLCBhbmQgdGhlIHJlc3QgYXJlIGp1c3QgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyAoYWxsIGluIGludGVnZXIgZm9ybSkuCgpgYGB7cn0Kc3RyKEFGTHN0YXRzKQpgYGAKCkl0J3MgaW1wb3J0IHRvIHVuZGVyc3RhbmQgYWxsIG91ciB2YXJpYWJsZXMgYmVmb3JlIHdlIGFjdHVhbGx5IGdldCBpbnRvIG1hbmlwdWxhdGluZyB0aGUgZGF0YTsgdG8gbWFrZSBzdXJlIHdlJ3JlIG5vdCBkZXN0cm95aW5nIHRoZSBpbnRlZ3JpdHkgb2Ygb3VyIGRhdGEgYXMgd2UgY2Fycnkgb3V0IG91ciBhbmFseXNpcy4gSW4gc2F5aW5nIHRoYXQsIG1vc3Qgb2YgdGhlIHZhcmlhYmxlcyBhcmUgcHJldHR5IHNlbGYgZXhwbGFuYXRvcnk7IGV2ZW4gdG8gc29tZW9uZSB3aG8gb25seSBoYXMgYSB2ZXJ5IGJhc2ljIGtub3dsZWRnZSBvZiBBRkwuIEhvd2V2ZXIsIHRoZXJlJ3MgY2VydGFpbmx5IHNvbWUgJ3NsYW5nJyB0ZXJtaW5vbG9neSB3aGljaCBtaWdodCBuZWVkIHRvIGJlIGNsZWFyZWQgdXA6CgoqIEJlaGluZHMgLSB3aGVuIHRoZSBiYWxsIGlzIHRvdWNoZWQgZWl0aGVyIGJ5IGFub3RoZXIgcGxheWVyIG9yIGJ5IHRoZSBnb2FsIHBvc3QgYnV0IHN0aWxsIHBhc3NlcyBvdmVyIHRoZSBnb2FsIGxpbmU7IG9yIHNpbXBseSB0aGUgYmFsbCBwYXNzZXMgb3ZlciB0aGUgYmVoaW5kIGxpbmUgKG1vcmUgaW5mb3JtYXRpb24gW2hlcmVdKGh0dHA6Ly93d3cuYWZscnVsZXMuY29tLmF1L2FmbC1nYW1lLXJ1bGVzL3BhcnQtZC1wcmUtbWF0Y2gtcHJvdmlzaW9ucy9hZmwtcnVsZXMtc2NvcmluZy1nb2Fscy1iZWhpbmRzLykpCiogSGl0b3V0cyAtIHdoZW4gYSBwbGF5ZXIgc3VjY2Vzc2Z1bGx5IGtub2NrcyB0aGUgYmFsbCBvdXQgb2YgYSBydWNrIAoqIFJ1Y2sgLSBhIHJ1Y2sgb2NjdXJzIGdlbmVyYWxseSBhZnRlciBhIGdvYWwgaXMgc2NvcmVkLCBwbGF5IGlzIGNlYXNlZCBhZnRlciBhIHN0YXRpYyB0YWNrbGUgb3IgdGhlIGJhbGwgZXhpdHMgdGhlIGZpZWxkIGxpbmVzOyB0byByZXNldCB0aGUgcGxheSBhIHJlZmVyZWUgd2lsbCBlaXRoZXIgYm91bmNlIHRoZSBiYWxsIHVwLCBvciB0aHJvdyB0aGUgYmFsbCBpbiBmcm9tIHRoZSBzaWRlbGluZSBhbmQgdGhlIG9wcG9zaW5nIHRlYW1zIHdpdGggY29udGVzdCB0byBoaXQgdGhlIGJhbGwgaW50byBwbGF5CiogUmVib3VuZDUwcyAtIHdoZW4gdGhlIGJhbGwgZXhpdHMgdGhlIHRlYW1zIGRlZmVuc2l2ZSA1MCBhcmVhICh0aGVpciBvd24gNTAgYXJlYSkKKiBDbGFuZ2VycyAtIGEgZ2VuZXJhbCB0dXJuIGdpdmVuIHRvIGEgcGxheWVyJ3Mgc21hbGwgZXJyb3Igd2hpY2ggcmVzdWx0cyBpbiBhIHR1cm4tb3ZlciBvZiB0aGUgYmFsbCAobW9yZSBpbmZvcm1hdGlvbiBbaGVyZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ2xhbmdlcl8oQXVzdHJhbGlhbl9ydWxlc19mb290YmFsbCkpKQoqIE9uZVBlcmNlbnRlcnMgLSBhcyB0aGUgbmFtZSBhbGx1ZGVzIHRvLCB0aGlzIGlzIGEgdGVybSBnaXZlbiB0byBjb2xsZWN0aW9uIG9mIHJhcmUgYWN0aW9ucyB3aXRoaW4gQUZMIHdoaWNoIGJlbmVmaXQgdGhlIHRlYW1zIHBsYXk7IGluY2x1ZGluZyBrbm9jay1vbnMsIHNwb2lscywgc21vdGhlcnMgYW5kIHNoZXBhcmRzIChtb3JlIGluZm9ybWF0aW9uIFtoZXJlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9PbmVfcGVyY2VudGVyXyhBdXN0cmFsaWFuX3J1bGVzX2Zvb3RiYWxsKSkpICAKCiMjIyBPdXIgbWV0aG9kcwoKV2hlbiB0YWtpbmcgYSBjbG9zZXIgbG9vayBhdCB0aGUgcHJvYmxlbSB3ZSBhcmUgb3V0IHRvIHNvbHZlLCBwcmVkaWN0aW5nIHdoaWNoIHRlYW0gaXMgbW9yZSBsaWtlbHkgdG8gd2luIGluIGFueSBnaXZlbiBBRkwgbWF0Y2gsIGl0IHNlZW1zIGxpa2UgcXVpdGUgYSBzaW1wbGUgY2xhc3NpZmljYXRpb24gcHJvYmxlbS4gV2Ugd2FudCB0byBiZSBhYmxlIHRvIGNsYXNzaWZ5IG9uZSB0ZWFtIGFzIHdpbm5pbmcgYW5kIG9uZSB0ZWFtIGFzIGxvc2luZywgYmFzZWQgdXBvbiBhbiBhc3NpZ25lZCBjaGFuY2Ugb2Ygd2lubmluZy4gVGhlIGNoYW5jZSB3aW5uaW5nIHdvdWxkIG9wdGltYWxseSBiZSBkZXRlcm1pbmVkIGJ5IGEgc3Vic2V0IG9mIHJlbGV2YW50IHZhcmlhYmxlcyBwaWNrZWQgZnJvbSB0aGUgMzIgd2UgaGF2ZSBhdmFpbGFibGUgd2l0aGluIHRoaXMgZGF0YSBzZXQuIFdpdGggdGhpcyBpbiBtaW5kIGl0IHNlZW1zIHRoYXQgcHJlZGljdGl2ZSBtb2RlbHMgbGlrZSAqKkxvZ2lzdGljIFJlZ3Jlc3Npb24qKiwgKipLLU5lYXJlc3QgTmVpZ2hib3VycyoqIGFuZCAqKkRlY2lzaW9uIFRyZWUvTWFueSBGb3Jlc3RzKiogd291bGQgYmUgcGVyZmVjdCBjYW5kaWRhdGVzIGZvciBoZWxwaW5nIHVzIHNvbHZlIG91ciBwcm9ibGVtLCBhcyB0aGV5IGVpdGhlciBkZXNpZ25lZCBmb3IgKG9yIGp1c3QgZ29vZCBmb3IpIGFzc2lnbmluZyBwcmVkaWN0aXZlIHByb2JhYmlsaXRpZXMgZm9yIHNpbXBsZSwgYmluYXJ5IGNsYXNzaWZpY2F0aW9uIHZhcmlhYmxlcy4gSG93ZXZlciwgd2Ugd2lsbCBleHBsb3JlIGhvdyBoZWxwZnVsIHRoZXNlIHRvb2xzICphY3R1YWxseSogYXJlIGluIGhlbHBpbmcgdXMgYWNoaWV2ZSBvdXIgZ29hbCBpbiB0aGUgbGF0ZXIgcGFydHMgb2YgdGhpcyByZXBvcnQuCgpBIHN1cnByaXNpbmdseSBtb3JlIGVmZmVjdGl2ZSBtZXRob2QgZm9yICpzb2x2aW5nKiBvdXIgcHJlZGljdGlvbiBwcm9ibGVtIGlzIHNpbXBseSB3cmFuZ2xpbmcgcGFydHMgb2YgdGhlIGRhdGEgc2V0IHRoYXQgd2Ugd2FudCB0byBsb29rIGF0IGludG8gYSB3b3JrYWJsZSBmb3JtLCBhbmQgdGhlbiB2aXN1YWxpemluZyB0aGVzZSBwYXJ0cyB0aHJvdWdoIHZhcmlvdXMgcGxvdHMuIFRoZSBhZHZhbnRhZ2UgdGhhdCB0aGlzIG1ldGhvZCBoYXMgb3ZlciB0aGUgbW9yZSBjb21wbGV4IHByZWRpY3RpdmUgbW9kZWxzIGlzIHRoYXQgcGxvdHMgbWFrZSBpdCBtdWNoIGVhc2llciB0byBjb21wcmVoZW5kIGEgc2ltcGxlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGEgc2VsZWN0IGZldyB2YXJpYWJsZXMuIFRoaXMgbWFrZXMgaXQgbW9yZSBlZmZpY2llbnQgZm9yIHVzIHRvIHVuZGVyc3RhbmQgKGFuZCBjb21tdW5pY2F0ZSkgdGhlIHN0cm9uZyAob3Igd2VhaykgY29ycmVsYXRpb25zIHRoYXQgd2UgaGF2ZSBjb21lIGFjcm9zcyB3aXRoaW4gb3VyIGRhdGEgc2V0LiBXaGVuIGNvbXBhcmluZyB0aGlzIHRvIHRoZSBtb3JlIGNvbXBsaWNhdGVkIHByZWRpY3RpdmUgbW9kZWxzIGl0J3MgY2xlYXIgdGhhdCB0aGV5IGJvdGggaGF2ZSB0aGVpciBwbGFjZSB3aXRoaW4gYW55IGFuYWx5c2lzLiBIb3dldmVyLCBhcyB3ZSBkaXNjb3ZlciBsYXRlciBpbiBvdXIgcHJvY2VzcyBpdCdzIGNsZWFyIHRoYXQgb25lICdzZXQnIG9mIG1ldGhvZHMgd29ya2VkIGJldHRlciB0aGFuIHRoZSBvdGhlci4KCioqKgoKIyMgX19NZXRob2RvbG9neV9fCgpBcyB3aGF0IHdlJ3JlIGludGVyZXN0ZWQgaW4gaXMgYSB0ZWFtJ3Mgb3ZlcmFsbCBwZXJmb3JtYW5jZSBhbmQgdGhlIGRhdGEgc2V0IHRoYXQgd2UncmUgbG9va2luZyBhdCBpcyBzdHJ1Y3R1cmVkIGluIHRlcm1zIG9mIGVhY2ggaW5kaXZpZHVhbCBwbGF5ZXIncyBwZXJmb3JtYW5jZSwgb3VyIGZpcnN0IGh1cmRsZSB3ZSBoYWQgdG8ganVtcCBvdmVyIHdhcyBnZXR0aW5nIHRoZSBkYXRhIHNldCBpbnRvIGEgbW9yZSB3b3JrYWJsZSBmb3JtIGZvciB3aGF0IHdlJ3ZlIHNldCBvdXQgdG8gYWNoaWV2ZS4KClRoZSBmaXJzdCB0aGluZyB3ZSBkaWQgd2FzIGRlbGV0ZWQgY29sdW1ucyB3ZSBkZWVtZWQgd291bGQgYmUgbmVjZXNzYXJ5IGdvaW5nIGZvcndhcmQgaW4gb3VyIGFuYWx5c2lzOyB0aGUgb2J2aW91cyBjYW5kaWRhdGVzIHRvIGdvIHdlcmUgJ1BsYXllcicgYW5kICdQZXJjZW50IFBsYXllZCcgYXMgdGhleSdkIGJlIHVzZWxlc3Mgb25jZSB3ZSBjb21iaW5lZCBldmVyeXRoaW5nIHRvIGJlIGluIHRlcm1zIG9mIGFueSB0ZWFtcyBub3QgcGxheWVycy4gSG93ZXZlciwgc2xpZ2h0bHkgbGVzcyBvYnZpb3VzIHZhcmlhYmxlcyB0aGF0IHdlIHJlbW92ZWQgd2VyZSAnRnJlZXNBZ2FpbnN0JyBhbmQgJ1VuY29udGVzdGVkIFBvc3Nlc3Npb25zLicgV2UgZGlkIHRoaXMgaXMgYmVjYXVzZSBvdXIgb25jZSBvbmUgdGVhbXMgc3RhdGlzdGljcyBhcmUgcHV0IGFnYWluc3QgYW5vdGhlciB0ZWFtcyBzdGF0aXN0aWNzIGZvciBjb21wYXJpc29uIG9uZSB0ZWFtJ3MgJ0ZyZWVzRm9yJyB3b3VsZCBiZSBleGFjdGx5IHRoZSBzYW1lIGFzIHRoZSBvcHBvc2luZyB0ZWFtLCBhbmQgdmlzZSB2ZXJzYSAoc2FtZSB0aGluZyBmb3IgQ29udGVzdGVkIHZzIFVuY29udGVzdGVkIFBvc3Nlc3Npb25zKS4gU28sIHRvIHByZXZlbnQgZnV0dXJlIHByb2JsZW1zIGFuZCBjbHV0dGVyIHdlIGRlbGV0ZWQgdGhlc2UgZm91ciBjb2x1bW5zIGNvbXBsZXRlbHkgZnJvbSBvdXIgZGF0YSBzZXQuCgpgYGB7cn0KQUZMIDwtIEFGTHN0YXRzCkFGTCA8LSBBRkxbYygxLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQsMTUsMTYsMTcsMTgsMTksMjAsMjEsMjIsMjMsMjUsMjcsMjgsMjksMzAsMzEpXQojY29sbmFtZXMoQUZMKQpgYGAKClRoZSBuZXh0IGxvZ2ljYWwgc3RlcCBpbiBvdXIgbWV0aG9kb2xvZ3kgd2FzIHRvIGFjdHVhbGx5IHNwbGl0IG91ciBkYXRhIHNldCBpbnRvIHRoZSBkaWZmZXJlbnQgdGVhbXMgdG90YWwgZ2FtZXMgYXMgdGhhdCdzIHdoYXQgd2UncmUgbW9zdCBpbnRlcmVzdGVkIGluLCBob3cgd2VsbCBvbmUgdGVhbSB3b3VsZCBwZXJmb3JtIGFnYWluc3QgYW55IG90aGVyLgoKYGBge3J9CmFsbF90ZWFtcyA8LSB1bmlxdWUoYXMuY2hhcmFjdGVyKEFGTCRUZWFtKSkKCmFkIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbMV0sXQpibCA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzJdLF0KY2x0IDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbM10sXQpjbHdkIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbNF0sXQplc2RuIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbNV0sXQpmZCA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzZdLF0KZ2xuZyA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzddLF0KZ2MgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1s4XSxdCmd3cyA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzldLF0KaHQgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxMF0sXQptbGIgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxMV0sXQpubWxiIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbMTJdLF0KcGFkIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbMTNdLF0Kcm1kIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbMTRdLF0Kc2sgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxNV0sXQpzeWQgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxNl0sXQp3YyA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzE3XSxdCndiIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbMThdLF0KYGBgCgpBZnRlciB0aGlzIGhhZCBiZWVuIGRvbmUgd2Ugc3RpbGwgZmFjZWQgdGhlIHByb2JsZW0gdGhhdCBvdXIgZGF0YSBpcyBpbiB0ZXJtcyBvZiByb3dzIG9mIHBsYXllciBzdGF0aXN0aWNzLCBhbmQgbm90IHJvd3Mgb2YgdG90YWwgZ2FtZSBzdGF0aXN0aWNzLiBUaHJvdWdoIHJpZ29yb3VzIHRyaWFsIGFuZCBlcnJvciB3ZSBjcmVhdGVkIGEgbmljZSwgbmVhdCBmdW5jdGlvbiB3aGljaCBjbGVhbnMgYWxsIHRoZSBkYXRhIGJ5IHdyYW5nbGluZyBpdCBpbnRvIHRoZSBmb3JtIHdlIHdhbnQgaXQgdG8gYmUgaW4gKHRvdGFsIHN0YXRpc3RpY3MgZm9yIGFueSBnaXZlbiBnYW1lKS4gSXQgaXMgYWxzbyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IG9ubHkgZml2ZSBvZiB0aGUgdG90YWwgZ2FtZXMgaW4gdGhlIGRhdGEgc2V0IHJlc3VsdGVkIGluIGEgZHJhdyAoc2hvd24gYmVsb3cpLCBzbyBpbiBhbiBlZmZvcnQgc28gc2ltcGxpZnkgb3VyIHRoZSBkYXRhIHdlIGRlY2lkZWQgaXQgd291bGQgYmUgYmVzdCB0byBqdXN0IHJlbW92ZSB0aGVzZSBjYXNlcyAodGhpcyBvY2N1cnMgd2l0aGluIHRoZSBjbGVhbmluZyBmdW5jdGlvbikuCgpgYGB7cn0KIyBwbGF5ZXJzIHdoaWNoIHdlcmUgcGFydCBvZiBhIHRpZSBnYW1lIC8gbnVtYmVyIG9mIHBsYXllcnMgaW4gdGVhbSAvIG51bWJlciBvZiB0ZWFtcyBpbiBhIG1hdGNoCm5yb3coQUZMW0FGTCRXaW5Mb3NzID09ICJEIixdKSAvIDIyIC8gMgpgYGAKClRoaXMgaXMgdGhlIGZ1bmN0aW9uIHdoaWNoICdjbGVhbnMnIG91ciBkYXRhLiBJdCBlc3NlbnRpYWwgdGFrZXMgdGhlIGZpcnN0IG5pbmUgY29sdW1ucyBhbmQgdGFrZXMgb25lIGNvcHkgb2YgdGhlIGluZm9ybWF0aW9uIHRoZXJlIGFzIGl0J3MgdW5pcXVlIHRvIGVhY2ggbWF0Y2guIFRoZSByZW1haW5pbmcgY29sdW1ucyBhcmUgcGxheWVyIHN0YXRpc3RpY3Mgc28gdGhleSBhcmUganVzdCBzdW1tZWQgdG9nZXRoZXIgd2l0aCBhbGwgdGhlIG90aGVyIHNpbWlsYXIgdmFyaWFibGVzIHdpdGhpbiBhbnkgbWF0Y2ggdG8gZ2V0IHRoZSBkYXRhIGludG8gdGhlIGZvcm0gd2Ugd2FudCBpdCB0byBiZSBpbi4gQXMgbWVudGlvbmVkIGJlZm9yZSwgdGhlIGdhbWVzIHRoYXQgd2VyZSB0aWVkIHdlcmUgcmVtb3ZlZCBmcm9tIHRoZSBkYXRhIHNldCBpbiB0aGlzIHN0ZXAuCgpgYGB7cn0KY2xlYW5BRkxkYXRhIDwtIGZ1bmN0aW9uKHRlYW0pewogIAogIGxpYnJhcnkocGx5cikKICB0ZWFtX3N0YXRzIDwtIGRkcGx5KHRlYW0sIC4oRGF0ZSksIG51bWNvbHdpc2Uoc3VtKSkKICB0ZWFtX3N0YXRzIDwtIHRlYW1fc3RhdHNbYygxLDU6MjMpXQogIHRlYW1fa2VlcCA8LSB0ZWFtW21hdGNoKHVuaXF1ZSh0ZWFtJERhdGUpLCB0ZWFtJERhdGUpLF0KICB0ZWFtX2tlZXAgPC0gdGVhbV9rZWVwWzE6OV0KICB0ZWFtX2NsZWFuIDwtIG1lcmdlKHRlYW1fa2VlcCwgdGVhbV9zdGF0cykKICB0ZWFtX2NsZWFuIDwtIHRlYW1fY2xlYW5bdGVhbV9jbGVhbiRXaW5Mb3NzICE9ICJEIixdCiAgdGVhbV9jbGVhbiRXaW5Mb3NzIDwtIGRyb3BsZXZlbHModGVhbV9jbGVhbiRXaW5Mb3NzKQogIGxldmVscyh0ZWFtX2NsZWFuJFdpbkxvc3MpW2xldmVscyh0ZWFtX2NsZWFuJFdpbkxvc3MpPT0iTCJdIDwtIDAKICBsZXZlbHModGVhbV9jbGVhbiRXaW5Mb3NzKVtsZXZlbHModGVhbV9jbGVhbiRXaW5Mb3NzKT09IlciXSA8LSAxCiAgdGVhbV9jbGVhbiRXaW5Mb3NzIDwtIGZhY3Rvcih0ZWFtX2NsZWFuJFdpbkxvc3MpCiAgCiAgcmV0dXJuKHRlYW1fY2xlYW4pCiAgCn0KYGBgCgpgYGB7cn0KYWRfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKGFkKQpibF9jbGVhbiA8LSBjbGVhbkFGTGRhdGEoYmwpCmNsdF9jbGVhbiA8LSBjbGVhbkFGTGRhdGEoY2x0KQpjbHdkX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShjbHdkKQplc2RuX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShlc2RuKQpmZF9jbGVhbiA8LSBjbGVhbkFGTGRhdGEoZmQpCmdsbmdfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKGdsbmcpCmdjX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShnYykKZ3dzX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShnd3MpCmh0X2NsZWFuIDwtIGNsZWFuQUZMZGF0YShodCkKbWxiX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShtbGIpCm5tbGJfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKG5tbGIpCnBhZF9jbGVhbiA8LSBjbGVhbkFGTGRhdGEocGFkKQpybWRfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKHJtZCkKc2tfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKHNrKQpzeWRfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKHN5ZCkKd2NfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKHdjKQp3Yl9jbGVhbiA8LSBjbGVhbkFGTGRhdGEod2IpCmBgYAoKQWZ0ZXIgZ29pbmcgdGhyb3VnaCBhbGwgdGhpcyBlZmZvcnQgdG8gc3BsaXQgdGhlIGRhdGEgdG8gYmUgaW4gdGVybXMgb2Ygc2VwYXJhdGUgdGVhbXMgaXQgYWN0dWFsbHkgbWVhbnQgdGhhdCBmb3IgZWFjaCB0ZWFtIHdlIG9ubHkgaGFkIGEgdmVyeSBzbWFsbCBzZXQgb2YgZGF0YSBwb2ludHMuIFRoaXMgbWFkZSBhbnkgcHJlZGljdGlvbiBtb2RlbHMgZm9yIGVhY2ggaW5kaXZpZHVhbCB0ZWFtIGltcG9zc2libGUgYXMgdGhlIHRyYWluaW5nIHNldCAoMjAxMiAtIDIwMTUpIG9ubHkgaGFkIHJvdWdobHkgMTAwIHJvd3MgYW5kIG91ciB0ZXN0IHNldCAoMjAxNikgb25seSBoYWQgYWJvdXQgMjUgcm93czsgd2hpY2ggUiBkaWRuJ3QgdGhpbmsgd2FzIGVub3VnaCBkYXRhIGZvciB0aGUgcHJlZGljdGl2ZSBtb2RlbHMgd2Ugd2FudGVkIHRvIGRvLiBTbyB3ZSBlbmRlZCB1cCBqdXN0IGNvbWJpbmluZyBldmVyeXRoaW5nIGJhY2sgaW50byBvbmUgbmljZSBiaWcgZGF0YSBzZXQgKGFmdGVyIGJlaW5nIGNsZWFuZWQpLgoKYGBge3J9CkFGTF9jbGVhbiA8LSByYmluZChhZF9jbGVhbiwgYmxfY2xlYW4sIGNsdF9jbGVhbiwgY2x3ZF9jbGVhbiwgZXNkbl9jbGVhbiwgZmRfY2xlYW4sIGdsbmdfY2xlYW4sIAogICAgICAgICAgICAgICAgICAgZ2NfY2xlYW4sIGd3c19jbGVhbiwgaHRfY2xlYW4sIG1sYl9jbGVhbiwgbm1sYl9jbGVhbiwgcGFkX2NsZWFuLCBybWRfY2xlYW4sCiAgICAgICAgICAgICAgICAgICBza19jbGVhbiwgc3lkX2NsZWFuLCB3Y19jbGVhbiwgd2JfY2xlYW4pCiNsZW5ndGgodW5pcXVlKEFGTF9jbGVhbiRUZWFtKSkKYGBgCgpPbmNlIG91ciBkYXRhIHdhcyAqb25jZSBhZ2FpbiogaW4gb25lIGJpZywgY29tYmluZWQgZGF0YSBmcmFtZSB3ZSBkZWNpZGVkIChhbmQgUiBhY2NlcHRlZCBvdXIgZGVjaXNpb24pIGl0IHdhcyBmaW5hbGx5IHRpbWUgdG8gc3VjY2Vzc2Z1bGx5IHNwbGl0IG91ciBkYXRhIGludG8gYSBzZXBhcmF0ZSB0cmFpbmluZyBhbmQgdGVzdCBzZXQuIEl0IG1hZGUgc2Vuc2UgZm9yIG91ciB0cmFpbmluZyBzZXQgdG8gYmUgYWxsIHRoZSBnYW1lcyBpbiAyMDEyIC0gMjAxNSAoaW5jbHVzaXZlKSBhbmQgdGhlbiBvdXIgdGVzdCBzZXQgdG8gYmUganVzdCB0aGUgbWF0Y2hlcyBwbGF5ZWQgaW4gdGhlIGxhdGVzdCAoaW4gb3VyIGRhdGEgc2V0KSAyMDE2IHNlYXNvbi4gVGhpcyBtYWRlIHNlbnNlIGFzIG91ciBnb2FsIGlzIHRvIHByZWRpY3QgdGhlIGN1cnJlbnQgKDIwMTcpIG1hdGNoZXMgc28gaGF2aW5nIG91ciB0ZXN0IHNldCBiZWluZyB0aGUgY2xvc2VzdCB0byB0aGF0IGFzIHBvc3NpYmxlIG1lYW50IG91ciBwcmVkaWN0aXZlIG1vZGVscyB3b3VsZCAoaW4gdGhlb3J5KSAndHVuZScgb3VyIG1vZGVsIHRvIGJlIGFzIGFjY3VyYXRlIGZvciB0aGUgY3VycmVudCBzZWFzb24gYXMgd2UgY291bGQgaG9wZSBmb3IuCgpgYGB7cn0KQUZMLnRlc3QgPC0gQUZMX2NsZWFuW0FGTF9jbGVhbiRTZWFzb24gPT0gMjAxNixdCiNoZWFkKEFGTC50ZXN0KQpBRkwudHJhaW4gPC0gQUZMX2NsZWFuW0FGTF9jbGVhbiRTZWFzb24gIT0gMjAxNixdCiNoZWFkKEFGTC50cmFpbikKYGBgCgpOZXh0IHdlIGRlY2lkZWQgdG8gZG8gYSBsaXR0bGUgZXhwbG9yYXRvcnkgYW5hbHlzaXMgb2Ygb3VyIHRyYWluaW5nIHNldCwgdGhpcyB3YXMgdG8gbWFrZSBzdXJlIGV2ZXJ5dGhpbmcgbWFrZXMgbG9naWNhbCBzZW5zZSAoY2hlY2sgd2UgaGF2ZW4ndCBtYWRlIGFuIGVycm9yIGR1cmluZyBvdXIgY2xlYW5pbmcgcHJvY2VzcyksIGFuZCBqdXN0IHRvIGV4cGxvcmUgdGhlIGRhdGEgYW5kIHNlZSBpZiB3ZSBjYW4gZmluZCBhbnl0aGluZyBpbnRlcmVzdGluZy4KCmBgYHtyfQpwbG90KEFGTC50cmFpbiRXaW5Mb3NzLCBBRkwudHJhaW4kRGlzcG9zYWxzKQpgYGAKCmBgYHtyfQpwbG90KEFGTC50cmFpbiRXaW5Mb3NzLCBBRkwudHJhaW4kSW5zaWRlNTBzKQpgYGAKCmBgYHtyfQpwbG90KEFGTC50cmFpbiRXaW5Mb3NzLCBBRkwudHJhaW4kQ2xlYXJhbmNlcykKYGBgCgpGcm9tIHRoZXNlIGZldyBwbG90cyB3ZSBjYW4gc2VlIHRoYXQgb3VyIGRhdGEgbG9va3MgdG8gYmUgaW50YWN0LCBhcyB3ZSBhY3R1YWxseSBnb3QgZ2l2ZW4gZnVuY3Rpb25hbCBib3ggcGxvdHMgYXMgb3V0cHV0cy4gQW5kIG1vcmUgaW1wb3J0YW50bHkgaXQgc2VlbXMgdGhhdCBhdCBsZWFzdCBzb21lIG9mIHRoZXNlIHZhcmlhYmxlcyBoYXZlIHNvbWUgc29ydCBvZiBhIGNvcnJlbGF0aW9uIHdpdGggdGVhbXMgd2hvJ3ZlIHdvbiAoMSkgYW5kIHRlYW1zIHdobyd2ZSBsb3N0ICgwKS4gVGhpcyBtZWFudCB0aGF0IHNvbWUgb2Ygb3VyIHZhcmlhYmxlcyBtaWdodCBiZSBzdHJvbmcgY2FuZGlkYXRlcyBpbiBiZWluZyBhYmxlIHRvIGFzc2lzdGFuY2UgdXMgaW4gcHJlZGljdGluZyB0aGUgb3V0Y29tZXMgb2YgZnV0dXJlIGdhbWVzLgoKQXMgYSBzZWNvbmQgbWVhc3VyZSB0byB0aGlzIHByb2Nlc3Mgd2UgY3JlYXRlZCBhIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGhpc3RvZ3JhbSBtYXRyaXggd2l0aCBhbGwgb2Ygb3VyIHZhcmlhYmxlcyB0byBzZWUgaWYgYW55dGhpbmcgaGFkIGEgcGFydGljdWxhcmx5IHN0cm9uZyBjb3JyZWxhdGlvbiB3aXRoIG91ciAnV2luTG9zcycgdmFyaWFibGUuCgpgYGB7ciAwMDAxLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTIsIGV2YWw9RkFMU0V9CnNvdXJjZSgiUGFpcnNXaXRoSGlzdC5SIikKcGFpcnMoQUZMX2NsZWFuLCBsb3dlci5wYW5lbCA9IHBhbmVsLnB0cywgdXBwZXIucGFuZWwgPSBwYW5lbC5jb3IsIGRpYWcucGFuZWwgPSBwYW5lbC5oaXN0KQpgYGAKCk91ciBuZXh0IHN0ZXAgd2FzIHRvIGFjdHVhbGx5IGJlZ2luIHByZXBhcmluZyB0byBtYWtlIHNvbWUgcHJlZGljdGl2ZSBtb2RlbHM7IHdoaWNoIHdhc24ndCBqdXN0IGFzIHNpbXBsZSBhcyBmaXR0aW5nIGEgbW9kZWwgd2l0aCBhbGwgdGhlIHZhcmlhYmxlcyBhbmQgdGhlbiBmaXR0aW5nIGFub3RoZXIgbW9kZWwgd2l0aCBub25lLCBhbmQgdGhlbiBydW5uaW5nIGZvcndhcmQgc2VsZWN0aW9uIGJldHdlZW4gdGhlIHR3byAoYWx0aG91Z2ggdGhhdCB3YXMgKm9uZSogc3RlcCB3aXRoaW4gdGhlIHByb2Nlc3MpLiBBZnRlciBzb21lIHRyaWFsIGFuZCBlcnJvciBhbmQgdGhvdWdodCBpbnRvIHdoYXQgd2Ugd2VyZSBhY3R1YWxseSBkb2luZyBzb21lIHRoaW5ncyBiZWNhbWUgY2xlYXI7IHdlIGhhZCB0b28gbWFueSB2YXJpYWJsZXMgYW5kIHNvbWUgdmFyaWFibGVzIGp1c3QgZGlkbid0IG1ha2Ugc2Vuc2UgYmVpbmcgaW4gb3VyIG1vZGVsKHMpLgoKVG8gcmVkdWNlIG91ciB0b3RhbCBhbW91bnQgb2YgdmFyaWFibGVzIHdlIGZpcnN0IHJlbW92ZWQgYWxsIHRoZSBjYXRlZ29yaWNhbCBleHBsYW5hdG9yeSB2YXJpYWJsZXMuIE91ciByZWFzb24gZm9yIGRvaW5nIHRoaXMgaXMgdGhhdCBlYWNoIGxldmVsIG9mIGEgZmFjdG9yIHZhcmlhYmxlIHdpdGhpbiBhIHByZWRpY3RpdmUgbW9kZWwgYmVjb21lcyBpdHMgb3duIHZhcmlhYmxlLCAqbm90KiBqdXN0IHRoZSBvbmUgZmFjdG9yIHZhcmlhYmxlIGl0c2VsZi4gRm9yIGV4YW1wbGUsIGJ5IGluY2x1ZGluZyB0aGUgZmFjdG9yIHZhcmlhYmxlICdWZW51ZScgd2UgYXJlIGludHJvZHVjaW5nIGlzIDE5IG5ldyBleHBsYW5hdG9yeSB2YXJpYWJsZXMgaW50byBvdXIgbW9kZWwsIG5vdCBzaW1wbHkganVzdCB2ZW51ZS4KClRoZSBuZXh0IHByb2JsZW0gd2FzIHRoYXQgdGhlICdNYXJnaW4nIHZhcmlhYmxlIHdhcyBhICoqcGVyZmVjdCoqIHByZWRpY3RvciBvZiB3aGV0aGVyIGEgdGVhbSB3b3VsZCB3aW4gb3IgbG9zZS4gSWYgdGhlIG1hcmdpbiBmb3IgYSBnYW1lIHdhcyBncmVhdGVyIHRoYW4gemVybyB0aGUgdGVhbSBoYWQgd29uLCBpZiB0aGUgbWFyZ2luIHdhcyBuZWdhdGl2ZSB0aGVuIHRoZSB0ZWFtIGhhZCBsb3N0LiBCeSBpbmNsdWRpbmcgdGhpcyBpbiBvdXIgbW9kZWwgaXQgd291bGQgaGF2ZSBoYWQgc3VjaCBhIHN0cm9uZyBjb3JyZWxhdGlvbiB0aGF0IGFsbCB0aGUgb3RoZXIgdmFyaWFibGVzLCB3aGljaCBhcmUgYXJndWFibHkgbW9yZSB1c2VmdWwgZm9yIG91ciBvdmVyYWxsIGdvYWwsIHdvdWxkIGJlIGxvc3QgYXMgbm9pc2U7IHNvIHdlIGhhZCB0byByZW1vdmUgaXQuCgpWYXJpYWJsZXMgbGlrZSAnU2NvcmUnIGFuZCAnR29hbHMnIHdlcmUgaW4gYSBzaW1pbGFyIHNvcnQgb2YgYm9hdCBiZWNhdXNlIHRlYW1zIHdobyB3aW4gYXJlIGFsd2F5cyBnb2luZyB0byBoYXZlIGEgaGlnaGVyIHNjb3JlLCBiZWNhdXNlIHRoYXQncyBob3cgeW91IHdpbiB0aGUgZ2FtZS4gVGhpcyBpcyBzdWNoIGEgYmFzaWMgcHJlbWlzZSB0aGF0IGl0IHJlYWxseSBtYWRlIG5vIHNlbnNlIHRvIGluY2x1ZGUgaW4gb3VyIG1vZGVsIGFzIGl0IHdhcyBqdXN0IGFkZGluZyBub2lzZS4gQW5kIHRoZSBsYXN0IHNldCBvZiB2YXJpYWJsZXMgd2UgZGlkbid0IGluY2x1ZGUgaW4gb3VyIG1vZGVsIHdlcmUgJ0hhbmRiYWxscycgYW5kICdLaWNrcycgYXMgdGhleSBhcmUganVzdCBzdWJzZXRzIG9mIHRoZSAnRGlzcG9zYWwnIHZhcmlhYmxlLCBtYWtpbmcgaXQgcG9pbnRsZXNzIHRvIGluY2x1ZGUgYWxsIHRocmVlIHdpdGhpbiBvdXIgbW9kZWwuCgpBZnRlciBhbGwgb2YgdGhlc2UgJ3R5cGVzJyBvZiB2YXJpYWJsZXMgd2VyZSBleGNsdWRlZCBmcm9tIG91ciBtb2RlbCB3ZSB3ZXJlIGxlZnQgd2l0aCB3aGF0IHdlIHdpdGggdGhlIG1ham9yaXR5IG9mIHRoZSBpbnRlZ2VyIG1hdGNoIHN0YXRpc3RpY3MgbGlrZSAnRGlzcG9zYWxzLCcgJ0luc2lkZTUwcywnICdDbGFuZ2VycywnIGV0Yy4KCmBgYHtyfQojIGp1c3QgJ251bWVyaWNhbCcgdmFyaWFibGVzCkFGTC5nbG0ubnVtIDwtIGdsbShXaW5Mb3NzIH4gIERpc3Bvc2FscyArIE1hcmtzICsgQmVoaW5kcyArIEluc2lkZTUwcyArIENsZWFyYW5jZXMgKyBDbGFuZ2VycyArIENvbnRlbmRlZFBvc3Nlc3Npb25zICsgQ29udGVzdGVkTWFya3MgKyBNYXJrc0luc2lkZTUwLCAKICAgICAgICAgICAgICAgICAgZGF0YSA9IEFGTC50cmFpbiwgZmFtaWx5ID0gYmlub21pYWwobG9naXQpKQpgYGAKClRoZW4gd2Ugc2V0IHVwIGEgbW9kZWwgd2l0aCAqbm8qIGV4cGxhbmF0b3J5IHZhcmlhYmxlcy4KCmBgYHtyfQpBRkwuZ2xtLjAgPC0gZ2xtKFdpbkxvc3MgfiAxLCBkYXRhID0gQUZMLnRyYWluLCBmYW1pbHkgPSBiaW5vbWlhbChsb2dpdCkpCmBgYAoKQW5kIGZpbmFsbHkgd2UgY29tcGFyZWQgdGhlc2UgdHdvIG1vZGVscyB0byBmaW5kIHRoZSBvcHRpbWFsIHNldCBvZiB2YXJpYWJsZXMgd2Ugc2hvdWxkIGJlIGluY2x1ZGluZyBpbiBvdXIgZmluYWwgbW9kZWwuIFdoaWNoIGJ5IGxvb2tpbmcgYXQgdGhlIHN1bW1hcnkgYmVsb3cgeW91IG1pZ2h0IGhhdmUgbm90aWNlZCBsZWZ0IHVzIHdpdGggc2V2ZW4gZXhwbGFuYXRvcnkgdmFyaWFibGVzIGluIG91ciBmaW5hbCBtb2RlbDsgcmVtb3ZpbmcgJ0NvbnRlc3RlZE1hcmtzJyBhbmQgJ0JlaGluZHMnIGFzIGZvcndhcmQgc2VsZWN0aW9uIGRpZG4ndCBmaW5kIHRoZW0gcmVsZXZhbnQuCgpBbGwgdGhlIHZhcmlhYmxlcyBsZWZ0IGluIHRoZSBtb2RlbCBhbHNvIGhhdmUgdmVyeSBzbWFsbCBwLXZhbHVlcyB3aGljaCBpcyBhbHdheXMgYSBnb29kIHNpZ24gdGhhdCB0aGV5ICpjb3VsZCogYmUgaGlnaGx5IGNvcnJlbGF0ZWQgdG8gdGhlIHJlc3BvbnNlIHZhcmlhYmxlICh3aW5uaW5nIGEgbWF0Y2gpLgoKYGBge3J9CkFGTC5nbG0uZndkIDwtIHN0ZXAoQUZMLmdsbS4wLCBzY29wZSA9IGZvcm11bGEoQUZMLmdsbS5udW0pLCBkaXJlY3Rpb24gPSAiZm9yd2FyZCIsIHRyYWNlID0gMCkKc3VtbWFyeShBRkwuZ2xtLmZ3ZCkKYGBgCgoqKioKCiMjIF9fQW5hbHlzaXNfXwojIyMgTG9naXN0aWMgUmVncmVzc2lvbgoKVGhlIGZpcnN0IHByZWRpY3RpdmUgbW9kZWwgdGhhdCB3ZSBkZWNpZGVkIHRvIGltcGxlbWVudCB3YXMgYSBMb2dpc3RpYyBSZWdyZXNzaW9uIG1vZGVsLCB3aXRoICdXaW5Mb3NzJyBiZWluZyBvdXIgcmVzcG9uc2UgdmFyaWFibGUsIG9idmlvdXNseSB3aXRoIGEgJzEnICh3aW5uaW5nIGdhbWUpIGJlaW5nIHRoZSBwcmVmZXJyZWQgb3V0Y29tZS4KCmBgYHtyfQpBRkwuYWxsLnByZWQgPC0gcHJlZGljdChBRkwuZ2xtLmZ3ZCwgbmV3ZGF0YSA9IEFGTC50ZXN0LCB0eXBlID0gInJlc3BvbnNlIikKI2hlYWQoQUZMLmFsbC5wcmVkKQpgYGAKCkFmdGVyIHRoZSBtb2RlbCBoYWQgbWFkZSBpdHMgcHJlZGljdGlvbnMgdXNpbmcgb3VyIHRlc3QgZGF0YSB0byAndHVuZScgaXQsIHdlIGhhZCB0byBjb21wYXJlIHRoZXNlIHByZWRpY3Rpb25zIHRvIHRoZSBhY3R1YWwgdHJ1ZSwgcHJlZGljdGVkIG91dGNvbWVzIGZvciAyMDE2LiBUaGUgbW9zdCBlZmZpY2llbnQgd2F5IG9mIGdvaW5nIGFib3V0IHRoaXMgd2FzIHRvIGNyZWF0ZSBhIGNvbmZ1c2lvbiBtYXRyaXggb2Ygb3VyIHByZWRpY3RlZCByZXN1bHRzIGFnYWluc3QgdGhlIHRydWUgcmVzdWx0cywgdGhpcyBnaXZlcyB1cyBhbiBpZGVhIG9mIGhvdyBhY2N1cmF0ZSBvdXIgbW9kZWwgYWN0dWFsbHkgaXMuCgpgYGB7cn0KQUZMLmFsbC5wcmVkLmNsYXNzIDwtIHJlcCgwLCBsZW5ndGgoQUZMLmFsbC5wcmVkKSkKQUZMLmFsbC5wcmVkLmNsYXNzW0FGTC5hbGwucHJlZCA+IDAuNzVdIDwtIDEKdGFibGUoQUZMLmFsbC5wcmVkLmNsYXNzLCBBRkwudGVzdCRXaW5Mb3NzKQpgYGAKYGBge3J9CigxODgrMTMyKS8oMTg4Kzc1KzE5KzEzMikKYGBgCgpGcm9tIHRoaXMgY29uZnVzaW9uIG1hdHJpeCB3ZSBjYW4gc2VlIHRoYXQgYWJvdXQgNzclIG9mIG91ciBwcmVkaWN0aW9ucyB3ZXJlIGNvcnJlY3Qgd2hlbiB3ZSB1c2VkIGEgY3V0LW9mZiB2YWx1ZSBvZiAwLjc1LiBUaGlzIG1lYW5zIHRoYXQgYW55IGdhbWUgcHJlZGljdGVkIGJ5IG91ciBtb2RlbCB0aGF0IGhhcyBhYm92ZSBhIDc1JSBjaGFuY2Ugb2Ygd2lubmluZyB3aWxsIG9ubHkgdGhlbiBiZSBjbGFzc2lmaWVkIGFzIGEgJ3N1cmUgd2luLicgVGhpcyBjdXQtb2ZmIHNlZW1zIGxpa2UgYSBmYWlyIGFzc3VtcHRpb24gYmVjYXVzZSwgYXNzdW1pbmcgdGhlIG1vZGVsIGFjdHVhbGx5IHdvcmtlZCBpbiB0aGUgd2F5IHdlIHdhbnRlZCBpdCB0bywgZXZlcnkgZm91ciBnYW1lcyB5b3UgcGxhY2VkIGEgYmV0IG9uIHlvdSdkIG9ubHkgbG9zZSBvbmUgb2YgdGhvc2UgZm91ci4gQW5kIHdoZW4geW91IGZhY3RvciBpbiBldmVuIHNvbWUgYXZlcmFnZSBvZGRzIHlvdSdkIGFsbW9zdCBhbHdheXMgY29tZSBvdXQgcHJvZml0YWJsZSBieSBhIGxvbmcgc2hvdCBpZiB0aGF0IHdhcyB0aGUgY2FzZS4KClRoaXMgbW9kZWwgaGFzIGEgcHJldHR5IHJlYXNvbmFibGUgc3VjY2VzcyByYXRlIGV2ZW4gd2hlbiB0aGlzIGhpZ2ggY3V0LW9mZiBpcyBwdXQgaW4gcGxhY2UgaG93ZXZlciwgaXQgc3VmZmVycyBmcm9tIHRoZSBzYW1lICdwcm9ibGVtJyB0aGF0IGFsbCBvdXIgb3RoZXIgcHJlZGljdGl2ZSBtb2RlbHMgZG8gKGRpc2N1c3NlZCBhZnRlciBhbGwgdGhlIHByZWRpY3RpdmUgbW9kZWxzIGhhdmUgYmVlbiAnc2hvd2Nhc2VkJykuCgojIyMgSy1OZWFyZXN0IE5laWdoYm91cnMKCkFmdGVyIG91ciBMb2dpc3RpYyBSZWdyZXNzaW9uIG1vZGVsIGNhbWUgb3V0IGFzIHN1Y2ggYSBzdWNjZXNzIHdlIGRlY2lkZWQgdGhhdCB0aGUgbmV4dCBiZXN0IHN0ZXAgd291bGQgYmUgdG8gdHJ5IGEgSy1OZWFyZXN0IE5laWdoYm9ycyBtb2RlbCB0byBzZWUgaWYgaXQncyBtb3JlIG9yIGxlc3MgYWNjdXJhdGUgd2hlbiBjb21wYXJlZCB0byBvdXIgcHJldmlvdXMgcHJlZGljdGl2ZSBtb2RlbC4KCmBgYHtyfQpsaWJyYXJ5KGNsYXNzKQpBRkwua25uLnByZWQgPC0ga25uKEFGTC50cmFpblssYygiRGlzcG9zYWxzIiwgIk1hcmtzIiwgIkluc2lkZTUwcyIsICJDbGVhcmFuY2VzIiwgIkNsYW5nZXJzIiwgIkNvbnRlbmRlZFBvc3Nlc3Npb25zIiwgIk1hcmtzSW5zaWRlNTAiKV0sIEFGTC50ZXN0WyxjKCJEaXNwb3NhbHMiLCAiTWFya3MiLCAiSW5zaWRlNTBzIiwgIkNsZWFyYW5jZXMiLCAiQ2xhbmdlcnMiLCAiQ29udGVuZGVkUG9zc2Vzc2lvbnMiLCAiTWFya3NJbnNpZGU1MCIpXSwgQUZMLnRyYWluJFdpbkxvc3MsIAogICAgayA9IDMpCnRhYmxlKEFGTC5rbm4ucHJlZCwgQUZMLnRlc3QkV2luTG9zcykKYGBgCmBgYHtyfQooMTM5KzE2NykvKDEzOSs0MCs2OCsxNjcpCmBgYAoKQW5kLCBwZXJoYXBzIHVuc3VycHJpc2luZ2x5LCB0aGUgSy1OZWFyZXN0IE5laWdoYm9ycyBtb2RlbCByZXN1bHRlZCBpbiBhIHByZWRpY3RlZCBwcm9iYWJpbGl0eSB3aGljaCBpcyBvbmx5IHJvdWdobHkgNCUgYmVoaW5kIHRoZSBMb2dpc3RpYyBSZWdyZXNzaW9uIG1vZGVsLiBUaGlzIHNob3dzIGFnYWluIHRoYXQgb3VyIG1vZGVscyBhcmUgY29uc2lzdGVudGx5IHByZWRpY3RpbmcgYXQgYSBtdWNoIGJldHRlciByYXRlIHRoZW4gYSA1MC81MCBndWVzcy4gSG93ZXZlciwgdGhpcyBtb2RlbCB1c2VzIHRoZSBzYW1lIHNldCBvZiB2YXJpYWJsZXMgYW5kIHRodXMgc3VmZmVycyBmcm9tIHRoZSBzYW1lIHByb2JsZW0gYXMgdGhlIHByZXZpb3VzLgoKIyMjIERlY2lzaW9uIFRyZWUocykKCldlJ2QgZG9uZSBhIExvZ2lzdGljIFJlZ3Jlc3Npb24gYW5kIGEgSy1OZWFyZXN0IE5laWdoYm9ycyBtb2RlbCwgc28gaXQgcmVhbGx5IG9ubHkgbWFkZSBzZW5zZSB0byBmaW5pc2ggdGhlIHRyaW8gYnkgYXR0ZW1wdGluZyB0byBjcmVhdGUgYSBEZWNpc2lvbiBUcmVlIG9yIE1hbnkgRm9yZXN0cyBtb2RlbC4KCmBgYHtyfQpsaWJyYXJ5KHJwYXJ0KQpBRkwubnVtLnRyZWUgPC0gcnBhcnQoV2luTG9zcyB+ICBEaXNwb3NhbHMgKyBNYXJrcyArIEJlaGluZHMgKyBJbnNpZGU1MHMgKyBDbGVhcmFuY2VzICsgQ2xhbmdlcnMgKyBDb250ZW5kZWRQb3NzZXNzaW9ucyArIENvbnRlc3RlZE1hcmtzICsgTWFya3NJbnNpZGU1MCwgCiAgICAgICAgICAgICAgICAgIEFGTF9jbGVhbikKYGBgCgpgYGB7cn0KQUZMLm51bS50cmVlLnByZWQgPSBwcmVkaWN0KEFGTC5udW0udHJlZSwgQUZMLnRlc3QpCkFGTC5udW0udHJlZS5wcmVkIDwtIEFGTC5udW0udHJlZS5wcmVkWywyXQojaGVhZChBRkwubnVtLnRyZWUucHJlZCkKYGBgCgpgYGB7cn0KQUZMLm51bS50cmVlLmNsYXNzIDwtIHJlcCgwLCBsZW5ndGgoQUZMLm51bS50cmVlLnByZWQpKQpBRkwubnVtLnRyZWUuY2xhc3NbQUZMLm51bS50cmVlLnByZWQgPiAwLjc1XSA8LSAxCnRhYmxlKEFGTC5udW0udHJlZS5jbGFzcywgQUZMLnRlc3QkV2luTG9zcykKYGBgCmBgYHtyfQooMTcyICsgMTM3KS8oMTcyKzcwKzM1KzEzNykKYGBgCgpUbyBvdXIgc3VycHJpc2UgdGhlIE1hbnkgRm9yZXN0cyBtb2RlbCBhY3R1YWxseSBwcmVkaWN0ZWQgd2l0aCBhbiBhbG1vc3QgaWRlbnRpY2FsIHN1Y2Nlc3MgcmF0ZSBhcyB0aGUgSy1OZWFyZXN0IE5laWdoYm9ycyBtb2RlbCBkaWQuIEkgZ3Vlc3MgdGhpcyBqdXN0IHNob3dzIHRoYXQgb3VyIHZhcmlhYmxlcyBtdXN0IGhhdmUgcXVpdGUgZXZlbiBzcGxpdHMgd2l0aCByZXNwZWN0IHRvIHdpbnMgYW5kIGxvc3Nlcy4gQWdhaW4sIHRoaXMgbW9kZWwgc3VmZmVycyBmcm9tIHRoZSBzYW1lIHByb2JsZW0gYXMgdGhlIHByZXZpb3VzIHR3by4KCkhvd2V2ZXIsIHRoaXMgbW9kZWwgd2Fzbid0IGFzIHVzZWxlc3MgYXMgdGhlIHByZXZpb3VzIHR3byBtb2RlbHMgYmVjYXVzZSB3ZSBjYW4gYWN0dWFsbHkgdmlzdWFsaXplIHRoZSBkaWZmZXJlbnQgc3BsaXRzIHRoYXQgdGhlIERlY2lzaW9uIFRyZWUgdXNlcyB3aGljaCBnaXZlcyB1cyB2YWx1YWJsZSBpbnNpZ2h0IGludG8gaG93IGRpZmZlcmVudCBjb21iaW5hdGlvbnMgb2YgdmFyaWFibGVzIGludGVyYWN0IHRvIGVpdGhlciBpbXByb3ZlIHlvdXIgY2hhbmNlcyBvZiB3aW5uaW5nIG9yIGh1cnQgdGhlbS4KCmBgYHtyIDAwMDIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03LCBldmFsPUZBTFNFfQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHBhcnR5a2l0KQpwbG90KGFzLnBhcnR5KEFGTC5udW0udHJlZSkpCmBgYAoKVGhlIERlY2lzaW9uIFRyZWUgZGlhZ3JhbSBpcyBvbmUgb2YgdGhlIG1vcmUgdmFsdWFibGUgdGhpbmdzIHRvIGNvbWUgb3V0IG9mIHRoZSBhbmFseXNpcyB3aXRoIHJlc3BlY3QgdG8gb3VyIGdvYWwgdG8gcHJlZGljdCB0aGUgb3V0Y29tZSBvZiBmdXR1cmUgZ2FtZXMuIEJ5IGp1c3QgZm9sbG93aW5nIHRoZSByaWdodCBtb3N0IHBhdGggb2YgdGhlIGRpYWdyYW0gd2UgY2FuIHNlZSB0aGF0IG1vcmUgSW5zaWRlNTBzIHBhaXJlZCB3aXRoIG1vcmUgTWFya3NJbnNpZGU1MCBpcyBhICdyZWNpcGUnIGZvciBhIHZlcnkgaGlnaCBjaGFuY2Ugb2Ygd2lubmluZzsgd2hpY2ggc2VlbXMgcXVpdGUgb2J2aW91cy4gQnkgZm9sbG93aW5nIHRoZSBsZWZ0IG1vc3QgcGF0aCB3ZSBjYW4gc2VlIHRoZSBjb21wbGV0ZSBvcHBvc2l0ZSwgZmV3ZXIgSW5zaWRlNTBzIHBhaXJlZCB3aXRoIGZld2VyIE1hcmtzSW5zaWRlNTAgcmVzdWx0cyBtb3JlIG9mdGVuIHRoZW4gbm90IGluIGEgbG93ZXIgY2hhbmNlIG9mIHdpbm5pbmc7IHdoaWNoIGFnYWluIHNlZW1zIHF1aXRlIG9idmlvdXMuCgpUaGlzIGRpYWdyYW0gb25seSByZWFsbHkgc3RhcnRzIHRvIGdldCBpbnRlcmVzdGluZyB3aGVuIHlvdSBsb29rIGF0IHRoZSBjb21iaW5hdGlvbnMgb2YgdGhlIG1hbnkgZGlmZmVyZW50IHZhcmlhYmxlcy4gRm9yIGV4YW1wbGUgaWYgYSB0ZWFtIGlzbid0IGdldHRpbmcgYSBsb3Qgb2YgSW5zaWRlNTBzIGJ1dCBpcyBnZXR0aW5nIGEgbG90IG9mIE1hcmtzSW5zaWRlNTAsIGFuZCB0aGVuIGFsc28gdGhlIHRlYW0gaGFzIGEgZGVjZW50IGFtb3VudCBvZiBNYXJrcyBvdmVyYWxsIHRoZW4gdGhleSBzdWRkZW5seSBoYXZlIGEgbXVjaCBiZXR0ZXIgY2hhbmNlIGF0IHdpbm5pbmcuCgpUaGlzIGRpYWdyYW0gbWlnaHQgYmUgZXNwZWNpYWxseSB1c2VmdWwgZm9yIHNvbWV0aGluZyBsaWtlIGhhbGYtdGltZSBiZXR0aW5nLiBJZiB0d28gdGVhbXMgYXJlIGFsbW9zdCBuZWNrIHRvIG5lY2sgYXQgaGFsZi10aW1lIGJ1dCB0aGVuIG9uZSB0ZWFtIGlzIGZvbGxvd2luZyBvbmUgb2YgdGhlc2UgJ3BhdGhzJyB0aGF0IGxlYWQgdG8gYSBoaWdoZXIgcHJvcG9ydGlvbiBvZiB3aW5zIHRoZW4gaXQgbWlnaHQgYmUgaW4geW91ciBiZXN0IGludGVyZXN0IHRvIGJldCBvbiB0aGF0IHRlYW0gb3ZlciB0aGVpciBvcHBvc2l0aW9uLiBJbiBubyB3YXkgaXMgaXQgYSBwZXJmZWN0IHNldCBvZiBwYXRocyB0aGF0IGxlYWQgdG8gdmljdG9yeSBmb3IgYSB0ZWFtIGhvd2V2ZXIsIHVzaW5nIGl0IGlzIGNlcnRhaW5seSBhIGxpdHRsZSBtb3JlIHVzZWZ1bCB0aGVuIGJsaW5kbHkgcGlja2luZyBvbmUgdGVhbSBvciB0aGUgb3RoZXIuCgojIyMgVGhlIFByb2JsZW0KClBlcmhhcHMgYSBsaXR0bGUgZm9yZXNpZ2h0IGludG8gd2hhdCBvdXIgbW9kZWxzIGFyZSBhY3R1YWxseSBkb2luZyB3b3VsZCd2ZSBiZWVuIHVzZWZ1bCBhbmQgc2F2ZWQgdXMgYSBsb3Qgb2YgKnNvcnQgb2YqIHdhc3RlZCB0aW1lLiBUaGUgcHJvYmxlbSB3aXRoIGFsbCBvdXIgJ3ByZWRpY3RpdmUnIG1vZGVscyBpcyB0aGF0IHRoZXkncmUgbm90IGFjdHVhbGx5IHJlYWxseSBwcmVkaWN0aW5nIGFueXRoaW5nIHVzZWZ1bC4gQWxsIHRoZXkgYXJlIGRvaW5nIGlzIGNsYXNzaWZ5aW5nIGEgZ2FtZSBhcyBlaXRoZXIgYSB3aW4gb3IgYSBsb3NzIHVzaW5nIGdpdmVuIHRoZSAqKnBvc3QtZ2FtZSBzdGF0aXN0aWNzKiogZm9yIGEgdGVhbS4gU3RhdGlzdGljcyB3aGljaCBjYW4gb25seSBiZSBjb2xsZWN0ZWQgb25jZSBhIGdhbWUgaGFzIGZpbmlzaGVkLCBub3QgYmVmb3JlIGEgZ2FtZSBoYXMgYmVlbiBwbGF5ZWQgd2hpY2ggd2FzIHRoZSB3aG9sZSAncHJvYmxlbScgd2Ugc2V0IG91dCB0byBzb2x2ZS4KClRoaXMgbWFrZXMgb3VyIG1vZGVscyBzb3J0IG9mIHVzZWxlc3MgY29tcGFyZWQgdG8gdGhlIHdheSB3ZSdkIGltYWdlZCB0aGVtIGJ1dCBub3QgKmNvbXBsZXRlbHkqIHVzZWxlc3MuIFRoZSBmYWN0IHRoYXQgdGhleSBjb3VsZCBwcmVkaWN0IHRocmVlIG91dCBvZiBldmVyeSBmb3VyIGdhbWVzIGNvcnJlY3Qgd2l0aCBqdXN0IHRoZSB2YXJpYWJsZXMgd2UgZ2F2ZSB0aGVtIHRvIHdvcmsgd2l0aCByZWluZm9yY2VzIHRoZSBmYWN0IHRoYXQgKipzb21lIHNvcnQqKiBvZiBjb3JyZWxhdGlvbiBleGlzdHMgYmV0d2VlbiB0aG9zZSB2YXJpYWJsZXMgYW5kIGEgdGVhbSB3aW5uaW5nIGEgZ2FtZS4gVG8gYWN0dWFsbHkgc29sdmUgb3VyIHByb2JsZW0gaXQgbWlnaHQgaGF2ZSBiZWVuIGEgbG90IG1vcmUgdXNlZnVsIHRvIGJlIGxvb2tpbmcgYXQgcXVhcnRlci10aW1lIG9yIGhhbGYtdGltZSB0ZWFtcyBzdGF0aXN0aWNzIGFuZCB0aGVuIHNlZWluZyBob3cgKnRob3NlKiByZWxhdGUgdG8gYSB3aW4gb3IgYSBsb3NzLiAKCiMjIyBQbG90dGluZwoKQWZ0ZXIgb3VyICdwcmVkaWN0aXZlJyBtb2RlbHMgcmVhbGx5IGRpZG4ndCBoZWxwIHVzIHdpdGggd2hhdCB3ZSB3YW50ZWQgdG8gYWNoaWV2ZSB3ZSBoYWQgdG8gdGFrZSBhIHN0ZXAgYmFjayBhbmQgdGhpbmsgYWJvdXQgYSBtb3JlIHNwZWNpZmljIHdheSBvZiBhbmFseXppbmcgdGhlIGRhdGEgKip3aGljaCBhY3R1YWxseSBzb21ld2hhdCBoZWxwcyBpbiBwcmVkaWN0aW5nIHRoZSBvdXRjb21lcyBvZiBmdXR1cmUgbWF0Y2hlcyoqLiBXZSB0aG91Z2h0IG9mICp0d28qIG1haW4gdGhpbmdzIHRoYXQgd2Ugd2VyZSBjdXJpb3VzIHRvIGxvb2sgaW50bzoKCjEuIEhvdyBvZnRlbiBhcmUgaW5kaXZpZHVhbCB0ZWFtcyB3aW5uaW5nICYgbG9zaW5nIHdpdGhpbiBvdXIgZGF0YSBzZXQsIGFuZCBob3cgZG9lcyB0aGlzIGNvbXBhcmUgdG8gdGhlaXIgcGVyZm9ybWFuY2UgaW4gdGhlIGN1cnJlbnQgMjAxNyBzZWFzb24/ICAKClRoZSBmaXJzdCB0aGluZyB3ZSBuZWVkZWQgdG8gZG8gd2FzIHNlcGFyYXRlIHRoZSB0b3RhbCB3aW5zIGFuZCBsb3NlcyB3aXRoIHJlc3BlY3QgdG8gZWFjaCB0ZWFtIHRocm91Z2hvdXQgdGhlIHdob2xlIGRhdGEgc2V0LiBUaGVuIHdlIHB1dCB0aGVtIGludG8gZGVzY2VuZGluZyBvcmRlciBiZWNhdXNlIHRoYXQncyBob3cgd2UgaW50ZW5kZWQgdGhlbSB0byBiZSBzaG93biB3aXRoaW4gb3VyIHBsb3RzIHdlIHBsYW5uZWQgb24gbWFraW5nLgoKYGBge3J9CnRhYmxlX2FsbCA8LSB0YWJsZShBRkxfY2xlYW4kVGVhbSwgQUZMX2NsZWFuJFdpbkxvc3MpCgphbGxfZnJlcSA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeCh0YWJsZV9hbGwpCmFsbF9mcmVxIDwtIGFsbF9mcmVxW29yZGVyKGFsbF9mcmVxJGAxYCwgZGVjcmVhc2luZyA9IFRSVUUpLF0KI2FsbF9mcmVxCmBgYAoKVGhpcyB3YXNuJ3QgdGhlIGVuZCBvZiB0aGUgc3RvcnkgdGhvdWdoLiBTaW1wbHkgZ3JhcGhpbmcgZWFjaCB0ZWFtJ3MgdG90YWwgZnJlcXVlbmN5IG9mIHdpbnMgYW5kIGxvc3NlcyBhbmQgdGhlbiBvcmRlcmluZyB0aGVtIGJ5IG51bWJlciBvZiB3aW5zIHdvdWxkIGJlIGNvbXBsZXRlbHkgdW5mYWlyLiBTb21lIHRlYW1zIGhhdmUganVzdCBwbGF5ZWQgbW9yZSBnYW1lcyB0aGFuIG90aGVyIHRlYW1zIHdoaWNoIG1lYW5zIHRoZXknZCBieSBkZWZhdWx0IHByb2JhYmx5IGhhdmUgbW9yZSB3aW5zIGJlY2F1c2UgdGhleSd2ZSBzaW1wbHkgcGxheWVkIG1vcmUgZ2FtZXMuIFRodXMsIHRvIHNob3cgdGhlbSBmYWlybHkgbmV4dCB0byBlYWNoIG90aGVyIHdlIGhhdmUgdG8gZ2V0IHRoZW0gaW4gdGVybXMgb2YgdGhlaXIgd2luL2xvc3MgcGVyY2VudGFnZSBvciBwcm9wb3J0aW9uLiBUaGF0IGlzLCBvdXQgb2YgYWxsIHRoZSBnYW1lcyB0aGV5J3ZlIHBsYXllZCB3aGF0IGZyYWN0aW9uIG9mIHRoZW0gZGlkIHRoZXkgd2luLCBhbmQgd2hhdCBmcmFjdGlvbiBkaWQgdGhleSBsb3NlPwoKYGBge3J9Cndpbl9wZXJjIDwtIGFsbF9mcmVxJGAxYCAvIChhbGxfZnJlcSRgMGAgKyBhbGxfZnJlcSRgMWApCmxvc3NfcGVyYyA8LSAxIC0gd2luX3BlcmMKYWxsX3BlcmMgPC0gY2JpbmQoYWxsX2ZyZXEsIGxvc3NfcGVyYywgd2luX3BlcmMpCmFsbF9wZXJjIDwtIGFsbF9wZXJjWzM6NF0KY29sbmFtZXMoYWxsX3BlcmMpIDwtIGMoIkxvc3MgUGVyY2VudGFnZSIsICJXaW4gUGVyY2VudGFnZSIpCiNhbGxfcGVyYwoKYWxsX3BlcmNfZGVjciA8LSBhbGxfcGVyY1tvcmRlcihhbGxfcGVyYyRgV2luIFBlcmNlbnRhZ2VgLCBkZWNyZWFzaW5nID0gVFJVRSksXQojYWxsX3BlcmNfZGVjcgpgYGAKCmBgYHtyIHQ4LCBmaWcud2lkdGg9MjUsIGZpZy5oZWlnaHQ9Nn0KbW9zYWljcGxvdChhbGxfcGVyY19kZWNyLCBjb2w9YyhyZ2IoMjE1LzI1NSwyNS8yNTUsMjgvMjU1KSxyZ2IoMTY2LzI1NSwyMTcvMjU1LDEwNi8yNTUpKSwgbWFpbiA9ICJUb3RhbCBXaW4gdnMgTG9zcyBQZXJjZW50YWdlIGZvciBlYWNoIHRlYW0gKDIwMTItMjAxNikgW0Rlc2NlbmRpbmcgT3JkZXJdIiwgY2V4LmF4aXMgPSAxLjIpCmBgYAoKSGVyZSB3ZSd2ZSBqdXN0IHNwbGl0IHRoZSBhYm92ZSBwbG90IGluIGhhbGYgc28gaXQncyBlYXNpZXIgdG8gc2VlIGFuZCB3aGF0IHdlJ3ZlIGRvbmUgaXMgY29tcGFyZWQgdGhlIHJhbmsgb2YgdGhlIHRlYW1zIGluIG91ciBkZXNjZW5kaW5nIHdpbiBwZXJjZW50YWdlIHBsb3QgZnJvbSB0aGUgMjAxMiAtIDIwMTYgZGF0YSBhZ2FpbnN0IHRoZSBjdXJyZW50IDIwMTcgbGFkZGVyIHBvc2l0aW9ucyAoYXMgb2YgMzEtMDUtMjAxNykuIEEgZ3JlZW4gZG90IG5leHQgdG8gYSB0ZWFtIHJlcHJlc2VudHMgdGhlbSBiZWluZyBpbiB0aGUgc2FtZSBoYWxmIG9mIGJvdGggdGhlIDIwMTcgbGFkZGVyIGFuZCBvdXIgMjAxMiAtIDIwMTYgZGVzY2VuZGluZyB3aW4gcGVyY2VudGFnZSByYW5raW5ncy4gQXMgeW91IG1pZ2h0IGhhdmUgbm90aWNlZCB3ZSBieSBkb2luZyB0aGlzIHdlIG9ubHkgbWFuYWdlZCB0byBwbGFjZSBqdXN0IG92ZXIgaGFsZiBvZiB0aGUgdGVhbXMgaW4gdGhlaXIgY29ycmVjdCBoYWx2ZXMsIDU2JSB0byBiZSBleGFjdC4gSG93ZXZlciwgcXVpdGUgYSBmZXcgb2YgdGhlIHRlYW1zIGFyZSBvbmx5IG9mZiBieSBhIG9uZSBvciB0d28gcG9zaXRpb25zLCBhbmQgdGhlIHNlYXNvbnMgbm90IGZpbmlzaGVkIHlldCBzbyB0aGVyZSdzIGFsd2F5cyByb29tIGZvciB0aGUgdGVhbXMgdG8gZG8gdGhlIG5hdHVyYWwgdGhpbmcgYW5kIGNvbnZlcmdlIG9uIHRoZWlyIHJlbGF0aXZlIGhpc3RvcmljYWwgcmFua2luZ3MuCgpOb3QgdG8gbWVudGlvbiB0aGlzIHNlYXNvbiBvZiBBRkwgaGFzIGp1c3QgYmVlbiBwYXJ0aWN1bGFybHkgdW5wcmVkaWN0YWJsZTsgdGVhbXMgd2hvJ3ZlIGdlbmVyYWxseSBiZWVuIG9uIHRvcCBoYXZlIGZhbGxlbiBhbmQgdGVhbXMgd2hvJ3ZlIGdlbmVyYWxseSBiZWVuIGF0IHRoZSBib3R0b20gaGF2ZSBzdGFydGVkIHRvIHJpc2UuIEp1c3QgbG9vayBhdCBHV1M7IGZyb20gMjAxMiAtIDIwMTYgdGhleSBoYXZlIHRoZSBmb3VydGggbG93ZXN0IHdpbiBwZXJjZW50YWdlIHlldCB0aGV5J3JlIGN1cnJlbnRseSBzZWNvbmQgb24gdGhlIGxhZGRlci4gVGhpcyBwcm9iYWJseSBjb21lcyBkb3duIHRvIHRoZW0gYmVpbmcgYSB5b3VuZyB0ZWFtIGFuZCBlYWNoIHllYXIgdGhlaXIgcGxheWVycyBhcmUgZ2V0dGluZyBleHBvbmVudGlhbGx5IG1vcmUgZXhwZXJpZW5jZWQgYW5kIHBsYXlpbmcgYmV0dGVyIGFzIGEgcmVzdWx0LCBidXQgaXQncyBpbnRlcmVzdGluZyBub25ldGhlbGVzcy4gCgohWyoqVGVhbSBXaW4gUGVyY2VudGFnZSB2cyAyMDE3IExhZGRlcioqXShhbGwucG5nKQoKCjIuIEFyZSBjZXJ0YWluIHRlYW1zIHdpbm5pbmcgYSBsb3QgbW9yZSBvZnRlbiBhdCBjZXJ0YWluIHZlbnVlcywgYW5kIGRvZXMgdGhpcyBoYXZlIGFueXRoaW5nIHRvIGRvIHdpdGggaG93IG11Y2ggZXhwZXJpZW5jZSB0aGV5IGhhdmUgcGxheWluZyBhdCB0aGVzZSB2ZW51ZXM/CgpUbyBhbnN3ZXIgdGhlc2UgcXVlc3Rpb25zIHdlIGZpcnN0IGhhZCB0byBkZXRlcm1pbmUgd2hpY2ggdmVudWVzIGFsbCBvZiB0aGUgdGVhbXMgaGF2ZSBwbGF5ZWQgYXQgc28gd2UgaGF2ZSBhIGRlY2VudCBzYW1wbGUgc2l6ZSBvZiBtYXRjaGVzIHRvIHdvcmsgd2l0aCwgYW5kIHRvIG1ha2Ugc3VyZSBlYWNoIHRlYW0gaXMgcmVwcmVzZW50ZWQgd2l0aGluIGVhY2ggdmVudWUncyBhbmFseXNpcy4gVG8gZG8gdGhpcyB3ZSBwbG90dGVkIG91ciBzY29yZSB2YXJpYWJsZSBmb3IgZWFjaCB0ZWFtIGZpbHRlcmVkIGJ5IGVhY2ggZGlmZmVyZW50IHZlbnVlIHdlIGhhdmUgaW4gb3VyIGRhdGEgc2V0LgoKYGBge3J9CmxpYnJhcnkobGF0dGljZSkKYmFyY2hhcnQoQUZMX2NsZWFuJFNjb3JlIH4gQUZMX2NsZWFuJFRlYW0gfCBBRkxfY2xlYW4kVmVudWUpCmBgYAoKVXNpbmcgdGhpcyBwbG90IGFuZCBjb21wYXJpbmcgdGhlIHRvdGFsIGFtb3VudCBvZiBnYW1lcyBwbGF5ZWQgYXQgZWFjaCB2ZW51ZSB3ZSBuYXJyb3dlZCB0aGUgdmVudWVzIHdlIHdhbnRlZCB0byBsb29rIGF0IGRvd24gdG8gU3ViaWFjbywgQWRlbGFpZGUgT3ZhbCwgTS5DLkcsIFMuQy5HIGFuZCBUaGUgR2FiYmEuIFRoZXNlIGxvY2F0aW9ucyB3ZXJlIGFsc28gcGlja2VkIGFzIHRoZXkncmUgdGhlIG1vc3QgcG9wdWxhciB2ZW51ZXMgZm9yIFBlcnRoLCBBZGVsYWlkZSwgTWVsYm91cm5lLCBTeWRuZXkgYW5kIEJyaXNiYW5lIHJlc3BlY3RpdmVseTsgbWVhbmluZyBpZiB0aGVyZSdzIGFueSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0ZWFtcyBwbGF5aW5nIG9uIHRoZWlyIGhvbWUgZ3JvdW5kIHdlIHNob3VsZCBiZSBhYmxlIHRvIHNlZSBpdCBhY3Jvc3MgYWxsIHRoZSBtYWluIHZlbnVlcy4KClRvIGRvIHRoaXMgd2UgaGFkIHRvIGV4dHJhY3QgdGhlIHdpbnMgYW5kIGxvc3NlcyB0aGF0IGVhY2ggdGVhbSBoYXMgaGFkIHdoZW4gb25seSBsb29raW5nIGF0IGEgc3BlY2lmaWMgdmVudWUuCgpgYGB7cn0KdmVudWVGaWx0ZXIgPC0gZnVuY3Rpb24odmVudWUpewogIAogIEFGTF92ZW51ZSA8LSBBRkxfY2xlYW5bQUZMX2NsZWFuJFZlbnVlID09IHZlbnVlLF0KICB2ZW51ZV90YWJsZSA8LSB0YWJsZShBRkxfdmVudWUkVGVhbSwgQUZMX3ZlbnVlJFdpbkxvc3MpCiAgdmVudWVfZGYgPC0gYXMuZGF0YS5mcmFtZS5tYXRyaXgodmVudWVfdGFibGUpCiAgdmVudWVfZnJlcSA8LSB2ZW51ZV9kZltvcmRlcih2ZW51ZV9kZiRgMWAsIGRlY3JlYXNpbmcgPSBUUlVFKSxdCiAgCiAgd2luX3BlcmMgPC0gdmVudWVfZnJlcSRgMWAgLyAodmVudWVfZnJlcSRgMGAgKyB2ZW51ZV9mcmVxJGAxYCkKICBsb3NzX3BlcmMgPC0gMSAtIHdpbl9wZXJjCiAgYWxsX3BlcmMgPC0gY2JpbmQodmVudWVfZnJlcSwgbG9zc19wZXJjLCB3aW5fcGVyYykKICBhbGxfcGVyYyA8LSBhbGxfcGVyY1szOjRdCiAgY29sbmFtZXMoYWxsX3BlcmMpIDwtIGMoIkxvc3MgUGVyY2VudGFnZSIsICJXaW4gUGVyY2VudGFnZSIpCiAgI2FsbF9wZXJjCiAgCiAgdGVhbV90b3RhbCA8LSAodmVudWVfZnJlcSRgMGAgKyB2ZW51ZV9mcmVxJGAxYCkKICBnYW1lX3RvdGFscyA8LSBzdW0odGVhbV90b3RhbCkKICB0ZWFtX3Byb3BvcnRpb24gPC0gdGVhbV90b3RhbCAvIGdhbWVfdG90YWxzCiAgdmVudWVfZnJlcSA8LSBjYmluZCh2ZW51ZV9mcmVxLCB0ZWFtX3Byb3BvcnRpb24pCiAgI3ZlbnVlX2ZyZXEKICAKICBhbGxfdmVudWUgPC0gY2JpbmQodmVudWVfZnJlcSwgYWxsX3BlcmMpCiAgYWxsX3ZlbnVlIDwtIGFsbF92ZW51ZVtvcmRlcihhbGxfdmVudWUkYFdpbiBQZXJjZW50YWdlYCwgZGVjcmVhc2luZyA9IFRSVUUpLF0KICByZXR1cm4oYWxsX3ZlbnVlKQoKfQpgYGAKCmBgYHtyfQpzdWJpYWNvIDwtIHZlbnVlRmlsdGVyKCJTdWJpYWNvIikKc3lkLnNob3dncm91bmQgPC0gdmVudWVGaWx0ZXIoIlN5ZG5leSBTaG93Z3JvdW5kIikKZ2FiYmEgPC0gdmVudWVGaWx0ZXIoIkdhYmJhIikKbWNnIDwtIHZlbnVlRmlsdGVyKCJNLkMuRy4iKQpzY2cgPC0gdmVudWVGaWx0ZXIoIlMuQy5HLiIpCmFkbC5vdmFsIDwtIHZlbnVlRmlsdGVyKCJBZGVsYWlkZSBPdmFsIikKY2FycmFyYSA8LSB2ZW51ZUZpbHRlcigiQ2FycmFyYSIpCmRvY2tsYW5kcyA8LSB2ZW51ZUZpbHRlcigiRG9ja2xhbmRzIikKZnRibC5wYXJrIDwtIHZlbnVlRmlsdGVyKCJGb290YmFsbCBQYXJrIikKYGBgCgpJJ3ZlIGp1c3QgaW5jbHVkZWQgdGhlIGNvZGUgZm9yIG9ubHkgbG9va2luZyBhdCBUaGUgR2FiYmEgYnV0IGV2ZXJ5IHNpbmdsZSBwbG90IHdhcyBtYWRlIHVzaW5nIHRoZSBzYW1lIGNvZGUgYW5kIGp1c3QgcmVwbGFjaW5nIHRoZSB2ZW51ZSB3aXRoIHdoaWNoZXZlciBvbmUgeW91IHdlcmUgaW50ZXJlc3RlZCBpbi4gCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKZ2FiYmEudHByb3AgPC0gZ2FiYmFbM10KYnAgPC0gZ2dwbG90KGdhYmJhLnRwcm9wLCBhZXMoeD0iIiwgeT10ZWFtX3Byb3BvcnRpb24sIGZpbGw9cm93Lm5hbWVzKGdhYmJhLnRwcm9wKSkgKSArIGdlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM5OTk5OTkiLCAiI0U2OUYwMCIsICIjNTZCNEU5IiwgIiM4ZmMwYTAiLCAiI2NjOWI1YiIsICIjNzE1MjliIiwgIiM3YmY2OGQiLCAiIzk2MjcwZiIsICIjMWU0NmJlIiwgIiNiNjBlZGMiLCAiIzY3OGRiZCIsICIjNGViMjBiIiwgIiM1ZjM3ZTUiLCAiIzAyMzM5YiIsICIjYTdhNGRmIiwgIiMyYjY4MWUiLCAiIzJjM2Y3YyIsICIjZDllZDhmIikpCmJwCgpwaWUgPC0gYnAgKyBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApCnBpZQpgYGAKCmBgYHtyIHQ5LCBmaWcud2lkdGg9MjgsIGZpZy5oZWlnaHQ9Nn0KZ2FiYmEud3BlcmMgPC0gZ2FiYmFbNDo1XQojc3ViaWFjby53cGVyYwoKbW9zYWljcGxvdChnYWJiYS53cGVyYywgY29sPWMocmdiKDIxNS8yNTUsMjUvMjU1LDI4LzI1NSkscmdiKDE2Ni8yNTUsMjE3LzI1NSwxMDYvMjU1KSksIG1haW4gPSAiVG90YWwgV2luIHZzIExvc3MgUGVyY2VudGFnZSBmb3IgZWFjaCB0ZWFtIGF0IFN1YmlhY28gT3ZhbCAoMjAxMi0yMDE2KSBbRGVzY2VuZGluZyBPcmRlcl0iLCBjZXguYXhpcyA9IDEuMikKYGBgCgpTbyB5b3UgZG9uJ3QgaGF2ZSB0byBsb29rIGF0IHRoZSBzYW1lIGNvZGUgdGVuIHRpbWVzIGFuZCBzcXVpbnQgdHJ5aW5nIHRvIGxvb2sgYXQgdGhlIGRvd25zaXplZCBSLW91dHB1dCBJJ3ZlIGp1c3QgaW5jbHVkZWQgdGhlIHNsaWRlcyBJIG1hZGUgZnJvbSB0aGUgcG93ZXIgcG9pbnQsIGJ1dCBsaWtlIEkgc2FpZCBiZWZvcmUgaXQgd2FzIGFsbCBtYWRlIHVzaW5nIHRoZSBleGFjdCBzYW1lIGNvZGUgYWJvdmUuCiAgCiMjIyMgU3ViaWFjbwoKU3ViaWFjbyBpcyB0aGUgaG9tZSBncm91bmQgb2YgRnJlbWFudGxlIGFuZCBXZXN0IENvYXN0IGFuZCBhcyBzZWVuIG9uIHRoZSBmaXJzdCBwbG90IHRoZXkncmUgYWxzbyBpbiB0aGUgdG9wIHR3byBwbGFjZXMgZm9yIHdpbiBwZXJjZW50YWdlIHdoZW4ganVzdCBsb29raW5nIGF0IGdhbWVzIHBsYXllZCBhdCBTdWJpYWNvLiBUaGUgc2Vjb25kIHBsb3Qgc2hvd3MgdGhlIGZyYWN0aW9uIG9mIGdhbWVzIHRoYXQgZWFjaCBpbmRpdmlkdWFsIHRlYW0gaGFzIHBsYXllZCBhdCBTdWJpYWNvIGFuZCBieSBubyBzdXJwcmlzZSBGcmVtYW50bGUgYW5kIFdlc3QgQ29hc3QgdG9nZXRoZXIgbWFrZSB1cCBhYm91dCA2MCUgb2YgdGhlIGdhbWVzLiBUaGlzIGxlYXZlcyB0aGUgb3RoZXIgMTYgdGVhbXMgdG8gc2hhcmUgdGhlIHJlbWFpbmluZyA0MCUgb2YgZ2FtZXMgYmV0d2VlbiB0aGVtc2VsdmVzLCB3aGljaCBlcXVhdGVzIHRvIGFib3V0IHNpeCBnYW1lcyBlYWNoLgoKQWZ0ZXIgbG9va2luZyBhdCB0aGVzZSBwbG90cyBpdCBtYWtlcyBhIGxvdCBtb3JlIHNlbnNlIHRoYXQgRnJlbWFudGxlIGFuZCBXZXN0IENvYXN0IGFyZSB0b3BwaW5nIHRoZSB3aW4gcGVyY2VudGFnZSBhdCBTdWJpYWNvIGJlY2F1c2UgdGhleSdyZSBzbyBkaXNwcm9wb3J0aW9uYXRlbHkgcmVwcmVzZW50ZWQgYXQgdGhlIHZlbnVlLiBOb3QgdG8gbWVudGlvbiB0aGF0IGFsbCB0aGUgb3RoZXIgdGVhbXMgaGF2ZSB0byBmbHkgcXVpdGUgYSBkaXN0YW5jZSB0byBnZXQgdG8gU3ViaWFjbywgdGhpcyBwcm9iYWJseSBjb250cmlidXRlcyBoZWF2aWx5IHRvIHRoZSBmYWN0IHRoYXQgdGhlIG1ham9yaXR5IG9mIHRoZSB0ZWFtcyBoYXZlIGxlc3MgdGhhbiBhIDUwJSB3aW4gcmF0ZS4KClJlbGF0aW5nIGJhY2sgdG8gb3VyIGdvYWwgb2YgcHJlZGljdGluZyBmdXR1cmUgb3V0Y29tZXMgb2YgZ2FtZXMsIHRoaXMgZ2l2ZXMgdXMgc29tZSB2ZXJ5IHVzZWZ1bCBpbmZvcm1hdGlvbi4gV2hlbiBCcmlzYmFuZSwgQ29sbGluZ3dvb2QsIE1lbGJvdXJuZSBvciBTdCBLaWxkYSBhcmUgcGxheWluZyBhdCBTdWJpYWNvIGl0J3MgcHJvYmFibHkgYSBnb29kIGlkZWEgdG8gYmV0IGFnYWluc3QgdGhlbSwgZXNwZWNpYWxseSBpZiB0aGV5J3JlIHVwIGFnYWluc3QgRnJlbWFudGxlIG9yIFdlc3QgQ29hc3QuCgohW10oc3ViaWFjb18yLnBuZykKIVtdKHN1YmlhY29fMy5wbmcpCgojIyMjIEFkZWxhaWRlIE92YWwKCkxvb2tpbmcgYXQgQWRlbGFpZGUgT3ZhbCB3ZSBzZWUgYSBzaW1pbGFyIHRyZW5kIHdpdGggdGhlIGhvbWUgdGVhbXMsIEFkZWxhaWRlIGFuZCBQb3J0IEFkZWxhaWRlLCB0b2dldGhlciBtYWtpbmcgdXAgYWJvdXQgNjAlIG9mIHRoZSB0b3RhbCBnYW1lcyBwbGF5ZWQgb24gdGhlaXIgaG9tZSB0dXJmLiBTaW1pbGFyaWx5LCB0aGlzIGxlYXZlcyB0aGUgb3RoZXIgMTYgdGVhbXMgdG8gc2hhcmUgdGhlIHJlbWFpbmluZyA0MCUgb2YgZ2FtZXMsIGJ1dCB0aGlzIHRpbWUgdGhhdCBvbmx5IGVxdWF0ZXMgdG8gYWJvdXQgZm91ciBnYW1lcyBlYWNoIChhcyBsZXNzIGdhbWVzIGhhdmUgYmVlbiBwbGF5ZWQgYXQgQWRlbGFpZGUgT3ZhbCBjb21wYXJlZCB0byBTdWJpYWNvKS4KCldoZW4gd2UgbG9vayBhdCB0aGUgd2luIHBlcmNlbnRhZ2VzIHRoZXJlJ3MgZGVmaW5pdGVseSBzb21lIGltcHJvdmVtZW50IGFzIFBvcnQgQWRlbGFpZGUgaGF2ZSBtb3ZlZCBmcm9tIG92ZXJhbGwgYmVpbmcgcmFua2VkIDEwdGggdG8gYmVpbmcgcmFua2VkIDZ0aCBob3dldmVyLCBBZGVsYWlkZSBoYXMgc3RheWVkIHN0YWduYW50IGluIHRoZSBzYW1lIHBvc2l0aW9uLiBJbnRlcmVzdGluZ2x5IHdlIGFsc28gaGF2ZSBhIGNvdXBsZSBkaWZmZXJlbnQgdGVhbXMgZG9taW5hdGluZyB0aGUgdG9wIHBvc2l0aW9ucyBhbmQgYSBjb3VwbGUgbW9yZSB0ZWFtcyBoYXZpbmcgbm90IGV2ZXIgb25lIGEgZ2FtZSBhdCB0aGUgdmVudWUuCgpVc2luZyB0aGlzIGluZm9ybWF0aW9uIHdlIGNvdWxkIHNheSB0aGF0IGlmIFBvcnQgQWRlbGFpZGUgaXMgcGxheWluZyBhIHRlYW0gdGhhdCdzIG9uIHBhciB3aXRoIHRoZW0sIGJ1dCB0aGV5J3JlIHBsYXlpbmcgYXQgQWRlbGFpZGUgT3ZhbCwgdGhlbiB0aGV5IG1pZ2h0IGp1c3QgaGF2ZSB0aGF0IGV4dHJhIGVkZ2UgZnJvbSB0aGUgaG9tZSBhZHZhbnRhZ2UgYW5kIGNvbWUgb3V0IG9uIHRvcC4gV2Ugc2VlIEJyaXNiYW5lLCBDb2xsaW5nd29vZCBhbmQgU3QgS2lsZGEgYmVpbmcgcmVwZWF0ZWQgb24gdGhlIGJvdHRvbSBvZiB0aGUgbGlzdCB3aXRoIG5vIHdpbnMgd2hhdCBzbyBldmVyLCBhZ2FpbiwgbWFraW5nIHlvdXIgY2hhbmNlcyBvZiB3aW5uaW5nIHdoZW4gYmV0dGluZyBhZ2FpbnN0IHRoZW0gKGlmIHRoZXkncmUgcGxheWluZyBhdCBTdWJpYWNvIG9yIEFkZWxhaWRlIE92YWwpIHRoYXQgbXVjaCBiZXR0ZXIuCgohW10oYWRsZG92bF8yLnBuZykKIVtdKGFkbGRvdmxfMy5wbmcpCgojIyMjIE0uQy5HLgoKVGhlIE0uQy5HIGlzIGEgbXVjaCBtb3JlIHBvcHVsYXIgdmVudWUgd2l0aCBhIHRvdGFsIG9mIDUwMCBnYW1lcyBwbGF5ZWQgYXQgaXQsIHdoaWNoIGlzIG1vcmUgZ2FtZXMgdGhhbiBTdWJpYWNvIGFuZCBBZGVsYWlkZSBPdmFsIGNvbWJpbmVkLiBJdCdzIGFsc28gaW4gTWVsYm91cm5lIHdoaWNoIGlzIHdpdGhpbiBhIGNvdXBsZSBob3VycyBkcml2ZSBvZiBvdmVyIGhhbGYgdGhlIEFGTCB0ZWFtcy4gVGhlc2UgZmFjdG9ycyBjb250cmlidXRlIHRoZSB0aGUgTS5DLkcgaGF2aW5nIGEgbXVjaCBtb3JlIGV2ZW4gc3ByZWFkIG9mIHRlYW0ncyB0b3RhbCBnYW1lcyBwbGF5ZWQgYXQgdGhlIHZlbnVlLCBhcyBzZWVuIG9uIHRoZSBzZWNvbmQgcGxvdCBlYWNoIGNsb3NlLWJ5IHRlYW0gaG9sZHMgYWJvdXQgNSUgLSAxMCUgb2YgdGhlIGdhbWVzIGluc3RlYWQgb2YgMzAlLgoKSG93ZXZlciwgZXZlbiB3aXRoIHRoZSBzcHJlYWQgYmVpbmcgYSBsb3QgbW9yZSBldmVuIGFuZCBubyB0ZWFtKHMpIGJlaW5nICpleHRyZW1lbGx5KiBkaXNwcm9wb3J0aW9uYXRlbHkgcmVwcmVzZW50ZWQsICoqd2Ugc3RpbGwqKiBzZWUgdGhpcyB0cmVuZCBvZiB0aGUgc3Vycm91bmRpbmcsIGNsb3NlLWJ5IHRlYW1zIHBsYXlpbmcgYmV0dGVyLiBFdmVyeSBzaW5nbGUgb24gb2YgdGhlIGNsb3NlLWJ5IHRlYW1zIGhhcyBlaXRoZXIgaGVsZCB0aGVpciBwb3NpdGlvbiBvciBpbXByb3ZpbmcgZHJhbWF0aWNhbGx5LCB3aXRoIHRoZSBXZXN0ZXJuIEJ1bGxkb2dzIGNsaW1iaW5nIG5lYXJseSBoYWxmIG9mIHRoZSByYW5rcy4gSG93ZXZlciwgd2Ugc3RpbGwgc2VlIEJyaXNiYW5lIGluIHRoZSBib3R0b20gZmV3IHJhbmtzIGFkZGluZyB0byB0aGUgcGlsZSBvZiBldmlkZW5jZSB0aGF0IHlvdSBzaG91bGQgcHJvYmFibHkgYWx3YXlzIGJldCBhZ2FpbnN0IHRoZW0gaWYgdGhleSdyZSBwbGF5aW5nIGFuIGF3YXkgZ2FtZS4KCiFbXShtY2dfMi5wbmcpCiFbXShtY2dfMy5wbmcpCgojIyMjIFMuQy5HLgoKVGhlIFMuQy5HIGhhcyB0aGUgc21hbGxlc3QgYW1vdW50IG9mIGdhbWVzIHBsYXllZCBhdCBpcyBjb21wYXJlZCB0byBhbGwgdGhlIG90aGVyIHZlbnVlcyB3ZSdyZSBsb29raW5nIGF0LCBvbmx5IDkwIGdhbWVzIGl0IHRvdGFsIHdoaWNoIGlzIGxlc3MgdGhhbiAxLzV0aCBvZiB0aGUgdG90YWwgZ2FtZXMgcGxheWVkIGF0IHRoZSBTLkMuRy4gVGhpcyBzb3J0IG9mIGV4cGxhaW5zIHRoZSAqKmV4dHJlbWUgZGlzcG9wb3J0aW9uYWxpdHkqKiB0aGF0IHdlIHNlZSBhdCB0aGUgdmVudWUsIHdpdGggU3lkbmV5IGhhdmluZyBwbGF5ZWQgYWJvdXQgKipoYWxmKiogb2YgYWxsIHRoZSBnYW1lcyBwbGF5ZWQgdGhlcmUuIEludGVyZXN0aW5nbHkgdGhvdWdoIEdXUyBwbGF5cyBqdXN0IGFib3V0IGFzIG9mdGVuIGFzIGFuIGF3YXkgdGVhbSBkZXNwaXRlIHRoZW0gYmVpbmcgc2l0dWF0ZWQgcXVpdGUgY2xvc2UgYnkgdG8gdGhlIHZlbnVlLiAKClRoZSB3aW4gcGVyY2VudGFnZXMgYXJlIHF1aXRlIHNrZXdlZCBoZXJlIGR1ZSB0byB0aGUgc21hbGwgYW1vdW50IG9mIGdhbWVzIHRoYXQgaGF2ZSBiZWVuIHBsYXllZCB0aGVyZSBieSBhd2F5IHRlYW1zLCBlYWNoIHRlYW0gaGFzIG9ubHkgcGxheWVkIGFib3V0IHR3byBvciB0aHJlZSBnYW1lcy4gVGhpcyBtYWtlcyBpdCBoYXJkIHRvIGdldCBhbnkgdXNlZnVsIGluZm9ybWF0aW9uIGZyb20gdGhlIHdpbiBwZXJjZW50YWdlIHBsb3QgaG93ZXZlciwgaXQgaGFzIHRvIGJlIG5vdGVkIHRoYXQgQnJpc2JhbmUgaGFzIG5vIHdpbnMgYWdhaW4gZGVzcGl0ZSBhY3R1YWxseSBiZWluZyByb3VnaGx5IG9uZSBvZiB0aGUgY2xvc2VzdCBhd2F5IHRlYW1zIHRvIHRoZSB2ZW51ZS4KCiFbXShzY2dfMi5wbmcpCiFbXShzY2dfMy5wbmcpCgojIyMjIFRoZSBHYWJiYQoKTG9va2luZyBhdCBUaGUgR2FiYmEgd2UgY2FuIGZ1bGx5IGNvbmZpcm0gdGhhdCB0aGluZ3MgcmVhbGx5IGFyZW4ndCBsb29raW5nIHRvbyBncmVhdCBmb3IgQnJpc2JhbmUuIERlc3BpdGUgaXQgYmVpbmcgdGhlaXIgaG9tZSBncm91bmQgYW5kIHRoZW4gaGF2aW5nIHBsYXllZCBhYm91dCA1MCUgb2YgdGhlIHRvdGFsIGdhbWVzIGF0IHRoZSB2ZW51ZSB0aGV5IGFyZSBzdGlsbCB2ZXJ5IGNsb3NlIHRvIHRoZSBib3R0b20gb2YgdGhlIHJhbmtzLiBBZ2FpbiBpdCBpcyBzdHJhbmdlIGhvdyBHb2xkIENvYXN0IGlzIHdpdGhpbiBhbiBob3VycyBkcml2ZSBvZiBUaGUgR2FiYmEgeWV0IHRoZXkgc3RpbGwgcGxheSB0aGVyZSBhcyBvZnRlbiBhcyBhbiBhd2F5IHRlYW0uCgpUaGUgR2FiYmEgZG9lc24ndCBoYXZlIG1hbnkgbW9yZSBnYW1lcyBwbGF5ZWQgYXQgaXQgdGhhbiB0aGUgUy5DLkcgaG93ZXZlciwgaXQncyBpbnRlcmVzdGluZyB0aGF0IHRoZSBza2V3IGlzIHNvcnQgb2YgaW4gdGhlIGNvbXBsZXRlbHkgb3Bwb3NpdGUgZGlyZWN0aW9uLCB3aXRoIG5lYXJseSBoYWxmIHRoZSB0ZWFtcyBoYXZpbmcgYSAob3IgY2xvc2UgdG8gYSkgMTAwJSB3aW4gcmF0ZS4gVGhpcyBtYWtlcyBzZW5zZSB3aGVuIHlvdSBjb25zaWRlciB0aGF0IG1vc3Qgb2YgdGhlIHRlYW1zIHdvdWxkIGJlIGZhY2luZyB1cCBhZ2FpbnN0IHRoZSBob21lIHRlYW0sIEJyaXNiYW5lLCB3aG8gd2UndmUgYWxyZWFkeSBlc3RhYmxpc2hlZCBwcm9iYWJseSBhcmVuJ3QgdGhlIGJlc3QgdGVhbSB0byBiZSBwdXR0aW5nIHlvdXIgbW9uZXkgb24uCgohW10oZ2FiYmFfMi5wbmcpCiFbXShnYWJiYV8zLnBuZykKCioqKgoKIyMgX19Db25jbHVzaW9uc19fCgpXZSBzZXQgb3V0IHRvIGFjaGlldmUgcXVpdGUgYW4gYW1iaXRpb3VzIGdvYWw7IGNyZWF0ZSBzb21lIHNvcnQgb2YgcmVhbGx5IGludGVsbGlnZW50IHN0YXRpc3RpY2FsIG1vZGVsIGZvciBwcmVkaWN0aW5nIHdobyBtaWdodCB3aW4gYW55IGdpdmVuIEFGTCBtYXRjaC4gVW5mb3J0dW5hdGVseSwgd2UgZGlkbid0IHJlYWxseSBjb21lIGNsb3NlIHRvIGNyZWF0aW5nIHRoaXMgcGVyZmVjdCBtb2RlbCB3ZSBkcmVhbWVkIG9mIGhvd2V2ZXIsIG91ciBhbmFseXNpcyBhcyBhIHdob2xlIHdhcyBubyB3aGVyZSBuZWFyIHVzZWxlc3MsIGV2ZW4gY29uc2lkZXJpbmcgd2hhdCB3ZSBzZXQgb3V0IHRvIGFjaGlldmUuCgpPdXIgcHJlZGljdGl2ZSBtb2RlbHMgdGhlbXNlbHZlcyB3ZXJlIHByZXR0eSB1c2VsZXNzIGFzIHRoZXkgcmVxdWlyZSBhIGdhbWUgdG8gaGF2ZSBhbHJlYWR5IGZpbmlzaGVkIHRvIGFjdHVhbGx5IGNsYXNzaWZ5IHRoZSBnYW1lIHdpdGggc29tZSBzdWNjZXNzLiBIb3dldmVyLCB0aGV5J3ZlIHNob3duIHRoYXQgdGhlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyB0aGV5IGNvbnRhaW4gbXVzdCBoYXZlIHNvbWUgc29ydCBvZiBzaWduaWZpY2FudCBpbXBhY3Qgb24gd2hldGhlciBhIHRlYW0gd2lsbCB3aW4gb3IgbG9zZS4gV2UgYWxzbyBtYW5hZ2VkIHRvIGdldCB0aGF0IHF1aXRlIGluc2lnaHRmdWwgdHJlZSBkaWFncmFtIG91dCBvZiBvbmUgb2Ygb3VyIHByZWRpY3RpdmUgbW9kZWxzIHdoaWNoIGlzIHVzZWZ1bCBmb3IgZGV0ZXJtaW5pbmcgd2hhdCBjb21iaW5hdGlvbnMgb2YgJ2JlaGF2aW9ycycgd2l0aGluIGFuIEFGTCBnYW1lIGFyZSBtb3JlIGxpa2VseSB0byBsZWFkIHRvIGEgdGVhbSB3aW5uaW5nLgoKVWx0aW1hdGVseSwgdGhlIG1hbnksICptYW55KiBwbG90cyB0aGF0IHdlIGNyZWF0ZWQgYmFzZWQgYXJvdW5kIHRoZSB2YXJpb3VzIGRpZmZlcmVudCB2ZW51ZXMgYXJlIHNvbWUgb2YgdGhlIG1vc3QgdXNlZnVsIHBhcnRzIG9mIHRoZSBhbmFseXNpcyB3aXRoIHJlc3BlY3QgdG8gb3VyIGFjdHVhbCBnb2FsLiBXaGVuZXZlciB0d28gdGVhbXMgYXJlIHBsYXlpbmcgYXQgYW55IG9uZSBvZiB0aG9zZSB2ZW51ZXMgd2UgY2FuIGdsYW5jZSBvdmVyIGF0IG91ciBwbG90cyBhbmQgaGF2ZSBxdWl0ZSBhIGdvb2Qgb3ZlcnZpZXcgb2YgaG93IHdlbGwgZWFjaCB0ZWFtIHBsYXlzIHJlbGF0aXZlIHRvIG9uZSBhbm90aGVyLiBJdCdzIG5vdGhpbmcgY2xvc2UgdG8gcHJlZGljdGluZyBhbnkgdGVhbSB3aWxsIHdpbiB3aXRoIDk1JSBjb25maWRlbmNlIGJ1dCBpdCdzIGRlZmluaXRlbHkgdXNlZnVsIGluZm9ybWF0aW9uLCBhbmQgcHJvdmlkZXMgYXQgbGVhc3QgKmEgbGl0dGxlIGJpdCogb2YgYW4gYWR2YW50YWdlIGNvbXBhcmVkIHRvIGdvaW5nIGludG8gdGhlIGJldCBjb21wbGV0ZWx5IGJsaW5kLgoKSSB0aGluayB0aGF0IHdlJ3JlIGhlbGQgYmFjayBhIGxpdHRsZSBiaXQgYnkgb3VyIGRhdGEsIHdlIG9ubHkgaGF2ZSB0aGUgc3RhdGlzdGljcyBmb3IgYWZ0ZXIgYSBnYW1lIGhhcyBiZWVuIHBsYXllZC4gSWYgd2UgaGFkIGFjY2VzcyB0byBzb21lIHNvcnQgb2YgJ3JlYWwtdGltZScgZGF0YSwgd2hlcmUgd2UgY291bGQgc2VlIGEgbGlzdCBvZiB0aGVzZSBzdGF0aXN0aWNzIGF0IGNlcnRhaW4gdGltZXMgd2l0aGluIHRoZSBnYW1lIEkgdGhpbmsgaXQgd291bGQgYmUgbXVjaCBtb3JlIHZhbHVhYmxlIHRvIHdoYXQgd2Ugd2FudGVkIHRvIGFjaGlldmUgKEkgc3VzcGVjdCB0aGF0J3MgaG93IGJpZyBiZXR0aW5nIGNvbXBhbmllcyBtdXN0IGRvIGl0KS4gTWF5YmUgdGhlcmUncyBzb21lIHNvcnQgb2YgYSB0cmVuZCByaWdodCBiZWZvcmUgYSB0ZWFtIGlzIGFib3V0IHRvIG1ha2UgYSBjb21lYmFjayB0aGF0IHlvdSBjb3VsZCBwaWNrIG91dD8gSG93ZXZlciwgaXQgd291bGQgb25seSByZWFsbHkgYmUgcGxhdXNpYmxlIGlmIHlvdSBoYWQgdGhlIG1hdGNoIGRhdGEgb3ZlciB0aW1lLgoKSSB0aGluayB0aGF0IHRvIGltcHJvdmUgb3VyIHByb2plY3Qgd2Ugc2hvdWxkIGhhdmUgZGVmaW5pdGVseSBoYWQgYSBtb3JlIGluIGRlcHRoIHBsYW5uaW5nIHByb2Nlc3MgYW5kIGFwcHJvYWNoZWQgdGhlIGRhdGEgc2V0IGFmdGVyIGhhdmluZyBhIHZlcnkgY2xlYXIsIGNvbmNpc2UgZ29hbCBvZiB3aGF0IG5lZWRzIHRvIGdldCBkb25lIGluIG1pbmQuCgpPdmVyYWxsLCB3ZSd2ZSBjb21lIG91dCB3aXRoIHNvbWUgdXNlZnVsIHJlc291cmNlcyBmb3IgYXNzaXN0aW5nIGluIHByZWRpY3RpbmcgYSB3aW5uaW5nIHRlYW0gYW5kIGNsaW1iZWQgYSBtb3VudGFpbiBvZiBSLWNvZGUgYWxvbmcgdGhlIHdheSAtIHNvIEknZCBzYXkgaXQgd2FzIGEgc3VjY2Vzcy4KCioqKgoqKioKCiMjIF9fQXBwZW5kaXhfXwoKYGBge3J9CkFGTHN0YXRzIDwtIHJlYWQuY3N2KCJzdGF0cy5jc3YiKQpgYGAKCgpgYGB7cn0KQUZMIDwtIEFGTHN0YXRzCkFGTCA8LSBBRkxbYygxLDMsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQsMTUsMTYsMTcsMTgsMTksMjAsMjEsMjIsMjMsMjUsMjcsMjgsMjksMzAsMzEpXQpjb2xuYW1lcyhBRkwpCmBgYAoKYGBge3J9CnN1bV9uYW1lcyA8LSBjb2xuYW1lcyhBRkwpWzEwOjI4XQpzdW1fbmFtZXMKYGBgCgpgYGB7cn0KYWxsX3RlYW1zIDwtIHVuaXF1ZShhcy5jaGFyYWN0ZXIoQUZMJFRlYW0pKQpgYGAKCgpgYGB7cn0KYWQgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxXSxdCmJsIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbMl0sXQpjbHQgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1szXSxdCmNsd2QgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1s0XSxdCmVzZG4gPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1s1XSxdCmZkIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbNl0sXQpnbG5nIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbN10sXQpnYyA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzhdLF0KZ3dzIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbOV0sXQpodCA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzEwXSxdCm1sYiA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzExXSxdCm5tbGIgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxMl0sXQpwYWQgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxM10sXQpybWQgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxNF0sXQpzayA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzE1XSxdCnN5ZCA8LSBBRkxbQUZMJFRlYW0gPT0gYWxsX3RlYW1zWzE2XSxdCndjIDwtIEFGTFtBRkwkVGVhbSA9PSBhbGxfdGVhbXNbMTddLF0Kd2IgPC0gQUZMW0FGTCRUZWFtID09IGFsbF90ZWFtc1sxOF0sXQpgYGAKCgpgYGB7cn0KY2xlYW5BRkxkYXRhIDwtIGZ1bmN0aW9uKHRlYW0pewogIAogIGxpYnJhcnkocGx5cikKICB0ZWFtX3N0YXRzIDwtIGRkcGx5KHRlYW0sIC4oRGF0ZSksIG51bWNvbHdpc2Uoc3VtKSkKICB0ZWFtX3N0YXRzIDwtIHRlYW1fc3RhdHNbYygxLDU6MjMpXQogIHRlYW1fa2VlcCA8LSB0ZWFtW21hdGNoKHVuaXF1ZSh0ZWFtJERhdGUpLCB0ZWFtJERhdGUpLF0KICB0ZWFtX2tlZXAgPC0gdGVhbV9rZWVwWzE6OV0KICB0ZWFtX2NsZWFuIDwtIG1lcmdlKHRlYW1fa2VlcCwgdGVhbV9zdGF0cykKICB0ZWFtX2NsZWFuIDwtIHRlYW1fY2xlYW5bdGVhbV9jbGVhbiRXaW5Mb3NzICE9ICJEIixdCiAgdGVhbV9jbGVhbiRXaW5Mb3NzIDwtIGRyb3BsZXZlbHModGVhbV9jbGVhbiRXaW5Mb3NzKQogIGxldmVscyh0ZWFtX2NsZWFuJFdpbkxvc3MpW2xldmVscyh0ZWFtX2NsZWFuJFdpbkxvc3MpPT0iTCJdIDwtIDAKICBsZXZlbHModGVhbV9jbGVhbiRXaW5Mb3NzKVtsZXZlbHModGVhbV9jbGVhbiRXaW5Mb3NzKT09IlciXSA8LSAxCiAgdGVhbV9jbGVhbiRXaW5Mb3NzIDwtIGZhY3Rvcih0ZWFtX2NsZWFuJFdpbkxvc3MpCiAgCiAgcmV0dXJuKHRlYW1fY2xlYW4pCiAgCn0KYGBgCgpgYGB7cn0Kc3lkX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShzeWQpCmhlYWQoc3lkX2NsZWFuKQpgYGAKCgpgYGB7cn0KYWRfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKGFkKQpibF9jbGVhbiA8LSBjbGVhbkFGTGRhdGEoYmwpCmNsdF9jbGVhbiA8LSBjbGVhbkFGTGRhdGEoY2x0KQpjbHdkX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShjbHdkKQplc2RuX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShlc2RuKQpmZF9jbGVhbiA8LSBjbGVhbkFGTGRhdGEoZmQpCmdsbmdfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKGdsbmcpCmdjX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShnYykKZ3dzX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShnd3MpCmh0X2NsZWFuIDwtIGNsZWFuQUZMZGF0YShodCkKbWxiX2NsZWFuIDwtIGNsZWFuQUZMZGF0YShtbGIpCm5tbGJfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKG5tbGIpCnBhZF9jbGVhbiA8LSBjbGVhbkFGTGRhdGEocGFkKQpybWRfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKHJtZCkKc2tfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKHNrKQpzeWRfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKHN5ZCkKd2NfY2xlYW4gPC0gY2xlYW5BRkxkYXRhKHdjKQp3Yl9jbGVhbiA8LSBjbGVhbkFGTGRhdGEod2IpCmBgYAoKYGBge3J9CkFGTF9jbGVhbiA8LSByYmluZChhZF9jbGVhbiwgYmxfY2xlYW4sIGNsdF9jbGVhbiwgY2x3ZF9jbGVhbiwgZXNkbl9jbGVhbiwgZmRfY2xlYW4sIGdsbmdfY2xlYW4sIAogICAgICAgICAgICAgICAgICAgZ2NfY2xlYW4sIGd3c19jbGVhbiwgaHRfY2xlYW4sIG1sYl9jbGVhbiwgbm1sYl9jbGVhbiwgcGFkX2NsZWFuLCBybWRfY2xlYW4sCiAgICAgICAgICAgICAgICAgICBza19jbGVhbiwgc3lkX2NsZWFuLCB3Y19jbGVhbiwgd2JfY2xlYW4pCmxlbmd0aCh1bmlxdWUoQUZMX2NsZWFuJFRlYW0pKQpgYGAKCgpgYGB7cn0KQUZMLnRlc3QgPC0gQUZMX2NsZWFuW0FGTF9jbGVhbiRTZWFzb24gPT0gMjAxNixdCmhlYWQoQUZMLnRlc3QpCkFGTC50cmFpbiA8LSBBRkxfY2xlYW5bQUZMX2NsZWFuJFNlYXNvbiAhPSAyMDE2LF0KaGVhZChBRkwudHJhaW4pCmBgYAoKYGBge3J9CnBsb3QoQUZMLnRyYWluJFdpbkxvc3MsIEFGTC50cmFpbiREaXNwb3NhbHMpCmBgYAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKcXBsb3QoV2luTG9zcywgRGlzcG9zYWxzLCBkYXRhID0gQUZMLnRyYWluKQpgYGAKCmBgYHtyIHRlc3QyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTIsIGV2YWw9RkFMU0V9CnNvdXJjZSgiUGFpcnNXaXRoSGlzdC5SIikKcGFpcnMoQUZMX2NsZWFuLCBsb3dlci5wYW5lbCA9IHBhbmVsLnB0cywgdXBwZXIucGFuZWwgPSBwYW5lbC5jb3IsIGRpYWcucGFuZWwgPSBwYW5lbC5oaXN0KQpgYGAKCgpgYGB7cn0KIyBqdXN0ICdudW1lcmljYWwnIHZhcmlhYmxlcwpBRkwuZ2xtLm51bSA8LSBnbG0oV2luTG9zcyB+ICBEaXNwb3NhbHMgKyBNYXJrcyArIEJlaGluZHMgKyBJbnNpZGU1MHMgKyBDbGVhcmFuY2VzICsgQ2xhbmdlcnMgKyBDb250ZW5kZWRQb3NzZXNzaW9ucyArIENvbnRlc3RlZE1hcmtzICsgTWFya3NJbnNpZGU1MCwgCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBBRkwudHJhaW4sIGZhbWlseSA9IGJpbm9taWFsKGxvZ2l0KSkKYGBgCgpgYGB7cn0KIyAnYWxsJyB2YXJpYWJsZXMgKE1hcmdpbiBleGNsdWRlZCBiZWNhdXNlIHRvbyBwZXJmZWN0KQpBRkwuZ2xtLmFsbCA8LSBnbG0oV2luTG9zcyB+ICBTY29yZSArIE9wcG9zaXRpb24gKyBWZW51ZSArIERpc3Bvc2FscyArIE1hcmtzICsgR29hbHMgKyBCZWhpbmRzICsgSGl0b3V0cyArIFRhY2tsZXMgKyBSZWJvdW5kNTBzICsgSW5zaWRlNTBzICsgQ2xlYXJhbmNlcyArIENsYW5nZXJzICsgRnJlZXNGb3IgKyBDb250ZW5kZWRQb3NzZXNzaW9ucyArIENvbnRlc3RlZE1hcmtzICsgTWFya3NJbnNpZGU1MCArIE9uZVBlcmNlbnRlcnMgKyBCb3VuY2VzK0dvYWxBc3Npc3RzLCAKICAgICAgICAgICAgICAgICAgZGF0YSA9IEFGTC50cmFpbiwgZmFtaWx5ID0gYmlub21pYWwpCiNzdW1tYXJ5KEFGTC5nbG0uYWxsKQpgYGAKCgpgYGB7cn0KQUZMLmdsbS4wIDwtIGdsbShXaW5Mb3NzIH4gMSwgZGF0YSA9IEFGTC50cmFpbiwgZmFtaWx5ID0gYmlub21pYWwobG9naXQpKQpgYGAKCmBgYHtyfQpBRkwuZ2xtLmZ3ZCA8LSBzdGVwKEFGTC5nbG0uMCwgc2NvcGUgPSBmb3JtdWxhKEFGTC5nbG0ubnVtKSwgZGlyZWN0aW9uID0gImZvcndhcmQiLCB0cmFjZSA9IDApCnN1bW1hcnkoQUZMLmdsbS5md2QpCmBgYAoKYGBge3J9CkFGTC5hbGwucHJlZCA8LSBwcmVkaWN0KEFGTC5nbG0uZndkLCBuZXdkYXRhID0gQUZMLnRlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQpoZWFkKEFGTC5hbGwucHJlZCkKYGBgCgpgYGB7cn0KQUZMLmFsbC5wcmVkLmNsYXNzIDwtIHJlcCgwLCBsZW5ndGgoQUZMLmFsbC5wcmVkKSkKQUZMLmFsbC5wcmVkLmNsYXNzW0FGTC5hbGwucHJlZCA+IDAuNzVdIDwtIDEKdGFibGUoQUZMLmFsbC5wcmVkLmNsYXNzLCBBRkwudGVzdCRXaW5Mb3NzKQpgYGAKCmBgYHtyfQooMTg4KzEzMikvKDE4OCs3NSsxOSsxMzIpCmBgYAoKYGBge3J9CmxpYnJhcnkocnBhcnQpCkFGTC50cmVlIDwtIHJwYXJ0KFdpbkxvc3MgfiAgT3Bwb3NpdGlvbiArIFZlbnVlICsgRGlzcG9zYWxzICsgTWFya3MgKyBHb2FscyArIEJlaGluZHMgKyBIaXRvdXRzICsgVGFja2xlcyArIFJlYm91bmQ1MHMgKyBJbnNpZGU1MHMgKyBDbGVhcmFuY2VzICsgQ2xhbmdlcnMgKyBGcmVlc0ZvciArIENvbnRlbmRlZFBvc3Nlc3Npb25zICsgQ29udGVzdGVkTWFya3MgKyBNYXJrc0luc2lkZTUwICsgT25lUGVyY2VudGVycyArIEJvdW5jZXMgKyBHb2FsQXNzaXN0cywgCiAgICAgICAgICAgICAgICAgIEFGTF9jbGVhbikKI3N1bW1hcnkoQUZMLnRyZWUpCmBgYAoKYGBge3J9CmxpYnJhcnkocnBhcnQpCkFGTC5udW0udHJlZSA8LSBycGFydChXaW5Mb3NzIH4gIERpc3Bvc2FscyArIE1hcmtzICsgQmVoaW5kcyArIEluc2lkZTUwcyArIENsZWFyYW5jZXMgKyBDbGFuZ2VycyArIENvbnRlbmRlZFBvc3Nlc3Npb25zICsgQ29udGVzdGVkTWFya3MgKyBNYXJrc0luc2lkZTUwLCAKICAgICAgICAgICAgICAgICAgQUZMX2NsZWFuKQpgYGAKCgpgYGB7cn0KQUZMLm51bS50cmVlLnByZWQgPSBwcmVkaWN0KEFGTC5udW0udHJlZSwgQUZMLnRlc3QpCkFGTC5udW0udHJlZS5wcmVkIDwtIEFGTC5udW0udHJlZS5wcmVkWywyXQpoZWFkKEFGTC5udW0udHJlZS5wcmVkKQpgYGAKCmBgYHtyfQpBRkwubnVtLnRyZWUuY2xhc3MgPC0gcmVwKDAsIGxlbmd0aChBRkwubnVtLnRyZWUucHJlZCkpCkFGTC5udW0udHJlZS5jbGFzc1tBRkwubnVtLnRyZWUucHJlZCA+IDAuNzVdIDwtIDEKdGFibGUoQUZMLm51bS50cmVlLmNsYXNzLCBBRkwudGVzdCRXaW5Mb3NzKQpgYGAKCmBgYHtyfQooMTcyICsgMTM3KS8oMTcyKzcwKzM1KzEzNykKYGBgCgoKYGBge3IgdGVzdDMsIGZpZy53aWR0aD0yMCwgZmlnLmhlaWdodD0xMiwgZXZhbD1GQUxTRX0KbGlicmFyeShycGFydC5wbG90KQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKbGlicmFyeShwYXJ0eWtpdCkKcGxvdChhcy5wYXJ0eShBRkwudHJlZSkpCmBgYAoKYGBge3J9CmxpYnJhcnkocnBhcnQpCkFGTC50ZWFtcyA8LSBycGFydChXaW5Mb3NzIH4gIFRlYW0gKyBPcHBvc2l0aW9uLAogICAgICAgICAgICAgICAgICBBRkxfY2xlYW4pCmBgYAoKYGBge3IgdGVzdDQsIGZpZy53aWR0aD02MCwgZmlnLmhlaWdodD0yMCwgZXZhbD1GQUxTRX0KbGlicmFyeShycGFydC5wbG90KQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkKbGlicmFyeShwYXJ0eWtpdCkKcGxvdChhcy5wYXJ0eShBRkwudGVhbXMpKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KHJwYXJ0KQpBRkwudmVudWUgPC0gcnBhcnQoV2luTG9zcyB+ICBUZWFtICsgVmVudWUsCiAgICAgICAgICAgICAgICAgIEFGTF9jbGVhbikKYGBgCgpgYGB7ciB0ZXN0NSwgZmlnLndpZHRoPTUwLCBmaWcuaGVpZ2h0PTIwLCBldmFsPUZBTFNFfQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHBhcnR5a2l0KQpwbG90KGFzLnBhcnR5KEFGTC52ZW51ZSkpCmBgYAoKYGBge3IgMDAwMDIsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03LCBldmFsPUZBTFNFfQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHBhcnR5a2l0KQpwbG90KGFzLnBhcnR5KEFGTC5udW0udHJlZSkpCmBgYAoKYGBge3J9CmxpYnJhcnkoY2xhc3MpCkFGTC5rbm4ucHJlZCA8LSBrbm4oQUZMLnRyYWluWyxjKCJEaXNwb3NhbHMiLCAiTWFya3MiLCAiQmVoaW5kcyIsICJJbnNpZGU1MHMiLCAiQ2xlYXJhbmNlcyIsICJDbGFuZ2VycyIsICJDb250ZW5kZWRQb3NzZXNzaW9ucyIsICJDb250ZXN0ZWRNYXJrcyIsICJNYXJrc0luc2lkZTUwIildLCBBRkwudGVzdFssYygiRGlzcG9zYWxzIiwgIk1hcmtzIiwgIkJlaGluZHMiLCAiSW5zaWRlNTBzIiwgIkNsZWFyYW5jZXMiLCAiQ2xhbmdlcnMiLCAiQ29udGVuZGVkUG9zc2Vzc2lvbnMiLCAiQ29udGVzdGVkTWFya3MiLCAiTWFya3NJbnNpZGU1MCIpXSwgQUZMLnRyYWluJFdpbkxvc3MsIAogICAgayA9IDMpCnRhYmxlKEFGTC5rbm4ucHJlZCwgQUZMLnRlc3QkV2luTG9zcykKYGBgCgpgYGB7cn0KKDE0MysxNjgpLygxNDMrMzkrNjQrMTY4KQpgYGAKCmBgYHtyfQp1bmlxdWUoQUZMX2NsZWFuJFZlbnVlKQpucm93KEFGTF9jbGVhbltBRkxfY2xlYW4kVmVudWUgPT0gIlRyYWVnZXIgUGFyayIsXSkKYGBgCgpgYGB7cn0KdGFibGUoQUZMX2NsZWFuJFZlbnVlKQpgYGAKCgpgYGB7cn0KdGFibGVfYWxsIDwtIHRhYmxlKEFGTF9jbGVhbiRUZWFtLCBBRkxfY2xlYW4kV2luTG9zcykgICMgd2lucyBhbmQgbG9zc2VzIGZvciBlYWNoIHRlYW0KCiMgY3JlYXRpbmcgdGFibGVzIG9mIGVhY2ggdGVhbSBhbmQgdGhpZXIgd2lucy9sb3NzZXMgYWdhaW5zdCBvcHBvc2l0aW9uCnRhYmxlX2FkIDwtIHRhYmxlKGFkX2NsZWFuJE9wcG9zaXRpb24sIGFkX2NsZWFuJFdpbkxvc3MpCnRhYmxlX3djIDwtIHRhYmxlKHdjX2NsZWFuJE9wcG9zaXRpb24sIHdjX2NsZWFuJFdpbkxvc3MpCnRhYmxlX2JsIDwtIHRhYmxlKGJsX2NsZWFuJE9wcG9zaXRpb24sIGJsX2NsZWFuJFdpbkxvc3MpCnRhYmxlX2NsdCA8LSB0YWJsZShjbHRfY2xlYW4kT3Bwb3NpdGlvbiwgY2x0X2NsZWFuJFdpbkxvc3MpCnRhYmxlX2Nsd2QgPC0gdGFibGUoY2x3ZF9jbGVhbiRPcHBvc2l0aW9uLCBjbHdkX2NsZWFuJFdpbkxvc3MpCnRhYmxlX2VzZG4gPC0gdGFibGUoZXNkbl9jbGVhbiRPcHBvc2l0aW9uLCBlc2RuX2NsZWFuJFdpbkxvc3MpCnRhYmxlX2ZkIDwtIHRhYmxlKGZkX2NsZWFuJE9wcG9zaXRpb24sIGZkX2NsZWFuJFdpbkxvc3MpCnRhYmxlX2djIDwtIHRhYmxlKGdjX2NsZWFuJE9wcG9zaXRpb24sIGdjX2NsZWFuJFdpbkxvc3MpCnRhYmxlX2dsbmcgPC0gdGFibGUoZ2xuZ19jbGVhbiRPcHBvc2l0aW9uLCBnbG5nX2NsZWFuJFdpbkxvc3MpCnRhYmxlX2d3cyA8LSB0YWJsZShnd3NfY2xlYW4kT3Bwb3NpdGlvbiwgZ3dzX2NsZWFuJFdpbkxvc3MpCmBgYAoKCmBgYHtyIHQsIGZpZy53aWR0aD0yNSwgZmlnLmhlaWdodD0xMCwgZXZhbD1GQUxTRX0KIyBQbG90dGluZyBNb3NhaWNzCgpsaWJyYXJ5KHZjZCkKCm1vc2FpY3Bsb3QodGFibGVfYWxsLCBjb2w9YygicmVkIiwiZGFya2dyZWVuIiksIG1haW4gPSAiV2lucyZMb3NzZXMgYWdhaW5zdCBlYWNoIHRlYW0gKDIwMTItMjAxNikiLCB4bGFiID0gIlRlYW0iLCB5bGFiID0gIldpbiBvciBsb3NzIikKCm1vc2FpY3Bsb3QodGFibGVfd2MsIGNvbD1jKCJyZWQiLCJkYXJrZ3JlZW4iKSxtYWluID0gIldlc3QgQ29hc3QgYW5kIHRoZWlyIHdpbnMgYW5kIGxvc3NlcyBhZ2FpbnN0IGVhY2ggdGVhbSAoMjAxMi0yMDE2KSIsICB4bGFiID0gIlRlYW0iLCB5bGFiID0gIldpbiBvciBsb3NzIikKCm1vc2FpY3Bsb3QodGFibGVfYWQsIGNvbD1jKCJyZWQiLCJkYXJrZ3JlZW4iKSwgbWFpbiA9ICJBZGVsYWlkZSBhbmQgdGhlaXIgd2lucyBhbmQgbG9zc2VzIGFnYWluc3QgZWFjaCB0ZWFtICgyMDEyLTIwMTYpIiwgIHhsYWIgPSAiVGVhbSIsIHlsYWIgPSAiV2luIG9yIGxvc3MiKQoKbW9zYWljcGxvdCh0YWJsZV9ibCwgY29sPWMoInJlZCIsImRhcmtncmVlbiIpLG1haW4gPSAiQnJpc2JhbmUgTGlvbnMgYW5kIHRoZWlyIHdpbnMgYW5kIGxvc3NlcyBhZ2FpbnN0IGVhY2ggdGVhbSAoMjAxMi0yMDE2KSIsICB4bGFiID0gIlRlYW0iLCB5bGFiID0gIldpbiBvciBsb3NzIikKCm1vc2FpY3Bsb3QodGFibGVfY2x0LCBjb2w9YygicmVkIiwiZGFya2dyZWVuIiksbWFpbiA9ICJDYXJsdG9uIGFuZCB0aGVpciB3aW5zIGFuZCBsb3NzZXMgYWdhaW5zdCBlYWNoIHRlYW0gKDIwMTItMjAxNikiLCAgeGxhYiA9ICJUZWFtIiwgeWxhYiA9ICJXaW4gb3IgbG9zcyIpCgptb3NhaWNwbG90KHRhYmxlX2Nsd2QsIGNvbD1jKCJyZWQiLCJkYXJrZ3JlZW4iKSxtYWluID0gIkNvbGxpbmd3b29kIGFuZCB0aGVpciB3aW5zIGFuZCBsb3NzZXMgYWdhaW5zdCBlYWNoIHRlYW0gKDIwMTItMjAxNikiLCAgeGxhYiA9ICJUZWFtIiwgeWxhYiA9ICJXaW4gb3IgbG9zcyIpCmBgYAoKCmBgYHtyfQphbGxfZnJlcSA8LSBhcy5kYXRhLmZyYW1lLm1hdHJpeCh0YWJsZV9hbGwpCmFsbF9mcmVxIDwtIGFsbF9mcmVxW29yZGVyKGFsbF9mcmVxJGAxYCwgZGVjcmVhc2luZyA9IFRSVUUpLF0KYWxsX2ZyZXEKYGBgCgpgYGB7ciB0NiwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTh9Cm1vc2FpY3Bsb3QoYWxsX2ZyZXEsIGNvbD1jKCJyZWQiLCJkYXJrZ3JlZW4iKSwgbWFpbiA9ICJXaW4gdnMgTG9zcyBGcmVxdWVuY3kgZm9yIGVhY2ggdGVhbSAoMjAxMi0yMDE2KSIsIGNleC5heGlzID0gMS4yKQpgYGAKCmBgYHtyfQp3aW5fcGVyYyA8LSBhbGxfZnJlcSRgMWAgLyAoYWxsX2ZyZXEkYDBgICsgYWxsX2ZyZXEkYDFgKQpsb3NzX3BlcmMgPC0gMSAtIHdpbl9wZXJjCmFsbF9wZXJjIDwtIGNiaW5kKGFsbF9mcmVxLCBsb3NzX3BlcmMsIHdpbl9wZXJjKQphbGxfcGVyYyA8LSBhbGxfcGVyY1szOjRdCmNvbG5hbWVzKGFsbF9wZXJjKSA8LSBjKCJMb3NzIFBlcmNlbnRhZ2UiLCAiV2luIFBlcmNlbnRhZ2UiKQphbGxfcGVyYwpgYGAKCgpgYGB7ciB0NywgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTh9Cm1vc2FpY3Bsb3QoYWxsX3BlcmMsIGNvbD1jKCJyZWQiLCJkYXJrZ3JlZW4iKSwgbWFpbiA9ICJXaW4gdnMgTG9zcyBQZXJjZW50YWdlIGZvciBlYWNoIHRlYW0gKDIwMTItMjAxNikiLCBjZXguYXhpcyA9IDEuMikKYGBgCgpgYGB7cn0KYWxsX3BlcmNfZGVjciA8LSBhbGxfcGVyY1tvcmRlcihhbGxfcGVyYyRgV2luIFBlcmNlbnRhZ2VgLCBkZWNyZWFzaW5nID0gVFJVRSksXQphbGxfcGVyY19kZWNyCmBgYAoKCgpgYGB7ciAwMDA0LCBmaWcud2lkdGg9MjUsIGZpZy5oZWlnaHQ9Nn0KbW9zYWljcGxvdChhbGxfcGVyY19kZWNyLCBjb2w9YyhyZ2IoMjE1LzI1NSwyNS8yNTUsMjgvMjU1KSxyZ2IoMTY2LzI1NSwyMTcvMjU1LDEwNi8yNTUpKSwgbWFpbiA9ICJUb3RhbCBXaW4gdnMgTG9zcyBQZXJjZW50YWdlIGZvciBlYWNoIHRlYW0gKDIwMTItMjAxNikgW0Rlc2NlbmRpbmcgT3JkZXJdIiwgY2V4LmF4aXMgPSAxLjIpCmBgYAoKYGBge3J9CmxpYnJhcnkobGF0dGljZSkKYmFyY2hhcnQoQUZMX2NsZWFuJFNjb3JlIH4gQUZMX2NsZWFuJFRlYW0gfCBBRkxfY2xlYW4kVmVudWUpCmBgYAoKYGBge3J9CnZlbnVlRmlsdGVyIDwtIGZ1bmN0aW9uKHZlbnVlKXsKICAKICBBRkxfdmVudWUgPC0gQUZMX2NsZWFuW0FGTF9jbGVhbiRWZW51ZSA9PSB2ZW51ZSxdCiAgdmVudWVfdGFibGUgPC0gdGFibGUoQUZMX3ZlbnVlJFRlYW0sIEFGTF92ZW51ZSRXaW5Mb3NzKQogIHZlbnVlX2RmIDwtIGFzLmRhdGEuZnJhbWUubWF0cml4KHZlbnVlX3RhYmxlKQogIHZlbnVlX2ZyZXEgPC0gdmVudWVfZGZbb3JkZXIodmVudWVfZGYkYDFgLCBkZWNyZWFzaW5nID0gVFJVRSksXQogIAogIHdpbl9wZXJjIDwtIHZlbnVlX2ZyZXEkYDFgIC8gKHZlbnVlX2ZyZXEkYDBgICsgdmVudWVfZnJlcSRgMWApCiAgbG9zc19wZXJjIDwtIDEgLSB3aW5fcGVyYwogIGFsbF9wZXJjIDwtIGNiaW5kKHZlbnVlX2ZyZXEsIGxvc3NfcGVyYywgd2luX3BlcmMpCiAgYWxsX3BlcmMgPC0gYWxsX3BlcmNbMzo0XQogIGNvbG5hbWVzKGFsbF9wZXJjKSA8LSBjKCJMb3NzIFBlcmNlbnRhZ2UiLCAiV2luIFBlcmNlbnRhZ2UiKQogICNhbGxfcGVyYwogIAogIHRlYW1fdG90YWwgPC0gKHZlbnVlX2ZyZXEkYDBgICsgdmVudWVfZnJlcSRgMWApCiAgZ2FtZV90b3RhbHMgPC0gc3VtKHRlYW1fdG90YWwpCiAgdGVhbV9wcm9wb3J0aW9uIDwtIHRlYW1fdG90YWwgLyBnYW1lX3RvdGFscwogIHZlbnVlX2ZyZXEgPC0gY2JpbmQodmVudWVfZnJlcSwgdGVhbV9wcm9wb3J0aW9uKQogICN2ZW51ZV9mcmVxCiAgCiAgYWxsX3ZlbnVlIDwtIGNiaW5kKHZlbnVlX2ZyZXEsIGFsbF9wZXJjKQogIGFsbF92ZW51ZSA8LSBhbGxfdmVudWVbb3JkZXIoYWxsX3ZlbnVlJGBXaW4gUGVyY2VudGFnZWAsIGRlY3JlYXNpbmcgPSBUUlVFKSxdCiAgcmV0dXJuKGFsbF92ZW51ZSkKCn0KYGBgCgpgYGB7cn0Kc3ViaWFjbyA8LSB2ZW51ZUZpbHRlcigiU3ViaWFjbyIpCnN5ZC5zaG93Z3JvdW5kIDwtIHZlbnVlRmlsdGVyKCJTeWRuZXkgU2hvd2dyb3VuZCIpCmdhYmJhIDwtIHZlbnVlRmlsdGVyKCJHYWJiYSIpCm1jZyA8LSB2ZW51ZUZpbHRlcigiTS5DLkcuIikKc2NnIDwtIHZlbnVlRmlsdGVyKCJTLkMuRy4iKQphZGwub3ZhbCA8LSB2ZW51ZUZpbHRlcigiQWRlbGFpZGUgT3ZhbCIpCmNhcnJhcmEgPC0gdmVudWVGaWx0ZXIoIkNhcnJhcmEiKQpkb2NrbGFuZHMgPC0gdmVudWVGaWx0ZXIoIkRvY2tsYW5kcyIpCmZ0YmwucGFyayA8LSB2ZW51ZUZpbHRlcigiRm9vdGJhbGwgUGFyayIpCmBgYAoKYGBge3J9CnN1bShnYWJiYVsxXSArIGdhYmJhWzJdKQpnYWJiYQpgYGAKCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKZ2FiYmEudHByb3AgPC0gZ2FiYmFbM10KYnAgPC0gZ2dwbG90KGdhYmJhLnRwcm9wLCBhZXMoeD0iIiwgeT10ZWFtX3Byb3BvcnRpb24sIGZpbGw9cm93Lm5hbWVzKGdhYmJhLnRwcm9wKSkgKSArIGdlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM5OTk5OTkiLCAiI0U2OUYwMCIsICIjNTZCNEU5IiwgIiM4ZmMwYTAiLCAiI2NjOWI1YiIsICIjNzE1MjliIiwgIiM3YmY2OGQiLCAiIzk2MjcwZiIsICIjMWU0NmJlIiwgIiNiNjBlZGMiLCAiIzY3OGRiZCIsICIjNGViMjBiIiwgIiM1ZjM3ZTUiLCAiIzAyMzM5YiIsICIjYTdhNGRmIiwgIiMyYjY4MWUiLCAiIzJjM2Y3YyIsICIjZDllZDhmIikpCmJwCgpwaWUgPC0gYnAgKyBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApCnBpZQpgYGAKCmBgYHtyIDAwMDMsIGZpZy53aWR0aD0yOCwgZmlnLmhlaWdodD02fQpnYWJiYS53cGVyYyA8LSBnYWJiYVs0OjVdCiNzdWJpYWNvLndwZXJjCgptb3NhaWNwbG90KGdhYmJhLndwZXJjLCBjb2w9YyhyZ2IoMjE1LzI1NSwyNS8yNTUsMjgvMjU1KSxyZ2IoMTY2LzI1NSwyMTcvMjU1LDEwNi8yNTUpKSwgbWFpbiA9ICJUb3RhbCBXaW4gdnMgTG9zcyBQZXJjZW50YWdlIGZvciBlYWNoIHRlYW0gYXQgU3ViaWFjbyBPdmFsICgyMDEyLTIwMTYpIFtEZXNjZW5kaW5nIE9yZGVyXSIsIGNleC5heGlzID0gMS4yKQpgYGAKCgoqKio=