Introduction
Analysing an RNAseq experiment begins with sequencing reads. These are aligned to a reference genome, then the number of reads mapped to each gene can be counted. This results in a table of counts, which is what we perform statistical analyses on in R. This tutorial explains how to download the raw data files from the NCBI Sequence Read Archive public repository, how to QC the reads with FASTQC and finally how to align the reads to the reference genome.
Set up a database of SRA runs
Raw reads from NGS experiments tend to be distributed through the Sequence Read Archive (SRA). SRA provide command line tools for downloading and processing the archive files as the SRA toolkit. In addition the SRAdb
Bioconductor package can be used to query and download files that are hosted in SRA. More information can be found in the package vignette.
vignette("SRAdb")
Firstly, we need to download a database file. This is a large file and could take some time (NA )Gb.
library(SRAdb)
sqlfile <-'SRAmetadb.sqlite'
if(!file.exists('SRAmetadb.sqlite')) sqlfile <<- getSRAdbFile()
sra_con <- dbConnect(SQLite(),sqlfile)
Download the set of sra files
We can now directly download each sra
file. The sra
file is SRA’s own archive format, but we can extract the raw reads in the more common .fastq
format in the next step.
Here, sapply
is a convenient way of repeating the same operation for a vector of arguments. In this case we want to run the getSRAfile
function with different file names.
sapply(sraInf$run, function(x) try(getSRAfile(x,sra_con, fileType="sra"),silent=TRUE))
Quality assessment of reads
The fastqc is recommened for a preliminary assessment of the read quality. However, caution should be exercised when interpreting the results as the reports are not specifically-tailored for RNA-seq. Some sections are know to flag-up warning or error messages for perfectly fine RNA-seq experiments.
We can run this at the command line:-
for fq in *.fastq
do
fastqc $fq
done
Downloading the reference genome
To align to the mm10
genome, we will first download the reference genome from UCSC. We have to download each individual chromosome separately, and then join together into a single file.
wget --timestamping 'ftp://hgdownload.cse.ucsc.edu/goldenPath/mm10/bigZips/chromFa.tar.gz' -O chromFa.tar.gz
gunzip chromFa.tar.gz
tar xvf chromFa.tar
cat *.fa > mm10.fa
rm chr*.fa
rm chromFa.tar.gz
Alignment using bowtie
Firstly, we need to build an index file from the reference genome that we have downloaded:-
bowtie2-build mm10.fa mm10
In practice, we would probably run the alignment of each sample in parallel using the high-performance cluster. However, for illustration purposes, we give the script that will align each sample individually.
bowtie2 -x mm10 -U SRR1552444.fastq -S SRR1552444.sam
samtools view -bS SRR1552444.sam > SRR1552444.bam
samtools sort SRR1552444.bam -o SRR1552444.sorted.bam
samtools index SRR1552444.sorted.bam
bowtie2 -x mm10 -U SRR1552445.fastq -S SRR1552445.sam
samtools view -bS SRR1552445.sam > SRR1552445.bam
samtools sort SRR1552445.bam -o SRR1552445.sorted.bam
samtools index SRR1552445.sorted.bam
bowtie2 -x mm10 -U SRR1552446.fastq -S SRR1552446.sam
samtools view -bS SRR1552446.sam > SRR1552446.bam
samtools sort SRR1552446.bam -o SRR1552446.sorted.bam
samtools index SRR1552446.sorted.bam
bowtie2 -x mm10 -U SRR1552447.fastq -S SRR1552447.sam
samtools view -bS SRR1552447.sam > SRR1552447.bam
samtools sort SRR1552447.bam -o SRR1552447.sorted.bam
samtools index SRR1552447.sorted.bam
bowtie2 -x mm10 -U SRR1552448.fastq -S SRR1552448.sam
samtools view -bS SRR1552448.sam > SRR1552448.bam
samtools sort SRR1552448.bam -o SRR1552448.sorted.bam
samtools index SRR1552448.sorted.bam
bowtie2 -x mm10 -U SRR1552449.fastq -S SRR1552449.sam
samtools view -bS SRR1552449.sam > SRR1552449.bam
samtools sort SRR1552449.bam -o SRR1552449.sorted.bam
samtools index SRR1552449.sorted.bam
bowtie2 -x mm10 -U SRR1552450.fastq -S SRR1552450.sam
samtools view -bS SRR1552450.sam > SRR1552450.bam
samtools sort SRR1552450.bam -o SRR1552450.sorted.bam
samtools index SRR1552450.sorted.bam
bowtie2 -x mm10 -U SRR1552451.fastq -S SRR1552451.sam
samtools view -bS SRR1552451.sam > SRR1552451.bam
samtools sort SRR1552451.bam -o SRR1552451.sorted.bam
samtools index SRR1552451.sorted.bam
bowtie2 -x mm10 -U SRR1552452.fastq -S SRR1552452.sam
samtools view -bS SRR1552452.sam > SRR1552452.bam
samtools sort SRR1552452.bam -o SRR1552452.sorted.bam
samtools index SRR1552452.sorted.bam
bowtie2 -x mm10 -U SRR1552453.fastq -S SRR1552453.sam
samtools view -bS SRR1552453.sam > SRR1552453.bam
samtools sort SRR1552453.bam -o SRR1552453.sorted.bam
samtools index SRR1552453.sorted.bam
bowtie2 -x mm10 -U SRR1552454.fastq -S SRR1552454.sam
samtools view -bS SRR1552454.sam SRR1552454.bam
samtools sort SRR1552454.bam -o SRR1552454.sorted.bam
samtools index SRR1552454.sorted.bam
bowtie2 -x mm10 -U SRR1552455.fastq -S SRR1552455.sam
samtools view -bS SRR1552455.sam > SRR1552455.bam
samtools sort SRR1552455.bam -o SRR1552455.sorted.bam
samtools index SRR1552455.sorted.bam
Renaming to be consistent with GEO
The files we have just created are named according to their SRA identifier. However, these names are not very useful for analysis. The Gene Expression Omnibus (GEO) entry for the dataset has the mapping information between SRA and sample identifers.
library(GEOquery)
tmp <- getGEO("GSE60450")
gseInf <- pData(tmp[[1]])
gseInf
We obtain a new name for each bam file by joining the metadata from SRA and GEO.
library(dplyr)
sraInf <- mutate(sraInf, bam=paste0(run, ".sorted.bam"))
gseInf <- mutate(gseInf, experiment = basename(as.character(supplementary_file_2)),
newbam = gsub("Sample name: ","", description),
newbam = gsub("-",".",newbam,fixed=TRUE),
newbam = paste0(newbam, ".bam"))
gseInf
combinedInf <- left_join(gseInf, sraInf, by="experiment")
combinedInf %>% select(description,description.1,experiment,bam,newbam)
combinedInf
The base R function file.symblink
can be used to create symbolic links from one file to another; thus retaining the original file name and avoid creating a complete copy of each file. Such links are often used in NGS data when we don’t want to create copies of files that are potentially rather large. With this approach, when we want to access MCL1.LA.bam
(for example), the file system will know to actually access SRR1552444.sorted.bam
.
for(i in seq_along(combinedInf$bam)){
file.symlink(combinedInf$bam[i], combinedInf$newbam[i])
file.symlink(paste0(combinedInf$bam[i],".bai"), paste0(combinedInf$newbam[i],".bai"))
}
list.files()
Fu, Nai Yang, Anne C Rios, Bhupinder Pal, Rina Soetanto, Aaron T L Lun, Kevin Liu, Tamara Beck, et al. 2015. “EGF-mediated induction of Mcl-1 at the switch to lactation is essential for alveolar cell survival.” Nature Cell Biology 17 (4): 365–75. doi:10.1038/ncb3117.
LS0tCnRpdGxlOiAiUk5BLXNlcSBhbmFseXNpcyBpbiBSIgphdXRob3I6ICJTdGVwaGFuZSBCYWxsZXJlYXUsIE1hcmsgRHVubmluZywgT3NjYXIgUnVlZGEsIEFzaGxleSBTYXdsZSIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwpsYXlvdXQ6IHBhZ2UKc3VidGl0bGU6IE9idGFpbmluZyBhbmQgYWxpZ25pbmcgUk5BLXNlcSByZWFkcyBmcm9tIHB1YmxpYyByZXBvc2l0b3JpZXMKYmlibGlvZ3JhcGh5OiByZWYuYmliCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSxldmFsPUZBTFNFKQpgYGAKCiMjIEludHJvZHVjdGlvbgoKQW5hbHlzaW5nIGFuIFJOQXNlcSBleHBlcmltZW50IGJlZ2lucyB3aXRoIHNlcXVlbmNpbmcgcmVhZHMuIFRoZXNlIGFyZSBhbGlnbmVkIHRvIGEgcmVmZXJlbmNlIGdlbm9tZSwgdGhlbiB0aGUgbnVtYmVyIG9mIHJlYWRzIG1hcHBlZCB0byBlYWNoIGdlbmUgY2FuIGJlIGNvdW50ZWQuIFRoaXMgcmVzdWx0cyBpbiBhIHRhYmxlIG9mIGNvdW50cywgd2hpY2ggaXMgd2hhdCB3ZSBwZXJmb3JtIHN0YXRpc3RpY2FsIGFuYWx5c2VzIG9uIGluIFIuIFRoaXMgdHV0b3JpYWwgZXhwbGFpbnMgaG93IHRvIGRvd25sb2FkIHRoZSByYXcgZGF0YSBmaWxlcyBmcm9tIHRoZSBbTkNCSSBTZXF1ZW5jZSBSZWFkIEFyY2hpdmVdKGh0dHBzOi8vdHJhY2UubmNiaS5ubG0ubmloLmdvdi9UcmFjZXMvc3JhLykgcHVibGljIHJlcG9zaXRvcnksIGhvdyB0byBRQyB0aGUgcmVhZHMgd2l0aCBbRkFTVFFDXShodHRwOi8vd3d3LmJpb2luZm9ybWF0aWNzLmJhYnJhaGFtLmFjLnVrL3Byb2plY3RzL2Zhc3RxYy8pIGFuZCBmaW5hbGx5IGhvdyB0byBhbGlnbiB0aGUgcmVhZHMgdG8gdGhlIHJlZmVyZW5jZSBnZW5vbWUuCgojIyBNb3VzZSBtYW1tYXJ5IGdsYW5kIGRhdGFzZXQKClRoZSBkYXRhIGZvciB0aGlzIGNvdXJzZSBjb21lcyBmcm9tIGEgTmF0dXJlIENlbGwgQmlvbG9neSBwYXBlciwgWypFR0YtbWVkaWF0ZWQgaW5kdWN0aW9uIG9mIE1jbC0xIGF0IHRoZSBzd2l0Y2ggdG8gbGFjdGF0aW9uIGlzIGVzc2VudGlhbCBmb3IgYWx2ZW9sYXIgY2VsbCBzdXJ2aXZhbCpdKGh0dHA6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9wdWJtZWQvMjU3MzA0NzIpIFtARnUyMDE1XS4gQm90aCB0aGUgcmF3IGRhdGEgKHNlcXVlbmNlIHJlYWRzKSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIFNSQSB1bmRlciBbU1JQMDQ1NTM0XShodHRwczovL3RyYWNlLm5jYmkubmxtLm5paC5nb3YvVHJhY2VzL3NyYS9zcmEuY2dpP3N0dWR5PVNSUDA0NTUzNClhbmQgcHJvY2Vzc2VkIGRhdGEgKGNvdW50cykgY2FuIGJlIGRvd25sb2FkZWQgZnJvbSBHZW5lIEV4cHJlc3Npb24gT21uaWJ1cyBkYXRhYmFzZSAoR0VPKSB1bmRlciBhY2Nlc3Npb24gbnVtYmVyIFtHU0U2MDQ1MF0oaHR0cDovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U2MDQ1MCkuCgojIyBTZXQgdXAgYSBkYXRhYmFzZSBvZiBTUkEgcnVucwoKUmF3IHJlYWRzIGZyb20gTkdTIGV4cGVyaW1lbnRzIHRlbmQgdG8gYmUgZGlzdHJpYnV0ZWQgdGhyb3VnaCB0aGUgU2VxdWVuY2UgUmVhZCBBcmNoaXZlIChTUkEpLiBTUkEgcHJvdmlkZSBjb21tYW5kIGxpbmUgdG9vbHMgZm9yIGRvd25sb2FkaW5nIGFuZCBwcm9jZXNzaW5nIHRoZSBhcmNoaXZlIGZpbGVzIGFzIHRoZSBbU1JBIHRvb2xraXRdKGh0dHBzOi8vdHJhY2UubmNiaS5ubG0ubmloLmdvdi9UcmFjZXMvc3JhL3NyYS5jZ2k/dmlldz1zb2Z0d2FyZSNoZWFkZXItZ2xvYmFsKS4gSW4gYWRkaXRpb24gdGhlIGBTUkFkYmAgQmlvY29uZHVjdG9yIHBhY2thZ2UgY2FuIGJlIHVzZWQgdG8gcXVlcnkgYW5kIGRvd25sb2FkIGZpbGVzIHRoYXQgYXJlIGhvc3RlZCBpbiBTUkEuIE1vcmUgaW5mb3JtYXRpb24gY2FuIGJlIGZvdW5kIGluIHRoZSBbcGFja2FnZSB2aWduZXR0ZV0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL3ZpZ25ldHRlcy9TUkFkYi9pbnN0L2RvYy9TUkFkYi5wZGYpLiAKCmBgYHtyIGV2YWw9RkFMU0V9CnZpZ25ldHRlKCJTUkFkYiIpCmBgYAoKRmlyc3RseSwgd2UgbmVlZCB0byBkb3dubG9hZCBhIGRhdGFiYXNlIGZpbGUuIFRoaXMgaXMgYSBsYXJnZSBmaWxlIGFuZCBjb3VsZCB0YWtlIHNvbWUgdGltZSAoYHIgcm91bmQoZmlsZS5zaXplKCJTUkFtZXRhZGIuc3FsaXRlIikvMTAwMDAwMDAwMCwyKWAgKUdiLgoKYGBge3J9CmxpYnJhcnkoU1JBZGIpCnNxbGZpbGUgPC0nU1JBbWV0YWRiLnNxbGl0ZScKaWYoIWZpbGUuZXhpc3RzKCdTUkFtZXRhZGIuc3FsaXRlJykpIHNxbGZpbGUgPDwtIGdldFNSQWRiRmlsZSgpCnNyYV9jb24gPC0gZGJDb25uZWN0KFNRTGl0ZSgpLHNxbGZpbGUpCmBgYAoKIyMgT2J0YWluIGluZm9ybWF0aW9uIGZvciBhIHBhcnRpY3VsYXIgZXhwZXJpbWVudAoKV2UgY2FuIG5vdyBxdWVyeSB3aGF0IGluZm9ybWF0aW9uIGlzIGF2YWlsYWJsZSBmb3IgYSBwYXJ0aWN1bGFyIGV4cGVyaW1lbnQ7IGluIHRoaXMgY2FzZSBgU1JQMDQ1NTM0YC4gVGhpcyBzaG91bGQgbGlzdCB0aGUgc2FtcGxlcyB0aGF0IGFyZSBhdmFpbGFibGUgYW5kIHRoZWlyIHJlc3BlY3RpdmUgaWRlbnRpZmllcnMuCgpgYGB7cn0Kc3JhSW5mIDwtIGdldFNSQWluZm8oIlNSUDA0NTUzNCIsc3JhX2Nvbiwgc3JhVHlwZT0ic3JhIikKc3JhSW5mCmBgYAoKIyMgRG93bmxvYWQgdGhlIHNldCBvZiBzcmEgZmlsZXMKCldlIGNhbiBub3cgZGlyZWN0bHkgZG93bmxvYWQgZWFjaCBgc3JhYCBmaWxlLiBUaGUgYHNyYWAgZmlsZSBpcyBTUkEncyBvd24gYXJjaGl2ZSBmb3JtYXQsIGJ1dCB3ZSBjYW4gZXh0cmFjdCB0aGUgcmF3IHJlYWRzIGluIHRoZSBtb3JlIGNvbW1vbiBgLmZhc3RxYCBmb3JtYXQgaW4gdGhlIG5leHQgc3RlcC4KCkhlcmUsIGBzYXBwbHlgIGlzIGEgY29udmVuaWVudCB3YXkgb2YgcmVwZWF0aW5nIHRoZSBzYW1lIG9wZXJhdGlvbiBmb3IgYSB2ZWN0b3Igb2YgYXJndW1lbnRzLiBJbiB0aGlzIGNhc2Ugd2Ugd2FudCB0byBydW4gdGhlIGBnZXRTUkFmaWxlYCBmdW5jdGlvbiB3aXRoIGRpZmZlcmVudCBmaWxlIG5hbWVzLiAKCmBgYHtyfQpzYXBwbHkoc3JhSW5mJHJ1biwgZnVuY3Rpb24oeCkgdHJ5KGdldFNSQWZpbGUoeCxzcmFfY29uLCBmaWxlVHlwZT0ic3JhIiksc2lsZW50PVRSVUUpKQpgYGAKCiMjIEV4dHJhY3RpbmcgZmFzdHEgZmlsZXMKClVzaW5nIHRoZSBbU1JBIFRvb2xraXRdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3Yvc3JhKSBjb21tYW5kLWxpbmUgdXRpbGl0eSBmcm9tIE5DQkkgd2UgY2FuIGdlbmVyYXRlIHRoZSBgZmFzdHFgIGZpbGVzIGZyb20gdGhlc2UgYXJjaGl2ZSBmaWxlcy4gV2UgY2FuIGRvIHRoaXMgd2l0aGluIGEgVGVybWluYWwgKGkuZS4gbm90IHdpdGhpbiBSU3R1ZGlvKSB3aXRoIHRoZSBmb2xsb3dpbmcsIG1ha2luZyBzdXJlIHlvdXIgd29ya2luZyBkaXJlY3RvcnkgY29udGFpbnMgdGhlIGAuc3JhYCBmaWxlcy4KCmBgYHtiYXNofQpmb3Igc3JhIGluICouc3JhCmRvCmZhc3RxLWR1bXAgJHNyYQpkb25lCgpgYGAKCkFmdGVyIGVhY2ggZmFzdHEgZmlsZSBoYXMgYmVlbiBleHRyYWN0ZWQsIHlvdSBzaG91bGQgc2VlIGEgbWVzc2FnZSB0byByZXBvcnQgaGF2ZSBtYW55IHJlYWRzIChzcG90cykgYXJlIGNvbnRhaW5lZCBpbiB0aGUgZmlsZQoKCiMjIFF1YWxpdHkgYXNzZXNzbWVudCBvZiByZWFkcwoKVGhlIFtmYXN0cWNdKGh0dHA6Ly93d3cuYmlvaW5mb3JtYXRpY3MuYmFicmFoYW0uYWMudWsvcHJvamVjdHMvZmFzdHFjLykgaXMgcmVjb21tZW5lZCBmb3IgYSBwcmVsaW1pbmFyeSBhc3Nlc3NtZW50IG9mIHRoZSByZWFkIHF1YWxpdHkuIEhvd2V2ZXIsIGNhdXRpb24gc2hvdWxkIGJlIGV4ZXJjaXNlZCB3aGVuIGludGVycHJldGluZyB0aGUgcmVzdWx0cyBhcyB0aGUgcmVwb3J0cyBhcmUgbm90IHNwZWNpZmljYWxseS10YWlsb3JlZCBmb3IgUk5BLXNlcS4gU29tZSBzZWN0aW9ucyBhcmUga25vdyB0byBmbGFnLXVwIHdhcm5pbmcgb3IgZXJyb3IgbWVzc2FnZXMgZm9yIHBlcmZlY3RseSBmaW5lIFJOQS1zZXEgZXhwZXJpbWVudHMuCgpXZSBjYW4gcnVuIHRoaXMgYXQgdGhlIGNvbW1hbmQgbGluZTotCgpgYGB7YmFzaCBjYWNoZT1UUlVFLGV2YWw9RkFMU0V9CmZvciBmcSBpbiAqLmZhc3RxCmRvCmZhc3RxYyAkZnEKZG9uZQoKYGBgCgojIyBEb3dubG9hZGluZyB0aGUgcmVmZXJlbmNlIGdlbm9tZQoKVG8gYWxpZ24gdG8gdGhlIGBtbTEwYCBnZW5vbWUsIHdlIHdpbGwgZmlyc3QgZG93bmxvYWQgdGhlIHJlZmVyZW5jZSBnZW5vbWUgZnJvbSBVQ1NDLiBXZSBoYXZlIHRvIGRvd25sb2FkIGVhY2ggaW5kaXZpZHVhbCBjaHJvbW9zb21lIHNlcGFyYXRlbHksIGFuZCB0aGVuIGpvaW4gdG9nZXRoZXIgaW50byBhIHNpbmdsZSBmaWxlLiAKCmBgYHtiYXNoIGV2YWw9RkFMU0V9CndnZXQgLS10aW1lc3RhbXBpbmcgJ2Z0cDovL2hnZG93bmxvYWQuY3NlLnVjc2MuZWR1L2dvbGRlblBhdGgvbW0xMC9iaWdaaXBzL2Nocm9tRmEudGFyLmd6JyAtTyBjaHJvbUZhLnRhci5negpndW56aXAgY2hyb21GYS50YXIuZ3oKdGFyIHh2ZiBjaHJvbUZhLnRhcgpjYXQgKi5mYSA+IG1tMTAuZmEKcm0gY2hyKi5mYQpybSBjaHJvbUZhLnRhci5negpgYGAKCiMjIEFsaWdubWVudCB1c2luZyBib3d0aWUKCkZpcnN0bHksIHdlIG5lZWQgdG8gYnVpbGQgYW4gKmluZGV4KiBmaWxlIGZyb20gdGhlIHJlZmVyZW5jZSBnZW5vbWUgdGhhdCB3ZSBoYXZlIGRvd25sb2FkZWQ6LQoKYGBge2Jhc2ggZXZhbD1GQUxTRX0KYm93dGllMi1idWlsZCBtbTEwLmZhIG1tMTAKYGBgCgpJbiBwcmFjdGljZSwgd2Ugd291bGQgcHJvYmFibHkgcnVuIHRoZSBhbGlnbm1lbnQgb2YgZWFjaCBzYW1wbGUgaW4gKnBhcmFsbGVsKiB1c2luZyB0aGUgaGlnaC1wZXJmb3JtYW5jZSBjbHVzdGVyLiBIb3dldmVyLCBmb3IgaWxsdXN0cmF0aW9uIHB1cnBvc2VzLCB3ZSBnaXZlIHRoZSBzY3JpcHQgdGhhdCB3aWxsIGFsaWduIGVhY2ggc2FtcGxlIGluZGl2aWR1YWxseS4KCmBgYHtiYXNoIGV2YWw9RkFMU0V9CmJvd3RpZTIgLXggbW0xMCAtVSBTUlIxNTUyNDQ0LmZhc3RxIC1TIFNSUjE1NTI0NDQuc2FtCnNhbXRvb2xzIHZpZXcgLWJTIFNSUjE1NTI0NDQuc2FtID4gU1JSMTU1MjQ0NC5iYW0Kc2FtdG9vbHMgc29ydCBTUlIxNTUyNDQ0LmJhbSAtbyBTUlIxNTUyNDQ0LnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggU1JSMTU1MjQ0NC5zb3J0ZWQuYmFtCgpib3d0aWUyIC14IG1tMTAgLVUgU1JSMTU1MjQ0NS5mYXN0cSAtUyBTUlIxNTUyNDQ1LnNhbQpzYW10b29scyB2aWV3IC1iUyBTUlIxNTUyNDQ1LnNhbSA+IFNSUjE1NTI0NDUuYmFtCnNhbXRvb2xzIHNvcnQgU1JSMTU1MjQ0NS5iYW0gLW8gU1JSMTU1MjQ0NS5zb3J0ZWQuYmFtCnNhbXRvb2xzIGluZGV4IFNSUjE1NTI0NDUuc29ydGVkLmJhbQoKYm93dGllMiAteCBtbTEwIC1VIFNSUjE1NTI0NDYuZmFzdHEgLVMgU1JSMTU1MjQ0Ni5zYW0Kc2FtdG9vbHMgdmlldyAtYlMgU1JSMTU1MjQ0Ni5zYW0gPiBTUlIxNTUyNDQ2LmJhbQpzYW10b29scyBzb3J0IFNSUjE1NTI0NDYuYmFtIC1vIFNSUjE1NTI0NDYuc29ydGVkLmJhbQpzYW10b29scyBpbmRleCBTUlIxNTUyNDQ2LnNvcnRlZC5iYW0KCmJvd3RpZTIgLXggbW0xMCAtVSBTUlIxNTUyNDQ3LmZhc3RxIC1TIFNSUjE1NTI0NDcuc2FtCnNhbXRvb2xzIHZpZXcgLWJTIFNSUjE1NTI0NDcuc2FtID4gU1JSMTU1MjQ0Ny5iYW0Kc2FtdG9vbHMgc29ydCBTUlIxNTUyNDQ3LmJhbSAtbyBTUlIxNTUyNDQ3LnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggU1JSMTU1MjQ0Ny5zb3J0ZWQuYmFtCgpib3d0aWUyIC14IG1tMTAgLVUgU1JSMTU1MjQ0OC5mYXN0cSAtUyBTUlIxNTUyNDQ4LnNhbQpzYW10b29scyB2aWV3IC1iUyBTUlIxNTUyNDQ4LnNhbSA+IFNSUjE1NTI0NDguYmFtCnNhbXRvb2xzIHNvcnQgU1JSMTU1MjQ0OC5iYW0gLW8gU1JSMTU1MjQ0OC5zb3J0ZWQuYmFtCnNhbXRvb2xzIGluZGV4IFNSUjE1NTI0NDguc29ydGVkLmJhbQoKYm93dGllMiAteCBtbTEwIC1VIFNSUjE1NTI0NDkuZmFzdHEgLVMgU1JSMTU1MjQ0OS5zYW0Kc2FtdG9vbHMgdmlldyAtYlMgU1JSMTU1MjQ0OS5zYW0gPiBTUlIxNTUyNDQ5LmJhbQpzYW10b29scyBzb3J0IFNSUjE1NTI0NDkuYmFtIC1vIFNSUjE1NTI0NDkuc29ydGVkLmJhbQpzYW10b29scyBpbmRleCBTUlIxNTUyNDQ5LnNvcnRlZC5iYW0KCmJvd3RpZTIgLXggbW0xMCAtVSBTUlIxNTUyNDUwLmZhc3RxIC1TIFNSUjE1NTI0NTAuc2FtCnNhbXRvb2xzIHZpZXcgLWJTIFNSUjE1NTI0NTAuc2FtID4gU1JSMTU1MjQ1MC5iYW0Kc2FtdG9vbHMgc29ydCBTUlIxNTUyNDUwLmJhbSAtbyBTUlIxNTUyNDUwLnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggU1JSMTU1MjQ1MC5zb3J0ZWQuYmFtCgpib3d0aWUyIC14IG1tMTAgLVUgU1JSMTU1MjQ1MS5mYXN0cSAtUyBTUlIxNTUyNDUxLnNhbQpzYW10b29scyB2aWV3IC1iUyBTUlIxNTUyNDUxLnNhbSA+IFNSUjE1NTI0NTEuYmFtCnNhbXRvb2xzIHNvcnQgU1JSMTU1MjQ1MS5iYW0gLW8gU1JSMTU1MjQ1MS5zb3J0ZWQuYmFtCnNhbXRvb2xzIGluZGV4IFNSUjE1NTI0NTEuc29ydGVkLmJhbQoKYm93dGllMiAteCBtbTEwIC1VIFNSUjE1NTI0NTIuZmFzdHEgLVMgU1JSMTU1MjQ1Mi5zYW0Kc2FtdG9vbHMgdmlldyAtYlMgU1JSMTU1MjQ1Mi5zYW0gPiBTUlIxNTUyNDUyLmJhbQpzYW10b29scyBzb3J0IFNSUjE1NTI0NTIuYmFtIC1vIFNSUjE1NTI0NTIuc29ydGVkLmJhbQpzYW10b29scyBpbmRleCBTUlIxNTUyNDUyLnNvcnRlZC5iYW0KCmJvd3RpZTIgLXggbW0xMCAtVSBTUlIxNTUyNDUzLmZhc3RxIC1TIFNSUjE1NTI0NTMuc2FtCnNhbXRvb2xzIHZpZXcgLWJTIFNSUjE1NTI0NTMuc2FtID4gU1JSMTU1MjQ1My5iYW0Kc2FtdG9vbHMgc29ydCBTUlIxNTUyNDUzLmJhbSAtbyBTUlIxNTUyNDUzLnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggU1JSMTU1MjQ1My5zb3J0ZWQuYmFtCgpib3d0aWUyIC14IG1tMTAgLVUgU1JSMTU1MjQ1NC5mYXN0cSAtUyBTUlIxNTUyNDU0LnNhbQpzYW10b29scyB2aWV3IC1iUyBTUlIxNTUyNDU0LnNhbSBTUlIxNTUyNDU0LmJhbQpzYW10b29scyBzb3J0IFNSUjE1NTI0NTQuYmFtIC1vIFNSUjE1NTI0NTQuc29ydGVkLmJhbQpzYW10b29scyBpbmRleCBTUlIxNTUyNDU0LnNvcnRlZC5iYW0KCmJvd3RpZTIgLXggbW0xMCAtVSBTUlIxNTUyNDU1LmZhc3RxIC1TIFNSUjE1NTI0NTUuc2FtCnNhbXRvb2xzIHZpZXcgLWJTIFNSUjE1NTI0NTUuc2FtID4gU1JSMTU1MjQ1NS5iYW0Kc2FtdG9vbHMgc29ydCBTUlIxNTUyNDU1LmJhbSAtbyBTUlIxNTUyNDU1LnNvcnRlZC5iYW0Kc2FtdG9vbHMgaW5kZXggU1JSMTU1MjQ1NS5zb3J0ZWQuYmFtCmBgYAoKIyMgUmVuYW1pbmcgdG8gYmUgY29uc2lzdGVudCB3aXRoIEdFTwoKVGhlIGZpbGVzIHdlIGhhdmUganVzdCBjcmVhdGVkIGFyZSBuYW1lZCBhY2NvcmRpbmcgdG8gdGhlaXIgU1JBIGlkZW50aWZpZXIuIEhvd2V2ZXIsIHRoZXNlIG5hbWVzIGFyZSBub3QgdmVyeSB1c2VmdWwgZm9yIGFuYWx5c2lzLiBUaGUgR2VuZSBFeHByZXNzaW9uIE9tbmlidXMgKEdFTykgZW50cnkgZm9yIHRoZSBkYXRhc2V0IGhhcyB0aGUgbWFwcGluZyBpbmZvcm1hdGlvbiBiZXR3ZWVuIFNSQSBhbmQgc2FtcGxlIGlkZW50aWZlcnMuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KEdFT3F1ZXJ5KQp0bXAgPC0gZ2V0R0VPKCJHU0U2MDQ1MCIpCmdzZUluZiA8LSBwRGF0YSh0bXBbWzFdXSkKZ3NlSW5mCgpgYGAKCldlIG9idGFpbiBhIG5ldyBuYW1lIGZvciBlYWNoIGJhbSBmaWxlIGJ5IGpvaW5pbmcgdGhlIG1ldGFkYXRhIGZyb20gU1JBIGFuZCBHRU8uCmBgYHtyfQoKbGlicmFyeShkcGx5cikKc3JhSW5mIDwtIG11dGF0ZShzcmFJbmYsIGJhbT1wYXN0ZTAocnVuLCAiLnNvcnRlZC5iYW0iKSkKCmdzZUluZiA8LSBtdXRhdGUoZ3NlSW5mLCBleHBlcmltZW50ID0gYmFzZW5hbWUoYXMuY2hhcmFjdGVyKHN1cHBsZW1lbnRhcnlfZmlsZV8yKSksCiAgICAgICAgICAgICAgICAgbmV3YmFtID0gZ3N1YigiU2FtcGxlIG5hbWU6ICIsIiIsIGRlc2NyaXB0aW9uKSwKICAgICAgICAgICAgICAgICBuZXdiYW0gPSBnc3ViKCItIiwiLiIsbmV3YmFtLGZpeGVkPVRSVUUpLAogICAgICAgICAgICAgICAgIG5ld2JhbSA9IHBhc3RlMChuZXdiYW0sICIuYmFtIikpCmdzZUluZgpjb21iaW5lZEluZiA8LSBsZWZ0X2pvaW4oZ3NlSW5mLCBzcmFJbmYsIGJ5PSJleHBlcmltZW50IikKY29tYmluZWRJbmYgJT4lIHNlbGVjdChkZXNjcmlwdGlvbixkZXNjcmlwdGlvbi4xLGV4cGVyaW1lbnQsYmFtLG5ld2JhbSkKY29tYmluZWRJbmYKYGBgCgpUaGUgYmFzZSBSIGZ1bmN0aW9uIGBmaWxlLnN5bWJsaW5rYCBjYW4gYmUgdXNlZCB0byBjcmVhdGUgKnN5bWJvbGljIGxpbmtzKiBmcm9tIG9uZSBmaWxlIHRvIGFub3RoZXI7IHRodXMgcmV0YWluaW5nIHRoZSBvcmlnaW5hbCBmaWxlIG5hbWUgYW5kIGF2b2lkIGNyZWF0aW5nIGEgY29tcGxldGUgY29weSBvZiBlYWNoIGZpbGUuIFN1Y2ggbGlua3MgYXJlIG9mdGVuIHVzZWQgaW4gTkdTIGRhdGEgd2hlbiB3ZSBkb24ndCB3YW50IHRvIGNyZWF0ZSBjb3BpZXMgb2YgZmlsZXMgdGhhdCBhcmUgcG90ZW50aWFsbHkgcmF0aGVyIGxhcmdlLiBXaXRoIHRoaXMgYXBwcm9hY2gsIHdoZW4gd2Ugd2FudCB0byBhY2Nlc3MgYE1DTDEuTEEuYmFtYCAoZm9yIGV4YW1wbGUpLCB0aGUgZmlsZSBzeXN0ZW0gd2lsbCBrbm93IHRvIGFjdHVhbGx5IGFjY2VzcyBgU1JSMTU1MjQ0NC5zb3J0ZWQuYmFtYC4KCmBgYHtyfQpmb3IoaSBpbiBzZXFfYWxvbmcoY29tYmluZWRJbmYkYmFtKSl7CiAgCiAgZmlsZS5zeW1saW5rKGNvbWJpbmVkSW5mJGJhbVtpXSwgY29tYmluZWRJbmYkbmV3YmFtW2ldKQogIGZpbGUuc3ltbGluayhwYXN0ZTAoY29tYmluZWRJbmYkYmFtW2ldLCIuYmFpIiksIHBhc3RlMChjb21iaW5lZEluZiRuZXdiYW1baV0sIi5iYWkiKSkKICAKfQpsaXN0LmZpbGVzKCkKYGBgCg==