5.1 Preprocesamiento de datos

Gráficos de letras del Tesoro

# Read the Excel file into a variable named datos
datos <- read.xlsx("datos.xlsx")
datos$Tiempo=as.factor(datos$Tiempo)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos_long <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
grafico1 <-ggplot(datos_long[datos_long$Periodo %in% c("3_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "3 meses",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")
grafico2 <-ggplot(datos_long[datos_long$Periodo %in% c("6_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color = "blue") +
  labs(title = "6 meses",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")
grafico3 <-ggplot(datos_long[datos_long$Periodo %in% c("9_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color = "green") +
  labs(title = "9 meses",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")
grafico4 <-ggplot(datos_long[datos_long$Periodo %in% c("12_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color = "orange") +
  labs(title = "12 meses",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")
# Juntar los gráficos
library(gridExtra)
grid.arrange(top = "Retornos de Letras del tesoro 2000-2024",grafico1, grafico2, grafico3, grafico4, ncol = 2)

Gráficos de bonos del Estado

grafico5<-ggplot(datos_long[datos_long$Periodo %in% c("3_años"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "3 años",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal()+
  theme(legend.position = "none")
grafico6<-ggplot(datos_long[datos_long$Periodo %in% c("5_años"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "5 años",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal()+
  theme(legend.position = "none")
# Juntar los gráficos
library(gridExtra)
grid.arrange(top = "Retornos de Bonos del Estado 2000-2024",grafico5, grafico6, ncol = 2)

Gráficos de obligaciones del Estado

grafico7<-ggplot(datos_long[datos_long$Periodo %in% c("10_años"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "10 años",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal()+
  theme(legend.position = "none")
grafico8<-ggplot(datos_long[datos_long$Periodo %in% c("15_años"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "15 años",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal()+
  theme(legend.position = "none")
grafico9<-ggplot(datos_long[datos_long$Periodo %in% c("30-50_años"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="green") +
  labs(title = "30-50 años",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal()+
  theme(legend.position = "none")
# Juntar los gráficos
library(gridExtra)
grid.arrange(top = "Retornos de Obligaciones del estado 2000-2024",grafico7, grafico8,grafico9, ncol = 3)

Cálculo de nulos y graficos a 3 meses

datos <- read.xlsx("datos.xlsx")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "3_meses" , ]
datos <- datos[94:nrow(datos), ]
nulos<-sum(is.na(datos));nulos
## [1] 1
total<-nrow(datos)
porcentaje<-nulos/total;porcentaje
## [1] 0.005376344

GRÁFICO ORIGINAL

# Read the Excel file into a variable named datos
datos <- read.xlsx("datos.xlsx")
datos$Tiempo=as.factor(datos$Tiempo)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos_long_completa <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
grafico1 <-ggplot(datos_long_completa[datos_long_completa$Periodo %in% c("3_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "Serie inicial",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

GRÁFICO NUEVO

datos_c=read.csv2("Datos_csv.csv", header = TRUE)
datos_c$Tiempo=as.factor(datos_c$Tiempo)
datos_c$Tiempo <- as.Date(paste0(datos_c$Tiempo, "_01"), format = "%B_%Y_%d")
datos_long <- pivot_longer(datos_c, -Tiempo, names_to = "Periodo", values_to = "Valor")
#View(datos_long)
datos_long <- datos_long[838:nrow(datos_long), ]
#View(datos_long)
grafico1_c<-ggplot(datos_long[datos_long$Periodo %in% c("X3_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "Serie final",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

UNIÓN DE LOS DOS GRAFICOS

# Juntar los gráficos
library(gridExtra)
library(grid)
citation("ForecastComb")
## 
## To cite package 'ForecastComb' in publications use:
## 
##   Weiss CE, Roetzer GR, Raviv E (2018). _ForecastComb: Forecast
##   Combination Methods_. R package version 1.3.1,
##   <https://CRAN.R-project.org/package=ForecastComb>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {ForecastComb: Forecast Combination Methods},
##     author = {Christoph E. Weiss and Gernot R. Roetzer and Eran Raviv},
##     year = {2018},
##     note = {R package version 1.3.1},
##     url = {https://CRAN.R-project.org/package=ForecastComb},
##   }
## 
## ATTENTION: This citation information has been auto-generated from the
## package DESCRIPTION file and may need manual editing, see
## 'help("citation")'.
# Personalizar el título
title <- textGrob("Retornos de Letras del tesoro a 3 meses", gp = gpar(fontface = "bold", fontsize = 12))

grid.arrange(top = title ,grafico1,grafico1_c, ncol = 2)

Cálculo de nulos y graficos a 6 meses

datos <- read.xlsx("datos.xlsx")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")

#Filtro por 3_meses
datos <- datos[datos$Periodo == "6_meses" , ]
datos <- datos[95:nrow(datos), ]
nulos<-sum(is.na(datos));nulos
## [1] 1
total<-nrow(datos)
porcentaje<-nulos/total;porcentaje
## [1] 0.005405405

GRÁFICO ORIGINAL

grafico2 <-ggplot(datos_long_completa[datos_long_completa$Periodo %in% c("6_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "Serie inicial",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

GRÁFICO NUEVO

datos_c=read.csv2("Datos_csv.csv", header = TRUE)
datos_c$Tiempo=as.factor(datos_c$Tiempo)
datos_c$Tiempo <- as.Date(paste0(datos_c$Tiempo, "_01"), format = "%B_%Y_%d")
datos_long <- pivot_longer(datos_c, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos_long <- datos_long[847:nrow(datos_long), ]
grafico2_c<-ggplot(datos_long[datos_long$Periodo %in% c("X6_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "Serie final",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

UNIÓN DE LOS DOS GRAFICOS

# Personalizar el título
title <- textGrob("Retornos de Letras del tesoro a 6 meses", gp = gpar(fontface = "bold", fontsize = 12))

grid.arrange(top = title ,grafico2,grafico2_c, ncol = 2)

Cálculo de nulos y graficos a 9 meses

datos <- read.xlsx("datos.xlsx")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos <- datos[datos$Periodo == "9_meses" , ]
datos <- datos[146:nrow(datos), ]
nulos<-sum(is.na(datos));nulos
## [1] 0
total<-nrow(datos)
porcentaje<-nulos/total;porcentaje
## [1] 0

GRÁFICO ORIGINAL

grafico3 <-ggplot(datos_long_completa[datos_long_completa$Periodo %in% c("9_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "Serie inicial",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

GRÁFICO NUEVO

datos_long <- pivot_longer(datos_c, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos_long <- datos_long[1306:nrow(datos_long), ]
grafico3_c<-ggplot(datos_long[datos_long$Periodo %in% c("X9_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "Serie final",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

UNIÓN DE LOS DOS GRAFICOS

title <- textGrob("Retornos de Letras del tesoro a 9 meses", gp = gpar(fontface = "bold", fontsize = 12))
grid.arrange(top = title ,grafico3,grafico3_c, ncol = 2)

Cálculo de nulos y graficos a 12 meses

datos <- read.xlsx("datos.xlsx")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "12_meses" , ]
datos <- datos[1:nrow(datos), ]
nulos<-sum(is.na(datos));nulos
## [1] 0
total<-nrow(datos)
porcentaje<-nulos/total;porcentaje
## [1] 0

GRÁFICO ORIGINAL

View(datos_long_completa[datos_long_completa$Periodo %in% c("12_meses"), ])
grafico4 <-ggplot(datos_long_completa[datos_long_completa$Periodo %in% c("12_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "Serie inicial",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

GRÁFICO NUEVO

datos_long <- pivot_longer(datos_c, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos_long <- datos_long[1:nrow(datos_long), ]
grafico4_c<-ggplot(datos_long[datos_long$Periodo %in% c("X12_meses"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "Serie final",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

UNIÓN DE LOS DOS GRAFICOS

title <- textGrob("Retornos de Letras del tesoro a 12 meses", gp = gpar(fontface = "bold", fontsize = 12))
grid.arrange(top = title ,grafico4,grafico4_c, ncol = 2)

Cálculo de nulos y graficos a 3 años

datos <- read.xlsx("datos.xlsx")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_años
datos <- datos[datos$Periodo == "3_años" , ]
datos <- datos[95:nrow(datos), ]
nulos<-sum(is.na(datos));nulos
## [1] 23
total<-nrow(datos)
porcentaje<-nulos/total;porcentaje
## [1] 0.1243243

GRÁFICO ORIGINAL

grafico5 <-ggplot(datos_long_completa[datos_long_completa$Periodo %in% c("3_años"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "Serie inicial",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

GRÁFICO NUEVO

datos_long <- pivot_longer(datos_c, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos_long <- datos_long[847:nrow(datos_long), ]
grafico5_c<-ggplot(datos_long[datos_long$Periodo %in% c("X3_anos"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "Serie final",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

UNIÓN DE LOS DOS GRAFICOS

title <- textGrob("Retornos de Bonos del Estado a 3 años", gp = gpar(fontface = "bold", fontsize = 12))
grid.arrange(top = title ,grafico5,grafico5_c, ncol = 2)

Cálculo de nulos y graficos a 5 años

datos <- read.xlsx("datos.xlsx")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 5_años
datos <- datos[datos$Periodo == "5_años" , ]
datos <- datos[94:nrow(datos), ]
nulos<-sum(is.na(datos));nulos
## [1] 15
total<-nrow(datos)
porcentaje<-nulos/total;porcentaje
## [1] 0.08064516

GRÁFICO ORIGINAL

grafico6 <-ggplot(datos_long_completa[datos_long_completa$Periodo %in% c("5_años"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "Serie inicial",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

GRÁFICO NUEVO

datos_long <- pivot_longer(datos_c, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos_long <- datos_long[838:nrow(datos_long), ]
grafico6_c<-ggplot(datos_long[datos_long$Periodo %in% c("X5_anos"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "Serie final",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

UNIÓN DE LOS DOS GRAFICOS

title <- textGrob("Retornos de Bonos del Estado a 5 años", gp = gpar(fontface = "bold", fontsize = 12))
grid.arrange(top = title ,grafico6,grafico6_c, ncol = 2)

Cálculo de nulos y graficos a 10 años

datos <- read.xlsx("datos.xlsx")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_años
datos <- datos[datos$Periodo == "10_años" , ]
datos <- datos[98:nrow(datos), ]
nulos<-sum(is.na(datos));nulos
## [1] 15
total<-nrow(datos)
porcentaje<-nulos/total;porcentaje
## [1] 0.08241758

GRÁFICO ORIGINAL

grafico7 <-ggplot(datos_long_completa[datos_long_completa$Periodo %in% c("10_años"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line() +
  labs(title = "Serie inicial",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

GRÁFICO NUEVO

datos_long <- pivot_longer(datos_c, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos_long <- datos_long[874:nrow(datos_long), ]
grafico7_c<-ggplot(datos_long[datos_long$Periodo %in% c("X10_anos"), ], 
       aes(x = Tiempo, y = Valor, color = Periodo)) +
  geom_line(color="blue") +
  labs(title = "Serie final",
       x = "Tiempo",
       y = "Valor",
       color = "Periodo")  +
  theme_minimal() +
  theme(legend.position = "none")

UNIÓN DE LOS DOS GRAFICOS

title <- textGrob("Retornos de Obligaciones del Estado a 10 años", gp = gpar(fontface = "bold", fontsize = 12))
grid.arrange(top = title ,grafico7,grafico7_c, ncol = 2)

###Mix 3 meses

library(ggplot2)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:gridExtra':
## 
##     combine
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X3_meses" , ]
calculated_df  <- datos[94:nrow(datos), ]
calculated_df  <- calculated_df[,-2 ]
calculated_df <- data.frame(fecha =calculated_df$Tiempo, valor=calculated_df$Valor  )

# Leer los datos desde el archivo Excel
datos <- read.xlsx("datos.xlsx")

# Convertir la columna 'Tiempo' a formato de fecha
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")

# Pivotar los datos
datos <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")

# Filtrar por el periodo '3_meses'
datos <- filter(datos, Periodo == "3_meses")
original_df  <- datos[94:nrow(datos), ]
original_df  <- original_df[,-2 ]
original_df <- data.frame(fecha =original_df$Tiempo, valor=original_df$Valor  )

original_df$tipo  <- 'Original'
calculated_df$tipo  <- 'Calculated'
combined_df <- rbind(original_df, calculated_df)
# Combinar los data frames para que los valores calculados reemplacen los nulos en los originales
combined_df <- original_df %>%
  full_join(calculated_df, by = "fecha", suffix = c("_original", "_calculated")) %>%
  mutate(
    valor = ifelse(is.na(valor_original), valor_calculated, valor_original),
    tipo = ifelse(is.na(valor_original), "Imputado", "Original")
  )
combined_df$tamano_punto <- ifelse(combined_df$tipo == "Imputado", 2.5, 1)
# Crear el gráfico
mix1<-ggplot(combined_df, aes(x = fecha, y = valor, color = tipo)) +
  geom_line(size = 1, aes(group = 1)) +
  geom_point(aes(size = tamano_punto)) +
  scale_color_manual(values = c("Original" = "blue", "Imputado" = "red")) +
  scale_size_identity() +
  labs(title = "Letras a 3 meses", x = "Fecha", y = "Valor") +
  theme_minimal()+ 
  theme(legend.position = "none")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.

###Mix 6 meses

datos=read.csv2("Datos_csv.csv", header = TRUE)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X6_meses" , ]
calculated_df  <- datos[95:nrow(datos), ]
calculated_df  <- calculated_df[,-2 ]
calculated_df <- data.frame(fecha =calculated_df$Tiempo, valor=calculated_df$Valor  )

# Leer los datos desde el archivo Excel
datos <- read.xlsx("datos.xlsx")

# Convertir la columna 'Tiempo' a formato de fecha
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")

# Pivotar los datos
datos <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")

# Filtrar por el periodo '3_meses'
datos <- filter(datos, Periodo == "6_meses")
original_df  <- datos[95:nrow(datos), ]
original_df  <- original_df[,-2 ]

original_df <- data.frame(fecha =original_df$Tiempo, valor=original_df$Valor  )

original_df$tipo  <- 'Original'
calculated_df$tipo  <- 'Calculated'
combined_df <- rbind(original_df, calculated_df)
# Combinar los data frames para que los valores calculados reemplacen los nulos en los originales
combined_df <- original_df %>%
  full_join(calculated_df, by = "fecha", suffix = c("_original", "_calculated")) %>%
  mutate(
    valor = ifelse(is.na(valor_original), valor_calculated, valor_original),
    tipo = ifelse(is.na(valor_original), "Imputado", "Original")
  )
combined_df$tamano_punto <- ifelse(combined_df$tipo == "Imputado", 2.5, 1)
# Crear el gráfico
mix2<-ggplot(combined_df, aes(x = fecha, y = valor, color = tipo)) +
  geom_line(size = 1, aes(group = 1)) +
  geom_point(aes(size = tamano_punto)) +
  scale_color_manual(values = c("Original" = "blue", "Imputado" = "red")) +
  scale_size_identity() +
  labs(title = "Letras a 6 meses", x = "Fecha", y = "Valor") +
  theme_minimal()+
  theme(legend.position = "none")

###Mix 9 meses

datos=read.csv2("Datos_csv.csv", header = TRUE)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X9_meses" , ]
calculated_df  <- datos[146:nrow(datos), ]
calculated_df  <- calculated_df[,-2 ]

calculated_df <- data.frame(fecha =calculated_df$Tiempo, valor=calculated_df$Valor  )

# Leer los datos desde el archivo Excel
datos <- read.xlsx("datos.xlsx")

# Convertir la columna 'Tiempo' a formato de fecha
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")

# Pivotar los datos
datos <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")

# Filtrar por el periodo '3_meses'
datos <- filter(datos, Periodo == "9_meses")
original_df  <- datos[146:nrow(datos), ]
original_df  <- original_df[,-2 ]

original_df <- data.frame(fecha =original_df$Tiempo, valor=original_df$Valor  )

original_df$tipo  <- 'Original'
calculated_df$tipo  <- 'Calculated'
combined_df <- rbind(original_df, calculated_df)
# Combinar los data frames para que los valores calculados reemplacen los nulos en los originales
combined_df <- original_df %>%
  full_join(calculated_df, by = "fecha", suffix = c("_original", "_calculated")) %>%
  mutate(
    valor = ifelse(is.na(valor_original), valor_calculated, valor_original),
    tipo = ifelse(is.na(valor_original), "Imputado", "Original")
  )
combined_df$tamano_punto <- ifelse(combined_df$tipo == "Imputado", 2.5, 1)
# Crear el gráfico
mix3<-ggplot(combined_df, aes(x = fecha, y = valor, color = tipo)) +
  geom_line(size = 1, aes(group = 1)) +
  geom_point(aes(size = tamano_punto)) +
  scale_color_manual(values = c("Original" = "blue", "Imputado" = "red")) +
  scale_size_identity() +
  labs(title = "Letras a 9 meses", x = "Fecha", y = "Valor") +
  theme_minimal()+
  theme(legend.position = "none")

###Mix 12 meses

datos=read.csv2("Datos_csv.csv", header = TRUE)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X12_meses" , ]
calculated_df  <- datos[1:nrow(datos), ]
calculated_df  <- calculated_df[,-2 ]

calculated_df <- data.frame(fecha =calculated_df$Tiempo, valor=calculated_df$Valor  )

# Leer los datos desde el archivo Excel
datos <- read.xlsx("datos.xlsx")

# Convertir la columna 'Tiempo' a formato de fecha
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")

# Pivotar los datos
datos <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")

# Filtrar por el periodo '3_meses'
datos <- filter(datos, Periodo == "12_meses")
original_df  <- datos[1:nrow(datos), ]
original_df  <- original_df[,-2 ]

original_df <- data.frame(fecha =original_df$Tiempo, valor=original_df$Valor  )

original_df$tipo  <- 'Original'
calculated_df$tipo  <- 'Calculated'
combined_df <- rbind(original_df, calculated_df)
# Combinar los data frames para que los valores calculados reemplacen los nulos en los originales
combined_df <- original_df %>%
  full_join(calculated_df, by = "fecha", suffix = c("_original", "_calculated")) %>%
  mutate(
    valor = ifelse(is.na(valor_original), valor_calculated, valor_original),
    tipo = ifelse(is.na(valor_original), "Imputado", "Original")
  )
combined_df$tamano_punto <- ifelse(combined_df$tipo == "Imputado", 2.5, 1)
# Crear el gráfico
mix4<-ggplot(combined_df, aes(x = fecha, y = valor, color = tipo)) +
  geom_line(size = 1, aes(group = 1)) +
  geom_point(aes(size = tamano_punto)) +
  scale_color_manual(values = c("Original" = "blue", "Imputado" = "red")) +
  scale_size_identity() +
  labs(title = "Letras a 12 meses", x = "Fecha", y = "Valor") +
  theme_minimal()+
  theme(legend.position = "none")

Unir graficos

# Juntar los gráficos
library(gridExtra)
grid.arrange(top = " Valores imputados en las Letras del tesoro 2000-2024",mix1, mix2, mix3, mix4, ncol = 2)

###Mix 3 años

datos=read.csv2("Datos_csv.csv", header = TRUE)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X3_anos" , ]
calculated_df  <- datos[95:nrow(datos), ]
calculated_df  <- calculated_df[,-2 ]

calculated_df <- data.frame(fecha =calculated_df$Tiempo, valor=calculated_df$Valor  )

# Leer los datos desde el archivo Excel
datos <- read.xlsx("datos.xlsx")

# Convertir la columna 'Tiempo' a formato de fecha
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")

# Pivotar los datos
datos <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")

# Filtrar por el periodo '3_meses'
datos <- filter(datos, Periodo == "3_años")
original_df  <- datos[95:nrow(datos), ]
original_df  <- original_df[,-2 ]

original_df <- data.frame(fecha =original_df$Tiempo, valor=original_df$Valor  )

original_df$tipo  <- 'Original'
calculated_df$tipo  <- 'Calculated'
combined_df <- rbind(original_df, calculated_df)
# Combinar los data frames para que los valores calculados reemplacen los nulos en los originales
combined_df <- original_df %>%
  full_join(calculated_df, by = "fecha", suffix = c("_original", "_calculated")) %>%
  mutate(
    valor = ifelse(is.na(valor_original), valor_calculated, valor_original),
    tipo = ifelse(is.na(valor_original), "Imputado", "Original")
  )
# Crear el gráfico
mix5<-ggplot(combined_df, aes(x = fecha, y = valor, color = tipo)) +
  geom_line(size = 1, aes(group = 1)) +
  geom_point() +
  scale_color_manual(values = c("Original" = "blue", "Imputado" = "red")) +
  scale_size_identity() +
  labs(title = "Bonos a 3 años", x = "Fecha", y = "Valor") +
  theme_minimal()+
  theme(legend.position = "none")

###Mix 5 años

datos=read.csv2("Datos_csv.csv", header = TRUE)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X5_anos" , ]
calculated_df  <- datos[94:nrow(datos), ]
calculated_df  <- calculated_df[,-2 ]

calculated_df <- data.frame(fecha =calculated_df$Tiempo, valor=calculated_df$Valor  )

# Leer los datos desde el archivo Excel
datos <- read.xlsx("datos.xlsx")

# Convertir la columna 'Tiempo' a formato de fecha
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")

# Pivotar los datos
datos <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")

# Filtrar por el periodo '3_meses'
datos <- filter(datos, Periodo == "5_años")
original_df  <- datos[94:nrow(datos), ]
original_df  <- original_df[,-2 ]

original_df <- data.frame(fecha =original_df$Tiempo, valor=original_df$Valor  )

original_df$tipo  <- 'Original'
calculated_df$tipo  <- 'Calculated'
combined_df <- rbind(original_df, calculated_df)
# Combinar los data frames para que los valores calculados reemplacen los nulos en los originales
combined_df <- original_df %>%
  full_join(calculated_df, by = "fecha", suffix = c("_original", "_calculated")) %>%
  mutate(
    valor = ifelse(is.na(valor_original), valor_calculated, valor_original),
    tipo = ifelse(is.na(valor_original), "Imputado", "Original")
  )
# Crear el gráfico
mix6<-ggplot(combined_df, aes(x = fecha, y = valor, color = tipo)) +
  geom_line(size = 1, aes(group = 1)) +
  geom_point() +
  scale_color_manual(values = c("Original" = "blue", "Imputado" = "red")) +
  scale_size_identity() +
  labs(title = "Bonos a 5 años", x = "Fecha", y = "Valor") +
  theme_minimal()+
  theme(legend.position = "none")

Unir graficos

###Mix 10 años

datos=read.csv2("Datos_csv.csv", header = TRUE)
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X10_anos" , ]
calculated_df  <- datos[98:nrow(datos), ]
calculated_df  <- calculated_df[,-2 ]

calculated_df <- data.frame(fecha =calculated_df$Tiempo, valor=calculated_df$Valor  )

# Leer los datos desde el archivo Excel
datos <- read.xlsx("datos.xlsx")

# Convertir la columna 'Tiempo' a formato de fecha
datos$Tiempo <- as.Date(paste0(datos$Tiempo, "_01"), format = "%B_%Y_%d")

# Pivotar los datos
datos <- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")

# Filtrar por el periodo '3_meses'
datos <- filter(datos, Periodo == "10_años")
original_df  <- datos[98:nrow(datos), ]
original_df  <- original_df[,-2 ]

original_df <- data.frame(fecha =original_df$Tiempo, valor=original_df$Valor  )

original_df$tipo  <- 'Original'
calculated_df$tipo  <- 'Calculated'
combined_df <- rbind(original_df, calculated_df)
# Combinar los data frames para que los valores calculados reemplacen los nulos en los originales
combined_df <- original_df %>%
  full_join(calculated_df, by = "fecha", suffix = c("_original", "_calculated")) %>%
  mutate(
    valor = ifelse(is.na(valor_original), valor_calculated, valor_original),
    tipo = ifelse(is.na(valor_original), "Imputado", "Original")
  )
# Crear el gráfico
mix7<-ggplot(combined_df, aes(x = fecha, y = valor, color = tipo)) +
  geom_line(size = 1, aes(group = 1)) +
  geom_point() +
  scale_color_manual(values = c("Original" = "blue", "Imputado" = "red")) +
  scale_size_identity() +
  labs(title = "Obligaciones a 10 años", x = "Fecha", y = "Valor") +
  theme_minimal()+
  theme(legend.position = "none")
# Juntar los gráficos
library(gridExtra)
grid.arrange(top = " Valores imputados en las Bonos del Estado 2000-2024", mix5, mix6, ncol = 2)

5.2.2 Modelos de predicción

5.2.2.1 Modelos de alPCA

Funciones que utiliza el modelo alPCA

calculo1<-function(j){
  # We define the general parameters
  serie<-ensayo[j]
  n_p<-12  
  n<-length(datos[[serie]]) 
  estac<-12 #monthly seasonality
  comienzo <- start(datos[[serie]])
  
  # We select the period T1(x) T2(x)
  x  <- ts(datos[[serie]][1:(n-(2*n_p))],start = comienzo, frequency = estac) #T1
  if(end(x)[2]==estac) 
  {comienzo2<-c(end(x)[1]+1,1)
  }else 
  {comienzo2<-c(end(x)[1],end(x)[2]+1)}
  x1 <- ts(datos[[serie]][(n-(2*n_p)+1):(n-n_p)], start = comienzo2,frequency = estac) #T2
  
  set.seed(846) 
  metodos<-list()
  #Naïve
  metodos[[1]]<-naive(x,h=n_p)
  #Naïve with seasonality
  metodos[[2]]<-snaive(x,h=n_p)
  #Exponential smoothing
  metodos[[3]]<-ets(x,model = "ZZZ", opt.crit="lik", damped = NULL)
  metodos[[3]]<-predict(metodos[[3]],n_p)
  metodos[[4]]<-ets(x,model = "ZZZ", opt.crit="mse", damped = NULL)
  metodos[[4]]<-predict(metodos[[4]],n_p)
  metodos[[5]]<-ets(x,model = "ZZZ", opt.crit="amse", damped = NULL)
  metodos[[5]]<-predict(metodos[[5]],n_p)
  metodos[[6]]<-ets(x,model = "ZZZ", opt.crit="sigma", damped = NULL)
  metodos[[6]]<-predict(metodos[[6]],n_p)
  metodos[[7]]<-ets(x,model = "ZZZ", opt.crit="mae", damped = NULL)
  metodos[[7]]<-predict(metodos[[7]],n_p)
  metodos[[8]]<-ets(x,model = "ZZZ", opt.crit="lik", damped = TRUE)
  metodos[[8]]<-predict(metodos[[8]],n_p)
  metodos[[9]]<-ets(x,model = "ZZZ", opt.crit="mse", damped = TRUE)
  metodos[[9]]<-predict(metodos[[9]],n_p)
  metodos[[10]]<-ets(x,model = "ZZZ", opt.crit="amse", damped = TRUE)
  metodos[[10]]<-predict(metodos[[10]],n_p)
  metodos[[11]]<-ets(x,model = "ZZZ", opt.crit="sigma", damped = TRUE)
  metodos[[11]]<-predict(metodos[[11]],n_p)
  metodos[[12]]<-ets(x,model = "ZZZ", opt.crit="mae", damped = TRUE)
  metodos[[12]]<-predict(metodos[[12]],n_p)
  #ARIMA
  a<-NULL
  metodos[[13]]<-auto.arima(x, ic="aic",approximation=T, trace=FALSE, allowdrift=F)
  a<-predict(metodos[[13]],n_p)
  metodos[[13]]$mean<-a$pred
  metodos[[14]]<-auto.arima(x, ic="bic",approximation=T, trace=FALSE, allowdrift=F)
  a<-predict(metodos[[14]],n_p)
  metodos[[14]]$mean<-a$pred
  #Theta
  metodos[[15]]<-otm.arxiv(x, h=n_p, s= NULL, g= "sAPE")
  metodos[[16]]<-dotm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="Nelder-Mead") #Dynamic Optimised Theta Model
  metodos[[17]]<-dotm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="L-BFGS-B") #Dynamic Optimised Theta Model
  metodos[[18]]<-dotm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="SANN") #Dynamic Optimised Theta Model
  metodos[[19]]<-dstm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="Nelder-Mead") #Dynamic Standard Theta Model
  metodos[[20]]<-dstm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="L-BFGS-B") #Dynamic Standard Theta Model
  metodos[[21]]<-dstm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="SANN") #Dynamic Standard Theta Model
  metodos[[22]]<-otm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="Nelder-Mead")  #Optimised Theta Model
  metodos[[23]]<-otm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="L-BFGS-B")  #Optimised Theta Model
  metodos[[24]]<-otm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="SANN")  #Optimised Theta Model
  metodos[[25]]<-stm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="Nelder-Mead")  #Standard Theta Model (Fiorucci)
  metodos[[26]]<-stm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="L-BFGS-B")  #Standard Theta Model (Fiorucci)
  metodos[[27]]<-stm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="SANN")  #Standard Theta Model (Fiorucci)
  metodos[[28]]<-stheta(x, h=n_p, s= NULL) #Standard Theta Model (Assimakopoulos)
  #STL (sólo series estacionales)
  metodos[[29]]<-stlf(x,h=n_p, method=c("ets"),robust = TRUE,allow.multiplicative.trend = TRUE)
  metodos[[30]]<-stlf(x,h=n_p, method=c("arima"),robust = TRUE,allow.multiplicative.trend = TRUE)
  metodos[[31]]<-stlf(x,h=n_p, forecastfunction=thetaf,robust = TRUE,allow.multiplicative.trend = TRUE)
  #Croston
  metodos[[32]]<-crost(x,h=n_p,type = "croston",cost = "mar",init.opt=TRUE)
  metodos[[32]]$fitted<-ts(metodos[[32]]$frc.in, start =comienzo,frequency = estac) 
  metodos[[32]]$mean<-  ts(metodos[[32]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[33]]<-crost(x,h=n_p,type = "croston",cost = "msr",init.opt=TRUE)
  metodos[[33]]$fitted<-ts(metodos[[33]]$frc.in, start =comienzo,frequency = estac) 
  metodos[[33]]$mean<-  ts(metodos[[33]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[34]]<-crost(x,h=n_p,type = "croston",cost = "mae",init.opt=TRUE)
  metodos[[34]]$fitted<-ts(metodos[[34]]$frc.in, start =comienzo,frequency = estac)
  metodos[[34]]$mean<-  ts(metodos[[34]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[35]]<-crost(x,h=n_p,type = "croston",cost = "mse",init.opt=TRUE)
  metodos[[35]]$fitted<-ts(metodos[[35]]$frc.in, start =comienzo,frequency = estac)
  metodos[[35]]$mean<-  ts(metodos[[35]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[36]]<-crost(x,h=n_p,type = "sba",cost = "mar",init.opt=TRUE)
  metodos[[36]]$fitted<-ts(metodos[[36]]$frc.in, start =comienzo,frequency = estac)
  metodos[[36]]$mean<-  ts(metodos[[36]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[37]]<-crost(x,h=n_p,type = "sba",cost = "msr",init.opt=TRUE)
  metodos[[37]]$fitted<-ts(metodos[[37]]$frc.in, start =comienzo,frequency = estac)
  metodos[[37]]$mean<-  ts(metodos[[37]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[38]]<-crost(x,h=n_p,type = "sba",cost = "mae",init.opt=TRUE)
  metodos[[38]]$fitted<-ts(metodos[[38]]$frc.in, start =comienzo,frequency = estac)
  metodos[[38]]$mean<-  ts(metodos[[38]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[39]]<-crost(x,h=n_p,type = "sba",cost = "mse",init.opt=TRUE)
  metodos[[39]]$fitted<-ts(metodos[[39]]$frc.in, start =comienzo,frequency = estac)
  metodos[[39]]$mean<-  ts(metodos[[39]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[40]]<-crost(x,h=n_p,type = "sbj",cost = "mar",init.opt=TRUE)
  metodos[[40]]$fitted<-ts(metodos[[40]]$frc.in, start =comienzo,frequency = estac)
  metodos[[40]]$mean<-  ts(metodos[[40]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[41]]<-crost(x,h=n_p,type = "sbj",cost = "msr",init.opt=TRUE)
  metodos[[41]]$fitted<-ts(metodos[[41]]$frc.in, start =comienzo,frequency = estac)
  metodos[[41]]$mean<-  ts(metodos[[41]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[42]]<-crost(x,h=n_p,type = "sbj",cost = "mae",init.opt=TRUE)
  metodos[[42]]$fitted<-ts(metodos[[42]]$frc.in, start =comienzo,frequency = estac)
  metodos[[42]]$mean<-  ts(metodos[[42]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[43]]<-crost(x,h=n_p,type = "sbj",cost = "mse",init.opt=TRUE)
  metodos[[43]]$fitted<-ts(metodos[[43]]$frc.in, start =comienzo,frequency = estac)
  metodos[[43]]$mean<-  ts(metodos[[43]]$frc.out, start =comienzo2,frequency = estac) 
  #Prophet
  set.seed(50)
  df<-data.frame(ds = seq(as.Date(paste(start(x)[1],"-",start(x)[2],"-","01",sep="")),
                          as.Date(paste(end(x)[1],"-",end(x)[2],"-","01",sep="")),by="m"), y = x)
  metodos[[44]]<-prophet(df,yearly.seasonality=TRUE,
                         weekly.seasonality = FALSE,
                         daily.seasonality = FALSE,seasonality.mode = "additive")
  a<-make_future_dataframe(metodos[[44]],periods = 18,freq = "month")
  b<-predict(metodos[[44]],a)
  metodos[[44]]$fitted <- ts(b$yhat[1:length(x)],start = comienzo,frequency = estac)
  metodos[[44]]$mean <- ts(b$yhat[(length(x)+1):(length(x)+n_p)],start = comienzo2,frequency = estac)
  metodos[[45]]<-prophet(df,yearly.seasonality=TRUE,
                         weekly.seasonality = FALSE,
                         daily.seasonality = FALSE,seasonality.mode = "multiplicative")
  a<-make_future_dataframe(metodos[[45]],periods = 18,freq = "month")
  b<-predict(metodos[[45]],a)
  metodos[[45]]$fitted <- ts(b$yhat[1:length(x)],start = comienzo,frequency = estac)
  metodos[[45]]$mean <- ts(b$yhat[(length(x)+1):(length(x)+n_p)],start = comienzo2,frequency = estac)
  set.seed(50)
  metodos[[46]]<-nnetar(x,p=estac,scale.inputs=T)
  a<-forecast(metodos[[46]],h=n_p)
  metodos[[46]]$mean<-a$mean  
  #TBATS model (Exponential smoothing state space model with Box-Cox transformation, ARMA errors, Trend and Seasonal components)
  set.seed(50)
  metodos[[47]]<-tbats(x, biasadj = TRUE)
  metodos[[47]]$fitted<-metodos[[47]]$fitted.values
  a<-forecast(metodos[[47]],h=n_p)
  metodos[[47]]$mean<-a$mean
  #GRNN: general regression neural network 
  set.seed(50)
  metodos[[48]]<-grnn_forecasting(x, h = n_p, transform = "additive")
  a<-rolling_origin(metodos[[48]],h = length(x)-estac-1)
  metodos[[48]]$fitted<-a$predictions[,1]
  metodos[[48]]$mean<-metodos[[48]]$prediction
  metodos[[49]]<-grnn_forecasting(x, h = n_p, transform = "multiplicative")
  a<-rolling_origin(metodos[[49]],h = length(x)-estac-1)
  metodos[[49]]$fitted<-a$predictions[,1]
  metodos[[49]]$mean<-metodos[[49]]$prediction
  #MLP: multilayer perceptron
  set.seed(50)
  metodos[[50]]<-mlp.thief(x, h=12)
  metodos[[51]]<-mlp(x, m=12, comb = c("median"),hd = c(10,5))
  a<-forecast(metodos[[51]],h=12)
  metodos[[51]]$mean<-a$mean
  metodos[[52]]<-mlp(x, m=12, comb = c("mean"),hd = c(10,5))
  a<-forecast(metodos[[52]],h=12)
  metodos[[52]]$mean<-a$mean
  gc()
  return(metodos)
}

calculo2<- function(j) {
  # We define the general parameters
  serie<-ensayo[j]
  n_p<-12 
  n<-length(datos[[serie]]) #nº de datos
  estac<-12 
  comienzo <- start(datos[[serie]])
  
  # We select the period T1(x) T2(x)
  x  <- ts(datos[[serie]][1:(n-(2*n_p))],start = comienzo, frequency = estac) #T1
  if(end(x)[2]==estac) 
  {comienzo2<-c(end(x)[1]+1,1)
  }else 
  {comienzo2<-c(end(x)[1],end(x)[2]+1)}
  x1 <- ts(datos[[serie]][(n-(2*n_p)+1):(n-n_p)], start = comienzo2,frequency = estac) #T2
  
  #sMAPE in T1: In some cases it is straightforward, but in others we lose initial values.
  metodos.T1[[j]][[1]]$sMapeT1 <- 100*Metrics::smape(x[2:length(x)],metodos.T1[[j]][[1]]$fitted[2:length(x)])
  metodos.T1[[j]][[2]]$sMapeT1 <- 100*Metrics::smape(x[(estac+1):length(x)],metodos.T1[[j]][[2]]$fitted[(estac+1):length(x)])
  for (i in 1:24) {
    metodos.T1[[j]][[2+i]]$sMapeT1 <- 100*Metrics::smape(x,metodos.T1[[j]][[2+i]]$fitted)
  }
  for (i in 1:17) {
    metodos.T1[[j]][[26+i]]$sMapeT1 <- 100*Metrics::smape(x[2:length(x)],metodos.T1[[j]][[26+i]]$fitted[2:length(x)])
  }
  for (i in 1:2) {
    metodos.T1[[j]][[43+i]]$sMapeT1 <- 100*Metrics::smape(x,metodos.T1[[j]][[43+i]]$fitted)
  }
  metodos.T1[[j]][[46]]$sMapeT1 <- 100*Metrics::smape(x[(estac+1):length(x)],metodos.T1[[j]][[46]]$fitted[(estac+1):length(x)])
  metodos.T1[[j]][[47]]$sMapeT1 <- 100*Metrics::smape(x,metodos.T1[[j]][[47]]$fitted)
  for (i in 1:2) {
    metodos.T1[[j]][[47+i]]$sMapeT1 <- 100*Metrics::smape(x[1:(length(x)-estac-1)],metodos.T1[[j]][[47+i]]$fitted)
  }
  metodos.T1[[j]][[50]]$sMapeT1 <- 100*Metrics::smape(x[(estac+3):length(x)],
                                                      metodos.T1[[j]][[50]]$fitted[(estac+3):length(x)])
  metodos.T1[[j]][[51]]$sMapeT1 <- 100*Metrics::smape(x,metodos.T1[[j]][[51]]$fitted)
  metodos.T1[[j]][[52]]$sMapeT1 <- 100*Metrics::smape(x,metodos.T1[[j]][[52]]$fitted)
  #MASE in T1: In some cases it is straightforward, but in others we lose initial values.
  metodos.T1[[j]][[1]]$maseT1 <- Metrics::mase(x[2:length(x)],metodos.T1[[j]][[1]]$fitted[2:length(x)],step_size=12)
  metodos.T1[[j]][[2]]$maseT1 <- Metrics::mase(x[(estac+1):length(x)],metodos.T1[[j]][[2]]$fitted[(estac+1):length(x)],step_size=12)
  for (i in 1:24) {
    metodos.T1[[j]][[2+i]]$maseT1 <- Metrics::mase(x,metodos.T1[[j]][[2+i]]$fitted,step_size=12)
  }
  for (i in 1:17) {
    metodos.T1[[j]][[26+i]]$maseT1 <- Metrics::mase(x[2:length(x)],metodos.T1[[j]][[26+i]]$fitted[2:length(x)],step_size=12)
  }
  for (i in 1:2) {
    metodos.T1[[j]][[43+i]]$maseT1 <- Metrics::mase(x,metodos.T1[[j]][[43+i]]$fitted,step_size=12)
  }
  metodos.T1[[j]][[46]]$maseT1 <- Metrics::mase(x[(estac+1):length(x)],metodos.T1[[j]][[46]]$fitted[(estac+1):length(x)],step_size=12)
  metodos.T1[[j]][[47]]$maseT1 <- Metrics::mase(x,metodos.T1[[j]][[47]]$fitted,step_size=12)
  for (i in 1:2) {
    metodos.T1[[j]][[47+i]]$maseT1 <- Metrics::mase(x[1:(length(x)-estac-1)],metodos.T1[[j]][[47+i]]$fitted,step_size=12)
  }
  metodos.T1[[j]][[50]]$maseT1 <- Metrics::mase(x[(estac+3):length(x)],metodos.T1[[j]][[50]]$fitted[(estac+3):length(x)])
  metodos.T1[[j]][[51]]$maseT1 <- Metrics::mase(x, metodos.T1[[j]][[51]]$fitted)
  metodos.T1[[j]][[52]]$maseT1 <- Metrics::mase(x, metodos.T1[[j]][[52]]$fitted)
  #sMAPE in T2
  for (i in 1:length(metodos.T1[[1]])) {
    metodos.T1[[j]][[i]]$sMapeT2 <- 100*Metrics::smape(x1,metodos.T1[[j]][[i]]$mean)
  }
  #RMSE in T2
  for (i in 1:length(metodos.T1[[1]])) {
    metodos.T1[[j]][[i]]$rmseT2 <- Metrics::rmse(x1,metodos.T1[[j]][[i]]$mean)
  }
  #mase en T2
  for (i in 1:length(metodos.T1[[1]])) {
    metodos.T1[[j]][[i]]$maseT2 <- Metrics::mase(x1,metodos.T1[[j]][[i]]$mean,step_size=1)
  }
  #OWA in T2
  for (i in 1:length(metodos.T1[[1]])) {
    metodos.T1[[j]][[i]]$owaT2 <- ((metodos.T1[[j]][[i]]$maseT2 / metodos.T1[[j]][[2]]$maseT2)+(metodos.T1[[j]][[i]]$sMapeT2 / metodos.T1[[j]][[2]]$sMapeT2))/2
  }
  return(metodos.T1[[j]])
}  

calculo3<-function(j){
  erroresT2[[j]]<-matrix(NA,nrow = length(metodos.T1[[1]]),ncol = 13)
  colnames(erroresT2[[j]])<-c("id","smapeT1","maseT1","rmse","smape",
                              "mase","owa","CP1","CP2", "dist", "w1","w2","w3")
  for (i in 1:length(metodos.T1[[1]])) {
    erroresT2[[j]][i,1]<-i
    erroresT2[[j]][i,2]<-metodos.T1[[j]][[i]]$sMapeT1
    erroresT2[[j]][i,3]<-metodos.T1[[j]][[i]]$maseT1
    erroresT2[[j]][i,4]<-metodos.T1[[j]][[i]]$rmseT2
    erroresT2[[j]][i,5]<-metodos.T1[[j]][[i]]$sMapeT2
    erroresT2[[j]][i,6]<-metodos.T1[[j]][[i]]$maseT2
    erroresT2[[j]][i,7]<-metodos.T1[[j]][[i]]$owaT2
  }
  dat1<-as.data.frame(round(erroresT2[[j]],3))
  dat1<-dat1 %>% distinct(smapeT1, .keep_all = TRUE)
  dat1<-dat1 %>% distinct(rmse, .keep_all = TRUE)
  erroresT2[[j]]<-as.matrix(dat1)
  return(erroresT2[[j]])
}

calculo4<-function(j){
  pca[[j]] <- prcomp(erroresT2[[j]][,2:7], scale = TRUE)
  if(sum(pca[[j]][["rotation"]][,1])<0) {erroresT2[[j]][,8]<- round(-(pca[[j]][["x"]][,1]),3)} else {erroresT2[[j]][,8]<- round(pca[[j]][["x"]][,1],3)}
  erroresT2[[j]][,9]<- round(pca[[j]][["x"]][,2],3)
  for (i in 1:dim(erroresT2[[j]])[1]) {
    erroresT2[[j]][i,10]<- round(abs(erroresT2[[j]][i,8] - min(erroresT2[[j]][,8])),3)
  }
  resultado[[1]][[j]]<-erroresT2[[j]]
  resultado[[2]][[j]]<-pca[[j]]
  return(resultado)
}

calculo4.m<-function(j){
  pca[[j]] <- prcomp(erroresT2[[j]][,2:7], scale = TRUE)
  a<-summary(pca[[j]])
  erroresT2[[j]][,8:9]<-round(pca[[j]][["x"]][,1:2],3)
  if(a[["importance"]][2,1]>0.80) {
    if(sum(pca[[j]][["rotation"]][3:6,1])<0) {for (i in 1:nrow(erroresT2[[j]])) {erroresT2[[j]][i,10]<- round(abs(erroresT2[[j]][i,8] - max(erroresT2[[j]][,8])),3)} } 
    else { for (i in 1:nrow(erroresT2[[j]])) {erroresT2[[j]][i,10]<- round(abs(erroresT2[[j]][i,8] - min(erroresT2[[j]][,8])),3)} }
  } else {
    if(sum(pca[[j]][["rotation"]][3:6,1])>0 & sum(pca[[j]][["rotation"]][1:2,2])>0) {
      m1 <- min(erroresT2[[j]][,8])
      m2 <- min(erroresT2[[j]][,9])} else if(sum(pca[[j]][["rotation"]][3:6,1])>0 & sum(pca[[j]][["rotation"]][1:2,2])<0) {
        m1 <- min(erroresT2[[j]][,8])
        m2 <- max(erroresT2[[j]][,9])} else if(sum(pca[[j]][["rotation"]][3:6,1])<0 & sum(pca[[j]][["rotation"]][1:2,2])>0) {
          m1 <- max(erroresT2[[j]][,8])
          m2 <- min(erroresT2[[j]][,9])} else {
            m1 <- max(erroresT2[[j]][,8])
            m2 <- max(erroresT2[[j]][,9])}
    #Manhattan distance
    for (i in 1:nrow(erroresT2[[j]])) {
      erroresT2[[j]][i,10]<- round(abs(erroresT2[[j]][i,8] - m1)+abs(erroresT2[[j]][i,9] - m2),3)
    }
  }
  resultado[[1]][[j]]<-erroresT2[[j]]
  resultado[[2]][[j]]<-pca[[j]]
  #If they exist, distances 0 must be replaced by distances 0.001
  for (i in 1:nrow(resultado[[1]][[j]])) {
    resultado[[1]][[j]][i,10]<-ifelse(resultado[[1]][[j]][i,10]==0.000, 0.001,resultado[[1]][[j]][i,10])
  }
  comparacion_bic <- BIC(
    mlexp(resultado[[1]][[j]][,10]),
    mlinvgamma(resultado[[1]][[j]][,10]),
    mlgamma(resultado[[1]][[j]][,10])
  )
  resultado[[3]][[j]]<- comparacion_bic %>% rownames_to_column(var = "distribucion") %>% arrange(BIC)
  return(resultado)
}

calculo5<-function(j,i){
  if(dis.ajust[j] ==1) {obj<-mlexp(resultado[[1]][[j]][,10])} else
    if(dis.ajust[j]==2) {obj<-mlgamma(resultado[[1]][[j]][,10])} else {obj<-mlinvgamma(resultado[[1]][[j]][,10])}
  dat<-subset(resultado[[1]][[j]],resultado[[1]][[j]][,10]<=round(qml(cota[i], obj = obj),4) )
  if(nrow(dat)<1) {dat<-subset(resultado[[1]][[j]], resultado[[1]][[j]][,1]==1)}
  a<-NULL
  b<-NULL
  for (m in 1:nrow(dat)) {
    a[m]<-round(1/dat[m,2],4) 
    b[m]<-round(1/dat[m,5],4) 
  }
  for (m in 1:nrow(dat)) {
    dat[m,11]<- round(abs(dat[m,8])/sum(abs(dat[,8])),4)  #w1
    dat[m,12]<- round(a[m] / sum(a),4)  #w2
    dat[m,13]<- round(b[m] / sum(b),4)  #w3
  }
  resultado[[4]][[j]][[i]]<-dat
  return(resultado)
}

calculo6<-function(j){
  # We define the general parameters
  serie<-ensayo[j]
  n_p<-12 
  n<-length(datos[[serie]]) 
  estac<-12 
  comienzo <- start(datos[[serie]])
  # Select the period T1(x) + T2(x)
  x  <- ts(datos[[serie]][1:(n-n_p)],start = comienzo, frequency = estac) #T1
  if(end(x)[2]==estac) 
  {comienzo2<-c(end(x)[1]+1,1)
  }else 
  {comienzo2<-c(end(x)[1],end(x)[2]+1)}
  x2 <- ts(datos[[serie]][(n-n_p+1):n],start = comienzo2,frequency = estac) #T2
  set.seed(846) 
  metodos<-list()
  #Naïve
  metodos[[1]]<-naive(x,h=n_p)
  #Naïve with seasonality
  metodos[[2]]<-snaive(x,h=n_p)
  #Exponential smoothing
  metodos[[3]]<-ets(x,model = "ZZZ", opt.crit="lik", damped = NULL)
  metodos[[3]]<-predict(metodos[[3]],n_p)
  metodos[[4]]<-ets(x,model = "ZZZ", opt.crit="mse", damped = NULL)
  metodos[[4]]<-predict(metodos[[4]],n_p)
  metodos[[5]]<-ets(x,model = "ZZZ", opt.crit="amse", damped = NULL)
  metodos[[5]]<-predict(metodos[[5]],n_p)
  metodos[[6]]<-ets(x,model = "ZZZ", opt.crit="sigma", damped = NULL)
  metodos[[6]]<-predict(metodos[[6]],n_p)
  metodos[[7]]<-ets(x,model = "ZZZ", opt.crit="mae", damped = NULL)
  metodos[[7]]<-predict(metodos[[7]],n_p)
  metodos[[8]]<-ets(x,model = "ZZZ", opt.crit="lik", damped = TRUE)
  metodos[[8]]<-predict(metodos[[8]],n_p)
  metodos[[9]]<-ets(x,model = "ZZZ", opt.crit="mse", damped = TRUE)
  metodos[[9]]<-predict(metodos[[9]],n_p)
  metodos[[10]]<-ets(x,model = "ZZZ", opt.crit="amse", damped = TRUE)
  metodos[[10]]<-predict(metodos[[10]],n_p)
  metodos[[11]]<-ets(x,model = "ZZZ", opt.crit="sigma", damped = TRUE)
  metodos[[11]]<-predict(metodos[[11]],n_p)
  metodos[[12]]<-ets(x,model = "ZZZ", opt.crit="mae", damped = TRUE)
  metodos[[12]]<-predict(metodos[[12]],n_p)
  #ARIMA
  a<-NULL
  metodos[[13]]<-auto.arima(x, ic="aic",approximation=T, trace=FALSE, allowdrift=F)
  a<-predict(metodos[[13]],n_p)
  metodos[[13]]$mean<-a$pred
  metodos[[14]]<-auto.arima(x, ic="bic",approximation=T, trace=FALSE, allowdrift=F)
  a<-predict(metodos[[14]],n_p)
  metodos[[14]]$mean<-a$pred
  #Theta
  metodos[[15]]<-otm.arxiv(x, h=n_p, s= NULL, g= "sAPE")
  metodos[[16]]<-dotm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="Nelder-Mead") #Dynamic Optimised Theta Model
  metodos[[17]]<-dotm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="L-BFGS-B") #Dynamic Optimised Theta Model
  metodos[[18]]<-dotm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="SANN") #Dynamic Optimised Theta Model
  metodos[[19]]<-dstm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="Nelder-Mead") #Dynamic Standard Theta Model
  metodos[[20]]<-dstm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="L-BFGS-B") #Dynamic Standard Theta Model
  metodos[[21]]<-dstm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="SANN") #Dynamic Standard Theta Model
  metodos[[22]]<-otm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="Nelder-Mead")  #Optimised Theta Model
  metodos[[23]]<-otm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="L-BFGS-B")  #Optimised Theta Model
  metodos[[24]]<-otm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="SANN")  #Optimised Theta Model
  metodos[[25]]<-stm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="Nelder-Mead")  #Standard Theta Model (Fiorucci)
  metodos[[26]]<-stm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="L-BFGS-B")  #Standard Theta Model (Fiorucci)
  metodos[[27]]<-stm(x, h=n_p, s= NULL, estimation=TRUE, opt.method="SANN")  #Standard Theta Model (Fiorucci)
  metodos[[28]]<-stheta(x, h=n_p, s= NULL) #Standard Theta Model (Assimakopoulos)
  #STL (seasonal series only)
  metodos[[29]]<-stlf(x,h=n_p, method=c("ets"),robust = TRUE,allow.multiplicative.trend = TRUE)
  metodos[[30]]<-stlf(x,h=n_p, method=c("arima"),robust = TRUE,allow.multiplicative.trend = TRUE)
  metodos[[31]]<-stlf(x,h=n_p, forecastfunction=thetaf,robust = TRUE,allow.multiplicative.trend = TRUE)
  #Croston
  metodos[[32]]<-crost(x,h=n_p,type = "croston",cost = "mar",init.opt=TRUE)
  metodos[[32]]$fitted<-ts(metodos[[32]]$frc.in, start =comienzo,frequency = estac) 
  metodos[[32]]$mean<-  ts(metodos[[32]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[33]]<-crost(x,h=n_p,type = "croston",cost = "msr",init.opt=TRUE)
  metodos[[33]]$fitted<-ts(metodos[[33]]$frc.in, start =comienzo,frequency = estac) 
  metodos[[33]]$mean<-  ts(metodos[[33]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[34]]<-crost(x,h=n_p,type = "croston",cost = "mae",init.opt=TRUE)
  metodos[[34]]$fitted<-ts(metodos[[34]]$frc.in, start =comienzo,frequency = estac)
  metodos[[34]]$mean<-  ts(metodos[[34]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[35]]<-crost(x,h=n_p,type = "croston",cost = "mse",init.opt=TRUE)
  metodos[[35]]$fitted<-ts(metodos[[35]]$frc.in, start =comienzo,frequency = estac)
  metodos[[35]]$mean<-  ts(metodos[[35]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[36]]<-crost(x,h=n_p,type = "sba",cost = "mar",init.opt=TRUE)
  metodos[[36]]$fitted<-ts(metodos[[36]]$frc.in, start =comienzo,frequency = estac)
  metodos[[36]]$mean<-  ts(metodos[[36]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[37]]<-crost(x,h=n_p,type = "sba",cost = "msr",init.opt=TRUE)
  metodos[[37]]$fitted<-ts(metodos[[37]]$frc.in, start =comienzo,frequency = estac)
  metodos[[37]]$mean<-  ts(metodos[[37]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[38]]<-crost(x,h=n_p,type = "sba",cost = "mae",init.opt=TRUE)
  metodos[[38]]$fitted<-ts(metodos[[38]]$frc.in, start =comienzo,frequency = estac)
  metodos[[38]]$mean<-  ts(metodos[[38]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[39]]<-crost(x,h=n_p,type = "sba",cost = "mse",init.opt=TRUE)
  metodos[[39]]$fitted<-ts(metodos[[39]]$frc.in, start =comienzo,frequency = estac)
  metodos[[39]]$mean<-  ts(metodos[[39]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[40]]<-crost(x,h=n_p,type = "sbj",cost = "mar",init.opt=TRUE)
  metodos[[40]]$fitted<-ts(metodos[[40]]$frc.in, start =comienzo,frequency = estac)
  metodos[[40]]$mean<-  ts(metodos[[40]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[41]]<-crost(x,h=n_p,type = "sbj",cost = "msr",init.opt=TRUE)
  metodos[[41]]$fitted<-ts(metodos[[41]]$frc.in, start =comienzo,frequency = estac)
  metodos[[41]]$mean<-  ts(metodos[[41]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[42]]<-crost(x,h=n_p,type = "sbj",cost = "mae",init.opt=TRUE)
  metodos[[42]]$fitted<-ts(metodos[[42]]$frc.in, start =comienzo,frequency = estac)
  metodos[[42]]$mean<-  ts(metodos[[42]]$frc.out, start =comienzo2,frequency = estac) 
  metodos[[43]]<-crost(x,h=n_p,type = "sbj",cost = "mse",init.opt=TRUE)
  metodos[[43]]$fitted<-ts(metodos[[43]]$frc.in, start =comienzo,frequency = estac)
  metodos[[43]]$mean<-  ts(metodos[[43]]$frc.out, start =comienzo2,frequency = estac) 
  #Prophet
  set.seed(50)
  df<-data.frame(ds = seq(as.Date(paste(start(x)[1],"-",start(x)[2],"-","01",sep="")),
                          as.Date(paste(end(x)[1],"-",end(x)[2],"-","01",sep="")),by="m"), y = x)
  metodos[[44]]<-prophet(df,yearly.seasonality=TRUE,
                         weekly.seasonality = FALSE,
                         daily.seasonality = FALSE,seasonality.mode = "additive")
  a<-make_future_dataframe(metodos[[44]],periods = 18,freq = "month")
  b<-predict(metodos[[44]],a)
  metodos[[44]]$fitted <- ts(b$yhat[1:length(x)],start = comienzo,frequency = estac)
  metodos[[44]]$mean <- ts(b$yhat[(length(x)+1):(length(x)+n_p)],start = comienzo2,frequency = estac)
  metodos[[45]]<-prophet(df,yearly.seasonality=TRUE,
                         weekly.seasonality = FALSE,
                         daily.seasonality = FALSE,seasonality.mode = "multiplicative")
  a<-make_future_dataframe(metodos[[45]],periods = 18,freq = "month")
  b<-predict(metodos[[45]],a)
  metodos[[45]]$fitted <- ts(b$yhat[1:length(x)],start = comienzo,frequency = estac)
  metodos[[45]]$mean <- ts(b$yhat[(length(x)+1):(length(x)+n_p)],start = comienzo2,frequency = estac)
  #NNAR: neural network autoregression
  set.seed(50)
  metodos[[46]]<-nnetar(x,p=estac,scale.inputs=T)
  a<-forecast(metodos[[46]],h=n_p)
  metodos[[46]]$mean<-a$mean
  #TBATS model (Exponential smoothing state space model with Box-Cox transformation, ARMA errors, Trend and Seasonal components)
  set.seed(50)
  metodos[[47]]<-tbats(x, biasadj = TRUE)
  metodos[[47]]$fitted<-metodos[[47]]$fitted.values
  a<-forecast(metodos[[47]],h=n_p)
  metodos[[47]]$mean<-a$mean
  #GRNN: general regression neural network 
  set.seed(50)
  metodos[[48]]<-grnn_forecasting(x, h = n_p, transform = "additive")
  a<-rolling_origin(metodos[[48]],h = length(x)-estac-1)
  metodos[[48]]$fitted<-a$predictions[,1]
  metodos[[48]]$mean<-metodos[[48]]$prediction
  metodos[[49]]<-grnn_forecasting(x, h = n_p, transform = "multiplicative")
  a<-rolling_origin(metodos[[49]],h = length(x)-estac-1)
  metodos[[49]]$fitted<-a$predictions[,1]
  metodos[[49]]$mean<-metodos[[49]]$prediction
  #MLP: multilayer perceptron
  # En fitted del 14:n, las primeras n_p+2 son NA
  set.seed(50)
  metodos[[50]]<-mlp.thief(x, h=12)
  # En fitted del 5:n, las primeras 4 son vacías
  metodos[[51]]<-mlp(x, m=12, comb = c("median"),hd = c(10,5))
  a<-forecast(metodos[[51]],h=12)
  metodos[[51]]$mean<-a$mean
  metodos[[52]]<-mlp(x, m=12, comb = c("mean"),hd = c(10,5))
  a<-forecast(metodos[[52]],h=12)
  metodos[[52]]$mean<-a$mean
  
  return(metodos)
}

Calculamos predicciones para cada tipo Letras, en primer lugar calcularemos las de a 3 meses.

3 meses

percentile.5 percentile.50 percentile.95
0.49 0.44 0.56
0.49 0.45 0.53
0.49 0.45 0.57
0.49 0.51 0.62
0.49 0.47 0.58
0.49 0.46 0.56
0.50 0.42 0.55
0.50 0.43 0.56
0.50 0.44 0.57
0.50 0.45 0.63
0.50 0.46 0.67
0.50 0.47 0.63
serie_12 <- c(datos$Valor, result_alPCA$percentile.95)

y=ts((serie_12),
     start=c(2008,10),
     frequency = 12)

ultimo_12 <- tail(y, 12)

# Crear el gráfico utilizando autoplot de ggplot2
ggplot() +
  # Agregar la línea completa en color azul
  geom_line(data = data.frame(tiempo = time(y), y = y), aes(x = tiempo, y = y), color = "#4C72B0") +
  # Agregar solo los últimos 12 puntos en color rojo
  geom_line(data = tail(data.frame(tiempo = time(y), y = y), 12), aes(x = tiempo, y = y), color = "red") +
  # Personalizar el aspecto del gráfico
  theme_minimal() +  # Establecer un tema minimalista
  theme(
    panel.grid.major = element_blank(),  # Eliminar las líneas de la cuadrícula mayor
    panel.grid.minor = element_blank(),  # Eliminar las líneas de la cuadrícula menor
    axis.title = element_text(size = 12),  # Tamaño del texto de los ejes
    plot.title = element_text(size = 16, face = "bold"),  # Tamaño y estilo del título
    plot.caption = element_text(size = 10, hjust = 0.5),  # Tamaño y alineación del pie de página
    legend.title = element_blank(),  # Eliminar el título de la leyenda
    legend.position = "bottom",  # Posicionar la leyenda en la parte inferior
    legend.text = element_text(size = 10)  # Tamaño del texto de la leyenda
  ) +
  labs(
    x = "Tiempo",     # Etiqueta del eje x
    y = "Índice nominal",      # Etiqueta del eje y
    title = "Letras a 3 meses",  # Título del gráfico
     # Texto de la fuente de datos
  )

#### 6 meses Ahora predecimos con las letras de 6 meses.

percentile.5 percentile.50 percentile.95
1 0.88 0.91
1 0.87 0.87
1 0.85 0.83
1 0.85 0.83
1 0.84 0.84
1 0.82 0.74
1 0.80 0.75
1 0.79 0.74
1 0.79 0.74
1 0.79 0.74
1 0.79 0.76
1 0.79 0.78

#### 9 meses A continuación predecimos con las letras de 9 meses.

percentile.5 percentile.50 percentile.95
0.49 0.47 0.37
0.51 0.49 0.39
0.54 0.50 0.39
0.56 0.54 0.42
0.58 0.55 0.42
0.60 0.55 0.41
0.62 0.56 0.42
0.65 0.56 0.43
0.67 0.58 0.45
0.70 0.59 0.45
0.72 0.61 0.46
0.74 0.65 0.48

#### 12 meses Por último en el apartado de letras predecimos las de 12 meses.

percentile.5 percentile.50 percentile.95
3.55 3.39 3.42
3.54 3.38 3.34
3.90 3.51 3.45
4.37 3.68 3.58
4.51 3.70 3.58
4.52 3.70 3.65
4.65 3.77 3.71
4.44 3.65 3.57
4.50 3.67 3.53
4.69 3.70 3.63
4.05 3.55 3.50
4.47 3.69 3.59

#### 3 años Bonos a 3 años

percentile.5 percentile.50 percentile.95
0.6 0.83 1.24
0.6 0.77 1.50
0.6 0.87 2.05
0.6 0.75 1.41
0.6 0.81 1.53
0.6 0.58 1.31
0.6 0.62 1.47
0.6 0.52 1.18
0.6 0.62 1.31
0.6 0.47 1.01
0.6 0.61 1.46
0.6 0.43 0.97

#### 5 años

Bonos a 5 años

percentile.5 percentile.50 percentile.95
2.61 5.08 4.69
2.76 4.06 3.79
2.23 6.16 5.64
2.21 2.01 2.06
2.44 4.62 4.29
2.09 4.44 4.12
1.97 5.16 4.73
2.06 2.72 2.67
1.97 2.76 2.73
2.11 9.88 8.29
2.30 4.62 4.31
2.35 4.12 3.89

#### 10 años

Bonos a 10 años

percentile.5 percentile.50 percentile.95
3.94 4.08 4.05
3.91 4.06 3.95
3.90 4.05 3.92
3.88 4.03 3.85
3.87 4.03 3.83
3.86 4.04 3.84
3.85 4.06 3.93
3.85 4.04 3.95
3.84 4.01 3.85
3.84 4.01 3.86
3.83 4.02 3.79
3.83 4.02 3.81

### Cálculos

Union de predicciones

indices_alPCA<-data.frame(result_alPCA_3meses,result_alPCA_6meses,result_alPCA_9meses,result_alPCA_12meses)

Programa

calcular_neto_corto <- function(retorno_bruto) {
  

  
  # Calcula la comisión bancaria
  if (retorno_bruto <= 600) {
    Comision_banco <- 0.9
  } else {
    Comision_banco <- retorno_bruto * 0.0015
  }
  
  # Calcula el neto después de impuestos y comisiones
  neto <- retorno_bruto - Comision_banco
  
  # Retorna el valor neto
  return(neto)
}
beneficio<-0
beneficio_total <- 0
inversion<-1000000
inversion_inicial<-1000000
posiciones <- c(1, 4, 7, 10)


resultados_3meses <- data.frame(Beneficio_Total = numeric(),Beneficio_operacion = numeric(),Beneficio_residual=numeric() )


# Iteramos sobre las columnas del dataframe
for (i in posiciones) {
    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion<-indices_alPCA[i, 1]/100 * inversion * 3/12
    beneficio_neto<-calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio+beneficio_neto
    beneficio_total<-beneficio_total+beneficio_neto
    
   
    while (beneficio / 1000 >= 1) {
        
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
        
    }
    
     resultados_3meses<-data.frame( Beneficio_Total_Acumulado = beneficio_total,Beneficio_Operacion_Neto = beneficio_neto)

}

resultados_3meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
5901.734 1578.928
tres_meses<-resultados_3meses[nrow(resultados_3meses), 1]
beneficio <- 0
beneficio_total <- 0
inversion <- 1000000
inversion_inicial <- 1000000
posiciones <- c(1, 7)

resultados_6meses <- data.frame(Beneficio_Total_Acumulado = numeric(), Beneficio_Operacion_Neto = numeric(), Beneficio_residual = numeric())

# Iteramos sobre las posiciones específicas
for (i in posiciones) {
    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion <- indices_alPCA[i, 2] / 100 * inversion * 6 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_6meses <-  data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto)
    if (nrow(resultados_6meses) == 2) break
}

resultados_6meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
8302.528 3759.352
seis_meses<-resultados_6meses[nrow(resultados_6meses), 1]
beneficio <- 0
beneficio_total <- 0
inversion <- 1000000
inversion_inicial <- 1000000


resultados_9_3_meses <- data.frame(Beneficio_Total_Acumulado = numeric(), Beneficio_Operacion_Neto = numeric(), Beneficio_residual = numeric())

# Iteramos sobre las posiciones específicas

    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion <- indices_alPCA[1, 3] / 100 * inversion * 9 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_9_3_meses <- rbind(resultados_9_3_meses, data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto))
    
 beneficio_operacion <- indices_alPCA[10, 1] / 100 * inversion * 3 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_9_3_meses <- rbind(resultados_9_3_meses, data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto))
    


resultados_9_3_meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
2770.838 2770.838
4346.620 1575.783
nueve_tres_meses<-resultados_9_3_meses[nrow(resultados_9_3_meses), 1]
beneficio <- 0
beneficio_total <- 0
inversion <- 1000000
inversion_inicial <- 1000000


resultados_3_9_meses <- data.frame(Beneficio_Total_Acumulado = numeric(), Beneficio_Operacion_Neto = numeric(), Beneficio_residual = numeric())

# Iteramos sobre las posiciones específicas

    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion <- indices_alPCA[1, 1] / 100 * inversion * 3 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_3_9_meses <- rbind(resultados_3_9_meses, data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto, Beneficio_residual = beneficio))
    
 beneficio_operacion <- indices_alPCA[4, 3] / 100 * inversion * 9 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_3_9_meses <-  data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto)
    


resultados_3_9_meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
4546.32 3148.42
tres_nueve_meses<-resultados_3_9_meses[nrow(resultados_3_9_meses), 1]
beneficio <- 0
beneficio_total <- 0
inversion <- 1000000
inversion_inicial <- 1000000


resultados_12meses <- data.frame(Beneficio_Total_Acumulado = numeric(), Beneficio_Operacion_Neto = numeric(), Beneficio_residual = numeric())

# Iteramos sobre las posiciones específicas

    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion <- indices_alPCA[1, 4] / 100 * inversion 
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto
    
resultados_12meses <- data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto)
    


resultados_12meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
34148.7 34148.7
doce_meses<-resultados_12meses[nrow(resultados_12meses), 1]
# Crear un vector con los valores de las variables
rentabilidades <- c(tres_meses, seis_meses, tres_nueve_meses, nueve_tres_meses, doce_meses)

# Crear un vector con los nombres de las variables
nombres_variables <- c("4 inversiones a 3 meses", "2 inversiones a 6 meses", "1 inversión a 3 meses y otra a 9 meses", "1 inversión a 9 meses y otra a 3 meses", "1 inversión a 12 meses ")

# Ordenar los valores de las rentabilidades de mayor a menor
ranking <- sort(rentabilidades, decreasing = TRUE)

# Imprimir el ranking
cat("Ranking de rentabilidad:\n")
## Ranking de rentabilidad:
for (i in 1:length(ranking)) {
    cat(i, ": ", nombres_variables[which(rentabilidades == ranking[i])], " - ", format(ranking[i], big.mark = ".", decimal.mark = ",", nsmall = 2), "€\n")
}
## 1 :  1 inversión a 12 meses   -  34.148,70 €
## 2 :  2 inversiones a 6 meses  -  8.302,528 €
## 3 :  4 inversiones a 3 meses  -  5.901,734 €
## 4 :  1 inversión a 3 meses y otra a 9 meses  -  4.546,32 €
## 5 :  1 inversión a 9 meses y otra a 3 meses  -  4.346,62 €
ranking <- sort(rentabilidades, decreasing = FALSE)


nombres_variables <- c("1 inversión a 12 meses ","1 inversión a 3 meses y otra a 9", "2 inversiones a 6 meses","4 inversiones a 3 meses", "1 inversión a 9 meses y otra a 3")

colores_azules <- colorRampPalette(c("#00688B", "#009ACD", "#00BFFF", "lightblue", "darkslategray1"))(length(ranking))

colores_azules_2 <- colorRampPalette(c("darkslategray1", "lightblue", "#00BFFF", "#009ACD", "#00688B"))(length(ranking))
par(mar = c(5, 4, 4, 5))  



bp <-barplot(ranking, 
        names.arg = rep("", length(ranking)), 
        horiz = TRUE, 
        col = colores_azules, 
        xlab = "Beneficio neto", 
        ylab = "Inversiones",
        main = "Beneficios de las inversiones según alPCA",
        las = 1,
         xlim = c(0, max(ranking) * 1.98)) 

valores_formateados <- sprintf("%.2f €", round(ranking, 2))

text(ranking, bp, labels = valores_formateados, pos = 4, cex = 0.8, col = "black")

legend(x = max(ranking) * 1.3, y = max(bp), legend = nombres_variables, fill = colores_azules_2, title = "Inversiones", xpd = TRUE)

5.2.2.2 Modelos de Forecastcomb

3 meses

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X3_meses" , ]
datos <- datos[94:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2008,10),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2011,1),end=c(2020,5))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=46,drift=TRUE) 

predicciones_deriva<-deriva$mean

###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 


predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   

predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 46, level = 95)   


predicciones_holt<-holt$mean

###ARIMA nuevas

auto.arima(y)
## Series: y 
## ARIMA(0,1,1)(0,0,1)[12] 
## 
## Coefficients:
##           ma1    sma1
##       -0.2033  0.2327
## s.e.   0.0783  0.0736
## 
## sigma^2 = 0.1786:  log likelihood = -102.53
## AIC=211.06   AICc=211.19   BIC=220.72
actividad_arima<-Arima(y,
                       order=c(0,1,1),  # arma estacional diferenciacion estacional
                       seasonal =c(0,0,1),  # baja cada 4= decimos que es estacional
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean


###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(0,1,1)(0,0,1)[12] 
## 
## Coefficients:
##           ma1    sma1
##       -0.2033  0.2327
## s.e.   0.0783  0.0736
## 
## sigma^2 = 0.1786:  log likelihood = -102.53
## AIC=211.06   AICc=211.19   BIC=220.72
actividad_arima<-Arima(f,
                       order=c(0,1,1),  # arma estacional diferenciacion estacional
                       seasonal =c(0,0,1),  # baja cada 4= decimos que es estacional
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=46)

predicciones_arima<-arima_forecast$mean

#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)

# Generar pronósticos
pronosticos <- forecast(modelo, h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)
accuracy(modelo) #para los errores
##                         ME      RMSE        MAE       MPE     MAPE      MASE
## Training set -0.0006989074 0.1074329 0.07578087 -23.91274 49.57177 0.3987012
##                   ACF1
## Training set -0.105779
# Generar pronósticos
pronosticos <- forecast(modelo, h=46)


# Imprimir los pronósticos

predicciones_redes<-pronosticos$mean

###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)

predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024


# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=46)

predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)
## Warning: package 'ForecastComb' was built under R version 4.2.3
citation('ForecastComb')
## 
## To cite package 'ForecastComb' in publications use:
## 
##   Weiss CE, Roetzer GR, Raviv E (2018). _ForecastComb: Forecast
##   Combination Methods_. R package version 1.3.1,
##   <https://CRAN.R-project.org/package=ForecastComb>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {ForecastComb: Forecast Combination Methods},
##     author = {Christoph E. Weiss and Gernot R. Roetzer and Eran Raviv},
##     year = {2018},
##     note = {R package version 1.3.1},
##     url = {https://CRAN.R-project.org/package=ForecastComb},
##   }
## 
## ATTENTION: This citation information has been auto-generated from the
## package DESCRIPTION file and may need manual editing, see
## 'help("citation")'.
newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 46  5
preds<-matrix(preds,46,5)

obs<-window(y,start=c(2020,6), end=c(2024,3))
length(obs)
## [1] 46
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:40]
train_p <- preds[1:40,]
test_o <- obs[41:46]
test_p <- preds[41:46,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test

# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test 

indices_tres_meses<-newpreds%*%modelo_InvW$Weights #######################  guardamos índice

# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test
predict(modelo_MED,newpreds)
##  [1] 3.137445 3.138417 3.139535 3.140794 3.142190 3.143718 3.145373 3.147153
##  [9] 3.149052 3.151067 3.153194 3.155431
## attr(,"tsp")
## [1] 2024.250 2025.167   12.000
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0.4
accuracy_TA <- modelo_TA$Accuracy_Test

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.5
accuracy_WA <- modelo_WA$Accuracy_Test

6 meses

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X6_meses" , ]
datos <- datos[95:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2008,11),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2011,1),end=c(2020,11))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=41,drift=TRUE) 

predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 

predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   


predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 41, level = 95)

predicciones_holt<-holt$mean


auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1574:  log likelihood = -90.96
## AIC=183.92   AICc=183.94   BIC=187.14
actividad_arima<-Arima(y,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1574:  log likelihood = -90.96
## AIC=183.92   AICc=183.94   BIC=187.14
actividad_arima<-Arima(f,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=41)

predicciones_arima<-arima_forecast$mean
#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)


# Generar pronósticos
pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)


# Generar pronósticos
pronosticos <- forecast(modelo, h=41)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)


predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=41)



predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)

preds<-matrix(preds,41,5)


obs<-window(y,start=c(2020,11), end=c(2024,3))

# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:35]
length(train_o)
## [1] 35
train_p <- preds[1:35,]
test_o <- obs[36:41]
test_p <- preds[36:41,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
## Training set prediction matrix is not full rank. Algorithm to remove linearly dependent models started.
## The input matrix is not full rank. The indices of the linearly dependent models are: 1, 2, 3
## Of these models, model 2 had the highest RMSE and was removed.
## Checking if the revised matrix is full rank.
## The revised matrix is now full rank.
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test

# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test 

# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test###################
indices_seis_meses<-modelo_MED$Predict(modelo_MED,newpreds = newpreds)

# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_TA <- modelo_TA$Accuracy_Test###################

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.1
accuracy_WA <- modelo_WA$Accuracy_Test

9 meses

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos <- datos[datos$Periodo == "X9_meses" , ]
datos <- datos[146:nrow(datos), ]

y=ts((datos$Valor),
     start=c(2013,2),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2013,2),c(2020,11))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=41,drift=TRUE) 
predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 
predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   
predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 41, level = 95)   

predicciones_holt<-holt$mean

###ARIMA nuevas

auto.arima(y)
## Series: y 
## ARIMA(0,2,1) 
## 
## Coefficients:
##           ma1
##       -0.8463
## s.e.   0.0592
## 
## sigma^2 = 0.02679:  log likelihood = 51.48
## AIC=-98.97   AICc=-98.88   BIC=-93.2
actividad_arima<-Arima(y,
                       order=c(0,1,1), 
                      seasonal =c(0,0,1), 
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=12)

predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(f)
## Series: f 
## ARIMA(0,1,2) with drift 
## 
## Coefficients:
##           ma1      ma2    drift
##       -0.2812  -0.2313  -0.0176
## s.e.   0.0993   0.0925   0.0060
## 
## sigma^2 = 0.01429:  log likelihood = 66.99
## AIC=-125.98   AICc=-125.53   BIC=-115.85
actividad_arima<-Arima(f,
                       order=c(0,1,1), 
                      seasonal =c(0,0,1), 
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=41)

predicciones_arima<-arima_forecast$mean

#Redes neuronales  nuevas

modelo <- nnetar(y)

pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)

pronosticos <- forecast(modelo, h=41)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

modelo_dot <- dotm(y , h=12)

predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

modelo_dot <- dotm(f , h=41)

predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
preds<-matrix(preds,41,5)

obs<-window(y,start=c(2020,11), end=c(2024,3))

# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:35]
length(train_o)
## [1] 35
train_p <- preds[1:35,]
test_o <- obs[36:41]
test_p <- preds[36:41,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Tes;accuracy_EIG2
##                  ME      RMSE       MAE       MPE     MAPE
## Test set -0.9121069 0.9962732 0.9121069 -25.64722 25.64722
newpreds%*%modelo$Weights
##              [,1]
##  [1,]   2.8437909
##  [2,]   2.1796053
##  [3,]   1.2404066
##  [4,]  -0.1543918
##  [5,]  -1.3923615
##  [6,]  -2.7535799
##  [7,]  -4.3587457
##  [8,]  -5.9394642
##  [9,]  -7.4064050
## [10,]  -8.9797760
## [11,] -10.6659590
## [12,] -12.4860437
# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test;accuracy_BG
##                ME     RMSE      MAE      MPE     MAPE
## Test set 4.507365 4.508774 4.507365 125.0534 125.0534
# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test;accuracy_InvW 
##               ME     RMSE     MAE      MPE     MAPE
## Test set 4.35519 4.356745 4.35519 120.8242 120.8242
indices_nueve_meses<-newpreds%*%modelo_InvW$Weights

# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test;accuracy_MED
##                ME     RMSE      MAE      MPE     MAPE
## Test set 4.877956 4.879064 4.877956 135.3531 135.3531
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test;accuracy_SA
##                ME     RMSE      MAE      MPE     MAPE
## Test set 4.575235 4.576582 4.575235 126.9397 126.9397
# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_TA <- modelo_TA$Accuracy_Test;accuracy_TA
##                ME     RMSE      MAE      MPE     MAPE
## Test set 4.575235 4.576582 4.575235 126.9397 126.9397
# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_WA <- modelo_WA$Accuracy_Test;accuracy_WA
##                ME     RMSE      MAE      MPE     MAPE
## Test set 4.575235 4.576582 4.575235 126.9397 126.9397

12 meses

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos <- datos[datos$Periodo == "X12_meses" , ]
datos <- datos[1:nrow(datos), ]

y=ts((datos$Valor),
     start=c(2001,1),
     frequency = 12)
autoplot(y)

f<-window(y,end=c(2014,1))


###Método de la deriva 2020-2024

deriva<-rwf(f,h=123,drift=TRUE) 
predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 
predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   
predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 123, level = 95)   

predicciones_holt<-holt$mean

###ARIMA nuevas

auto.arima(y)
## Series: y 
## ARIMA(3,1,3)(0,0,2)[12] 
## 
## Coefficients:
##           ar1     ar2     ar3     ma1     ma2      ma3    sma1    sma2
##       -0.4979  0.0513  0.3097  0.6469  0.0614  -0.4913  0.1218  0.0810
## s.e.   0.4235  0.4942  0.2770  0.4113  0.5353   0.3501  0.0641  0.0558
## 
## sigma^2 = 0.1022:  log likelihood = -73.78
## AIC=165.56   AICc=166.23   BIC=198.21
actividad_arima<-Arima(y,
                       order=c(3,1,3), 
                      seasonal =c(0,0,2), 
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=12)

predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(f)
## Series: f 
## ARIMA(0,1,0)(0,0,1)[12] 
## 
## Coefficients:
##         sma1
##       0.1391
## s.e.  0.0722
## 
## sigma^2 = 0.1741:  log likelihood = -84.61
## AIC=173.22   AICc=173.3   BIC=179.32
actividad_arima<-Arima(f,
                       order=c(3,1,3), 
                      seasonal =c(0,0,2), 
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=123)

predicciones_arima<-arima_forecast$mean

#Redes neuronales  nuevas

modelo <- nnetar(y)

pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)

pronosticos <- forecast(modelo, h=123)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

modelo_dot <- dotm(y , h=12)

predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

modelo_dot <- dotm(f , h=123)

predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 123   5
preds<-matrix(preds,123,5)

obs<-window(y,start=c(2014,1), end=c(2024,3))
length(obs)
## [1] 123
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:100]
length(train_o)
## [1] 100
train_p <- preds[1:100,]
test_o <- obs[101:123]
test_p <- preds[101:123,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Tes;accuracy_EIG2
##                ME     RMSE      MAE      MPE     MAPE
## Test set 5.644442 5.966122 5.644442 286.3536 286.3536
newpreds%*%modelo$Weights
##             [,1]
##  [1,]  107816689
##  [2,]   32195061
##  [3,]  -46190975
##  [4,]  -54351409
##  [5,]  -33451643
##  [6,]  -64393511
##  [7,]  -59748611
##  [8,]   -8079544
##  [9,] -127280086
## [10,]  -99677470
## [11,]  -75037102
## [12,] -152427201
# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test;accuracy_BG
##                ME     RMSE      MAE      MPE     MAPE
## Test set 4.091971 4.283664 4.091971 223.3733 223.3733
indices_doce_meses<-newpreds%*%modelo_BG$Weights

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test;accuracy_InvW 
##                ME     RMSE      MAE     MPE    MAPE
## Test set 3.727384 3.943813 3.727384 180.659 180.659
# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test;accuracy_MED
##                ME     RMSE      MAE      MPE     MAPE
## Test set 2.734071 2.998076 2.734814 95.79174 96.62703
predict(modelo_MED,newpreds)
##  [1] 3.507592 3.505201 3.599402 3.615292 3.498027 3.495635 3.493244 3.484849
##  [9] 3.488461 3.478561 3.475417 3.481287
## attr(,"tsp")
## [1] 2024.250 2025.167   12.000
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test;accuracy_SA
##                ME     RMSE     MAE      MPE     MAPE
## Test set 3.020494 3.294187 3.02953 102.6736 112.8265
# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0.2
accuracy_TA <- modelo_TA$Accuracy_Test;accuracy_TA
##               ME     RMSE     MAE      MPE     MAPE
## Test set 3.09407 3.327819 3.09407 131.3187 131.3187
# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.25
accuracy_WA <- modelo_WA$Accuracy_Test;accuracy_WA
##               ME     RMSE     MAE      MPE     MAPE
## Test set 3.16607 3.394527 3.16607 138.4241 138.4241

Cálculos

indices<-cbind( indices_tres_meses,indices_seis_meses, indices_nueve_meses, indices_doce_meses)
indices<-data.frame(indices) ###poner nombres a las columnas 
colnames(indices) <- c("Tres meses", "Seis meses", "Nueve meses", "Doce meses")
beneficio<-0
beneficio_total <- 0
inversion<-1000000
inversion_inicial<-1000000
posiciones <- c(1, 4, 7, 10)


resultados_3meses <- data.frame(Beneficio_Total = numeric(),Beneficio_operacion = numeric(),Beneficio_residual=numeric() )


# Iteramos sobre las columnas del dataframe
for (i in posiciones) {
    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion<-indices[i, 1]/100 * inversion * 3/12
    beneficio_neto<-calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio+beneficio_neto
    beneficio_total<-beneficio_total+beneficio_neto
    
   
    while (beneficio / 1000 >= 1) {
        
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
        
    }
    
     resultados_3meses<- data.frame( Beneficio_Total_Acumulado = beneficio_total,Beneficio_Operacion_Neto = beneficio_neto)

}

resultados_3meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
31071.66 7967.01
tres_meses<-resultados_3meses[nrow(resultados_3meses), 1]
beneficio <- 0
beneficio_total <- 0
inversion <- 1000000
inversion_inicial <- 1000000
posiciones <- c(1, 7)

resultados_6meses <- data.frame(Beneficio_Total_Acumulado = numeric(), Beneficio_Operacion_Neto = numeric(), Beneficio_residual = numeric())

# Iteramos sobre las posiciones específicas
for (i in posiciones) {
    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion <- indices[i, 2] / 100 * inversion * 6 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_6meses <-data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto)
    if (nrow(resultados_6meses) == 2) break
}

resultados_6meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
25629.03 12804.87
seis_meses<-resultados_6meses[nrow(resultados_6meses), 1]
beneficio <- 0
beneficio_total <- 0
inversion <- 1000000
inversion_inicial <- 1000000


resultados_9_3_meses <- data.frame(Beneficio_Total_Acumulado = numeric(), Beneficio_Operacion_Neto = numeric())

# Iteramos sobre las posiciones específicas

    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion <- indices[1, 3] / 100 * inversion * 9 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_9_3_meses <-data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto)
    
 beneficio_operacion <- indices[10, 1] / 100 * inversion * 3 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_9_3_meses <- rbind(resultados_9_3_meses, data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto))
    


resultados_9_3_meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
26670.38 26670.380
34660.75 7990.374
nueve_tres_meses<-resultados_9_3_meses[nrow(resultados_9_3_meses), 1]
beneficio <- 0
beneficio_total <- 0
inversion <- 1000000
inversion_inicial <- 1000000


resultados_3_9_meses <- data.frame(Beneficio_Total_Acumulado = numeric(), Beneficio_Operacion_Neto = numeric())

# Iteramos sobre las posiciones específicas

    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion <- indices[1, 1] / 100 * inversion * 3 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_3_9_meses <-data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto)
    
 beneficio_operacion <- indices[4, 3] / 100 * inversion * 9 / 12
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto

    while (beneficio / 1000 >= 1) {
        inversion <- inversion + 1000
        beneficio <- beneficio - 1000
    }

    resultados_3_9_meses <- rbind(resultados_3_9_meses, data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto))
    


resultados_3_9_meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
7749.481 7749.481
33685.340 25935.860
tres_nueve_meses<-resultados_3_9_meses[nrow(resultados_3_9_meses), 1]
beneficio <- 0
beneficio_total <- 0
inversion <- 1000000
inversion_inicial <- 1000000


resultados_12meses <- data.frame(Beneficio_Total_Acumulado = numeric(), Beneficio_Operacion_Neto = numeric())

# Iteramos sobre las posiciones específicas

    # Obtenemos el beneficio multiplicando el primer elemento de la columna por el valor introducido por el usuario
    beneficio_operacion <- indices[1, 4] / 100 * inversion 
    beneficio_neto <- calcular_neto_corto(beneficio_operacion) 
    beneficio <- beneficio + beneficio_neto
    beneficio_total <- beneficio_total + beneficio_neto
    
resultados_12meses <- data.frame(Beneficio_Total_Acumulado = beneficio_total, Beneficio_Operacion_Neto = beneficio_neto)
    


resultados_12meses
Beneficio_Total_Acumulado Beneficio_Operacion_Neto
33851.93 33851.93
doce_meses<-resultados_12meses[nrow(resultados_12meses), 1]
# Crear un vector con los valores de las variables
rentabilidades <- c(tres_meses, seis_meses, tres_nueve_meses, nueve_tres_meses, doce_meses)

# Crear un vector con los nombres de las variables
nombres_variables <- c("4 inversiones a 3 meses", "2 inversiones a 6 meses", "1 inversión a 3 meses y otra a 9", "1 inversión a 9 meses y otra a 3", "1 inversión a 12 meses ")

# Ordenar los valores de las rentabilidades de mayor a menor
ranking <- sort(rentabilidades, decreasing = TRUE)

# Imprimir el ranking
cat("Ranking de rentabilidad:\n")
## Ranking de rentabilidad:
for (i in 1:length(ranking)) {
    cat(i, ": ", nombres_variables[which(rentabilidades == ranking[i])], " - ", format(ranking[i], big.mark = ".", decimal.mark = ",", nsmall = 2), "€\n")
}
## 1 :  1 inversión a 9 meses y otra a 3  -  34.660,75 €
## 2 :  1 inversión a 12 meses   -  33.851,93 €
## 3 :  1 inversión a 3 meses y otra a 9  -  33.685,34 €
## 4 :  4 inversiones a 3 meses  -  31.071,66 €
## 5 :  2 inversiones a 6 meses  -  25.629,03 €
ranking <- sort(rentabilidades, decreasing = FALSE)
nombres_variables <- c("1 inversión a 9 meses y otra a 3", "1 inversión a 12 meses ","1 inversión a 3 meses y otra a 9","4 inversiones a 3 meses", "2 inversiones a 6 meses")

colores_azules <- colorRampPalette(c("#00688B", "#009ACD", "#00BFFF", "lightblue", "darkslategray1"))(length(ranking))

colores_azules_2 <- colorRampPalette(c("darkslategray1", "lightblue", "#00BFFF", "#009ACD", "#00688B"))(length(ranking))
par(mar = c(5, 4, 4, 5))  



bp <-barplot(ranking, 
        names.arg = rep("", length(ranking)), 
        horiz = TRUE, 
        col = colores_azules, 
        xlab = "Beneficio neto", 
        ylab = "Inversiones",
        main = "Beneficios de las inversiones según Forecastcomb",
        las = 1,
         xlim = c(0, max(ranking) * 1.98)) 

valores_formateados <- sprintf("%.2f €", round(ranking, 2))

text(ranking, bp, labels = valores_formateados, pos = 4, cex = 0.8, col = "black")

legend(x = max(ranking) * 1.3, y = max(bp), legend = nombres_variables, fill = colores_azules_2, title = "Inversiones", xpd = TRUE)

####3 años

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X3_anos" , ]
datos <- datos[95:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2008,11),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2011,1),end=c(2020,11))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=41,drift=TRUE) 

predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 

predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   


predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 41, level = 95)

predicciones_holt<-holt$mean


auto.arima(y)
## Series: y 
## ARIMA(1,1,1)(0,0,1)[12] 
## 
## Coefficients:
##           ar1     ma1    sma1
##       -0.9090  0.7127  0.1237
## s.e.   0.0578  0.1012  0.0809
## 
## sigma^2 = 0.1559:  log likelihood = -88.93
## AIC=185.86   AICc=186.08   BIC=198.72
actividad_arima<-Arima(y,
                       order=c(0,1,0),  
                       include.drift=FALSE)


arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(1,1,1)(0,0,1)[12] 
## 
## Coefficients:
##           ar1     ma1    sma1
##       -0.9090  0.7127  0.1237
## s.e.   0.0578  0.1012  0.0809
## 
## sigma^2 = 0.1559:  log likelihood = -88.93
## AIC=185.86   AICc=186.08   BIC=198.72
actividad_arima<-Arima(f,
                       order=c(0,1,0),  
                       include.drift=FALSE)


arima_forecast<-forecast(actividad_arima, h=41)

predicciones_arima<-arima_forecast$mean
#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)


# Generar pronósticos
pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)


# Generar pronósticos
pronosticos <- forecast(modelo, h=41)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)


predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=41)



predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 41  5
preds<-matrix(preds,41,5)


obs<-window(y,start=c(2020,11), end=c(2024,3))

# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:35]
length(train_o)
## [1] 35
train_p <- preds[1:35,]
test_o <- obs[36:41]
test_p <- preds[36:41,]
# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
## Training set prediction matrix is not full rank. Algorithm to remove linearly dependent models started.
## The input matrix is not full rank. The indices of the linearly dependent models are: 1, 2, 3
## Of these models, model 3 had the highest RMSE and was removed.
## Checking if the revised matrix is full rank.
## The revised matrix is now full rank.
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test

# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test
newpreds[,-1]%*%modelo_BG$Weights
##           [,1]
##  [1,] 2.866441
##  [2,] 3.430955
##  [3,] 4.410895
##  [4,] 3.215096
##  [5,] 3.342913
##  [6,] 3.302461
##  [7,] 3.617683
##  [8,] 3.131463
##  [9,] 3.267583
## [10,] 2.912604
## [11,] 3.531205
## [12,] 2.818358
# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test 
indices_tres_anos<-newpreds[,-3]%*%modelo_InvW$Weights

# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0.25
accuracy_TA <- modelo_TA$Accuracy_Test###################

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.34
accuracy_WA <- modelo_WA$Accuracy_Test

####5 años

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X5_anos" , ]
datos <- datos[94:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2008,10),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2011,1),end=c(2020,11))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=41,drift=TRUE) 

predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 

predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   


predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 41, level = 95)

predicciones_holt<-holt$mean


auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1232:  log likelihood = -68.84
## AIC=139.68   AICc=139.7   BIC=142.9
actividad_arima<-Arima(y,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1232:  log likelihood = -68.84
## AIC=139.68   AICc=139.7   BIC=142.9
actividad_arima<-Arima(f,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=41)

predicciones_arima<-arima_forecast$mean
#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)


# Generar pronósticos
pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)


# Generar pronósticos
pronosticos <- forecast(modelo, h=41)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)


predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=41)
predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 41  5
preds<-matrix(preds,41,5)

obs<-window(y,start=c(2020,11), end=c(2024,3))

# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:35]
length(train_o)
## [1] 35
train_p <- preds[1:35,]
test_o <- obs[36:41]
test_p <- preds[36:41,]
# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
## Training set prediction matrix is not full rank. Algorithm to remove linearly dependent models started.
## The input matrix is not full rank. The indices of the linearly dependent models are: 1, 2, 3
## Of these models, model 1 had the highest RMSE and was removed.
## Checking if the revised matrix is full rank.
## The revised matrix is now full rank.
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test

# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test #########

indices_cinco_anos<-newpreds[,-1]%*%modelo_InvW$Weights
# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Tes

# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0.25
accuracy_TA <- modelo_TA$Accuracy_Test

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.34
accuracy_WA <- modelo_WA$Accuracy_Test

####10 años

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X10_anos" , ]
datos <- datos[98:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2009,2),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2011,1),end=c(2021,2))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=38,drift=TRUE) 

predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 

predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   


predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 38, level = 95)

predicciones_holt<-holt$mean


auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1147:  log likelihood = -60.87
## AIC=123.75   AICc=123.77   BIC=126.95
actividad_arima<-Arima(y,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1147:  log likelihood = -60.87
## AIC=123.75   AICc=123.77   BIC=126.95
actividad_arima<-Arima(f,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=38)

predicciones_arima<-arima_forecast$mean
#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)


# Generar pronósticos
pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)


# Generar pronósticos
pronosticos <- forecast(modelo, h=38)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)


predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=38)



predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 38  5
preds<-matrix(preds,38,5)


obs<-window(y,start=c(2021,2), end=c(2024,3))
length(obs)
## [1] 38
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:33]
length(train_o)
## [1] 33
train_p <- preds[1:33,]
test_o <- obs[34:38]
test_p <- preds[34:38,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
## Training set prediction matrix is not full rank. Algorithm to remove linearly dependent models started.
## The input matrix is not full rank. The indices of the linearly dependent models are: 1, 2, 3
## Of these models, model 3 had the highest RMSE and was removed.
## Checking if the revised matrix is full rank.
## The revised matrix is now full rank.
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test
indices_diez_anos<-modelo$Predict(modelo,newpreds = newpreds[,-3])
# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test 
newpreds[,-1]%*%modelo_InvW$Weights
##           [,1]
##  [1,] 4.582563
##  [2,] 4.472874
##  [3,] 4.620003
##  [4,] 4.571841
##  [5,] 4.564057
##  [6,] 4.636753
##  [7,] 4.731881
##  [8,] 4.891689
##  [9,] 4.895124
## [10,] 4.986868
## [11,] 4.854235
## [12,] 4.840904
# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test


# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_TA <- modelo_TA$Accuracy_Tes

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_WA <- modelo_WA$Accuracy_Test

Predicciones para calcular el MAPE con Forecastcomb

3 meses

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X3_meses" , ]
datos <- datos[94:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2008,10),
     frequency = 12)
f<-window(y,start=c(2011,1),end=c(2020,5))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=35,drift=TRUE) 

predicciones_deriva<-deriva$mean

###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 


predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   

predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 35, level = 95)   


predicciones_holt<-holt$mean

###ARIMA nuevas

auto.arima(y)
## Series: y 
## ARIMA(0,1,1)(0,0,1)[12] 
## 
## Coefficients:
##           ma1    sma1
##       -0.2033  0.2327
## s.e.   0.0783  0.0736
## 
## sigma^2 = 0.1786:  log likelihood = -102.53
## AIC=211.06   AICc=211.19   BIC=220.72
actividad_arima<-Arima(y,
                       order=c(0,1,1),  # arma estacional diferenciacion estacional
                       seasonal =c(0,0,1),  # baja cada 4= decimos que es estacional
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean


###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(0,1,1)(0,0,1)[12] 
## 
## Coefficients:
##           ma1    sma1
##       -0.2033  0.2327
## s.e.   0.0783  0.0736
## 
## sigma^2 = 0.1786:  log likelihood = -102.53
## AIC=211.06   AICc=211.19   BIC=220.72
actividad_arima<-Arima(f,
                       order=c(0,1,1),  # arma estacional diferenciacion estacional
                       seasonal =c(0,0,1),  # baja cada 4= decimos que es estacional
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=35)

predicciones_arima<-arima_forecast$mean

#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)

# Generar pronósticos
pronosticos <- forecast(modelo, h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)
accuracy(modelo) #para los errores
##                         ME      RMSE        MAE       MPE     MAPE      MASE
## Training set -0.0001002032 0.1108833 0.07729607 -23.77392 49.89867 0.4066731
##                    ACF1
## Training set -0.1172348
# Generar pronósticos
pronosticos <- forecast(modelo, h=35)


# Imprimir los pronósticos

predicciones_redes<-pronosticos$mean

###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)

predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024


# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=35)

predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)
citation('ForecastComb')
## 
## To cite package 'ForecastComb' in publications use:
## 
##   Weiss CE, Roetzer GR, Raviv E (2018). _ForecastComb: Forecast
##   Combination Methods_. R package version 1.3.1,
##   <https://CRAN.R-project.org/package=ForecastComb>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {ForecastComb: Forecast Combination Methods},
##     author = {Christoph E. Weiss and Gernot R. Roetzer and Eran Raviv},
##     year = {2018},
##     note = {R package version 1.3.1},
##     url = {https://CRAN.R-project.org/package=ForecastComb},
##   }
## 
## ATTENTION: This citation information has been auto-generated from the
## package DESCRIPTION file and may need manual editing, see
## 'help("citation")'.
newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 35  5
preds<-matrix(preds,35,5)

obs<-window(y,start=c(2020,6), end=c(2023,4))
length(obs)
## [1] 35
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:29]
train_p <- preds[1:29,]
test_o <- obs[30:35]
test_p <- preds[30:35,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
# Evaluating all the forecast combination methods and returning the best.
# If necessary, it uses the built-in automated parameter optimisation methods
# for the different methods.

modelo<-comb_EIG2(data)
modelo$Accuracy_Test
##                ME     RMSE      MAE     MPE    MAPE
## Test set 2.266546 2.320136 2.266546 554.054 554.054
modelo$Predict(modelo,newpreds=newpreds)
##  [1] 57.11325 56.35855 56.22684 56.74451 56.57322 56.87752 57.31191 57.63254
##  [9] 58.27276 60.04296 61.18425 62.66472
#comb_EIG4(data, ntop_pred = 2, criterion = 'MAE')
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test

# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test 

# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test
predict(modelo_MED,newpreds)
##  [1] 3.137445 3.138417 3.139535 3.140794 3.142190 3.143718 3.145373 3.147153
##  [9] 3.149052 3.151067 3.153194 3.155431
## attr(,"tsp")
## [1] 2024.250 2025.167   12.000
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_TA <- modelo_TA$Accuracy_Test

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.12
accuracy_WA <- modelo_WA$Accuracy_Test

6 meses

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X6_meses" , ]
datos <- datos[95:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2008,11),
     frequency = 12)
f<-window(y,start=c(2011,1),end=c(2020,11))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=30,drift=TRUE) 

predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 

predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   


predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 30, level = 95)

predicciones_holt<-holt$mean


auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1574:  log likelihood = -90.96
## AIC=183.92   AICc=183.94   BIC=187.14
actividad_arima<-Arima(y,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1574:  log likelihood = -90.96
## AIC=183.92   AICc=183.94   BIC=187.14
actividad_arima<-Arima(f,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=30)

predicciones_arima<-arima_forecast$mean
#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)


# Generar pronósticos
pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)


# Generar pronósticos
pronosticos <- forecast(modelo, h=30)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)


predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=30)



predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 30  5
preds<-matrix(preds,30,5)


obs<-window(y,start=c(2020,11), end=c(2023,4))
length(obs)
## [1] 30
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:24]
length(train_o)
## [1] 24
train_p <- preds[1:24,]
test_o <- obs[25:30]
test_p <- preds[25:30,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
## Training set prediction matrix is not full rank. Algorithm to remove linearly dependent models started.
## The input matrix is not full rank. The indices of the linearly dependent models are: 1, 2, 3
## Of these models, model 2 had the highest RMSE and was removed.
## Checking if the revised matrix is full rank.
## The revised matrix is now full rank.
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test

# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test 

# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test###################
-modelo_MED$Predict(modelo_MED,newpreds = newpreds)
##  [1] -2.568685 -2.562370 -2.556054 -2.549739 -2.543424 -2.538910 -2.534408
##  [8] -2.529907 -2.525405 -2.520904 -2.516402 -2.511901
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_TA <- modelo_TA$Accuracy_Test###################

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_WA <- modelo_WA$Accuracy_Test

9 meses

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos <- datos[datos$Periodo == "X9_meses" , ]
datos <- datos[146:nrow(datos), ]

y=ts((datos$Valor),
     start=c(2013,2),
     frequency = 12)
f<-window(y,start=c(2013,2),c(2020,11))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=30,drift=TRUE) 
predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 
predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   
predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 30, level = 95)   

predicciones_holt<-holt$mean

###ARIMA nuevas

auto.arima(y)
## Series: y 
## ARIMA(0,2,1) 
## 
## Coefficients:
##           ma1
##       -0.8463
## s.e.   0.0592
## 
## sigma^2 = 0.02679:  log likelihood = 51.48
## AIC=-98.97   AICc=-98.88   BIC=-93.2
actividad_arima<-Arima(y,
                       order=c(0,1,1), 
                      seasonal =c(0,0,1), 
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=12)

predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(f)
## Series: f 
## ARIMA(0,1,2) with drift 
## 
## Coefficients:
##           ma1      ma2    drift
##       -0.2812  -0.2313  -0.0176
## s.e.   0.0993   0.0925   0.0060
## 
## sigma^2 = 0.01429:  log likelihood = 66.99
## AIC=-125.98   AICc=-125.53   BIC=-115.85
actividad_arima<-Arima(f,
                       order=c(0,1,1), 
                      seasonal =c(0,0,1), 
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=30)

predicciones_arima<-arima_forecast$mean

#Redes neuronales  nuevas

modelo <- nnetar(y)

pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)

pronosticos <- forecast(modelo, h=30)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

modelo_dot <- dotm(y , h=12)

predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

modelo_dot <- dotm(f , h=30)

predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 30  5
preds<-matrix(preds,30,5)


obs<-window(y,start=c(2020,11), end=c(2023,4))
length(obs)
## [1] 30
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:24]
length(train_o)
## [1] 24
train_p <- preds[1:24,]
test_o <- obs[25:30]
test_p <- preds[25:30,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Tes;accuracy_EIG2
##                ME    RMSE      MAE      MPE     MAPE
## Test set 1.139805 1.14732 1.139805 40.73981 40.73981
newpreds%*%modelo$Weights
##              [,1]
##  [1,]  3.07532169
##  [2,]  2.60695334
##  [3,]  1.93811233
##  [4,]  0.93312779
##  [5,]  0.01585775
##  [6,] -0.99025893
##  [7,] -2.13359685
##  [8,] -3.23531171
##  [9,] -4.25348676
## [10,] -5.34109816
## [11,] -6.51709873
## [12,] -7.80954895
# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test;accuracy_BG
##                ME     RMSE      MAE      MPE     MAPE
## Test set 3.563585 3.578612 3.563585 127.5573 127.5573
# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test;accuracy_InvW 
##                ME     RMSE      MAE      MPE     MAPE
## Test set 3.481224 3.496274 3.481224 124.5918 124.5918
indices_nueve_meses<-newpreds%*%modelo_InvW$Weights

# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test;accuracy_MED
##                ME     RMSE      MAE      MPE     MAPE
## Test set 3.880134 3.895357 3.880134 138.9434 138.9434
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test;accuracy_SA
##                ME     RMSE      MAE      MPE     MAPE
## Test set 3.650442 3.665496 3.650442 130.6826 130.6826
# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_TA <- modelo_TA$Accuracy_Test;accuracy_TA
##                ME     RMSE      MAE      MPE     MAPE
## Test set 3.650442 3.665496 3.650442 130.6826 130.6826
# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_WA <- modelo_WA$Accuracy_Test;accuracy_WA
##                ME     RMSE      MAE      MPE     MAPE
## Test set 3.650442 3.665496 3.650442 130.6826 130.6826

12 meses

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
datos <- datos[datos$Periodo == "X12_meses" , ]
datos <- datos[1:nrow(datos), ]

y=ts((datos$Valor),
     start=c(2001,1),
     frequency = 12)
f<-window(y,end=c(2014,1))


###Método de la deriva 2020-2024

deriva<-rwf(f,h=112,drift=TRUE) 
predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 
predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   
predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 112, level = 95)   

predicciones_holt<-holt$mean

###ARIMA nuevas

auto.arima(y)
## Series: y 
## ARIMA(3,1,3)(0,0,2)[12] 
## 
## Coefficients:
##           ar1     ar2     ar3     ma1     ma2      ma3    sma1    sma2
##       -0.4979  0.0513  0.3097  0.6469  0.0614  -0.4913  0.1218  0.0810
## s.e.   0.4235  0.4942  0.2770  0.4113  0.5353   0.3501  0.0641  0.0558
## 
## sigma^2 = 0.1022:  log likelihood = -73.78
## AIC=165.56   AICc=166.23   BIC=198.21
actividad_arima<-Arima(y,
                       order=c(3,1,3), 
                      seasonal =c(0,0,2), 
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=12)

predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(f)
## Series: f 
## ARIMA(0,1,0)(0,0,1)[12] 
## 
## Coefficients:
##         sma1
##       0.1391
## s.e.  0.0722
## 
## sigma^2 = 0.1741:  log likelihood = -84.61
## AIC=173.22   AICc=173.3   BIC=179.32
actividad_arima<-Arima(f,
                       order=c(3,1,3), 
                      seasonal =c(0,0,2), 
                       include.drift=TRUE)

arima_forecast<-forecast(actividad_arima, h=112)

predicciones_arima<-arima_forecast$mean

#Redes neuronales  nuevas

modelo <- nnetar(y)

pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)

pronosticos <- forecast(modelo, h=112)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

modelo_dot <- dotm(y , h=12)

predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

modelo_dot <- dotm(f , h=112)

predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 112   5
preds<-matrix(preds,112,5)

obs<-window(y,start=c(2014,1), end=c(2023,4))
length(obs)
## [1] 112
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:90]
length(train_o)
## [1] 90
train_p <- preds[1:90,]
test_o <- obs[91:112]
test_p <- preds[91:112,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Tes;accuracy_EIG2
##                ME     RMSE      MAE      MPE     MAPE
## Test set 1.178847 1.844486 1.290127 94.67623 100.4859
newpreds%*%modelo$Weights
##           [,1]
##  [1,] 1.133015
##  [2,] 2.321134
##  [3,] 3.563636
##  [4,] 3.349617
##  [5,] 2.772721
##  [6,] 3.074544
##  [7,] 2.635940
##  [8,] 1.416701
##  [9,] 3.599077
## [10,] 2.784741
## [11,] 2.109135
## [12,] 3.284501
# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test;accuracy_BG
##                ME     RMSE      MAE      MPE     MAPE
## Test set 2.124121 2.649724 2.124121 104.2086 241.3308
newpreds%*%modelo_BG$Weights
##           [,1]
##  [1,] 3.419172
##  [2,] 3.471100
##  [3,] 3.523877
##  [4,] 3.524399
##  [5,] 3.494943
##  [6,] 3.510227
##  [7,] 3.499396
##  [8,] 3.446945
##  [9,] 3.524227
## [10,] 3.493991
## [11,] 3.463636
## [12,] 3.517159
# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test;accuracy_InvW 
##                ME     RMSE      MAE      MPE     MAPE
## Test set 1.524026 2.261037 1.578446 115.2871 131.3385
# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test;accuracy_MED
##                 ME     RMSE      MAE      MPE   MAPE
## Test set 0.7103539 1.690844 1.335335 109.4867 110.36
predict(modelo_MED,newpreds)
##  [1] 3.507592 3.505201 3.599402 3.615292 3.498027 3.495635 3.493244 3.490852
##  [9] 3.488461 3.478561 3.483678 3.672421
## attr(,"tsp")
## [1] 2024.250 2025.167   12.000
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test;accuracy_SA
##                 ME     RMSE      MAE      MPE     MAPE
## Test set 0.7374993 1.941983 1.577655 130.9102 144.1567
# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0.2
accuracy_TA <- modelo_TA$Accuracy_Test;accuracy_TA
##               ME     RMSE      MAE      MPE     MAPE
## Test set 1.07586 1.865265 1.358133 105.7714 105.7714
# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.25
accuracy_WA <- modelo_WA$Accuracy_Test;accuracy_WA
##                ME     RMSE      MAE      MPE     MAPE
## Test set 1.148961 1.906661 1.367203 105.0284 106.5728

####3 años

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X3_anos" , ]
datos <- datos[95:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2008,11),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2011,1),end=c(2020,11))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=30,drift=TRUE) 

predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 

predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   


predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 30, level = 95)

predicciones_holt<-holt$mean


auto.arima(y)
## Series: y 
## ARIMA(1,1,1)(0,0,1)[12] 
## 
## Coefficients:
##           ar1     ma1    sma1
##       -0.9090  0.7127  0.1237
## s.e.   0.0578  0.1012  0.0809
## 
## sigma^2 = 0.1559:  log likelihood = -88.93
## AIC=185.86   AICc=186.08   BIC=198.72
actividad_arima<-Arima(y,
                       order=c(0,1,0),  
                       include.drift=FALSE)


arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(1,1,1)(0,0,1)[12] 
## 
## Coefficients:
##           ar1     ma1    sma1
##       -0.9090  0.7127  0.1237
## s.e.   0.0578  0.1012  0.0809
## 
## sigma^2 = 0.1559:  log likelihood = -88.93
## AIC=185.86   AICc=186.08   BIC=198.72
actividad_arima<-Arima(f,
                       order=c(0,1,0),  
                       include.drift=FALSE)


arima_forecast<-forecast(actividad_arima, h=30)

predicciones_arima<-arima_forecast$mean
#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)


# Generar pronósticos
pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)


# Generar pronósticos
pronosticos <- forecast(modelo, h=30)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)


predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=30)



predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 30  5
preds<-matrix(preds,30,5)


obs<-window(y,start=c(2020,11), end=c(2023,4))
length(obs)
## [1] 30
# Generar observaciones y predicciones



# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:24]
length(train_o)
## [1] 24
train_p <- preds[1:24,]
test_o <- obs[25:30]
test_p <- preds[25:30,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
## Training set prediction matrix is not full rank. Algorithm to remove linearly dependent models started.
## The input matrix is not full rank. The indices of the linearly dependent models are: 1, 2, 3
## Of these models, model 1 had the highest RMSE and was removed.
## Checking if the revised matrix is full rank.
## The revised matrix is now full rank.
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test

# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test
newpreds[,-1]%*%modelo_BG$Weights
##           [,1]
##  [1,] 2.838342
##  [2,] 3.463587
##  [3,] 4.556715
##  [4,] 3.240629
##  [5,] 3.398346
##  [6,] 3.346458
##  [7,] 3.697201
##  [8,] 3.172421
##  [9,] 3.315001
## [10,] 2.914772
## [11,] 3.626523
## [12,] 2.799262
# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test 
indices_tres_anos<-newpreds[,-3]%*%modelo_InvW$Weights

# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test
# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_TA <- modelo_TA$Accuracy_Test###################

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.1
accuracy_WA <- modelo_WA$Accuracy_Test

####5 años

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X5_anos" , ]
datos <- datos[94:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2008,10),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2011,1),end=c(2020,11))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=30,drift=TRUE) 

predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 

predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   


predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 30, level = 95)

predicciones_holt<-holt$mean


auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1232:  log likelihood = -68.84
## AIC=139.68   AICc=139.7   BIC=142.9
actividad_arima<-Arima(y,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1232:  log likelihood = -68.84
## AIC=139.68   AICc=139.7   BIC=142.9
actividad_arima<-Arima(f,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=30)

predicciones_arima<-arima_forecast$mean
#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)


# Generar pronósticos
pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)


# Generar pronósticos
pronosticos <- forecast(modelo, h=30)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)


predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=30)



predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 30  5
preds<-matrix(preds,30,5)


obs<-window(y,start=c(2020,11), end=c(2023,4))
length(obs)
## [1] 30
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:24]
length(train_o)
## [1] 24
train_p <- preds[1:24,]
test_o <- obs[25:30]
test_p <- preds[25:30,]
# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
## Training set prediction matrix is not full rank. Algorithm to remove linearly dependent models started.
## The input matrix is not full rank. The indices of the linearly dependent models are: 1, 2, 3
## Of these models, model 1 had the highest RMSE and was removed.
## Checking if the revised matrix is full rank.
## The revised matrix is now full rank.
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test

# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test #########

indices_cinco_anos<-newpreds[,-1]%*%modelo_InvW$Weights
# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Tes

# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0.25
accuracy_TA <- modelo_TA$Accuracy_Test

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0.34
accuracy_WA <- modelo_WA$Accuracy_Test

####10 años

library(forecast)
library(tidyr)
datos=read.csv2("Datos_csv.csv", header = TRUE)
datos<- pivot_longer(datos, -Tiempo, names_to = "Periodo", values_to = "Valor")
#Filtro por 3_meses
datos <- datos[datos$Periodo == "X10_anos" , ]
datos <- datos[98:nrow(datos), ]

y=ts(rev(datos$Valor),
     start=c(2009,2),
     frequency = 12)
autoplot(y)

f<-window(y,start=c(2011,1),end=c(2021,2))

###Método de la deriva 2020-2024

deriva<-rwf(f,h=27,drift=TRUE) 

predicciones_deriva<-deriva$mean


###Método de la deriva nuevas

deriva<-rwf(y,h=12,drift=TRUE) 

predicciones_deriva_nuevas<-deriva$mean


###Alisado exponencial de Holt nuevas

holt <- holt(y, h = 12, level = 95)   


predicciones_holt_nuevas<-holt$mean

###Alisado exponencial de Holt 2024-2024

holt <- holt(f, h = 27, level = 95)

predicciones_holt<-holt$mean


auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1147:  log likelihood = -60.87
## AIC=123.75   AICc=123.77   BIC=126.95
actividad_arima<-Arima(y,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=12)


predicciones_arima_nuevas<-arima_forecast$mean

###ARIMA de 2020-2024

auto.arima(y)
## Series: y 
## ARIMA(0,1,0) 
## 
## sigma^2 = 0.1147:  log likelihood = -60.87
## AIC=123.75   AICc=123.77   BIC=126.95
actividad_arima<-Arima(f,
                       order=c(0,1,0),  
                       include.drift=FALSE)

arima_forecast<-forecast(actividad_arima, h=27)

predicciones_arima<-arima_forecast$mean
#Redes neuronales  nuevas

# Ajustar el modelo nnetar
modelo <- nnetar(y)


# Generar pronósticos
pronosticos <- forecast(modelo,h=12)

predicciones_redes_nuevas<-pronosticos$mean

#Redes neuronales  2020-2024

# Ajustar el modelo nnetar
modelo <- nnetar(f)


# Generar pronósticos
pronosticos <- forecast(modelo, h=27)

predicciones_redes<-pronosticos$mean


###Dynamic Optimized Theta Model nuevas

library(forecTheta)

# Ajustar el modelo DOT
modelo_dot <- dotm(y , h=12)


predicciones_dot_nuevas<-modelo_dot$mean

###Dynamic Optimized Theta Model 2020-2024

# Ajustar el modelo DOT
modelo_dot <- dotm(f , h=27)



predicciones_dot<-modelo_dot$mean
#install.packages("ForecastComb")
library(ForecastComb)

newpreds  = cbind(predicciones_deriva_nuevas, predicciones_arima_nuevas, predicciones_holt_nuevas, predicciones_redes_nuevas, predicciones_dot_nuevas)
preds = cbind(predicciones_deriva, predicciones_arima, predicciones_holt, predicciones_redes, predicciones_dot)
dim(preds)
## [1] 27  5
preds<-matrix(preds,27,5)


obs<-window(y,start=c(2021,2), end=c(2023,4))
length(obs)
## [1] 27
# Dividir los datos en conjuntos de entrenamiento y prueba
train_o <- obs[1:23]
length(train_o)
## [1] 23
train_p <- preds[1:23,]
test_o <- obs[24:27]
test_p <- preds[24:27,]

# Combinar predicciones utilizando foreccomb
data <- foreccomb(train_o, train_p, test_o, test_p)
## Training set prediction matrix is not full rank. Algorithm to remove linearly dependent models started.
## The input matrix is not full rank. The indices of the linearly dependent models are: 1, 2, 3
## Of these models, model 3 had the highest RMSE and was removed.
## Checking if the revised matrix is full rank.
## The revised matrix is now full rank.
# Entrenar modelo comb_EIG2
modelo<-comb_EIG2(data)
accuracy_EIG2<-modelo$Accuracy_Test
indices_diez_anos<-modelo$Predict(modelo,newpreds = newpreds[,-3])
# Entrenar modelo comb_BG
modelo_BG <- comb_BG(data)
accuracy_BG <- modelo_BG$Accuracy_Test

# Entrenar modelo comb_InvW
modelo_InvW <- comb_InvW(data)
accuracy_InvW <- modelo_InvW$Accuracy_Test 
newpreds[,-1]%*%modelo_InvW$Weights
##           [,1]
##  [1,] 4.588797
##  [2,] 4.462987
##  [3,] 4.602636
##  [4,] 4.544460
##  [5,] 4.526867
##  [6,] 4.591169
##  [7,] 4.675167
##  [8,] 4.826576
##  [9,] 4.825818
## [10,] 4.914997
## [11,] 4.778783
## [12,] 4.758558
# Entrenar modelo comb_MED
modelo_MED <- comb_MED(data)
accuracy_MED <- modelo_MED$Accuracy_Test


# Entrenar modelo comb_SA
modelo_SA <- comb_SA(data)
accuracy_SA <- modelo_SA$Accuracy_Test

# Entrenar modelo comb_TA
modelo_TA <- comb_TA(data)
## Optimization algorithm chooses trim factor for trimmed mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_TA <- modelo_TA$Accuracy_Tes

# Entrenar modelo comb_WA
modelo_WA <- comb_WA(data)
## Optimization algorithm chooses trim factor for winsorized mean approach...
## Algorithm finished. Optimized trim factor: 0
accuracy_WA <- modelo_WA$Accuracy_Test

Predicciones para calcular el MAPE con Forecastcomb

3 meses

percentile.5 percentile.50 percentile.95
1.67 1.53 1.88
1.62 1.54 1.79
2.20 1.87 2.05
2.45 1.97 7.34
2.09 1.87 2.19
2.08 1.92 1.81
1.89 1.81 1.95
2.06 1.87 1.98
2.37 1.97 2.15
3.13 2.16 2.66
3.90 2.44 2.71
3.54 2.31 2.46

6 meses

Ahora predecimos con las letras de 6 meses.

percentile.5 percentile.50 percentile.95
2.30 1.64 1.99
1.98 1.54 1.89
1.95 1.50 1.86
2.09 1.75 2.25
2.03 1.72 2.28
1.77 0.99 1.69
1.60 1.54 2.08
1.58 1.50 1.86
1.58 1.48 1.84
1.66 1.59 1.96
1.88 1.79 2.15
1.99 1.86 2.22

9 meses

A continuación predecimos con las letras de 9 meses.

percentile.5 percentile.50 percentile.95
1.02 1.15 0.84
0.92 1.18 0.86
1.00 1.15 0.83
1.05 1.20 0.85
0.99 1.22 0.86
0.96 1.23 0.85
0.96 1.26 0.87
0.92 1.28 0.92
1.06 1.31 0.84
1.07 1.31 0.83
1.03 1.29 0.97
1.02 1.31 0.98

12 meses

Por último en el apartado de letras predecimos las de 12 meses.

percentile.5 percentile.50 percentile.95
3.08 3.18 3.33
3.03 3.15 3.20
3.06 3.15 3.26
3.10 3.19 3.33
3.18 3.23 3.35
3.24 3.29 3.45
3.30 3.33 3.50
3.22 3.26 3.37
3.29 3.28 3.31
3.20 3.21 3.38
3.25 3.24 3.34
3.12 3.17 3.32

3 años

Bonos a 3 años

percentile.5 percentile.50 percentile.95
2.75 2.37 2.44
2.90 3.34 2.96
3.76 5.38 3.92
2.95 2.92 2.74
3.43 3.47 3.05
3.20 3.28 2.94
3.36 3.90 3.31
2.86 2.93 2.84
3.09 3.21 2.98
2.58 2.47 2.65
3.80 3.95 3.36
2.91 2.42 2.54

5 años

Bonos a 5 años

percentile.5 percentile.50 percentile.95
4.35 6.91 6.50
4.52 5.54 5.11
5.14 8.44 8.07
5.24 2.88 2.71
5.01 6.27 5.83
4.75 6.15 5.81
4.45 7.10 6.69
4.24 3.83 3.64
3.89 3.85 3.70
4.19 13.48 11.64
5.03 6.42 6.10
5.48 5.74 5.47

10 años

Bonos a 10 años

percentile.5 percentile.50 percentile.95
4.62 4.56 4.54
4.87 4.45 4.43
4.65 4.57 4.55
4.63 4.48 4.46
4.83 4.47 4.45
4.74 4.55 4.54
4.86 4.67 4.66
4.93 4.81 4.80
5.09 4.75 4.75
4.36 4.81 4.81
4.74 4.66 4.66
4.99 4.69 4.68
LS0tDQp0aXRsZTogJ0FuZXhvX1RGRycNCmF1dGhvcjogIkFsaWNpYSBEw61hei1Sb25jZXJvIg0KZGF0ZTogIjIwMjQtMDItMDkiDQpvdXRwdXQ6IA0KICBybWRmb3JtYXRzOjptYXRlcmlhbDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiAic2hvdyINCiAgICBoaWdobGlnaHQ6IGthdGUNCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQ0KICAgIHRodW1ibmFpbHM6IHRydWUNCiAgICBnYWxsZXJ5OiB0cnVlDQogICAgZmlnX3dpZHRoOiA0DQogICAgZmlnX2hlaWdodDogNA0KICAgIGRmX3ByaW50OiBrYWJsZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCg0KDQojIDUuMSBQcmVwcm9jZXNhbWllbnRvIGRlIGRhdG9zDQoNCkdyw6FmaWNvcyBkZSBsZXRyYXMgZGVsIFRlc29ybw0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KG9wZW54bHN4KQ0KYGBgDQoNCmBgYHtyfQ0KDQoNCiMgUmVhZCB0aGUgRXhjZWwgZmlsZSBpbnRvIGEgdmFyaWFibGUgbmFtZWQgZGF0b3MNCmRhdG9zIDwtIHJlYWQueGxzeCgiZGF0b3MueGxzeCIpDQpkYXRvcyRUaWVtcG89YXMuZmFjdG9yKGRhdG9zJFRpZW1wbykNCmRhdG9zJFRpZW1wbyA8LSBhcy5EYXRlKHBhc3RlMChkYXRvcyRUaWVtcG8sICJfMDEiKSwgZm9ybWF0ID0gIiVCXyVZXyVkIikNCmRhdG9zX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCg0KDQpgYGANCg0KYGBge3J9DQpncmFmaWNvMSA8LWdncGxvdChkYXRvc19sb25nW2RhdG9zX2xvbmckUGVyaW9kbyAlaW4lIGMoIjNfbWVzZXMiKSwgXSwgDQogICAgICAgYWVzKHggPSBUaWVtcG8sIHkgPSBWYWxvciwgY29sb3IgPSBQZXJpb2RvKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiMyBtZXNlcyIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KYGBge3J9DQpncmFmaWNvMiA8LWdncGxvdChkYXRvc19sb25nW2RhdG9zX2xvbmckUGVyaW9kbyAlaW4lIGMoIjZfbWVzZXMiKSwgXSwgDQogICAgICAgYWVzKHggPSBUaWVtcG8sIHkgPSBWYWxvciwgY29sb3IgPSBQZXJpb2RvKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICI2IG1lc2VzIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsDQogICAgICAgeSA9ICJWYWxvciIsDQogICAgICAgY29sb3IgPSAiUGVyaW9kbyIpICArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpgYGB7cn0NCmdyYWZpY28zIDwtZ2dwbG90KGRhdG9zX2xvbmdbZGF0b3NfbG9uZyRQZXJpb2RvICVpbiUgYygiOV9tZXNlcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZShjb2xvciA9ICJncmVlbiIpICsNCiAgbGFicyh0aXRsZSA9ICI5IG1lc2VzIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsDQogICAgICAgeSA9ICJWYWxvciIsDQogICAgICAgY29sb3IgPSAiUGVyaW9kbyIpICArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpgYGB7cn0NCg0KZ3JhZmljbzQgPC1nZ3Bsb3QoZGF0b3NfbG9uZ1tkYXRvc19sb25nJFBlcmlvZG8gJWluJSBjKCIxMl9tZXNlcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZShjb2xvciA9ICJvcmFuZ2UiKSArDQogIGxhYnModGl0bGUgPSAiMTIgbWVzZXMiLA0KICAgICAgIHggPSAiVGllbXBvIiwNCiAgICAgICB5ID0gIlZhbG9yIiwNCiAgICAgICBjb2xvciA9ICJQZXJpb2RvIikgICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpgYGANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCiMgSnVudGFyIGxvcyBncsOhZmljb3MNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KZ3JpZC5hcnJhbmdlKHRvcCA9ICJSZXRvcm5vcyBkZSBMZXRyYXMgZGVsIHRlc29ybyAyMDAwLTIwMjQiLGdyYWZpY28xLCBncmFmaWNvMiwgZ3JhZmljbzMsIGdyYWZpY280LCBuY29sID0gMikNCmBgYA0KDQpHcsOhZmljb3MgZGUgYm9ub3MgZGVsIEVzdGFkbw0KDQpgYGB7cn0NCmdyYWZpY281PC1nZ3Bsb3QoZGF0b3NfbG9uZ1tkYXRvc19sb25nJFBlcmlvZG8gJWluJSBjKCIzX2HDsW9zIiksIF0sIA0KICAgICAgIGFlcyh4ID0gVGllbXBvLCB5ID0gVmFsb3IsIGNvbG9yID0gUGVyaW9kbykpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBsYWJzKHRpdGxlID0gIjMgYcOxb3MiLA0KICAgICAgIHggPSAiVGllbXBvIiwNCiAgICAgICB5ID0gIlZhbG9yIiwNCiAgICAgICBjb2xvciA9ICJQZXJpb2RvIikgICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KYGBge3J9DQpncmFmaWNvNjwtZ2dwbG90KGRhdG9zX2xvbmdbZGF0b3NfbG9uZyRQZXJpb2RvICVpbiUgYygiNV9hw7FvcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZShjb2xvcj0iYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICI1IGHDsW9zIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsDQogICAgICAgeSA9ICJWYWxvciIsDQogICAgICAgY29sb3IgPSAiUGVyaW9kbyIpICArDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KIyBKdW50YXIgbG9zIGdyw6FmaWNvcw0KbGlicmFyeShncmlkRXh0cmEpDQpncmlkLmFycmFuZ2UodG9wID0gIlJldG9ybm9zIGRlIEJvbm9zIGRlbCBFc3RhZG8gMjAwMC0yMDI0IixncmFmaWNvNSwgZ3JhZmljbzYsIG5jb2wgPSAyKQ0KYGBgDQoNCkdyw6FmaWNvcyBkZSBvYmxpZ2FjaW9uZXMgZGVsIEVzdGFkbw0KDQpgYGB7cn0NCmdyYWZpY283PC1nZ3Bsb3QoZGF0b3NfbG9uZ1tkYXRvc19sb25nJFBlcmlvZG8gJWluJSBjKCIxMF9hw7FvcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICIxMCBhw7FvcyIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpgYGB7cn0NCmdyYWZpY284PC1nZ3Bsb3QoZGF0b3NfbG9uZ1tkYXRvc19sb25nJFBlcmlvZG8gJWluJSBjKCIxNV9hw7FvcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZShjb2xvcj0iYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICIxNSBhw7FvcyIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpgYGB7cn0NCmdyYWZpY285PC1nZ3Bsb3QoZGF0b3NfbG9uZ1tkYXRvc19sb25nJFBlcmlvZG8gJWluJSBjKCIzMC01MF9hw7FvcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZShjb2xvcj0iZ3JlZW4iKSArDQogIGxhYnModGl0bGUgPSAiMzAtNTAgYcOxb3MiLA0KICAgICAgIHggPSAiVGllbXBvIiwNCiAgICAgICB5ID0gIlZhbG9yIiwNCiAgICAgICBjb2xvciA9ICJQZXJpb2RvIikgICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KYGBge3Isd2FybmluZz1GQUxTRX0NCiMgSnVudGFyIGxvcyBncsOhZmljb3MNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KZ3JpZC5hcnJhbmdlKHRvcCA9ICJSZXRvcm5vcyBkZSBPYmxpZ2FjaW9uZXMgZGVsIGVzdGFkbyAyMDAwLTIwMjQiLGdyYWZpY283LCBncmFmaWNvOCxncmFmaWNvOSwgbmNvbCA9IDMpDQpgYGANCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQoNCkPDoWxjdWxvIGRlIG51bG9zIHkgZ3JhZmljb3MgYSAzIG1lc2VzDQoNCmBgYHtyfQ0KZGF0b3MgPC0gcmVhZC54bHN4KCJkYXRvcy54bHN4IikNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDNfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIjNfbWVzZXMiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTQ6bnJvdyhkYXRvcyksIF0NCm51bG9zPC1zdW0oaXMubmEoZGF0b3MpKTtudWxvcw0KdG90YWw8LW5yb3coZGF0b3MpDQpwb3JjZW50YWplPC1udWxvcy90b3RhbDtwb3JjZW50YWplDQpgYGANCg0KDQpHUsOBRklDTyBPUklHSU5BTA0KDQpgYGB7cn0NCiMgUmVhZCB0aGUgRXhjZWwgZmlsZSBpbnRvIGEgdmFyaWFibGUgbmFtZWQgZGF0b3MNCmRhdG9zIDwtIHJlYWQueGxzeCgiZGF0b3MueGxzeCIpDQpkYXRvcyRUaWVtcG89YXMuZmFjdG9yKGRhdG9zJFRpZW1wbykNCmRhdG9zJFRpZW1wbyA8LSBhcy5EYXRlKHBhc3RlMChkYXRvcyRUaWVtcG8sICJfMDEiKSwgZm9ybWF0ID0gIiVCXyVZXyVkIikNCmRhdG9zX2xvbmdfY29tcGxldGEgPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCg0KDQpgYGANCg0KYGBge3J9DQpncmFmaWNvMSA8LWdncGxvdChkYXRvc19sb25nX2NvbXBsZXRhW2RhdG9zX2xvbmdfY29tcGxldGEkUGVyaW9kbyAlaW4lIGMoIjNfbWVzZXMiKSwgXSwgDQogICAgICAgYWVzKHggPSBUaWVtcG8sIHkgPSBWYWxvciwgY29sb3IgPSBQZXJpb2RvKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiU2VyaWUgaW5pY2lhbCIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KR1LDgUZJQ08gTlVFVk8NCg0KYGBge3J9DQoNCmRhdG9zX2M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zX2MkVGllbXBvPWFzLmZhY3RvcihkYXRvc19jJFRpZW1wbykNCmRhdG9zX2MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zX2MkVGllbXBvLCAiXzAxIiksIGZvcm1hdCA9ICIlQl8lWV8lZCIpDQpkYXRvc19sb25nIDwtIHBpdm90X2xvbmdlcihkYXRvc19jLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNWaWV3KGRhdG9zX2xvbmcpDQpkYXRvc19sb25nIDwtIGRhdG9zX2xvbmdbODM4Om5yb3coZGF0b3NfbG9uZyksIF0NCiNWaWV3KGRhdG9zX2xvbmcpDQpgYGANCg0KYGBge3J9DQpncmFmaWNvMV9jPC1nZ3Bsb3QoZGF0b3NfbG9uZ1tkYXRvc19sb25nJFBlcmlvZG8gJWluJSBjKCJYM19tZXNlcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZShjb2xvcj0iYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICJTZXJpZSBmaW5hbCIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCmBgYA0KDQpVTknDk04gREUgTE9TIERPUyBHUkFGSUNPUw0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQojIEp1bnRhciBsb3MgZ3LDoWZpY29zDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoZ3JpZCkNCmNpdGF0aW9uKCJGb3JlY2FzdENvbWIiKQ0KIyBQZXJzb25hbGl6YXIgZWwgdMOtdHVsbw0KdGl0bGUgPC0gdGV4dEdyb2IoIlJldG9ybm9zIGRlIExldHJhcyBkZWwgdGVzb3JvIGEgMyBtZXNlcyIsIGdwID0gZ3Bhcihmb250ZmFjZSA9ICJib2xkIiwgZm9udHNpemUgPSAxMikpDQoNCmdyaWQuYXJyYW5nZSh0b3AgPSB0aXRsZSAsZ3JhZmljbzEsZ3JhZmljbzFfYywgbmNvbCA9IDIpDQpgYGANCg0KDQoNCkPDoWxjdWxvIGRlIG51bG9zIHkgZ3JhZmljb3MgYSA2IG1lc2VzDQoNCmBgYHtyfQ0KZGF0b3MgPC0gcmVhZC54bHN4KCJkYXRvcy54bHN4IikNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCg0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiNl9tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NTpucm93KGRhdG9zKSwgXQ0KbnVsb3M8LXN1bShpcy5uYShkYXRvcykpO251bG9zDQp0b3RhbDwtbnJvdyhkYXRvcykNCnBvcmNlbnRhamU8LW51bG9zL3RvdGFsO3BvcmNlbnRhamUNCmBgYA0KDQoNCkdSw4FGSUNPIE9SSUdJTkFMDQoNCg0KYGBge3J9DQpncmFmaWNvMiA8LWdncGxvdChkYXRvc19sb25nX2NvbXBsZXRhW2RhdG9zX2xvbmdfY29tcGxldGEkUGVyaW9kbyAlaW4lIGMoIjZfbWVzZXMiKSwgXSwgDQogICAgICAgYWVzKHggPSBUaWVtcG8sIHkgPSBWYWxvciwgY29sb3IgPSBQZXJpb2RvKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiU2VyaWUgaW5pY2lhbCIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KR1LDgUZJQ08gTlVFVk8NCg0KYGBge3J9DQpkYXRvc19jPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvc19jJFRpZW1wbz1hcy5mYWN0b3IoZGF0b3NfYyRUaWVtcG8pDQpkYXRvc19jJFRpZW1wbyA8LSBhcy5EYXRlKHBhc3RlMChkYXRvc19jJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KZGF0b3NfbG9uZyA8LSBwaXZvdF9sb25nZXIoZGF0b3NfYywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQpkYXRvc19sb25nIDwtIGRhdG9zX2xvbmdbODQ3Om5yb3coZGF0b3NfbG9uZyksIF0NCmBgYA0KDQpgYGB7cn0NCmdyYWZpY28yX2M8LWdncGxvdChkYXRvc19sb25nW2RhdG9zX2xvbmckUGVyaW9kbyAlaW4lIGMoIlg2X21lc2VzIiksIF0sIA0KICAgICAgIGFlcyh4ID0gVGllbXBvLCB5ID0gVmFsb3IsIGNvbG9yID0gUGVyaW9kbykpICsNCiAgZ2VvbV9saW5lKGNvbG9yPSJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlNlcmllIGZpbmFsIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsDQogICAgICAgeSA9ICJWYWxvciIsDQogICAgICAgY29sb3IgPSAiUGVyaW9kbyIpICArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KYGBgDQoNClVOScOTTiBERSBMT1MgRE9TIEdSQUZJQ09TDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCiMgUGVyc29uYWxpemFyIGVsIHTDrXR1bG8NCnRpdGxlIDwtIHRleHRHcm9iKCJSZXRvcm5vcyBkZSBMZXRyYXMgZGVsIHRlc29ybyBhIDYgbWVzZXMiLCBncCA9IGdwYXIoZm9udGZhY2UgPSAiYm9sZCIsIGZvbnRzaXplID0gMTIpKQ0KDQpncmlkLmFycmFuZ2UodG9wID0gdGl0bGUgLGdyYWZpY28yLGdyYWZpY28yX2MsIG5jb2wgPSAyKQ0KYGBgDQoNCkPDoWxjdWxvIGRlIG51bG9zIHkgZ3JhZmljb3MgYSA5IG1lc2VzDQoNCmBgYHtyfQ0KDQpkYXRvcyA8LSByZWFkLnhsc3goImRhdG9zLnhsc3giKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiOV9tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1sxNDY6bnJvdyhkYXRvcyksIF0NCm51bG9zPC1zdW0oaXMubmEoZGF0b3MpKTtudWxvcw0KdG90YWw8LW5yb3coZGF0b3MpDQpwb3JjZW50YWplPC1udWxvcy90b3RhbDtwb3JjZW50YWplDQpgYGANCg0KR1LDgUZJQ08gT1JJR0lOQUwNCg0KYGBge3J9DQpncmFmaWNvMyA8LWdncGxvdChkYXRvc19sb25nX2NvbXBsZXRhW2RhdG9zX2xvbmdfY29tcGxldGEkUGVyaW9kbyAlaW4lIGMoIjlfbWVzZXMiKSwgXSwgDQogICAgICAgYWVzKHggPSBUaWVtcG8sIHkgPSBWYWxvciwgY29sb3IgPSBQZXJpb2RvKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiU2VyaWUgaW5pY2lhbCIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KR1LDgUZJQ08gTlVFVk8NCg0KYGBge3J9DQpkYXRvc19sb25nIDwtIHBpdm90X2xvbmdlcihkYXRvc19jLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCmRhdG9zX2xvbmcgPC0gZGF0b3NfbG9uZ1sxMzA2Om5yb3coZGF0b3NfbG9uZyksIF0NCmBgYA0KDQpgYGB7cn0NCmdyYWZpY28zX2M8LWdncGxvdChkYXRvc19sb25nW2RhdG9zX2xvbmckUGVyaW9kbyAlaW4lIGMoIlg5X21lc2VzIiksIF0sIA0KICAgICAgIGFlcyh4ID0gVGllbXBvLCB5ID0gVmFsb3IsIGNvbG9yID0gUGVyaW9kbykpICsNCiAgZ2VvbV9saW5lKGNvbG9yPSJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlNlcmllIGZpbmFsIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsDQogICAgICAgeSA9ICJWYWxvciIsDQogICAgICAgY29sb3IgPSAiUGVyaW9kbyIpICArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KYGBgDQoNClVOScOTTiBERSBMT1MgRE9TIEdSQUZJQ09TDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQp0aXRsZSA8LSB0ZXh0R3JvYigiUmV0b3Jub3MgZGUgTGV0cmFzIGRlbCB0ZXNvcm8gYSA5IG1lc2VzIiwgZ3AgPSBncGFyKGZvbnRmYWNlID0gImJvbGQiLCBmb250c2l6ZSA9IDEyKSkNCmdyaWQuYXJyYW5nZSh0b3AgPSB0aXRsZSAsZ3JhZmljbzMsZ3JhZmljbzNfYywgbmNvbCA9IDIpDQpgYGANCg0KDQpDw6FsY3VsbyBkZSBudWxvcyB5IGdyYWZpY29zIGEgMTIgbWVzZXMNCg0KYGBge3J9DQpkYXRvcyA8LSByZWFkLnhsc3goImRhdG9zLnhsc3giKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiMTJfbWVzZXMiICwgXQ0KZGF0b3MgPC0gZGF0b3NbMTpucm93KGRhdG9zKSwgXQ0KbnVsb3M8LXN1bShpcy5uYShkYXRvcykpO251bG9zDQp0b3RhbDwtbnJvdyhkYXRvcykNCnBvcmNlbnRhamU8LW51bG9zL3RvdGFsO3BvcmNlbnRhamUNCmBgYA0KDQpHUsOBRklDTyBPUklHSU5BTA0KDQpgYGB7cn0NClZpZXcoZGF0b3NfbG9uZ19jb21wbGV0YVtkYXRvc19sb25nX2NvbXBsZXRhJFBlcmlvZG8gJWluJSBjKCIxMl9tZXNlcyIpLCBdKQ0KZ3JhZmljbzQgPC1nZ3Bsb3QoZGF0b3NfbG9uZ19jb21wbGV0YVtkYXRvc19sb25nX2NvbXBsZXRhJFBlcmlvZG8gJWluJSBjKCIxMl9tZXNlcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICJTZXJpZSBpbmljaWFsIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsDQogICAgICAgeSA9ICJWYWxvciIsDQogICAgICAgY29sb3IgPSAiUGVyaW9kbyIpICArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpHUsOBRklDTyBOVUVWTw0KDQpgYGB7cn0NCmRhdG9zX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKGRhdG9zX2MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KZGF0b3NfbG9uZyA8LSBkYXRvc19sb25nWzE6bnJvdyhkYXRvc19sb25nKSwgXQ0KYGBgDQoNCmBgYHtyfQ0KZ3JhZmljbzRfYzwtZ2dwbG90KGRhdG9zX2xvbmdbZGF0b3NfbG9uZyRQZXJpb2RvICVpbiUgYygiWDEyX21lc2VzIiksIF0sIA0KICAgICAgIGFlcyh4ID0gVGllbXBvLCB5ID0gVmFsb3IsIGNvbG9yID0gUGVyaW9kbykpICsNCiAgZ2VvbV9saW5lKGNvbG9yPSJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlNlcmllIGZpbmFsIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsDQogICAgICAgeSA9ICJWYWxvciIsDQogICAgICAgY29sb3IgPSAiUGVyaW9kbyIpICArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KYGBgDQoNClVOScOTTiBERSBMT1MgRE9TIEdSQUZJQ09TDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQp0aXRsZSA8LSB0ZXh0R3JvYigiUmV0b3Jub3MgZGUgTGV0cmFzIGRlbCB0ZXNvcm8gYSAxMiBtZXNlcyIsIGdwID0gZ3Bhcihmb250ZmFjZSA9ICJib2xkIiwgZm9udHNpemUgPSAxMikpDQpncmlkLmFycmFuZ2UodG9wID0gdGl0bGUgLGdyYWZpY280LGdyYWZpY280X2MsIG5jb2wgPSAyKQ0KYGBgDQoNCg0KDQpDw6FsY3VsbyBkZSBudWxvcyB5IGdyYWZpY29zIGEgMyBhw7Fvcw0KDQoNCg0KYGBge3J9DQpkYXRvcyA8LSByZWFkLnhsc3goImRhdG9zLnhsc3giKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19hw7Fvcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiM19hw7FvcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NTpucm93KGRhdG9zKSwgXQ0KbnVsb3M8LXN1bShpcy5uYShkYXRvcykpO251bG9zDQp0b3RhbDwtbnJvdyhkYXRvcykNCnBvcmNlbnRhamU8LW51bG9zL3RvdGFsO3BvcmNlbnRhamUNCmBgYA0KDQpHUsOBRklDTyBPUklHSU5BTA0KDQpgYGB7cn0NCg0KZ3JhZmljbzUgPC1nZ3Bsb3QoZGF0b3NfbG9uZ19jb21wbGV0YVtkYXRvc19sb25nX2NvbXBsZXRhJFBlcmlvZG8gJWluJSBjKCIzX2HDsW9zIiksIF0sIA0KICAgICAgIGFlcyh4ID0gVGllbXBvLCB5ID0gVmFsb3IsIGNvbG9yID0gUGVyaW9kbykpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBsYWJzKHRpdGxlID0gIlNlcmllIGluaWNpYWwiLA0KICAgICAgIHggPSAiVGllbXBvIiwNCiAgICAgICB5ID0gIlZhbG9yIiwNCiAgICAgICBjb2xvciA9ICJQZXJpb2RvIikgICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCkdSw4FGSUNPIE5VRVZPDQoNCmBgYHtyfQ0KZGF0b3NfbG9uZyA8LSBwaXZvdF9sb25nZXIoZGF0b3NfYywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQpkYXRvc19sb25nIDwtIGRhdG9zX2xvbmdbODQ3Om5yb3coZGF0b3NfbG9uZyksIF0NCmBgYA0KDQpgYGB7cn0NCmdyYWZpY281X2M8LWdncGxvdChkYXRvc19sb25nW2RhdG9zX2xvbmckUGVyaW9kbyAlaW4lIGMoIlgzX2Fub3MiKSwgXSwgDQogICAgICAgYWVzKHggPSBUaWVtcG8sIHkgPSBWYWxvciwgY29sb3IgPSBQZXJpb2RvKSkgKw0KICBnZW9tX2xpbmUoY29sb3I9ImJsdWUiKSArDQogIGxhYnModGl0bGUgPSAiU2VyaWUgZmluYWwiLA0KICAgICAgIHggPSAiVGllbXBvIiwNCiAgICAgICB5ID0gIlZhbG9yIiwNCiAgICAgICBjb2xvciA9ICJQZXJpb2RvIikgICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpgYGANCg0KVU5Jw5NOIERFIExPUyBET1MgR1JBRklDT1MNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCnRpdGxlIDwtIHRleHRHcm9iKCJSZXRvcm5vcyBkZSBCb25vcyBkZWwgRXN0YWRvIGEgMyBhw7FvcyIsIGdwID0gZ3Bhcihmb250ZmFjZSA9ICJib2xkIiwgZm9udHNpemUgPSAxMikpDQpncmlkLmFycmFuZ2UodG9wID0gdGl0bGUgLGdyYWZpY281LGdyYWZpY281X2MsIG5jb2wgPSAyKQ0KYGBgDQoNCg0KQ8OhbGN1bG8gZGUgbnVsb3MgeSBncmFmaWNvcyBhIDUgYcOxb3MNCg0KDQpgYGB7cn0NCmRhdG9zIDwtIHJlYWQueGxzeCgiZGF0b3MueGxzeCIpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciA1X2HDsW9zDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICI1X2HDsW9zIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzk0Om5yb3coZGF0b3MpLCBdDQpudWxvczwtc3VtKGlzLm5hKGRhdG9zKSk7bnVsb3MNCnRvdGFsPC1ucm93KGRhdG9zKQ0KcG9yY2VudGFqZTwtbnVsb3MvdG90YWw7cG9yY2VudGFqZQ0KYGBgDQoNCkdSw4FGSUNPIE9SSUdJTkFMDQoNCmBgYHtyfQ0KDQpncmFmaWNvNiA8LWdncGxvdChkYXRvc19sb25nX2NvbXBsZXRhW2RhdG9zX2xvbmdfY29tcGxldGEkUGVyaW9kbyAlaW4lIGMoIjVfYcOxb3MiKSwgXSwgDQogICAgICAgYWVzKHggPSBUaWVtcG8sIHkgPSBWYWxvciwgY29sb3IgPSBQZXJpb2RvKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiU2VyaWUgaW5pY2lhbCIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KR1LDgUZJQ08gTlVFVk8NCg0KYGBge3J9DQpkYXRvc19sb25nIDwtIHBpdm90X2xvbmdlcihkYXRvc19jLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCmRhdG9zX2xvbmcgPC0gZGF0b3NfbG9uZ1s4Mzg6bnJvdyhkYXRvc19sb25nKSwgXQ0KYGBgDQoNCmBgYHtyfQ0KZ3JhZmljbzZfYzwtZ2dwbG90KGRhdG9zX2xvbmdbZGF0b3NfbG9uZyRQZXJpb2RvICVpbiUgYygiWDVfYW5vcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZShjb2xvcj0iYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICJTZXJpZSBmaW5hbCIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCmBgYA0KDQpVTknDk04gREUgTE9TIERPUyBHUkFGSUNPUw0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KdGl0bGUgPC0gdGV4dEdyb2IoIlJldG9ybm9zIGRlIEJvbm9zIGRlbCBFc3RhZG8gYSA1IGHDsW9zIiwgZ3AgPSBncGFyKGZvbnRmYWNlID0gImJvbGQiLCBmb250c2l6ZSA9IDEyKSkNCmdyaWQuYXJyYW5nZSh0b3AgPSB0aXRsZSAsZ3JhZmljbzYsZ3JhZmljbzZfYywgbmNvbCA9IDIpDQpgYGANCg0KDQoNCg0KQ8OhbGN1bG8gZGUgbnVsb3MgeSBncmFmaWNvcyBhIDEwIGHDsW9zDQoNCg0KDQpgYGB7cn0NCmRhdG9zIDwtIHJlYWQueGxzeCgiZGF0b3MueGxzeCIpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX2HDsW9zDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICIxMF9hw7FvcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5ODpucm93KGRhdG9zKSwgXQ0KbnVsb3M8LXN1bShpcy5uYShkYXRvcykpO251bG9zDQp0b3RhbDwtbnJvdyhkYXRvcykNCnBvcmNlbnRhamU8LW51bG9zL3RvdGFsO3BvcmNlbnRhamUNCmBgYA0KDQpHUsOBRklDTyBPUklHSU5BTA0KDQpgYGB7cn0NCg0KZ3JhZmljbzcgPC1nZ3Bsb3QoZGF0b3NfbG9uZ19jb21wbGV0YVtkYXRvc19sb25nX2NvbXBsZXRhJFBlcmlvZG8gJWluJSBjKCIxMF9hw7FvcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICJTZXJpZSBpbmljaWFsIiwNCiAgICAgICB4ID0gIlRpZW1wbyIsDQogICAgICAgeSA9ICJWYWxvciIsDQogICAgICAgY29sb3IgPSAiUGVyaW9kbyIpICArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpHUsOBRklDTyBOVUVWTw0KDQpgYGB7cn0NCmRhdG9zX2xvbmcgPC0gcGl2b3RfbG9uZ2VyKGRhdG9zX2MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KZGF0b3NfbG9uZyA8LSBkYXRvc19sb25nWzg3NDpucm93KGRhdG9zX2xvbmcpLCBdDQpgYGANCg0KYGBge3J9DQpncmFmaWNvN19jPC1nZ3Bsb3QoZGF0b3NfbG9uZ1tkYXRvc19sb25nJFBlcmlvZG8gJWluJSBjKCJYMTBfYW5vcyIpLCBdLCANCiAgICAgICBhZXMoeCA9IFRpZW1wbywgeSA9IFZhbG9yLCBjb2xvciA9IFBlcmlvZG8pKSArDQogIGdlb21fbGluZShjb2xvcj0iYmx1ZSIpICsNCiAgbGFicyh0aXRsZSA9ICJTZXJpZSBmaW5hbCIsDQogICAgICAgeCA9ICJUaWVtcG8iLA0KICAgICAgIHkgPSAiVmFsb3IiLA0KICAgICAgIGNvbG9yID0gIlBlcmlvZG8iKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCmBgYA0KDQpVTknDk04gREUgTE9TIERPUyBHUkFGSUNPUw0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KdGl0bGUgPC0gdGV4dEdyb2IoIlJldG9ybm9zIGRlIE9ibGlnYWNpb25lcyBkZWwgRXN0YWRvIGEgMTAgYcOxb3MiLCBncCA9IGdwYXIoZm9udGZhY2UgPSAiYm9sZCIsIGZvbnRzaXplID0gMTIpKQ0KZ3JpZC5hcnJhbmdlKHRvcCA9IHRpdGxlICxncmFmaWNvNyxncmFmaWNvN19jLCBuY29sID0gMikNCmBgYA0KDQoNCg0KDQoNCg0KIyMjTWl4IDMgbWVzZXMNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCg0KYGBgDQoNCmBgYHtyfQ0KDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDNfbWVzZXMiICwgXQ0KY2FsY3VsYXRlZF9kZiAgPC0gZGF0b3NbOTQ6bnJvdyhkYXRvcyksIF0NCmNhbGN1bGF0ZWRfZGYgIDwtIGNhbGN1bGF0ZWRfZGZbLC0yIF0NCmNhbGN1bGF0ZWRfZGYgPC0gZGF0YS5mcmFtZShmZWNoYSA9Y2FsY3VsYXRlZF9kZiRUaWVtcG8sIHZhbG9yPWNhbGN1bGF0ZWRfZGYkVmFsb3IgICkNCg0KIyBMZWVyIGxvcyBkYXRvcyBkZXNkZSBlbCBhcmNoaXZvIEV4Y2VsDQpkYXRvcyA8LSByZWFkLnhsc3goImRhdG9zLnhsc3giKQ0KDQojIENvbnZlcnRpciBsYSBjb2x1bW5hICdUaWVtcG8nIGEgZm9ybWF0byBkZSBmZWNoYQ0KZGF0b3MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KDQojIFBpdm90YXIgbG9zIGRhdG9zDQpkYXRvcyA8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KDQojIEZpbHRyYXIgcG9yIGVsIHBlcmlvZG8gJzNfbWVzZXMnDQpkYXRvcyA8LSBmaWx0ZXIoZGF0b3MsIFBlcmlvZG8gPT0gIjNfbWVzZXMiKQ0Kb3JpZ2luYWxfZGYgIDwtIGRhdG9zWzk0Om5yb3coZGF0b3MpLCBdDQpvcmlnaW5hbF9kZiAgPC0gb3JpZ2luYWxfZGZbLC0yIF0NCm9yaWdpbmFsX2RmIDwtIGRhdGEuZnJhbWUoZmVjaGEgPW9yaWdpbmFsX2RmJFRpZW1wbywgdmFsb3I9b3JpZ2luYWxfZGYkVmFsb3IgICkNCg0Kb3JpZ2luYWxfZGYkdGlwbyAgPC0gJ09yaWdpbmFsJw0KY2FsY3VsYXRlZF9kZiR0aXBvICA8LSAnQ2FsY3VsYXRlZCcNCmNvbWJpbmVkX2RmIDwtIHJiaW5kKG9yaWdpbmFsX2RmLCBjYWxjdWxhdGVkX2RmKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb21iaW5hciBsb3MgZGF0YSBmcmFtZXMgcGFyYSBxdWUgbG9zIHZhbG9yZXMgY2FsY3VsYWRvcyByZWVtcGxhY2VuIGxvcyBudWxvcyBlbiBsb3Mgb3JpZ2luYWxlcw0KY29tYmluZWRfZGYgPC0gb3JpZ2luYWxfZGYgJT4lDQogIGZ1bGxfam9pbihjYWxjdWxhdGVkX2RmLCBieSA9ICJmZWNoYSIsIHN1ZmZpeCA9IGMoIl9vcmlnaW5hbCIsICJfY2FsY3VsYXRlZCIpKSAlPiUNCiAgbXV0YXRlKA0KICAgIHZhbG9yID0gaWZlbHNlKGlzLm5hKHZhbG9yX29yaWdpbmFsKSwgdmFsb3JfY2FsY3VsYXRlZCwgdmFsb3Jfb3JpZ2luYWwpLA0KICAgIHRpcG8gPSBpZmVsc2UoaXMubmEodmFsb3Jfb3JpZ2luYWwpLCAiSW1wdXRhZG8iLCAiT3JpZ2luYWwiKQ0KICApDQpjb21iaW5lZF9kZiR0YW1hbm9fcHVudG8gPC0gaWZlbHNlKGNvbWJpbmVkX2RmJHRpcG8gPT0gIkltcHV0YWRvIiwgMi41LCAxKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhciBlbCBncsOhZmljbw0KbWl4MTwtZ2dwbG90KGNvbWJpbmVkX2RmLCBhZXMoeCA9IGZlY2hhLCB5ID0gdmFsb3IsIGNvbG9yID0gdGlwbykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLCBhZXMoZ3JvdXAgPSAxKSkgKw0KICBnZW9tX3BvaW50KGFlcyhzaXplID0gdGFtYW5vX3B1bnRvKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiT3JpZ2luYWwiID0gImJsdWUiLCAiSW1wdXRhZG8iID0gInJlZCIpKSArDQogIHNjYWxlX3NpemVfaWRlbnRpdHkoKSArDQogIGxhYnModGl0bGUgPSAiTGV0cmFzIGEgMyBtZXNlcyIsIHggPSAiRmVjaGEiLCB5ID0gIlZhbG9yIikgKw0KICB0aGVtZV9taW5pbWFsKCkrIA0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KIyMjTWl4IDYgbWVzZXMNCg0KYGBge3J9DQoNCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvcyRUaWVtcG8gPC0gYXMuRGF0ZShwYXN0ZTAoZGF0b3MkVGllbXBvLCAiXzAxIiksIGZvcm1hdCA9ICIlQl8lWV8lZCIpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYNl9tZXNlcyIgLCBdDQpjYWxjdWxhdGVkX2RmICA8LSBkYXRvc1s5NTpucm93KGRhdG9zKSwgXQ0KY2FsY3VsYXRlZF9kZiAgPC0gY2FsY3VsYXRlZF9kZlssLTIgXQ0KY2FsY3VsYXRlZF9kZiA8LSBkYXRhLmZyYW1lKGZlY2hhID1jYWxjdWxhdGVkX2RmJFRpZW1wbywgdmFsb3I9Y2FsY3VsYXRlZF9kZiRWYWxvciAgKQ0KDQojIExlZXIgbG9zIGRhdG9zIGRlc2RlIGVsIGFyY2hpdm8gRXhjZWwNCmRhdG9zIDwtIHJlYWQueGxzeCgiZGF0b3MueGxzeCIpDQoNCiMgQ29udmVydGlyIGxhIGNvbHVtbmEgJ1RpZW1wbycgYSBmb3JtYXRvIGRlIGZlY2hhDQpkYXRvcyRUaWVtcG8gPC0gYXMuRGF0ZShwYXN0ZTAoZGF0b3MkVGllbXBvLCAiXzAxIiksIGZvcm1hdCA9ICIlQl8lWV8lZCIpDQoNCiMgUGl2b3RhciBsb3MgZGF0b3MNCmRhdG9zIDwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQoNCiMgRmlsdHJhciBwb3IgZWwgcGVyaW9kbyAnM19tZXNlcycNCmRhdG9zIDwtIGZpbHRlcihkYXRvcywgUGVyaW9kbyA9PSAiNl9tZXNlcyIpDQpvcmlnaW5hbF9kZiAgPC0gZGF0b3NbOTU6bnJvdyhkYXRvcyksIF0NCm9yaWdpbmFsX2RmICA8LSBvcmlnaW5hbF9kZlssLTIgXQ0KDQpvcmlnaW5hbF9kZiA8LSBkYXRhLmZyYW1lKGZlY2hhID1vcmlnaW5hbF9kZiRUaWVtcG8sIHZhbG9yPW9yaWdpbmFsX2RmJFZhbG9yICApDQoNCm9yaWdpbmFsX2RmJHRpcG8gIDwtICdPcmlnaW5hbCcNCmNhbGN1bGF0ZWRfZGYkdGlwbyAgPC0gJ0NhbGN1bGF0ZWQnDQpjb21iaW5lZF9kZiA8LSByYmluZChvcmlnaW5hbF9kZiwgY2FsY3VsYXRlZF9kZikNCmBgYA0KDQpgYGB7cn0NCiMgQ29tYmluYXIgbG9zIGRhdGEgZnJhbWVzIHBhcmEgcXVlIGxvcyB2YWxvcmVzIGNhbGN1bGFkb3MgcmVlbXBsYWNlbiBsb3MgbnVsb3MgZW4gbG9zIG9yaWdpbmFsZXMNCmNvbWJpbmVkX2RmIDwtIG9yaWdpbmFsX2RmICU+JQ0KICBmdWxsX2pvaW4oY2FsY3VsYXRlZF9kZiwgYnkgPSAiZmVjaGEiLCBzdWZmaXggPSBjKCJfb3JpZ2luYWwiLCAiX2NhbGN1bGF0ZWQiKSkgJT4lDQogIG11dGF0ZSgNCiAgICB2YWxvciA9IGlmZWxzZShpcy5uYSh2YWxvcl9vcmlnaW5hbCksIHZhbG9yX2NhbGN1bGF0ZWQsIHZhbG9yX29yaWdpbmFsKSwNCiAgICB0aXBvID0gaWZlbHNlKGlzLm5hKHZhbG9yX29yaWdpbmFsKSwgIkltcHV0YWRvIiwgIk9yaWdpbmFsIikNCiAgKQ0KY29tYmluZWRfZGYkdGFtYW5vX3B1bnRvIDwtIGlmZWxzZShjb21iaW5lZF9kZiR0aXBvID09ICJJbXB1dGFkbyIsIDIuNSwgMSkNCmBgYA0KDQoNCg0KYGBge3J9DQojIENyZWFyIGVsIGdyw6FmaWNvDQptaXgyPC1nZ3Bsb3QoY29tYmluZWRfZGYsIGFlcyh4ID0gZmVjaGEsIHkgPSB2YWxvciwgY29sb3IgPSB0aXBvKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEsIGFlcyhncm91cCA9IDEpKSArDQogIGdlb21fcG9pbnQoYWVzKHNpemUgPSB0YW1hbm9fcHVudG8pKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJPcmlnaW5hbCIgPSAiYmx1ZSIsICJJbXB1dGFkbyIgPSAicmVkIikpICsNCiAgc2NhbGVfc2l6ZV9pZGVudGl0eSgpICsNCiAgbGFicyh0aXRsZSA9ICJMZXRyYXMgYSA2IG1lc2VzIiwgeCA9ICJGZWNoYSIsIHkgPSAiVmFsb3IiKSArDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpgYGANCg0KIyMjTWl4IDkgbWVzZXMNCg0KYGBge3J9DQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDlfbWVzZXMiICwgXQ0KY2FsY3VsYXRlZF9kZiAgPC0gZGF0b3NbMTQ2Om5yb3coZGF0b3MpLCBdDQpjYWxjdWxhdGVkX2RmICA8LSBjYWxjdWxhdGVkX2RmWywtMiBdDQoNCmNhbGN1bGF0ZWRfZGYgPC0gZGF0YS5mcmFtZShmZWNoYSA9Y2FsY3VsYXRlZF9kZiRUaWVtcG8sIHZhbG9yPWNhbGN1bGF0ZWRfZGYkVmFsb3IgICkNCg0KIyBMZWVyIGxvcyBkYXRvcyBkZXNkZSBlbCBhcmNoaXZvIEV4Y2VsDQpkYXRvcyA8LSByZWFkLnhsc3goImRhdG9zLnhsc3giKQ0KDQojIENvbnZlcnRpciBsYSBjb2x1bW5hICdUaWVtcG8nIGEgZm9ybWF0byBkZSBmZWNoYQ0KZGF0b3MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KDQojIFBpdm90YXIgbG9zIGRhdG9zDQpkYXRvcyA8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KDQojIEZpbHRyYXIgcG9yIGVsIHBlcmlvZG8gJzNfbWVzZXMnDQpkYXRvcyA8LSBmaWx0ZXIoZGF0b3MsIFBlcmlvZG8gPT0gIjlfbWVzZXMiKQ0Kb3JpZ2luYWxfZGYgIDwtIGRhdG9zWzE0Njpucm93KGRhdG9zKSwgXQ0Kb3JpZ2luYWxfZGYgIDwtIG9yaWdpbmFsX2RmWywtMiBdDQoNCm9yaWdpbmFsX2RmIDwtIGRhdGEuZnJhbWUoZmVjaGEgPW9yaWdpbmFsX2RmJFRpZW1wbywgdmFsb3I9b3JpZ2luYWxfZGYkVmFsb3IgICkNCg0Kb3JpZ2luYWxfZGYkdGlwbyAgPC0gJ09yaWdpbmFsJw0KY2FsY3VsYXRlZF9kZiR0aXBvICA8LSAnQ2FsY3VsYXRlZCcNCmNvbWJpbmVkX2RmIDwtIHJiaW5kKG9yaWdpbmFsX2RmLCBjYWxjdWxhdGVkX2RmKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb21iaW5hciBsb3MgZGF0YSBmcmFtZXMgcGFyYSBxdWUgbG9zIHZhbG9yZXMgY2FsY3VsYWRvcyByZWVtcGxhY2VuIGxvcyBudWxvcyBlbiBsb3Mgb3JpZ2luYWxlcw0KY29tYmluZWRfZGYgPC0gb3JpZ2luYWxfZGYgJT4lDQogIGZ1bGxfam9pbihjYWxjdWxhdGVkX2RmLCBieSA9ICJmZWNoYSIsIHN1ZmZpeCA9IGMoIl9vcmlnaW5hbCIsICJfY2FsY3VsYXRlZCIpKSAlPiUNCiAgbXV0YXRlKA0KICAgIHZhbG9yID0gaWZlbHNlKGlzLm5hKHZhbG9yX29yaWdpbmFsKSwgdmFsb3JfY2FsY3VsYXRlZCwgdmFsb3Jfb3JpZ2luYWwpLA0KICAgIHRpcG8gPSBpZmVsc2UoaXMubmEodmFsb3Jfb3JpZ2luYWwpLCAiSW1wdXRhZG8iLCAiT3JpZ2luYWwiKQ0KICApDQpjb21iaW5lZF9kZiR0YW1hbm9fcHVudG8gPC0gaWZlbHNlKGNvbWJpbmVkX2RmJHRpcG8gPT0gIkltcHV0YWRvIiwgMi41LCAxKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhciBlbCBncsOhZmljbw0KbWl4MzwtZ2dwbG90KGNvbWJpbmVkX2RmLCBhZXMoeCA9IGZlY2hhLCB5ID0gdmFsb3IsIGNvbG9yID0gdGlwbykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLCBhZXMoZ3JvdXAgPSAxKSkgKw0KICBnZW9tX3BvaW50KGFlcyhzaXplID0gdGFtYW5vX3B1bnRvKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiT3JpZ2luYWwiID0gImJsdWUiLCAiSW1wdXRhZG8iID0gInJlZCIpKSArDQogIHNjYWxlX3NpemVfaWRlbnRpdHkoKSArDQogIGxhYnModGl0bGUgPSAiTGV0cmFzIGEgOSBtZXNlcyIsIHggPSAiRmVjaGEiLCB5ID0gIlZhbG9yIikgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KYGBgDQoNCg0KIyMjTWl4IDEyIG1lc2VzDQoNCmBgYHtyfQ0KDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDEyX21lc2VzIiAsIF0NCmNhbGN1bGF0ZWRfZGYgIDwtIGRhdG9zWzE6bnJvdyhkYXRvcyksIF0NCmNhbGN1bGF0ZWRfZGYgIDwtIGNhbGN1bGF0ZWRfZGZbLC0yIF0NCg0KY2FsY3VsYXRlZF9kZiA8LSBkYXRhLmZyYW1lKGZlY2hhID1jYWxjdWxhdGVkX2RmJFRpZW1wbywgdmFsb3I9Y2FsY3VsYXRlZF9kZiRWYWxvciAgKQ0KDQojIExlZXIgbG9zIGRhdG9zIGRlc2RlIGVsIGFyY2hpdm8gRXhjZWwNCmRhdG9zIDwtIHJlYWQueGxzeCgiZGF0b3MueGxzeCIpDQoNCiMgQ29udmVydGlyIGxhIGNvbHVtbmEgJ1RpZW1wbycgYSBmb3JtYXRvIGRlIGZlY2hhDQpkYXRvcyRUaWVtcG8gPC0gYXMuRGF0ZShwYXN0ZTAoZGF0b3MkVGllbXBvLCAiXzAxIiksIGZvcm1hdCA9ICIlQl8lWV8lZCIpDQoNCiMgUGl2b3RhciBsb3MgZGF0b3MNCmRhdG9zIDwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQoNCiMgRmlsdHJhciBwb3IgZWwgcGVyaW9kbyAnM19tZXNlcycNCmRhdG9zIDwtIGZpbHRlcihkYXRvcywgUGVyaW9kbyA9PSAiMTJfbWVzZXMiKQ0Kb3JpZ2luYWxfZGYgIDwtIGRhdG9zWzE6bnJvdyhkYXRvcyksIF0NCm9yaWdpbmFsX2RmICA8LSBvcmlnaW5hbF9kZlssLTIgXQ0KDQpvcmlnaW5hbF9kZiA8LSBkYXRhLmZyYW1lKGZlY2hhID1vcmlnaW5hbF9kZiRUaWVtcG8sIHZhbG9yPW9yaWdpbmFsX2RmJFZhbG9yICApDQoNCm9yaWdpbmFsX2RmJHRpcG8gIDwtICdPcmlnaW5hbCcNCmNhbGN1bGF0ZWRfZGYkdGlwbyAgPC0gJ0NhbGN1bGF0ZWQnDQpjb21iaW5lZF9kZiA8LSByYmluZChvcmlnaW5hbF9kZiwgY2FsY3VsYXRlZF9kZikNCmBgYA0KDQpgYGB7cn0NCiMgQ29tYmluYXIgbG9zIGRhdGEgZnJhbWVzIHBhcmEgcXVlIGxvcyB2YWxvcmVzIGNhbGN1bGFkb3MgcmVlbXBsYWNlbiBsb3MgbnVsb3MgZW4gbG9zIG9yaWdpbmFsZXMNCmNvbWJpbmVkX2RmIDwtIG9yaWdpbmFsX2RmICU+JQ0KICBmdWxsX2pvaW4oY2FsY3VsYXRlZF9kZiwgYnkgPSAiZmVjaGEiLCBzdWZmaXggPSBjKCJfb3JpZ2luYWwiLCAiX2NhbGN1bGF0ZWQiKSkgJT4lDQogIG11dGF0ZSgNCiAgICB2YWxvciA9IGlmZWxzZShpcy5uYSh2YWxvcl9vcmlnaW5hbCksIHZhbG9yX2NhbGN1bGF0ZWQsIHZhbG9yX29yaWdpbmFsKSwNCiAgICB0aXBvID0gaWZlbHNlKGlzLm5hKHZhbG9yX29yaWdpbmFsKSwgIkltcHV0YWRvIiwgIk9yaWdpbmFsIikNCiAgKQ0KY29tYmluZWRfZGYkdGFtYW5vX3B1bnRvIDwtIGlmZWxzZShjb21iaW5lZF9kZiR0aXBvID09ICJJbXB1dGFkbyIsIDIuNSwgMSkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhciBlbCBncsOhZmljbw0KbWl4NDwtZ2dwbG90KGNvbWJpbmVkX2RmLCBhZXMoeCA9IGZlY2hhLCB5ID0gdmFsb3IsIGNvbG9yID0gdGlwbykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLCBhZXMoZ3JvdXAgPSAxKSkgKw0KICBnZW9tX3BvaW50KGFlcyhzaXplID0gdGFtYW5vX3B1bnRvKSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiT3JpZ2luYWwiID0gImJsdWUiLCAiSW1wdXRhZG8iID0gInJlZCIpKSArDQogIHNjYWxlX3NpemVfaWRlbnRpdHkoKSArDQogIGxhYnModGl0bGUgPSAiTGV0cmFzIGEgMTIgbWVzZXMiLCB4ID0gIkZlY2hhIiwgeSA9ICJWYWxvciIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCmBgYA0KDQoNCiMjIyMgVW5pciBncmFmaWNvcw0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQojIEp1bnRhciBsb3MgZ3LDoWZpY29zDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmdyaWQuYXJyYW5nZSh0b3AgPSAiIFZhbG9yZXMgaW1wdXRhZG9zIGVuIGxhcyBMZXRyYXMgZGVsIHRlc29ybyAyMDAwLTIwMjQiLG1peDEsIG1peDIsIG1peDMsIG1peDQsIG5jb2wgPSAyKQ0KYGBgDQojIyNNaXggMyBhw7Fvcw0KDQpgYGB7cn0NCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvcyRUaWVtcG8gPC0gYXMuRGF0ZShwYXN0ZTAoZGF0b3MkVGllbXBvLCAiXzAxIiksIGZvcm1hdCA9ICIlQl8lWV8lZCIpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYM19hbm9zIiAsIF0NCmNhbGN1bGF0ZWRfZGYgIDwtIGRhdG9zWzk1Om5yb3coZGF0b3MpLCBdDQpjYWxjdWxhdGVkX2RmICA8LSBjYWxjdWxhdGVkX2RmWywtMiBdDQoNCmNhbGN1bGF0ZWRfZGYgPC0gZGF0YS5mcmFtZShmZWNoYSA9Y2FsY3VsYXRlZF9kZiRUaWVtcG8sIHZhbG9yPWNhbGN1bGF0ZWRfZGYkVmFsb3IgICkNCg0KIyBMZWVyIGxvcyBkYXRvcyBkZXNkZSBlbCBhcmNoaXZvIEV4Y2VsDQpkYXRvcyA8LSByZWFkLnhsc3goImRhdG9zLnhsc3giKQ0KDQojIENvbnZlcnRpciBsYSBjb2x1bW5hICdUaWVtcG8nIGEgZm9ybWF0byBkZSBmZWNoYQ0KZGF0b3MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KDQojIFBpdm90YXIgbG9zIGRhdG9zDQpkYXRvcyA8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KDQojIEZpbHRyYXIgcG9yIGVsIHBlcmlvZG8gJzNfbWVzZXMnDQpkYXRvcyA8LSBmaWx0ZXIoZGF0b3MsIFBlcmlvZG8gPT0gIjNfYcOxb3MiKQ0Kb3JpZ2luYWxfZGYgIDwtIGRhdG9zWzk1Om5yb3coZGF0b3MpLCBdDQpvcmlnaW5hbF9kZiAgPC0gb3JpZ2luYWxfZGZbLC0yIF0NCg0Kb3JpZ2luYWxfZGYgPC0gZGF0YS5mcmFtZShmZWNoYSA9b3JpZ2luYWxfZGYkVGllbXBvLCB2YWxvcj1vcmlnaW5hbF9kZiRWYWxvciAgKQ0KDQpvcmlnaW5hbF9kZiR0aXBvICA8LSAnT3JpZ2luYWwnDQpjYWxjdWxhdGVkX2RmJHRpcG8gIDwtICdDYWxjdWxhdGVkJw0KY29tYmluZWRfZGYgPC0gcmJpbmQob3JpZ2luYWxfZGYsIGNhbGN1bGF0ZWRfZGYpDQpgYGANCg0KYGBge3J9DQojIENvbWJpbmFyIGxvcyBkYXRhIGZyYW1lcyBwYXJhIHF1ZSBsb3MgdmFsb3JlcyBjYWxjdWxhZG9zIHJlZW1wbGFjZW4gbG9zIG51bG9zIGVuIGxvcyBvcmlnaW5hbGVzDQpjb21iaW5lZF9kZiA8LSBvcmlnaW5hbF9kZiAlPiUNCiAgZnVsbF9qb2luKGNhbGN1bGF0ZWRfZGYsIGJ5ID0gImZlY2hhIiwgc3VmZml4ID0gYygiX29yaWdpbmFsIiwgIl9jYWxjdWxhdGVkIikpICU+JQ0KICBtdXRhdGUoDQogICAgdmFsb3IgPSBpZmVsc2UoaXMubmEodmFsb3Jfb3JpZ2luYWwpLCB2YWxvcl9jYWxjdWxhdGVkLCB2YWxvcl9vcmlnaW5hbCksDQogICAgdGlwbyA9IGlmZWxzZShpcy5uYSh2YWxvcl9vcmlnaW5hbCksICJJbXB1dGFkbyIsICJPcmlnaW5hbCIpDQogICkNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXIgZWwgZ3LDoWZpY28NCm1peDU8LWdncGxvdChjb21iaW5lZF9kZiwgYWVzKHggPSBmZWNoYSwgeSA9IHZhbG9yLCBjb2xvciA9IHRpcG8pKSArDQogIGdlb21fbGluZShzaXplID0gMSwgYWVzKGdyb3VwID0gMSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIk9yaWdpbmFsIiA9ICJibHVlIiwgIkltcHV0YWRvIiA9ICJyZWQiKSkgKw0KICBzY2FsZV9zaXplX2lkZW50aXR5KCkgKw0KICBsYWJzKHRpdGxlID0gIkJvbm9zIGEgMyBhw7FvcyIsIHggPSAiRmVjaGEiLCB5ID0gIlZhbG9yIikgKw0KICB0aGVtZV9taW5pbWFsKCkrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KYGBgDQoNCg0KIyMjTWl4IDUgYcOxb3MNCg0KYGBge3J9DQoNCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvcyRUaWVtcG8gPC0gYXMuRGF0ZShwYXN0ZTAoZGF0b3MkVGllbXBvLCAiXzAxIiksIGZvcm1hdCA9ICIlQl8lWV8lZCIpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYNV9hbm9zIiAsIF0NCmNhbGN1bGF0ZWRfZGYgIDwtIGRhdG9zWzk0Om5yb3coZGF0b3MpLCBdDQpjYWxjdWxhdGVkX2RmICA8LSBjYWxjdWxhdGVkX2RmWywtMiBdDQoNCmNhbGN1bGF0ZWRfZGYgPC0gZGF0YS5mcmFtZShmZWNoYSA9Y2FsY3VsYXRlZF9kZiRUaWVtcG8sIHZhbG9yPWNhbGN1bGF0ZWRfZGYkVmFsb3IgICkNCg0KIyBMZWVyIGxvcyBkYXRvcyBkZXNkZSBlbCBhcmNoaXZvIEV4Y2VsDQpkYXRvcyA8LSByZWFkLnhsc3goImRhdG9zLnhsc3giKQ0KDQojIENvbnZlcnRpciBsYSBjb2x1bW5hICdUaWVtcG8nIGEgZm9ybWF0byBkZSBmZWNoYQ0KZGF0b3MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KDQojIFBpdm90YXIgbG9zIGRhdG9zDQpkYXRvcyA8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KDQojIEZpbHRyYXIgcG9yIGVsIHBlcmlvZG8gJzNfbWVzZXMnDQpkYXRvcyA8LSBmaWx0ZXIoZGF0b3MsIFBlcmlvZG8gPT0gIjVfYcOxb3MiKQ0Kb3JpZ2luYWxfZGYgIDwtIGRhdG9zWzk0Om5yb3coZGF0b3MpLCBdDQpvcmlnaW5hbF9kZiAgPC0gb3JpZ2luYWxfZGZbLC0yIF0NCg0Kb3JpZ2luYWxfZGYgPC0gZGF0YS5mcmFtZShmZWNoYSA9b3JpZ2luYWxfZGYkVGllbXBvLCB2YWxvcj1vcmlnaW5hbF9kZiRWYWxvciAgKQ0KDQpvcmlnaW5hbF9kZiR0aXBvICA8LSAnT3JpZ2luYWwnDQpjYWxjdWxhdGVkX2RmJHRpcG8gIDwtICdDYWxjdWxhdGVkJw0KY29tYmluZWRfZGYgPC0gcmJpbmQob3JpZ2luYWxfZGYsIGNhbGN1bGF0ZWRfZGYpDQpgYGANCg0KYGBge3J9DQojIENvbWJpbmFyIGxvcyBkYXRhIGZyYW1lcyBwYXJhIHF1ZSBsb3MgdmFsb3JlcyBjYWxjdWxhZG9zIHJlZW1wbGFjZW4gbG9zIG51bG9zIGVuIGxvcyBvcmlnaW5hbGVzDQpjb21iaW5lZF9kZiA8LSBvcmlnaW5hbF9kZiAlPiUNCiAgZnVsbF9qb2luKGNhbGN1bGF0ZWRfZGYsIGJ5ID0gImZlY2hhIiwgc3VmZml4ID0gYygiX29yaWdpbmFsIiwgIl9jYWxjdWxhdGVkIikpICU+JQ0KICBtdXRhdGUoDQogICAgdmFsb3IgPSBpZmVsc2UoaXMubmEodmFsb3Jfb3JpZ2luYWwpLCB2YWxvcl9jYWxjdWxhdGVkLCB2YWxvcl9vcmlnaW5hbCksDQogICAgdGlwbyA9IGlmZWxzZShpcy5uYSh2YWxvcl9vcmlnaW5hbCksICJJbXB1dGFkbyIsICJPcmlnaW5hbCIpDQogICkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhciBlbCBncsOhZmljbw0KbWl4NjwtZ2dwbG90KGNvbWJpbmVkX2RmLCBhZXMoeCA9IGZlY2hhLCB5ID0gdmFsb3IsIGNvbG9yID0gdGlwbykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLCBhZXMoZ3JvdXAgPSAxKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiT3JpZ2luYWwiID0gImJsdWUiLCAiSW1wdXRhZG8iID0gInJlZCIpKSArDQogIHNjYWxlX3NpemVfaWRlbnRpdHkoKSArDQogIGxhYnModGl0bGUgPSAiQm9ub3MgYSA1IGHDsW9zIiwgeCA9ICJGZWNoYSIsIHkgPSAiVmFsb3IiKSArDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQpgYGANCg0KDQojIyMjIFVuaXIgZ3JhZmljb3MNCg0KDQoNCg0KIyMjTWl4IDEwIGHDsW9zDQoNCmBgYHtyfQ0KDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3MkVGllbXBvIDwtIGFzLkRhdGUocGFzdGUwKGRhdG9zJFRpZW1wbywgIl8wMSIpLCBmb3JtYXQgPSAiJUJfJVlfJWQiKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDEwX2Fub3MiICwgXQ0KY2FsY3VsYXRlZF9kZiAgPC0gZGF0b3NbOTg6bnJvdyhkYXRvcyksIF0NCmNhbGN1bGF0ZWRfZGYgIDwtIGNhbGN1bGF0ZWRfZGZbLC0yIF0NCg0KY2FsY3VsYXRlZF9kZiA8LSBkYXRhLmZyYW1lKGZlY2hhID1jYWxjdWxhdGVkX2RmJFRpZW1wbywgdmFsb3I9Y2FsY3VsYXRlZF9kZiRWYWxvciAgKQ0KDQojIExlZXIgbG9zIGRhdG9zIGRlc2RlIGVsIGFyY2hpdm8gRXhjZWwNCmRhdG9zIDwtIHJlYWQueGxzeCgiZGF0b3MueGxzeCIpDQoNCiMgQ29udmVydGlyIGxhIGNvbHVtbmEgJ1RpZW1wbycgYSBmb3JtYXRvIGRlIGZlY2hhDQpkYXRvcyRUaWVtcG8gPC0gYXMuRGF0ZShwYXN0ZTAoZGF0b3MkVGllbXBvLCAiXzAxIiksIGZvcm1hdCA9ICIlQl8lWV8lZCIpDQoNCiMgUGl2b3RhciBsb3MgZGF0b3MNCmRhdG9zIDwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQoNCiMgRmlsdHJhciBwb3IgZWwgcGVyaW9kbyAnM19tZXNlcycNCmRhdG9zIDwtIGZpbHRlcihkYXRvcywgUGVyaW9kbyA9PSAiMTBfYcOxb3MiKQ0Kb3JpZ2luYWxfZGYgIDwtIGRhdG9zWzk4Om5yb3coZGF0b3MpLCBdDQpvcmlnaW5hbF9kZiAgPC0gb3JpZ2luYWxfZGZbLC0yIF0NCg0Kb3JpZ2luYWxfZGYgPC0gZGF0YS5mcmFtZShmZWNoYSA9b3JpZ2luYWxfZGYkVGllbXBvLCB2YWxvcj1vcmlnaW5hbF9kZiRWYWxvciAgKQ0KDQpvcmlnaW5hbF9kZiR0aXBvICA8LSAnT3JpZ2luYWwnDQpjYWxjdWxhdGVkX2RmJHRpcG8gIDwtICdDYWxjdWxhdGVkJw0KY29tYmluZWRfZGYgPC0gcmJpbmQob3JpZ2luYWxfZGYsIGNhbGN1bGF0ZWRfZGYpDQpgYGANCg0KYGBge3J9DQojIENvbWJpbmFyIGxvcyBkYXRhIGZyYW1lcyBwYXJhIHF1ZSBsb3MgdmFsb3JlcyBjYWxjdWxhZG9zIHJlZW1wbGFjZW4gbG9zIG51bG9zIGVuIGxvcyBvcmlnaW5hbGVzDQpjb21iaW5lZF9kZiA8LSBvcmlnaW5hbF9kZiAlPiUNCiAgZnVsbF9qb2luKGNhbGN1bGF0ZWRfZGYsIGJ5ID0gImZlY2hhIiwgc3VmZml4ID0gYygiX29yaWdpbmFsIiwgIl9jYWxjdWxhdGVkIikpICU+JQ0KICBtdXRhdGUoDQogICAgdmFsb3IgPSBpZmVsc2UoaXMubmEodmFsb3Jfb3JpZ2luYWwpLCB2YWxvcl9jYWxjdWxhdGVkLCB2YWxvcl9vcmlnaW5hbCksDQogICAgdGlwbyA9IGlmZWxzZShpcy5uYSh2YWxvcl9vcmlnaW5hbCksICJJbXB1dGFkbyIsICJPcmlnaW5hbCIpDQogICkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBDcmVhciBlbCBncsOhZmljbw0KbWl4NzwtZ2dwbG90KGNvbWJpbmVkX2RmLCBhZXMoeCA9IGZlY2hhLCB5ID0gdmFsb3IsIGNvbG9yID0gdGlwbykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLCBhZXMoZ3JvdXAgPSAxKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiT3JpZ2luYWwiID0gImJsdWUiLCAiSW1wdXRhZG8iID0gInJlZCIpKSArDQogIHNjYWxlX3NpemVfaWRlbnRpdHkoKSArDQogIGxhYnModGl0bGUgPSAiT2JsaWdhY2lvbmVzIGEgMTAgYcOxb3MiLCB4ID0gIkZlY2hhIiwgeSA9ICJWYWxvciIpICsNCiAgdGhlbWVfbWluaW1hbCgpKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCmBgYA0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KIyBKdW50YXIgbG9zIGdyw6FmaWNvcw0KbGlicmFyeShncmlkRXh0cmEpDQpncmlkLmFycmFuZ2UodG9wID0gIiBWYWxvcmVzIGltcHV0YWRvcyBlbiBsYXMgQm9ub3MgZGVsIEVzdGFkbyAyMDAwLTIwMjQiLCBtaXg1LCBtaXg2LCBuY29sID0gMikNCmBgYA0KDQoNCiMgNS4yLjIgTW9kZWxvcyBkZSBwcmVkaWNjacOzbg0KDQojIDUuMi4yLjEgIE1vZGVsb3MgZGUgYWxQQ0ENCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KHRzaW50ZXJtaXR0ZW50KTsgbGlicmFyeShubmZvcikNCmxpYnJhcnkoTWV0cmljcyk7IGxpYnJhcnkoZm9yZWNhc3QpOyBsaWJyYXJ5KHRzZXJpZXMpO2xpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSk7IGxpYnJhcnkocHJvcGhldCkNCmxpYnJhcnkoZm9yZWNUaGV0YSk7bGlicmFyeSh0c2Zncm5uKSANCmxpYnJhcnkoeWFnZXIpO2xpYnJhcnkodW5pdmFyaWF0ZU1MKTsNCmxpYnJhcnkodGhpZWYpDQpgYGANCg0KRnVuY2lvbmVzIHF1ZSB1dGlsaXphIGVsIG1vZGVsbyBhbFBDQQ0KDQpgYGB7cn0NCg0KY2FsY3VsbzE8LWZ1bmN0aW9uKGopew0KICAjIFdlIGRlZmluZSB0aGUgZ2VuZXJhbCBwYXJhbWV0ZXJzDQogIHNlcmllPC1lbnNheW9bal0NCiAgbl9wPC0xMiAgDQogIG48LWxlbmd0aChkYXRvc1tbc2VyaWVdXSkgDQogIGVzdGFjPC0xMiAjbW9udGhseSBzZWFzb25hbGl0eQ0KICBjb21pZW56byA8LSBzdGFydChkYXRvc1tbc2VyaWVdXSkNCiAgDQogICMgV2Ugc2VsZWN0IHRoZSBwZXJpb2QgVDEoeCkgVDIoeCkNCiAgeCAgPC0gdHMoZGF0b3NbW3NlcmllXV1bMToobi0oMipuX3ApKV0sc3RhcnQgPSBjb21pZW56bywgZnJlcXVlbmN5ID0gZXN0YWMpICNUMQ0KICBpZihlbmQoeClbMl09PWVzdGFjKSANCiAge2NvbWllbnpvMjwtYyhlbmQoeClbMV0rMSwxKQ0KICB9ZWxzZSANCiAge2NvbWllbnpvMjwtYyhlbmQoeClbMV0sZW5kKHgpWzJdKzEpfQ0KICB4MSA8LSB0cyhkYXRvc1tbc2VyaWVdXVsobi0oMipuX3ApKzEpOihuLW5fcCldLCBzdGFydCA9IGNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgI1QyDQogIA0KICBzZXQuc2VlZCg4NDYpIA0KICBtZXRvZG9zPC1saXN0KCkNCiAgI05hw692ZQ0KICBtZXRvZG9zW1sxXV08LW5haXZlKHgsaD1uX3ApDQogICNOYcOvdmUgd2l0aCBzZWFzb25hbGl0eQ0KICBtZXRvZG9zW1syXV08LXNuYWl2ZSh4LGg9bl9wKQ0KICAjRXhwb25lbnRpYWwgc21vb3RoaW5nDQogIG1ldG9kb3NbWzNdXTwtZXRzKHgsbW9kZWwgPSAiWlpaIiwgb3B0LmNyaXQ9ImxpayIsIGRhbXBlZCA9IE5VTEwpDQogIG1ldG9kb3NbWzNdXTwtcHJlZGljdChtZXRvZG9zW1szXV0sbl9wKQ0KICBtZXRvZG9zW1s0XV08LWV0cyh4LG1vZGVsID0gIlpaWiIsIG9wdC5jcml0PSJtc2UiLCBkYW1wZWQgPSBOVUxMKQ0KICBtZXRvZG9zW1s0XV08LXByZWRpY3QobWV0b2Rvc1tbNF1dLG5fcCkNCiAgbWV0b2Rvc1tbNV1dPC1ldHMoeCxtb2RlbCA9ICJaWloiLCBvcHQuY3JpdD0iYW1zZSIsIGRhbXBlZCA9IE5VTEwpDQogIG1ldG9kb3NbWzVdXTwtcHJlZGljdChtZXRvZG9zW1s1XV0sbl9wKQ0KICBtZXRvZG9zW1s2XV08LWV0cyh4LG1vZGVsID0gIlpaWiIsIG9wdC5jcml0PSJzaWdtYSIsIGRhbXBlZCA9IE5VTEwpDQogIG1ldG9kb3NbWzZdXTwtcHJlZGljdChtZXRvZG9zW1s2XV0sbl9wKQ0KICBtZXRvZG9zW1s3XV08LWV0cyh4LG1vZGVsID0gIlpaWiIsIG9wdC5jcml0PSJtYWUiLCBkYW1wZWQgPSBOVUxMKQ0KICBtZXRvZG9zW1s3XV08LXByZWRpY3QobWV0b2Rvc1tbN11dLG5fcCkNCiAgbWV0b2Rvc1tbOF1dPC1ldHMoeCxtb2RlbCA9ICJaWloiLCBvcHQuY3JpdD0ibGlrIiwgZGFtcGVkID0gVFJVRSkNCiAgbWV0b2Rvc1tbOF1dPC1wcmVkaWN0KG1ldG9kb3NbWzhdXSxuX3ApDQogIG1ldG9kb3NbWzldXTwtZXRzKHgsbW9kZWwgPSAiWlpaIiwgb3B0LmNyaXQ9Im1zZSIsIGRhbXBlZCA9IFRSVUUpDQogIG1ldG9kb3NbWzldXTwtcHJlZGljdChtZXRvZG9zW1s5XV0sbl9wKQ0KICBtZXRvZG9zW1sxMF1dPC1ldHMoeCxtb2RlbCA9ICJaWloiLCBvcHQuY3JpdD0iYW1zZSIsIGRhbXBlZCA9IFRSVUUpDQogIG1ldG9kb3NbWzEwXV08LXByZWRpY3QobWV0b2Rvc1tbMTBdXSxuX3ApDQogIG1ldG9kb3NbWzExXV08LWV0cyh4LG1vZGVsID0gIlpaWiIsIG9wdC5jcml0PSJzaWdtYSIsIGRhbXBlZCA9IFRSVUUpDQogIG1ldG9kb3NbWzExXV08LXByZWRpY3QobWV0b2Rvc1tbMTFdXSxuX3ApDQogIG1ldG9kb3NbWzEyXV08LWV0cyh4LG1vZGVsID0gIlpaWiIsIG9wdC5jcml0PSJtYWUiLCBkYW1wZWQgPSBUUlVFKQ0KICBtZXRvZG9zW1sxMl1dPC1wcmVkaWN0KG1ldG9kb3NbWzEyXV0sbl9wKQ0KICAjQVJJTUENCiAgYTwtTlVMTA0KICBtZXRvZG9zW1sxM11dPC1hdXRvLmFyaW1hKHgsIGljPSJhaWMiLGFwcHJveGltYXRpb249VCwgdHJhY2U9RkFMU0UsIGFsbG93ZHJpZnQ9RikNCiAgYTwtcHJlZGljdChtZXRvZG9zW1sxM11dLG5fcCkNCiAgbWV0b2Rvc1tbMTNdXSRtZWFuPC1hJHByZWQNCiAgbWV0b2Rvc1tbMTRdXTwtYXV0by5hcmltYSh4LCBpYz0iYmljIixhcHByb3hpbWF0aW9uPVQsIHRyYWNlPUZBTFNFLCBhbGxvd2RyaWZ0PUYpDQogIGE8LXByZWRpY3QobWV0b2Rvc1tbMTRdXSxuX3ApDQogIG1ldG9kb3NbWzE0XV0kbWVhbjwtYSRwcmVkDQogICNUaGV0YQ0KICBtZXRvZG9zW1sxNV1dPC1vdG0uYXJ4aXYoeCwgaD1uX3AsIHM9IE5VTEwsIGc9ICJzQVBFIikNCiAgbWV0b2Rvc1tbMTZdXTwtZG90bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJOZWxkZXItTWVhZCIpICNEeW5hbWljIE9wdGltaXNlZCBUaGV0YSBNb2RlbA0KICBtZXRvZG9zW1sxN11dPC1kb3RtKHgsIGg9bl9wLCBzPSBOVUxMLCBlc3RpbWF0aW9uPVRSVUUsIG9wdC5tZXRob2Q9IkwtQkZHUy1CIikgI0R5bmFtaWMgT3B0aW1pc2VkIFRoZXRhIE1vZGVsDQogIG1ldG9kb3NbWzE4XV08LWRvdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iU0FOTiIpICNEeW5hbWljIE9wdGltaXNlZCBUaGV0YSBNb2RlbA0KICBtZXRvZG9zW1sxOV1dPC1kc3RtKHgsIGg9bl9wLCBzPSBOVUxMLCBlc3RpbWF0aW9uPVRSVUUsIG9wdC5tZXRob2Q9Ik5lbGRlci1NZWFkIikgI0R5bmFtaWMgU3RhbmRhcmQgVGhldGEgTW9kZWwNCiAgbWV0b2Rvc1tbMjBdXTwtZHN0bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJMLUJGR1MtQiIpICNEeW5hbWljIFN0YW5kYXJkIFRoZXRhIE1vZGVsDQogIG1ldG9kb3NbWzIxXV08LWRzdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iU0FOTiIpICNEeW5hbWljIFN0YW5kYXJkIFRoZXRhIE1vZGVsDQogIG1ldG9kb3NbWzIyXV08LW90bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJOZWxkZXItTWVhZCIpICAjT3B0aW1pc2VkIFRoZXRhIE1vZGVsDQogIG1ldG9kb3NbWzIzXV08LW90bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJMLUJGR1MtQiIpICAjT3B0aW1pc2VkIFRoZXRhIE1vZGVsDQogIG1ldG9kb3NbWzI0XV08LW90bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJTQU5OIikgICNPcHRpbWlzZWQgVGhldGEgTW9kZWwNCiAgbWV0b2Rvc1tbMjVdXTwtc3RtKHgsIGg9bl9wLCBzPSBOVUxMLCBlc3RpbWF0aW9uPVRSVUUsIG9wdC5tZXRob2Q9Ik5lbGRlci1NZWFkIikgICNTdGFuZGFyZCBUaGV0YSBNb2RlbCAoRmlvcnVjY2kpDQogIG1ldG9kb3NbWzI2XV08LXN0bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJMLUJGR1MtQiIpICAjU3RhbmRhcmQgVGhldGEgTW9kZWwgKEZpb3J1Y2NpKQ0KICBtZXRvZG9zW1syN11dPC1zdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iU0FOTiIpICAjU3RhbmRhcmQgVGhldGEgTW9kZWwgKEZpb3J1Y2NpKQ0KICBtZXRvZG9zW1syOF1dPC1zdGhldGEoeCwgaD1uX3AsIHM9IE5VTEwpICNTdGFuZGFyZCBUaGV0YSBNb2RlbCAoQXNzaW1ha29wb3Vsb3MpDQogICNTVEwgKHPDs2xvIHNlcmllcyBlc3RhY2lvbmFsZXMpDQogIG1ldG9kb3NbWzI5XV08LXN0bGYoeCxoPW5fcCwgbWV0aG9kPWMoImV0cyIpLHJvYnVzdCA9IFRSVUUsYWxsb3cubXVsdGlwbGljYXRpdmUudHJlbmQgPSBUUlVFKQ0KICBtZXRvZG9zW1szMF1dPC1zdGxmKHgsaD1uX3AsIG1ldGhvZD1jKCJhcmltYSIpLHJvYnVzdCA9IFRSVUUsYWxsb3cubXVsdGlwbGljYXRpdmUudHJlbmQgPSBUUlVFKQ0KICBtZXRvZG9zW1szMV1dPC1zdGxmKHgsaD1uX3AsIGZvcmVjYXN0ZnVuY3Rpb249dGhldGFmLHJvYnVzdCA9IFRSVUUsYWxsb3cubXVsdGlwbGljYXRpdmUudHJlbmQgPSBUUlVFKQ0KICAjQ3Jvc3Rvbg0KICBtZXRvZG9zW1szMl1dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAiY3Jvc3RvbiIsY29zdCA9ICJtYXIiLGluaXQub3B0PVRSVUUpDQogIG1ldG9kb3NbWzMyXV0kZml0dGVkPC10cyhtZXRvZG9zW1szMl1dJGZyYy5pbiwgc3RhcnQgPWNvbWllbnpvLGZyZXF1ZW5jeSA9IGVzdGFjKSANCiAgbWV0b2Rvc1tbMzJdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzMyXV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzMzXV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJjcm9zdG9uIixjb3N0ID0gIm1zciIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbMzNdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzMzXV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1szM11dJG1lYW48LSAgdHMobWV0b2Rvc1tbMzNdXSRmcmMub3V0LCBzdGFydCA9Y29taWVuem8yLGZyZXF1ZW5jeSA9IGVzdGFjKSANCiAgbWV0b2Rvc1tbMzRdXTwtY3Jvc3QoeCxoPW5fcCx0eXBlID0gImNyb3N0b24iLGNvc3QgPSAibWFlIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1szNF1dJGZpdHRlZDwtdHMobWV0b2Rvc1tbMzRdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbMzRdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzM0XV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzM1XV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJjcm9zdG9uIixjb3N0ID0gIm1zZSIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbMzVdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzM1XV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzM1XV0kbWVhbjwtICB0cyhtZXRvZG9zW1szNV1dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1szNl1dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAic2JhIixjb3N0ID0gIm1hciIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbMzZdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzM2XV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzM2XV0kbWVhbjwtICB0cyhtZXRvZG9zW1szNl1dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1szN11dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAic2JhIixjb3N0ID0gIm1zciIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbMzddXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzM3XV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzM3XV0kbWVhbjwtICB0cyhtZXRvZG9zW1szN11dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1szOF1dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAic2JhIixjb3N0ID0gIm1hZSIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbMzhdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzM4XV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzM4XV0kbWVhbjwtICB0cyhtZXRvZG9zW1szOF1dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1szOV1dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAic2JhIixjb3N0ID0gIm1zZSIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbMzldXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzM5XV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzM5XV0kbWVhbjwtICB0cyhtZXRvZG9zW1szOV1dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1s0MF1dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAic2JqIixjb3N0ID0gIm1hciIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbNDBdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzQwXV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzQwXV0kbWVhbjwtICB0cyhtZXRvZG9zW1s0MF1dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1s0MV1dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAic2JqIixjb3N0ID0gIm1zciIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbNDFdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzQxXV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzQxXV0kbWVhbjwtICB0cyhtZXRvZG9zW1s0MV1dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1s0Ml1dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAic2JqIixjb3N0ID0gIm1hZSIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbNDJdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzQyXV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzQyXV0kbWVhbjwtICB0cyhtZXRvZG9zW1s0Ml1dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1s0M11dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAic2JqIixjb3N0ID0gIm1zZSIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbNDNdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzQzXV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzQzXV0kbWVhbjwtICB0cyhtZXRvZG9zW1s0M11dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICAjUHJvcGhldA0KICBzZXQuc2VlZCg1MCkNCiAgZGY8LWRhdGEuZnJhbWUoZHMgPSBzZXEoYXMuRGF0ZShwYXN0ZShzdGFydCh4KVsxXSwiLSIsc3RhcnQoeClbMl0sIi0iLCIwMSIsc2VwPSIiKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFzLkRhdGUocGFzdGUoZW5kKHgpWzFdLCItIixlbmQoeClbMl0sIi0iLCIwMSIsc2VwPSIiKSksYnk9Im0iKSwgeSA9IHgpDQogIG1ldG9kb3NbWzQ0XV08LXByb3BoZXQoZGYseWVhcmx5LnNlYXNvbmFsaXR5PVRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgd2Vla2x5LnNlYXNvbmFsaXR5ID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgZGFpbHkuc2Vhc29uYWxpdHkgPSBGQUxTRSxzZWFzb25hbGl0eS5tb2RlID0gImFkZGl0aXZlIikNCiAgYTwtbWFrZV9mdXR1cmVfZGF0YWZyYW1lKG1ldG9kb3NbWzQ0XV0scGVyaW9kcyA9IDE4LGZyZXEgPSAibW9udGgiKQ0KICBiPC1wcmVkaWN0KG1ldG9kb3NbWzQ0XV0sYSkNCiAgbWV0b2Rvc1tbNDRdXSRmaXR0ZWQgPC0gdHMoYiR5aGF0WzE6bGVuZ3RoKHgpXSxzdGFydCA9IGNvbWllbnpvLGZyZXF1ZW5jeSA9IGVzdGFjKQ0KICBtZXRvZG9zW1s0NF1dJG1lYW4gPC0gdHMoYiR5aGF0WyhsZW5ndGgoeCkrMSk6KGxlbmd0aCh4KStuX3ApXSxzdGFydCA9IGNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbNDVdXTwtcHJvcGhldChkZix5ZWFybHkuc2Vhc29uYWxpdHk9VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB3ZWVrbHkuc2Vhc29uYWxpdHkgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYWlseS5zZWFzb25hbGl0eSA9IEZBTFNFLHNlYXNvbmFsaXR5Lm1vZGUgPSAibXVsdGlwbGljYXRpdmUiKQ0KICBhPC1tYWtlX2Z1dHVyZV9kYXRhZnJhbWUobWV0b2Rvc1tbNDVdXSxwZXJpb2RzID0gMTgsZnJlcSA9ICJtb250aCIpDQogIGI8LXByZWRpY3QobWV0b2Rvc1tbNDVdXSxhKQ0KICBtZXRvZG9zW1s0NV1dJGZpdHRlZCA8LSB0cyhiJHloYXRbMTpsZW5ndGgoeCldLHN0YXJ0ID0gY29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzQ1XV0kbWVhbiA8LSB0cyhiJHloYXRbKGxlbmd0aCh4KSsxKToobGVuZ3RoKHgpK25fcCldLHN0YXJ0ID0gY29taWVuem8yLGZyZXF1ZW5jeSA9IGVzdGFjKQ0KICBzZXQuc2VlZCg1MCkNCiAgbWV0b2Rvc1tbNDZdXTwtbm5ldGFyKHgscD1lc3RhYyxzY2FsZS5pbnB1dHM9VCkNCiAgYTwtZm9yZWNhc3QobWV0b2Rvc1tbNDZdXSxoPW5fcCkNCiAgbWV0b2Rvc1tbNDZdXSRtZWFuPC1hJG1lYW4gIA0KICAjVEJBVFMgbW9kZWwgKEV4cG9uZW50aWFsIHNtb290aGluZyBzdGF0ZSBzcGFjZSBtb2RlbCB3aXRoIEJveC1Db3ggdHJhbnNmb3JtYXRpb24sIEFSTUEgZXJyb3JzLCBUcmVuZCBhbmQgU2Vhc29uYWwgY29tcG9uZW50cykNCiAgc2V0LnNlZWQoNTApDQogIG1ldG9kb3NbWzQ3XV08LXRiYXRzKHgsIGJpYXNhZGogPSBUUlVFKQ0KICBtZXRvZG9zW1s0N11dJGZpdHRlZDwtbWV0b2Rvc1tbNDddXSRmaXR0ZWQudmFsdWVzDQogIGE8LWZvcmVjYXN0KG1ldG9kb3NbWzQ3XV0saD1uX3ApDQogIG1ldG9kb3NbWzQ3XV0kbWVhbjwtYSRtZWFuDQogICNHUk5OOiBnZW5lcmFsIHJlZ3Jlc3Npb24gbmV1cmFsIG5ldHdvcmsgDQogIHNldC5zZWVkKDUwKQ0KICBtZXRvZG9zW1s0OF1dPC1ncm5uX2ZvcmVjYXN0aW5nKHgsIGggPSBuX3AsIHRyYW5zZm9ybSA9ICJhZGRpdGl2ZSIpDQogIGE8LXJvbGxpbmdfb3JpZ2luKG1ldG9kb3NbWzQ4XV0saCA9IGxlbmd0aCh4KS1lc3RhYy0xKQ0KICBtZXRvZG9zW1s0OF1dJGZpdHRlZDwtYSRwcmVkaWN0aW9uc1ssMV0NCiAgbWV0b2Rvc1tbNDhdXSRtZWFuPC1tZXRvZG9zW1s0OF1dJHByZWRpY3Rpb24NCiAgbWV0b2Rvc1tbNDldXTwtZ3Jubl9mb3JlY2FzdGluZyh4LCBoID0gbl9wLCB0cmFuc2Zvcm0gPSAibXVsdGlwbGljYXRpdmUiKQ0KICBhPC1yb2xsaW5nX29yaWdpbihtZXRvZG9zW1s0OV1dLGggPSBsZW5ndGgoeCktZXN0YWMtMSkNCiAgbWV0b2Rvc1tbNDldXSRmaXR0ZWQ8LWEkcHJlZGljdGlvbnNbLDFdDQogIG1ldG9kb3NbWzQ5XV0kbWVhbjwtbWV0b2Rvc1tbNDldXSRwcmVkaWN0aW9uDQogICNNTFA6IG11bHRpbGF5ZXIgcGVyY2VwdHJvbg0KICBzZXQuc2VlZCg1MCkNCiAgbWV0b2Rvc1tbNTBdXTwtbWxwLnRoaWVmKHgsIGg9MTIpDQogIG1ldG9kb3NbWzUxXV08LW1scCh4LCBtPTEyLCBjb21iID0gYygibWVkaWFuIiksaGQgPSBjKDEwLDUpKQ0KICBhPC1mb3JlY2FzdChtZXRvZG9zW1s1MV1dLGg9MTIpDQogIG1ldG9kb3NbWzUxXV0kbWVhbjwtYSRtZWFuDQogIG1ldG9kb3NbWzUyXV08LW1scCh4LCBtPTEyLCBjb21iID0gYygibWVhbiIpLGhkID0gYygxMCw1KSkNCiAgYTwtZm9yZWNhc3QobWV0b2Rvc1tbNTJdXSxoPTEyKQ0KICBtZXRvZG9zW1s1Ml1dJG1lYW48LWEkbWVhbg0KICBnYygpDQogIHJldHVybihtZXRvZG9zKQ0KfQ0KDQpjYWxjdWxvMjwtIGZ1bmN0aW9uKGopIHsNCiAgIyBXZSBkZWZpbmUgdGhlIGdlbmVyYWwgcGFyYW1ldGVycw0KICBzZXJpZTwtZW5zYXlvW2pdDQogIG5fcDwtMTIgDQogIG48LWxlbmd0aChkYXRvc1tbc2VyaWVdXSkgI27CuiBkZSBkYXRvcw0KICBlc3RhYzwtMTIgDQogIGNvbWllbnpvIDwtIHN0YXJ0KGRhdG9zW1tzZXJpZV1dKQ0KICANCiAgIyBXZSBzZWxlY3QgdGhlIHBlcmlvZCBUMSh4KSBUMih4KQ0KICB4ICA8LSB0cyhkYXRvc1tbc2VyaWVdXVsxOihuLSgyKm5fcCkpXSxzdGFydCA9IGNvbWllbnpvLCBmcmVxdWVuY3kgPSBlc3RhYykgI1QxDQogIGlmKGVuZCh4KVsyXT09ZXN0YWMpIA0KICB7Y29taWVuem8yPC1jKGVuZCh4KVsxXSsxLDEpDQogIH1lbHNlIA0KICB7Y29taWVuem8yPC1jKGVuZCh4KVsxXSxlbmQoeClbMl0rMSl9DQogIHgxIDwtIHRzKGRhdG9zW1tzZXJpZV1dWyhuLSgyKm5fcCkrMSk6KG4tbl9wKV0sIHN0YXJ0ID0gY29taWVuem8yLGZyZXF1ZW5jeSA9IGVzdGFjKSAjVDINCiAgDQogICNzTUFQRSBpbiBUMTogSW4gc29tZSBjYXNlcyBpdCBpcyBzdHJhaWdodGZvcndhcmQsIGJ1dCBpbiBvdGhlcnMgd2UgbG9zZSBpbml0aWFsIHZhbHVlcy4NCiAgbWV0b2Rvcy5UMVtbal1dW1sxXV0kc01hcGVUMSA8LSAxMDAqTWV0cmljczo6c21hcGUoeFsyOmxlbmd0aCh4KV0sbWV0b2Rvcy5UMVtbal1dW1sxXV0kZml0dGVkWzI6bGVuZ3RoKHgpXSkNCiAgbWV0b2Rvcy5UMVtbal1dW1syXV0kc01hcGVUMSA8LSAxMDAqTWV0cmljczo6c21hcGUoeFsoZXN0YWMrMSk6bGVuZ3RoKHgpXSxtZXRvZG9zLlQxW1tqXV1bWzJdXSRmaXR0ZWRbKGVzdGFjKzEpOmxlbmd0aCh4KV0pDQogIGZvciAoaSBpbiAxOjI0KSB7DQogICAgbWV0b2Rvcy5UMVtbal1dW1syK2ldXSRzTWFwZVQxIDwtIDEwMCpNZXRyaWNzOjpzbWFwZSh4LG1ldG9kb3MuVDFbW2pdXVtbMitpXV0kZml0dGVkKQ0KICB9DQogIGZvciAoaSBpbiAxOjE3KSB7DQogICAgbWV0b2Rvcy5UMVtbal1dW1syNitpXV0kc01hcGVUMSA8LSAxMDAqTWV0cmljczo6c21hcGUoeFsyOmxlbmd0aCh4KV0sbWV0b2Rvcy5UMVtbal1dW1syNitpXV0kZml0dGVkWzI6bGVuZ3RoKHgpXSkNCiAgfQ0KICBmb3IgKGkgaW4gMToyKSB7DQogICAgbWV0b2Rvcy5UMVtbal1dW1s0MytpXV0kc01hcGVUMSA8LSAxMDAqTWV0cmljczo6c21hcGUoeCxtZXRvZG9zLlQxW1tqXV1bWzQzK2ldXSRmaXR0ZWQpDQogIH0NCiAgbWV0b2Rvcy5UMVtbal1dW1s0Nl1dJHNNYXBlVDEgPC0gMTAwKk1ldHJpY3M6OnNtYXBlKHhbKGVzdGFjKzEpOmxlbmd0aCh4KV0sbWV0b2Rvcy5UMVtbal1dW1s0Nl1dJGZpdHRlZFsoZXN0YWMrMSk6bGVuZ3RoKHgpXSkNCiAgbWV0b2Rvcy5UMVtbal1dW1s0N11dJHNNYXBlVDEgPC0gMTAwKk1ldHJpY3M6OnNtYXBlKHgsbWV0b2Rvcy5UMVtbal1dW1s0N11dJGZpdHRlZCkNCiAgZm9yIChpIGluIDE6Mikgew0KICAgIG1ldG9kb3MuVDFbW2pdXVtbNDcraV1dJHNNYXBlVDEgPC0gMTAwKk1ldHJpY3M6OnNtYXBlKHhbMToobGVuZ3RoKHgpLWVzdGFjLTEpXSxtZXRvZG9zLlQxW1tqXV1bWzQ3K2ldXSRmaXR0ZWQpDQogIH0NCiAgbWV0b2Rvcy5UMVtbal1dW1s1MF1dJHNNYXBlVDEgPC0gMTAwKk1ldHJpY3M6OnNtYXBlKHhbKGVzdGFjKzMpOmxlbmd0aCh4KV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRvZG9zLlQxW1tqXV1bWzUwXV0kZml0dGVkWyhlc3RhYyszKTpsZW5ndGgoeCldKQ0KICBtZXRvZG9zLlQxW1tqXV1bWzUxXV0kc01hcGVUMSA8LSAxMDAqTWV0cmljczo6c21hcGUoeCxtZXRvZG9zLlQxW1tqXV1bWzUxXV0kZml0dGVkKQ0KICBtZXRvZG9zLlQxW1tqXV1bWzUyXV0kc01hcGVUMSA8LSAxMDAqTWV0cmljczo6c21hcGUoeCxtZXRvZG9zLlQxW1tqXV1bWzUyXV0kZml0dGVkKQ0KICAjTUFTRSBpbiBUMTogSW4gc29tZSBjYXNlcyBpdCBpcyBzdHJhaWdodGZvcndhcmQsIGJ1dCBpbiBvdGhlcnMgd2UgbG9zZSBpbml0aWFsIHZhbHVlcy4NCiAgbWV0b2Rvcy5UMVtbal1dW1sxXV0kbWFzZVQxIDwtIE1ldHJpY3M6Om1hc2UoeFsyOmxlbmd0aCh4KV0sbWV0b2Rvcy5UMVtbal1dW1sxXV0kZml0dGVkWzI6bGVuZ3RoKHgpXSxzdGVwX3NpemU9MTIpDQogIG1ldG9kb3MuVDFbW2pdXVtbMl1dJG1hc2VUMSA8LSBNZXRyaWNzOjptYXNlKHhbKGVzdGFjKzEpOmxlbmd0aCh4KV0sbWV0b2Rvcy5UMVtbal1dW1syXV0kZml0dGVkWyhlc3RhYysxKTpsZW5ndGgoeCldLHN0ZXBfc2l6ZT0xMikNCiAgZm9yIChpIGluIDE6MjQpIHsNCiAgICBtZXRvZG9zLlQxW1tqXV1bWzIraV1dJG1hc2VUMSA8LSBNZXRyaWNzOjptYXNlKHgsbWV0b2Rvcy5UMVtbal1dW1syK2ldXSRmaXR0ZWQsc3RlcF9zaXplPTEyKQ0KICB9DQogIGZvciAoaSBpbiAxOjE3KSB7DQogICAgbWV0b2Rvcy5UMVtbal1dW1syNitpXV0kbWFzZVQxIDwtIE1ldHJpY3M6Om1hc2UoeFsyOmxlbmd0aCh4KV0sbWV0b2Rvcy5UMVtbal1dW1syNitpXV0kZml0dGVkWzI6bGVuZ3RoKHgpXSxzdGVwX3NpemU9MTIpDQogIH0NCiAgZm9yIChpIGluIDE6Mikgew0KICAgIG1ldG9kb3MuVDFbW2pdXVtbNDMraV1dJG1hc2VUMSA8LSBNZXRyaWNzOjptYXNlKHgsbWV0b2Rvcy5UMVtbal1dW1s0MytpXV0kZml0dGVkLHN0ZXBfc2l6ZT0xMikNCiAgfQ0KICBtZXRvZG9zLlQxW1tqXV1bWzQ2XV0kbWFzZVQxIDwtIE1ldHJpY3M6Om1hc2UoeFsoZXN0YWMrMSk6bGVuZ3RoKHgpXSxtZXRvZG9zLlQxW1tqXV1bWzQ2XV0kZml0dGVkWyhlc3RhYysxKTpsZW5ndGgoeCldLHN0ZXBfc2l6ZT0xMikNCiAgbWV0b2Rvcy5UMVtbal1dW1s0N11dJG1hc2VUMSA8LSBNZXRyaWNzOjptYXNlKHgsbWV0b2Rvcy5UMVtbal1dW1s0N11dJGZpdHRlZCxzdGVwX3NpemU9MTIpDQogIGZvciAoaSBpbiAxOjIpIHsNCiAgICBtZXRvZG9zLlQxW1tqXV1bWzQ3K2ldXSRtYXNlVDEgPC0gTWV0cmljczo6bWFzZSh4WzE6KGxlbmd0aCh4KS1lc3RhYy0xKV0sbWV0b2Rvcy5UMVtbal1dW1s0NytpXV0kZml0dGVkLHN0ZXBfc2l6ZT0xMikNCiAgfQ0KICBtZXRvZG9zLlQxW1tqXV1bWzUwXV0kbWFzZVQxIDwtIE1ldHJpY3M6Om1hc2UoeFsoZXN0YWMrMyk6bGVuZ3RoKHgpXSxtZXRvZG9zLlQxW1tqXV1bWzUwXV0kZml0dGVkWyhlc3RhYyszKTpsZW5ndGgoeCldKQ0KICBtZXRvZG9zLlQxW1tqXV1bWzUxXV0kbWFzZVQxIDwtIE1ldHJpY3M6Om1hc2UoeCwgbWV0b2Rvcy5UMVtbal1dW1s1MV1dJGZpdHRlZCkNCiAgbWV0b2Rvcy5UMVtbal1dW1s1Ml1dJG1hc2VUMSA8LSBNZXRyaWNzOjptYXNlKHgsIG1ldG9kb3MuVDFbW2pdXVtbNTJdXSRmaXR0ZWQpDQogICNzTUFQRSBpbiBUMg0KICBmb3IgKGkgaW4gMTpsZW5ndGgobWV0b2Rvcy5UMVtbMV1dKSkgew0KICAgIG1ldG9kb3MuVDFbW2pdXVtbaV1dJHNNYXBlVDIgPC0gMTAwKk1ldHJpY3M6OnNtYXBlKHgxLG1ldG9kb3MuVDFbW2pdXVtbaV1dJG1lYW4pDQogIH0NCiAgI1JNU0UgaW4gVDINCiAgZm9yIChpIGluIDE6bGVuZ3RoKG1ldG9kb3MuVDFbWzFdXSkpIHsNCiAgICBtZXRvZG9zLlQxW1tqXV1bW2ldXSRybXNlVDIgPC0gTWV0cmljczo6cm1zZSh4MSxtZXRvZG9zLlQxW1tqXV1bW2ldXSRtZWFuKQ0KICB9DQogICNtYXNlIGVuIFQyDQogIGZvciAoaSBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1sxXV0pKSB7DQogICAgbWV0b2Rvcy5UMVtbal1dW1tpXV0kbWFzZVQyIDwtIE1ldHJpY3M6Om1hc2UoeDEsbWV0b2Rvcy5UMVtbal1dW1tpXV0kbWVhbixzdGVwX3NpemU9MSkNCiAgfQ0KICAjT1dBIGluIFQyDQogIGZvciAoaSBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1sxXV0pKSB7DQogICAgbWV0b2Rvcy5UMVtbal1dW1tpXV0kb3dhVDIgPC0gKChtZXRvZG9zLlQxW1tqXV1bW2ldXSRtYXNlVDIgLyBtZXRvZG9zLlQxW1tqXV1bWzJdXSRtYXNlVDIpKyhtZXRvZG9zLlQxW1tqXV1bW2ldXSRzTWFwZVQyIC8gbWV0b2Rvcy5UMVtbal1dW1syXV0kc01hcGVUMikpLzINCiAgfQ0KICByZXR1cm4obWV0b2Rvcy5UMVtbal1dKQ0KfSAgDQoNCmNhbGN1bG8zPC1mdW5jdGlvbihqKXsNCiAgZXJyb3Jlc1QyW1tqXV08LW1hdHJpeChOQSxucm93ID0gbGVuZ3RoKG1ldG9kb3MuVDFbWzFdXSksbmNvbCA9IDEzKQ0KICBjb2xuYW1lcyhlcnJvcmVzVDJbW2pdXSk8LWMoImlkIiwic21hcGVUMSIsIm1hc2VUMSIsInJtc2UiLCJzbWFwZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibWFzZSIsIm93YSIsIkNQMSIsIkNQMiIsICJkaXN0IiwgIncxIiwidzIiLCJ3MyIpDQogIGZvciAoaSBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1sxXV0pKSB7DQogICAgZXJyb3Jlc1QyW1tqXV1baSwxXTwtaQ0KICAgIGVycm9yZXNUMltbal1dW2ksMl08LW1ldG9kb3MuVDFbW2pdXVtbaV1dJHNNYXBlVDENCiAgICBlcnJvcmVzVDJbW2pdXVtpLDNdPC1tZXRvZG9zLlQxW1tqXV1bW2ldXSRtYXNlVDENCiAgICBlcnJvcmVzVDJbW2pdXVtpLDRdPC1tZXRvZG9zLlQxW1tqXV1bW2ldXSRybXNlVDINCiAgICBlcnJvcmVzVDJbW2pdXVtpLDVdPC1tZXRvZG9zLlQxW1tqXV1bW2ldXSRzTWFwZVQyDQogICAgZXJyb3Jlc1QyW1tqXV1baSw2XTwtbWV0b2Rvcy5UMVtbal1dW1tpXV0kbWFzZVQyDQogICAgZXJyb3Jlc1QyW1tqXV1baSw3XTwtbWV0b2Rvcy5UMVtbal1dW1tpXV0kb3dhVDINCiAgfQ0KICBkYXQxPC1hcy5kYXRhLmZyYW1lKHJvdW5kKGVycm9yZXNUMltbal1dLDMpKQ0KICBkYXQxPC1kYXQxICU+JSBkaXN0aW5jdChzbWFwZVQxLCAua2VlcF9hbGwgPSBUUlVFKQ0KICBkYXQxPC1kYXQxICU+JSBkaXN0aW5jdChybXNlLCAua2VlcF9hbGwgPSBUUlVFKQ0KICBlcnJvcmVzVDJbW2pdXTwtYXMubWF0cml4KGRhdDEpDQogIHJldHVybihlcnJvcmVzVDJbW2pdXSkNCn0NCg0KY2FsY3VsbzQ8LWZ1bmN0aW9uKGopew0KICBwY2FbW2pdXSA8LSBwcmNvbXAoZXJyb3Jlc1QyW1tqXV1bLDI6N10sIHNjYWxlID0gVFJVRSkNCiAgaWYoc3VtKHBjYVtbal1dW1sicm90YXRpb24iXV1bLDFdKTwwKSB7ZXJyb3Jlc1QyW1tqXV1bLDhdPC0gcm91bmQoLShwY2FbW2pdXVtbIngiXV1bLDFdKSwzKX0gZWxzZSB7ZXJyb3Jlc1QyW1tqXV1bLDhdPC0gcm91bmQocGNhW1tqXV1bWyJ4Il1dWywxXSwzKX0NCiAgZXJyb3Jlc1QyW1tqXV1bLDldPC0gcm91bmQocGNhW1tqXV1bWyJ4Il1dWywyXSwzKQ0KICBmb3IgKGkgaW4gMTpkaW0oZXJyb3Jlc1QyW1tqXV0pWzFdKSB7DQogICAgZXJyb3Jlc1QyW1tqXV1baSwxMF08LSByb3VuZChhYnMoZXJyb3Jlc1QyW1tqXV1baSw4XSAtIG1pbihlcnJvcmVzVDJbW2pdXVssOF0pKSwzKQ0KICB9DQogIHJlc3VsdGFkb1tbMV1dW1tqXV08LWVycm9yZXNUMltbal1dDQogIHJlc3VsdGFkb1tbMl1dW1tqXV08LXBjYVtbal1dDQogIHJldHVybihyZXN1bHRhZG8pDQp9DQoNCmNhbGN1bG80Lm08LWZ1bmN0aW9uKGopew0KICBwY2FbW2pdXSA8LSBwcmNvbXAoZXJyb3Jlc1QyW1tqXV1bLDI6N10sIHNjYWxlID0gVFJVRSkNCiAgYTwtc3VtbWFyeShwY2FbW2pdXSkNCiAgZXJyb3Jlc1QyW1tqXV1bLDg6OV08LXJvdW5kKHBjYVtbal1dW1sieCJdXVssMToyXSwzKQ0KICBpZihhW1siaW1wb3J0YW5jZSJdXVsyLDFdPjAuODApIHsNCiAgICBpZihzdW0ocGNhW1tqXV1bWyJyb3RhdGlvbiJdXVszOjYsMV0pPDApIHtmb3IgKGkgaW4gMTpucm93KGVycm9yZXNUMltbal1dKSkge2Vycm9yZXNUMltbal1dW2ksMTBdPC0gcm91bmQoYWJzKGVycm9yZXNUMltbal1dW2ksOF0gLSBtYXgoZXJyb3Jlc1QyW1tqXV1bLDhdKSksMyl9IH0gDQogICAgZWxzZSB7IGZvciAoaSBpbiAxOm5yb3coZXJyb3Jlc1QyW1tqXV0pKSB7ZXJyb3Jlc1QyW1tqXV1baSwxMF08LSByb3VuZChhYnMoZXJyb3Jlc1QyW1tqXV1baSw4XSAtIG1pbihlcnJvcmVzVDJbW2pdXVssOF0pKSwzKX0gfQ0KICB9IGVsc2Ugew0KICAgIGlmKHN1bShwY2FbW2pdXVtbInJvdGF0aW9uIl1dWzM6NiwxXSk+MCAmIHN1bShwY2FbW2pdXVtbInJvdGF0aW9uIl1dWzE6MiwyXSk+MCkgew0KICAgICAgbTEgPC0gbWluKGVycm9yZXNUMltbal1dWyw4XSkNCiAgICAgIG0yIDwtIG1pbihlcnJvcmVzVDJbW2pdXVssOV0pfSBlbHNlIGlmKHN1bShwY2FbW2pdXVtbInJvdGF0aW9uIl1dWzM6NiwxXSk+MCAmIHN1bShwY2FbW2pdXVtbInJvdGF0aW9uIl1dWzE6MiwyXSk8MCkgew0KICAgICAgICBtMSA8LSBtaW4oZXJyb3Jlc1QyW1tqXV1bLDhdKQ0KICAgICAgICBtMiA8LSBtYXgoZXJyb3Jlc1QyW1tqXV1bLDldKX0gZWxzZSBpZihzdW0ocGNhW1tqXV1bWyJyb3RhdGlvbiJdXVszOjYsMV0pPDAgJiBzdW0ocGNhW1tqXV1bWyJyb3RhdGlvbiJdXVsxOjIsMl0pPjApIHsNCiAgICAgICAgICBtMSA8LSBtYXgoZXJyb3Jlc1QyW1tqXV1bLDhdKQ0KICAgICAgICAgIG0yIDwtIG1pbihlcnJvcmVzVDJbW2pdXVssOV0pfSBlbHNlIHsNCiAgICAgICAgICAgIG0xIDwtIG1heChlcnJvcmVzVDJbW2pdXVssOF0pDQogICAgICAgICAgICBtMiA8LSBtYXgoZXJyb3Jlc1QyW1tqXV1bLDldKX0NCiAgICAjTWFuaGF0dGFuIGRpc3RhbmNlDQogICAgZm9yIChpIGluIDE6bnJvdyhlcnJvcmVzVDJbW2pdXSkpIHsNCiAgICAgIGVycm9yZXNUMltbal1dW2ksMTBdPC0gcm91bmQoYWJzKGVycm9yZXNUMltbal1dW2ksOF0gLSBtMSkrYWJzKGVycm9yZXNUMltbal1dW2ksOV0gLSBtMiksMykNCiAgICB9DQogIH0NCiAgcmVzdWx0YWRvW1sxXV1bW2pdXTwtZXJyb3Jlc1QyW1tqXV0NCiAgcmVzdWx0YWRvW1syXV1bW2pdXTwtcGNhW1tqXV0NCiAgI0lmIHRoZXkgZXhpc3QsIGRpc3RhbmNlcyAwIG11c3QgYmUgcmVwbGFjZWQgYnkgZGlzdGFuY2VzIDAuMDAxDQogIGZvciAoaSBpbiAxOm5yb3cocmVzdWx0YWRvW1sxXV1bW2pdXSkpIHsNCiAgICByZXN1bHRhZG9bWzFdXVtbal1dW2ksMTBdPC1pZmVsc2UocmVzdWx0YWRvW1sxXV1bW2pdXVtpLDEwXT09MC4wMDAsIDAuMDAxLHJlc3VsdGFkb1tbMV1dW1tqXV1baSwxMF0pDQogIH0NCiAgY29tcGFyYWNpb25fYmljIDwtIEJJQygNCiAgICBtbGV4cChyZXN1bHRhZG9bWzFdXVtbal1dWywxMF0pLA0KICAgIG1saW52Z2FtbWEocmVzdWx0YWRvW1sxXV1bW2pdXVssMTBdKSwNCiAgICBtbGdhbW1hKHJlc3VsdGFkb1tbMV1dW1tqXV1bLDEwXSkNCiAgKQ0KICByZXN1bHRhZG9bWzNdXVtbal1dPC0gY29tcGFyYWNpb25fYmljICU+JSByb3duYW1lc190b19jb2x1bW4odmFyID0gImRpc3RyaWJ1Y2lvbiIpICU+JSBhcnJhbmdlKEJJQykNCiAgcmV0dXJuKHJlc3VsdGFkbykNCn0NCg0KY2FsY3VsbzU8LWZ1bmN0aW9uKGosaSl7DQogIGlmKGRpcy5hanVzdFtqXSA9PTEpIHtvYmo8LW1sZXhwKHJlc3VsdGFkb1tbMV1dW1tqXV1bLDEwXSl9IGVsc2UNCiAgICBpZihkaXMuYWp1c3Rbal09PTIpIHtvYmo8LW1sZ2FtbWEocmVzdWx0YWRvW1sxXV1bW2pdXVssMTBdKX0gZWxzZSB7b2JqPC1tbGludmdhbW1hKHJlc3VsdGFkb1tbMV1dW1tqXV1bLDEwXSl9DQogIGRhdDwtc3Vic2V0KHJlc3VsdGFkb1tbMV1dW1tqXV0scmVzdWx0YWRvW1sxXV1bW2pdXVssMTBdPD1yb3VuZChxbWwoY290YVtpXSwgb2JqID0gb2JqKSw0KSApDQogIGlmKG5yb3coZGF0KTwxKSB7ZGF0PC1zdWJzZXQocmVzdWx0YWRvW1sxXV1bW2pdXSwgcmVzdWx0YWRvW1sxXV1bW2pdXVssMV09PTEpfQ0KICBhPC1OVUxMDQogIGI8LU5VTEwNCiAgZm9yIChtIGluIDE6bnJvdyhkYXQpKSB7DQogICAgYVttXTwtcm91bmQoMS9kYXRbbSwyXSw0KSANCiAgICBiW21dPC1yb3VuZCgxL2RhdFttLDVdLDQpIA0KICB9DQogIGZvciAobSBpbiAxOm5yb3coZGF0KSkgew0KICAgIGRhdFttLDExXTwtIHJvdW5kKGFicyhkYXRbbSw4XSkvc3VtKGFicyhkYXRbLDhdKSksNCkgICN3MQ0KICAgIGRhdFttLDEyXTwtIHJvdW5kKGFbbV0gLyBzdW0oYSksNCkgICN3Mg0KICAgIGRhdFttLDEzXTwtIHJvdW5kKGJbbV0gLyBzdW0oYiksNCkgICN3Mw0KICB9DQogIHJlc3VsdGFkb1tbNF1dW1tqXV1bW2ldXTwtZGF0DQogIHJldHVybihyZXN1bHRhZG8pDQp9DQoNCmNhbGN1bG82PC1mdW5jdGlvbihqKXsNCiAgIyBXZSBkZWZpbmUgdGhlIGdlbmVyYWwgcGFyYW1ldGVycw0KICBzZXJpZTwtZW5zYXlvW2pdDQogIG5fcDwtMTIgDQogIG48LWxlbmd0aChkYXRvc1tbc2VyaWVdXSkgDQogIGVzdGFjPC0xMiANCiAgY29taWVuem8gPC0gc3RhcnQoZGF0b3NbW3NlcmllXV0pDQogICMgU2VsZWN0IHRoZSBwZXJpb2QgVDEoeCkgKyBUMih4KQ0KICB4ICA8LSB0cyhkYXRvc1tbc2VyaWVdXVsxOihuLW5fcCldLHN0YXJ0ID0gY29taWVuem8sIGZyZXF1ZW5jeSA9IGVzdGFjKSAjVDENCiAgaWYoZW5kKHgpWzJdPT1lc3RhYykgDQogIHtjb21pZW56bzI8LWMoZW5kKHgpWzFdKzEsMSkNCiAgfWVsc2UgDQogIHtjb21pZW56bzI8LWMoZW5kKHgpWzFdLGVuZCh4KVsyXSsxKX0NCiAgeDIgPC0gdHMoZGF0b3NbW3NlcmllXV1bKG4tbl9wKzEpOm5dLHN0YXJ0ID0gY29taWVuem8yLGZyZXF1ZW5jeSA9IGVzdGFjKSAjVDINCiAgc2V0LnNlZWQoODQ2KSANCiAgbWV0b2RvczwtbGlzdCgpDQogICNOYcOvdmUNCiAgbWV0b2Rvc1tbMV1dPC1uYWl2ZSh4LGg9bl9wKQ0KICAjTmHDr3ZlIHdpdGggc2Vhc29uYWxpdHkNCiAgbWV0b2Rvc1tbMl1dPC1zbmFpdmUoeCxoPW5fcCkNCiAgI0V4cG9uZW50aWFsIHNtb290aGluZw0KICBtZXRvZG9zW1szXV08LWV0cyh4LG1vZGVsID0gIlpaWiIsIG9wdC5jcml0PSJsaWsiLCBkYW1wZWQgPSBOVUxMKQ0KICBtZXRvZG9zW1szXV08LXByZWRpY3QobWV0b2Rvc1tbM11dLG5fcCkNCiAgbWV0b2Rvc1tbNF1dPC1ldHMoeCxtb2RlbCA9ICJaWloiLCBvcHQuY3JpdD0ibXNlIiwgZGFtcGVkID0gTlVMTCkNCiAgbWV0b2Rvc1tbNF1dPC1wcmVkaWN0KG1ldG9kb3NbWzRdXSxuX3ApDQogIG1ldG9kb3NbWzVdXTwtZXRzKHgsbW9kZWwgPSAiWlpaIiwgb3B0LmNyaXQ9ImFtc2UiLCBkYW1wZWQgPSBOVUxMKQ0KICBtZXRvZG9zW1s1XV08LXByZWRpY3QobWV0b2Rvc1tbNV1dLG5fcCkNCiAgbWV0b2Rvc1tbNl1dPC1ldHMoeCxtb2RlbCA9ICJaWloiLCBvcHQuY3JpdD0ic2lnbWEiLCBkYW1wZWQgPSBOVUxMKQ0KICBtZXRvZG9zW1s2XV08LXByZWRpY3QobWV0b2Rvc1tbNl1dLG5fcCkNCiAgbWV0b2Rvc1tbN11dPC1ldHMoeCxtb2RlbCA9ICJaWloiLCBvcHQuY3JpdD0ibWFlIiwgZGFtcGVkID0gTlVMTCkNCiAgbWV0b2Rvc1tbN11dPC1wcmVkaWN0KG1ldG9kb3NbWzddXSxuX3ApDQogIG1ldG9kb3NbWzhdXTwtZXRzKHgsbW9kZWwgPSAiWlpaIiwgb3B0LmNyaXQ9ImxpayIsIGRhbXBlZCA9IFRSVUUpDQogIG1ldG9kb3NbWzhdXTwtcHJlZGljdChtZXRvZG9zW1s4XV0sbl9wKQ0KICBtZXRvZG9zW1s5XV08LWV0cyh4LG1vZGVsID0gIlpaWiIsIG9wdC5jcml0PSJtc2UiLCBkYW1wZWQgPSBUUlVFKQ0KICBtZXRvZG9zW1s5XV08LXByZWRpY3QobWV0b2Rvc1tbOV1dLG5fcCkNCiAgbWV0b2Rvc1tbMTBdXTwtZXRzKHgsbW9kZWwgPSAiWlpaIiwgb3B0LmNyaXQ9ImFtc2UiLCBkYW1wZWQgPSBUUlVFKQ0KICBtZXRvZG9zW1sxMF1dPC1wcmVkaWN0KG1ldG9kb3NbWzEwXV0sbl9wKQ0KICBtZXRvZG9zW1sxMV1dPC1ldHMoeCxtb2RlbCA9ICJaWloiLCBvcHQuY3JpdD0ic2lnbWEiLCBkYW1wZWQgPSBUUlVFKQ0KICBtZXRvZG9zW1sxMV1dPC1wcmVkaWN0KG1ldG9kb3NbWzExXV0sbl9wKQ0KICBtZXRvZG9zW1sxMl1dPC1ldHMoeCxtb2RlbCA9ICJaWloiLCBvcHQuY3JpdD0ibWFlIiwgZGFtcGVkID0gVFJVRSkNCiAgbWV0b2Rvc1tbMTJdXTwtcHJlZGljdChtZXRvZG9zW1sxMl1dLG5fcCkNCiAgI0FSSU1BDQogIGE8LU5VTEwNCiAgbWV0b2Rvc1tbMTNdXTwtYXV0by5hcmltYSh4LCBpYz0iYWljIixhcHByb3hpbWF0aW9uPVQsIHRyYWNlPUZBTFNFLCBhbGxvd2RyaWZ0PUYpDQogIGE8LXByZWRpY3QobWV0b2Rvc1tbMTNdXSxuX3ApDQogIG1ldG9kb3NbWzEzXV0kbWVhbjwtYSRwcmVkDQogIG1ldG9kb3NbWzE0XV08LWF1dG8uYXJpbWEoeCwgaWM9ImJpYyIsYXBwcm94aW1hdGlvbj1ULCB0cmFjZT1GQUxTRSwgYWxsb3dkcmlmdD1GKQ0KICBhPC1wcmVkaWN0KG1ldG9kb3NbWzE0XV0sbl9wKQ0KICBtZXRvZG9zW1sxNF1dJG1lYW48LWEkcHJlZA0KICAjVGhldGENCiAgbWV0b2Rvc1tbMTVdXTwtb3RtLmFyeGl2KHgsIGg9bl9wLCBzPSBOVUxMLCBnPSAic0FQRSIpDQogIG1ldG9kb3NbWzE2XV08LWRvdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iTmVsZGVyLU1lYWQiKSAjRHluYW1pYyBPcHRpbWlzZWQgVGhldGEgTW9kZWwNCiAgbWV0b2Rvc1tbMTddXTwtZG90bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJMLUJGR1MtQiIpICNEeW5hbWljIE9wdGltaXNlZCBUaGV0YSBNb2RlbA0KICBtZXRvZG9zW1sxOF1dPC1kb3RtKHgsIGg9bl9wLCBzPSBOVUxMLCBlc3RpbWF0aW9uPVRSVUUsIG9wdC5tZXRob2Q9IlNBTk4iKSAjRHluYW1pYyBPcHRpbWlzZWQgVGhldGEgTW9kZWwNCiAgbWV0b2Rvc1tbMTldXTwtZHN0bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJOZWxkZXItTWVhZCIpICNEeW5hbWljIFN0YW5kYXJkIFRoZXRhIE1vZGVsDQogIG1ldG9kb3NbWzIwXV08LWRzdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iTC1CRkdTLUIiKSAjRHluYW1pYyBTdGFuZGFyZCBUaGV0YSBNb2RlbA0KICBtZXRvZG9zW1syMV1dPC1kc3RtKHgsIGg9bl9wLCBzPSBOVUxMLCBlc3RpbWF0aW9uPVRSVUUsIG9wdC5tZXRob2Q9IlNBTk4iKSAjRHluYW1pYyBTdGFuZGFyZCBUaGV0YSBNb2RlbA0KICBtZXRvZG9zW1syMl1dPC1vdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iTmVsZGVyLU1lYWQiKSAgI09wdGltaXNlZCBUaGV0YSBNb2RlbA0KICBtZXRvZG9zW1syM11dPC1vdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iTC1CRkdTLUIiKSAgI09wdGltaXNlZCBUaGV0YSBNb2RlbA0KICBtZXRvZG9zW1syNF1dPC1vdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iU0FOTiIpICAjT3B0aW1pc2VkIFRoZXRhIE1vZGVsDQogIG1ldG9kb3NbWzI1XV08LXN0bSh4LCBoPW5fcCwgcz0gTlVMTCwgZXN0aW1hdGlvbj1UUlVFLCBvcHQubWV0aG9kPSJOZWxkZXItTWVhZCIpICAjU3RhbmRhcmQgVGhldGEgTW9kZWwgKEZpb3J1Y2NpKQ0KICBtZXRvZG9zW1syNl1dPC1zdG0oeCwgaD1uX3AsIHM9IE5VTEwsIGVzdGltYXRpb249VFJVRSwgb3B0Lm1ldGhvZD0iTC1CRkdTLUIiKSAgI1N0YW5kYXJkIFRoZXRhIE1vZGVsIChGaW9ydWNjaSkNCiAgbWV0b2Rvc1tbMjddXTwtc3RtKHgsIGg9bl9wLCBzPSBOVUxMLCBlc3RpbWF0aW9uPVRSVUUsIG9wdC5tZXRob2Q9IlNBTk4iKSAgI1N0YW5kYXJkIFRoZXRhIE1vZGVsIChGaW9ydWNjaSkNCiAgbWV0b2Rvc1tbMjhdXTwtc3RoZXRhKHgsIGg9bl9wLCBzPSBOVUxMKSAjU3RhbmRhcmQgVGhldGEgTW9kZWwgKEFzc2ltYWtvcG91bG9zKQ0KICAjU1RMIChzZWFzb25hbCBzZXJpZXMgb25seSkNCiAgbWV0b2Rvc1tbMjldXTwtc3RsZih4LGg9bl9wLCBtZXRob2Q9YygiZXRzIikscm9idXN0ID0gVFJVRSxhbGxvdy5tdWx0aXBsaWNhdGl2ZS50cmVuZCA9IFRSVUUpDQogIG1ldG9kb3NbWzMwXV08LXN0bGYoeCxoPW5fcCwgbWV0aG9kPWMoImFyaW1hIikscm9idXN0ID0gVFJVRSxhbGxvdy5tdWx0aXBsaWNhdGl2ZS50cmVuZCA9IFRSVUUpDQogIG1ldG9kb3NbWzMxXV08LXN0bGYoeCxoPW5fcCwgZm9yZWNhc3RmdW5jdGlvbj10aGV0YWYscm9idXN0ID0gVFJVRSxhbGxvdy5tdWx0aXBsaWNhdGl2ZS50cmVuZCA9IFRSVUUpDQogICNDcm9zdG9uDQogIG1ldG9kb3NbWzMyXV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJjcm9zdG9uIixjb3N0ID0gIm1hciIsaW5pdC5vcHQ9VFJVRSkNCiAgbWV0b2Rvc1tbMzJdXSRmaXR0ZWQ8LXRzKG1ldG9kb3NbWzMyXV0kZnJjLmluLCBzdGFydCA9Y29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1szMl1dJG1lYW48LSAgdHMobWV0b2Rvc1tbMzJdXSRmcmMub3V0LCBzdGFydCA9Y29taWVuem8yLGZyZXF1ZW5jeSA9IGVzdGFjKSANCiAgbWV0b2Rvc1tbMzNdXTwtY3Jvc3QoeCxoPW5fcCx0eXBlID0gImNyb3N0b24iLGNvc3QgPSAibXNyIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1szM11dJGZpdHRlZDwtdHMobWV0b2Rvc1tbMzNdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzMzXV0kbWVhbjwtICB0cyhtZXRvZG9zW1szM11dJGZyYy5vdXQsIHN0YXJ0ID1jb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpIA0KICBtZXRvZG9zW1szNF1dPC1jcm9zdCh4LGg9bl9wLHR5cGUgPSAiY3Jvc3RvbiIsY29zdCA9ICJtYWUiLGluaXQub3B0PVRSVUUpDQogIG1ldG9kb3NbWzM0XV0kZml0dGVkPC10cyhtZXRvZG9zW1szNF1dJGZyYy5pbiwgc3RhcnQgPWNvbWllbnpvLGZyZXF1ZW5jeSA9IGVzdGFjKQ0KICBtZXRvZG9zW1szNF1dJG1lYW48LSAgdHMobWV0b2Rvc1tbMzRdXSRmcmMub3V0LCBzdGFydCA9Y29taWVuem8yLGZyZXF1ZW5jeSA9IGVzdGFjKSANCiAgbWV0b2Rvc1tbMzVdXTwtY3Jvc3QoeCxoPW5fcCx0eXBlID0gImNyb3N0b24iLGNvc3QgPSAibXNlIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1szNV1dJGZpdHRlZDwtdHMobWV0b2Rvc1tbMzVdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbMzVdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzM1XV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzM2XV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJzYmEiLGNvc3QgPSAibWFyIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1szNl1dJGZpdHRlZDwtdHMobWV0b2Rvc1tbMzZdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbMzZdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzM2XV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzM3XV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJzYmEiLGNvc3QgPSAibXNyIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1szN11dJGZpdHRlZDwtdHMobWV0b2Rvc1tbMzddXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbMzddXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzM3XV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzM4XV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJzYmEiLGNvc3QgPSAibWFlIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1szOF1dJGZpdHRlZDwtdHMobWV0b2Rvc1tbMzhdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbMzhdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzM4XV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzM5XV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJzYmEiLGNvc3QgPSAibXNlIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1szOV1dJGZpdHRlZDwtdHMobWV0b2Rvc1tbMzldXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbMzldXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzM5XV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzQwXV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJzYmoiLGNvc3QgPSAibWFyIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1s0MF1dJGZpdHRlZDwtdHMobWV0b2Rvc1tbNDBdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbNDBdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzQwXV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzQxXV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJzYmoiLGNvc3QgPSAibXNyIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1s0MV1dJGZpdHRlZDwtdHMobWV0b2Rvc1tbNDFdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbNDFdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzQxXV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzQyXV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJzYmoiLGNvc3QgPSAibWFlIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1s0Ml1dJGZpdHRlZDwtdHMobWV0b2Rvc1tbNDJdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbNDJdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzQyXV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogIG1ldG9kb3NbWzQzXV08LWNyb3N0KHgsaD1uX3AsdHlwZSA9ICJzYmoiLGNvc3QgPSAibXNlIixpbml0Lm9wdD1UUlVFKQ0KICBtZXRvZG9zW1s0M11dJGZpdHRlZDwtdHMobWV0b2Rvc1tbNDNdXSRmcmMuaW4sIHN0YXJ0ID1jb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbNDNdXSRtZWFuPC0gIHRzKG1ldG9kb3NbWzQzXV0kZnJjLm91dCwgc3RhcnQgPWNvbWllbnpvMixmcmVxdWVuY3kgPSBlc3RhYykgDQogICNQcm9waGV0DQogIHNldC5zZWVkKDUwKQ0KICBkZjwtZGF0YS5mcmFtZShkcyA9IHNlcShhcy5EYXRlKHBhc3RlKHN0YXJ0KHgpWzFdLCItIixzdGFydCh4KVsyXSwiLSIsIjAxIixzZXA9IiIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuRGF0ZShwYXN0ZShlbmQoeClbMV0sIi0iLGVuZCh4KVsyXSwiLSIsIjAxIixzZXA9IiIpKSxieT0ibSIpLCB5ID0geCkNCiAgbWV0b2Rvc1tbNDRdXTwtcHJvcGhldChkZix5ZWFybHkuc2Vhc29uYWxpdHk9VFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB3ZWVrbHkuc2Vhc29uYWxpdHkgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYWlseS5zZWFzb25hbGl0eSA9IEZBTFNFLHNlYXNvbmFsaXR5Lm1vZGUgPSAiYWRkaXRpdmUiKQ0KICBhPC1tYWtlX2Z1dHVyZV9kYXRhZnJhbWUobWV0b2Rvc1tbNDRdXSxwZXJpb2RzID0gMTgsZnJlcSA9ICJtb250aCIpDQogIGI8LXByZWRpY3QobWV0b2Rvc1tbNDRdXSxhKQ0KICBtZXRvZG9zW1s0NF1dJGZpdHRlZCA8LSB0cyhiJHloYXRbMTpsZW5ndGgoeCldLHN0YXJ0ID0gY29taWVuem8sZnJlcXVlbmN5ID0gZXN0YWMpDQogIG1ldG9kb3NbWzQ0XV0kbWVhbiA8LSB0cyhiJHloYXRbKGxlbmd0aCh4KSsxKToobGVuZ3RoKHgpK25fcCldLHN0YXJ0ID0gY29taWVuem8yLGZyZXF1ZW5jeSA9IGVzdGFjKQ0KICBtZXRvZG9zW1s0NV1dPC1wcm9waGV0KGRmLHllYXJseS5zZWFzb25hbGl0eT1UUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHdlZWtseS5zZWFzb25hbGl0eSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGRhaWx5LnNlYXNvbmFsaXR5ID0gRkFMU0Usc2Vhc29uYWxpdHkubW9kZSA9ICJtdWx0aXBsaWNhdGl2ZSIpDQogIGE8LW1ha2VfZnV0dXJlX2RhdGFmcmFtZShtZXRvZG9zW1s0NV1dLHBlcmlvZHMgPSAxOCxmcmVxID0gIm1vbnRoIikNCiAgYjwtcHJlZGljdChtZXRvZG9zW1s0NV1dLGEpDQogIG1ldG9kb3NbWzQ1XV0kZml0dGVkIDwtIHRzKGIkeWhhdFsxOmxlbmd0aCh4KV0sc3RhcnQgPSBjb21pZW56byxmcmVxdWVuY3kgPSBlc3RhYykNCiAgbWV0b2Rvc1tbNDVdXSRtZWFuIDwtIHRzKGIkeWhhdFsobGVuZ3RoKHgpKzEpOihsZW5ndGgoeCkrbl9wKV0sc3RhcnQgPSBjb21pZW56bzIsZnJlcXVlbmN5ID0gZXN0YWMpDQogICNOTkFSOiBuZXVyYWwgbmV0d29yayBhdXRvcmVncmVzc2lvbg0KICBzZXQuc2VlZCg1MCkNCiAgbWV0b2Rvc1tbNDZdXTwtbm5ldGFyKHgscD1lc3RhYyxzY2FsZS5pbnB1dHM9VCkNCiAgYTwtZm9yZWNhc3QobWV0b2Rvc1tbNDZdXSxoPW5fcCkNCiAgbWV0b2Rvc1tbNDZdXSRtZWFuPC1hJG1lYW4NCiAgI1RCQVRTIG1vZGVsIChFeHBvbmVudGlhbCBzbW9vdGhpbmcgc3RhdGUgc3BhY2UgbW9kZWwgd2l0aCBCb3gtQ294IHRyYW5zZm9ybWF0aW9uLCBBUk1BIGVycm9ycywgVHJlbmQgYW5kIFNlYXNvbmFsIGNvbXBvbmVudHMpDQogIHNldC5zZWVkKDUwKQ0KICBtZXRvZG9zW1s0N11dPC10YmF0cyh4LCBiaWFzYWRqID0gVFJVRSkNCiAgbWV0b2Rvc1tbNDddXSRmaXR0ZWQ8LW1ldG9kb3NbWzQ3XV0kZml0dGVkLnZhbHVlcw0KICBhPC1mb3JlY2FzdChtZXRvZG9zW1s0N11dLGg9bl9wKQ0KICBtZXRvZG9zW1s0N11dJG1lYW48LWEkbWVhbg0KICAjR1JOTjogZ2VuZXJhbCByZWdyZXNzaW9uIG5ldXJhbCBuZXR3b3JrIA0KICBzZXQuc2VlZCg1MCkNCiAgbWV0b2Rvc1tbNDhdXTwtZ3Jubl9mb3JlY2FzdGluZyh4LCBoID0gbl9wLCB0cmFuc2Zvcm0gPSAiYWRkaXRpdmUiKQ0KICBhPC1yb2xsaW5nX29yaWdpbihtZXRvZG9zW1s0OF1dLGggPSBsZW5ndGgoeCktZXN0YWMtMSkNCiAgbWV0b2Rvc1tbNDhdXSRmaXR0ZWQ8LWEkcHJlZGljdGlvbnNbLDFdDQogIG1ldG9kb3NbWzQ4XV0kbWVhbjwtbWV0b2Rvc1tbNDhdXSRwcmVkaWN0aW9uDQogIG1ldG9kb3NbWzQ5XV08LWdybm5fZm9yZWNhc3RpbmcoeCwgaCA9IG5fcCwgdHJhbnNmb3JtID0gIm11bHRpcGxpY2F0aXZlIikNCiAgYTwtcm9sbGluZ19vcmlnaW4obWV0b2Rvc1tbNDldXSxoID0gbGVuZ3RoKHgpLWVzdGFjLTEpDQogIG1ldG9kb3NbWzQ5XV0kZml0dGVkPC1hJHByZWRpY3Rpb25zWywxXQ0KICBtZXRvZG9zW1s0OV1dJG1lYW48LW1ldG9kb3NbWzQ5XV0kcHJlZGljdGlvbg0KICAjTUxQOiBtdWx0aWxheWVyIHBlcmNlcHRyb24NCiAgIyBFbiBmaXR0ZWQgZGVsIDE0Om4sIGxhcyBwcmltZXJhcyBuX3ArMiBzb24gTkENCiAgc2V0LnNlZWQoNTApDQogIG1ldG9kb3NbWzUwXV08LW1scC50aGllZih4LCBoPTEyKQ0KICAjIEVuIGZpdHRlZCBkZWwgNTpuLCBsYXMgcHJpbWVyYXMgNCBzb24gdmFjw61hcw0KICBtZXRvZG9zW1s1MV1dPC1tbHAoeCwgbT0xMiwgY29tYiA9IGMoIm1lZGlhbiIpLGhkID0gYygxMCw1KSkNCiAgYTwtZm9yZWNhc3QobWV0b2Rvc1tbNTFdXSxoPTEyKQ0KICBtZXRvZG9zW1s1MV1dJG1lYW48LWEkbWVhbg0KICBtZXRvZG9zW1s1Ml1dPC1tbHAoeCwgbT0xMiwgY29tYiA9IGMoIm1lYW4iKSxoZCA9IGMoMTAsNSkpDQogIGE8LWZvcmVjYXN0KG1ldG9kb3NbWzUyXV0saD0xMikNCiAgbWV0b2Rvc1tbNTJdXSRtZWFuPC1hJG1lYW4NCiAgDQogIHJldHVybihtZXRvZG9zKQ0KfQ0KDQpgYGANCg0KQ2FsY3VsYW1vcyBwcmVkaWNjaW9uZXMgcGFyYSBjYWRhIHRpcG8gTGV0cmFzLCBlbiBwcmltZXIgbHVnYXIgY2FsY3VsYXJlbW9zIGxhcyBkZSBhIDMgbWVzZXMuDQoNCiMjIyMgMyBtZXNlcw0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYM19tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NDpucm93KGRhdG9zKSwgXQ0KDQp4PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOCwxMCksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KZGF0b3M9bGlzdCh4PXgpDQoNCmBgYA0KDQpgYGB7cixlY2hvPUZBTFNFfQ0KDQojIyBUaGUgYWxnb3JpdGhtIGlzIGRlc2lnbmVkIHRvIG9idGFpbiB0aGUgcHJlZGljdGlvbnMgb2YgYSBtb250aGx5IHNlcmllcw0KZW5zYXlvPC0xOmxlbmd0aChkYXRvcykNCg0KIyMgQXBwbHkgZWFjaCBtZXRob2Qgd2l0aCBhbGwgaXRzIGNvbmZpZ3VyYXRpb25zIGluIHBlcmlvZCBUMQ0KbWV0b2Rvcy5UMTwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxW1tqXV08LWNhbGN1bG8xKGopDQp9DQoNCiMjIE9idGFpbnMgbG9zcyBmdW5jdGlvbnMgaW4gVDEgYW5kIFQyDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxW1tqXV08LWNhbGN1bG8yKGopIA0KfQ0KZXJyb3Jlc1QyPC1saXN0KCkgDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBlcnJvcmVzVDJbW2pdXTwtY2FsY3VsbzMoaikNCn0NCg0KIyMgQ2FsY3VsYXRlcyBQQ0EgYW5kIE1hbmhhdHRhbiBkaXN0YW5jZSB0byB0aGUgbWV0aG9kIGFzc29jaWF0ZWQgd2l0aCB0aGUgc21hbGxlc3QgZXJyb3INCnJlc3VsdGFkbzwtbGlzdCgpDQpwY2E8LXJlc3VsdGFkb1tbMV1dPC1yZXN1bHRhZG9bWzJdXTwtcmVzdWx0YWRvW1szXV08LWxpc3QoKQ0KZGlzLmFqdXN0PC1yZXAoTkEsbGVuZ3RoKGVuc2F5bykpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICByZXN1bHRhZG88LWNhbGN1bG80Lm0oaikgDQogIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxleHAiKT09VFJVRSkge2Rpcy5hanVzdFtqXTwtMX0gZWxzZSANCiAgICBpZihzdHJfZGV0ZWN0KHJlc3VsdGFkb1tbM11dW1tqXV1bMSwxXSwgIm1sZ2FtbWEiKT09VFJVRSkge2Rpcy5hanVzdFtqXTwtMn0gZWxzZSB7ZGlzLmFqdXN0W2pdPC0zfQ0KfQ0KDQoNCiMjIGFsUENBIGVzdGFibGlzaGVzIHRoZSBwZXJjZW50aWxlcyBvZiB0aGUgYWRqdXN0ZWQgZGlzdHJpYnV0aW9uIGFzIGN1dC1vZmYgcG9pbnRzIGZvciBtZXRob2Qgc2VsZWN0aW9uDQpjb3RhPC1jKDAuMDUsMC41MCwgMC45NSkgDQpyZXN1bHRhZG9bWzRdXTwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgeyAgDQogIHJlc3VsdGFkb1tbNF1dW1tqXV08LWxpc3QoKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgoY290YSkpIHsgIA0KICAgIHJlc3VsdGFkbzwtY2FsY3VsbzUoaixpKQ0KICB9DQp9DQoNCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIHRoZSBwZXJpb2QgVDErVDINCm1ldG9kb3MuVDEyPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDEyW1tqXV08LWNhbGN1bG82KGopDQp9DQoNCiMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyBPYnRhaW4gdGhlIHByZWRpY3Rpb25zIGZvciBPYnRhaW5zIHRoZSBwcmVkaWN0aW9ucyBmb3IgVDMgYWNjb3JkaW5nIHRvIHRoZSBwZXJjZW50aWxlIGNob3NlbiBieSB0aGUgdXNlcg0Kaj0xO25fcD0xMjtwcmVkNT1wcmVkNTA9cHJlZDk1PU5VTEw7eXU9MQ0KYjE8LXJlcChOQSxuX3ApDQojI2ZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQpmb3IgKGkgaW4gMTpsZW5ndGgoY290YSkpIHsNCiAgDQogIG5fcDwtMTIgI3ByZWRpY2Npb25lcw0KICBkYXQ8LXJlc3VsdGFkb1tbNF1dW1tqXV1bW2ldXQ0KICAjI01hdHJpeiBwcmVkaWNjaW9uZXMgbcOpdG9kb3MgcGFyYSBUMg0KICBwcmVkX1QyPC1wcmVkX1QzPC1tYXRyaXgoTkEsbnJvdz1kaW0oZGF0KVsxXSxuY29sID0gbl9wKQ0KICBmb3IgKGsgaW4gMTpkaW0oZGF0KVsxXSkgew0KICAgIGZvciAoa2sgaW4gMTpsZW5ndGgobWV0b2Rvcy5UMVtbal1dKSkgeyANCiAgICAgICNwcmludChtZXRvZG9zLlQxMltbal1dW1tra11dJG1lYW4pDQogICAgICBpZihkYXRbaywxXT09a2spe3ByZWRfVDJbayxdPC1tZXRvZG9zLlQxW1tqXV1bW2trXV0kbWVhbjsgcHJlZF9UM1trLF08LW1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbn0NCiAgICB9DQogIH0NCiAgZm9yIChrIGluIDE6bl9wKSB7DQogICAgYjFba108LXByZWRfVDNbLGtdJSolZGF0WywxMV07aWYoaT09MSl7cHJlZDVba109YjFba119OyBpZihpPT0yKXtwcmVkNTBba109YjFba119OyBpZihpPT0zKXtwcmVkOTVba109YjFba119DQogIH0NCn0NCnBlcmNlbnRpbGUuNT1yb3VuZChwcmVkNSwgZGlnaXRzPTIpO3BlcmNlbnRpbGUuNTA9cm91bmQocHJlZDUwLCBkaWdpdHM9Mik7cGVyY2VudGlsZS45NT1yb3VuZChwcmVkOTUsIGRpZ2l0cz0yKQ0KcmVzdWx0X2FsUENBPC1jYmluZC5kYXRhLmZyYW1lKHBlcmNlbnRpbGUuNSxwZXJjZW50aWxlLjUwLHBlcmNlbnRpbGUuOTUpDQoNCnJtKGRhdCk7cm0oZXJyb3Jlc1QyKTtybShtZXRvZG9zLlQxKTtybShtZXRvZG9zLlQxMik7cm0ocGNhKQ0Kcm0ocHJlZF9UMik7cm0oYjEpOw0Kcm0oY290YSk7cm0oZGlzLmFqdXN0KTtybShlbnNheW8pO3JtKGkpO3JtKGopO3JtKGspO3JtKGtrKQ0Kcm0obl9wKTtybSh5dSk7cm0ocHJlZF9UMyk7cm0ocmVzdWx0YWRvKTtybShwcmVkNSk7cm0ocHJlZDUwKTtybShwcmVkOTUpDQpybShwZXJjZW50aWxlLjUpO3JtKHBlcmNlbnRpbGUuNTApO3JtKHBlcmNlbnRpbGUuOTUpDQpyZXN1bHRfYWxQQ0ENCiMjIFlvdSBjYW4gY2hvb3NlIHRoZSBwZXJjZW50aWxlIGNvbHVtbiBhbmQgY29ycmVzcG9uZGluZyBwcmVkaWN0aW9uDQpyZXN1bHRfYWxQQ0FfM21lc2VzPC1yZXN1bHRfYWxQQ0EkcGVyY2VudGlsZS45NQ0KDQpgYGANCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpkYXRvcyA8LSByZWFkLnhsc3goImRhdG9zLnhsc3giKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiM19tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NDpucm93KGRhdG9zKSwgXQ0KDQpgYGANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCnNlcmllXzEyIDwtIGMoZGF0b3MkVmFsb3IsIHJlc3VsdF9hbFBDQSRwZXJjZW50aWxlLjk1KQ0KDQp5PXRzKChzZXJpZV8xMiksDQogICAgIHN0YXJ0PWMoMjAwOCwxMCksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KDQp1bHRpbW9fMTIgPC0gdGFpbCh5LCAxMikNCg0KIyBDcmVhciBlbCBncsOhZmljbyB1dGlsaXphbmRvIGF1dG9wbG90IGRlIGdncGxvdDINCmdncGxvdCgpICsNCiAgIyBBZ3JlZ2FyIGxhIGzDrW5lYSBjb21wbGV0YSBlbiBjb2xvciBhenVsDQogIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh0aWVtcG8gPSB0aW1lKHkpLCB5ID0geSksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gIiM0QzcyQjAiKSArDQogICMgQWdyZWdhciBzb2xvIGxvcyDDumx0aW1vcyAxMiBwdW50b3MgZW4gY29sb3Igcm9qbw0KICBnZW9tX2xpbmUoZGF0YSA9IHRhaWwoZGF0YS5mcmFtZSh0aWVtcG8gPSB0aW1lKHkpLCB5ID0geSksIDEyKSwgYWVzKHggPSB0aWVtcG8sIHkgPSB5KSwgY29sb3IgPSAicmVkIikgKw0KICAjIFBlcnNvbmFsaXphciBlbCBhc3BlY3RvIGRlbCBncsOhZmljbw0KICB0aGVtZV9taW5pbWFsKCkgKyAgIyBFc3RhYmxlY2VyIHVuIHRlbWEgbWluaW1hbGlzdGENCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWF5b3INCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBsYXMgbMOtbmVhcyBkZSBsYSBjdWFkcsOtY3VsYSBtZW5vcg0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgVGFtYcOxbyBkZWwgdGV4dG8gZGUgbG9zIGVqZXMNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksICAjIFRhbWHDsW8geSBlc3RpbG8gZGVsIHTDrXR1bG8NCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSksICAjIFRhbWHDsW8geSBhbGluZWFjacOzbiBkZWwgcGllIGRlIHDDoWdpbmENCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGVsIHTDrXR1bG8gZGUgbGEgbGV5ZW5kYQ0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAgIyBQb3NpY2lvbmFyIGxhIGxleWVuZGEgZW4gbGEgcGFydGUgaW5mZXJpb3INCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxhIGxleWVuZGENCiAgKSArDQogIGxhYnMoDQogICAgeCA9ICJUaWVtcG8iLCAgICAgIyBFdGlxdWV0YSBkZWwgZWplIHgNCiAgICB5ID0gIsONbmRpY2Ugbm9taW5hbCIsICAgICAgIyBFdGlxdWV0YSBkZWwgZWplIHkNCiAgICB0aXRsZSA9ICJMZXRyYXMgYSAzIG1lc2VzIiwgICMgVMOtdHVsbyBkZWwgZ3LDoWZpY28NCiAgICAgIyBUZXh0byBkZSBsYSBmdWVudGUgZGUgZGF0b3MNCiAgKQ0KDQoNCmBgYA0KIyMjIyA2IG1lc2VzDQpBaG9yYSBwcmVkZWNpbW9zIGNvbiBsYXMgbGV0cmFzIGRlIDYgbWVzZXMuDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDNfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlg2X21lc2VzIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzk1Om5yb3coZGF0b3MpLCBdDQoNCng9dHMocmV2KGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDA4LDExKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQpkYXRvcz1saXN0KHg9eCkNCg0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0V9DQoNCiMjIFRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQgdG8gb2J0YWluIHRoZSBwcmVkaWN0aW9ucyBvZiBhIG1vbnRobHkgc2VyaWVzDQplbnNheW88LTE6bGVuZ3RoKGRhdG9zKQ0KDQojIyBBcHBseSBlYWNoIG1ldGhvZCB3aXRoIGFsbCBpdHMgY29uZmlndXJhdGlvbnMgaW4gcGVyaW9kIFQxDQptZXRvZG9zLlQxPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzEoaikNCn0NCg0KIyMgT2J0YWlucyBsb3NzIGZ1bmN0aW9ucyBpbiBUMSBhbmQgVDINCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzIoaikgDQp9DQplcnJvcmVzVDI8LWxpc3QoKSANCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIGVycm9yZXNUMltbal1dPC1jYWxjdWxvMyhqKQ0KfQ0KDQojIyBDYWxjdWxhdGVzIFBDQSBhbmQgTWFuaGF0dGFuIGRpc3RhbmNlIHRvIHRoZSBtZXRob2QgYXNzb2NpYXRlZCB3aXRoIHRoZSBzbWFsbGVzdCBlcnJvcg0KcmVzdWx0YWRvPC1saXN0KCkNCnBjYTwtcmVzdWx0YWRvW1sxXV08LXJlc3VsdGFkb1tbMl1dPC1yZXN1bHRhZG9bWzNdXTwtbGlzdCgpDQpkaXMuYWp1c3Q8LXJlcChOQSxsZW5ndGgoZW5zYXlvKSkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIHJlc3VsdGFkbzwtY2FsY3VsbzQubShqKSANCiAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGV4cCIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0xfSBlbHNlIA0KICAgIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxnYW1tYSIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0yfSBlbHNlIHtkaXMuYWp1c3Rbal08LTN9DQp9DQoNCg0KIyMgYWxQQ0EgZXN0YWJsaXNoZXMgdGhlIHBlcmNlbnRpbGVzIG9mIHRoZSBhZGp1c3RlZCBkaXN0cmlidXRpb24gYXMgY3V0LW9mZiBwb2ludHMgZm9yIG1ldGhvZCBzZWxlY3Rpb24NCmNvdGE8LWMoMC4wNSwwLjUwLCAwLjk1KSANCnJlc3VsdGFkb1tbNF1dPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7ICANCiAgcmVzdWx0YWRvW1s0XV1bW2pdXTwtbGlzdCgpDQogIGZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgeyAgDQogICAgcmVzdWx0YWRvPC1jYWxjdWxvNShqLGkpDQogIH0NCn0NCg0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHBlcmlvZCBUMStUMg0KbWV0b2Rvcy5UMTI8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMTJbW2pdXTwtY2FsY3VsbzYoaikNCn0NCg0KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIE9idGFpbnMgdGhlIHByZWRpY3Rpb25zIGZvciBUMyBhY2NvcmRpbmcgdG8gdGhlIHBlcmNlbnRpbGUgY2hvc2VuIGJ5IHRoZSB1c2VyDQpqPTE7bl9wPTEyO3ByZWQ1PXByZWQ1MD1wcmVkOTU9TlVMTDt5dT0xDQpiMTwtcmVwKE5BLG5fcCkNCiMjZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCmZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgew0KICANCiAgbl9wPC0xMiAjcHJlZGljY2lvbmVzDQogIGRhdDwtcmVzdWx0YWRvW1s0XV1bW2pdXVtbaV1dDQogICMjTWF0cml6IHByZWRpY2Npb25lcyBtw6l0b2RvcyBwYXJhIFQyDQogIHByZWRfVDI8LXByZWRfVDM8LW1hdHJpeChOQSxucm93PWRpbShkYXQpWzFdLG5jb2wgPSBuX3ApDQogIGZvciAoayBpbiAxOmRpbShkYXQpWzFdKSB7DQogICAgZm9yIChrayBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1tqXV0pKSB7IA0KICAgICAgI3ByaW50KG1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbikNCiAgICAgIGlmKGRhdFtrLDFdPT1rayl7cHJlZF9UMltrLF08LW1ldG9kb3MuVDFbW2pdXVtba2tdXSRtZWFuOyBwcmVkX1QzW2ssXTwtbWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFufQ0KICAgIH0NCiAgfQ0KICBmb3IgKGsgaW4gMTpuX3ApIHsNCiAgICBiMVtrXTwtcHJlZF9UM1ssa10lKiVkYXRbLDExXTtpZihpPT0xKXtwcmVkNVtrXT1iMVtrXX07IGlmKGk9PTIpe3ByZWQ1MFtrXT1iMVtrXX07IGlmKGk9PTMpe3ByZWQ5NVtrXT1iMVtrXX0NCiAgfQ0KfQ0KcGVyY2VudGlsZS41PXJvdW5kKHByZWQ1LCBkaWdpdHM9Mik7cGVyY2VudGlsZS41MD1yb3VuZChwcmVkNTAsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjk1PXJvdW5kKHByZWQ5NSwgZGlnaXRzPTIpDQpyZXN1bHRfYWxQQ0E8LWNiaW5kLmRhdGEuZnJhbWUocGVyY2VudGlsZS41LHBlcmNlbnRpbGUuNTAscGVyY2VudGlsZS45NSkNCg0Kcm0oZGF0KTtybShlcnJvcmVzVDIpO3JtKG1ldG9kb3MuVDEpO3JtKG1ldG9kb3MuVDEyKTtybShwY2EpDQpybShwcmVkX1QyKTtybShiMSk7DQpybShjb3RhKTtybShkaXMuYWp1c3QpO3JtKGVuc2F5byk7cm0oaSk7cm0oaik7cm0oayk7cm0oa2spDQpybShuX3ApO3JtKHl1KTtybShwcmVkX1QzKTtybShyZXN1bHRhZG8pO3JtKHByZWQ1KTtybShwcmVkNTApO3JtKHByZWQ5NSkNCnJtKHBlcmNlbnRpbGUuNSk7cm0ocGVyY2VudGlsZS41MCk7cm0ocGVyY2VudGlsZS45NSkNCg0KIyMgWW91IGNhbiBjaG9vc2UgdGhlIHBlcmNlbnRpbGUgY29sdW1uIGFuZCBjb3JyZXNwb25kaW5nIHByZWRpY3Rpb24NCnJlc3VsdF9hbFBDQQ0KDQpyZXN1bHRfYWxQQ0FfNm1lc2VzPC1yZXN1bHRfYWxQQ0EkcGVyY2VudGlsZS45NQ0KYGBgDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3MgPC0gcmVhZC54bHN4KCJkYXRvcy54bHN4IikNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDNfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIjZfbWVzZXMiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTU6bnJvdyhkYXRvcyksIF0NCg0KYGBgDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsZWNobz1GQUxTRX0NCnNlcmllXzEyIDwtIGMoZGF0b3MkVmFsb3IsIHJlc3VsdF9hbFBDQSRwZXJjZW50aWxlLjk1KQ0KDQp5PXRzKChzZXJpZV8xMiksDQogICAgIHN0YXJ0PWMoMjAwOCwxMSksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KDQp1bHRpbW9fMTIgPC0gdGFpbCh5LCAxMikNCg0KIyBDcmVhciBlbCBncsOhZmljbyB1dGlsaXphbmRvIGF1dG9wbG90IGRlIGdncGxvdDINCmdncGxvdCgpICsNCiAgIyBBZ3JlZ2FyIGxhIGzDrW5lYSBjb21wbGV0YSBlbiBjb2xvciBhenVsDQogIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh0aWVtcG8gPSB0aW1lKHkpLCB5ID0geSksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gIiM0QzcyQjAiKSArDQogICMgQWdyZWdhciBzb2xvIGxvcyDDumx0aW1vcyAxMiBwdW50b3MgZW4gY29sb3Igcm9qbw0KICBnZW9tX2xpbmUoZGF0YSA9IHRhaWwoZGF0YS5mcmFtZSh0aWVtcG8gPSB0aW1lKHkpLCB5ID0geSksIDEyKSwgYWVzKHggPSB0aWVtcG8sIHkgPSB5KSwgY29sb3IgPSAicmVkIikgKw0KICAjIFBlcnNvbmFsaXphciBlbCBhc3BlY3RvIGRlbCBncsOhZmljbw0KICB0aGVtZV9taW5pbWFsKCkgKyAgIyBFc3RhYmxlY2VyIHVuIHRlbWEgbWluaW1hbGlzdGENCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWF5b3INCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBsYXMgbMOtbmVhcyBkZSBsYSBjdWFkcsOtY3VsYSBtZW5vcg0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgVGFtYcOxbyBkZWwgdGV4dG8gZGUgbG9zIGVqZXMNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksICAjIFRhbWHDsW8geSBlc3RpbG8gZGVsIHTDrXR1bG8NCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSksICAjIFRhbWHDsW8geSBhbGluZWFjacOzbiBkZWwgcGllIGRlIHDDoWdpbmENCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGVsIHTDrXR1bG8gZGUgbGEgbGV5ZW5kYQ0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAgIyBQb3NpY2lvbmFyIGxhIGxleWVuZGEgZW4gbGEgcGFydGUgaW5mZXJpb3INCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxhIGxleWVuZGENCiAgKSArDQogIGxhYnMoDQogICAgeCA9ICJUaWVtcG8iLCAgICAgIyBFdGlxdWV0YSBkZWwgZWplIHgNCiAgICB5ID0gIsONbmRpY2Ugbm9taW5hbCIsICAgICAgIyBFdGlxdWV0YSBkZWwgZWplIHkNCiAgICB0aXRsZSA9ICJMZXRyYXMgYSA2IG1lc2VzIiwgICMgVMOtdHVsbyBkZWwgZ3LDoWZpY28NCiAgICAgIyBUZXh0byBkZSBsYSBmdWVudGUgZGUgZGF0b3MNCiAgKQ0KDQoNCmBgYA0KIyMjIyA5IG1lc2VzDQpBIGNvbnRpbnVhY2nDs24gcHJlZGVjaW1vcyBjb24gbGFzIGxldHJhcyBkZSA5IG1lc2VzLg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciA5X21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYOV9tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1sxNDY6bnJvdyhkYXRvcyksIF0NCg0KeD10cyhyZXYoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMTMsMiksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KZGF0b3M9bGlzdCh4PXgpDQoNCmBgYA0KDQpgYGB7cixlY2hvPUZBTFNFfQ0KDQojIyBUaGUgYWxnb3JpdGhtIGlzIGRlc2lnbmVkIHRvIG9idGFpbiB0aGUgcHJlZGljdGlvbnMgb2YgYSBtb250aGx5IHNlcmllcw0KZW5zYXlvPC0xOmxlbmd0aChkYXRvcykNCg0KIyMgQXBwbHkgZWFjaCBtZXRob2Qgd2l0aCBhbGwgaXRzIGNvbmZpZ3VyYXRpb25zIGluIHBlcmlvZCBUMQ0KbWV0b2Rvcy5UMTwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxW1tqXV08LWNhbGN1bG8xKGopDQp9DQoNCiMjIE9idGFpbnMgbG9zcyBmdW5jdGlvbnMgaW4gVDEgYW5kIFQyDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxW1tqXV08LWNhbGN1bG8yKGopIA0KfQ0KZXJyb3Jlc1QyPC1saXN0KCkgDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBlcnJvcmVzVDJbW2pdXTwtY2FsY3VsbzMoaikNCn0NCg0KIyMgQ2FsY3VsYXRlcyBQQ0EgYW5kIE1hbmhhdHRhbiBkaXN0YW5jZSB0byB0aGUgbWV0aG9kIGFzc29jaWF0ZWQgd2l0aCB0aGUgc21hbGxlc3QgZXJyb3INCnJlc3VsdGFkbzwtbGlzdCgpDQpwY2E8LXJlc3VsdGFkb1tbMV1dPC1yZXN1bHRhZG9bWzJdXTwtcmVzdWx0YWRvW1szXV08LWxpc3QoKQ0KZGlzLmFqdXN0PC1yZXAoTkEsbGVuZ3RoKGVuc2F5bykpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICByZXN1bHRhZG88LWNhbGN1bG80Lm0oaikgDQogIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxleHAiKT09VFJVRSkge2Rpcy5hanVzdFtqXTwtMX0gZWxzZSANCiAgICBpZihzdHJfZGV0ZWN0KHJlc3VsdGFkb1tbM11dW1tqXV1bMSwxXSwgIm1sZ2FtbWEiKT09VFJVRSkge2Rpcy5hanVzdFtqXTwtMn0gZWxzZSB7ZGlzLmFqdXN0W2pdPC0zfQ0KfQ0KDQoNCiMjIGFsUENBIGVzdGFibGlzaGVzIHRoZSBwZXJjZW50aWxlcyBvZiB0aGUgYWRqdXN0ZWQgZGlzdHJpYnV0aW9uIGFzIGN1dC1vZmYgcG9pbnRzIGZvciBtZXRob2Qgc2VsZWN0aW9uDQpjb3RhPC1jKDAuMDUsMC41MCwgMC45NSkgDQpyZXN1bHRhZG9bWzRdXTwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgeyAgDQogIHJlc3VsdGFkb1tbNF1dW1tqXV08LWxpc3QoKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgoY290YSkpIHsgIA0KICAgIHJlc3VsdGFkbzwtY2FsY3VsbzUoaixpKQ0KICB9DQp9DQoNCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIHRoZSBwZXJpb2QgVDErVDINCm1ldG9kb3MuVDEyPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDEyW1tqXV08LWNhbGN1bG82KGopDQp9DQoNCiMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyBPYnRhaW4gdGhlIHByZWRpY3Rpb25zIGZvciBPYnRhaW5zIHRoZSBwcmVkaWN0aW9ucyBmb3IgVDMgYWNjb3JkaW5nIHRvIHRoZSBwZXJjZW50aWxlIGNob3NlbiBieSB0aGUgdXNlcg0Kaj0xO25fcD0xMjtwcmVkNT1wcmVkNTA9cHJlZDk1PU5VTEw7eXU9MQ0KYjE8LXJlcChOQSxuX3ApDQojI2ZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQpmb3IgKGkgaW4gMTpsZW5ndGgoY290YSkpIHsNCiAgDQogIG5fcDwtMTIgI3ByZWRpY2Npb25lcw0KICBkYXQ8LXJlc3VsdGFkb1tbNF1dW1tqXV1bW2ldXQ0KICAjI01hdHJpeiBwcmVkaWNjaW9uZXMgbcOpdG9kb3MgcGFyYSBUMg0KICBwcmVkX1QyPC1wcmVkX1QzPC1tYXRyaXgoTkEsbnJvdz1kaW0oZGF0KVsxXSxuY29sID0gbl9wKQ0KICBmb3IgKGsgaW4gMTpkaW0oZGF0KVsxXSkgew0KICAgIGZvciAoa2sgaW4gMTpsZW5ndGgobWV0b2Rvcy5UMVtbal1dKSkgeyANCiAgICAgICNwcmludChtZXRvZG9zLlQxMltbal1dW1tra11dJG1lYW4pDQogICAgICBpZihkYXRbaywxXT09a2spe3ByZWRfVDJbayxdPC1tZXRvZG9zLlQxW1tqXV1bW2trXV0kbWVhbjsgcHJlZF9UM1trLF08LW1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbn0NCiAgICB9DQogIH0NCiAgZm9yIChrIGluIDE6bl9wKSB7DQogICAgYjFba108LXByZWRfVDNbLGtdJSolZGF0WywxMV07aWYoaT09MSl7cHJlZDVba109YjFba119OyBpZihpPT0yKXtwcmVkNTBba109YjFba119OyBpZihpPT0zKXtwcmVkOTVba109YjFba119DQogIH0NCn0NCnBlcmNlbnRpbGUuNT1yb3VuZChwcmVkNSwgZGlnaXRzPTIpO3BlcmNlbnRpbGUuNTA9cm91bmQocHJlZDUwLCBkaWdpdHM9Mik7cGVyY2VudGlsZS45NT1yb3VuZChwcmVkOTUsIGRpZ2l0cz0yKQ0KcmVzdWx0X2FsUENBPC1jYmluZC5kYXRhLmZyYW1lKHBlcmNlbnRpbGUuNSxwZXJjZW50aWxlLjUwLHBlcmNlbnRpbGUuOTUpDQoNCnJtKGRhdCk7cm0oZXJyb3Jlc1QyKTtybShtZXRvZG9zLlQxKTtybShtZXRvZG9zLlQxMik7cm0ocGNhKQ0Kcm0ocHJlZF9UMik7cm0oYjEpOw0Kcm0oY290YSk7cm0oZGlzLmFqdXN0KTtybShlbnNheW8pO3JtKGkpO3JtKGopO3JtKGspO3JtKGtrKQ0Kcm0obl9wKTtybSh5dSk7cm0ocHJlZF9UMyk7cm0ocmVzdWx0YWRvKTtybShwcmVkNSk7cm0ocHJlZDUwKTtybShwcmVkOTUpDQpybShwZXJjZW50aWxlLjUpO3JtKHBlcmNlbnRpbGUuNTApO3JtKHBlcmNlbnRpbGUuOTUpDQoNCiMjIFlvdSBjYW4gY2hvb3NlIHRoZSBwZXJjZW50aWxlIGNvbHVtbiBhbmQgY29ycmVzcG9uZGluZyBwcmVkaWN0aW9uDQpyZXN1bHRfYWxQQ0ENCnJlc3VsdF9hbFBDQV85bWVzZXM8LXJlc3VsdF9hbFBDQSRwZXJjZW50aWxlLjk1DQoNCmBgYA0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmRhdG9zIDwtIHJlYWQueGxzeCgiZGF0b3MueGxzeCIpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciA5X21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICI5X21lc2VzIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzE0Njpucm93KGRhdG9zKSwgXQ0KDQpgYGANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxlY2hvPUZBTFNFfQ0Kc2VyaWVfMTIgPC0gYyhkYXRvcyRWYWxvciwgcmVzdWx0X2FsUENBJHBlcmNlbnRpbGUuOTUpDQoNCnk9dHMoKHNlcmllXzEyKSwNCiAgICAgc3RhcnQ9YygyMDEzLDIpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCg0KdWx0aW1vXzEyIDwtIHRhaWwoeSwgMTIpDQoNCiMgQ3JlYXIgZWwgZ3LDoWZpY28gdXRpbGl6YW5kbyBhdXRvcGxvdCBkZSBnZ3Bsb3QyDQpnZ3Bsb3QoKSArDQogICMgQWdyZWdhciBsYSBsw61uZWEgY29tcGxldGEgZW4gY29sb3IgYXp1bA0KICBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICIjNEM3MkIwIikgKw0KICAjIEFncmVnYXIgc29sbyBsb3Mgw7psdGltb3MgMTIgcHVudG9zIGVuIGNvbG9yIHJvam8NCiAgZ2VvbV9saW5lKGRhdGEgPSB0YWlsKGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCAxMiksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gInJlZCIpICsNCiAgIyBQZXJzb25hbGl6YXIgZWwgYXNwZWN0byBkZWwgZ3LDoWZpY28NCiAgdGhlbWVfbWluaW1hbCgpICsgICMgRXN0YWJsZWNlciB1biB0ZW1hIG1pbmltYWxpc3RhDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1heW9yDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWVub3INCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxvcyBlamVzDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLCAgIyBUYW1hw7FvIHkgZXN0aWxvIGRlbCB0w610dWxvDQogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpLCAgIyBUYW1hw7FvIHkgYWxpbmVhY2nDs24gZGVsIHBpZSBkZSBww6FnaW5hDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBlbCB0w610dWxvIGRlIGxhIGxleWVuZGENCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgICMgUG9zaWNpb25hciBsYSBsZXllbmRhIGVuIGxhIHBhcnRlIGluZmVyaW9yDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsYSBsZXllbmRhDQogICkgKw0KICBsYWJzKA0KICAgIHggPSAiVGllbXBvIiwgICAgICMgRXRpcXVldGEgZGVsIGVqZSB4DQogICAgeSA9ICLDjW5kaWNlIG5vbWluYWwiLCAgICAgICMgRXRpcXVldGEgZGVsIGVqZSB5DQogICAgdGl0bGUgPSAiTGV0cmFzIGEgOSBtZXNlcyIsICAjIFTDrXR1bG8gZGVsIGdyw6FmaWNvDQogICAgICMgVGV4dG8gZGUgbGEgZnVlbnRlIGRlIGRhdG9zDQogICkNCg0KDQpgYGANCiMjIyMgMTIgbWVzZXMNClBvciDDumx0aW1vIGVuIGVsIGFwYXJ0YWRvIGRlIGxldHJhcyBwcmVkZWNpbW9zIGxhcyBkZSAxMiBtZXNlcy4NCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgMTJfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlgxMl9tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1sxOm5yb3coZGF0b3MpLCBdDQoNCng9dHMocmV2KGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDAwLDEpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmRhdG9zPWxpc3QoeD14KQ0KDQpgYGANCg0KYGBge3IsZWNobz1GQUxTRX0NCg0KIyMgVGhlIGFsZ29yaXRobSBpcyBkZXNpZ25lZCB0byBvYnRhaW4gdGhlIHByZWRpY3Rpb25zIG9mIGEgbW9udGhseSBzZXJpZXMNCmVuc2F5bzwtMTpsZW5ndGgoZGF0b3MpDQoNCiMjIEFwcGx5IGVhY2ggbWV0aG9kIHdpdGggYWxsIGl0cyBjb25maWd1cmF0aW9ucyBpbiBwZXJpb2QgVDENCm1ldG9kb3MuVDE8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMVtbal1dPC1jYWxjdWxvMShqKQ0KfQ0KDQojIyBPYnRhaW5zIGxvc3MgZnVuY3Rpb25zIGluIFQxIGFuZCBUMg0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMVtbal1dPC1jYWxjdWxvMihqKSANCn0NCmVycm9yZXNUMjwtbGlzdCgpIA0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgZXJyb3Jlc1QyW1tqXV08LWNhbGN1bG8zKGopDQp9DQoNCiMjIENhbGN1bGF0ZXMgUENBIGFuZCBNYW5oYXR0YW4gZGlzdGFuY2UgdG8gdGhlIG1ldGhvZCBhc3NvY2lhdGVkIHdpdGggdGhlIHNtYWxsZXN0IGVycm9yDQpyZXN1bHRhZG88LWxpc3QoKQ0KcGNhPC1yZXN1bHRhZG9bWzFdXTwtcmVzdWx0YWRvW1syXV08LXJlc3VsdGFkb1tbM11dPC1saXN0KCkNCmRpcy5hanVzdDwtcmVwKE5BLGxlbmd0aChlbnNheW8pKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgcmVzdWx0YWRvPC1jYWxjdWxvNC5tKGopIA0KICBpZihzdHJfZGV0ZWN0KHJlc3VsdGFkb1tbM11dW1tqXV1bMSwxXSwgIm1sZXhwIik9PVRSVUUpIHtkaXMuYWp1c3Rbal08LTF9IGVsc2UgDQogICAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGdhbW1hIik9PVRSVUUpIHtkaXMuYWp1c3Rbal08LTJ9IGVsc2Uge2Rpcy5hanVzdFtqXTwtM30NCn0NCg0KDQojIyBhbFBDQSBlc3RhYmxpc2hlcyB0aGUgcGVyY2VudGlsZXMgb2YgdGhlIGFkanVzdGVkIGRpc3RyaWJ1dGlvbiBhcyBjdXQtb2ZmIHBvaW50cyBmb3IgbWV0aG9kIHNlbGVjdGlvbg0KY290YTwtYygwLjA1LDAuNTAsIDAuOTUpIA0KcmVzdWx0YWRvW1s0XV08LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsgIA0KICByZXN1bHRhZG9bWzRdXVtbal1dPC1saXN0KCkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGNvdGEpKSB7ICANCiAgICByZXN1bHRhZG88LWNhbGN1bG81KGosaSkNCiAgfQ0KfQ0KDQojIyBPYnRhaW4gdGhlIHByZWRpY3Rpb25zIGZvciB0aGUgcGVyaW9kIFQxK1QyDQptZXRvZG9zLlQxMjwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxMltbal1dPC1jYWxjdWxvNihqKQ0KfQ0KDQojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgT2J0YWlucyB0aGUgcHJlZGljdGlvbnMgZm9yIFQzIGFjY29yZGluZyB0byB0aGUgcGVyY2VudGlsZSBjaG9zZW4gYnkgdGhlIHVzZXINCmo9MTtuX3A9MTI7cHJlZDU9cHJlZDUwPXByZWQ5NT1OVUxMO3l1PTENCmIxPC1yZXAoTkEsbl9wKQ0KIyNmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KZm9yIChpIGluIDE6bGVuZ3RoKGNvdGEpKSB7DQogIA0KICBuX3A8LTEyICNwcmVkaWNjaW9uZXMNCiAgZGF0PC1yZXN1bHRhZG9bWzRdXVtbal1dW1tpXV0NCiAgIyNNYXRyaXogcHJlZGljY2lvbmVzIG3DqXRvZG9zIHBhcmEgVDINCiAgcHJlZF9UMjwtcHJlZF9UMzwtbWF0cml4KE5BLG5yb3c9ZGltKGRhdClbMV0sbmNvbCA9IG5fcCkNCiAgZm9yIChrIGluIDE6ZGltKGRhdClbMV0pIHsNCiAgICBmb3IgKGtrIGluIDE6bGVuZ3RoKG1ldG9kb3MuVDFbW2pdXSkpIHsgDQogICAgICAjcHJpbnQobWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFuKQ0KICAgICAgaWYoZGF0W2ssMV09PWtrKXtwcmVkX1QyW2ssXTwtbWV0b2Rvcy5UMVtbal1dW1tra11dJG1lYW47IHByZWRfVDNbayxdPC1tZXRvZG9zLlQxMltbal1dW1tra11dJG1lYW59DQogICAgfQ0KICB9DQogIGZvciAoayBpbiAxOm5fcCkgew0KICAgIGIxW2tdPC1wcmVkX1QzWyxrXSUqJWRhdFssMTFdO2lmKGk9PTEpe3ByZWQ1W2tdPWIxW2tdfTsgaWYoaT09Mil7cHJlZDUwW2tdPWIxW2tdfTsgaWYoaT09Myl7cHJlZDk1W2tdPWIxW2tdfQ0KICB9DQp9DQpwZXJjZW50aWxlLjU9cm91bmQocHJlZDUsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjUwPXJvdW5kKHByZWQ1MCwgZGlnaXRzPTIpO3BlcmNlbnRpbGUuOTU9cm91bmQocHJlZDk1LCBkaWdpdHM9MikNCnJlc3VsdF9hbFBDQTwtY2JpbmQuZGF0YS5mcmFtZShwZXJjZW50aWxlLjUscGVyY2VudGlsZS41MCxwZXJjZW50aWxlLjk1KQ0KDQpybShkYXQpO3JtKGVycm9yZXNUMik7cm0obWV0b2Rvcy5UMSk7cm0obWV0b2Rvcy5UMTIpO3JtKHBjYSkNCnJtKHByZWRfVDIpO3JtKGIxKTsNCnJtKGNvdGEpO3JtKGRpcy5hanVzdCk7cm0oZW5zYXlvKTtybShpKTtybShqKTtybShrKTtybShraykNCnJtKG5fcCk7cm0oeXUpO3JtKHByZWRfVDMpO3JtKHJlc3VsdGFkbyk7cm0ocHJlZDUpO3JtKHByZWQ1MCk7cm0ocHJlZDk1KQ0Kcm0ocGVyY2VudGlsZS41KTtybShwZXJjZW50aWxlLjUwKTtybShwZXJjZW50aWxlLjk1KQ0KDQojIyBZb3UgY2FuIGNob29zZSB0aGUgcGVyY2VudGlsZSBjb2x1bW4gYW5kIGNvcnJlc3BvbmRpbmcgcHJlZGljdGlvbg0KcmVzdWx0X2FsUENBDQpyZXN1bHRfYWxQQ0FfMTJtZXNlczwtcmVzdWx0X2FsUENBJHBlcmNlbnRpbGUuOTUNCg0KYGBgDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3MgPC0gcmVhZC54bHN4KCJkYXRvcy54bHN4IikNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDNfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIjEyX21lc2VzIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzE6bnJvdyhkYXRvcyksIF0NCg0KYGBgDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsZWNobz1GQUxTRX0NCnNlcmllXzEyIDwtIGMoZGF0b3MkVmFsb3IsIHJlc3VsdF9hbFBDQSRwZXJjZW50aWxlLjk1KQ0KDQp5PXRzKChzZXJpZV8xMiksDQogICAgIHN0YXJ0PWMoMjAwMCwxKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCnVsdGltb18xMiA8LSB0YWlsKHksIDEyKQ0KDQojIENyZWFyIGVsIGdyw6FmaWNvIHV0aWxpemFuZG8gYXV0b3Bsb3QgZGUgZ2dwbG90Mg0KZ2dwbG90KCkgKw0KICAjIEFncmVnYXIgbGEgbMOtbmVhIGNvbXBsZXRhIGVuIGNvbG9yIGF6dWwNCiAgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHRpZW1wbyA9IHRpbWUoeSksIHkgPSB5KSwgYWVzKHggPSB0aWVtcG8sIHkgPSB5KSwgY29sb3IgPSAiIzRDNzJCMCIpICsNCiAgIyBBZ3JlZ2FyIHNvbG8gbG9zIMO6bHRpbW9zIDEyIHB1bnRvcyBlbiBjb2xvciByb2pvDQogIGdlb21fbGluZShkYXRhID0gdGFpbChkYXRhLmZyYW1lKHRpZW1wbyA9IHRpbWUoeSksIHkgPSB5KSwgMTIpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICJyZWQiKSArDQogICMgUGVyc29uYWxpemFyIGVsIGFzcGVjdG8gZGVsIGdyw6FmaWNvDQogIHRoZW1lX21pbmltYWwoKSArICAjIEVzdGFibGVjZXIgdW4gdGVtYSBtaW5pbWFsaXN0YQ0KICB0aGVtZSgNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBsYXMgbMOtbmVhcyBkZSBsYSBjdWFkcsOtY3VsYSBtYXlvcg0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1lbm9yDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsb3MgZWplcw0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwgICMgVGFtYcOxbyB5IGVzdGlsbyBkZWwgdMOtdHVsbw0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSwgICMgVGFtYcOxbyB5IGFsaW5lYWNpw7NuIGRlbCBwaWUgZGUgcMOhZ2luYQ0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgZWwgdMOtdHVsbyBkZSBsYSBsZXllbmRhDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsICAjIFBvc2ljaW9uYXIgbGEgbGV5ZW5kYSBlbiBsYSBwYXJ0ZSBpbmZlcmlvcg0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkgICMgVGFtYcOxbyBkZWwgdGV4dG8gZGUgbGEgbGV5ZW5kYQ0KICApICsNCiAgbGFicygNCiAgICB4ID0gIlRpZW1wbyIsICAgICAjIEV0aXF1ZXRhIGRlbCBlamUgeA0KICAgIHkgPSAiw41uZGljZSBub21pbmFsIiwgICAgICAjIEV0aXF1ZXRhIGRlbCBlamUgeQ0KICAgIHRpdGxlID0gIkxldHJhcyBhIDEyIG1lc2VzIiwgICMgVMOtdHVsbyBkZWwgZ3LDoWZpY28NCiAgICAgIyBUZXh0byBkZSBsYSBmdWVudGUgZGUgZGF0b3MNCiAgKQ0KDQoNCmBgYA0KIyMjIyAzIGHDsW9zDQpCb25vcyBhIDMgYcOxb3MgDQoNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgMTJfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlgzX2Fub3MiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTU6bnJvdyhkYXRvcyksIF0NCg0KeD10cyhyZXYoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMDgsMTEpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmRhdG9zPWxpc3QoeD14KQ0KDQpgYGANCg0KYGBge3IsZWNobz1GQUxTRX0NCg0KIyMgVGhlIGFsZ29yaXRobSBpcyBkZXNpZ25lZCB0byBvYnRhaW4gdGhlIHByZWRpY3Rpb25zIG9mIGEgbW9udGhseSBzZXJpZXMNCmVuc2F5bzwtMTpsZW5ndGgoZGF0b3MpDQoNCiMjIEFwcGx5IGVhY2ggbWV0aG9kIHdpdGggYWxsIGl0cyBjb25maWd1cmF0aW9ucyBpbiBwZXJpb2QgVDENCm1ldG9kb3MuVDE8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMVtbal1dPC1jYWxjdWxvMShqKQ0KfQ0KDQojIyBPYnRhaW5zIGxvc3MgZnVuY3Rpb25zIGluIFQxIGFuZCBUMg0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMVtbal1dPC1jYWxjdWxvMihqKSANCn0NCmVycm9yZXNUMjwtbGlzdCgpIA0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgZXJyb3Jlc1QyW1tqXV08LWNhbGN1bG8zKGopDQp9DQoNCiMjIENhbGN1bGF0ZXMgUENBIGFuZCBNYW5oYXR0YW4gZGlzdGFuY2UgdG8gdGhlIG1ldGhvZCBhc3NvY2lhdGVkIHdpdGggdGhlIHNtYWxsZXN0IGVycm9yDQpyZXN1bHRhZG88LWxpc3QoKQ0KcGNhPC1yZXN1bHRhZG9bWzFdXTwtcmVzdWx0YWRvW1syXV08LXJlc3VsdGFkb1tbM11dPC1saXN0KCkNCmRpcy5hanVzdDwtcmVwKE5BLGxlbmd0aChlbnNheW8pKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgcmVzdWx0YWRvPC1jYWxjdWxvNC5tKGopIA0KICBpZihzdHJfZGV0ZWN0KHJlc3VsdGFkb1tbM11dW1tqXV1bMSwxXSwgIm1sZXhwIik9PVRSVUUpIHtkaXMuYWp1c3Rbal08LTF9IGVsc2UgDQogICAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGdhbW1hIik9PVRSVUUpIHtkaXMuYWp1c3Rbal08LTJ9IGVsc2Uge2Rpcy5hanVzdFtqXTwtM30NCn0NCg0KDQojIyBhbFBDQSBlc3RhYmxpc2hlcyB0aGUgcGVyY2VudGlsZXMgb2YgdGhlIGFkanVzdGVkIGRpc3RyaWJ1dGlvbiBhcyBjdXQtb2ZmIHBvaW50cyBmb3IgbWV0aG9kIHNlbGVjdGlvbg0KY290YTwtYygwLjA1LDAuNTAsIDAuOTUpIA0KcmVzdWx0YWRvW1s0XV08LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsgIA0KICByZXN1bHRhZG9bWzRdXVtbal1dPC1saXN0KCkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGNvdGEpKSB7ICANCiAgICByZXN1bHRhZG88LWNhbGN1bG81KGosaSkNCiAgfQ0KfQ0KDQojIyBPYnRhaW4gdGhlIHByZWRpY3Rpb25zIGZvciB0aGUgcGVyaW9kIFQxK1QyDQptZXRvZG9zLlQxMjwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxMltbal1dPC1jYWxjdWxvNihqKQ0KfQ0KDQojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgT2J0YWlucyB0aGUgcHJlZGljdGlvbnMgZm9yIFQzIGFjY29yZGluZyB0byB0aGUgcGVyY2VudGlsZSBjaG9zZW4gYnkgdGhlIHVzZXINCmo9MTtuX3A9MTI7cHJlZDU9cHJlZDUwPXByZWQ5NT1OVUxMO3l1PTENCmIxPC1yZXAoTkEsbl9wKQ0KIyNmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KZm9yIChpIGluIDE6bGVuZ3RoKGNvdGEpKSB7DQogIA0KICBuX3A8LTEyICNwcmVkaWNjaW9uZXMNCiAgZGF0PC1yZXN1bHRhZG9bWzRdXVtbal1dW1tpXV0NCiAgIyNNYXRyaXogcHJlZGljY2lvbmVzIG3DqXRvZG9zIHBhcmEgVDINCiAgcHJlZF9UMjwtcHJlZF9UMzwtbWF0cml4KE5BLG5yb3c9ZGltKGRhdClbMV0sbmNvbCA9IG5fcCkNCiAgZm9yIChrIGluIDE6ZGltKGRhdClbMV0pIHsNCiAgICBmb3IgKGtrIGluIDE6bGVuZ3RoKG1ldG9kb3MuVDFbW2pdXSkpIHsgDQogICAgICAjcHJpbnQobWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFuKQ0KICAgICAgaWYoZGF0W2ssMV09PWtrKXtwcmVkX1QyW2ssXTwtbWV0b2Rvcy5UMVtbal1dW1tra11dJG1lYW47IHByZWRfVDNbayxdPC1tZXRvZG9zLlQxMltbal1dW1tra11dJG1lYW59DQogICAgfQ0KICB9DQogIGZvciAoayBpbiAxOm5fcCkgew0KICAgIGIxW2tdPC1wcmVkX1QzWyxrXSUqJWRhdFssMTFdO2lmKGk9PTEpe3ByZWQ1W2tdPWIxW2tdfTsgaWYoaT09Mil7cHJlZDUwW2tdPWIxW2tdfTsgaWYoaT09Myl7cHJlZDk1W2tdPWIxW2tdfQ0KICB9DQp9DQpwZXJjZW50aWxlLjU9cm91bmQocHJlZDUsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjUwPXJvdW5kKHByZWQ1MCwgZGlnaXRzPTIpO3BlcmNlbnRpbGUuOTU9cm91bmQocHJlZDk1LCBkaWdpdHM9MikNCnJlc3VsdF9hbFBDQTwtY2JpbmQuZGF0YS5mcmFtZShwZXJjZW50aWxlLjUscGVyY2VudGlsZS41MCxwZXJjZW50aWxlLjk1KQ0KDQpybShkYXQpO3JtKGVycm9yZXNUMik7cm0obWV0b2Rvcy5UMSk7cm0obWV0b2Rvcy5UMTIpO3JtKHBjYSkNCnJtKHByZWRfVDIpO3JtKGIxKTsNCnJtKGNvdGEpO3JtKGRpcy5hanVzdCk7cm0oZW5zYXlvKTtybShpKTtybShqKTtybShrKTtybShraykNCnJtKG5fcCk7cm0oeXUpO3JtKHByZWRfVDMpO3JtKHJlc3VsdGFkbyk7cm0ocHJlZDUpO3JtKHByZWQ1MCk7cm0ocHJlZDk1KQ0Kcm0ocGVyY2VudGlsZS41KTtybShwZXJjZW50aWxlLjUwKTtybShwZXJjZW50aWxlLjk1KQ0KDQojIyBZb3UgY2FuIGNob29zZSB0aGUgcGVyY2VudGlsZSBjb2x1bW4gYW5kIGNvcnJlc3BvbmRpbmcgcHJlZGljdGlvbg0KcmVzdWx0X2FsUENBDQoNCmBgYA0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYM19hbm9zIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzk1Om5yb3coZGF0b3MpLCBdDQojIGNvbXByb2JhciAgMjAwOCBub3ZpZW1icmUNCg0KYGBgDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsZWNobz1GQUxTRX0NCnNlcmllXzEyIDwtIGMoZGF0b3MkVmFsb3IsIHJlc3VsdF9hbFBDQSRwZXJjZW50aWxlLjk1KQ0KDQp5PXRzKChzZXJpZV8xMiksDQogICAgIHN0YXJ0PWMoMjAwOCwxMSksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KDQp1bHRpbW9fMTIgPC0gdGFpbCh5LCAxMikNCg0KIyBDcmVhciBlbCBncsOhZmljbyB1dGlsaXphbmRvIGF1dG9wbG90IGRlIGdncGxvdDINCmdncGxvdCgpICsNCiAgIyBBZ3JlZ2FyIGxhIGzDrW5lYSBjb21wbGV0YSBlbiBjb2xvciBhenVsDQogIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh0aWVtcG8gPSB0aW1lKHkpLCB5ID0geSksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gIiM0QzcyQjAiKSArDQogICMgQWdyZWdhciBzb2xvIGxvcyDDumx0aW1vcyAxMiBwdW50b3MgZW4gY29sb3Igcm9qbw0KICBnZW9tX2xpbmUoZGF0YSA9IHRhaWwoZGF0YS5mcmFtZSh0aWVtcG8gPSB0aW1lKHkpLCB5ID0geSksIDEyKSwgYWVzKHggPSB0aWVtcG8sIHkgPSB5KSwgY29sb3IgPSAicmVkIikgKw0KICAjIFBlcnNvbmFsaXphciBlbCBhc3BlY3RvIGRlbCBncsOhZmljbw0KICB0aGVtZV9taW5pbWFsKCkgKyAgIyBFc3RhYmxlY2VyIHVuIHRlbWEgbWluaW1hbGlzdGENCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWF5b3INCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBsYXMgbMOtbmVhcyBkZSBsYSBjdWFkcsOtY3VsYSBtZW5vcg0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgVGFtYcOxbyBkZWwgdGV4dG8gZGUgbG9zIGVqZXMNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksICAjIFRhbWHDsW8geSBlc3RpbG8gZGVsIHTDrXR1bG8NCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSksICAjIFRhbWHDsW8geSBhbGluZWFjacOzbiBkZWwgcGllIGRlIHDDoWdpbmENCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGVsIHTDrXR1bG8gZGUgbGEgbGV5ZW5kYQ0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAgIyBQb3NpY2lvbmFyIGxhIGxleWVuZGEgZW4gbGEgcGFydGUgaW5mZXJpb3INCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxhIGxleWVuZGENCiAgKSArDQogIGxhYnMoDQogICAgeCA9ICJUaWVtcG8iLCAgICAgIyBFdGlxdWV0YSBkZWwgZWplIHgNCiAgICB5ID0gIsONbmRpY2Ugbm9taW5hbCIsICAgICAgIyBFdGlxdWV0YSBkZWwgZWplIHkNCiAgICB0aXRsZSA9ICJCb25vcyBhIDMgYcOxb3MiLCAgIyBUw610dWxvIGRlbCBncsOhZmljbw0KICAgICAjIFRleHRvIGRlIGxhIGZ1ZW50ZSBkZSBkYXRvcw0KICApDQoNCg0KYGBgDQojIyMjIDUgYcOxb3MNCg0KQm9ub3MgYSA1IGHDsW9zIA0KDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDEyX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYNV9hbm9zIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzk0Om5yb3coZGF0b3MpLCBdDQoNCng9dHMocmV2KGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDA4LDEwKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQpkYXRvcz1saXN0KHg9eCkNCg0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0V9DQoNCiMjIFRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQgdG8gb2J0YWluIHRoZSBwcmVkaWN0aW9ucyBvZiBhIG1vbnRobHkgc2VyaWVzDQplbnNheW88LTE6bGVuZ3RoKGRhdG9zKQ0KDQojIyBBcHBseSBlYWNoIG1ldGhvZCB3aXRoIGFsbCBpdHMgY29uZmlndXJhdGlvbnMgaW4gcGVyaW9kIFQxDQptZXRvZG9zLlQxPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzEoaikNCn0NCg0KIyMgT2J0YWlucyBsb3NzIGZ1bmN0aW9ucyBpbiBUMSBhbmQgVDINCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzIoaikgDQp9DQplcnJvcmVzVDI8LWxpc3QoKSANCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIGVycm9yZXNUMltbal1dPC1jYWxjdWxvMyhqKQ0KfQ0KDQojIyBDYWxjdWxhdGVzIFBDQSBhbmQgTWFuaGF0dGFuIGRpc3RhbmNlIHRvIHRoZSBtZXRob2QgYXNzb2NpYXRlZCB3aXRoIHRoZSBzbWFsbGVzdCBlcnJvcg0KcmVzdWx0YWRvPC1saXN0KCkNCnBjYTwtcmVzdWx0YWRvW1sxXV08LXJlc3VsdGFkb1tbMl1dPC1yZXN1bHRhZG9bWzNdXTwtbGlzdCgpDQpkaXMuYWp1c3Q8LXJlcChOQSxsZW5ndGgoZW5zYXlvKSkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIHJlc3VsdGFkbzwtY2FsY3VsbzQubShqKSANCiAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGV4cCIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0xfSBlbHNlIA0KICAgIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxnYW1tYSIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0yfSBlbHNlIHtkaXMuYWp1c3Rbal08LTN9DQp9DQoNCg0KIyMgYWxQQ0EgZXN0YWJsaXNoZXMgdGhlIHBlcmNlbnRpbGVzIG9mIHRoZSBhZGp1c3RlZCBkaXN0cmlidXRpb24gYXMgY3V0LW9mZiBwb2ludHMgZm9yIG1ldGhvZCBzZWxlY3Rpb24NCmNvdGE8LWMoMC4wNSwwLjUwLCAwLjk1KSANCnJlc3VsdGFkb1tbNF1dPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7ICANCiAgcmVzdWx0YWRvW1s0XV1bW2pdXTwtbGlzdCgpDQogIGZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgeyAgDQogICAgcmVzdWx0YWRvPC1jYWxjdWxvNShqLGkpDQogIH0NCn0NCg0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHBlcmlvZCBUMStUMg0KbWV0b2Rvcy5UMTI8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMTJbW2pdXTwtY2FsY3VsbzYoaikNCn0NCg0KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIE9idGFpbnMgdGhlIHByZWRpY3Rpb25zIGZvciBUMyBhY2NvcmRpbmcgdG8gdGhlIHBlcmNlbnRpbGUgY2hvc2VuIGJ5IHRoZSB1c2VyDQpqPTE7bl9wPTEyO3ByZWQ1PXByZWQ1MD1wcmVkOTU9TlVMTDt5dT0xDQpiMTwtcmVwKE5BLG5fcCkNCiMjZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCmZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgew0KICANCiAgbl9wPC0xMiAjcHJlZGljY2lvbmVzDQogIGRhdDwtcmVzdWx0YWRvW1s0XV1bW2pdXVtbaV1dDQogICMjTWF0cml6IHByZWRpY2Npb25lcyBtw6l0b2RvcyBwYXJhIFQyDQogIHByZWRfVDI8LXByZWRfVDM8LW1hdHJpeChOQSxucm93PWRpbShkYXQpWzFdLG5jb2wgPSBuX3ApDQogIGZvciAoayBpbiAxOmRpbShkYXQpWzFdKSB7DQogICAgZm9yIChrayBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1tqXV0pKSB7IA0KICAgICAgI3ByaW50KG1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbikNCiAgICAgIGlmKGRhdFtrLDFdPT1rayl7cHJlZF9UMltrLF08LW1ldG9kb3MuVDFbW2pdXVtba2tdXSRtZWFuOyBwcmVkX1QzW2ssXTwtbWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFufQ0KICAgIH0NCiAgfQ0KICBmb3IgKGsgaW4gMTpuX3ApIHsNCiAgICBiMVtrXTwtcHJlZF9UM1ssa10lKiVkYXRbLDExXTtpZihpPT0xKXtwcmVkNVtrXT1iMVtrXX07IGlmKGk9PTIpe3ByZWQ1MFtrXT1iMVtrXX07IGlmKGk9PTMpe3ByZWQ5NVtrXT1iMVtrXX0NCiAgfQ0KfQ0KcGVyY2VudGlsZS41PXJvdW5kKHByZWQ1LCBkaWdpdHM9Mik7cGVyY2VudGlsZS41MD1yb3VuZChwcmVkNTAsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjk1PXJvdW5kKHByZWQ5NSwgZGlnaXRzPTIpDQpyZXN1bHRfYWxQQ0E8LWNiaW5kLmRhdGEuZnJhbWUocGVyY2VudGlsZS41LHBlcmNlbnRpbGUuNTAscGVyY2VudGlsZS45NSkNCg0Kcm0oZGF0KTtybShlcnJvcmVzVDIpO3JtKG1ldG9kb3MuVDEpO3JtKG1ldG9kb3MuVDEyKTtybShwY2EpDQpybShwcmVkX1QyKTtybShiMSk7DQpybShjb3RhKTtybShkaXMuYWp1c3QpO3JtKGVuc2F5byk7cm0oaSk7cm0oaik7cm0oayk7cm0oa2spDQpybShuX3ApO3JtKHl1KTtybShwcmVkX1QzKTtybShyZXN1bHRhZG8pO3JtKHByZWQ1KTtybShwcmVkNTApO3JtKHByZWQ5NSkNCnJtKHBlcmNlbnRpbGUuNSk7cm0ocGVyY2VudGlsZS41MCk7cm0ocGVyY2VudGlsZS45NSkNCg0KIyMgWW91IGNhbiBjaG9vc2UgdGhlIHBlcmNlbnRpbGUgY29sdW1uIGFuZCBjb3JyZXNwb25kaW5nIHByZWRpY3Rpb24NCnJlc3VsdF9hbFBDQQ0KDQpgYGANCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDVfYW5vcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NDpucm93KGRhdG9zKSwgXQ0KIyBjb21wcm9iYXIgIDIwMDggb2N0DQoNCmBgYA0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLGVjaG89RkFMU0V9DQpzZXJpZV8xMiA8LSBjKGRhdG9zJFZhbG9yLCByZXN1bHRfYWxQQ0EkcGVyY2VudGlsZS45NSkNCg0KeT10cygoc2VyaWVfMTIpLA0KICAgICBzdGFydD1jKDIwMDgsMTApLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCg0KdWx0aW1vXzEyIDwtIHRhaWwoeSwgMTIpDQoNCiMgQ3JlYXIgZWwgZ3LDoWZpY28gdXRpbGl6YW5kbyBhdXRvcGxvdCBkZSBnZ3Bsb3QyDQpnZ3Bsb3QoKSArDQogICMgQWdyZWdhciBsYSBsw61uZWEgY29tcGxldGEgZW4gY29sb3IgYXp1bA0KICBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICIjNEM3MkIwIikgKw0KICAjIEFncmVnYXIgc29sbyBsb3Mgw7psdGltb3MgMTIgcHVudG9zIGVuIGNvbG9yIHJvam8NCiAgZ2VvbV9saW5lKGRhdGEgPSB0YWlsKGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCAxMiksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gInJlZCIpICsNCiAgIyBQZXJzb25hbGl6YXIgZWwgYXNwZWN0byBkZWwgZ3LDoWZpY28NCiAgdGhlbWVfbWluaW1hbCgpICsgICMgRXN0YWJsZWNlciB1biB0ZW1hIG1pbmltYWxpc3RhDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1heW9yDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWVub3INCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxvcyBlamVzDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLCAgIyBUYW1hw7FvIHkgZXN0aWxvIGRlbCB0w610dWxvDQogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpLCAgIyBUYW1hw7FvIHkgYWxpbmVhY2nDs24gZGVsIHBpZSBkZSBww6FnaW5hDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBlbCB0w610dWxvIGRlIGxhIGxleWVuZGENCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgICMgUG9zaWNpb25hciBsYSBsZXllbmRhIGVuIGxhIHBhcnRlIGluZmVyaW9yDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsYSBsZXllbmRhDQogICkgKw0KICBsYWJzKA0KICAgIHggPSAiVGllbXBvIiwgICAgICMgRXRpcXVldGEgZGVsIGVqZSB4DQogICAgeSA9ICLDjW5kaWNlIG5vbWluYWwiLCAgICAgICMgRXRpcXVldGEgZGVsIGVqZSB5DQogICAgdGl0bGUgPSAiQm9ub3MgYSA1IGHDsW9zIiwgICMgVMOtdHVsbyBkZWwgZ3LDoWZpY28NCiAgICAgIyBUZXh0byBkZSBsYSBmdWVudGUgZGUgZGF0b3MNCiAgKQ0KDQoNCmBgYA0KIyMjIyAxMCBhw7Fvcw0KDQpCb25vcyBhIDEwIGHDsW9zIA0KDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDEyX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYMTBfYW5vcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5ODpucm93KGRhdG9zKSwgXQ0KDQp4PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOSwyKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQpkYXRvcz1saXN0KHg9eCkNCg0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0V9DQoNCiMjIFRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQgdG8gb2J0YWluIHRoZSBwcmVkaWN0aW9ucyBvZiBhIG1vbnRobHkgc2VyaWVzDQplbnNheW88LTE6bGVuZ3RoKGRhdG9zKQ0KDQojIyBBcHBseSBlYWNoIG1ldGhvZCB3aXRoIGFsbCBpdHMgY29uZmlndXJhdGlvbnMgaW4gcGVyaW9kIFQxDQptZXRvZG9zLlQxPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzEoaikNCn0NCg0KIyMgT2J0YWlucyBsb3NzIGZ1bmN0aW9ucyBpbiBUMSBhbmQgVDINCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzIoaikgDQp9DQplcnJvcmVzVDI8LWxpc3QoKSANCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIGVycm9yZXNUMltbal1dPC1jYWxjdWxvMyhqKQ0KfQ0KDQojIyBDYWxjdWxhdGVzIFBDQSBhbmQgTWFuaGF0dGFuIGRpc3RhbmNlIHRvIHRoZSBtZXRob2QgYXNzb2NpYXRlZCB3aXRoIHRoZSBzbWFsbGVzdCBlcnJvcg0KcmVzdWx0YWRvPC1saXN0KCkNCnBjYTwtcmVzdWx0YWRvW1sxXV08LXJlc3VsdGFkb1tbMl1dPC1yZXN1bHRhZG9bWzNdXTwtbGlzdCgpDQpkaXMuYWp1c3Q8LXJlcChOQSxsZW5ndGgoZW5zYXlvKSkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIHJlc3VsdGFkbzwtY2FsY3VsbzQubShqKSANCiAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGV4cCIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0xfSBlbHNlIA0KICAgIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxnYW1tYSIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0yfSBlbHNlIHtkaXMuYWp1c3Rbal08LTN9DQp9DQoNCg0KIyMgYWxQQ0EgZXN0YWJsaXNoZXMgdGhlIHBlcmNlbnRpbGVzIG9mIHRoZSBhZGp1c3RlZCBkaXN0cmlidXRpb24gYXMgY3V0LW9mZiBwb2ludHMgZm9yIG1ldGhvZCBzZWxlY3Rpb24NCmNvdGE8LWMoMC4wNSwwLjUwLCAwLjk1KSANCnJlc3VsdGFkb1tbNF1dPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7ICANCiAgcmVzdWx0YWRvW1s0XV1bW2pdXTwtbGlzdCgpDQogIGZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgeyAgDQogICAgcmVzdWx0YWRvPC1jYWxjdWxvNShqLGkpDQogIH0NCn0NCg0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHBlcmlvZCBUMStUMg0KbWV0b2Rvcy5UMTI8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMTJbW2pdXTwtY2FsY3VsbzYoaikNCn0NCg0KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIE9idGFpbnMgdGhlIHByZWRpY3Rpb25zIGZvciBUMyBhY2NvcmRpbmcgdG8gdGhlIHBlcmNlbnRpbGUgY2hvc2VuIGJ5IHRoZSB1c2VyDQpqPTE7bl9wPTEyO3ByZWQ1PXByZWQ1MD1wcmVkOTU9TlVMTDt5dT0xDQpiMTwtcmVwKE5BLG5fcCkNCiMjZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCmZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgew0KICANCiAgbl9wPC0xMiAjcHJlZGljY2lvbmVzDQogIGRhdDwtcmVzdWx0YWRvW1s0XV1bW2pdXVtbaV1dDQogICMjTWF0cml6IHByZWRpY2Npb25lcyBtw6l0b2RvcyBwYXJhIFQyDQogIHByZWRfVDI8LXByZWRfVDM8LW1hdHJpeChOQSxucm93PWRpbShkYXQpWzFdLG5jb2wgPSBuX3ApDQogIGZvciAoayBpbiAxOmRpbShkYXQpWzFdKSB7DQogICAgZm9yIChrayBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1tqXV0pKSB7IA0KICAgICAgI3ByaW50KG1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbikNCiAgICAgIGlmKGRhdFtrLDFdPT1rayl7cHJlZF9UMltrLF08LW1ldG9kb3MuVDFbW2pdXVtba2tdXSRtZWFuOyBwcmVkX1QzW2ssXTwtbWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFufQ0KICAgIH0NCiAgfQ0KICBmb3IgKGsgaW4gMTpuX3ApIHsNCiAgICBiMVtrXTwtcHJlZF9UM1ssa10lKiVkYXRbLDExXTtpZihpPT0xKXtwcmVkNVtrXT1iMVtrXX07IGlmKGk9PTIpe3ByZWQ1MFtrXT1iMVtrXX07IGlmKGk9PTMpe3ByZWQ5NVtrXT1iMVtrXX0NCiAgfQ0KfQ0KcGVyY2VudGlsZS41PXJvdW5kKHByZWQ1LCBkaWdpdHM9Mik7cGVyY2VudGlsZS41MD1yb3VuZChwcmVkNTAsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjk1PXJvdW5kKHByZWQ5NSwgZGlnaXRzPTIpDQpyZXN1bHRfYWxQQ0E8LWNiaW5kLmRhdGEuZnJhbWUocGVyY2VudGlsZS41LHBlcmNlbnRpbGUuNTAscGVyY2VudGlsZS45NSkNCg0Kcm0oZGF0KTtybShlcnJvcmVzVDIpO3JtKG1ldG9kb3MuVDEpO3JtKG1ldG9kb3MuVDEyKTtybShwY2EpDQpybShwcmVkX1QyKTtybShiMSk7DQpybShjb3RhKTtybShkaXMuYWp1c3QpO3JtKGVuc2F5byk7cm0oaSk7cm0oaik7cm0oayk7cm0oa2spDQpybShuX3ApO3JtKHl1KTtybShwcmVkX1QzKTtybShyZXN1bHRhZG8pO3JtKHByZWQ1KTtybShwcmVkNTApO3JtKHByZWQ5NSkNCnJtKHBlcmNlbnRpbGUuNSk7cm0ocGVyY2VudGlsZS41MCk7cm0ocGVyY2VudGlsZS45NSkNCg0KIyMgWW91IGNhbiBjaG9vc2UgdGhlIHBlcmNlbnRpbGUgY29sdW1uIGFuZCBjb3JyZXNwb25kaW5nIHByZWRpY3Rpb24NCnJlc3VsdF9hbFBDQQ0KDQpgYGANCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDEwX2Fub3MiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTg6bnJvdyhkYXRvcyksIF0NCg0KDQpgYGANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxlY2hvPUZBTFNFfQ0Kc2VyaWVfMTIgPC0gYyhkYXRvcyRWYWxvciwgcmVzdWx0X2FsUENBJHBlcmNlbnRpbGUuOTUpDQoNCnk9dHMoKHNlcmllXzEyKSwNCiAgICAgc3RhcnQ9YygyMDA5LDIpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCg0KdWx0aW1vXzEyIDwtIHRhaWwoeSwgMTIpDQoNCiMgQ3JlYXIgZWwgZ3LDoWZpY28gdXRpbGl6YW5kbyBhdXRvcGxvdCBkZSBnZ3Bsb3QyDQpnZ3Bsb3QoKSArDQogICMgQWdyZWdhciBsYSBsw61uZWEgY29tcGxldGEgZW4gY29sb3IgYXp1bA0KICBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICIjNEM3MkIwIikgKw0KICAjIEFncmVnYXIgc29sbyBsb3Mgw7psdGltb3MgMTIgcHVudG9zIGVuIGNvbG9yIHJvam8NCiAgZ2VvbV9saW5lKGRhdGEgPSB0YWlsKGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCAxMiksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gInJlZCIpICsNCiAgIyBQZXJzb25hbGl6YXIgZWwgYXNwZWN0byBkZWwgZ3LDoWZpY28NCiAgdGhlbWVfbWluaW1hbCgpICsgICMgRXN0YWJsZWNlciB1biB0ZW1hIG1pbmltYWxpc3RhDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1heW9yDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWVub3INCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxvcyBlamVzDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLCAgIyBUYW1hw7FvIHkgZXN0aWxvIGRlbCB0w610dWxvDQogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpLCAgIyBUYW1hw7FvIHkgYWxpbmVhY2nDs24gZGVsIHBpZSBkZSBww6FnaW5hDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBlbCB0w610dWxvIGRlIGxhIGxleWVuZGENCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgICMgUG9zaWNpb25hciBsYSBsZXllbmRhIGVuIGxhIHBhcnRlIGluZmVyaW9yDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsYSBsZXllbmRhDQogICkgKw0KICBsYWJzKA0KICAgIHggPSAiVGllbXBvIiwgICAgICMgRXRpcXVldGEgZGVsIGVqZSB4DQogICAgeSA9ICLDjW5kaWNlIG5vbWluYWwiLCAgICAgICMgRXRpcXVldGEgZGVsIGVqZSB5DQogICAgdGl0bGUgPSAiT2JsaWdhY2lvbmVzIGEgMTAgYcOxb3MiLCAgIyBUw610dWxvIGRlbCBncsOhZmljbw0KICAgICAjIFRleHRvIGRlIGxhIGZ1ZW50ZSBkZSBkYXRvcw0KICApDQoNCg0KYGBgDQojIyMgQ8OhbGN1bG9zDQoNClVuaW9uIGRlIHByZWRpY2Npb25lcw0KYGBge3J9DQppbmRpY2VzX2FsUENBPC1kYXRhLmZyYW1lKHJlc3VsdF9hbFBDQV8zbWVzZXMscmVzdWx0X2FsUENBXzZtZXNlcyxyZXN1bHRfYWxQQ0FfOW1lc2VzLHJlc3VsdF9hbFBDQV8xMm1lc2VzKQ0KYGBgDQoNClByb2dyYW1hDQoNCg0KDQpgYGB7cn0NCmNhbGN1bGFyX25ldG9fY29ydG8gPC0gZnVuY3Rpb24ocmV0b3Jub19icnV0bykgew0KICANCg0KICANCiAgIyBDYWxjdWxhIGxhIGNvbWlzacOzbiBiYW5jYXJpYQ0KICBpZiAocmV0b3Jub19icnV0byA8PSA2MDApIHsNCiAgICBDb21pc2lvbl9iYW5jbyA8LSAwLjkNCiAgfSBlbHNlIHsNCiAgICBDb21pc2lvbl9iYW5jbyA8LSByZXRvcm5vX2JydXRvICogMC4wMDE1DQogIH0NCiAgDQogICMgQ2FsY3VsYSBlbCBuZXRvIGRlc3B1w6lzIGRlIGltcHVlc3RvcyB5IGNvbWlzaW9uZXMNCiAgbmV0byA8LSByZXRvcm5vX2JydXRvIC0gQ29taXNpb25fYmFuY28NCiAgDQogICMgUmV0b3JuYSBlbCB2YWxvciBuZXRvDQogIHJldHVybihuZXRvKQ0KfQ0KDQoNCmBgYA0KDQpgYGB7cn0NCmJlbmVmaWNpbzwtMA0KYmVuZWZpY2lvX3RvdGFsIDwtIDANCmludmVyc2lvbjwtMTAwMDAwMA0KaW52ZXJzaW9uX2luaWNpYWw8LTEwMDAwMDANCnBvc2ljaW9uZXMgPC0gYygxLCA0LCA3LCAxMCkNCg0KDQpyZXN1bHRhZG9zXzNtZXNlcyA8LSBkYXRhLmZyYW1lKEJlbmVmaWNpb19Ub3RhbCA9IG51bWVyaWMoKSxCZW5lZmljaW9fb3BlcmFjaW9uID0gbnVtZXJpYygpLEJlbmVmaWNpb19yZXNpZHVhbD1udW1lcmljKCkgKQ0KDQoNCiMgSXRlcmFtb3Mgc29icmUgbGFzIGNvbHVtbmFzIGRlbCBkYXRhZnJhbWUNCmZvciAoaSBpbiBwb3NpY2lvbmVzKSB7DQogICAgIyBPYnRlbmVtb3MgZWwgYmVuZWZpY2lvIG11bHRpcGxpY2FuZG8gZWwgcHJpbWVyIGVsZW1lbnRvIGRlIGxhIGNvbHVtbmEgcG9yIGVsIHZhbG9yIGludHJvZHVjaWRvIHBvciBlbCB1c3VhcmlvDQogICAgYmVuZWZpY2lvX29wZXJhY2lvbjwtaW5kaWNlc19hbFBDQVtpLCAxXS8xMDAgKiBpbnZlcnNpb24gKiAzLzEyDQogICAgYmVuZWZpY2lvX25ldG88LWNhbGN1bGFyX25ldG9fY29ydG8oYmVuZWZpY2lvX29wZXJhY2lvbikgDQogICAgYmVuZWZpY2lvIDwtIGJlbmVmaWNpbytiZW5lZmljaW9fbmV0bw0KICAgIGJlbmVmaWNpb190b3RhbDwtYmVuZWZpY2lvX3RvdGFsK2JlbmVmaWNpb19uZXRvDQogICAgDQogICANCiAgICB3aGlsZSAoYmVuZWZpY2lvIC8gMTAwMCA+PSAxKSB7DQogICAgICAgIA0KICAgICAgICBpbnZlcnNpb24gPC0gaW52ZXJzaW9uICsgMTAwMA0KICAgICAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvIC0gMTAwMA0KICAgICAgICANCiAgICB9DQogICAgDQogICAgIHJlc3VsdGFkb3NfM21lc2VzPC1kYXRhLmZyYW1lKCBCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gYmVuZWZpY2lvX3RvdGFsLEJlbmVmaWNpb19PcGVyYWNpb25fTmV0byA9IGJlbmVmaWNpb19uZXRvKQ0KDQp9DQoNCnJlc3VsdGFkb3NfM21lc2VzDQoNCnRyZXNfbWVzZXM8LXJlc3VsdGFkb3NfM21lc2VzW25yb3cocmVzdWx0YWRvc18zbWVzZXMpLCAxXQ0KDQpgYGANCg0KDQpgYGB7cn0NCmJlbmVmaWNpbyA8LSAwDQpiZW5lZmljaW9fdG90YWwgPC0gMA0KaW52ZXJzaW9uIDwtIDEwMDAwMDANCmludmVyc2lvbl9pbmljaWFsIDwtIDEwMDAwMDANCnBvc2ljaW9uZXMgPC0gYygxLCA3KQ0KDQpyZXN1bHRhZG9zXzZtZXNlcyA8LSBkYXRhLmZyYW1lKEJlbmVmaWNpb19Ub3RhbF9BY3VtdWxhZG8gPSBudW1lcmljKCksIEJlbmVmaWNpb19PcGVyYWNpb25fTmV0byA9IG51bWVyaWMoKSwgQmVuZWZpY2lvX3Jlc2lkdWFsID0gbnVtZXJpYygpKQ0KDQojIEl0ZXJhbW9zIHNvYnJlIGxhcyBwb3NpY2lvbmVzIGVzcGVjw61maWNhcw0KZm9yIChpIGluIHBvc2ljaW9uZXMpIHsNCiAgICAjIE9idGVuZW1vcyBlbCBiZW5lZmljaW8gbXVsdGlwbGljYW5kbyBlbCBwcmltZXIgZWxlbWVudG8gZGUgbGEgY29sdW1uYSBwb3IgZWwgdmFsb3IgaW50cm9kdWNpZG8gcG9yIGVsIHVzdWFyaW8NCiAgICBiZW5lZmljaW9fb3BlcmFjaW9uIDwtIGluZGljZXNfYWxQQ0FbaSwgMl0gLyAxMDAgKiBpbnZlcnNpb24gKiA2IC8gMTINCiAgICBiZW5lZmljaW9fbmV0byA8LSBjYWxjdWxhcl9uZXRvX2NvcnRvKGJlbmVmaWNpb19vcGVyYWNpb24pIA0KICAgIGJlbmVmaWNpbyA8LSBiZW5lZmljaW8gKyBiZW5lZmljaW9fbmV0bw0KICAgIGJlbmVmaWNpb190b3RhbCA8LSBiZW5lZmljaW9fdG90YWwgKyBiZW5lZmljaW9fbmV0bw0KDQogICAgd2hpbGUgKGJlbmVmaWNpbyAvIDEwMDAgPj0gMSkgew0KICAgICAgICBpbnZlcnNpb24gPC0gaW52ZXJzaW9uICsgMTAwMA0KICAgICAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvIC0gMTAwMA0KICAgIH0NCg0KICAgIHJlc3VsdGFkb3NfNm1lc2VzIDwtICBkYXRhLmZyYW1lKEJlbmVmaWNpb19Ub3RhbF9BY3VtdWxhZG8gPSBiZW5lZmljaW9fdG90YWwsIEJlbmVmaWNpb19PcGVyYWNpb25fTmV0byA9IGJlbmVmaWNpb19uZXRvKQ0KICAgIGlmIChucm93KHJlc3VsdGFkb3NfNm1lc2VzKSA9PSAyKSBicmVhaw0KfQ0KDQpyZXN1bHRhZG9zXzZtZXNlcw0KDQpzZWlzX21lc2VzPC1yZXN1bHRhZG9zXzZtZXNlc1tucm93KHJlc3VsdGFkb3NfNm1lc2VzKSwgMV0NCg0KYGBgDQoNCmBgYHtyfQ0KYmVuZWZpY2lvIDwtIDANCmJlbmVmaWNpb190b3RhbCA8LSAwDQppbnZlcnNpb24gPC0gMTAwMDAwMA0KaW52ZXJzaW9uX2luaWNpYWwgPC0gMTAwMDAwMA0KDQoNCnJlc3VsdGFkb3NfOV8zX21lc2VzIDwtIGRhdGEuZnJhbWUoQmVuZWZpY2lvX1RvdGFsX0FjdW11bGFkbyA9IG51bWVyaWMoKSwgQmVuZWZpY2lvX09wZXJhY2lvbl9OZXRvID0gbnVtZXJpYygpLCBCZW5lZmljaW9fcmVzaWR1YWwgPSBudW1lcmljKCkpDQoNCiMgSXRlcmFtb3Mgc29icmUgbGFzIHBvc2ljaW9uZXMgZXNwZWPDrWZpY2FzDQoNCiAgICAjIE9idGVuZW1vcyBlbCBiZW5lZmljaW8gbXVsdGlwbGljYW5kbyBlbCBwcmltZXIgZWxlbWVudG8gZGUgbGEgY29sdW1uYSBwb3IgZWwgdmFsb3IgaW50cm9kdWNpZG8gcG9yIGVsIHVzdWFyaW8NCiAgICBiZW5lZmljaW9fb3BlcmFjaW9uIDwtIGluZGljZXNfYWxQQ0FbMSwgM10gLyAxMDAgKiBpbnZlcnNpb24gKiA5IC8gMTINCiAgICBiZW5lZmljaW9fbmV0byA8LSBjYWxjdWxhcl9uZXRvX2NvcnRvKGJlbmVmaWNpb19vcGVyYWNpb24pIA0KICAgIGJlbmVmaWNpbyA8LSBiZW5lZmljaW8gKyBiZW5lZmljaW9fbmV0bw0KICAgIGJlbmVmaWNpb190b3RhbCA8LSBiZW5lZmljaW9fdG90YWwgKyBiZW5lZmljaW9fbmV0bw0KDQogICAgd2hpbGUgKGJlbmVmaWNpbyAvIDEwMDAgPj0gMSkgew0KICAgICAgICBpbnZlcnNpb24gPC0gaW52ZXJzaW9uICsgMTAwMA0KICAgICAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvIC0gMTAwMA0KICAgIH0NCg0KICAgIHJlc3VsdGFkb3NfOV8zX21lc2VzIDwtIHJiaW5kKHJlc3VsdGFkb3NfOV8zX21lc2VzLCBkYXRhLmZyYW1lKEJlbmVmaWNpb19Ub3RhbF9BY3VtdWxhZG8gPSBiZW5lZmljaW9fdG90YWwsIEJlbmVmaWNpb19PcGVyYWNpb25fTmV0byA9IGJlbmVmaWNpb19uZXRvKSkNCiAgICANCiBiZW5lZmljaW9fb3BlcmFjaW9uIDwtIGluZGljZXNfYWxQQ0FbMTAsIDFdIC8gMTAwICogaW52ZXJzaW9uICogMyAvIDEyDQogICAgYmVuZWZpY2lvX25ldG8gPC0gY2FsY3VsYXJfbmV0b19jb3J0byhiZW5lZmljaW9fb3BlcmFjaW9uKSANCiAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvICsgYmVuZWZpY2lvX25ldG8NCiAgICBiZW5lZmljaW9fdG90YWwgPC0gYmVuZWZpY2lvX3RvdGFsICsgYmVuZWZpY2lvX25ldG8NCg0KICAgIHdoaWxlIChiZW5lZmljaW8gLyAxMDAwID49IDEpIHsNCiAgICAgICAgaW52ZXJzaW9uIDwtIGludmVyc2lvbiArIDEwMDANCiAgICAgICAgYmVuZWZpY2lvIDwtIGJlbmVmaWNpbyAtIDEwMDANCiAgICB9DQoNCiAgICByZXN1bHRhZG9zXzlfM19tZXNlcyA8LSByYmluZChyZXN1bHRhZG9zXzlfM19tZXNlcywgZGF0YS5mcmFtZShCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gYmVuZWZpY2lvX3RvdGFsLCBCZW5lZmljaW9fT3BlcmFjaW9uX05ldG8gPSBiZW5lZmljaW9fbmV0bykpDQogICAgDQoNCg0KcmVzdWx0YWRvc185XzNfbWVzZXMNCg0KbnVldmVfdHJlc19tZXNlczwtcmVzdWx0YWRvc185XzNfbWVzZXNbbnJvdyhyZXN1bHRhZG9zXzlfM19tZXNlcyksIDFdDQoNCmBgYA0KDQpgYGB7cn0NCmJlbmVmaWNpbyA8LSAwDQpiZW5lZmljaW9fdG90YWwgPC0gMA0KaW52ZXJzaW9uIDwtIDEwMDAwMDANCmludmVyc2lvbl9pbmljaWFsIDwtIDEwMDAwMDANCg0KDQpyZXN1bHRhZG9zXzNfOV9tZXNlcyA8LSBkYXRhLmZyYW1lKEJlbmVmaWNpb19Ub3RhbF9BY3VtdWxhZG8gPSBudW1lcmljKCksIEJlbmVmaWNpb19PcGVyYWNpb25fTmV0byA9IG51bWVyaWMoKSwgQmVuZWZpY2lvX3Jlc2lkdWFsID0gbnVtZXJpYygpKQ0KDQojIEl0ZXJhbW9zIHNvYnJlIGxhcyBwb3NpY2lvbmVzIGVzcGVjw61maWNhcw0KDQogICAgIyBPYnRlbmVtb3MgZWwgYmVuZWZpY2lvIG11bHRpcGxpY2FuZG8gZWwgcHJpbWVyIGVsZW1lbnRvIGRlIGxhIGNvbHVtbmEgcG9yIGVsIHZhbG9yIGludHJvZHVjaWRvIHBvciBlbCB1c3VhcmlvDQogICAgYmVuZWZpY2lvX29wZXJhY2lvbiA8LSBpbmRpY2VzX2FsUENBWzEsIDFdIC8gMTAwICogaW52ZXJzaW9uICogMyAvIDEyDQogICAgYmVuZWZpY2lvX25ldG8gPC0gY2FsY3VsYXJfbmV0b19jb3J0byhiZW5lZmljaW9fb3BlcmFjaW9uKSANCiAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvICsgYmVuZWZpY2lvX25ldG8NCiAgICBiZW5lZmljaW9fdG90YWwgPC0gYmVuZWZpY2lvX3RvdGFsICsgYmVuZWZpY2lvX25ldG8NCg0KICAgIHdoaWxlIChiZW5lZmljaW8gLyAxMDAwID49IDEpIHsNCiAgICAgICAgaW52ZXJzaW9uIDwtIGludmVyc2lvbiArIDEwMDANCiAgICAgICAgYmVuZWZpY2lvIDwtIGJlbmVmaWNpbyAtIDEwMDANCiAgICB9DQoNCiAgICByZXN1bHRhZG9zXzNfOV9tZXNlcyA8LSByYmluZChyZXN1bHRhZG9zXzNfOV9tZXNlcywgZGF0YS5mcmFtZShCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gYmVuZWZpY2lvX3RvdGFsLCBCZW5lZmljaW9fT3BlcmFjaW9uX05ldG8gPSBiZW5lZmljaW9fbmV0bywgQmVuZWZpY2lvX3Jlc2lkdWFsID0gYmVuZWZpY2lvKSkNCiAgICANCiBiZW5lZmljaW9fb3BlcmFjaW9uIDwtIGluZGljZXNfYWxQQ0FbNCwgM10gLyAxMDAgKiBpbnZlcnNpb24gKiA5IC8gMTINCiAgICBiZW5lZmljaW9fbmV0byA8LSBjYWxjdWxhcl9uZXRvX2NvcnRvKGJlbmVmaWNpb19vcGVyYWNpb24pIA0KICAgIGJlbmVmaWNpbyA8LSBiZW5lZmljaW8gKyBiZW5lZmljaW9fbmV0bw0KICAgIGJlbmVmaWNpb190b3RhbCA8LSBiZW5lZmljaW9fdG90YWwgKyBiZW5lZmljaW9fbmV0bw0KDQogICAgd2hpbGUgKGJlbmVmaWNpbyAvIDEwMDAgPj0gMSkgew0KICAgICAgICBpbnZlcnNpb24gPC0gaW52ZXJzaW9uICsgMTAwMA0KICAgICAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvIC0gMTAwMA0KICAgIH0NCg0KICAgIHJlc3VsdGFkb3NfM185X21lc2VzIDwtICBkYXRhLmZyYW1lKEJlbmVmaWNpb19Ub3RhbF9BY3VtdWxhZG8gPSBiZW5lZmljaW9fdG90YWwsIEJlbmVmaWNpb19PcGVyYWNpb25fTmV0byA9IGJlbmVmaWNpb19uZXRvKQ0KICAgIA0KDQoNCnJlc3VsdGFkb3NfM185X21lc2VzDQoNCnRyZXNfbnVldmVfbWVzZXM8LXJlc3VsdGFkb3NfM185X21lc2VzW25yb3cocmVzdWx0YWRvc18zXzlfbWVzZXMpLCAxXQ0KDQpgYGANCg0KDQpgYGB7cn0NCmJlbmVmaWNpbyA8LSAwDQpiZW5lZmljaW9fdG90YWwgPC0gMA0KaW52ZXJzaW9uIDwtIDEwMDAwMDANCmludmVyc2lvbl9pbmljaWFsIDwtIDEwMDAwMDANCg0KDQpyZXN1bHRhZG9zXzEybWVzZXMgPC0gZGF0YS5mcmFtZShCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gbnVtZXJpYygpLCBCZW5lZmljaW9fT3BlcmFjaW9uX05ldG8gPSBudW1lcmljKCksIEJlbmVmaWNpb19yZXNpZHVhbCA9IG51bWVyaWMoKSkNCg0KIyBJdGVyYW1vcyBzb2JyZSBsYXMgcG9zaWNpb25lcyBlc3BlY8OtZmljYXMNCg0KICAgICMgT2J0ZW5lbW9zIGVsIGJlbmVmaWNpbyBtdWx0aXBsaWNhbmRvIGVsIHByaW1lciBlbGVtZW50byBkZSBsYSBjb2x1bW5hIHBvciBlbCB2YWxvciBpbnRyb2R1Y2lkbyBwb3IgZWwgdXN1YXJpbw0KICAgIGJlbmVmaWNpb19vcGVyYWNpb24gPC0gaW5kaWNlc19hbFBDQVsxLCA0XSAvIDEwMCAqIGludmVyc2lvbiANCiAgICBiZW5lZmljaW9fbmV0byA8LSBjYWxjdWxhcl9uZXRvX2NvcnRvKGJlbmVmaWNpb19vcGVyYWNpb24pIA0KICAgIGJlbmVmaWNpbyA8LSBiZW5lZmljaW8gKyBiZW5lZmljaW9fbmV0bw0KICAgIGJlbmVmaWNpb190b3RhbCA8LSBiZW5lZmljaW9fdG90YWwgKyBiZW5lZmljaW9fbmV0bw0KICAgIA0KcmVzdWx0YWRvc18xMm1lc2VzIDwtIGRhdGEuZnJhbWUoQmVuZWZpY2lvX1RvdGFsX0FjdW11bGFkbyA9IGJlbmVmaWNpb190b3RhbCwgQmVuZWZpY2lvX09wZXJhY2lvbl9OZXRvID0gYmVuZWZpY2lvX25ldG8pDQogICAgDQoNCg0KcmVzdWx0YWRvc18xMm1lc2VzDQoNCmRvY2VfbWVzZXM8LXJlc3VsdGFkb3NfMTJtZXNlc1tucm93KHJlc3VsdGFkb3NfMTJtZXNlcyksIDFdDQoNCmBgYA0KDQoNCmBgYHtyfQ0KIyBDcmVhciB1biB2ZWN0b3IgY29uIGxvcyB2YWxvcmVzIGRlIGxhcyB2YXJpYWJsZXMNCnJlbnRhYmlsaWRhZGVzIDwtIGModHJlc19tZXNlcywgc2Vpc19tZXNlcywgdHJlc19udWV2ZV9tZXNlcywgbnVldmVfdHJlc19tZXNlcywgZG9jZV9tZXNlcykNCg0KIyBDcmVhciB1biB2ZWN0b3IgY29uIGxvcyBub21icmVzIGRlIGxhcyB2YXJpYWJsZXMNCm5vbWJyZXNfdmFyaWFibGVzIDwtIGMoIjQgaW52ZXJzaW9uZXMgYSAzIG1lc2VzIiwgIjIgaW52ZXJzaW9uZXMgYSA2IG1lc2VzIiwgIjEgaW52ZXJzacOzbiBhIDMgbWVzZXMgeSBvdHJhIGEgOSBtZXNlcyIsICIxIGludmVyc2nDs24gYSA5IG1lc2VzIHkgb3RyYSBhIDMgbWVzZXMiLCAiMSBpbnZlcnNpw7NuIGEgMTIgbWVzZXMgIikNCg0KIyBPcmRlbmFyIGxvcyB2YWxvcmVzIGRlIGxhcyByZW50YWJpbGlkYWRlcyBkZSBtYXlvciBhIG1lbm9yDQpyYW5raW5nIDwtIHNvcnQocmVudGFiaWxpZGFkZXMsIGRlY3JlYXNpbmcgPSBUUlVFKQ0KDQojIEltcHJpbWlyIGVsIHJhbmtpbmcNCmNhdCgiUmFua2luZyBkZSByZW50YWJpbGlkYWQ6XG4iKQ0KZm9yIChpIGluIDE6bGVuZ3RoKHJhbmtpbmcpKSB7DQogICAgY2F0KGksICI6ICIsIG5vbWJyZXNfdmFyaWFibGVzW3doaWNoKHJlbnRhYmlsaWRhZGVzID09IHJhbmtpbmdbaV0pXSwgIiAtICIsIGZvcm1hdChyYW5raW5nW2ldLCBiaWcubWFyayA9ICIuIiwgZGVjaW1hbC5tYXJrID0gIiwiLCBuc21hbGwgPSAyKSwgIuKCrFxuIikNCn0NCmBgYA0KYGBge3J9DQpyYW5raW5nIDwtIHNvcnQocmVudGFiaWxpZGFkZXMsIGRlY3JlYXNpbmcgPSBGQUxTRSkNCg0KDQpub21icmVzX3ZhcmlhYmxlcyA8LSBjKCIxIGludmVyc2nDs24gYSAxMiBtZXNlcyAiLCIxIGludmVyc2nDs24gYSAzIG1lc2VzIHkgb3RyYSBhIDkiLCAiMiBpbnZlcnNpb25lcyBhIDYgbWVzZXMiLCI0IGludmVyc2lvbmVzIGEgMyBtZXNlcyIsICIxIGludmVyc2nDs24gYSA5IG1lc2VzIHkgb3RyYSBhIDMiKQ0KDQpjb2xvcmVzX2F6dWxlcyA8LSBjb2xvclJhbXBQYWxldHRlKGMoIiMwMDY4OEIiLCAiIzAwOUFDRCIsICIjMDBCRkZGIiwgImxpZ2h0Ymx1ZSIsICJkYXJrc2xhdGVncmF5MSIpKShsZW5ndGgocmFua2luZykpDQoNCmNvbG9yZXNfYXp1bGVzXzIgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJkYXJrc2xhdGVncmF5MSIsICJsaWdodGJsdWUiLCAiIzAwQkZGRiIsICIjMDA5QUNEIiwgIiMwMDY4OEIiKSkobGVuZ3RoKHJhbmtpbmcpKQ0KcGFyKG1hciA9IGMoNSwgNCwgNCwgNSkpICANCg0KDQoNCmJwIDwtYmFycGxvdChyYW5raW5nLCANCiAgICAgICAgbmFtZXMuYXJnID0gcmVwKCIiLCBsZW5ndGgocmFua2luZykpLCANCiAgICAgICAgaG9yaXogPSBUUlVFLCANCiAgICAgICAgY29sID0gY29sb3Jlc19henVsZXMsIA0KICAgICAgICB4bGFiID0gIkJlbmVmaWNpbyBuZXRvIiwgDQogICAgICAgIHlsYWIgPSAiSW52ZXJzaW9uZXMiLA0KICAgICAgICBtYWluID0gIkJlbmVmaWNpb3MgZGUgbGFzIGludmVyc2lvbmVzIHNlZ8O6biBhbFBDQSIsDQogICAgICAgIGxhcyA9IDEsDQogICAgICAgICB4bGltID0gYygwLCBtYXgocmFua2luZykgKiAxLjk4KSkgDQoNCnZhbG9yZXNfZm9ybWF0ZWFkb3MgPC0gc3ByaW50ZigiJS4yZiDigqwiLCByb3VuZChyYW5raW5nLCAyKSkNCg0KdGV4dChyYW5raW5nLCBicCwgbGFiZWxzID0gdmFsb3Jlc19mb3JtYXRlYWRvcywgcG9zID0gNCwgY2V4ID0gMC44LCBjb2wgPSAiYmxhY2siKQ0KDQpsZWdlbmQoeCA9IG1heChyYW5raW5nKSAqIDEuMywgeSA9IG1heChicCksIGxlZ2VuZCA9IG5vbWJyZXNfdmFyaWFibGVzLCBmaWxsID0gY29sb3Jlc19henVsZXNfMiwgdGl0bGUgPSAiSW52ZXJzaW9uZXMiLCB4cGQgPSBUUlVFKQ0KDQpgYGANCg0KDQoNCiMgNS4yLjIuMiAgTW9kZWxvcyBkZSBGb3JlY2FzdGNvbWINCg0KIyMjIyAgMyBtZXNlcw0KYGBge3J9DQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeSh0aWR5cikNCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYM19tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NDpucm93KGRhdG9zKSwgXQ0KDQp5PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOCwxMCksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KYXV0b3Bsb3QoeSkNCg0KYGBgDQoNCg0KYGBge3J9DQoNCmY8LXdpbmRvdyh5LHN0YXJ0PWMoMjAxMSwxKSxlbmQ9YygyMDIwLDUpKQ0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSAyMDIwLTIwMjQNCg0KZGVyaXZhPC1yd2YoZixoPTQ2LGRyaWZ0PVRSVUUpIA0KDQpwcmVkaWNjaW9uZXNfZGVyaXZhPC1kZXJpdmEkbWVhbg0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSBudWV2YXMNCg0KZGVyaXZhPC1yd2YoeSxoPTEyLGRyaWZ0PVRSVUUpIA0KDQoNCnByZWRpY2Npb25lc19kZXJpdmFfbnVldmFzPC1kZXJpdmEkbWVhbg0KDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCBudWV2YXMNCg0KaG9sdCA8LSBob2x0KHksIGggPSAxMiwgbGV2ZWwgPSA5NSkgICANCg0KcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzPC1ob2x0JG1lYW4NCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IDIwMjQtMjAyNA0KDQpob2x0IDwtIGhvbHQoZiwgaCA9IDQ2LCBsZXZlbCA9IDk1KSAgIA0KDQoNCnByZWRpY2Npb25lc19ob2x0PC1ob2x0JG1lYW4NCg0KIyMjQVJJTUEgbnVldmFzDQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYSh5LA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwxKSwgICMgYXJtYSBlc3RhY2lvbmFsIGRpZmVyZW5jaWFjaW9uIGVzdGFjaW9uYWwNCiAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwgPWMoMCwwLDEpLCAgIyBiYWphIGNhZGEgND0gZGVjaW1vcyBxdWUgZXMgZXN0YWNpb25hbA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PVRSVUUpDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MTIpDQoNCg0KcHJlZGljY2lvbmVzX2FyaW1hX251ZXZhczwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KDQoNCiMjI0FSSU1BIGRlIDIwMjAtMjAyNA0KDQphdXRvLmFyaW1hKHkpDQoNCmFjdGl2aWRhZF9hcmltYTwtQXJpbWEoZiwNCiAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9YygwLDEsMSksICAjIGFybWEgZXN0YWNpb25hbCBkaWZlcmVuY2lhY2lvbiBlc3RhY2lvbmFsDQogICAgICAgICAgICAgICAgICAgICAgIHNlYXNvbmFsID1jKDAsMCwxKSwgICMgYmFqYSBjYWRhIDQ9IGRlY2ltb3MgcXVlIGVzIGVzdGFjaW9uYWwNCiAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5kcmlmdD1UUlVFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTQ2KQ0KDQpwcmVkaWNjaW9uZXNfYXJpbWE8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCg0KI1JlZGVzIG5ldXJvbmFsZXMgIG51ZXZhcw0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcih5KQ0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8sIGg9MTIpDQoNCnByZWRpY2Npb25lc19yZWRlc19udWV2YXM8LXByb25vc3RpY29zJG1lYW4NCg0KI1JlZGVzIG5ldXJvbmFsZXMgIDIwMjAtMjAyNA0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcihmKQ0KYWNjdXJhY3kobW9kZWxvKSAjcGFyYSBsb3MgZXJyb3Jlcw0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8sIGg9NDYpDQoNCg0KIyBJbXByaW1pciBsb3MgcHJvbsOzc3RpY29zDQoNCnByZWRpY2Npb25lc19yZWRlczwtcHJvbm9zdGljb3MkbWVhbg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCBudWV2YXMNCg0KbGlicmFyeShmb3JlY1RoZXRhKQ0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIERPVA0KbW9kZWxvX2RvdCA8LSBkb3RtKHkgLCBoPTEyKQ0KDQpwcmVkaWNjaW9uZXNfZG90X251ZXZhczwtbW9kZWxvX2RvdCRtZWFuDQoNCiMjI0R5bmFtaWMgT3B0aW1pemVkIFRoZXRhIE1vZGVsIDIwMjAtMjAyNA0KDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oZiAsIGg9NDYpDQoNCnByZWRpY2Npb25lc19kb3Q8LW1vZGVsb19kb3QkbWVhbg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkZvcmVjYXN0Q29tYiIpDQpsaWJyYXJ5KEZvcmVjYXN0Q29tYikNCmNpdGF0aW9uKCdGb3JlY2FzdENvbWInKQ0KbmV3cHJlZHMgID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXMsIHByZWRpY2Npb25lc19hcmltYV9udWV2YXMsIHByZWRpY2Npb25lc19ob2x0X251ZXZhcywgcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhcywgcHJlZGljY2lvbmVzX2RvdF9udWV2YXMpDQpwcmVkcyA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmEsIHByZWRpY2Npb25lc19hcmltYSwgcHJlZGljY2lvbmVzX2hvbHQsIHByZWRpY2Npb25lc19yZWRlcywgcHJlZGljY2lvbmVzX2RvdCkNCmRpbShwcmVkcykNCg0KcHJlZHM8LW1hdHJpeChwcmVkcyw0Niw1KQ0KDQpvYnM8LXdpbmRvdyh5LHN0YXJ0PWMoMjAyMCw2KSwgZW5kPWMoMjAyNCwzKSkNCmxlbmd0aChvYnMpDQoNCg0KIyBEaXZpZGlyIGxvcyBkYXRvcyBlbiBjb25qdW50b3MgZGUgZW50cmVuYW1pZW50byB5IHBydWViYQ0KdHJhaW5fbyA8LSBvYnNbMTo0MF0NCnRyYWluX3AgPC0gcHJlZHNbMTo0MCxdDQp0ZXN0X28gPC0gb2JzWzQxOjQ2XQ0KdGVzdF9wIDwtIHByZWRzWzQxOjQ2LF0NCg0KIyBDb21iaW5hciBwcmVkaWNjaW9uZXMgdXRpbGl6YW5kbyBmb3JlY2NvbWINCmRhdGEgPC0gZm9yZWNjb21iKHRyYWluX28sIHRyYWluX3AsIHRlc3RfbywgdGVzdF9wKQ0KDQpgYGANCg0KYGBge3J9DQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0VJRzINCm1vZGVsbzwtY29tYl9FSUcyKGRhdGEpDQphY2N1cmFjeV9FSUcyPC1tb2RlbG8kQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0JHDQptb2RlbG9fQkcgPC0gY29tYl9CRyhkYXRhKQ0KYWNjdXJhY3lfQkcgPC0gbW9kZWxvX0JHJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9JbnZXDQptb2RlbG9fSW52VyA8LSBjb21iX0ludlcoZGF0YSkNCmFjY3VyYWN5X0ludlcgPC0gbW9kZWxvX0ludlckQWNjdXJhY3lfVGVzdCANCg0KaW5kaWNlc190cmVzX21lc2VzPC1uZXdwcmVkcyUqJW1vZGVsb19JbnZXJFdlaWdodHMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgIGd1YXJkYW1vcyDDrW5kaWNlDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfTUVEDQptb2RlbG9fTUVEIDwtIGNvbWJfTUVEKGRhdGEpDQphY2N1cmFjeV9NRUQgPC0gbW9kZWxvX01FRCRBY2N1cmFjeV9UZXN0DQpwcmVkaWN0KG1vZGVsb19NRUQsbmV3cHJlZHMpDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfU0ENCm1vZGVsb19TQSA8LSBjb21iX1NBKGRhdGEpDQphY2N1cmFjeV9TQSA8LSBtb2RlbG9fU0EkQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1RBDQptb2RlbG9fVEEgPC0gY29tYl9UQShkYXRhKQ0KYWNjdXJhY3lfVEEgPC0gbW9kZWxvX1RBJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9XQQ0KbW9kZWxvX1dBIDwtIGNvbWJfV0EoZGF0YSkNCmFjY3VyYWN5X1dBIDwtIG1vZGVsb19XQSRBY2N1cmFjeV9UZXN0DQoNCg0KYGBgDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxlY2hvPUZBTFNFfQ0Kc2VyaWVfMTIgPC0gYyhkYXRvcyRWYWxvciwgaW5kaWNlc190cmVzX21lc2VzKQ0KDQp5PXRzKChzZXJpZV8xMiksDQogICAgIHN0YXJ0PWMoMjAwOCwxMCksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KDQp1bHRpbW9fMTIgPC0gdGFpbCh5LCAxMikNCg0KIyBDcmVhciBlbCBncsOhZmljbyB1dGlsaXphbmRvIGF1dG9wbG90IGRlIGdncGxvdDINCmdncGxvdCgpICsNCiAgIyBBZ3JlZ2FyIGxhIGzDrW5lYSBjb21wbGV0YSBlbiBjb2xvciBhenVsDQogIGdlb21fbGluZShkYXRhID0gZGF0YS5mcmFtZSh0aWVtcG8gPSB0aW1lKHkpLCB5ID0geSksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gIiM0QzcyQjAiKSArDQogICMgQWdyZWdhciBzb2xvIGxvcyDDumx0aW1vcyAxMiBwdW50b3MgZW4gY29sb3Igcm9qbw0KICBnZW9tX2xpbmUoZGF0YSA9IHRhaWwoZGF0YS5mcmFtZSh0aWVtcG8gPSB0aW1lKHkpLCB5ID0geSksIDEyKSwgYWVzKHggPSB0aWVtcG8sIHkgPSB5KSwgY29sb3IgPSAicmVkIikgKw0KICAjIFBlcnNvbmFsaXphciBlbCBhc3BlY3RvIGRlbCBncsOhZmljbw0KICB0aGVtZV9taW5pbWFsKCkgKyAgIyBFc3RhYmxlY2VyIHVuIHRlbWEgbWluaW1hbGlzdGENCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWF5b3INCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBsYXMgbMOtbmVhcyBkZSBsYSBjdWFkcsOtY3VsYSBtZW5vcg0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgICMgVGFtYcOxbyBkZWwgdGV4dG8gZGUgbG9zIGVqZXMNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksICAjIFRhbWHDsW8geSBlc3RpbG8gZGVsIHTDrXR1bG8NCiAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBoanVzdCA9IDAuNSksICAjIFRhbWHDsW8geSBhbGluZWFjacOzbiBkZWwgcGllIGRlIHDDoWdpbmENCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGVsIHTDrXR1bG8gZGUgbGEgbGV5ZW5kYQ0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAgIyBQb3NpY2lvbmFyIGxhIGxleWVuZGEgZW4gbGEgcGFydGUgaW5mZXJpb3INCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxhIGxleWVuZGENCiAgKSArDQogIGxhYnMoDQogICAgeCA9ICJUaWVtcG8iLCAgICAgIyBFdGlxdWV0YSBkZWwgZWplIHgNCiAgICB5ID0gIsONbmRpY2Ugbm9taW5hbCIsICAgICAgIyBFdGlxdWV0YSBkZWwgZWplIHkNCiAgICB0aXRsZSA9ICJMZXRyYXMgYSAzIG1lc2VzIiwgICMgZ3JhZmZpdG8gZGVsIGdyw6FmaWNvDQogICAgICMgVGV4dG8gZGUgbGEgZnVlbnRlIGRlIGRhdG9zDQogICkNCg0KDQpgYGANCg0KDQoNCiMjIyMgIDYgbWVzZXMNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkodGlkeXIpDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDZfbWVzZXMiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTU6bnJvdyhkYXRvcyksIF0NCg0KeT10cyhyZXYoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMDgsMTEpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmF1dG9wbG90KHkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpmPC13aW5kb3coeSxzdGFydD1jKDIwMTEsMSksZW5kPWMoMjAyMCwxMSkpDQoNCiMjI03DqXRvZG8gZGUgbGEgZGVyaXZhIDIwMjAtMjAyNA0KDQpkZXJpdmE8LXJ3ZihmLGg9NDEsZHJpZnQ9VFJVRSkgDQoNCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgbnVldmFzDQoNCmRlcml2YTwtcndmKHksaD0xMixkcmlmdD1UUlVFKSANCg0KcHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXM8LWRlcml2YSRtZWFuDQoNCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IG51ZXZhcw0KDQpob2x0IDwtIGhvbHQoeSwgaCA9IDEyLCBsZXZlbCA9IDk1KSAgIA0KDQoNCnByZWRpY2Npb25lc19ob2x0X251ZXZhczwtaG9sdCRtZWFuDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCAyMDI0LTIwMjQNCg0KaG9sdCA8LSBob2x0KGYsIGggPSA0MSwgbGV2ZWwgPSA5NSkNCg0KcHJlZGljY2lvbmVzX2hvbHQ8LWhvbHQkbWVhbg0KDQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYSh5LA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19hcmltYV9udWV2YXM8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCg0KIyMjQVJJTUEgZGUgMjAyMC0yMDI0DQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYShmLA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTQxKQ0KDQpwcmVkaWNjaW9uZXNfYXJpbWE8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCiNSZWRlcyBuZXVyb25hbGVzICBudWV2YXMNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBubmV0YXINCm1vZGVsbyA8LSBubmV0YXIoeSkNCg0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8saD0xMikNCg0KcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhczwtcHJvbm9zdGljb3MkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gbm5ldGFyDQptb2RlbG8gPC0gbm5ldGFyKGYpDQoNCg0KIyBHZW5lcmFyIHByb27Ds3N0aWNvcw0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLCBoPTQxKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXM8LXByb25vc3RpY29zJG1lYW4NCg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCBudWV2YXMNCg0KbGlicmFyeShmb3JlY1RoZXRhKQ0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIERPVA0KbW9kZWxvX2RvdCA8LSBkb3RtKHkgLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19kb3RfbnVldmFzPC1tb2RlbG9fZG90JG1lYW4NCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oZiAsIGg9NDEpDQoNCg0KDQpwcmVkaWNjaW9uZXNfZG90PC1tb2RlbG9fZG90JG1lYW4NCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJGb3JlY2FzdENvbWIiKQ0KbGlicmFyeShGb3JlY2FzdENvbWIpDQoNCm5ld3ByZWRzICA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmFfbnVldmFzLCBwcmVkaWNjaW9uZXNfYXJpbWFfbnVldmFzLCBwcmVkaWNjaW9uZXNfaG9sdF9udWV2YXMsIHByZWRpY2Npb25lc19yZWRlc19udWV2YXMsIHByZWRpY2Npb25lc19kb3RfbnVldmFzKQ0KcHJlZHMgPSBjYmluZChwcmVkaWNjaW9uZXNfZGVyaXZhLCBwcmVkaWNjaW9uZXNfYXJpbWEsIHByZWRpY2Npb25lc19ob2x0LCBwcmVkaWNjaW9uZXNfcmVkZXMsIHByZWRpY2Npb25lc19kb3QpDQoNCnByZWRzPC1tYXRyaXgocHJlZHMsNDEsNSkNCg0KDQpvYnM8LXdpbmRvdyh5LHN0YXJ0PWMoMjAyMCwxMSksIGVuZD1jKDIwMjQsMykpDQoNCiMgRGl2aWRpciBsb3MgZGF0b3MgZW4gY29uanVudG9zIGRlIGVudHJlbmFtaWVudG8geSBwcnVlYmENCnRyYWluX28gPC0gb2JzWzE6MzVdDQpsZW5ndGgodHJhaW5fbykNCnRyYWluX3AgPC0gcHJlZHNbMTozNSxdDQp0ZXN0X28gPC0gb2JzWzM2OjQxXQ0KdGVzdF9wIDwtIHByZWRzWzM2OjQxLF0NCg0KIyBDb21iaW5hciBwcmVkaWNjaW9uZXMgdXRpbGl6YW5kbyBmb3JlY2NvbWINCmRhdGEgPC0gZm9yZWNjb21iKHRyYWluX28sIHRyYWluX3AsIHRlc3RfbywgdGVzdF9wKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfRUlHMg0KbW9kZWxvPC1jb21iX0VJRzIoZGF0YSkNCmFjY3VyYWN5X0VJRzI8LW1vZGVsbyRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfQkcNCm1vZGVsb19CRyA8LSBjb21iX0JHKGRhdGEpDQphY2N1cmFjeV9CRyA8LSBtb2RlbG9fQkckQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0ludlcNCm1vZGVsb19JbnZXIDwtIGNvbWJfSW52VyhkYXRhKQ0KYWNjdXJhY3lfSW52VyA8LSBtb2RlbG9fSW52VyRBY2N1cmFjeV9UZXN0IA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX01FRA0KbW9kZWxvX01FRCA8LSBjb21iX01FRChkYXRhKQ0KYWNjdXJhY3lfTUVEIDwtIG1vZGVsb19NRUQkQWNjdXJhY3lfVGVzdCMjIyMjIyMjIyMjIyMjIyMjIyMNCmluZGljZXNfc2Vpc19tZXNlczwtbW9kZWxvX01FRCRQcmVkaWN0KG1vZGVsb19NRUQsbmV3cHJlZHMgPSBuZXdwcmVkcykNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9TQQ0KbW9kZWxvX1NBIDwtIGNvbWJfU0EoZGF0YSkNCmFjY3VyYWN5X1NBIDwtIG1vZGVsb19TQSRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfVEENCm1vZGVsb19UQSA8LSBjb21iX1RBKGRhdGEpDQphY2N1cmFjeV9UQSA8LSBtb2RlbG9fVEEkQWNjdXJhY3lfVGVzdCMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9XQQ0KbW9kZWxvX1dBIDwtIGNvbWJfV0EoZGF0YSkNCmFjY3VyYWN5X1dBIDwtIG1vZGVsb19XQSRBY2N1cmFjeV9UZXN0DQoNCg0KYGBgDQoNCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLGVjaG89RkFMU0V9DQpzZXJpZV8xMiA8LSBjKGRhdG9zJFZhbG9yLCBpbmRpY2VzX3NlaXNfbWVzZXMpDQoNCnk9dHMoKHNlcmllXzEyKSwNCiAgICAgc3RhcnQ9YygyMDA4LDExKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCnVsdGltb18xMiA8LSB0YWlsKHksIDEyKQ0KDQojIENyZWFyIGVsIGdyw6FmaWNvIHV0aWxpemFuZG8gYXV0b3Bsb3QgZGUgZ2dwbG90Mg0KZ2dwbG90KCkgKw0KICAjIEFncmVnYXIgbGEgbMOtbmVhIGNvbXBsZXRhIGVuIGNvbG9yIGF6dWwNCiAgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHRpZW1wbyA9IHRpbWUoeSksIHkgPSB5KSwgYWVzKHggPSB0aWVtcG8sIHkgPSB5KSwgY29sb3IgPSAiIzRDNzJCMCIpICsNCiAgIyBBZ3JlZ2FyIHNvbG8gbG9zIMO6bHRpbW9zIDEyIHB1bnRvcyBlbiBjb2xvciByb2pvDQogIGdlb21fbGluZShkYXRhID0gdGFpbChkYXRhLmZyYW1lKHRpZW1wbyA9IHRpbWUoeSksIHkgPSB5KSwgMTIpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICJyZWQiKSArDQogICMgUGVyc29uYWxpemFyIGVsIGFzcGVjdG8gZGVsIGdyw6FmaWNvDQogIHRoZW1lX21pbmltYWwoKSArICAjIEVzdGFibGVjZXIgdW4gdGVtYSBtaW5pbWFsaXN0YQ0KICB0aGVtZSgNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBsYXMgbMOtbmVhcyBkZSBsYSBjdWFkcsOtY3VsYSBtYXlvcg0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1lbm9yDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsb3MgZWplcw0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwgICMgVGFtYcOxbyB5IGVzdGlsbyBkZWwgdMOtdHVsbw0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSwgICMgVGFtYcOxbyB5IGFsaW5lYWNpw7NuIGRlbCBwaWUgZGUgcMOhZ2luYQ0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgZWwgdMOtdHVsbyBkZSBsYSBsZXllbmRhDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsICAjIFBvc2ljaW9uYXIgbGEgbGV5ZW5kYSBlbiBsYSBwYXJ0ZSBpbmZlcmlvcg0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkgICMgVGFtYcOxbyBkZWwgdGV4dG8gZGUgbGEgbGV5ZW5kYQ0KICApICsNCiAgbGFicygNCiAgICB4ID0gIlRpZW1wbyIsICAgICAjIEV0aXF1ZXRhIGRlbCBlamUgeA0KICAgIHkgPSAiw41uZGljZSBub21pbmFsIiwgICAgICAjIEV0aXF1ZXRhIGRlbCBlamUgeQ0KICAgIHRpdGxlID0gIkxldHJhcyBhIDYgbWVzZXMiLCAgIyBncmFmZml0byBkZWwgZ3LDoWZpY28NCiAgICAgIyBUZXh0byBkZSBsYSBmdWVudGUgZGUgZGF0b3MNCiAgKQ0KDQoNCmBgYA0KDQojIyMjIDkgbWVzZXMNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkodGlkeXIpDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDlfbWVzZXMiICwgXQ0KZGF0b3MgPC0gZGF0b3NbMTQ2Om5yb3coZGF0b3MpLCBdDQoNCnk9dHMoKGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDEzLDIpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmF1dG9wbG90KHkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpmPC13aW5kb3coeSxzdGFydD1jKDIwMTMsMiksYygyMDIwLDExKSkNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgMjAyMC0yMDI0DQoNCmRlcml2YTwtcndmKGYsaD00MSxkcmlmdD1UUlVFKSANCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgbnVldmFzDQoNCmRlcml2YTwtcndmKHksaD0xMixkcmlmdD1UUlVFKSANCnByZWRpY2Npb25lc19kZXJpdmFfbnVldmFzPC1kZXJpdmEkbWVhbg0KDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCBudWV2YXMNCg0KaG9sdCA8LSBob2x0KHksIGggPSAxMiwgbGV2ZWwgPSA5NSkgICANCnByZWRpY2Npb25lc19ob2x0X251ZXZhczwtaG9sdCRtZWFuDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCAyMDI0LTIwMjQNCg0KaG9sdCA8LSBob2x0KGYsIGggPSA0MSwgbGV2ZWwgPSA5NSkgICANCg0KcHJlZGljY2lvbmVzX2hvbHQ8LWhvbHQkbWVhbg0KDQojIyNBUklNQSBudWV2YXMNCg0KYXV0by5hcmltYSh5KQ0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKHksDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDEpLCANCiAgICAgICAgICAgICAgICAgICAgICBzZWFzb25hbCA9YygwLDAsMSksIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PVRSVUUpDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MTIpDQoNCnByZWRpY2Npb25lc19hcmltYV9udWV2YXM8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCg0KIyMjQVJJTUEgZGUgMjAyMC0yMDI0DQoNCmF1dG8uYXJpbWEoZikNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYShmLA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwxKSwgDQogICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwgPWMoMCwwLDEpLCANCiAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5kcmlmdD1UUlVFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTQxKQ0KDQpwcmVkaWNjaW9uZXNfYXJpbWE8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCg0KI1JlZGVzIG5ldXJvbmFsZXMgIG51ZXZhcw0KDQptb2RlbG8gPC0gbm5ldGFyKHkpDQoNCnByb25vc3RpY29zIDwtIGZvcmVjYXN0KG1vZGVsbyxoPTEyKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXNfbnVldmFzPC1wcm9ub3N0aWNvcyRtZWFuDQoNCiNSZWRlcyBuZXVyb25hbGVzICAyMDIwLTIwMjQNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBubmV0YXINCm1vZGVsbyA8LSBubmV0YXIoZikNCg0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLCBoPTQxKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXM8LXByb25vc3RpY29zJG1lYW4NCg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCBudWV2YXMNCg0KbGlicmFyeShmb3JlY1RoZXRhKQ0KDQptb2RlbG9fZG90IDwtIGRvdG0oeSAsIGg9MTIpDQoNCnByZWRpY2Npb25lc19kb3RfbnVldmFzPC1tb2RlbG9fZG90JG1lYW4NCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgMjAyMC0yMDI0DQoNCm1vZGVsb19kb3QgPC0gZG90bShmICwgaD00MSkNCg0KcHJlZGljY2lvbmVzX2RvdDwtbW9kZWxvX2RvdCRtZWFuDQpgYGANCg0KDQoNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiRm9yZWNhc3RDb21iIikNCmxpYnJhcnkoRm9yZWNhc3RDb21iKQ0KDQpuZXdwcmVkcyAgPSBjYmluZChwcmVkaWNjaW9uZXNfZGVyaXZhX251ZXZhcywgcHJlZGljY2lvbmVzX2FyaW1hX251ZXZhcywgcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzLCBwcmVkaWNjaW9uZXNfcmVkZXNfbnVldmFzLCBwcmVkaWNjaW9uZXNfZG90X251ZXZhcykNCnByZWRzID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YSwgcHJlZGljY2lvbmVzX2FyaW1hLCBwcmVkaWNjaW9uZXNfaG9sdCwgcHJlZGljY2lvbmVzX3JlZGVzLCBwcmVkaWNjaW9uZXNfZG90KQ0KcHJlZHM8LW1hdHJpeChwcmVkcyw0MSw1KQ0KDQpvYnM8LXdpbmRvdyh5LHN0YXJ0PWMoMjAyMCwxMSksIGVuZD1jKDIwMjQsMykpDQoNCiMgRGl2aWRpciBsb3MgZGF0b3MgZW4gY29uanVudG9zIGRlIGVudHJlbmFtaWVudG8geSBwcnVlYmENCnRyYWluX28gPC0gb2JzWzE6MzVdDQpsZW5ndGgodHJhaW5fbykNCnRyYWluX3AgPC0gcHJlZHNbMTozNSxdDQp0ZXN0X28gPC0gb2JzWzM2OjQxXQ0KdGVzdF9wIDwtIHByZWRzWzM2OjQxLF0NCg0KIyBDb21iaW5hciBwcmVkaWNjaW9uZXMgdXRpbGl6YW5kbyBmb3JlY2NvbWINCmRhdGEgPC0gZm9yZWNjb21iKHRyYWluX28sIHRyYWluX3AsIHRlc3RfbywgdGVzdF9wKQ0KDQpgYGANCg0KYGBge3J9DQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0VJRzINCm1vZGVsbzwtY29tYl9FSUcyKGRhdGEpDQphY2N1cmFjeV9FSUcyPC1tb2RlbG8kQWNjdXJhY3lfVGVzO2FjY3VyYWN5X0VJRzINCm5ld3ByZWRzJSolbW9kZWxvJFdlaWdodHMNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9CRw0KbW9kZWxvX0JHIDwtIGNvbWJfQkcoZGF0YSkNCmFjY3VyYWN5X0JHIDwtIG1vZGVsb19CRyRBY2N1cmFjeV9UZXN0O2FjY3VyYWN5X0JHDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfSW52Vw0KbW9kZWxvX0ludlcgPC0gY29tYl9JbnZXKGRhdGEpDQphY2N1cmFjeV9JbnZXIDwtIG1vZGVsb19JbnZXJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfSW52VyANCmluZGljZXNfbnVldmVfbWVzZXM8LW5ld3ByZWRzJSolbW9kZWxvX0ludlckV2VpZ2h0cw0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX01FRA0KbW9kZWxvX01FRCA8LSBjb21iX01FRChkYXRhKQ0KYWNjdXJhY3lfTUVEIDwtIG1vZGVsb19NRUQkQWNjdXJhY3lfVGVzdDthY2N1cmFjeV9NRUQNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9TQQ0KbW9kZWxvX1NBIDwtIGNvbWJfU0EoZGF0YSkNCmFjY3VyYWN5X1NBIDwtIG1vZGVsb19TQSRBY2N1cmFjeV9UZXN0O2FjY3VyYWN5X1NBDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfVEENCm1vZGVsb19UQSA8LSBjb21iX1RBKGRhdGEpDQphY2N1cmFjeV9UQSA8LSBtb2RlbG9fVEEkQWNjdXJhY3lfVGVzdDthY2N1cmFjeV9UQQ0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1dBDQptb2RlbG9fV0EgPC0gY29tYl9XQShkYXRhKQ0KYWNjdXJhY3lfV0EgPC0gbW9kZWxvX1dBJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfV0ENCg0KDQpgYGANCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLGVjaG89RkFMU0V9DQpzZXJpZV8xMiA8LSBjKGRhdG9zJFZhbG9yLCBpbmRpY2VzX251ZXZlX21lc2VzKQ0KDQp5PXRzKChzZXJpZV8xMiksDQogICAgIHN0YXJ0PWMoMjAxMywyKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCnVsdGltb18xMiA8LSB0YWlsKHksIDEyKQ0KDQojIENyZWFyIGVsIGdyw6FmaWNvIHV0aWxpemFuZG8gYXV0b3Bsb3QgZGUgZ2dwbG90Mg0KZ2dwbG90KCkgKw0KICAjIEFncmVnYXIgbGEgbMOtbmVhIGNvbXBsZXRhIGVuIGNvbG9yIGF6dWwNCiAgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHRpZW1wbyA9IHRpbWUoeSksIHkgPSB5KSwgYWVzKHggPSB0aWVtcG8sIHkgPSB5KSwgY29sb3IgPSAiIzRDNzJCMCIpICsNCiAgIyBBZ3JlZ2FyIHNvbG8gbG9zIMO6bHRpbW9zIDEyIHB1bnRvcyBlbiBjb2xvciByb2pvDQogIGdlb21fbGluZShkYXRhID0gdGFpbChkYXRhLmZyYW1lKHRpZW1wbyA9IHRpbWUoeSksIHkgPSB5KSwgMTIpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICJyZWQiKSArDQogICMgUGVyc29uYWxpemFyIGVsIGFzcGVjdG8gZGVsIGdyw6FmaWNvDQogIHRoZW1lX21pbmltYWwoKSArICAjIEVzdGFibGVjZXIgdW4gdGVtYSBtaW5pbWFsaXN0YQ0KICB0aGVtZSgNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBsYXMgbMOtbmVhcyBkZSBsYSBjdWFkcsOtY3VsYSBtYXlvcg0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1lbm9yDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsb3MgZWplcw0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwgICMgVGFtYcOxbyB5IGVzdGlsbyBkZWwgdMOtdHVsbw0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSwgICMgVGFtYcOxbyB5IGFsaW5lYWNpw7NuIGRlbCBwaWUgZGUgcMOhZ2luYQ0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgZWwgdMOtdHVsbyBkZSBsYSBsZXllbmRhDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsICAjIFBvc2ljaW9uYXIgbGEgbGV5ZW5kYSBlbiBsYSBwYXJ0ZSBpbmZlcmlvcg0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkgICMgVGFtYcOxbyBkZWwgdGV4dG8gZGUgbGEgbGV5ZW5kYQ0KICApICsNCiAgbGFicygNCiAgICB4ID0gIlRpZW1wbyIsICAgICAjIEV0aXF1ZXRhIGRlbCBlamUgeA0KICAgIHkgPSAiw41uZGljZSBub21pbmFsIiwgICAgICAjIEV0aXF1ZXRhIGRlbCBlamUgeQ0KICAgIHRpdGxlID0gIkxldHJhcyBhIDkgbWVzZXMiLCAgIyBncmFmZml0byBkZWwgZ3LDoWZpY28NCiAgICAgIyBUZXh0byBkZSBsYSBmdWVudGUgZGUgZGF0b3MNCiAgKQ0KDQoNCmBgYA0KDQoNCiMjIyMgIDEyIG1lc2VzDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHRpZHlyKQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlgxMl9tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1sxOm5yb3coZGF0b3MpLCBdDQoNCnk9dHMoKGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDAxLDEpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmF1dG9wbG90KHkpDQoNCmBgYA0KDQpgYGB7cn0NCg0KZjwtd2luZG93KHksZW5kPWMoMjAxNCwxKSkNCg0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSAyMDIwLTIwMjQNCg0KZGVyaXZhPC1yd2YoZixoPTEyMyxkcmlmdD1UUlVFKSANCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgbnVldmFzDQoNCmRlcml2YTwtcndmKHksaD0xMixkcmlmdD1UUlVFKSANCnByZWRpY2Npb25lc19kZXJpdmFfbnVldmFzPC1kZXJpdmEkbWVhbg0KDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCBudWV2YXMNCg0KaG9sdCA8LSBob2x0KHksIGggPSAxMiwgbGV2ZWwgPSA5NSkgICANCnByZWRpY2Npb25lc19ob2x0X251ZXZhczwtaG9sdCRtZWFuDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCAyMDI0LTIwMjQNCg0KaG9sdCA8LSBob2x0KGYsIGggPSAxMjMsIGxldmVsID0gOTUpICAgDQoNCnByZWRpY2Npb25lc19ob2x0PC1ob2x0JG1lYW4NCg0KIyMjQVJJTUEgbnVldmFzDQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYSh5LA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDMsMSwzKSwgDQogICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwgPWMoMCwwLDIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5kcmlmdD1UUlVFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTEyKQ0KDQpwcmVkaWNjaW9uZXNfYXJpbWFfbnVldmFzPC1hcmltYV9mb3JlY2FzdCRtZWFuDQoNCiMjI0FSSU1BIGRlIDIwMjAtMjAyNA0KDQphdXRvLmFyaW1hKGYpDQoNCmFjdGl2aWRhZF9hcmltYTwtQXJpbWEoZiwNCiAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9YygzLDEsMyksIA0KICAgICAgICAgICAgICAgICAgICAgIHNlYXNvbmFsID1jKDAsMCwyKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9VFJVRSkNCg0KYXJpbWFfZm9yZWNhc3Q8LWZvcmVjYXN0KGFjdGl2aWRhZF9hcmltYSwgaD0xMjMpDQoNCnByZWRpY2Npb25lc19hcmltYTwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgbnVldmFzDQoNCm1vZGVsbyA8LSBubmV0YXIoeSkNCg0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLGg9MTIpDQoNCnByZWRpY2Npb25lc19yZWRlc19udWV2YXM8LXByb25vc3RpY29zJG1lYW4NCg0KI1JlZGVzIG5ldXJvbmFsZXMgIDIwMjAtMjAyNA0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcihmKQ0KDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8sIGg9MTIzKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXM8LXByb25vc3RpY29zJG1lYW4NCg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCBudWV2YXMNCg0KbGlicmFyeShmb3JlY1RoZXRhKQ0KDQptb2RlbG9fZG90IDwtIGRvdG0oeSAsIGg9MTIpDQoNCnByZWRpY2Npb25lc19kb3RfbnVldmFzPC1tb2RlbG9fZG90JG1lYW4NCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgMjAyMC0yMDI0DQoNCm1vZGVsb19kb3QgPC0gZG90bShmICwgaD0xMjMpDQoNCnByZWRpY2Npb25lc19kb3Q8LW1vZGVsb19kb3QkbWVhbg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkZvcmVjYXN0Q29tYiIpDQpsaWJyYXJ5KEZvcmVjYXN0Q29tYikNCg0KbmV3cHJlZHMgID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXMsIHByZWRpY2Npb25lc19hcmltYV9udWV2YXMsIHByZWRpY2Npb25lc19ob2x0X251ZXZhcywgcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhcywgcHJlZGljY2lvbmVzX2RvdF9udWV2YXMpDQpwcmVkcyA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmEsIHByZWRpY2Npb25lc19hcmltYSwgcHJlZGljY2lvbmVzX2hvbHQsIHByZWRpY2Npb25lc19yZWRlcywgcHJlZGljY2lvbmVzX2RvdCkNCmRpbShwcmVkcykNCg0KcHJlZHM8LW1hdHJpeChwcmVkcywxMjMsNSkNCg0Kb2JzPC13aW5kb3coeSxzdGFydD1jKDIwMTQsMSksIGVuZD1jKDIwMjQsMykpDQpsZW5ndGgob2JzKQ0KDQojIERpdmlkaXIgbG9zIGRhdG9zIGVuIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQp0cmFpbl9vIDwtIG9ic1sxOjEwMF0NCmxlbmd0aCh0cmFpbl9vKQ0KdHJhaW5fcCA8LSBwcmVkc1sxOjEwMCxdDQp0ZXN0X28gPC0gb2JzWzEwMToxMjNdDQp0ZXN0X3AgPC0gcHJlZHNbMTAxOjEyMyxdDQoNCiMgQ29tYmluYXIgcHJlZGljY2lvbmVzIHV0aWxpemFuZG8gZm9yZWNjb21iDQpkYXRhIDwtIGZvcmVjY29tYih0cmFpbl9vLCB0cmFpbl9wLCB0ZXN0X28sIHRlc3RfcCkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9FSUcyDQptb2RlbG88LWNvbWJfRUlHMihkYXRhKQ0KYWNjdXJhY3lfRUlHMjwtbW9kZWxvJEFjY3VyYWN5X1RlczthY2N1cmFjeV9FSUcyDQoNCm5ld3ByZWRzJSolbW9kZWxvJFdlaWdodHMNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfQkcNCm1vZGVsb19CRyA8LSBjb21iX0JHKGRhdGEpDQphY2N1cmFjeV9CRyA8LSBtb2RlbG9fQkckQWNjdXJhY3lfVGVzdDthY2N1cmFjeV9CRw0KaW5kaWNlc19kb2NlX21lc2VzPC1uZXdwcmVkcyUqJW1vZGVsb19CRyRXZWlnaHRzDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfSW52Vw0KbW9kZWxvX0ludlcgPC0gY29tYl9JbnZXKGRhdGEpDQphY2N1cmFjeV9JbnZXIDwtIG1vZGVsb19JbnZXJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfSW52VyANCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9NRUQNCm1vZGVsb19NRUQgPC0gY29tYl9NRUQoZGF0YSkNCmFjY3VyYWN5X01FRCA8LSBtb2RlbG9fTUVEJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfTUVEDQpwcmVkaWN0KG1vZGVsb19NRUQsbmV3cHJlZHMpDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1NBDQptb2RlbG9fU0EgPC0gY29tYl9TQShkYXRhKQ0KYWNjdXJhY3lfU0EgPC0gbW9kZWxvX1NBJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfU0ENCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9UQQ0KbW9kZWxvX1RBIDwtIGNvbWJfVEEoZGF0YSkNCmFjY3VyYWN5X1RBIDwtIG1vZGVsb19UQSRBY2N1cmFjeV9UZXN0O2FjY3VyYWN5X1RBDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfV0ENCm1vZGVsb19XQSA8LSBjb21iX1dBKGRhdGEpDQphY2N1cmFjeV9XQSA8LSBtb2RlbG9fV0EkQWNjdXJhY3lfVGVzdDthY2N1cmFjeV9XQQ0KDQoNCmBgYA0KDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxlY2hvPUZBTFNFfQ0Kc2VyaWVfMTIgPC0gYyhkYXRvcyRWYWxvciwgaW5kaWNlc19kb2NlX21lc2VzKQ0KDQp5PXRzKChzZXJpZV8xMiksDQogICAgIHN0YXJ0PWMoMjAwMSwxKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCnVsdGltb18xMiA8LSB0YWlsKHksIDEyKQ0KDQojIENyZWFyIGVsIGdyw6FmaWNvIHV0aWxpemFuZG8gYXV0b3Bsb3QgZGUgZ2dwbG90Mg0KZ2dwbG90KCkgKw0KICAjIEFncmVnYXIgbGEgbMOtbmVhIGNvbXBsZXRhIGVuIGNvbG9yIGF6dWwNCiAgZ2VvbV9saW5lKGRhdGEgPSBkYXRhLmZyYW1lKHRpZW1wbyA9IHRpbWUoeSksIHkgPSB5KSwgYWVzKHggPSB0aWVtcG8sIHkgPSB5KSwgY29sb3IgPSAiIzRDNzJCMCIpICsNCiAgIyBBZ3JlZ2FyIHNvbG8gbG9zIMO6bHRpbW9zIDEyIHB1bnRvcyBlbiBjb2xvciByb2pvDQogIGdlb21fbGluZShkYXRhID0gdGFpbChkYXRhLmZyYW1lKHRpZW1wbyA9IHRpbWUoeSksIHkgPSB5KSwgMTIpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICJyZWQiKSArDQogICMgUGVyc29uYWxpemFyIGVsIGFzcGVjdG8gZGVsIGdyw6FmaWNvDQogIHRoZW1lX21pbmltYWwoKSArICAjIEVzdGFibGVjZXIgdW4gdGVtYSBtaW5pbWFsaXN0YQ0KICB0aGVtZSgNCiAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBsYXMgbMOtbmVhcyBkZSBsYSBjdWFkcsOtY3VsYSBtYXlvcg0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1lbm9yDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsb3MgZWplcw0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwgICMgVGFtYcOxbyB5IGVzdGlsbyBkZWwgdMOtdHVsbw0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGhqdXN0ID0gMC41KSwgICMgVGFtYcOxbyB5IGFsaW5lYWNpw7NuIGRlbCBwaWUgZGUgcMOhZ2luYQ0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgZWwgdMOtdHVsbyBkZSBsYSBsZXllbmRhDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsICAjIFBvc2ljaW9uYXIgbGEgbGV5ZW5kYSBlbiBsYSBwYXJ0ZSBpbmZlcmlvcg0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkgICMgVGFtYcOxbyBkZWwgdGV4dG8gZGUgbGEgbGV5ZW5kYQ0KICApICsNCiAgbGFicygNCiAgICB4ID0gIlRpZW1wbyIsICAgICAjIEV0aXF1ZXRhIGRlbCBlamUgeA0KICAgIHkgPSAiw41uZGljZSBub21pbmFsIiwgICAgICAjIEV0aXF1ZXRhIGRlbCBlamUgeQ0KICAgIHRpdGxlID0gIkxldHJhcyBhIDEyIG1lc2VzIiwgICMgZ3JhZmZpdG8gZGVsIGdyw6FmaWNvDQogICAgICMgVGV4dG8gZGUgbGEgZnVlbnRlIGRlIGRhdG9zDQogICkNCg0KDQpgYGANCg0KDQojIyMgQ8OhbGN1bG9zDQogDQpgYGB7cn0NCmluZGljZXM8LWNiaW5kKCBpbmRpY2VzX3RyZXNfbWVzZXMsaW5kaWNlc19zZWlzX21lc2VzLCBpbmRpY2VzX251ZXZlX21lc2VzLCBpbmRpY2VzX2RvY2VfbWVzZXMpDQppbmRpY2VzPC1kYXRhLmZyYW1lKGluZGljZXMpICMjI3BvbmVyIG5vbWJyZXMgYSBsYXMgY29sdW1uYXMgDQpjb2xuYW1lcyhpbmRpY2VzKSA8LSBjKCJUcmVzIG1lc2VzIiwgIlNlaXMgbWVzZXMiLCAiTnVldmUgbWVzZXMiLCAiRG9jZSBtZXNlcyIpDQoNCmBgYA0KDQoNCg0KDQoNCmBgYHtyfQ0KYmVuZWZpY2lvPC0wDQpiZW5lZmljaW9fdG90YWwgPC0gMA0KaW52ZXJzaW9uPC0xMDAwMDAwDQppbnZlcnNpb25faW5pY2lhbDwtMTAwMDAwMA0KcG9zaWNpb25lcyA8LSBjKDEsIDQsIDcsIDEwKQ0KDQoNCnJlc3VsdGFkb3NfM21lc2VzIDwtIGRhdGEuZnJhbWUoQmVuZWZpY2lvX1RvdGFsID0gbnVtZXJpYygpLEJlbmVmaWNpb19vcGVyYWNpb24gPSBudW1lcmljKCksQmVuZWZpY2lvX3Jlc2lkdWFsPW51bWVyaWMoKSApDQoNCg0KIyBJdGVyYW1vcyBzb2JyZSBsYXMgY29sdW1uYXMgZGVsIGRhdGFmcmFtZQ0KZm9yIChpIGluIHBvc2ljaW9uZXMpIHsNCiAgICAjIE9idGVuZW1vcyBlbCBiZW5lZmljaW8gbXVsdGlwbGljYW5kbyBlbCBwcmltZXIgZWxlbWVudG8gZGUgbGEgY29sdW1uYSBwb3IgZWwgdmFsb3IgaW50cm9kdWNpZG8gcG9yIGVsIHVzdWFyaW8NCiAgICBiZW5lZmljaW9fb3BlcmFjaW9uPC1pbmRpY2VzW2ksIDFdLzEwMCAqIGludmVyc2lvbiAqIDMvMTINCiAgICBiZW5lZmljaW9fbmV0bzwtY2FsY3VsYXJfbmV0b19jb3J0byhiZW5lZmljaW9fb3BlcmFjaW9uKSANCiAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvK2JlbmVmaWNpb19uZXRvDQogICAgYmVuZWZpY2lvX3RvdGFsPC1iZW5lZmljaW9fdG90YWwrYmVuZWZpY2lvX25ldG8NCiAgICANCiAgIA0KICAgIHdoaWxlIChiZW5lZmljaW8gLyAxMDAwID49IDEpIHsNCiAgICAgICAgDQogICAgICAgIGludmVyc2lvbiA8LSBpbnZlcnNpb24gKyAxMDAwDQogICAgICAgIGJlbmVmaWNpbyA8LSBiZW5lZmljaW8gLSAxMDAwDQogICAgICAgIA0KICAgIH0NCiAgICANCiAgICAgcmVzdWx0YWRvc18zbWVzZXM8LSBkYXRhLmZyYW1lKCBCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gYmVuZWZpY2lvX3RvdGFsLEJlbmVmaWNpb19PcGVyYWNpb25fTmV0byA9IGJlbmVmaWNpb19uZXRvKQ0KDQp9DQoNCnJlc3VsdGFkb3NfM21lc2VzDQoNCnRyZXNfbWVzZXM8LXJlc3VsdGFkb3NfM21lc2VzW25yb3cocmVzdWx0YWRvc18zbWVzZXMpLCAxXQ0KDQpgYGANCg0KDQoNCg0KYGBge3J9DQpiZW5lZmljaW8gPC0gMA0KYmVuZWZpY2lvX3RvdGFsIDwtIDANCmludmVyc2lvbiA8LSAxMDAwMDAwDQppbnZlcnNpb25faW5pY2lhbCA8LSAxMDAwMDAwDQpwb3NpY2lvbmVzIDwtIGMoMSwgNykNCg0KcmVzdWx0YWRvc182bWVzZXMgPC0gZGF0YS5mcmFtZShCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gbnVtZXJpYygpLCBCZW5lZmljaW9fT3BlcmFjaW9uX05ldG8gPSBudW1lcmljKCksIEJlbmVmaWNpb19yZXNpZHVhbCA9IG51bWVyaWMoKSkNCg0KIyBJdGVyYW1vcyBzb2JyZSBsYXMgcG9zaWNpb25lcyBlc3BlY8OtZmljYXMNCmZvciAoaSBpbiBwb3NpY2lvbmVzKSB7DQogICAgIyBPYnRlbmVtb3MgZWwgYmVuZWZpY2lvIG11bHRpcGxpY2FuZG8gZWwgcHJpbWVyIGVsZW1lbnRvIGRlIGxhIGNvbHVtbmEgcG9yIGVsIHZhbG9yIGludHJvZHVjaWRvIHBvciBlbCB1c3VhcmlvDQogICAgYmVuZWZpY2lvX29wZXJhY2lvbiA8LSBpbmRpY2VzW2ksIDJdIC8gMTAwICogaW52ZXJzaW9uICogNiAvIDEyDQogICAgYmVuZWZpY2lvX25ldG8gPC0gY2FsY3VsYXJfbmV0b19jb3J0byhiZW5lZmljaW9fb3BlcmFjaW9uKSANCiAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvICsgYmVuZWZpY2lvX25ldG8NCiAgICBiZW5lZmljaW9fdG90YWwgPC0gYmVuZWZpY2lvX3RvdGFsICsgYmVuZWZpY2lvX25ldG8NCg0KICAgIHdoaWxlIChiZW5lZmljaW8gLyAxMDAwID49IDEpIHsNCiAgICAgICAgaW52ZXJzaW9uIDwtIGludmVyc2lvbiArIDEwMDANCiAgICAgICAgYmVuZWZpY2lvIDwtIGJlbmVmaWNpbyAtIDEwMDANCiAgICB9DQoNCiAgICByZXN1bHRhZG9zXzZtZXNlcyA8LWRhdGEuZnJhbWUoQmVuZWZpY2lvX1RvdGFsX0FjdW11bGFkbyA9IGJlbmVmaWNpb190b3RhbCwgQmVuZWZpY2lvX09wZXJhY2lvbl9OZXRvID0gYmVuZWZpY2lvX25ldG8pDQogICAgaWYgKG5yb3cocmVzdWx0YWRvc182bWVzZXMpID09IDIpIGJyZWFrDQp9DQoNCnJlc3VsdGFkb3NfNm1lc2VzDQoNCnNlaXNfbWVzZXM8LXJlc3VsdGFkb3NfNm1lc2VzW25yb3cocmVzdWx0YWRvc182bWVzZXMpLCAxXQ0KDQpgYGANCmBgYHtyfQ0KYmVuZWZpY2lvIDwtIDANCmJlbmVmaWNpb190b3RhbCA8LSAwDQppbnZlcnNpb24gPC0gMTAwMDAwMA0KaW52ZXJzaW9uX2luaWNpYWwgPC0gMTAwMDAwMA0KDQoNCnJlc3VsdGFkb3NfOV8zX21lc2VzIDwtIGRhdGEuZnJhbWUoQmVuZWZpY2lvX1RvdGFsX0FjdW11bGFkbyA9IG51bWVyaWMoKSwgQmVuZWZpY2lvX09wZXJhY2lvbl9OZXRvID0gbnVtZXJpYygpKQ0KDQojIEl0ZXJhbW9zIHNvYnJlIGxhcyBwb3NpY2lvbmVzIGVzcGVjw61maWNhcw0KDQogICAgIyBPYnRlbmVtb3MgZWwgYmVuZWZpY2lvIG11bHRpcGxpY2FuZG8gZWwgcHJpbWVyIGVsZW1lbnRvIGRlIGxhIGNvbHVtbmEgcG9yIGVsIHZhbG9yIGludHJvZHVjaWRvIHBvciBlbCB1c3VhcmlvDQogICAgYmVuZWZpY2lvX29wZXJhY2lvbiA8LSBpbmRpY2VzWzEsIDNdIC8gMTAwICogaW52ZXJzaW9uICogOSAvIDEyDQogICAgYmVuZWZpY2lvX25ldG8gPC0gY2FsY3VsYXJfbmV0b19jb3J0byhiZW5lZmljaW9fb3BlcmFjaW9uKSANCiAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvICsgYmVuZWZpY2lvX25ldG8NCiAgICBiZW5lZmljaW9fdG90YWwgPC0gYmVuZWZpY2lvX3RvdGFsICsgYmVuZWZpY2lvX25ldG8NCg0KICAgIHdoaWxlIChiZW5lZmljaW8gLyAxMDAwID49IDEpIHsNCiAgICAgICAgaW52ZXJzaW9uIDwtIGludmVyc2lvbiArIDEwMDANCiAgICAgICAgYmVuZWZpY2lvIDwtIGJlbmVmaWNpbyAtIDEwMDANCiAgICB9DQoNCiAgICByZXN1bHRhZG9zXzlfM19tZXNlcyA8LWRhdGEuZnJhbWUoQmVuZWZpY2lvX1RvdGFsX0FjdW11bGFkbyA9IGJlbmVmaWNpb190b3RhbCwgQmVuZWZpY2lvX09wZXJhY2lvbl9OZXRvID0gYmVuZWZpY2lvX25ldG8pDQogICAgDQogYmVuZWZpY2lvX29wZXJhY2lvbiA8LSBpbmRpY2VzWzEwLCAxXSAvIDEwMCAqIGludmVyc2lvbiAqIDMgLyAxMg0KICAgIGJlbmVmaWNpb19uZXRvIDwtIGNhbGN1bGFyX25ldG9fY29ydG8oYmVuZWZpY2lvX29wZXJhY2lvbikgDQogICAgYmVuZWZpY2lvIDwtIGJlbmVmaWNpbyArIGJlbmVmaWNpb19uZXRvDQogICAgYmVuZWZpY2lvX3RvdGFsIDwtIGJlbmVmaWNpb190b3RhbCArIGJlbmVmaWNpb19uZXRvDQoNCiAgICB3aGlsZSAoYmVuZWZpY2lvIC8gMTAwMCA+PSAxKSB7DQogICAgICAgIGludmVyc2lvbiA8LSBpbnZlcnNpb24gKyAxMDAwDQogICAgICAgIGJlbmVmaWNpbyA8LSBiZW5lZmljaW8gLSAxMDAwDQogICAgfQ0KDQogICAgcmVzdWx0YWRvc185XzNfbWVzZXMgPC0gcmJpbmQocmVzdWx0YWRvc185XzNfbWVzZXMsIGRhdGEuZnJhbWUoQmVuZWZpY2lvX1RvdGFsX0FjdW11bGFkbyA9IGJlbmVmaWNpb190b3RhbCwgQmVuZWZpY2lvX09wZXJhY2lvbl9OZXRvID0gYmVuZWZpY2lvX25ldG8pKQ0KICAgIA0KDQoNCnJlc3VsdGFkb3NfOV8zX21lc2VzDQoNCm51ZXZlX3RyZXNfbWVzZXM8LXJlc3VsdGFkb3NfOV8zX21lc2VzW25yb3cocmVzdWx0YWRvc185XzNfbWVzZXMpLCAxXQ0KDQpgYGANCg0KYGBge3J9DQpiZW5lZmljaW8gPC0gMA0KYmVuZWZpY2lvX3RvdGFsIDwtIDANCmludmVyc2lvbiA8LSAxMDAwMDAwDQppbnZlcnNpb25faW5pY2lhbCA8LSAxMDAwMDAwDQoNCg0KcmVzdWx0YWRvc18zXzlfbWVzZXMgPC0gZGF0YS5mcmFtZShCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gbnVtZXJpYygpLCBCZW5lZmljaW9fT3BlcmFjaW9uX05ldG8gPSBudW1lcmljKCkpDQoNCiMgSXRlcmFtb3Mgc29icmUgbGFzIHBvc2ljaW9uZXMgZXNwZWPDrWZpY2FzDQoNCiAgICAjIE9idGVuZW1vcyBlbCBiZW5lZmljaW8gbXVsdGlwbGljYW5kbyBlbCBwcmltZXIgZWxlbWVudG8gZGUgbGEgY29sdW1uYSBwb3IgZWwgdmFsb3IgaW50cm9kdWNpZG8gcG9yIGVsIHVzdWFyaW8NCiAgICBiZW5lZmljaW9fb3BlcmFjaW9uIDwtIGluZGljZXNbMSwgMV0gLyAxMDAgKiBpbnZlcnNpb24gKiAzIC8gMTINCiAgICBiZW5lZmljaW9fbmV0byA8LSBjYWxjdWxhcl9uZXRvX2NvcnRvKGJlbmVmaWNpb19vcGVyYWNpb24pIA0KICAgIGJlbmVmaWNpbyA8LSBiZW5lZmljaW8gKyBiZW5lZmljaW9fbmV0bw0KICAgIGJlbmVmaWNpb190b3RhbCA8LSBiZW5lZmljaW9fdG90YWwgKyBiZW5lZmljaW9fbmV0bw0KDQogICAgd2hpbGUgKGJlbmVmaWNpbyAvIDEwMDAgPj0gMSkgew0KICAgICAgICBpbnZlcnNpb24gPC0gaW52ZXJzaW9uICsgMTAwMA0KICAgICAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvIC0gMTAwMA0KICAgIH0NCg0KICAgIHJlc3VsdGFkb3NfM185X21lc2VzIDwtZGF0YS5mcmFtZShCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gYmVuZWZpY2lvX3RvdGFsLCBCZW5lZmljaW9fT3BlcmFjaW9uX05ldG8gPSBiZW5lZmljaW9fbmV0bykNCiAgICANCiBiZW5lZmljaW9fb3BlcmFjaW9uIDwtIGluZGljZXNbNCwgM10gLyAxMDAgKiBpbnZlcnNpb24gKiA5IC8gMTINCiAgICBiZW5lZmljaW9fbmV0byA8LSBjYWxjdWxhcl9uZXRvX2NvcnRvKGJlbmVmaWNpb19vcGVyYWNpb24pIA0KICAgIGJlbmVmaWNpbyA8LSBiZW5lZmljaW8gKyBiZW5lZmljaW9fbmV0bw0KICAgIGJlbmVmaWNpb190b3RhbCA8LSBiZW5lZmljaW9fdG90YWwgKyBiZW5lZmljaW9fbmV0bw0KDQogICAgd2hpbGUgKGJlbmVmaWNpbyAvIDEwMDAgPj0gMSkgew0KICAgICAgICBpbnZlcnNpb24gPC0gaW52ZXJzaW9uICsgMTAwMA0KICAgICAgICBiZW5lZmljaW8gPC0gYmVuZWZpY2lvIC0gMTAwMA0KICAgIH0NCg0KICAgIHJlc3VsdGFkb3NfM185X21lc2VzIDwtIHJiaW5kKHJlc3VsdGFkb3NfM185X21lc2VzLCBkYXRhLmZyYW1lKEJlbmVmaWNpb19Ub3RhbF9BY3VtdWxhZG8gPSBiZW5lZmljaW9fdG90YWwsIEJlbmVmaWNpb19PcGVyYWNpb25fTmV0byA9IGJlbmVmaWNpb19uZXRvKSkNCiAgICANCg0KDQpyZXN1bHRhZG9zXzNfOV9tZXNlcw0KDQp0cmVzX251ZXZlX21lc2VzPC1yZXN1bHRhZG9zXzNfOV9tZXNlc1tucm93KHJlc3VsdGFkb3NfM185X21lc2VzKSwgMV0NCg0KYGBgDQoNCg0KYGBge3J9DQpiZW5lZmljaW8gPC0gMA0KYmVuZWZpY2lvX3RvdGFsIDwtIDANCmludmVyc2lvbiA8LSAxMDAwMDAwDQppbnZlcnNpb25faW5pY2lhbCA8LSAxMDAwMDAwDQoNCg0KcmVzdWx0YWRvc18xMm1lc2VzIDwtIGRhdGEuZnJhbWUoQmVuZWZpY2lvX1RvdGFsX0FjdW11bGFkbyA9IG51bWVyaWMoKSwgQmVuZWZpY2lvX09wZXJhY2lvbl9OZXRvID0gbnVtZXJpYygpKQ0KDQojIEl0ZXJhbW9zIHNvYnJlIGxhcyBwb3NpY2lvbmVzIGVzcGVjw61maWNhcw0KDQogICAgIyBPYnRlbmVtb3MgZWwgYmVuZWZpY2lvIG11bHRpcGxpY2FuZG8gZWwgcHJpbWVyIGVsZW1lbnRvIGRlIGxhIGNvbHVtbmEgcG9yIGVsIHZhbG9yIGludHJvZHVjaWRvIHBvciBlbCB1c3VhcmlvDQogICAgYmVuZWZpY2lvX29wZXJhY2lvbiA8LSBpbmRpY2VzWzEsIDRdIC8gMTAwICogaW52ZXJzaW9uIA0KICAgIGJlbmVmaWNpb19uZXRvIDwtIGNhbGN1bGFyX25ldG9fY29ydG8oYmVuZWZpY2lvX29wZXJhY2lvbikgDQogICAgYmVuZWZpY2lvIDwtIGJlbmVmaWNpbyArIGJlbmVmaWNpb19uZXRvDQogICAgYmVuZWZpY2lvX3RvdGFsIDwtIGJlbmVmaWNpb190b3RhbCArIGJlbmVmaWNpb19uZXRvDQogICAgDQpyZXN1bHRhZG9zXzEybWVzZXMgPC0gZGF0YS5mcmFtZShCZW5lZmljaW9fVG90YWxfQWN1bXVsYWRvID0gYmVuZWZpY2lvX3RvdGFsLCBCZW5lZmljaW9fT3BlcmFjaW9uX05ldG8gPSBiZW5lZmljaW9fbmV0bykNCiAgICANCg0KDQpyZXN1bHRhZG9zXzEybWVzZXMNCg0KZG9jZV9tZXNlczwtcmVzdWx0YWRvc18xMm1lc2VzW25yb3cocmVzdWx0YWRvc18xMm1lc2VzKSwgMV0NCg0KYGBgDQoNCg0KYGBge3J9DQojIENyZWFyIHVuIHZlY3RvciBjb24gbG9zIHZhbG9yZXMgZGUgbGFzIHZhcmlhYmxlcw0KcmVudGFiaWxpZGFkZXMgPC0gYyh0cmVzX21lc2VzLCBzZWlzX21lc2VzLCB0cmVzX251ZXZlX21lc2VzLCBudWV2ZV90cmVzX21lc2VzLCBkb2NlX21lc2VzKQ0KDQojIENyZWFyIHVuIHZlY3RvciBjb24gbG9zIG5vbWJyZXMgZGUgbGFzIHZhcmlhYmxlcw0Kbm9tYnJlc192YXJpYWJsZXMgPC0gYygiNCBpbnZlcnNpb25lcyBhIDMgbWVzZXMiLCAiMiBpbnZlcnNpb25lcyBhIDYgbWVzZXMiLCAiMSBpbnZlcnNpw7NuIGEgMyBtZXNlcyB5IG90cmEgYSA5IiwgIjEgaW52ZXJzacOzbiBhIDkgbWVzZXMgeSBvdHJhIGEgMyIsICIxIGludmVyc2nDs24gYSAxMiBtZXNlcyAiKQ0KDQojIE9yZGVuYXIgbG9zIHZhbG9yZXMgZGUgbGFzIHJlbnRhYmlsaWRhZGVzIGRlIG1heW9yIGEgbWVub3INCnJhbmtpbmcgPC0gc29ydChyZW50YWJpbGlkYWRlcywgZGVjcmVhc2luZyA9IFRSVUUpDQoNCiMgSW1wcmltaXIgZWwgcmFua2luZw0KY2F0KCJSYW5raW5nIGRlIHJlbnRhYmlsaWRhZDpcbiIpDQpmb3IgKGkgaW4gMTpsZW5ndGgocmFua2luZykpIHsNCiAgICBjYXQoaSwgIjogIiwgbm9tYnJlc192YXJpYWJsZXNbd2hpY2gocmVudGFiaWxpZGFkZXMgPT0gcmFua2luZ1tpXSldLCAiIC0gIiwgZm9ybWF0KHJhbmtpbmdbaV0sIGJpZy5tYXJrID0gIi4iLCBkZWNpbWFsLm1hcmsgPSAiLCIsIG5zbWFsbCA9IDIpLCAi4oKsXG4iKQ0KfQ0KYGBgDQpgYGB7cn0NCnJhbmtpbmcgPC0gc29ydChyZW50YWJpbGlkYWRlcywgZGVjcmVhc2luZyA9IEZBTFNFKQ0Kbm9tYnJlc192YXJpYWJsZXMgPC0gYygiMSBpbnZlcnNpw7NuIGEgOSBtZXNlcyB5IG90cmEgYSAzIiwgIjEgaW52ZXJzacOzbiBhIDEyIG1lc2VzICIsIjEgaW52ZXJzacOzbiBhIDMgbWVzZXMgeSBvdHJhIGEgOSIsIjQgaW52ZXJzaW9uZXMgYSAzIG1lc2VzIiwgIjIgaW52ZXJzaW9uZXMgYSA2IG1lc2VzIikNCg0KY29sb3Jlc19henVsZXMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjMDA2ODhCIiwgIiMwMDlBQ0QiLCAiIzAwQkZGRiIsICJsaWdodGJsdWUiLCAiZGFya3NsYXRlZ3JheTEiKSkobGVuZ3RoKHJhbmtpbmcpKQ0KDQpjb2xvcmVzX2F6dWxlc18yIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiZGFya3NsYXRlZ3JheTEiLCAibGlnaHRibHVlIiwgIiMwMEJGRkYiLCAiIzAwOUFDRCIsICIjMDA2ODhCIikpKGxlbmd0aChyYW5raW5nKSkNCnBhcihtYXIgPSBjKDUsIDQsIDQsIDUpKSAgDQoNCg0KDQpicCA8LWJhcnBsb3QocmFua2luZywgDQogICAgICAgIG5hbWVzLmFyZyA9IHJlcCgiIiwgbGVuZ3RoKHJhbmtpbmcpKSwgDQogICAgICAgIGhvcml6ID0gVFJVRSwgDQogICAgICAgIGNvbCA9IGNvbG9yZXNfYXp1bGVzLCANCiAgICAgICAgeGxhYiA9ICJCZW5lZmljaW8gbmV0byIsIA0KICAgICAgICB5bGFiID0gIkludmVyc2lvbmVzIiwNCiAgICAgICAgbWFpbiA9ICJCZW5lZmljaW9zIGRlIGxhcyBpbnZlcnNpb25lcyBzZWfDum4gRm9yZWNhc3Rjb21iIiwNCiAgICAgICAgbGFzID0gMSwNCiAgICAgICAgIHhsaW0gPSBjKDAsIG1heChyYW5raW5nKSAqIDEuOTgpKSANCg0KdmFsb3Jlc19mb3JtYXRlYWRvcyA8LSBzcHJpbnRmKCIlLjJmIOKCrCIsIHJvdW5kKHJhbmtpbmcsIDIpKQ0KDQp0ZXh0KHJhbmtpbmcsIGJwLCBsYWJlbHMgPSB2YWxvcmVzX2Zvcm1hdGVhZG9zLCBwb3MgPSA0LCBjZXggPSAwLjgsIGNvbCA9ICJibGFjayIpDQoNCmxlZ2VuZCh4ID0gbWF4KHJhbmtpbmcpICogMS4zLCB5ID0gbWF4KGJwKSwgbGVnZW5kID0gbm9tYnJlc192YXJpYWJsZXMsIGZpbGwgPSBjb2xvcmVzX2F6dWxlc18yLCB0aXRsZSA9ICJJbnZlcnNpb25lcyIsIHhwZCA9IFRSVUUpDQoNCmBgYA0KDQojIyMjMyBhw7Fvcw0KDQoNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkodGlkeXIpDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDNfYW5vcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NTpucm93KGRhdG9zKSwgXQ0KDQp5PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOCwxMSksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KYXV0b3Bsb3QoeSkNCg0KYGBgDQoNCg0KYGBge3J9DQoNCmY8LXdpbmRvdyh5LHN0YXJ0PWMoMjAxMSwxKSxlbmQ9YygyMDIwLDExKSkNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgMjAyMC0yMDI0DQoNCmRlcml2YTwtcndmKGYsaD00MSxkcmlmdD1UUlVFKSANCg0KcHJlZGljY2lvbmVzX2Rlcml2YTwtZGVyaXZhJG1lYW4NCg0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSBudWV2YXMNCg0KZGVyaXZhPC1yd2YoeSxoPTEyLGRyaWZ0PVRSVUUpIA0KDQpwcmVkaWNjaW9uZXNfZGVyaXZhX251ZXZhczwtZGVyaXZhJG1lYW4NCg0KDQojIyNBbGlzYWRvIGV4cG9uZW5jaWFsIGRlIEhvbHQgbnVldmFzDQoNCmhvbHQgPC0gaG9sdCh5LCBoID0gMTIsIGxldmVsID0gOTUpICAgDQoNCg0KcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzPC1ob2x0JG1lYW4NCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IDIwMjQtMjAyNA0KDQpob2x0IDwtIGhvbHQoZiwgaCA9IDQxLCBsZXZlbCA9IDk1KQ0KDQpwcmVkaWNjaW9uZXNfaG9sdDwtaG9sdCRtZWFuDQoNCg0KYXV0by5hcmltYSh5KQ0KDQoNCmFjdGl2aWRhZF9hcmltYTwtQXJpbWEoeSwNCiAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9YygwLDEsMCksICANCiAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5kcmlmdD1GQUxTRSkNCg0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19hcmltYV9udWV2YXM8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCg0KIyMjQVJJTUEgZGUgMjAyMC0yMDI0DQoNCmF1dG8uYXJpbWEoeSkNCg0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKGYsDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDApLCAgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9RkFMU0UpDQoNCg0KYXJpbWFfZm9yZWNhc3Q8LWZvcmVjYXN0KGFjdGl2aWRhZF9hcmltYSwgaD00MSkNCg0KcHJlZGljY2lvbmVzX2FyaW1hPC1hcmltYV9mb3JlY2FzdCRtZWFuDQojUmVkZXMgbmV1cm9uYWxlcyAgbnVldmFzDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gbm5ldGFyDQptb2RlbG8gPC0gbm5ldGFyKHkpDQoNCg0KIyBHZW5lcmFyIHByb27Ds3N0aWNvcw0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLGg9MTIpDQoNCnByZWRpY2Npb25lc19yZWRlc19udWV2YXM8LXByb25vc3RpY29zJG1lYW4NCg0KI1JlZGVzIG5ldXJvbmFsZXMgIDIwMjAtMjAyNA0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcihmKQ0KDQoNCiMgR2VuZXJhciBwcm9uw7NzdGljb3MNCnByb25vc3RpY29zIDwtIGZvcmVjYXN0KG1vZGVsbywgaD00MSkNCg0KcHJlZGljY2lvbmVzX3JlZGVzPC1wcm9ub3N0aWNvcyRtZWFuDQoNCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgbnVldmFzDQoNCmxpYnJhcnkoZm9yZWNUaGV0YSkNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBET1QNCm1vZGVsb19kb3QgPC0gZG90bSh5ICwgaD0xMikNCg0KDQpwcmVkaWNjaW9uZXNfZG90X251ZXZhczwtbW9kZWxvX2RvdCRtZWFuDQoNCiMjI0R5bmFtaWMgT3B0aW1pemVkIFRoZXRhIE1vZGVsIDIwMjAtMjAyNA0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIERPVA0KbW9kZWxvX2RvdCA8LSBkb3RtKGYgLCBoPTQxKQ0KDQoNCg0KcHJlZGljY2lvbmVzX2RvdDwtbW9kZWxvX2RvdCRtZWFuDQpgYGANCg0KDQoNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiRm9yZWNhc3RDb21iIikNCmxpYnJhcnkoRm9yZWNhc3RDb21iKQ0KDQpuZXdwcmVkcyAgPSBjYmluZChwcmVkaWNjaW9uZXNfZGVyaXZhX251ZXZhcywgcHJlZGljY2lvbmVzX2FyaW1hX251ZXZhcywgcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzLCBwcmVkaWNjaW9uZXNfcmVkZXNfbnVldmFzLCBwcmVkaWNjaW9uZXNfZG90X251ZXZhcykNCnByZWRzID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YSwgcHJlZGljY2lvbmVzX2FyaW1hLCBwcmVkaWNjaW9uZXNfaG9sdCwgcHJlZGljY2lvbmVzX3JlZGVzLCBwcmVkaWNjaW9uZXNfZG90KQ0KZGltKHByZWRzKQ0KDQpwcmVkczwtbWF0cml4KHByZWRzLDQxLDUpDQoNCg0Kb2JzPC13aW5kb3coeSxzdGFydD1jKDIwMjAsMTEpLCBlbmQ9YygyMDI0LDMpKQ0KDQojIERpdmlkaXIgbG9zIGRhdG9zIGVuIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQp0cmFpbl9vIDwtIG9ic1sxOjM1XQ0KbGVuZ3RoKHRyYWluX28pDQp0cmFpbl9wIDwtIHByZWRzWzE6MzUsXQ0KdGVzdF9vIDwtIG9ic1szNjo0MV0NCnRlc3RfcCA8LSBwcmVkc1szNjo0MSxdDQojIENvbWJpbmFyIHByZWRpY2Npb25lcyB1dGlsaXphbmRvIGZvcmVjY29tYg0KZGF0YSA8LSBmb3JlY2NvbWIodHJhaW5fbywgdHJhaW5fcCwgdGVzdF9vLCB0ZXN0X3ApDQoNCg0KYGBgDQoNCmBgYHtyfQ0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9FSUcyDQptb2RlbG88LWNvbWJfRUlHMihkYXRhKQ0KYWNjdXJhY3lfRUlHMjwtbW9kZWxvJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9CRw0KbW9kZWxvX0JHIDwtIGNvbWJfQkcoZGF0YSkNCmFjY3VyYWN5X0JHIDwtIG1vZGVsb19CRyRBY2N1cmFjeV9UZXN0DQpuZXdwcmVkc1ssLTFdJSolbW9kZWxvX0JHJFdlaWdodHMNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9JbnZXDQptb2RlbG9fSW52VyA8LSBjb21iX0ludlcoZGF0YSkNCmFjY3VyYWN5X0ludlcgPC0gbW9kZWxvX0ludlckQWNjdXJhY3lfVGVzdCANCmluZGljZXNfdHJlc19hbm9zPC1uZXdwcmVkc1ssLTNdJSolbW9kZWxvX0ludlckV2VpZ2h0cw0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX01FRA0KbW9kZWxvX01FRCA8LSBjb21iX01FRChkYXRhKQ0KYWNjdXJhY3lfTUVEIDwtIG1vZGVsb19NRUQkQWNjdXJhY3lfVGVzdA0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9TQQ0KbW9kZWxvX1NBIDwtIGNvbWJfU0EoZGF0YSkNCmFjY3VyYWN5X1NBIDwtIG1vZGVsb19TQSRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfVEENCm1vZGVsb19UQSA8LSBjb21iX1RBKGRhdGEpDQphY2N1cmFjeV9UQSA8LSBtb2RlbG9fVEEkQWNjdXJhY3lfVGVzdCMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9XQQ0KbW9kZWxvX1dBIDwtIGNvbWJfV0EoZGF0YSkNCmFjY3VyYWN5X1dBIDwtIG1vZGVsb19XQSRBY2N1cmFjeV9UZXN0DQoNCg0KYGBgDQoNCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFLGVjaG89RkFMU0V9DQpzZXJpZV8xMiA8LSBjKGRhdG9zJFZhbG9yLCBpbmRpY2VzX3RyZXNfYW5vcykNCg0KeT10cygoc2VyaWVfMTIpLA0KICAgICBzdGFydD1jKDIwMDgsMTEpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCg0KdWx0aW1vXzEyIDwtIHRhaWwoeSwgMTIpDQoNCiMgQ3JlYXIgZWwgZ3LDoWZpY28gdXRpbGl6YW5kbyBhdXRvcGxvdCBkZSBnZ3Bsb3QyDQpnZ3Bsb3QoKSArDQogICMgQWdyZWdhciBsYSBsw61uZWEgY29tcGxldGEgZW4gY29sb3IgYXp1bA0KICBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICIjNEM3MkIwIikgKw0KICAjIEFncmVnYXIgc29sbyBsb3Mgw7psdGltb3MgMTIgcHVudG9zIGVuIGNvbG9yIHJvam8NCiAgZ2VvbV9saW5lKGRhdGEgPSB0YWlsKGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCAxMiksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gInJlZCIpICsNCiAgIyBQZXJzb25hbGl6YXIgZWwgYXNwZWN0byBkZWwgZ3LDoWZpY28NCiAgdGhlbWVfbWluaW1hbCgpICsgICMgRXN0YWJsZWNlciB1biB0ZW1hIG1pbmltYWxpc3RhDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1heW9yDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWVub3INCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxvcyBlamVzDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLCAgIyBUYW1hw7FvIHkgZXN0aWxvIGRlbCB0w610dWxvDQogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpLCAgIyBUYW1hw7FvIHkgYWxpbmVhY2nDs24gZGVsIHBpZSBkZSBww6FnaW5hDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBlbCB0w610dWxvIGRlIGxhIGxleWVuZGENCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgICMgUG9zaWNpb25hciBsYSBsZXllbmRhIGVuIGxhIHBhcnRlIGluZmVyaW9yDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsYSBsZXllbmRhDQogICkgKw0KICBsYWJzKA0KICAgIHggPSAiVGllbXBvIiwgICAgICMgRXRpcXVldGEgZGVsIGVqZSB4DQogICAgeSA9ICLDjW5kaWNlIG5vbWluYWwiLCAgICAgICMgRXRpcXVldGEgZGVsIGVqZSB5DQogICAgdGl0bGUgPSAiQm9ub3MgYSAzIGHDsW9zIiwgICMgZ3JhZmZpdG8gZGVsIGdyw6FmaWNvDQogICAgICMgVGV4dG8gZGUgbGEgZnVlbnRlIGRlIGRhdG9zDQogICkNCg0KDQpgYGANCg0KIyMjIzUgYcOxb3MNCg0KDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHRpZHlyKQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDNfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlg1X2Fub3MiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTQ6bnJvdyhkYXRvcyksIF0NCg0KeT10cyhyZXYoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMDgsMTApLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmF1dG9wbG90KHkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpmPC13aW5kb3coeSxzdGFydD1jKDIwMTEsMSksZW5kPWMoMjAyMCwxMSkpDQoNCiMjI03DqXRvZG8gZGUgbGEgZGVyaXZhIDIwMjAtMjAyNA0KDQpkZXJpdmE8LXJ3ZihmLGg9NDEsZHJpZnQ9VFJVRSkgDQoNCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgbnVldmFzDQoNCmRlcml2YTwtcndmKHksaD0xMixkcmlmdD1UUlVFKSANCg0KcHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXM8LWRlcml2YSRtZWFuDQoNCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IG51ZXZhcw0KDQpob2x0IDwtIGhvbHQoeSwgaCA9IDEyLCBsZXZlbCA9IDk1KSAgIA0KDQoNCnByZWRpY2Npb25lc19ob2x0X251ZXZhczwtaG9sdCRtZWFuDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCAyMDI0LTIwMjQNCg0KaG9sdCA8LSBob2x0KGYsIGggPSA0MSwgbGV2ZWwgPSA5NSkNCg0KcHJlZGljY2lvbmVzX2hvbHQ8LWhvbHQkbWVhbg0KDQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYSh5LA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19hcmltYV9udWV2YXM8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCg0KIyMjQVJJTUEgZGUgMjAyMC0yMDI0DQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYShmLA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTQxKQ0KDQpwcmVkaWNjaW9uZXNfYXJpbWE8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCiNSZWRlcyBuZXVyb25hbGVzICBudWV2YXMNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBubmV0YXINCm1vZGVsbyA8LSBubmV0YXIoeSkNCg0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8saD0xMikNCg0KcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhczwtcHJvbm9zdGljb3MkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gbm5ldGFyDQptb2RlbG8gPC0gbm5ldGFyKGYpDQoNCg0KIyBHZW5lcmFyIHByb27Ds3N0aWNvcw0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLCBoPTQxKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXM8LXByb25vc3RpY29zJG1lYW4NCg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCBudWV2YXMNCg0KbGlicmFyeShmb3JlY1RoZXRhKQ0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIERPVA0KbW9kZWxvX2RvdCA8LSBkb3RtKHkgLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19kb3RfbnVldmFzPC1tb2RlbG9fZG90JG1lYW4NCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oZiAsIGg9NDEpDQpwcmVkaWNjaW9uZXNfZG90PC1tb2RlbG9fZG90JG1lYW4NCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJGb3JlY2FzdENvbWIiKQ0KbGlicmFyeShGb3JlY2FzdENvbWIpDQoNCm5ld3ByZWRzICA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmFfbnVldmFzLCBwcmVkaWNjaW9uZXNfYXJpbWFfbnVldmFzLCBwcmVkaWNjaW9uZXNfaG9sdF9udWV2YXMsIHByZWRpY2Npb25lc19yZWRlc19udWV2YXMsIHByZWRpY2Npb25lc19kb3RfbnVldmFzKQ0KcHJlZHMgPSBjYmluZChwcmVkaWNjaW9uZXNfZGVyaXZhLCBwcmVkaWNjaW9uZXNfYXJpbWEsIHByZWRpY2Npb25lc19ob2x0LCBwcmVkaWNjaW9uZXNfcmVkZXMsIHByZWRpY2Npb25lc19kb3QpDQpkaW0ocHJlZHMpDQoNCnByZWRzPC1tYXRyaXgocHJlZHMsNDEsNSkNCg0Kb2JzPC13aW5kb3coeSxzdGFydD1jKDIwMjAsMTEpLCBlbmQ9YygyMDI0LDMpKQ0KDQojIERpdmlkaXIgbG9zIGRhdG9zIGVuIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQp0cmFpbl9vIDwtIG9ic1sxOjM1XQ0KbGVuZ3RoKHRyYWluX28pDQp0cmFpbl9wIDwtIHByZWRzWzE6MzUsXQ0KdGVzdF9vIDwtIG9ic1szNjo0MV0NCnRlc3RfcCA8LSBwcmVkc1szNjo0MSxdDQojIENvbWJpbmFyIHByZWRpY2Npb25lcyB1dGlsaXphbmRvIGZvcmVjY29tYg0KZGF0YSA8LSBmb3JlY2NvbWIodHJhaW5fbywgdHJhaW5fcCwgdGVzdF9vLCB0ZXN0X3ApDQoNCg0KYGBgDQoNCmBgYHtyfQ0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9FSUcyDQptb2RlbG88LWNvbWJfRUlHMihkYXRhKQ0KYWNjdXJhY3lfRUlHMjwtbW9kZWxvJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9CRw0KbW9kZWxvX0JHIDwtIGNvbWJfQkcoZGF0YSkNCmFjY3VyYWN5X0JHIDwtIG1vZGVsb19CRyRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfSW52Vw0KbW9kZWxvX0ludlcgPC0gY29tYl9JbnZXKGRhdGEpDQphY2N1cmFjeV9JbnZXIDwtIG1vZGVsb19JbnZXJEFjY3VyYWN5X1Rlc3QgIyMjIyMjIyMjDQoNCmluZGljZXNfY2luY29fYW5vczwtbmV3cHJlZHNbLC0xXSUqJW1vZGVsb19JbnZXJFdlaWdodHMNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfTUVEDQptb2RlbG9fTUVEIDwtIGNvbWJfTUVEKGRhdGEpDQphY2N1cmFjeV9NRUQgPC0gbW9kZWxvX01FRCRBY2N1cmFjeV9UZXMNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9TQQ0KbW9kZWxvX1NBIDwtIGNvbWJfU0EoZGF0YSkNCmFjY3VyYWN5X1NBIDwtIG1vZGVsb19TQSRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfVEENCm1vZGVsb19UQSA8LSBjb21iX1RBKGRhdGEpDQphY2N1cmFjeV9UQSA8LSBtb2RlbG9fVEEkQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1dBDQptb2RlbG9fV0EgPC0gY29tYl9XQShkYXRhKQ0KYWNjdXJhY3lfV0EgPC0gbW9kZWxvX1dBJEFjY3VyYWN5X1Rlc3QNCg0KDQpgYGANCg0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0UsZWNobz1GQUxTRX0NCnNlcmllXzEyIDwtIGMoZGF0b3MkVmFsb3IsIGluZGljZXNfY2luY29fYW5vcykNCg0KeT10cygoc2VyaWVfMTIpLA0KICAgICBzdGFydD1jKDIwMDgsMTApLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCg0KdWx0aW1vXzEyIDwtIHRhaWwoeSwgMTIpDQoNCiMgQ3JlYXIgZWwgZ3LDoWZpY28gdXRpbGl6YW5kbyBhdXRvcGxvdCBkZSBnZ3Bsb3QyDQpnZ3Bsb3QoKSArDQogICMgQWdyZWdhciBsYSBsw61uZWEgY29tcGxldGEgZW4gY29sb3IgYXp1bA0KICBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICIjNEM3MkIwIikgKw0KICAjIEFncmVnYXIgc29sbyBsb3Mgw7psdGltb3MgMTIgcHVudG9zIGVuIGNvbG9yIHJvam8NCiAgZ2VvbV9saW5lKGRhdGEgPSB0YWlsKGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCAxMiksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gInJlZCIpICsNCiAgIyBQZXJzb25hbGl6YXIgZWwgYXNwZWN0byBkZWwgZ3LDoWZpY28NCiAgdGhlbWVfbWluaW1hbCgpICsgICMgRXN0YWJsZWNlciB1biB0ZW1hIG1pbmltYWxpc3RhDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1heW9yDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWVub3INCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxvcyBlamVzDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLCAgIyBUYW1hw7FvIHkgZXN0aWxvIGRlbCB0w610dWxvDQogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpLCAgIyBUYW1hw7FvIHkgYWxpbmVhY2nDs24gZGVsIHBpZSBkZSBww6FnaW5hDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBlbCB0w610dWxvIGRlIGxhIGxleWVuZGENCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgICMgUG9zaWNpb25hciBsYSBsZXllbmRhIGVuIGxhIHBhcnRlIGluZmVyaW9yDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsYSBsZXllbmRhDQogICkgKw0KICBsYWJzKA0KICAgIHggPSAiVGllbXBvIiwgICAgICMgRXRpcXVldGEgZGVsIGVqZSB4DQogICAgeSA9ICLDjW5kaWNlIG5vbWluYWwiLCAgICAgICMgRXRpcXVldGEgZGVsIGVqZSB5DQogICAgdGl0bGUgPSAiQm9ub3MgYSA1IGHDsW9zIiwgICMgZ3JhZmZpdG8gZGVsIGdyw6FmaWNvDQogICAgICMgVGV4dG8gZGUgbGEgZnVlbnRlIGRlIGRhdG9zDQogICkNCg0KDQpgYGANCg0KIyMjIzEwIGHDsW9zDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeSh0aWR5cikNCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYMTBfYW5vcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5ODpucm93KGRhdG9zKSwgXQ0KDQp5PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOSwyKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQphdXRvcGxvdCh5KQ0KDQpgYGANCg0KDQpgYGB7cn0NCg0KZjwtd2luZG93KHksc3RhcnQ9YygyMDExLDEpLGVuZD1jKDIwMjEsMikpDQoNCiMjI03DqXRvZG8gZGUgbGEgZGVyaXZhIDIwMjAtMjAyNA0KDQpkZXJpdmE8LXJ3ZihmLGg9MzgsZHJpZnQ9VFJVRSkgDQoNCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgbnVldmFzDQoNCmRlcml2YTwtcndmKHksaD0xMixkcmlmdD1UUlVFKSANCg0KcHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXM8LWRlcml2YSRtZWFuDQoNCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IG51ZXZhcw0KDQpob2x0IDwtIGhvbHQoeSwgaCA9IDEyLCBsZXZlbCA9IDk1KSAgIA0KDQoNCnByZWRpY2Npb25lc19ob2x0X251ZXZhczwtaG9sdCRtZWFuDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCAyMDI0LTIwMjQNCg0KaG9sdCA8LSBob2x0KGYsIGggPSAzOCwgbGV2ZWwgPSA5NSkNCg0KcHJlZGljY2lvbmVzX2hvbHQ8LWhvbHQkbWVhbg0KDQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYSh5LA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19hcmltYV9udWV2YXM8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCg0KIyMjQVJJTUEgZGUgMjAyMC0yMDI0DQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYShmLA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTM4KQ0KDQpwcmVkaWNjaW9uZXNfYXJpbWE8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCiNSZWRlcyBuZXVyb25hbGVzICBudWV2YXMNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBubmV0YXINCm1vZGVsbyA8LSBubmV0YXIoeSkNCg0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8saD0xMikNCg0KcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhczwtcHJvbm9zdGljb3MkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gbm5ldGFyDQptb2RlbG8gPC0gbm5ldGFyKGYpDQoNCg0KIyBHZW5lcmFyIHByb27Ds3N0aWNvcw0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLCBoPTM4KQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXM8LXByb25vc3RpY29zJG1lYW4NCg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCBudWV2YXMNCg0KbGlicmFyeShmb3JlY1RoZXRhKQ0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIERPVA0KbW9kZWxvX2RvdCA8LSBkb3RtKHkgLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19kb3RfbnVldmFzPC1tb2RlbG9fZG90JG1lYW4NCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oZiAsIGg9MzgpDQoNCg0KDQpwcmVkaWNjaW9uZXNfZG90PC1tb2RlbG9fZG90JG1lYW4NCmBgYA0KDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkZvcmVjYXN0Q29tYiIpDQpsaWJyYXJ5KEZvcmVjYXN0Q29tYikNCg0KbmV3cHJlZHMgID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXMsIHByZWRpY2Npb25lc19hcmltYV9udWV2YXMsIHByZWRpY2Npb25lc19ob2x0X251ZXZhcywgcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhcywgcHJlZGljY2lvbmVzX2RvdF9udWV2YXMpDQpwcmVkcyA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmEsIHByZWRpY2Npb25lc19hcmltYSwgcHJlZGljY2lvbmVzX2hvbHQsIHByZWRpY2Npb25lc19yZWRlcywgcHJlZGljY2lvbmVzX2RvdCkNCmRpbShwcmVkcykNCg0KcHJlZHM8LW1hdHJpeChwcmVkcywzOCw1KQ0KDQoNCm9iczwtd2luZG93KHksc3RhcnQ9YygyMDIxLDIpLCBlbmQ9YygyMDI0LDMpKQ0KbGVuZ3RoKG9icykNCg0KIyBEaXZpZGlyIGxvcyBkYXRvcyBlbiBjb25qdW50b3MgZGUgZW50cmVuYW1pZW50byB5IHBydWViYQ0KdHJhaW5fbyA8LSBvYnNbMTozM10NCmxlbmd0aCh0cmFpbl9vKQ0KdHJhaW5fcCA8LSBwcmVkc1sxOjMzLF0NCnRlc3RfbyA8LSBvYnNbMzQ6MzhdDQp0ZXN0X3AgPC0gcHJlZHNbMzQ6MzgsXQ0KDQojIENvbWJpbmFyIHByZWRpY2Npb25lcyB1dGlsaXphbmRvIGZvcmVjY29tYg0KZGF0YSA8LSBmb3JlY2NvbWIodHJhaW5fbywgdHJhaW5fcCwgdGVzdF9vLCB0ZXN0X3ApDQoNCmBgYA0KDQpgYGB7cn0NCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfRUlHMg0KbW9kZWxvPC1jb21iX0VJRzIoZGF0YSkNCmFjY3VyYWN5X0VJRzI8LW1vZGVsbyRBY2N1cmFjeV9UZXN0DQppbmRpY2VzX2RpZXpfYW5vczwtbW9kZWxvJFByZWRpY3QobW9kZWxvLG5ld3ByZWRzID0gbmV3cHJlZHNbLC0zXSkNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfQkcNCm1vZGVsb19CRyA8LSBjb21iX0JHKGRhdGEpDQphY2N1cmFjeV9CRyA8LSBtb2RlbG9fQkckQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0ludlcNCm1vZGVsb19JbnZXIDwtIGNvbWJfSW52VyhkYXRhKQ0KYWNjdXJhY3lfSW52VyA8LSBtb2RlbG9fSW52VyRBY2N1cmFjeV9UZXN0IA0KbmV3cHJlZHNbLC0xXSUqJW1vZGVsb19JbnZXJFdlaWdodHMNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfTUVEDQptb2RlbG9fTUVEIDwtIGNvbWJfTUVEKGRhdGEpDQphY2N1cmFjeV9NRUQgPC0gbW9kZWxvX01FRCRBY2N1cmFjeV9UZXN0DQoNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9TQQ0KbW9kZWxvX1NBIDwtIGNvbWJfU0EoZGF0YSkNCmFjY3VyYWN5X1NBIDwtIG1vZGVsb19TQSRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfVEENCm1vZGVsb19UQSA8LSBjb21iX1RBKGRhdGEpDQphY2N1cmFjeV9UQSA8LSBtb2RlbG9fVEEkQWNjdXJhY3lfVGVzDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfV0ENCm1vZGVsb19XQSA8LSBjb21iX1dBKGRhdGEpDQphY2N1cmFjeV9XQSA8LSBtb2RlbG9fV0EkQWNjdXJhY3lfVGVzdA0KDQoNCmBgYA0KDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxlY2hvPUZBTFNFfQ0Kc2VyaWVfMTIgPC0gYyhkYXRvcyRWYWxvciwgaW5kaWNlc19kaWV6X2Fub3MpDQoNCnk9dHMoKHNlcmllXzEyKSwNCiAgICAgc3RhcnQ9YygyMDA5LDIpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCg0KdWx0aW1vXzEyIDwtIHRhaWwoeSwgMTIpDQoNCiMgQ3JlYXIgZWwgZ3LDoWZpY28gdXRpbGl6YW5kbyBhdXRvcGxvdCBkZSBnZ3Bsb3QyDQpnZ3Bsb3QoKSArDQogICMgQWdyZWdhciBsYSBsw61uZWEgY29tcGxldGEgZW4gY29sb3IgYXp1bA0KICBnZW9tX2xpbmUoZGF0YSA9IGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCBhZXMoeCA9IHRpZW1wbywgeSA9IHkpLCBjb2xvciA9ICIjNEM3MkIwIikgKw0KICAjIEFncmVnYXIgc29sbyBsb3Mgw7psdGltb3MgMTIgcHVudG9zIGVuIGNvbG9yIHJvam8NCiAgZ2VvbV9saW5lKGRhdGEgPSB0YWlsKGRhdGEuZnJhbWUodGllbXBvID0gdGltZSh5KSwgeSA9IHkpLCAxMiksIGFlcyh4ID0gdGllbXBvLCB5ID0geSksIGNvbG9yID0gInJlZCIpICsNCiAgIyBQZXJzb25hbGl6YXIgZWwgYXNwZWN0byBkZWwgZ3LDoWZpY28NCiAgdGhlbWVfbWluaW1hbCgpICsgICMgRXN0YWJsZWNlciB1biB0ZW1hIG1pbmltYWxpc3RhDQogIHRoZW1lKA0KICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjIEVsaW1pbmFyIGxhcyBsw61uZWFzIGRlIGxhIGN1YWRyw61jdWxhIG1heW9yDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgICMgRWxpbWluYXIgbGFzIGzDrW5lYXMgZGUgbGEgY3VhZHLDrWN1bGEgbWVub3INCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAjIFRhbWHDsW8gZGVsIHRleHRvIGRlIGxvcyBlamVzDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLCAgIyBUYW1hw7FvIHkgZXN0aWxvIGRlbCB0w610dWxvDQogICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgaGp1c3QgPSAwLjUpLCAgIyBUYW1hw7FvIHkgYWxpbmVhY2nDs24gZGVsIHBpZSBkZSBww6FnaW5hDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgIyBFbGltaW5hciBlbCB0w610dWxvIGRlIGxhIGxleWVuZGENCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgICMgUG9zaWNpb25hciBsYSBsZXllbmRhIGVuIGxhIHBhcnRlIGluZmVyaW9yDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSAgIyBUYW1hw7FvIGRlbCB0ZXh0byBkZSBsYSBsZXllbmRhDQogICkgKw0KICBsYWJzKA0KICAgIHggPSAiVGllbXBvIiwgICAgICMgRXRpcXVldGEgZGVsIGVqZSB4DQogICAgeSA9ICLDjW5kaWNlIG5vbWluYWwiLCAgICAgICMgRXRpcXVldGEgZGVsIGVqZSB5DQogICAgdGl0bGUgPSAiT2JsaWdhY2lvbmVzIGEgMTAgYcOxb3MiLCAgIyBncmFmZml0byBkZWwgZ3LDoWZpY28NCiAgICAgIyBUZXh0byBkZSBsYSBmdWVudGUgZGUgZGF0b3MNCiAgKQ0KDQoNCmBgYA0KDQoNCg0KIyBQcmVkaWNjaW9uZXMgcGFyYSBjYWxjdWxhciBlbCBNQVBFIGNvbiBGb3JlY2FzdGNvbWINCg0KIyMjIyAgMyBtZXNlcw0KYGBge3J9DQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeSh0aWR5cikNCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYM19tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NDpucm93KGRhdG9zKSwgXQ0KDQp5PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOCwxMCksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KDQpgYGANCg0KDQpgYGB7cn0NCg0KZjwtd2luZG93KHksc3RhcnQ9YygyMDExLDEpLGVuZD1jKDIwMjAsNSkpDQoNCiMjI03DqXRvZG8gZGUgbGEgZGVyaXZhIDIwMjAtMjAyNA0KDQpkZXJpdmE8LXJ3ZihmLGg9MzUsZHJpZnQ9VFJVRSkgDQoNCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCiMjI03DqXRvZG8gZGUgbGEgZGVyaXZhIG51ZXZhcw0KDQpkZXJpdmE8LXJ3Zih5LGg9MTIsZHJpZnQ9VFJVRSkgDQoNCg0KcHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXM8LWRlcml2YSRtZWFuDQoNCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IG51ZXZhcw0KDQpob2x0IDwtIGhvbHQoeSwgaCA9IDEyLCBsZXZlbCA9IDk1KSAgIA0KDQpwcmVkaWNjaW9uZXNfaG9sdF9udWV2YXM8LWhvbHQkbWVhbg0KDQojIyNBbGlzYWRvIGV4cG9uZW5jaWFsIGRlIEhvbHQgMjAyNC0yMDI0DQoNCmhvbHQgPC0gaG9sdChmLCBoID0gMzUsIGxldmVsID0gOTUpICAgDQoNCg0KcHJlZGljY2lvbmVzX2hvbHQ8LWhvbHQkbWVhbg0KDQojIyNBUklNQSBudWV2YXMNCg0KYXV0by5hcmltYSh5KQ0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKHksDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDEpLCAgIyBhcm1hIGVzdGFjaW9uYWwgZGlmZXJlbmNpYWNpb24gZXN0YWNpb25hbA0KICAgICAgICAgICAgICAgICAgICAgICBzZWFzb25hbCA9YygwLDAsMSksICAjIGJhamEgY2FkYSA0PSBkZWNpbW9zIHF1ZSBlcyBlc3RhY2lvbmFsDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9VFJVRSkNCg0KYXJpbWFfZm9yZWNhc3Q8LWZvcmVjYXN0KGFjdGl2aWRhZF9hcmltYSwgaD0xMikNCg0KDQpwcmVkaWNjaW9uZXNfYXJpbWFfbnVldmFzPC1hcmltYV9mb3JlY2FzdCRtZWFuDQoNCg0KIyMjQVJJTUEgZGUgMjAyMC0yMDI0DQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYShmLA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwxKSwgICMgYXJtYSBlc3RhY2lvbmFsIGRpZmVyZW5jaWFjaW9uIGVzdGFjaW9uYWwNCiAgICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwgPWMoMCwwLDEpLCAgIyBiYWphIGNhZGEgND0gZGVjaW1vcyBxdWUgZXMgZXN0YWNpb25hbA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PVRSVUUpDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MzUpDQoNCnByZWRpY2Npb25lc19hcmltYTwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgbnVldmFzDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gbm5ldGFyDQptb2RlbG8gPC0gbm5ldGFyKHkpDQoNCiMgR2VuZXJhciBwcm9uw7NzdGljb3MNCnByb25vc3RpY29zIDwtIGZvcmVjYXN0KG1vZGVsbywgaD0xMikNCg0KcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhczwtcHJvbm9zdGljb3MkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gbm5ldGFyDQptb2RlbG8gPC0gbm5ldGFyKGYpDQphY2N1cmFjeShtb2RlbG8pICNwYXJhIGxvcyBlcnJvcmVzDQoNCiMgR2VuZXJhciBwcm9uw7NzdGljb3MNCnByb25vc3RpY29zIDwtIGZvcmVjYXN0KG1vZGVsbywgaD0zNSkNCg0KDQojIEltcHJpbWlyIGxvcyBwcm9uw7NzdGljb3MNCg0KcHJlZGljY2lvbmVzX3JlZGVzPC1wcm9ub3N0aWNvcyRtZWFuDQoNCiMjI0R5bmFtaWMgT3B0aW1pemVkIFRoZXRhIE1vZGVsIG51ZXZhcw0KDQpsaWJyYXJ5KGZvcmVjVGhldGEpDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oeSAsIGg9MTIpDQoNCnByZWRpY2Npb25lc19kb3RfbnVldmFzPC1tb2RlbG9fZG90JG1lYW4NCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgMjAyMC0yMDI0DQoNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBET1QNCm1vZGVsb19kb3QgPC0gZG90bShmICwgaD0zNSkNCg0KcHJlZGljY2lvbmVzX2RvdDwtbW9kZWxvX2RvdCRtZWFuDQpgYGANCg0KDQoNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiRm9yZWNhc3RDb21iIikNCmxpYnJhcnkoRm9yZWNhc3RDb21iKQ0KY2l0YXRpb24oJ0ZvcmVjYXN0Q29tYicpDQpuZXdwcmVkcyAgPSBjYmluZChwcmVkaWNjaW9uZXNfZGVyaXZhX251ZXZhcywgcHJlZGljY2lvbmVzX2FyaW1hX251ZXZhcywgcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzLCBwcmVkaWNjaW9uZXNfcmVkZXNfbnVldmFzLCBwcmVkaWNjaW9uZXNfZG90X251ZXZhcykNCnByZWRzID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YSwgcHJlZGljY2lvbmVzX2FyaW1hLCBwcmVkaWNjaW9uZXNfaG9sdCwgcHJlZGljY2lvbmVzX3JlZGVzLCBwcmVkaWNjaW9uZXNfZG90KQ0KZGltKHByZWRzKQ0KDQpwcmVkczwtbWF0cml4KHByZWRzLDM1LDUpDQoNCm9iczwtd2luZG93KHksc3RhcnQ9YygyMDIwLDYpLCBlbmQ9YygyMDIzLDQpKQ0KbGVuZ3RoKG9icykNCg0KDQojIERpdmlkaXIgbG9zIGRhdG9zIGVuIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQp0cmFpbl9vIDwtIG9ic1sxOjI5XQ0KdHJhaW5fcCA8LSBwcmVkc1sxOjI5LF0NCnRlc3RfbyA8LSBvYnNbMzA6MzVdDQp0ZXN0X3AgPC0gcHJlZHNbMzA6MzUsXQ0KDQojIENvbWJpbmFyIHByZWRpY2Npb25lcyB1dGlsaXphbmRvIGZvcmVjY29tYg0KZGF0YSA8LSBmb3JlY2NvbWIodHJhaW5fbywgdHJhaW5fcCwgdGVzdF9vLCB0ZXN0X3ApDQojIEV2YWx1YXRpbmcgYWxsIHRoZSBmb3JlY2FzdCBjb21iaW5hdGlvbiBtZXRob2RzIGFuZCByZXR1cm5pbmcgdGhlIGJlc3QuDQojIElmIG5lY2Vzc2FyeSwgaXQgdXNlcyB0aGUgYnVpbHQtaW4gYXV0b21hdGVkIHBhcmFtZXRlciBvcHRpbWlzYXRpb24gbWV0aG9kcw0KIyBmb3IgdGhlIGRpZmZlcmVudCBtZXRob2RzLg0KDQptb2RlbG88LWNvbWJfRUlHMihkYXRhKQ0KbW9kZWxvJEFjY3VyYWN5X1Rlc3QNCg0KDQoNCg0KbW9kZWxvJFByZWRpY3QobW9kZWxvLG5ld3ByZWRzPW5ld3ByZWRzKQ0KI2NvbWJfRUlHNChkYXRhLCBudG9wX3ByZWQgPSAyLCBjcml0ZXJpb24gPSAnTUFFJykNCg0KYGBgDQoNCmBgYHtyfQ0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9FSUcyDQptb2RlbG88LWNvbWJfRUlHMihkYXRhKQ0KYWNjdXJhY3lfRUlHMjwtbW9kZWxvJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9CRw0KbW9kZWxvX0JHIDwtIGNvbWJfQkcoZGF0YSkNCmFjY3VyYWN5X0JHIDwtIG1vZGVsb19CRyRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfSW52Vw0KbW9kZWxvX0ludlcgPC0gY29tYl9JbnZXKGRhdGEpDQphY2N1cmFjeV9JbnZXIDwtIG1vZGVsb19JbnZXJEFjY3VyYWN5X1Rlc3QgDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfTUVEDQptb2RlbG9fTUVEIDwtIGNvbWJfTUVEKGRhdGEpDQphY2N1cmFjeV9NRUQgPC0gbW9kZWxvX01FRCRBY2N1cmFjeV9UZXN0DQpwcmVkaWN0KG1vZGVsb19NRUQsbmV3cHJlZHMpDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfU0ENCm1vZGVsb19TQSA8LSBjb21iX1NBKGRhdGEpDQphY2N1cmFjeV9TQSA8LSBtb2RlbG9fU0EkQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1RBDQptb2RlbG9fVEEgPC0gY29tYl9UQShkYXRhKQ0KYWNjdXJhY3lfVEEgPC0gbW9kZWxvX1RBJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9XQQ0KbW9kZWxvX1dBIDwtIGNvbWJfV0EoZGF0YSkNCmFjY3VyYWN5X1dBIDwtIG1vZGVsb19XQSRBY2N1cmFjeV9UZXN0DQoNCg0KYGBgDQoNCg0KDQojIyMjICA2IG1lc2VzDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHRpZHlyKQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDNfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlg2X21lc2VzIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzk1Om5yb3coZGF0b3MpLCBdDQoNCnk9dHMocmV2KGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDA4LDExKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpmPC13aW5kb3coeSxzdGFydD1jKDIwMTEsMSksZW5kPWMoMjAyMCwxMSkpDQoNCiMjI03DqXRvZG8gZGUgbGEgZGVyaXZhIDIwMjAtMjAyNA0KDQpkZXJpdmE8LXJ3ZihmLGg9MzAsZHJpZnQ9VFJVRSkgDQoNCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgbnVldmFzDQoNCmRlcml2YTwtcndmKHksaD0xMixkcmlmdD1UUlVFKSANCg0KcHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXM8LWRlcml2YSRtZWFuDQoNCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IG51ZXZhcw0KDQpob2x0IDwtIGhvbHQoeSwgaCA9IDEyLCBsZXZlbCA9IDk1KSAgIA0KDQoNCnByZWRpY2Npb25lc19ob2x0X251ZXZhczwtaG9sdCRtZWFuDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCAyMDI0LTIwMjQNCg0KaG9sdCA8LSBob2x0KGYsIGggPSAzMCwgbGV2ZWwgPSA5NSkNCg0KcHJlZGljY2lvbmVzX2hvbHQ8LWhvbHQkbWVhbg0KDQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYSh5LA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19hcmltYV9udWV2YXM8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCg0KIyMjQVJJTUEgZGUgMjAyMC0yMDI0DQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYShmLA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTMwKQ0KDQpwcmVkaWNjaW9uZXNfYXJpbWE8LWFyaW1hX2ZvcmVjYXN0JG1lYW4NCiNSZWRlcyBuZXVyb25hbGVzICBudWV2YXMNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBubmV0YXINCm1vZGVsbyA8LSBubmV0YXIoeSkNCg0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8saD0xMikNCg0KcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhczwtcHJvbm9zdGljb3MkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gbm5ldGFyDQptb2RlbG8gPC0gbm5ldGFyKGYpDQoNCg0KIyBHZW5lcmFyIHByb27Ds3N0aWNvcw0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLCBoPTMwKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXM8LXByb25vc3RpY29zJG1lYW4NCg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCBudWV2YXMNCg0KbGlicmFyeShmb3JlY1RoZXRhKQ0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIERPVA0KbW9kZWxvX2RvdCA8LSBkb3RtKHkgLCBoPTEyKQ0KDQoNCnByZWRpY2Npb25lc19kb3RfbnVldmFzPC1tb2RlbG9fZG90JG1lYW4NCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgMjAyMC0yMDI0DQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oZiAsIGg9MzApDQoNCg0KDQpwcmVkaWNjaW9uZXNfZG90PC1tb2RlbG9fZG90JG1lYW4NCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJGb3JlY2FzdENvbWIiKQ0KbGlicmFyeShGb3JlY2FzdENvbWIpDQoNCm5ld3ByZWRzICA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmFfbnVldmFzLCBwcmVkaWNjaW9uZXNfYXJpbWFfbnVldmFzLCBwcmVkaWNjaW9uZXNfaG9sdF9udWV2YXMsIHByZWRpY2Npb25lc19yZWRlc19udWV2YXMsIHByZWRpY2Npb25lc19kb3RfbnVldmFzKQ0KcHJlZHMgPSBjYmluZChwcmVkaWNjaW9uZXNfZGVyaXZhLCBwcmVkaWNjaW9uZXNfYXJpbWEsIHByZWRpY2Npb25lc19ob2x0LCBwcmVkaWNjaW9uZXNfcmVkZXMsIHByZWRpY2Npb25lc19kb3QpDQpkaW0ocHJlZHMpDQoNCnByZWRzPC1tYXRyaXgocHJlZHMsMzAsNSkNCg0KDQpvYnM8LXdpbmRvdyh5LHN0YXJ0PWMoMjAyMCwxMSksIGVuZD1jKDIwMjMsNCkpDQpsZW5ndGgob2JzKQ0KDQoNCiMgRGl2aWRpciBsb3MgZGF0b3MgZW4gY29uanVudG9zIGRlIGVudHJlbmFtaWVudG8geSBwcnVlYmENCnRyYWluX28gPC0gb2JzWzE6MjRdDQpsZW5ndGgodHJhaW5fbykNCnRyYWluX3AgPC0gcHJlZHNbMToyNCxdDQp0ZXN0X28gPC0gb2JzWzI1OjMwXQ0KdGVzdF9wIDwtIHByZWRzWzI1OjMwLF0NCg0KIyBDb21iaW5hciBwcmVkaWNjaW9uZXMgdXRpbGl6YW5kbyBmb3JlY2NvbWINCmRhdGEgPC0gZm9yZWNjb21iKHRyYWluX28sIHRyYWluX3AsIHRlc3RfbywgdGVzdF9wKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfRUlHMg0KbW9kZWxvPC1jb21iX0VJRzIoZGF0YSkNCmFjY3VyYWN5X0VJRzI8LW1vZGVsbyRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfQkcNCm1vZGVsb19CRyA8LSBjb21iX0JHKGRhdGEpDQphY2N1cmFjeV9CRyA8LSBtb2RlbG9fQkckQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0ludlcNCm1vZGVsb19JbnZXIDwtIGNvbWJfSW52VyhkYXRhKQ0KYWNjdXJhY3lfSW52VyA8LSBtb2RlbG9fSW52VyRBY2N1cmFjeV9UZXN0IA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX01FRA0KbW9kZWxvX01FRCA8LSBjb21iX01FRChkYXRhKQ0KYWNjdXJhY3lfTUVEIDwtIG1vZGVsb19NRUQkQWNjdXJhY3lfVGVzdCMjIyMjIyMjIyMjIyMjIyMjIyMNCi1tb2RlbG9fTUVEJFByZWRpY3QobW9kZWxvX01FRCxuZXdwcmVkcyA9IG5ld3ByZWRzKQ0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1NBDQptb2RlbG9fU0EgPC0gY29tYl9TQShkYXRhKQ0KYWNjdXJhY3lfU0EgPC0gbW9kZWxvX1NBJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9UQQ0KbW9kZWxvX1RBIDwtIGNvbWJfVEEoZGF0YSkNCmFjY3VyYWN5X1RBIDwtIG1vZGVsb19UQSRBY2N1cmFjeV9UZXN0IyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1dBDQptb2RlbG9fV0EgPC0gY29tYl9XQShkYXRhKQ0KYWNjdXJhY3lfV0EgPC0gbW9kZWxvX1dBJEFjY3VyYWN5X1Rlc3QNCg0KDQpgYGANCg0KDQojIyMjIDkgbWVzZXMNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkodGlkeXIpDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDlfbWVzZXMiICwgXQ0KZGF0b3MgPC0gZGF0b3NbMTQ2Om5yb3coZGF0b3MpLCBdDQoNCnk9dHMoKGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDEzLDIpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCg0KYGBgDQoNCg0KYGBge3J9DQoNCmY8LXdpbmRvdyh5LHN0YXJ0PWMoMjAxMywyKSxjKDIwMjAsMTEpKQ0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSAyMDIwLTIwMjQNCg0KZGVyaXZhPC1yd2YoZixoPTMwLGRyaWZ0PVRSVUUpIA0KcHJlZGljY2lvbmVzX2Rlcml2YTwtZGVyaXZhJG1lYW4NCg0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSBudWV2YXMNCg0KZGVyaXZhPC1yd2YoeSxoPTEyLGRyaWZ0PVRSVUUpIA0KcHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXM8LWRlcml2YSRtZWFuDQoNCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IG51ZXZhcw0KDQpob2x0IDwtIGhvbHQoeSwgaCA9IDEyLCBsZXZlbCA9IDk1KSAgIA0KcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzPC1ob2x0JG1lYW4NCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IDIwMjQtMjAyNA0KDQpob2x0IDwtIGhvbHQoZiwgaCA9IDMwLCBsZXZlbCA9IDk1KSAgIA0KDQpwcmVkaWNjaW9uZXNfaG9sdDwtaG9sdCRtZWFuDQoNCiMjI0FSSU1BIG51ZXZhcw0KDQphdXRvLmFyaW1hKHkpDQoNCmFjdGl2aWRhZF9hcmltYTwtQXJpbWEoeSwNCiAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9YygwLDEsMSksIA0KICAgICAgICAgICAgICAgICAgICAgIHNlYXNvbmFsID1jKDAsMCwxKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9VFJVRSkNCg0KYXJpbWFfZm9yZWNhc3Q8LWZvcmVjYXN0KGFjdGl2aWRhZF9hcmltYSwgaD0xMikNCg0KcHJlZGljY2lvbmVzX2FyaW1hX251ZXZhczwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KDQojIyNBUklNQSBkZSAyMDIwLTIwMjQNCg0KYXV0by5hcmltYShmKQ0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKGYsDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDEpLCANCiAgICAgICAgICAgICAgICAgICAgICBzZWFzb25hbCA9YygwLDAsMSksIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PVRSVUUpDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MzApDQoNCnByZWRpY2Npb25lc19hcmltYTwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgbnVldmFzDQoNCm1vZGVsbyA8LSBubmV0YXIoeSkNCg0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLGg9MTIpDQoNCnByZWRpY2Npb25lc19yZWRlc19udWV2YXM8LXByb25vc3RpY29zJG1lYW4NCg0KI1JlZGVzIG5ldXJvbmFsZXMgIDIwMjAtMjAyNA0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcihmKQ0KDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8sIGg9MzApDQoNCnByZWRpY2Npb25lc19yZWRlczwtcHJvbm9zdGljb3MkbWVhbg0KDQoNCiMjI0R5bmFtaWMgT3B0aW1pemVkIFRoZXRhIE1vZGVsIG51ZXZhcw0KDQpsaWJyYXJ5KGZvcmVjVGhldGEpDQoNCm1vZGVsb19kb3QgPC0gZG90bSh5ICwgaD0xMikNCg0KcHJlZGljY2lvbmVzX2RvdF9udWV2YXM8LW1vZGVsb19kb3QkbWVhbg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCAyMDIwLTIwMjQNCg0KbW9kZWxvX2RvdCA8LSBkb3RtKGYgLCBoPTMwKQ0KDQpwcmVkaWNjaW9uZXNfZG90PC1tb2RlbG9fZG90JG1lYW4NCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJGb3JlY2FzdENvbWIiKQ0KbGlicmFyeShGb3JlY2FzdENvbWIpDQoNCm5ld3ByZWRzICA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmFfbnVldmFzLCBwcmVkaWNjaW9uZXNfYXJpbWFfbnVldmFzLCBwcmVkaWNjaW9uZXNfaG9sdF9udWV2YXMsIHByZWRpY2Npb25lc19yZWRlc19udWV2YXMsIHByZWRpY2Npb25lc19kb3RfbnVldmFzKQ0KcHJlZHMgPSBjYmluZChwcmVkaWNjaW9uZXNfZGVyaXZhLCBwcmVkaWNjaW9uZXNfYXJpbWEsIHByZWRpY2Npb25lc19ob2x0LCBwcmVkaWNjaW9uZXNfcmVkZXMsIHByZWRpY2Npb25lc19kb3QpDQpkaW0ocHJlZHMpDQoNCnByZWRzPC1tYXRyaXgocHJlZHMsMzAsNSkNCg0KDQpvYnM8LXdpbmRvdyh5LHN0YXJ0PWMoMjAyMCwxMSksIGVuZD1jKDIwMjMsNCkpDQpsZW5ndGgob2JzKQ0KDQojIERpdmlkaXIgbG9zIGRhdG9zIGVuIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQp0cmFpbl9vIDwtIG9ic1sxOjI0XQ0KbGVuZ3RoKHRyYWluX28pDQp0cmFpbl9wIDwtIHByZWRzWzE6MjQsXQ0KdGVzdF9vIDwtIG9ic1syNTozMF0NCnRlc3RfcCA8LSBwcmVkc1syNTozMCxdDQoNCiMgQ29tYmluYXIgcHJlZGljY2lvbmVzIHV0aWxpemFuZG8gZm9yZWNjb21iDQpkYXRhIDwtIGZvcmVjY29tYih0cmFpbl9vLCB0cmFpbl9wLCB0ZXN0X28sIHRlc3RfcCkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9FSUcyDQptb2RlbG88LWNvbWJfRUlHMihkYXRhKQ0KYWNjdXJhY3lfRUlHMjwtbW9kZWxvJEFjY3VyYWN5X1RlczthY2N1cmFjeV9FSUcyDQpuZXdwcmVkcyUqJW1vZGVsbyRXZWlnaHRzDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfQkcNCm1vZGVsb19CRyA8LSBjb21iX0JHKGRhdGEpDQphY2N1cmFjeV9CRyA8LSBtb2RlbG9fQkckQWNjdXJhY3lfVGVzdDthY2N1cmFjeV9CRw0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0ludlcNCm1vZGVsb19JbnZXIDwtIGNvbWJfSW52VyhkYXRhKQ0KYWNjdXJhY3lfSW52VyA8LSBtb2RlbG9fSW52VyRBY2N1cmFjeV9UZXN0O2FjY3VyYWN5X0ludlcgDQppbmRpY2VzX251ZXZlX21lc2VzPC1uZXdwcmVkcyUqJW1vZGVsb19JbnZXJFdlaWdodHMNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9NRUQNCm1vZGVsb19NRUQgPC0gY29tYl9NRUQoZGF0YSkNCmFjY3VyYWN5X01FRCA8LSBtb2RlbG9fTUVEJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfTUVEDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfU0ENCm1vZGVsb19TQSA8LSBjb21iX1NBKGRhdGEpDQphY2N1cmFjeV9TQSA8LSBtb2RlbG9fU0EkQWNjdXJhY3lfVGVzdDthY2N1cmFjeV9TQQ0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1RBDQptb2RlbG9fVEEgPC0gY29tYl9UQShkYXRhKQ0KYWNjdXJhY3lfVEEgPC0gbW9kZWxvX1RBJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfVEENCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9XQQ0KbW9kZWxvX1dBIDwtIGNvbWJfV0EoZGF0YSkNCmFjY3VyYWN5X1dBIDwtIG1vZGVsb19XQSRBY2N1cmFjeV9UZXN0O2FjY3VyYWN5X1dBDQoNCg0KYGBgDQoNCg0KIyMjIyAgMTIgbWVzZXMNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkodGlkeXIpDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDEyX21lc2VzIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzE6bnJvdyhkYXRvcyksIF0NCg0KeT10cygoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMDEsMSksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCg0KZjwtd2luZG93KHksZW5kPWMoMjAxNCwxKSkNCg0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSAyMDIwLTIwMjQNCg0KZGVyaXZhPC1yd2YoZixoPTExMixkcmlmdD1UUlVFKSANCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgbnVldmFzDQoNCmRlcml2YTwtcndmKHksaD0xMixkcmlmdD1UUlVFKSANCnByZWRpY2Npb25lc19kZXJpdmFfbnVldmFzPC1kZXJpdmEkbWVhbg0KDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCBudWV2YXMNCg0KaG9sdCA8LSBob2x0KHksIGggPSAxMiwgbGV2ZWwgPSA5NSkgICANCnByZWRpY2Npb25lc19ob2x0X251ZXZhczwtaG9sdCRtZWFuDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCAyMDI0LTIwMjQNCg0KaG9sdCA8LSBob2x0KGYsIGggPSAxMTIsIGxldmVsID0gOTUpICAgDQoNCnByZWRpY2Npb25lc19ob2x0PC1ob2x0JG1lYW4NCg0KIyMjQVJJTUEgbnVldmFzDQoNCmF1dG8uYXJpbWEoeSkNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYSh5LA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDMsMSwzKSwgDQogICAgICAgICAgICAgICAgICAgICAgc2Vhc29uYWwgPWMoMCwwLDIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZS5kcmlmdD1UUlVFKQ0KDQphcmltYV9mb3JlY2FzdDwtZm9yZWNhc3QoYWN0aXZpZGFkX2FyaW1hLCBoPTEyKQ0KDQpwcmVkaWNjaW9uZXNfYXJpbWFfbnVldmFzPC1hcmltYV9mb3JlY2FzdCRtZWFuDQoNCiMjI0FSSU1BIGRlIDIwMjAtMjAyNA0KDQphdXRvLmFyaW1hKGYpDQoNCmFjdGl2aWRhZF9hcmltYTwtQXJpbWEoZiwNCiAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9YygzLDEsMyksIA0KICAgICAgICAgICAgICAgICAgICAgIHNlYXNvbmFsID1jKDAsMCwyKSwgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9VFJVRSkNCg0KYXJpbWFfZm9yZWNhc3Q8LWZvcmVjYXN0KGFjdGl2aWRhZF9hcmltYSwgaD0xMTIpDQoNCnByZWRpY2Npb25lc19hcmltYTwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KDQojUmVkZXMgbmV1cm9uYWxlcyAgbnVldmFzDQoNCm1vZGVsbyA8LSBubmV0YXIoeSkNCg0KcHJvbm9zdGljb3MgPC0gZm9yZWNhc3QobW9kZWxvLGg9MTIpDQoNCnByZWRpY2Npb25lc19yZWRlc19udWV2YXM8LXByb25vc3RpY29zJG1lYW4NCg0KI1JlZGVzIG5ldXJvbmFsZXMgIDIwMjAtMjAyNA0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcihmKQ0KDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8sIGg9MTEyKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXM8LXByb25vc3RpY29zJG1lYW4NCg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCBudWV2YXMNCg0KbGlicmFyeShmb3JlY1RoZXRhKQ0KDQptb2RlbG9fZG90IDwtIGRvdG0oeSAsIGg9MTIpDQoNCnByZWRpY2Npb25lc19kb3RfbnVldmFzPC1tb2RlbG9fZG90JG1lYW4NCg0KIyMjRHluYW1pYyBPcHRpbWl6ZWQgVGhldGEgTW9kZWwgMjAyMC0yMDI0DQoNCm1vZGVsb19kb3QgPC0gZG90bShmICwgaD0xMTIpDQoNCnByZWRpY2Npb25lc19kb3Q8LW1vZGVsb19kb3QkbWVhbg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkZvcmVjYXN0Q29tYiIpDQpsaWJyYXJ5KEZvcmVjYXN0Q29tYikNCg0KbmV3cHJlZHMgID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXMsIHByZWRpY2Npb25lc19hcmltYV9udWV2YXMsIHByZWRpY2Npb25lc19ob2x0X251ZXZhcywgcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhcywgcHJlZGljY2lvbmVzX2RvdF9udWV2YXMpDQpwcmVkcyA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmEsIHByZWRpY2Npb25lc19hcmltYSwgcHJlZGljY2lvbmVzX2hvbHQsIHByZWRpY2Npb25lc19yZWRlcywgcHJlZGljY2lvbmVzX2RvdCkNCmRpbShwcmVkcykNCg0KcHJlZHM8LW1hdHJpeChwcmVkcywxMTIsNSkNCg0Kb2JzPC13aW5kb3coeSxzdGFydD1jKDIwMTQsMSksIGVuZD1jKDIwMjMsNCkpDQpsZW5ndGgob2JzKQ0KDQojIERpdmlkaXIgbG9zIGRhdG9zIGVuIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQp0cmFpbl9vIDwtIG9ic1sxOjkwXQ0KbGVuZ3RoKHRyYWluX28pDQp0cmFpbl9wIDwtIHByZWRzWzE6OTAsXQ0KdGVzdF9vIDwtIG9ic1s5MToxMTJdDQp0ZXN0X3AgPC0gcHJlZHNbOTE6MTEyLF0NCg0KIyBDb21iaW5hciBwcmVkaWNjaW9uZXMgdXRpbGl6YW5kbyBmb3JlY2NvbWINCmRhdGEgPC0gZm9yZWNjb21iKHRyYWluX28sIHRyYWluX3AsIHRlc3RfbywgdGVzdF9wKQ0KDQpgYGANCg0KYGBge3J9DQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0VJRzINCm1vZGVsbzwtY29tYl9FSUcyKGRhdGEpDQphY2N1cmFjeV9FSUcyPC1tb2RlbG8kQWNjdXJhY3lfVGVzO2FjY3VyYWN5X0VJRzINCg0KbmV3cHJlZHMlKiVtb2RlbG8kV2VpZ2h0cw0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9CRw0KbW9kZWxvX0JHIDwtIGNvbWJfQkcoZGF0YSkNCmFjY3VyYWN5X0JHIDwtIG1vZGVsb19CRyRBY2N1cmFjeV9UZXN0O2FjY3VyYWN5X0JHDQpuZXdwcmVkcyUqJW1vZGVsb19CRyRXZWlnaHRzDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfSW52Vw0KbW9kZWxvX0ludlcgPC0gY29tYl9JbnZXKGRhdGEpDQphY2N1cmFjeV9JbnZXIDwtIG1vZGVsb19JbnZXJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfSW52VyANCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9NRUQNCm1vZGVsb19NRUQgPC0gY29tYl9NRUQoZGF0YSkNCmFjY3VyYWN5X01FRCA8LSBtb2RlbG9fTUVEJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfTUVEDQpwcmVkaWN0KG1vZGVsb19NRUQsbmV3cHJlZHMpDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1NBDQptb2RlbG9fU0EgPC0gY29tYl9TQShkYXRhKQ0KYWNjdXJhY3lfU0EgPC0gbW9kZWxvX1NBJEFjY3VyYWN5X1Rlc3Q7YWNjdXJhY3lfU0ENCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9UQQ0KbW9kZWxvX1RBIDwtIGNvbWJfVEEoZGF0YSkNCmFjY3VyYWN5X1RBIDwtIG1vZGVsb19UQSRBY2N1cmFjeV9UZXN0O2FjY3VyYWN5X1RBDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfV0ENCm1vZGVsb19XQSA8LSBjb21iX1dBKGRhdGEpDQphY2N1cmFjeV9XQSA8LSBtb2RlbG9fV0EkQWNjdXJhY3lfVGVzdDthY2N1cmFjeV9XQQ0KDQoNCmBgYA0KDQoNCg0KIyMjIzMgYcOxb3MNCg0KDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHRpZHlyKQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDNfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlgzX2Fub3MiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTU6bnJvdyhkYXRvcyksIF0NCg0KeT10cyhyZXYoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMDgsMTEpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmF1dG9wbG90KHkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpmPC13aW5kb3coeSxzdGFydD1jKDIwMTEsMSksZW5kPWMoMjAyMCwxMSkpDQoNCiMjI03DqXRvZG8gZGUgbGEgZGVyaXZhIDIwMjAtMjAyNA0KDQpkZXJpdmE8LXJ3ZihmLGg9MzAsZHJpZnQ9VFJVRSkgDQoNCnByZWRpY2Npb25lc19kZXJpdmE8LWRlcml2YSRtZWFuDQoNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgbnVldmFzDQoNCmRlcml2YTwtcndmKHksaD0xMixkcmlmdD1UUlVFKSANCg0KcHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXM8LWRlcml2YSRtZWFuDQoNCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IG51ZXZhcw0KDQpob2x0IDwtIGhvbHQoeSwgaCA9IDEyLCBsZXZlbCA9IDk1KSAgIA0KDQoNCnByZWRpY2Npb25lc19ob2x0X251ZXZhczwtaG9sdCRtZWFuDQoNCiMjI0FsaXNhZG8gZXhwb25lbmNpYWwgZGUgSG9sdCAyMDI0LTIwMjQNCg0KaG9sdCA8LSBob2x0KGYsIGggPSAzMCwgbGV2ZWwgPSA5NSkNCg0KcHJlZGljY2lvbmVzX2hvbHQ8LWhvbHQkbWVhbg0KDQoNCmF1dG8uYXJpbWEoeSkNCg0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKHksDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDApLCAgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9RkFMU0UpDQoNCg0KYXJpbWFfZm9yZWNhc3Q8LWZvcmVjYXN0KGFjdGl2aWRhZF9hcmltYSwgaD0xMikNCg0KDQpwcmVkaWNjaW9uZXNfYXJpbWFfbnVldmFzPC1hcmltYV9mb3JlY2FzdCRtZWFuDQoNCiMjI0FSSU1BIGRlIDIwMjAtMjAyNA0KDQphdXRvLmFyaW1hKHkpDQoNCg0KYWN0aXZpZGFkX2FyaW1hPC1BcmltYShmLA0KICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1jKDAsMSwwKSwgIA0KICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlLmRyaWZ0PUZBTFNFKQ0KDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MzApDQoNCnByZWRpY2Npb25lc19hcmltYTwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KI1JlZGVzIG5ldXJvbmFsZXMgIG51ZXZhcw0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcih5KQ0KDQoNCiMgR2VuZXJhciBwcm9uw7NzdGljb3MNCnByb25vc3RpY29zIDwtIGZvcmVjYXN0KG1vZGVsbyxoPTEyKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXNfbnVldmFzPC1wcm9ub3N0aWNvcyRtZWFuDQoNCiNSZWRlcyBuZXVyb25hbGVzICAyMDIwLTIwMjQNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBubmV0YXINCm1vZGVsbyA8LSBubmV0YXIoZikNCg0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8sIGg9MzApDQoNCnByZWRpY2Npb25lc19yZWRlczwtcHJvbm9zdGljb3MkbWVhbg0KDQoNCiMjI0R5bmFtaWMgT3B0aW1pemVkIFRoZXRhIE1vZGVsIG51ZXZhcw0KDQpsaWJyYXJ5KGZvcmVjVGhldGEpDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oeSAsIGg9MTIpDQoNCg0KcHJlZGljY2lvbmVzX2RvdF9udWV2YXM8LW1vZGVsb19kb3QkbWVhbg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCAyMDIwLTIwMjQNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBET1QNCm1vZGVsb19kb3QgPC0gZG90bShmICwgaD0zMCkNCg0KDQoNCnByZWRpY2Npb25lc19kb3Q8LW1vZGVsb19kb3QkbWVhbg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkZvcmVjYXN0Q29tYiIpDQpsaWJyYXJ5KEZvcmVjYXN0Q29tYikNCg0KbmV3cHJlZHMgID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXMsIHByZWRpY2Npb25lc19hcmltYV9udWV2YXMsIHByZWRpY2Npb25lc19ob2x0X251ZXZhcywgcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhcywgcHJlZGljY2lvbmVzX2RvdF9udWV2YXMpDQpwcmVkcyA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmEsIHByZWRpY2Npb25lc19hcmltYSwgcHJlZGljY2lvbmVzX2hvbHQsIHByZWRpY2Npb25lc19yZWRlcywgcHJlZGljY2lvbmVzX2RvdCkNCmRpbShwcmVkcykNCg0KcHJlZHM8LW1hdHJpeChwcmVkcywzMCw1KQ0KDQoNCm9iczwtd2luZG93KHksc3RhcnQ9YygyMDIwLDExKSwgZW5kPWMoMjAyMyw0KSkNCmxlbmd0aChvYnMpDQoNCiMgR2VuZXJhciBvYnNlcnZhY2lvbmVzIHkgcHJlZGljY2lvbmVzDQoNCg0KDQojIERpdmlkaXIgbG9zIGRhdG9zIGVuIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQp0cmFpbl9vIDwtIG9ic1sxOjI0XQ0KbGVuZ3RoKHRyYWluX28pDQp0cmFpbl9wIDwtIHByZWRzWzE6MjQsXQ0KdGVzdF9vIDwtIG9ic1syNTozMF0NCnRlc3RfcCA8LSBwcmVkc1syNTozMCxdDQoNCiMgQ29tYmluYXIgcHJlZGljY2lvbmVzIHV0aWxpemFuZG8gZm9yZWNjb21iDQpkYXRhIDwtIGZvcmVjY29tYih0cmFpbl9vLCB0cmFpbl9wLCB0ZXN0X28sIHRlc3RfcCkNCg0KDQpgYGANCg0KYGBge3J9DQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0VJRzINCm1vZGVsbzwtY29tYl9FSUcyKGRhdGEpDQphY2N1cmFjeV9FSUcyPC1tb2RlbG8kQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0JHDQptb2RlbG9fQkcgPC0gY29tYl9CRyhkYXRhKQ0KYWNjdXJhY3lfQkcgPC0gbW9kZWxvX0JHJEFjY3VyYWN5X1Rlc3QNCm5ld3ByZWRzWywtMV0lKiVtb2RlbG9fQkckV2VpZ2h0cw0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0ludlcNCm1vZGVsb19JbnZXIDwtIGNvbWJfSW52VyhkYXRhKQ0KYWNjdXJhY3lfSW52VyA8LSBtb2RlbG9fSW52VyRBY2N1cmFjeV9UZXN0IA0KaW5kaWNlc190cmVzX2Fub3M8LW5ld3ByZWRzWywtM10lKiVtb2RlbG9fSW52VyRXZWlnaHRzDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfTUVEDQptb2RlbG9fTUVEIDwtIGNvbWJfTUVEKGRhdGEpDQphY2N1cmFjeV9NRUQgPC0gbW9kZWxvX01FRCRBY2N1cmFjeV9UZXN0DQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1NBDQptb2RlbG9fU0EgPC0gY29tYl9TQShkYXRhKQ0KYWNjdXJhY3lfU0EgPC0gbW9kZWxvX1NBJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9UQQ0KbW9kZWxvX1RBIDwtIGNvbWJfVEEoZGF0YSkNCmFjY3VyYWN5X1RBIDwtIG1vZGVsb19UQSRBY2N1cmFjeV9UZXN0IyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1dBDQptb2RlbG9fV0EgPC0gY29tYl9XQShkYXRhKQ0KYWNjdXJhY3lfV0EgPC0gbW9kZWxvX1dBJEFjY3VyYWN5X1Rlc3QNCg0KDQpgYGANCg0KDQojIyMjNSBhw7Fvcw0KDQoNCmBgYHtyfQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkodGlkeXIpDQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDVfYW5vcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NDpucm93KGRhdG9zKSwgXQ0KDQp5PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOCwxMCksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KYXV0b3Bsb3QoeSkNCg0KYGBgDQoNCg0KYGBge3J9DQoNCmY8LXdpbmRvdyh5LHN0YXJ0PWMoMjAxMSwxKSxlbmQ9YygyMDIwLDExKSkNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgMjAyMC0yMDI0DQoNCmRlcml2YTwtcndmKGYsaD0zMCxkcmlmdD1UUlVFKSANCg0KcHJlZGljY2lvbmVzX2Rlcml2YTwtZGVyaXZhJG1lYW4NCg0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSBudWV2YXMNCg0KZGVyaXZhPC1yd2YoeSxoPTEyLGRyaWZ0PVRSVUUpIA0KDQpwcmVkaWNjaW9uZXNfZGVyaXZhX251ZXZhczwtZGVyaXZhJG1lYW4NCg0KDQojIyNBbGlzYWRvIGV4cG9uZW5jaWFsIGRlIEhvbHQgbnVldmFzDQoNCmhvbHQgPC0gaG9sdCh5LCBoID0gMTIsIGxldmVsID0gOTUpICAgDQoNCg0KcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzPC1ob2x0JG1lYW4NCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IDIwMjQtMjAyNA0KDQpob2x0IDwtIGhvbHQoZiwgaCA9IDMwLCBsZXZlbCA9IDk1KQ0KDQpwcmVkaWNjaW9uZXNfaG9sdDwtaG9sdCRtZWFuDQoNCg0KYXV0by5hcmltYSh5KQ0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKHksDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDApLCAgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9RkFMU0UpDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MTIpDQoNCg0KcHJlZGljY2lvbmVzX2FyaW1hX251ZXZhczwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KDQojIyNBUklNQSBkZSAyMDIwLTIwMjQNCg0KYXV0by5hcmltYSh5KQ0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKGYsDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDApLCAgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9RkFMU0UpDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MzApDQoNCnByZWRpY2Npb25lc19hcmltYTwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KI1JlZGVzIG5ldXJvbmFsZXMgIG51ZXZhcw0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcih5KQ0KDQoNCiMgR2VuZXJhciBwcm9uw7NzdGljb3MNCnByb25vc3RpY29zIDwtIGZvcmVjYXN0KG1vZGVsbyxoPTEyKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXNfbnVldmFzPC1wcm9ub3N0aWNvcyRtZWFuDQoNCiNSZWRlcyBuZXVyb25hbGVzICAyMDIwLTIwMjQNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBubmV0YXINCm1vZGVsbyA8LSBubmV0YXIoZikNCg0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8sIGg9MzApDQoNCnByZWRpY2Npb25lc19yZWRlczwtcHJvbm9zdGljb3MkbWVhbg0KDQoNCiMjI0R5bmFtaWMgT3B0aW1pemVkIFRoZXRhIE1vZGVsIG51ZXZhcw0KDQpsaWJyYXJ5KGZvcmVjVGhldGEpDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oeSAsIGg9MTIpDQoNCg0KcHJlZGljY2lvbmVzX2RvdF9udWV2YXM8LW1vZGVsb19kb3QkbWVhbg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCAyMDIwLTIwMjQNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBET1QNCm1vZGVsb19kb3QgPC0gZG90bShmICwgaD0zMCkNCg0KDQoNCnByZWRpY2Npb25lc19kb3Q8LW1vZGVsb19kb3QkbWVhbg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkZvcmVjYXN0Q29tYiIpDQpsaWJyYXJ5KEZvcmVjYXN0Q29tYikNCg0KbmV3cHJlZHMgID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YV9udWV2YXMsIHByZWRpY2Npb25lc19hcmltYV9udWV2YXMsIHByZWRpY2Npb25lc19ob2x0X251ZXZhcywgcHJlZGljY2lvbmVzX3JlZGVzX251ZXZhcywgcHJlZGljY2lvbmVzX2RvdF9udWV2YXMpDQpwcmVkcyA9IGNiaW5kKHByZWRpY2Npb25lc19kZXJpdmEsIHByZWRpY2Npb25lc19hcmltYSwgcHJlZGljY2lvbmVzX2hvbHQsIHByZWRpY2Npb25lc19yZWRlcywgcHJlZGljY2lvbmVzX2RvdCkNCmRpbShwcmVkcykNCg0KcHJlZHM8LW1hdHJpeChwcmVkcywzMCw1KQ0KDQoNCm9iczwtd2luZG93KHksc3RhcnQ9YygyMDIwLDExKSwgZW5kPWMoMjAyMyw0KSkNCmxlbmd0aChvYnMpDQoNCg0KIyBEaXZpZGlyIGxvcyBkYXRvcyBlbiBjb25qdW50b3MgZGUgZW50cmVuYW1pZW50byB5IHBydWViYQ0KdHJhaW5fbyA8LSBvYnNbMToyNF0NCmxlbmd0aCh0cmFpbl9vKQ0KdHJhaW5fcCA8LSBwcmVkc1sxOjI0LF0NCnRlc3RfbyA8LSBvYnNbMjU6MzBdDQp0ZXN0X3AgPC0gcHJlZHNbMjU6MzAsXQ0KIyBDb21iaW5hciBwcmVkaWNjaW9uZXMgdXRpbGl6YW5kbyBmb3JlY2NvbWINCmRhdGEgPC0gZm9yZWNjb21iKHRyYWluX28sIHRyYWluX3AsIHRlc3RfbywgdGVzdF9wKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfRUlHMg0KbW9kZWxvPC1jb21iX0VJRzIoZGF0YSkNCmFjY3VyYWN5X0VJRzI8LW1vZGVsbyRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfQkcNCm1vZGVsb19CRyA8LSBjb21iX0JHKGRhdGEpDQphY2N1cmFjeV9CRyA8LSBtb2RlbG9fQkckQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX0ludlcNCm1vZGVsb19JbnZXIDwtIGNvbWJfSW52VyhkYXRhKQ0KYWNjdXJhY3lfSW52VyA8LSBtb2RlbG9fSW52VyRBY2N1cmFjeV9UZXN0ICMjIyMjIyMjIw0KDQppbmRpY2VzX2NpbmNvX2Fub3M8LW5ld3ByZWRzWywtMV0lKiVtb2RlbG9fSW52VyRXZWlnaHRzDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX01FRA0KbW9kZWxvX01FRCA8LSBjb21iX01FRChkYXRhKQ0KYWNjdXJhY3lfTUVEIDwtIG1vZGVsb19NRUQkQWNjdXJhY3lfVGVzDQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfU0ENCm1vZGVsb19TQSA8LSBjb21iX1NBKGRhdGEpDQphY2N1cmFjeV9TQSA8LSBtb2RlbG9fU0EkQWNjdXJhY3lfVGVzdA0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1RBDQptb2RlbG9fVEEgPC0gY29tYl9UQShkYXRhKQ0KYWNjdXJhY3lfVEEgPC0gbW9kZWxvX1RBJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9XQQ0KbW9kZWxvX1dBIDwtIGNvbWJfV0EoZGF0YSkNCmFjY3VyYWN5X1dBIDwtIG1vZGVsb19XQSRBY2N1cmFjeV9UZXN0DQoNCg0KYGBgDQoNCg0KDQojIyMjMTAgYcOxb3MNCg0KDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQpsaWJyYXJ5KHRpZHlyKQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDNfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlgxMF9hbm9zIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzk4Om5yb3coZGF0b3MpLCBdDQoNCnk9dHMocmV2KGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDA5LDIpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmF1dG9wbG90KHkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpmPC13aW5kb3coeSxzdGFydD1jKDIwMTEsMSksZW5kPWMoMjAyMSwyKSkNCg0KIyMjTcOpdG9kbyBkZSBsYSBkZXJpdmEgMjAyMC0yMDI0DQoNCmRlcml2YTwtcndmKGYsaD0yNyxkcmlmdD1UUlVFKSANCg0KcHJlZGljY2lvbmVzX2Rlcml2YTwtZGVyaXZhJG1lYW4NCg0KDQojIyNNw6l0b2RvIGRlIGxhIGRlcml2YSBudWV2YXMNCg0KZGVyaXZhPC1yd2YoeSxoPTEyLGRyaWZ0PVRSVUUpIA0KDQpwcmVkaWNjaW9uZXNfZGVyaXZhX251ZXZhczwtZGVyaXZhJG1lYW4NCg0KDQojIyNBbGlzYWRvIGV4cG9uZW5jaWFsIGRlIEhvbHQgbnVldmFzDQoNCmhvbHQgPC0gaG9sdCh5LCBoID0gMTIsIGxldmVsID0gOTUpICAgDQoNCg0KcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzPC1ob2x0JG1lYW4NCg0KIyMjQWxpc2FkbyBleHBvbmVuY2lhbCBkZSBIb2x0IDIwMjQtMjAyNA0KDQpob2x0IDwtIGhvbHQoZiwgaCA9IDI3LCBsZXZlbCA9IDk1KQ0KDQpwcmVkaWNjaW9uZXNfaG9sdDwtaG9sdCRtZWFuDQoNCg0KYXV0by5hcmltYSh5KQ0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKHksDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDApLCAgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9RkFMU0UpDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MTIpDQoNCg0KcHJlZGljY2lvbmVzX2FyaW1hX251ZXZhczwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KDQojIyNBUklNQSBkZSAyMDIwLTIwMjQNCg0KYXV0by5hcmltYSh5KQ0KDQphY3RpdmlkYWRfYXJpbWE8LUFyaW1hKGYsDQogICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPWMoMCwxLDApLCAgDQogICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUuZHJpZnQ9RkFMU0UpDQoNCmFyaW1hX2ZvcmVjYXN0PC1mb3JlY2FzdChhY3RpdmlkYWRfYXJpbWEsIGg9MjcpDQoNCnByZWRpY2Npb25lc19hcmltYTwtYXJpbWFfZm9yZWNhc3QkbWVhbg0KI1JlZGVzIG5ldXJvbmFsZXMgIG51ZXZhcw0KDQojIEFqdXN0YXIgZWwgbW9kZWxvIG5uZXRhcg0KbW9kZWxvIDwtIG5uZXRhcih5KQ0KDQoNCiMgR2VuZXJhciBwcm9uw7NzdGljb3MNCnByb25vc3RpY29zIDwtIGZvcmVjYXN0KG1vZGVsbyxoPTEyKQ0KDQpwcmVkaWNjaW9uZXNfcmVkZXNfbnVldmFzPC1wcm9ub3N0aWNvcyRtZWFuDQoNCiNSZWRlcyBuZXVyb25hbGVzICAyMDIwLTIwMjQNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBubmV0YXINCm1vZGVsbyA8LSBubmV0YXIoZikNCg0KDQojIEdlbmVyYXIgcHJvbsOzc3RpY29zDQpwcm9ub3N0aWNvcyA8LSBmb3JlY2FzdChtb2RlbG8sIGg9MjcpDQoNCnByZWRpY2Npb25lc19yZWRlczwtcHJvbm9zdGljb3MkbWVhbg0KDQoNCiMjI0R5bmFtaWMgT3B0aW1pemVkIFRoZXRhIE1vZGVsIG51ZXZhcw0KDQpsaWJyYXJ5KGZvcmVjVGhldGEpDQoNCiMgQWp1c3RhciBlbCBtb2RlbG8gRE9UDQptb2RlbG9fZG90IDwtIGRvdG0oeSAsIGg9MTIpDQoNCg0KcHJlZGljY2lvbmVzX2RvdF9udWV2YXM8LW1vZGVsb19kb3QkbWVhbg0KDQojIyNEeW5hbWljIE9wdGltaXplZCBUaGV0YSBNb2RlbCAyMDIwLTIwMjQNCg0KIyBBanVzdGFyIGVsIG1vZGVsbyBET1QNCm1vZGVsb19kb3QgPC0gZG90bShmICwgaD0yNykNCg0KDQoNCnByZWRpY2Npb25lc19kb3Q8LW1vZGVsb19kb3QkbWVhbg0KYGBgDQoNCg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiRm9yZWNhc3RDb21iIikNCmxpYnJhcnkoRm9yZWNhc3RDb21iKQ0KDQpuZXdwcmVkcyAgPSBjYmluZChwcmVkaWNjaW9uZXNfZGVyaXZhX251ZXZhcywgcHJlZGljY2lvbmVzX2FyaW1hX251ZXZhcywgcHJlZGljY2lvbmVzX2hvbHRfbnVldmFzLCBwcmVkaWNjaW9uZXNfcmVkZXNfbnVldmFzLCBwcmVkaWNjaW9uZXNfZG90X251ZXZhcykNCnByZWRzID0gY2JpbmQocHJlZGljY2lvbmVzX2Rlcml2YSwgcHJlZGljY2lvbmVzX2FyaW1hLCBwcmVkaWNjaW9uZXNfaG9sdCwgcHJlZGljY2lvbmVzX3JlZGVzLCBwcmVkaWNjaW9uZXNfZG90KQ0KZGltKHByZWRzKQ0KDQpwcmVkczwtbWF0cml4KHByZWRzLDI3LDUpDQoNCg0Kb2JzPC13aW5kb3coeSxzdGFydD1jKDIwMjEsMiksIGVuZD1jKDIwMjMsNCkpDQpsZW5ndGgob2JzKQ0KDQojIERpdmlkaXIgbG9zIGRhdG9zIGVuIGNvbmp1bnRvcyBkZSBlbnRyZW5hbWllbnRvIHkgcHJ1ZWJhDQp0cmFpbl9vIDwtIG9ic1sxOjIzXQ0KbGVuZ3RoKHRyYWluX28pDQp0cmFpbl9wIDwtIHByZWRzWzE6MjMsXQ0KdGVzdF9vIDwtIG9ic1syNDoyN10NCnRlc3RfcCA8LSBwcmVkc1syNDoyNyxdDQoNCiMgQ29tYmluYXIgcHJlZGljY2lvbmVzIHV0aWxpemFuZG8gZm9yZWNjb21iDQpkYXRhIDwtIGZvcmVjY29tYih0cmFpbl9vLCB0cmFpbl9wLCB0ZXN0X28sIHRlc3RfcCkNCg0KYGBgDQoNCmBgYHtyfQ0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9FSUcyDQptb2RlbG88LWNvbWJfRUlHMihkYXRhKQ0KYWNjdXJhY3lfRUlHMjwtbW9kZWxvJEFjY3VyYWN5X1Rlc3QNCmluZGljZXNfZGllel9hbm9zPC1tb2RlbG8kUHJlZGljdChtb2RlbG8sbmV3cHJlZHMgPSBuZXdwcmVkc1ssLTNdKQ0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9CRw0KbW9kZWxvX0JHIDwtIGNvbWJfQkcoZGF0YSkNCmFjY3VyYWN5X0JHIDwtIG1vZGVsb19CRyRBY2N1cmFjeV9UZXN0DQoNCiMgRW50cmVuYXIgbW9kZWxvIGNvbWJfSW52Vw0KbW9kZWxvX0ludlcgPC0gY29tYl9JbnZXKGRhdGEpDQphY2N1cmFjeV9JbnZXIDwtIG1vZGVsb19JbnZXJEFjY3VyYWN5X1Rlc3QgDQpuZXdwcmVkc1ssLTFdJSolbW9kZWxvX0ludlckV2VpZ2h0cw0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9NRUQNCm1vZGVsb19NRUQgPC0gY29tYl9NRUQoZGF0YSkNCmFjY3VyYWN5X01FRCA8LSBtb2RlbG9fTUVEJEFjY3VyYWN5X1Rlc3QNCg0KDQojIEVudHJlbmFyIG1vZGVsbyBjb21iX1NBDQptb2RlbG9fU0EgPC0gY29tYl9TQShkYXRhKQ0KYWNjdXJhY3lfU0EgPC0gbW9kZWxvX1NBJEFjY3VyYWN5X1Rlc3QNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9UQQ0KbW9kZWxvX1RBIDwtIGNvbWJfVEEoZGF0YSkNCmFjY3VyYWN5X1RBIDwtIG1vZGVsb19UQSRBY2N1cmFjeV9UZXMNCg0KIyBFbnRyZW5hciBtb2RlbG8gY29tYl9XQQ0KbW9kZWxvX1dBIDwtIGNvbWJfV0EoZGF0YSkNCmFjY3VyYWN5X1dBIDwtIG1vZGVsb19XQSRBY2N1cmFjeV9UZXN0DQoNCg0KYGBgDQoNCg0KIyBQcmVkaWNjaW9uZXMgcGFyYSBjYWxjdWxhciBlbCBNQVBFIGNvbiBGb3JlY2FzdGNvbWINCg0KDQojIyMjIDMgbWVzZXMNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgM19tZXNlcw0KZGF0b3MgPC0gZGF0b3NbZGF0b3MkUGVyaW9kbyA9PSAiWDNfbWVzZXMiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTQ6bnJvdyhkYXRvcyktMTIsIF0NCg0KeD10cyhyZXYoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMDgsMTApLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmRhdG9zPWxpc3QoeD14KQ0KDQpgYGANCg0KYGBge3IsZWNobz1GQUxTRX0NCg0KIyMgVGhlIGFsZ29yaXRobSBpcyBkZXNpZ25lZCB0byBvYnRhaW4gdGhlIHByZWRpY3Rpb25zIG9mIGEgbW9udGhseSBzZXJpZXMNCmVuc2F5bzwtMTpsZW5ndGgoZGF0b3MpDQoNCiMjIEFwcGx5IGVhY2ggbWV0aG9kIHdpdGggYWxsIGl0cyBjb25maWd1cmF0aW9ucyBpbiBwZXJpb2QgVDENCm1ldG9kb3MuVDE8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMVtbal1dPC1jYWxjdWxvMShqKQ0KfQ0KDQojIyBPYnRhaW5zIGxvc3MgZnVuY3Rpb25zIGluIFQxIGFuZCBUMg0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMVtbal1dPC1jYWxjdWxvMihqKSANCn0NCmVycm9yZXNUMjwtbGlzdCgpIA0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgZXJyb3Jlc1QyW1tqXV08LWNhbGN1bG8zKGopDQp9DQoNCiMjIENhbGN1bGF0ZXMgUENBIGFuZCBNYW5oYXR0YW4gZGlzdGFuY2UgdG8gdGhlIG1ldGhvZCBhc3NvY2lhdGVkIHdpdGggdGhlIHNtYWxsZXN0IGVycm9yDQpyZXN1bHRhZG88LWxpc3QoKQ0KcGNhPC1yZXN1bHRhZG9bWzFdXTwtcmVzdWx0YWRvW1syXV08LXJlc3VsdGFkb1tbM11dPC1saXN0KCkNCmRpcy5hanVzdDwtcmVwKE5BLGxlbmd0aChlbnNheW8pKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgcmVzdWx0YWRvPC1jYWxjdWxvNC5tKGopIA0KICBpZihzdHJfZGV0ZWN0KHJlc3VsdGFkb1tbM11dW1tqXV1bMSwxXSwgIm1sZXhwIik9PVRSVUUpIHtkaXMuYWp1c3Rbal08LTF9IGVsc2UgDQogICAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGdhbW1hIik9PVRSVUUpIHtkaXMuYWp1c3Rbal08LTJ9IGVsc2Uge2Rpcy5hanVzdFtqXTwtM30NCn0NCg0KDQojIyBhbFBDQSBlc3RhYmxpc2hlcyB0aGUgcGVyY2VudGlsZXMgb2YgdGhlIGFkanVzdGVkIGRpc3RyaWJ1dGlvbiBhcyBjdXQtb2ZmIHBvaW50cyBmb3IgbWV0aG9kIHNlbGVjdGlvbg0KY290YTwtYygwLjA1LDAuNTAsIDAuOTUpIA0KcmVzdWx0YWRvW1s0XV08LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsgIA0KICByZXN1bHRhZG9bWzRdXVtbal1dPC1saXN0KCkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGNvdGEpKSB7ICANCiAgICByZXN1bHRhZG88LWNhbGN1bG81KGosaSkNCiAgfQ0KfQ0KDQojIyBPYnRhaW4gdGhlIHByZWRpY3Rpb25zIGZvciB0aGUgcGVyaW9kIFQxK1QyDQptZXRvZG9zLlQxMjwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxMltbal1dPC1jYWxjdWxvNihqKQ0KfQ0KDQojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgT2J0YWlucyB0aGUgcHJlZGljdGlvbnMgZm9yIFQzIGFjY29yZGluZyB0byB0aGUgcGVyY2VudGlsZSBjaG9zZW4gYnkgdGhlIHVzZXINCmo9MTtuX3A9MTI7cHJlZDU9cHJlZDUwPXByZWQ5NT1OVUxMO3l1PTENCmIxPC1yZXAoTkEsbl9wKQ0KIyNmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KZm9yIChpIGluIDE6bGVuZ3RoKGNvdGEpKSB7DQogIA0KICBuX3A8LTEyICNwcmVkaWNjaW9uZXMNCiAgZGF0PC1yZXN1bHRhZG9bWzRdXVtbal1dW1tpXV0NCiAgIyNNYXRyaXogcHJlZGljY2lvbmVzIG3DqXRvZG9zIHBhcmEgVDINCiAgcHJlZF9UMjwtcHJlZF9UMzwtbWF0cml4KE5BLG5yb3c9ZGltKGRhdClbMV0sbmNvbCA9IG5fcCkNCiAgZm9yIChrIGluIDE6ZGltKGRhdClbMV0pIHsNCiAgICBmb3IgKGtrIGluIDE6bGVuZ3RoKG1ldG9kb3MuVDFbW2pdXSkpIHsgDQogICAgICAjcHJpbnQobWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFuKQ0KICAgICAgaWYoZGF0W2ssMV09PWtrKXtwcmVkX1QyW2ssXTwtbWV0b2Rvcy5UMVtbal1dW1tra11dJG1lYW47IHByZWRfVDNbayxdPC1tZXRvZG9zLlQxMltbal1dW1tra11dJG1lYW59DQogICAgfQ0KICB9DQogIGZvciAoayBpbiAxOm5fcCkgew0KICAgIGIxW2tdPC1wcmVkX1QzWyxrXSUqJWRhdFssMTFdO2lmKGk9PTEpe3ByZWQ1W2tdPWIxW2tdfTsgaWYoaT09Mil7cHJlZDUwW2tdPWIxW2tdfTsgaWYoaT09Myl7cHJlZDk1W2tdPWIxW2tdfQ0KICB9DQp9DQpwZXJjZW50aWxlLjU9cm91bmQocHJlZDUsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjUwPXJvdW5kKHByZWQ1MCwgZGlnaXRzPTIpO3BlcmNlbnRpbGUuOTU9cm91bmQocHJlZDk1LCBkaWdpdHM9MikNCnJlc3VsdF9hbFBDQTwtY2JpbmQuZGF0YS5mcmFtZShwZXJjZW50aWxlLjUscGVyY2VudGlsZS41MCxwZXJjZW50aWxlLjk1KQ0KDQpybShkYXQpO3JtKGVycm9yZXNUMik7cm0obWV0b2Rvcy5UMSk7cm0obWV0b2Rvcy5UMTIpO3JtKHBjYSkNCnJtKHByZWRfVDIpO3JtKGIxKTsNCnJtKGNvdGEpO3JtKGRpcy5hanVzdCk7cm0oZW5zYXlvKTtybShpKTtybShqKTtybShrKTtybShraykNCnJtKG5fcCk7cm0oeXUpO3JtKHByZWRfVDMpO3JtKHJlc3VsdGFkbyk7cm0ocHJlZDUpO3JtKHByZWQ1MCk7cm0ocHJlZDk1KQ0Kcm0ocGVyY2VudGlsZS41KTtybShwZXJjZW50aWxlLjUwKTtybShwZXJjZW50aWxlLjk1KQ0KcmVzdWx0X2FsUENBDQojIyBZb3UgY2FuIGNob29zZSB0aGUgcGVyY2VudGlsZSBjb2x1bW4gYW5kIGNvcnJlc3BvbmRpbmcgcHJlZGljdGlvbg0KcmVzdWx0X2FsUENBXzNtZXNlczwtcmVzdWx0X2FsUENBJHBlcmNlbnRpbGUuOTUNCg0KYGBgDQoNCiMjIyMgNiBtZXNlcw0KQWhvcmEgcHJlZGVjaW1vcyBjb24gbGFzIGxldHJhcyBkZSA2IG1lc2VzLg0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCmRhdG9zPXJlYWQuY3N2MigiRGF0b3NfY3N2LmNzdiIsIGhlYWRlciA9IFRSVUUpDQpkYXRvczwtIHBpdm90X2xvbmdlcihkYXRvcywgLVRpZW1wbywgbmFtZXNfdG8gPSAiUGVyaW9kbyIsIHZhbHVlc190byA9ICJWYWxvciIpDQojRmlsdHJvIHBvciAzX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYNl9tZXNlcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5NTpucm93KGRhdG9zKS0xMiwgXQ0KDQp4PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOCwxMSksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KZGF0b3M9bGlzdCh4PXgpDQoNCmBgYA0KDQpgYGB7cixlY2hvPUZBTFNFfQ0KDQojIyBUaGUgYWxnb3JpdGhtIGlzIGRlc2lnbmVkIHRvIG9idGFpbiB0aGUgcHJlZGljdGlvbnMgb2YgYSBtb250aGx5IHNlcmllcw0KZW5zYXlvPC0xOmxlbmd0aChkYXRvcykNCg0KIyMgQXBwbHkgZWFjaCBtZXRob2Qgd2l0aCBhbGwgaXRzIGNvbmZpZ3VyYXRpb25zIGluIHBlcmlvZCBUMQ0KbWV0b2Rvcy5UMTwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxW1tqXV08LWNhbGN1bG8xKGopDQp9DQoNCiMjIE9idGFpbnMgbG9zcyBmdW5jdGlvbnMgaW4gVDEgYW5kIFQyDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxW1tqXV08LWNhbGN1bG8yKGopIA0KfQ0KZXJyb3Jlc1QyPC1saXN0KCkgDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBlcnJvcmVzVDJbW2pdXTwtY2FsY3VsbzMoaikNCn0NCg0KIyMgQ2FsY3VsYXRlcyBQQ0EgYW5kIE1hbmhhdHRhbiBkaXN0YW5jZSB0byB0aGUgbWV0aG9kIGFzc29jaWF0ZWQgd2l0aCB0aGUgc21hbGxlc3QgZXJyb3INCnJlc3VsdGFkbzwtbGlzdCgpDQpwY2E8LXJlc3VsdGFkb1tbMV1dPC1yZXN1bHRhZG9bWzJdXTwtcmVzdWx0YWRvW1szXV08LWxpc3QoKQ0KZGlzLmFqdXN0PC1yZXAoTkEsbGVuZ3RoKGVuc2F5bykpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICByZXN1bHRhZG88LWNhbGN1bG80Lm0oaikgDQogIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxleHAiKT09VFJVRSkge2Rpcy5hanVzdFtqXTwtMX0gZWxzZSANCiAgICBpZihzdHJfZGV0ZWN0KHJlc3VsdGFkb1tbM11dW1tqXV1bMSwxXSwgIm1sZ2FtbWEiKT09VFJVRSkge2Rpcy5hanVzdFtqXTwtMn0gZWxzZSB7ZGlzLmFqdXN0W2pdPC0zfQ0KfQ0KDQoNCiMjIGFsUENBIGVzdGFibGlzaGVzIHRoZSBwZXJjZW50aWxlcyBvZiB0aGUgYWRqdXN0ZWQgZGlzdHJpYnV0aW9uIGFzIGN1dC1vZmYgcG9pbnRzIGZvciBtZXRob2Qgc2VsZWN0aW9uDQpjb3RhPC1jKDAuMDUsMC41MCwgMC45NSkgDQpyZXN1bHRhZG9bWzRdXTwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgeyAgDQogIHJlc3VsdGFkb1tbNF1dW1tqXV08LWxpc3QoKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgoY290YSkpIHsgIA0KICAgIHJlc3VsdGFkbzwtY2FsY3VsbzUoaixpKQ0KICB9DQp9DQoNCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIHRoZSBwZXJpb2QgVDErVDINCm1ldG9kb3MuVDEyPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDEyW1tqXV08LWNhbGN1bG82KGopDQp9DQoNCiMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyBPYnRhaW4gdGhlIHByZWRpY3Rpb25zIGZvciBPYnRhaW5zIHRoZSBwcmVkaWN0aW9ucyBmb3IgVDMgYWNjb3JkaW5nIHRvIHRoZSBwZXJjZW50aWxlIGNob3NlbiBieSB0aGUgdXNlcg0Kaj0xO25fcD0xMjtwcmVkNT1wcmVkNTA9cHJlZDk1PU5VTEw7eXU9MQ0KYjE8LXJlcChOQSxuX3ApDQojI2ZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQpmb3IgKGkgaW4gMTpsZW5ndGgoY290YSkpIHsNCiAgDQogIG5fcDwtMTIgI3ByZWRpY2Npb25lcw0KICBkYXQ8LXJlc3VsdGFkb1tbNF1dW1tqXV1bW2ldXQ0KICAjI01hdHJpeiBwcmVkaWNjaW9uZXMgbcOpdG9kb3MgcGFyYSBUMg0KICBwcmVkX1QyPC1wcmVkX1QzPC1tYXRyaXgoTkEsbnJvdz1kaW0oZGF0KVsxXSxuY29sID0gbl9wKQ0KICBmb3IgKGsgaW4gMTpkaW0oZGF0KVsxXSkgew0KICAgIGZvciAoa2sgaW4gMTpsZW5ndGgobWV0b2Rvcy5UMVtbal1dKSkgeyANCiAgICAgICNwcmludChtZXRvZG9zLlQxMltbal1dW1tra11dJG1lYW4pDQogICAgICBpZihkYXRbaywxXT09a2spe3ByZWRfVDJbayxdPC1tZXRvZG9zLlQxW1tqXV1bW2trXV0kbWVhbjsgcHJlZF9UM1trLF08LW1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbn0NCiAgICB9DQogIH0NCiAgZm9yIChrIGluIDE6bl9wKSB7DQogICAgYjFba108LXByZWRfVDNbLGtdJSolZGF0WywxMV07aWYoaT09MSl7cHJlZDVba109YjFba119OyBpZihpPT0yKXtwcmVkNTBba109YjFba119OyBpZihpPT0zKXtwcmVkOTVba109YjFba119DQogIH0NCn0NCnBlcmNlbnRpbGUuNT1yb3VuZChwcmVkNSwgZGlnaXRzPTIpO3BlcmNlbnRpbGUuNTA9cm91bmQocHJlZDUwLCBkaWdpdHM9Mik7cGVyY2VudGlsZS45NT1yb3VuZChwcmVkOTUsIGRpZ2l0cz0yKQ0KcmVzdWx0X2FsUENBPC1jYmluZC5kYXRhLmZyYW1lKHBlcmNlbnRpbGUuNSxwZXJjZW50aWxlLjUwLHBlcmNlbnRpbGUuOTUpDQoNCnJtKGRhdCk7cm0oZXJyb3Jlc1QyKTtybShtZXRvZG9zLlQxKTtybShtZXRvZG9zLlQxMik7cm0ocGNhKQ0Kcm0ocHJlZF9UMik7cm0oYjEpOw0Kcm0oY290YSk7cm0oZGlzLmFqdXN0KTtybShlbnNheW8pO3JtKGkpO3JtKGopO3JtKGspO3JtKGtrKQ0Kcm0obl9wKTtybSh5dSk7cm0ocHJlZF9UMyk7cm0ocmVzdWx0YWRvKTtybShwcmVkNSk7cm0ocHJlZDUwKTtybShwcmVkOTUpDQpybShwZXJjZW50aWxlLjUpO3JtKHBlcmNlbnRpbGUuNTApO3JtKHBlcmNlbnRpbGUuOTUpDQoNCiMjIFlvdSBjYW4gY2hvb3NlIHRoZSBwZXJjZW50aWxlIGNvbHVtbiBhbmQgY29ycmVzcG9uZGluZyBwcmVkaWN0aW9uDQpyZXN1bHRfYWxQQ0ENCg0KcmVzdWx0X2FsUENBXzZtZXNlczwtcmVzdWx0X2FsUENBJHBlcmNlbnRpbGUuOTUNCmBgYA0KDQojIyMjIDkgbWVzZXMNCkEgY29udGludWFjacOzbiBwcmVkZWNpbW9zIGNvbiBsYXMgbGV0cmFzIGRlIDkgbWVzZXMuDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDlfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlg5X21lc2VzIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzE0Njpucm93KGRhdG9zKS0xMiwgXQ0KDQp4PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAxMywyKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQpkYXRvcz1saXN0KHg9eCkNCg0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0V9DQoNCiMjIFRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQgdG8gb2J0YWluIHRoZSBwcmVkaWN0aW9ucyBvZiBhIG1vbnRobHkgc2VyaWVzDQplbnNheW88LTE6bGVuZ3RoKGRhdG9zKQ0KDQojIyBBcHBseSBlYWNoIG1ldGhvZCB3aXRoIGFsbCBpdHMgY29uZmlndXJhdGlvbnMgaW4gcGVyaW9kIFQxDQptZXRvZG9zLlQxPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzEoaikNCn0NCg0KIyMgT2J0YWlucyBsb3NzIGZ1bmN0aW9ucyBpbiBUMSBhbmQgVDINCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzIoaikgDQp9DQplcnJvcmVzVDI8LWxpc3QoKSANCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIGVycm9yZXNUMltbal1dPC1jYWxjdWxvMyhqKQ0KfQ0KDQojIyBDYWxjdWxhdGVzIFBDQSBhbmQgTWFuaGF0dGFuIGRpc3RhbmNlIHRvIHRoZSBtZXRob2QgYXNzb2NpYXRlZCB3aXRoIHRoZSBzbWFsbGVzdCBlcnJvcg0KcmVzdWx0YWRvPC1saXN0KCkNCnBjYTwtcmVzdWx0YWRvW1sxXV08LXJlc3VsdGFkb1tbMl1dPC1yZXN1bHRhZG9bWzNdXTwtbGlzdCgpDQpkaXMuYWp1c3Q8LXJlcChOQSxsZW5ndGgoZW5zYXlvKSkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIHJlc3VsdGFkbzwtY2FsY3VsbzQubShqKSANCiAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGV4cCIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0xfSBlbHNlIA0KICAgIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxnYW1tYSIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0yfSBlbHNlIHtkaXMuYWp1c3Rbal08LTN9DQp9DQoNCg0KIyMgYWxQQ0EgZXN0YWJsaXNoZXMgdGhlIHBlcmNlbnRpbGVzIG9mIHRoZSBhZGp1c3RlZCBkaXN0cmlidXRpb24gYXMgY3V0LW9mZiBwb2ludHMgZm9yIG1ldGhvZCBzZWxlY3Rpb24NCmNvdGE8LWMoMC4wNSwwLjUwLCAwLjk1KSANCnJlc3VsdGFkb1tbNF1dPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7ICANCiAgcmVzdWx0YWRvW1s0XV1bW2pdXTwtbGlzdCgpDQogIGZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgeyAgDQogICAgcmVzdWx0YWRvPC1jYWxjdWxvNShqLGkpDQogIH0NCn0NCg0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHBlcmlvZCBUMStUMg0KbWV0b2Rvcy5UMTI8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMTJbW2pdXTwtY2FsY3VsbzYoaikNCn0NCg0KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIE9idGFpbnMgdGhlIHByZWRpY3Rpb25zIGZvciBUMyBhY2NvcmRpbmcgdG8gdGhlIHBlcmNlbnRpbGUgY2hvc2VuIGJ5IHRoZSB1c2VyDQpqPTE7bl9wPTEyO3ByZWQ1PXByZWQ1MD1wcmVkOTU9TlVMTDt5dT0xDQpiMTwtcmVwKE5BLG5fcCkNCiMjZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCmZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgew0KICANCiAgbl9wPC0xMiAjcHJlZGljY2lvbmVzDQogIGRhdDwtcmVzdWx0YWRvW1s0XV1bW2pdXVtbaV1dDQogICMjTWF0cml6IHByZWRpY2Npb25lcyBtw6l0b2RvcyBwYXJhIFQyDQogIHByZWRfVDI8LXByZWRfVDM8LW1hdHJpeChOQSxucm93PWRpbShkYXQpWzFdLG5jb2wgPSBuX3ApDQogIGZvciAoayBpbiAxOmRpbShkYXQpWzFdKSB7DQogICAgZm9yIChrayBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1tqXV0pKSB7IA0KICAgICAgI3ByaW50KG1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbikNCiAgICAgIGlmKGRhdFtrLDFdPT1rayl7cHJlZF9UMltrLF08LW1ldG9kb3MuVDFbW2pdXVtba2tdXSRtZWFuOyBwcmVkX1QzW2ssXTwtbWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFufQ0KICAgIH0NCiAgfQ0KICBmb3IgKGsgaW4gMTpuX3ApIHsNCiAgICBiMVtrXTwtcHJlZF9UM1ssa10lKiVkYXRbLDExXTtpZihpPT0xKXtwcmVkNVtrXT1iMVtrXX07IGlmKGk9PTIpe3ByZWQ1MFtrXT1iMVtrXX07IGlmKGk9PTMpe3ByZWQ5NVtrXT1iMVtrXX0NCiAgfQ0KfQ0KcGVyY2VudGlsZS41PXJvdW5kKHByZWQ1LCBkaWdpdHM9Mik7cGVyY2VudGlsZS41MD1yb3VuZChwcmVkNTAsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjk1PXJvdW5kKHByZWQ5NSwgZGlnaXRzPTIpDQpyZXN1bHRfYWxQQ0E8LWNiaW5kLmRhdGEuZnJhbWUocGVyY2VudGlsZS41LHBlcmNlbnRpbGUuNTAscGVyY2VudGlsZS45NSkNCg0Kcm0oZGF0KTtybShlcnJvcmVzVDIpO3JtKG1ldG9kb3MuVDEpO3JtKG1ldG9kb3MuVDEyKTtybShwY2EpDQpybShwcmVkX1QyKTtybShiMSk7DQpybShjb3RhKTtybShkaXMuYWp1c3QpO3JtKGVuc2F5byk7cm0oaSk7cm0oaik7cm0oayk7cm0oa2spDQpybShuX3ApO3JtKHl1KTtybShwcmVkX1QzKTtybShyZXN1bHRhZG8pO3JtKHByZWQ1KTtybShwcmVkNTApO3JtKHByZWQ5NSkNCnJtKHBlcmNlbnRpbGUuNSk7cm0ocGVyY2VudGlsZS41MCk7cm0ocGVyY2VudGlsZS45NSkNCg0KIyMgWW91IGNhbiBjaG9vc2UgdGhlIHBlcmNlbnRpbGUgY29sdW1uIGFuZCBjb3JyZXNwb25kaW5nIHByZWRpY3Rpb24NCnJlc3VsdF9hbFBDQQ0KcmVzdWx0X2FsUENBXzltZXNlczwtcmVzdWx0X2FsUENBJHBlcmNlbnRpbGUuOTUNCg0KYGBgDQojIyMjIDEyIG1lc2VzDQpQb3Igw7psdGltbyBlbiBlbCBhcGFydGFkbyBkZSBsZXRyYXMgcHJlZGVjaW1vcyBsYXMgZGUgMTIgbWVzZXMuDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDEyX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYMTJfbWVzZXMiICwgXQ0KbGVuZ3RoKGRhdG9zKQ0KZGF0b3MgPC0gZGF0b3NbMToyNjcsIF0NCg0KeD10cyhyZXYoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMDAsMSksDQogICAgIGZyZXF1ZW5jeSA9IDEyKQ0KZGF0b3M9bGlzdCh4PXgpDQoNCmBgYA0KDQpgYGB7cixlY2hvPUZBTFNFfQ0KDQojIyBUaGUgYWxnb3JpdGhtIGlzIGRlc2lnbmVkIHRvIG9idGFpbiB0aGUgcHJlZGljdGlvbnMgb2YgYSBtb250aGx5IHNlcmllcw0KZW5zYXlvPC0xOmxlbmd0aChkYXRvcykNCg0KIyMgQXBwbHkgZWFjaCBtZXRob2Qgd2l0aCBhbGwgaXRzIGNvbmZpZ3VyYXRpb25zIGluIHBlcmlvZCBUMQ0KbWV0b2Rvcy5UMTwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxW1tqXV08LWNhbGN1bG8xKGopDQp9DQoNCiMjIE9idGFpbnMgbG9zcyBmdW5jdGlvbnMgaW4gVDEgYW5kIFQyDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxW1tqXV08LWNhbGN1bG8yKGopIA0KfQ0KZXJyb3Jlc1QyPC1saXN0KCkgDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBlcnJvcmVzVDJbW2pdXTwtY2FsY3VsbzMoaikNCn0NCg0KIyMgQ2FsY3VsYXRlcyBQQ0EgYW5kIE1hbmhhdHRhbiBkaXN0YW5jZSB0byB0aGUgbWV0aG9kIGFzc29jaWF0ZWQgd2l0aCB0aGUgc21hbGxlc3QgZXJyb3INCnJlc3VsdGFkbzwtbGlzdCgpDQpwY2E8LXJlc3VsdGFkb1tbMV1dPC1yZXN1bHRhZG9bWzJdXTwtcmVzdWx0YWRvW1szXV08LWxpc3QoKQ0KZGlzLmFqdXN0PC1yZXAoTkEsbGVuZ3RoKGVuc2F5bykpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICByZXN1bHRhZG88LWNhbGN1bG80Lm0oaikgDQogIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxleHAiKT09VFJVRSkge2Rpcy5hanVzdFtqXTwtMX0gZWxzZSANCiAgICBpZihzdHJfZGV0ZWN0KHJlc3VsdGFkb1tbM11dW1tqXV1bMSwxXSwgIm1sZ2FtbWEiKT09VFJVRSkge2Rpcy5hanVzdFtqXTwtMn0gZWxzZSB7ZGlzLmFqdXN0W2pdPC0zfQ0KfQ0KDQoNCiMjIGFsUENBIGVzdGFibGlzaGVzIHRoZSBwZXJjZW50aWxlcyBvZiB0aGUgYWRqdXN0ZWQgZGlzdHJpYnV0aW9uIGFzIGN1dC1vZmYgcG9pbnRzIGZvciBtZXRob2Qgc2VsZWN0aW9uDQpjb3RhPC1jKDAuMDUsMC41MCwgMC45NSkgDQpyZXN1bHRhZG9bWzRdXTwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgeyAgDQogIHJlc3VsdGFkb1tbNF1dW1tqXV08LWxpc3QoKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgoY290YSkpIHsgIA0KICAgIHJlc3VsdGFkbzwtY2FsY3VsbzUoaixpKQ0KICB9DQp9DQoNCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIHRoZSBwZXJpb2QgVDErVDINCm1ldG9kb3MuVDEyPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDEyW1tqXV08LWNhbGN1bG82KGopDQp9DQoNCiMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIyBPYnRhaW4gdGhlIHByZWRpY3Rpb25zIGZvciBPYnRhaW5zIHRoZSBwcmVkaWN0aW9ucyBmb3IgVDMgYWNjb3JkaW5nIHRvIHRoZSBwZXJjZW50aWxlIGNob3NlbiBieSB0aGUgdXNlcg0Kaj0xO25fcD0xMjtwcmVkNT1wcmVkNTA9cHJlZDk1PU5VTEw7eXU9MQ0KYjE8LXJlcChOQSxuX3ApDQojI2ZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQpmb3IgKGkgaW4gMTpsZW5ndGgoY290YSkpIHsNCiAgDQogIG5fcDwtMTIgI3ByZWRpY2Npb25lcw0KICBkYXQ8LXJlc3VsdGFkb1tbNF1dW1tqXV1bW2ldXQ0KICAjI01hdHJpeiBwcmVkaWNjaW9uZXMgbcOpdG9kb3MgcGFyYSBUMg0KICBwcmVkX1QyPC1wcmVkX1QzPC1tYXRyaXgoTkEsbnJvdz1kaW0oZGF0KVsxXSxuY29sID0gbl9wKQ0KICBmb3IgKGsgaW4gMTpkaW0oZGF0KVsxXSkgew0KICAgIGZvciAoa2sgaW4gMTpsZW5ndGgobWV0b2Rvcy5UMVtbal1dKSkgeyANCiAgICAgICNwcmludChtZXRvZG9zLlQxMltbal1dW1tra11dJG1lYW4pDQogICAgICBpZihkYXRbaywxXT09a2spe3ByZWRfVDJbayxdPC1tZXRvZG9zLlQxW1tqXV1bW2trXV0kbWVhbjsgcHJlZF9UM1trLF08LW1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbn0NCiAgICB9DQogIH0NCiAgZm9yIChrIGluIDE6bl9wKSB7DQogICAgYjFba108LXByZWRfVDNbLGtdJSolZGF0WywxMV07aWYoaT09MSl7cHJlZDVba109YjFba119OyBpZihpPT0yKXtwcmVkNTBba109YjFba119OyBpZihpPT0zKXtwcmVkOTVba109YjFba119DQogIH0NCn0NCnBlcmNlbnRpbGUuNT1yb3VuZChwcmVkNSwgZGlnaXRzPTIpO3BlcmNlbnRpbGUuNTA9cm91bmQocHJlZDUwLCBkaWdpdHM9Mik7cGVyY2VudGlsZS45NT1yb3VuZChwcmVkOTUsIGRpZ2l0cz0yKQ0KcmVzdWx0X2FsUENBPC1jYmluZC5kYXRhLmZyYW1lKHBlcmNlbnRpbGUuNSxwZXJjZW50aWxlLjUwLHBlcmNlbnRpbGUuOTUpDQoNCnJtKGRhdCk7cm0oZXJyb3Jlc1QyKTtybShtZXRvZG9zLlQxKTtybShtZXRvZG9zLlQxMik7cm0ocGNhKQ0Kcm0ocHJlZF9UMik7cm0oYjEpOw0Kcm0oY290YSk7cm0oZGlzLmFqdXN0KTtybShlbnNheW8pO3JtKGkpO3JtKGopO3JtKGspO3JtKGtrKQ0Kcm0obl9wKTtybSh5dSk7cm0ocHJlZF9UMyk7cm0ocmVzdWx0YWRvKTtybShwcmVkNSk7cm0ocHJlZDUwKTtybShwcmVkOTUpDQpybShwZXJjZW50aWxlLjUpO3JtKHBlcmNlbnRpbGUuNTApO3JtKHBlcmNlbnRpbGUuOTUpDQoNCiMjIFlvdSBjYW4gY2hvb3NlIHRoZSBwZXJjZW50aWxlIGNvbHVtbiBhbmQgY29ycmVzcG9uZGluZyBwcmVkaWN0aW9uDQpyZXN1bHRfYWxQQ0ENCnJlc3VsdF9hbFBDQV8xMm1lc2VzPC1yZXN1bHRfYWxQQ0EkcGVyY2VudGlsZS45NQ0KDQpgYGANCg0KIyMjIyAzIGHDsW9zDQpCb25vcyBhIDMgYcOxb3MgDQoNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpkYXRvcz1yZWFkLmNzdjIoIkRhdG9zX2Nzdi5jc3YiLCBoZWFkZXIgPSBUUlVFKQ0KZGF0b3M8LSBwaXZvdF9sb25nZXIoZGF0b3MsIC1UaWVtcG8sIG5hbWVzX3RvID0gIlBlcmlvZG8iLCB2YWx1ZXNfdG8gPSAiVmFsb3IiKQ0KI0ZpbHRybyBwb3IgMTJfbWVzZXMNCmRhdG9zIDwtIGRhdG9zW2RhdG9zJFBlcmlvZG8gPT0gIlgzX2Fub3MiICwgXQ0KZGF0b3MgPC0gZGF0b3NbOTU6bnJvdyhkYXRvcyktMTIsIF0NCg0KeD10cyhyZXYoZGF0b3MkVmFsb3IpLA0KICAgICBzdGFydD1jKDIwMDgsMTEpLA0KICAgICBmcmVxdWVuY3kgPSAxMikNCmRhdG9zPWxpc3QoeD14KQ0KDQpgYGANCg0KYGBge3IsZWNobz1GQUxTRX0NCg0KIyMgVGhlIGFsZ29yaXRobSBpcyBkZXNpZ25lZCB0byBvYnRhaW4gdGhlIHByZWRpY3Rpb25zIG9mIGEgbW9udGhseSBzZXJpZXMNCmVuc2F5bzwtMTpsZW5ndGgoZGF0b3MpDQoNCiMjIEFwcGx5IGVhY2ggbWV0aG9kIHdpdGggYWxsIGl0cyBjb25maWd1cmF0aW9ucyBpbiBwZXJpb2QgVDENCm1ldG9kb3MuVDE8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMVtbal1dPC1jYWxjdWxvMShqKQ0KfQ0KDQojIyBPYnRhaW5zIGxvc3MgZnVuY3Rpb25zIGluIFQxIGFuZCBUMg0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMVtbal1dPC1jYWxjdWxvMihqKSANCn0NCmVycm9yZXNUMjwtbGlzdCgpIA0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgZXJyb3Jlc1QyW1tqXV08LWNhbGN1bG8zKGopDQp9DQoNCiMjIENhbGN1bGF0ZXMgUENBIGFuZCBNYW5oYXR0YW4gZGlzdGFuY2UgdG8gdGhlIG1ldGhvZCBhc3NvY2lhdGVkIHdpdGggdGhlIHNtYWxsZXN0IGVycm9yDQpyZXN1bHRhZG88LWxpc3QoKQ0KcGNhPC1yZXN1bHRhZG9bWzFdXTwtcmVzdWx0YWRvW1syXV08LXJlc3VsdGFkb1tbM11dPC1saXN0KCkNCmRpcy5hanVzdDwtcmVwKE5BLGxlbmd0aChlbnNheW8pKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgcmVzdWx0YWRvPC1jYWxjdWxvNC5tKGopIA0KICBpZihzdHJfZGV0ZWN0KHJlc3VsdGFkb1tbM11dW1tqXV1bMSwxXSwgIm1sZXhwIik9PVRSVUUpIHtkaXMuYWp1c3Rbal08LTF9IGVsc2UgDQogICAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGdhbW1hIik9PVRSVUUpIHtkaXMuYWp1c3Rbal08LTJ9IGVsc2Uge2Rpcy5hanVzdFtqXTwtM30NCn0NCg0KDQojIyBhbFBDQSBlc3RhYmxpc2hlcyB0aGUgcGVyY2VudGlsZXMgb2YgdGhlIGFkanVzdGVkIGRpc3RyaWJ1dGlvbiBhcyBjdXQtb2ZmIHBvaW50cyBmb3IgbWV0aG9kIHNlbGVjdGlvbg0KY290YTwtYygwLjA1LDAuNTAsIDAuOTUpIA0KcmVzdWx0YWRvW1s0XV08LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsgIA0KICByZXN1bHRhZG9bWzRdXVtbal1dPC1saXN0KCkNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGNvdGEpKSB7ICANCiAgICByZXN1bHRhZG88LWNhbGN1bG81KGosaSkNCiAgfQ0KfQ0KDQojIyBPYnRhaW4gdGhlIHByZWRpY3Rpb25zIGZvciB0aGUgcGVyaW9kIFQxK1QyDQptZXRvZG9zLlQxMjwtbGlzdCgpDQpmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KICBtZXRvZG9zLlQxMltbal1dPC1jYWxjdWxvNihqKQ0KfQ0KDQojIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgT2J0YWlucyB0aGUgcHJlZGljdGlvbnMgZm9yIFQzIGFjY29yZGluZyB0byB0aGUgcGVyY2VudGlsZSBjaG9zZW4gYnkgdGhlIHVzZXINCmo9MTtuX3A9MTI7cHJlZDU9cHJlZDUwPXByZWQ5NT1OVUxMO3l1PTENCmIxPC1yZXAoTkEsbl9wKQ0KIyNmb3IgKGogaW4gMTpsZW5ndGgoZW5zYXlvKSkgew0KZm9yIChpIGluIDE6bGVuZ3RoKGNvdGEpKSB7DQogIA0KICBuX3A8LTEyICNwcmVkaWNjaW9uZXMNCiAgZGF0PC1yZXN1bHRhZG9bWzRdXVtbal1dW1tpXV0NCiAgIyNNYXRyaXogcHJlZGljY2lvbmVzIG3DqXRvZG9zIHBhcmEgVDINCiAgcHJlZF9UMjwtcHJlZF9UMzwtbWF0cml4KE5BLG5yb3c9ZGltKGRhdClbMV0sbmNvbCA9IG5fcCkNCiAgZm9yIChrIGluIDE6ZGltKGRhdClbMV0pIHsNCiAgICBmb3IgKGtrIGluIDE6bGVuZ3RoKG1ldG9kb3MuVDFbW2pdXSkpIHsgDQogICAgICAjcHJpbnQobWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFuKQ0KICAgICAgaWYoZGF0W2ssMV09PWtrKXtwcmVkX1QyW2ssXTwtbWV0b2Rvcy5UMVtbal1dW1tra11dJG1lYW47IHByZWRfVDNbayxdPC1tZXRvZG9zLlQxMltbal1dW1tra11dJG1lYW59DQogICAgfQ0KICB9DQogIGZvciAoayBpbiAxOm5fcCkgew0KICAgIGIxW2tdPC1wcmVkX1QzWyxrXSUqJWRhdFssMTFdO2lmKGk9PTEpe3ByZWQ1W2tdPWIxW2tdfTsgaWYoaT09Mil7cHJlZDUwW2tdPWIxW2tdfTsgaWYoaT09Myl7cHJlZDk1W2tdPWIxW2tdfQ0KICB9DQp9DQpwZXJjZW50aWxlLjU9cm91bmQocHJlZDUsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjUwPXJvdW5kKHByZWQ1MCwgZGlnaXRzPTIpO3BlcmNlbnRpbGUuOTU9cm91bmQocHJlZDk1LCBkaWdpdHM9MikNCnJlc3VsdF9hbFBDQTwtY2JpbmQuZGF0YS5mcmFtZShwZXJjZW50aWxlLjUscGVyY2VudGlsZS41MCxwZXJjZW50aWxlLjk1KQ0KDQpybShkYXQpO3JtKGVycm9yZXNUMik7cm0obWV0b2Rvcy5UMSk7cm0obWV0b2Rvcy5UMTIpO3JtKHBjYSkNCnJtKHByZWRfVDIpO3JtKGIxKTsNCnJtKGNvdGEpO3JtKGRpcy5hanVzdCk7cm0oZW5zYXlvKTtybShpKTtybShqKTtybShrKTtybShraykNCnJtKG5fcCk7cm0oeXUpO3JtKHByZWRfVDMpO3JtKHJlc3VsdGFkbyk7cm0ocHJlZDUpO3JtKHByZWQ1MCk7cm0ocHJlZDk1KQ0Kcm0ocGVyY2VudGlsZS41KTtybShwZXJjZW50aWxlLjUwKTtybShwZXJjZW50aWxlLjk1KQ0KDQojIyBZb3UgY2FuIGNob29zZSB0aGUgcGVyY2VudGlsZSBjb2x1bW4gYW5kIGNvcnJlc3BvbmRpbmcgcHJlZGljdGlvbg0KcmVzdWx0X2FsUENBDQoNCmBgYA0KDQojIyMjIDUgYcOxb3MNCg0KQm9ub3MgYSA1IGHDsW9zIA0KDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDEyX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYNV9hbm9zIiAsIF0NCmRhdG9zIDwtIGRhdG9zWzk0Om5yb3coZGF0b3MpLTEyLCBdDQoNCng9dHMocmV2KGRhdG9zJFZhbG9yKSwNCiAgICAgc3RhcnQ9YygyMDA4LDEwKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQpkYXRvcz1saXN0KHg9eCkNCg0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0V9DQoNCiMjIFRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQgdG8gb2J0YWluIHRoZSBwcmVkaWN0aW9ucyBvZiBhIG1vbnRobHkgc2VyaWVzDQplbnNheW88LTE6bGVuZ3RoKGRhdG9zKQ0KDQojIyBBcHBseSBlYWNoIG1ldGhvZCB3aXRoIGFsbCBpdHMgY29uZmlndXJhdGlvbnMgaW4gcGVyaW9kIFQxDQptZXRvZG9zLlQxPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzEoaikNCn0NCg0KIyMgT2J0YWlucyBsb3NzIGZ1bmN0aW9ucyBpbiBUMSBhbmQgVDINCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzIoaikgDQp9DQplcnJvcmVzVDI8LWxpc3QoKSANCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIGVycm9yZXNUMltbal1dPC1jYWxjdWxvMyhqKQ0KfQ0KDQojIyBDYWxjdWxhdGVzIFBDQSBhbmQgTWFuaGF0dGFuIGRpc3RhbmNlIHRvIHRoZSBtZXRob2QgYXNzb2NpYXRlZCB3aXRoIHRoZSBzbWFsbGVzdCBlcnJvcg0KcmVzdWx0YWRvPC1saXN0KCkNCnBjYTwtcmVzdWx0YWRvW1sxXV08LXJlc3VsdGFkb1tbMl1dPC1yZXN1bHRhZG9bWzNdXTwtbGlzdCgpDQpkaXMuYWp1c3Q8LXJlcChOQSxsZW5ndGgoZW5zYXlvKSkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIHJlc3VsdGFkbzwtY2FsY3VsbzQubShqKSANCiAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGV4cCIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0xfSBlbHNlIA0KICAgIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxnYW1tYSIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0yfSBlbHNlIHtkaXMuYWp1c3Rbal08LTN9DQp9DQoNCg0KIyMgYWxQQ0EgZXN0YWJsaXNoZXMgdGhlIHBlcmNlbnRpbGVzIG9mIHRoZSBhZGp1c3RlZCBkaXN0cmlidXRpb24gYXMgY3V0LW9mZiBwb2ludHMgZm9yIG1ldGhvZCBzZWxlY3Rpb24NCmNvdGE8LWMoMC4wNSwwLjUwLCAwLjk1KSANCnJlc3VsdGFkb1tbNF1dPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7ICANCiAgcmVzdWx0YWRvW1s0XV1bW2pdXTwtbGlzdCgpDQogIGZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgeyAgDQogICAgcmVzdWx0YWRvPC1jYWxjdWxvNShqLGkpDQogIH0NCn0NCg0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHBlcmlvZCBUMStUMg0KbWV0b2Rvcy5UMTI8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMTJbW2pdXTwtY2FsY3VsbzYoaikNCn0NCg0KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIE9idGFpbnMgdGhlIHByZWRpY3Rpb25zIGZvciBUMyBhY2NvcmRpbmcgdG8gdGhlIHBlcmNlbnRpbGUgY2hvc2VuIGJ5IHRoZSB1c2VyDQpqPTE7bl9wPTEyO3ByZWQ1PXByZWQ1MD1wcmVkOTU9TlVMTDt5dT0xDQpiMTwtcmVwKE5BLG5fcCkNCiMjZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCmZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgew0KICANCiAgbl9wPC0xMiAjcHJlZGljY2lvbmVzDQogIGRhdDwtcmVzdWx0YWRvW1s0XV1bW2pdXVtbaV1dDQogICMjTWF0cml6IHByZWRpY2Npb25lcyBtw6l0b2RvcyBwYXJhIFQyDQogIHByZWRfVDI8LXByZWRfVDM8LW1hdHJpeChOQSxucm93PWRpbShkYXQpWzFdLG5jb2wgPSBuX3ApDQogIGZvciAoayBpbiAxOmRpbShkYXQpWzFdKSB7DQogICAgZm9yIChrayBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1tqXV0pKSB7IA0KICAgICAgI3ByaW50KG1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbikNCiAgICAgIGlmKGRhdFtrLDFdPT1rayl7cHJlZF9UMltrLF08LW1ldG9kb3MuVDFbW2pdXVtba2tdXSRtZWFuOyBwcmVkX1QzW2ssXTwtbWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFufQ0KICAgIH0NCiAgfQ0KICBmb3IgKGsgaW4gMTpuX3ApIHsNCiAgICBiMVtrXTwtcHJlZF9UM1ssa10lKiVkYXRbLDExXTtpZihpPT0xKXtwcmVkNVtrXT1iMVtrXX07IGlmKGk9PTIpe3ByZWQ1MFtrXT1iMVtrXX07IGlmKGk9PTMpe3ByZWQ5NVtrXT1iMVtrXX0NCiAgfQ0KfQ0KcGVyY2VudGlsZS41PXJvdW5kKHByZWQ1LCBkaWdpdHM9Mik7cGVyY2VudGlsZS41MD1yb3VuZChwcmVkNTAsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjk1PXJvdW5kKHByZWQ5NSwgZGlnaXRzPTIpDQpyZXN1bHRfYWxQQ0E8LWNiaW5kLmRhdGEuZnJhbWUocGVyY2VudGlsZS41LHBlcmNlbnRpbGUuNTAscGVyY2VudGlsZS45NSkNCg0Kcm0oZGF0KTtybShlcnJvcmVzVDIpO3JtKG1ldG9kb3MuVDEpO3JtKG1ldG9kb3MuVDEyKTtybShwY2EpDQpybShwcmVkX1QyKTtybShiMSk7DQpybShjb3RhKTtybShkaXMuYWp1c3QpO3JtKGVuc2F5byk7cm0oaSk7cm0oaik7cm0oayk7cm0oa2spDQpybShuX3ApO3JtKHl1KTtybShwcmVkX1QzKTtybShyZXN1bHRhZG8pO3JtKHByZWQ1KTtybShwcmVkNTApO3JtKHByZWQ5NSkNCnJtKHBlcmNlbnRpbGUuNSk7cm0ocGVyY2VudGlsZS41MCk7cm0ocGVyY2VudGlsZS45NSkNCg0KIyMgWW91IGNhbiBjaG9vc2UgdGhlIHBlcmNlbnRpbGUgY29sdW1uIGFuZCBjb3JyZXNwb25kaW5nIHByZWRpY3Rpb24NCnJlc3VsdF9hbFBDQQ0KDQpgYGANCg0KIyMjIyAxMCBhw7Fvcw0KDQpCb25vcyBhIDEwIGHDsW9zIA0KDQoNCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQ0KZGF0b3M9cmVhZC5jc3YyKCJEYXRvc19jc3YuY3N2IiwgaGVhZGVyID0gVFJVRSkNCmRhdG9zPC0gcGl2b3RfbG9uZ2VyKGRhdG9zLCAtVGllbXBvLCBuYW1lc190byA9ICJQZXJpb2RvIiwgdmFsdWVzX3RvID0gIlZhbG9yIikNCiNGaWx0cm8gcG9yIDEyX21lc2VzDQpkYXRvcyA8LSBkYXRvc1tkYXRvcyRQZXJpb2RvID09ICJYMTBfYW5vcyIgLCBdDQpkYXRvcyA8LSBkYXRvc1s5ODpucm93KGRhdG9zKS0xMiwgXQ0KDQp4PXRzKHJldihkYXRvcyRWYWxvciksDQogICAgIHN0YXJ0PWMoMjAwOSwyKSwNCiAgICAgZnJlcXVlbmN5ID0gMTIpDQpkYXRvcz1saXN0KHg9eCkNCg0KYGBgDQoNCmBgYHtyLGVjaG89RkFMU0V9DQoNCiMjIFRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQgdG8gb2J0YWluIHRoZSBwcmVkaWN0aW9ucyBvZiBhIG1vbnRobHkgc2VyaWVzDQplbnNheW88LTE6bGVuZ3RoKGRhdG9zKQ0KDQojIyBBcHBseSBlYWNoIG1ldGhvZCB3aXRoIGFsbCBpdHMgY29uZmlndXJhdGlvbnMgaW4gcGVyaW9kIFQxDQptZXRvZG9zLlQxPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzEoaikNCn0NCg0KIyMgT2J0YWlucyBsb3NzIGZ1bmN0aW9ucyBpbiBUMSBhbmQgVDINCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIG1ldG9kb3MuVDFbW2pdXTwtY2FsY3VsbzIoaikgDQp9DQplcnJvcmVzVDI8LWxpc3QoKSANCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIGVycm9yZXNUMltbal1dPC1jYWxjdWxvMyhqKQ0KfQ0KDQojIyBDYWxjdWxhdGVzIFBDQSBhbmQgTWFuaGF0dGFuIGRpc3RhbmNlIHRvIHRoZSBtZXRob2QgYXNzb2NpYXRlZCB3aXRoIHRoZSBzbWFsbGVzdCBlcnJvcg0KcmVzdWx0YWRvPC1saXN0KCkNCnBjYTwtcmVzdWx0YWRvW1sxXV08LXJlc3VsdGFkb1tbMl1dPC1yZXN1bHRhZG9bWzNdXTwtbGlzdCgpDQpkaXMuYWp1c3Q8LXJlcChOQSxsZW5ndGgoZW5zYXlvKSkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7DQogIHJlc3VsdGFkbzwtY2FsY3VsbzQubShqKSANCiAgaWYoc3RyX2RldGVjdChyZXN1bHRhZG9bWzNdXVtbal1dWzEsMV0sICJtbGV4cCIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0xfSBlbHNlIA0KICAgIGlmKHN0cl9kZXRlY3QocmVzdWx0YWRvW1szXV1bW2pdXVsxLDFdLCAibWxnYW1tYSIpPT1UUlVFKSB7ZGlzLmFqdXN0W2pdPC0yfSBlbHNlIHtkaXMuYWp1c3Rbal08LTN9DQp9DQoNCg0KIyMgYWxQQ0EgZXN0YWJsaXNoZXMgdGhlIHBlcmNlbnRpbGVzIG9mIHRoZSBhZGp1c3RlZCBkaXN0cmlidXRpb24gYXMgY3V0LW9mZiBwb2ludHMgZm9yIG1ldGhvZCBzZWxlY3Rpb24NCmNvdGE8LWMoMC4wNSwwLjUwLCAwLjk1KSANCnJlc3VsdGFkb1tbNF1dPC1saXN0KCkNCmZvciAoaiBpbiAxOmxlbmd0aChlbnNheW8pKSB7ICANCiAgcmVzdWx0YWRvW1s0XV1bW2pdXTwtbGlzdCgpDQogIGZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgeyAgDQogICAgcmVzdWx0YWRvPC1jYWxjdWxvNShqLGkpDQogIH0NCn0NCg0KIyMgT2J0YWluIHRoZSBwcmVkaWN0aW9ucyBmb3IgdGhlIHBlcmlvZCBUMStUMg0KbWV0b2Rvcy5UMTI8LWxpc3QoKQ0KZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCiAgbWV0b2Rvcy5UMTJbW2pdXTwtY2FsY3VsbzYoaikNCn0NCg0KIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMjIE9idGFpbiB0aGUgcHJlZGljdGlvbnMgZm9yIE9idGFpbnMgdGhlIHByZWRpY3Rpb25zIGZvciBUMyBhY2NvcmRpbmcgdG8gdGhlIHBlcmNlbnRpbGUgY2hvc2VuIGJ5IHRoZSB1c2VyDQpqPTE7bl9wPTEyO3ByZWQ1PXByZWQ1MD1wcmVkOTU9TlVMTDt5dT0xDQpiMTwtcmVwKE5BLG5fcCkNCiMjZm9yIChqIGluIDE6bGVuZ3RoKGVuc2F5bykpIHsNCmZvciAoaSBpbiAxOmxlbmd0aChjb3RhKSkgew0KICANCiAgbl9wPC0xMiAjcHJlZGljY2lvbmVzDQogIGRhdDwtcmVzdWx0YWRvW1s0XV1bW2pdXVtbaV1dDQogICMjTWF0cml6IHByZWRpY2Npb25lcyBtw6l0b2RvcyBwYXJhIFQyDQogIHByZWRfVDI8LXByZWRfVDM8LW1hdHJpeChOQSxucm93PWRpbShkYXQpWzFdLG5jb2wgPSBuX3ApDQogIGZvciAoayBpbiAxOmRpbShkYXQpWzFdKSB7DQogICAgZm9yIChrayBpbiAxOmxlbmd0aChtZXRvZG9zLlQxW1tqXV0pKSB7IA0KICAgICAgI3ByaW50KG1ldG9kb3MuVDEyW1tqXV1bW2trXV0kbWVhbikNCiAgICAgIGlmKGRhdFtrLDFdPT1rayl7cHJlZF9UMltrLF08LW1ldG9kb3MuVDFbW2pdXVtba2tdXSRtZWFuOyBwcmVkX1QzW2ssXTwtbWV0b2Rvcy5UMTJbW2pdXVtba2tdXSRtZWFufQ0KICAgIH0NCiAgfQ0KICBmb3IgKGsgaW4gMTpuX3ApIHsNCiAgICBiMVtrXTwtcHJlZF9UM1ssa10lKiVkYXRbLDExXTtpZihpPT0xKXtwcmVkNVtrXT1iMVtrXX07IGlmKGk9PTIpe3ByZWQ1MFtrXT1iMVtrXX07IGlmKGk9PTMpe3ByZWQ5NVtrXT1iMVtrXX0NCiAgfQ0KfQ0KcGVyY2VudGlsZS41PXJvdW5kKHByZWQ1LCBkaWdpdHM9Mik7cGVyY2VudGlsZS41MD1yb3VuZChwcmVkNTAsIGRpZ2l0cz0yKTtwZXJjZW50aWxlLjk1PXJvdW5kKHByZWQ5NSwgZGlnaXRzPTIpDQpyZXN1bHRfYWxQQ0E8LWNiaW5kLmRhdGEuZnJhbWUocGVyY2VudGlsZS41LHBlcmNlbnRpbGUuNTAscGVyY2VudGlsZS45NSkNCg0Kcm0oZGF0KTtybShlcnJvcmVzVDIpO3JtKG1ldG9kb3MuVDEpO3JtKG1ldG9kb3MuVDEyKTtybShwY2EpDQpybShwcmVkX1QyKTtybShiMSk7DQpybShjb3RhKTtybShkaXMuYWp1c3QpO3JtKGVuc2F5byk7cm0oaSk7cm0oaik7cm0oayk7cm0oa2spDQpybShuX3ApO3JtKHl1KTtybShwcmVkX1QzKTtybShyZXN1bHRhZG8pO3JtKHByZWQ1KTtybShwcmVkNTApO3JtKHByZWQ5NSkNCnJtKHBlcmNlbnRpbGUuNSk7cm0ocGVyY2VudGlsZS41MCk7cm0ocGVyY2VudGlsZS45NSkNCg0KIyMgWW91IGNhbiBjaG9vc2UgdGhlIHBlcmNlbnRpbGUgY29sdW1uIGFuZCBjb3JyZXNwb25kaW5nIHByZWRpY3Rpb24NCnJlc3VsdF9hbFBDQQ0KDQpgYGA=