Script para el proyecto de fin del Curso de Programación y análisis de datos con R de la USAL (Abril de 2025) - Vicente Fernández Rodero.

Este Script pretende analizar datos de RNAseq de Neuroblastoma (GSE224037) https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE224037

Los datos que utilizo son datos de RNAseq de acceso público a través de NCBI. Los datos utilizados tienen datos de secuenciación de dos líenas celulares de Neuroblastoma (BE2C y Kelly) en las que se ha silenciado AhR (Aryl Hydrocarbon Receptor) a través del uso de dos shRNAs. A efectos de este script utilizo solo para el análisis los datos de la línea celular BE2C. En un caso real sería conveniente analizar ambas líneas y sacar conclusiones con los datos de ambas (Como checkeo extra incluyo también una copia del script para ver si el mismo análisis es reproducible para la línea celular Kelly ( en carpeta “/Kelly cells reproducible”).

**NOTA: En este archivo alguna de las fuciones están modificadas para que en lugar de guardar las gráficas en PDF haga print de ellas en el documento. Además para los análsis GO y GSEA también se ha quitado la parte de las funciones que guardan los datos en un archivo excel.

1. Directorio de trabajo y carga de paquetes a utilizar

Lo primero que hago es definir el directorio en el que voy a trabajar:

setwd(dir = "~/Desktop/R/Trabajo Fin Curso R/")

Luego cargo los paquetes que utilizaré para analizar los datos seleccionados:

# Paquetes generales:
library(tidyverse)
library(furrr)
library(openxlsx)
library(ggrepel)
# Para importar datos de NCBI:
library(GEOquery)
# Para expresión diferencial:
library(DESeq2)
# Dispersión muestras
library(FactoMineR)
library(factoextra)
#Venn Diagrams
library(ggvenn)
# GO & GSEA 
library(clusterProfiler)
library(org.Hs.eg.db)
library(enrichplot)
library(AnnotationDbi)
library(ggridges)
# Heatmap
library(ComplexHeatmap)
library(colorRamp2)

2. Importar datos de NCBI de nuestro problema (“GSE224037”)

Como los datos a utilizar se pueden acceder desde NCBI, hago uso del paquete GEOquery que permite acceder a los datos directamente desde R. Además, modifico y selecciono los datos de interés para que sea más sencillo utilizarlos más adelante.

GEOquery::getGEOSuppFiles(GEO ="GSE224037", fetch_files = TRUE)
  # Obtener metadata y seleccionar los datos que nos interesan
metadata <- GEOquery::getGEO(GEO ="GSE224037")
  # Trato los datos para quedarme la info  y formato/nombre de interés 
sampledata <- metadata$GSE224037_series_matrix.txt.gz@phenoData@data %>% 
  dplyr::select(title, geo_accession, `cell line:ch1`, `genotype:ch1`, `treatment:ch1`) %>% 
  dplyr::rename(Cell =`cell line:ch1`, Genotype =`genotype:ch1`, Treatment=`treatment:ch1` ) %>% 
  mutate(title = gsub("-", ".", title)) %>% # *NOTA
  mutate(Treatment = gsub("AhR-", "", Treatment)) 
rownames(sampledata) <- NULL
    # *NOTA: en los counts hay separación por puntos y en la info por guión (sustituyo para que el nombre sea igual)

Separo los datos de ambos shRNAs y los modifico para que tengan el formato recomendado para su análisis con DESeq2

  # Datos de la línea BE2C celular separando ambos shRNAs 
BE2C.gz <- gzfile("GSE224037/GSE224037_BE2C_RawCounts.csv.gz")
BE2C.csv <- as.data.frame(read.csv(BE2C.gz) %>% dplyr::rename(Symbol=X) %>% column_to_rownames(var="Symbol"))
      #Para el shRNA 1
BE2C.sh1 <- BE2C.csv %>%  dplyr::select(-BE2C.shAhR2.1, -BE2C.shAhR2.2, -BE2C.shAhR2.3)
BE2C.sh1.info <- sampledata %>% subset(Cell == "BE2C" & Treatment != "sh2" )
BE2C.sh1.info$Treatment <- as.factor(BE2C.sh1.info$Treatment) # DESeq recomienda que el diseño sea factor y no character
ncol(BE2C.sh1) == nrow(BE2C.sh1.info) # Checkear mismo nº de filas para crear DESeq2 object
      #Para el shRNA 2
BE2C.sh2 <- BE2C.csv %>%  dplyr::select(-BE2C.shAhR1.1, -BE2C.shAhR1.2, -BE2C.shAhR1.3)
BE2C.sh2.info <- sampledata %>% subset(Cell == "BE2C" & Treatment != "sh1" )
BE2C.sh2.info$Treatment <- as.factor(BE2C.sh2.info$Treatment) # DESeq recomienda que el diseño sea factor y no character
ncol(BE2C.sh2) == nrow(BE2C.sh2.info) # Checkear mismo nº de filas  = nº columnas para crear DESeq2 object

Utilizo DESeq2 para crear para ambos shRNAs un objeto de DESeq2 que permite analizar los datos de RNAseq definiendo los grupos a comparar y cual utilizar como control de la comparación de los datos de entrada (counts).

  # Crear DESeqDataSet de cada una de las líneas celulares 
BE2C_sh1.dds <- DESeq2::DESeqDataSetFromMatrix(countData = BE2C.sh1,
                                           colData = BE2C.sh1.info,
                                           design = ~ Treatment)

BE2C_sh2.dds <- DESeq2::DESeqDataSetFromMatrix(countData = BE2C.sh2,
                                               colData = BE2C.sh2.info,
                                               design = ~ Treatment)

Uno ambos objetos DESeq2 creados en una lista para después modificarlos y procesarlos a la vez aplicando funciones.

  # Unir los datos de ambos sh en una lista para modificaciones con purrr y furrr
BE2C.counts <- list(BE2C_sh1.dds, BE2C_sh2.dds)
names(BE2C.counts) <- list("BE2C_sh1.dds", "BE2C_sh2.dds")

3. Realizar pre-procesamiento

Primero creo una función que me permita filtrar y no tener en cuenta genes que no tengan relevancia biológica para este problema, para ello selecciono como corte (no muy estricto) que en total de las tres replicas del control + las tres replicas del shRNA haya al menos un total de 10 counts. DEfino también que la referencia de las comparaciones sea el control (pLKO: plásmido sin la secuencia del shRNA). Por último aplico a la lista la función creada de manera que se modifiquen las comparaciones de ambos shRNAs de un solo paso.

  # Filtrar filas (genes) con al menos 10 cuentas en total y determinar el 
  #factor/caracteristica de diseño (control) de referencia para comparar
filter.counts <- function(data){
  a <- rowSums(counts(data)) >= 10
  data <- data[a,]
  data$Treatment <- as.factor(data$Treatment)
  data$Treatment <- relevel(data$Treatment, ref = "pLKO")
return(data)
}
BE2C.counts <- purrr::map(.x=BE2C.counts, .f= filter.counts)

4. Realizar expresión diferencial con DESeq2

Una vez pre-procesados los datos contenidos en los objetos DESeq2 realizo el análisis de expresión diferencial. Para ello creo dos tipos de funciones de manera que pueda tener los datos en crudo (de cada uno de los shRNAs) y los datos filtrados de solo los genes que se expresan diferencialmente de forma significativa.

  # Expresión diferencial sin filtrar
DESeq2.res <- function(data, alpha =0.05){
  data <- DESeq(data)
  result <- DESeq2::results(data, alpha = alpha)
  result <- as.data.frame(result) %>% rownames_to_column(var="Symbol") %>% 
    mutate(negLog10Pvalue = -log10(pvalue),
           rank = rank(stat)) %>% 
    dplyr::arrange(desc(rank)) %>% 
  return(result)
}

 # Expresión diferencial filtrando significativas p<0.05 y -0.5 > FC > 0.5
Diff.exp <- function(data, alpha =0.05){
  data <- DESeq(data)
  result <- DESeq2::results(data, alpha = alpha)
  result <- as.data.frame(result) %>% rownames_to_column(var="Symbol") %>% 
    subset(padj<alpha) %>% 
    subset( log2FoldChange < -1 | log2FoldChange > 1 )
  return(result)
}
BE2C.res <- purrr::map(.x=BE2C.counts, .f= Diff.exp, .progress = TRUE)
names(BE2C.res) <- list("BE2C_sh1.res", "BE2C_sh2.res")

list2env(BE2C.res, envir = .GlobalEnv)

5. Explorar genes diferencialmente expresados (DE) en ambos shRNA

Una vez ya tengo los datos de expresión diferencial genero diferentes formas de visualizarlos y sacar diferentes conclusiones de ellos.

Lo primero que hago es ver como se distribuyen mis muestras, es decir como se distribuyen las replicas del control, sh1 y sh2 en función de dos componentes que deberían diferenciarlos.

 # Realizar diferentes representaciones de los datos de Diff exp.
    # Dispersión de las muestras
  log_TPM <- log2(BE2C.csv + 1)  # Avoid log(0) issues
  pca_res <- prcomp(t(log_TPM))  # Transpose to have samples as rows
  print(fviz_eig(pca_res)) # To see variances and choose for presenting

  print(fviz_pca_ind(pca_res,
                   geom = "text",
                   col.ind = "black", 
                   repel = TRUE) )

Como se puede observar al comparar las muestras vemos que para cada uno de los tipos (control, sh1 y sh2) sus replicas se agrupan entre sí pero no entre ellas, lo que nos puede indicar que al sileciar AhR las células se diferencian del control. Sin embargo, en base a estos dos componentes vemos que los sh1 y sh2 no se agrupan lo que también nos puede reflejar que quizás la eficiencia de silenciamiento también varía entre ambos (cosa que se puede apreciar en el paper de origen).

Por otro lado, exploro los genes diferencialmente a través de Volcano Plots y haciendo un ranking de los genes en función del estadístico stat que calcula DESeq2 (Wald). Para ello como utilizo en ambas representaciones los datos sin filtrar creo una función que me represente ambas gráficas con ggplot.

    # Volcano y Gene Score plots de los datos de expresión 
      # Utilizo los resultados sin filtrar de DESeq2 
DESeq2.DE <- purrr::map(.x=BE2C.counts, .f= DESeq2.res, .progress = TRUE)
names(DESeq2.DE) <- list("SH1_DE", "SH2_DE")
      # Creo una función para representar de un solo paso todo (lo llamaré VS.plot)
  VS.plot <- function(data, pthreshold = 1.3 , xlim= 6, ylim =20){
    print(ggplot(data, aes(x = log2FoldChange, y = negLog10Pvalue, color = Symbol)) +
            geom_point(color = "grey75" ) +
            geom_point(data = subset(data, log2FoldChange > 1 &  negLog10Pvalue > pthreshold), color = "red") +
            geom_point(data = subset(data, log2FoldChange < -1 &  negLog10Pvalue > pthreshold), color = "blue") +
            geom_text_repel(data = dplyr::slice_max(data, rank, n = 20),
                            aes(label = Symbol),
                            color = "black", size = 3, box.padding = 0.5,
                            point.padding = 0.3, max.overlaps = Inf)  +
            geom_text_repel(data = dplyr::slice_min(data, rank, n = 20),
                            aes(label = Symbol),
                            color = "black", size = 3, box.padding = 0.5,
                            point.padding = 0.3, max.overlaps = Inf)  +
      theme_classic() +
      labs(title = "Volcano Plot", x = "Log2 Fold Change", y = "-log10(p-value)") +
      geom_hline(yintercept = 1.3, linetype = "dashed", color = "black", size = 0.5) +  
      geom_vline(xintercept = c(-1, 1), linetype = "dashed", color = "black", size = 0.5) + 
      xlim(c(-xlim, xlim)) + ylim(c(0, ylim)) ) 
    
    print(ggplot(data, aes(x = rank, y = stat, color = Symbol)) +
            geom_point(color = "grey75" ) +
            geom_point(data = subset(data, log2FoldChange > 1 ), color = "red") +
            geom_point(data = subset(data, log2FoldChange < -1 ), color = "blue") +
            geom_text_repel(data = dplyr::slice_max(data, rank, n = 20),
                      aes(label = Symbol),
                      color = "black", size = 3, box.padding = 0.5,
                      point.padding = 0.3, max.overlaps = Inf)  +
            geom_text_repel(data = dplyr::slice_min(data, rank, n = 20),
                            aes(label = Symbol),
                            color = "black", size = 3, box.padding = 0.5,
                            point.padding = 0.3, max.overlaps = Inf)  +
      theme_classic() +
      labs(title = "Gene Score Distribution", x = "Rank", y = "Stat") )
  }

Para el sh1:

VS.plot(DESeq2.DE$SH1_DE)

Para el sh2:

VS.plot(DESeq2.DE$SH2_DE)

Por otro lado, haciendo uso de los datos filtrados, represento en un driagrama de Venn los genes diferencialmente expresados de forma significativa para ambos shRNAs, convergiendo en el centro los que se expresan diferencialmente en ambos shRNAs al compararlos con el control.

    # Venn Diagram para ver el número de genes DE comunes
Venn <-  BE2C.csv %>% rownames_to_column(var = "Symbol") %>% 
  dplyr::mutate(sh1.DE= ifelse(Symbol %in% BE2C_sh1.res$Symbol, yes=TRUE, no= FALSE),
                sh2.DE= ifelse(Symbol %in% BE2C_sh2.res$Symbol, yes=TRUE, no= FALSE)) %>% 
  dplyr::select(Symbol, sh1.DE, sh2.DE)

print(ggvenn(Venn, columns =  c("sh1.DE", "sh2.DE"), show_outside = "none", auto_scale = FALSE) ) # A efectos de visualización pongo autoescalado FALSE (en el PDF adjunto está como TRUE para ver también la diferencia en genes DE en el tamaño)

Como podemos ver en los la representación del Volcano Plot, Gene Score y el Venn Diagram, el sh2 tiene como resultado un mayor número de genes DE, lo que puede explicar también las diferencias en la distribución de las muestras comentadas anteriormente. En total 474 genes en total están DE.

6. Ver GeneOntology terms enriquecidos en nuestro set de genes

Utilizando el set de genes que de forma común se encuentran DE en ambos shRNAs, realizo GeneOntology para ver a que términos están asociados estos genes. Hago el análisis para el total de los 474 genes, de igual manera también hago este análisis para los genes que dentro de estos 474 suben o bajan en ambos shRNAs. Para ello, creo una función que me permita realizar el análisis un un menor número de pasos en las tres aproximaciones.

  # Crear una lista de genes comunmente DE entre ambos shRNA

BE2C.DE.list <- inner_join(x = BE2C_sh1.res, y = BE2C_sh2.res, by= "Symbol") 

colnames(BE2C.DE.list) <- gsub(pattern = ".x", replacement = "_sh1", colnames(BE2C.DE.list), fixed = TRUE)
colnames(BE2C.DE.list) <- gsub(pattern = ".y", replacement = "_sh2", colnames(BE2C.DE.list), fixed = TRUE)
  
  # Seleccionar Lista de genes que comunmente en los sh están up- or down- regulated
BE2C.DE.up <- BE2C.DE.list %>% subset(BE2C.DE.list$log2FoldChange_sh1 > 1 & BE2C.DE.list$log2FoldChange_sh2 > 1 )

BE2C.DE.down <- BE2C.DE.list %>% subset(BE2C.DE.list$log2FoldChange_sh1 < -1 & BE2C.DE.list$log2FoldChange_sh2 < -1 )

  # Realizar GO Analysis de los genes que comunmente en los sh están up- or down- regulated

    # PerformGO: Realiza GO enrichment
    PerformGO <- function(data, universe) {
    data <- enrichGO( 
    gene = data,
    OrgDb = org.Hs.eg.db,
    keyType = "SYMBOL",
    ont = "ALL",
    universe = universe,
    pAdjustMethod = "BH",
    pvalueCutoff = 0.05
  )
  data <- dplyr::arrange(data, desc(RichFactor))
  return(data)
  }

    # GO.visualization: Guarda gráficas como PDF
    GO.visualization <- function(data, PDF_name) {
  enrich_result <- pairwise_termsim(data)

  print(dotplot(data, showCategory = 20, font.size = 7.5, title = "TOP GO terms", x = "GeneRatio"))
  print(barplot(data, showCategory = 20, font.size = 6, title = " TOP GO terms"))
  print(emapplot(enrich_result, showCategory = 20))
    }

    # GO.analysis: Combina ambas funciones
    GO.analysis <- function(data, data_name,  universe) {
  # Realizar GO analysis
  GO.res <- PerformGO(data$Symbol,  universe = rownames(BE2C.sh1))
  # Generar y guardar las gráficas
  GO.visualization(GO.res, PDF_name = data_name)
    }

Para el total de los 474 genes DE:

  GO.analysis(data = BE2C.DE.list, data_name = "All DE genes GO")

Para los genes que comunmente suben su expresión:

  GO.analysis(data = BE2C.DE.up, data_name = "UP DE genes GO")

Para los genes que comunmente bajan su expresión:

  GO.analysis(data = BE2C.DE.down, data_name = "DOWN DE genes GO")

Como podemos ver de los tres análisis, tanto en el análisis del total de 474 genes como en los genes que bajan en expresión vemos términos relacionados con el ciclo y división celular. Lo que tendremos en cuenta para ejemplificar la aproximación de como proceder en la última parte de este documento.

7. Analisis GSEA de nuestro set de genes DE

De forma similar, realizo un análisis GSEA de todos los genes (utilizando de nuevo los datos sin filtrar y teniendo en cuenta el ranking en base a stat). Para ello creo de nuevo una función para aplicar a ambos shRNAs.

  DESeq2.gsea <- purrr::map(.x=BE2C.counts, .f= DESeq2.res, .progress = TRUE)
  names(DESeq2.gsea) <- list("SH1_GSEA", "SH2_GSEA")

  GSEA.analysis <- function(data){
      GSEA.res <- gseGO(
        geneList = setNames(data$rank, data$Symbol), 
        OrgDb = org.Hs.eg.db, 
        keyType = "SYMBOL", 
        ont = "ALL", 
        pAdjustMethod = "BH", 
        pvalueCutoff = 0.05 )
      
      gseaplot <- lapply(1:10, function(i) {
        gseaplot2(GSEA.res, 
                  geneSetID = i, 
                  title = GSEA.res@result$Description[i], 
                  subplots = 1:2)
     })
      
       # Generar y guardar las gráficas
     print(gseaplot)
     print(dotplot(GSEA.res, showCategory = 20, title = "Top GSEA Enriched GO Terms", font.size = 8))
     print(ridgeplot(GSEA.res) + ggtitle("Distribution of Enrichment Scores") +theme(axis.text.y = element_text(size = 8) ))
     print(emapplot(pairwise_termsim(GSEA.res), showCategory = 20, label_format = 20))
       
  }

Para el sh1:

GSEA.analysis(DESeq2.gsea$`SH1_GSEA`)    

Para el sh2:

GSEA.analysis(DESeq2.gsea$`SH2_GSEA`)    

8. Conclusiones y evaluación del problema

Finalmente, para ejemplificar como finalizaría el análisis de estos datos, analizaría alguna de las vías/terminos de interés y/o que saliera enriquecida en los análisis anteriores. Para ejemplificar utilizo un set de genes relacionados con Ciclo celular y División para ver como se expresan en un heatmap en el que represento log2FC de cada shRNA respecto al control (alternativamente con los counts podría representar TPM o calcular y represental z-Scores de las muestras). De esta forma de ese set de genes de traget (de ciclo/división) represento los que están comunmente DE en ambos shRNAs (reduciendo su número). Veo por tanto que acorde con lo que se veía en el GO análisis vemos que al silenciar AhR hay reguladores clave del ciclo y división celular alterados, lo que podría usarse en este ejemplo como base para una validación funcional de esta posible regulación de AhR.

 # Crear una lista de targets de AhR y anotación de su función principal

Target.list <- data.frame(Targets = c("CDK1", "CDK2", "CDK4", "CDK6", "CCNA2", "CCNB1",
                                                           "CCND1", "CCNE1", "RB1", "TP53", "E2F1", "MYC", 
                                                           "AURKA", "AURKB", "PLK1", "BUB1", "MAD2L1", "CDC20",
                                                           "CDH1", "SKP2", "APC", "PTTG1", "TTK", "CENPE", 
                                                           "KIF11", "KIF2C", "RACGAP1", "CYLD", "CHEK1", 
                                                           "CHEK2", "PCNA", "RFC1", "CDC6", "MCM2", "MCM7",
                                                           "ORC1", "CCNH", "CDK7", "CDK9", "KIF23", "NEK2",
                                                           "ESPL1", "SGO1", "SGO2", "INCENP", "CDK11B", "CCNH",
                                                           "CDK12", "CDK13", "FOXM1", "UBE2S", "CDK5", "CDK10",
                                                           "CCND2", "CCND3", "CCNA1", "CCNB2", "CCNB3", "CCNE2",
                                                           "RBL1", "RBL2")) %>% 
  dplyr::mutate(Function = "Cell cicle")


  # Seleccionar de ambos shRNA los log2FC respecto al control para un HeatMap
BE2C.HM <- BE2C.DE.list %>% dplyr::select(Symbol, log2FoldChange_sh1, log2FoldChange_sh2) 
  # Selecciono de ambos sh los genes Targets que sean significativos en ambos shRNA
Targets.HM <- BE2C.DE.list %>% dplyr::select(Symbol, log2FoldChange_sh1, log2FoldChange_sh2) %>% 
  subset(Symbol %in% Target.list$Targets) 
rownames(Targets.HM) <- NULL
Targets.HM <- Targets.HM %>% column_to_rownames(var="Symbol") %>% 
  dplyr::rename(SH1 = log2FoldChange_sh1, SH2 = log2FoldChange_sh2)
Target.HM.ann <- as.data.frame(Target.list) %>% subset(Targets %in% rownames(Targets.HM)) 


  # Genero un Heatmap con los targets que son significativos para ambos shRNAs
      # Crear matriz y annotation input para Complex Hatmap
  Targets.HM <- as.matrix(Targets.HM)
  Target.HM.ann <- HeatmapAnnotation(df = Target.HM.ann$Function, name = "Target Function", which= "row")
  
  print( ComplexHeatmap::Heatmap(Targets.HM, 
                                  cluster_columns = FALSE, 
                                  cluster_rows = TRUE,
                                  row_title = "AhR target Expression",
                                  row_title_side = "left",
                                  column_names_rot = 45,
                                  column_names_gp = gpar(fontsize = 12),
                                  row_names_gp = gpar(fontsize = 10), 
                                  right_annotation = Target.HM.ann,
                                  name= "Log2FC", 
                                  col =colorRamp2(c(-4, 0, 4), c("blue", "white", "red")),
                                  heatmap_width = unit(2, "npc") , heatmap_height = unit(8, "npc")
                                  ))

**NOTA: Al comprobar si el mismo script podría utilizarse en la línea celular Kelly, no se ve un efecto tan claro en el ciclo por lo que en un caso real, se deberían combinar kos resultados de ambas líneas celulares para buscar targets que validar funcionalmente.

9. Guardar el entorno de trabajo

save.image(file = "Trabajo Fin Curso R Vicente Fernández Rodero RMD.RData")
LS0tCnRpdGxlOiAiVHJhYmFqbyBkZSBGaW4gZGUgQ3Vyc28gUiBWaWNlbnRlIEZlcm7DoW5kZXogUm9kZXJvIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGRldj0iQ2Fpcm9QTkciKQpgYGAKClNjcmlwdCBwYXJhIGVsIHByb3llY3RvIGRlIGZpbiBkZWwgQ3Vyc28gZGUgUHJvZ3JhbWFjacOzbiB5IGFuw6FsaXNpcyBkZSBkYXRvcyBjb24gUiBkZSBsYSBVU0FMIChBYnJpbCBkZSAyMDI1KSAtIFZpY2VudGUgRmVybsOhbmRleiBSb2Rlcm8uCgpFc3RlIFNjcmlwdCBwcmV0ZW5kZSBhbmFsaXphciBkYXRvcyBkZSBSTkFzZXEgZGUgTmV1cm9ibGFzdG9tYSAoR1NFMjI0MDM3KSA8aHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vcXVlcnkvYWNjLmNnaT9hY2M9R1NFMjI0MDM3PgoKTG9zIGRhdG9zIHF1ZSB1dGlsaXpvIHNvbiBkYXRvcyBkZSBSTkFzZXEgZGUgYWNjZXNvIHDDumJsaWNvIGEgdHJhdsOpcyBkZSBOQ0JJLiBMb3MgZGF0b3MgdXRpbGl6YWRvcyB0aWVuZW4gZGF0b3MgZGUgc2VjdWVuY2lhY2nDs24gZGUgZG9zIGzDrWVuYXMgY2VsdWxhcmVzIGRlIE5ldXJvYmxhc3RvbWEgKEJFMkMgeSBLZWxseSkgZW4gbGFzIHF1ZSBzZSBoYSBzaWxlbmNpYWRvIEFoUiAoQXJ5bCBIeWRyb2NhcmJvbiBSZWNlcHRvcikgYSB0cmF2w6lzIGRlbCB1c28gZGUgZG9zIHNoUk5Bcy4gQSBlZmVjdG9zIGRlIGVzdGUgc2NyaXB0IHV0aWxpem8gc29sbyBwYXJhIGVsIGFuw6FsaXNpcyBsb3MgZGF0b3MgZGUgbGEgbMOtbmVhIGNlbHVsYXIgQkUyQy4gRW4gdW4gY2FzbyByZWFsIHNlcsOtYSBjb252ZW5pZW50ZSBhbmFsaXphciBhbWJhcyBsw61uZWFzIHkgc2FjYXIgY29uY2x1c2lvbmVzIGNvbiBsb3MgZGF0b3MgZGUgYW1iYXMgKENvbW8gY2hlY2tlbyBleHRyYSBpbmNsdXlvIHRhbWJpw6luIHVuYSBjb3BpYSBkZWwgc2NyaXB0IHBhcmEgdmVyIHNpIGVsIG1pc21vIGFuw6FsaXNpcyBlcyByZXByb2R1Y2libGUgcGFyYSBsYSBsw61uZWEgY2VsdWxhciBLZWxseSAoIGVuIGNhcnBldGEgIi9LZWxseSBjZWxscyByZXByb2R1Y2libGUiKS4KClwqXCpOT1RBOiBFbiBlc3RlIGFyY2hpdm8gYWxndW5hIGRlIGxhcyBmdWNpb25lcyBlc3TDoW4gbW9kaWZpY2FkYXMgcGFyYSBxdWUgZW4gbHVnYXIgZGUgZ3VhcmRhciBsYXMgZ3LDoWZpY2FzIGVuIFBERiBoYWdhIHByaW50IGRlIGVsbGFzIGVuIGVsIGRvY3VtZW50by4gQWRlbcOhcyBwYXJhIGxvcyBhbsOhbHNpcyBHTyB5IEdTRUEgdGFtYmnDqW4gc2UgaGEgcXVpdGFkbyBsYSBwYXJ0ZSBkZSBsYXMgZnVuY2lvbmVzIHF1ZSBndWFyZGFuIGxvcyBkYXRvcyBlbiB1biBhcmNoaXZvIGV4Y2VsLgoKIyMjIDEuIERpcmVjdG9yaW8gZGUgdHJhYmFqbyB5IGNhcmdhIGRlIHBhcXVldGVzIGEgdXRpbGl6YXIKCkxvIHByaW1lcm8gcXVlIGhhZ28gZXMgZGVmaW5pciBlbCBkaXJlY3RvcmlvIGVuIGVsIHF1ZSB2b3kgYSB0cmFiYWphcjoKCmBgYHtyIGV2YWw9RkFMU0V9CnNldHdkKGRpciA9ICJ+L0Rlc2t0b3AvUi9UcmFiYWpvIEZpbiBDdXJzbyBSLyIpCmBgYAoKTHVlZ28gY2FyZ28gbG9zIHBhcXVldGVzIHF1ZSB1dGlsaXphcsOpIHBhcmEgYW5hbGl6YXIgbG9zIGRhdG9zIHNlbGVjY2lvbmFkb3M6CgpgYGB7cn0KCiMgUGFxdWV0ZXMgZ2VuZXJhbGVzOgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShmdXJycikKbGlicmFyeShvcGVueGxzeCkKbGlicmFyeShnZ3JlcGVsKQojIFBhcmEgaW1wb3J0YXIgZGF0b3MgZGUgTkNCSToKbGlicmFyeShHRU9xdWVyeSkKIyBQYXJhIGV4cHJlc2nDs24gZGlmZXJlbmNpYWw6CmxpYnJhcnkoREVTZXEyKQojIERpc3BlcnNpw7NuIG11ZXN0cmFzCmxpYnJhcnkoRmFjdG9NaW5lUikKbGlicmFyeShmYWN0b2V4dHJhKQojVmVubiBEaWFncmFtcwpsaWJyYXJ5KGdndmVubikKIyBHTyAmIEdTRUEgCmxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikKbGlicmFyeShlbnJpY2hwbG90KQpsaWJyYXJ5KEFubm90YXRpb25EYmkpCmxpYnJhcnkoZ2dyaWRnZXMpCiMgSGVhdG1hcApsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNvbG9yUmFtcDIpCmBgYAoKIyMjIDIuIEltcG9ydGFyIGRhdG9zIGRlIE5DQkkgZGUgbnVlc3RybyBwcm9ibGVtYSAoIkdTRTIyNDAzNyIpCgpDb21vIGxvcyBkYXRvcyBhIHV0aWxpemFyIHNlIHB1ZWRlbiBhY2NlZGVyIGRlc2RlIE5DQkksIGhhZ28gdXNvIGRlbCBwYXF1ZXRlIEdFT3F1ZXJ5IHF1ZSBwZXJtaXRlIGFjY2VkZXIgYSBsb3MgZGF0b3MgZGlyZWN0YW1lbnRlIGRlc2RlIFIuIEFkZW3DoXMsIG1vZGlmaWNvIHkgc2VsZWNjaW9ubyBsb3MgZGF0b3MgZGUgaW50ZXLDqXMgcGFyYSBxdWUgc2VhIG3DoXMgc2VuY2lsbG8gdXRpbGl6YXJsb3MgbcOhcyBhZGVsYW50ZS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQpHRU9xdWVyeTo6Z2V0R0VPU3VwcEZpbGVzKEdFTyA9IkdTRTIyNDAzNyIsIGZldGNoX2ZpbGVzID0gVFJVRSkKICAjIE9idGVuZXIgbWV0YWRhdGEgeSBzZWxlY2Npb25hciBsb3MgZGF0b3MgcXVlIG5vcyBpbnRlcmVzYW4KbWV0YWRhdGEgPC0gR0VPcXVlcnk6OmdldEdFTyhHRU8gPSJHU0UyMjQwMzciKQogICMgVHJhdG8gbG9zIGRhdG9zIHBhcmEgcXVlZGFybWUgbGEgaW5mbyAgeSBmb3JtYXRvL25vbWJyZSBkZSBpbnRlcsOpcyAKc2FtcGxlZGF0YSA8LSBtZXRhZGF0YSRHU0UyMjQwMzdfc2VyaWVzX21hdHJpeC50eHQuZ3pAcGhlbm9EYXRhQGRhdGEgJT4lIAogIGRwbHlyOjpzZWxlY3QodGl0bGUsIGdlb19hY2Nlc3Npb24sIGBjZWxsIGxpbmU6Y2gxYCwgYGdlbm90eXBlOmNoMWAsIGB0cmVhdG1lbnQ6Y2gxYCkgJT4lIAogIGRwbHlyOjpyZW5hbWUoQ2VsbCA9YGNlbGwgbGluZTpjaDFgLCBHZW5vdHlwZSA9YGdlbm90eXBlOmNoMWAsIFRyZWF0bWVudD1gdHJlYXRtZW50OmNoMWAgKSAlPiUgCiAgbXV0YXRlKHRpdGxlID0gZ3N1YigiLSIsICIuIiwgdGl0bGUpKSAlPiUgIyAqTk9UQQogIG11dGF0ZShUcmVhdG1lbnQgPSBnc3ViKCJBaFItIiwgIiIsIFRyZWF0bWVudCkpIApyb3duYW1lcyhzYW1wbGVkYXRhKSA8LSBOVUxMCiAgICAjICpOT1RBOiBlbiBsb3MgY291bnRzIGhheSBzZXBhcmFjacOzbiBwb3IgcHVudG9zIHkgZW4gbGEgaW5mbyBwb3IgZ3Vpw7NuIChzdXN0aXR1eW8gcGFyYSBxdWUgZWwgbm9tYnJlIHNlYSBpZ3VhbCkKYGBgCgpTZXBhcm8gbG9zIGRhdG9zIGRlIGFtYm9zIHNoUk5BcyB5IGxvcyBtb2RpZmljbyBwYXJhIHF1ZSB0ZW5nYW4gZWwgZm9ybWF0byByZWNvbWVuZGFkbyBwYXJhIHN1IGFuw6FsaXNpcyBjb24gREVTZXEyCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQoKICAjIERhdG9zIGRlIGxhIGzDrW5lYSBCRTJDIGNlbHVsYXIgc2VwYXJhbmRvIGFtYm9zIHNoUk5BcyAKQkUyQy5neiA8LSBnemZpbGUoIkdTRTIyNDAzNy9HU0UyMjQwMzdfQkUyQ19SYXdDb3VudHMuY3N2Lmd6IikKQkUyQy5jc3YgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdihCRTJDLmd6KSAlPiUgZHBseXI6OnJlbmFtZShTeW1ib2w9WCkgJT4lIGNvbHVtbl90b19yb3duYW1lcyh2YXI9IlN5bWJvbCIpKQogICAgICAjUGFyYSBlbCBzaFJOQSAxCkJFMkMuc2gxIDwtIEJFMkMuY3N2ICU+JSAgZHBseXI6OnNlbGVjdCgtQkUyQy5zaEFoUjIuMSwgLUJFMkMuc2hBaFIyLjIsIC1CRTJDLnNoQWhSMi4zKQpCRTJDLnNoMS5pbmZvIDwtIHNhbXBsZWRhdGEgJT4lIHN1YnNldChDZWxsID09ICJCRTJDIiAmIFRyZWF0bWVudCAhPSAic2gyIiApCkJFMkMuc2gxLmluZm8kVHJlYXRtZW50IDwtIGFzLmZhY3RvcihCRTJDLnNoMS5pbmZvJFRyZWF0bWVudCkgIyBERVNlcSByZWNvbWllbmRhIHF1ZSBlbCBkaXNlw7FvIHNlYSBmYWN0b3IgeSBubyBjaGFyYWN0ZXIKbmNvbChCRTJDLnNoMSkgPT0gbnJvdyhCRTJDLnNoMS5pbmZvKSAjIENoZWNrZWFyIG1pc21vIG7CuiBkZSBmaWxhcyBwYXJhIGNyZWFyIERFU2VxMiBvYmplY3QKICAgICAgI1BhcmEgZWwgc2hSTkEgMgpCRTJDLnNoMiA8LSBCRTJDLmNzdiAlPiUgIGRwbHlyOjpzZWxlY3QoLUJFMkMuc2hBaFIxLjEsIC1CRTJDLnNoQWhSMS4yLCAtQkUyQy5zaEFoUjEuMykKQkUyQy5zaDIuaW5mbyA8LSBzYW1wbGVkYXRhICU+JSBzdWJzZXQoQ2VsbCA9PSAiQkUyQyIgJiBUcmVhdG1lbnQgIT0gInNoMSIgKQpCRTJDLnNoMi5pbmZvJFRyZWF0bWVudCA8LSBhcy5mYWN0b3IoQkUyQy5zaDIuaW5mbyRUcmVhdG1lbnQpICMgREVTZXEgcmVjb21pZW5kYSBxdWUgZWwgZGlzZcOxbyBzZWEgZmFjdG9yIHkgbm8gY2hhcmFjdGVyCm5jb2woQkUyQy5zaDIpID09IG5yb3coQkUyQy5zaDIuaW5mbykgIyBDaGVja2VhciBtaXNtbyBuwrogZGUgZmlsYXMgID0gbsK6IGNvbHVtbmFzIHBhcmEgY3JlYXIgREVTZXEyIG9iamVjdApgYGAKClV0aWxpem8gREVTZXEyIHBhcmEgY3JlYXIgcGFyYSBhbWJvcyBzaFJOQXMgdW4gb2JqZXRvIGRlIERFU2VxMiBxdWUgcGVybWl0ZSBhbmFsaXphciBsb3MgZGF0b3MgZGUgUk5Bc2VxIGRlZmluaWVuZG8gbG9zIGdydXBvcyBhIGNvbXBhcmFyIHkgY3VhbCB1dGlsaXphciBjb21vIGNvbnRyb2wgZGUgbGEgY29tcGFyYWNpw7NuIGRlIGxvcyBkYXRvcyBkZSBlbnRyYWRhIChjb3VudHMpLgoKYGBge3J9CgoKICAjIENyZWFyIERFU2VxRGF0YVNldCBkZSBjYWRhIHVuYSBkZSBsYXMgbMOtbmVhcyBjZWx1bGFyZXMgCkJFMkNfc2gxLmRkcyA8LSBERVNlcTI6OkRFU2VxRGF0YVNldEZyb21NYXRyaXgoY291bnREYXRhID0gQkUyQy5zaDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gQkUyQy5zaDEuaW5mbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gVHJlYXRtZW50KQoKQkUyQ19zaDIuZGRzIDwtIERFU2VxMjo6REVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBCRTJDLnNoMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xEYXRhID0gQkUyQy5zaDIuaW5mbywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IFRyZWF0bWVudCkKCmBgYAoKVW5vIGFtYm9zIG9iamV0b3MgREVTZXEyIGNyZWFkb3MgZW4gdW5hIGxpc3RhIHBhcmEgZGVzcHXDqXMgbW9kaWZpY2FybG9zIHkgcHJvY2VzYXJsb3MgYSBsYSB2ZXogYXBsaWNhbmRvIGZ1bmNpb25lcy4KCmBgYHtyfQoKICAjIFVuaXIgbG9zIGRhdG9zIGRlIGFtYm9zIHNoIGVuIHVuYSBsaXN0YSBwYXJhIG1vZGlmaWNhY2lvbmVzIGNvbiBwdXJyciB5IGZ1cnJyCkJFMkMuY291bnRzIDwtIGxpc3QoQkUyQ19zaDEuZGRzLCBCRTJDX3NoMi5kZHMpCm5hbWVzKEJFMkMuY291bnRzKSA8LSBsaXN0KCJCRTJDX3NoMS5kZHMiLCAiQkUyQ19zaDIuZGRzIikKYGBgCgojIyMgMy4gUmVhbGl6YXIgcHJlLXByb2Nlc2FtaWVudG8KClByaW1lcm8gY3JlbyB1bmEgZnVuY2nDs24gcXVlIG1lIHBlcm1pdGEgZmlsdHJhciB5IG5vIHRlbmVyIGVuIGN1ZW50YSBnZW5lcyBxdWUgbm8gdGVuZ2FuIHJlbGV2YW5jaWEgYmlvbMOzZ2ljYSBwYXJhIGVzdGUgcHJvYmxlbWEsIHBhcmEgZWxsbyBzZWxlY2Npb25vIGNvbW8gY29ydGUgKG5vIG11eSBlc3RyaWN0bykgcXVlIGVuIHRvdGFsIGRlIGxhcyB0cmVzIHJlcGxpY2FzIGRlbCBjb250cm9sICsgbGFzIHRyZXMgcmVwbGljYXMgZGVsIHNoUk5BIGhheWEgYWwgbWVub3MgdW4gdG90YWwgZGUgMTAgY291bnRzLiBERWZpbm8gdGFtYmnDqW4gcXVlIGxhIHJlZmVyZW5jaWEgZGUgbGFzIGNvbXBhcmFjaW9uZXMgc2VhIGVsIGNvbnRyb2wgKHBMS086IHBsw6FzbWlkbyBzaW4gbGEgc2VjdWVuY2lhIGRlbCBzaFJOQSkuIFBvciDDumx0aW1vIGFwbGljbyBhIGxhIGxpc3RhIGxhIGZ1bmNpw7NuIGNyZWFkYSBkZSBtYW5lcmEgcXVlIHNlIG1vZGlmaXF1ZW4gbGFzIGNvbXBhcmFjaW9uZXMgZGUgYW1ib3Mgc2hSTkFzIGRlIHVuIHNvbG8gcGFzby4KCmBgYHtyfQoKICAjIEZpbHRyYXIgZmlsYXMgKGdlbmVzKSBjb24gYWwgbWVub3MgMTAgY3VlbnRhcyBlbiB0b3RhbCB5IGRldGVybWluYXIgZWwgCiAgI2ZhY3Rvci9jYXJhY3RlcmlzdGljYSBkZSBkaXNlw7FvIChjb250cm9sKSBkZSByZWZlcmVuY2lhIHBhcmEgY29tcGFyYXIKZmlsdGVyLmNvdW50cyA8LSBmdW5jdGlvbihkYXRhKXsKICBhIDwtIHJvd1N1bXMoY291bnRzKGRhdGEpKSA+PSAxMAogIGRhdGEgPC0gZGF0YVthLF0KICBkYXRhJFRyZWF0bWVudCA8LSBhcy5mYWN0b3IoZGF0YSRUcmVhdG1lbnQpCiAgZGF0YSRUcmVhdG1lbnQgPC0gcmVsZXZlbChkYXRhJFRyZWF0bWVudCwgcmVmID0gInBMS08iKQpyZXR1cm4oZGF0YSkKfQpCRTJDLmNvdW50cyA8LSBwdXJycjo6bWFwKC54PUJFMkMuY291bnRzLCAuZj0gZmlsdGVyLmNvdW50cykKYGBgCgojIyMgNC4gUmVhbGl6YXIgZXhwcmVzacOzbiBkaWZlcmVuY2lhbCBjb24gREVTZXEyCgpVbmEgdmV6IHByZS1wcm9jZXNhZG9zIGxvcyBkYXRvcyBjb250ZW5pZG9zIGVuIGxvcyBvYmpldG9zIERFU2VxMiByZWFsaXpvIGVsIGFuw6FsaXNpcyBkZSBleHByZXNpw7NuIGRpZmVyZW5jaWFsLiBQYXJhIGVsbG8gY3JlbyBkb3MgdGlwb3MgZGUgZnVuY2lvbmVzIGRlIG1hbmVyYSBxdWUgcHVlZGEgdGVuZXIgbG9zIGRhdG9zIGVuIGNydWRvIChkZSBjYWRhIHVubyBkZSBsb3Mgc2hSTkFzKSB5IGxvcyBkYXRvcyBmaWx0cmFkb3MgZGUgc29sbyBsb3MgZ2VuZXMgcXVlIHNlIGV4cHJlc2FuIGRpZmVyZW5jaWFsbWVudGUgZGUgZm9ybWEgc2lnbmlmaWNhdGl2YS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9VFJVRSwgcmVzdWx0cz0naGlkZSd9CiAgIyBFeHByZXNpw7NuIGRpZmVyZW5jaWFsIHNpbiBmaWx0cmFyCkRFU2VxMi5yZXMgPC0gZnVuY3Rpb24oZGF0YSwgYWxwaGEgPTAuMDUpewogIGRhdGEgPC0gREVTZXEoZGF0YSkKICByZXN1bHQgPC0gREVTZXEyOjpyZXN1bHRzKGRhdGEsIGFscGhhID0gYWxwaGEpCiAgcmVzdWx0IDwtIGFzLmRhdGEuZnJhbWUocmVzdWx0KSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKHZhcj0iU3ltYm9sIikgJT4lIAogICAgbXV0YXRlKG5lZ0xvZzEwUHZhbHVlID0gLWxvZzEwKHB2YWx1ZSksCiAgICAgICAgICAgcmFuayA9IHJhbmsoc3RhdCkpICU+JSAKICAgIGRwbHlyOjphcnJhbmdlKGRlc2MocmFuaykpICU+JSAKICByZXR1cm4ocmVzdWx0KQp9CgogIyBFeHByZXNpw7NuIGRpZmVyZW5jaWFsIGZpbHRyYW5kbyBzaWduaWZpY2F0aXZhcyBwPDAuMDUgeSAtMC41ID4gRkMgPiAwLjUKRGlmZi5leHAgPC0gZnVuY3Rpb24oZGF0YSwgYWxwaGEgPTAuMDUpewogIGRhdGEgPC0gREVTZXEoZGF0YSkKICByZXN1bHQgPC0gREVTZXEyOjpyZXN1bHRzKGRhdGEsIGFscGhhID0gYWxwaGEpCiAgcmVzdWx0IDwtIGFzLmRhdGEuZnJhbWUocmVzdWx0KSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKHZhcj0iU3ltYm9sIikgJT4lIAogICAgc3Vic2V0KHBhZGo8YWxwaGEpICU+JSAKICAgIHN1YnNldCggbG9nMkZvbGRDaGFuZ2UgPCAtMSB8IGxvZzJGb2xkQ2hhbmdlID4gMSApCiAgcmV0dXJuKHJlc3VsdCkKfQpCRTJDLnJlcyA8LSBwdXJycjo6bWFwKC54PUJFMkMuY291bnRzLCAuZj0gRGlmZi5leHAsIC5wcm9ncmVzcyA9IFRSVUUpCm5hbWVzKEJFMkMucmVzKSA8LSBsaXN0KCJCRTJDX3NoMS5yZXMiLCAiQkUyQ19zaDIucmVzIikKCmxpc3QyZW52KEJFMkMucmVzLCBlbnZpciA9IC5HbG9iYWxFbnYpCmBgYAoKIyMjIDUuIEV4cGxvcmFyIGdlbmVzIGRpZmVyZW5jaWFsbWVudGUgZXhwcmVzYWRvcyAoREUpIGVuIGFtYm9zIHNoUk5BCgpVbmEgdmV6IHlhIHRlbmdvIGxvcyBkYXRvcyBkZSBleHByZXNpw7NuIGRpZmVyZW5jaWFsIGdlbmVybyBkaWZlcmVudGVzIGZvcm1hcyBkZSB2aXN1YWxpemFybG9zIHkgc2FjYXIgZGlmZXJlbnRlcyBjb25jbHVzaW9uZXMgZGUgZWxsb3MuCgpMbyBwcmltZXJvIHF1ZSBoYWdvIGVzIHZlciBjb21vIHNlIGRpc3RyaWJ1eWVuIG1pcyBtdWVzdHJhcywgZXMgZGVjaXIgY29tbyBzZSBkaXN0cmlidXllbiBsYXMgcmVwbGljYXMgZGVsIGNvbnRyb2wsIHNoMSB5IHNoMiBlbiBmdW5jacOzbiBkZSBkb3MgY29tcG9uZW50ZXMgcXVlIGRlYmVyw61hbiBkaWZlcmVuY2lhcmxvcy4KCmBgYHtyfQogIyBSZWFsaXphciBkaWZlcmVudGVzIHJlcHJlc2VudGFjaW9uZXMgZGUgbG9zIGRhdG9zIGRlIERpZmYgZXhwLgogICAgIyBEaXNwZXJzacOzbiBkZSBsYXMgbXVlc3RyYXMKICBsb2dfVFBNIDwtIGxvZzIoQkUyQy5jc3YgKyAxKSAgIyBBdm9pZCBsb2coMCkgaXNzdWVzCiAgcGNhX3JlcyA8LSBwcmNvbXAodChsb2dfVFBNKSkgICMgVHJhbnNwb3NlIHRvIGhhdmUgc2FtcGxlcyBhcyByb3dzCiAgcHJpbnQoZnZpel9laWcocGNhX3JlcykpICMgVG8gc2VlIHZhcmlhbmNlcyBhbmQgY2hvb3NlIGZvciBwcmVzZW50aW5nCiAgcHJpbnQoZnZpel9wY2FfaW5kKHBjYV9yZXMsCiAgICAgICAgICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgICAgICAgICAgY29sLmluZCA9ICJibGFjayIsIAogICAgICAgICAgICAgICAgICAgcmVwZWwgPSBUUlVFKSApCmBgYAoKQ29tbyBzZSBwdWVkZSBvYnNlcnZhciBhbCBjb21wYXJhciBsYXMgbXVlc3RyYXMgdmVtb3MgcXVlIHBhcmEgY2FkYSB1bm8gZGUgbG9zIHRpcG9zIChjb250cm9sLCBzaDEgeSBzaDIpIHN1cyByZXBsaWNhcyBzZSBhZ3J1cGFuIGVudHJlIHPDrSBwZXJvIG5vIGVudHJlIGVsbGFzLCBsbyBxdWUgbm9zIHB1ZWRlIGluZGljYXIgcXVlIGFsIHNpbGVjaWFyIEFoUiBsYXMgY8OpbHVsYXMgc2UgZGlmZXJlbmNpYW4gZGVsIGNvbnRyb2wuIFNpbiBlbWJhcmdvLCBlbiBiYXNlIGEgZXN0b3MgZG9zIGNvbXBvbmVudGVzIHZlbW9zIHF1ZSBsb3Mgc2gxIHkgc2gyIG5vIHNlIGFncnVwYW4gbG8gcXVlIHRhbWJpw6luIG5vcyBwdWVkZSByZWZsZWphciBxdWUgcXVpesOhcyBsYSBlZmljaWVuY2lhIGRlIHNpbGVuY2lhbWllbnRvIHRhbWJpw6luIHZhcsOtYSBlbnRyZSBhbWJvcyAoY29zYSBxdWUgc2UgcHVlZGUgYXByZWNpYXIgZW4gZWwgcGFwZXIgZGUgb3JpZ2VuKS4KClBvciBvdHJvIGxhZG8sIGV4cGxvcm8gbG9zIGdlbmVzIGRpZmVyZW5jaWFsbWVudGUgYSB0cmF2w6lzIGRlIFZvbGNhbm8gUGxvdHMgeSBoYWNpZW5kbyB1biByYW5raW5nIGRlIGxvcyBnZW5lcyBlbiBmdW5jacOzbiBkZWwgZXN0YWTDrXN0aWNvIHN0YXQgcXVlIGNhbGN1bGEgREVTZXEyIChXYWxkKS4gUGFyYSBlbGxvIGNvbW8gdXRpbGl6byBlbiBhbWJhcyByZXByZXNlbnRhY2lvbmVzIGxvcyBkYXRvcyBzaW4gZmlsdHJhciBjcmVvIHVuYSBmdW5jacOzbiBxdWUgbWUgcmVwcmVzZW50ZSBhbWJhcyBncsOhZmljYXMgY29uIGdncGxvdC4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9VFJVRX0KCiAgICAjIFZvbGNhbm8geSBHZW5lIFNjb3JlIHBsb3RzIGRlIGxvcyBkYXRvcyBkZSBleHByZXNpw7NuIAogICAgICAjIFV0aWxpem8gbG9zIHJlc3VsdGFkb3Mgc2luIGZpbHRyYXIgZGUgREVTZXEyIApERVNlcTIuREUgPC0gcHVycnI6Om1hcCgueD1CRTJDLmNvdW50cywgLmY9IERFU2VxMi5yZXMsIC5wcm9ncmVzcyA9IFRSVUUpCm5hbWVzKERFU2VxMi5ERSkgPC0gbGlzdCgiU0gxX0RFIiwgIlNIMl9ERSIpCiAgICAgICMgQ3JlbyB1bmEgZnVuY2nDs24gcGFyYSByZXByZXNlbnRhciBkZSB1biBzb2xvIHBhc28gdG9kbyAobG8gbGxhbWFyw6kgVlMucGxvdCkKICBWUy5wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIHB0aHJlc2hvbGQgPSAxLjMgLCB4bGltPSA2LCB5bGltID0yMCl7CiAgICBwcmludChnZ3Bsb3QoZGF0YSwgYWVzKHggPSBsb2cyRm9sZENoYW5nZSwgeSA9IG5lZ0xvZzEwUHZhbHVlLCBjb2xvciA9IFN5bWJvbCkpICsKICAgICAgICAgICAgZ2VvbV9wb2ludChjb2xvciA9ICJncmV5NzUiICkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoZGF0YSwgbG9nMkZvbGRDaGFuZ2UgPiAxICYgIG5lZ0xvZzEwUHZhbHVlID4gcHRocmVzaG9sZCksIGNvbG9yID0gInJlZCIpICsKICAgICAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRhdGEsIGxvZzJGb2xkQ2hhbmdlIDwgLTEgJiAgbmVnTG9nMTBQdmFsdWUgPiBwdGhyZXNob2xkKSwgY29sb3IgPSAiYmx1ZSIpICsKICAgICAgICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBkcGx5cjo6c2xpY2VfbWF4KGRhdGEsIHJhbmssIG4gPSAyMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBTeW1ib2wpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMywgYm94LnBhZGRpbmcgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC4zLCBtYXgub3ZlcmxhcHMgPSBJbmYpICArCiAgICAgICAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZHBseXI6OnNsaWNlX21pbihkYXRhLCByYW5rLCBuID0gMjApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gU3ltYm9sKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIGJveC5wYWRkaW5nID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMywgbWF4Lm92ZXJsYXBzID0gSW5mKSAgKwogICAgICB0aGVtZV9jbGFzc2ljKCkgKwogICAgICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gUGxvdCIsIHggPSAiTG9nMiBGb2xkIENoYW5nZSIsIHkgPSAiLWxvZzEwKHAtdmFsdWUpIikgKwogICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAxLjMsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkgKyAgCiAgICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLTEsIDEpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAwLjUpICsgCiAgICAgIHhsaW0oYygteGxpbSwgeGxpbSkpICsgeWxpbShjKDAsIHlsaW0pKSApIAogICAgCiAgICBwcmludChnZ3Bsb3QoZGF0YSwgYWVzKHggPSByYW5rLCB5ID0gc3RhdCwgY29sb3IgPSBTeW1ib2wpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleTc1IiApICsKICAgICAgICAgICAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRhdGEsIGxvZzJGb2xkQ2hhbmdlID4gMSApLCBjb2xvciA9ICJyZWQiKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChkYXRhLCBsb2cyRm9sZENoYW5nZSA8IC0xICksIGNvbG9yID0gImJsdWUiKSArCiAgICAgICAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gZHBseXI6OnNsaWNlX21heChkYXRhLCByYW5rLCBuID0gMjApLAogICAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gU3ltYm9sKSwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIGJveC5wYWRkaW5nID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMywgbWF4Lm92ZXJsYXBzID0gSW5mKSAgKwogICAgICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGRwbHlyOjpzbGljZV9taW4oZGF0YSwgcmFuaywgbiA9IDIwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IFN5bWJvbCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzLCBib3gucGFkZGluZyA9IDAuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvaW50LnBhZGRpbmcgPSAwLjMsIG1heC5vdmVybGFwcyA9IEluZikgICsKICAgICAgdGhlbWVfY2xhc3NpYygpICsKICAgICAgbGFicyh0aXRsZSA9ICJHZW5lIFNjb3JlIERpc3RyaWJ1dGlvbiIsIHggPSAiUmFuayIsIHkgPSAiU3RhdCIpICkKICB9CiAgCmBgYAoKUGFyYSBlbCBzaDE6CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpWUy5wbG90KERFU2VxMi5ERSRTSDFfREUpCmBgYAoKUGFyYSBlbCBzaDI6CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpWUy5wbG90KERFU2VxMi5ERSRTSDJfREUpCmBgYAoKUG9yIG90cm8gbGFkbywgaGFjaWVuZG8gdXNvIGRlIGxvcyBkYXRvcyBmaWx0cmFkb3MsIHJlcHJlc2VudG8gZW4gdW4gZHJpYWdyYW1hIGRlIFZlbm4gbG9zIGdlbmVzIGRpZmVyZW5jaWFsbWVudGUgZXhwcmVzYWRvcyBkZSBmb3JtYSBzaWduaWZpY2F0aXZhIHBhcmEgYW1ib3Mgc2hSTkFzLCBjb252ZXJnaWVuZG8gZW4gZWwgY2VudHJvIGxvcyBxdWUgc2UgZXhwcmVzYW4gZGlmZXJlbmNpYWxtZW50ZSBlbiBhbWJvcyBzaFJOQXMgYWwgY29tcGFyYXJsb3MgY29uIGVsIGNvbnRyb2wuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKICAgICMgVmVubiBEaWFncmFtIHBhcmEgdmVyIGVsIG7Dum1lcm8gZGUgZ2VuZXMgREUgY29tdW5lcwpWZW5uIDwtICBCRTJDLmNzdiAlPiUgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJTeW1ib2wiKSAlPiUgCiAgZHBseXI6Om11dGF0ZShzaDEuREU9IGlmZWxzZShTeW1ib2wgJWluJSBCRTJDX3NoMS5yZXMkU3ltYm9sLCB5ZXM9VFJVRSwgbm89IEZBTFNFKSwKICAgICAgICAgICAgICAgIHNoMi5ERT0gaWZlbHNlKFN5bWJvbCAlaW4lIEJFMkNfc2gyLnJlcyRTeW1ib2wsIHllcz1UUlVFLCBubz0gRkFMU0UpKSAlPiUgCiAgZHBseXI6OnNlbGVjdChTeW1ib2wsIHNoMS5ERSwgc2gyLkRFKQoKcHJpbnQoZ2d2ZW5uKFZlbm4sIGNvbHVtbnMgPSAgYygic2gxLkRFIiwgInNoMi5ERSIpLCBzaG93X291dHNpZGUgPSAibm9uZSIsIGF1dG9fc2NhbGUgPSBGQUxTRSkgKSAjIEEgZWZlY3RvcyBkZSB2aXN1YWxpemFjacOzbiBwb25nbyBhdXRvZXNjYWxhZG8gRkFMU0UgKGVuIGVsIFBERiBhZGp1bnRvIGVzdMOhIGNvbW8gVFJVRSBwYXJhIHZlciB0YW1iacOpbiBsYSBkaWZlcmVuY2lhIGVuIGdlbmVzIERFIGVuIGVsIHRhbWHDsW8pCmBgYAoKQ29tbyBwb2RlbW9zIHZlciBlbiBsb3MgbGEgcmVwcmVzZW50YWNpw7NuIGRlbCBWb2xjYW5vIFBsb3QsIEdlbmUgU2NvcmUgeSBlbCBWZW5uIERpYWdyYW0sIGVsIHNoMiB0aWVuZSBjb21vIHJlc3VsdGFkbyB1biBtYXlvciBuw7ptZXJvIGRlIGdlbmVzIERFLCBsbyBxdWUgcHVlZGUgZXhwbGljYXIgdGFtYmnDqW4gbGFzIGRpZmVyZW5jaWFzIGVuIGxhIGRpc3RyaWJ1Y2nDs24gZGUgbGFzIG11ZXN0cmFzIGNvbWVudGFkYXMgYW50ZXJpb3JtZW50ZS4gRW4gdG90YWwgNDc0IGdlbmVzIGVuIHRvdGFsIGVzdMOhbiBERS4KCiMjIyA2LiBWZXIgR2VuZU9udG9sb2d5IHRlcm1zIGVucmlxdWVjaWRvcyBlbiBudWVzdHJvIHNldCBkZSBnZW5lcwoKVXRpbGl6YW5kbyBlbCBzZXQgZGUgZ2VuZXMgcXVlIGRlIGZvcm1hIGNvbcO6biBzZSBlbmN1ZW50cmFuIERFIGVuIGFtYm9zIHNoUk5BcywgcmVhbGl6byBHZW5lT250b2xvZ3kgcGFyYSB2ZXIgYSBxdWUgdMOpcm1pbm9zIGVzdMOhbiBhc29jaWFkb3MgZXN0b3MgZ2VuZXMuIEhhZ28gZWwgYW7DoWxpc2lzIHBhcmEgZWwgdG90YWwgZGUgbG9zIDQ3NCBnZW5lcywgZGUgaWd1YWwgbWFuZXJhIHRhbWJpw6luIGhhZ28gZXN0ZSBhbsOhbGlzaXMgcGFyYSBsb3MgZ2VuZXMgcXVlIGRlbnRybyBkZSBlc3RvcyA0NzQgc3ViZW4gbyBiYWphbiBlbiBhbWJvcyBzaFJOQXMuIFBhcmEgZWxsbywgY3JlbyB1bmEgZnVuY2nDs24gcXVlIG1lIHBlcm1pdGEgcmVhbGl6YXIgZWwgYW7DoWxpc2lzIHVuIHVuIG1lbm9yIG7Dum1lcm8gZGUgcGFzb3MgZW4gbGFzIHRyZXMgYXByb3hpbWFjaW9uZXMuCgpgYGB7cn0KCiAgIyBDcmVhciB1bmEgbGlzdGEgZGUgZ2VuZXMgY29tdW5tZW50ZSBERSBlbnRyZSBhbWJvcyBzaFJOQQoKQkUyQy5ERS5saXN0IDwtIGlubmVyX2pvaW4oeCA9IEJFMkNfc2gxLnJlcywgeSA9IEJFMkNfc2gyLnJlcywgYnk9ICJTeW1ib2wiKSAKCmNvbG5hbWVzKEJFMkMuREUubGlzdCkgPC0gZ3N1YihwYXR0ZXJuID0gIi54IiwgcmVwbGFjZW1lbnQgPSAiX3NoMSIsIGNvbG5hbWVzKEJFMkMuREUubGlzdCksIGZpeGVkID0gVFJVRSkKY29sbmFtZXMoQkUyQy5ERS5saXN0KSA8LSBnc3ViKHBhdHRlcm4gPSAiLnkiLCByZXBsYWNlbWVudCA9ICJfc2gyIiwgY29sbmFtZXMoQkUyQy5ERS5saXN0KSwgZml4ZWQgPSBUUlVFKQogIAogICMgU2VsZWNjaW9uYXIgTGlzdGEgZGUgZ2VuZXMgcXVlIGNvbXVubWVudGUgZW4gbG9zIHNoIGVzdMOhbiB1cC0gb3IgZG93bi0gcmVndWxhdGVkCkJFMkMuREUudXAgPC0gQkUyQy5ERS5saXN0ICU+JSBzdWJzZXQoQkUyQy5ERS5saXN0JGxvZzJGb2xkQ2hhbmdlX3NoMSA+IDEgJiBCRTJDLkRFLmxpc3QkbG9nMkZvbGRDaGFuZ2Vfc2gyID4gMSApCgpCRTJDLkRFLmRvd24gPC0gQkUyQy5ERS5saXN0ICU+JSBzdWJzZXQoQkUyQy5ERS5saXN0JGxvZzJGb2xkQ2hhbmdlX3NoMSA8IC0xICYgQkUyQy5ERS5saXN0JGxvZzJGb2xkQ2hhbmdlX3NoMiA8IC0xICkKCiAgIyBSZWFsaXphciBHTyBBbmFseXNpcyBkZSBsb3MgZ2VuZXMgcXVlIGNvbXVubWVudGUgZW4gbG9zIHNoIGVzdMOhbiB1cC0gb3IgZG93bi0gcmVndWxhdGVkCgogICAgIyBQZXJmb3JtR086IFJlYWxpemEgR08gZW5yaWNobWVudAogICAgUGVyZm9ybUdPIDwtIGZ1bmN0aW9uKGRhdGEsIHVuaXZlcnNlKSB7CiAgICBkYXRhIDwtIGVucmljaEdPKCAKICAgIGdlbmUgPSBkYXRhLAogICAgT3JnRGIgPSBvcmcuSHMuZWcuZGIsCiAgICBrZXlUeXBlID0gIlNZTUJPTCIsCiAgICBvbnQgPSAiQUxMIiwKICAgIHVuaXZlcnNlID0gdW5pdmVyc2UsCiAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwKICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUKICApCiAgZGF0YSA8LSBkcGx5cjo6YXJyYW5nZShkYXRhLCBkZXNjKFJpY2hGYWN0b3IpKQogIHJldHVybihkYXRhKQogIH0KCiAgICAjIEdPLnZpc3VhbGl6YXRpb246IEd1YXJkYSBncsOhZmljYXMgY29tbyBQREYKICAgIEdPLnZpc3VhbGl6YXRpb24gPC0gZnVuY3Rpb24oZGF0YSwgUERGX25hbWUpIHsKICBlbnJpY2hfcmVzdWx0IDwtIHBhaXJ3aXNlX3Rlcm1zaW0oZGF0YSkKCiAgcHJpbnQoZG90cGxvdChkYXRhLCBzaG93Q2F0ZWdvcnkgPSAyMCwgZm9udC5zaXplID0gNy41LCB0aXRsZSA9ICJUT1AgR08gdGVybXMiLCB4ID0gIkdlbmVSYXRpbyIpKQogIHByaW50KGJhcnBsb3QoZGF0YSwgc2hvd0NhdGVnb3J5ID0gMjAsIGZvbnQuc2l6ZSA9IDYsIHRpdGxlID0gIiBUT1AgR08gdGVybXMiKSkKICBwcmludChlbWFwcGxvdChlbnJpY2hfcmVzdWx0LCBzaG93Q2F0ZWdvcnkgPSAyMCkpCiAgICB9CgogICAgIyBHTy5hbmFseXNpczogQ29tYmluYSBhbWJhcyBmdW5jaW9uZXMKICAgIEdPLmFuYWx5c2lzIDwtIGZ1bmN0aW9uKGRhdGEsIGRhdGFfbmFtZSwgIHVuaXZlcnNlKSB7CiAgIyBSZWFsaXphciBHTyBhbmFseXNpcwogIEdPLnJlcyA8LSBQZXJmb3JtR08oZGF0YSRTeW1ib2wsICB1bml2ZXJzZSA9IHJvd25hbWVzKEJFMkMuc2gxKSkKICAjIEdlbmVyYXIgeSBndWFyZGFyIGxhcyBncsOhZmljYXMKICBHTy52aXN1YWxpemF0aW9uKEdPLnJlcywgUERGX25hbWUgPSBkYXRhX25hbWUpCiAgICB9CgoKYGBgCgpQYXJhIGVsIHRvdGFsIGRlIGxvcyA0NzQgZ2VuZXMgREU6CgpgYGB7cn0KICBHTy5hbmFseXNpcyhkYXRhID0gQkUyQy5ERS5saXN0LCBkYXRhX25hbWUgPSAiQWxsIERFIGdlbmVzIEdPIikKCmBgYAoKUGFyYSBsb3MgZ2VuZXMgcXVlIGNvbXVubWVudGUgc3ViZW4gc3UgZXhwcmVzacOzbjoKCmBgYHtyIGV2YWw9RkFMU0V9CiAgR08uYW5hbHlzaXMoZGF0YSA9IEJFMkMuREUudXAsIGRhdGFfbmFtZSA9ICJVUCBERSBnZW5lcyBHTyIpCgpgYGAKClBhcmEgbG9zIGdlbmVzIHF1ZSBjb211bm1lbnRlIGJhamFuIHN1IGV4cHJlc2nDs246CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQogIEdPLmFuYWx5c2lzKGRhdGEgPSBCRTJDLkRFLmRvd24sIGRhdGFfbmFtZSA9ICJET1dOIERFIGdlbmVzIEdPIikKYGBgCgpDb21vIHBvZGVtb3MgdmVyIGRlIGxvcyB0cmVzIGFuw6FsaXNpcywgdGFudG8gZW4gZWwgYW7DoWxpc2lzIGRlbCB0b3RhbCBkZSA0NzQgZ2VuZXMgY29tbyBlbiBsb3MgZ2VuZXMgcXVlIGJhamFuIGVuIGV4cHJlc2nDs24gdmVtb3MgdMOpcm1pbm9zIHJlbGFjaW9uYWRvcyBjb24gZWwgY2ljbG8geSBkaXZpc2nDs24gY2VsdWxhci4gTG8gcXVlIHRlbmRyZW1vcyBlbiBjdWVudGEgcGFyYSBlamVtcGxpZmljYXIgbGEgYXByb3hpbWFjacOzbiBkZSBjb21vIHByb2NlZGVyIGVuIGxhIMO6bHRpbWEgcGFydGUgZGUgZXN0ZSBkb2N1bWVudG8uCgojIyMgNy4gQW5hbGlzaXMgR1NFQSBkZSBudWVzdHJvIHNldCBkZSBnZW5lcyBERQoKRGUgZm9ybWEgc2ltaWxhciwgcmVhbGl6byB1biBhbsOhbGlzaXMgR1NFQSBkZSB0b2RvcyBsb3MgZ2VuZXMgKHV0aWxpemFuZG8gZGUgbnVldm8gbG9zIGRhdG9zIHNpbiBmaWx0cmFyIHkgdGVuaWVuZG8gZW4gY3VlbnRhIGVsIHJhbmtpbmcgZW4gYmFzZSBhIHN0YXQpLiBQYXJhIGVsbG8gY3JlbyBkZSBudWV2byB1bmEgZnVuY2nDs24gcGFyYSBhcGxpY2FyIGEgYW1ib3Mgc2hSTkFzLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiAgREVTZXEyLmdzZWEgPC0gcHVycnI6Om1hcCgueD1CRTJDLmNvdW50cywgLmY9IERFU2VxMi5yZXMsIC5wcm9ncmVzcyA9IFRSVUUpCiAgbmFtZXMoREVTZXEyLmdzZWEpIDwtIGxpc3QoIlNIMV9HU0VBIiwgIlNIMl9HU0VBIikKCiAgR1NFQS5hbmFseXNpcyA8LSBmdW5jdGlvbihkYXRhKXsKICAgICAgR1NFQS5yZXMgPC0gZ3NlR08oCiAgICAgICAgZ2VuZUxpc3QgPSBzZXROYW1lcyhkYXRhJHJhbmssIGRhdGEkU3ltYm9sKSwgCiAgICAgICAgT3JnRGIgPSBvcmcuSHMuZWcuZGIsIAogICAgICAgIGtleVR5cGUgPSAiU1lNQk9MIiwgCiAgICAgICAgb250ID0gIkFMTCIsIAogICAgICAgIHBBZGp1c3RNZXRob2QgPSAiQkgiLCAKICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1ICkKICAgICAgCiAgICAgIGdzZWFwbG90IDwtIGxhcHBseSgxOjEwLCBmdW5jdGlvbihpKSB7CiAgICAgICAgZ3NlYXBsb3QyKEdTRUEucmVzLCAKICAgICAgICAgICAgICAgICAgZ2VuZVNldElEID0gaSwgCiAgICAgICAgICAgICAgICAgIHRpdGxlID0gR1NFQS5yZXNAcmVzdWx0JERlc2NyaXB0aW9uW2ldLCAKICAgICAgICAgICAgICAgICAgc3VicGxvdHMgPSAxOjIpCiAgICAgfSkKICAgICAgCiAgICAgICAjIEdlbmVyYXIgeSBndWFyZGFyIGxhcyBncsOhZmljYXMKICAgICBwcmludChnc2VhcGxvdCkKICAgICBwcmludChkb3RwbG90KEdTRUEucmVzLCBzaG93Q2F0ZWdvcnkgPSAyMCwgdGl0bGUgPSAiVG9wIEdTRUEgRW5yaWNoZWQgR08gVGVybXMiLCBmb250LnNpemUgPSA4KSkKICAgICBwcmludChyaWRnZXBsb3QoR1NFQS5yZXMpICsgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIEVucmljaG1lbnQgU2NvcmVzIikgK3RoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSApKQogICAgIHByaW50KGVtYXBwbG90KHBhaXJ3aXNlX3Rlcm1zaW0oR1NFQS5yZXMpLCBzaG93Q2F0ZWdvcnkgPSAyMCwgbGFiZWxfZm9ybWF0ID0gMjApKQogICAgICAgCiAgfQogIAoKYGBgCgpQYXJhIGVsIHNoMToKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQpHU0VBLmFuYWx5c2lzKERFU2VxMi5nc2VhJGBTSDFfR1NFQWApICAgIAoKYGBgCgpQYXJhIGVsIHNoMjoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQpHU0VBLmFuYWx5c2lzKERFU2VxMi5nc2VhJGBTSDJfR1NFQWApICAgIApgYGAKCiMjIyA4LiBDb25jbHVzaW9uZXMgeSBldmFsdWFjacOzbiBkZWwgcHJvYmxlbWEKCkZpbmFsbWVudGUsIHBhcmEgZWplbXBsaWZpY2FyIGNvbW8gZmluYWxpemFyw61hIGVsIGFuw6FsaXNpcyBkZSBlc3RvcyBkYXRvcywgYW5hbGl6YXLDrWEgYWxndW5hIGRlIGxhcyB2w61hcy90ZXJtaW5vcyBkZSBpbnRlcsOpcyB5L28gcXVlIHNhbGllcmEgZW5yaXF1ZWNpZGEgZW4gbG9zIGFuw6FsaXNpcyBhbnRlcmlvcmVzLiBQYXJhIGVqZW1wbGlmaWNhciB1dGlsaXpvIHVuIHNldCBkZSBnZW5lcyByZWxhY2lvbmFkb3MgY29uIENpY2xvIGNlbHVsYXIgeSBEaXZpc2nDs24gcGFyYSB2ZXIgY29tbyBzZSBleHByZXNhbiBlbiB1biBoZWF0bWFwIGVuIGVsIHF1ZSByZXByZXNlbnRvIGxvZzJGQyBkZSBjYWRhIHNoUk5BIHJlc3BlY3RvIGFsIGNvbnRyb2wgKGFsdGVybmF0aXZhbWVudGUgY29uIGxvcyBjb3VudHMgcG9kcsOtYSByZXByZXNlbnRhciBUUE0gbyBjYWxjdWxhciB5IHJlcHJlc2VudGFsIHotU2NvcmVzIGRlIGxhcyBtdWVzdHJhcykuIERlIGVzdGEgZm9ybWEgZGUgZXNlIHNldCBkZSBnZW5lcyBkZSB0cmFnZXQgKGRlIGNpY2xvL2RpdmlzacOzbikgcmVwcmVzZW50byBsb3MgcXVlIGVzdMOhbiBjb211bm1lbnRlIERFIGVuIGFtYm9zIHNoUk5BcyAocmVkdWNpZW5kbyBzdSBuw7ptZXJvKS4gVmVvIHBvciB0YW50byBxdWUgYWNvcmRlIGNvbiBsbyBxdWUgc2UgdmXDrWEgZW4gZWwgR08gYW7DoWxpc2lzIHZlbW9zIHF1ZSBhbCBzaWxlbmNpYXIgQWhSIGhheSByZWd1bGFkb3JlcyBjbGF2ZSBkZWwgY2ljbG8geSBkaXZpc2nDs24gY2VsdWxhciBhbHRlcmFkb3MsIGxvIHF1ZSBwb2Ryw61hIHVzYXJzZSBlbiBlc3RlIGVqZW1wbG8gY29tbyBiYXNlIHBhcmEgdW5hIHZhbGlkYWNpw7NuIGZ1bmNpb25hbCBkZSBlc3RhIHBvc2libGUgcmVndWxhY2nDs24gZGUgQWhSLgoKYGBge3J9CiAjIENyZWFyIHVuYSBsaXN0YSBkZSB0YXJnZXRzIGRlIEFoUiB5IGFub3RhY2nDs24gZGUgc3UgZnVuY2nDs24gcHJpbmNpcGFsCgpUYXJnZXQubGlzdCA8LSBkYXRhLmZyYW1lKFRhcmdldHMgPSBjKCJDREsxIiwgIkNESzIiLCAiQ0RLNCIsICJDREs2IiwgIkNDTkEyIiwgIkNDTkIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0NORDEiLCAiQ0NORTEiLCAiUkIxIiwgIlRQNTMiLCAiRTJGMSIsICJNWUMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQVVSS0EiLCAiQVVSS0IiLCAiUExLMSIsICJCVUIxIiwgIk1BRDJMMSIsICJDREMyMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNESDEiLCAiU0tQMiIsICJBUEMiLCAiUFRURzEiLCAiVFRLIiwgIkNFTlBFIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktJRjExIiwgIktJRjJDIiwgIlJBQ0dBUDEiLCAiQ1lMRCIsICJDSEVLMSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDSEVLMiIsICJQQ05BIiwgIlJGQzEiLCAiQ0RDNiIsICJNQ00yIiwgIk1DTTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPUkMxIiwgIkNDTkgiLCAiQ0RLNyIsICJDREs5IiwgIktJRjIzIiwgIk5FSzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFU1BMMSIsICJTR08xIiwgIlNHTzIiLCAiSU5DRU5QIiwgIkNESzExQiIsICJDQ05IIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ0RLMTIiLCAiQ0RLMTMiLCAiRk9YTTEiLCAiVUJFMlMiLCAiQ0RLNSIsICJDREsxMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNDTkQyIiwgIkNDTkQzIiwgIkNDTkExIiwgIkNDTkIyIiwgIkNDTkIzIiwgIkNDTkUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUkJMMSIsICJSQkwyIikpICU+JSAKICBkcGx5cjo6bXV0YXRlKEZ1bmN0aW9uID0gIkNlbGwgY2ljbGUiKQoKCiAgIyBTZWxlY2Npb25hciBkZSBhbWJvcyBzaFJOQSBsb3MgbG9nMkZDIHJlc3BlY3RvIGFsIGNvbnRyb2wgcGFyYSB1biBIZWF0TWFwCkJFMkMuSE0gPC0gQkUyQy5ERS5saXN0ICU+JSBkcGx5cjo6c2VsZWN0KFN5bWJvbCwgbG9nMkZvbGRDaGFuZ2Vfc2gxLCBsb2cyRm9sZENoYW5nZV9zaDIpIAogICMgU2VsZWNjaW9ubyBkZSBhbWJvcyBzaCBsb3MgZ2VuZXMgVGFyZ2V0cyBxdWUgc2VhbiBzaWduaWZpY2F0aXZvcyBlbiBhbWJvcyBzaFJOQQpUYXJnZXRzLkhNIDwtIEJFMkMuREUubGlzdCAlPiUgZHBseXI6OnNlbGVjdChTeW1ib2wsIGxvZzJGb2xkQ2hhbmdlX3NoMSwgbG9nMkZvbGRDaGFuZ2Vfc2gyKSAlPiUgCiAgc3Vic2V0KFN5bWJvbCAlaW4lIFRhcmdldC5saXN0JFRhcmdldHMpIApyb3duYW1lcyhUYXJnZXRzLkhNKSA8LSBOVUxMClRhcmdldHMuSE0gPC0gVGFyZ2V0cy5ITSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKHZhcj0iU3ltYm9sIikgJT4lIAogIGRwbHlyOjpyZW5hbWUoU0gxID0gbG9nMkZvbGRDaGFuZ2Vfc2gxLCBTSDIgPSBsb2cyRm9sZENoYW5nZV9zaDIpClRhcmdldC5ITS5hbm4gPC0gYXMuZGF0YS5mcmFtZShUYXJnZXQubGlzdCkgJT4lIHN1YnNldChUYXJnZXRzICVpbiUgcm93bmFtZXMoVGFyZ2V0cy5ITSkpIAoKCiAgIyBHZW5lcm8gdW4gSGVhdG1hcCBjb24gbG9zIHRhcmdldHMgcXVlIHNvbiBzaWduaWZpY2F0aXZvcyBwYXJhIGFtYm9zIHNoUk5BcwogICAgICAjIENyZWFyIG1hdHJpeiB5IGFubm90YXRpb24gaW5wdXQgcGFyYSBDb21wbGV4IEhhdG1hcAogIFRhcmdldHMuSE0gPC0gYXMubWF0cml4KFRhcmdldHMuSE0pCiAgVGFyZ2V0LkhNLmFubiA8LSBIZWF0bWFwQW5ub3RhdGlvbihkZiA9IFRhcmdldC5ITS5hbm4kRnVuY3Rpb24sIG5hbWUgPSAiVGFyZ2V0IEZ1bmN0aW9uIiwgd2hpY2g9ICJyb3ciKQogIAogIHByaW50KCBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChUYXJnZXRzLkhNLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3dfdGl0bGUgPSAiQWhSIHRhcmdldCBFeHByZXNzaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd190aXRsZV9zaWRlID0gImxlZnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX3JvdCA9IDQ1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDEyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSAxMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmlnaHRfYW5ub3RhdGlvbiA9IFRhcmdldC5ITS5hbm4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lPSAiTG9nMkZDIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPWNvbG9yUmFtcDIoYygtNCwgMCwgNCksIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF93aWR0aCA9IHVuaXQoMiwgIm5wYyIpICwgaGVhdG1hcF9oZWlnaHQgPSB1bml0KDgsICJucGMiKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkKCmBgYAoKXCpcKk5PVEE6IEFsIGNvbXByb2JhciBzaSBlbCBtaXNtbyBzY3JpcHQgcG9kcsOtYSB1dGlsaXphcnNlIGVuIGxhIGzDrW5lYSBjZWx1bGFyIEtlbGx5LCBubyBzZSB2ZSB1biBlZmVjdG8gdGFuIGNsYXJvIGVuIGVsIGNpY2xvIHBvciBsbyBxdWUgZW4gdW4gY2FzbyByZWFsLCBzZSBkZWJlcsOtYW4gY29tYmluYXIga29zIHJlc3VsdGFkb3MgZGUgYW1iYXMgbMOtbmVhcyBjZWx1bGFyZXMgcGFyYSBidXNjYXIgdGFyZ2V0cyBxdWUgdmFsaWRhciBmdW5jaW9uYWxtZW50ZS4KCiMjIyA5LiBHdWFyZGFyIGVsIGVudG9ybm8gZGUgdHJhYmFqbwoKYGBge3J9CnNhdmUuaW1hZ2UoZmlsZSA9ICJUcmFiYWpvIEZpbiBDdXJzbyBSIFZpY2VudGUgRmVybsOhbmRleiBSb2Rlcm8gUk1ELlJEYXRhIikKYGBgCg==