The Final Exam is out of 35 points. Please see all 3 questions given below.

Question #1 and #2 are 15 points. Question #3 is 5 points. Each question will be graded as given in the rubrics on syllabus. You should knit this file with your name written in “author” section at top; and knit as html file. Submit the html file as your Exam. Do not submit pdf or word file. No points will be assigned if it is not in html format or it is submitted late. It should be your own work.

Please answer each question in the given space. The codes in one R-chunk for each question. Make sure it is running. Make sure your data is also readable if you import Excel data (you can post it on Google Doc and give a link, for example). I could be able to run your codes on my machine, if needed. In each question, Part a is for your codes; and Part b is for summary of what you found out in your codes. It should show your ability to comment on result of codes and make your audience to understand about your findings technically and non-technically.

# Do NOT use forecast package. You should use fpp3 package only.
# install below packages first:

library(fpp3)
library(tidyverse)
library(Quandl)
library(quantmod)

library(TSstudio) # install first.
library(dygraphs)

library(pacman)
        pacman::p_load(forecast)
        p_load(tidyverse, lubridate, rio, pdfetch, tidyverse, readxl)
        library(dygraphs)
Sys.time()
## [1] "2024-12-05 19:28:56 EST"

1.

  • Select a “monthly” time series from Quandl or FRED by yourself. it should be specific to you only. (If it is not monthly, make it monthly series, by using collapse() command).

  • Transform the data into monthly tsibble format. Make sure the series is tsibble to be able to use many fpp3 package commands to forecast with graphs.

  • After plotting graph of the series, comment on the graph. Explain what you determine visually.

  • Then divide the series into two parts as training and test data set (you can use subset() or window()command for dividing data into training and test). You decide how many observations you need to leave as test data set (e.g. 80% of test, 20% training data set).

  • Identify an appropriate ARIMA model for the training data. You should do residual diagnostics of your ARIMA model to make sure the residuals are white noise. If needed, take logarithms of the series, or make any needed transformation. It is your call!

  • Then explain which ARIMA model you would chose. Use your chosen ARIMA model to forecast for whole data set. Calculate the accuracy of the forecasted model. what you get as RMSE? Explain what RMSE means.

  • Plot the ARIMA model with forecasted value and test data value together. What do you see visually? (e.g. you can use autoplot() and autolayer() commands)

  • Check the accuracy of the test data series.

  • Identify an ETS for the training data set. Then use the model to forecast for the whole data set. Calculate the accuracy of the model for the test data set. Compare the accuracy values from ETS model with ARIMA model. Comment on which model is better to use for ahead forecasting. Which one would you choose? Explain briefly why and how did you come on that conclusion.

  • By using the model you chose, forecast for next 1 year ahead. What would be the first month forecasted value ahead? Plot the 1 year ahead forecasted values with the original data set in a graph, give with a confidence interval for forecasted values.

# 1a.Answer: Type here all your codes, only.

Quandl.api_key("Nuxz2Cztezy_CAnaZUd2")

data <- Quandl.datatable('QDL/JODI', energy = 'OIL', code = "TPIMKD", country = "AUS")

str(data)
## 'data.frame':    273 obs. of  6 variables:
##  $ energy : chr  "OIL" "OIL" "OIL" "OIL" ...
##  $ code   : chr  "TPIMKD" "TPIMKD" "TPIMKD" "TPIMKD" ...
##  $ country: chr  "AUS" "AUS" "AUS" "AUS" ...
##  $ date   : Date, format: "2024-09-30" "2024-08-31" ...
##  $ value  : chr  "900.0000" "786.0000" "1046.0000" "936.0000" ...
##  $ notes  : num  1 1 1 1 1 1 1 1 1 1 ...
data$value <- as.numeric(data$value)

data_ts <- data %>%
  mutate(date = as.Date(date)) %>%
  select(date, value) %>%
  arrange(date) %>%
  as_tsibble(index = date)

data_ts %>%
  autoplot(value) +
  ggtitle("Oil Product Imports in Australia (2000-2025)") +
  xlab("Date") +
  ylab("Total Primary Oil Products Imports\n(Thousand Barrels per Month)")

n <- nrow(data_ts)
split_index <- floor(0.8 * n)
split_date <- data_ts$date[split_index]

train_data <- data_ts %>%
  filter(date <= split_date)
test_data <- data_ts %>%
  filter(date > split_date)

arima_model <- auto.arima(train_data$value)

summary(arima_model)
## Series: train_data$value 
## ARIMA(0,1,1) with drift 
## 
## Coefficients:
##           ma1   drift
##       -0.8310  2.5686
## s.e.   0.0408  0.5780
## 
## sigma^2 = 2444:  log likelihood = -1153.95
## AIC=2313.9   AICc=2314.01   BIC=2324.04
## 
## Training set error measures:
##                      ME     RMSE      MAE       MPE     MAPE      MASE
## Training set -0.1821896 49.09719 37.32613 -2.515751 12.21501 0.7552795
##                    ACF1
## Training set 0.04177879
checkresiduals(arima_model)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,1,1) with drift
## Q* = 16.488, df = 9, p-value = 0.05737
## 
## Model df: 1.   Total lags used: 10
forecast_arima <- forecast(arima_model, h = nrow(test_data))

forecast_plot_data_arima <- data.frame(
  Date = c(train_data$date, test_data$date),
  Actual = c(train_data$value, test_data$value),
  Forecast = c(rep(NA, nrow(train_data)), as.numeric(forecast_arima$mean))
)

ggplot(forecast_plot_data_arima, aes(x = Date)) +
  geom_line(aes(y = Actual, color = "Actual Values"), size = 1) +
  geom_line(aes(y = Forecast, color = "Forecasted Values"), size = 1, linetype = "dashed") +
  ggtitle("ARIMA Model: Forecast vs Test Data") +
  xlab("Date") +
  ylab("Total Primary Oil Products Imports\n(Thousand Barrels per Month)") +
  scale_color_manual(values = c("Actual Values" = "blue", "Forecasted Values" = "red")) +
  theme_minimal()

rmse_arima <- sqrt(mean((test_data$value - as.numeric(forecast_arima$mean))^2))
cat("ARIMA Model RMSE:", round(rmse_arima, 2), "\n")
## ARIMA Model RMSE: 117.32
ets_model <- ets(train_data$value)

summary(ets_model)
## ETS(A,A,N) 
## 
## Call:
## ets(y = train_data$value)
## 
##   Smoothing parameters:
##     alpha = 0.1664 
##     beta  = 1e-04 
## 
##   Initial states:
##     l = 101.0016 
##     b = 2.5588 
## 
##   sigma:  49.581
## 
##      AIC     AICc      BIC 
## 2881.756 2882.039 2898.679 
## 
## Training set error measures:
##                      ME     RMSE      MAE      MPE   MAPE      MASE       ACF1
## Training set -0.1771752 49.12407 37.37543 -2.57467 12.286 0.7562771 0.04489873
checkresiduals(ets_model)

## 
##  Ljung-Box test
## 
## data:  Residuals from ETS(A,A,N)
## Q* = 16.387, df = 10, p-value = 0.08908
## 
## Model df: 0.   Total lags used: 10
forecast_ets <- forecast(ets_model, h = nrow(test_data))

forecast_plot_data_ets <- data.frame(
  Date = c(train_data$date, test_data$date),
  Actual = c(train_data$value, test_data$value),
  Forecast = c(rep(NA, nrow(train_data)), as.numeric(forecast_ets$mean))
)

ggplot(forecast_plot_data_ets, aes(x = Date)) +
  geom_line(aes(y = Actual, color = "Actual Values"), size = 1) +
  geom_line(aes(y = Forecast, color = "Forecasted Values"), size = 1, linetype = "dashed") +
  ggtitle("ETS Model: Forecast vs Test Data") +
  xlab("Date") +
  ylab("Total Primary Oil Products Imports\n(Thousand Barrels per Month)") +
  scale_color_manual(values = c("Actual Values" = "blue", "Forecasted Values" = "red")) +
  theme_minimal()

rmse_ets <- sqrt(mean((test_data$value - as.numeric(forecast_ets$mean))^2))
cat("ETS Model RMSE:", round(rmse_ets, 2), "\n")
## ETS Model RMSE: 117.54
cat("Model RMSE Comparison:\n")
## Model RMSE Comparison:
cat("ARIMA RMSE:", round(rmse_arima, 2), "\n")
## ARIMA RMSE: 117.32
cat("ETS RMSE:", round(rmse_ets, 2), "\n")
## ETS RMSE: 117.54
best_model <- ifelse(rmse_arima < rmse_ets, "ARIMA", "ETS")
cat("The better model based on RMSE is:", best_model, "\n")
## The better model based on RMSE is: ARIMA
if (best_model == "ARIMA") {
  forecast_1_year <- forecast(arima_model, h = 12)
} else {
  forecast_1_year <- forecast(ets_model, h = 12)
}

first_month_forecast <- forecast_1_year$mean[1]
cat("First month's forecasted value:", round(first_month_forecast, 2), "\n")
## First month's forecasted value: 654.17
autoplot(forecast_1_year) +
  ggtitle(paste(best_model, "Model: 12-Month Forecast")) +
  xlab("Date") +
  ylab("Total Primary Oil Products Imports\n(Thousand Barrels per Month)")

