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.
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()

## 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