library(fpp3)
library(ggfortify)
library(forecast)

Instructions

Do the exercises 9.1, 9.2, 9.3, 9.5, 9.6, 9.7, 9.8 in Hyndman. Please submit both the Rpubs link as well as your .rmd file.

9.1

Figure 9.32 shows the ACFs for 36 random numbers, 360 random numbers and 1,000 random numbers.

a.

Explain the differences among these figures. Do they all indicate that the data are white noise?

9.32
9.32

They differ in in range and all fall below the dashed lines. As the values lessens from about 0.25 and progressively lessen towards 0, the weaker the presence of autocorrelation. As the autocorrelation decreases, the more the values resemble white noise.

b.

Why are the critical values at different distances from the mean of zero? Why are the autocorrelations different in each figure when they each refer to white noise?

They are at different distances because they are at different times or lengths of time \(T\) which is approximated by the formula \(\pm2\sqrt{T}\) (ref) Meaning the critical values decrease as the length of time increases.

9.2.

A classic example of a non-stationary series are stock prices. Plot the daily closing prices for Amazon stock (contained in gafa_stock), along with the ACF and PACF. Explain how each plot shows that the series is non-stationary and should be differenced.

unique(gafa_stock$Symbol)
[1] "AAPL" "AMZN" "FB"   "GOOG"
df_amzn<-gafa_stock%>%
         filter(Symbol=="AMZN")
ggplot(df_amzn, aes(x = Date, y = Close)) +
  geom_line(color = "blue") +
  labs(title = "Daily Closing Prices for Amazon Stock", x = "Date", y = "Closing Price") +
  theme_minimal()


df_amzn%>%
gg_tsdisplay(Close,plot_type = 'partial',)+
  labs(title="Partial Autocorrelation Funtion (PACF)")


df_amzn%>%
gg_tsdisplay(difference(Close),plot_type = 'partial')+
  labs(title="Partial Autocorrelation Funtion (PACF)")

9.3.

For the following series, find an appropriate Box-Cox transformation and order of differencing in order to obtain stationary data.

a.

Turkish GDP from global_economy.

#filter out for turkey
df_turkey <- global_economy %>% 
  filter(Country=='Turkey') 

#find lambda value
lambda_turkey <- df_turkey %>%
  features(GDP, features = guerrero) %>%
  pull(lambda_guerrero)

#find ndiffs 
df_turkey  %>%
  mutate(GDP = box_cox(GDP, lambda_turkey)) %>%
  features(GDP, unitroot_ndiffs)

lambda_turkey
[1] 0.1572187

The best Box-Cox transformation is using a lambda of 0.1572187 or roughly 0.16. As far as differencing, the number of difference needed to obtain stationary data is 1.

b.

Accommodation takings in the state of Tasmania from aus_accommodation.

df_tasmania <- aus_accommodation %>% 
  filter(State == 'Tasmania')

lambda_tasmania <- df_tasmania %>%
  features(Takings, features = guerrero) %>%
  pull(lambda_guerrero)

df_tasmania %>%
  mutate(Takings = box_cox(Takings, lambda_tasmania)) %>%
  features(Takings, unitroot_ndiffs) 

lambda_tasmania
[1] 0.001819643

The best lambda for the Box-cox transformation of aus_accommodation Takings for Tasmania is 0.001819643 or roughly 0.002, with the number of difference needed for stationary data being 1.

c.

Monthly sales from souvenirs.


lambda_souvenirs <- souvenirs %>% 
  features(Sales, features = guerrero) %>%
  pull(lambda_guerrero)

souvenirs %>%
  mutate(Sales = box_cox(Sales, lambda_souvenirs)) %>%
  features(Sales, unitroot_ndiffs)

lambda_souvenirs
[1] 0.002118221

The best lambda for the Box-cox transformation of Monthly sales from souvenirs is 0.002118221 or roughly 0.002, with the number of difference needed for stationary data being 1.

9.5

For your retail data (from Exercise 7 in Section 2.10), find the appropriate order of differencing (after transformation if necessary) to obtain stationary data.

set.seed(1234)
myseries <- aus_retail |>
  filter(`Series ID` == sample(aus_retail$`Series ID`,1))
myseries %>%
    gg_tsdisplay(Turnover, plot_type = 'partial', lag_max = 36) +
  labs(title= "Monthly Australian retail data | Turnover", y = NULL)

myseries %>% 
  transmute(
    `Turnover` = Turnover,
    `log(Turnover)` = log(Turnover),
    `log(Turnover) | Ann. Change` = difference(log(Turnover), 12),
        `DD log(Turnover)` =
                     difference(difference(log(Turnover), 12), 1)
  )%>%
  pivot_longer(-Month, names_to="Type", values_to="Turnover") %>%
  mutate(
    Type = factor(Type, levels = c(
      "Turnover",
      "log(Turnover)",
      "log(Turnover) | Ann. Change",
      "DD log(Turnover)"))
  ) %>%
  ggplot(aes(x = Month, y = Turnover)) +
  geom_line() +
  facet_grid(vars(Type), scales = "free_y") +
  labs(title= "Monthly Australian retail data | Turnover", y = NULL)

There clearly seems to be a increasing trend. It appears that there is a seasonal pattern for the series that increases in size, most notable for the spike in each level. Regardless it lack predictability for the stationary series. Log transformation stabilizes the data but maintians the increasing trend, while the seasonally differenced data of the log transformation was the most reasonably stabilized.

9.6

Simulate and plot some data from simple ARIMA models.

Use the following R code to generate data from an AR(1) model with \(\emptyset_1=0.6\)

and \(\sigma^2=1\). The process starts with \(y_1=0\).

y <- numeric(100)
e <- rnorm(100)
for(i in 2:100)
  y[i] <- 0.6*y[i-1] + e[i]
sim <- tsibble(idx = seq_len(100), y = y, index = idx)
head(sim)

b.

Produce a time plot for the series. How does the plot change as you change \(\phi_1\)?


sim %>% autoplot(y) +
  labs(title = expression("AR(1) model with" ~ phi[1] ~ "=" ~ 0.6 ~ "," ~ sigma^2 ~ "=" ~ 1 ~ "," ~ y[1] ~ "=" ~ 0))


for(i in 2:100)
  y[i] <- .8*y[i-1] + e[i]
sim1 <- tsibble(idx = seq_len(100), y = y, index = idx)

for(i in 2:100)
  y[i] <- 0.2*y[i-1] + e[i]
sim2 <- tsibble(idx = seq_len(100), y = y, index = idx)

for(i in 2:100)
  y[i] <- -1.0*y[i-1] + e[i]
sim3 <- tsibble(idx = seq_len(100), y = y, index = idx)

for(i in 2:100)
  y[i] <- 1.4*y[i-1] + e[i]
sim4 <- tsibble(idx = seq_len(100), y = y, index = idx)




plt1 <- sim1 %>% autoplot(y) +
 labs(title = expression("AR(1) model with" ~ phi[1] ~ "=" ~ 0.8 ~ "," ~ sigma^2 ~ "=" ~ 1 ))
plt2 <- sim2 %>% autoplot(y) +
  labs(title = expression("AR(1) model with" ~ phi[1] ~ "=" ~ 0.2 ~ "," ~ sigma^2 ~ "=" ~ 1 ))
plt3 <- sim3 %>% autoplot(y) +
  labs(title = expression("AR(1) model with" ~ phi[1] ~ "=" ~ -1.0 ~ "," ~ sigma^2 ~ "=" ~ 1 ))
plt4 <- sim4 %>% autoplot(y) +
  labs(title = expression("AR(1) model with" ~ phi[1] ~ "=" ~ 1.4 ~ "," ~ sigma^2 ~ "=" ~ 1 ))


gridExtra::grid.arrange(plt1, plt2, plt3, plt4, ncol = 2, nrow = 2)

As \(\phi_{1}\) increases or decrease, the magnitude and wavelength matches \(\phi_{1}\) increasing and decreasing in the same direction. This is also true for the magnitude and wavelength for values between 0 and 1, but for \(\phi\) values greater than 1 the plot emulates an exponential function \(f(x)=a^x\), while wavelength shortens for values less than 0, meaning a higher frequency with varying amplitudes.

c.

Write your own code to generate data from an MA(1) model with \(\theta_1\)=0.6 and \(\sigma^2=1\).

# Reference
# y <- numeric(100)
# e <- rnorm(100)
# for(i in 2:100)
#   y[i] <- 0.6*y[i-1] + e[i]
# sim <- tsibble(idx = seq_len(100), y = y, index = idx)
for(i in 2:100)
  y[i] <- 0.6*e[i-1] + e[i] 

sim_ma <- tsibble(idx = seq_len(100), y = y, index = idx)

d.

Produce a time plot for the series. How does the plot change as you change \(\theta_1\)?

sim_ma %>% autoplot(y)+
  labs(title = expression("AR(1) model with" ~ phi[1] ~ "=" ~ 0.6 ~ "," ~ sigma^2 ~ "=" ~ 1))

