library(ggplot2)
library(forecast)
## Warning: package 'forecast' was built under R version 4.3.3
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(tseries)
## Warning: package 'tseries' was built under R version 4.3.3
library(zoo)
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(lubridate)
## 
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(TSA)
## Registered S3 methods overwritten by 'TSA':
##   method       from    
##   fitted.Arima forecast
##   plot.Arima   forecast
## 
## Attaching package: 'TSA'
## The following objects are masked from 'package:stats':
## 
##     acf, arima
## The following object is masked from 'package:utils':
## 
##     tar
library(xts)
## Warning: package 'xts' was built under R version 4.3.3
library(dplyr)
## 
## ######################### Warning from 'xts' package ##########################
## #                                                                             #
## # The dplyr lag() function breaks how base R's lag() function is supposed to  #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
## # source() into this session won't work correctly.                            #
## #                                                                             #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
## # dplyr from breaking base R's lag() function.                                #
## #                                                                             #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## ###############################################################################
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:xts':
## 
##     first, last
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(scales)
library(vars)
## Loading required package: MASS
## 
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
## 
##     select
## Loading required package: strucchange
## Loading required package: sandwich
## Loading required package: urca
## Warning: package 'urca' was built under R version 4.3.3
## Loading required package: lmtest
library(readxl)
library(car)
## Loading required package: carData
## 
## Attaching package: 'car'
## The following object is masked from 'package:dplyr':
## 
##     recode
library(glmnet)
## Loading required package: Matrix
## Loaded glmnet 4.1-8
library(gridExtra)
## 
## Attaching package: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine

I. Introduction

Time series analysis is a statistical technique that involves analyzing data points collected or recorded at specific time intervals. This type of analysis is used to identify patterns, trends, cycles, and seasonal variations in the data over time. The primary goal of time series analysis is to forecast future values based on historical data, making it particularly useful in fields such as finance, economics, weather forecasting, and many other areas where data evolves over time (Chatfield, 2003).

Stationarity is a fundamental concept in time series analysis, which implies that the statistical properties of the series, such as mean and variance, remain constant over time. Stationary time series are easier to model and predict because their behavior is consistent over time. Non-stationary data, on the other hand, may exhibit trends, seasonality, or varying variance, complicating the analysis and potentially leading to misleading results. Ensuring stationarity often involves transforming or differencing the data to remove trends and seasonality, allowing for more accurate modeling and forecasting (Box et al., 2015).

Time series analysis is crucial for business intelligence because it enables organizations to make data-driven decisions by forecasting future trends, demand, or performance based on historical data. It can be applied to various business problems, such as predicting sales, stock prices, or customer behavior. This analysis helps businesses anticipate changes, manage inventory, optimize marketing strategies, and improve operational efficiency by understanding patterns over time and responding proactively.

Sources Chatfield, C. (2003). The Analysis of Time Series: An Introduction. Chapman and Hall/CRC. Box, G.E.P., Jenkins, G.M., Reinsel, G.C., & Ljung, G.M. (2015). Time Series Analysis: Forecasting and Control. Wiley.

II. Description of the Problem Situation

For this problem situation, our objective is to build a predictive model for Arca Continental, mainly for their biggest and most popular branch in Latin America, Coca-Cola. We want to create a model that can successfully forecast the a designated target variable, with which we can predict the company’s box sales (referred to as “grids”) in the metropolitan area of Guadalajara, Jalisco.

III. Data and Methodology

Format transformation

# Display the structure of the dataset
str(cocacola)
## tibble [48 × 15] (S3: tbl_df/tbl/data.frame)
##  $ tperiod           : POSIXct[1:48], format: "2021-01-15" "2021-02-15" ...
##  $ sales_unitboxes   : num [1:48] 5516689 5387496 5886747 6389182 6448275 ...
##  $ consumer_sentiment: num [1:48] 38.1 37.5 38.5 37.8 38 ...
##  $ CPI               : num [1:48] 87.1 87.3 87.6 87.4 87 ...
##  $ inflation_rate    : num [1:48] -0.09 0.19 0.41 -0.26 -0.5 0.17 0.15 0.21 0.37 0.51 ...
##  $ unemp_rate        : num [1:48] 0.0523 0.0531 0.0461 0.051 0.0552 ...
##  $ gdp_percapita     : num [1:48] 11660 11660 11660 11626 11626 ...
##  $ itaee             : num [1:48] 104 104 104 108 108 ...
##  $ itaee_growth      : num [1:48] 0.0497 0.0497 0.0497 0.0318 0.0318 ...
##  $ pop_density       : num [1:48] 98.5 98.5 98.5 98.8 98.8 ...
##  $ job_density       : num [1:48] 18.3 18.5 18.6 18.7 18.7 ...
##  $ pop_minwage       : num [1:48] 9.66 9.66 9.66 9.59 9.59 ...
##  $ exchange_rate     : num [1:48] 14.7 14.9 15.2 15.2 15.3 ...
##  $ max_temperature   : num [1:48] 28 31 29 32 34 32 29 29 29 29 ...
##  $ holiday_month     : num [1:48] 0 0 0 1 0 0 0 0 1 0 ...
# Convert 'holiday_month' to a factor
cocacola$holiday_month <- as.factor(cocacola$holiday_month)
# Convert 'tperiod' to a Date object if it isn't already one
cocacola$tperiod <- as.Date(cocacola$tperiod, format = "%d-%m-%Y")

# Extract the day (which represents the year) and the month
day_month <- format(cocacola$tperiod, "%d-%m")

# Convert the day to a proper year (e.g., 0015 to 2015) and create the correct date
corrected_year <- as.numeric(substr(day_month, 1, 2)) + 2000
corrected_month_day <- paste0(corrected_year, "-", substr(day_month, 4, 5))

# Convert back to Date object
cocacola$tperiod <- as.Date(paste0(corrected_month_day, "-01"), format = "%Y-%m-%d")

# Check the results
head(cocacola$tperiod)
## [1] "2015-01-01" "2015-02-01" "2015-03-01" "2015-04-01" "2015-05-01"
## [6] "2015-06-01"
# structure after conversions
str(cocacola)
## tibble [48 × 15] (S3: tbl_df/tbl/data.frame)
##  $ tperiod           : Date[1:48], format: "2015-01-01" "2015-02-01" ...
##  $ sales_unitboxes   : num [1:48] 5516689 5387496 5886747 6389182 6448275 ...
##  $ consumer_sentiment: num [1:48] 38.1 37.5 38.5 37.8 38 ...
##  $ CPI               : num [1:48] 87.1 87.3 87.6 87.4 87 ...
##  $ inflation_rate    : num [1:48] -0.09 0.19 0.41 -0.26 -0.5 0.17 0.15 0.21 0.37 0.51 ...
##  $ unemp_rate        : num [1:48] 0.0523 0.0531 0.0461 0.051 0.0552 ...
##  $ gdp_percapita     : num [1:48] 11660 11660 11660 11626 11626 ...
##  $ itaee             : num [1:48] 104 104 104 108 108 ...
##  $ itaee_growth      : num [1:48] 0.0497 0.0497 0.0497 0.0318 0.0318 ...
##  $ pop_density       : num [1:48] 98.5 98.5 98.5 98.8 98.8 ...
##  $ job_density       : num [1:48] 18.3 18.5 18.6 18.7 18.7 ...
##  $ pop_minwage       : num [1:48] 9.66 9.66 9.66 9.59 9.59 ...
##  $ exchange_rate     : num [1:48] 14.7 14.9 15.2 15.2 15.3 ...
##  $ max_temperature   : num [1:48] 28 31 29 32 34 32 29 29 29 29 ...
##  $ holiday_month     : Factor w/ 2 levels "0","1": 1 1 1 2 1 1 1 1 2 1 ...

Dependent variable analysis

ggplot(cocacola, aes(x=tperiod, y=sales_unitboxes)) + 
  geom_line() + 
  labs(title='Box Sales Over Time', x='Time', y='sales_unitboxes') + 
  theme(axis.text.x = element_text(angle=45, hjust=1))

cocacola$Moving_Avg <- rollmean(cocacola$sales_unitboxes, 12, fill = NA)
# Plot moving average
ggplot(cocacola, aes(x=tperiod)) + 
  geom_line(aes(y=sales_unitboxes, colour="Original")) + 
  geom_line(aes(y=Moving_Avg, colour="4-Periods Moving Average")) +
  labs(title='Moving Average Plot', x='Date', y='Sales (Unit Boxes)') +
  scale_colour_manual("", breaks = c("Original", "12-Periods Moving Average"), values = c("blue", "red"))
## Warning: Removed 11 rows containing missing values or values outside the scale range
## (`geom_line()`).

cocacola_ts <- ts(cocacola$sales_unitboxes, frequency=12, start=c(2015, 1))
decomposed <- decompose(cocacola_ts, type="additive")

plot(decomposed)

adf_test <- adf.test(cocacola$sales_unitboxes, alternative="stationary")
## Warning in adf.test(cocacola$sales_unitboxes, alternative = "stationary"):
## p-value smaller than printed p-value
print(paste("ADF Statistic: ", adf_test$statistic))
## [1] "ADF Statistic:  -4.42816565181968"
print(paste("p-value: ", adf_test$p.value))
## [1] "p-value:  0.01"
sales_ts <- ts(cocacola$sales_unitboxes, frequency=12) 

# Plot the autocorrelation function (ACF)
acf(sales_ts, main="Autocorrelation of Sales (Unit Boxes)")

# Plot the partial autocorrelation function (PACF)
pacf(sales_ts, main="Partial Autocorrelation of Sales (Unit Boxes)")

The dependent variable sales_unitboxes has shown to have an upward trend that generally increases over time, as well as clear seasonality. It is also stationary, showing a p-value of 0.01 and autocorrelation in the ACF plot.

IV. Time Series Regression Analysis

Model Comparison

# ARMA
p_values <- 0:5  
q_values <- 0:5 

best_aic_arma <- Inf
best_order_arma <- c(0, 0)
best_model_arma <- NULL

for (p in p_values) {
  for (q in q_values) {
    model <- tryCatch({
      arima(sales_ts, order = c(p, 0, q))  # Modelo ARMA (sin diferenciación, d = 0)
    }, error = function(e) NULL)  
    
    if (!is.null(model)) {
      current_aic <- AIC(model)  
      
      if (current_aic < best_aic_arma) {
        best_aic_arma <- current_aic
        best_order_arma <- c(p, q)
        best_model_arma <- model
      }
    }
  }
}
## Warning in stats::arima(x = x, order = order, seasonal = seasonal, xreg = xreg,
## : possible convergence problem: optim gave code = 1
# ARIMA
p_values <- 0:5  
d_values <- 1:2  
q_values <- 0:5 

best_aic_arima <- Inf
best_order_arima <- c(0, 1, 0)
best_model_arima <- NULL

for (p in p_values) {
  for (d in d_values) {
    for (q in q_values) {
      model <- tryCatch({
        arima(sales_ts, order = c(p, d, q))  # Modelo ARIMA
      }, error = function(e) NULL)  
    
      if (!is.null(model)) {
        current_aic <- AIC(model)  
        
        if (current_aic < best_aic_arima) {
          best_aic_arima <- current_aic
          best_order_arima <- c(p, d, q)
          best_model_arima <- model
        }
      }
    }
  }
}
## Warning in log(s2): NaNs produced
## Warning in log(s2): NaNs produced

## Warning in log(s2): NaNs produced

## Warning in log(s2): NaNs produced

## Warning in log(s2): NaNs produced
# AR (Autoregressive)
p_values <- 0:5  

best_aic_ar <- Inf
best_order_ar <- c(0, 0)
best_model_ar <- NULL

for (p in p_values) {
  model <- tryCatch({
    arima(sales_ts, order = c(p, 0, 0))  # Modelo AR (sin componente MA, q = 0)
  }, error = function(e) NULL)  
    
  if (!is.null(model)) {
    current_aic <- AIC(model)  
    
    if (current_aic < best_aic_ar) {
      best_aic_ar <- current_aic
      best_order_ar <- c(p, 0)
      best_model_ar <- model
    }
  }
}

# MA (Moving Average)
q_values <- 0:5  

best_aic_ma <- Inf
best_order_ma <- c(0, 0)
best_model_ma <- NULL

for (q in q_values) {
  model <- tryCatch({
    arima(sales_ts, order = c(0, 0, q))  # Modelo MA (sin componente AR, p = 0)
  }, error = function(e) NULL)  
    
  if (!is.null(model)) {
    current_aic <- AIC(model)  
    
    if (current_aic < best_aic_ma) {
      best_aic_ma <- current_aic
      best_order_ma <- c(0, q)
      best_model_ma <- model
    }
  }
}

# Comparar los modelos con base en el AIC
modelos <- c("ARMA", "ARIMA", "AR", "MA")
mejores_aic <- c(best_aic_arma, best_aic_arima, best_aic_ar, best_aic_ma)

mejor_modelo <- modelos[which.min(mejores_aic)]
mejor_aic <- min(mejores_aic)

print(paste("El mejor modelo es:", mejor_modelo))
## [1] "El mejor modelo es: ARIMA"
print(paste("El mejor AIC es:", mejor_aic))
## [1] "El mejor AIC es: 1354.53371359874"
# Mostrar el resumen del mejor modelo
if (mejor_modelo == "ARMA") {
  summary(best_model_arma)
} else if (mejor_modelo == "ARIMA") {
  summary(best_model_arima)
} else if (mejor_modelo == "AR") {
  summary(best_model_ar)
} else if (mejor_modelo == "MA") {
  summary(best_model_ma)
}
## 
## Call:
## arima(x = sales_ts, order = c(p, d, q))
## 
## Coefficients:
##           ar1      ar2      ma1      ma2      ma3     ma4     ma5
##       -0.7718  -0.5521  -0.5689  -0.3367  -0.8268  0.0062  0.7522
## s.e.   0.1690   0.1435   0.1924   0.2089   0.1744  0.1985  0.1731
## 
## sigma^2 estimated as 1.885e+11:  log likelihood = -669.27,  aic = 1352.53
## 
## Training set error measures:
## Warning in trainingaccuracy(object, test, d, D): test elements must be within
## sample
##               ME RMSE MAE MPE MAPE
## Training set NaN  NaN NaN NaN  NaN

