Introducción

En este notebook presenta el procedimiento llevado a cabo para el análisis de datos de metabarcoding, a partir el artículo The sources and transmission routes of microbial populations throughout a meat processing facility. Este artículo busca analizar e identificar fuentes potenciales de contaminación microbiana en la carne de cerdos, durante el proceso de faena en el matadero.

Objetivo

El objetivo de nuestro estudio se reduce a analizar cómo cambia la composición microbiana en la carne de los cerdos de los distintos granjeros, e identificar si alguna composición presenta un peligro mayor para el matadero.

1. Preprocesamiento de los datos

Para llevar a cabo el análisis, primero se deben cargar, filtrar y preprocesar los datos tomados en el matadero, los cuales fueron descargados del European Nucleotide Archive.

1.1 Setup del ambiente de trabajo

Se hará uso de paquetes de R, y librerías opensource de Bioconductor para el procesamiento de datos.

library(ampvis2)
library(mia)
library(miaViz)
library(ggplot2)
library(Biostrings)
library(phyloseq)
library(ANCOMBC)
library(tidyr)
library(patchwork)
library(ggsignif)
library(scater)
library(ggpubr)
library(reshape2)
library(RColorBrewer)
library(dada2)
library(vegan)

Además, se debe setear el directorio que contenga los archivos (.fastq) con los datos descargados:

# Reemplazar por directorio utilizado
setwd("~/UCU/alimentos1/")
path <- getwd()

1.2 Registrar metainformación de las muestras

Diferentes muestras fueron tomadas durante el proceso de faena, en diferentes cerdos, lugares y herramientas. Las lecturas realizadas en cada muestra fueron almacenadas en archivos .fastq, los cuales contienen las secuencias de ADN y calidad de sus respectivas lecturas.

Debido a que los archivos contienen demasiada información, nunca se cargan en memoria. Sino que las funciones utilizadas se encargan de buscar los datos en cada archivo cuando sea necesario, a partir de su ruta. Entonces, se guarda el listado de rutas hacia los archivos de todas las lecturas forward (f) y reverse (r).

# Archivos que contengan '_1.fastq' representan lecturas f
fnFs <- sort(list.files(path, pattern="_1.fastq", full.names = TRUE)) 
# Archivos que contengan '_2.fastq' representan lecturas r
fnRs <- sort(list.files(path, pattern="_2.fastq", full.names = TRUE))

Luego, se extraen los nombres de cada muestra a partir del nombre de los archivos:

# Formato: SAMPLENAME_XXX.fastq
sample.names <- sapply(strsplit(basename(fnFs), "_"), `[`, 1)

Finalmente se importa la metadata de cada muestra:

sample.metadata <- read.csv("sampleData_alimentos.csv")

1.3 Chequeo y filtrado de calidad

Para filtrar lecturas de mala calidad, se grafica la calidad de las mismas para definir donde truncarlas.

Es característico de una lectura Illumina que al final de la secuencia, el score de calidad decaiga, y que la lectura reverse tenga menor calidad que la forward. Entonces se trunca las regiones de mala calidad.

En este caso, se utilizarán las regiones donde el score sea mayor a 30 aproximadamente. Por lo tanto se truncaran las bases posteriores a 290 en lecturas forward, y 200 para reverse.

Para realizar el filtrado y truncado es útil la función filterAndTrim, la cual permite:

  • Cortar registros de mala calidad

  • Filtrar lecturas con datos nulos

  • Cortar primers

Utilizando los parámetros:

  • truncLen=c(290, 200) ⟶ Lecturas con tamaño menor a 290 (forward) o 200 (reverse) son descartadas

  • maxEE=(2,2) ⟶ Lecturas con mas de 2 errores esperados son descartadas

  • maxN=0 ⟶ Lecturas con datos nulos son descartadas

Se debe tener cuidado de no truncar en exceso, debido a que dada2 solo tiene en cuenta la secuencia solapada entre forward y reverse, entonces al truncar de más habrá poco solapamiento, y menos datos serán procesados por dada2.

# Archivos donde se almacenarán las muestras filtradas y truncadas
filtFs <- file.path(path, "filtered", paste0(sample.names, "_F_filt.fastq.gz")) 
filtRs <- file.path(path, "filtered", paste0(sample.names, "_R_filt.fastq.gz"))

# Filtrado y truncado de datos
filterAndTrim_out <- filterAndTrim(fnFs, filtFs, fnRs, filtRs, truncLen=c(290,200), maxN=0, maxEE=c(2,2), truncQ=2, rm.phix=TRUE, compress=TRUE, multithread=TRUE)

# Almacenar resultado
save(filterAndTrim_out, file="filterAndTrim_out")

Al ser una operación costosa, se almacena el resultado en un archivo para evitar recalcular:

load("filterAndTrim_out")

El resultado de esta función es una matriz que contiene para cada muestra, la cantidad de secuencias que existían originalmente, y cuántas resultaron luego del filtrado.

Permite identificar cuantas lecturas fueron descartadas. Además, almacena en los archivos especificados (filtFs y filtRs) los resultados filtrados.

col_sum <- colSums(filterAndTrim_out)
filtered_reads <- col_sum[1] - col_sum[2]

message(sprintf("Fueron filtradas %s lecturas, un %%%.2f de las lecturas originales.", filtered_reads, filtered_reads * 100 / col_sum[1]))
Fueron filtradas 456939 lecturas, un %20.61 de las lecturas originales.

1.4 Aprendizaje de errores

Dada2 necesita conocer las probabilidades de errores para poder decidir si dos secuencias que se diferencian en 1 o 2 bases son la misma y haya ocurrido un error, o sean realmente dos secuencias diferentes.

Por lo tanto, se debe estimar los errores, y modelar las probabilidades de que hayan ocurrido transiciones (A ⟶ T, A ⟶ C, C ⟶ A, etc):

# forward
errF <- learnErrors(filtFs, multithread=TRUE)
save(errF, file="errF")

# reverse
errR <- learnErrors(filtRs, multithread=TRUE)
save(errR, file="errR")

Al ser una operación costosa, se almacena el resultado en un archivo para evitar recalcular:

load("errF")
load("errR")

Se puede observar que los errores son acordes a sus estimados, ya que presentan gráficos similares:

1.5 Derreplicacion

La derreplicacion consiste en contar las apariciones de cada secuencia en las muestras, lo cual es necesario para luego agrupar los ASVs encontrados:

# forward
derepFs <- derepFastq(filtFs, verbose=TRUE)
names(derepFs) <- sample.names
save(derepFs, file= "derepFs")

# reverse
derepRs <- derepFastq(filtRs, verbose=TRUE)
names(derepRs) <- sample.names
save(derepRs, file= "derepRs")

Para evitar el costoso cálculo:

load("derepFs")
load("derepRs")

Obteniéndose por muestra, una matriz con el conteo de cada secuencia, y sus scores promedio de calidad.

1.6 Identificar ASVs con DADA2

Ahora que se tienen los datos prontos para agrupar, se identifican los ASVs para las secuencias forward y reverse utilizando la función dada:

dadaFs <- dada(derepFs, err=errF, pool = TRUE , multithread=TRUE)
save(dadaFs, file="dadaFs")

#Reverse reads
dadaRs <- dada(derepRs, err=errR, pool = TRUE , multithread=TRUE)
save(dadaRs, file="dadaRs")

Para evitar el costoso cálculo:

1.7 Identificar cuantas veces aparece un ASV en cada muestra

Para esto, se debe unir los ASVs forward y reverse en una única secuencia:

mergers <- mergePairs(dadaFs, derepFs, dadaRs, derepRs, verbose=TRUE) 
save(mergers, file="mergers")

Para evitar el costoso cálculo:

El resultado de esta función es una lista de dataframes con información sobre la unión de las secuencias forward y reverse, por ejemplo: solapamiento, missmatch, etc.

Esta información es utilizada para contar las apariciones de cada ASV en cada muestra:

1.8 Filtrado de ASVs resultantes y quimeras

Las secuencias se esperan que tengan un largo dentro de un intervalo. Este intervalo se calcula a partir de los primers, largo de forward y reverse, porque se espera que las secuencias tengan un largo similar al solapamiento del forward y reverse.

En este caso el intervalo es entre 440 y 466, por lo tanto se filtran todos los ASV que no lo cumplan:

Además, es posible que existan quimeras en los datos. Las quimeras son secuencias malformadas y no existentes que pueden aparecer luego de la secuenciación, y deben ser filtradas:

seqtab.nochim <- removeBimeraDenovo(seqtab, method="consensus", multithread=TRUE, verbose=TRUE)
save(seqtab.nochim,file="seqtab.nochim")

Para evitar el costoso cálculo:

1.9 Resumen de información perdida durante el proceso

Es posible obtener la cantidad de ASVs forward y reverse (denoised), cantidad de secuencias resultantes del merge, y cantidad de ASVs resultantes luego de eliminar largos no esperados y que contengan quimeras.

Estos cálculos permiten identificar la información recortada durante el preprocesamiento de los datos:

# Funcion auxiliar para contar los valores unicos de una colección
getN <- function(x) sum(getUniques(x))

track <- cbind(filterAndTrim_out, sapply(dadaFs, getN), sapply(dadaRs, getN), sapply(mergers, getN), rowSums(seqtab.nochim))
colnames(track) <- c("input", "filtered", "denoisedF", "denoisedR", "merged", "nonchim") 
rownames(track) <- sample.names

# Mostrar resultados
head(track)
           input filtered denoisedF denoisedR merged nonchim
ERR4144822 51293    44055     43140     43110  39762   27904
ERR4144823 37976    34005     33267     33215  30645   21949
ERR4144824 31514    27413     26851     26763  24647   17679
ERR4144825 31934    28665     28023     28071  26052   19236
ERR4144826 26243    21528     21016     21058  19637   13124
ERR4144827 37867    33094     32246     32539  30534   20689
total_nonchim <- sum(track[, 'nonchim'])
total_input <-sum(track[, 'input'])
message(sprintf("Se perdieron un total de %s muestras, un %%%.2f",  total_input - total_nonchim, sum(total_nonchim * 100 / total_input)))
Se perdieron un total de 1040460 muestras, un %53.06

1.10 Unificar datos con Phyloseq

Phyloseq es un framework que ofrece funcionalidades para analizar los datos, por ejemplo diversidad alfa, beta, abundancias diferenciales, etc, en base a toda la información de las muestras que se hayan recolectado.

Se debe cargar la base de datos que contiene la taxonomía (informacion biologica) de los ASVs:

taxa_gtdb <- assignTaxonomy(seqtab.nochim, "", multithread=TRUE)

Para evitar el costoso cálculo:

load("taxa_gtdb")

Luego, importar las muestras que se vayan a utilizar:

