40-40 Project

The Premise

Baseball is back and so is Ronald Acuna Jr! I’m an unabashed Braves fan, and am extremely excited to watch a health RAJ play this year. In 2019, he fell just shy of reaching the mythical 40-40 club with a season of 41 homeruns but only 37 steals. The following year was the Covid shortened season, and in 2021 tragedy struck. Ronald was on track to have an incredible season, but had an unfortunate ACL tear shorty before the All Star break.

Acuna was able to return for a portion of the 2022 season, but it was obvious that he was not himself. You could see a distrust of the knee when swinging and running. He reported that some days it felt great and others it did not, so he could never really know what he was getting on any given day. But this year, everything seems to be healed and ready to go. He’s got power, he’s got speed, he’s got the arm, and this year I think everything will be on display for a full season.

We’re over halfway through the season at this point and he’s making a real run at one of the most incredible seasons ever. So, I’ve built some charts and graphs to help me compare this season to the 4 previously recorded 40-40 seasons in terms of pace through number of team games played. So far, we’ve flown by 40 steals, but 40 home runs is probably going to come right down to the end. Whether Ronald makes it or not, this year has already been fantastic and incredibly fun to watch. But it sure would be nice to join the club…

The Data

Data for this project comes primarily from FanGraphs through the baseballr package, as well as through some manual updates from Baseball Reference. None of the players who previously achieved 40-40 played a full 162 game season, so all references to games played here is to player games played rather than through team games played. The difference should be minimal as the least number of games played for any of the successful 40-40 players was 158. Using the baseballr package will allow me to much more easily update the RAJ stats without having to manually copy tables from BR and convert them to a .csv file.

The Process

If we’re going to be making visualizations with data, we’re gonna need some packages to work with. Here’s what I’ve got loaded up for this project:

library(tidyverse)
library(ggplot2)
library(here)
library(ggrepel)
library(gridExtra)
library(cowplot)
library(ggpubr)
library(baseballr)

Next, we need to get the data into R so we can start working with it.

sbo <- read_csv(here("raw", "SBO.csv"))

raj2023 <- fg_batter_game_logs(18401, year = 2023) %>%
  select(PlayerName, Date, HR, SB, AB) %>%
  arrange(Date) %>%
  mutate(Game = row_number())

jc1988 <- fg_batter_game_logs(1001918, year = 1988) %>%
  select(PlayerName, Date, HR, SB, AB) %>%
  arrange(Date) %>%
  mutate(Game = row_number())

bb1996 <- fg_batter_game_logs(1109, year = 1996) %>%
  select(PlayerName, Date, HR, SB, AB) %>%
  arrange(Date) %>%
  mutate(Game = row_number())

arod1998 <- fg_batter_game_logs(1274, year = 1998) %>%
  select(PlayerName, Date, HR, SB, AB) %>%
  arrange(Date) %>%
  mutate(Game = row_number())

as2006 <- fg_batter_game_logs(847, year = 2006) %>%
  select(PlayerName, Date, HR, SB, AB) %>%
  arrange(Date) %>%
  mutate(Game = row_number())


forty_forty <- do.call("rbind", list(raj2023, jc1988, bb1996, arod1998, as2006)) %>%
  rename(Player = PlayerName)

Home Runs

The first thing I’d like to do is create some visuals for understanding HR pace. I’ll need to do some tidying of the data to get just what we need. I’ll also be creating a variable for cumulative home runs (CHR).

forty_hr <- forty_forty %>%
  select(Player, HR, Game) %>%
  group_by(Player) %>%
  mutate(CHR = cumsum(HR))

Now that we’ve got a home run focused dataset, let’s get to graphing. First, I’ll make a basic chart showing HR through games played for all players. I’ll save that to a variable so that we can continue to build on it.

hr <- ggplot(forty_hr, aes(x = Game , y = CHR, group = Player)) +
  geom_line(linewidth = 1, aes(color = Player)) 

hr <- hr + 
  geom_abline(intercept = 0.2469, slope = 0.2469)

In the first iteration of this chart I had endpoint labels and lines that were all the same color except for Acuna’s. I’ve gotten several helpful suggestions to remove the end labels since they’re a little cluttery and just have lines colored by player. This way you can track each player’s season a little more easily. But, I don’t love the default colors very much, so let’s work on that before we move on.

I’ve also added a reference line for the “perfect pace” through 162 games. That equates to somewhere around 1 HR every 4 games. This way you can see at a glance whether or not a player is above or below pace at any given moment.

hr <- hr +
  scale_color_manual(values=c('#005C5C','#14225A', '#FD5A1E', '#EFB21E', '#CE1141'))

I’ve now changed each player’s line color to align with a color from the team they were with in their 40-40 season. Hopefully this will make the chart easier to read, but also fun! Now I’ll add some general labels and remove the grid marks for a cleaner look.

#Add titles and axis labels
hr <- hr + 
  labs(title = "Home Runs") +
  xlab("Games Played") +
  ylab("Cumulative Home Runs") +
  theme(plot.title = element_text(hjust = 0.5))

#Modify theme elements
hr <- hr +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        legend.position = c(0.87, 0.25),
        legend.background = element_rect(fill = "white", color = "black"))

        
hr

In addition to looking at the cumulative home runs per team game, I’d also like to see the home run rates for everyone. This should allow me look towards the future and see how quickly RAJ will reach the milestone and compare that to the 4 years where I know the player achieved the feat. For this, I’d like to calculate At Bats per Home Run (AB/HR) to get a simple metric for seeing how often a player hits a homer. So let’s create that new dataset and variable.

ab_per_hr <- forty_forty %>%
  select(Player, HR, AB) %>%
  group_by(Player) %>%
  summarise(total_HR = sum(HR),
            total_AB = sum(AB)) %>%
  mutate(ABHR = round(total_AB/total_HR, 2))%>%
  arrange(desc(ABHR)) 

Now that we have some data, let’s chart it so we can see how the players stack up.

abhr <- ggplot(ab_per_hr, aes(x=ABHR, y=reorder(Player, -ABHR))) +
  geom_col(aes(fill=Player), show.legend = FALSE) 

abhr<- abhr +
  scale_fill_manual(values=c('#005C5C','#14225A', '#FD5A1E', '#EFB21E', '#CE1141'))
  
abhr <- abhr + 
  labs(title = "At Bats per Home Run") +
  xlab("AB/HR") +
  ylab("Player") +
  theme(plot.title = element_text(hjust = 0.5)) 

#Modify theme elements
abhr <- abhr +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

abhr

As of the beginning of August he is a little below the pace for a 40 HR season. He is hitting the ball a lot and hitting it hard. Now that the summer heat is upon us, I expect even more balls to get out.

Ronald v Ronald: HR

Before we move on from home runs, let’s briefly look at Acuna vs himself. How does this season compare to Acuna’s 2019 or 2021?

raj2019 <- fg_batter_game_logs(18401, year = 2019) %>%
  select(PlayerName, Date, HR, SB, AB) %>%
  arrange(Date) %>%
  mutate(Game = row_number())

raj2021 <- fg_batter_game_logs(18401, year = 2021) %>%
  select(PlayerName, Date, HR, SB, AB) %>%
  arrange(Date) %>%
  mutate(Game = row_number())

acuna_self <- do.call("rbind", list(raj2023, raj2019, raj2021)) %>%
  rename(Player = PlayerName) %>%
  mutate(Date = ymd(Date)) %>% 
  mutate_at(vars(Date), funs(year, month, day)) %>%
  rename(Year = year)
  
acuna_self$Year <- as.factor(acuna_self$Year)

forty_hr_self <- acuna_self %>%
  select(HR, Game, Year) %>%
  group_by(Year) %>%
  mutate(CHR = cumsum(HR))

