library(ggplot2)
library(lubridate)
## 
## Adjuntando el paquete: 'lubridate'
## The following objects are masked from 'package:base':
## 
##     date, intersect, setdiff, union
library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(forecast)
## Warning: package 'forecast' was built under R version 4.4.3
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(tidytext)
## Warning: package 'tidytext' was built under R version 4.4.3
library(corrplot)
## Warning: package 'corrplot' was built under R version 4.4.3
## corrplot 0.95 loaded
library(textdata)
## Warning: package 'textdata' was built under R version 4.4.3
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats 1.0.0     ✔ stringr 1.5.1
## ✔ purrr   1.0.4     ✔ tibble  3.2.1
## ✔ readr   2.1.5     ✔ tidyr   1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(wordcloud)
## Warning: package 'wordcloud' was built under R version 4.4.3
## Cargando paquete requerido: RColorBrewer
library(readxl)
## Warning: package 'readxl' was built under R version 4.4.3
library(stopwords)
## Warning: package 'stopwords' was built under R version 4.4.3
library(RColorBrewer)
library(stringr)

Cargar datos

PS <- read_excel("C:\\Users\\Luis Mendoza\\Downloads\\Encuesta Perfil y SatisfacciĂ³n 2024.xlsx")
## Warning: Expecting numeric in T1172 / R1172C20: got 'Cada semana por trabajo'
colnames(PS)
##  [1] "Mes"                              "Procedencia PaĂ­s"                
##  [3] "Procedencia Ciudad"               "Edad"                            
##  [5] "Motivo Visita"                    "Motivo Visita Otro"              
##  [7] "Noches Hospedadas"                "Hospedaje en Hotel"              
##  [9] "Hospedaje en Extra Hotelero"      "Hospedaje en Casa Familia/amigos"
## [11] "No se hospedĂ³"                    "Hospedaje en Otro"               
## [13] "Transporte AviĂ³n"                 "Transporte AutobĂºs"              
## [15] "Transporte AutomĂ³vil"             "Transporte Motocicleta"          
## [17] "Transporte Otro"                  "Gasto total en pesos"            
## [19] "Primera Visita"                   "Visitas Previas"                 
## [21] "EV Experiencia"                   "EV Sostenibilidad"               
## [23] "Experiencia"                      "Si Recomienda"                   
## [25] "No Recomienda"                    "Comentario RecomendaciĂ³n"

I. Transporte mĂ¡s utilizado

PS_transporte <- PS %>%
  pivot_longer(cols = starts_with("Transporte "), 
               names_to = "TipoTransporte", 
               values_to = "valor") %>%
  filter(!is.na(valor) & valor > 0)

transporte_total <- PS_transporte %>%
  group_by(TipoTransporte) %>%
  summarise(Cantidad = n()) %>%
  mutate(TipoTransporte = str_remove(TipoTransporte, "Transporte "))

ggplot(transporte_total, aes(x = reorder(TipoTransporte, -Cantidad), y = Cantidad)) +
  geom_col(fill = "steelblue") +
  labs(title = "Transporte mĂ¡s utilizado (Todos los visitantes)", x = "Transporte", y = "Cantidad de personas") +
  theme_minimal()

PS_extranjeros <- PS %>% filter(`Procedencia País` != "México" & !is.na(`Procedencia País`))

transporte_extranjeros <- PS_extranjeros %>%
  pivot_longer(cols = starts_with("Transporte "), 
               names_to = "TipoTransporte", 
               values_to = "valor") %>%
  filter(!is.na(valor) & valor > 0) %>%
  group_by(TipoTransporte) %>%
  summarise(Cantidad = n()) %>%
  mutate(TipoTransporte = str_remove(TipoTransporte, "Transporte "))

ggplot(transporte_extranjeros, aes(x = reorder(TipoTransporte, -Cantidad), y = Cantidad)) +
  geom_col(fill = "darkorange") +
  labs(title = "Transporte mĂ¡s utilizado (Visitantes extranjeros)", x = "Transporte", y = "Cantidad de personas") +
  theme_minimal()

II. AnĂ¡lisis Exploratorio de Datos

# 1. Definir nombres de meses vĂ¡lidos
meses_es <- c("enero", "febrero", "marzo", "abril", "mayo", "junio",
              "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre")

# 2. Normalizar y convertir "Mes" a fecha
PS$Mes <- tolower(trimws(PS$Mes))                # limpiar texto
PS$MesNum <- match(PS$Mes, meses_es)             # convertir a nĂºmero de mes
PS$MesFecha <- as.Date(ISOdate(2024, PS$MesNum, 1))  # crear fecha segura

# 3. Filtrar registros vĂ¡lidos
PS <- PS[!is.na(PS$MesFecha), ]

# 4. Clasificar visitantes como "Normal" u "Outlier" segĂºn gasto
q3 <- quantile(PS$`Gasto total en pesos`, 0.75, na.rm = TRUE)
iqr <- IQR(PS$`Gasto total en pesos`, na.rm = TRUE)
limite_superior <- q3 + 1.5 * iqr