sample_data <- as.data.frame(read.table("sampleData_alimentos.csv", header=TRUE, sep=","))
rownames(sample_data) <- sample_data[,1]

Finalmente, cargar los datos a Phyloseq:

physeq <- phyloseq(otu_table(seqtab.nochim, taxa_are_rows=FALSE),sample_data(sample_data),tax_table(taxa_gtdb)) 
save(physeq, file="physeq")

2. Interpretación de resultados

2.1 Análisis de muestras

Para el objetivo del estudio no es necesario analizar todas las muestras tomadas, es posible tomar un subset útil para la investigación. En este caso, son las muestras tomadas inicialmente de la piel de los cerdos, y en el recto anal.

ps <- subset_samples(physeq, Sampling.position %in% c("Sticking", "Anal swab"))
ps.prop <- transform_sample_counts(ps, function(OTU) OTU/sum(OTU))

message("Cantidad de muestras: ", nsamples(ps), "\n",
        "Cantidad de ASVs: ", ntaxa(ps), "\n",
        "Cantidad de ASVs no presentes en las muestras: ", ntaxa(prune_taxa(taxa_sums(ps) == 0, ps)))
Cantidad de muestras: 8
Cantidad de ASVs: 15413
Cantidad de ASVs no presentes en las muestras: 7606

Se filtran los ASV que no estan presentes en ninguna muestra, porque no son utiles:

ps <- prune_taxa(taxa_sums(ps) > 0, ps)

La distribución de ASV resultantes por muestra se puede apreciar en el siguiente gráfico:

reads_per_ASV <- data.frame(nreads=sort(taxa_sums(ps)), TRUE, SVs= 1:ntaxa(ps))

ggplot(data=reads_per_ASV,aes(x = SVs, y = nreads)) +
  geom_area (stat="identity", fill = "#69BE28") +
  scale_x_log10()

Como del granjero A se muestrearon dos cerdos, existen cerca del doble de lecturas que los demás granjeros.

# Filter reads
filtered_reads <- data.frame(
    nreads=sort(sample_sums(ps)), 
    Farmer=sample_data(ps)$Farmer,
    Position=sample_data(ps)$Sampling.position, 
    TRUE, 
    samples=sample_data(ps)$Farmer, 
    type = "Samples"
)

# Plot
ggplot(filtered_reads, aes(x=samples, y=nreads)) +
  geom_bar(aes(color=Position, fill=Position), stat="identity") + 
  facet_wrap(~Farmer, scales="free_x")

2.2 Abundancias relativas

# Graficar abundancias relativas por filo
plot_bar(ps.prop, x="Sample", fill="Phylum") +
  facet_wrap(~Farmer, scales="free_x") +
  geom_bar(aes(color=Phylum, fill=Phylum), stat="identity", position="stack")

En el gráfico anterior se puede ver en todas las muestras una gran presencia de bacteroidetes, las mismas son comunes en el intestino de muchos animales. Estas bacterias pueden desempeñar un papel importante en la digestión y la absorción de nutrientes en los animales, así como en la salud general de su sistema digestivo.

Además, se puede ver presencia de fusobacteriotas en las muestras. Son conocidas por su capacidad para causar infecciones en los animales, incluyendo infecciones orales, abscesos y enfermedades respiratorias. Por ejemplo, en los cerdos se ha informado que las Fusobacterium pueden estar implicadas en casos de necrosis hepática y abscesos. La infección con Fusobacterium puede ser el resultado de una lesión en la piel o una infección previa, lo que permite que las bacterias entren en el cuerpo y causen una infección.

Otras bacterias presentes en todas las muestras son del filo Spirochaetota, las mismas son conocidas por ser patógenas pero es común en el tracto gastrointestinal de los cerdos, aunque no se consideran patógenas en este caso para ellos.

# Abundancias relativas por género
tse <- makeTreeSummarizedExperimentFromPhyloseq(ps) 
tse <- transformSamples(tse, method = "relabundance")
tse <- addTaxonomyTree(tse)
tse_gen <- agglomerateByRank(tse, rank = "Genus")
rownames(tse_gen)<-sub("Genus:","", rownames(tse_gen))

plotAbundanceDensity(tse_gen, layout = "jitter", abund_values="relabundance", n = 20, point_size=1, point_shape=19, point_alpha=0.5,                                               colour_by="Farmer") + 
  scale_x_log10(label=scales::percent)

En este otro gráfico, se puede ver los porcentajes de abundancia relativa entre cada género.

Como por ejemplo, campylobacter está presente en un más de 10% de abundancia relativa en una muestra del cerdo perteneciente al granjero B. Campylobacter puede ser un patógeno que causa enfermedades gastrointestinales en humanos, por lo que la presencia de Campylobacter en cerdos puede plantear preocupaciones en términos de seguridad alimentaria. La bacteria puede transmitirse a los humanos a través del consumo de alimentos contaminados con Campylobacter, como carne cruda o insuficientemente cocida, así como a través del contacto directo con animales infectados.

# Abundancia relativa por familia
tse_family <- agglomerateByRank(tse, rank = "Family")
rownames(tse_family)<-sub("Family:","", rownames(tse_family))

plotAbundanceDensity(tse_family, layout = "jitter", abund_values="relabundance", n = 20, point_size=1, point_shape=19, point_alpha=0.5,                                               colour_by="Farmer") + scale_x_log10(label=scales::percent)

Las bacterias pertenecientes a la familia Moraxellaceae se encuentran presente en todas las muestras pero mayormente en los chanchos del granjero A y C. Moraxellaceae pueden causar problemas en la calidad de la carne de cerdo, como el olor rancio, lo que puede ser un problema importante para la industria cárnica.

Las bacterias enterobacteriaceae son patógenas y pueden causar enfermedades como la salmonella. La misma está más presente en las muestras del chancho del granjero B.

2.3 Diversidad alfa

La diversidad alfa en metabarcoding se refiere a la diversidad de especies o tipos de organismos presentes en una muestra de ADN biológico y se puede medir utilizando varios índices, como el índice de Simpson.


rarefied<-rarefy_even_depth(ps,min(sample_sums(ps)),rngseed=1)
tse_rarefied <- makeTreeSummarizedExperimentFromPhyloseq(rarefied)
# Uniformidad
tse_rarefied <- estimateEvenness(tse_rarefied, 
                        abund_values = "counts",
                        index="simpson")

plots <- lapply(c("simpson"),
                plotColData,
                object = tse_rarefied,
                x = "Farmer",
                colour_by = "Farmer")

plots <- lapply(plots, "+", 
                theme(axis.text.x = element_blank(),
                      axis.title.x = element_blank(),
                      axis.ticks.x = element_blank()))

plots[[1]] + plot_layout(guides = "collect")

La gráfica de diversidad alfa de Simpson muestra la riqueza de especies y la equitabilidad en una comunidad. En esta gráfica, la diversidad alfa se representa en el eje de las ordenadas, mientras que el tamaño de la muestra se representa en el eje de abscisas.

La diversidad de Simpson se expresa como un índice de dominancia y se calcula restando la suma de los cuadrados de las proporciones de cada especie en la comunidad de uno. Cuanto más cercano sea el índice de Simpson a 1, menor será la diversidad alfa, lo que indica una mayor dominancia de una o unas pocas especies en la comunidad. Por otro lado, cuanto más cercano sea el índice de Simpson a 0, mayor será la diversidad alfa, lo que indica que las especies están distribuidas de manera uniforme en la comunidad.


message("Indice simpson promedio del granjero B: ", mean(subset(x, Farmer == "B")$simpson))
Indice simpson promedio del granjero B: 0.200961048738534
message("Indice simpson promedio del granjero A: ", mean(subset(x, Farmer == "A")$simpson))
Indice simpson promedio del granjero A: 0.402348623504202

En este caso, las muestras del granjero B presentan un menor índice simpson promedio, por lo tanto cuenta con una mayor diversidad alfa.

Por otro lado, las muestras del granjero A presentan un mayor índice simpson que las demás muestras, por lo tanto cuentan con una diversidad alfa menor. Esto puede significar que existe menor cantidad de bacterias diferentes, o una gran proporción de las bacterias presentes son similares.

2.4 Diversidad beta

En metabarcoding, la diversidad beta se refiere a la variación en la composición de especies entre diferentes muestras.

# Aglomeracion por gènero manteniendo los NA
tse_genus <- agglomerateByRank(tse, rank = "Genus")
tse_genus_notNA <- tse_genus[!is.na(rowData(tse_genus)$Genus), ]
tse_genus_notNA <- runMDS(tse_genus_notNA, FUN = vegan::vegdist, method = "bray", name = "PCoA_BC", exprs_values = "counts")

# Create ggplot object
p <- plotReducedDim(tse_genus_notNA, "PCoA_BC", colour_by = "Farmer")

# Add explained variance for each axis
e <- attr(reducedDim(tse_genus_notNA, "PCoA_BC"), "eig");
rel_eig <- e/sum(e[e>0])          
p + labs(x = paste("PCoA 1 (", round(100 * rel_eig[[1]],1), "%", ")", sep = ""),
              y = paste("PCoA 2 (", round(100 * rel_eig[[2]],1), "%", ")", sep = ""))

Los puntos en la gráfica representan las muestras y su posición se basa en su similitud biológica. Las muestras que están más cerca en la gráfica se consideran más similares en términos de su composición biológica.

Analizando la gráfica se puede concluir que las muestras coinciden con lo esperado ya que entre ellas hay cierta distancia lo que refiere a que las muestras no tienen composición biológica similar.

3. Conclusión

Luego de analizar las muestras bacterianas de los cerdos de los tres granjeros, se encontró que el cerdo del granjero B tenía la mayor cantidad de Campylobacter y Chloroflexi en su tracto gastrointestinal. Esto puede ser preocupante ya que ambas bacterias se sabe que pueden causar enfermedades en los seres humanos.

La presencia de Campylobacter en los cerdos es particularmente preocupante ya que se ha demostrado que esta bacteria puede ser un importante factor de riesgo para la transmisión de enfermedades alimentarias. Además, la resistencia a los antimicrobianos.

La presencia de Chloroflexi en los cerdos también puede ser preocupante ya que se sabe que algunas especies pueden causar enfermedades respiratorias en los seres humanos. Además, se sabe que algunas bacterias Chloroflexi son resistentes a los antimicrobianos, lo que puede complicar el tratamiento de las enfermedades causadas por estas bacterias.

Por otro lado, se puede ver que la carne de los granjeros A y C tienen una mayor cantidad de Moraxellaceae que la carne del granjero B. La misma presenta un riesgo en términos de desperdicio alimenticio pero no de riesgo humano.

Contestando la pregunta disparadora de la investigación se podría decir que la carne del granjero B es la que mayor peligrosidad tiene.