hr_self <- ggplot(forty_hr_self, aes(x = Game , y = CHR, group = Year)) +
  geom_line(size = 1, (aes(color = Year))) +
  geom_abline(intercept = 0.2469, slope = 0.2469) +
  scale_color_manual(values=c('#CE1141','#13274F','#EAAA00')) 
  
hr_self <- hr_self + 
  labs(title = "Home Runs") +
  xlab("Games Played") +
  ylab("Cumulative Home Runs") +
  theme(plot.title = element_text(hjust = 0.5))

hr_self <- hr_self +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        legend.position = c(0.87, 0.25),
        legend.background = element_rect(fill = "white", color = "black"))

hr_self

ggsave(here("figs", "ronald_self_comp_hr_8.3.23.png"))

First off, man what a season he was having in 2021! Well above 40HR pace and no sign of slowing down. In 2019 he reached the mark but was below the pace for a significant portion of the season. In fact, the 2023 line looks strikingly similar to the 2019 one. This continues to give us hope of 40 HR this year!

Stolen Bases

For stolen bases, I’m going to do functionally the same thing as I did for homeruns just subbing in a different variable. So let’s get our dataset:

forty_sb <- forty_forty %>%
  select(Player, SB, Game) %>%
#  filter(Game < 5) %>%
  group_by(Player) %>%
  mutate(CSB = cumsum(SB),
         RAJ = case_when(Player == "Ronald Acuna Jr" ~ "Yes",
                         Player != "Ronald Acuna Jr" ~ "No"))

And now let’s make the graph for cumulative stolen bases:

#Create General plot
sb <- ggplot(forty_sb, aes(x = Game , y = CSB, group = Player)) +
  geom_line(size = 1, aes(color = Player)) 

sb <- sb +
  geom_abline(intercept = 0.2469, slope = 0.2469)

sb <- sb +
  scale_color_manual(values=c('#005C5C','#14225A', '#FD5A1E', '#EFB21E', '#CE1141'))

#Add titles and axis labels
sb <- sb + 
  labs(title = "Stolen Bases") +
  xlab("Games Played") +
  ylab("Cumulative Stolen Bases") +
  theme(plot.title = element_text(hjust = 0.5))

#Change the color scales
sb <- sb +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        legend.position = c(0.87, 0.25),
        legend.background = element_rect(fill = "white", color = "black"))

#Final Plot for SB
sb

With that out of the way, let’s look at the rate of stolen bases. Originally, I looked at plate appearances per stolen base, but that didn’t really feel like the best choice. After soliciting feedback from the wonderful folks at r/sabermetrics on Reddit, u/ASpring27 noted that looking at something like “stolen bases per time on base with the next base open” might be interesting. They then let me know that Baseball Reference already gives the number of stolen base opportunities (SBO) which would drastically help me figure out this rate stat! Many many thanks!!

So, let’s get to calculating!

sbo <- sbo %>%
  mutate(sb_rate = (SB/SBO)*100)

sbr <- ggplot(sbo, aes(x=sb_rate, y=reorder(Player, sb_rate))) +
  geom_col(aes(fill=Player), show.legend = FALSE) 

sbr<- sbr +
  scale_fill_manual(values=c('#005C5C','#14225A', '#FD5A1E', '#EFB21E', '#CE1141'))
  
sbr <- sbr + 
  labs(title = "Percent of Possible Bases Stolen") +
  xlab("SB Rate") +
  ylab("Player") +
  theme(plot.title = element_text(hjust = 0.5)) 

#Modify theme elements
sbr <- sbr +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank())

sbr

Ronald’s base stealing this year has been incredible. I know that we’ve got a pitch clock and slightly larger bases, but not everyone has 50+ steals. It seems like he has decided to really go for it and is running a lot. I don’t know if I want him to do it forever (lots of injury potential for what is really only a small gain) but it’s been fun to watch this year!

Ronald v Ronald: SB

Just like with the HR’s, let’s take a look at a Ronald vs Ronald comparison.

forty_sb_self <- acuna_self %>%
  select(SB, Game, Year) %>%
  group_by(Year) %>%
  mutate(CSB = cumsum(SB))

sb_self <- ggplot(forty_sb_self, aes(x = Game , y = CSB, group = Year)) +
  geom_line(size = 1, (aes(color = Year))) +
  geom_abline(intercept = 0.2469, slope = 0.2469) +
  scale_color_manual(values=c('#CE1141','#13274F','#EAAA00')) 
  
sb_self <- sb_self + 
  labs(title = "Stolen Bases") +
  xlab("Games Played") +
  ylab("Cumulative Stolen Bases") +
  theme(plot.title = element_text(hjust = 0.5))

sb_self <- sb_self +
  theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(),
        legend.position = c(0.87, 0.25),
        legend.background = element_rect(fill = "white", color = "black"))

sb_self

ggsave(here("figs", "ronald self_comp_sb_8.3.23.png"))

Putting It All Together

The last thing I’d like to do is put all these graphics together into one cohesive picture. This way I can update throughout the season and share a single image that has everything in it. So let’s arrange those charts to all hang together.

raj4040 <- grid.arrange(hr, abhr, sb, sbr,
             ncol = 2)
