Motivations

Customer Segments (or Market Segmentation) allow the companies to be able to utilize their resources (time, finance) to serve their’s goals: increasing sales, increasing profits, retaining important customers as well as implementing marketing campaigns more effectively, which based on the understanding of their customer’s behavior, habits, and preferences.

Method and Data

RFM is a method used for analyzing customer value. It is commonly used in database marketing and direct marketing and has received particular attention in retail and professional services industries.

RFM stands for the three dimensions: - R (Recency) The value of how recently a customer purchased at the establishment. - F (Frequency) How frequent the customer’s transactions are at the establishment. - M (Monetary) The dollar (or pounds in our case) value of all the transactions that the customer made at the establishment.

F and M are inputs for a period (1 year, a month, a period). Particularly R depends on the modeler preference and it does not affect the model’s results.

Data can be download here. First, let’s have a look at the data:

InvoiceNo StockCode Description Quantity
561197 22120 WELCOME WOODEN BLOCK LETTERS 4
542174 21326 AGED GLASS SILVER T-LIGHT HOLDER 36
553009 23191 BUNDLE OF 3 RETRO NOTE BOOKS 2
576837 84661B BLACK SQUARE TABLE CLOCK 1
539836 85232D SET/3 DECOUPAGE STACKING TINS 1
556187 47590A BLUE HAPPY BIRTHDAY BUNTING 3
578664 23267 SET OF 4 SANTA PLACE SETTINGS 2
577358 21240 BLUE POLKADOT CUP 1
575324 23130 MISTLETOE HEART WREATH GREEN 4
554512 21390 FILIGRIS HEART WITH BUTTERFLY 1
InvoiceDate UnitPrice CustomerID Country
9/20/2011 11:32 1.85 13767 United Kingdom
3/28/2011 11:34 1.25 17841 United Kingdom
10/12/2011 12:54 2.55 12691 France
6/23/2011 11:38 0.42 13268 United Kingdom
12/15/2010 15:50 8.50 17980 United Kingdom
5/27/2011 12:41 8.50 16275 United Kingdom
12/7/2011 14:01 1.65 16426 United Kingdom
8/1/2011 11:23 2.10 14408 United Kingdom
6/23/2011 18:44 1.25 15023 United Kingdom
7/7/2011 16:30 4.13 NA United Kingdom

The variables are really descriptive. We should also make a preliminary assessment of the quality of the data. First of all, let have a look at the missing rate:

## # A tibble: 1 x 8
##   InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID
##       <dbl>     <dbl>       <dbl>    <dbl>       <dbl>     <dbl>      <dbl>
## 1         0         0       0.268        0           0         0       24.9
## # … with 1 more variable: Country <dbl>

Approximately 27% of stockcode is without description. Similarly, ~ 25% customters are not labled with IDs, but it’s not quite important in this situation.

The measures in this data set - if they are quantitative variables, must be non-negative. So we also need to check whether we have any negative record or not.

##  Quantity UnitPrice 
## 1.9604768 0.4644691

Because we need an exact time to calculate R so that InvoiceDate will need to be modifed.

Exploratory Data Analysis

This analysis is taken to look for some insights. For example, sales (accurate to the minute) have two spike times over 70,000 records. These are unusual:

Sales by day tended to increase in the last stage. On the other hand, there are two days of abnormal sales (which is predictable):

From Figure 2 we also see that there is another anomaly: there are two times where the data is not continuous. Specifically, from 2010-12-22 to 2011-01-03, there are 12 consecutive days of data missing.

## # A tibble: 1 x 4
##   time_ymd   sales lag1       duration_date
##   <date>     <dbl> <date>             <dbl>
## 1 2011-01-04  8639 2010-12-23            12

Doanh thu cả theo số lượng bán và tiền ở 4 tháng cuối năm cao hơn hẳn các tháng còn lại trong năm:

More than 4000 items are being sold, but the revenue distribution is not even:

Once again we can see the presence of principle 80 - 20: 80% of the sales of this online retailer comes from 827 product codes (corresponding to 20.59% of product codes):

## [1] 0.2059776

Products which bring the most revenue:

Description sales money_percent cum_money
DOTCOM POSTAGE 206248.77 0.0193358 0.0193358
REGENCY CAKESTAND 3 TIER 174484.74 0.0163579 0.0356937
PAPER CRAFT , LITTLE BIRDIE 168469.60 0.0157940 0.0514877
WHITE HANGING HEART T-LIGHT HOLDER 106292.77 0.0099649 0.0614526
PARTY BUNTING 99504.33 0.0093285 0.0707812
JUMBO BAG RED RETROSPOT 94340.05 0.0088444 0.0796255

The company may use this information for its business purposes. For example, the company may prioritize shipping for orders listed above or prioritize preparing inventory for these codes. In other words, it is necessary to focus on logistics (logistics - warehousing - transportation) for the brands that bring up to 80% of the revenue for the company.

Customer Segments

Apply the K-means Clustering for Customer Segmentation with RFM method:

CustomerID money recency freq
12346 77183.60 348 1
12347 4310.00 24 182
12348 1797.24 97 31
12349 1757.55 41 73
12350 334.40 332 17
12352 2506.04 58 85

Optimal K will be chosen following this method: Elbow Method:

## # A tibble: 3 x 4
##   Group   money recency  freq
##   <chr>   <dbl>   <dbl> <dbl>
## 1 Group 2  3308      54   165
## 2 Group 1   799     176    41
## 3 Group 3   607     316    24

