How This Model Works

If you want to model sports, you need two things: a way to measure the goodness of teams, and a way to turn that goodness into an estimate of their winningness.

At its core, our NHL projections are derived from the same basic methodology we’ve been using for modelling soccer for several years. Ice hockey is a simple game, scored in increments of one that are relatively rare, and it’s quite reasonable to model that with a Poisson distribution with a mean around 3 - teams usually score a couple of goals a game, sometimes they don’t score any, sometimes they score a lot; a Poisson distribution means all of these happen with regularity. (In a hypothetical simulated season for 2024/25, we didn’t get any 0-0 games (compare with three from 2023/24), and no team scored ten in a game (again, three last season), so if anything this is maybe a little narrow compared to the real world.)

But to get results from a Poisson process, we need an expected number of occurrences - one where, if you repeat the process enough times, your average result would be that number. And because the 32 teams in the NHL are different to each other, and almost every game is played at an arena with some level of home-ice advantage, that mean is going to be different for each team in each game.

Our model changes that mean by, in short, comparing a team’s expected performance across the season to its actual performance, measuring that expectation by whom it has and hasn’t played. We start this off by taking a sample of games played, and using that as our dataset. We tally up the number of games played between each pair of teams, and the total goals for and against by each team,1 and use that for an important calculation: how many goals each team should have scored and conceded based on their opponents.

To illustrate the point: assume Philadelphia score an average of 3.2 goals per game, and concede an average of 2.6, across their entire season. On the other hand, Pittsburgh score at a rate 2.8/game and concede at a rate of 2.4. If the two teams played each other four times (two at each venue, to cancel out home advantage), our initial expectation would be for the teams to score and concede at halfway between their two averages - the Flyers would score 2.8 goals per game across the four matches (the median of their regular 3.2 offence and the Penguins’ regular 2.4 defense), and the Penguins would score 2.7.

If, over the sum of those four games, the Flyers scored a total of 12 goals and the Penguins scored 10, the former would have overperformed and the latter would have underperformed. Our intuition from this is that Philadelphia are slightly better than our hypothetical average team, and the Penguins are slightly worse. The model is basically doing that across an entire season, with all 32 teams involved, and aiming to counteract the effects of the innately uneven schedule.

Once we’ve done all that, each team ends up with a rating for both offense and defense, expressed relative to the average:

Team GF xGF FDiff GA xGA ADiff TDiff
1 Florida 329 322.55 0.06 254 288.75 -0.33 0.39
2 Edmonton 368 339.86 0.26 295 307.78 -0.12 0.38
3 Carolina 309 294.77 0.15 238 257.25 -0.21 0.36
4 Dallas 336 319.28 0.17 271 288.52 -0.17 0.34
5 Colorado 338 307.11 0.33 283 279.59 0.04 0.29
28 Montreal 226 235.67 -0.12 273 259.75 0.16 -0.28
29 Columbus 231 238.09 -0.09 288 267.06 0.26 -0.34
30 Anaheim 198 221.64 -0.29 290 267.24 0.28 -0.57
31 Chicago 173 208.91 -0.44 284 265.54 0.23 -0.66
32 San Jose 176 209.79 -0.41 322 283.66 0.47 -0.88

The interpretation of this is that, if Florida played a hypothetical average strength team, they would score (on average) 0.06 goals more per game than that average team normally conceded, and concede 0.33 fewer. On the flipside, any of the three teams with very poor ratings would be expected to lose by quite a margin.

All in all, we use these differences to adjust the expected score for each future matchup. Take the first North American game for 2024/25, St. Louis at Seattle. We start off expecting the Kraken to score the average home score from 2023/24 (3.10), reduce that score based on Seattle’s lower-than-league-standard offence (-0.23), and then very slightly adjust it based on St. Louis’ defense (-0.0017) to get the expected tally, 2.86. A similar process gives us the Blues’ expected score, 2.64.

We then generate an simulated score for this game by throwing both teams into their own Poisson process and seeing what number comes out (in our one simulated season we’re referring to here, St. Louis won this game 3-1). That’s what we do for the other 1,311 games through the season, working out how many points each team gets from the game, and then we add those results into a table - Florida come out on top in this scenario, gathering 107 points across the year; while Anaheim loiter at the bottom with 59. (This is not to say that this is definitely what’s happening this year - we run 10,000 different simulations of the season, and this is just the result that we get in one of them.)

Given that tally of the standings, we’re able to come up with our own playoff bracket, running through a new function to predict a seven-game playoff season that outputs a winner - cascading through it until our virtual Stanley Cup champions are crowned.

What This Model Says

If we run through 10,000 possible ways this upcoming NHL season could pan out, what results happen regularly? Who’s got a good chance to taking out the Stanley Cup, or who should be content with merely making the playoffs?

