Intro/Overview

In this document, we are getting the data that Brendan sent to me into a nice PLINK format.

Along the way we are making a few assumptions, etc. Basically what we are doing is:

  1. Creating a sex-average recombination map from the linkage map we had.
  2. Randomly interspersing unmapped markers from the large SNP panels into positions on the linkage map. (This is obviously an approximation, but it is what I have done in order to simulate physical linkage).
  3. Turn those linkage map distances in cM into megabases, using 1 cM = 1 Mb. I do this basically because I want to pass base pair coordinates to PLINK. Markers that are in the exact same linkage block are just given serial base pair coordinates. Ultimately, when I filter out linked markers, I will be tossing things within “100 Kb” or so, and this lets us do that easily.
  4. Take all the data that are in Genepop format and put them into PLINK and create binary plink filesets that we will save in intermediates/01.
  5. The final products from all this are these files in intermediates/01:

    binary-ns.bed      binary-ns.fam      binary-ns.nosex    binary-west.bim    binary-west.log
    binary-ns.bim      binary-ns.log      binary-west.bed    binary-west.fam    binary-west.nosex
    I will commit these and all future analyses will start with those file.
  6. After making all those I just checked the names of the individuals in the files and confirmed that they looked correct.
  7. After all that, I decided that it was easiest to keep everything in a tidy format, so this also creates compressed output files: tidy-ns.rds and tidy-west.rds in intermediates/01.

Some libraries

library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages --------------------------------------------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats
library(stringr)

Quick looking at the data

First, grab the locus names out of the genotype files and put them in some temporary files:

mkdir data/processed
gzcat data/NSaggregated_RAW.txt.gz | awk '$1 != "Pop" && NR>1 && NF==1' > data/processed/ns_loci.txt  
gzcat data/WestAgged_RAW.txt.gz | awk '$1 != "Pop" && NR>1 && NF==1' > data/processed/west_loci.txt  

Then we can compare that to what we have in the two different files:

ns_snp <- read_table("data/processed/ns_loci.txt", col_names = FALSE)
cols(
  X1 = col_character()
)
west_snp <- read_table("data/processed/west_loci.txt", col_names = FALSE)
cols(
  X1 = col_character()
)
conv <- read_csv("data/Convert_220K_names_to_4.7K_Ilmn-Affy_CommonSNP.csv")
Parsed with column specification:
cols(
  `Illumina V2 ID` = col_character(),
  `Affy 220K ID` = col_character()
)
# linkage map has some commas instead of periods for decimal points
# so we need to fix that. (Also I had to edit the csv file (Mono in a numeric column))
lmap <- read_csv("data/LinkageMap.csv",
                 col_types = cols(`Female map` = col_character(),
                                  `Male map` = col_double())) %>%
  mutate(fem_map = as.numeric(str_replace(`Female map`, ",", ".")))
# so these things don't overlap super well:
dim(ns_snp)
[1] 4359    1
dim(west_snp)
[1] 4367    1
sum(ns_snp$X1 %in% lmap$SNP_ID)
[1] 2862
sum(ns_snp$X1 %in% c(lmap$SNP_ID, conv$`Illumina V2 ID`))
[1] 3636
sum(west_snp$X1 %in% lmap$SNP_ID)
[1] 2876
sum(west_snp$X1 %in% c(lmap$SNP_ID, conv$`Illumina V2 ID`))
[1] 3653

So, we are a long way from having everything on the map.

Making a sex-averaged map, and then sprinkling the unknown SNPs amongst them

Here is what I am going to do—it is an exact science to be sure, but we are interested in seeing what sort of effects physical linkage might have. I willl randomly sprinkle unmapped SNPs around the genome and seeing what the effect is.

Sex-averaged recombination

We will take a simple mean:

sex_aved <- lmap %>%
  mutate(sex_ave_map = (fem_map + `Male map`) / 2)
# and we will store that in the intermediates directory so that we
# can grab it later to tell us which markers were on the map and which
# weren't.
saveRDS(sex_aved, file = "intermediates/01/sex_averaged_map.rds", compress = "xz")

Placing unmapped markers in there

For the markers that are not on the map, for the purposes of simulation, I am going to randomly intersperse them, and then interpolate the map distances, and pretend that is the truth. This is not right, of course, but it will be a good approximation.

To do this, we will write a quick function called sprinkle_into_map: you pass it 1) the sex-averaged map, and 2) a list of all the markers; in turn it retains only the markers from the list. Those markers that do not appear on the map are sorted randomly into the mapped markers and their “pseudo-mapped” positions are given as the average value of the flanking mapped SNPs.