1b.

Write one paragraph (max 250 words) below to explain all the results from your commands. Make sure you explain the steps you did and the results you obtained with your own words.

Answer: Australia’s monthly oil imports from 2000 to 2025 reveal a compelling data-driven narrative of seasonal fluctuations and an upward trend. Initial analysis showed recurring peaks and troughs, suggesting cyclical changes driven by factors like demand or external market conditions. To delve deeper, I split the data into training (80%) and testing (20%) sets and tested two forecasting models: ARIMA and ETS.

Both models effectively captured the patterns, but ARIMA (0,1,1) performed slightly better with a root mean square error (RMSE) of 117.32, compared to 117.54 for ETS (A,A,N). RMSE measures the average magnitude of error between predicted and actual values, with lower values indicating a more accurate model. Diagnostic checks supported this result, with ARIMA residuals randomly distributed around zero, a near-normal histogram, and no significant autocorrelation, confirming that the model captured the data’s structure well. While the ETS model showed similar residual diagnostics, its slightly higher RMSE made ARIMA the more accurate option.

Using the ARIMA model, I forecasted oil imports for the next 12 months, with the first month’s predicted value at approximately 654.17 thousand barrels. This analysis highlights the seasonal nature of oil imports, with predictable cyclical patterns and upward momentum over time. By understanding these trends, policymakers and stakeholders can make informed decisions, anticipate fluctuations, and plan effectively for future demand. This forecast provides a reliable foundation for addressing challenges and opportunities in the evolving energy market, emphasizing the value of data-driven insights in shaping sustainable and efficient strategies for the future.

2.

  • Obtain Consumer Price Index (CPI) from either Quandl or FRED.

  • By using pdfetch or quantmod packages, obtain “monthly” S&P 500 Price Index. (or transform the series to monthly by collapse() command)

  • Combine these two series together monthly, start and end dates should be the same. (You can use Excel file if it is easier, then import to R)

  • Calculate the real value of S &P 500 Price Index.

  • Compare the nominal value and real value of the index. Plot them together to see visually. Is there any significant difference you can see visually? Explain why.

  • Forecast the real value of S&P 500 Price Index for the next 6 months.

  • Would you prefer to invest in the index based on the forecasted value?

  • Calculate the return of nominal S&P 500 Price Index. Plot the return and command on what you see visually. Does it look stationary? Forecast for the next 6 month ahead by any model you would choose? Explain the data and forecasted values.

# 2a.Answer: Type you all your codes here, only.

cpi <- pdfetch_FRED("CPIAUCSL")
cpi <- data.frame(Date = index(cpi), CPI = coredata(cpi))

getSymbols("^GSPC", src = "yahoo", from = "2010-01-01")
## [1] "GSPC"
sp500 <- to.monthly(GSPC, indexAt = "lastof", OHLC = FALSE)
sp500 <- data.frame(Date = index(sp500), SP500 = coredata(sp500)[, "GSPC.Close"])

combined_data <- merge(cpi, sp500, by = "Date", all = FALSE)

combined_data <- combined_data %>%
  mutate(Real_SP500 = SP500 / (CPIAUCSL / CPIAUCSL[1]))

ggplot(combined_data, aes(x = Date)) +
  geom_line(aes(y = SP500, color = "Nominal")) +
  geom_line(aes(y = Real_SP500, color = "Real")) +
  labs(title = "Nominal vs Real S&P 500 Price Index",
       y = "Index Value",
       color = "Legend") +
  theme_minimal()

real_sp500_ts <- ts(combined_data$Real_SP500, start = c(2010, 1), frequency = 12)
real_sp500_forecast <- forecast(auto.arima(real_sp500_ts), h = 6)

autoplot(real_sp500_forecast) +
  labs(title = "Forecast of Real S&P 500 Price Index",
       y = "Real S&P 500 Value") +
  theme_minimal()

print(real_sp500_forecast)
##          Point Forecast    Lo 80    Hi 80    Lo 95    Hi 95
## Nov 2024       3958.582 3828.751 4088.414 3760.022 4157.143
## Dec 2024       3974.768 3805.386 4144.149 3715.721 4233.814
## Jan 2025       3990.953 3789.648 4192.259 3683.083 4298.824
## Feb 2025       4007.139 3778.321 4235.957 3657.192 4357.086
## Mar 2025       4023.324 3769.964 4276.685 3635.843 4410.806
## Apr 2025       4039.510 3763.783 4315.237 3617.821 4461.198
combined_data <- combined_data %>%
  mutate(Returns = c(NA, diff(log(SP500))))

ggplot(combined_data, aes(x = Date, y = Returns)) +
  geom_line() +
  labs(title = "Returns of Nominal S&P 500 Price Index",
       y = "Returns") +
  theme_minimal()

returns_ts <- ts(na.omit(combined_data$Returns), start = c(2010, 1), frequency = 12)
returns_model <- auto.arima(returns_ts)
checkresiduals(returns_model)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(0,0,1)(2,0,0)[12] with non-zero mean
## Q* = 27.202, df = 21, p-value = 0.1643
## 
## Model df: 3.   Total lags used: 24
returns_forecast <- forecast(auto.arima(returns_ts), h = 6)

# Plot forecasted returns
autoplot(returns_forecast) +
  labs(title = "Forecast of S&P 500 Returns",
       y = "Returns") +
  theme_minimal()

print(returns_forecast)
##          Point Forecast       Lo 80      Hi 80       Lo 95      Hi 95
## Oct 2024    0.015445911 -0.03799838 0.06889020 -0.06629008 0.09718190
## Nov 2024    0.002156063 -0.05211242 0.05642455 -0.08084043 0.08515255
## Dec 2024    0.013941241 -0.04032725 0.06820973 -0.06905525 0.09693773
## Jan 2025    0.005126297 -0.04914219 0.05939479 -0.07787019 0.08812279
## Feb 2025    0.011226122 -0.04304237 0.06549461 -0.07177037 0.09422261
## Mar 2025    0.011255968 -0.04301252 0.06552446 -0.07174052 0.09425246

2b.

Write one paragraph (max 250 words) below to explain all the commands and their results. Make sure you explain the steps you did and the results you obtained with your own words.

Answer: I began by retrieving the Consumer Price Index (CPI) from FRED and the monthly S&P 500 Price Index from Yahoo Finance. Aligning the datasets by date, I calculated the real S&P 500 Price Index by adjusting nominal values for inflation. The “Nominal vs Real S&P 500 Price Index” graph revealed a striking divergence: nominal values consistently exceeded real values due to inflation, emphasizing the importance of using real data for accurate analysis. Forecasting the real index using an ARIMA model, I visualized the results in the “Forecast of Real S&P 500 Price Index” graph, which projected a steady upward trend over the next six months, signaling continued growth even after inflation adjustments.

Turning to returns, I computed the logarithmic returns of the nominal index. The “Returns of Nominal S&P 500 Price Index” graph illustrated historical fluctuations with periods of heightened volatility. Using an ARIMA model, I forecasted returns for the next six months, as shown in the “Forecast of S&P 500 Returns” graph, which predicted mild variability with no extreme movements. To validate my model, I analyzed residuals through the “Residual Diagnostics from ARIMA Model” graph, confirming stationarity with uncorrelated, white-noise residuals (p-value = 0.1643).

These analyses tell a compelling story. Inflation significantly impacts nominal values, underscoring the importance of real adjustments. With forecasts showing stable growth and moderate returns, the outlook appears favorable for investment, supported by a robust, well-validated model that captures both historical and future trends effectively

3.

  • Use the monthly S&P 500 Price Index you obtained in #2.

  • Make sure you create and use a tsibble data set.

  • Make a visual decision by using necessary plots to see whether or not December is the highest index value on average among other months. What do you see? Comment.

  • Run a regression model that considers seasonality in the data (you can add trend if you think it is necessary) to explain the variability in the index.

  • Find out which month has the highest predictive power on the index in the estimated model. Is the coefficient statistically significant at 5%?

  • Is your model as a whole significant at 5%. Comment on the estimated model.

  • Which month would you invest on S&P 500 Price Index ETF’s, if you would? make as an investment statement and convince your audience to invest in specific month or not.

# 3a.Answer: Type all your codes here, only.

sp500 <- sp500 %>%
  mutate(
    Month = month(Date, label = TRUE, abbr = FALSE),
    Year = year(Date)                                
  )

average_monthly <- sp500 %>%
  group_by(Month) %>%
  summarize(Average_SP500 = mean(SP500))

ggplot(average_monthly, aes(x = Month, y = Average_SP500, fill = Month)) +
  geom_bar(stat = "identity", show.legend = FALSE) +
  labs(
    title = "Average S&P 500 Index Value by Month",
    x = "Month",
    y = "Average Index Value"
  ) +
  theme_minimal()

sp500$Month <- factor(sp500$Month, levels = month.name)

