For Lab 3 we used data from Camak et al. (2021)1 to explore population structure using LEA. This week we’re going to run a couple of additional analyses to explore these data further, and we’re going to use the results from these analyses to re-examine some of the conclusions Camak et al. reached. I’ll be asking you not only to produce numerical/graphical results from these analyses, but also to interpret what they mean biologically.

But first I have to introduce the methods, and we’ll use the Protea repens data you’ve already seen to do that.

PCA

First, we’ll run a principal components analysis of the data. To do this we first have to get the data into R using read.structure() from adegenet.2

library(adegenet)
repens <- read.structure("http://darwin.eeb.uconn.edu/eeb348-resources/repens-outliers.stru")
summary(repens)

// Number of individuals: 662
// Group sizes: 12 52 8 36 46 22 41 72 24 39 47 22 12 28 36 42 58 30 35
// Number of alleles per locus: 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
// Number of alleles per group: 247 334 233 325 321 303 316 326 299 295 326 305 292 308 319 328 332 319 323
// Percentage of missing data: 48.99 %
// Observed heterozygosity: 0.13 0.38 0.16 0.28 0.07 0.06 0.15 0.25 0.3 0.02 0.15 0.12 0.13 0.46 0.12 0.23 0.22 0.14 0.34 0.18 0.24 0.16 0.15 0.21 0.25 0.32 0.09 0.18 0.22 0.24 0.18 0.06 0.03 0.08 0.3 0.08 0.42 0.24 0.21 0.14 0.11 0.18 0.42 0.36 0.17 0.44 0.33 0.31 0.16 0.2 0.17 0.34 0.18 0.33 0.17 0.33 0.12 0.14 0.15 0.06 0.24 0.1 0.19 0.2 0.27 0.2 0.12 0.07 0.16 0.23 0.09 0.37 0.16 0.14 0.25 0.15 0.2 0.34 0.34 0.28 0.16 0.26 0.27 0.28 0.23 0.19 0.07 0.09 0.1 0.07 0.13 0.2 0.15 0.36 0.25 0.2 0.1 0.14 0.1 0.37 0.17 0.09 0.49 0.42 0.09 0.45 0.19 0.13 0.15 0.17 0.06 0.31 0.19 0.13 0.1 0.13 0.21 0.34 0.07 0.26 0.16 0.06 0.16 0.06 0.07 0.07 0.23 0.39 0.18 0.15 0.34 0.15 0.11 0.21 0.04 0.38 0.18 0.12 0.27 0.47 0.1 0.13 0.29 0.23 0.24 0.07 0.36 0.1 0.08 0.05 0.2 0.45 0.21 0.25 0.26 0.17 0.22 0.15 0.27 0.12 0.17 0.26 0.22 0.14 0.19 0.2 0.29 0.28 0.18 0.15 0.32 0.04 0.09
// Expected heterozygosity: 0.5 0.46 0.32 0.36 0.2 0.27 0.37 0.34 0.43 0.29 0.3 0.19 0.24 0.48 0.42 0.32 0.33 0.44 0.41 0.47 0.33 0.24 0.47 0.3 0.5 0.32 0.16 0.3 0.38 0.48 0.45 0.22 0.19 0.43 0.35 0.31 0.49 0.46 0.45 0.25 0.16 0.29 0.5 0.49 0.22 0.5 0.5 0.42 0.27 0.49 0.26 0.5 0.35 0.49 0.32 0.46 0.15 0.35 0.24 0.19 0.4 0.5 0.24 0.36 0.5 0.34 0.23 0.45 0.38 0.3 0.16 0.49 0.42 0.18 0.32 0.27 0.48 0.49 0.49 0.47 0.33 0.48 0.35 0.47 0.31 0.44 0.49 0.32 0.28 0.3 0.16 0.28 0.19 0.41 0.49 0.26 0.14 0.32 0.37 0.49 0.48 0.16 0.45 0.45 0.34 0.49 0.24 0.47 0.48 0.3 0.17 0.46 0.36 0.18 0.18 0.18 0.36 0.5 0.37 0.32 0.41 0.15 0.23 0.11 0.19 0.18 0.36 0.42 0.31 0.19 0.41 0.47 0.37 0.25 0.3 0.45 0.49 0.41 0.45 0.5 0.28 0.38 0.46 0.32 0.5 0.49 0.49 0.13 0.19 0.42 0.25 0.48 0.49 0.28 0.41 0.26 0.36 0.19 0.34 0.22 0.39 0.47 0.28 0.18 0.43 0.3 0.3 0.49 0.41 0.22 0.43 0.12 0.34

If we summarize the data, we’ll see that nearly fifty percent of the data is missing, i.e., fifty percent of the individual/locus combinations are empty because we couldn’t score that individual at that locus.3 This amount of missing data isn’t uncommon with genotype-by-sequencing (GBS) data when you don’t have a reference genome, but it is a problem for analysis by principal components. We fill the empty cells by replacing them with the mean allele frequency at the locus in question. This gives us complete data, but it understates the degree to which groupings in the data are different from one another. We also need a data frame that contains the population labels. Here’s how we do those two things:4

repens_mod <- tab(repens, freq = TRUE, NA.method = "mean")
repens_df <- data.frame(Population = pop(repens))

