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] "/tmp/RtmpPGuTJL/temp_libpath9dc2c624a249c/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
## Reading the file containing the identical reads: s1/step2_identical_reads.txt.xz
## Removing any differences before position: 24.
## Before pruning, there are: 1156535 reads.
## After position pruning, there are: 1037310 reads: 90%.
## Removing any reads with 'N' as the hit.
## Before N pruning, there are: 1037310 reads.
## After N pruning, there are: 1021066 reads: 98%.
## Gathering information about the indexes observed, this is slow.
## Before read pruning, there are: 1742743 indexes.
## After read pruning, there are: 838158 indexes.
## Removing indexes with fewer than 3 indexes.
## Before index pruning, there are: 1021066 changed reads.
## Before index pruning, there are: 4681501 identical reads.
## After index pruning, there are: 532571 changed reads: 52%.
## After index pruning, there are: 3663814 identical reads: 78%.
## Gathering identical, mutant, and sequencer reads/indexes.
## Counting by direction.
## Counting by string.
## Subsetting based on mutations with at least 3 indexes.
## Counting by reference position.
## Counting by identity string.
## Counting by reference nucleotide.
## Counting by product nucleotide.
## Counting by mutation type.
## Counting by transitions/transversion.
## Counting strong/weak.
## Counting insertions by position.
## Counting insertions by nucleotide
## Counting deletions by position.
## Counting deletions by nucleotide.
## Starting sample: 2.
## Reading the file containing mutations: s2/step4.txt.xz
## Reading the file containing the identical reads: s2/step2_identical_reads.txt.xz
## Removing any differences before position: 24.
## Before pruning, there are: 3421203 reads.
## After position pruning, there are: 1758479 reads: 51%.
## Removing any reads with 'N' as the hit.
## Before N pruning, there are: 1758479 reads.
## After N pruning, there are: 1732605 reads: 99%.
## Gathering information about the indexes observed, this is slow.
## Before read pruning, there are: 1263563 indexes.
## After read pruning, there are: 694479 indexes.
## Removing indexes with fewer than 3 indexes.
## Before index pruning, there are: 1732605 changed reads.
## Before index pruning, there are: 5230976 identical reads.
## After index pruning, there are: 874781 changed reads: 50%.
## After index pruning, there are: 4834367 identical reads: 92%.
## Gathering identical, mutant, and sequencer reads/indexes.
## Counting by direction.
## Counting by string.
## Subsetting based on mutations with at least 3 indexes.
## Counting by reference position.
## Counting by identity string.
## Counting by reference nucleotide.
## Counting by product nucleotide.
## Counting by mutation type.
## Counting by transitions/transversion.
## Counting strong/weak.
## Counting insertions by position.
## Counting insertions by nucleotide
## Counting deletions by position.
## Counting deletions by nucleotide.
## Starting sample: 3.
## Reading the file containing mutations: s3/step4.txt.xz
## Reading the file containing the identical reads: s3/step2_identical_reads.txt.xz
## Removing any differences before position: 24.
## Before pruning, there are: 4309681 reads.
## After position pruning, there are: 1564155 reads: 36%.
## Removing any reads with 'N' as the hit.
## Before N pruning, there are: 1564155 reads.
## After N pruning, there are: 1532016 reads: 98%.
## Gathering information about the indexes observed, this is slow.
## Before read pruning, there are: 887982 indexes.
## After read pruning, there are: 465046 indexes.
## Removing indexes with fewer than 3 indexes.
## Before index pruning, there are: 1532016 changed reads.
## Before index pruning, there are: 3583390 identical reads.
## After index pruning, there are: 783358 changed reads: 51%.
## After index pruning, there are: 3332425 identical reads: 93%.
## Gathering identical, mutant, and sequencer reads/indexes.
## Counting by direction.
## Counting by string.
## Subsetting based on mutations with at least 3 indexes.
## Counting by reference position.
## Counting by identity string.
## Counting by reference nucleotide.
## Counting by product nucleotide.
## Counting by mutation type.
## Counting by transitions/transversion.
## Counting strong/weak.
## Counting insertions by position.
## Counting insertions by nucleotide
## Counting deletions by position.
## Counting deletions by nucleotide.
## Working on miss_reads_by_position.
## Working on miss_indexes_by_position.
## Working on miss_sequencer_by_position.
## Working on miss_reads_by_string.
## Warning in order(as.numeric(rownames(matrices[[t]]))): NAs introduced by
## coercion
## Working on miss_indexes_by_string.
## Warning in order(as.numeric(rownames(matrices[[t]]))): NAs introduced by
## coercion
## Working on miss_sequencer_by_string.
## Warning in order(as.numeric(rownames(matrices[[t]]))): NAs introduced by
## coercion
## Working on miss_reads_by_ref_nt.
## Working on miss_indexes_by_ref_nt.
## Working on miss_sequencer_by_ref_nt.
## Working on miss_reads_by_hit_nt.
## Working on miss_indexes_by_hit_nt.
## Working on miss_sequencer_by_hit_nt.
## Working on miss_reads_by_type.
## Working on miss_indexes_by_type.
## Working on miss_sequencer_by_type.
## Working on miss_reads_by_trans.
## Working on miss_indexes_by_trans.
## Working on miss_sequencer_by_trans.
## Working on miss_reads_by_strength.
## Working on miss_indexes_by_strength.
## Working on miss_sequencer_by_strength.
## Working on insert_reads_by_position.
## Working on insert_indexes_by_position.
## Working on insert_sequencer_by_position.
## Working on insert_reads_by_nt.
## Working on insert_indexes_by_nt.
## Working on insert_sequencer_by_nt.
## Working on delete_reads_by_position.
## Working on delete_indexes_by_position.
## Working on delete_sequencer_by_position.
## Working on delete_reads_by_nt.
## Working on delete_indexes_by_nt.
## Working on delete_sequencer_by_nt.
## Skipping table: delete_reads_by_position
## Skipping table: delete_indexes_by_position
## Skipping table: delete_sequencer_by_position
## Skipping table: delete_reads_by_nt
## Skipping table: delete_indexes_by_nt
## Skipping table: delete_sequencer_by_nt

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.
## Warning in gplots::heatmap.2(error_data[["normalized_by_counts"]]
## [["miss_indexes_by_position"]], : Discrepancy: Rowv is FALSE, while dendrogram
## is `both'. Omitting row dendogram.

