library(readxl)
library(dplyr)
library(ggplot2)
library(forecast)
install.packages("forecast")
install.packages("dplyr")
install.packages("ggplot2")
install.packages("dplyr")
install.packages("lubridate")
install.packages("tidyr")
install.packages("purrr")
install.packages("writexl")
head(Historial_2020_al_2024)
names(Historial_2020_al_2024)
 [1] "Producto"        "Nombre"          "BienServicio"    "Validacion"      "Fecha"           "Proveedor"       "NombreProveedor" "Marca"          
 [9] "NombreMarca"     "Cantidad"        "Unidadcompra"    "CostoBruto"      "Monto"           "Bodegadestino"   "Bodega"          "Orden"          
[17] "Usuario"         "Factura"         "FechaFactura"    "Estadoorden"    
str(Historial_2020_al_2024)
tibble [54,651 × 20] (S3: tbl_df/tbl/data.frame)
 $ Producto       : num [1:54651] 221064 221063 221062 221103 221072 ...
 $ Nombre         : chr [1:54651] "LECHE ENTERA" "LECHE DESLACTOSADA" "LECHE DESCREMADA" "QUESO PARMESANO" ...
 $ BienServicio   : chr [1:54651] "B" "B" "B" "B" ...
 $ Validacion     : num [1:54651] 22351 22351 22351 22351 22351 ...
 $ Fecha          : POSIXct[1:54651], format: "2020-01-04" "2020-01-04" "2020-01-04" "2020-01-04" ...
 $ Proveedor      : chr [1:54651] "26157020" "26157020" "26157020" "26157020" ...
 $ NombreProveedor: chr [1:54651] "OPCIONES SALUDABLES, S.A" "OPCIONES SALUDABLES, S.A" "OPCIONES SALUDABLES, S.A" "OPCIONES SALUDABLES, S.A" ...
 $ Marca          : num [1:54651] NA NA NA NA NA NA NA 358 54 30 ...
 $ NombreMarca    : chr [1:54651] NA NA NA NA ...
 $ Cantidad       : num [1:54651] 72 3 3 2 3 1 1 1 5 4 ...
 $ Unidadcompra   : chr [1:54651] "UNIDAD" "UNIDAD" "UNIDAD" "UNIDAD" ...
 $ CostoBruto     : num [1:54651] 9.45 9.45 9.45 140 98 95 75 1200 125 55.7 ...
 $ Monto          : num [1:54651] 680.4 28.4 28.4 280 294 ...
 $ Bodegadestino  : num [1:54651] 206 206 206 206 206 206 206 101 101 101 ...
 $ Bodega         : chr [1:54651] "Cafeteria" "Cafeteria" "Cafeteria" "Cafeteria" ...
 $ Orden          : num [1:54651] 23569 23569 23569 23569 23569 ...
 $ Usuario        : chr [1:54651] "KAREN" "KAREN" "KAREN" "KAREN" ...
 $ Factura        : chr [1:54651] "B-140991" "B-140991" "B-140991" "B-140991" ...
 $ FechaFactura   : POSIXct[1:54651], format: "2020-01-04" "2020-01-04" "2020-01-04" "2020-01-04" ...
 $ Estadoorden    : chr [1:54651] "S" "S" "S" "S" ...
summary(Historial_2020_al_2024)
    Producto         Nombre          BienServicio         Validacion        Fecha                        Proveedor         NombreProveedor        Marca      
 Min.   :110002   Length:54651       Length:54651       Min.   :    0   Min.   :2020-01-04 00:00:00.0   Length:54651       Length:54651       Min.   :  1.0  
 1st Qu.:112773   Class :character   Class :character   1st Qu.:36117   1st Qu.:2021-05-20 00:00:00.0   Class :character   Class :character   1st Qu.: 34.0  
 Median :211023   Mode  :character   Mode  :character   Median :50763   Median :2022-08-03 00:00:00.0   Mode  :character   Mode  :character   Median :130.0  
 Mean   :184299                                         Mean   :49468   Mean   :2022-07-29 06:10:50.5                                         Mean   :194.8  
 3rd Qu.:220447                                         3rd Qu.:62942   3rd Qu.:2023-11-07 00:00:00.0                                         3rd Qu.:262.0  
 Max.   :330015                                         Max.   :75396   Max.   :2024-12-30 00:00:00.0                                         Max.   :784.0  
                                                                        NA's   :4544                                                          NA's   :10956  
 NombreMarca           Cantidad       Unidadcompra         CostoBruto            Monto         Bodegadestino      Bodega              Orden      
 Length:54651       Min.   :    1.0   Length:54651       Min.   :      0.0   Min.   :      0   Min.   :101.0   Length:54651       Min.   :23516  
 Class :character   1st Qu.:    2.0   Class :character   1st Qu.:     14.0   1st Qu.:    146   1st Qu.:101.0   Class :character   1st Qu.:32956  
 Mode  :character   Median :    6.0   Mode  :character   Median :     54.9   Median :    455   Median :101.0   Mode  :character   Median :42258  
                    Mean   :  139.5                      Mean   :    624.8   Mean   :   2345   Mean   :134.3                      Mean   :41852  
                    3rd Qu.:   30.0                      3rd Qu.:    247.5   3rd Qu.:   1600   3rd Qu.:201.0                      3rd Qu.:50607  
                    Max.   :50000.0                      Max.   :1721216.0   Max.   :1721216   Max.   :208.0                      Max.   :59699  
                                                                                                                                                 
   Usuario            Factura           FechaFactura                   Estadoorden       
 Length:54651       Length:54651       Min.   :2020-01-04 00:00:00.0   Length:54651      
 Class :character   Class :character   1st Qu.:2021-06-17 00:00:00.0   Class :character  
 Mode  :character   Mode  :character   Median :2022-09-02 00:00:00.0   Mode  :character  
                                       Mean   :2022-08-19 13:29:59.7                     
                                       3rd Qu.:2023-11-10 00:00:00.0                     
                                       Max.   :2024-12-30 00:00:00.0                     
                                                                                         
library(dplyr)
library(lubridate)
Aviso: package ‘lubridate’ was built under R version 4.4.3
Adjuntando el paquete: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
# Creamos columna Año-Mes
Historial_mensual <- Historial_2020_al_2024 %>%
  mutate(AnioMes = floor_date(Fecha, unit = "month")) %>% 
  group_by(AnioMes, Nombre, Bodega) %>%
  summarise(Compras = sum(Cantidad), .groups = "drop")

# Ver ejemplo de los datos resumidos
head(Historial_mensual)
NA
NA
library(dplyr)
library(purrr)
library(lubridate)
library(forecast)
library(tibble)

# Suponemos que esta es tu tabla resumida mensual por ítem y bodega:
# Historial_mensual

# Paso clave: dividir en una lista por grupo antes del map
lista_por_grupo <- Historial_mensual %>%
  group_by(Nombre, Bodega) %>%
  group_split()

# Aplicar modelo ARIMA por grupo
proyecciones <- map_df(lista_por_grupo, function(df) {
  if (nrow(df) < 2 || any(is.na(df$AnioMes)) || any(is.na(df$Compras))) {
    return(NULL)
  }

  nombre_item <- unique(df$Nombre)
  bodega_id <- unique(df$Bodega)

  ts_data <- ts(df$Compras, start = c(year(min(df$AnioMes)), month(min(df$AnioMes))), frequency = 12)

  modelo <- auto.arima(ts_data)
  forecast_2025 <- forecast(modelo, h = 12)

  fechas_proj <- seq(from = as.Date("2025-01-01"), by = "month", length.out = 12)

  tibble(
    Nombre = nombre_item,
    Bodega = bodega_id,
    Mes = fechas_proj,
    Compra_Prevista = as.numeric(forecast_2025$mean)
  )
})
head(proyecciones)
NA
proyecciones_limpias <- proyecciones %>%
  distinct(Nombre, Bodega, Mes, .keep_all = TRUE)
