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
  1. 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.

  2. 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.

  3. 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
  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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=