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, 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:
| 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?
| 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).
LS0tDQp0aXRsZTogIk5ITCAyMDI0LzI1IFByb2plY3Rpb25zIg0KYXV0aG9yOiAiQ2xhaXJlIEZyZWRyaWtzc29uIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyMgSG93IFRoaXMgTW9kZWwgV29ya3MNCg0KSWYgeW91IHdhbnQgdG8gbW9kZWwgc3BvcnRzLCB5b3UgbmVlZCB0d28gdGhpbmdzOiBhIHdheSB0byBtZWFzdXJlIHRoZSBnb29kbmVzcyBvZiB0ZWFtcywgYW5kIGEgd2F5IHRvIHR1cm4gdGhhdCBnb29kbmVzcyBpbnRvIGFuIGVzdGltYXRlIG9mIHRoZWlyIHdpbm5pbmduZXNzLg0KDQpBdCBpdHMgY29yZSwgb3VyIE5ITCBwcm9qZWN0aW9ucyBhcmUgZGVyaXZlZCBmcm9tIHRoZSBzYW1lIGJhc2ljIG1ldGhvZG9sb2d5IHdlJ3ZlIGJlZW4gdXNpbmcgZm9yIG1vZGVsbGluZyBzb2NjZXIgZm9yIHNldmVyYWwgeWVhcnMuIEljZSBob2NrZXkgaXMgYSBzaW1wbGUgZ2FtZSwgc2NvcmVkIGluIGluY3JlbWVudHMgb2Ygb25lIHRoYXQgYXJlIHJlbGF0aXZlbHkgcmFyZSwgYW5kIGl0J3MgcXVpdGUgcmVhc29uYWJsZSB0byBtb2RlbCB0aGF0IHdpdGggYSBQb2lzc29uIGRpc3RyaWJ1dGlvbiB3aXRoIGEgbWVhbiBhcm91bmQgMyAtIHRlYW1zIHVzdWFsbHkgc2NvcmUgYSBjb3VwbGUgb2YgZ29hbHMgYSBnYW1lLCBzb21ldGltZXMgdGhleSBkb24ndCBzY29yZSBhbnksIHNvbWV0aW1lcyB0aGV5IHNjb3JlIGEgbG90OyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uIG1lYW5zIGFsbCBvZiB0aGVzZSBoYXBwZW4gd2l0aCByZWd1bGFyaXR5LiAoSW4gYSBoeXBvdGhldGljYWwgc2ltdWxhdGVkIHNlYXNvbiBmb3IgMjAyNC8yNSwgd2UgZGlkbid0IGdldCBhbnkgMC0wIGdhbWVzIChjb21wYXJlIHdpdGggdGhyZWUgZnJvbSAyMDIzLzI0KSwgYW5kIG5vIHRlYW0gc2NvcmVkIHRlbiBpbiBhIGdhbWUgKGFnYWluLCB0aHJlZSBsYXN0IHNlYXNvbiksIHNvIGlmIGFueXRoaW5nIHRoaXMgaXMgbWF5YmUgYSBsaXR0bGUgbmFycm93IGNvbXBhcmVkIHRvIHRoZSByZWFsIHdvcmxkLikNCg0KQnV0IHRvIGdldCByZXN1bHRzIGZyb20gYSBQb2lzc29uIHByb2Nlc3MsIHdlIG5lZWQgYW4gZXhwZWN0ZWQgbnVtYmVyIG9mIG9jY3VycmVuY2VzIC0gb25lIHdoZXJlLCBpZiB5b3UgcmVwZWF0IHRoZSBwcm9jZXNzIGVub3VnaCB0aW1lcywgeW91ciBhdmVyYWdlIHJlc3VsdCB3b3VsZCBiZSB0aGF0IG51bWJlci4gQW5kIGJlY2F1c2UgdGhlIDMyIHRlYW1zIGluIHRoZSBOSEwgYXJlIGRpZmZlcmVudCB0byBlYWNoIG90aGVyLCBhbmQgYWxtb3N0IGV2ZXJ5IGdhbWUgaXMgcGxheWVkIGF0IGFuIGFyZW5hIHdpdGggc29tZSBsZXZlbCBvZiBob21lLWljZSBhZHZhbnRhZ2UsIHRoYXQgbWVhbiBpcyBnb2luZyB0byBiZSBkaWZmZXJlbnQgZm9yIGVhY2ggdGVhbSBpbiBlYWNoIGdhbWUuDQoNCk91ciBtb2RlbCBjaGFuZ2VzIHRoYXQgbWVhbiBieSwgaW4gc2hvcnQsIGNvbXBhcmluZyBhIHRlYW0ncyAqZXhwZWN0ZWQqIHBlcmZvcm1hbmNlIGFjcm9zcyB0aGUgc2Vhc29uIHRvIGl0cyAqYWN0dWFsKiBwZXJmb3JtYW5jZSwgbWVhc3VyaW5nIHRoYXQgZXhwZWN0YXRpb24gYnkgd2hvbSBpdCBoYXMgYW5kIGhhc24ndCBwbGF5ZWQuIFdlIHN0YXJ0IHRoaXMgb2ZmIGJ5IHRha2luZyBhIHNhbXBsZSBvZiBnYW1lcyBwbGF5ZWQsIGFuZCB1c2luZyB0aGF0IGFzIG91ciBkYXRhc2V0LiBXZSB0YWxseSB1cCB0aGUgbnVtYmVyIG9mIGdhbWVzIHBsYXllZCBiZXR3ZWVuIGVhY2ggcGFpciBvZiB0ZWFtcywgYW5kIHRoZSB0b3RhbCBnb2FscyBmb3IgYW5kIGFnYWluc3QgYnkgZWFjaCB0ZWFtLFteMV0gYW5kIHVzZSB0aGF0IGZvciBhbiBpbXBvcnRhbnQgY2FsY3VsYXRpb246IGhvdyBtYW55IGdvYWxzIGVhY2ggdGVhbSAqc2hvdWxkKiBoYXZlIHNjb3JlZCBhbmQgY29uY2VkZWQgYmFzZWQgb24gdGhlaXIgb3Bwb25lbnRzLg0KDQpbXjFdOiBBZnRlciB3ZSd2ZSB0YWtlbiBvdXQgYW55IGdvYWxzIGZyb20gb3ZlcnRpbWUgYW5kIHNob290b3V0cyAtIHRoZSBleHBlY3RhdGlvbiBiZWhpbmQgb3VyIFBvaXNzb24gbWVhbiBpcyB0aGF0IGl0J3MgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIGdvYWxzIHNjb3JlZCAqYWNyb3NzIDYwIG1pbnV0ZXMgb2YgZ2FtZXRpbWUqLCB3aGljaCBpcyBicm9rZW4gd2hlbiBvdmVydGltZSBjb21lcyBpbnRvIHRoZSBlcXVhdGlvbi4gKFdlIGFsc28gYmVsaWV2ZSB0aGF0IGlmIHlvdSdyZSB0aWVkIGFmdGVyIHRocmVlIHBlcmlvZHMsIGZpdmUgbWludXRlcyBvZiB0aHJlZS1vbi10aHJlZSBpcyBiYXNpY2FsbHkgYSBjb2luIHRvc3MuKQ0KDQpUbyBpbGx1c3RyYXRlIHRoZSBwb2ludDogYXNzdW1lIFBoaWxhZGVscGhpYSBzY29yZSBhbiBhdmVyYWdlIG9mIDMuMiBnb2FscyBwZXIgZ2FtZSwgYW5kIGNvbmNlZGUgYW4gYXZlcmFnZSBvZiAyLjYsIGFjcm9zcyB0aGVpciBlbnRpcmUgc2Vhc29uLiBPbiB0aGUgb3RoZXIgaGFuZCwgUGl0dHNidXJnaCBzY29yZSBhdCBhIHJhdGUgMi44L2dhbWUgYW5kIGNvbmNlZGUgYXQgYSByYXRlIG9mIDIuNC4gSWYgdGhlIHR3byB0ZWFtcyBwbGF5ZWQgZWFjaCBvdGhlciBmb3VyIHRpbWVzICh0d28gYXQgZWFjaCB2ZW51ZSwgdG8gY2FuY2VsIG91dCBob21lIGFkdmFudGFnZSksIG91ciBpbml0aWFsIGV4cGVjdGF0aW9uIHdvdWxkIGJlIGZvciB0aGUgdGVhbXMgdG8gc2NvcmUgYW5kIGNvbmNlZGUgYXQgaGFsZndheSBiZXR3ZWVuIHRoZWlyIHR3byBhdmVyYWdlcyAtIHRoZSBGbHllcnMgd291bGQgc2NvcmUgMi44IGdvYWxzIHBlciBnYW1lIGFjcm9zcyB0aGUgZm91ciBtYXRjaGVzICh0aGUgbWVkaWFuIG9mIHRoZWlyIHJlZ3VsYXIgMy4yIG9mZmVuY2UgYW5kIHRoZSBQZW5ndWlucycgcmVndWxhciAyLjQgZGVmZW5zZSksIGFuZCB0aGUgUGVuZ3VpbnMgd291bGQgc2NvcmUgMi43Lg0KDQpJZiwgb3ZlciB0aGUgc3VtIG9mIHRob3NlIGZvdXIgZ2FtZXMsIHRoZSBGbHllcnMgc2NvcmVkIGEgdG90YWwgb2YgMTIgZ29hbHMgYW5kIHRoZSBQZW5ndWlucyBzY29yZWQgMTAsIHRoZSBmb3JtZXIgd291bGQgaGF2ZSBvdmVycGVyZm9ybWVkIGFuZCB0aGUgbGF0dGVyIHdvdWxkIGhhdmUgdW5kZXJwZXJmb3JtZWQuIE91ciBpbnR1aXRpb24gZnJvbSB0aGlzIGlzIHRoYXQgUGhpbGFkZWxwaGlhIGFyZSBzbGlnaHRseSBiZXR0ZXIgdGhhbiBvdXIgaHlwb3RoZXRpY2FsIGF2ZXJhZ2UgdGVhbSwgYW5kIHRoZSBQZW5ndWlucyBhcmUgc2xpZ2h0bHkgd29yc2UuIFRoZSBtb2RlbCBpcyBiYXNpY2FsbHkgZG9pbmcgdGhhdCBhY3Jvc3MgYW4gZW50aXJlIHNlYXNvbiwgd2l0aCBhbGwgMzIgdGVhbXMgaW52b2x2ZWQsIGFuZCBhaW1pbmcgdG8gY291bnRlcmFjdCB0aGUgZWZmZWN0cyBvZiB0aGUgaW5uYXRlbHkgdW5ldmVuIHNjaGVkdWxlLg0KDQpPbmNlIHdlJ3ZlIGRvbmUgYWxsIHRoYXQsIGVhY2ggdGVhbSBlbmRzIHVwIHdpdGggYSByYXRpbmcgZm9yIGJvdGggb2ZmZW5zZSBhbmQgZGVmZW5zZSwgZXhwcmVzc2VkIHJlbGF0aXZlIHRvIHRoZSBhdmVyYWdlOg0KDQp8ICAgICB8IFRlYW0gICAgIHwgR0YgIHwgeEdGICAgIHwgRkRpZmYgfCBHQSAgfCB4R0EgICAgfCBBRGlmZiB8IFREaWZmIHwNCnwtLS0tLXwtLS0tLS0tLS0tfC0tLS0tfC0tLS0tLS0tfC0tLS0tLS18LS0tLS18LS0tLS0tLS18LS0tLS0tLXwtLS0tLS0tfA0KfCAxICAgfCBGbG9yaWRhICB8IDMyOSB8IDMyMi41NSB8IDAuMDYgIHwgMjU0IHwgMjg4Ljc1IHwgLTAuMzMgfCAwLjM5ICB8DQp8IDIgICB8IEVkbW9udG9uIHwgMzY4IHwgMzM5Ljg2IHwgMC4yNiAgfCAyOTUgfCAzMDcuNzggfCAtMC4xMiB8IDAuMzggIHwNCnwgMyAgIHwgQ2Fyb2xpbmEgfCAzMDkgfCAyOTQuNzcgfCAwLjE1ICB8IDIzOCB8IDI1Ny4yNSB8IC0wLjIxIHwgMC4zNiAgfA0KfCA0ICAgfCBEYWxsYXMgICB8IDMzNiB8IDMxOS4yOCB8IDAuMTcgIHwgMjcxIHwgMjg4LjUyIHwgLTAuMTcgfCAwLjM0ICB8DQp8IDUgICB8IENvbG9yYWRvIHwgMzM4IHwgMzA3LjExIHwgMC4zMyAgfCAyODMgfCAyNzkuNTkgfCAwLjA0ICB8IDAuMjkgIHwNCnwgLi4uIHwgICAgICAgICAgfCAgICAgfCAgICAgICAgfCAgICAgICB8ICAgICB8ICAgICAgICB8ICAgICAgIHwgICAgICAgfA0KfCAyOCAgfCBNb250cmVhbCB8IDIyNiB8IDIzNS42NyB8IC0wLjEyIHwgMjczIHwgMjU5Ljc1IHwgMC4xNiAgfCAtMC4yOCB8DQp8IDI5ICB8IENvbHVtYnVzIHwgMjMxIHwgMjM4LjA5IHwgLTAuMDkgfCAyODggfCAyNjcuMDYgfCAwLjI2ICB8IC0wLjM0IHwNCnwgMzAgIHwgQW5haGVpbSAgfCAxOTggfCAyMjEuNjQgfCAtMC4yOSB8IDI5MCB8IDI2Ny4yNCB8IDAuMjggIHwgLTAuNTcgfA0KfCAzMSAgfCBDaGljYWdvICB8IDE3MyB8IDIwOC45MSB8IC0wLjQ0IHwgMjg0IHwgMjY1LjU0IHwgMC4yMyAgfCAtMC42NiB8DQp8IDMyICB8IFNhbiBKb3NlIHwgMTc2IHwgMjA5Ljc5IHwgLTAuNDEgfCAzMjIgfCAyODMuNjYgfCAwLjQ3ICB8IC0wLjg4IHwNCg0KVGhlIGludGVycHJldGF0aW9uIG9mIHRoaXMgaXMgdGhhdCwgaWYgRmxvcmlkYSBwbGF5ZWQgYSBoeXBvdGhldGljYWwgYXZlcmFnZSBzdHJlbmd0aCB0ZWFtLCB0aGV5IHdvdWxkIHNjb3JlIChvbiBhdmVyYWdlKSAwLjA2IGdvYWxzIG1vcmUgcGVyIGdhbWUgdGhhbiB0aGF0IGF2ZXJhZ2UgdGVhbSBub3JtYWxseSBjb25jZWRlZCwgYW5kIGNvbmNlZGUgMC4zMyBmZXdlci4gT24gdGhlIGZsaXBzaWRlLCBhbnkgb2YgdGhlIHRocmVlIHRlYW1zIHdpdGggdmVyeSBwb29yIHJhdGluZ3Mgd291bGQgYmUgZXhwZWN0ZWQgdG8gbG9zZSBieSBxdWl0ZSBhIG1hcmdpbi4NCg0KQWxsIGluIGFsbCwgd2UgdXNlIHRoZXNlIGRpZmZlcmVuY2VzIHRvIGFkanVzdCB0aGUgZXhwZWN0ZWQgc2NvcmUgZm9yIGVhY2ggZnV0dXJlIG1hdGNodXAuIFRha2UgdGhlIGZpcnN0IE5vcnRoIEFtZXJpY2FuIGdhbWUgZm9yIDIwMjQvMjUsIFN0LiBMb3VpcyBhdCBTZWF0dGxlLiBXZSBzdGFydCBvZmYgZXhwZWN0aW5nIHRoZSBLcmFrZW4gdG8gc2NvcmUgdGhlIGF2ZXJhZ2UgaG9tZSBzY29yZSBmcm9tIDIwMjMvMjQgKDMuMTApLCByZWR1Y2UgdGhhdCBzY29yZSBiYXNlZCBvbiBTZWF0dGxlJ3MgbG93ZXItdGhhbi1sZWFndWUtc3RhbmRhcmQgb2ZmZW5jZSAoLTAuMjMpLCBhbmQgdGhlbiB2ZXJ5IHNsaWdodGx5IGFkanVzdCBpdCBiYXNlZCBvbiBTdC4gTG91aXMnIGRlZmVuc2UgKC0wLjAwMTcpIHRvIGdldCB0aGUgZXhwZWN0ZWQgdGFsbHksIDIuODYuIEEgc2ltaWxhciBwcm9jZXNzIGdpdmVzIHVzIHRoZSBCbHVlcycgZXhwZWN0ZWQgc2NvcmUsIDIuNjQuDQoNCldlIHRoZW4gZ2VuZXJhdGUgYW4gc2ltdWxhdGVkIHNjb3JlIGZvciB0aGlzIGdhbWUgYnkgdGhyb3dpbmcgYm90aCB0ZWFtcyBpbnRvIHRoZWlyIG93biBQb2lzc29uIHByb2Nlc3MgYW5kIHNlZWluZyB3aGF0IG51bWJlciBjb21lcyBvdXQgKGluIG91ciBvbmUgc2ltdWxhdGVkIHNlYXNvbiB3ZSdyZSByZWZlcnJpbmcgdG8gaGVyZSwgU3QuIExvdWlzIHdvbiB0aGlzIGdhbWUgMy0xKS4gVGhhdCdzIHdoYXQgd2UgZG8gZm9yIHRoZSBvdGhlciAxLDMxMSBnYW1lcyB0aHJvdWdoIHRoZSBzZWFzb24sIHdvcmtpbmcgb3V0IGhvdyBtYW55IHBvaW50cyBlYWNoIHRlYW0gZ2V0cyBmcm9tIHRoZSBnYW1lLCBhbmQgdGhlbiB3ZSBhZGQgdGhvc2UgcmVzdWx0cyBpbnRvIGEgdGFibGUgLSBGbG9yaWRhIGNvbWUgb3V0IG9uIHRvcCBpbiB0aGlzIHNjZW5hcmlvLCBnYXRoZXJpbmcgMTA3IHBvaW50cyBhY3Jvc3MgdGhlIHllYXI7IHdoaWxlIEFuYWhlaW0gbG9pdGVyIGF0IHRoZSBib3R0b20gd2l0aCA1OS4gKFRoaXMgaXMgKm5vdCogdG8gc2F5IHRoYXQgdGhpcyBpcyBkZWZpbml0ZWx5IHdoYXQncyBoYXBwZW5pbmcgdGhpcyB5ZWFyIC0gd2UgcnVuIDEwLDAwMCBkaWZmZXJlbnQgc2ltdWxhdGlvbnMgb2YgdGhlIHNlYXNvbiwgYW5kIHRoaXMgaXMganVzdCB0aGUgcmVzdWx0IHRoYXQgd2UgZ2V0IGluIG9uZSBvZiB0aGVtLikNCg0KR2l2ZW4gdGhhdCB0YWxseSBvZiB0aGUgc3RhbmRpbmdzLCB3ZSdyZSBhYmxlIHRvIGNvbWUgdXAgd2l0aCBvdXIgb3duIHBsYXlvZmYgYnJhY2tldCwgcnVubmluZyB0aHJvdWdoIGEgbmV3IGZ1bmN0aW9uIHRvIHByZWRpY3QgYSBzZXZlbi1nYW1lIHBsYXlvZmYgc2Vhc29uIHRoYXQgb3V0cHV0cyBhIHdpbm5lciAtIGNhc2NhZGluZyB0aHJvdWdoIGl0IHVudGlsIG91ciB2aXJ0dWFsIFN0YW5sZXkgQ3VwIGNoYW1waW9ucyBhcmUgY3Jvd25lZC4NCg0KIyMgV2hhdCBUaGlzIE1vZGVsIFNheXMNCg0KSWYgd2UgcnVuIHRocm91Z2ggMTAsMDAwIHBvc3NpYmxlIHdheXMgdGhpcyB1cGNvbWluZyBOSEwgc2Vhc29uIGNvdWxkIHBhbiBvdXQsIHdoYXQgcmVzdWx0cyBoYXBwZW4gcmVndWxhcmx5PyBXaG8ncyBnb3QgYSBnb29kIGNoYW5jZSB0byB0YWtpbmcgb3V0IHRoZSBTdGFubGV5IEN1cCwgb3Igd2hvIHNob3VsZCBiZSBjb250ZW50IHdpdGggbWVyZWx5IG1ha2luZyB0aGUgcGxheW9mZnM/DQoNCnwgVGVhbSB8IENvbmYvRGl2IHwgUG9pbnRzIHwgV2luIERpdiB8IFdpbiBQVCB8IFBsYXlvZmZzIHwgMm5kIFJkIHwgQ29uZiBGaW5hbCB8IFdpbiBDb25mIHwgV2luIEN1cCB8DQp8LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18LS0tLS0tLS18DQp8IEZsb3JpZGEgfCBFIC8gQVRMIHwgOTkuNyB8IDM4LjYlIHwgMTIuNSUgfCA4OC44JSB8IDU0LjYlIHwgMzIuOSUgfCAxOS4xJSB8IDEwLjUlIHwNCnwgQ2Fyb2xpbmEgfCBFIC8gTUVUIHwgOTguOSB8IDQ0LjclIHwgMTAuNiUgfCA4OC4yJSB8IDU1LjUlIHwgMzMuMyUgfCAxOC4zJSB8IDEwLjElIHwNCnwgRWRtb250b24gfCBXIC8gUEFDIHwgOTkuNyB8IDM3LjQlIHwgMTIuMyUgfCA4OS4yJSB8IDUzLjIlIHwgMzAuNyUgfCAxNy4yJSB8IDkuNyUgfA0KfCBEYWxsYXMgfCBXIC8gQ0VOIHwgOTguNyB8IDMzLjQlIHwgMTAuNSUgfCA4NS4zJSB8IDQ5LjIlIHwgMjcuMSUgfCAxNC43JSB8IDcuOSUgfA0KfCBWYW5jb3V2ZXIgfCBXIC8gUEFDIHwgOTcuOSB8IDI4LjAlIHwgOC41JSB8IDg0LjglIHwgNDcuNiUgfCAyNC44JSB8IDEzLjYlIHwgNy4zJSB8DQp8IENvbG9yYWRvIHwgVyAvIENFTiB8IDk3LjAgfCAyNS42JSB8IDcuMSUgfCA4MS4wJSB8IDQ0LjclIHwgMjQuMSUgfCAxMi40JSB8IDYuNyUgfA0KfCBOWSBSYW5nZXJzIHwgRSAvIE1FVCB8IDk1LjcgfCAyNy44JSB8IDUuNCUgfCA3OS4xJSB8IDQ2LjAlIHwgMjQuNyUgfCAxMi44JSB8IDYuNyUgfA0KfCBCb3N0b24gfCBFIC8gQVRMIHwgOTYuMiB8IDIyLjUlIHwgNS42JSB8IDc4LjklIHwgNDMuOCUgfCAyMy40JSB8IDEyLjYlIHwgNi42JSB8DQp8IFdpbm5pcGVnIHwgVyAvIENFTiB8IDk2LjIgfCAyMS41JSB8IDUuOSUgfCA3OC43JSB8IDQyLjElIHwgMjIuMCUgfCAxMC43JSB8IDUuNSUgfA0KfCBMb3MgQW5nZWxlcyB8IFcgLyBQQUMgfCA5NC45IHwgMTcuNSUgfCA0LjUlIHwgNzQuNSUgfCAzNi44JSB8IDE4LjIlIHwgOS4xJSB8IDQuNSUgfA0KfCBUb3JvbnRvIHwgRSAvIEFUTCB8IDkzLjYgfCAxNC42JSB8IDMuNSUgfCA2OC44JSB8IDM1LjMlIHwgMTcuMyUgfCA4LjYlIHwgMy45JSB8DQp8IFRhbXBhIEJheSB8IEUgLyBBVEwgfCA5MS40IHwgOS41JSB8IDIuMCUgfCA1OS42JSB8IDI4LjAlIHwgMTIuOCUgfCA2LjMlIHwgMi44JSB8DQp8IFZlZ2FzIHwgVyAvIFBBQyB8IDkxLjggfCAxMC4yJSB8IDIuMiUgfCA2MS40JSB8IDI4LjYlIHwgMTMuMyUgfCA2LjAlIHwgMi44JSB8DQp8IE5hc2h2aWxsZSB8IFcgLyBDRU4gfCA5MS44IHwgOS45JSB8IDIuMCUgfCA2MC41JSB8IDI3LjUlIHwgMTIuOCUgfCA1LjclIHwgMi42JSB8DQp8IFBpdHRzYnVyZ2ggfCBFIC8gTUVUIHwgOTAuMiB8IDEwLjclIHwgMS40JSB8IDU2LjAlIHwgMjYuOCUgfCAxMS45JSB8IDUuMyUgfCAyLjIlIHwNCnwgQnVmZmFsbyB8IEUgLyBBVEwgfCA4OS4xIHwgNi4yJSB8IDEuMCUgfCA0OC44JSB8IDIwLjclIHwgOS4wJSB8IDMuOCUgfCAxLjUlIHwNCnwgRGV0cm9pdCB8IEUgLyBBVEwgfCA4OC41IHwgNS42JSB8IDEuMCUgfCA0NS4zJSB8IDE4LjUlIHwgNy43JSB8IDMuMSUgfCAxLjMlIHwNCnwgTWlubmVzb3RhIHwgVyAvIENFTiB8IDg3LjYgfCA0LjAlIHwgMC42JSB8IDQwLjclIHwgMTYuNCUgfCA2LjYlIHwgMi44JSB8IDEuMSUgfA0KfCBOWSBJc2xhbmRlcnMgfCBFIC8gTUVUIHwgODYuNSB8IDUuMiUgfCAwLjUlIHwgMzkuMSUgfCAxNi4zJSB8IDYuNSUgfCAyLjclIHwgMC45JSB8DQp8IFNlYXR0bGUgfCBXIC8gUEFDIHwgODYuOCB8IDMuNyUgfCAwLjYlIHwgMzcuNSUgfCAxNC43JSB8IDUuNyUgfCAyLjElIHwgMC45JSB8DQp8IENhbGdhcnkgfCBXIC8gUEFDIHwgODYuMyB8IDMuMSUgfCAwLjQlIHwgMzUuOSUgfCAxMy4zJSB8IDUuMiUgfCAyLjElIHwgMC45JSB8DQp8IFBoaWxhZGVscGhpYSB8IEUgLyBNRVQgfCA4NS40IHwgMy45JSB8IDAuMyUgfCAzNC4wJSB8IDEzLjQlIHwgNS4xJSB8IDIuMCUgfCAwLjclIHwNCnwgU3QuIExvdWlzIHwgVyAvIENFTiB8IDg1LjggfCAzLjAlIHwgMC40JSB8IDMyLjclIHwgMTIuNCUgfCA1LjAlIHwgMS45JSB8IDAuNyUgfA0KfCBVdGFoIHwgVyAvIENFTiB8IDg1LjYgfCAyLjclIHwgMC4zJSB8IDMyLjElIHwgMTIuMiUgfCA0LjQlIHwgMS43JSB8IDAuNyUgfA0KfCBOZXcgSmVyc2V5IHwgRSAvIE1FVCB8IDg1LjIgfCA0LjQlIHwgMC4zJSB8IDMzLjAlIHwgMTMuMiUgfCA1LjIlIHwgMS45JSB8IDAuNiUgfA0KfCBXYXNoaW5ndG9uIHwgRSAvIE1FVCB8IDgyLjggfCAyLjMlIHwgMC4zJSB8IDIzLjUlIHwgOC41JSB8IDMuMSUgfCAxLjIlIHwgMC41JSB8DQp8IE90dGF3YSB8IEUgLyBBVEwgfCA4My42IHwgMS45JSB8IDAuMiUgfCAyNC44JSB8IDguMyUgfCAzLjIlIHwgMS4yJSB8IDAuMyUgfA0KfCBNb250cmVhbCB8IEUgLyBBVEwgfCA4MS41IHwgMS4xJSB8IDAuMSUgfCAxNy41JSB8IDUuOSUgfCAyLjElIHwgMC43JSB8IDAuMiUgfA0KfCBDb2x1bWJ1cyB8IEUgLyBNRVQgfCA3OS45IHwgMS4wJSB8IDAuMCUgfCAxNC43JSB8IDUuMSUgfCAxLjclIHwgMC41JSB8IDAuMSUgfA0KfCBBbmFoZWltIHwgVyAvIFBBQyB8IDczLjggfCAwLjElIHwgMC4wJSB8IDMuOSUgfCAxLjAlIHwgMC4yJSB8IDAuMSUgfCAwLjAlIHwNCnwgQ2hpY2FnbyB8IFcgLyBDRU4gfCA3MS4yIHwgMC4xJSB8IDAuMCUgfCAxLjYlIHwgMC4zJSB8IDAuMSUgfCAwLjAlIHwgMC4wJSB8DQp8IFNhbiBKb3NlIHwgVyAvIFBBQyB8IDY1LjYgfCAwLjAlIHwgMC4wJSB8IDAuMyUgfCAwLjAlIHwgMC4wJSB8IDAuMCUgfCAwLjAlIHwNCg0KSG93IHNob2NraW5nOiB0aGUgdGVhbSB0aGF0IHdvbiBsYXN0IHNlYXNvbiBpcyB0aGUgZmF2b3VyaXRlIHRvIHdpbiBhZ2FpbiB0aGlzIHNlYXNvbi4gT2YgY291cnNlLCBzaW5jZSB3ZSBkb24ndCBoYXZlIGEgcGxheWVyLWJhc2VkIG1vZGVsIGxpa2Ugc29tZSBvZiB0aGUgZXhwZXJ0cyBpbiB0aGUgZmllbGQgZG8sIHdlIGNhbiBvbmx5IGdvIG9mZiB0aGUgcmVzdWx0cyBvZiB0aGUgamVyc2V5LCBhbmQgd2UgKndpbGwqIGJlIGdyYWR1YWxseSBzaGlmdGluZyB0b3dhcmRzIHRha2luZyBpbmZsdWVuY2UgZnJvbSB0aGlzIHNlYXNvbidzIHJlc3VsdHMgcmF0aGVyIHRoYW4gbGFzdCBzZWFzb24ncyAoYnkgdGhlIHRpbWUgZXZlcnkgdGVhbSdzIHBsYXllZCAyMCBnYW1lcywgaXQnbGwgYmUgMTAwJSBiYXNlZCBvbiAyMDI0LzI1IHJlc3VsdHMpLg0KDQpBcyBhIGxhc3QgdGhvdWdodCwgd2UnbGwgbGVhdmUgeW91IHdpdGggdGhlIG1vc3QgZGlzZ3VzdGluZyBjb2RlIHdlJ3ZlIGV2ZXIgaGFkIHRvIHdyaXRlIChhIHZhZ3VlIGF0dGVtcHQgYXQgd29ya2luZyBvdXQgdGhlICJjdXRvZmYgcG9pbnQiIHRvIGdldCBhIHdpbGRjYXJkIHBsYXlvZmYgc3BvdCBpbiBlYWNoIGNvbmZlcmVuY2UpOg0KDQpgYGAgcg0KRVdDY3V0b2ZmIDwtIGFwcGVuZChFV0NjdXRvZmYsDQogICAgICAgICAgICAgICAgICAgICgobWF4KG5obDI0MjVfdGFibGUkUHRzW3doaWNoKG5obDI0MjVfdGFibGUkQ29uZmVyZW5jZSA9PSAiRSIgJiBuaGwyNDI1X3RhYmxlJFBsYXlvZmZzID09IEZBTFNFKV0pKQ0KICAgICAgICAgICAgICAgICAgICAgKyBuaGwyNDI1X3RhYmxlJFB0c1t3aGljaChuaGwyNDI1X3RhYmxlJENvbmZlcmVuY2UgPT0gIkUiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICYgbmhsMjQyNV90YWJsZSRDb25mUmFuaw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA9PSAobmhsMjQyNV90YWJsZSRDb25mUmFua1ttYXRjaChuaGwyNDI1X3RhYmxlJFRlYW1bd2hpY2gobmhsMjQyNV90YWJsZSRQdHMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID09IChtYXgobmhsMjQyNV90YWJsZSRQdHNbd2hpY2gobmhsMjQyNV90YWJsZSRDb25mZXJlbmNlID09ICJFIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmIG5obDI0MjVfdGFibGUkUGxheW9mZnMgPT0gRkFMU0UpXSkpDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAmIG5obDI0MjVfdGFibGUkQ29uZmVyZW5jZSA9PSAiRSIpXVsxXSxuaGwyNDI1X3RhYmxlJFRlYW0pXSktMSldKSAvMikNCmBgYA0KDQpIaWRlb3VzLg0KDQoqSWYgeW91IHdhbnQgdG8gc2VlIGhvdyB0aGlzIGFsbCBlbmRzIHVwIGZvciB0aGUgcmVzdCBvZiB0aGUgc2Vhc29uLCBJJ2xsIGJlIHBvc3RpbmcgdXBkYXRlcyBbb24gbXkgVHdpdHRlcl0oaHR0cHM6Ly94LmNvbS9mdXp6eWJsdWVyYWluKSAoYW5kIHllcywgaXQgaXMgc3RpbGwgVHdpdHRlciwgcmVnYXJkbGVzcyBvZiB3aGF0IHJhY2lzdCBiaWxsaW9uYWlyZXMgd2FudCB0byBjYWxsIGl0KS4qDQo=