INTRODUCTION

  • The notebook includes a short exploratory analysis and comparison of few selected models for house price prediction task.
  • It uses Beijing housing prices from 2011-2017 period scrapped from Lianjia.com and shared on kaggle datastes: https://www.kaggle.com/ruiqurm/lianjia
  • Some of the variables included in the original dataset has been removed after innitial exploration to make sure the modeling corresponds as much as possible to a real situation.

LOAD DATA

  • getting data and removing columns with unique ids
  • innitial spliting for test and development set
  • small sample dataframe for interactive development
 [1] "Lng"                 "Lat"                 "tradeTime"          
 [4] "DOM"                 "followers"           "totalPrice"         
 [7] "price"               "square"              "livingRoom"         
[10] "drawingRoom"         "kitchen"             "bathRoom"           
[13] "floor"               "buildingType"        "constructionTime"   
[16] "renovationCondition" "buildingStructure"   "ladderRatio"        
[19] "elevator"            "fiveYearsProperty"   "subway"             
[22] "district"            "communityAverage"    "split_flag"         
[1] "Development dataset size:  286904" "Development dataset size:  24"    

SHORT EDA

Lng 116.4636 116.2589 116.3398 116.2866 116.3045 116.4557
Lat 39.88396 39.93433 40.08534 39.84665 39.89543 39.96799
tradeTime 2016-02-21 2017-07-23 2015-03-21 2013-09-13 2015-11-05 2013-01-30
DOM NA 127 NA NA 1 NA
followers 10 88 0 12 4 0
totalPrice 300 1022 100 190 203 230
price 33367 78707 22417 30400 37524 38251
square 89.91 129.85 44.61 62.50 54.10 60.13
livingRoom 3 3 1 2 1 2
drawingRoom 1 1 0 1 0 1
kitchen 1 1 1 1 1 1
bathRoom 1 2 1 1 1 1
floor ¶¥ 6 µÍ 13 ÖÐ 7 ÖÐ 6 µÍ 24 µÍ 6
buildingType 4 3 1 4 1 4
constructionTime 1984 2004 2012 1994 1997 δ֪
renovationCondition 4 4 1 1 3 1
buildingStructure 4 6 6 2 6 2
ladderRatio 0.500 0.500 0.062 0.333 0.250 0.333
elevator 0 1 1 0 1 0
fiveYearsProperty 1 1 0 1 1 1
subway 1 0 0 0 0 1
district 7 8 6 2 8 7
communityAverage 58694 76569 31381 44440 65996 65909
split_flag dev dev dev dev dev dev


Check types

The `show_plot = TRUE` is deprecated and will be removed in a future version.  The `show_plot()` function should be used instead.  For more info, check out the help file ?show_plot()

  • It seems many of the integers are actually categorical variables. I will deal with that in the preprocessing section and make a decision based on what makes more sense for the modeling.


Check NAs

  • DOM (Days on market) looks really bad with almost half of the records with NAs. Since data was scrapped from the online platform this can potentially have different meanings, for example we can miss data for properties that were just added (NA = 0). This would be important for filling missing data so let’s try to verify it by checking its distribution.

  • Looking at the distribution it seems quite possible that the NAs values in DOM are actually “day 0” on the market. Also Beijing is very fast-paced with 50% of the offers staying under 6 days on the portal!

  • Beside buildingType and communityAverage it looks quite complete.


Price distribution (target variable)

Correlations

  • Looking into correlation of price with available continious variables:

  • Ok, there are few highly correlated variables… but should we really use them? In the real situation they wouldn’t be available. We will have to remove things like totalPrice as well as days on the market, number of followers and even communityAverage as it could have been calculated using our targets. It looks the only continious variable we have left is the apartment’s size (square).


PREPROCESSING

Preprocessing steps:

  • Removing all non property features - the ones we hadn’t have available in the real world situation (totalPrice, communityAverage, days on market, followers)
  • Correcting columns types
  • Remove extreame outliers
  • Removing prices below 10k as obviosuly erroneous data
  • Removing some extreme outliers (ladderRatio)
  • Extract floor number from floor text field
  • Extract date related features for tradeTime
  • One-hot encode categorical variables
  • Split training and validation
  • Imputing missing values with knn
  • Reduce correlation and coolinearity
  • Remove variables with near zero variance (+ levels with very small frequency)
  • Scale & center


Date features

                1      2      3      4      5      6     
year            "2016" "2017" "2015" "2013" "2015" "2013"
quarter_of_year "1"    "3"    "1"    "3"    "4"    "1"   
month_name      "lut"  "lip"  "mar"  "wrz"  "lis"  "sty" 
week_of_year    " 8"   "30"   "12"   "37"   "45"   " 5"  
week_of_month   "3"    "4"    "3"    "2"    "1"    "5"   
day_of_month    "21"   "23"   "21"   "13"   " 5"   "30"  
weekday_name    "nie"  "nie"  "sob"  "pią"  "czw"  "śro" 
weekend         "1"    "1"    "1"    "0"    "0"    "0"   


Final preprocessing (imputation, colinearity, scaling, nzv)

caret preproc: 21.237 sec elapsed
Created from 205238 samples and 83 variables

Pre-processing:
  - conditionalX (42)
  - ignored (0)
  - removed (41)
Created from 205238 samples and 83 variables

Pre-processing:
  - centered (9)
  - ignored (74)
  - 5 nearest neighbor imputation (9)
  - scaled (9)
  - Yeo-Johnson transformation (6)

Lambda estimates for Yeo-Johnson transformation:
-0.19, 0.77, 0.72, 0.66, 0.8, -0.26


This is how our final training set looks like:

Lng 0.4035157 -1.4338343 -0.7080302 -1.1852662 -1.0249428 -0.4532327
Lat -0.7164604 -0.1664836 1.4823553 -1.1237949 -0.5912225 -0.3342391
square 0.4824763 1.3850917 -1.4036126 -0.4685898 -0.8624907 1.4433778
constructionTime -1.7182057 0.5572131 1.4673806 -0.5804963 -0.2391835 0.1021293
ladderRatio 0.9986597 0.9986597 -2.3033900 -0.2298309 -0.8535341 -0.8535341
week_of_year -1.2676085 0.2791417 -0.9414940 0.6938684 1.1425776 0.9770737
day_of_month 0.5413353 0.7515817 0.5413353 -0.3444333 -1.3476779 0.2184442
floor_number -0.9926215 0.3295334 -0.7191630 -0.9926215 1.2673657 0.3295334
livingRoom_3 2.0000000 2.0000000 1.0000000 1.0000000 1.0000000 2.0000000
livingRoom_1 1.0000000 1.0000000 2.0000000 1.0000000 2.0000000 1.0000000
livingRoom_2 1.0000000 1.0000000 1.0000000 2.0000000 1.0000000 1.0000000
drawingRoom_1 2.0000000 2.0000000 1.0000000 2.0000000 1.0000000 1.0000000
drawingRoom_0 1.0000000 1.0000000 2.0000000 1.0000000 2.0000000 1.0000000
drawingRoom_2 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000 2.0000000
bathRoom_2 1.0000000 2.0000000 1.0000000 1.0000000 1.0000000 2.0000000
buildingType_4 2.0000000 1.0000000 1.0000000 2.0000000 1.0000000 1.0000000
buildingType_3 1.0000000 2.0000000 1.0000000 1.0000000 1.0000000 1.0000000
buildingType_1 1.0000000 1.0000000 2.0000000 1.0000000 2.0000000 2.0000000
renovationCondition_4 2.0000000 2.0000000 1.0000000 1.0000000 1.0000000 1.0000000
renovationCondition_1 1.0000000 1.0000000 2.0000000 2.0000000 1.0000000 1.0000000
renovationCondition_3 1.0000000 1.0000000 1.0000000 1.0000000 2.0000000 2.0000000
buildingStructure_2 1.0000000 1.0000000 1.0000000 2.0000000 1.0000000 1.0000000
elevator_1 1.0000000 2.0000000 2.0000000 1.0000000 2.0000000 2.0000000
fiveYearsProperty_1 2.0000000 2.0000000 1.0000000 2.0000000 2.0000000 1.0000000
subway_1 2.0000000 1.0000000 1.0000000 1.0000000 1.0000000 2.0000000
district_7 2.0000000 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000
district_8 1.0000000 2.0000000 1.0000000 1.0000000 2.0000000 1.0000000
district_6 1.0000000 1.0000000 2.0000000 1.0000000 1.0000000 1.0000000
district_2 1.0000000 1.0000000 1.0000000 2.0000000 1.0000000 1.0000000
district_10 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000 2.0000000
district_1 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000
year_2016 2.0000000 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000
year_2017 1.0000000 2.0000000 1.0000000 1.0000000 1.0000000 1.0000000
year_2015 1.0000000 1.0000000 2.0000000 1.0000000 2.0000000 2.0000000
year_2013 1.0000000 1.0000000 1.0000000 2.0000000 1.0000000 1.0000000
year_2012 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000
year_2014 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000
quarter_of_year_1 2.0000000 1.0000000 2.0000000 1.0000000 1.0000000 1.0000000
quarter_of_year_3 1.0000000 2.0000000 1.0000000 2.0000000 1.0000000 1.0000000
quarter_of_year_4 1.0000000 1.0000000 1.0000000 1.0000000 2.0000000 2.0000000
quarter_of_year_2 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000 1.0000000
weekend_1 2.0000000 2.0000000 2.0000000 1.0000000 1.0000000 2.0000000
[1] 205238     42