nrow(proyecciones_limpias)
[1] 28284
View(proyecciones_limpias)        # Abre la tabla completa en pestaña
# Cambia los valores por el código o nombre del ítem que quieres ver
proyecciones_limpias %>%
  filter(Nombre == "ROCEPHIN IV 1G LIOFILIZADO VIAL",
         Bodega == "FARMACIA")

library(ggplot2)
library(dplyr)

# 1. Datos históricos del producto
historico <- Historial_mensual %>%
  filter(Nombre == "ROCEPHIN IV 1G LIOFILIZADO VIAL", Bodega == "FARMACIA") %>%
  mutate(Tipo = "Histórico", Fecha = AnioMes) %>%
  select(Fecha, Compras, Tipo)

# 2. Proyección del mismo producto
futuro <- proyecciones_limpias %>%
  filter(Nombre == "ROCEPHIN IV 1G LIOFILIZADO VIAL", Bodega == "FARMACIA") %>%
  mutate(Tipo = "Proyección", Fecha = Mes) %>%
  rename(Compras = Compra_Prevista) %>%
  select(Fecha, Compras, Tipo)

# 3. Unir ambos datasets
todo <- bind_rows(historico, futuro)

# 4. Graficar
ggplot(todo, aes(x = Fecha, y = Compras, color = Tipo)) +
  geom_line(size = 1.2) +
  geom_point(size = 2) +
  labs(
    title = "Compras históricas + proyección mensual (2025)",
    subtitle = "Producto: ROCEPHIN IV 1G LIOFILIZADO VIAL | Bodega: FARMACIA",
    x = "Mes",
    y = "Cantidad comprada / proyectada",
    color = "Serie"
  ) +
  theme_minimal()
Aviso: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

facet_wrap

top5_productos <- Historial_mensual %>%
  group_by(Nombre) %>%
  summarise(Total = sum(Compras), .groups = "drop") %>%
  arrange(desc(Total)) %>%
  slice_head(n = 5) %>%
  pull(Nombre)
# HISTÓRICO (solo para los top 5)
historico <- Historial_mensual %>%
  filter(Nombre %in% top5_productos, Bodega == "FARMACIA") %>%
  mutate(Tipo = "Histórico", Fecha = AnioMes) %>%
  select(Nombre, Fecha, Compras, Tipo)

# PROYECCIÓN
futuro <- proyecciones_limpias %>%
  filter(Nombre %in% top5_productos, Bodega == "FARMACIA") %>%
  mutate(Tipo = "Proyección", Fecha = Mes) %>%
  rename(Compras = Compra_Prevista) %>%
  select(Nombre, Fecha, Compras, Tipo)

# COMBINAR TODO
todo <- bind_rows(historico, futuro)

# Recalcular los 5 productos más comprados
top5_productos <- Historial_mensual %>%
  group_by(Nombre) %>%
  summarise(Total = sum(Compras), .groups = "drop") %>%
  arrange(desc(Total)) %>%
  slice_head(n = 5) %>%
  pull(Nombre)

# Datos históricos
historico <- Historial_mensual %>%
  filter(Nombre %in% top5_productos, Bodega == "FARMACIA") %>%
  mutate(Tipo = "Histórico", Fecha = AnioMes) %>%
  select(Nombre, Fecha, Compras, Tipo)

# Proyecciones
futuro <- proyecciones_limpias %>%
  filter(Nombre %in% top5_productos, Bodega == "FARMACIA") %>%
  mutate(Tipo = "Proyección", Fecha = Mes) %>%
  rename(Compras = Compra_Prevista) %>%
  select(Nombre, Fecha, Compras, Tipo)

# Unir todo
todo <- bind_rows(historico, futuro)
# Filtrar datos válidos
todo_filtrado <- todo %>%
  filter(!is.na(Compras))

# Verificar productos con proyección
table(todo_filtrado$Tipo, todo_filtrado$Nombre)
           
            GUANTE NITRILO MEDIANO NO ESTERIL MASCARILLA QUIRURGICA CON ELASTICO
  Histórico                                39                                 42

#RETOMANDo

producto_datos <- Historial_mensual %>%
  filter(Nombre == "PERENTEROL 250 MG SOBRE", Bodega == "FARMACIA")
library(forecast)

# Verificamos si hay suficientes datos
if (nrow(producto_datos) >= 2) {
  ts_perenterol <- ts(producto_datos$Compras,
                      start = c(year(min(producto_datos$AnioMes)), month(min(producto_datos$AnioMes))),
                      frequency = 12)

  modelo <- auto.arima(ts_perenterol)
  proyeccion <- forecast(modelo, h = 12)

  fechas_2025 <- seq(as.Date("2025-01-01"), by = "month", length.out = 12)

  proyeccion_perenterol <- tibble(
    Mes = fechas_2025,
    Compra_Prevista = as.numeric(proyeccion$mean)
  )
  
  print(proyeccion_perenterol)
} else {
  print("No hay suficientes datos históricos para proyectar este producto.")
}
Error en if (start > end) stop("'start' cannot be after 'end'"): 
  valor ausente donde TRUE/FALSE es necesario
summary(producto_datos$AnioMes)
                      Min.                    1st Qu.                     Median                       Mean                    3rd Qu. 
"2021-06-01 00:00:00.0000" "2023-03-31 12:00:00.0000" "2023-12-01 00:00:00.0000" "2023-09-18 11:22:06.3157" "2024-05-16 12:00:00.0000" 
                      Max.                       NA's 
"2024-11-01 00:00:00.0000"                        "1" 
if (nrow(producto_datos) >= 2 && !any(is.na(producto_datos$AnioMes))) {
  
  ts_perenterol <- ts(producto_datos$Compras,
                      start = c(year(min(producto_datos$AnioMes)),
                                month(min(producto_datos$AnioMes))),
                      frequency = 12)

  modelo <- auto.arima(ts_perenterol)
  proyeccion <- forecast(modelo, h = 12)

  fechas_2025 <- seq(as.Date("2025-01-01"), by = "month", length.out = 12)

  proyeccion_perenterol <- tibble(
    Mes = fechas_2025,
    Compra_Prevista = as.numeric(proyeccion$mean)
  )

  print(proyeccion_perenterol)

} else {
  print("⚠️ No hay suficientes datos históricos o las fechas están incompletas.")
}
[1] "⚠️ No hay suficientes datos históricos o las fechas están incompletas."
Historial_mensual <- Historial_2020_al_2024 %>%
  mutate(AnioMes = lubridate::floor_date(FechaFactura, unit = "month")) %>%
  group_by(Nombre, Bodega, AnioMes) %>%
  summarise(Compras = sum(Cantidad), .groups = "drop")
summary(Historial_mensual$AnioMes)
                      Min.                    1st Qu.                     Median                       Mean                    3rd Qu. 
"2020-01-01 00:00:00.0000" "2021-06-01 00:00:00.0000" "2022-09-01 00:00:00.0000" "2022-08-20 03:57:35.5056" "2023-11-01 00:00:00.0000" 
                      Max. 
"2024-12-01 00:00:00.0000" 
any(is.na(Historial_mensual$AnioMes))  # Debe dar FALSE
[1] FALSE
library(dplyr)
library(lubridate)

Historial_mensual <- Historial_2020_al_2024 %>%
  mutate(AnioMes = floor_date(FechaFactura, unit = "month")) %>%
  group_by(Nombre, Bodega, AnioMes) %>%
  summarise(Compras = sum(Cantidad), .groups = "drop")
producto_datos <- Historial_mensual %>%
  filter(Nombre == "PERENTEROL 250 MG SOBRE", Bodega == "FARMACIA")
library(ggplot2)

# Crear el histórico en formato gráfico
historico <- producto_datos %>%
  mutate(Fecha = AnioMes, Tipo = "Histórico") %>%
  select(Fecha, Compras, Tipo)

# Crear la proyección
futuro <- proyeccion_perenterol %>%
  mutate(Fecha = Mes, Tipo = "Proyección") %>%
  rename(Compras = Compra_Prevista) %>%
  select(Fecha, Compras, Tipo)

# Unir ambos
todo_perenterol <- bind_rows(historico, futuro)

