Logistics for this lab exercise

This lab exercise will be different from others we’ve had in the course in several ways:

  1. No one will analyze all of the data themselves. We’re using a cheap form of parallelization. Rather than spreading the computational burden across different CPUs in a computational cluster, we’re spreading it across the CPUs in your laptops. Each of you will be responsible for only part of the analysis.
  2. You will complete the exercise in stages.
    1. Run the analysis for the specific combination of trait and loci to which you are assigned (see below).
    2. Send the results of your analysis, i.e., the GWAS-results.csv file that is produced, to Nick by the end of the day on Friday.
    3. By the end of the day Monday, Nick will compile the results of the separate analyses into three large results files (one for each trait) including all of the loci. By Tuesday morning, I’ll have the results files posted on the course website.
    4. Using the combined results from all traits and all loci you’ll answer the questions posed below.

Setting up for the analysis

You’ll be using a new R package for this lab exercise. Installing it is slightly more complicated than usual, because you first have to install a C/C++ compiler and a Fortran compiler in a place where R can find it. Fortunately there’s a really good set of instructions at https://learnb4ss.github.io/learnB4SS/articles/install-brms.html. If you follow those instructions, you should be all set.1 We recommend that you install brms before lab on Tuesday so that you can try the example analysis together in lab and talk about how to interpret the results.

Individual assignments

Note: We’ve assigned two people to loci 141:176 for all of the traits. There shouldn’t be noticeable differences in the results, but this gives us a way of checking a few of the results to make sure that they are consistent.

Loci Mass PD TDT
1:35 Isabelle Rowan Akriti
36:71 Emma Agustin Matthew
71:106 Nora Kaitlyn Gaurav
107:142 Henry Cynthia Nick
143:178 Sophia Fahad Claire
Alyssa Maya Kyle
179:218 Michelle Michelle Sanjay
McDonald Neitzey

GWAS with brms

Friedline et al.2 investigated the role of natural selection in the rapid spread of gypsy moth (Lymantria dispar) across Eastern North America. Their analysis focused on analysis of polymorphisms at 91,468 SNPs identified through double digest restriction-site associated DNA sequencing (ddRADseq). As part of their work, they also collected data on pupal mass (Mass), pupal development time (PD), and total development time (TDT). This allows us to perform a genome-wide association analysis that assesses the relationship, if any, between individual SNP loci and each of the phenotypes. You’ll find a smaller version of the data set on the course web site:

The data set is a subset of the data included in the paper. Specifically, I filtered the data to include only (a) loci that were scored in more than 100 individuals, (b) individuals that had more than 4000 of the remaing SNP loci scored, and (c) loci that were scored in all of the remaining individuals. The resulting data set includes 141 individuals scored at 218 loci. Using these data, answer the following questions:

  1. What loci (if any) are associated with each of the three traits (pupal mass, pupal development time, and total development time)?

    IMPORTANT NOTE: In the output, the loci are ordered from the strongest estimated effect (mean) to the weakest, but loci showing a weaker estimated effect may have stronger evidence for them, i.e., the credible interval may not overlap zero. Because of the small sample size, I recommend considering both the 80% and the 95% credible intervals whenn answering these questions.

    In the example below you’ll see that X4293 has the largest estimated effect, 0.01457 and that that the 95% credible interval on this effect does not overlap zero.3 This suggests that genotypic differences at X4293 have an effect on pupal mass. Notice that the 80% credible intervals for X2840, X4680, X2841, and X5330 do not overlap zero, providing suggestive evidence that differences at these loci may also affect pupal mass. Estimated effects at remaining loci are smaller and the 80% credible intervals overlap zero indicating that we have very weak evidence that differences at these loci have an effect on pupal mass.4

  2. Is there evidence that any of the SNP loci are associated with variation in more than one of the traits?

  3. Given your answer to #2, would you expect to see a change in pupal development time, total development time, or both if natural selection led to a change in pupal mass? Why or why not?

Short example of an analysis

After reading in the data from gypsymoth.csv, we extract the genotype data,5 and use popkin to estimate the relatedness of all of the samples from the genotype data. Then we run the analysis, in this case using Mass as the trait and looking only at loci 1:10 and sending the relatedness matrix to the function that runs the analysis. You’ll need to change the second to last line in this block of code to pick the trait and the range of loci that you’ve been assigned.

You’ll find GWAS-results.csv in the directory where you ran the analysis. The numbers you see in it will match those in the table that’s displayed after the analysis.6

options(tidyverse.quiet = TRUE)
library(tidyverse)
library(rstan)
library(brms)
library(popkin)

rm(list = ls())

options(mc.cores = parallel::detectCores())

analyze_trait <- function(dat, A, trait, loci, n_samples = 2000, n_chains = 4) {
  n_loci <- length(loci)
  p_raw <- matrix(nrow = n_loci, ncol = n_chains*n_samples/2)
  mean_p <- numeric(n_loci)
  mu <- array(dim = c(n_loci, n_samples, nrow(dat)))
  for (i in 1:n_loci) {
    locus <- colnames(dat)[loci[i] + 5]
    cat("Checking locus ", loci[i], " (", locus, ")\n", sep = "")
    fit <- brm(paste(trait, locus, sep = " ~ "),
               data = dat,
               data2 = list(A = A),
               family = gaussian(),
               iter = n_samples,
               refresh = 0)
    x <- as.data.frame(fit)
    brm_locus <- paste("b_", locus, sep ="")
    p_raw[i,] <- x[[brm_locus]]
    mean_p[i] <- mean(x[[brm_locus]])
  }
  loci <- data.frame(locus = colnames(dat)[(1:n_loci) + 5],
                     p_mean = mean_p,
                     index = 1:n_loci)
  loci <- loci[order(abs(loci$p_mean), decreasing = TRUE), ]
  p <- matrix(nrow = n_loci, ncol = n_chains*n_samples/2)
  for (i in 1:n_loci) {
    p[i, ] <- p_raw[loci$index[i],]
  }
  rownames(p) <- loci$locus
  return(list(loci = loci, p = p))
}

