1 Calculating error rates.

This document is intended to step through the process followed in these estimates of RT error rates. This R package is a companion to the similarly named ‘errrt’ Perl package, which is responsible for preprocessing the raw sequencing data.

2 Introduction

An outline of the experimental process is represented by:

Experimental Design

Experimental Design

The errrt package is responsible for the bottom and right side of the image. It takes the raw fastq data, merges the reads using flash, writes out the index and identifier for all reads identical to the template, writes out the sequences for the ones not identical to the template, aligns them with fasta36 against the template, interprets the fasta36 output, and writes a table of the mismatches, insertions, deletions from it.

This package, creatively named ‘errRt’ takes a sample sheet describing the samples, reads the tables of identical reads/mismatches, and creates matrices of mutation rates for various classes of mutations.

3 Installation

The perl package may be installed via:

The R package may be installed via:

4 Preprocessing Steps

I split up the preprocessing into discrete steps so that I could check on each phase and try to ensure correctness. The errrt Perl package was written so that pretty much everything may be set via command line parameters, but all the defaults were written to suite this data set. Thus I have a pretty simple bash for loop which handled the work of preprocessing the data.

The following fragment of bash was run inside my directory preprocessing/, which contained 3 subdirectories: s1/, s2/, and s3/ which contain the raw reads for the 3 samples. The excel spreadsheet in inst/extdata/all_samples.xlsx describes them, but s1 is the control, s2 has low magnesium, and s3 has high.

As the above shell fragment states, the final outputs of errrt are step4.txt.xz and step2_identical_reads.txt.xz. These filenames are therefore recorded in the sample sheet in the columns ‘ident_table’ and ‘mutation_table’ for each sample.

I decided for the moment at least to copy these files into inst/extdata and modify the sample sheet in inst/extdata to refer to them.

5 Processing the data

I wrapped the various steps performed by errRt into a primary main function, create_matrices(). The following is the invocation I used for this data, along with some explanation of what is happening.

5.1 Getting Started

## [1] "/mnt/sshfs/major/local/R/3.6.1/3.10/errRt/extdata/all_samples.xlsx"

Here is the resulting experimental design. The create_matrices() function will use this to read the tables of identical reads and mutated reads.

sampleid condition batch identtable mutationtable
s1 s1 control b201910 s1/step2_identical_reads.txt.xz s1/step4.txt.xz
s2 s2 low b201910 s2/step2_identical_reads.txt.xz s2/step4.txt.xz
s3 s3 high b201910 s3/step2_identical_reads.txt.xz s3/step4.txt.xz

5.2 Create matrices

The create_matrices() function has a few options to adjust the stringency of the various ways to define an error. It functions to count the various error types with quantify_parsed(), gather them into matrices by sample along with the perceived errors by the sequencer, and calculate aggregate error rates for the various classes of error. In other words, it does pretty much everything in one function call.

## Starting sample: 1.
## Reading the file containing mutations: s1/step4.txt.xz
## Error: 's1/step4.txt.xz' does not exist in current working directory ('/mnt/sshfs/cbcbsub/fs/cbcb-lab/nelsayed/scratch/atb/dnaseq/rt_erates_destefano_2019/errRt/vignettes').

The options are explained in the help documentation, but here is an overview of the ones which might not be self-evident.

  • min_reads: How many reads/index are required for a given index to be considered usable? This is the first toggle of stringency and is affected primarily by the sequencing depth.
  • min_indexes: How many separate indexes are expected to agree on a given mutation in order for it to be considered real? This toggle of stringency is primarily affected by the success in getting a large number of different RT primers to bind and extend during the early stages of creating the sequencing libraries.
  • min_sequencer: In order for a given mutation to be deemed ‘sequencer-specific’, 1 and only 1 read out of ‘n’ for a given index must have the mutation. This toggles ‘n’, higher numbers are thus more stringent and require greater sequencing depth. If this is set too low, a pool of false positives will result; the default of 10 is completely arbitrary.
  • min_position: In the actual experiment, it appears that the primer used for the experimental samples may have had an error, or it was differently cut out of the PAGE purification gel, or something else happened which resulted in a tremendous percentage of the reads presenting themselves as either a deletion at ~ position 20, or a A to C mutation at position 22 or somesuch. This parameter just ignores any mutations occuring before this position. We observed that this strange trend stopped at position 22, and so set it to 24 to be safe.
  • prune_n: When true, this simply drops entries in the mutation table which have Ns.

create_matrices() works in 3 main stages:

  1. For each sample in the metadata, invoke quantify_parsed(). This function is responsible for reading the tables and collating the various error types. This is the most interesting portion of code and most likely to have problems.
  2. Collect the various tables from step 1 and merge them into 1 matrix per table where the rows are mutation types and columns are samples.
  3. Calculate error rates by read/index and write new versions of the matrices from step 2 accordingly.

