We are exploring data on reviews of coffee quality from different suppliers. The data was downloaded from Kaggle - click on the link to visit the Dataset page.
The dataset contains 1338 rows and 53 columns. Here are the first 10 rows.
The first column is an index, columns 2-20 give information on the coffee being assessed and columns 21-36 contain the quality measurements; columns 21-31 give cupping measurements and columns 32-36 give measurements for the green unroasted beans. Columns 37-40 give information on the coffee certification and columns 41-44 are derived from the farm altitude data.
Let’s see summaries of each of the columns.
-- Data Summary ------------------------
Values
Name bean_data
Number of rows 1339
Number of columns 44
_______________________
Column type frequency:
character 24
numeric 20
________________________
Group variables None
-- Variable type: character -----------------------------------------------------------------------------------------
# A tibble: 24 x 8
skim_variable n_missing complete_rate min max empty n_unique whitespace
* <chr> <int> <dbl> <int> <int> <int> <int> <int>
1 Species 0 1 7 7 0 2 0
2 Owner 7 0.995 3 50 0 315 0
3 Country.of.Origin 1 0.999 4 28 0 36 0
4 Farm.Name 359 0.732 1 101 0 571 0
5 Lot.Number 1063 0.206 1 93 0 227 0
6 Mill 318 0.763 1 101 0 459 0
7 ICO.Number 157 0.883 1 40 0 846 0
8 Company 209 0.844 3 73 0 281 0
9 Altitude 226 0.831 1 41 0 396 0
10 Region 59 0.956 3 76 0 356 0
11 Producer 232 0.827 1 104 0 690 0
12 Bag.Weight 0 1 1 8 0 56 0
13 In.Country.Partner 0 1 7 88 0 27 0
14 Harvest.Year 47 0.965 3 24 0 46 0
15 Grading.Date 0 1 13 20 0 567 0
16 Owner.1 7 0.995 3 50 0 319 0
17 Variety 226 0.831 4 21 0 29 0
18 Processing.Method 170 0.873 5 25 0 5 0
19 Color 218 0.837 4 12 0 4 0
20 Expiration 0 1 13 20 0 566 0
21 Certification.Body 0 1 7 88 0 26 0
22 Certification.Address 0 1 40 40 0 32 0
23 Certification.Contact 0 1 40 40 0 29 0
24 unit_of_measurement 0 1 1 2 0 2 0
-- Variable type: numeric -------------------------------------------------------------------------------------------
# A tibble: 20 x 10
skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100
* <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 X 0 1 669 387. 0 334. 669 1004. 1338
2 Number.of.Bags 0 1 154. 130. 0 14 175 275 1062
3 Aroma 0 1 7.57 0.378 0 7.42 7.58 7.75 8.75
4 Flavor 0 1 7.52 0.398 0 7.33 7.58 7.75 8.83
5 Aftertaste 0 1 7.40 0.404 0 7.25 7.42 7.58 8.67
6 Acidity 0 1 7.54 0.380 0 7.33 7.58 7.75 8.75
7 Body 0 1 7.52 0.370 0 7.33 7.5 7.67 8.58
8 Balance 0 1 7.52 0.409 0 7.33 7.5 7.75 8.75
9 Uniformity 0 1 9.83 0.555 0 10 10 10 10
10 Clean.Cup 0 1 9.84 0.764 0 10 10 10 10
11 Sweetness 0 1 9.86 0.616 0 10 10 10 10
12 Cupper.Points 0 1 7.50 0.473 0 7.25 7.5 7.75 10
13 Total.Cup.Points 0 1 82.1 3.50 0 81.1 82.5 83.7 90.6
14 Moisture 0 1 0.0884 0.0483 0 0.09 0.11 0.12 0.28
15 Category.One.Defects 0 1 0.479 2.55 0 0 0 0 63
16 Quakers 1 0.999 0.173 0.832 0 0 0 0 11
17 Category.Two.Defects 0 1 3.56 5.31 0 0 2 4 55
18 altitude_low_meters 230 0.828 1751. 8669. 1 1100 1311. 1600 190164
19 altitude_high_meters 230 0.828 1799. 8669. 1 1100 1350 1650 190164
20 altitude_mean_meters 230 0.828 1775. 8669. 1 1100 1311. 1600 190164
We can see that there is plenty of missing data in the character variables, while most of the numeric variables are complete. Some altitude data is missing.
Data Cleaning
Some of the coffee description columns are not of interest - ICO.Number and Lot.Number are just identifiers. Owner is lower case transformed Owner.1, so will be preferentially used.
There are several fields for the origin of the coffee, i.e. Owner, Farm.Name, Mill, Company, Producer and also In.Country.Partner. By far the most complete of these data is the Owner and this may be the best field to use, however some Owners list several Producers, which suggests different origins for these beans. Initially, we will just use Owner and not use Farm.Name, Company, Producer or In.Country.Partner.
Note that we have some weird characters present in the Owner and Region field. We can remove these using string replace. One of the samples has a rating of zero for all scores and can be removed as likely erroneous.
Grading.Date will be transformed from a string to a date and sample Bag.Weight will be changed to a numeric, harmonising to kg units.
Missing Data
We will deal with missing data as follows:
- Those rows with missing Owner, we will replace the missing value with the Farm.Name
- The one row with missing Country.Of.Origin is clearly from Colombia as clear from the Owner entry
- Where Region data is missing, we will default to the Country
- There is a small amount of missing data for Harvest.Year, where the rows could be removed
- For Variety, we can’t make deductions about missing values, so it’s likely we will have to remove missing data
- Similarly for Processing.Method, the missing data can’t be assumed so will likely have to be removed
- Color has some values of “None” which is assumed to be missing data, rather than the beans being colourless
- Where altitude data is missing, we could impute the mean
- One missing value for Quakers can be assumed to be 0
Data Correction
The variable Harvest.Year has the year in various different formats and needs to be cleaned up. We will use the following rules for cleaning: we default to a single year value; where two years are given, we will use the second year; when no year is given we use NA. This variable can then be transformed to a numeric.
Some of the altitude data also requires correction as it appears to have been entered incorrectly or misread. We will re-parse the original Altitude data to correct the errors. After re-parsing to numeric values for mean and range of altitude, the number of missing values becomes 244.
Apart from in Hawaii and Puerto Rico, the United States does not produce coffee, so where Country.of.Origin has been given as United States, this appears to be a data error. We will fix this data using Region and Producer information.
Output Variables
We have 10 individual quality scores, plus a summed overall quality. Let’s visualise how these scores are distributed.

From the output variables, we see:
- Aroma, Flavor, Aftertaste, Acidity, Body, Balance and overall Cupper.Points are similarly distributed with mean values around 7.5 and approximate range of 6.5-8.5
- Uniformity, Clean.Cup and Sweetness are generally highly rated with little deviation from scores of 10
- Total.Cup.Points has a distribution with a mean of approximately 83 and general range of 77-87
We are really interested in Total.Cup.Points as our main quality variable, so let’s look at how this output variable is impacted by the different input variables.
Variable Relationships
We can draw boxplots to illustrate relationships of Total.Cup.Points with categorical input variables.





What do these relationships tell us?
- Arabica beans tend to give slightly higher scores than Robusta
- We see more variation in scores with Country.of.Origin and bean Variety - Ethiopian Yirgacheffe appears to be generally the coffee with highest scores
- We see less variation in scores with Processing.Method and Color
We can draw scatter plots to illustrate relationships of Total.Cup.Points with numeric input variables.