summarize_analysis <- function(results) {
  n_report <- nrow(results$loci)
  cat("\n\n",
      "         Mean: (    2.5%,      10%,      50%,      90%,    97.5%)\n")
  dat <- tibble(Locus = character(n_report),
                Mean = numeric(n_report), 
                `2.5%` = numeric(n_report), 
                `10%` = numeric(n_report),
                `50%` = numeric(n_report), 
                `90%` = numeric(n_report), 
                `97.5%` = numeric(n_report))
  for (i in 1:n_report) {
    ci <- quantile(results$p[results$loci$locus[i], ],
                   c(0.025, 0.1, 0.5, 0.9, 0.975))
    output <- sprintf("%6s: %8.5f (%8.5f, %8.5f, %8.5f, %8.5f, %8.5f)\n",
                      results$loci$locus[i],
                      results$loci$p_mean[i],
                      ci[1], ci[2], ci[3], ci[4], ci[5])
    cat(output)
    dat$Locus[i] <- results$loci$locus[i]
    dat$Mean[i] <- results$loci$p_mean[i]
    dat$`2.5%`[i] <- ci[1]
    dat$`10%`[i] <- ci[2]
    dat$`50%`[i] <- ci[3]
    dat$`90%`[i] <- ci[4]
    dat$`97.5%`[i] <- ci[5]
  }
  write_csv(dat, "GWAS-results.csv")
}

## read the data
##
dat <- read_csv("gypsymoth.csv",
                show_col_types = FALSE)
## get the relatedness matrix
##
genos <- dat %>% select(starts_with("X"))
A <- popkin(t(as.matrix(genos)),
            subpops = dat$pops)
## identify the rows with the sample names
##
rownames(A) <- dat$sample

## Change 1:10 to match the range of loci you have been assigned and "Mass" to the 
## trait you have been assigned 
##
results <- analyze_trait(dat, A, "Mass", 1:10)
Checking locus 1 (X2517)
Checking locus 2 (X2840)
Checking locus 3 (X2841)
Checking locus 4 (X3915)
Checking locus 5 (X4293)
Checking locus 6 (X4612)
Checking locus 7 (X4680)
Checking locus 8 (X4753)
Checking locus 9 (X5330)
Checking locus 10 (X6607)
summarize_analysis(results)

          Mean: (    2.5%,      10%,      50%,      90%,    97.5%)
 X4293:  0.01457 ( 0.00214,  0.00634,  0.01448,  0.02285,  0.02717)
 X2840: -0.01185 (-0.02509, -0.02049, -0.01192, -0.00296,  0.00161)
 X4680:  0.01142 (-0.00161,  0.00298,  0.01166,  0.01950,  0.02412)
 X2841: -0.01121 (-0.02437, -0.02011, -0.01117, -0.00223,  0.00274)
 X5330:  0.00912 (-0.00321,  0.00119,  0.00916,  0.01702,  0.02135)
 X3915: -0.00418 (-0.01686, -0.01267, -0.00416,  0.00435,  0.00910)
 X4753: -0.00412 (-0.01670, -0.01247, -0.00401,  0.00413,  0.00858)
 X4612:  0.00133 (-0.01521, -0.00994,  0.00109,  0.01268,  0.01868)
 X2517: -0.00107 (-0.01492, -0.01003, -0.00112,  0.00813,  0.01327)
 X6607:  0.00019 (-0.01459, -0.00932,  0.00016,  0.00978,  0.01486)

How I prepared the data for analysis

If you’re interested in seeing how I prepared the data from the version on Dryad. Here’s what I did.

First, I downloaded the data from Dryad: https://doi.org/10.5061/dryad.8ts2867. Then I renamed the phenotype filename so that it begins with gm. The rest is pretty straightforward.

library(tidyverse)
library(popkin)

rm(list = ls())

genos <- read_tsv("gm_genotypes_012_final_5122017.txt",
                  col_names = FALSE,
                  na = "-1",
                  show_col_types = FALSE)
phenos <- read_csv("gm_phenotypes.csv",
                   show_col_types = FALSE)

## keep only loci scored in more than 100 individuals
##
n_scored <- apply(genos, 2, sum, na.rm = TRUE)
genos <- genos[, n_scored > 100]

## keep only individuals scored at more than 4000 loci
##
n_scored <- apply(genos, 1, sum, na.rm = TRUE)
genos <- genos[n_scored > 4000, ]
phenos <- phenos[n_scored > 4000, ]
## and exclude any remaining loci where one or more individuals isn't scored
##
genos <- genos[, !is.na(apply(genos, 2, sum))]

## strip off the individual ids to identify populations
##
pops <- gsub("_.*", "", phenos$sample)

## bind the poopulation, phenotype, and genotype information together and save it in a CSV file
##
dat <- cbind(pops, phenos, genos)
write_csv(dat, 
          file = "gypsymoth.csv")

  1. You’ll find instructions for macOS, Windows, and Linux at that link.↩︎

  2. Friedline, C.J., T.M. Faske, B.M. Lind, E.M. Hobson, D. Parry, R.J. Dyer, D.M. Johnson, L.M. Thompson, K.L. Grayson, and A.J. Eckert. 2019. Evolutionary genomics of gypsy moth populations sampled along a latitudinal gradient. Molecular Ecology 28:2206-2223 doi: https://doi.org/10.1111/mec.15069↩︎

  3. You’ll get slightly slightly different numbers than the ones below, because brm uses Monte-Carlo sampling to estimate the posterior distribution. The differences shouldn’t affect the interpretation.↩︎

  4. It’s important to be very careful in your conclusion here. We do not have evidence that differences at these loci have no effect. With a larger sample of individuals we might have detected an effect at any of these loci.↩︎

  5. All of the columns with genotype information begin with “X”, and none of the other columns do.↩︎

  6. The results that are displayed are rounded to 5 digits. The numbers in the GWAS-results.csv include all digits found in the results.↩︎