#create the data
for(i in 2:100)
  y[i] <- 0*e[i-1] + e[i] 
sim1_ma <- tsibble(idx = seq_len(100), y = y, index = idx)

for(i in 2:100)
  y[i] <- .8*e[i-1] + e[i] 
sim2_ma <- tsibble(idx = seq_len(100), y = y, index = idx)

for(i in 2:100)
  y[i] <- -1.4*e[i-1] + e[i] 
sim3_ma<- tsibble(idx = seq_len(100), y = y, index = idx)

for(i in 2:100)
  y[i] <- 1.5*e[i-1] + e[i] 
sim4_ma<- tsibble(idx = seq_len(100), y = y, index = idx)

#create the ggplot objects
plt5 <- sim1_ma %>% autoplot(y)+
  labs(title = expression("MA(1) model with" ~ phi[1] ~ "=" ~ 0 ~ "," ~ sigma^2 ~ "=" ~ 1))


plt6 <- sim2_ma %>% autoplot(y)+
  labs(title = expression("MA(1) model with" ~ phi[1] ~ "=" ~ 0.8 ~ "," ~ sigma^2 ~ "=" ~ 1))

plt7 <- sim3_ma %>% autoplot(y)+
  labs(title = expression("MA(1) model with" ~ phi[1] ~ "=" ~ -1.4 ~ "," ~ sigma^2 ~ "=" ~ 1))

plt8 <- sim4_ma %>% autoplot(y)+
  labs(title = expression("MA(1) model with" ~ phi[1] ~ "=" ~ 1.5 ~ "," ~ sigma^2 ~ "=" ~ 1))

#plot them all togther
gridExtra::grid.arrange(plt5, plt6, plt7, plt8, ncol = 2, nrow = 2)

As \(\theta\) changes, the plots barely change to be honest.They all show a steady variance and implying the MA(1) series are stationary. Amplitude minimum and maximum hardly changes as well and there are no shifts in wavelengths.

e.

Generate data from an ARMA(1,1) model with \(\phi_1=0.6\), \(\theta_1=0.6\) and \(\sigma^2=1\)

for(i in 2:100)
  y[i] <- 0.6*y[i-1] + 0.6*e[i-1] + e[i]

sim_arma11 <- tsibble(idx = seq_len(100), y = y, index = idx)

f.

Generate data from an AR(2) model with \(\phi_1=−0.8\), \(\phi_2=0.3\) and \(\sigma^2=1\). (Note that these parameters will give a non-stationary series.)

phi_1<--0.8
phi_2<-0.3


for(i in 3:100)
  y[i] <- -phi_1*y[i-1] + phi_2*y[i-2] + e[i]

sim_ar_8_3 <- tsibble(idx = seq_len(100), y = y, index = idx)

g.

Graph the latter two series and compare them.

plt9 <- sim_arma11 %>% autoplot(y)+
  labs(title = expression("ARMA(1,1) model with" ~ phi[1] ~ "=" ~ 0.6 ~ "," ~ theta[1] ~ "=" ~ 0.6 ~ "," ~ sigma^2 ~ "=" ~ 1))

plt10 <- sim_ar_8_3 %>% autoplot(y)+
  labs(title = expression("ARMA model with" ~ phi[1] ~ "=" ~ -0.8 ~ "," ~ theta[1] ~ "=" ~ 0.3 ~ "," ~ sigma^2 ~ "=" ~ 1))

gridExtra::grid.arrange(plt9, plt10, ncol = 1, nrow = 2)

Arma(1,1) model has a plot that does not show seasonality or trend, with a maximum of 3 and minimum of -6, while the 2nd ARMA model emulates a exponential fucntion as noted in a previous example.

9.7

Consider aus_airpassengers, the total number of passengers (in millions) from Australian air carriers for the period 1970-2011.

max(aus_airpassengers$Year)
[1] 2016
min(aus_airpassengers$Year)
[1] 1970
head(aus_airpassengers)

a.

Use ARIMA() to find an appropriate ARIMA model. What model was selected. Check that the residuals look like white noise. Plot forecasts for the next 10 periods.

aus_air_pd_71_11<-aus_airpassengers %>%
  filter(Year>=1971&Year<=2011)

fit_aus <- aus_air_pd_71_11%>%
  model(ARIMA(Passengers))

#find the fit
report(fit_aus)
Series: Passengers 
Model: ARIMA(0,2,1) 

Coefficients:
          ma1
      -0.8765
s.e.   0.0742

sigma^2 estimated as 4.778:  log likelihood=-86.06
AIC=176.13   AICc=176.46   BIC=179.45
#forecast 10 periods
fit_aus %>% forecast(h=10) %>%
  autoplot(aus_airpassengers) +
  labs(y = "Passengers (Millions)", 
       title = "Australian Air Passengers (1971-2011)",
       subtitle = "10 Year Forecast")

fit_aus %>% gg_tsresiduals() + 
  labs(title = "Australian Air Passengers (1971-2011)",
       subtitle = "Australian Air Passengers (1971-2011)")

Function ARIMA() automatically selected for aus_airpassengers data model, which was an ARIMA(0,2,1). Function gg_tsresiduals() confirms there is white noise base on the residuals.

b.

Write the model in terms of the backshift operator.

\(y_t = -0.8963 * \epsilon_{t-1} + \epsilon_{t}\)

c.

Plot forecasts from an ARIMA(0,1,0) model with drift and compare these to part a.

fit_aus_010 <- aus_air_pd_71_11 %>%
  model(ARIMA(Passengers ~ pdq(0,1,0)))

report(fit_aus_010)
Series: Passengers 
Model: ARIMA(0,1,0) w/ drift 

Coefficients:
      constant
        1.4008
s.e.    0.3384

sigma^2 estimated as 4.699:  log likelihood=-87.2
AIC=178.4   AICc=178.72   BIC=181.77

#plot forecast
fit_aus_010 %>% forecast(h=10) %>%
  autoplot(aus_air_pd_71_11) +
  labs(y = "Passengers (Millions", 
       title = "Australian Air Passengers (1971-2011)",
       subtitle = "10 Year Forecast")


#plot residuals
fit_aus_010 %>% gg_tsresiduals() +
  labs(title = "Australian Air Passengers (1971-2011)",
       subtitle = "10 Year Forecast")

Part A forecasts higher, and part C residuals show white noise.

d.

Plot forecasts from an ARIMA(2,1,2) model with drift and compare these to parts a and c. Remove the constant and see what happens.

fit_aus212 <- aus_air_pd_71_11 %>%
  model(ARIMA(Passengers ~ pdq(2,1,2)))
fit_aus212_no_const <- aus_airpassengers %>%
  model(ARIMA(Passengers ~1+ pdq(2,1,2),include.constant=FALSE)) 
Warning: 1 error encountered for ARIMA(Passengers ~ 1 + pdq(2, 1, 2), include.constant = FALSE)
[1] unused argument (include.constant = FALSE)
report(fit_aus212)
Series: Passengers 
Model: ARIMA(2,1,2) 

Coefficients:
         ar1      ar2      ma1     ma2
      1.5370  -0.5426  -1.6145  0.6863
s.e.  0.9194   0.9077   0.8176  0.6743

sigma^2 estimated as 4.816:  log likelihood=-87.05
AIC=184.1   AICc=185.87   BIC=192.55
#plot forecast
fit_aus212 %>% forecast(h=10) %>%
  autoplot(aus_air_pd_71_11) +
  labs(y = "Passengers (Millions", 
       title = "Australian Air Passengers (1971-2011)",
       subtitle = "10 Year Forecast")


#plot residuals
fit_aus212 %>% gg_tsresiduals() +
  labs(title = "Australian Air Passengers (1971-2011)",
       subtitle = "10 Year Forecast")

This model compared to part a and b seems worse and has white noise. Removing the constant produced an error.

e.

Plot forecasts from an ARIMA(0,2,1) model with a constant. What happens?

section 9.7 a does this already. It is the best fitting model.

9.8

For the United States GDP series (from global_economy):

df_us<-global_economy%>%
        filter(Country =="United States")

a.

if necessary, find a suitable Box-Cox transformation for the data;


df_us %>% autoplot(GDP) +
  labs(title = "US GDP")

No need for a transformation, the data doesn’t vary.

b.

fit a suitable ARIMA model to the transformed data using ARIMA();

fit_us <- df_us %>%
  model(
    arima = ARIMA(GDP, stepwise = FALSE, approx = FALSE))

report(fit_us)
Series: GDP 
Model: ARIMA(0,2,2) 

Coefficients:
          ma1      ma2
      -0.4206  -0.3048
s.e.   0.1197   0.1078

sigma^2 estimated as 2.615e+22:  log likelihood=-1524.08
AIC=3054.15   AICc=3054.61   BIC=3060.23

Model: ARIMA(0,2,2) according to ARIMA()

c.

try some other plausible models by experimenting with the orders chosen;

df_us %>%
  features(GDP, unitroot_ndiffs)

df_us %>%
  gg_tsdisplay(GDP, plot_type = 'partial')


