Introduction
This analysis aims to predict housing prices using various machine
learning models and census data. We will compare the performance of
Linear Regression, Random Forest, Mixed Effects, and Neural Network
models to identify the best approach for predicting housing prices.
The dataset includes housing features such as the number of bedrooms,
bathrooms, square footage, and location, as well as census data like
median income, population, education levels, and employment rates by ZIP
code.
Data Preprocessing
First, we read the housing dataset download from kaggle. We convert relevant columns to
appropriate data types and create new features like house age, years
since renovation, and living area to lot size ratio.
head(housing_data)
datatable(housing_data, options = list(pageLength = 5))
Warning: It seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.htmlWarning: It seems your data is too big for client-side DataTables. You may consider server-side processing: https://rstudio.github.io/DT/server.html
price: Ranges from $0 (which may indicate missing or placeholder
values) to $26,590,000 with a mean of $551,963.
bedrooms: Ranges from 0 to 9, with an average of about 3.4
bedrooms per house.
bathrooms: Ranges from 0 to 8, with a mean of approximately 2.16
bathrooms per house.
sqft_living: Living area square footage ranges from 370 to 13,540
sqft, with a mean of 2,139 sqft.
sqft_lot: Lot size square footage varies widely from 638 to
1,074,218 sqft, indicating a mix of urban to very large rural
properties.
floors, waterfront, view, condition: These features are
categorical or binary, with floors ranging from 1 to 3.5, indicating
multi-level houses are included. Waterfront has a binary indicator, with
very few waterfront properties present. View and condition are ordinal,
with their own range of values.
skim(housing_data)
# Price Distribution
ggplot(housing_data, aes(x = price)) +
geom_histogram(bins = 30, fill = "lightblue", color = "black") +
theme_minimal() +
labs(title = "Price Distribution")
# Bedrooms Distribution
ggplot(housing_data, aes(x = factor(bedrooms))) +
geom_bar(fill = "lightblue", color = "black") +
theme_minimal() +
labs(title = "Bedrooms Distribution", x = "Bedrooms")
# Bathrooms Distribution
ggplot(housing_data, aes(x = factor(bathrooms))) +
geom_bar(fill = "lightblue", color = "black") +
theme_minimal() +
labs(title = "Bathrooms Distribution", x = "Bathrooms")
# Living Area sqft Distribution
ggplot(housing_data, aes(x = sqft_living)) +
geom_histogram(bins = 30, fill = "lightblue", color = "black") +
theme_minimal() +
labs(title = "Living Area sqft Distribution")
# Correlation Heatmap
numeric_data <- select(housing_data, where(is.numeric))
cor_matrix <- cor(numeric_data)
corrplot(cor_matrix, method = 'circle', type = "upper", order = "hclust",
tl.col = "black", tl.srt = 45)
Incorperating Census Data
# Set the Census API key
census_api_key('bcbac889885cad20c1a787bc242d4aa4011251a2')
# Retrieve median income data by ZIP code
income_data <- get_acs(
geography = "zcta",
variables = "B19013_001",
year = 2019,
survey = "acs5"
)
f_income <- income_data %>%
filter(GEOID %in% housing_data$ZIP) %>%
rename(ZIP = GEOID, median_income = estimate)
# Retrieve population data for each ZIP code (ZCTA) from the latest ACS data
population_data <- get_acs(
geography = "zcta",
variables = "B01003_001",
year = 2019,
survey = "acs5"
)
f_pop <- population_data %>%
filter(GEOID %in% housing_data$ZIP) %>%
rename(ZIP = GEOID, population = estimate)
# Retrieve education data by ZIP code
education_data <- get_acs(
geography = "zcta",
variables = c(
"DP02_0059P", # Percent high school graduate or higher
"DP02_0065PE" # Percent bachelor's degree or higher
),
year = 2019,
survey = "acs5"
)
education_data <- education_data %>%
pivot_wider(names_from = variable, values_from = estimate) %>%
group_by(GEOID) %>%
summarize(
pct_high_school_grad = mean(DP02_0059P, na.rm = TRUE),
pct_bachelors_degree = mean(DP02_0065P, na.rm = TRUE)
)
# Retrieve employment and occupation data by ZIP code
employment_data <- get_acs(
geography = "zcta",
variables = c(
"DP03_0005PE", # Percent unemployed
"DP03_0027PE", # Percent in management, business, science, and arts occupations
"DP03_0028PE", # Percent in service occupations
"DP03_0029PE", # Percent in sales and office occupations
"DP03_0030PE" # Percent in natural resources, construction, and maintenance occupations
),
year = 2019,
survey = "acs5"
)
employment_data <- employment_data %>%
pivot_wider(names_from = variable, values_from = estimate) %>%
group_by(GEOID) %>%
summarize(
pct_unemployed = mean(DP03_0005P, na.rm = TRUE),
pct_mgmt_occupation = mean(DP03_0027P, na.rm = TRUE),
pct_service_occupation = mean(DP03_0028P, na.rm = TRUE),
pct_sales_office_occupation = mean(DP03_0029P, na.rm = TRUE),
pct_construction_occupation = mean(DP03_0030P, na.rm = TRUE)
)
# Retrieve housing characteristics data by ZIP code
housing_chars_data <- get_acs(
geography = "zcta",
variables = c(
"DP04_0046PE", # Percent owner-occupied housing units
"DP04_0039E", # Median year built for housing structures
"DP04_0093PE" # Percent of housing units with central air conditioning
),
year = 2019,
survey = "acs5"
)
housing_chars_data <- housing_chars_data %>%
pivot_wider(names_from = variable, values_from = estimate) %>%
group_by(GEOID) %>%
summarize(
pct_owner_occupied = mean(DP04_0046P, na.rm = TRUE),
median_year_built = mean(DP04_0039, na.rm = TRUE),
pct_central_air = mean(DP04_0093P, na.rm = TRUE)
)
# Retrieve commuting patterns data by ZIP code
commuting_data <- get_acs(
geography = "zcta",
variables = c(
"DP03_0025E", # Mean travel time to work (minutes)
"DP03_0021PE", # Percent using public transportation (excluding taxicab)
"DP03_0024PE" # Percent working from home
),
year = 2019,
survey = "acs5"
)
commuting_data <- commuting_data %>%
pivot_wider(names_from = variable, values_from = estimate) %>%
group_by(GEOID) %>%
summarize(
mean_travel_time = mean(DP03_0025, na.rm = TRUE),
pct_public_transport = mean(DP03_0021P, na.rm = TRUE),
pct_work_from_home = mean(DP03_0024P, na.rm = TRUE)
)
# Retrieve household characteristics data by ZIP code
household_data <- get_acs(
geography = "zcta",
variables = c(
"DP02_0016E", # Average household size
"DP02_0007PE", # Percent of households with own children under 18 years
"DP02_0009PE", # Percent of single-parent households
"DP05_0018E" # Median age
),
year = 2019,
survey = "acs5"
)
household_data <- household_data %>%
pivot_wider(names_from = variable, values_from = estimate) %>%
group_by(GEOID) %>%
summarize(
avg_household_size = mean(DP02_0016, na.rm = TRUE),
pct_households_with_children = mean(DP02_0007P, na.rm = TRUE),
pct_single_parent_households = mean(DP02_0009P, na.rm = TRUE),
median_age = mean(DP05_0018, na.rm = TRUE)
)
# Merge all the Census data with the housing data
housing_data_with_census <- housing_data %>%
left_join(f_income, by = "ZIP") %>%
left_join(f_pop, by = "ZIP") %>%
left_join(education_data, by = c("ZIP" = "GEOID")) %>%
left_join(employment_data, by = c("ZIP" = "GEOID")) %>%
left_join(housing_chars_data, by = c("ZIP" = "GEOID")) %>%
left_join(commuting_data, by = c("ZIP" = "GEOID")) %>%
left_join(household_data, by = c("ZIP" = "GEOID"))%>%
select(
date, price, bedrooms, bathrooms, sqft_living, sqft_lot, floors, waterfront, view, condition,
sqft_above, sqft_basement, yr_built, yr_renovated, street, city, country, ZIP,
house_age, years_since_renovation, total_rooms, living_lot_ratio, has_basement, renovated,
floor_area_ratio, outdoor_space, season,
median_income, population,
pct_high_school_grad, pct_bachelors_degree,
pct_unemployed, pct_mgmt_occupation, pct_service_occupation, pct_sales_office_occupation, pct_construction_occupation,
pct_owner_occupied, median_year_built, pct_central_air,
mean_travel_time, pct_public_transport, pct_work_from_home,
avg_household_size, pct_households_with_children, pct_single_parent_households, median_age
)%>%
#write_csv('Data_with_census.csv')
Preprocessing
housing_data_with_census <- read_csv('Data_with_census.csv')
Rows: 4412 Columns: 47── Column specification ───────────────────────────────────────────
Delimiter: ","
chr (5): street, city, country, State, season
dbl (41): price, bedrooms, bathrooms, sqft_living, sqft_lot, f...
date (1): date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

