Introduction

The purpose of this project is to predict the log error of Zillow’s Zestimate at six potential sale times in 2016 and 2017 using time series forecasting models. Zillow made all 2016 home sales data from three counties available on Kaggle including all fields in Table 1. Predicting error rates over time will help Zillow to improve their model over time and give consumers better information as they buy and sell the largest assets they’ll likely ever own.

R code for all calculations and visuals can be found here

Column Name Description
'airconditioningtypeid' Type of cooling system present in the home (if any)
'architecturalstyletypeid' Architectural style of the home (i.e. ranch, colonial, split-level, etc…)
'basementsqft' Finished living area below or partially below ground level
'bathroomcnt' Number of bathrooms in home including fractional bathrooms
'bedroomcnt' Number of bedrooms in home
'buildingqualitytypeid' Overall assessment of condition of the building from best (lowest) to worst (highest)
'buildingclasstypeid' The building framing type (steel frame, wood frame, concrete/brick)
'calculatedbathnbr' Number of bathrooms in home including fractional bathroom
'decktypeid' Type of deck (if any) present on parcel
'threequarterbathnbr' Number of 3/4 bathrooms in house (shower + sink + toilet)
'finishedfloor1squarefeet' Size of the finished living area on the first (entry) floor of the home
'calculatedfinishedsquarefeet' Calculated total finished living area of the home
'finishedsquarefeet6' Base unfinished and finished area
'finishedsquarefeet12' Finished living area
'finishedsquarefeet13' Perimeter living area
'finishedsquarefeet15' Total area
'finishedsquarefeet50' Size of the finished living area on the first (entry) floor of the home
'fips' Federal Information Processing Standard code - see https://en.wikipedia.org/wiki/FIPS_county_code for more details
'fireplacecnt' Number of fireplaces in a home (if any)
'fireplaceflag' Is a fireplace present in this home
'fullbathcnt' Number of full bathrooms (sink, shower + bathtub, and toilet) present in home
'garagecarcnt' Total number of garages on the lot including an attached garage
'garagetotalsqft' Total number of square feet of all garages on lot including an attached garage
'hashottuborspa' Does the home have a hot tub or spa
'heatingorsystemtypeid' Type of home heating system
'latitude' Latitude of the middle of the parcel multiplied by 10e6
'longitude' Longitude of the middle of the parcel multiplied by 10e6
'lotsizesquarefeet' Area of the lot in square feet
'numberofstories' Number of stories or levels the home has
'parcelid' Unique identifier for parcels (lots)
'poolcnt' Number of pools on the lot (if any)
'poolsizesum' Total square footage of all pools on property
'pooltypeid10' Spa or Hot Tub
'pooltypeid2' Pool with Spa/Hot Tub
'pooltypeid7' Pool without hot tub
'propertycountylandusecode' County land use code i.e. it's zoning at the county level
'propertylandusetypeid' Type of land use the property is zoned for
'propertyzoningdesc' Description of the allowed land uses (zoning) for that property
'rawcensustractandblock' Census tract and block ID combined - also contains blockgroup assignment by extension
'censustractandblock' Census tract and block ID combined - also contains blockgroup assignment by extension
'regionidcounty' County in which the property is located
'regionidcity' City in which the property is located (if any)
'regionidzip' Zip code in which the property is located
'regionidneighborhood' Neighborhood in which the property is located
'roomcnt' Total number of rooms in the principal residence
'storytypeid' Type of floors in a multi-story house (i.e. basement and main level, split-level, attic, etc.). See tab for details.
'typeconstructiontypeid' What type of construction material was used to construct the home
'unitcnt' Number of units the structure is built into (i.e. 2 = duplex, 3 = triplex, etc...)
'yardbuildingsqft17' Patio in yard
'yardbuildingsqft26' Storage shed/building in yard
'yearbuilt' The Year the principal residence was built
'taxvaluedollarcnt' The total tax assessed value of the parcel
'structuretaxvaluedollarcnt' The assessed value of the built structure on the parcel
'landtaxvaluedollarcnt' The assessed value of the land area of the parcel
'taxamount' The total property tax assessed for that assessment year
'assessmentyear' The year of the property tax assessment
'taxdelinquencyflag' Property taxes for this parcel are past due as of 2015
'taxdelinquencyyear' Year for which the unpaid propert taxes were due

Exploratory Data Analysis

As a first exploratory step, the number of null values for each variable are distplayed in Figure 0.1. Many of the variables in the data set have significant numbers of missing values. This could be due to lack of self-reporting or differing standards for reporting from various government data sources employed by Zillow. A decision rule of 75% completion is used for retention of a variable. Missing values in the remaining columns are imputed using boosted regression and classification trees.

Joining, by = "parcelid"

From here I create dummy variables for all categorical values and then summarise all variables so we have average daily values rather than multiple values per day. Some variables (like city id) are removed in this step because we have more granular data (like zip codes) that measure the same thing.


 iter imp variable
  1   1  calculatedbathnbr  calculatedfinishedsquarefeet  lotsizesquarefeet  yearbuilt  structuretaxvaluedollarcnt  taxvaluedollarcnt  landtaxvaluedollarcnt  taxamount
  1   2  calculatedbathnbr  calculatedfinishedsquarefeet  lotsizesquarefeet  yearbuilt  structuretaxvaluedollarcnt  taxvaluedollarcnt  landtaxvaluedollarcnt  taxamount
  1   3  calculatedbathnbr  calculatedfinishedsquarefeet  lotsizesquarefeet  yearbuilt  structuretaxvaluedollarcnt  taxvaluedollarcnt  landtaxvaluedollarcnt  taxamount
  1   4  calculatedbathnbr  calculatedfinishedsquarefeet  lotsizesquarefeet  yearbuilt  structuretaxvaluedollarcnt  taxvaluedollarcnt  landtaxvaluedollarcnt  taxamount
  1   5  calculatedbathnbr  calculatedfinishedsquarefeet  lotsizesquarefeet  yearbuilt  structuretaxvaluedollarcnt  taxvaluedollarcnt  landtaxvaluedollarcnt  taxamount

The logerror rates almost represent a perfect white noise time series over the course of 2016.

Model Development and Testing

Three models are attempted for this project. The first model is a multiple linear regression model using variables selected with a backwards variable selection method. The second model is a simple exponential smoothing model. The final model uses the ARIMA method. Models are evaluated by MAE, ME, and RMSE as well as the average error in the six out of sample test points tests as part of Zillow’s Kaggle competition.

Multiple Linear Regression Model

A multiple linear regression model uses linear relationships between predictors and a target value to predict future unseen values. The model takes the form \(y_t=\beta_0 + \beta_1 x_{1,t}+ \beta_2 x_{2,t}+ \beta_3 x_{3,t}+ \beta_i x_{i,t}+\varepsilon_t.\) for i predictor variables.

The backwards variable selection method employed here starts with all possible predictors in the data set and whittles them away iteratively after testing to see whether or not the inclusion of a variable minimizes the Akaike Information Criterion (AIC). The algorithm’s output is the best possible model for each potential number of predictor variables to be included as well as various model health metrics for each potential model. Figure X plots the first 60 or so potential models against residual sum of squares (RSS) and adjusted r-squared (Adj. R-squared), with each variable indexed against it’s maximum value vor purposes of comparing them on the same plot [^2]. The 3-variable model is the best possible linear combination of predictor variables according to this method since that value maximizes Adj. R-squared and is near the point of diminishing marginal returns for RSS.

In implementing the model chosen through backwards variable selection, we see fitted values that are more conservative than the actual LogError values observed in the data. The model only explains 22% of the variance in the data and several predictor variables with correlation coefficients indistinguishable frmo zero, although for predictive purposes that is less important than optimizing AIC or out of sample error rates [^3].

[1] "calculatedfinishedsquarefeet" "structuretaxvaluedollarcnt"   "taxvaluedollarcnt"           

