Faceoffs in hockey aren’t particularly something that I’ve thought much about because I’ve never had any good ideas on how to quantify them. The first piece of research that I read regarding faceoffs in women’s hockey was this Big Data Cup project from Shayna Goldman, Mike Murphy, and Alyssa Longmuir. They discovered that winning a faceoff in the offensive zone frequently contributes to a shot attempt, making faceoffs worth more in the women’s game than the men’s.

The second time that I started thinking about faceoffs was (once again) thanks to Mike when he tweeted about Jillian Dempsey’s stellar 23-6 faceoff record against the Minnesota Whitecaps on November 7th, a 1-0 win for the Boston Pride.

With the recent development of the fastRhockey package to pull play-by-play data from the Premier Hockey Federation (PHF), I figured that exploring faceoffs through the lense of Jillian Dempsey’s skill would be an interesting test of the package.

The first place I started was with Raw Win-Loss records in the PHF; 23-6 seemed like a really good record, so I started by comparing Dempsey’s unique faceoff W/L combos against the unique records in the PHF. Turns out that Dempsey has been dominant relative to the league.

Above is a representation of every unique faceoff W-L record that has been recorded in the PHF (that we have faceoff play-by-play data for) where Dempsey’s games are in gold and the leagues are in grey.

In almost every single game that Dempsey has played, she’s won more faceoffs than she’s lost. Not only that, she’s done it with an efficiency that no one else in the league has matched. Her 23 faceoff wins tied the PHF record (Kendall Cornine on January 11th, 2020), but Dempsey did it in just 29 faceoff opportunities, where Cornine took 40 opportunities to accumulate 23 wins.

Dempsey holds a 22-5 game, numerous games with 15+ wins, and an 18-2 game. PHF players, as a whole, have occasionally approached those numbers, but that’s comparing Dempsey alone to the entire league. This graphic alone showcases that Jillian Dempsey is in a league of her own when it comes to winning faceoffs in the PHF.


ELO Ratings

After visualizing Dempsey’s brilliance, I wanted to figure out a way to try and evaluate player skill on faceoffs beyond using raw W-L records.

To me, the hardest part of measuring this sort of skill is accounting for the strength of the opponent. While W-L% is helpful (because winning faceoffs is helpful), there’s clearly a difference between winning one against Dempsey or winning one against the worst faceoff player in the league.

Thinking through the problem this way led me to using Elo Ratings to measure faceoff skill since a faceoff is a zero-sum game where one player wins and the other loses.

I was first aware of Elo Ratings in the context of rating chess players or serving as team-level rating. Turns out, it’s alsao been used to rate NHL skaters on faceoffs by multiple people (Tyrel Stokes, HockeyEloRatings, and more). (You can find a good Elo primer here). However, as with many things, no one has developed skater-level Elo ratings for women’s hockey on faceoffs. So, I did!

With Elo Ratings, everyone starts from the same point which, in this instance, is 1500. So, every time a PHF player takes their first faceoff, they begin as average, with an Elo score of 1500.

As you win (or lose) faceoffs against other players, your Elo rating begins to change. The idea is that if a player with a high Elo beats a player with a low Elo, neither score changes much, because that’s expected! However, if the reverse happens and an underdog beats a high-level player, there will be a much more drastic change in their post-faceoff Elo ratings.

The other key point is that the changes in Elo will always equal 0; so when player 1 wins +10 points, player 2 loses -10 points to their Elo, and vice versa.

The formula to calculate Elo is below:

\[ ExpWinPct_{Player} = Elo_{StartPlayer} / (Elo_{P1} + Elo_{P2}) \] \[ Elo_{winner} = Elo_{start} + K * (1 - ExpWinPct_{P1}) \] \[ Elo_{loser} = Elo_{start} + K * (0 - ExpWinPct_{P2}) \]

One of the fun components of calculating Elo is that is has a built in Expected Winning% to it, which is easy to calculate by taking the Elo of an individual player and comparing it to the total Elo of the players involved in the faceoff.

So, if Player A enters a faceoff with a 1800 Elo and Player B enters at 1200, we’d say that Player A has a 60% Expected Winning% (1800 / 3000), while Player B is at 40%. This results in the system that rewards upsets and only assigns a few points for winning faceoffs that you’re supposed to.

The magnitude of the change in Elo is determined by the K parameter. For PHF faceoff Elo, I chose to set this at 25. Why? Because I wanted to. High K values result in more volatility after an event, while lower K values make it harder to dig yourself out of a hole. There is definitely more that I could do to tune this K, but I felt that the gain from adjusting my K would be marginal at best.

It’s a simple system and equation, but one that took my a little while to figure out how to code. (I’m about to dive into the code that I wrote to produce the Elo ratings, so if you don’t want to listen to me ramble about it, you can skip to the results section. Though, I realize as I write this that I’ve been rambling about how Elo and the equations work so, if you’ve made it this far without skipping, you’re probably not skipping the code talk. Anyways, time to get back on topic).

The first step was to take the raw PHF play-by-play data and turn it into a representation of the two players involved in a faceoff and who won. In addition, since the order of who you play does have ramifications at the Elo-level, it was important to keep track of the order in which each faceoff occured.

Since I’ve saved all pre-2021-2022 PHF data into it’s own separate GitHub repository from the fastRhockey package, I loaded the old data from there and used the scraper for the new data.

I re-shaped the play-by-play data to produce a few important columns. First, I combined both the winning and losing player into one column with the winner/loser ID in a second column. In addition, I added a faceoff_id column to provide a sequential ID for all faceoffs, and added faceoff_played, which is how many faceoffs that player has played by that point.

## # A tibble: 6 x 5
##   game_id faceoff_id result player             faceoff_played
##     <int>      <int> <chr>  <chr>                       <int>
## 1  268078          1 winner Sara Bustad                     1
## 2  268078          1 loser  Hanna Beattie                   1
## 3  268078          2 winner Becki Bowering                  1
## 4  268078          2 loser  Hanna Beattie                   2
## 5  268078          3 winner Becki Bowering                  2
## 6  268078          3 loser  Sarah Schwenzfeier              1

From there, I opted to create a for loop to run through all the faceoffs in the pbp data to derive Elo Ratings. There may be a more efficient way to do this, but for me, this worked well.

#### Looping through all the recorded faceoffs in the current season
for (i in 1:max(data$faceoff_id, na.rm = TRUE)) {
  # selecting the next faceoff in the sequence
  # this will return a dataframe that is two rows long with the above columns
  fo <- data %>%
    dplyr::filter(faceoff_id == i)
  # once the dataframe has been selected, it splits into an if statement
  # the statement evaluates whether or not this is the "debut" faceoff for both players
  # if this is the first faceoff that each player has partcipated in, it's easy
  # 1500 is listed as the default value for each and does the calculation
  if (max(fo$faceoff_id, na.rm = TRUE) == 1) {
    # defaulting to 1500 as the starting elo when neither player has played a faceoff 
    fo <- fo %>%
      dplyr::mutate(prev_elo = ifelse(faceoff_played == 1, 1500, NA))
    
    tot <- fo %>%
      group_by(faceoff_id) %>%
      summarise(total_elo = sum(prev_elo))
    
    fo1 <- fo %>%
      left_join(tot, by = c("faceoff_id")) %>%
      # uses a function that I created to calcuate the Elo Ratings using my set-up
      elo() %>%
      ungroup()
    # these values will get added into a list to provide starting values for a player's 
    # second and beyond faceoffs
  } else if (max(fo$faceoff_id, na.rm = TRUE) != 1) {
    # pull all the previous results for the instances where at 
    # least 1 player has had a previous faceoff
    # since they've had 1+ faceoff, they'll have an Elo rating
    prev_res <- dplyr::bind_rows(lst)
    
    fo1 <- fo %>%
      # add the starting Elo value to the player it belongs to
      left_join(prev_res %>%
                  ungroup() %>%
                  group_by(player) %>%
                  # only selecting the values for players in the current faceoff
                  dplyr::filter(player %in% fo$player) %>%
                  # only taking the most recent post-Elo rating for the players
                  # because there's no need to take an un-updated Elo rating
                  dplyr::filter(faceoff_played == 
                                  max(faceoff_played, na.rm = TRUE)) %>%
                  dplyr::rename("prev_elo" = "post_elo") %>%
                  dplyr::select(player, prev_elo),
                by = c("player")) %>%
      # since there are scenarios where one player in a combo has 1+ faceoff
      # and the other is making their "debut", there's a quick ifelse there to handle it
      dplyr::mutate(prev_elo = ifelse(faceoff_played == 1, 1500, prev_elo)) %>%
      # in retrospect, I didn't need the two separate portions of the loop
      # but since it's already written and works, I'm not changing it
      elo() %>%
      ungroup()
    
  }
  # store the results (and which faceoff # it was) in a list so that we can pull it later
  res <- fo1 %>%
    dplyr::select(player, post_elo, faceoff_played)
  
  lst[[i]] <- res 
  # message to stay up to date on where the loop is progress-wise becayse it's fun
  print(paste0("Just completed faceoff #", i, " out of ", max(data$faceoff_id, na.rm = TRUE), 
               ". Have completed ", round((i / max(data$faceoff_id, na.rm = TRUE)) * 100, 
                                         digits = 1), "% of the faceoffs."))
  
}

results <- dplyr::bind_rows(lst)

First, I ran the loop here on all the 2019-2020 play-by-play data that is available, producing Elo numbers for that season. For the 2020-2021 and 2021-2022 seasons, I wanted to make each players’ starting point for the season their ending rating from the prior year, rather than start them at 1500 again. Doing so required a slight re-write of the above loop, but the idea is the same. The only difference is that for a players’ first faceoff of a new season, their default is their ending value from the last season (assuming they have recorded a faceoff).


