Bike sharing has become a popular transportation option in many
cities around the world. With increasing environmental awareness and the
need for sustainable transportation options, bike sharing systems have
seen significant growth. However, for these systems to operate
efficiently, it is crucial to predict the demand for bikes at different
stations and locations.
The goal of this project is to develop a predictive model that can
estimate bike sharing demand based on various factors such as weather,
time of day, day of the week, and special events. Using data analytics
and machine learning techniques, the project aims to provide a tool that
helps bike sharing system operators optimize bike distribution and
availability, thereby improving user experience and operational
efficiency.
1.Libraries
library(tidymodels) #For modeling and machine learning
library(tidyverse) # Share common data representations and 'API' design
library(stringr) # Consistent wrapper for common string operations
library(readr) # Read rectangular text data
library(broom) # Convert statistical objects into tidy tibbles
library(dplyr) # A grammar of data manipulation
library(yardstick) # Tidy characterization of model performance
library(glmnet) # Lassso and Elastic Net
library(kableExtra) # Construct Complex Table
2.Database
The database contains detailed weather information, including
temperature, humidity, wind speed, visibility, dew point, solar
radiation, snowfall, and rainfall. Additionally, it records the number
of bikes rented per hour and date information from the Seoul
bike-sharing system.
Technical Analysis: The weather variables such as
temperature, humidity, wind speed, visibility, dew point, solar
radiation, snowfall, and rainfall are crucial as they can significantly
influence the demand for bike rentals. For instance, temperature affects
user comfort, while humidity impacts the perception of heat. Wind speed
can make biking easier or harder, and visibility is important for
cyclist safety. Dew point is an indicator of humidity and thermal
comfort, and solar radiation can influence the decision to rent bikes.
Snowfall and rainfall are critical factors that can reduce bike rental
demand.
The bike rental data, specifically the number of bikes rented per
hour, serves as the key dependent variable for regression analysis. The
date information allows for the examination of temporal and seasonal
patterns in bike usage.
Project Objective: The goal is to use the weather
and temporal variables to predict the number of bikes rented per hour.
This can help optimize the management of the bike-sharing system,
anticipate demand, and improve user experience.
2.1 Variables
The seoul_bike_sharing_converted_normalized.csv will be
our main dataset which has following variables:
The response variable:
RENTED BIKE COUNT- Count of bikes rented at each
hour
Weather predictor variables:
TEMPERATURE - Temperature in Celsius
HUMIDITY - Unit is %
WIND_SPEED - Unit is m/s
VISIBILITY - Multiplied by 10m
DEW_POINT_TEMPERATURE - The temperature to which the
air would have to cool down in order to reach saturation, unit is
Celsius
SOLAR_RADIATION - MJ/m2
RAINFALL - mm
SNOWFALL - cm
Date/time predictor variables:
DATE - Year-month-day
HOUR- Hour of he day
FUNCTIONAL DAY - NoFunc(Non Functional Hours),
Fun(Functional hours)
HOLIDAY - Holiday/No holiday
SEASONS - Winter, Spring, Summer, Autumn
2.2 Load database
seoul_bike_sharing_converted_normalized <- read_csv("Bases limpias/seoul_bike_sharing_converted_normalized.csv")
2.3 Convert into a df
bike_sharing_df <- seoul_bike_sharing_converted_normalized %>%
select(-DATE, -FUNCTIONING_DAY_YES,-FUNCTIONING_DAY_NO)
We will not be utilizing the DATE column in its current
form, as it essentially functions as a data entry index. However, with
additional time, we could transform the DATE column to
derive new features such as ‘day of the week’ or ‘isWeekend’, which
might influence bike rental preferences. Additionally, the
FUNCTIONAL DAY column will not be used because, after
processing missing values, it only contains a single distinct value
(YES).
3. Split training and testing data
bike_split <- initial_split(bike_sharing_df, prop = 3/4)
train_data <- training(bike_split)
test_data <- testing(bike_split)
3.1 Build a linear regression model using weather variables
only
Weather conditions are likely to influence individuals’ decisions
regarding bike rentals. For instance, adverse weather such as cold and
rainy conditions may lead people to opt for alternative modes of
transportation like buses or taxis. Conversely, favorable weather, such
as sunny days, may increase the propensity to rent bikes for
short-distance travel.
# Pick linear regression
lm_spec <- linear_reg() %>%
# Set engine'
set_engine(engine = "lm")
# Print the linear function
lm_spec
Linear Regression Model Specification (regression)
Computational engine: lm
# To fit the model
lm_model_weather <- lm_spec %>%
fit(RENTED_BIKE_COUNT ~ TEMPERATURE + HUMIDITY + WIND_SPEED + VISIBILITY + DEW_POINT_TEMPERATURE + SOLAR_RADIATION + RAINFALL + SNOWFALL, data = train_data)
Print the fit summary for the lm_model_weather
model.
# Create the table with the regression results
summary(lm_model_weather$fit)
Call:
stats::lm(formula = RENTED_BIKE_COUNT ~ TEMPERATURE + HUMIDITY +
WIND_SPEED + VISIBILITY + DEW_POINT_TEMPERATURE + SOLAR_RADIATION +
RAINFALL + SNOWFALL, data = data)
Residuals:
Min 1Q Median 3Q Max
-0.38129 -0.08315 -0.01554 0.05840 0.65491
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.046472 0.017060 2.724 0.00647 **
TEMPERATURE 0.631318 0.077664 8.129 0.000000000000000517 ***
HUMIDITY -0.276702 0.037778 -7.324 0.000000000000269685 ***
WIND_SPEED 0.105148 0.013491 7.794 0.000000000000007544 ***
VISIBILITY 0.006179 0.006982 0.885 0.37615
DEW_POINT_TEMPERATURE -0.036681 0.082867 -0.443 0.65804
SOLAR_RADIATION -0.120406 0.009805 -12.281 < 0.0000000000000002 ***
RAINFALL -0.582041 0.057417 -10.137 < 0.0000000000000002 ***
SNOWFALL 0.099439 0.037289 2.667 0.00768 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.1368 on 6339 degrees of freedom
Multiple R-squared: 0.4385, Adjusted R-squared: 0.4378
F-statistic: 618.7 on 8 and 6339 DF, p-value: < 0.00000000000000022
The regression analysis aims to predict the
RENTED_BIKE_COUNT using several independent variables:
TEMPERATURE, HUMIDITY,
WIND_SPEED, VISIBILITY,
DEW_POINT_TEMPERATURE, SOLAR_RADIATION,
RAINFALL, and SNOWFALL. The Intercept has an
estimate of 0.046472 , indicating the baseline level of bike
rentals when all other variables are zero.
TEMPERATURE has a positive coefficient of 0.631318
, suggesting that as the temperature increases, the number of
rented bikes also increases. This relationship is highly significant
with a p-value less than 5.17e-16. HUMIDITY has a negative
coefficient of -0.276702, indicating that higher humidity
levels are associated with fewer bike rentals, and this effect is also
highly significant.
VISIBILITY has a very small positive coefficient (
0.006179 ), indicating a slight increase in bike rentals with
better visibility, and this effect is significant.
SOLAR_RADIATION has a strong positive coefficient of
1.034800, showing that higher solar radiation levels
significantly increase bike rentals. RAINFALL and
SNOWFALL both have coefficients ( -0.582041 and
-0.099439, respectively), indicating that more rainfall and
snowfall lead to fewer bike rentals. These effects are statistically
significant.
The residual standard error is 0.1371,
indicating the average distance that the observed values fall from the
regression line. The multiple R-squared and adjusted R-squared values,
along with the F-statistic, are not provided in the image, but they
would typically indicate the overall fit of the model and the
significance of the regression equation, respectively.
Overall, the analysis shows that weather conditions significantly
impact bike rentals, with temperature and solar radiation having the
most substantial positive effects, while humidity, wind speed, rainfall,
and snowfall negatively affect bike rentals.
3.2 Build a linear regression model using all variables
lm_model_all <- lm_spec %>%
fit(RENTED_BIKE_COUNT ~ ., data = train_data)
Print the fit summary for lm_model_all.
summary(lm_model_all$fit)
Call:
stats::lm(formula = RENTED_BIKE_COUNT ~ ., data = data)
Residuals:
Min 1Q Median 3Q Max
-0.39281 -0.06190 -0.00194 0.05861 0.49754
Coefficients: (4 not defined because of singularities)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.087389 0.015264 5.725 0.00000001081522389 ***
TEMPERATURE 0.196831 0.063112 3.119 0.001824 **
HUMIDITY -0.262832 0.029823 -8.813 < 0.0000000000000002 ***
WIND_SPEED -0.005507 0.011317 -0.487 0.626537
VISIBILITY 0.007780 0.005690 1.367 0.171578
DEW_POINT_TEMPERATURE 0.200671 0.066111 3.035 0.002412 **
SOLAR_RADIATION 0.077443 0.011718 6.609 0.00000000004188262 ***
RAINFALL -0.688387 0.045162 -15.242 < 0.0000000000000002 ***
SNOWFALL 0.068333 0.029379 2.326 0.020057 *
SPRING 0.058158 0.005407 10.755 < 0.0000000000000002 ***
SUMMER 0.057523 0.008139 7.067 0.00000000000175034 ***
AUTUMN 0.101296 0.005643 17.951 < 0.0000000000000002 ***
WINTER NA NA NA NA
HOLIDAY_YES NA NA NA NA
HOLIDAY_NO NA NA NA NA
HOUR_0 -0.032833 0.009085 -3.614 0.000304 ***
HOUR_1 -0.060741 0.009369 -6.483 0.00000000009673282 ***
HOUR_2 -0.095024 0.009283 -10.237 < 0.0000000000000002 ***
HOUR_3 -0.118588 0.009296 -12.757 < 0.0000000000000002 ***
HOUR_4 -0.134490 0.009324 -14.424 < 0.0000000000000002 ***
HOUR_5 -0.131373 0.009355 -14.044 < 0.0000000000000002 ***
HOUR_6 -0.083756 0.009493 -8.823 < 0.0000000000000002 ***
HOUR_7 0.002330 0.009229 0.252 0.800703
HOUR_8 0.120016 0.009489 12.648 < 0.0000000000000002 ***
HOUR_9 -0.029439 0.009677 -3.042 0.002358 **
HOUR_10 -0.089353 0.009933 -8.996 < 0.0000000000000002 ***
HOUR_11 -0.095492 0.010401 -9.181 < 0.0000000000000002 ***
HOUR_12 -0.084640 0.010827 -7.818 0.00000000000000626 ***
HOUR_13 -0.085297 0.010742 -7.941 0.00000000000000236 ***
HOUR_14 -0.080889 0.010658 -7.590 0.00000000000003666 ***
HOUR_15 -0.051860 0.010369 -5.001 0.00000058457951176 ***
HOUR_16 -0.019734 0.009990 -1.975 0.048272 *
HOUR_17 0.056302 0.009708 5.800 0.00000000697015086 ***
HOUR_18 0.192734 0.009389 20.527 < 0.0000000000000002 ***
HOUR_19 0.113208 0.009394 12.052 < 0.0000000000000002 ***
HOUR_20 0.092515 0.009361 9.883 < 0.0000000000000002 ***
HOUR_21 0.093144 0.009312 10.003 < 0.0000000000000002 ***
HOUR_22 0.068434 0.009194 7.444 0.00000000000011101 ***
HOUR_23 NA NA NA NA
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.1063 on 6313 degrees of freedom
Multiple R-squared: 0.6621, Adjusted R-squared: 0.6603
F-statistic: 363.8 on 34 and 6313 DF, p-value: < 0.00000000000000022
The model explains approximately 66.2% of the
variance in MODEL_BIKE_COUNT, as indicated by the R-squared
value. The significant predictors (e.g., HOUR,
TEMPERATURE) suggest that these factors have a substantial
impact on bike count. The high F-statistic and its corresponding p-value
indicate that the model is statistically significant overall.
4. Model evaluation
Model evaluation is crucial in regression analysis because it helps
determine how well a model fits the data and predicts outcomes.
R-squared is a key metric that indicates the proportion
of the variance in the dependent variable that is predictable from the
independent variables. A higher R-squared value signifies that the model
explains a greater portion of the variance, suggesting a better fit.
This is important for understanding the strength of the relationship
between the predictors and the outcome, and for assessing the model’s
explanatory power.
RMSE (Root Mean Square Error), on the other hand,
measures the average magnitude of the errors between predicted and
observed values. It provides insight into the model’s predictive
accuracy. A lower RMSE indicates that the model’s predictions are closer
to the actual values, which is essential for making reliable forecasts.
Evaluating models using both R-squared and RMSE ensures a balanced
assessment of their performance, considering both the goodness of fit
and the precision of predictions. This comprehensive evaluation helps in
selecting the most appropriate model for practical applications.
4.1 Root Mean Squared Error (RMSE)
# Making the predictions
predictions_weather <- predict(lm_model_weather, new_data = test_data)
predictions_all <- predict(lm_model_all, new_data = test_data)
# Calculating errors
error_weather <- train_data$RENTED_BIKE_COUNT - predictions_weather
error_all <- train_data$RENTED_BIKE_COUNT - predictions_all
# Calculating Squared Errors
squared_error_weather <- error_weather^2
squared_error_all <- error_all^2
# Calculate the average of the squared errors
mean_squared_error_weather <- mean(squared_error_weather$.pred)
mean_squared_error_all <- mean(squared_error_all$.pred)
# Calculate RMSE
rmse_weather <- sqrt(mean_squared_error_weather)
rmse_all <- sqrt(mean_squared_error_all)
4.2 R-squared
summary_m_weather <- summary(lm_model_weather$fit)
r2_weather <- summary_m_weather$r.squared
summary_m_all <- summary(lm_model_all$fit)
r2_all <- summary_m_all$r.squared
4.3 Comparing models
results <- data.frame(
Model = c("Weather Model", "All Variables Model"),
R_squared = c(r2_weather, r2_all),
RMSE = c(rmse_weather, rmse_all)
)
print(results)
The “Weather Model” has an R-squared value of
0.4303461 and an RMSE of 0.2224554 The R-squared value indicates that
approximately 43.03% of the variance in the dependent variable can be
explained by the independent variables in this model. The RMSE value
represents the root mean square error, which measures the average
magnitude of the errors between the predicted and observed values. A
lower RMSE indicates better predictive accuracy.
On the other hand, the “All Variables Model” has an
R-squared value of 0.6602304 and an RMSE of 0.2348928 This model
explains approximately 66.02% of the variance in the dependent variable,
which is higher than the “Weather Model.” However, the RMSE is higher at
795.9658, indicating that the average prediction error is larger
compared to the “Weather Model.”
To determine the best model, we need to consider the trade-off
between the goodness of fit (R-squared) and the predictive accuracy
(RMSE). The “All Variables Model” has a higher
R-squared value, suggesting it fits the data better and explains more
variance. However, its higher RMSE indicates that its predictions are
less accurate on average compared to the “Weather
Model.”
If the primary goal is to have a model that explains more variance in
the dependent variable, the “All Variables Model” would
be preferred due to its higher R-squared value. Conversely, if the goal
is to minimize prediction errors, the “Weather Model”
would be better due to its lower RMSE.
In summary, the choice of the best model depends on the specific
objectives of the analysis. If explaining more variance is prioritized,
the “All Variables Model” is better. If minimizing
prediction errors is more important, the “Weather
Model” is the preferred choice.
4.4 Bar Chart for Coefficients
# Obtener los coeficientes del modelo
coefficients_all <- tidy(lm_model_all)
# Crear un gráfico de barras para visualizar los coeficientes
ggplot(coefficients_all, aes(x = reorder(term, estimate), y = estimate)) +
geom_bar(stat = "identity", fill = "maroon") +
geom_errorbar(aes(ymin = estimate - std.error, ymax = estimate + std.error), width = 0.2, color = "black") +
labs(title = "Coefficients of Linear Regression Model (All)",
x = "Predictor Variables",
y = "Coefficient Estimate") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.title.x = element_text(size = 14),
axis.title.y = element_text(size = 14),
axis.text = element_text(size = 12)
) +
coord_flip() # Voltear el gráfico para mejor visualización

4.5 Bar Chart for Coefficients
# Obtener los coeficientes del modelo
coefficients_weather <- tidy(lm_model_weather)
# Crear un gráfico de barras para visualizar los coeficientes
ggplot(coefficients_weather, aes(x = reorder(term, estimate), y = estimate)) +
geom_bar(stat = "identity", fill = "steelblue") +
geom_errorbar(aes(ymin = estimate - std.error, ymax = estimate + std.error), width = 0.2, color = "darkred") +
labs(title = "Coefficients of Linear Regression Model (Weather)",
x = "Predictor Variables",
y = "Coefficient Estimate") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.title.x = element_text(size = 14),
axis.title.y = element_text(size = 14),
axis.text = element_text(size = 12)
) +
coord_flip() # Voltear el gráfico para mejor visualización

NA
NA
5. Add polynomial terms
# Plot the higher order polynomial fits
ggplot(train_data, aes(x = RENTED_BIKE_COUNT, y = TEMPERATURE)) +
geom_point() +
geom_smooth(method = "lm", formula = y ~ poly(x, 2), color = "red", se = FALSE) +
geom_smooth(method = "lm", formula = y ~ poly(x, 3), color = "blue", se = FALSE) +
geom_smooth(method = "lm", formula = y ~ poly(x, 4), color = "green", se = FALSE) +
geom_smooth(method = "lm", formula = y ~ poly(x, 5), color = "purple", se = FALSE) +
labs(title = "Polynomial Regression Fits",
x = "Rented Bikes",
y = "Temperature") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.title.x = element_text(size = 14),
axis.title.y = element_text(size = 14),
axis.text = element_text(size = 12)
)

