Overview

Today we will be exploring how we can analyze genetic/genomic data using various R packages. Many of the code examples are taken from the Grunwald tutorial shared in the Canvas module.

Objectives:

Using R to perform population genetic analysis

Traditionally, many calculations could be performed by hand. Today, in the genomic era, we increasingly require computers to help us perform calculations on very large genetic/genomic datasets associated with populations. Nowhere is this more apparent than analysis of human genomic information.

Fortunately, the R environment has a very large user base involved in population genetics research. Today, we will use R to calculate various population genetics parameters and perform principle component analysis on data.

Below are some packages we will use to day. Go ahead and load them.

library(poppr)
library(adegenet)
library(mmod)

Load our genetic data

Often, we need to consolidate data from varied sources. Concerning genomic data, the most popular format is the Variant Call Format (or VCF). There are many R packages to help you perform these tasks such as vcfR. For the sake of brevity, today, I have generated an R data object in the GenInd format that is used by a variety of R population genetics packages. Load that file below.

load("humans.RData") # load the human genome population variant data from Baxevanis et al.
#load("/opt/BIOL5436/humans.RData")

humans # let's look at what this object is

Locus summary statistics

When starting to work with any dataset, it is informatic to review determine some basic understanding of the underlying diversity in alleles. We can use this information to make decisions about how useful certain genetic loci might be for downstream applications such as generating phylogenetic relationships.

library("poppr")     # Make sure poppr is loaded if you haven't done so already.
library("magrittr")  # We will also use magrittr for part of this chapter
data("Pinf")         # P. infestans data set from Mexico and South America
locus_table(Pinf)

As we can see, Pi33 has a low number of alleles in this particular example population. With that in mind, we may conclude that is not a good locus for certain types of analysis.

How about our human dataset?

locus_table(humans)

This particular dataset is heavily pared down and designed to allow us to do expedited calculations we will do later. So, since there is very little genetic diversity at each locus we can conclude that this data may not be extremely useful for certain types of calculations since this overall population is not very genetically diverse.

Population structure

We often want to understand how genetically distant populations and individuals are from each other, respectively.

Gst (a genomic form of Fst)

There are some simple functions available in R to calculate Gst (an analog of Fst). Recall that Fst (or Gst) are looking to understand the amount of genetic variation within a subpopulation compared to a broader population. Gst = 0 implies that the variation observed in the subpopulation is indistiguishable from other groups whereas Gst = 1 implies that the variation in subpopulation is distinct from other populations (i.e. it is differentiating).

Here’s an example…

library("mmod")
data("nancycats")
nancycats
Gst_Nei(nancycats)

How about for our subset of humans?

Gst_Nei(humans)

On a per locus basis, we see there is variability in Gst values. As we see, some values are quite low (even negative?). These values indicate there is no differences in variation at those loci or there may be low heterozygosity. Overall, we see a Gst value on par with what we expect - around 11-15%.

Genetic distance

Sometimes, we need to take an individual-level perspective to undertand population structure. This is particularly important when we know little about any underlying distinct populations in a group of individuals. There are variety of measures to calculate genetic distance. Here, we’ll focus on Nei’s Distance - a commonly used metric.

Let’s see an example of calculating distance and then check out our humans.

library("poppr")
library("ape") # To visualize the tree using the "nj" function
library("magrittr")
data(microbov)
set.seed(10)
ten_samples <- sample(nInd(microbov), 10)
mic10       <- microbov[ten_samples]
(micdist    <- provesti.dist(mic10))

We now have distances… Where have we used distances to infer relationships before? Phylogenetic trees!

While not a formal phylogeny, we can use trees to help build intuition about possible underlying population structure. Let’s use a stock dataset of bovine data from Africa and France.

theTree <- micdist %>%
  nj() %>%    # calculate neighbor-joining tree
  ladderize() # organize branches by clade
plot(theTree)
add.scale.bar(length = 0.05) # add a scale bar showing 5% difference.

Question

What can we conclude about the populations of African and French cattle?


Let’s look at our humans.

humDist <- nei.dist(humans)