# Graficar
ggplot(todo_perenterol, aes(x = Fecha, y = Compras, color = Tipo)) +
  geom_line(size = 1.2) +
  geom_point(size = 2) +
  labs(
    title = "Histórico + Proyección 2025",
    subtitle = "Producto: PERENTEROL 250 MG SOBRE | Bodega: FARMACIA",
    x = "Mes",
    y = "Cantidad"
  ) +
  scale_color_manual(values = c("Histórico" = "firebrick", "Proyección" = "steelblue")) +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

#Hay más factores que pueden intervenir, estas compras muchas veces son ejecutadas en farmacias externas en lugar de mantener un estiado de 1 a 2 cajas mensuales en stock.

PARA PAPEL MIXTO

Historial_mensual <- Historial_2020_al_2024 %>%
  mutate(AnioMes = floor_date(FechaFactura, unit = "month")) %>%
  group_by(Nombre, Bodega, AnioMes) %>%
  summarise(Compras = sum(Cantidad), .groups = "drop")
producto_datos <- Historial_mensual %>%
  filter(Nombre == "PAPEL MIXTO ROLLO DE 15 X 200 MTS", Bodega == "FARMACIA")
ggplot(producto_datos, aes(x = AnioMes, y = Compras)) +
  geom_line(color = "firebrick", size = 1.2) +
  geom_point(color = "firebrick", size = 2) +
  labs(
    title = "Histórico de compras mensuales",
    subtitle = "PAPEL MIXTO ROLLO DE 15 X 200 MTS | Bodega: FARMACIA",
    x = "Mes", y = "Cantidad"
  ) +
  theme_minimal()


if (nrow(producto_datos) >= 2 && !any(is.na(producto_datos$AnioMes))) {
  
  ts_data <- ts(producto_datos$Compras,
                start = c(year(min(producto_datos$AnioMes)), month(min(producto_datos$AnioMes))),
                frequency = 12)

  modelo <- auto.arima(ts_data)
  proyeccion <- forecast(modelo, h = 12)

  fechas_2025 <- seq(as.Date("2025-01-01"), by = "month", length.out = 12)

  proyeccion_papel <- tibble(
    Mes = fechas_2025,
    Compra_Prevista = as.numeric(proyeccion$mean)
  )

  print(proyeccion_papel)

} else {
  print("⚠️ No hay suficientes datos para proyectar.")
}
NA
# Preparar histórico
historico <- producto_datos %>%
  mutate(Fecha = AnioMes, Tipo = "Histórico") %>%
  select(Fecha, Compras, Tipo)

# Preparar proyección
futuro <- proyeccion_papel %>%
  mutate(Fecha = Mes, Tipo = "Proyección") %>%
  rename(Compras = Compra_Prevista) %>%
  select(Fecha, Compras, Tipo)

# Unir
todo <- bind_rows(historico, futuro)

# Graficar
ggplot(todo, aes(x = Fecha, y = Compras, color = Tipo)) +
  geom_line(size = 1.2) +
  geom_point(size = 2) +
  scale_color_manual(values = c("Histórico" = "firebrick", "Proyección" = "steelblue")) +
  labs(
    title = "Histórico + Proyección 2025",
    subtitle = "PAPEL MIXTO ROLLO DE 15 X 200 MTS | Bodega: FARMACIA",
    x = "Mes", y = "Cantidad"
  ) +
  theme_minimal(base_size = 12) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

library(dplyr)
library(lubridate)
library(forecast)
library(ggplot2)
library(purrr)
library(writexl)

# Paso 1: Crear historial mensual confiable
Historial_mensual <- Historial_2020_al_2024 %>%
  mutate(AnioMes = floor_date(FechaFactura, unit = "month")) %>%
  group_by(Nombre, Bodega, AnioMes) %>%
  summarise(Compras = sum(Cantidad), .groups = "drop")

# Paso 2 al 5: Automatizar todo por producto y bodega
proyecciones_completas <- Historial_mensual %>%
  group_by(Nombre, Bodega) %>%
  group_split() %>%
  map_df(function(df) {
    nombre_item <- unique(df$Nombre)
    bodega <- unique(df$Bodega)

    if (nrow(df) < 2 || any(is.na(df$AnioMes))) return(NULL)

    # Serie temporal
    ts_data <- ts(df$Compras,
                  start = c(year(min(df$AnioMes)), month(min(df$AnioMes))),
                  frequency = 12)

    modelo <- tryCatch(auto.arima(ts_data), error = function(e) return(NULL))
    if (is.null(modelo)) return(NULL)

    forecast_result <- forecast(modelo, h = 12)
    fechas_proj <- seq(as.Date("2025-01-01"), by = "month", length.out = 12)

    tibble(
      Nombre = nombre_item,
      Bodega = bodega,
      Mes = fechas_proj,
      Compra_Prevista = as.numeric(forecast_result$mean)
    )
  })
# Crear carpeta si no existe
if (!dir.exists("graficos_productos")) dir.create("graficos_productos")

# Crear gráfico por producto
productos_unicos <- unique(proyecciones_completas$Nombre)