5.1 Fit the Polynomial Regression Model
# Assuming the important variables are TEMPERATURE and HUMIDITY
lm_bikly <- lm(RENTED_BIKE_COUNT ~ poly(TEMPERATURE, 2) + poly(HUMIDITY, 2), data = train_data)
# Print the model summary
summary(lm_bikly)
Call:
lm(formula = RENTED_BIKE_COUNT ~ poly(TEMPERATURE, 2) + poly(HUMIDITY,
2), data = train_data)
Residuals:
Min 1Q Median 3Q Max
-0.33139 -0.08465 -0.02142 0.06042 0.65952
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.204909 0.001699 120.61 <0.0000000000000002 ***
poly(TEMPERATURE, 2)1 8.784310 0.137846 63.73 <0.0000000000000002 ***
poly(TEMPERATURE, 2)2 -1.673604 0.140230 -11.94 <0.0000000000000002 ***
poly(HUMIDITY, 2)1 -4.779434 0.141199 -33.85 <0.0000000000000002 ***
poly(HUMIDITY, 2)2 -2.570957 0.136853 -18.79 <0.0000000000000002 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.1354 on 6343 degrees of freedom
Multiple R-squared: 0.4469, Adjusted R-squared: 0.4466
F-statistic: 1281 on 4 and 6343 DF, p-value: < 0.00000000000000022
The regression analysis presented aims to predict the
RENTED_BIKE_COUNT based on polynomial transformations of
TEMPERATURE and HUMIDITY. This approach allows
for capturing non-linear relationships between these predictors and the
dependent variable, which can be more reflective of real-world scenarios
where changes in temperature and humidity might not have a
straightforward linear effect on bike rentals.
5.1.1 Model Summary
The regression model includes polynomial terms for both
TEMPERATURE and HUMIDITY, specifically up to
the second degree. The coefficients table shows the estimated effects of
each predictor, along with their standard errors, t-values, and
p-values. The intercept is highly significant, with an estimate a very
low p-value (<2e-16), indicating a strong baseline effect when all
predictors are at their mean values.
5.1.2 Coefficients and Significance
The first-degree polynomial term for TEMPERATURE has a
large negative coefficient and is highly significant (p-value
<2e-16), suggesting that as temperature increases, the number of
rented bikes decreases significantly. The second-degree term for
TEMPERATURE has a negative coefficient and is also
significant (p-value <2e-16), indicating a diminishing return effect;
at higher temperatures, the decrease in bike rentals slows down.
Similarly, the first-degree polynomial term for HUMIDITY
has a significant negative coefficient , suggesting that higher humidity
levels reduce bike rentals. The second-degree term for
HUMIDITY also has a negative coefficient relationship.
5.1.3 Model Fit
The model’s multiple R-squared value is 0.4666, indicating that
approximately 44.69% of the variance in RENTED_BIKE_COUNT
is explained by the model. The adjusted R-squared value is the same,
suggesting that the model’s explanatory power is robust even after
adjusting for the number of predictors. The F-statistic is very high,
with a corresponding p-value <2.2e-16, indicating that the model is
statistically significant overall.
5.1.4 Theoretical Insights
Evaluating a model using both R-squared and RMSE is crucial for
understanding its overall performance. The R-squared value provides
insight into how well the model explains the variability in the data,
which is important for assessing the model’s explanatory power. On the
other hand, RMSE provides a measure of the model’s predictive accuracy,
which is essential for making reliable forecasts. By considering both
metrics, we can ensure a balanced evaluation of the model, taking into
account both its ability to fit the data and its precision in
predictions.
In summary, this polynomial regression model effectively captures the
non-linear effects of temperature and humidity on bike rentals, with
significant coefficients for both predictors. The model explains a
substantial portion of the variance in bike rentals, making it a useful
tool for understanding and predicting bike rental patterns based on
weather conditions.
5.2 Make Predictions on the Test Dataset
# Make predictions on the test dataset using the lm_bikly model
y_pred <- predict(lm_bikly, newdata = test_data)
# Convert negative predictions to zero
y_pred <- ifelse(y_pred < 0, 0, y_pred)
5.2.1 Calculate R-squared and RMSE
# Calculating errors
error_bikly <- train_data$RENTED_BIKE_COUNT - y_pred
Warning: longer object length is not a multiple of shorter object length
# Calculating Squared Errors
squared_error_bikly <- error_bikly^2
# Calculate the average of the squared errors
mean_squared_error_bikly <- mean(squared_error_bikly)
# Calculate RMSE
rmse_bikly <- sqrt(mean_squared_error_bikly)
# Calculate R-squared
summary_m_bikly <- summary(lm_bikly)
rsq_bikly <- summary_m_bikly$r.squared
# Display the results
results_bikly <- data.frame(
Model = "Polynomial Regression Model (Bikly)",
R_squared = rsq_bikly,
RMSE = rmse_bikly
)
print(results_bikly)
NA
Based on the provided data, the polynomial regression model named
“Biky” has an R-squared value of 0.44696063 and an RMSE of
0.2136526.
5.2.2 Model Evaluation
The R-squared value of 0.44696063 indicates that
approximately 44.70% of the variance in the dependent variable is
explained by the independent variables in this model. This suggests a
moderate level of explanatory power, meaning that the model captures a
significant portion of the variability in the data but leaves some
unexplained variance. A higher R-squared value generally indicates a
better fit of the model to the data.
The RMSE (Root Mean Square Error) value of 0.2136526
measures the average magnitude of the errors between the predicted and
observed values. A lower RMSE indicates better predictive accuracy, as
it means the model’s predictions are closer to the actual values. In
this case, the RMSE value is relatively low, suggesting that the model
has good predictive performance.
5.2.3 Theoretical Insights
Evaluating a model using both R-squared and RMSE is crucial for
understanding its overall performance. The R-squared value provides
insight into how well the model explains the variability in the data,
which is important for assessing the model’s explanatory power. On the
other hand, RMSE provides a measure of the model’s predictive accuracy,
which is essential for making reliable forecasts. By considering both
metrics, we can ensure a balanced evaluation of the model, taking into
account both its ability to fit the data and its precision in
predictions.
The polynomial regression model “Biky” demonstrates a moderate level
of explanatory power and good predictive accuracy, making it a useful
tool for understanding and predicting the dependent variable based on
the given predictors.
6. Fit the Polynomial Regression Model with Interaction Terms
# Fit a polynomial regression model with interaction terms
lm_bikly_interaction <- lm(RENTED_BIKE_COUNT ~ poly(TEMPERATURE, 2) * HUMIDITY + poly(TEMPERATURE, 2) * WIND_SPEED, data = train_data)
# Print the model summary
summary(lm_bikly_interaction)
Call:
lm(formula = RENTED_BIKE_COUNT ~ poly(TEMPERATURE, 2) * HUMIDITY +
poly(TEMPERATURE, 2) * WIND_SPEED, data = train_data)
Residuals:
Min 1Q Median 3Q Max
-0.38995 -0.08537 -0.02348 0.05322 0.66347
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.332614 0.007418 44.836 < 0.0000000000000002 ***
poly(TEMPERATURE, 2)1 11.106750 0.613015 18.118 < 0.0000000000000002 ***
poly(TEMPERATURE, 2)2 -3.933440 0.663528 -5.928 0.000000003226468181 ***
HUMIDITY -0.245753 0.009693 -25.355 < 0.0000000000000002 ***
WIND_SPEED 0.099425 0.013255 7.501 0.000000000000072139 ***
poly(TEMPERATURE, 2)1:HUMIDITY -6.744694 0.821792 -8.207 0.000000000000000272 ***
poly(TEMPERATURE, 2)2:HUMIDITY 4.200620 0.976027 4.304 0.000017042362283015 ***
poly(TEMPERATURE, 2)1:WIND_SPEED 5.105744 1.055849 4.836 0.000001358254989286 ***
poly(TEMPERATURE, 2)2:WIND_SPEED 2.539413 1.098471 2.312 0.0208 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.137 on 6339 degrees of freedom
Multiple R-squared: 0.4336, Adjusted R-squared: 0.4329
F-statistic: 606.6 on 8 and 6339 DF, p-value: < 0.00000000000000022
The regression analysis presented aims to predict the
RENTED_BIKE_COUNT based on polynomial transformations of
TEMPERATURE, HUMIDITY, and their interaction
with WIND_SPEED. This approach allows for capturing complex
relationships between these predictors and the dependent variable, which
can be more reflective of real-world scenarios where weather conditions
interact in non-linear ways to affect bike rentals.
6.1 Model Summary
The regression model includes polynomial terms for
TEMPERATURE up to the second degree, HUMIDITY,
and the interaction between TEMPERATURE and
WIND_SPEED. The coefficients table shows the estimated
effects of each predictor, along with their standard errors, t-values,
and p-values.
6.1.1 Coefficients and Significance
The first-degree polynomial term for TEMPERATURE has a
significant negative coefficient , suggesting that as temperature
increases, the number of rented bikes decreases slightly. The
second-degree term for TEMPERATURE is not significant,
indicating that higher-order temperature effects are negligible.
HUMIDITY has a significant negative coefficient ,
suggesting that higher humidity levels reduce bike rentals. The
interaction term between the first-degree polynomial of
TEMPERATURE and HUMIDITY is significant ,
indicating that the combined effect of these variables has a meaningful
impact on bike rentals. Similarly, WIND_SPEED has a
significant negative coefficient , and its interaction with the
first-degree polynomial of TEMPERATURE is significant ,
suggesting that wind speed also plays a crucial role in bike
rentals.
6.1.2 Model Fit
The model’s multiple R-squared value is 0.4329, indicating that
approximately 43.36% of the variance in RENTED_BIKE_COUNT
is explained by the model. The adjusted R-squared value is very close,
suggesting that the model’s explanatory power is robust even after
adjusting for the number of predictors. The F-statistic is very high,
with a corresponding p-value <2e-16, indicating that the model is
statistically significant overall.
6.1.3 Theoretical Insights
Evaluating a model using both R-squared and RMSE is crucial for
understanding its overall performance. The R-squared value provides
insight into how well the model explains the variability in the data,
which is important for assessing the model’s explanatory power. On the
other hand, RMSE provides a measure of the model’s predictive accuracy,
which is essential for making reliable forecasts. By considering both
metrics, we can ensure a balanced evaluation of the model, taking into
account both its ability to fit the data and its precision in
predictions.
In summary, this polynomial regression model effectively captures the
complex interactions between temperature, humidity, and wind speed on
bike rentals, with significant coefficients for all predictors. The
model explains a substantial portion of the variance in bike rentals,
making it a useful tool for understanding and predicting bike rental
patterns based on weather conditions.
6.2 Make Predictions on the Test Dataset
# Make predictions on the test dataset using the lm_bikly model
y_pred_interaction <- predict(lm_bikly_interaction, newdata = test_data)
# Convert negative predictions to zero
y_pred_interaction <- ifelse(y_pred < 0, 0, y_pred_interaction)
6.2.1 Calculate R-squared and RMSE
# Calculating errors
error_bikly_interaction <- train_data$RENTED_BIKE_COUNT - y_pred_interaction
Warning: longer object length is not a multiple of shorter object length
# Calculating Squared Errors
squared_error_bikly_interaction <- error_bikly^2
# Calculate the average of the squared errors
mean_squared_error_bikly_interaction <- mean(squared_error_bikly_interaction)
# Calculate RMSE
rmse_bikly_interaction <- sqrt(mean_squared_error_bikly_interaction)
# Calculate R-squared
summary_m_bikly_interaction <- summary(lm_bikly_interaction)
rsq_bikly_interaction <- summary_m_bikly_interaction$r.squared
# Display the results
results_bikly_interaction <- data.frame(
Model = "Polynomial Regression Model (Bikly_interaction)",
R_squared = rsq_bikly_interaction,
RMSE = rmse_bikly_interaction
)
print(results_bikly_interaction)
Based on the provided data, the polynomial regression model has an
R-squared value of 0.4335922 and an RMSE of 0.2168526.
6.2.2 Model Evaluation
The R-squared value of 0.4335922 indicates that
approximately 43.36% of the variance in the dependent variable is
explained by the independent variables in this model. This suggests a
moderate level of explanatory power, meaning that the model captures a
significant portion of the variability in the data but leaves some
unexplained variance. A higher R-squared value generally indicates a
better fit of the model to the data.
The RMSE (Root Mean Square Error) value of 0.2168526
measures the average magnitude of the errors between the predicted and
observed values. A lower RMSE indicates better predictive accuracy, as
it means the model’s predictions are closer to the actual values. In
this case, the RMSE value is relatively low, suggesting that the model
has good predictive performance.
6.2.3 Theoretical Insights
Evaluating a model using both R-squared and RMSE is crucial for
understanding its overall performance. The R-squared value provides
insight into how well the model explains the variability in the data,
which is important for assessing the model’s explanatory power. On the
other hand, RMSE provides a measure of the model’s predictive accuracy,
which is essential for making reliable forecasts. By considering both
metrics, we can ensure a balanced evaluation of the model, taking into
account both its ability to fit the data and its precision in
predictions.
7. Add regularization
7.1 Create a recipe
bike_recipe <- recipe(RENTED_BIKE_COUNT ~ ., data = train_data) %>%
step_zv(all_predictors()) %>%
step_normalize(all_predictors()) %>%
step_poly(all_predictors(), degree = 2) %>%
step_interact(terms = ~ all_predictors():all_predictors())
7.2 Specify the work flow and fit the model
bike_workflow <- workflow() %>%
add_recipe(bike_recipe) %>%
add_model(glmnet_spec)
7.3 Elastic Net Regularization & (L1 and L2)
# Model 1: Adding regularization (L2 Ridge)
ridge_spe <- linear_reg(penalty = 0.1, mixture = 0) %>%
set_engine("glmnet")
train_f1 <- ridge_spe %>%
fit(RENTED_BIKE_COUNT ~ TEMPERATURE + HUMIDITY + SOLAR_RADIATION + RAINFALL + SNOWFALL, data = train_data)
# Model 2: Adding regularization (L1 Lasso)
ridge_spe1 <- linear_reg(penalty = 0.1, mixture = 1) %>%
set_engine("glmnet")
train_f2 <- ridge_spe1 %>%
fit(RENTED_BIKE_COUNT ~ TEMPERATURE + HUMIDITY+ SOLAR_RADIATION + RAINFALL + SNOWFALL, data = train_data)
# Model 2: Adding regularization (L1 Lasso and L2 Ridge)
ridge_spe2 <- linear_reg(penalty = 0.1, mixture = 0.5) %>%
set_engine("glmnet")
train_f3 <- ridge_spe2 %>%
fit(RENTED_BIKE_COUNT ~ TEMPERATURE + HUMIDITY + SOLAR_RADIATION + RAINFALL + SNOWFALL, data = train_data)
# Extract predictions
predic1 <- predict(train_f1, train_data)$.pred
predic2 <- predict(train_f2, train_data)$.pred
predic3 <- predict(train_f3, train_data)$.pred
# Calculate RMSE manually
rmse_manual <- function(actual, predicted) {
sqrt(mean((actual - predicted)^2))
}
# Calculate RMSE for each model
rmse_1 <- rmse_manual(train_data$RENTED_BIKE_COUNT, predic1)
rmse_2 <- rmse_manual(train_data$RENTED_BIKE_COUNT, predic2)
rmse_3 <- rmse_manual(train_data$RENTED_BIKE_COUNT, predic3)
# Combine results into a table
result_regularization <- tibble(
model = c("Model 1: L2 Ridge", "Model 2: L1 Lasso", "Model 3: L1 Lasso and L2 Ridge"),
RMSE = c(rmse_1, rmse_2, rmse_3)
)
# Display the results
print(result_regularization)
NA
Model 1: L2 Ridge - This model, which applies L2
regularization, has an RMSE of 0.1464844. L2 regularization helps to
prevent overfitting by penalizing large coefficients, leading to a more
generalized model. The relatively low RMSE indicates that this model
performs well in predicting the target variable, balancing bias and
variance effectively.
Model 2: L1 Lasso - The L1 regularization model,
known as Lasso, has a higher RMSE of 0.1805229. Lasso regularization not
only helps in preventing overfitting but also performs feature selection
by shrinking some coefficients to zero. The higher RMSE suggests that
while Lasso is useful for identifying important features, it may not
always provide the best predictive accuracy compared to Ridge regression
in this context.
Model 3: Combination of L1 Lasso and L2 Ridge -
This model combines both L1 and L2 regularization techniques, resulting
in an RMSE of 0.1623183. This approach, often referred to as Elastic
Net, aims to leverage the strengths of both regularization methods. The
RMSE value indicates that the combination model performs better than
Lasso alone but not as well as Ridge regression. This suggests that
while combining both regularization techniques can be beneficial, the
specific context and data characteristics play a crucial role in
determining the optimal regularization strategy.
Overall, the insights highlight the importance of selecting the
appropriate regularization technique based on the specific
characteristics of the data and the modeling goals. Ridge regression
(L2) appears to be the most effective in this scenario, providing a good
balance between model complexity and predictive accuracy. Lasso (L1) is
useful for feature selection but may not always yield the lowest RMSE.
The combination of L1 and L2 regularization offers a middle ground,
potentially improving model performance in certain contexts.
8. Experiment to search for improved models
# Define the model specifications
lm_spec <- linear_reg() %>%
set_engine("lm")
# Model 1: Adding more features
train_fit5 <- lm_spec %>%
fit(RENTED_BIKE_COUNT ~ TEMPERATURE + HUMIDITY + WIND_SPEED + VISIBILITY + DEW_POINT_TEMPERATURE + SOLAR_RADIATION + RAINFALL + SNOWFALL, data = train_data)
# Model 2: Adding regularization (L2 Ridge)
ridge_spec <- linear_reg(penalty = 0.1, mixture = 0) %>%
set_engine("glmnet")
train_fit6 <- ridge_spec %>%
fit(RENTED_BIKE_COUNT ~ TEMPERATURE + HUMIDITY + WIND_SPEED + VISIBILITY + DEW_POINT_TEMPERATURE + SOLAR_RADIATION + RAINFALL + SNOWFALL, data = train_data)
# Model 3: Adding polynomial components
poly_spec <- linear_reg() %>%
set_engine("lm")
train_fit7 <- poly_spec %>%
fit(RENTED_BIKE_COUNT ~ poly(TEMPERATURE, 2) + poly(HUMIDITY, 2) + poly(WIND_SPEED, 2) + poly(VISIBILITY, 2) + poly(DEW_POINT_TEMPERATURE, 2) + poly(SOLAR_RADIATION, 2) + poly(RAINFALL, 2) + poly(SNOWFALL, 2), data = train_data)
# Model 4: Adding interaction terms
interaction_spec <- linear_reg() %>%
set_engine("lm")
train_fit8 <- interaction_spec %>%
fit(RENTED_BIKE_COUNT ~ TEMPERATURE * HUMIDITY + WIND_SPEED * VISIBILITY + DEW_POINT_TEMPERATURE * SOLAR_RADIATION + RAINFALL * SNOWFALL, data = train_data)
# Model 5: Using decision tree regression
tree_spec <- linear_reg() %>%
set_engine("lm")
train_fit9 <- tree_spec %>%
fit(RENTED_BIKE_COUNT ~ TEMPERATURE + HUMIDITY + WIND_SPEED + SOLAR_RADIATION + RAINFALL + SNOWFALL, data = train_data)
# Extract predictions
pred5 <- predict(train_fit5, train_data)$.pred
pred6 <- predict(train_fit6, train_data)$.pred
pred7 <- predict(train_fit7, train_data)$.pred
pred8 <- predict(train_fit8, train_data)$.pred
pred9 <- predict(train_fit9, train_data)$.pred
# Calculate RMSE manually
rmse_manual <- function(actual, predicted) {
sqrt(mean((actual - predicted)^2))
}
# Calculate RMSE for each model
rmse5 <- rmse_manual(train_data$RENTED_BIKE_COUNT, pred5)
rmse6 <- rmse_manual(train_data$RENTED_BIKE_COUNT, pred6)
rmse7 <- rmse_manual(train_data$RENTED_BIKE_COUNT, pred7)
rmse8 <- rmse_manual(train_data$RENTED_BIKE_COUNT, pred8)
rmse9 <- rmse_manual(train_data$RENTED_BIKE_COUNT, pred9)
# Combine results into a table
results <- tibble(
model = c("Model 1: More Features", "Model 2: Ridge Regularization", "Model 3: Polynomial Components", "Model 4: Interaction Terms", "Model 5: LM"),
RMSE = c(rmse5, rmse6, rmse7, rmse8, rmse9)
)
# Display the results
print(results)
NA
Model 1: More Features - This model includes
multiple features such as temperature, humidity, wind speed, visibility,
dew point temperature, solar radiation, rainfall, and snowfall. It has
an RMSE of 0.1370119, indicating a relatively good fit. The inclusion of
diverse features helps capture various aspects affecting the rented bike
count.
Model 2: Ridge Regularization - This model
applies L2 regularization to prevent overfitting. Despite the
regularization, its RMSE is slightly higher at 0.1437533. This suggests
that while regularization helps in controlling model complexity, it may
not always lead to better performance in terms of RMSE.
Model 3: Polynomial Components - By adding
polynomial components, this model captures non-linear relationships
between the predictors and the target variable. It has the lowest RMSE
of 0.1291756, indicating that non-linear transformations of the features
significantly improve the model’s predictive accuracy.
Model 4: Interaction Terms - This model includes
interaction terms between pairs of features, allowing it to capture the
combined effect of two variables on the target. With an RMSE of
0.1338293, it performs better than the ridge regularization model but
not as well as the polynomial components model. Interaction terms can be
useful but may not always lead to the best performance.
Model 5: - This model uses a decision tree
algorithm, which is different from linear regression. It has an RMSE of
0.1370168, similar to Model 1. Decision trees can capture complex
relationships and interactions between features, but they may not always
outperform linear models with polynomial components.
Overall, the polynomial components model (Model 3) shows the best
performance in terms of RMSE, suggesting that capturing non-linear
relationships is crucial for predicting the rented bike count
accurately. Regularization and interaction terms also contribute to
model performance but may not always lead to the lowest RMSE.
# Crear Q-Q Plot para cada modelo
qq_plot <- function(predictions, model_name) {
ggplot(data.frame(residuals = train_data$RENTED_BIKE_COUNT - predictions), aes(sample = residuals)) +
stat_qq() +
stat_qq_line() +
labs(title = paste("Q-Q Plot for", model_name),
x = "Theoretical Quantiles",
y = "Sample Quantiles") +
theme_minimal()
}
# Generar Q-Q Plots
qq_plot1 <- qq_plot(pred5, "Model 1: More Features")
qq_plot2 <- qq_plot(pred6, "Model 2: Ridge Regularization")
qq_plot3 <- qq_plot(pred7, "Model 3: Polynomial Components")
qq_plot4 <- qq_plot(pred8, "Model 4: Interaction Terms")
qq_plot5 <- qq_plot(pred9, "Model 5: LM")
# Mostrar los Q-Q Plots
print(qq_plot1)

