Introduction

Deep Learning regression using Tensorflow for house prices prediction.

House Prices: Advanced Regression Techniques

link for the kaggle competition: https://www.kaggle.com/c/house-prices-advanced-regression-techniques

datasets: https://www.kaggle.com/c/house-prices-advanced-regression-techniques/data

repository with the code of this notebook and the tensorflow model: https://github.com/dimitreOliveira/HousePrices

Overview

Ask a home buyer to describe their dream house, and they probably won’t begin with the height of the basement ceiling or the proximity to an east-west railroad. But this playground competition’s dataset proves that much more influences price negotiations than the number of bedrooms or a white-picket fence.

With 79 explanatory variables describing (almost) every aspect of residential homes in Ames, Iowa, this competition challenges you to predict the final price of each home.

Acknowledgments

The Ames Housing dataset was compiled by Dean De Cock for use in data science education. It’s an incredible alternative for data scientists looking for a modernized and expanded version of the often cited Boston Housing dataset.

Exploratory Data Analysis

Null occurrence

First let’s take a look at how many null values we have on the train set.

As we can see we have lots of null values among all columns, to make our work easier we’ll take them out for now and latter decide how to deal with them.

Label analysis

Now we have our train set with no null values, so first let’s take a look at how is our label feature (“SalePrice”) distribution with a histogram and see the feature summary.

   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  34900  129975  163000  180921  214000  755000 

We can see some interesting properties, our label has a peak around 160000, then it starts to decline and forms a long tail ending at 75500, as our summary shows.

Next we will apply a logarithmic transformation to make our distribution looks more friendly, note that now it will look more normalized, and will lose it’s right side long tail.

   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  10.46   11.78   12.00   12.02   12.27   13.53 

Numerical features correlation

After this let’s start taking a look at how the remaining 26 numeric features correlate with the target “SalePrice” with a correlation matrix.

Categorical features correlation

And to our remaining 20 categorical features lets take a look at some box plots, to feel how our data behaves with “SalePrice”.

As we can see we have lots of features that have really low correlation (numerical) or low variance (categorical) with with “SalePrice”, features like this can disturb the training of our model, maybe latter we can feature engineer them to have more useful features, but for now we’ll set them aside to have a simpler model.

Data pre-processing

Now that we have more information about the features of our dataset, we can filter out all the unwanted features and work with a cleaner dataset.

Features behavior

After filtering out the unwanted features let’s take a look how our remaining features behaves with the target features and others with some scatter plot matrices.

Data inference

Now we can go back to our features with null values, the ones with high amount of missing data (more than 15%) we will drop, as the effort of inferring values would probably be too much and would still have chances of adding bias to the training, but the remaining we will try to infer the missing values.

As the remaining missing features have low missing count, we will use a simple technique to infer data, we will replace the missing values with the median or mode of the feature.

Inferred data correlation

Now let’s take a look at how the date we just created behaves with the target feature the same way we did before.

First the numerical features.

Then the categorical features.

As you can see we still have a number of irrelevant features, so we will also remove them.

Then we will do the same process to our test set.

Export the data

After all the data cleaning and processing we can write the resulting data frame into two csv files (train and test) and use it on our model.

reminder the link with the tensorflow code is at: https://github.com/dimitreOliveira/HousePrices

