Introduction

Now we all have the chance to use Eric’s R-package whoa to estimate heterozygote miscall rates from GBS data stored in VCF files.

This is a small, lightweight package that lets users investigate the distribution of genotypes in genotype-by-sequencing (GBS) data where they expect (by and large) Hardy-Weinberg equilibrium, in order to assess rates of genotyping errors and the dependence of those rates on read depth.

The name comes from the bolded letters in this sentence:

Where’s my Heterozygotes at? Observations on genotyping Accuracy.

It also fits well with Eric’s reaction when he started investigating heterozygote miscall rates (rates at which true heterozygotes are incorrectly called as homozygotes) in some RAD-seq data sets—His eyes bugged out and he said, “Whoa!”

Installing whoa

Unfortunately, CRAN was shuttered this last week, so we were not able to get this package onto CRAN. So, there are a few options:

Option 1: From github

If you have all the tools to compile R packages that use C++ code on your computer then this option is fine.

devtools::install_github(repo = "eriqande/whoa")

Option 2: Precompiled versions for Mac and Windows

If you are working on a Linux machine than you probably have all the tools for compiling packages with C++. However, not all Mac and Windows users will have that capacity, so I have some precompiled binary packages for those users.

First, you will have to ensure that all the dependencies (necessary other packages) are also installed. If you think you don’t have the packages, you can install them easily with:

install.packages(c("tidyverse", "Rcpp", "vcfR", "viridis"))

Then, depending on if you are a Mac or a Windows user there are two further options.

Installing the binary for Mac OS X

# download the package 
download.file("https://www.dropbox.com/s/cnc6rfqe8afp8fd/whoa_0.0.1.tgz?dl=1", 
              destfile = "whoa_0.0.1.tgz")

# then install the package
install.packages("whoa_0.0.1.tgz", repos = NULL)

Installing the binary for Windows

# download the package 
download.file("https://www.dropbox.com/s/f48h75oft1rlzr8/whoa_0.0.1.zip?dl=1", 
              destfile = "whoa_0.0.1.zip")

# then install the package
install.packages("whoa_0.0.1.zip", repos = NULL)

A first run through

The package comes with a small bit of data from lobster to play with. The rest of this document shows a quick run through a few of the functions to do an analysis of a data set.

Packages

# load up the package:
library(whoa)

Lobster data

Read about the lobster data here. Execute this if you want:

help("lobster_buz_2000")

The main thing to know is that it is a vcfR object. You can make such an object yourself by reading in a VCF file using vcfR::read.vcfR().

Make a quick genotype frequency scatter plot

# first get compute expected and observed genotype frequencies
gfreqs <- exp_and_obs_geno_freqs(lobster_buz_2000)
# then plot those.  Set max_plot_loci so that all 2000
# loci will be plotted
geno_freqs_scatter(gfreqs, max_plot_loci = 2000)

Now infer an overall heterozygote miscall rate.

If we want to estimate the het miscall rate (over all read depth bins) we just set the minimum bin size to a very large value so it make just one bin:

overall <- infer_m(lobster_buz_2000, minBin = 1e15)
Preparing data structures for MCMC
Running MCMC
Tidying output

Now look at that:

overall$m_posteriors

Wow! (Or should we say “WHOA!”) A het miscall rate of around 25%.

Now infer a miscall rate for read depth bins

See the total_n above is about 65,000. That means 65,000 genotypes. (2000 loci typed at 36 individuals, with some missing data).
We will bin those up so that there are at least 2000 genotypes in each bin and then estimate the het miscall rate for each read depth bin.

binned <- infer_m(lobster_buz_2000, minBin = 2000)
Preparing data structures for MCMC
Running MCMC
Tidying output

And then we can plot the posterior mean and CIs for each read depth bin.

posteriors_plot(binned$m_posteriors)

Again, WHOA! The het miscall rate at low read depths is super high!

Another analysis from a VCF file

Now, let’s look at doing this while beginning with a VCF file.

For those that aren’t familiar with VCF files (that stands for Variant Call Format), you will want to get to know the format well. It is one of the main standards for storing information about SNPs (and other variants) that were obtained from sequencing. It allows the storage of extra information like variant quality scores and read depths. You can read more about VCF formats on Wikipedia.

