1 RNA quantification

Like for bulk RNA sequencing, we start the analysis of scRNASeq data with a list of fastq files.

For the first part of this practical, we are going to work on a 1k cells 1:1 Mixture of Fresh Frozen Human (HEK293T) and Mouse (NIH3T3) Cells available from the 10x website. First, we download the fastq files (6.34 GB).

mkdir data/hgmm_1k
wget http://cf.10xgenomics.com/samples/cell-exp/2.1.0/hgmm_1k/hgmm_1k_fastqs.tar -O data/hgmm_1k/hgmm_1k_fastqs.tar
cd data/hgmm_1k/
tar -xvf hgmm_1k_fastqs.tar
cd ../..

On pedago-ngs.univ-lyon1.fr, you can find the file here

mkdir data
ln -s /data/share/MADT/TP_lmodolo/data/hgmm_1k/ data/hgmm_1k

This is a toy dataset, which is not representative of the size of the data generated in scRNASeq.

Check the size of the data generated for the Single-cell RNA-seq of 1.3 million brain cells from E18 mice.

The processing of scRNASeq fastq file can be a tedious and time-consuming process. In addition to the size of the data to handle, for each read, we have to:

  • locate the read on the reference genome or transcritome
  • identify the cell molecular barcode
  • identify the UMI
  • identify the sample molecular barcode (if any)

To ease these steps, the barcode are often at the start or the end of the reads, where the read quality is the lowest. Most scRNASeq pipeline will clip the barcode / UMI information from the read and add them to the read name. Therefore, the read can be mapped and we can assign it to a given cell and remove PCR duplicate afterward.

Luckily for you, the kb pipeline (Kallisto-Bustools) can handle most of these steps in few commands (Melsted et al.).

2 Reference indexing