Results

I’ll give a little overview of some results, but the best place to check out the up to-date results would be here, on the R Shiny leaderboard that I threw together.

Something that I’m interested in following over the next few years is whether anyone can approach both the dominance and longevity that Dempsey has posted as a faceoff artist. From the games that we have play-by-play data on, the beginning of Dempsey’s run was a little bit bumpy, even trending below-average at times, but since about her 75th faceoff, she’s been nearly unbeatable on a consistent basis.

Mikyla Grant-Mentis and Shiann Darkangelo both began their careers more efficiently than Dempsey did, but have slowed down as they crossed the 100-career faceoff threshold. The player who pops the most is a former teammate of Dempsey’s, Tereza Vanisoza, who accumulated a 1952 Elo rating in just 116 PHF faceoffs. That’s insane efficiency, especially as a rookie in the PHF. Given that she’s only 25 and so good, Vanisova would be a phenomenal bet to challenge for Dempsey’s crown, but we may not see that happen as she’s moved to the SDHL for 2021-2022.


Beyond these rolling graphs, I’ve included a table of Faceoff Elo Ratings for the first few weeks of the 2021-2022 PHF season. Dempsey’s gap over the rest of the field is absurd; she’s on her own scale, to be honest.

Many of the names at the top of the table have accumulated a ton of faceoff opportunities over their career, but there are some relative newcomers making an appearance in the top 10.

Shiann Darkangelo, Mikyla Grant-Mentis, and Meghan Lorence are those notable names to me; it’ll be almost unfair if MGM becomes an excellent faceoff artist on top of everything else that she does (Mike said as much in a recent piece discussing PHF fantasy team-building trends).

(Again, would recommend using my R Shiny leaderboard to view the full results (over multiple seasons) and PHF Elo Ratings + a few other things I threw in)