Download a VCF file

We can get the red-drum data like this:

download.file("https://www.dropbox.com/s/twyylsui15q65yj/red_drum_Final_Filtered_SNPs.vcf.gz?dl=1",
              destfile = "red_drum_Final_Filtered_SNPs.vcf.gz")

This is a gzipped file, so it is a little hard to look at. Here is what the first 20 lines of the file look like:

gzcat red_drum_Final_Filtered_SNPs.vcf.gz | head -n 20
##fileformat=VCFv4.1
##source=freeBayes v0.9.20
##reference=reference.fasta
##phasing=none
##filter="AB > 0.25 | AB < 0.01 genotypes filtered with: QR > 0 | QA > 0 "
##filter="QUAL / DP > 0.2"
##filter="MQM / MQMR > 0.25 & MQM / MQMR < 1.75"
##filter="SAF / SAR > 100 & SRF / SRR > 100 | SAR / SAF > 100 & SRR / SRF > 50"
##filter="PAIRED > 0.05 & PAIREDR > 0.05 & PAIREDR / PAIRED < 1.75 & PAIREDR / PAIRED > 0.25 | PAIRED < 0.05 & PAIREDR < 0.05"
##INFO=<ID=NS,Number=1,Type=Integer,Description="Number of samples with data">
##INFO=<ID=DP,Number=1,Type=Integer,Description="Total read depth at the locus">
##INFO=<ID=DPB,Number=1,Type=Float,Description="Total read depth per bp at the locus; bases in reads overlapping / bases in haplotype">
##INFO=<ID=AC,Number=A,Type=Integer,Description="Total number of alternate alleles in called genotypes">
##INFO=<ID=AN,Number=1,Type=Integer,Description="Total number of alleles in called genotypes">
##INFO=<ID=AF,Number=A,Type=Float,Description="Estimated allele frequency in the range (0,1]">
##INFO=<ID=RO,Number=1,Type=Integer,Description="Reference allele observation count, with partial observations recorded fractionally">
##INFO=<ID=AO,Number=A,Type=Integer,Description="Alternate allele observations, with partial observations recorded fractionally">
##INFO=<ID=PRO,Number=1,Type=Float,Description="Reference allele observation count, with partial observations recorded fractionally">
##INFO=<ID=PAO,Number=A,Type=Float,Description="Alternate allele observations, with partial observations recorded fractionally">
##INFO=<ID=QR,Number=1,Type=Integer,Description="Reference allele quality sum in phred">
gzcat: error writing to output: Broken pipe
gzcat: red_drum_Final_Filtered_SNPs.vcf.gz: uncompress failed

Read in the VCF file

We use read.vcfR() from the vcfR package for this

drum_vcf <- vcfR::read.vcfR("red_drum_Final_Filtered_SNPs.vcf.gz")
Scanning file to determine attributes.
File attributes:
  meta lines: 59
  header_line: 60
  variant count: 7382
  column count: 214

Meta line 59 read in.
All meta lines processed.
gt matrix initialized.
Character matrix gt created.
  Character matrix gt rows: 7382
  Character matrix gt cols: 214
  skip: 0
  nrows: 7382
  row_num: 0

Processed variant 1000
Processed variant 2000
Processed variant 3000
Processed variant 4000
Processed variant 5000
Processed variant 6000
Processed variant 7000
Processed variant: 7382
All variants processed

Analysis

From this point on, its just the same as we did with the lobster data:

# first get compute expected and observed genotype frequencies
drumfreqs <- exp_and_obs_geno_freqs(drum_vcf)
# then plot those.  Set max_plot_loci so that all 2000
# loci will be plotted
geno_freqs_scatter(drumfreqs, max_plot_loci = 5000)

That looks a lot better…

Now, how about an overall het miscall rate?

drum_overall <- infer_m(drum_vcf, minBin = 1e15)
Preparing data structures for MCMC
Running MCMC
Tidying output
drum_overall$m_posteriors

That takes a bit longer (more individuals), but the results are good—about a 5% het miscall rate.

But note that the miscall rate is clearly higher at lower read depths.

First, check how many individuals and how many loci we have here:

dim(drum_vcf@gt)
[1] 7382  206