#' make a map with unmapped markers randomly placed in there
#' @param m the data frame with the sex-averaged map
#' @param s the data frame with a column X1 that has the snp names
#' @return a data frame with two new columns: new_chrom and new_map
#' that have what we want.
sprinkle_into_map <- function(m, s) {
  # this just puts them all together (drops mapped SNPs not on the s list)
  t1 <- left_join(s,m, by = c("X1" = "SNP_ID")) %>% 
    arrange(Chromosome, sex_ave_map) %>%
    mutate(new_chrom = Chromosome)
  
  # now we count up how many mapped SNPs are in each chromosome.  We need this
  # info because we are going to sprinkle unmapped SNPs onto chromosomes at a
  # rate proportional to the number of mapped SNPs already on the chromosome.
  chrom_cts <- t1 %>%
    filter(!is.na(Chromosome)) %>%
    count(Chromosome)
  
  # in this ugly step we assign the unmapped SNPs to chromosomes
  t1$new_chrom[is.na(t1$new_chrom)] <- sample(x = chrom_cts$Chromosome, 
                                              size = sum(is.na(t1$new_chrom)), 
                                              replace = TRUE, 
                                              prob = chrom_cts$n)
  
  # now, we sort do a step that gives simulates the order of the unmapped
  # markers---essentially sprinkling them into the existing ones. But we never
  # put an unmapped one on the end.
  t2 <- t1 %>%
    arrange(new_chrom, sex_ave_map) %>%
    group_by(new_chrom) %>%
    mutate(tmp_rank = 1:n(),
           tmp_rank = ifelse(!is.na(sex_ave_map), tmp_rank, sample(x = 2:(sum(!is.na(sex_ave_map)) - 1),
                                                                   size = sum(is.na(sex_ave_map)),
                                                                   replace = TRUE))) %>%
    arrange(new_chrom, tmp_rank)
  
  # and now we just have some NA values in the sex_ave_map column to fill. I will fill them up and 
  # down with the last observed value and then average those, which will have the effect of 
  # filling them in with the mean of the two flanking mapped SNPs
  t3 <- t2 %>%
    mutate(fillup = sex_ave_map,
           filldown = sex_ave_map) %>%
    tidyr::fill(fillup, .direction = "up") %>%
    tidyr::fill(filldown, .direction = "down") %>%
    mutate(new_map = (fillup + filldown) / 2)
  
  # then drop a few columns and return after making pseudo-bp coordinates
  # that correspond to 1 cm = 1 Mb. And if things are in the same linkage
  # group they just get put 1 bp apart
  t3 %>%
    select(-(tmp_rank:filldown)) %>%
    group_by(new_chrom, new_map) %>%
    mutate(pseudo_bp = new_map * 10^6 + 1:n()) %>%
    ungroup()
  
}

So, let’s sprinkle! We set seeds so we get the same results each time.