MODELING

            used   (Mb) gc trigger   (Mb)  max used   (Mb)
Ncells   3138924  167.7    4703850  251.3   4703850  251.3
Vcells 153017995 1167.5  316973979 2418.4 316915827 2417.9

Train

I train 4 different models including basic grid search of hyperparameters and repeated 5-fold crossvalidation. I’ve tried to select varying approaches and allowing for somehow interpretable results:

  • Lasso - linnear regression with L1 regularization (lasso)
  • Multivariate Adaptive Regression Splines (MARS - link). (Also called earth)
  • Bayesian regularized neural network, 2layers (brnn)
  • Random forest (random_forest)


VALIDATION

Ranger result

Call:
 ranger::ranger(dependent.variable.name = ".outcome", data = x,      mtry = min(param$mtry, ncol(x)), min.node.size = param$min.node.size,      splitrule = as.character(param$splitrule), write.forest = TRUE,      probability = classProbs, ...) 

Type:                             Regression 
Number of trees:                  500 
Sample size:                      205238 
Number of independent variables:  42 
Mtry:                             42 
Target node size:                 5 
Variable importance mode:         impurity 
Splitrule:                        variance 
OOB prediction error (MSE):       24989313 
R squared (OOB):                  0.9464689 
  • The best performing model (~0.95 R-squared) is a random forest with 500 trees, 22 randomly selected predictors, minimal node size of 5 and variance based spliting rule. Altough doing extensive grid search over 3 hyperparameters took much more time than other models the result is significantly better.

Let’s visualize performance of the best model:

Median price in the validation set: 39330.5
Mean absolute error: 3199.81


Features Importance:

  • Predictions were generated mainly by looking at the location (Lat/Lng or district) and time the apartment was posted for sale. Such characteristics as size of the apartment, construction time or renovation condition didn’t make it into top 10.


Individual examples

At the end let’s look at few individual examples of apartments and try to explain which variables were the most important for the model to make price estimation:


Attaching package: ‘lime’

The following object is masked from ‘package:dplyr’:

    explain