Now we run the principal components analysis (PCA) and plot the results. IMPORTANT NOTE: If you leave out the “u” in “colour” you won’t get the results that you want.

library(ggfortify)

repens_pca <- prcomp(repens_mod)
## must be spelled "colour" with a "u"
##
autoplot(repens_pca, data = repens_df, colour = "Population",
         frame.type = "norm") + 
  theme_bw()

With these data the first two principal components don’t account for much of the variation, only a little more than 15%. None of the populations are cleanly distinguished from others, but there are a few that don’t overlap. If you find the ellipses confusing, leave off the frame.type argument.

autoplot(repens_pca, data = repens_df, colour = "Population") + 
  theme_bw()

DAPC

DAPC is an acronym for “discriminant analysis of principal components”. As the name suggests, it combines principal components analysis (like what we’ve just seen) with another statistical technique, discriminant analysis. Principal components analysis is used to reduce the number of dimensions in a data set without trying to tell groups apart. Discriminant analysis takes pre-defined groups and tries to find mathematical functions that can be used to “discriminate” among them.

The first step in a DAPC analysis is to find genetic clusters in the data. The find.clusters() function in adegenet uses a technique known as k-means clustering.5 When you run find.clusters(), you’ll be asked how many PCs to retain. You’ll want to run this interactively on the console rather than through an R notebook so that you can answer the questions and see the plots before you answer

clusters <- find.clusters(repens)

In this case I picked 75 for the number of PCs to retain, because that gives me about 80 percent of the total variance. I picked 12 for the number of clusters, since that is the smallest BIC. Since 10-15 are all pretty close, if I were doing this for real, I’d probably look at the results for all of those possibilities to see if one made more sense than another.6

Protea repens PCs Protea repens clusters

Now you’re ready to run dapc(). Specify the same number of PCs that you did with find.clusters(). Again you’ll want to run this interactively, because dapc() will ask you how many discriminant functions to retain. There isn’t a clear cutoff with these data, but there’s a reasonably large jump from 3 to 4, and I picked 3.

repens_dapc <- dapc(repens, pop = pop(repens), n.pca = 75)

Protea repens DAPC

Once you’ve got that, then it’s just a matter of plotting the results.

scatter(repens_dapc)

The numbers in squares are the population labels (numbers from 1-19 in this case), the points correspond to individuals with the color matching the color of the population to which they belong, and the ellipses show the boundaries of where you expect most individuals belonging to that population to fall.

By default, scatter() plots the first two discriminant axes, but I specified three discriminant functions, so we can also plot axis 1 against axis 3 and axis 2 against axis 3 to get a more complete picture.

scatter(repens_dapc, 1, 3)

scatter(repens_dapc, 2, 3)

As you can see, population 8 seems to separate a bit from the remaining populations in these plots.

Notice that if we compare the number of individuals belonging to each population before dapc(), i.e., the number collected in a particular location, it differs from the number belonging to each population after dapc(). This means that some individuals are more similar to those from other individuals from other collection sites than to individuals in the site where they were collected.

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────── tidyverse 1.3.1 ──
✓ tibble  3.1.4     ✓ dplyr   1.0.7
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   2.0.1     ✓ forcats 0.5.1
✓ purrr   0.3.4     
── Conflicts ──────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
results <- data.frame(Initial = summary(repens_dapc)$prior.grp.size,
                      Final = summary(repens_dapc)$post.grp.size) %>%
  rename(Population = Initial.Var1,
         Initial = Initial.Freq,
         Final = Final.Freq) %>%
  select(-Final.Var1)
results

If we’re interested, we can determine which individuals have been reclassified.

results <- data.frame(Initial = repens_dapc$grp,
                      Final = repens_dapc$assign) %>%
  filter(Initial != Final)
results

You may or may not need to see which individuals have been classified, but at least you have the code to do so if you think it will be useful.

Project 1

When you submit your project please remember to use this naming convention:

Project1_first name_last name.extension

For example,

