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:
- First is in the true essence of the metric, which is a bar plot, the the NPS score displayed on the plot.
- 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.
Set Up
Load the Packages
To begin, the environment must be set up. The first step is to load the packages that will be used.
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
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
|
Option One: Visualise Bar Plot
Summarise NPS Data
To calculate the NPS score, the following steps are performed on the data:
- Coerce the data in to a
data.frame;
- Add a
Category variable to determine the category of the score;
- Count the number of scores in each category;
- Calculate the percentage of the different categories;
- Calculate the NPS score; and
- Coerce it again in to a
data.frame to add a new variable called NPS.
Once generated, the data is ready to be visualised.
NpsScore <- NpsData %>%
data.frame(Score=.) %>%
mutate(Category="Promoters"
,Category=ifelse(Score<=8, "Passives", Category)
,Category=ifelse(Score<=6, "Detractors", Category)
,Category=factor(Category, levels=c("Promoters", "Passives", "Detractors"))
) %>%
count(Category, name="Count") %>%
mutate(Percentage=Count/sum(Count)) %>%
(function(x){
Pro <- x %>% filter(Category=="Promoters") %>% select(Percentage) %>% pull()
Det <- x %>% filter(Category=="Detractors") %>% select(Percentage) %>% pull()
return((Pro-Det)*10)
}) %>%
data.frame(Score=.) %>%
mutate(Name="NPS")
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.
NpsFrame <- seq(from=0, to=10, by=1) %>%
data.frame(NPS=.) %>%
mutate(Name="NPS"
,Category="Promoters"
,Category=ifelse(NPS<9,"Passives",Category)
,Category=ifelse(NPS<7,"Detractors",Category)
,Category=factor(Category, levels=c("Promoters", "Passives", "Detractors"))
,NPS=factor(NPS, levels=0:10)
)
|
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
|
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
|
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:
Pipe the FinalData data frame in to the ggplot() function, using the Name variable as the sole aesthetic variable.
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.
Add a geom_point() layer, using the following arguments:
- ‘
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.
- ‘
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.
- ‘
shape’ is a plus symbol, which is used to determine the exact location of the point, as convenient for the human eye to see.
- ‘
size’ is the size of the symbol, which in this instance is 25 units.
Add a geom_label() layer, using the following arguments:
- ‘
data’ is again manipulated to determine the same value as used in geom_point().
- ‘
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.
- ‘
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.
- ‘
size’ is used to determine the size of the label; which in this instance is 5 units.
Determine how many breaks should be used, and the limits of the y axis, using the scale_y_continuous() layer.
Determine the colours that should be used in the three different Categories, using the scale_fill_manual() layer.
Hide the axis text for the y axis, using the axis.text.y.left argument of the theme() layer.
Flip the coordinates of the plot, so that it appears to be a bar from left to right, using the coord_flip() layer.
Label the axes, using the labs() layer, to ensure that the correct information is displayed in the correct positions.
FinalData %>%
ggplot(aes(Name)) +
geom_bar(aes(fill=Category), colour="darkgrey", width=0.5, alpha=0.5) +
geom_point(data=function(x) {x <- x %>% select(Name, Score) %>% mutate(Score=round(Score,2)) %>% distinct()}
,stat="identity"
,aes(y=Score)
,shape="plus"
,size=25
) +
geom_label(data=function(x) {x %>% select(Name, Score) %>% mutate(Score=round(Score,2)) %>% distinct}
,stat="identity"
,aes(y=Score, label=Score)
,size=5
) +
scale_y_continuous(breaks=seq(0,10,1), limits=c(0,10), oob=squish) +
scale_fill_manual(values=c("#66bd63", "#fdae61", "#d73027")) +
theme(axis.text.y.left=element_blank()) +
coord_flip() +
labs(title="NPS Score"
,fill="Category"
,y="NPS Score"
,x="NPS"
)

Option Two: Visualise Density Plot
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:
- Coerce in to a
data.frame; and
- Add the
Category variable.
FinalFrame <- NpsData %>%
data.frame(Score=.) %>%
mutate(Category="Promoters"
,Category=ifelse(Score<=8, "Passives", Category)
,Category=ifelse(Score<=6, "Detractors", Category)
,Category=factor(Category, levels=c("Promoters", "Passives", "Detractors"))
)
|
Score
|
Category
|
|
9
|
Promoters
|
|
10
|
Promoters
|
|
9
|
Promoters
|
|
6
|
Detractors
|
|
8
|
Passives
|
|
9
|
Promoters
|
|
10
|
Promoters
|
|
6
|
Detractors
|
|
10
|
Promoters
|
|
10
|
Promoters
|
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:
- Pipe the
FinalData data frame in to the ggplot() function, using the Score variable as the sole aesthetic.
- 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.
- 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.
- Determine the colours that should be used for the three different Categories, using the
scale_fill_manual() layer.
- Determine the breaks and the limits of the
x axis, using the scale_x_continuous() layer.
- Remove the legend from the plot, using the
theme() layer.
- Add labels for the plot, using the
labs() layer.
FinalFrame %>%
ggplot(aes(Score)) +
geom_bar(aes(fill=Category), colour="darkgrey", alpha=0.3) +
geom_density(aes(y=..count..), colour="blue", adjust=3, size=1) +
scale_fill_manual(values=c("#66bd63", "#fdae61", "#d73027")) +
scale_x_continuous(breaks=seq(0,10,1), limits=c(-0.5,10.5)) +
theme(legend.position="none") +
labs(title="Density Plot of NPS"
,x="Score"
,y="Count"
)

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.
Post Script
Publications: This report is also published on the following sites:
- RPubs: RPubs/chrimaho/PlottingNPS
- GitHub: GitHub/chrimaho/PlottingNPS
- Medium: Medium/chrimaho/PlottingNPS
Change Log: This publication was modified on the following dates:
- 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