While create_matrices() is running, it should print some information about what it is doing and give some idea about how much data is lost due to the stringency parameters. Its result is a list:

  • samples: The result from quantify_parsed() for each sample in the metadata.
  • reads_per_sample: The sum of the reads observed in the mutation data and the identity data, e.g. how many reads survived.
  • indexes_per_sample: The sum of the indexes observed. This is our primary normalization factor, but along with reads_per_sample should help define a healthy vs. problematic sample.
  • matrices: The ‘raw’ matrices for each table.
  • matrices_by_counts: The matrices dividied by the indexes.
  • normalized: The ‘raw’ matrices as cpm, thus normalized by library sizes.
  • normalized_by_counts: The matrices as cpm and dividied by indexes.

Thus, as one might imagine, there are a lot of matrices returned, here are some examples of the data after normalization, in each case I will first print the data for the RT mutations followed by the sequencer:

  1. Mismatches by position as a heatmap: This is quite curious, as we see apparently fewer variants at the end of the sequence.
## Error in gplots::heatmap.2(error_data[["normalized_by_counts"]][["miss_indexes_by_position"]], : object 'error_data' not found
## Error in gplots::heatmap.2(error_data[["normalized_by_counts"]][["miss_sequencer_by_position"]], : object 'error_data' not found
  1. Mismatches by reference nucleotide
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_indexes_by_ref_nt"]]): object 'error_data' not found
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_sequencer_by_ref_nt"]]): object 'error_data' not found
  1. Mismatches by mutated nucleotide
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_indexes_by_hit_nt"]]): object 'error_data' not found
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_sequencer_by_hit_nt"]]): object 'error_data' not found
  1. Mismatches by type: A to C, G to A etc etc
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_indexes_by_type"]]): object 'error_data' not found
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_sequencer_by_type"]]): object 'error_data' not found
  1. Mismatches by transition/transversion
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_indexes_by_trans"]]): object 'error_data' not found
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_sequencer_by_trans"]]): object 'error_data' not found
  1. Mismatches by strong/weak
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_indexes_by_strength"]]): object 'error_data' not found
## Error in knitr::kable(error_data[["normalized_by_counts"]][["miss_sequencer_by_strength"]]): object 'error_data' not found
  1. Insertions by position
## Error in knitr::kable(error_data[["normalized_by_counts"]][["insert_indexes_by_position"]]): object 'error_data' not found
## Error in knitr::kable(error_data[["normalized_by_counts"]][["insert_sequencer_by_position"]]): object 'error_data' not found
  1. Insertions by nt
## Error in knitr::kable(error_data[["normalized_by_counts"]][["insert_indexes_by_nt"]]): object 'error_data' not found
## Error in knitr::kable(error_data[["normalized_by_counts"]][["insert_sequencer_by_nt"]]): object 'error_data' not found
  1. Deletions by position

There was insufficient data to quantify these.

