# Clear workspace: 
rm(list = ls())

# Import packages for data manipulation: 
library(tidyverse)
library(magrittr)

# A function for black theme: 

my_theme <- function(...) {
  theme(
    axis.line = element_blank(),  
    axis.text.x = element_text(color = "white", lineheight = 0.9),  
    axis.text.y = element_text(color = "white", lineheight = 0.9),  
    axis.ticks = element_line(color = "white", size  =  0.2),  
    axis.title.x = element_text(color = "white", margin = margin(0, 10, 0, 0)),  
    axis.title.y = element_text(color = "white", angle = 90, margin = margin(0, 10, 0, 0)),  
    axis.ticks.length = unit(0.3, "lines"),   
    legend.background = element_rect(color = NA, fill = "gray10"),  
    legend.key = element_rect(color = "white",  fill = "gray10"),  
    legend.key.size = unit(1.2, "lines"),  
    legend.key.height = NULL,  
    legend.key.width = NULL,      
    legend.text = element_text(color = "white"),  
    legend.title = element_text(face = "bold", hjust = 0, color = "white"),  
    legend.text.align = NULL,  
    legend.title.align = NULL,  
    legend.direction = "vertical",  
    legend.box = NULL, 
    panel.background = element_rect(fill = "gray10", color  =  NA),  
    panel.border = element_blank(),
    panel.grid.major = element_line(color = "grey35"),  
    panel.grid.minor = element_line(color = "grey20"),  
    panel.spacing = unit(0.5, "lines"),   
    strip.background = element_rect(fill = "grey30", color = "grey10"),  
    strip.text.x = element_text(color = "white"),  
    strip.text.y = element_text(color = "white", angle = -90),  
    plot.background = element_rect(color = "gray10", fill = "gray10"),  
    plot.title = element_text(color = "white", hjust = 0, lineheight = 1.25, margin = margin(2, 2, 2, 2)),  
    plot.subtitle = element_text(color = "white", hjust = 0, margin = margin(2, 2, 2, 2)),  
    plot.caption = element_text(color = "white", hjust = 0),  
    plot.margin = unit(rep(1, 4), "lines"))
  
}

# Inspect data path and load data: 
my_path <- dir("/home/chidung/Desktop/atm_cash_data", full.names = TRUE)
my_case <- read_csv(my_path[1])

# Create lag 1 variable: 
my_case_lag <- my_case %>% 
  mutate(lag1 = lag(daily_cash, 1)) %>% 
  na.omit()

# Scale 0-1 for variables: 

my_case_lag_scaled <- my_case_lag %>% 
  mutate_if(is.numeric, function(x) {(x - min(x)) / (max(x) - min(x))})


# Split data: 
N <- nrow(my_case_lag_scaled)
train_df <- my_case_lag_scaled %>% slice(1:450)
test_df <- my_case_lag_scaled %>% slice(451:N)

# Specify features and output: 

y_train <- train_df %>% pull(2)
x_train <- train_df %>% pull(3)

y_test <- test_df %>% pull(2)
x_test <- test_df %>% pull(3)


#======================================================
#  Long Short Term Memory (LSTM) Networks (Version 1)
#======================================================

# Load package keras: 
library(keras)

# Reshape the input to 3-dim: 
dim(x_train) <- c(length(x_train), 1, 1)

# specify required arguments: 
X_shape2 <- dim(x_train)[2]
X_shape3 <- dim(x_train)[3]
batch_size <- 1                
units <- 1                     

# Specify conditions for LSTM Model: 

model <- keras_model_sequential() %>%
  layer_lstm(units, batch_input_shape = c(batch_size, X_shape2, X_shape3), stateful = TRUE) %>%
  layer_dense(units = 1) %>% 
  compile(loss = "mean_squared_error",
          optimizer = optimizer_adam(lr = 0.005, decay = 1e-10),  
          metrics = c("accuracy"))

# Train / fit LSTM Model: 

Epochs <- 10   
for(i in 1:Epochs) {
  model %>% 
    fit(x_train, 
        y_train, 
        epochs = 1,
        batch_size = batch_size, 
        verbose = 1, 
        shuffle = FALSE)
  model %>% reset_states()
}

# A function for forecasting: 

my_predict_from_lstm <- function(k) {
  x <- x_test[k]
  dim(x) <- c(1, 1, 1)
  y_hat <- model %>% predict(x, batch_size = 1)
  return(y_hat)
}