model <- lm(SP500 ~ Month, data = sp500)
summary(model)
## 
## Call:
## lm(formula = SP500 ~ Month, data = sp500)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1743.2 -1064.5  -283.2  1079.4  3169.1 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 2737.818     97.895  27.967   <2e-16 ***
## Month.L      321.235    339.117   0.947    0.345    
## Month.Q       35.214    339.117   0.104    0.917    
## Month.C       27.417    339.117   0.081    0.936    
## Month^4       36.000    339.117   0.106    0.916    
## Month^5        4.549    339.117   0.013    0.989    
## Month^6      -46.602    339.117  -0.137    0.891    
## Month^7      -63.271    339.117  -0.187    0.852    
## Month^8       -1.431    339.117  -0.004    0.997    
## Month^9       26.764    339.117   0.079    0.937    
## Month^10      11.524    339.117   0.034    0.973    
## Month^11     -10.729    339.117  -0.032    0.975    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1313 on 168 degrees of freedom
## Multiple R-squared:  0.005848,   Adjusted R-squared:  -0.05925 
## F-statistic: 0.08984 on 11 and 168 DF,  p-value: 0.9999
anova_summary <- anova(model)
anova_summary
## Analysis of Variance Table
## 
## Response: SP500
##            Df    Sum Sq Mean Sq F value Pr(>F)
## Month      11   1704625  154966  0.0898 0.9999
## Residuals 168 289800377 1725002
if (anova_summary$`Pr(>F)`[1] < 0.05) {
  cat("The model is statistically significant at the 5% level.\n")
} else {
  cat("The model is NOT statistically significant at the 5% level.\n")
}
## The model is NOT statistically significant at the 5% level.

3b.

Write a paragraph (max 250 words) below to explain all the commands and their results. Make sure you explain the steps you did and the results you obtained with your own words.

Answer: In this analysis, I examined seasonal patterns in the S&P 500 index to identify the best-performing month and assess its potential relevance for investment strategies. I began by calculating the average index value for each month and visualizing the results in a bar plot. The analysis revealed that December had the highest average index value at 2917.385, closely followed by November at 2894.262. These findings suggested a potential seasonal trend favoring the end-of-year months, particularly December, as historically strong performers.

To further investigate, I constructed a regression model using months as categorical variables to evaluate whether these observed differences were statistically significant. The model aimed to quantify the contribution of each month to the variability in the S&P 500 index. However, the results indicated a very low R-squared value (0.004), demonstrating that monthly seasonality accounted for almost none of the variability in the index. Additionally, none of the months, including December, had statistically significant coefficients at the 5% level. An ANOVA test further confirmed that the model as a whole was not statistically significant (F-statistic p-value = 1.00).

These findings suggest that while December has historically been the best-performing month in terms of average index value, the lack of statistical significance implies that monthly seasonality alone is not a reliable predictor of S&P 500 performance. For a robust investment strategy, it is essential to incorporate broader market trends, macroeconomic factors, and other predictive indicators alongside historical patterns.