print(qq_plot2)

print(qq_plot3)

print(qq_plot4)

print(qq_plot5)

LS0tDQp0aXRsZTogIkJpY3ljbGUgcmVudGFsIHByZWRpY3Rpb24iDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19kZXB0aDogNQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KLS0tDQoNCmBgYHtyICwgbWVzc2FnZT1GQUxTRSAsZWNobz1GQUxTRX0NCm9wdGlvbnMoc2NpcGVuPTk5OTkpDQpzZXQuc2VlZCgxMjM0KQ0KYGBgDQoNCkJpa2Ugc2hhcmluZyBoYXMgYmVjb21lIGEgcG9wdWxhciB0cmFuc3BvcnRhdGlvbiBvcHRpb24gaW4gbWFueSBjaXRpZXMgYXJvdW5kIHRoZSB3b3JsZC4gV2l0aCBpbmNyZWFzaW5nIGVudmlyb25tZW50YWwgYXdhcmVuZXNzIGFuZCB0aGUgbmVlZCBmb3Igc3VzdGFpbmFibGUgdHJhbnNwb3J0YXRpb24gb3B0aW9ucywgYmlrZSBzaGFyaW5nIHN5c3RlbXMgaGF2ZSBzZWVuIHNpZ25pZmljYW50IGdyb3d0aC4gSG93ZXZlciwgZm9yIHRoZXNlIHN5c3RlbXMgdG8gb3BlcmF0ZSBlZmZpY2llbnRseSwgaXQgaXMgY3J1Y2lhbCB0byBwcmVkaWN0IHRoZSBkZW1hbmQgZm9yIGJpa2VzIGF0IGRpZmZlcmVudCBzdGF0aW9ucyBhbmQgbG9jYXRpb25zLg0KDQpUaGUgZ29hbCBvZiB0aGlzIHByb2plY3QgaXMgdG8gZGV2ZWxvcCBhIHByZWRpY3RpdmUgbW9kZWwgdGhhdCBjYW4gZXN0aW1hdGUgYmlrZSBzaGFyaW5nIGRlbWFuZCBiYXNlZCBvbiB2YXJpb3VzIGZhY3RvcnMgc3VjaCBhcyB3ZWF0aGVyLCB0aW1lIG9mIGRheSwgZGF5IG9mIHRoZSB3ZWVrLCBhbmQgc3BlY2lhbCBldmVudHMuIFVzaW5nIGRhdGEgYW5hbHl0aWNzIGFuZCBtYWNoaW5lIGxlYXJuaW5nIHRlY2huaXF1ZXMsIHRoZSBwcm9qZWN0IGFpbXMgdG8gcHJvdmlkZSBhIHRvb2wgdGhhdCBoZWxwcyBiaWtlIHNoYXJpbmcgc3lzdGVtIG9wZXJhdG9ycyBvcHRpbWl6ZSBiaWtlIGRpc3RyaWJ1dGlvbiBhbmQgYXZhaWxhYmlsaXR5LCB0aGVyZWJ5IGltcHJvdmluZyB1c2VyIGV4cGVyaWVuY2UgYW5kIG9wZXJhdGlvbmFsIGVmZmljaWVuY3kuDQoNCiMjIyAxLkxpYnJhcmllcw0KDQpgYGB7ciAsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHRpZHltb2RlbHMpICNGb3IgbW9kZWxpbmcgYW5kIG1hY2hpbmUgbGVhcm5pbmcgDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgIyBTaGFyZSBjb21tb24gZGF0YSByZXByZXNlbnRhdGlvbnMgYW5kICdBUEknIGRlc2lnbg0KbGlicmFyeShzdHJpbmdyKSAjIENvbnNpc3RlbnQgd3JhcHBlciBmb3IgY29tbW9uIHN0cmluZyBvcGVyYXRpb25zDQpsaWJyYXJ5KHJlYWRyKSAjIFJlYWQgcmVjdGFuZ3VsYXIgdGV4dCBkYXRhDQpsaWJyYXJ5KGJyb29tKSAjIENvbnZlcnQgc3RhdGlzdGljYWwgb2JqZWN0cyBpbnRvIHRpZHkgdGliYmxlcw0KbGlicmFyeShkcGx5cikgIyBBIGdyYW1tYXIgb2YgZGF0YSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoeWFyZHN0aWNrKSAjIFRpZHkgY2hhcmFjdGVyaXphdGlvbiBvZiBtb2RlbCBwZXJmb3JtYW5jZQ0KbGlicmFyeShnbG1uZXQpICMgTGFzc3NvIGFuZCBFbGFzdGljIE5ldA0KbGlicmFyeShrYWJsZUV4dHJhKSAjIENvbnN0cnVjdCBDb21wbGV4IFRhYmxlDQoNCg0KYGBgDQoNCiMjIyAyLkRhdGFiYXNlDQoNClRoZSBkYXRhYmFzZSBjb250YWlucyBkZXRhaWxlZCB3ZWF0aGVyIGluZm9ybWF0aW9uLCBpbmNsdWRpbmcgdGVtcGVyYXR1cmUsIGh1bWlkaXR5LCB3aW5kIHNwZWVkLCB2aXNpYmlsaXR5LCBkZXcgcG9pbnQsIHNvbGFyIHJhZGlhdGlvbiwgc25vd2ZhbGwsIGFuZCByYWluZmFsbC4gQWRkaXRpb25hbGx5LCBpdCByZWNvcmRzIHRoZSBudW1iZXIgb2YgYmlrZXMgcmVudGVkIHBlciBob3VyIGFuZCBkYXRlIGluZm9ybWF0aW9uIGZyb20gdGhlIFNlb3VsIGJpa2Utc2hhcmluZyBzeXN0ZW0uDQoNCioqVGVjaG5pY2FsIEFuYWx5c2lzOioqDQpUaGUgd2VhdGhlciB2YXJpYWJsZXMgc3VjaCBhcyB0ZW1wZXJhdHVyZSwgaHVtaWRpdHksIHdpbmQgc3BlZWQsIHZpc2liaWxpdHksIGRldyBwb2ludCwgc29sYXIgcmFkaWF0aW9uLCBzbm93ZmFsbCwgYW5kIHJhaW5mYWxsIGFyZSBjcnVjaWFsIGFzIHRoZXkgY2FuIHNpZ25pZmljYW50bHkgaW5mbHVlbmNlIHRoZSBkZW1hbmQgZm9yIGJpa2UgcmVudGFscy4gRm9yIGluc3RhbmNlLCB0ZW1wZXJhdHVyZSBhZmZlY3RzIHVzZXIgY29tZm9ydCwgd2hpbGUgaHVtaWRpdHkgaW1wYWN0cyB0aGUgcGVyY2VwdGlvbiBvZiBoZWF0LiBXaW5kIHNwZWVkIGNhbiBtYWtlIGJpa2luZyBlYXNpZXIgb3IgaGFyZGVyLCBhbmQgdmlzaWJpbGl0eSBpcyBpbXBvcnRhbnQgZm9yIGN5Y2xpc3Qgc2FmZXR5LiBEZXcgcG9pbnQgaXMgYW4gaW5kaWNhdG9yIG9mIGh1bWlkaXR5IGFuZCB0aGVybWFsIGNvbWZvcnQsIGFuZCBzb2xhciByYWRpYXRpb24gY2FuIGluZmx1ZW5jZSB0aGUgZGVjaXNpb24gdG8gcmVudCBiaWtlcy4gU25vd2ZhbGwgYW5kIHJhaW5mYWxsIGFyZSBjcml0aWNhbCBmYWN0b3JzIHRoYXQgY2FuIHJlZHVjZSBiaWtlIHJlbnRhbCBkZW1hbmQuDQoNClRoZSBiaWtlIHJlbnRhbCBkYXRhLCBzcGVjaWZpY2FsbHkgdGhlIG51bWJlciBvZiBiaWtlcyByZW50ZWQgcGVyIGhvdXIsIHNlcnZlcyBhcyB0aGUga2V5IGRlcGVuZGVudCB2YXJpYWJsZSBmb3IgcmVncmVzc2lvbiBhbmFseXNpcy4gVGhlIGRhdGUgaW5mb3JtYXRpb24gYWxsb3dzIGZvciB0aGUgZXhhbWluYXRpb24gb2YgdGVtcG9yYWwgYW5kIHNlYXNvbmFsIHBhdHRlcm5zIGluIGJpa2UgdXNhZ2UuDQoNCioqUHJvamVjdCBPYmplY3RpdmU6KioNClRoZSBnb2FsIGlzIHRvIHVzZSB0aGUgd2VhdGhlciBhbmQgdGVtcG9yYWwgdmFyaWFibGVzIHRvIHByZWRpY3QgdGhlIG51bWJlciBvZiBiaWtlcyByZW50ZWQgcGVyIGhvdXIuIFRoaXMgY2FuIGhlbHAgb3B0aW1pemUgdGhlIG1hbmFnZW1lbnQgb2YgdGhlIGJpa2Utc2hhcmluZyBzeXN0ZW0sIGFudGljaXBhdGUgZGVtYW5kLCBhbmQgaW1wcm92ZSB1c2VyIGV4cGVyaWVuY2UuDQoNCiMjIyMgMi4xIFZhcmlhYmxlcyANCg0KVGhlIGBzZW91bF9iaWtlX3NoYXJpbmdfY29udmVydGVkX25vcm1hbGl6ZWQuY3N2YCB3aWxsIGJlIG91ciBtYWluIGRhdGFzZXQgd2hpY2ggaGFzIGZvbGxvd2luZyB2YXJpYWJsZXM6DQoNClRoZSByZXNwb25zZSB2YXJpYWJsZToNCg0KLSBgUkVOVEVEIEJJS0UgQ09VTlRgLSBDb3VudCBvZiBiaWtlcyByZW50ZWQgYXQgZWFjaCBob3VyDQoNCldlYXRoZXIgcHJlZGljdG9yIHZhcmlhYmxlczoNCg0KLSBgVEVNUEVSQVRVUkVgIC0gVGVtcGVyYXR1cmUgaW4gQ2Vsc2l1cw0KLSBgSFVNSURJVFlgIC0gVW5pdCBpcyBgJWANCi0gYFdJTkRfU1BFRURgIC0gVW5pdCBpcyBgbS9zYA0KLSBgVklTSUJJTElUWWAgLSBNdWx0aXBsaWVkIGJ5IDEwbQ0KLSBgREVXX1BPSU5UX1RFTVBFUkFUVVJFYCAtIFRoZSB0ZW1wZXJhdHVyZSB0byB3aGljaCB0aGUgYWlyIHdvdWxkIGhhdmUgdG8gY29vbCBkb3duIGluIG9yZGVyIHRvIHJlYWNoIHNhdHVyYXRpb24sIHVuaXQgaXMgQ2Vsc2l1cw0KLSBgU09MQVJfUkFESUFUSU9OYCAtIE1KL20yDQotIGBSQUlORkFMTGAgLSBtbQ0KLSBgU05PV0ZBTExgIC0gY20NCg0KRGF0ZS90aW1lIHByZWRpY3RvciB2YXJpYWJsZXM6DQoNCi0gYERBVEVgIC0gWWVhci1tb250aC1kYXkNCi0gYEhPVVJgLSBIb3VyIG9mIGhlIGRheQ0KLSBgRlVOQ1RJT05BTCBEQVlgIC0gTm9GdW5jKE5vbiBGdW5jdGlvbmFsIEhvdXJzKSwgRnVuKEZ1bmN0aW9uYWwgaG91cnMpDQotIGBIT0xJREFZYCAtIEhvbGlkYXkvTm8gaG9saWRheQ0KLSBgU0VBU09OU2AgLSBXaW50ZXIsIFNwcmluZywgU3VtbWVyLCBBdXR1bW4NCg0KDQojIyMjIDIuMiBMb2FkIGRhdGFiYXNlDQoNCmBgYHtyfQ0Kc2VvdWxfYmlrZV9zaGFyaW5nX2NvbnZlcnRlZF9ub3JtYWxpemVkIDwtIHJlYWRfY3N2KCJCYXNlcyBsaW1waWFzL3Nlb3VsX2Jpa2Vfc2hhcmluZ19jb252ZXJ0ZWRfbm9ybWFsaXplZC5jc3YiKQ0KYGBgDQoNCiMjIyMgMi4zIENvbnZlcnQgaW50byBhIGRmDQoNCmBgYHtyfQ0KYmlrZV9zaGFyaW5nX2RmIDwtIHNlb3VsX2Jpa2Vfc2hhcmluZ19jb252ZXJ0ZWRfbm9ybWFsaXplZCAlPiUgDQogICAgICAgICAgICAgICAgICAgc2VsZWN0KC1EQVRFLCAtRlVOQ1RJT05JTkdfREFZX1lFUywtRlVOQ1RJT05JTkdfREFZX05PKQ0KYGBgDQoNCldlIHdpbGwgbm90IGJlIHV0aWxpemluZyB0aGUgYERBVEVgIGNvbHVtbiBpbiBpdHMgY3VycmVudCBmb3JtLCBhcyBpdCBlc3NlbnRpYWxseSBmdW5jdGlvbnMgYXMgYSBkYXRhIGVudHJ5IGluZGV4LiBIb3dldmVyLCB3aXRoIGFkZGl0aW9uYWwgdGltZSwgd2UgY291bGQgdHJhbnNmb3JtIHRoZSBgREFURWAgY29sdW1uIHRvIGRlcml2ZSBuZXcgZmVhdHVyZXMgc3VjaCBhcyAnZGF5IG9mIHRoZSB3ZWVrJyBvciAnaXNXZWVrZW5kJywgd2hpY2ggbWlnaHQgaW5mbHVlbmNlIGJpa2UgcmVudGFsIHByZWZlcmVuY2VzLiBBZGRpdGlvbmFsbHksIHRoZSBgRlVOQ1RJT05BTCBEQVlgIGNvbHVtbiB3aWxsIG5vdCBiZSB1c2VkIGJlY2F1c2UsIGFmdGVyIHByb2Nlc3NpbmcgbWlzc2luZyB2YWx1ZXMsIGl0IG9ubHkgY29udGFpbnMgYSBzaW5nbGUgZGlzdGluY3QgdmFsdWUgKGBZRVNgKS4NCg0KIyMjIDMuIFNwbGl0IHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGEgDQoNCmBgYHtyfQ0KYmlrZV9zcGxpdCA8LSBpbml0aWFsX3NwbGl0KGJpa2Vfc2hhcmluZ19kZiwgcHJvcCA9IDMvNCkNCnRyYWluX2RhdGEgPC0gdHJhaW5pbmcoYmlrZV9zcGxpdCkNCnRlc3RfZGF0YSA8LSB0ZXN0aW5nKGJpa2Vfc3BsaXQpDQoNCmBgYA0KDQojIyMjIDMuMSBCdWlsZCBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHVzaW5nIHdlYXRoZXIgdmFyaWFibGVzIG9ubHkNCg0KV2VhdGhlciBjb25kaXRpb25zIGFyZSBsaWtlbHkgdG8gaW5mbHVlbmNlIGluZGl2aWR1YWxzJyBkZWNpc2lvbnMgcmVnYXJkaW5nIGJpa2UgcmVudGFscy4gRm9yIGluc3RhbmNlLCBhZHZlcnNlIHdlYXRoZXIgc3VjaCBhcyBjb2xkIGFuZCByYWlueSBjb25kaXRpb25zIG1heSBsZWFkIHBlb3BsZSB0byBvcHQgZm9yIGFsdGVybmF0aXZlIG1vZGVzIG9mIHRyYW5zcG9ydGF0aW9uIGxpa2UgYnVzZXMgb3IgdGF4aXMuIENvbnZlcnNlbHksIGZhdm9yYWJsZSB3ZWF0aGVyLCBzdWNoIGFzIHN1bm55IGRheXMsIG1heSBpbmNyZWFzZSB0aGUgcHJvcGVuc2l0eSB0byByZW50IGJpa2VzIGZvciBzaG9ydC1kaXN0YW5jZSB0cmF2ZWwuDQoNCmBgYHtyfQ0KIyBQaWNrIGxpbmVhciByZWdyZXNzaW9uDQpsbV9zcGVjIDwtIGxpbmVhcl9yZWcoKSAlPiUNCiAgIyBTZXQgZW5naW5lJw0KICBzZXRfZW5naW5lKGVuZ2luZSA9ICJsbSIpDQoNCiMgUHJpbnQgdGhlIGxpbmVhciBmdW5jdGlvbg0KbG1fc3BlYw0KYGBgDQpgYGB7cn0NCiMgVG8gIGZpdCB0aGUgbW9kZWwgDQoNCmxtX21vZGVsX3dlYXRoZXIgPC0gbG1fc3BlYyAlPiUgDQogIGZpdChSRU5URURfQklLRV9DT1VOVCB+IFRFTVBFUkFUVVJFICsgSFVNSURJVFkgKyBXSU5EX1NQRUVEICsgVklTSUJJTElUWSArIERFV19QT0lOVF9URU1QRVJBVFVSRSArIFNPTEFSX1JBRElBVElPTiArIFJBSU5GQUxMICsgU05PV0ZBTEwsIGRhdGEgPSB0cmFpbl9kYXRhKQ0KYGBgDQoNClByaW50IHRoZSBmaXQgc3VtbWFyeSBmb3IgdGhlIGBsbV9tb2RlbF93ZWF0aGVyYCBtb2RlbC4NCg0KYGBge3IgLCBtZXNzYWdlPUZBTFNFLCBldmFsPSBGYWxzZX0NCg0KDQojIENyZWF0ZSB0aGUgdGFibGUgd2l0aCB0aGUgcmVncmVzc2lvbiByZXN1bHRzDQoNCnN1bW1hcnkobG1fbW9kZWxfd2VhdGhlciRmaXQpDQoNCmBgYA0KDQoNClRoZSByZWdyZXNzaW9uIGFuYWx5c2lzIGFpbXMgdG8gcHJlZGljdCB0aGUgYFJFTlRFRF9CSUtFX0NPVU5UYCB1c2luZyBzZXZlcmFsIGluZGVwZW5kZW50IHZhcmlhYmxlczogYFRFTVBFUkFUVVJFYCwgYEhVTUlESVRZYCwgYFdJTkRfU1BFRURgLCBgVklTSUJJTElUWWAsIGBERVdfUE9JTlRfVEVNUEVSQVRVUkVgLCBgU09MQVJfUkFESUFUSU9OYCwgYFJBSU5GQUxMYCwgYW5kIGBTTk9XRkFMTGAuIFRoZSBJbnRlcmNlcHQgaGFzIGFuIGVzdGltYXRlIG9mIF8wLjA0NjQ3MiAgXywgaW5kaWNhdGluZyB0aGUgYmFzZWxpbmUgbGV2ZWwgb2YgYmlrZSByZW50YWxzIHdoZW4gYWxsIG90aGVyIHZhcmlhYmxlcyBhcmUgemVyby4NCg0KYFRFTVBFUkFUVVJFYCBoYXMgYSBwb3NpdGl2ZSBjb2VmZmljaWVudCBvZiBfMC42MzEzMTggICBfLCBzdWdnZXN0aW5nIHRoYXQgYXMgdGhlIHRlbXBlcmF0dXJlIGluY3JlYXNlcywgdGhlIG51bWJlciBvZiByZW50ZWQgYmlrZXMgYWxzbyBpbmNyZWFzZXMuIFRoaXMgcmVsYXRpb25zaGlwIGlzIGhpZ2hseSBzaWduaWZpY2FudCB3aXRoIGEgcC12YWx1ZSBsZXNzIHRoYW4gNS4xN2UtMTYuIGBIVU1JRElUWWAgaGFzIGEgbmVnYXRpdmUgY29lZmZpY2llbnQgb2YgXy0wLjI3NjcwMl8sIGluZGljYXRpbmcgdGhhdCBoaWdoZXIgaHVtaWRpdHkgbGV2ZWxzIGFyZSBhc3NvY2lhdGVkIHdpdGggZmV3ZXIgYmlrZSByZW50YWxzLCBhbmQgdGhpcyBlZmZlY3QgaXMgYWxzbyBoaWdobHkgc2lnbmlmaWNhbnQuDQoNCiBgVklTSUJJTElUWWAgaGFzIGEgdmVyeSBzbWFsbCBwb3NpdGl2ZSBjb2VmZmljaWVudCAoIF8wLjAwNjE3OSAgXyApLCBpbmRpY2F0aW5nIGEgc2xpZ2h0IGluY3JlYXNlIGluIGJpa2UgcmVudGFscyB3aXRoIGJldHRlciB2aXNpYmlsaXR5LCBhbmQgdGhpcyBlZmZlY3QgaXMgc2lnbmlmaWNhbnQuIGBTT0xBUl9SQURJQVRJT05gIGhhcyBhIHN0cm9uZyBwb3NpdGl2ZSBjb2VmZmljaWVudCBvZiBfMS4wMzQ4MDBfLCBzaG93aW5nIHRoYXQgaGlnaGVyIHNvbGFyIHJhZGlhdGlvbiBsZXZlbHMgc2lnbmlmaWNhbnRseSBpbmNyZWFzZSBiaWtlIHJlbnRhbHMuIGBSQUlORkFMTGAgYW5kIGBTTk9XRkFMTGAgYm90aCBoYXZlICBjb2VmZmljaWVudHMgKCBfLTAuNTgyMDQxXyBhbmQgXy0wLjA5OTQzOV8sIHJlc3BlY3RpdmVseSksIGluZGljYXRpbmcgdGhhdCBtb3JlIHJhaW5mYWxsIGFuZCBzbm93ZmFsbCBsZWFkIHRvIGZld2VyIGJpa2UgcmVudGFscy4gVGhlc2UgZWZmZWN0cyBhcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4NCg0KVGhlICoqcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IqKiBpcyBfMC4xMzcxXywgaW5kaWNhdGluZyB0aGUgYXZlcmFnZSBkaXN0YW5jZSB0aGF0IHRoZSBvYnNlcnZlZCB2YWx1ZXMgZmFsbCBmcm9tIHRoZSByZWdyZXNzaW9uIGxpbmUuIFRoZSBtdWx0aXBsZSBSLXNxdWFyZWQgYW5kIGFkanVzdGVkIFItc3F1YXJlZCB2YWx1ZXMsIGFsb25nIHdpdGggdGhlIEYtc3RhdGlzdGljLCBhcmUgbm90IHByb3ZpZGVkIGluIHRoZSBpbWFnZSwgYnV0IHRoZXkgd291bGQgdHlwaWNhbGx5IGluZGljYXRlIHRoZSBvdmVyYWxsIGZpdCBvZiB0aGUgbW9kZWwgYW5kIHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlIHJlZ3Jlc3Npb24gZXF1YXRpb24sIHJlc3BlY3RpdmVseS4NCg0KT3ZlcmFsbCwgdGhlIGFuYWx5c2lzIHNob3dzIHRoYXQgd2VhdGhlciBjb25kaXRpb25zIHNpZ25pZmljYW50bHkgaW1wYWN0IGJpa2UgcmVudGFscywgd2l0aCB0ZW1wZXJhdHVyZSBhbmQgc29sYXIgcmFkaWF0aW9uIGhhdmluZyB0aGUgbW9zdCBzdWJzdGFudGlhbCBwb3NpdGl2ZSBlZmZlY3RzLCB3aGlsZSBodW1pZGl0eSwgd2luZCBzcGVlZCwgcmFpbmZhbGwsIGFuZCBzbm93ZmFsbCBuZWdhdGl2ZWx5IGFmZmVjdCBiaWtlIHJlbnRhbHMuDQoNCiMjIyMgMy4yIEJ1aWxkIGEgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgdXNpbmcgYWxsIHZhcmlhYmxlcw0KDQpgYGB7cn0NCmxtX21vZGVsX2FsbCA8LSBsbV9zcGVjICU+JSANCiAgZml0KFJFTlRFRF9CSUtFX0NPVU5UIH4gLiwgZGF0YSA9IHRyYWluX2RhdGEpDQpgYGANCg0KUHJpbnQgdGhlIGZpdCBzdW1tYXJ5IGZvciBgbG1fbW9kZWxfYWxsYC4NCg0KYGBge3J9DQpzdW1tYXJ5KGxtX21vZGVsX2FsbCRmaXQpDQpgYGANCg0KVGhlIG1vZGVsIGV4cGxhaW5zIGFwcHJveGltYXRlbHkgKio2Ni4yJSoqIG9mIHRoZSB2YXJpYW5jZSBpbiBgTU9ERUxfQklLRV9DT1VOVGAsIGFzIGluZGljYXRlZCBieSB0aGUgUi1zcXVhcmVkIHZhbHVlLiBUaGUgc2lnbmlmaWNhbnQgcHJlZGljdG9ycyAoZS5nLiwgYEhPVVJgLCBgVEVNUEVSQVRVUkVgKSBzdWdnZXN0IHRoYXQgdGhlc2UgZmFjdG9ycyBoYXZlIGEgc3Vic3RhbnRpYWwgaW1wYWN0IG9uIGJpa2UgY291bnQuIFRoZSBoaWdoIEYtc3RhdGlzdGljIGFuZCBpdHMgY29ycmVzcG9uZGluZyBwLXZhbHVlIGluZGljYXRlIHRoYXQgdGhlIG1vZGVsIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgb3ZlcmFsbC4NCg0KDQojIyMgNC4gTW9kZWwgZXZhbHVhdGlvbiANCg0KTW9kZWwgZXZhbHVhdGlvbiBpcyBjcnVjaWFsIGluIHJlZ3Jlc3Npb24gYW5hbHlzaXMgYmVjYXVzZSBpdCBoZWxwcyBkZXRlcm1pbmUgaG93IHdlbGwgYSBtb2RlbCBmaXRzIHRoZSBkYXRhIGFuZCBwcmVkaWN0cyBvdXRjb21lcy4gKipSLXNxdWFyZWQqKiBpcyBhIGtleSBtZXRyaWMgdGhhdCBpbmRpY2F0ZXMgdGhlIHByb3BvcnRpb24gb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgdGhhdCBpcyBwcmVkaWN0YWJsZSBmcm9tIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMuIEEgaGlnaGVyIFItc3F1YXJlZCB2YWx1ZSBzaWduaWZpZXMgdGhhdCB0aGUgbW9kZWwgZXhwbGFpbnMgYSBncmVhdGVyIHBvcnRpb24gb2YgdGhlIHZhcmlhbmNlLCBzdWdnZXN0aW5nIGEgYmV0dGVyIGZpdC4gVGhpcyBpcyBpbXBvcnRhbnQgZm9yIHVuZGVyc3RhbmRpbmcgdGhlIHN0cmVuZ3RoIG9mIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgcHJlZGljdG9ycyBhbmQgdGhlIG91dGNvbWUsIGFuZCBmb3IgYXNzZXNzaW5nIHRoZSBtb2RlbCdzIGV4cGxhbmF0b3J5IHBvd2VyLg0KDQoqKlJNU0UgKFJvb3QgTWVhbiBTcXVhcmUgRXJyb3IpKiosIG9uIHRoZSBvdGhlciBoYW5kLCBtZWFzdXJlcyB0aGUgYXZlcmFnZSBtYWduaXR1ZGUgb2YgdGhlIGVycm9ycyBiZXR3ZWVuIHByZWRpY3RlZCBhbmQgb2JzZXJ2ZWQgdmFsdWVzLiBJdCBwcm92aWRlcyBpbnNpZ2h0IGludG8gdGhlIG1vZGVsJ3MgcHJlZGljdGl2ZSBhY2N1cmFjeS4gQSBsb3dlciBSTVNFIGluZGljYXRlcyB0aGF0IHRoZSBtb2RlbCdzIHByZWRpY3Rpb25zIGFyZSBjbG9zZXIgdG8gdGhlIGFjdHVhbCB2YWx1ZXMsIHdoaWNoIGlzIGVzc2VudGlhbCBmb3IgbWFraW5nIHJlbGlhYmxlIGZvcmVjYXN0cy4gRXZhbHVhdGluZyBtb2RlbHMgdXNpbmcgYm90aCBSLXNxdWFyZWQgYW5kIFJNU0UgZW5zdXJlcyBhIGJhbGFuY2VkIGFzc2Vzc21lbnQgb2YgdGhlaXIgcGVyZm9ybWFuY2UsIGNvbnNpZGVyaW5nIGJvdGggdGhlIGdvb2RuZXNzIG9mIGZpdCBhbmQgdGhlIHByZWNpc2lvbiBvZiBwcmVkaWN0aW9ucy4gVGhpcyBjb21wcmVoZW5zaXZlIGV2YWx1YXRpb24gaGVscHMgaW4gc2VsZWN0aW5nIHRoZSBtb3N0IGFwcHJvcHJpYXRlIG1vZGVsIGZvciBwcmFjdGljYWwgYXBwbGljYXRpb25zLg0KDQoNCg0KDQojIyMjIDQuMSBSb290IE1lYW4gU3F1YXJlZCBFcnJvciAoUk1TRSkNCg0KYGBge3IgLCB3YXJuaW5nPUZBTFNFfQ0KIyBNYWtpbmcgdGhlIHByZWRpY3Rpb25zDQpwcmVkaWN0aW9uc193ZWF0aGVyIDwtIHByZWRpY3QobG1fbW9kZWxfd2VhdGhlciwgbmV3X2RhdGEgPSB0ZXN0X2RhdGEpDQpwcmVkaWN0aW9uc19hbGwgPC0gcHJlZGljdChsbV9tb2RlbF9hbGwsIG5ld19kYXRhID0gdGVzdF9kYXRhKQ0KIyBDYWxjdWxhdGluZyBlcnJvcnMNCmVycm9yX3dlYXRoZXIgPC0gdHJhaW5fZGF0YSRSRU5URURfQklLRV9DT1VOVCAtIHByZWRpY3Rpb25zX3dlYXRoZXINCmVycm9yX2FsbCA8LSB0cmFpbl9kYXRhJFJFTlRFRF9CSUtFX0NPVU5UIC0gcHJlZGljdGlvbnNfYWxsDQojIENhbGN1bGF0aW5nIFNxdWFyZWQgRXJyb3JzDQpzcXVhcmVkX2Vycm9yX3dlYXRoZXIgPC0gZXJyb3Jfd2VhdGhlcl4yDQpzcXVhcmVkX2Vycm9yX2FsbCA8LSBlcnJvcl9hbGxeMg0KIyBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2Ugb2YgdGhlIHNxdWFyZWQgZXJyb3JzDQptZWFuX3NxdWFyZWRfZXJyb3Jfd2VhdGhlciA8LSBtZWFuKHNxdWFyZWRfZXJyb3Jfd2VhdGhlciQucHJlZCkNCm1lYW5fc3F1YXJlZF9lcnJvcl9hbGwgPC0gbWVhbihzcXVhcmVkX2Vycm9yX2FsbCQucHJlZCkNCiMgQ2FsY3VsYXRlIFJNU0UNCnJtc2Vfd2VhdGhlciA8LSBzcXJ0KG1lYW5fc3F1YXJlZF9lcnJvcl93ZWF0aGVyKQ0Kcm1zZV9hbGwgPC0gc3FydChtZWFuX3NxdWFyZWRfZXJyb3JfYWxsKQ0KDQpgYGANCg0KIyMjIyA0LjIgUi1zcXVhcmVkDQpgYGB7cn0NCiMgR2V0IHRoZSBSIHNxdWFyZWQgZnJvbSB0aGUgbW9kZWwNCnN1bW1hcnlfbV93ZWF0aGVyIDwtIHN1bW1hcnkobG1fbW9kZWxfd2VhdGhlciRmaXQpDQpzdW1tYXJ5X21fYWxsIDwtIHN1bW1hcnkobG1fbW9kZWxfYWxsJGZpdCkNCiMgUHJpbnQgdGhlIFIgc3F1YXJlZA0KcjJfd2VhdGhlciA8LSBzdW1tYXJ5X21fd2VhdGhlciRyLnNxdWFyZWQNCnIyX2FsbCA8LSBzdW1tYXJ5X21fYWxsJHIuc3F1YXJlZA0KYGBgDQoNCiMjIyMgNC4zIENvbXBhcmluZyBtb2RlbHMNCmBgYHtyfQ0KDQojQ3JlYXRlIERhcmFmcmFtZQ0KcmVzdWx0cyA8LSBkYXRhLmZyYW1lKA0KICBNb2RlbCA9IGMoIldlYXRoZXIgTW9kZWwiLCAiQWxsIFZhcmlhYmxlcyBNb2RlbCIpLA0KICBSX3NxdWFyZWQgPSBjKHIyX3dlYXRoZXIsIHIyX2FsbCksDQogIFJNU0UgPSBjKHJtc2Vfd2VhdGhlciwgcm1zZV9hbGwpDQopDQoNCnByaW50KHJlc3VsdHMpDQpgYGANCg0KVGhlICoqIldlYXRoZXIgTW9kZWwiKiogaGFzIGFuIFItc3F1YXJlZCB2YWx1ZSBvZiAwLjQzMDM0NjEgYW5kIGFuIFJNU0Ugb2YgMC4yMjI0NTU0IFRoZSBSLXNxdWFyZWQgdmFsdWUgaW5kaWNhdGVzIHRoYXQgYXBwcm94aW1hdGVseSA0My4wMyUgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgY2FuIGJlIGV4cGxhaW5lZCBieSB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGluIHRoaXMgbW9kZWwuIFRoZSBSTVNFIHZhbHVlIHJlcHJlc2VudHMgdGhlIHJvb3QgbWVhbiBzcXVhcmUgZXJyb3IsIHdoaWNoIG1lYXN1cmVzIHRoZSBhdmVyYWdlIG1hZ25pdHVkZSBvZiB0aGUgZXJyb3JzIGJldHdlZW4gdGhlIHByZWRpY3RlZCBhbmQgb2JzZXJ2ZWQgdmFsdWVzLiBBIGxvd2VyIFJNU0UgaW5kaWNhdGVzIGJldHRlciBwcmVkaWN0aXZlIGFjY3VyYWN5Lg0KDQpPbiB0aGUgb3RoZXIgaGFuZCwgdGhlICoqIkFsbCBWYXJpYWJsZXMgTW9kZWwiKiogaGFzIGFuIFItc3F1YXJlZCB2YWx1ZSBvZiAwLjY2MDIzMDQgYW5kIGFuIFJNU0Ugb2YgMC4yMzQ4OTI4IFRoaXMgbW9kZWwgZXhwbGFpbnMgYXBwcm94aW1hdGVseSA2Ni4wMiUgb2YgdGhlIHZhcmlhbmNlIGluIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUsIHdoaWNoIGlzIGhpZ2hlciB0aGFuIHRoZSAiV2VhdGhlciBNb2RlbC4iIEhvd2V2ZXIsIHRoZSBSTVNFIGlzIGhpZ2hlciBhdCA3OTUuOTY1OCwgaW5kaWNhdGluZyB0aGF0IHRoZSBhdmVyYWdlIHByZWRpY3Rpb24gZXJyb3IgaXMgbGFyZ2VyIGNvbXBhcmVkIHRvIHRoZSAiV2VhdGhlciBNb2RlbC4iDQoNClRvIGRldGVybWluZSB0aGUgYmVzdCBtb2RlbCwgd2UgbmVlZCB0byBjb25zaWRlciB0aGUgdHJhZGUtb2ZmIGJldHdlZW4gdGhlIGdvb2RuZXNzIG9mIGZpdCAoUi1zcXVhcmVkKSBhbmQgdGhlIHByZWRpY3RpdmUgYWNjdXJhY3kgKFJNU0UpLiBUaGUgKioiQWxsIFZhcmlhYmxlcyBNb2RlbCIqKiBoYXMgYSBoaWdoZXIgUi1zcXVhcmVkIHZhbHVlLCBzdWdnZXN0aW5nIGl0IGZpdHMgdGhlIGRhdGEgYmV0dGVyIGFuZCBleHBsYWlucyBtb3JlIHZhcmlhbmNlLiBIb3dldmVyLCBpdHMgaGlnaGVyIFJNU0UgaW5kaWNhdGVzIHRoYXQgaXRzIHByZWRpY3Rpb25zIGFyZSBsZXNzIGFjY3VyYXRlIG9uIGF2ZXJhZ2UgY29tcGFyZWQgdG8gdGhlICoqIldlYXRoZXIgTW9kZWwuIioqDQoNCklmIHRoZSBwcmltYXJ5IGdvYWwgaXMgdG8gaGF2ZSBhIG1vZGVsIHRoYXQgZXhwbGFpbnMgbW9yZSB2YXJpYW5jZSBpbiB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLCB0aGUgKioiQWxsIFZhcmlhYmxlcyBNb2RlbCIqKiB3b3VsZCBiZSBwcmVmZXJyZWQgZHVlIHRvIGl0cyBoaWdoZXIgUi1zcXVhcmVkIHZhbHVlLiBDb252ZXJzZWx5LCBpZiB0aGUgZ29hbCBpcyB0byBtaW5pbWl6ZSBwcmVkaWN0aW9uIGVycm9ycywgdGhlICoqIldlYXRoZXIgTW9kZWwiKiogd291bGQgYmUgYmV0dGVyIGR1ZSB0byBpdHMgbG93ZXIgUk1TRS4NCg0KSW4gc3VtbWFyeSwgdGhlIGNob2ljZSBvZiB0aGUgYmVzdCBtb2RlbCBkZXBlbmRzIG9uIHRoZSBzcGVjaWZpYyBvYmplY3RpdmVzIG9mIHRoZSBhbmFseXNpcy4gSWYgZXhwbGFpbmluZyBtb3JlIHZhcmlhbmNlIGlzIHByaW9yaXRpemVkLCB0aGUgKioiQWxsIFZhcmlhYmxlcyBNb2RlbCIqKiBpcyBiZXR0ZXIuIElmIG1pbmltaXppbmcgcHJlZGljdGlvbiBlcnJvcnMgaXMgbW9yZSBpbXBvcnRhbnQsIHRoZSAqKiJXZWF0aGVyIE1vZGVsIioqIGlzIHRoZSBwcmVmZXJyZWQgY2hvaWNlLg0KDQojIyMjIDQuNCBCYXIgQ2hhcnQgZm9yIENvZWZmaWNpZW50cw0KDQpgYGB7cn0NCiMgT2J0YWluIHRoZSBtb2RlbCBjb2VmZmljaWVudHMNCmNvZWZmaWNpZW50c19hbGwgPC0gdGlkeShsbV9tb2RlbF9hbGwpDQoNCiMgQ3JlYXRlIGEgYmFyIGNoYXJ0IHRvIHZpc3VhbGl6ZSB0aGUgY29lZmZpY2llbnRzDQpnZ3Bsb3QoY29lZmZpY2llbnRzX2FsbCwgYWVzKHggPSByZW9yZGVyKHRlcm0sIGVzdGltYXRlKSwgeSA9IGVzdGltYXRlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJtYXJvb24iKSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBlc3RpbWF0ZSAtIHN0ZC5lcnJvciwgeW1heCA9IGVzdGltYXRlICsgc3RkLmVycm9yKSwgd2lkdGggPSAwLjIsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKHRpdGxlID0gIkNvZWZmaWNpZW50cyBvZiBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCAoQWxsKSIsDQogICAgICAgeCA9ICJQcmVkaWN0b3IgVmFyaWFibGVzIiwNCiAgICAgICB5ID0gIkNvZWZmaWNpZW50IEVzdGltYXRlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpDQogICkgKw0KICBjb29yZF9mbGlwKCkgIyBGbGlwIHRoZSBjaGFydCBmb3IgYmV0dGVyIHZpZXdpbmcNCg0KYGBgDQoNCiMjIyMgNC41IEJhciBDaGFydCBmb3IgQ29lZmZpY2llbnRzDQoNCmBgYHtyfQ0KDQojIE9idGFpbiB0aGUgbW9kZWwgY29lZmZpY2llbnRzDQpjb2VmZmljaWVudHNfd2VhdGhlciA8LSB0aWR5KGxtX21vZGVsX3dlYXRoZXIpDQoNCiMgQ3JlYXRlIGEgYmFyIGNoYXJ0IHRvIHZpc3VhbGl6ZSB0aGUgY29lZmZpY2llbnRzDQpnZ3Bsb3QoY29lZmZpY2llbnRzX3dlYXRoZXIsIGFlcyh4ID0gcmVvcmRlcih0ZXJtLCBlc3RpbWF0ZSksIHkgPSBlc3RpbWF0ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gZXN0aW1hdGUgLSBzdGQuZXJyb3IsIHltYXggPSBlc3RpbWF0ZSArIHN0ZC5lcnJvciksIHdpZHRoID0gMC4yLCBjb2xvciA9ICJkYXJrcmVkIikgKw0KICBsYWJzKHRpdGxlID0gIkNvZWZmaWNpZW50cyBvZiBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbCAoV2VhdGhlcikiLA0KICAgICAgIHggPSAiUHJlZGljdG9yIFZhcmlhYmxlcyIsDQogICAgICAgeSA9ICJDb2VmZmljaWVudCBFc3RpbWF0ZSIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKQ0KICApICsNCiAgY29vcmRfZmxpcCgpICAjIEZsaXAgdGhlIGNoYXJ0IGZvciBiZXR0ZXIgdmlld2luZw0KDQoNCmBgYA0KDQoNCiMjIyA1LiBBZGQgcG9seW5vbWlhbCB0ZXJtcw0KDQpgYGB7cn0NCg0KIyBQbG90IHRoZSBoaWdoZXIgb3JkZXIgcG9seW5vbWlhbCBmaXRzDQoNCmdncGxvdCh0cmFpbl9kYXRhLCBhZXMoeCA9IFJFTlRFRF9CSUtFX0NPVU5ULCB5ID0gVEVNUEVSQVRVUkUpKSArIA0KICBnZW9tX3BvaW50KCkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiBwb2x5KHgsIDIpLCBjb2xvciA9ICJyZWQiLCBzZSA9IEZBTFNFKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHBvbHkoeCwgMyksIGNvbG9yID0gImJsdWUiLCBzZSA9IEZBTFNFKSArIA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHBvbHkoeCwgNCksIGNvbG9yID0gImdyZWVuIiwgc2UgPSBGQUxTRSkgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9IHkgfiBwb2x5KHgsIDUpLCBjb2xvciA9ICJwdXJwbGUiLCBzZSA9IEZBTFNFKSArDQogIGxhYnModGl0bGUgPSAiUG9seW5vbWlhbCBSZWdyZXNzaW9uIEZpdHMiLA0KICAgICAgIHggPSAiUmVudGVkIEJpa2VzIiwNCiAgICAgICB5ID0gIlRlbXBlcmF0dXJlIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpDQogICkNCg0KYGBgDQoNCiMjIyMgNS4xIEZpdCB0aGUgUG9seW5vbWlhbCBSZWdyZXNzaW9uIE1vZGVsDQoNCg0KYGBge3J9DQojIEFzc3VtaW5nIHRoZSBpbXBvcnRhbnQgdmFyaWFibGVzIGFyZSBURU1QRVJBVFVSRSBhbmQgSFVNSURJVFkNCmxtX2Jpa2x5IDwtIGxtKFJFTlRFRF9CSUtFX0NPVU5UIH4gcG9seShURU1QRVJBVFVSRSwgMikgKyBwb2x5KEhVTUlESVRZLCAyKSwgZGF0YSA9IHRyYWluX2RhdGEpDQoNCiMgUHJpbnQgdGhlIG1vZGVsIHN1bW1hcnkNCnN1bW1hcnkobG1fYmlrbHkpDQoNCmBgYA0KVGhlIHJlZ3Jlc3Npb24gYW5hbHlzaXMgcHJlc2VudGVkIGFpbXMgdG8gcHJlZGljdCB0aGUgYFJFTlRFRF9CSUtFX0NPVU5UYCBiYXNlZCBvbiBwb2x5bm9taWFsIHRyYW5zZm9ybWF0aW9ucyBvZiBgVEVNUEVSQVRVUkVgIGFuZCBgSFVNSURJVFlgLiBUaGlzIGFwcHJvYWNoIGFsbG93cyBmb3IgY2FwdHVyaW5nIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZXNlIHByZWRpY3RvcnMgYW5kIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUsIHdoaWNoIGNhbiBiZSBtb3JlIHJlZmxlY3RpdmUgb2YgcmVhbC13b3JsZCBzY2VuYXJpb3Mgd2hlcmUgY2hhbmdlcyBpbiB0ZW1wZXJhdHVyZSBhbmQgaHVtaWRpdHkgbWlnaHQgbm90IGhhdmUgYSBzdHJhaWdodGZvcndhcmQgbGluZWFyIGVmZmVjdCBvbiBiaWtlIHJlbnRhbHMuDQoNCiMjIyMjIDUuMS4xIE1vZGVsIFN1bW1hcnkNClRoZSByZWdyZXNzaW9uIG1vZGVsIGluY2x1ZGVzIHBvbHlub21pYWwgdGVybXMgZm9yIGJvdGggYFRFTVBFUkFUVVJFYCBhbmQgYEhVTUlESVRZYCwgc3BlY2lmaWNhbGx5IHVwIHRvIHRoZSBzZWNvbmQgZGVncmVlLiAgVGhlIGNvZWZmaWNpZW50cyB0YWJsZSBzaG93cyB0aGUgZXN0aW1hdGVkIGVmZmVjdHMgb2YgZWFjaCBwcmVkaWN0b3IsIGFsb25nIHdpdGggdGhlaXIgc3RhbmRhcmQgZXJyb3JzLCB0LXZhbHVlcywgYW5kIHAtdmFsdWVzLiBUaGUgaW50ZXJjZXB0IGlzIGhpZ2hseSBzaWduaWZpY2FudCwgd2l0aCBhbiBlc3RpbWF0ZSBhIHZlcnkgbG93IHAtdmFsdWUgKDwyZS0xNiksIGluZGljYXRpbmcgYSBzdHJvbmcgYmFzZWxpbmUgZWZmZWN0IHdoZW4gYWxsIHByZWRpY3RvcnMgYXJlIGF0IHRoZWlyIG1lYW4gdmFsdWVzLg0KDQojIyMjIyA1LjEuMiBDb2VmZmljaWVudHMgYW5kIFNpZ25pZmljYW5jZQ0KVGhlIGZpcnN0LWRlZ3JlZSBwb2x5bm9taWFsIHRlcm0gZm9yIGBURU1QRVJBVFVSRWAgaGFzIGEgbGFyZ2UgbmVnYXRpdmUgY29lZmZpY2llbnQgIGFuZCBpcyBoaWdobHkgc2lnbmlmaWNhbnQgKHAtdmFsdWUgPDJlLTE2KSwgc3VnZ2VzdGluZyB0aGF0IGFzIHRlbXBlcmF0dXJlIGluY3JlYXNlcywgdGhlIG51bWJlciBvZiByZW50ZWQgYmlrZXMgZGVjcmVhc2VzIHNpZ25pZmljYW50bHkuIFRoZSBzZWNvbmQtZGVncmVlIHRlcm0gZm9yIGBURU1QRVJBVFVSRWAgaGFzIGEgbmVnYXRpdmUgY29lZmZpY2llbnQgIGFuZCBpcyBhbHNvIHNpZ25pZmljYW50IChwLXZhbHVlIDwyZS0xNiksIGluZGljYXRpbmcgYSBkaW1pbmlzaGluZyByZXR1cm4gZWZmZWN0OyBhdCBoaWdoZXIgdGVtcGVyYXR1cmVzLCB0aGUgZGVjcmVhc2UgaW4gYmlrZSByZW50YWxzIHNsb3dzIGRvd24uIFNpbWlsYXJseSwgdGhlIGZpcnN0LWRlZ3JlZSBwb2x5bm9taWFsIHRlcm0gZm9yIGBIVU1JRElUWWAgaGFzIGEgc2lnbmlmaWNhbnQgbmVnYXRpdmUgY29lZmZpY2llbnQgLCBzdWdnZXN0aW5nIHRoYXQgaGlnaGVyIGh1bWlkaXR5IGxldmVscyByZWR1Y2UgYmlrZSByZW50YWxzLiBUaGUgc2Vjb25kLWRlZ3JlZSB0ZXJtIGZvciBgSFVNSURJVFlgIGFsc28gaGFzIGEgbmVnYXRpdmUgY29lZmZpY2llbnQgcmVsYXRpb25zaGlwLg0KDQojIyMjIyA1LjEuMyBNb2RlbCBGaXQNClRoZSBtb2RlbCdzIG11bHRpcGxlIFItc3F1YXJlZCB2YWx1ZSBpcyAwLjQ2NjYsIGluZGljYXRpbmcgdGhhdCBhcHByb3hpbWF0ZWx5IDQ0LjY5JSBvZiB0aGUgdmFyaWFuY2UgaW4gYFJFTlRFRF9CSUtFX0NPVU5UYCBpcyBleHBsYWluZWQgYnkgdGhlIG1vZGVsLiBUaGUgYWRqdXN0ZWQgUi1zcXVhcmVkIHZhbHVlIGlzIHRoZSBzYW1lLCBzdWdnZXN0aW5nIHRoYXQgdGhlIG1vZGVsJ3MgZXhwbGFuYXRvcnkgcG93ZXIgaXMgcm9idXN0IGV2ZW4gYWZ0ZXIgYWRqdXN0aW5nIGZvciB0aGUgbnVtYmVyIG9mIHByZWRpY3RvcnMuIFRoZSBGLXN0YXRpc3RpYyBpcyB2ZXJ5IGhpZ2gsIHdpdGggYSBjb3JyZXNwb25kaW5nIHAtdmFsdWUgPDIuMmUtMTYsIGluZGljYXRpbmcgdGhhdCB0aGUgbW9kZWwgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBvdmVyYWxsLg0KDQojIyMjIyA1LjEuNCBUaGVvcmV0aWNhbCBJbnNpZ2h0cw0KRXZhbHVhdGluZyBhIG1vZGVsIHVzaW5nIGJvdGggUi1zcXVhcmVkIGFuZCBSTVNFIGlzIGNydWNpYWwgZm9yIHVuZGVyc3RhbmRpbmcgaXRzIG92ZXJhbGwgcGVyZm9ybWFuY2UuIFRoZSBSLXNxdWFyZWQgdmFsdWUgcHJvdmlkZXMgaW5zaWdodCBpbnRvIGhvdyB3ZWxsIHRoZSBtb2RlbCBleHBsYWlucyB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEsIHdoaWNoIGlzIGltcG9ydGFudCBmb3IgYXNzZXNzaW5nIHRoZSBtb2RlbCdzIGV4cGxhbmF0b3J5IHBvd2VyLiBPbiB0aGUgb3RoZXIgaGFuZCwgUk1TRSBwcm92aWRlcyBhIG1lYXN1cmUgb2YgdGhlIG1vZGVsJ3MgcHJlZGljdGl2ZSBhY2N1cmFjeSwgd2hpY2ggaXMgZXNzZW50aWFsIGZvciBtYWtpbmcgcmVsaWFibGUgZm9yZWNhc3RzLiBCeSBjb25zaWRlcmluZyBib3RoIG1ldHJpY3MsIHdlIGNhbiBlbnN1cmUgYSBiYWxhbmNlZCBldmFsdWF0aW9uIG9mIHRoZSBtb2RlbCwgdGFraW5nIGludG8gYWNjb3VudCBib3RoIGl0cyBhYmlsaXR5IHRvIGZpdCB0aGUgZGF0YSBhbmQgaXRzIHByZWNpc2lvbiBpbiBwcmVkaWN0aW9ucy4NCg0KSW4gc3VtbWFyeSwgdGhpcyBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwgZWZmZWN0aXZlbHkgY2FwdHVyZXMgdGhlIG5vbi1saW5lYXIgZWZmZWN0cyBvZiB0ZW1wZXJhdHVyZSBhbmQgaHVtaWRpdHkgb24gYmlrZSByZW50YWxzLCB3aXRoIHNpZ25pZmljYW50IGNvZWZmaWNpZW50cyBmb3IgYm90aCBwcmVkaWN0b3JzLiBUaGUgbW9kZWwgZXhwbGFpbnMgYSBzdWJzdGFudGlhbCBwb3J0aW9uIG9mIHRoZSB2YXJpYW5jZSBpbiBiaWtlIHJlbnRhbHMsIG1ha2luZyBpdCBhIHVzZWZ1bCB0b29sIGZvciB1bmRlcnN0YW5kaW5nIGFuZCBwcmVkaWN0aW5nIGJpa2UgcmVudGFsIHBhdHRlcm5zIGJhc2VkIG9uIHdlYXRoZXIgY29uZGl0aW9ucy4NCg0KDQoNCiMjIyMgNS4yIE1ha2UgUHJlZGljdGlvbnMgb24gdGhlIFRlc3QgRGF0YXNldA0KDQpgYGB7cn0NCiMgTWFrZSBwcmVkaWN0aW9ucyBvbiB0aGUgdGVzdCBkYXRhc2V0IHVzaW5nIHRoZSBsbV9iaWtseSBtb2RlbA0KeV9wcmVkIDwtIHByZWRpY3QobG1fYmlrbHksIG5ld2RhdGEgPSB0ZXN0X2RhdGEpDQoNCiMgQ29udmVydCBuZWdhdGl2ZSBwcmVkaWN0aW9ucyB0byB6ZXJvDQp5X3ByZWQgPC0gaWZlbHNlKHlfcHJlZCA8IDAsIDAsIHlfcHJlZCkNCg0KYGBgDQoNCiMjIyMjIDUuMi4xIENhbGN1bGF0ZSBSLXNxdWFyZWQgYW5kIFJNU0UNCg0KYGBge3J9DQoNCg0KDQojIENhbGN1bGF0aW5nIGVycm9ycw0KZXJyb3JfYmlrbHkgPC0gdHJhaW5fZGF0YSRSRU5URURfQklLRV9DT1VOVCAtIHlfcHJlZA0KDQojIENhbGN1bGF0aW5nIFNxdWFyZWQgRXJyb3JzDQpzcXVhcmVkX2Vycm9yX2Jpa2x5IDwtIGVycm9yX2Jpa2x5XjINCg0KIyBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2Ugb2YgdGhlIHNxdWFyZWQgZXJyb3JzDQptZWFuX3NxdWFyZWRfZXJyb3JfYmlrbHkgIDwtIG1lYW4oc3F1YXJlZF9lcnJvcl9iaWtseSkNCg0KIyBDYWxjdWxhdGUgUk1TRQ0Kcm1zZV9iaWtseSA8LSBzcXJ0KG1lYW5fc3F1YXJlZF9lcnJvcl9iaWtseSkNCg0KDQojIENhbGN1bGF0ZSBSLXNxdWFyZWQNCg0Kc3VtbWFyeV9tX2Jpa2x5IDwtIHN1bW1hcnkobG1fYmlrbHkpDQpyc3FfYmlrbHkgPC0gc3VtbWFyeV9tX2Jpa2x5JHIuc3F1YXJlZA0KDQoNCiMgRGlzcGxheSB0aGUgcmVzdWx0cw0KcmVzdWx0c19iaWtseSA8LSBkYXRhLmZyYW1lKA0KICBNb2RlbCA9ICJQb2x5bm9taWFsIFJlZ3Jlc3Npb24gTW9kZWwgKEJpa2x5KSIsDQogIFJfc3F1YXJlZCA9IHJzcV9iaWtseSwNCiAgUk1TRSA9IHJtc2VfYmlrbHkNCikNCg0KcHJpbnQocmVzdWx0c19iaWtseSkNCg0KYGBgDQpCYXNlZCBvbiB0aGUgcHJvdmlkZWQgZGF0YSwgdGhlIHBvbHlub21pYWwgcmVncmVzc2lvbiBtb2RlbCBuYW1lZCAiQmlreSIgaGFzIGFuIFItc3F1YXJlZCB2YWx1ZSBvZiAwLjQ0Njk2MDYzIGFuZCBhbiBSTVNFIG9mIDAuMjEzNjUyNi4gDQoNCiMjIyMjIDUuMi4yIE1vZGVsIEV2YWx1YXRpb24NCg0KVGhlICoqUi1zcXVhcmVkKiogdmFsdWUgb2YgMC40NDY5NjA2MyBpbmRpY2F0ZXMgdGhhdCBhcHByb3hpbWF0ZWx5IDQ0LjcwJSBvZiB0aGUgdmFyaWFuY2UgaW4gdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBpcyBleHBsYWluZWQgYnkgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyBpbiB0aGlzIG1vZGVsLiBUaGlzIHN1Z2dlc3RzIGEgbW9kZXJhdGUgbGV2ZWwgb2YgZXhwbGFuYXRvcnkgcG93ZXIsIG1lYW5pbmcgdGhhdCB0aGUgbW9kZWwgY2FwdHVyZXMgYSBzaWduaWZpY2FudCBwb3J0aW9uIG9mIHRoZSB2YXJpYWJpbGl0eSBpbiB0aGUgZGF0YSBidXQgbGVhdmVzIHNvbWUgdW5leHBsYWluZWQgdmFyaWFuY2UuIEEgaGlnaGVyIFItc3F1YXJlZCB2YWx1ZSBnZW5lcmFsbHkgaW5kaWNhdGVzIGEgYmV0dGVyIGZpdCBvZiB0aGUgbW9kZWwgdG8gdGhlIGRhdGEuDQoNClRoZSAqKlJNU0UgKFJvb3QgTWVhbiBTcXVhcmUgRXJyb3IpKiogdmFsdWUgb2YgMC4yMTM2NTI2IG1lYXN1cmVzIHRoZSBhdmVyYWdlIG1hZ25pdHVkZSBvZiB0aGUgZXJyb3JzIGJldHdlZW4gdGhlIHByZWRpY3RlZCBhbmQgb2JzZXJ2ZWQgdmFsdWVzLiBBIGxvd2VyIFJNU0UgaW5kaWNhdGVzIGJldHRlciBwcmVkaWN0aXZlIGFjY3VyYWN5LCBhcyBpdCBtZWFucyB0aGUgbW9kZWwncyBwcmVkaWN0aW9ucyBhcmUgY2xvc2VyIHRvIHRoZSBhY3R1YWwgdmFsdWVzLiBJbiB0aGlzIGNhc2UsIHRoZSBSTVNFIHZhbHVlIGlzIHJlbGF0aXZlbHkgbG93LCBzdWdnZXN0aW5nIHRoYXQgdGhlIG1vZGVsIGhhcyBnb29kIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuDQoNCiMjIyMjIDUuMi4zIFRoZW9yZXRpY2FsIEluc2lnaHRzDQoNCkV2YWx1YXRpbmcgYSBtb2RlbCB1c2luZyBib3RoIFItc3F1YXJlZCBhbmQgUk1TRSBpcyBjcnVjaWFsIGZvciB1bmRlcnN0YW5kaW5nIGl0cyBvdmVyYWxsIHBlcmZvcm1hbmNlLiBUaGUgUi1zcXVhcmVkIHZhbHVlIHByb3ZpZGVzIGluc2lnaHQgaW50byBob3cgd2VsbCB0aGUgbW9kZWwgZXhwbGFpbnMgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBkYXRhLCB3aGljaCBpcyBpbXBvcnRhbnQgZm9yIGFzc2Vzc2luZyB0aGUgbW9kZWwncyBleHBsYW5hdG9yeSBwb3dlci4gT24gdGhlIG90aGVyIGhhbmQsIFJNU0UgcHJvdmlkZXMgYSBtZWFzdXJlIG9mIHRoZSBtb2RlbCdzIHByZWRpY3RpdmUgYWNjdXJhY3ksIHdoaWNoIGlzIGVzc2VudGlhbCBmb3IgbWFraW5nIHJlbGlhYmxlIGZvcmVjYXN0cy4gQnkgY29uc2lkZXJpbmcgYm90aCBtZXRyaWNzLCB3ZSBjYW4gZW5zdXJlIGEgYmFsYW5jZWQgZXZhbHVhdGlvbiBvZiB0aGUgbW9kZWwsIHRha2luZyBpbnRvIGFjY291bnQgYm90aCBpdHMgYWJpbGl0eSB0byBmaXQgdGhlIGRhdGEgYW5kIGl0cyBwcmVjaXNpb24gaW4gcHJlZGljdGlvbnMuDQoNClRoZSBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwgIkJpa3kiIGRlbW9uc3RyYXRlcyBhIG1vZGVyYXRlIGxldmVsIG9mIGV4cGxhbmF0b3J5IHBvd2VyIGFuZCBnb29kIHByZWRpY3RpdmUgYWNjdXJhY3ksIG1ha2luZyBpdCBhIHVzZWZ1bCB0b29sIGZvciB1bmRlcnN0YW5kaW5nIGFuZCBwcmVkaWN0aW5nIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgYmFzZWQgb24gdGhlIGdpdmVuIHByZWRpY3RvcnMuDQoNCiMjIyA2LiBGaXQgdGhlIFBvbHlub21pYWwgUmVncmVzc2lvbiBNb2RlbCB3aXRoIEludGVyYWN0aW9uIFRlcm1zDQoNCmBgYHtyfQ0KIyBGaXQgYSBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwgd2l0aCBpbnRlcmFjdGlvbiB0ZXJtcw0KbG1fYmlrbHlfaW50ZXJhY3Rpb24gPC0gbG0oUkVOVEVEX0JJS0VfQ09VTlQgfiBwb2x5KFRFTVBFUkFUVVJFLCAyKSAqIEhVTUlESVRZICsgcG9seShURU1QRVJBVFVSRSwgMikgKiBXSU5EX1NQRUVELCBkYXRhID0gdHJhaW5fZGF0YSkNCg0KIyBQcmludCB0aGUgbW9kZWwgc3VtbWFyeQ0Kc3VtbWFyeShsbV9iaWtseV9pbnRlcmFjdGlvbikNCg0KYGBgDQoNClRoZSByZWdyZXNzaW9uIGFuYWx5c2lzIHByZXNlbnRlZCBhaW1zIHRvIHByZWRpY3QgdGhlIGBSRU5URURfQklLRV9DT1VOVGAgYmFzZWQgb24gcG9seW5vbWlhbCB0cmFuc2Zvcm1hdGlvbnMgb2YgYFRFTVBFUkFUVVJFYCwgYEhVTUlESVRZYCwgYW5kIHRoZWlyIGludGVyYWN0aW9uIHdpdGggYFdJTkRfU1BFRURgLiBUaGlzIGFwcHJvYWNoIGFsbG93cyBmb3IgY2FwdHVyaW5nIGNvbXBsZXggcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZXNlIHByZWRpY3RvcnMgYW5kIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUsIHdoaWNoIGNhbiBiZSBtb3JlIHJlZmxlY3RpdmUgb2YgcmVhbC13b3JsZCBzY2VuYXJpb3Mgd2hlcmUgd2VhdGhlciBjb25kaXRpb25zIGludGVyYWN0IGluIG5vbi1saW5lYXIgd2F5cyB0byBhZmZlY3QgYmlrZSByZW50YWxzLg0KDQojIyMjIDYuMSBNb2RlbCBTdW1tYXJ5DQpUaGUgcmVncmVzc2lvbiBtb2RlbCBpbmNsdWRlcyBwb2x5bm9taWFsIHRlcm1zIGZvciBgVEVNUEVSQVRVUkVgIHVwIHRvIHRoZSBzZWNvbmQgZGVncmVlLCBgSFVNSURJVFlgLCBhbmQgdGhlIGludGVyYWN0aW9uIGJldHdlZW4gYFRFTVBFUkFUVVJFYCBhbmQgYFdJTkRfU1BFRURgLiBUaGUgY29lZmZpY2llbnRzIHRhYmxlIHNob3dzIHRoZSBlc3RpbWF0ZWQgZWZmZWN0cyBvZiBlYWNoIHByZWRpY3RvciwgYWxvbmcgd2l0aCB0aGVpciBzdGFuZGFyZCBlcnJvcnMsIHQtdmFsdWVzLCBhbmQgcC12YWx1ZXMuIA0KDQojIyMjIyA2LjEuMSBDb2VmZmljaWVudHMgYW5kIFNpZ25pZmljYW5jZQ0KVGhlIGZpcnN0LWRlZ3JlZSBwb2x5bm9taWFsIHRlcm0gZm9yIGBURU1QRVJBVFVSRWAgaGFzIGEgc2lnbmlmaWNhbnQgbmVnYXRpdmUgY29lZmZpY2llbnQgLCBzdWdnZXN0aW5nIHRoYXQgYXMgdGVtcGVyYXR1cmUgaW5jcmVhc2VzLCB0aGUgbnVtYmVyIG9mIHJlbnRlZCBiaWtlcyBkZWNyZWFzZXMgc2xpZ2h0bHkuIFRoZSBzZWNvbmQtZGVncmVlIHRlcm0gZm9yIGBURU1QRVJBVFVSRWAgaXMgbm90IHNpZ25pZmljYW50LCBpbmRpY2F0aW5nIHRoYXQgaGlnaGVyLW9yZGVyIHRlbXBlcmF0dXJlIGVmZmVjdHMgYXJlIG5lZ2xpZ2libGUuIGBIVU1JRElUWWAgaGFzIGEgc2lnbmlmaWNhbnQgbmVnYXRpdmUgY29lZmZpY2llbnQgLCBzdWdnZXN0aW5nIHRoYXQgaGlnaGVyIGh1bWlkaXR5IGxldmVscyByZWR1Y2UgYmlrZSByZW50YWxzLiBUaGUgaW50ZXJhY3Rpb24gdGVybSBiZXR3ZWVuIHRoZSBmaXJzdC1kZWdyZWUgcG9seW5vbWlhbCBvZiBgVEVNUEVSQVRVUkVgIGFuZCBgSFVNSURJVFlgIGlzIHNpZ25pZmljYW50ICwgaW5kaWNhdGluZyB0aGF0IHRoZSBjb21iaW5lZCBlZmZlY3Qgb2YgdGhlc2UgdmFyaWFibGVzIGhhcyBhIG1lYW5pbmdmdWwgaW1wYWN0IG9uIGJpa2UgcmVudGFscy4gU2ltaWxhcmx5LCBgV0lORF9TUEVFRGAgaGFzIGEgc2lnbmlmaWNhbnQgbmVnYXRpdmUgY29lZmZpY2llbnQgLCBhbmQgaXRzIGludGVyYWN0aW9uIHdpdGggdGhlIGZpcnN0LWRlZ3JlZSBwb2x5bm9taWFsIG9mIGBURU1QRVJBVFVSRWAgaXMgc2lnbmlmaWNhbnQgLCBzdWdnZXN0aW5nIHRoYXQgd2luZCBzcGVlZCBhbHNvIHBsYXlzIGEgY3J1Y2lhbCByb2xlIGluIGJpa2UgcmVudGFscy4NCg0KIyMjIyMgNi4xLjIgTW9kZWwgRml0DQpUaGUgbW9kZWwncyBtdWx0aXBsZSBSLXNxdWFyZWQgdmFsdWUgaXMgMC40MzI5LCBpbmRpY2F0aW5nIHRoYXQgYXBwcm94aW1hdGVseSA0My4zNiUgb2YgdGhlIHZhcmlhbmNlIGluIGBSRU5URURfQklLRV9DT1VOVGAgaXMgZXhwbGFpbmVkIGJ5IHRoZSBtb2RlbC4gVGhlIGFkanVzdGVkIFItc3F1YXJlZCB2YWx1ZSBpcyB2ZXJ5IGNsb3NlLCBzdWdnZXN0aW5nIHRoYXQgdGhlIG1vZGVsJ3MgZXhwbGFuYXRvcnkgcG93ZXIgaXMgcm9idXN0IGV2ZW4gYWZ0ZXIgYWRqdXN0aW5nIGZvciB0aGUgbnVtYmVyIG9mIHByZWRpY3RvcnMuIFRoZSBGLXN0YXRpc3RpYyBpcyB2ZXJ5IGhpZ2gsIHdpdGggYSBjb3JyZXNwb25kaW5nIHAtdmFsdWUgPDJlLTE2LCBpbmRpY2F0aW5nIHRoYXQgdGhlIG1vZGVsIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgb3ZlcmFsbC4NCg0KIyMjIyMgNi4xLjMgVGhlb3JldGljYWwgSW5zaWdodHMNCkV2YWx1YXRpbmcgYSBtb2RlbCB1c2luZyBib3RoIFItc3F1YXJlZCBhbmQgUk1TRSBpcyBjcnVjaWFsIGZvciB1bmRlcnN0YW5kaW5nIGl0cyBvdmVyYWxsIHBlcmZvcm1hbmNlLiBUaGUgUi1zcXVhcmVkIHZhbHVlIHByb3ZpZGVzIGluc2lnaHQgaW50byBob3cgd2VsbCB0aGUgbW9kZWwgZXhwbGFpbnMgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBkYXRhLCB3aGljaCBpcyBpbXBvcnRhbnQgZm9yIGFzc2Vzc2luZyB0aGUgbW9kZWwncyBleHBsYW5hdG9yeSBwb3dlci4gT24gdGhlIG90aGVyIGhhbmQsIFJNU0UgcHJvdmlkZXMgYSBtZWFzdXJlIG9mIHRoZSBtb2RlbCdzIHByZWRpY3RpdmUgYWNjdXJhY3ksIHdoaWNoIGlzIGVzc2VudGlhbCBmb3IgbWFraW5nIHJlbGlhYmxlIGZvcmVjYXN0cy4gQnkgY29uc2lkZXJpbmcgYm90aCBtZXRyaWNzLCB3ZSBjYW4gZW5zdXJlIGEgYmFsYW5jZWQgZXZhbHVhdGlvbiBvZiB0aGUgbW9kZWwsIHRha2luZyBpbnRvIGFjY291bnQgYm90aCBpdHMgYWJpbGl0eSB0byBmaXQgdGhlIGRhdGEgYW5kIGl0cyBwcmVjaXNpb24gaW4gcHJlZGljdGlvbnMuDQoNCkluIHN1bW1hcnksIHRoaXMgcG9seW5vbWlhbCByZWdyZXNzaW9uIG1vZGVsIGVmZmVjdGl2ZWx5IGNhcHR1cmVzIHRoZSBjb21wbGV4IGludGVyYWN0aW9ucyBiZXR3ZWVuIHRlbXBlcmF0dXJlLCBodW1pZGl0eSwgYW5kIHdpbmQgc3BlZWQgb24gYmlrZSByZW50YWxzLCB3aXRoIHNpZ25pZmljYW50IGNvZWZmaWNpZW50cyBmb3IgYWxsIHByZWRpY3RvcnMuIFRoZSBtb2RlbCBleHBsYWlucyBhIHN1YnN0YW50aWFsIHBvcnRpb24gb2YgdGhlIHZhcmlhbmNlIGluIGJpa2UgcmVudGFscywgbWFraW5nIGl0IGEgdXNlZnVsIHRvb2wgZm9yIHVuZGVyc3RhbmRpbmcgYW5kIHByZWRpY3RpbmcgYmlrZSByZW50YWwgcGF0dGVybnMgYmFzZWQgb24gd2VhdGhlciBjb25kaXRpb25zLg0KDQojIyMjIDYuMiBNYWtlIFByZWRpY3Rpb25zIG9uIHRoZSBUZXN0IERhdGFzZXQNCg0KYGBge3J9DQojIE1ha2UgcHJlZGljdGlvbnMgb24gdGhlIHRlc3QgZGF0YXNldCB1c2luZyB0aGUgbG1fYmlrbHkgbW9kZWwNCnlfcHJlZF9pbnRlcmFjdGlvbiA8LSBwcmVkaWN0KGxtX2Jpa2x5X2ludGVyYWN0aW9uLCBuZXdkYXRhID0gdGVzdF9kYXRhKQ0KDQojIENvbnZlcnQgbmVnYXRpdmUgcHJlZGljdGlvbnMgdG8gemVybw0KeV9wcmVkX2ludGVyYWN0aW9uIDwtIGlmZWxzZSh5X3ByZWQgPCAwLCAwLCB5X3ByZWRfaW50ZXJhY3Rpb24pDQoNCmBgYA0KDQoNCiMjIyMjIDYuMi4xIENhbGN1bGF0ZSBSLXNxdWFyZWQgYW5kIFJNU0UNCg0KYGBge3J9DQojIENhbGN1bGF0aW5nIGVycm9ycw0KZXJyb3JfYmlrbHlfaW50ZXJhY3Rpb24gPC0gdHJhaW5fZGF0YSRSRU5URURfQklLRV9DT1VOVCAtIHlfcHJlZF9pbnRlcmFjdGlvbg0KDQojIENhbGN1bGF0aW5nIFNxdWFyZWQgRXJyb3JzDQpzcXVhcmVkX2Vycm9yX2Jpa2x5X2ludGVyYWN0aW9uIDwtIGVycm9yX2Jpa2x5XjINCg0KIyBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2Ugb2YgdGhlIHNxdWFyZWQgZXJyb3JzDQptZWFuX3NxdWFyZWRfZXJyb3JfYmlrbHlfaW50ZXJhY3Rpb24gIDwtIG1lYW4oc3F1YXJlZF9lcnJvcl9iaWtseV9pbnRlcmFjdGlvbikNCg0KIyBDYWxjdWxhdGUgUk1TRQ0Kcm1zZV9iaWtseV9pbnRlcmFjdGlvbiA8LSBzcXJ0KG1lYW5fc3F1YXJlZF9lcnJvcl9iaWtseV9pbnRlcmFjdGlvbikNCg0KDQojIENhbGN1bGF0ZSBSLXNxdWFyZWQNCg0Kc3VtbWFyeV9tX2Jpa2x5X2ludGVyYWN0aW9uIDwtIHN1bW1hcnkobG1fYmlrbHlfaW50ZXJhY3Rpb24pDQpyc3FfYmlrbHlfaW50ZXJhY3Rpb24gPC0gc3VtbWFyeV9tX2Jpa2x5X2ludGVyYWN0aW9uJHIuc3F1YXJlZA0KDQoNCiMgRGlzcGxheSB0aGUgcmVzdWx0cw0KcmVzdWx0c19iaWtseV9pbnRlcmFjdGlvbiA8LSBkYXRhLmZyYW1lKA0KICBNb2RlbCA9ICJQb2x5bm9taWFsIFJlZ3Jlc3Npb24gTW9kZWwgKEJpa2x5X2ludGVyYWN0aW9uKSIsDQogIFJfc3F1YXJlZCA9IHJzcV9iaWtseV9pbnRlcmFjdGlvbiwNCiAgUk1TRSA9IHJtc2VfYmlrbHlfaW50ZXJhY3Rpb24NCikNCg0KcHJpbnQocmVzdWx0c19iaWtseV9pbnRlcmFjdGlvbikNCmBgYA0KQmFzZWQgb24gdGhlIHByb3ZpZGVkIGRhdGEsIHRoZSBwb2x5bm9taWFsIHJlZ3Jlc3Npb24gbW9kZWwgaGFzIGFuIFItc3F1YXJlZCB2YWx1ZSBvZiAwLjQzMzU5MjIgYW5kIGFuIFJNU0Ugb2YgMC4yMTY4NTI2Lg0KDQojIyMjIyA2LjIuMiBNb2RlbCBFdmFsdWF0aW9uDQoNClRoZSAqKlItc3F1YXJlZCoqIHZhbHVlIG9mIDAuNDMzNTkyMiBpbmRpY2F0ZXMgdGhhdCBhcHByb3hpbWF0ZWx5IDQzLjM2JSBvZiB0aGUgdmFyaWFuY2UgaW4gdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBpcyBleHBsYWluZWQgYnkgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyBpbiB0aGlzIG1vZGVsLiBUaGlzIHN1Z2dlc3RzIGEgbW9kZXJhdGUgbGV2ZWwgb2YgZXhwbGFuYXRvcnkgcG93ZXIsIG1lYW5pbmcgdGhhdCB0aGUgbW9kZWwgY2FwdHVyZXMgYSBzaWduaWZpY2FudCBwb3J0aW9uIG9mIHRoZSB2YXJpYWJpbGl0eSBpbiB0aGUgZGF0YSBidXQgbGVhdmVzIHNvbWUgdW5leHBsYWluZWQgdmFyaWFuY2UuIEEgaGlnaGVyIFItc3F1YXJlZCB2YWx1ZSBnZW5lcmFsbHkgaW5kaWNhdGVzIGEgYmV0dGVyIGZpdCBvZiB0aGUgbW9kZWwgdG8gdGhlIGRhdGEuDQoNClRoZSAqKlJNU0UgKFJvb3QgTWVhbiBTcXVhcmUgRXJyb3IpKiogdmFsdWUgb2YgMC4yMTY4NTI2IG1lYXN1cmVzIHRoZSBhdmVyYWdlIG1hZ25pdHVkZSBvZiB0aGUgZXJyb3JzIGJldHdlZW4gdGhlIHByZWRpY3RlZCBhbmQgb2JzZXJ2ZWQgdmFsdWVzLiBBIGxvd2VyIFJNU0UgaW5kaWNhdGVzIGJldHRlciBwcmVkaWN0aXZlIGFjY3VyYWN5LCBhcyBpdCBtZWFucyB0aGUgbW9kZWwncyBwcmVkaWN0aW9ucyBhcmUgY2xvc2VyIHRvIHRoZSBhY3R1YWwgdmFsdWVzLiBJbiB0aGlzIGNhc2UsIHRoZSBSTVNFIHZhbHVlIGlzIHJlbGF0aXZlbHkgbG93LCBzdWdnZXN0aW5nIHRoYXQgdGhlIG1vZGVsIGhhcyBnb29kIHByZWRpY3RpdmUgcGVyZm9ybWFuY2UuDQoNCiMjIyMjIDYuMi4zIFRoZW9yZXRpY2FsIEluc2lnaHRzDQoNCkV2YWx1YXRpbmcgYSBtb2RlbCB1c2luZyBib3RoIFItc3F1YXJlZCBhbmQgUk1TRSBpcyBjcnVjaWFsIGZvciB1bmRlcnN0YW5kaW5nIGl0cyBvdmVyYWxsIHBlcmZvcm1hbmNlLiBUaGUgUi1zcXVhcmVkIHZhbHVlIHByb3ZpZGVzIGluc2lnaHQgaW50byBob3cgd2VsbCB0aGUgbW9kZWwgZXhwbGFpbnMgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBkYXRhLCB3aGljaCBpcyBpbXBvcnRhbnQgZm9yIGFzc2Vzc2luZyB0aGUgbW9kZWwncyBleHBsYW5hdG9yeSBwb3dlci4gT24gdGhlIG90aGVyIGhhbmQsIFJNU0UgcHJvdmlkZXMgYSBtZWFzdXJlIG9mIHRoZSBtb2RlbCdzIHByZWRpY3RpdmUgYWNjdXJhY3ksIHdoaWNoIGlzIGVzc2VudGlhbCBmb3IgbWFraW5nIHJlbGlhYmxlIGZvcmVjYXN0cy4gQnkgY29uc2lkZXJpbmcgYm90aCBtZXRyaWNzLCB3ZSBjYW4gZW5zdXJlIGEgYmFsYW5jZWQgZXZhbHVhdGlvbiBvZiB0aGUgbW9kZWwsIHRha2luZyBpbnRvIGFjY291bnQgYm90aCBpdHMgYWJpbGl0eSB0byBmaXQgdGhlIGRhdGEgYW5kIGl0cyBwcmVjaXNpb24gaW4gcHJlZGljdGlvbnMuDQoNCg0KIyMjIDcuIEFkZCByZWd1bGFyaXphdGlvbg0KDQojIyMjIDcuMSBDcmVhdGUgYSByZWNpcGUNCg0KYGBge3J9DQpiaWtlX3JlY2lwZSA8LSByZWNpcGUoUkVOVEVEX0JJS0VfQ09VTlQgfiAuLCBkYXRhID0gdHJhaW5fZGF0YSkgJT4lDQogIHN0ZXBfenYoYWxsX3ByZWRpY3RvcnMoKSkgJT4lDQogIHN0ZXBfbm9ybWFsaXplKGFsbF9wcmVkaWN0b3JzKCkpICU+JQ0KICBzdGVwX3BvbHkoYWxsX3ByZWRpY3RvcnMoKSwgZGVncmVlID0gMikgJT4lDQogIHN0ZXBfaW50ZXJhY3QodGVybXMgPSB+IGFsbF9wcmVkaWN0b3JzKCk6YWxsX3ByZWRpY3RvcnMoKSkNCg0KDQpgYGANCg0KIyMjIyA3LjIgU3BlY2lmeSB0aGUgd29yayBmbG93IGFuZCBmaXQgdGhlIG1vZGVsDQoNCmBgYHtyfQ0KYmlrZV93b3JrZmxvdyA8LSB3b3JrZmxvdygpICU+JQ0KICBhZGRfcmVjaXBlKGJpa2VfcmVjaXBlKSAlPiUNCiAgYWRkX21vZGVsKGdsbW5ldF9zcGVjKQ0KDQpgYGANCg0KIyMjIyA3LjMgRWxhc3RpYyBOZXQgUmVndWxhcml6YXRpb24gJiAoTDEgYW5kIEwyKSANCg0KYGBge3J9DQojIE1vZGVsIDE6IEFkZGluZyByZWd1bGFyaXphdGlvbiAoTDIgUmlkZ2UpDQpyaWRnZV9zcGUgPC0gbGluZWFyX3JlZyhwZW5hbHR5ID0gMC4xLCBtaXh0dXJlID0gMCkgJT4lDQogIHNldF9lbmdpbmUoImdsbW5ldCIpDQoNCnRyYWluX2YxIDwtIHJpZGdlX3NwZSAlPiUgDQogIGZpdChSRU5URURfQklLRV9DT1VOVCB+IFRFTVBFUkFUVVJFICsgSFVNSURJVFkgKyBTT0xBUl9SQURJQVRJT04gKyBSQUlORkFMTCArIFNOT1dGQUxMLCBkYXRhID0gdHJhaW5fZGF0YSkNCg0KIyBNb2RlbCAyOiBBZGRpbmcgcmVndWxhcml6YXRpb24gKEwxIExhc3NvKQ0KcmlkZ2Vfc3BlMSA8LSBsaW5lYXJfcmVnKHBlbmFsdHkgPSAwLjEsIG1peHR1cmUgPSAxKSAlPiUNCiAgc2V0X2VuZ2luZSgiZ2xtbmV0IikNCg0KdHJhaW5fZjIgPC0gcmlkZ2Vfc3BlMSAlPiUgDQogIGZpdChSRU5URURfQklLRV9DT1VOVCB+IFRFTVBFUkFUVVJFICsgSFVNSURJVFkrIFNPTEFSX1JBRElBVElPTiArIFJBSU5GQUxMICsgU05PV0ZBTEwsIGRhdGEgPSB0cmFpbl9kYXRhKQ0KDQojIE1vZGVsIDI6IEFkZGluZyByZWd1bGFyaXphdGlvbiAoTDEgTGFzc28gYW5kIEwyIFJpZGdlKQ0KcmlkZ2Vfc3BlMiA8LSBsaW5lYXJfcmVnKHBlbmFsdHkgPSAwLjEsIG1peHR1cmUgPSAwLjUpICU+JQ0KICBzZXRfZW5naW5lKCJnbG1uZXQiKQ0KDQp0cmFpbl9mMyA8LSByaWRnZV9zcGUyICU+JSANCiAgZml0KFJFTlRFRF9CSUtFX0NPVU5UIH4gVEVNUEVSQVRVUkUgKyBIVU1JRElUWSArIFNPTEFSX1JBRElBVElPTiArIFJBSU5GQUxMICsgU05PV0ZBTEwsIGRhdGEgPSB0cmFpbl9kYXRhKQ0KDQojIEV4dHJhY3QgcHJlZGljdGlvbnMNCnByZWRpYzEgPC0gcHJlZGljdCh0cmFpbl9mMSwgdHJhaW5fZGF0YSkkLnByZWQNCnByZWRpYzIgPC0gcHJlZGljdCh0cmFpbl9mMiwgdHJhaW5fZGF0YSkkLnByZWQNCnByZWRpYzMgPC0gcHJlZGljdCh0cmFpbl9mMywgdHJhaW5fZGF0YSkkLnByZWQNCg0KIyBDYWxjdWxhdGUgUk1TRSBtYW51YWxseQ0Kcm1zZV9tYW51YWwgPC0gZnVuY3Rpb24oYWN0dWFsLCBwcmVkaWN0ZWQpIHsNCiAgc3FydChtZWFuKChhY3R1YWwgLSBwcmVkaWN0ZWQpXjIpKQ0KfQ0KDQojIENhbGN1bGF0ZSBSTVNFIGZvciBlYWNoIG1vZGVsDQpybXNlXzEgPC0gcm1zZV9tYW51YWwodHJhaW5fZGF0YSRSRU5URURfQklLRV9DT1VOVCwgcHJlZGljMSkNCnJtc2VfMiA8LSBybXNlX21hbnVhbCh0cmFpbl9kYXRhJFJFTlRFRF9CSUtFX0NPVU5ULCBwcmVkaWMyKQ0Kcm1zZV8zIDwtIHJtc2VfbWFudWFsKHRyYWluX2RhdGEkUkVOVEVEX0JJS0VfQ09VTlQsIHByZWRpYzMpDQoNCiMgQ29tYmluZSByZXN1bHRzIGludG8gYSB0YWJsZQ0KcmVzdWx0X3JlZ3VsYXJpemF0aW9uIDwtIHRpYmJsZSgNCiAgbW9kZWwgPSBjKCJNb2RlbCAxOiBMMiBSaWRnZSIsICJNb2RlbCAyOiBMMSBMYXNzbyIsICJNb2RlbCAzOiBMMSBMYXNzbyBhbmQgTDIgUmlkZ2UiKSwNCiAgUk1TRSA9IGMocm1zZV8xLCBybXNlXzIsIHJtc2VfMykNCikNCg0KIyBEaXNwbGF5IHRoZSByZXN1bHRzDQpwcmludChyZXN1bHRfcmVndWxhcml6YXRpb24pDQoNCmBgYA0KDQoxLiAqKk1vZGVsIDE6IEwyIFJpZGdlKiogLSBUaGlzIG1vZGVsLCB3aGljaCBhcHBsaWVzIEwyIHJlZ3VsYXJpemF0aW9uLCBoYXMgYW4gUk1TRSBvZiAwLjE0NjQ4NDQuIEwyIHJlZ3VsYXJpemF0aW9uIGhlbHBzIHRvIHByZXZlbnQgb3ZlcmZpdHRpbmcgYnkgcGVuYWxpemluZyBsYXJnZSBjb2VmZmljaWVudHMsIGxlYWRpbmcgdG8gYSBtb3JlIGdlbmVyYWxpemVkIG1vZGVsLiBUaGUgcmVsYXRpdmVseSBsb3cgUk1TRSBpbmRpY2F0ZXMgdGhhdCB0aGlzIG1vZGVsIHBlcmZvcm1zIHdlbGwgaW4gcHJlZGljdGluZyB0aGUgdGFyZ2V0IHZhcmlhYmxlLCBiYWxhbmNpbmcgYmlhcyBhbmQgdmFyaWFuY2UgZWZmZWN0aXZlbHkuDQoNCjIuICoqTW9kZWwgMjogTDEgTGFzc28qKiAtIFRoZSBMMSByZWd1bGFyaXphdGlvbiBtb2RlbCwga25vd24gYXMgTGFzc28sIGhhcyBhIGhpZ2hlciBSTVNFIG9mIDAuMTgwNTIyOS4gTGFzc28gcmVndWxhcml6YXRpb24gbm90IG9ubHkgaGVscHMgaW4gcHJldmVudGluZyBvdmVyZml0dGluZyBidXQgYWxzbyBwZXJmb3JtcyBmZWF0dXJlIHNlbGVjdGlvbiBieSBzaHJpbmtpbmcgc29tZSBjb2VmZmljaWVudHMgdG8gemVyby4gVGhlIGhpZ2hlciBSTVNFIHN1Z2dlc3RzIHRoYXQgd2hpbGUgTGFzc28gaXMgdXNlZnVsIGZvciBpZGVudGlmeWluZyBpbXBvcnRhbnQgZmVhdHVyZXMsIGl0IG1heSBub3QgYWx3YXlzIHByb3ZpZGUgdGhlIGJlc3QgcHJlZGljdGl2ZSBhY2N1cmFjeSBjb21wYXJlZCB0byBSaWRnZSByZWdyZXNzaW9uIGluIHRoaXMgY29udGV4dC4NCg0KMy4gKipNb2RlbCAzOiBDb21iaW5hdGlvbiBvZiBMMSBMYXNzbyBhbmQgTDIgUmlkZ2UqKiAtIFRoaXMgbW9kZWwgY29tYmluZXMgYm90aCBMMSBhbmQgTDIgcmVndWxhcml6YXRpb24gdGVjaG5pcXVlcywgcmVzdWx0aW5nIGluIGFuIFJNU0Ugb2YgMC4xNjIzMTgzLiBUaGlzIGFwcHJvYWNoLCBvZnRlbiByZWZlcnJlZCB0byBhcyBFbGFzdGljIE5ldCwgYWltcyB0byBsZXZlcmFnZSB0aGUgc3RyZW5ndGhzIG9mIGJvdGggcmVndWxhcml6YXRpb24gbWV0aG9kcy4gVGhlIFJNU0UgdmFsdWUgaW5kaWNhdGVzIHRoYXQgdGhlIGNvbWJpbmF0aW9uIG1vZGVsIHBlcmZvcm1zIGJldHRlciB0aGFuIExhc3NvIGFsb25lIGJ1dCBub3QgYXMgd2VsbCBhcyBSaWRnZSByZWdyZXNzaW9uLiBUaGlzIHN1Z2dlc3RzIHRoYXQgd2hpbGUgY29tYmluaW5nIGJvdGggcmVndWxhcml6YXRpb24gdGVjaG5pcXVlcyBjYW4gYmUgYmVuZWZpY2lhbCwgdGhlIHNwZWNpZmljIGNvbnRleHQgYW5kIGRhdGEgY2hhcmFjdGVyaXN0aWNzIHBsYXkgYSBjcnVjaWFsIHJvbGUgaW4gZGV0ZXJtaW5pbmcgdGhlIG9wdGltYWwgcmVndWxhcml6YXRpb24gc3RyYXRlZ3kuDQoNCk92ZXJhbGwsIHRoZSBpbnNpZ2h0cyBoaWdobGlnaHQgdGhlIGltcG9ydGFuY2Ugb2Ygc2VsZWN0aW5nIHRoZSBhcHByb3ByaWF0ZSByZWd1bGFyaXphdGlvbiB0ZWNobmlxdWUgYmFzZWQgb24gdGhlIHNwZWNpZmljIGNoYXJhY3RlcmlzdGljcyBvZiB0aGUgZGF0YSBhbmQgdGhlIG1vZGVsaW5nIGdvYWxzLiBSaWRnZSByZWdyZXNzaW9uIChMMikgYXBwZWFycyB0byBiZSB0aGUgbW9zdCBlZmZlY3RpdmUgaW4gdGhpcyBzY2VuYXJpbywgcHJvdmlkaW5nIGEgZ29vZCBiYWxhbmNlIGJldHdlZW4gbW9kZWwgY29tcGxleGl0eSBhbmQgcHJlZGljdGl2ZSBhY2N1cmFjeS4gTGFzc28gKEwxKSBpcyB1c2VmdWwgZm9yIGZlYXR1cmUgc2VsZWN0aW9uIGJ1dCBtYXkgbm90IGFsd2F5cyB5aWVsZCB0aGUgbG93ZXN0IFJNU0UuIFRoZSBjb21iaW5hdGlvbiBvZiBMMSBhbmQgTDIgcmVndWxhcml6YXRpb24gb2ZmZXJzIGEgbWlkZGxlIGdyb3VuZCwgcG90ZW50aWFsbHkgaW1wcm92aW5nIG1vZGVsIHBlcmZvcm1hbmNlIGluIGNlcnRhaW4gY29udGV4dHMuDQoNCg0KIyMjIDguIEV4cGVyaW1lbnQgdG8gc2VhcmNoIGZvciBpbXByb3ZlZCBtb2RlbHMNCg0KDQpgYGB7cn0NCiMgRGVmaW5lIHRoZSBtb2RlbCBzcGVjaWZpY2F0aW9ucw0KbG1fc3BlYyA8LSBsaW5lYXJfcmVnKCkgJT4lDQogIHNldF9lbmdpbmUoImxtIikNCg0KIyBNb2RlbCAxOiBBZGRpbmcgbW9yZSBmZWF0dXJlcw0KdHJhaW5fZml0NSA8LSBsbV9zcGVjICU+JSANCiAgZml0KFJFTlRFRF9CSUtFX0NPVU5UIH4gVEVNUEVSQVRVUkUgKyBIVU1JRElUWSArIFdJTkRfU1BFRUQgKyBWSVNJQklMSVRZICsgREVXX1BPSU5UX1RFTVBFUkFUVVJFICsgU09MQVJfUkFESUFUSU9OICsgUkFJTkZBTEwgKyBTTk9XRkFMTCwgZGF0YSA9IHRyYWluX2RhdGEpDQoNCiMgTW9kZWwgMjogQWRkaW5nIHJlZ3VsYXJpemF0aW9uIChMMiBSaWRnZSkNCnJpZGdlX3NwZWMgPC0gbGluZWFyX3JlZyhwZW5hbHR5ID0gMC4xLCBtaXh0dXJlID0gMCkgJT4lDQogIHNldF9lbmdpbmUoImdsbW5ldCIpDQoNCnRyYWluX2ZpdDYgPC0gcmlkZ2Vfc3BlYyAlPiUgDQogIGZpdChSRU5URURfQklLRV9DT1VOVCB+IFRFTVBFUkFUVVJFICsgSFVNSURJVFkgKyBXSU5EX1NQRUVEICsgVklTSUJJTElUWSArIERFV19QT0lOVF9URU1QRVJBVFVSRSArIFNPTEFSX1JBRElBVElPTiArIFJBSU5GQUxMICsgU05PV0ZBTEwsIGRhdGEgPSB0cmFpbl9kYXRhKQ0KDQojIE1vZGVsIDM6IEFkZGluZyBwb2x5bm9taWFsIGNvbXBvbmVudHMNCnBvbHlfc3BlYyA8LSBsaW5lYXJfcmVnKCkgJT4lDQogIHNldF9lbmdpbmUoImxtIikNCg0KdHJhaW5fZml0NyA8LSBwb2x5X3NwZWMgJT4lIA0KICBmaXQoUkVOVEVEX0JJS0VfQ09VTlQgfiBwb2x5KFRFTVBFUkFUVVJFLCAyKSArIHBvbHkoSFVNSURJVFksIDIpICsgcG9seShXSU5EX1NQRUVELCAyKSArIHBvbHkoVklTSUJJTElUWSwgMikgKyBwb2x5KERFV19QT0lOVF9URU1QRVJBVFVSRSwgMikgKyBwb2x5KFNPTEFSX1JBRElBVElPTiwgMikgKyBwb2x5KFJBSU5GQUxMLCAyKSArIHBvbHkoU05PV0ZBTEwsIDIpLCBkYXRhID0gdHJhaW5fZGF0YSkNCg0KIyBNb2RlbCA0OiBBZGRpbmcgaW50ZXJhY3Rpb24gdGVybXMNCmludGVyYWN0aW9uX3NwZWMgPC0gbGluZWFyX3JlZygpICU+JQ0KICBzZXRfZW5naW5lKCJsbSIpDQoNCnRyYWluX2ZpdDggPC0gaW50ZXJhY3Rpb25fc3BlYyAlPiUgDQogIGZpdChSRU5URURfQklLRV9DT1VOVCB+IFRFTVBFUkFUVVJFICogSFVNSURJVFkgKyBXSU5EX1NQRUVEICogVklTSUJJTElUWSArIERFV19QT0lOVF9URU1QRVJBVFVSRSAqIFNPTEFSX1JBRElBVElPTiArIFJBSU5GQUxMICogU05PV0ZBTEwsIGRhdGEgPSB0cmFpbl9kYXRhKQ0KDQojIE1vZGVsIDU6IA0KdHJlZV9zcGVjIDwtIGxpbmVhcl9yZWcoKSAlPiUNCiAgc2V0X2VuZ2luZSgibG0iKQ0KDQp0cmFpbl9maXQ5IDwtIHRyZWVfc3BlYyAlPiUgDQogIGZpdChSRU5URURfQklLRV9DT1VOVCB+IFRFTVBFUkFUVVJFICsgSFVNSURJVFkgKyBXSU5EX1NQRUVEICsgU09MQVJfUkFESUFUSU9OICsgUkFJTkZBTEwgKyBTTk9XRkFMTCwgZGF0YSA9IHRyYWluX2RhdGEpDQoNCiMgRXh0cmFjdCBwcmVkaWN0aW9ucw0KcHJlZDUgPC0gcHJlZGljdCh0cmFpbl9maXQ1LCB0cmFpbl9kYXRhKSQucHJlZA0KcHJlZDYgPC0gcHJlZGljdCh0cmFpbl9maXQ2LCB0cmFpbl9kYXRhKSQucHJlZA0KcHJlZDcgPC0gcHJlZGljdCh0cmFpbl9maXQ3LCB0cmFpbl9kYXRhKSQucHJlZA0KcHJlZDggPC0gcHJlZGljdCh0cmFpbl9maXQ4LCB0cmFpbl9kYXRhKSQucHJlZA0KcHJlZDkgPC0gcHJlZGljdCh0cmFpbl9maXQ5LCB0cmFpbl9kYXRhKSQucHJlZA0KDQojIENhbGN1bGF0ZSBSTVNFIG1hbnVhbGx5DQpybXNlX21hbnVhbCA8LSBmdW5jdGlvbihhY3R1YWwsIHByZWRpY3RlZCkgew0KICBzcXJ0KG1lYW4oKGFjdHVhbCAtIHByZWRpY3RlZCleMikpDQp9DQoNCiMgQ2FsY3VsYXRlIFJNU0UgZm9yIGVhY2ggbW9kZWwNCnJtc2U1IDwtIHJtc2VfbWFudWFsKHRyYWluX2RhdGEkUkVOVEVEX0JJS0VfQ09VTlQsIHByZWQ1KQ0Kcm1zZTYgPC0gcm1zZV9tYW51YWwodHJhaW5fZGF0YSRSRU5URURfQklLRV9DT1VOVCwgcHJlZDYpDQpybXNlNyA8LSBybXNlX21hbnVhbCh0cmFpbl9kYXRhJFJFTlRFRF9CSUtFX0NPVU5ULCBwcmVkNykNCnJtc2U4IDwtIHJtc2VfbWFudWFsKHRyYWluX2RhdGEkUkVOVEVEX0JJS0VfQ09VTlQsIHByZWQ4KQ0Kcm1zZTkgPC0gcm1zZV9tYW51YWwodHJhaW5fZGF0YSRSRU5URURfQklLRV9DT1VOVCwgcHJlZDkpDQoNCiMgQ29tYmluZSByZXN1bHRzIGludG8gYSB0YWJsZQ0KcmVzdWx0cyA8LSB0aWJibGUoDQogIG1vZGVsID0gYygiTW9kZWwgMTogTW9yZSBGZWF0dXJlcyIsICJNb2RlbCAyOiBSaWRnZSBSZWd1bGFyaXphdGlvbiIsICJNb2RlbCAzOiBQb2x5bm9taWFsIENvbXBvbmVudHMiLCAiTW9kZWwgNDogSW50ZXJhY3Rpb24gVGVybXMiLCAiTW9kZWwgNTogTE0iKSwNCiAgUk1TRSA9IGMocm1zZTUsIHJtc2U2LCBybXNlNywgcm1zZTgsIHJtc2U5KQ0KKQ0KDQojIERpc3BsYXkgdGhlIHJlc3VsdHMNCnByaW50KHJlc3VsdHMpDQoNCmBgYA0KDQoNCg0KMS4gKipNb2RlbCAxOiBNb3JlIEZlYXR1cmVzKiogLSBUaGlzIG1vZGVsIGluY2x1ZGVzIG11bHRpcGxlIGZlYXR1cmVzIHN1Y2ggYXMgdGVtcGVyYXR1cmUsIGh1bWlkaXR5LCB3aW5kIHNwZWVkLCB2aXNpYmlsaXR5LCBkZXcgcG9pbnQgdGVtcGVyYXR1cmUsIHNvbGFyIHJhZGlhdGlvbiwgcmFpbmZhbGwsIGFuZCBzbm93ZmFsbC4gSXQgaGFzIGFuIFJNU0Ugb2YgMC4xMzcwMTE5LCBpbmRpY2F0aW5nIGEgcmVsYXRpdmVseSBnb29kIGZpdC4gVGhlIGluY2x1c2lvbiBvZiBkaXZlcnNlIGZlYXR1cmVzIGhlbHBzIGNhcHR1cmUgdmFyaW91cyBhc3BlY3RzIGFmZmVjdGluZyB0aGUgcmVudGVkIGJpa2UgY291bnQuDQoNCjIuICoqTW9kZWwgMjogUmlkZ2UgUmVndWxhcml6YXRpb24qKiAtIFRoaXMgbW9kZWwgYXBwbGllcyBMMiByZWd1bGFyaXphdGlvbiB0byBwcmV2ZW50IG92ZXJmaXR0aW5nLiBEZXNwaXRlIHRoZSByZWd1bGFyaXphdGlvbiwgaXRzIFJNU0UgaXMgc2xpZ2h0bHkgaGlnaGVyIGF0IDAuMTQzNzUzMy4gVGhpcyBzdWdnZXN0cyB0aGF0IHdoaWxlIHJlZ3VsYXJpemF0aW9uIGhlbHBzIGluIGNvbnRyb2xsaW5nIG1vZGVsIGNvbXBsZXhpdHksIGl0IG1heSBub3QgYWx3YXlzIGxlYWQgdG8gYmV0dGVyIHBlcmZvcm1hbmNlIGluIHRlcm1zIG9mIFJNU0UuDQoNCjMuICoqTW9kZWwgMzogUG9seW5vbWlhbCBDb21wb25lbnRzKiogLSBCeSBhZGRpbmcgcG9seW5vbWlhbCBjb21wb25lbnRzLCB0aGlzIG1vZGVsIGNhcHR1cmVzIG5vbi1saW5lYXIgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBwcmVkaWN0b3JzIGFuZCB0aGUgdGFyZ2V0IHZhcmlhYmxlLiBJdCBoYXMgdGhlIGxvd2VzdCBSTVNFIG9mIDAuMTI5MTc1NiwgaW5kaWNhdGluZyB0aGF0IG5vbi1saW5lYXIgdHJhbnNmb3JtYXRpb25zIG9mIHRoZSBmZWF0dXJlcyBzaWduaWZpY2FudGx5IGltcHJvdmUgdGhlIG1vZGVsJ3MgcHJlZGljdGl2ZSBhY2N1cmFjeS4NCg0KNC4gKipNb2RlbCA0OiBJbnRlcmFjdGlvbiBUZXJtcyoqIC0gVGhpcyBtb2RlbCBpbmNsdWRlcyBpbnRlcmFjdGlvbiB0ZXJtcyBiZXR3ZWVuIHBhaXJzIG9mIGZlYXR1cmVzLCBhbGxvd2luZyBpdCB0byBjYXB0dXJlIHRoZSBjb21iaW5lZCBlZmZlY3Qgb2YgdHdvIHZhcmlhYmxlcyBvbiB0aGUgdGFyZ2V0LiBXaXRoIGFuIFJNU0Ugb2YgMC4xMzM4MjkzLCBpdCBwZXJmb3JtcyBiZXR0ZXIgdGhhbiB0aGUgcmlkZ2UgcmVndWxhcml6YXRpb24gbW9kZWwgYnV0IG5vdCBhcyB3ZWxsIGFzIHRoZSBwb2x5bm9taWFsIGNvbXBvbmVudHMgbW9kZWwuIEludGVyYWN0aW9uIHRlcm1zIGNhbiBiZSB1c2VmdWwgYnV0IG1heSBub3QgYWx3YXlzIGxlYWQgdG8gdGhlIGJlc3QgcGVyZm9ybWFuY2UuDQoNCjUuICoqTW9kZWwgNToqKiAtIFRoaXMgbW9kZWwgdXNlcyBhIGRlY2lzaW9uIHRyZWUgYWxnb3JpdGhtLCB3aGljaCBpcyBkaWZmZXJlbnQgZnJvbSBsaW5lYXIgcmVncmVzc2lvbi4gSXQgaGFzIGFuIFJNU0Ugb2YgMC4xMzcwMTY4LCBzaW1pbGFyIHRvIE1vZGVsIDEuIERlY2lzaW9uIHRyZWVzIGNhbiBjYXB0dXJlIGNvbXBsZXggcmVsYXRpb25zaGlwcyBhbmQgaW50ZXJhY3Rpb25zIGJldHdlZW4gZmVhdHVyZXMsIGJ1dCB0aGV5IG1heSBub3QgYWx3YXlzIG91dHBlcmZvcm0gbGluZWFyIG1vZGVscyB3aXRoIHBvbHlub21pYWwgY29tcG9uZW50cy4NCg0KT3ZlcmFsbCwgdGhlIHBvbHlub21pYWwgY29tcG9uZW50cyBtb2RlbCAoTW9kZWwgMykgc2hvd3MgdGhlIGJlc3QgcGVyZm9ybWFuY2UgaW4gdGVybXMgb2YgUk1TRSwgc3VnZ2VzdGluZyB0aGF0IGNhcHR1cmluZyBub24tbGluZWFyIHJlbGF0aW9uc2hpcHMgaXMgY3J1Y2lhbCBmb3IgcHJlZGljdGluZyB0aGUgcmVudGVkIGJpa2UgY291bnQgYWNjdXJhdGVseS4gUmVndWxhcml6YXRpb24gYW5kIGludGVyYWN0aW9uIHRlcm1zIGFsc28gY29udHJpYnV0ZSB0byBtb2RlbCBwZXJmb3JtYW5jZSBidXQgbWF5IG5vdCBhbHdheXMgbGVhZCB0byB0aGUgbG93ZXN0IFJNU0UuDQoNCg0KYGBge3J9DQoNCiMgQ3JlYXRlIFEtUSBQbG90IGZvciBlYWNoIG1vZGVsDQpxcV9wbG90IDwtIGZ1bmN0aW9uKHByZWRpY3Rpb25zLCBtb2RlbF9uYW1lKSB7DQogIGdncGxvdChkYXRhLmZyYW1lKHJlc2lkdWFscyA9IHRyYWluX2RhdGEkUkVOVEVEX0JJS0VfQ09VTlQgLSBwcmVkaWN0aW9ucyksIGFlcyhzYW1wbGUgPSByZXNpZHVhbHMpKSArDQogICAgc3RhdF9xcSgpICsNCiAgICBzdGF0X3FxX2xpbmUoKSArDQogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJRLVEgUGxvdCBmb3IiLCBtb2RlbF9uYW1lKSwNCiAgICAgICAgIHggPSAiVGhlb3JldGljYWwgUXVhbnRpbGVzIiwNCiAgICAgICAgIHkgPSAiU2FtcGxlIFF1YW50aWxlcyIpICsNCiAgICB0aGVtZV9taW5pbWFsKCkNCn0NCg0KIyBHZW5lcmF0ZSBRLVEgUGxvdHMNCnFxX3Bsb3QxIDwtIHFxX3Bsb3QocHJlZDUsICJNb2RlbCAxOiBNb3JlIEZlYXR1cmVzIikNCnFxX3Bsb3QyIDwtIHFxX3Bsb3QocHJlZDYsICJNb2RlbCAyOiBSaWRnZSBSZWd1bGFyaXphdGlvbiIpDQpxcV9wbG90MyA8LSBxcV9wbG90KHByZWQ3LCAiTW9kZWwgMzogUG9seW5vbWlhbCBDb21wb25lbnRzIikNCnFxX3Bsb3Q0IDwtIHFxX3Bsb3QocHJlZDgsICJNb2RlbCA0OiBJbnRlcmFjdGlvbiBUZXJtcyIpDQpxcV9wbG90NSA8LSBxcV9wbG90KHByZWQ5LCAiTW9kZWwgNTogTE0iKQ0KDQojIERpc3BsYXkgUS1RIFBsb3RzDQpwcmludChxcV9wbG90MSkNCnByaW50KHFxX3Bsb3QyKQ0KcHJpbnQocXFfcGxvdDMpDQpwcmludChxcV9wbG90NCkNCnByaW50KHFxX3Bsb3Q1KQ0KDQpgYGANCg0KDQo=