LS0tDQp0aXRsZTogIlBIRiBGYWNlb2ZmIEVsbyBSYXRpbmdzIg0KYXV0aG9yOiAiQmVuIEhvd2VsbCINCmRhdGU6ICIxMS8xNS8yMDIxIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGhpZ2hsaWdodGVyOiBudWxsDQogICAgdGhlbWU6ICJmbGF0bHkiDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQ0KYGBgDQoNCioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKiAgICAgICAgICAgICAgIA0KDQpGYWNlb2ZmcyBpbiBob2NrZXkgYXJlbid0IHBhcnRpY3VsYXJseSBzb21ldGhpbmcgdGhhdCBJJ3ZlIHRob3VnaHQgbXVjaCBhYm91dCBiZWNhdXNlIEkndmUgbmV2ZXIgaGFkIGFueSBnb29kIGlkZWFzIG9uIGhvdyB0byBxdWFudGlmeSB0aGVtLiBUaGUgZmlyc3QgcGllY2Ugb2YgcmVzZWFyY2ggdGhhdCBJIHJlYWQgcmVnYXJkaW5nIGZhY2VvZmZzIGluIHdvbWVuJ3MgaG9ja2V5IHdhcyB0aGlzIFtCaWcgRGF0YSBDdXAgcHJvamVjdF0oaHR0cHM6Ly9ob2NrZXktZ3JhcGhzLmNvbS8yMDIxLzA0LzE0L2hvdy1pbXBvcnRhbnQtYXJlLWZhY2VvZmZzLXRvLXBvc3Nlc3Npb24taW4td29tZW5zLWhvY2tleS8pIGZyb20gU2hheW5hIEdvbGRtYW4sIE1pa2UgTXVycGh5LCBhbmQgQWx5c3NhIExvbmdtdWlyLiBUaGV5IGRpc2NvdmVyZWQgdGhhdCB3aW5uaW5nIGEgZmFjZW9mZiBpbiB0aGUgb2ZmZW5zaXZlIHpvbmUgZnJlcXVlbnRseSBjb250cmlidXRlcyB0byBhIHNob3QgYXR0ZW1wdCwgbWFraW5nIGZhY2VvZmZzIHdvcnRoIG1vcmUgaW4gdGhlIHdvbWVuJ3MgZ2FtZSB0aGFuIHRoZSBtZW4ncy4gICAgICAgICAgICAgICAgDQoNClRoZSBzZWNvbmQgdGltZSB0aGF0IEkgc3RhcnRlZCB0aGlua2luZyBhYm91dCBmYWNlb2ZmcyB3YXMgKG9uY2UgYWdhaW4pIHRoYW5rcyB0byBNaWtlIHdoZW4gaGUgdHdlZXRlZCBhYm91dCBKaWxsaWFuIERlbXBzZXkncyBzdGVsbGFyIDIzLTYgZmFjZW9mZiByZWNvcmQgYWdhaW5zdCB0aGUgTWlubmVzb3RhIFdoaXRlY2FwcyBvbiBOb3ZlbWJlciA3dGgsIGEgMS0wIHdpbiBmb3IgdGhlIEJvc3RvbiBQcmlkZS4gICAgICAgICAgICAgICAgICAgIA0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh0d2VldHJtZCkNCmxpYnJhcnkoZmFzdFJob2NrZXkpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoc3RyaW5ncikNCmBgYA0KDQpgYGB7cn0NCnR3ZWV0X2VtYmVkKCJodHRwczovL3R3aXR0ZXIuY29tL0RpZ0RlZXBCU0Ivc3RhdHVzLzE0NTkxNjM5ODQxMTI4OTM5NTgiLCBhbGlnbiA9ICJjZW50ZXIiKQ0KYGBgDQoNCldpdGggdGhlIHJlY2VudCBkZXZlbG9wbWVudCBvZiB0aGUgW2BmYXN0UmhvY2tleWBdKGh0dHBzOi8vYmVuaG93ZWxsNzEuZ2l0aHViLmlvL2Zhc3RSaG9ja2V5LykgcGFja2FnZSB0byBwdWxsIHBsYXktYnktcGxheSBkYXRhIGZyb20gdGhlIFByZW1pZXIgSG9ja2V5IEZlZGVyYXRpb24gKFBIRiksIEkgZmlndXJlZCB0aGF0IGV4cGxvcmluZyBmYWNlb2ZmcyB0aHJvdWdoIHRoZSBsZW5zZSBvZiBKaWxsaWFuIERlbXBzZXkncyBza2lsbCB3b3VsZCBiZSBhbiBpbnRlcmVzdGluZyB0ZXN0IG9mIHRoZSBwYWNrYWdlLiAgICAgICAgICAgDQoNClRoZSBmaXJzdCBwbGFjZSBJIHN0YXJ0ZWQgd2FzIHdpdGggUmF3IFdpbi1Mb3NzIHJlY29yZHMgaW4gdGhlIFBIRjsgMjMtNiBzZWVtZWQgbGlrZSBhICpyZWFsbHkqIGdvb2QgcmVjb3JkLCBzbyBJIHN0YXJ0ZWQgYnkgY29tcGFyaW5nIERlbXBzZXkncyB1bmlxdWUgZmFjZW9mZiBXL0wgY29tYm9zIGFnYWluc3QgdGhlIHVuaXF1ZSByZWNvcmRzIGluIHRoZSBQSEYuIFR1cm5zIG91dCB0aGF0IERlbXBzZXkgaGFzIGJlZW4gKmRvbWluYW50KiByZWxhdGl2ZSB0byB0aGUgbGVhZ3VlLiAgICAgICAgICAgICAgICAgICANCg0KYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTd9DQojIGdtIDwtIGxpc3QoKQ0KIyANCiMgZm9yIChzIGluIDIwMjIpIHsNCiMgICANCiMgICAjIHByaW50KHBhc3RlMCgiU2NyYXBpbmcgZGF0YSBmb3IgIiwgcywgIiBub3chIikpDQojICAgDQojICAgZ21zIDwtIGZhc3RSaG9ja2V5OjpwaGZfc2NoZWR1bGUoc2Vhc29uID0gcykgJT4lDQojICAgICBkcGx5cjo6ZmlsdGVyKHN0YXR1cyA9PSAiRmluYWwiKSAlPiUNCiMgICAgIGRwbHlyOjpzZWxlY3QoZ2FtZV9pZCkNCiMgICANCiMgICBnbVtbc11dIDwtIGdtcw0KIyAgIA0KIyB9DQojIA0KIyBnYW1lX2lkcyA8LSBkcGx5cjo6YmluZF9yb3dzKGdtKQ0KIyANCiMgcGJwIDwtIGxpc3QoKQ0KIyANCiMgaWRzIDwtIGdhbWVfaWRzJGdhbWVfaWQNCiMgDQojIGZvciAoaSBpbiBpZHMpIHsNCiMgICANCiMgICAjIHByaW50KGkpDQojICAgDQojICAgZGF0YSA8LSBmYXN0UmhvY2tleTo6bG9hZF9waGZfcGJwKGdhbWVfaWQgPSBpKQ0KIyAgIA0KIyAgIHBicFtbaV1dIDwtIGRhdGENCiMgICANCiMgfQ0KIyANCiMgZGYxIDwtIGRwbHlyOjpiaW5kX3Jvd3MocGJwKQ0KIyANCiMgZGYxIDwtIGRmMSAlPiUNCiMgICBkcGx5cjo6c2VsZWN0KGdhbWVfaWQsIGhvbWVfdGVhbSwgYXdheV90ZWFtLCBldmVudCwgZGVzY3JpcHRpb24sDQojICAgICAgICAgICAgICAgICB0ZWFtLCBmaXJzdF9wbGF5ZXIsIHNlY29uZF9wbGF5ZXIpDQojIA0KIyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgIyMjIyBGb3IgdGhlIGluaXRhbCBydW4gdGhyb3VnaCBJIGp1c3QgcHVsbGVkIGFuIG9sZCBjb3B5IG9mIGFsbCB0aGUgZGF0YSAjIyMjDQojICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyANCiMgZGYyIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYmVuaG93ZWxsNzEvZmFzdFJob2NrZXktZGF0YS9tYWluL3BsYXlfYnlfcGxheS5jc3YiKSAlPiUNCiMgICBkcGx5cjo6c2VsZWN0KGdhbWVfaWQsIGhvbWVfdGVhbSwgYXdheV90ZWFtLCBldmVudCwgZGVzY3JpcHRpb24sDQojICAgICAgICAgICAgICAgICB0ZWFtLCBmaXJzdF9wbGF5ZXIsIHNlY29uZF9wbGF5ZXIpDQojIA0KIyBkZiA8LSBkcGx5cjo6YmluZF9yb3dzKGRmMiwgZGYxKQ0KIyANCiMgZmFjZW9mZnMgPC0gZGYgJT4lDQojICAgZHBseXI6OmZpbHRlcihldmVudCA9PSAiRmFjZW9mZiIpICU+JQ0KIyAgIGRwbHlyOjpzZWxlY3QoZ2FtZV9pZCwgaG9tZV90ZWFtLCBhd2F5X3RlYW0sIGRlc2NyaXB0aW9uLA0KIyAgICAgICAgICAgICAgICAgdGVhbSwgZmlyc3RfcGxheWVyLCBzZWNvbmRfcGxheWVyKSAlPiUNCiMgICBkcGx5cjo6bXV0YXRlKGlzX2hvbWVfdGVhbSA9IGlmZWxzZShzdHJpbmdyOjpzdHJfZGV0ZWN0KHRlYW0sIGhvbWVfdGVhbSksIDEsIDApKQ0KIyANCiMgIyBjaSA8LSBiaW5jb25mKHN1bShmYWNlb2ZmcyRpc19ob21lX3RlYW1bZmFjZW9mZnMkaXNfaG9tZV90ZWFtID09IDFdKSwgbnJvdyhmYWNlb2ZmcykpDQojIA0KIyAjIGh3cCA8LSBtZWFuKGZhY2VvZmZzJGlzX2hvbWVfdGVhbSwgbmEucm0gPSBUUlVFKQ0KIyAjIGhvbWUgdGVhbSB3aW5zIHRoZSBmYWNlb2ZmIGFib3V0IDQ5LjclIG9mIHRoZSB0aW1lDQojICMgYW5kIHRoZSBDSSBnb2VzIGZyb20gMC40ODIgdG8gMC41MTEgc28gbm8gYWR2YW50YWdlIHRoZXJlLCB3aGljaCBpcyB1bnN1cnByaXNpbmcNCiMgDQojIHdpbl9kYXRlIDwtIGZhY2VvZmZzICU+JQ0KIyAgIGdyb3VwX2J5KGdhbWVfaWQsIGZpcnN0X3BsYXllcikgJT4lDQojICAgc3VtbWFyaXNlKHdpbnMgPSBuKCkpICU+JQ0KIyAgIHJlbmFtZSgicGxheWVyIiA9ICJmaXJzdF9wbGF5ZXIiKQ0KIyANCiMgbG9zc19kYXRlIDwtIGZhY2VvZmZzICU+JQ0KIyAgIGdyb3VwX2J5KGdhbWVfaWQsIHNlY29uZF9wbGF5ZXIpICU+JQ0KIyAgIHN1bW1hcmlzZShsb3NzZXMgPSBuKCkpICU+JQ0KIyAgIHJlbmFtZSgicGxheWVyIiA9ICJzZWNvbmRfcGxheWVyIikNCiMgDQojIHJlY29yZCA8LSBmdWxsX2pvaW4od2luX2RhdGUsIGxvc3NfZGF0ZSwgYnkgPSBjKCJnYW1lX2lkIiwgInBsYXllciIpKQ0KIyANCiMgcmVjb3JkIDwtIHJlY29yZCAlPiUNCiMgICBkcGx5cjo6bXV0YXRlKHdpbnMgPSByZXBsYWNlX25hKHdpbnMsIDApLA0KIyAgICAgICAgICAgICAgICAgbG9zc2VzID0gcmVwbGFjZV9uYShsb3NzZXMsIDApLA0KIyAgICAgICAgICAgICAgICAgdG90YWwgPSB3aW5zICsgbG9zc2VzLA0KIyAgICAgICAgICAgICAgICAgd2luX3BjdCA9IHNjYWxlczo6cGVyY2VudCh3aW5zIC8gdG90YWwpLA0KIyAgICAgICAgICAgICAgICAgbG9zc19wY3QgPSBzY2FsZXM6OnBlcmNlbnQobG9zc2VzIC8gdG90YWwpKQ0KIyANCiMgZnJlcSA8LSByZWNvcmQgJT4lDQojICAgdW5ncm91cCgpICU+JQ0KIyAgIGNvdW50KHdpbnMsIGxvc3NlcykNCiMgDQojIEZJTExTIDwtIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJKaWxsaWFuIERlbXBzZXkiID0gImdvbGQiLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV2ZXJ5b25lIEVsc2UiID0gImdyZXkxMSIpKQ0KIyANCiMgcmVjb3JkICU+JQ0KIyAgIGxlZnRfam9pbihmcmVxLCBieSA9IGMoIndpbnMiLCAibG9zc2VzIikpICU+JQ0KIyAgIG11dGF0ZShjb2wgPSBpZmVsc2UocGxheWVyID09ICJKaWxsaWFuIERlbXBzZXkiLCAiSmlsbGlhbiBEZW1wc2V5IiwgIkV2ZXJ5b25lIEVsc2UiKSwNCiMgICAgICAgICAgYWxwID0gaWZlbHNlKHBsYXllciA9PSAiSmlsbGlhbiBEZW1wc2V5IiwgMSwgMC42KSkgJT4lDQojICAgZ3JvdXBfYnkoY29sLCB3aW5zLCBsb3NzZXMpICU+JQ0KIyAgIHN1bW1hcmlzZShuID0gbigpKSAlPiUNCiMgICBtdXRhdGUoYWxwID0gaWZlbHNlKGNvbCA9PSAiSmlsbGlhbiBEZW1wc2V5IiwgMSwgMC42KSkgJT4lDQojICAgZ2dwbG90KCkgKw0KIyAgIGdlb21fYWJsaW5lKHNpemUgPSAxKSArDQojICAgZ2VvbV9wb2ludChhZXMoeCA9IGxvc3NlcywgeSA9IHdpbnMsIGNvbG9yID0gY29sLCBhbHBoYSA9IEkoYWxwKSksIA0KIyAgICAgICAgICAgICAgc2l6ZSA9IDQuNSkgKw0KIyAgIGxhYnMoeCA9ICJGYWNlb2ZmIExvc3NlcyIsIHkgPSAiRmFjZW9mZiBXaW5zIiwNCiMgICAgICAgIHRpdGxlID0gIkluZGl2aWR1YWwgRmFjZW9mZiBXL0wgYnkgR2FtZSBpbiBQSEYgSGlzdG9yeSIsDQojICAgICAgICBzdWJ0aXRsZSA9ICJKaWxsaWFuIERlbXBzZXkgPSBEb21pbmFudCIsDQojICAgICAgICBjYXB0aW9uID0gIkJlbiBIb3dlbGwgfCBAYmVuaG93ZWxsNzEgfCBEYXRhIGZyb20gZmFzdFJob2NrZXkiKSArDQojICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCkgKw0KIyAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDApICsNCiMgICB0aGVtZV9taW5pbWFsKCkgKw0KIyAgIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQojICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQojICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KIyAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSksDQojICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxMiwgaGp1c3QgPSAwLjUpLA0KIyAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpLA0KIyAgICAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIml2b3J5IiwgY29sb3VyID0gIml2b3J5IikpICsNCiMgICBGSUxMUyArIA0KIyAgIGNvb3JkX2ZpeGVkKCkgKw0KIyAgIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI1KSkgKw0KIyAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDI1KSkgKw0KIyAgIGdlb21fdGV4dCh4ID0gMjEsIHkgPSAzLCBsYWJlbCA9ICJNb3JlIGxvc3NlcyIsIGFuZ2xlID0gMCwgc2l6ZSA9IDYsDQojICAgICAgICAgICAgIGNvbG9yID0gIiNEQjQzMjUiKSArDQojICAgZ2VvbV90ZXh0KHggPSAxMC4zLCB5ID0gMjQsIGxhYmVsID0gIk1vcmUgd2lucyIsIGFuZ2xlID0gMCwgc2l6ZSA9IDYsDQojICAgICAgICAgICAgIGNvbG9yID0gIiMwMDYxNjQiKSArDQojICAgZ2VvbV90ZXh0KHggPSAyLjEsIHkgPSAyMywgbGFiZWwgPSAiMjMtNlxuKDExLzA3KSIsIHNpemUgPSA2LA0KIyAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsgDQojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMy41LCB5ID0gMjMuNywgeGVuZCA9IDUuNSwgeWVuZCA9IDIzLjEyKSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjUsICJjbSIpKSkNCmBgYA0KDQo8Y2VudGVyPg0KDQohW10ocGhvdG9zL2RlbXBzZXlfdXNlLmpwZykNCg0KPC9jZW50ZXI+DQoNCkFib3ZlIGlzIGEgcmVwcmVzZW50YXRpb24gb2YgZXZlcnkgdW5pcXVlIGZhY2VvZmYgVy1MIHJlY29yZCB0aGF0IGhhcyBiZWVuIHJlY29yZGVkIGluIHRoZSBQSEYgKHRoYXQgd2UgaGF2ZSBmYWNlb2ZmIHBsYXktYnktcGxheSBkYXRhIGZvcikgd2hlcmUgRGVtcHNleSdzIGdhbWVzIGFyZSBpbiBnb2xkIGFuZCB0aGUgbGVhZ3VlcyBhcmUgaW4gZ3JleS4gICAgICAgICAgICAgICAgICAgICANCg0KSW4gYWxtb3N0IGV2ZXJ5IHNpbmdsZSBnYW1lIHRoYXQgRGVtcHNleSBoYXMgcGxheWVkLCBzaGUncyB3b24gbW9yZSBmYWNlb2ZmcyB0aGFuIHNoZSdzIGxvc3QuIE5vdCBvbmx5IHRoYXQsIHNoZSdzIGRvbmUgaXQgd2l0aCBhbiBlZmZpY2llbmN5IHRoYXQgbm8gb25lIGVsc2UgaW4gdGhlIGxlYWd1ZSBoYXMgbWF0Y2hlZC4gSGVyIDIzIGZhY2VvZmYgd2lucyB0aWVkIHRoZSBQSEYgcmVjb3JkIChLZW5kYWxsIENvcm5pbmUgb24gSmFudWFyeSAxMXRoLCAyMDIwKSwgYnV0IERlbXBzZXkgZGlkIGl0IGluIGp1c3QgMjkgZmFjZW9mZiBvcHBvcnR1bml0aWVzLCB3aGVyZSBDb3JuaW5lIHRvb2sgNDAgb3Bwb3J0dW5pdGllcyB0byBhY2N1bXVsYXRlIDIzIHdpbnMuICAgICAgICAgICAgDQoNCkRlbXBzZXkgaG9sZHMgYSAyMi01IGdhbWUsIG51bWVyb3VzIGdhbWVzIHdpdGggMTUrIHdpbnMsIGFuZCBhbiAxOC0yIGdhbWUuIFBIRiBwbGF5ZXJzLCBhcyBhIHdob2xlLCBoYXZlIG9jY2FzaW9uYWxseSBhcHByb2FjaGVkIHRob3NlIG51bWJlcnMsIGJ1dCB0aGF0J3MgY29tcGFyaW5nIERlbXBzZXkgYWxvbmUgdG8gdGhlIGVudGlyZSBsZWFndWUuIFRoaXMgZ3JhcGhpYyBhbG9uZSBzaG93Y2FzZXMgdGhhdCBKaWxsaWFuIERlbXBzZXkgaXMgaW4gYSBsZWFndWUgb2YgaGVyIG93biB3aGVuIGl0IGNvbWVzIHRvIHdpbm5pbmcgZmFjZW9mZnMgaW4gdGhlIFBIRi4gICAgICAgICAgICAgICAgICAgICAgICAgIA0KDQoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqDQoNCiMgRUxPIFJhdGluZ3MNCg0KQWZ0ZXIgdmlzdWFsaXppbmcgRGVtcHNleSdzIGJyaWxsaWFuY2UsIEkgd2FudGVkIHRvIGZpZ3VyZSBvdXQgYSB3YXkgdG8gdHJ5IGFuZCBldmFsdWF0ZSBwbGF5ZXIgc2tpbGwgb24gZmFjZW9mZnMgYmV5b25kIHVzaW5nIHJhdyBXLUwgcmVjb3Jkcy4gICAgICAgICAgICAgICANCg0KVG8gbWUsIHRoZSBoYXJkZXN0IHBhcnQgb2YgbWVhc3VyaW5nIHRoaXMgc29ydCBvZiBza2lsbCBpcyBhY2NvdW50aW5nIGZvciB0aGUgc3RyZW5ndGggb2YgdGhlIG9wcG9uZW50LiBXaGlsZSBXLUwlIGlzIGhlbHBmdWwgKGJlY2F1c2Ugd2lubmluZyBmYWNlb2ZmcyBpcyBoZWxwZnVsKSwgdGhlcmUncyBjbGVhcmx5IGEgZGlmZmVyZW5jZSBiZXR3ZWVuIHdpbm5pbmcgb25lIGFnYWluc3QgRGVtcHNleSBvciB3aW5uaW5nIG9uZSBhZ2FpbnN0IHRoZSB3b3JzdCBmYWNlb2ZmIHBsYXllciBpbiB0aGUgbGVhZ3VlLiAgICAgICAgICAgICAgICAgICAgICAgDQoNClRoaW5raW5nIHRocm91Z2ggdGhlIHByb2JsZW0gdGhpcyB3YXkgbGVkIG1lIHRvIHVzaW5nIEVsbyBSYXRpbmdzIHRvIG1lYXN1cmUgZmFjZW9mZiBza2lsbCBzaW5jZSBhIGZhY2VvZmYgaXMgYSB6ZXJvLXN1bSBnYW1lIHdoZXJlIG9uZSBwbGF5ZXIgd2lucyBhbmQgdGhlIG90aGVyIGxvc2VzLiAgICAgICAgICAgICAgICANCg0KSSB3YXMgZmlyc3QgYXdhcmUgb2YgRWxvIFJhdGluZ3MgaW4gdGhlIGNvbnRleHQgb2YgcmF0aW5nIGNoZXNzIHBsYXllcnMgb3Igc2VydmluZyBhcyB0ZWFtLWxldmVsIHJhdGluZy4gVHVybnMgb3V0LCBpdCdzIGFsc2FvIGJlZW4gdXNlZCB0byByYXRlIE5ITCBza2F0ZXJzIG9uIGZhY2VvZmZzIGJ5IG11bHRpcGxlIHBlb3BsZSAoW1R5cmVsIFN0b2tlc10oaHR0cHM6Ly9zdGF0c2J5c3Rva2VzLndvcmRwcmVzcy5jb20vMjAxNi8wNy8yOC9pbnRyb2R1Y2luZy1hbi1lbG8tdHlwZS1tb2RlbC1mb3ItbmhsLWZhY2VvZmZzLXplbm9uLXJhbmtpbmdzLyksIFtIb2NrZXlFbG9SYXRpbmdzXShodHRwczovL2hvY2tleWVsb3JhdGluZ3MuY29tL3BsYXllcnMvZmFjZW9mZnJhdGluZ3MpLCBhbmQgbW9yZSkuIChZb3UgY2FuIGZpbmQgYSBnb29kIEVsbyBwcmltZXIgW2hlcmVdKGh0dHBzOi8vbWVkaXVtLmNvbS9wdXJwbGUtdGhlb3J5L3doYXQtaXMtZWxvLXJhdGluZy1jNGViN2E5MDYxZTApKS4gSG93ZXZlciwgYXMgd2l0aCBtYW55IHRoaW5ncywgbm8gb25lIGhhcyBkZXZlbG9wZWQgc2thdGVyLWxldmVsIEVsbyByYXRpbmdzIGZvciB3b21lbidzIGhvY2tleSBvbiBmYWNlb2Zmcy4gU28sIEkgZGlkISAgICAgICAgICAgICAgICANCg0KV2l0aCBFbG8gUmF0aW5ncywgZXZlcnlvbmUgc3RhcnRzIGZyb20gdGhlIHNhbWUgcG9pbnQgd2hpY2gsIGluIHRoaXMgaW5zdGFuY2UsIGlzIDE1MDAuIFNvLCBldmVyeSB0aW1lIGEgUEhGIHBsYXllciB0YWtlcyB0aGVpciBmaXJzdCBmYWNlb2ZmLCB0aGV5IGJlZ2luIGFzICphdmVyYWdlKiwgd2l0aCBhbiBFbG8gc2NvcmUgb2YgMTUwMC4gICAgICAgICAgDQoNCkFzIHlvdSB3aW4gKG9yIGxvc2UpIGZhY2VvZmZzIGFnYWluc3Qgb3RoZXIgcGxheWVycywgeW91ciBFbG8gcmF0aW5nIGJlZ2lucyB0byBjaGFuZ2UuIFRoZSBpZGVhIGlzIHRoYXQgaWYgYSBwbGF5ZXIgd2l0aCBhIGhpZ2ggRWxvIGJlYXRzIGEgcGxheWVyIHdpdGggYSBsb3cgRWxvLCBuZWl0aGVyIHNjb3JlIGNoYW5nZXMgbXVjaCwgYmVjYXVzZSB0aGF0J3MgZXhwZWN0ZWQhIEhvd2V2ZXIsIGlmIHRoZSByZXZlcnNlIGhhcHBlbnMgYW5kIGFuIHVuZGVyZG9nIGJlYXRzIGEgaGlnaC1sZXZlbCBwbGF5ZXIsIHRoZXJlIHdpbGwgYmUgYSBtdWNoIG1vcmUgZHJhc3RpYyBjaGFuZ2UgaW4gdGhlaXIgcG9zdC1mYWNlb2ZmIEVsbyByYXRpbmdzLiAgICAgICAgICAgICAgICAgDQoNClRoZSBvdGhlciBrZXkgcG9pbnQgaXMgdGhhdCB0aGUgY2hhbmdlcyBpbiBFbG8gd2lsbCBhbHdheXMgZXF1YWwgMDsgc28gd2hlbiBwbGF5ZXIgMSB3aW5zICsxMCBwb2ludHMsIHBsYXllciAyIGxvc2VzIC0xMCBwb2ludHMgdG8gdGhlaXIgRWxvLCBhbmQgdmljZSB2ZXJzYS4gICAgICAgICAgICAgICAgICAgICAgICAgIA0KDQpUaGUgZm9ybXVsYSB0byBjYWxjdWxhdGUgRWxvIGlzIGJlbG93OiAgICAgICAgICAgICANCg0KJCQgRXhwV2luUGN0X3tQbGF5ZXJ9ID0gRWxvX3tTdGFydFBsYXllcn0gLyAoRWxvX3tQMX0gKyBFbG9fe1AyfSkgJCQNCiQkIEVsb197d2lubmVyfSA9IEVsb197c3RhcnR9ICsgSyAqICgxIC0gRXhwV2luUGN0X3tQMX0pICQkDQokJCBFbG9fe2xvc2VyfSA9IEVsb197c3RhcnR9ICsgSyAqICgwIC0gRXhwV2luUGN0X3tQMn0pICQkICAgICAgICAgICAgICAgDQoNCk9uZSBvZiB0aGUgZnVuIGNvbXBvbmVudHMgb2YgY2FsY3VsYXRpbmcgRWxvIGlzIHRoYXQgaXMgaGFzIGEgYnVpbHQgaW4gYEV4cGVjdGVkIFdpbm5pbmclYCB0byBpdCwgd2hpY2ggaXMgZWFzeSB0byBjYWxjdWxhdGUgYnkgdGFraW5nIHRoZSBgRWxvYCBvZiBhbiBpbmRpdmlkdWFsIHBsYXllciBhbmQgY29tcGFyaW5nIGl0IHRvIHRoZSB0b3RhbCBFbG8gb2YgdGhlIHBsYXllcnMgaW52b2x2ZWQgaW4gdGhlIGZhY2VvZmYuICAgICAgICAgICAgICAgICANCg0KU28sIGlmIFBsYXllciBBIGVudGVycyBhIGZhY2VvZmYgd2l0aCBhIDE4MDAgRWxvIGFuZCBQbGF5ZXIgQiBlbnRlcnMgYXQgMTIwMCwgd2UnZCBzYXkgdGhhdCBQbGF5ZXIgQSBoYXMgYSA2MCUgRXhwZWN0ZWQgV2lubmluZyUgKDE4MDAgLyAzMDAwKSwgd2hpbGUgUGxheWVyIEIgaXMgYXQgNDAlLiBUaGlzIHJlc3VsdHMgaW4gdGhlIHN5c3RlbSB0aGF0IHJld2FyZHMgdXBzZXRzIGFuZCBvbmx5IGFzc2lnbnMgYSBmZXcgcG9pbnRzIGZvciB3aW5uaW5nIGZhY2VvZmZzIHRoYXQgeW91J3JlIHN1cHBvc2VkIHRvLiAgICAgIA0KDQpUaGUgbWFnbml0dWRlIG9mIHRoZSBjaGFuZ2UgaW4gRWxvIGlzIGRldGVybWluZWQgYnkgdGhlIGBLYCBwYXJhbWV0ZXIuIEZvciBQSEYgZmFjZW9mZiBFbG8sIEkgY2hvc2UgdG8gc2V0IHRoaXMgYXQgMjUuIFdoeT8gQmVjYXVzZSBJIHdhbnRlZCB0by4gSGlnaCBgS2AgdmFsdWVzIHJlc3VsdCBpbiBtb3JlIHZvbGF0aWxpdHkgYWZ0ZXIgYW4gZXZlbnQsIHdoaWxlIGxvd2VyIGBLYCB2YWx1ZXMgbWFrZSBpdCBoYXJkZXIgdG8gZGlnIHlvdXJzZWxmIG91dCBvZiBhIGhvbGUuIFRoZXJlIGlzIGRlZmluaXRlbHkgbW9yZSB0aGF0IEkgY291bGQgZG8gdG8gdHVuZSB0aGlzIGBLYCwgYnV0IEkgZmVsdCB0aGF0IHRoZSBnYWluIGZyb20gYWRqdXN0aW5nIG15IGBLYCB3b3VsZCBiZSBtYXJnaW5hbCBhdCBiZXN0LiAgICAgICAgICAgICAgICAgICAgICANCg0KSXQncyBhIHNpbXBsZSBzeXN0ZW0gYW5kIGVxdWF0aW9uLCBidXQgb25lIHRoYXQgdG9vayBteSBhIGxpdHRsZSB3aGlsZSB0byBmaWd1cmUgb3V0IGhvdyB0byBjb2RlLiAoSSdtIGFib3V0IHRvIGRpdmUgaW50byB0aGUgY29kZSB0aGF0IEkgd3JvdGUgdG8gcHJvZHVjZSB0aGUgRWxvIHJhdGluZ3MsIHNvIGlmIHlvdSBkb24ndCB3YW50IHRvIGxpc3RlbiB0byBtZSByYW1ibGUgYWJvdXQgaXQsIHlvdSBjYW4gc2tpcCB0byB0aGUgcmVzdWx0cyBzZWN0aW9uLiBUaG91Z2gsIEkgcmVhbGl6ZSBhcyBJIHdyaXRlIHRoaXMgdGhhdCBJJ3ZlIGJlZW4gcmFtYmxpbmcgYWJvdXQgaG93IEVsbyBhbmQgdGhlIGVxdWF0aW9ucyB3b3JrIHNvLCBpZiB5b3UndmUgbWFkZSBpdCB0aGlzIGZhciB3aXRob3V0IHNraXBwaW5nLCB5b3UncmUgcHJvYmFibHkgbm90IHNraXBwaW5nIHRoZSBjb2RlIHRhbGsuIEFueXdheXMsIHRpbWUgdG8gZ2V0IGJhY2sgb24gdG9waWMpLiAgICAgICAgICAgIA0KDQpUaGUgZmlyc3Qgc3RlcCB3YXMgdG8gdGFrZSB0aGUgcmF3IFBIRiBwbGF5LWJ5LXBsYXkgZGF0YSBhbmQgdHVybiBpdCBpbnRvIGEgcmVwcmVzZW50YXRpb24gb2YgdGhlIHR3byBwbGF5ZXJzIGludm9sdmVkIGluIGEgZmFjZW9mZiBhbmQgd2hvIHdvbi4gSW4gYWRkaXRpb24sIHNpbmNlIHRoZSBvcmRlciBvZiB3aG8geW91IHBsYXkgZG9lcyBoYXZlIHJhbWlmaWNhdGlvbnMgYXQgdGhlIEVsby1sZXZlbCwgaXQgd2FzIGltcG9ydGFudCB0byBrZWVwIHRyYWNrIG9mIHRoZSBvcmRlciBpbiB3aGljaCBlYWNoIGZhY2VvZmYgb2NjdXJlZC4gICAgICAgICAgICAgICAgDQoNCmBgYHtyfQ0KZGYgPC0gDQogIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYmVuaG93ZWxsNzEvZmFzdFJob2NrZXktZGF0YS9tYWluL3BsYXlfYnlfcGxheS5jc3YiKSAlPiUNCiAgZmlsdGVyKGV2ZW50ID09ICJGYWNlb2ZmIikgICU+JQ0KICBkcGx5cjo6c2VsZWN0KGdhbWVfaWQsIGZpcnN0X3BsYXllciwgc2Vjb25kX3BsYXllcikgJT4lDQogIG11dGF0ZShmYWNlb2ZmX2lkID0gcm93X251bWJlcigpKSAlPiUNCiAgcmVuYW1lKGMoIndpbm5lciIgPSAiZmlyc3RfcGxheWVyIiwgDQogICAgICAgICAgICJsb3NlciIgPSAic2Vjb25kX3BsYXllciIpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKHdpbm5lciwgbG9zZXIpLA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAicmVzdWx0IiwNCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJwbGF5ZXIiKSAlPiUNCiAgZ3JvdXBfYnkocGxheWVyKSAlPiUNCiAgbXV0YXRlKGZhY2VvZmZfcGxheWVkID0gcm93X251bWJlcigpKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KU2luY2UgSSd2ZSBzYXZlZCBhbGwgcHJlLTIwMjEtMjAyMiBQSEYgZGF0YSBpbnRvIGl0J3Mgb3duIHNlcGFyYXRlIFtHaXRIdWIgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL2Jlbmhvd2VsbDcxL2Zhc3RSaG9ja2V5LWRhdGEpIGZyb20gdGhlIGBmYXN0UmhvY2tleWAgcGFja2FnZSwgSSBsb2FkZWQgdGhlIG9sZCBkYXRhIGZyb20gdGhlcmUgYW5kIHVzZWQgdGhlIHNjcmFwZXIgZm9yIHRoZSBuZXcgZGF0YS4gICAgICAgICAgICAgDQoNCkkgcmUtc2hhcGVkIHRoZSBwbGF5LWJ5LXBsYXkgZGF0YSB0byBwcm9kdWNlIGEgZmV3IGltcG9ydGFudCBjb2x1bW5zLiBGaXJzdCwgSSBjb21iaW5lZCBib3RoIHRoZSB3aW5uaW5nIGFuZCBsb3NpbmcgcGxheWVyIGludG8gb25lIGNvbHVtbiB3aXRoIHRoZSBgd2lubmVyL2xvc2VyYCBJRCBpbiBhIHNlY29uZCBjb2x1bW4uIEluIGFkZGl0aW9uLCBJIGFkZGVkIGEgYGZhY2VvZmZfaWRgIGNvbHVtbiB0byBwcm92aWRlIGEgc2VxdWVudGlhbCBJRCBmb3IgYWxsIGZhY2VvZmZzLCBhbmQgYWRkZWQgYGZhY2VvZmZfcGxheWVkYCwgd2hpY2ggaXMgaG93IG1hbnkgZmFjZW9mZnMgdGhhdCBwbGF5ZXIgaGFzIHBsYXllZCBieSB0aGF0IHBvaW50LiAgICAgICAgICAgIA0KDQpgYGB7cn0NCmhlYWQoZGYpDQpgYGANCg0KRnJvbSB0aGVyZSwgSSBvcHRlZCB0byBjcmVhdGUgYSBgZm9yYCBsb29wIHRvIHJ1biB0aHJvdWdoIGFsbCB0aGUgZmFjZW9mZnMgaW4gdGhlIHBicCBkYXRhIHRvIGRlcml2ZSBFbG8gUmF0aW5ncy4gVGhlcmUgbWF5IGJlIGEgbW9yZSBlZmZpY2llbnQgd2F5IHRvIGRvIHRoaXMsIGJ1dCBmb3IgbWUsIHRoaXMgd29ya2VkIHdlbGwuICAgICAgICAgICAgIA0KDQpgYGByDQojIyMjIExvb3BpbmcgdGhyb3VnaCBhbGwgdGhlIHJlY29yZGVkIGZhY2VvZmZzIGluIHRoZSBjdXJyZW50IHNlYXNvbg0KZm9yIChpIGluIDE6bWF4KGRhdGEkZmFjZW9mZl9pZCwgbmEucm0gPSBUUlVFKSkgew0KICAjIHNlbGVjdGluZyB0aGUgbmV4dCBmYWNlb2ZmIGluIHRoZSBzZXF1ZW5jZQ0KICAjIHRoaXMgd2lsbCByZXR1cm4gYSBkYXRhZnJhbWUgdGhhdCBpcyB0d28gcm93cyBsb25nIHdpdGggdGhlIGFib3ZlIGNvbHVtbnMNCiAgZm8gPC0gZGF0YSAlPiUNCiAgICBkcGx5cjo6ZmlsdGVyKGZhY2VvZmZfaWQgPT0gaSkNCiAgIyBvbmNlIHRoZSBkYXRhZnJhbWUgaGFzIGJlZW4gc2VsZWN0ZWQsIGl0IHNwbGl0cyBpbnRvIGFuIGlmIHN0YXRlbWVudA0KICAjIHRoZSBzdGF0ZW1lbnQgZXZhbHVhdGVzIHdoZXRoZXIgb3Igbm90IHRoaXMgaXMgdGhlICJkZWJ1dCIgZmFjZW9mZiBmb3IgYm90aCBwbGF5ZXJzDQogICMgaWYgdGhpcyBpcyB0aGUgZmlyc3QgZmFjZW9mZiB0aGF0IGVhY2ggcGxheWVyIGhhcyBwYXJ0Y2lwYXRlZCBpbiwgaXQncyBlYXN5DQogICMgMTUwMCBpcyBsaXN0ZWQgYXMgdGhlIGRlZmF1bHQgdmFsdWUgZm9yIGVhY2ggYW5kIGRvZXMgdGhlIGNhbGN1bGF0aW9uDQogIGlmIChtYXgoZm8kZmFjZW9mZl9pZCwgbmEucm0gPSBUUlVFKSA9PSAxKSB7DQogICAgIyBkZWZhdWx0aW5nIHRvIDE1MDAgYXMgdGhlIHN0YXJ0aW5nIGVsbyB3aGVuIG5laXRoZXIgcGxheWVyIGhhcyBwbGF5ZWQgYSBmYWNlb2ZmIA0KICAgIGZvIDwtIGZvICU+JQ0KICAgICAgZHBseXI6Om11dGF0ZShwcmV2X2VsbyA9IGlmZWxzZShmYWNlb2ZmX3BsYXllZCA9PSAxLCAxNTAwLCBOQSkpDQogICAgDQogICAgdG90IDwtIGZvICU+JQ0KICAgICAgZ3JvdXBfYnkoZmFjZW9mZl9pZCkgJT4lDQogICAgICBzdW1tYXJpc2UodG90YWxfZWxvID0gc3VtKHByZXZfZWxvKSkNCiAgICANCiAgICBmbzEgPC0gZm8gJT4lDQogICAgICBsZWZ0X2pvaW4odG90LCBieSA9IGMoImZhY2VvZmZfaWQiKSkgJT4lDQogICAgICAjIHVzZXMgYSBmdW5jdGlvbiB0aGF0IEkgY3JlYXRlZCB0byBjYWxjdWF0ZSB0aGUgRWxvIFJhdGluZ3MgdXNpbmcgbXkgc2V0LXVwDQogICAgICBlbG8oKSAlPiUNCiAgICAgIHVuZ3JvdXAoKQ0KICAgICMgdGhlc2UgdmFsdWVzIHdpbGwgZ2V0IGFkZGVkIGludG8gYSBsaXN0IHRvIHByb3ZpZGUgc3RhcnRpbmcgdmFsdWVzIGZvciBhIHBsYXllcidzIA0KICAgICMgc2Vjb25kIGFuZCBiZXlvbmQgZmFjZW9mZnMNCiAgfSBlbHNlIGlmIChtYXgoZm8kZmFjZW9mZl9pZCwgbmEucm0gPSBUUlVFKSAhPSAxKSB7DQogICAgIyBwdWxsIGFsbCB0aGUgcHJldmlvdXMgcmVzdWx0cyBmb3IgdGhlIGluc3RhbmNlcyB3aGVyZSBhdCANCiAgICAjIGxlYXN0IDEgcGxheWVyIGhhcyBoYWQgYSBwcmV2aW91cyBmYWNlb2ZmDQogICAgIyBzaW5jZSB0aGV5J3ZlIGhhZCAxKyBmYWNlb2ZmLCB0aGV5J2xsIGhhdmUgYW4gRWxvIHJhdGluZw0KICAgIHByZXZfcmVzIDwtIGRwbHlyOjpiaW5kX3Jvd3MobHN0KQ0KICAgIA0KICAgIGZvMSA8LSBmbyAlPiUNCiAgICAgICMgYWRkIHRoZSBzdGFydGluZyBFbG8gdmFsdWUgdG8gdGhlIHBsYXllciBpdCBiZWxvbmdzIHRvDQogICAgICBsZWZ0X2pvaW4ocHJldl9yZXMgJT4lDQogICAgICAgICAgICAgICAgICB1bmdyb3VwKCkgJT4lDQogICAgICAgICAgICAgICAgICBncm91cF9ieShwbGF5ZXIpICU+JQ0KICAgICAgICAgICAgICAgICAgIyBvbmx5IHNlbGVjdGluZyB0aGUgdmFsdWVzIGZvciBwbGF5ZXJzIGluIHRoZSBjdXJyZW50IGZhY2VvZmYNCiAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIocGxheWVyICVpbiUgZm8kcGxheWVyKSAlPiUNCiAgICAgICAgICAgICAgICAgICMgb25seSB0YWtpbmcgdGhlIG1vc3QgcmVjZW50IHBvc3QtRWxvIHJhdGluZyBmb3IgdGhlIHBsYXllcnMNCiAgICAgICAgICAgICAgICAgICMgYmVjYXVzZSB0aGVyZSdzIG5vIG5lZWQgdG8gdGFrZSBhbiB1bi11cGRhdGVkIEVsbyByYXRpbmcNCiAgICAgICAgICAgICAgICAgIGRwbHlyOjpmaWx0ZXIoZmFjZW9mZl9wbGF5ZWQgPT0gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGZhY2VvZmZfcGxheWVkLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgICAgICAgICAgICAgICAgIGRwbHlyOjpyZW5hbWUoInByZXZfZWxvIiA9ICJwb3N0X2VsbyIpICU+JQ0KICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChwbGF5ZXIsIHByZXZfZWxvKSwNCiAgICAgICAgICAgICAgICBieSA9IGMoInBsYXllciIpKSAlPiUNCiAgICAgICMgc2luY2UgdGhlcmUgYXJlIHNjZW5hcmlvcyB3aGVyZSBvbmUgcGxheWVyIGluIGEgY29tYm8gaGFzIDErIGZhY2VvZmYNCiAgICAgICMgYW5kIHRoZSBvdGhlciBpcyBtYWtpbmcgdGhlaXIgImRlYnV0IiwgdGhlcmUncyBhIHF1aWNrIGlmZWxzZSB0aGVyZSB0byBoYW5kbGUgaXQNCiAgICAgIGRwbHlyOjptdXRhdGUocHJldl9lbG8gPSBpZmVsc2UoZmFjZW9mZl9wbGF5ZWQgPT0gMSwgMTUwMCwgcHJldl9lbG8pKSAlPiUNCiAgICAgICMgaW4gcmV0cm9zcGVjdCwgSSBkaWRuJ3QgbmVlZCB0aGUgdHdvIHNlcGFyYXRlIHBvcnRpb25zIG9mIHRoZSBsb29wDQogICAgICAjIGJ1dCBzaW5jZSBpdCdzIGFscmVhZHkgd3JpdHRlbiBhbmQgd29ya3MsIEknbSBub3QgY2hhbmdpbmcgaXQNCiAgICAgIGVsbygpICU+JQ0KICAgICAgdW5ncm91cCgpDQogICAgDQogIH0NCiAgIyBzdG9yZSB0aGUgcmVzdWx0cyAoYW5kIHdoaWNoIGZhY2VvZmYgIyBpdCB3YXMpIGluIGEgbGlzdCBzbyB0aGF0IHdlIGNhbiBwdWxsIGl0IGxhdGVyDQogIHJlcyA8LSBmbzEgJT4lDQogICAgZHBseXI6OnNlbGVjdChwbGF5ZXIsIHBvc3RfZWxvLCBmYWNlb2ZmX3BsYXllZCkNCiAgDQogIGxzdFtbaV1dIDwtIHJlcyANCiAgIyBtZXNzYWdlIHRvIHN0YXkgdXAgdG8gZGF0ZSBvbiB3aGVyZSB0aGUgbG9vcCBpcyBwcm9ncmVzcy13aXNlIGJlY2F5c2UgaXQncyBmdW4NCiAgcHJpbnQocGFzdGUwKCJKdXN0IGNvbXBsZXRlZCBmYWNlb2ZmICMiLCBpLCAiIG91dCBvZiAiLCBtYXgoZGF0YSRmYWNlb2ZmX2lkLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICIuIEhhdmUgY29tcGxldGVkICIsIHJvdW5kKChpIC8gbWF4KGRhdGEkZmFjZW9mZl9pZCwgbmEucm0gPSBUUlVFKSkgKiAxMDAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWdpdHMgPSAxKSwgIiUgb2YgdGhlIGZhY2VvZmZzLiIpKQ0KICANCn0NCg0KcmVzdWx0cyA8LSBkcGx5cjo6YmluZF9yb3dzKGxzdCkNCmBgYA0KDQpGaXJzdCwgSSByYW4gdGhlIGxvb3AgaGVyZSBvbiBhbGwgdGhlIDIwMTktMjAyMCBwbGF5LWJ5LXBsYXkgZGF0YSB0aGF0IGlzIGF2YWlsYWJsZSwgcHJvZHVjaW5nIEVsbyBudW1iZXJzIGZvciB0aGF0IHNlYXNvbi4gRm9yIHRoZSAyMDIwLTIwMjEgYW5kIDIwMjEtMjAyMiBzZWFzb25zLCBJIHdhbnRlZCB0byBtYWtlIGVhY2ggcGxheWVycycgc3RhcnRpbmcgcG9pbnQgZm9yIHRoZSBzZWFzb24gdGhlaXIgZW5kaW5nIHJhdGluZyBmcm9tIHRoZSBwcmlvciB5ZWFyLCByYXRoZXIgdGhhbiBzdGFydCB0aGVtIGF0IDE1MDAgYWdhaW4uIERvaW5nIHNvIHJlcXVpcmVkIGEgc2xpZ2h0IHJlLXdyaXRlIG9mIHRoZSBhYm92ZSBsb29wLCBidXQgdGhlIGlkZWEgaXMgdGhlIHNhbWUuIFRoZSBvbmx5IGRpZmZlcmVuY2UgaXMgdGhhdCBmb3IgYSBwbGF5ZXJzJyBmaXJzdCBmYWNlb2ZmIG9mIGEgKm5ldyBzZWFzb24qLCB0aGVpciBkZWZhdWx0IGlzIHRoZWlyIGVuZGluZyB2YWx1ZSBmcm9tIHRoZSBsYXN0IHNlYXNvbiAoYXNzdW1pbmcgdGhleSBoYXZlIHJlY29yZGVkIGEgZmFjZW9mZikuICAgICAgICAgICAgDQoNCioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioNCg0KIyBSZXN1bHRzICAgICAgICAgICAgICAgICAgIA0KDQpJJ2xsIGdpdmUgYSBsaXR0bGUgb3ZlcnZpZXcgb2Ygc29tZSByZXN1bHRzLCBidXQgdGhlIGJlc3QgcGxhY2UgdG8gY2hlY2sgb3V0IHRoZSB1cCB0by1kYXRlIHJlc3VsdHMgd291bGQgYmUgaGVyZSwgb24gdGhlIFtSIFNoaW55IGxlYWRlcmJvYXJkXShodHRwczovL2Jlbmhvd2VsbDcxLnNoaW55YXBwcy5pby9waGYtZmFjZW9mZi1lbG8vKSB0aGF0IEkgdGhyZXcgdG9nZXRoZXIuICAgICAgICAgICAgICAgICANCg0KYGBge3J9DQpkZjEgPC0gcmVhZC5jc3YoInBvc3RfMjAyMF9yZXN1bHRzLmNzdiIpICU+JQ0KICBtdXRhdGUoc2Vhc29uID0gMjAyMCkNCmRmMiA8LSByZWFkLmNzdigicG9zdF8yMDIxX3Jlc3VsdHMuY3N2IikgJT4lDQogIG11dGF0ZShzZWFzb24gPSAyMDIxKQ0KZGYzIDwtIHJlYWQuY3N2KCJwb3N0XzIwMjJfcmVzdWx0cy5jc3YiKSAlPiUNCiAgbXV0YXRlKHNlYXNvbiA9IDIwMjIpDQoNCmRmIDwtIGRwbHlyOjpiaW5kX3Jvd3MoZGYxLCBkZjIsIGRmMykNCg0KbGlicmFyeShyZWFjdGFibGUpDQpsaWJyYXJ5KHJlYWN0YWJsZWZtdHIpDQoNCm1ha2VfY29sb3JfcGFsIDwtIGZ1bmN0aW9uKGNvbG9ycywgYmlhcyA9IDEpIHsNCiAgICBnZXRfY29sb3IgPC0gY29sb3JSYW1wKGNvbG9ycywgYmlhcyA9IGJpYXMpDQogICAgZnVuY3Rpb24oeCkgcmdiKGdldF9jb2xvcih4KSwgbWF4Q29sb3JWYWx1ZSA9IDI1NSkNCn0NCg0KZ29vZF9jb2xvciA8LSBtYWtlX2NvbG9yX3BhbChjKCIjQ0EwQ0UxIiwgIiNGOEY5RjgiLCAiIzE2Q0EzMSIpKQ0KYGBgDQoNClNvbWV0aGluZyB0aGF0IEknbSBpbnRlcmVzdGVkIGluIGZvbGxvd2luZyBvdmVyIHRoZSBuZXh0IGZldyB5ZWFycyBpcyB3aGV0aGVyIGFueW9uZSBjYW4gYXBwcm9hY2ggYm90aCB0aGUgZG9taW5hbmNlIGFuZCBsb25nZXZpdHkgdGhhdCBEZW1wc2V5IGhhcyBwb3N0ZWQgYXMgYSBmYWNlb2ZmIGFydGlzdC4gRnJvbSB0aGUgZ2FtZXMgdGhhdCB3ZSBoYXZlIHBsYXktYnktcGxheSBkYXRhIG9uLCB0aGUgYmVnaW5uaW5nIG9mIERlbXBzZXkncyBydW4gd2FzIGEgbGl0dGxlIGJpdCBidW1weSwgZXZlbiB0cmVuZGluZyBiZWxvdy1hdmVyYWdlIGF0IHRpbWVzLCBidXQgc2luY2UgYWJvdXQgaGVyIDc1dGggZmFjZW9mZiwgc2hlJ3MgYmVlbiBuZWFybHkgdW5iZWF0YWJsZSBvbiBhIGNvbnNpc3RlbnQgYmFzaXMuICAgICAgICAgICAgICAgIA0KDQpNaWt5bGEgR3JhbnQtTWVudGlzIGFuZCBTaGlhbm4gRGFya2FuZ2VsbyBib3RoIGJlZ2FuIHRoZWlyIGNhcmVlcnMgbW9yZSBlZmZpY2llbnRseSB0aGFuIERlbXBzZXkgZGlkLCBidXQgaGF2ZSBzbG93ZWQgZG93biBhcyB0aGV5IGNyb3NzZWQgdGhlIDEwMC1jYXJlZXIgZmFjZW9mZiB0aHJlc2hvbGQuIFRoZSBwbGF5ZXIgd2hvIHBvcHMgdGhlIG1vc3QgaXMgYSBmb3JtZXIgdGVhbW1hdGUgb2YgRGVtcHNleSdzLCBUZXJlemEgVmFuaXNvemEsIHdobyBhY2N1bXVsYXRlZCBhIDE5NTIgRWxvIHJhdGluZyBpbiBqdXN0IDExNiBQSEYgZmFjZW9mZnMuIFRoYXQncyBpbnNhbmUgZWZmaWNpZW5jeSwgZXNwZWNpYWxseSBhcyBhIHJvb2tpZSBpbiB0aGUgUEhGLiBHaXZlbiB0aGF0IHNoZSdzIG9ubHkgMjUgYW5kIHNvIGdvb2QsIFZhbmlzb3ZhIHdvdWxkIGJlIGEgcGhlbm9tZW5hbCBiZXQgdG8gY2hhbGxlbmdlIGZvciBEZW1wc2V5J3MgY3Jvd24sIGJ1dCB3ZSBtYXkgbm90IHNlZSB0aGF0IGhhcHBlbiBhcyBzaGUncyBtb3ZlZCB0byB0aGUgU0RITCBmb3IgMjAyMS0yMDIyLg0KDQpgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0NCmxpYnJhcnkoZ2d0aGVtZXMpDQoNCnBwbCA8LSBjKCJKaWxsaWFuIERlbXBzZXkiLCAiTWlreWxhIEdyYW50LU1lbnRpcyIsIA0KICAgICAgICAgIlNoaWFubiBEYXJrYW5nZWxvIiwgIlRlcmV6YSBWYW5pc292YSIpDQoNCiMgZmluYWxpemVkIDwtIGRmICU+JQ0KIyAgIGdyb3VwX2J5KHBsYXllcikgJT4lDQojICAgZHBseXI6OmZpbHRlcihmYWNlb2ZmX3BsYXllZCA9PSBtYXgoZmFjZW9mZl9wbGF5ZWQsIG5hLnJtID0gVFJVRSkpDQoNCmRmICU+JQ0KICAjIGZpbHRlcihwbGF5ZXIgJWluJSBwcGwpICU+JQ0KICAjIGZpbHRlcihmYWNlb2ZmX3BsYXllZCA+PSA1MCkgJT4lDQogIGdncGxvdCgpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMTUwMCkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSBmYWNlb2ZmX3BsYXllZCwgeSA9IHBvc3RfZWxvLCBncm91cCA9IHBsYXllciksDQogICAgICAgICAgICBjb2xvciA9ICJncmV5ODQiLCBzaXplID0gMSkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGRmICU+JQ0KICAgICAgICAgICAgICBmaWx0ZXIocGxheWVyICVpbiUgcHBsKSwgDQogICAgICAgICAgICBhZXMoeCA9IGZhY2VvZmZfcGxheWVkLCB5ID0gcG9zdF9lbG8sDQogICAgICAgICAgICAgICAgY29sb3IgPSBwbGF5ZXIpLCBzaXplID0gMikgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCA4NTApLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDg1MCwgYnkgPSAxMDApKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDEwMDAsIDI1MDApLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDEwMDAsIDI1MDAsIGJ5ID0gMjUwKSkgKw0KICBsYWJzKHggPSAiQ2FyZWVyIFBIRiBGYWNlb2ZmcyIsDQogICAgICAgeSA9ICJFbG8gUmF0aW5nIiwNCiAgICAgICB0aXRsZSA9ICJGYWNlb2ZmIEVsbyBSYXRpbmcgYnkgQ2FyZWVyIEZhY2VvZmYiLA0KICAgICAgIHN1YnRpdGxlID0gIkNoYXNpbmcgRGVtcHNleTogQ2FuIGFueW9uZSBkbyBpdD8iLA0KICAgICAgIGNhcHRpb24gPSAiQmVuIEhvd2VsbCB8IEBiZW5ob3dlbGw3MSB8IERhdGEgZnJvbSBmYXN0UmhvY2tleSIpICsgDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJibGFjayIsIHNpemUgPSAxKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwNCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIsIHNpemUgPSAxMiwgaGp1c3QgPSAwLjUpLA0KICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoZmFjZSA9ICJpdGFsaWMiLCBzaXplID0gMTAsIGhqdXN0ID0gMC41KSwNCiAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiaXZvcnkiLCBjb2xvdXIgPSAiaXZvcnkiKSkgKw0KICBzY2FsZV9jb2xvcl9jb2xvcmJsaW5kKCkNCmBgYA0KDQoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioNCg0KQmV5b25kIHRoZXNlIHJvbGxpbmcgZ3JhcGhzLCBJJ3ZlIGluY2x1ZGVkIGEgdGFibGUgb2YgRmFjZW9mZiBFbG8gUmF0aW5ncyBmb3IgdGhlIGZpcnN0IGZldyB3ZWVrcyBvZiB0aGUgMjAyMS0yMDIyIFBIRiBzZWFzb24uIERlbXBzZXkncyBnYXAgb3ZlciB0aGUgcmVzdCBvZiB0aGUgZmllbGQgaXMgYWJzdXJkOyBzaGUncyBvbiBoZXIgb3duIHNjYWxlLCB0byBiZSBob25lc3QuICAgICAgICAgDQoNCk1hbnkgb2YgdGhlIG5hbWVzIGF0IHRoZSB0b3Agb2YgdGhlIHRhYmxlIGhhdmUgYWNjdW11bGF0ZWQgYSB0b24gb2YgZmFjZW9mZiBvcHBvcnR1bml0aWVzIG92ZXIgdGhlaXIgY2FyZWVyLCBidXQgdGhlcmUgYXJlIHNvbWUgcmVsYXRpdmUgbmV3Y29tZXJzIG1ha2luZyBhbiBhcHBlYXJhbmNlIGluIHRoZSB0b3AgMTAuICAgICAgICAgICAgICAgICAgDQoNClNoaWFubiBEYXJrYW5nZWxvLCBNaWt5bGEgR3JhbnQtTWVudGlzLCBhbmQgTWVnaGFuIExvcmVuY2UgYXJlIHRob3NlIG5vdGFibGUgbmFtZXMgdG8gbWU7IGl0J2xsIGJlIGFsbW9zdCB1bmZhaXIgaWYgTUdNIGJlY29tZXMgYW4gZXhjZWxsZW50IGZhY2VvZmYgYXJ0aXN0IG9uIHRvcCBvZiBldmVyeXRoaW5nIGVsc2UgdGhhdCBzaGUgZG9lcyAoTWlrZSBzYWlkIGFzIG11Y2ggaW4gYSBbcmVjZW50IHBpZWNlIGRpc2N1c3NpbmcgUEhGIGZhbnRhc3kgdGVhbS1idWlsZGluZyB0cmVuZHNdKGh0dHBzOi8vd3d3LnRoZWljZWdhcmRlbi5jb20vMjAyMS8xMS8xNS8yMjc3NTM5Mi90aWdwaGZmYW50YXN5LTIwMjEtMjItdGVhbS1idWlsZGluZy10cmVuZHMtcGhmLWZhbnRhc3ktaG9ja2V5KSkuICAgICAgICAgICAgIA0KDQoqKEFnYWluLCB3b3VsZCByZWNvbW1lbmQgdXNpbmcgbXkgW1IgU2hpbnkgbGVhZGVyYm9hcmRdKGh0dHBzOi8vYmVuaG93ZWxsNzEuc2hpbnlhcHBzLmlvL3BoZi1mYWNlb2ZmLWVsby8pIHRvIHZpZXcgdGhlIGZ1bGwgcmVzdWx0cyAob3ZlciBtdWx0aXBsZSBzZWFzb25zKSBhbmQgUEhGIEVsbyBSYXRpbmdzICsgYSBmZXcgb3RoZXIgdGhpbmdzIEkgdGhyZXcgaW4pKg0KDQpgYGB7cn0NCmRmICU+JQ0KICBkcGx5cjo6ZmlsdGVyKHNlYXNvbiA9PSAyMDIyKSAlPiUNCiAgZ3JvdXBfYnkocGxheWVyKSAlPiUNCiAgZmlsdGVyKGZhY2VvZmZfcGxheWVkID09IG1heChmYWNlb2ZmX3BsYXllZCwgbmEucm0gPSBUUlVFKSAmDQogICAgICAgICAgICEgaXMubmEocG9zdF9lbG8pKSAlPiUNCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhwb3N0X2VsbyksIGZhY2VvZmZfcGxheWVkKSAlPiUNCiAgZHBseXI6Om11dGF0ZShmYWNlb2ZmX2VsbyA9IHJvdW5kKHBvc3RfZWxvLCBkaWdpdHMgPSAwKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QocGxheWVyLCBmYWNlb2ZmX3BsYXllZCwgZmFjZW9mZl9lbG8pICU+JQ0KICAjIGhlYWQoOCkgJT4lDQogIHJlYWN0YWJsZShkZWZhdWx0UGFnZVNpemUgPSAyNSwNCiAgICAgICAgICAgIHN0cmlwZWQgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICNib3JkZXJlZCA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdENvbERlZiA9IGNvbERlZigNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAiY2VudGVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbWluV2lkdGggPSA1MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyQ2xhc3MgPSAiaGVhZGVyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc29ydE5BTGFzdCA9IFRSVUUsIGhlYWRlclN0eWxlID0gbGlzdChmb250V2VpZ2h0ID0gNzAwKQ0KICAgICAgICAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICAgICAgICAgdGhlbWUgPSByZWFjdGFibGVUaGVtZSgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhZGVyU3R5bGUgPSBsaXN0KA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiY6aG92ZXJbYXJpYS1zb3J0XSIgPSBsaXN0KGJhY2tncm91bmQgPSAiaHNsKDAsIDAlLCA5NiUpIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiJlthcmlhLXNvcnQ9J2FzY2VuZGluZyddLCAmW2FyaWEtc29ydD0nZGVzY2VuZGluZyddIiA9IGxpc3QoYmFja2dyb3VuZCA9ICJoc2woMCwgMCUsIDk2JSkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yID0gIiM1NTUiDQogICAgICAgICAgICAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlckNvbG9yID0gIiNkZmUyZTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpcGVkQ29sb3IgPSAiI2Y2ZjhmYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2hsaWdodENvbG9yID0gIiNmMGY1ZjkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsUGFkZGluZyA9ICI4cHggMTJweCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN0eWxlID0gbGlzdChmb250RmFtaWx5ID0gIi1hcHBsZS1zeXN0ZW0sIEJsaW5rTWFjU3lzdGVtRm9udCwgU2Vnb2UgVUksIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWYiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2VhcmNoSW5wdXRTdHlsZSA9IGxpc3Qod2lkdGggPSAiMTAwJSIpDQogICAgICAgICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgIGNvbHVtbnMgPSBsaXN0KA0KICAgICAgICAgICAgICBwbGF5ZXIgPSBjb2xEZWYoDQogICAgICAgICAgICAgICAgbmFtZSA9ICJQbGF5ZXIiDQogICAgICAgICAgICAgICksDQogICAgICAgICAgICAgIGZhY2VvZmZfcGxheWVkID0gY29sRGVmKA0KICAgICAgICAgICAgICAgIG5hbWUgPSAiQ2FyZWVyIFBIRiBGYWNlb2ZmcyINCiAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgZmFjZW9mZl9lbG8gPSBjb2xEZWYoDQogICAgICAgICAgICAgICAgbmFtZSA9ICJGYWNlb2ZmIEVsbyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHlsZSA9IGZ1bmN0aW9uKHZhbHVlKSB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemVkIDwtICh2YWx1ZSAtIDEyNTApIC8gKDI1MDAgLSAxMjUwKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yIDwtIGdvb2RfY29sb3Iobm9ybWFsaXplZCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KGJhY2tncm91bmQgPSBjb2xvcikNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgKSkgDQpgYGANCg0KDQoNCg==