raj4040 <- annotate_figure(raj4040,
                top = text_grob("Ronald Acuna Jr 40/40 Watch",
                                face = "bold",
                                size = 25),
                bottom = text_grob("Data courtesy of Baseball Reference \n
                                  Current as of 8/3/2023"))
raj4040

ggsave(here("figs", "raj4040_8.3.23.png"), bg = "white")

Conclusion

And there you have it! Getting past the halfway point, it’s looking like this is actually possible. When I started this earlier in the year I wasn’t sure, but it’s been such an incredible first half. Really looking forward to the second half to see if Ronald can keep this up! If you’d like to check out the raw data or code, here’s a link to the GitHub repo with all of the files [(https://github.com/mwooten34/baseball_with_r)]. Thanks for checking it out!

Updates

Update 4/7/2023: After posting this over on r/sabermetrics I got a wonderful suggestion from u/ASpring27 about both the formatting of the charts and a better metric to use for thinking about stolen bases per opportunity. I updated the colors of the charts to align with team colors, and changed my stolen base rate metric from plate appearances per stolen base to stolen bases per stolen base opportunity. I think everything looks cleaner right now, and the stolen base metric makes more sense.

Update 4/7/2023: Added baseballr package to pull historical seasons and current season for Ronald Acuna Jr. through FanGraphs. Previously I had added games into the log to account for team games in which the player did not play so that everyone would have 162, but with the current setup the data will simply go by player games. I don’t believe this will make a big difference.

Update 5/1/2023: We’re 27 games in thus far and things have been interesting. Ronald is stealing right, left, and center which is promising for 40-40. He’s also making a ton of hard contact. Unfortunately, a lot of that hard contact is going into the ground. He’s going to need to get the ball in the air if there’s any hope of getting to 40 HR. It’s still really early in the season, but getting too far behind pace now might be tough to recover from.

Update 6/1/2023: Similar spot to the beginning of last month. RAJ had four homers in four games which was helpful, but he’s still quite a bit off the pace. Contact has been good, but still a lot of balls on the ground. Hopefully with the aire getting warmer, we’ll start to see more long fly outs become home runs!

Update 6/22/2023: Added reference lines in the line plots for both HR and SB. Provides easy way to see whether Acuna is above or below pace at any given time. This was thanks to several r/sabermetrics suggestions. Also added Ronald v Ronald plots comparing this year and his 2019/2021 seasons.

LS0tDQp0aXRsZTogIjQwLTQwIFByb2plY3QiDQphdXRob3I6ICJNYXJrIFdvb3RlbiINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246DQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdG9jX2RlcHRoOiA0DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQotLS0NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkNCmBgYA0KDQojIyBUaGUgUHJlbWlzZQ0KQmFzZWJhbGwgaXMgYmFjayBhbmQgc28gaXMgUm9uYWxkIEFjdW5hIEpyISBJJ20gYW4gdW5hYmFzaGVkIEJyYXZlcyBmYW4sIGFuZCBhbSBleHRyZW1lbHkgZXhjaXRlZCB0byB3YXRjaCBhIGhlYWx0aCBSQUogcGxheSB0aGlzIHllYXIuIEluIDIwMTksIGhlIGZlbGwganVzdCBzaHkgb2YgcmVhY2hpbmcgdGhlIG15dGhpY2FsIDQwLTQwIGNsdWIgd2l0aCBhIHNlYXNvbiBvZiA0MSBob21lcnVucyBidXQgb25seSAzNyBzdGVhbHMuIFRoZSBmb2xsb3dpbmcgeWVhciB3YXMgdGhlIENvdmlkIHNob3J0ZW5lZCBzZWFzb24sIGFuZCBpbiAyMDIxIHRyYWdlZHkgc3RydWNrLiBSb25hbGQgd2FzIG9uIHRyYWNrIHRvIGhhdmUgYW4gaW5jcmVkaWJsZSBzZWFzb24sIGJ1dCBoYWQgYW4gdW5mb3J0dW5hdGUgQUNMIHRlYXIgc2hvcnR5IGJlZm9yZSB0aGUgQWxsIFN0YXIgYnJlYWsuIA0KDQpBY3VuYSB3YXMgYWJsZSB0byByZXR1cm4gZm9yIGEgcG9ydGlvbiBvZiB0aGUgMjAyMiBzZWFzb24sIGJ1dCBpdCB3YXMgb2J2aW91cyB0aGF0IGhlIHdhcyBub3QgaGltc2VsZi4gWW91IGNvdWxkIHNlZSBhIGRpc3RydXN0IG9mIHRoZSBrbmVlIHdoZW4gc3dpbmdpbmcgYW5kIHJ1bm5pbmcuIEhlIHJlcG9ydGVkIHRoYXQgc29tZSBkYXlzIGl0IGZlbHQgZ3JlYXQgYW5kIG90aGVycyBpdCBkaWQgbm90LCBzbyBoZSBjb3VsZCBuZXZlciByZWFsbHkga25vdyB3aGF0IGhlIHdhcyBnZXR0aW5nIG9uIGFueSBnaXZlbiBkYXkuIEJ1dCB0aGlzIHllYXIsIGV2ZXJ5dGhpbmcgc2VlbXMgdG8gYmUgaGVhbGVkIGFuZCByZWFkeSB0byBnby4gSGUncyBnb3QgcG93ZXIsIGhlJ3MgZ290IHNwZWVkLCBoZSdzIGdvdCB0aGUgYXJtLCBhbmQgdGhpcyB5ZWFyIEkgdGhpbmsgZXZlcnl0aGluZyB3aWxsIGJlIG9uIGRpc3BsYXkgZm9yIGEgZnVsbCBzZWFzb24uIA0KDQpXZSdyZSBvdmVyIGhhbGZ3YXkgdGhyb3VnaCB0aGUgc2Vhc29uIGF0IHRoaXMgcG9pbnQgYW5kIGhlJ3MgbWFraW5nIGEgcmVhbCBydW4gYXQgb25lIG9mIHRoZSBtb3N0IGluY3JlZGlibGUgc2Vhc29ucyBldmVyLiBTbywgSSd2ZSBidWlsdCBzb21lIGNoYXJ0cyBhbmQgZ3JhcGhzIHRvIGhlbHAgbWUgY29tcGFyZSB0aGlzIHNlYXNvbiB0byB0aGUgNCBwcmV2aW91c2x5IHJlY29yZGVkIDQwLTQwIHNlYXNvbnMgaW4gdGVybXMgb2YgcGFjZSB0aHJvdWdoIG51bWJlciBvZiB0ZWFtIGdhbWVzIHBsYXllZC4gU28gZmFyLCB3ZSd2ZSBmbG93biBieSA0MCBzdGVhbHMsIGJ1dCA0MCBob21lIHJ1bnMgaXMgcHJvYmFibHkgZ29pbmcgdG8gY29tZSByaWdodCBkb3duIHRvIHRoZSBlbmQuIFdoZXRoZXIgUm9uYWxkIG1ha2VzIGl0IG9yIG5vdCwgdGhpcyB5ZWFyIGhhcyBhbHJlYWR5IGJlZW4gZmFudGFzdGljIGFuZCBpbmNyZWRpYmx5IGZ1biB0byB3YXRjaC4gQnV0IGl0IHN1cmUgd291bGQgYmUgbmljZSB0byBqb2luIHRoZSBjbHViLi4uIA0KDQojIyBUaGUgRGF0YQ0KRGF0YSBmb3IgdGhpcyBwcm9qZWN0IGNvbWVzIHByaW1hcmlseSBmcm9tIEZhbkdyYXBocyB0aHJvdWdoIHRoZSBiYXNlYmFsbHIgcGFja2FnZSwgYXMgd2VsbCBhcyB0aHJvdWdoIHNvbWUgbWFudWFsIHVwZGF0ZXMgZnJvbSBCYXNlYmFsbCBSZWZlcmVuY2UuIE5vbmUgb2YgdGhlIHBsYXllcnMgd2hvIHByZXZpb3VzbHkgYWNoaWV2ZWQgNDAtNDAgcGxheWVkIGEgZnVsbCAxNjIgZ2FtZSBzZWFzb24sIHNvIGFsbCByZWZlcmVuY2VzIHRvIGdhbWVzIHBsYXllZCBoZXJlIGlzIHRvIHBsYXllciBnYW1lcyBwbGF5ZWQgcmF0aGVyIHRoYW4gdGhyb3VnaCB0ZWFtIGdhbWVzIHBsYXllZC4gVGhlIGRpZmZlcmVuY2Ugc2hvdWxkIGJlIG1pbmltYWwgYXMgdGhlIGxlYXN0IG51bWJlciBvZiBnYW1lcyBwbGF5ZWQgZm9yIGFueSBvZiB0aGUgc3VjY2Vzc2Z1bCA0MC00MCBwbGF5ZXJzIHdhcyAxNTguIFVzaW5nIHRoZSBiYXNlYmFsbHIgcGFja2FnZSB3aWxsIGFsbG93IG1lIHRvIG11Y2ggbW9yZSBlYXNpbHkgdXBkYXRlIHRoZSBSQUogc3RhdHMgd2l0aG91dCBoYXZpbmcgdG8gbWFudWFsbHkgY29weSB0YWJsZXMgZnJvbSBCUiBhbmQgY29udmVydCB0aGVtIHRvIGEgLmNzdiBmaWxlLiANCg0KIyMgVGhlIFByb2Nlc3MNCklmIHdlJ3JlIGdvaW5nIHRvIGJlIG1ha2luZyB2aXN1YWxpemF0aW9ucyB3aXRoIGRhdGEsIHdlJ3JlIGdvbm5hIG5lZWQgc29tZSBwYWNrYWdlcyB0byB3b3JrIHdpdGguIEhlcmUncyB3aGF0IEkndmUgZ290IGxvYWRlZCB1cCBmb3IgdGhpcyBwcm9qZWN0OiANCg0KYGBge3IgTGlicmFyaWVzIFVzZWR9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkoZ2dyZXBlbCkNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KGJhc2ViYWxscikNCmBgYA0KDQpOZXh0LCB3ZSBuZWVkIHRvIGdldCB0aGUgZGF0YSBpbnRvIFIgc28gd2UgY2FuIHN0YXJ0IHdvcmtpbmcgd2l0aCBpdC4gDQoNCmBgYHtyIERhdGF9DQoNCnNibyA8LSByZWFkX2NzdihoZXJlKCJyYXciLCAiU0JPLmNzdiIpKQ0KDQpyYWoyMDIzIDwtIGZnX2JhdHRlcl9nYW1lX2xvZ3MoMTg0MDEsIHllYXIgPSAyMDIzKSAlPiUNCiAgc2VsZWN0KFBsYXllck5hbWUsIERhdGUsIEhSLCBTQiwgQUIpICU+JQ0KICBhcnJhbmdlKERhdGUpICU+JQ0KICBtdXRhdGUoR2FtZSA9IHJvd19udW1iZXIoKSkNCg0KamMxOTg4IDwtIGZnX2JhdHRlcl9nYW1lX2xvZ3MoMTAwMTkxOCwgeWVhciA9IDE5ODgpICU+JQ0KICBzZWxlY3QoUGxheWVyTmFtZSwgRGF0ZSwgSFIsIFNCLCBBQikgJT4lDQogIGFycmFuZ2UoRGF0ZSkgJT4lDQogIG11dGF0ZShHYW1lID0gcm93X251bWJlcigpKQ0KDQpiYjE5OTYgPC0gZmdfYmF0dGVyX2dhbWVfbG9ncygxMTA5LCB5ZWFyID0gMTk5NikgJT4lDQogIHNlbGVjdChQbGF5ZXJOYW1lLCBEYXRlLCBIUiwgU0IsIEFCKSAlPiUNCiAgYXJyYW5nZShEYXRlKSAlPiUNCiAgbXV0YXRlKEdhbWUgPSByb3dfbnVtYmVyKCkpDQoNCmFyb2QxOTk4IDwtIGZnX2JhdHRlcl9nYW1lX2xvZ3MoMTI3NCwgeWVhciA9IDE5OTgpICU+JQ0KICBzZWxlY3QoUGxheWVyTmFtZSwgRGF0ZSwgSFIsIFNCLCBBQikgJT4lDQogIGFycmFuZ2UoRGF0ZSkgJT4lDQogIG11dGF0ZShHYW1lID0gcm93X251bWJlcigpKQ0KDQphczIwMDYgPC0gZmdfYmF0dGVyX2dhbWVfbG9ncyg4NDcsIHllYXIgPSAyMDA2KSAlPiUNCiAgc2VsZWN0KFBsYXllck5hbWUsIERhdGUsIEhSLCBTQiwgQUIpICU+JQ0KICBhcnJhbmdlKERhdGUpICU+JQ0KICBtdXRhdGUoR2FtZSA9IHJvd19udW1iZXIoKSkNCg0KDQpmb3J0eV9mb3J0eSA8LSBkby5jYWxsKCJyYmluZCIsIGxpc3QocmFqMjAyMywgamMxOTg4LCBiYjE5OTYsIGFyb2QxOTk4LCBhczIwMDYpKSAlPiUNCiAgcmVuYW1lKFBsYXllciA9IFBsYXllck5hbWUpDQpgYGANCg0KIyBIb21lIFJ1bnMNClRoZSBmaXJzdCB0aGluZyBJJ2QgbGlrZSB0byBkbyBpcyBjcmVhdGUgc29tZSB2aXN1YWxzIGZvciB1bmRlcnN0YW5kaW5nIEhSIHBhY2UuIEknbGwgbmVlZCB0byBkbyBzb21lIHRpZHlpbmcgb2YgdGhlIGRhdGEgdG8gZ2V0IGp1c3Qgd2hhdCB3ZSBuZWVkLiBJJ2xsIGFsc28gYmUgY3JlYXRpbmcgYSB2YXJpYWJsZSBmb3IgY3VtdWxhdGl2ZSBob21lIHJ1bnMgKENIUikuDQoNCmBgYHtyIERhdGEgc3Vic2V0IGZvciBIUn0NCmZvcnR5X2hyIDwtIGZvcnR5X2ZvcnR5ICU+JQ0KICBzZWxlY3QoUGxheWVyLCBIUiwgR2FtZSkgJT4lDQogIGdyb3VwX2J5KFBsYXllcikgJT4lDQogIG11dGF0ZShDSFIgPSBjdW1zdW0oSFIpKQ0KYGBgDQogDQpOb3cgdGhhdCB3ZSd2ZSBnb3QgYSBob21lIHJ1biBmb2N1c2VkIGRhdGFzZXQsIGxldCdzIGdldCB0byBncmFwaGluZy4gRmlyc3QsIEknbGwgbWFrZSBhIGJhc2ljIGNoYXJ0IHNob3dpbmcgSFIgdGhyb3VnaCBnYW1lcyBwbGF5ZWQgZm9yIGFsbCBwbGF5ZXJzLiBJJ2xsIHNhdmUgdGhhdCB0byBhIHZhcmlhYmxlIHNvIHRoYXQgd2UgY2FuIGNvbnRpbnVlIHRvIGJ1aWxkIG9uIGl0LiANCg0KYGBge3IgQmFzaWMgSFIgUGxvdH0NCmhyIDwtIGdncGxvdChmb3J0eV9ociwgYWVzKHggPSBHYW1lICwgeSA9IENIUiwgZ3JvdXAgPSBQbGF5ZXIpKSArDQogIGdlb21fbGluZShsaW5ld2lkdGggPSAxLCBhZXMoY29sb3IgPSBQbGF5ZXIpKSANCg0KaHIgPC0gaHIgKyANCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMC4yNDY5LCBzbG9wZSA9IDAuMjQ2OSkNCmBgYA0KDQpJbiB0aGUgZmlyc3QgaXRlcmF0aW9uIG9mIHRoaXMgY2hhcnQgSSBoYWQgZW5kcG9pbnQgbGFiZWxzIGFuZCBsaW5lcyB0aGF0IHdlcmUgYWxsIHRoZSBzYW1lIGNvbG9yIGV4Y2VwdCBmb3IgQWN1bmEncy4gSSd2ZSBnb3R0ZW4gc2V2ZXJhbCBoZWxwZnVsIHN1Z2dlc3Rpb25zIHRvIHJlbW92ZSB0aGUgZW5kIGxhYmVscyBzaW5jZSB0aGV5J3JlIGEgbGl0dGxlIGNsdXR0ZXJ5IGFuZCBqdXN0IGhhdmUgbGluZXMgY29sb3JlZCBieSBwbGF5ZXIuIFRoaXMgd2F5IHlvdSBjYW4gdHJhY2sgZWFjaCBwbGF5ZXIncyBzZWFzb24gYSBsaXR0bGUgbW9yZSBlYXNpbHkuIEJ1dCwgSSBkb24ndCBsb3ZlIHRoZSBkZWZhdWx0IGNvbG9ycyB2ZXJ5IG11Y2gsIHNvIGxldCdzIHdvcmsgb24gdGhhdCBiZWZvcmUgd2UgbW92ZSBvbi4gDQoNCkkndmUgYWxzbyBhZGRlZCBhIHJlZmVyZW5jZSBsaW5lIGZvciB0aGUgInBlcmZlY3QgcGFjZSIgdGhyb3VnaCAxNjIgZ2FtZXMuIFRoYXQgZXF1YXRlcyB0byBzb21ld2hlcmUgYXJvdW5kIDEgSFIgZXZlcnkgNCBnYW1lcy4gVGhpcyB3YXkgeW91IGNhbiBzZWUgYXQgYSBnbGFuY2Ugd2hldGhlciBvciBub3QgYSBwbGF5ZXIgaXMgYWJvdmUgb3IgYmVsb3cgcGFjZSBhdCBhbnkgZ2l2ZW4gbW9tZW50LiANCg0KYGBge3IgTW9kaWZ5IExpbmUgQ29sb3JzfQ0KDQpociA8LSBociArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnIzAwNUM1QycsJyMxNDIyNUEnLCAnI0ZENUExRScsICcjRUZCMjFFJywgJyNDRTExNDEnKSkNCg0KYGBgDQoNCkkndmUgbm93IGNoYW5nZWQgZWFjaCBwbGF5ZXIncyBsaW5lIGNvbG9yIHRvIGFsaWduIHdpdGggYSBjb2xvciBmcm9tIHRoZSB0ZWFtIHRoZXkgd2VyZSB3aXRoIGluIHRoZWlyIDQwLTQwIHNlYXNvbi4gSG9wZWZ1bGx5IHRoaXMgd2lsbCBtYWtlIHRoZSBjaGFydCBlYXNpZXIgdG8gcmVhZCwgYnV0IGFsc28gZnVuISBOb3cgSSdsbCBhZGQgc29tZSBnZW5lcmFsIGxhYmVscyBhbmQgcmVtb3ZlIHRoZSBncmlkIG1hcmtzIGZvciBhIGNsZWFuZXIgbG9vay4gDQoNCmBgYHtyIEFkZCBUaXRsZXMgYW5kIExhYmVsc30NCg0KI0FkZCB0aXRsZXMgYW5kIGF4aXMgbGFiZWxzDQpociA8LSBociArIA0KICBsYWJzKHRpdGxlID0gIkhvbWUgUnVucyIpICsNCiAgeGxhYigiR2FtZXMgUGxheWVkIikgKw0KICB5bGFiKCJDdW11bGF0aXZlIEhvbWUgUnVucyIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCiNNb2RpZnkgdGhlbWUgZWxlbWVudHMNCmhyIDwtIGhyICsNCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjg3LCAwLjI1KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikpDQoNCiAgICAgICAgDQpocg0KYGBgDQoNCkluIGFkZGl0aW9uIHRvIGxvb2tpbmcgYXQgdGhlIGN1bXVsYXRpdmUgaG9tZSBydW5zIHBlciB0ZWFtIGdhbWUsIEknZCBhbHNvIGxpa2UgdG8gc2VlIHRoZSBob21lIHJ1biByYXRlcyBmb3IgZXZlcnlvbmUuIFRoaXMgc2hvdWxkIGFsbG93IG1lIGxvb2sgdG93YXJkcyB0aGUgZnV0dXJlIGFuZCBzZWUgaG93IHF1aWNrbHkgUkFKIHdpbGwgcmVhY2ggdGhlIG1pbGVzdG9uZSBhbmQgY29tcGFyZSB0aGF0IHRvIHRoZSA0IHllYXJzIHdoZXJlIEkga25vdyB0aGUgcGxheWVyIGFjaGlldmVkIHRoZSBmZWF0LiBGb3IgdGhpcywgSSdkIGxpa2UgdG8gY2FsY3VsYXRlIEF0IEJhdHMgcGVyIEhvbWUgUnVuIChBQi9IUikgdG8gZ2V0IGEgc2ltcGxlIG1ldHJpYyBmb3Igc2VlaW5nIGhvdyBvZnRlbiBhIHBsYXllciBoaXRzIGEgaG9tZXIuIFNvIGxldCdzIGNyZWF0ZSB0aGF0IG5ldyBkYXRhc2V0IGFuZCB2YXJpYWJsZS4gDQoNCmBgYHtyfQ0KYWJfcGVyX2hyIDwtIGZvcnR5X2ZvcnR5ICU+JQ0KICBzZWxlY3QoUGxheWVyLCBIUiwgQUIpICU+JQ0KICBncm91cF9ieShQbGF5ZXIpICU+JQ0KICBzdW1tYXJpc2UodG90YWxfSFIgPSBzdW0oSFIpLA0KICAgICAgICAgICAgdG90YWxfQUIgPSBzdW0oQUIpKSAlPiUNCiAgbXV0YXRlKEFCSFIgPSByb3VuZCh0b3RhbF9BQi90b3RhbF9IUiwgMikpJT4lDQogIGFycmFuZ2UoZGVzYyhBQkhSKSkgDQoNCmBgYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIHNvbWUgZGF0YSwgbGV0J3MgY2hhcnQgaXQgc28gd2UgY2FuIHNlZSBob3cgdGhlIHBsYXllcnMgc3RhY2sgdXAuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NX0NCmFiaHIgPC0gZ2dwbG90KGFiX3Blcl9ociwgYWVzKHg9QUJIUiwgeT1yZW9yZGVyKFBsYXllciwgLUFCSFIpKSkgKw0KICBnZW9tX2NvbChhZXMoZmlsbD1QbGF5ZXIpLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSANCg0KYWJocjwtIGFiaHIgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygnIzAwNUM1QycsJyMxNDIyNUEnLCAnI0ZENUExRScsICcjRUZCMjFFJywgJyNDRTExNDEnKSkNCiAgDQphYmhyIDwtIGFiaHIgKyANCiAgbGFicyh0aXRsZSA9ICJBdCBCYXRzIHBlciBIb21lIFJ1biIpICsNCiAgeGxhYigiQUIvSFIiKSArDQogIHlsYWIoIlBsYXllciIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpIA0KDQojTW9kaWZ5IHRoZW1lIGVsZW1lbnRzDQphYmhyIDwtIGFiaHIgKw0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQ0KDQphYmhyDQpgYGANCg0KQXMgb2YgdGhlIGJlZ2lubmluZyBvZiBBdWd1c3QgaGUgaXMgYSBsaXR0bGUgYmVsb3cgdGhlIHBhY2UgZm9yIGEgNDAgSFIgc2Vhc29uLiBIZSBpcyBoaXR0aW5nIHRoZSBiYWxsIGEgbG90IGFuZCBoaXR0aW5nIGl0IGhhcmQuIE5vdyB0aGF0IHRoZSBzdW1tZXIgaGVhdCBpcyB1cG9uIHVzLCBJIGV4cGVjdCBldmVuIG1vcmUgYmFsbHMgdG8gZ2V0IG91dC4gDQoNCiMgUm9uYWxkIHYgUm9uYWxkOiBIUg0KQmVmb3JlIHdlIG1vdmUgb24gZnJvbSBob21lIHJ1bnMsIGxldCdzIGJyaWVmbHkgbG9vayBhdCBBY3VuYSB2cyBoaW1zZWxmLiBIb3cgZG9lcyB0aGlzIHNlYXNvbiBjb21wYXJlIHRvIEFjdW5hJ3MgMjAxOSBvciAyMDIxPyANCg0KYGBge3J9DQoNCnJhajIwMTkgPC0gZmdfYmF0dGVyX2dhbWVfbG9ncygxODQwMSwgeWVhciA9IDIwMTkpICU+JQ0KICBzZWxlY3QoUGxheWVyTmFtZSwgRGF0ZSwgSFIsIFNCLCBBQikgJT4lDQogIGFycmFuZ2UoRGF0ZSkgJT4lDQogIG11dGF0ZShHYW1lID0gcm93X251bWJlcigpKQ0KDQpyYWoyMDIxIDwtIGZnX2JhdHRlcl9nYW1lX2xvZ3MoMTg0MDEsIHllYXIgPSAyMDIxKSAlPiUNCiAgc2VsZWN0KFBsYXllck5hbWUsIERhdGUsIEhSLCBTQiwgQUIpICU+JQ0KICBhcnJhbmdlKERhdGUpICU+JQ0KICBtdXRhdGUoR2FtZSA9IHJvd19udW1iZXIoKSkNCg0KYWN1bmFfc2VsZiA8LSBkby5jYWxsKCJyYmluZCIsIGxpc3QocmFqMjAyMywgcmFqMjAxOSwgcmFqMjAyMSkpICU+JQ0KICByZW5hbWUoUGxheWVyID0gUGxheWVyTmFtZSkgJT4lDQogIG11dGF0ZShEYXRlID0geW1kKERhdGUpKSAlPiUgDQogIG11dGF0ZV9hdCh2YXJzKERhdGUpLCBmdW5zKHllYXIsIG1vbnRoLCBkYXkpKSAlPiUNCiAgcmVuYW1lKFllYXIgPSB5ZWFyKQ0KICANCmFjdW5hX3NlbGYkWWVhciA8LSBhcy5mYWN0b3IoYWN1bmFfc2VsZiRZZWFyKQ0KDQpmb3J0eV9ocl9zZWxmIDwtIGFjdW5hX3NlbGYgJT4lDQogIHNlbGVjdChIUiwgR2FtZSwgWWVhcikgJT4lDQogIGdyb3VwX2J5KFllYXIpICU+JQ0KICBtdXRhdGUoQ0hSID0gY3Vtc3VtKEhSKSkNCg0KaHJfc2VsZiA8LSBnZ3Bsb3QoZm9ydHlfaHJfc2VsZiwgYWVzKHggPSBHYW1lICwgeSA9IENIUiwgZ3JvdXAgPSBZZWFyKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEsIChhZXMoY29sb3IgPSBZZWFyKSkpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMC4yNDY5LCBzbG9wZSA9IDAuMjQ2OSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJyNDRTExNDEnLCcjMTMyNzRGJywnI0VBQUEwMCcpKSANCiAgDQpocl9zZWxmIDwtIGhyX3NlbGYgKyANCiAgbGFicyh0aXRsZSA9ICJIb21lIFJ1bnMiKSArDQogIHhsYWIoIkdhbWVzIFBsYXllZCIpICsNCiAgeWxhYigiQ3VtdWxhdGl2ZSBIb21lIFJ1bnMiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQpocl9zZWxmIDwtIGhyX3NlbGYgKw0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuODcsIDAuMjUpLA0KICAgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSkNCg0KaHJfc2VsZg0KDQpnZ3NhdmUoaGVyZSgiZmlncyIsICJyb25hbGRfc2VsZl9jb21wX2hyXzguMy4yMy5wbmciKSkNCg0KYGBgDQoNCkZpcnN0IG9mZiwgbWFuIHdoYXQgYSBzZWFzb24gaGUgd2FzIGhhdmluZyBpbiAyMDIxISBXZWxsIGFib3ZlIDQwSFIgcGFjZSBhbmQgbm8gc2lnbiBvZiBzbG93aW5nIGRvd24uIEluIDIwMTkgaGUgcmVhY2hlZCB0aGUgbWFyayBidXQgd2FzIGJlbG93IHRoZSBwYWNlIGZvciBhIHNpZ25pZmljYW50IHBvcnRpb24gb2YgdGhlIHNlYXNvbi4gSW4gZmFjdCwgdGhlIDIwMjMgbGluZSBsb29rcyBzdHJpa2luZ2x5IHNpbWlsYXIgdG8gdGhlIDIwMTkgb25lLiBUaGlzIGNvbnRpbnVlcyB0byBnaXZlIHVzIGhvcGUgb2YgNDAgSFIgdGhpcyB5ZWFyIQ0KDQoNCiMgU3RvbGVuIEJhc2VzDQpGb3Igc3RvbGVuIGJhc2VzLCBJJ20gZ29pbmcgdG8gZG8gZnVuY3Rpb25hbGx5IHRoZSBzYW1lIHRoaW5nIGFzIEkgZGlkIGZvciBob21lcnVucyBqdXN0IHN1YmJpbmcgaW4gYSBkaWZmZXJlbnQgdmFyaWFibGUuIFNvIGxldCdzIGdldCBvdXIgZGF0YXNldDogDQoNCmBgYHtyfQ0KZm9ydHlfc2IgPC0gZm9ydHlfZm9ydHkgJT4lDQogIHNlbGVjdChQbGF5ZXIsIFNCLCBHYW1lKSAlPiUNCiMgIGZpbHRlcihHYW1lIDwgNSkgJT4lDQogIGdyb3VwX2J5KFBsYXllcikgJT4lDQogIG11dGF0ZShDU0IgPSBjdW1zdW0oU0IpLA0KICAgICAgICAgUkFKID0gY2FzZV93aGVuKFBsYXllciA9PSAiUm9uYWxkIEFjdW5hIEpyIiB+ICJZZXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIFBsYXllciAhPSAiUm9uYWxkIEFjdW5hIEpyIiB+ICJObyIpKQ0KYGBgDQoNCkFuZCBub3cgbGV0J3MgbWFrZSB0aGUgZ3JhcGggZm9yIGN1bXVsYXRpdmUgc3RvbGVuIGJhc2VzOg0KDQpgYGB7cn0NCiNDcmVhdGUgR2VuZXJhbCBwbG90DQpzYiA8LSBnZ3Bsb3QoZm9ydHlfc2IsIGFlcyh4ID0gR2FtZSAsIHkgPSBDU0IsIGdyb3VwID0gUGxheWVyKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEsIGFlcyhjb2xvciA9IFBsYXllcikpIA0KDQpzYiA8LSBzYiArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAuMjQ2OSwgc2xvcGUgPSAwLjI0NjkpDQoNCnNiIDwtIHNiICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCcjMDA1QzVDJywnIzE0MjI1QScsICcjRkQ1QTFFJywgJyNFRkIyMUUnLCAnI0NFMTE0MScpKQ0KDQojQWRkIHRpdGxlcyBhbmQgYXhpcyBsYWJlbHMNCnNiIDwtIHNiICsgDQogIGxhYnModGl0bGUgPSAiU3RvbGVuIEJhc2VzIikgKw0KICB4bGFiKCJHYW1lcyBQbGF5ZWQiKSArDQogIHlsYWIoIkN1bXVsYXRpdmUgU3RvbGVuIEJhc2VzIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0KI0NoYW5nZSB0aGUgY29sb3Igc2NhbGVzDQpzYiA8LSBzYiArDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44NywgMC4yNSksDQogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpKQ0KDQojRmluYWwgUGxvdCBmb3IgU0INCnNiDQpgYGANCg0KV2l0aCB0aGF0IG91dCBvZiB0aGUgd2F5LCBsZXQncyBsb29rIGF0IHRoZSByYXRlIG9mIHN0b2xlbiBiYXNlcy4gT3JpZ2luYWxseSwgSSBsb29rZWQgYXQgcGxhdGUgYXBwZWFyYW5jZXMgcGVyIHN0b2xlbiBiYXNlLCBidXQgdGhhdCBkaWRuJ3QgcmVhbGx5IGZlZWwgbGlrZSB0aGUgYmVzdCBjaG9pY2UuIEFmdGVyIHNvbGljaXRpbmcgZmVlZGJhY2sgZnJvbSB0aGUgd29uZGVyZnVsIGZvbGtzIGF0IHIvc2FiZXJtZXRyaWNzIG9uIFJlZGRpdCwgdS9BU3ByaW5nMjcgbm90ZWQgdGhhdCBsb29raW5nIGF0IHNvbWV0aGluZyBsaWtlICJzdG9sZW4gYmFzZXMgcGVyIHRpbWUgb24gYmFzZSB3aXRoIHRoZSBuZXh0IGJhc2Ugb3BlbiIgbWlnaHQgYmUgaW50ZXJlc3RpbmcuIFRoZXkgdGhlbiBsZXQgbWUga25vdyB0aGF0IEJhc2ViYWxsIFJlZmVyZW5jZSBhbHJlYWR5IGdpdmVzIHRoZSBudW1iZXIgb2Ygc3RvbGVuIGJhc2Ugb3Bwb3J0dW5pdGllcyAoU0JPKSB3aGljaCB3b3VsZCBkcmFzdGljYWxseSBoZWxwIG1lIGZpZ3VyZSBvdXQgdGhpcyByYXRlIHN0YXQhIE1hbnkgbWFueSB0aGFua3MhISANCg0KU28sIGxldCdzIGdldCB0byBjYWxjdWxhdGluZyENCg0KYGBge3IgU3RvbGVuIEJhc2VzIHBlciBTQk99DQpzYm8gPC0gc2JvICU+JQ0KICBtdXRhdGUoc2JfcmF0ZSA9IChTQi9TQk8pKjEwMCkNCg0Kc2JyIDwtIGdncGxvdChzYm8sIGFlcyh4PXNiX3JhdGUsIHk9cmVvcmRlcihQbGF5ZXIsIHNiX3JhdGUpKSkgKw0KICBnZW9tX2NvbChhZXMoZmlsbD1QbGF5ZXIpLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSANCg0Kc2JyPC0gc2JyICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoJyMwMDVDNUMnLCcjMTQyMjVBJywgJyNGRDVBMUUnLCAnI0VGQjIxRScsICcjQ0UxMTQxJykpDQogIA0Kc2JyIDwtIHNiciArIA0KICBsYWJzKHRpdGxlID0gIlBlcmNlbnQgb2YgUG9zc2libGUgQmFzZXMgU3RvbGVuIikgKw0KICB4bGFiKCJTQiBSYXRlIikgKw0KICB5bGFiKCJQbGF5ZXIiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSANCg0KI01vZGlmeSB0aGVtZSBlbGVtZW50cw0Kc2JyIDwtIHNiciArDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpDQoNCnNicg0KDQoNCmBgYA0KDQpSb25hbGQncyBiYXNlIHN0ZWFsaW5nIHRoaXMgeWVhciBoYXMgYmVlbiBpbmNyZWRpYmxlLiBJIGtub3cgdGhhdCB3ZSd2ZSBnb3QgYSBwaXRjaCBjbG9jayBhbmQgc2xpZ2h0bHkgbGFyZ2VyIGJhc2VzLCBidXQgbm90IGV2ZXJ5b25lIGhhcyA1MCsgc3RlYWxzLiBJdCBzZWVtcyBsaWtlIGhlIGhhcyBkZWNpZGVkIHRvIHJlYWxseSBnbyBmb3IgaXQgYW5kIGlzIHJ1bm5pbmcgYSBsb3QuIEkgZG9uJ3Qga25vdyBpZiBJIHdhbnQgaGltIHRvIGRvIGl0IGZvcmV2ZXIgKGxvdHMgb2YgaW5qdXJ5IHBvdGVudGlhbCBmb3Igd2hhdCBpcyByZWFsbHkgb25seSBhIHNtYWxsIGdhaW4pIGJ1dCBpdCdzIGJlZW4gZnVuIHRvIHdhdGNoIHRoaXMgeWVhciENCg0KIyBSb25hbGQgdiBSb25hbGQ6IFNCDQpKdXN0IGxpa2Ugd2l0aCB0aGUgSFIncywgbGV0J3MgdGFrZSBhIGxvb2sgYXQgYSBSb25hbGQgdnMgUm9uYWxkIGNvbXBhcmlzb24uIA0KDQpgYGB7cn0NCg0KZm9ydHlfc2Jfc2VsZiA8LSBhY3VuYV9zZWxmICU+JQ0KICBzZWxlY3QoU0IsIEdhbWUsIFllYXIpICU+JQ0KICBncm91cF9ieShZZWFyKSAlPiUNCiAgbXV0YXRlKENTQiA9IGN1bXN1bShTQikpDQoNCnNiX3NlbGYgPC0gZ2dwbG90KGZvcnR5X3NiX3NlbGYsIGFlcyh4ID0gR2FtZSAsIHkgPSBDU0IsIGdyb3VwID0gWWVhcikpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLCAoYWVzKGNvbG9yID0gWWVhcikpKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAuMjQ2OSwgc2xvcGUgPSAwLjI0NjkpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCcjQ0UxMTQxJywnIzEzMjc0RicsJyNFQUFBMDAnKSkgDQogIA0Kc2Jfc2VsZiA8LSBzYl9zZWxmICsgDQogIGxhYnModGl0bGUgPSAiU3RvbGVuIEJhc2VzIikgKw0KICB4bGFiKCJHYW1lcyBQbGF5ZWQiKSArDQogIHlsYWIoIkN1bXVsYXRpdmUgU3RvbGVuIEJhc2VzIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkNCg0Kc2Jfc2VsZiA8LSBzYl9zZWxmICsNCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjg3LCAwLjI1KSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikpDQoNCnNiX3NlbGYNCg0KZ2dzYXZlKGhlcmUoImZpZ3MiLCAicm9uYWxkIHNlbGZfY29tcF9zYl84LjMuMjMucG5nIikpDQoNCmBgYA0KDQoNCiMgUHV0dGluZyBJdCBBbGwgVG9nZXRoZXINClRoZSBsYXN0IHRoaW5nIEknZCBsaWtlIHRvIGRvIGlzIHB1dCBhbGwgdGhlc2UgZ3JhcGhpY3MgdG9nZXRoZXIgaW50byBvbmUgY29oZXNpdmUgcGljdHVyZS4gVGhpcyB3YXkgSSBjYW4gdXBkYXRlIHRocm91Z2hvdXQgdGhlIHNlYXNvbiBhbmQgc2hhcmUgYSBzaW5nbGUgaW1hZ2UgdGhhdCBoYXMgZXZlcnl0aGluZyBpbiBpdC4gU28gbGV0J3MgYXJyYW5nZSB0aG9zZSBjaGFydHMgdG8gYWxsIGhhbmcgdG9nZXRoZXIuIA0KDQpgYGB7ciwgZmlnLndpZHRoPSAyMCwgZmlnLmhlaWdodD0gMTAsIGZpZy5zaG93PSAnaGlkZSd9DQoNCnJhajQwNDAgPC0gZ3JpZC5hcnJhbmdlKGhyLCBhYmhyLCBzYiwgc2JyLA0KICAgICAgICAgICAgIG5jb2wgPSAyKQ0KDQpyYWo0MDQwIDwtIGFubm90YXRlX2ZpZ3VyZShyYWo0MDQwLA0KICAgICAgICAgICAgICAgIHRvcCA9IHRleHRfZ3JvYigiUm9uYWxkIEFjdW5hIEpyIDQwLzQwIFdhdGNoIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJib2xkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDI1KSwNCiAgICAgICAgICAgICAgICBib3R0b20gPSB0ZXh0X2dyb2IoIkRhdGEgY291cnRlc3kgb2YgQmFzZWJhbGwgUmVmZXJlbmNlIFxuDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ3VycmVudCBhcyBvZiA4LzMvMjAyMyIpKQ0KDQoNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPSAyMCwgZmlnLmhlaWdodD0gMTB9DQoNCnJhajQwNDANCg0KZ2dzYXZlKGhlcmUoImZpZ3MiLCAicmFqNDA0MF84LjMuMjMucG5nIiksIGJnID0gIndoaXRlIikNCmBgYA0KDQojIyBDb25jbHVzaW9uDQpBbmQgdGhlcmUgeW91IGhhdmUgaXQhIEdldHRpbmcgcGFzdCB0aGUgaGFsZndheSBwb2ludCwgaXQncyBsb29raW5nIGxpa2UgdGhpcyBpcyBhY3R1YWxseSBwb3NzaWJsZS4gV2hlbiBJIHN0YXJ0ZWQgdGhpcyBlYXJsaWVyIGluIHRoZSB5ZWFyIEkgd2Fzbid0IHN1cmUsIGJ1dCBpdCdzIGJlZW4gc3VjaCBhbiBpbmNyZWRpYmxlIGZpcnN0IGhhbGYuIFJlYWxseSBsb29raW5nIGZvcndhcmQgdG8gdGhlIHNlY29uZCBoYWxmIHRvIHNlZSBpZiBSb25hbGQgY2FuIGtlZXAgdGhpcyB1cCEgSWYgeW91J2QgbGlrZSB0byBjaGVjayBvdXQgdGhlIHJhdyBkYXRhIG9yIGNvZGUsIGhlcmUncyBhIGxpbmsgdG8gdGhlIEdpdEh1YiByZXBvIHdpdGggYWxsIG9mIHRoZSBmaWxlcyBbKGh0dHBzOi8vZ2l0aHViLmNvbS9td29vdGVuMzQvYmFzZWJhbGxfd2l0aF9yKV0uIFRoYW5rcyBmb3IgY2hlY2tpbmcgaXQgb3V0ISANCg0KIyBVcGRhdGVzDQoNClVwZGF0ZSA0LzcvMjAyMzogQWZ0ZXIgcG9zdGluZyB0aGlzIG92ZXIgb24gci9zYWJlcm1ldHJpY3MgSSBnb3QgYSB3b25kZXJmdWwgc3VnZ2VzdGlvbiBmcm9tIHUvQVNwcmluZzI3IGFib3V0IGJvdGggdGhlIGZvcm1hdHRpbmcgb2YgdGhlIGNoYXJ0cyBhbmQgYSBiZXR0ZXIgbWV0cmljIHRvIHVzZSBmb3IgdGhpbmtpbmcgYWJvdXQgc3RvbGVuIGJhc2VzIHBlciBvcHBvcnR1bml0eS4gSSB1cGRhdGVkIHRoZSBjb2xvcnMgb2YgdGhlIGNoYXJ0cyB0byBhbGlnbiB3aXRoIHRlYW0gY29sb3JzLCBhbmQgY2hhbmdlZCBteSBzdG9sZW4gYmFzZSByYXRlIG1ldHJpYyBmcm9tIHBsYXRlIGFwcGVhcmFuY2VzIHBlciBzdG9sZW4gYmFzZSB0byBzdG9sZW4gYmFzZXMgcGVyIHN0b2xlbiBiYXNlIG9wcG9ydHVuaXR5LiBJIHRoaW5rIGV2ZXJ5dGhpbmcgbG9va3MgY2xlYW5lciByaWdodCBub3csIGFuZCB0aGUgc3RvbGVuIGJhc2UgbWV0cmljIG1ha2VzIG1vcmUgc2Vuc2UuIA0KDQpVcGRhdGUgNC83LzIwMjM6IEFkZGVkIGJhc2ViYWxsciBwYWNrYWdlIHRvIHB1bGwgaGlzdG9yaWNhbCBzZWFzb25zIGFuZCBjdXJyZW50IHNlYXNvbiBmb3IgUm9uYWxkIEFjdW5hIEpyLiB0aHJvdWdoIEZhbkdyYXBocy4gUHJldmlvdXNseSBJIGhhZCBhZGRlZCBnYW1lcyBpbnRvIHRoZSBsb2cgdG8gYWNjb3VudCBmb3IgdGVhbSBnYW1lcyBpbiB3aGljaCB0aGUgcGxheWVyIGRpZCBub3QgcGxheSBzbyB0aGF0IGV2ZXJ5b25lIHdvdWxkIGhhdmUgMTYyLCBidXQgd2l0aCB0aGUgY3VycmVudCBzZXR1cCB0aGUgZGF0YSB3aWxsIHNpbXBseSBnbyBieSBwbGF5ZXIgZ2FtZXMuIEkgZG9uJ3QgYmVsaWV2ZSB0aGlzIHdpbGwgbWFrZSBhIGJpZyBkaWZmZXJlbmNlLiANCg0KVXBkYXRlIDUvMS8yMDIzOiBXZSdyZSAyNyBnYW1lcyBpbiB0aHVzIGZhciBhbmQgdGhpbmdzIGhhdmUgYmVlbiBpbnRlcmVzdGluZy4gUm9uYWxkIGlzIHN0ZWFsaW5nIHJpZ2h0LCBsZWZ0LCBhbmQgY2VudGVyIHdoaWNoIGlzIHByb21pc2luZyBmb3IgNDAtNDAuIEhlJ3MgYWxzbyBtYWtpbmcgYSB0b24gb2YgaGFyZCBjb250YWN0LiBVbmZvcnR1bmF0ZWx5LCBhIGxvdCBvZiB0aGF0IGhhcmQgY29udGFjdCBpcyBnb2luZyBpbnRvIHRoZSBncm91bmQuIEhlJ3MgZ29pbmcgdG8gbmVlZCB0byBnZXQgdGhlIGJhbGwgaW4gdGhlIGFpciBpZiB0aGVyZSdzIGFueSBob3BlIG9mIGdldHRpbmcgdG8gNDAgSFIuIEl0J3Mgc3RpbGwgcmVhbGx5IGVhcmx5IGluIHRoZSBzZWFzb24sIGJ1dCBnZXR0aW5nIHRvbyBmYXIgYmVoaW5kIHBhY2Ugbm93IG1pZ2h0IGJlIHRvdWdoIHRvIHJlY292ZXIgZnJvbS4gDQoNClVwZGF0ZSA2LzEvMjAyMzogU2ltaWxhciBzcG90IHRvIHRoZSBiZWdpbm5pbmcgb2YgbGFzdCBtb250aC4gUkFKIGhhZCBmb3VyIGhvbWVycyBpbiBmb3VyIGdhbWVzIHdoaWNoIHdhcyBoZWxwZnVsLCBidXQgaGUncyBzdGlsbCBxdWl0ZSBhIGJpdCBvZmYgdGhlIHBhY2UuIENvbnRhY3QgaGFzIGJlZW4gZ29vZCwgYnV0IHN0aWxsIGEgbG90IG9mIGJhbGxzIG9uIHRoZSBncm91bmQuIEhvcGVmdWxseSB3aXRoIHRoZSBhaXJlIGdldHRpbmcgd2FybWVyLCB3ZSdsbCBzdGFydCB0byBzZWUgbW9yZSBsb25nIGZseSBvdXRzIGJlY29tZSBob21lIHJ1bnMhIA0KDQpVcGRhdGUgNi8yMi8yMDIzOiBBZGRlZCByZWZlcmVuY2UgbGluZXMgaW4gdGhlIGxpbmUgcGxvdHMgZm9yIGJvdGggSFIgYW5kIFNCLiBQcm92aWRlcyBlYXN5IHdheSB0byBzZWUgd2hldGhlciBBY3VuYSBpcyBhYm92ZSBvciBiZWxvdyBwYWNlIGF0IGFueSBnaXZlbiB0aW1lLiBUaGlzIHdhcyB0aGFua3MgdG8gc2V2ZXJhbCByL3NhYmVybWV0cmljcyBzdWdnZXN0aW9ucy4gQWxzbyBhZGRlZCBSb25hbGQgdiBSb25hbGQgcGxvdHMgY29tcGFyaW5nIHRoaXMgeWVhciBhbmQgaGlzIDIwMTkvMjAyMSBzZWFzb25zLiANCg==