library(tidyverse)
library(patchwork)
library(zoo)
library(caret)
Loading required package: lattice
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Attaching package: ‘caret’
The following object is masked from ‘package:purrr’:
lift
dir()
[1] "ADA-USD.csv" "BTC-USD.csv" "DOGE-USD.csv" "ETH-USD.csv"
[5] "XGBoost.nb.html" "XGBoost.Rmd"
1. Import các dataset
Có 4 loại tiền điện tử cần phần tích:
- Bitcoin
- Dogecoin
- Ethereum
- Cardano
Khái niệm:
Bitcoin is a digital currency which operates free of
any central control or the oversight of banks or governments. Instead it
relies on peer-to-peer software and cryptography. A public ledger
records all bitcoin transactions and copies are held on servers around
the world.
Dogecoin is primarily used for tipping users on
Reddit and Twitter, but it is also accepted as a method of payment by a
few dozen merchants. It can be used to buy food, household supplies and
even website domains.
Ethereum operates on a decentralized computer
network, or distributed ledger called a blockchain, which manages and
tracks the currency. It can be useful to think of a blockchain like a
running receipt of every transaction that’s ever taken place in the
cryptocurrency.
Cardano: The cardano blockchain can be used to build
smart contracts, and in turn, create decentralized applications and
protocols. Additionally, the ability to send and receive funds instantly
through, for minimal fees, have many applications in the world of
business and finance.
1.1. Bitcoin
bitcoindf <-
read.csv(
'./BTC-USD.csv',
col.names = c("date", "open", "high", "low", "close", "adj_close", "volume")
)
head(bitcoindf)
1.2. Dogecoin
dogecoindf <-
read.csv(
'DOGE-USD.csv',
col.names = c("date", "open", "high", "low", "close", "adj_close", "volume")
)
head(dogecoindf)
1.3. Ethereum
ethereumdf <-
read.csv(
'ETH-USD.csv',
col.names = c("date", "open", "high", "low", "close", "adj_close", "volume")
)
head(ethereumdf)
### 1.4. Cardano
cardanodf <-
read.csv(
'ADA-USD.csv',
col.names = c("date", "open", "high", "low", "close", "adj_close", "volume")
)
head(cardanodf)
Preprocessing
Bài viết gốc sử dụng forward fill của pandas, đối với R có thể dùng
hàm fill của package tidyverse
bitcoindf <- bitcoindf %>% fill(open, close, .direction = 'down')
dogecoindf <- dogecoindf %>% fill(open, close, .direction = 'down')
ethereumdf <- ethereumdf %>% fill(open, close, .direction = 'down')
cardanodf <- cardanodf %>% fill(open, close, .direction = 'down')
Convert others column into numeric
bitcoindf <- bitcoindf %>% mutate_if(is.character, as.numeric)
Warning: There were 6 warnings in `mutate()`.
The first warning was:
ℹ In argument: `open = .Primitive("as.double")(open)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 5 remaining warnings.
dogecoindf <- dogecoindf %>% mutate_if(is.character, as.numeric)
Warning: There were 6 warnings in `mutate()`.
The first warning was:
ℹ In argument: `open = .Primitive("as.double")(open)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 5 remaining warnings.
ethereumdf <- ethereumdf %>% mutate_if(is.character, as.numeric)
Warning: There were 6 warnings in `mutate()`.
The first warning was:
ℹ In argument: `open = .Primitive("as.double")(open)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 5 remaining warnings.
cardanodf <- cardanodf %>% mutate_if(is.character, as.numeric)
Warning: There were 6 warnings in `mutate()`.
The first warning was:
ℹ In argument: `open = .Primitive("as.double")(open)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 5 remaining warnings.
Visualization
Plotting close price of Bitcoin, Cardano, Dogecoin and Ethereum
line_bitcoindf <- bitcoindf %>%
ggplot(aes(x = date, y = close)) +
geom_line(col = "red") +
labs(
title = "Bitcoin Close Price",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)
line_cardanodf <- cardanodf %>%
ggplot(aes(x = date, y = close)) +
geom_line() +
labs(
title = "Cardano Close Price",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)
line_dogecoindf <- dogecoindf %>%
ggplot(aes(x = date, y = close)) +
geom_line(col = "orange") +
labs(
title = "Dogecoin Close Price",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)
line_ethereumdf <- ethereumdf %>%
ggplot(aes(x = date, y = close)) +
geom_line(col = "darkgreen") +
labs(
title = "Dogecoin Close Price",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)
line_bitcoindf + line_cardanodf + line_dogecoindf + line_ethereumdf

Plotting only 2020-2021 year close price of Bitcoin, Cardano,
Dogecoin and Ethereum
last1year_bitcoindf <- bitcoindf %>%
filter(date > as.Date("01-09-2020", format = "%d-%m-%Y"))
last1year_cardanodf <- cardanodf %>%
filter(date > as.Date("01-09-2020", format = "%d-%m-%Y"))
last1year_dogecoindf <- dogecoindf %>%
filter(date > as.Date("01-09-2020", format = "%d-%m-%Y"))
last1year_ethereumdf <- ethereumdf %>%
filter(date > as.Date("01-09-2020", format = "%d-%m-%Y"))
line_bitcoindf <- last1year_bitcoindf %>%
ggplot(aes(x = date, y = close)) +
geom_line(col = "red") +
labs(
title = "Bitcoin Close Price",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)
line_cardanodf <- last1year_cardanodf %>%
ggplot(aes(x = date, y = close)) +
geom_line() +
labs(
title = "Cardano Close Price",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)
line_dogecoindf <- last1year_dogecoindf %>%
ggplot(aes(x = date, y = close)) +
geom_line(col = "orange") +
labs(
title = "Dogecoin Close Price",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)
line_ethereumdf <- last1year_ethereumdf %>%
filter(date > as.Date("01-09-2020", format = "%d-%m-%Y")) %>%
ggplot(aes(x = date, y = close)) +
geom_line(col = "darkgreen") +
labs(
title = "Ethereum Close Price",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)
line_bitcoindf +
line_cardanodf +
line_dogecoindf +
line_ethereumdf +
plot_layout(nrow = 4)

Plotting only 2020-2021 year volume of Bitcoin, Cardano, Dogecoin and
Ethereum
ggplot(NULL) +
geom_line(aes(last1year_bitcoindf$date, last1year_bitcoindf$volume, col = "bitcoin")) +
geom_line(aes(last1year_cardanodf$date, last1year_cardanodf$volume, col = "cardanod")) +
geom_line(aes(last1year_dogecoindf$date, last1year_dogecoindf$volume, col = "dogecoin")) +
geom_line(aes(last1year_ethereumdf$date, last1year_ethereumdf$volume, col = "ethereum")) +
theme_bw() +
labs(
title = "Volume of Bitcoin, Cardano, Dogecoin, Ethereum",
x = element_blank(),
y = element_blank()
) +
scale_color_manual(
name=element_blank(),
breaks=c('bitcoin', 'cardanod', 'dogecoin', "ethereum"),
values=c('bitcoin'='red', 'cardanod'='black', 'dogecoin'='orange', 'ethereum' = "darkgreen")
)

Plotting last month open and close price of Bitcoin, Cardano,
Dogecoin and Ethereum with comparision
last1month_bitcoindf <-bitcoindf %>%
filter(date > as.Date("01-08-2021", format = "%d-%m-%Y"))
last1month_cardanodf <- cardanodf %>%
filter(date > as.Date("01-08-2021", format = "%d-%m-%Y"))
last1month_dogecoindf <- dogecoindf %>%
filter(date > as.Date("01-08-2021", format = "%d-%m-%Y"))
last1month_ethereumdf <- ethereumdf %>%
filter(date > as.Date("01-08-2021", format = "%d-%m-%Y"))
last1month_bitcoindf %>%
ggplot(aes(date)) +
geom_line(aes(y = close, col = "Close")) +
geom_line(aes(y = open, col = "Open")) +
labs(
col = element_blank(),
title = "bitcoin",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9) +
last1month_cardanodf %>%
ggplot(aes(date)) +
geom_line(aes(y = close, col = "C"), show.legend = F) +
geom_line(aes(y = open, col = "O"), show.legend = F) +
labs(
col = element_blank(),
title = "cardano",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9) +
last1month_dogecoindf %>%
ggplot(aes(date)) +
geom_line(aes(y = close, col = "C"), show.legend = F) +
geom_line(aes(y = open, col = "O"), show.legend = F) +
labs(
col = element_blank(),
title = "dogecoin",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9) +
last1month_ethereumdf %>%
ggplot(aes(date)) +
geom_line(aes(y = close, col = "C"), show.legend = F) +
geom_line(aes(y = open, col = "O"), show.legend = F) +
labs(
col = element_blank(),
title = "ethereum",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9) +
plot_layout(ncol = 1)