We decided to perform a log transformation on the price for make the
distribution more normal
print(vif_values)
bedrooms bathrooms
1.708079 2.840310
sqft_living floors
3.061817 2.763125
condition living_lot_ratio
1.271854 2.283708
pct_service_occupation pct_sales_office_occupation
3.574640 2.379895
pct_construction_occupation median_year_built
4.023819 4.305020
mean_travel_time pct_work_from_home
2.076649 3.530957
avg_household_size pct_households_with_children
4.812068 1.362034
pct_single_parent_households median_age
2.331042 3.079090
waterfront_X1 view_X1
1.326975 1.034925
view_X2 view_X3
1.061864 1.082554
view_X4 has_basement_X1
1.303335 1.705690
renovated_X1 season_Summer
1.235108 1.005978
VIF is bellow 5 for all predictors so we have addressed colinearity
problems.
library(multilevelmod)
set.seed(333)
## Create cross-validation folds for each model type
cv_folds_lm_rf <- vfold_cv(lm_rf_data, v = 10)
cv_folds_me <- vfold_cv(me_data_selected, v = 10)
cv_folds_nn <- vfold_cv(housing_data_for_model, v = 10)
# Create recipes for models
rec_lm_rf <- recipe(log_price ~ ., data = lm_rf_data)
rec_me <- recipe(log_price ~ ., data = me_data_selected)
rec_nn <- recipe(log_price ~ ., data = housing_data_for_model) %>%
step_normalize(all_numeric_predictors(), -all_outcomes()) %>%
step_dummy(all_nominal_predictors(), -all_outcomes())
# Define the models
lm_model <- linear_reg() %>% set_engine("lm") %>% set_mode("regression")
rf_model <- rand_forest(trees = 1000) %>% set_mode("regression") %>% set_engine("ranger", importance = "impurity")
me_model <- linear_reg() %>% set_engine("lmer") %>% set_mode("regression")
nn_model <- mlp(hidden_units = 10) %>% set_engine("nnet", linout = TRUE, trace = T) %>% set_mode("regression")
# Create workflows
lm_workflow <- workflow() %>% add_recipe(rec_lm_rf) %>% add_model(lm_model)
rf_workflow <- workflow() %>% add_recipe(rec_lm_rf) %>% add_model(rf_model)
me_workflow <- workflow() %>% add_recipe(rec_me) %>% add_model(me_model, formula = log_price ~ . + (1 | ZIP))
nn_workflow <- workflow() %>% add_recipe(rec_nn) %>% add_model(nn_model)
# Perform cross-validation
lm_cv_results <- fit_resamples(lm_workflow, resamples = cv_folds_lm_rf, metrics = metric_set(rmse, mae, rsq))
rf_cv_results <- fit_resamples(rf_workflow, resamples = cv_folds_lm_rf, metrics = metric_set(rmse, mae, rsq))
me_cv_results <- fit_resamples(me_workflow, resamples = cv_folds_me, metrics = metric_set(rmse, mae, rsq))
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
→ A | warning: unable to evaluate scaled gradient, Model failed to converge: degenerate Hessian with 1 negative eigenvalues
There were issues with some computations A: x1
There were issues with some computations A: x1
nn_cv_results <- fit_resamples(nn_workflow, resamples = cv_folds_nn, metrics = metric_set(rmse, mae, rsq))
# Summarize and compare the cross-validation results
cv_results <- bind_rows(
collect_metrics(lm_cv_results) %>% mutate(model = "Linear Regression"),
collect_metrics(rf_cv_results) %>% mutate(model = "Random Forest"),
collect_metrics(me_cv_results) %>% mutate(model = "Mixed Effects"),
collect_metrics(nn_cv_results) %>% mutate(model = "Neural Network")
)
cv_summary <- cv_results %>%
group_by(model, .metric) %>%
summarize(
mean_value = mean(mean, na.rm = TRUE),
std_error = mean(std_err, na.rm = TRUE),
.groups = 'drop'
) %>%
pivot_wider(names_from = .metric, values_from = c(mean_value, std_error))
print(cv_summary)
# Split the data into training and testing sets for each model type
data_split_lm_rf <- initial_split(lm_rf_data, prop = 0.8)
train_data_lm_rf <- training(data_split_lm_rf)
test_data_lm_rf <- testing(data_split_lm_rf)
data_split_me <- initial_split(me_data_selected, prop = 0.8)
train_data_me <- training(data_split_me)
test_data_me <- testing(data_split_me)
data_split_nn <- initial_split(housing_data_for_model, prop = 0.8)
train_data_nn <- training(data_split_nn)
test_data_nn <- testing(data_split_nn)
# Fit and evaluate each model using last_fit()
lm_last_fit <- last_fit(lm_workflow, data_split_lm_rf)
rf_last_fit <- last_fit(rf_workflow, data_split_lm_rf)
me_last_fit <- last_fit(me_workflow, data_split_me)
fixed-effect model matrix is rank deficient so dropping 10 columns / coefficients
boundary (singular) fit: see help('isSingular')
nn_last_fit <- last_fit(nn_workflow, data_split_nn)
# Extract predictions and metrics for each model
lm_results <- lm_last_fit %>% collect_predictions() %>% mutate(model = "Linear Regression")
rf_results <- rf_last_fit %>% collect_predictions() %>% mutate(model = "Random Forest")
me_results <- me_last_fit %>% collect_predictions() %>% mutate(model = "Mixed Effects")
nn_results <- nn_last_fit %>% collect_predictions() %>% mutate(model = "Neural Network")
# Combine the results
results <- bind_rows(lm_results, rf_results, me_results, nn_results)
# Plot predicted vs. actual for each model with R^2
results %>%
ggplot(aes(x = log_price, y = .pred, color = model)) +
geom_point(alpha = 0.5) +
geom_abline(slope = 1, intercept = 0, linetype = "dashed") +
facet_wrap(~ model, scales = "free") +
labs(
title = "Predicted vs. Actual House Prices",
x = "Actual Price",
y = "Predicted Price"
) +
theme_minimal() +
theme(legend.position = "none") +
geom_text(
data = results %>%
group_by(model) %>%
summarise(rsq = cor(log_price, .pred)^2),
aes(x = Inf, y = -Inf, label = sprintf("R^2 = %.3f", rsq)),
hjust = 1.1,
vjust = -1.1
)

The model with the best predictive accuracy was the random forest
model, followed by the linear regression model, neural network model,
and mixed effects model. Issues with model fitting are clear with the
mixed effects model due to the strong left skew in the error.
# Extract the trained Random Forest model from the last_fit object
rf_model <- rf_last_fit %>%
extract_fit_engine()
# Extract variable importance from the Random Forest model
rf_importance <- rf_model %>%
ranger::importance()
# Plot variable importance
ggplot(data.frame(variable = names(rf_importance), importance = rf_importance),
aes(x = reorder(variable, importance), y = importance, fill = importance)) +
geom_bar(stat = "identity") +
coord_flip() +
labs(x = "Variable", y = "Importance", title = "Random Forest Variable Importance") +
theme_minimal()

SQFT of living space followed by occupation prevalence appear to be
most predictive of housing price. Things like view, and commuting time
are suprisingly much lower by comparison!