Based on the results of the K-Means Clustering, customers will be classified into the following 3 groups:

  • Group 2 Group has those customers who spend a lot, make the purchase very often, and have the smallest Recency. This group is called * Champions *. The way of “taking care” of these customers is well described [here] (https://www.putler.com/rfm-analysis/). On average, each customer in this group spends £ 3308.

  • Group 1 This is Loyal Customers. This is a group of customers with the potential to turn into Champions if the company knows how to implement customer care and promotion strategies appropriately.

  • Group 3 Less purchase, and bring less money to the company.

Before we can use the above Insights to cater to the business strategies, we should consider these following points: the K-means clustering algorithm is very sensitive to outliers. Although the data has been scaled to minimize the impact of these outliers, it does not guarantee that the results will not be deformed.

Figure 8 shows some customers with massive spending. They may not be individual customers but maybe the form of a small store to buy and resell. Similarly, the buying frequency (Figure 9, the unit on the Y-axis is 1000):

Because the K-Means Clustering is very sensitive to outliers, so we should split these outliers for separate analysis while focusing on those common customers.

Re-apply K means clustering - Find the optimal K:

After removing outliers, optimal K is 4.

Group money recency freq
Group 4 2668 41 136
Group 2 999 103 51
Group 3 726 214 35
Group 1 566 331 24

Calculate the proportion of revenue from these customer groups:

Group 4 accounts for 49.1% of total customers and is the group that brings nearly 75% of revenue to the company:

Group Labelling

To answer the question a consumer whose behavior is described by R, F and M, which group will this customer be?. Many approaches/models can be applied to this problem and one of them is to use classification algorithms, for example, Random Forest:

## note: only 2 unique complexity parameters in default grid. Truncating the grid to 2 .

As the business getting imput for a new customer with M = 1757.55, R = 41, F = 73, which segmentation that customer will be:

money recency freq
1757.55 41 73

That customer will be in group 4:

## [1] "Group 4"

Model evaluation:

## Confusion Matrix and Statistics
## 
##           Reference
## Prediction Group 1 Group 2 Group 3 Group 4
##    Group 1      99       0       0       0
##    Group 2       0     217       0       1
##    Group 3       0       0     123       0
##    Group 4       0       0       0     424
## 
## Overall Statistics
##                                      
##                Accuracy : 0.9988     
##                  95% CI : (0.9936, 1)
##     No Information Rate : 0.4919     
##     P-Value [Acc > NIR] : < 2.2e-16  
##                                      
##                   Kappa : 0.9983     
##                                      
##  Mcnemar's Test P-Value : NA         
## 
## Statistics by Class:
## 
##                      Class: Group 1 Class: Group 2 Class: Group 3
## Sensitivity                  1.0000         1.0000         1.0000
## Specificity                  1.0000         0.9985         1.0000
## Pos Pred Value               1.0000         0.9954         1.0000
## Neg Pred Value               1.0000         1.0000         1.0000
## Prevalence                   0.1146         0.2512         0.1424
## Detection Rate               0.1146         0.2512         0.1424
## Detection Prevalence         0.1146         0.2523         0.1424
## Balanced Accuracy            1.0000         0.9992         1.0000
##                      Class: Group 4
## Sensitivity                  0.9976
## Specificity                  1.0000
## Pos Pred Value               1.0000
## Neg Pred Value               0.9977
## Prevalence                   0.4919
## Detection Rate               0.4907
## Detection Prevalence         0.4907
## Balanced Accuracy            0.9988
GroupPredicted money recency freq
Group 1 453 330 22
Group 2 1018 103 53
Group 3 505 217 29
Group 4 2753 42 147
Group money recency freq
Group 1 594 331 24
Group 2 995 103 51
Group 3 782 213 36
Group 4 2647 41 133

References

  1. Chapman, C., & Feit, E. M. (2019). R for marketing research and analytics. New York, NY: Springer.
  2. Chen, D., Sain, S. L., & Guo, K. (2012). Data mining for the online retail industry: A case study of RFM model-based customer segmentation using data mining. Journal of Database Marketing & Customer Strategy Management, 19(3), 197-208.
  3. Khajvand, M., & Tarokh, M. J. (2011). Estimating customer future value of different customer segments based on adapted RFM model in retail banking context. Procedia Computer Science, 3, 1327-1332.
  4. Shmueli, G., Bruce, P. C., Yahav, I., Patel, N. R., & Lichtendahl Jr, K. C. (2017). Data mining for business analytics: concepts, techniques, and applications in R. John Wiley & Sons.
  5. Zakrzewska, D., & Murlewski, J. (2005, September). Clustering algorithms for bank customer segmentation. In 5th International Conference on Intelligent Systems Design and Applications (ISDA’05) (pp. 197-202). IEEE.
LS0tCnRpdGxlOiAiTWFya2V0IFNlZ21lbnRhdGlvbjogSy1tZWFucyBDbHVzdGVyaW5nIEFsZ29yaXRobSIgCnN1YnRpdGxlOiAiRGF0YSBTY2llbmNlIGZvciBNYXJrZXRpbmcgU2VyaWVzIgphdXRob3I6ICJKZW5ueSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IAogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgI2NvZGVfZm9sZGluZzogaGlkZQogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICAjIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogImZsYXRseSIKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OiBUUlVFCi0tLQoKYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2KQpgYGAKCgojIE1vdGl2YXRpb25zCgpDdXN0b21lciBTZWdtZW50cyAob3IgTWFya2V0IFNlZ21lbnRhdGlvbikgYWxsb3cgdGhlIGNvbXBhbmllcyB0byBiZSBhYmxlIHRvIHV0aWxpemUgdGhlaXIgcmVzb3VyY2VzICh0aW1lLCBmaW5hbmNlKSB0byBzZXJ2ZSB0aGVpcidzIGdvYWxzOiBpbmNyZWFzaW5nIHNhbGVzLCBpbmNyZWFzaW5nIHByb2ZpdHMsIHJldGFpbmluZyBpbXBvcnRhbnQgY3VzdG9tZXJzIGFzIHdlbGwgYXMgaW1wbGVtZW50aW5nIG1hcmtldGluZyBjYW1wYWlnbnMgbW9yZSBlZmZlY3RpdmVseSwgd2hpY2ggIGJhc2VkIG9uIHRoZSB1bmRlcnN0YW5kaW5nIG9mIHRoZWlyIGN1c3RvbWVyJ3MgYmVoYXZpb3IsIGhhYml0cywgYW5kIHByZWZlcmVuY2VzLgoKCiMgTWV0aG9kIGFuZCBEYXRhIAoKUkZNIGlzIGEgbWV0aG9kIHVzZWQgZm9yIGFuYWx5emluZyBjdXN0b21lciB2YWx1ZS4gSXQgaXMgY29tbW9ubHkgdXNlZCBpbiBkYXRhYmFzZSBtYXJrZXRpbmcgYW5kIGRpcmVjdCBtYXJrZXRpbmcgYW5kIGhhcyByZWNlaXZlZCBwYXJ0aWN1bGFyIGF0dGVudGlvbiBpbiByZXRhaWwgYW5kIHByb2Zlc3Npb25hbCBzZXJ2aWNlcyBpbmR1c3RyaWVzLgoKUkZNIHN0YW5kcyBmb3IgdGhlIHRocmVlIGRpbWVuc2lvbnM6IFxuCi0gKipSIChSZWNlbmN5KSoqIFRoZSB2YWx1ZSBvZiBob3cgcmVjZW50bHkgYSBjdXN0b21lciBwdXJjaGFzZWQgYXQgdGhlIGVzdGFibGlzaG1lbnQuCi0gKipGIChGcmVxdWVuY3kpKiogSG93IGZyZXF1ZW50IHRoZSBjdXN0b21lcuKAmXMgdHJhbnNhY3Rpb25zIGFyZSBhdCB0aGUgZXN0YWJsaXNobWVudC4KLSAqKk0gKE1vbmV0YXJ5KSoqIFRoZSBkb2xsYXIgKG9yIHBvdW5kcyBpbiBvdXIgY2FzZSkgdmFsdWUgb2YgYWxsIHRoZSB0cmFuc2FjdGlvbnMgdGhhdCB0aGUgY3VzdG9tZXIgbWFkZSBhdCB0aGUgZXN0YWJsaXNobWVudC4KCkYgYW5kIE0gYXJlIGlucHV0cyBmb3IgYSBwZXJpb2QgKDEgeWVhciwgYSBtb250aCwgYSBwZXJpb2QpLiBQYXJ0aWN1bGFybHkgUiBkZXBlbmRzIG9uIHRoZSBtb2RlbGVyIHByZWZlcmVuY2UgYW5kIGl0IGRvZXMgbm90IGFmZmVjdCB0aGUgbW9kZWwncyByZXN1bHRzLgoKRGF0YSBjYW4gYmUgZG93bmxvYWQgW2hlcmVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vY2FycmllMS9lY29tbWVyY2UtZGF0YSkuIEZpcnN0LCBsZXQncyBoYXZlIGEgbG9vayBhdCB0aGUgZGF0YToKCmBgYHtyfQojIExvYWQgZGF0YTogCnJtKGxpc3QgPSBscygpKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShrbml0cikKbXlfZGYgPC0gcmVhZF9jc3YoIi9Vc2Vycy9qZW5ueW5ndXllbi9Eb3dubG9hZHMvZGF0YS5jc3YiKQoKIyBTZWUgdGhlIGZpcnN0IDEwIHRyYW5zYWN0aW9uIHdpdGggdGhlIGZpcnN0IDQgY29sdW1ucwpzZXQuc2VlZCgyOSkKbXlfZGYgJT4lIAogIHNhbXBsZV9uKDEwKSAlPiUgCiAgc2VsZWN0KDE6NCkgJT4lIAogIGthYmxlKCkKCiMgU2VlIHRoZSBmaXJzdCAxMCB0cmFuc2FjdGlvbiB3aXRoIHRoZSBsYXN0IDQgY29sdW1ucwpteV9kZiAlPiUgCiAgc2FtcGxlX24oMTApICU+JSAKICBzZWxlY3QoNTo4KSAlPiUgCiAga2FibGUoKQpgYGAKClRoZSB2YXJpYWJsZXMgYXJlIHJlYWxseSBkZXNjcmlwdGl2ZS4gIFdlIHNob3VsZCBhbHNvIG1ha2UgYSBwcmVsaW1pbmFyeSBhc3Nlc3NtZW50IG9mIHRoZSBxdWFsaXR5IG9mIHRoZSBkYXRhLiBGaXJzdCBvZiBhbGwsIGxldCBoYXZlIGEgbG9vayBhdCB0aGUgbWlzc2luZyByYXRlOgoKYGBge3J9CgpuYV9yYXRlIDwtIGZ1bmN0aW9uKHgpIHsxMDAqc3VtKGlzLm5hKHgpKSAvIGxlbmd0aCh4KX0KCm15X2RmICU+JQogIHN1bW1hcmlzZV9hbGwobmFfcmF0ZSkKYGBgCgpBcHByb3hpbWF0ZWx5IDI3JSBvZiBzdG9ja2NvZGUgaXMgd2l0aG91dCBkZXNjcmlwdGlvbi4gU2ltaWxhcmx5LCB+IDI1JSBjdXN0b210ZXJzIGFyZSBub3QgbGFibGVkIHdpdGggSURzLCBidXQgaXQncyBub3QgcXVpdGUgaW1wb3J0YW50IGluIHRoaXMgc2l0dWF0aW9uLgoKVGhlIG1lYXN1cmVzIGluIHRoaXMgZGF0YSBzZXQgLSBpZiB0aGV5IGFyZSBxdWFudGl0YXRpdmUgdmFyaWFibGVzLCBtdXN0IGJlIG5vbi1uZWdhdGl2ZS4gU28gd2UgYWxzbyBuZWVkIHRvIGNoZWNrIHdoZXRoZXIgd2UgaGF2ZSBhbnkgbmVnYXRpdmUgcmVjb3JkIG9yIG5vdC4gCgoKYGBge3J9CgpuZWdhdGl2ZV9kZWN0ZWN0IDwtIGZ1bmN0aW9uKHgpIHsxMDAqc3VtKHggPD0gMCkgLyBsZW5ndGgoeCl9CgoKc2FwcGx5KG15X2RmICU+JSBzZWxlY3QoUXVhbnRpdHksIFVuaXRQcmljZSksIG5lZ2F0aXZlX2RlY3RlY3QpCgojIFJlbW92ZSBuZWdhdGl2ZSByZWNvcmRzCm15X2RmICU+JSAKICBmaWx0ZXIoUXVhbnRpdHkgPiAwLCBVbml0UHJpY2UgPiAwKSAtPiBteV9kZgpgYGAKCgpCZWNhdXNlIHdlIG5lZWQgYW4gZXhhY3QgdGltZSB0byBjYWxjdWxhdGUgUiBzbyB0aGF0IEludm9pY2VEYXRlIHdpbGwgbmVlZCB0byBiZSBtb2RpZmVkLgoKYGBge3J9CmxpYnJhcnkobHVicmlkYXRlKQoKbXlfZGYgJT4lIAogIG11dGF0ZSh0aW1lX3ltZF9obSA9IG1keV9obShJbnZvaWNlRGF0ZSksIAogICAgICAgICB0aW1lX2hvdXIgPSBob3VyKHRpbWVfeW1kX2htKSwgCiAgICAgICAgIHRpbWVfbWluID0gbWludXRlKHRpbWVfeW1kX2htKSwgCiAgICAgICAgIHdfZGF5ID0gd2RheSh0aW1lX3ltZF9obSwgbGFiZWwgPSBUUlVFLCBhYmJyID0gVFJVRSksIAogICAgICAgICB0aW1lX21vbiA9IG1vbnRoKHRpbWVfeW1kX2htLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBUUlVFKSwgCiAgICAgICAgIHRpbWVfeW1kID0gSW52b2ljZURhdGUgJT4lIHN0cl9zcGxpdCgiICIsIHNpbXBsaWZ5ID0gVFJVRSkgJT4lIGRhdGEuZnJhbWUoKSAlPiUgcHVsbChYMSkgJT4lIG1keSkgLT4gbXlfZGYKYGBgCgojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKClRoaXMgYW5hbHlzaXMgaXMgdGFrZW4gdG8gbG9vayBmb3Igc29tZSBpbnNpZ2h0cy4gRm9yIGV4YW1wbGUsIHNhbGVzIChhY2N1cmF0ZSB0byB0aGUgbWludXRlKSBoYXZlIHR3byBzcGlrZSB0aW1lcyBvdmVyIDcwLDAwMCByZWNvcmRzLiBUaGVzZSBhcmUgdW51c3VhbDoKCgpgYGB7cn0KbGlicmFyeShocmJydGhlbWVzKQp0aGVtZV9zZXQodGhlbWVfbW9kZXJuX3JjKCkpCgpteV9kZiAlPiUgCiAgZ3JvdXBfYnkodGltZV95bWRfaG0pICU+JSAKICBzdW1tYXJpc2Uoc2FsZXMgPSBzdW0oUXVhbnRpdHkpKSAlPiUgCiAgdW5ncm91cCgpIC0+IHNhbGVzX2J5VGltZV9obQoKc2FsZXNfYnlUaW1lX2htICU+JSAKICBnZ3Bsb3QoYWVzKHRpbWVfeW1kX2htLCBzYWxlcykpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBsYWJzKHRpdGxlID0gIkZpZ3VyZSAxOiBVbml0IFNhbGVzIGJ5IE1pbiIsIHggPSAiVGltZSIsIHkgPSAiUXVhbnRpdHkiKQogIApgYGAKClNhbGVzIGJ5IGRheSB0ZW5kZWQgdG8gaW5jcmVhc2UgaW4gdGhlIGxhc3Qgc3RhZ2UuIE9uIHRoZSBvdGhlciBoYW5kLCB0aGVyZSBhcmUgdHdvIGRheXMgb2YgYWJub3JtYWwgc2FsZXMgKHdoaWNoIGlzIHByZWRpY3RhYmxlKToKCmBgYHtyfQpteV9kZiAlPiUgCiAgZ3JvdXBfYnkodGltZV95bWQpICU+JSAKICBzdW1tYXJpc2Uoc2FsZXMgPSBzdW0oUXVhbnRpdHkpKSAlPiUgCiAgdW5ncm91cCgpIC0+IHNhbGVzX2J5VGltZQoKc2FsZXNfYnlUaW1lICU+JSAKICBnZ3Bsb3QoYWVzKHRpbWVfeW1kLCBzYWxlcykpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX3BvaW50KGNvbG9yID0gImZpcmVicmljayIpICsgCiAgbGFicyh0aXRsZSA9ICJGaWd1cmUgMjogVW5pdCBTYWxlcyBieSBEYXkiLCB4ID0gIlRpbWUiLCB5ID0gIlF1YW50aXR5IikKCmBgYAoKRnJvbSBGaWd1cmUgMiB3ZSBhbHNvIHNlZSB0aGF0IHRoZXJlIGlzIGFub3RoZXIgYW5vbWFseTogdGhlcmUgYXJlIHR3byB0aW1lcyB3aGVyZSB0aGUgZGF0YSBpcyBub3QgY29udGludW91cy4gU3BlY2lmaWNhbGx5LCBmcm9tIDIwMTAtMTItMjIgdG8gMjAxMS0wMS0wMywgdGhlcmUgYXJlIDEyIGNvbnNlY3V0aXZlIGRheXMgb2YgZGF0YSBtaXNzaW5nLgoKCmBgYHtyfQpzYWxlc19ieVRpbWUgJT4lIAogIG11dGF0ZShsYWcxID0gbGFnKHRpbWVfeW1kLCBuID0gMUwpKSAlPiUgCiAgbXV0YXRlKGR1cmF0aW9uX2RhdGUgPSB0aW1lX3ltZCAtIGxhZzEpICU+JSAKICBtdXRhdGUoZHVyYXRpb25fZGF0ZSA9IGFzLm51bWVyaWMoZHVyYXRpb25fZGF0ZSkpICU+JSAKICBzbGljZSh3aGljaC5tYXgoZHVyYXRpb25fZGF0ZSkpIAoKYGBgCgoKCmBgYHtyfQpteV9kZiAlPiUgCiAgbXV0YXRlKG1vbmV5ID0gUXVhbnRpdHkqVW5pdFByaWNlKSAtPiBteV9kZgoKbXlfZGYgJT4lIAogIGdyb3VwX2J5KHRpbWVfeW1kX2htKSAlPiUgCiAgc3VtbWFyaXNlKG1vbmV5U2FsZXMgPSBzdW0obW9uZXkpKSAlPiUgCiAgdW5ncm91cCgpIC0+IHNhbGVzX2J5VGltZV9obV9tb25leQoKc2FsZXNfYnlUaW1lX2htX21vbmV5ICU+JSAKICBnZ3Bsb3QoYWVzKHRpbWVfeW1kX2htLCBtb25leVNhbGVzKSkgKyAKICBnZW9tX2xpbmUoKSArIAogIGxhYnModGl0bGUgPSAiRmlndXJlIDM6IE1vbmV0YXJ5IFNhbGVzIGJ5IE1pbiIsIHggPSAiVGltZSIsIHkgPSAiIikKCm15X2RmICU+JSAKICBncm91cF9ieSh0aW1lX3ltZCkgJT4lIAogIHN1bW1hcmlzZShtb25leVNhbGVzID0gc3VtKG1vbmV5KSkgJT4lIAogIHVuZ3JvdXAoKSAtPiBzYWxlc19ieVRpbWVfbW9uZXkKCnNhbGVzX2J5VGltZV9tb25leSAlPiUgCiAgZ2dwbG90KGFlcyh0aW1lX3ltZCwgbW9uZXlTYWxlcykpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBnZW9tX3BvaW50KGNvbG9yID0gImZpcmVicmljayIpICsgCiAgbGFicyh0aXRsZSA9ICJGaWd1cmUgNDogTW9uZXRhcnkgU2FsZXMgYnkgRGF5IiwgeCA9ICJUaW1lIiwgeSA9ICIiKQogIApgYGAKCkRvYW5oIHRodSBj4bqjIHRoZW8gc+G7kSBsxrDhu6NuZyBiw6FuIHbDoCB0aeG7gW4g4bufIDQgdGjDoW5nIGN14buRaSBuxINtIGNhbyBoxqFuIGjhurNuIGPDoWMgdGjDoW5nIGPDsm4gbOG6oWkgdHJvbmcgbsSDbTogCgoKYGBge3J9Cm15X2RmICU+JSAKICBncm91cF9ieSh0aW1lX21vbikgJT4lIAogIHN1bW1hcmlzZV9lYWNoKGZ1bnMoc3VtKSwgUXVhbnRpdHkpICU+JSAKICBtdXRhdGUoUXVhbnRpdHkgPSBRdWFudGl0eSAvIDEwMDApICU+JSAKICBnZ3Bsb3QoYWVzKHRpbWVfbW9uLCBRdWFudGl0eSkpICsgCiAgZ2VvbV9jb2woKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBsYWJzKHRpdGxlID0gIkZpZ3VyZSA1OiBVbml0IFNhbGVzIGluIHRob3VzYW5kcyBieSBNb250aCIsIHggPSAiVGltZSIsIHkgPSAiUXVhbmxpdHkiKSArIAogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDgwMCkpCgpteV9kZiAlPiUgCiAgZ3JvdXBfYnkodGltZV9tb24pICU+JSAKICBzdW1tYXJpc2VfZWFjaChmdW5zKHN1bSksIG1vbmV5KSAlPiUgCiAgbXV0YXRlKG1vbmV5ID0gbW9uZXkgLyAxMDAwKSAlPiUgCiAgZ2dwbG90KGFlcyh0aW1lX21vbiwgbW9uZXkpKSArIAogIGdlb21fY29sKCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgbGFicyh0aXRsZSA9ICJGaWd1cmUgNjogTW9uZXRhcnkgU2FsZXMgaW4gdGhvdXNhbmRzIGJ5IE1vbnRoIiwgeCA9ICJUaW1lIiwgeSA9ICIiKQogIApgYGAKCk1vcmUgdGhhbiA0MDAwIGl0ZW1zIGFyZSBiZWluZyBzb2xkLCBidXQgdGhlIHJldmVudWUgZGlzdHJpYnV0aW9uIGlzIG5vdCBldmVuOgoKYGBge3J9Cm15X2RmICU+JSAKICBncm91cF9ieShEZXNjcmlwdGlvbikgJT4lIAogIHN1bW1hcmlzZShzYWxlcyA9IHN1bShtb25leSkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGFycmFuZ2UoLXNhbGVzKSAlPiUgCiAgbXV0YXRlKERlc2NyaXB0aW9uID0gZmFjdG9yKERlc2NyaXB0aW9uLCBsZXZlbHMgPSBEZXNjcmlwdGlvbikpICU+JSAKICBtdXRhdGUodG90YWwgPSBzdW0oc2FsZXMpKSAlPiUgCiAgbXV0YXRlKG1vbmV5X3BlcmNlbnQgPSBzYWxlcyAvIHRvdGFsKSAlPiUgCiAgbXV0YXRlKGN1bV9tb25leSA9IGN1bXN1bShtb25leV9wZXJjZW50KSkgLT4gbW9uZXlTYWxlc19JdGVtCgptb25leVNhbGVzX0l0ZW0gJT4lIAogIGdncGxvdChhZXMoRGVzY3JpcHRpb24sIHNhbGVzKSkgKyAKICBnZW9tX2NvbCgpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArIAogIGxhYnModGl0bGUgPSAiRmlndXJlIDY6IE1vbmV5IFNhbGVzIGJ5IFByb2R1Y3QiLCB4ID0gIiIsIHkgPSAiIikKICAKYGBgCgpPbmNlIGFnYWluIHdlIGNhbiBzZWUgdGhlIHByZXNlbmNlIG9mICoqcHJpbmNpcGxlIDgwIC0gMjAqKjogODAlIG9mIHRoZSBzYWxlcyBvZiB0aGlzIG9ubGluZSByZXRhaWxlciBjb21lcyBmcm9tIDgyNyBwcm9kdWN0IGNvZGVzIChjb3JyZXNwb25kaW5nIHRvIDIwLjU5JSBvZiBwcm9kdWN0IGNvZGVzKToKCmBgYHtyfQptb25leVNhbGVzX0l0ZW0gJT4lIAogIGZpbHRlcihjdW1fbW9uZXkgPD0gMC44KSAtPiB0b3A4MF9zYWxlcwoKdG9wODBfc2FsZXMgJT4lIG5yb3coKSAvIG5yb3cobW9uZXlTYWxlc19JdGVtKQpgYGAKClByb2R1Y3RzIHdoaWNoIGJyaW5nIHRoZSBtb3N0IHJldmVudWU6CgpgYGB7cn0KbW9uZXlTYWxlc19JdGVtICU+JSAKICBzZWxlY3QoLXRvdGFsKSAlPiUgCiAgaGVhZCgpICU+JSAKICBrYWJsZSgpCmBgYAoKVGhlIGNvbXBhbnkgbWF5IHVzZSB0aGlzIGluZm9ybWF0aW9uIGZvciBpdHMgYnVzaW5lc3MgcHVycG9zZXMuIEZvciBleGFtcGxlLCB0aGUgY29tcGFueSBtYXkgcHJpb3JpdGl6ZSBzaGlwcGluZyBmb3Igb3JkZXJzIGxpc3RlZCBhYm92ZSBvciBwcmlvcml0aXplIHByZXBhcmluZyBpbnZlbnRvcnkgZm9yIHRoZXNlIGNvZGVzLiBJbiBvdGhlciB3b3JkcywgaXQgaXMgbmVjZXNzYXJ5IHRvIGZvY3VzIG9uIGxvZ2lzdGljcyAobG9naXN0aWNzIC0gd2FyZWhvdXNpbmcgLSB0cmFuc3BvcnRhdGlvbikgZm9yIHRoZSBicmFuZHMgdGhhdCBicmluZyB1cCB0byA4MCUgb2YgdGhlIHJldmVudWUgZm9yIHRoZSBjb21wYW55LgoKIyBDdXN0b21lciBTZWdtZW50cwoKQXBwbHkgdGhlIEstbWVhbnMgQ2x1c3RlcmluZyBmb3IgQ3VzdG9tZXIgU2VnbWVudGF0aW9uIHdpdGggUkZNIG1ldGhvZDogCgpgYGB7cn0KCnkgPC0gYXMuZHVyYXRpb24oeW1kX2htKCIyMDExLTEyLTMxIDI0OjU5IikgLSBteV9kZiR0aW1lX3ltZF9obSkgJT4lIGFzLm51bWVyaWMoKQp5IDwtIHJvdW5kKHkgLyAoMzYwMCoyNCksIDApCgojIENyZWF0ZSBSZWNlbmN5OiAKbXlfZGYgJT4lIG11dGF0ZShyZWNlbmN5ID0geSkgLT4gbXlfZGYKCiMgUHVyY2hhc2UgYW1tb3VudCBmb3IgaW5kaXZpZHVhbCBjdXN0b21lcnM6IApteV9kZiAlPiUgCiAgZ3JvdXBfYnkoQ3VzdG9tZXJJRCkgJT4lIAogIHN1bW1hcmlzZV9lYWNoKGZ1bnMoc3VtKSwgbW9uZXkpICU+JSAKICB1bmdyb3VwKCkgLT4gZGZfbW9uZXkKCiMgUjogCm15X2RmICU+JSAKICBncm91cF9ieShDdXN0b21lcklEKSAlPiUgCiAgc3VtbWFyaXNlX2VhY2goZnVucyhtaW4pLCByZWNlbmN5KSAlPiUgCiAgdW5ncm91cCgpIC0+IGRmX3JlY2VuY3kKCiMgRjogCm15X2RmICU+JSAKICBncm91cF9ieShDdXN0b21lcklEKSAlPiUgCiAgY291bnQoKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICByZW5hbWUoZnJlcSA9IG4pIC0+IGRmX2ZyZXEKCiMgRGF0YSBmb3IgRURBOiAgCgpkZl9tb25leSAlPiUgCiAgZnVsbF9qb2luKGRmX3JlY2VuY3ksIGJ5ID0gIkN1c3RvbWVySUQiKSAlPiUgCiAgZnVsbF9qb2luKGRmX2ZyZXEsIGJ5ID0gIkN1c3RvbWVySUQiKSAlPiUgCiAgbXV0YXRlKEN1c3RvbWVySUQgPSBhcy5jaGFyYWN0ZXIoQ3VzdG9tZXJJRCkpIC0+IGZpbmFsX2RmCgogCmZpbmFsX2RmICU+JSAKICBoZWFkKCkgJT4lIAogIGthYmxlKCkKCmBgYAoKCgpgYGB7cn0KIyBTY2FsaW5nIGRhdGFzZXQ6IApmaW5hbF9kZiAlPiUgCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpIHsoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKX0pICU+JSAKICBzZWxlY3QoLUN1c3RvbWVySUQpIC0+IGZpbmFsX2RmX3NjYWxlZApgYGAKCgpPcHRpbWFsIEsgd2lsbCBiZSBjaG9zZW4gZm9sbG93aW5nIHRoaXMgbWV0aG9kOiBbRWxib3cgTWV0aG9kXShodHRwczovL2JsLm9ja3Mub3JnL3JwZ292ZS8wMDYwZmYzYjY1NjYxOGU5MTM2Yik6IAoKYGBge3J9CnNldC5zZWVkKDI5KQp3c3MgPC0gc2FwcGx5KDE6MTAsIAogICAgICAgICAgICAgIGZ1bmN0aW9uKGspe2ttZWFucyhmaW5hbF9kZl9zY2FsZWQgJT4lIHNhbXBsZV9mcmFjKDAuMiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrLCBuc3RhcnQgPSAzMCkkdG90LndpdGhpbnNzfSkKCgp1IDwtIGRhdGEuZnJhbWUoayA9IDE6MTAsIFdTUyA9IHdzcykKCnUgJT4lIAogIGdncGxvdChhZXMoaywgV1NTKSkgKyAKICBnZW9tX2xpbmUoKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IHUgJT4lIGZpbHRlcihrID09IDMpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMykgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDEsIDEwLCBieSA9IDEpKSArIAogIGxhYnModGl0bGUgPSAiRmlndXJlIDc6IFRoZSBPcHRpbWFsIE51bWJlciBvZiBDbHVzdGVycywgRWxib3cgTWV0aG9kIiwgeCA9ICJOdW1iZXIgb2YgQ2x1c3RlcnMgKEspIikgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCgoKYGBge3J9CiMgQ2x1c3RlciB3aXRoIEsgPSAzIApzZXQuc2VlZCgxMjMpCmttLnJlcyA8LSBrbWVhbnMoZmluYWxfZGZfc2NhbGVkLCAzLCBuc3RhcnQgPSAzMCkKCmZpbmFsX2RmICU+JSAKICBtdXRhdGUoR3JvdXAgPSBrbS5yZXMkY2x1c3RlcikgJT4lIAogIG11dGF0ZShHcm91cCA9IHBhc3RlKCJHcm91cCIsIEdyb3VwKSkgLT4gZmluYWxfZGZfY2x1c3RlcmVkCgoKIyBHcm91cHMgb2YgY3VzdG9tZXI6IApmaW5hbF9kZl9jbHVzdGVyZWQgJT4lIAogIGdyb3VwX2J5KEdyb3VwKSAlPiUgCiAgc3VtbWFyaXNlX2VhY2goZnVucyhtZWFuKSwgbW9uZXksIHJlY2VuY3ksIGZyZXEpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7cm91bmQoeCwgMCl9KSAlPiUgCiAgYXJyYW5nZSgtbW9uZXkpIAoKYGBgCgpCYXNlZCBvbiB0aGUgcmVzdWx0cyBvZiB0aGUgSy1NZWFucyBDbHVzdGVyaW5nLCBjdXN0b21lcnMgd2lsbCBiZSBjbGFzc2lmaWVkIGludG8gdGhlIGZvbGxvd2luZyAzIGdyb3VwczogCgotICoqR3JvdXAgMioqIEdyb3VwIGhhcyB0aG9zZSBjdXN0b21lcnMgd2hvIHNwZW5kIGEgbG90LCBtYWtlIHRoZSBwdXJjaGFzZSB2ZXJ5IG9mdGVuLCBhbmQgaGF2ZSB0aGUgc21hbGxlc3QgUmVjZW5jeS4gVGhpcyBncm91cCBpcyBjYWxsZWQgKiBDaGFtcGlvbnMgKi4gVGhlIHdheSBvZiAidGFraW5nIGNhcmUiIG9mIHRoZXNlIGN1c3RvbWVycyBpcyB3ZWxsIGRlc2NyaWJlZCBbaGVyZV0gKGh0dHBzOi8vd3d3LnB1dGxlci5jb20vcmZtLWFuYWx5c2lzLykuIE9uIGF2ZXJhZ2UsIGVhY2ggY3VzdG9tZXIgaW4gdGhpcyBncm91cCBzcGVuZHMgwqMgMzMwOC4KCi0gKipHcm91cCAxKiogVGhpcyBpcyAqTG95YWwgQ3VzdG9tZXJzKi4gVGhpcyBpcyBhIGdyb3VwIG9mIGN1c3RvbWVycyB3aXRoIHRoZSBwb3RlbnRpYWwgdG8gdHVybiBpbnRvIENoYW1waW9ucyBpZiB0aGUgY29tcGFueSBrbm93cyBob3cgdG8gaW1wbGVtZW50IGN1c3RvbWVyIGNhcmUgYW5kIHByb21vdGlvbiBzdHJhdGVnaWVzIGFwcHJvcHJpYXRlbHkuCgotICoqR3JvdXAgMyoqIExlc3MgcHVyY2hhc2UsIGFuZCBicmluZyBsZXNzIG1vbmV5IHRvIHRoZSBjb21wYW55LgoKQmVmb3JlIHdlIGNhbiB1c2UgdGhlIGFib3ZlIEluc2lnaHRzIHRvIGNhdGVyIHRvIHRoZSBidXNpbmVzcyBzdHJhdGVnaWVzLCB3ZSBzaG91bGQgY29uc2lkZXIgdGhlc2UgZm9sbG93aW5nIHBvaW50czogdGhlIEstbWVhbnMgY2x1c3RlcmluZyBhbGdvcml0aG0gaXMgdmVyeSBzZW5zaXRpdmUgdG8gb3V0bGllcnMuIEFsdGhvdWdoIHRoZSBkYXRhIGhhcyBiZWVuIHNjYWxlZCB0byBtaW5pbWl6ZSB0aGUgaW1wYWN0IG9mIHRoZXNlIG91dGxpZXJzLCBpdCBkb2VzIG5vdCBndWFyYW50ZWUgdGhhdCB0aGUgcmVzdWx0cyB3aWxsIG5vdCBiZSBkZWZvcm1lZC4KCmBgYHtyfQpmaW5hbF9kZiAlPiUgCiAgZ2dwbG90KGFlcyhDdXN0b21lcklELCBtb25leSkpICsgCiAgZ2VvbV9jb2woKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArIAogIGxhYnModGl0bGUgPSAiRmlndXJlIDg6IFNwZW5kaW5nIGJ5IEN1c3RvbWVyIiwgeSA9ICIiKQpgYGAKCkZpZ3VyZSA4IHNob3dzIHNvbWUgY3VzdG9tZXJzIHdpdGggbWFzc2l2ZSBzcGVuZGluZy4gVGhleSBtYXkgbm90IGJlIGluZGl2aWR1YWwgY3VzdG9tZXJzIGJ1dCBtYXliZSB0aGUgZm9ybSBvZiBhIHNtYWxsIHN0b3JlIHRvIGJ1eSBhbmQgcmVzZWxsLiBTaW1pbGFybHksIHRoZSBidXlpbmcgZnJlcXVlbmN5IChGaWd1cmUgOSwgdGhlIHVuaXQgb24gdGhlIFktYXhpcyBpcyAxMDAwKToKCgpgYGB7cn0KZmluYWxfZGYgJT4lIAogIG11dGF0ZShmcmVxID0gZnJlcSAvIDEwMDApICU+JSAKICBnZ3Bsb3QoYWVzKEN1c3RvbWVySUQsIGZyZXEpKSArIAogIGdlb21fY29sKCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBsYWJzKHRpdGxlID0gIkZpZ3VyZSA5OiBGcmVxdWVuY3kgYnkgQ3VzdG9tZXIiLCB5ID0gIiIpCmBgYAoKCkJlY2F1c2UgdGhlIEstTWVhbnMgQ2x1c3RlcmluZyBpcyB2ZXJ5IHNlbnNpdGl2ZSB0byBvdXRsaWVycywgc28gd2Ugc2hvdWxkIHNwbGl0IHRoZXNlIG91dGxpZXJzIGZvciBzZXBhcmF0ZSBhbmFseXNpcyB3aGlsZSBmb2N1c2luZyBvbiB0aG9zZSBjb21tb24gY3VzdG9tZXJzLgoKYGBge3J9CiMgSWRlbnRpZnkgT3V0bGllcjogCm91dGxpZXJfbGFiZWwgPC0gZnVuY3Rpb24oeCkgewogIGEgPC0gbWVhbih4KQogIGIgPC0gc2QoeCkKICB0aDEgPC0gYSAtIDMqYgogIHRoMiA8LSBhICsgMypiCiAgeSA8LSBjYXNlX3doZW4oeCA+PSB0aDEgJiB4IDw9IHRoMiB+ICJOb3JtYWwiLCBUUlVFIH4gIk91dGxpZXIiKQogIHJldHVybih5KQogIAp9CgojIE9ubHkgYXBwbHkgTm9ybWFsIE9ic2VydmF0aW9uIGZvciBLLW1lYW5zIENsdXN0ZXJpbmc6IAoKZmluYWxfZGYgJT4lIAogIG11dGF0ZShub3JfbW9uZXkgPSBvdXRsaWVyX2xhYmVsKG1vbmV5KSwgbm9yX2ZyZXEgPSBvdXRsaWVyX2xhYmVsKGZyZXEpKSAlPiUgCiAgZmlsdGVyKG5vcl9tb25leSA9PSAiTm9ybWFsIiwgbm9yX2ZyZXEgPT0gIk5vcm1hbCIpICU+JSAKICBzZWxlY3QoMTo0KSAtPiBmaW5hbF9kZl9ub3JtYWwKCmZpbmFsX2RmX25vcm1hbCAlPiUgCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpIHsoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKX0pIC0+IGZpbmFsX2RmX25vcm1hbF9zY2FsZWQKCmBgYAoKUmUtYXBwbHkgSyBtZWFucyBjbHVzdGVyaW5nIC0gRmluZCB0aGUgb3B0aW1hbCBLOiAKCgpgYGB7cn0Kc2V0LnNlZWQoMjkpCndzcyA8LSBzYXBwbHkoMToxMCwgCiAgICAgICAgICAgICAgZnVuY3Rpb24oayl7a21lYW5zKGZpbmFsX2RmX25vcm1hbF9zY2FsZWQgJT4lIHNlbGVjdCgtQ3VzdG9tZXJJRCkgJT4lIHNhbXBsZV9mcmFjKDAuMiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrLCBuc3RhcnQgPSAzMCkkdG90LndpdGhpbnNzfSkKCgp1IDwtIGRhdGEuZnJhbWUoayA9IDE6MTAsIFdTUyA9IHdzcykKCnUgJT4lIAogIGdncGxvdChhZXMoaywgV1NTKSkgKyAKICBnZW9tX2xpbmUoKSArIAogIGdlb21fcG9pbnQoKSArIAogIGdlb21fcG9pbnQoZGF0YSA9IHUgJT4lIGZpbHRlcihrID09IDQpLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMykgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDEsIDEwLCBieSA9IDEpKSArIAogIGxhYnModGl0bGUgPSAiRmlndXJlIDEwOiBUaGUgT3B0aW1hbCBOdW1iZXIgb2YgQ2x1c3RlcnMsIEVsYm93IE1ldGhvZCIsIAogICAgICAgc3VidGl0bGUgPSAiT3V0bGllcnMgYXJlIGFyZSByZW1vdmVkIGZyb20gc2FtcGxlLiIsIAogICAgICAgeCA9ICJOdW1iZXIgb2YgQ2x1c3RlcnMgKEspIikgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCgpBZnRlciByZW1vdmluZyBvdXRsaWVycywgb3B0aW1hbCBLIGlzIDQuCgpgYGB7cn0KIyBHcm91cGluZyB3aXRoIGsgPSA0OiAKc2V0LnNlZWQoMTIzKQprbS5yZXM0IDwtIGttZWFucyhmaW5hbF9kZl9ub3JtYWxfc2NhbGVkICU+JSBzZWxlY3QoLUN1c3RvbWVySUQpLCA0LCBuc3RhcnQgPSAzMCkKCgpmaW5hbF9kZl9ub3JtYWwgJT4lIAogIG11dGF0ZShHcm91cCA9IGttLnJlczQkY2x1c3RlcikgJT4lIAogIG11dGF0ZShHcm91cCA9IHBhc3RlKCJHcm91cCIsIEdyb3VwKSkgLT4gZmluYWxfZGZfY2x1c3RlcmVkCgpgYGAKCgoKYGBge3J9CiMgIEdyb3VwcyBvZiBjdXN0b21lcnMgZGVzY3JpcHRpb246IApmaW5hbF9kZl9jbHVzdGVyZWQgJT4lIAogIGdyb3VwX2J5KEdyb3VwKSAlPiUgCiAgc3VtbWFyaXNlX2VhY2goZnVucyhtZWFuKSwgbW9uZXksIHJlY2VuY3ksIGZyZXEpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7cm91bmQoeCwgMCl9KSAlPiUgCiAgYXJyYW5nZSgtbW9uZXkpICU+JSAKICBrYWJsZSgpCmBgYAoKQ2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9uIG9mIHJldmVudWUgZnJvbSB0aGVzZSBjdXN0b21lciBncm91cHM6CgpgYGB7cn0KZmluYWxfZGZfY2x1c3RlcmVkICU+JSAKICBncm91cF9ieShHcm91cCkgJT4lIAogIHN1bW1hcmlzZV9lYWNoKGZ1bnMoc3VtLCBtZWFuLCBtZWRpYW4sIG1pbiwgbWF4LCBzZCwgbigpKSwgbW9uZXkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShwZXJfc2FsZSA9IHJvdW5kKDEwMCpzdW0gLyBzdW0oc3VtKSwgMikpIC0+IHNhbGVfZ3JvdXAKCgpsaWJyYXJ5KGdndGhlbWVzKQoKc2FsZV9ncm91cCAlPiUgCiAgZ2dwbG90KGFlcyhyZW9yZGVyKEdyb3VwLCBwZXJfc2FsZSksIHBlcl9zYWxlLCBmaWxsID0gR3JvdXAsIGNvbG9yID0gR3JvdXApKSArIAogIGdlb21fY29sKHdpZHRoID0gMC41LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArIAogIGNvb3JkX2ZsaXAoKSArIAogIGdlb21fdGV4dChhZXMobGFiZWwgPSBwYXN0ZShwZXJfc2FsZSwgcGFzdGUwKHBhc3RlMCgiKCIsICIlIikpLCAiKSIpKSwgCiAgICAgICAgICAgIGhqdXN0ID0gLTAuMDUsIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDUpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgOTApLCBleHBhbmQgPSBjKDAuMDEsIDApKSArIAogIHNjYWxlX2ZpbGxfdGFibGVhdSgpICsgCiAgc2NhbGVfY29sb3JfdGFibGVhdSgpICsgCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgbGFicyh4ID0gTlVMTCwgdGl0bGUgPSAiRmlndXJlIDExOiBTaGFyZSBvZiBTYWxlcyBieSBDdXN0b21lciBHcm91cCIpCgpgYGAKCgpHcm91cCA0IGFjY291bnRzIGZvciA0OS4xJSBvZiB0b3RhbCBjdXN0b21lcnMgYW5kIGlzIHRoZSBncm91cCB0aGF0IGJyaW5ncyBuZWFybHkgNzUlIG9mIHJldmVudWUgdG8gdGhlIGNvbXBhbnk6CgpgYGB7cn0Kc2FsZV9ncm91cCAlPiUgCiAgc2VsZWN0KEdyb3VwLCBuKSAlPiUgCiAgbXV0YXRlKHRvdGFsID0gc3VtKG4pKSAlPiUgCiAgbXV0YXRlKGxhYmVsID0gMTAwKm4gLyB0b3RhbCkgJT4lIAogIG11dGF0ZShsYWJlbCA9IHBhc3RlKHJvdW5kKGxhYmVsLCAxKSwgIiUiKSkgJT4lIAogIGdncGxvdChhZXMoR3JvdXAsIG4sIGZpbGwgPSBHcm91cCwgY29sb3IgPSBHcm91cCkpICsgCiAgZ2VvbV9jb2wod2lkdGggPSAwLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVsKSwgY29sb3IgPSAid2hpdGUiLCB2anVzdCA9IDEuNCwgc2l6ZSA9IDUpICsgCiAgc2NhbGVfZmlsbF90YWJsZWF1KCkgKyAKICBzY2FsZV9jb2xvcl90YWJsZWF1KCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgdGl0bGUgPSAiRmlndXJlIDEyOiBOdW1iZXIgb2YgQ3VzdG9tZXJzIGJ5IEdyb3VwIikKICAKYGBgCgoKIyBHcm91cCBMYWJlbGxpbmcKClRvIGFuc3dlciB0aGUgcXVlc3Rpb24gKiphIGNvbnN1bWVyIHdob3NlIGJlaGF2aW9yIGlzIGRlc2NyaWJlZCBieSBSLCBGIGFuZCBNLCB3aGljaCBncm91cCB3aWxsIHRoaXMgY3VzdG9tZXIgYmU/KiouICBNYW55IGFwcHJvYWNoZXMvbW9kZWxzIGNhbiBiZSBhcHBsaWVkIHRvIHRoaXMgcHJvYmxlbSBhbmQgb25lIG9mIHRoZW0gaXMgdG8gdXNlIGNsYXNzaWZpY2F0aW9uIGFsZ29yaXRobXMsIGZvciBleGFtcGxlLCBSYW5kb20gRm9yZXN0OgoKYGBge3J9CiMgRGF0YSBmb3IgTUw6ICAKZGZfZm9yTUwgPC0gZmluYWxfZGZfY2x1c3RlcmVkICU+JSAKICBzZWxlY3QoLSBDdXN0b21lcklEKSAlPiUgCiAgbXV0YXRlKEdyb3VwID0gYXMuZmFjdG9yKEdyb3VwKSkKCiMgU3BsaXQgZGF0YSBpbnRvIHRyYWluaW5nLCB0ZXN0aW5nOgoKbGlicmFyeShjYXJldCkKc2V0LnNlZWQoMSkKaWQgPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihkZl9mb3JNTCRHcm91cCwgcCA9IDAuOCwgbGlzdCA9IEZBTFNFKQpkZl90cmFpbiA8LSBkZl9mb3JNTFtpZCwgXQpkZl90ZXN0IDwtIGRmX2Zvck1MWy1pZCwgXQoKIyBUcmFpbiBSYW5kb20gRm9yZXN0OiAKc2V0LnNlZWQoMSkKbXlfcmYgPC0gdHJhaW4oR3JvdXAgfi4sIG1ldGhvZCA9ICJyZiIsIGRhdGEgPSBkZl90cmFpbikKCmBgYAoKQXMgdGhlIGJ1c2luZXNzIGdldHRpbmcgaW1wdXQgZm9yIGEgbmV3IGN1c3RvbWVyIHdpdGggTSA9IDE3NTcuNTUsIFIgPSA0MSwgRiA9IDczLCB3aGljaCBzZWdtZW50YXRpb24gdGhhdCBjdXN0b21lciB3aWxsIGJlOiAgIApgYGB7cn0KCmN1czEgPC0gZGZfdGVzdCAlPiUgCiAgc2xpY2UoMSkgJT4lIAogIHNlbGVjdCgtR3JvdXApCgpjdXMxICU+JSAKICBrYWJsZSgpCgpgYGAKClRoYXQgY3VzdG9tZXIgd2lsbCBiZSBpbiBncm91cCA0OiAKCmBgYHtyfQpwcmVkaWN0KG15X3JmLCBjdXMxKSAlPiUgYXMuY2hhcmFjdGVyKCkKYGBgCgpNb2RlbCBldmFsdWF0aW9uOiAKCmBgYHtyfQoKcHJlZCA8LSBwcmVkaWN0KG15X3JmLCBkZl90ZXN0KQoKCmNvbmZ1c2lvbk1hdHJpeChwcmVkLCBkZl90ZXN0JEdyb3VwKQpgYGAKCgpgYGB7cn0KCiMgU3RhdGlzdGljYWwgZGVzY3JpcHRpb24gZm9yIHRoZSBwcmVkaWN0aW9uIG9uIHRlc3QgZGF0YTogCmRmX3Rlc3QgJT4lIAogIG11dGF0ZShHcm91cFByZWRpY3RlZCA9IHByZWQpICU+JSAKICBncm91cF9ieShHcm91cFByZWRpY3RlZCkgJT4lIAogIHN1bW1hcmlzZV9lYWNoKGZ1bnMobWVhbiksIG1vbmV5LCByZWNlbmN5LCBmcmVxKSAlPiUgCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpIHtyb3VuZCh4LCAwKX0pICU+JSAKICBrYWJsZSgpCgojIFN0YXRpc3RpY2FsIGRlc2NyaXB0aW9uIGZvciB0aGUgcHJlZGljdGlvbiBvbiB0cmFpbiBkYXRhIGNvbXBhcmVkIHRvIHRlc3QgZGF0YTogCmRmX3RyYWluICU+JSAKICBncm91cF9ieShHcm91cCkgJT4lIAogIHN1bW1hcmlzZV9lYWNoKGZ1bnMobWVhbiksIG1vbmV5LCByZWNlbmN5LCBmcmVxKSAlPiUgCiAgbXV0YXRlX2lmKGlzLm51bWVyaWMsIGZ1bmN0aW9uKHgpIHtyb3VuZCh4LCAwKX0pICU+JSAKICBrYWJsZSgpCgogIApgYGAKCgoKIyBSZWZlcmVuY2VzCgoxLiBDaGFwbWFuLCBDLiwgJiBGZWl0LCBFLiBNLiAoMjAxOSkuIFIgZm9yIG1hcmtldGluZyByZXNlYXJjaCBhbmQgYW5hbHl0aWNzLiBOZXcgWW9yaywgTlk6IFNwcmluZ2VyLgoyLiBDaGVuLCBELiwgU2FpbiwgUy4gTC4sICYgR3VvLCBLLiAoMjAxMikuIERhdGEgbWluaW5nIGZvciB0aGUgb25saW5lIHJldGFpbCBpbmR1c3RyeTogQSBjYXNlIHN0dWR5IG9mIFJGTSBtb2RlbC1iYXNlZCBjdXN0b21lciBzZWdtZW50YXRpb24gdXNpbmcgZGF0YSBtaW5pbmcuIEpvdXJuYWwgb2YgRGF0YWJhc2UgTWFya2V0aW5nICYgQ3VzdG9tZXIgU3RyYXRlZ3kgTWFuYWdlbWVudCwgMTkoMyksIDE5Ny0yMDguCjMuIEtoYWp2YW5kLCBNLiwgJiBUYXJva2gsIE0uIEouICgyMDExKS4gRXN0aW1hdGluZyBjdXN0b21lciBmdXR1cmUgdmFsdWUgb2YgZGlmZmVyZW50IGN1c3RvbWVyIHNlZ21lbnRzIGJhc2VkIG9uIGFkYXB0ZWQgUkZNIG1vZGVsIGluIHJldGFpbCBiYW5raW5nIGNvbnRleHQuIFByb2NlZGlhIENvbXB1dGVyIFNjaWVuY2UsIDMsIDEzMjctMTMzMi4KNC4gU2htdWVsaSwgRy4sIEJydWNlLCBQLiBDLiwgWWFoYXYsIEkuLCBQYXRlbCwgTi4gUi4sICYgTGljaHRlbmRhaGwgSnIsIEsuIEMuICgyMDE3KS4gRGF0YSBtaW5pbmcgZm9yIGJ1c2luZXNzIGFuYWx5dGljczogY29uY2VwdHMsIHRlY2huaXF1ZXMsIGFuZCBhcHBsaWNhdGlvbnMgaW4gUi4gSm9obiBXaWxleSAmIFNvbnMuCjUuIFpha3J6ZXdza2EsIEQuLCAmIE11cmxld3NraSwgSi4gKDIwMDUsIFNlcHRlbWJlcikuIENsdXN0ZXJpbmcgYWxnb3JpdGhtcyBmb3IgYmFuayBjdXN0b21lciBzZWdtZW50YXRpb24uIEluIDV0aCBJbnRlcm5hdGlvbmFsIENvbmZlcmVuY2Ugb24gSW50ZWxsaWdlbnQgU3lzdGVtcyBEZXNpZ24gYW5kIEFwcGxpY2F0aW9ucyAoSVNEQScwNSkgKHBwLiAxOTctMjAyKS4gSUVFRS4KCgoKCgo=