humTree <- humDist %>%
  nj() %>%
  ladderize()

plot(humTree)

Clustering

Particularly with large datasets, use of clustering techniques can help provide some insights into population structure. Clustering is a statistical approach that often employs Principle Component Analysis (PCA). Clustering is distinct from overall distance-based methods in that it allows each locus to contribute to the overall determination of similarity and differences between individuals. However, clustering methods like k-means clustering do use per locus genetic distances as the basis for PCA. This ultimately gives rise to distinct clusters of individuals if there is sufficient genetic variation in the population and similarity between individuals. That said, this approach is not good for very clonal populations.

Let’s see an example. We’ll use a fungal pathogen dataset to illustrate some concepts.

library("poppr")
data("Pinf")
Pinf

For best results, copy and past the text below into the console window.

MX <- popsub(Pinf, "North America") # here we're extracting only the samples from North America in our dataset
MXclust <- find.clusters(MX)

We can see that we can form clusters using this analysis. The utlity and meaning of these clusters are up to the investigator, however. Remember, these are just statistical outcomes based on selected parameters.

How about our humans?!

Paste the following in the console, below.

find.clusters(humans)

Discriminant Analysis of Principle Components

Another method to infer population structure is Discriminant Analysis of Principle Components. Like clustering, it is a method reliant on PCA. DAPC is a method that looks to find groups/clusters of genetically related individuals. Unlike hierarchical clustering methods like k-means (that we explored above) DAPC is able to be used on highly clonal or partially clonal populations.

Let’s explore an example to see how this works. This example examines H3N2 influenza data across epidemic years. You might need to run this twice to get plot to show up.

# DAPC requires the adegenet package. Let's load this package:
library("adegenet")
data(H3N2) # load the H3N2 influenza data. Type ?H3N2 for more info.
pop(H3N2) <- H3N2$other$epid
dapc.H3N2 <- dapc(H3N2, var.contrib = TRUE, scale = FALSE, n.pca = 30, n.da = nPop(H3N2) - 1)
scatter(dapc.H3N2, cell = 0, pch = 18:23, cstar = 0, mstree = TRUE, lwd = 2, lty = 2)

Now, how about our humans?!

DAPC.humans <- dapc(humans, var.contrib = TRUE, scale = FALSE, n.pca = 20, n.da = nPop(H3N2) - 1)
scatter(DAPC.humans)

Question

Compare these results to Figure 15.1 in Baxevanis. Do we see any similarities/differences?


