5.1
Produce forecasts for the following series using whichever of
NAIVE(y), SNAIVE(y) or
RW(y ~ drift()) is more appropriate in each case:
i
Australian Population (global_economy)
df_aus <- global_economy %>%
filter(Country == "Australia")
head(df_aus)
aus_plot1 <- df_aus%>%
autoplot(Population)+
labs(title= "Australian Population")+
annotate("text", x = Inf, y = -Inf, hjust = 1, vjust = -1,
label = "There is an upward trend")
aus_fit <- df_aus %>%
# no filter needed
model(RW(Population ~ drift()))
aus_fc <- aus_fit %>%
forecast(h = 10)
aus_plot2<- aus_fc %>%
autoplot(df_aus)
plot_grid(aus_plot1, aus_plot2, ncol = 2)

Alternatively the plotting can be done with one function
df_aus%>%
model(RW(Population ~ drift()))%>%
forecast(h = 10)%>%
autoplot(df_aus)

In section 5.2 Drift method is explained to “allow the forecasts to
increase or decrease over time”. Since the data did not show high
seasonality and was not economic or financial, I did not use
Naïve method and seasonal naïve. The example
shown also uses random walk forecast in conjunction with drift (refer
below)
bricks |> model(RW(Bricks ~ drift()))
ii
Bricks (aus_production)
head(aus_production)
brick_plot1<-aus_production%>%
autoplot(Bricks)+
labs(title= "Bricks Production")+
annotate("text", x = Inf, y = -Inf, hjust = 1, vjust = -1,
label = "There is obvious seasonality but not trend")
brick_plot2<-aus_production%>%
# Warning: Removed 20 rows containing missing values (`geom_line()`).
# therefore filter is added
filter(!is.na(Bricks))%>%
model(SNAIVE(Bricks~lag("year")))%>%
forecast(h = 10)%>%
autoplot(aus_production)+
# aus_production added twice to ensure visual has full plot
labs(title= "Bricks Production Forecast")
plot_grid(brick_plot1,brick_plot2, ncol = 1)
Warning: Removed 20 rows containing missing values (`geom_line()`).Warning: Removed 20 rows containing missing values (`geom_line()`).

There was obvious seasonality despite the fact that annually there
was no trend. Regardless SNAIVE(y) was used as it seemed to
be the best fit.
iii
NSW Lambs (aus_livestock)
cat(paste(unique(aus_livestock$State), collapse = "\n"))
Australian Capital Territory
New South Wales
Northern Territory
Queensland
South Australia
Tasmania
Victoria
Western Australia
df_lambs<-aus_livestock%>%
filter(State == "New South Wales", str_detect(Animal,"Lambs"))
head(df_lambs)
lambs_plot1 <- df_lambs%>%
autoplot()+
labs(title= "New South Wales Count")+
annotate("text", x = Inf, y = -Inf, hjust = 1, vjust = -1,
label = "There is no obvious seasonality or trend")
Plot variable not specified, automatically selected `.vars = Count`
lambs_plot2<-df_lambs%>%
model(NAIVE(Count))%>%
forecast(h = 10)%>%
autoplot(df_lambs)+
labs(title= "New South Wales Forecast")


No clear seasonality or trend so NAIVE(y) was most
appropriate
iv
Household wealth (hh_budget).
head(hh_budget)
wealth_plot1<-hh_budget%>%
autoplot(Wealth, show.legend= FALSE)+
facet_grid(Country~., scales = "free", space = "free_y")
wealth_plot2<- hh_budget%>%
model(RW(Wealth~drift()))%>%
forecast(h=5)%>%
autoplot(hh_budget)


I genuinely considered the NAIVE(y) model, because the
data was a time series regarding finance. I also debated the seasonal
aspect and considered SNAIVE(y), noting the dip in wealth
at certain intervals for certain countries. However, I think the trend
is primarily upward, with exception of years that tie in with the recent
recessions, therefore RW(y ~ drift()) or Drift method was
used.
v
Australian takeaway food turnover (aus_retail).
cat(paste(unique(aus_retail$State), collapse = "\n"))
Australian Capital Territory
New South Wales
Northern Territory
Queensland
South Australia
Tasmania
Victoria
Western Australia
head(aus_retail)
aus_retail%>%
filter(str_detect(Industry,"takeaway"))%>%
autoplot(Turnover)+
scale_color_discrete(name = "State", labels = unique(aus_retail$State))+
labs(title = "Turnover (Australian takeaway) by State")

aus_retail %>%
filter(str_detect(Industry,"takeaway")) %>%
model(RW(Turnover ~ drift())) %>%
forecast(h = 10) %>%
autoplot(aus_retail)+
facet_wrap(~State, scales = "free")

Just like the
Australian Population (`global_economy`)
The data for all states showed an upward trend. So modeling each
state using the Drift method made the mose sense.
5.2
Use the Facebook stock price (data set gafa_stock) to do
the following:
a.
Produce a time plot of the series.
cat(paste(unique(gafa_stock$Symbol), collapse = "\n"))
AAPL
AMZN
FB
GOOG
distinct(gafa_stock, year = lubridate::year(Date))
df_fb <- gafa_stock %>%
filter(Symbol == "FB")
head(df_fb)
# Re-index based on trading days
FB_stock <- df_fb %>%
# already filtered
mutate(day = row_number()) %>%
update_tsibble(index = day, regular = TRUE)
FB_stock%>%
autoplot(Close)+
labs(y = '$US', title = 'The Facebook Daily Closing Stock Price')

NA
NA
b.
Produce forecasts using the drift method and plot them.
AS PER Example: Google’s daily closing stock
price
# Filter the year of interest
FB_2015 <- FB_stock %>% filter(year(Date) == 2015)
# Fit the models
FB_fit <- FB_2015 |>
model(
Drift = NAIVE(Close ~ drift())
)
# Produce forecasts for the trading days in January 2016
FB_jan_2016 <- FB_stock |>
filter(yearmonth(Date) == yearmonth("2016 Jan"))
FB_fc <- FB_fit |>
forecast(new_data = FB_jan_2016)
# Plot the forecasts
FB_fc |>
autoplot(FB_2015, level = NULL) +
autolayer(FB_jan_2016, Close, colour = "black") +
labs(y = "$US",
title = "Facebook daily closing stock prices",
subtitle = "(Jan 2015 - Jan 2016)") +
guides(colour = guide_legend(title = "Forecast"))