walk(productos_unicos, function(producto) {
  historial <- Historial_mensual %>%
    filter(Nombre == producto) %>%
    mutate(Tipo = "Histórico", Fecha = AnioMes)

  proy <- proyecciones_completas %>%
    filter(Nombre == producto) %>%
    mutate(Tipo = "Proyección", Fecha = Mes) %>%
    rename(Compras = Compra_Prevista)

  todo <- bind_rows(historial, proy)

  grafico <- ggplot(todo, aes(x = Fecha, y = Compras, color = Tipo)) +
    geom_line(size = 1.2) +
    geom_point(size = 2) +
    labs(
      title = paste("Histórico + Proyección 2025\nProducto:", producto),
      x = "Mes", y = "Cantidad"
    ) +
    scale_color_manual(values = c("Histórico" = "firebrick", "Proyección" = "steelblue")) +
    theme_minimal(base_size = 11) +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))

  ggsave(filename = paste0("graficos_productos/", gsub("[^a-zA-Z0-9]", "_", producto), ".png"),
         plot = grafico, width = 9, height = 5)
})
LS0tDQp0aXRsZTogIkZvcmVjYXN0aW5nIENvbXByYXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZm9yZWNhc3QpDQpgYGANCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygiZm9yZWNhc3QiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCg0KYGBgDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KYGBgDQoNCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQpgYGANCg0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCJsdWJyaWRhdGUiKQ0KYGBgDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXIiKQ0KaW5zdGFsbC5wYWNrYWdlcygicHVycnIiKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoIndyaXRleGwiKQ0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KaGVhZChIaXN0b3JpYWxfMjAyMF9hbF8yMDI0KQ0KYGBgDQoNCmBgYHtyfQ0KbmFtZXMoSGlzdG9yaWFsXzIwMjBfYWxfMjAyNCkNCmBgYA0KYGBge3J9DQpzdHIoSGlzdG9yaWFsXzIwMjBfYWxfMjAyNCkNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkoSGlzdG9yaWFsXzIwMjBfYWxfMjAyNCkNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCg0KIyBDcmVhbW9zIGNvbHVtbmEgQcOxby1NZXMNCkhpc3RvcmlhbF9tZW5zdWFsIDwtIEhpc3RvcmlhbF8yMDIwX2FsXzIwMjQgJT4lDQogIG11dGF0ZShBbmlvTWVzID0gZmxvb3JfZGF0ZShGZWNoYSwgdW5pdCA9ICJtb250aCIpKSAlPiUgDQogIGdyb3VwX2J5KEFuaW9NZXMsIE5vbWJyZSwgQm9kZWdhKSAlPiUNCiAgc3VtbWFyaXNlKENvbXByYXMgPSBzdW0oQ2FudGlkYWQpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojIFZlciBlamVtcGxvIGRlIGxvcyBkYXRvcyByZXN1bWlkb3MNCmhlYWQoSGlzdG9yaWFsX21lbnN1YWwpDQoNCg0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShwdXJycikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkodGliYmxlKQ0KDQojIFN1cG9uZW1vcyBxdWUgZXN0YSBlcyB0dSB0YWJsYSByZXN1bWlkYSBtZW5zdWFsIHBvciDDrXRlbSB5IGJvZGVnYToNCiMgSGlzdG9yaWFsX21lbnN1YWwNCg0KIyBQYXNvIGNsYXZlOiBkaXZpZGlyIGVuIHVuYSBsaXN0YSBwb3IgZ3J1cG8gYW50ZXMgZGVsIG1hcA0KbGlzdGFfcG9yX2dydXBvIDwtIEhpc3RvcmlhbF9tZW5zdWFsICU+JQ0KICBncm91cF9ieShOb21icmUsIEJvZGVnYSkgJT4lDQogIGdyb3VwX3NwbGl0KCkNCg0KIyBBcGxpY2FyIG1vZGVsbyBBUklNQSBwb3IgZ3J1cG8NCnByb3llY2Npb25lcyA8LSBtYXBfZGYobGlzdGFfcG9yX2dydXBvLCBmdW5jdGlvbihkZikgew0KICBpZiAobnJvdyhkZikgPCAyIHx8IGFueShpcy5uYShkZiRBbmlvTWVzKSkgfHwgYW55KGlzLm5hKGRmJENvbXByYXMpKSkgew0KICAgIHJldHVybihOVUxMKQ0KICB9DQoNCiAgbm9tYnJlX2l0ZW0gPC0gdW5pcXVlKGRmJE5vbWJyZSkNCiAgYm9kZWdhX2lkIDwtIHVuaXF1ZShkZiRCb2RlZ2EpDQoNCiAgdHNfZGF0YSA8LSB0cyhkZiRDb21wcmFzLCBzdGFydCA9IGMoeWVhcihtaW4oZGYkQW5pb01lcykpLCBtb250aChtaW4oZGYkQW5pb01lcykpKSwgZnJlcXVlbmN5ID0gMTIpDQoNCiAgbW9kZWxvIDwtIGF1dG8uYXJpbWEodHNfZGF0YSkNCiAgZm9yZWNhc3RfMjAyNSA8LSBmb3JlY2FzdChtb2RlbG8sIGggPSAxMikNCg0KICBmZWNoYXNfcHJvaiA8LSBzZXEoZnJvbSA9IGFzLkRhdGUoIjIwMjUtMDEtMDEiKSwgYnkgPSAibW9udGgiLCBsZW5ndGgub3V0ID0gMTIpDQoNCiAgdGliYmxlKA0KICAgIE5vbWJyZSA9IG5vbWJyZV9pdGVtLA0KICAgIEJvZGVnYSA9IGJvZGVnYV9pZCwNCiAgICBNZXMgPSBmZWNoYXNfcHJvaiwNCiAgICBDb21wcmFfUHJldmlzdGEgPSBhcy5udW1lcmljKGZvcmVjYXN0XzIwMjUkbWVhbikNCiAgKQ0KfSkNCg0KDQpgYGANCg0KYGBge3J9DQpoZWFkKHByb3llY2Npb25lcykNCg0KYGBgDQoNCg0KYGBge3J9DQpwcm95ZWNjaW9uZXNfbGltcGlhcyA8LSBwcm95ZWNjaW9uZXMgJT4lDQogIGRpc3RpbmN0KE5vbWJyZSwgQm9kZWdhLCBNZXMsIC5rZWVwX2FsbCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpoZWFkKHByb3llY2Npb25lc19saW1waWFzKQ0KbnJvdyhwcm95ZWNjaW9uZXMpDQpucm93KHByb3llY2Npb25lc19saW1waWFzKQ0KDQpgYGANCmBgYHtyfQ0KaGVhZChwcm95ZWNjaW9uZXNfbGltcGlhcywgMTApICAgICMgTXVlc3RyYSBsYXMgMTAgcHJpbWVyYXMgZmlsYXMNCmBgYA0KDQpgYGB7cn0NClZpZXcocHJveWVjY2lvbmVzX2xpbXBpYXMpICAgICAgICAjIEFicmUgbGEgdGFibGEgY29tcGxldGEgZW4gcGVzdGHDsWENCmBgYA0KDQoNCmBgYHtyfQ0KIyBDYW1iaWEgbG9zIHZhbG9yZXMgcG9yIGVsIGPDs2RpZ28gbyBub21icmUgZGVsIMOtdGVtIHF1ZSBxdWllcmVzIHZlcg0KcHJveWVjY2lvbmVzX2xpbXBpYXMgJT4lDQogIGZpbHRlcihOb21icmUgPT0gIlJPQ0VQSElOIElWIDFHIExJT0ZJTElaQURPIFZJQUwiLA0KICAgICAgICAgQm9kZWdhID09ICJGQVJNQUNJQSIpDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCg0KcHJveWVjY2lvbmVzX2xpbXBpYXMgJT4lDQogIGZpbHRlcihOb21icmUgPT0gIlJPQ0VQSElOIElWIDFHIExJT0ZJTElaQURPIFZJQUwiLA0KICAgICAgICAgQm9kZWdhID09ICJGQVJNQUNJQSIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBNZXMsIHkgPSBDb21wcmFfUHJldmlzdGEpKSArDQogIGdlb21fbGluZShjb2xvciA9ICJibHVlIikgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gImRhcmtibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlByb3llY2Npw7NuIGRlIGNvbXByYXMgbWVuc3VhbGVzIDIwMjUiLA0KICAgICAgIHggPSAiTWVzIiwgeSA9ICJDYW50aWRhZCBwcm95ZWN0YWRhIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojIDEuIERhdG9zIGhpc3TDs3JpY29zIGRlbCBwcm9kdWN0bw0KaGlzdG9yaWNvIDwtIEhpc3RvcmlhbF9tZW5zdWFsICU+JQ0KICBmaWx0ZXIoTm9tYnJlID09ICJST0NFUEhJTiBJViAxRyBMSU9GSUxJWkFETyBWSUFMIiwgQm9kZWdhID09ICJGQVJNQUNJQSIpICU+JQ0KICBtdXRhdGUoVGlwbyA9ICJIaXN0w7NyaWNvIiwgRmVjaGEgPSBBbmlvTWVzKSAlPiUNCiAgc2VsZWN0KEZlY2hhLCBDb21wcmFzLCBUaXBvKQ0KDQojIDIuIFByb3llY2Npw7NuIGRlbCBtaXNtbyBwcm9kdWN0bw0KZnV0dXJvIDwtIHByb3llY2Npb25lc19saW1waWFzICU+JQ0KICBmaWx0ZXIoTm9tYnJlID09ICJST0NFUEhJTiBJViAxRyBMSU9GSUxJWkFETyBWSUFMIiwgQm9kZWdhID09ICJGQVJNQUNJQSIpICU+JQ0KICBtdXRhdGUoVGlwbyA9ICJQcm95ZWNjacOzbiIsIEZlY2hhID0gTWVzKSAlPiUNCiAgcmVuYW1lKENvbXByYXMgPSBDb21wcmFfUHJldmlzdGEpICU+JQ0KICBzZWxlY3QoRmVjaGEsIENvbXByYXMsIFRpcG8pDQoNCiMgMy4gVW5pciBhbWJvcyBkYXRhc2V0cw0KdG9kbyA8LSBiaW5kX3Jvd3MoaGlzdG9yaWNvLCBmdXR1cm8pDQoNCiMgNC4gR3JhZmljYXINCmdncGxvdCh0b2RvLCBhZXMoeCA9IEZlY2hhLCB5ID0gQ29tcHJhcywgY29sb3IgPSBUaXBvKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQ29tcHJhcyBoaXN0w7NyaWNhcyArIHByb3llY2Npw7NuIG1lbnN1YWwgKDIwMjUpIiwNCiAgICBzdWJ0aXRsZSA9ICJQcm9kdWN0bzogUk9DRVBISU4gSVYgMUcgTElPRklMSVpBRE8gVklBTCB8IEJvZGVnYTogRkFSTUFDSUEiLA0KICAgIHggPSAiTWVzIiwNCiAgICB5ID0gIkNhbnRpZGFkIGNvbXByYWRhIC8gcHJveWVjdGFkYSIsDQogICAgY29sb3IgPSAiU2VyaWUiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQojIyBmYWNldF93cmFwDQoNCmBgYHtyfQ0KdG9wNV9wcm9kdWN0b3MgPC0gSGlzdG9yaWFsX21lbnN1YWwgJT4lDQogIGdyb3VwX2J5KE5vbWJyZSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbCA9IHN1bShDb21wcmFzKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGFycmFuZ2UoZGVzYyhUb3RhbCkpICU+JQ0KICBzbGljZV9oZWFkKG4gPSA1KSAlPiUNCiAgcHVsbChOb21icmUpDQpgYGANCg0KYGBge3J9DQojIEhJU1TDk1JJQ08gKHNvbG8gcGFyYSBsb3MgdG9wIDUpDQpoaXN0b3JpY28gPC0gSGlzdG9yaWFsX21lbnN1YWwgJT4lDQogIGZpbHRlcihOb21icmUgJWluJSB0b3A1X3Byb2R1Y3RvcywgQm9kZWdhID09ICJGQVJNQUNJQSIpICU+JQ0KICBtdXRhdGUoVGlwbyA9ICJIaXN0w7NyaWNvIiwgRmVjaGEgPSBBbmlvTWVzKSAlPiUNCiAgc2VsZWN0KE5vbWJyZSwgRmVjaGEsIENvbXByYXMsIFRpcG8pDQoNCiMgUFJPWUVDQ0nDk04NCmZ1dHVybyA8LSBwcm95ZWNjaW9uZXNfbGltcGlhcyAlPiUNCiAgZmlsdGVyKE5vbWJyZSAlaW4lIHRvcDVfcHJvZHVjdG9zLCBCb2RlZ2EgPT0gIkZBUk1BQ0lBIikgJT4lDQogIG11dGF0ZShUaXBvID0gIlByb3llY2Npw7NuIiwgRmVjaGEgPSBNZXMpICU+JQ0KICByZW5hbWUoQ29tcHJhcyA9IENvbXByYV9QcmV2aXN0YSkgJT4lDQogIHNlbGVjdChOb21icmUsIEZlY2hhLCBDb21wcmFzLCBUaXBvKQ0KDQojIENPTUJJTkFSIFRPRE8NCnRvZG8gPC0gYmluZF9yb3dzKGhpc3RvcmljbywgZnV0dXJvKQ0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCmdncGxvdCh0b2RvLCBhZXMoeCA9IEZlY2hhLCB5ID0gQ29tcHJhcywgY29sb3IgPSBUaXBvKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMS44KSArDQogIGZhY2V0X3dyYXAofiBOb21icmUsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiQ29tcHJhcyBoaXN0w7NyaWNhcyArIHByb3llY2Npb25lcyAyMDI1IHBvciBwcm9kdWN0byIsDQogICAgc3VidGl0bGUgPSAiVG9wIDUgcHJvZHVjdG9zIHwgQm9kZWdhOiBGQVJNQUNJQSIsDQogICAgeCA9ICJNZXMiLA0KICAgIHkgPSAiQ2FudGlkYWQiLA0KICAgIGNvbG9yID0gIlNlcmllIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCg0KYGBge3J9DQojIFJlY2FsY3VsYXIgbG9zIDUgcHJvZHVjdG9zIG3DoXMgY29tcHJhZG9zDQp0b3A1X3Byb2R1Y3RvcyA8LSBIaXN0b3JpYWxfbWVuc3VhbCAlPiUNCiAgZ3JvdXBfYnkoTm9tYnJlKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKENvbXByYXMpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgYXJyYW5nZShkZXNjKFRvdGFsKSkgJT4lDQogIHNsaWNlX2hlYWQobiA9IDUpICU+JQ0KICBwdWxsKE5vbWJyZSkNCg0KIyBEYXRvcyBoaXN0w7NyaWNvcw0KaGlzdG9yaWNvIDwtIEhpc3RvcmlhbF9tZW5zdWFsICU+JQ0KICBmaWx0ZXIoTm9tYnJlICVpbiUgdG9wNV9wcm9kdWN0b3MsIEJvZGVnYSA9PSAiRkFSTUFDSUEiKSAlPiUNCiAgbXV0YXRlKFRpcG8gPSAiSGlzdMOzcmljbyIsIEZlY2hhID0gQW5pb01lcykgJT4lDQogIHNlbGVjdChOb21icmUsIEZlY2hhLCBDb21wcmFzLCBUaXBvKQ0KDQojIFByb3llY2Npb25lcw0KZnV0dXJvIDwtIHByb3llY2Npb25lc19saW1waWFzICU+JQ0KICBmaWx0ZXIoTm9tYnJlICVpbiUgdG9wNV9wcm9kdWN0b3MsIEJvZGVnYSA9PSAiRkFSTUFDSUEiKSAlPiUNCiAgbXV0YXRlKFRpcG8gPSAiUHJveWVjY2nDs24iLCBGZWNoYSA9IE1lcykgJT4lDQogIHJlbmFtZShDb21wcmFzID0gQ29tcHJhX1ByZXZpc3RhKSAlPiUNCiAgc2VsZWN0KE5vbWJyZSwgRmVjaGEsIENvbXByYXMsIFRpcG8pDQoNCiMgVW5pciB0b2RvDQp0b2RvIDwtIGJpbmRfcm93cyhoaXN0b3JpY28sIGZ1dHVybykNCmBgYA0KDQpgYGB7cn0NCiMgRmlsdHJhciBkYXRvcyB2w6FsaWRvcw0KdG9kb19maWx0cmFkbyA8LSB0b2RvICU+JQ0KICBmaWx0ZXIoIWlzLm5hKENvbXByYXMpKQ0KDQojIFZlcmlmaWNhciBwcm9kdWN0b3MgY29uIHByb3llY2Npw7NuDQp0YWJsZSh0b2RvX2ZpbHRyYWRvJFRpcG8sIHRvZG9fZmlsdHJhZG8kTm9tYnJlKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KHRvZG9fZmlsdHJhZG8sIGFlcyh4ID0gRmVjaGEsIHkgPSBDb21wcmFzLCBjb2xvciA9IFRpcG8pKSArDQogIGdlb21fbGluZShzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAxLjgpICsNCiAgZmFjZXRfd3JhcCh+IE5vbWJyZSwgc2NhbGVzID0gImZyZWVfeSIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJDb21wcmFzIGhpc3TDs3JpY2FzICsgcHJveWVjY2lvbmVzIDIwMjUgcG9yIHByb2R1Y3RvIiwNCiAgICBzdWJ0aXRsZSA9ICJUb3AgNSBwcm9kdWN0b3MgfCBCb2RlZ2E6IEZBUk1BQ0lBIiwNCiAgICB4ID0gIk1lcyIsDQogICAgeSA9ICJDYW50aWRhZCIsDQogICAgY29sb3IgPSAiU2VyaWUiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDExKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KDQojUkVUT01BTkRvIA0KDQpgYGB7cn0NCkhpc3RvcmlhbF9tZW5zdWFsICU+JQ0KICBmaWx0ZXIoTm9tYnJlID09ICJQRVJFTlRFUk9MIDI1MCBNRyBTT0JSRSIpICU+JQ0KICBhcnJhbmdlKEFuaW9NZXMpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KDQpIaXN0b3JpYWxfbWVuc3VhbCAlPiUNCiAgZmlsdGVyKE5vbWJyZSA9PSAiUEVSRU5URVJPTCAyNTAgTUcgU09CUkUiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gQW5pb01lcywgeSA9IENvbXByYXMsIGNvbG9yID0gQm9kZWdhKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiSGlzdMOzcmljbyBkZSBjb21wcmFzIG1lbnN1YWxlcyAtIFBFUkVOVEVST0wgMjUwIE1HIFNPQlJFIiwNCiAgICB4ID0gIk1lcyIsDQogICAgeSA9ICJDYW50aWRhZCBjb21wcmFkYSIsDQogICAgY29sb3IgPSAiQm9kZWdhIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3J9DQpwcm9kdWN0b19kYXRvcyA8LSBIaXN0b3JpYWxfbWVuc3VhbCAlPiUNCiAgZmlsdGVyKE5vbWJyZSA9PSAiUEVSRU5URVJPTCAyNTAgTUcgU09CUkUiLCBCb2RlZ2EgPT0gIkZBUk1BQ0lBIikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQoNCiMgVmVyaWZpY2Ftb3Mgc2kgaGF5IHN1ZmljaWVudGVzIGRhdG9zDQppZiAobnJvdyhwcm9kdWN0b19kYXRvcykgPj0gMikgew0KICB0c19wZXJlbnRlcm9sIDwtIHRzKHByb2R1Y3RvX2RhdG9zJENvbXByYXMsDQogICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSBjKHllYXIobWluKHByb2R1Y3RvX2RhdG9zJEFuaW9NZXMpKSwgbW9udGgobWluKHByb2R1Y3RvX2RhdG9zJEFuaW9NZXMpKSksDQogICAgICAgICAgICAgICAgICAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCiAgbW9kZWxvIDwtIGF1dG8uYXJpbWEodHNfcGVyZW50ZXJvbCkNCiAgcHJveWVjY2lvbiA8LSBmb3JlY2FzdChtb2RlbG8sIGggPSAxMikNCg0KICBmZWNoYXNfMjAyNSA8LSBzZXEoYXMuRGF0ZSgiMjAyNS0wMS0wMSIpLCBieSA9ICJtb250aCIsIGxlbmd0aC5vdXQgPSAxMikNCg0KICBwcm95ZWNjaW9uX3BlcmVudGVyb2wgPC0gdGliYmxlKA0KICAgIE1lcyA9IGZlY2hhc18yMDI1LA0KICAgIENvbXByYV9QcmV2aXN0YSA9IGFzLm51bWVyaWMocHJveWVjY2lvbiRtZWFuKQ0KICApDQogIA0KICBwcmludChwcm95ZWNjaW9uX3BlcmVudGVyb2wpDQp9IGVsc2Ugew0KICBwcmludCgiTm8gaGF5IHN1ZmljaWVudGVzIGRhdG9zIGhpc3TDs3JpY29zIHBhcmEgcHJveWVjdGFyIGVzdGUgcHJvZHVjdG8uIikNCn0NCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnkocHJvZHVjdG9fZGF0b3MkQW5pb01lcykNCmBgYA0KDQpgYGB7cn0NCmlmIChucm93KHByb2R1Y3RvX2RhdG9zKSA+PSAyICYmICFhbnkoaXMubmEocHJvZHVjdG9fZGF0b3MkQW5pb01lcykpKSB7DQogIA0KICB0c19wZXJlbnRlcm9sIDwtIHRzKHByb2R1Y3RvX2RhdG9zJENvbXByYXMsDQogICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSBjKHllYXIobWluKHByb2R1Y3RvX2RhdG9zJEFuaW9NZXMpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9udGgobWluKHByb2R1Y3RvX2RhdG9zJEFuaW9NZXMpKSksDQogICAgICAgICAgICAgICAgICAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCiAgbW9kZWxvIDwtIGF1dG8uYXJpbWEodHNfcGVyZW50ZXJvbCkNCiAgcHJveWVjY2lvbiA8LSBmb3JlY2FzdChtb2RlbG8sIGggPSAxMikNCg0KICBmZWNoYXNfMjAyNSA8LSBzZXEoYXMuRGF0ZSgiMjAyNS0wMS0wMSIpLCBieSA9ICJtb250aCIsIGxlbmd0aC5vdXQgPSAxMikNCg0KICBwcm95ZWNjaW9uX3BlcmVudGVyb2wgPC0gdGliYmxlKA0KICAgIE1lcyA9IGZlY2hhc18yMDI1LA0KICAgIENvbXByYV9QcmV2aXN0YSA9IGFzLm51bWVyaWMocHJveWVjY2lvbiRtZWFuKQ0KICApDQoNCiAgcHJpbnQocHJveWVjY2lvbl9wZXJlbnRlcm9sKQ0KDQp9IGVsc2Ugew0KICBwcmludCgi4pqg77iPIE5vIGhheSBzdWZpY2llbnRlcyBkYXRvcyBoaXN0w7NyaWNvcyBvIGxhcyBmZWNoYXMgZXN0w6FuIGluY29tcGxldGFzLiIpDQp9DQpgYGANCg0KYGBge3J9DQpIaXN0b3JpYWxfbWVuc3VhbCA8LSBIaXN0b3JpYWxfMjAyMF9hbF8yMDI0ICU+JQ0KICBtdXRhdGUoQW5pb01lcyA9IGx1YnJpZGF0ZTo6Zmxvb3JfZGF0ZShGZWNoYUZhY3R1cmEsIHVuaXQgPSAibW9udGgiKSkgJT4lDQogIGdyb3VwX2J5KE5vbWJyZSwgQm9kZWdhLCBBbmlvTWVzKSAlPiUNCiAgc3VtbWFyaXNlKENvbXByYXMgPSBzdW0oQ2FudGlkYWQpLCAuZ3JvdXBzID0gImRyb3AiKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShIaXN0b3JpYWxfbWVuc3VhbCRBbmlvTWVzKQ0KYGBgDQoNCmBgYHtyfQ0KYW55KGlzLm5hKEhpc3RvcmlhbF9tZW5zdWFsJEFuaW9NZXMpKSAgIyBEZWJlIGRhciBGQUxTRQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkobHVicmlkYXRlKQ0KDQpIaXN0b3JpYWxfbWVuc3VhbCA8LSBIaXN0b3JpYWxfMjAyMF9hbF8yMDI0ICU+JQ0KICBtdXRhdGUoQW5pb01lcyA9IGZsb29yX2RhdGUoRmVjaGFGYWN0dXJhLCB1bml0ID0gIm1vbnRoIikpICU+JQ0KICBncm91cF9ieShOb21icmUsIEJvZGVnYSwgQW5pb01lcykgJT4lDQogIHN1bW1hcmlzZShDb21wcmFzID0gc3VtKENhbnRpZGFkKSwgLmdyb3VwcyA9ICJkcm9wIikNCmBgYA0KDQoNCmBgYHtyfQ0KcHJvZHVjdG9fZGF0b3MgPC0gSGlzdG9yaWFsX21lbnN1YWwgJT4lDQogIGZpbHRlcihOb21icmUgPT0gIlBFUkVOVEVST0wgMjUwIE1HIFNPQlJFIiwgQm9kZWdhID09ICJGQVJNQUNJQSIpDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoZm9yZWNhc3QpDQoNCmlmIChucm93KHByb2R1Y3RvX2RhdG9zKSA+PSAyICYmICFhbnkoaXMubmEocHJvZHVjdG9fZGF0b3MkQW5pb01lcykpKSB7DQogIA0KICB0c19wZXJlbnRlcm9sIDwtIHRzKHByb2R1Y3RvX2RhdG9zJENvbXByYXMsDQogICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSBjKHllYXIobWluKHByb2R1Y3RvX2RhdG9zJEFuaW9NZXMpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9udGgobWluKHByb2R1Y3RvX2RhdG9zJEFuaW9NZXMpKSksDQogICAgICAgICAgICAgICAgICAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCiAgbW9kZWxvIDwtIGF1dG8uYXJpbWEodHNfcGVyZW50ZXJvbCkNCiAgcHJveWVjY2lvbiA8LSBmb3JlY2FzdChtb2RlbG8sIGggPSAxMikNCg0KICBmZWNoYXNfMjAyNSA8LSBzZXEoYXMuRGF0ZSgiMjAyNS0wMS0wMSIpLCBieSA9ICJtb250aCIsIGxlbmd0aC5vdXQgPSAxMikNCg0KICBwcm95ZWNjaW9uX3BlcmVudGVyb2wgPC0gdGliYmxlKA0KICAgIE1lcyA9IGZlY2hhc18yMDI1LA0KICAgIENvbXByYV9QcmV2aXN0YSA9IGFzLm51bWVyaWMocHJveWVjY2lvbiRtZWFuKQ0KICApDQoNCiAgcHJpbnQocHJveWVjY2lvbl9wZXJlbnRlcm9sKQ0KDQp9IGVsc2Ugew0KICBwcmludCgi4pqg77iPIE5vIGhheSBzdWZpY2llbnRlcyBkYXRvcyB2w6FsaWRvcyBwYXJhIGdlbmVyYXIgbGEgcHJveWVjY2nDs24uIikNCn0NCmBgYA0KDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgQ3JlYXIgZWwgaGlzdMOzcmljbyBlbiBmb3JtYXRvIGdyw6FmaWNvDQpoaXN0b3JpY28gPC0gcHJvZHVjdG9fZGF0b3MgJT4lDQogIG11dGF0ZShGZWNoYSA9IEFuaW9NZXMsIFRpcG8gPSAiSGlzdMOzcmljbyIpICU+JQ0KICBzZWxlY3QoRmVjaGEsIENvbXByYXMsIFRpcG8pDQoNCiMgQ3JlYXIgbGEgcHJveWVjY2nDs24NCmZ1dHVybyA8LSBwcm95ZWNjaW9uX3BlcmVudGVyb2wgJT4lDQogIG11dGF0ZShGZWNoYSA9IE1lcywgVGlwbyA9ICJQcm95ZWNjacOzbiIpICU+JQ0KICByZW5hbWUoQ29tcHJhcyA9IENvbXByYV9QcmV2aXN0YSkgJT4lDQogIHNlbGVjdChGZWNoYSwgQ29tcHJhcywgVGlwbykNCg0KIyBVbmlyIGFtYm9zDQp0b2RvX3BlcmVudGVyb2wgPC0gYmluZF9yb3dzKGhpc3RvcmljbywgZnV0dXJvKQ0KDQojIEdyYWZpY2FyDQpnZ3Bsb3QodG9kb19wZXJlbnRlcm9sLCBhZXMoeCA9IEZlY2hhLCB5ID0gQ29tcHJhcywgY29sb3IgPSBUaXBvKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiSGlzdMOzcmljbyArIFByb3llY2Npw7NuIDIwMjUiLA0KICAgIHN1YnRpdGxlID0gIlByb2R1Y3RvOiBQRVJFTlRFUk9MIDI1MCBNRyBTT0JSRSB8IEJvZGVnYTogRkFSTUFDSUEiLA0KICAgIHggPSAiTWVzIiwNCiAgICB5ID0gIkNhbnRpZGFkIg0KICApICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkhpc3TDs3JpY28iID0gImZpcmVicmljayIsICJQcm95ZWNjacOzbiIgPSAic3RlZWxibHVlIikpICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMikgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQojSGF5IG3DoXMgZmFjdG9yZXMgcXVlIHB1ZWRlbiBpbnRlcnZlbmlyLCBlc3RhcyBjb21wcmFzIG11Y2hhcyB2ZWNlcyBzb24gZWplY3V0YWRhcyBlbiBmYXJtYWNpYXMgZXh0ZXJuYXMgZW4gbHVnYXIgZGUgbWFudGVuZXIgdW4gZXN0aWFkbyBkZSAxIGEgMiBjYWphcyBtZW5zdWFsZXMgZW4gc3RvY2suIA0KDQojIyBQQVJBIFBBUEVMIE1JWFRPDQoNCmBgYHtyfQ0KSGlzdG9yaWFsX21lbnN1YWwgPC0gSGlzdG9yaWFsXzIwMjBfYWxfMjAyNCAlPiUNCiAgbXV0YXRlKEFuaW9NZXMgPSBmbG9vcl9kYXRlKEZlY2hhRmFjdHVyYSwgdW5pdCA9ICJtb250aCIpKSAlPiUNCiAgZ3JvdXBfYnkoTm9tYnJlLCBCb2RlZ2EsIEFuaW9NZXMpICU+JQ0KICBzdW1tYXJpc2UoQ29tcHJhcyA9IHN1bShDYW50aWRhZCksIC5ncm91cHMgPSAiZHJvcCIpDQpgYGANCg0KDQpgYGB7cn0NCnByb2R1Y3RvX2RhdG9zIDwtIEhpc3RvcmlhbF9tZW5zdWFsICU+JQ0KICBmaWx0ZXIoTm9tYnJlID09ICJQQVBFTCBNSVhUTyBST0xMTyBERSAxNSBYIDIwMCBNVFMiLCBCb2RlZ2EgPT0gIkZBUk1BQ0lBIikNCg0KYGBgDQoNCg0KYGBge3J9DQpnZ3Bsb3QocHJvZHVjdG9fZGF0b3MsIGFlcyh4ID0gQW5pb01lcywgeSA9IENvbXByYXMpKSArDQogIGdlb21fbGluZShjb2xvciA9ICJmaXJlYnJpY2siLCBzaXplID0gMS4yKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiZmlyZWJyaWNrIiwgc2l6ZSA9IDIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJIaXN0w7NyaWNvIGRlIGNvbXByYXMgbWVuc3VhbGVzIiwNCiAgICBzdWJ0aXRsZSA9ICJQQVBFTCBNSVhUTyBST0xMTyBERSAxNSBYIDIwMCBNVFMgfCBCb2RlZ2E6IEZBUk1BQ0lBIiwNCiAgICB4ID0gIk1lcyIsIHkgPSAiQ2FudGlkYWQiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCmBgYHtyfQ0KDQppZiAobnJvdyhwcm9kdWN0b19kYXRvcykgPj0gMiAmJiAhYW55KGlzLm5hKHByb2R1Y3RvX2RhdG9zJEFuaW9NZXMpKSkgew0KICANCiAgdHNfZGF0YSA8LSB0cyhwcm9kdWN0b19kYXRvcyRDb21wcmFzLA0KICAgICAgICAgICAgICAgIHN0YXJ0ID0gYyh5ZWFyKG1pbihwcm9kdWN0b19kYXRvcyRBbmlvTWVzKSksIG1vbnRoKG1pbihwcm9kdWN0b19kYXRvcyRBbmlvTWVzKSkpLA0KICAgICAgICAgICAgICAgIGZyZXF1ZW5jeSA9IDEyKQ0KDQogIG1vZGVsbyA8LSBhdXRvLmFyaW1hKHRzX2RhdGEpDQogIHByb3llY2Npb24gPC0gZm9yZWNhc3QobW9kZWxvLCBoID0gMTIpDQoNCiAgZmVjaGFzXzIwMjUgPC0gc2VxKGFzLkRhdGUoIjIwMjUtMDEtMDEiKSwgYnkgPSAibW9udGgiLCBsZW5ndGgub3V0ID0gMTIpDQoNCiAgcHJveWVjY2lvbl9wYXBlbCA8LSB0aWJibGUoDQogICAgTWVzID0gZmVjaGFzXzIwMjUsDQogICAgQ29tcHJhX1ByZXZpc3RhID0gYXMubnVtZXJpYyhwcm95ZWNjaW9uJG1lYW4pDQogICkNCg0KICBwcmludChwcm95ZWNjaW9uX3BhcGVsKQ0KDQp9IGVsc2Ugew0KICBwcmludCgi4pqg77iPIE5vIGhheSBzdWZpY2llbnRlcyBkYXRvcyBwYXJhIHByb3llY3Rhci4iKQ0KfQ0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBQcmVwYXJhciBoaXN0w7NyaWNvDQpoaXN0b3JpY28gPC0gcHJvZHVjdG9fZGF0b3MgJT4lDQogIG11dGF0ZShGZWNoYSA9IEFuaW9NZXMsIFRpcG8gPSAiSGlzdMOzcmljbyIpICU+JQ0KICBzZWxlY3QoRmVjaGEsIENvbXByYXMsIFRpcG8pDQoNCiMgUHJlcGFyYXIgcHJveWVjY2nDs24NCmZ1dHVybyA8LSBwcm95ZWNjaW9uX3BhcGVsICU+JQ0KICBtdXRhdGUoRmVjaGEgPSBNZXMsIFRpcG8gPSAiUHJveWVjY2nDs24iKSAlPiUNCiAgcmVuYW1lKENvbXByYXMgPSBDb21wcmFfUHJldmlzdGEpICU+JQ0KICBzZWxlY3QoRmVjaGEsIENvbXByYXMsIFRpcG8pDQoNCiMgVW5pcg0KdG9kbyA8LSBiaW5kX3Jvd3MoaGlzdG9yaWNvLCBmdXR1cm8pDQoNCiMgR3JhZmljYXINCmdncGxvdCh0b2RvLCBhZXMoeCA9IEZlY2hhLCB5ID0gQ29tcHJhcywgY29sb3IgPSBUaXBvKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJIaXN0w7NyaWNvIiA9ICJmaXJlYnJpY2siLCAiUHJveWVjY2nDs24iID0gInN0ZWVsYmx1ZSIpKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiSGlzdMOzcmljbyArIFByb3llY2Npw7NuIDIwMjUiLA0KICAgIHN1YnRpdGxlID0gIlBBUEVMIE1JWFRPIFJPTExPIERFIDE1IFggMjAwIE1UUyB8IEJvZGVnYTogRkFSTUFDSUEiLA0KICAgIHggPSAiTWVzIiwgeSA9ICJDYW50aWRhZCINCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShwdXJycikNCmxpYnJhcnkod3JpdGV4bCkNCg0KIyBQYXNvIDE6IENyZWFyIGhpc3RvcmlhbCBtZW5zdWFsIGNvbmZpYWJsZQ0KSGlzdG9yaWFsX21lbnN1YWwgPC0gSGlzdG9yaWFsXzIwMjBfYWxfMjAyNCAlPiUNCiAgbXV0YXRlKEFuaW9NZXMgPSBmbG9vcl9kYXRlKEZlY2hhRmFjdHVyYSwgdW5pdCA9ICJtb250aCIpKSAlPiUNCiAgZ3JvdXBfYnkoTm9tYnJlLCBCb2RlZ2EsIEFuaW9NZXMpICU+JQ0KICBzdW1tYXJpc2UoQ29tcHJhcyA9IHN1bShDYW50aWRhZCksIC5ncm91cHMgPSAiZHJvcCIpDQoNCiMgUGFzbyAyIGFsIDU6IEF1dG9tYXRpemFyIHRvZG8gcG9yIHByb2R1Y3RvIHkgYm9kZWdhDQpwcm95ZWNjaW9uZXNfY29tcGxldGFzIDwtIEhpc3RvcmlhbF9tZW5zdWFsICU+JQ0KICBncm91cF9ieShOb21icmUsIEJvZGVnYSkgJT4lDQogIGdyb3VwX3NwbGl0KCkgJT4lDQogIG1hcF9kZihmdW5jdGlvbihkZikgew0KICAgIG5vbWJyZV9pdGVtIDwtIHVuaXF1ZShkZiROb21icmUpDQogICAgYm9kZWdhIDwtIHVuaXF1ZShkZiRCb2RlZ2EpDQoNCiAgICBpZiAobnJvdyhkZikgPCAyIHx8IGFueShpcy5uYShkZiRBbmlvTWVzKSkpIHJldHVybihOVUxMKQ0KDQogICAgIyBTZXJpZSB0ZW1wb3JhbA0KICAgIHRzX2RhdGEgPC0gdHMoZGYkQ29tcHJhcywNCiAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gYyh5ZWFyKG1pbihkZiRBbmlvTWVzKSksIG1vbnRoKG1pbihkZiRBbmlvTWVzKSkpLA0KICAgICAgICAgICAgICAgICAgZnJlcXVlbmN5ID0gMTIpDQoNCiAgICBtb2RlbG8gPC0gdHJ5Q2F0Y2goYXV0by5hcmltYSh0c19kYXRhKSwgZXJyb3IgPSBmdW5jdGlvbihlKSByZXR1cm4oTlVMTCkpDQogICAgaWYgKGlzLm51bGwobW9kZWxvKSkgcmV0dXJuKE5VTEwpDQoNCiAgICBmb3JlY2FzdF9yZXN1bHQgPC0gZm9yZWNhc3QobW9kZWxvLCBoID0gMTIpDQogICAgZmVjaGFzX3Byb2ogPC0gc2VxKGFzLkRhdGUoIjIwMjUtMDEtMDEiKSwgYnkgPSAibW9udGgiLCBsZW5ndGgub3V0ID0gMTIpDQoNCiAgICB0aWJibGUoDQogICAgICBOb21icmUgPSBub21icmVfaXRlbSwNCiAgICAgIEJvZGVnYSA9IGJvZGVnYSwNCiAgICAgIE1lcyA9IGZlY2hhc19wcm9qLA0KICAgICAgQ29tcHJhX1ByZXZpc3RhID0gYXMubnVtZXJpYyhmb3JlY2FzdF9yZXN1bHQkbWVhbikNCiAgICApDQogIH0pDQpgYGANCg0KDQpgYGB7cn0NCiMgQ3JlYXIgY2FycGV0YSBzaSBubyBleGlzdGUNCmlmICghZGlyLmV4aXN0cygiZ3JhZmljb3NfcHJvZHVjdG9zIikpIGRpci5jcmVhdGUoImdyYWZpY29zX3Byb2R1Y3RvcyIpDQoNCiMgQ3JlYXIgZ3LDoWZpY28gcG9yIHByb2R1Y3RvDQpwcm9kdWN0b3NfdW5pY29zIDwtIHVuaXF1ZShwcm95ZWNjaW9uZXNfY29tcGxldGFzJE5vbWJyZSkNCg0Kd2Fsayhwcm9kdWN0b3NfdW5pY29zLCBmdW5jdGlvbihwcm9kdWN0bykgew0KICBoaXN0b3JpYWwgPC0gSGlzdG9yaWFsX21lbnN1YWwgJT4lDQogICAgZmlsdGVyKE5vbWJyZSA9PSBwcm9kdWN0bykgJT4lDQogICAgbXV0YXRlKFRpcG8gPSAiSGlzdMOzcmljbyIsIEZlY2hhID0gQW5pb01lcykNCg0KICBwcm95IDwtIHByb3llY2Npb25lc19jb21wbGV0YXMgJT4lDQogICAgZmlsdGVyKE5vbWJyZSA9PSBwcm9kdWN0bykgJT4lDQogICAgbXV0YXRlKFRpcG8gPSAiUHJveWVjY2nDs24iLCBGZWNoYSA9IE1lcykgJT4lDQogICAgcmVuYW1lKENvbXByYXMgPSBDb21wcmFfUHJldmlzdGEpDQoNCiAgdG9kbyA8LSBiaW5kX3Jvd3MoaGlzdG9yaWFsLCBwcm95KQ0KDQogIGdyYWZpY28gPC0gZ2dwbG90KHRvZG8sIGFlcyh4ID0gRmVjaGEsIHkgPSBDb21wcmFzLCBjb2xvciA9IFRpcG8pKSArDQogICAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsNCiAgICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogICAgbGFicygNCiAgICAgIHRpdGxlID0gcGFzdGUoIkhpc3TDs3JpY28gKyBQcm95ZWNjacOzbiAyMDI1XG5Qcm9kdWN0bzoiLCBwcm9kdWN0byksDQogICAgICB4ID0gIk1lcyIsIHkgPSAiQ2FudGlkYWQiDQogICAgKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkhpc3TDs3JpY28iID0gImZpcmVicmljayIsICJQcm95ZWNjacOzbiIgPSAic3RlZWxibHVlIikpICsNCiAgICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDExKSArDQogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCg0KICBnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoImdyYWZpY29zX3Byb2R1Y3Rvcy8iLCBnc3ViKCJbXmEtekEtWjAtOV0iLCAiXyIsIHByb2R1Y3RvKSwgIi5wbmciKSwNCiAgICAgICAgIHBsb3QgPSBncmFmaWNvLCB3aWR0aCA9IDksIGhlaWdodCA9IDUpDQp9KQ0KDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=