LS0tCnRpdGxlOiAiTW9kdWxlIDEyIEV4ZXJjaXNlcyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKIyBPdmVydmlldwoKVG9kYXkgd2Ugd2lsbCBiZSBleHBsb3JpbmcgaG93IHdlIGNhbiBhbmFseXplIGdlbmV0aWMvZ2Vub21pYyBkYXRhIHVzaW5nIHZhcmlvdXMgUiBwYWNrYWdlcy4gTWFueSBvZiB0aGUgY29kZSBleGFtcGxlcyBhcmUgdGFrZW4gZnJvbSB0aGUgR3J1bndhbGQgdHV0b3JpYWwgc2hhcmVkIGluIHRoZSBDYW52YXMgbW9kdWxlLgoKT2JqZWN0aXZlczoKCi0gRGV2ZWxvcCBmYW1pbGlhcml0eSB3aXRoIHBvcHVsYXIgUiBwb3B1bGF0aW9uIGdlbmV0aWNzIGFuYWx5c2lzIHBhY2thZ2VzLgoKLSBBbmFseXplIHNvbWUgZGF0YSBmcm9tIHRoZSAxMDAwIEdlbm9tZXMgUHJvamVjdC4KCgojIFVzaW5nIFIgdG8gcGVyZm9ybSBwb3B1bGF0aW9uIGdlbmV0aWMgYW5hbHlzaXMKClRyYWRpdGlvbmFsbHksIG1hbnkgY2FsY3VsYXRpb25zIGNvdWxkIGJlIHBlcmZvcm1lZCBieSBoYW5kLiBUb2RheSwgaW4gdGhlIGdlbm9taWMgZXJhLCB3ZSBpbmNyZWFzaW5nbHkgcmVxdWlyZSBjb21wdXRlcnMgdG8gaGVscCB1cyBwZXJmb3JtIGNhbGN1bGF0aW9ucyBvbiB2ZXJ5IGxhcmdlIGdlbmV0aWMvZ2Vub21pYyBkYXRhc2V0cyBhc3NvY2lhdGVkIHdpdGggcG9wdWxhdGlvbnMuIE5vd2hlcmUgaXMgdGhpcyBtb3JlIGFwcGFyZW50IHRoYW4gYW5hbHlzaXMgb2YgaHVtYW4gZ2Vub21pYyBpbmZvcm1hdGlvbi4KCkZvcnR1bmF0ZWx5LCB0aGUgUiBlbnZpcm9ubWVudCBoYXMgYSB2ZXJ5IGxhcmdlIHVzZXIgYmFzZSBpbnZvbHZlZCBpbiBwb3B1bGF0aW9uIGdlbmV0aWNzIHJlc2VhcmNoLiBUb2RheSwgd2Ugd2lsbCB1c2UgUiB0byBjYWxjdWxhdGUgdmFyaW91cyBwb3B1bGF0aW9uIGdlbmV0aWNzIHBhcmFtZXRlcnMgYW5kIHBlcmZvcm0gcHJpbmNpcGxlIGNvbXBvbmVudCBhbmFseXNpcyBvbiBkYXRhLgoKQmVsb3cgYXJlIHNvbWUgcGFja2FnZXMgd2Ugd2lsbCB1c2UgdG8gZGF5LiBHbyBhaGVhZCBhbmQgbG9hZCB0aGVtLgoKYGBge3J9CmxpYnJhcnkocG9wcHIpCmxpYnJhcnkoYWRlZ2VuZXQpCmxpYnJhcnkobW1vZCkKYGBgCgoKIyBMb2FkIG91ciBnZW5ldGljIGRhdGEKCk9mdGVuLCB3ZSBuZWVkIHRvIGNvbnNvbGlkYXRlIGRhdGEgZnJvbSB2YXJpZWQgc291cmNlcy4gQ29uY2VybmluZyBnZW5vbWljIGRhdGEsIHRoZSBtb3N0IHBvcHVsYXIgZm9ybWF0IGlzIHRoZSBWYXJpYW50IENhbGwgRm9ybWF0IChvciBWQ0YpLiBUaGVyZSBhcmUgbWFueSBSIHBhY2thZ2VzIHRvIGhlbHAgeW91IHBlcmZvcm0gdGhlc2UgdGFza3Mgc3VjaCBhcyBgdmNmUmAuIEZvciB0aGUgc2FrZSBvZiBicmV2aXR5LCB0b2RheSwgSSBoYXZlIGdlbmVyYXRlZCBhbiBSIGRhdGEgb2JqZWN0IGluIHRoZSBHZW5JbmQgZm9ybWF0IHRoYXQgaXMgdXNlZCBieSBhIHZhcmlldHkgb2YgUiBwb3B1bGF0aW9uIGdlbmV0aWNzIHBhY2thZ2VzLiBMb2FkIHRoYXQgZmlsZSBiZWxvdy4KCmBgYHtyfQpsb2FkKCJodW1hbnMuUkRhdGEiKSAjIGxvYWQgdGhlIGh1bWFuIGdlbm9tZSBwb3B1bGF0aW9uIHZhcmlhbnQgZGF0YSBmcm9tIEJheGV2YW5pcyBldCBhbC4KI2xvYWQoIi9vcHQvQklPTDU0MzYvaHVtYW5zLlJEYXRhIikKCmh1bWFucyAjIGxldCdzIGxvb2sgYXQgd2hhdCB0aGlzIG9iamVjdCBpcwpgYGAKCiMgTG9jdXMgc3VtbWFyeSBzdGF0aXN0aWNzCgpXaGVuIHN0YXJ0aW5nIHRvIHdvcmsgd2l0aCBhbnkgZGF0YXNldCwgaXQgaXMgaW5mb3JtYXRpYyB0byByZXZpZXcgZGV0ZXJtaW5lIHNvbWUgYmFzaWMgdW5kZXJzdGFuZGluZyBvZiB0aGUgdW5kZXJseWluZyBkaXZlcnNpdHkgaW4gYWxsZWxlcy4gV2UgY2FuIHVzZSB0aGlzIGluZm9ybWF0aW9uIHRvIG1ha2UgZGVjaXNpb25zIGFib3V0IGhvdyB1c2VmdWwgY2VydGFpbiBnZW5ldGljIGxvY2kgbWlnaHQgYmUgZm9yIGRvd25zdHJlYW0gYXBwbGljYXRpb25zIHN1Y2ggYXMgZ2VuZXJhdGluZyBwaHlsb2dlbmV0aWMgcmVsYXRpb25zaGlwcy4KCmBgYHtyfQpsaWJyYXJ5KCJwb3BwciIpICAgICAjIE1ha2Ugc3VyZSBwb3BwciBpcyBsb2FkZWQgaWYgeW91IGhhdmVuJ3QgZG9uZSBzbyBhbHJlYWR5LgpsaWJyYXJ5KCJtYWdyaXR0ciIpICAjIFdlIHdpbGwgYWxzbyB1c2UgbWFncml0dHIgZm9yIHBhcnQgb2YgdGhpcyBjaGFwdGVyCmRhdGEoIlBpbmYiKSAgICAgICAgICMgUC4gaW5mZXN0YW5zIGRhdGEgc2V0IGZyb20gTWV4aWNvIGFuZCBTb3V0aCBBbWVyaWNhCmxvY3VzX3RhYmxlKFBpbmYpCmBgYAoKQXMgd2UgY2FuIHNlZSwgUGkzMyBoYXMgYSBsb3cgbnVtYmVyIG9mIGFsbGVsZXMgaW4gdGhpcyBwYXJ0aWN1bGFyIGV4YW1wbGUgcG9wdWxhdGlvbi4gV2l0aCB0aGF0IGluIG1pbmQsIHdlIG1heSBjb25jbHVkZSB0aGF0IGlzIG5vdCBhIGdvb2QgbG9jdXMgZm9yIGNlcnRhaW4gdHlwZXMgb2YgYW5hbHlzaXMuCgpIb3cgYWJvdXQgb3VyIGh1bWFuIGRhdGFzZXQ/CgpgYGB7cn0KbG9jdXNfdGFibGUoaHVtYW5zKQpgYGAKClRoaXMgcGFydGljdWxhciBkYXRhc2V0IGlzIGhlYXZpbHkgcGFyZWQgZG93biBhbmQgZGVzaWduZWQgdG8gYWxsb3cgdXMgdG8gZG8gZXhwZWRpdGVkIGNhbGN1bGF0aW9ucyB3ZSB3aWxsIGRvIGxhdGVyLiBTbywgc2luY2UgdGhlcmUgaXMgdmVyeSBsaXR0bGUgZ2VuZXRpYyBkaXZlcnNpdHkgYXQgZWFjaCBsb2N1cyB3ZSBjYW4gY29uY2x1ZGUgdGhhdCB0aGlzIGRhdGEgbWF5IG5vdCBiZSBleHRyZW1lbHkgdXNlZnVsIGZvciBjZXJ0YWluIHR5cGVzIG9mIGNhbGN1bGF0aW9ucyBzaW5jZSB0aGlzIG92ZXJhbGwgcG9wdWxhdGlvbiBpcyBub3QgdmVyeSBnZW5ldGljYWxseSBkaXZlcnNlLgoKIyBQb3B1bGF0aW9uIHN0cnVjdHVyZQoKV2Ugb2Z0ZW4gd2FudCB0byB1bmRlcnN0YW5kIGhvdyBnZW5ldGljYWxseSBkaXN0YW50IHBvcHVsYXRpb25zIGFuZCBpbmRpdmlkdWFscyBhcmUgZnJvbSBlYWNoIG90aGVyLCByZXNwZWN0aXZlbHkuIAoKIyMgR3N0IChhIGdlbm9taWMgZm9ybSBvZiBGc3QpCgpUaGVyZSBhcmUgc29tZSBzaW1wbGUgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBSIHRvIGNhbGN1bGF0ZSBHc3QgKGFuIGFuYWxvZyBvZiBGc3QpLiBSZWNhbGwgdGhhdCBGc3QgKG9yIEdzdCkgYXJlIGxvb2tpbmcgdG8gdW5kZXJzdGFuZCB0aGUgYW1vdW50IG9mIGdlbmV0aWMgdmFyaWF0aW9uIHdpdGhpbiBhIHN1YnBvcHVsYXRpb24gY29tcGFyZWQgdG8gYSBicm9hZGVyIHBvcHVsYXRpb24uIEdzdCA9IDAgaW1wbGllcyB0aGF0IHRoZSB2YXJpYXRpb24gb2JzZXJ2ZWQgaW4gdGhlIHN1YnBvcHVsYXRpb24gaXMgaW5kaXN0aWd1aXNoYWJsZSBmcm9tIG90aGVyIGdyb3VwcyB3aGVyZWFzIEdzdCA9IDEgaW1wbGllcyB0aGF0IHRoZSB2YXJpYXRpb24gaW4gc3VicG9wdWxhdGlvbiBpcyBkaXN0aW5jdCBmcm9tIG90aGVyIHBvcHVsYXRpb25zIChpLmUuIGl0IGlzIGRpZmZlcmVudGlhdGluZykuIAoKSGVyZSdzIGFuIGV4YW1wbGUuLi4KCmBgYHtyfQpsaWJyYXJ5KCJtbW9kIikKZGF0YSgibmFuY3ljYXRzIikKbmFuY3ljYXRzCmBgYApgYGB7cn0KR3N0X05laShuYW5jeWNhdHMpCmBgYAoKSG93IGFib3V0IGZvciBvdXIgc3Vic2V0IG9mIGh1bWFucz8KCmBgYHtyfQpHc3RfTmVpKGh1bWFucykKYGBgCgpPbiBhIHBlciBsb2N1cyBiYXNpcywgd2Ugc2VlIHRoZXJlIGlzIHZhcmlhYmlsaXR5IGluIEdzdCB2YWx1ZXMuIEFzIHdlIHNlZSwgc29tZSB2YWx1ZXMgYXJlIHF1aXRlIGxvdyAoZXZlbiBuZWdhdGl2ZT8pLiBUaGVzZSB2YWx1ZXMgaW5kaWNhdGUgdGhlcmUgaXMgbm8gZGlmZmVyZW5jZXMgaW4gdmFyaWF0aW9uIGF0IHRob3NlIGxvY2kgb3IgdGhlcmUgbWF5IGJlIGxvdyBoZXRlcm96eWdvc2l0eS4gT3ZlcmFsbCwgd2Ugc2VlIGEgR3N0IHZhbHVlIG9uIHBhciB3aXRoIHdoYXQgd2UgZXhwZWN0IC0gYXJvdW5kIDExLTE1JS4KCiMjIEdlbmV0aWMgZGlzdGFuY2UKClNvbWV0aW1lcywgd2UgbmVlZCB0byB0YWtlIGFuIGluZGl2aWR1YWwtbGV2ZWwgcGVyc3BlY3RpdmUgdG8gdW5kZXJ0YW5kIHBvcHVsYXRpb24gc3RydWN0dXJlLiBUaGlzIGlzIHBhcnRpY3VsYXJseSBpbXBvcnRhbnQgd2hlbiB3ZSBrbm93IGxpdHRsZSBhYm91dCBhbnkgdW5kZXJseWluZyBkaXN0aW5jdCBwb3B1bGF0aW9ucyBpbiBhIGdyb3VwIG9mIGluZGl2aWR1YWxzLiBUaGVyZSBhcmUgdmFyaWV0eSBvZiBtZWFzdXJlcyB0byBjYWxjdWxhdGUgZ2VuZXRpYyBkaXN0YW5jZS4gSGVyZSwgd2UnbGwgZm9jdXMgb24gTmVpJ3MgRGlzdGFuY2UgLSBhIGNvbW1vbmx5IHVzZWQgbWV0cmljLgoKTGV0J3Mgc2VlIGFuIGV4YW1wbGUgb2YgY2FsY3VsYXRpbmcgZGlzdGFuY2UgYW5kIHRoZW4gY2hlY2sgb3V0IG91ciBodW1hbnMuCgpgYGB7cn0KbGlicmFyeSgicG9wcHIiKQpsaWJyYXJ5KCJhcGUiKSAjIFRvIHZpc3VhbGl6ZSB0aGUgdHJlZSB1c2luZyB0aGUgIm5qIiBmdW5jdGlvbgpsaWJyYXJ5KCJtYWdyaXR0ciIpCmRhdGEobWljcm9ib3YpCnNldC5zZWVkKDEwKQp0ZW5fc2FtcGxlcyA8LSBzYW1wbGUobkluZChtaWNyb2JvdiksIDEwKQptaWMxMCAgICAgICA8LSBtaWNyb2Jvdlt0ZW5fc2FtcGxlc10KKG1pY2Rpc3QgICAgPC0gcHJvdmVzdGkuZGlzdChtaWMxMCkpCmBgYAoKV2Ugbm93IGhhdmUgZGlzdGFuY2VzLi4uIFdoZXJlIGhhdmUgd2UgdXNlZCBkaXN0YW5jZXMgdG8gaW5mZXIgcmVsYXRpb25zaGlwcyBiZWZvcmU/IFBoeWxvZ2VuZXRpYyB0cmVlcyEKCldoaWxlIG5vdCBhIGZvcm1hbCBwaHlsb2dlbnksIHdlIGNhbiB1c2UgdHJlZXMgdG8gaGVscCBidWlsZCBpbnR1aXRpb24gYWJvdXQgcG9zc2libGUgdW5kZXJseWluZyBwb3B1bGF0aW9uIHN0cnVjdHVyZS4gTGV0J3MgdXNlIGEgc3RvY2sgZGF0YXNldCBvZiBib3ZpbmUgZGF0YSBmcm9tIEFmcmljYSBhbmQgRnJhbmNlLgoKYGBge3J9CnRoZVRyZWUgPC0gbWljZGlzdCAlPiUKICBuaigpICU+JSAgICAjIGNhbGN1bGF0ZSBuZWlnaGJvci1qb2luaW5nIHRyZWUKICBsYWRkZXJpemUoKSAjIG9yZ2FuaXplIGJyYW5jaGVzIGJ5IGNsYWRlCnBsb3QodGhlVHJlZSkKYWRkLnNjYWxlLmJhcihsZW5ndGggPSAwLjA1KSAjIGFkZCBhIHNjYWxlIGJhciBzaG93aW5nIDUlIGRpZmZlcmVuY2UuCmBgYAoKLS0tCgoqKlF1ZXN0aW9uKioKCldoYXQgY2FuIHdlIGNvbmNsdWRlIGFib3V0IHRoZSBwb3B1bGF0aW9ucyBvZiBBZnJpY2FuIGFuZCBGcmVuY2ggY2F0dGxlPwoKLS0tCgpMZXQncyBsb29rIGF0IG91ciBodW1hbnMuCgpgYGB7cn0KaHVtRGlzdCA8LSBuZWkuZGlzdChodW1hbnMpCgpodW1UcmVlIDwtIGh1bURpc3QgJT4lCiAgbmooKSAlPiUKICBsYWRkZXJpemUoKQoKcGxvdChodW1UcmVlKQoKYGBgCgojIyBDbHVzdGVyaW5nCgpQYXJ0aWN1bGFybHkgd2l0aCBsYXJnZSBkYXRhc2V0cywgdXNlIG9mIGNsdXN0ZXJpbmcgdGVjaG5pcXVlcyBjYW4gaGVscCBwcm92aWRlIHNvbWUgaW5zaWdodHMgaW50byBwb3B1bGF0aW9uIHN0cnVjdHVyZS4gQ2x1c3RlcmluZyBpcyBhIHN0YXRpc3RpY2FsIGFwcHJvYWNoIHRoYXQgb2Z0ZW4gZW1wbG95cyBQcmluY2lwbGUgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpLiBDbHVzdGVyaW5nIGlzIGRpc3RpbmN0IGZyb20gb3ZlcmFsbCBkaXN0YW5jZS1iYXNlZCBtZXRob2RzIGluIHRoYXQgaXQgYWxsb3dzIGVhY2ggbG9jdXMgdG8gY29udHJpYnV0ZSB0byB0aGUgb3ZlcmFsbCBkZXRlcm1pbmF0aW9uIG9mIHNpbWlsYXJpdHkgYW5kIGRpZmZlcmVuY2VzIGJldHdlZW4gaW5kaXZpZHVhbHMuIEhvd2V2ZXIsIGNsdXN0ZXJpbmcgbWV0aG9kcyBsaWtlIGstbWVhbnMgY2x1c3RlcmluZyBkbyB1c2UgcGVyIGxvY3VzIGdlbmV0aWMgZGlzdGFuY2VzIGFzIHRoZSBiYXNpcyBmb3IgUENBLiBUaGlzIHVsdGltYXRlbHkgZ2l2ZXMgcmlzZSB0byBkaXN0aW5jdCBjbHVzdGVycyBvZiBpbmRpdmlkdWFscyBpZiB0aGVyZSBpcyBzdWZmaWNpZW50IGdlbmV0aWMgdmFyaWF0aW9uIGluIHRoZSBwb3B1bGF0aW9uIGFuZCBzaW1pbGFyaXR5IGJldHdlZW4gaW5kaXZpZHVhbHMuIFRoYXQgc2FpZCwgdGhpcyBhcHByb2FjaCBpcyBub3QgZ29vZCBmb3IgdmVyeSBjbG9uYWwgcG9wdWxhdGlvbnMuCgpMZXQncyBzZWUgYW4gZXhhbXBsZS4gV2UnbGwgdXNlIGEgZnVuZ2FsIHBhdGhvZ2VuIGRhdGFzZXQgdG8gaWxsdXN0cmF0ZSBzb21lIGNvbmNlcHRzLgoKYGBge3J9CmxpYnJhcnkoInBvcHByIikKZGF0YSgiUGluZiIpClBpbmYKYGBgCgpGb3IgYmVzdCByZXN1bHRzLCBjb3B5IGFuZCBwYXN0IHRoZSB0ZXh0IGJlbG93IGludG8gdGhlIGNvbnNvbGUgd2luZG93LgpgYGB7fQpNWCA8LSBwb3BzdWIoUGluZiwgIk5vcnRoIEFtZXJpY2EiKSAjIGhlcmUgd2UncmUgZXh0cmFjdGluZyBvbmx5IHRoZSBzYW1wbGVzIGZyb20gTm9ydGggQW1lcmljYSBpbiBvdXIgZGF0YXNldApNWGNsdXN0IDwtIGZpbmQuY2x1c3RlcnMoTVgpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHdlIGNhbiBmb3JtIGNsdXN0ZXJzIHVzaW5nIHRoaXMgYW5hbHlzaXMuIFRoZSB1dGxpdHkgYW5kIG1lYW5pbmcgb2YgdGhlc2UgY2x1c3RlcnMgYXJlIHVwIHRvIHRoZSBpbnZlc3RpZ2F0b3IsIGhvd2V2ZXIuIFJlbWVtYmVyLCB0aGVzZSBhcmUganVzdCBzdGF0aXN0aWNhbCBvdXRjb21lcyBiYXNlZCBvbiBzZWxlY3RlZCBwYXJhbWV0ZXJzLgoKSG93IGFib3V0IG91ciBodW1hbnM/IQoKUGFzdGUgdGhlIGZvbGxvd2luZyBpbiB0aGUgY29uc29sZSwgYmVsb3cuCmBgYHt9CmZpbmQuY2x1c3RlcnMoaHVtYW5zKQpgYGAKCiMjIERpc2NyaW1pbmFudCBBbmFseXNpcyBvZiBQcmluY2lwbGUgQ29tcG9uZW50cwoKQW5vdGhlciBtZXRob2QgdG8gaW5mZXIgcG9wdWxhdGlvbiBzdHJ1Y3R1cmUgaXMgRGlzY3JpbWluYW50IEFuYWx5c2lzIG9mIFByaW5jaXBsZSBDb21wb25lbnRzLiBMaWtlIGNsdXN0ZXJpbmcsIGl0IGlzIGEgbWV0aG9kIHJlbGlhbnQgb24gUENBLiBEQVBDIGlzIGEgbWV0aG9kIHRoYXQgbG9va3MgdG8gZmluZCBncm91cHMvY2x1c3RlcnMgb2YgZ2VuZXRpY2FsbHkgcmVsYXRlZCBpbmRpdmlkdWFscy4gVW5saWtlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG1ldGhvZHMgbGlrZSBrLW1lYW5zICh0aGF0IHdlIGV4cGxvcmVkIGFib3ZlKSBEQVBDIGlzIGFibGUgdG8gYmUgdXNlZCBvbiBoaWdobHkgY2xvbmFsIG9yIHBhcnRpYWxseSBjbG9uYWwgcG9wdWxhdGlvbnMuCgpMZXQncyBleHBsb3JlIGFuIGV4YW1wbGUgdG8gc2VlIGhvdyB0aGlzIHdvcmtzLiBUaGlzIGV4YW1wbGUgZXhhbWluZXMgSDNOMiBpbmZsdWVuemEgZGF0YSBhY3Jvc3MgZXBpZGVtaWMgeWVhcnMuIFlvdSBtaWdodCBuZWVkIHRvIHJ1biB0aGlzIHR3aWNlIHRvIGdldCBwbG90IHRvIHNob3cgdXAuCgpgYGB7cn0KIyBEQVBDIHJlcXVpcmVzIHRoZSBhZGVnZW5ldCBwYWNrYWdlLiBMZXQncyBsb2FkIHRoaXMgcGFja2FnZToKbGlicmFyeSgiYWRlZ2VuZXQiKQpkYXRhKEgzTjIpICMgbG9hZCB0aGUgSDNOMiBpbmZsdWVuemEgZGF0YS4gVHlwZSA/SDNOMiBmb3IgbW9yZSBpbmZvLgpwb3AoSDNOMikgPC0gSDNOMiRvdGhlciRlcGlkCmRhcGMuSDNOMiA8LSBkYXBjKEgzTjIsIHZhci5jb250cmliID0gVFJVRSwgc2NhbGUgPSBGQUxTRSwgbi5wY2EgPSAzMCwgbi5kYSA9IG5Qb3AoSDNOMikgLSAxKQpzY2F0dGVyKGRhcGMuSDNOMiwgY2VsbCA9IDAsIHBjaCA9IDE4OjIzLCBjc3RhciA9IDAsIG1zdHJlZSA9IFRSVUUsIGx3ZCA9IDIsIGx0eSA9IDIpCmBgYAoKTm93LCBob3cgYWJvdXQgb3VyIGh1bWFucz8hCgpgYGB7cn0KREFQQy5odW1hbnMgPC0gZGFwYyhodW1hbnMsIHZhci5jb250cmliID0gVFJVRSwgc2NhbGUgPSBGQUxTRSwgbi5wY2EgPSAyMCwgbi5kYSA9IG5Qb3AoSDNOMikgLSAxKQpzY2F0dGVyKERBUEMuaHVtYW5zKQpgYGAKCi0tLQoKKipRdWVzdGlvbioqCgpDb21wYXJlIHRoZXNlIHJlc3VsdHMgdG8gRmlndXJlIDE1LjEgaW4gQmF4ZXZhbmlzLiBEbyB3ZSBzZWUgYW55IHNpbWlsYXJpdGllcy9kaWZmZXJlbmNlcz8KCi0tLQ==