What you see here is an R notebook. We’ll talk about how to use them in class on Tuesday. Let’s start with a simple example. We’ll use the data from Isotoma petraea that we discussed in class to illustrate hierfstat and Hickory.

Converting the Isotoma data to a different format

We’ll start by using hierfstat. It needs the data in a different format from what is available on the website. This piece of code reads in the data directly from the website and converts it to the format that hierfstat uses. Other than the cute trick of reading the CSV file directly from the server rather than downloading it first, you can ignore all of the other coding.

library("tidyverse")
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.2     ✓ dplyr   1.0.7
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.1
── Conflicts ──────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
## The following line clears out any objects in memory. I make it a habit to do this so that I can make sure
## that things left over from the last time I ran R don't confuse things.
##
rm(list = ls())

dat <- read_csv("http://darwin.eeb.uconn.edu/eeb348-resources/isotoma.csv",
                col_types = cols(pop = col_character(), .default = col_integer()))

## If you know R, you'll recognize that I could have expressed this much more succintly, but for anyone who
## cares to follow the logic, this will be easier to see.
##
for (i in 1:nrow(dat)) {
  for (j in 2:ncol(dat)) {
    if (!is.na(dat[i, j])) {
      if (dat[i, j] == 0) {
        dat[i, j] <- 11
      } else if (dat[i, j] == 1) {
        dat[i, j] <- 12
      } else if (dat[i, j] == 2) {
        dat[i, j] <- 22
      }
    }   
  }
}
## This line converts the population abbreviatons to numbers
##
dat$Location <- as.numeric(as.factor(dat$pop))
## The first line puts Location in the first column. The second drops the pop column.
##
dat <- relocate(dat, Location) %>%
  select(-pop)
dat <- as.data.frame(dat)

Using hierfstat

We use wc() to estimate Weir and Cockerham’s \(F\)-statistics.1

library(hierfstat)

dat_fst <- wc(dat, diploid = TRUE)
dat_fst
$FST
[1] 0.03869543

$FIS
[1] 0.539859

That’s all there is to it. You don’t need to include the diploid = TRUE, but it’s good form to do so. If you happened to try this with the original file you downloaded, you’d get an error because the data aren’t in the right format. The numbers above match what I mentioned in lecture (with a small rounding error in the 4th decimal place for \(F_{IS}\))