Where the model residuals depart from the normal distribution most notably is a heavy negative tail. In general, the linear model is more conservative than the data - hewing closer to the overall average value than.

Simple Exponential Smoothing

Exponential smoothing methods produce weighted averages of past values controlled by a smoothing parameter \(\alpha\) in order to optimize the level \(\ell_t\). A smaller value for \(\alpha\) means more weight is applied to older observations while a larger value applies more weight to more recent values[^3].

The optimal simple exponential smoothing function for the zillow data uses \(\ell_{t} = .0133 y_{t} + (1 -.0133)\ell_{t-1}\) and an initial \(\ell\) value of .0101.

The smoothed predicted values are mostly clustered alightly above zero (around .01). The residuals are not exactly normally distributed because they cluster too closely around 0. Additionally, there are a few outlying values (moreso than the MLR model).

ARIMA Model

The AutoRegressive Integrated Moving Average (ARIMA) model is a regression model acounting for lagged error terms and lagged predicted values of the outcome of interest. The model is presented in the form ARIMA(p,d,q) where p represents the autoregressive formula, d indicates the amount of differencing applied to the time series, and q describes the moving average component[^3].

The auto.arima() R function, which uses a step-wise variation of the Hyndman-Khandakar model selection method [^4] to optimize for AICc, returned a first-order autoregressive model as the optimal model for the data. Essentially, the method uses a single period lag as a regressor to make predictions about future outcomes.

The fitted values of the ARIMA model follow a similar pattern to the linear model. Fitted values follow a similar, if more conservative, pattern as the observed values and, while there are some outlying residuals, the bulk of the values are clustered around zero.

Model Evaluation

As stated earlier, models are evaluated by MAE, ME, and RMSE as well as the average error in the six out of sample test points tests as part of Zillow’s Kaggle competition.

The average error among all models was fairly small. Differences in RMSE and MAE were also fairly small. Simple Exponential Smoothing performed best on MAE which makes sense because the pattern of the fitted values makes it difficult to imagine that model overfitting the data. ARIMA and MLR perform similarly across the board.

Finally, the big test is how well the predictions compared to actual values on six prescribed months that Zillow is interested in checking: October 2016 (201610), November 2016 (201611), December 2016 (201612), October 2017 (201710), November 2017 (201711), and December 2017 (201712). The predicted values for the first day of each 2016 month are below:

Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.

Conclusion

The goal of this project was to predict Zillow Zestimate LogError values. Three models were trained and tested for their predictive accuracy - multiple linear regression, simple exponential smoothing, and ARIMA. The ARIMA and multiple regression models appeared to perform best and teh ARIMA model was used to make predictions about out of sample data points.

Citations