There are no clear trends in these relationships. However, the best scores, i.e. >85 Total.Cup.Points, tend to only be seen when the number of defects and Quakers are low. Quality appears to perhaps increase up to around 2000 m altitude, and then decrease for the few coffees at higher altitudes.
We can now take this data forward in an attempt to model Total.Cup.Points.
End
LS0tDQp0aXRsZTogIkNvZmZlZSBSZXZpZXcgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCldlIGFyZSBleHBsb3JpbmcgZGF0YSBvbiByZXZpZXdzIG9mIGNvZmZlZSBxdWFsaXR5IGZyb20gZGlmZmVyZW50IHN1cHBsaWVycy4gVGhlIGRhdGEgd2FzIGRvd25sb2FkZWQgZnJvbSBbS2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL3ZvbHBhdHRvL2NvZmZlZS1xdWFsaXR5LWRhdGFiYXNlLWZyb20tY3FpKSAtIGNsaWNrIG9uIHRoZSBsaW5rIHRvIHZpc2l0IHRoZSBEYXRhc2V0IHBhZ2UuICANCg0KYGBge3IgTG9hZCBsaWJyYXJpZXMgYW5kIGRhdGEsIG1lc3NhZ2U9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpybShsaXN0ID0gbHMoKSkNCg0KbGlicmFyeShoZXJlKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGFueXRpbWUpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCg0KYmVhbl9kYXRhIDwtIHJlYWQuY3N2KGhlcmUoImRhdGEvcmF3L21lcmdlZF9kYXRhX2NsZWFuZWQuY3N2IiksIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgICAgc3RyaXAud2hpdGUgPSBUUlVFLCBuYS5zdHJpbmdzID0gYygiIiwgIiAiKSkNCg0KYGBgDQoNCmBgYHtyIEZ1bmN0aW9uIGRlZmluaXRpb24sIGluY2x1ZGU9RkFMU0V9DQpkaXN0X2Jhcl9wbG90IDwtIGZ1bmN0aW9uKGRmLCB2YXIsIHRvcF9udW0gPSBOVUxMKXsNCiAgDQogIG5ld192YXIgPC0gZW5xdW8odmFyKQ0KICB0aXRsZSA8LSBwYXN0ZTAoIkRpc3RyaWJ1dGlvbiBvZiAiLCBkZXBhcnNlKHN1YnN0aXR1dGUodmFyKSkpDQogIA0KICBwbG90X2RmIDwtIGRmICU+JSANCiAgICBmaWx0ZXIoaXMubmEoISFuZXdfdmFyKSA9PSBGQUxTRSkgJT4lDQogICAgZ3JvdXBfYnkoISFuZXdfdmFyKSAlPiUgDQogICAgc3VtbWFyaXNlKENvdW50ID0gbigpKQ0KICANCiAgaWYgKGlzLm51bGwodG9wX251bSkgPT0gRkFMU0Upew0KICAgIHBsb3RfZGYgPC0gcGxvdF9kZiAlPiUNCiAgICAgIHNsaWNlX21heChDb3VudCwgbiA9IHRvcF9udW0pDQogICAgDQogICAgdGl0bGUgPC0gcGFzdGUwKHRpdGxlLCAiOlxuVG9wICIsIHRvcF9udW0pDQogIH0NCiAgDQogIHBsb3RfZGYgPC0gcGxvdF9kZiAlPiUNCiAgICBhcnJhbmdlKC1Db3VudCkNCiAgDQogIHBsb3QgPC0gZ2dwbG90KHBsb3RfZGYsIGFlcyh4ID0gcmVvcmRlcighIW5ld192YXIsIC1Db3VudCksIHkgPSBDb3VudCkpICsgDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvdXIgPSAiYmxhY2siKSArDQogICAgdGhlbWVfYncoKSArIGxhYnMoeCA9ICIiLCB5ID0gIkNvdW50IikgKyBnZ3RpdGxlKHRpdGxlKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpDQogIA0KICByZXR1cm4ocGxvdCkNCn0NCg0KYGBgDQoNClRoZSBkYXRhc2V0IGNvbnRhaW5zIGByIG5yb3coYmVhbl9kYXRhKWAgcm93cyBhbmQgYHIgbmNvbChiZWFuX2RhdGEpYCBjb2x1bW5zLiBIZXJlIGFyZSB0aGUgZmlyc3QgMTAgcm93cy4gIA0KDQpgYGB7ciBEYXRhIHN1bW1hcnksIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpoZWFkKGJlYW5fZGF0YSwgMTApDQoNCmBgYA0KDQpUaGUgZmlyc3QgY29sdW1uIGlzIGFuIGluZGV4LCBjb2x1bW5zIDItMjAgZ2l2ZSBpbmZvcm1hdGlvbiBvbiB0aGUgY29mZmVlIGJlaW5nIGFzc2Vzc2VkIGFuZCBjb2x1bW5zIDIxLTM2IGNvbnRhaW4gdGhlIHF1YWxpdHkgbWVhc3VyZW1lbnRzOyBjb2x1bW5zIDIxLTMxIGdpdmUgY3VwcGluZyBtZWFzdXJlbWVudHMgYW5kIGNvbHVtbnMgMzItMzYgZ2l2ZSBtZWFzdXJlbWVudHMgZm9yIHRoZSBncmVlbiB1bnJvYXN0ZWQgYmVhbnMuIENvbHVtbnMgMzctNDAgZ2l2ZSBpbmZvcm1hdGlvbiBvbiB0aGUgY29mZmVlIGNlcnRpZmljYXRpb24gYW5kIGNvbHVtbnMgNDEtNDQgYXJlIGRlcml2ZWQgZnJvbSB0aGUgZmFybSBhbHRpdHVkZSBkYXRhLiAgDQoNCkxldCdzIHNlZSBzdW1tYXJpZXMgb2YgZWFjaCBvZiB0aGUgY29sdW1ucy4gIA0KDQpgYGB7ciBTdW1tYXJ5LCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3aWR0aD0xMH0NCnNraW1yOjpza2ltX3dpdGhvdXRfY2hhcnRzKGJlYW5fZGF0YSkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlcmUgaXMgcGxlbnR5IG9mIG1pc3NpbmcgZGF0YSBpbiB0aGUgY2hhcmFjdGVyIHZhcmlhYmxlcywgd2hpbGUgbW9zdCBvZiB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgYXJlIGNvbXBsZXRlLiBTb21lIGFsdGl0dWRlIGRhdGEgaXMgbWlzc2luZy4gIA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6dGVhbDsiPkRhdGEgQ2xlYW5pbmc8L3NwYW4+ICANCg0KU29tZSBvZiB0aGUgY29mZmVlIGRlc2NyaXB0aW9uIGNvbHVtbnMgYXJlIG5vdCBvZiBpbnRlcmVzdCAtIElDTy5OdW1iZXIgYW5kIExvdC5OdW1iZXIgYXJlIGp1c3QgaWRlbnRpZmllcnMuIE93bmVyIGlzIGxvd2VyIGNhc2UgdHJhbnNmb3JtZWQgT3duZXIuMSwgc28gd2lsbCBiZSBwcmVmZXJlbnRpYWxseSB1c2VkLiAgDQoNClRoZXJlIGFyZSBzZXZlcmFsIGZpZWxkcyBmb3IgdGhlIG9yaWdpbiBvZiB0aGUgY29mZmVlLCBpLmUuIE93bmVyLCBGYXJtLk5hbWUsIE1pbGwsIENvbXBhbnksIFByb2R1Y2VyIGFuZCBhbHNvIEluLkNvdW50cnkuUGFydG5lci4gQnkgZmFyIHRoZSBtb3N0IGNvbXBsZXRlIG9mIHRoZXNlIGRhdGEgaXMgdGhlIE93bmVyIGFuZCB0aGlzIG1heSBiZSB0aGUgYmVzdCBmaWVsZCB0byB1c2UsIGhvd2V2ZXIgc29tZSBPd25lcnMgbGlzdCBzZXZlcmFsIFByb2R1Y2Vycywgd2hpY2ggc3VnZ2VzdHMgZGlmZmVyZW50IG9yaWdpbnMgZm9yIHRoZXNlIGJlYW5zLiBJbml0aWFsbHksIHdlIHdpbGwganVzdCB1c2UgT3duZXIgYW5kIG5vdCB1c2UgRmFybS5OYW1lLCBDb21wYW55LCBQcm9kdWNlciBvciBJbi5Db3VudHJ5LlBhcnRuZXIuICANCg0KTm90ZSB0aGF0IHdlIGhhdmUgc29tZSB3ZWlyZCBjaGFyYWN0ZXJzIHByZXNlbnQgaW4gdGhlIE93bmVyIGFuZCBSZWdpb24gZmllbGQuIFdlIGNhbiByZW1vdmUgdGhlc2UgdXNpbmcgc3RyaW5nIHJlcGxhY2UuIE9uZSBvZiB0aGUgc2FtcGxlcyBoYXMgYSByYXRpbmcgb2YgemVybyBmb3IgYWxsIHNjb3JlcyBhbmQgY2FuIGJlIHJlbW92ZWQgYXMgbGlrZWx5IGVycm9uZW91cy4gICANCg0KDQpgYGB7ciBGaXggQ2hhcmFjdGVycywgaW5jbHVkZT1GQUxTRX0NCmJlYW5fZGF0YSA8LSBiZWFuX2RhdGEgJT4lDQogIGZpbHRlcihUb3RhbC5DdXAuUG9pbnRzICE9IDApICU+JQ0KICBtdXRhdGUoT3duZXIgPSBzdHJfcmVwbGFjZV9hbGwoT3duZXIsICJbXmEtekEtWjAtOSBdIiwgIiIpLA0KICAgICAgICAgUmVnaW9uID0gc3RyX3JlcGxhY2VfYWxsKFJlZ2lvbiwgIlteYS16QS1aMC05IF0iLCAiIiksDQogICAgICAgICBPd25lciA9IHN0cl90cmltKE93bmVyKSwNCiAgICAgICAgIFJlZ2lvbiA9IHN0cl90cmltKFJlZ2lvbikpDQoNCmBgYA0KDQpHcmFkaW5nLkRhdGUgd2lsbCBiZSB0cmFuc2Zvcm1lZCBmcm9tIGEgc3RyaW5nIHRvIGEgZGF0ZSBhbmQgc2FtcGxlIEJhZy5XZWlnaHQgd2lsbCBiZSBjaGFuZ2VkIHRvIGEgbnVtZXJpYywgaGFybW9uaXNpbmcgdG8ga2cgdW5pdHMuICANCg0KYGBge3IgRGF0ZSBhbmQgd2VpZ2h0LCBpbmNsdWRlPUZBTFNFfQ0KYmVhbl9kYXRhIDwtIGJlYW5fZGF0YSAlPiUNCiAgbXV0YXRlKG5ld19kYXRlID0gc3RyX3JlcGxhY2VfYWxsKEdyYWRpbmcuRGF0ZSwgIig/PD1bMC05XSlzdCIsICIiKSwNCiAgICAgICAgIG5ld19kYXRlID0gc3RyX3JlcGxhY2VfYWxsKG5ld19kYXRlLCAiKD88PVswLTldKW5kIiwgIiIpLA0KICAgICAgICAgbmV3X2RhdGUgPSBzdHJfcmVwbGFjZV9hbGwobmV3X2RhdGUsICIoPzw9WzAtOV0pcmQiLCAiIiksDQogICAgICAgICBuZXdfZGF0ZSA9IHN0cl9yZXBsYWNlX2FsbChuZXdfZGF0ZSwgIig/PD1bMC05XSl0aCIsICIiKSwNCiAgICAgICAgIG5ld19kYXRlID0gYW55ZGF0ZShuZXdfZGF0ZSkpDQoNCmJlYW5fZGF0YSR3dF9pbl9sYiA8LSBzdHJfZGV0ZWN0KGJlYW5fZGF0YSRCYWcuV2VpZ2h0LCAibGIiKQ0KDQpiZWFuX2RhdGEgPC0gYmVhbl9kYXRhICU+JQ0KICBtdXRhdGUobmV3X3d0ID0gc3RyX3JlcGxhY2VfYWxsKGJlYW5fZGF0YSRCYWcuV2VpZ2h0LCAiW15bOmRpZ2l0Ol1dIiwgIiIpLA0KICAgICAgICAgbmV3X3d0ID0gYXMubnVtZXJpYyhuZXdfd3QpKQ0KDQpiZWFuX2RhdGEkbmV3X3d0W3doaWNoKGJlYW5fZGF0YSR3dF9pbl9sYiA9PSBUUlVFKV0gPC0gDQogIGJlYW5fZGF0YSRuZXdfd3Rbd2hpY2goYmVhbl9kYXRhJHd0X2luX2xiID09IFRSVUUpXSAvIDIuMjA0NjINCg0KYGBgDQoNCg0KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjpzdGVlbGJsdWU7Ij5NaXNzaW5nIERhdGE8L3NwYW4+ICANCg0KV2Ugd2lsbCBkZWFsIHdpdGggbWlzc2luZyBkYXRhIGFzIGZvbGxvd3M6IA0KDQoqIFRob3NlIHJvd3Mgd2l0aCBtaXNzaW5nIE93bmVyLCB3ZSB3aWxsIHJlcGxhY2UgdGhlIG1pc3NpbmcgdmFsdWUgd2l0aCB0aGUgRmFybS5OYW1lDQoqIFRoZSBvbmUgcm93IHdpdGggbWlzc2luZyBDb3VudHJ5Lk9mLk9yaWdpbiBpcyBjbGVhcmx5IGZyb20gQ29sb21iaWEgYXMgY2xlYXIgZnJvbSB0aGUgT3duZXIgZW50cnkNCiogV2hlcmUgUmVnaW9uIGRhdGEgaXMgbWlzc2luZywgd2Ugd2lsbCBkZWZhdWx0IHRvIHRoZSBDb3VudHJ5DQoqIFRoZXJlIGlzIGEgc21hbGwgYW1vdW50IG9mIG1pc3NpbmcgZGF0YSBmb3IgSGFydmVzdC5ZZWFyLCB3aGVyZSB0aGUgcm93cyBjb3VsZCBiZSByZW1vdmVkDQoqIEZvciBWYXJpZXR5LCB3ZSBjYW4ndCBtYWtlIGRlZHVjdGlvbnMgYWJvdXQgbWlzc2luZyB2YWx1ZXMsIHNvIGl0J3MgbGlrZWx5IHdlIHdpbGwgaGF2ZSB0byByZW1vdmUgbWlzc2luZyBkYXRhDQoqIFNpbWlsYXJseSBmb3IgUHJvY2Vzc2luZy5NZXRob2QsIHRoZSBtaXNzaW5nIGRhdGEgY2FuJ3QgYmUgYXNzdW1lZCBzbyB3aWxsIGxpa2VseSBoYXZlIHRvIGJlIHJlbW92ZWQNCiogQ29sb3IgaGFzIHNvbWUgdmFsdWVzIG9mICJOb25lIiB3aGljaCBpcyBhc3N1bWVkIHRvIGJlIG1pc3NpbmcgZGF0YSwgcmF0aGVyIHRoYW4gdGhlIGJlYW5zIGJlaW5nIGNvbG91cmxlc3MNCiogV2hlcmUgYWx0aXR1ZGUgZGF0YSBpcyBtaXNzaW5nLCB3ZSBjb3VsZCBpbXB1dGUgdGhlIG1lYW4NCiogT25lIG1pc3NpbmcgdmFsdWUgZm9yIFF1YWtlcnMgY2FuIGJlIGFzc3VtZWQgdG8gYmUgMA0KDQoNCmBgYHtyIEZpeCBNaXNzaW5nLCBpbmNsdWRlPUZBTFNFfQ0KYmVhbl9kYXRhJE93bmVyW3doaWNoKGlzLm5hKGJlYW5fZGF0YSRPd25lcikgPT0gVFJVRSldIDwtIGJlYW5fZGF0YSRGYXJtLk5hbWVbd2hpY2goaXMubmEoYmVhbl9kYXRhJE93bmVyKSA9PSBUUlVFKV0NCmJlYW5fZGF0YSRDb3VudHJ5Lm9mLk9yaWdpblt3aGljaChpcy5uYShiZWFuX2RhdGEkQ291bnRyeS5vZi5PcmlnaW4pID09IFRSVUUpXSA8LSAiQ29sb21iaWEiDQpiZWFuX2RhdGEkUmVnaW9uW3doaWNoKGlzLm5hKGJlYW5fZGF0YSRSZWdpb24pID09IFRSVUUpXSA8LSANCiAgdG9sb3dlcihiZWFuX2RhdGEkQ291bnRyeS5vZi5PcmlnaW5bd2hpY2goaXMubmEoYmVhbl9kYXRhJFJlZ2lvbikgPT0gVFJVRSldKQ0KYmVhbl9kYXRhJFJlZ2lvblt3aGljaChiZWFuX2RhdGEkUmVnaW9uID09ICIiKV0gPC0gDQogIHRvbG93ZXIoYmVhbl9kYXRhJENvdW50cnkub2YuT3JpZ2luW3doaWNoKGJlYW5fZGF0YSRSZWdpb24gPT0gIiIpXSkNCmJlYW5fZGF0YSRDb2xvclt3aGljaChiZWFuX2RhdGEkQ29sb3IgPT0gIk5vbmUiKV0gPC0gTkENCmJlYW5fZGF0YSRRdWFrZXJzW3doaWNoKGlzLm5hKGJlYW5fZGF0YSRRdWFrZXJzKSA9PSBUUlVFKV0gPC0gMA0KDQpwcm9wX2JsdWUgPC0gYmVhbl9kYXRhICU+JSANCiAgc2VsZWN0KENvbG9yKSAlPiUNCiAgZmlsdGVyKGlzLm5hKENvbG9yKSA9PSBGQUxTRSkgJT4lIA0KICBtdXRhdGUoY29sX2JsdWUgPSAxIC0gKGFzLm51bWVyaWMoQ29sb3IgPT0gIkdyZWVuIikpKSAlPiUNCiAgc2VsZWN0KGNvbF9ibHVlKSAlPiUNCiAgdW5saXN0KCkgJT4lDQogIG1lYW4oKQ0KDQpgYGANCg0KIyMjIDxzcGFuIHN0eWxlPSJjb2xvcjpzdGVlbGJsdWU7Ij5EYXRhIENvcnJlY3Rpb248L3NwYW4+ICANCg0KVGhlIHZhcmlhYmxlIEhhcnZlc3QuWWVhciBoYXMgdGhlIHllYXIgaW4gdmFyaW91cyBkaWZmZXJlbnQgZm9ybWF0cyBhbmQgbmVlZHMgdG8gYmUgY2xlYW5lZCB1cC4gV2Ugd2lsbCB1c2UgdGhlIGZvbGxvd2luZyBydWxlcyBmb3IgY2xlYW5pbmc6IHdlIGRlZmF1bHQgdG8gYSBzaW5nbGUgeWVhciB2YWx1ZTsgd2hlcmUgdHdvIHllYXJzIGFyZSBnaXZlbiwgd2Ugd2lsbCB1c2UgdGhlIHNlY29uZCB5ZWFyOyB3aGVuIG5vIHllYXIgaXMgZ2l2ZW4gd2UgdXNlIE5BLiBUaGlzIHZhcmlhYmxlIGNhbiB0aGVuIGJlIHRyYW5zZm9ybWVkIHRvIGEgbnVtZXJpYy4gIA0KDQpgYGB7ciBGaXggWWVhciwgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmJlYW5fZGF0YSRIYXJ2ZXN0LlllYXJbd2hpY2goc3RyX2RldGVjdChiZWFuX2RhdGEkSGFydmVzdC5ZZWFyLCAiWzAtOV0iKSA9PSBGQUxTRSldIDwtIE5BDQoNCmJlYW5fZGF0YSA8LSBiZWFuX2RhdGEgJT4lDQogIG11dGF0ZShIYXJ2ZXN0LlllYXIgPSBzdHJfcmVwbGFjZV9hbGwoYmVhbl9kYXRhJEhhcnZlc3QuWWVhciwgIlteWzpkaWdpdDpdXSIsICIiKSwNCiAgICAgICAgIEhhcnZlc3QuWWVhciA9IHN1YnN0cihIYXJ2ZXN0LlllYXIsIHN0YXJ0ID0gKG5jaGFyKEhhcnZlc3QuWWVhcikgLSAzKSwgc3RvcCA9IG5jaGFyKEhhcnZlc3QuWWVhcikpKQ0KDQpiZWFuX2RhdGEkSGFydmVzdC5ZZWFyW3doaWNoKGJlYW5fZGF0YSRIYXJ2ZXN0LlllYXIgPT0gIjA4MDkiKV0gPC0gIjIwMDkiDQpiZWFuX2RhdGEkSGFydmVzdC5ZZWFyW3doaWNoKGJlYW5fZGF0YSRIYXJ2ZXN0LlllYXIgPT0gIjQxMCIpXSA8LSAiMjAxMCINCg0KYmVhbl9kYXRhIDwtIGJlYW5fZGF0YSAlPiUNCiAgbXV0YXRlKEhhcnZlc3QuWWVhciA9IGFzLm51bWVyaWMoSGFydmVzdC5ZZWFyKSkNCg0KYGBgDQoNCg0KDQpgYGB7ciBGaXggYWx0aXR1ZGUsIG1lc3NhZ2U9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpiZWFuX2RhdGEkYWx0X2luX2ZlZXQgPC0gc3RyX2RldGVjdChiZWFuX2RhdGEkQWx0aXR1ZGUsICJmIikNCmJlYW5fZGF0YSRhbHRfaW5fZmVldFt3aGljaChiZWFuX2RhdGEkT3duZXIgPT0gImp1YW4gbHVpcyBhbHZhcmFkbyByb21lcm8iKV0gPC0gVFJVRQ0KYmVhbl9kYXRhJGFsdF9pbl9mZWV0W3doaWNoKGJlYW5fZGF0YSRDb3VudHJ5Lm9mLk9yaWdpbiA9PSAiTXlhbm1hciIpXSA8LSBUUlVFDQpiZWFuX2RhdGEkYWx0X2luX2ZlZXRbd2hpY2goYmVhbl9kYXRhJFggJWluJSBjKDEzMTYsIDEzMzMpKV0gPC0gVFJVRQ0KDQpiZWFuX2RhdGEkQWx0aXR1ZGVbd2hpY2goc3RyX2RldGVjdChiZWFuX2RhdGEkQWx0aXR1ZGUsICJbMC05XSIpID09IEZBTFNFKV0gPC0gTkENCg0KYmVhbl9kYXRhIDwtIGJlYW5fZGF0YSAlPiUNCiAgbXV0YXRlKG5ld19hbHQgPSBzdHJfcmVwbGFjZV9hbGwoYmVhbl9kYXRhJEFsdGl0dWRlLCAiW15bOmRpZ2l0Ol1dIiwgIiIpLA0KICAgICAgICAgbG93X2FsdCA9IE5BLA0KICAgICAgICAgaGlnaF9hbHQgPSBOQSkNCg0KYmVhbl9kYXRhJG5ld19hbHRbd2hpY2goYmVhbl9kYXRhJEFsdGl0dWRlID09ICIxMm9vIildIDwtICIxMjAwIg0KYmVhbl9kYXRhJG5ld19hbHRbd2hpY2goYmVhbl9kYXRhJEFsdGl0dWRlID09ICIxLjIiKV0gPC0gIjEyMDAiDQpiZWFuX2RhdGEkbmV3X2FsdFt3aGljaChiZWFuX2RhdGEkQWx0aXR1ZGUgPT0gIjEuMyIpXSA8LSAiMTMwMCINCmJlYW5fZGF0YSRuZXdfYWx0W3doaWNoKGJlYW5fZGF0YSRuZXdfYWx0ID09ICIxOTAxNjQiKV0gPC0gIjE5MDIiDQpiZWFuX2RhdGEkbmV3X2FsdFt3aGljaChiZWFuX2RhdGEkbmV3X2FsdCA9PSAiMTEwMDAiKV0gPC0gIjExMDAiDQpiZWFuX2RhdGEkbmV3X2FsdFt3aGljaChiZWFuX2RhdGEkbmV3X2FsdCA9PSAiMTEwMDAwIildIDwtICIxMTAwIg0KYmVhbl9kYXRhJG5ld19hbHRbd2hpY2goYmVhbl9kYXRhJG5ld19hbHQgPT0gIjEwMDIwMDAiKV0gPC0gIjEwMDAyMDAwIg0KYmVhbl9kYXRhJG5ld19hbHRbd2hpY2goYmVhbl9kYXRhJG5ld19hbHQgPT0gIjE4MDA1OTAwIildIDwtICIxODAwIg0KDQpiZWFuX2RhdGEkbG93X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gNildIDwtIA0KICBzdWJzdHIoYmVhbl9kYXRhJG5ld19hbHRbd2hpY2gobmNoYXIoYmVhbl9kYXRhJG5ld19hbHQpID09IDYpXSwgc3RhcnQgPSAxLCBzdG9wID0gMykNCg0KYmVhbl9kYXRhJGhpZ2hfYWx0W3doaWNoKG5jaGFyKGJlYW5fZGF0YSRuZXdfYWx0KSA9PSA2KV0gPC0gDQogIHN1YnN0cihiZWFuX2RhdGEkbmV3X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gNildLCBzdGFydCA9IDQsIHN0b3AgPSA2KQ0KDQpiZWFuX2RhdGEkbG93X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gNyldIDwtIA0KICBzdWJzdHIoYmVhbl9kYXRhJG5ld19hbHRbd2hpY2gobmNoYXIoYmVhbl9kYXRhJG5ld19hbHQpID09IDcpXSwgc3RhcnQgPSAxLCBzdG9wID0gMykNCg0KYmVhbl9kYXRhJGhpZ2hfYWx0W3doaWNoKG5jaGFyKGJlYW5fZGF0YSRuZXdfYWx0KSA9PSA3KV0gPC0gDQogIHN1YnN0cihiZWFuX2RhdGEkbmV3X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gNyldLCBzdGFydCA9IDQsIHN0b3AgPSA3KQ0KDQpiZWFuX2RhdGEkbG93X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gOCldIDwtIA0KICBzdWJzdHIoYmVhbl9kYXRhJG5ld19hbHRbd2hpY2gobmNoYXIoYmVhbl9kYXRhJG5ld19hbHQpID09IDgpXSwgc3RhcnQgPSAxLCBzdG9wID0gNCkNCg0KYmVhbl9kYXRhJGhpZ2hfYWx0W3doaWNoKG5jaGFyKGJlYW5fZGF0YSRuZXdfYWx0KSA9PSA4KV0gPC0gDQogIHN1YnN0cihiZWFuX2RhdGEkbmV3X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gOCldLCBzdGFydCA9IDUsIHN0b3AgPSA4KQ0KDQpiZWFuX2RhdGEkbG93X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gMyldIDwtIGJlYW5fZGF0YSRuZXdfYWx0W3doaWNoKG5jaGFyKGJlYW5fZGF0YSRuZXdfYWx0KSA9PSAzKV0NCmJlYW5fZGF0YSRoaWdoX2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gMyldIDwtIGJlYW5fZGF0YSRuZXdfYWx0W3doaWNoKG5jaGFyKGJlYW5fZGF0YSRuZXdfYWx0KSA9PSAzKV0NCg0KYmVhbl9kYXRhJGxvd19hbHRbd2hpY2gobmNoYXIoYmVhbl9kYXRhJG5ld19hbHQpID09IDQpXSA8LSBiZWFuX2RhdGEkbmV3X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gNCldDQpiZWFuX2RhdGEkaGlnaF9hbHRbd2hpY2gobmNoYXIoYmVhbl9kYXRhJG5ld19hbHQpID09IDQpXSA8LSBiZWFuX2RhdGEkbmV3X2FsdFt3aGljaChuY2hhcihiZWFuX2RhdGEkbmV3X2FsdCkgPT0gNCldDQoNCmJlYW5fZGF0YSA8LSBiZWFuX2RhdGEgJT4lDQogIG11dGF0ZShsb3dfYWx0ID0gYXMubnVtZXJpYyhsb3dfYWx0KSwNCiAgICAgICAgIGhpZ2hfYWx0ID0gYXMubnVtZXJpYyhoaWdoX2FsdCkpDQoNCmJlYW5fZGF0YSRsb3dfYWx0W3doaWNoKGJlYW5fZGF0YSRhbHRfaW5fZmVldCA9PSBUUlVFKV0gPC0gDQogIGJlYW5fZGF0YSRsb3dfYWx0W3doaWNoKGJlYW5fZGF0YSRhbHRfaW5fZmVldCA9PSBUUlVFKV0gLyAzLjI4MDg0DQoNCmJlYW5fZGF0YSRoaWdoX2FsdFt3aGljaChiZWFuX2RhdGEkYWx0X2luX2ZlZXQgPT0gVFJVRSldIDwtIA0KICBiZWFuX2RhdGEkaGlnaF9hbHRbd2hpY2goYmVhbl9kYXRhJGFsdF9pbl9mZWV0ID09IFRSVUUpXSAvIDMuMjgwODQNCg0KYmVhbl9kYXRhIDwtIGJlYW5fZGF0YSAlPiUNCiAgbXV0YXRlKG1lYW5fYWx0ID0gKGxvd19hbHQgKyBoaWdoX2FsdCkgLyAyLA0KICAgICAgICAgcmFuZ2VfYWx0ID0gaGlnaF9hbHQgLSBsb3dfYWx0KQ0KDQpgYGANCg0KU29tZSBvZiB0aGUgYWx0aXR1ZGUgZGF0YSBhbHNvIHJlcXVpcmVzIGNvcnJlY3Rpb24gYXMgaXQgYXBwZWFycyB0byBoYXZlIGJlZW4gZW50ZXJlZCBpbmNvcnJlY3RseSBvciBtaXNyZWFkLiBXZSB3aWxsIHJlLXBhcnNlIHRoZSBvcmlnaW5hbCBBbHRpdHVkZSBkYXRhIHRvIGNvcnJlY3QgdGhlIGVycm9ycy4gQWZ0ZXIgcmUtcGFyc2luZyB0byBudW1lcmljIHZhbHVlcyBmb3IgbWVhbiBhbmQgcmFuZ2Ugb2YgYWx0aXR1ZGUsIHRoZSBudW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgYmVjb21lcyBgciBzdW0oaXMubmEoYmVhbl9kYXRhJG1lYW5fYWx0KSlgLiAgDQoNCkFwYXJ0IGZyb20gaW4gSGF3YWlpIGFuZCBQdWVydG8gUmljbywgdGhlIFVuaXRlZCBTdGF0ZXMgZG9lcyBub3QgcHJvZHVjZSBjb2ZmZWUsIHNvIHdoZXJlIENvdW50cnkub2YuT3JpZ2luIGhhcyBiZWVuIGdpdmVuIGFzIFVuaXRlZCBTdGF0ZXMsIHRoaXMgYXBwZWFycyB0byBiZSBhIGRhdGEgZXJyb3IuIFdlIHdpbGwgZml4IHRoaXMgZGF0YSB1c2luZyBSZWdpb24gYW5kIFByb2R1Y2VyIGluZm9ybWF0aW9uLiAgDQoNCmBgYHtyIEZpeCBDb3VudHJ5LCBpbmNsdWRlPUZBTFNFfQ0KYmVhbl9kYXRhJENvdW50cnkub2YuT3JpZ2luW3doaWNoKHN0cl9kZXRlY3QoYmVhbl9kYXRhJENvdW50cnkub2YuT3JpZ2luLCAiVGFuemFuaWEiKSA9PSBUUlVFKV0gPC0gIlRhbnphbmlhIg0KDQpiZWFuX2RhdGEkQ291bnRyeS5vZi5PcmlnaW5bd2hpY2goYmVhbl9kYXRhJFJlZ2lvbiA9PSAiYW50aW9xdWlhIildIDwtICJDb2xvbWJpYSINCmJlYW5fZGF0YSRDb3VudHJ5Lm9mLk9yaWdpblt3aGljaChiZWFuX2RhdGEkUmVnaW9uID09ICJiZXJhc3RhZ2kiKV0gPC0gIkluZG9uZXNpYSINCmJlYW5fZGF0YSRDb3VudHJ5Lm9mLk9yaWdpblt3aGljaChiZWFuX2RhdGEkUHJvZHVjZXIgPT0gIkp1YW5BbmEgQ29mZmVlIEFzc29jaWF0aW9uIildIDwtICJHdWF0ZW1hbGEiDQpiZWFuX2RhdGEkQ291bnRyeS5vZi5PcmlnaW5bd2hpY2goYmVhbl9kYXRhJFByb2R1Y2VyID09ICJTZXRodXJhbWFuIEVzdGF0ZXMiKV0gPC0gIkluZGlhIg0KYmVhbl9kYXRhJENvdW50cnkub2YuT3JpZ2luW3doaWNoKGJlYW5fZGF0YSRSZWdpb24gPT0gImt3YW56YSBub3J0ZSBwcm92aW5jZSBhbmdvbGEiKV0gPC0gIkFuZ29sYSINCg0KYGBgDQoNCg0KYGBge3IgU2F2ZSBkYXRhLCBtZXNzYWdlPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kc2F2ZShiZWFuX2RhdGEsIGZpbGU9IGhlcmUoImRhdGEvaW50ZXJpbS9iZWFuX2RhdGFfY2xlYW4uUkRhdGEiKSkNCmBgYA0KDQojIyA8c3BhbiBzdHlsZT0iY29sb3I6dGVhbDsiPklucHV0IFZhcmlhYmxlczwvc3Bhbj4gIA0KDQpMZXQncyBsb29rIGF0IGRpc3RyaWJ1dGlvbnMgb2YgdGhlIGlucHV0IHZhcmlhYmxlcy4gV2Ugd2lsbCBwbG90IHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgYXMgYmFyIGNoYXJ0cywgc2hvd2luZyB0aGUgbW9zdCBwb3B1bGFyIHZhbHVlcy4gIA0KDQpgYGB7ciBJbnB1dCB2YXJzLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcGxvdF8xIDwtIGRpc3RfYmFyX3Bsb3QoYmVhbl9kYXRhLCBPd25lciwgMTApDQpwbG90XzIgPC0gZGlzdF9iYXJfcGxvdChiZWFuX2RhdGEsIFNwZWNpZXMpDQpwbG90XzMgPC0gZGlzdF9iYXJfcGxvdChiZWFuX2RhdGEsIENvdW50cnkub2YuT3JpZ2luLCAxMCkNCnBsb3RfNCA8LSBkaXN0X2Jhcl9wbG90KGJlYW5fZGF0YSwgVmFyaWV0eSwgMTApDQpwbG90XzUgPC0gZGlzdF9iYXJfcGxvdChiZWFuX2RhdGEsIFByb2Nlc3NpbmcuTWV0aG9kLCA1KQ0KcGxvdF82IDwtIGRpc3RfYmFyX3Bsb3QoYmVhbl9kYXRhLCBDb2xvcikNCg0KZ3JpZF8xIDwtIGFycmFuZ2VHcm9iKHBsb3RfMSwgcGxvdF8yLCBwbG90XzMsIHBsb3RfNCwgcGxvdF81LCBwbG90XzYsIG5jb2wgPSAyKQ0KZ2dzYXZlKGdyaWRfMSwgZmlsZSA9IGhlcmUoInBsb3RzL2dyaWRfMS5wbmciKSwgaGVpZ2h0ID0gMTIsIGRwaSA9IDIwMCkNCg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgiL3Bsb3RzL2dyaWRfMS5wbmciKSkNCmBgYA0KDQpXZSB3aWxsIHBsb3QgdGhlIG51bWVyaWMgdmFyaWFibGVzIGFzIGhpc3RvZ3JhbXMuICANCg0KYGBge3IgSGlzdG9ncmFtcywgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3RfMSA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoSGFydmVzdC5ZZWFyKSkgKyBnZW9tX2JhcihmaWxsID0gImRhcmtncmF5IiwgY29sb3VyID0gImJsYWNrIikgKyB0aGVtZV9taW5pbWFsKCkNCnBsb3RfMiA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMobmV3X2RhdGUpKSArIGdlb21fZGVuc2l0eShmaWxsID0gImRhcmtncmF5IiwgY29sb3VyID0gImJsYWNrIikgKyB0aGVtZV9taW5pbWFsKCkgKyANCiAgbGFicyh4ID0gIkdyYWRpbmcgRGF0ZSIpDQpwbG90XzMgPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKG1lYW5fYWx0KSkgKyBnZW9tX2RlbnNpdHkoZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG91ciA9ICJibGFjayIpICsgdGhlbWVfbWluaW1hbCgpICsgDQogIGxhYnMoeCA9ICJNZWFuIEFsdGl0dWRlIChtKSIpDQpwbG90XzQgPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKG5ld193dCkpICsgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvdXIgPSAiYmxhY2siKSArIHRoZW1lX21pbmltYWwoKSArIA0KICBsYWJzKHggPSAiU2FtcGxlIFdlaWdodCAoa2cpIikgKyB4bGltKDAsIDEwMCkNCnBsb3RfNSA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoTW9pc3R1cmUpKSArIGdlb21fZGVuc2l0eShmaWxsID0gImRhcmtncmF5IiwgY29sb3VyID0gImJsYWNrIikgKyB0aGVtZV9taW5pbWFsKCkNCnBsb3RfNiA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoQ2F0ZWdvcnkuT25lLkRlZmVjdHMpKSArIGdlb21fZGVuc2l0eShmaWxsID0gImRhcmtncmF5IiwgY29sb3VyID0gImJsYWNrIikgKyANCiAgdGhlbWVfbWluaW1hbCgpDQpwbG90XzcgPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKENhdGVnb3J5LlR3by5EZWZlY3RzKSkgKyBnZW9tX2RlbnNpdHkoZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG91ciA9ICJibGFjayIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpwbG90XzggPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKFF1YWtlcnMpKSArIGdlb21fYmFyKGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvdXIgPSAiYmxhY2siKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpncmlkXzIgPC0gYXJyYW5nZUdyb2IocGxvdF8zLCBwbG90XzEsIHBsb3RfNCwgcGxvdF8yLCBwbG90XzUsIHBsb3RfNiwgcGxvdF83LCBwbG90XzgpDQpnZ3NhdmUoZ3JpZF8yLCBmaWxlID0gaGVyZSgicGxvdHMvZ3JpZF8yLnBuZyIpLCBoZWlnaHQgPSA5LCBkcGkgPSAyMDApDQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoIi9wbG90cy9ncmlkXzIucG5nIikpDQpgYGANCg0KV2hhdCB3ZSd2ZSBkaXNjb3ZlcmVkIGZyb20gdGhlIGlucHV0IHZhcmlhYmxlczoNCg0KKiBPbmx5IGEgdmVyeSBzbWFsbCBudW1iZXIgb2YgUm9idXN0YSBiZWFucyBoYXZlIGJlZW4gZ3JhZGVkDQoqIFRoZXJlIGFyZSBtYW55IGRpZmZlcmVudCB2YWx1ZXMgZm9yIE93bmVyIG9mIHdoaWNoIG1vc3QgYXJlIG9mIGEgbG93IGZyZXF1ZW5jeQ0KKiBDb3VudHJ5IG9mIG9yaWdpbiBpcyBxdWl0ZSB3aWRlbHkgZGlzdHJpYnV0ZWQgd2l0aCAzNiBjb3VudHJpZXMgcmVwcmVzZW50ZWQgLSBNZXhpY28gaGFzIHRoZSBsYXJnZXN0IGFtb3VudCBvZiBkYXRhDQoqIFdlIGhhdmUgMjkgZGlmZmVyZW50IHZhcmlldGllcywgd2l0aCBDYXR1cnJhLCBCb3VyYm9uIGFuZCBUeXBpY2EgYmVpbmcgbW9zdCBjb21tb24NCiogTW9zdCBvZiB0aGUgc2FtcGxlcyBoYXZlIGJlZW4gcHJvY2Vzc2VkIHVzaW5nIHRoZSBXYXNoZWQgLyBXZXQgbWV0aG9kIGFuZCB0aGUgcmVtYWluZGVyIGFyZSBtb3N0bHkgTmF0dXJhbCAvIERyeQ0KKiBNb3N0IG9mIHRoZSBncmVlbiBiZWFucyBhcmUgY29sb3VyZWQgR3JlZW4sIHRoZSBwcm9wb3J0aW9uIHdpdGggYW55IEJsdWUgY29sb3VyIGlzIGByIHJvdW5kKHByb3BfYmx1ZSAqIDEwMCwgMClgJQ0KKiBDb2ZmZWUgaXMgbWFpbmx5IGdyb3duIGJldHdlZW4gMTAwMCBhbmQgMjAwMCBtOyBzb21lIGlzIGFsc28gZ3Jvd24gYXQgbG93ZXIgYWx0aXR1ZGVzIGJ1dCB2ZXJ5IGxpdHRsZSBpcyBncm93biBhdCBoaWdoZXIgYWx0aXR1ZGVzDQoqIFNhbXBsZXMgYXJlIGdlbmVyYWxseSBwcm92aWRlZCBlaXRoZXIgaW4gYmFncyBvZiA8MTAga2cgb3IgNTAtODAga2cNCiogSGFydmVzdC5ZZWFyIGFuZCBHcmFkaW5nLkRhdGUgc2hvdyBhIHNpbWlsYXIgcGF0dGVybiwgc3VnZ2VzdGluZyB0aGF0IHRoZSBhZ2Ugb2YgdGhlIGJlYW5zIGF0IGdyYWRpbmcgaXMgZ2VuZXJhbGx5IHNpbWlsYXIgYmV0d2VlbiBzYW1wbGVzOyB3ZSBkb24ndCBrbm93IGV4YWN0bHkgd2hlbiB0aGUgYmVhbnMgd2VyZSBoYXJ2ZXN0ZWQgc28gYW55IGNhbGN1bGF0aW9uIG9mIGFnZSBvZiBiZWFucyB1c2luZyBhc3N1bXB0aW9ucyBpcyBwcm9uZSB0byBlcnJvcg0KKiBTb21lIHNhbXBsZXMgYXBwZWFyIHRvIGJlIGNvbXBsZXRlbHkgZHJ5IGZyb20gdGhlaXIgTW9pc3R1cmUgdmFsdWVzLCB3aGljaCBzZWVtcyBhIGJpdCBvZGQgYXMgdGhlIG1ham9yaXR5IG9mIHNhbXBsZXMgaGF2ZSAwLjA4LTAuMTQlIHdhdGVyIGNvbnRlbnQNCiogQmVhbiBkZWZlY3RzIG9mIENhdGVnb3J5IE9uZSBhcmUgcmFyZSwgYXMgYXJlIFF1YWtlcnMNCiogQmVhbiBkZWZlY3RzIG9mIENhdGVnb3J5IFR3byBhcmUgbW9yZSBjb21tb24gYW5kIHRoZSBkaXN0cmlidXRpb24gYXBwZWFycyB0byBiZSBhcHByb3hpbWF0ZWx5IGxvZy1ub3JtYWwNCg0KIyMgPHNwYW4gc3R5bGU9ImNvbG9yOnRlYWw7Ij5PdXRwdXQgVmFyaWFibGVzPC9zcGFuPiAgDQoNCldlIGhhdmUgMTAgaW5kaXZpZHVhbCBxdWFsaXR5IHNjb3JlcywgcGx1cyBhIHN1bW1lZCBvdmVyYWxsIHF1YWxpdHkuIExldCdzIHZpc3VhbGlzZSBob3cgdGhlc2Ugc2NvcmVzIGFyZSBkaXN0cmlidXRlZC4gIA0KDQpgYGB7ciBPdXRwdXQgZGlzdCwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCnBsb3RfMSA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoQXJvbWEpKSArIGdlb21fZGVuc2l0eShmaWxsID0gImRhcmtncmF5IiwgY29sb3VyID0gImJsYWNrIikgKyB0aGVtZV9taW5pbWFsKCkNCnBsb3RfMiA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoRmxhdm9yKSkgKyBnZW9tX2RlbnNpdHkoZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG91ciA9ICJibGFjayIpICsgdGhlbWVfbWluaW1hbCgpDQpwbG90XzMgPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKEFmdGVydGFzdGUpKSArIGdlb21fZGVuc2l0eShmaWxsID0gImRhcmtncmF5IiwgY29sb3VyID0gImJsYWNrIikgKyB0aGVtZV9taW5pbWFsKCkNCnBsb3RfNCA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoQWNpZGl0eSkpICsgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvdXIgPSAiYmxhY2siKSArIHRoZW1lX21pbmltYWwoKQ0KcGxvdF81IDwtIGdncGxvdChiZWFuX2RhdGEsIGFlcyhCb2R5KSkgKyBnZW9tX2RlbnNpdHkoZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG91ciA9ICJibGFjayIpICsgdGhlbWVfbWluaW1hbCgpDQpwbG90XzYgPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKEJhbGFuY2UpKSArIGdlb21fZGVuc2l0eShmaWxsID0gImRhcmtncmF5IiwgY29sb3VyID0gImJsYWNrIikgKyB0aGVtZV9taW5pbWFsKCkNCnBsb3RfNyA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoVW5pZm9ybWl0eSkpICsgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiZGFya2dyYXkiLCBjb2xvdXIgPSAiYmxhY2siKSArIHRoZW1lX21pbmltYWwoKQ0KcGxvdF84IDwtIGdncGxvdChiZWFuX2RhdGEsIGFlcyhDbGVhbi5DdXApKSArIGdlb21fZGVuc2l0eShmaWxsID0gImRhcmtncmF5IiwgY29sb3VyID0gImJsYWNrIikgKyB0aGVtZV9taW5pbWFsKCkNCnBsb3RfOSA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoU3dlZXRuZXNzKSkgKyBnZW9tX2RlbnNpdHkoZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG91ciA9ICJibGFjayIpICsgdGhlbWVfbWluaW1hbCgpDQpwbG90XzEwIDwtIGdncGxvdChiZWFuX2RhdGEsIGFlcyhDdXBwZXIuUG9pbnRzKSkgKyBnZW9tX2RlbnNpdHkoZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG91ciA9ICJibGFjayIpICsgdGhlbWVfbWluaW1hbCgpDQpwbG90XzExIDwtIGdncGxvdChiZWFuX2RhdGEsIGFlcyhUb3RhbC5DdXAuUG9pbnRzKSkgKyBnZW9tX2RlbnNpdHkoZmlsbCA9ICJkYXJrZ3JheSIsIGNvbG91ciA9ICJibGFjayIpICsgdGhlbWVfbWluaW1hbCgpDQoNCmdyaWRfMyA8LSBhcnJhbmdlR3JvYihwbG90XzEsIHBsb3RfMiwgcGxvdF8zLCBwbG90XzQsIHBsb3RfNSwgcGxvdF82LCBwbG90XzcsIHBsb3RfOCwgcGxvdF85LCBwbG90XzEwLCBwbG90XzExKQ0KZ2dzYXZlKGdyaWRfMywgZmlsZSA9IGhlcmUoInBsb3RzL2dyaWRfMy5wbmciKSwgaGVpZ2h0ID0gMTIsIGRwaSA9IDIwMCkNCg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgiL3Bsb3RzL2dyaWRfMy5wbmciKSkNCmBgYA0KDQpGcm9tIHRoZSBvdXRwdXQgdmFyaWFibGVzLCB3ZSBzZWU6ICANCg0KKiBBcm9tYSwgRmxhdm9yLCBBZnRlcnRhc3RlLCBBY2lkaXR5LCBCb2R5LCBCYWxhbmNlIGFuZCBvdmVyYWxsIEN1cHBlci5Qb2ludHMgYXJlIHNpbWlsYXJseSBkaXN0cmlidXRlZCB3aXRoIG1lYW4gdmFsdWVzIGFyb3VuZCA3LjUgYW5kIGFwcHJveGltYXRlIHJhbmdlIG9mIDYuNS04LjUNCiogVW5pZm9ybWl0eSwgQ2xlYW4uQ3VwIGFuZCBTd2VldG5lc3MgYXJlIGdlbmVyYWxseSBoaWdobHkgcmF0ZWQgd2l0aCBsaXR0bGUgZGV2aWF0aW9uIGZyb20gc2NvcmVzIG9mIDEwDQoqIFRvdGFsLkN1cC5Qb2ludHMgaGFzIGEgZGlzdHJpYnV0aW9uIHdpdGggYSBtZWFuIG9mIGFwcHJveGltYXRlbHkgODMgYW5kIGdlbmVyYWwgcmFuZ2Ugb2YgNzctODcNCg0KV2UgYXJlIHJlYWxseSBpbnRlcmVzdGVkIGluICoqVG90YWwuQ3VwLlBvaW50cyoqIGFzIG91ciBtYWluIHF1YWxpdHkgdmFyaWFibGUsIHNvIGxldCdzIGxvb2sgYXQgaG93IHRoaXMgb3V0cHV0IHZhcmlhYmxlIGlzIGltcGFjdGVkIGJ5IHRoZSBkaWZmZXJlbnQgaW5wdXQgdmFyaWFibGVzLiAgDQoNCiMjIDxzcGFuIHN0eWxlPSJjb2xvcjp0ZWFsOyI+VmFyaWFibGUgUmVsYXRpb25zaGlwczwvc3Bhbj4gIA0KDQpXZSBjYW4gZHJhdyBib3hwbG90cyB0byBpbGx1c3RyYXRlIHJlbGF0aW9uc2hpcHMgb2YgVG90YWwuQ3VwLlBvaW50cyB3aXRoIGNhdGVnb3JpY2FsIGlucHV0IHZhcmlhYmxlcy4gIA0KDQpgYGB7ciBCb3ggcGxvdHMsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpwbG90XzEgPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKFNwZWNpZXMsIFRvdGFsLkN1cC5Qb2ludHMpKSArIGdlb21fYm94cGxvdChmaWxsID0gImRhcmtncmF5IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9mbGlwKCkgKyBnZ3RpdGxlKCJFZmZlY3Qgb2YgU3BlY2llcyBvbiBUb3RhbC5DdXAuUG9pbnRzIikNCnBsb3RfMiA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoQ291bnRyeS5vZi5PcmlnaW4sIFRvdGFsLkN1cC5Qb2ludHMpKSArIGdlb21fYm94cGxvdChmaWxsID0gImRhcmtncmF5IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9mbGlwKCkgKyBnZ3RpdGxlKCJFZmZlY3Qgb2YgQ291bnRyeS5vZi5PcmlnaW4gb24gVG90YWwuQ3VwLlBvaW50cyIpDQpwbG90XzMgPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKFZhcmlldHksIFRvdGFsLkN1cC5Qb2ludHMpKSArIGdlb21fYm94cGxvdChmaWxsID0gImRhcmtncmF5IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9mbGlwKCkgKyBnZ3RpdGxlKCJFZmZlY3Qgb2YgVmFyaWV0eSBvbiBUb3RhbC5DdXAuUG9pbnRzIikNCnBsb3RfNCA8LSBnZ3Bsb3QoYmVhbl9kYXRhLCBhZXMoUHJvY2Vzc2luZy5NZXRob2QsIFRvdGFsLkN1cC5Qb2ludHMpKSArIGdlb21fYm94cGxvdChmaWxsID0gImRhcmtncmF5IikgKw0KICB0aGVtZV9taW5pbWFsKCkgKyBjb29yZF9mbGlwKCkgKyBnZ3RpdGxlKCJFZmZlY3Qgb2YgUHJvY2Vzc2luZy5NZXRob2Qgb24gVG90YWwuQ3VwLlBvaW50cyIpDQpwbG90XzUgPC0gZ2dwbG90KGJlYW5fZGF0YSwgYWVzKENvbG9yLCBUb3RhbC5DdXAuUG9pbnRzKSkgKyBnZW9tX2JveHBsb3QoZmlsbCA9ICJkYXJrZ3JheSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgY29vcmRfZmxpcCgpICsgZ2d0aXRsZSgiRWZmZWN0IG9mIENvbG9yIG9uIFRvdGFsLkN1cC5Qb2ludHMiKQ0KDQpnZ3NhdmUocGxvdF8xLCBmaWxlID0gaGVyZSgicGxvdHMvcGxvdF8xLnBuZyIpLCBoZWlnaHQgPSAyLCBkcGkgPSAyMDApDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCIvcGxvdHMvcGxvdF8xLnBuZyIpKQ0KDQpnZ3NhdmUocGxvdF8yLCBmaWxlID0gaGVyZSgicGxvdHMvcGxvdF8yLnBuZyIpLCBoZWlnaHQgPSA4LCBkcGkgPSAyMDApDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCIvcGxvdHMvcGxvdF8yLnBuZyIpKQ0KDQpnZ3NhdmUocGxvdF8zLCBmaWxlID0gaGVyZSgicGxvdHMvcGxvdF8zLnBuZyIpLCBoZWlnaHQgPSA2LCBkcGkgPSAyMDApDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCIvcGxvdHMvcGxvdF8zLnBuZyIpKQ0KDQpnZ3NhdmUocGxvdF80LCBmaWxlID0gaGVyZSgicGxvdHMvcGxvdF80LnBuZyIpLCBoZWlnaHQgPSAzLCBkcGkgPSAyMDApDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCIvcGxvdHMvcGxvdF80LnBuZyIpKQ0KDQpnZ3NhdmUocGxvdF81LCBmaWxlID0gaGVyZSgicGxvdHMvcGxvdF81LnBuZyIpLCBoZWlnaHQgPSAzLCBkcGkgPSAyMDApDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCIvcGxvdHMvcGxvdF81LnBuZyIpKQ0KDQoNCmBgYA0KDQpXaGF0IGRvIHRoZXNlIHJlbGF0aW9uc2hpcHMgdGVsbCB1cz8NCg0KKiBBcmFiaWNhIGJlYW5zIHRlbmQgdG8gZ2l2ZSBzbGlnaHRseSBoaWdoZXIgc2NvcmVzIHRoYW4gUm9idXN0YQ0KKiBXZSBzZWUgbW9yZSB2YXJpYXRpb24gaW4gc2NvcmVzIHdpdGggQ291bnRyeS5vZi5PcmlnaW4gYW5kIGJlYW4gVmFyaWV0eSAtIEV0aGlvcGlhbiBZaXJnYWNoZWZmZSBhcHBlYXJzIHRvIGJlIGdlbmVyYWxseSB0aGUgY29mZmVlIHdpdGggaGlnaGVzdCBzY29yZXMNCiogV2Ugc2VlIGxlc3MgdmFyaWF0aW9uIGluIHNjb3JlcyB3aXRoIFByb2Nlc3NpbmcuTWV0aG9kIGFuZCBDb2xvcg0KDQpXZSBjYW4gZHJhdyBzY2F0dGVyIHBsb3RzIHRvIGlsbHVzdHJhdGUgcmVsYXRpb25zaGlwcyBvZiBUb3RhbC5DdXAuUG9pbnRzIHdpdGggbnVtZXJpYyBpbnB1dCB2YXJpYWJsZXMuICANCg0KYGBge3IgU2NhdHRlciBwbG90cywgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBsb3RfZGYgPC0gYmVhbl9kYXRhICU+JQ0KICBtdXRhdGUoU2FtcGxlLldlaWdodCA9IG5ld193dCwNCiAgICAgICAgIEdyYWRpbmcuRGF0ZSA9IG5ld19kYXRlLA0KICAgICAgICAgTWVhbi5BbHRpdHVkZSA9IG1lYW5fYWx0KSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCJNZWFuLkFsdGl0dWRlIiwgIkhhcnZlc3QuWWVhciIsICJTYW1wbGUuV2VpZ2h0IiwgIk1vaXN0dXJlIiwgIkNhdGVnb3J5Lk9uZS5EZWZlY3RzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAiQ2F0ZWdvcnkuVHdvLkRlZmVjdHMiLCAiUXVha2VycyIpLCBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpDQoNCnBsb3RfMSA8LSBnZ3Bsb3QocGxvdF9kZiwgYWVzKHZhbHVlLCBUb3RhbC5DdXAuUG9pbnRzKSkgKyBnZW9tX3BvaW50KCkgKyANCiAgZmFjZXRfd3JhcCh+IHZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZV94IiwgbmNvbCA9IDIpICsgdGhlbWVfYncoKQ0KDQpnZ3NhdmUocGxvdF8xLCBmaWxlID0gaGVyZSgicGxvdHMvcGxvdF8xLnBuZyIpLCBoZWlnaHQgPSAxMiwgZHBpID0gMjAwKQ0KDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCIvcGxvdHMvcGxvdF8xLnBuZyIpKQ0KYGBgDQoNClRoZXJlIGFyZSBubyBjbGVhciB0cmVuZHMgaW4gdGhlc2UgcmVsYXRpb25zaGlwcy4gSG93ZXZlciwgdGhlIGJlc3Qgc2NvcmVzLCBpLmUuID44NSBUb3RhbC5DdXAuUG9pbnRzLCB0ZW5kIHRvIG9ubHkgYmUgc2VlbiB3aGVuIHRoZSBudW1iZXIgb2YgZGVmZWN0cyBhbmQgUXVha2VycyBhcmUgbG93LiBRdWFsaXR5IGFwcGVhcnMgdG8gcGVyaGFwcyBpbmNyZWFzZSB1cCB0byBhcm91bmQgMjAwMCBtIGFsdGl0dWRlLCBhbmQgdGhlbiBkZWNyZWFzZSBmb3IgdGhlIGZldyBjb2ZmZWVzIGF0IGhpZ2hlciBhbHRpdHVkZXMuICANCg0KV2UgY2FuIG5vdyB0YWtlIHRoaXMgZGF0YSBmb3J3YXJkIGluIGFuIGF0dGVtcHQgdG8gbW9kZWwgVG90YWwuQ3VwLlBvaW50cy4gIA0KDQoqKioNCg0KRW5kDQo=