1 Introduction

The Net Promoter Score (NPS) is a trusted metric used by countless business to decide whether customers are Detractors or Promoters of the business. There is extensive resources online to justify the use of this metric, including on sites such as qualtrics.com, wikipedia.org and netpromoter.com.

The calculation of the NPS value is quite simple: NPS = %Promoters − %Detractors

There are two options that can be used to visualise the NPS Value:

  1. First is in the true essence of the metric, which is a bar plot, the the NPS score displayed on the plot.
  2. Second is to display a density plot for the data.

This Vignette is provides a helpful guide to visualise the NPS data for both these methods, using ggplot2 in the R programming language.

2 Set Up

2.1 Load the Packages

To begin, the environment must be set up. The first step is to load the packages that will be used.

2.2 Generate the Data

The next step is to generate the NPS data. For this, the sample() function is used to generate 1, 000 values between 6 and 10. The first 20 values are printed below for convenience.

Noting that this dummy data is generated by using a function. However, this data can be collected from any survey software, and fed in to this data pipeline at this point. The only prerequisite is that the data be a single vector of numbers that are all integers between 0 and 10, inclusive.

##  [1]  9 10  9  6  8  9 10  6 10 10  7 10 10 10  9  6  9  9  9  7

2.3 Check the Data

Next, to confirm that the data looks correct, the descriptive statistics are calculated for the generated data. For this, the summarise_all() function is used to calculate some key statistics.

Statistic Value
Min 6.00
Max 10.00
Mean 9.09
Standard Deviation 1.10
Count 1000.00

3 Option One: Visualise Bar Plot

3.1 Summarise NPS Data

To calculate the NPS score, the following steps are performed on the data:

  1. Coerce the data in to a data.frame;
  2. Add a Category variable to determine the category of the score;
  3. Count the number of scores in each category;
  4. Calculate the percentage of the different categories;
  5. Calculate the NPS score; and
  6. Coerce it again in to a data.frame to add a new variable called NPS.

Once generated, the data is ready to be visualised.

Score Name
7.86 NPS

3.2 Generate BarPlot Data Frame

In order to properly visualise the NPS score, an empty data frame is generated, with one row being each of the possible scores. The reason for this is to allow for the Bar Plot to be adequately displayed. The way that this data is generated is by using the seq() function to create an ordered sequence of numbers from 0 to 10, incrementing by 1 each time.

NPS Name Category
0 NPS Detractors
1 NPS Detractors
2 NPS Detractors
3 NPS Detractors
4 NPS Detractors
5 NPS Detractors
6 NPS Detractors
7 NPS Passives
8 NPS Passives
9 NPS Promoters
10 NPS Promoters

3.3 Join them all together

Next, the NPS score and the NPS frame are joined together, so that the NPS score is replicated over each line. This is done by using the left_join() function, and using Name as the joining variable between the two frames.

NPS Name Category Score
0 NPS Detractors 7.86
1 NPS Detractors 7.86
2 NPS Detractors 7.86
3 NPS Detractors 7.86
4 NPS Detractors 7.86
5 NPS Detractors 7.86
6 NPS Detractors 7.86
7 NPS Passives 7.86
8 NPS Passives 7.86
9 NPS Promoters 7.86
10 NPS Promoters 7.86

3.4 Plot the final output

Finally, the result is plotted using the ggplot() function and the following layers: geom_bar(), geom_point(), and geom_label().

The following steps were followed:

  1. Pipe the FinalData data frame in to the ggplot() function, using the Name variable as the sole aesthetic variable.

  2. Add a geom_bar() layer, using the Category variable to determine which colours to use to fill the column, then add a border around the categories using the colour ‘DarkGrey’, and give it a width of 0.5 units.

  3. Add a geom_point() layer, using the following arguments:

    1. data’ is created using an anonymous function. This is so that the data used by the ggplot() function can be manipulated, without using another external variable. The manipulation was effectively used to create a single NPS score which can be used in this layer.
    2. aes’ is the aesthetic used for the y axis; which in this instance is the NPS score. This is used to determine where on the plot the point should be placed.
    3. shape’ is a plus symbol, which is used to determine the exact location of the point, as convenient for the human eye to see.
    4. size’ is the size of the symbol, which in this instance is 25 units.
  4. Add a geom_label() layer, using the following arguments:

    1. data’ is again manipulated to determine the same value as used in geom_point().
    2. stat’ is the statistic used to calculate the position of the label; which in this instance is the value identity, which effectively tells ggplot to use the own identity of the data, and not calculate any other statistic for the data.
    3. aes’ is used to determine that the label should be the value from the Score variable, and that it should be placed at the Score position on the y axis. Effectively, this aesthetic is used to decide what the value of the label should be, and where it should be place on the plot.
    4. size’ is used to determine the size of the label; which in this instance is 5 units.
  5. Determine how many breaks should be used, and the limits of the y axis, using the scale_y_continuous() layer.

  6. Determine the colours that should be used in the three different Categories, using the scale_fill_manual() layer.

  7. Hide the axis text for the y axis, using the axis.text.y.left argument of the theme() layer.

  8. Flip the coordinates of the plot, so that it appears to be a bar from left to right, using the coord_flip() layer.

  9. Label the axes, using the labs() layer, to ensure that the correct information is displayed in the correct positions.

4 Option Two: Visualise Density Plot

4.1 Generate DensityPlot Data Frame

In order to visualise the Density Plot, the data does not need to be summarised, but it is better to remain in its raw form. It does, however, need to undergo the following manipulations:

  1. Coerce in to a data.frame; and
  2. Add the Category variable.
Score Category
9 Promoters
10 Promoters
9 Promoters
6 Detractors
8 Passives
9 Promoters
10 Promoters
6 Detractors
10 Promoters
10 Promoters

4.2 Visualise the DensityPlot data

Once the Density data frame is generated, it can be visualised through ggplot(), using the following aesthetics: geom_bar() and geom_density().

The following steps were used:

  1. Pipe the FinalData data frame in to the ggplot() function, using the Score variable as the sole aesthetic.
  2. Add a geom_bar() layer, using the Category variable to determine the colouers to use to fill the column, then add a border around the categories using the colour ‘DarkGrey’, and give it a transparency value of 0.3.
  3. Add a geom_density() layer, using an aesthetic y value to determine that this value should be a ‘count’ of the data, not a ‘density’ of the data, then give it a ‘Blue’ colour, and increase the size to 1 unit.
  4. Determine the colours that should be used for the three different Categories, using the scale_fill_manual() layer.
  5. Determine the breaks and the limits of the x axis, using the scale_x_continuous() layer.
  6. Remove the legend from the plot, using the theme() layer.
  7. Add labels for the plot, using the labs() layer.

5 Conclusion

As seen, the Net Promoter Score is a useful metric to see the percentage of customers who are Promoters, Passives or Detractors of the business. This metric can be visualised in a simple BarPlot, with a static value displayed on the chart, or it can be visualised as a DensityPlot, showing the proportion of customers in the different categories. Both of these methodologies are provided in this Vignette, with a step-by-step guide from data manipulation to plotting.

6 Post Script

Publications: This report is also published on the following sites:

  1. RPubs: RPubs/chrimaho/PlottingNPS
  2. GitHub: GitHub/chrimaho/PlottingNPS
  3. Medium: Medium/chrimaho/PlottingNPS

Change Log: This publication was modified on the following dates:

  1. 29/Jan/2020: Original Publication Date