Conversly in the linear model, having an outswtanding view seems to
be the most important predictor. Very different apporaches!
LS0tDQp0aXRsZTogIkVEQSBIb3VzaW5nIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UsIHdhcm4uY29uZmxpY3RzID0gRikNCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkobG1lNCkNCmxpYnJhcnkobHVicmlkYXRlKSAgIyBMb2FkIGx1YnJpZGF0ZSBmb3IgZGF0ZSBtYW5pcHVsYXRpb24NCmxpYnJhcnkodGlkeWNlbnN1cykgICMgTG9hZCB0aWR5Y2Vuc3VzIGZvciBjZW5zdXMgZGF0YSByZXRyaWV2YWwNCmxpYnJhcnkoY2FyKSAgIyBMb2FkIGNhciBmb3IgVklGIGNhbGN1bGF0aW9uDQpsaWJyYXJ5KGNhcmV0KSAgIyBMb2FkIGNhcmV0IGZvciBmaW5kaW5nIGhpZ2ggY29ycmVsYXRpb25zDQpsaWJyYXJ5KE1BU1MpICAjIExvYWQgTUFTUyBmb3Igc3RlcEFJQyBmdW5jdGlvbg0KbGlicmFyeSh0aWR5bW9kZWxzLCB3YXJuLmNvbmZsaWN0cyA9IEYpICAjIExvYWQgdGlkeW1vZGVscyBmb3IgbW9kZWxpbmcNCmxpYnJhcnkocmFuZ2VyKSAgIyBMb2FkIHJhbmdlciBmb3IgUmFuZG9tIEZvcmVzdA0KbGlicmFyeShza2ltcikNCmxpYnJhcnkoRFQpDQoNCnRpZHltb2RlbHNfcHJlZmVyKCkNCmBgYA0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyBhbmFseXNpcyBhaW1zIHRvIHByZWRpY3QgaG91c2luZyBwcmljZXMgdXNpbmcgdmFyaW91cyBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscyBhbmQgY2Vuc3VzIGRhdGEuIFdlIHdpbGwgY29tcGFyZSB0aGUgcGVyZm9ybWFuY2Ugb2YgTGluZWFyIFJlZ3Jlc3Npb24sIFJhbmRvbSBGb3Jlc3QsIE1peGVkIEVmZmVjdHMsIGFuZCBOZXVyYWwgTmV0d29yayBtb2RlbHMgdG8gaWRlbnRpZnkgdGhlIGJlc3QgYXBwcm9hY2ggZm9yIHByZWRpY3RpbmcgaG91c2luZyBwcmljZXMuDQoNClRoZSBkYXRhc2V0IGluY2x1ZGVzIGhvdXNpbmcgZmVhdHVyZXMgc3VjaCBhcyB0aGUgbnVtYmVyIG9mIGJlZHJvb21zLCBiYXRocm9vbXMsIHNxdWFyZSBmb290YWdlLCBhbmQgbG9jYXRpb24sIGFzIHdlbGwgYXMgY2Vuc3VzIGRhdGEgbGlrZSBtZWRpYW4gaW5jb21lLCBwb3B1bGF0aW9uLCBlZHVjYXRpb24gbGV2ZWxzLCBhbmQgZW1wbG95bWVudCByYXRlcyBieSBaSVAgY29kZS4NCg0KIyMgRGF0YSBQcmVwcm9jZXNzaW5nDQoNCkZpcnN0LCB3ZSByZWFkIHRoZSBob3VzaW5nIGRhdGFzZXQgZG93bmxvYWQgZnJvbSBba2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL3NocmVlMTk5Mi9ob3VzZWRhdGEvZGF0YSAiRGF0YXNldCBEb3dubG9hZCIpLiBXZSBjb252ZXJ0IHJlbGV2YW50IGNvbHVtbnMgdG8gYXBwcm9wcmlhdGUgZGF0YSB0eXBlcyBhbmQgY3JlYXRlIG5ldyBmZWF0dXJlcyBsaWtlIGhvdXNlIGFnZSwgeWVhcnMgc2luY2UgcmVub3ZhdGlvbiwgYW5kIGxpdmluZyBhcmVhIHRvIGxvdCBzaXplIHJhdGlvLg0KDQpoZWFkKGhvdXNpbmdfZGF0YSkNCg0KYGBge3J9DQpob3VzaW5nX2RhdGEgPC0gcmVhZF9jc3YoJ2RhdGEuY3N2JykNCg0KIyBDb252ZXJ0IHJlbGV2YW50IGNvbHVtbnMgdG8gYXBwcm9wcmlhdGUgZGF0YSB0eXBlcw0KaG91c2luZ19kYXRhJHZpZXcgPC0gYXMuZmFjdG9yKGhvdXNpbmdfZGF0YSR2aWV3KQ0KaG91c2luZ19kYXRhJHdhdGVyZnJvbnQgPC0gYXMuZmFjdG9yKGhvdXNpbmdfZGF0YSR3YXRlcmZyb250KQ0KaG91c2luZ19kYXRhJGNvbmRpdGlvbiA8LSBhcy5mYWN0b3IoaG91c2luZ19kYXRhJGNvbmRpdGlvbikNCmhvdXNpbmdfZGF0YSRkYXRlIDwtIGFzLkRhdGUoaG91c2luZ19kYXRhJGRhdGUpDQpob3VzaW5nX2RhdGEkcHJpY2UgPC0gYXMubnVtZXJpYyhob3VzaW5nX2RhdGEkcHJpY2UpDQpob3VzaW5nX2RhdGEkYmVkcm9vbXMgPC0gYXMubnVtZXJpYyhob3VzaW5nX2RhdGEkYmVkcm9vbXMpDQpob3VzaW5nX2RhdGEkYmF0aHJvb21zIDwtIGFzLm51bWVyaWMoaG91c2luZ19kYXRhJGJhdGhyb29tcykNCmhvdXNpbmdfZGF0YSRzcWZ0X2xpdmluZyA8LSBhcy5udW1lcmljKGhvdXNpbmdfZGF0YSRzcWZ0X2xpdmluZykNCmhvdXNpbmdfZGF0YSRzcWZ0X2xvdCA8LSBhcy5udW1lcmljKGhvdXNpbmdfZGF0YSRzcWZ0X2xvdCkNCmhvdXNpbmdfZGF0YSRmbG9vcnMgPC0gYXMubnVtZXJpYyhob3VzaW5nX2RhdGEkZmxvb3JzKQ0KaG91c2luZ19kYXRhJHNxZnRfYWJvdmUgPC0gYXMubnVtZXJpYyhob3VzaW5nX2RhdGEkc3FmdF9hYm92ZSkNCmhvdXNpbmdfZGF0YSRzcWZ0X2Jhc2VtZW50IDwtIGFzLm51bWVyaWMoaG91c2luZ19kYXRhJHNxZnRfYmFzZW1lbnQpDQpob3VzaW5nX2RhdGEkeXJfYnVpbHQgPC0gYXMuaW50ZWdlcihob3VzaW5nX2RhdGEkeXJfYnVpbHQpDQpob3VzaW5nX2RhdGEkeXJfcmVub3ZhdGVkIDwtIGFzLmludGVnZXIoaG91c2luZ19kYXRhJHlyX3Jlbm92YXRlZCkNCmhvdXNpbmdfZGF0YSRzdHJlZXQgPC0gYXMuY2hhcmFjdGVyKGhvdXNpbmdfZGF0YSRzdHJlZXQpDQpob3VzaW5nX2RhdGEkY2l0eSA8LSBhcy5jaGFyYWN0ZXIoaG91c2luZ19kYXRhJGNpdHkpDQpob3VzaW5nX2RhdGEkc3RhdGV6aXAgPC0gYXMuY2hhcmFjdGVyKGhvdXNpbmdfZGF0YSRzdGF0ZXppcCkNCmhvdXNpbmdfZGF0YSRjb3VudHJ5IDwtIGFzLmNoYXJhY3Rlcihob3VzaW5nX2RhdGEkY291bnRyeSkNCg0Kc3VtbWFyeShob3VzaW5nX2RhdGEpDQoNCmhvdXNpbmdfZGF0YSA8LSBob3VzaW5nX2RhdGEgJT4lDQogIG11dGF0ZShaSVAgPSBzdHJfZXh0cmFjdChzdGF0ZXppcCwgIlxcZHs1fSIpKQ0KDQoNCg0KaG91c2luZ19kYXRhIDwtIGhvdXNpbmdfZGF0YSAlPiUNCiAgbXV0YXRlKA0KICAgIGhvdXNlX2FnZSA9IGFzLmludGVnZXIoZm9ybWF0KFN5cy5EYXRlKCksICIlWSIpKSAtIHlyX2J1aWx0LA0KICAgIHllYXJzX3NpbmNlX3Jlbm92YXRpb24gPSBpZl9lbHNlKHlyX3Jlbm92YXRlZCA+IDAsIGFzLmludGVnZXIoZm9ybWF0KFN5cy5EYXRlKCksICIlWSIpKSAtIHlyX3Jlbm92YXRlZCwgaG91c2VfYWdlKSwNCiAgICB0b3RhbF9yb29tcyA9IGJlZHJvb21zICsgYmF0aHJvb21zLA0KICAgIGxpdmluZ19sb3RfcmF0aW8gPSBzcWZ0X2xpdmluZyAvIHNxZnRfbG90LA0KICAgIGhhc19iYXNlbWVudCA9IGFzLmludGVnZXIoc3FmdF9iYXNlbWVudCA+IDApLA0KICAgIHJlbm92YXRlZCA9IGFzLmludGVnZXIoeXJfcmVub3ZhdGVkID4gMCksDQogICAgZmxvb3JfYXJlYV9yYXRpbyA9IHNxZnRfYWJvdmUgLyBzcWZ0X2xvdCwNCiAgICBvdXRkb29yX3NwYWNlID0gc3FmdF9sb3QgLSBzcWZ0X2xpdmluZywNCiAgICBzZWFzb24gPSBjYXNlX3doZW4oDQogICAgICBtb250aChkYXRlKSAlaW4lIDM6NSB+ICJTcHJpbmciLA0KICAgICAgbW9udGgoZGF0ZSkgJWluJSA2OjggfiAiU3VtbWVyIiwNCiAgICAgIG1vbnRoKGRhdGUpICVpbiUgOToxMSB+ICJBdXR1bW4iLA0KICAgICAgVFJVRSB+ICJXaW50ZXIiDQogICAgKQ0KICApDQpkYXRhdGFibGUoaG91c2luZ19kYXRhLCBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gNSkpDQpgYGANCg0KLSAgIHByaWNlOiBSYW5nZXMgZnJvbSBcJDAgKHdoaWNoIG1heSBpbmRpY2F0ZSBtaXNzaW5nIG9yIHBsYWNlaG9sZGVyIHZhbHVlcykgdG8gXCQyNiw1OTAsMDAwIHdpdGggYSBtZWFuIG9mIFwkNTUxLDk2My4NCg0KLSAgIGJlZHJvb21zOiBSYW5nZXMgZnJvbSAwIHRvIDksIHdpdGggYW4gYXZlcmFnZSBvZiBhYm91dCAzLjQgYmVkcm9vbXMgcGVyIGhvdXNlLg0KDQotICAgYmF0aHJvb21zOiBSYW5nZXMgZnJvbSAwIHRvIDgsIHdpdGggYSBtZWFuIG9mIGFwcHJveGltYXRlbHkgMi4xNiBiYXRocm9vbXMgcGVyIGhvdXNlLg0KDQotICAgc3FmdF9saXZpbmc6IExpdmluZyBhcmVhIHNxdWFyZSBmb290YWdlIHJhbmdlcyBmcm9tIDM3MCB0byAxMyw1NDAgc3FmdCwgd2l0aCBhIG1lYW4gb2YgMiwxMzkgc3FmdC4NCg0KLSAgIHNxZnRfbG90OiBMb3Qgc2l6ZSBzcXVhcmUgZm9vdGFnZSB2YXJpZXMgd2lkZWx5IGZyb20gNjM4IHRvIDEsMDc0LDIxOCBzcWZ0LCBpbmRpY2F0aW5nIGEgbWl4IG9mIHVyYmFuIHRvIHZlcnkgbGFyZ2UgcnVyYWwgcHJvcGVydGllcy4NCg0KLSAgIGZsb29ycywgd2F0ZXJmcm9udCwgdmlldywgY29uZGl0aW9uOiBUaGVzZSBmZWF0dXJlcyBhcmUgY2F0ZWdvcmljYWwgb3IgYmluYXJ5LCB3aXRoIGZsb29ycyByYW5naW5nIGZyb20gMSB0byAzLjUsIGluZGljYXRpbmcgbXVsdGktbGV2ZWwgaG91c2VzIGFyZSBpbmNsdWRlZC4gV2F0ZXJmcm9udCBoYXMgYSBiaW5hcnkgaW5kaWNhdG9yLCB3aXRoIHZlcnkgZmV3IHdhdGVyZnJvbnQgcHJvcGVydGllcyBwcmVzZW50LiBWaWV3IGFuZCBjb25kaXRpb24gYXJlIG9yZGluYWwsIHdpdGggdGhlaXIgb3duIHJhbmdlIG9mIHZhbHVlcy4NCg0KYGBge3J9DQpza2ltKGhvdXNpbmdfZGF0YSkNCiMgUHJpY2UgRGlzdHJpYnV0aW9uDQpnZ3Bsb3QoaG91c2luZ19kYXRhLCBhZXMoeCA9IHByaWNlKSkgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG9yID0gImJsYWNrIikgKyANCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIGxhYnModGl0bGUgPSAiUHJpY2UgRGlzdHJpYnV0aW9uIikNCg0KIyBCZWRyb29tcyBEaXN0cmlidXRpb24NCmdncGxvdChob3VzaW5nX2RhdGEsIGFlcyh4ID0gZmFjdG9yKGJlZHJvb21zKSkpICsgDQogIGdlb21fYmFyKGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAiYmxhY2siKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgbGFicyh0aXRsZSA9ICJCZWRyb29tcyBEaXN0cmlidXRpb24iLCB4ID0gIkJlZHJvb21zIikNCg0KIyBCYXRocm9vbXMgRGlzdHJpYnV0aW9uDQpnZ3Bsb3QoaG91c2luZ19kYXRhLCBhZXMoeCA9IGZhY3RvcihiYXRocm9vbXMpKSkgKyANCiAgZ2VvbV9iYXIoZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJibGFjayIpICsgDQogIHRoZW1lX21pbmltYWwoKSArIA0KICBsYWJzKHRpdGxlID0gIkJhdGhyb29tcyBEaXN0cmlidXRpb24iLCB4ID0gIkJhdGhyb29tcyIpDQoNCiMgTGl2aW5nIEFyZWEgc3FmdCBEaXN0cmlidXRpb24NCmdncGxvdChob3VzaW5nX2RhdGEsIGFlcyh4ID0gc3FmdF9saXZpbmcpKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzAsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3IgPSAiYmxhY2siKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgbGFicyh0aXRsZSA9ICJMaXZpbmcgQXJlYSBzcWZ0IERpc3RyaWJ1dGlvbiIpDQoNCiMgQ29ycmVsYXRpb24gSGVhdG1hcA0KbnVtZXJpY19kYXRhIDwtIHNlbGVjdChob3VzaW5nX2RhdGEsIHdoZXJlKGlzLm51bWVyaWMpKQ0KY29yX21hdHJpeCA8LSBjb3IobnVtZXJpY19kYXRhKQ0KY29ycnBsb3QoY29yX21hdHJpeCwgbWV0aG9kID0gJ2NpcmNsZScsIHR5cGUgPSAidXBwZXIiLCBvcmRlciA9ICJoY2x1c3QiLCANCiAgICAgICAgIHRsLmNvbCA9ICJibGFjayIsIHRsLnNydCA9IDQ1KQ0KYGBgDQoNCiMjIEluY29ycGVyYXRpbmcgQ2Vuc3VzIERhdGENCmBgYHtyfQ0KIyBTZXQgdGhlIENlbnN1cyBBUEkga2V5DQpjZW5zdXNfYXBpX2tleSgnYmNiYWM4ODk4ODVjYWQyMGMxYTc4N2JjMjQyZDRhYTQwMTEyNTFhMicpDQoNCiMgUmV0cmlldmUgbWVkaWFuIGluY29tZSBkYXRhIGJ5IFpJUCBjb2RlDQppbmNvbWVfZGF0YSA8LSBnZXRfYWNzKA0KICBnZW9ncmFwaHkgPSAiemN0YSIsDQogIHZhcmlhYmxlcyA9ICJCMTkwMTNfMDAxIiwNCiAgeWVhciA9IDIwMTksDQogIHN1cnZleSA9ICJhY3M1Ig0KKQ0KDQpmX2luY29tZSA8LSBpbmNvbWVfZGF0YSAlPiUNCiAgZmlsdGVyKEdFT0lEICVpbiUgaG91c2luZ19kYXRhJFpJUCkgJT4lDQogIHJlbmFtZShaSVAgPSBHRU9JRCwgbWVkaWFuX2luY29tZSA9IGVzdGltYXRlKQ0KDQojIFJldHJpZXZlIHBvcHVsYXRpb24gZGF0YSBmb3IgZWFjaCBaSVAgY29kZSAoWkNUQSkgZnJvbSB0aGUgbGF0ZXN0IEFDUyBkYXRhDQpwb3B1bGF0aW9uX2RhdGEgPC0gZ2V0X2FjcygNCiAgZ2VvZ3JhcGh5ID0gInpjdGEiLA0KICB2YXJpYWJsZXMgPSAiQjAxMDAzXzAwMSIsDQogIHllYXIgPSAyMDE5LA0KICBzdXJ2ZXkgPSAiYWNzNSINCikNCg0KZl9wb3AgPC0gcG9wdWxhdGlvbl9kYXRhICU+JQ0KICBmaWx0ZXIoR0VPSUQgJWluJSBob3VzaW5nX2RhdGEkWklQKSAlPiUNCiAgcmVuYW1lKFpJUCA9IEdFT0lELCBwb3B1bGF0aW9uID0gZXN0aW1hdGUpDQoNCiMgUmV0cmlldmUgZWR1Y2F0aW9uIGRhdGEgYnkgWklQIGNvZGUNCmVkdWNhdGlvbl9kYXRhIDwtIGdldF9hY3MoDQogIGdlb2dyYXBoeSA9ICJ6Y3RhIiwNCiAgdmFyaWFibGVzID0gYygNCiAgICAiRFAwMl8wMDU5UCIsICMgUGVyY2VudCBoaWdoIHNjaG9vbCBncmFkdWF0ZSBvciBoaWdoZXINCiAgICAiRFAwMl8wMDY1UEUiICAjIFBlcmNlbnQgYmFjaGVsb3IncyBkZWdyZWUgb3IgaGlnaGVyDQogICksDQogIHllYXIgPSAyMDE5LA0KICBzdXJ2ZXkgPSAiYWNzNSINCikNCg0KZWR1Y2F0aW9uX2RhdGEgPC0gZWR1Y2F0aW9uX2RhdGEgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB2YXJpYWJsZSwgdmFsdWVzX2Zyb20gPSBlc3RpbWF0ZSkgJT4lDQogIGdyb3VwX2J5KEdFT0lEKSAlPiUNCiAgc3VtbWFyaXplKA0KICAgIHBjdF9oaWdoX3NjaG9vbF9ncmFkID0gbWVhbihEUDAyXzAwNTlQLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBjdF9iYWNoZWxvcnNfZGVncmVlID0gbWVhbihEUDAyXzAwNjVQLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KIyBSZXRyaWV2ZSBlbXBsb3ltZW50IGFuZCBvY2N1cGF0aW9uIGRhdGEgYnkgWklQIGNvZGUNCmVtcGxveW1lbnRfZGF0YSA8LSBnZXRfYWNzKA0KICBnZW9ncmFwaHkgPSAiemN0YSIsDQogIHZhcmlhYmxlcyA9IGMoDQogICAgIkRQMDNfMDAwNVBFIiwgIyBQZXJjZW50IHVuZW1wbG95ZWQNCiAgICAiRFAwM18wMDI3UEUiLCAjIFBlcmNlbnQgaW4gbWFuYWdlbWVudCwgYnVzaW5lc3MsIHNjaWVuY2UsIGFuZCBhcnRzIG9jY3VwYXRpb25zDQogICAgIkRQMDNfMDAyOFBFIiwgIyBQZXJjZW50IGluIHNlcnZpY2Ugb2NjdXBhdGlvbnMNCiAgICAiRFAwM18wMDI5UEUiLCAjIFBlcmNlbnQgaW4gc2FsZXMgYW5kIG9mZmljZSBvY2N1cGF0aW9ucw0KICAgICJEUDAzXzAwMzBQRSIgICMgUGVyY2VudCBpbiBuYXR1cmFsIHJlc291cmNlcywgY29uc3RydWN0aW9uLCBhbmQgbWFpbnRlbmFuY2Ugb2NjdXBhdGlvbnMNCiAgKSwNCiAgeWVhciA9IDIwMTksDQogIHN1cnZleSA9ICJhY3M1Ig0KKQ0KDQplbXBsb3ltZW50X2RhdGEgPC0gZW1wbG95bWVudF9kYXRhICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdmFyaWFibGUsIHZhbHVlc19mcm9tID0gZXN0aW1hdGUpICU+JQ0KICBncm91cF9ieShHRU9JRCkgJT4lDQogIHN1bW1hcml6ZSgNCiAgICBwY3RfdW5lbXBsb3llZCA9IG1lYW4oRFAwM18wMDA1UCwgbmEucm0gPSBUUlVFKSwNCiAgICBwY3RfbWdtdF9vY2N1cGF0aW9uID0gbWVhbihEUDAzXzAwMjdQLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBjdF9zZXJ2aWNlX29jY3VwYXRpb24gPSBtZWFuKERQMDNfMDAyOFAsIG5hLnJtID0gVFJVRSksDQogICAgcGN0X3NhbGVzX29mZmljZV9vY2N1cGF0aW9uID0gbWVhbihEUDAzXzAwMjlQLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBjdF9jb25zdHJ1Y3Rpb25fb2NjdXBhdGlvbiA9IG1lYW4oRFAwM18wMDMwUCwgbmEucm0gPSBUUlVFKQ0KICApDQoNCiMgUmV0cmlldmUgaG91c2luZyBjaGFyYWN0ZXJpc3RpY3MgZGF0YSBieSBaSVAgY29kZQ0KaG91c2luZ19jaGFyc19kYXRhIDwtIGdldF9hY3MoDQogIGdlb2dyYXBoeSA9ICJ6Y3RhIiwNCiAgdmFyaWFibGVzID0gYygNCiAgICAiRFAwNF8wMDQ2UEUiLCAjIFBlcmNlbnQgb3duZXItb2NjdXBpZWQgaG91c2luZyB1bml0cw0KICAgICJEUDA0XzAwMzlFIiwgIyBNZWRpYW4geWVhciBidWlsdCBmb3IgaG91c2luZyBzdHJ1Y3R1cmVzDQogICAgIkRQMDRfMDA5M1BFIiAgIyBQZXJjZW50IG9mIGhvdXNpbmcgdW5pdHMgd2l0aCBjZW50cmFsIGFpciBjb25kaXRpb25pbmcNCiAgKSwNCiAgeWVhciA9IDIwMTksDQogIHN1cnZleSA9ICJhY3M1Ig0KKQ0KDQpob3VzaW5nX2NoYXJzX2RhdGEgPC0gaG91c2luZ19jaGFyc19kYXRhICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdmFyaWFibGUsIHZhbHVlc19mcm9tID0gZXN0aW1hdGUpICU+JQ0KICBncm91cF9ieShHRU9JRCkgJT4lDQogIHN1bW1hcml6ZSgNCiAgICBwY3Rfb3duZXJfb2NjdXBpZWQgPSBtZWFuKERQMDRfMDA0NlAsIG5hLnJtID0gVFJVRSksDQogICAgbWVkaWFuX3llYXJfYnVpbHQgPSBtZWFuKERQMDRfMDAzOSwgbmEucm0gPSBUUlVFKSwNCiAgICBwY3RfY2VudHJhbF9haXIgPSBtZWFuKERQMDRfMDA5M1AsIG5hLnJtID0gVFJVRSkNCiAgKQ0KDQojIFJldHJpZXZlIGNvbW11dGluZyBwYXR0ZXJucyBkYXRhIGJ5IFpJUCBjb2RlDQpjb21tdXRpbmdfZGF0YSA8LSBnZXRfYWNzKA0KICBnZW9ncmFwaHkgPSAiemN0YSIsDQogIHZhcmlhYmxlcyA9IGMoDQogICAgIkRQMDNfMDAyNUUiLCAjIE1lYW4gdHJhdmVsIHRpbWUgdG8gd29yayAobWludXRlcykNCiAgICAiRFAwM18wMDIxUEUiLCAjIFBlcmNlbnQgdXNpbmcgcHVibGljIHRyYW5zcG9ydGF0aW9uIChleGNsdWRpbmcgdGF4aWNhYikNCiAgICAiRFAwM18wMDI0UEUiICAjIFBlcmNlbnQgd29ya2luZyBmcm9tIGhvbWUNCiAgKSwNCiAgeWVhciA9IDIwMTksDQogIHN1cnZleSA9ICJhY3M1Ig0KKQ0KDQpjb21tdXRpbmdfZGF0YSA8LSBjb21tdXRpbmdfZGF0YSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHZhcmlhYmxlLCB2YWx1ZXNfZnJvbSA9IGVzdGltYXRlKSAlPiUNCiAgZ3JvdXBfYnkoR0VPSUQpICU+JQ0KICBzdW1tYXJpemUoDQogICAgbWVhbl90cmF2ZWxfdGltZSA9IG1lYW4oRFAwM18wMDI1LCBuYS5ybSA9IFRSVUUpLA0KICAgIHBjdF9wdWJsaWNfdHJhbnNwb3J0ID0gbWVhbihEUDAzXzAwMjFQLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBjdF93b3JrX2Zyb21faG9tZSA9IG1lYW4oRFAwM18wMDI0UCwgbmEucm0gPSBUUlVFKQ0KICApDQoNCiMgUmV0cmlldmUgaG91c2Vob2xkIGNoYXJhY3RlcmlzdGljcyBkYXRhIGJ5IFpJUCBjb2RlDQpob3VzZWhvbGRfZGF0YSA8LSBnZXRfYWNzKA0KICBnZW9ncmFwaHkgPSAiemN0YSIsDQogIHZhcmlhYmxlcyA9IGMoDQogICAgIkRQMDJfMDAxNkUiLCAjIEF2ZXJhZ2UgaG91c2Vob2xkIHNpemUNCiAgICAiRFAwMl8wMDA3UEUiLCAjIFBlcmNlbnQgb2YgaG91c2Vob2xkcyB3aXRoIG93biBjaGlsZHJlbiB1bmRlciAxOCB5ZWFycw0KICAgICJEUDAyXzAwMDlQRSIsICMgUGVyY2VudCBvZiBzaW5nbGUtcGFyZW50IGhvdXNlaG9sZHMNCiAgICAiRFAwNV8wMDE4RSIgICMgTWVkaWFuIGFnZQ0KICApLA0KICB5ZWFyID0gMjAxOSwNCiAgc3VydmV5ID0gImFjczUiDQopDQoNCmhvdXNlaG9sZF9kYXRhIDwtIGhvdXNlaG9sZF9kYXRhICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdmFyaWFibGUsIHZhbHVlc19mcm9tID0gZXN0aW1hdGUpICU+JQ0KICBncm91cF9ieShHRU9JRCkgJT4lDQogIHN1bW1hcml6ZSgNCiAgICBhdmdfaG91c2Vob2xkX3NpemUgPSBtZWFuKERQMDJfMDAxNiwgbmEucm0gPSBUUlVFKSwNCiAgICBwY3RfaG91c2Vob2xkc193aXRoX2NoaWxkcmVuID0gbWVhbihEUDAyXzAwMDdQLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBjdF9zaW5nbGVfcGFyZW50X2hvdXNlaG9sZHMgPSBtZWFuKERQMDJfMDAwOVAsIG5hLnJtID0gVFJVRSksDQogICAgbWVkaWFuX2FnZSA9IG1lYW4oRFAwNV8wMDE4LCBuYS5ybSA9IFRSVUUpDQogICkNCg0KIyBNZXJnZSBhbGwgdGhlIENlbnN1cyBkYXRhIHdpdGggdGhlIGhvdXNpbmcgZGF0YQ0KaG91c2luZ19kYXRhX3dpdGhfY2Vuc3VzIDwtIGhvdXNpbmdfZGF0YSAlPiUNCiAgbGVmdF9qb2luKGZfaW5jb21lLCBieSA9ICJaSVAiKSAlPiUNCiAgbGVmdF9qb2luKGZfcG9wLCBieSA9ICJaSVAiKSAlPiUNCiAgbGVmdF9qb2luKGVkdWNhdGlvbl9kYXRhLCBieSA9IGMoIlpJUCIgPSAiR0VPSUQiKSkgJT4lDQogIGxlZnRfam9pbihlbXBsb3ltZW50X2RhdGEsIGJ5ID0gYygiWklQIiA9ICJHRU9JRCIpKSAlPiUNCiAgbGVmdF9qb2luKGhvdXNpbmdfY2hhcnNfZGF0YSwgYnkgPSBjKCJaSVAiID0gIkdFT0lEIikpICU+JQ0KICBsZWZ0X2pvaW4oY29tbXV0aW5nX2RhdGEsIGJ5ID0gYygiWklQIiA9ICJHRU9JRCIpKSAlPiUNCiAgbGVmdF9qb2luKGhvdXNlaG9sZF9kYXRhLCBieSA9IGMoIlpJUCIgPSAiR0VPSUQiKSklPiUNCiAgICBzZWxlY3QoDQogICAgZGF0ZSwgcHJpY2UsIGJlZHJvb21zLCBiYXRocm9vbXMsIHNxZnRfbGl2aW5nLCBzcWZ0X2xvdCwgZmxvb3JzLCB3YXRlcmZyb250LCB2aWV3LCBjb25kaXRpb24sDQogICAgc3FmdF9hYm92ZSwgc3FmdF9iYXNlbWVudCwgeXJfYnVpbHQsIHlyX3Jlbm92YXRlZCwgc3RyZWV0LCBjaXR5LCBjb3VudHJ5LCBaSVAsDQogICAgaG91c2VfYWdlLCB5ZWFyc19zaW5jZV9yZW5vdmF0aW9uLCB0b3RhbF9yb29tcywgbGl2aW5nX2xvdF9yYXRpbywgaGFzX2Jhc2VtZW50LCByZW5vdmF0ZWQsDQogICAgZmxvb3JfYXJlYV9yYXRpbywgb3V0ZG9vcl9zcGFjZSwgc2Vhc29uLA0KICAgIG1lZGlhbl9pbmNvbWUsIHBvcHVsYXRpb24sDQogICAgcGN0X2hpZ2hfc2Nob29sX2dyYWQsIHBjdF9iYWNoZWxvcnNfZGVncmVlLA0KICAgIHBjdF91bmVtcGxveWVkLCBwY3RfbWdtdF9vY2N1cGF0aW9uLCBwY3Rfc2VydmljZV9vY2N1cGF0aW9uLCBwY3Rfc2FsZXNfb2ZmaWNlX29jY3VwYXRpb24sIHBjdF9jb25zdHJ1Y3Rpb25fb2NjdXBhdGlvbiwNCiAgICBwY3Rfb3duZXJfb2NjdXBpZWQsIG1lZGlhbl95ZWFyX2J1aWx0LCBwY3RfY2VudHJhbF9haXIsDQogICAgbWVhbl90cmF2ZWxfdGltZSwgcGN0X3B1YmxpY190cmFuc3BvcnQsIHBjdF93b3JrX2Zyb21faG9tZSwNCiAgICBhdmdfaG91c2Vob2xkX3NpemUsIHBjdF9ob3VzZWhvbGRzX3dpdGhfY2hpbGRyZW4sIHBjdF9zaW5nbGVfcGFyZW50X2hvdXNlaG9sZHMsIG1lZGlhbl9hZ2UNCiAgKSU+JQ0KICAjd3JpdGVfY3N2KCdEYXRhX3dpdGhfY2Vuc3VzLmNzdicpDQoNCg0KDQpgYGANCg0KIyBQcmVwcm9jZXNzaW5nDQpgYGB7cn0NCmhvdXNpbmdfZGF0YV93aXRoX2NlbnN1cyA8LSByZWFkX2NzdignRGF0YV93aXRoX2NlbnN1cy5jc3YnKQ0KDQojIENoZWNrIGZvciBtdWx0aWNvbGxpbmVhcml0eSB1c2luZyBWSUYNCmhvdXNpbmdfZGF0YV9maW5hbF9udW0gPC0gaG91c2luZ19kYXRhX2Zvcl9tb2RlbCAlPiUNCiAgc2VsZWN0X2lmKGlzLm51bWVyaWMpDQoNCnZpZl9tb2RlbCA8LSBsbShwcmljZSB+IC4sIGRhdGEgPSBob3VzaW5nX2RhdGFfZmluYWxfbnVtKQ0KdmlmX3ZhbHVlcyA8LSB2aWYodmlmX21vZGVsKQ0KcHJpbnQodmlmX3ZhbHVlcykNCg0KIyBDb3JyZWxhdGlvbiBtYXRyaXgNCmNvcl9tYXRyaXggPC0gY29yKGhvdXNpbmdfZGF0YV9maW5hbF9udW0pDQojd3JpdGUuY3N2KGNvcl9tYXRyaXgsIGZpbGUgPSAiY29ycmVsYXRpb25fbWF0cml4LmNzdiIsIHJvdy5uYW1lcyA9IFRSVUUpDQpjb3JycGxvdDo6Y29ycnBsb3QoY29yX21hdHJpeCwgbWV0aG9kID0gImNpcmNsZSIpDQoNCiMgRmluZCBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMNCmhpZ2hfY29ycmVsYXRpb24gPC0gZmluZENvcnJlbGF0aW9uKGNvcl9tYXRyaXgsIGN1dG9mZiA9IDAuNzUpDQpoaWdoX2NvcnJlbGF0aW9uX25hbWVzIDwtIG5hbWVzKGhvdXNpbmdfZGF0YV9maW5hbF9udW0pW2hpZ2hfY29ycmVsYXRpb25dDQpwcmludChoaWdoX2NvcnJlbGF0aW9uX25hbWVzKQ0KDQojIFJlbW92ZSBoaWdobHkgY29ycmVsYXRlZCB2YXJpYWJsZXMgKG9uZSBmcm9tIGVhY2ggcGFpcikNCmhvdXNpbmdfZGF0YV9yZWR1Y2VkIDwtIGhvdXNpbmdfZGF0YV9maW5hbF9udW1bLCAtaGlnaF9jb3JyZWxhdGlvbl0NCmBgYA0KDQpgYGB7cn0NCiMgUHJlcGFyZSBkYXRhIGZvciBtb2RlbGluZw0KaG91c2luZ19kYXRhX2Zvcl9tb2RlbCA8LSBob3VzaW5nX2RhdGFfd2l0aF9jZW5zdXMgJT4lDQogIHNlbGVjdCgNCiAgICAteXJfYnVpbHQsIC15cl9yZW5vdmF0ZWQsIC1mbG9vcl9hcmVhX3JhdGlvLCAtY291bnRyeSwNCiAgICAtU3RhdGUsIC1zdHJlZXQsIC1kYXRlLCAtdG90YWxfcm9vbXMsIC1vdXRkb29yX3NwYWNlLA0KICAgIC1zcWZ0X2Jhc2VtZW50LCAtc3FmdF9hYm92ZSwgLXBjdF9tZ210X29jY3VwYXRpb24sIC1wY3RfaGlnaF9zY2hvb2xfZ3JhZCwgLXBjdF9wdWJsaWNfdHJhbnNwb3J0LCAtbWVkaWFuX2luY29tZSwgLXBjdF9vd25lcl9vY2N1cGllZCwgLXBjdF9jZW50cmFsX2FpciwgLXBjdF9iYWNoZWxvcnNfZGVncmVlLCAtY2l0eSwgLWNvdW50cnkpDQoNCiMgVHJhbnNmb3JtIHByaWNlIHRvIGxvZyBzY2FsZQ0KaG91c2luZ19kYXRhX2Zvcl9tb2RlbCA8LSBob3VzaW5nX2RhdGFfZm9yX21vZGVsICU+JQ0KICBmaWx0ZXIocHJpY2UgIT0gMCwgIWlzLm5hKG1lYW5fdHJhdmVsX3RpbWUpKSU+JQ0KICBtdXRhdGUobG9nX3ByaWNlID0gbG9nKHByaWNlKSkNCg0KZ2dwbG90KGhvdXNpbmdfZGF0YV9mb3JfbW9kZWwsIGFlcyh4PWxvZ19wcmljZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz0zMCwgZmlsbD0ibGlnaHRibHVlIiwgY29sb3I9J2JsYWNrJywgYWxwaGE9MC43KSArDQogIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiBMb2ctdHJhbnNmb3JtZWQgUHJpY2VzIikgKw0KICB4bGFiKCJMb2cgb2YgUHJpY2UiKSArDQogIHlsYWIoIkZyZXF1ZW5jeSIpICMgbXVjaCBiZXR0ZXIgc2hhcGUgdGhhbiBlYXJsaWVyDQoNCmBgYA0KV2UgZGVjaWRlZCB0byBwZXJmb3JtIGEgbG9nIHRyYW5zZm9ybWF0aW9uIG9uIHRoZSBwcmljZSBmb3IgbWFrZSB0aGUgZGlzdHJpYnV0aW9uIG1vcmUgbm9ybWFsDQoNCg0KYGBge3J9DQojIEJhc2ljIHByZXByb2Nlc3NpbmcgYXBwbGljYWJsZSB0byBhbGwgbW9kZWxzDQpob3VzaW5nX2RhdGFfZm9yX21vZGVsIDwtIGhvdXNpbmdfZGF0YV93aXRoX2NlbnN1cyAlPiUNCiAgc2VsZWN0KC15cl9idWlsdCwgLXlyX3Jlbm92YXRlZCwgLWZsb29yX2FyZWFfcmF0aW8sIC1jb3VudHJ5LCANCiAgICAgICAgIC1TdGF0ZSwgLXN0cmVldCwgLWRhdGUsIC10b3RhbF9yb29tcywgLW91dGRvb3Jfc3BhY2UsDQogICAgICAgICAtc3FmdF9iYXNlbWVudCwgLXNxZnRfYWJvdmUsIC1wY3RfbWdtdF9vY2N1cGF0aW9uLCANCiAgICAgICAgIC1wY3RfaGlnaF9zY2hvb2xfZ3JhZCwgLXBjdF9wdWJsaWNfdHJhbnNwb3J0LCAtbWVkaWFuX2luY29tZSwgDQogICAgICAgICAtcGN0X293bmVyX29jY3VwaWVkLCAtcGN0X2NlbnRyYWxfYWlyLCAtcGN0X2JhY2hlbG9yc19kZWdyZWUsIC1jaXR5LCAtY291bnRyeSkgJT4lDQogIGZpbHRlcihwcmljZSAhPSAwLCAhaXMubmEobWVhbl90cmF2ZWxfdGltZSkpICU+JQ0KICBtdXRhdGUobG9nX3ByaWNlID0gbG9nKHByaWNlKSkgJT4lDQogIHNlbGVjdCgtcHJpY2UpJT4lDQogIG11dGF0ZSgNCiAgICBzZWFzb24gPSBhcy5mYWN0b3Ioc2Vhc29uKSwNCiAgICB3YXRlcmZyb250ID0gYXMuZmFjdG9yKHdhdGVyZnJvbnQpLA0KICAgIHZpZXcgPSBhcy5mYWN0b3IodmlldyksDQogICAgaGFzX2Jhc2VtZW50ID0gYXMuZmFjdG9yKGhhc19iYXNlbWVudCksDQogICAgcmVub3ZhdGVkID0gYXMuZmFjdG9yKHJlbm92YXRlZCkNCiAgKQ0KIyBTZXBhcmF0ZSBkYXRhIGZvciBtaXhlZC1lZmZlY3RzIG1vZGVsIHRvIGluY2x1ZGUgWklQDQptZV9kYXRhIDwtIGhvdXNpbmdfZGF0YV9mb3JfbW9kZWwgJT4lDQogIG11dGF0ZShaSVAgPSBhcy5mYWN0b3IoWklQKSkNCg0KIyBEYXRhIGZvciBvdGhlciBtb2RlbHMgd2hlcmUgWklQIGlzIG5vdCByZXF1aXJlZA0Kb3RoZXJfbW9kZWxzX2RhdGEgPC0gc2VsZWN0KGhvdXNpbmdfZGF0YV9mb3JfbW9kZWwsIC1aSVApDQoNCiMgQ3JlYXRlIHJlY2lwZXMgZm9yIG90aGVyIG1vZGVscyBhbmQgbWl4ZWQtZWZmZWN0cyBtb2RlbA0KcmVjX290aGVyIDwtIHJlY2lwZShsb2dfcHJpY2UgfiAuLCBkYXRhID0gb3RoZXJfbW9kZWxzX2RhdGEpICU+JQ0KICBzdGVwX25vcm1hbGl6ZShhbGxfbnVtZXJpY19wcmVkaWN0b3JzKCksIC1hbGxfb3V0Y29tZXMoKSkgJT4lDQogIHN0ZXBfZHVtbXkoYWxsX25vbWluYWxfcHJlZGljdG9ycygpLCAtYWxsX291dGNvbWVzKCkpDQoNCnJlY19tZSA8LSByZWNpcGUobG9nX3ByaWNlIH4gLiwgZGF0YSA9IG1lX2RhdGEpICU+JQ0KICBzdGVwX25vcm1hbGl6ZShhbGxfbnVtZXJpY19wcmVkaWN0b3JzKCksIC1hbGxfb3V0Y29tZXMoKSkgJT4lDQogIHN0ZXBfZHVtbXkoYWxsX25vbWluYWxfcHJlZGljdG9ycygpLCAtYWxsX291dGNvbWVzKCksIC1aSVApDQoNCiMgUHJlcHJvY2VzcyB0aGUgZGF0YSBmb3Igc3RlcHdpc2UgQUlDDQpwcmVwcm9jZXNzZWRfZGF0YSA8LSBwcmVwKHJlY19vdGhlcikgJT4lIGJha2UobmV3X2RhdGEgPSBOVUxMKQ0KcHJlcHJvY2Vzc2VkX21lX2RhdGEgPC0gcHJlcChyZWNfbWUpICU+JSBiYWtlKG5ld19kYXRhID0gTlVMTCkNCg0KIyBQZXJmb3JtIHN0ZXB3aXNlIEFJQyBmZWF0dXJlIHNlbGVjdGlvbg0KZnVsbF9tb2RlbCA8LSBsbShsb2dfcHJpY2UgfiAuLCBkYXRhID0gcHJlcHJvY2Vzc2VkX2RhdGEpDQpyZWR1Y2VkX21vZGVsIDwtIHN0ZXBBSUMoZnVsbF9tb2RlbCwgZGlyZWN0aW9uID0gImJvdGgiLCB0cmFjZSA9IFQpDQoNCiMgUHJpbnQgdGhlIHNlbGVjdGVkIHZhcmlhYmxlcw0Kc2VsZWN0ZWRfdmFycyA8LSBuYW1lcyhyZWR1Y2VkX21vZGVsJGNvZWZmaWNpZW50cylbLTFdDQpwcmludChwYXN0ZSgiU2VsZWN0ZWQgdmFyaWFibGVzOiIsIHBhc3RlKHNlbGVjdGVkX3ZhcnMsIGNvbGxhcHNlID0gIiwgIikpKQ0KDQojIFVwZGF0ZSB0aGUgZGF0YSBmb3IgbW9kZWxzIChleGNlcHQgbmV1cmFsIG5ldHdvcmspIHdpdGggdGhlIHNlbGVjdGVkIHZhcmlhYmxlcw0KbG1fcmZfZGF0YSA8LSBwcmVwcm9jZXNzZWRfZGF0YSAlPiUNCiAgc2VsZWN0KGxvZ19wcmljZSwgYWxsX29mKHNlbGVjdGVkX3ZhcnMpKQ0KDQptZV9kYXRhX3NlbGVjdGVkIDwtIHByZXByb2Nlc3NlZF9tZV9kYXRhICU+JQ0KICBzZWxlY3QobG9nX3ByaWNlLCBaSVAsIGFsbF9vZihzZWxlY3RlZF92YXJzKSkNCg0KIyBDaGVjayBmb3IgbXVsdGljb2xsaW5lYXJpdHkgdXNpbmcgVklGDQp2aWZfbW9kZWwgPC0gbG0obG9nX3ByaWNlIH4gLiwgZGF0YSA9IGxtX3JmX2RhdGEpDQp2aWZfdmFsdWVzIDwtIHZpZih2aWZfbW9kZWwpDQpwcmludCh2aWZfdmFsdWVzKQ0KYGBgDQpWSUYgaXMgYmVsbG93IDUgZm9yIGFsbCBwcmVkaWN0b3JzIHNvIHdlIGhhdmUgYWRkcmVzc2VkIGNvbGluZWFyaXR5IHByb2JsZW1zLg0KDQpgYGB7cn0NCmxpYnJhcnkobXVsdGlsZXZlbG1vZCkNCnNldC5zZWVkKDMzMykNCg0KIyMgQ3JlYXRlIGNyb3NzLXZhbGlkYXRpb24gZm9sZHMgZm9yIGVhY2ggbW9kZWwgdHlwZQ0KY3ZfZm9sZHNfbG1fcmYgPC0gdmZvbGRfY3YobG1fcmZfZGF0YSwgdiA9IDEwKQ0KY3ZfZm9sZHNfbWUgPC0gdmZvbGRfY3YobWVfZGF0YV9zZWxlY3RlZCwgdiA9IDEwKQ0KY3ZfZm9sZHNfbm4gPC0gdmZvbGRfY3YoaG91c2luZ19kYXRhX2Zvcl9tb2RlbCwgdiA9IDEwKQ0KDQojIENyZWF0ZSByZWNpcGVzIGZvciBtb2RlbHMNCnJlY19sbV9yZiA8LSByZWNpcGUobG9nX3ByaWNlIH4gLiwgZGF0YSA9IGxtX3JmX2RhdGEpDQpyZWNfbWUgPC0gcmVjaXBlKGxvZ19wcmljZSB+IC4sIGRhdGEgPSBtZV9kYXRhX3NlbGVjdGVkKQ0KcmVjX25uIDwtIHJlY2lwZShsb2dfcHJpY2UgfiAuLCBkYXRhID0gaG91c2luZ19kYXRhX2Zvcl9tb2RlbCkgJT4lDQogIHN0ZXBfbm9ybWFsaXplKGFsbF9udW1lcmljX3ByZWRpY3RvcnMoKSwgLWFsbF9vdXRjb21lcygpKSAlPiUNCiAgc3RlcF9kdW1teShhbGxfbm9taW5hbF9wcmVkaWN0b3JzKCksIC1hbGxfb3V0Y29tZXMoKSkNCg0KIyBEZWZpbmUgdGhlIG1vZGVscw0KbG1fbW9kZWwgPC0gbGluZWFyX3JlZygpICU+JSBzZXRfZW5naW5lKCJsbSIpICU+JSBzZXRfbW9kZSgicmVncmVzc2lvbiIpDQpyZl9tb2RlbCA8LSByYW5kX2ZvcmVzdCh0cmVlcyA9IDEwMDApICU+JSBzZXRfbW9kZSgicmVncmVzc2lvbiIpICU+JSBzZXRfZW5naW5lKCJyYW5nZXIiLCBpbXBvcnRhbmNlID0gImltcHVyaXR5IikNCm1lX21vZGVsIDwtIGxpbmVhcl9yZWcoKSAlPiUgc2V0X2VuZ2luZSgibG1lciIpICU+JSBzZXRfbW9kZSgicmVncmVzc2lvbiIpDQpubl9tb2RlbCA8LSBtbHAoaGlkZGVuX3VuaXRzID0gMTApICU+JSBzZXRfZW5naW5lKCJubmV0IiwgbGlub3V0ID0gVFJVRSwgdHJhY2UgPSBUKSAlPiUgc2V0X21vZGUoInJlZ3Jlc3Npb24iKQ0KDQojIENyZWF0ZSB3b3JrZmxvd3MNCmxtX3dvcmtmbG93IDwtIHdvcmtmbG93KCkgJT4lIGFkZF9yZWNpcGUocmVjX2xtX3JmKSAlPiUgYWRkX21vZGVsKGxtX21vZGVsKQ0KcmZfd29ya2Zsb3cgPC0gd29ya2Zsb3coKSAlPiUgYWRkX3JlY2lwZShyZWNfbG1fcmYpICU+JSBhZGRfbW9kZWwocmZfbW9kZWwpDQptZV93b3JrZmxvdyA8LSB3b3JrZmxvdygpICU+JSBhZGRfcmVjaXBlKHJlY19tZSkgJT4lIGFkZF9tb2RlbChtZV9tb2RlbCwgZm9ybXVsYSA9IGxvZ19wcmljZSB+IC4gKyAoMSB8IFpJUCkpDQpubl93b3JrZmxvdyA8LSB3b3JrZmxvdygpICU+JSBhZGRfcmVjaXBlKHJlY19ubikgJT4lIGFkZF9tb2RlbChubl9tb2RlbCkNCg0KIyBQZXJmb3JtIGNyb3NzLXZhbGlkYXRpb24NCmxtX2N2X3Jlc3VsdHMgPC0gZml0X3Jlc2FtcGxlcyhsbV93b3JrZmxvdywgcmVzYW1wbGVzID0gY3ZfZm9sZHNfbG1fcmYsIG1ldHJpY3MgPSBtZXRyaWNfc2V0KHJtc2UsIG1hZSwgcnNxKSkNCnJmX2N2X3Jlc3VsdHMgPC0gZml0X3Jlc2FtcGxlcyhyZl93b3JrZmxvdywgcmVzYW1wbGVzID0gY3ZfZm9sZHNfbG1fcmYsIG1ldHJpY3MgPSBtZXRyaWNfc2V0KHJtc2UsIG1hZSwgcnNxKSkNCm1lX2N2X3Jlc3VsdHMgPC0gZml0X3Jlc2FtcGxlcyhtZV93b3JrZmxvdywgcmVzYW1wbGVzID0gY3ZfZm9sZHNfbWUsIG1ldHJpY3MgPSBtZXRyaWNfc2V0KHJtc2UsIG1hZSwgcnNxKSkNCm5uX2N2X3Jlc3VsdHMgPC0gZml0X3Jlc2FtcGxlcyhubl93b3JrZmxvdywgcmVzYW1wbGVzID0gY3ZfZm9sZHNfbm4sIG1ldHJpY3MgPSBtZXRyaWNfc2V0KHJtc2UsIG1hZSwgcnNxKSkNCg0KIyBTdW1tYXJpemUgYW5kIGNvbXBhcmUgdGhlIGNyb3NzLXZhbGlkYXRpb24gcmVzdWx0cw0KY3ZfcmVzdWx0cyA8LSBiaW5kX3Jvd3MoDQogIGNvbGxlY3RfbWV0cmljcyhsbV9jdl9yZXN1bHRzKSAlPiUgbXV0YXRlKG1vZGVsID0gIkxpbmVhciBSZWdyZXNzaW9uIiksDQogIGNvbGxlY3RfbWV0cmljcyhyZl9jdl9yZXN1bHRzKSAlPiUgbXV0YXRlKG1vZGVsID0gIlJhbmRvbSBGb3Jlc3QiKSwNCiAgY29sbGVjdF9tZXRyaWNzKG1lX2N2X3Jlc3VsdHMpICU+JSBtdXRhdGUobW9kZWwgPSAiTWl4ZWQgRWZmZWN0cyIpLA0KICBjb2xsZWN0X21ldHJpY3Mobm5fY3ZfcmVzdWx0cykgJT4lIG11dGF0ZShtb2RlbCA9ICJOZXVyYWwgTmV0d29yayIpDQopDQoNCmN2X3N1bW1hcnkgPC0gY3ZfcmVzdWx0cyAlPiUNCiAgZ3JvdXBfYnkobW9kZWwsIC5tZXRyaWMpICU+JQ0KICBzdW1tYXJpemUoDQogICAgbWVhbl92YWx1ZSA9IG1lYW4obWVhbiwgbmEucm0gPSBUUlVFKSwNCiAgICBzdGRfZXJyb3IgPSBtZWFuKHN0ZF9lcnIsIG5hLnJtID0gVFJVRSksDQogICAgLmdyb3VwcyA9ICdkcm9wJw0KICApICU+JQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gLm1ldHJpYywgdmFsdWVzX2Zyb20gPSBjKG1lYW5fdmFsdWUsIHN0ZF9lcnJvcikpDQoNCnByaW50KGN2X3N1bW1hcnkpDQojIFNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyBmb3IgZWFjaCBtb2RlbCB0eXBlDQpkYXRhX3NwbGl0X2xtX3JmIDwtIGluaXRpYWxfc3BsaXQobG1fcmZfZGF0YSwgcHJvcCA9IDAuOCkNCnRyYWluX2RhdGFfbG1fcmYgPC0gdHJhaW5pbmcoZGF0YV9zcGxpdF9sbV9yZikNCnRlc3RfZGF0YV9sbV9yZiA8LSB0ZXN0aW5nKGRhdGFfc3BsaXRfbG1fcmYpDQoNCmRhdGFfc3BsaXRfbWUgPC0gaW5pdGlhbF9zcGxpdChtZV9kYXRhX3NlbGVjdGVkLCBwcm9wID0gMC44KQ0KdHJhaW5fZGF0YV9tZSA8LSB0cmFpbmluZyhkYXRhX3NwbGl0X21lKQ0KdGVzdF9kYXRhX21lIDwtIHRlc3RpbmcoZGF0YV9zcGxpdF9tZSkNCg0KZGF0YV9zcGxpdF9ubiA8LSBpbml0aWFsX3NwbGl0KGhvdXNpbmdfZGF0YV9mb3JfbW9kZWwsIHByb3AgPSAwLjgpDQp0cmFpbl9kYXRhX25uIDwtIHRyYWluaW5nKGRhdGFfc3BsaXRfbm4pDQp0ZXN0X2RhdGFfbm4gPC0gdGVzdGluZyhkYXRhX3NwbGl0X25uKQ0KDQojIEZpdCBhbmQgZXZhbHVhdGUgZWFjaCBtb2RlbCB1c2luZyBsYXN0X2ZpdCgpDQpsbV9sYXN0X2ZpdCA8LSBsYXN0X2ZpdChsbV93b3JrZmxvdywgZGF0YV9zcGxpdF9sbV9yZikNCnJmX2xhc3RfZml0IDwtIGxhc3RfZml0KHJmX3dvcmtmbG93LCBkYXRhX3NwbGl0X2xtX3JmKQ0KbWVfbGFzdF9maXQgPC0gbGFzdF9maXQobWVfd29ya2Zsb3csIGRhdGFfc3BsaXRfbWUpDQpubl9sYXN0X2ZpdCA8LSBsYXN0X2ZpdChubl93b3JrZmxvdywgZGF0YV9zcGxpdF9ubikNCg0KIyBFeHRyYWN0IHByZWRpY3Rpb25zIGFuZCBtZXRyaWNzIGZvciBlYWNoIG1vZGVsDQpsbV9yZXN1bHRzIDwtIGxtX2xhc3RfZml0ICU+JSBjb2xsZWN0X3ByZWRpY3Rpb25zKCkgJT4lIG11dGF0ZShtb2RlbCA9ICJMaW5lYXIgUmVncmVzc2lvbiIpDQpyZl9yZXN1bHRzIDwtIHJmX2xhc3RfZml0ICU+JSBjb2xsZWN0X3ByZWRpY3Rpb25zKCkgJT4lIG11dGF0ZShtb2RlbCA9ICJSYW5kb20gRm9yZXN0IikNCm1lX3Jlc3VsdHMgPC0gbWVfbGFzdF9maXQgJT4lIGNvbGxlY3RfcHJlZGljdGlvbnMoKSAlPiUgbXV0YXRlKG1vZGVsID0gIk1peGVkIEVmZmVjdHMiKQ0Kbm5fcmVzdWx0cyA8LSBubl9sYXN0X2ZpdCAlPiUgY29sbGVjdF9wcmVkaWN0aW9ucygpICU+JSBtdXRhdGUobW9kZWwgPSAiTmV1cmFsIE5ldHdvcmsiKQ0KDQojIENvbWJpbmUgdGhlIHJlc3VsdHMNCnJlc3VsdHMgPC0gYmluZF9yb3dzKGxtX3Jlc3VsdHMsIHJmX3Jlc3VsdHMsIG1lX3Jlc3VsdHMsIG5uX3Jlc3VsdHMpDQoNCiMgUGxvdCBwcmVkaWN0ZWQgdnMuIGFjdHVhbCBmb3IgZWFjaCBtb2RlbCB3aXRoIFJeMg0KcmVzdWx0cyAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gbG9nX3ByaWNlLCB5ID0gLnByZWQsIGNvbG9yID0gbW9kZWwpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSAxLCBpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogIGZhY2V0X3dyYXAofiBtb2RlbCwgc2NhbGVzID0gImZyZWUiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiUHJlZGljdGVkIHZzLiBBY3R1YWwgSG91c2UgUHJpY2VzIiwNCiAgICB4ID0gIkFjdHVhbCBQcmljZSIsDQogICAgeSA9ICJQcmVkaWN0ZWQgUHJpY2UiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2VvbV90ZXh0KA0KICAgIGRhdGEgPSByZXN1bHRzICU+JQ0KICAgICAgZ3JvdXBfYnkobW9kZWwpICU+JQ0KICAgICAgc3VtbWFyaXNlKHJzcSA9IGNvcihsb2dfcHJpY2UsIC5wcmVkKV4yKSwNCiAgICBhZXMoeCA9IEluZiwgeSA9IC1JbmYsIGxhYmVsID0gc3ByaW50ZigiUl4yID0gJS4zZiIsIHJzcSkpLA0KICAgIGhqdXN0ID0gMS4xLA0KICAgIHZqdXN0ID0gLTEuMQ0KICApDQpgYGANClRoZSBtb2RlbCB3aXRoIHRoZSBiZXN0IHByZWRpY3RpdmUgYWNjdXJhY3kgd2FzIHRoZSByYW5kb20gZm9yZXN0IG1vZGVsLCBmb2xsb3dlZCBieSB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwsIG5ldXJhbCBuZXR3b3JrIG1vZGVsLCBhbmQgbWl4ZWQgZWZmZWN0cyBtb2RlbC4gSXNzdWVzIHdpdGggbW9kZWwgZml0dGluZyBhcmUgY2xlYXIgd2l0aCB0aGUgbWl4ZWQgZWZmZWN0cyBtb2RlbCBkdWUgdG8gdGhlIHN0cm9uZyBsZWZ0IHNrZXcgaW4gdGhlIGVycm9yLg0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgdHJhaW5lZCBSYW5kb20gRm9yZXN0IG1vZGVsIGZyb20gdGhlIGxhc3RfZml0IG9iamVjdA0KcmZfbW9kZWwgPC0gcmZfbGFzdF9maXQgJT4lIA0KICBleHRyYWN0X2ZpdF9lbmdpbmUoKQ0KDQojIEV4dHJhY3QgdmFyaWFibGUgaW1wb3J0YW5jZSBmcm9tIHRoZSBSYW5kb20gRm9yZXN0IG1vZGVsDQpyZl9pbXBvcnRhbmNlIDwtIHJmX21vZGVsICU+JSANCiAgcmFuZ2VyOjppbXBvcnRhbmNlKCkNCg0KIyBQbG90IHZhcmlhYmxlIGltcG9ydGFuY2UNCmdncGxvdChkYXRhLmZyYW1lKHZhcmlhYmxlID0gbmFtZXMocmZfaW1wb3J0YW5jZSksIGltcG9ydGFuY2UgPSByZl9pbXBvcnRhbmNlKSwgDQogICAgICAgYWVzKHggPSByZW9yZGVyKHZhcmlhYmxlLCBpbXBvcnRhbmNlKSwgeSA9IGltcG9ydGFuY2UsIGZpbGwgPSBpbXBvcnRhbmNlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHggPSAiVmFyaWFibGUiLCB5ID0gIkltcG9ydGFuY2UiLCB0aXRsZSA9ICJSYW5kb20gRm9yZXN0IFZhcmlhYmxlIEltcG9ydGFuY2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANClNRRlQgb2YgbGl2aW5nIHNwYWNlIGZvbGxvd2VkIGJ5IG9jY3VwYXRpb24gcHJldmFsZW5jZSBhcHBlYXIgdG8gYmUgbW9zdCBwcmVkaWN0aXZlIG9mIGhvdXNpbmcgcHJpY2UuIFRoaW5ncyBsaWtlIHZpZXcsIGFuZCBjb21tdXRpbmcgdGltZSBhcmUgc3VwcmlzaW5nbHkgbXVjaCBsb3dlciBieSBjb21wYXJpc29uIQ0KYGBge3J9DQojIEV4dHJhY3QgdGhlIHRyYWluZWQgbW9kZWxzIGZyb20gdGhlIGxhc3RfZml0IG9iamVjdHMNCmxtX21vZGVsIDwtIGxtX2xhc3RfZml0ICU+JSBleHRyYWN0X2ZpdF9lbmdpbmUoKQ0KcmZfbW9kZWwgPC0gcmZfbGFzdF9maXQgJT4lIGV4dHJhY3RfZml0X2VuZ2luZSgpDQptZV9tb2RlbCA8LSBtZV9sYXN0X2ZpdCAlPiUgZXh0cmFjdF9maXRfZW5naW5lKCkNCm5uX21vZGVsIDwtIG5uX2xhc3RfZml0ICU+JSBleHRyYWN0X2ZpdF9lbmdpbmUoKQ0KDQojIEV4dHJhY3QgY29lZmZpY2llbnRzIGZyb20gdGhlIExpbmVhciBSZWdyZXNzaW9uIG1vZGVsDQpsbV9jb2VmIDwtIGNvZWYobG1fbW9kZWwpDQpsbV9jb2VmX2RmIDwtIGRhdGEuZnJhbWUodmFyaWFibGUgPSBuYW1lcyhsbV9jb2VmKSwgY29lZmZpY2llbnQgPSBsbV9jb2VmKQ0KDQojIFBsb3QgY29lZmZpY2llbnRzIGZvciB0aGUgTGluZWFyIFJlZ3Jlc3Npb24gbW9kZWwNCmdncGxvdChsbV9jb2VmX2RmLCBhZXMoeCA9IHJlb3JkZXIodmFyaWFibGUsIGNvZWZmaWNpZW50KSwgeSA9IGNvZWZmaWNpZW50LCBmaWxsID0gY29lZmZpY2llbnQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnMoeCA9ICJWYXJpYWJsZSIsIHkgPSAiQ29lZmZpY2llbnQiLCB0aXRsZSA9ICJMaW5lYXIgUmVncmVzc2lvbiBDb2VmZmljaWVudHMiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpDb252ZXJzbHkgaW4gdGhlIGxpbmVhciBtb2RlbCwgaGF2aW5nIGFuIG91dHN3dGFuZGluZyB2aWV3IHNlZW1zIHRvIGJlIHRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3IuIFZlcnkgZGlmZmVyZW50IGFwcG9yYWNoZXMhDQoNCg==