## Warning in gplots::heatmap.2(error_data[["normalized_by_counts"]]
## [["miss_sequencer_by_position"]], : Discrepancy: Rowv is FALSE, while dendrogram
## is `both'. Omitting row dendogram.

  1. Mismatches by reference nucleotide
s1 s2 s3
A 0.0965 0.2340 0.5623
C 0.6078 0.8293 0.4911
G 0.8937 0.3998 0.3085
T 0.0971 0.1681 0.2422
s1 s2 s3
A 0.3294 0.3464 0.5231
C 0.3180 0.5133 0.2713
G 0.3688 0.2011 0.2530
T 0.3956 0.5071 0.7605
  1. Mismatches by mutated nucleotide
s1 s2 s3
A 0.4794 0.7054 0.6678
C 0.1254 0.1721 0.1012
G 0.1745 0.2213 0.4140
T 0.5130 0.3522 0.6819
s1 s2 s3
A 0.2307 0.2967 0.4339
C 0.4693 0.6286 0.3616
G 0.6785 0.4078 0.4838
T 0.1971 0.2302 0.3422
  1. Mismatches by type: A to C, G to A etc etc
s1 s2 s3
A_C 0.0553 0.0404 0.0433
A_G 0.0376 0.1609 0.2962
A_T 0.0181 0.0362 0.0419
C_A 0.3978 0.2640 0.1602
C_G 0.0176 0.0416 0.0557
C_T 0.1644 0.2914 0.5133
G_A 0.0720 0.2747 0.1711
G_C 0.0146 0.0182 0.0171
G_T 0.7420 0.1984 0.1267
T_A 0.0095 0.0458 0.0392
T_C 0.0441 0.0483 0.0528
T_G 0.0921 0.0964 0.0927
s1 s2 s3
A_C 0.2403 0.1817 0.1850
A_G 0.0803 0.0892 0.0884
A_T 0.0407 0.0565 0.0576
C_A 0.1290 0.1515 0.1462
C_G 0.0732 0.0728 0.0661
C_T 0.1331 0.1315 0.1267
G_A 0.0560 0.0518 0.0526
G_C 0.0580 0.0759 0.0838
G_T 0.1813 0.1558 0.1579
T_A 0.0457 0.0425 0.0419
T_C 0.1214 0.1257 0.1293
T_G 0.4494 0.4930 0.4918
  1. Mismatches by transition/transversion
s1 s2 s3
transition 0.2309 1.0989 0.8993
transversion 1.1613 0.5834 0.8074
s1 s2 s3
transition 0.297 0.5457 0.366
transversion 1.081 0.8903 1.604
  1. Mismatches by strong/weak
s1 s2 s3
strong_strong 0.0267 0.0598 0.1087
strong_weak 1.1740 1.4607 0.6864
weak_strong 0.3137 0.2672 0.4634
weak_weak 0.0195 0.0795 0.1125
s1 s2 s3
strong_strong 0.1087 0.1487 0.2238
strong_weak 0.4338 0.6537 0.3568
weak_strong 1.1836 0.6333 0.7703
weak_weak 0.0683 0.0892 0.1332
  1. Insertions by position
s2 s3
30 0.0332 0.0226
46 0.0000 0.0076
48 0.2105 0.2173
51 0.4135 0.6765
52 0.0000 0.0339
55 0.0000 0.0302
72 0.0000 0.1101
81 0.0000 0.0151
91 0.0886 0.2032
92 0.3308 0.0661
93 0.0000 0.0169
97 0.6616 0.1568
100 0.0332 0.0000
123 0.0000 0.0454
142 0.0000 0.0198
144 0.0000 0.0170
148 0.0000 0.0113
151 0.0662 0.0000
154 0.0886 0.0000
s2 s3
29 0.1440 0.1129
34 0.0000 0.1265
35 0.1080 0.0000
42 0.0000 0.1687
46 0.0000 0.0847
48 0.2688 0.2108
51 0.1080 0.1412
55 0.0000 0.1265
66 0.1440 0.0000
92 0.8601 0.5903
97 0.0000 0.0847
149 0.0000 0.1687
151 0.0000 0.0847
156 0.2688 0.0000
  1. Insertions by nt
s2 s3
A 0.2769 0.7219
C 0.3804 0.1975
G 0.3434 0.1682
T 0.8436 0.6237
s2 s3
A 0.108 0.2259
C 1.236 1.0119
G 0.504 0.4517
T 0.000 0.1265
  1. Deletions by position

There was insufficient data to quantify these.