fit_us222 <- df_us %>%
  model(ARIMA(GDP ~ pdq(2,2,2)))

fit_us122 <- df_us %>%
  model(ARIMA(GDP ~ pdq(1,2,2)))

fit_us220 <- df_us %>%
  model(ARIMA(GDP ~ pdq(2,2,0)))

fit_us221 <- df_us %>%
  model(ARIMA(GDP ~ pdq(2,2,1)))

report(fit_us222)
Series: GDP 
Model: ARIMA(2,2,2) 

Coefficients:
         ar1      ar2      ma1     ma2
      1.3764  -0.4780  -1.9659  1.0000
s.e.  0.1216   0.1354   0.0723  0.0719

sigma^2 estimated as 2.283e+22:  log likelihood=-1521.14
AIC=3052.27   AICc=3053.47   BIC=3062.4
report(fit_us122)
Series: GDP 
Model: ARIMA(1,2,2) 

Coefficients:
         ar1      ma1      ma2
      0.2053  -0.5912  -0.1928
s.e.  0.3008   0.2886   0.2102

sigma^2 estimated as 2.646e+22:  log likelihood=-1523.86
AIC=3055.72   AICc=3056.51   BIC=3063.82
report(fit_us220)
Series: GDP 
Model: ARIMA(2,2,0) 

Coefficients:
          ar1      ar2
      -0.2088  -0.2059
s.e.   0.1320   0.1317

sigma^2 estimated as 2.984e+22:  log likelihood=-1527.51
AIC=3061.02   AICc=3061.48   BIC=3067.09
report(fit_us221)
Series: GDP 
Model: ARIMA(2,2,1) 

Coefficients:
         ar1      ar2      ma1
      0.4321  -0.1605  -0.8028
s.e.  0.1537   0.1405   0.0908

sigma^2 estimated as 2.619e+22:  log likelihood=-1523.61
AIC=3055.22   AICc=3056   BIC=3063.32
  • AIC, AICc, and BIC values increase when increasing q value from 2 to 1.
  • AIC, AICc, and BIC values decreased when decreasing q values.
  • 2,2,2 is the best fit from the combinations.

d.

choose what you think is the best model and check the residual diagnostics;

ARIMA(2,2,2) - But the ACF looks like white noise and residuals is left skewed

fit_us222 %>% gg_tsresiduals() +
  labs(title = "ARIMA(2,2,2)")

e.

produce forecasts of your fitted model. Do the forecasts look reasonable?

fit_us222 %>% forecast(h=10) %>%
  autoplot(df_us) +
  labs(title = "ARIMA(2,2,2)")

Pretty much reasonable base on what our previous data showed.

f.

compare the results with what you would obtain using ETS() (with no transformation).

fit_ets <- df_us %>% 
  model(ETS(GDP))

report(fit_ets)
Series: GDP 
Model: ETS(M,A,N) 
  Smoothing parameters:
    alpha = 0.9990876 
    beta  = 0.5011949 

  Initial states:
         l[0]        b[0]
 448093333334 64917355687

  sigma^2:  7e-04

     AIC     AICc      BIC 
3190.787 3191.941 3201.089 
fit_ets %>% forecast(h = 10) %>%
  autoplot(df_us) 

fit_ets %>% gg_tsresiduals()

The comparison for ARIMA(2,2,2) AICc=3053.47 vs. ETS(M,A,N) AICc=3191.941, ARIMA(2,2,2) performs better and forecasts are good for both considering the ACF plots lack correlation.