Moving Averages
As we know the stock prices are highly volatile and prices change
quickly with time. To observe any trend or pattern we can take the help
of a 50-day 200-day average.
bitcoindf <- bitcoindf %>% fill(open, close, .direction = 'down')
dogecoindf <- dogecoindf %>% fill(open, close, .direction = 'down')
ethereumdf <- ethereumdf %>% fill(open, close, .direction = 'down')
cardanodf <- cardanodf %>% fill(open, close, .direction = 'down')
bitcoindf %>%
ggplot(aes(x = date)) +
geom_line(aes(y = rollmean(close, 50, fill = NA, align = "right")), col = "blue") +
geom_line(aes(y = rollmean(close, 200, fill = NA, align = "right")), col = "orange") +
labs(
title = "Bitcoin Close Price moving average",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9) +
cardanodf %>%
ggplot(aes(x = date)) +
geom_line(aes(y = rollmean(close, 50, fill = NA, align = "right")), col = "black") +
geom_line(aes(y = rollmean(close, 200, fill = NA, align = "right")), col = "red") +
labs(
title = "Bitcoin Close Price moving average",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9) +
dogecoindf %>%
ggplot(aes(x = date)) +
geom_line(aes(y = rollmean(close, 50, fill = NA, align = "right")), col = "orange") +
geom_line(aes(y = rollmean(close, 200, fill = NA, align = "right")), col = "grey") +
labs(
title = "Bitcoin Close Price moving average",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9) +
ethereumdf %>%
ggplot(aes(x = date)) +
geom_line(aes(y = rollmean(close, 50, fill = NA, align = "right")), col = "green") +
geom_line(aes(y = rollmean(close, 200, fill = NA, align = "right")), col = "blue") +
labs(
title = "Bitcoin Close Price moving average",
x = element_blank(),
y = element_blank()
) +
theme_bw(base_size = 9)

Plotting histogram with mean indicator of all four
cryptocurrencies
bitcoindf %>%
ggplot(aes(close)) +
geom_histogram(fill ="darkred", alpha = 0.5, col = "black") +
ylab("count") +
theme_bw(base_size = 9) +
annotate("text", label = "Bitcoin Close Price", 50000, 400) +
cardanodf %>%
ggplot(aes(close)) +
geom_histogram(fill ="darkgreen", alpha = 0.5, col = "black") +
ylab("count") +
theme_bw(base_size = 9) +
annotate("text", label = "Cardano Close Price", 2.3, 200) +
dogecoindf %>%
ggplot(aes(close)) +
geom_histogram(fill ="grey", bins = 400) +
theme_bw(base_size = 9) +
ylab("count") +
annotate("text", label = "Dogecoin Close Price", 0.52, 200) +
ethereumdf %>%
ggplot(aes(close)) +
geom_histogram(fill ="darkorange", alpha = 0.5, col = "black") +
theme_bw(base_size = 9) +
annotate("text", label = "Ethereum Close Price", 3100, 400) +
plot_layout(ncol = 1)

bitcoindf %>%
ggplot(aes(date)) +
geom_histogram() +
ylab(element_blank()) +
theme_bw() +
bitcoindf %>%
ggplot(aes(open)) +
geom_histogram() +
ylab(element_blank()) +
theme_bw() +
bitcoindf %>%
ggplot(aes(high)) +
geom_histogram() +
ylab(element_blank()) +
theme_bw() +
bitcoindf %>%
ggplot(aes(low)) +
geom_histogram() +
ylab(element_blank()) +
theme_bw() +
bitcoindf %>%
ggplot(aes(close)) +
geom_histogram() +
ylab(element_blank()) +
theme_bw() +
bitcoindf %>%
ggplot(aes(adj_close)) +
geom_histogram() +
ylab(element_blank()) +
theme_bw() +
bitcoindf %>%
ggplot(aes(volume)) +
geom_histogram() +
ylab(element_blank()) +
theme_bw()

rm(cardanodf)
rm(dogecoindf)
rm(ethereumdf)
rm(last1month_bitcoindf)
rm(last1month_cardanodf)
rm(last1month_dogecoindf)
rm(last1month_ethereumdf)
rm(last1year_bitcoindf)
rm(last1year_cardanodf)
rm(last1year_dogecoindf)
rm(last1year_ethereumdf)
rm(line_bitcoindf)
rm(line_cardanodf)
rm(line_dogecoindf)
rm(line_ethereumdf)
Bitcoin Close Price Prediction using XGBoost
Dataframe of bitcoin close price
closedf <- select(bitcoindf, date, close)
cat("Shape of close dataframe:", dim(closedf))
Shape of close dataframe: 2193 2
closedf <- closedf %>% filter(date > as.Date('2020-09-13'))
close_stock <- closedf
cat("Total data for prediction: ", nrow(closedf))
Total data for prediction: 365
Normalizing close price value
closedf <-
predict(select(closedf, close) %>% preProcess(method = "range"),
select(closedf, close))
dim(closedf)
[1] 365 1
Separate data for Train and Test
training_size <-round(nrow(closedf)*0.70)
train_data <- data.frame(close = closedf[1:training_size, ])
test_data <- data.frame(close = closedf[(training_size + 1):nrow(closedf), ])
cat("train_data: ", dim(train_data))
train_data: 255 1
cat("test_data: ", dim(test_data))
test_data: 110 1
ggplot(NULL) +
geom_line(
aes(
x = close_stock$date[1:training_size],
y = train_data$close,
col = "train"
)
) +
geom_line(
aes(
x = close_stock$date[(training_size + 1):nrow(closedf)],
y = test_data$close,
col = "test"
)
) +
scale_color_manual(
breaks = c("train", "test"),
values = c("train" = "black", "test" = "red")
) +
labs(title = 'Train & Test data', x = 'Date', y = 'Weekly Sales', col = element_blank()) +
theme_bw()