PS <- PS %>%
  mutate(GrupoGasto = ifelse(`Gasto total en pesos` > limite_superior, "Outlier", "Normal"))

# 5. Calcular gasto total mensual por grupo
gasto_mensual_segmentado <- PS %>%
  group_by(MesFecha, GrupoGasto) %>%
  summarise(GastoTotal = sum(`Gasto total en pesos`, na.rm = TRUE)) %>%
  ungroup()
## `summarise()` has grouped output by 'MesFecha'. You can override using the
## `.groups` argument.
# 6. Graficar tendencia mensual por grupo
ggplot(gasto_mensual_segmentado, aes(x = MesFecha, y = GastoTotal, color = GrupoGasto)) +
  geom_line(size = 1.2) +
  geom_smooth(se = FALSE, linetype = "dashed") +
  scale_x_date(date_labels = "%b", date_breaks = "1 month") +
  labs(title = "Tendencia mensual del gasto total por grupo de gasto",
       x = "Mes", y = "Gasto total (MXN)", color = "Grupo de gasto") +
  theme_minimal()
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

# Agregar una etiqueta para distinguir grupos
PS <- PS %>%
  mutate(
    GrupoGasto = ifelse(`Gasto total en pesos` > limite_superior, "Outliers", "Normales")
  )

# Eliminar valores NA en gasto
PS_filtrado <- PS %>% filter(!is.na(`Gasto total en pesos`))

# Boxplot comparativo
library(ggplot2)
ggplot(PS_filtrado, aes(x = GrupoGasto, y = `Gasto total en pesos`, fill = GrupoGasto)) +
  geom_boxplot(outlier.color = "red", outlier.shape = 16) +
  scale_y_continuous(labels = scales::comma) +
  labs(
    title = "ComparaciĂ³n del Gasto Total",
    x = "Grupo",
    y = "Gasto total en pesos (MXN)"
  ) +
  theme_minimal()

# ------------------------
# Definir nombres legibles para las variables
# ------------------------
nombre_vars <- c("Gasto", "Edad", "Noches", "Visitas")

# ------------------------
# 1. Matriz general
# ------------------------
vars_cor <- PS[, c("Gasto total en pesos", "Edad", "Noches Hospedadas", "Visitas Previas")] %>%
  mutate_all(as.numeric)
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `Edad = .Primitive("as.double")(Edad)`.
## Caused by warning:
## ! NAs introducidos por coerciĂ³n
colnames(vars_cor) <- nombre_vars
cor_mat <- cor(vars_cor, use = "complete.obs")

corrplot(cor_mat,
         method = "circle",
         type = "lower",
         tl.col = "black",
         tl.cex = 1,
         tl.srt = 45,
         addCoef.col = "black",
         number.cex = 0.7,
         col = colorRampPalette(c("firebrick", "white", "steelblue"))(200),
         title = "Matriz de CorrelaciĂ³n General",
         mar = c(0, 0, 2, 0))

# ------------------------
# 2. Matriz para visitantes normales
# ------------------------
normales <- PS %>%
  filter(GrupoGasto == "Normales") %>%
  select(`Gasto total en pesos`, Edad, `Noches Hospedadas`, `Visitas Previas`) %>%
  mutate_all(as.numeric)
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `Edad = .Primitive("as.double")(Edad)`.
## Caused by warning:
## ! NAs introducidos por coerciĂ³n
colnames(normales) <- nombre_vars
cor_normales <- cor(normales, use = "complete.obs")

corrplot(cor_normales,
         method = "circle",
         type = "lower",
         tl.col = "black",
         tl.cex = 1,
         tl.srt = 45,
         addCoef.col = "black",
         number.cex = 0.7,
         col = colorRampPalette(c("firebrick", "white", "steelblue"))(200),
         title = "CorrelaciĂ³n - Visitantes Normales",
         mar = c(0, 0, 2, 0))

# ------------------------
# 3. Matriz para outliers
# ------------------------
outliers <- PS %>%
  filter(GrupoGasto == "Outliers") %>%
  select(`Gasto total en pesos`, Edad, `Noches Hospedadas`, `Visitas Previas`) %>%
  mutate_all(as.numeric)
## Warning: There was 1 warning in `mutate()`.
## ℹ In argument: `Edad = .Primitive("as.double")(Edad)`.
## Caused by warning:
## ! NAs introducidos por coerciĂ³n
colnames(outliers) <- nombre_vars
cor_outliers <- cor(outliers, use = "complete.obs")

corrplot(cor_outliers,
         method = "circle",
         type = "lower",
         tl.col = "black",
         tl.cex = 1,
         tl.srt = 45,
         addCoef.col = "black",
         number.cex = 0.7,
         col = colorRampPalette(c("firebrick", "white", "steelblue"))(200),
         title = "CorrelaciĂ³n - Outliers",
         mar = c(0, 0, 2, 0))

V. AnĂ¡lisis de Sentimiento