c.
Show that the forecasts are identical to extending the line drawn
between the first and last observations.
FB_fc%>%
autoplot(FB_2015, level = NULL) +
geom_line(data = slice(FB_2015, range(cumsum(!is.na(Close)))),
aes(y=Close), linetype = 'dashed')

d.
Try using some of the other benchmark functions to forecast the same
data set. Which do you think is best? Why?
FB_fit2 <- FB_2015 %>%
model(
Mean = MEAN(Close),
Naive = NAIVE(Close)
)
# to make the forecasts for the trading days in January 2016
FB_jan_2016 <- FB_stock %>%
filter(yearmonth(Date) == yearmonth("2016 Jan"))
FB_fc2 <- FB_fit2 %>%
forecast(new_data = FB_jan_2016)
# Plotting
FB_fc2 %>%
autoplot(FB_2015, level = NULL) +
autolayer(FB_jan_2016, Close, colour = "green") +
labs(y = "$USD",
title = "FB Closing Stock Prices (Daily)",
subtitle = "(Jan 2015 - Jan 2016)") +
guides(colour = guide_legend(title = "The Forecast"))

Naive I believe is the most accurate, b/c there is no seasonality and
Naive works best with financial data according to the textbook
"This method works remarkably well for many economic and financial time series."
5.3
Apply a seasonal naïve method to the quarterly Australian beer
production data from 1992. Check if the residuals look like white noise,
and plot the forecasts. The following code will help.
# Extract data of interest
recent_production <- aus_production |>
filter(year(Quarter) >= 1992)
# Define and estimate a model
fit <- recent_production |> model(SNAIVE(Beer))
# Look at the residuals
fit |> gg_tsresiduals()

# Look a some forecasts
fit |> forecast() |> autoplot(recent_production)

What do you conclude?
I don’t believe this is white noise. This does not appear to be an
unpredictable sequence of numbers, rather it seems to have a constant
variance, and .resid that centers around 0. For the lag
value of 4 in the acf plot, it is larger than normal but also follows
the occurrence every 4 quarters. So the model can be improved, it is not
likely white noise and therefore seasonal naïve method is valid.
5.4
Repeat the previous exercise using the Australian Exports series from
global_economy and the Bricks series from
aus_production. Use whichever of NAIVE() or
SNAIVE() is more appropriate in each case.
** Note**: df_aus established in 5.1 for Australian
exports
i
Australian Exports series from
global_economy
# Define and estimate a model
df_aus_fit <- df_aus %>%
model(NAIVE(Exports))
# Look at the residuals
df_aus_fit %>%
gg_tsresiduals()

# Look at some forecasts
df_aus_fit %>%
forecast() %>% autoplot(df_aus)

aus_aug |> features(.innov, box_pierce, lag = 10)
aus_aug |> features(.innov, ljung_box, lag = 10)
NA
What do you conclude?
BP p-value of 0.1481135 and LB p-value of 0.08963678 suggests there’s
not significant autocorrelation, since they’re both greater than 0.05.
There’s also constant variation. I cannot differentiate this from white
noise and I’m unsure if the model could be improved.
ii
# Define and estimate a model
aus_prod_fit <- aus_production %>%
model(NAIVE(Bricks))
# Look at the residuals
aus_prod_fit %>%
gg_tsresiduals()

# Look at some forecasts
aus_prod_fit %>%
forecast() %>% autoplot(aus_production)

aus_prod_aug<-aus_prod_fit%>%
augment()
aus_prod_aug |> features(.innov, box_pierce, lag = 10)
aus_prod_aug |> features(.innov, ljung_box, lag = 10)
NA
The acf being consistently past the dotted line suggests significant
periodic autocorrelation and seasonality. The evidence of
autocorrelation is supported by the BP and LB statistic.
5.7
For your retail time series (from Exercise 7 in Section 2.10):
a.
Create a training dataset consisting of observations before 2011
using
set.seed(123)
myseries <- aus_retail %>%
filter(`Series ID` == sample(aus_retail$`Series ID`,1))
myseries_train <- myseries |>
filter(year(Month) < 2011)
b.
Check that your data have been split appropriately by producing the
following plot.
autoplot(myseries, Turnover) +
autolayer(myseries_train, Turnover, colour = "red")

c.
Fit a seasonal naïve model using SNAIVE() applied to your training
data (myseries_train).
fit <- myseries_train |>
model(SNAIVE(Turnover))
d.
Check the residuals.
fit |> gg_tsresiduals()

Do the residuals appear to be uncorrelated and normally
distributed?
The data appears to be correlated, but have a constant variation
changing from positive to negative at lag 10 indicating
heteroscedasticity. I also does not appear to be normally distributed or
center around 0.
e.
Produce forecasts for the test data
fc <- fit |>
forecast(new_data = anti_join(myseries, myseries_train))
Joining with `by = join_by(State, Industry, `Series ID`, Month, Turnover)`
fc |> autoplot(myseries)