Team Conf/Div Points Win Div Win PT Playoffs 2nd Rd Conf Final Win Conf Win Cup
Florida E / ATL 99.7 38.6% 12.5% 88.8% 54.6% 32.9% 19.1% 10.5%
Carolina E / MET 98.9 44.7% 10.6% 88.2% 55.5% 33.3% 18.3% 10.1%
Edmonton W / PAC 99.7 37.4% 12.3% 89.2% 53.2% 30.7% 17.2% 9.7%
Dallas W / CEN 98.7 33.4% 10.5% 85.3% 49.2% 27.1% 14.7% 7.9%
Vancouver W / PAC 97.9 28.0% 8.5% 84.8% 47.6% 24.8% 13.6% 7.3%
Colorado W / CEN 97.0 25.6% 7.1% 81.0% 44.7% 24.1% 12.4% 6.7%
NY Rangers E / MET 95.7 27.8% 5.4% 79.1% 46.0% 24.7% 12.8% 6.7%
Boston E / ATL 96.2 22.5% 5.6% 78.9% 43.8% 23.4% 12.6% 6.6%
Winnipeg W / CEN 96.2 21.5% 5.9% 78.7% 42.1% 22.0% 10.7% 5.5%
Los Angeles W / PAC 94.9 17.5% 4.5% 74.5% 36.8% 18.2% 9.1% 4.5%
Toronto E / ATL 93.6 14.6% 3.5% 68.8% 35.3% 17.3% 8.6% 3.9%
Tampa Bay E / ATL 91.4 9.5% 2.0% 59.6% 28.0% 12.8% 6.3% 2.8%
Vegas W / PAC 91.8 10.2% 2.2% 61.4% 28.6% 13.3% 6.0% 2.8%
Nashville W / CEN 91.8 9.9% 2.0% 60.5% 27.5% 12.8% 5.7% 2.6%
Pittsburgh E / MET 90.2 10.7% 1.4% 56.0% 26.8% 11.9% 5.3% 2.2%
Buffalo E / ATL 89.1 6.2% 1.0% 48.8% 20.7% 9.0% 3.8% 1.5%
Detroit E / ATL 88.5 5.6% 1.0% 45.3% 18.5% 7.7% 3.1% 1.3%
Minnesota W / CEN 87.6 4.0% 0.6% 40.7% 16.4% 6.6% 2.8% 1.1%
NY Islanders E / MET 86.5 5.2% 0.5% 39.1% 16.3% 6.5% 2.7% 0.9%
Seattle W / PAC 86.8 3.7% 0.6% 37.5% 14.7% 5.7% 2.1% 0.9%
Calgary W / PAC 86.3 3.1% 0.4% 35.9% 13.3% 5.2% 2.1% 0.9%
Philadelphia E / MET 85.4 3.9% 0.3% 34.0% 13.4% 5.1% 2.0% 0.7%
St. Louis W / CEN 85.8 3.0% 0.4% 32.7% 12.4% 5.0% 1.9% 0.7%
Utah W / CEN 85.6 2.7% 0.3% 32.1% 12.2% 4.4% 1.7% 0.7%
New Jersey E / MET 85.2 4.4% 0.3% 33.0% 13.2% 5.2% 1.9% 0.6%
Washington E / MET 82.8 2.3% 0.3% 23.5% 8.5% 3.1% 1.2% 0.5%
Ottawa E / ATL 83.6 1.9% 0.2% 24.8% 8.3% 3.2% 1.2% 0.3%
Montreal E / ATL 81.5 1.1% 0.1% 17.5% 5.9% 2.1% 0.7% 0.2%
Columbus E / MET 79.9 1.0% 0.0% 14.7% 5.1% 1.7% 0.5% 0.1%
Anaheim W / PAC 73.8 0.1% 0.0% 3.9% 1.0% 0.2% 0.1% 0.0%
Chicago W / CEN 71.2 0.1% 0.0% 1.6% 0.3% 0.1% 0.0% 0.0%
San Jose W / PAC 65.6 0.0% 0.0% 0.3% 0.0% 0.0% 0.0% 0.0%

How shocking: the team that won last season is the favourite to win again this season. Of course, since we don’t have a player-based model like some of the experts in the field do, we can only go off the results of the jersey, and we will be gradually shifting towards taking influence from this season’s results rather than last season’s (by the time every team’s played 20 games, it’ll be 100% based on 2024/25 results).

As a last thought, we’ll leave you with the most disgusting code we’ve ever had to write (a vague attempt at working out the “cutoff point” to get a wildcard playoff spot in each conference):

EWCcutoff <- append(EWCcutoff,
                    ((max(nhl2425_table$Pts[which(nhl2425_table$Conference == "E" & nhl2425_table$Playoffs == FALSE)]))
                     + nhl2425_table$Pts[which(nhl2425_table$Conference == "E"
                                               & nhl2425_table$ConfRank
                                               == (nhl2425_table$ConfRank[match(nhl2425_table$Team[which(nhl2425_table$Pts
                                                                                                         == (max(nhl2425_table$Pts[which(nhl2425_table$Conference == "E"
                                                                                                                                         & nhl2425_table$Playoffs == FALSE)]))
                                                                                                         & nhl2425_table$Conference == "E")][1],nhl2425_table$Team)])-1)]) /2)

Hideous.

If you want to see how this all ends up for the rest of the season, I’ll be posting updates on my Twitter (and yes, it is still Twitter, regardless of what racist billionaires want to call it).


  1. After we’ve taken out any goals from overtime and shootouts - the expectation behind our Poisson mean is that it’s the average number of goals scored across 60 minutes of gametime, which is broken when overtime comes into the equation. (We also believe that if you’re tied after three periods, five minutes of three-on-three is basically a coin toss.)↩︎