The kb pipeline uses Kallisto. Therefore, we need a reference index before processing the data. The dataset is a mixture of Human and Mouse cells; thus, we want to quantify the transcript of a Human-Mouse hybrid reference. We can run the following command to build the index with the kallistobustools container (docker://lbmc/tp_scrna:0.1.0)

wget ftp://ftp.ensembl.org/pub/release-99/fasta/homo_sapiens/cdna/Homo_sapiens.GRCh38.cdna.all.fa.gz -O data/hgmm_1k/hs_cdna.fa.gz
wget ftp://ftp.ensembl.org/pub/release-99/fasta/mus_musculus/cdna/Mus_musculus.GRCm38.cdna.all.fa.gz -O data/hgmm_1k/mm_cdna.fa.gz

kallisto index -i data/hgmm_1k/hs_mm_tr_index.idx data/hgmm_1k/hs_cdna.fa.gz data/hgmm_1k/mm_cdna.fa.gz
[build] loading fasta file data/hgmm_1k/hs_cdna.fa.gz
[build] loading fasta file data/hgmm_1k/mm_cdna.fa.gz
[build] k-mer length: 31
[build] warning: clipped off poly-A tail (longer than 10)
        from 2130 target sequences
[build] warning: replaced 8 non-ACGUT characters in the input sequence
        with pseudorandom nucleotides
[build] counting k-mers ... done.
[build] building target de Bruijn graph ...  done
[build] creating equivalence classes ...  done
[build] target de Bruijn graph has 2192295 contigs and contains 209728067 k-mers

3 RNA quantification

Here we will generate the bus file. bus stands for Barbode, UMI, Set. In text form, it is a table whose first column is the barcode. The second column is the UMI that are associated with the barcode. The third column is the index of the equivalence class reads with the UMI maps. The fourth column is the count of reads with this barcode, UMI, and equivalence class combination, which is ignored as one UMI should stand for one molecule. See this paper for more detail.

Also, since the reads were generated with the 10x Genomics Chromium Single Cell v2 Chemistry, the -x 10xv2 argument is used. To view others supported technologies, and see the information kb needs, run kallisto bus --list.

mkdir -p results/hgmm_1k/
kallisto bus \
  -i data/hgmm_1k/hs_mm_tr_index.idx \
  -o results/hgmm_1k/ -x 10xv2 -t 10 \
  data/hgmm_1k/fastqs/hgmm_1k_S1_L001_R1_001.fastq.gz data/hgmm_1k/fastqs/hgmm_1k_S1_L001_R2_001.fastq.gz \
  data/hgmm_1k/fastqs/hgmm_1k_S1_L002_R1_001.fastq.gz data/hgmm_1k/fastqs/hgmm_1k_S1_L002_R2_001.fastq.gz \
  data/hgmm_1k/fastqs/hgmm_1k_S1_L003_R1_001.fastq.gz data/hgmm_1k/fastqs/hgmm_1k_S1_L003_R2_001.fastq.gz \
  data/hgmm_1k/fastqs/hgmm_1k_S1_L004_R1_001.fastq.gz data/hgmm_1k/fastqs/hgmm_1k_S1_L004_R2_001.fastq.gz \
  data/hgmm_1k/fastqs/hgmm_1k_S1_L005_R1_001.fastq.gz data/hgmm_1k/fastqs/hgmm_1k_S1_L005_R2_001.fastq.gz \
  data/hgmm_1k/fastqs/hgmm_1k_S1_L006_R1_001.fastq.gz data/hgmm_1k/fastqs/hgmm_1k_S1_L006_R2_001.fastq.gz \
  data/hgmm_1k/fastqs/hgmm_1k_S1_L007_R1_001.fastq.gz data/hgmm_1k/fastqs/hgmm_1k_S1_L007_R2_001.fastq.gz \
  data/hgmm_1k/fastqs/hgmm_1k_S1_L008_R1_001.fastq.gz data/hgmm_1k/fastqs/hgmm_1k_S1_L008_R2_001.fastq.gz
[index] k-mer length: 31
[index] number of targets: 309,785
[index] number of k-mers: 209,728,067
[index] number of equivalence classes: 1,288,400
[quant] will process sample 1: data/hgmm_1k/fastqs/hgmm_1k_S1_L001_R1_001.fastq.gz
                               data/hgmm_1k/fastqs/hgmm_1k_S1_L001_R2_001.fastq.gz
[quant] will process sample 2: data/hgmm_1k/fastqs/hgmm_1k_S1_L002_R1_001.fastq.gz
                               data/hgmm_1k/fastqs/hgmm_1k_S1_L002_R2_001.fastq.gz
[quant] will process sample 3: data/hgmm_1k/fastqs/hgmm_1k_S1_L003_R1_001.fastq.gz
                               data/hgmm_1k/fastqs/hgmm_1k_S1_L003_R2_001.fastq.gz
[quant] will process sample 4: data/hgmm_1k/fastqs/hgmm_1k_S1_L004_R1_001.fastq.gz
                               data/hgmm_1k/fastqs/hgmm_1k_S1_L004_R2_001.fastq.gz
[quant] will process sample 5: data/hgmm_1k/fastqs/hgmm_1k_S1_L005_R1_001.fastq.gz
                               data/hgmm_1k/fastqs/hgmm_1k_S1_L005_R2_001.fastq.gz
[quant] will process sample 6: data/hgmm_1k/fastqs/hgmm_1k_S1_L006_R1_001.fastq.gz
                               data/hgmm_1k/fastqs/hgmm_1k_S1_L006_R2_001.fastq.gz
[quant] will process sample 7: data/hgmm_1k/fastqs/hgmm_1k_S1_L007_R1_001.fastq.gz
                               data/hgmm_1k/fastqs/hgmm_1k_S1_L007_R2_001.fastq.gz
[quant] will process sample 8: data/hgmm_1k/fastqs/hgmm_1k_S1_L008_R1_001.fastq.gz
                               data/hgmm_1k/fastqs/hgmm_1k_S1_L008_R2_001.fastq.gz
[quant] finding pseudoalignments for the reads ...
 done
[quant] processed 63,252,296 reads, 52,281,921 reads pseudoaligned

The quantification run in around 2 min on 10 threads and creates 4 files:

ls results/hgmm_1k/
  • matrix.ec: A text file with two columns. The first column is the 0 based index of equivalence classes. The second column is the set of transcripts (denoted by 0 based index based on order of appearance in the transcriptome fasta file) present in the corresponding equivalence class.
  • output.bus: The data represented in bus format. This is a binary file, so don’t use something like read.table to read it into R.
  • run_info.json: Information about the call to kallisto bus, including the command used, number and percentage of reads pseudoaligned, version of kallisto used, etc.
  • transcript.txt: A text file with one column, which is the transcripts present in the data, in the same order as in the transcriptome fasta file.

With the current 3’ sequencing protocols, most people are only interested in how many UMIs per gene per cell we can quantify. We will quantify this from the bus output, and to do so, we need to find which gene corresponds to each transcript. The t2g.txt file is a transcript-to-gene mapping, which allows us to have access to genes counts.

dir.create("results/hgmm_1k", recursive = T)
tr2g <- transcript2gene(
  fasta_file = c("data/hgmm_1k/hs_cdna.fa.gz", "./data/hgmm_1k/mm_cdna.fa.gz"),
  kallisto_out_path = "results/hgmm_1k/"
)
head(tr2g)
save_tr2g_bustools(tr2g, "results/hgmm_1k/tr2g_hgmm.tsv")

The /data/share/MADT/TP_lmodolo/10xv2_whitelist.txt contains all the barcodes known to be present in the kit v2 is provided by 10x. We are ready to make the gene count matrix. First, bustools runs barcode error correction on the bus file. Then, the corrected bus file is sorted by barcode, UMI, and equivalence classes. Then the UMIs are counted and the counts are collapsed into gene level.

mkdir -p results/hgmm_1k/genecount tmp
bustools correct -w data/whitelist_v2.txt -p results/hgmm_1k/output.bus | \
  bustools sort -T tmp/ -t 4 -p - | \
  bustools count -o results/hgmm_1k/genecount/genes -g results/hgmm_1k/tr2g_hgmm.tsv \
  -e results/hgmm_1k/matrix.ec -t results/hgmm_1k/transcripts.txt --genecounts
Found 737280 barcodes in the whitelist
Processed 52281921 BUS records
In whitelist = 50825261
Corrected    = 348752
Uncorrected  = 1107908

The run logs information is accessible in json format in the results/hmm_1k/ directory.

You can check how to load the data in R with the single-cell experiment or direclty go to the quality control of the data

LS0tCnRpdGxlOiAic2NSTkEtc2VxOiBSTkEgcXVhbnRpZmljYXRpb24iCmF1dGhvcjogTGF1cmVudCBNb2RvbG8KZGF0ZTogMjAyMS0yMDIyCm91dHB1dDoKICAgIGh0bWxfbm90ZWJvb2s6CiAgICAgIHRvYzogdHJ1ZQogICAgICB0b2NfZmxvYXQ6CiAgICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgICB0aGVtZTogc2FuZHN0b25lCiAgICAgIGhpZ2hsaWdodDogcHlnbWVudHMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFQsCiAgd2FybmluZyA9IEYsCiAgbWVzc2FnZSA9IEYsCiAgY2FjaGUgPSBULAogIHJvb3QuZGlyID0gIi4uIiwKICBmaXQud2lkdGggPSAxMCwKICBmaWcuaGVpZ2h0ID0gNSwKICBmaWcucGF0aCA9ICcuL2ltZy8nLAogIGRwaSA9IDEwMCwKICBwcm9ncmVzcyA9IFRSVUUKKQpgYGAKCiMgUk5BIHF1YW50aWZpY2F0aW9uCgpMaWtlIGZvciBidWxrIFJOQSBzZXF1ZW5jaW5nLCB3ZSBzdGFydCB0aGUgYW5hbHlzaXMgb2Ygc2NSTkFTZXEgZGF0YSB3aXRoIGEgbGlzdCBvZiBmYXN0cSBmaWxlcy4KCkZvciB0aGUgZmlyc3QgcGFydCBvZiB0aGlzIHByYWN0aWNhbCwgd2UgYXJlIGdvaW5nIHRvIHdvcmsgb24gYSAxayBjZWxscyAxOjEgTWl4dHVyZSBvZiBGcmVzaCBGcm96ZW4gSHVtYW4gKEhFSzI5M1QpIGFuZCBNb3VzZSAoTklIM1QzKSBDZWxscyBhdmFpbGFibGUgZnJvbSB0aGUgMTB4IHdlYnNpdGUuIEZpcnN0LCB3ZSBkb3dubG9hZCB0aGUgZmFzdHEgZmlsZXMgKDYuMzQgR0IpLgoKYGBge2Jhc2ggZ2V0X2RhdGEsIGV2YWw9Rn0KbWtkaXIgZGF0YS9oZ21tXzFrCndnZXQgaHR0cDovL2NmLjEweGdlbm9taWNzLmNvbS9zYW1wbGVzL2NlbGwtZXhwLzIuMS4wL2hnbW1fMWsvaGdtbV8xa19mYXN0cXMudGFyIC1PIGRhdGEvaGdtbV8xay9oZ21tXzFrX2Zhc3Rxcy50YXIKY2QgZGF0YS9oZ21tXzFrLwp0YXIgLXh2ZiBoZ21tXzFrX2Zhc3Rxcy50YXIKY2QgLi4vLi4KYGBgCgpPbiBgcGVkYWdvLW5ncy51bml2LWx5b24xLmZyYCwgeW91IGNhbiBmaW5kIHRoZSBmaWxlIGhlcmUKCmBgYHtiYXNoIGdldF9kYXRhX3BlZGFnbywgZXZhbD1GfQpta2RpciBkYXRhCmxuIC1zIC9kYXRhL3NoYXJlL01BRFQvVFBfbG1vZG9sby9kYXRhL2hnbW1fMWsvIGRhdGEvaGdtbV8xawpgYGAKClRoaXMgaXMgYSB0b3kgZGF0YXNldCwgd2hpY2ggaXMgbm90IHJlcHJlc2VudGF0aXZlIG9mIHRoZSBzaXplIG9mIHRoZSBkYXRhIGdlbmVyYXRlZCBpbiBzY1JOQVNlcS4KCkNoZWNrIHRoZSBzaXplIG9mIHRoZSBkYXRhIGdlbmVyYXRlZCBmb3IgdGhlIFtTaW5nbGUtY2VsbCBSTkEtc2VxIG9mIDEuMyBtaWxsaW9uIGJyYWluIGNlbGxzIGZyb20gRTE4IG1pY2VdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvL2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0U5MzQyMSkuCgpUaGUgcHJvY2Vzc2luZyBvZiBzY1JOQVNlcSBmYXN0cSBmaWxlIGNhbiBiZSBhIHRlZGlvdXMgYW5kIHRpbWUtY29uc3VtaW5nIHByb2Nlc3MuIEluIGFkZGl0aW9uIHRvIHRoZSBzaXplIG9mIHRoZSBkYXRhIHRvIGhhbmRsZSwgZm9yIGVhY2ggcmVhZCwgd2UgaGF2ZSB0bzoKCi0gbG9jYXRlIHRoZSByZWFkIG9uIHRoZSByZWZlcmVuY2UgZ2Vub21lIG9yIHRyYW5zY3JpdG9tZQotIGlkZW50aWZ5IHRoZSBjZWxsIG1vbGVjdWxhciBiYXJjb2RlCi0gaWRlbnRpZnkgdGhlIFVNSQotIGlkZW50aWZ5IHRoZSBzYW1wbGUgbW9sZWN1bGFyIGJhcmNvZGUgKGlmIGFueSkKClRvIGVhc2UgdGhlc2Ugc3RlcHMsIHRoZSBiYXJjb2RlIGFyZSBvZnRlbiBhdCB0aGUgc3RhcnQgb3IgdGhlIGVuZCBvZiB0aGUgcmVhZHMsIHdoZXJlIHRoZSByZWFkIHF1YWxpdHkgaXMgdGhlIGxvd2VzdC4KTW9zdCBzY1JOQVNlcSBwaXBlbGluZSB3aWxsIGNsaXAgdGhlIGJhcmNvZGUgLyBVTUkgaW5mb3JtYXRpb24gZnJvbSB0aGUgcmVhZCBhbmQgYWRkIHRoZW0gdG8gdGhlIHJlYWQgbmFtZS4gVGhlcmVmb3JlLCB0aGUgcmVhZCBjYW4gYmUgbWFwcGVkIGFuZCB3ZSBjYW4gYXNzaWduIGl0IHRvIGEgZ2l2ZW4gY2VsbCBhbmQgcmVtb3ZlIFBDUiBkdXBsaWNhdGUgYWZ0ZXJ3YXJkLgoKTHVja2lseSBmb3IgeW91LCB0aGUgW2tiIHBpcGVsaW5lIChLYWxsaXN0by1CdXN0b29scyldKGh0dHBzOi8vd3d3LmthbGxpc3RvYnVzLnRvb2xzLykgY2FuIGhhbmRsZSBtb3N0IG9mIHRoZXNlIHN0ZXBzIGluIGZldyBjb21tYW5kcyAoW01lbHN0ZWQgZXQgYWwuXShodHRwczovL3d3dy5iaW9yeGl2Lm9yZy9jb250ZW50LzEwLjExMDEvNjczMjg1djIpKS4KCiMgUmVmZXJlbmNlIGluZGV4aW5nCgpUaGUgYGtiIHBpcGVsaW5lYCB1c2VzIFtLYWxsaXN0b10oaHR0cHM6Ly9wYWNodGVybGFiLmdpdGh1Yi5pby9rYWxsaXN0by8pLiBUaGVyZWZvcmUsIHdlIG5lZWQgYSByZWZlcmVuY2UgaW5kZXggYmVmb3JlIHByb2Nlc3NpbmcgdGhlIGRhdGEuIFRoZSBkYXRhc2V0IGlzIGEgbWl4dHVyZSBvZiBIdW1hbiBhbmQgTW91c2UgY2VsbHM7IHRodXMsIHdlIHdhbnQgdG8gcXVhbnRpZnkgdGhlIHRyYW5zY3JpcHQgb2YgYSBIdW1hbi1Nb3VzZSBoeWJyaWQgcmVmZXJlbmNlLiBXZSBjYW4gcnVuIHRoZSBmb2xsb3dpbmcgY29tbWFuZCB0byBidWlsZCB0aGUgaW5kZXggd2l0aCB0aGUgYGthbGxpc3RvYnVzdG9vbHNgIGNvbnRhaW5lciAoYGRvY2tlcjovL2xibWMvdHBfc2NybmE6MC4xLjBgKQoKYGBge2Jhc2ggZ2V0X3JlZiwgZXZhbCA9IEZ9CndnZXQgZnRwOi8vZnRwLmVuc2VtYmwub3JnL3B1Yi9yZWxlYXNlLTk5L2Zhc3RhL2hvbW9fc2FwaWVucy9jZG5hL0hvbW9fc2FwaWVucy5HUkNoMzguY2RuYS5hbGwuZmEuZ3ogLU8gZGF0YS9oZ21tXzFrL2hzX2NkbmEuZmEuZ3oKd2dldCBmdHA6Ly9mdHAuZW5zZW1ibC5vcmcvcHViL3JlbGVhc2UtOTkvZmFzdGEvbXVzX211c2N1bHVzL2NkbmEvTXVzX211c2N1bHVzLkdSQ20zOC5jZG5hLmFsbC5mYS5neiAtTyBkYXRhL2hnbW1fMWsvbW1fY2RuYS5mYS5negoKa2FsbGlzdG8gaW5kZXggLWkgZGF0YS9oZ21tXzFrL2hzX21tX3RyX2luZGV4LmlkeCBkYXRhL2hnbW1fMWsvaHNfY2RuYS5mYS5neiBkYXRhL2hnbW1fMWsvbW1fY2RuYS5mYS5negpgYGAKYGBgCltidWlsZF0gbG9hZGluZyBmYXN0YSBmaWxlIGRhdGEvaGdtbV8xay9oc19jZG5hLmZhLmd6CltidWlsZF0gbG9hZGluZyBmYXN0YSBmaWxlIGRhdGEvaGdtbV8xay9tbV9jZG5hLmZhLmd6CltidWlsZF0gay1tZXIgbGVuZ3RoOiAzMQpbYnVpbGRdIHdhcm5pbmc6IGNsaXBwZWQgb2ZmIHBvbHktQSB0YWlsIChsb25nZXIgdGhhbiAxMCkKICAgICAgICBmcm9tIDIxMzAgdGFyZ2V0IHNlcXVlbmNlcwpbYnVpbGRdIHdhcm5pbmc6IHJlcGxhY2VkIDggbm9uLUFDR1VUIGNoYXJhY3RlcnMgaW4gdGhlIGlucHV0IHNlcXVlbmNlCiAgICAgICAgd2l0aCBwc2V1ZG9yYW5kb20gbnVjbGVvdGlkZXMKW2J1aWxkXSBjb3VudGluZyBrLW1lcnMgLi4uIGRvbmUuCltidWlsZF0gYnVpbGRpbmcgdGFyZ2V0IGRlIEJydWlqbiBncmFwaCAuLi4gIGRvbmUKW2J1aWxkXSBjcmVhdGluZyBlcXVpdmFsZW5jZSBjbGFzc2VzIC4uLiAgZG9uZQpbYnVpbGRdIHRhcmdldCBkZSBCcnVpam4gZ3JhcGggaGFzIDIxOTIyOTUgY29udGlncyBhbmQgY29udGFpbnMgMjA5NzI4MDY3IGstbWVycwpgYGAKCiMgUk5BIHF1YW50aWZpY2F0aW9uCgpIZXJlIHdlIHdpbGwgZ2VuZXJhdGUgdGhlIGJ1cyBmaWxlLiAqYnVzKiBzdGFuZHMgZm9yIEJhcmJvZGUsIFVNSSwgU2V0LiBJbiB0ZXh0IGZvcm0sIGl0IGlzIGEgdGFibGUgd2hvc2UgZmlyc3QgY29sdW1uIGlzIHRoZSBiYXJjb2RlLiBUaGUgc2Vjb25kIGNvbHVtbiBpcyB0aGUgVU1JIHRoYXQgYXJlIGFzc29jaWF0ZWQgd2l0aCB0aGUgYmFyY29kZS4gVGhlIHRoaXJkIGNvbHVtbiBpcyB0aGUgaW5kZXggb2YgdGhlIGVxdWl2YWxlbmNlIGNsYXNzIHJlYWRzIHdpdGggdGhlIFVNSSBtYXBzLiBUaGUgZm91cnRoIGNvbHVtbiBpcyB0aGUgY291bnQgb2YgcmVhZHMgd2l0aCB0aGlzIGJhcmNvZGUsIFVNSSwgYW5kIGVxdWl2YWxlbmNlIGNsYXNzIGNvbWJpbmF0aW9uLCB3aGljaCBpcyBpZ25vcmVkIGFzIG9uZSBVTUkgc2hvdWxkIHN0YW5kIGZvciBvbmUgbW9sZWN1bGUuIFtTZWUgdGhpcyBwYXBlciBmb3IgbW9yZSBkZXRhaWxdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDkzL2Jpb2luZm9ybWF0aWNzL2J0ejI3OSkuCgpBbHNvLCBzaW5jZSB0aGUgcmVhZHMgd2VyZSBnZW5lcmF0ZWQgd2l0aCB0aGUgMTB4IEdlbm9taWNzIENocm9taXVtIFNpbmdsZSBDZWxsIHYyIENoZW1pc3RyeSwgdGhlIGAteCAxMHh2MmAgYXJndW1lbnQgaXMgdXNlZC4gVG8gdmlldyBvdGhlcnMgc3VwcG9ydGVkIHRlY2hub2xvZ2llcywgYW5kIHNlZSB0aGUgaW5mb3JtYXRpb24ga2IgbmVlZHMsIHJ1biBga2FsbGlzdG8gYnVzIC0tbGlzdGAuCgpgYGB7YmFzaCBxdWFudGlmX2thbGxpc3RvLCBldmFsPUZ9Cm1rZGlyIC1wIHJlc3VsdHMvaGdtbV8xay8Ka2FsbGlzdG8gYnVzIFwKICAtaSBkYXRhL2hnbW1fMWsvaHNfbW1fdHJfaW5kZXguaWR4IFwKICAtbyByZXN1bHRzL2hnbW1fMWsvIC14IDEweHYyIC10IDEwIFwKICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwMV9SMV8wMDEuZmFzdHEuZ3ogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDFfUjJfMDAxLmZhc3RxLmd6IFwKICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwMl9SMV8wMDEuZmFzdHEuZ3ogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDJfUjJfMDAxLmZhc3RxLmd6IFwKICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwM19SMV8wMDEuZmFzdHEuZ3ogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDNfUjJfMDAxLmZhc3RxLmd6IFwKICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwNF9SMV8wMDEuZmFzdHEuZ3ogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDRfUjJfMDAxLmZhc3RxLmd6IFwKICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwNV9SMV8wMDEuZmFzdHEuZ3ogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDVfUjJfMDAxLmZhc3RxLmd6IFwKICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwNl9SMV8wMDEuZmFzdHEuZ3ogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDZfUjJfMDAxLmZhc3RxLmd6IFwKICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwN19SMV8wMDEuZmFzdHEuZ3ogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDdfUjJfMDAxLmZhc3RxLmd6IFwKICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwOF9SMV8wMDEuZmFzdHEuZ3ogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDhfUjJfMDAxLmZhc3RxLmd6CmBgYAoKYGBgCltpbmRleF0gay1tZXIgbGVuZ3RoOiAzMQpbaW5kZXhdIG51bWJlciBvZiB0YXJnZXRzOiAzMDksNzg1CltpbmRleF0gbnVtYmVyIG9mIGstbWVyczogMjA5LDcyOCwwNjcKW2luZGV4XSBudW1iZXIgb2YgZXF1aXZhbGVuY2UgY2xhc3NlczogMSwyODgsNDAwCltxdWFudF0gd2lsbCBwcm9jZXNzIHNhbXBsZSAxOiBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwMV9SMV8wMDEuZmFzdHEuZ3oKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEvaGdtbV8xay9mYXN0cXMvaGdtbV8xa19TMV9MMDAxX1IyXzAwMS5mYXN0cS5negpbcXVhbnRdIHdpbGwgcHJvY2VzcyBzYW1wbGUgMjogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDJfUjFfMDAxLmZhc3RxLmd6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwMl9SMl8wMDEuZmFzdHEuZ3oKW3F1YW50XSB3aWxsIHByb2Nlc3Mgc2FtcGxlIDM6IGRhdGEvaGdtbV8xay9mYXN0cXMvaGdtbV8xa19TMV9MMDAzX1IxXzAwMS5mYXN0cS5negogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDNfUjJfMDAxLmZhc3RxLmd6CltxdWFudF0gd2lsbCBwcm9jZXNzIHNhbXBsZSA0OiBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwNF9SMV8wMDEuZmFzdHEuZ3oKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEvaGdtbV8xay9mYXN0cXMvaGdtbV8xa19TMV9MMDA0X1IyXzAwMS5mYXN0cS5negpbcXVhbnRdIHdpbGwgcHJvY2VzcyBzYW1wbGUgNTogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDVfUjFfMDAxLmZhc3RxLmd6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwNV9SMl8wMDEuZmFzdHEuZ3oKW3F1YW50XSB3aWxsIHByb2Nlc3Mgc2FtcGxlIDY6IGRhdGEvaGdtbV8xay9mYXN0cXMvaGdtbV8xa19TMV9MMDA2X1IxXzAwMS5mYXN0cS5negogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDZfUjJfMDAxLmZhc3RxLmd6CltxdWFudF0gd2lsbCBwcm9jZXNzIHNhbXBsZSA3OiBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwN19SMV8wMDEuZmFzdHEuZ3oKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEvaGdtbV8xay9mYXN0cXMvaGdtbV8xa19TMV9MMDA3X1IyXzAwMS5mYXN0cS5negpbcXVhbnRdIHdpbGwgcHJvY2VzcyBzYW1wbGUgODogZGF0YS9oZ21tXzFrL2Zhc3Rxcy9oZ21tXzFrX1MxX0wwMDhfUjFfMDAxLmZhc3RxLmd6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhL2hnbW1fMWsvZmFzdHFzL2hnbW1fMWtfUzFfTDAwOF9SMl8wMDEuZmFzdHEuZ3oKW3F1YW50XSBmaW5kaW5nIHBzZXVkb2FsaWdubWVudHMgZm9yIHRoZSByZWFkcyAuLi4KIGRvbmUKW3F1YW50XSBwcm9jZXNzZWQgNjMsMjUyLDI5NiByZWFkcywgNTIsMjgxLDkyMSByZWFkcyBwc2V1ZG9hbGlnbmVkCmBgYAoKVGhlIHF1YW50aWZpY2F0aW9uIHJ1biBpbiBhcm91bmQgMiBtaW4gb24gMTAgdGhyZWFkcyBhbmQgY3JlYXRlcyA0IGZpbGVzOgoKYGBgc2gKbHMgcmVzdWx0cy9oZ21tXzFrLwpgYGAKCi0gYG1hdHJpeC5lY2A6IEEgdGV4dCBmaWxlIHdpdGggdHdvIGNvbHVtbnMuIFRoZSBmaXJzdCBjb2x1bW4gaXMgdGhlIDAgYmFzZWQgaW5kZXggb2YgZXF1aXZhbGVuY2UgY2xhc3Nlcy4gVGhlIHNlY29uZCBjb2x1bW4gaXMgdGhlIHNldCBvZiB0cmFuc2NyaXB0cyAoZGVub3RlZCBieSAwIGJhc2VkIGluZGV4IGJhc2VkIG9uIG9yZGVyIG9mIGFwcGVhcmFuY2UgaW4gdGhlIHRyYW5zY3JpcHRvbWUgZmFzdGEgZmlsZSkgcHJlc2VudCBpbiB0aGUgY29ycmVzcG9uZGluZyBlcXVpdmFsZW5jZSBjbGFzcy4KLSBgb3V0cHV0LmJ1c2A6IFRoZSBkYXRhIHJlcHJlc2VudGVkIGluIGJ1cyBmb3JtYXQuIFRoaXMgaXMgYSBiaW5hcnkgZmlsZSwgc28gZG9u4oCZdCB1c2Ugc29tZXRoaW5nIGxpa2UgYHJlYWQudGFibGVgIHRvIHJlYWQgaXQgaW50byBSLgotIGBydW5faW5mby5qc29uYDogSW5mb3JtYXRpb24gYWJvdXQgdGhlIGNhbGwgdG8ga2FsbGlzdG8gYnVzLCBpbmNsdWRpbmcgdGhlIGNvbW1hbmQgdXNlZCwgbnVtYmVyIGFuZCBwZXJjZW50YWdlIG9mIHJlYWRzIHBzZXVkb2FsaWduZWQsIHZlcnNpb24gb2Yga2FsbGlzdG8gdXNlZCwgZXRjLgotIGB0cmFuc2NyaXB0LnR4dGA6IEEgdGV4dCBmaWxlIHdpdGggb25lIGNvbHVtbiwgd2hpY2ggaXMgdGhlIHRyYW5zY3JpcHRzIHByZXNlbnQgaW4gdGhlIGRhdGEsIGluIHRoZSBzYW1lIG9yZGVyIGFzIGluIHRoZSB0cmFuc2NyaXB0b21lIGZhc3RhIGZpbGUuCgpXaXRoIHRoZSBjdXJyZW50IDPigJkgc2VxdWVuY2luZyBwcm90b2NvbHMsIG1vc3QgcGVvcGxlIGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gaG93IG1hbnkgVU1JcyBwZXIgZ2VuZSBwZXIgY2VsbCB3ZSBjYW4gcXVhbnRpZnkuIFdlIHdpbGwgcXVhbnRpZnkgdGhpcyBmcm9tIHRoZSBidXMgb3V0cHV0LCBhbmQgdG8gZG8gc28sIHdlIG5lZWQgdG8gZmluZCB3aGljaCBnZW5lIGNvcnJlc3BvbmRzIHRvIGVhY2ggdHJhbnNjcmlwdC4KVGhlIGB0MmcudHh0YCBmaWxlIGlzIGEgdHJhbnNjcmlwdC10by1nZW5lIG1hcHBpbmcsIHdoaWNoIGFsbG93cyB1cyB0byBoYXZlIGFjY2VzcyB0byBnZW5lcyBjb3VudHMuCgpgYGB7ciB0MmdfaGdtbV8xaywgZGVwZW5kc29uPSJsaWJyYXJ5IiwgZXZhbD1GfQpkaXIuY3JlYXRlKCJyZXN1bHRzL2hnbW1fMWsiLCByZWN1cnNpdmUgPSBUKQp0cjJnIDwtIHRyYW5zY3JpcHQyZ2VuZSgKICBmYXN0YV9maWxlID0gYygiZGF0YS9oZ21tXzFrL2hzX2NkbmEuZmEuZ3oiLCAiLi9kYXRhL2hnbW1fMWsvbW1fY2RuYS5mYS5neiIpLAogIGthbGxpc3RvX291dF9wYXRoID0gInJlc3VsdHMvaGdtbV8xay8iCikKaGVhZCh0cjJnKQpzYXZlX3RyMmdfYnVzdG9vbHModHIyZywgInJlc3VsdHMvaGdtbV8xay90cjJnX2hnbW0udHN2IikKYGBgCgpUaGUgYC9kYXRhL3NoYXJlL01BRFQvVFBfbG1vZG9sby8xMHh2Ml93aGl0ZWxpc3QudHh0YCBjb250YWlucyBhbGwgdGhlIGJhcmNvZGVzIGtub3duIHRvIGJlIHByZXNlbnQgaW4gdGhlIGtpdCB2MiBpcyBwcm92aWRlZCBieSAxMHguCldlIGFyZSByZWFkeSB0byBtYWtlIHRoZSBnZW5lIGNvdW50IG1hdHJpeC4gRmlyc3QsIGBidXN0b29sc2AgcnVucyBiYXJjb2RlIGVycm9yIGNvcnJlY3Rpb24gb24gdGhlIGJ1cyBmaWxlLiBUaGVuLCB0aGUgY29ycmVjdGVkIGJ1cyBmaWxlIGlzIHNvcnRlZCBieSBiYXJjb2RlLCBVTUksIGFuZCBlcXVpdmFsZW5jZSBjbGFzc2VzLiBUaGVuIHRoZSBVTUlzIGFyZSBjb3VudGVkIGFuZCB0aGUgY291bnRzIGFyZSBjb2xsYXBzZWQgaW50byBnZW5lIGxldmVsLgoKYGBge2Jhc2ggYnVzdG9vbHMsIGV2YWw9Rn0KbWtkaXIgLXAgcmVzdWx0cy9oZ21tXzFrL2dlbmVjb3VudCB0bXAKYnVzdG9vbHMgY29ycmVjdCAtdyBkYXRhL3doaXRlbGlzdF92Mi50eHQgLXAgcmVzdWx0cy9oZ21tXzFrL291dHB1dC5idXMgfCBcCiAgYnVzdG9vbHMgc29ydCAtVCB0bXAvIC10IDQgLXAgLSB8IFwKICBidXN0b29scyBjb3VudCAtbyByZXN1bHRzL2hnbW1fMWsvZ2VuZWNvdW50L2dlbmVzIC1nIHJlc3VsdHMvaGdtbV8xay90cjJnX2hnbW0udHN2IFwKICAtZSByZXN1bHRzL2hnbW1fMWsvbWF0cml4LmVjIC10IHJlc3VsdHMvaGdtbV8xay90cmFuc2NyaXB0cy50eHQgLS1nZW5lY291bnRzCmBgYAoKYGBgCkZvdW5kIDczNzI4MCBiYXJjb2RlcyBpbiB0aGUgd2hpdGVsaXN0ClByb2Nlc3NlZCA1MjI4MTkyMSBCVVMgcmVjb3JkcwpJbiB3aGl0ZWxpc3QgPSA1MDgyNTI2MQpDb3JyZWN0ZWQgICAgPSAzNDg3NTIKVW5jb3JyZWN0ZWQgID0gMTEwNzkwOApgYGAKClRoZSBydW4gbG9ncyBpbmZvcm1hdGlvbiBpcyBhY2Nlc3NpYmxlIGluIGBqc29uYCBmb3JtYXQgaW4gdGhlIGByZXN1bHRzL2htbV8xay9gIGRpcmVjdG9yeS4KCj4gWW91IGNhbiBjaGVjayBob3cgdG8gbG9hZCB0aGUgZGF0YSBpbiBSIHdpdGggW3RoZSBzaW5nbGUtY2VsbCBleHBlcmltZW50XSguL3NpbmdlY2VsbGV4cGVyaW1lbnQubmIuaHRtbCkgb3IgZGlyZWNsdHkgZ28gdG8gW3RoZSBxdWFsaXR5IGNvbnRyb2wgb2YgdGhlIGRhdGFdKHF1YWxpdHlfY29udHJvbC5uYi5odG1sKQoK