LS0tCnRpdGxlOiAnREFUQSA2MjQ6IFBSRURJQ1RJVkUgQU5BTFlUSUNTIEhXNicKYXV0aG9yOiAiR2FicmllbCBDYW1wb3MiCmRhdGU6ICJMYXN0IGVkaXRlZCBgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVCICVkLCAlWScpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBnZW9tZXRyeTogbGVmdD0wLjVjbSxyaWdodD0wLjVjbSx0b3A9MWNtLGJvdHRvbT0yY20KICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgcGRmX2RvY3VtZW50OgogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CnVybGNvbG9yOiBibHVlCi0tLQoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZnBwMykKbGlicmFyeShnZ2ZvcnRpZnkpCmxpYnJhcnkoZm9yZWNhc3QpCmBgYAoKIyBJbnN0cnVjdGlvbnMKCkRvIHRoZSBleGVyY2lzZXMgOS4xLCA5LjIsIDkuMywgOS41LCA5LjYsIDkuNywgOS44IGluIEh5bmRtYW4uICBQbGVhc2Ugc3VibWl0IGJvdGggdGhlIFJwdWJzIGxpbmsgYXMgd2VsbCBhcyB5b3VyIC5ybWQgZmlsZS4KCgojIDkuMQoKRmlndXJlIDkuMzIgc2hvd3MgdGhlIEFDRnMgZm9yIDM2IHJhbmRvbSBudW1iZXJzLCAzNjAgcmFuZG9tIG51bWJlcnMgYW5kIDEsMDAwIHJhbmRvbSBudW1iZXJzLgoKIyMgYS4KCkV4cGxhaW4gdGhlIGRpZmZlcmVuY2VzIGFtb25nIHRoZXNlIGZpZ3VyZXMuIERvIHRoZXkgYWxsIGluZGljYXRlIHRoYXQgdGhlIGRhdGEgYXJlIHdoaXRlIG5vaXNlPwoKIVs5LjMyXShGaWd1cmVfOV8zMi5wbmcpCgpUaGV5IGRpZmZlciBpbiBpbiByYW5nZSBhbmQgYWxsIGZhbGwgYmVsb3cgdGhlIGRhc2hlZCBsaW5lcy4gQXMgdGhlIHZhbHVlcyBsZXNzZW5zIGZyb20gYWJvdXQgMC4yNSBhbmQgcHJvZ3Jlc3NpdmVseSBsZXNzZW4gdG93YXJkcyAwLCB0aGUgd2Vha2VyIHRoZSBwcmVzZW5jZSBvZiBhdXRvY29ycmVsYXRpb24uIEFzIHRoZSBhdXRvY29ycmVsYXRpb24gZGVjcmVhc2VzLCB0aGUgbW9yZSB0aGUgdmFsdWVzIHJlc2VtYmxlIHdoaXRlIG5vaXNlLgoKIyMgYi4KCldoeSBhcmUgdGhlIGNyaXRpY2FsIHZhbHVlcyBhdCBkaWZmZXJlbnQgZGlzdGFuY2VzIGZyb20gdGhlIG1lYW4gb2YgemVybz8gV2h5IGFyZSB0aGUgYXV0b2NvcnJlbGF0aW9ucyBkaWZmZXJlbnQgaW4gZWFjaCBmaWd1cmUgd2hlbiB0aGV5IGVhY2ggcmVmZXIgdG8gd2hpdGUgbm9pc2U/CgpUaGV5IGFyZSBhdCBkaWZmZXJlbnQgZGlzdGFuY2VzIGJlY2F1c2UgdGhleSBhcmUgYXQgZGlmZmVyZW50IHRpbWVzIG9yIGxlbmd0aHMgb2YgdGltZSAkVCQgd2hpY2ggaXMgYXBwcm94aW1hdGVkIGJ5IHRoZSBmb3JtdWxhICRccG0yXHNxcnR7VH0kIChbcmVmXShodHRwOi8vc2ZiNjQ5Lndpd2kuaHUtYmVybGluLmRlL2ZlZGNfaG9tZXBhZ2UveHBsb3JlL3R1dG9yaWFscy94ZWdib2h0bWxub2RlMzkuaHRtbCkpIE1lYW5pbmcgdGhlIGNyaXRpY2FsIHZhbHVlcyBkZWNyZWFzZSBhcyB0aGUgbGVuZ3RoIG9mIHRpbWUgaW5jcmVhc2VzLgoKIyA5LjIuCgpBIGNsYXNzaWMgZXhhbXBsZSBvZiBhIG5vbi1zdGF0aW9uYXJ5IHNlcmllcyBhcmUgc3RvY2sgcHJpY2VzLiBQbG90IHRoZSBkYWlseSBjbG9zaW5nIHByaWNlcyBmb3IgQW1hem9uIHN0b2NrIChjb250YWluZWQgaW4gYGdhZmFfc3RvY2tgKSwgYWxvbmcgd2l0aCB0aGUgQUNGIGFuZCBQQUNGLiBFeHBsYWluIGhvdyBlYWNoIHBsb3Qgc2hvd3MgdGhhdCB0aGUgc2VyaWVzIGlzIG5vbi1zdGF0aW9uYXJ5IGFuZCBzaG91bGQgYmUgZGlmZmVyZW5jZWQuCgpgYGB7cn0KdW5pcXVlKGdhZmFfc3RvY2skU3ltYm9sKQpgYGAKCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KZGZfYW16bjwtZ2FmYV9zdG9jayU+JQogICAgICAgICBmaWx0ZXIoU3ltYm9sPT0iQU1aTiIpCmdncGxvdChkZl9hbXpuLCBhZXMoeCA9IERhdGUsIHkgPSBDbG9zZSkpICsKICBnZW9tX2xpbmUoY29sb3IgPSAiYmx1ZSIpICsKICBsYWJzKHRpdGxlID0gIkRhaWx5IENsb3NpbmcgUHJpY2VzIGZvciBBbWF6b24gU3RvY2siLCB4ID0gIkRhdGUiLCB5ID0gIkNsb3NpbmcgUHJpY2UiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpkZl9hbXpuJT4lCmdnX3RzZGlzcGxheShDbG9zZSxwbG90X3R5cGUgPSAncGFydGlhbCcsKSsKICBsYWJzKHRpdGxlPSJQYXJ0aWFsIEF1dG9jb3JyZWxhdGlvbiBGdW50aW9uIChQQUNGKSIpCgpkZl9hbXpuJT4lCmdnX3RzZGlzcGxheShkaWZmZXJlbmNlKENsb3NlKSxwbG90X3R5cGUgPSAncGFydGlhbCcpKwogIGxhYnModGl0bGU9IlBhcnRpYWwgQXV0b2NvcnJlbGF0aW9uIEZ1bnRpb24gKFBBQ0YpIikKCmBgYAoKKiBDbG9zaW5nIFByaWNlIHBsb3Qgc2hvd3MgYW4gdXB3YXJkIHRyZW5kLCBwbGVudHkgb2YgdmFyaWF0aW9uIGJ1dCBubyByZWFsIHNlYXNvbmFsaXR5IG9yIGN5Y2xpYyBiZWhhdmlvci4KKiBBQ0YgcGxvdCBhZ2FpbiBzaG93cyBubyBzZWFzb25hbGl0eSBidXQgaXQgaXMgc2xpZ2h0bHkgdHJlbmRpbmcgbmVnYXRpdmVseS4KKiBEaWZmZXJlbmNpbmcgd2hpY2ggd291bGQgcmVtb3ZlIHRoZSBjaGFuZ2VzIGluIGxldmVsLCB3aGljaCBjb3VsZCByZW1vdmUvcmVkdWNlIHRoZSB0cmVuZCBhbmQgc3RhYmlsaXplIHRoZSBtZWFuLgoqIERpZmZlcmVuY2luZyBBQ0YgcGxvdCBzdXBwb3J0cyB0aGF0IEFtYXpvbiBDbG9zaW5nIHByaWNlIGhhcyBubyBhdXRvY29ycmVsYXRpb24uCgoKIyA5LjMuCgpGb3IgdGhlIGZvbGxvd2luZyBzZXJpZXMsIGZpbmQgYW4gYXBwcm9wcmlhdGUgQm94LUNveCB0cmFuc2Zvcm1hdGlvbiBhbmQgb3JkZXIgb2YgZGlmZmVyZW5jaW5nIGluIG9yZGVyIHRvIG9idGFpbiBzdGF0aW9uYXJ5IGRhdGEuCgojIyBhLgoKVHVya2lzaCBHRFAgZnJvbSBgZ2xvYmFsX2Vjb25vbXlgLgoKCmBgYHtyfQojZmlsdGVyIG91dCBmb3IgdHVya2V5CmRmX3R1cmtleSA8LSBnbG9iYWxfZWNvbm9teSAlPiUgCiAgZmlsdGVyKENvdW50cnk9PSdUdXJrZXknKSAKCiNmaW5kIGxhbWJkYSB2YWx1ZQpsYW1iZGFfdHVya2V5IDwtIGRmX3R1cmtleSAlPiUKICBmZWF0dXJlcyhHRFAsIGZlYXR1cmVzID0gZ3VlcnJlcm8pICU+JQogIHB1bGwobGFtYmRhX2d1ZXJyZXJvKQoKI2ZpbmQgbmRpZmZzIApkZl90dXJrZXkgICU+JQogIG11dGF0ZShHRFAgPSBib3hfY294KEdEUCwgbGFtYmRhX3R1cmtleSkpICU+JQogIGZlYXR1cmVzKEdEUCwgdW5pdHJvb3RfbmRpZmZzKQoKbGFtYmRhX3R1cmtleQpgYGAKClRoZSBiZXN0IEJveC1Db3ggdHJhbnNmb3JtYXRpb24gaXMgdXNpbmcgYSBsYW1iZGEgb2YgMC4xNTcyMTg3ICBvciByb3VnaGx5IDAuMTYuIEFzIGZhciBhcyBkaWZmZXJlbmNpbmcsIHRoZSBudW1iZXIgb2YgZGlmZmVyZW5jZSBuZWVkZWQgdG8gb2J0YWluIHN0YXRpb25hcnkgZGF0YSBpcyAxLiAKCgojIyBiLgoKQWNjb21tb2RhdGlvbiB0YWtpbmdzIGluIHRoZSBzdGF0ZSBvZiBUYXNtYW5pYSBmcm9tIGF1c19hY2NvbW1vZGF0aW9uLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGZfdGFzbWFuaWEgPC0gYXVzX2FjY29tbW9kYXRpb24gJT4lIAogIGZpbHRlcihTdGF0ZSA9PSAnVGFzbWFuaWEnKQoKbGFtYmRhX3Rhc21hbmlhIDwtIGRmX3Rhc21hbmlhICU+JQogIGZlYXR1cmVzKFRha2luZ3MsIGZlYXR1cmVzID0gZ3VlcnJlcm8pICU+JQogIHB1bGwobGFtYmRhX2d1ZXJyZXJvKQoKZGZfdGFzbWFuaWEgJT4lCiAgbXV0YXRlKFRha2luZ3MgPSBib3hfY294KFRha2luZ3MsIGxhbWJkYV90YXNtYW5pYSkpICU+JQogIGZlYXR1cmVzKFRha2luZ3MsIHVuaXRyb290X25kaWZmcykgCgpsYW1iZGFfdGFzbWFuaWEKYGBgClRoZSBiZXN0IGxhbWJkYSBmb3IgdGhlIEJveC1jb3ggdHJhbnNmb3JtYXRpb24gb2YgYGF1c19hY2NvbW1vZGF0aW9uYCBgVGFraW5nc2AgZm9yIGBUYXNtYW5pYWAgaXMgMC4wMDE4MTk2NDMgb3Igcm91Z2hseSAwLjAwMiwgd2l0aCB0aGUgbnVtYmVyIG9mIGRpZmZlcmVuY2UgbmVlZGVkIGZvciBzdGF0aW9uYXJ5IGRhdGEgYmVpbmcgMS4KCiMjIGMuCgpNb250aGx5IHNhbGVzIGZyb20gc291dmVuaXJzLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCmxhbWJkYV9zb3V2ZW5pcnMgPC0gc291dmVuaXJzICU+JSAKICBmZWF0dXJlcyhTYWxlcywgZmVhdHVyZXMgPSBndWVycmVybykgJT4lCiAgcHVsbChsYW1iZGFfZ3VlcnJlcm8pCgpzb3V2ZW5pcnMgJT4lCiAgbXV0YXRlKFNhbGVzID0gYm94X2NveChTYWxlcywgbGFtYmRhX3NvdXZlbmlycykpICU+JQogIGZlYXR1cmVzKFNhbGVzLCB1bml0cm9vdF9uZGlmZnMpCgpsYW1iZGFfc291dmVuaXJzCmBgYAoKVGhlIGJlc3QgbGFtYmRhIGZvciB0aGUgQm94LWNveCB0cmFuc2Zvcm1hdGlvbiBvZiBNb250aGx5IHNhbGVzIGZyb20gYHNvdXZlbmlyc2AgaXMgMC4wMDIxMTgyMjEgb3Igcm91Z2hseSAwLjAwMiwgd2l0aCB0aGUgbnVtYmVyIG9mIGRpZmZlcmVuY2UgbmVlZGVkIGZvciBzdGF0aW9uYXJ5IGRhdGEgYmVpbmcgMS4KCiMgOS41CgpGb3IgeW91ciByZXRhaWwgZGF0YSAoZnJvbSBFeGVyY2lzZSA3IGluIFNlY3Rpb24gWzIuMTBdKGh0dHBzOi8vb3RleHRzLmNvbS9mcHAzL2dyYXBoaWNzLWV4ZXJjaXNlcy5odG1sI2dyYXBoaWNzLWV4ZXJjaXNlcykpLCBmaW5kIHRoZSBhcHByb3ByaWF0ZSBvcmRlciBvZiBkaWZmZXJlbmNpbmcgKGFmdGVyIHRyYW5zZm9ybWF0aW9uIGlmIG5lY2Vzc2FyeSkgdG8gb2J0YWluIHN0YXRpb25hcnkgZGF0YS4KCmBgYHtyfQpzZXQuc2VlZCgxMjM0KQpteXNlcmllcyA8LSBhdXNfcmV0YWlsIHw+CiAgZmlsdGVyKGBTZXJpZXMgSURgID09IHNhbXBsZShhdXNfcmV0YWlsJGBTZXJpZXMgSURgLDEpKQpgYGAKCmBgYHtyfQpteXNlcmllcyAlPiUKICAgIGdnX3RzZGlzcGxheShUdXJub3ZlciwgcGxvdF90eXBlID0gJ3BhcnRpYWwnLCBsYWdfbWF4ID0gMzYpICsKICBsYWJzKHRpdGxlPSAiTW9udGhseSBBdXN0cmFsaWFuIHJldGFpbCBkYXRhIHwgVHVybm92ZXIiLCB5ID0gTlVMTCkKYGBgCgoKYGBge3IsIGZpZy5oZWlnaHQ9OX0KbXlzZXJpZXMgJT4lIAogIHRyYW5zbXV0ZSgKICAgIGBUdXJub3ZlcmAgPSBUdXJub3ZlciwKICAgIGBsb2coVHVybm92ZXIpYCA9IGxvZyhUdXJub3ZlciksCiAgICBgbG9nKFR1cm5vdmVyKSB8IEFubi4gQ2hhbmdlYCA9IGRpZmZlcmVuY2UobG9nKFR1cm5vdmVyKSwgMTIpLAogICAgICAgIGBERCBsb2coVHVybm92ZXIpYCA9CiAgICAgICAgICAgICAgICAgICAgIGRpZmZlcmVuY2UoZGlmZmVyZW5jZShsb2coVHVybm92ZXIpLCAxMiksIDEpCiAgKSU+JQogIHBpdm90X2xvbmdlcigtTW9udGgsIG5hbWVzX3RvPSJUeXBlIiwgdmFsdWVzX3RvPSJUdXJub3ZlciIpICU+JQogIG11dGF0ZSgKICAgIFR5cGUgPSBmYWN0b3IoVHlwZSwgbGV2ZWxzID0gYygKICAgICAgIlR1cm5vdmVyIiwKICAgICAgImxvZyhUdXJub3ZlcikiLAogICAgICAibG9nKFR1cm5vdmVyKSB8IEFubi4gQ2hhbmdlIiwKICAgICAgIkREIGxvZyhUdXJub3ZlcikiKSkKICApICU+JQogIGdncGxvdChhZXMoeCA9IE1vbnRoLCB5ID0gVHVybm92ZXIpKSArCiAgZ2VvbV9saW5lKCkgKwogIGZhY2V0X2dyaWQodmFycyhUeXBlKSwgc2NhbGVzID0gImZyZWVfeSIpICsKICBsYWJzKHRpdGxlPSAiTW9udGhseSBBdXN0cmFsaWFuIHJldGFpbCBkYXRhIHwgVHVybm92ZXIiLCB5ID0gTlVMTCkKYGBgCgpUaGVyZSBjbGVhcmx5IHNlZW1zIHRvIGJlIGEgaW5jcmVhc2luZyB0cmVuZC4gSXQgYXBwZWFycyB0aGF0IHRoZXJlIGlzIGEgc2Vhc29uYWwgcGF0dGVybiBmb3IgdGhlIHNlcmllcyB0aGF0IGluY3JlYXNlcyBpbiBzaXplLCBtb3N0IG5vdGFibGUgZm9yIHRoZSBzcGlrZSBpbiBlYWNoIGxldmVsLiBSZWdhcmRsZXNzIGl0IGxhY2sgcHJlZGljdGFiaWxpdHkgZm9yIHRoZSBzdGF0aW9uYXJ5IHNlcmllcy4gTG9nIHRyYW5zZm9ybWF0aW9uIHN0YWJpbGl6ZXMgdGhlIGRhdGEgYnV0IG1haW50aWFucyB0aGUgaW5jcmVhc2luZyB0cmVuZCwgd2hpbGUgdGhlIHNlYXNvbmFsbHkgZGlmZmVyZW5jZWQgZGF0YSBvZiB0aGUgbG9nIHRyYW5zZm9ybWF0aW9uIHdhcyB0aGUgbW9zdCByZWFzb25hYmx5IHN0YWJpbGl6ZWQuCgojIDkuNgoKU2ltdWxhdGUgYW5kIHBsb3Qgc29tZSBkYXRhIGZyb20gc2ltcGxlIEFSSU1BIG1vZGVscy4KClVzZSB0aGUgZm9sbG93aW5nIFIgY29kZSB0byBnZW5lcmF0ZSBkYXRhIGZyb20gYW4gQVIoMSkgbW9kZWwgd2l0aCAkXGVtcHR5c2V0XzE9MC42JAoKYW5kICRcc2lnbWFeMj0xJC4gVGhlIHByb2Nlc3Mgc3RhcnRzIHdpdGggJHlfMT0wJC4KCmBgYHtyfQp5IDwtIG51bWVyaWMoMTAwKQplIDwtIHJub3JtKDEwMCkKZm9yKGkgaW4gMjoxMDApCiAgeVtpXSA8LSAwLjYqeVtpLTFdICsgZVtpXQpzaW0gPC0gdHNpYmJsZShpZHggPSBzZXFfbGVuKDEwMCksIHkgPSB5LCBpbmRleCA9IGlkeCkKYGBgCgpgYGB7cn0KaGVhZChzaW0pCmBgYAoKCiMjIGIuCgpQcm9kdWNlIGEgdGltZSBwbG90IGZvciB0aGUgc2VyaWVzLiBIb3cgZG9lcyB0aGUgcGxvdCBjaGFuZ2UgYXMgeW91IGNoYW5nZSAkXHBoaV8xJD8KCmBgYHtyfQoKc2ltICU+JSBhdXRvcGxvdCh5KSArCiAgbGFicyh0aXRsZSA9IGV4cHJlc3Npb24oIkFSKDEpIG1vZGVsIHdpdGgiIH4gcGhpWzFdIH4gIj0iIH4gMC42IH4gIiwiIH4gc2lnbWFeMiB+ICI9IiB+IDEgfiAiLCIgfiB5WzFdIH4gIj0iIH4gMCkpCgpgYGAKCmBgYHtyfQoKZm9yKGkgaW4gMjoxMDApCiAgeVtpXSA8LSAuOCp5W2ktMV0gKyBlW2ldCnNpbTEgPC0gdHNpYmJsZShpZHggPSBzZXFfbGVuKDEwMCksIHkgPSB5LCBpbmRleCA9IGlkeCkKCmZvcihpIGluIDI6MTAwKQogIHlbaV0gPC0gMC4yKnlbaS0xXSArIGVbaV0Kc2ltMiA8LSB0c2liYmxlKGlkeCA9IHNlcV9sZW4oMTAwKSwgeSA9IHksIGluZGV4ID0gaWR4KQoKZm9yKGkgaW4gMjoxMDApCiAgeVtpXSA8LSAtMS4wKnlbaS0xXSArIGVbaV0Kc2ltMyA8LSB0c2liYmxlKGlkeCA9IHNlcV9sZW4oMTAwKSwgeSA9IHksIGluZGV4ID0gaWR4KQoKZm9yKGkgaW4gMjoxMDApCiAgeVtpXSA8LSAxLjQqeVtpLTFdICsgZVtpXQpzaW00IDwtIHRzaWJibGUoaWR4ID0gc2VxX2xlbigxMDApLCB5ID0geSwgaW5kZXggPSBpZHgpCgoKCgpwbHQxIDwtIHNpbTEgJT4lIGF1dG9wbG90KHkpICsKIGxhYnModGl0bGUgPSBleHByZXNzaW9uKCJBUigxKSBtb2RlbCB3aXRoIiB+IHBoaVsxXSB+ICI9IiB+IDAuOCB+ICIsIiB+IHNpZ21hXjIgfiAiPSIgfiAxICkpCnBsdDIgPC0gc2ltMiAlPiUgYXV0b3Bsb3QoeSkgKwogIGxhYnModGl0bGUgPSBleHByZXNzaW9uKCJBUigxKSBtb2RlbCB3aXRoIiB+IHBoaVsxXSB+ICI9IiB+IDAuMiB+ICIsIiB+IHNpZ21hXjIgfiAiPSIgfiAxICkpCnBsdDMgPC0gc2ltMyAlPiUgYXV0b3Bsb3QoeSkgKwogIGxhYnModGl0bGUgPSBleHByZXNzaW9uKCJBUigxKSBtb2RlbCB3aXRoIiB+IHBoaVsxXSB+ICI9IiB+IC0xLjAgfiAiLCIgfiBzaWdtYV4yIH4gIj0iIH4gMSApKQpwbHQ0IDwtIHNpbTQgJT4lIGF1dG9wbG90KHkpICsKICBsYWJzKHRpdGxlID0gZXhwcmVzc2lvbigiQVIoMSkgbW9kZWwgd2l0aCIgfiBwaGlbMV0gfiAiPSIgfiAxLjQgfiAiLCIgfiBzaWdtYV4yIH4gIj0iIH4gMSApKQoKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHBsdDEsIHBsdDIsIHBsdDMsIHBsdDQsIG5jb2wgPSAyLCBucm93ID0gMikKYGBgCgpBcyAkXHBoaV97MX0kIGluY3JlYXNlcyBvciBkZWNyZWFzZSwgdGhlIG1hZ25pdHVkZSBhbmQgd2F2ZWxlbmd0aCBtYXRjaGVzICAkXHBoaV97MX0kIGluY3JlYXNpbmcgYW5kIGRlY3JlYXNpbmcgaW4gdGhlIHNhbWUgZGlyZWN0aW9uLiBUaGlzIGlzIGFsc28gdHJ1ZSBmb3IgdGhlIG1hZ25pdHVkZSBhbmQgd2F2ZWxlbmd0aCBmb3IgdmFsdWVzIGJldHdlZW4gMCBhbmQgMSwgYnV0IGZvciAkXHBoaSQgdmFsdWVzIGdyZWF0ZXIgdGhhbiAxIHRoZSBwbG90IGVtdWxhdGVzIGFuIGV4cG9uZW50aWFsIGZ1bmN0aW9uICRmKHgpPWFeeCQsIHdoaWxlIHdhdmVsZW5ndGggc2hvcnRlbnMgZm9yIHZhbHVlcyBsZXNzIHRoYW4gMCwgbWVhbmluZyBhIGhpZ2hlciBmcmVxdWVuY3kgd2l0aCB2YXJ5aW5nIGFtcGxpdHVkZXMuCgoKIyMgYy4KCldyaXRlIHlvdXIgb3duIGNvZGUgdG8gZ2VuZXJhdGUgZGF0YSBmcm9tIGFuIE1BKDEpIG1vZGVsIHdpdGggJFx0aGV0YV8xJD0wLjYKYW5kICRcc2lnbWFeMj0xJC4KCmBgYHtyfQojIFJlZmVyZW5jZQojIHkgPC0gbnVtZXJpYygxMDApCiMgZSA8LSBybm9ybSgxMDApCiMgZm9yKGkgaW4gMjoxMDApCiMgICB5W2ldIDwtIDAuNip5W2ktMV0gKyBlW2ldCiMgc2ltIDwtIHRzaWJibGUoaWR4ID0gc2VxX2xlbigxMDApLCB5ID0geSwgaW5kZXggPSBpZHgpCmBgYAoKCmBgYHtyfQpmb3IoaSBpbiAyOjEwMCkKICB5W2ldIDwtIDAuNiplW2ktMV0gKyBlW2ldIAoKc2ltX21hIDwtIHRzaWJibGUoaWR4ID0gc2VxX2xlbigxMDApLCB5ID0geSwgaW5kZXggPSBpZHgpCmBgYAoKIyMgZC4KClByb2R1Y2UgYSB0aW1lIHBsb3QgZm9yIHRoZSBzZXJpZXMuIEhvdyBkb2VzIHRoZSBwbG90IGNoYW5nZSBhcyB5b3UgY2hhbmdlICRcdGhldGFfMSQ/CgpgYGB7cn0Kc2ltX21hICU+JSBhdXRvcGxvdCh5KSsKICBsYWJzKHRpdGxlID0gZXhwcmVzc2lvbigiQVIoMSkgbW9kZWwgd2l0aCIgfiBwaGlbMV0gfiAiPSIgfiAwLjYgfiAiLCIgfiBzaWdtYV4yIH4gIj0iIH4gMSkpCmBgYAoKYGBge3J9CiNjcmVhdGUgdGhlIGRhdGEKZm9yKGkgaW4gMjoxMDApCiAgeVtpXSA8LSAwKmVbaS0xXSArIGVbaV0gCnNpbTFfbWEgPC0gdHNpYmJsZShpZHggPSBzZXFfbGVuKDEwMCksIHkgPSB5LCBpbmRleCA9IGlkeCkKCmZvcihpIGluIDI6MTAwKQogIHlbaV0gPC0gLjgqZVtpLTFdICsgZVtpXSAKc2ltMl9tYSA8LSB0c2liYmxlKGlkeCA9IHNlcV9sZW4oMTAwKSwgeSA9IHksIGluZGV4ID0gaWR4KQoKZm9yKGkgaW4gMjoxMDApCiAgeVtpXSA8LSAtMS40KmVbaS0xXSArIGVbaV0gCnNpbTNfbWE8LSB0c2liYmxlKGlkeCA9IHNlcV9sZW4oMTAwKSwgeSA9IHksIGluZGV4ID0gaWR4KQoKZm9yKGkgaW4gMjoxMDApCiAgeVtpXSA8LSAxLjUqZVtpLTFdICsgZVtpXSAKc2ltNF9tYTwtIHRzaWJibGUoaWR4ID0gc2VxX2xlbigxMDApLCB5ID0geSwgaW5kZXggPSBpZHgpCgojY3JlYXRlIHRoZSBnZ3Bsb3Qgb2JqZWN0cwpwbHQ1IDwtIHNpbTFfbWEgJT4lIGF1dG9wbG90KHkpKwogIGxhYnModGl0bGUgPSBleHByZXNzaW9uKCJNQSgxKSBtb2RlbCB3aXRoIiB+IHBoaVsxXSB+ICI9IiB+IDAgfiAiLCIgfiBzaWdtYV4yIH4gIj0iIH4gMSkpCgoKcGx0NiA8LSBzaW0yX21hICU+JSBhdXRvcGxvdCh5KSsKICBsYWJzKHRpdGxlID0gZXhwcmVzc2lvbigiTUEoMSkgbW9kZWwgd2l0aCIgfiBwaGlbMV0gfiAiPSIgfiAwLjggfiAiLCIgfiBzaWdtYV4yIH4gIj0iIH4gMSkpCgpwbHQ3IDwtIHNpbTNfbWEgJT4lIGF1dG9wbG90KHkpKwogIGxhYnModGl0bGUgPSBleHByZXNzaW9uKCJNQSgxKSBtb2RlbCB3aXRoIiB+IHBoaVsxXSB+ICI9IiB+IC0xLjQgfiAiLCIgfiBzaWdtYV4yIH4gIj0iIH4gMSkpCgpwbHQ4IDwtIHNpbTRfbWEgJT4lIGF1dG9wbG90KHkpKwogIGxhYnModGl0bGUgPSBleHByZXNzaW9uKCJNQSgxKSBtb2RlbCB3aXRoIiB+IHBoaVsxXSB+ICI9IiB+IDEuNSB+ICIsIiB+IHNpZ21hXjIgfiAiPSIgfiAxKSkKCiNwbG90IHRoZW0gYWxsIHRvZ3RoZXIKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocGx0NSwgcGx0NiwgcGx0NywgcGx0OCwgbmNvbCA9IDIsIG5yb3cgPSAyKQpgYGAKCkFzICRcdGhldGEkIGNoYW5nZXMsIHRoZSBwbG90cyBiYXJlbHkgY2hhbmdlIHRvIGJlIGhvbmVzdC5UaGV5IGFsbCBzaG93IGEgc3RlYWR5IHZhcmlhbmNlIGFuZCBpbXBseWluZyB0aGUgTUEoMSkgc2VyaWVzIGFyZSBzdGF0aW9uYXJ5LiBBbXBsaXR1ZGUgbWluaW11bSBhbmQgbWF4aW11bSBoYXJkbHkgY2hhbmdlcyBhcyB3ZWxsIGFuZCB0aGVyZSBhcmUgbm8gc2hpZnRzIGluIHdhdmVsZW5ndGhzLgoKCiMjIGUuIAoKR2VuZXJhdGUgZGF0YSBmcm9tIGFuIEFSTUEoMSwxKSBtb2RlbCB3aXRoICRccGhpXzE9MC42JCwgJFx0aGV0YV8xPTAuNiQgYW5kICRcc2lnbWFeMj0xJAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CmZvcihpIGluIDI6MTAwKQogIHlbaV0gPC0gMC42KnlbaS0xXSArIDAuNiplW2ktMV0gKyBlW2ldCgpzaW1fYXJtYTExIDwtIHRzaWJibGUoaWR4ID0gc2VxX2xlbigxMDApLCB5ID0geSwgaW5kZXggPSBpZHgpCmBgYAoKIyMgZi4KCkdlbmVyYXRlIGRhdGEgZnJvbSBhbiBBUigyKSBtb2RlbCB3aXRoICRccGhpXzE94oiSMC44JCwgJFxwaGlfMj0wLjMkIGFuZCAkXHNpZ21hXjI9MSQuIChOb3RlIHRoYXQgdGhlc2UgcGFyYW1ldGVycyB3aWxsIGdpdmUgYSBub24tc3RhdGlvbmFyeSBzZXJpZXMuKQoKYGBge3J9CnBoaV8xPC0tMC44CnBoaV8yPC0wLjMKCgpmb3IoaSBpbiAzOjEwMCkKICB5W2ldIDwtIC1waGlfMSp5W2ktMV0gKyBwaGlfMip5W2ktMl0gKyBlW2ldCgpzaW1fYXJfOF8zIDwtIHRzaWJibGUoaWR4ID0gc2VxX2xlbigxMDApLCB5ID0geSwgaW5kZXggPSBpZHgpCmBgYAoKCiMjIGcuCgpHcmFwaCB0aGUgbGF0dGVyIHR3byBzZXJpZXMgYW5kIGNvbXBhcmUgdGhlbS4KCmBgYHtyfQpwbHQ5IDwtIHNpbV9hcm1hMTEgJT4lIGF1dG9wbG90KHkpKwogIGxhYnModGl0bGUgPSBleHByZXNzaW9uKCJBUk1BKDEsMSkgbW9kZWwgd2l0aCIgfiBwaGlbMV0gfiAiPSIgfiAwLjYgfiAiLCIgfiB0aGV0YVsxXSB+ICI9IiB+IDAuNiB+ICIsIiB+IHNpZ21hXjIgfiAiPSIgfiAxKSkKCnBsdDEwIDwtIHNpbV9hcl84XzMgJT4lIGF1dG9wbG90KHkpKwogIGxhYnModGl0bGUgPSBleHByZXNzaW9uKCJBUk1BIG1vZGVsIHdpdGgiIH4gcGhpWzFdIH4gIj0iIH4gLTAuOCB+ICIsIiB+IHRoZXRhWzFdIH4gIj0iIH4gMC4zIH4gIiwiIH4gc2lnbWFeMiB+ICI9IiB+IDEpKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocGx0OSwgcGx0MTAsIG5jb2wgPSAxLCBucm93ID0gMikKYGBgCgpBcm1hKDEsMSkgbW9kZWwgaGFzIGEgcGxvdCB0aGF0IGRvZXMgbm90IHNob3cgc2Vhc29uYWxpdHkgb3IgdHJlbmQsIHdpdGggYSBtYXhpbXVtIG9mIDMgYW5kIG1pbmltdW0gb2YgLTYsIHdoaWxlIHRoZSAybmQgQVJNQSBtb2RlbCBlbXVsYXRlcyBhIGV4cG9uZW50aWFsIGZ1Y250aW9uIGFzIG5vdGVkIGluIGEgcHJldmlvdXMgZXhhbXBsZS4KCiMgOS43CgpDb25zaWRlciBhdXNfYWlycGFzc2VuZ2VycywgdGhlIHRvdGFsIG51bWJlciBvZiBwYXNzZW5nZXJzIChpbiBtaWxsaW9ucykgZnJvbSBBdXN0cmFsaWFuIGFpciBjYXJyaWVycyBmb3IgdGhlIHBlcmlvZCAxOTcwLTIwMTEuCgpgYGB7cn0KbWF4KGF1c19haXJwYXNzZW5nZXJzJFllYXIpCgpgYGAKCmBgYHtyfQptaW4oYXVzX2FpcnBhc3NlbmdlcnMkWWVhcikKYGBgCgpgYGB7cn0KaGVhZChhdXNfYWlycGFzc2VuZ2VycykKYGBgCgoKIyMgYS4KClVzZSBBUklNQSgpIHRvIGZpbmQgYW4gYXBwcm9wcmlhdGUgQVJJTUEgbW9kZWwuIFdoYXQgbW9kZWwgd2FzIHNlbGVjdGVkLiBDaGVjayB0aGF0IHRoZSByZXNpZHVhbHMgbG9vayBsaWtlIHdoaXRlIG5vaXNlLiBQbG90IGZvcmVjYXN0cyBmb3IgdGhlIG5leHQgMTAgcGVyaW9kcy4KCmBgYHtyfQphdXNfYWlyX3BkXzcxXzExPC1hdXNfYWlycGFzc2VuZ2VycyAlPiUKICBmaWx0ZXIoWWVhcj49MTk3MSZZZWFyPD0yMDExKQoKZml0X2F1cyA8LSBhdXNfYWlyX3BkXzcxXzExJT4lCiAgbW9kZWwoQVJJTUEoUGFzc2VuZ2VycykpCgojZmluZCB0aGUgZml0CnJlcG9ydChmaXRfYXVzKQpgYGAKCmBgYHtyfQojZm9yZWNhc3QgMTAgcGVyaW9kcwpmaXRfYXVzICU+JSBmb3JlY2FzdChoPTEwKSAlPiUKICBhdXRvcGxvdChhdXNfYWlycGFzc2VuZ2VycykgKwogIGxhYnMoeSA9ICJQYXNzZW5nZXJzIChNaWxsaW9ucykiLCAKICAgICAgIHRpdGxlID0gIkF1c3RyYWxpYW4gQWlyIFBhc3NlbmdlcnMgKDE5NzEtMjAxMSkiLAogICAgICAgc3VidGl0bGUgPSAiMTAgWWVhciBGb3JlY2FzdCIpCmBgYAoKCmBgYHtyfQpmaXRfYXVzICU+JSBnZ190c3Jlc2lkdWFscygpICsgCiAgbGFicyh0aXRsZSA9ICJBdXN0cmFsaWFuIEFpciBQYXNzZW5nZXJzICgxOTcxLTIwMTEpIiwKICAgICAgIHN1YnRpdGxlID0gIkF1c3RyYWxpYW4gQWlyIFBhc3NlbmdlcnMgKDE5NzEtMjAxMSkiKQpgYGAKCkZ1bmN0aW9uIGBBUklNQSgpYCBhdXRvbWF0aWNhbGx5IHNlbGVjdGVkIGZvciBgYXVzX2FpcnBhc3NlbmdlcnNgIGRhdGEgbW9kZWwsIHdoaWNoIHdhcyBhbiBBUklNQSgwLDIsMSkuIEZ1bmN0aW9uIGBnZ190c3Jlc2lkdWFscygpYCBjb25maXJtcyB0aGVyZSBpcyB3aGl0ZSBub2lzZSBiYXNlIG9uIHRoZSByZXNpZHVhbHMuCgoKIyMgYi4KCldyaXRlIHRoZSBtb2RlbCBpbiB0ZXJtcyBvZiB0aGUgYmFja3NoaWZ0IG9wZXJhdG9yLgoKJHlfdCA9IC0wLjg5NjMgKiBcZXBzaWxvbl97dC0xfSArIFxlcHNpbG9uX3t0fSQKCiMjIGMuCgpQbG90IGZvcmVjYXN0cyBmcm9tIGFuIEFSSU1BKDAsMSwwKSBtb2RlbCB3aXRoIGRyaWZ0IGFuZCBjb21wYXJlIHRoZXNlIHRvIHBhcnQgYS4KCmBgYHtyfQpmaXRfYXVzXzAxMCA8LSBhdXNfYWlyX3BkXzcxXzExICU+JQogIG1vZGVsKEFSSU1BKFBhc3NlbmdlcnMgfiBwZHEoMCwxLDApKSkKCnJlcG9ydChmaXRfYXVzXzAxMCkKYGBgCgoKYGBge3J9CgojcGxvdCBmb3JlY2FzdApmaXRfYXVzXzAxMCAlPiUgZm9yZWNhc3QoaD0xMCkgJT4lCiAgYXV0b3Bsb3QoYXVzX2Fpcl9wZF83MV8xMSkgKwogIGxhYnMoeSA9ICJQYXNzZW5nZXJzIChNaWxsaW9ucyIsIAogICAgICAgdGl0bGUgPSAiQXVzdHJhbGlhbiBBaXIgUGFzc2VuZ2VycyAoMTk3MS0yMDExKSIsCiAgICAgICBzdWJ0aXRsZSA9ICIxMCBZZWFyIEZvcmVjYXN0IikKCiNwbG90IHJlc2lkdWFscwpmaXRfYXVzXzAxMCAlPiUgZ2dfdHNyZXNpZHVhbHMoKSArCiAgbGFicyh0aXRsZSA9ICJBdXN0cmFsaWFuIEFpciBQYXNzZW5nZXJzICgxOTcxLTIwMTEpIiwKICAgICAgIHN1YnRpdGxlID0gIjEwIFllYXIgRm9yZWNhc3QiKQpgYGAKClBhcnQgQSBmb3JlY2FzdHMgaGlnaGVyLCBhbmQgcGFydCBDIHJlc2lkdWFscyBzaG93IHdoaXRlIG5vaXNlLgoKIyMgZC4KClBsb3QgZm9yZWNhc3RzIGZyb20gYW4gQVJJTUEoMiwxLDIpIG1vZGVsIHdpdGggZHJpZnQgYW5kIGNvbXBhcmUgdGhlc2UgdG8gcGFydHMgYSBhbmQgYy4gUmVtb3ZlIHRoZSBjb25zdGFudCBhbmQgc2VlIHdoYXQgaGFwcGVucy4KCmBgYHtyfQpmaXRfYXVzMjEyIDwtIGF1c19haXJfcGRfNzFfMTEgJT4lCiAgbW9kZWwoQVJJTUEoUGFzc2VuZ2VycyB+IHBkcSgyLDEsMikpKQoKYGBgCgpgYGB7cn0KZml0X2F1czIxMl9ub19jb25zdCA8LSBhdXNfYWlycGFzc2VuZ2VycyAlPiUKICBtb2RlbChBUklNQShQYXNzZW5nZXJzIH4gcGRxKDIsMSwyKSxpbmNsdWRlLmNvbnN0YW50PUZBTFNFKSkgCmBgYAoKCmBgYHtyfQpyZXBvcnQoZml0X2F1czIxMikKYGBgCgoKCmBgYHtyfQojcGxvdCBmb3JlY2FzdApmaXRfYXVzMjEyICU+JSBmb3JlY2FzdChoPTEwKSAlPiUKICBhdXRvcGxvdChhdXNfYWlyX3BkXzcxXzExKSArCiAgbGFicyh5ID0gIlBhc3NlbmdlcnMgKE1pbGxpb25zIiwgCiAgICAgICB0aXRsZSA9ICJBdXN0cmFsaWFuIEFpciBQYXNzZW5nZXJzICgxOTcxLTIwMTEpIiwKICAgICAgIHN1YnRpdGxlID0gIjEwIFllYXIgRm9yZWNhc3QiKQoKI3Bsb3QgcmVzaWR1YWxzCmZpdF9hdXMyMTIgJT4lIGdnX3RzcmVzaWR1YWxzKCkgKwogIGxhYnModGl0bGUgPSAiQXVzdHJhbGlhbiBBaXIgUGFzc2VuZ2VycyAoMTk3MS0yMDExKSIsCiAgICAgICBzdWJ0aXRsZSA9ICIxMCBZZWFyIEZvcmVjYXN0IikKYGBgCgpUaGlzIG1vZGVsIGNvbXBhcmVkIHRvIHBhcnQgYSBhbmQgYiBzZWVtcyB3b3JzZSBhbmQgaGFzIHdoaXRlIG5vaXNlLiBSZW1vdmluZyB0aGUgY29uc3RhbnQgcHJvZHVjZWQgYW4gZXJyb3IuCgojIyBlLgoKUGxvdCBmb3JlY2FzdHMgZnJvbSBhbiBBUklNQSgwLDIsMSkgbW9kZWwgd2l0aCBhIGNvbnN0YW50LiBXaGF0IGhhcHBlbnM/CgpzZWN0aW9uIDkuNyBhIGRvZXMgdGhpcyBhbHJlYWR5LiBJdCBpcyB0aGUgYmVzdCBmaXR0aW5nIG1vZGVsLgoKIyA5LjgKCkZvciB0aGUgVW5pdGVkIFN0YXRlcyBHRFAgc2VyaWVzIChmcm9tIGdsb2JhbF9lY29ub215KToKCmBgYHtyfQpkZl91czwtZ2xvYmFsX2Vjb25vbXklPiUKICAgICAgICBmaWx0ZXIoQ291bnRyeSA9PSJVbml0ZWQgU3RhdGVzIikKYGBgCgoKIyMgYS4KCmlmIG5lY2Vzc2FyeSwgZmluZCBhIHN1aXRhYmxlIEJveC1Db3ggdHJhbnNmb3JtYXRpb24gZm9yIHRoZSBkYXRhOwoKYGBge3J9CgpkZl91cyAlPiUgYXV0b3Bsb3QoR0RQKSArCiAgbGFicyh0aXRsZSA9ICJVUyBHRFAiKQpgYGAKCk5vIG5lZWQgZm9yIGEgdHJhbnNmb3JtYXRpb24sIHRoZSBkYXRhIGRvZXNuJ3QgdmFyeS4KCiMjIGIuCgpmaXQgYSBzdWl0YWJsZSBBUklNQSBtb2RlbCB0byB0aGUgdHJhbnNmb3JtZWQgZGF0YSB1c2luZyBBUklNQSgpOwoKYGBge3J9CmZpdF91cyA8LSBkZl91cyAlPiUKICBtb2RlbCgKICAgIGFyaW1hID0gQVJJTUEoR0RQLCBzdGVwd2lzZSA9IEZBTFNFLCBhcHByb3ggPSBGQUxTRSkpCgpyZXBvcnQoZml0X3VzKQpgYGAKCk1vZGVsOiBBUklNQSgwLDIsMikgYWNjb3JkaW5nIHRvIEFSSU1BKCkKCiMjIGMuCgp0cnkgc29tZSBvdGhlciBwbGF1c2libGUgbW9kZWxzIGJ5IGV4cGVyaW1lbnRpbmcgd2l0aCB0aGUgb3JkZXJzIGNob3NlbjsKCmBgYHtyfQpkZl91cyAlPiUKICBmZWF0dXJlcyhHRFAsIHVuaXRyb290X25kaWZmcykKCmRmX3VzICU+JQogIGdnX3RzZGlzcGxheShHRFAsIHBsb3RfdHlwZSA9ICdwYXJ0aWFsJykKCmZpdF91czIyMiA8LSBkZl91cyAlPiUKICBtb2RlbChBUklNQShHRFAgfiBwZHEoMiwyLDIpKSkKCmZpdF91czExMiA8LSBkZl91cyAlPiUKICBtb2RlbChBUklNQShHRFAgfiBwZHEoMSwxLDIpKSkKCmZpdF91czIxMSA8LSBkZl91cyAlPiUKICBtb2RlbChBUklNQShHRFAgfiBwZHEoMiwxLDEpKSkKCmZpdF91czIyMSA8LSBkZl91cyAlPiUKICBtb2RlbChBUklNQShHRFAgfiBwZHEoMiwyLDEpKSkKCnJlcG9ydChmaXRfdXMyMjIpCnJlcG9ydChmaXRfdXMxMTIpCnJlcG9ydChmaXRfdXMyMTEpCnJlcG9ydChmaXRfdXMyMjEpCmBgYAoKLSBBSUMsIEFJQ2MsIGFuZCBCSUMgdmFsdWVzIGluY3JlYXNlIHdoZW4gaW5jcmVhc2luZyBxIHZhbHVlIGZyb20gMiB0byAxLgotIEFJQywgQUlDYywgYW5kIEJJQyB2YWx1ZXMgZGVjcmVhc2VkIHdoZW4gZGVjcmVhc2luZyBxIHZhbHVlcy4KLSAyLDIsMiBpcyB0aGUgYmVzdCBmaXQgZnJvbSB0aGUgY29tYmluYXRpb25zLgoKCiMjIGQuCgpjaG9vc2Ugd2hhdCB5b3UgdGhpbmsgaXMgdGhlIGJlc3QgbW9kZWwgYW5kIGNoZWNrIHRoZSByZXNpZHVhbCBkaWFnbm9zdGljczsKCkFSSU1BKDIsMiwyKSAtIEJ1dCB0aGUgQUNGIGxvb2tzIGxpa2Ugd2hpdGUgbm9pc2UgYW5kIHJlc2lkdWFscyBpcyBsZWZ0IHNrZXdlZAoKYGBge3J9CmZpdF91czIyMiAlPiUgZ2dfdHNyZXNpZHVhbHMoKSArCiAgbGFicyh0aXRsZSA9ICJBUklNQSgyLDIsMikiKQpgYGAKCgojIyBlLgoKcHJvZHVjZSBmb3JlY2FzdHMgb2YgeW91ciBmaXR0ZWQgbW9kZWwuIERvIHRoZSBmb3JlY2FzdHMgbG9vayByZWFzb25hYmxlPwoKYGBge3J9CmZpdF91czIyMiAlPiUgZm9yZWNhc3QoaD0xMCkgJT4lCiAgYXV0b3Bsb3QoZGZfdXMpICsKICBsYWJzKHRpdGxlID0gIkFSSU1BKDIsMiwyKSIpCmBgYAoKUHJldHR5IG11Y2ggcmVhc29uYWJsZSBiYXNlIG9uIHdoYXQgb3VyIHByZXZpb3VzIGRhdGEgc2hvd2VkLgoKIyMgZi4KCmNvbXBhcmUgdGhlIHJlc3VsdHMgd2l0aCB3aGF0IHlvdSB3b3VsZCBvYnRhaW4gdXNpbmcgRVRTKCkgKHdpdGggbm8gdHJhbnNmb3JtYXRpb24pLgoKYGBge3J9CmZpdF9ldHMgPC0gZGZfdXMgJT4lIAogIG1vZGVsKEVUUyhHRFApKQoKcmVwb3J0KGZpdF9ldHMpCmBgYAoKYGBge3J9CmZpdF9ldHMgJT4lIGZvcmVjYXN0KGggPSAxMCkgJT4lCiAgYXV0b3Bsb3QoZGZfdXMpIApgYGAKCmBgYHtyfQpmaXRfZXRzICU+JSBnZ190c3Jlc2lkdWFscygpCmBgYAoKCgoKVGhlIGNvbXBhcmlzb24gZm9yIEFSSU1BKDIsMiwyKSBBSUNjPTMwNTMuNDcgIHZzLiBFVFMoTSxBLE4pIEFJQ2M9MzE5MS45NDEsIEFSSU1BKDIsMiwyKSBwZXJmb3JtcyBiZXR0ZXIgYW5kIGZvcmVjYXN0cyBhcmUgZ29vZCBmb3IgYm90aCBjb25zaWRlcmluZyB0aGUgQUNGIHBsb3RzIGxhY2sgY29ycmVsYXRpb24uCgo=