LS0tDQp0aXRsZTogIk5ITCAyMDI0LzI1IFByb2plY3Rpb25zIg0KYXV0aG9yOiAiQ2xhaXJlIEZyZWRyaWtzc29uIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyMgSG93IFRoaXMgTW9kZWwgV29ya3MNCg0KSWYgeW91IHdhbnQgdG8gbW9kZWwgc3BvcnRzLCB5b3UgbmVlZCB0d28gdGhpbmdzOiBhIHdheSB0byBtZWFzdXJlIHRoZSBnb29kbmVzcyBvZiB0ZWFtcywgYW5kIGEgd2F5IHRvIHR1cm4gdGhhdCBnb29kbmVzcyBpbnRvIGFuIGVzdGltYXRlIG9mIHRoZWlyIHdpbm5pbmduZXNzLg0KDQpBdCBpdHMgY29yZSwgb3VyIE5ITCBwcm9qZWN0aW9ucyBhcmUgZGVyaXZlZCBmcm9tIHRoZSBzYW1lIGJhc2ljIG1ldGhvZG9sb2d5IHdlJ3ZlIGJlZW4gdXNpbmcgZm9yIG1vZGVsbGluZyBzb2NjZXIgZm9yIHNldmVyYWwgeWVhcnMuIEljZSBob2NrZXkgaXMgYSBzaW1wbGUgZ2FtZSwgc2NvcmVkIGluIGluY3JlbWVudHMgb2Ygb25lIHRoYXQgYXJlIHJlbGF0aXZlbHkgcmFyZSwgYW5kIGl0J3MgcXVpdGUgcmVhc29uYWJsZSB0byBtb2RlbCB0aGF0IHdpdGggYSBQb2lzc29uIGRpc3RyaWJ1dGlvbiB3aXRoIGEgbWVhbiBhcm91bmQgMyAtIHRlYW1zIHVzdWFsbHkgc2NvcmUgYSBjb3VwbGUgb2YgZ29hbHMgYSBnYW1lLCBzb21ldGltZXMgdGhleSBkb24ndCBzY29yZSBhbnksIHNvbWV0aW1lcyB0aGV5IHNjb3JlIGEgbG90OyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uIG1lYW5zIGFsbCBvZiB0aGVzZSBoYXBwZW4gd2l0aCByZWd1bGFyaXR5LiAoSW4gYSBoeXBvdGhldGljYWwgc2ltdWxhdGVkIHNlYXNvbiBmb3IgMjAyNC8yNSwgd2UgZGlkbid0IGdldCBhbnkgMC0wIGdhbWVzIChjb21wYXJlIHdpdGggdGhyZWUgZnJvbSAyMDIzLzI0KSwgYW5kIG5vIHRlYW0gc2NvcmVkIHRlbiBpbiBhIGdhbWUgKGFnYWluLCB0aHJlZSBsYXN0IHNlYXNvbiksIHNvIGlmIGFueXRoaW5nIHRoaXMgaXMgbWF5YmUgYSBsaXR0bGUgbmFycm93IGNvbXBhcmVkIHRvIHRoZSByZWFsIHdvcmxkLikNCg0KQnV0IHRvIGdldCByZXN1bHRzIGZyb20gYSBQb2lzc29uIHByb2Nlc3MsIHdlIG5lZWQgYW4gZXhwZWN0ZWQgbnVtYmVyIG9mIG9jY3VycmVuY2VzIC0gb25lIHdoZXJlLCBpZiB5b3UgcmVwZWF0IHRoZSBwcm9jZXNzIGVub3VnaCB0aW1lcywgeW91ciBhdmVyYWdlIHJlc3VsdCB3b3VsZCBiZSB0aGF0IG51bWJlci4gQW5kIGJlY2F1c2UgdGhlIDMyIHRlYW1zIGluIHRoZSBOSEwgYXJlIGRpZmZlcmVudCB0byBlYWNoIG90aGVyLCBhbmQgYWxtb3N0IGV2ZXJ5IGdhbWUgaXMgcGxheWVkIGF0IGFuIGFyZW5hIHdpdGggc29tZSBsZXZlbCBvZiBob21lLWljZSBhZHZhbnRhZ2UsIHRoYXQgbWVhbiBpcyBnb2luZyB0byBiZSBkaWZmZXJlbnQgZm9yIGVhY2ggdGVhbSBpbiBlYWNoIGdhbWUuDQoNCk91ciBtb2RlbCBjaGFuZ2VzIHRoYXQgbWVhbiBieSwgaW4gc2hvcnQsIGNvbXBhcmluZyBhIHRlYW0ncyAqZXhwZWN0ZWQqIHBlcmZvcm1hbmNlIGFjcm9zcyB0aGUgc2Vhc29uIHRvIGl0cyAqYWN0dWFsKiBwZXJmb3JtYW5jZSwgbWVhc3VyaW5nIHRoYXQgZXhwZWN0YXRpb24gYnkgd2hvbSBpdCBoYXMgYW5kIGhhc24ndCBwbGF5ZWQuIFdlIHN0YXJ0IHRoaXMgb2ZmIGJ5IHRha2luZyBhIHNhbXBsZSBvZiBnYW1lcyBwbGF5ZWQsIGFuZCB1c2luZyB0aGF0IGFzIG91ciBkYXRhc2V0LiBXZSB0YWxseSB1cCB0aGUgbnVtYmVyIG9mIGdhbWVzIHBsYXllZCBiZXR3ZWVuIGVhY2ggcGFpciBvZiB0ZWFtcywgYW5kIHRoZSB0b3RhbCBnb2FscyBmb3IgYW5kIGFnYWluc3QgYnkgZWFjaCB0ZWFtLFteMV0gYW5kIHVzZSB0aGF0IGZvciBhbiBpbXBvcnRhbnQgY2FsY3VsYXRpb246IGhvdyBtYW55IGdvYWxzIGVhY2ggdGVhbSAqc2hvdWxkKiBoYXZlIHNjb3JlZCBhbmQgY29uY2VkZWQgYmFzZWQgb24gdGhlaXIgb3Bwb25lbnRzLg0KDQpbXjFdOiBBZnRlciB3ZSd2ZSB0YWtlbiBvdXQgYW55IGdvYWxzIGZyb20gb3ZlcnRpbWUgYW5kIHNob290b3V0cyAtIHRoZSBleHBlY3RhdGlvbiBiZWhpbmQgb3VyIFBvaXNzb24gbWVhbiBpcyB0aGF0IGl0J3MgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGdvYWxzIHNjb3JlZCAqYWNyb3NzIDYwIG1pbnV0ZXMgb2YgZ2FtZXRpbWUqLCB3aGljaCBpcyBicm9rZW4gd2hlbiBvdmVydGltZSBjb21lcyBpbnRvIHRoZSBlcXVhdGlvbi4gKFdlIGFsc28gYmVsaWV2ZSB0aGF0IGlmIHlvdSdyZSB0aWVkIGFmdGVyIHRocmVlIHBlcmlvZHMsIGZpdmUgbWludXRlcyBvZiB0aHJlZS1vbi10aHJlZSBpcyBiYXNpY2FsbHkgYSBjb2luIHRvc3MuKQ0KDQpUbyBpbGx1c3RyYXRlIHRoZSBwb2ludDogYXNzdW1lIFBoaWxhZGVscGhpYSBzY29yZSBhbiBhdmVyYWdlIG9mIDMuMiBnb2FscyBwZXIgZ2FtZSwgYW5kIGNvbmNlZGUgYW4gYXZlcmFnZSBvZiAyLjYsIGFjcm9zcyB0aGVpciBlbnRpcmUgc2Vhc29uLiBPbiB0aGUgb3RoZXIgaGFuZCwgUGl0dHNidXJnaCBzY29yZSBhdCBhIHJhdGUgMi44L2dhbWUgYW5kIGNvbmNlZGUgYXQgYSByYXRlIG9mIDIuNC4gSWYgdGhlIHR3byB0ZWFtcyBwbGF5ZWQgZWFjaCBvdGhlciBmb3VyIHRpbWVzICh0d28gYXQgZWFjaCB2ZW51ZSwgdG8gY2FuY2VsIG91dCBob21lIGFkdmFudGFnZSksIG91ciBpbml0aWFsIGV4cGVjdGF0aW9uIHdvdWxkIGJlIGZvciB0aGUgdGVhbXMgdG8gc2NvcmUgYW5kIGNvbmNlZGUgYXQgaGFsZndheSBiZXR3ZWVuIHRoZWlyIHR3byBhdmVyYWdlcyAtIHRoZSBGbHllcnMgd291bGQgc2NvcmUgMi44IGdvYWxzIHBlciBnYW1lIGFjcm9zcyB0aGUgZm91ciBtYXRjaGVzICh0aGUgbWVkaWFuIG9mIHRoZWlyIHJlZ3VsYXIgMy4yIG9mZmVuY2UgYW5kIHRoZSBQZW5ndWlucycgcmVndWxhciAyLjQgZGVmZW5zZSksIGFuZCB0aGUgUGVuZ3VpbnMgd291bGQgc2NvcmUgMi43Lg0KDQpJZiwgb3ZlciB0aGUgc3VtIG9mIHRob3NlIGZvdXIgZ2FtZXMsIHRoZSBGbHllcnMgc2NvcmVkIGEgdG90YWwgb2YgMTIgZ29hbHMgYW5kIHRoZSBQZW5ndWlucyBzY29yZWQgMTAsIHRoZSBmb3JtZXIgd291bGQgaGF2ZSBvdmVycGVyZm9ybWVkIGFuZCB0aGUgbGF0dGVyIHdvdWxkIGhhdmUgdW5kZXJwZXJmb3JtZWQuIE91ciBpbnR1aXRpb24gZnJvbSB0aGlzIGlzIHRoYXQgUGhpbGFkZWxwaGlhIGFyZSBzbGlnaHRseSBiZXR0ZXIgdGhhbiBvdXIgaHlwb3RoZXRpY2FsIGF2ZXJhZ2UgdGVhbSwgYW5kIHRoZSBQZW5ndWlucyBhcmUgc2xpZ2h0bHkgd29yc2UuIFRoZSBtb2RlbCBpcyBiYXNpY2FsbHkgZG9pbmcgdGhhdCBhY3Jvc3MgYW4gZW50aXJlIHNlYXNvbiwgd2l0aCBhbGwgMzIgdGVhbXMgaW52b2x2ZWQsIGFuZCBhaW1pbmcgdG8gY291bnRlcmFjdCB0aGUgZWZmZWN0cyBvZiB0aGUgaW5uYXRlbHkgdW5ldmVuIHNjaGVkdWxlLg0KDQpPbmNlIHdlJ3ZlIGRvbmUgYWxsIHRoYXQsIGVhY2ggdGVhbSBlbmRzIHVwIHdpdGggYSByYXRpbmcgZm9yIGJvdGggb2ZmZW5zZSBhbmQgZGVmZW5zZSwgZXhwcmVzc2VkIHJlbGF0aXZlIHRvIHRoZSBhdmVyYWdlOg0KDQp8ICAgICB8IFRlYW0gICAgIHwgR0YgIHwgeEdGICAgIHwgRkRpZmYgfCBHQSAgfCB4R0EgICAgfCBBRGlmZiB8IFREaWZmIHwNCnwtLS0tLXwtLS0tLS0tLS0tfC0tLS0tfC0tLS0tLS0tfC0tLS0tLS18LS0tLS18LS0tLS0tLS18LS0tLS0tLXwtLS0tLS0tfA0KfCAxICAgfCBGbG9yaWRhICB8IDMyOSB8IDMyMi41NSB8IDAuMDYgIHwgMjU0IHwgMjg4Ljc1IHwgLTAuMzMgfCAwLjM5ICB8DQp8IDIgICB8IEVkbW9udG9uIHwgMzY4IHwgMzM5Ljg2IHwgMC4yNiAgfCAyOTUgfCAzMDcuNzggfCAtMC4xMiB8IDAuMzggIHwNCnwgMyAgIHwgQ2Fyb2xpbmEgfCAzMDkgfCAyOTQuNzcgfCAwLjE1ICB8IDIzOCB8IDI1Ny4yNSB8IC0wLjIxIHwgMC4zNiAgfA0KfCA0ICAgfCBEYWxsYXMgICB8IDMzNiB8IDMxOS4yOCB8IDAuMTcgIHwgMjcxIHwgMjg4LjUyIHwgLTAuMTcgfCAwLjM0ICB8DQp8IDUgICB8IENvbG9yYWRvIHwgMzM4IHwgMzA3LjExIHwgMC4zMyAgfCAyODMgfCAyNzkuNTkgfCAwLjA0ICB8IDAuMjkgIHwNCnwgLi4uIHwgICAgICAgICAgfCAgICAgfCAgICAgICAgfCAgICAgICB8ICAgICB8ICAgICAgICB8ICAgICAgIHwgICAgICAgfA0KfCAyOCAgfCBNb250cmVhbCB8IDIyNiB8IDIzNS42NyB8IC0wLjEyIHwgMjczIHwgMjU5Ljc1IHwgMC4xNiAgfCAtMC4yOCB8DQp8IDI5ICB8IENvbHVtYnVzIHwgMjMxIHwgMjM4LjA5IHwgLTAuMDkgfCAyODggfCAyNjcuMDYgfCAwLjI2ICB8IC0wLjM0IHwNCnwgMzAgIHwgQW5haGVpbSAgfCAxOTggfCAyMjEuNjQgfCAtMC4yOSB8IDI5MCB8IDI2Ny4yNCB8IDAuMjggIHwgLTAuNTcgfA0KfCAzMSAgfCBDaGljYWdvICB8IDE3MyB8IDIwOC45MSB8IC0wLjQ0IHwgMjg0IHwgMjY1LjU0IHwgMC4yMyAgfCAtMC42NiB8DQp8IDMyICB8IFNhbiBKb3NlIHwgMTc2IHwgMjA5Ljc5IHwgLTAuNDEgfCAzMjIgfCAyODMuNjYgfCAwLjQ3ICB8IC0wLjg4IHwNCg0KVGhlIGludGVycHJldGF0aW9uIG9mIHRoaXMgaXMgdGhhdCwgaWYgRmxvcmlkYSBwbGF5ZWQgYSBoeXBvdGhldGljYWwgYXZlcmFnZSBzdHJlbmd0aCB0ZWFtLCB0aGV5IHdvdWxkIHNjb3JlIChvbiBhdmVyYWdlKSAwLjA2IGdvYWxzIG1vcmUgcGVyIGdhbWUgdGhhbiB0aGF0IGF2ZXJhZ2UgdGVhbSBub3JtYWxseSBjb25jZWRlZCwgYW5kIGNvbmNlZGUgMC4zMyBmZXdlci4gT24gdGhlIGZsaXBzaWRlLCBhbnkgb2YgdGhlIHRocmVlIHRlYW1zIHdpdGggdmVyeSBwb29yIHJhdGluZ3Mgd291bGQgYmUgZXhwZWN0ZWQgdG8gbG9zZSBieSBxdWl0ZSBhIG1hcmdpbi4NCg0KQWxsIGluIGFsbCwgd2UgdXNlIHRoZXNlIGRpZmZlcmVuY2VzIHRvIGFkanVzdCB0aGUgZXhwZWN0ZWQgc2NvcmUgZm9yIGVhY2ggZnV0dXJlIG1hdGNodXAuIFRha2UgdGhlIGZpcnN0IE5vcnRoIEFtZXJpY2FuIGdhbWUgZm9yIDIwMjQvMjUsIFN0LiBMb3VpcyBhdCBTZWF0dGxlLiBXZSBzdGFydCBvZmYgZXhwZWN0aW5nIHRoZSBLcmFrZW4gdG8gc2NvcmUgdGhlIGF2ZXJhZ2UgaG9tZSBzY29yZSBmcm9tIDIwMjMvMjQgKDMuMTApLCByZWR1Y2UgdGhhdCBzY29yZSBiYXNlZCBvbiBTZWF0dGxlJ3MgbG93ZXItdGhhbi1sZWFndWUtc3RhbmRhcmQgb2ZmZW5jZSAoLTAuMjMpLCBhbmQgdGhlbiB2ZXJ5IHNsaWdodGx5IGFkanVzdCBpdCBiYXNlZCBvbiBTdC4gTG91aXMnIGRlZmVuc2UgKC0wLjAwMTcpIHRvIGdldCB0aGUgZXhwZWN0ZWQgdGFsbHksIDIuODYuIEEgc2ltaWxhciBwcm9jZXNzIGdpdmVzIHVzIHRoZSBCbHVlcycgZXhwZWN0ZWQgc2NvcmUsIDIuNjQuDQoNCldlIHRoZW4gZ2VuZXJhdGUgYW4gc2ltdWxhdGVkIHNjb3JlIGZvciB0aGlzIGdhbWUgYnkgdGhyb3dpbmcgYm90aCB0ZWFtcyBpbnRvIHRoZWlyIG93biBQb2lzc29uIHByb2Nlc3MgYW5kIHNlZWluZyB3aGF0IG51bWJlciBjb21lcyBvdXQgKGluIG91ciBvbmUgc2ltdWxhdGVkIHNlYXNvbiB3ZSdyZSByZWZlcnJpbmcgdG8gaGVyZSwgU3QuIExvdWlzIHdvbiB0aGlzIGdhbWUgMy0xKS4gVGhhdCdzIHdoYXQgd2UgZG8gZm9yIHRoZSBvdGhlciAxLDMxMSBnYW1lcyB0aHJvdWdoIHRoZSBzZWFzb24sIHdvcmtpbmcgb3V0IGhvdyBtYW55IHBvaW50cyBlYWNoIHRlYW0gZ2V0cyBmcm9tIHRoZSBnYW1lLCBhbmQgdGhlbiB3ZSBhZGQgdGhvc2UgcmVzdWx0cyBpbnRvIGEgdGFibGUgLSBGbG9yaWRhIGNvbWUgb3V0IG9uIHRvcCBpbiB0aGlzIHNjZW5hcmlvLCBnYXRoZXJpbmcgMTA3IHBvaW50cyBhY3Jvc3MgdGhlIHllYXI7IHdoaWxlIEFuYWhlaW0gbG9pdGVyIGF0IHRoZSBib3R0b20gd2l0aCA1OS4gKFRoaXMgaXMgKm5vdCogdG8gc2F5IHRoYXQgdGhpcyBpcyBkZWZpbml0ZWx5IHdoYXQncyBoYXBwZW5pbmcgdGhpcyB5ZWFyIC0gd2UgcnVuIDEwLDAwMCBkaWZmZXJlbnQgc2ltdWxhdGlvbnMgb2YgdGhlIHNlYXNvbiwgYW5kIHRoaXMgaXMganVzdCB0aGUgcmVzdWx0IHRoYXQgd2UgZ2V0IGluIG9uZSBvZiB0aGVtLikNCg0KR2l2ZW4gdGhhdCB0YWxseSBvZiB0aGUgc3RhbmRpbmdzLCB3ZSdyZSBhYmxlIHRvIGNvbWUgdXAgd2l0aCBvdXIgb3duIHBsYXlvZmYgYnJhY2tldCwgcnVubmluZyB0aHJvdWdoIGEgbmV3IGZ1bmN0aW9uIHRvIHByZWRpY3QgYSBzZXZlbi1nYW1lIHBsYXlvZmYgc2Vhc29uIHRoYXQgb3V0cHV0cyBhIHdpbm5lciAtIGNhc2NhZGluZyB0aHJvdWdoIGl0IHVudGlsIG91ciB2aXJ0dWFsIFN0YW5sZXkgQ3VwIGNoYW1waW9ucyBhcmUgY3Jvd25lZC4NCg0KIyMgV2hhdCBUaGlzIE1vZGVsIFNheXMNCg0KSWYgd2UgcnVuIHRocm91Z2ggMTAsMDAwIHBvc3NpYmxlIHdheXMgdGhpcyB1cGNvbWluZyBOSEwgc2Vhc29uIGNvdWxkIHBhbiBvdXQsIHdoYXQgcmVzdWx0cyBoYXBwZW4gcmVndWxhcmx5PyBXaG8ncyBnb3QgYSBnb29kIGNoYW5jZSB0byB0YWtpbmcgb3V0IHRoZSBTdGFubGV5IEN1cCwgb3Igd2hvIHNob3VsZCBiZSBjb250ZW50IHdpdGggbWVyZWx5IG1ha2luZyB0aGUgcGxheW9mZnM/DQoNCnwgVGVhbSB8IENvbmYvRGl2IHwgUG9pbnRzIHwgV2luIERpdiB8IFdpbiBQVCB8IFBsYXlvZmZzIHwgMm5kIFJkIHwgQ29uZiBGaW5hbCB8IFdpbiBDb25mIHwgV2luIEN1cCB8DQp8LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18DQp8IEZsb3JpZGEgfCBFIC8gQVRMIHwgOTkuNyB8IDM4LjYlIHwgMTIuNSUgfCA4OC44JSB8IDU0LjYlIHwgMzIuOSUgfCAxOS4xJSB8IDEwLjUlIHwNCnwgQ2Fyb2xpbmEgfCBFIC8gTUVUIHwgOTguOSB8IDQ0LjclIHwgMTAuNiUgfCA4OC4yJSB8IDU1LjUlIHwgMzMuMyUgfCAxOC4zJSB8IDEwLjElIHwNCnwgRWRtb250b24gfCBXIC8gUEFDIHwgOTkuNyB8IDM3LjQlIHwgMTIuMyUgfCA4OS4yJSB8IDUzLjIlIHwgMzAuNyUgfCAxNy4yJSB8IDkuNyUgfA0KfCBEYWxsYXMgfCBXIC8gQ0VOIHwgOTguNyB8IDMzLjQlIHwgMTAuNSUgfCA4NS4zJSB8IDQ5LjIlIHwgMjcuMSUgfCAxNC43JSB8IDcuOSUgfA0KfCBWYW5jb3V2ZXIgfCBXIC8gUEFDIHwgOTcuOSB8IDI4LjAlIHwgOC41JSB8IDg0LjglIHwgNDcuNiUgfCAyNC44JSB8IDEzLjYlIHwgNy4zJSB8DQp8IENvbG9yYWRvIHwgVyAvIENFTiB8IDk3LjAgfCAyNS42JSB8IDcuMSUgfCA4MS4wJSB8IDQ0LjclIHwgMjQuMSUgfCAxMi40JSB8IDYuNyUgfA0KfCBOWSBSYW5nZXJzIHwgRSAvIE1FVCB8IDk1LjcgfCAyNy44JSB8IDUuNCUgfCA3OS4xJSB8IDQ2LjAlIHwgMjQuNyUgfCAxMi44JSB8IDYuNyUgfA0KfCBCb3N0b24gfCBFIC8gQVRMIHwgOTYuMiB8IDIyLjUlIHwgNS42JSB8IDc4LjklIHwgNDMuOCUgfCAyMy40JSB8IDEyLjYlIHwgNi42JSB8DQp8IFdpbm5pcGVnIHwgVyAvIENFTiB8IDk2LjIgfCAyMS41JSB8IDUuOSUgfCA3OC43JSB8IDQyLjElIHwgMjIuMCUgfCAxMC43JSB8IDUuNSUgfA0KfCBMb3MgQW5nZWxlcyB8IFcgLyBQQUMgfCA5NC45IHwgMTcuNSUgfCA0LjUlIHwgNzQuNSUgfCAzNi44JSB8IDE4LjIlIHwgOS4xJSB8IDQuNSUgfA0KfCBUb3JvbnRvIHwgRSAvIEFUTCB8IDkzLjYgfCAxNC42JSB8IDMuNSUgfCA2OC44JSB8IDM1LjMlIHwgMTcuMyUgfCA4LjYlIHwgMy45JSB8DQp8IFRhbXBhIEJheSB8IEUgLyBBVEwgfCA5MS40IHwgOS41JSB8IDIuMCUgfCA1OS42JSB8IDI4LjAlIHwgMTIuOCUgfCA2LjMlIHwgMi44JSB8DQp8IFZlZ2FzIHwgVyAvIFBBQyB8IDkxLjggfCAxMC4yJSB8IDIuMiUgfCA2MS40JSB8IDI4LjYlIHwgMTMuMyUgfCA2LjAlIHwgMi44JSB8DQp8IE5hc2h2aWxsZSB8IFcgLyBDRU4gfCA5MS44IHwgOS45JSB8IDIuMCUgfCA2MC41JSB8IDI3LjUlIHwgMTIuOCUgfCA1LjclIHwgMi42JSB8DQp8IFBpdHRzYnVyZ2ggfCBFIC8gTUVUIHwgOTAuMiB8IDEwLjclIHwgMS40JSB8IDU2LjAlIHwgMjYuOCUgfCAxMS45JSB8IDUuMyUgfCAyLjIlIHwNCnwgQnVmZmFsbyB8IEUgLyBBVEwgfCA4OS4xIHwgNi4yJSB8IDEuMCUgfCA0OC44JSB8IDIwLjclIHwgOS4wJSB8IDMuOCUgfCAxLjUlIHwNCnwgRGV0cm9pdCB8IEUgLyBBVEwgfCA4OC41IHwgNS42JSB8IDEuMCUgfCA0NS4zJSB8IDE4LjUlIHwgNy43JSB8IDMuMSUgfCAxLjMlIHwNCnwgTWlubmVzb3RhIHwgVyAvIENFTiB8IDg3LjYgfCA0LjAlIHwgMC42JSB8IDQwLjclIHwgMTYuNCUgfCA2LjYlIHwgMi44JSB8IDEuMSUgfA0KfCBOWSBJc2xhbmRlcnMgfCBFIC8gTUVUIHwgODYuNSB8IDUuMiUgfCAwLjUlIHwgMzkuMSUgfCAxNi4zJSB8IDYuNSUgfCAyLjclIHwgMC45JSB8DQp8IFNlYXR0bGUgfCBXIC8gUEFDIHwgODYuOCB8IDMuNyUgfCAwLjYlIHwgMzcuNSUgfCAxNC43JSB8IDUuNyUgfCAyLjElIHwgMC45JSB8DQp8IENhbGdhcnkgfCBXIC8gUEFDIHwgODYuMyB8IDMuMSUgfCAwLjQlIHwgMzUuOSUgfCAxMy4zJSB8IDUuMiUgfCAyLjElIHwgMC45JSB8DQp8IFBoaWxhZGVscGhpYSB8IEUgLyBNRVQgfCA4NS40IHwgMy45JSB8IDAuMyUgfCAzNC4wJSB8IDEzLjQlIHwgNS4xJSB8IDIuMCUgfCAwLjclIHwNCnwgU3QuIExvdWlzIHwgVyAvIENFTiB8IDg1LjggfCAzLjAlIHwgMC40JSB8IDMyLjclIHwgMTIuNCUgfCA1LjAlIHwgMS45JSB8IDAuNyUgfA0KfCBVdGFoIHwgVyAvIENFTiB8IDg1LjYgfCAyLjclIHwgMC4zJSB8IDMyLjElIHwgMTIuMiUgfCA0LjQlIHwgMS43JSB8IDAuNyUgfA0KfCBOZXcgSmVyc2V5IHwgRSAvIE1FVCB8IDg1LjIgfCA0LjQlIHwgMC4zJSB8IDMzLjAlIHwgMTMuMiUgfCA1LjIlIHwgMS45JSB8IDAuNiUgfA0KfCBXYXNoaW5ndG9uIHwgRSAvIE1FVCB8IDgyLjggfCAyLjMlIHwgMC4zJSB8IDIzLjUlIHwgOC41JSB8IDMuMSUgfCAxLjIlIHwgMC41JSB8DQp8IE90dGF3YSB8IEUgLyBBVEwgfCA4My42IHwgMS45JSB8IDAuMiUgfCAyNC44JSB8IDguMyUgfCAzLjIlIHwgMS4yJSB8IDAuMyUgfA0KfCBNb250cmVhbCB8IEUgLyBBVEwgfCA4MS41IHwgMS4xJSB8IDAuMSUgfCAxNy41JSB8IDUuOSUgfCAyLjElIHwgMC43JSB8IDAuMiUgfA0KfCBDb2x1bWJ1cyB8IEUgLyBNRVQgfCA3OS45IHwgMS4wJSB8IDAuMCUgfCAxNC43JSB8IDUuMSUgfCAxLjclIHwgMC41JSB8IDAuMSUgfA0KfCBBbmFoZWltIHwgVyAvIFBBQyB8IDczLjggfCAwLjElIHwgMC4wJSB8IDMuOSUgfCAxLjAlIHwgMC4yJSB8IDAuMSUgfCAwLjAlIHwNCnwgQ2hpY2FnbyB8IFcgLyBDRU4gfCA3MS4yIHwgMC4xJSB8IDAuMCUgfCAxLjYlIHwgMC4zJSB8IDAuMSUgfCAwLjAlIHwgMC4wJSB8DQp8IFNhbiBKb3NlIHwgVyAvIFBBQyB8IDY1LjYgfCAwLjAlIHwgMC4wJSB8IDAuMyUgfCAwLjAlIHwgMC4wJSB8IDAuMCUgfCAwLjAlIHwNCg0KSG93IHNob2NraW5nOiB0aGUgdGVhbSB0aGF0IHdvbiBsYXN0IHNlYXNvbiBpcyB0aGUgZmF2b3VyaXRlIHRvIHdpbiBhZ2FpbiB0aGlzIHNlYXNvbi4gT2YgY291cnNlLCBzaW5jZSB3ZSBkb24ndCBoYXZlIGEgcGxheWVyLWJhc2VkIG1vZGVsIGxpa2Ugc29tZSBvZiB0aGUgZXhwZXJ0cyBpbiB0aGUgZmllbGQgZG8sIHdlIGNhbiBvbmx5IGdvIG9mZiB0aGUgcmVzdWx0cyBvZiB0aGUgamVyc2V5LCBhbmQgd2UgKndpbGwqIGJlIGdyYWR1YWxseSBzaGlmdGluZyB0b3dhcmRzIHRha2luZyBpbmZsdWVuY2UgZnJvbSB0aGlzIHNlYXNvbidzIHJlc3VsdHMgcmF0aGVyIHRoYW4gbGFzdCBzZWFzb24ncyAoYnkgdGhlIHRpbWUgZXZlcnkgdGVhbSdzIHBsYXllZCAyMCBnYW1lcywgaXQnbGwgYmUgMTAwJSBiYXNlZCBvbiAyMDI0LzI1IHJlc3VsdHMpLg0KDQpBcyBhIGxhc3QgdGhvdWdodCwgd2UnbGwgbGVhdmUgeW91IHdpdGggdGhlIG1vc3QgZGlzZ3VzdGluZyBjb2RlIHdlJ3ZlIGV2ZXIgaGFkIHRvIHdyaXRlIChhIHZhZ3VlIGF0dGVtcHQgYXQgd29ya2luZyBvdXQgdGhlICJjdXRvZmYgcG9pbnQiIHRvIGdldCBhIHdpbGRjYXJkIHBsYXlvZmYgc3BvdCBpbiBlYWNoIGNvbmZlcmVuY2UpOg0KDQpgYGAgcg0KRVdDY3V0b2ZmIDwtIGFwcGVuZChFV0NjdXRvZmYsDQogICAgICAgICAgICAgICAgICAgICgobWF4KG5obDI0MjVfdGFibGUkUHRzW3doaWNoKG5obDI0MjVfdGFibGUkQ29uZmVyZW5jZSA9PSAiRSIgJiBuaGwyNDI1X3RhYmxlJFBsYXlvZmZzID09IEZBTFNFKV0pKQ0KICAgICAgICAgICAgICAgICAgICAgKyBuaGwyNDI1X3RhYmxlJFB0c1t3aGljaChuaGwyNDI1X3RhYmxlJENvbmZlcmVuY2UgPT0gIkUiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICYgbmhsMjQyNV90YWJsZSRDb25mUmFuaw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9PSAobmhsMjQyNV90YWJsZSRDb25mUmFua1ttYXRjaChuaGwyNDI1X3RhYmxlJFRlYW1bd2hpY2gobmhsMjQyNV90YWJsZSRQdHMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID09IChtYXgobmhsMjQyNV90YWJsZSRQdHNbd2hpY2gobmhsMjQyNV90YWJsZSRDb25mZXJlbmNlID09ICJFIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmIG5obDI0MjVfdGFibGUkUGxheW9mZnMgPT0gRkFMU0UpXSkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmIG5obDI0MjVfdGFibGUkQ29uZmVyZW5jZSA9PSAiRSIpXVsxXSxuaGwyNDI1X3RhYmxlJFRlYW0pXSktMSldKSAvMikNCmBgYA0KDQpIaWRlb3VzLg0KDQoqSWYgeW91IHdhbnQgdG8gc2VlIGhvdyB0aGlzIGFsbCBlbmRzIHVwIGZvciB0aGUgcmVzdCBvZiB0aGUgc2Vhc29uLCBJJ2xsIGJlIHBvc3RpbmcgdXBkYXRlcyBbb24gbXkgVHdpdHRlcl0oaHR0cHM6Ly94LmNvbS9mdXp6eWJsdWVyYWluKSAoYW5kIHllcywgaXQgaXMgc3RpbGwgVHdpdHRlciwgcmVnYXJkbGVzcyBvZiB3aGF0IHJhY2lzdCBiaWxsaW9uYWlyZXMgd2FudCB0byBjYWxsIGl0KS4qDQo=