Forecast using ARIMA

# Definir los valores de p, d y q
p_values <- 0:5  
d_values <- 1:2  
q_values <- 0:5 

# Inicializar el mejor AIC, el mejor orden y el mejor modelo
best_aic_arima <- Inf
best_order_arima <- c(0, 1, 0)
best_model_arima <- NULL

# Iterar sobre los posibles valores de p, d y q
for (p in p_values) {
  for (d in d_values) {
    for (q in q_values) {
      model <- tryCatch({
        Arima(sales_ts, order = c(p, d, q))  # Modelo ARIMA
      }, error = function(e) NULL)  
    
      if (!is.null(model)) {
        current_aic <- AIC(model)  
        
        if (current_aic < best_aic_arima) {
          best_aic_arima <- current_aic
          best_order_arima <- c(p, d, q)
          best_model_arima <- model
        }
      }
    }
  }
}

# Verificar si se encontró un modelo
if (!is.null(best_model_arima)) {
  print(paste("Mejor modelo ARIMA:", paste(best_order_arima, collapse = ",")))
  print(paste("Mejor AIC ARIMA:", best_aic_arima))
  summary(best_model_arima)

  # Generar pronóstico a 5 períodos
  forecast_arima <- forecast(best_model_arima, h = 5)
  print(forecast_arima)

  # Graficar el pronóstico
  autoplot(forecast_arima) +
    labs(title = "Pronóstico a 5 períodos", x = "Tiempo", y = "Ventas (Cajas Unitarias)") +
    theme_minimal()

} else {
  print("No se encontró un modelo ARIMA válido.")
}
## [1] "Mejor modelo ARIMA: 2,2,5"
## [1] "Mejor AIC ARIMA: 1354.53371359874"
##       Point Forecast   Lo 80   Hi 80   Lo 95   Hi 95
## Jan 5        6823548 6196309 7450787 5864269 7782828
## Feb 5        6790215 6031908 7548521 5630485 7949944
## Mar 5        6909395 6092700 7726089 5660368 8158421
## Apr 5        6735581 5913620 7557542 5478500 7992662
## May 5        6822849 5996149 7649549 5558520 8087177

Time series model 2

Instructions: From the time series dataset, select 2 explanatory variables that might affect the sales unitboxes.

# Calcular la correlación entre sales_unitboxes y otras variables
correlation_matrix <- cor(cocacola[, c("sales_unitboxes", 
                                       "consumer_sentiment", 
                                       "gdp_percapita", 
                                       "CPI", 
                                       "inflation_rate", 
                                       "unemp_rate", 
                                       "exchange_rate", 
                                       "max_temperature", 
                                       "Moving_Avg")], 
                          use = "complete.obs")

# Ver la matriz de correlación
print(correlation_matrix)
##                    sales_unitboxes consumer_sentiment gdp_percapita         CPI
## sales_unitboxes         1.00000000         0.35524547    0.05786549  0.08807625
## consumer_sentiment      0.35524547         1.00000000   -0.52631149 -0.40446243
## gdp_percapita           0.05786549        -0.52631149    1.00000000  0.86618848
## CPI                     0.08807625        -0.40446243    0.86618848  1.00000000
## inflation_rate         -0.55212057        -0.52620078   -0.01727899  0.10332937
## unemp_rate              0.04231135         0.51952301   -0.76038173 -0.80824729
## exchange_rate          -0.06014764        -0.73031564    0.66028999  0.54067761
## max_temperature         0.68348780         0.03848755    0.37713494  0.17746354
## Moving_Avg              0.19129530        -0.20416077    0.73121504  0.65558384
##                    inflation_rate  unemp_rate exchange_rate max_temperature
## sales_unitboxes       -0.55212057  0.04231135   -0.06014764      0.68348780
## consumer_sentiment    -0.52620078  0.51952301   -0.73031564      0.03848755
## gdp_percapita         -0.01727899 -0.76038173    0.66028999      0.37713494
## CPI                    0.10332937 -0.80824729    0.54067761      0.17746354
## inflation_rate         1.00000000 -0.19810803    0.41019512     -0.55467320
## unemp_rate            -0.19810803  1.00000000   -0.63120125     -0.11731604
## exchange_rate          0.41019512 -0.63120125    1.00000000      0.13404524
## max_temperature       -0.55467320 -0.11731604    0.13404524      1.00000000
## Moving_Avg            -0.10970141 -0.51257546    0.52480755      0.25792091
##                    Moving_Avg
## sales_unitboxes     0.1912953
## consumer_sentiment -0.2041608
## gdp_percapita       0.7312150
## CPI                 0.6555838
## inflation_rate     -0.1097014
## unemp_rate         -0.5125755
## exchange_rate       0.5248076
## max_temperature     0.2579209
## Moving_Avg          1.0000000
# Filtrar las correlaciones con sales_unitboxes
sales_correlations <- correlation_matrix[1, -1]  # Excluimos la primera fila que es la de sales_unitboxes

# Ver las correlaciones ordenadas
print(sales_correlations[order(abs(sales_correlations), decreasing = TRUE)])
##    max_temperature     inflation_rate consumer_sentiment         Moving_Avg 
##         0.68348780        -0.55212057         0.35524547         0.19129530 
##                CPI      exchange_rate      gdp_percapita         unemp_rate 
##         0.08807625        -0.06014764         0.05786549         0.04231135
# Diferenciar las series max_temperature e inflation_rate para hacerlas estacionarias
max_temperature_diff <- diff(cocacola$max_temperature)
inflation_rate_diff <- diff(cocacola$inflation_rate)

# Escalar las variables exógenas (max_temperature_diff e inflation_rate_diff)
# Alineamos las variables eliminando la primera observación de sales_unitboxes
explanatory_variables <- scale(cbind(max_temperature_diff, inflation_rate_diff))

# Verificar que no haya NA en las variables exógenas escaladas y en sales_unitboxes
sales_ts <- ts(na.omit(cocacola$sales_unitboxes)[-1], frequency = 12, start = c(2015, 1))  # Ajustamos sales_unitboxes para que coincida con las variables diferenciadas

# Utilizar el mejor modelo ARIMA encontrado previamente para el ajuste del ARIMAX
best_model_arimax <- Arima(sales_ts, order = best_order_arima, xreg = explanatory_variables)

# Mostrar el resumen del modelo ARIMAX ajustado
summary(best_model_arimax)
## Series: sales_ts 
## Regression with ARIMA(2,2,5) errors 
## 
## Coefficients:
##           ar1      ar2      ma1      ma2      ma3      ma4     ma5
##       -0.7896  -0.5474  -0.5440  -0.3685  -0.8064  -0.0319  0.7796
## s.e.   0.1707   0.1443   0.1853   0.2102   0.1695   0.2031  0.1736
##       max_temperature_diff  inflation_rate_diff
##                  -16830.07             146.7015
## s.e.              58146.01           38965.7032
## 
## sigma^2 = 2.352e+11:  log likelihood = -654.79
## AIC=1329.59   AICc=1336.06   BIC=1347.65
## 
## Training set error measures:
##                     ME     RMSE      MAE       MPE     MAPE      MASE
## Training set -67568.01 424484.1 311138.3 -1.446068 4.960722 0.9214931
##                    ACF1
## Training set -0.0099564
# Realizar un pronóstico para los próximos 5 períodos
# Diferenciar los próximos valores de inflation_rate y max_temperature para el pronóstico
max_temperature_forecast_diff <- diff(cocacola$max_temperature[1:6])  # Diferenciar los próximos valores de max_temperature
inflation_rate_forecast_diff <- diff(cocacola$inflation_rate[1:6])  # Diferenciar los próximos valores de inflation_rate

# Escalar las variables proyectadas
explanatory_forecast <- scale(cbind(max_temperature_forecast_diff, inflation_rate_forecast_diff))

# Generar el pronóstico usando el modelo ARIMAX con las variables exógenas proyectadas y escaladas
forecast_arimax <- forecast(best_model_arimax, xreg = explanatory_forecast, h = 5)
## Warning in forecast.forecast_ARIMA(best_model_arimax, xreg =
## explanatory_forecast, : xreg contains different column names from the xreg used
## in training. Please check that the regressors are in the same order.
# Crear una tabla con los resultados del pronóstico
forecast_table <- data.frame(
  Period = time(forecast_arimax$mean),
  Forecast = as.numeric(forecast_arimax$mean),
  Lower_95 = as.numeric(forecast_arimax$lower[,2]),
  Upper_95 = as.numeric(forecast_arimax$upper[,2])
)

# Imprimir la tabla de pronóstico
print(forecast_table)
##     Period Forecast Lower_95 Upper_95
## 1 2018.917  6795278  5808470  7782087
## 2 2019.000  6763246  5564154  7962339
## 3 2019.083  6863740  5571412  8156067
## 4 2019.167  6696910  5394784  7999037
## 5 2019.250  6808127  5496554  8119700
# Graficar el pronóstico
autoplot(forecast_arimax) +
  labs(title = "Pronóstico a 5 períodos con ARIMAX usando variables escaladas y diferenciadas",
       x = "Tiempo", y = "Ventas (Cajas Unitarias)") +
  theme_minimal()

Describe the hypothetical relationship / impact between each selected explanatory variable and the dependent variable “sales unit boxes”.

There is highly likely a positive relationship between “max_temperature” and sales unit boxes of Coca-Cola. As the maximum temperature increases, particularly in warmer months or regions, the demand for cold beverages like Coca-Cola tends to rise. People are more inclined to purchase refreshing drinks when the weather is hot to stay hydrated and cool down.On the other hand, lower temperatures may reduce demand as people may prefer hot drinks or other beverages better suited for colder weather.

There is likely a negative relationship between “inflation_rate” and sales unit boxes of Coca-Cola. An increase in the inflation rate generally leads to a rise in the cost of goods and services, which can result in higher prices for Coca-Cola. As prices go up, consumers’ purchasing power declines, potentially reducing the overall sales volume. During periods of high inflation, consumers may prioritize essential items over discretionary spending. Coca-Cola, being a non-essential item, might experience reduced sales volume as consumers cut back on such expenditures.

Include a time series plot that displays the selected variables’ performance over the time period.

# Crear un nuevo dataframe que tenga todas las series alineadas correctamente
aligned_data <- data.frame(
  tperiod = cocacola$tperiod[-1],  # Eliminar la primera observación de tperiod
  sales_unitboxes = cocacola$sales_unitboxes[-1],  # Eliminar la primera observación
  max_temperature_diff = max_temperature_diff,  # Ya está alineada
  inflation_rate_diff = inflation_rate_diff  # Ya está alineada
)

# Graficar las ventas en un eje, y max_temperature_diff e inflation_rate_diff en el eje secundario
ggplot() +
  # Graficar sales_unitboxes en el eje primario
  geom_line(data = aligned_data, aes(x = tperiod, y = sales_unitboxes, color = "Ventas (Cajas Unitarias)"), size = 1) +
  
  # Graficar max_temperature_diff en el eje secundario (multiplicada para ajuste visual)
  geom_line(data = aligned_data, aes(x = tperiod, y = max_temperature_diff * 200000, color = "Temperatura Máxima (Diferenciada)"), size = 1, linetype = "dashed") +
  
  # Graficar inflation_rate_diff en el eje secundario (multiplicada para ajuste visual)
  geom_line(data = aligned_data, aes(x = tperiod, y = inflation_rate_diff * 2000000, color = "Tasa de Inflación (Diferenciada)"), size = 1, linetype = "dotted") +
  
  # Configuración de los ejes y sus respectivas escalas
  scale_y_continuous(
    name = "Ventas (Cajas Unitarias)",
    sec.axis = sec_axis(~ . / 100000, name = "Temperatura (°C) y Tasa de Inflación Diferenciada (%)")
  ) +
  
  # Etiquetas y título del gráfico
  labs(title = "Ventas, Temperatura Máxima y Tasa de Inflación Diferenciada con Ejes Secundarios",
       x = "Tiempo", color = "") +
  
  # Tema minimalista y ajuste de leyenda
  theme_minimal() +
  theme(legend.position = "bottom")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Do the selected explanatory variables show serial autocorrelation and a stationary process?

acf_max_temperature_diff <- Acf(max_temperature_diff, main = "ACF de max_temperature (Diferenciada)")

# Calcular la ACF para la serie diferenciada de inflation_rate
acf_inflation_rate_diff <- Acf(inflation_rate_diff, main = "ACF de inflation_rate (Diferenciada)")

# Prueba ADF para la serie diferenciada de max_temperature
adf_test_max_temp_diff <- adf.test(max_temperature_diff, alternative = "stationary")
print(adf_test_max_temp_diff)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  max_temperature_diff
## Dickey-Fuller = -4.0323, Lag order = 3, p-value = 0.01639
## alternative hypothesis: stationary
# Prueba ADF para la serie diferenciada de inflation_rate
adf_test_inflation_diff <- adf.test(inflation_rate_diff, alternative = "stationary")
print(adf_test_inflation_diff)
## 
##  Augmented Dickey-Fuller Test
## 
## data:  inflation_rate_diff
## Dickey-Fuller = -3.895, Lag order = 3, p-value = 0.02207
## alternative hypothesis: stationary