LS0tCnRpdGxlOiAiQ291bnRpbmcgUlQgbXV0YXRpb25zIGZyb20gaWxsdW1pbmEgc2VxdWVuY2luZyBkYXRhLiIKYXV0aG9yOiAiYXRiIGFiZWxld0BnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRmX3ByaW50OiBwYWdlZAogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgd2lkdGg6IDMwMAogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIHRvY19mbG9hdDogdHJ1ZQogIEJpb2NTdHlsZTo6aHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIHRvY19mbG9hdDogdHJ1ZQp2aWduZXR0ZTogPgogICVcVmlnbmV0dGVJbmRleEVudHJ5e2Vycm9yX3F1YW50aWZpY2F0aW9ufQogICVcVmlnbmV0dGVFbmdpbmV7a25pdHI6OnJtYXJrZG93bn0KICBcdXNlcGFja2FnZVt1dGY4XXtpbnB1dGVuY30KLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgZm9udC1zaXplOiAxNnB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDE2cHg7Cn0KcHJlIHsKIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCJlcnJSdCIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHdpZHRoPTEyMCwKICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3M9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cz00LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemU9MTApKQpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQ9IiVZJW0lZCIpCiMjdG1wIDwtIHNtKGxvYWRtZShmaWxlbmFtZT1wYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXByZXZpb3VzX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikpKQojI3JtZF9maWxlIDwtICIwM19leHByZXNzaW9uX2luZmVjdGlvbl8yMDE4MDgyMi5SbWQiCmBgYAoKIyBDYWxjdWxhdGluZyBlcnJvciByYXRlcy4KClRoaXMgZG9jdW1lbnQgaXMgaW50ZW5kZWQgdG8gc3RlcCB0aHJvdWdoIHRoZSBwcm9jZXNzIGZvbGxvd2VkIGluIHRoZXNlCmVzdGltYXRlcyBvZiBSVCBlcnJvciByYXRlcy4gIFRoaXMgUiBwYWNrYWdlIGlzIGEgY29tcGFuaW9uIHRvIHRoZSBzaW1pbGFybHkKbmFtZWQgJ2VycnJ0JyBQZXJsIHBhY2thZ2UsIHdoaWNoIGlzIHJlc3BvbnNpYmxlIGZvciBwcmVwcm9jZXNzaW5nIHRoZSByYXcKc2VxdWVuY2luZyBkYXRhLgoKIyBJbnRyb2R1Y3Rpb24KCkFuIG91dGxpbmUgb2YgdGhlIGV4cGVyaW1lbnRhbCBwcm9jZXNzIGlzIHJlcHJlc2VudGVkIGJ5OgoKIVtFeHBlcmltZW50YWwgRGVzaWduXShleHBlcmltZW50YWxfZGVzaWduLnN2ZykKClRoZSBlcnJydCBwYWNrYWdlIGlzIHJlc3BvbnNpYmxlIGZvciB0aGUgYm90dG9tIGFuZCByaWdodCBzaWRlIG9mIHRoZSBpbWFnZS4gIEl0IHRha2VzIHRoZQpyYXcgZmFzdHEgZGF0YSwgbWVyZ2VzIHRoZSByZWFkcyB1c2luZyBmbGFzaCwgd3JpdGVzIG91dCB0aGUgaW5kZXggYW5kCmlkZW50aWZpZXIgZm9yIGFsbCByZWFkcyBpZGVudGljYWwgdG8gdGhlIHRlbXBsYXRlLCB3cml0ZXMgb3V0IHRoZSBzZXF1ZW5jZXMgZm9yCnRoZSBvbmVzIG5vdCBpZGVudGljYWwgdG8gdGhlIHRlbXBsYXRlLCBhbGlnbnMgdGhlbSB3aXRoIGZhc3RhMzYgYWdhaW5zdCB0aGUKdGVtcGxhdGUsIGludGVycHJldHMgdGhlIGZhc3RhMzYgb3V0cHV0LCBhbmQgd3JpdGVzIGEgdGFibGUgb2YgdGhlIG1pc21hdGNoZXMsCmluc2VydGlvbnMsIGRlbGV0aW9ucyBmcm9tIGl0LgoKVGhpcyBwYWNrYWdlLCBjcmVhdGl2ZWx5IG5hbWVkICdlcnJSdCcgdGFrZXMgYSBzYW1wbGUgc2hlZXQgZGVzY3JpYmluZyB0aGUKc2FtcGxlcywgcmVhZHMgdGhlIHRhYmxlcyBvZiBpZGVudGljYWwgcmVhZHMvbWlzbWF0Y2hlcywgYW5kIGNyZWF0ZXMgbWF0cmljZXMKb2YgbXV0YXRpb24gcmF0ZXMgZm9yIHZhcmlvdXMgY2xhc3NlcyBvZiBtdXRhdGlvbnMuCgojIEluc3RhbGxhdGlvbgoKVGhlIHBlcmwgcGFja2FnZSBtYXkgYmUgaW5zdGFsbGVkIHZpYToKCmBgYHtiYXNoIGluc3RhbGxfZXJycnQsIGV2YWw9RkFMU0V9CmdpdCBjbG9uZSBodHRwczovL2dpdGh1Yi5jb20vYWJlbGV3L2VycnJ0CmNkIGVycnJ0CnBlcmwgQnVpbGQuUEwKcGVybCBCdWlsZCBpbnN0YWxsCmBgYAoKVGhlIFIgcGFja2FnZSBtYXkgYmUgaW5zdGFsbGVkIHZpYToKCmBgYHtyIGluc3RhbGxhdGlvbiwgZXZhbD1GQUxTRX0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJhYmVsZXcvZXJyUnQiKQpgYGAKCiMgUHJlcHJvY2Vzc2luZyBTdGVwcwoKSSBzcGxpdCB1cCB0aGUgcHJlcHJvY2Vzc2luZyBpbnRvIGRpc2NyZXRlIHN0ZXBzIHNvIHRoYXQgSSBjb3VsZCBjaGVjayBvbiBlYWNoCnBoYXNlIGFuZCB0cnkgdG8gZW5zdXJlIGNvcnJlY3RuZXNzLiAgVGhlIGVycnJ0IFBlcmwgcGFja2FnZSB3YXMgd3JpdHRlbiBzbyB0aGF0CnByZXR0eSBtdWNoIGV2ZXJ5dGhpbmcgbWF5IGJlIHNldCB2aWEgY29tbWFuZCBsaW5lIHBhcmFtZXRlcnMsIGJ1dCBhbGwgdGhlCmRlZmF1bHRzIHdlcmUgd3JpdHRlbiB0byBzdWl0ZSB0aGlzIGRhdGEgc2V0LiAgVGh1cyBJIGhhdmUgYSBwcmV0dHkgc2ltcGxlIGJhc2gKZm9yIGxvb3Agd2hpY2ggaGFuZGxlZCB0aGUgd29yayBvZiBwcmVwcm9jZXNzaW5nIHRoZSBkYXRhLgoKVGhlIGZvbGxvd2luZyBmcmFnbWVudCBvZiBiYXNoIHdhcyBydW4gaW5zaWRlIG15IGRpcmVjdG9yeSAqcHJlcHJvY2Vzc2luZy8qLAp3aGljaCBjb250YWluZWQgMyBzdWJkaXJlY3RvcmllczogKnMxLyosICpzMi8qLCBhbmQgKnMzLyogd2hpY2ggY29udGFpbiB0aGUKcmF3IHJlYWRzIGZvciB0aGUgMyBzYW1wbGVzLiAgVGhlIGV4Y2VsIHNwcmVhZHNoZWV0IGluCippbnN0L2V4dGRhdGEvYWxsX3NhbXBsZXMueGxzeCogZGVzY3JpYmVzIHRoZW0sIGJ1dCBzMSBpcyB0aGUgY29udHJvbCwgczIgaGFzCmxvdyBtYWduZXNpdW0sIGFuZCBzMyBoYXMgaGlnaC4KCmBgYHtiYXNoIHByZXByb2Nlc3NpbmcsIGV2YWw9RkFMU0V9CiMhL3Vzci9iaW4vZW52IGJhc2gKCiMjIFRoZSBkZWZhdWx0IG9wdGlvbnMgc2hvdWxkIHRha2UgY2FyZSBvZiBtb3N0IG9mIHdoYXQgdGhlc2UgcmVxdWlyZS4Kc3RhcnQ9JChwd2QpCmZvciBzYW1wbGUgaW4gJCgvYmluL2xzIC1kIHMqKTsgZG8KICAgIGNkICR7c2FtcGxlfQogICAgIyMgTWVyZ2UgdGhlIGZvcndhcmQgYW5kIHJldmVyc2UgcmVhZHMuCiAgICAuLi8uLi9lcnJydC9zY3JpcHQvbWVyZ2VfcmVhZHMucGwgLS1yZWFkMSAqXzEuZmFzdHEuZ3ogLS1yZWFkMiAqXzIuZmFzdHEuZ3oKICAgICMjIEV4dHJhY3QgdGhlIHJlYWRzIHdpdGggdGhlIHRlbXBsYXRlIHNlcXVlbmNlIGludG8gYSBmYXN0YSBmaWxlLgogICAgIyMgVGhpcyBhc3N1bWVzIGlucHV0IG5hbWVkICdzdGVwMS5leHRlbmRlZEZyYWdzLmZhc3RxLnh6JyBhbmQgb3V0cHV0IG5hbWVkICdzdGVwMi5mYXN0YScKICAgICMjIFRoaXMgb3V0cHV0IGlzIG5vdCBjb21wcmVzc2VkIGJlY2F1c2UgaXQgaXMgZWFzaWVyIHRvIGludm9rZSBmYXN0YTM2IG9uIHVuY29tcHJlc3NlZCBkYXRhLgogICAgIyMgVGh1cyBpdCBkZWZhdWx0cyB0byBzdGVwMi5mYXN0YSBhcyBvdXRwdXQuCiAgICAuLi8uLi9lcnJydC9zY3JpcHQvZXh0cmFjdF9mYXN0YS5wbAogICAgIyMgUnVuIGZhc3RhIGFnYWluc3QgdGhlIHRlbXBsYXRlIHNlcXVlbmNlLgogICAgIyMgVGhpcyBhc3N1bWVzIHN0ZXAyLmZhc3RhIGFzIGlucHV0IGFuZCBwcm92aWRlcyBzdGVwMy50eHQueHogYXMgb3V0cHV0LgogICAgLi4vLi4vZXJycnQvc2NyaXB0L3J1bl9mYXN0YS5wbAogICAgIyMgQm9pbCB0aGUgZmFzdGEgb3V0cHV0IGludG8gc29tZXRoaW5nIHdlIGNhbiByZWFkLgogICAgIyMgVGhpcyBhc3N1bWVzIHN0ZXAzLnR4dC54eiBhcyBpbnB1dCBhbmQgc3RlcDQudHh0Lnh6IGFzIG91dHB1dC4KICAgIC4uLy4uL2VycnJ0L3NjcmlwdC9wYXJzZV9mYXN0YS5wbAogICAgY2QgJHtzdGFydH0KZG9uZQpgYGAKCkFzIHRoZSBhYm92ZSBzaGVsbCBmcmFnbWVudCBzdGF0ZXMsIHRoZSBmaW5hbCBvdXRwdXRzIG9mIGVycnJ0IGFyZSAqc3RlcDQudHh0Lnh6KgphbmQgKnN0ZXAyX2lkZW50aWNhbF9yZWFkcy50eHQueHoqLiAgVGhlc2UgZmlsZW5hbWVzIGFyZSB0aGVyZWZvcmUgcmVjb3JkZWQgaW4KdGhlIHNhbXBsZSBzaGVldCBpbiB0aGUgY29sdW1ucyAnaWRlbnRfdGFibGUnIGFuZCAnbXV0YXRpb25fdGFibGUnIGZvciBlYWNoCnNhbXBsZS4KCkkgZGVjaWRlZCBmb3IgdGhlIG1vbWVudCBhdCBsZWFzdCB0byBjb3B5IHRoZXNlIGZpbGVzIGludG8gaW5zdC9leHRkYXRhIGFuZAptb2RpZnkgdGhlIHNhbXBsZSBzaGVldCBpbiBpbnN0L2V4dGRhdGEgdG8gcmVmZXIgdG8gdGhlbS4KCiMgUHJvY2Vzc2luZyB0aGUgZGF0YQoKSSB3cmFwcGVkIHRoZSB2YXJpb3VzIHN0ZXBzIHBlcmZvcm1lZCBieSBlcnJSdCBpbnRvIGEgcHJpbWFyeSBtYWluIGZ1bmN0aW9uLAoqY3JlYXRlX21hdHJpY2VzKCkqLiAgVGhlIGZvbGxvd2luZyBpcyB0aGUgaW52b2NhdGlvbiBJIHVzZWQgZm9yIHRoaXMgZGF0YSwKYWxvbmcgd2l0aCBzb21lIGV4cGxhbmF0aW9uIG9mIHdoYXQgaXMgaGFwcGVuaW5nLgoKIyMgR2V0dGluZyBTdGFydGVkCgpgYGB7ciBnZXR0aW5nX3N0YXJ0ZWR9CmxpYnJhcnkoZXJyUnQpCmJhc2VkaXIgPC0gc3lzdGVtLmZpbGUoImV4dGRhdGEiLCBwYWNrYWdlPSJlcnJSdCIpCm1ldGFkYXRhX2ZpbGUgPC0gZmlsZS5wYXRoKGJhc2VkaXIsICJhbGxfc2FtcGxlcy54bHN4IikKbWV0YWRhdGFfZmlsZQpzZXR3ZChiYXNlZGlyKQptZXRhIDwtIGhwZ2x0b29sczo6ZXh0cmFjdF9tZXRhZGF0YShtZXRhZGF0YV9maWxlKQpgYGAKCkhlcmUgaXMgdGhlIHJlc3VsdGluZyBleHBlcmltZW50YWwgZGVzaWduLiAgVGhlICpjcmVhdGVfbWF0cmljZXMoKSogZnVuY3Rpb24Kd2lsbCB1c2UgdGhpcyB0byByZWFkIHRoZSB0YWJsZXMgb2YgaWRlbnRpY2FsIHJlYWRzIGFuZCBtdXRhdGVkIHJlYWRzLgoKYGBge3IgbWV0YWRhdGEsIHJlc3VsdHM9J2FzaXMnfQprbml0cjo6a2FibGUobWV0YSkKYGBgCgojIyBDcmVhdGUgbWF0cmljZXMKClRoZSAqY3JlYXRlX21hdHJpY2VzKCkqIGZ1bmN0aW9uIGhhcyBhIGZldyBvcHRpb25zIHRvIGFkanVzdCB0aGUKc3RyaW5nZW5jeSBvZiB0aGUgdmFyaW91cyB3YXlzIHRvIGRlZmluZSBhbiBlcnJvci4gIEl0IGZ1bmN0aW9ucyB0byBjb3VudCB0aGUKdmFyaW91cyBlcnJvciB0eXBlcyB3aXRoICpxdWFudGlmeV9wYXJzZWQoKSosIGdhdGhlciB0aGVtIGludG8gbWF0cmljZXMgYnkKc2FtcGxlIGFsb25nIHdpdGggdGhlIHBlcmNlaXZlZCBlcnJvcnMgYnkgdGhlIHNlcXVlbmNlciwgYW5kIGNhbGN1bGF0ZSBhZ2dyZWdhdGUKZXJyb3IgcmF0ZXMgZm9yIHRoZSB2YXJpb3VzIGNsYXNzZXMgb2YgZXJyb3IuICBJbiBvdGhlciB3b3JkcywgaXQgZG9lcyBwcmV0dHkKbXVjaCBldmVyeXRoaW5nIGluIG9uZSBmdW5jdGlvbiBjYWxsLgoKYGBge3IgY3JlYXRlX21hdHJpY2VzfQplcnJvcl9kYXRhIDwtIGNyZWF0ZV9tYXRyaWNlcyhzYW1wbGVfc2hlZXQ9bWV0YWRhdGFfZmlsZSwgaWRlbnRfY29sdW1uPSJpZGVudHRhYmxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0X2NvbHVtbj0ibXV0YXRpb250YWJsZSIsIG1pbl9yZWFkcz0zLCBtaW5faW5kZXhlcz0zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fc2VxdWVuY2VyPTEwLCBtaW5fcG9zaXRpb249MjQsIHBydW5lX249VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFKQpgYGAKClRoZSBvcHRpb25zIGFyZSBleHBsYWluZWQgaW4gdGhlIGhlbHAgZG9jdW1lbnRhdGlvbiwgYnV0IGhlcmUgaXMgYW4gb3ZlcnZpZXcgb2YKdGhlIG9uZXMgd2hpY2ggbWlnaHQgbm90IGJlIHNlbGYtZXZpZGVudC4KCiogIG1pbl9yZWFkczogIEhvdyBtYW55IHJlYWRzL2luZGV4IGFyZSByZXF1aXJlZCBmb3IgYSBnaXZlbiBpbmRleCB0byBiZQogICBjb25zaWRlcmVkIHVzYWJsZT8gIFRoaXMgaXMgdGhlIGZpcnN0IHRvZ2dsZSBvZiBzdHJpbmdlbmN5IGFuZCBpcyBhZmZlY3RlZAogICBwcmltYXJpbHkgYnkgdGhlIHNlcXVlbmNpbmcgZGVwdGguCiogIG1pbl9pbmRleGVzOiAgSG93IG1hbnkgc2VwYXJhdGUgaW5kZXhlcyBhcmUgZXhwZWN0ZWQgdG8gYWdyZWUgb24gYSBnaXZlbgogICBtdXRhdGlvbiBpbiBvcmRlciBmb3IgaXQgdG8gYmUgY29uc2lkZXJlZCByZWFsPyAgVGhpcyB0b2dnbGUgb2Ygc3RyaW5nZW5jeSBpcwogICBwcmltYXJpbHkgYWZmZWN0ZWQgYnkgdGhlIHN1Y2Nlc3MgaW4gZ2V0dGluZyBhIGxhcmdlIG51bWJlciBvZiBkaWZmZXJlbnQgUlQKICAgcHJpbWVycyB0byBiaW5kIGFuZCBleHRlbmQgZHVyaW5nIHRoZSBlYXJseSBzdGFnZXMgb2YgY3JlYXRpbmcgdGhlIHNlcXVlbmNpbmcKICAgbGlicmFyaWVzLgoqICBtaW5fc2VxdWVuY2VyOiAgSW4gb3JkZXIgZm9yIGEgZ2l2ZW4gbXV0YXRpb24gdG8gYmUgZGVlbWVkCiAgICdzZXF1ZW5jZXItc3BlY2lmaWMnLCAxIGFuZCBvbmx5IDEgcmVhZCBvdXQgb2YgJ24nIGZvciBhIGdpdmVuIGluZGV4IG11c3QgaGF2ZQogICB0aGUgbXV0YXRpb24uICBUaGlzIHRvZ2dsZXMgJ24nLCBoaWdoZXIgbnVtYmVycyBhcmUgdGh1cyBtb3JlIHN0cmluZ2VudCBhbmQKICAgcmVxdWlyZSBncmVhdGVyIHNlcXVlbmNpbmcgZGVwdGguICBJZiB0aGlzIGlzIHNldCB0b28gbG93LCBhIHBvb2wgb2YKICAgZmFsc2UgcG9zaXRpdmVzIHdpbGwgcmVzdWx0OyB0aGUgZGVmYXVsdCBvZiAxMCBpcyBjb21wbGV0ZWx5IGFyYml0cmFyeS4KKiAgbWluX3Bvc2l0aW9uOiAgSW4gdGhlIGFjdHVhbCBleHBlcmltZW50LCBpdCBhcHBlYXJzIHRoYXQgdGhlIHByaW1lciB1c2VkIGZvcgogICB0aGUgZXhwZXJpbWVudGFsIHNhbXBsZXMgbWF5IGhhdmUgaGFkIGFuIGVycm9yLCBvciBpdCB3YXMgZGlmZmVyZW50bHkgY3V0IG91dAogICBvZiB0aGUgUEFHRSBwdXJpZmljYXRpb24gZ2VsLCBvciBzb21ldGhpbmcgZWxzZSBoYXBwZW5lZCB3aGljaCByZXN1bHRlZCBpbiBhCiAgIHRyZW1lbmRvdXMgcGVyY2VudGFnZSBvZiB0aGUgcmVhZHMgcHJlc2VudGluZyB0aGVtc2VsdmVzIGFzIGVpdGhlciBhIGRlbGV0aW9uCiAgIGF0IH4gcG9zaXRpb24gMjAsIG9yIGEgQSB0byBDIG11dGF0aW9uIGF0IHBvc2l0aW9uIDIyIG9yIHNvbWVzdWNoLiAgVGhpcwogICBwYXJhbWV0ZXIganVzdCBpZ25vcmVzIGFueSBtdXRhdGlvbnMgb2NjdXJpbmcgYmVmb3JlIHRoaXMgcG9zaXRpb24uICBXZQogICBvYnNlcnZlZCB0aGF0IHRoaXMgc3RyYW5nZSB0cmVuZCBzdG9wcGVkIGF0IHBvc2l0aW9uIDIyLCBhbmQgc28gc2V0IGl0IHRvIDI0CiAgIHRvIGJlIHNhZmUuCiogIHBydW5lX246ICBXaGVuIHRydWUsIHRoaXMgc2ltcGx5IGRyb3BzIGVudHJpZXMgaW4gdGhlIG11dGF0aW9uIHRhYmxlIHdoaWNoCiAgIGhhdmUgTnMuCgoqY3JlYXRlX21hdHJpY2VzKCkqIHdvcmtzIGluIDMgbWFpbiBzdGFnZXM6CgoxLiAgRm9yIGVhY2ggc2FtcGxlIGluIHRoZSBtZXRhZGF0YSwgaW52b2tlICpxdWFudGlmeV9wYXJzZWQoKSouICBUaGlzIGZ1bmN0aW9uCiAgICBpcyByZXNwb25zaWJsZSBmb3IgcmVhZGluZyB0aGUgdGFibGVzIGFuZCBjb2xsYXRpbmcgdGhlIHZhcmlvdXMgZXJyb3IKICAgIHR5cGVzLiAgVGhpcyBpcyB0aGUgbW9zdCBpbnRlcmVzdGluZyBwb3J0aW9uIG9mIGNvZGUgYW5kIG1vc3QgbGlrZWx5IHRvIGhhdmUKICAgIHByb2JsZW1zLgoyLiAgQ29sbGVjdCB0aGUgdmFyaW91cyB0YWJsZXMgZnJvbSBzdGVwIDEgYW5kIG1lcmdlIHRoZW0gaW50byAxIG1hdHJpeCBwZXIKICAgIHRhYmxlIHdoZXJlIHRoZSByb3dzIGFyZSBtdXRhdGlvbiB0eXBlcyBhbmQgY29sdW1ucyBhcmUgc2FtcGxlcy4KMy4gIENhbGN1bGF0ZSBlcnJvciByYXRlcyBieSByZWFkL2luZGV4IGFuZCB3cml0ZSBuZXcgdmVyc2lvbnMgb2YgdGhlIG1hdHJpY2VzCiAgICBmcm9tIHN0ZXAgMiBhY2NvcmRpbmdseS4KCldoaWxlICpjcmVhdGVfbWF0cmljZXMoKSogaXMgcnVubmluZywgaXQgc2hvdWxkIHByaW50IHNvbWUgaW5mb3JtYXRpb24KYWJvdXQgd2hhdCBpdCBpcyBkb2luZyBhbmQgZ2l2ZSBzb21lIGlkZWEgYWJvdXQgaG93IG11Y2ggZGF0YSBpcyBsb3N0IGR1ZSB0byB0aGUKc3RyaW5nZW5jeSBwYXJhbWV0ZXJzLiAgSXRzIHJlc3VsdCBpcyBhIGxpc3Q6CgoqICBzYW1wbGVzOiBUaGUgcmVzdWx0IGZyb20gKnF1YW50aWZ5X3BhcnNlZCgpKiBmb3IgZWFjaCBzYW1wbGUgaW4gdGhlIG1ldGFkYXRhLgoqICByZWFkc19wZXJfc2FtcGxlOiBUaGUgc3VtIG9mIHRoZSByZWFkcyBvYnNlcnZlZCBpbiB0aGUgbXV0YXRpb24gZGF0YSBhbmQgdGhlCiAgIGlkZW50aXR5IGRhdGEsIGUuZy4gaG93IG1hbnkgcmVhZHMgc3Vydml2ZWQuCiogIGluZGV4ZXNfcGVyX3NhbXBsZTogVGhlIHN1bSBvZiB0aGUgaW5kZXhlcyBvYnNlcnZlZC4gIFRoaXMgaXMgb3VyIHByaW1hcnkKICAgbm9ybWFsaXphdGlvbiBmYWN0b3IsIGJ1dCBhbG9uZyB3aXRoIHJlYWRzX3Blcl9zYW1wbGUgc2hvdWxkIGhlbHAgZGVmaW5lIGEKICAgaGVhbHRoeSB2cy4gcHJvYmxlbWF0aWMgc2FtcGxlLgoqICBtYXRyaWNlczogVGhlICdyYXcnIG1hdHJpY2VzIGZvciBlYWNoIHRhYmxlLgoqICBtYXRyaWNlc19ieV9jb3VudHM6IFRoZSBtYXRyaWNlcyBkaXZpZGllZCBieSB0aGUgaW5kZXhlcy4KKiAgbm9ybWFsaXplZDogVGhlICdyYXcnIG1hdHJpY2VzIGFzIGNwbSwgdGh1cyBub3JtYWxpemVkIGJ5IGxpYnJhcnkgc2l6ZXMuCiogIG5vcm1hbGl6ZWRfYnlfY291bnRzOiBUaGUgbWF0cmljZXMgYXMgY3BtIGFuZCBkaXZpZGllZCBieSBpbmRleGVzLgoKVGh1cywgYXMgb25lIG1pZ2h0IGltYWdpbmUsIHRoZXJlIGFyZSBhIF9sb3RfIG9mIG1hdHJpY2VzIHJldHVybmVkLCBoZXJlIGFyZQpzb21lIGV4YW1wbGVzIG9mIHRoZSBkYXRhIGFmdGVyIG5vcm1hbGl6YXRpb24sIGluIGVhY2ggY2FzZSBJIHdpbGwgZmlyc3QgcHJpbnQKdGhlIGRhdGEgZm9yIHRoZSBSVCBtdXRhdGlvbnMgZm9sbG93ZWQgYnkgdGhlIHNlcXVlbmNlcjoKCjEuICBNaXNtYXRjaGVzIGJ5IHBvc2l0aW9uIGFzIGEgaGVhdG1hcDogVGhpcyBpcyBxdWl0ZSBjdXJpb3VzLCBhcyB3ZSBzZWUKICAgIGFwcGFyZW50bHkgZmV3ZXIgdmFyaWFudHMgYXQgdGhlIGVuZCBvZiB0aGUgc2VxdWVuY2UuCgpgYGB7ciBtaXNtYXRjaGVzX2J5X3Bvc2l0aW9ufQpncGxvdHM6OmhlYXRtYXAuMihlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX2luZGV4ZXNfYnlfcG9zaXRpb24iXV0sCiAgICAgICAgICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgICAgICAgICAgUm93dj1GQUxTRSkKZ3Bsb3RzOjpoZWF0bWFwLjIoZXJyb3JfZGF0YVtbIm5vcm1hbGl6ZWRfYnlfY291bnRzIl1dW1sibWlzc19zZXF1ZW5jZXJfYnlfcG9zaXRpb24iXV0sCiAgICAgICAgICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgICAgICAgICAgUm93dj1GQUxTRSkKYGBgCgoyLiAgTWlzbWF0Y2hlcyBieSByZWZlcmVuY2UgbnVjbGVvdGlkZQoKYGBge3IgcmVmZXJlbmNlX251Y2xlb3RpZGVzLCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3NfaW5kZXhlc19ieV9yZWZfbnQiXV0pCmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX3NlcXVlbmNlcl9ieV9yZWZfbnQiXV0pCmBgYAoKMy4gIE1pc21hdGNoZXMgYnkgbXV0YXRlZCBudWNsZW90aWRlCgpgYGB7ciBtdXRhdGVkX251Y2xlb3RpZGVzLCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3NfaW5kZXhlc19ieV9oaXRfbnQiXV0pCmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX3NlcXVlbmNlcl9ieV9oaXRfbnQiXV0pCmBgYAoKNC4gIE1pc21hdGNoZXMgYnkgdHlwZTogQSB0byBDLCBHIHRvIEEgZXRjIGV0YwoKYGBge3IgdHlwZV9udWNsZW90aWRlcywgcmVzdWx0cz0nYXNpcyd9CmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX2luZGV4ZXNfYnlfdHlwZSJdXSkKa25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3Nfc2VxdWVuY2VyX2J5X3R5cGUiXV0pCmBgYAoKNS4gIE1pc21hdGNoZXMgYnkgdHJhbnNpdGlvbi90cmFuc3ZlcnNpb24KCmBgYHtyIHRyYW5zX251Y2xlb3RpZGVzLCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3NfaW5kZXhlc19ieV90cmFucyJdXSkKa25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3Nfc2VxdWVuY2VyX2J5X3RyYW5zIl1dKQpgYGAKCjYuICBNaXNtYXRjaGVzIGJ5IHN0cm9uZy93ZWFrCgpgYGB7ciBzdHJlbmd0aF9udWNsZW90aWRlcywgcmVzdWx0cz0nYXNpcyd9CmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX2luZGV4ZXNfYnlfc3RyZW5ndGgiXV0pCmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX3NlcXVlbmNlcl9ieV9zdHJlbmd0aCJdXSkKYGBgCgo3LiAgSW5zZXJ0aW9ucyBieSBwb3NpdGlvbgoKYGBge3IgaW5zZXJ0X3Bvc2l0aW9uLCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbImluc2VydF9pbmRleGVzX2J5X3Bvc2l0aW9uIl1dKQprbml0cjo6a2FibGUoZXJyb3JfZGF0YVtbIm5vcm1hbGl6ZWRfYnlfY291bnRzIl1dW1siaW5zZXJ0X3NlcXVlbmNlcl9ieV9wb3NpdGlvbiJdXSkKYGBgCgo4LiAgSW5zZXJ0aW9ucyBieSBudAoKYGBge3IgaW5zZXJ0X250LCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbImluc2VydF9pbmRleGVzX2J5X250Il1dKQprbml0cjo6a2FibGUoZXJyb3JfZGF0YVtbIm5vcm1hbGl6ZWRfYnlfY291bnRzIl1dW1siaW5zZXJ0X3NlcXVlbmNlcl9ieV9udCJdXSkKYGBgCgo5LiAgRGVsZXRpb25zIGJ5IHBvc2l0aW9uCgpUaGVyZSB3YXMgaW5zdWZmaWNpZW50IGRhdGEgdG8gcXVhbnRpZnkgdGhlc2UuCg==