So, 7382 loci and 205 individuals. That means close to 1.5 million genotypes. So, if we want to break that up into read depth bins, we could put 50,000 in each bin and still have a large number of bins:

drum_binned <- infer_m(drum_vcf, minBin = 50000)
Preparing data structures for MCMC
Running MCMC
Tidying output
posteriors_plot(drum_binned$m_posteriors)

And we see a clear trend there.

Now, please use your own data set

If you have RAD data in vcf format lying around, please run it through whoa!

Note that if you have multiple, genetically distinct populations in your VCF, you can select individuals from just one population by indexing them out of the vcfR object. For example, if we wanted only a subset of samples from the drum VCF file we could do like this:

sams <- c("AR_001", 
          "AR_003",
          "AR_004",
          "AR_005",
          "AR_008",
          "AR_010",
          "AR_012",
          "AR_014",
          "AR_015",
          "AR_016")
drum_subset <- drum_vcf[, c("FORMAT", sams)]
# check the dimensions of the genotypes in that object:
dim(drum_subset@gt)
[1] 7382   11

Notice how you have to include the column “FORMAT” when you index the object. This is critical—that is the column that tells you how all the auxillary information that comes with the genotypes (like read depths and quality scores) is formatted.

LS0tCnRpdGxlOiAiSGFuZHMgT24gU2Vzc2lvbjogRXhwbG9yaW5nIGdlbm90eXBpbmcgZXJyb3Igd2l0aCBSIHBhY2thZ2UgJ3dob2EnIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKYXV0aG9yOiAiRXJpYyBDLiBBbmRlcnNvbiIKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCk5vdyB3ZSBhbGwgaGF2ZSB0aGUgY2hhbmNlIHRvIHVzZSBFcmljJ3MgUi1wYWNrYWdlIGB3aG9hYCB0byBlc3RpbWF0ZQpoZXRlcm96eWdvdGUgbWlzY2FsbCByYXRlcyBmcm9tIEdCUyBkYXRhIHN0b3JlZCBpbiBWQ0YgZmlsZXMuICAKClRoaXMgaXMgYSBzbWFsbCwgbGlnaHR3ZWlnaHQgcGFja2FnZSB0aGF0IGxldHMgdXNlcnMgaW52ZXN0aWdhdGUgdGhlCmRpc3RyaWJ1dGlvbiBvZiBnZW5vdHlwZXMgaW4gZ2Vub3R5cGUtYnktc2VxdWVuY2luZyAoR0JTKSBkYXRhIHdoZXJlIHRoZXkgZXhwZWN0IChieSBhbmQgbGFyZ2UpCkhhcmR5LVdlaW5iZXJnIGVxdWlsaWJyaXVtLCBpbiBvcmRlciB0byBhc3Nlc3MgcmF0ZXMgb2YgZ2Vub3R5cGluZyBlcnJvcnMgYW5kCnRoZSBkZXBlbmRlbmNlIG9mIHRob3NlIHJhdGVzIG9uIHJlYWQgZGVwdGguCgpUaGUgbmFtZSBjb21lcyBmcm9tIHRoZSBib2xkZWQgbGV0dGVycyBpbiB0aGlzIHNlbnRlbmNlOgoKKipXKipoZXJlJ3MgbXkgKipIKipldGVyb3p5Z290ZXMgYXQ/ICAqKk8qKmJzZXJ2YXRpb25zIG9uIGdlbm90eXBpbmcgKipBKipjY3VyYWN5LgoKSXQgYWxzbyBmaXRzIHdlbGwgd2l0aCBFcmljJ3MgcmVhY3Rpb24gd2hlbiBoZSBzdGFydGVkIGludmVzdGlnYXRpbmcgCmhldGVyb3p5Z290ZSBtaXNjYWxsIHJhdGVzIChyYXRlcyBhdCB3aGljaCB0cnVlIGhldGVyb3p5Z290ZXMgYXJlCmluY29ycmVjdGx5IGNhbGxlZCBhcyBob21venlnb3RlcykgaW4gc29tZSBSQUQtc2VxIGRhdGEgc2V0cy0tLUhpcyBleWVzCmJ1Z2dlZCBvdXQgYW5kIGhlIHNhaWQsICJXaG9hISIKCiMjIEluc3RhbGxpbmcgYHdob2FgCgpVbmZvcnR1bmF0ZWx5LCBDUkFOIHdhcyBzaHV0dGVyZWQgdGhpcyBsYXN0IHdlZWssIHNvIHdlIHdlcmUgbm90IGFibGUgdG8gZ2V0CnRoaXMgcGFja2FnZSBvbnRvIENSQU4uICBTbywgdGhlcmUgYXJlIGEgZmV3IG9wdGlvbnM6CgojIyMgT3B0aW9uIDE6IEZyb20gZ2l0aHViCgpJZiB5b3UgaGF2ZSBhbGwgdGhlIHRvb2xzIHRvIGNvbXBpbGUgUiBwYWNrYWdlcyB0aGF0IHVzZSBDKysgY29kZSBvbiB5b3VyIGNvbXB1dGVyCnRoZW4gdGhpcyBvcHRpb24gaXMgZmluZS4KYGBge3IsIGV2YWwgPSBGQUxTRX0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKHJlcG8gPSAiZXJpcWFuZGUvd2hvYSIpCmBgYAoKIyMjIE9wdGlvbiAyOiBQcmVjb21waWxlZCB2ZXJzaW9ucyBmb3IgTWFjIGFuZCBXaW5kb3dzCgpJZiB5b3UgYXJlIHdvcmtpbmcgb24gYSBMaW51eCBtYWNoaW5lIHRoYW4geW91IHByb2JhYmx5IGhhdmUgYWxsIHRoZQp0b29scyBmb3IgY29tcGlsaW5nIHBhY2thZ2VzIHdpdGggQysrLiAgSG93ZXZlciwgbm90IGFsbCBNYWMgYW5kIFdpbmRvd3MKdXNlcnMgd2lsbCBoYXZlIHRoYXQgY2FwYWNpdHksIHNvIEkgaGF2ZSBzb21lIHByZWNvbXBpbGVkIGJpbmFyeSBwYWNrYWdlcwpmb3IgdGhvc2UgdXNlcnMuIAoKRmlyc3QsIHlvdSB3aWxsIGhhdmUgdG8gZW5zdXJlIHRoYXQgYWxsIHRoZSBkZXBlbmRlbmNpZXMgKG5lY2Vzc2FyeSBvdGhlciBwYWNrYWdlcykgCmFyZSBhbHNvIGluc3RhbGxlZC4gSWYgeW91IHRoaW5rIHlvdSBkb24ndCBoYXZlIHRoZSBwYWNrYWdlcywgeW91IGNhbiAKaW5zdGFsbCB0aGVtIGVhc2lseSB3aXRoOgpgYGB7ciwgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcyhjKCJ0aWR5dmVyc2UiLCAiUmNwcCIsICJ2Y2ZSIiwgInZpcmlkaXMiKSkKYGBgCgpUaGVuLCBkZXBlbmRpbmcgb24gaWYgeW91IGFyZSBhIE1hYyBvciBhIFdpbmRvd3MgdXNlciB0aGVyZSBhcmUgdHdvCmZ1cnRoZXIgb3B0aW9ucy4KCiMjIyMgSW5zdGFsbGluZyB0aGUgYmluYXJ5IGZvciBNYWMgT1MgWApgYGB7ciwgZXZhbD1GQUxTRX0KIyBkb3dubG9hZCB0aGUgcGFja2FnZSAKZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy9jbmM2cmZxZThhZnA4ZmQvd2hvYV8wLjAuMS50Z3o/ZGw9MSIsIAogICAgICAgICAgICAgIGRlc3RmaWxlID0gIndob2FfMC4wLjEudGd6IikKCiMgdGhlbiBpbnN0YWxsIHRoZSBwYWNrYWdlCmluc3RhbGwucGFja2FnZXMoIndob2FfMC4wLjEudGd6IiwgcmVwb3MgPSBOVUxMKQpgYGAKCgojIyMjIEluc3RhbGxpbmcgdGhlIGJpbmFyeSBmb3IgV2luZG93cwpgYGB7ciwgZXZhbD1GQUxTRX0KIyBkb3dubG9hZCB0aGUgcGFja2FnZSAKZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy9mNDhoNzVvZnQxcmx6cjgvd2hvYV8wLjAuMS56aXA/ZGw9MSIsIAogICAgICAgICAgICAgIGRlc3RmaWxlID0gIndob2FfMC4wLjEuemlwIikKCiMgdGhlbiBpbnN0YWxsIHRoZSBwYWNrYWdlCmluc3RhbGwucGFja2FnZXMoIndob2FfMC4wLjEuemlwIiwgcmVwb3MgPSBOVUxMKQpgYGAKCgojIyBBIGZpcnN0IHJ1biB0aHJvdWdoCgpUaGUgcGFja2FnZSBjb21lcyB3aXRoIGEgc21hbGwgYml0IG9mIGRhdGEgZnJvbSBsb2JzdGVyIHRvIHBsYXkgd2l0aC4gIFRoZSByZXN0IG9mIAp0aGlzIGRvY3VtZW50IHNob3dzIGEgcXVpY2sgcnVuIHRocm91Z2ggYSBmZXcgb2YgdGhlIGZ1bmN0aW9ucyB0byBkbyBhbiAKYW5hbHlzaXMgb2YgYSBkYXRhIHNldC4KCgojIyMgUGFja2FnZXMKCmBgYHtyfQojIGxvYWQgdXAgdGhlIHBhY2thZ2U6CmxpYnJhcnkod2hvYSkKCmBgYAoKIyMjIExvYnN0ZXIgZGF0YQoKUmVhZCBhYm91dCB0aGUgbG9ic3RlciBkYXRhIGhlcmUuIEV4ZWN1dGUgdGhpcyBpZiB5b3Ugd2FudDoKYGBge3IsIGV2YWw9RkFMU0V9CmhlbHAoImxvYnN0ZXJfYnV6XzIwMDAiKQpgYGAKVGhlIG1haW4gdGhpbmcgdG8ga25vdyBpcyB0aGF0IGl0IGlzIGEgdmNmUiBvYmplY3QuICBZb3UgY2FuIAptYWtlIHN1Y2ggYW4gb2JqZWN0IHlvdXJzZWxmIGJ5IHJlYWRpbmcgaW4gYSBWQ0YgZmlsZSAKdXNpbmcgYHZjZlI6OnJlYWQudmNmUigpYC4KCiMjIyBNYWtlIGEgcXVpY2sgZ2Vub3R5cGUgZnJlcXVlbmN5IHNjYXR0ZXIgcGxvdAoKYGBge3J9CiMgZmlyc3QgZ2V0IGNvbXB1dGUgZXhwZWN0ZWQgYW5kIG9ic2VydmVkIGdlbm90eXBlIGZyZXF1ZW5jaWVzCmdmcmVxcyA8LSBleHBfYW5kX29ic19nZW5vX2ZyZXFzKGxvYnN0ZXJfYnV6XzIwMDApCgojIHRoZW4gcGxvdCB0aG9zZS4gIFNldCBtYXhfcGxvdF9sb2NpIHNvIHRoYXQgYWxsIDIwMDAKIyBsb2NpIHdpbGwgYmUgcGxvdHRlZApnZW5vX2ZyZXFzX3NjYXR0ZXIoZ2ZyZXFzLCBtYXhfcGxvdF9sb2NpID0gMjAwMCkKYGBgCgojIyMgTm93IGluZmVyIGFuIG92ZXJhbGwgaGV0ZXJvenlnb3RlIG1pc2NhbGwgcmF0ZS4KCklmIHdlIHdhbnQgdG8gZXN0aW1hdGUgdGhlIGhldCBtaXNjYWxsIHJhdGUgKG92ZXIgYWxsIHJlYWQgZGVwdGggYmlucykKd2UganVzdCBzZXQgdGhlIG1pbmltdW0gYmluIHNpemUgdG8gYSB2ZXJ5IGxhcmdlIHZhbHVlIHNvIGl0IG1ha2UganVzdCBvbmUgYmluOgpgYGB7cn0Kb3ZlcmFsbCA8LSBpbmZlcl9tKGxvYnN0ZXJfYnV6XzIwMDAsIG1pbkJpbiA9IDFlMTUpCmBgYApOb3cgbG9vayBhdCB0aGF0OgpgYGB7cn0Kb3ZlcmFsbCRtX3Bvc3RlcmlvcnMKYGBgCgpXb3chIChPciBzaG91bGQgd2Ugc2F5ICJXSE9BISIpIEEgaGV0IG1pc2NhbGwgcmF0ZSBvZiBhcm91bmQgMjUlLgoKIyMjIE5vdyBpbmZlciBhIG1pc2NhbGwgcmF0ZSBmb3IgcmVhZCBkZXB0aCBiaW5zCgpTZWUgdGhlIHRvdGFsX24gYWJvdmUgaXMgYWJvdXQgNjUsMDAwLiAgVGhhdCBtZWFucyA2NSwwMDAgZ2Vub3R5cGVzLiAKKDIwMDAgbG9jaSB0eXBlZCBhdCAzNiBpbmRpdmlkdWFscywgd2l0aCBzb21lIG1pc3NpbmcgZGF0YSkuICAKV2Ugd2lsbCBiaW4gdGhvc2UgdXAgc28gdGhhdCB0aGVyZSBhcmUgYXQgbGVhc3QgMjAwMCBnZW5vdHlwZXMgaW4gZWFjaCAKYmluIGFuZCB0aGVuIGVzdGltYXRlIHRoZSBoZXQgbWlzY2FsbCByYXRlIGZvciBlYWNoIHJlYWQgZGVwdGggYmluLgoKYGBge3J9CmJpbm5lZCA8LSBpbmZlcl9tKGxvYnN0ZXJfYnV6XzIwMDAsIG1pbkJpbiA9IDIwMDApCmBgYAoKQW5kIHRoZW4gd2UgY2FuIHBsb3QgdGhlIHBvc3RlcmlvciBtZWFuIGFuZCBDSXMgZm9yIGVhY2ggcmVhZCBkZXB0aCBiaW4uCmBgYHtyfQpwb3N0ZXJpb3JzX3Bsb3QoYmlubmVkJG1fcG9zdGVyaW9ycykKYGBgCgpBZ2FpbiwgV0hPQSEgIFRoZSBoZXQgbWlzY2FsbCByYXRlIGF0IGxvdyByZWFkIGRlcHRocyBpcyBzdXBlciBoaWdoIQoKCiMjIEFub3RoZXIgYW5hbHlzaXMgZnJvbSBhIFZDRiBmaWxlCgpOb3csIGxldCdzIGxvb2sgYXQgZG9pbmcgdGhpcyB3aGlsZSBiZWdpbm5pbmcgd2l0aCBhIFZDRiBmaWxlLgoKRm9yIHRob3NlIHRoYXQgYXJlbid0IGZhbWlsaWFyIHdpdGggVkNGIGZpbGVzICh0aGF0IHN0YW5kcyBmb3IgVmFyaWFudApDYWxsIEZvcm1hdCksIHlvdSB3aWxsIHdhbnQgdG8gZ2V0IHRvIGtub3cgdGhlIGZvcm1hdCB3ZWxsLiAgSXQgaXMgb25lIG9mIHRoZQptYWluIHN0YW5kYXJkcyBmb3Igc3RvcmluZyBpbmZvcm1hdGlvbiBhYm91dCBTTlBzIChhbmQgb3RoZXIgdmFyaWFudHMpIAp0aGF0IHdlcmUgb2J0YWluZWQgZnJvbSBzZXF1ZW5jaW5nLiAgSXQgYWxsb3dzIHRoZSBzdG9yYWdlIG9mIGV4dHJhIGluZm9ybWF0aW9uCmxpa2UgdmFyaWFudCBxdWFsaXR5IHNjb3JlcyBhbmQgcmVhZCBkZXB0aHMuICBZb3UgY2FuIHJlYWQgbW9yZSBhYm91dCBWQ0YgZm9ybWF0cwpbb24gV2lraXBlZGlhXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9WYXJpYW50X0NhbGxfRm9ybWF0KS4KCiMjIyBEb3dubG9hZCBhIFZDRiBmaWxlCgpXZSBjYW4gZ2V0IHRoZSByZWQtZHJ1bSBkYXRhIGxpa2UgdGhpczoKYGBge3IsIGV2YWw9RkFMU0V9CmRvd25sb2FkLmZpbGUoImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvdHd5eWxzdWkxNXE2NXlqL3JlZF9kcnVtX0ZpbmFsX0ZpbHRlcmVkX1NOUHMudmNmLmd6P2RsPTEiLAogICAgICAgICAgICAgIGRlc3RmaWxlID0gInJlZF9kcnVtX0ZpbmFsX0ZpbHRlcmVkX1NOUHMudmNmLmd6IikKYGBgCgpUaGlzIGlzIGEgZ3ppcHBlZCBmaWxlLCBzbyBpdCBpcyBhIGxpdHRsZSBoYXJkIHRvIGxvb2sgYXQuICBIZXJlIGlzIHdoYXQgdGhlIGZpcnN0CjIwIGxpbmVzIG9mIHRoZSBmaWxlIGxvb2sgbGlrZToKYGBge3NofQpnemNhdCByZWRfZHJ1bV9GaW5hbF9GaWx0ZXJlZF9TTlBzLnZjZi5neiB8IGhlYWQgLW4gMjAKYGBgCgojIyMgUmVhZCBpbiB0aGUgVkNGIGZpbGUKCldlIHVzZSBgcmVhZC52Y2ZSKClgIGZyb20gdGhlIHZjZlIgcGFja2FnZSBmb3IgdGhpcwpgYGB7cn0KZHJ1bV92Y2YgPC0gdmNmUjo6cmVhZC52Y2ZSKCJyZWRfZHJ1bV9GaW5hbF9GaWx0ZXJlZF9TTlBzLnZjZi5neiIpCmBgYAoKIyMjIEFuYWx5c2lzCgpGcm9tIHRoaXMgcG9pbnQgb24sIGl0cyBqdXN0IHRoZSBzYW1lIGFzIHdlIGRpZCB3aXRoIHRoZSBsb2JzdGVyIGRhdGE6CmBgYHtyfQojIGZpcnN0IGdldCBjb21wdXRlIGV4cGVjdGVkIGFuZCBvYnNlcnZlZCBnZW5vdHlwZSBmcmVxdWVuY2llcwpkcnVtZnJlcXMgPC0gZXhwX2FuZF9vYnNfZ2Vub19mcmVxcyhkcnVtX3ZjZikKCiMgdGhlbiBwbG90IHRob3NlLiAgU2V0IG1heF9wbG90X2xvY2kgc28gdGhhdCBhbGwgMjAwMAojIGxvY2kgd2lsbCBiZSBwbG90dGVkCmdlbm9fZnJlcXNfc2NhdHRlcihkcnVtZnJlcXMsIG1heF9wbG90X2xvY2kgPSA1MDAwKQpgYGAKClRoYXQgbG9va3MgYSBsb3QgYmV0dGVyLi4uCgpOb3csIGhvdyBhYm91dCBhbiBvdmVyYWxsIGhldCBtaXNjYWxsIHJhdGU/CmBgYHtyLCBjYWNoZT1UUlVFfQpkcnVtX292ZXJhbGwgPC0gaW5mZXJfbShkcnVtX3ZjZiwgbWluQmluID0gMWUxNSkKCmRydW1fb3ZlcmFsbCRtX3Bvc3RlcmlvcnMKYGBgCgpUaGF0IHRha2VzIGEgYml0IGxvbmdlciAobW9yZSBpbmRpdmlkdWFscyksIGJ1dCB0aGUgcmVzdWx0cyBhcmUgZ29vZC0tLWFib3V0IGEgNSUgaGV0Cm1pc2NhbGwgcmF0ZS4KCkJ1dCBub3RlIHRoYXQgdGhlIG1pc2NhbGwgcmF0ZSBpcyBjbGVhcmx5IGhpZ2hlciBhdCBsb3dlciByZWFkIGRlcHRocy4KCkZpcnN0LCBjaGVjayBob3cgbWFueSBpbmRpdmlkdWFscyBhbmQgaG93IG1hbnkgbG9jaSB3ZSBoYXZlIGhlcmU6CmBgYHtyfQpkaW0oZHJ1bV92Y2ZAZ3QpCmBgYApTbywgNzM4MiBsb2NpIGFuZCAyMDUgaW5kaXZpZHVhbHMuICBUaGF0IG1lYW5zIGNsb3NlIHRvIDEuNSBtaWxsaW9uIGdlbm90eXBlcy4KU28sIGlmIHdlIHdhbnQgdG8gYnJlYWsgdGhhdCB1cCBpbnRvIHJlYWQgZGVwdGggYmlucywgd2UgY291bGQgcHV0IDUwLDAwMCBpbiBlYWNoIGJpbiBhbmQgc3RpbGwKaGF2ZSBhIGxhcmdlIG51bWJlciBvZiBiaW5zOgpgYGB7ciwgY2FjaGU9VFJVRX0KZHJ1bV9iaW5uZWQgPC0gaW5mZXJfbShkcnVtX3ZjZiwgbWluQmluID0gNTAwMDApCgpwb3N0ZXJpb3JzX3Bsb3QoZHJ1bV9iaW5uZWQkbV9wb3N0ZXJpb3JzKQpgYGAKQW5kIHdlIHNlZSBhIGNsZWFyIHRyZW5kIHRoZXJlLgoKIyMgTm93LCBwbGVhc2UgdXNlIHlvdXIgb3duIGRhdGEgc2V0CgpJZiB5b3UgaGF2ZSBSQUQgZGF0YSBpbiB2Y2YgZm9ybWF0IGx5aW5nIGFyb3VuZCwgcGxlYXNlIHJ1biBpdCB0aHJvdWdoIGB3aG9hYCEKCk5vdGUgdGhhdCBpZiB5b3UgaGF2ZSBtdWx0aXBsZSwgZ2VuZXRpY2FsbHkgZGlzdGluY3QgcG9wdWxhdGlvbnMgaW4geW91ciBWQ0YsCnlvdSBjYW4gc2VsZWN0IGluZGl2aWR1YWxzIGZyb20ganVzdCBvbmUgcG9wdWxhdGlvbiBieSBpbmRleGluZyB0aGVtCm91dCBvZiB0aGUgdmNmUiBvYmplY3QuICBGb3IgZXhhbXBsZSwgaWYgd2Ugd2FudGVkIG9ubHkgYSBzdWJzZXQgb2Ygc2FtcGxlcyBmcm9tIHRoZSAKZHJ1bSBWQ0YgZmlsZSB3ZSBjb3VsZCBkbyBsaWtlIHRoaXM6CmBgYHtyfQpzYW1zIDwtIGMoIkFSXzAwMSIsIAogICAgICAgICAgIkFSXzAwMyIsCiAgICAgICAgICAiQVJfMDA0IiwKICAgICAgICAgICJBUl8wMDUiLAogICAgICAgICAgIkFSXzAwOCIsCiAgICAgICAgICAiQVJfMDEwIiwKICAgICAgICAgICJBUl8wMTIiLAogICAgICAgICAgIkFSXzAxNCIsCiAgICAgICAgICAiQVJfMDE1IiwKICAgICAgICAgICJBUl8wMTYiKQoKZHJ1bV9zdWJzZXQgPC0gZHJ1bV92Y2ZbLCBjKCJGT1JNQVQiLCBzYW1zKV0KCiMgY2hlY2sgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIGdlbm90eXBlcyBpbiB0aGF0IG9iamVjdDoKZGltKGRydW1fc3Vic2V0QGd0KQpgYGAKCk5vdGljZSBob3cgeW91IGhhdmUgdG8gaW5jbHVkZSB0aGUgY29sdW1uICJGT1JNQVQiIHdoZW4geW91IGluZGV4IHRoZQpvYmplY3QuICBUaGlzIGlzIGNyaXRpY2FsLS0tdGhhdCBpcyB0aGUgY29sdW1uIHRoYXQgdGVsbHMgeW91IGhvdyBhbGwgdGhlIAphdXhpbGxhcnkgaW5mb3JtYXRpb24gdGhhdCBjb21lcyB3aXRoIHRoZSBnZW5vdHlwZXMgKGxpa2UgcmVhZCBkZXB0aHMgYW5kIApxdWFsaXR5IHNjb3JlcykgaXMgZm9ybWF0dGVkLgoK