Worklfow anàlisis RNA-seq

  • Aquest document descriu el worflow per analitzar les dades de RNA-seq, des de el seu alineament fins als anàlisis d’enriquiment funcional.
  • Conté codi BASH, identificat en el codi amb el chunk {bash} i en l’informe amb color verd pàlid, i codi R, identificar en el codi amb el chunk {r} i amb color violeta pàlid en l’informe. Els programes amb BASH no s’executen a R, van directament a la Terminal/Consola. Son els segünets:
    • FastQC
    • MultiQC
    • STAR
    • samtools
  • El codi esta estructurat dins chunks, si es vol executar una part del codi cal substituïr l’atribut eval=F per eval=T.
  • Està preparat per ser executat en l’ordenador de Genòmica a través del link facilitat. En cas de que es vulgui instalar en un altra ordenador caldrà instalar les dependències corresponents.

Quality Control of FASTQ files

El primer pas del flux de treball RNA-Seq és agafar els fitxers FASTQ rebuts de la seqüenciació i avaluar la qualitat de les lectures de la seqüència.

Assessing quality with FastQC

mkdir ./RESULTATS/QC

fastqc -o ./RESULTATS/QC  ./RAW/*/*.gz
# Es borrarà els QC anteriors
rm -d ./RESULTATS/QC/multiqc_data/*
rm -d ./RESULTATS/QC/multiqc_data/
rm ./RESULTATS/QC/multiqc_report.html

multiqc -o ./RESULTATS/QC/ ./RESULTATS/QC/ 

Interpretció FastQC Report

Basic statistics

Estadístiques bàsiques de la mostra. Comprovar que la longitud de lectura i el contingut %GC siguin els esperats.

Per base sequence quality

Gràfica amb la distribució de les puntuacions de qualitat a cada posició de la lectura en totes les lectures. L’eix Y dóna les puntuacions de qualitat, mentre que l’eix X representa la posició a la lectura. La codificació de colors de la trama indica la qualitat de la puntuació (Alt-Mitg-Baix).

Pot indicar possibles errors técnicns durant la seqüenciació.

Per sequence quality scores

Mostra la puntuació de qualitat mitjana a l’eix X i el nombre de seqüències amb aquesta mitjana a l’eix Y. S’espera que la majoria de les lectures tinguin una puntuació de qualitat mitjana alta.

Per base sequence content

Sempre dóna un FAIL per a les dades de la seqüència d’ARN. Això es deu al fet que les primeres 10-12 bases resulten de l’encebament de l’hexàmer “atzar” que es produeix durant la preparació de la biblioteca d’ARN-seq. Aquest cebament no és tan aleatori com podríem esperar, donant un enriquiment en bases particulars per a aquests nucleòtids inicials.

Per sequence GC content

Mostra la distribució de GC en totes les seqüències. La distribució hauria de ser normal tret que hi hagi seqüències sobrerepresentades (pics aguts en una distribució normal) o contaminació amb un altre organisme (pic ampli). Les seqüències sobrerepresentades poden indicar contaminació o un gen molt sobreexpressat.

Over-represented sequences

Presència de seqüències (almenys 20 pb) que es produeixen en més del 0,1% del nombre total de seqüències. Aquesta taula ajuda a identificar la contaminació, com ara seqüències de vectors o adaptadors. Si el contingut %GC falla, aquesta taula pot ajudar a identificar la font. Si no apareix com a adaptador o vector conegut, pot ajudar a BLAST la seqüència per determinar la identitat.

Adapter Content

Avís si hi ha alguna seqüència en més del 10% de totes les lectures.

FastQC Report de totes les mostres (MultiQC)

`

Alineament

Index

STAR Aligner

Creating a genome index

20 minuts

Genoma de referència i GTF descarregats de http://www.ensembl.org/info/data/ftp/index.html

  • Mus_musculus.GRCm39.dna.primary_assembly.fa
  • Mus_musculus.GRCm39.105.gtf

–readFilesCommand zcat: No funciona en el genoma de referenica i el gtf

–sjdbOverhang option needs to be specified for detecting possible splicing sites. Default default: 100 Hauria de ser longitud de lectura -1

S’ha de fer correr una única vegada. Un cop creat el index (50 minuts aprox) evitar tornar-lo a executar (eval=F).

–runThreadN 10: No tocar

STAR --runMode genomeGenerate --genomeDir REF/ --genomeFastaFiles REF/Mus_musculus.GRCm39.dna.primary_assembly.fa --sjdbGTFfile REF/Mus_musculus.GRCm39.105.gtf --outFileNamePrefix RESULTATS/INDEX/INDEX --runThreadN 10 --sjdbOverhang 99

Aligning reads to the genome

4~6 minuts per mostra.

Alineament mostra per mostra:


STAR --genomeDir RESULTATS/INDEX/ --readFilesIn RAW/C4E2/C4E2_1.fq.gz RAW/C4E2/C4E2_2.fq.gz --readFilesCommand zcat --outSAMtype BAM SortedByCoordinate --quantMode GeneCounts --outFileNamePrefix alignments/C4E2 --runThreadN 10

STAR --genomeDir RESULTATS/INDEX/ --readFilesIn RAW/C4E4/C4E4_1.fq.gz RAW/C4E4/C4E4_2.fq.gz --readFilesCommand zcat --outSAMtype BAM SortedByCoordinate --quantMode GeneCounts --outFileNamePrefix alignments/C4E4 --runThreadN 10


STAR --genomeDir RESULTATS/INDEX/ --readFilesIn RAW/C5E1/C5E1_1.fq.gz RAW/C5E1/C5E1_2.fq.gz --readFilesCommand zcat --outSAMtype BAM SortedByCoordinate --quantMode GeneCounts --outFileNamePrefix alignments/C5E1 --runThreadN 10

STAR --genomeDir RESULTATS/INDEX/ --readFilesIn RAW/C5E2/C5E2_1.fq.gz RAW/C5E2/C5E2_2.fq.gz --readFilesCommand zcat --outSAMtype BAM SortedByCoordinate --quantMode GeneCounts --outFileNamePrefix alignments/C5E2 --runThreadN 10

Loop per alinear tores les mostres dins la carpeta RAW.

BAM to SAM

No cal. STAR ofereix la sortida directament a BAM (si es vol). Si es necessita obtenir el SAM per algún motiu es pot fer amb la segünet instrucció (canviar eval=F per eval=T)

samtools view -h alignments/TEST_5Aligned.sortedByCoord.out.bam > alignments/TEST_5.sam

Visualització IGV, browser etc

Fer l’index de cada mostra. Es crea C4E2Aligned.sortedByCoord.out.bam.bai


 samtools index alignments/C4E2Aligned.sortedByCoord.out.bam
 

Anàlisi qualitat dels alignment

10 min aprox per mostra

WARNING: out of memory!

Ull viu amb la memòria.

Aquest pas requereix bastanta memòria. Hi ha una carpeta tempora que després es buida sola. El resultat final es desa en la carpeta RESULTATS/QC

mkdir ../tmp
export _JAVA_OPTIONS="-Djava.io.tmpdir=../tmp -Xmx6G"
../qualimap_v2.2.1/qualimap rnaseq -pe -bam alignments/C4E4Aligned.sortedByCoord.out.bam -gtf REF/Mus_musculus.GRCm39.105.gtf   -outdir RESULTATS/QC/C4E4Aligned -p strand-specific-reverse

# 5.1 GB

MultiQC

# Aquest multiqc porta el nom multiqc_report_1.html. 
# Es borrarà els QC anteriors
rm -d ./RESULTATS/QC/multiqc_data_1/*
rm -d ./RESULTATS/QC/multiqc_data_1/
rm ./RESULTATS/QC/multiqc_report_1.html

multiqc -o ./RESULTATS/QC/ ./RESULTATS/QC/ 

Expressió diferencial

# Lectura dels Counts
arxius_reads<-list.files("./alignments/",pattern = "ReadsPerGene.out.tab",full.names = T)
count_matrix = lapply(arxius_reads, function(x) {read.delim(x,header = F,col.names = c("gene ID","counts unstranded","counts 1st read strand","counts2nd read strand"),skip=4)})
columna_ID = colnames(head(count_matrix[[1]]))[c(4)]
result = lapply(count_matrix, "[",,  columna_ID)
result<-(data.frame(result))
# Assignar els gens
rownames(result)<-count_matrix[[1]]$gene.ID
## reverse stranded -> 4ta columna
# Crear taula fenotipica
sample_sheet<-
data.frame(
'filename'=basename(arxius_reads),
'sample name'=gsub("ReadsPerGene.out.tab","",basename(arxius_reads)),
'group'=c(1,1,2,2))
rownames(sample_sheet)<-sample_sheet$filename
# Guardar i modificar en l'excel
write_xlsx(sample_sheet,"./sample_sheet.xlsx")
# Lerctura de la taula modificada
sample_sheet<-read_excel("./sample_sheet.xlsx")
sample_sheet$group<-as.factor(sample_sheet$group)

datatable(sample_sheet,caption = "Taula fenotip per completar",rownames = F,extensions = 'Buttons',options = list(
    pageLength = 5,dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))
colnames(result)<-sample_sheet$filename
# crear el DESeqDataSet. A Design s'ha de definir la comparativa
se_star_matrix <- DESeqDataSetFromMatrix(countData = result,
                                  colData = sample_sheet,
                                  design = ~ group)

Filtratge

  • Filtrar gens amb més de 10 reads
pre_n<-nrow(se_star_matrix)

# Filtrar gens amb més de 10 reads
se_star_matrix <- se_star_matrix[rowSums(counts(se_star_matrix)) > 10, ]


# Number of genes left after low-count filtering:
post_n<-nrow(se_star_matrix)
print(paste0("S'han eliminat ",pre_n-post_n, " gens"))

[1] “S’han eliminat 37316 gens”

Model estadístic

Transformació de les dades

vsd <- vst(se_star_matrix_2, blind = FALSE)

rld <- rlog(se_star_matrix_2, blind = FALSE)




dds <- estimateSizeFactors(se_star_matrix_2)

df <- bind_rows(
  as_data_frame(log2(counts(se_star_matrix_2, normalized=TRUE)[, 1:2]+1)) %>%
    mutate(transformation = "log2(x + 1)"),
  as_data_frame(assay(vsd)[, 1:2]) %>% mutate(transformation = "vst"),
  as_data_frame(assay(rld)[, 1:2]) %>% mutate(transformation = "rlog"))

colnames(df)[1:2] <- c("x", "y")  

lvls <- c("log2(x + 1)", "vst", "rlog")
df$transformation <- factor(df$transformation, levels=lvls)

ggplot(df, aes(x = x, y = y)) + geom_hex(bins = 80) +
  coord_fixed() + facet_grid( . ~ transformation)  

Visualització

resultsNames(se_star_matrix_2)
## [1] "Intercept"    "group_2_vs_1"
de <- results(object = se_star_matrix_2, 
        name=resultsNames(se_star_matrix_2)[2])

de_shrink <- lfcShrink(dds = se_star_matrix_2,coef=resultsNames(se_star_matrix_2)[2],type="apeglm")

Gens DE significatius

dir.create("./RESULTATS/TAULES")
de_symbols<-merge(log_counts,unique(gtf_df[,c("gene_id","gene_name")]),by.x="row.names",by.y="gene_id")
de_symbols <- merge(unique(gtf_df[,c("gene_id","gene_name")]), data.frame(ID=rownames(de_shrink), de_shrink), by=1, all=F)
write.table(de_symbols, "./RESULTATS/TAULES/deseq2_results.txt", quote=F, col.names=T, row.names=F, sep="\t")

datatable(de_symbols %>% filter(padj<=0.05),caption = "Gene Ontology (ORA)",extensions = 'Buttons',options = list(
  columnDefs = list(list(className = 'dt-center', targets = 5)),
  pageLength = 5,dom = 'Bfrtip',buttons = c('copy', 'csv', 'excel')))

Significància biologica. EXEMPLES NO SIGNIFICATIUS!!!!!

  • Rich Factor: proporció de gens introduïts (per exemple, DE) que s’assignen a un terme en relació a tots els gens estudiats anotats en aquest terme.

  • Gene Ratio: proporció de gens introduïts (per exemple, DE) que s’assignen a un terme en relació a tots els gens en aquest terme.

#Referències:

LS0tCmF1dGhvcjogIlBsYXRhZm9ybWEgR2Vuw7JtaWNhIGkgQmlvaW5mb3Jtw6B0aWNhICBbUGxhbnRpbGxhIFNvbMK3bGljaXR1ZF0oaHR0cDovL3d3dy5pZGlzYmEuZXMvY2F0L1BvcnRhbHMvMC9Eb2N1bWVudG9zL1BsYXRhZm9ybWFzJTIwV2ViL0dlbm9taWNhL1NvbGljaXR1ZCUyMFNlcnZpY2lvcyUyMEdFTk9NSUNBLnBkZikiCm91dHB1dDoKICAjIG91dHB1dDogcGRmX2RvY3VtZW50CiAgcm1kZm9ybWF0czo6cm9ib2Jvb2s6CiAgICAKICAgIGhpZ2hsaWdodDoga2F0ZQogICAgdG9jOiAyCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCnNlbGZfY29udGFpbmVkOiBmYWxzZQogICAgCi0tLQpgYGB7cixpbmNsdWRlPUZ9CnNvdXJjZSgiLi9jb2RpL2xpYnMuUiIpCmtuaXRyOjpvcHRzX2NodW5rJHNldChpbmNsdWRlPUYsd2FybmluZyA9IEYsbWVzc2FnZSA9IEYpCmBgYAoKYGBge3IsaW5jbHVkZT1GLGVjaG89VH0KbG9nb3M8LXBhc3RlKCIhW10oL3Vzci9sb2NhbC9saWIvUi9zaXRlLWxpYnJhcnkvcm1kZm9ybWF0cy90ZW1wbGF0ZXMvcm9ib2Jvb2svTG9nb3RpcG9Db3Jwb3JhdGl2by5wbmcpe3dpZHRoPTIwJX1cdCFbXSgvdXNyL2xvY2FsL2xpYi9SL3NpdGUtbGlicmFyeS9ybWRmb3JtYXRzL3RlbXBsYXRlcy9yb2JvYm9vay9iaW9pbmZvX2xvZ28ucG5nKXt3aWR0aD0yMCV9IikKdGl0bGVfZXNwPC1wYXN0ZSgiICIpCnRpdGxlPC1wYXN0ZTAodGl0bGVfZXNwLCIgXG4iLCJcblxuIixsb2dvcykKdmVyc2lvbjwtcGFzdGUwKCJWZXJzacOzOiAiLFN5cy50aW1lKCkpCmBgYAoKLS0tCnRpdGxlOiAiIGByIHRpdGxlYCIKZGF0ZTogIiBgciB2ZXJzaW9uYCIKLS0tCgoKIyBXb3JrbGZvdyBhbsOgbGlzaXMgUk5BLXNlcQoKICAqIEFxdWVzdCBkb2N1bWVudCBkZXNjcml1IGVsIHdvcmZsb3cgcGVyIGFuYWxpdHphciBsZXMgZGFkZXMgZGUgUk5BLXNlcSwgZGVzIGRlIGVsIHNldSBhbGluZWFtZW50IGZpbnMgYWxzIGFuw6BsaXNpcyBkJ2VucmlxdWltZW50IGZ1bmNpb25hbC4KICAqIENvbnTDqSBjb2RpICoqQkFTSCoqLCBpZGVudGlmaWNhdCBlbiBlbCBjb2RpIGFtYiAgZWwgKmNodW5rIHtiYXNofSogaSBlbiBsJ2luZm9ybWUgYW1iIGNvbG9yIHZlcmQgcMOgbGlkLCBpIGNvZGkgKipSKiosIGlkZW50aWZpY2FyIGVuIGVsIGNvZGkgYW1iIGVsICpjaHVuayB7cn0qIGkgYW1iIGNvbG9yIHZpb2xldGEgcMOgbGlkIGVuIGwnaW5mb3JtZS4gRWxzIHByb2dyYW1lcyBhbWIgQkFTSCBubyBzJ2V4ZWN1dGVuIGEgUiwgdmFuIGRpcmVjdGFtZW50IGEgbGEgVGVybWluYWwvQ29uc29sYS4gU29uIGVscyBzZWfDvG5ldHM6CiAgICAgICogRmFzdFFDCiAgICAgICogTXVsdGlRQwogICAgICAqIFNUQVIKICAgICAgKiBzYW10b29scwogICogRWwgY29kaSBlc3RhIGVzdHJ1Y3R1cmF0IGRpbnMgKmNodW5rcyosIHNpIGVzIHZvbCBleGVjdXRhciB1bmEgcGFydCBkZWwgY29kaSBjYWwgc3Vic3RpdHXDr3IgbCdhdHJpYnV0IGV2YWw9RiBwZXIgZXZhbD1ULgogICogRXN0w6AgcHJlcGFyYXQgcGVyIHNlciBleGVjdXRhdCBlbiBsJ29yZGVuYWRvciBkZSBHZW7Dsm1pY2EgYSB0cmF2w6lzIGRlbCBsaW5rIGZhY2lsaXRhdC4gRW4gY2FzIGRlIHF1ZSBlcyB2dWxndWkgaW5zdGFsYXIgZW4gdW4gYWx0cmEgb3JkZW5hZG9yIGNhbGRyw6AgaW5zdGFsYXIgbGVzIGRlcGVuZMOobmNpZXMgY29ycmVzcG9uZW50cy4KICAKCiMgUXVhbGl0eSBDb250cm9sIG9mIEZBU1RRIGZpbGVzCgpFbCBwcmltZXIgcGFzIGRlbCBmbHV4IGRlIHRyZWJhbGwgUk5BLVNlcSDDqXMgYWdhZmFyIGVscyBmaXR4ZXJzIEZBU1RRIHJlYnV0cyBkZSBsYSBzZXHDvGVuY2lhY2nDsyBpIGF2YWx1YXIgbGEgcXVhbGl0YXQgZGUgbGVzIGxlY3R1cmVzIGRlIGxhIHNlccO8w6huY2lhLgoKCiMjIEFzc2Vzc2luZyBxdWFsaXR5IHdpdGggRmFzdFFDCmBgYHtiYXNoLGV2YWw9RixpbmNsdWRlPVR9Cm1rZGlyIC4vUkVTVUxUQVRTL1FDCgoKYGBgCgpgYGB7YmFzaCxldmFsPUYsaW5jbHVkZT1UfQoKZmFzdHFjIC1vIC4vUkVTVUxUQVRTL1FDICAuL1JBVy8qLyouZ3oKCgpgYGAKCgpgYGB7YmFzaCxldmFsPUYsaW5jbHVkZT1UfQojIEVzIGJvcnJhcsOgIGVscyBRQyBhbnRlcmlvcnMKcm0gLWQgLi9SRVNVTFRBVFMvUUMvbXVsdGlxY19kYXRhLyoKcm0gLWQgLi9SRVNVTFRBVFMvUUMvbXVsdGlxY19kYXRhLwpybSAuL1JFU1VMVEFUUy9RQy9tdWx0aXFjX3JlcG9ydC5odG1sCgptdWx0aXFjIC1vIC4vUkVTVUxUQVRTL1FDLyAuL1JFU1VMVEFUUy9RQy8gCgpgYGAKCmBgYHtyLCBvdXQud2lkdGg9IjEwMCUiLHJlc3VsdHM9J2FzaXMnLGluY2x1ZGU9VCxlY2hvPUZ9CmNhdCgnPGJ1dHRvbj48YSBocmVmPSIuL1JFU1VMVEFUUy9RQy9DNEUyXzFfZmFzdHFjLmh0bWwiIHRhcmdldD0iX2JsYW5rIj5PYnJpciBGYXN0UUMgUmVwb3J0IGVuIHVuYSBub3ZhIGZpbmVzdHJhPC9hPjwvYnV0dG9uPicpCmtuaXRyOjppbmNsdWRlX3VybCgiLi9SRVNVTFRBVFMvUUMvQzRFMl8xX2Zhc3RxYy5odG1sIixoZWlnaHQgPSAzMDApCgpgYGAKCiMjIyBJbnRlcnByZXRjacOzIEZhc3RRQyBSZXBvcnQKCiMjIyMgQmFzaWMgc3RhdGlzdGljcwoKRXN0YWTDrXN0aXF1ZXMgYsOgc2lxdWVzIGRlIGxhIG1vc3RyYS4gQ29tcHJvdmFyIHF1ZSBsYSBsb25naXR1ZCBkZSBsZWN0dXJhIGkgZWwgY29udGluZ3V0ICVHQyBzaWd1aW4gZWxzIGVzcGVyYXRzLgoKIyMjIyBQZXIgYmFzZSBzZXF1ZW5jZSBxdWFsaXR5CgpHcsOgZmljYSBhbWIgbGEgZGlzdHJpYnVjacOzIGRlIGxlcyBwdW50dWFjaW9ucyBkZSBxdWFsaXRhdCBhIGNhZGEgcG9zaWNpw7MgZGUgbGEgbGVjdHVyYSBlbiB0b3RlcyBsZXMgbGVjdHVyZXMuIEwnZWl4IFkgZMOzbmEgbGVzIHB1bnR1YWNpb25zIGRlIHF1YWxpdGF0LCBtZW50cmUgcXVlIGwnZWl4IFggcmVwcmVzZW50YSBsYSBwb3NpY2nDsyBhIGxhIGxlY3R1cmEuIExhIGNvZGlmaWNhY2nDsyBkZSBjb2xvcnMgZGUgbGEgdHJhbWEgaW5kaWNhIGxhIHF1YWxpdGF0IGRlIGxhIHB1bnR1YWNpw7MgKEFsdC1NaXRnLUJhaXgpLgoKUG90IGluZGljYXIgcG9zc2libGVzIGVycm9ycyB0w6ljbmljbnMgZHVyYW50IGxhIHNlccO8ZW5jaWFjacOzLgoKIyMjIyBQZXIgc2VxdWVuY2UgcXVhbGl0eSBzY29yZXMKCk1vc3RyYSBsYSBwdW50dWFjacOzIGRlIHF1YWxpdGF0IG1pdGphbmEgYSBsJ2VpeCBYIGkgZWwgbm9tYnJlIGRlIHNlccO8w6huY2llcyBhbWIgYXF1ZXN0YSBtaXRqYW5hIGEgbCdlaXggWS4gUydlc3BlcmEgcXVlIGxhIG1ham9yaWEgZGUgbGVzIGxlY3R1cmVzIHRpbmd1aW4gdW5hIHB1bnR1YWNpw7MgZGUgcXVhbGl0YXQgbWl0amFuYSBhbHRhLgoKIyMjIyBQZXIgYmFzZSBzZXF1ZW5jZSBjb250ZW50CgpTZW1wcmUgZMOzbmEgdW4gRkFJTCBwZXIgYSBsZXMgZGFkZXMgZGUgbGEgc2Vxw7zDqG5jaWEgZCdBUk4uIEFpeMOyIGVzIGRldSBhbCBmZXQgcXVlIGxlcyBwcmltZXJlcyAxMC0xMiBiYXNlcyByZXN1bHRlbiBkZSBsJ2VuY2ViYW1lbnQgZGUgbCdoZXjDoG1lciAiYXR6YXIiIHF1ZSBlcyBwcm9kdWVpeCBkdXJhbnQgbGEgcHJlcGFyYWNpw7MgZGUgbGEgYmlibGlvdGVjYSBkJ0FSTi1zZXEuIEFxdWVzdCBjZWJhbWVudCBubyDDqXMgdGFuIGFsZWF0b3JpIGNvbSBwb2Ryw61lbSBlc3BlcmFyLCBkb25hbnQgdW4gZW5yaXF1aW1lbnQgZW4gYmFzZXMgcGFydGljdWxhcnMgcGVyIGEgYXF1ZXN0cyBudWNsZcOydGlkcyBpbmljaWFscy4KCiMjIyMgUGVyIHNlcXVlbmNlIEdDIGNvbnRlbnQKCk1vc3RyYSBsYSBkaXN0cmlidWNpw7MgZGUgR0MgZW4gdG90ZXMgbGVzIHNlccO8w6huY2llcy4gTGEgZGlzdHJpYnVjacOzIGhhdXJpYSBkZSBzZXIgbm9ybWFsIHRyZXQgcXVlIGhpIGhhZ2kgc2Vxw7zDqG5jaWVzIHNvYnJlcmVwcmVzZW50YWRlcyAocGljcyBhZ3V0cyBlbiB1bmEgZGlzdHJpYnVjacOzIG5vcm1hbCkgbyBjb250YW1pbmFjacOzIGFtYiB1biBhbHRyZSBvcmdhbmlzbWUgKHBpYyBhbXBsaSkuCkxlcyBzZXHDvMOobmNpZXMgc29icmVyZXByZXNlbnRhZGVzIHBvZGVuIGluZGljYXIgIGNvbnRhbWluYWNpw7MgbyB1biBnZW4gbW9sdCBzb2JyZWV4cHJlc3NhdC4KCgogCiMjIyMgT3Zlci1yZXByZXNlbnRlZCBzZXF1ZW5jZXMKClByZXPDqG5jaWEgZGUgc2Vxw7zDqG5jaWVzIChhbG1lbnlzIDIwIHBiKSBxdWUgZXMgcHJvZHVlaXhlbiBlbiBtw6lzIGRlbCAwLDElIGRlbCBub21icmUgdG90YWwgZGUgc2Vxw7zDqG5jaWVzLiBBcXVlc3RhIHRhdWxhIGFqdWRhIGEgaWRlbnRpZmljYXIgbGEgY29udGFtaW5hY2nDsywgY29tIGFyYSBzZXHDvMOobmNpZXMgZGUgdmVjdG9ycyBvIGFkYXB0YWRvcnMuIFNpIGVsIGNvbnRpbmd1dCAlR0MgZmFsbGEsIGFxdWVzdGEgdGF1bGEgcG90IGFqdWRhciBhIGlkZW50aWZpY2FyIGxhIGZvbnQuIFNpIG5vIGFwYXJlaXggY29tIGEgYWRhcHRhZG9yIG8gdmVjdG9yIGNvbmVndXQsIHBvdCBhanVkYXIgYSAqQkxBU1QqIGxhIHNlccO8w6huY2lhIHBlciBkZXRlcm1pbmFyIGxhIGlkZW50aXRhdC4KCgojIyMjIEFkYXB0ZXIgQ29udGVudAoKQXbDrXMgc2kgaGkgaGEgYWxndW5hIHNlccO8w6huY2lhIGVuIG3DqXMgZGVsIDEwJSBkZSB0b3RlcyBsZXMgbGVjdHVyZXMuCgojIyBGYXN0UUMgUmVwb3J0IGRlIHRvdGVzIGxlcyBtb3N0cmVzIChNdWx0aVFDKQpgCmBgYHtyLCBvdXQud2lkdGg9IjEwMCUiLHJlc3VsdHM9J2FzaXMnLGluY2x1ZGU9VCxlY2hvPUZ9CgpjYXQoJzxidXR0b24+PGEgaHJlZj0iLi9SRVNVTFRBVFMvUUMvbXVsdGlxY19yZXBvcnQuaHRtbCIgdGFyZ2V0PSJfYmxhbmsiPk9icmlyIE11bHRpUUMgZW4gdW5hIG5vdmEgZmluZXN0cmE8L2E+PC9idXR0b24+JykKa25pdHI6OmluY2x1ZGVfdXJsKCIuL1JFU1VMVEFUUy9RQy9tdWx0aXFjX3JlcG9ydC5odG1sIixoZWlnaHQgPSAzMDApCmBgYAoKIyBBbGluZWFtZW50CgpgYGB7cixpbmNsdWRlPVQsZWNobz1GLG91dC53aWR0aD0iODAlIn0KCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL1JFU1VMVEFUUy9QTE9UUy9hbGluZWFtZW50LnBuZyIpCgpgYGAKCiMjIEluZGV4CgoKIyMgU1RBUiBBbGlnbmVyCgojIyMgQ3JlYXRpbmcgYSBnZW5vbWUgaW5kZXgKCjIwIG1pbnV0cwoKR2Vub21hIGRlIHJlZmVyw6huY2lhIGkgR1RGIGRlc2NhcnJlZ2F0cyBkZSBodHRwOi8vd3d3LmVuc2VtYmwub3JnL2luZm8vZGF0YS9mdHAvaW5kZXguaHRtbAoKICAqIE11c19tdXNjdWx1cy5HUkNtMzkuZG5hLnByaW1hcnlfYXNzZW1ibHkuZmEKICAqIE11c19tdXNjdWx1cy5HUkNtMzkuMTA1Lmd0ZgoKLS1yZWFkRmlsZXNDb21tYW5kIHpjYXQ6IE5vIGZ1bmNpb25hIGVuIGVsIGdlbm9tYSBkZSByZWZlcmVuaWNhIGkgZWwgZ3RmCgotLXNqZGJPdmVyaGFuZyBvcHRpb24gbmVlZHMgdG8gYmUgc3BlY2lmaWVkIGZvciBkZXRlY3RpbmcgcG9zc2libGUgc3BsaWNpbmcgc2l0ZXMuIERlZmF1bHQgZGVmYXVsdDogMTAwCkhhdXJpYSBkZSBzZXIgbG9uZ2l0dWQgZGUgbGVjdHVyYSAtMQoKClMnaGEgZGUgZmVyIGNvcnJlciB1bmEgw7puaWNhIHZlZ2FkYS4gVW4gY29wIGNyZWF0IGVsIGluZGV4ICg1MCBtaW51dHMgYXByb3gpIGV2aXRhciB0b3JuYXItbG8gYSBleGVjdXRhciAoKipldmFsPUYqKikuCgoqKi0tcnVuVGhyZWFkTiAxMDogTm8gdG9jYXIqKgoKCmBgYHtiYXNoLGV2YWw9RixpbmNsdWRlPVR9ClNUQVIgLS1ydW5Nb2RlIGdlbm9tZUdlbmVyYXRlIC0tZ2Vub21lRGlyIFJFRi8gLS1nZW5vbWVGYXN0YUZpbGVzIFJFRi9NdXNfbXVzY3VsdXMuR1JDbTM5LmRuYS5wcmltYXJ5X2Fzc2VtYmx5LmZhIC0tc2pkYkdURmZpbGUgUkVGL011c19tdXNjdWx1cy5HUkNtMzkuMTA1Lmd0ZiAtLW91dEZpbGVOYW1lUHJlZml4IFJFU1VMVEFUUy9JTkRFWC9JTkRFWCAtLXJ1blRocmVhZE4gMTAgLS1zamRiT3ZlcmhhbmcgOTkKCmBgYAoKCiMjIEFsaWduaW5nIHJlYWRzIHRvIHRoZSBnZW5vbWUKCjR+NiBtaW51dHMgcGVyIG1vc3RyYS4KCkFsaW5lYW1lbnQgbW9zdHJhIHBlciBtb3N0cmE6CgpgYGB7YmFzaCxldmFsPUYsaW5jbHVkZT1UfQoKU1RBUiAtLWdlbm9tZURpciBSRVNVTFRBVFMvSU5ERVgvIC0tcmVhZEZpbGVzSW4gUkFXL0M0RTIvQzRFMl8xLmZxLmd6IFJBVy9DNEUyL0M0RTJfMi5mcS5neiAtLXJlYWRGaWxlc0NvbW1hbmQgemNhdCAtLW91dFNBTXR5cGUgQkFNIFNvcnRlZEJ5Q29vcmRpbmF0ZSAtLXF1YW50TW9kZSBHZW5lQ291bnRzIC0tb3V0RmlsZU5hbWVQcmVmaXggYWxpZ25tZW50cy9DNEUyIC0tcnVuVGhyZWFkTiAxMAoKU1RBUiAtLWdlbm9tZURpciBSRVNVTFRBVFMvSU5ERVgvIC0tcmVhZEZpbGVzSW4gUkFXL0M0RTQvQzRFNF8xLmZxLmd6IFJBVy9DNEU0L0M0RTRfMi5mcS5neiAtLXJlYWRGaWxlc0NvbW1hbmQgemNhdCAtLW91dFNBTXR5cGUgQkFNIFNvcnRlZEJ5Q29vcmRpbmF0ZSAtLXF1YW50TW9kZSBHZW5lQ291bnRzIC0tb3V0RmlsZU5hbWVQcmVmaXggYWxpZ25tZW50cy9DNEU0IC0tcnVuVGhyZWFkTiAxMAoKClNUQVIgLS1nZW5vbWVEaXIgUkVTVUxUQVRTL0lOREVYLyAtLXJlYWRGaWxlc0luIFJBVy9DNUUxL0M1RTFfMS5mcS5neiBSQVcvQzVFMS9DNUUxXzIuZnEuZ3ogLS1yZWFkRmlsZXNDb21tYW5kIHpjYXQgLS1vdXRTQU10eXBlIEJBTSBTb3J0ZWRCeUNvb3JkaW5hdGUgLS1xdWFudE1vZGUgR2VuZUNvdW50cyAtLW91dEZpbGVOYW1lUHJlZml4IGFsaWdubWVudHMvQzVFMSAtLXJ1blRocmVhZE4gMTAKClNUQVIgLS1nZW5vbWVEaXIgUkVTVUxUQVRTL0lOREVYLyAtLXJlYWRGaWxlc0luIFJBVy9DNUUyL0M1RTJfMS5mcS5neiBSQVcvQzVFMi9DNUUyXzIuZnEuZ3ogLS1yZWFkRmlsZXNDb21tYW5kIHpjYXQgLS1vdXRTQU10eXBlIEJBTSBTb3J0ZWRCeUNvb3JkaW5hdGUgLS1xdWFudE1vZGUgR2VuZUNvdW50cyAtLW91dEZpbGVOYW1lUHJlZml4IGFsaWdubWVudHMvQzVFMiAtLXJ1blRocmVhZE4gMTAKCgpgYGAKCkxvb3AgcGVyIGFsaW5lYXIgdG9yZXMgbGVzIG1vc3RyZXMgZGlucyBsYSBjYXJwZXRhIFJBVy4KCgoKQkFNIHRvIFNBTQoKTm8gY2FsLiBTVEFSIG9mZXJlaXggbGEgc29ydGlkYSBkaXJlY3RhbWVudCBhIEJBTSAoc2kgZXMgdm9sKS4gU2kgZXMgbmVjZXNzaXRhIG9idGVuaXIgZWwgU0FNIHBlciBhbGfDum4gbW90aXUgZXMgcG90IGZlciBhbWIgbGEgc2Vnw7xuZXQgaW5zdHJ1Y2Npw7MgKGNhbnZpYXIgZXZhbD1GIHBlciBldmFsPVQpCgpgYGB7YmFzaCxldmFsPUYsaW5jbHVkZT1UfQpzYW10b29scyB2aWV3IC1oIGFsaWdubWVudHMvVEVTVF81QWxpZ25lZC5zb3J0ZWRCeUNvb3JkLm91dC5iYW0gPiBhbGlnbm1lbnRzL1RFU1RfNS5zYW0KYGBgCgpWaXN1YWxpdHphY2nDsyBJR1YsIGJyb3dzZXIgZXRjCgpGZXIgbCdpbmRleCBkZSBjYWRhIG1vc3RyYS4gRXMgY3JlYSBDNEUyQWxpZ25lZC5zb3J0ZWRCeUNvb3JkLm91dC5iYW0uKipiYWkqKgoKYGBge2Jhc2gsZXZhbD1GLGluY2x1ZGU9VH0KCiBzYW10b29scyBpbmRleCBhbGlnbm1lbnRzL0M0RTJBbGlnbmVkLnNvcnRlZEJ5Q29vcmQub3V0LmJhbQogCmBgYAoKCmBgYHtyLGluY2x1ZGU9VCxlY2hvPUYsb3V0LndpZHRoPSI4MCUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiLi9pZ3YucG5nIikKYGBgCgojIyBBbsOgbGlzaSBxdWFsaXRhdCBkZWxzIGFsaWdubWVudCAKCjEwIG1pbiBhcHJveCBwZXIgbW9zdHJhCgpXQVJOSU5HOiBvdXQgb2YgbWVtb3J5IQoKVWxsIHZpdSBhbWIgbGEgbWVtw7JyaWEuCgpBcXVlc3QgcGFzIHJlcXVlcmVpeCBiYXN0YW50YSBtZW3DsnJpYS4gSGkgaGEgdW5hIGNhcnBldGEgdGVtcG9yYSBxdWUgZGVzcHLDqXMgZXMgYnVpZGEgc29sYS4gRWwgcmVzdWx0YXQgZmluYWwgZXMgZGVzYSBlbiBsYSBjYXJwZXRhIFJFU1VMVEFUUy9RQwoKYGBge2Jhc2gsZXZhbD1GLGluY2x1ZGU9VH0KbWtkaXIgLi4vdG1wCgpgYGAKCmBgYHtiYXNoLGV2YWw9RixpbmNsdWRlPVR9CmV4cG9ydCBfSkFWQV9PUFRJT05TPSItRGphdmEuaW8udG1wZGlyPS4uL3RtcCAtWG14NkciCi4uL3F1YWxpbWFwX3YyLjIuMS9xdWFsaW1hcCBybmFzZXEgLXBlIC1iYW0gYWxpZ25tZW50cy9DNEU0QWxpZ25lZC5zb3J0ZWRCeUNvb3JkLm91dC5iYW0gLWd0ZiBSRUYvTXVzX211c2N1bHVzLkdSQ20zOS4xMDUuZ3RmIAktb3V0ZGlyIFJFU1VMVEFUUy9RQy9DNEU0QWxpZ25lZCAtcCBzdHJhbmQtc3BlY2lmaWMtcmV2ZXJzZQoKIyA1LjEgR0IKCmBgYAoKYGBge3IsIG91dC53aWR0aD0iMTAwJSIscmVzdWx0cz0nYXNpcycsaW5jbHVkZT1ULGVjaG89Rn0KY2F0KCc8YnV0dG9uPjxhIGhyZWY9Ii4vUkVTVUxUQVRTL1FDL0M0RTRBbGlnbmVkL3F1YWxpbWFwUmVwb3J0Lmh0bWwiIHRhcmdldD0iX2JsYW5rIj5PYnJpciBRQyBBbGluZWFtZW50IGVuIHVuYSBub3ZhIGZpbmVzdHJhPC9hPjwvYnV0dG9uPicpCmtuaXRyOjppbmNsdWRlX3VybCgiLi9SRVNVTFRBVFMvUUMvQzRFNEFsaWduZWQvcXVhbGltYXBSZXBvcnQuaHRtbCIsaGVpZ2h0ID0gMzAwKQpgYGAKCiMjIyBNdWx0aVFDCgpgYGB7YmFzaCxldmFsPUYsaW5jbHVkZT1UfQojIEFxdWVzdCBtdWx0aXFjIHBvcnRhIGVsIG5vbSBtdWx0aXFjX3JlcG9ydF8xLmh0bWwuIAojIEVzIGJvcnJhcsOgIGVscyBRQyBhbnRlcmlvcnMKcm0gLWQgLi9SRVNVTFRBVFMvUUMvbXVsdGlxY19kYXRhXzEvKgpybSAtZCAuL1JFU1VMVEFUUy9RQy9tdWx0aXFjX2RhdGFfMS8Kcm0gLi9SRVNVTFRBVFMvUUMvbXVsdGlxY19yZXBvcnRfMS5odG1sCgptdWx0aXFjIC1vIC4vUkVTVUxUQVRTL1FDLyAuL1JFU1VMVEFUUy9RQy8gCgpgYGAKCmBgYHtyLCBvdXQud2lkdGg9IjEwMCUiLHJlc3VsdHM9J2FzaXMnLGluY2x1ZGU9VCxlY2hvPUZ9CmNhdCgnPGJ1dHRvbj48YSBocmVmPSIuL1JFU1VMVEFUUy9RQy9tdWx0aXFjX3JlcG9ydF8xLmh0bWwiIHRhcmdldD0iX2JsYW5rIj5PYnJpciBNdWx0aVFDIGVuIHVuYSBub3ZhIGZpbmVzdHJhPC9hPjwvYnV0dG9uPicpCmtuaXRyOjppbmNsdWRlX3VybCgiLi9SRVNVTFRBVFMvUUMvbXVsdGlxY19yZXBvcnRfMS5odG1sIixoZWlnaHQgPSAzMDApCmBgYAoKIyBFeHByZXNzacOzIGRpZmVyZW5jaWFsCgpgYGB7YmFzaCxldmFsPUYsY2xhc3Muc291cmNlPSJiZy1kYW5nZXIiLCBjbGFzcy5vdXRwdXQ9ImJnLXdhcm5pbmcifQpta2RpciAtcCAuL2Rlc2VxMgpgYGAKCmBgYHtyLGluY2x1ZGU9VCxlY2hvPVR9CiMgTGVjdHVyYSBkZWxzIENvdW50cwphcnhpdXNfcmVhZHM8LWxpc3QuZmlsZXMoIi4vYWxpZ25tZW50cy8iLHBhdHRlcm4gPSAiUmVhZHNQZXJHZW5lLm91dC50YWIiLGZ1bGwubmFtZXMgPSBUKQpjb3VudF9tYXRyaXggPSBsYXBwbHkoYXJ4aXVzX3JlYWRzLCBmdW5jdGlvbih4KSB7cmVhZC5kZWxpbSh4LGhlYWRlciA9IEYsY29sLm5hbWVzID0gYygiZ2VuZSBJRCIsImNvdW50cyB1bnN0cmFuZGVkIiwiY291bnRzIDFzdCByZWFkIHN0cmFuZCIsImNvdW50czJuZCByZWFkIHN0cmFuZCIpLHNraXA9NCl9KQpjb2x1bW5hX0lEID0gY29sbmFtZXMoaGVhZChjb3VudF9tYXRyaXhbWzFdXSkpW2MoNCldCnJlc3VsdCA9IGxhcHBseShjb3VudF9tYXRyaXgsICJbIiwsICBjb2x1bW5hX0lEKQpyZXN1bHQ8LShkYXRhLmZyYW1lKHJlc3VsdCkpCiMgQXNzaWduYXIgZWxzIGdlbnMKcm93bmFtZXMocmVzdWx0KTwtY291bnRfbWF0cml4W1sxXV0kZ2VuZS5JRAojIyByZXZlcnNlIHN0cmFuZGVkIC0+IDR0YSBjb2x1bW5hCgpgYGAKCmBgYHtyLGluY2x1ZGU9VCxlY2hvPVR9CiMgQ3JlYXIgdGF1bGEgZmVub3RpcGljYQpzYW1wbGVfc2hlZXQ8LQpkYXRhLmZyYW1lKAonZmlsZW5hbWUnPWJhc2VuYW1lKGFyeGl1c19yZWFkcyksCidzYW1wbGUgbmFtZSc9Z3N1YigiUmVhZHNQZXJHZW5lLm91dC50YWIiLCIiLGJhc2VuYW1lKGFyeGl1c19yZWFkcykpLAonZ3JvdXAnPWMoMSwxLDIsMikpCnJvd25hbWVzKHNhbXBsZV9zaGVldCk8LXNhbXBsZV9zaGVldCRmaWxlbmFtZQojIEd1YXJkYXIgaSBtb2RpZmljYXIgZW4gbCdleGNlbAp3cml0ZV94bHN4KHNhbXBsZV9zaGVldCwiLi9zYW1wbGVfc2hlZXQueGxzeCIpCiMgTGVyY3R1cmEgZGUgbGEgdGF1bGEgbW9kaWZpY2FkYQpzYW1wbGVfc2hlZXQ8LXJlYWRfZXhjZWwoIi4vc2FtcGxlX3NoZWV0Lnhsc3giKQpzYW1wbGVfc2hlZXQkZ3JvdXA8LWFzLmZhY3RvcihzYW1wbGVfc2hlZXQkZ3JvdXApCgpkYXRhdGFibGUoc2FtcGxlX3NoZWV0LGNhcHRpb24gPSAiVGF1bGEgZmVub3RpcCBwZXIgY29tcGxldGFyIixyb3duYW1lcyA9IEYsZXh0ZW5zaW9ucyA9ICdCdXR0b25zJyxvcHRpb25zID0gbGlzdCgKICAgIHBhZ2VMZW5ndGggPSA1LGRvbSA9ICdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcpKSkKCmBgYAoKCgpgYGB7cixpbmNsdWRlPVQsZWNobz1UfQpjb2xuYW1lcyhyZXN1bHQpPC1zYW1wbGVfc2hlZXQkZmlsZW5hbWUKIyBjcmVhciBlbCBERVNlcURhdGFTZXQuIEEgRGVzaWduIHMnaGEgZGUgZGVmaW5pciBsYSBjb21wYXJhdGl2YQpzZV9zdGFyX21hdHJpeCA8LSBERVNlcURhdGFTZXRGcm9tTWF0cml4KGNvdW50RGF0YSA9IHJlc3VsdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBzYW1wbGVfc2hlZXQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGdyb3VwKQpgYGAKCiMjIEZpbHRyYXRnZQoKICAqIEZpbHRyYXIgZ2VucyBhbWIgbcOpcyBkZSAxMCByZWFkcwoKYGBge3IsaW5jbHVkZT1ULGVjaG89VCxyZXN1bHRzPSdhc2lzJ30KCnByZV9uPC1ucm93KHNlX3N0YXJfbWF0cml4KQoKIyBGaWx0cmFyIGdlbnMgYW1iIG3DqXMgZGUgMTAgcmVhZHMKc2Vfc3Rhcl9tYXRyaXggPC0gc2Vfc3Rhcl9tYXRyaXhbcm93U3Vtcyhjb3VudHMoc2Vfc3Rhcl9tYXRyaXgpKSA+IDEwLCBdCgoKIyBOdW1iZXIgb2YgZ2VuZXMgbGVmdCBhZnRlciBsb3ctY291bnQgZmlsdGVyaW5nOgpwb3N0X248LW5yb3coc2Vfc3Rhcl9tYXRyaXgpCnByaW50KHBhc3RlMCgiUydoYW4gZWxpbWluYXQgIixwcmVfbi1wb3N0X24sICIgZ2VucyIpKQoKYGBgCgojIyBNb2RlbCBlc3RhZMOtc3RpYwoKYGBge3J9CnNlX3N0YXJfbWF0cml4XzIgPC0gREVTZXEoc2Vfc3Rhcl9tYXRyaXgpCmxvZ19jb3VudHMgPC0gbG9nMihjb3VudHMoc2Vfc3Rhcl9tYXRyaXhfMiwgbm9ybWFsaXplZCA9IFRSVUUpKzEpCmd0ZiA8LSBydHJhY2tsYXllcjo6aW1wb3J0KCIvaG9tZS9qb3NlcC9Eb2N1bWVudHMvMDFfSURJU0JBXzIwMjAvMjRfUk5BX3NlcS9SU3ViUmVhZF9EZXNlcTItbWFpbi9SRUYvTXVzX211c2N1bHVzLkdSQ20zOS4xMDUuZ3RmIikKZ3RmX2RmPWFzLmRhdGEuZnJhbWUoZ3RmKQpoZWFkKGd0Zl9kZlssYygiZ2VuZV9pZCIsImdlbmVfbmFtZSIpXSkKCmxvZ19jb3VudHNfc3ltYm9sPC1tZXJnZShsb2dfY291bnRzLHVuaXF1ZShndGZfZGZbLGMoImdlbmVfaWQiLCJnZW5lX25hbWUiKV0pLGJ5Lng9InJvdy5uYW1lcyIsYnkueT0iZ2VuZV9pZCIpCiMgVWxsIHZpdSBhbWIgZWwgbWF0Y2gsIEdURiB0ZSByZXBldGljaW9ucyBpIGdlbmVyYSBOQXMKIyNsb2dfY291bnRzX3N5bWJvbDwtbG9nX2NvdW50c1ttYXRjaChndGZfZGYkZ2VuZV9pZCxyb3duYW1lcyhsb2dfY291bnRzKSksXQoKYGBgCgojIyBUcmFuc2Zvcm1hY2nDsyBkZSBsZXMgZGFkZXMKCmBgYHtyLGluY2x1ZGU9VH0KdnNkIDwtIHZzdChzZV9zdGFyX21hdHJpeF8yLCBibGluZCA9IEZBTFNFKQoKcmxkIDwtIHJsb2coc2Vfc3Rhcl9tYXRyaXhfMiwgYmxpbmQgPSBGQUxTRSkKCgoKCmRkcyA8LSBlc3RpbWF0ZVNpemVGYWN0b3JzKHNlX3N0YXJfbWF0cml4XzIpCgpkZiA8LSBiaW5kX3Jvd3MoCiAgYXNfZGF0YV9mcmFtZShsb2cyKGNvdW50cyhzZV9zdGFyX21hdHJpeF8yLCBub3JtYWxpemVkPVRSVUUpWywgMToyXSsxKSkgJT4lCiAgICBtdXRhdGUodHJhbnNmb3JtYXRpb24gPSAibG9nMih4ICsgMSkiKSwKICBhc19kYXRhX2ZyYW1lKGFzc2F5KHZzZClbLCAxOjJdKSAlPiUgbXV0YXRlKHRyYW5zZm9ybWF0aW9uID0gInZzdCIpLAogIGFzX2RhdGFfZnJhbWUoYXNzYXkocmxkKVssIDE6Ml0pICU+JSBtdXRhdGUodHJhbnNmb3JtYXRpb24gPSAicmxvZyIpKQoKY29sbmFtZXMoZGYpWzE6Ml0gPC0gYygieCIsICJ5IikgIAoKbHZscyA8LSBjKCJsb2cyKHggKyAxKSIsICJ2c3QiLCAicmxvZyIpCmRmJHRyYW5zZm9ybWF0aW9uIDwtIGZhY3RvcihkZiR0cmFuc2Zvcm1hdGlvbiwgbGV2ZWxzPWx2bHMpCgpnZ3Bsb3QoZGYsIGFlcyh4ID0geCwgeSA9IHkpKSArIGdlb21faGV4KGJpbnMgPSA4MCkgKwogIGNvb3JkX2ZpeGVkKCkgKyBmYWNldF9ncmlkKCAuIH4gdHJhbnNmb3JtYXRpb24pICAKCgpgYGAKCiMjIFZpc3VhbGl0emFjacOzCgpgYGB7ciwgcmVzdWx0cz0nYXNpcycsaW5jbHVkZT1ULGVjaG89Rn0KdnNkIDwtIHZzdChzZV9zdGFyX21hdHJpeF8yKQoKc2FtcGxlRGlzdE1hdHJpeCA8LSBhcy5tYXRyaXgoZGlzdCh0KGFzc2F5KHZzZCkpKSkKZGlyLmNyZWF0ZSgiLi9SRVNVTFRBVFMvUExPVFMiKQpwbmcoIi4vUkVTVUxUQVRTL1BMT1RTL2hlYXRtYXAucG5nIix3aWR0aCA9IDEwMDAsaGVpZ2h0ID0gMTAwMCxyZXM9MTUwKQogIHBoZWF0bWFwKHNhbXBsZURpc3RNYXRyaXgpCi50bXA8LWRldi5vZmYoKSAKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vUkVTVUxUQVRTL1BMT1RTL2hlYXRtYXAucG5nIikKCmBgYAoKYGBge3IsIHJlc3VsdHM9J2FzaXMnLGluY2x1ZGU9VCxlY2hvPUZ9CnBuZygiLi9SRVNVTFRBVFMvUExPVFMvUENBci5wbmciLHdpZHRoID0gMTAwMCxoZWlnaHQgPSAxMDAwLHJlcz0xNTApCnBsb3RQQ0Eob2JqZWN0ID0gdnNkLAoJCWludGdyb3VwID0gImdyb3VwIikKLnRtcDwtZGV2Lm9mZigpCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL1JFU1VMVEFUUy9QTE9UUy9QQ0FyLnBuZyIpCgpgYGAKCgoKYGBge3IsaW5jbHVkZT1ULGVjaG89VH0KCnJlc3VsdHNOYW1lcyhzZV9zdGFyX21hdHJpeF8yKQpkZSA8LSByZXN1bHRzKG9iamVjdCA9IHNlX3N0YXJfbWF0cml4XzIsIAoJCW5hbWU9cmVzdWx0c05hbWVzKHNlX3N0YXJfbWF0cml4XzIpWzJdKQoKZGVfc2hyaW5rIDwtIGxmY1NocmluayhkZHMgPSBzZV9zdGFyX21hdHJpeF8yLGNvZWY9cmVzdWx0c05hbWVzKHNlX3N0YXJfbWF0cml4XzIpWzJdLHR5cGU9ImFwZWdsbSIpCgpgYGAKCiMjIEdlbnMgREUgc2lnbmlmaWNhdGl1cwoKYGBge3IsaW5jbHVkZT1ULGVjaG89VH0KZGlyLmNyZWF0ZSgiLi9SRVNVTFRBVFMvVEFVTEVTIikKZGVfc3ltYm9sczwtbWVyZ2UobG9nX2NvdW50cyx1bmlxdWUoZ3RmX2RmWyxjKCJnZW5lX2lkIiwiZ2VuZV9uYW1lIildKSxieS54PSJyb3cubmFtZXMiLGJ5Lnk9ImdlbmVfaWQiKQpkZV9zeW1ib2xzIDwtIG1lcmdlKHVuaXF1ZShndGZfZGZbLGMoImdlbmVfaWQiLCJnZW5lX25hbWUiKV0pLCBkYXRhLmZyYW1lKElEPXJvd25hbWVzKGRlX3NocmluayksIGRlX3NocmluayksIGJ5PTEsIGFsbD1GKQp3cml0ZS50YWJsZShkZV9zeW1ib2xzLCAiLi9SRVNVTFRBVFMvVEFVTEVTL2Rlc2VxMl9yZXN1bHRzLnR4dCIsIHF1b3RlPUYsIGNvbC5uYW1lcz1ULCByb3cubmFtZXM9Riwgc2VwPSJcdCIpCgpkYXRhdGFibGUoZGVfc3ltYm9scyAlPiUgZmlsdGVyKHBhZGo8PTAuMDUpLGNhcHRpb24gPSAiR2VuZSBPbnRvbG9neSAoT1JBKSIsZXh0ZW5zaW9ucyA9ICdCdXR0b25zJyxvcHRpb25zID0gbGlzdCgKICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KGNsYXNzTmFtZSA9ICdkdC1jZW50ZXInLCB0YXJnZXRzID0gNSkpLAogIHBhZ2VMZW5ndGggPSA1LGRvbSA9ICdCZnJ0aXAnLGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcpKSkKYGBgCgoKYGBge3IsaW5jbHVkZT1ULGVjaG89RixvdXQud2lkdGg9IjgwJSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCIuL1JFU1VMVEFUUy9QTE9UUy9ERV9leGFtcGxlLnBuZyIpCmBgYAoKIyBTaWduaWZpY8OgbmNpYSBiaW9sb2dpY2EuIEVYRU1QTEVTIE5PIFNJR05JRklDQVRJVVMhISEhIQoKCmBgYHtyLCBvdXQud2lkdGg9IjEwMCUiLHJlc3VsdHM9J2FzaXMnLGluY2x1ZGU9VCxlY2hvPUZ9CmRpci5jcmVhdGUoIi4vUkVTVUxUQVRTL1NJR05JRklDQU5DSUEiKQpnZW5zX0RFPC1kZV9zeW1ib2xzICU+JSBmaWx0ZXIocGFkajw9MC4wNSkgJT4lIC4kZ2VuZV9uYW1lCmdlbnNfQWxsPC1kZV9zeW1ib2xzICU+JSBmaWx0ZXIocGFkajw9MC4wNSkgJT4lIC4kZ2VuZV9uYW1lCmlmKGZpbGUuZXhpc3RzKCIuL1JFU1VMVEFUUy9TSUdOSUZJQ0FOQ0lBL2VHT19PUkEiKSl7CiAgbG9hZCgiLi9SRVNVTFRBVFMvU0lHTklGSUNBTkNJQS9lR09fT1JBIil9ZWxzZXsKc2V0LnNlZWQoMTIzKQplR09fT1JBIDwtIGVucmljaEdPKGdlbmUgICAgICAgICAgPSBnZW5zX0RFLAogICAgICAgICAgICAgICAgICAgICAgICAgIHVuaXZlcnNlICAgICAgPSBnZW5zX0FsbCwKICAgICAgICAgICAgICAgICAgICAgICAgIE9yZ0RiICAgICAgICAgPSBvcmcuTW0uZWcuZGIsCiAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgICAgICAgICAgID0gIkFMTCIsCiAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiAgPSAxLHF2YWx1ZUN1dG9mZiA9IDEsCiAgICAgICAgICAgICAgICAgICAga2V5VHlwZSA9ICJTWU1CT0wiKQoKZUdPX09SQSA8LSBtdXRhdGUoZUdPX09SQSwgcmljaEZhY3RvciA9IENvdW50IC8gYXMubnVtZXJpYyhzdWIoIi9cXGQrIiwgIiIsIEJnUmF0aW8pKSkKc2F2ZShlR09fT1JBLGZpbGUgPSAiLi9SRVNVTFRBVFMvU0lHTklGSUNBTkNJQS9lR09fT1JBIikKfQpkYXRhdGFibGUoZGF0YS5mcmFtZShoZWFkKGVHT19PUkFAcmVzdWx0KSksY2FwdGlvbiA9ICJHZW5lIE9udG9sb2d5IChPUkEpIixleHRlbnNpb25zID0gJ0J1dHRvbnMnLG9wdGlvbnMgPSBsaXN0KAogIGNvbHVtbkRlZnMgPSBsaXN0KGxpc3QoY2xhc3NOYW1lID0gJ2R0LWNlbnRlcicsIHRhcmdldHMgPSA1KSksCiAgcGFnZUxlbmd0aCA9IDUsZG9tID0gJ0JmcnRpcCcsYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJykpKQoKCmBgYAoKYGBge3IsIG91dC53aWR0aD0iMTAwJSIscmVzdWx0cz0nYXNpcycsaW5jbHVkZT1ULGVjaG89Rn0KZ2dwbG90KGVHT19PUkEsIHNob3dDYXRlZ29yeSA9IDEwLGFlcyhyaWNoRmFjdG9yLGZjdF9yZW9yZGVyKERlc2NyaXB0aW9uLCByaWNoRmFjdG9yKSkpICsKZ2VvbV9zZWdtZW50KGFlcyh4ZW5kPTAsIHllbmQgPSBEZXNjcmlwdGlvbikpICsKZ2VvbV9wb2ludChhZXMoY29sb3I9cC5hZGp1c3QsIHNpemUgPSBDb3VudCkpICsKc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlPWMoMiwgMTApKSArCnhsYWIoIlJpY2ggRmFjdG9yIikgKwp5bGFiKE5VTEwpICsKZ2d0aXRsZSgiQmlvbG9naWNhbCBQcm9jZXNzZXMiKQpnZ3Bsb3QoZUdPX09SQSwgc2hvd0NhdGVnb3J5ID0gMTAsYWVzKEdlbmVSYXRpbyxmY3RfcmVvcmRlcihEZXNjcmlwdGlvbiwgR2VuZVJhdGlvKSkpICsKZ2VvbV9zZWdtZW50KGFlcyh4ZW5kPTAsIHllbmQgPSBEZXNjcmlwdGlvbikpICsKZ2VvbV9wb2ludChhZXMoY29sb3I9cC5hZGp1c3QsIHNpemUgPSBDb3VudCkpICsKc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlPWMoMiwgMTApKSArCnhsYWIoIkdlbmVSYXRpbyIpICsKeWxhYihOVUxMKSArCmdndGl0bGUoIkJpb2xvZ2ljYWwgUHJvY2Vzc2VzIikKYGBgCgogICogUmljaCBGYWN0b3I6IHByb3BvcmNpw7MgZGUgZ2VucyBpbnRyb2R1w690cyAocGVyIGV4ZW1wbGUsIERFKSBxdWUgcydhc3NpZ25lbiBhIHVuIHRlcm1lIGVuIHJlbGFjacOzIGEgdG90cyBlbHMgZ2VucyBlc3R1ZGlhdHMgYW5vdGF0cyBlbiBhcXVlc3QgdGVybWUuCgogICogR2VuZSBSYXRpbzogcHJvcG9yY2nDsyBkZSBnZW5zIGludHJvZHXDr3RzIChwZXIgZXhlbXBsZSwgREUpIHF1ZSBzJ2Fzc2lnbmVuIGEgdW4gdGVybWUgZW4gcmVsYWNpw7MgYSB0b3RzIGVscyBnZW5zIGVuIGFxdWVzdCB0ZXJtZS4KCgojUmVmZXLDqG5jaWVzOgoKKiBodHRwOi8vbWFzdGVyLmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS93b3JrZmxvd3MvdmlnbmV0dGVzL3JuYXNlcUdlbmUvaW5zdC9kb2Mvcm5hc2VxR2VuZS5odG1sI3ByZXBhcmluZy1xdWFudGlmaWNhdGlvbi1pbnB1dC10by1kZXNlcTIKCiogaHR0cHM6Ly9oYmN0cmFpbmluZy5naXRodWIuaW8vSW50cm8tdG8tcm5hc2VxLWhwYy1zYWxtb24tZmxpcHBlZC9zY2hlZHVsZS9saW5rcy10by1sZXNzb25zLmh0bWwKCiogaHR0cHM6Ly9waHlzaW9sb2d5Lm1lZC5jb3JuZWxsLmVkdS9mYWN1bHR5L3NrcmFiYW5lay9sYWIvYW5nc2QvbGVjdHVyZV9ub3Rlcy9TVEFSbWFudWFsLnBkZgoKKiBodHRwczovL3NjaWVuY2VwYXJrc3R1ZHlncm91cC5naXRodWIuaW8vcm5hLXNlcS1sZXNzb24vMDMtcWMtb2Ytc2VxdWVuY2luZy1yZXN1bHRzL2luZGV4Lmh0bWwjNC10cmltbWluZy1hbmQtZmlsdGVyaW5n