Using `Hickory

Using Hickory is pretty straigthforward.2 It is, however, a bit more challenging to install than a regular R package. It uses RStan, and you’ll need to refer to this guide to install RStan. In particular, you’ll need to “configure the C++ toolchain”. If that sounds frightening, don’t worry about it. You don’t need to learn to program in C++, and you don’t even have to know how to compile a C++ program. The links will walk you through installing a C++ compiler on your machine. Once you’ve done that, installing Rstan is straigtforward, and then you’re ready to install Hickory. Just do the following:

install.packages("devtools")
install.packages(c("bayesplot"))
devtools::install_github("kholsinger/Hickory", build_vignettes = TRUE)

That last step will take a while, maybe as much as 10 minutes, and you’ll see a bunch of arcane messages in your R console window. So long as you don’t see something that says “Error”, you’re fine. If you do, let me know, and we’ll figure out how to work around it.

Once you’ve got Hickory installed, running the analysis is pretty straightforward.

library("Hickory")

## This line allows Hickory to run four chains simultaneously (assuming that your computer has at least)
## four cores
##
options(mc.cores = parallel::detectCores())

genos <- read_marker_data("http://darwin.eeb.uconn.edu/eeb348-resources/isotoma.csv")
## The "refresh = 0" prevents a lot of messages from being printed in this document. You probably will want
## to leave it out when you run your analyses so that you can see the progress messages and be reassured
## that there's something going on.
##
fit <- analyze_codominant(genos, refresh = 0)
Inference for Stan model: analyze_codominant.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

          mean se_mean    sd     2.5%      25%      50%      75%
f        0.345   0.002 0.102    0.148    0.272    0.344    0.415
theta    0.077   0.002 0.047    0.018    0.043    0.066    0.099
lp__  -121.868   0.194 4.339 -131.204 -124.632 -121.460 -118.723
         97.5% n_eff  Rhat
f        0.549  3659 1.000
theta    0.194   708 1.003
lp__  -114.641   501 1.004

Samples were drawn using NUTS(diag_e) at Sat Sep  4 14:59:39 2021.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).

If you compare those numbers with the numbers I reported in lecture, you’ll see that they are very close. They aren’t exactly the same because we’re simulating samples from the posterior distribution rather than calculating it exactly. The figure shows the median (the dot), the 50 percent credible interval (the thick dark blue line), and the 90 percent credible interval (the thin light blue line) for each of the parameters.

Lab exercise #2

We are going to use a subset of the data that Rachel Prunier and collaborators (including me) used to analyze the genetic structure of Protea repens https://doi.org/10.3732/ajb.1600232. You’ll find the data on the course website at http://darwin.eeb.uconn.edu/eeb348-resources/repens-outliers.csv and at http://darwin.eeb.uconn.edu/eeb348-resources/repens-outliers.stru.3

  1. Downlad the data in the .stru file. Then work through the following steps to analyze the data with wc().4

    1. Convert the data to a form that hierfstat can use by running read.structure() from adegenet.5. Note: 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. If everything worked properly, you’ll see the following message:
     Converting data from a STRUCTURE .stru file to a genind object... 
     
    1. Now use wc() to produce estimates of \(F_{IS}\) and \(F_{ST}\).

    2. Now convert the genind object that you used with wc() to a hierfstat data frame (using genind2hierfstat()), and use that dataframe to construct 95 percent confidence intervals for \(F_{IS}\) and \(F_{ST}\)

     boot.vc(repens_df[, 1], repens_df[, -1], diploid = TRUE)$ci
     

    repens_df is the name of the data frame I used. The first argument selects only the pop column (the one with the populations), and the second excludes the pop column, leaving a data frame with only the genotype data.

  2. Use Hickory and the data as provided in the .csv file to estimate \(F_{IS}\) and \(F_{ST}\). Report both the posterior mean and the 95 percent credible intervals. Note: The analysis took about 15 minutes on my MacBook. Be sure to plan ahead and leave yourself enough time to commplete the assignment.

  3. Is there evidence of inbreeding within populations?

  4. Is there evidence of genetic differentiation among populations?

  5. Does your answer to #3 or #4 depend on whether you look at results from hierfstat() or Hickory.

  6. BONUS QUESTION: Take a look at the help for analyze_codominant() and see if you can explain why the estimates from hierfstat and Hickory look a bit different even though we have quite a bit of data available.

Feel free simply to embed the R code in a copy of this notebook if you’re so inclined. but if you do, be sure to state explicitly how to interpret the output.[^I know how to do it, of course, but I want to make sure you know how to do it too.]


  1. You can probably guess why the function is called wc().↩︎

  2. Of course I’m probably biased. I wrote Hickory.↩︎

  3. I’ll explain what the .stru extension refers to next week.↩︎

  4. You can also use the trick of simply using the URL above to read the data directly from the course website.↩︎

  5. You’ll need to use install.packages() to install adegenet↩︎

LS0tCnRpdGxlOiAiJEYkLXN0YXRpc3RpY3Mgd2l0aCBgaGllcmZzdGF0YCBhbmQgYEhpY2tvcnlgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpXaGF0IHlvdSBzZWUgaGVyZSBpcyBhbiBgUmAgbm90ZWJvb2suIFdlJ2xsIHRhbGsgYWJvdXQgaG93IHRvIHVzZSB0aGVtIGluIGNsYXNzIG9uIFR1ZXNkYXkuIExldCdzIHN0YXJ0IHdpdGggYSBzaW1wbGUgZXhhbXBsZS4gV2UnbGwgdXNlIHRoZSBkYXRhIGZyb20gKklzb3RvbWEgcGV0cmFlYSogdGhhdCB3ZSBkaXNjdXNzZWQgaW4gY2xhc3MgdG8gaWxsdXN0cmF0ZSBgaGllcmZzdGF0YCBhbmQgYEhpY2tvcnlgLiAKCiMjIENvbnZlcnRpbmcgdGhlICpJc290b21hKiBkYXRhIHRvIGEgZGlmZmVyZW50IGZvcm1hdAoKV2UnbGwgc3RhcnQgYnkgdXNpbmcgYGhpZXJmc3RhdGAuIEl0IG5lZWRzIHRoZSBkYXRhIGluIGEgZGlmZmVyZW50IGZvcm1hdCBmcm9tIHdoYXQgaXMgYXZhaWxhYmxlIG9uIHRoZSB3ZWJzaXRlLiBUaGlzIHBpZWNlIG9mIGNvZGUgcmVhZHMgaW4gdGhlIGRhdGEgZGlyZWN0bHkgZnJvbSB0aGUgd2Vic2l0ZSBhbmQgY29udmVydHMgaXQgdG8gdGhlIGZvcm1hdCB0aGF0IGBoaWVyZnN0YXRgIHVzZXMuIE90aGVyIHRoYW4gdGhlIGN1dGUgdHJpY2sgb2YgcmVhZGluZyB0aGUgQ1NWIGZpbGUgZGlyZWN0bHkgZnJvbSB0aGUgc2VydmVyIHJhdGhlciB0aGFuIGRvd25sb2FkaW5nIGl0IGZpcnN0LCB5b3UgY2FuIGlnbm9yZSBhbGwgb2YgdGhlIG90aGVyIGNvZGluZy4KCmBgYHtyfQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQoKIyMgVGhlIGZvbGxvd2luZyBsaW5lIGNsZWFycyBvdXQgYW55IG9iamVjdHMgaW4gbWVtb3J5LiBJIG1ha2UgaXQgYSBoYWJpdCB0byBkbyB0aGlzIHNvIHRoYXQgSSBjYW4gbWFrZSBzdXJlCiMjIHRoYXQgdGhpbmdzIGxlZnQgb3ZlciBmcm9tIHRoZSBsYXN0IHRpbWUgSSByYW4gUiBkb24ndCBjb25mdXNlIHRoaW5ncy4KIyMKcm0obGlzdCA9IGxzKCkpCgpkYXQgPC0gcmVhZF9jc3YoImh0dHA6Ly9kYXJ3aW4uZWViLnVjb25uLmVkdS9lZWIzNDgtcmVzb3VyY2VzL2lzb3RvbWEuY3N2IiwKICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMocG9wID0gY29sX2NoYXJhY3RlcigpLCAuZGVmYXVsdCA9IGNvbF9pbnRlZ2VyKCkpKQoKIyMgSWYgeW91IGtub3cgUiwgeW91J2xsIHJlY29nbml6ZSB0aGF0IEkgY291bGQgaGF2ZSBleHByZXNzZWQgdGhpcyBtdWNoIG1vcmUgc3VjY2ludGx5LCBidXQgZm9yIGFueW9uZSB3aG8KIyMgY2FyZXMgdG8gZm9sbG93IHRoZSBsb2dpYywgdGhpcyB3aWxsIGJlIGVhc2llciB0byBzZWUuCiMjCmZvciAoaSBpbiAxOm5yb3coZGF0KSkgewogIGZvciAoaiBpbiAyOm5jb2woZGF0KSkgewogICAgaWYgKCFpcy5uYShkYXRbaSwgal0pKSB7CiAgICAgIGlmIChkYXRbaSwgal0gPT0gMCkgewogICAgICAgIGRhdFtpLCBqXSA8LSAxMQogICAgICB9IGVsc2UgaWYgKGRhdFtpLCBqXSA9PSAxKSB7CiAgICAgICAgZGF0W2ksIGpdIDwtIDEyCiAgICAgIH0gZWxzZSBpZiAoZGF0W2ksIGpdID09IDIpIHsKICAgICAgICBkYXRbaSwgal0gPC0gMjIKICAgICAgfQogICAgfSAgIAogIH0KfQojIyBUaGlzIGxpbmUgY29udmVydHMgdGhlIHBvcHVsYXRpb24gYWJicmV2aWF0b25zIHRvIG51bWJlcnMKIyMKZGF0JExvY2F0aW9uIDwtIGFzLm51bWVyaWMoYXMuZmFjdG9yKGRhdCRwb3ApKQojIyBUaGUgZmlyc3QgbGluZSBwdXRzIExvY2F0aW9uIGluIHRoZSBmaXJzdCBjb2x1bW4uIFRoZSBzZWNvbmQgZHJvcHMgdGhlIHBvcCBjb2x1bW4uCiMjCmRhdCA8LSByZWxvY2F0ZShkYXQsIExvY2F0aW9uKSAlPiUKICBzZWxlY3QoLXBvcCkKZGF0IDwtIGFzLmRhdGEuZnJhbWUoZGF0KQpgYGAKCiMjIFVzaW5nIGBoaWVyZnN0YXRgCgpXZSB1c2UgYHdjKClgIHRvIGVzdGltYXRlIFdlaXIgYW5kIENvY2tlcmhhbSdzICRGJC1zdGF0aXN0aWNzLl5bWW91IGNhbiBwcm9iYWJseSBndWVzcyB3aHkgdGhlIGZ1bmN0aW9uIGlzIGNhbGxlZCBgd2MoKWAuXQoKYGBge3J9CmxpYnJhcnkoaGllcmZzdGF0KQoKZGF0X2ZzdCA8LSB3YyhkYXQsIGRpcGxvaWQgPSBUUlVFKQpkYXRfZnN0CmBgYAoKVGhhdCdzIGFsbCB0aGVyZSBpcyB0byBpdC4gWW91IGRvbid0IG5lZWQgdG8gaW5jbHVkZSB0aGUgYGRpcGxvaWQgPSBUUlVFYCwgYnV0IGl0J3MgZ29vZCBmb3JtIHRvIGRvIHNvLiBJZiB5b3UgaGFwcGVuZWQgdG8gdHJ5IHRoaXMgd2l0aCB0aGUgb3JpZ2luYWwgZmlsZSB5b3UgZG93bmxvYWRlZCwgeW91J2QgZ2V0IGFuIGVycm9yIGJlY2F1c2UgdGhlIGRhdGEgYXJlbid0IGluIHRoZSByaWdodCBmb3JtYXQuIFRoZSBudW1iZXJzIGFib3ZlIG1hdGNoIHdoYXQgSSBtZW50aW9uZWQgaW4gbGVjdHVyZSAod2l0aCBhIHNtYWxsIHJvdW5kaW5nIGVycm9yIGluIHRoZSA0dGggZGVjaW1hbCBwbGFjZSBmb3IgJEZfe0lTfSQpCgojIyBVc2luZyBgSGlja29yeQoKVXNpbmcgYEhpY2tvcnlgIGlzIHByZXR0eSBzdHJhaWd0aGZvcndhcmQuXltPZiBjb3Vyc2UgSSdtIHByb2JhYmx5IGJpYXNlZC4gSSB3cm90ZSBgSGlja29yeWAuXSBJdCBpcywgaG93ZXZlciwgYSBiaXQgbW9yZSBjaGFsbGVuZ2luZyB0byBpbnN0YWxsIHRoYW4gYSByZWd1bGFyIGBSYCBwYWNrYWdlLiBJdCB1c2VzIGBSU3RhbmAsIGFuZCB5b3UnbGwgbmVlZCB0byByZWZlciB0byBbdGhpcyBndWlkZV0oaHR0cHM6Ly9naXRodWIuY29tL3N0YW4tZGV2L3JzdGFuL3dpa2kvUlN0YW4tR2V0dGluZy1TdGFydGVkKSB0byBpbnN0YWxsIGBSU3RhbmAuIEluIHBhcnRpY3VsYXIsIHlvdSdsbCBuZWVkIHRvICJjb25maWd1cmUgdGhlIEMrKyB0b29sY2hhaW4iLiBJZiB0aGF0IHNvdW5kcyBmcmlnaHRlbmluZywgZG9uJ3Qgd29ycnkgYWJvdXQgaXQuIFlvdSBkb24ndCBuZWVkIHRvIGxlYXJuIHRvIHByb2dyYW0gaW4gQysrLCBhbmQgeW91IGRvbid0IGV2ZW4gaGF2ZSB0byBrbm93IGhvdyB0byBjb21waWxlIGEgQysrIHByb2dyYW0uIFRoZSBsaW5rcyB3aWxsIHdhbGsgeW91IHRocm91Z2ggaW5zdGFsbGluZyBhIEMrKyBjb21waWxlciBvbiB5b3VyIG1hY2hpbmUuIE9uY2UgeW91J3ZlIGRvbmUgdGhhdCwgaW5zdGFsbGluZyBgUnN0YW5gIGlzIHN0cmFpZ3Rmb3J3YXJkLCBhbmQgdGhlbiB5b3UncmUgcmVhZHkgdG8gaW5zdGFsbCBgSGlja29yeWAuIEp1c3QgZG8gdGhlIGZvbGxvd2luZzoKCjxwcmU+Cmluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikKaW5zdGFsbC5wYWNrYWdlcyhjKCJiYXllc3Bsb3QiKSkKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJraG9sc2luZ2VyL0hpY2tvcnkiLCBidWlsZF92aWduZXR0ZXMgPSBUUlVFKQo8L3ByZT4KClRoYXQgbGFzdCBzdGVwIHdpbGwgdGFrZSBhIHdoaWxlLCBtYXliZSBhcyBtdWNoIGFzIDEwIG1pbnV0ZXMsIGFuZCB5b3UnbGwgc2VlIGEgYnVuY2ggb2YgYXJjYW5lIG1lc3NhZ2VzIGluIHlvdXIgYFJgIGNvbnNvbGUgd2luZG93LiBTbyBsb25nIGFzIHlvdSBkb24ndCBzZWUgc29tZXRoaW5nIHRoYXQgc2F5cyAiRXJyb3IiLCB5b3UncmUgZmluZS4gSWYgeW91IGRvLCBsZXQgbWUga25vdywgYW5kIHdlJ2xsIGZpZ3VyZSBvdXQgaG93IHRvIHdvcmsgYXJvdW5kIGl0LgoKT25jZSB5b3UndmUgZ290IGBIaWNrb3J5YCBpbnN0YWxsZWQsIHJ1bm5pbmcgdGhlIGFuYWx5c2lzIGlzIHByZXR0eSBzdHJhaWdodGZvcndhcmQuCgpgYGB7cn0KbGlicmFyeSgiSGlja29yeSIpCgojIyBUaGlzIGxpbmUgYWxsb3dzIEhpY2tvcnkgdG8gcnVuIGZvdXIgY2hhaW5zIHNpbXVsdGFuZW91c2x5IChhc3N1bWluZyB0aGF0IHlvdXIgY29tcHV0ZXIgaGFzIGF0IGxlYXN0KQojIyBmb3VyIGNvcmVzCiMjCm9wdGlvbnMobWMuY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSkKCmdlbm9zIDwtIHJlYWRfbWFya2VyX2RhdGEoImh0dHA6Ly9kYXJ3aW4uZWViLnVjb25uLmVkdS9lZWIzNDgtcmVzb3VyY2VzL2lzb3RvbWEuY3N2IikKIyMgVGhlICJyZWZyZXNoID0gMCIgcHJldmVudHMgYSBsb3Qgb2YgbWVzc2FnZXMgZnJvbSBiZWluZyBwcmludGVkIGluIHRoaXMgZG9jdW1lbnQuIFlvdSBwcm9iYWJseSB3aWxsIHdhbnQKIyMgdG8gbGVhdmUgaXQgb3V0IHdoZW4geW91IHJ1biB5b3VyIGFuYWx5c2VzIHNvIHRoYXQgeW91IGNhbiBzZWUgdGhlIHByb2dyZXNzIG1lc3NhZ2VzIGFuZCBiZSByZWFzc3VyZWQKIyMgdGhhdCB0aGVyZSdzIHNvbWV0aGluZyBnb2luZyBvbi4KIyMKZml0IDwtIGFuYWx5emVfY29kb21pbmFudChnZW5vcywgcmVmcmVzaCA9IDApCmBgYAoKSWYgeW91IGNvbXBhcmUgdGhvc2UgbnVtYmVycyB3aXRoIHRoZSBudW1iZXJzIEkgcmVwb3J0ZWQgaW4gbGVjdHVyZSwgeW91J2xsIHNlZSB0aGF0IHRoZXkgYXJlIHZlcnkgY2xvc2UuIFRoZXkgYXJlbid0IGV4YWN0bHkgdGhlIHNhbWUgYmVjYXVzZSB3ZSdyZSBzaW11bGF0aW5nIHNhbXBsZXMgZnJvbSB0aGUgcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbiByYXRoZXIgdGhhbiBjYWxjdWxhdGluZyBpdCBleGFjdGx5LiBUaGUgZmlndXJlIHNob3dzIHRoZSBtZWRpYW4gKHRoZSBkb3QpLCB0aGUgNTAgcGVyY2VudCBjcmVkaWJsZSBpbnRlcnZhbCAodGhlIHRoaWNrIGRhcmsgYmx1ZSBsaW5lKSwgYW5kIHRoZSA5MCBwZXJjZW50IGNyZWRpYmxlIGludGVydmFsICh0aGUgdGhpbiBsaWdodCBibHVlIGxpbmUpIGZvciBlYWNoIG9mIHRoZSBwYXJhbWV0ZXJzLgoKIyMgTGFiIGV4ZXJjaXNlICMyCgpXZSBhcmUgZ29pbmcgdG8gdXNlIGEgc3Vic2V0IG9mIHRoZSBkYXRhIHRoYXQgUmFjaGVsIFBydW5pZXIgYW5kIGNvbGxhYm9yYXRvcnMgKGluY2x1ZGluZyBtZSkgdXNlZCB0byBhbmFseXplIHRoZSBnZW5ldGljIHN0cnVjdHVyZSBvZiAqUHJvdGVhIHJlcGVucyogW2h0dHBzOi8vZG9pLm9yZy8xMC4zNzMyL2FqYi4xNjAwMjMyXShodHRwczovL2RvaS5vcmcvMTAuMzczMi9hamIuMTYwMDIzMikuIFlvdSdsbCBmaW5kIHRoZSBkYXRhIG9uIHRoZSBjb3Vyc2Ugd2Vic2l0ZSBhdCBbaHR0cDovL2Rhcndpbi5lZWIudWNvbm4uZWR1L2VlYjM0OC1yZXNvdXJjZXMvcmVwZW5zLW91dGxpZXJzLmNzdl0oaHR0cDovL2Rhcndpbi5lZWIudWNvbm4uZWR1L2VlYjM0OC1yZXNvdXJjZXMvcmVwZW5zLW91dGxpZXJzLmNzdikgYW5kIGF0IFtodHRwOi8vZGFyd2luLmVlYi51Y29ubi5lZHUvZWViMzQ4LXJlc291cmNlcy9yZXBlbnMtb3V0bGllcnMuc3RydV0oaHR0cDovL2Rhcndpbi5lZWIudWNvbm4uZWR1L2VlYjM0OC1yZXNvdXJjZXMvcmVwZW5zLW91dGxpZXJzLnN0cnUpLl5bSSdsbCBleHBsYWluIHdoYXQgdGhlIGAuc3RydWAgZXh0ZW5zaW9uIHJlZmVycyB0byBuZXh0IHdlZWsuXQoKMS4gRG93bmxhZCB0aGUgZGF0YSBpbiB0aGUgYC5zdHJ1YCBmaWxlLiBUaGVuIHdvcmsgdGhyb3VnaCB0aGUgZm9sbG93aW5nIHN0ZXBzIHRvIGFuYWx5emUgdGhlIGRhdGEgd2l0aCBgd2MoKWAuXltZb3UgY2FuIGFsc28gdXNlIHRoZSB0cmljayBvZiBzaW1wbHkgdXNpbmcgdGhlIFVSTCBhYm92ZSB0byByZWFkIHRoZSBkYXRhIGRpcmVjdGx5IGZyb20gdGhlIGNvdXJzZSB3ZWJzaXRlLl0KCiAgICBhLiBDb252ZXJ0IHRoZSBkYXRhIHRvIGEgZm9ybSB0aGF0IGBoaWVyZnN0YXRgIGNhbiB1c2UgYnkgcnVubmluZyBgcmVhZC5zdHJ1Y3R1cmUoKWAgZnJvbSBgYWRlZ2VuZXRgLl5bWW91J2xsIG5lZWQgdG8gdXNlIGBpbnN0YWxsLnBhY2thZ2VzKClgIHRvIGluc3RhbGwgYGFkZWdlbmV0YF0uIE5vdGU6IFRoZXJlIGFyZSA2NjIgZ2Vub3R5cGVzIChpbmRpdmlkdWFscyksIDE3MyBtYXJrZXJzIChsb2NpKSwgY29sdW1uIDEgY29udGFpbnMgdGhlIGxhYmVscyBmb3IgZ2Vub3R5cGVzIChpbmRpdmlkdWFscyksIGNvbHVtbiAyIGNvbnRhaW5zIHRoZSBwb3B1bGF0aW9uIGZhY3RvciAodGhlIHBvcHVsYXRpb24vbG9jYWxpdHkgZnJvbSB3aGljaCBhIHBhcnRpY3VsYXIgc2FtcGxlIHdhcyBjb2xsZWN0ZWQpLCB0aGVyZSBhcmUgbm8gb3B0aW9uYWwgY29sdW1ucywgcm93IDEgY29udGFpbnMgdGhlIG1hcmtlciAobG9jdXMpIG5hbWVzLCBhbmQgZ2Vub3R5cGVzIGFyZSBjb2RlZCBpbiB0d28gcm93cy4gSWYgZXZlcnl0aGluZyB3b3JrZWQgcHJvcGVybHksIHlvdSdsbCBzZWUgdGhlIGZvbGxvd2luZyBtZXNzYWdlOgogICAgCiAgICA8cHJlPgogQ29udmVydGluZyBkYXRhIGZyb20gYSBTVFJVQ1RVUkUgLnN0cnUgZmlsZSB0byBhIGdlbmluZCBvYmplY3QuLi4gCiAgICA8L3ByZT4KICAgIAogICAgYi4gTm93IHVzZSBgd2MoKWAgdG8gcHJvZHVjZSBlc3RpbWF0ZXMgb2YgJEZfe0lTfSQgYW5kICRGX3tTVH0kLgogICAgCiAgICBjLiBOb3cgY29udmVydCB0aGUgYGdlbmluZGAgb2JqZWN0IHRoYXQgeW91IHVzZWQgd2l0aCBgd2MoKWAgdG8gYSBgaGllcmZzdGF0YCBkYXRhIGZyYW1lICh1c2luZyBgZ2VuaW5kMmhpZXJmc3RhdCgpYCksIGFuZCB1c2UgdGhhdCBkYXRhZnJhbWUgdG8gY29uc3RydWN0IDk1IHBlcmNlbnQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yICRGX3tJU30kIGFuZCAkRl97U1R9JAogICAgCiAgICA8cHJlPgogICAgYm9vdC52YyhyZXBlbnNfZGZbLCAxXSwgcmVwZW5zX2RmWywgLTFdLCBkaXBsb2lkID0gVFJVRSkkY2kKICAgIDwvcHJlPgogICAgCiAgICBgcmVwZW5zX2RmYCBpcyB0aGUgbmFtZSBvZiB0aGUgZGF0YSBmcmFtZSBJIHVzZWQuIFRoZSBmaXJzdCBhcmd1bWVudCBzZWxlY3RzIG9ubHkgdGhlIGBwb3BgIGNvbHVtbiAodGhlIG9uZSB3aXRoIHRoZSBwb3B1bGF0aW9ucyksIGFuZCB0aGUgc2Vjb25kIGV4Y2x1ZGVzIHRoZSBgcG9wYCBjb2x1bW4sIGxlYXZpbmcgYSBkYXRhIGZyYW1lIHdpdGggb25seSB0aGUgZ2Vub3R5cGUgZGF0YS4gCiAgICAKMi4gVXNlIGBIaWNrb3J5YCBhbmQgdGhlIGRhdGEgYXMgcHJvdmlkZWQgaW4gdGhlIGAuY3N2YCBmaWxlIHRvIGVzdGltYXRlICRGX3tJU30kIGFuZCAkRl97U1R9JC4gUmVwb3J0IGJvdGggdGhlIHBvc3RlcmlvciBtZWFuIGFuZCB0aGUgOTUgcGVyY2VudCBjcmVkaWJsZSBpbnRlcnZhbHMuIE5vdGU6IFRoZSBhbmFseXNpcyB0b29rIGFib3V0IDE1IG1pbnV0ZXMgb24gbXkgTWFjQm9vay4gQmUgc3VyZSB0byBwbGFuIGFoZWFkIGFuZCBsZWF2ZSB5b3Vyc2VsZiBlbm91Z2ggdGltZSB0byBjb21tcGxldGUgdGhlIGFzc2lnbm1lbnQuCgozLiBJcyB0aGVyZSBldmlkZW5jZSBvZiBpbmJyZWVkaW5nIHdpdGhpbiBwb3B1bGF0aW9ucz8gCgo0LiBJcyB0aGVyZSBldmlkZW5jZSBvZiBnZW5ldGljIGRpZmZlcmVudGlhdGlvbiBhbW9uZyBwb3B1bGF0aW9ucz8KCjUuIERvZXMgeW91ciBhbnN3ZXIgdG8gIzMgb3IgIzQgZGVwZW5kIG9uIHdoZXRoZXIgeW91IGxvb2sgYXQgcmVzdWx0cyBmcm9tIGBoaWVyZnN0YXQoKWAgb3IgYEhpY2tvcnlgLgoKNi4gKipCT05VUyBRVUVTVElPTioqOiBUYWtlIGEgbG9vayBhdCB0aGUgaGVscCBmb3IgYGFuYWx5emVfY29kb21pbmFudCgpYCBhbmQgc2VlIGlmIHlvdSBjYW4gZXhwbGFpbiB3aHkgdGhlIGVzdGltYXRlcyBmcm9tIGBoaWVyZnN0YXRgIGFuZCBgSGlja29yeWAgbG9vayBhIGJpdCBkaWZmZXJlbnQgZXZlbiB0aG91Z2ggd2UgaGF2ZSBxdWl0ZSBhIGJpdCBvZiBkYXRhIGF2YWlsYWJsZS4KCkZlZWwgZnJlZSBzaW1wbHkgdG8gZW1iZWQgdGhlIGBSYCBjb2RlIGluIGEgY29weSBvZiB0aGlzIG5vdGVib29rIGlmIHlvdSdyZSBzbyBpbmNsaW5lZC4gKioqYnV0KioqIGlmIHlvdSBkbywgYmUgc3VyZSB0byBzdGF0ZSBleHBsaWNpdGx5IGhvdyB0byBpbnRlcnByZXQgdGhlIG91dHB1dC5bXkkga25vdyBob3cgdG8gZG8gaXQsIG9mIGNvdXJzZSwgYnV0IEkgd2FudCB0byBtYWtlIHN1cmUgeW91IGtub3cgaG93IHRvIGRvIGl0IHRvby5dIA==