f.
Compare the accuracy of your forecasts against the actual values.
fit |> accuracy()
fc |> accuracy(myseries)
NA
The forecast models does not perform well on the test data and the
errors for training seem smaller in comparison.
g.
How sensitive are the accuracy measures to the amount of training
data used?
The measures are highly sensitive to the quantity of data involved,
which may be negatively impacted the measures.
LS0tDQp0aXRsZTogJ0RBVEEgNjI0OiBQUkVESUNUSVZFIEFOQUxZVElDUyBIVzMnDQphdXRob3I6ICJHYWJyaWVsIENhbXBvcyINCmRhdGU6ICJMYXN0IGVkaXRlZCBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBnZW9tZXRyeTogbGVmdD0wLjVjbSxyaWdodD0wLjVjbSx0b3A9MWNtLGJvdHRvbT0yY20NCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgcGRmX2RvY3VtZW50Og0KICAgIGxhdGV4X2VuZ2luZTogeGVsYXRleA0KdXJsY29sb3I6IGJsdWUNCi0tLQ0KDQojIEluc3RydWN0aW9ucw0KDQpEbyBleGVyY2lzZXMgNS4xLCA1LjIsIDUuMywgNS40IGFuZCA1LjcgaW4gdGhlIEh5bmRtYW4gYm9vay4gIFBsZWFzZSBzdWJtaXQgeW91ciBbUnB1YnMgbGlua10oaHR0cHM6Ly9ycHVicy5jb20vZ2NhbXBvczEwMC9EQVRBXzYyNF9IVzMpIGFzIHdlbGwgYXMgeW91ciAucGRmIGZpbGUgc2hvd2luZyB5b3VyIHJ1biBjb2RlLg0KDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkoZnBwMykNCmxpYnJhcnkoY293cGxvdCkNCmBgYA0KDQojIDUuMQ0KDQpQcm9kdWNlIGZvcmVjYXN0cyBmb3IgdGhlIGZvbGxvd2luZyBzZXJpZXMgdXNpbmcgd2hpY2hldmVyIG9mIGBOQUlWRSh5KWAsIGBTTkFJVkUoeSlgIG9yIGBSVyh5IH4gZHJpZnQoKSlgIGlzIG1vcmUgYXBwcm9wcmlhdGUgaW4gZWFjaCBjYXNlOg0KDQojIyBpDQoNCkF1c3RyYWxpYW4gUG9wdWxhdGlvbiAoYGdsb2JhbF9lY29ub215YCkNCg0KYGBge3J9DQpkZl9hdXMgPC0gZ2xvYmFsX2Vjb25vbXkgJT4lDQogICAgICAgICAgICBmaWx0ZXIoQ291bnRyeSA9PSAiQXVzdHJhbGlhIikNCg0KaGVhZChkZl9hdXMpDQpgYGANCg0KYGBge3J9DQogYXVzX3Bsb3QxIDwtIGRmX2F1cyU+JQ0KICBhdXRvcGxvdChQb3B1bGF0aW9uKSsNCiAgbGFicyh0aXRsZT0gIkF1c3RyYWxpYW4gUG9wdWxhdGlvbiIpKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSBJbmYsIHkgPSAtSW5mLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEsDQogICAgICAgICAgIGxhYmVsID0gIlRoZXJlIGlzIGFuIHVwd2FyZCB0cmVuZCIpDQoNCmF1c19maXQgPC0gZGZfYXVzICU+JQ0KICAgICAgICAgICAgIyBubyBmaWx0ZXIgbmVlZGVkDQogICAgICAgICAgICBtb2RlbChSVyhQb3B1bGF0aW9uIH4gZHJpZnQoKSkpDQoNCmF1c19mYyA8LSBhdXNfZml0ICU+JQ0KICAgICAgICAgICAgZm9yZWNhc3QoaCA9IDEwKQ0KDQphdXNfcGxvdDI8LSBhdXNfZmMgJT4lIA0KICBhdXRvcGxvdChkZl9hdXMpDQoNCg0KDQpwbG90X2dyaWQoYXVzX3Bsb3QxLCBhdXNfcGxvdDIsIG5jb2wgPSAyKQ0KYGBgDQoNCkFsdGVybmF0aXZlbHkgdGhlIHBsb3R0aW5nIGNhbiBiZSBkb25lIHdpdGggb25lIGZ1bmN0aW9uIA0KDQpgYGB7cn0NCmRmX2F1cyU+JQ0KICBtb2RlbChSVyhQb3B1bGF0aW9uIH4gZHJpZnQoKSkpJT4lDQogICAgZm9yZWNhc3QoaCA9IDEwKSU+JQ0KICAgICAgYXV0b3Bsb3QoZGZfYXVzKQ0KYGBgDQoNCg0KSW4gc2VjdGlvbiA1LjIgRHJpZnQgbWV0aG9kIGlzIGV4cGxhaW5lZCB0byAiYWxsb3cgdGhlIGZvcmVjYXN0cyB0byBpbmNyZWFzZSBvciBkZWNyZWFzZSBvdmVyIHRpbWUiLiBTaW5jZSB0aGUgZGF0YSBkaWQgbm90IHNob3cgaGlnaCBzZWFzb25hbGl0eSBhbmQgd2FzIG5vdCBlY29ub21pYyBvciBmaW5hbmNpYWwsIEkgZGlkIG5vdCB1c2UgYE5hw692ZWAgbWV0aG9kIGFuZCBgc2Vhc29uYWwgbmHDr3ZlYC4gVGhlIGV4YW1wbGUgc2hvd24gYWxzbyB1c2VzIHJhbmRvbSB3YWxrIGZvcmVjYXN0IGluIGNvbmp1bmN0aW9uIHdpdGggZHJpZnQgKHJlZmVyIGJlbG93KQ0KDQogICAgICAgICAgICAgICAgICAgICAgICBicmlja3MgfD4gbW9kZWwoUlcoQnJpY2tzIH4gZHJpZnQoKSkpDQoNCiMjIGlpDQoNCkJyaWNrcyAoYGF1c19wcm9kdWN0aW9uYCkNCg0KYGBge3J9DQpoZWFkKGF1c19wcm9kdWN0aW9uKQ0KYGBgDQoNCg0KYGBge3J9DQpicmlja19wbG90MTwtYXVzX3Byb2R1Y3Rpb24lPiUNCiAgICAgICAgICAgICAgICBhdXRvcGxvdChCcmlja3MpKw0KICAgICAgICAgICAgICAgIGxhYnModGl0bGU9ICJCcmlja3MgUHJvZHVjdGlvbiIpKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSBJbmYsIHkgPSAtSW5mLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEsDQogICAgICAgICAgIGxhYmVsID0gIlRoZXJlIGlzIG9idmlvdXMgc2Vhc29uYWxpdHkgYnV0IG5vdCB0cmVuZCIpDQoNCmJyaWNrX3Bsb3QyPC1hdXNfcHJvZHVjdGlvbiU+JQ0KIyBXYXJuaW5nOiBSZW1vdmVkIDIwIHJvd3MgY29udGFpbmluZyBtaXNzaW5nIHZhbHVlcyAoYGdlb21fbGluZSgpYCkuDQojIHRoZXJlZm9yZSBmaWx0ZXIgaXMgYWRkZWQNCiAgZmlsdGVyKCFpcy5uYShCcmlja3MpKSU+JQ0KICBtb2RlbChTTkFJVkUoQnJpY2tzfmxhZygieWVhciIpKSklPiUNCiAgICBmb3JlY2FzdChoID0gMTApJT4lDQogICAgICBhdXRvcGxvdChhdXNfcHJvZHVjdGlvbikrDQojIGF1c19wcm9kdWN0aW9uIGFkZGVkIHR3aWNlIHRvIGVuc3VyZSB2aXN1YWwgaGFzIGZ1bGwgcGxvdA0KICBsYWJzKHRpdGxlPSAiQnJpY2tzIFByb2R1Y3Rpb24gRm9yZWNhc3QiKQ0KDQpwbG90X2dyaWQoYnJpY2tfcGxvdDEsYnJpY2tfcGxvdDIsIG5jb2wgPSAxKQ0KYGBgDQpUaGVyZSB3YXMgb2J2aW91cyBzZWFzb25hbGl0eSBkZXNwaXRlIHRoZSBmYWN0IHRoYXQgYW5udWFsbHkgdGhlcmUgd2FzIG5vIHRyZW5kLiBSZWdhcmRsZXNzIGBTTkFJVkUoeSlgIHdhcyB1c2VkIGFzIGl0IHNlZW1lZCB0byBiZSB0aGUgYmVzdCBmaXQuDQoNCiMjIGlpaQ0KDQpOU1cgTGFtYnMgKGBhdXNfbGl2ZXN0b2NrYCkNCg0KYGBge3J9DQpjYXQocGFzdGUodW5pcXVlKGF1c19saXZlc3RvY2skU3RhdGUpLCBjb2xsYXBzZSA9ICJcbiIpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmRmX2xhbWJzPC1hdXNfbGl2ZXN0b2NrJT4lDQogIGZpbHRlcihTdGF0ZSA9PSAiTmV3IFNvdXRoIFdhbGVzIiwgc3RyX2RldGVjdChBbmltYWwsIkxhbWJzIikpDQpoZWFkKGRmX2xhbWJzKQ0KYGBgDQoNCg0KYGBge3J9DQpsYW1ic19wbG90MSA8LSBkZl9sYW1icyU+JQ0KICAgICAgICAgICAgICAgIGF1dG9wbG90KCkrDQogICAgICAgICAgICAgICAgbGFicyh0aXRsZT0gIk5ldyBTb3V0aCBXYWxlcyBDb3VudCIpKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSBJbmYsIHkgPSAtSW5mLCBoanVzdCA9IDEsIHZqdXN0ID0gLTEsDQogICAgICAgICAgIGxhYmVsID0gIlRoZXJlIGlzIG5vIG9idmlvdXMgc2Vhc29uYWxpdHkgb3IgdHJlbmQiKQ0KDQpsYW1ic19wbG90MjwtZGZfbGFtYnMlPiUNCiAgIG1vZGVsKE5BSVZFKENvdW50KSklPiUNCiAgICAgZm9yZWNhc3QoaCA9IDEwKSU+JQ0KICAgICAgIGF1dG9wbG90KGRmX2xhbWJzKSsNCiAgIGxhYnModGl0bGU9ICJOZXcgU291dGggV2FsZXMgRm9yZWNhc3QiKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0KbGFtYnNfcGxvdDENCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmxhbWJzX3Bsb3QyDQpgYGANCg0KDQpObyBjbGVhciBzZWFzb25hbGl0eSBvciB0cmVuZCBzbyBgTkFJVkUoeSlgIHdhcyBtb3N0IGFwcHJvcHJpYXRlDQoNCiMjIGl2DQoNCkhvdXNlaG9sZCB3ZWFsdGggKGBoaF9idWRnZXRgKS4NCg0KYGBge3J9DQpoZWFkKGhoX2J1ZGdldCkNCmBgYA0KDQpgYGB7cn0NCndlYWx0aF9wbG90MTwtaGhfYnVkZ2V0JT4lDQogICAgICAgICAgICAgICAgYXV0b3Bsb3QoV2VhbHRoLCBzaG93LmxlZ2VuZD0gRkFMU0UpKw0KICAgICAgICAgICAgICAgICAgZmFjZXRfZ3JpZChDb3VudHJ5fi4sIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZV95IikNCg0Kd2VhbHRoX3Bsb3QyPC0gaGhfYnVkZ2V0JT4lDQogICAgICAgICAgICAgICAgbW9kZWwoUlcoV2VhbHRofmRyaWZ0KCkpKSU+JQ0KICAgICAgICAgICAgICAgICAgZm9yZWNhc3QoaD01KSU+JQ0KICAgICAgICAgICAgICAgICAgICBhdXRvcGxvdChoaF9idWRnZXQpDQoNCmBgYA0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCndlYWx0aF9wbG90MQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Kd2VhbHRoX3Bsb3QyDQpgYGANCg0KSSBnZW51aW5lbHkgY29uc2lkZXJlZCB0aGUgYE5BSVZFKHkpYCBtb2RlbCwgYmVjYXVzZSB0aGUgZGF0YSB3YXMgYSB0aW1lIHNlcmllcyByZWdhcmRpbmcgZmluYW5jZS4gSSBhbHNvIGRlYmF0ZWQgdGhlIHNlYXNvbmFsIGFzcGVjdCBhbmQgY29uc2lkZXJlZCBgU05BSVZFKHkpYCwgbm90aW5nIHRoZSBkaXAgaW4gd2VhbHRoIGF0IGNlcnRhaW4gaW50ZXJ2YWxzIGZvciBjZXJ0YWluIGNvdW50cmllcy4gSG93ZXZlciwgSSB0aGluayB0aGUgdHJlbmQgaXMgcHJpbWFyaWx5IHVwd2FyZCwgd2l0aCBleGNlcHRpb24gb2YgeWVhcnMgdGhhdCB0aWUgaW4gd2l0aCB0aGUgcmVjZW50IHJlY2Vzc2lvbnMsIHRoZXJlZm9yZSBgUlcoeSB+IGRyaWZ0KCkpYCBvciBEcmlmdCBtZXRob2Qgd2FzIHVzZWQuDQoNCiMjIHYNCg0KQXVzdHJhbGlhbiB0YWtlYXdheSBmb29kIHR1cm5vdmVyIChgYXVzX3JldGFpbGApLg0KDQpgYGB7cn0NCmNhdChwYXN0ZSh1bmlxdWUoYXVzX3JldGFpbCRTdGF0ZSksIGNvbGxhcHNlID0gIlxuIikpDQpgYGANCg0KDQpgYGB7cn0NCmhlYWQoYXVzX3JldGFpbCkNCmBgYA0KDQpgYGB7cn0NCmF1c19yZXRhaWwlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QoSW5kdXN0cnksInRha2Vhd2F5IikpJT4lDQogIGF1dG9wbG90KFR1cm5vdmVyKSsNCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJTdGF0ZSIsIGxhYmVscyA9IHVuaXF1ZShhdXNfcmV0YWlsJFN0YXRlKSkrDQogIGxhYnModGl0bGUgPSAiVHVybm92ZXIgKEF1c3RyYWxpYW4gdGFrZWF3YXkpIGJ5IFN0YXRlIikNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD01fQ0KYXVzX3JldGFpbCAlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QoSW5kdXN0cnksInRha2Vhd2F5IikpICU+JQ0KICBtb2RlbChSVyhUdXJub3ZlciB+IGRyaWZ0KCkpKSAlPiUNCiAgZm9yZWNhc3QoaCA9IDEwKSAlPiUNCiAgYXV0b3Bsb3QoYXVzX3JldGFpbCkrDQogICBmYWNldF93cmFwKH5TdGF0ZSwgc2NhbGVzID0gImZyZWUiKQ0KYGBgDQoNCkp1c3QgbGlrZSB0aGUgDQogICAgDQogICAgICAgICAgICAgIEF1c3RyYWxpYW4gUG9wdWxhdGlvbiAoYGdsb2JhbF9lY29ub215YCkNCiAgICAgICAgICAgICAgDQpUaGUgZGF0YSBmb3IgYWxsIHN0YXRlcyBzaG93ZWQgYW4gdXB3YXJkIHRyZW5kLiBTbyBtb2RlbGluZyBlYWNoIHN0YXRlIHVzaW5nIHRoZSBEcmlmdCBtZXRob2QgbWFkZSB0aGUgbW9zZSBzZW5zZS4NCg0KIyA1LjINCg0KVXNlIHRoZSBGYWNlYm9vayBzdG9jayBwcmljZSAoZGF0YSBzZXQgYGdhZmFfc3RvY2tgKSB0byBkbyB0aGUgZm9sbG93aW5nOg0KDQojIyBhLg0KDQpQcm9kdWNlIGEgdGltZSBwbG90IG9mIHRoZSBzZXJpZXMuDQoNCmBgYHtyfQ0KY2F0KHBhc3RlKHVuaXF1ZShnYWZhX3N0b2NrJFN5bWJvbCksIGNvbGxhcHNlID0gIlxuIikpDQpgYGANCg0KYGBge3J9DQpkaXN0aW5jdChnYWZhX3N0b2NrLCB5ZWFyID0gbHVicmlkYXRlOjp5ZWFyKERhdGUpKQ0KYGBgDQoNCg0KYGBge3J9DQpkZl9mYiA8LSBnYWZhX3N0b2NrICU+JQ0KICBmaWx0ZXIoU3ltYm9sID09ICJGQiIpDQoNCmhlYWQoZGZfZmIpDQpgYGANCg0KYGBge3J9DQojIFJlLWluZGV4IGJhc2VkIG9uIHRyYWRpbmcgZGF5cw0KRkJfc3RvY2sgPC0gZGZfZmIgJT4lDQojIGFscmVhZHkgZmlsdGVyZWQNCiAgbXV0YXRlKGRheSA9IHJvd19udW1iZXIoKSkgJT4lDQogIHVwZGF0ZV90c2liYmxlKGluZGV4ID0gZGF5LCByZWd1bGFyID0gVFJVRSkNCg0KRkJfc3RvY2slPiUNCiAgYXV0b3Bsb3QoQ2xvc2UpKw0KICBsYWJzKHkgPSAnJFVTJywgdGl0bGUgPSAnVGhlIEZhY2Vib29rIERhaWx5IENsb3NpbmcgU3RvY2sgUHJpY2UnKQ0KDQoNCmBgYA0KDQoNCiMjIGIuDQoNClByb2R1Y2UgZm9yZWNhc3RzIHVzaW5nIHRoZSBkcmlmdCBtZXRob2QgYW5kIHBsb3QgdGhlbS4NCg0KKipBUyBQRVIgRXhhbXBsZTogR29vZ2xl4oCZcyBkYWlseSBjbG9zaW5nIHN0b2NrIHByaWNlKioNCg0KYGBge3J9DQojIEZpbHRlciB0aGUgeWVhciBvZiBpbnRlcmVzdA0KRkJfMjAxNSA8LSBGQl9zdG9jayAlPiUgZmlsdGVyKHllYXIoRGF0ZSkgPT0gMjAxNSkNCiMgRml0IHRoZSBtb2RlbHMNCkZCX2ZpdCA8LSBGQl8yMDE1IHw+DQogIG1vZGVsKA0KICAgIERyaWZ0ID0gTkFJVkUoQ2xvc2UgfiBkcmlmdCgpKQ0KICApDQojIFByb2R1Y2UgZm9yZWNhc3RzIGZvciB0aGUgdHJhZGluZyBkYXlzIGluIEphbnVhcnkgMjAxNg0KRkJfamFuXzIwMTYgPC0gRkJfc3RvY2sgfD4NCiAgZmlsdGVyKHllYXJtb250aChEYXRlKSA9PSB5ZWFybW9udGgoIjIwMTYgSmFuIikpDQpGQl9mYyA8LSBGQl9maXQgfD4NCiAgZm9yZWNhc3QobmV3X2RhdGEgPSBGQl9qYW5fMjAxNikNCiMgUGxvdCB0aGUgZm9yZWNhc3RzDQpGQl9mYyB8Pg0KICBhdXRvcGxvdChGQl8yMDE1LCBsZXZlbCA9IE5VTEwpICsNCiAgYXV0b2xheWVyKEZCX2phbl8yMDE2LCBDbG9zZSwgY29sb3VyID0gImJsYWNrIikgKw0KICBsYWJzKHkgPSAiJFVTIiwNCiAgICAgICB0aXRsZSA9ICJGYWNlYm9vayBkYWlseSBjbG9zaW5nIHN0b2NrIHByaWNlcyIsDQogICAgICAgc3VidGl0bGUgPSAiKEphbiAyMDE1IC0gSmFuIDIwMTYpIikgKw0KICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIkZvcmVjYXN0IikpDQpgYGANCg0KDQojIyBjLg0KDQpTaG93IHRoYXQgdGhlIGZvcmVjYXN0cyBhcmUgaWRlbnRpY2FsIHRvIGV4dGVuZGluZyB0aGUgbGluZSBkcmF3biBiZXR3ZWVuIHRoZSBmaXJzdCBhbmQgbGFzdCBvYnNlcnZhdGlvbnMuDQoNCmBgYHtyfQ0KRkJfZmMlPiUgDQogIGF1dG9wbG90KEZCXzIwMTUsIGxldmVsID0gTlVMTCkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IHNsaWNlKEZCXzIwMTUsIHJhbmdlKGN1bXN1bSghaXMubmEoQ2xvc2UpKSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh5PUNsb3NlKSwgbGluZXR5cGUgPSAnZGFzaGVkJykNCmBgYA0KDQoNCiMjIGQuDQoNClRyeSB1c2luZyBzb21lIG9mIHRoZSBvdGhlciBiZW5jaG1hcmsgZnVuY3Rpb25zIHRvIGZvcmVjYXN0IHRoZSBzYW1lIGRhdGEgc2V0LiBXaGljaCBkbyB5b3UgdGhpbmsgaXMgYmVzdD8gV2h5Pw0KDQpgYGB7cn0NCkZCX2ZpdDIgPC0gRkJfMjAxNSAlPiUNCiAgbW9kZWwoDQogICAgTWVhbiA9IE1FQU4oQ2xvc2UpLA0KICAgIE5haXZlID0gTkFJVkUoQ2xvc2UpDQogICkNCiMgdG8gbWFrZSB0aGUgZm9yZWNhc3RzIGZvciB0aGUgdHJhZGluZyBkYXlzIGluIEphbnVhcnkgMjAxNg0KRkJfamFuXzIwMTYgPC0gRkJfc3RvY2sgJT4lDQogIGZpbHRlcih5ZWFybW9udGgoRGF0ZSkgPT0geWVhcm1vbnRoKCIyMDE2IEphbiIpKQ0KDQpGQl9mYzIgPC0gRkJfZml0MiAlPiUNCiAgZm9yZWNhc3QobmV3X2RhdGEgPSBGQl9qYW5fMjAxNikNCiMgUGxvdHRpbmcNCkZCX2ZjMiAlPiUNCiAgYXV0b3Bsb3QoRkJfMjAxNSwgbGV2ZWwgPSBOVUxMKSArDQogIGF1dG9sYXllcihGQl9qYW5fMjAxNiwgQ2xvc2UsIGNvbG91ciA9ICJncmVlbiIpICsNCiAgbGFicyh5ID0gIiRVU0QiLA0KICAgICAgIHRpdGxlID0gIkZCICBDbG9zaW5nIFN0b2NrIFByaWNlcyAoRGFpbHkpIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIoSmFuIDIwMTUgLSBKYW4gMjAxNikiKSArDQogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAiVGhlIEZvcmVjYXN0IikpDQpgYGANCg0KTmFpdmUgSSBiZWxpZXZlIGlzIHRoZSBtb3N0IGFjY3VyYXRlLCBiL2MgdGhlcmUgaXMgbm8gc2Vhc29uYWxpdHkgYW5kIE5haXZlIHdvcmtzIGJlc3Qgd2l0aCBmaW5hbmNpYWwgZGF0YSBhY2NvcmRpbmcgdG8gdGhlIHRleHRib29rDQoNCiAgICAgICAgICAgICJUaGlzIG1ldGhvZCB3b3JrcyByZW1hcmthYmx5IHdlbGwgZm9yIG1hbnkgZWNvbm9taWMgYW5kIGZpbmFuY2lhbCB0aW1lIHNlcmllcy4iDQoNCiMgNS4zDQoNCkFwcGx5IGEgc2Vhc29uYWwgbmHDr3ZlIG1ldGhvZCB0byB0aGUgcXVhcnRlcmx5IEF1c3RyYWxpYW4gYmVlciBwcm9kdWN0aW9uIGRhdGEgZnJvbSAxOTkyLiBDaGVjayBpZiB0aGUgcmVzaWR1YWxzIGxvb2sgbGlrZSB3aGl0ZSBub2lzZSwgYW5kIHBsb3QgdGhlIGZvcmVjYXN0cy4gVGhlIGZvbGxvd2luZyBjb2RlIHdpbGwgaGVscC4NCg0KYGBge3J9DQojIEV4dHJhY3QgZGF0YSBvZiBpbnRlcmVzdA0KcmVjZW50X3Byb2R1Y3Rpb24gPC0gYXVzX3Byb2R1Y3Rpb24gfD4NCiAgZmlsdGVyKHllYXIoUXVhcnRlcikgPj0gMTk5MikNCiMgRGVmaW5lIGFuZCBlc3RpbWF0ZSBhIG1vZGVsDQpmaXQgPC0gcmVjZW50X3Byb2R1Y3Rpb24gfD4gbW9kZWwoU05BSVZFKEJlZXIpKQ0KIyBMb29rIGF0IHRoZSByZXNpZHVhbHMNCmZpdCB8PiBnZ190c3Jlc2lkdWFscygpDQojIExvb2sgYSBzb21lIGZvcmVjYXN0cw0KZml0IHw+IGZvcmVjYXN0KCkgfD4gYXV0b3Bsb3QocmVjZW50X3Byb2R1Y3Rpb24pDQoNCmBgYA0KDQpXaGF0IGRvIHlvdSBjb25jbHVkZT8NCg0KSSBkb24ndCBiZWxpZXZlIHRoaXMgaXMgd2hpdGUgbm9pc2UuIFRoaXMgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGFuIHVucHJlZGljdGFibGUgc2VxdWVuY2Ugb2YgbnVtYmVycywgcmF0aGVyIGl0IHNlZW1zIHRvIGhhdmUgYSBjb25zdGFudCB2YXJpYW5jZSwgYW5kIGAucmVzaWRgIHRoYXQgY2VudGVycyBhcm91bmQgMC4gRm9yIHRoZSBsYWcgdmFsdWUgb2YgNCBpbiB0aGUgYWNmIHBsb3QsIGl0IGlzIGxhcmdlciB0aGFuIG5vcm1hbCBidXQgYWxzbyBmb2xsb3dzIHRoZSBvY2N1cnJlbmNlIGV2ZXJ5IDQgcXVhcnRlcnMuIFNvIHRoZSBtb2RlbCBjYW4gYmUgaW1wcm92ZWQsIGl0IGlzIG5vdCBsaWtlbHkgd2hpdGUgbm9pc2UgYW5kIHRoZXJlZm9yZSBzZWFzb25hbCBuYcOvdmUgbWV0aG9kIGlzIHZhbGlkLg0KDQojIDUuNCANCg0KUmVwZWF0IHRoZSBwcmV2aW91cyBleGVyY2lzZSB1c2luZyB0aGUgQXVzdHJhbGlhbiBFeHBvcnRzIHNlcmllcyBmcm9tIGBnbG9iYWxfZWNvbm9teWAgYW5kIHRoZSBCcmlja3Mgc2VyaWVzIGZyb20gYGF1c19wcm9kdWN0aW9uLmAgVXNlIHdoaWNoZXZlciBvZiBgTkFJVkUoKWAgb3IgYFNOQUlWRSgpYCBpcyBtb3JlIGFwcHJvcHJpYXRlIGluIGVhY2ggY2FzZS4NCg0KKiogTm90ZSoqOiBgZGZfYXVzYCAqZXN0YWJsaXNoZWQgaW4gNS4xIGZvciBBdXN0cmFsaWFuIGV4cG9ydHMqDQoNCiMjIGkNCg0KKkF1c3RyYWxpYW4gRXhwb3J0cyBzZXJpZXMgZnJvbSogYGdsb2JhbF9lY29ub215YA0KDQpgYGB7cn0NCiMgRGVmaW5lIGFuZCBlc3RpbWF0ZSBhIG1vZGVsDQpkZl9hdXNfZml0IDwtIGRmX2F1cyAlPiUgDQogIG1vZGVsKE5BSVZFKEV4cG9ydHMpKQ0KDQojIExvb2sgYXQgdGhlIHJlc2lkdWFscw0KZGZfYXVzX2ZpdCAlPiUgDQogIGdnX3RzcmVzaWR1YWxzKCkNCg0KIyBMb29rIGF0IHNvbWUgZm9yZWNhc3RzDQpkZl9hdXNfZml0ICU+JSANCiAgZm9yZWNhc3QoKSAlPiUgYXV0b3Bsb3QoZGZfYXVzKQ0KYGBgDQoNCmBgYHtyfQ0KYXVzX2F1ZzwtZGZfYXVzX2ZpdCU+JQ0KICAgICAgICAgIGF1Z21lbnQoKQ0KDQphdXNfYXVnIHw+IGZlYXR1cmVzKC5pbm5vdiwgYm94X3BpZXJjZSwgbGFnID0gMTApDQoNCmF1c19hdWcgfD4gZmVhdHVyZXMoLmlubm92LCBsanVuZ19ib3gsIGxhZyA9IDEwKQ0KDQpgYGANCg0KV2hhdCBkbyB5b3UgY29uY2x1ZGU/DQoNCiBCUCBwLXZhbHVlIG9mIDAuMTQ4MTEzNSBhbmQgTEIgcC12YWx1ZSBvZiAwLjA4OTYzNjc4IHN1Z2dlc3RzIHRoZXJlJ3Mgbm90IHNpZ25pZmljYW50IGF1dG9jb3JyZWxhdGlvbiwgc2luY2UgdGhleSdyZSBib3RoIGdyZWF0ZXIgdGhhbiAwLjA1LiBUaGVyZSdzIGFsc28gY29uc3RhbnQgdmFyaWF0aW9uLiBJIGNhbm5vdCBkaWZmZXJlbnRpYXRlIHRoaXMgZnJvbSB3aGl0ZSBub2lzZSBhbmQgSSdtIHVuc3VyZSBpZiB0aGUgbW9kZWwgY291bGQgYmUgaW1wcm92ZWQuDQogDQojIyBpaSANCiANCmBgYHtyfQ0KIyBEZWZpbmUgYW5kIGVzdGltYXRlIGEgbW9kZWwNCmF1c19wcm9kX2ZpdCA8LSBhdXNfcHJvZHVjdGlvbiAlPiUgDQogIG1vZGVsKE5BSVZFKEJyaWNrcykpDQoNCiMgTG9vayBhdCB0aGUgcmVzaWR1YWxzDQphdXNfcHJvZF9maXQgJT4lIA0KICBnZ190c3Jlc2lkdWFscygpDQoNCiMgTG9vayBhdCBzb21lIGZvcmVjYXN0cw0KYXVzX3Byb2RfZml0ICU+JSANCiAgZm9yZWNhc3QoKSAlPiUgYXV0b3Bsb3QoYXVzX3Byb2R1Y3Rpb24pDQpgYGANCg0KYGBge3J9DQphdXNfcHJvZF9hdWc8LWF1c19wcm9kX2ZpdCU+JQ0KICAgICAgICAgIGF1Z21lbnQoKQ0KDQphdXNfcHJvZF9hdWcgfD4gZmVhdHVyZXMoLmlubm92LCBib3hfcGllcmNlLCBsYWcgPSAxMCkNCg0KYXVzX3Byb2RfYXVnIHw+IGZlYXR1cmVzKC5pbm5vdiwgbGp1bmdfYm94LCBsYWcgPSAxMCkNCg0KYGBgDQpUaGUgYWNmIGJlaW5nIGNvbnNpc3RlbnRseSBwYXN0IHRoZSBkb3R0ZWQgbGluZSBzdWdnZXN0cyBzaWduaWZpY2FudCBwZXJpb2RpYyBhdXRvY29ycmVsYXRpb24gYW5kIHNlYXNvbmFsaXR5LiBUaGUgZXZpZGVuY2Ugb2YgYXV0b2NvcnJlbGF0aW9uIGlzIHN1cHBvcnRlZCBieSB0aGUgQlAgYW5kIExCIHN0YXRpc3RpYy4NCg0KIyA1LjcNCg0KRm9yIHlvdXIgcmV0YWlsIHRpbWUgc2VyaWVzIChmcm9tIEV4ZXJjaXNlIDcgaW4gU2VjdGlvbiBbMi4xMF0oaHR0cHM6Ly9vdGV4dHMuY29tL2ZwcDMvZ3JhcGhpY3MtZXhlcmNpc2VzLmh0bWwjZ3JhcGhpY3MtZXhlcmNpc2VzKSk6DQoNCiMjIGEuIA0KDQpDcmVhdGUgYSB0cmFpbmluZyBkYXRhc2V0IGNvbnNpc3Rpbmcgb2Ygb2JzZXJ2YXRpb25zIGJlZm9yZSAyMDExIHVzaW5nDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzKQ0KDQoNCm15c2VyaWVzIDwtIGF1c19yZXRhaWwgJT4lDQogIGZpbHRlcihgU2VyaWVzIElEYCA9PSBzYW1wbGUoYXVzX3JldGFpbCRgU2VyaWVzIElEYCwxKSkNCg0KbXlzZXJpZXNfdHJhaW4gPC0gbXlzZXJpZXMgfD4NCiAgZmlsdGVyKHllYXIoTW9udGgpIDwgMjAxMSkNCg0KYGBgDQoNCiMjIGIuDQoNCkNoZWNrIHRoYXQgeW91ciBkYXRhIGhhdmUgYmVlbiBzcGxpdCBhcHByb3ByaWF0ZWx5IGJ5IHByb2R1Y2luZyB0aGUgZm9sbG93aW5nIHBsb3QuDQoNCmBgYHtyfQ0KYXV0b3Bsb3QobXlzZXJpZXMsIFR1cm5vdmVyKSArDQogIGF1dG9sYXllcihteXNlcmllc190cmFpbiwgVHVybm92ZXIsIGNvbG91ciA9ICJyZWQiKQ0KDQpgYGANCg0KIyMgYy4gDQoNCkZpdCBhIHNlYXNvbmFsIG5hw692ZSBtb2RlbCB1c2luZyBTTkFJVkUoKSBhcHBsaWVkIHRvIHlvdXIgdHJhaW5pbmcgZGF0YSAoYG15c2VyaWVzX3RyYWluYCkuDQoNCmBgYHtyfQ0KZml0IDwtIG15c2VyaWVzX3RyYWluIHw+DQogIG1vZGVsKFNOQUlWRShUdXJub3ZlcikpDQoNCmBgYA0KDQojIyBkLiANCg0KQ2hlY2sgdGhlIHJlc2lkdWFscy4NCmBgYHtyfQ0KZml0IHw+IGdnX3RzcmVzaWR1YWxzKCkNCg0KYGBgDQoNCkRvIHRoZSByZXNpZHVhbHMgYXBwZWFyIHRvIGJlIHVuY29ycmVsYXRlZCBhbmQgbm9ybWFsbHkgZGlzdHJpYnV0ZWQ/DQoNClRoZSBkYXRhIGFwcGVhcnMgdG8gYmUgY29ycmVsYXRlZCwgYnV0IGhhdmUgYSBjb25zdGFudCB2YXJpYXRpb24gY2hhbmdpbmcgZnJvbSBwb3NpdGl2ZSB0byBuZWdhdGl2ZSBhdCBsYWcgMTAgaW5kaWNhdGluZyBoZXRlcm9zY2VkYXN0aWNpdHkuIEkgYWxzbyBkb2VzIG5vdCBhcHBlYXIgdG8gYmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQgb3IgY2VudGVyIGFyb3VuZCAwLg0KDQojIyBlLiANCg0KUHJvZHVjZSBmb3JlY2FzdHMgZm9yIHRoZSB0ZXN0IGRhdGENCg0KYGBge3J9DQpmYyA8LSBmaXQgfD4NCiAgZm9yZWNhc3QobmV3X2RhdGEgPSBhbnRpX2pvaW4obXlzZXJpZXMsIG15c2VyaWVzX3RyYWluKSkNCmZjIHw+IGF1dG9wbG90KG15c2VyaWVzKQ0KDQpgYGANCg0KIyMgZi4NCg0KQ29tcGFyZSB0aGUgYWNjdXJhY3kgb2YgeW91ciBmb3JlY2FzdHMgYWdhaW5zdCB0aGUgYWN0dWFsIHZhbHVlcy4NCg0KYGBge3J9DQpmaXQgfD4gYWNjdXJhY3koKQ0KZmMgfD4gYWNjdXJhY3kobXlzZXJpZXMpDQoNCmBgYA0KDQpUaGUgZm9yZWNhc3QgbW9kZWxzIGRvZXMgbm90IHBlcmZvcm0gd2VsbCBvbiB0aGUgdGVzdCBkYXRhIGFuZCB0aGUgZXJyb3JzIGZvciB0cmFpbmluZyBzZWVtIHNtYWxsZXIgaW4gY29tcGFyaXNvbi4NCg0KIyMgZy4gDQoNCkhvdyBzZW5zaXRpdmUgYXJlIHRoZSBhY2N1cmFjeSBtZWFzdXJlcyB0byB0aGUgYW1vdW50IG9mIHRyYWluaW5nIGRhdGEgdXNlZD8NCg0KVGhlIG1lYXN1cmVzIGFyZSBoaWdobHkgc2Vuc2l0aXZlIHRvIHRoZSBxdWFudGl0eSBvZiBkYXRhIGludm9sdmVkLCB3aGljaCBtYXkgYmUgbmVnYXRpdmVseSBpbXBhY3RlZCB0aGUgbWVhc3VyZXMuDQo=