# Make predictions: 
sapply(1:length(x_test), my_predict_from_lstm) ->> pred_scaled

# A function covert to origin: 

convert_to_origin <- function(x) {
  x_base <- my_case_lag$daily_cash
  y <- x*(max(x_base) - min(x_base)) + min(x_base)
  return(y)
}

# Compare actuals vs predictions: 
test_df %>% 
  rename(Actual = daily_cash) %>% 
  mutate(Predicted = convert_to_origin(pred_scaled), Actual = convert_to_origin(Actual)) %>% 
  select(-lag1) -> lstm_results

lstm_results %>% 
  gather(a, b, -DAYID) %>% 
  ggplot(aes(DAYID, b, color = a)) + 
  geom_line() + 
  scale_color_manual(values = c("purple", "orange"), name = "Series:") + 
  my_theme() + 
  labs(x = NULL, y = NULL, subtitle = "Approach Used: Long Short Term Memory (LSTM) Networks", 
       title = "One-step Ahead Prediction for Cash Withdrawals (120 Consecutive Days)")

#===================
#  ARIMA Approach
#===================

library(fpp2)

predict_from_arima <- function(k) {
  
  n_ahead <- 1
  train_arima <- my_case_lag %>% slice((1 + k):(450 + k)) %>% select(1:2)
  test_arima  <- my_case_lag %>% slice(450 + k + n_ahead) %>% select(1:2)
  
  # Automatic ARIMA model as proposed by Hyndman and Khandakar (2008): 
  my_arima <- auto.arima(train_arima[, 2] %>% ts(start = 1))
  
  # Use the model for forecasting: 
  predicted_arima <- forecast(my_arima, h = 1)$mean %>% as.vector()
  
  actual_predicted_df_test <- test_arima %>% 
    mutate(predicted = predicted_arima) 
  
  return(actual_predicted_df_test)
  
}

# Use this function: 

system.time(lapply(0:119, predict_from_arima) ->> arima_results)
##    user  system elapsed 
##  32.355  58.790  14.956
arima_results <- do.call("bind_rows", arima_results)

# Compare cash demands predicted by ARIMA approach and actuals: 

arima_results %<>%  
  rename(Actual = daily_cash, Predicted = predicted) 

arima_results %>% 
  gather(a, b, -DAYID) %>% 
  ggplot(aes(DAYID, b, color = a)) + 
  geom_line() + 
  scale_color_manual(values = c("purple", "orange"), name = "Series:") + 
  my_theme() + 
  labs(x = NULL, y = NULL, subtitle = "Approach Used: Auto ARIMA", 
       title = "One-step Ahead Prediction for Cash Withdrawals (120 Consecutive Days)")

# Function calculate some accuracy measures: 
get_accuracy_measures <- function(your_result_df) {

  act <- your_result_df %>% pull(Actual)
  pred <- your_result_df %>% pull(Predicted)
  err <- act - pred 
  per_err <- abs(err / act)
  
  # Mean Absolute Error (MAE): 
  mae <- err %>% abs() %>% mean()
  
  # Mean Squared Error (MSE): 
  mse <- mean(err^2)
  
  # Mean Absolute Percentage Error (MAPE): 
  mape <- 100*mean(per_err)
  
  # Return results: 
  return(data.frame(MAE = mae, MSE = mse, MAPE = mape, N = length(act)))
}

# Use above function for comparing: 

bind_rows(lstm_results %>% get_accuracy_measures(), arima_results %>% get_accuracy_measures) %>% 
  mutate(Approach = c("LSTM", "ARIMA")) %>% 
  select(Approach, everything()) %>% 
  mutate_if(is.numeric, function(x) {round(x, 2)}) %>% 
  knitr::kable()
