library(tidyverse)
library(magrittr)
library(ggplot2)
library(arules)
library(splitstackshape)
crudos <- read_csv("Kudos to DXY.cn Last update_ 03_13_2020, 8_00 PM (EST).csv", col_types = cols())
glimpse(crudos, width = 80)
Observations: 3,397
Variables: 22
$ id [3m[38;5;246m<dbl>[39m[23m 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1...
$ case_in_country [3m[38;5;246m<dbl>[39m[23m NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
$ `reporting date` [3m[38;5;246m<chr>[39m[23m "1/20/2020", "1/20/2020", "1/21/2020", "1/21...
$ location [3m[38;5;246m<chr>[39m[23m "Shenzhen, Guangdong", "Shanghai", "Zhejiang...
$ country [3m[38;5;246m<chr>[39m[23m "China", "China", "China", "China", "China",...
$ gender [3m[38;5;246m<chr>[39m[23m "male", "female", "male", "female", "male", ...
$ age [3m[38;5;246m<dbl>[39m[23m 66, 56, 46, 60, 58, 44, 34, 37, 39, 56, 18, ...
$ symptom_onset [3m[38;5;246m<chr>[39m[23m "1/3/2020", "1/15/2020", "1/4/2020", NA, NA,...
$ If_onset_approximated [3m[38;5;246m<dbl>[39m[23m 0, 0, 0, NA, NA, 0, 0, 0, 0, 0, 0, 0, NA, 0,...
$ hosp_visit_date [3m[38;5;246m<chr>[39m[23m "1/11/2020", "1/15/2020", "1/17/2020", "1/19...
$ international_traveler [3m[38;5;246m<dbl>[39m[23m NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
$ domestic_traveler [3m[38;5;246m<dbl>[39m[23m NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
$ exposure_start [3m[38;5;246m<chr>[39m[23m "12/29/2019", NA, NA, NA, NA, NA, NA, "1/10/...
$ exposure_end [3m[38;5;246m<chr>[39m[23m "1/4/2020", "1/12/2020", "1/3/2020", NA, NA,...
$ traveler [3m[38;5;246m<dbl>[39m[23m NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
$ `visiting Wuhan` [3m[38;5;246m<dbl>[39m[23m 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,...
$ `from Wuhan` [3m[38;5;246m<dbl>[39m[23m 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1,...
$ death [3m[38;5;246m<chr>[39m[23m "0", "0", "0", "0", "0", "0", "0", "0", "0",...
$ recovered [3m[38;5;246m<chr>[39m[23m "0", "0", "0", "0", "0", "0", "0", "0", "0",...
$ symptom [3m[38;5;246m<chr>[39m[23m NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, ...
$ source [3m[38;5;246m<chr>[39m[23m "Shenzhen Municipal Health Commission", "Off...
$ link [3m[38;5;246m<chr>[39m[23m "http://wjw.sz.gov.cn/wzx/202001/t20200120_1...
# Registros de sintomas a variables dummy.
datos_snulos <- crudos %>%
drop_na(symptom) %>%
filter(symptom != "")
datos_snulos$symptom %<>% gsub("\\s+", "", .) # trim entre palabras.
split <- datos_snulos$symptom %>% strsplit(",") # separa por comas en cada celda (lista de listas).
niveles <- split %>%
unlist() %>% # lista plana.
unique() # valores unicos de sintomas.
dummys <- split %>% lapply(function(x) table(factor(x, levels = niveles))) # dummys x registro.
datos <- data.frame(do.call(rbind, dummys))
tbl_df(datos)
# Homologamos sintomas.
sintomas <- datos %>%
mutate(fever = feaver + fever + feve. + highfever + mildfever) %>% # Suma de columnas iguales.
select(-c(feaver, feve., highfever, mildfever)) %>% # Descartamos las ya sumadas.
mutate(chestpain = chestdiscomfort + chestpain) %>%
select(-c(chestdiscomfort)) %>%
mutate(chills = chill + chills) %>%
select(-c(chill)) %>%
mutate(cough = cough + coughing + mildcough) %>%
select(-c(coughing, mildcough)) %>%
mutate(
difficultybreathing = difficultinbreathing + difficultybreathing + shortnessofbreath + breathlessness + respiratorydistress + dyspnea
) %>% select(
-c(
difficultinbreathing, shortnessofbreath,
breathlessness, respiratorydistress, dyspnea
)
) %>%
mutate(headache = headache + heavyhead) %>%
select(-c(heavyhead)) %>%
mutate(coughwithsputum = coughwithsputum + sputum) %>%
select(-c(sputum)) %>%
mutate(runnynose = runnynose + nasaldischarge) %>%
select(-c(nasaldischarge)) %>%
mutate(malaise = malaise + generalmalaise + physicaldiscomfort + wholebodypain + sorebody + myalgias, tired) %>%
select(-c(generalmalaise, physicaldiscomfort, wholebodypain, sorebody, myalgias, tired)) %>%
mutate(musclepain = musclepain + myalgia + musclecramps + muscleaches + achingmuscles + backpain, abdominalpain) %>%
select(-c(myalgia, musclecramps, muscleaches, achingmuscles, backpain, abdominalpain)) %>%
mutate(sorethroat = sorethroat + throatpain + throatdiscomfort + itchythroat) %>%
select(-c(throatpain, throatdiscomfort, itchythroat)) %>%
mutate(jointpain = jointpain + jointmusclepain) %>%
select(-c(jointmusclepain)) %>%
mutate(nausea = nausea + vomiting) %>%
select(-c(vomiting)) %>%
select(-c(hospitalization, flu, flusymptoms, cold, pneumonia, difficultywalking))
sintomas <- cbind(datos_snulos$id, sintomas)
names(sintomas)[1] <- "id"
sintomas_id <- sintomas
write_csv(sintomas, file.path("D:/dbs/datos/covid", "sintomas_covid.csv"))
rownames(sintomas) <- sintomas[, 1]
sintomas %<>% select(-id)
sintomas_casos <- rowSums(sintomas)
casos_sin_sintomas <- which(sintomas_casos == 0)
sintomas %<>% slice(-c(casos_sin_sintomas))
# Calculamos proporciones.
sintomas[sintomas > 0] <- 1
sumas_df <- as.data.frame(colSums(sintomas) / nrow(sintomas)) %>%
rownames_to_column("symptom")
names(sumas_df) <- c("sintomas", "proporcion")
n_registros <- nrow(sintomas)
sub_titulo <- paste(n_registros, "Casos Registrados (Wuhan)")
ggplot(data = sumas_df, aes(x = reorder(sintomas, proporcion), y = proporcion)) +
geom_bar(stat="identity") +
labs(x = "", y = "Proporcion", title = "Frecuencia de Sintomas COVID-19", subtitle = sub_titulo) +
coord_flip() +
theme(plot.title = element_text(hjust = 0.5), plot.subtitle = element_text(hjust = 0.5))

# Seleccion de variables y formato para Apriori.
top_n_sintomas <- 20
top_sintomas_nombres <- sumas_df %>%
arrange(desc(proporcion)) %>%
head(top_n_sintomas)
top_sintomas <- sintomas %>% select(top_sintomas_nombres$sintoma)
datos_transac <- as(
data.frame(lapply(top_sintomas, as.character), stringsAsFactors=T),
"transactions"
)
# Solo queremos combinaciones donde los sintomas esten presentes.
valores <- c()
for(col in names(top_sintomas)) {
condicion <- paste(col, "=1", sep = "")
valores <- c(valores, condicion)
}
reglas <- apriori(
datos_transac,
parameter = list(supp = 0.025, target = "frequent itemsets", minlen = 2, maxlen = 4),
appearance = list(items = valores)
)
Apriori
Parameter specification:
Algorithmic control:
Absolute minimum support count: 8
set item appearances ...[19 item(s)] done [0.00s].
set transactions ...[19 item(s), 336 transaction(s)] done [0.00s].
sorting and recoding items ... [14 item(s)] done [0.00s].
creating transaction tree ... done [0.00s].
checking subsets of size 1 2 3 done [0.00s].
writing ... [19 set(s)] done [0.00s].
creating S4 object ... done [0.00s].
summary(reglas)
set of 19 itemsets
most frequent items:
fever=1 cough=1 malaise=1 sorethroat=1 difficultybreathing=1 (Other)
14 9 3 3 3 9
element (itemset/transaction) length distribution:sizes
2 3
16 3
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.000 2.000 2.000 2.158 2.000 3.000
summary of quality measures:
support count
Min. :0.02679 Min. : 9.00
1st Qu.:0.02976 1st Qu.:10.00
Median :0.03571 Median :12.00
Mean :0.05686 Mean :19.11
3rd Qu.:0.05506 3rd Qu.:18.50
Max. :0.28869 Max. :97.00
includes transaction ID lists: FALSE
mining info:
reglas_df <- as.data.frame(inspect(reglas))
reglas_df$items %<>%
lapply(
function(x) mgsub::mgsub(as.character(x), c("=1", "\\{", "}", ","), c("", "", "", " + "))
) %>%
unlist()
n_registros <- nrow(datos_transac)
subtitulo <- paste(n_registros, "Casos Registrados (Wuhan)")
ggplot(data = reglas_df, aes(x = reorder(items, support), y = support)) +
geom_bar(stat = "identity") +
labs(x = "", y = "Proporcion (>= 0.025)", title = "Combinacion de Sintomas COVID-19", subtitle = subtitulo) +
theme(plot.title = element_text(hjust = 0.5), plot.subtitle = element_text(hjust = 0.5)) +
coord_flip()

# Revisamos proporcion de decesos por combinacion de sintomas.
datos_sintomas <- sintomas_id %>%
inner_join(datos_snulos %>% select(id, death), by = "id")
datos_sintomas %<>%
mutate(muerte = ifelse(death == 0, 0, 1)) # algunos registros son la fecha del deceso (pasamos todo a binario).
datos_sintomas %>%
filter(fever == 1, cough == 1) %>%
group_by(muerte) %>%
summarise(n = n()) %>%
mutate(freq = n / sum(n))
# Vector basado en agrupamiento.
pct_muertes_sintomas <- c(1, 0, 4, 0, 0, 0, 6, 6, 0, 0, 9, 0, 0, 0, 0, 0, 0, 11, 0)
reglas_df <- arrange(reglas_df, desc(count))
reglas_decesos <- reglas_df %>% mutate(pct_muertes = pct_muertes_sintomas)
reglas_decesos %<>% filter(pct_muertes > 0)
reglas_decesos %>% mutate(n_decesos = ceiling(count * (pct_muertes / 100)))
ggplot(data = reglas_decesos, aes(x = reorder(items, pct_muertes), y = pct_muertes)) +
geom_bar(stat = "identity") +
labs(x = "", y = "% descesos", title = "Pct. Decesos por Combinacion de Sintomas COVID-19") +
theme(plot.title = element_text(hjust = 0.5), plot.subtitle = element_text(hjust = 0.5), title = element_text(size = 10)) +
coord_flip()

LS0tDQp0aXRsZTogJ1NpbnRvbWFzIENPVklELTE5OiBBbmFsaXNpcyBkZSBGcmVjdWVuY2lhJw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGFydWxlcykNCmxpYnJhcnkoc3BsaXRzdGFja3NoYXBlKQ0KYGBgDQoNCmBgYHtyfQ0KY3J1ZG9zIDwtIHJlYWRfY3N2KCJLdWRvcyB0byBEWFkuY24gTGFzdCB1cGRhdGVfIDAzXzEzXzIwMjAsICA4XzAwIFBNIChFU1QpLmNzdiIsIGNvbF90eXBlcyA9IGNvbHMoKSkNCmdsaW1wc2UoY3J1ZG9zLCB3aWR0aCA9IDgwKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSZWdpc3Ryb3MgZGUgc2ludG9tYXMgYSB2YXJpYWJsZXMgZHVtbXkuDQpkYXRvc19zbnVsb3MgPC0gY3J1ZG9zICU+JQ0KICBkcm9wX25hKHN5bXB0b20pICU+JQ0KICBmaWx0ZXIoc3ltcHRvbSAhPSAiIikNCiAgDQpkYXRvc19zbnVsb3Mkc3ltcHRvbSAlPD4lIGdzdWIoIlxccysiLCAiIiwgLikgIyB0cmltIGVudHJlIHBhbGFicmFzLg0KDQpzcGxpdCA8LSBkYXRvc19zbnVsb3Mkc3ltcHRvbSAlPiUgc3Ryc3BsaXQoIiwiKSAjIHNlcGFyYSBwb3IgY29tYXMgZW4gY2FkYSBjZWxkYSAobGlzdGEgZGUgbGlzdGFzKS4NCm5pdmVsZXMgPC0gc3BsaXQgJT4lDQogIHVubGlzdCgpICU+JSAjIGxpc3RhIHBsYW5hLg0KICB1bmlxdWUoKSAjIHZhbG9yZXMgdW5pY29zIGRlIHNpbnRvbWFzLg0KZHVtbXlzIDwtIHNwbGl0ICU+JSBsYXBwbHkoZnVuY3Rpb24oeCkgdGFibGUoZmFjdG9yKHgsIGxldmVscyA9IG5pdmVsZXMpKSkgIyBkdW1teXMgeCByZWdpc3Ryby4NCmRhdG9zIDwtIGRhdGEuZnJhbWUoZG8uY2FsbChyYmluZCwgZHVtbXlzKSkNCnRibF9kZihkYXRvcykNCmBgYA0KDQpgYGB7cn0NCiMgSG9tb2xvZ2Ftb3Mgc2ludG9tYXMuDQpzaW50b21hcyA8LSBkYXRvcyAlPiUgDQogIG11dGF0ZShmZXZlciA9IGZlYXZlciArIGZldmVyICsgZmV2ZS4gKyBoaWdoZmV2ZXIgKyBtaWxkZmV2ZXIpICU+JSAjIFN1bWEgZGUgY29sdW1uYXMgaWd1YWxlcy4NCiAgc2VsZWN0KC1jKGZlYXZlciwgZmV2ZS4sIGhpZ2hmZXZlciwgbWlsZGZldmVyKSkgJT4lICMgRGVzY2FydGFtb3MgbGFzIHlhIHN1bWFkYXMuDQogIA0KICBtdXRhdGUoY2hlc3RwYWluID0gY2hlc3RkaXNjb21mb3J0ICsgY2hlc3RwYWluKSAlPiUNCiAgc2VsZWN0KC1jKGNoZXN0ZGlzY29tZm9ydCkpICU+JSANCiAgDQogIG11dGF0ZShjaGlsbHMgPSBjaGlsbCArIGNoaWxscykgJT4lDQogIHNlbGVjdCgtYyhjaGlsbCkpICU+JSANCiAgDQogIG11dGF0ZShjb3VnaCA9IGNvdWdoICsgY291Z2hpbmcgKyBtaWxkY291Z2gpICU+JQ0KICBzZWxlY3QoLWMoY291Z2hpbmcsIG1pbGRjb3VnaCkpICU+JSANCiAgDQogIG11dGF0ZSgNCiAgICBkaWZmaWN1bHR5YnJlYXRoaW5nID0gZGlmZmljdWx0aW5icmVhdGhpbmcgKyBkaWZmaWN1bHR5YnJlYXRoaW5nICsgc2hvcnRuZXNzb2ZicmVhdGggKyBicmVhdGhsZXNzbmVzcyArIHJlc3BpcmF0b3J5ZGlzdHJlc3MgKyBkeXNwbmVhDQogICkgJT4lIHNlbGVjdCgNCiAgICAtYygNCiAgICAgIGRpZmZpY3VsdGluYnJlYXRoaW5nLCBzaG9ydG5lc3NvZmJyZWF0aCwNCiAgICAgIGJyZWF0aGxlc3NuZXNzLCByZXNwaXJhdG9yeWRpc3RyZXNzLCBkeXNwbmVhDQogICAgKQ0KICApICU+JSANCg0KICBtdXRhdGUoaGVhZGFjaGUgPSBoZWFkYWNoZSArIGhlYXZ5aGVhZCkgJT4lDQogIHNlbGVjdCgtYyhoZWF2eWhlYWQpKSAlPiUNCiAgDQogIG11dGF0ZShjb3VnaHdpdGhzcHV0dW0gPSBjb3VnaHdpdGhzcHV0dW0gKyBzcHV0dW0pICU+JQ0KICBzZWxlY3QoLWMoc3B1dHVtKSkgJT4lDQogIA0KICBtdXRhdGUocnVubnlub3NlID0gcnVubnlub3NlICsgbmFzYWxkaXNjaGFyZ2UpICU+JQ0KICBzZWxlY3QoLWMobmFzYWxkaXNjaGFyZ2UpKSAlPiUNCiAgDQogIG11dGF0ZShtYWxhaXNlID0gbWFsYWlzZSArIGdlbmVyYWxtYWxhaXNlICsgcGh5c2ljYWxkaXNjb21mb3J0ICsgd2hvbGVib2R5cGFpbiArIHNvcmVib2R5ICsgbXlhbGdpYXMsIHRpcmVkKSAlPiUNCiAgc2VsZWN0KC1jKGdlbmVyYWxtYWxhaXNlLCBwaHlzaWNhbGRpc2NvbWZvcnQsIHdob2xlYm9keXBhaW4sIHNvcmVib2R5LCBteWFsZ2lhcywgdGlyZWQpKSAlPiUNCiAgDQogIG11dGF0ZShtdXNjbGVwYWluID0gbXVzY2xlcGFpbiArIG15YWxnaWEgKyBtdXNjbGVjcmFtcHMgKyBtdXNjbGVhY2hlcyArIGFjaGluZ211c2NsZXMgKyBiYWNrcGFpbiwgYWJkb21pbmFscGFpbikgJT4lDQogIHNlbGVjdCgtYyhteWFsZ2lhLCBtdXNjbGVjcmFtcHMsIG11c2NsZWFjaGVzLCBhY2hpbmdtdXNjbGVzLCBiYWNrcGFpbiwgYWJkb21pbmFscGFpbikpICU+JQ0KICANCiAgbXV0YXRlKHNvcmV0aHJvYXQgPSBzb3JldGhyb2F0ICsgdGhyb2F0cGFpbiArIHRocm9hdGRpc2NvbWZvcnQgKyBpdGNoeXRocm9hdCkgJT4lDQogIHNlbGVjdCgtYyh0aHJvYXRwYWluLCB0aHJvYXRkaXNjb21mb3J0LCBpdGNoeXRocm9hdCkpICU+JQ0KICANCiAgbXV0YXRlKGpvaW50cGFpbiA9IGpvaW50cGFpbiArIGpvaW50bXVzY2xlcGFpbikgJT4lDQogIHNlbGVjdCgtYyhqb2ludG11c2NsZXBhaW4pKSAlPiUNCiAgDQogIG11dGF0ZShuYXVzZWEgPSBuYXVzZWEgKyB2b21pdGluZykgJT4lDQogIHNlbGVjdCgtYyh2b21pdGluZykpICU+JQ0KICANCiAgc2VsZWN0KC1jKGhvc3BpdGFsaXphdGlvbiwgZmx1LCBmbHVzeW1wdG9tcywgY29sZCwgcG5ldW1vbmlhLCBkaWZmaWN1bHR5d2Fsa2luZykpDQoNCiAgc2ludG9tYXMgPC0gY2JpbmQoZGF0b3Nfc251bG9zJGlkLCBzaW50b21hcykNCiAgbmFtZXMoc2ludG9tYXMpWzFdIDwtICJpZCINCiAgc2ludG9tYXNfaWQgPC0gc2ludG9tYXMNCiAgd3JpdGVfY3N2KHNpbnRvbWFzLCBmaWxlLnBhdGgoIkQ6L2Ricy9kYXRvcy9jb3ZpZCIsICJzaW50b21hc19jb3ZpZC5jc3YiKSkNCiAgcm93bmFtZXMoc2ludG9tYXMpIDwtIHNpbnRvbWFzWywgMV0NCiAgc2ludG9tYXMgJTw+JSBzZWxlY3QoLWlkKQ0KDQogIHNpbnRvbWFzX2Nhc29zIDwtIHJvd1N1bXMoc2ludG9tYXMpDQogIGNhc29zX3Npbl9zaW50b21hcyA8LSB3aGljaChzaW50b21hc19jYXNvcyA9PSAwKQ0KICBzaW50b21hcyAlPD4lIHNsaWNlKC1jKGNhc29zX3Npbl9zaW50b21hcykpDQpgYGANCg0KYGBge3IgZmlnLmhlaWdodCA9IDQsIGZpZy53aWR0aCA9IDd9DQojIENhbGN1bGFtb3MgcHJvcG9yY2lvbmVzLg0Kc2ludG9tYXNbc2ludG9tYXMgPiAwXSA8LSAxDQpzdW1hc19kZiA8LSBhcy5kYXRhLmZyYW1lKGNvbFN1bXMoc2ludG9tYXMpIC8gbnJvdyhzaW50b21hcykpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4oInN5bXB0b20iKQ0KbmFtZXMoc3VtYXNfZGYpIDwtIGMoInNpbnRvbWFzIiwgInByb3BvcmNpb24iKQ0Kbl9yZWdpc3Ryb3MgPC0gbnJvdyhzaW50b21hcykNCnN1Yl90aXR1bG8gPC0gcGFzdGUobl9yZWdpc3Ryb3MsICJDYXNvcyBSZWdpc3RyYWRvcyAoV3VoYW4pIikNCg0KZ2dwbG90KGRhdGEgPSBzdW1hc19kZiwgYWVzKHggPSByZW9yZGVyKHNpbnRvbWFzLCBwcm9wb3JjaW9uKSwgeSA9IHByb3BvcmNpb24pKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKw0KICBsYWJzKHggPSAiIiwgeSA9ICJQcm9wb3JjaW9uIiwgdGl0bGUgPSAiRnJlY3VlbmNpYSBkZSBTaW50b21hcyBDT1ZJRC0xOSIsIHN1YnRpdGxlID0gc3ViX3RpdHVsbykgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQpgYGANCg0KYGBge3J9DQojIFNlbGVjY2lvbiBkZSB2YXJpYWJsZXMgeSBmb3JtYXRvIHBhcmEgQXByaW9yaS4NCnRvcF9uX3NpbnRvbWFzIDwtIDIwDQp0b3Bfc2ludG9tYXNfbm9tYnJlcyA8LSBzdW1hc19kZiAlPiUNCiAgYXJyYW5nZShkZXNjKHByb3BvcmNpb24pKSAlPiUNCiAgaGVhZCh0b3Bfbl9zaW50b21hcykNCnRvcF9zaW50b21hcyA8LSBzaW50b21hcyAlPiUgc2VsZWN0KHRvcF9zaW50b21hc19ub21icmVzJHNpbnRvbWEpDQoNCmRhdG9zX3RyYW5zYWMgPC0gYXMoDQogIGRhdGEuZnJhbWUobGFwcGx5KHRvcF9zaW50b21hcywgYXMuY2hhcmFjdGVyKSwgc3RyaW5nc0FzRmFjdG9ycz1UKSwNCiAgInRyYW5zYWN0aW9ucyINCikNCg0KIyBTb2xvIHF1ZXJlbW9zIGNvbWJpbmFjaW9uZXMgZG9uZGUgbG9zIHNpbnRvbWFzIGVzdGVuIHByZXNlbnRlcy4NCnZhbG9yZXMgPC0gYygpDQpmb3IoY29sIGluIG5hbWVzKHRvcF9zaW50b21hcykpIHsNCiAgY29uZGljaW9uIDwtIHBhc3RlKGNvbCwgIj0xIiwgc2VwID0gIiIpDQogIHZhbG9yZXMgPC0gYyh2YWxvcmVzLCBjb25kaWNpb24pDQp9DQpgYGANCg0KYGBge3J9DQpyZWdsYXMgPC0gYXByaW9yaSgNCiAgZGF0b3NfdHJhbnNhYywNCiAgcGFyYW1ldGVyID0gbGlzdChzdXBwID0gMC4wMjUsIHRhcmdldCA9ICJmcmVxdWVudCBpdGVtc2V0cyIsIG1pbmxlbiA9IDIsIG1heGxlbiA9IDQpLA0KICBhcHBlYXJhbmNlID0gbGlzdChpdGVtcyA9IHZhbG9yZXMpDQopDQpzdW1tYXJ5KHJlZ2xhcykNCmBgYA0KDQpgYGB7cn0NCnJlZ2xhc19kZiA8LSBhcy5kYXRhLmZyYW1lKGluc3BlY3QocmVnbGFzKSkNCnJlZ2xhc19kZiRpdGVtcyAlPD4lDQogIGxhcHBseSgNCiAgICBmdW5jdGlvbih4KSBtZ3N1Yjo6bWdzdWIoYXMuY2hhcmFjdGVyKHgpLCBjKCI9MSIsICJcXHsiLCAifSIsICIsIiksIGMoIiIsICIiLCAiIiwgIiArICIpKQ0KICApICU+JQ0KICB1bmxpc3QoKQ0KYGBgDQoNCmBgYHtyIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSA4fQ0Kbl9yZWdpc3Ryb3MgPC0gbnJvdyhkYXRvc190cmFuc2FjKQ0Kc3VidGl0dWxvIDwtIHBhc3RlKG5fcmVnaXN0cm9zLCAiQ2Fzb3MgUmVnaXN0cmFkb3MgKFd1aGFuKSIpDQoNCmdncGxvdChkYXRhID0gcmVnbGFzX2RmLCBhZXMoeCA9IHJlb3JkZXIoaXRlbXMsIHN1cHBvcnQpLCB5ID0gc3VwcG9ydCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgbGFicyh4ID0gIiIsIHkgPSAiUHJvcG9yY2lvbiAoPj0gMC4wMjUpIiwgdGl0bGUgPSAiQ29tYmluYWNpb24gZGUgU2ludG9tYXMgQ09WSUQtMTkiLCBzdWJ0aXRsZSA9IHN1YnRpdHVsbykgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgY29vcmRfZmxpcCgpDQpgYGANCg0KYGBge3J9DQojIFJldmlzYW1vcyBwcm9wb3JjaW9uIGRlIGRlY2Vzb3MgcG9yIGNvbWJpbmFjaW9uIGRlIHNpbnRvbWFzLg0KZGF0b3Nfc2ludG9tYXMgPC0gc2ludG9tYXNfaWQgJT4lDQogIGlubmVyX2pvaW4oZGF0b3Nfc251bG9zICU+JSBzZWxlY3QoaWQsIGRlYXRoKSwgYnkgPSAiaWQiKQ0KDQpkYXRvc19zaW50b21hcyAlPD4lIA0KICBtdXRhdGUobXVlcnRlID0gaWZlbHNlKGRlYXRoID09IDAsIDAsIDEpKSAjIGFsZ3Vub3MgcmVnaXN0cm9zIHNvbiBsYSBmZWNoYSBkZWwgZGVjZXNvIChwYXNhbW9zIHRvZG8gYSBiaW5hcmlvKS4NCg0KZGF0b3Nfc2ludG9tYXMgJT4lDQogIGZpbHRlcihmZXZlciA9PSAxLCBjb3VnaCA9PSAxKSAlPiUNCiAgZ3JvdXBfYnkobXVlcnRlKSAlPiUNCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JQ0KICBtdXRhdGUoZnJlcSA9IG4gLyBzdW0obikpDQoNCiMgVmVjdG9yIGJhc2FkbyBlbiBhZ3J1cGFtaWVudG8uDQpwY3RfbXVlcnRlc19zaW50b21hcyA8LSBjKDEsIDAsIDQsIDAsCTAsIDAsCTYsCTYsCTAsCTAsCTksCTAsCTAsCTAsCTAsCTAsCTAsCTExLAkwKQ0KYGBgDQoNCmBgYHtyfQ0KcmVnbGFzX2RmIDwtIGFycmFuZ2UocmVnbGFzX2RmLCBkZXNjKGNvdW50KSkNCnJlZ2xhc19kZWNlc29zIDwtIHJlZ2xhc19kZiAlPiUgbXV0YXRlKHBjdF9tdWVydGVzID0gcGN0X211ZXJ0ZXNfc2ludG9tYXMpDQpyZWdsYXNfZGVjZXNvcyAlPD4lICBmaWx0ZXIocGN0X211ZXJ0ZXMgPiAwKQ0KcmVnbGFzX2RlY2Vzb3MgJT4lIG11dGF0ZShuX2RlY2Vzb3MgPSBjZWlsaW5nKGNvdW50ICogKHBjdF9tdWVydGVzIC8gMTAwKSkpDQpgYGANCg0KYGBge3IgZmlnLmhlaWdodCA9IDMsIGZpZy53aWR0aCA9IDZ9DQpnZ3Bsb3QoZGF0YSA9IHJlZ2xhc19kZWNlc29zLCBhZXMoeCA9IHJlb3JkZXIoaXRlbXMsIHBjdF9tdWVydGVzKSwgeSA9IHBjdF9tdWVydGVzKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBsYWJzKHggPSAiIiwgeSA9ICIlIGRlc2Nlc29zIiwgdGl0bGUgPSAiUGN0LiBEZWNlc29zIHBvciBDb21iaW5hY2lvbiBkZSBTaW50b21hcyBDT1ZJRC0xOSIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCB0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArDQogIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCg0K