Project1_Kent_Holsinger.nb.html
  1. Use the data from http://darwin.eeb.uconn.edu/eeb348-resources/gila-trout.stru to perform a principal components analysis and a DAPC of genetic variation in gila trout from southwestern New Mexico. To read the data from this file you’ll need to know that rhere are 154 individuals, the genotype ID is in column 3, and the population ID is in column 1. There is one extra column : column 2. There is not a row of allele names. Individuals appear on two lines rather than one. Be sure to say how many PCs you retained for the DAPC analysis and why you picked that number. Also be sure to say how many clusters you picked and how many discriminant functions you used.

  2. Compare your results from this week’s PCA and DAPC with last week’s LEA analysis. Do you see noticeable differences among the patterns of genetic structure depending on the type of analysis you perform?

  3. Camak et al. assert that “All lineages have experienced population bottlenecks associated with mortality from drought and severe wildfires.” The data you have don’t allow you to detect any effects of drought or wildfires, but are the patterns of genetic structure you detected consistent with population bottlenecks.7

  4. Given that we expect populations that are closer to one another to be more genetically similar, is the geographical distribution of the populations illustrated in Figure 1 of Camak et al. consistent with the results of your analyses?


  1. Camak, D.T., M.J. Osborne, and T.F. Turner. 2021. Population genomics and conservation of Gila trout (Onchorhyncus gilae). Conservation Genetics https://doi.org/10.1007/s10592-021-01355-0.↩︎

  2. Remember that there are 662 genotypes (individuals), 173 markers (loci), column 1 contains the labels for genotypes (individuals), column 2 contains the population factor (the population/locality from which a particular sample was collected), there are no optional columns, row 1 contains the marker (locus) names, and genotypes are coded in two rows.↩︎

  3. We see this in the line labeled “Percentage of missing data”.↩︎

  4. Note: If you happen to have latitudes and longitudes for your collection sites, you could also add those to the data frame. Then you could plot individual points on a map colored by scores on one of the principal component axes.↩︎

  5. Given K, this approach finds the center of K clusters such that each individual belongs to one cluster and the distance between every individual and the center of the cluster to which they belong is minimized. See this Wikipedia article for more details.↩︎

  6. Depending on your data, the BIC plot may look more like the cross-entropy plot you saw with the gila trout data and LEA last week. In that case, pick a number of clusters that gives you close to the smallest BIC.↩︎

  7. This is stretching you a bit, since we won’t talk about genetic drift for a couple of weeks, but you should be able to answer based on your basic understanding of biology.↩︎

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKRm9yIFtMYWIgM10oaHR0cDovL2Rhcndpbi5lZWIudWNvbm4uZWR1L2VlYjM0OC1yZXNvdXJjZXMvTGFiMy5uYi5odG1sKSB3ZSB1c2VkIGRhdGEgZnJvbSBDYW1hayBldCBhbC4gKDIwMjEpXltDYW1haywgRC5ULiwgTS5KLiBPc2Jvcm5lLCBhbmQgVC5GLiBUdXJuZXIuIDIwMjEuIFBvcHVsYXRpb24gZ2Vub21pY3MgYW5kIGNvbnNlcnZhdGlvbiBvZiBHaWxhIHRyb3V0ICgqT25jaG9yaHluY3VzIGdpbGFlKikuICpDb25zZXJ2YXRpb24gR2VuZXRpY3MqIFtodHRwczovL2RvaS5vcmcvMTAuMTAwNy9zMTA1OTItMDIxLTAxMzU1LTBdKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDA3L3MxMDU5Mi0wMjEtMDEzNTUtMCkuXSB0byBleHBsb3JlIHBvcHVsYXRpb24gc3RydWN0dXJlIHVzaW5nIGBMRUFgLiBUaGlzIHdlZWsgd2UncmUgZ29pbmcgdG8gcnVuIGEgY291cGxlIG9mIGFkZGl0aW9uYWwgYW5hbHlzZXMgdG8gZXhwbG9yZSB0aGVzZSBkYXRhIGZ1cnRoZXIsICoqKmFuZCoqKiB3ZSdyZSBnb2luZyB0byB1c2UgdGhlIHJlc3VsdHMgZnJvbSB0aGVzZSBhbmFseXNlcyB0byByZS1leGFtaW5lIHNvbWUgb2YgdGhlIGNvbmNsdXNpb25zIENhbWFrIGV0IGFsLiByZWFjaGVkLiBJJ2xsIGJlIGFza2luZyB5b3Ugbm90IG9ubHkgdG8gcHJvZHVjZSBudW1lcmljYWwvZ3JhcGhpY2FsIHJlc3VsdHMgZnJvbSB0aGVzZSBhbmFseXNlcywgYnV0IGFsc28gdG8gaW50ZXJwcmV0IHdoYXQgdGhleSBtZWFuIGJpb2xvZ2ljYWxseS4KCkJ1dCBmaXJzdCBJIGhhdmUgdG8gaW50cm9kdWNlIHRoZSBtZXRob2RzLCBhbmQgd2UnbGwgdXNlIHRoZSAqUHJvdGVhIHJlcGVucyogZGF0YSB5b3UndmUgYWxyZWFkeSBzZWVuIHRvIGRvIHRoYXQuCgojIyBQQ0EKCkZpcnN0LCB3ZSdsbCBydW4gYSBwcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyBvZiB0aGUgZGF0YS4gVG8gZG8gdGhpcyB3ZSBmaXJzdCBoYXZlIHRvIGdldCB0aGUgZGF0YSBpbnRvIGBSYCB1c2luZyBgcmVhZC5zdHJ1Y3R1cmUoKWAgZnJvbSBgYWRlZ2VuZXRgLl5bUmVtZW1iZXIgdGhhdCB0aGVyZSBhcmUgNjYyIGdlbm90eXBlcyAoaW5kaXZpZHVhbHMpLCAxNzMgbWFya2VycyAobG9jaSksIGNvbHVtbiAxIGNvbnRhaW5zIHRoZSBsYWJlbHMgZm9yIGdlbm90eXBlcyAoaW5kaXZpZHVhbHMpLCBjb2x1bW4gMiBjb250YWlucyB0aGUgcG9wdWxhdGlvbiBmYWN0b3IgKHRoZSBwb3B1bGF0aW9uL2xvY2FsaXR5IGZyb20gd2hpY2ggYSBwYXJ0aWN1bGFyIHNhbXBsZSB3YXMgY29sbGVjdGVkKSwgdGhlcmUgYXJlIG5vIG9wdGlvbmFsIGNvbHVtbnMsIHJvdyAxIGNvbnRhaW5zIHRoZSBtYXJrZXIgKGxvY3VzKSBuYW1lcywgYW5kIGdlbm90eXBlcyBhcmUgY29kZWQgaW4gdHdvIHJvd3MuXQoKPHByZT4KbGlicmFyeShhZGVnZW5ldCkKcmVwZW5zIDwtIHJlYWQuc3RydWN0dXJlKCJodHRwOi8vZGFyd2luLmVlYi51Y29ubi5lZHUvZWViMzQ4LXJlc291cmNlcy9yZXBlbnMtb3V0bGllcnMuc3RydSIpCjwvcHJlPgoKYGBge3J9CnN1bW1hcnkocmVwZW5zKQpgYGAKCklmIHdlIHN1bW1hcml6ZSB0aGUgZGF0YSwgd2UnbGwgc2VlIHRoYXQgbmVhcmx5IGZpZnR5IHBlcmNlbnQgb2YgdGhlIGRhdGEgaXMgbWlzc2luZywgaS5lLiwgZmlmdHkgcGVyY2VudCBvZiB0aGUgaW5kaXZpZHVhbC9sb2N1cyBjb21iaW5hdGlvbnMgYXJlIGVtcHR5IGJlY2F1c2Ugd2UgY291bGRuJ3Qgc2NvcmUgdGhhdCBpbmRpdmlkdWFsIGF0IHRoYXQgbG9jdXMuXltXZSBzZWUgdGhpcyBpbiB0aGUgbGluZSBsYWJlbGVkICJQZXJjZW50YWdlIG9mIG1pc3NpbmcgZGF0YSIuXSBUaGlzIGFtb3VudCBvZiBtaXNzaW5nIGRhdGEgaXNuJ3QgdW5jb21tb24gd2l0aCBnZW5vdHlwZS1ieS1zZXF1ZW5jaW5nIChHQlMpIGRhdGEgd2hlbiB5b3UgZG9uJ3QgaGF2ZSBhIHJlZmVyZW5jZSBnZW5vbWUsIGJ1dCBpdCBpcyBhIHByb2JsZW0gZm9yIGFuYWx5c2lzIGJ5IHByaW5jaXBhbCBjb21wb25lbnRzLiBXZSBmaWxsIHRoZSBlbXB0eSBjZWxscyBieSByZXBsYWNpbmcgdGhlbSB3aXRoIHRoZSBtZWFuIGFsbGVsZSBmcmVxdWVuY3kgYXQgdGhlIGxvY3VzIGluIHF1ZXN0aW9uLiBUaGlzIGdpdmVzIHVzIGNvbXBsZXRlIGRhdGEsIGJ1dCBpdCB1bmRlcnN0YXRlcyB0aGUgZGVncmVlIHRvIHdoaWNoIGdyb3VwaW5ncyBpbiB0aGUgZGF0YSBhcmUgZGlmZmVyZW50IGZyb20gb25lIGFub3RoZXIuIFdlIGFsc28gbmVlZCBhIGRhdGEgZnJhbWUgdGhhdCBjb250YWlucyB0aGUgcG9wdWxhdGlvbiBsYWJlbHMuIEhlcmUncyBob3cgd2UgZG8gdGhvc2UgdHdvIHRoaW5nczpeW05vdGU6IElmIHlvdSBoYXBwZW4gdG8gaGF2ZSBsYXRpdHVkZXMgYW5kIGxvbmdpdHVkZXMgZm9yIHlvdXIgY29sbGVjdGlvbiBzaXRlcywgeW91IGNvdWxkIGFsc28gYWRkIHRob3NlIHRvIHRoZSBkYXRhIGZyYW1lLiBUaGVuIHlvdSBjb3VsZCBwbG90IGluZGl2aWR1YWwgcG9pbnRzIG9uIGEgbWFwIGNvbG9yZWQgYnkgc2NvcmVzIG9uIG9uZSBvZiB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudCBheGVzLl0KCmBgYHtyfQpyZXBlbnNfbW9kIDwtIHRhYihyZXBlbnMsIGZyZXEgPSBUUlVFLCBOQS5tZXRob2QgPSAibWVhbiIpCnJlcGVuc19kZiA8LSBkYXRhLmZyYW1lKFBvcHVsYXRpb24gPSBwb3AocmVwZW5zKSkKYGBgCgpOb3cgd2UgcnVuIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyAoUENBKSBhbmQgcGxvdCB0aGUgcmVzdWx0cy4gKioqSU1QT1JUQU5UIE5PVEUqKio6IElmIHlvdSBsZWF2ZSBvdXQgdGhlICJ1IiBpbiAiY29sb3VyIiB5b3Ugd29uJ3QgZ2V0IHRoZSByZXN1bHRzIHRoYXQgeW91IHdhbnQuCgpgYGB7cn0KbGlicmFyeShnZ2ZvcnRpZnkpCgpyZXBlbnNfcGNhIDwtIHByY29tcChyZXBlbnNfbW9kKQojIyBtdXN0IGJlIHNwZWxsZWQgImNvbG91ciIgd2l0aCBhICJ1IgojIwphdXRvcGxvdChyZXBlbnNfcGNhLCBkYXRhID0gcmVwZW5zX2RmLCBjb2xvdXIgPSAiUG9wdWxhdGlvbiIsCiAgICAgICAgIGZyYW1lLnR5cGUgPSAibm9ybSIpICsgCiAgdGhlbWVfYncoKQpgYGAKV2l0aCB0aGVzZSBkYXRhIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMgZG9uJ3QgYWNjb3VudCBmb3IgbXVjaCBvZiB0aGUgdmFyaWF0aW9uLCBvbmx5IGEgbGl0dGxlIG1vcmUgdGhhbiAxNSUuIE5vbmUgb2YgdGhlIHBvcHVsYXRpb25zIGFyZSBjbGVhbmx5IGRpc3Rpbmd1aXNoZWQgZnJvbSBvdGhlcnMsIGJ1dCB0aGVyZSBhcmUgYSBmZXcgdGhhdCBkb24ndCBvdmVybGFwLiBJZiB5b3UgZmluZCB0aGUgZWxsaXBzZXMgY29uZnVzaW5nLCBsZWF2ZSBvZmYgdGhlIGBmcmFtZS50eXBlYCBhcmd1bWVudC4KCmBgYHtyfQphdXRvcGxvdChyZXBlbnNfcGNhLCBkYXRhID0gcmVwZW5zX2RmLCBjb2xvdXIgPSAiUG9wdWxhdGlvbiIpICsgCiAgdGhlbWVfYncoKQpgYGAKCiMjIERBUEMKCkRBUEMgaXMgYW4gYWNyb255bSBmb3IgImRpc2NyaW1pbmFudCBhbmFseXNpcyBvZiBwcmluY2lwYWwgY29tcG9uZW50cyIuIEFzIHRoZSBuYW1lIHN1Z2dlc3RzLCBpdCBjb21iaW5lcyBwcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyAobGlrZSB3aGF0IHdlJ3ZlIGp1c3Qgc2Vlbikgd2l0aCBhbm90aGVyIHN0YXRpc3RpY2FsIHRlY2huaXF1ZSwgZGlzY3JpbWluYW50IGFuYWx5c2lzLiBQcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyBpcyB1c2VkIHRvIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGRpbWVuc2lvbnMgaW4gYSBkYXRhIHNldCB3aXRob3V0IHRyeWluZyB0byB0ZWxsIGdyb3VwcyBhcGFydC4gRGlzY3JpbWluYW50IGFuYWx5c2lzIHRha2VzIHByZS1kZWZpbmVkIGdyb3VwcyBhbmQgdHJpZXMgdG8gZmluZCBtYXRoZW1hdGljYWwgZnVuY3Rpb25zIHRoYXQgY2FuIGJlIHVzZWQgdG8gImRpc2NyaW1pbmF0ZSIgYW1vbmcgdGhlbS4KClRoZSBmaXJzdCBzdGVwIGluIGEgREFQQyBhbmFseXNpcyBpcyB0byBmaW5kIGdlbmV0aWMgY2x1c3RlcnMgaW4gdGhlIGRhdGEuIFRoZSBgZmluZC5jbHVzdGVycygpYCBmdW5jdGlvbiBpbiBgYWRlZ2VuZXRgIHVzZXMgYSB0ZWNobmlxdWUga25vd24gYXMgay1tZWFucyBjbHVzdGVyaW5nLl5bR2l2ZW4gSywgdGhpcyBhcHByb2FjaCBmaW5kcyB0aGUgY2VudGVyIG9mIEsgY2x1c3RlcnMgc3VjaCB0aGF0IGVhY2ggaW5kaXZpZHVhbCBiZWxvbmdzIHRvIG9uZSBjbHVzdGVyIGFuZCB0aGUgZGlzdGFuY2UgYmV0d2VlbiBldmVyeSBpbmRpdmlkdWFsIGFuZCB0aGUgY2VudGVyIG9mIHRoZSBjbHVzdGVyIHRvIHdoaWNoIHRoZXkgYmVsb25nIGlzIG1pbmltaXplZC4gU2VlIFt0aGlzIFdpa2lwZWRpYSBhcnRpY2xlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9LLW1lYW5zX2NsdXN0ZXJpbmcpIGZvciBtb3JlIGRldGFpbHMuXSBXaGVuIHlvdSBydW4gYGZpbmQuY2x1c3RlcnMoKWAsIHlvdSdsbCBiZSBhc2tlZCBob3cgbWFueSBQQ3MgdG8gcmV0YWluLiBZb3UnbGwgd2FudCB0byBydW4gdGhpcyBpbnRlcmFjdGl2ZWx5IG9uIHRoZSBjb25zb2xlIHJhdGhlciB0aGFuIHRocm91Z2ggYW4gUiBub3RlYm9vayBzbyB0aGF0IHlvdSBjYW4gYW5zd2VyIHRoZSBxdWVzdGlvbnMgYW5kIHNlZSB0aGUgcGxvdHMgYmVmb3JlIHlvdSBhbnN3ZXIKCjxwcmU+CmNsdXN0ZXJzIDwtIGZpbmQuY2x1c3RlcnMocmVwZW5zKQo8L3ByZT4KCkluIHRoaXMgY2FzZSBJIHBpY2tlZCA3NSBmb3IgdGhlIG51bWJlciBvZiBQQ3MgdG8gcmV0YWluLCBiZWNhdXNlIHRoYXQgZ2l2ZXMgbWUgYWJvdXQgODAgcGVyY2VudCBvZiB0aGUgdG90YWwgdmFyaWFuY2UuIEkgcGlja2VkIDEyIGZvciB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzLCBzaW5jZSB0aGF0IGlzIHRoZSBzbWFsbGVzdCBCSUMuIFNpbmNlIDEwLTE1IGFyZSBhbGwgcHJldHR5IGNsb3NlLCBpZiBJIHdlcmUgZG9pbmcgdGhpcyBmb3IgcmVhbCwgSSdkIHByb2JhYmx5IGxvb2sgYXQgdGhlIHJlc3VsdHMgZm9yIGFsbCBvZiB0aG9zZSBwb3NzaWJpbGl0aWVzIHRvIHNlZSBpZiBvbmUgbWFkZSBtb3JlIHNlbnNlIHRoYW4gYW5vdGhlci5eW0RlcGVuZGluZyBvbiB5b3VyIGRhdGEsIHRoZSBCSUMgcGxvdCBtYXkgbG9vayBtb3JlIGxpa2UgdGhlIGNyb3NzLWVudHJvcHkgcGxvdCB5b3Ugc2F3IHdpdGggdGhlIGdpbGEgdHJvdXQgZGF0YSBhbmQgYExFQWAgbGFzdCB3ZWVrLiBJbiB0aGF0IGNhc2UsIHBpY2sgYSBudW1iZXIgb2YgY2x1c3RlcnMgdGhhdCBnaXZlcyB5b3UgY2xvc2UgdG8gdGhlIHNtYWxsZXN0IEJJQy5dCgohW1Byb3RlYSByZXBlbnMgUENzXShyZXBlbnMtUENzLnBuZykKIVtQcm90ZWEgcmVwZW5zIGNsdXN0ZXJzXShyZXBlbnMtY2x1c3RlcnMucG5nKQoKTm93IHlvdSdyZSByZWFkeSB0byBydW4gYGRhcGMoKWAuIFNwZWNpZnkgdGhlIHNhbWUgbnVtYmVyIG9mIFBDcyB0aGF0IHlvdSBkaWQgd2l0aCBgZmluZC5jbHVzdGVycygpYC4gQWdhaW4geW91J2xsIHdhbnQgdG8gcnVuIHRoaXMgaW50ZXJhY3RpdmVseSwgYmVjYXVzZSBgZGFwYygpYCB3aWxsIGFzayB5b3UgaG93IG1hbnkgZGlzY3JpbWluYW50IGZ1bmN0aW9ucyB0byByZXRhaW4uIFRoZXJlIGlzbid0IGEgY2xlYXIgY3V0b2ZmIHdpdGggdGhlc2UgZGF0YSwgYnV0IHRoZXJlJ3MgYSByZWFzb25hYmx5IGxhcmdlIGp1bXAgZnJvbSAzIHRvIDQsIGFuZCBJIHBpY2tlZCAzLgoKPHByZT4KcmVwZW5zX2RhcGMgPC0gZGFwYyhyZXBlbnMsIHBvcCA9IHBvcChyZXBlbnMpLCBuLnBjYSA9IDc1KQo8L3ByZT4KCiFbUHJvdGVhIHJlcGVucyBEQVBDXShyZXBlbnMtREFQQy5wbmcpCgpPbmNlIHlvdSd2ZSBnb3QgdGhhdCwgdGhlbiBpdCdzIGp1c3QgYSBtYXR0ZXIgb2YgcGxvdHRpbmcgdGhlIHJlc3VsdHMuCgpgYGB7cn0Kc2NhdHRlcihyZXBlbnNfZGFwYykKYGBgClRoZSBudW1iZXJzIGluIHNxdWFyZXMgYXJlIHRoZSBwb3B1bGF0aW9uIGxhYmVscyAobnVtYmVycyBmcm9tIDEtMTkgaW4gdGhpcyBjYXNlKSwgdGhlIHBvaW50cyBjb3JyZXNwb25kIHRvIGluZGl2aWR1YWxzIHdpdGggdGhlIGNvbG9yIG1hdGNoaW5nIHRoZSBjb2xvciBvZiB0aGUgcG9wdWxhdGlvbiB0byB3aGljaCB0aGV5IGJlbG9uZywgYW5kIHRoZSBlbGxpcHNlcyBzaG93IHRoZSBib3VuZGFyaWVzIG9mIHdoZXJlIHlvdSBleHBlY3QgbW9zdCBpbmRpdmlkdWFscyBiZWxvbmdpbmcgdG8gdGhhdCBwb3B1bGF0aW9uIHRvIGZhbGwuCgpCeSBkZWZhdWx0LCBgc2NhdHRlcigpYCBwbG90cyB0aGUgZmlyc3QgdHdvIGRpc2NyaW1pbmFudCBheGVzLCBidXQgSSBzcGVjaWZpZWQgdGhyZWUgZGlzY3JpbWluYW50IGZ1bmN0aW9ucywgc28gd2UgY2FuIGFsc28gcGxvdCBheGlzIDEgYWdhaW5zdCBheGlzIDMgYW5kIGF4aXMgMiBhZ2FpbnN0IGF4aXMgMyB0byBnZXQgYSBtb3JlIGNvbXBsZXRlIHBpY3R1cmUuCgpgYGB7cn0Kc2NhdHRlcihyZXBlbnNfZGFwYywgMSwgMykKc2NhdHRlcihyZXBlbnNfZGFwYywgMiwgMykKYGBgCgpBcyB5b3UgY2FuIHNlZSwgcG9wdWxhdGlvbiA4IHNlZW1zIHRvIHNlcGFyYXRlIGEgYml0IGZyb20gdGhlIHJlbWFpbmluZyBwb3B1bGF0aW9ucyBpbiB0aGVzZSBwbG90cy4KCk5vdGljZSB0aGF0IGlmIHdlIGNvbXBhcmUgdGhlIG51bWJlciBvZiBpbmRpdmlkdWFscyBiZWxvbmdpbmcgdG8gZWFjaCBwb3B1bGF0aW9uIGJlZm9yZSBgZGFwYygpYCwgaS5lLiwgdGhlIG51bWJlciBjb2xsZWN0ZWQgaW4gYSBwYXJ0aWN1bGFyIGxvY2F0aW9uLCBpdCBkaWZmZXJzIGZyb20gdGhlIG51bWJlciBiZWxvbmdpbmcgdG8gZWFjaCBwb3B1bGF0aW9uIGFmdGVyIGBkYXBjKClgLiBUaGlzIG1lYW5zIHRoYXQgc29tZSBpbmRpdmlkdWFscyBhcmUgbW9yZSBzaW1pbGFyIHRvIHRob3NlIGZyb20gb3RoZXIgaW5kaXZpZHVhbHMgZnJvbSBvdGhlciBjb2xsZWN0aW9uIHNpdGVzIHRoYW4gdG8gaW5kaXZpZHVhbHMgaW4gdGhlIHNpdGUgd2hlcmUgdGhleSB3ZXJlIGNvbGxlY3RlZC4KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCnJlc3VsdHMgPC0gZGF0YS5mcmFtZShJbml0aWFsID0gc3VtbWFyeShyZXBlbnNfZGFwYykkcHJpb3IuZ3JwLnNpemUsCiAgICAgICAgICAgICAgICAgICAgICBGaW5hbCA9IHN1bW1hcnkocmVwZW5zX2RhcGMpJHBvc3QuZ3JwLnNpemUpICU+JQogIHJlbmFtZShQb3B1bGF0aW9uID0gSW5pdGlhbC5WYXIxLAogICAgICAgICBJbml0aWFsID0gSW5pdGlhbC5GcmVxLAogICAgICAgICBGaW5hbCA9IEZpbmFsLkZyZXEpICU+JQogIHNlbGVjdCgtRmluYWwuVmFyMSkKcmVzdWx0cwpgYGAKCklmIHdlJ3JlIGludGVyZXN0ZWQsIHdlIGNhbiBkZXRlcm1pbmUgd2hpY2ggaW5kaXZpZHVhbHMgaGF2ZSBiZWVuIHJlY2xhc3NpZmllZC4KCmBgYHtyfQpyZXN1bHRzIDwtIGRhdGEuZnJhbWUoSW5pdGlhbCA9IHJlcGVuc19kYXBjJGdycCwKICAgICAgICAgICAgICAgICAgICAgIEZpbmFsID0gcmVwZW5zX2RhcGMkYXNzaWduKSAlPiUKICBmaWx0ZXIoSW5pdGlhbCAhPSBGaW5hbCkKcmVzdWx0cwpgYGAKCllvdSBtYXkgb3IgbWF5IG5vdCBuZWVkIHRvIHNlZSB3aGljaCBpbmRpdmlkdWFscyBoYXZlIGJlZW4gY2xhc3NpZmllZCwgYnV0IGF0IGxlYXN0IHlvdSBoYXZlIHRoZSBjb2RlIHRvIGRvIHNvIGlmIHlvdSB0aGluayBpdCB3aWxsIGJlIHVzZWZ1bC4KCiMgUHJvamVjdCAxCgpXaGVuIHlvdSBzdWJtaXQgeW91ciBwcm9qZWN0IHBsZWFzZSByZW1lbWJlciB0byB1c2UgdGhpcyBuYW1pbmcgY29udmVudGlvbjoKCjxwcmU+ClByb2plY3QxX2ZpcnN0IG5hbWVfbGFzdCBuYW1lLmV4dGVuc2lvbgo8L3ByZT4KCkZvciBleGFtcGxlLAoKPHByZT4KUHJvamVjdDFfS2VudF9Ib2xzaW5nZXIubmIuaHRtbAo8L3ByZT4KCjEuIFVzZSB0aGUgZGF0YSBmcm9tIFtodHRwOi8vZGFyd2luLmVlYi51Y29ubi5lZHUvZWViMzQ4LXJlc291cmNlcy9naWxhLXRyb3V0LnN0cnVdKGh0dHA6Ly9kYXJ3aW4uZWViLnVjb25uLmVkdS9lZWIzNDgtcmVzb3VyY2VzL2dpbGEtdHJvdXQuc3RydSkgdG8gcGVyZm9ybSBhIHByaW5jaXBhbCBjb21wb25lbnRzIGFuYWx5c2lzIGFuZCBhIERBUEMgb2YgZ2VuZXRpYyB2YXJpYXRpb24gaW4gZ2lsYSB0cm91dCBmcm9tIHNvdXRod2VzdGVybiBOZXcgTWV4aWNvLiBUbyByZWFkIHRoZSBkYXRhIGZyb20gdGhpcyBmaWxlIHlvdSdsbCBuZWVkIHRvIGtub3cgdGhhdCByaGVyZSBhcmUgMTU0IGluZGl2aWR1YWxzLCB0aGUgZ2Vub3R5cGUgSUQgaXMgaW4gY29sdW1uIDMsIGFuZCB0aGUgcG9wdWxhdGlvbiBJRCBpcyBpbiBjb2x1bW4gMS4gVGhlcmUgaXMgb25lIGV4dHJhIGNvbHVtbiA6IGNvbHVtbiAyLiBUaGVyZSBpcyBub3QgYSByb3cgb2YgYWxsZWxlIG5hbWVzLiBJbmRpdmlkdWFscyBhcHBlYXIgb24gdHdvIGxpbmVzIHJhdGhlciB0aGFuIG9uZS4gQmUgc3VyZSB0byBzYXkgaG93IG1hbnkgUENzIHlvdSByZXRhaW5lZCBmb3IgdGhlIERBUEMgYW5hbHlzaXMgYW5kIHdoeSB5b3UgcGlja2VkIHRoYXQgbnVtYmVyLiBBbHNvIGJlIHN1cmUgdG8gc2F5IGhvdyBtYW55IGNsdXN0ZXJzIHlvdSBwaWNrZWQgYW5kIGhvdyBtYW55IGRpc2NyaW1pbmFudCBmdW5jdGlvbnMgeW91IHVzZWQuCgoyLiBDb21wYXJlIHlvdXIgcmVzdWx0cyBmcm9tIHRoaXMgd2VlaydzIFBDQSBhbmQgREFQQyB3aXRoIGxhc3Qgd2VlaydzIGBMRUFgIGFuYWx5c2lzLiBEbyB5b3Ugc2VlIG5vdGljZWFibGUgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIHBhdHRlcm5zIG9mIGdlbmV0aWMgc3RydWN0dXJlIGRlcGVuZGluZyBvbiB0aGUgdHlwZSBvZiBhbmFseXNpcyB5b3UgcGVyZm9ybT8KCjMuIENhbWFrIGV0IGFsLiBhc3NlcnQgdGhhdCAiQWxsIGxpbmVhZ2VzIGhhdmUgZXhwZXJpZW5jZWQgcG9wdWxhdGlvbiBib3R0bGVuZWNrcyBhc3NvY2lhdGVkIHdpdGggbW9ydGFsaXR5IGZyb20gZHJvdWdodCBhbmQgc2V2ZXJlIHdpbGRmaXJlcy4iIFRoZSBkYXRhIHlvdSBoYXZlIGRvbid0IGFsbG93IHlvdSB0byBkZXRlY3QgYW55IGVmZmVjdHMgb2YgZHJvdWdodCBvciB3aWxkZmlyZXMsIGJ1dCBhcmUgdGhlIHBhdHRlcm5zIG9mIGdlbmV0aWMgc3RydWN0dXJlIHlvdSBkZXRlY3RlZCBjb25zaXN0ZW50IHdpdGggcG9wdWxhdGlvbiBib3R0bGVuZWNrcy5eW1RoaXMgaXMgc3RyZXRjaGluZyB5b3UgYSBiaXQsIHNpbmNlIHdlIHdvbid0IHRhbGsgYWJvdXQgZ2VuZXRpYyBkcmlmdCBmb3IgYSBjb3VwbGUgb2Ygd2Vla3MsIGJ1dCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gYW5zd2VyIGJhc2VkIG9uIHlvdXIgYmFzaWMgdW5kZXJzdGFuZGluZyBvZiBiaW9sb2d5Ll0KCjQuIEdpdmVuIHRoYXQgd2UgZXhwZWN0IHBvcHVsYXRpb25zIHRoYXQgYXJlIGNsb3NlciB0byBvbmUgYW5vdGhlciB0byBiZSBtb3JlIGdlbmV0aWNhbGx5IHNpbWlsYXIsIGlzIHRoZSBnZW9ncmFwaGljYWwgZGlzdHJpYnV0aW9uIG9mIHRoZSBwb3B1bGF0aW9ucyBpbGx1c3RyYXRlZCBpbiBGaWd1cmUgMSBvZiBDYW1hayBldCBhbC4gY29uc2lzdGVudCB3aXRoIHRoZSByZXN1bHRzIG9mIHlvdXIgYW5hbHlzZXM/