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.

Input Variables

Let’s look at distributions of the input variables. We will plot the categorical variables as bar charts, showing the most popular values.

We will plot the numeric variables as histograms.

What we’ve discovered from the input variables:

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:

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?

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=