THE END
LS0tCnRpdGxlOiAiLy8vIgpzdWJ0aXRsZTogPGgzPkJlaWppbmcgaG91c2luZyBtYXJrZXQgLSBleHBsb3JhdG9yeSBhbmFseXNpcyBhbmQgcHJpY2UgbW9kZWxpbmc8L2gzPgphdXRob3I6ICJtaWMuc3RhbkBvdXRsb29rLmNvbSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICAjY29kZV9mb2xkaW5nOiBoaWRlCiAgICBmaWdfd2lkdGg6IDEwCiAgICBmaWdfaGVpZ2h0OiA2CiAgICBzbWFydDogbm8KICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBtYXRoamF4OiBudWxsCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICB0aGVtZTogbHVtZW4KLS0tCgo8c3R5bGU+CiN0b2NpZnktaGVhZGVyMCB7CiAgICBkaXNwbGF5OiBub25lOwp9CgojdG9jaWZ5LWhlYWRlcjEgewogICAgZGlzcGxheTogbm9uZTsKfQo8L3N0eWxlPgoKLS0tLS0tCgojIyMgSU5UUk9EVUNUSU9OCgotIFRoZSBub3RlYm9vayBpbmNsdWRlcyBhIHNob3J0IGV4cGxvcmF0b3J5IGFuYWx5c2lzIGFuZCBjb21wYXJpc29uIG9mIGZldyBzZWxlY3RlZCBtb2RlbHMgZm9yIGhvdXNlIHByaWNlIHByZWRpY3Rpb24gdGFzay4KLSBJdCB1c2VzIEJlaWppbmcgaG91c2luZyBwcmljZXMgZnJvbSAyMDExLTIwMTcgcGVyaW9kIHNjcmFwcGVkIGZyb20gTGlhbmppYS5jb20gYW5kIHNoYXJlZCBvbiBrYWdnbGUgZGF0YXN0ZXM6IFtodHRwczovL3d3dy5rYWdnbGUuY29tL3J1aXF1cm0vbGlhbmppYV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9ydWlxdXJtL2xpYW5qaWEpCi0gU29tZSBvZiB0aGUgdmFyaWFibGVzIGluY2x1ZGVkIGluIHRoZSBvcmlnaW5hbCBkYXRhc2V0IGhhcyBiZWVuIHJlbW92ZWQgYWZ0ZXIgaW5uaXRpYWwgZXhwbG9yYXRpb24gdG8gbWFrZSBzdXJlIHRoZSBtb2RlbGluZyBjb3JyZXNwb25kcyBhcyBtdWNoIGFzIHBvc3NpYmxlIHRvIGEgcmVhbCBzaXR1YXRpb24uCgotLS0tLS0KCiMjIyBTRVRVUAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGljdG9jKTsgbGlicmFyeShkb1BhcmFsbGVsKQpsaWJyYXJ5KHN0cmluZ3IpOyBsaWJyYXJ5KGx1YnJpZGF0ZSk7IGxpYnJhcnkoaW5zcGVjdGRmKTsgbGlicmFyeShQZXJmb3JtYW5jZUFuYWx5dGljcyk7IGxpYnJhcnkoZmFzdER1bW1pZXMpCmxpYnJhcnkoY2FyZXQpOyBsaWJyYXJ5KG1sYmVuY2gpOyBsaWJyYXJ5KHhnYm9vc3QpOyBsaWJyYXJ5KGVhcnRoKTsgbGlicmFyeShyYW5nZXIpCmxpYnJhcnkoT3BlblN0cmVldE1hcCkKbGlicmFyeShwYXRjaHdvcmspOyBsaWJyYXJ5KGhyYnJ0aGVtZXMpOyB0aGVtZV9zZXQodGhlbWVfaXBzdW0oKSkKYGBgCgojIyMgTE9BRCBEQVRBCgotIGdldHRpbmcgZGF0YSBhbmQgcmVtb3ZpbmcgY29sdW1ucyB3aXRoIHVuaXF1ZSBpZHMKLSBpbm5pdGlhbCBzcGxpdGluZyBmb3IgdGVzdCBhbmQgZGV2ZWxvcG1lbnQgc2V0Ci0gc21hbGwgc2FtcGxlIGRhdGFmcmFtZSBmb3IgaW50ZXJhY3RpdmUgZGV2ZWxvcG1lbnQKCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBiZWlqaW5nX2hvdXNpbmcgLT4gYmgKYmggPC0gcmVhZF9jc3YoImRhdGEvbmV3LmNzdiIsIGxvY2FsZSA9IHJlYWRyOjpsb2NhbGUoZW5jb2RpbmcgPSAibGF0aW4xIikpICU+JQogIHNlbGVjdCgtdXJsLCAtaWQsIC1DaWQpICMgcmVtb3ZpbmcgaWRzCgojIHNhdmUgdGVzdCBhbmQgbWFrZSBzYW1wbGUgZGYKc2V0LnNlZWQoMjMpCmJoIDwtIGJoW3NhbXBsZShucm93KGJoKSksXSAjIHJhbmRvbSBzaHVmZmxlIHJvd3MKZGZfc3BsaXRzIDwtIGJoICU+JQogIG11dGF0ZShzcGxpdF9mbGFnID0gc2FtcGxlKDE6Miwgc2l6ZSA9IGRpbShiaClbMV0sIHByb2I9YygwLjEsIDAuOSksIHJlcGxhY2U9VFJVRSkpICU+JQogIG11dGF0ZShzcGxpdF9mbGFnID0gaWZlbHNlKHNwbGl0X2ZsYWcgPT0gMSwgInRlc3QiLCAiZGV2IikpICU+JQogIGdyb3VwX2J5KHNwbGl0X2ZsYWcpICU+JQogIGdyb3VwX3NwbGl0KCkKCmJoX2RldiA8LSBkZl9zcGxpdHNbWzFdXQpiaF90ZXN0IDwtIGRmX3NwbGl0c1tbMl1dCndyaXRlX2NzdihiaF90ZXN0LCAiYmhfdGVzdC5jc3YiKQpiaCA8LSBiaF9kZXYKYmhfc2FtcGxlIDwtIGJoICU+JSBzYW1wbGVfbigwLjAyICogZGltKGJoKVsxXSkKYGBgCgpgYGB7cn0KY29sbmFtZXMoYmgpCnByaW50KHBhc3RlKCJEZXZlbG9wbWVudCBkYXRhc2V0IHNpemU6ICIsIGRpbShiaCkpKQpgYGAKCgojIyMgU0hPUlQgRURBCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQprbml0cjo6a2FibGUodChoZWFkKGJoKSkpCmBgYAoKPGJyPiAgCgojIyMjICoqQ2hlY2sgdHlwZXMqKgoKYGBge3J9Cmluc3BlY3RfdHlwZXMoYmgsIHNob3dfcGxvdCA9IFRSVUUpCmBgYAoKLSBJdCBzZWVtcyBtYW55IG9mIHRoZSBpbnRlZ2VycyBhcmUgYWN0dWFsbHkgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiBJIHdpbGwgZGVhbCB3aXRoIHRoYXQgaW4gdGhlIHByZXByb2Nlc3Npbmcgc2VjdGlvbiBhbmQgbWFrZSBhIGRlY2lzaW9uIGJhc2VkIG9uIHdoYXQgbWFrZXMgbW9yZSBzZW5zZSBmb3IgdGhlIG1vZGVsaW5nLgoKPGJyPgoKIyMjIyAqKkNoZWNrIE5BcyoqCgpgYGB7cn0KaW5zcGVjdF9uYShiaCkKYGBgCgotIERPTSAoRGF5cyBvbiBtYXJrZXQpIGxvb2tzIHJlYWxseSBiYWQgd2l0aCBhbG1vc3QgaGFsZiBvZiB0aGUgcmVjb3JkcyB3aXRoIE5Bcy4gU2luY2UgZGF0YSB3YXMgc2NyYXBwZWQgZnJvbSB0aGUgb25saW5lIHBsYXRmb3JtIHRoaXMgY2FuIHBvdGVudGlhbGx5IGhhdmUgZGlmZmVyZW50IG1lYW5pbmdzLCBmb3IgZXhhbXBsZSB3ZSBjYW4gbWlzcyBkYXRhIGZvciBwcm9wZXJ0aWVzIHRoYXQgd2VyZSBqdXN0IGFkZGVkIChOQSA9IDApLiBUaGlzIHdvdWxkIGJlIGltcG9ydGFudCBmb3IgZmlsbGluZyBtaXNzaW5nIGRhdGEgc28gbGV0J3MgdHJ5IHRvIHZlcmlmeSBpdCBieSBjaGVja2luZyBpdHMgZGlzdHJpYnV0aW9uLgoKYGBge3J9CmdncGxvdChiaCwgYWVzKERPTSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVkaWFuKGJoJERPTSwgbmEucm09VFJVRSksIGNvbG91ciA9ICJyZWQiKQpgYGAKLSBMb29raW5nIGF0IHRoZSBkaXN0cmlidXRpb24gaXQgc2VlbXMgcXVpdGUgcG9zc2libGUgdGhhdCB0aGUgTkFzIHZhbHVlcyBpbiBET00gYXJlIGFjdHVhbGx5ICJkYXkgMCIgb24gdGhlIG1hcmtldC4gQWxzbyBCZWlqaW5nIGlzIHZlcnkgZmFzdC1wYWNlZCB3aXRoIDUwJSBvZiB0aGUgb2ZmZXJzIHN0YXlpbmcgdW5kZXIgNiBkYXlzIG9uIHRoZSBwb3J0YWwhCgotIEJlc2lkZSBidWlsZGluZ1R5cGUgYW5kIGNvbW11bml0eUF2ZXJhZ2UgaXQgbG9va3MgcXVpdGUgY29tcGxldGUuIAoKPGJyPgoKIyMjIyAqKlByaWNlIGRpc3RyaWJ1dGlvbiAodGFyZ2V0IHZhcmlhYmxlKSoqCgoKYGBge3J9CmdncGxvdChiaCwgYWVzKHByaWNlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTEwMCkKYGBgCgoKIyMjIyAqKkNvcnJlbGF0aW9ucyoqCgotIExvb2tpbmcgaW50byBjb3JyZWxhdGlvbiBvZiBwcmljZSB3aXRoIGF2YWlsYWJsZSBjb250aW5pb3VzIHZhcmlhYmxlczoKCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTYsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9ClBlcmZvcm1hbmNlQW5hbHl0aWNzOjpjaGFydC5Db3JyZWxhdGlvbihzZWxlY3QoYmhfc2FtcGxlLCBET00sIGZvbGxvd2VycywgcHJpY2UsIHNxdWFyZSwgbGFkZGVyUmF0aW8sIHRvdGFsUHJpY2UsIGNvbW11bml0eUF2ZXJhZ2UpKQpgYGAKCi0gT2ssIHRoZXJlIGFyZSBmZXcgaGlnaGx5IGNvcnJlbGF0ZWQgdmFyaWFibGVzLi4uIGJ1dCBzaG91bGQgd2UgcmVhbGx5IHVzZSB0aGVtPyAqKkluIHRoZSByZWFsIHNpdHVhdGlvbiB0aGV5IHdvdWxkbid0IGJlIGF2YWlsYWJsZSoqLiBXZSB3aWxsIGhhdmUgdG8gcmVtb3ZlIHRoaW5ncyBsaWtlIHRvdGFsUHJpY2UgYXMgd2VsbCBhcyBkYXlzIG9uIHRoZSBtYXJrZXQsIG51bWJlciBvZiBmb2xsb3dlcnMgYW5kIGV2ZW4gY29tbXVuaXR5QXZlcmFnZSBhcyBpdCBjb3VsZCBoYXZlIGJlZW4gY2FsY3VsYXRlZCB1c2luZyBvdXIgdGFyZ2V0cy4gSXQgbG9va3MgdGhlIG9ubHkgY29udGluaW91cyB2YXJpYWJsZSB3ZSBoYXZlIGxlZnQgaXMgdGhlIGFwYXJ0bWVudCdzIHNpemUgKGBzcXVhcmVgKS4KCjxicj4KCiMjIyMjICoqVGltZSB0cmVuZHMqKgoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CndlZWtseV9hdmcgPC0gYmggJT4lCiAgZ3JvdXBfYnkod2VlayA9IGx1YnJpZGF0ZTo6Zmxvb3JfZGF0ZShhcy5EYXRlKHRyYWRlVGltZSksIHVuaXQgPSAid2VlayIpKSAlPiUKICBzdW1tYXJpemUoYXZnX3ByaWNlID0gbWVhbihwcmljZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIoeWVhcih3ZWVrKSA+IDIwMTEpCgpnZ3Bsb3Qod2Vla2x5X2F2ZywgYWVzKHdlZWssIGF2Z19wcmljZSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9zbW9vdGgoKSArCiAgbGFicyh5ID0gIkF2ZXJhZ2Ugd2Vla2x5IHByaWNlIiwgeCA9ICJXZWVrIikKYGBgCgotIEluIGdlbmVyYWwgdGhlcmUgaXMgYSB2aXNpYmxlIHVwd2FyZCB0cmVuZCBidXQgYmVjYXVzZSB0aGVyZSBpcyBubyBjb25zZWN1dGl2ZSBncm93dGggYWNyb3NzIGFsbCBkYXRlcyAoMjAxNyA+IDIwMTYgYnV0IDIwMTggPCAyMDE3KSBpdCBtaWdodCBiZSB1c2VmdWxsIHRvIGtlcHAgeWVhciBhcyBjYXRlZ29yaWNhbCB2YXJpYWJsZS4KCjxicj4KCiMjIyMjICoqR2VvIHRyZW5kcyoqCgoKYGBge3Igd2FybmluZz1GQUxTRX0KbWFwIDwtIG9wZW5tYXAodXBwZXJMZWZ0ID0gYyhtYXgoYmgkTGF0KSxtaW4oYmgkTG5nKSksCiAgICAgICAgICAgICAgIGxvd2VyUmlnaHQgPSBjKG1pbihiaCRMYXQpLG1heChiaCRMbmcpKSwKICAgICAgICAgICAgICAgem9vbSA9IE5VTEwsCiAgICAgICAgICAgICAgIHR5cGUgPSBjKCJvc20iKSkKbWFwLmxhdGxvbiA8LSBvcGVucHJvaihtYXAsIHByb2plY3Rpb24gPSAiK3Byb2o9bG9uZ2xhdCArZWxscHM9V0dTODQgK2RhdHVtPVdHUzg0ICtub19kZWZzIikKCmF1dG9wbG90KG1hcC5sYXRsb24pICsKICBnZW9tX3BvaW50KGRhdGEgPSBiaF9zYW1wbGUsIGFlcyhMbmcsIExhdCwgY29sb3VyID0gcHJpY2UpLCBhbHBoYSA9IDAuNikgKyAKICBsYWJzKHRpdGxlID0gIkJlaWppbmcgLSBob3VzZSBwcmljZSBieSBsb2NhdGlvbiIsIHggPSAiICIsIHkgPSAiICIsIGNvbG91ciA9ICJQcmljZSIpICsgCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMCwwLDAsMCksImNtIikpCmBgYAoKPGJyPiAKKipEaXN0cmljdHMgcHJpY2VzOioqCgpgYGB7cn0KcDEgPC0gYXV0b3Bsb3QobWFwLmxhdGxvbikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGJoX3NhbXBsZSwgYWVzKExuZywgTGF0LCBjb2xvdXIgPSBhcy5mYWN0b3IoZGlzdHJpY3QpKSwgYWxwaGEgPSAwLjYpICsgCiAgbGFicyh0aXRsZSA9ICJQcmljZSBkaXN0cmlidXRpb24gYnkgZGlzdHJpY3QiLCB4ID0gIiAiLCB5ID0gIiAiLCBjb2xvdXIgPSAiRGlzdHJpY3QiKSArIAogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsMCwwLDApLCJjbSIpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpwMiA8LSBnZ3Bsb3QoYmgsIGFlcyhhcy5mYWN0b3IoZGlzdHJpY3QpLCBwcmljZSwgZmlsbCA9IGFzLmZhY3RvcihkaXN0cmljdCkpKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShwb3NpdGlvbiA9ICJ0b3AiKSArCiAgbGFicyh0aXRsZSA9ICIgIiwgeCA9ICJEaXN0cmljdCIsIHkgPSAiUHJpY2UiLCBjb2xvdXIgPSAiICIpICsgCiAgY29vcmRfZmxpcCh5bGltPWMoMCwgMTAwMDAwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGxvdC5tYXJnaW4gPSB1bml0KGMoMCwwLDAsMCksImNtIikpCgpwMSArIHAyICsgcGxvdF9sYXlvdXQobmNvbCA9IDIpCmBgYAoKLSBEaXN0cmljdHMgMSBhbmQgMTAgbG9jYXRlZCBpbiB0aGUgY2VudGVyIHdpdGggdmlzaWJseSBoaWdoZXIgcHJpY2VzLiBJdCBkb2VzIG5vdCBtYWtlIHNlbnNlIHRvIGtlZXAgZGlzdHJpY3RzIGFzIGNvbnRpbmlvdXMgdmFyaWFibGVzIGFzIHRoZSBudW1iZXJzIGNvcmVzcG9uZCB2ZXJ5IGxvc2VseSB0byBsb2NhdGlvbnMuCgo8YnI+CgojIyMjICoqQmFzZWxpbmUgbW9kZWwqKgoKQmVmb3JlIG1vdmluZyB0byB0aGUgcHJlcHJvY2Vzc2luZyBwYXJ0IGxldHMgdHJ5IHRvIGZpdCBzaW1wbGUgbGluZWFyIG1vZGVsIHdpdGggcmF3IGRhdGEgYXMgYSBiYXNlbGluZToKYGBge3J9CmJhc2VfZml0IDwtIGxtKHByaWNlIH4gLiwgZGF0YSA9IHNlbGVjdChiaCwgLXRvdGFsUHJpY2UsIC1jb21tdW5pdHlBdmVyYWdlLCAtZm9sbG93ZXJzLCAtRE9NLCAtc3BsaXRfZmxhZykgJT4lIG5hLm9taXQpCmNhdChwYXN0ZTAoIlItc3F1YXJlZDogIiwgcm91bmQoc3VtbWFyeShiYXNlX2ZpdCkkYWRqLnIuc3F1YXJlZCwgMiksCiAgICAgICAgICAgIlxuUk1TRTogIiwgcm91bmQoc3FydChtZWFuKGJhc2VfZml0JHJlc2lkdWFsc14yKSksIDIpLAogICAgICAgICAgICJcbk1BRTogIiwgcm91bmQobWVhbihhYnMoYmFzZV9maXQkcmVzaWR1YWxzKSksIDIpKSkKYGBgCgoKLS0tLS0tCgojIyMgUFJFUFJPQ0VTU0lORwoKKipQcmVwcm9jZXNzaW5nIHN0ZXBzOioqICAKCi0gUmVtb3ZpbmcgYWxsIG5vbiBwcm9wZXJ0eSBmZWF0dXJlcyAtIHRoZSBvbmVzIHdlIGhhZG4ndCBoYXZlIGF2YWlsYWJsZSBpbiB0aGUgcmVhbCB3b3JsZCBzaXR1YXRpb24gKHRvdGFsUHJpY2UsIGNvbW11bml0eUF2ZXJhZ2UsIGRheXMgb24gbWFya2V0LCBmb2xsb3dlcnMpCi0gQ29ycmVjdGluZyBjb2x1bW5zIHR5cGVzCi0gUmVtb3ZlIGV4dHJlYW1lIG91dGxpZXJzCi0gUmVtb3ZpbmcgcHJpY2VzIGJlbG93IDEwayBhcyBvYnZpb3N1bHkgZXJyb25lb3VzIGRhdGEKLSBSZW1vdmluZyBzb21lIGV4dHJlbWUgb3V0bGllcnMgKGxhZGRlclJhdGlvKQotIEV4dHJhY3QgZmxvb3IgbnVtYmVyIGZyb20gZmxvb3IgdGV4dCBmaWVsZAotIEV4dHJhY3QgZGF0ZSByZWxhdGVkIGZlYXR1cmVzIGZvciB0cmFkZVRpbWUKLSBPbmUtaG90IGVuY29kZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKLSBTcGxpdCB0cmFpbmluZyBhbmQgdmFsaWRhdGlvbgotIEltcHV0aW5nIG1pc3NpbmcgdmFsdWVzIHdpdGgga25uCi0gUmVkdWNlIGNvcnJlbGF0aW9uIGFuZCBjb29saW5lYXJpdHkKLSBSZW1vdmUgdmFyaWFibGVzIHdpdGggbmVhciB6ZXJvIHZhcmlhbmNlICgrIGxldmVscyB3aXRoIHZlcnkgc21hbGwgZnJlcXVlbmN5KQotIFNjYWxlICYgY2VudGVyCgo8YnI+CgojIyMjICoqRGF0ZSBmZWF0dXJlcyoqCgpgYGB7cn0KZGF0ZV9mZWF0dXJlcyA8LSBmdW5jdGlvbihkYXRlcywgYXNfZmFjdG9ycyA9IEZBTFNFKXsKICAiVGFrZXMgYSB2ZWN0b3Igb2YgZGF0ZXMgYW5kIHJldHVybnMgYSBkYXRhZnJhbWUgaW5jbHVkaW5nIHllYXIsIHF1YXJ0ZXJfb2ZfeWVhciwgbW9udGhfb2ZfeWVhciwgd2Vla19vZl95ZWFyLCB3ZWVrX29mX21vbnRoLCBkYXlfb2ZfbW9udGgsIHdlZWtkYXkgYW5kIHdlZWtlbmQiCiAgCiAgbW9udGh3ZWVrcyA8LSBmdW5jdGlvbih4KSB7CiAgICBjZWlsaW5nKGFzLm51bWVyaWMoZm9ybWF0KHgsICIlZCIpKS83KQogIH0KICBkYXRlcyA8LSBhcy5kYXRhLmZyYW1lKGRhdGVzKQogIGNvbG5hbWVzKGRhdGVzKSA8LSBjKCJkYXRlIikKICBkYXRlc19mZWF0dXJlcyA8LSBkYXRlcyAlPiUgbXV0YXRlKHllYXIgPSBsdWJyaWRhdGU6OnllYXIoZGF0ZSkpICU+JSAKICAgIG11dGF0ZShxdWFydGVyX29mX3llYXIgPSBsdWJyaWRhdGU6OnF1YXJ0ZXIoZGF0ZSkpICU+JSAKICAgIG11dGF0ZShtb250aF9vZl95ZWFyID0gbHVicmlkYXRlOjptb250aChkYXRlKSkgJT4lCiAgICBtdXRhdGUobW9udGhfbmFtZSA9IGx1YnJpZGF0ZTo6bW9udGgoZGF0ZSwgbGFiZWwgPSBUUlVFKSkgJT4lCiAgICBtdXRhdGUod2Vla19vZl95ZWFyID0gbHVicmlkYXRlOjp3ZWVrKGRhdGUpKSAlPiUgCiAgICBtdXRhdGUod2Vla19vZl9tb250aCA9IG1vbnRod2Vla3MoZGF0ZSkpICU+JSBtdXRhdGUoZGF5X29mX21vbnRoID0gbHVicmlkYXRlOjpkYXkoZGF0ZSkpICU+JSAKICAgIG11dGF0ZShkYXlfb2ZfeWVhciA9IGx1YnJpZGF0ZTo6eWRheShkYXRlKSkgJT4lIG11dGF0ZSh3ZWVrZGF5ID0gbHVicmlkYXRlOjp3ZGF5KGRhdGUpKSAlPiUgCiAgICBtdXRhdGUod2Vla2RheV9uYW1lID0gbHVicmlkYXRlOjp3ZGF5KGRhdGUsIGxhYmVsID0gVFJVRSkpICU+JSAKICAgIG11dGF0ZSh3ZWVrZW5kID0gaWZlbHNlKHdlZWtkYXkgJWluJSBjKDEsIDcpLCAxLCAwKSkgJT4lIAogICAgbXV0YXRlKHltZF9mb3JtYXQgPSBsdWJyaWRhdGU6OnltZChwYXN0ZSh5ZWFyLCBtb250aF9vZl95ZWFyLCBkYXlfb2ZfbW9udGgsIHNlcCA9ICItIikpKQogIGlmIChhc19mYWN0b3JzKSB7CiAgICBkYXRlc19mZWF0dXJlc1tjb2xuYW1lcyhkYXRlc19mZWF0dXJlcylbLTFdXSA8LSBsYXBwbHkoZGF0ZXNfZmVhdHVyZXNbY29sbmFtZXMoZGF0ZXNfZmVhdHVyZXMpWy0xXV0sIAogICAgICBmYWN0b3IpCiAgfQogIHJldHVybihkYXRlc19mZWF0dXJlcykKfQoKdHJhZGV0aW1lX2ZlYXR1cmVzIDwtIGRhdGVfZmVhdHVyZXMoYmgkdHJhZGVUaW1lLCBhc19mYWN0b3JzID0gRkFMU0UpICU+JSBzZWxlY3QoLWRhdGUsIC15bWRfZm9ybWF0LCAtd2Vla2RheSwgLW1vbnRoX29mX3llYXIsIC1kYXlfb2ZfeWVhcikKYGBgCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQp0KGhlYWQodHJhZGV0aW1lX2ZlYXR1cmVzKSkKYGBgCgo8YnI+CgojIyMjICoqQ2xlYW5pbmcqKgoKYGBge3Igd2FybmluZz1GQUxTRX0KYmhfcHJvY2Vzc2VkIDwtIGJoICU+JQogIHJlbmFtZShmbG9vcl9jaGFyYWN0ZXIgPSBgZmxvb3JgKSAlPiUKICBjYmluZCh0cmFkZXRpbWVfZmVhdHVyZXMpICU+JQogIG11dGF0ZShmbG9vcl9udW1iZXIgPSBzdHJfZXh0cmFjdChmbG9vcl9jaGFyYWN0ZXIsICIoXFxkKykoPzogXFxkKykqJCIpKSAlPiUKICBmaWx0ZXIoYXMubnVtZXJpYyh5ZWFyKSA+IDIwMTEpICU+JQogIG1hcF9hdChjKCJsaXZpbmdSb29tIiwgImRyYXdpbmdSb29tIiwgImtpdGNoZW4iLCAiYmF0aFJvb20iLCAiYnVpbGRpbmdUeXBlIiAsICJidWlsZGluZ1N0cnVjdHVyZSIsICJyZW5vdmF0aW9uQ29uZGl0aW9uIiwgImZpdmVZZWFyc1Byb3BlcnR5IiwgImVsZXZhdG9yIiwgInN1YndheSIsICJkaXN0cmljdCIsICJxdWFydGVyX29mX3llYXIiLCAid2Vla2VuZCIsICJ3ZWVrZGF5X25hbWUiLCAibW9udGhfbmFtZSIsICJ5ZWFyIiksIGFzLmZhY3RvcikgJT4lCiAgbWFwX2F0KGMoIkxuZyIsICJMYXQiLCAiRE9NIiwgImZvbGxvd2VycyIsICJwcmljZSIsICJzcXVhcmUiLCAiY29uc3RydWN0aW9uVGltZSIsICJsYWRkZXJSYXRpbyIsICJmbG9vcl9udW1iZXIiLCAiY29uc3RydWN0aW9uVGltZSIsICJ3ZWVrX29mX3llYXIiLCAiZGF5X29mX21vbnRoIiksIGFzLm51bWVyaWMpICU+JQogIGFzX3RpYmJsZSgpICU+JQogIHNlbGVjdCgtZmxvb3JfY2hhcmFjdGVyLCAtdHJhZGVUaW1lLCAtdG90YWxQcmljZSwgLWNvbW11bml0eUF2ZXJhZ2UsIC1ET00sIC1mb2xsb3dlcnMsIC1zcGxpdF9mbGFnKSAlPiUKICBmaWx0ZXIoYnVpbGRpbmdUeXBlICVpbiUgYygxLCAyLCAzLCA0KSkgJT4lCiAgZmlsdGVyKGxhZGRlclJhdGlvIDwgcXVhbnRpbGUoYmgkbGFkZGVyUmF0aW8sIDAuOTkpKSAlPiUKICBmaWx0ZXIocHJpY2UgPiAxMDAwMCkgJT4lCiAgZmlsdGVyKGNvbnN0cnVjdGlvblRpbWUgPiAxODAwKSAlPiUKICBtdXRhdGUobmFDb25zdHJ1Y3Rpb24gPSBhcy5mYWN0b3IoaWZlbHNlKGlzLm5hKGNvbnN0cnVjdGlvblRpbWUpLCAibmFfY3QiLCAib2tfY3QiKSkpICU+JQogIG11dGF0ZShjb25zdHJ1Y3Rpb25UaW1lID0gcmVwbGFjZV9uYShjb25zdHJ1Y3Rpb25UaW1lLCAwKSkgJT4lCiAgbmEub21pdCgpCgpzdG9waWZub3QobWFwKGJoX3Byb2Nlc3NlZCwgY2xhc3MpWzFdICVpbiUgYygiZmFjdG9yIiwgIm9yZGVyZWQiLCAibnVtZXJpYyIpKQpzdG9waWZub3Qoc3VtKGlzLm5hKGJoX3Byb2Nlc3NlZCkpID09IDApCmBgYAoKPGJyPgoKIyMjIyAqKkR1bW1pZXMqKgoKYGBge3J9CiMgZmFzdGVyIHRoZW4gY2FyZXQgZGVmYXVsdHMKYmhfZmFjdG9ycyA8LSBzZWxlY3RfaWYoYmhfcHJvY2Vzc2VkLCBpcy5mYWN0b3IpCmR1bW1pZXMgPC0gc2VsZWN0X2lmKGZhc3REdW1taWVzOjpkdW1teV9jb2xzKGJoX2ZhY3RvcnMpLCBpcy5pbnRlZ2VyKQpiaF9wcm9jZXNzZWQgPC0gc2VsZWN0X2lmKGJoX3Byb2Nlc3NlZCwgaXMubnVtZXJpYykgJT4lIGNiaW5kKGR1bW1pZXMpCmJoX3Byb2Nlc3NlZCA8LSBjYmluZChiaF9wcm9jZXNzZWRbLDE6MTBdLCBtYXBfZGYoYmhfcHJvY2Vzc2VkWzExOmRpbShiaF9wcm9jZXNzZWQpWzJdXSwgYXMuZmFjdG9yKSkKYGBgCgo8YnI+CgojIyMjICoqVHJhaW5pbmcgc3BsaXQqKgoKYGBge3J9CnNldC5zZWVkKDIzKQp0dl9zcGxpdCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGJoX3Byb2Nlc3NlZCRwcmljZSwgcCA9IDAuOCwgbGlzdD1GQUxTRSkKYmhfdHJhaW4gPC0gYmhfcHJvY2Vzc2VkW3R2X3NwbGl0LF0KYmhfdmFsIDwtIGJoX3Byb2Nlc3NlZFstdHZfc3BsaXQsXQoKeF90cmFpbiA8LSBiaF90cmFpbiAlPiUgc2VsZWN0KC1wcmljZSkKeV90cmFpbiA8LSBiaF90cmFpbiAlPiUgc2VsZWN0KHByaWNlKQp4X3ZhbCA8LSBiaF92YWwgJT4lIHNlbGVjdCgtcHJpY2UpCnlfdmFsIDwtIGJoX3ZhbCAlPiUgc2VsZWN0KHByaWNlKQpgYGAKCjxicj4KCiMjIyMgKipGaW5hbCBwcmVwcm9jZXNzaW5nIChpbXB1dGF0aW9uLCBjb2xpbmVhcml0eSwgc2NhbGluZywgbnp2KSoqCgpgYGB7ciB3YXJuaW5nPUZBTFNFfQoKIyBjYXJldCBwcmVwcm9jZXNzaW5nIHdvdWxkIGF1dG9tYXRpY2FsbHkgc2NhbGUgZHVtbWllcyBpZiB3ZSBrZWVwIHRoZW0gYXMgbnVtZXJpYyBhbmQgc2tpcCBvdGhlciBvcGVyYXRpb25zIGlmIHdlIGtlZXAgdGhlbSBhcyBmYWN0b3JzIC0+IHR3byBwcmVwcm9jIHN0ZXBzCgp0aWMoImNhcmV0IHByZXByb2MiKQpwcmVwcm9jX251bWVyaWMgPC0gcHJlUHJvY2Vzcyh4X3RyYWluLCBtZXRob2QgPSBjKCJjZW50ZXIiLCAic2NhbGUiLCAiWWVvSm9obnNvbiIsICJrbm5JbXB1dGUiKSkKeF90cmFpbiA8LSBwcmVkaWN0KHByZXByb2NfbnVtZXJpYywgeF90cmFpbikKeF90cmFpbiA8LSBtYXBfZGYoeF90cmFpbiwgYXMubnVtZXJpYykKcHJlcHJvY19hbGwgPC0gcHJlUHJvY2Vzcyh4X3RyYWluLCBtZXRob2QgPSBjKCJuenYiLCAiY29yciIsICJjb25kaXRpb25hbFgiKSkKeF90cmFpbiA8LSBwcmVkaWN0KHByZXByb2NfYWxsLCB4X3RyYWluKQoKeF92YWwgPC0gcHJlZGljdChwcmVwcm9jX251bWVyaWMsIHhfdmFsKQp4X3ZhbCA8LSBtYXBfZGYoeF92YWwsIGFzLm51bWVyaWMpCnhfdmFsIDwtIHByZWRpY3QocHJlcHJvY19hbGwsIHhfdmFsKQoKd3JpdGVfcmRzKHhfdHJhaW4sICJkYXRhL3hfdHJhaW4ucmRzIikKd3JpdGVfcmRzKHhfdmFsLCAiZGF0YS94X3ZhbC5yZHMiKQoKdG9jKCkKCmBgYAoKYGBge3J9CnByaW50KHByZXByb2NfYWxsKQpwcmludChwcmVwcm9jX251bWVyaWMpCmBgYAoKPGJyPgoKKipUaGlzIGlzIGhvdyBvdXIgZmluYWwgdHJhaW5pbmcgc2V0IGxvb2tzIGxpa2U6KioKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CmtuaXRyOjprYWJsZSh0KGhlYWQoeF90cmFpbikpKQpgYGAKCmBgYHtyfQpkaW0oeF90cmFpbikKYGBgCgotLS0tLS0KCiMjIyBNT0RFTElORwoKYGBge3J9CmdjKCkKYGBgCgojIyMjIFRyYWluCgpJIHRyYWluIDQgZGlmZmVyZW50IG1vZGVscyBpbmNsdWRpbmcgYmFzaWMgZ3JpZCBzZWFyY2ggb2YgaHlwZXJwYXJhbWV0ZXJzIGFuZCByZXBlYXRlZCA1LWZvbGQgY3Jvc3N2YWxpZGF0aW9uLiBJJ3ZlIHRyaWVkIHRvIHNlbGVjdCB2YXJ5aW5nIGFwcHJvYWNoZXMgYW5kIGFsbG93aW5nIGZvciBzb21laG93IGludGVycHJldGFibGUgcmVzdWx0czoKCi0gTGFzc28gLSBsaW5uZWFyIHJlZ3Jlc3Npb24gd2l0aCBMMSByZWd1bGFyaXphdGlvbiAoYGxhc3NvYCkKLSBNdWx0aXZhcmlhdGUgQWRhcHRpdmUgUmVncmVzc2lvbiBTcGxpbmVzIChNQVJTIC0gW2xpbmtdKGh0dHA6Ly91Yy1yLmdpdGh1Yi5pby9tYXJzKSkuIChBbHNvIGNhbGxlZCBgZWFydGhgKQotIEJheWVzaWFuIHJlZ3VsYXJpemVkIG5ldXJhbCBuZXR3b3JrLCAybGF5ZXJzIChgYnJubmApCi0gUmFuZG9tIGZvcmVzdCAoYHJhbmRvbV9mb3Jlc3RgKQoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CnRpYygiVHJhaW5pbmcgbW9kZWxzIikKc2V0LnNlZWQoMjMpCnRyYWluaW5nX2RhdGEgPC0gY2JpbmQoeV90cmFpbiwgeF90cmFpbikKbWV0aG9kcyA8LSBjKCJsYXNzbyIsICJlYXJ0aCIsICJicm5uIiwgInJhbmdlciIpCmZpdENvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbnVtYmVyID0gNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwZWF0cyA9IDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlYXJjaCA9ICJncmlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxsb3dQYXJhbGxlbCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2VJdGVyID0gVFJVRSkKCiNjbCA8LSBtYWtlUFNPQ0tjbHVzdGVyKDIpCiNyZWdpc3RlckRvUGFyYWxsZWwoY2wpCm1vZGVsc19tZXRyaWNzIDwtIGxpc3QoKQpmb3IgKG0gaW4gbWV0aG9kcykgewoKICBtb2RlbCA8LSBjYXJldDo6dHJhaW4ocHJpY2UgfiAuLAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5pbmdfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gbSwKICAgICAgICAgICAgICAgICAgICAgICAgdHJDb250cm9sID0gZml0Q29udHJvbCwKICAgICAgICAgICAgICAgICAgICAgICAgaW1wb3J0YW5jZSA9ICdpbXB1cml0eScsCiAgICAgICAgICAgICAgICAgICAgICAgIG5hLmFjdGlvbiA9IG5hLnBhc3MpCgogIHNhdmVSRFMobW9kZWwsIHBhc3RlMCgibW9kZWxzLyIsIG0sICIucmRzIikpCiAgbW9kZWxzX21ldHJpY3NbW21dXSA8LSBtb2RlbCRyZXNhbXBsZQogIGdjKCkKfQoKI3N0b3BDbHVzdGVyKGNsKQp0b2MoKQpgYGAKCjxicj4KCiMjIyMgKipDb21wYXJlIG1vZGVscyBwZXJmb3JtYW5jZSAqKgoKYGBge3J9Cm1vZGVscyA8LSBsaXN0LmZpbGVzKCJtb2RlbHMvIikKbW9kZWxzX21ldHJpY3MgPC0gbGlzdCgpCmZvciAobW9kZWwgaW4gbW9kZWxzKSB7CiAgZml0IDwtIHJlYWRfcmRzKHBhc3RlMCgibW9kZWxzLyIsIG1vZGVsKSkKICBtb2RlbHNfbWV0cmljc1tbZml0JG1ldGhvZF1dIDwtIGZpdCRyZXNhbXBsZQp9CmBgYAoKYGBge3J9CmRmX21ldHJpY3MgPC0gZG8uY2FsbCh3aGF0ID0gcmJpbmQsIGFyZ3MgPSBtb2RlbHNfbWV0cmljcykgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lCiAgc2VwYXJhdGUocm93bmFtZSwgaW50byA9IGMoIm1vZGVsX25hbWUiLCAiaXRlcmF0aW9uIikpICU+JQogIGZpbHRlcihtb2RlbF9uYW1lICVpbiUgYygibGFzc28iLCAiZWFydGgiLCAiYnJubiIsICJyYW5nZXIiKSkgJT4lCiAgbXV0YXRlKG1vZGVsX25hbWUgPSBpZmVsc2UobW9kZWxfbmFtZSA9PSAicmFuZ2VyIiwgInJhbmRvbV9mb3Jlc3QiLCBtb2RlbF9uYW1lKSkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xMH0KcDEgPC0gZ2dwbG90KGRmX21ldHJpY3MsIGFlcyhmb3JjYXRzOjpmY3RfcmVvcmRlcihtb2RlbF9uYW1lLCBNQUUsIC5kZXNjID0gVFJVRSksIE1BRSkpICsKICBnZW9tX2JveHBsb3QoKSArIAogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJSZXNhbXBsZWQgbWV0cmljcyIsIHN1YnRpdGxlID0gIk1lYW4gYWJzb2x1dGUgZXJyb3IiLCB4ID0gIiAiLCB5ID0gIk1BRSIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLDAsMCwwKSwiY20iKSkKCnAyIDwtIGdncGxvdChkZl9tZXRyaWNzLCBhZXMoZm9yY2F0czo6ZmN0X3Jlb3JkZXIobW9kZWxfbmFtZSwgUnNxdWFyZWQsIC5kZXNjID0gVFJVRSksIFJzcXVhcmVkKSkgKwogIGdlb21fYm94cGxvdCgpICsgCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHN1YnRpdGxlID0gIlJzcXVhcmVkIiwgeCA9ICIgIiwgeSA9ICJSc3F1YXJlZCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwbG90Lm1hcmdpbiA9IHVuaXQoYygwLDAsMCwxKSwiY20iKSkKCnAxICsgcDIgKyBwYXRjaHdvcms6OnBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKClJhbmRvbSBmb3Jlc3Qgb3V0cGVyZm9ybXMgb3RoZXIgc2VsZWN0ZWQgbWV0aG9kcy4gTGV0J3QgdmFsaWRhdGUgaXRzIHBlcmZvcm1hbmNlIGluIG1vcmUgZGV0YWlsIGFuZCBsb29rIGludG8gbW9zdCBpbXBvcnRhbnQgZmVhdHVyZXMgaXQgcGlja2VkIHVwLgoKLS0tLS0tCgojIyMgVkFMSURBVElPTgoKYGBge3J9CnJmIDwtIHJlYWRfcmRzKCJtb2RlbHMvcmFuZ2VyLnJkcyIpCnJmJGZpbmFsTW9kZWwKYGBgCgotIFRoZSBiZXN0IHBlcmZvcm1pbmcgbW9kZWwgKH4wLjk1IFItc3F1YXJlZCkgaXMgYSByYW5kb20gZm9yZXN0IHdpdGggNTAwIHRyZWVzLCAyMiByYW5kb21seSBzZWxlY3RlZCBwcmVkaWN0b3JzLCBtaW5pbWFsIG5vZGUgc2l6ZSBvZiA1IGFuZCB2YXJpYW5jZSBiYXNlZCBzcGxpdGluZyBydWxlLiBBbHRvdWdoIGRvaW5nIGV4dGVuc2l2ZSBncmlkIHNlYXJjaCBvdmVyIDMgaHlwZXJwYXJhbWV0ZXJzIHRvb2sgbXVjaCBtb3JlIHRpbWUgdGhhbiBvdGhlciBtb2RlbHMgdGhlIHJlc3VsdCBpcyBzaWduaWZpY2FudGx5IGJldHRlci4KCkxldCdzIHZpc3VhbGl6ZSBwZXJmb3JtYW5jZSBvZiB0aGUgYmVzdCBtb2RlbDoKCmBgYHtyfQptb2RlbCA8LSByZgp5X2hhdCA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0geF92YWwpCgpwcmVkc19kZiA8LSBhcy5kYXRhLmZyYW1lKHlfaGF0KSAlPiUKICBtdXRhdGUoeSA9IHlfdmFsJHByaWNlKSAlPiUKICBtdXRhdGUocmVzaWR1YWxzID0geV9oYXQgLSB5LAogICAgICAgICBzZF9yZXNpZHVhbHMgPSBzY2FsZShyZXNpZHVhbHMpLAogICAgICAgICBhYnNfZXJyb3IgPSBhYnMocmVzaWR1YWxzKSkKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTAsIHdhcm5pbmc9RkFMU0V9CnAxIDwtIGdncGxvdCgpICsgCiAgZ2VvbV9kZW5zaXR5KGFlcyh5KSwgZGF0YSA9IHByZWRzX2RmLCBjb2xvdXIgPSAiZGFya2dyZWVuIiwgZmlsbCA9ICJkYXJrZ3JlZW4iLCBhbHBoYSA9IDAuNSkgKyAKICBnZW9tX2RlbnNpdHkoYWVzKHlfaGF0KSwgZGF0YSA9IHByZWRzX2RmLCBjb2xvdXIgPSAiZGFya2JsdWUiLCBmaWxsID0gImRhcmtibHVlIiwgYWxwaGEgPSAwLjUpICsgCiAgbGFicyh4ID0gInByaWNlIiwgdGl0bGU9IkJlc3QgbW9kZWwgLSB2YWxpZGF0aW9uIiwgc3VidGl0bGU9IkFjdHVhbHMgJiBwcmVkaWN0aW9ucyBkaXN0cmlidXRpb25zIikgKwogIHNjYWxlX3hfY29tbWEoKQoKcDIgPC0gZ2dwbG90KHByZWRzX2RmLCBhZXMoeSwgeV9oYXQpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKwogIGdlb21fc21vb3RoKCkgKyAKICBzY2FsZV94X2NvbW1hKCkgKwogIHNjYWxlX3lfY29tbWEoKSArCiAgbGFicyh4ID0gIlByaWNlIiwgeT0iUHJlZGljdGVkIHByaWNlIiwgc3VidGl0bGU9IkFjdHVhbHMgdnMgcHJlZGljdGlvbnMiKQoKcDMgPC0gZ2dwbG90KHByZWRzX2RmLCBhZXMoYWJzX2Vycm9yKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDApICsKICBzY2FsZV94X2NvbW1hKGxpbWl0cyA9IGMoMCwgNDAwMDApKSArCiAgbGFicyh4ID0gIkFic29sdXRlIGVycm9yIiwgc3VidGl0bGUgPSAiQWJzb2x1dGUgZXJyb3IgZGlzdHJpYnV0aW9uIikKCnA0IDwtIGdncGxvdChwcmVkc19kZiwgYWVzKHlfaGF0LCBzZF9yZXNpZHVhbHMpKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgY29sb3VyPSJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArIAogIGxhYnMoeCA9ICJQcmVkaWN0ZWQgcHJpY2UiLCB5ID0gIlN0YW5kYXJpemVkIHJlc2lkdWFscyIsIHN1YnRpdGxlID0gIlJlc2lkdWFscyIpCgpwMSArIHAyICsgcDMgKyBwNCArIHBsb3RfbGF5b3V0KG5yb3cgPSAyLCBuY29sID0gMikKYGBgCgoKYGBge3J9CmNhdChwYXN0ZTAoIk1lZGlhbiBwcmljZSBpbiB0aGUgdmFsaWRhdGlvbiBzZXQ6ICIsIG1lZGlhbih5X3ZhbCRwcmljZSksCiAgICAgICAgICAiXG5NZWFuIGFic29sdXRlIGVycm9yOiAiLCByb3VuZChtZWFuKG1vZGVsJHJlc2FtcGxlJE1BRSksIDIpKSkKYGBgCjxicj4KCgojIyMjICoqRmVhdHVyZXMgSW1wb3J0YW5jZToqKgoKCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTd9CmZlYXR1cmVzX2ltcG9ydGFuY2UgPC0gdmFySW1wKG1vZGVsKSRpbXBvcnRhbmNlICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIHNlbGVjdChmZWF0dXJlID0gcm93bmFtZSwgZ2luaV9pbXBvcnRhbmNlID0gT3ZlcmFsbCkgJT4lCiAgYXJyYW5nZShkZXNjKGdpbmlfaW1wb3J0YW5jZSkpICU+JQogIGhlYWQoMTUpCgpnZ3Bsb3QoZmVhdHVyZXNfaW1wb3J0YW5jZSwgYWVzKGZvcmNhdHM6OmZjdF9yZW9yZGVyKGZlYXR1cmUsIGdpbmlfaW1wb3J0YW5jZSksIGdpbmlfaW1wb3J0YW5jZSkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMikgKyAKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeCA9ICIgIiwgeSA9ICJHaW5pIGltcG9ydGFuY2UgKE1lYW4gRGVjcmVhc2UgaW4gSW1wdXJpdHkpIiwgc3VidGl0bGUgPSAiVG9wIGZlYXR1cmVzIHVzZWQgYnkgcmFuZG9tIGZvcmVzdCBmb3Igc3BsaXRzIikKYGBgCgotIFByZWRpY3Rpb25zIHdlcmUgZ2VuZXJhdGVkIG1haW5seSBieSBsb29raW5nIGF0IHRoZSBsb2NhdGlvbiAoTGF0L0xuZyBvciBkaXN0cmljdCkgYW5kIHRpbWUgdGhlIGFwYXJ0bWVudCB3YXMgcG9zdGVkIGZvciBzYWxlLiBTdWNoIGNoYXJhY3RlcmlzdGljcyBhcyBzaXplIG9mIHRoZSBhcGFydG1lbnQsIGNvbnN0cnVjdGlvbiB0aW1lIG9yIHJlbm92YXRpb24gY29uZGl0aW9uIGRpZG4ndCBtYWtlIGl0IGludG8gdG9wIDEwLiAKCjxicj4KCioqSW5kaXZpZHVhbCBleGFtcGxlcyoqCgpBdCB0aGUgZW5kIGxldCdzIGxvb2sgYXQgZmV3IGluZGl2aWR1YWwgZXhhbXBsZXMgb2YgYXBhcnRtZW50cyBhbmQgdHJ5IHRvIGV4cGxhaW4gd2hpY2ggdmFyaWFibGVzIHdlcmUgdGhlIG1vc3QgaW1wb3J0YW50IGZvciB0aGUgbW9kZWwgdG8gbWFrZSBwcmljZSBlc3RpbWF0aW9uOgoKYGBge3Igd2FybmluZz1GQUxTRX0KbGlicmFyeShsaW1lKQpzZXQuc2VlZCgyMykKc2VsZWN0ZWRfYXBhcnRtZW50cyA8LSBzYW1wbGVfbih4X3ZhbCwgNikKZXhwbGFpbmVyIDwtIGxpbWUoc2VsZWN0ZWRfYXBhcnRtZW50cywgbW9kZWwgPSBtb2RlbCkKZXhwbGFuYXRpb24gPC0gZXhwbGFpbihzZWxlY3RlZF9hcGFydG1lbnRzLCBleHBsYWluZXIsIG5fZmVhdHVyZXMgPSA3KQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRX0KcGxvdF9mZWF0dXJlcyhleHBsYW5hdGlvbikKYGBgCgotLS0tLS0KCjxjZW50ZXI+VEhFIEVORDwvY2VudGVyPgoK