comentarios <- PS %>%
  filter(!is.na(`Comentario RecomendaciĂ³n`)) %>%
  tidytext::unnest_tokens(input = `Comentario RecomendaciĂ³n`, output = word) %>%
  anti_join(get_stopwords(language = "es"))
## Joining with `by = join_by(word)`
lexico_es <- tibble(
  word = c("bueno", "excelente", "agradable", "recomendado", "feliz", "malo", "terrible", "sucio", "caro", "pésimo"),
  sentiment = c("positivo", "positivo", "positivo", "positivo", "positivo", "negativo", "negativo", "negativo", "negativo", "negativo")
)

sentimientos <- comentarios %>%
  inner_join(lexico_es, by = "word") %>%
  count(sentiment, sort = TRUE)

ggplot(sentimientos, aes(x = reorder(sentiment, n), y = n, fill = sentiment)) +
  geom_col() +
  coord_flip() +
  labs(title = "AnĂ¡lisis de sentimiento de los comentarios (Español)", x = "Sentimiento", y = "Frecuencia") +
  theme_minimal()

VI. Nube de Palabras por RecomendaciĂ³n

# -----------------------------
# Limpieza y procesamiento - SĂ­ Recomienda
# -----------------------------
recomienda <- PS %>%
  filter(!is.na(`Si Recomienda`)) %>%
  select(`Si Recomienda`) %>%
  rename(texto = `Si Recomienda`) %>%
  unnest_tokens(word, texto) %>%
  filter(!word %in% stopwords::stopwords("es"),
         !str_detect(word, "\\d"),         # elimina nĂºmeros
         str_length(word) > 2,             # evita palabras cortas irrelevantes
         !is.na(word)) %>%
  mutate(word = case_when(                # corregir sinĂ³nimos o variantes
    word == "banio" ~ "baño",
    word == "limpia" ~ "limpio",
    word == "recomiendo" ~ "recomendar",
    TRUE ~ word
  )) %>%
  count(word, sort = TRUE)

# -----------------------------
# Limpieza y procesamiento - No Recomienda
# -----------------------------
no_recomienda <- PS %>%
  filter(!is.na(`No Recomienda`)) %>%
  select(`No Recomienda`) %>%
  rename(texto = `No Recomienda`) %>%
  unnest_tokens(word, texto) %>%
  filter(!word %in% stopwords::stopwords("es"),
         !str_detect(word, "\\d"),
         str_length(word) > 2,
         !is.na(word)) %>%
  mutate(word = case_when(
    word == "banio" ~ "baño",
    word == "sucio" ~ "suciedad",
    TRUE ~ word
  )) %>%
  count(word, sort = TRUE)

# -----------------------------
# Graficar nubes de palabras mejoradas
# -----------------------------
par(mfrow = c(1, 2))  # panel de 2 grĂ¡ficos

# Wordcloud para SĂ­ Recomienda
if (nrow(recomienda) > 0) {
  wordcloud(words = recomienda$word,
            freq = recomienda$n,
            scale = c(4, 0.8),
            max.words = 100,
            colors = brewer.pal(8, "Dark2"),
            random.order = FALSE)
  title("Lugares que SĂ­ Recomiendan")
} else {
  plot.new(); title("Sin datos: SĂ­ Recomienda")
}

# Wordcloud para No Recomienda
if (nrow(no_recomienda) > 0) {
  wordcloud(words = no_recomienda$word,
            freq = no_recomienda$n,
            scale = c(4, 0.8),
            max.words = 100,
            colors = brewer.pal(8, "Reds"),
            random.order = FALSE)
  title("Lugares que No Recomiendan")
} else {
  plot.new(); title("Sin datos: No Recomienda")
}