LS0tCnRpdGxlOiAiRW50cmVnYSBGaW5hbCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCiMgSW50cm9kdWNjacOzbgoKRW4gZXN0ZSBub3RlYm9vayBwcmVzZW50YSBlbCBwcm9jZWRpbWllbnRvIGxsZXZhZG8gYSBjYWJvIHBhcmEgZWwgYW7DoWxpc2lzIGRlIGRhdG9zIGRlIG1ldGFiYXJjb2RpbmcsIGEgcGFydGlyIGVsIGFydMOtY3VsbyAqIltUaGUgc291cmNlcyBhbmQgdHJhbnNtaXNzaW9uIHJvdXRlcyBvZiBtaWNyb2JpYWwgcG9wdWxhdGlvbnMgdGhyb3VnaG91dCBhIG1lYXQgcHJvY2Vzc2luZyBmYWNpbGl0eV0oaHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1MjItMDIwLTAxMzYteikiKi4gRXN0ZSBhcnTDrWN1bG8gYnVzY2EgYW5hbGl6YXIgZSBpZGVudGlmaWNhciBmdWVudGVzIHBvdGVuY2lhbGVzIGRlIGNvbnRhbWluYWNpw7NuIG1pY3JvYmlhbmEgZW4gbGEgY2FybmUgZGUgY2VyZG9zLCBkdXJhbnRlIGVsIHByb2Nlc28gZGUgZmFlbmEgZW4gZWwgbWF0YWRlcm8uCgojIyBPYmpldGl2bwoKRWwgb2JqZXRpdm8gZGUgbnVlc3RybyBlc3R1ZGlvIHNlIHJlZHVjZSBhIGFuYWxpemFyIGPDs21vIGNhbWJpYSBsYSBjb21wb3NpY2nDs24gbWljcm9iaWFuYSBlbiBsYSBjYXJuZSBkZSBsb3MgY2VyZG9zIGRlIGxvcyBkaXN0aW50b3MgZ3Jhbmplcm9zLCBlIGlkZW50aWZpY2FyIHNpIGFsZ3VuYSBjb21wb3NpY2nDs24gcHJlc2VudGEgdW4gcGVsaWdybyBtYXlvciBwYXJhIGVsIG1hdGFkZXJvLgoKIyAxLiBQcmVwcm9jZXNhbWllbnRvIGRlIGxvcyBkYXRvcwoKUGFyYSBsbGV2YXIgYSBjYWJvIGVsIGFuw6FsaXNpcywgcHJpbWVybyBzZSBkZWJlbiBjYXJnYXIsIGZpbHRyYXIgeSBwcmVwcm9jZXNhciBsb3MgZGF0b3MgdG9tYWRvcyBlbiBlbCBtYXRhZGVybywgbG9zIGN1YWxlcyBmdWVyb24gZGVzY2FyZ2Fkb3MgZGVsIFtFdXJvcGVhbiBOdWNsZW90aWRlIEFyY2hpdmVdKGh0dHBzOi8vd3d3LmViaS5hYy51ay9lbmEvYnJvd3Nlci92aWV3L1BSSkVCMzc0MzQpLgoKIyMgMS4xIFNldHVwIGRlbCBhbWJpZW50ZSBkZSB0cmFiYWpvCgpTZSBoYXLDoSB1c28gZGUgcGFxdWV0ZXMgZGUgUiwgeSBsaWJyZXLDrWFzIG9wZW5zb3VyY2UgZGUgQmlvY29uZHVjdG9yIHBhcmEgZWwgcHJvY2VzYW1pZW50byBkZSBkYXRvcy4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoYW1wdmlzMikKbGlicmFyeShtaWEpCmxpYnJhcnkobWlhVml6KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoQmlvc3RyaW5ncykKbGlicmFyeShwaHlsb3NlcSkKbGlicmFyeShBTkNPTUJDKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShnZ3NpZ25pZikKbGlicmFyeShzY2F0ZXIpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShkYWRhMikKbGlicmFyeSh2ZWdhbikKYGBgCgpBZGVtw6FzLCBzZSBkZWJlIHNldGVhciBlbCBkaXJlY3RvcmlvIHF1ZSBjb250ZW5nYSBsb3MgYXJjaGl2b3MgKC5mYXN0cSkgY29uIGxvcyBkYXRvcyBkZXNjYXJnYWRvczoKCmBgYHtyfQojIFJlZW1wbGF6YXIgcG9yIGRpcmVjdG9yaW8gdXRpbGl6YWRvCnNldHdkKCJ+L1VDVS9hbGltZW50b3MxLyIpCnBhdGggPC0gZ2V0d2QoKQpgYGAKCiMjIDEuMiBSZWdpc3RyYXIgbWV0YWluZm9ybWFjacOzbiBkZSBsYXMgbXVlc3RyYXMKCkRpZmVyZW50ZXMgbXVlc3RyYXMgZnVlcm9uIHRvbWFkYXMgZHVyYW50ZSBlbCBwcm9jZXNvIGRlIGZhZW5hLCBlbiBkaWZlcmVudGVzIGNlcmRvcywgbHVnYXJlcyB5IGhlcnJhbWllbnRhcy4gTGFzIGxlY3R1cmFzIHJlYWxpemFkYXMgZW4gY2FkYSBtdWVzdHJhIGZ1ZXJvbiBhbG1hY2VuYWRhcyBlbiBhcmNoaXZvcyAuZmFzdHEsIGxvcyBjdWFsZXMgY29udGllbmVuIGxhcyBzZWN1ZW5jaWFzIGRlIEFETiB5IGNhbGlkYWQgZGUgc3VzIHJlc3BlY3RpdmFzIGxlY3R1cmFzLgoKRGViaWRvIGEgcXVlIGxvcyBhcmNoaXZvcyBjb250aWVuZW4gZGVtYXNpYWRhIGluZm9ybWFjacOzbiwgbnVuY2Egc2UgY2FyZ2FuIGVuIG1lbW9yaWEuIFNpbm8gcXVlIGxhcyBmdW5jaW9uZXMgdXRpbGl6YWRhcyBzZSBlbmNhcmdhbiBkZSBidXNjYXIgbG9zIGRhdG9zIGVuIGNhZGEgYXJjaGl2byBjdWFuZG8gc2VhIG5lY2VzYXJpbywgYSBwYXJ0aXIgZGUgc3UgcnV0YS4gRW50b25jZXMsIHNlIGd1YXJkYSBlbCBsaXN0YWRvIGRlIHJ1dGFzIGhhY2lhIGxvcyBhcmNoaXZvcyBkZSB0b2RhcyBsYXMgbGVjdHVyYXMgZm9yd2FyZCAoZikgeSByZXZlcnNlIChyKS4KCmBgYHtyfQojIEFyY2hpdm9zIHF1ZSBjb250ZW5nYW4gJ18xLmZhc3RxJyByZXByZXNlbnRhbiBsZWN0dXJhcyBmCmZuRnMgPC0gc29ydChsaXN0LmZpbGVzKHBhdGgsIHBhdHRlcm49Il8xLmZhc3RxIiwgZnVsbC5uYW1lcyA9IFRSVUUpKSAKIyBBcmNoaXZvcyBxdWUgY29udGVuZ2FuICdfMi5mYXN0cScgcmVwcmVzZW50YW4gbGVjdHVyYXMgcgpmblJzIDwtIHNvcnQobGlzdC5maWxlcyhwYXRoLCBwYXR0ZXJuPSJfMi5mYXN0cSIsIGZ1bGwubmFtZXMgPSBUUlVFKSkKYGBgCgpMdWVnbywgc2UgZXh0cmFlbiBsb3Mgbm9tYnJlcyBkZSBjYWRhIG11ZXN0cmEgYSBwYXJ0aXIgZGVsIG5vbWJyZSBkZSBsb3MgYXJjaGl2b3M6CgpgYGB7cn0KIyBGb3JtYXRvOiBTQU1QTEVOQU1FX1hYWC5mYXN0cQpzYW1wbGUubmFtZXMgPC0gc2FwcGx5KHN0cnNwbGl0KGJhc2VuYW1lKGZuRnMpLCAiXyIpLCBgW2AsIDEpCmBgYAoKRmluYWxtZW50ZSBzZSBpbXBvcnRhIGxhIG1ldGFkYXRhIGRlIGNhZGEgbXVlc3RyYToKCmBgYHtyfQpzYW1wbGUubWV0YWRhdGEgPC0gcmVhZC5jc3YoInNhbXBsZURhdGFfYWxpbWVudG9zLmNzdiIpCmBgYAoKIyMgMS4zIENoZXF1ZW8geSBmaWx0cmFkbyBkZSBjYWxpZGFkCgpQYXJhIGZpbHRyYXIgbGVjdHVyYXMgZGUgbWFsYSBjYWxpZGFkLCBzZSBncmFmaWNhIGxhIGNhbGlkYWQgZGUgbGFzIG1pc21hcyBwYXJhIGRlZmluaXIgZG9uZGUgdHJ1bmNhcmxhcy4KCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgRm9yd2FyZCByZWFkcwpwbG90UXVhbGl0eVByb2ZpbGUoZm5Gc1sxOjJdKSAKIyBSZXZlcnNlIHJlYWRzCnBsb3RRdWFsaXR5UHJvZmlsZShmblJzWzE6Ml0pCmBgYAoKRXMgY2FyYWN0ZXLDrXN0aWNvIGRlIHVuYSBsZWN0dXJhIElsbHVtaW5hIHF1ZSBhbCBmaW5hbCBkZSBsYSBzZWN1ZW5jaWEsIGVsIHNjb3JlIGRlIGNhbGlkYWQgZGVjYWlnYSwgeSBxdWUgbGEgbGVjdHVyYSByZXZlcnNlIHRlbmdhIG1lbm9yIGNhbGlkYWQgcXVlIGxhIGZvcndhcmQuIEVudG9uY2VzIHNlIHRydW5jYSBsYXMgcmVnaW9uZXMgZGUgbWFsYSBjYWxpZGFkLgoKRW4gZXN0ZSBjYXNvLCBzZSB1dGlsaXphcsOhbiBsYXMgcmVnaW9uZXMgZG9uZGUgZWwgc2NvcmUgc2VhIG1heW9yIGEgMzAgYXByb3hpbWFkYW1lbnRlLiBQb3IgbG8gdGFudG8gc2UgdHJ1bmNhcmFuIGxhcyBiYXNlcyBwb3N0ZXJpb3JlcyBhIDI5MCBlbiBsZWN0dXJhcyBmb3J3YXJkLCB5IDIwMCBwYXJhIHJldmVyc2UuCgpQYXJhIHJlYWxpemFyIGVsIGZpbHRyYWRvIHkgdHJ1bmNhZG8gZXMgw7p0aWwgbGEgZnVuY2nDs24gZmlsdGVyQW5kVHJpbSwgbGEgY3VhbCBwZXJtaXRlOgoKLSAgIENvcnRhciByZWdpc3Ryb3MgZGUgbWFsYSBjYWxpZGFkCgotICAgRmlsdHJhciBsZWN0dXJhcyBjb24gZGF0b3MgbnVsb3MKCi0gICBDb3J0YXIgcHJpbWVycwoKVXRpbGl6YW5kbyBsb3MgcGFyw6FtZXRyb3M6CgotICAgdHJ1bmNMZW49YygyOTAsIDIwMCkg4p+2IExlY3R1cmFzIGNvbiB0YW1hw7FvIG1lbm9yIGEgMjkwIChmb3J3YXJkKSBvIDIwMCAocmV2ZXJzZSkgc29uIGRlc2NhcnRhZGFzCgotICAgbWF4RUU9KDIsMikg4p+2IExlY3R1cmFzIGNvbiBtYXMgZGUgMiBlcnJvcmVzIGVzcGVyYWRvcyBzb24gZGVzY2FydGFkYXMKCi0gICBtYXhOPTAg4p+2IExlY3R1cmFzIGNvbiBkYXRvcyBudWxvcyBzb24gZGVzY2FydGFkYXMKClNlIGRlYmUgdGVuZXIgY3VpZGFkbyBkZSBubyB0cnVuY2FyIGVuIGV4Y2VzbywgZGViaWRvIGEgcXVlIGRhZGEyIHNvbG8gdGllbmUgZW4gY3VlbnRhIGxhIHNlY3VlbmNpYSBzb2xhcGFkYSBlbnRyZSBmb3J3YXJkIHkgcmV2ZXJzZSwgZW50b25jZXMgYWwgdHJ1bmNhciBkZSBtw6FzIGhhYnLDoSBwb2NvIHNvbGFwYW1pZW50bywgeSBtZW5vcyBkYXRvcyBzZXLDoW4gcHJvY2VzYWRvcyBwb3IgZGFkYTIuCgpgYGB7ciBldmFsPUZBTFNFfQojIEFyY2hpdm9zIGRvbmRlIHNlIGFsbWFjZW5hcsOhbiBsYXMgbXVlc3RyYXMgZmlsdHJhZGFzIHkgdHJ1bmNhZGFzCmZpbHRGcyA8LSBmaWxlLnBhdGgocGF0aCwgImZpbHRlcmVkIiwgcGFzdGUwKHNhbXBsZS5uYW1lcywgIl9GX2ZpbHQuZmFzdHEuZ3oiKSkgCmZpbHRScyA8LSBmaWxlLnBhdGgocGF0aCwgImZpbHRlcmVkIiwgcGFzdGUwKHNhbXBsZS5uYW1lcywgIl9SX2ZpbHQuZmFzdHEuZ3oiKSkKCiMgRmlsdHJhZG8geSB0cnVuY2FkbyBkZSBkYXRvcwpmaWx0ZXJBbmRUcmltX291dCA8LSBmaWx0ZXJBbmRUcmltKGZuRnMsIGZpbHRGcywgZm5ScywgZmlsdFJzLCB0cnVuY0xlbj1jKDI5MCwyMDApLCBtYXhOPTAsIG1heEVFPWMoMiwyKSwgdHJ1bmNRPTIsIHJtLnBoaXg9VFJVRSwgY29tcHJlc3M9VFJVRSwgbXVsdGl0aHJlYWQ9VFJVRSkKCiMgQWxtYWNlbmFyIHJlc3VsdGFkbwpzYXZlKGZpbHRlckFuZFRyaW1fb3V0LCBmaWxlPSJmaWx0ZXJBbmRUcmltX291dCIpCmBgYAoKQWwgc2VyIHVuYSBvcGVyYWNpw7NuIGNvc3Rvc2EsIHNlIGFsbWFjZW5hIGVsIHJlc3VsdGFkbyBlbiB1biBhcmNoaXZvIHBhcmEgZXZpdGFyIHJlY2FsY3VsYXI6CgpgYGB7cn0KbG9hZCgiZmlsdGVyQW5kVHJpbV9vdXQiKQpgYGAKCkVsIHJlc3VsdGFkbyBkZSBlc3RhIGZ1bmNpw7NuIGVzIHVuYSBtYXRyaXogcXVlIGNvbnRpZW5lIHBhcmEgY2FkYSBtdWVzdHJhLCBsYSBjYW50aWRhZCBkZSBzZWN1ZW5jaWFzIHF1ZSBleGlzdMOtYW4gb3JpZ2luYWxtZW50ZSwgeSBjdcOhbnRhcyByZXN1bHRhcm9uIGx1ZWdvIGRlbCBmaWx0cmFkby4KClBlcm1pdGUgaWRlbnRpZmljYXIgY3VhbnRhcyBsZWN0dXJhcyBmdWVyb24gZGVzY2FydGFkYXMuIEFkZW3DoXMsIGFsbWFjZW5hIGVuIGxvcyBhcmNoaXZvcyBlc3BlY2lmaWNhZG9zIChmaWx0RnMgeSBmaWx0UnMpIGxvcyByZXN1bHRhZG9zIGZpbHRyYWRvcy4KCmBgYHtyfQpjb2xfc3VtIDwtIGNvbFN1bXMoZmlsdGVyQW5kVHJpbV9vdXQpCmZpbHRlcmVkX3JlYWRzIDwtIGNvbF9zdW1bMV0gLSBjb2xfc3VtWzJdCgptZXNzYWdlKHNwcmludGYoIkZ1ZXJvbiBmaWx0cmFkYXMgJXMgbGVjdHVyYXMsIHVuICUlJS4yZiBkZSBsYXMgbGVjdHVyYXMgb3JpZ2luYWxlcy4iLCBmaWx0ZXJlZF9yZWFkcywgZmlsdGVyZWRfcmVhZHMgKiAxMDAgLyBjb2xfc3VtWzFdKSkKYGBgCgojIyAxLjQgQXByZW5kaXphamUgZGUgZXJyb3JlcwoKRGFkYTIgbmVjZXNpdGEgY29ub2NlciBsYXMgcHJvYmFiaWxpZGFkZXMgZGUgZXJyb3JlcyBwYXJhIHBvZGVyIGRlY2lkaXIgc2kgZG9zIHNlY3VlbmNpYXMgcXVlIHNlIGRpZmVyZW5jaWFuIGVuIDEgbyAyIGJhc2VzIHNvbiBsYSBtaXNtYSB5IGhheWEgb2N1cnJpZG8gdW4gZXJyb3IsIG8gc2VhbiByZWFsbWVudGUgZG9zIHNlY3VlbmNpYXMgZGlmZXJlbnRlcy4KClBvciBsbyB0YW50bywgc2UgZGViZSBlc3RpbWFyIGxvcyBlcnJvcmVzLCB5IG1vZGVsYXIgbGFzIHByb2JhYmlsaWRhZGVzIGRlIHF1ZSBoYXlhbiBvY3VycmlkbyB0cmFuc2ljaW9uZXMgKEEg4p+2IFQsIEEg4p+2IEMsIEMg4p+2IEEsIGV0Yyk6CgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIGZvcndhcmQKZXJyRiA8LSBsZWFybkVycm9ycyhmaWx0RnMsIG11bHRpdGhyZWFkPVRSVUUpCnNhdmUoZXJyRiwgZmlsZT0iZXJyRiIpCgojIHJldmVyc2UKZXJyUiA8LSBsZWFybkVycm9ycyhmaWx0UnMsIG11bHRpdGhyZWFkPVRSVUUpCnNhdmUoZXJyUiwgZmlsZT0iZXJyUiIpCmBgYAoKQWwgc2VyIHVuYSBvcGVyYWNpw7NuIGNvc3Rvc2EsIHNlIGFsbWFjZW5hIGVsIHJlc3VsdGFkbyBlbiB1biBhcmNoaXZvIHBhcmEgZXZpdGFyIHJlY2FsY3VsYXI6CgpgYGB7cn0KbG9hZCgiZXJyRiIpCmxvYWQoImVyclIiKQpgYGAKClNlIHB1ZWRlIG9ic2VydmFyIHF1ZSBsb3MgZXJyb3JlcyBzb24gYWNvcmRlcyBhIHN1cyBlc3RpbWFkb3MsIHlhIHF1ZSBwcmVzZW50YW4gZ3LDoWZpY29zIHNpbWlsYXJlczoKCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnBsb3RFcnJvcnMoZXJyRiwgbm9taW5hbFE9VFJVRSkKcGxvdEVycm9ycyhlcnJSLCBub21pbmFsUT1UUlVFKQpgYGAKCiMjIDEuNSBEZXJyZXBsaWNhY2lvbgoKTGEgZGVycmVwbGljYWNpb24gY29uc2lzdGUgZW4gY29udGFyIGxhcyBhcGFyaWNpb25lcyBkZSBjYWRhIHNlY3VlbmNpYSBlbiBsYXMgbXVlc3RyYXMsIGxvIGN1YWwgZXMgbmVjZXNhcmlvIHBhcmEgbHVlZ28gYWdydXBhciBsb3MgQVNWcyBlbmNvbnRyYWRvczoKCmBgYHtyIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgZm9yd2FyZApkZXJlcEZzIDwtIGRlcmVwRmFzdHEoZmlsdEZzLCB2ZXJib3NlPVRSVUUpCm5hbWVzKGRlcmVwRnMpIDwtIHNhbXBsZS5uYW1lcwpzYXZlKGRlcmVwRnMsIGZpbGU9ICJkZXJlcEZzIikKCiMgcmV2ZXJzZQpkZXJlcFJzIDwtIGRlcmVwRmFzdHEoZmlsdFJzLCB2ZXJib3NlPVRSVUUpCm5hbWVzKGRlcmVwUnMpIDwtIHNhbXBsZS5uYW1lcwpzYXZlKGRlcmVwUnMsIGZpbGU9ICJkZXJlcFJzIikKYGBgCgpQYXJhIGV2aXRhciBlbCBjb3N0b3NvIGPDoWxjdWxvOgoKYGBge3J9CmxvYWQoImRlcmVwRnMiKQpsb2FkKCJkZXJlcFJzIikKYGBgCgpPYnRlbmnDqW5kb3NlIHBvciBtdWVzdHJhLCB1bmEgbWF0cml6IGNvbiBlbCBjb250ZW8gZGUgY2FkYSBzZWN1ZW5jaWEsIHkgc3VzIHNjb3JlcyBwcm9tZWRpbyBkZSBjYWxpZGFkLgoKIyMgMS42IElkZW50aWZpY2FyIEFTVnMgY29uIERBREEyCgpBaG9yYSBxdWUgc2UgdGllbmVuIGxvcyBkYXRvcyBwcm9udG9zIHBhcmEgYWdydXBhciwgc2UgaWRlbnRpZmljYW4gbG9zIEFTVnMgcGFyYSBsYXMgc2VjdWVuY2lhcyBmb3J3YXJkIHkgcmV2ZXJzZSB1dGlsaXphbmRvIGxhIGZ1bmNpw7NuIGRhZGE6CgpgYGB7ciBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpkYWRhRnMgPC0gZGFkYShkZXJlcEZzLCBlcnI9ZXJyRiwgcG9vbCA9IFRSVUUgLCBtdWx0aXRocmVhZD1UUlVFKQpzYXZlKGRhZGFGcywgZmlsZT0iZGFkYUZzIikKCiNSZXZlcnNlIHJlYWRzCmRhZGFScyA8LSBkYWRhKGRlcmVwUnMsIGVycj1lcnJSLCBwb29sID0gVFJVRSAsIG11bHRpdGhyZWFkPVRSVUUpCnNhdmUoZGFkYVJzLCBmaWxlPSJkYWRhUnMiKQpgYGAKClBhcmEgZXZpdGFyIGVsIGNvc3Rvc28gY8OhbGN1bG86CgpgYGB7cn0KbG9hZCgiZGFkYUZzIikKbG9hZCgiZGFkYVJzIikKYGBgCgojIyAxLjcgSWRlbnRpZmljYXIgY3VhbnRhcyB2ZWNlcyBhcGFyZWNlIHVuIEFTViBlbiBjYWRhIG11ZXN0cmEKClBhcmEgZXN0bywgc2UgZGViZSB1bmlyIGxvcyBBU1ZzIGZvcndhcmQgeSByZXZlcnNlIGVuIHVuYSDDum5pY2Egc2VjdWVuY2lhOgoKYGBge3IgZXZhbD1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbWVyZ2VycyA8LSBtZXJnZVBhaXJzKGRhZGFGcywgZGVyZXBGcywgZGFkYVJzLCBkZXJlcFJzLCB2ZXJib3NlPVRSVUUpIApzYXZlKG1lcmdlcnMsIGZpbGU9Im1lcmdlcnMiKQpgYGAKClBhcmEgZXZpdGFyIGVsIGNvc3Rvc28gY8OhbGN1bG86CgpgYGB7cn0KbG9hZCgibWVyZ2VycyIpCmBgYAoKRWwgcmVzdWx0YWRvIGRlIGVzdGEgZnVuY2nDs24gZXMgdW5hIGxpc3RhIGRlIGRhdGFmcmFtZXMgY29uIGluZm9ybWFjacOzbiBzb2JyZSBsYSB1bmnDs24gZGUgbGFzIHNlY3VlbmNpYXMgZm9yd2FyZCB5IHJldmVyc2UsIHBvciBlamVtcGxvOiBzb2xhcGFtaWVudG8sIG1pc3NtYXRjaCwgZXRjLgoKRXN0YSBpbmZvcm1hY2nDs24gZXMgdXRpbGl6YWRhIHBhcmEgY29udGFyIGxhcyBhcGFyaWNpb25lcyBkZSBjYWRhIEFTViBlbiBjYWRhIG11ZXN0cmE6CgpgYGB7cn0Kc2VxdGFiIDwtIG1ha2VTZXF1ZW5jZVRhYmxlKG1lcmdlcnMpCmBgYAoKIyMgMS44IEZpbHRyYWRvIGRlIEFTVnMgcmVzdWx0YW50ZXMgeSBxdWltZXJhcwoKTGFzIHNlY3VlbmNpYXMgc2UgZXNwZXJhbiBxdWUgdGVuZ2FuIHVuIGxhcmdvIGRlbnRybyBkZSB1biBpbnRlcnZhbG8uIEVzdGUgaW50ZXJ2YWxvIHNlIGNhbGN1bGEgYSBwYXJ0aXIgZGUgbG9zIHByaW1lcnMsIGxhcmdvIGRlIGZvcndhcmQgeSByZXZlcnNlLCBwb3JxdWUgc2UgZXNwZXJhIHF1ZSBsYXMgc2VjdWVuY2lhcyB0ZW5nYW4gdW4gbGFyZ28gc2ltaWxhciBhbCBzb2xhcGFtaWVudG8gZGVsIGZvcndhcmQgeSByZXZlcnNlLgoKRW4gZXN0ZSBjYXNvIGVsIGludGVydmFsbyBlcyBlbnRyZSA0NDAgeSA0NjYsIHBvciBsbyB0YW50byBzZSBmaWx0cmFuIHRvZG9zIGxvcyBBU1YgcXVlIG5vIGxvIGN1bXBsYW46CgpgYGB7cn0Kc2VxdGFiIDwtIHNlcXRhYlssbmNoYXIoY29sbmFtZXMoc2VxdGFiKSkgJWluJSBzZXEoNDQwLDQ2NildCmBgYAoKQWRlbcOhcywgZXMgcG9zaWJsZSBxdWUgZXhpc3RhbiBxdWltZXJhcyBlbiBsb3MgZGF0b3MuIExhcyBxdWltZXJhcyBzb24gc2VjdWVuY2lhcyBtYWxmb3JtYWRhcyB5IG5vIGV4aXN0ZW50ZXMgcXVlIHB1ZWRlbiBhcGFyZWNlciBsdWVnbyBkZSBsYSBzZWN1ZW5jaWFjacOzbiwgeSBkZWJlbiBzZXIgZmlsdHJhZGFzOgoKYGBge3IgZXZhbD1GQUxTRX0Kc2VxdGFiLm5vY2hpbSA8LSByZW1vdmVCaW1lcmFEZW5vdm8oc2VxdGFiLCBtZXRob2Q9ImNvbnNlbnN1cyIsIG11bHRpdGhyZWFkPVRSVUUsIHZlcmJvc2U9VFJVRSkKc2F2ZShzZXF0YWIubm9jaGltLGZpbGU9InNlcXRhYi5ub2NoaW0iKQpgYGAKClBhcmEgZXZpdGFyIGVsIGNvc3Rvc28gY8OhbGN1bG86CgpgYGB7cn0KbG9hZCgic2VxdGFiLm5vY2hpbSIpCmBgYAoKIyMgMS45IFJlc3VtZW4gZGUgaW5mb3JtYWNpw7NuIHBlcmRpZGEgZHVyYW50ZSBlbCBwcm9jZXNvCgpFcyBwb3NpYmxlIG9idGVuZXIgbGEgY2FudGlkYWQgZGUgQVNWcyBmb3J3YXJkIHkgcmV2ZXJzZSAoZGVub2lzZWQpLCBjYW50aWRhZCBkZSBzZWN1ZW5jaWFzIHJlc3VsdGFudGVzIGRlbCBtZXJnZSwgeSBjYW50aWRhZCBkZSBBU1ZzIHJlc3VsdGFudGVzIGx1ZWdvIGRlIGVsaW1pbmFyIGxhcmdvcyBubyBlc3BlcmFkb3MgeSBxdWUgY29udGVuZ2FuIHF1aW1lcmFzLgoKRXN0b3MgY8OhbGN1bG9zIHBlcm1pdGVuIGlkZW50aWZpY2FyIGxhIGluZm9ybWFjacOzbiByZWNvcnRhZGEgZHVyYW50ZSBlbCBwcmVwcm9jZXNhbWllbnRvIGRlIGxvcyBkYXRvczoKCmBgYHtyfQojIEZ1bmNpb24gYXV4aWxpYXIgcGFyYSBjb250YXIgbG9zIHZhbG9yZXMgdW5pY29zIGRlIHVuYSBjb2xlY2Npw7NuCmdldE4gPC0gZnVuY3Rpb24oeCkgc3VtKGdldFVuaXF1ZXMoeCkpCgp0cmFjayA8LSBjYmluZChmaWx0ZXJBbmRUcmltX291dCwgc2FwcGx5KGRhZGFGcywgZ2V0TiksIHNhcHBseShkYWRhUnMsIGdldE4pLCBzYXBwbHkobWVyZ2VycywgZ2V0TiksIHJvd1N1bXMoc2VxdGFiLm5vY2hpbSkpCmNvbG5hbWVzKHRyYWNrKSA8LSBjKCJpbnB1dCIsICJmaWx0ZXJlZCIsICJkZW5vaXNlZEYiLCAiZGVub2lzZWRSIiwgIm1lcmdlZCIsICJub25jaGltIikgCnJvd25hbWVzKHRyYWNrKSA8LSBzYW1wbGUubmFtZXMKCiMgTW9zdHJhciByZXN1bHRhZG9zCmhlYWQodHJhY2spCnRvdGFsX25vbmNoaW0gPC0gc3VtKHRyYWNrWywgJ25vbmNoaW0nXSkKdG90YWxfaW5wdXQgPC1zdW0odHJhY2tbLCAnaW5wdXQnXSkKbWVzc2FnZShzcHJpbnRmKCJTZSBwZXJkaWVyb24gdW4gdG90YWwgZGUgJXMgbXVlc3RyYXMsIHVuICUlJS4yZiIsICB0b3RhbF9pbnB1dCAtIHRvdGFsX25vbmNoaW0sIHN1bSh0b3RhbF9ub25jaGltICogMTAwIC8gdG90YWxfaW5wdXQpKSkKYGBgCgojIyAxLjEwIFVuaWZpY2FyIGRhdG9zIGNvbiBQaHlsb3NlcQoKUGh5bG9zZXEgZXMgdW4gZnJhbWV3b3JrIHF1ZSBvZnJlY2UgZnVuY2lvbmFsaWRhZGVzIHBhcmEgYW5hbGl6YXIgbG9zIGRhdG9zLCBwb3IgZWplbXBsbyBkaXZlcnNpZGFkIGFsZmEsIGJldGEsIGFidW5kYW5jaWFzIGRpZmVyZW5jaWFsZXMsIGV0YywgZW4gYmFzZSBhIHRvZGEgbGEgaW5mb3JtYWNpw7NuIGRlIGxhcyBtdWVzdHJhcyBxdWUgc2UgaGF5YW4gcmVjb2xlY3RhZG8uCgpTZSBkZWJlIGNhcmdhciBsYSBiYXNlIGRlIGRhdG9zIHF1ZSBjb250aWVuZSBsYSB0YXhvbm9tw61hIChpbmZvcm1hY2lvbiBiaW9sb2dpY2EpIGRlIGxvcyBBU1ZzOgoKYGBge3IgZXZhbD1GQUxTRX0KdGF4YV9ndGRiIDwtIGFzc2lnblRheG9ub215KHNlcXRhYi5ub2NoaW0sICIiLCBtdWx0aXRocmVhZD1UUlVFKQpgYGAKClBhcmEgZXZpdGFyIGVsIGNvc3Rvc28gY8OhbGN1bG86CgpgYGB7cn0KbG9hZCgidGF4YV9ndGRiIikKYGBgCgpMdWVnbywgaW1wb3J0YXIgbGFzIG11ZXN0cmFzIHF1ZSBzZSB2YXlhbiBhIHV0aWxpemFyOgoKYGBge3J9CnNhbXBsZV9kYXRhIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZSgic2FtcGxlRGF0YV9hbGltZW50b3MuY3N2IiwgaGVhZGVyPVRSVUUsIHNlcD0iLCIpKQpyb3duYW1lcyhzYW1wbGVfZGF0YSkgPC0gc2FtcGxlX2RhdGFbLDFdCmBgYAoKRmluYWxtZW50ZSwgY2FyZ2FyIGxvcyBkYXRvcyBhIFBoeWxvc2VxOgoKYGBge3J9CnBoeXNlcSA8LSBwaHlsb3NlcShvdHVfdGFibGUoc2VxdGFiLm5vY2hpbSwgdGF4YV9hcmVfcm93cz1GQUxTRSksc2FtcGxlX2RhdGEoc2FtcGxlX2RhdGEpLHRheF90YWJsZSh0YXhhX2d0ZGIpKSAKc2F2ZShwaHlzZXEsIGZpbGU9InBoeXNlcSIpCmBgYAoKIyAyLiBJbnRlcnByZXRhY2nDs24gZGUgcmVzdWx0YWRvcwoKIyMgMi4xIEFuw6FsaXNpcyBkZSBtdWVzdHJhcwoKUGFyYSBlbCBvYmpldGl2byBkZWwgZXN0dWRpbyBubyBlcyBuZWNlc2FyaW8gYW5hbGl6YXIgdG9kYXMgbGFzIG11ZXN0cmFzIHRvbWFkYXMsIGVzIHBvc2libGUgdG9tYXIgdW4gc3Vic2V0IMO6dGlsIHBhcmEgbGEgaW52ZXN0aWdhY2nDs24uIEVuIGVzdGUgY2Fzbywgc29uIGxhcyBtdWVzdHJhcyB0b21hZGFzIGluaWNpYWxtZW50ZSBkZSBsYSBwaWVsIGRlIGxvcyBjZXJkb3MsIHkgZW4gZWwgcmVjdG8gYW5hbC4KCmBgYHtyIHNwbGl0PUZBTFNFfQpwcyA8LSBzdWJzZXRfc2FtcGxlcyhwaHlzZXEsIFNhbXBsaW5nLnBvc2l0aW9uICVpbiUgYygiU3RpY2tpbmciLCAiQW5hbCBzd2FiIikpCnBzLnByb3AgPC0gdHJhbnNmb3JtX3NhbXBsZV9jb3VudHMocHMsIGZ1bmN0aW9uKE9UVSkgT1RVL3N1bShPVFUpKQoKbWVzc2FnZSgiQ2FudGlkYWQgZGUgbXVlc3RyYXM6ICIsIG5zYW1wbGVzKHBzKSwgIlxuIiwKICAgICAgICAiQ2FudGlkYWQgZGUgQVNWczogIiwgbnRheGEocHMpLCAiXG4iLAogICAgICAgICJDYW50aWRhZCBkZSBBU1ZzIG5vIHByZXNlbnRlcyBlbiBsYXMgbXVlc3RyYXM6ICIsIG50YXhhKHBydW5lX3RheGEodGF4YV9zdW1zKHBzKSA9PSAwLCBwcykpKQpgYGAKClNlIGZpbHRyYW4gbG9zIEFTViBxdWUgbm8gZXN0YW4gcHJlc2VudGVzIGVuIG5pbmd1bmEgbXVlc3RyYSwgcG9ycXVlIG5vIHNvbiB1dGlsZXM6CgpgYGB7cn0KcHMgPC0gcHJ1bmVfdGF4YSh0YXhhX3N1bXMocHMpID4gMCwgcHMpCmBgYAoKTGEgZGlzdHJpYnVjacOzbiBkZSBBU1YgcmVzdWx0YW50ZXMgcG9yIG11ZXN0cmEgc2UgcHVlZGUgYXByZWNpYXIgZW4gZWwgc2lndWllbnRlIGdyw6FmaWNvOgoKYGBge3J9CnJlYWRzX3Blcl9BU1YgPC0gZGF0YS5mcmFtZShucmVhZHM9c29ydCh0YXhhX3N1bXMocHMpKSwgVFJVRSwgU1ZzPSAxOm50YXhhKHBzKSkKCmdncGxvdChkYXRhPXJlYWRzX3Blcl9BU1YsYWVzKHggPSBTVnMsIHkgPSBucmVhZHMpKSArCiAgZ2VvbV9hcmVhIChzdGF0PSJpZGVudGl0eSIsIGZpbGwgPSAiIzY5QkUyOCIpICsKICBzY2FsZV94X2xvZzEwKCkKYGBgCgpDb21vIGRlbCBncmFuamVybyBBIHNlIG11ZXN0cmVhcm9uIGRvcyBjZXJkb3MsIGV4aXN0ZW4gY2VyY2EgZGVsIGRvYmxlIGRlIGxlY3R1cmFzIHF1ZSBsb3MgZGVtw6FzIGdyYW5qZXJvcy4KCmBgYHtyfQojIEZpbHRlciByZWFkcwpmaWx0ZXJlZF9yZWFkcyA8LSBkYXRhLmZyYW1lKAogICAgbnJlYWRzPXNvcnQoc2FtcGxlX3N1bXMocHMpKSwgCiAgICBGYXJtZXI9c2FtcGxlX2RhdGEocHMpJEZhcm1lciwKICAgIFBvc2l0aW9uPXNhbXBsZV9kYXRhKHBzKSRTYW1wbGluZy5wb3NpdGlvbiwgCiAgICBUUlVFLCAKICAgIHNhbXBsZXM9c2FtcGxlX2RhdGEocHMpJEZhcm1lciwgCiAgICB0eXBlID0gIlNhbXBsZXMiCikKCiMgUGxvdApnZ3Bsb3QoZmlsdGVyZWRfcmVhZHMsIGFlcyh4PXNhbXBsZXMsIHk9bnJlYWRzKSkgKwogIGdlb21fYmFyKGFlcyhjb2xvcj1Qb3NpdGlvbiwgZmlsbD1Qb3NpdGlvbiksIHN0YXQ9ImlkZW50aXR5IikgKyAKICBmYWNldF93cmFwKH5GYXJtZXIsIHNjYWxlcz0iZnJlZV94IikKYGBgCgojIyAyLjIgQWJ1bmRhbmNpYXMgcmVsYXRpdmFzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEdyYWZpY2FyIGFidW5kYW5jaWFzIHJlbGF0aXZhcyBwb3IgZmlsbwpwbG90X2Jhcihwcy5wcm9wLCB4PSJTYW1wbGUiLCBmaWxsPSJQaHlsdW0iKSArCiAgZmFjZXRfd3JhcCh+RmFybWVyLCBzY2FsZXM9ImZyZWVfeCIpICsKICBnZW9tX2JhcihhZXMoY29sb3I9UGh5bHVtLCBmaWxsPVBoeWx1bSksIHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIikKYGBgCgpFbiBlbCBncsOhZmljbyBhbnRlcmlvciBzZSBwdWVkZSB2ZXIgZW4gdG9kYXMgbGFzIG11ZXN0cmFzIHVuYSBncmFuIHByZXNlbmNpYSBkZSBiYWN0ZXJvaWRldGVzLCBsYXMgbWlzbWFzIHNvbiBjb211bmVzIGVuIGVsIGludGVzdGlubyBkZSBtdWNob3MgYW5pbWFsZXMuIEVzdGFzIGJhY3RlcmlhcyBwdWVkZW4gZGVzZW1wZcOxYXIgdW4gcGFwZWwgaW1wb3J0YW50ZSBlbiBsYSBkaWdlc3Rpw7NuIHkgbGEgYWJzb3JjacOzbiBkZSBudXRyaWVudGVzIGVuIGxvcyBhbmltYWxlcywgYXPDrSBjb21vIGVuIGxhIHNhbHVkIGdlbmVyYWwgZGUgc3Ugc2lzdGVtYSBkaWdlc3Rpdm8uCgpBZGVtw6FzLCBzZSBwdWVkZSB2ZXIgcHJlc2VuY2lhIGRlIGZ1c29iYWN0ZXJpb3RhcyBlbiBsYXMgbXVlc3RyYXMuIFNvbiBjb25vY2lkYXMgcG9yIHN1IGNhcGFjaWRhZCBwYXJhIGNhdXNhciBpbmZlY2Npb25lcyBlbiBsb3MgYW5pbWFsZXMsIGluY2x1eWVuZG8gaW5mZWNjaW9uZXMgb3JhbGVzLCBhYnNjZXNvcyB5IGVuZmVybWVkYWRlcyByZXNwaXJhdG9yaWFzLiBQb3IgZWplbXBsbywgZW4gbG9zIGNlcmRvcyBzZSBoYSBpbmZvcm1hZG8gcXVlIGxhcyBGdXNvYmFjdGVyaXVtIHB1ZWRlbiBlc3RhciBpbXBsaWNhZGFzIGVuIGNhc29zIGRlIG5lY3Jvc2lzIGhlcMOhdGljYSB5IGFic2Nlc29zLiBMYSBpbmZlY2Npw7NuIGNvbiBGdXNvYmFjdGVyaXVtIHB1ZWRlIHNlciBlbCByZXN1bHRhZG8gZGUgdW5hIGxlc2nDs24gZW4gbGEgcGllbCBvIHVuYSBpbmZlY2Npw7NuIHByZXZpYSwgbG8gcXVlIHBlcm1pdGUgcXVlIGxhcyBiYWN0ZXJpYXMgZW50cmVuIGVuIGVsIGN1ZXJwbyB5IGNhdXNlbiB1bmEgaW5mZWNjacOzbi4KCk90cmFzIGJhY3RlcmlhcyBwcmVzZW50ZXMgZW4gdG9kYXMgbGFzIG11ZXN0cmFzIHNvbiBkZWwgZmlsbyBTcGlyb2NoYWV0b3RhLCBsYXMgbWlzbWFzIHNvbiBjb25vY2lkYXMgcG9yIHNlciBwYXTDs2dlbmFzIHBlcm8gZXMgY29tw7puIGVuIGVsIHRyYWN0byBnYXN0cm9pbnRlc3RpbmFsIGRlIGxvcyBjZXJkb3MsIGF1bnF1ZSBubyBzZSBjb25zaWRlcmFuIHBhdMOzZ2VuYXMgZW4gZXN0ZSBjYXNvIHBhcmEgZWxsb3MuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIEFidW5kYW5jaWFzIHJlbGF0aXZhcyBwb3IgZ8OpbmVybwp0c2UgPC0gbWFrZVRyZWVTdW1tYXJpemVkRXhwZXJpbWVudEZyb21QaHlsb3NlcShwcykgCnRzZSA8LSB0cmFuc2Zvcm1TYW1wbGVzKHRzZSwgbWV0aG9kID0gInJlbGFidW5kYW5jZSIpCnRzZSA8LSBhZGRUYXhvbm9teVRyZWUodHNlKQp0c2VfZ2VuIDwtIGFnZ2xvbWVyYXRlQnlSYW5rKHRzZSwgcmFuayA9ICJHZW51cyIpCnJvd25hbWVzKHRzZV9nZW4pPC1zdWIoIkdlbnVzOiIsIiIsIHJvd25hbWVzKHRzZV9nZW4pKQoKcGxvdEFidW5kYW5jZURlbnNpdHkodHNlX2dlbiwgbGF5b3V0ID0gImppdHRlciIsIGFidW5kX3ZhbHVlcz0icmVsYWJ1bmRhbmNlIiwgbiA9IDIwLCBwb2ludF9zaXplPTEsIHBvaW50X3NoYXBlPTE5LCBwb2ludF9hbHBoYT0wLjUsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXJfYnk9IkZhcm1lciIpICsgCiAgc2NhbGVfeF9sb2cxMChsYWJlbD1zY2FsZXM6OnBlcmNlbnQpCmBgYAoKRW4gZXN0ZSBvdHJvIGdyw6FmaWNvLCBzZSBwdWVkZSB2ZXIgbG9zIHBvcmNlbnRhamVzIGRlIGFidW5kYW5jaWEgcmVsYXRpdmEgZW50cmUgY2FkYSBnw6luZXJvLgoKQ29tbyBwb3IgZWplbXBsbywgY2FtcHlsb2JhY3RlciBlc3TDoSBwcmVzZW50ZSBlbiB1biBtw6FzIGRlIDEwJSBkZSBhYnVuZGFuY2lhIHJlbGF0aXZhIGVuIHVuYSBtdWVzdHJhIGRlbCBjZXJkbyBwZXJ0ZW5lY2llbnRlIGFsIGdyYW5qZXJvIEIuIENhbXB5bG9iYWN0ZXIgcHVlZGUgc2VyIHVuIHBhdMOzZ2VubyBxdWUgY2F1c2EgZW5mZXJtZWRhZGVzIGdhc3Ryb2ludGVzdGluYWxlcyBlbiBodW1hbm9zLCBwb3IgbG8gcXVlIGxhIHByZXNlbmNpYSBkZSBDYW1weWxvYmFjdGVyIGVuIGNlcmRvcyBwdWVkZSBwbGFudGVhciBwcmVvY3VwYWNpb25lcyBlbiB0w6lybWlub3MgZGUgc2VndXJpZGFkIGFsaW1lbnRhcmlhLiBMYSBiYWN0ZXJpYSBwdWVkZSB0cmFuc21pdGlyc2UgYSBsb3MgaHVtYW5vcyBhIHRyYXbDqXMgZGVsIGNvbnN1bW8gZGUgYWxpbWVudG9zIGNvbnRhbWluYWRvcyBjb24gQ2FtcHlsb2JhY3RlciwgY29tbyBjYXJuZSBjcnVkYSBvIGluc3VmaWNpZW50ZW1lbnRlIGNvY2lkYSwgYXPDrSBjb21vIGEgdHJhdsOpcyBkZWwgY29udGFjdG8gZGlyZWN0byBjb24gYW5pbWFsZXMgaW5mZWN0YWRvcy4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgQWJ1bmRhbmNpYSByZWxhdGl2YSBwb3IgZmFtaWxpYQp0c2VfZmFtaWx5IDwtIGFnZ2xvbWVyYXRlQnlSYW5rKHRzZSwgcmFuayA9ICJGYW1pbHkiKQpyb3duYW1lcyh0c2VfZmFtaWx5KTwtc3ViKCJGYW1pbHk6IiwiIiwgcm93bmFtZXModHNlX2ZhbWlseSkpCgpwbG90QWJ1bmRhbmNlRGVuc2l0eSh0c2VfZmFtaWx5LCBsYXlvdXQgPSAiaml0dGVyIiwgYWJ1bmRfdmFsdWVzPSJyZWxhYnVuZGFuY2UiLCBuID0gMjAsIHBvaW50X3NpemU9MSwgcG9pbnRfc2hhcGU9MTksIHBvaW50X2FscGhhPTAuNSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91cl9ieT0iRmFybWVyIikgKyBzY2FsZV94X2xvZzEwKGxhYmVsPXNjYWxlczo6cGVyY2VudCkKYGBgCgpMYXMgYmFjdGVyaWFzIHBlcnRlbmVjaWVudGVzIGEgbGEgZmFtaWxpYSBNb3JheGVsbGFjZWFlIHNlIGVuY3VlbnRyYW4gcHJlc2VudGUgZW4gdG9kYXMgbGFzIG11ZXN0cmFzIHBlcm8gbWF5b3JtZW50ZSBlbiBsb3MgY2hhbmNob3MgZGVsIGdyYW5qZXJvIEEgeSBDLiBNb3JheGVsbGFjZWFlIHB1ZWRlbiBjYXVzYXIgcHJvYmxlbWFzIGVuIGxhIGNhbGlkYWQgZGUgbGEgY2FybmUgZGUgY2VyZG8sIGNvbW8gZWwgb2xvciByYW5jaW8sIGxvIHF1ZSBwdWVkZSBzZXIgdW4gcHJvYmxlbWEgaW1wb3J0YW50ZSBwYXJhIGxhIGluZHVzdHJpYSBjw6FybmljYS4KCkxhcyBiYWN0ZXJpYXMgZW50ZXJvYmFjdGVyaWFjZWFlIHNvbiBwYXTDs2dlbmFzIHkgcHVlZGVuIGNhdXNhciBlbmZlcm1lZGFkZXMgY29tbyBsYSBzYWxtb25lbGxhLiBMYSBtaXNtYSBlc3TDoSBtw6FzIHByZXNlbnRlIGVuIGxhcyBtdWVzdHJhcyBkZWwgY2hhbmNobyBkZWwgZ3Jhbmplcm8gQi4KCiMjIDIuMyBEaXZlcnNpZGFkIGFsZmEKCkxhIGRpdmVyc2lkYWQgYWxmYSBlbiBtZXRhYmFyY29kaW5nIHNlIHJlZmllcmUgYSBsYSBkaXZlcnNpZGFkIGRlIGVzcGVjaWVzIG8gdGlwb3MgZGUgb3JnYW5pc21vcyBwcmVzZW50ZXMgZW4gdW5hIG11ZXN0cmEgZGUgQUROIGJpb2zDs2dpY28geSBzZSBwdWVkZSBtZWRpciB1dGlsaXphbmRvIHZhcmlvcyDDrW5kaWNlcywgY29tbyBlbCDDrW5kaWNlIGRlIFNpbXBzb24uCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKcmFyZWZpZWQ8LXJhcmVmeV9ldmVuX2RlcHRoKHBzLG1pbihzYW1wbGVfc3VtcyhwcykpLHJuZ3NlZWQ9MSkKdHNlX3JhcmVmaWVkIDwtIG1ha2VUcmVlU3VtbWFyaXplZEV4cGVyaW1lbnRGcm9tUGh5bG9zZXEocmFyZWZpZWQpCiMgVW5pZm9ybWlkYWQKdHNlX3JhcmVmaWVkIDwtIGVzdGltYXRlRXZlbm5lc3ModHNlX3JhcmVmaWVkLCAKICAgICAgICAgICAgICAgICAgICAgICAgYWJ1bmRfdmFsdWVzID0gImNvdW50cyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGluZGV4PSJzaW1wc29uIikKCnBsb3RzIDwtIGxhcHBseShjKCJzaW1wc29uIiksCiAgICAgICAgICAgICAgICBwbG90Q29sRGF0YSwKICAgICAgICAgICAgICAgIG9iamVjdCA9IHRzZV9yYXJlZmllZCwKICAgICAgICAgICAgICAgIHggPSAiRmFybWVyIiwKICAgICAgICAgICAgICAgIGNvbG91cl9ieSA9ICJGYXJtZXIiKQoKcGxvdHMgPC0gbGFwcGx5KHBsb3RzLCAiKyIsIAogICAgICAgICAgICAgICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCkpKQoKcGxvdHNbWzFdXSArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikKYGBgCgpMYSBncsOhZmljYSBkZSBkaXZlcnNpZGFkIGFsZmEgZGUgU2ltcHNvbiBtdWVzdHJhIGxhIHJpcXVlemEgZGUgZXNwZWNpZXMgeSBsYSBlcXVpdGFiaWxpZGFkIGVuIHVuYSBjb211bmlkYWQuIEVuIGVzdGEgZ3LDoWZpY2EsIGxhIGRpdmVyc2lkYWQgYWxmYSBzZSByZXByZXNlbnRhIGVuIGVsIGVqZSBkZSBsYXMgb3JkZW5hZGFzLCBtaWVudHJhcyBxdWUgZWwgdGFtYcOxbyBkZSBsYSBtdWVzdHJhIHNlIHJlcHJlc2VudGEgZW4gZWwgZWplIGRlIGFic2Npc2FzLgoKTGEgZGl2ZXJzaWRhZCBkZSBTaW1wc29uIHNlIGV4cHJlc2EgY29tbyB1biDDrW5kaWNlIGRlIGRvbWluYW5jaWEgeSBzZSBjYWxjdWxhIHJlc3RhbmRvIGxhIHN1bWEgZGUgbG9zIGN1YWRyYWRvcyBkZSBsYXMgcHJvcG9yY2lvbmVzIGRlIGNhZGEgZXNwZWNpZSBlbiBsYSBjb211bmlkYWQgZGUgdW5vLiBDdWFudG8gbcOhcyBjZXJjYW5vIHNlYSBlbCDDrW5kaWNlIGRlIFNpbXBzb24gYSAxLCBtZW5vciBzZXLDoSBsYSBkaXZlcnNpZGFkIGFsZmEsIGxvIHF1ZSBpbmRpY2EgdW5hIG1heW9yIGRvbWluYW5jaWEgZGUgdW5hIG8gdW5hcyBwb2NhcyBlc3BlY2llcyBlbiBsYSBjb211bmlkYWQuIFBvciBvdHJvIGxhZG8sIGN1YW50byBtw6FzIGNlcmNhbm8gc2VhIGVsIMOtbmRpY2UgZGUgU2ltcHNvbiBhIDAsIG1heW9yIHNlcsOhIGxhIGRpdmVyc2lkYWQgYWxmYSwgbG8gcXVlIGluZGljYSBxdWUgbGFzIGVzcGVjaWVzIGVzdMOhbiBkaXN0cmlidWlkYXMgZGUgbWFuZXJhIHVuaWZvcm1lIGVuIGxhIGNvbXVuaWRhZC4KCmBgYHtyfQptZXNzYWdlKCJJbmRpY2Ugc2ltcHNvbiBwcm9tZWRpbyBkZWwgZ3Jhbmplcm8gQjogIiwgbWVhbihzdWJzZXQoeCwgRmFybWVyID09ICJCIikkc2ltcHNvbikpCm1lc3NhZ2UoIkluZGljZSBzaW1wc29uIHByb21lZGlvIGRlbCBncmFuamVybyBBOiAiLCBtZWFuKHN1YnNldCh4LCBGYXJtZXIgPT0gIkEiKSRzaW1wc29uKSkKYGBgCgpFbiBlc3RlIGNhc28sIGxhcyBtdWVzdHJhcyBkZWwgZ3Jhbmplcm8gQiBwcmVzZW50YW4gdW4gbWVub3Igw61uZGljZSBzaW1wc29uIHByb21lZGlvLCBwb3IgbG8gdGFudG8gY3VlbnRhIGNvbiB1bmEgbWF5b3IgZGl2ZXJzaWRhZCBhbGZhLgoKUG9yIG90cm8gbGFkbywgbGFzIG11ZXN0cmFzIGRlbCBncmFuamVybyBBIHByZXNlbnRhbiB1biBtYXlvciDDrW5kaWNlIHNpbXBzb24gcXVlIGxhcyBkZW3DoXMgbXVlc3RyYXMsIHBvciBsbyB0YW50byBjdWVudGFuIGNvbiB1bmEgZGl2ZXJzaWRhZCBhbGZhIG1lbm9yLiBFc3RvIHB1ZWRlIHNpZ25pZmljYXIgcXVlIGV4aXN0ZSBtZW5vciBjYW50aWRhZCBkZSBiYWN0ZXJpYXMgZGlmZXJlbnRlcywgbyB1bmEgZ3JhbiBwcm9wb3JjacOzbiBkZSBsYXMgYmFjdGVyaWFzIHByZXNlbnRlcyBzb24gc2ltaWxhcmVzLgoKIyMgMi40IERpdmVyc2lkYWQgYmV0YQoKRW4gbWV0YWJhcmNvZGluZywgbGEgZGl2ZXJzaWRhZCBiZXRhIHNlIHJlZmllcmUgYSBsYSB2YXJpYWNpw7NuIGVuIGxhIGNvbXBvc2ljacOzbiBkZSBlc3BlY2llcyBlbnRyZSBkaWZlcmVudGVzIG11ZXN0cmFzLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBBZ2xvbWVyYWNpb24gcG9yIGfDqG5lcm8gbWFudGVuaWVuZG8gbG9zIE5BCnRzZV9nZW51cyA8LSBhZ2dsb21lcmF0ZUJ5UmFuayh0c2UsIHJhbmsgPSAiR2VudXMiKQp0c2VfZ2VudXNfbm90TkEgPC0gdHNlX2dlbnVzWyFpcy5uYShyb3dEYXRhKHRzZV9nZW51cykkR2VudXMpLCBdCnRzZV9nZW51c19ub3ROQSA8LSBydW5NRFModHNlX2dlbnVzX25vdE5BLCBGVU4gPSB2ZWdhbjo6dmVnZGlzdCwgbWV0aG9kID0gImJyYXkiLCBuYW1lID0gIlBDb0FfQkMiLCBleHByc192YWx1ZXMgPSAiY291bnRzIikKCiMgQ3JlYXRlIGdncGxvdCBvYmplY3QKcCA8LSBwbG90UmVkdWNlZERpbSh0c2VfZ2VudXNfbm90TkEsICJQQ29BX0JDIiwgY29sb3VyX2J5ID0gIkZhcm1lciIpCgojIEFkZCBleHBsYWluZWQgdmFyaWFuY2UgZm9yIGVhY2ggYXhpcwplIDwtIGF0dHIocmVkdWNlZERpbSh0c2VfZ2VudXNfbm90TkEsICJQQ29BX0JDIiksICJlaWciKTsKcmVsX2VpZyA8LSBlL3N1bShlW2U+MF0pICAgICAgICAgIApwICsgbGFicyh4ID0gcGFzdGUoIlBDb0EgMSAoIiwgcm91bmQoMTAwICogcmVsX2VpZ1tbMV1dLDEpLCAiJSIsICIpIiwgc2VwID0gIiIpLAogICAgICAgICAgICAgIHkgPSBwYXN0ZSgiUENvQSAyICgiLCByb3VuZCgxMDAgKiByZWxfZWlnW1syXV0sMSksICIlIiwgIikiLCBzZXAgPSAiIikpCmBgYAoKTG9zIHB1bnRvcyBlbiBsYSBncsOhZmljYSByZXByZXNlbnRhbiBsYXMgbXVlc3RyYXMgeSBzdSBwb3NpY2nDs24gc2UgYmFzYSBlbiBzdSBzaW1pbGl0dWQgYmlvbMOzZ2ljYS4gTGFzIG11ZXN0cmFzIHF1ZSBlc3TDoW4gbcOhcyBjZXJjYSBlbiBsYSBncsOhZmljYSBzZSBjb25zaWRlcmFuIG3DoXMgc2ltaWxhcmVzIGVuIHTDqXJtaW5vcyBkZSBzdSBjb21wb3NpY2nDs24gYmlvbMOzZ2ljYS4KCkFuYWxpemFuZG8gbGEgZ3LDoWZpY2Egc2UgcHVlZGUgY29uY2x1aXIgcXVlIGxhcyBtdWVzdHJhcyBjb2luY2lkZW4gY29uIGxvIGVzcGVyYWRvIHlhIHF1ZSBlbnRyZSBlbGxhcyBoYXkgY2llcnRhIGRpc3RhbmNpYSBsbyBxdWUgcmVmaWVyZSBhIHF1ZSBsYXMgbXVlc3RyYXMgbm8gdGllbmVuIGNvbXBvc2ljacOzbiBiaW9sw7NnaWNhIHNpbWlsYXIuCgojIDMuIENvbmNsdXNpw7NuCgpMdWVnbyBkZSBhbmFsaXphciBsYXMgbXVlc3RyYXMgYmFjdGVyaWFuYXMgZGUgbG9zIGNlcmRvcyBkZSBsb3MgdHJlcyBncmFuamVyb3MsIHNlIGVuY29udHLDsyBxdWUgZWwgY2VyZG8gZGVsIGdyYW5qZXJvIEIgdGVuw61hIGxhIG1heW9yIGNhbnRpZGFkIGRlIENhbXB5bG9iYWN0ZXIgeSBDaGxvcm9mbGV4aSBlbiBzdSB0cmFjdG8gZ2FzdHJvaW50ZXN0aW5hbC4gRXN0byBwdWVkZSBzZXIgcHJlb2N1cGFudGUgeWEgcXVlIGFtYmFzIGJhY3RlcmlhcyBzZSBzYWJlIHF1ZSBwdWVkZW4gY2F1c2FyIGVuZmVybWVkYWRlcyBlbiBsb3Mgc2VyZXMgaHVtYW5vcy4KCkxhIHByZXNlbmNpYSBkZSBDYW1weWxvYmFjdGVyIGVuIGxvcyBjZXJkb3MgZXMgcGFydGljdWxhcm1lbnRlIHByZW9jdXBhbnRlIHlhIHF1ZSBzZSBoYSBkZW1vc3RyYWRvIHF1ZSBlc3RhIGJhY3RlcmlhIHB1ZWRlIHNlciB1biBpbXBvcnRhbnRlIGZhY3RvciBkZSByaWVzZ28gcGFyYSBsYSB0cmFuc21pc2nDs24gZGUgZW5mZXJtZWRhZGVzIGFsaW1lbnRhcmlhcy4gQWRlbcOhcywgbGEgcmVzaXN0ZW5jaWEgYSBsb3MgYW50aW1pY3JvYmlhbm9zLgoKTGEgcHJlc2VuY2lhIGRlIENobG9yb2ZsZXhpIGVuIGxvcyBjZXJkb3MgdGFtYmnDqW4gcHVlZGUgc2VyIHByZW9jdXBhbnRlIHlhIHF1ZSBzZSBzYWJlIHF1ZSBhbGd1bmFzIGVzcGVjaWVzIHB1ZWRlbiBjYXVzYXIgZW5mZXJtZWRhZGVzIHJlc3BpcmF0b3JpYXMgZW4gbG9zIHNlcmVzIGh1bWFub3MuIEFkZW3DoXMsIHNlIHNhYmUgcXVlIGFsZ3VuYXMgYmFjdGVyaWFzIENobG9yb2ZsZXhpIHNvbiByZXNpc3RlbnRlcyBhIGxvcyBhbnRpbWljcm9iaWFub3MsIGxvIHF1ZSBwdWVkZSBjb21wbGljYXIgZWwgdHJhdGFtaWVudG8gZGUgbGFzIGVuZmVybWVkYWRlcyBjYXVzYWRhcyBwb3IgZXN0YXMgYmFjdGVyaWFzLgoKUG9yIG90cm8gbGFkbywgc2UgcHVlZGUgdmVyIHF1ZSBsYSBjYXJuZSBkZSBsb3MgZ3Jhbmplcm9zIEEgeSBDIHRpZW5lbiB1bmEgbWF5b3IgY2FudGlkYWQgZGUgTW9yYXhlbGxhY2VhZSBxdWUgbGEgY2FybmUgZGVsIGdyYW5qZXJvIEIuIExhIG1pc21hIHByZXNlbnRhIHVuIHJpZXNnbyBlbiB0w6lybWlub3MgZGUgZGVzcGVyZGljaW8gYWxpbWVudGljaW8gcGVybyBubyBkZSByaWVzZ28gaHVtYW5vLgoKQ29udGVzdGFuZG8gbGEgcHJlZ3VudGEgZGlzcGFyYWRvcmEgZGUgbGEgaW52ZXN0aWdhY2nDs24gc2UgcG9kcsOtYSBkZWNpciBxdWUgbGEgY2FybmUgZGVsIGdyYW5qZXJvIEIgZXMgbGEgcXVlIG1heW9yIHBlbGlncm9zaWRhZCB0aWVuZS4K