Approach MAE MSE MAPE N
LSTM 46.03 7726.99 138.52 120
ARIMA 41.22 6465.79 110.74 120
LS0tCnRpdGxlOiAiRm9yZWNhc3RpbmcgRGFpbHkgQ2FzaCBEZW1hbmQ6IExvbmcgU2hvcnQgVGVybSBNZW1vcnkgdnMgQVJJTUEgQXBycm9hY2giIApzdWJ0aXRsZTogIlIgZm9yIEZ1biIKYXV0aG9yOiAiTmd1eWVuIENoaSBEdW5nIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgICMgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiAiZmxhdGx5IgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKLS0tCgpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSkKYGBgCgpgYGB7cn0KIyBDbGVhciB3b3Jrc3BhY2U6IApybShsaXN0ID0gbHMoKSkKCiMgSW1wb3J0IHBhY2thZ2VzIGZvciBkYXRhIG1hbmlwdWxhdGlvbjogCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG1hZ3JpdHRyKQoKIyBBIGZ1bmN0aW9uIGZvciBibGFjayB0aGVtZTogCgpteV90aGVtZSA8LSBmdW5jdGlvbiguLi4pIHsKICB0aGVtZSgKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwgIAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiLCBsaW5laGVpZ2h0ID0gMC45KSwgIAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiLCBsaW5laGVpZ2h0ID0gMC45KSwgIAogICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgID0gIDAuMiksICAKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ3aGl0ZSIsIG1hcmdpbiA9IG1hcmdpbigwLCAxMCwgMCwgMCkpLCAgCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiLCBhbmdsZSA9IDkwLCBtYXJnaW4gPSBtYXJnaW4oMCwgMTAsIDAsIDApKSwgIAogICAgYXhpcy50aWNrcy5sZW5ndGggPSB1bml0KDAuMywgImxpbmVzIiksICAgCiAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9IE5BLCBmaWxsID0gImdyYXkxMCIpLCAgCiAgICBsZWdlbmQua2V5ID0gZWxlbWVudF9yZWN0KGNvbG9yID0gIndoaXRlIiwgIGZpbGwgPSAiZ3JheTEwIiksICAKICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMS4yLCAibGluZXMiKSwgIAogICAgbGVnZW5kLmtleS5oZWlnaHQgPSBOVUxMLCAgCiAgICBsZWdlbmQua2V5LndpZHRoID0gTlVMTCwgICAgICAKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIndoaXRlIiksICAKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAsIGNvbG9yID0gIndoaXRlIiksICAKICAgIGxlZ2VuZC50ZXh0LmFsaWduID0gTlVMTCwgIAogICAgbGVnZW5kLnRpdGxlLmFsaWduID0gTlVMTCwgIAogICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIsICAKICAgIGxlZ2VuZC5ib3ggPSBOVUxMLCAKICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmF5MTAiLCBjb2xvciAgPSAgTkEpLCAgCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyZXkzNSIpLCAgCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyZXkyMCIpLCAgCiAgICBwYW5lbC5zcGFjaW5nID0gdW5pdCgwLjUsICJsaW5lcyIpLCAgIAogICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyZXkzMCIsIGNvbG9yID0gImdyZXkxMCIpLCAgCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiKSwgIAogICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIndoaXRlIiwgYW5nbGUgPSAtOTApLCAgCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiZ3JheTEwIiwgZmlsbCA9ICJncmF5MTAiKSwgIAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ3aGl0ZSIsIGhqdXN0ID0gMCwgbGluZWhlaWdodCA9IDEuMjUsIG1hcmdpbiA9IG1hcmdpbigyLCAyLCAyLCAyKSksICAKICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3IgPSAid2hpdGUiLCBoanVzdCA9IDAsIG1hcmdpbiA9IG1hcmdpbigyLCAyLCAyLCAyKSksICAKICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJ3aGl0ZSIsIGhqdXN0ID0gMCksICAKICAgIHBsb3QubWFyZ2luID0gdW5pdChyZXAoMSwgNCksICJsaW5lcyIpKQogIAp9CgojIEluc3BlY3QgZGF0YSBwYXRoIGFuZCBsb2FkIGRhdGE6IApteV9wYXRoIDwtIGRpcigiL2hvbWUvY2hpZHVuZy9EZXNrdG9wL2F0bV9jYXNoX2RhdGEiLCBmdWxsLm5hbWVzID0gVFJVRSkKbXlfY2FzZSA8LSByZWFkX2NzdihteV9wYXRoWzFdKQoKIyBDcmVhdGUgbGFnIDEgdmFyaWFibGU6IApteV9jYXNlX2xhZyA8LSBteV9jYXNlICU+JSAKICBtdXRhdGUobGFnMSA9IGxhZyhkYWlseV9jYXNoLCAxKSkgJT4lIAogIG5hLm9taXQoKQoKIyBTY2FsZSAwLTEgZm9yIHZhcmlhYmxlczogCgpteV9jYXNlX2xhZ19zY2FsZWQgPC0gbXlfY2FzZV9sYWcgJT4lIAogIG11dGF0ZV9pZihpcy5udW1lcmljLCBmdW5jdGlvbih4KSB7KHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSl9KQoKCiMgU3BsaXQgZGF0YTogCk4gPC0gbnJvdyhteV9jYXNlX2xhZ19zY2FsZWQpCnRyYWluX2RmIDwtIG15X2Nhc2VfbGFnX3NjYWxlZCAlPiUgc2xpY2UoMTo0NTApCnRlc3RfZGYgPC0gbXlfY2FzZV9sYWdfc2NhbGVkICU+JSBzbGljZSg0NTE6TikKCiMgU3BlY2lmeSBmZWF0dXJlcyBhbmQgb3V0cHV0OiAKCnlfdHJhaW4gPC0gdHJhaW5fZGYgJT4lIHB1bGwoMikKeF90cmFpbiA8LSB0cmFpbl9kZiAlPiUgcHVsbCgzKQoKeV90ZXN0IDwtIHRlc3RfZGYgJT4lIHB1bGwoMikKeF90ZXN0IDwtIHRlc3RfZGYgJT4lIHB1bGwoMykKCgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgIExvbmcgU2hvcnQgVGVybSBNZW1vcnkgKExTVE0pIE5ldHdvcmtzIChWZXJzaW9uIDEpCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCiMgTG9hZCBwYWNrYWdlIGtlcmFzOiAKbGlicmFyeShrZXJhcykKCiMgUmVzaGFwZSB0aGUgaW5wdXQgdG8gMy1kaW06IApkaW0oeF90cmFpbikgPC0gYyhsZW5ndGgoeF90cmFpbiksIDEsIDEpCgojIHNwZWNpZnkgcmVxdWlyZWQgYXJndW1lbnRzOiAKWF9zaGFwZTIgPC0gZGltKHhfdHJhaW4pWzJdClhfc2hhcGUzIDwtIGRpbSh4X3RyYWluKVszXQpiYXRjaF9zaXplIDwtIDEgICAgICAgICAgICAgICAgCnVuaXRzIDwtIDEgICAgICAgICAgICAgICAgICAgICAKCiMgU3BlY2lmeSBjb25kaXRpb25zIGZvciBMU1RNIE1vZGVsOiAKCm1vZGVsIDwtIGtlcmFzX21vZGVsX3NlcXVlbnRpYWwoKSAlPiUKICBsYXllcl9sc3RtKHVuaXRzLCBiYXRjaF9pbnB1dF9zaGFwZSA9IGMoYmF0Y2hfc2l6ZSwgWF9zaGFwZTIsIFhfc2hhcGUzKSwgc3RhdGVmdWwgPSBUUlVFKSAlPiUKICBsYXllcl9kZW5zZSh1bml0cyA9IDEpICU+JSAKICBjb21waWxlKGxvc3MgPSAibWVhbl9zcXVhcmVkX2Vycm9yIiwKICAgICAgICAgIG9wdGltaXplciA9IG9wdGltaXplcl9hZGFtKGxyID0gMC4wMDUsIGRlY2F5ID0gMWUtMTApLCAgCiAgICAgICAgICBtZXRyaWNzID0gYygiYWNjdXJhY3kiKSkKCiMgVHJhaW4gLyBmaXQgTFNUTSBNb2RlbDogCgpFcG9jaHMgPC0gMTAgICAKZm9yKGkgaW4gMTpFcG9jaHMpIHsKICBtb2RlbCAlPiUgCiAgICBmaXQoeF90cmFpbiwgCiAgICAgICAgeV90cmFpbiwgCiAgICAgICAgZXBvY2hzID0gMSwKICAgICAgICBiYXRjaF9zaXplID0gYmF0Y2hfc2l6ZSwgCiAgICAgICAgdmVyYm9zZSA9IDEsIAogICAgICAgIHNodWZmbGUgPSBGQUxTRSkKICBtb2RlbCAlPiUgcmVzZXRfc3RhdGVzKCkKfQoKIyBBIGZ1bmN0aW9uIGZvciBmb3JlY2FzdGluZzogCgpteV9wcmVkaWN0X2Zyb21fbHN0bSA8LSBmdW5jdGlvbihrKSB7CiAgeCA8LSB4X3Rlc3Rba10KICBkaW0oeCkgPC0gYygxLCAxLCAxKQogIHlfaGF0IDwtIG1vZGVsICU+JSBwcmVkaWN0KHgsIGJhdGNoX3NpemUgPSAxKQogIHJldHVybih5X2hhdCkKfQoKIyBNYWtlIHByZWRpY3Rpb25zOiAKc2FwcGx5KDE6bGVuZ3RoKHhfdGVzdCksIG15X3ByZWRpY3RfZnJvbV9sc3RtKSAtPj4gcHJlZF9zY2FsZWQKCiMgQSBmdW5jdGlvbiBjb3ZlcnQgdG8gb3JpZ2luOiAKCmNvbnZlcnRfdG9fb3JpZ2luIDwtIGZ1bmN0aW9uKHgpIHsKICB4X2Jhc2UgPC0gbXlfY2FzZV9sYWckZGFpbHlfY2FzaAogIHkgPC0geCoobWF4KHhfYmFzZSkgLSBtaW4oeF9iYXNlKSkgKyBtaW4oeF9iYXNlKQogIHJldHVybih5KQp9CgojIENvbXBhcmUgYWN0dWFscyB2cyBwcmVkaWN0aW9uczogCnRlc3RfZGYgJT4lIAogIHJlbmFtZShBY3R1YWwgPSBkYWlseV9jYXNoKSAlPiUgCiAgbXV0YXRlKFByZWRpY3RlZCA9IGNvbnZlcnRfdG9fb3JpZ2luKHByZWRfc2NhbGVkKSwgQWN0dWFsID0gY29udmVydF90b19vcmlnaW4oQWN0dWFsKSkgJT4lIAogIHNlbGVjdCgtbGFnMSkgLT4gbHN0bV9yZXN1bHRzCgpsc3RtX3Jlc3VsdHMgJT4lIAogIGdhdGhlcihhLCBiLCAtREFZSUQpICU+JSAKICBnZ3Bsb3QoYWVzKERBWUlELCBiLCBjb2xvciA9IGEpKSArIAogIGdlb21fbGluZSgpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInB1cnBsZSIsICJvcmFuZ2UiKSwgbmFtZSA9ICJTZXJpZXM6IikgKyAKICBteV90aGVtZSgpICsgCiAgbGFicyh4ID0gTlVMTCwgeSA9IE5VTEwsIHN1YnRpdGxlID0gIkFwcHJvYWNoIFVzZWQ6IExvbmcgU2hvcnQgVGVybSBNZW1vcnkgKExTVE0pIE5ldHdvcmtzIiwgCiAgICAgICB0aXRsZSA9ICJPbmUtc3RlcCBBaGVhZCBQcmVkaWN0aW9uIGZvciBDYXNoIFdpdGhkcmF3YWxzICgxMjAgQ29uc2VjdXRpdmUgRGF5cykiKQoKIz09PT09PT09PT09PT09PT09PT0KIyAgQVJJTUEgQXBwcm9hY2gKIz09PT09PT09PT09PT09PT09PT0KCmxpYnJhcnkoZnBwMikKCnByZWRpY3RfZnJvbV9hcmltYSA8LSBmdW5jdGlvbihrKSB7CiAgCiAgbl9haGVhZCA8LSAxCiAgdHJhaW5fYXJpbWEgPC0gbXlfY2FzZV9sYWcgJT4lIHNsaWNlKCgxICsgayk6KDQ1MCArIGspKSAlPiUgc2VsZWN0KDE6MikKICB0ZXN0X2FyaW1hICA8LSBteV9jYXNlX2xhZyAlPiUgc2xpY2UoNDUwICsgayArIG5fYWhlYWQpICU+JSBzZWxlY3QoMToyKQogIAogICMgQXV0b21hdGljIEFSSU1BIG1vZGVsIGFzIHByb3Bvc2VkIGJ5IEh5bmRtYW4gYW5kIEtoYW5kYWthciAoMjAwOCk6IAogIG15X2FyaW1hIDwtIGF1dG8uYXJpbWEodHJhaW5fYXJpbWFbLCAyXSAlPiUgdHMoc3RhcnQgPSAxKSkKICAKICAjIFVzZSB0aGUgbW9kZWwgZm9yIGZvcmVjYXN0aW5nOiAKICBwcmVkaWN0ZWRfYXJpbWEgPC0gZm9yZWNhc3QobXlfYXJpbWEsIGggPSAxKSRtZWFuICU+JSBhcy52ZWN0b3IoKQogIAogIGFjdHVhbF9wcmVkaWN0ZWRfZGZfdGVzdCA8LSB0ZXN0X2FyaW1hICU+JSAKICAgIG11dGF0ZShwcmVkaWN0ZWQgPSBwcmVkaWN0ZWRfYXJpbWEpIAogIAogIHJldHVybihhY3R1YWxfcHJlZGljdGVkX2RmX3Rlc3QpCiAgCn0KCiMgVXNlIHRoaXMgZnVuY3Rpb246IAoKc3lzdGVtLnRpbWUobGFwcGx5KDA6MTE5LCBwcmVkaWN0X2Zyb21fYXJpbWEpIC0+PiBhcmltYV9yZXN1bHRzKQphcmltYV9yZXN1bHRzIDwtIGRvLmNhbGwoImJpbmRfcm93cyIsIGFyaW1hX3Jlc3VsdHMpCgojIENvbXBhcmUgY2FzaCBkZW1hbmRzIHByZWRpY3RlZCBieSBBUklNQSBhcHByb2FjaCBhbmQgYWN0dWFsczogCgphcmltYV9yZXN1bHRzICU8PiUgIAogIHJlbmFtZShBY3R1YWwgPSBkYWlseV9jYXNoLCBQcmVkaWN0ZWQgPSBwcmVkaWN0ZWQpIAoKYXJpbWFfcmVzdWx0cyAlPiUgCiAgZ2F0aGVyKGEsIGIsIC1EQVlJRCkgJT4lIAogIGdncGxvdChhZXMoREFZSUQsIGIsIGNvbG9yID0gYSkpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicHVycGxlIiwgIm9yYW5nZSIpLCBuYW1lID0gIlNlcmllczoiKSArIAogIG15X3RoZW1lKCkgKyAKICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwgc3VidGl0bGUgPSAiQXBwcm9hY2ggVXNlZDogQXV0byBBUklNQSIsIAogICAgICAgdGl0bGUgPSAiT25lLXN0ZXAgQWhlYWQgUHJlZGljdGlvbiBmb3IgQ2FzaCBXaXRoZHJhd2FscyAoMTIwIENvbnNlY3V0aXZlIERheXMpIikKCiMgRnVuY3Rpb24gY2FsY3VsYXRlIHNvbWUgYWNjdXJhY3kgbWVhc3VyZXM6IApnZXRfYWNjdXJhY3lfbWVhc3VyZXMgPC0gZnVuY3Rpb24oeW91cl9yZXN1bHRfZGYpIHsKCiAgYWN0IDwtIHlvdXJfcmVzdWx0X2RmICU+JSBwdWxsKEFjdHVhbCkKICBwcmVkIDwtIHlvdXJfcmVzdWx0X2RmICU+JSBwdWxsKFByZWRpY3RlZCkKICBlcnIgPC0gYWN0IC0gcHJlZCAKICBwZXJfZXJyIDwtIGFicyhlcnIgLyBhY3QpCiAgCiAgIyBNZWFuIEFic29sdXRlIEVycm9yIChNQUUpOiAKICBtYWUgPC0gZXJyICU+JSBhYnMoKSAlPiUgbWVhbigpCiAgCiAgIyBNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSk6IAogIG1zZSA8LSBtZWFuKGVycl4yKQogIAogICMgTWVhbiBBYnNvbHV0ZSBQZXJjZW50YWdlIEVycm9yIChNQVBFKTogCiAgbWFwZSA8LSAxMDAqbWVhbihwZXJfZXJyKQogIAogICMgUmV0dXJuIHJlc3VsdHM6IAogIHJldHVybihkYXRhLmZyYW1lKE1BRSA9IG1hZSwgTVNFID0gbXNlLCBNQVBFID0gbWFwZSwgTiA9IGxlbmd0aChhY3QpKSkKfQoKIyBVc2UgYWJvdmUgZnVuY3Rpb24gZm9yIGNvbXBhcmluZzogCgpiaW5kX3Jvd3MobHN0bV9yZXN1bHRzICU+JSBnZXRfYWNjdXJhY3lfbWVhc3VyZXMoKSwgYXJpbWFfcmVzdWx0cyAlPiUgZ2V0X2FjY3VyYWN5X21lYXN1cmVzKSAlPiUgCiAgbXV0YXRlKEFwcHJvYWNoID0gYygiTFNUTSIsICJBUklNQSIpKSAlPiUgCiAgc2VsZWN0KEFwcHJvYWNoLCBldmVyeXRoaW5nKCkpICU+JSAKICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZnVuY3Rpb24oeCkge3JvdW5kKHgsIDIpfSkgJT4lIAogIGtuaXRyOjprYWJsZSgpCmBgYAoK