Explanation of test results

Max Temperature: The ADF test statistic of -4.0323 is significantly lower than the critical values at common confidence levels (like -3.43 for 5% significance level). The p-value is less than 0.05, indicating that we can reject the null hypothesis in favor of the alternative hypothesis that the series is in fact, stationary.

Inflation Rate: The ADF test statistic of -3.895 is also below the critical values for stationarity (e.g., -3.43 at 5% significance level).The p-value (0.02207) is less than 0.05, allowing us to reject the null hypothesis of non-stationarity, which in turn tells us that this series is also stationary.

ADF Conclusion: These tests suggest that the statistical properties of both series remain constant over time when differenced, making them suitable for time series analysis techniques that assume stationarity.

Autocorrelacion analysis after differentiation: Both series show autocorrelation at lag 1. The significant spike at lag 1 indicates that the differenced data is not completely free of autocorrelation. However, most of the other lags do not show significant autocorrelation, implying that the differencing has reduced much of the autocorrelation in the original series. In summary, while there is some remaining autocorrelation, particularly at lag 1 for both series, the data does not exhibit strong autocorrelation at other lags.

Briefly interpret the estimated VAR regression results. That is, is there a statistically significant relationship between the explanatory variable(s) and the dependent variable?

# Crear un dataframe con las series diferenciadas alineadas (sales_unitboxes e inflation_rate_diff)
var_data <- data.frame(
  sales_unitboxes = cocacola$sales_unitboxes[-1],  # Alinear eliminando la primera observación
  inflation_rate_diff = inflation_rate_diff  # Serie diferenciada ya preparada
)

# Selección del número óptimo de rezagos basado en AIC (equivalente a maxlags=4 y ic='aic' en Python)
lag_selection <- VARselect(var_data, lag.max = 4, type = "const")
optimal_lag <- lag_selection$selection["AIC(n)"]  # Extraemos el número óptimo de rezagos según el criterio AIC

# Estimación del modelo VAR con el número de rezagos óptimo
VAR_model <- VAR(var_data, p = optimal_lag, type = "const")

# Mostrar el resumen del modelo VAR ajustado
summary(VAR_model)
## 
## VAR Estimation Results:
## ========================= 
## Endogenous variables: sales_unitboxes, inflation_rate_diff 
## Deterministic variables: const 
## Sample size: 44 
## Log Likelihood: -636.938 
## Roots of the characteristic polynomial:
## 0.7906 0.7906 0.7632 0.7632 0.7044 0.7044
## Call:
## VAR(y = var_data, p = optimal_lag, type = "const")
## 
## 
## Estimation results for equation sales_unitboxes: 
## ================================================ 
## sales_unitboxes = sales_unitboxes.l1 + inflation_rate_diff.l1 + sales_unitboxes.l2 + inflation_rate_diff.l2 + sales_unitboxes.l3 + inflation_rate_diff.l3 + const 
## 
##                          Estimate Std. Error t value Pr(>|t|)   
## sales_unitboxes.l1      4.718e-01  1.514e-01   3.117  0.00353 **
## inflation_rate_diff.l1 -5.059e+05  2.238e+05  -2.260  0.02978 * 
## sales_unitboxes.l2     -1.179e-02  1.726e-01  -0.068  0.94590   
## inflation_rate_diff.l2 -2.666e+05  2.408e+05  -1.107  0.27529   
## sales_unitboxes.l3      6.755e-02  1.649e-01   0.410  0.68445   
## inflation_rate_diff.l3 -5.693e+05  2.055e+05  -2.771  0.00870 **
## const                   3.105e+06  1.136e+06   2.733  0.00956 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 463100 on 37 degrees of freedom
## Multiple R-Squared: 0.4425,  Adjusted R-squared: 0.3521 
## F-statistic: 4.895 on 6 and 37 DF,  p-value: 0.0008983 
## 
## 
## Estimation results for equation inflation_rate_diff: 
## ==================================================== 
## inflation_rate_diff = sales_unitboxes.l1 + inflation_rate_diff.l1 + sales_unitboxes.l2 + inflation_rate_diff.l2 + sales_unitboxes.l3 + inflation_rate_diff.l3 + const 
## 
##                          Estimate Std. Error t value Pr(>|t|)    
## sales_unitboxes.l1      6.975e-08  9.946e-08   0.701  0.48752    
## inflation_rate_diff.l1 -6.279e-01  1.470e-01  -4.271  0.00013 ***
## sales_unitboxes.l2      1.302e-07  1.134e-07   1.148  0.25825    
## inflation_rate_diff.l2 -4.187e-01  1.582e-01  -2.647  0.01186 *  
## sales_unitboxes.l3      3.463e-07  1.083e-07   3.196  0.00285 ** 
## inflation_rate_diff.l3 -2.437e-01  1.350e-01  -1.805  0.07922 .  
## const                  -3.523e+00  7.463e-01  -4.721 3.33e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Residual standard error: 0.3042 on 37 degrees of freedom
## Multiple R-Squared: 0.4472,  Adjusted R-squared: 0.3576 
## F-statistic: 4.989 on 6 and 37 DF,  p-value: 0.0007832 
## 
## 
## 
## Covariance matrix of residuals:
##                     sales_unitboxes inflation_rate_diff
## sales_unitboxes           2.145e+11          -4.109e+04
## inflation_rate_diff      -4.109e+04           9.256e-02
## 
## Correlation matrix of residuals:
##                     sales_unitboxes inflation_rate_diff
## sales_unitboxes              1.0000             -0.2917
## inflation_rate_diff         -0.2917              1.0000
# Realizar el pronóstico para los próximos 5 períodos
forecast_steps <- 5
var_forecast <- predict(VAR_model, n.ahead = forecast_steps)

# Crear un índice para los datos pronosticados
forecast_index <- seq(as.Date(tail(cocacola$tperiod, 1)), by = "month", length.out = forecast_steps)

# Crear un dataframe con los resultados del pronóstico
forecast_df <- data.frame(
  Period = forecast_index,
  sales_unitboxes_pred = var_forecast$fcst$sales_unitboxes[, 1],  # Predicción de sales_unitboxes
  inflation_rate_pred = var_forecast$fcst$inflation_rate_diff[, 1]  # Predicción de inflation_rate_diff
)

# Mostrar el pronóstico
print(forecast_df)
##       Period sales_unitboxes_pred inflation_rate_pred
## 1 2018-12-01              6485870         -0.13676581
## 2 2019-01-01              6423873         -0.02262784
## 3 2019-02-01              6635569          0.14764207
## 4 2019-03-01              6607301         -0.02813486
## 5 2019-04-01              6565818         -0.01274115
# Realizar la prueba de causalidad de Granger para ver si inflation_rate_diff causa sales_unitboxes
granger_test_1 <- causality(VAR_model, cause = "inflation_rate_diff")
print(granger_test_1$Granger)
## 
##  Granger causality H0: inflation_rate_diff do not Granger-cause
##  sales_unitboxes
## 
## data:  VAR object VAR_model
## F-Test = 3.7763, df1 = 3, df2 = 74, p-value = 0.01403
# Realizar la prueba de causalidad de Granger para ver si sales_unitboxes causa inflation_rate_diff
granger_test_2 <- causality(VAR_model, cause = "sales_unitboxes")
print(granger_test_2$Granger)
## 
##  Granger causality H0: sales_unitboxes do not Granger-cause
##  inflation_rate_diff
## 
## data:  VAR object VAR_model
## F-Test = 7.9594, df1 = 3, df2 = 74, p-value = 0.0001142
  • Based on the selected VAR_Model, show the forecast of coca sales in the Guadalajara Metropolitan Area for the next 5 periods. Display the estimated forecast in a time series plot.
# create df that combines real sales and forecast
combined_data <- data.frame(
  Period = c(as.Date(cocacola$tperiod[-1]), forecast_df$Period), # real dates and forecast
  sales_unitboxes = c(cocacola$sales_unitboxes[-1], rep(NA, forecast_steps)),  #Real Sales 
  sales_unitboxes_pred = c(rep(NA, length(cocacola$sales_unitboxes[-1])), forecast_df$sales_unitboxes_pred)  #  Forecast
)

ggplot() +
  # Plot sales_unitboxes
  geom_line(data = combined_data, aes(x = Period, y = sales_unitboxes, color = "Sales (Real)"), size = 1) +
  
  # Plot sales_unitboxes_pred
  geom_line(data = combined_data, aes(x = Period, y = sales_unitboxes_pred, color = "Sales (Forecast)"), size = 1, linetype = "dashed") +
  
 # Vertical line to mark the start of the forecast
  geom_vline(xintercept = as.numeric(forecast_df$Period[1]), color = "yellow", linetype = "dashed", size = 1) +
  
  
  labs(title = "Monthly Sales and VAR forecast of Unit Boxes",
       x = "Date", y = "Sales (Unit Boxes)") +
  
 
  scale_color_manual(values = c("Sales (Real)" = "blue", "Sales (Forecast)" = "green")) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  theme(legend.position = "bottom") +
  theme(legend.title = element_blank())
## Warning: Removed 5 rows containing missing values or values outside the scale range
## (`geom_line()`).
## Warning: Removed 47 rows containing missing values or values outside the scale range
## (`geom_line()`).

V. Conclusions and Recommendations

  1. While sales_unitboxes Granger-cause inflation_rate_diff, the reverse is not true. This suggests that past values of sales_unitboxes provide significant information for predicting changes in inflation rate.

  2. The rejection of the null hypothesis in the second Granger-causality test indicates that sales unit boxes have a predictive influence on inflation rates after differencing. This might suggest that changes in sales activity could be an indicator of inflationary trends.

  3. Since the first Granger-causality test fails to reject the null hypothesis , inflation (after differencing) does not appear to Granger-cause sales unit boxes. This could imply that fluctuations in inflation may not be directly influencing sales activity

  4. The covariance matrix of residuals shows a weak negative correlation (-0.2917) between sales_unitboxes and inflation_rate_diff. This suggests that, while the residuals of both variables are interconnected, they do not exhibit a strong linear relationship, implying that other factors may influence the dynamics between sales and inflation beyond what is captured by their past values.

  5. The VAR forecast begins in late 2018, just after a moderate upward trend in real sales. The forecast shows a relatively stable prediction for future sales, with values slightly above the final real sales point. This indicates that the model expects a stabilization of sales based on recent patterns, projecting moderate growth or consistency in the near future.

  6. The forecasted section of the graph maintains the clear seasonal patterns seen in historical data, which suggests that these patterns will persist. Coca-Cola can use this information to prepare for peak demand periods, ensuring inventory and marketing efforts are aligned with these predictable sales surges.

Recommendations

  1. Adjust prices for Coca-Cola products based on the weather. For example, offer discounts or promotions on hotter days to encourage more sales. In colder months, you might focus on bundling with other products.

  2. Implement a pricing strategy that adjusts to inflationary pressures. For example, offering the product in smaller containers, might make it easier for customers to buy it, mainly in countries like Mexico, where the income is low in many families.

  3. We could also consider using machine learning models like neural networks, which can handle time series data and complex non-linear relationships. By feeding these models with relevant time series features, we can improve the accuracy of the forecasts we made.