create_dataset <- function (dataset, time_step=1) {
dataX <- list()
dataY <- list()
for (i in 1:(nrow(dataset)-time_step-1)) {
a <- dataset[i:(i+time_step-1), 1]
dataX[[i]] <- a
dataY[[i]] <- dataset[i + time_step, 1]
}
dataX <- matrix(unlist(dataX), ncol = time_step, byrow = TRUE)
colnames(dataX) <- paste0("V", 1:time_step)
dataY <- unlist(dataY)
return(list(dataX = dataX, dataY = dataY))
}
train_set <- create_dataset(train_data, time_step = 15)
X_train <- train_set[[1]]
Y_train <- train_set[[2]]
rm(train_set)
test_set <- create_dataset(test_data, time_step = 15)
X_test <- test_set[[1]]
Y_test <- test_set[[2]]
rm(test_set)
cat("X_train: ", dim(X_train))
X_train: 239 15
cat("Y_train: ", dim(Y_train))
Y_train:
cat("X_test: ", dim(X_test))
X_test: 94 15
cat("Y_test", dim(Y_test))
Y_test
xgb_trcontrol <- caret::trainControl(
method = "cv",
number = 5,
allowParallel = TRUE,
verboseIter = FALSE,
returnData = FALSE
)
xgb_grid <- base::expand.grid(
list(
nrounds=1000,
# base_score=0.5,
# colsample_bylevel=1,
# colsample_bynode=1,
colsample_bytree = 1, # subsample ratio of columns when construction each tree
# # nrounds = 1000,
max_depth = 6, # maximum depth of a tree
eta = 0.3, # learning rate
gamma = 0, # minimum loss reduction
min_child_weight = 1, # minimum sum of instance weight (hessian) needed ina child
subsample = 1 # subsample ratio of the training instances
# tree_method='exact',
# interaction_constraints='',
# max_delta_step=0,
# n_estimators=1000,
# validate_parameters=1,
# random_state=0,
# n_jobs=4
))
xgb_model <- train(
X_train, Y_train,
trControl = xgb_trcontrol,
tuneGrid = xgb_grid,
method = "xgbTree",
nthread = 1
)
predicted <- predict(xgb_model, X_test)
cat("Mean Absolute Error - MAE : ", MAE(Y_test, predicted))
Mean Absolute Error - MAE : 0.02658388
cat("Root Mean squared Error - RMSE : ", RMSE(Y_test, predicted))
Root Mean squared Error - RMSE : 0.03641536
train_predict <- predict(xgb_model, X_train)
test_predict <- predict(xgb_model, X_test)
descaler <- function(xv, mx, nx) {
dvals <- xv * (mx - nx) + nx
dvals <- round(dvals, digits = 5)
return(dvals)
}
ggplot(NULL) +
geom_line(aes(
x = close_stock$date,
y = closedf$close %>% descaler(max(close_stock$close), min(close_stock$close)),
col = 'Original close price'
),) +
geom_line(
aes(
x = close_stock$date[17:training_size - 1],
y = train_predict %>% descaler(max(close_stock$close), min(close_stock$close)),
col = 'Train predicted close price'),
) +
geom_line(
aes(
x = close_stock$date[(training_size + 16):(nrow(close_stock) - 1)],
y = test_predict %>% descaler(max(close_stock$close), min(close_stock$close)),
col = 'Test predicted close price'),
col = "green"
) +
scale_color_manual(
breaks = c(
'Original close price',
'Train predicted close price',
'Test predicted close price'
),
values = c(
'Original close price' = "blue",
'Train predicted close price' = "red",
'Test predicted close price' = "green"
),
) +
theme_bw() +
labs(
x = 'Date',
y = 'Close price',
title = 'Comparision between original close price vs predicted close price',
col = element_blank()
)