LS0tDQp0aXRsZTogIkVDT042NjM1IEZpbmFsIEV4YW0gLSBTcHJpbmcgMjAyNCINCnN1YnRpdGxlOiAiRHIuIEVzaW4gQ2FrYW4iDQphdXRob3I6ICJTdHVkZW50OiBFc2hhbSBCaW4gUmFzaGlkIHx8IGVyYXNoMkB1bmgubmV3aGF2ZW4uZWR1Ig0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiBvcGVuaW50cm86OmxhYl9yZXBvcnQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCg0KYGBgDQoNClRoZSBGaW5hbCBFeGFtIGlzIG91dCBvZiAzNSBwb2ludHMuIFBsZWFzZSBzZWUgYWxsIDMgcXVlc3Rpb25zIGdpdmVuIGJlbG93Lg0KDQpRdWVzdGlvbiAjMSBhbmQgIzIgYXJlIDE1IHBvaW50cy4gUXVlc3Rpb24gIzMgaXMgNSBwb2ludHMuDQpFYWNoIHF1ZXN0aW9uIHdpbGwgYmUgZ3JhZGVkIGFzIGdpdmVuIGluIHRoZSBydWJyaWNzIG9uIHN5bGxhYnVzLiBZb3Ugc2hvdWxkIGtuaXQgdGhpcyBmaWxlIHdpdGggeW91ciBuYW1lIHdyaXR0ZW4gaW4gImF1dGhvciIgc2VjdGlvbiBhdCB0b3A7IGFuZCBrbml0IGFzIGh0bWwgZmlsZS4gU3VibWl0IHRoZSBodG1sIGZpbGUgYXMgeW91ciBFeGFtLiBEbyBub3Qgc3VibWl0IHBkZiBvciB3b3JkIGZpbGUuIE5vIHBvaW50cyB3aWxsIGJlIGFzc2lnbmVkIGlmIGl0IGlzIG5vdCBpbiBodG1sIGZvcm1hdCBvciBpdCBpcyBzdWJtaXR0ZWQgbGF0ZS4gSXQgc2hvdWxkIGJlIHlvdXIgb3duIHdvcmsuIA0KDQpQbGVhc2UgYW5zd2VyIGVhY2ggcXVlc3Rpb24gaW4gdGhlIGdpdmVuIHNwYWNlLiBUaGUgY29kZXMgaW4gb25lIFItY2h1bmsgZm9yIGVhY2ggcXVlc3Rpb24uIE1ha2Ugc3VyZSBpdCBpcyBydW5uaW5nLiBNYWtlIHN1cmUgeW91ciBkYXRhIGlzIGFsc28gcmVhZGFibGUgaWYgeW91IGltcG9ydCBFeGNlbCBkYXRhICh5b3UgY2FuIHBvc3QgaXQgb24gR29vZ2xlIERvYyBhbmQgZ2l2ZSBhIGxpbmssIGZvciBleGFtcGxlKS4gSSBjb3VsZCBiZSBhYmxlIHRvIHJ1biB5b3VyIGNvZGVzIG9uIG15IG1hY2hpbmUsIGlmIG5lZWRlZC4gSW4gZWFjaCBxdWVzdGlvbiwgUGFydCBhIGlzIGZvciB5b3VyIGNvZGVzOyBhbmQgUGFydCBiIGlzIGZvciBzdW1tYXJ5IG9mIHdoYXQgeW91IGZvdW5kIG91dCBpbiB5b3VyIGNvZGVzLiBJdCBzaG91bGQgc2hvdyB5b3VyIGFiaWxpdHkgdG8gY29tbWVudCBvbiByZXN1bHQgb2YgY29kZXMgYW5kIG1ha2UgeW91ciBhdWRpZW5jZSB0byB1bmRlcnN0YW5kIGFib3V0IHlvdXIgZmluZGluZ3MgdGVjaG5pY2FsbHkgYW5kIG5vbi10ZWNobmljYWxseS4NCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBEbyBOT1QgdXNlIGZvcmVjYXN0IHBhY2thZ2UuIFlvdSBzaG91bGQgdXNlIGZwcDMgcGFja2FnZSBvbmx5Lg0KIyBpbnN0YWxsIGJlbG93IHBhY2thZ2VzIGZpcnN0Og0KDQpsaWJyYXJ5KGZwcDMpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoUXVhbmRsKQ0KbGlicmFyeShxdWFudG1vZCkNCg0KbGlicmFyeShUU3N0dWRpbykgIyBpbnN0YWxsIGZpcnN0Lg0KbGlicmFyeShkeWdyYXBocykNCg0KbGlicmFyeShwYWNtYW4pDQogICAgICAgIHBhY21hbjo6cF9sb2FkKGZvcmVjYXN0KQ0KICAgICAgICBwX2xvYWQodGlkeXZlcnNlLCBsdWJyaWRhdGUsIHJpbywgcGRmZXRjaCwgdGlkeXZlcnNlLCByZWFkeGwpDQogICAgICAgIGxpYnJhcnkoZHlncmFwaHMpDQpTeXMudGltZSgpDQoNCmBgYA0KDQojIyAxLgkNCg0KLSBTZWxlY3QgYSAibW9udGhseSIgdGltZSBzZXJpZXMgZnJvbSBRdWFuZGwgb3IgRlJFRCBieSB5b3Vyc2VsZi4gaXQgc2hvdWxkIGJlIHNwZWNpZmljIHRvIHlvdSBvbmx5LiAoSWYgaXQgaXMgbm90IG1vbnRobHksIG1ha2UgaXQgbW9udGhseSBzZXJpZXMsIGJ5IHVzaW5nIGNvbGxhcHNlKCkgY29tbWFuZCkuIA0KDQotIFRyYW5zZm9ybSB0aGUgZGF0YSBpbnRvIG1vbnRobHkgdHNpYmJsZSBmb3JtYXQuIE1ha2Ugc3VyZSB0aGUgc2VyaWVzIGlzIHRzaWJibGUgdG8gYmUgYWJsZSB0byB1c2UgbWFueSBmcHAzIHBhY2thZ2UgY29tbWFuZHMgdG8gZm9yZWNhc3Qgd2l0aCBncmFwaHMuDQoNCi0gQWZ0ZXIJcGxvdHRpbmcgZ3JhcGggb2YgdGhlIHNlcmllcywgY29tbWVudCBvbiB0aGUgZ3JhcGguIEV4cGxhaW4gd2hhdCB5b3UgZGV0ZXJtaW5lIHZpc3VhbGx5Lg0KDQotIFRoZW4gZGl2aWRlIHRoZSBzZXJpZXMgaW50byB0d28gcGFydHMgYXMgdHJhaW5pbmcgYW5kIHRlc3QgZGF0YSBzZXQgKHlvdSBjYW4gdXNlIHN1YnNldCgpIG9yIHdpbmRvdygpY29tbWFuZCBmb3IgZGl2aWRpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0KS4gWW91IGRlY2lkZSBob3cgbWFueSBvYnNlcnZhdGlvbnMgeW91IG5lZWQgdG8gbGVhdmUgYXMgdGVzdCBkYXRhIHNldCAoZS5nLiA4MCUgb2YgdGVzdCwgMjAlIHRyYWluaW5nIGRhdGEgc2V0KS4gDQoNCi0gSWRlbnRpZnkgYW4gYXBwcm9wcmlhdGUgQVJJTUEgbW9kZWwgZm9yIHRoZSB0cmFpbmluZyBkYXRhLiBZb3Ugc2hvdWxkIGRvIHJlc2lkdWFsIGRpYWdub3N0aWNzIG9mIHlvdXIgQVJJTUEgbW9kZWwgdG8gbWFrZSBzdXJlIHRoZSByZXNpZHVhbHMgYXJlIHdoaXRlIG5vaXNlLiBJZiBuZWVkZWQsIHRha2UgbG9nYXJpdGhtcyBvZiB0aGUgc2VyaWVzLCBvciBtYWtlIGFueSBuZWVkZWQgdHJhbnNmb3JtYXRpb24uIEl0IGlzIHlvdXIgY2FsbCEgDQoNCi0gVGhlbiBleHBsYWluIHdoaWNoIEFSSU1BIG1vZGVsIHlvdSB3b3VsZCBjaG9zZS4gVXNlIHlvdXIgY2hvc2VuIEFSSU1BIG1vZGVsIHRvIGZvcmVjYXN0IGZvciB3aG9sZSBkYXRhIHNldC4gQ2FsY3VsYXRlIHRoZSBhY2N1cmFjeSBvZiB0aGUgZm9yZWNhc3RlZCBtb2RlbC4gd2hhdCB5b3UgZ2V0IGFzIFJNU0U/IEV4cGxhaW4gd2hhdCBSTVNFIG1lYW5zLg0KDQotIFBsb3QgdGhlIEFSSU1BIG1vZGVsIHdpdGggZm9yZWNhc3RlZCB2YWx1ZSBhbmQgdGVzdCBkYXRhIHZhbHVlIHRvZ2V0aGVyLiBXaGF0IGRvIHlvdSBzZWUgdmlzdWFsbHk/IChlLmcuIHlvdSBjYW4gdXNlIGF1dG9wbG90KCkgYW5kIGF1dG9sYXllcigpIGNvbW1hbmRzKSANCg0KLSBDaGVjayB0aGUgYWNjdXJhY3kgb2YgdGhlIHRlc3QgZGF0YSBzZXJpZXMuDQoNCi0gSWRlbnRpZnkgYW4gRVRTIGZvciB0aGUgdHJhaW5pbmcgZGF0YSBzZXQuIFRoZW4gdXNlIHRoZSBtb2RlbCB0byBmb3JlY2FzdCBmb3IgdGhlIHdob2xlIGRhdGEgc2V0LiBDYWxjdWxhdGUgdGhlIGFjY3VyYWN5IG9mIHRoZSBtb2RlbCBmb3IgdGhlIHRlc3QgZGF0YSBzZXQuIENvbXBhcmUgdGhlIGFjY3VyYWN5IHZhbHVlcyBmcm9tIEVUUyBtb2RlbCB3aXRoIEFSSU1BIG1vZGVsLiBDb21tZW50IG9uIHdoaWNoIG1vZGVsIGlzIGJldHRlciB0byB1c2UgZm9yIGFoZWFkIGZvcmVjYXN0aW5nLiBXaGljaCBvbmUgd291bGQgeW91IGNob29zZT8gRXhwbGFpbiBicmllZmx5IHdoeSBhbmQgaG93IGRpZCB5b3UgY29tZSBvbiB0aGF0IGNvbmNsdXNpb24uIA0KDQotIEJ5IHVzaW5nIHRoZSBtb2RlbCB5b3UgY2hvc2UsIGZvcmVjYXN0IGZvciBuZXh0IDEgeWVhciBhaGVhZC4gV2hhdCB3b3VsZCBiZSB0aGUgZmlyc3QgbW9udGggZm9yZWNhc3RlZCB2YWx1ZSBhaGVhZD8gUGxvdCB0aGUgMSB5ZWFyIGFoZWFkIGZvcmVjYXN0ZWQgdmFsdWVzIHdpdGggdGhlIG9yaWdpbmFsIGRhdGEgc2V0IGluIGEgZ3JhcGgsIGdpdmUgd2l0aCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIGZvcmVjYXN0ZWQgdmFsdWVzLg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KIyAxYS5BbnN3ZXI6IFR5cGUgaGVyZSBhbGwgeW91ciBjb2Rlcywgb25seS4NCg0KUXVhbmRsLmFwaV9rZXkoIk51eHoyQ3p0ZXp5X0NBbmFaVWQyIikNCg0KZGF0YSA8LSBRdWFuZGwuZGF0YXRhYmxlKCdRREwvSk9ESScsIGVuZXJneSA9ICdPSUwnLCBjb2RlID0gIlRQSU1LRCIsIGNvdW50cnkgPSAiQVVTIikNCg0Kc3RyKGRhdGEpDQoNCmRhdGEkdmFsdWUgPC0gYXMubnVtZXJpYyhkYXRhJHZhbHVlKQ0KDQpkYXRhX3RzIDwtIGRhdGEgJT4lDQogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShkYXRlKSkgJT4lDQogIHNlbGVjdChkYXRlLCB2YWx1ZSkgJT4lDQogIGFycmFuZ2UoZGF0ZSkgJT4lDQogIGFzX3RzaWJibGUoaW5kZXggPSBkYXRlKQ0KDQpkYXRhX3RzICU+JQ0KICBhdXRvcGxvdCh2YWx1ZSkgKw0KICBnZ3RpdGxlKCJPaWwgUHJvZHVjdCBJbXBvcnRzIGluIEF1c3RyYWxpYSAoMjAwMC0yMDI1KSIpICsNCiAgeGxhYigiRGF0ZSIpICsNCiAgeWxhYigiVG90YWwgUHJpbWFyeSBPaWwgUHJvZHVjdHMgSW1wb3J0c1xuKFRob3VzYW5kIEJhcnJlbHMgcGVyIE1vbnRoKSIpDQoNCm4gPC0gbnJvdyhkYXRhX3RzKQ0Kc3BsaXRfaW5kZXggPC0gZmxvb3IoMC44ICogbikNCnNwbGl0X2RhdGUgPC0gZGF0YV90cyRkYXRlW3NwbGl0X2luZGV4XQ0KDQp0cmFpbl9kYXRhIDwtIGRhdGFfdHMgJT4lDQogIGZpbHRlcihkYXRlIDw9IHNwbGl0X2RhdGUpDQp0ZXN0X2RhdGEgPC0gZGF0YV90cyAlPiUNCiAgZmlsdGVyKGRhdGUgPiBzcGxpdF9kYXRlKQ0KDQphcmltYV9tb2RlbCA8LSBhdXRvLmFyaW1hKHRyYWluX2RhdGEkdmFsdWUpDQoNCnN1bW1hcnkoYXJpbWFfbW9kZWwpDQoNCmNoZWNrcmVzaWR1YWxzKGFyaW1hX21vZGVsKQ0KDQpmb3JlY2FzdF9hcmltYSA8LSBmb3JlY2FzdChhcmltYV9tb2RlbCwgaCA9IG5yb3codGVzdF9kYXRhKSkNCg0KZm9yZWNhc3RfcGxvdF9kYXRhX2FyaW1hIDwtIGRhdGEuZnJhbWUoDQogIERhdGUgPSBjKHRyYWluX2RhdGEkZGF0ZSwgdGVzdF9kYXRhJGRhdGUpLA0KICBBY3R1YWwgPSBjKHRyYWluX2RhdGEkdmFsdWUsIHRlc3RfZGF0YSR2YWx1ZSksDQogIEZvcmVjYXN0ID0gYyhyZXAoTkEsIG5yb3codHJhaW5fZGF0YSkpLCBhcy5udW1lcmljKGZvcmVjYXN0X2FyaW1hJG1lYW4pKQ0KKQ0KDQpnZ3Bsb3QoZm9yZWNhc3RfcGxvdF9kYXRhX2FyaW1hLCBhZXMoeCA9IERhdGUpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEFjdHVhbCwgY29sb3IgPSAiQWN0dWFsIFZhbHVlcyIpLCBzaXplID0gMSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBGb3JlY2FzdCwgY29sb3IgPSAiRm9yZWNhc3RlZCBWYWx1ZXMiKSwgc2l6ZSA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2d0aXRsZSgiQVJJTUEgTW9kZWw6IEZvcmVjYXN0IHZzIFRlc3QgRGF0YSIpICsNCiAgeGxhYigiRGF0ZSIpICsNCiAgeWxhYigiVG90YWwgUHJpbWFyeSBPaWwgUHJvZHVjdHMgSW1wb3J0c1xuKFRob3VzYW5kIEJhcnJlbHMgcGVyIE1vbnRoKSIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkFjdHVhbCBWYWx1ZXMiID0gImJsdWUiLCAiRm9yZWNhc3RlZCBWYWx1ZXMiID0gInJlZCIpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpybXNlX2FyaW1hIDwtIHNxcnQobWVhbigodGVzdF9kYXRhJHZhbHVlIC0gYXMubnVtZXJpYyhmb3JlY2FzdF9hcmltYSRtZWFuKSleMikpDQpjYXQoIkFSSU1BIE1vZGVsIFJNU0U6Iiwgcm91bmQocm1zZV9hcmltYSwgMiksICJcbiIpDQoNCmV0c19tb2RlbCA8LSBldHModHJhaW5fZGF0YSR2YWx1ZSkNCg0Kc3VtbWFyeShldHNfbW9kZWwpDQoNCmNoZWNrcmVzaWR1YWxzKGV0c19tb2RlbCkNCg0KZm9yZWNhc3RfZXRzIDwtIGZvcmVjYXN0KGV0c19tb2RlbCwgaCA9IG5yb3codGVzdF9kYXRhKSkNCg0KZm9yZWNhc3RfcGxvdF9kYXRhX2V0cyA8LSBkYXRhLmZyYW1lKA0KICBEYXRlID0gYyh0cmFpbl9kYXRhJGRhdGUsIHRlc3RfZGF0YSRkYXRlKSwNCiAgQWN0dWFsID0gYyh0cmFpbl9kYXRhJHZhbHVlLCB0ZXN0X2RhdGEkdmFsdWUpLA0KICBGb3JlY2FzdCA9IGMocmVwKE5BLCBucm93KHRyYWluX2RhdGEpKSwgYXMubnVtZXJpYyhmb3JlY2FzdF9ldHMkbWVhbikpDQopDQoNCmdncGxvdChmb3JlY2FzdF9wbG90X2RhdGFfZXRzLCBhZXMoeCA9IERhdGUpKSArDQogIGdlb21fbGluZShhZXMoeSA9IEFjdHVhbCwgY29sb3IgPSAiQWN0dWFsIFZhbHVlcyIpLCBzaXplID0gMSkgKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBGb3JlY2FzdCwgY29sb3IgPSAiRm9yZWNhc3RlZCBWYWx1ZXMiKSwgc2l6ZSA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2d0aXRsZSgiRVRTIE1vZGVsOiBGb3JlY2FzdCB2cyBUZXN0IERhdGEiKSArDQogIHhsYWIoIkRhdGUiKSArDQogIHlsYWIoIlRvdGFsIFByaW1hcnkgT2lsIFByb2R1Y3RzIEltcG9ydHNcbihUaG91c2FuZCBCYXJyZWxzIHBlciBNb250aCkiKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJBY3R1YWwgVmFsdWVzIiA9ICJibHVlIiwgIkZvcmVjYXN0ZWQgVmFsdWVzIiA9ICJyZWQiKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0Kcm1zZV9ldHMgPC0gc3FydChtZWFuKCh0ZXN0X2RhdGEkdmFsdWUgLSBhcy5udW1lcmljKGZvcmVjYXN0X2V0cyRtZWFuKSleMikpDQpjYXQoIkVUUyBNb2RlbCBSTVNFOiIsIHJvdW5kKHJtc2VfZXRzLCAyKSwgIlxuIikNCg0KY2F0KCJNb2RlbCBSTVNFIENvbXBhcmlzb246XG4iKQ0KY2F0KCJBUklNQSBSTVNFOiIsIHJvdW5kKHJtc2VfYXJpbWEsIDIpLCAiXG4iKQ0KY2F0KCJFVFMgUk1TRToiLCByb3VuZChybXNlX2V0cywgMiksICJcbiIpDQoNCmJlc3RfbW9kZWwgPC0gaWZlbHNlKHJtc2VfYXJpbWEgPCBybXNlX2V0cywgIkFSSU1BIiwgIkVUUyIpDQpjYXQoIlRoZSBiZXR0ZXIgbW9kZWwgYmFzZWQgb24gUk1TRSBpczoiLCBiZXN0X21vZGVsLCAiXG4iKQ0KDQppZiAoYmVzdF9tb2RlbCA9PSAiQVJJTUEiKSB7DQogIGZvcmVjYXN0XzFfeWVhciA8LSBmb3JlY2FzdChhcmltYV9tb2RlbCwgaCA9IDEyKQ0KfSBlbHNlIHsNCiAgZm9yZWNhc3RfMV95ZWFyIDwtIGZvcmVjYXN0KGV0c19tb2RlbCwgaCA9IDEyKQ0KfQ0KDQpmaXJzdF9tb250aF9mb3JlY2FzdCA8LSBmb3JlY2FzdF8xX3llYXIkbWVhblsxXQ0KY2F0KCJGaXJzdCBtb250aCdzIGZvcmVjYXN0ZWQgdmFsdWU6Iiwgcm91bmQoZmlyc3RfbW9udGhfZm9yZWNhc3QsIDIpLCAiXG4iKQ0KDQphdXRvcGxvdChmb3JlY2FzdF8xX3llYXIpICsNCiAgZ2d0aXRsZShwYXN0ZShiZXN0X21vZGVsLCAiTW9kZWw6IDEyLU1vbnRoIEZvcmVjYXN0IikpICsNCiAgeGxhYigiRGF0ZSIpICsNCiAgeWxhYigiVG90YWwgUHJpbWFyeSBPaWwgUHJvZHVjdHMgSW1wb3J0c1xuKFRob3VzYW5kIEJhcnJlbHMgcGVyIE1vbnRoKSIpDQoNCmBgYA0KDQoNCiMjIyAxYi4gDQpXcml0ZSBvbmUgcGFyYWdyYXBoIChtYXggMjUwIHdvcmRzKSBiZWxvdyB0byBleHBsYWluIGFsbCB0aGUgcmVzdWx0cyBmcm9tIHlvdXIgY29tbWFuZHMuIE1ha2Ugc3VyZSB5b3UgZXhwbGFpbiB0aGUgc3RlcHMgeW91IGRpZCBhbmQgdGhlIHJlc3VsdHMgeW91IG9idGFpbmVkIHdpdGggeW91ciBvd24gd29yZHMuDQoNCkFuc3dlcjogQXVzdHJhbGlh4oCZcyBtb250aGx5IG9pbCBpbXBvcnRzIGZyb20gMjAwMCB0byAyMDI1IHJldmVhbCBhIGNvbXBlbGxpbmcgZGF0YS1kcml2ZW4gbmFycmF0aXZlIG9mIHNlYXNvbmFsIGZsdWN0dWF0aW9ucyBhbmQgYW4gdXB3YXJkIHRyZW5kLiBJbml0aWFsIGFuYWx5c2lzIHNob3dlZCByZWN1cnJpbmcgcGVha3MgYW5kIHRyb3VnaHMsIHN1Z2dlc3RpbmcgY3ljbGljYWwgY2hhbmdlcyBkcml2ZW4gYnkgZmFjdG9ycyBsaWtlIGRlbWFuZCBvciBleHRlcm5hbCBtYXJrZXQgY29uZGl0aW9ucy4gVG8gZGVsdmUgZGVlcGVyLCBJIHNwbGl0IHRoZSBkYXRhIGludG8gdHJhaW5pbmcgKDgwJSkgYW5kIHRlc3RpbmcgKDIwJSkgc2V0cyBhbmQgdGVzdGVkIHR3byBmb3JlY2FzdGluZyBtb2RlbHM6IEFSSU1BIGFuZCBFVFMuDQoNCkJvdGggbW9kZWxzIGVmZmVjdGl2ZWx5IGNhcHR1cmVkIHRoZSBwYXR0ZXJucywgYnV0IEFSSU1BICgwLDEsMSkgcGVyZm9ybWVkIHNsaWdodGx5IGJldHRlciB3aXRoIGEgcm9vdCBtZWFuIHNxdWFyZSBlcnJvciAoUk1TRSkgb2YgMTE3LjMyLCBjb21wYXJlZCB0byAxMTcuNTQgZm9yIEVUUyAoQSxBLE4pLiBSTVNFIG1lYXN1cmVzIHRoZSBhdmVyYWdlIG1hZ25pdHVkZSBvZiBlcnJvciBiZXR3ZWVuIHByZWRpY3RlZCBhbmQgYWN0dWFsIHZhbHVlcywgd2l0aCBsb3dlciB2YWx1ZXMgaW5kaWNhdGluZyBhIG1vcmUgYWNjdXJhdGUgbW9kZWwuIERpYWdub3N0aWMgY2hlY2tzIHN1cHBvcnRlZCB0aGlzIHJlc3VsdCwgd2l0aCBBUklNQSByZXNpZHVhbHMgcmFuZG9tbHkgZGlzdHJpYnV0ZWQgYXJvdW5kIHplcm8sIGEgbmVhci1ub3JtYWwgaGlzdG9ncmFtLCBhbmQgbm8gc2lnbmlmaWNhbnQgYXV0b2NvcnJlbGF0aW9uLCBjb25maXJtaW5nIHRoYXQgdGhlIG1vZGVsIGNhcHR1cmVkIHRoZSBkYXRh4oCZcyBzdHJ1Y3R1cmUgd2VsbC4gV2hpbGUgdGhlIEVUUyBtb2RlbCBzaG93ZWQgc2ltaWxhciByZXNpZHVhbCBkaWFnbm9zdGljcywgaXRzIHNsaWdodGx5IGhpZ2hlciBSTVNFIG1hZGUgQVJJTUEgdGhlIG1vcmUgYWNjdXJhdGUgb3B0aW9uLg0KDQpVc2luZyB0aGUgQVJJTUEgbW9kZWwsIEkgZm9yZWNhc3RlZCBvaWwgaW1wb3J0cyBmb3IgdGhlIG5leHQgMTIgbW9udGhzLCB3aXRoIHRoZSBmaXJzdCBtb250aOKAmXMgcHJlZGljdGVkIHZhbHVlIGF0IGFwcHJveGltYXRlbHkgNjU0LjE3IHRob3VzYW5kIGJhcnJlbHMuIFRoaXMgYW5hbHlzaXMgaGlnaGxpZ2h0cyB0aGUgc2Vhc29uYWwgbmF0dXJlIG9mIG9pbCBpbXBvcnRzLCB3aXRoIHByZWRpY3RhYmxlIGN5Y2xpY2FsIHBhdHRlcm5zIGFuZCB1cHdhcmQgbW9tZW50dW0gb3ZlciB0aW1lLiBCeSB1bmRlcnN0YW5kaW5nIHRoZXNlIHRyZW5kcywgcG9saWN5bWFrZXJzIGFuZCBzdGFrZWhvbGRlcnMgY2FuIG1ha2UgaW5mb3JtZWQgZGVjaXNpb25zLCBhbnRpY2lwYXRlIGZsdWN0dWF0aW9ucywgYW5kIHBsYW4gZWZmZWN0aXZlbHkgZm9yIGZ1dHVyZSBkZW1hbmQuIFRoaXMgZm9yZWNhc3QgcHJvdmlkZXMgYSByZWxpYWJsZSBmb3VuZGF0aW9uIGZvciBhZGRyZXNzaW5nIGNoYWxsZW5nZXMgYW5kIG9wcG9ydHVuaXRpZXMgaW4gdGhlIGV2b2x2aW5nIGVuZXJneSBtYXJrZXQsIGVtcGhhc2l6aW5nIHRoZSB2YWx1ZSBvZiBkYXRhLWRyaXZlbiBpbnNpZ2h0cyBpbiBzaGFwaW5nIHN1c3RhaW5hYmxlIGFuZCBlZmZpY2llbnQgc3RyYXRlZ2llcyBmb3IgdGhlIGZ1dHVyZS4NCg0KDQojIyAyLgkNCg0KLSBPYnRhaW4gQ29uc3VtZXIgUHJpY2UgSW5kZXggKENQSSkgZnJvbSBlaXRoZXIgUXVhbmRsIG9yIEZSRUQuIA0KDQotIEJ5IHVzaW5nIHBkZmV0Y2ggb3IgcXVhbnRtb2QgcGFja2FnZXMsIG9idGFpbiAibW9udGhseSIgUyZQIDUwMCBQcmljZSBJbmRleC4gKG9yIHRyYW5zZm9ybSB0aGUgc2VyaWVzIHRvIG1vbnRobHkgYnkgY29sbGFwc2UoKSBjb21tYW5kKQ0KDQotIENvbWJpbmUgdGhlc2UgdHdvIHNlcmllcyB0b2dldGhlciBtb250aGx5LCBzdGFydCBhbmQgZW5kIGRhdGVzIHNob3VsZCBiZSB0aGUgc2FtZS4gKFlvdSBjYW4gdXNlIEV4Y2VsIGZpbGUgaWYgaXQgaXMgZWFzaWVyLCB0aGVuIGltcG9ydCB0byBSKQ0KDQotIENhbGN1bGF0ZSB0aGUgcmVhbCB2YWx1ZSBvZiBTICZQIDUwMCBQcmljZSBJbmRleC4NCg0KLSBDb21wYXJlIHRoZSBub21pbmFsIHZhbHVlIGFuZCByZWFsIHZhbHVlIG9mIHRoZSBpbmRleC4gUGxvdCB0aGVtIHRvZ2V0aGVyIHRvIHNlZSB2aXN1YWxseS4gSXMgdGhlcmUgYW55IHNpZ25pZmljYW50IGRpZmZlcmVuY2UgeW91IGNhbiBzZWUgdmlzdWFsbHk/IEV4cGxhaW4gd2h5Lg0KDQotIEZvcmVjYXN0IHRoZSByZWFsIHZhbHVlIG9mIFMmUCA1MDAgUHJpY2UgSW5kZXggZm9yIHRoZSBuZXh0IDYgbW9udGhzLg0KDQotIFdvdWxkIHlvdSBwcmVmZXIgdG8gaW52ZXN0IGluIHRoZSBpbmRleCBiYXNlZCBvbiB0aGUgZm9yZWNhc3RlZCB2YWx1ZT8gDQoNCi0gQ2FsY3VsYXRlIHRoZSByZXR1cm4gb2Ygbm9taW5hbCBTJlAgNTAwIFByaWNlIEluZGV4LiBQbG90IHRoZSByZXR1cm4gYW5kIGNvbW1hbmQgb24gd2hhdCB5b3Ugc2VlIHZpc3VhbGx5LiBEb2VzIGl0IGxvb2sgc3RhdGlvbmFyeT8gRm9yZWNhc3QgZm9yIHRoZSBuZXh0IDYgbW9udGggYWhlYWQgYnkgYW55IG1vZGVsIHlvdSB3b3VsZCBjaG9vc2U/IEV4cGxhaW4gdGhlIGRhdGEgYW5kIGZvcmVjYXN0ZWQgdmFsdWVzLg0KDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCiMgMmEuQW5zd2VyOiBUeXBlIHlvdSBhbGwgeW91ciBjb2RlcyBoZXJlLCBvbmx5Lg0KDQpjcGkgPC0gcGRmZXRjaF9GUkVEKCJDUElBVUNTTCIpDQpjcGkgPC0gZGF0YS5mcmFtZShEYXRlID0gaW5kZXgoY3BpKSwgQ1BJID0gY29yZWRhdGEoY3BpKSkNCg0KZ2V0U3ltYm9scygiXkdTUEMiLCBzcmMgPSAieWFob28iLCBmcm9tID0gIjIwMTAtMDEtMDEiKQ0KDQpzcDUwMCA8LSB0by5tb250aGx5KEdTUEMsIGluZGV4QXQgPSAibGFzdG9mIiwgT0hMQyA9IEZBTFNFKQ0Kc3A1MDAgPC0gZGF0YS5mcmFtZShEYXRlID0gaW5kZXgoc3A1MDApLCBTUDUwMCA9IGNvcmVkYXRhKHNwNTAwKVssICJHU1BDLkNsb3NlIl0pDQoNCmNvbWJpbmVkX2RhdGEgPC0gbWVyZ2UoY3BpLCBzcDUwMCwgYnkgPSAiRGF0ZSIsIGFsbCA9IEZBTFNFKQ0KDQpjb21iaW5lZF9kYXRhIDwtIGNvbWJpbmVkX2RhdGEgJT4lDQogIG11dGF0ZShSZWFsX1NQNTAwID0gU1A1MDAgLyAoQ1BJQVVDU0wgLyBDUElBVUNTTFsxXSkpDQoNCmdncGxvdChjb21iaW5lZF9kYXRhLCBhZXMoeCA9IERhdGUpKSArDQogIGdlb21fbGluZShhZXMoeSA9IFNQNTAwLCBjb2xvciA9ICJOb21pbmFsIikpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gUmVhbF9TUDUwMCwgY29sb3IgPSAiUmVhbCIpKSArDQogIGxhYnModGl0bGUgPSAiTm9taW5hbCB2cyBSZWFsIFMmUCA1MDAgUHJpY2UgSW5kZXgiLA0KICAgICAgIHkgPSAiSW5kZXggVmFsdWUiLA0KICAgICAgIGNvbG9yID0gIkxlZ2VuZCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCnJlYWxfc3A1MDBfdHMgPC0gdHMoY29tYmluZWRfZGF0YSRSZWFsX1NQNTAwLCBzdGFydCA9IGMoMjAxMCwgMSksIGZyZXF1ZW5jeSA9IDEyKQ0KcmVhbF9zcDUwMF9mb3JlY2FzdCA8LSBmb3JlY2FzdChhdXRvLmFyaW1hKHJlYWxfc3A1MDBfdHMpLCBoID0gNikNCg0KYXV0b3Bsb3QocmVhbF9zcDUwMF9mb3JlY2FzdCkgKw0KICBsYWJzKHRpdGxlID0gIkZvcmVjYXN0IG9mIFJlYWwgUyZQIDUwMCBQcmljZSBJbmRleCIsDQogICAgICAgeSA9ICJSZWFsIFMmUCA1MDAgVmFsdWUiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpwcmludChyZWFsX3NwNTAwX2ZvcmVjYXN0KQ0KDQpjb21iaW5lZF9kYXRhIDwtIGNvbWJpbmVkX2RhdGEgJT4lDQogIG11dGF0ZShSZXR1cm5zID0gYyhOQSwgZGlmZihsb2coU1A1MDApKSkpDQoNCmdncGxvdChjb21iaW5lZF9kYXRhLCBhZXMoeCA9IERhdGUsIHkgPSBSZXR1cm5zKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiUmV0dXJucyBvZiBOb21pbmFsIFMmUCA1MDAgUHJpY2UgSW5kZXgiLA0KICAgICAgIHkgPSAiUmV0dXJucyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCg0KcmV0dXJuc190cyA8LSB0cyhuYS5vbWl0KGNvbWJpbmVkX2RhdGEkUmV0dXJucyksIHN0YXJ0ID0gYygyMDEwLCAxKSwgZnJlcXVlbmN5ID0gMTIpDQpyZXR1cm5zX21vZGVsIDwtIGF1dG8uYXJpbWEocmV0dXJuc190cykNCmNoZWNrcmVzaWR1YWxzKHJldHVybnNfbW9kZWwpDQpyZXR1cm5zX2ZvcmVjYXN0IDwtIGZvcmVjYXN0KGF1dG8uYXJpbWEocmV0dXJuc190cyksIGggPSA2KQ0KDQojIFBsb3QgZm9yZWNhc3RlZCByZXR1cm5zDQphdXRvcGxvdChyZXR1cm5zX2ZvcmVjYXN0KSArDQogIGxhYnModGl0bGUgPSAiRm9yZWNhc3Qgb2YgUyZQIDUwMCBSZXR1cm5zIiwNCiAgICAgICB5ID0gIlJldHVybnMiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpwcmludChyZXR1cm5zX2ZvcmVjYXN0KQ0KDQpgYGANCg0KDQojIyMgMmIuDQpXcml0ZSBvbmUgcGFyYWdyYXBoIChtYXggMjUwIHdvcmRzKSBiZWxvdyB0byBleHBsYWluIGFsbCB0aGUgY29tbWFuZHMgYW5kIHRoZWlyIHJlc3VsdHMuIE1ha2Ugc3VyZSB5b3UgZXhwbGFpbiB0aGUgc3RlcHMgeW91IGRpZCBhbmQgdGhlIHJlc3VsdHMgeW91IG9idGFpbmVkIHdpdGggeW91ciBvd24gd29yZHMuDQoNCkFuc3dlcjogSSBiZWdhbiBieSByZXRyaWV2aW5nIHRoZSBDb25zdW1lciBQcmljZSBJbmRleCAoQ1BJKSBmcm9tIEZSRUQgYW5kIHRoZSBtb250aGx5IFMmUCA1MDAgUHJpY2UgSW5kZXggZnJvbSBZYWhvbyBGaW5hbmNlLiBBbGlnbmluZyB0aGUgZGF0YXNldHMgYnkgZGF0ZSwgSSBjYWxjdWxhdGVkIHRoZSByZWFsIFMmUCA1MDAgUHJpY2UgSW5kZXggYnkgYWRqdXN0aW5nIG5vbWluYWwgdmFsdWVzIGZvciBpbmZsYXRpb24uIFRoZSAiTm9taW5hbCB2cyBSZWFsIFMmUCA1MDAgUHJpY2UgSW5kZXgiIGdyYXBoIHJldmVhbGVkIGEgc3RyaWtpbmcgZGl2ZXJnZW5jZTogbm9taW5hbCB2YWx1ZXMgY29uc2lzdGVudGx5IGV4Y2VlZGVkIHJlYWwgdmFsdWVzIGR1ZSB0byBpbmZsYXRpb24sIGVtcGhhc2l6aW5nIHRoZSBpbXBvcnRhbmNlIG9mIHVzaW5nIHJlYWwgZGF0YSBmb3IgYWNjdXJhdGUgYW5hbHlzaXMuIEZvcmVjYXN0aW5nIHRoZSByZWFsIGluZGV4IHVzaW5nIGFuIEFSSU1BIG1vZGVsLCBJIHZpc3VhbGl6ZWQgdGhlIHJlc3VsdHMgaW4gdGhlICJGb3JlY2FzdCBvZiBSZWFsIFMmUCA1MDAgUHJpY2UgSW5kZXgiIGdyYXBoLCB3aGljaCBwcm9qZWN0ZWQgYSBzdGVhZHkgdXB3YXJkIHRyZW5kIG92ZXIgdGhlIG5leHQgc2l4IG1vbnRocywgc2lnbmFsaW5nIGNvbnRpbnVlZCBncm93dGggZXZlbiBhZnRlciBpbmZsYXRpb24gYWRqdXN0bWVudHMuDQoNClR1cm5pbmcgdG8gcmV0dXJucywgSSBjb21wdXRlZCB0aGUgbG9nYXJpdGhtaWMgcmV0dXJucyBvZiB0aGUgbm9taW5hbCBpbmRleC4gVGhlICJSZXR1cm5zIG9mIE5vbWluYWwgUyZQIDUwMCBQcmljZSBJbmRleCIgZ3JhcGggaWxsdXN0cmF0ZWQgaGlzdG9yaWNhbCBmbHVjdHVhdGlvbnMgd2l0aCBwZXJpb2RzIG9mIGhlaWdodGVuZWQgdm9sYXRpbGl0eS4gVXNpbmcgYW4gQVJJTUEgbW9kZWwsIEkgZm9yZWNhc3RlZCByZXR1cm5zIGZvciB0aGUgbmV4dCBzaXggbW9udGhzLCBhcyBzaG93biBpbiB0aGUgIkZvcmVjYXN0IG9mIFMmUCA1MDAgUmV0dXJucyIgZ3JhcGgsIHdoaWNoIHByZWRpY3RlZCBtaWxkIHZhcmlhYmlsaXR5IHdpdGggbm8gZXh0cmVtZSBtb3ZlbWVudHMuIFRvIHZhbGlkYXRlIG15IG1vZGVsLCBJIGFuYWx5emVkIHJlc2lkdWFscyB0aHJvdWdoIHRoZSAiUmVzaWR1YWwgRGlhZ25vc3RpY3MgZnJvbSBBUklNQSBNb2RlbCIgZ3JhcGgsIGNvbmZpcm1pbmcgc3RhdGlvbmFyaXR5IHdpdGggdW5jb3JyZWxhdGVkLCB3aGl0ZS1ub2lzZSByZXNpZHVhbHMgKHAtdmFsdWUgPSAwLjE2NDMpLg0KDQpUaGVzZSBhbmFseXNlcyB0ZWxsIGEgY29tcGVsbGluZyBzdG9yeS4gSW5mbGF0aW9uIHNpZ25pZmljYW50bHkgaW1wYWN0cyBub21pbmFsIHZhbHVlcywgdW5kZXJzY29yaW5nIHRoZSBpbXBvcnRhbmNlIG9mIHJlYWwgYWRqdXN0bWVudHMuIFdpdGggZm9yZWNhc3RzIHNob3dpbmcgc3RhYmxlIGdyb3d0aCBhbmQgbW9kZXJhdGUgcmV0dXJucywgdGhlIG91dGxvb2sgYXBwZWFycyBmYXZvcmFibGUgZm9yIGludmVzdG1lbnQsIHN1cHBvcnRlZCBieSBhIHJvYnVzdCwgd2VsbC12YWxpZGF0ZWQgbW9kZWwgdGhhdCBjYXB0dXJlcyBib3RoIGhpc3RvcmljYWwgYW5kIGZ1dHVyZSB0cmVuZHMgZWZmZWN0aXZlbHkNCg0KDQojIyAzLgkNCg0KLSBVc2UgdGhlIG1vbnRobHkgUyZQIDUwMCBQcmljZSBJbmRleCB5b3Ugb2J0YWluZWQgaW4gIzIuIA0KDQotIE1ha2Ugc3VyZSB5b3UgY3JlYXRlIGFuZCB1c2UgYSB0c2liYmxlIGRhdGEgc2V0LiANCg0KLSBNYWtlIGEgdmlzdWFsIGRlY2lzaW9uIGJ5IHVzaW5nIG5lY2Vzc2FyeSBwbG90cyB0byBzZWUgd2hldGhlciBvciBub3QgRGVjZW1iZXIgaXMgdGhlIGhpZ2hlc3QgaW5kZXggdmFsdWUgb24gYXZlcmFnZSBhbW9uZyBvdGhlciBtb250aHMuIFdoYXQgZG8geW91IHNlZT8gQ29tbWVudC4gDQoNCi0gUnVuIGEgcmVncmVzc2lvbiBtb2RlbCB0aGF0IGNvbnNpZGVycyBzZWFzb25hbGl0eSBpbiB0aGUgZGF0YSAoeW91IGNhbiBhZGQgdHJlbmQgaWYgeW91IHRoaW5rIGl0IGlzIG5lY2Vzc2FyeSkgdG8gZXhwbGFpbiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIGluZGV4LiANCg0KLSBGaW5kIG91dCB3aGljaCBtb250aCBoYXMgdGhlIGhpZ2hlc3QgcHJlZGljdGl2ZSBwb3dlciBvbiB0aGUgaW5kZXggaW4gdGhlIGVzdGltYXRlZCBtb2RlbC4gSXMgdGhlIGNvZWZmaWNpZW50IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYXQgNSU/IA0KDQotIElzIHlvdXIgbW9kZWwgYXMgYSB3aG9sZSBzaWduaWZpY2FudCBhdCA1JS4gQ29tbWVudCBvbiB0aGUgZXN0aW1hdGVkIG1vZGVsLg0KDQotIFdoaWNoIG1vbnRoIHdvdWxkIHlvdSBpbnZlc3Qgb24gUyZQIDUwMCBQcmljZSBJbmRleCBFVEYncywgaWYgeW91IHdvdWxkPyBtYWtlIGFzIGFuIGludmVzdG1lbnQgc3RhdGVtZW50IGFuZCBjb252aW5jZSB5b3VyIGF1ZGllbmNlIHRvIGludmVzdCBpbiBzcGVjaWZpYyBtb250aCBvciBub3QuDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQoNCiMgM2EuQW5zd2VyOiBUeXBlIGFsbCB5b3VyIGNvZGVzIGhlcmUsIG9ubHkuDQoNCnNwNTAwIDwtIHNwNTAwICU+JQ0KICBtdXRhdGUoDQogICAgTW9udGggPSBtb250aChEYXRlLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBGQUxTRSksDQogICAgWWVhciA9IHllYXIoRGF0ZSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICApDQoNCmF2ZXJhZ2VfbW9udGhseSA8LSBzcDUwMCAlPiUNCiAgZ3JvdXBfYnkoTW9udGgpICU+JQ0KICBzdW1tYXJpemUoQXZlcmFnZV9TUDUwMCA9IG1lYW4oU1A1MDApKQ0KDQpnZ3Bsb3QoYXZlcmFnZV9tb250aGx5LCBhZXMoeCA9IE1vbnRoLCB5ID0gQXZlcmFnZV9TUDUwMCwgZmlsbCA9IE1vbnRoKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkF2ZXJhZ2UgUyZQIDUwMCBJbmRleCBWYWx1ZSBieSBNb250aCIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJBdmVyYWdlIEluZGV4IFZhbHVlIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCnNwNTAwJE1vbnRoIDwtIGZhY3RvcihzcDUwMCRNb250aCwgbGV2ZWxzID0gbW9udGgubmFtZSkNCg0KbW9kZWwgPC0gbG0oU1A1MDAgfiBNb250aCwgZGF0YSA9IHNwNTAwKQ0Kc3VtbWFyeShtb2RlbCkNCg0KYW5vdmFfc3VtbWFyeSA8LSBhbm92YShtb2RlbCkNCmFub3ZhX3N1bW1hcnkNCg0KaWYgKGFub3ZhX3N1bW1hcnkkYFByKD5GKWBbMV0gPCAwLjA1KSB7DQogIGNhdCgiVGhlIG1vZGVsIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgYXQgdGhlIDUlIGxldmVsLlxuIikNCn0gZWxzZSB7DQogIGNhdCgiVGhlIG1vZGVsIGlzIE5PVCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGF0IHRoZSA1JSBsZXZlbC5cbiIpDQp9DQoNCmBgYA0KDQojIyMgM2IuIA0KV3JpdGUgYSBwYXJhZ3JhcGggKG1heCAyNTAgd29yZHMpIGJlbG93IHRvIGV4cGxhaW4gYWxsIHRoZSBjb21tYW5kcyBhbmQgdGhlaXIgcmVzdWx0cy4gTWFrZSBzdXJlIHlvdSBleHBsYWluIHRoZSBzdGVwcyB5b3UgZGlkIGFuZCB0aGUgcmVzdWx0cyB5b3Ugb2J0YWluZWQgd2l0aCB5b3VyIG93biB3b3Jkcy4NCg0KQW5zd2VyOiBJbiB0aGlzIGFuYWx5c2lzLCBJIGV4YW1pbmVkIHNlYXNvbmFsIHBhdHRlcm5zIGluIHRoZSBTJlAgNTAwIGluZGV4IHRvIGlkZW50aWZ5IHRoZSBiZXN0LXBlcmZvcm1pbmcgbW9udGggYW5kIGFzc2VzcyBpdHMgcG90ZW50aWFsIHJlbGV2YW5jZSBmb3IgaW52ZXN0bWVudCBzdHJhdGVnaWVzLiBJIGJlZ2FuIGJ5IGNhbGN1bGF0aW5nIHRoZSBhdmVyYWdlIGluZGV4IHZhbHVlIGZvciBlYWNoIG1vbnRoIGFuZCB2aXN1YWxpemluZyB0aGUgcmVzdWx0cyBpbiBhIGJhciBwbG90LiBUaGUgYW5hbHlzaXMgcmV2ZWFsZWQgdGhhdCBEZWNlbWJlciBoYWQgdGhlIGhpZ2hlc3QgYXZlcmFnZSBpbmRleCB2YWx1ZSBhdCAyOTE3LjM4NSwgY2xvc2VseSBmb2xsb3dlZCBieSBOb3ZlbWJlciBhdCAyODk0LjI2Mi4gVGhlc2UgZmluZGluZ3Mgc3VnZ2VzdGVkIGEgcG90ZW50aWFsIHNlYXNvbmFsIHRyZW5kIGZhdm9yaW5nIHRoZSBlbmQtb2YteWVhciBtb250aHMsIHBhcnRpY3VsYXJseSBEZWNlbWJlciwgYXMgaGlzdG9yaWNhbGx5IHN0cm9uZyBwZXJmb3JtZXJzLg0KDQpUbyBmdXJ0aGVyIGludmVzdGlnYXRlLCBJIGNvbnN0cnVjdGVkIGEgcmVncmVzc2lvbiBtb2RlbCB1c2luZyBtb250aHMgYXMgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRvIGV2YWx1YXRlIHdoZXRoZXIgdGhlc2Ugb2JzZXJ2ZWQgZGlmZmVyZW5jZXMgd2VyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBUaGUgbW9kZWwgYWltZWQgdG8gcXVhbnRpZnkgdGhlIGNvbnRyaWJ1dGlvbiBvZiBlYWNoIG1vbnRoIHRvIHRoZSB2YXJpYWJpbGl0eSBpbiB0aGUgUyZQIDUwMCBpbmRleC4gSG93ZXZlciwgdGhlIHJlc3VsdHMgaW5kaWNhdGVkIGEgdmVyeSBsb3cgUi1zcXVhcmVkIHZhbHVlICgwLjAwNCksIGRlbW9uc3RyYXRpbmcgdGhhdCBtb250aGx5IHNlYXNvbmFsaXR5IGFjY291bnRlZCBmb3IgYWxtb3N0IG5vbmUgb2YgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBpbmRleC4gQWRkaXRpb25hbGx5LCBub25lIG9mIHRoZSBtb250aHMsIGluY2x1ZGluZyBEZWNlbWJlciwgaGFkIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgY29lZmZpY2llbnRzIGF0IHRoZSA1JSBsZXZlbC4gQW4gQU5PVkEgdGVzdCBmdXJ0aGVyIGNvbmZpcm1lZCB0aGF0IHRoZSBtb2RlbCBhcyBhIHdob2xlIHdhcyBub3Qgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCAoRi1zdGF0aXN0aWMgcC12YWx1ZSA9IDEuMDApLg0KDQpUaGVzZSBmaW5kaW5ncyBzdWdnZXN0IHRoYXQgd2hpbGUgRGVjZW1iZXIgaGFzIGhpc3RvcmljYWxseSBiZWVuIHRoZSBiZXN0LXBlcmZvcm1pbmcgbW9udGggaW4gdGVybXMgb2YgYXZlcmFnZSBpbmRleCB2YWx1ZSwgdGhlIGxhY2sgb2Ygc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIGltcGxpZXMgdGhhdCBtb250aGx5IHNlYXNvbmFsaXR5IGFsb25lIGlzIG5vdCBhIHJlbGlhYmxlIHByZWRpY3RvciBvZiBTJlAgNTAwIHBlcmZvcm1hbmNlLiBGb3IgYSByb2J1c3QgaW52ZXN0bWVudCBzdHJhdGVneSwgaXQgaXMgZXNzZW50aWFsIHRvIGluY29ycG9yYXRlIGJyb2FkZXIgbWFya2V0IHRyZW5kcywgbWFjcm9lY29ub21pYyBmYWN0b3JzLCBhbmQgb3RoZXIgcHJlZGljdGl2ZSBpbmRpY2F0b3JzIGFsb25nc2lkZSBoaXN0b3JpY2FsIHBhdHRlcm5zLg0K