LS0tCnRpdGxlOiAiRXZpZGVuY2lhMiIKYXV0aG9yOiAiQWRyaWFuIE1vcmFsZXMgUXVpcm9nYSwgUGFibG8gU2FuY2hvIEdvbnrDoWxleiwgTWlndWVsIEFuZ2VsIExvcGV6LCBNYW51ZWwgUmFtaXJleiIKZGF0ZTogIjIwMjQtMDktMDgiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKLS0tCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGZvcmVjYXN0KQpsaWJyYXJ5KHRzZXJpZXMpCmxpYnJhcnkoem9vKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKbGlicmFyeShUU0EpCmxpYnJhcnkoeHRzKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeSh2YXJzKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShjYXIpCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KGdyaWRFeHRyYSkKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KY29jYWNvbGEgIDwtIHJlYWRfZXhjZWwoIi9Vc2Vycy9wYWJsb3NhbmNoby9EZXNrdG9wL2NvY2Fjb2xhc2FsZXMueGxzeCIpCmBgYAoKIyBJLiBJbnRyb2R1Y3Rpb24KVGltZSBzZXJpZXMgYW5hbHlzaXMgaXMgYSBzdGF0aXN0aWNhbCB0ZWNobmlxdWUgdGhhdCBpbnZvbHZlcyBhbmFseXppbmcgZGF0YSBwb2ludHMgY29sbGVjdGVkIG9yIHJlY29yZGVkIGF0IHNwZWNpZmljIHRpbWUgaW50ZXJ2YWxzLiBUaGlzIHR5cGUgb2YgYW5hbHlzaXMgaXMgdXNlZCB0byBpZGVudGlmeSBwYXR0ZXJucywgdHJlbmRzLCBjeWNsZXMsIGFuZCBzZWFzb25hbCB2YXJpYXRpb25zIGluIHRoZSBkYXRhIG92ZXIgdGltZS4gVGhlIHByaW1hcnkgZ29hbCBvZiB0aW1lIHNlcmllcyBhbmFseXNpcyBpcyB0byBmb3JlY2FzdCBmdXR1cmUgdmFsdWVzIGJhc2VkIG9uIGhpc3RvcmljYWwgZGF0YSwgbWFraW5nIGl0IHBhcnRpY3VsYXJseSB1c2VmdWwgaW4gZmllbGRzIHN1Y2ggYXMgZmluYW5jZSwgZWNvbm9taWNzLCB3ZWF0aGVyIGZvcmVjYXN0aW5nLCBhbmQgbWFueSBvdGhlciBhcmVhcyB3aGVyZSBkYXRhIGV2b2x2ZXMgb3ZlciB0aW1lIChDaGF0ZmllbGQsIDIwMDMpLgoKU3RhdGlvbmFyaXR5IGlzIGEgZnVuZGFtZW50YWwgY29uY2VwdCBpbiB0aW1lIHNlcmllcyBhbmFseXNpcywgd2hpY2ggaW1wbGllcyB0aGF0IHRoZSBzdGF0aXN0aWNhbCBwcm9wZXJ0aWVzIG9mIHRoZSBzZXJpZXMsIHN1Y2ggYXMgbWVhbiBhbmQgdmFyaWFuY2UsIHJlbWFpbiBjb25zdGFudCBvdmVyIHRpbWUuIFN0YXRpb25hcnkgdGltZSBzZXJpZXMgYXJlIGVhc2llciB0byBtb2RlbCBhbmQgcHJlZGljdCBiZWNhdXNlIHRoZWlyIGJlaGF2aW9yIGlzIGNvbnNpc3RlbnQgb3ZlciB0aW1lLiBOb24tc3RhdGlvbmFyeSBkYXRhLCBvbiB0aGUgb3RoZXIgaGFuZCwgbWF5IGV4aGliaXQgdHJlbmRzLCBzZWFzb25hbGl0eSwgb3IgdmFyeWluZyB2YXJpYW5jZSwgY29tcGxpY2F0aW5nIHRoZSBhbmFseXNpcyBhbmQgcG90ZW50aWFsbHkgbGVhZGluZyB0byBtaXNsZWFkaW5nIHJlc3VsdHMuIEVuc3VyaW5nIHN0YXRpb25hcml0eSBvZnRlbiBpbnZvbHZlcyB0cmFuc2Zvcm1pbmcgb3IgZGlmZmVyZW5jaW5nIHRoZSBkYXRhIHRvIHJlbW92ZSB0cmVuZHMgYW5kIHNlYXNvbmFsaXR5LCBhbGxvd2luZyBmb3IgbW9yZSBhY2N1cmF0ZSBtb2RlbGluZyBhbmQgZm9yZWNhc3RpbmcgKEJveCBldCBhbC4sIDIwMTUpLgoKVGltZSBzZXJpZXMgYW5hbHlzaXMgaXMgY3J1Y2lhbCBmb3IgYnVzaW5lc3MgaW50ZWxsaWdlbmNlIGJlY2F1c2UgaXQgZW5hYmxlcyBvcmdhbml6YXRpb25zIHRvIG1ha2UgZGF0YS1kcml2ZW4gZGVjaXNpb25zIGJ5IGZvcmVjYXN0aW5nIGZ1dHVyZSB0cmVuZHMsIGRlbWFuZCwgb3IgcGVyZm9ybWFuY2UgYmFzZWQgb24gaGlzdG9yaWNhbCBkYXRhLiBJdCBjYW4gYmUgYXBwbGllZCB0byB2YXJpb3VzIGJ1c2luZXNzIHByb2JsZW1zLCBzdWNoIGFzIHByZWRpY3Rpbmcgc2FsZXMsIHN0b2NrIHByaWNlcywgb3IgY3VzdG9tZXIgYmVoYXZpb3IuIFRoaXMgYW5hbHlzaXMgaGVscHMgYnVzaW5lc3NlcyBhbnRpY2lwYXRlIGNoYW5nZXMsIG1hbmFnZSBpbnZlbnRvcnksIG9wdGltaXplIG1hcmtldGluZyBzdHJhdGVnaWVzLCBhbmQgaW1wcm92ZSBvcGVyYXRpb25hbCBlZmZpY2llbmN5IGJ5IHVuZGVyc3RhbmRpbmcgcGF0dGVybnMgb3ZlciB0aW1lIGFuZCByZXNwb25kaW5nIHByb2FjdGl2ZWx5LgoKU291cmNlcwpDaGF0ZmllbGQsIEMuICgyMDAzKS4gVGhlIEFuYWx5c2lzIG9mIFRpbWUgU2VyaWVzOiBBbiBJbnRyb2R1Y3Rpb24uIENoYXBtYW4gYW5kIEhhbGwvQ1JDLgpCb3gsIEcuRS5QLiwgSmVua2lucywgRy5NLiwgUmVpbnNlbCwgRy5DLiwgJiBManVuZywgRy5NLiAoMjAxNSkuIFRpbWUgU2VyaWVzIEFuYWx5c2lzOiBGb3JlY2FzdGluZyBhbmQgQ29udHJvbC4gV2lsZXkuCgoKIyMgSUkuIERlc2NyaXB0aW9uIG9mIHRoZSBQcm9ibGVtIFNpdHVhdGlvbgpGb3IgdGhpcyBwcm9ibGVtIHNpdHVhdGlvbiwgb3VyIG9iamVjdGl2ZSBpcyB0byBidWlsZCBhIHByZWRpY3RpdmUgbW9kZWwgZm9yIEFyY2EgQ29udGluZW50YWwsIG1haW5seSBmb3IgdGhlaXIgYmlnZ2VzdCBhbmQgbW9zdCBwb3B1bGFyIGJyYW5jaCBpbiBMYXRpbiBBbWVyaWNhLCBDb2NhLUNvbGEuCldlIHdhbnQgdG8gY3JlYXRlIGEgbW9kZWwgdGhhdCBjYW4gc3VjY2Vzc2Z1bGx5IGZvcmVjYXN0IHRoZSBhIGRlc2lnbmF0ZWQgdGFyZ2V0IHZhcmlhYmxlLCB3aXRoIHdoaWNoIHdlIGNhbiBwcmVkaWN0IHRoZSBjb21wYW55J3MgYm94IHNhbGVzIChyZWZlcnJlZCB0byBhcyAiZ3JpZHMiKSBpbiB0aGUgbWV0cm9wb2xpdGFuIGFyZWEgb2YgR3VhZGFsYWphcmEsIEphbGlzY28uCgojIElJSS4gRGF0YSBhbmQgTWV0aG9kb2xvZ3kKCiMjIyBGb3JtYXQgdHJhbnNmb3JtYXRpb24KYGBge3J9CiMgRGlzcGxheSB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0CnN0cihjb2NhY29sYSkKYGBgCgpgYGB7cn0KIyBDb252ZXJ0ICdob2xpZGF5X21vbnRoJyB0byBhIGZhY3Rvcgpjb2NhY29sYSRob2xpZGF5X21vbnRoIDwtIGFzLmZhY3Rvcihjb2NhY29sYSRob2xpZGF5X21vbnRoKQpgYGAKCmBgYHtyfQojIENvbnZlcnQgJ3RwZXJpb2QnIHRvIGEgRGF0ZSBvYmplY3QgaWYgaXQgaXNuJ3QgYWxyZWFkeSBvbmUKY29jYWNvbGEkdHBlcmlvZCA8LSBhcy5EYXRlKGNvY2Fjb2xhJHRwZXJpb2QsIGZvcm1hdCA9ICIlZC0lbS0lWSIpCgojIEV4dHJhY3QgdGhlIGRheSAod2hpY2ggcmVwcmVzZW50cyB0aGUgeWVhcikgYW5kIHRoZSBtb250aApkYXlfbW9udGggPC0gZm9ybWF0KGNvY2Fjb2xhJHRwZXJpb2QsICIlZC0lbSIpCgojIENvbnZlcnQgdGhlIGRheSB0byBhIHByb3BlciB5ZWFyIChlLmcuLCAwMDE1IHRvIDIwMTUpIGFuZCBjcmVhdGUgdGhlIGNvcnJlY3QgZGF0ZQpjb3JyZWN0ZWRfeWVhciA8LSBhcy5udW1lcmljKHN1YnN0cihkYXlfbW9udGgsIDEsIDIpKSArIDIwMDAKY29ycmVjdGVkX21vbnRoX2RheSA8LSBwYXN0ZTAoY29ycmVjdGVkX3llYXIsICItIiwgc3Vic3RyKGRheV9tb250aCwgNCwgNSkpCgojIENvbnZlcnQgYmFjayB0byBEYXRlIG9iamVjdApjb2NhY29sYSR0cGVyaW9kIDwtIGFzLkRhdGUocGFzdGUwKGNvcnJlY3RlZF9tb250aF9kYXksICItMDEiKSwgZm9ybWF0ID0gIiVZLSVtLSVkIikKCiMgQ2hlY2sgdGhlIHJlc3VsdHMKaGVhZChjb2NhY29sYSR0cGVyaW9kKQpgYGAKCmBgYHtyfQojIHN0cnVjdHVyZSBhZnRlciBjb252ZXJzaW9ucwpzdHIoY29jYWNvbGEpCmBgYAoKIyMgRGVwZW5kZW50IHZhcmlhYmxlIGFuYWx5c2lzCgpgYGB7cn0KZ2dwbG90KGNvY2Fjb2xhLCBhZXMoeD10cGVyaW9kLCB5PXNhbGVzX3VuaXRib3hlcykpICsgCiAgZ2VvbV9saW5lKCkgKyAKICBsYWJzKHRpdGxlPSdCb3ggU2FsZXMgT3ZlciBUaW1lJywgeD0nVGltZScsIHk9J3NhbGVzX3VuaXRib3hlcycpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0PTEpKQpgYGAKCmBgYHtyfQpjb2NhY29sYSRNb3ZpbmdfQXZnIDwtIHJvbGxtZWFuKGNvY2Fjb2xhJHNhbGVzX3VuaXRib3hlcywgMTIsIGZpbGwgPSBOQSkKYGBgCgpgYGB7cn0KIyBQbG90IG1vdmluZyBhdmVyYWdlCmdncGxvdChjb2NhY29sYSwgYWVzKHg9dHBlcmlvZCkpICsgCiAgZ2VvbV9saW5lKGFlcyh5PXNhbGVzX3VuaXRib3hlcywgY29sb3VyPSJPcmlnaW5hbCIpKSArIAogIGdlb21fbGluZShhZXMoeT1Nb3ZpbmdfQXZnLCBjb2xvdXI9IjQtUGVyaW9kcyBNb3ZpbmcgQXZlcmFnZSIpKSArCiAgbGFicyh0aXRsZT0nTW92aW5nIEF2ZXJhZ2UgUGxvdCcsIHg9J0RhdGUnLCB5PSdTYWxlcyAoVW5pdCBCb3hlcyknKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCgiIiwgYnJlYWtzID0gYygiT3JpZ2luYWwiLCAiMTItUGVyaW9kcyBNb3ZpbmcgQXZlcmFnZSIpLCB2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpKQpgYGAKCmBgYHtyfQpjb2NhY29sYV90cyA8LSB0cyhjb2NhY29sYSRzYWxlc191bml0Ym94ZXMsIGZyZXF1ZW5jeT0xMiwgc3RhcnQ9YygyMDE1LCAxKSkKZGVjb21wb3NlZCA8LSBkZWNvbXBvc2UoY29jYWNvbGFfdHMsIHR5cGU9ImFkZGl0aXZlIikKCnBsb3QoZGVjb21wb3NlZCkKYGBgCgpgYGB7cn0KYWRmX3Rlc3QgPC0gYWRmLnRlc3QoY29jYWNvbGEkc2FsZXNfdW5pdGJveGVzLCBhbHRlcm5hdGl2ZT0ic3RhdGlvbmFyeSIpCgpwcmludChwYXN0ZSgiQURGIFN0YXRpc3RpYzogIiwgYWRmX3Rlc3Qkc3RhdGlzdGljKSkKcHJpbnQocGFzdGUoInAtdmFsdWU6ICIsIGFkZl90ZXN0JHAudmFsdWUpKQpgYGAKCmBgYHtyfQpzYWxlc190cyA8LSB0cyhjb2NhY29sYSRzYWxlc191bml0Ym94ZXMsIGZyZXF1ZW5jeT0xMikgCgojIFBsb3QgdGhlIGF1dG9jb3JyZWxhdGlvbiBmdW5jdGlvbiAoQUNGKQphY2Yoc2FsZXNfdHMsIG1haW49IkF1dG9jb3JyZWxhdGlvbiBvZiBTYWxlcyAoVW5pdCBCb3hlcykiKQoKIyBQbG90IHRoZSBwYXJ0aWFsIGF1dG9jb3JyZWxhdGlvbiBmdW5jdGlvbiAoUEFDRikKcGFjZihzYWxlc190cywgbWFpbj0iUGFydGlhbCBBdXRvY29ycmVsYXRpb24gb2YgU2FsZXMgKFVuaXQgQm94ZXMpIikKYGBgClRoZSBkZXBlbmRlbnQgdmFyaWFibGUgc2FsZXNfdW5pdGJveGVzIGhhcyBzaG93biB0byBoYXZlIGFuIHVwd2FyZCB0cmVuZCB0aGF0IGdlbmVyYWxseSBpbmNyZWFzZXMgb3ZlciB0aW1lLCBhcyB3ZWxsIGFzIGNsZWFyIHNlYXNvbmFsaXR5LiBJdCBpcyBhbHNvIHN0YXRpb25hcnksIHNob3dpbmcgYSBwLXZhbHVlIG9mIDAuMDEgYW5kIGF1dG9jb3JyZWxhdGlvbiBpbiB0aGUgQUNGIHBsb3QuCgoKIyBJVi4gVGltZSBTZXJpZXMgUmVncmVzc2lvbiBBbmFseXNpcwoKCiMjIE1vZGVsIENvbXBhcmlzb24KYGBge3J9CiMgQVJNQQpwX3ZhbHVlcyA8LSAwOjUgIApxX3ZhbHVlcyA8LSAwOjUgCgpiZXN0X2FpY19hcm1hIDwtIEluZgpiZXN0X29yZGVyX2FybWEgPC0gYygwLCAwKQpiZXN0X21vZGVsX2FybWEgPC0gTlVMTAoKZm9yIChwIGluIHBfdmFsdWVzKSB7CiAgZm9yIChxIGluIHFfdmFsdWVzKSB7CiAgICBtb2RlbCA8LSB0cnlDYXRjaCh7CiAgICAgIGFyaW1hKHNhbGVzX3RzLCBvcmRlciA9IGMocCwgMCwgcSkpICAjIE1vZGVsbyBBUk1BIChzaW4gZGlmZXJlbmNpYWNpw7NuLCBkID0gMCkKICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkgIAogICAgCiAgICBpZiAoIWlzLm51bGwobW9kZWwpKSB7CiAgICAgIGN1cnJlbnRfYWljIDwtIEFJQyhtb2RlbCkgIAogICAgICAKICAgICAgaWYgKGN1cnJlbnRfYWljIDwgYmVzdF9haWNfYXJtYSkgewogICAgICAgIGJlc3RfYWljX2FybWEgPC0gY3VycmVudF9haWMKICAgICAgICBiZXN0X29yZGVyX2FybWEgPC0gYyhwLCBxKQogICAgICAgIGJlc3RfbW9kZWxfYXJtYSA8LSBtb2RlbAogICAgICB9CiAgICB9CiAgfQp9CgojIEFSSU1BCnBfdmFsdWVzIDwtIDA6NSAgCmRfdmFsdWVzIDwtIDE6MiAgCnFfdmFsdWVzIDwtIDA6NSAKCmJlc3RfYWljX2FyaW1hIDwtIEluZgpiZXN0X29yZGVyX2FyaW1hIDwtIGMoMCwgMSwgMCkKYmVzdF9tb2RlbF9hcmltYSA8LSBOVUxMCgpmb3IgKHAgaW4gcF92YWx1ZXMpIHsKICBmb3IgKGQgaW4gZF92YWx1ZXMpIHsKICAgIGZvciAocSBpbiBxX3ZhbHVlcykgewogICAgICBtb2RlbCA8LSB0cnlDYXRjaCh7CiAgICAgICAgYXJpbWEoc2FsZXNfdHMsIG9yZGVyID0gYyhwLCBkLCBxKSkgICMgTW9kZWxvIEFSSU1BCiAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkgIAogICAgCiAgICAgIGlmICghaXMubnVsbChtb2RlbCkpIHsKICAgICAgICBjdXJyZW50X2FpYyA8LSBBSUMobW9kZWwpICAKICAgICAgICAKICAgICAgICBpZiAoY3VycmVudF9haWMgPCBiZXN0X2FpY19hcmltYSkgewogICAgICAgICAgYmVzdF9haWNfYXJpbWEgPC0gY3VycmVudF9haWMKICAgICAgICAgIGJlc3Rfb3JkZXJfYXJpbWEgPC0gYyhwLCBkLCBxKQogICAgICAgICAgYmVzdF9tb2RlbF9hcmltYSA8LSBtb2RlbAogICAgICAgIH0KICAgICAgfQogICAgfQogIH0KfQoKIyBBUiAoQXV0b3JlZ3Jlc3NpdmUpCnBfdmFsdWVzIDwtIDA6NSAgCgpiZXN0X2FpY19hciA8LSBJbmYKYmVzdF9vcmRlcl9hciA8LSBjKDAsIDApCmJlc3RfbW9kZWxfYXIgPC0gTlVMTAoKZm9yIChwIGluIHBfdmFsdWVzKSB7CiAgbW9kZWwgPC0gdHJ5Q2F0Y2goewogICAgYXJpbWEoc2FsZXNfdHMsIG9yZGVyID0gYyhwLCAwLCAwKSkgICMgTW9kZWxvIEFSIChzaW4gY29tcG9uZW50ZSBNQSwgcSA9IDApCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKSAgCiAgICAKICBpZiAoIWlzLm51bGwobW9kZWwpKSB7CiAgICBjdXJyZW50X2FpYyA8LSBBSUMobW9kZWwpICAKICAgIAogICAgaWYgKGN1cnJlbnRfYWljIDwgYmVzdF9haWNfYXIpIHsKICAgICAgYmVzdF9haWNfYXIgPC0gY3VycmVudF9haWMKICAgICAgYmVzdF9vcmRlcl9hciA8LSBjKHAsIDApCiAgICAgIGJlc3RfbW9kZWxfYXIgPC0gbW9kZWwKICAgIH0KICB9Cn0KCiMgTUEgKE1vdmluZyBBdmVyYWdlKQpxX3ZhbHVlcyA8LSAwOjUgIAoKYmVzdF9haWNfbWEgPC0gSW5mCmJlc3Rfb3JkZXJfbWEgPC0gYygwLCAwKQpiZXN0X21vZGVsX21hIDwtIE5VTEwKCmZvciAocSBpbiBxX3ZhbHVlcykgewogIG1vZGVsIDwtIHRyeUNhdGNoKHsKICAgIGFyaW1hKHNhbGVzX3RzLCBvcmRlciA9IGMoMCwgMCwgcSkpICAjIE1vZGVsbyBNQSAoc2luIGNvbXBvbmVudGUgQVIsIHAgPSAwKQogIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgTlVMTCkgIAogICAgCiAgaWYgKCFpcy5udWxsKG1vZGVsKSkgewogICAgY3VycmVudF9haWMgPC0gQUlDKG1vZGVsKSAgCiAgICAKICAgIGlmIChjdXJyZW50X2FpYyA8IGJlc3RfYWljX21hKSB7CiAgICAgIGJlc3RfYWljX21hIDwtIGN1cnJlbnRfYWljCiAgICAgIGJlc3Rfb3JkZXJfbWEgPC0gYygwLCBxKQogICAgICBiZXN0X21vZGVsX21hIDwtIG1vZGVsCiAgICB9CiAgfQp9CgojIENvbXBhcmFyIGxvcyBtb2RlbG9zIGNvbiBiYXNlIGVuIGVsIEFJQwptb2RlbG9zIDwtIGMoIkFSTUEiLCAiQVJJTUEiLCAiQVIiLCAiTUEiKQptZWpvcmVzX2FpYyA8LSBjKGJlc3RfYWljX2FybWEsIGJlc3RfYWljX2FyaW1hLCBiZXN0X2FpY19hciwgYmVzdF9haWNfbWEpCgptZWpvcl9tb2RlbG8gPC0gbW9kZWxvc1t3aGljaC5taW4obWVqb3Jlc19haWMpXQptZWpvcl9haWMgPC0gbWluKG1lam9yZXNfYWljKQoKcHJpbnQocGFzdGUoIkVsIG1lam9yIG1vZGVsbyBlczoiLCBtZWpvcl9tb2RlbG8pKQpwcmludChwYXN0ZSgiRWwgbWVqb3IgQUlDIGVzOiIsIG1lam9yX2FpYykpCgojIE1vc3RyYXIgZWwgcmVzdW1lbiBkZWwgbWVqb3IgbW9kZWxvCmlmIChtZWpvcl9tb2RlbG8gPT0gIkFSTUEiKSB7CiAgc3VtbWFyeShiZXN0X21vZGVsX2FybWEpCn0gZWxzZSBpZiAobWVqb3JfbW9kZWxvID09ICJBUklNQSIpIHsKICBzdW1tYXJ5KGJlc3RfbW9kZWxfYXJpbWEpCn0gZWxzZSBpZiAobWVqb3JfbW9kZWxvID09ICJBUiIpIHsKICBzdW1tYXJ5KGJlc3RfbW9kZWxfYXIpCn0gZWxzZSBpZiAobWVqb3JfbW9kZWxvID09ICJNQSIpIHsKICBzdW1tYXJ5KGJlc3RfbW9kZWxfbWEpCn0KCmBgYAoKIyMgRm9yZWNhc3QgdXNpbmcgQVJJTUEKYGBge3J9CiMgRGVmaW5pciBsb3MgdmFsb3JlcyBkZSBwLCBkIHkgcQpwX3ZhbHVlcyA8LSAwOjUgIApkX3ZhbHVlcyA8LSAxOjIgIApxX3ZhbHVlcyA8LSAwOjUgCgojIEluaWNpYWxpemFyIGVsIG1lam9yIEFJQywgZWwgbWVqb3Igb3JkZW4geSBlbCBtZWpvciBtb2RlbG8KYmVzdF9haWNfYXJpbWEgPC0gSW5mCmJlc3Rfb3JkZXJfYXJpbWEgPC0gYygwLCAxLCAwKQpiZXN0X21vZGVsX2FyaW1hIDwtIE5VTEwKCiMgSXRlcmFyIHNvYnJlIGxvcyBwb3NpYmxlcyB2YWxvcmVzIGRlIHAsIGQgeSBxCmZvciAocCBpbiBwX3ZhbHVlcykgewogIGZvciAoZCBpbiBkX3ZhbHVlcykgewogICAgZm9yIChxIGluIHFfdmFsdWVzKSB7CiAgICAgIG1vZGVsIDwtIHRyeUNhdGNoKHsKICAgICAgICBBcmltYShzYWxlc190cywgb3JkZXIgPSBjKHAsIGQsIHEpKSAgIyBNb2RlbG8gQVJJTUEKICAgICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSBOVUxMKSAgCiAgICAKICAgICAgaWYgKCFpcy5udWxsKG1vZGVsKSkgewogICAgICAgIGN1cnJlbnRfYWljIDwtIEFJQyhtb2RlbCkgIAogICAgICAgIAogICAgICAgIGlmIChjdXJyZW50X2FpYyA8IGJlc3RfYWljX2FyaW1hKSB7CiAgICAgICAgICBiZXN0X2FpY19hcmltYSA8LSBjdXJyZW50X2FpYwogICAgICAgICAgYmVzdF9vcmRlcl9hcmltYSA8LSBjKHAsIGQsIHEpCiAgICAgICAgICBiZXN0X21vZGVsX2FyaW1hIDwtIG1vZGVsCiAgICAgICAgfQogICAgICB9CiAgICB9CiAgfQp9CgojIFZlcmlmaWNhciBzaSBzZSBlbmNvbnRyw7MgdW4gbW9kZWxvCmlmICghaXMubnVsbChiZXN0X21vZGVsX2FyaW1hKSkgewogIHByaW50KHBhc3RlKCJNZWpvciBtb2RlbG8gQVJJTUE6IiwgcGFzdGUoYmVzdF9vcmRlcl9hcmltYSwgY29sbGFwc2UgPSAiLCIpKSkKICBwcmludChwYXN0ZSgiTWVqb3IgQUlDIEFSSU1BOiIsIGJlc3RfYWljX2FyaW1hKSkKICBzdW1tYXJ5KGJlc3RfbW9kZWxfYXJpbWEpCgogICMgR2VuZXJhciBwcm9uw7NzdGljbyBhIDUgcGVyw61vZG9zCiAgZm9yZWNhc3RfYXJpbWEgPC0gZm9yZWNhc3QoYmVzdF9tb2RlbF9hcmltYSwgaCA9IDUpCiAgcHJpbnQoZm9yZWNhc3RfYXJpbWEpCgogICMgR3JhZmljYXIgZWwgcHJvbsOzc3RpY28KICBhdXRvcGxvdChmb3JlY2FzdF9hcmltYSkgKwogICAgbGFicyh0aXRsZSA9ICJQcm9uw7NzdGljbyBhIDUgcGVyw61vZG9zIiwgeCA9ICJUaWVtcG8iLCB5ID0gIlZlbnRhcyAoQ2FqYXMgVW5pdGFyaWFzKSIpICsKICAgIHRoZW1lX21pbmltYWwoKQoKfSBlbHNlIHsKICBwcmludCgiTm8gc2UgZW5jb250csOzIHVuIG1vZGVsbyBBUklNQSB2w6FsaWRvLiIpCn0KCmBgYAoKIyBUaW1lIHNlcmllcyBtb2RlbCAyCgpJbnN0cnVjdGlvbnM6IEZyb20gdGhlIHRpbWUgc2VyaWVzIGRhdGFzZXQsIHNlbGVjdCAyIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyB0aGF0IG1pZ2h0IGFmZmVjdCB0aGUgc2FsZXMgdW5pdGJveGVzLgoKYGBge3J9CiMgQ2FsY3VsYXIgbGEgY29ycmVsYWNpw7NuIGVudHJlIHNhbGVzX3VuaXRib3hlcyB5IG90cmFzIHZhcmlhYmxlcwpjb3JyZWxhdGlvbl9tYXRyaXggPC0gY29yKGNvY2Fjb2xhWywgYygic2FsZXNfdW5pdGJveGVzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb25zdW1lcl9zZW50aW1lbnQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdkcF9wZXJjYXBpdGEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNQSSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaW5mbGF0aW9uX3JhdGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuZW1wX3JhdGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImV4Y2hhbmdlX3JhdGUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm1heF90ZW1wZXJhdHVyZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTW92aW5nX0F2ZyIpXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlID0gImNvbXBsZXRlLm9icyIpCgojIFZlciBsYSBtYXRyaXogZGUgY29ycmVsYWNpw7NuCnByaW50KGNvcnJlbGF0aW9uX21hdHJpeCkKCiMgRmlsdHJhciBsYXMgY29ycmVsYWNpb25lcyBjb24gc2FsZXNfdW5pdGJveGVzCnNhbGVzX2NvcnJlbGF0aW9ucyA8LSBjb3JyZWxhdGlvbl9tYXRyaXhbMSwgLTFdICAjIEV4Y2x1aW1vcyBsYSBwcmltZXJhIGZpbGEgcXVlIGVzIGxhIGRlIHNhbGVzX3VuaXRib3hlcwoKIyBWZXIgbGFzIGNvcnJlbGFjaW9uZXMgb3JkZW5hZGFzCnByaW50KHNhbGVzX2NvcnJlbGF0aW9uc1tvcmRlcihhYnMoc2FsZXNfY29ycmVsYXRpb25zKSwgZGVjcmVhc2luZyA9IFRSVUUpXSkKCmBgYApgYGB7cn0KIyBEaWZlcmVuY2lhciBsYXMgc2VyaWVzIG1heF90ZW1wZXJhdHVyZSBlIGluZmxhdGlvbl9yYXRlIHBhcmEgaGFjZXJsYXMgZXN0YWNpb25hcmlhcwptYXhfdGVtcGVyYXR1cmVfZGlmZiA8LSBkaWZmKGNvY2Fjb2xhJG1heF90ZW1wZXJhdHVyZSkKaW5mbGF0aW9uX3JhdGVfZGlmZiA8LSBkaWZmKGNvY2Fjb2xhJGluZmxhdGlvbl9yYXRlKQoKIyBFc2NhbGFyIGxhcyB2YXJpYWJsZXMgZXjDs2dlbmFzIChtYXhfdGVtcGVyYXR1cmVfZGlmZiBlIGluZmxhdGlvbl9yYXRlX2RpZmYpCiMgQWxpbmVhbW9zIGxhcyB2YXJpYWJsZXMgZWxpbWluYW5kbyBsYSBwcmltZXJhIG9ic2VydmFjacOzbiBkZSBzYWxlc191bml0Ym94ZXMKZXhwbGFuYXRvcnlfdmFyaWFibGVzIDwtIHNjYWxlKGNiaW5kKG1heF90ZW1wZXJhdHVyZV9kaWZmLCBpbmZsYXRpb25fcmF0ZV9kaWZmKSkKCiMgVmVyaWZpY2FyIHF1ZSBubyBoYXlhIE5BIGVuIGxhcyB2YXJpYWJsZXMgZXjDs2dlbmFzIGVzY2FsYWRhcyB5IGVuIHNhbGVzX3VuaXRib3hlcwpzYWxlc190cyA8LSB0cyhuYS5vbWl0KGNvY2Fjb2xhJHNhbGVzX3VuaXRib3hlcylbLTFdLCBmcmVxdWVuY3kgPSAxMiwgc3RhcnQgPSBjKDIwMTUsIDEpKSAgIyBBanVzdGFtb3Mgc2FsZXNfdW5pdGJveGVzIHBhcmEgcXVlIGNvaW5jaWRhIGNvbiBsYXMgdmFyaWFibGVzIGRpZmVyZW5jaWFkYXMKCiMgVXRpbGl6YXIgZWwgbWVqb3IgbW9kZWxvIEFSSU1BIGVuY29udHJhZG8gcHJldmlhbWVudGUgcGFyYSBlbCBhanVzdGUgZGVsIEFSSU1BWApiZXN0X21vZGVsX2FyaW1heCA8LSBBcmltYShzYWxlc190cywgb3JkZXIgPSBiZXN0X29yZGVyX2FyaW1hLCB4cmVnID0gZXhwbGFuYXRvcnlfdmFyaWFibGVzKQoKIyBNb3N0cmFyIGVsIHJlc3VtZW4gZGVsIG1vZGVsbyBBUklNQVggYWp1c3RhZG8Kc3VtbWFyeShiZXN0X21vZGVsX2FyaW1heCkKCiMgUmVhbGl6YXIgdW4gcHJvbsOzc3RpY28gcGFyYSBsb3MgcHLDs3hpbW9zIDUgcGVyw61vZG9zCiMgRGlmZXJlbmNpYXIgbG9zIHByw7N4aW1vcyB2YWxvcmVzIGRlIGluZmxhdGlvbl9yYXRlIHkgbWF4X3RlbXBlcmF0dXJlIHBhcmEgZWwgcHJvbsOzc3RpY28KbWF4X3RlbXBlcmF0dXJlX2ZvcmVjYXN0X2RpZmYgPC0gZGlmZihjb2NhY29sYSRtYXhfdGVtcGVyYXR1cmVbMTo2XSkgICMgRGlmZXJlbmNpYXIgbG9zIHByw7N4aW1vcyB2YWxvcmVzIGRlIG1heF90ZW1wZXJhdHVyZQppbmZsYXRpb25fcmF0ZV9mb3JlY2FzdF9kaWZmIDwtIGRpZmYoY29jYWNvbGEkaW5mbGF0aW9uX3JhdGVbMTo2XSkgICMgRGlmZXJlbmNpYXIgbG9zIHByw7N4aW1vcyB2YWxvcmVzIGRlIGluZmxhdGlvbl9yYXRlCgojIEVzY2FsYXIgbGFzIHZhcmlhYmxlcyBwcm95ZWN0YWRhcwpleHBsYW5hdG9yeV9mb3JlY2FzdCA8LSBzY2FsZShjYmluZChtYXhfdGVtcGVyYXR1cmVfZm9yZWNhc3RfZGlmZiwgaW5mbGF0aW9uX3JhdGVfZm9yZWNhc3RfZGlmZikpCgojIEdlbmVyYXIgZWwgcHJvbsOzc3RpY28gdXNhbmRvIGVsIG1vZGVsbyBBUklNQVggY29uIGxhcyB2YXJpYWJsZXMgZXjDs2dlbmFzIHByb3llY3RhZGFzIHkgZXNjYWxhZGFzCmZvcmVjYXN0X2FyaW1heCA8LSBmb3JlY2FzdChiZXN0X21vZGVsX2FyaW1heCwgeHJlZyA9IGV4cGxhbmF0b3J5X2ZvcmVjYXN0LCBoID0gNSkKCiMgQ3JlYXIgdW5hIHRhYmxhIGNvbiBsb3MgcmVzdWx0YWRvcyBkZWwgcHJvbsOzc3RpY28KZm9yZWNhc3RfdGFibGUgPC0gZGF0YS5mcmFtZSgKICBQZXJpb2QgPSB0aW1lKGZvcmVjYXN0X2FyaW1heCRtZWFuKSwKICBGb3JlY2FzdCA9IGFzLm51bWVyaWMoZm9yZWNhc3RfYXJpbWF4JG1lYW4pLAogIExvd2VyXzk1ID0gYXMubnVtZXJpYyhmb3JlY2FzdF9hcmltYXgkbG93ZXJbLDJdKSwKICBVcHBlcl85NSA9IGFzLm51bWVyaWMoZm9yZWNhc3RfYXJpbWF4JHVwcGVyWywyXSkKKQoKIyBJbXByaW1pciBsYSB0YWJsYSBkZSBwcm9uw7NzdGljbwpwcmludChmb3JlY2FzdF90YWJsZSkKCiMgR3JhZmljYXIgZWwgcHJvbsOzc3RpY28KYXV0b3Bsb3QoZm9yZWNhc3RfYXJpbWF4KSArCiAgbGFicyh0aXRsZSA9ICJQcm9uw7NzdGljbyBhIDUgcGVyw61vZG9zIGNvbiBBUklNQVggdXNhbmRvIHZhcmlhYmxlcyBlc2NhbGFkYXMgeSBkaWZlcmVuY2lhZGFzIiwKICAgICAgIHggPSAiVGllbXBvIiwgeSA9ICJWZW50YXMgKENhamFzIFVuaXRhcmlhcykiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpgYGAKCgojIyBEZXNjcmliZSB0aGUgaHlwb3RoZXRpY2FsIHJlbGF0aW9uc2hpcCAvIGltcGFjdCBiZXR3ZWVuIGVhY2ggc2VsZWN0ZWQgZXhwbGFuYXRvcnkgdmFyaWFibGUgYW5kIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUg4oCcc2FsZXMgdW5pdCBib3hlc+KAnS4KClRoZXJlIGlzIGhpZ2hseSBsaWtlbHkgYSBwb3NpdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiAibWF4X3RlbXBlcmF0dXJlIiBhbmQgc2FsZXMgdW5pdCBib3hlcyBvZiBDb2NhLUNvbGEuIEFzIHRoZSBtYXhpbXVtIHRlbXBlcmF0dXJlIGluY3JlYXNlcywgcGFydGljdWxhcmx5IGluIHdhcm1lciBtb250aHMgb3IgcmVnaW9ucywgdGhlIGRlbWFuZCBmb3IgY29sZCBiZXZlcmFnZXMgbGlrZSBDb2NhLUNvbGEgdGVuZHMgdG8gcmlzZS4gUGVvcGxlIGFyZSBtb3JlIGluY2xpbmVkIHRvIHB1cmNoYXNlIHJlZnJlc2hpbmcgZHJpbmtzIHdoZW4gdGhlIHdlYXRoZXIgaXMgaG90IHRvIHN0YXkgaHlkcmF0ZWQgYW5kIGNvb2wgZG93bi5PbiB0aGUgb3RoZXIgaGFuZCwgbG93ZXIgdGVtcGVyYXR1cmVzIG1heSByZWR1Y2UgZGVtYW5kIGFzIHBlb3BsZSBtYXkgcHJlZmVyIGhvdCBkcmlua3Mgb3Igb3RoZXIgYmV2ZXJhZ2VzIGJldHRlciBzdWl0ZWQgZm9yIGNvbGRlciB3ZWF0aGVyLgoKVGhlcmUgaXMgbGlrZWx5IGEgbmVnYXRpdmUgcmVsYXRpb25zaGlwIGJldHdlZW4gImluZmxhdGlvbl9yYXRlIiBhbmQgc2FsZXMgdW5pdCBib3hlcyBvZiBDb2NhLUNvbGEuIEFuIGluY3JlYXNlIGluIHRoZSBpbmZsYXRpb24gcmF0ZSBnZW5lcmFsbHkgbGVhZHMgdG8gYSByaXNlIGluIHRoZSBjb3N0IG9mIGdvb2RzIGFuZCBzZXJ2aWNlcywgd2hpY2ggY2FuIHJlc3VsdCBpbiBoaWdoZXIgcHJpY2VzIGZvciBDb2NhLUNvbGEuIEFzIHByaWNlcyBnbyB1cCwgY29uc3VtZXJzJyBwdXJjaGFzaW5nIHBvd2VyIGRlY2xpbmVzLCBwb3RlbnRpYWxseSByZWR1Y2luZyB0aGUgb3ZlcmFsbCBzYWxlcyB2b2x1bWUuCkR1cmluZyBwZXJpb2RzIG9mIGhpZ2ggaW5mbGF0aW9uLCBjb25zdW1lcnMgbWF5IHByaW9yaXRpemUgZXNzZW50aWFsIGl0ZW1zIG92ZXIgZGlzY3JldGlvbmFyeSBzcGVuZGluZy4gQ29jYS1Db2xhLCBiZWluZyBhIG5vbi1lc3NlbnRpYWwgaXRlbSwgbWlnaHQgZXhwZXJpZW5jZSByZWR1Y2VkIHNhbGVzIHZvbHVtZSBhcyBjb25zdW1lcnMgY3V0IGJhY2sgb24gc3VjaCBleHBlbmRpdHVyZXMuCgojIyBJbmNsdWRlIGEgdGltZSBzZXJpZXMgcGxvdCB0aGF0IGRpc3BsYXlzIHRoZSBzZWxlY3RlZCB2YXJpYWJsZXPigJkgcGVyZm9ybWFuY2Ugb3ZlciB0aGUgdGltZSBwZXJpb2QuCgpgYGB7cn0KCiMgQ3JlYXIgdW4gbnVldm8gZGF0YWZyYW1lIHF1ZSB0ZW5nYSB0b2RhcyBsYXMgc2VyaWVzIGFsaW5lYWRhcyBjb3JyZWN0YW1lbnRlCmFsaWduZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIHRwZXJpb2QgPSBjb2NhY29sYSR0cGVyaW9kWy0xXSwgICMgRWxpbWluYXIgbGEgcHJpbWVyYSBvYnNlcnZhY2nDs24gZGUgdHBlcmlvZAogIHNhbGVzX3VuaXRib3hlcyA9IGNvY2Fjb2xhJHNhbGVzX3VuaXRib3hlc1stMV0sICAjIEVsaW1pbmFyIGxhIHByaW1lcmEgb2JzZXJ2YWNpw7NuCiAgbWF4X3RlbXBlcmF0dXJlX2RpZmYgPSBtYXhfdGVtcGVyYXR1cmVfZGlmZiwgICMgWWEgZXN0w6EgYWxpbmVhZGEKICBpbmZsYXRpb25fcmF0ZV9kaWZmID0gaW5mbGF0aW9uX3JhdGVfZGlmZiAgIyBZYSBlc3TDoSBhbGluZWFkYQopCgojIEdyYWZpY2FyIGxhcyB2ZW50YXMgZW4gdW4gZWplLCB5IG1heF90ZW1wZXJhdHVyZV9kaWZmIGUgaW5mbGF0aW9uX3JhdGVfZGlmZiBlbiBlbCBlamUgc2VjdW5kYXJpbwpnZ3Bsb3QoKSArCiAgIyBHcmFmaWNhciBzYWxlc191bml0Ym94ZXMgZW4gZWwgZWplIHByaW1hcmlvCiAgZ2VvbV9saW5lKGRhdGEgPSBhbGlnbmVkX2RhdGEsIGFlcyh4ID0gdHBlcmlvZCwgeSA9IHNhbGVzX3VuaXRib3hlcywgY29sb3IgPSAiVmVudGFzIChDYWphcyBVbml0YXJpYXMpIiksIHNpemUgPSAxKSArCiAgCiAgIyBHcmFmaWNhciBtYXhfdGVtcGVyYXR1cmVfZGlmZiBlbiBlbCBlamUgc2VjdW5kYXJpbyAobXVsdGlwbGljYWRhIHBhcmEgYWp1c3RlIHZpc3VhbCkKICBnZW9tX2xpbmUoZGF0YSA9IGFsaWduZWRfZGF0YSwgYWVzKHggPSB0cGVyaW9kLCB5ID0gbWF4X3RlbXBlcmF0dXJlX2RpZmYgKiAyMDAwMDAsIGNvbG9yID0gIlRlbXBlcmF0dXJhIE3DoXhpbWEgKERpZmVyZW5jaWFkYSkiKSwgc2l6ZSA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICAKICAjIEdyYWZpY2FyIGluZmxhdGlvbl9yYXRlX2RpZmYgZW4gZWwgZWplIHNlY3VuZGFyaW8gKG11bHRpcGxpY2FkYSBwYXJhIGFqdXN0ZSB2aXN1YWwpCiAgZ2VvbV9saW5lKGRhdGEgPSBhbGlnbmVkX2RhdGEsIGFlcyh4ID0gdHBlcmlvZCwgeSA9IGluZmxhdGlvbl9yYXRlX2RpZmYgKiAyMDAwMDAwLCBjb2xvciA9ICJUYXNhIGRlIEluZmxhY2nDs24gKERpZmVyZW5jaWFkYSkiKSwgc2l6ZSA9IDEsIGxpbmV0eXBlID0gImRvdHRlZCIpICsKICAKICAjIENvbmZpZ3VyYWNpw7NuIGRlIGxvcyBlamVzIHkgc3VzIHJlc3BlY3RpdmFzIGVzY2FsYXMKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBuYW1lID0gIlZlbnRhcyAoQ2FqYXMgVW5pdGFyaWFzKSIsCiAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4gLiAvIDEwMDAwMCwgbmFtZSA9ICJUZW1wZXJhdHVyYSAowrBDKSB5IFRhc2EgZGUgSW5mbGFjacOzbiBEaWZlcmVuY2lhZGEgKCUpIikKICApICsKICAKICAjIEV0aXF1ZXRhcyB5IHTDrXR1bG8gZGVsIGdyw6FmaWNvCiAgbGFicyh0aXRsZSA9ICJWZW50YXMsIFRlbXBlcmF0dXJhIE3DoXhpbWEgeSBUYXNhIGRlIEluZmxhY2nDs24gRGlmZXJlbmNpYWRhIGNvbiBFamVzIFNlY3VuZGFyaW9zIiwKICAgICAgIHggPSAiVGllbXBvIiwgY29sb3IgPSAiIikgKwogIAogICMgVGVtYSBtaW5pbWFsaXN0YSB5IGFqdXN0ZSBkZSBsZXllbmRhCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmBgYAoKIyMgRG8gdGhlIHNlbGVjdGVkIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyBzaG93IHNlcmlhbCBhdXRvY29ycmVsYXRpb24gYW5kIGEgc3RhdGlvbmFyeSBwcm9jZXNzPyAKYGBge3J9CmFjZl9tYXhfdGVtcGVyYXR1cmVfZGlmZiA8LSBBY2YobWF4X3RlbXBlcmF0dXJlX2RpZmYsIG1haW4gPSAiQUNGIGRlIG1heF90ZW1wZXJhdHVyZSAoRGlmZXJlbmNpYWRhKSIpCgojIENhbGN1bGFyIGxhIEFDRiBwYXJhIGxhIHNlcmllIGRpZmVyZW5jaWFkYSBkZSBpbmZsYXRpb25fcmF0ZQphY2ZfaW5mbGF0aW9uX3JhdGVfZGlmZiA8LSBBY2YoaW5mbGF0aW9uX3JhdGVfZGlmZiwgbWFpbiA9ICJBQ0YgZGUgaW5mbGF0aW9uX3JhdGUgKERpZmVyZW5jaWFkYSkiKQoKYGBgCmBgYHtyfQojIFBydWViYSBBREYgcGFyYSBsYSBzZXJpZSBkaWZlcmVuY2lhZGEgZGUgbWF4X3RlbXBlcmF0dXJlCmFkZl90ZXN0X21heF90ZW1wX2RpZmYgPC0gYWRmLnRlc3QobWF4X3RlbXBlcmF0dXJlX2RpZmYsIGFsdGVybmF0aXZlID0gInN0YXRpb25hcnkiKQpwcmludChhZGZfdGVzdF9tYXhfdGVtcF9kaWZmKQoKIyBQcnVlYmEgQURGIHBhcmEgbGEgc2VyaWUgZGlmZXJlbmNpYWRhIGRlIGluZmxhdGlvbl9yYXRlCmFkZl90ZXN0X2luZmxhdGlvbl9kaWZmIDwtIGFkZi50ZXN0KGluZmxhdGlvbl9yYXRlX2RpZmYsIGFsdGVybmF0aXZlID0gInN0YXRpb25hcnkiKQpwcmludChhZGZfdGVzdF9pbmZsYXRpb25fZGlmZikKYGBgCgojIyMgRXhwbGFuYXRpb24gb2YgdGVzdCByZXN1bHRzCioqTWF4IFRlbXBlcmF0dXJlOioqClRoZSBBREYgdGVzdCBzdGF0aXN0aWMgb2YgLTQuMDMyMyBpcyBzaWduaWZpY2FudGx5IGxvd2VyIHRoYW4gdGhlIGNyaXRpY2FsIHZhbHVlcyBhdCBjb21tb24gY29uZmlkZW5jZSBsZXZlbHMgKGxpa2UgLTMuNDMgZm9yIDUlIHNpZ25pZmljYW5jZSBsZXZlbCkuIFRoZSBwLXZhbHVlIGlzIGxlc3MgdGhhbiAwLjA1LCBpbmRpY2F0aW5nIHRoYXQgd2UgY2FuIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGluIGZhdm9yIG9mIHRoZSBhbHRlcm5hdGl2ZSBoeXBvdGhlc2lzIHRoYXQgdGhlIHNlcmllcyBpcyBpbiBmYWN0LCBzdGF0aW9uYXJ5LgoKKipJbmZsYXRpb24gUmF0ZToqKgpUaGUgQURGIHRlc3Qgc3RhdGlzdGljIG9mIC0zLjg5NSBpcyBhbHNvIGJlbG93IHRoZSBjcml0aWNhbCB2YWx1ZXMgZm9yIHN0YXRpb25hcml0eSAoZS5nLiwgLTMuNDMgYXQgNSUgc2lnbmlmaWNhbmNlIGxldmVsKS5UaGUgcC12YWx1ZSAoMC4wMjIwNykgaXMgbGVzcyB0aGFuIDAuMDUsIGFsbG93aW5nIHVzIHRvIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIG5vbi1zdGF0aW9uYXJpdHksIHdoaWNoIGluIHR1cm4gdGVsbHMgdXMgdGhhdCB0aGlzIHNlcmllcyBpcyBhbHNvIHN0YXRpb25hcnkuCgoqKkFERiBDb25jbHVzaW9uOioqClRoZXNlIHRlc3RzIHN1Z2dlc3QgdGhhdCB0aGUgc3RhdGlzdGljYWwgcHJvcGVydGllcyBvZiBib3RoIHNlcmllcyByZW1haW4gY29uc3RhbnQgb3ZlciB0aW1lIHdoZW4gZGlmZmVyZW5jZWQsIG1ha2luZyB0aGVtIHN1aXRhYmxlIGZvciB0aW1lIHNlcmllcyBhbmFseXNpcyB0ZWNobmlxdWVzIHRoYXQgYXNzdW1lIHN0YXRpb25hcml0eS4KCgoqKkF1dG9jb3JyZWxhY2lvbiBhbmFseXNpcyBhZnRlciBkaWZmZXJlbnRpYXRpb246KioKQm90aCBzZXJpZXMgc2hvdyBhdXRvY29ycmVsYXRpb24gYXQgbGFnIDEuIFRoZSBzaWduaWZpY2FudCBzcGlrZSBhdCBsYWcgMSBpbmRpY2F0ZXMgdGhhdCB0aGUgZGlmZmVyZW5jZWQgZGF0YSBpcyBub3QgY29tcGxldGVseSBmcmVlIG9mIGF1dG9jb3JyZWxhdGlvbi4gSG93ZXZlciwgbW9zdCBvZiB0aGUgb3RoZXIgbGFncyBkbyBub3Qgc2hvdyBzaWduaWZpY2FudCBhdXRvY29ycmVsYXRpb24sIGltcGx5aW5nIHRoYXQgdGhlIGRpZmZlcmVuY2luZyBoYXMgcmVkdWNlZCBtdWNoIG9mIHRoZSBhdXRvY29ycmVsYXRpb24gaW4gdGhlIG9yaWdpbmFsIHNlcmllcy4gSW4gc3VtbWFyeSwgd2hpbGUgdGhlcmUgaXMgc29tZSByZW1haW5pbmcgYXV0b2NvcnJlbGF0aW9uLCBwYXJ0aWN1bGFybHkgYXQgbGFnIDEgZm9yIGJvdGggc2VyaWVzLCB0aGUgZGF0YSBkb2VzIG5vdCBleGhpYml0IHN0cm9uZyBhdXRvY29ycmVsYXRpb24gYXQgb3RoZXIgbGFncy4KCgojIyBCcmllZmx5IGludGVycHJldCB0aGUgZXN0aW1hdGVkIFZBUiByZWdyZXNzaW9uIHJlc3VsdHMuIFRoYXQgaXMsIGlzIHRoZXJlIGEgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGUocykgYW5kIHRoZSBkZXBlbmRlbnQgdmFyaWFibGU/CgpgYGB7cn0KIyBDcmVhciB1biBkYXRhZnJhbWUgY29uIGxhcyBzZXJpZXMgZGlmZXJlbmNpYWRhcyBhbGluZWFkYXMgKHNhbGVzX3VuaXRib3hlcyBlIGluZmxhdGlvbl9yYXRlX2RpZmYpCnZhcl9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgc2FsZXNfdW5pdGJveGVzID0gY29jYWNvbGEkc2FsZXNfdW5pdGJveGVzWy0xXSwgICMgQWxpbmVhciBlbGltaW5hbmRvIGxhIHByaW1lcmEgb2JzZXJ2YWNpw7NuCiAgaW5mbGF0aW9uX3JhdGVfZGlmZiA9IGluZmxhdGlvbl9yYXRlX2RpZmYgICMgU2VyaWUgZGlmZXJlbmNpYWRhIHlhIHByZXBhcmFkYQopCgojIFNlbGVjY2nDs24gZGVsIG7Dum1lcm8gw7NwdGltbyBkZSByZXphZ29zIGJhc2FkbyBlbiBBSUMgKGVxdWl2YWxlbnRlIGEgbWF4bGFncz00IHkgaWM9J2FpYycgZW4gUHl0aG9uKQpsYWdfc2VsZWN0aW9uIDwtIFZBUnNlbGVjdCh2YXJfZGF0YSwgbGFnLm1heCA9IDQsIHR5cGUgPSAiY29uc3QiKQpvcHRpbWFsX2xhZyA8LSBsYWdfc2VsZWN0aW9uJHNlbGVjdGlvblsiQUlDKG4pIl0gICMgRXh0cmFlbW9zIGVsIG7Dum1lcm8gw7NwdGltbyBkZSByZXphZ29zIHNlZ8O6biBlbCBjcml0ZXJpbyBBSUMKCiMgRXN0aW1hY2nDs24gZGVsIG1vZGVsbyBWQVIgY29uIGVsIG7Dum1lcm8gZGUgcmV6YWdvcyDDs3B0aW1vClZBUl9tb2RlbCA8LSBWQVIodmFyX2RhdGEsIHAgPSBvcHRpbWFsX2xhZywgdHlwZSA9ICJjb25zdCIpCgojIE1vc3RyYXIgZWwgcmVzdW1lbiBkZWwgbW9kZWxvIFZBUiBhanVzdGFkbwpzdW1tYXJ5KFZBUl9tb2RlbCkKCiMgUmVhbGl6YXIgZWwgcHJvbsOzc3RpY28gcGFyYSBsb3MgcHLDs3hpbW9zIDUgcGVyw61vZG9zCmZvcmVjYXN0X3N0ZXBzIDwtIDUKdmFyX2ZvcmVjYXN0IDwtIHByZWRpY3QoVkFSX21vZGVsLCBuLmFoZWFkID0gZm9yZWNhc3Rfc3RlcHMpCgojIENyZWFyIHVuIMOtbmRpY2UgcGFyYSBsb3MgZGF0b3MgcHJvbm9zdGljYWRvcwpmb3JlY2FzdF9pbmRleCA8LSBzZXEoYXMuRGF0ZSh0YWlsKGNvY2Fjb2xhJHRwZXJpb2QsIDEpKSwgYnkgPSAibW9udGgiLCBsZW5ndGgub3V0ID0gZm9yZWNhc3Rfc3RlcHMpCgojIENyZWFyIHVuIGRhdGFmcmFtZSBjb24gbG9zIHJlc3VsdGFkb3MgZGVsIHByb27Ds3N0aWNvCmZvcmVjYXN0X2RmIDwtIGRhdGEuZnJhbWUoCiAgUGVyaW9kID0gZm9yZWNhc3RfaW5kZXgsCiAgc2FsZXNfdW5pdGJveGVzX3ByZWQgPSB2YXJfZm9yZWNhc3QkZmNzdCRzYWxlc191bml0Ym94ZXNbLCAxXSwgICMgUHJlZGljY2nDs24gZGUgc2FsZXNfdW5pdGJveGVzCiAgaW5mbGF0aW9uX3JhdGVfcHJlZCA9IHZhcl9mb3JlY2FzdCRmY3N0JGluZmxhdGlvbl9yYXRlX2RpZmZbLCAxXSAgIyBQcmVkaWNjacOzbiBkZSBpbmZsYXRpb25fcmF0ZV9kaWZmCikKCiMgTW9zdHJhciBlbCBwcm9uw7NzdGljbwpwcmludChmb3JlY2FzdF9kZikKCmBgYAoKYGBge3J9CiMgUmVhbGl6YXIgbGEgcHJ1ZWJhIGRlIGNhdXNhbGlkYWQgZGUgR3JhbmdlciBwYXJhIHZlciBzaSBpbmZsYXRpb25fcmF0ZV9kaWZmIGNhdXNhIHNhbGVzX3VuaXRib3hlcwpncmFuZ2VyX3Rlc3RfMSA8LSBjYXVzYWxpdHkoVkFSX21vZGVsLCBjYXVzZSA9ICJpbmZsYXRpb25fcmF0ZV9kaWZmIikKcHJpbnQoZ3Jhbmdlcl90ZXN0XzEkR3JhbmdlcikKCiMgUmVhbGl6YXIgbGEgcHJ1ZWJhIGRlIGNhdXNhbGlkYWQgZGUgR3JhbmdlciBwYXJhIHZlciBzaSBzYWxlc191bml0Ym94ZXMgY2F1c2EgaW5mbGF0aW9uX3JhdGVfZGlmZgpncmFuZ2VyX3Rlc3RfMiA8LSBjYXVzYWxpdHkoVkFSX21vZGVsLCBjYXVzZSA9ICJzYWxlc191bml0Ym94ZXMiKQpwcmludChncmFuZ2VyX3Rlc3RfMiRHcmFuZ2VyKQpgYGAKCi0gQmFzZWQgb24gdGhlIHNlbGVjdGVkIFZBUl9Nb2RlbCwgc2hvdyB0aGUgZm9yZWNhc3Qgb2YgY29jYSBzYWxlcyBpbiB0aGUgR3VhZGFsYWphcmEKTWV0cm9wb2xpdGFuIEFyZWEgZm9yIHRoZSBuZXh0IDUgcGVyaW9kcy4gRGlzcGxheSB0aGUgZXN0aW1hdGVkIGZvcmVjYXN0IGluIGEgdGltZSBzZXJpZXMKcGxvdC4KYGBge3J9CiMgY3JlYXRlIGRmIHRoYXQgY29tYmluZXMgcmVhbCBzYWxlcyBhbmQgZm9yZWNhc3QKY29tYmluZWRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFBlcmlvZCA9IGMoYXMuRGF0ZShjb2NhY29sYSR0cGVyaW9kWy0xXSksIGZvcmVjYXN0X2RmJFBlcmlvZCksICMgcmVhbCBkYXRlcyBhbmQgZm9yZWNhc3QKICBzYWxlc191bml0Ym94ZXMgPSBjKGNvY2Fjb2xhJHNhbGVzX3VuaXRib3hlc1stMV0sIHJlcChOQSwgZm9yZWNhc3Rfc3RlcHMpKSwgICNSZWFsIFNhbGVzIAogIHNhbGVzX3VuaXRib3hlc19wcmVkID0gYyhyZXAoTkEsIGxlbmd0aChjb2NhY29sYSRzYWxlc191bml0Ym94ZXNbLTFdKSksIGZvcmVjYXN0X2RmJHNhbGVzX3VuaXRib3hlc19wcmVkKSAgIyAgRm9yZWNhc3QKKQoKZ2dwbG90KCkgKwogICMgUGxvdCBzYWxlc191bml0Ym94ZXMKICBnZW9tX2xpbmUoZGF0YSA9IGNvbWJpbmVkX2RhdGEsIGFlcyh4ID0gUGVyaW9kLCB5ID0gc2FsZXNfdW5pdGJveGVzLCBjb2xvciA9ICJTYWxlcyAoUmVhbCkiKSwgc2l6ZSA9IDEpICsKICAKICAjIFBsb3Qgc2FsZXNfdW5pdGJveGVzX3ByZWQKICBnZW9tX2xpbmUoZGF0YSA9IGNvbWJpbmVkX2RhdGEsIGFlcyh4ID0gUGVyaW9kLCB5ID0gc2FsZXNfdW5pdGJveGVzX3ByZWQsIGNvbG9yID0gIlNhbGVzIChGb3JlY2FzdCkiKSwgc2l6ZSA9IDEsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICAKICMgVmVydGljYWwgbGluZSB0byBtYXJrIHRoZSBzdGFydCBvZiB0aGUgZm9yZWNhc3QKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBhcy5udW1lcmljKGZvcmVjYXN0X2RmJFBlcmlvZFsxXSksIGNvbG9yID0gInllbGxvdyIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArCiAgCiAgCiAgbGFicyh0aXRsZSA9ICJNb250aGx5IFNhbGVzIGFuZCBWQVIgZm9yZWNhc3Qgb2YgVW5pdCBCb3hlcyIsCiAgICAgICB4ID0gIkRhdGUiLCB5ID0gIlNhbGVzIChVbml0IEJveGVzKSIpICsKICAKIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJTYWxlcyAoUmVhbCkiID0gImJsdWUiLCAiU2FsZXMgKEZvcmVjYXN0KSIgPSAiZ3JlZW4iKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCiMgVi4gQ29uY2x1c2lvbnMgYW5kIFJlY29tbWVuZGF0aW9ucwoKMS4gV2hpbGUgc2FsZXNfdW5pdGJveGVzIEdyYW5nZXItY2F1c2UgaW5mbGF0aW9uX3JhdGVfZGlmZiwgdGhlIHJldmVyc2UgaXMgbm90IHRydWUuIFRoaXMgc3VnZ2VzdHMgdGhhdCBwYXN0IHZhbHVlcyBvZiBzYWxlc191bml0Ym94ZXMgcHJvdmlkZSBzaWduaWZpY2FudCBpbmZvcm1hdGlvbiBmb3IgcHJlZGljdGluZyBjaGFuZ2VzIGluIGluZmxhdGlvbiByYXRlLgoKMi4gVGhlIHJlamVjdGlvbiBvZiB0aGUgbnVsbCBoeXBvdGhlc2lzIGluIHRoZSBzZWNvbmQgR3Jhbmdlci1jYXVzYWxpdHkgdGVzdCBpbmRpY2F0ZXMgdGhhdCBzYWxlcyB1bml0IGJveGVzIGhhdmUgYSBwcmVkaWN0aXZlIGluZmx1ZW5jZSBvbiBpbmZsYXRpb24gcmF0ZXMgYWZ0ZXIgZGlmZmVyZW5jaW5nLiBUaGlzIG1pZ2h0IHN1Z2dlc3QgdGhhdCBjaGFuZ2VzIGluIHNhbGVzIGFjdGl2aXR5IGNvdWxkIGJlIGFuIGluZGljYXRvciBvZiBpbmZsYXRpb25hcnkgdHJlbmRzLgoKMy4gU2luY2UgdGhlIGZpcnN0IEdyYW5nZXItY2F1c2FsaXR5IHRlc3QgZmFpbHMgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgLCBpbmZsYXRpb24gKGFmdGVyIGRpZmZlcmVuY2luZykgZG9lcyBub3QgYXBwZWFyIHRvIEdyYW5nZXItY2F1c2Ugc2FsZXMgdW5pdCBib3hlcy4gVGhpcyBjb3VsZCBpbXBseSB0aGF0IGZsdWN0dWF0aW9ucyBpbiBpbmZsYXRpb24gbWF5IG5vdCBiZSBkaXJlY3RseSBpbmZsdWVuY2luZyBzYWxlcyBhY3Rpdml0eQoKNC4gVGhlIGNvdmFyaWFuY2UgbWF0cml4IG9mIHJlc2lkdWFscyBzaG93cyBhIHdlYWsgbmVnYXRpdmUgY29ycmVsYXRpb24gKC0wLjI5MTcpIGJldHdlZW4gc2FsZXNfdW5pdGJveGVzIGFuZCBpbmZsYXRpb25fcmF0ZV9kaWZmLiBUaGlzIHN1Z2dlc3RzIHRoYXQsIHdoaWxlIHRoZSByZXNpZHVhbHMgb2YgYm90aCB2YXJpYWJsZXMgYXJlIGludGVyY29ubmVjdGVkLCB0aGV5IGRvIG5vdCBleGhpYml0IGEgc3Ryb25nIGxpbmVhciByZWxhdGlvbnNoaXAsIGltcGx5aW5nIHRoYXQgb3RoZXIgZmFjdG9ycyBtYXkgaW5mbHVlbmNlIHRoZSBkeW5hbWljcyBiZXR3ZWVuIHNhbGVzIGFuZCBpbmZsYXRpb24gYmV5b25kIHdoYXQgaXMgY2FwdHVyZWQgYnkgdGhlaXIgcGFzdCB2YWx1ZXMuCgo1LiBUaGUgVkFSIGZvcmVjYXN0IGJlZ2lucyBpbiBsYXRlIDIwMTgsIGp1c3QgYWZ0ZXIgYSBtb2RlcmF0ZSB1cHdhcmQgdHJlbmQgaW4gcmVhbCBzYWxlcy4gVGhlIGZvcmVjYXN0IHNob3dzIGEgcmVsYXRpdmVseSBzdGFibGUgcHJlZGljdGlvbiBmb3IgZnV0dXJlIHNhbGVzLCB3aXRoIHZhbHVlcyBzbGlnaHRseSBhYm92ZSB0aGUgZmluYWwgcmVhbCBzYWxlcyBwb2ludC4gVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgZXhwZWN0cyBhIHN0YWJpbGl6YXRpb24gb2Ygc2FsZXMgYmFzZWQgb24gcmVjZW50IHBhdHRlcm5zLCBwcm9qZWN0aW5nIG1vZGVyYXRlIGdyb3d0aCBvciBjb25zaXN0ZW5jeSBpbiB0aGUgbmVhciBmdXR1cmUuCgo2LiBUaGUgZm9yZWNhc3RlZCBzZWN0aW9uIG9mIHRoZSBncmFwaCBtYWludGFpbnMgdGhlIGNsZWFyIHNlYXNvbmFsIHBhdHRlcm5zIHNlZW4gaW4gaGlzdG9yaWNhbCBkYXRhLCB3aGljaCBzdWdnZXN0cyB0aGF0IHRoZXNlIHBhdHRlcm5zIHdpbGwgcGVyc2lzdC4gQ29jYS1Db2xhIGNhbiB1c2UgdGhpcyBpbmZvcm1hdGlvbiB0byBwcmVwYXJlIGZvciBwZWFrIGRlbWFuZCBwZXJpb2RzLCBlbnN1cmluZyBpbnZlbnRvcnkgYW5kIG1hcmtldGluZyBlZmZvcnRzIGFyZSBhbGlnbmVkIHdpdGggdGhlc2UgcHJlZGljdGFibGUgc2FsZXMgc3VyZ2VzLgoKIyMgUmVjb21tZW5kYXRpb25zCgoxLiBBZGp1c3QgcHJpY2VzIGZvciBDb2NhLUNvbGEgcHJvZHVjdHMgYmFzZWQgb24gdGhlIHdlYXRoZXIuIEZvciBleGFtcGxlLCBvZmZlciBkaXNjb3VudHMgb3IgcHJvbW90aW9ucyBvbiBob3R0ZXIgZGF5cyB0byBlbmNvdXJhZ2UgbW9yZSBzYWxlcy4gSW4gY29sZGVyIG1vbnRocywgeW91IG1pZ2h0IGZvY3VzIG9uIGJ1bmRsaW5nIHdpdGggb3RoZXIgcHJvZHVjdHMuCgoyLiBJbXBsZW1lbnQgYSBwcmljaW5nIHN0cmF0ZWd5IHRoYXQgYWRqdXN0cyB0byBpbmZsYXRpb25hcnkgcHJlc3N1cmVzLiBGb3IgZXhhbXBsZSwgb2ZmZXJpbmcgdGhlIHByb2R1Y3QgaW4gc21hbGxlciBjb250YWluZXJzLCBtaWdodCBtYWtlIGl0IGVhc2llciBmb3IgY3VzdG9tZXJzIHRvIGJ1eSBpdCwgbWFpbmx5IGluIGNvdW50cmllcyBsaWtlIE1leGljbywgd2hlcmUgdGhlIGluY29tZSBpcyBsb3cgaW4gbWFueSBmYW1pbGllcy4KCjMuIFdlIGNvdWxkIGFsc28gY29uc2lkZXIgdXNpbmcgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgbGlrZSBuZXVyYWwgbmV0d29ya3MsIHdoaWNoIGNhbiBoYW5kbGUgdGltZSBzZXJpZXMgZGF0YSBhbmQgY29tcGxleCBub24tbGluZWFyIHJlbGF0aW9uc2hpcHMuIEJ5IGZlZWRpbmcgdGhlc2UgbW9kZWxzIHdpdGggcmVsZXZhbnQgdGltZSBzZXJpZXMgZmVhdHVyZXMsIHdlIGNhbiBpbXByb3ZlIHRoZSBhY2N1cmFjeSBvZiB0aGUgZm9yZWNhc3RzIHdlIG1hZGUuCgoKCgoKCgoKCgoKCgoKCg==