LS0tCnRpdGxlOiAiQ291bnRpbmcgUlQgbXV0YXRpb25zIGZyb20gaWxsdW1pbmEgc2VxdWVuY2luZyBkYXRhLiIKYXV0aG9yOiAiYXRiIGFiZWxld0BnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGtlZXBfbWQ6IGZhbHNlCiAgICBtb2RlOiBzZWxmY29udGFpbmVkCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICB0aGVtZTogcmVhZGFibGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGRmX3ByaW50OiBwYWdlZAogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgd2lkdGg6IDMwMAogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIHRvY19mbG9hdDogdHJ1ZQogIEJpb2NTdHlsZTo6aHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIHRvY19mbG9hdDogdHJ1ZQp2aWduZXR0ZTogPgogICVcVmlnbmV0dGVJbmRleEVudHJ5e2Vycm9yX3F1YW50aWZpY2F0aW9ufQogICVcVmlnbmV0dGVFbmdpbmV7a25pdHI6OnJtYXJrZG93bn0KICBcdXNlcGFja2FnZVt1dGY4XXtpbnB1dGVuY30KLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5LCB0ZCB7CiAgZm9udC1zaXplOiAxNnB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDE2cHg7Cn0KcHJlIHsKIGZvbnQtc2l6ZTogMTZweAp9Cjwvc3R5bGU+CgpgYGB7ciBvcHRpb25zLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCJlcnJSdCIpCmJhc2VkaXIgPC0gc3lzdGVtLmZpbGUoImV4dGRhdGEiLCBwYWNrYWdlPSJlcnJSdCIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHdpZHRoPTEyMCwKICAgICAgICAgICAgICAgICAgICAgcm9vdC5kaXI9YmFzZWRpciwKICAgICAgICAgICAgICAgICAgICAgcHJvZ3Jlc3M9VFJVRSwKICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFLAogICAgICAgICAgICAgICAgICAgICBlY2hvPVRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlcnJvcj1UUlVFLAogICAgICAgICAgICAgICAgICAgICAgZHBpPTk2KQpvbGRfb3B0aW9ucyA8LSBvcHRpb25zKGRpZ2l0cz00LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAga25pdHIuZHVwbGljYXRlLmxhYmVsPSJhbGxvdyIpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9idyhiYXNlX3NpemU9MTApKQpydW5kYXRlIDwtIGZvcm1hdChTeXMuRGF0ZSgpLCBmb3JtYXQ9IiVZJW0lZCIpCiMjdG1wIDwtIHNtKGxvYWRtZShmaWxlbmFtZT1wYXN0ZTAoZ3N1YihwYXR0ZXJuPSJcXC5SbWQiLCByZXBsYWNlPSIiLCB4PXByZXZpb3VzX2ZpbGUpLCAiLXYiLCB2ZXIsICIucmRhLnh6IikpKQojI3JtZF9maWxlIDwtICIwM19leHByZXNzaW9uX2luZmVjdGlvbl8yMDE4MDgyMi5SbWQiCmBgYAoKIyBDYWxjdWxhdGluZyBlcnJvciByYXRlcy4KClRoaXMgZG9jdW1lbnQgaXMgaW50ZW5kZWQgdG8gc3RlcCB0aHJvdWdoIHRoZSBwcm9jZXNzIGZvbGxvd2VkIGluIHRoZXNlCmVzdGltYXRlcyBvZiBSVCBlcnJvciByYXRlcy4gIFRoaXMgUiBwYWNrYWdlIGlzIGEgY29tcGFuaW9uIHRvIHRoZSBzaW1pbGFybHkKbmFtZWQgJ2VycnJ0JyBQZXJsIHBhY2thZ2UsIHdoaWNoIGlzIHJlc3BvbnNpYmxlIGZvciBwcmVwcm9jZXNzaW5nIHRoZSByYXcKc2VxdWVuY2luZyBkYXRhLgoKIyBJbnRyb2R1Y3Rpb24KCkFuIG91dGxpbmUgb2YgdGhlIGV4cGVyaW1lbnRhbCBwcm9jZXNzIGlzIHJlcHJlc2VudGVkIGJ5OgoKIVtFeHBlcmltZW50YWwgRGVzaWduXShleHBlcmltZW50YWxfZGVzaWduLnN2ZykKClRoZSBlcnJydCBwYWNrYWdlIGlzIHJlc3BvbnNpYmxlIGZvciB0aGUgYm90dG9tIGFuZCByaWdodCBzaWRlIG9mIHRoZSBpbWFnZS4gIEl0IHRha2VzIHRoZQpyYXcgZmFzdHEgZGF0YSwgbWVyZ2VzIHRoZSByZWFkcyB1c2luZyBmbGFzaCwgd3JpdGVzIG91dCB0aGUgaW5kZXggYW5kCmlkZW50aWZpZXIgZm9yIGFsbCByZWFkcyBpZGVudGljYWwgdG8gdGhlIHRlbXBsYXRlLCB3cml0ZXMgb3V0IHRoZSBzZXF1ZW5jZXMgZm9yCnRoZSBvbmVzIG5vdCBpZGVudGljYWwgdG8gdGhlIHRlbXBsYXRlLCBhbGlnbnMgdGhlbSB3aXRoIGZhc3RhMzYgYWdhaW5zdCB0aGUKdGVtcGxhdGUsIGludGVycHJldHMgdGhlIGZhc3RhMzYgb3V0cHV0LCBhbmQgd3JpdGVzIGEgdGFibGUgb2YgdGhlIG1pc21hdGNoZXMsCmluc2VydGlvbnMsIGRlbGV0aW9ucyBmcm9tIGl0LgoKVGhpcyBwYWNrYWdlLCBjcmVhdGl2ZWx5IG5hbWVkICdlcnJSdCcgdGFrZXMgYSBzYW1wbGUgc2hlZXQgZGVzY3JpYmluZyB0aGUKc2FtcGxlcywgcmVhZHMgdGhlIHRhYmxlcyBvZiBpZGVudGljYWwgcmVhZHMvbWlzbWF0Y2hlcywgYW5kIGNyZWF0ZXMgbWF0cmljZXMKb2YgbXV0YXRpb24gcmF0ZXMgZm9yIHZhcmlvdXMgY2xhc3NlcyBvZiBtdXRhdGlvbnMuCgojIEluc3RhbGxhdGlvbgoKVGhlIHBlcmwgcGFja2FnZSBtYXkgYmUgaW5zdGFsbGVkIHZpYToKCmBgYHtiYXNoIGluc3RhbGxfZXJycnQsIGV2YWw9RkFMU0V9CmdpdCBjbG9uZSBodHRwczovL2dpdGh1Yi5jb20vYWJlbGV3L2VycnJ0CmNkIGVycnJ0CnBlcmwgQnVpbGQuUEwKcGVybCBCdWlsZCBpbnN0YWxsCmBgYAoKVGhlIFIgcGFja2FnZSBtYXkgYmUgaW5zdGFsbGVkIHZpYToKCmBgYHtyIGluc3RhbGxhdGlvbiwgZXZhbD1GQUxTRX0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJhYmVsZXcvZXJyUnQiKQpgYGAKCiMgUHJlcHJvY2Vzc2luZyBTdGVwcwoKSSBzcGxpdCB1cCB0aGUgcHJlcHJvY2Vzc2luZyBpbnRvIGRpc2NyZXRlIHN0ZXBzIHNvIHRoYXQgSSBjb3VsZCBjaGVjayBvbiBlYWNoCnBoYXNlIGFuZCB0cnkgdG8gZW5zdXJlIGNvcnJlY3RuZXNzLiAgVGhlIGVycnJ0IFBlcmwgcGFja2FnZSB3YXMgd3JpdHRlbiBzbyB0aGF0CnByZXR0eSBtdWNoIGV2ZXJ5dGhpbmcgbWF5IGJlIHNldCB2aWEgY29tbWFuZCBsaW5lIHBhcmFtZXRlcnMsIGJ1dCBhbGwgdGhlCmRlZmF1bHRzIHdlcmUgd3JpdHRlbiB0byBzdWl0ZSB0aGlzIGRhdGEgc2V0LiAgVGh1cyBJIGhhdmUgYSBwcmV0dHkgc2ltcGxlIGJhc2gKZm9yIGxvb3Agd2hpY2ggaGFuZGxlZCB0aGUgd29yayBvZiBwcmVwcm9jZXNzaW5nIHRoZSBkYXRhLgoKVGhlIGZvbGxvd2luZyBmcmFnbWVudCBvZiBiYXNoIHdhcyBydW4gaW5zaWRlIG15IGRpcmVjdG9yeSAqcHJlcHJvY2Vzc2luZy8qLAp3aGljaCBjb250YWluZWQgMyBzdWJkaXJlY3RvcmllczogKnMxLyosICpzMi8qLCBhbmQgKnMzLyogd2hpY2ggY29udGFpbiB0aGUKcmF3IHJlYWRzIGZvciB0aGUgMyBzYW1wbGVzLiAgVGhlIGV4Y2VsIHNwcmVhZHNoZWV0IGluCippbnN0L2V4dGRhdGEvYWxsX3NhbXBsZXMueGxzeCogZGVzY3JpYmVzIHRoZW0sIGJ1dCBzMSBpcyB0aGUgY29udHJvbCwgczIgaGFzCmxvdyBtYWduZXNpdW0sIGFuZCBzMyBoYXMgaGlnaC4KCmBgYHtiYXNoIHByZXByb2Nlc3NpbmcsIGV2YWw9RkFMU0V9CiMhL3Vzci9iaW4vZW52IGJhc2gKCiMjIFRoZSBkZWZhdWx0IG9wdGlvbnMgc2hvdWxkIHRha2UgY2FyZSBvZiBtb3N0IG9mIHdoYXQgdGhlc2UgcmVxdWlyZS4Kc3RhcnQ9JChwd2QpCmZvciBzYW1wbGUgaW4gJCgvYmluL2xzIC1kIHMqKTsgZG8KICAgIGNkICR7c2FtcGxlfQogICAgIyMgTWVyZ2UgdGhlIGZvcndhcmQgYW5kIHJldmVyc2UgcmVhZHMuCiAgICAuLi8uLi9lcnJydC9zY3JpcHQvbWVyZ2VfcmVhZHMucGwgLS1yZWFkMSAqXzEuZmFzdHEuZ3ogLS1yZWFkMiAqXzIuZmFzdHEuZ3oKICAgICMjIEV4dHJhY3QgdGhlIHJlYWRzIHdpdGggdGhlIHRlbXBsYXRlIHNlcXVlbmNlIGludG8gYSBmYXN0YSBmaWxlLgogICAgIyMgVGhpcyBhc3N1bWVzIGlucHV0IG5hbWVkICdzdGVwMS5leHRlbmRlZEZyYWdzLmZhc3RxLnh6JyBhbmQgb3V0cHV0IG5hbWVkICdzdGVwMi5mYXN0YScKICAgICMjIFRoaXMgb3V0cHV0IGlzIG5vdCBjb21wcmVzc2VkIGJlY2F1c2UgaXQgaXMgZWFzaWVyIHRvIGludm9rZSBmYXN0YTM2IG9uIHVuY29tcHJlc3NlZCBkYXRhLgogICAgIyMgVGh1cyBpdCBkZWZhdWx0cyB0byBzdGVwMi5mYXN0YSBhcyBvdXRwdXQuCiAgICAuLi8uLi9lcnJydC9zY3JpcHQvZXh0cmFjdF9mYXN0YS5wbAogICAgIyMgUnVuIGZhc3RhIGFnYWluc3QgdGhlIHRlbXBsYXRlIHNlcXVlbmNlLgogICAgIyMgVGhpcyBhc3N1bWVzIHN0ZXAyLmZhc3RhIGFzIGlucHV0IGFuZCBwcm92aWRlcyBzdGVwMy50eHQueHogYXMgb3V0cHV0LgogICAgLi4vLi4vZXJycnQvc2NyaXB0L3J1bl9mYXN0YS5wbAogICAgIyMgQm9pbCB0aGUgZmFzdGEgb3V0cHV0IGludG8gc29tZXRoaW5nIHdlIGNhbiByZWFkLgogICAgIyMgVGhpcyBhc3N1bWVzIHN0ZXAzLnR4dC54eiBhcyBpbnB1dCBhbmQgc3RlcDQudHh0Lnh6IGFzIG91dHB1dC4KICAgIC4uLy4uL2VycnJ0L3NjcmlwdC9wYXJzZV9mYXN0YS5wbAogICAgY2QgJHtzdGFydH0KZG9uZQpgYGAKCkFzIHRoZSBhYm92ZSBzaGVsbCBmcmFnbWVudCBzdGF0ZXMsIHRoZSBmaW5hbCBvdXRwdXRzIG9mIGVycnJ0IGFyZSAqc3RlcDQudHh0Lnh6KgphbmQgKnN0ZXAyX2lkZW50aWNhbF9yZWFkcy50eHQueHoqLiAgVGhlc2UgZmlsZW5hbWVzIGFyZSB0aGVyZWZvcmUgcmVjb3JkZWQgaW4KdGhlIHNhbXBsZSBzaGVldCBpbiB0aGUgY29sdW1ucyAnaWRlbnRfdGFibGUnIGFuZCAnbXV0YXRpb25fdGFibGUnIGZvciBlYWNoCnNhbXBsZS4KCkkgZGVjaWRlZCBmb3IgdGhlIG1vbWVudCBhdCBsZWFzdCB0byBjb3B5IHRoZXNlIGZpbGVzIGludG8gaW5zdC9leHRkYXRhIGFuZAptb2RpZnkgdGhlIHNhbXBsZSBzaGVldCBpbiBpbnN0L2V4dGRhdGEgdG8gcmVmZXIgdG8gdGhlbS4KCiMgUHJvY2Vzc2luZyB0aGUgZGF0YQoKSSB3cmFwcGVkIHRoZSB2YXJpb3VzIHN0ZXBzIHBlcmZvcm1lZCBieSBlcnJSdCBpbnRvIGEgcHJpbWFyeSBtYWluIGZ1bmN0aW9uLAoqY3JlYXRlX21hdHJpY2VzKCkqLiAgVGhlIGZvbGxvd2luZyBpcyB0aGUgaW52b2NhdGlvbiBJIHVzZWQgZm9yIHRoaXMgZGF0YSwKYWxvbmcgd2l0aCBzb21lIGV4cGxhbmF0aW9uIG9mIHdoYXQgaXMgaGFwcGVuaW5nLgoKIyMgR2V0dGluZyBTdGFydGVkCgpgYGB7ciBnZXR0aW5nX3N0YXJ0ZWR9CmxpYnJhcnkoZXJyUnQpCmJhc2VkaXIgPC0gc3lzdGVtLmZpbGUoImV4dGRhdGEiLCBwYWNrYWdlPSJlcnJSdCIpCm1ldGFkYXRhX2ZpbGUgPC0gZmlsZS5wYXRoKGJhc2VkaXIsICJhbGxfc2FtcGxlcy54bHN4IikKbWV0YWRhdGFfZmlsZQpzZXR3ZChiYXNlZGlyKQptZXRhIDwtIGhwZ2x0b29sczo6ZXh0cmFjdF9tZXRhZGF0YShtZXRhZGF0YV9maWxlKQpgYGAKCkhlcmUgaXMgdGhlIHJlc3VsdGluZyBleHBlcmltZW50YWwgZGVzaWduLiAgVGhlICpjcmVhdGVfbWF0cmljZXMoKSogZnVuY3Rpb24Kd2lsbCB1c2UgdGhpcyB0byByZWFkIHRoZSB0YWJsZXMgb2YgaWRlbnRpY2FsIHJlYWRzIGFuZCBtdXRhdGVkIHJlYWRzLgoKYGBge3IgbWV0YWRhdGEsIHJlc3VsdHM9J2FzaXMnfQprbml0cjo6a2FibGUobWV0YSkKYGBgCgojIyBDcmVhdGUgbWF0cmljZXMKClRoZSAqY3JlYXRlX21hdHJpY2VzKCkqIGZ1bmN0aW9uIGhhcyBhIGZldyBvcHRpb25zIHRvIGFkanVzdCB0aGUKc3RyaW5nZW5jeSBvZiB0aGUgdmFyaW91cyB3YXlzIHRvIGRlZmluZSBhbiBlcnJvci4gIEl0IGZ1bmN0aW9ucyB0byBjb3VudCB0aGUKdmFyaW91cyBlcnJvciB0eXBlcyB3aXRoICpxdWFudGlmeV9wYXJzZWQoKSosIGdhdGhlciB0aGVtIGludG8gbWF0cmljZXMgYnkKc2FtcGxlIGFsb25nIHdpdGggdGhlIHBlcmNlaXZlZCBlcnJvcnMgYnkgdGhlIHNlcXVlbmNlciwgYW5kIGNhbGN1bGF0ZSBhZ2dyZWdhdGUKZXJyb3IgcmF0ZXMgZm9yIHRoZSB2YXJpb3VzIGNsYXNzZXMgb2YgZXJyb3IuICBJbiBvdGhlciB3b3JkcywgaXQgZG9lcyBwcmV0dHkKbXVjaCBldmVyeXRoaW5nIGluIG9uZSBmdW5jdGlvbiBjYWxsLgoKYGBge3IgY3JlYXRlX21hdHJpY2VzfQplcnJvcl9kYXRhIDwtIGNyZWF0ZV9tYXRyaWNlcyhzYW1wbGVfc2hlZXQ9bWV0YWRhdGFfZmlsZSwgaWRlbnRfY29sdW1uPSJpZGVudHRhYmxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXV0X2NvbHVtbj0ibXV0YXRpb250YWJsZSIsIG1pbl9yZWFkcz0zLCBtaW5faW5kZXhlcz0zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fc2VxdWVuY2VyPTEwLCBtaW5fcG9zaXRpb249MjQsIHBydW5lX249VFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZT1UUlVFKQpgYGAKClRoZSBvcHRpb25zIGFyZSBleHBsYWluZWQgaW4gdGhlIGhlbHAgZG9jdW1lbnRhdGlvbiwgYnV0IGhlcmUgaXMgYW4gb3ZlcnZpZXcgb2YKdGhlIG9uZXMgd2hpY2ggbWlnaHQgbm90IGJlIHNlbGYtZXZpZGVudC4KCiogIG1pbl9yZWFkczogIEhvdyBtYW55IHJlYWRzL2luZGV4IGFyZSByZXF1aXJlZCBmb3IgYSBnaXZlbiBpbmRleCB0byBiZQogICBjb25zaWRlcmVkIHVzYWJsZT8gIFRoaXMgaXMgdGhlIGZpcnN0IHRvZ2dsZSBvZiBzdHJpbmdlbmN5IGFuZCBpcyBhZmZlY3RlZAogICBwcmltYXJpbHkgYnkgdGhlIHNlcXVlbmNpbmcgZGVwdGguCiogIG1pbl9pbmRleGVzOiAgSG93IG1hbnkgc2VwYXJhdGUgaW5kZXhlcyBhcmUgZXhwZWN0ZWQgdG8gYWdyZWUgb24gYSBnaXZlbgogICBtdXRhdGlvbiBpbiBvcmRlciBmb3IgaXQgdG8gYmUgY29uc2lkZXJlZCByZWFsPyAgVGhpcyB0b2dnbGUgb2Ygc3RyaW5nZW5jeSBpcwogICBwcmltYXJpbHkgYWZmZWN0ZWQgYnkgdGhlIHN1Y2Nlc3MgaW4gZ2V0dGluZyBhIGxhcmdlIG51bWJlciBvZiBkaWZmZXJlbnQgUlQKICAgcHJpbWVycyB0byBiaW5kIGFuZCBleHRlbmQgZHVyaW5nIHRoZSBlYXJseSBzdGFnZXMgb2YgY3JlYXRpbmcgdGhlIHNlcXVlbmNpbmcKICAgbGlicmFyaWVzLgoqICBtaW5fc2VxdWVuY2VyOiAgSW4gb3JkZXIgZm9yIGEgZ2l2ZW4gbXV0YXRpb24gdG8gYmUgZGVlbWVkCiAgICdzZXF1ZW5jZXItc3BlY2lmaWMnLCAxIGFuZCBvbmx5IDEgcmVhZCBvdXQgb2YgJ24nIGZvciBhIGdpdmVuIGluZGV4IG11c3QgaGF2ZQogICB0aGUgbXV0YXRpb24uICBUaGlzIHRvZ2dsZXMgJ24nLCBoaWdoZXIgbnVtYmVycyBhcmUgdGh1cyBtb3JlIHN0cmluZ2VudCBhbmQKICAgcmVxdWlyZSBncmVhdGVyIHNlcXVlbmNpbmcgZGVwdGguICBJZiB0aGlzIGlzIHNldCB0b28gbG93LCBhIHBvb2wgb2YKICAgZmFsc2UgcG9zaXRpdmVzIHdpbGwgcmVzdWx0OyB0aGUgZGVmYXVsdCBvZiAxMCBpcyBjb21wbGV0ZWx5IGFyYml0cmFyeS4KKiAgbWluX3Bvc2l0aW9uOiAgSW4gdGhlIGFjdHVhbCBleHBlcmltZW50LCBpdCBhcHBlYXJzIHRoYXQgdGhlIHByaW1lciB1c2VkIGZvcgogICB0aGUgZXhwZXJpbWVudGFsIHNhbXBsZXMgbWF5IGhhdmUgaGFkIGFuIGVycm9yLCBvciBpdCB3YXMgZGlmZmVyZW50bHkgY3V0IG91dAogICBvZiB0aGUgUEFHRSBwdXJpZmljYXRpb24gZ2VsLCBvciBzb21ldGhpbmcgZWxzZSBoYXBwZW5lZCB3aGljaCByZXN1bHRlZCBpbiBhCiAgIHRyZW1lbmRvdXMgcGVyY2VudGFnZSBvZiB0aGUgcmVhZHMgcHJlc2VudGluZyB0aGVtc2VsdmVzIGFzIGVpdGhlciBhIGRlbGV0aW9uCiAgIGF0IH4gcG9zaXRpb24gMjAsIG9yIGEgQSB0byBDIG11dGF0aW9uIGF0IHBvc2l0aW9uIDIyIG9yIHNvbWVzdWNoLiAgVGhpcwogICBwYXJhbWV0ZXIganVzdCBpZ25vcmVzIGFueSBtdXRhdGlvbnMgb2NjdXJpbmcgYmVmb3JlIHRoaXMgcG9zaXRpb24uICBXZQogICBvYnNlcnZlZCB0aGF0IHRoaXMgc3RyYW5nZSB0cmVuZCBzdG9wcGVkIGF0IHBvc2l0aW9uIDIyLCBhbmQgc28gc2V0IGl0IHRvIDI0CiAgIHRvIGJlIHNhZmUuCiogIHBydW5lX246ICBXaGVuIHRydWUsIHRoaXMgc2ltcGx5IGRyb3BzIGVudHJpZXMgaW4gdGhlIG11dGF0aW9uIHRhYmxlIHdoaWNoCiAgIGhhdmUgTnMuCgoqY3JlYXRlX21hdHJpY2VzKCkqIHdvcmtzIGluIDMgbWFpbiBzdGFnZXM6CgoxLiAgRm9yIGVhY2ggc2FtcGxlIGluIHRoZSBtZXRhZGF0YSwgaW52b2tlICpxdWFudGlmeV9wYXJzZWQoKSouICBUaGlzIGZ1bmN0aW9uCiAgICBpcyByZXNwb25zaWJsZSBmb3IgcmVhZGluZyB0aGUgdGFibGVzIGFuZCBjb2xsYXRpbmcgdGhlIHZhcmlvdXMgZXJyb3IKICAgIHR5cGVzLiAgVGhpcyBpcyB0aGUgbW9zdCBpbnRlcmVzdGluZyBwb3J0aW9uIG9mIGNvZGUgYW5kIG1vc3QgbGlrZWx5IHRvIGhhdmUKICAgIHByb2JsZW1zLgoyLiAgQ29sbGVjdCB0aGUgdmFyaW91cyB0YWJsZXMgZnJvbSBzdGVwIDEgYW5kIG1lcmdlIHRoZW0gaW50byAxIG1hdHJpeCBwZXIKICAgIHRhYmxlIHdoZXJlIHRoZSByb3dzIGFyZSBtdXRhdGlvbiB0eXBlcyBhbmQgY29sdW1ucyBhcmUgc2FtcGxlcy4KMy4gIENhbGN1bGF0ZSBlcnJvciByYXRlcyBieSByZWFkL2luZGV4IGFuZCB3cml0ZSBuZXcgdmVyc2lvbnMgb2YgdGhlIG1hdHJpY2VzCiAgICBmcm9tIHN0ZXAgMiBhY2NvcmRpbmdseS4KCldoaWxlICpjcmVhdGVfbWF0cmljZXMoKSogaXMgcnVubmluZywgaXQgc2hvdWxkIHByaW50IHNvbWUgaW5mb3JtYXRpb24KYWJvdXQgd2hhdCBpdCBpcyBkb2luZyBhbmQgZ2l2ZSBzb21lIGlkZWEgYWJvdXQgaG93IG11Y2ggZGF0YSBpcyBsb3N0IGR1ZSB0byB0aGUKc3RyaW5nZW5jeSBwYXJhbWV0ZXJzLiAgSXRzIHJlc3VsdCBpcyBhIGxpc3Q6CgoqICBzYW1wbGVzOiBUaGUgcmVzdWx0IGZyb20gKnF1YW50aWZ5X3BhcnNlZCgpKiBmb3IgZWFjaCBzYW1wbGUgaW4gdGhlIG1ldGFkYXRhLgoqICByZWFkc19wZXJfc2FtcGxlOiBUaGUgc3VtIG9mIHRoZSByZWFkcyBvYnNlcnZlZCBpbiB0aGUgbXV0YXRpb24gZGF0YSBhbmQgdGhlCiAgIGlkZW50aXR5IGRhdGEsIGUuZy4gaG93IG1hbnkgcmVhZHMgc3Vydml2ZWQuCiogIGluZGV4ZXNfcGVyX3NhbXBsZTogVGhlIHN1bSBvZiB0aGUgaW5kZXhlcyBvYnNlcnZlZC4gIFRoaXMgaXMgb3VyIHByaW1hcnkKICAgbm9ybWFsaXphdGlvbiBmYWN0b3IsIGJ1dCBhbG9uZyB3aXRoIHJlYWRzX3Blcl9zYW1wbGUgc2hvdWxkIGhlbHAgZGVmaW5lIGEKICAgaGVhbHRoeSB2cy4gcHJvYmxlbWF0aWMgc2FtcGxlLgoqICBtYXRyaWNlczogVGhlICdyYXcnIG1hdHJpY2VzIGZvciBlYWNoIHRhYmxlLgoqICBtYXRyaWNlc19ieV9jb3VudHM6IFRoZSBtYXRyaWNlcyBkaXZpZGllZCBieSB0aGUgaW5kZXhlcy4KKiAgbm9ybWFsaXplZDogVGhlICdyYXcnIG1hdHJpY2VzIGFzIGNwbSwgdGh1cyBub3JtYWxpemVkIGJ5IGxpYnJhcnkgc2l6ZXMuCiogIG5vcm1hbGl6ZWRfYnlfY291bnRzOiBUaGUgbWF0cmljZXMgYXMgY3BtIGFuZCBkaXZpZGllZCBieSBpbmRleGVzLgoKVGh1cywgYXMgb25lIG1pZ2h0IGltYWdpbmUsIHRoZXJlIGFyZSBhIF9sb3RfIG9mIG1hdHJpY2VzIHJldHVybmVkLCBoZXJlIGFyZQpzb21lIGV4YW1wbGVzIG9mIHRoZSBkYXRhIGFmdGVyIG5vcm1hbGl6YXRpb24sIGluIGVhY2ggY2FzZSBJIHdpbGwgZmlyc3QgcHJpbnQKdGhlIGRhdGEgZm9yIHRoZSBSVCBtdXRhdGlvbnMgZm9sbG93ZWQgYnkgdGhlIHNlcXVlbmNlcjoKCjEuICBNaXNtYXRjaGVzIGJ5IHBvc2l0aW9uIGFzIGEgaGVhdG1hcDogVGhpcyBpcyBxdWl0ZSBjdXJpb3VzLCBhcyB3ZSBzZWUKICAgIGFwcGFyZW50bHkgZmV3ZXIgdmFyaWFudHMgYXQgdGhlIGVuZCBvZiB0aGUgc2VxdWVuY2UuCgpgYGB7ciBtaXNtYXRjaGVzX2J5X3Bvc2l0aW9ufQpncGxvdHM6OmhlYXRtYXAuMihlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX2luZGV4ZXNfYnlfcG9zaXRpb24iXV0sCiAgICAgICAgICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgICAgICAgICAgUm93dj1GQUxTRSkKZ3Bsb3RzOjpoZWF0bWFwLjIoZXJyb3JfZGF0YVtbIm5vcm1hbGl6ZWRfYnlfY291bnRzIl1dW1sibWlzc19zZXF1ZW5jZXJfYnlfcG9zaXRpb24iXV0sCiAgICAgICAgICAgICAgICAgIHRyYWNlPSJub25lIiwKICAgICAgICAgICAgICAgICAgUm93dj1GQUxTRSkKYGBgCgoyLiAgTWlzbWF0Y2hlcyBieSByZWZlcmVuY2UgbnVjbGVvdGlkZQoKYGBge3IgcmVmZXJlbmNlX251Y2xlb3RpZGVzLCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3NfaW5kZXhlc19ieV9yZWZfbnQiXV0pCmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX3NlcXVlbmNlcl9ieV9yZWZfbnQiXV0pCmBgYAoKMy4gIE1pc21hdGNoZXMgYnkgbXV0YXRlZCBudWNsZW90aWRlCgpgYGB7ciBtdXRhdGVkX251Y2xlb3RpZGVzLCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3NfaW5kZXhlc19ieV9oaXRfbnQiXV0pCmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX3NlcXVlbmNlcl9ieV9oaXRfbnQiXV0pCmBgYAoKNC4gIE1pc21hdGNoZXMgYnkgdHlwZTogQSB0byBDLCBHIHRvIEEgZXRjIGV0YwoKYGBge3IgdHlwZV9udWNsZW90aWRlcywgcmVzdWx0cz0nYXNpcyd9CmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX2luZGV4ZXNfYnlfdHlwZSJdXSkKa25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3Nfc2VxdWVuY2VyX2J5X3R5cGUiXV0pCmBgYAoKNS4gIE1pc21hdGNoZXMgYnkgdHJhbnNpdGlvbi90cmFuc3ZlcnNpb24KCmBgYHtyIHRyYW5zX251Y2xlb3RpZGVzLCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3NfaW5kZXhlc19ieV90cmFucyJdXSkKa25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbIm1pc3Nfc2VxdWVuY2VyX2J5X3RyYW5zIl1dKQpgYGAKCjYuICBNaXNtYXRjaGVzIGJ5IHN0cm9uZy93ZWFrCgpgYGB7ciBzdHJlbmd0aF9udWNsZW90aWRlcywgcmVzdWx0cz0nYXNpcyd9CmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX2luZGV4ZXNfYnlfc3RyZW5ndGgiXV0pCmtuaXRyOjprYWJsZShlcnJvcl9kYXRhW1sibm9ybWFsaXplZF9ieV9jb3VudHMiXV1bWyJtaXNzX3NlcXVlbmNlcl9ieV9zdHJlbmd0aCJdXSkKYGBgCgo3LiAgSW5zZXJ0aW9ucyBieSBwb3NpdGlvbgoKYGBge3IgaW5zZXJ0X3Bvc2l0aW9uLCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbImluc2VydF9pbmRleGVzX2J5X3Bvc2l0aW9uIl1dKQprbml0cjo6a2FibGUoZXJyb3JfZGF0YVtbIm5vcm1hbGl6ZWRfYnlfY291bnRzIl1dW1siaW5zZXJ0X3NlcXVlbmNlcl9ieV9wb3NpdGlvbiJdXSkKYGBgCgo4LiAgSW5zZXJ0aW9ucyBieSBudAoKYGBge3IgaW5zZXJ0X250LCByZXN1bHRzPSdhc2lzJ30Ka25pdHI6OmthYmxlKGVycm9yX2RhdGFbWyJub3JtYWxpemVkX2J5X2NvdW50cyJdXVtbImluc2VydF9pbmRleGVzX2J5X250Il1dKQprbml0cjo6a2FibGUoZXJyb3JfZGF0YVtbIm5vcm1hbGl6ZWRfYnlfY291bnRzIl1dW1siaW5zZXJ0X3NlcXVlbmNlcl9ieV9udCJdXSkKYGBgCgo5LiAgRGVsZXRpb25zIGJ5IHBvc2l0aW9uCgpUaGVyZSB3YXMgaW5zdWZmaWNpZW50IGRhdGEgdG8gcXVhbnRpZnkgdGhlc2UuCg==