LS0tDQp0aXRsZTogIkhvdXNlIFByaWNlcyAtIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IA0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGZpZzpoZWlnaHQ6IDQNCiAgICBmaWdfd2lkdGg6IDYNCiAgICB0aGVtZTogcmVhZGFibGUNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIGVkaXRvcl9vcHRpb25zOg0KICAgIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KDQohW10oaHR0cHM6Ly9rYWdnbGUyLmJsb2IuY29yZS53aW5kb3dzLm5ldC9jb21wZXRpdGlvbnMva2FnZ2xlLzU0MDcvbWVkaWEvaG91c2VzYmFubmVyLnBuZykNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KIyMgRGVlcCBMZWFybmluZyByZWdyZXNzaW9uIHVzaW5nIFRlbnNvcmZsb3cgZm9yIGhvdXNlIHByaWNlcyBwcmVkaWN0aW9uLg0KDQojIyMgSG91c2UgUHJpY2VzOiBBZHZhbmNlZCBSZWdyZXNzaW9uIFRlY2huaXF1ZXMNCg0KbGluayBmb3IgdGhlIGthZ2dsZSBjb21wZXRpdGlvbjogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9jL2hvdXNlLXByaWNlcy1hZHZhbmNlZC1yZWdyZXNzaW9uLXRlY2huaXF1ZXMNCg0KZGF0YXNldHM6IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYy9ob3VzZS1wcmljZXMtYWR2YW5jZWQtcmVncmVzc2lvbi10ZWNobmlxdWVzL2RhdGENCg0KcmVwb3NpdG9yeSB3aXRoIHRoZSBjb2RlIG9mIHRoaXMgbm90ZWJvb2sgYW5kIHRoZSB0ZW5zb3JmbG93IG1vZGVsOiBodHRwczovL2dpdGh1Yi5jb20vZGltaXRyZU9saXZlaXJhL0hvdXNlUHJpY2VzDQoNCiMjIyBPdmVydmlldw0KQXNrIGEgaG9tZSBidXllciB0byBkZXNjcmliZSB0aGVpciBkcmVhbSBob3VzZSwgYW5kIHRoZXkgcHJvYmFibHkgd29uJ3QgYmVnaW4gd2l0aCB0aGUgaGVpZ2h0IG9mIHRoZSBiYXNlbWVudCBjZWlsaW5nIG9yIHRoZSBwcm94aW1pdHkgdG8gYW4gZWFzdC13ZXN0IHJhaWxyb2FkLiBCdXQgdGhpcyBwbGF5Z3JvdW5kIGNvbXBldGl0aW9uJ3MgZGF0YXNldCBwcm92ZXMgdGhhdCBtdWNoIG1vcmUgaW5mbHVlbmNlcyBwcmljZSBuZWdvdGlhdGlvbnMgdGhhbiB0aGUgbnVtYmVyIG9mIGJlZHJvb21zIG9yIGEgd2hpdGUtcGlja2V0IGZlbmNlLg0KDQpXaXRoIDc5IGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBkZXNjcmliaW5nIChhbG1vc3QpIGV2ZXJ5IGFzcGVjdCBvZiByZXNpZGVudGlhbCBob21lcyBpbiBBbWVzLCBJb3dhLCB0aGlzIGNvbXBldGl0aW9uIGNoYWxsZW5nZXMgeW91IHRvIHByZWRpY3QgdGhlIGZpbmFsIHByaWNlIG9mIGVhY2ggaG9tZS4NCg0KIyMjIEFja25vd2xlZGdtZW50cw0KVGhlIEFtZXMgSG91c2luZyBkYXRhc2V0IHdhcyBjb21waWxlZCBieSBEZWFuIERlIENvY2sgZm9yIHVzZSBpbiBkYXRhIHNjaWVuY2UgZWR1Y2F0aW9uLiBJdCdzIGFuIGluY3JlZGlibGUgYWx0ZXJuYXRpdmUgZm9yIGRhdGEgc2NpZW50aXN0cyBsb29raW5nIGZvciBhIG1vZGVybml6ZWQgYW5kIGV4cGFuZGVkIHZlcnNpb24gb2YgdGhlIG9mdGVuIGNpdGVkIEJvc3RvbiBIb3VzaW5nIGRhdGFzZXQuIA0KDQojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiMgc2V0IHVwDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoZ2dwbG90MikNCm9wdGlvbnMoc2NpcGVuID0gNCkNCmBgYA0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KIyBMb2FkaW5nIGRhdGENCnRyYWluIDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9kaW1pdC9EZXNrdG9wL1Byb2pldG9zL0hvdXNlIFByaWNlcy9kYXRhL3RyYWluLmNzdiIsIGVuY29kaW5nPSJVVEYtOCIpDQp0ZXN0IDwtIHJlYWQuY3N2KCdDOi9Vc2Vycy9kaW1pdC9EZXNrdG9wL1Byb2pldG9zL0hvdXNlIFByaWNlcy9kYXRhL3Rlc3QuY3N2JywgZW5jb2Rpbmc9IlVURi04IikNCnNhbXBsZV9zdWJtaXNzaW9uIDwtIHJlYWQuY3N2KCdDOi9Vc2Vycy9kaW1pdC9EZXNrdG9wL1Byb2pldG9zL0hvdXNlIFByaWNlcy9kYXRhL3NhbXBsZV9zdWJtaXNzaW9uLmNzdicsIGVuY29kaW5nPSJVVEYtOCIpDQpgYGANCg0KIyMjIE51bGwgb2NjdXJyZW5jZQ0KDQpGaXJzdCBsZXQncyB0YWtlIGEgbG9vayBhdCBob3cgbWFueSBudWxsIHZhbHVlcyB3ZSBoYXZlIG9uIHRoZSB0cmFpbiBzZXQuDQpgYGB7ciBlY2hvPUZBTFNFfQ0KbWlzc2luZ192YWx1ZXMgPC0gc2FwcGx5KHRyYWluLCBmdW5jdGlvbih4KSBzdW0oaXMubmEoeCkpKQ0KbnVsbF9jb3VudCA8LSBkYXRhLmZyYW1lKENvdW50ID0gbWlzc2luZ192YWx1ZXMsIFByb3BvcnRpb24gPSBtaXNzaW5nX3ZhbHVlcy9ucm93KHRyYWluKSkNCm51bGxfY291bnRfZ3RlWmVybyA8LSBudWxsX2NvdW50W251bGxfY291bnQkQ291bnQgPiAwLCBdDQpudWxsX2NvdW50X2d0ZVplcm9bb3JkZXIoLW51bGxfY291bnRfZ3RlWmVybyRDb3VudCksXQ0KDQpgYGANCg0KQXMgd2UgY2FuIHNlZSB3ZSBoYXZlIGxvdHMgb2YgbnVsbCB2YWx1ZXMgYW1vbmcgYWxsIGNvbHVtbnMsIHRvIG1ha2Ugb3VyIHdvcmsgZWFzaWVyIHdlJ2xsIHRha2UgdGhlbSBvdXQgZm9yIG5vdyBhbmQgbGF0dGVyIGRlY2lkZSBob3cgdG8gZGVhbCB3aXRoIHRoZW0uDQoNCmBgYHtyIGVjaG89RkFMU0V9DQp0cmFpbl9ub25fbnVsbCA8LSBzZWxlY3QodHJhaW4sIC1Mb3RGcm9udGFnZSwgLUFsbGV5LCAtTWFzVm5yVHlwZSwgLU1hc1ZuckFyZWEsIC1GaXJlcGxhY2VRdSwgLUdhcmFnZVR5cGUsIC1HYXJhZ2VZckJsdCwgLUdhcmFnZUZpbmlzaCwgLUdhcmFnZVF1YWwsIC1HYXJhZ2VDb25kLCAtQnNtdFF1YWwsIC1Cc210Q29uZCwgLUJzbXRFeHBvc3VyZSwgLUJzbXRGaW5UeXBlMSwgLVBvb2xRQywgLUZlbmNlLCAtTWlzY0ZlYXR1cmUsIC1Cc210RmluVHlwZTEsIC1Cc210RmluVHlwZTIsIC1FbGVjdHJpY2FsLCAtQnNtdEZpblNGMiwgLUJzbXRVbmZTRiwgLVRvdGFsQnNtdFNGLCAtQnNtdEZ1bGxCYXRoLCAtQnNtdEhhbGZCYXRoLCAtR2FyYWdlQ2FycywgLUdhcmFnZUFyZWEsIC1NU1pvbmluZywgLVV0aWxpdGllcywgLUV4dGVyaW9yMXN0LCAtRXh0ZXJpb3IybmQsICAtQnNtdEZpblNGMSwgLUtpdGNoZW5RdWFsLCAtRnVuY3Rpb25hbCwgLVNhbGVUeXBlKQ0KYGBgDQoNCiMjIyBMYWJlbCBhbmFseXNpcw0KDQpOb3cgd2UgaGF2ZSBvdXIgdHJhaW4gc2V0IHdpdGggbm8gbnVsbCB2YWx1ZXMsIHNvIGZpcnN0IGxldCdzIHRha2UgYSBsb29rIGF0IGhvdyBpcyBvdXIgbGFiZWwgZmVhdHVyZSAoIlNhbGVQcmljZSIpIGRpc3RyaWJ1dGlvbiB3aXRoIGEgaGlzdG9ncmFtIGFuZCBzZWUgdGhlIGZlYXR1cmUgc3VtbWFyeS4NCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpnZ3Bsb3QoZGF0YT10cmFpbl9ub25fbnVsbCwgYWVzKHRyYWluX25vbl9udWxsJFNhbGVQcmljZSkpICsgDQogIGdlb21faGlzdG9ncmFtKGNvbD0icmVkIiwgYWVzKGZpbGw9Li5jb3VudC4uKSkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50KCJDb3VudCIsIGxvdz0id2hpdGUiLCBoaWdoPSJyZWQiKSArIA0KICBsYWJzKHRpdGxlID0gIlNhbGUgcHJpY2UgaGlzdG9ncmFtIiwgeCA9ICJTYWxlIHByaWNlIiwgeSA9ICJDb3VudCIpDQoNCnN1bW1hcnkodHJhaW5fbm9uX251bGwkU2FsZVByaWNlKQ0KYGBgDQoNCldlIGNhbiBzZWUgc29tZSBpbnRlcmVzdGluZyBwcm9wZXJ0aWVzLCBvdXIgbGFiZWwgaGFzIGEgcGVhayBhcm91bmQgMTYwMDAwLCB0aGVuIGl0IHN0YXJ0cyB0byBkZWNsaW5lIGFuZCBmb3JtcyBhIGxvbmcgdGFpbCBlbmRpbmcgYXQgNzU1MDAsIGFzIG91ciBzdW1tYXJ5IHNob3dzLg0KDQpOZXh0IHdlIHdpbGwgYXBwbHkgYSBsb2dhcml0aG1pYyB0cmFuc2Zvcm1hdGlvbiB0byBtYWtlIG91ciBkaXN0cmlidXRpb24gbG9va3MgbW9yZSBmcmllbmRseSwgbm90ZSB0aGF0IG5vdyBpdCB3aWxsIGxvb2sgbW9yZSBub3JtYWxpemVkLCBhbmQgd2lsbCBsb3NlIGl0J3MgcmlnaHQgc2lkZSBsb25nIHRhaWwuDQpgYGB7ciBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KdHJhaW5fbm9uX251bGwkU2FsZVByaWNlIDwtIGxvZzFwKHRyYWluX25vbl9udWxsJFNhbGVQcmljZSkNCg0KZ2dwbG90KGRhdGE9dHJhaW5fbm9uX251bGwsIGFlcyh0cmFpbl9ub25fbnVsbCRTYWxlUHJpY2UpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShjb2w9InJlZCIsIGFlcyhmaWxsPS4uY291bnQuLikpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudCgiQ291bnQiLCBsb3c9IndoaXRlIiwgaGlnaD0icmVkIikgKyANCiAgbGFicyh0aXRsZSA9ICJTYWxlIHByaWNlIGhpc3RvZ3JhbSIsIHggPSAiU2FsZSBwcmljZSIsIHkgPSAiQ291bnQiKQ0KDQpzdW1tYXJ5KHRyYWluX25vbl9udWxsJFNhbGVQcmljZSkNCmBgYA0KDQojIyMgTnVtZXJpY2FsIGZlYXR1cmVzIGNvcnJlbGF0aW9uDQoNCkFmdGVyIHRoaXMgbGV0J3Mgc3RhcnQgdGFraW5nIGEgbG9vayBhdCBob3cgdGhlIHJlbWFpbmluZyAyNiBudW1lcmljIGZlYXR1cmVzIGNvcnJlbGF0ZSB3aXRoIHRoZSB0YXJnZXQgIlNhbGVQcmljZSIgd2l0aCBhIGNvcnJlbGF0aW9uIG1hdHJpeC4NCg0KYGBge3IgZWNobz1GQUxTRX0NCiMgU2VsZWN0aW5nIG9ubHkgbnVtZXJpYyBmZWF0dXJlcw0KdHJhaW5fbnVtZXJpYyA8LSBzZWxlY3QodHJhaW5fbm9uX251bGwsIC1Ib3VzZVN0eWxlLCAtUm9vZk1hdGwsIC1IZWF0aW5nLCAtQ29uZGl0aW9uMiwgLVJvb2ZTdHlsZSwgLUV4dGVyUXVhbCwgLUJsZGdUeXBlLCAtRXh0ZXJDb25kLCAtRm91bmRhdGlvbiwgLUhlYXRpbmdRQywgLUNlbnRyYWxBaXIsIC1Db25kaXRpb24xLCAtTmVpZ2hib3Job29kLCAtTGFuZFNsb3BlLCAtTG90Q29uZmlnLCAtTGFuZENvbnRvdXIsIC1Mb3RTaGFwZSwgLVN0cmVldCwgLVBhdmVkRHJpdmUsIC1TYWxlQ29uZGl0aW9uKQ0KYGBgDQoNCmBgYHtyIGVjaG89RkFMU0V9DQojIFN0b3JlIHRoZSBvdmVyYWxsIGNvcnJlbGF0aW9uIGluICdjb3JyZWxhdGlvbnMnDQpjb3JyZWxhdGlvbnMgPC0gY29yKHRyYWluX251bWVyaWNbLDE6MjddKQ0KDQojIFBsb3QgdGhlIGNvcnJlbGF0aW9uIHBsb3Qgd2l0aCAnY29ycmVsYXRpb25zJw0KY29ycnBsb3QoY29ycmVsYXRpb25zLCBtZXRob2Q9InNxdWFyZSIsIHR5cGUgPSAidXBwZXIiKQ0KYGBgDQoNCiMjIyBDYXRlZ29yaWNhbCBmZWF0dXJlcyBjb3JyZWxhdGlvbg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KIyBTZWxlY3Rpbmcgb25seSBjYXRlZ29yaWNhbCBmZWF0dXJlcyBhbmQgdGhlIGxhYmVsDQp0cmFpbl9jYXRlZ29yaWMgPC0gc2VsZWN0KHRyYWluX25vbl9udWxsLCBIb3VzZVN0eWxlLCBSb29mTWF0bCwgSGVhdGluZywgQ29uZGl0aW9uMiwgUm9vZlN0eWxlLCBFeHRlclF1YWwsIEJsZGdUeXBlLCAgRXh0ZXJDb25kLCBGb3VuZGF0aW9uLCBIZWF0aW5nUUMsIENlbnRyYWxBaXIsIENvbmRpdGlvbjEsIE5laWdoYm9yaG9vZCwgTGFuZFNsb3BlLCBMb3RDb25maWcsIExhbmRDb250b3VyLCBMb3RTaGFwZSwgU3RyZWV0LCBQYXZlZERyaXZlLCBTYWxlQ29uZGl0aW9uLCBTYWxlUHJpY2UpDQpgYGANCg0KQW5kIHRvIG91ciByZW1haW5pbmcgMjAgY2F0ZWdvcmljYWwgZmVhdHVyZXMgbGV0cyB0YWtlIGEgbG9vayBhdCBzb21lIGJveCBwbG90cywgdG8gZmVlbCBob3cgb3VyIGRhdGEgYmVoYXZlcyB3aXRoICJTYWxlUHJpY2UiLg0KYGBge3IgZWNobz1GQUxTRX0NCmdncGxvdChkYXRhPXRyYWluX2NhdGVnb3JpYywgYWVzKHk9IFNhbGVQcmljZSwgeD1Ib3VzZVN0eWxlLCBmaWxsPUhvdXNlU3R5bGUpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIEhvdXNlU3R5bGUiKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIkhvdXNlU3R5bGUiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9Um9vZk1hdGwsIGZpbGw9Um9vZk1hdGwgKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBSb29mTWF0bCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiUm9vZk1hdGwiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9SGVhdGluZywgZmlsbD1IZWF0aW5nICkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgSGVhdGluZyIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiSGVhdGluZyIpDQoNCmdncGxvdChkYXRhPXRyYWluX2NhdGVnb3JpYywgYWVzKHk9IFNhbGVQcmljZSwgeD1Db25kaXRpb24yLCBmaWxsPUNvbmRpdGlvbjIpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIENvbmRpdGlvbjIiKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIkNvbmRpdGlvbjIiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9Um9vZlN0eWxlLCBmaWxsPVJvb2ZTdHlsZSkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgUm9vZlN0eWxlIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJSb29mU3R5bGUiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9RXh0ZXJRdWFsLCBmaWxsPUV4dGVyUXVhbCApICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIEV4dGVyUXVhbCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiRXh0ZXJRdWFsIikNCg0KZ2dwbG90KGRhdGE9dHJhaW5fY2F0ZWdvcmljLCBhZXMoeT0gU2FsZVByaWNlLCB4PUJsZGdUeXBlLCBmaWxsPUJsZGdUeXBlKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBCbGRnVHlwZSIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiQmxkZ1R5cGUiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9RXh0ZXJDb25kLCBmaWxsPUV4dGVyQ29uZCkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgRXh0ZXJDb25kIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJFeHRlckNvbmQiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9Rm91bmRhdGlvbiwgZmlsbD1Gb3VuZGF0aW9uKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBGb3VuZGF0aW9uIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJGb3VuZGF0aW9uIikNCg0KZ2dwbG90KGRhdGE9dHJhaW5fY2F0ZWdvcmljLCBhZXMoeT0gU2FsZVByaWNlLCB4PUhlYXRpbmdRQywgZmlsbD1IZWF0aW5nUUMpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIEhlYXRpbmdRQyIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiSGVhdGluZ1FDIikNCg0KZ2dwbG90KGRhdGE9dHJhaW5fY2F0ZWdvcmljLCBhZXMoeT0gU2FsZVByaWNlLCB4PUNlbnRyYWxBaXIsIGZpbGw9Q2VudHJhbEFpcikgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgQ2VudHJhbEFpciIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiQ2VudHJhbEFpciIpDQoNCmdncGxvdChkYXRhPXRyYWluX2NhdGVnb3JpYywgYWVzKHk9IFNhbGVQcmljZSwgeD1Db25kaXRpb24xLCBmaWxsPUNvbmRpdGlvbjEpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIENvbmRpdGlvbjEiKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIkNvbmRpdGlvbjEiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9TmVpZ2hib3Job29kLCBmaWxsPU5laWdoYm9yaG9vZCkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgTmVpZ2hib3Job29kIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJOZWlnaGJvcmhvb2QiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9TGFuZFNsb3BlLCBmaWxsPUxhbmRTbG9wZSkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgTGFuZFNsb3BlIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJMYW5kU2xvcGUiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9TG90Q29uZmlnLCBmaWxsPUxvdENvbmZpZykgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgTG90Q29uZmlnIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJMb3RDb25maWciKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl9jYXRlZ29yaWMsIGFlcyh5PSBTYWxlUHJpY2UsIHg9TGFuZENvbnRvdXIsIGZpbGw9TGFuZENvbnRvdXIpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIExhbmRDb250b3VyIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJMYW5kQ29udG91ciIpDQoNCmdncGxvdChkYXRhPXRyYWluX2NhdGVnb3JpYywgYWVzKHk9IFNhbGVQcmljZSwgeD1Mb3RTaGFwZSwgZmlsbD1Mb3RTaGFwZSkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgTG90U2hhcGUiKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIkxvdFNoYXBlIikNCg0KZ2dwbG90KGRhdGE9dHJhaW5fY2F0ZWdvcmljLCBhZXMoeT0gU2FsZVByaWNlLCB4PVN0cmVldCwgZmlsbD1TdHJlZXQpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIFN0cmVldCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiU3RyZWV0IikNCg0KZ2dwbG90KGRhdGE9dHJhaW5fY2F0ZWdvcmljLCBhZXMoeT0gU2FsZVByaWNlLCB4PVBhdmVkRHJpdmUsIGZpbGw9UGF2ZWREcml2ZSkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgUGF2ZWREcml2ZSIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiUGF2ZWREcml2ZSIpDQoNCmdncGxvdChkYXRhPXRyYWluX2NhdGVnb3JpYywgYWVzKHk9IFNhbGVQcmljZSwgeD1TYWxlQ29uZGl0aW9uLCBmaWxsPVNhbGVDb25kaXRpb24pICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIFNhbGVDb25kaXRpb24iKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIlNhbGVDb25kaXRpb24iKQ0KYGBgDQoNCkFzIHdlIGNhbiBzZWUgd2UgaGF2ZSBsb3RzIG9mIGZlYXR1cmVzIHRoYXQgaGF2ZSByZWFsbHkgbG93IGNvcnJlbGF0aW9uIChudW1lcmljYWwpIG9yIGxvdyB2YXJpYW5jZSAoY2F0ZWdvcmljYWwpIHdpdGggd2l0aCAiU2FsZVByaWNlIiwgZmVhdHVyZXMgbGlrZSB0aGlzIGNhbiBkaXN0dXJiIHRoZSB0cmFpbmluZyBvZiBvdXIgbW9kZWwsIG1heWJlIGxhdHRlciB3ZSBjYW4gZmVhdHVyZSBlbmdpbmVlciB0aGVtIHRvIGhhdmUgbW9yZSB1c2VmdWwgZmVhdHVyZXMsIGJ1dCBmb3Igbm93IHdlJ2xsIHNldCB0aGVtIGFzaWRlIHRvIGhhdmUgYSBzaW1wbGVyIG1vZGVsLg0KDQojIERhdGEgcHJlLXByb2Nlc3NpbmcNCg0KTm93IHRoYXQgd2UgaGF2ZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBmZWF0dXJlcyBvZiBvdXIgZGF0YXNldCwgd2UgY2FuIGZpbHRlciBvdXQgYWxsIHRoZSB1bndhbnRlZCBmZWF0dXJlcyBhbmQgd29yayB3aXRoIGEgY2xlYW5lciBkYXRhc2V0Lg0KDQpgYGB7ciBlY2hvPUZBTFNFfQ0KIyByZW1vdmluIGxvdyBjb3JyZWxhdGlvbiBudW1lcmljYWwNCnRyYWluX2NsZWFuIDwtIHNlbGVjdCh0cmFpbl9ub25fbnVsbCwgLU1TU3ViQ2xhc3MsIC1PdmVyYWxsQ29uZCwgLUxvd1F1YWxGaW5TRiwgLUJlZHJvb21BYnZHciwgLUtpdGNoZW5BYnZHciwgLUVuY2xvc2VkUG9yY2gsIC1YM1NzblBvcmNoLCAtU2NyZWVuUG9yY2gsIC1Qb29sQXJlYSwgLU1pc2NWYWwsIC1Nb1NvbGQsIC1ZclNvbGQpDQojIHJlbW92aW4gbG93IHZhcmlhbmNlIGNhdGVnb3JpY2FsDQp0cmFpbl9jbGVhbiA8LSBzZWxlY3QodHJhaW5fY2xlYW4sIC1Sb29mU3R5bGUsIC1CbGRnVHlwZSwgLUxhbmRTbG9wZSwgLUxvdENvbmZpZywgLUxhbmRDb250b3VyLCAtSGVhdGluZywgLUV4dGVyQ29uZCwgLVJvb2ZNYXRsLCAtQ29uZGl0aW9uMSwgLUNvbmRpdGlvbjIsIC1TdHJlZXQpDQpgYGANCg0KIyMjIEZlYXR1cmVzIGJlaGF2aW9yDQoNCkFmdGVyIGZpbHRlcmluZyBvdXQgdGhlIHVud2FudGVkIGZlYXR1cmVzIGxldCdzIHRha2UgYSBsb29rIGhvdyBvdXIgcmVtYWluaW5nIGZlYXR1cmVzIGJlaGF2ZXMgd2l0aCB0aGUgdGFyZ2V0IGZlYXR1cmVzIGFuZCBvdGhlcnMgd2l0aCBzb21lIHNjYXR0ZXIgcGxvdCBtYXRyaWNlcy4NCmBgYHtyIGVjaG89RkFMU0V9DQpwYWlycyh+IExvdEFyZWEgKyBOZWlnaGJvcmhvb2QgKyBPdmVyYWxsUXVhbCArIE9wZW5Qb3JjaFNGICsgU2FsZVByaWNlLCBkYXRhID0gdHJhaW5fY2xlYW4pDQpwYWlycyh+IFllYXJCdWlsdCArIFllYXJSZW1vZEFkZCArIEV4dGVyUXVhbCArIEZvdW5kYXRpb24gKyBTYWxlUHJpY2UsIGRhdGEgPSB0cmFpbl9jbGVhbikNCnBhaXJzKH4gSGVhdGluZ1FDICsgWDFzdEZsclNGICsgWDJuZEZsclNGICsgRnVsbEJhdGggKyBTYWxlUHJpY2UsIGRhdGEgPSB0cmFpbl9jbGVhbikNCnBhaXJzKH4gSGFsZkJhdGggKyBXb29kRGVja1NGICsgUGF2ZWREcml2ZSArIEZpcmVwbGFjZXMgKyBTYWxlUHJpY2UsIGRhdGEgPSB0cmFpbl9jbGVhbikNCnBhaXJzKH4gVG90Um1zQWJ2R3JkICsgR3JMaXZBcmVhICsgU2FsZUNvbmRpdGlvbiArIFNhbGVQcmljZSwgZGF0YSA9IHRyYWluX2NsZWFuKQ0KYGBgDQoNCiMjIyBEYXRhIGluZmVyZW5jZQ0KDQpOb3cgd2UgY2FuIGdvIGJhY2sgdG8gb3VyIGZlYXR1cmVzIHdpdGggbnVsbCB2YWx1ZXMsIHRoZSBvbmVzIHdpdGggaGlnaCBhbW91bnQgb2YgbWlzc2luZyBkYXRhIChtb3JlIHRoYW4gMTUlKSB3ZSB3aWxsIGRyb3AsIGFzIHRoZSBlZmZvcnQgb2YgaW5mZXJyaW5nIHZhbHVlcyB3b3VsZCBwcm9iYWJseSBiZSB0b28gbXVjaCBhbmQgd291bGQgc3RpbGwgaGF2ZSBjaGFuY2VzIG9mIGFkZGluZyBiaWFzIHRvIHRoZSB0cmFpbmluZywgYnV0IHRoZSByZW1haW5pbmcgd2Ugd2lsbCB0cnkgdG8gaW5mZXIgdGhlIG1pc3NpbmcgdmFsdWVzLg0KDQpBcyB0aGUgcmVtYWluaW5nIG1pc3NpbmcgZmVhdHVyZXMgaGF2ZSBsb3cgbWlzc2luZyBjb3VudCwgd2Ugd2lsbCB1c2UgYSBzaW1wbGUgdGVjaG5pcXVlIHRvIGluZmVyIGRhdGEsIHdlIHdpbGwgcmVwbGFjZSB0aGUgbWlzc2luZyB2YWx1ZXMgd2l0aCB0aGUgbWVkaWFuIG9yIG1vZGUgb2YgdGhlIGZlYXR1cmUuDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KdHJhaW5fdG9fcHJvY2Vzc19udW1lcmljYWwgPC0gc2VsZWN0KHRyYWluLCBCc210RmluU0YyLCBCc210VW5mU0YsIFRvdGFsQnNtdFNGLCBCc210RnVsbEJhdGgsIEJzbXRIYWxmQmF0aCwgR2FyYWdlQ2FycywgR2FyYWdlQXJlYSwgQnNtdEZpblNGMSkNCg0KdHJhaW5fdG9fcHJvY2Vzc19jYXRlZ29yaWNhbCA8LSBzZWxlY3QodHJhaW4sIE1hc1ZuckFyZWEsIEdhcmFnZVlyQmx0LCBNYXNWbnJUeXBlLCBHYXJhZ2VUeXBlLCBHYXJhZ2VGaW5pc2gsIEdhcmFnZVF1YWwsIEdhcmFnZUNvbmQsIEJzbXRRdWFsLCBCc210Q29uZCwgQnNtdEV4cG9zdXJlLCBCc210RmluVHlwZTEsIEJzbXRGaW5UeXBlMSwgQnNtdEZpblR5cGUyLCBFbGVjdHJpY2FsLCBNU1pvbmluZywgVXRpbGl0aWVzLCBFeHRlcmlvcjFzdCwgRXh0ZXJpb3IybmQsIEtpdGNoZW5RdWFsLCBGdW5jdGlvbmFsLCBGZW5jZSwgQWxsZXksIFBvb2xRQywgTWlzY0ZlYXR1cmUsIFNhbGVUeXBlKQ0KYGBgDQoNCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpNb2RlIDwtIGZ1bmN0aW9uKHgpIHsNCiAgdXggPC0gdW5pcXVlKHgpDQogIHV4W3doaWNoLm1heCh0YWJ1bGF0ZShtYXRjaCh4LCB1eCkpKV0NCn0NCg0KZm9yKGkgaW4gMTpuY29sKHRyYWluX3RvX3Byb2Nlc3NfbnVtZXJpY2FsKSl7DQogIHRyYWluX3RvX3Byb2Nlc3NfbnVtZXJpY2FsW2lzLm5hKHRyYWluX3RvX3Byb2Nlc3NfbnVtZXJpY2FsWyxpXSksIGldIDwtIG1lYW4odHJhaW5fdG9fcHJvY2Vzc19udW1lcmljYWxbLGldLCBuYS5ybSA9IFRSVUUpDQp9DQoNCmZvcihpIGluIDE6bmNvbCh0cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsKSl7DQogIHRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWxbaXMubmEodHJhaW5fdG9fcHJvY2Vzc19jYXRlZ29yaWNhbFssaV0pLCBpXSA8LSBNb2RlKHRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWxbLGldKQ0KfQ0KYGBgDQoNCiMjIyBJbmZlcnJlZCBkYXRhIGNvcnJlbGF0aW9uDQoNCk5vdyBsZXQncyB0YWtlIGEgbG9vayBhdCBob3cgdGhlIGRhdGUgd2UganVzdCBjcmVhdGVkIGJlaGF2ZXMgd2l0aCB0aGUgdGFyZ2V0IGZlYXR1cmUgdGhlIHNhbWUgd2F5IHdlIGRpZCBiZWZvcmUuDQoNCkZpcnN0IHRoZSBudW1lcmljYWwgZmVhdHVyZXMuDQpgYGB7ciBlY2hvPUZBTFNFfQ0KIyBhZGQgdGFyZ2V0IGZlYXR1cmUgdG8gdGhlIHNldA0KdHJhaW5fdG9fcHJvY2Vzc19udW1lcmljYWwgPC0gY2JpbmQodHJhaW5fdG9fcHJvY2Vzc19udW1lcmljYWwsIHNlbGVjdCh0cmFpbiwgU2FsZVByaWNlKSkNCg0KIyBTdG9yZSB0aGUgb3ZlcmFsbCBjb3JyZWxhdGlvbiBpbiAnY29ycmVsYXRpb25zJw0KY29ycmVsYXRpb25zIDwtIGNvcih0cmFpbl90b19wcm9jZXNzX251bWVyaWNhbFssMTo5XSkNCg0KIyBQbG90IHRoZSBjb3JyZWxhdGlvbiBwbG90IHdpdGggJ2NvcnJlbGF0aW9ucycNCmNvcnJwbG90KGNvcnJlbGF0aW9ucywgbWV0aG9kPSJzcXVhcmUiLCB0eXBlID0gInVwcGVyIikNCmBgYA0KDQpUaGVuIHRoZSBjYXRlZ29yaWNhbCBmZWF0dXJlcy4NCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp0cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsIDwtIGNiaW5kKHRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIHNlbGVjdCh0cmFpbiwgU2FsZVByaWNlKSkNCg0KZ2dwbG90KGRhdGE9dHJhaW5fdG9fcHJvY2Vzc19jYXRlZ29yaWNhbCwgYWVzKHk9IFNhbGVQcmljZSwgeD1NYXNWbnJBcmVhLCBmaWxsPU1hc1ZuckFyZWEpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIE1hc1ZuckFyZWEiKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIk1hc1ZuckFyZWEiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PUdhcmFnZVlyQmx0LCBmaWxsPUdhcmFnZVlyQmx0KSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBHYXJhZ2VZckJsdCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiR2FyYWdlWXJCbHQiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PU1hc1ZuclR5cGUsIGZpbGw9TWFzVm5yVHlwZSkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgTWFzVm5yVHlwZSIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiTWFzVm5yVHlwZSIpDQoNCmdncGxvdChkYXRhPXRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIGFlcyh5PSBTYWxlUHJpY2UsIHg9R2FyYWdlVHlwZSwgZmlsbD1HYXJhZ2VUeXBlKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBHYXJhZ2VUeXBlIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJHYXJhZ2VUeXBlIikNCg0KZ2dwbG90KGRhdGE9dHJhaW5fdG9fcHJvY2Vzc19jYXRlZ29yaWNhbCwgYWVzKHk9IFNhbGVQcmljZSwgeD1HYXJhZ2VGaW5pc2gsIGZpbGw9R2FyYWdlRmluaXNoKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBHYXJhZ2VGaW5pc2giKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIkdhcmFnZUZpbmlzaCIpDQoNCmdncGxvdChkYXRhPXRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIGFlcyh5PSBTYWxlUHJpY2UsIHg9TWFzVm5yVHlwZSwgZmlsbD1HYXJhZ2VRdWFsKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBHYXJhZ2VRdWFsIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJHYXJhZ2VRdWFsIikNCg0KZ2dwbG90KGRhdGE9dHJhaW5fdG9fcHJvY2Vzc19jYXRlZ29yaWNhbCwgYWVzKHk9IFNhbGVQcmljZSwgeD1HYXJhZ2VDb25kLCBmaWxsPUdhcmFnZUNvbmQpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIEdhcmFnZUNvbmQiKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIkdhcmFnZUNvbmQiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PUJzbXRRdWFsLCBmaWxsPUJzbXRRdWFsKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBCc210UXVhbCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiQnNtdFF1YWwiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PU1hc1ZuclR5cGUsIGZpbGw9QnNtdENvbmQpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIEJzbXRDb25kIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJCc210Q29uZCIpDQoNCmdncGxvdChkYXRhPXRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIGFlcyh5PSBTYWxlUHJpY2UsIHg9TWFzVm5yVHlwZSwgZmlsbD1Cc210RXhwb3N1cmUpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIEJzbXRFeHBvc3VyZSIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiQnNtdEV4cG9zdXJlIikNCg0KZ2dwbG90KGRhdGE9dHJhaW5fdG9fcHJvY2Vzc19jYXRlZ29yaWNhbCwgYWVzKHk9IFNhbGVQcmljZSwgeD1Cc210RmluVHlwZTEsIGZpbGw9QnNtdEZpblR5cGUxKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBCc210RmluVHlwZTEiKSArICANCiAgeWxhYigiU2FsZSBQcmljZSIpICsgDQogIHhsYWIoIkJzbXRGaW5UeXBlMSIpDQoNCmdncGxvdChkYXRhPXRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIGFlcyh5PSBTYWxlUHJpY2UsIHg9QnNtdEZpblR5cGUxLCBmaWxsPUJzbXRGaW5UeXBlMikgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgQnNtdEZpblR5cGUyIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJCc210RmluVHlwZTIiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PUVsZWN0cmljYWwsIGZpbGw9RWxlY3RyaWNhbCkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgRWxlY3RyaWNhbCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiRWxlY3RyaWNhbCIpDQoNCmdncGxvdChkYXRhPXRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIGFlcyh5PSBTYWxlUHJpY2UsIHg9TVNab25pbmcsIGZpbGw9TVNab25pbmcpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIE1TWm9uaW5nIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJNU1pvbmluZyIpDQoNCmdncGxvdChkYXRhPXRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIGFlcyh5PSBTYWxlUHJpY2UsIHg9VXRpbGl0aWVzLCBmaWxsPVV0aWxpdGllcykgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgVXRpbGl0aWVzIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJVdGlsaXRpZXMiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PUV4dGVyaW9yMXN0LCBmaWxsPUV4dGVyaW9yMXN0KSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBFeHRlcmlvcjFzdCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiRXh0ZXJpb3Ixc3QiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PUV4dGVyaW9yMm5kLCBmaWxsPUV4dGVyaW9yMm5kKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBFeHRlcmlvcjJuZCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiRXh0ZXJpb3IybmQiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PUtpdGNoZW5RdWFsLCBmaWxsPUtpdGNoZW5RdWFsKSApICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBLaXRjaGVuUXVhbCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiS2l0Y2hlblF1YWwiKQ0KDQpnZ3Bsb3QoZGF0YT10cmFpbl90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCBhZXMoeT0gU2FsZVByaWNlLCB4PUZ1bmN0aW9uYWwsIGZpbGw9RnVuY3Rpb25hbCkgKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24gb2YgRnVuY3Rpb25hbCIpICsgIA0KICB5bGFiKCJTYWxlIFByaWNlIikgKyANCiAgeGxhYigiRnVuY3Rpb25hbCIpDQoNCmdncGxvdChkYXRhPXRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIGFlcyh5PSBTYWxlUHJpY2UsIHg9U2FsZVR5cGUsIGZpbGw9U2FsZVR5cGUpICkgKyANCiAgZ2VvbV9ib3hwbG90KCkgKyANCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIFNhbGVUeXBlIikgKyAgDQogIHlsYWIoIlNhbGUgUHJpY2UiKSArIA0KICB4bGFiKCJTYWxlVHlwZSIpDQoNCmBgYA0KDQpBcyB5b3UgY2FuIHNlZSB3ZSBzdGlsbCBoYXZlIGEgbnVtYmVyIG9mIGlycmVsZXZhbnQgZmVhdHVyZXMsIHNvIHdlIHdpbGwgYWxzbyByZW1vdmUgdGhlbS4NCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCnRyYWluX3RvX3Byb2Nlc3MgPC0gY2JpbmQoc2VsZWN0KHRyYWluX3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwsIC1TYWxlUHJpY2UpLCB0cmFpbl90b19wcm9jZXNzX251bWVyaWNhbCkNCg0KdHJhaW5fZmluYWwgPC0gY2JpbmQoc2VsZWN0KHRyYWluX3RvX3Byb2Nlc3MsIC1BbGxleSwgLUJzbXRGaW5UeXBlMiwgLUZlbmNlLCAtRnVuY3Rpb25hbCwgLVV0aWxpdGllcywgLVBvb2xRQywgLUJzbXRIYWxmQmF0aCwgLUJzbXRGaW5TRjIsIC1NaXNjRmVhdHVyZSwgLVNhbGVQcmljZSksIHRyYWluX2NsZWFuKQ0KYGBgDQoNClRoZW4gd2Ugd2lsbCBkbyB0aGUgc2FtZSBwcm9jZXNzIHRvIG91ciB0ZXN0IHNldC4NCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQp0ZXN0X25vbl9udWxsIDwtIHNlbGVjdCh0ZXN0LCAtTG90RnJvbnRhZ2UsIC1BbGxleSwgLU1hc1ZuclR5cGUsIC1NYXNWbnJBcmVhLCAtRmlyZXBsYWNlUXUsIC1HYXJhZ2VUeXBlLCAtR2FyYWdlWXJCbHQsIC1HYXJhZ2VGaW5pc2gsIC1HYXJhZ2VRdWFsLCAtR2FyYWdlQ29uZCwgLUJzbXRRdWFsLCAtQnNtdENvbmQsIC1Cc210RXhwb3N1cmUsIC1Cc210RmluVHlwZTEsIC1Qb29sUUMsIC1GZW5jZSwgLU1pc2NGZWF0dXJlLCAtQnNtdEZpblR5cGUxLCAtQnNtdEZpblR5cGUyLCAtRWxlY3RyaWNhbCwgLUJzbXRGaW5TRjIsIC1Cc210VW5mU0YsIC1Ub3RhbEJzbXRTRiwgLUJzbXRGdWxsQmF0aCwgLUJzbXRIYWxmQmF0aCwgLUdhcmFnZUNhcnMsIC1HYXJhZ2VBcmVhLCAtTVNab25pbmcsIC1VdGlsaXRpZXMsIC1FeHRlcmlvcjFzdCwgLUV4dGVyaW9yMm5kLCAgLUJzbXRGaW5TRjEsIC1LaXRjaGVuUXVhbCwgLUZ1bmN0aW9uYWwsIC1TYWxlVHlwZSkNCg0KdGVzdF9jbGVhbiA8LSBzZWxlY3QodGVzdF9ub25fbnVsbCwgLU1TU3ViQ2xhc3MsIC1PdmVyYWxsQ29uZCwgLUxvd1F1YWxGaW5TRiwgLUJlZHJvb21BYnZHciwgLUtpdGNoZW5BYnZHciwgLUVuY2xvc2VkUG9yY2gsIC1YM1NzblBvcmNoLCAtU2NyZWVuUG9yY2gsIC1Qb29sQXJlYSwgLU1pc2NWYWwsIC1Nb1NvbGQsIC1ZclNvbGQpDQp0ZXN0X2NsZWFuIDwtIHNlbGVjdCh0ZXN0X2NsZWFuLCAtUm9vZlN0eWxlLCAtQmxkZ1R5cGUsIC1MYW5kU2xvcGUsIC1Mb3RDb25maWcsIC1MYW5kQ29udG91ciwgLUhlYXRpbmcsIC1FeHRlckNvbmQsIC1Sb29mTWF0bCwgLUNvbmRpdGlvbjEsIC1Db25kaXRpb24yLCAtU3RyZWV0KQ0KDQp0ZXN0X3RvX3Byb2Nlc3NfbnVtZXJpY2FsIDwtIHNlbGVjdCh0ZXN0LCBCc210RmluU0YyLCBCc210VW5mU0YsIFRvdGFsQnNtdFNGLCBCc210RnVsbEJhdGgsIEJzbXRIYWxmQmF0aCwgR2FyYWdlQ2FycywgR2FyYWdlQXJlYSwgQnNtdEZpblNGMSkNCg0KdGVzdF90b19wcm9jZXNzX2NhdGVnb3JpY2FsIDwtIHNlbGVjdCh0ZXN0LCBNYXNWbnJBcmVhLCBHYXJhZ2VZckJsdCwgTWFzVm5yVHlwZSwgR2FyYWdlVHlwZSwgR2FyYWdlRmluaXNoLCBHYXJhZ2VRdWFsLCBHYXJhZ2VDb25kLCBCc210UXVhbCwgQnNtdENvbmQsIEJzbXRFeHBvc3VyZSwgQnNtdEZpblR5cGUxLCBCc210RmluVHlwZTEsIEJzbXRGaW5UeXBlMiwgRWxlY3RyaWNhbCwgTVNab25pbmcsIFV0aWxpdGllcywgRXh0ZXJpb3Ixc3QsIEV4dGVyaW9yMm5kLCBLaXRjaGVuUXVhbCwgRnVuY3Rpb25hbCwgRmVuY2UsIEFsbGV5LCBQb29sUUMsIE1pc2NGZWF0dXJlLCBTYWxlVHlwZSkNCg0KDQpmb3IoaSBpbiAxOm5jb2wodGVzdF90b19wcm9jZXNzX251bWVyaWNhbCkpew0KICB0ZXN0X3RvX3Byb2Nlc3NfbnVtZXJpY2FsW2lzLm5hKHRlc3RfdG9fcHJvY2Vzc19udW1lcmljYWxbLGldKSwgaV0gPC0gbWVhbih0ZXN0X3RvX3Byb2Nlc3NfbnVtZXJpY2FsWyxpXSwgbmEucm0gPSBUUlVFKQ0KfQ0KDQpmb3IoaSBpbiAxOm5jb2wodGVzdF90b19wcm9jZXNzX2NhdGVnb3JpY2FsKSl7DQogIHRlc3RfdG9fcHJvY2Vzc19jYXRlZ29yaWNhbFtpcy5uYSh0ZXN0X3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWxbLGldKSwgaV0gPC0gTW9kZSh0ZXN0X3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWxbLGldKQ0KfQ0KDQp0ZXN0X3RvX3Byb2Nlc3MgPC0gY2JpbmQodGVzdF90b19wcm9jZXNzX2NhdGVnb3JpY2FsLCB0ZXN0X3RvX3Byb2Nlc3NfbnVtZXJpY2FsKQ0KDQp0ZXN0X2ZpbmFsIDwtIGNiaW5kKHNlbGVjdCh0ZXN0X3RvX3Byb2Nlc3MsIC1BbGxleSwgLUJzbXRGaW5UeXBlMiwgLUZlbmNlLCAtRnVuY3Rpb25hbCwgLVV0aWxpdGllcywgLVBvb2xRQywgLUJzbXRIYWxmQmF0aCwgLUJzbXRGaW5TRjIsIC1NaXNjRmVhdHVyZSksIHRlc3RfY2xlYW4pDQpgYGANCg0KIyMjIEV4cG9ydCB0aGUgZGF0YQ0KDQpBZnRlciBhbGwgdGhlIGRhdGEgY2xlYW5pbmcgYW5kIHByb2Nlc3Npbmcgd2UgY2FuIHdyaXRlIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZSBpbnRvIHR3byBjc3YgZmlsZXMgKHRyYWluIGFuZCB0ZXN0KSBhbmQgdXNlIGl0IG9uIG91ciBtb2RlbC4NCg0KcmVtaW5kZXIgdGhlIGxpbmsgd2l0aCB0aGUgdGVuc29yZmxvdyBjb2RlIGlzIGF0OiBodHRwczovL2dpdGh1Yi5jb20vZGltaXRyZU9saXZlaXJhL0hvdXNlUHJpY2VzDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KdGVzdF9ub25fbnVsbCA8LSBzZWxlY3QodGVzdCwgLUxvdEZyb250YWdlLCAtQWxsZXksIC1NYXNWbnJUeXBlLCAtTWFzVm5yQXJlYSwgLUZpcmVwbGFjZVF1LCAtR2FyYWdlVHlwZSwgLUdhcmFnZVlyQmx0LCAtR2FyYWdlRmluaXNoLCAtR2FyYWdlUXVhbCwgLUdhcmFnZUNvbmQsIC1Cc210UXVhbCwgLUJzbXRDb25kLCAtQnNtdEV4cG9zdXJlLCAtQnNtdEZpblR5cGUxLCAtUG9vbFFDLCAtRmVuY2UsIC1NaXNjRmVhdHVyZSwgLUJzbXRGaW5UeXBlMSwgLUJzbXRGaW5UeXBlMiwgLUVsZWN0cmljYWwsIC1Cc210RmluU0YyLCAtQnNtdFVuZlNGLCAtVG90YWxCc210U0YsIC1Cc210RnVsbEJhdGgsIC1Cc210SGFsZkJhdGgsIC1HYXJhZ2VDYXJzLCAtR2FyYWdlQXJlYSwgLU1TWm9uaW5nLCAtVXRpbGl0aWVzLCAtRXh0ZXJpb3Ixc3QsIC1FeHRlcmlvcjJuZCwgIC1Cc210RmluU0YxLCAtS2l0Y2hlblF1YWwsIC1GdW5jdGlvbmFsLCAtU2FsZVR5cGUpDQoNCnRlc3RfdG9fcHJvY2Vzc19udW1lcmljYWwgPC0gc2VsZWN0KHRlc3QsIEJzbXRGaW5TRjIsIEJzbXRVbmZTRiwgVG90YWxCc210U0YsIEJzbXRGdWxsQmF0aCwgQnNtdEhhbGZCYXRoLCBHYXJhZ2VDYXJzLCBHYXJhZ2VBcmVhLCBCc210RmluU0YxKQ0KDQp0ZXN0X3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWwgPC0gc2VsZWN0KHRlc3QsIE1hc1ZuckFyZWEsIEdhcmFnZVlyQmx0LCBNYXNWbnJUeXBlLCBHYXJhZ2VUeXBlLCBHYXJhZ2VGaW5pc2gsIEdhcmFnZVF1YWwsIEdhcmFnZUNvbmQsIEJzbXRRdWFsLCBCc210Q29uZCwgQnNtdEV4cG9zdXJlLCBCc210RmluVHlwZTEsIEJzbXRGaW5UeXBlMSwgQnNtdEZpblR5cGUyLCBFbGVjdHJpY2FsLCBNU1pvbmluZywgVXRpbGl0aWVzLCBFeHRlcmlvcjFzdCwgRXh0ZXJpb3IybmQsIEtpdGNoZW5RdWFsLCBGdW5jdGlvbmFsLCBTYWxlVHlwZSkNCg0KZm9yKGkgaW4gMTpuY29sKHRlc3RfdG9fcHJvY2Vzc19udW1lcmljYWwpKXsNCiAgdGVzdF90b19wcm9jZXNzX251bWVyaWNhbFtpcy5uYSh0ZXN0X3RvX3Byb2Nlc3NfbnVtZXJpY2FsWyxpXSksIGldIDwtIG1lYW4odGVzdF90b19wcm9jZXNzX251bWVyaWNhbFssaV0sIG5hLnJtID0gVFJVRSkNCn0NCg0KZm9yKGkgaW4gMTpuY29sKHRlc3RfdG9fcHJvY2Vzc19jYXRlZ29yaWNhbCkpew0KICB0ZXN0X3RvX3Byb2Nlc3NfY2F0ZWdvcmljYWxbaXMubmEodGVzdF90b19wcm9jZXNzX2NhdGVnb3JpY2FsWyxpXSksIGldIDwtIE1vZGUodGVzdF90b19wcm9jZXNzX2NhdGVnb3JpY2FsWyxpXSkNCn0NCg0KdGVzdF90b19wcm9jZXNzIDwtIGNiaW5kKHRlc3RfdG9fcHJvY2Vzc19jYXRlZ29yaWNhbCwgdGVzdF90b19wcm9jZXNzX251bWVyaWNhbCkNCg0Kd3JpdGUuY3N2KHRyYWluX2ZpbmFsLCBmaWxlID0gIkM6L1VzZXJzL2RpbWl0L0Rlc2t0b3AvUHJvamV0b3MvSG91c2UgUHJpY2VzL2RhdGEvdHJhaW5fY2xlYW5lZC5jc3YiKQ0Kd3JpdGUuY3N2KHRlc3RfZmluYWwsIGZpbGUgPSAiQzovVXNlcnMvZGltaXQvRGVza3RvcC9Qcm9qZXRvcy9Ib3VzZSBQcmljZXMvZGF0YS90ZXN0X2NsZWFuZWQuY3N2IikNCmBgYA==