[^1:] Time Series Analysis and Its Application with R examples [^2:] James, G., Witten, D., Hastie, T., & Tibshirani, R. (2017). An Introduction to Statistical Learning: with Applications in R. New York: Springer. [^3:] Hyndman (http://otexts.org/fpp2/Regr-LSprinciple.html) [^4:] Hyndman, Rob J, and Yeasmin Khandakar. 2008. “Automatic Time Series Forecasting: The Forecast Package for R.” Journal of Statistical Software 27 (1): 1-22.

LS0tDQp0aXRsZTogIlppbGxvdydzIEhvbWUgVmFsdWUgUHJlZGljdGlvbiAoWmVzdGltYXRlKSINCnN1YnRpdGxlOiAiUHJlZGljdGluZyBlcnJvciB0byBpbXByb3ZlIHRoZSBaZXN0aW1hdGUiDQphdXRob3I6ICdKb3NoIFlhem1hbicNCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgSW50cm9kdWN0aW9uDQpUaGUgcHVycG9zZSBvZiB0aGlzIHByb2plY3QgaXMgdG8gcHJlZGljdCB0aGUgbG9nIGVycm9yIG9mIFppbGxvdydzIFplc3RpbWF0ZSBhdCBzaXggcG90ZW50aWFsIHNhbGUgdGltZXMgaW4gMjAxNiBhbmQgMjAxNyB1c2luZyB0aW1lIHNlcmllcyBmb3JlY2FzdGluZyBtb2RlbHMuIFppbGxvdyBtYWRlIGFsbCAyMDE2IGhvbWUgc2FsZXMgZGF0YSBmcm9tIHRocmVlIGNvdW50aWVzIGF2YWlsYWJsZSBvbiBbS2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL2MvemlsbG93LXByaXplLTEpIGluY2x1ZGluZyBhbGwgZmllbGRzIGluIFRhYmxlIDEuIFByZWRpY3RpbmcgZXJyb3IgcmF0ZXMgb3ZlciB0aW1lIHdpbGwgaGVscCBaaWxsb3cgdG8gaW1wcm92ZSB0aGVpciBtb2RlbCBvdmVyIHRpbWUgYW5kIGdpdmUgY29uc3VtZXJzIGJldHRlciBpbmZvcm1hdGlvbiBhcyB0aGV5IGJ1eSBhbmQgc2VsbCB0aGUgbGFyZ2VzdCBhc3NldHMgdGhleSdsbCBsaWtlbHkgZXZlciBvd24uDQoNClIgY29kZSBmb3IgYWxsIGNhbGN1bGF0aW9ucyBhbmQgdmlzdWFscyBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9qb3NoeWF6bWFuL25vcnRod2VzdGVybi91cGxvYWQvbWFzdGVyL1BSRURJQ1Q0MTMvemlsbG93LXByb2plY3QpDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2V0d2QoJ0M6L1VzZXJzL2pvc2h5L0dvb2dsZSBEcml2ZS9ub3J0aHdlc3Rlcm4vUFJFRElDVC00MTMvZ2VuZXJhbC1kb2NzL3ppbGxvdycpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHlhenRoZW1lKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGtuaXRyKQ0KDQoNCmFsbF8yMDE2IDwtIHJlYWRfY3N2KCdwcm9wZXJ0aWVzXzIwMTYuY3N2JykNCmRhdGFfZGljdCA8LSByZWFkX2V4Y2VsKCJ6aWxsb3dfZGF0YV9kaWN0aW9uYXJ5Lnhsc3giKQ0KdHJhaW5fMjAxNiA8LSByZWFkX2NzdigndHJhaW5fMjAxNl92Mi5jc3YnKQ0Ka2FibGUoZGF0YV9kaWN0JT4lc2VsZWN0KGBDb2x1bW4gTmFtZWAgPSBGZWF0dXJlLCBEZXNjcmlwdGlvbiksDQogICAgICBjYXB0aW9uID0gJ1RhYmxlIDE6IExpc3Rpbmcgb2YgQXZhaWxhYmxlIERhdGEnKQ0KYGBgDQoNCiMjIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMNCkFzIGEgZmlyc3QgZXhwbG9yYXRvcnkgc3RlcCwgdGhlIG51bWJlciBvZiBudWxsIHZhbHVlcyBmb3IgZWFjaCB2YXJpYWJsZSBhcmUgZGlzdHBsYXllZCBpbiBGaWd1cmUgMC4xLiBNYW55IG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIGRhdGEgc2V0IGhhdmUgc2lnbmlmaWNhbnQgbnVtYmVycyBvZiBtaXNzaW5nIHZhbHVlcy4gVGhpcyBjb3VsZCBiZSBkdWUgdG8gbGFjayBvZiBzZWxmLXJlcG9ydGluZyBvciBkaWZmZXJpbmcgc3RhbmRhcmRzIGZvciByZXBvcnRpbmcgZnJvbSB2YXJpb3VzIGdvdmVybm1lbnQgZGF0YSBzb3VyY2VzIGVtcGxveWVkIGJ5IFppbGxvdy4gQSBkZWNpc2lvbiBydWxlIG9mIDc1JSBjb21wbGV0aW9uIGlzIHVzZWQgZm9yIHJldGVudGlvbiBvZiBhIHZhcmlhYmxlLiBNaXNzaW5nIHZhbHVlcyBpbiB0aGUgcmVtYWluaW5nIGNvbHVtbnMgYXJlIGltcHV0ZWQgdXNpbmcgYm9vc3RlZCByZWdyZXNzaW9uIGFuZCBjbGFzc2lmaWNhdGlvbiB0cmVlcy4gDQpgYGB7ciwgZmlnLmhlaWdodCA9IDcsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHJlc2hhcGUyKQ0KZnVsbF90cmFpbiA8LSBsZWZ0X2pvaW4odHJhaW5fMjAxNiwgYWxsXzIwMTYpDQoNCm51bGxzIDwtIGRhdGEuZnJhbWUoY29sID0gYXMuY2hhcmFjdGVyKGNvbG5hbWVzKGZ1bGxfdHJhaW4pKSwgDQogICAgICAgICAgICAgICAgICAgIHBjdF9udWxsID0gY29sU3Vtcyhpcy5uYShmdWxsX3RyYWluKSkqMTAwLyhjb2xTdW1zKGlzLm5hKGZ1bGxfdHJhaW4pKStjb2xTdW1zKCFpcy5uYShmdWxsX3RyYWluKSkpKSU+JQ0KICBmaWx0ZXIoY29sICE9ICdwYXJjZWxpZCcpDQpudWxsX2NvdW50IDwtIGdncGxvdChudWxscywgYWVzKHggPSByZW9yZGVyKGNvbCxwY3RfbnVsbCksIHkgPSBwY3RfbnVsbCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBpZmVsc2UocGN0X251bGwgPiAyNSwgJ1JlbW92ZScsJ1JldGFpbicpKSkrDQogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSsNCiAgY29vcmRfZmxpcCgpKw0KICBsYWJzKHRpdGxlID0gJ0ZpZ3VyZSAwLjE6IERpc3RyaWJ1dGlvbiBvZiBNaXNzaW5nIERhdGEnLA0KICAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksIHkgPSAnUGVyY2VudCBvZiBJbmZvcm1hdGlvbiBNaXNzaW5nJywNCiAgICAgICBzdWJ0aXRsZSA9ICdQcmlvciB0byBtaXNzaW5nIHZhcmlhYmxlIGltcHV0YXRpb24gb3IgZGltZW5zaW9uIGVsaW1pbmF0aW9uJykrDQogIHlhenRoZW1lOjp0aGVtZV95YXooKSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAncmlnaHQnKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMjUsIGxpbmV0eXBlID0gJ2Rhc2hlZCcpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB5YXpfY29sc1tjKDQsNSldLCBuYW1lID0gJ0FjdGlvbicpDQoNCnRyYWluIDwtIGZ1bGxfdHJhaW5bLGNvbG5hbWVzKGZ1bGxfdHJhaW4pICVpbiUgbnVsbHMkY29sW251bGxzJHBjdF9udWxsIDwgMjVdXQ0KDQojIEhvbGRpbmcgb3V0IHRoZXNlIHZhcmlhYmxlcyBzbyBhcyBub3QgdG8gdXNlIHRoZW0gaW4gdGhlIGltcHV0YXRpb24gcHJvY2VzcyENCmhvbGQgPC0gdHJhaW4lPiVzZWxlY3QobG9nZXJyb3IsIHRyYW5zYWN0aW9uZGF0ZSkNCmltcHV0ZSA8LSB0cmFpbiU+JXNlbGVjdCgtbG9nZXJyb3IsIC0gdHJhbnNhY3Rpb25kYXRlLCAtY2Vuc3VzdHJhY3RhbmRibG9jaywtcmVnaW9uaWR6aXAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIC1wcm9wZXJ0eWNvdW50eWxhbmR1c2Vjb2RlLCAtcmVnaW9uaWRjb3VudHksIC1wcm9wZXJ0eWxhbmR1c2V0eXBlaWQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIC1maXBzLCAtbG9uZ2l0dWRlLCAtbGF0aXR1ZGUsIC1yZWdpb25pZGNpdHksLXJhd2NlbnN1c3RyYWN0YW5kYmxvY2ssIA0KICAgICAgICAgICAgICAgICAgICAgICAgIC1yZWdpb25pZGNvdW50eSwgLWJhdGhyb29tY250LCAtZnVsbGJhdGhjbnQsLWFzc2Vzc21lbnR5ZWFyKQ0KDQpudWxsX2NvdW50DQpgYGANCg0KRnJvbSBoZXJlIEkgY3JlYXRlIGR1bW15IHZhcmlhYmxlcyBmb3IgYWxsIGNhdGVnb3JpY2FsIHZhbHVlcyBhbmQgdGhlbiBzdW1tYXJpc2UgYWxsIHZhcmlhYmxlcyBzbyB3ZSBoYXZlIGF2ZXJhZ2UgZGFpbHkgdmFsdWVzIHJhdGhlciB0aGFuIG11bHRpcGxlIHZhbHVlcyBwZXIgZGF5LiBTb21lIHZhcmlhYmxlcyAobGlrZSBjaXR5IGlkKSBhcmUgcmVtb3ZlZCBpbiB0aGlzIHN0ZXAgYmVjYXVzZSB3ZSBoYXZlIG1vcmUgZ3JhbnVsYXIgZGF0YSAobGlrZSB6aXAgY29kZXMpIHRoYXQgbWVhc3VyZSB0aGUgc2FtZSB0aGluZy4gDQpgYGB7ciwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkobWljZSkNCnRlbXBfdHJhaW4gPC0gbWljZShpbXB1dGUsIG1ldGhvZCA9ICdjYXJ0JyxtYXhpdCA9IDEpDQp0cmFpbiA8LSBjb21wbGV0ZSh0ZW1wX3RyYWluLCAxKSU+JQ0KICBiaW5kX2NvbHMoaG9sZCkNCg0KbGlicmFyeShsdWJyaWRhdGUpDQoNCnRyYWluLmNsZWFuIDwtIHRyYWluJT4lDQogIG11dGF0ZShob21lX2FnZSA9IHllYXIoU3lzLkRhdGUoKSkteWVhcmJ1aWx0KSU+JQ0KICBncm91cF9ieSh0cmFuc2FjdGlvbmRhdGUpJT4lDQogIHN1bW1hcmlzZV9hbGwoZnVucyhtZWFuKSkNCg0KemlsbG93LnRzIDwtIHRzKHRyYWluLmNsZWFuLCBmcmVxdWVuY3kgPSAzNjUsIHN0YXJ0PWMoMjAxNiwxLDEpKQ0KeiA8LSB6aWxsb3cudHNbLCdsb2dlcnJvciddDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmdyaWQuYXJyYW5nZSgNCiAgYXV0b3Bsb3QoeikrDQogICAgeWF6dGhlbWU6OnRoZW1lX3lheigpKw0KICAgIGxhYnMoeCA9ICdEYXlzJywgeSA9ICdMb2dFcnJvcicsDQogICAgICAgICB0aXRsZSA9ICdMb2cgRXJyb3Igb2YgWmlsbG93XCdzIFplc3RpbWF0ZSBPdmVyIFRpbWUnKSwNCiAgZ2dwbG90KHRyYWluLmNsZWFuLCBhZXMoeCA9IGxvZ2Vycm9yKSkrDQogICAgZ2VvbV9kZW5zaXR5KGZpbGwgPSB5YXpfY29sc1sxXSwgYWxwaGEgPSAuNzUpKw0KICAgIGxhYnModGl0bGUgPSAnRGlzdHJpYnV0aW9uJywNCiAgICAgICAgIHggPSAnRGFpbHkgQXZlcmFnZSBMb2dFcnJvcicsDQogICAgICAgICB5ID0gJ0RlbnNpdHknKSsNCiAgICB5YXp0aGVtZTo6dGhlbWVfeWF6KCkrDQogICAgY29vcmRfZmxpcCgpLA0KICBucm93ID0gMSwgd2lkdGhzID0gYygzLDEpDQopDQpgYGANCg0KVGhlIGxvZ2Vycm9yIHJhdGVzIGFsbW9zdCByZXByZXNlbnQgYSBwZXJmZWN0IHdoaXRlIG5vaXNlIHRpbWUgc2VyaWVzIG92ZXIgdGhlIGNvdXJzZSBvZiAyMDE2LiANCmBgYHtyLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZ2dBY2YoeikrDQogIHlhenRoZW1lOjp0aGVtZV95YXooKSsNCiAgbGFicyh0aXRsZSA9ICdBQ0YgUGxvdCBvZiBBdmVyYWdlTG9nIEVycm9yIGJ5IERheScpDQpgYGANCg0KIyBNb2RlbCBEZXZlbG9wbWVudCBhbmQgVGVzdGluZw0KVGhyZWUgbW9kZWxzIGFyZSBhdHRlbXB0ZWQgZm9yIHRoaXMgcHJvamVjdC4gVGhlIGZpcnN0IG1vZGVsIGlzIGEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgdmFyaWFibGVzIHNlbGVjdGVkIHdpdGggYSBiYWNrd2FyZHMgdmFyaWFibGUgc2VsZWN0aW9uIG1ldGhvZC4gVGhlIHNlY29uZCBtb2RlbCBpcyBhIHNpbXBsZSBleHBvbmVudGlhbCBzbW9vdGhpbmcgbW9kZWwuIFRoZSBmaW5hbCBtb2RlbCB1c2VzIHRoZSBBUklNQSBtZXRob2QuIE1vZGVscyBhcmUgZXZhbHVhdGVkIGJ5IE1BRSwgTUUsIGFuZCBSTVNFIGFzIHdlbGwgYXMgdGhlIGF2ZXJhZ2UgZXJyb3IgaW4gdGhlIHNpeCBvdXQgb2Ygc2FtcGxlIHRlc3QgcG9pbnRzIHRlc3RzIGFzIHBhcnQgb2YgWmlsbG93J3MgS2FnZ2xlIGNvbXBldGl0aW9uLiANCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQptZXRob2QgPSBjKCkNCmFjY3VyYWN5ID0gbGlzdCgpDQpgYGANCg0KIyMgTXVsdGlwbGUgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwNCkEgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdXNlcyBsaW5lYXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHByZWRpY3RvcnMgYW5kIGEgdGFyZ2V0IHZhbHVlIHRvIHByZWRpY3QgZnV0dXJlIHVuc2VlbiB2YWx1ZXMuIFRoZSBtb2RlbCB0YWtlcyB0aGUgZm9ybSAkeV90PVxiZXRhXzAgKyBcYmV0YV8xIHhfezEsdH0rIFxiZXRhXzIgeF97Mix0fSsgXGJldGFfMyB4X3szLHR9KyBcYmV0YV9pIHhfe2ksdH0rXHZhcmVwc2lsb25fdC4kIGZvciBgaWAgcHJlZGljdG9yIHZhcmlhYmxlcy4NCg0KVGhlIGJhY2t3YXJkcyB2YXJpYWJsZSBzZWxlY3Rpb24gbWV0aG9kIGVtcGxveWVkIGhlcmUgc3RhcnRzIHdpdGggYWxsIHBvc3NpYmxlIHByZWRpY3RvcnMgaW4gdGhlIGRhdGEgc2V0IGFuZCB3aGl0dGxlcyB0aGVtIGF3YXkgaXRlcmF0aXZlbHkgYWZ0ZXIgdGVzdGluZyB0byBzZWUgd2hldGhlciBvciBub3QgdGhlIGluY2x1c2lvbiBvZiBhIHZhcmlhYmxlIG1pbmltaXplcyB0aGUgQWthaWtlIEluZm9ybWF0aW9uIENyaXRlcmlvbiAoQUlDKS4gVGhlIGFsZ29yaXRobSdzIG91dHB1dCBpcyB0aGUgYmVzdCBwb3NzaWJsZSBtb2RlbCBmb3IgZWFjaCBwb3RlbnRpYWwgbnVtYmVyIG9mIHByZWRpY3RvciB2YXJpYWJsZXMgdG8gYmUgaW5jbHVkZWQgYXMgd2VsbCBhcyB2YXJpb3VzIG1vZGVsIGhlYWx0aCBtZXRyaWNzIGZvciBlYWNoIHBvdGVudGlhbCBtb2RlbC4gRmlndXJlIFggcGxvdHMgdGhlIGZpcnN0IDYwIG9yIHNvIHBvdGVudGlhbCBtb2RlbHMgYWdhaW5zdCByZXNpZHVhbCBzdW0gb2Ygc3F1YXJlcyAoUlNTKSBhbmQgYWRqdXN0ZWQgci1zcXVhcmVkIChBZGouIFItc3F1YXJlZCksIHdpdGggZWFjaCB2YXJpYWJsZSBpbmRleGVkIGFnYWluc3QgaXQncyBtYXhpbXVtIHZhbHVlIHZvciBwdXJwb3NlcyBvZiBjb21wYXJpbmcgdGhlbSBvbiB0aGUgc2FtZSBwbG90IFteMl0uIFRoZSAzLXZhcmlhYmxlIG1vZGVsIGlzIHRoZSBiZXN0IHBvc3NpYmxlIGxpbmVhciBjb21iaW5hdGlvbiBvZiBwcmVkaWN0b3IgdmFyaWFibGVzIGFjY29yZGluZyB0byB0aGlzIG1ldGhvZCBzaW5jZSB0aGF0IHZhbHVlIG1heGltaXplcyBBZGouIFItc3F1YXJlZCBhbmQgaXMgbmVhciB0aGUgcG9pbnQgb2YgZGltaW5pc2hpbmcgbWFyZ2luYWwgcmV0dXJucyBmb3IgUlNTLg0KDQpgYGB7ciwgZWNobyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nID0gRkFMU0V9DQpsaWJyYXJ5KGxlYXBzKQ0KDQptb2RlbC5iYWNrIDwtIHJlZ3N1YnNldHMobG9nZXJyb3J+LiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLmNsZWFuJT4lZHBseXI6OnNlbGVjdCgtdHJhbnNhY3Rpb25kYXRlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBudm1heCA9IDEwMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gJ2JhY2t3YXJkJykNCm1vZC5zdW0gPC0gc3VtbWFyeShtb2RlbC5iYWNrKQ0KbW9kLnN1bS5oZWFsdGggPC0gZGF0YS5mcmFtZShyc3MgPSBtb2Quc3VtJHJzcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWRqcjIgPSBtb2Quc3VtJGFkanIyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuX3ZhcnMgPSBzZXEoMSxsZW5ndGgobW9kLnN1bSRhZGpyMikpKQ0KZ2dwbG90KG1vZC5zdW0uaGVhbHRoJT4lDQogICAgICAgICBtdXRhdGUocnNzID0gcnNzL21heChtb2Quc3VtLmhlYWx0aCRyc3MpLA0KICAgICAgICAgICAgICAgIGFkanIyID0gYWRqcjIvbWF4KG1vZC5zdW0uaGVhbHRoJGFkanIyKSklPiUNCiAgICAgICAgIHJlc2hhcGUyOjptZWx0KGlkLnZhcnMgPSAnbl92YXJzJyksIGFlcyh4ID0gbl92YXJzLCB5ID0gdmFsdWUsIGNvbG9yID0gdmFyaWFibGUpKSsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLjUpKw0KICB5YXp0aGVtZTo6dGhlbWVfeWF6KCkrDQogIGxhYnModGl0bGUgPSAnTW9kZWwgSGVhbHRoIGJ5IE51bWJlciBvZiBMaW5lYXIgUHJlZGljdG9ycyBJbmNsdWRlZCcsDQogICAgICAgc3VidGl0bGUgPSAnUlNTIGFuZCBBZGouIFItc3F1YXJlZCBJbmRleGVkIEFnYWluc3QgVGhlbXNlbHZlcycsDQogICAgICAgeSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICB4ID0gJ051bWJlciBvZiBWYXJpYWJsZXMgSW5jbHVkZWQgaW4gYW4gTUxSIE1vZGVsJykrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSB5YXpfY29sc1tjKDMsNCldLCBuYW1lID0gJ01vZGVsIEhlYWx0aCBNZXRyaWMnLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygnUlNTJywnQWRqLiBSLXNxdWFyZWQnKSkNCmBgYA0KDQpJbiBpbXBsZW1lbnRpbmcgdGhlIG1vZGVsIGNob3NlbiB0aHJvdWdoIGJhY2t3YXJkcyB2YXJpYWJsZSBzZWxlY3Rpb24sIHdlIHNlZSBmaXR0ZWQgdmFsdWVzIHRoYXQgYXJlIG1vcmUgY29uc2VydmF0aXZlIHRoYW4gdGhlIGFjdHVhbCBMb2dFcnJvciB2YWx1ZXMgb2JzZXJ2ZWQgaW4gdGhlIGRhdGEuIFRoZSBtb2RlbCBvbmx5IGV4cGxhaW5zIDIyJSBvZiB0aGUgdmFyaWFuY2UgaW4gdGhlIGRhdGEgYW5kIHNldmVyYWwgcHJlZGljdG9yIHZhcmlhYmxlcyB3aXRoIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBpbmRpc3Rpbmd1aXNoYWJsZSBmcm1vIHplcm8sIGFsdGhvdWdoIGZvciBwcmVkaWN0aXZlIHB1cnBvc2VzIHRoYXQgaXMgbGVzcyBpbXBvcnRhbnQgdGhhbiBvcHRpbWl6aW5nIEFJQyBvciBvdXQgb2Ygc2FtcGxlIGVycm9yIHJhdGVzIFteM10uDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAuNSxmaWcuaGVpZ2h0PTQsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIG1heGFkanIyIDwtIHdoaWNoLm1heChtb2Quc3VtJGFkanIyKQ0KIyB2YXJzIDwtIG5hbWVzKG1vZC5zdW0kd2hpY2hbbWF4YWRqcjIsXVttb2Quc3VtJHdoaWNoW21heGFkanIyLF0gPT0gVF0pWy0xXQ0KIyBmb3JtdWxhIDwtIHBhc3RlMCgnbG0obG9nZXJyb3IgfiAnLHBhc3RlKHZhcnMsIGNvbGxhcHNlID0gJysnKSwnLCBkYXRhID0gdHJhaW4uY2xlYW4nKQ0KbG0uZml0IDwtIHRzbG0obG9nZXJyb3IgfiBjYWxjdWxhdGVkZmluaXNoZWRzcXVhcmVmZWV0K3N0cnVjdHVyZXRheHZhbHVlZG9sbGFyY250Kw0KICAgICAgICAgICAgICAgICB0YXh2YWx1ZWRvbGxhcmNudCwgZGF0YSA9IHppbGxvdy50cykNCnN1bW1hcnkobG0uZml0KQ0KbWV0aG9kWzFdID0gJ011bHRpcGxlIExpbmVhciBSZWdyZXNzaW9uJw0KYWNjdXJhY3lbWzFdXSA9IGRhdGEuZnJhbWUoYWNjdXJhY3kobG0uZml0KSkNCmBgYA0KDQpXaGVyZSB0aGUgbW9kZWwgcmVzaWR1YWxzIGRlcGFydCBmcm9tIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uIG1vc3Qgbm90YWJseSBpcyBhIGhlYXZ5IG5lZ2F0aXZlIHRhaWwuIEluIGdlbmVyYWwsIHRoZSBsaW5lYXIgbW9kZWwgaXMgbW9yZSBjb25zZXJ2YXRpdmUgdGhhbiB0aGUgZGF0YSAtIGhld2luZyBjbG9zZXIgdG8gdGhlIG92ZXJhbGwgYXZlcmFnZSB2YWx1ZSB0aGFuLiANCmBgYHtyLCBmaWcud2lkdGg9MTAuNSxmaWcuaGVpZ2h0PTQsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpncmlkLmFycmFuZ2UoDQogIGF1dG9wbG90KHosIHNlcmllcz0iQWN0dWFsIikgKw0KICAgIGZvcmVjYXN0OjphdXRvbGF5ZXIoZml0dGVkKGxtLmZpdCksIHNlcmllcz0iUHJlZGljdGVkIikrDQogICAgeWF6dGhlbWU6OnRoZW1lX3lheigpKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdyaWdodCcsDQogICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gJ1NlcmllcycsIHZhbHVlcyA9IHlhel9jb2xzW2MoMyw0KV0pKw0KICAgIGxhYnModGl0bGUgPSAnUHJlZGljdGVkIHZzLiBBY3R1YWwgQXZlcmFnZSBMb2dFcnJvciBWYWx1ZXMnLA0KICAgICAgICAgeSA9ICdMb2dFcnJvcicsIHggPSAnVGltZScpLA0KICBnZ3Bsb3QoZGF0YS5mcmFtZShwcmVkaWN0ZWQgPSBsbS5maXQkZml0dGVkLnZhbHVlcywgYWN0dWFsID0gbG0uZml0JHJlc2lkdWFscytsbS5maXQkZml0dGVkLnZhbHVlcyksDQogICAgICAgICBhZXMoeCA9IHByZWRpY3RlZCwgeSA9IGFjdHVhbCkpKw0KICAgIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSB5YXpfY29sc1sxXSwgYWxwaGEgPSAuNykrDQogICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwKSsNCiAgICB5YXp0aGVtZTo6dGhlbWVfeWF6KCkrDQogICAgbGFicyh0aXRsZSA9ICcgJywNCiAgICAgICAgIHkgPSAnQWN0dWFsIExvZ0Vycm9yJywNCiAgICAgICAgIHggPSAnTUxSIE1vZGVsJykrDQogICAgeGxpbShtaW4oeiksbWF4KHopKSsNCiAgICB5bGltKG1pbih6KSxtYXgoeikpLA0KICBnZ3Bsb3QoZGF0YS5mcmFtZShyZXNpZHVhbHMgPSBsbS5maXQkcmVzaWR1YWxzKSwgYWVzKHggPSByZXNpZHVhbHMpKSsNCiAgICBnZW9tX2RlbnNpdHkoZmlsbCA9IHlhel9jb2xzWzFdLCBhbHBoYSA9IC43KSsNCiAgICB5YXp0aGVtZTo6dGhlbWVfeWF6KCkrDQogICAgbGFicyh5ID0gJ0RlbnNpdHknLCB4ID0gJ1Jlc2lkdWFsIExvZ0Vycm9yJywNCiAgICAgICAgIHRpdGxlID0gJ1Jlc2lkdWFsc1xuRGlzdHJpYnV0aW9uJykrDQogICAgY29vcmRfZmxpcCgpLA0KICBucm93ID0gMSwNCiAgd2lkdGhzID0gYygzLDIsMikNCikNCg0KZm9yZWNhc3QobG0uZml0JG1vZGVsLCBoID0gMTApDQpgYGANCg0KIyMgU2ltcGxlIEV4cG9uZW50aWFsIFNtb290aGluZw0KRXhwb25lbnRpYWwgc21vb3RoaW5nIG1ldGhvZHMgcHJvZHVjZSB3ZWlnaHRlZCBhdmVyYWdlcyBvZiBwYXN0IHZhbHVlcyBjb250cm9sbGVkIGJ5IGEgc21vb3RoaW5nIHBhcmFtZXRlciAkXGFscGhhJCBpbiBvcmRlciB0byBvcHRpbWl6ZSB0aGUgbGV2ZWwgJFxlbGxfdCQuIEEgc21hbGxlciB2YWx1ZSBmb3IgJFxhbHBoYSQgbWVhbnMgbW9yZSB3ZWlnaHQgaXMgYXBwbGllZCB0byBvbGRlciBvYnNlcnZhdGlvbnMgd2hpbGUgYSBsYXJnZXIgdmFsdWUgYXBwbGllcyBtb3JlIHdlaWdodCB0byBtb3JlIHJlY2VudCB2YWx1ZXNbXjNdLiANCg0KVGhlIG9wdGltYWwgc2ltcGxlIGV4cG9uZW50aWFsIHNtb290aGluZyBmdW5jdGlvbiBmb3IgdGhlIHppbGxvdyBkYXRhIHVzZXMgJFxlbGxfe3R9ID0gLjAxMzMgeV97dH0gKyAoMSAtLjAxMzMpXGVsbF97dC0xfSQgYW5kIGFuIGluaXRpYWwgJFxlbGwkIHZhbHVlIG9mIC4wMTAxLiANCg0KYGBge3IsIGZpZy53aWR0aD0xMC41LGZpZy5oZWlnaHQ9NCwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNlcy5maXQgPC0gc2VzKHopDQoNCiMgc3VtbWFyeShzZXMuZml0KQ0KbWV0aG9kWzJdID0gJ1NpbXBsZSBFeHBvbmVudGlhbCBTbW9vdGhpbmcnDQphY2N1cmFjeVtbMl1dID0gZGF0YS5mcmFtZShhY2N1cmFjeShzZXMuZml0KSkNCmdyaWQuYXJyYW5nZSgNCiAgYXV0b3Bsb3Qoeiwgc2VyaWVzID0gIkFjdHVhbCBMb2dFcnJvciIpKw0KICAgIGF1dG9sYXllcihzZXMuZml0JGZpdHRlZCwgc2VyaWVzID0gIlNFUyBNb2RlbCIpKw0KICAgIHRoZW1lX3lheigpKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gJ1NlcmllcycsIHZhbHVlcyA9IHlhel9jb2xzW2MoMyw0KV0pKw0KICAgIGxhYnModGl0bGUgPSAnUHJlZGljdGVkIHZzLiBBY3R1YWwgQXZlcmFnZSBMb2dFcnJvciBWYWx1ZXMnLA0KICAgICAgICAgeSA9ICdMb2dFcnJvcicsIHggPSAnVGltZScpKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdyaWdodCcsDQogICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpLA0KICBnZ3Bsb3QoZGF0YS5mcmFtZShwcmVkaWN0ZWQgPSBzZXMuZml0JGZpdHRlZCwgYWN0dWFsID0gc2VzLmZpdCRyZXNpZHVhbHMrc2VzLmZpdCRmaXR0ZWQpLA0KICAgICAgICAgYWVzKHggPSBwcmVkaWN0ZWQsIHkgPSBhY3R1YWwpKSsNCiAgICBnZW9tX3BvaW50KHNpemUgPSAxLjUsIGNvbG9yID0geWF6X2NvbHNbMV0sIGFscGhhID0gLjcpKw0KICAgIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgaW50ZXJjZXB0ID0gMCkrDQogICAgeWF6dGhlbWU6OnRoZW1lX3lheigpKw0KICAgIGxhYnModGl0bGUgPSAnICcsDQogICAgICAgICB5ID0gJ0FjdHVhbCBMb2dFcnJvcicsDQogICAgICAgICB4ID0gJ1NFUyBNb2RlbCcpKw0KICAgIHhsaW0obWluKHopLG1heCh6KSkrDQogICAgeWxpbShtaW4oeiksbWF4KHopKSwNCiAgZ2dwbG90KGRhdGEuZnJhbWUocmVzaWR1YWxzID0gc2VzLmZpdCRyZXNpZHVhbHMpLCBhZXMoeCA9IHJlc2lkdWFscykpKw0KICAgIGdlb21fZGVuc2l0eShmaWxsID0geWF6X2NvbHNbMV0sIGFscGhhID0gLjcpKw0KICAgIHlhenRoZW1lOjp0aGVtZV95YXooKSsNCiAgICBsYWJzKHkgPSAnRGVuc2l0eScsIHggPSAnUmVzaWR1YWwgTG9nRXJyb3InLA0KICAgICAgICAgdGl0bGUgPSAnUmVzaWR1YWxzXG5EaXN0cmlidXRpb24nKSsNCiAgICBjb29yZF9mbGlwKCksDQogIG5yb3cgPSAxLA0KICB3aWR0aHMgPSBjKDMsMiwyKQ0KKQ0KYGBgDQoNClRoZSBzbW9vdGhlZCBwcmVkaWN0ZWQgdmFsdWVzIGFyZSBtb3N0bHkgY2x1c3RlcmVkIGFsaWdodGx5IGFib3ZlIHplcm8gKGFyb3VuZCAuMDEpLiBUaGUgcmVzaWR1YWxzIGFyZSBub3QgZXhhY3RseSBub3JtYWxseSBkaXN0cmlidXRlZCBiZWNhdXNlIHRoZXkgY2x1c3RlciB0b28gY2xvc2VseSBhcm91bmQgMC4gQWRkaXRpb25hbGx5LCB0aGVyZSBhcmUgYSBmZXcgb3V0bHlpbmcgdmFsdWVzIChtb3Jlc28gdGhhbiB0aGUgTUxSIG1vZGVsKS4NCg0KIyMgQVJJTUEgTW9kZWwNClRoZSBBdXRvUmVncmVzc2l2ZSBJbnRlZ3JhdGVkIE1vdmluZyBBdmVyYWdlIChBUklNQSkgbW9kZWwgaXMgYSByZWdyZXNzaW9uIG1vZGVsIGFjb3VudGluZyBmb3IgbGFnZ2VkIGVycm9yIHRlcm1zIGFuZCBsYWdnZWQgcHJlZGljdGVkIHZhbHVlcyBvZiB0aGUgb3V0Y29tZSBvZiBpbnRlcmVzdC4gVGhlIG1vZGVsIGlzIHByZXNlbnRlZCBpbiB0aGUgZm9ybSBgQVJJTUEocCxkLHEpYCB3aGVyZSBgcGAgcmVwcmVzZW50cyB0aGUgYXV0b3JlZ3Jlc3NpdmUgZm9ybXVsYSwgYGRgIGluZGljYXRlcyB0aGUgYW1vdW50IG9mIGRpZmZlcmVuY2luZyBhcHBsaWVkIHRvIHRoZSB0aW1lIHNlcmllcywgYW5kIGBxYCBkZXNjcmliZXMgdGhlIG1vdmluZyBhdmVyYWdlIGNvbXBvbmVudFteM10uIA0KDQpUaGUgYGF1dG8uYXJpbWEoKWAgUiBmdW5jdGlvbiwgd2hpY2ggdXNlcyBhIHN0ZXAtd2lzZSB2YXJpYXRpb24gb2YgdGhlIEh5bmRtYW4tS2hhbmRha2FyIG1vZGVsIHNlbGVjdGlvbiBtZXRob2QgW140XSB0byBvcHRpbWl6ZSBmb3IgQUlDfmN+LCByZXR1cm5lZCBhIGZpcnN0LW9yZGVyIGF1dG9yZWdyZXNzaXZlIG1vZGVsIGFzIHRoZSBvcHRpbWFsIG1vZGVsIGZvciB0aGUgZGF0YS4gRXNzZW50aWFsbHksIHRoZSBtZXRob2QgdXNlcyBhIHNpbmdsZSBwZXJpb2QgbGFnIGFzIGEgcmVncmVzc29yIHRvIG1ha2UgcHJlZGljdGlvbnMgYWJvdXQgZnV0dXJlIG91dGNvbWVzLiAgDQpgYGB7ciwgZmlnLndpZHRoPTEwLjUsZmlnLmhlaWdodD00LCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYXJpbWEuZml0IDwtIGF1dG8uYXJpbWEoeix4cmVnID0gemlsbG93LnRzWyxjKCdjYWxjdWxhdGVkZmluaXNoZWRzcXVhcmVmZWV0JywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ3N0cnVjdHVyZXRheHZhbHVlZG9sbGFyY250JywndGF4dmFsdWVkb2xsYXJjbnQnKV0sDQogICAgICAgICAgICAgICAgICAgICAgICBzZWFzb25hbCA9IEYsIHN0YXRpb25hcnkgPSBUKQ0KIyBzdW1tYXJ5KGFyaW1hLmZpdCkNCm1ldGhvZFszXSA9ICdBUklNQScNCmFjY3VyYWN5W1szXV0gPSBkYXRhLmZyYW1lKGFjY3VyYWN5KGFyaW1hLmZpdCkpDQpncmlkLmFycmFuZ2UoDQogIGF1dG9wbG90KHosIHNlcmllcyA9ICJBY3R1YWwgTG9nRXJyb3IiKSsNCiAgICBhdXRvbGF5ZXIoYXJpbWEuZml0JGZpdHRlZCwgc2VyaWVzID0gIkFSSU1BIE1vZGVsIikrDQogICAgdGhlbWVfeWF6KCkrDQogICAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAnU2VyaWVzJywgdmFsdWVzID0geWF6X2NvbHNbYygzLDQpXSkrDQogICAgbGFicyh0aXRsZSA9ICdQcmVkaWN0ZWQgdnMuIEFjdHVhbCBBdmVyYWdlIExvZ0Vycm9yIFZhbHVlcycsDQogICAgICAgICB5ID0gJ0xvZ0Vycm9yJywgeCA9ICdUaW1lJykrDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ3JpZ2h0JywNCiAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gInZlcnRpY2FsIiksDQogIGdncGxvdChkYXRhLmZyYW1lKHByZWRpY3RlZCA9IGFyaW1hLmZpdCRmaXR0ZWQsIGFjdHVhbCA9IGFyaW1hLmZpdCRyZXNpZHVhbHMrYXJpbWEuZml0JGZpdHRlZCksDQogICAgICAgICBhZXMoeCA9IHByZWRpY3RlZCwgeSA9IGFjdHVhbCkpKw0KICAgIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSB5YXpfY29sc1sxXSwgYWxwaGEgPSAuNykrDQogICAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwKSsNCiAgICB5YXp0aGVtZTo6dGhlbWVfeWF6KCkrDQogICAgbGFicyh0aXRsZSA9ICcgJywNCiAgICAgICAgIHkgPSAnQWN0dWFsIExvZ0Vycm9yJywNCiAgICAgICAgIHggPSAnQVJJTUEgTW9kZWwnKSsNCiAgICB4bGltKG1pbih6KSxtYXgoeikpKw0KICAgIHlsaW0obWluKHopLG1heCh6KSksDQogIGdncGxvdChkYXRhLmZyYW1lKHJlc2lkdWFscyA9IGFyaW1hLmZpdCRyZXNpZHVhbHMpLCBhZXMoeCA9IHJlc2lkdWFscykpKw0KICAgIGdlb21fZGVuc2l0eShmaWxsID0geWF6X2NvbHNbMV0sIGFscGhhID0gLjcpKw0KICAgIHlhenRoZW1lOjp0aGVtZV95YXooKSsNCiAgICBsYWJzKHkgPSAnRGVuc2l0eScsIHggPSAnUmVzaWR1YWwgTG9nRXJyb3InLA0KICAgICAgICAgdGl0bGUgPSAnUmVzaWR1YWxzXG5EaXN0cmlidXRpb24nKSsNCiAgICBjb29yZF9mbGlwKCksDQogIG5yb3cgPSAxLA0KICB3aWR0aHMgPSBjKDMsMiwyKQ0KKQ0KYGBgDQoNClRoZSBmaXR0ZWQgdmFsdWVzIG9mIHRoZSBBUklNQSBtb2RlbCBmb2xsb3cgYSBzaW1pbGFyIHBhdHRlcm4gdG8gdGhlIGxpbmVhciBtb2RlbC4gRml0dGVkIHZhbHVlcyBmb2xsb3cgYSBzaW1pbGFyLCBpZiBtb3JlIGNvbnNlcnZhdGl2ZSwgcGF0dGVybiBhcyB0aGUgb2JzZXJ2ZWQgdmFsdWVzIGFuZCwgd2hpbGUgdGhlcmUgYXJlIHNvbWUgb3V0bHlpbmcgcmVzaWR1YWxzLCB0aGUgYnVsayBvZiB0aGUgdmFsdWVzIGFyZSBjbHVzdGVyZWQgYXJvdW5kIHplcm8uIA0KDQojIE1vZGVsIEV2YWx1YXRpb24NCkFzIHN0YXRlZCBlYXJsaWVyLCBtb2RlbHMgYXJlIGV2YWx1YXRlZCBieSBNQUUsIE1FLCBhbmQgUk1TRSBhcyB3ZWxsIGFzIHRoZSBhdmVyYWdlIGVycm9yIGluIHRoZSBzaXggb3V0IG9mIHNhbXBsZSB0ZXN0IHBvaW50cyB0ZXN0cyBhcyBwYXJ0IG9mIFppbGxvdydzIEthZ2dsZSBjb21wZXRpdGlvbi4NCg0KVGhlIGF2ZXJhZ2UgZXJyb3IgYW1vbmcgYWxsIG1vZGVscyB3YXMgZmFpcmx5IHNtYWxsLiBEaWZmZXJlbmNlcyBpbiBSTVNFIGFuZCBNQUUgd2VyZSBhbHNvIGZhaXJseSBzbWFsbC4gU2ltcGxlIEV4cG9uZW50aWFsIFNtb290aGluZyBwZXJmb3JtZWQgYmVzdCBvbiBNQUUgd2hpY2ggbWFrZXMgc2Vuc2UgYmVjYXVzZSB0aGUgcGF0dGVybiBvZiB0aGUgZml0dGVkIHZhbHVlcyBtYWtlcyBpdCBkaWZmaWN1bHQgdG8gaW1hZ2luZSB0aGF0IG1vZGVsIG92ZXJmaXR0aW5nIHRoZSBkYXRhLiBBUklNQSBhbmQgTUxSIHBlcmZvcm0gc2ltaWxhcmx5IGFjcm9zcyB0aGUgYm9hcmQuIA0KYGBge3IsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTIuNSwgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm1vZC5oZWFsdGggPC0gYmluZF9yb3dzKGFjY3VyYWN5KSU+JQ0KICBiaW5kX2NvbHMoZGF0YS5mcmFtZShtZXRob2QpKSU+JQ0KICBkcGx5cjo6c2VsZWN0KE1FLCBSTVNFLCBNQUUsIG1ldGhvZCkNCg0KZ2dwbG90KG1vZC5oZWFsdGglPiUNCiAgICAgICAgIG1lbHQoaWQudmFycyA9ICdtZXRob2QnKSwgYWVzKHggPSBtZXRob2QsIHkgPSB2YWx1ZSkpKw0KICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JywgZmlsbCA9IHlhel9jb2xzWzFdKSsNCiAgZmFjZXRfd3JhcCh+dmFyaWFibGUpKw0KICBjb29yZF9mbGlwKCkrDQogIHRoZW1lX3lheigpKw0KICBsYWJzKHRpdGxlID0gJ01vZGVsIEV2YWx1YXRpb25zJywgeSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICB4ID0gZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCkZpbmFsbHksIHRoZSBiaWcgdGVzdCBpcyBob3cgd2VsbCB0aGUgcHJlZGljdGlvbnMgY29tcGFyZWQgdG8gYWN0dWFsIHZhbHVlcyBvbiBzaXggcHJlc2NyaWJlZCBtb250aHMgdGhhdCBaaWxsb3cgaXMgaW50ZXJlc3RlZCBpbiBjaGVja2luZzogT2N0b2JlciAyMDE2ICgyMDE2MTApLCBOb3ZlbWJlciAyMDE2ICgyMDE2MTEpLCBEZWNlbWJlciAyMDE2ICgyMDE2MTIpLCBPY3RvYmVyIDIwMTcgKDIwMTcxMCksIE5vdmVtYmVyIDIwMTcgKDIwMTcxMSksIGFuZCBEZWNlbWJlciAyMDE3ICgyMDE3MTIpLiBUaGUgcHJlZGljdGVkIHZhbHVlcyBmb3IgdGhlIGZpcnN0IGRheSBvZiBlYWNoIDIwMTYgbW9udGggYXJlIGJlbG93OiANCg0KYGBge3IsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQphbGxfMjAxNyA8LSByZWFkX2NzdigncHJvcGVydGllc18yMDE3LmNzdicpDQp0ZXN0XzIwMTcgPC0gcmVhZF9jc3YoJ3RyYWluXzIwMTcuY3N2JykNCmZ1bGxfdGVzdCA8LSBsZWZ0X2pvaW4odGVzdF8yMDE3LCBhbGxfMjAxNykNCg0KDQoNCnRlc3QgPC0gZnVsbF90ZXN0JT4lc2VsZWN0KHRyYW5zYWN0aW9uZGF0ZSwgYmVkcm9vbWNudCwgY2FsY3VsYXRlZGJhdGhuYnIsIGNhbGN1bGF0ZWRmaW5pc2hlZHNxdWFyZWZlZXQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbmlzaGVkc3F1YXJlZmVldDEyLCBsb3RzaXplc3F1YXJlZmVldCwgcm9vbWNudCwgeWVhcmJ1aWx0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RydWN0dXJldGF4dmFsdWVkb2xsYXJjbnQsIHRheHZhbHVlZG9sbGFyY250LCBsYW5kdGF4dmFsdWVkb2xsYXJjbnQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRheGFtb3VudCwgbG9nZXJyb3IpDQojIEhvbGRpbmcgb3V0IHRoZXNlIHZhcmlhYmxlcyBzbyBhcyBub3QgdG8gdXNlIHRoZW0gaW4gdGhlIGltcHV0YXRpb24gcHJvY2VzcyENCmhvbGQgPC0gdGVzdCU+JXNlbGVjdChsb2dlcnJvciwgdHJhbnNhY3Rpb25kYXRlKQ0KaW1wdXRlIDwtIHRlc3QlPiVzZWxlY3QoLWxvZ2Vycm9yLCAtIHRyYW5zYWN0aW9uZGF0ZSkNCg0KbGlicmFyeShtaWNlKQ0KdGVtcF90ZXN0IDwtIG1pY2UoaW1wdXRlLCBtZXRob2QgPSAnY2FydCcsbWF4aXQgPSAxKQ0KdGVzdCA8LSBjb21wbGV0ZSh0ZW1wX3RyYWluLCAxKSU+JQ0KICBiaW5kX2NvbHMoaG9sZCkNCg0KbGlicmFyeShsdWJyaWRhdGUpDQoNCnRlc3QuY2xlYW4gPC0gdGVzdCU+JQ0KICBtdXRhdGUoaG9tZV9hZ2UgPSB5ZWFyKFN5cy5EYXRlKCkpLXllYXJidWlsdCklPiUNCiAgZ3JvdXBfYnkodHJhbnNhY3Rpb25kYXRlKSU+JQ0KICBzdW1tYXJpc2VfYWxsKGZ1bnMobWVhbikpDQoNCnRlc3QuemlsbG93LnRzIDwtIHRzKHRlc3QuY2xlYW4sIGZyZXF1ZW5jeSA9IDM2NSwgc3RhcnQ9YygyMDE3LDEsMSkpDQp0ZXN0LmZ1bGwgPC0gdHMoYmluZF9yb3dzKHRyYWluLmNsZWFuLCB0ZXN0LmNsZWFuKSU+JQ0KICAgICAgICAgICAgICAgICAgZmlsdGVyKHRyYW5zYWN0aW9uZGF0ZSAlaW4lIGMoYXMuRGF0ZSgnMjAxNi0xMC0wMScpLGFzLkRhdGUoJzIwMTYtMTEtMDEnKSxhcy5EYXRlKCcyMDE2LTEyLTAxJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5EYXRlKCcyMDE3LTEwLTAxJyksYXMuRGF0ZSgnMjAxNy0xMS0wMScpLGFzLkRhdGUoJzIwMTctMTItMDEnKSkpLCANCiAgICAgICAgICAgICAgICBmcmVxdWVuY3kgPSAzNjUsIHN0YXJ0ID0gYygyMDE2LDEsMSkpDQpmb3JlY2FzdChhcmltYS5maXQsIHhyZWcgPSB0ZXN0LmZ1bGxbLGMoJ2NhbGN1bGF0ZWRmaW5pc2hlZHNxdWFyZWZlZXQnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnc3RydWN0dXJldGF4dmFsdWVkb2xsYXJjbnQnLCd0YXh2YWx1ZWRvbGxhcmNudCcpXSklPiUNCiAgYXV0b3Bsb3QoKSsNCiAgdGhlbWVfeWF6KCkrDQogIGxhYnMoeSA9ICdMb2dFcnJvcicpKw0KICB4bGltKDIwMTYuODUsMjAxNykNCmBgYA0KDQojIENvbmNsdXNpb24NClRoZSBnb2FsIG9mIHRoaXMgcHJvamVjdCB3YXMgdG8gcHJlZGljdCBaaWxsb3cgWmVzdGltYXRlIExvZ0Vycm9yIHZhbHVlcy4gVGhyZWUgbW9kZWxzIHdlcmUgdHJhaW5lZCBhbmQgdGVzdGVkIGZvciB0aGVpciBwcmVkaWN0aXZlIGFjY3VyYWN5IC0gbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24sIHNpbXBsZSBleHBvbmVudGlhbCBzbW9vdGhpbmcsIGFuZCBBUklNQS4gVGhlIEFSSU1BIGFuZCBtdWx0aXBsZSByZWdyZXNzaW9uIG1vZGVscyBhcHBlYXJlZCB0byBwZXJmb3JtIGJlc3QgYW5kIHRlaCBBUklNQSBtb2RlbCB3YXMgdXNlZCB0byBtYWtlIHByZWRpY3Rpb25zIGFib3V0IG91dCBvZiBzYW1wbGUgZGF0YSBwb2ludHMuDQoNCiMgQ2l0YXRpb25zDQpbXjE6XSBUaW1lIFNlcmllcyBBbmFseXNpcyBhbmQgSXRzIEFwcGxpY2F0aW9uIHdpdGggUiBleGFtcGxlcw0KW14yOl0gSmFtZXMsIEcuLCBXaXR0ZW4sIEQuLCBIYXN0aWUsIFQuLCAmIFRpYnNoaXJhbmksIFIuICgyMDE3KS4gQW4gSW50cm9kdWN0aW9uIHRvIFN0YXRpc3RpY2FsIExlYXJuaW5nOiB3aXRoIEFwcGxpY2F0aW9ucyBpbiBSLiBOZXcgWW9yazogU3ByaW5nZXIuDQpbXjM6XSBIeW5kbWFuIChodHRwOi8vb3RleHRzLm9yZy9mcHAyL1JlZ3ItTFNwcmluY2lwbGUuaHRtbCkNClteNDpdIEh5bmRtYW4sIFJvYiBKLCBhbmQgWWVhc21pbiBLaGFuZGFrYXIuIDIwMDguICJBdXRvbWF0aWMgVGltZSBTZXJpZXMgRm9yZWNhc3Rpbmc6IFRoZSBGb3JlY2FzdCBQYWNrYWdlIGZvciBSLiIgSm91cm5hbCBvZiBTdGF0aXN0aWNhbCBTb2Z0d2FyZSAyNyAoMSk6IDEtMjIu