par(mfrow = c(1, 1))  # restaurar panel normal
LS0tDQp0aXRsZTogIkFuw6FsaXNpcyBkZSBUdXJpc21vIGVuIGxhIFpvbmEgTWV0cm9wb2xpdGFuYSBkZSBNb250ZXJyZXkiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAgdGhlbWU6IGNlcnVsZWFuDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KYGBge3IgbGlicmVyaWFzfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KGNvcnJwbG90KQ0KbGlicmFyeSh0ZXh0ZGF0YSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeSh3b3JkY2xvdWQpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoc3RvcHdvcmRzKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQoNCmBgYA0KDQojIyBDYXJnYXIgZGF0b3MNCg0KYGBge3IgY2FyZ2FyLWRhdG9zfQ0KUFMgPC0gcmVhZF9leGNlbCgiQzpcXFVzZXJzXFxMdWlzIE1lbmRvemFcXERvd25sb2Fkc1xcRW5jdWVzdGEgUGVyZmlsIHkgU2F0aXNmYWNjacOzbiAyMDI0Lnhsc3giKQ0KY29sbmFtZXMoUFMpDQpgYGANCg0KIyMgSS4gVHJhbnNwb3J0ZSBtw6FzIHV0aWxpemFkbw0KDQpgYGB7ciB0cmFuc3BvcnRlfQ0KUFNfdHJhbnNwb3J0ZSA8LSBQUyAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiVHJhbnNwb3J0ZSAiKSwgDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJUaXBvVHJhbnNwb3J0ZSIsIA0KICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbG9yIikgJT4lDQogIGZpbHRlcighaXMubmEodmFsb3IpICYgdmFsb3IgPiAwKQ0KDQp0cmFuc3BvcnRlX3RvdGFsIDwtIFBTX3RyYW5zcG9ydGUgJT4lDQogIGdyb3VwX2J5KFRpcG9UcmFuc3BvcnRlKSAlPiUNCiAgc3VtbWFyaXNlKENhbnRpZGFkID0gbigpKSAlPiUNCiAgbXV0YXRlKFRpcG9UcmFuc3BvcnRlID0gc3RyX3JlbW92ZShUaXBvVHJhbnNwb3J0ZSwgIlRyYW5zcG9ydGUgIikpDQoNCmdncGxvdCh0cmFuc3BvcnRlX3RvdGFsLCBhZXMoeCA9IHJlb3JkZXIoVGlwb1RyYW5zcG9ydGUsIC1DYW50aWRhZCksIHkgPSBDYW50aWRhZCkpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICJzdGVlbGJsdWUiKSArDQogIGxhYnModGl0bGUgPSAiVHJhbnNwb3J0ZSBtw6FzIHV0aWxpemFkbyAoVG9kb3MgbG9zIHZpc2l0YW50ZXMpIiwgeCA9ICJUcmFuc3BvcnRlIiwgeSA9ICJDYW50aWRhZCBkZSBwZXJzb25hcyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNClBTX2V4dHJhbmplcm9zIDwtIFBTICU+JSBmaWx0ZXIoYFByb2NlZGVuY2lhIFBhw61zYCAhPSAiTcOpeGljbyIgJiAhaXMubmEoYFByb2NlZGVuY2lhIFBhw61zYCkpDQoNCnRyYW5zcG9ydGVfZXh0cmFuamVyb3MgPC0gUFNfZXh0cmFuamVyb3MgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoIlRyYW5zcG9ydGUgIiksIA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiVGlwb1RyYW5zcG9ydGUiLCANCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWxvciIpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHZhbG9yKSAmIHZhbG9yID4gMCkgJT4lDQogIGdyb3VwX2J5KFRpcG9UcmFuc3BvcnRlKSAlPiUNCiAgc3VtbWFyaXNlKENhbnRpZGFkID0gbigpKSAlPiUNCiAgbXV0YXRlKFRpcG9UcmFuc3BvcnRlID0gc3RyX3JlbW92ZShUaXBvVHJhbnNwb3J0ZSwgIlRyYW5zcG9ydGUgIikpDQoNCmdncGxvdCh0cmFuc3BvcnRlX2V4dHJhbmplcm9zLCBhZXMoeCA9IHJlb3JkZXIoVGlwb1RyYW5zcG9ydGUsIC1DYW50aWRhZCksIHkgPSBDYW50aWRhZCkpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICJkYXJrb3JhbmdlIikgKw0KICBsYWJzKHRpdGxlID0gIlRyYW5zcG9ydGUgbcOhcyB1dGlsaXphZG8gKFZpc2l0YW50ZXMgZXh0cmFuamVyb3MpIiwgeCA9ICJUcmFuc3BvcnRlIiwgeSA9ICJDYW50aWRhZCBkZSBwZXJzb25hcyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMgSUkuIEFuw6FsaXNpcyBFeHBsb3JhdG9yaW8gZGUgRGF0b3MNCg0KYGBge3IgdGVuZGVuY2lhLWdhc3RvfQ0KIyAxLiBEZWZpbmlyIG5vbWJyZXMgZGUgbWVzZXMgdsOhbGlkb3MNCm1lc2VzX2VzIDwtIGMoImVuZXJvIiwgImZlYnJlcm8iLCAibWFyem8iLCAiYWJyaWwiLCAibWF5byIsICJqdW5pbyIsDQogICAgICAgICAgICAgICJqdWxpbyIsICJhZ29zdG8iLCAic2VwdGllbWJyZSIsICJvY3R1YnJlIiwgIm5vdmllbWJyZSIsICJkaWNpZW1icmUiKQ0KDQojIDIuIE5vcm1hbGl6YXIgeSBjb252ZXJ0aXIgIk1lcyIgYSBmZWNoYQ0KUFMkTWVzIDwtIHRvbG93ZXIodHJpbXdzKFBTJE1lcykpICAgICAgICAgICAgICAgICMgbGltcGlhciB0ZXh0bw0KUFMkTWVzTnVtIDwtIG1hdGNoKFBTJE1lcywgbWVzZXNfZXMpICAgICAgICAgICAgICMgY29udmVydGlyIGEgbsO6bWVybyBkZSBtZXMNClBTJE1lc0ZlY2hhIDwtIGFzLkRhdGUoSVNPZGF0ZSgyMDI0LCBQUyRNZXNOdW0sIDEpKSAgIyBjcmVhciBmZWNoYSBzZWd1cmENCg0KIyAzLiBGaWx0cmFyIHJlZ2lzdHJvcyB2w6FsaWRvcw0KUFMgPC0gUFNbIWlzLm5hKFBTJE1lc0ZlY2hhKSwgXQ0KDQojIDQuIENsYXNpZmljYXIgdmlzaXRhbnRlcyBjb21vICJOb3JtYWwiIHUgIk91dGxpZXIiIHNlZ8O6biBnYXN0bw0KcTMgPC0gcXVhbnRpbGUoUFMkYEdhc3RvIHRvdGFsIGVuIHBlc29zYCwgMC43NSwgbmEucm0gPSBUUlVFKQ0KaXFyIDwtIElRUihQUyRgR2FzdG8gdG90YWwgZW4gcGVzb3NgLCBuYS5ybSA9IFRSVUUpDQpsaW1pdGVfc3VwZXJpb3IgPC0gcTMgKyAxLjUgKiBpcXINCg0KUFMgPC0gUFMgJT4lDQogIG11dGF0ZShHcnVwb0dhc3RvID0gaWZlbHNlKGBHYXN0byB0b3RhbCBlbiBwZXNvc2AgPiBsaW1pdGVfc3VwZXJpb3IsICJPdXRsaWVyIiwgIk5vcm1hbCIpKQ0KDQojIDUuIENhbGN1bGFyIGdhc3RvIHRvdGFsIG1lbnN1YWwgcG9yIGdydXBvDQpnYXN0b19tZW5zdWFsX3NlZ21lbnRhZG8gPC0gUFMgJT4lDQogIGdyb3VwX2J5KE1lc0ZlY2hhLCBHcnVwb0dhc3RvKSAlPiUNCiAgc3VtbWFyaXNlKEdhc3RvVG90YWwgPSBzdW0oYEdhc3RvIHRvdGFsIGVuIHBlc29zYCwgbmEucm0gPSBUUlVFKSkgJT4lDQogIHVuZ3JvdXAoKQ0KDQojIDYuIEdyYWZpY2FyIHRlbmRlbmNpYSBtZW5zdWFsIHBvciBncnVwbw0KZ2dwbG90KGdhc3RvX21lbnN1YWxfc2VnbWVudGFkbywgYWVzKHggPSBNZXNGZWNoYSwgeSA9IEdhc3RvVG90YWwsIGNvbG9yID0gR3J1cG9HYXN0bykpICsNCiAgZ2VvbV9saW5lKHNpemUgPSAxLjIpICsNCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9sYWJlbHMgPSAiJWIiLCBkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIikgKw0KICBsYWJzKHRpdGxlID0gIlRlbmRlbmNpYSBtZW5zdWFsIGRlbCBnYXN0byB0b3RhbCBwb3IgZ3J1cG8gZGUgZ2FzdG8iLA0KICAgICAgIHggPSAiTWVzIiwgeSA9ICJHYXN0byB0b3RhbCAoTVhOKSIsIGNvbG9yID0gIkdydXBvIGRlIGdhc3RvIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCmBgYHtyIGJveHBsb3QtZ2FzdG99DQoNCg0KIyBBZ3JlZ2FyIHVuYSBldGlxdWV0YSBwYXJhIGRpc3Rpbmd1aXIgZ3J1cG9zDQpQUyA8LSBQUyAlPiUNCiAgbXV0YXRlKA0KICAgIEdydXBvR2FzdG8gPSBpZmVsc2UoYEdhc3RvIHRvdGFsIGVuIHBlc29zYCA+IGxpbWl0ZV9zdXBlcmlvciwgIk91dGxpZXJzIiwgIk5vcm1hbGVzIikNCiAgKQ0KDQojIEVsaW1pbmFyIHZhbG9yZXMgTkEgZW4gZ2FzdG8NClBTX2ZpbHRyYWRvIDwtIFBTICU+JSBmaWx0ZXIoIWlzLm5hKGBHYXN0byB0b3RhbCBlbiBwZXNvc2ApKQ0KDQojIEJveHBsb3QgY29tcGFyYXRpdm8NCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChQU19maWx0cmFkbywgYWVzKHggPSBHcnVwb0dhc3RvLCB5ID0gYEdhc3RvIHRvdGFsIGVuIHBlc29zYCwgZmlsbCA9IEdydXBvR2FzdG8pKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gInJlZCIsIG91dGxpZXIuc2hhcGUgPSAxNikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkNvbXBhcmFjacOzbiBkZWwgR2FzdG8gVG90YWwiLA0KICAgIHggPSAiR3J1cG8iLA0KICAgIHkgPSAiR2FzdG8gdG90YWwgZW4gcGVzb3MgKE1YTikiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCmBgYHtyIG1hdHJpei1jb3JyZWxhY2lvbn0NCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIERlZmluaXIgbm9tYnJlcyBsZWdpYmxlcyBwYXJhIGxhcyB2YXJpYWJsZXMNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpub21icmVfdmFycyA8LSBjKCJHYXN0byIsICJFZGFkIiwgIk5vY2hlcyIsICJWaXNpdGFzIikNCg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCiMgMS4gTWF0cml6IGdlbmVyYWwNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQp2YXJzX2NvciA8LSBQU1ssIGMoIkdhc3RvIHRvdGFsIGVuIHBlc29zIiwgIkVkYWQiLCAiTm9jaGVzIEhvc3BlZGFkYXMiLCAiVmlzaXRhcyBQcmV2aWFzIildICU+JQ0KICBtdXRhdGVfYWxsKGFzLm51bWVyaWMpDQpjb2xuYW1lcyh2YXJzX2NvcikgPC0gbm9tYnJlX3ZhcnMNCmNvcl9tYXQgPC0gY29yKHZhcnNfY29yLCB1c2UgPSAiY29tcGxldGUub2JzIikNCg0KY29ycnBsb3QoY29yX21hdCwNCiAgICAgICAgIG1ldGhvZCA9ICJjaXJjbGUiLA0KICAgICAgICAgdHlwZSA9ICJsb3dlciIsDQogICAgICAgICB0bC5jb2wgPSAiYmxhY2siLA0KICAgICAgICAgdGwuY2V4ID0gMSwNCiAgICAgICAgIHRsLnNydCA9IDQ1LA0KICAgICAgICAgYWRkQ29lZi5jb2wgPSAiYmxhY2siLA0KICAgICAgICAgbnVtYmVyLmNleCA9IDAuNywNCiAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZmlyZWJyaWNrIiwgIndoaXRlIiwgInN0ZWVsYmx1ZSIpKSgyMDApLA0KICAgICAgICAgdGl0bGUgPSAiTWF0cml6IGRlIENvcnJlbGFjacOzbiBHZW5lcmFsIiwNCiAgICAgICAgIG1hciA9IGMoMCwgMCwgMiwgMCkpDQoNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIDIuIE1hdHJpeiBwYXJhIHZpc2l0YW50ZXMgbm9ybWFsZXMNCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpub3JtYWxlcyA8LSBQUyAlPiUNCiAgZmlsdGVyKEdydXBvR2FzdG8gPT0gIk5vcm1hbGVzIikgJT4lDQogIHNlbGVjdChgR2FzdG8gdG90YWwgZW4gcGVzb3NgLCBFZGFkLCBgTm9jaGVzIEhvc3BlZGFkYXNgLCBgVmlzaXRhcyBQcmV2aWFzYCkgJT4lDQogIG11dGF0ZV9hbGwoYXMubnVtZXJpYykNCmNvbG5hbWVzKG5vcm1hbGVzKSA8LSBub21icmVfdmFycw0KY29yX25vcm1hbGVzIDwtIGNvcihub3JtYWxlcywgdXNlID0gImNvbXBsZXRlLm9icyIpDQoNCmNvcnJwbG90KGNvcl9ub3JtYWxlcywNCiAgICAgICAgIG1ldGhvZCA9ICJjaXJjbGUiLA0KICAgICAgICAgdHlwZSA9ICJsb3dlciIsDQogICAgICAgICB0bC5jb2wgPSAiYmxhY2siLA0KICAgICAgICAgdGwuY2V4ID0gMSwNCiAgICAgICAgIHRsLnNydCA9IDQ1LA0KICAgICAgICAgYWRkQ29lZi5jb2wgPSAiYmxhY2siLA0KICAgICAgICAgbnVtYmVyLmNleCA9IDAuNywNCiAgICAgICAgIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZmlyZWJyaWNrIiwgIndoaXRlIiwgInN0ZWVsYmx1ZSIpKSgyMDApLA0KICAgICAgICAgdGl0bGUgPSAiQ29ycmVsYWNpw7NuIC0gVmlzaXRhbnRlcyBOb3JtYWxlcyIsDQogICAgICAgICBtYXIgPSBjKDAsIDAsIDIsIDApKQ0KDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAzLiBNYXRyaXogcGFyYSBvdXRsaWVycw0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCm91dGxpZXJzIDwtIFBTICU+JQ0KICBmaWx0ZXIoR3J1cG9HYXN0byA9PSAiT3V0bGllcnMiKSAlPiUNCiAgc2VsZWN0KGBHYXN0byB0b3RhbCBlbiBwZXNvc2AsIEVkYWQsIGBOb2NoZXMgSG9zcGVkYWRhc2AsIGBWaXNpdGFzIFByZXZpYXNgKSAlPiUNCiAgbXV0YXRlX2FsbChhcy5udW1lcmljKQ0KY29sbmFtZXMob3V0bGllcnMpIDwtIG5vbWJyZV92YXJzDQpjb3Jfb3V0bGllcnMgPC0gY29yKG91dGxpZXJzLCB1c2UgPSAiY29tcGxldGUub2JzIikNCg0KY29ycnBsb3QoY29yX291dGxpZXJzLA0KICAgICAgICAgbWV0aG9kID0gImNpcmNsZSIsDQogICAgICAgICB0eXBlID0gImxvd2VyIiwNCiAgICAgICAgIHRsLmNvbCA9ICJibGFjayIsDQogICAgICAgICB0bC5jZXggPSAxLA0KICAgICAgICAgdGwuc3J0ID0gNDUsDQogICAgICAgICBhZGRDb2VmLmNvbCA9ICJibGFjayIsDQogICAgICAgICBudW1iZXIuY2V4ID0gMC43LA0KICAgICAgICAgY29sID0gY29sb3JSYW1wUGFsZXR0ZShjKCJmaXJlYnJpY2siLCAid2hpdGUiLCAic3RlZWxibHVlIikpKDIwMCksDQogICAgICAgICB0aXRsZSA9ICJDb3JyZWxhY2nDs24gLSBPdXRsaWVycyIsDQogICAgICAgICBtYXIgPSBjKDAsIDAsIDIsIDApKQ0KDQpgYGANCg0KDQoNCiMjIFYuIEFuw6FsaXNpcyBkZSBTZW50aW1pZW50bw0KDQpgYGB7ciBzZW50aW1pZW50b30NCmNvbWVudGFyaW9zIDwtIFBTICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGBDb21lbnRhcmlvIFJlY29tZW5kYWNpw7NuYCkpICU+JQ0KICB0aWR5dGV4dDo6dW5uZXN0X3Rva2VucyhpbnB1dCA9IGBDb21lbnRhcmlvIFJlY29tZW5kYWNpw7NuYCwgb3V0cHV0ID0gd29yZCkgJT4lDQogIGFudGlfam9pbihnZXRfc3RvcHdvcmRzKGxhbmd1YWdlID0gImVzIikpDQoNCmxleGljb19lcyA8LSB0aWJibGUoDQogIHdvcmQgPSBjKCJidWVubyIsICJleGNlbGVudGUiLCAiYWdyYWRhYmxlIiwgInJlY29tZW5kYWRvIiwgImZlbGl6IiwgIm1hbG8iLCAidGVycmlibGUiLCAic3VjaW8iLCAiY2FybyIsICJww6lzaW1vIiksDQogIHNlbnRpbWVudCA9IGMoInBvc2l0aXZvIiwgInBvc2l0aXZvIiwgInBvc2l0aXZvIiwgInBvc2l0aXZvIiwgInBvc2l0aXZvIiwgIm5lZ2F0aXZvIiwgIm5lZ2F0aXZvIiwgIm5lZ2F0aXZvIiwgIm5lZ2F0aXZvIiwgIm5lZ2F0aXZvIikNCikNCg0Kc2VudGltaWVudG9zIDwtIGNvbWVudGFyaW9zICU+JQ0KICBpbm5lcl9qb2luKGxleGljb19lcywgYnkgPSAid29yZCIpICU+JQ0KICBjb3VudChzZW50aW1lbnQsIHNvcnQgPSBUUlVFKQ0KDQpnZ3Bsb3Qoc2VudGltaWVudG9zLCBhZXMoeCA9IHJlb3JkZXIoc2VudGltZW50LCBuKSwgeSA9IG4sIGZpbGwgPSBzZW50aW1lbnQpKSArDQogIGdlb21fY29sKCkgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIkFuw6FsaXNpcyBkZSBzZW50aW1pZW50byBkZSBsb3MgY29tZW50YXJpb3MgKEVzcGHDsW9sKSIsIHggPSAiU2VudGltaWVudG8iLCB5ID0gIkZyZWN1ZW5jaWEiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMjIFZJLiBOdWJlIGRlIFBhbGFicmFzIHBvciBSZWNvbWVuZGFjacOzbg0KDQpgYGB7ciBudWJlcy1wYWxhYnJhcywgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIExpbXBpZXphIHkgcHJvY2VzYW1pZW50byAtIFPDrSBSZWNvbWllbmRhDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpyZWNvbWllbmRhIDwtIFBTICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGBTaSBSZWNvbWllbmRhYCkpICU+JQ0KICBzZWxlY3QoYFNpIFJlY29taWVuZGFgKSAlPiUNCiAgcmVuYW1lKHRleHRvID0gYFNpIFJlY29taWVuZGFgKSAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0bykgJT4lDQogIGZpbHRlcighd29yZCAlaW4lIHN0b3B3b3Jkczo6c3RvcHdvcmRzKCJlcyIpLA0KICAgICAgICAgIXN0cl9kZXRlY3Qod29yZCwgIlxcZCIpLCAgICAgICAgICMgZWxpbWluYSBuw7ptZXJvcw0KICAgICAgICAgc3RyX2xlbmd0aCh3b3JkKSA+IDIsICAgICAgICAgICAgICMgZXZpdGEgcGFsYWJyYXMgY29ydGFzIGlycmVsZXZhbnRlcw0KICAgICAgICAgIWlzLm5hKHdvcmQpKSAlPiUNCiAgbXV0YXRlKHdvcmQgPSBjYXNlX3doZW4oICAgICAgICAgICAgICAgICMgY29ycmVnaXIgc2luw7NuaW1vcyBvIHZhcmlhbnRlcw0KICAgIHdvcmQgPT0gImJhbmlvIiB+ICJiYcOxbyIsDQogICAgd29yZCA9PSAibGltcGlhIiB+ICJsaW1waW8iLA0KICAgIHdvcmQgPT0gInJlY29taWVuZG8iIH4gInJlY29tZW5kYXIiLA0KICAgIFRSVUUgfiB3b3JkDQogICkpICU+JQ0KICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkNCg0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyBMaW1waWV6YSB5IHByb2Nlc2FtaWVudG8gLSBObyBSZWNvbWllbmRhDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpub19yZWNvbWllbmRhIDwtIFBTICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGBObyBSZWNvbWllbmRhYCkpICU+JQ0KICBzZWxlY3QoYE5vIFJlY29taWVuZGFgKSAlPiUNCiAgcmVuYW1lKHRleHRvID0gYE5vIFJlY29taWVuZGFgKSAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0bykgJT4lDQogIGZpbHRlcighd29yZCAlaW4lIHN0b3B3b3Jkczo6c3RvcHdvcmRzKCJlcyIpLA0KICAgICAgICAgIXN0cl9kZXRlY3Qod29yZCwgIlxcZCIpLA0KICAgICAgICAgc3RyX2xlbmd0aCh3b3JkKSA+IDIsDQogICAgICAgICAhaXMubmEod29yZCkpICU+JQ0KICBtdXRhdGUod29yZCA9IGNhc2Vfd2hlbigNCiAgICB3b3JkID09ICJiYW5pbyIgfiAiYmHDsW8iLA0KICAgIHdvcmQgPT0gInN1Y2lvIiB+ICJzdWNpZWRhZCIsDQogICAgVFJVRSB+IHdvcmQNCiAgKSkgJT4lDQogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQ0KDQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojIEdyYWZpY2FyIG51YmVzIGRlIHBhbGFicmFzIG1lam9yYWRhcw0KIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KcGFyKG1mcm93ID0gYygxLCAyKSkgICMgcGFuZWwgZGUgMiBncsOhZmljb3MNCg0KIyBXb3JkY2xvdWQgcGFyYSBTw60gUmVjb21pZW5kYQ0KaWYgKG5yb3cocmVjb21pZW5kYSkgPiAwKSB7DQogIHdvcmRjbG91ZCh3b3JkcyA9IHJlY29taWVuZGEkd29yZCwNCiAgICAgICAgICAgIGZyZXEgPSByZWNvbWllbmRhJG4sDQogICAgICAgICAgICBzY2FsZSA9IGMoNCwgMC44KSwNCiAgICAgICAgICAgIG1heC53b3JkcyA9IDEwMCwNCiAgICAgICAgICAgIGNvbG9ycyA9IGJyZXdlci5wYWwoOCwgIkRhcmsyIiksDQogICAgICAgICAgICByYW5kb20ub3JkZXIgPSBGQUxTRSkNCiAgdGl0bGUoIkx1Z2FyZXMgcXVlIFPDrSBSZWNvbWllbmRhbiIpDQp9IGVsc2Ugew0KICBwbG90Lm5ldygpOyB0aXRsZSgiU2luIGRhdG9zOiBTw60gUmVjb21pZW5kYSIpDQp9DQoNCiMgV29yZGNsb3VkIHBhcmEgTm8gUmVjb21pZW5kYQ0KaWYgKG5yb3cobm9fcmVjb21pZW5kYSkgPiAwKSB7DQogIHdvcmRjbG91ZCh3b3JkcyA9IG5vX3JlY29taWVuZGEkd29yZCwNCiAgICAgICAgICAgIGZyZXEgPSBub19yZWNvbWllbmRhJG4sDQogICAgICAgICAgICBzY2FsZSA9IGMoNCwgMC44KSwNCiAgICAgICAgICAgIG1heC53b3JkcyA9IDEwMCwNCiAgICAgICAgICAgIGNvbG9ycyA9IGJyZXdlci5wYWwoOCwgIlJlZHMiKSwNCiAgICAgICAgICAgIHJhbmRvbS5vcmRlciA9IEZBTFNFKQ0KICB0aXRsZSgiTHVnYXJlcyBxdWUgTm8gUmVjb21pZW5kYW4iKQ0KfSBlbHNlIHsNCiAgcGxvdC5uZXcoKTsgdGl0bGUoIlNpbiBkYXRvczogTm8gUmVjb21pZW5kYSIpDQp9DQoNCnBhcihtZnJvdyA9IGMoMSwgMSkpICAjIHJlc3RhdXJhciBwYW5lbCBub3JtYWwNCg0KYGBgDQoNCg==