LS0tCnRpdGxlOiAiW0thZ2dsZV0gQml0Y29pbixkb2dlY29pbixldGMgcHJpY2UgcHJlZGljdGlvbiAtWEdCb29zdCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KHpvbykKbGlicmFyeShjYXJldCkKYGBgCgpgYGB7cn0KZGlyKCkKYGBgCgojIyAxLiBJbXBvcnQgY8OhYyBkYXRhc2V0CgpDw7MgNCBsb+G6oWkgdGnhu4FuIMSRaeG7h24gdOG7rSBj4bqnbiBwaOG6p24gdMOtY2g6CgotIEJpdGNvaW4KLSBEb2dlY29pbgotIEV0aGVyZXVtCi0gQ2FyZGFubwoKKipLaMOhaSBuaeG7h206KioKCioqQml0Y29pbioqIGlzIGEgZGlnaXRhbCBjdXJyZW5jeSB3aGljaCBvcGVyYXRlcyBmcmVlIG9mIGFueSBjZW50cmFsIGNvbnRyb2wgb3IgdGhlIG92ZXJzaWdodCBvZiBiYW5rcyBvciBnb3Zlcm5tZW50cy4gSW5zdGVhZCBpdCByZWxpZXMgb24gcGVlci10by1wZWVyIHNvZnR3YXJlIGFuZCBjcnlwdG9ncmFwaHkuIEEgcHVibGljIGxlZGdlciByZWNvcmRzIGFsbCBiaXRjb2luIHRyYW5zYWN0aW9ucyBhbmQgY29waWVzIGFyZSBoZWxkIG9uIHNlcnZlcnMgYXJvdW5kIHRoZSB3b3JsZC4KCioqRG9nZWNvaW4qKiBpcyBwcmltYXJpbHkgdXNlZCBmb3IgdGlwcGluZyB1c2VycyBvbiBSZWRkaXQgYW5kIFR3aXR0ZXIsIGJ1dCBpdCBpcyBhbHNvIGFjY2VwdGVkIGFzIGEgbWV0aG9kIG9mIHBheW1lbnQgYnkgYSBmZXcgZG96ZW4gbWVyY2hhbnRzLiBJdCBjYW4gYmUgdXNlZCB0byBidXkgZm9vZCwgaG91c2Vob2xkIHN1cHBsaWVzIGFuZCBldmVuIHdlYnNpdGUgZG9tYWlucy4KCioqRXRoZXJldW0qKiBvcGVyYXRlcyBvbiBhIGRlY2VudHJhbGl6ZWQgY29tcHV0ZXIgbmV0d29yaywgb3IgZGlzdHJpYnV0ZWQgbGVkZ2VyIGNhbGxlZCBhIGJsb2NrY2hhaW4sIHdoaWNoIG1hbmFnZXMgYW5kIHRyYWNrcyB0aGUgY3VycmVuY3kuIEl0IGNhbiBiZSB1c2VmdWwgdG8gdGhpbmsgb2YgYSBibG9ja2NoYWluIGxpa2UgYSBydW5uaW5nIHJlY2VpcHQgb2YgZXZlcnkgdHJhbnNhY3Rpb24gdGhhdCdzIGV2ZXIgdGFrZW4gcGxhY2UgaW4gdGhlIGNyeXB0b2N1cnJlbmN5LgoKKipDYXJkYW5vKio6IFRoZSBjYXJkYW5vIGJsb2NrY2hhaW4gY2FuIGJlIHVzZWQgdG8gYnVpbGQgc21hcnQgY29udHJhY3RzLCBhbmQgaW4gdHVybiwgY3JlYXRlIGRlY2VudHJhbGl6ZWQgYXBwbGljYXRpb25zIGFuZCBwcm90b2NvbHMuIEFkZGl0aW9uYWxseSwgdGhlIGFiaWxpdHkgdG8gc2VuZCBhbmQgcmVjZWl2ZSBmdW5kcyBpbnN0YW50bHkgdGhyb3VnaCwgZm9yIG1pbmltYWwgZmVlcywgaGF2ZSBtYW55IGFwcGxpY2F0aW9ucyBpbiB0aGUgd29ybGQgb2YgYnVzaW5lc3MgYW5kIGZpbmFuY2UuCgo8YnI+CgojIyMgMS4xLiBCaXRjb2luCgpgYGB7cn0KYml0Y29pbmRmIDwtCiAgcmVhZC5jc3YoCiAgICAnLi9CVEMtVVNELmNzdicsCiAgICBjb2wubmFtZXMgPSBjKCJkYXRlIiwgIm9wZW4iLCAiaGlnaCIsICJsb3ciLCAiY2xvc2UiLCAiYWRqX2Nsb3NlIiwgInZvbHVtZSIpCiAgKQpoZWFkKGJpdGNvaW5kZikKYGBgCjxicj4KCiMjIyAxLjIuIERvZ2Vjb2luCgpgYGB7cn0KZG9nZWNvaW5kZiA8LQogIHJlYWQuY3N2KAogICAgJ0RPR0UtVVNELmNzdicsCiAgICBjb2wubmFtZXMgPSBjKCJkYXRlIiwgIm9wZW4iLCAiaGlnaCIsICJsb3ciLCAiY2xvc2UiLCAiYWRqX2Nsb3NlIiwgInZvbHVtZSIpCiAgKQpoZWFkKGRvZ2Vjb2luZGYpCmBgYAoKCjxicj4KCiMjIyAxLjMuIEV0aGVyZXVtCgpgYGB7cn0KZXRoZXJldW1kZiA8LQogIHJlYWQuY3N2KAogICAgJ0VUSC1VU0QuY3N2JywKICAgIGNvbC5uYW1lcyA9IGMoImRhdGUiLCAib3BlbiIsICJoaWdoIiwgImxvdyIsICJjbG9zZSIsICJhZGpfY2xvc2UiLCAidm9sdW1lIikKICApCmhlYWQoZXRoZXJldW1kZikKYGBgCgo8YnI+CiMjIyAxLjQuIENhcmRhbm8KCmBgYHtyfQpjYXJkYW5vZGYgPC0KICByZWFkLmNzdigKICAgICdBREEtVVNELmNzdicsCiAgICBjb2wubmFtZXMgPSBjKCJkYXRlIiwgIm9wZW4iLCAiaGlnaCIsICJsb3ciLCAiY2xvc2UiLCAiYWRqX2Nsb3NlIiwgInZvbHVtZSIpCiAgKQpoZWFkKGNhcmRhbm9kZikKYGBgCgo8YnI+CgojIyBQcmVwcm9jZXNzaW5nCgpCw6BpIHZp4bq/dCBn4buRYyBz4butIGThu6VuZyBmb3J3YXJkIGZpbGwgY+G7p2EgcGFuZGFzLCDEkeG7kWkgduG7m2kgUiBjw7MgdGjhu4MgZMO5bmcgaMOgbSBgZmlsbGAgY+G7p2EgcGFja2FnZSBgdGlkeXZlcnNlYAoKYGBge3J9CmJpdGNvaW5kZiA8LSBiaXRjb2luZGYgJT4lIGZpbGwob3BlbiwgY2xvc2UsIC5kaXJlY3Rpb24gPSAnZG93bicpCmRvZ2Vjb2luZGYgPC0gZG9nZWNvaW5kZiAlPiUgZmlsbChvcGVuLCBjbG9zZSwgLmRpcmVjdGlvbiA9ICdkb3duJykKZXRoZXJldW1kZiA8LSBldGhlcmV1bWRmICU+JSBmaWxsKG9wZW4sIGNsb3NlLCAuZGlyZWN0aW9uID0gJ2Rvd24nKQpjYXJkYW5vZGYgPC0gY2FyZGFub2RmICU+JSBmaWxsKG9wZW4sIGNsb3NlLCAuZGlyZWN0aW9uID0gJ2Rvd24nKQpgYGAKCjxicj4KCiMjIyBDb252ZXJ0IERhdGUgY29sdW1uIGludG8gRGF0ZSBmb3JtYXQKCmBgYHtyfQpiaXRjb2luZGYkZGF0ZSA8LSBhcy5EYXRlKGJpdGNvaW5kZiRkYXRlKQpkb2dlY29pbmRmJGRhdGUgPC0gYXMuRGF0ZShkb2dlY29pbmRmJGRhdGUpCmV0aGVyZXVtZGYkZGF0ZSA8LSBhcy5EYXRlKGV0aGVyZXVtZGYkZGF0ZSkKY2FyZGFub2RmJGRhdGUgPC0gYXMuRGF0ZShjYXJkYW5vZGYkZGF0ZSkKYGBgCgo8YnI+CgojIyMgQ29udmVydCBvdGhlcnMgY29sdW1uIGludG8gbnVtZXJpYwoKYGBge3J9CmJpdGNvaW5kZiA8LSBiaXRjb2luZGYgJT4lIG11dGF0ZV9pZihpcy5jaGFyYWN0ZXIsIGFzLm51bWVyaWMpCmRvZ2Vjb2luZGYgPC0gZG9nZWNvaW5kZiAlPiUgbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgYXMubnVtZXJpYykKZXRoZXJldW1kZiA8LSBldGhlcmV1bWRmICU+JSBtdXRhdGVfaWYoaXMuY2hhcmFjdGVyLCBhcy5udW1lcmljKQpjYXJkYW5vZGYgPC0gY2FyZGFub2RmICU+JSBtdXRhdGVfaWYoaXMuY2hhcmFjdGVyLCBhcy5udW1lcmljKQpgYGAKCjxicj4KCiMjIFZpc3VhbGl6YXRpb24KCjxicj4KClBsb3R0aW5nIGNsb3NlIHByaWNlIG9mIEJpdGNvaW4sIENhcmRhbm8sIERvZ2Vjb2luIGFuZCBFdGhlcmV1bQoKYGBge3J9CmxpbmVfYml0Y29pbmRmIDwtIGJpdGNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IGNsb3NlKSkgKwogICAgZ2VvbV9saW5lKGNvbCA9ICJyZWQiKSArIAogICAgbGFicygKICAgICAgdGl0bGUgPSAiQml0Y29pbiBDbG9zZSBQcmljZSIsCiAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkKCmxpbmVfY2FyZGFub2RmIDwtIGNhcmRhbm9kZiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IGNsb3NlKSkgKwogICAgZ2VvbV9saW5lKCkgKyAKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkNhcmRhbm8gQ2xvc2UgUHJpY2UiLAogICAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgICB5ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDkpCgoKbGluZV9kb2dlY29pbmRmIDwtIGRvZ2Vjb2luZGYgJT4lIAogIGdncGxvdChhZXMoeCA9IGRhdGUsIHkgPSBjbG9zZSkpICsKICAgIGdlb21fbGluZShjb2wgPSAib3JhbmdlIikgKyAKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkRvZ2Vjb2luIENsb3NlIFByaWNlIiwKICAgICAgeCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgeSA9IGVsZW1lbnRfYmxhbmsoKQogICAgKSArCiAgICB0aGVtZV9idyhiYXNlX3NpemUgPSA5KQoKCmxpbmVfZXRoZXJldW1kZiA8LSBldGhlcmV1bWRmICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY2xvc2UpKSArCiAgICBnZW9tX2xpbmUoY29sID0gImRhcmtncmVlbiIpICsgCiAgICBsYWJzKAogICAgICB0aXRsZSA9ICJEb2dlY29pbiBDbG9zZSBQcmljZSIsCiAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkKCmxpbmVfYml0Y29pbmRmICsgbGluZV9jYXJkYW5vZGYgKyBsaW5lX2RvZ2Vjb2luZGYgKyBsaW5lX2V0aGVyZXVtZGYKYGBgCgo8YnI+CgpQbG90dGluZyBvbmx5IDIwMjAtMjAyMSB5ZWFyIGNsb3NlIHByaWNlIG9mIEJpdGNvaW4sIENhcmRhbm8sIERvZ2Vjb2luIGFuZCBFdGhlcmV1bQoKYGBge3J9Cmxhc3QxeWVhcl9iaXRjb2luZGYgPC0gYml0Y29pbmRmICU+JSAKICBmaWx0ZXIoZGF0ZSA+IGFzLkRhdGUoIjAxLTA5LTIwMjAiLCBmb3JtYXQgPSAiJWQtJW0tJVkiKSkKCmxhc3QxeWVhcl9jYXJkYW5vZGYgPC0gY2FyZGFub2RmICU+JSAKICBmaWx0ZXIoZGF0ZSA+IGFzLkRhdGUoIjAxLTA5LTIwMjAiLCBmb3JtYXQgPSAiJWQtJW0tJVkiKSkKCmxhc3QxeWVhcl9kb2dlY29pbmRmIDwtIGRvZ2Vjb2luZGYgJT4lIAogIGZpbHRlcihkYXRlID4gYXMuRGF0ZSgiMDEtMDktMjAyMCIsIGZvcm1hdCA9ICIlZC0lbS0lWSIpKQoKbGFzdDF5ZWFyX2V0aGVyZXVtZGYgPC0gZXRoZXJldW1kZiAlPiUgCiAgZmlsdGVyKGRhdGUgPiBhcy5EYXRlKCIwMS0wOS0yMDIwIiwgZm9ybWF0ID0gIiVkLSVtLSVZIikpCgpsaW5lX2JpdGNvaW5kZiA8LSBsYXN0MXllYXJfYml0Y29pbmRmICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY2xvc2UpKSArCiAgICBnZW9tX2xpbmUoY29sID0gInJlZCIpICsgCiAgICBsYWJzKAogICAgICB0aXRsZSA9ICJCaXRjb2luIENsb3NlIFByaWNlIiwKICAgICAgeCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgeSA9IGVsZW1lbnRfYmxhbmsoKQogICAgKSArCiAgICB0aGVtZV9idyhiYXNlX3NpemUgPSA5KQoKbGluZV9jYXJkYW5vZGYgPC0gbGFzdDF5ZWFyX2NhcmRhbm9kZiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IGNsb3NlKSkgKwogICAgZ2VvbV9saW5lKCkgKyAKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkNhcmRhbm8gQ2xvc2UgUHJpY2UiLAogICAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgICB5ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDkpCgoKbGluZV9kb2dlY29pbmRmIDwtIGxhc3QxeWVhcl9kb2dlY29pbmRmICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gY2xvc2UpKSArCiAgICBnZW9tX2xpbmUoY29sID0gIm9yYW5nZSIpICsgCiAgICBsYWJzKAogICAgICB0aXRsZSA9ICJEb2dlY29pbiBDbG9zZSBQcmljZSIsCiAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkKCgpsaW5lX2V0aGVyZXVtZGYgPC0gbGFzdDF5ZWFyX2V0aGVyZXVtZGYgJT4lIAogIGZpbHRlcihkYXRlID4gYXMuRGF0ZSgiMDEtMDktMjAyMCIsIGZvcm1hdCA9ICIlZC0lbS0lWSIpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSwgeSA9IGNsb3NlKSkgKwogICAgZ2VvbV9saW5lKGNvbCA9ICJkYXJrZ3JlZW4iKSArIAogICAgbGFicygKICAgICAgdGl0bGUgPSAiRXRoZXJldW0gQ2xvc2UgUHJpY2UiLAogICAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgICB5ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDkpCgpsaW5lX2JpdGNvaW5kZiArCiAgbGluZV9jYXJkYW5vZGYgKwogIGxpbmVfZG9nZWNvaW5kZiArCiAgbGluZV9ldGhlcmV1bWRmICsKICBwbG90X2xheW91dChucm93ID0gNCkKYGBgCgo8YnI+CgpQbG90dGluZyBvbmx5IDIwMjAtMjAyMSB5ZWFyIHZvbHVtZSBvZiBCaXRjb2luLCBDYXJkYW5vLCBEb2dlY29pbiBhbmQgRXRoZXJldW0KCmBgYHtyfQpnZ3Bsb3QoTlVMTCkgKwogIGdlb21fbGluZShhZXMobGFzdDF5ZWFyX2JpdGNvaW5kZiRkYXRlLCBsYXN0MXllYXJfYml0Y29pbmRmJHZvbHVtZSwgY29sID0gImJpdGNvaW4iKSkgKyAKICBnZW9tX2xpbmUoYWVzKGxhc3QxeWVhcl9jYXJkYW5vZGYkZGF0ZSwgbGFzdDF5ZWFyX2NhcmRhbm9kZiR2b2x1bWUsIGNvbCA9ICJjYXJkYW5vZCIpKSArIAogIGdlb21fbGluZShhZXMobGFzdDF5ZWFyX2RvZ2Vjb2luZGYkZGF0ZSwgbGFzdDF5ZWFyX2RvZ2Vjb2luZGYkdm9sdW1lLCBjb2wgPSAiZG9nZWNvaW4iKSkgKyAKICBnZW9tX2xpbmUoYWVzKGxhc3QxeWVhcl9ldGhlcmV1bWRmJGRhdGUsIGxhc3QxeWVhcl9ldGhlcmV1bWRmJHZvbHVtZSwgY29sID0gImV0aGVyZXVtIikpICsKICB0aGVtZV9idygpICsKICBsYWJzKAogICAgdGl0bGUgPSAiVm9sdW1lIG9mIEJpdGNvaW4sIENhcmRhbm8sIERvZ2Vjb2luLCBFdGhlcmV1bSIsCiAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgeSA9IGVsZW1lbnRfYmxhbmsoKQogICkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCgKICAgIG5hbWU9ZWxlbWVudF9ibGFuaygpLAogICAgYnJlYWtzPWMoJ2JpdGNvaW4nLCAnY2FyZGFub2QnLCAnZG9nZWNvaW4nLCAiZXRoZXJldW0iKSwKICAgIHZhbHVlcz1jKCdiaXRjb2luJz0ncmVkJywgJ2NhcmRhbm9kJz0nYmxhY2snLCAnZG9nZWNvaW4nPSdvcmFuZ2UnLCAnZXRoZXJldW0nID0gImRhcmtncmVlbiIpCiAgKQpgYGAKCgpQbG90dGluZyBsYXN0IG1vbnRoIG9wZW4gYW5kIGNsb3NlIHByaWNlIG9mIEJpdGNvaW4sIENhcmRhbm8sIERvZ2Vjb2luIGFuZCBFdGhlcmV1bSB3aXRoIGNvbXBhcmlzaW9uCgpgYGB7cn0KbGFzdDFtb250aF9iaXRjb2luZGYgPC1iaXRjb2luZGYgJT4lIAogIGZpbHRlcihkYXRlID4gYXMuRGF0ZSgiMDEtMDgtMjAyMSIsIGZvcm1hdCA9ICIlZC0lbS0lWSIpKQpsYXN0MW1vbnRoX2NhcmRhbm9kZiA8LSBjYXJkYW5vZGYgJT4lIAogIGZpbHRlcihkYXRlID4gYXMuRGF0ZSgiMDEtMDgtMjAyMSIsIGZvcm1hdCA9ICIlZC0lbS0lWSIpKQpsYXN0MW1vbnRoX2RvZ2Vjb2luZGYgPC0gZG9nZWNvaW5kZiAlPiUgCiAgZmlsdGVyKGRhdGUgPiBhcy5EYXRlKCIwMS0wOC0yMDIxIiwgZm9ybWF0ID0gIiVkLSVtLSVZIikpCmxhc3QxbW9udGhfZXRoZXJldW1kZiA8LSBldGhlcmV1bWRmICU+JSAKICBmaWx0ZXIoZGF0ZSA+IGFzLkRhdGUoIjAxLTA4LTIwMjEiLCBmb3JtYXQgPSAiJWQtJW0tJVkiKSkKYGBgCgoKYGBge3J9Cmxhc3QxbW9udGhfYml0Y29pbmRmICU+JSAKICBnZ3Bsb3QoYWVzKGRhdGUpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBjbG9zZSwgY29sID0gIkNsb3NlIikpICsgCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBvcGVuLCBjb2wgPSAiT3BlbiIpKSArCiAgICBsYWJzKAogICAgICBjb2wgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHRpdGxlID0gImJpdGNvaW4iLAogICAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgICB5ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSA5KSArCmxhc3QxbW9udGhfY2FyZGFub2RmICU+JSAKICBnZ3Bsb3QoYWVzKGRhdGUpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBjbG9zZSwgY29sID0gIkMiKSwgc2hvdy5sZWdlbmQgPSBGKSArIAogICAgZ2VvbV9saW5lKGFlcyh5ID0gb3BlbiwgY29sID0gIk8iKSwgc2hvdy5sZWdlbmQgPSBGKSArCiAgICBsYWJzKAogICAgICBjb2wgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHRpdGxlID0gImNhcmRhbm8iLAogICAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgICB5ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSA5KSArCmxhc3QxbW9udGhfZG9nZWNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyhkYXRlKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gY2xvc2UsIGNvbCA9ICJDIiksIHNob3cubGVnZW5kID0gRikgKyAKICAgIGdlb21fbGluZShhZXMoeSA9IG9wZW4sIGNvbCA9ICJPIiksIHNob3cubGVnZW5kID0gRikgKwogICAgbGFicygKICAgICAgY29sID0gZWxlbWVudF9ibGFuaygpLAogICAgICB0aXRsZSA9ICJkb2dlY29pbiIsCiAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgKwogIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDkpICsKbGFzdDFtb250aF9ldGhlcmV1bWRmICU+JSAKICBnZ3Bsb3QoYWVzKGRhdGUpKSArCiAgICBnZW9tX2xpbmUoYWVzKHkgPSBjbG9zZSwgY29sID0gIkMiKSwgc2hvdy5sZWdlbmQgPSBGKSArIAogICAgZ2VvbV9saW5lKGFlcyh5ID0gb3BlbiwgY29sID0gIk8iKSwgc2hvdy5sZWdlbmQgPSBGKSArCiAgICBsYWJzKAogICAgICBjb2wgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHRpdGxlID0gImV0aGVyZXVtIiwKICAgICAgeCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgeSA9IGVsZW1lbnRfYmxhbmsoKQogICAgKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkgKwpwbG90X2xheW91dChuY29sID0gMSkKYGBgCjxicj4KCiMjIyBNb3ZpbmcgQXZlcmFnZXMKCkFzIHdlIGtub3cgdGhlIHN0b2NrIHByaWNlcyBhcmUgaGlnaGx5IHZvbGF0aWxlIGFuZCBwcmljZXMgY2hhbmdlIHF1aWNrbHkgd2l0aCB0aW1lLiBUbyBvYnNlcnZlIGFueSB0cmVuZCBvciBwYXR0ZXJuIHdlIGNhbiB0YWtlIHRoZSBoZWxwIG9mIGEgNTAtZGF5IDIwMC1kYXkgYXZlcmFnZS4KCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpiaXRjb2luZGYgPC0gYml0Y29pbmRmICU+JSBmaWxsKG9wZW4sIGNsb3NlLCAuZGlyZWN0aW9uID0gJ2Rvd24nKQpkb2dlY29pbmRmIDwtIGRvZ2Vjb2luZGYgJT4lIGZpbGwob3BlbiwgY2xvc2UsIC5kaXJlY3Rpb24gPSAnZG93bicpCmV0aGVyZXVtZGYgPC0gZXRoZXJldW1kZiAlPiUgZmlsbChvcGVuLCBjbG9zZSwgLmRpcmVjdGlvbiA9ICdkb3duJykKY2FyZGFub2RmIDwtIGNhcmRhbm9kZiAlPiUgZmlsbChvcGVuLCBjbG9zZSwgLmRpcmVjdGlvbiA9ICdkb3duJykKCmJpdGNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHJvbGxtZWFuKGNsb3NlLCA1MCwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpKSwgY29sID0gImJsdWUiKSArIAogICAgZ2VvbV9saW5lKGFlcyh5ID0gcm9sbG1lYW4oY2xvc2UsIDIwMCwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpKSwgY29sID0gIm9yYW5nZSIpICsKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkJpdGNvaW4gQ2xvc2UgUHJpY2UgbW92aW5nIGF2ZXJhZ2UiLAogICAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgICB5ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsgCiAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkgKyAKY2FyZGFub2RmICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkYXRlKSkgKwogICAgZ2VvbV9saW5lKGFlcyh5ID0gcm9sbG1lYW4oY2xvc2UsIDUwLCBmaWxsID0gTkEsIGFsaWduID0gInJpZ2h0IikpLCBjb2wgPSAiYmxhY2siKSArIAogICAgZ2VvbV9saW5lKGFlcyh5ID0gcm9sbG1lYW4oY2xvc2UsIDIwMCwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpKSwgY29sID0gInJlZCIpICsKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkJpdGNvaW4gQ2xvc2UgUHJpY2UgbW92aW5nIGF2ZXJhZ2UiLAogICAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgICB5ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsgCiAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkgKyAKZG9nZWNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHJvbGxtZWFuKGNsb3NlLCA1MCwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpKSwgY29sID0gIm9yYW5nZSIpICsgCiAgICBnZW9tX2xpbmUoYWVzKHkgPSByb2xsbWVhbihjbG9zZSwgMjAwLCBmaWxsID0gTkEsIGFsaWduID0gInJpZ2h0IikpLCBjb2wgPSAiZ3JleSIpICsKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkJpdGNvaW4gQ2xvc2UgUHJpY2UgbW92aW5nIGF2ZXJhZ2UiLAogICAgICB4ID0gZWxlbWVudF9ibGFuaygpLAogICAgICB5ID0gZWxlbWVudF9ibGFuaygpCiAgICApICsgCiAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkgKyAKZXRoZXJldW1kZiAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZGF0ZSkpICsKICAgIGdlb21fbGluZShhZXMoeSA9IHJvbGxtZWFuKGNsb3NlLCA1MCwgZmlsbCA9IE5BLCBhbGlnbiA9ICJyaWdodCIpKSwgY29sID0gImdyZWVuIikgKyAKICAgIGdlb21fbGluZShhZXMoeSA9IHJvbGxtZWFuKGNsb3NlLCAyMDAsIGZpbGwgPSBOQSwgYWxpZ24gPSAicmlnaHQiKSksIGNvbCA9ICJibHVlIikgKwogICAgbGFicygKICAgICAgdGl0bGUgPSAiQml0Y29pbiBDbG9zZSBQcmljZSBtb3ZpbmcgYXZlcmFnZSIsCiAgICAgIHggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHkgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgKyAKICB0aGVtZV9idyhiYXNlX3NpemUgPSA5KQpgYGAKCjxicj4KClBsb3R0aW5nIGhpc3RvZ3JhbSB3aXRoIG1lYW4gaW5kaWNhdG9yIG9mIGFsbCBmb3VyIGNyeXB0b2N1cnJlbmNpZXMKCmBgYHtyfQpiaXRjb2luZGYgJT4lIAogIGdncGxvdChhZXMoY2xvc2UpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShmaWxsID0iZGFya3JlZCIsIGFscGhhID0gMC41LCBjb2wgPSAiYmxhY2siKSArIAogICAgeWxhYigiY291bnQiKSArCiAgICB0aGVtZV9idyhiYXNlX3NpemUgPSA5KSArIAogICAgYW5ub3RhdGUoInRleHQiLCBsYWJlbCA9ICJCaXRjb2luIENsb3NlIFByaWNlIiwgNTAwMDAsIDQwMCkgKyAKY2FyZGFub2RmICU+JSAKICBnZ3Bsb3QoYWVzKGNsb3NlKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ImRhcmtncmVlbiIsIGFscGhhID0gMC41LCBjb2wgPSAiYmxhY2siKSArCiAgICB5bGFiKCJjb3VudCIpICsKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDkpICsgCiAgICBhbm5vdGF0ZSgidGV4dCIsIGxhYmVsID0gIkNhcmRhbm8gQ2xvc2UgUHJpY2UiLCAyLjMsIDIwMCkgKyAKZG9nZWNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyhjbG9zZSkpICsKICAgIGdlb21faGlzdG9ncmFtKGZpbGwgPSJncmV5IiwgYmlucyA9IDQwMCkgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkgKyAKICAgIHlsYWIoImNvdW50IikgKwogICAgYW5ub3RhdGUoInRleHQiLCBsYWJlbCA9ICJEb2dlY29pbiBDbG9zZSBQcmljZSIsIDAuNTIsIDIwMCkgKyAKZXRoZXJldW1kZiAlPiUKICBnZ3Bsb3QoYWVzKGNsb3NlKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ImRhcmtvcmFuZ2UiLCBhbHBoYSA9IDAuNSwgY29sID0gImJsYWNrIikgKwogICAgdGhlbWVfYncoYmFzZV9zaXplID0gOSkgKyAKICAgIGFubm90YXRlKCJ0ZXh0IiwgbGFiZWwgPSAiRXRoZXJldW0gQ2xvc2UgUHJpY2UiLCAzMTAwLCA0MDApICsKcGxvdF9sYXlvdXQobmNvbCA9IDEpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CmJpdGNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyhkYXRlKSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oKSArIAogICAgeWxhYihlbGVtZW50X2JsYW5rKCkpICsKICAgIHRoZW1lX2J3KCkgKyAKYml0Y29pbmRmICU+JSAKICBnZ3Bsb3QoYWVzKG9wZW4pKSArCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsgCiAgICB5bGFiKGVsZW1lbnRfYmxhbmsoKSkgKwogICAgdGhlbWVfYncoKSArIApiaXRjb2luZGYgJT4lIAogIGdncGxvdChhZXMoaGlnaCkpICsKICAgIGdlb21faGlzdG9ncmFtKCkgKyAKICAgIHlsYWIoZWxlbWVudF9ibGFuaygpKSArCiAgICB0aGVtZV9idygpICsgCmJpdGNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyhsb3cpKSArCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsgCiAgICB5bGFiKGVsZW1lbnRfYmxhbmsoKSkgKwogICAgdGhlbWVfYncoKSArIApiaXRjb2luZGYgJT4lIAogIGdncGxvdChhZXMoY2xvc2UpKSArCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsKICAgIHlsYWIoZWxlbWVudF9ibGFuaygpKSArCiAgICB0aGVtZV9idygpICsgCmJpdGNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyhhZGpfY2xvc2UpKSArCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsKICAgIHlsYWIoZWxlbWVudF9ibGFuaygpKSArCiAgICB0aGVtZV9idygpICsgCmJpdGNvaW5kZiAlPiUgCiAgZ2dwbG90KGFlcyh2b2x1bWUpKSArCiAgICBnZW9tX2hpc3RvZ3JhbSgpICsKICAgIHlsYWIoZWxlbWVudF9ibGFuaygpKSArCiAgICB0aGVtZV9idygpIApgYGAKYGBge3J9CnJtKGNhcmRhbm9kZikKcm0oZG9nZWNvaW5kZikKcm0oZXRoZXJldW1kZikKcm0obGFzdDFtb250aF9iaXRjb2luZGYpCnJtKGxhc3QxbW9udGhfY2FyZGFub2RmKQpybShsYXN0MW1vbnRoX2RvZ2Vjb2luZGYpCnJtKGxhc3QxbW9udGhfZXRoZXJldW1kZikKcm0obGFzdDF5ZWFyX2JpdGNvaW5kZikKcm0obGFzdDF5ZWFyX2NhcmRhbm9kZikKcm0obGFzdDF5ZWFyX2RvZ2Vjb2luZGYpCnJtKGxhc3QxeWVhcl9ldGhlcmV1bWRmKQpybShsaW5lX2JpdGNvaW5kZikKcm0obGluZV9jYXJkYW5vZGYpCnJtKGxpbmVfZG9nZWNvaW5kZikKcm0obGluZV9ldGhlcmV1bWRmKQpgYGAKCgojIyBCaXRjb2luIENsb3NlIFByaWNlIFByZWRpY3Rpb24gdXNpbmcgWEdCb29zdAoKPGJyPgoKRGF0YWZyYW1lIG9mIGJpdGNvaW4gY2xvc2UgcHJpY2UKCmBgYHtyfQpjbG9zZWRmIDwtIHNlbGVjdChiaXRjb2luZGYsIGRhdGUsIGNsb3NlKQpjYXQoIlNoYXBlIG9mIGNsb3NlIGRhdGFmcmFtZToiLCBkaW0oY2xvc2VkZikpCmBgYAoKYGBge3J9CmNsb3NlZGYgPC0gY2xvc2VkZiAlPiUgZmlsdGVyKGRhdGUgPiBhcy5EYXRlKCcyMDIwLTA5LTEzJykpCmNsb3NlX3N0b2NrIDwtIGNsb3NlZGYKY2F0KCJUb3RhbCBkYXRhIGZvciBwcmVkaWN0aW9uOiAiLCBucm93KGNsb3NlZGYpKQpgYGAKPGJyPgoKTm9ybWFsaXppbmcgY2xvc2UgcHJpY2UgdmFsdWUKCmBgYHtyfQpjbG9zZWRmIDwtCiAgcHJlZGljdChzZWxlY3QoY2xvc2VkZiwgY2xvc2UpICU+JSBwcmVQcm9jZXNzKG1ldGhvZCA9ICJyYW5nZSIpLAogICAgICAgICAgc2VsZWN0KGNsb3NlZGYsIGNsb3NlKSkKZGltKGNsb3NlZGYpCmBgYAoKPGJyPgpTZXBhcmF0ZSBkYXRhIGZvciBUcmFpbiBhbmQgVGVzdAoKYGBge3J9CnRyYWluaW5nX3NpemUgPC1yb3VuZChucm93KGNsb3NlZGYpKjAuNzApCgp0cmFpbl9kYXRhIDwtIGRhdGEuZnJhbWUoY2xvc2UgPSBjbG9zZWRmWzE6dHJhaW5pbmdfc2l6ZSwgXSkKdGVzdF9kYXRhIDwtIGRhdGEuZnJhbWUoY2xvc2UgPSBjbG9zZWRmWyh0cmFpbmluZ19zaXplICsgMSk6bnJvdyhjbG9zZWRmKSwgXSkKCmNhdCgidHJhaW5fZGF0YTogIiwgZGltKHRyYWluX2RhdGEpKQpjYXQoInRlc3RfZGF0YTogIiwgZGltKHRlc3RfZGF0YSkpCmBgYAoKYGBge3J9CmdncGxvdChOVUxMKSArCiAgZ2VvbV9saW5lKAogICAgYWVzKAogICAgICB4ID0gY2xvc2Vfc3RvY2skZGF0ZVsxOnRyYWluaW5nX3NpemVdLAogICAgICB5ID0gdHJhaW5fZGF0YSRjbG9zZSwKICAgICAgY29sID0gInRyYWluIgogICAgKQogICkgKwogIGdlb21fbGluZSgKICAgIGFlcygKICAgICAgeCA9IGNsb3NlX3N0b2NrJGRhdGVbKHRyYWluaW5nX3NpemUgKyAxKTpucm93KGNsb3NlZGYpXSwKICAgICAgeSA9IHRlc3RfZGF0YSRjbG9zZSwKICAgICAgY29sID0gInRlc3QiCiAgICApCiAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgYnJlYWtzID0gYygidHJhaW4iLCAidGVzdCIpLAogICAgdmFsdWVzID0gYygidHJhaW4iID0gImJsYWNrIiwgInRlc3QiID0gInJlZCIpCiAgKSArCiAgbGFicyh0aXRsZSA9ICdUcmFpbiAmIFRlc3QgZGF0YScsIHggPSAnRGF0ZScsIHkgPSAnV2Vla2x5IFNhbGVzJywgY29sID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWVfYncoKQpgYGAKCmBgYHtyfQpjcmVhdGVfZGF0YXNldCA8LSBmdW5jdGlvbiAoZGF0YXNldCwgdGltZV9zdGVwPTEpIHsKICBkYXRhWCA8LSBsaXN0KCkKICBkYXRhWSA8LSBsaXN0KCkKICAKICBmb3IgKGkgaW4gMToobnJvdyhkYXRhc2V0KS10aW1lX3N0ZXAtMSkpIHsKICAgIGEgPC0gZGF0YXNldFtpOihpK3RpbWVfc3RlcC0xKSwgMV0KICAgIGRhdGFYW1tpXV0gPC0gYQogICAgZGF0YVlbW2ldXSA8LSBkYXRhc2V0W2kgKyB0aW1lX3N0ZXAsIDFdCiAgfQogIAogIGRhdGFYIDwtIG1hdHJpeCh1bmxpc3QoZGF0YVgpLCBuY29sID0gdGltZV9zdGVwLCBieXJvdyA9IFRSVUUpCiAgY29sbmFtZXMoZGF0YVgpIDwtIHBhc3RlMCgiViIsIDE6dGltZV9zdGVwKQogIGRhdGFZIDwtIHVubGlzdChkYXRhWSkKICAKICByZXR1cm4obGlzdChkYXRhWCA9IGRhdGFYLCBkYXRhWSA9IGRhdGFZKSkKfQpgYGAKCmBgYHtyfQp0cmFpbl9zZXQgPC0gY3JlYXRlX2RhdGFzZXQodHJhaW5fZGF0YSwgdGltZV9zdGVwID0gMTUpClhfdHJhaW4gPC0gdHJhaW5fc2V0W1sxXV0KWV90cmFpbiA8LSB0cmFpbl9zZXRbWzJdXQpybSh0cmFpbl9zZXQpCgp0ZXN0X3NldCA8LSBjcmVhdGVfZGF0YXNldCh0ZXN0X2RhdGEsIHRpbWVfc3RlcCA9IDE1KQpYX3Rlc3QgPC0gdGVzdF9zZXRbWzFdXQpZX3Rlc3QgPC0gdGVzdF9zZXRbWzJdXQpybSh0ZXN0X3NldCkKCmNhdCgiWF90cmFpbjogIiwgZGltKFhfdHJhaW4pKQpjYXQoIllfdHJhaW46ICIsIGRpbShZX3RyYWluKSkKY2F0KCJYX3Rlc3Q6ICIsIGRpbShYX3Rlc3QpKQpjYXQoIllfdGVzdCIsIGRpbShZX3Rlc3QpKQpgYGAKCmBgYHtyfQp4Z2JfdHJjb250cm9sIDwtIGNhcmV0Ojp0cmFpbkNvbnRyb2woCiAgIG1ldGhvZCA9ICJjdiIsIAogICBudW1iZXIgPSA1LAogICBhbGxvd1BhcmFsbGVsID0gVFJVRSwgCiAgIHZlcmJvc2VJdGVyID0gRkFMU0UsIAogICByZXR1cm5EYXRhID0gRkFMU0UKKQoKeGdiX2dyaWQgPC0gYmFzZTo6ZXhwYW5kLmdyaWQoCiAgIGxpc3QoCiAgICBucm91bmRzPTEwMDAsCiAgICAjIGJhc2Vfc2NvcmU9MC41LAogICAgIyBjb2xzYW1wbGVfYnlsZXZlbD0xLAogICAgIyBjb2xzYW1wbGVfYnlub2RlPTEsCiAgICBjb2xzYW1wbGVfYnl0cmVlID0gMSwgIyBzdWJzYW1wbGUgcmF0aW8gb2YgY29sdW1ucyB3aGVuIGNvbnN0cnVjdGlvbiBlYWNoIHRyZWUKICAgICMgIyBucm91bmRzID0gMTAwMCwKICAgIG1heF9kZXB0aCA9IDYsICMgbWF4aW11bSBkZXB0aCBvZiBhIHRyZWUKICAgIGV0YSA9IDAuMywgIyBsZWFybmluZyByYXRlCiAgICBnYW1tYSA9IDAsICMgbWluaW11bSBsb3NzIHJlZHVjdGlvbgogICAgbWluX2NoaWxkX3dlaWdodCA9IDEsICAjIG1pbmltdW0gc3VtIG9mIGluc3RhbmNlIHdlaWdodCAoaGVzc2lhbikgbmVlZGVkIGluYSBjaGlsZAogICAgc3Vic2FtcGxlID0gMSAjIHN1YnNhbXBsZSByYXRpbyBvZiB0aGUgdHJhaW5pbmcgaW5zdGFuY2VzCiAgICAjIHRyZWVfbWV0aG9kPSdleGFjdCcsCiAgICAjIGludGVyYWN0aW9uX2NvbnN0cmFpbnRzPScnLAogICAgIyBtYXhfZGVsdGFfc3RlcD0wLAogICAgIyBuX2VzdGltYXRvcnM9MTAwMCwKICAgICMgdmFsaWRhdGVfcGFyYW1ldGVycz0xLAogICAgIyByYW5kb21fc3RhdGU9MCwKICAgICMgbl9qb2JzPTQKKSkKYGBgCgpgYGB7cn0KeGdiX21vZGVsIDwtIHRyYWluKAogICBYX3RyYWluLCBZX3RyYWluLAogICB0ckNvbnRyb2wgPSB4Z2JfdHJjb250cm9sLAogICB0dW5lR3JpZCA9IHhnYl9ncmlkLAogICBtZXRob2QgPSAieGdiVHJlZSIsCiAgIG50aHJlYWQgPSAxCikKYGBgCgpgYGB7cn0KcHJlZGljdGVkIDwtIHByZWRpY3QoeGdiX21vZGVsLCBYX3Rlc3QpCgpjYXQoIk1lYW4gQWJzb2x1dGUgRXJyb3IgLSBNQUUgOiAiLCBNQUUoWV90ZXN0LCBwcmVkaWN0ZWQpKQpjYXQoIlJvb3QgTWVhbiBzcXVhcmVkIEVycm9yIC0gUk1TRSA6ICIsIFJNU0UoWV90ZXN0LCBwcmVkaWN0ZWQpKQpgYGAKCmBgYHtyfQp0cmFpbl9wcmVkaWN0IDwtIHByZWRpY3QoeGdiX21vZGVsLCBYX3RyYWluKQp0ZXN0X3ByZWRpY3QgPC0gcHJlZGljdCh4Z2JfbW9kZWwsIFhfdGVzdCkKYGBgCgpgYGB7cn0KZGVzY2FsZXIgPC0gZnVuY3Rpb24oeHYsIG14LCBueCkgewogIGR2YWxzIDwtIHh2ICogKG14IC0gbngpICsgbngKICBkdmFscyA8LSByb3VuZChkdmFscywgZGlnaXRzID0gNSkKICByZXR1cm4oZHZhbHMpCn0KYGBgCgpgYGB7cn0KZ2dwbG90KE5VTEwpICsKICBnZW9tX2xpbmUoYWVzKAogICAgeCA9IGNsb3NlX3N0b2NrJGRhdGUsCiAgICB5ID0gY2xvc2VkZiRjbG9zZSAlPiUgZGVzY2FsZXIobWF4KGNsb3NlX3N0b2NrJGNsb3NlKSwgbWluKGNsb3NlX3N0b2NrJGNsb3NlKSksCiAgICBjb2wgPSAnT3JpZ2luYWwgY2xvc2UgcHJpY2UnCiAgKSwKICApICsKICBnZW9tX2xpbmUoCiAgICBhZXMoCiAgICAgIHggPSBjbG9zZV9zdG9jayRkYXRlWzE3OnRyYWluaW5nX3NpemUgLSAxXSwKICAgICAgeSA9IHRyYWluX3ByZWRpY3QgJT4lIGRlc2NhbGVyKG1heChjbG9zZV9zdG9jayRjbG9zZSksIG1pbihjbG9zZV9zdG9jayRjbG9zZSkpLAogICAgICBjb2wgPSAnVHJhaW4gcHJlZGljdGVkIGNsb3NlIHByaWNlJwogICAgKSwKICApICsKICBnZW9tX2xpbmUoCiAgICBhZXMoCiAgICAgIHggPSBjbG9zZV9zdG9jayRkYXRlWyh0cmFpbmluZ19zaXplICsgMTYpOihucm93KGNsb3NlX3N0b2NrKSAtIDEpXSwKICAgICAgeSA9IHRlc3RfcHJlZGljdCAlPiUgZGVzY2FsZXIobWF4KGNsb3NlX3N0b2NrJGNsb3NlKSwgbWluKGNsb3NlX3N0b2NrJGNsb3NlKSksCiAgICAgIGNvbCA9ICdUZXN0IHByZWRpY3RlZCBjbG9zZSBwcmljZScKICAgICksCiAgICBjb2wgPSAiZ3JlZW4iCiAgKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKAogICAgYnJlYWtzID0gYygKICAgICAgJ09yaWdpbmFsIGNsb3NlIHByaWNlJywKICAgICAgJ1RyYWluIHByZWRpY3RlZCBjbG9zZSBwcmljZScsCiAgICAgICdUZXN0IHByZWRpY3RlZCBjbG9zZSBwcmljZScKICAgICksCiAgICB2YWx1ZXMgPSBjKAogICAgICAnT3JpZ2luYWwgY2xvc2UgcHJpY2UnID0gImJsdWUiLAogICAgICAnVHJhaW4gcHJlZGljdGVkIGNsb3NlIHByaWNlJyA9ICJyZWQiLAogICAgICAnVGVzdCBwcmVkaWN0ZWQgY2xvc2UgcHJpY2UnID0gImdyZWVuIgogICAgKSwKICApICsKICB0aGVtZV9idygpICsKICBsYWJzKAogICAgeCA9ICdEYXRlJywKICAgIHkgPSAnQ2xvc2UgcHJpY2UnLAogICAgdGl0bGUgPSAnQ29tcGFyaXNpb24gYmV0d2VlbiBvcmlnaW5hbCBjbG9zZSBwcmljZSB2cyBwcmVkaWN0ZWQgY2xvc2UgcHJpY2UnLAogICAgY29sID0gZWxlbWVudF9ibGFuaygpCiAgKQpgYGAKCgoKCgo=