LS0tDQp0aXRsZTogJ1Bsb3R0aW5nIE5QUyBEYXRhJw0Kc3VidGl0bGU6ICdBIFN0ZXAtYnktU3RlcCBXYWxrdGhyb3VnaCBmb3IgUGxvdHRpbmcgTlBTIERhdGEgdXNpbmcgR0dQbG90Jw0KYXV0aG9yOiAnQXV0aG9yOiBbQ2hyaXMgTWFob25leV0oaHR0cHM6Ly93d3cubGlua2VkaW4uY29tL2luL2NocmltYWhvLyknDQpkYXRlOiAnUHVibGlzaGVkOiAyOS9KYW4vMjAyMCcNCnRvYy10aXRsZTogQ29udGVudHMNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBoaWdobGlnaHQ6IGhhZGRvY2sNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRlbXBsYXRlOiBkZWZhdWx0X3RvYy5odG1sDQogICAgdGhlbWU6IGx1bWVuDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IG5vDQogICAgaW5jbHVkZXM6DQogICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sDQogICAgICBhZnRlcl9ib2R5OiBmb290ZXIuaHRtbA0KICBodG1sX25vdGVib29rOg0KICAgIGhpZ2hsaWdodDogaGFkZG9jaw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IG5vDQogICAgaW5jbHVkZXM6DQogICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sDQogICAgICBhZnRlcl9ib2R5OiBmb290ZXIuaHRtbA0KLS0tDQoNCjxzdHlsZT4NCi5tYXRoIHsNCiAgICA8IS0tIGZvbnQtc2l6ZTogMTIwJTsgLS0+DQogICAgZm9udC1zdHlsZTogbm9ybWFsOw0KICAgIGZvbnQtZmFtaWx5OiAiQ2FtYnJpYSBNYXRoIjsNCn0NCi5jb2x1bW4gew0KICAgIGZsb2F0OiBsZWZ0Ow0KICAgIHdpZHRoOiA1MCU7DQogICAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7DQp9DQoucm93OmFmdGVyIHsNCiAgICBjb250ZW50OiAiIjsNCiAgICBkaXNwbGF5OiB0YWJsZTsNCiAgICBjbGVhcjogYm90aDsNCn0NCmgxLCAuaDEgew0KICAgIG1hcmdpbi10b3A6IDQwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMiwgLmgyIHsNCiAgICBtYXJnaW4tdG9wOiA0MHB4Ow0KICAgIG1hcmdpbi1sZWZ0OiA0MHB4Ow0KfQ0KPC9zdHlsZT4NCg0KDQo8IS0tIEFja25vd2xlZGdlbWVudHM6IC0tPg0KDQoNCg0KPCEtLSBTZXQgVXAgRW52aXJvbm1lbnQgLS0+DQoNCmBgYHtyIFNFVCBGdW5jdGlvbiwgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQ0KIyBEZWZpbmUgZnVuY3Rpb24gdG8gbG9hZCBwYWNrYWdlcyAtLS0tDQpMb2FkUGFja2FnZXMgPC0gZnVuY3Rpb24ocGFja2FnZXMsIGluc3RhbGw9RkFMU0UpIHsNCiAgICANCiAgICAjIElucHV0Og0KICAgICMgLSAncGFja2FnZXMnIDogQW4gYXRvbWljIHN0cmluZyBvciBhIHN0cmluZyB2ZWN0b3Igb2YgdGhlIGxpc3Qgb2YgcGFja2FnZXMgdG8gbG9hZC4NCiAgICAjIC0gJ2luc3RhbGwnICA6IEEgYm9vbGVhbiB2YWx1ZSBmb3Igd2hldGhlciBvciBub3QgdG8gaW5zdGFsbCB0aGUgcGFja2FnZXMgdGhhdCBhcmUgbWlzc2luZy4NCiAgICANCiAgICAjIE91dHB1dDoNCiAgICAjIC0gQSBsb2dpY2FsIHJlc3VsdCAoVFJVRSBvZiBGQUxTRSkgZm9yIGlmIHRoZXkgd2VyZSBzdWNjZXNzZnVsbHkgbG9hZGVkLg0KICAgIA0KICAgICMgVmFsaWRhdGlvbnM6DQogICAgc3RvcGlmbm90KGlzLmNoYXJhY3RlcihwYWNrYWdlcykpDQogICAgc3RvcGlmbm90KGlzLmxvZ2ljYWwoaW5zdGFsbCkpDQogICAgDQogICAgIyBSZW1vdmUgYWxsIHBhY2thZ2VzLiBOb3RlOiBUaGUgc3VwcHJlc3Npb24gZnVuY3Rpb25zIGFyZSB0byBsaW1pdCB0aGUgYW1vdW50IG9mIHByaW50ZWQgb3V0cHV0Lg0KICAgIGZvciAocGFja2FnZSBpbiAucGFja2FnZXMoKSkgew0KICAgICAgICBpZiAoIXBhY2thZ2UgJWluJSBjKCJwYXJhbGxlbCIsICJzdGF0cyIsICJncmFwaGljcyIsICJnckRldmljZXMiLCAiZGF0YXNldHMiLCAidXRpbHMiLCAibWV0aG9kcyIsICJiYXNlIikpIHsgI1RIRVNFIFBBQ0tBR0VTIEFSRSBQQVJUIE9GIEJBU0UhISBZT1UgQ0FOTk9UIFJFTU9WRSBUSEVNISEgQnV0IHlvdSBjYW4gcmVtb3ZlIGV2ZXJ5dGhpbmcgZWxzZS4uLg0KICAgICAgICAgICAgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzICggDQogICAgICAgICAgICAgICAgc3VwcHJlc3NNZXNzYWdlcyAoIA0KICAgICAgICAgICAgICAgICAgICBzdXBwcmVzc1dhcm5pbmdzICggDQogICAgICAgICAgICAgICAgICAgICAgICBkZXRhY2ggKCBwYXN0ZTAoInBhY2thZ2U6IiwgcGFja2FnZSkgI1RoZSBgZGV0YWNoKClgIGZ1bmN0aW9uIGlzIGxpa2UgdGhlIHJldmVyc2Ugb2YgYGxpYnJhcnkoKWAgb3IgYHJlcXVpcmUoKWAuDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHVubG9hZCA9IFRSVUUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFDQogICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICAgICAgICApDQogICAgICAgICAgICApDQogICAgICAgIH0NCiAgICB9DQogICAgDQogICAgIyBJbnN0YWxsIGFsbCBkZWZpbmVkIHBhY2thZ2VzDQogICAgaWYgKGluc3RhbGw9PVRSVUUpIHsNCiAgICAgICAgZm9yIChwYWNrYWdlIGluIHBhY2thZ2VzKSB7DQogICAgICAgICAgICBpZiAoIXBhY2thZ2UgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkgeyAjVGhlIGBpbnN0YWxsZWQucGFja2FnZXMoKWAgZnVuY3Rpb24gIHJldHVybnMgYSB2ZWN0b3Igb2YgYWxsIHRoZSBpbnN0YWxsZWQgcGFja2FnZXMuLi4NCiAgICAgICAgICAgICAgICBpbnN0YWxsLnBhY2thZ2VzICggcGFja2FnZQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICwgcXVpZXQgPSBUUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgLCB2ZXJib3NlID0gRkFMU0UNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLCBkZXBlbmRlbmNpZXMgPSBUUlVFDQogICAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgfQ0KICAgIA0KICAgICMgTG9hZCBhbGwgZGVmaW5lZCBwYWNrYWdlcw0KICAgIGZvciAocGFja2FnZSBpbiBwYWNrYWdlcykgeyAjTmVlZCB0byBsb29wIHRocm91Z2ggYSBzZWNvbmQgdGltZSBiZWNhdXNlIGl0IGRvZXMgZnVubnkgdGhpbmdzIGlmIHlvdSBjb21iaW5lIHRoZSBgaW5zdGFsbC5wYWNrYWdlcygpYCBhbmQgYGxpYnJhcnkoKWAgc3RlcHMgaW4gdG8gb25lLg0KICAgICAgICBpZiAoIXBhY2thZ2UgJWluJSAucGFja2FnZXMoKSkgeyAjYC5wYWNrYWdlcygpYCByZXR1cm5zIGEgdmVjdG9yIG9mIGFsbCB0aGUgbG9hZGVkIHBhY2thZ2VzLi4uDQogICAgICAgICAgICBzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMgKA0KICAgICAgICAgICAgICAgIGxpYnJhcnkgKCBwYWNrYWdlDQogICAgICAgICAgICAgICAgICAgICAgICAgICwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICwgcXVpZXRseSA9IFRSVUUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgLCB3YXJuLmNvbmZsaWN0cyA9IEZBTFNFDQogICAgICAgICAgICAgICAgICAgICAgICAgICwgdmVyYm9zZSA9IEZBTFNFDQogICAgICAgICAgICAgICAgKQ0KICAgICAgICAgICAgKQ0KICAgICAgICB9DQogICAgICAgIGlmICghcGFja2FnZSAlaW4lIC5wYWNrYWdlcygpKSB7DQogICAgICAgICAgICBzdG9wKHBhc3RlMCgiUGFja2FnZSAnIiwgcGFja2FnZSwgIicgd2FzIG5vdCBsb2FkZWQgcHJvcGVybHkuIikpDQogICAgICAgIH0NCiAgICB9DQogICAgDQogICAgIyBSZXR1cm4NCiAgICByZXR1cm4oVFJVRSkNCiAgICANCn0NCg0KIyMjIyBUaHJlZSBzdHJpbmcgbWFuaXB1bGF0aW9uIGZ1bmN0aW9ucyAoTEVGVCxSSUdIVCxNSUQpICMjIyMNCnN0cl9sZWZ0IDwtIGZ1bmN0aW9uKHN0cmluZywgbnVtX2NoYXJzKSB7DQogICAgDQogICAgIyBJbnB1dDoNCiAgICAjIC0gJ3N0cmluZycgaXMgdGhlIHRleHQgc3RyaW5nIHlvdSB3YW50IHRvIHNlbGVjdCBmcm9tOyBtdXN0IGJlIGFuIGNoYXJhY3RlciB0eXBlLg0KICAgICMgLSAnbnVtX2NoYXJzJyBpcyB0aGUgbnVtYmVyIG9mIGNoYXJhY3RlcnMgdGhhdCB5b3Ugd2FudCB0byBzZWxlY3Q7IG11c3QgYmUgYW4gYXRvbWljIG51bWVyaWMgdHlwZS4NCiAgICANCiAgICAjIE91dHB1dDoNCiAgICAjIC0gQSB0ZXh0IHN0cmluZyBvZiBsZW5ndGggJ251bV9jaGFycycgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgbGVmdCBtb3N0IG51bWJlciBvZiBjaGFyYWN0ZXJzIGZyb20gdGhlICdzdHJpbmcnIG9wdGlvbi4NCiAgICANCiAgICAjIFZhbGlkYXRpb25zOg0KICAgIHN0b3BpZm5vdChpcy5jaGFyYWN0ZXIoc3RyaW5nKSkNCiAgICBzdG9waWZub3QoaXMubnVtZXJpYyhudW1fY2hhcnMpKQ0KICAgIHN0b3BpZm5vdChpcy5hdG9taWMobnVtX2NoYXJzKSkNCiAgICANCiAgICAjIERvIHdvcmsNCiAgICByZXR1cm4gPC0gc3Vic3RyKHN0cmluZywgMSwgbnVtX2NoYXJzKQ0KICAgIA0KICAgICMgUmV0dXJuDQogICAgcmV0dXJuKHJldHVybikNCiAgICANCn0NCg0Kc3RyX21pZCA8LSBmdW5jdGlvbihzdHJpbmcsIHN0YXJ0X251bSwgbnVtX2NoYXJzKSB7DQogICAgDQogICAgIyBJbnB1dDoNCiAgICAjIC0gJ3N0cmluZycgaXMgdGhlIHRleHQgc3RyaW5nIHlvdSB3YW50IHRvIHNlbGVjdCBmcm9tOyBtdXN0IGJlIGFuIGF0b3BpYyBzdHJpbmcuDQogICAgIyAtICdzdGFydF9udW0nIGlzIHRoZSBzdGFydGluZyBwb3NpdGlvbiBvZiB0aGUgbWlkLXRleHQgc3RyaW5nIHlvdSB3YW50IHRvIHNlbGVjdCBmcm9tOyBtdXN0IGJlIGFuIGF0b21pYyBudW1lcmljIHR5cGUuDQogICAgIyAtICdudW1fY2hhcnMnIGlzIHRoZSBudW1iZXIgb2YgY2hhcmFjdGVycyB0aGF0IHlvdSB3YW50IHRvIHNlbGVjdDsgbXVzdCBiZSBhbiBhdG9taWMgbnVtZXJpYyB0eXBlLg0KICAgIA0KICAgICMgT3V0cHV0Og0KICAgICMgLSBBIHRleHQgc3RyaW5nIG9mIGxlbmd0aCAnbnVtX2NoYXJzJyB0aGF0IGNvcnJlc3BvbmRzIHRvIHRoZSBjaGFyYWN0ZXJzIGZyb20gdGhlICdzdGFydF9udW0nIHN0YXJ0aW5nIHBvc2l0aW9uIGZyb20gdGhlICdzdHJpbmcnIG9wdGlvbi4NCiAgICANCiAgICAjIFZhbGlkYXRpb25zOg0KICAgIHN0b3BpZm5vdChpcy5jaGFyYWN0ZXIoc3RyaW5nKSkNCiAgICBzdG9waWZub3QoaXMubnVtZXJpYyhzdGFydF9udW0pKQ0KICAgIHN0b3BpZm5vdChpcy5hdG9taWMoc3RhcnRfbnVtKSkNCiAgICBzdG9waWZub3QoaXMubnVtZXJpYyhudW1fY2hhcnMpKQ0KICAgIHN0b3BpZm5vdChpcy5hdG9taWMobnVtX2NoYXJzKSkNCiAgICANCiAgICAjIERvIHdvcmsNCiAgICByZXR1cm4gPC0gc3Vic3RyKHN0cmluZywgc3RhcnRfbnVtLCBzdGFydF9udW0gKyBudW1fY2hhcnMgLSAxKQ0KICAgIA0KICAgICMgUmV0dXJuDQogICAgcmV0dXJuKHJldHVybikNCiAgICANCn0NCg0Kc3RyX3JpZ2h0IDwtIGZ1bmN0aW9uKHN0cmluZywgbnVtX2NoYXJzKSB7DQogICAgDQogICAgIyBJbnB1dDoNCiAgICAjIC0gJ3N0cmluZycgaXMgdGhlIHRleHQgc3RyaW5nIHlvdSB3YW50IHRvIHNlbGVjdCBmcm9tOyBtdXN0IGJlIGFuIGNoYXJhY3RlciB0eXBlLg0KICAgICMgLSAnbnVtX2NoYXJzJyBpcyB0aGUgbnVtYmVyIG9mIGNoYXJhY3RlcnMgdGhhdCB5b3Ugd2FudCB0byBzZWxlY3Q7IG11c3QgYmUgYW4gYXRvbWljIG51bWVyaWMgdHlwZS4NCiAgICANCiAgICAjIE91dHB1dDoNCiAgICAjIC0gQSB0ZXh0IHN0cmluZyBvZiBsZW5ndGggJ251bV9jaGFycycgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgcmlnaHQgbW9zdCBudW1iZXIgb2YgY2hhcmFjdGVycyBmcm9tIHRoZSAnc3RyaW5nJyBvcHRpb24uDQogICAgDQogICAgIyBWYWxpZGF0aW9uczoNCiAgICBzdG9waWZub3QoaXMuY2hhcmFjdGVyKHN0cmluZykpDQogICAgc3RvcGlmbm90KGlzLm51bWVyaWMobnVtX2NoYXJzKSkNCiAgICBzdG9waWZub3QoaXMuYXRvbWljKG51bV9jaGFycykpDQogICAgDQogICAgIyBEbyB3b3JrDQogICAgcmV0dXJuIDwtIHN1YnN0cihzdHJpbmcsIG5jaGFyKHN0cmluZykgLSAobnVtX2NoYXJzIC0gMSksIG5jaGFyKHN0cmluZykpDQogICAgDQogICAgIyBSZXR1cm4NCiAgICByZXR1cm4ocmV0dXJuKQ0KICAgIA0KfQ0KDQpgYGANCg0KDQpgYGB7ciBMT0FEIFBhY2thZ2VzIHdpdGggRnVuY3Rpb24sIGVjaG89RkFMU0UsIGV2YWw9VFJVRSwgcmVzdWx0cz0iaGlkZSIsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpMb2FkUGFja2FnZXMoYygiZ2dwbG90MiIsICJkcGx5ciIsICJtYWdyaXR0ciIsICJ0aWR5ciIsICJrbml0ciIsICJrYWJsZUV4dHJhIiwgInNjYWxlcyIpKQ0KYGBgDQoNCg0KYGBge3IgU0VUIERlZmF1bHRzLCBlY2hvPUZBTFNFLCBldmFsPVRSVUV9DQojIFNldCBEZWZhdWx0IHRoZW1lcw0KdGhlbWVfc2V0KHRoZW1lX2J3KCkpDQp0aGVtZV91cGRhdGUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUpDQogICAgICAgICAgICAscGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUpDQogICAgICAgICAgICApDQoNCiMgU2V0IERlZmF1bHQgdGFibGUgYW5kIGZpZ3VyZSBzaXplcw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHJvd3MucHJpbnQ9MjAwLCBjb2xzLnByaW50PTMwLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NykNCg0KIyBTZXQgRGVmYXVsdCByb3VuZGluZyBsZW5ndGgNCm9wdGlvbnMoZGlnaXRzID0gNCkNCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KYGBgDQoNCg0KPCEtLSBSZXBvcnQgLS0+DQoNCiMgSW50cm9kdWN0aW9uIHsjSW50cm9kdWN0aW9ufQ0KDQpUaGUgTmV0IFByb21vdGVyIFNjb3JlIChOUFMpIGlzIGEgdHJ1c3RlZCBtZXRyaWMgdXNlZCBieSBjb3VudGxlc3MgYnVzaW5lc3MgdG8gZGVjaWRlIHdoZXRoZXIgY3VzdG9tZXJzIGFyZSBEZXRyYWN0b3JzIG9yIFByb21vdGVycyBvZiB0aGUgYnVzaW5lc3MuIFRoZXJlIGlzIGV4dGVuc2l2ZSByZXNvdXJjZXMgb25saW5lIHRvIGp1c3RpZnkgdGhlIHVzZSBvZiB0aGlzIG1ldHJpYywgaW5jbHVkaW5nIG9uIHNpdGVzIHN1Y2ggYXMgW3F1YWx0cmljcy5jb21dKGh0dHBzOi8vd3d3LnF1YWx0cmljcy5jb20vZXhwZXJpZW5jZS1tYW5hZ2VtZW50L2N1c3RvbWVyL25ldC1wcm9tb3Rlci1zY29yZS8pLCBbd2lraXBlZGlhLm9yZ10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTmV0X1Byb21vdGVyKSBhbmQgW25ldHByb21vdGVyLmNvbV0oaHR0cHM6Ly93d3cubmV0cHJvbW90ZXIuY29tL2tub3cvKS4NCg0KVGhlIGNhbGN1bGF0aW9uIG9mIHRoZSBOUFMgdmFsdWUgaXMgcXVpdGUgc2ltcGxlOiAkTlBTID0gXCVQcm9tb3RlcnMgLSBcJURldHJhY3RvcnMkDQoNCg0KVGhlcmUgYXJlIHR3byBvcHRpb25zIHRoYXQgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXNlIHRoZSBOUFMgVmFsdWU6DQoNCjEuIEZpcnN0IGlzIGluIHRoZSB0cnVlIGVzc2VuY2Ugb2YgdGhlIG1ldHJpYywgd2hpY2ggaXMgYSBiYXIgcGxvdCwgdGhlIHRoZSBOUFMgc2NvcmUgZGlzcGxheWVkIG9uIHRoZSBwbG90Lg0KMi4gU2Vjb25kIGlzIHRvIGRpc3BsYXkgYSBkZW5zaXR5IHBsb3QgZm9yIHRoZSBkYXRhLg0KDQpUaGlzIFZpZ25ldHRlIGlzIHByb3ZpZGVzIGEgaGVscGZ1bCBndWlkZSB0byB2aXN1YWxpc2UgdGhlIE5QUyBkYXRhIGZvciBib3RoIHRoZXNlIG1ldGhvZHMsIHVzaW5nIFtgZ2dwbG90MmBdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykgaW4gdGhlIFtgUmBdKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcvKSBwcm9ncmFtbWluZyBsYW5ndWFnZS4NCg0KDQojIFNldCBVcCB7I1NldFVwfQ0KDQojIyBMb2FkIHRoZSBQYWNrYWdlcyB7I0xvYWRQYWNrYWdlc30NCg0KVG8gYmVnaW4sIHRoZSBlbnZpcm9ubWVudCBtdXN0IGJlIHNldCB1cC4gVGhlIGZpcnN0IHN0ZXAgaXMgdG8gbG9hZCB0aGUgcGFja2FnZXMgdGhhdCB3aWxsIGJlIHVzZWQuDQoNCmBgYHtyIExPQUQgUGFja2FnZXMsIGVjaG89VFJVRSwgZXZhbD1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeSh0aWR5cikNCmBgYA0KDQoNCiMjIEdlbmVyYXRlIHRoZSBEYXRhIHsjR2VuZXJhdGVEYXRhfQ0KDQpUaGUgbmV4dCBzdGVwIGlzIHRvIGdlbmVyYXRlIHRoZSBOUFMgZGF0YS4gRm9yIHRoaXMsIHRoZSBgc2FtcGxlKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gZ2VuZXJhdGUgJDEsMDAwJCB2YWx1ZXMgYmV0d2VlbiAkNiQgYW5kICQxMCQuIFRoZSBmaXJzdCAkMjAkIHZhbHVlcyBhcmUgcHJpbnRlZCBiZWxvdyBmb3IgY29udmVuaWVuY2UuDQoNCk5vdGluZyB0aGF0IHRoaXMgZHVtbXkgZGF0YSBpcyBnZW5lcmF0ZWQgYnkgdXNpbmcgYSBmdW5jdGlvbi4gSG93ZXZlciwgdGhpcyBkYXRhIGNhbiBiZSBjb2xsZWN0ZWQgZnJvbSBhbnkgc3VydmV5IHNvZnR3YXJlLCBhbmQgZmVkIGluIHRvIHRoaXMgZGF0YSBwaXBlbGluZSBhdCB0aGlzIHBvaW50LiBUaGUgb25seSBwcmVyZXF1aXNpdGUgaXMgdGhhdCB0aGUgZGF0YSBiZSBhIHNpbmdsZSB2ZWN0b3Igb2YgbnVtYmVycyB0aGF0IGFyZSBhbGwgaW50ZWdlcnMgYmV0d2VlbiAkMCQgYW5kICQxMCQsIGluY2x1c2l2ZS4NCg0KYGBge3IgR0VOIE5QUyBEYXRhLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0NCnNldC5zZWVkKDEyMykNCk5wc0RhdGEgPC0gc2FtcGxlKHg9NjoxMCwgc2l6ZT0xMDAwLCByZXBsYWNlPVRSVUUsIHByb2I9YygwLjA2LCAwLjA1LCAwLjA1LCAwLjQyLCAwLjQyKSkNCk5wc0RhdGEgJT4lIGhlYWQoMjApICU+JSBwcmludCgpDQpgYGANCg0KDQojIyBDaGVjayB0aGUgRGF0YSB7I0NoZWNrRGF0YX0NCg0KTmV4dCwgdG8gY29uZmlybSB0aGF0IHRoZSBkYXRhIGxvb2tzIGNvcnJlY3QsIHRoZSBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIGFyZSBjYWxjdWxhdGVkIGZvciB0aGUgZ2VuZXJhdGVkIGRhdGEuIEZvciB0aGlzLCB0aGUgYHN1bW1hcmlzZV9hbGwoKWAgZnVuY3Rpb24gaXMgdXNlZCB0byBjYWxjdWxhdGUgc29tZSBrZXkgc3RhdGlzdGljcy4NCg0KYGBge3IgR0VOIERlc2NyaXB0aW9uIG9mIE5QUyBEYXRhLCBlY2hvPVRSVUUsIGV2YWw9RkFMU0V9DQpOcHNEYXRhICU+JQ0KICAgIGRhdGEuZnJhbWUoKSAlPiUgDQogICAgc3VtbWFyaXNlX2FsbChsaXN0KE1pbj1taW4sIE1heD1tYXgsIE1lYW49bWVhbiwgYFN0YW5kYXJkIERldmlhdGlvbmA9c2QsIENvdW50PU5ST1cpKSAlPiUNCiAgICBnYXRoZXIoIlN0YXRpc3RpYyIsICJWYWx1ZSIpICU+JSANCiAgICBtdXRhdGVfYXQoIlZhbHVlIiwgcm91bmQsIDIpDQpgYGANCg0KYGBge3IgUkVWSUVXIERlc2NyaXB0aW9uIG9mIE5QUyBEYXRhLCBlY2hvPUZBTFNFLCBldmFsPVRSVUV9DQpOcHNEYXRhICU+JQ0KICAgIGRhdGEuZnJhbWUoKSAlPiUgDQogICAgc3VtbWFyaXNlX2FsbChsaXN0KE1pbj1taW4sIE1heD1tYXgsIE1lYW49bWVhbiwgYFN0YW5kYXJkIERldmlhdGlvbmA9c2QsIENvdW50PU5ST1cpKSAlPiUNCiAgICBnYXRoZXIoIlN0YXRpc3RpYyIsICJWYWx1ZSIpICU+JSANCiAgICBtdXRhdGVfYXQoIlZhbHVlIiwgcm91bmQsIDIpICU+JSANCiAgICBrYWJsZShhbGlnbj0ibCIpICU+JSANCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zPWMoInN0cmlwZWQiLCJib3JkZXJlZCIsImNvbmRlbnNlZCIpDQogICAgICAgICAgICAgICAgICxmdWxsX3dpZHRoPUZBTFNFDQogICAgICAgICAgICAgICAgICxwb3NpdGlvbj0ibGVmdCINCiAgICAgICAgICAgICAgICAgKSAlPiUgDQogICAgKGZ1bmN0aW9uKHgpew0KICAgICAgICB4ICU+JSBzYXZlX2thYmxlKCJJbWFnZXMvRGVzY3JpcHRpb25PZk5wc0RhdGEucG5nIikNCiAgICAgICAgeCAlPiUgcmV0dXJuKCkNCiAgICB9KQ0KYGBgDQoNCg0KIyBPcHRpb24gT25lOiBWaXN1YWxpc2UgQmFyIFBsb3QgeyNCYXJQbG90fQ0KDQojIyBTdW1tYXJpc2UgTlBTIERhdGEgeyNHZW5lcmF0ZVNjb3JlfQ0KDQpUbyBjYWxjdWxhdGUgdGhlIE5QUyBzY29yZSwgdGhlIGZvbGxvd2luZyBzdGVwcyBhcmUgcGVyZm9ybWVkIG9uIHRoZSBkYXRhOg0KDQoxLiBDb2VyY2UgdGhlIGRhdGEgaW4gdG8gYSBgZGF0YS5mcmFtZWA7DQoxLiBBZGQgYSBgQ2F0ZWdvcnlgIHZhcmlhYmxlIHRvIGRldGVybWluZSB0aGUgY2F0ZWdvcnkgb2YgdGhlIHNjb3JlOw0KMS4gQ291bnQgdGhlIG51bWJlciBvZiBzY29yZXMgaW4gZWFjaCBjYXRlZ29yeTsNCjEuIENhbGN1bGF0ZSB0aGUgcGVyY2VudGFnZSBvZiB0aGUgZGlmZmVyZW50IGNhdGVnb3JpZXM7DQoxLiBDYWxjdWxhdGUgdGhlIE5QUyBzY29yZTsgYW5kDQoxLiBDb2VyY2UgaXQgYWdhaW4gaW4gdG8gYSBgZGF0YS5mcmFtZWAgdG8gYWRkIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCBgTlBTYC4NCg0KT25jZSBnZW5lcmF0ZWQsIHRoZSBkYXRhIGlzIHJlYWR5IHRvIGJlIHZpc3VhbGlzZWQuDQoNCmBgYHtyIEdFTiBCYXIgU3VtbWFyeSBNZWFuLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0NCk5wc1Njb3JlIDwtIE5wc0RhdGEgJT4lIA0KICAgIGRhdGEuZnJhbWUoU2NvcmU9LikgJT4lIA0KICAgIG11dGF0ZShDYXRlZ29yeT0iUHJvbW90ZXJzIg0KICAgICAgICAgICxDYXRlZ29yeT1pZmVsc2UoU2NvcmU8PTgsICJQYXNzaXZlcyIsIENhdGVnb3J5KQ0KICAgICAgICAgICxDYXRlZ29yeT1pZmVsc2UoU2NvcmU8PTYsICJEZXRyYWN0b3JzIiwgQ2F0ZWdvcnkpDQogICAgICAgICAgLENhdGVnb3J5PWZhY3RvcihDYXRlZ29yeSwgbGV2ZWxzPWMoIlByb21vdGVycyIsICJQYXNzaXZlcyIsICJEZXRyYWN0b3JzIikpDQogICAgICAgICAgKSAlPiUgDQogICAgY291bnQoQ2F0ZWdvcnksIG5hbWU9IkNvdW50IikgJT4lIA0KICAgIG11dGF0ZShQZXJjZW50YWdlPUNvdW50L3N1bShDb3VudCkpICU+JSANCiAgICAoZnVuY3Rpb24oeCl7DQogICAgICAgIFBybyA8LSB4ICU+JSBmaWx0ZXIoQ2F0ZWdvcnk9PSJQcm9tb3RlcnMiKSAlPiUgc2VsZWN0KFBlcmNlbnRhZ2UpICU+JSBwdWxsKCkNCiAgICAgICAgRGV0IDwtIHggJT4lIGZpbHRlcihDYXRlZ29yeT09IkRldHJhY3RvcnMiKSAlPiUgc2VsZWN0KFBlcmNlbnRhZ2UpICU+JSBwdWxsKCkNCiAgICAgICAgcmV0dXJuKChQcm8tRGV0KSoxMCkNCiAgICB9KSAlPiUgDQogICAgZGF0YS5mcmFtZShTY29yZT0uKSAlPiUgDQogICAgbXV0YXRlKE5hbWU9Ik5QUyIpDQpgYGANCg0KYGBge3IgUkVWSUVXIEJhciBTdW1tYXJ5IE1lYW4sIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0NCk5wc1Njb3JlICU+JSANCiAgICBrYWJsZShhbGlnbj0ibCIpICU+JSANCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zPWMoInN0cmlwZWQiLCJib3JkZXJlZCIsImNvbmRlbnNlZCIpDQogICAgICAgICAgICAgICAgICxmdWxsX3dpZHRoPUZBTFNFDQogICAgICAgICAgICAgICAgICxwb3NpdGlvbj0ibGVmdCINCiAgICAgICAgICAgICAgICAgKSAlPiUgDQogICAgKGZ1bmN0aW9uKHgpew0KICAgICAgICB4ICU+JSBzYXZlX2thYmxlKCJJbWFnZXMvU3VtbWFyeU9mQmFyRGF0YS5wbmciKQ0KICAgICAgICB4ICU+JSByZXR1cm4oKQ0KICAgIH0pDQpgYGANCg0KDQojIyBHZW5lcmF0ZSBCYXJQbG90IERhdGEgRnJhbWUgeyNHZW5lcmF0ZUJhcn0NCg0KSW4gb3JkZXIgdG8gcHJvcGVybHkgdmlzdWFsaXNlIHRoZSBOUFMgc2NvcmUsIGFuIGVtcHR5IGRhdGEgZnJhbWUgaXMgZ2VuZXJhdGVkLCB3aXRoIG9uZSByb3cgYmVpbmcgZWFjaCBvZiB0aGUgcG9zc2libGUgc2NvcmVzLiBUaGUgcmVhc29uIGZvciB0aGlzIGlzIHRvIGFsbG93IGZvciB0aGUgQmFyIFBsb3QgdG8gYmUgYWRlcXVhdGVseSBkaXNwbGF5ZWQuIFRoZSB3YXkgdGhhdCB0aGlzIGRhdGEgaXMgZ2VuZXJhdGVkIGlzIGJ5IHVzaW5nIHRoZSBgc2VxKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhbiBvcmRlcmVkIHNlcXVlbmNlIG9mIG51bWJlcnMgZnJvbSAkMCQgdG8gJDEwJCwgaW5jcmVtZW50aW5nIGJ5ICQxJCBlYWNoIHRpbWUuDQoNCmBgYHtyIEdFTiBCYXIgRW1wdHkgRGF0YS5GcmFtZSwgZWNobz1UUlVFLCBldmFsPVRSVUV9DQpOcHNGcmFtZSA8LSBzZXEoZnJvbT0wLCB0bz0xMCwgYnk9MSkgJT4lIA0KICAgIGRhdGEuZnJhbWUoTlBTPS4pICU+JSANCiAgICBtdXRhdGUoTmFtZT0iTlBTIg0KICAgICAgICAgICxDYXRlZ29yeT0iUHJvbW90ZXJzIg0KICAgICAgICAgICxDYXRlZ29yeT1pZmVsc2UoTlBTPDksIlBhc3NpdmVzIixDYXRlZ29yeSkNCiAgICAgICAgICAsQ2F0ZWdvcnk9aWZlbHNlKE5QUzw3LCJEZXRyYWN0b3JzIixDYXRlZ29yeSkNCiAgICAgICAgICAsQ2F0ZWdvcnk9ZmFjdG9yKENhdGVnb3J5LCBsZXZlbHM9YygiUHJvbW90ZXJzIiwgIlBhc3NpdmVzIiwgIkRldHJhY3RvcnMiKSkNCiAgICAgICAgICAsTlBTPWZhY3RvcihOUFMsIGxldmVscz0wOjEwKQ0KICAgICAgICAgICkNCmBgYA0KDQpgYGB7ciBSRVZJRVcgQmFyIEVtcHR5IERhdGEuRnJhbWUsIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0NCk5wc0ZyYW1lICU+JSANCiAgICBrYWJsZShhbGlnbj0ibCIpICU+JSANCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zPWMoInN0cmlwZWQiLCAiYm9yZGVyZWQiLCAiY29uZGVuc2VkIikNCiAgICAgICAgICAgICAgICAgLGZ1bGxfd2lkdGg9RkFMU0UNCiAgICAgICAgICAgICAgICAgLHBvc2l0aW9uPSJsZWZ0Ig0KICAgICAgICAgICAgICAgICApICU+JSANCiAgICAoZnVuY3Rpb24oeCl7DQogICAgICAgIHggJT4lIHNhdmVfa2FibGUoIkltYWdlcy9CYXJFbXB0eURhdGFGcmFtZS5wbmciKQ0KICAgICAgICB4ICU+JSByZXR1cm4oKQ0KICAgIH0pDQpgYGANCg0KDQojIyBKb2luIHRoZW0gYWxsIHRvZ2V0aGVyIHsjSm9pbkRhdGF9DQoNCk5leHQsIHRoZSBOUFMgc2NvcmUgYW5kIHRoZSBOUFMgZnJhbWUgYXJlIGpvaW5lZCB0b2dldGhlciwgc28gdGhhdCB0aGUgTlBTIHNjb3JlIGlzIHJlcGxpY2F0ZWQgb3ZlciBlYWNoIGxpbmUuIFRoaXMgaXMgZG9uZSBieSB1c2luZyB0aGUgYGxlZnRfam9pbigpYCBmdW5jdGlvbiwgYW5kIHVzaW5nIGBOYW1lYCBhcyB0aGUgam9pbmluZyB2YXJpYWJsZSBiZXR3ZWVuIHRoZSB0d28gZnJhbWVzLg0KDQpgYGB7ciBHRU4gQmFyIEpvaW5lZCBEYXRhLkZyYW1lcywgZWNobz1UUlVFLCBldmFsPVRSVUV9DQpGaW5hbERhdGEgPC0gbGVmdF9qb2luKHg9TnBzRnJhbWUNCiAgICAgICAgICAgICAgICAgICAgICAseT1OcHNTY29yZQ0KICAgICAgICAgICAgICAgICAgICAgICxieT0iTmFtZSINCiAgICAgICAgICAgICAgICAgICAgICApDQpgYGANCg0KYGBge3IgUkVWSUVXIEJhciBKb2luZWQgRGF0YS5GcmFtZXMsIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0NCkZpbmFsRGF0YSAlPiUgDQogICAga2FibGUoYWxpZ249ImwiKSAlPiUgDQogICAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucz1jKCJzdHJpcGVkIiwiYm9yZGVyZWQiLCJjb25kZW5zZWQiKQ0KICAgICAgICAgICAgICAgICAsZnVsbF93aWR0aD1GQUxTRQ0KICAgICAgICAgICAgICAgICAscG9zaXRpb249ImxlZnQiDQogICAgICAgICAgICAgICAgICkgJT4lIA0KICAgIChmdW5jdGlvbih4KXsNCiAgICAgICAgeCAlPiUgc2F2ZV9rYWJsZSgiSW1hZ2VzL0JhckpvaW5lZERhdGFGcmFtZS5wbmciKQ0KICAgICAgICB4ICU+JSByZXR1cm4oKQ0KICAgIH0pDQpgYGANCg0KIyMgUGxvdCB0aGUgZmluYWwgb3V0cHV0IHsjUGxvdE5wc30NCg0KRmluYWxseSwgdGhlIHJlc3VsdCBpcyBwbG90dGVkIHVzaW5nIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uIGFuZCB0aGUgZm9sbG93aW5nIGxheWVyczogYGdlb21fYmFyKClgLCBgZ2VvbV9wb2ludCgpYCwgYW5kIGBnZW9tX2xhYmVsKClgLg0KDQpUaGUgZm9sbG93aW5nIHN0ZXBzIHdlcmUgZm9sbG93ZWQ6DQoNCjEuIFBpcGUgdGhlIGBGaW5hbERhdGFgIGRhdGEgZnJhbWUgaW4gdG8gdGhlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24sIHVzaW5nIHRoZSBgTmFtZWAgdmFyaWFibGUgYXMgdGhlIHNvbGUgYWVzdGhldGljIHZhcmlhYmxlLg0KMS4gQWRkIGEgYGdlb21fYmFyKClgIGxheWVyLCB1c2luZyB0aGUgYENhdGVnb3J5YCB2YXJpYWJsZSB0byBkZXRlcm1pbmUgd2hpY2ggY29sb3VycyB0byB1c2UgdG8gZmlsbCB0aGUgY29sdW1uLCB0aGVuIGFkZCBhIGJvcmRlciBhcm91bmQgdGhlIGNhdGVnb3JpZXMgdXNpbmcgdGhlIGNvbG91ciAnRGFya0dyZXknLCBhbmQgZ2l2ZSBpdCBhIHdpZHRoIG9mICQwLjUkIHVuaXRzLg0KMS4gQWRkIGEgYGdlb21fcG9pbnQoKWAgbGF5ZXIsIHVzaW5nIHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzOg0KDQogICAgMS4gJ2BkYXRhYCcgaXMgY3JlYXRlZCB1c2luZyBhbiBhbm9ueW1vdXMgZnVuY3Rpb24uIFRoaXMgaXMgc28gdGhhdCB0aGUgZGF0YSB1c2VkIGJ5IHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uIGNhbiBiZSBtYW5pcHVsYXRlZCwgd2l0aG91dCB1c2luZyBhbm90aGVyIGV4dGVybmFsIHZhcmlhYmxlLiBUaGUgbWFuaXB1bGF0aW9uIHdhcyBlZmZlY3RpdmVseSB1c2VkIHRvIGNyZWF0ZSBhIHNpbmdsZSBOUFMgc2NvcmUgd2hpY2ggY2FuIGJlIHVzZWQgaW4gdGhpcyBsYXllci4NCiAgICAxLiAnYGFlc2AnIGlzIHRoZSBhZXN0aGV0aWMgdXNlZCBmb3IgdGhlIGB5YCBheGlzOyB3aGljaCBpbiB0aGlzIGluc3RhbmNlIGlzIHRoZSBOUFMgc2NvcmUuIFRoaXMgaXMgdXNlZCB0byBkZXRlcm1pbmUgd2hlcmUgb24gdGhlIHBsb3QgdGhlIHBvaW50IHNob3VsZCBiZSBwbGFjZWQuDQogICAgMS4gJ2BzaGFwZWAnIGlzIGEgYHBsdXNgIHN5bWJvbCwgd2hpY2ggaXMgdXNlZCB0byBkZXRlcm1pbmUgdGhlIGV4YWN0IGxvY2F0aW9uIG9mIHRoZSBwb2ludCwgYXMgY29udmVuaWVudCBmb3IgdGhlIGh1bWFuIGV5ZSB0byBzZWUuDQogICAgMS4gJ2BzaXplYCcgaXMgdGhlIHNpemUgb2YgdGhlIHN5bWJvbCwgd2hpY2ggaW4gdGhpcyBpbnN0YW5jZSBpcyAkMjUkIHVuaXRzLg0KICAgIA0KMS4gQWRkIGEgYGdlb21fbGFiZWwoKWAgbGF5ZXIsIHVzaW5nIHRoZSBmb2xsb3dpbmcgYXJndW1lbnRzOg0KDQogICAgMS4gJ2BkYXRhYCcgaXMgYWdhaW4gbWFuaXB1bGF0ZWQgdG8gZGV0ZXJtaW5lIHRoZSBzYW1lIHZhbHVlIGFzIHVzZWQgaW4gYGdlb21fcG9pbnQoKWAuDQogICAgMS4gJ2BzdGF0YCcgaXMgdGhlIHN0YXRpc3RpYyB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgcG9zaXRpb24gb2YgdGhlIGxhYmVsOyB3aGljaCBpbiB0aGlzIGluc3RhbmNlIGlzIHRoZSB2YWx1ZSBgaWRlbnRpdHlgLCB3aGljaCBlZmZlY3RpdmVseSB0ZWxscyBgZ2dwbG90YCB0byB1c2UgdGhlIG93biBpZGVudGl0eSBvZiB0aGUgZGF0YSwgYW5kIG5vdCBjYWxjdWxhdGUgYW55IG90aGVyIHN0YXRpc3RpYyBmb3IgdGhlIGRhdGEuDQogICAgMS4gJ2BhZXNgJyBpcyB1c2VkIHRvIGRldGVybWluZSB0aGF0IHRoZSBgbGFiZWxgIHNob3VsZCBiZSB0aGUgdmFsdWUgZnJvbSB0aGUgYFNjb3JlYCB2YXJpYWJsZSwgYW5kIHRoYXQgaXQgc2hvdWxkIGJlIHBsYWNlZCBhdCB0aGUgYFNjb3JlYCBwb3NpdGlvbiBvbiB0aGUgYHlgIGF4aXMuIEVmZmVjdGl2ZWx5LCB0aGlzIGFlc3RoZXRpYyBpcyB1c2VkIHRvIGRlY2lkZSBfd2hhdF8gdGhlIHZhbHVlIG9mIHRoZSBsYWJlbCBzaG91bGQgYmUsIGFuZCBfd2hlcmVfIGl0IHNob3VsZCBiZSBwbGFjZSBvbiB0aGUgcGxvdC4NCiAgICAxLiAnYHNpemVgJyBpcyB1c2VkIHRvIGRldGVybWluZSB0aGUgc2l6ZSBvZiB0aGUgbGFiZWw7IHdoaWNoIGluIHRoaXMgaW5zdGFuY2UgaXMgJDUkIHVuaXRzLg0KICAgIA0KMS4gRGV0ZXJtaW5lIGhvdyBtYW55IGJyZWFrcyBzaG91bGQgYmUgdXNlZCwgYW5kIHRoZSBsaW1pdHMgb2YgdGhlIGB5YCBheGlzLCB1c2luZyB0aGUgYHNjYWxlX3lfY29udGludW91cygpYCBsYXllci4NCjEuIERldGVybWluZSB0aGUgY29sb3VycyB0aGF0IHNob3VsZCBiZSB1c2VkIGluIHRoZSB0aHJlZSBkaWZmZXJlbnQgQ2F0ZWdvcmllcywgdXNpbmcgdGhlIGBzY2FsZV9maWxsX21hbnVhbCgpYCBsYXllci4NCjEuIEhpZGUgdGhlIGF4aXMgdGV4dCBmb3IgdGhlIGB5YCBheGlzLCB1c2luZyB0aGUgYGF4aXMudGV4dC55LmxlZnRgIGFyZ3VtZW50IG9mIHRoZSBgdGhlbWUoKWAgbGF5ZXIuDQoxLiBGbGlwIHRoZSBjb29yZGluYXRlcyBvZiB0aGUgcGxvdCwgc28gdGhhdCBpdCBhcHBlYXJzIHRvIGJlIGEgYmFyIGZyb20gbGVmdCB0byByaWdodCwgdXNpbmcgdGhlIGBjb29yZF9mbGlwKClgIGxheWVyLg0KMS4gTGFiZWwgdGhlIGF4ZXMsIHVzaW5nIHRoZSBgbGFicygpYCBsYXllciwgdG8gZW5zdXJlIHRoYXQgdGhlIGNvcnJlY3QgaW5mb3JtYXRpb24gaXMgZGlzcGxheWVkIGluIHRoZSBjb3JyZWN0IHBvc2l0aW9ucy4NCg0KYGBge3IgUExPVCBCYXIgRGF0YSwgZWNobz1UUlVFLCBldmFsPVRSVUUsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0zLCBlcnJvcj1GQUxTRX0NCkZpbmFsRGF0YSAlPiUgDQogICAgZ2dwbG90KGFlcyhOYW1lKSkgKw0KICAgIGdlb21fYmFyKGFlcyhmaWxsPUNhdGVnb3J5KSwgY29sb3VyPSJkYXJrZ3JleSIsIHdpZHRoPTAuNSwgYWxwaGE9MC41KSArDQogICAgZ2VvbV9wb2ludChkYXRhPWZ1bmN0aW9uKHgpIHt4IDwtIHggJT4lIHNlbGVjdChOYW1lLCBTY29yZSkgJT4lIG11dGF0ZShTY29yZT1yb3VuZChTY29yZSwyKSkgJT4lIGRpc3RpbmN0KCl9DQogICAgICAgICAgICAgICxzdGF0PSJpZGVudGl0eSINCiAgICAgICAgICAgICAgLGFlcyh5PVNjb3JlKQ0KICAgICAgICAgICAgICAsc2hhcGU9InBsdXMiDQogICAgICAgICAgICAgICxzaXplPTI1DQogICAgICAgICAgICAgICkgKw0KICAgIGdlb21fbGFiZWwoZGF0YT1mdW5jdGlvbih4KSB7eCAlPiUgc2VsZWN0KE5hbWUsIFNjb3JlKSAlPiUgbXV0YXRlKFNjb3JlPXJvdW5kKFNjb3JlLDIpKSAlPiUgZGlzdGluY3R9DQogICAgICAgICAgICAgICxzdGF0PSJpZGVudGl0eSINCiAgICAgICAgICAgICAgLGFlcyh5PVNjb3JlLCBsYWJlbD1TY29yZSkNCiAgICAgICAgICAgICAgLHNpemU9NQ0KICAgICAgICAgICAgICApICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDEwLDEpLCBsaW1pdHM9YygwLDEwKSwgb29iPXNxdWlzaCkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjNjZiZDYzIiwgIiNmZGFlNjEiLCAiI2Q3MzAyNyIpKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnkubGVmdD1lbGVtZW50X2JsYW5rKCkpICsNCiAgICBjb29yZF9mbGlwKCkgKw0KICAgIGxhYnModGl0bGU9Ik5QUyBTY29yZSINCiAgICAgICAgLGZpbGw9IkNhdGVnb3J5Ig0KICAgICAgICAseT0iTlBTIFNjb3JlIg0KICAgICAgICAseD0iTlBTIg0KICAgICAgICApDQpgYGANCg0KYGBge3IgU0FWRSBCYXIgUGxvdCwgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQ0KZ2dzYXZlKHBsb3Q9bGFzdF9wbG90KCkNCiAgICAgICxmaWxlbmFtZT0iSW1hZ2VzL0JhclBsb3QucG5nIg0KICAgICAgLHdpZHRoPTE1DQogICAgICAsaGVpZ2h0PTMNCiAgICAgICkNCmBgYA0KDQoNCiMgT3B0aW9uIFR3bzogVmlzdWFsaXNlIERlbnNpdHkgUGxvdCB7I0RlbnNpdHlQbG90fQ0KDQojIyBHZW5lcmF0ZSBEZW5zaXR5UGxvdCBEYXRhIEZyYW1lIHsjR2VuZXJhdGVEZW5zaXR5fQ0KDQpJbiBvcmRlciB0byB2aXN1YWxpc2UgdGhlIERlbnNpdHkgUGxvdCwgdGhlIGRhdGEgZG9lcyBub3QgbmVlZCB0byBiZSBzdW1tYXJpc2VkLCBidXQgaXQgaXMgYmV0dGVyIHRvIHJlbWFpbiBpbiBpdHMgcmF3IGZvcm0uIEl0IGRvZXMsIGhvd2V2ZXIsIG5lZWQgdG8gdW5kZXJnbyB0aGUgZm9sbG93aW5nIG1hbmlwdWxhdGlvbnM6DQoNCjEuIENvZXJjZSBpbiB0byBhIGBkYXRhLmZyYW1lYDsgYW5kDQoxLiBBZGQgdGhlIGBDYXRlZ29yeWAgdmFyaWFibGUuDQoNCmBgYHtyIEdFTiBEZW5zaXR5IEZyYW1lLCBlY2hvPVRSVUUsIGV2YWw9VFJVRX0NCkZpbmFsRnJhbWUgPC0gTnBzRGF0YSAlPiUgDQogICAgZGF0YS5mcmFtZShTY29yZT0uKSAlPiUgDQogICAgbXV0YXRlKENhdGVnb3J5PSJQcm9tb3RlcnMiDQogICAgICAgICAgLENhdGVnb3J5PWlmZWxzZShTY29yZTw9OCwgIlBhc3NpdmVzIiwgQ2F0ZWdvcnkpDQogICAgICAgICAgLENhdGVnb3J5PWlmZWxzZShTY29yZTw9NiwgIkRldHJhY3RvcnMiLCBDYXRlZ29yeSkNCiAgICAgICAgICAsQ2F0ZWdvcnk9ZmFjdG9yKENhdGVnb3J5LCBsZXZlbHM9YygiUHJvbW90ZXJzIiwgIlBhc3NpdmVzIiwgIkRldHJhY3RvcnMiKSkNCiAgICAgICAgICApDQpgYGANCg0KYGBge3IgUkVWSUVXIERlbnNpdHkgRnJhbWUsIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0NCkZpbmFsRnJhbWUgJT4lIA0KICAgIGhlYWQoMTApICU+JSANCiAgICBrYWJsZShhbGlnbj0ibCIpICU+JSANCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zPWMoInN0cmlwZWQiLCJib3JkZXJlZCIsImNvbmRlbnNlZCIpDQogICAgICAgICAgICAgICAgICxmdWxsX3dpZHRoPUZBTFNFDQogICAgICAgICAgICAgICAgICxwb3NpdGlvbj0ibGVmdCINCiAgICAgICAgICAgICAgICAgKSAlPiUgDQogICAgKGZ1bmN0aW9uKHgpew0KICAgICAgICB4ICU+JSBzYXZlX2thYmxlKCJJbWFnZXMvRGVuc2l0eUZpbmFsRGF0YUZyYW1lLnBuZyIpDQogICAgICAgIHggJT4lIHJldHVybigpDQogICAgfSkNCmBgYA0KDQoNCiMjIFZpc3VhbGlzZSB0aGUgRGVuc2l0eVBsb3QgZGF0YSB7I1Zpc3VhbGlzZURlbnNpdHl9DQoNCk9uY2UgdGhlIERlbnNpdHkgZGF0YSBmcmFtZSBpcyBnZW5lcmF0ZWQsIGl0IGNhbiBiZSB2aXN1YWxpc2VkIHRocm91Z2ggYGdncGxvdCgpYCwgdXNpbmcgdGhlIGZvbGxvd2luZyBhZXN0aGV0aWNzOiBgZ2VvbV9iYXIoKWAgYW5kIGBnZW9tX2RlbnNpdHkoKWAuDQoNClRoZSBmb2xsb3dpbmcgc3RlcHMgd2VyZSB1c2VkOg0KDQoxLiBQaXBlIHRoZSBgRmluYWxEYXRhYCBkYXRhIGZyYW1lIGluIHRvIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uLCB1c2luZyB0aGUgYFNjb3JlYCB2YXJpYWJsZSBhcyB0aGUgc29sZSBhZXN0aGV0aWMuDQoxLiBBZGQgYSBgZ2VvbV9iYXIoKWAgbGF5ZXIsIHVzaW5nIHRoZSBgQ2F0ZWdvcnlgIHZhcmlhYmxlIHRvIGRldGVybWluZSB0aGUgY29sb3VlcnMgdG8gdXNlIHRvIGZpbGwgdGhlIGNvbHVtbiwgdGhlbiBhZGQgYSBib3JkZXIgYXJvdW5kIHRoZSBjYXRlZ29yaWVzIHVzaW5nIHRoZSBjb2xvdXIgJ0RhcmtHcmV5JywgYW5kIGdpdmUgaXQgYSB0cmFuc3BhcmVuY3kgdmFsdWUgb2YgJDAuMyQuDQoxLiBBZGQgYSBgZ2VvbV9kZW5zaXR5KClgIGxheWVyLCB1c2luZyBhbiBhZXN0aGV0aWMgYHlgIHZhbHVlIHRvIGRldGVybWluZSB0aGF0IHRoaXMgdmFsdWUgc2hvdWxkIGJlIGEgJ2NvdW50JyBvZiB0aGUgZGF0YSwgbm90IGEgJ2RlbnNpdHknIG9mIHRoZSBkYXRhLCB0aGVuIGdpdmUgaXQgYSAnQmx1ZScgY29sb3VyLCBhbmQgaW5jcmVhc2UgdGhlIHNpemUgdG8gJDEkIHVuaXQuDQoxLiBEZXRlcm1pbmUgdGhlIGNvbG91cnMgdGhhdCBzaG91bGQgYmUgdXNlZCBmb3IgdGhlIHRocmVlIGRpZmZlcmVudCBDYXRlZ29yaWVzLCB1c2luZyB0aGUgYHNjYWxlX2ZpbGxfbWFudWFsKClgIGxheWVyLg0KMS4gRGV0ZXJtaW5lIHRoZSBicmVha3MgYW5kIHRoZSBsaW1pdHMgb2YgdGhlIGB4YCBheGlzLCB1c2luZyB0aGUgYHNjYWxlX3hfY29udGludW91cygpYCBsYXllci4NCjEuIFJlbW92ZSB0aGUgbGVnZW5kIGZyb20gdGhlIHBsb3QsIHVzaW5nIHRoZSBgdGhlbWUoKWAgbGF5ZXIuDQoxLiBBZGQgbGFiZWxzIGZvciB0aGUgcGxvdCwgdXNpbmcgdGhlIGBsYWJzKClgIGxheWVyLg0KDQpgYGB7ciBQTE9UIERlbnNpdHkgRGF0YSwgZWNobz1UUlVFLCBldmFsPVRSVUUsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD01fQ0KRmluYWxGcmFtZSAlPiUgDQogICAgZ2dwbG90KGFlcyhTY29yZSkpICsNCiAgICBnZW9tX2JhcihhZXMoZmlsbD1DYXRlZ29yeSksIGNvbG91cj0iZGFya2dyZXkiLCBhbHBoYT0wLjMpICsNCiAgICBnZW9tX2RlbnNpdHkoYWVzKHk9Li5jb3VudC4uKSwgY29sb3VyPSJibHVlIiwgYWRqdXN0PTMsIHNpemU9MSkgKw0KICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjNjZiZDYzIiwgIiNmZGFlNjEiLCAiI2Q3MzAyNyIpKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMCwxKSwgbGltaXRzPWMoLTAuNSwxMC41KSkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsNCiAgICBsYWJzKHRpdGxlPSJEZW5zaXR5IFBsb3Qgb2YgTlBTIg0KICAgICAgICAseD0iU2NvcmUiDQogICAgICAgICx5PSJDb3VudCINCiAgICAgICAgKQ0KYGBgDQoNCmBgYHtyIFNBVkUgRGVuc2l0eSBQbG90LCBlY2hvPUZBTFNFLCBldmFsPVRSVUV9DQpnZ3NhdmUocGxvdD1sYXN0X3Bsb3QoKQ0KICAgICAgLGZpbGVuYW1lPSJJbWFnZXMvRGVuc2l0eVBsb3QucG5nIg0KICAgICAgLHdpZHRoPTE1DQogICAgICAsaGVpZ2h0PTUNCiAgICAgICkNCmBgYA0KDQoNCiMgQ29uY2x1c2lvbiB7I0NvbmNsdXNpb259DQoNCkFzIHNlZW4sIHRoZSBOZXQgUHJvbW90ZXIgU2NvcmUgaXMgYSB1c2VmdWwgbWV0cmljIHRvIHNlZSB0aGUgcGVyY2VudGFnZSBvZiBjdXN0b21lcnMgd2hvIGFyZSBQcm9tb3RlcnMsIFBhc3NpdmVzIG9yIERldHJhY3RvcnMgb2YgdGhlIGJ1c2luZXNzLiBUaGlzIG1ldHJpYyBjYW4gYmUgdmlzdWFsaXNlZCBpbiBhIHNpbXBsZSBCYXJQbG90LCB3aXRoIGEgc3RhdGljIHZhbHVlIGRpc3BsYXllZCBvbiB0aGUgY2hhcnQsIG9yIGl0IGNhbiBiZSB2aXN1YWxpc2VkIGFzIGEgRGVuc2l0eVBsb3QsIHNob3dpbmcgdGhlIHByb3BvcnRpb24gb2YgY3VzdG9tZXJzIGluIHRoZSBkaWZmZXJlbnQgY2F0ZWdvcmllcy4gQm90aCBvZiB0aGVzZSBtZXRob2RvbG9naWVzIGFyZSBwcm92aWRlZCBpbiB0aGlzIFZpZ25ldHRlLCB3aXRoIGEgc3RlcC1ieS1zdGVwIGd1aWRlIGZyb20gZGF0YSBtYW5pcHVsYXRpb24gdG8gcGxvdHRpbmcuDQoNCg0KIyBQb3N0IFNjcmlwdCB7I1Bvc3RTY3JpcHR9DQoNCioqUHVibGljYXRpb25zKio6IFRoaXMgcmVwb3J0IGlzIGFsc28gcHVibGlzaGVkIG9uIHRoZSBmb2xsb3dpbmcgc2l0ZXM6DQoNCjEuIFJQdWJzOiBbUlB1YnMvY2hyaW1haG8vUGxvdHRpbmdOUFNdKGh0dHA6Ly9ycHVicy5jb20vY2hyaW1haG8vUGxvdHRpbmdOUFMpDQoxLiBHaXRIdWI6IFtHaXRIdWIvY2hyaW1haG8vUGxvdHRpbmdOUFNdKGh0dHBzOi8vZ2l0aHViLmNvbS9jaHJpbWFoby9QbG90dGluZ05QUykNCjEuIE1lZGl1bTogW01lZGl1bS9jaHJpbWFoby9QbG90dGluZ05QU10oaHR0cHM6Ly9tZWRpdW0uY29tL0BjaHJpbWFoby9wbG90dGluZ25wcy0yOTU4YjY0MmE1MWY/c291cmNlPWZyaWVuZHNfbGluayZzaz0zODI1NTdhZTZkZDYyMjdkMDA0ZWI0MmEzNzRmYmI4ZikNCg0KKipDaGFuZ2UgTG9nOioqIFRoaXMgcHVibGljYXRpb24gd2FzIG1vZGlmaWVkIG9uIHRoZSBmb2xsb3dpbmcgZGF0ZXM6DQoNCjEuIDI5L0phbi8yMDIwOiBPcmlnaW5hbCBQdWJsaWNhdGlvbiBEYXRl
 

Report compiled by Chris Mahoney

chrismahoney@hotmail.com