LS0tCnRpdGxlOiAiR1dBUyBpbiBneXBzeSBtb3RocyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBMb2dpc3RpY3MgZm9yIHRoaXMgbGFiIGV4ZXJjaXNlCgpUaGlzIGxhYiBleGVyY2lzZSB3aWxsIGJlIGRpZmZlcmVudCBmcm9tIG90aGVycyB3ZSd2ZSBoYWQgaW4gdGhlIGNvdXJzZSBpbiBzZXZlcmFsIHdheXM6CgoxLiBObyBvbmUgd2lsbCBhbmFseXplIGFsbCBvZiB0aGUgZGF0YSB0aGVtc2VsdmVzLiBXZSdyZSB1c2luZyBhIGNoZWFwIGZvcm0gb2YgcGFyYWxsZWxpemF0aW9uLiBSYXRoZXIgdGhhbiBzcHJlYWRpbmcgdGhlIGNvbXB1dGF0aW9uYWwgYnVyZGVuIGFjcm9zcyBkaWZmZXJlbnQgQ1BVcyBpbiBhIGNvbXB1dGF0aW9uYWwgY2x1c3Rlciwgd2UncmUgc3ByZWFkaW5nIGl0IGFjcm9zcyB0aGUgQ1BVcyBpbiB5b3VyIGxhcHRvcHMuIEVhY2ggb2YgeW91IHdpbGwgYmUgcmVzcG9uc2libGUgZm9yIG9ubHkgcGFydCBvZiB0aGUgYW5hbHlzaXMuCjIuIFlvdSB3aWxsIGNvbXBsZXRlIHRoZSBleGVyY2lzZSBpbiBzdGFnZXMuCiAgICBhLiBSdW4gdGhlIGFuYWx5c2lzIGZvciB0aGUgc3BlY2lmaWMgY29tYmluYXRpb24gb2YgdHJhaXQgYW5kIGxvY2kgdG8gd2hpY2ggeW91IGFyZSBhc3NpZ25lZCAoc2VlIGJlbG93KS4KICAgIGIuIFNlbmQgdGhlIHJlc3VsdHMgb2YgeW91ciBhbmFseXNpcywgaS5lLiwgdGhlIGBHV0FTLXJlc3VsdHMuY3N2YCBmaWxlIHRoYXQgaXMgcHJvZHVjZWQsIHRvIE5pY2sgYnkgdGhlIGVuZCBvZiB0aGUgZGF5IG9uIEZyaWRheS4KICAgIGMuIEJ5IHRoZSBlbmQgb2YgdGhlIGRheSBNb25kYXksIE5pY2sgd2lsbCBjb21waWxlIHRoZSByZXN1bHRzIG9mIHRoZSBzZXBhcmF0ZSBhbmFseXNlcyBpbnRvIHRocmVlIGxhcmdlIHJlc3VsdHMgZmlsZXMgKG9uZSBmb3IgZWFjaCB0cmFpdCkgaW5jbHVkaW5nIGFsbCBvZiB0aGUgbG9jaS4gQnkgVHVlc2RheSBtb3JuaW5nLCBJJ2xsIGhhdmUgdGhlIHJlc3VsdHMgZmlsZXMgcG9zdGVkIG9uIHRoZSBjb3Vyc2Ugd2Vic2l0ZS4KICAgIGQuIFVzaW5nIHRoZSBjb21iaW5lZCByZXN1bHRzIGZyb20gYWxsIHRyYWl0cyBhbmQgYWxsIGxvY2kgeW91J2xsIGFuc3dlciB0aGUgcXVlc3Rpb25zIHBvc2VkIGJlbG93LgogIAojIyBTZXR0aW5nIHVwIGZvciB0aGUgYW5hbHlzaXMKCllvdSdsbCBiZSB1c2luZyBhIG5ldyBgUmAgcGFja2FnZSBmb3IgdGhpcyBsYWIgZXhlcmNpc2UuIEluc3RhbGxpbmcgaXQgaXMgc2xpZ2h0bHkgbW9yZSBjb21wbGljYXRlZCB0aGFuIHVzdWFsLCBiZWNhdXNlIHlvdSBmaXJzdCBoYXZlIHRvIGluc3RhbGwgYSBDL0MrKyBjb21waWxlciBhbmQgYSBGb3J0cmFuIGNvbXBpbGVyIGluIGEgcGxhY2Ugd2hlcmUgYFJgIGNhbiBmaW5kIGl0LiBGb3J0dW5hdGVseSB0aGVyZSdzIGEgcmVhbGx5IGdvb2Qgc2V0IG9mIGluc3RydWN0aW9ucyBhdCBbaHR0cHM6Ly9sZWFybmI0c3MuZ2l0aHViLmlvL2xlYXJuQjRTUy9hcnRpY2xlcy9pbnN0YWxsLWJybXMuaHRtbF0oaHR0cHM6Ly9sZWFybmI0c3MuZ2l0aHViLmlvL2xlYXJuQjRTUy9hcnRpY2xlcy9pbnN0YWxsLWJybXMuaHRtbCkuIElmIHlvdSBmb2xsb3cgdGhvc2UgaW5zdHJ1Y3Rpb25zLCB5b3Ugc2hvdWxkIGJlIGFsbCBzZXQuXltZb3UnbGwgZmluZCBpbnN0cnVjdGlvbnMgZm9yIG1hY09TLCBXaW5kb3dzLCBhbmQgTGludXggYXQgdGhhdCBsaW5rLl0gV2UgcmVjb21tZW5kIHRoYXQgeW91IGluc3RhbGwgYGJybXNgICoqKmJlZm9yZSoqKiBsYWIgb24gVHVlc2RheSBzbyB0aGF0IHlvdSBjYW4gdHJ5IHRoZSBleGFtcGxlIGFuYWx5c2lzIHRvZ2V0aGVyIGluIGxhYiBhbmQgdGFsayBhYm91dCBob3cgdG8gaW50ZXJwcmV0IHRoZSByZXN1bHRzLgoKIyMgSW5kaXZpZHVhbCBhc3NpZ25tZW50cwoKTm90ZTogV2UndmUgYXNzaWduZWQgdHdvIHBlb3BsZSB0byBsb2NpIDE0MToxNzYgZm9yIGFsbCBvZiB0aGUgdHJhaXRzLiBUaGVyZSBzaG91bGRuJ3QgYmUgbm90aWNlYWJsZSBkaWZmZXJlbmNlcyBpbiB0aGUgcmVzdWx0cywgYnV0IHRoaXMgZ2l2ZXMgdXMgYSB3YXkgb2YgY2hlY2tpbmcgYSBmZXcgb2YgdGhlIHJlc3VsdHMgdG8gbWFrZSBzdXJlIHRoYXQgdGhleSBhcmUgY29uc2lzdGVudC4KCjx0YWJsZT4KTG9jaSAgICAgTWFzcyAgICAgUEQgICAgICAgVERUCi0tLS0tLS0tIC0tLS0tLS0tIC0tLS0tLS0gIC0tLS0tLS0KMTozNSAgICAgSXNhYmVsbGUgUm93YW4gICAgQWtyaXRpCjM2OjcxICAgIEVtbWEgICAgIEFndXN0aW4gIE1hdHRoZXcKNzE6MTA2ICAgTm9yYSAgICAgS2FpdGx5biAgR2F1cmF2CjEwNzoxNDIgIEhlbnJ5ICAgIEN5bnRoaWEgIE5pY2sKMTQzOjE3OCAgU29waGlhICAgRmFoYWQgICAgQ2xhaXJlCiAgICAgICAgIEFseXNzYSAgIE1heWEgICAgIEt5bGUKMTc5OjIxOCAgTWljaGVsbGUgTWljaGVsbGUgU2FuamF5CiAgICAgICAgIE1jRG9uYWxkIE5laXR6ZXkKPC90YWJsZT4KCgojIEdXQVMgd2l0aCBgYnJtc2AKCkZyaWVkbGluZSBldCBhbC5eW0ZyaWVkbGluZSwgQy5KLiwgVC5NLiBGYXNrZSwgQi5NLiBMaW5kLCBFLk0uIEhvYnNvbiwgRC4gUGFycnksIFIuSi4gRHllciwgRC5NLiBKb2huc29uLCBMLk0uIFRob21wc29uLCBLLkwuIEdyYXlzb24sIGFuZCBBLkouIEVja2VydC4gIDIwMTkuICBFdm9sdXRpb25hcnkgZ2Vub21pY3Mgb2YgZ3lwc3kgbW90aCBwb3B1bGF0aW9ucyBzYW1wbGVkIGFsb25nIGEgbGF0aXR1ZGluYWwgZ3JhZGllbnQuIF9Nb2xlY3VsYXIgRWNvbG9neV8gMjg6MjIwNi0yMjIzIGRvaTogW2h0dHBzOi8vZG9pLm9yZy8xMC4xMTExL21lYy4xNTA2OV0oaHR0cHM6Ly9kb2kub3JnLzEwLjExMTEvbWVjLjE1MDY5KV0gaW52ZXN0aWdhdGVkIHRoZSByb2xlIG9mIG5hdHVyYWwgc2VsZWN0aW9uIGluIHRoZSByYXBpZCBzcHJlYWQgb2YgZ3lwc3kgbW90aCAoX0x5bWFudHJpYSBkaXNwYXJfKSBhY3Jvc3MgRWFzdGVybiBOb3J0aCBBbWVyaWNhLiBUaGVpciBhbmFseXNpcyBmb2N1c2VkIG9uIGFuYWx5c2lzIG9mIHBvbHltb3JwaGlzbXMgYXQgOTEsNDY4IFNOUHMgaWRlbnRpZmllZCB0aHJvdWdoIGRvdWJsZSBkaWdlc3QgcmVzdHJpY3Rpb24tc2l0ZSBhc3NvY2lhdGVkIEROQSBzZXF1ZW5jaW5nIChkZFJBRHNlcSkuIEFzIHBhcnQgb2YgdGhlaXIgd29yaywgdGhleSBhbHNvIGNvbGxlY3RlZCBkYXRhIG9uIHB1cGFsIG1hc3MgKE1hc3MpLCBwdXBhbCBkZXZlbG9wbWVudCB0aW1lIChQRCksIGFuZCB0b3RhbCBkZXZlbG9wbWVudCB0aW1lIChURFQpLiBUaGlzIGFsbG93cyB1cyB0byBwZXJmb3JtIGEgZ2Vub21lLXdpZGUgYXNzb2NpYXRpb24gYW5hbHlzaXMgdGhhdCBhc3Nlc3NlcyB0aGUgcmVsYXRpb25zaGlwLCBpZiBhbnksIGJldHdlZW4gaW5kaXZpZHVhbCBTTlAgbG9jaSBhbmQgZWFjaCBvZiB0aGUgcGhlbm90eXBlcy4gWW914oCZbGwgZmluZCBhIHNtYWxsZXIgdmVyc2lvbiBvZiB0aGUgZGF0YSBzZXQgb24gdGhlIGNvdXJzZSB3ZWIgc2l0ZToKCi0gW2d5cHN5bW90aC5jc3ZdKGh0dHA6Ly9kYXJ3aW4uZWViLnVjb25uLmVkdS9lZWIzNDgtcmVzb3VyY2VzL2d5cHN5bW90aC5jc3YpIEEgQ1NWIGZpbGUgY29udGFpbmluZyBpbmZvcm1hdGlvbiBhYm91dCBlYWNoIGluZGl2aWR1YWwgaW4gdGhlIHNhbXBsZSAocG9wdWxhdGlvbiBvZiBvcmlnaW4sIHNhbXBsZSBsYWJlbCwgcGhlbm90eXBlLCBnZW5vdHlwZSBhdCBlYWNoIFNOUCBsb2N1cykuCgpUaGUgZGF0YSBzZXQgaXMgYSBzdWJzZXQgb2YgdGhlIGRhdGEgaW5jbHVkZWQgaW4gdGhlIHBhcGVyLiBTcGVjaWZpY2FsbHksIEkgZmlsdGVyZWQgdGhlIGRhdGEgdG8gaW5jbHVkZSBvbmx5IChhKSBsb2NpIHRoYXQgd2VyZSBzY29yZWQgaW4gbW9yZSB0aGFuIDEwMCBpbmRpdmlkdWFscywgKGIpIGluZGl2aWR1YWxzIHRoYXQgaGFkIG1vcmUgdGhhbiA0MDAwIG9mIHRoZSByZW1haW5nIFNOUCBsb2NpIHNjb3JlZCwgYW5kIChjKSBsb2NpIHRoYXQgd2VyZSBzY29yZWQgaW4gYWxsIG9mIHRoZSByZW1haW5pbmcgaW5kaXZpZHVhbHMuIFRoZSByZXN1bHRpbmcgZGF0YSBzZXQgaW5jbHVkZXMgMTQxIGluZGl2aWR1YWxzIHNjb3JlZCBhdCAyMTggbG9jaS4gVXNpbmcgdGhlc2UgZGF0YSwgYW5zd2VyIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zOgoKMS4gV2hhdCBsb2NpIChpZiBhbnkpIGFyZSBhc3NvY2lhdGVkIHdpdGggZWFjaCBvZiB0aGUgdGhyZWUgdHJhaXRzIChwdXBhbCBtYXNzLCBwdXBhbCBkZXZlbG9wbWVudCB0aW1lLCBhbmQgdG90YWwgZGV2ZWxvcG1lbnQgdGltZSk/CgogICAgSU1QT1JUQU5UIE5PVEU6IEluIHRoZSBvdXRwdXQsIHRoZSBsb2NpIGFyZSBvcmRlcmVkIGZyb20gdGhlIHN0cm9uZ2VzdCBlc3RpbWF0ZWQgZWZmZWN0IChtZWFuKSB0byB0aGUgd2Vha2VzdCwgYnV0IGxvY2kgc2hvd2luZyBhIHdlYWtlciBlc3RpbWF0ZWQgZWZmZWN0IG1heSBoYXZlIHN0cm9uZ2VyIGV2aWRlbmNlIGZvciB0aGVtLCBpLmUuLCB0aGUgY3JlZGlibGUgaW50ZXJ2YWwgbWF5IG5vdCBvdmVybGFwIHplcm8uIEJlY2F1c2Ugb2YgdGhlIHNtYWxsIHNhbXBsZSBzaXplLCBJIHJlY29tbWVuZCBjb25zaWRlcmluZyBib3RoIHRoZSA4MCUgYW5kIHRoZSA5NSUgY3JlZGlibGUgaW50ZXJ2YWxzIHdoZW5uIGFuc3dlcmluZyB0aGVzZSBxdWVzdGlvbnMuIAoKICAgIEluIHRoZSBleGFtcGxlIGJlbG93IHlvdSdsbCBzZWUgdGhhdCBYNDI5MyBoYXMgdGhlIGxhcmdlc3QgZXN0aW1hdGVkIGVmZmVjdCwgMC4wMTQ1NyBhbmQgdGhhdCB0aGF0IHRoZSA5NSUgY3JlZGlibGUgaW50ZXJ2YWwgb24gdGhpcyBlZmZlY3QgZG9lcyBub3Qgb3ZlcmxhcCB6ZXJvLl5bWW91J2xsIGdldCBzbGlnaHRseSBzbGlnaHRseSBkaWZmZXJlbnQgbnVtYmVycyB0aGFuIHRoZSBvbmVzIGJlbG93LCBiZWNhdXNlIGBicm1gIHVzZXMgTW9udGUtQ2FybG8gc2FtcGxpbmcgdG8gZXN0aW1hdGUgdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24uIFRoZSBkaWZmZXJlbmNlcyBzaG91bGRuJ3QgYWZmZWN0IHRoZSBpbnRlcnByZXRhdGlvbi5dIFRoaXMgc3VnZ2VzdHMgdGhhdCBnZW5vdHlwaWMgZGlmZmVyZW5jZXMgYXQgWDQyOTMgaGF2ZSBhbiBlZmZlY3Qgb24gcHVwYWwgbWFzcy4gTm90aWNlIHRoYXQgdGhlIDgwJSBjcmVkaWJsZSBpbnRlcnZhbHMgZm9yIFgyODQwLCBYNDY4MCwgWDI4NDEsIGFuZCBYNTMzMCBkbyBub3Qgb3ZlcmxhcCB6ZXJvLCBwcm92aWRpbmcgc3VnZ2VzdGl2ZSBldmlkZW5jZSB0aGF0IGRpZmZlcmVuY2VzIGF0IHRoZXNlIGxvY2kgbWF5IGFsc28gYWZmZWN0IHB1cGFsIG1hc3MuIEVzdGltYXRlZCBlZmZlY3RzIGF0IHJlbWFpbmluZyBsb2NpIGFyZSBzbWFsbGVyIGFuZCB0aGUgODAlIGNyZWRpYmxlIGludGVydmFscyBvdmVybGFwIHplcm8gaW5kaWNhdGluZyB0aGF0IHdlIGhhdmUgdmVyeSB3ZWFrIGV2aWRlbmNlIHRoYXQgZGlmZmVyZW5jZXMgYXQgdGhlc2UgbG9jaSBoYXZlIGFuIGVmZmVjdCBvbiBwdXBhbCBtYXNzLl5bSXQncyBpbXBvcnRhbnQgdG8gYmUgdmVyeSBjYXJlZnVsIGluIHlvdXIgY29uY2x1c2lvbiBoZXJlLiBXZSBkbyAqKipub3QqKiogaGF2ZSBldmlkZW5jZSB0aGF0IGRpZmZlcmVuY2VzIGF0IHRoZXNlIGxvY2kgaGF2ZSBubyBlZmZlY3QuIFdpdGggYSBsYXJnZXIgc2FtcGxlIG9mIGluZGl2aWR1YWxzIHdlIG1pZ2h0IGhhdmUgZGV0ZWN0ZWQgYW4gZWZmZWN0IGF0IGFueSBvZiB0aGVzZSBsb2NpLl0KCjIuIElzIHRoZXJlIGV2aWRlbmNlIHRoYXQgYW55IG9mIHRoZSBTTlAgbG9jaSBhcmUgYXNzb2NpYXRlZCB3aXRoIHZhcmlhdGlvbiBpbiBtb3JlIHRoYW4gb25lIG9mIHRoZSB0cmFpdHM/CgozLiBHaXZlbiB5b3VyIGFuc3dlciB0byAjMiwgd291bGQgeW91IGV4cGVjdCB0byBzZWUgYSBjaGFuZ2UgaW4gcHVwYWwgZGV2ZWxvcG1lbnQgdGltZSwgdG90YWwgZGV2ZWxvcG1lbnQgdGltZSwgb3IgYm90aCBpZiBuYXR1cmFsIHNlbGVjdGlvbiBsZWQgdG8gYSBjaGFuZ2UgaW4gcHVwYWwgbWFzcz8gV2h5IG9yIHdoeSBub3Q/CgojIFNob3J0IGV4YW1wbGUgb2YgYW4gYW5hbHlzaXMKCkFmdGVyIHJlYWRpbmcgaW4gdGhlIGRhdGEgZnJvbSBgZ3lwc3ltb3RoLmNzdmAsIHdlIGV4dHJhY3QgdGhlIGdlbm90eXBlIGRhdGEsXltBbGwgb2YgdGhlIGNvbHVtbnMgd2l0aCBnZW5vdHlwZSBpbmZvcm1hdGlvbiBiZWdpbiB3aXRoICJYIiwgYW5kIG5vbmUgb2YgdGhlIG90aGVyIGNvbHVtbnMgZG8uXSBhbmQgdXNlIGBwb3BraW5gIHRvIGVzdGltYXRlIHRoZSByZWxhdGVkbmVzcyBvZiBhbGwgb2YgdGhlIHNhbXBsZXMgZnJvbSB0aGUgZ2Vub3R5cGUgZGF0YS4gVGhlbiB3ZSBydW4gdGhlIGFuYWx5c2lzLCBpbiB0aGlzIGNhc2UgdXNpbmcgTWFzcyBhcyB0aGUgdHJhaXQgYW5kIGxvb2tpbmcgb25seSBhdCBsb2NpIDE6MTAgYW5kIHNlbmRpbmcgdGhlIHJlbGF0ZWRuZXNzIG1hdHJpeCB0byB0aGUgZnVuY3Rpb24gdGhhdCBydW5zIHRoZSBhbmFseXNpcy4gWW91J2xsIG5lZWQgdG8gY2hhbmdlIHRoZSBzZWNvbmQgdG8gbGFzdCBsaW5lIGluIHRoaXMgYmxvY2sgb2YgY29kZSB0byBwaWNrIHRoZSB0cmFpdCBhbmQgdGhlIHJhbmdlIG9mIGxvY2kgdGhhdCB5b3UndmUgYmVlbiBhc3NpZ25lZC4KCllvdSdsbCBmaW5kIGBHV0FTLXJlc3VsdHMuY3N2YCBpbiB0aGUgZGlyZWN0b3J5IHdoZXJlIHlvdSByYW4gdGhlIGFuYWx5c2lzLiBUaGUgbnVtYmVycyB5b3Ugc2VlIGluIGl0IHdpbGwgbWF0Y2ggdGhvc2UgaW4gdGhlIHRhYmxlIHRoYXQncyBkaXNwbGF5ZWQgYWZ0ZXIgdGhlIGFuYWx5c2lzLl5bVGhlIHJlc3VsdHMgdGhhdCBhcmUgZGlzcGxheWVkIGFyZSByb3VuZGVkIHRvIDUgZGlnaXRzLiBUaGUgbnVtYmVycyBpbiB0aGUgYEdXQVMtcmVzdWx0cy5jc3ZgIGluY2x1ZGUgYWxsIGRpZ2l0cyBmb3VuZCBpbiB0aGUgcmVzdWx0cy5dCgpgYGB7ciBtZXNzYWdlID0gRkFMU0V9Cm9wdGlvbnModGlkeXZlcnNlLnF1aWV0ID0gVFJVRSkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocnN0YW4pCmxpYnJhcnkoYnJtcykKbGlicmFyeShwb3BraW4pCgpybShsaXN0ID0gbHMoKSkKCm9wdGlvbnMobWMuY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSkKCmFuYWx5emVfdHJhaXQgPC0gZnVuY3Rpb24oZGF0LCBBLCB0cmFpdCwgbG9jaSwgbl9zYW1wbGVzID0gMjAwMCwgbl9jaGFpbnMgPSA0KSB7CiAgbl9sb2NpIDwtIGxlbmd0aChsb2NpKQogIHBfcmF3IDwtIG1hdHJpeChucm93ID0gbl9sb2NpLCBuY29sID0gbl9jaGFpbnMqbl9zYW1wbGVzLzIpCiAgbWVhbl9wIDwtIG51bWVyaWMobl9sb2NpKQogIG11IDwtIGFycmF5KGRpbSA9IGMobl9sb2NpLCBuX3NhbXBsZXMsIG5yb3coZGF0KSkpCiAgZm9yIChpIGluIDE6bl9sb2NpKSB7CiAgICBsb2N1cyA8LSBjb2xuYW1lcyhkYXQpW2xvY2lbaV0gKyA1XQogICAgY2F0KCJDaGVja2luZyBsb2N1cyAiLCBsb2NpW2ldLCAiICgiLCBsb2N1cywgIilcbiIsIHNlcCA9ICIiKQogICAgZml0IDwtIGJybShwYXN0ZSh0cmFpdCwgbG9jdXMsIHNlcCA9ICIgfiAiKSwKICAgICAgICAgICAgICAgZGF0YSA9IGRhdCwKICAgICAgICAgICAgICAgZGF0YTIgPSBsaXN0KEEgPSBBKSwKICAgICAgICAgICAgICAgZmFtaWx5ID0gZ2F1c3NpYW4oKSwKICAgICAgICAgICAgICAgaXRlciA9IG5fc2FtcGxlcywKICAgICAgICAgICAgICAgcmVmcmVzaCA9IDApCiAgICB4IDwtIGFzLmRhdGEuZnJhbWUoZml0KQogICAgYnJtX2xvY3VzIDwtIHBhc3RlKCJiXyIsIGxvY3VzLCBzZXAgPSIiKQogICAgcF9yYXdbaSxdIDwtIHhbW2JybV9sb2N1c11dCiAgICBtZWFuX3BbaV0gPC0gbWVhbih4W1ticm1fbG9jdXNdXSkKICB9CiAgbG9jaSA8LSBkYXRhLmZyYW1lKGxvY3VzID0gY29sbmFtZXMoZGF0KVsoMTpuX2xvY2kpICsgNV0sCiAgICAgICAgICAgICAgICAgICAgIHBfbWVhbiA9IG1lYW5fcCwKICAgICAgICAgICAgICAgICAgICAgaW5kZXggPSAxOm5fbG9jaSkKICBsb2NpIDwtIGxvY2lbb3JkZXIoYWJzKGxvY2kkcF9tZWFuKSwgZGVjcmVhc2luZyA9IFRSVUUpLCBdCiAgcCA8LSBtYXRyaXgobnJvdyA9IG5fbG9jaSwgbmNvbCA9IG5fY2hhaW5zKm5fc2FtcGxlcy8yKQogIGZvciAoaSBpbiAxOm5fbG9jaSkgewogICAgcFtpLCBdIDwtIHBfcmF3W2xvY2kkaW5kZXhbaV0sXQogIH0KICByb3duYW1lcyhwKSA8LSBsb2NpJGxvY3VzCiAgcmV0dXJuKGxpc3QobG9jaSA9IGxvY2ksIHAgPSBwKSkKfQoKc3VtbWFyaXplX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKHJlc3VsdHMpIHsKICBuX3JlcG9ydCA8LSBucm93KHJlc3VsdHMkbG9jaSkKICBjYXQoIlxuXG4iLAogICAgICAiICAgICAgICAgTWVhbjogKCAgICAyLjUlLCAgICAgIDEwJSwgICAgICA1MCUsICAgICAgOTAlLCAgICA5Ny41JSlcbiIpCiAgZGF0IDwtIHRpYmJsZShMb2N1cyA9IGNoYXJhY3RlcihuX3JlcG9ydCksCiAgICAgICAgICAgICAgICBNZWFuID0gbnVtZXJpYyhuX3JlcG9ydCksIAogICAgICAgICAgICAgICAgYDIuNSVgID0gbnVtZXJpYyhuX3JlcG9ydCksIAogICAgICAgICAgICAgICAgYDEwJWAgPSBudW1lcmljKG5fcmVwb3J0KSwKICAgICAgICAgICAgICAgIGA1MCVgID0gbnVtZXJpYyhuX3JlcG9ydCksIAogICAgICAgICAgICAgICAgYDkwJWAgPSBudW1lcmljKG5fcmVwb3J0KSwgCiAgICAgICAgICAgICAgICBgOTcuNSVgID0gbnVtZXJpYyhuX3JlcG9ydCkpCiAgZm9yIChpIGluIDE6bl9yZXBvcnQpIHsKICAgIGNpIDwtIHF1YW50aWxlKHJlc3VsdHMkcFtyZXN1bHRzJGxvY2kkbG9jdXNbaV0sIF0sCiAgICAgICAgICAgICAgICAgICBjKDAuMDI1LCAwLjEsIDAuNSwgMC45LCAwLjk3NSkpCiAgICBvdXRwdXQgPC0gc3ByaW50ZigiJTZzOiAlOC41ZiAoJTguNWYsICU4LjVmLCAlOC41ZiwgJTguNWYsICU4LjVmKVxuIiwKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMkbG9jaSRsb2N1c1tpXSwKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMkbG9jaSRwX21lYW5baV0sCiAgICAgICAgICAgICAgICAgICAgICBjaVsxXSwgY2lbMl0sIGNpWzNdLCBjaVs0XSwgY2lbNV0pCiAgICBjYXQob3V0cHV0KQogICAgZGF0JExvY3VzW2ldIDwtIHJlc3VsdHMkbG9jaSRsb2N1c1tpXQogICAgZGF0JE1lYW5baV0gPC0gcmVzdWx0cyRsb2NpJHBfbWVhbltpXQogICAgZGF0JGAyLjUlYFtpXSA8LSBjaVsxXQogICAgZGF0JGAxMCVgW2ldIDwtIGNpWzJdCiAgICBkYXQkYDUwJWBbaV0gPC0gY2lbM10KICAgIGRhdCRgOTAlYFtpXSA8LSBjaVs0XQogICAgZGF0JGA5Ny41JWBbaV0gPC0gY2lbNV0KICB9CiAgd3JpdGVfY3N2KGRhdCwgIkdXQVMtcmVzdWx0cy5jc3YiKQp9CgojIyByZWFkIHRoZSBkYXRhCiMjCmRhdCA8LSByZWFkX2NzdigiZ3lwc3ltb3RoLmNzdiIsCiAgICAgICAgICAgICAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQojIyBnZXQgdGhlIHJlbGF0ZWRuZXNzIG1hdHJpeAojIwpnZW5vcyA8LSBkYXQgJT4lIHNlbGVjdChzdGFydHNfd2l0aCgiWCIpKQpBIDwtIHBvcGtpbih0KGFzLm1hdHJpeChnZW5vcykpLAogICAgICAgICAgICBzdWJwb3BzID0gZGF0JHBvcHMpCiMjIGlkZW50aWZ5IHRoZSByb3dzIHdpdGggdGhlIHNhbXBsZSBuYW1lcwojIwpyb3duYW1lcyhBKSA8LSBkYXQkc2FtcGxlCgojIyBDaGFuZ2UgMToxMCB0byBtYXRjaCB0aGUgcmFuZ2Ugb2YgbG9jaSB5b3UgaGF2ZSBiZWVuIGFzc2lnbmVkIGFuZCAiTWFzcyIgdG8gdGhlIAojIyB0cmFpdCB5b3UgaGF2ZSBiZWVuIGFzc2lnbmVkIAojIwpyZXN1bHRzIDwtIGFuYWx5emVfdHJhaXQoZGF0LCBBLCAiTWFzcyIsIDE6MTApCnN1bW1hcml6ZV9hbmFseXNpcyhyZXN1bHRzKQpgYGAKCiMgSG93IEkgcHJlcGFyZWQgdGhlIGRhdGEgZm9yIGFuYWx5c2lzCgpJZiB5b3UncmUgaW50ZXJlc3RlZCBpbiBzZWVpbmcgaG93IEkgcHJlcGFyZWQgdGhlIGRhdGEgZnJvbSB0aGUgdmVyc2lvbiBvbiBEcnlhZC4gSGVyZSdzIHdoYXQgSSBkaWQuIAoKRmlyc3QsIEkgZG93bmxvYWRlZCB0aGUgZGF0YSBmcm9tIERyeWFkOiBbaHR0cHM6Ly9kb2kub3JnLzEwLjUwNjEvZHJ5YWQuOHRzMjg2N10oaHR0cHM6Ly9kb2kub3JnLzEwLjUwNjEvZHJ5YWQuOHRzMjg2NykuIFRoZW4gSSByZW5hbWVkIHRoZSBwaGVub3R5cGUgZmlsZW5hbWUgc28gdGhhdCBpdCBiZWdpbnMgd2l0aCBgZ21gLiBUaGUgcmVzdCBpcyBwcmV0dHkgc3RyYWlnaHRmb3J3YXJkLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBvcGtpbikKCnJtKGxpc3QgPSBscygpKQoKZ2Vub3MgPC0gcmVhZF90c3YoImdtX2dlbm90eXBlc18wMTJfZmluYWxfNTEyMjAxNy50eHQiLAogICAgICAgICAgICAgICAgICBjb2xfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgbmEgPSAiLTEiLAogICAgICAgICAgICAgICAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQpwaGVub3MgPC0gcmVhZF9jc3YoImdtX3BoZW5vdHlwZXMuY3N2IiwKICAgICAgICAgICAgICAgICAgIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCgojIyBrZWVwIG9ubHkgbG9jaSBzY29yZWQgaW4gbW9yZSB0aGFuIDEwMCBpbmRpdmlkdWFscwojIwpuX3Njb3JlZCA8LSBhcHBseShnZW5vcywgMiwgc3VtLCBuYS5ybSA9IFRSVUUpCmdlbm9zIDwtIGdlbm9zWywgbl9zY29yZWQgPiAxMDBdCgojIyBrZWVwIG9ubHkgaW5kaXZpZHVhbHMgc2NvcmVkIGF0IG1vcmUgdGhhbiA0MDAwIGxvY2kKIyMKbl9zY29yZWQgPC0gYXBwbHkoZ2Vub3MsIDEsIHN1bSwgbmEucm0gPSBUUlVFKQpnZW5vcyA8LSBnZW5vc1tuX3Njb3JlZCA+IDQwMDAsIF0KcGhlbm9zIDwtIHBoZW5vc1tuX3Njb3JlZCA+IDQwMDAsIF0KIyMgYW5kIGV4Y2x1ZGUgYW55IHJlbWFpbmluZyBsb2NpIHdoZXJlIG9uZSBvciBtb3JlIGluZGl2aWR1YWxzIGlzbid0IHNjb3JlZAojIwpnZW5vcyA8LSBnZW5vc1ssICFpcy5uYShhcHBseShnZW5vcywgMiwgc3VtKSldCgojIyBzdHJpcCBvZmYgdGhlIGluZGl2aWR1YWwgaWRzIHRvIGlkZW50aWZ5IHBvcHVsYXRpb25zCiMjCnBvcHMgPC0gZ3N1YigiXy4qIiwgIiIsIHBoZW5vcyRzYW1wbGUpCgojIyBiaW5kIHRoZSBwb29wdWxhdGlvbiwgcGhlbm90eXBlLCBhbmQgZ2Vub3R5cGUgaW5mb3JtYXRpb24gdG9nZXRoZXIgYW5kIHNhdmUgaXQgaW4gYSBDU1YgZmlsZQojIwpkYXQgPC0gY2JpbmQocG9wcywgcGhlbm9zLCBnZW5vcykKd3JpdGVfY3N2KGRhdCwgCiAgICAgICAgICBmaWxlID0gImd5cHN5bW90aC5jc3YiKQpgYGAK