set.seed(123)
ns_new <- sprinkle_into_map(sex_aved, ns_snp)
set.seed(456)
west_new <- sprinkle_into_map(sex_aved, west_snp)
LS0tCnRpdGxlOiAiSW5pdGlhbCBEYXRhIE1hbmV1dmVycyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCi0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIHNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgYWx3YXlzIHRvIHRoZSBwcm9qZWN0IGRpcmVjdG9yeSAob25lIGxldmVsIHVwKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IG5vcm1hbGl6ZVBhdGgocnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpKSAKYGBgCgoKIyMgSW50cm8vT3ZlcnZpZXcKCkluIHRoaXMgZG9jdW1lbnQsIHdlIGFyZSBnZXR0aW5nIHRoZSBkYXRhIHRoYXQgQnJlbmRhbiBzZW50IHRvIG1lIGludG8gYSBuaWNlIFBMSU5LIGZvcm1hdC4KCkFsb25nIHRoZSB3YXkgd2UgYXJlIG1ha2luZyBhIGZldyBhc3N1bXB0aW9ucywgZXRjLiBCYXNpY2FsbHkgd2hhdCB3ZSBhcmUgZG9pbmcgaXM6CgoxLiBDcmVhdGluZyBhIHNleC1hdmVyYWdlIHJlY29tYmluYXRpb24gbWFwIGZyb20gdGhlIGxpbmthZ2UgbWFwIHdlIGhhZC4KMi4gUmFuZG9tbHkgaW50ZXJzcGVyc2luZyB1bm1hcHBlZCBtYXJrZXJzIGZyb20gdGhlIGxhcmdlIFNOUCBwYW5lbHMgaW50byBwb3NpdGlvbnMgCm9uIHRoZSBsaW5rYWdlIG1hcC4gIChUaGlzIGlzIG9idmlvdXNseSBhbiBhcHByb3hpbWF0aW9uLCBidXQgaXQgaXMgd2hhdCBJIGhhdmUgZG9uZSBpbgpvcmRlciB0byBzaW11bGF0ZSBwaHlzaWNhbCBsaW5rYWdlKS4KMy4gVHVybiB0aG9zZSBsaW5rYWdlIG1hcCBkaXN0YW5jZXMgaW4gY00gaW50byBtZWdhYmFzZXMsIHVzaW5nIDEgY00gPSAxIE1iLiAgSSBkbyB0aGlzCmJhc2ljYWxseSBiZWNhdXNlIEkgd2FudCB0byBwYXNzIGJhc2UgcGFpciBjb29yZGluYXRlcyB0byBQTElOSy4gIE1hcmtlcnMgdGhhdCBhcmUgaW4gdGhlIGV4YWN0CnNhbWUgbGlua2FnZSBibG9jayBhcmUganVzdCBnaXZlbiBzZXJpYWwgYmFzZSBwYWlyIGNvb3JkaW5hdGVzLiAgVWx0aW1hdGVseSwgd2hlbiBJIGZpbHRlciBvdXQKbGlua2VkIG1hcmtlcnMsIEkgd2lsbCBiZSB0b3NzaW5nIHRoaW5ncyB3aXRoaW4gIjEwMCBLYiIgb3Igc28sIGFuZCB0aGlzIGxldHMgdXMgZG8gdGhhdCAKZWFzaWx5Lgo0LiBUYWtlIGFsbCB0aGUgZGF0YSB0aGF0IGFyZSBpbiBHZW5lcG9wIGZvcm1hdCBhbmQgcHV0IHRoZW0gaW50byBQTElOSyBhbmQgY3JlYXRlCmJpbmFyeSBwbGluayBmaWxlc2V0cyB0aGF0IHdlIHdpbGwgc2F2ZSBpbiBgaW50ZXJtZWRpYXRlcy8wMWAuCjUuIFRoZSBmaW5hbCBwcm9kdWN0cyBmcm9tIGFsbCB0aGlzIGFyZSB0aGVzZSBmaWxlcyBpbiBgaW50ZXJtZWRpYXRlcy8wMWA6CiAgICBgYGAKYmluYXJ5LW5zLmJlZCAgICAgIGJpbmFyeS1ucy5mYW0gICAgICBiaW5hcnktbnMubm9zZXggICAgYmluYXJ5LXdlc3QuYmltICAgIGJpbmFyeS13ZXN0LmxvZwpiaW5hcnktbnMuYmltICAgICAgYmluYXJ5LW5zLmxvZyAgICAgIGJpbmFyeS13ZXN0LmJlZCAgICBiaW5hcnktd2VzdC5mYW0gICAgYmluYXJ5LXdlc3Qubm9zZXgKICAgIGBgYApJIHdpbGwgY29tbWl0IHRoZXNlIGFuZCBhbGwgZnV0dXJlIGFuYWx5c2VzIHdpbGwgc3RhcnQgd2l0aCB0aG9zZSBmaWxlLgo2LiBBZnRlciBtYWtpbmcgYWxsIHRob3NlIEkganVzdCBjaGVja2VkIHRoZSBuYW1lcyBvZiB0aGUgaW5kaXZpZHVhbHMgaW4gdGhlIGZpbGVzIGFuZCBjb25maXJtZWQgdGhhdAp0aGV5IGxvb2tlZCBjb3JyZWN0Lgo3LiBBZnRlciBhbGwgdGhhdCwgSSBkZWNpZGVkIHRoYXQgaXQgd2FzIGVhc2llc3QgdG8ga2VlcCBldmVyeXRoaW5nIGluIGEgdGlkeSBmb3JtYXQsIHNvIHRoaXMgYWxzbyBjcmVhdGVzCmNvbXByZXNzZWQgb3V0cHV0IGZpbGVzOiBgdGlkeS1ucy5yZHNgIGFuZCBgdGlkeS13ZXN0LnJkc2AgaW4gYGludGVybWVkaWF0ZXMvMDFgLgoKCgpTb21lIGxpYnJhcmllcwpgYGB7ciBsb2FkLWxpYnN9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHN0cmluZ3IpCmBgYAojIyBRdWljayBsb29raW5nIGF0IHRoZSBkYXRhCgpGaXJzdCwgZ3JhYiB0aGUgbG9jdXMgbmFtZXMgb3V0IG9mIHRoZSBnZW5vdHlwZSBmaWxlcyBhbmQgcHV0IHRoZW0gaW4gCnNvbWUgdGVtcG9yYXJ5IGZpbGVzOgpgYGB7c2ggbG9jLW5hbWVzfQpta2RpciBkYXRhL3Byb2Nlc3NlZApnemNhdCBkYXRhL05TYWdncmVnYXRlZF9SQVcudHh0Lmd6IHwgYXdrICckMSAhPSAiUG9wIiAmJiBOUj4xICYmIE5GPT0xJyA+IGRhdGEvcHJvY2Vzc2VkL25zX2xvY2kudHh0ICAKZ3pjYXQgZGF0YS9XZXN0QWdnZWRfUkFXLnR4dC5neiB8IGF3ayAnJDEgIT0gIlBvcCIgJiYgTlI+MSAmJiBORj09MScgPiBkYXRhL3Byb2Nlc3NlZC93ZXN0X2xvY2kudHh0ICAKYGBgCgpUaGVuIHdlIGNhbiBjb21wYXJlIHRoYXQgdG8gd2hhdCB3ZSBoYXZlIGluIHRoZSB0d28gZGlmZmVyZW50IGZpbGVzOgpgYGB7ciByZWFkLXNucC1kYXR9Cm5zX3NucCA8LSByZWFkX3RhYmxlKCJkYXRhL3Byb2Nlc3NlZC9uc19sb2NpLnR4dCIsIGNvbF9uYW1lcyA9IEZBTFNFKQp3ZXN0X3NucCA8LSByZWFkX3RhYmxlKCJkYXRhL3Byb2Nlc3NlZC93ZXN0X2xvY2kudHh0IiwgY29sX25hbWVzID0gRkFMU0UpCgoKY29udiA8LSByZWFkX2NzdigiZGF0YS9Db252ZXJ0XzIyMEtfbmFtZXNfdG9fNC43S19JbG1uLUFmZnlfQ29tbW9uU05QLmNzdiIpCgojIGxpbmthZ2UgbWFwIGhhcyBzb21lIGNvbW1hcyBpbnN0ZWFkIG9mIHBlcmlvZHMgZm9yIGRlY2ltYWwgcG9pbnRzCiMgc28gd2UgbmVlZCB0byBmaXggdGhhdC4gKEFsc28gSSBoYWQgdG8gZWRpdCB0aGUgY3N2IGZpbGUgKE1vbm8gaW4gYSBudW1lcmljIGNvbHVtbikpCmxtYXAgPC0gcmVhZF9jc3YoImRhdGEvTGlua2FnZU1hcC5jc3YiLAogICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoYEZlbWFsZSBtYXBgID0gY29sX2NoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE1hbGUgbWFwYCA9IGNvbF9kb3VibGUoKSkpICU+JQogIG11dGF0ZShmZW1fbWFwID0gYXMubnVtZXJpYyhzdHJfcmVwbGFjZShgRmVtYWxlIG1hcGAsICIsIiwgIi4iKSkpCgojIHNvIHRoZXNlIHRoaW5ncyBkb24ndCBvdmVybGFwIHN1cGVyIHdlbGw6CmRpbShuc19zbnApCmRpbSh3ZXN0X3NucCkKCnN1bShuc19zbnAkWDEgJWluJSBsbWFwJFNOUF9JRCkKc3VtKG5zX3NucCRYMSAlaW4lIGMobG1hcCRTTlBfSUQsIGNvbnYkYElsbHVtaW5hIFYyIElEYCkpCgpzdW0od2VzdF9zbnAkWDEgJWluJSBsbWFwJFNOUF9JRCkKc3VtKHdlc3Rfc25wJFgxICVpbiUgYyhsbWFwJFNOUF9JRCwgY29udiRgSWxsdW1pbmEgVjIgSURgKSkKYGBgCgpTbywgd2UgYXJlIGEgbG9uZyB3YXkgZnJvbSBoYXZpbmcgZXZlcnl0aGluZyBvbiB0aGUgbWFwLgoKIyMgTWFraW5nIGEgc2V4LWF2ZXJhZ2VkIG1hcCwgYW5kIHRoZW4gc3ByaW5rbGluZyB0aGUgdW5rbm93biBTTlBzIGFtb25nc3QgdGhlbQoKSGVyZSBpcyB3aGF0IEkgYW0gZ29pbmcgdG8gZG8tLS1pdCBpcyBhbiBleGFjdCBzY2llbmNlIHRvIGJlIHN1cmUsIGJ1dCB3ZSBhcmUgaW50ZXJlc3RlZCBpbgpzZWVpbmcgd2hhdCBzb3J0IG9mIGVmZmVjdHMgcGh5c2ljYWwgbGlua2FnZSBtaWdodCBoYXZlLiAgSSB3aWxsbCByYW5kb21seSBzcHJpbmtsZSB1bm1hcHBlZApTTlBzIGFyb3VuZCB0aGUgZ2Vub21lIGFuZCBzZWVpbmcgd2hhdCB0aGUgZWZmZWN0IGlzLgoKIyMjIFNleC1hdmVyYWdlZCByZWNvbWJpbmF0aW9uCgpXZSB3aWxsIHRha2UgYSBzaW1wbGUgbWVhbjoKYGBge3Igc2V4LWF2ZX0Kc2V4X2F2ZWQgPC0gbG1hcCAlPiUKICBtdXRhdGUoc2V4X2F2ZV9tYXAgPSAoZmVtX21hcCArIGBNYWxlIG1hcGApIC8gMikKCiMgYW5kIHdlIHdpbGwgc3RvcmUgdGhhdCBpbiB0aGUgaW50ZXJtZWRpYXRlcyBkaXJlY3Rvcnkgc28gdGhhdCB3ZQojIGNhbiBncmFiIGl0IGxhdGVyIHRvIHRlbGwgdXMgd2hpY2ggbWFya2VycyB3ZXJlIG9uIHRoZSBtYXAgYW5kIHdoaWNoCiMgd2VyZW4ndC4Kc2F2ZVJEUyhzZXhfYXZlZCwgZmlsZSA9ICJpbnRlcm1lZGlhdGVzLzAxL3NleF9hdmVyYWdlZF9tYXAucmRzIiwgY29tcHJlc3MgPSAieHoiKQpgYGAKCiMjIyBQbGFjaW5nIHVubWFwcGVkIG1hcmtlcnMgaW4gdGhlcmUKCkZvciB0aGUgbWFya2VycyB0aGF0IGFyZSBub3Qgb24gdGhlIG1hcCwgZm9yIHRoZSBwdXJwb3NlcyBvZiBzaW11bGF0aW9uLCBJIGFtIGdvaW5nCnRvIHJhbmRvbWx5IGludGVyc3BlcnNlIHRoZW0sIGFuZCB0aGVuIGludGVycG9sYXRlIHRoZSBtYXAgZGlzdGFuY2VzLCBhbmQgcHJldGVuZCB0aGF0IGlzIAp0aGUgdHJ1dGguICBUaGlzIGlzIG5vdCByaWdodCwgb2YgY291cnNlLCBidXQgaXQgd2lsbCBiZSBhIGdvb2QgYXBwcm94aW1hdGlvbi4KClRvIGRvIHRoaXMsIHdlIHdpbGwgd3JpdGUgYSBxdWljayBmdW5jdGlvbiBjYWxsZWQgYHNwcmlua2xlX2ludG9fbWFwYDogeW91IHBhc3MgaXQgMSkgdGhlIHNleC1hdmVyYWdlZCBtYXAsCmFuZCAyKSBhIGxpc3Qgb2YgYWxsIHRoZSBtYXJrZXJzOyBpbiB0dXJuIGl0IHJldGFpbnMgb25seSB0aGUgbWFya2VycyBmcm9tIHRoZSBsaXN0LiBUaG9zZSBtYXJrZXJzIHRoYXQgCmRvIG5vdCBhcHBlYXIgb24gdGhlIG1hcCBhcmUgc29ydGVkIHJhbmRvbWx5IGludG8gdGhlIG1hcHBlZCBtYXJrZXJzIGFuZCB0aGVpciAicHNldWRvLW1hcHBlZCIgcG9zaXRpb25zCmFyZSBnaXZlbiBhcyB0aGUgYXZlcmFnZSB2YWx1ZSBvZiB0aGUgZmxhbmtpbmcgbWFwcGVkIFNOUHMuCgpgYGB7ciBzcHJpbmtsZS1mdW5jfQojJyBtYWtlIGEgbWFwIHdpdGggdW5tYXBwZWQgbWFya2VycyByYW5kb21seSBwbGFjZWQgaW4gdGhlcmUKIycgQHBhcmFtIG0gdGhlIGRhdGEgZnJhbWUgd2l0aCB0aGUgc2V4LWF2ZXJhZ2VkIG1hcAojJyBAcGFyYW0gcyB0aGUgZGF0YSBmcmFtZSB3aXRoIGEgY29sdW1uIFgxIHRoYXQgaGFzIHRoZSBzbnAgbmFtZXMKIycgQHJldHVybiBhIGRhdGEgZnJhbWUgd2l0aCB0d28gbmV3IGNvbHVtbnM6IG5ld19jaHJvbSBhbmQgbmV3X21hcAojJyB0aGF0IGhhdmUgd2hhdCB3ZSB3YW50LgpzcHJpbmtsZV9pbnRvX21hcCA8LSBmdW5jdGlvbihtLCBzKSB7CiAgIyB0aGlzIGp1c3QgcHV0cyB0aGVtIGFsbCB0b2dldGhlciAoZHJvcHMgbWFwcGVkIFNOUHMgbm90IG9uIHRoZSBzIGxpc3QpCiAgdDEgPC0gbGVmdF9qb2luKHMsbSwgYnkgPSBjKCJYMSIgPSAiU05QX0lEIikpICU+JSAKICAgIGFycmFuZ2UoQ2hyb21vc29tZSwgc2V4X2F2ZV9tYXApICU+JQogICAgbXV0YXRlKG5ld19jaHJvbSA9IENocm9tb3NvbWUpCiAgCiAgIyBub3cgd2UgY291bnQgdXAgaG93IG1hbnkgbWFwcGVkIFNOUHMgYXJlIGluIGVhY2ggY2hyb21vc29tZS4gIFdlIG5lZWQgdGhpcwogICMgaW5mbyBiZWNhdXNlIHdlIGFyZSBnb2luZyB0byBzcHJpbmtsZSB1bm1hcHBlZCBTTlBzIG9udG8gY2hyb21vc29tZXMgYXQgYQogICMgcmF0ZSBwcm9wb3J0aW9uYWwgdG8gdGhlIG51bWJlciBvZiBtYXBwZWQgU05QcyBhbHJlYWR5IG9uIHRoZSBjaHJvbW9zb21lLgogIGNocm9tX2N0cyA8LSB0MSAlPiUKICAgIGZpbHRlcighaXMubmEoQ2hyb21vc29tZSkpICU+JQogICAgY291bnQoQ2hyb21vc29tZSkKICAKICAjIGluIHRoaXMgdWdseSBzdGVwIHdlIGFzc2lnbiB0aGUgdW5tYXBwZWQgU05QcyB0byBjaHJvbW9zb21lcwogIHQxJG5ld19jaHJvbVtpcy5uYSh0MSRuZXdfY2hyb20pXSA8LSBzYW1wbGUoeCA9IGNocm9tX2N0cyRDaHJvbW9zb21lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBzdW0oaXMubmEodDEkbmV3X2Nocm9tKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZSA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYiA9IGNocm9tX2N0cyRuKQogIAogICMgbm93LCB3ZSBzb3J0IGRvIGEgc3RlcCB0aGF0IGdpdmVzIHNpbXVsYXRlcyB0aGUgb3JkZXIgb2YgdGhlIHVubWFwcGVkCiAgIyBtYXJrZXJzLS0tZXNzZW50aWFsbHkgc3ByaW5rbGluZyB0aGVtIGludG8gdGhlIGV4aXN0aW5nIG9uZXMuIEJ1dCB3ZSBuZXZlcgogICMgcHV0IGFuIHVubWFwcGVkIG9uZSBvbiB0aGUgZW5kLgogIHQyIDwtIHQxICU+JQogICAgYXJyYW5nZShuZXdfY2hyb20sIHNleF9hdmVfbWFwKSAlPiUKICAgIGdyb3VwX2J5KG5ld19jaHJvbSkgJT4lCiAgICBtdXRhdGUodG1wX3JhbmsgPSAxOm4oKSwKICAgICAgICAgICB0bXBfcmFuayA9IGlmZWxzZSghaXMubmEoc2V4X2F2ZV9tYXApLCB0bXBfcmFuaywgc2FtcGxlKHggPSAyOihzdW0oIWlzLm5hKHNleF9hdmVfbWFwKSkgLSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBzdW0oaXMubmEoc2V4X2F2ZV9tYXApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSBUUlVFKSkpICU+JQogICAgYXJyYW5nZShuZXdfY2hyb20sIHRtcF9yYW5rKQogIAogICMgYW5kIG5vdyB3ZSBqdXN0IGhhdmUgc29tZSBOQSB2YWx1ZXMgaW4gdGhlIHNleF9hdmVfbWFwIGNvbHVtbiB0byBmaWxsLiBJIHdpbGwgZmlsbCB0aGVtIHVwIGFuZCAKICAjIGRvd24gd2l0aCB0aGUgbGFzdCBvYnNlcnZlZCB2YWx1ZSBhbmQgdGhlbiBhdmVyYWdlIHRob3NlLCB3aGljaCB3aWxsIGhhdmUgdGhlIGVmZmVjdCBvZiAKICAjIGZpbGxpbmcgdGhlbSBpbiB3aXRoIHRoZSBtZWFuIG9mIHRoZSB0d28gZmxhbmtpbmcgbWFwcGVkIFNOUHMKICB0MyA8LSB0MiAlPiUKICAgIG11dGF0ZShmaWxsdXAgPSBzZXhfYXZlX21hcCwKICAgICAgICAgICBmaWxsZG93biA9IHNleF9hdmVfbWFwKSAlPiUKICAgIHRpZHlyOjpmaWxsKGZpbGx1cCwgLmRpcmVjdGlvbiA9ICJ1cCIpICU+JQogICAgdGlkeXI6OmZpbGwoZmlsbGRvd24sIC5kaXJlY3Rpb24gPSAiZG93biIpICU+JQogICAgbXV0YXRlKG5ld19tYXAgPSAoZmlsbHVwICsgZmlsbGRvd24pIC8gMikKICAKICAjIHRoZW4gZHJvcCBhIGZldyBjb2x1bW5zIGFuZCByZXR1cm4gYWZ0ZXIgbWFraW5nIHBzZXVkby1icCBjb29yZGluYXRlcwogICMgdGhhdCBjb3JyZXNwb25kIHRvIDEgY20gPSAxIE1iLiBBbmQgaWYgdGhpbmdzIGFyZSBpbiB0aGUgc2FtZSBsaW5rYWdlCiAgIyBncm91cCB0aGV5IGp1c3QgZ2V0IHB1dCAxIGJwIGFwYXJ0CiAgdDMgJT4lCiAgICBzZWxlY3QoLSh0bXBfcmFuazpmaWxsZG93bikpICU+JQogICAgZ3JvdXBfYnkobmV3X2Nocm9tLCBuZXdfbWFwKSAlPiUKICAgIG11dGF0ZShwc2V1ZG9fYnAgPSBuZXdfbWFwICogMTBeNiArIDE6bigpKSAlPiUKICAgIHVuZ3JvdXAoKQogIAp9CgpgYGAKClNvLCBsZXQncyBzcHJpbmtsZSEgV2Ugc2V0IHNlZWRzIHNvIHdlIGdldCB0aGUgc2FtZSByZXN1bHRzIGVhY2ggdGltZS4KYGBge3IgZG8tc3ByaW5rbGV9CnNldC5zZWVkKDEyMykKbnNfbmV3IDwtIHNwcmlua2xlX2ludG9fbWFwKHNleF9hdmVkLCBuc19zbnApCgpzZXQuc2VlZCg0NTYpCndlc3RfbmV3IDwtIHNwcmlua2xlX2ludG9fbWFwKHNleF9hdmVkLCB3ZXN0X3NucCkKYGBgCgoKIyMgQ29udmVydGluZyBHZW5vdHlwZSBEYXRhIHRvIFBMSU5LIGZvcm1hdCAKCiMjIyBQbGluayAucGVkIGFuZCAubWFwIGZpbGVzCk5vdyB0aGF0IHdlIGhhdmUgYSAocHNldWRvKS1saW5rYWdlIG1hcCBmb3IgYWxsIHRoZSBtYXJrZXJzIHdlIGNhbiBleHRyYWN0IHRoZSBnZW5vdHlwZXMgZnJvbSB0aGUgR2VuZXBvcCBmaWxlcyAKYW5kIGNvbnZlcnQgaXQgYWxsIGludG8gUExJTksgZm9ybWF0LiBJIHdpbGwgZG8gdGhpcyB3aXRoIGBhd2tgLiBSZWNhbGwgdGhhdCB3ZSBnb3QgdGhlIGxvY3VzIG5hbWVzIGZyb20gdGhvc2UgCmZpbGVzIGVhcmxpZXIuICBOb3RlIHRoYXQgd2UgYXJlIGxpa2VseSBnb2luZyB0byBoYXZlIHRvIHJlb3JkZXIgdGhlIG1hcmtlcnMgaW4gdGhlc2UgZmlsZXMgc28gdGhhdCB0aGV5IGNvcnJlc3BvbmQKdG8gdGhlIHNwcmlua2xlZCBtYXJrZXIgb3JkZXIuCmBgYHtzaCBtYWtlLXBsaW5rLXBlZH0KbWtkaXIgZGF0YS9wcm9jZXNzZWQKCmd6Y2F0IGRhdGEvTlNhZ2dyZWdhdGVkX1JBVy50eHQuZ3ogfCBhd2sgJ05GPjEwMCB7cHJpbnRmKCIlcyAlcyAwIDAgMCAtOSIsICQxLCAkMSk7IGZvcihpPTM7aTw9TkY7aSsrKSBwcmludGYoIiAlZCAlZCIsIHN1YnN0cigkaSwgMSwgMyksIHN1YnN0cigkaSwgNCwgMykpOyBwcmludGYoIlxuIik7fScgID4gZGF0YS9wcm9jZXNzZWQvdG1wX25zLnBlZAoKZ3pjYXQgZGF0YS9XZXN0QWdnZWRfUkFXLnR4dC5neiB8IGF3ayAnTkY+MTAwIHtwcmludGYoIiVzICVzIDAgMCAwIC05IiwgJDEsICQxKTsgZm9yKGk9MztpPD1ORjtpKyspIHByaW50ZigiICVkICVkIiwgc3Vic3RyKCRpLCAxLCAzKSwgc3Vic3RyKCRpLCA0LCAzKSk7IHByaW50ZigiXG4iKTt9JyAgPiBkYXRhL3Byb2Nlc3NlZC90bXBfd2VzdC5wZWQKCmBgYApOb3csIHdlIGhhdmUgdG8gbWFrZSB0aGUgLm1hcCBmaWxlcy4gIFRoaXMgc2hvdWxkIGluY2x1ZGUgb25seSB0aG9zZSBtYXJrZXJzIHRoYXQgYXJlIGFjdHVhbGx5IGluIHRoZQpHZW5lUG9wIGZpbGVzIGFuZCB3aGljaCBhcmUgaW4gdGhlIHNwcmlua2xlZCBtYXBzLCBhbmQgd2Ugd2FudCB0aGVtIHRvIGJlIGluIG1hcCBvcmRlci4KYGBge3IgbWFrZS1tYXAtZmlsZXN9Cm5zX2xvY2kgPC0gc2NhbigiZGF0YS9wcm9jZXNzZWQvbnNfbG9jaS50eHQiLCB3aGF0ID0gImNoYXJhY3RlciIpCgpuc19wbGlua19tYXAgPC0gbnNfbmV3ICU+JQogIG11dGF0ZShtYXBfZHVtbXkgPSAwKSAlPiUKICByZW5hbWUoVmFyaWFudCA9IFgxKSAlPiUKICBzZWxlY3QobmV3X2Nocm9tLCBWYXJpYW50LCBtYXBfZHVtbXksIHBzZXVkb19icCkgJT4lCiAgZmlsdGVyKFZhcmlhbnQgJWluJSBuc19sb2NpKQoKCndlc3RfbG9jaSA8LSBzY2FuKCJkYXRhL3Byb2Nlc3NlZC93ZXN0X2xvY2kudHh0Iiwgd2hhdCA9ICJjaGFyYWN0ZXIiKQoKd2VzdF9wbGlua19tYXAgPC0gd2VzdF9uZXcgJT4lCiAgbXV0YXRlKG1hcF9kdW1teSA9IDApICU+JQogIHJlbmFtZShWYXJpYW50ID0gWDEpICU+JQogIHNlbGVjdChuZXdfY2hyb20sIFZhcmlhbnQsIG1hcF9kdW1teSwgcHNldWRvX2JwKSAlPiUKICBmaWx0ZXIoVmFyaWFudCAlaW4lIHdlc3RfbG9jaSkKYGBgCk5vdyB0aGF0IHdlIGhhdmUgdGhvc2UgcHNldWRvLW1hcCBmaWxlcywgd2UgYXJlIGdvaW5nIHRvIG5lZWQgdG8gcHV0IHRoZSBsb2NpIGluIHRoZQpyaWdodCBvcmRlciBpbiB0aGUgUExJTksgZmlsZXMuIFdlIHdpbGwganVzdCByZWFkIGl0IGFsbCBpbnRvIGEgdGJsX2RmKCkgYW5kCnRoZW4gc2VsZWN0IHRoZSBjb2x1bW5zIHRvIGJlIGluIHRoZSByaWdodCBvcmRlcjoKYGBge3IgcmVvcmRlci1tYXJrZXJzfQppZF9jb2xzIDwtIHBhc3RlKCJpZCIsIDE6Niwgc2VwID0gIiIpCgpuc19jb2xzIDwtIHBhc3RlKHJlcChuc19sb2NpLCBlYWNoID0gMiksIDE6Miwgc2VwID0gIl8iKQpuc19jb2xzX3Jlb3JkZXJlZCA8LSBwYXN0ZShyZXAobnNfcGxpbmtfbWFwJFZhcmlhbnQsIGVhY2ggPSAyKSwgMToyLCBzZXAgPSAiXyIpCgpuc2RmIDwtIHJlYWRfZGVsaW0oImRhdGEvcHJvY2Vzc2VkL3RtcF9ucy5wZWQiLCBkZWxpbSA9ICIgIiwgY29sX25hbWVzID0gYyhpZF9jb2xzLCBuc19jb2xzKSwgcHJvZ3Jlc3MgPSBGQUxTRSkKbnNfcmVvcmRlcmVkIDwtIG5zZGZbLCBjKGlkX2NvbHMsIG5zX2NvbHNfcmVvcmRlcmVkKV0KIAoKCndlc3RfY29scyA8LSBwYXN0ZShyZXAod2VzdF9sb2NpLCBlYWNoID0gMiksIDE6Miwgc2VwID0gIl8iKQp3ZXN0X2NvbHNfcmVvcmRlcmVkIDwtIHBhc3RlKHJlcCh3ZXN0X3BsaW5rX21hcCRWYXJpYW50LCBlYWNoID0gMiksIDE6Miwgc2VwID0gIl8iKQoKd2VzdGRmIDwtIHJlYWRfZGVsaW0oImRhdGEvcHJvY2Vzc2VkL3RtcF93ZXN0LnBlZCIsIGRlbGltID0gIiAiLCBjb2xfbmFtZXMgPSBjKGlkX2NvbHMsIHdlc3RfY29scyksIHByb2dyZXNzID0gRkFMU0UpCndlc3RfcmVvcmRlcmVkIDwtIHdlc3RkZlssIGMoaWRfY29scywgd2VzdF9jb2xzX3Jlb3JkZXJlZCldIAoKIyBhbmQsIGhhdmluZyBnb3R0ZW4gdGhvc2UgdG9nZXRoZXIsIHdlIGNhbiBub3cgd3JpdGUgdGhlbSB0byBQTElOSyAucGVkIGZpbGVzCndyaXRlX2RlbGltKG5zX3Jlb3JkZXJlZCwgcGF0aCA9ICJkYXRhL3Byb2Nlc3NlZC9ucy5wZWQiLCBkZWxpbSA9ICIgIiwgY29sX25hbWVzID0gRkFMU0UpCndyaXRlX2RlbGltKHdlc3RfcmVvcmRlcmVkLCBwYXRoID0gImRhdGEvcHJvY2Vzc2VkL3dlc3QucGVkIiwgZGVsaW0gPSAiICIsIGNvbF9uYW1lcyA9IEZBTFNFKQoKIyBhbmQgd2Ugd3JpdGUgb3V0IHRoZSAubWFwIGZpbGVzIGFzIHdlbGwKd3JpdGVfZGVsaW0obnNfcGxpbmtfbWFwLCBwYXRoID0gImRhdGEvcHJvY2Vzc2VkL25zLm1hcCIsIGRlbGltID0gIlx0IiwgY29sX25hbWVzID0gRkFMU0UpCndyaXRlX2RlbGltKHdlc3RfcGxpbmtfbWFwLCBwYXRoID0gImRhdGEvcHJvY2Vzc2VkL3dlc3QubWFwIiwgZGVsaW0gPSAiXHQiLCBjb2xfbmFtZXMgPSBGQUxTRSkKCmBgYAoKCiMjIyBNYWtpbmcgUGxpbmsgYmluYXJ5IGZpbGVzZXRzCgpUaGVzZSB3aWxsIGJlIHRoZSAiZmluYWwgcHJvZHVjdHMiIGZyb20gdGhpcyBwYXJ0aWN1bGFyIFJtZC4gIFdlIHdpbGwgc3RvcmUgdGhlbQppbiBgaW50ZXJtZWRpYXRlc2Agc28gdGhhdCB3ZSBjYW4gd29yayBmcm9tIHRoZW0gZGlyZWN0bHkgbGF0ZXIgb24uICBGb3IgdGhpcyB0bwp3b3JrIHlvdSBhcmUgZ29ubmEgbmVlZCBgcGxpbmtgIG9uIHlvdXIgc3lzdGVtIHBhdGggc29tZXdoZXJlLgpgYGB7c2ggYmluYXJ5LWZpbGVzZXRzfQpta2RpciAtcCBpbnRlcm1lZGlhdGVzLzAxCmNkIGRhdGEvcHJvY2Vzc2VkCgpwbGluayAtLWZpbGUgbnMgLS1hZWMgLS1tYWtlLWJlZCAtLW91dCAuLi8uLi9pbnRlcm1lZGlhdGVzLzAxL2JpbmFyeS1ucyA+IC9kZXYvbnVsbCAyPiYxCnBsaW5rIC0tZmlsZSB3ZXN0IC0tYWVjIC0tbWFrZS1iZWQgLS1vdXQgLi4vLi4vaW50ZXJtZWRpYXRlcy8wMS9iaW5hcnktd2VzdCA+IC9kZXYvbnVsbCAyPiYxCmBgYAoKCiMjIyBDaGVja2luZyB0aGUgTmFtZXMgb2YgaW5kaXZpZHVhbHMKCgpBbmQgbm93IHdlIGNhbiByZWFkIGluIHRoZSBuYW1lcyBvZiBpbmRpdmlkdWFscyBhbmQgZ2V0IHJlYWR5IHRvIHNlbGVjdCBkaWZmZXJlbnQgc3Vic2V0cy4KYGBge3IgcmVhZC1uYW1lc30KbnNfbmFtZXMgPC0gcmVhZF9kZWxpbSgiaW50ZXJtZWRpYXRlcy8wMS9iaW5hcnktbnMuZmFtIiwgZGVsaW0gPSAiICIsIGNvbF9uYW1lcyA9IEZBTFNFKSAlPiUKICBtdXRhdGUoZ3JvdXAgPSBzdHJfc3ViKFgxLCAxLCAzKSkKd2VzdF9uYW1lcyA8LSByZWFkX2RlbGltKCJpbnRlcm1lZGlhdGVzLzAxL2JpbmFyeS13ZXN0LmZhbSIsIGRlbGltID0gIiAiLCBjb2xfbmFtZXMgPSBGQUxTRSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gc3RyX3N1YihYMSwgMSwgMykpCgpgYGAKQW5kIGhlcmUgd2UganVzdCBvZmZlciBhIGxpdHRsZSBzdW1tYXJ5IG9mIHRoZSBudW1iZXJzIGluIGVhY2g6CmBgYHtyIG5hbWUtbnVtcy1uc30KbnNfbmFtZXMgJT4lIGNvdW50KGdyb3VwKQpgYGAKYGBge3IgbmFtZS1udW1zLXdlc3R9Cndlc3RfbmFtZXMgJT4lIGNvdW50KGdyb3VwKQpgYGAKVGhvc2UgbnVtYmVycyBhcmUgaW4gYWdyZWVtZW50IHdpdGggdGhlIG51bWJlcnMgb2YgZmlzaCBmcm9tIHRoZSBkaWZmZXJlbnQgd2lsZCBwb3B1bGF0aW9ucwppbiB0aGUgcGFwZXIuICBOb3RlIHRoYXQgIm5zIiBpcyB0aGUgTWFyaXRpbWVzLCBhbmQgIndlc3QiIGlzIE5ld2ZvdW5kbGFuZC4KCiMjIEJlZm9yZSBwcm9jZWVkaW5nIHdpdGggUExJTksuLi4KCkJlZm9yZSB3ZSBnbyBvZmYgYW5kIHVzZSBQTElOSyB0byBjb21wdXRlIEZzdCwgSSBhbSBnb2luZyB0byBleHBsb3JlIGp1c3QKcHV0dGluZyBhbGwgdGhlIGRhdGEgaW4gYSB0aWR5IGZvcm1hdCwgYW5kIHRoZW4gY29tcHV0aW5nIGV4cGVjdGVkIGFzc2lnbm1lbnQKcHJvYmFiaWxpdGllcywgc2luY2UgdGhhdCBpcyBhbm90aGVyIHZhbGlkIHdheSBvZiBjaG9vc2luZyBsb2NpLCBhbmQgcG9zc2libHkgYmV0dGVyCmluIHNvbWUgY29udGV4dHMuCgpTbywgaG93IGFib3V0IGEgZnVuY3Rpb24gdG8gcmVhZCBwbGluayBwZWQgYW5kIG1hcCBmaWxlcyB0byB0aWR5IGZvcm1hdC4gIFdlIGhhdmUgd3JpdHRlbgpvbmUgb2YgdGhvc2UgaW4gaXRzIG93biBmaWxlLCBzbyB0aGF0IGl0IHdpbGwgYmUgZWFzaWVyIHRvIHBhY2thZ2UgaXQgdXAgbGF0ZXIuCmBgYHtyIHBsaW5rLXRvLXRpZHl9CnNvdXJjZSgiUi9wbGluazJ0aWR5LlIiKQpgYGAKSGVyZSBpcyBhIGxpc3Rpbmcgb2YgaXQ6CmBgYHIKYHIgcGFzdGUocmVhZExpbmVzKCJSL3BsaW5rMnRpZHkuUiIpLCBjb2xsYXBzZSA9ICJcbiIpYApgYGAKCiMjIyBTdG9yaW5nIGludGVybWVkaWF0ZSB0aWR5IGZpbGVzOgoKTm93IHdlIHVzZSB0aGF0IGZ1bmN0aW9uIHRvIHN0b3JlIHRpZHkgdmVyc2lvbnMgb2YgdGhvc2UgZGF0YSBzZXRzOgpgYGB7ciBtYWtlLXRpZHktdmVyc2lvbnN9Cm5zX3RpZHkgPC0gcGxpbmsydGlkeSgiZGF0YS9wcm9jZXNzZWQvbnMucGVkIiwgImRhdGEvcHJvY2Vzc2VkL25zLm1hcCIpCndlc3RfdGlkeSA8LSBwbGluazJ0aWR5KCJkYXRhL3Byb2Nlc3NlZC93ZXN0LnBlZCIsICJkYXRhL3Byb2Nlc3NlZC93ZXN0Lm1hcCIpCgpzYXZlUkRTKG5zX3RpZHksIGZpbGUgPSAiaW50ZXJtZWRpYXRlcy8wMS90aWR5LW5zLnJkcyIsIGNvbXByZXNzID0gInh6IikKCnNhdmVSRFMod2VzdF90aWR5LCBmaWxlID0gImludGVybWVkaWF0ZXMvMDEvdGlkeS13ZXN0LnJkcyIsIGNvbXByZXNzID0gInh6IikKYGBg