Intro/Overview

Going to show how I cranked through all the different replicates for Newfoundland. We are, in this case, using all the training individuals from wild and farmed as reference samples (using the Z option) in NewHybrids.

We create our own nomenclature so that the directory name tells us about the simulation.

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)
library(SalarHybPower)

Here we use the Nova Scotia data. But we will call this dat. Please readd dev-01-initial-data-maneuvers.Rmd to see how we processed the data to this point.

dat <- readRDS("intermediates/01/tidy-west.rds") %>%
  mutate(pop = str_sub(id, 1, 3),
         group = ifelse(pop == "AQU" | pop == "WLN", "farmed", "wild"))

First, count how many individuals we have from each wild population:

dat %>%
  group_by(pop) %>%
  summarize(num = n_distinct(id))

This means that if we are doing F2’s for LHR we will only be able to make 4 or 5 per time, and for backcrosses, we will only get about 4 per data set out of them. OK. Well, let us not worry about the number of individuals we get from each population. Let’s just do SPLITS different splits of the data into training and holdout…

Set up some info for the sims:

SPLITS <- 100 # the number of times to split the data and rank markers.  100 will give us a minimum of 400 simulated BXs for the smallest sample pop
LOCS <- c(48, 96, 144, 192, 240)  # number of loci.  Smallish numbers for Newfoundland
POPLIST <- c("BDN", "CNR", "GRR", "LHR", "LPR")  # names of wild populations
HYB_CATS <- c("PureW", "PureF", "F1", "F2", "BX")

Then just cycle over things and do it

main_out_dir <- "/tmp/NF_long_runs"
dir.create(main_out_dir)
set.seed(555)
for (s in 1:SPLITS) {
  message("Starting split number ", s)
  SAR <- split_and_rank(dat)
  for (pop in POPLIST) {
    for (hc in HYB_CATS) {
      for (locs in LOCS) {
        dirname <- paste(s,pop,hc,locs, sep = "_")
        create_hybrid_dataset(SAR = SAR, wild_pop = pop, hyb_cat = hc, L = locs, 
                              dir = file.path(main_out_dir, dirname))
      }
    }
  }
  
}

That takes about four hours and creates about 2 Gb of output in about 12500 directories, each with a single simulated data set in it.

The directories are named like this: 1_GRR_PureF_480 which denotes:

  • Split = 1
  • Population = GRR
  • Simulated hybrid category = Pure Farmed
  • Number of loci = 480

Now I just need to run NewHybrids over each like this:

~/Documents/git-repos/newhybrids/newhybs -d nh_data.txt -g P0 1 0 0 -g p1 0 0 1 -g F1 0 1 0 -g F2 .25 .5 .25 -g BX0 .5 .5 0 -g BX1 0 .5 .5 --pi-prior fixed  1 1 1 1 1 1

We will do that using the GNU parallel script.

Creating a GNU parallel script

We could use the parallel R package, but I have had better success using GNU parallel – a Perl script. I have included it in the bin directory in the repo.

We basically need to write a series of shell commands, each one launching newhybrids on a different data set.

We can use R to write those out. Note that this all assumes that the nh_reps_directory is in the top level of the repository, and that the command to run parallel will be given in that nh_reps_directory. Note that it is imporant to divert stderr to a newhybs_stderr file so we can search for cases that had underflow issues. I am going to do 100 burn in and 500 sweeps, cuz we can do short runs when we are working with having some indivs of known origin.

# first, get the absolute path of NEWHYBS to pass into the parallel script
NEWHYBS <- normalizePath("bin/newhybs")
# set the seed again for reproducibility
set.seed(100)
comms <- lapply(dir(main_out_dir, full.names = TRUE), function(x) {
  paste0("echo \"Starting ", 
         x, 
         " at $(date)\"; cd ", 
         x, 
         "; ",
         NEWHYBS,
         " -d nh_data.txt -g P0 1 0 0 -g p1 0 0 1 -g F1 0 1 0 -g F2 .25 .5 .25 -g BX0 .5 .5 0 -g BX1 0 .5 .5 --pi-prior fixed  1 1 1 1 1 1 --no-gui --burn-in 200 --num-sweeps 500 --seeds ",
         paste(ceiling(runif(2, min = 100, max = 10000000)), collapse = " "), 
         " > newhybs_stdout.txt 2> newhybs_stderr.txt; cd ..; echo \"Done with ", x, " at $(date)\"")
})
cat(unlist(comms), sep = "\n", file = "NF-long-run-para-comms.txt")

I synced all that to our 24 core box and then put the runs on 22 cores. I made a script called NF_run_script.sh in the top level of the repo. Its contents look like:

cat NF-long-run-para-comms.txt | ./bin/parallel -P22 > NF_long_run_BIG_LOG.txt

I can run that under nohup:

2017-08-02 10:55 /SalarHybPower/--% (master) pwd
/Users/eriq/Documents/git-repos/SalarHybPower

2017-08-02 11:00 /SalarHybPower/--% (master) nohup NF_run_script.sh &
[1] 36403
appending output to nohup.out

It only too 2.5 hours to get through that. That was less time than it took to generate the data sets on my laptop. Cool. It does generate 10 more Gb of output…but most of that we can ditch eventually.

Slurping up the output

Since this is on the big machine at work, it will be easiest for me to grab the output using awk over ssh:

2017-08-02 15:44 /NF_long_runs/--% pwd
/tmp/NF_long_runs

for i in *; do 
  warn=$(wc -l $i/newhybs_stderr.txt | awk '{print $1}'); 
  awk -v w=$warn -v i=$i 'NR>1 && !/train_/ {print i, w, $0}' $i/aa-PofZ.txt;
done  > ../NF_long_output.txt

That took a couple minutes. Then I gzipped that and brought it over to the outputs folder in the repository in /outputs/NF_long_output.txt.gz.

Reading in the output, and making a data frame for Brendan

First, a job for readr:

nh_output <- read_table2("outputs/NF_long_output.txt.gz",   # requires readr 1.1.1
                         col_names = c("sim", "warn", "idx", "iname", 
                                       "pure_farmed", "pure_wild", "F1", "F2", "bx_farm", "bx_wild"))
Parsed with column specification:
cols(
  sim = col_character(),
  warn = col_integer(),
  idx = col_integer(),
  iname = col_character(),
  pure_farmed = col_double(),
  pure_wild = col_double(),
  F1 = col_double(),
  F2 = col_double(),
  bx_farm = col_double(),
  bx_wild = col_double()
)

Then separate some columns

nh_sepped <- nh_output %>%
  separate(col = sim, into = c("split", "pop", "true_hyb_cat", "num_loci"), sep = "_", convert = TRUE) %>%
  separate(col = iname, into = c("drop", "s_idx"), sep = "_", convert = TRUE) %>% 
  mutate(s_idx = paste("split", split, s_idx, sep = "_")) %>%
  select(-drop) 

Really quickly, check which runs received warnings…None of them. Great!

Let’s gather it and make inferred_hyb_cat a factor so it comes out in a good order in the plots.

nh_tidy <- nh_sepped %>% 
  filter(warn == 0) %>% 
  gather(key = "inferred_hyb_cat", value = "post_prob", pure_farmed:bx_wild) %>%
  mutate(inferred_hyb_cat = factor(inferred_hyb_cat, levels = c("bx_wild", "bx_farm", "F2", "F1", "pure_wild", "pure_farmed"))) %>%
  arrange(split, pop, num_loci, true_hyb_cat, idx)
# and save this
#saveRDS(nh_tidy, file = "outputs/3_splits_nh_tidy.rds", compress = "xz")

And that is it. Almost 1.4 million rows of data. The columns are:

  • split — a number between 1 and 100. This is which “split” the results come from. A split is a separate splitting of the data randomly into a training set and a test set. For the smaller samples, the only way to simulate enough individuals without replacement is by doing lots of different splits. Which is why we have that many.
  • pop — the name of the population that the genes we sampled came from.
  • true_hyb_cat — the true hybrid category of the individual. This will be one of “BX”, “F1”, “F2”, “PureF”, “PureW”. BX is a backcross-to-wild. PureF is pure farmed. PureW is pure wild.
  • num_loci — is the number of loci.
  • warn — the number of lines in the standard error output for newhybrids. This is 0 for everyone, ’cuz newhybds didn’t throw any complaints.
  • idx — this is the number the fish got in the newhybrids data set. Probably won’t be used.
  • s_idx — a different identifier for the fish. Counting from 1. Not unique. Not all that useful.
  • inferred_hyb_cat — the hybrid category for which the posterior prob in the next column is computed. This is a factor with levels = c("bx_wild", "bx_farm", "F2", "F1", "pure_wild", "pure_farmed"). For some reason, I deemed that a good order for making the plots I previously made…
  • post_prob — the posterior prob for each simulated individual for the inferred_hyb_cat.

So, let’s save that and send it to Brendan.

saveRDS(nh_tidy, file = "outputs/NF_long_runs_tibble.rds", compress = "xz")
LS0tCnRpdGxlOiAiTm92aWEgU2NvdGlhIEJpZyBSdW5zIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIHNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgYWx3YXlzIHRvIHRoZSBwcm9qZWN0IGRpcmVjdG9yeSAob25lIGxldmVsIHVwKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IG5vcm1hbGl6ZVBhdGgocnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpKSAKYGBgCgoKIyMgSW50cm8vT3ZlcnZpZXcKCkdvaW5nIHRvIHNob3cgaG93IEkgY3JhbmtlZCB0aHJvdWdoIGFsbCB0aGUgZGlmZmVyZW50IHJlcGxpY2F0ZXMgZm9yIE5ld2ZvdW5kbGFuZC4KV2UgYXJlLCBpbiB0aGlzIGNhc2UsIHVzaW5nIGFsbCB0aGUgdHJhaW5pbmcgaW5kaXZpZHVhbHMgZnJvbSB3aWxkIGFuZCBmYXJtZWQgYXMgCnJlZmVyZW5jZSBzYW1wbGVzICh1c2luZyB0aGUgWiBvcHRpb24pIGluIE5ld0h5YnJpZHMuCgpXZSBjcmVhdGUgb3VyIG93biBub21lbmNsYXR1cmUgc28gdGhhdCB0aGUgZGlyZWN0b3J5IG5hbWUgdGVsbHMgdXMgYWJvdXQgdGhlIHNpbXVsYXRpb24uCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShTYWxhckh5YlBvd2VyKQpgYGAKCkhlcmUgd2UgdXNlIHRoZSBOb3ZhIFNjb3RpYSBkYXRhLiBCdXQgd2Ugd2lsbApjYWxsIHRoaXMgYGRhdGAuICBQbGVhc2UgcmVhZGQgYGRldi0wMS1pbml0aWFsLWRhdGEtbWFuZXV2ZXJzLlJtZGAgdG8gc2VlCmhvdyB3ZSBwcm9jZXNzZWQgdGhlIGRhdGEgdG8gdGhpcyBwb2ludC4gCmBgYHtyfQpkYXQgPC0gcmVhZFJEUygiaW50ZXJtZWRpYXRlcy8wMS90aWR5LXdlc3QucmRzIikgJT4lCiAgbXV0YXRlKHBvcCA9IHN0cl9zdWIoaWQsIDEsIDMpLAogICAgICAgICBncm91cCA9IGlmZWxzZShwb3AgPT0gIkFRVSIgfCBwb3AgPT0gIldMTiIsICJmYXJtZWQiLCAid2lsZCIpKQpgYGAKCkZpcnN0LCBjb3VudCBob3cgbWFueSBpbmRpdmlkdWFscyB3ZSBoYXZlIGZyb20gZWFjaCB3aWxkIHBvcHVsYXRpb246CmBgYHtyfQpkYXQgJT4lCiAgZ3JvdXBfYnkocG9wKSAlPiUKICBzdW1tYXJpemUobnVtID0gbl9kaXN0aW5jdChpZCkpCmBgYAoKVGhpcyBtZWFucyB0aGF0IGlmIHdlIGFyZSBkb2luZyBGMidzIGZvciBMSFIgd2Ugd2lsbCBvbmx5IGJlIGFibGUgdG8gbWFrZSA0IG9yIDUgcGVyIHRpbWUsCmFuZCBmb3IgYmFja2Nyb3NzZXMsIHdlIHdpbGwgb25seSBnZXQgYWJvdXQgNCBwZXIgZGF0YSBzZXQgb3V0IG9mIHRoZW0uICBPSy4gIFdlbGwsIGxldCB1cyAKbm90IHdvcnJ5IGFib3V0IHRoZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgd2UgZ2V0IGZyb20gZWFjaCBwb3B1bGF0aW9uLiAgTGV0J3MganVzdCBkbyBTUExJVFMgCmRpZmZlcmVudCBzcGxpdHMgb2YgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgaG9sZG91dC4uLgoKClNldCB1cCBzb21lIGluZm8gZm9yIHRoZSBzaW1zOgpgYGB7cn0KU1BMSVRTIDwtIDEwMCAjIHRoZSBudW1iZXIgb2YgdGltZXMgdG8gc3BsaXQgdGhlIGRhdGEgYW5kIHJhbmsgbWFya2Vycy4gIDEwMCB3aWxsIGdpdmUgdXMgYSBtaW5pbXVtIG9mIDQwMCBzaW11bGF0ZWQgQlhzIGZvciB0aGUgc21hbGxlc3Qgc2FtcGxlIHBvcApMT0NTIDwtIGMoNDgsIDk2LCAxNDQsIDE5MiwgMjQwKSAgIyBudW1iZXIgb2YgbG9jaS4gIFNtYWxsaXNoIG51bWJlcnMgZm9yIE5ld2ZvdW5kbGFuZApQT1BMSVNUIDwtIGMoIkJETiIsICJDTlIiLCAiR1JSIiwgIkxIUiIsICJMUFIiKSAgIyBuYW1lcyBvZiB3aWxkIHBvcHVsYXRpb25zCkhZQl9DQVRTIDwtIGMoIlB1cmVXIiwgIlB1cmVGIiwgIkYxIiwgIkYyIiwgIkJYIikKYGBgCgpUaGVuIGp1c3QgY3ljbGUgb3ZlciB0aGluZ3MgYW5kIGRvIGl0CmBgYHtyLCBldmFsPUZBTFNFfQptYWluX291dF9kaXIgPC0gIi90bXAvTkZfbG9uZ19ydW5zIgpkaXIuY3JlYXRlKG1haW5fb3V0X2RpcikKc2V0LnNlZWQoNTU1KQpmb3IgKHMgaW4gMTpTUExJVFMpIHsKICBtZXNzYWdlKCJTdGFydGluZyBzcGxpdCBudW1iZXIgIiwgcykKICBTQVIgPC0gc3BsaXRfYW5kX3JhbmsoZGF0KQogIGZvciAocG9wIGluIFBPUExJU1QpIHsKICAgIGZvciAoaGMgaW4gSFlCX0NBVFMpIHsKICAgICAgZm9yIChsb2NzIGluIExPQ1MpIHsKICAgICAgICBkaXJuYW1lIDwtIHBhc3RlKHMscG9wLGhjLGxvY3MsIHNlcCA9ICJfIikKICAgICAgICBjcmVhdGVfaHlicmlkX2RhdGFzZXQoU0FSID0gU0FSLCB3aWxkX3BvcCA9IHBvcCwgaHliX2NhdCA9IGhjLCBMID0gbG9jcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpciA9IGZpbGUucGF0aChtYWluX291dF9kaXIsIGRpcm5hbWUpKQogICAgICB9CiAgICB9CiAgfQogIAp9CgpgYGAKClRoYXQgdGFrZXMgYWJvdXQgZm91ciBob3VycyBhbmQgY3JlYXRlcyBhYm91dCAyIEdiIG9mIG91dHB1dCBpbiBhYm91dCAxMjUwMCBkaXJlY3RvcmllcywgZWFjaCB3aXRoIGEgc2luZ2xlIHNpbXVsYXRlZCBkYXRhIHNldCBpbiBpdC4gIAoKVGhlIGRpcmVjdG9yaWVzIGFyZSBuYW1lZCBsaWtlIHRoaXM6IGAxX0dSUl9QdXJlRl80ODBgICB3aGljaCBkZW5vdGVzOgoKLSBTcGxpdCA9IDEKLSBQb3B1bGF0aW9uID0gR1JSCi0gU2ltdWxhdGVkIGh5YnJpZCBjYXRlZ29yeSA9IFB1cmUgRmFybWVkCi0gTnVtYmVyIG9mIGxvY2kgPSA0ODAKCgoKCk5vdyBJIGp1c3QgbmVlZCB0byBydW4gTmV3SHlicmlkcyBvdmVyIGVhY2ggbGlrZSB0aGlzOgpgYGBzaAp+L0RvY3VtZW50cy9naXQtcmVwb3MvbmV3aHlicmlkcy9uZXdoeWJzIC1kIG5oX2RhdGEudHh0IC1nIFAwIDEgMCAwIC1nIHAxIDAgMCAxIC1nIEYxIDAgMSAwIC1nIEYyIC4yNSAuNSAuMjUgLWcgQlgwIC41IC41IDAgLWcgQlgxIDAgLjUgLjUgLS1waS1wcmlvciBmaXhlZCAgMSAxIDEgMSAxIDEKYGBgCgpXZSB3aWxsIGRvIHRoYXQgdXNpbmcgdGhlIEdOVSBwYXJhbGxlbCBzY3JpcHQuCgojIyBDcmVhdGluZyBhIEdOVSBwYXJhbGxlbCBzY3JpcHQKCldlIGNvdWxkIHVzZSB0aGUgYHBhcmFsbGVsYCBSIHBhY2thZ2UsIGJ1dCBJIGhhdmUgaGFkIGJldHRlciBzdWNjZXNzIHVzaW5nIEdOVSBwYXJhbGxlbCAtLSBhIFBlcmwgc2NyaXB0LiAgSSBoYXZlIGluY2x1ZGVkIGl0IGluIHRoZSAKYGJpbmAgZGlyZWN0b3J5IGluIHRoZSByZXBvLgoKV2UgYmFzaWNhbGx5IG5lZWQgdG8gd3JpdGUgYSBzZXJpZXMgb2Ygc2hlbGwgY29tbWFuZHMsIGVhY2ggb25lIGxhdW5jaGluZyBuZXdoeWJyaWRzIG9uIGEgZGlmZmVyZW50IGRhdGEgc2V0LgoKV2UgY2FuIHVzZSBSIHRvIHdyaXRlIHRob3NlIG91dC4gIE5vdGUgdGhhdCB0aGlzIGFsbCBhc3N1bWVzIHRoYXQgdGhlIGBuaF9yZXBzX2RpcmVjdG9yeWAgaXMgaW4gdGhlIHRvcCBsZXZlbApvZiB0aGUgcmVwb3NpdG9yeSwgYW5kIHRoYXQgdGhlIGNvbW1hbmQgdG8gcnVuIHBhcmFsbGVsIHdpbGwgYmUgZ2l2ZW4gaW4gdGhhdCBuaF9yZXBzX2RpcmVjdG9yeS4KTm90ZSB0aGF0IGl0IGlzIGltcG9yYW50IHRvIGRpdmVydCBzdGRlcnIgdG8gYSBuZXdoeWJzX3N0ZGVyciBmaWxlIHNvIHdlIGNhbiBzZWFyY2ggZm9yIGNhc2VzCnRoYXQgaGFkIHVuZGVyZmxvdyBpc3N1ZXMuICBJIGFtIGdvaW5nIHRvIGRvIDEwMCBidXJuIGluIGFuZCA1MDAgc3dlZXBzLCBjdXogd2UgY2FuIGRvIHNob3J0IHJ1bnMKd2hlbiB3ZSBhcmUgd29ya2luZyB3aXRoIGhhdmluZyBzb21lIGluZGl2cyBvZiBrbm93biBvcmlnaW4uCmBgYHtyfQojIGZpcnN0LCBnZXQgdGhlIGFic29sdXRlIHBhdGggb2YgTkVXSFlCUyB0byBwYXNzIGludG8gdGhlIHBhcmFsbGVsIHNjcmlwdApORVdIWUJTIDwtIG5vcm1hbGl6ZVBhdGgoImJpbi9uZXdoeWJzIikKCiMgc2V0IHRoZSBzZWVkIGFnYWluIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTAwKQoKY29tbXMgPC0gbGFwcGx5KGRpcihtYWluX291dF9kaXIsIGZ1bGwubmFtZXMgPSBUUlVFKSwgZnVuY3Rpb24oeCkgewogIHBhc3RlMCgiZWNobyBcIlN0YXJ0aW5nICIsIAogICAgICAgICB4LCAKICAgICAgICAgIiBhdCAkKGRhdGUpXCI7IGNkICIsIAogICAgICAgICB4LCAKICAgICAgICAgIjsgIiwKICAgICAgICAgTkVXSFlCUywKICAgICAgICAgIiAtZCBuaF9kYXRhLnR4dCAtZyBQMCAxIDAgMCAtZyBwMSAwIDAgMSAtZyBGMSAwIDEgMCAtZyBGMiAuMjUgLjUgLjI1IC1nIEJYMCAuNSAuNSAwIC1nIEJYMSAwIC41IC41IC0tcGktcHJpb3IgZml4ZWQgIDEgMSAxIDEgMSAxIC0tbm8tZ3VpIC0tYnVybi1pbiAyMDAgLS1udW0tc3dlZXBzIDUwMCAtLXNlZWRzICIsCiAgICAgICAgIHBhc3RlKGNlaWxpbmcocnVuaWYoMiwgbWluID0gMTAwLCBtYXggPSAxMDAwMDAwMCkpLCBjb2xsYXBzZSA9ICIgIiksIAogICAgICAgICAiID4gbmV3aHlic19zdGRvdXQudHh0IDI+IG5ld2h5YnNfc3RkZXJyLnR4dDsgY2QgLi47IGVjaG8gXCJEb25lIHdpdGggIiwgeCwgIiBhdCAkKGRhdGUpXCIiKQp9KQpjYXQodW5saXN0KGNvbW1zKSwgc2VwID0gIlxuIiwgZmlsZSA9ICJORi1sb25nLXJ1bi1wYXJhLWNvbW1zLnR4dCIpCmBgYAoKSSBzeW5jZWQgYWxsIHRoYXQgdG8gb3VyIDI0IGNvcmUgYm94IGFuZCB0aGVuIHB1dCB0aGUgcnVucyBvbiAyMiBjb3Jlcy4gIEkgbWFkZSBhIHNjcmlwdCAKY2FsbGVkIGBORl9ydW5fc2NyaXB0LnNoYCBpbiB0aGUgdG9wIGxldmVsIG9mIHRoZSByZXBvLiBJdHMgY29udGVudHMgbG9vayBsaWtlOgpgYGBzaApjYXQgTkYtbG9uZy1ydW4tcGFyYS1jb21tcy50eHQgfCAuL2Jpbi9wYXJhbGxlbCAtUDIyID4gTkZfbG9uZ19ydW5fQklHX0xPRy50eHQKYGBgCkkgY2FuIHJ1biB0aGF0IHVuZGVyIG5vaHVwOgpgYGBzaAoyMDE3LTA4LTAyIDEwOjU1IC9TYWxhckh5YlBvd2VyLy0tJSAobWFzdGVyKSBwd2QKL1VzZXJzL2VyaXEvRG9jdW1lbnRzL2dpdC1yZXBvcy9TYWxhckh5YlBvd2VyCgoyMDE3LTA4LTAyIDExOjAwIC9TYWxhckh5YlBvd2VyLy0tJSAobWFzdGVyKSBub2h1cCBORl9ydW5fc2NyaXB0LnNoICYKWzFdIDM2NDAzCmFwcGVuZGluZyBvdXRwdXQgdG8gbm9odXAub3V0CmBgYAoKSXQgb25seSB0b28gMi41IGhvdXJzIHRvIGdldCB0aHJvdWdoIHRoYXQuICBUaGF0IHdhcyBsZXNzIHRpbWUgdGhhbiBpdCB0b29rCnRvIGdlbmVyYXRlIHRoZSBkYXRhIHNldHMgb24gbXkgbGFwdG9wLiAgQ29vbC4gIEl0IGRvZXMgZ2VuZXJhdGUgMTAgbW9yZSBHYiBvZgpvdXRwdXQuLi5idXQgbW9zdCBvZiB0aGF0IHdlIGNhbiBkaXRjaCBldmVudHVhbGx5LgoKIyMgU2x1cnBpbmcgdXAgdGhlIG91dHB1dAoKU2luY2UgdGhpcyBpcyBvbiB0aGUgYmlnIG1hY2hpbmUgYXQgd29yaywgaXQgd2lsbCBiZSBlYXNpZXN0IGZvciBtZSB0byBncmFiCnRoZSBvdXRwdXQgdXNpbmcgYGF3a2Agb3ZlciBgc3NoYDoKYGBgc2gKMjAxNy0wOC0wMiAxNTo0NCAvTkZfbG9uZ19ydW5zLy0tJSBwd2QKL3RtcC9ORl9sb25nX3J1bnMKCmZvciBpIGluICo7IGRvIAogIHdhcm49JCh3YyAtbCAkaS9uZXdoeWJzX3N0ZGVyci50eHQgfCBhd2sgJ3twcmludCAkMX0nKTsgCiAgYXdrIC12IHc9JHdhcm4gLXYgaT0kaSAnTlI+MSAmJiAhL3RyYWluXy8ge3ByaW50IGksIHcsICQwfScgJGkvYWEtUG9mWi50eHQ7CmRvbmUgID4gLi4vTkZfbG9uZ19vdXRwdXQudHh0CgpgYGAKVGhhdCB0b29rIGEgY291cGxlIG1pbnV0ZXMuClRoZW4gSSBnemlwcGVkIHRoYXQgYW5kIGJyb3VnaHQgaXQgb3ZlciB0byB0aGUgYG91dHB1dHNgIGZvbGRlciBpbiB0aGUgcmVwb3NpdG9yeQppbiBgL291dHB1dHMvTkZfbG9uZ19vdXRwdXQudHh0Lmd6YC4gCgojIyBSZWFkaW5nIGluIHRoZSBvdXRwdXQsIGFuZCBtYWtpbmcgYSBkYXRhIGZyYW1lIGZvciBCcmVuZGFuCgpGaXJzdCwgYSBqb2IgZm9yIHJlYWRyOgpgYGB7cn0Kbmhfb3V0cHV0IDwtIHJlYWRfdGFibGUyKCJvdXRwdXRzL05GX2xvbmdfb3V0cHV0LnR4dC5neiIsICAgIyByZXF1aXJlcyByZWFkciAxLjEuMQogICAgICAgICAgICAgICAgICAgICAgICAgY29sX25hbWVzID0gYygic2ltIiwgIndhcm4iLCAiaWR4IiwgImluYW1lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwdXJlX2Zhcm1lZCIsICJwdXJlX3dpbGQiLCAiRjEiLCAiRjIiLCAiYnhfZmFybSIsICJieF93aWxkIikpCmBgYAoKVGhlbiBzZXBhcmF0ZSBzb21lIGNvbHVtbnMKYGBge3J9Cm5oX3NlcHBlZCA8LSBuaF9vdXRwdXQgJT4lCiAgc2VwYXJhdGUoY29sID0gc2ltLCBpbnRvID0gYygic3BsaXQiLCAicG9wIiwgInRydWVfaHliX2NhdCIsICJudW1fbG9jaSIpLCBzZXAgPSAiXyIsIGNvbnZlcnQgPSBUUlVFKSAlPiUKICBzZXBhcmF0ZShjb2wgPSBpbmFtZSwgaW50byA9IGMoImRyb3AiLCAic19pZHgiKSwgc2VwID0gIl8iLCBjb252ZXJ0ID0gVFJVRSkgJT4lIAogIG11dGF0ZShzX2lkeCA9IHBhc3RlKCJzcGxpdCIsIHNwbGl0LCBzX2lkeCwgc2VwID0gIl8iKSkgJT4lCiAgc2VsZWN0KC1kcm9wKSAKCgpgYGAKClJlYWxseSBxdWlja2x5LCBjaGVjayB3aGljaCBydW5zIHJlY2VpdmVkIHdhcm5pbmdzLi4uTm9uZSBvZiB0aGVtLiAgR3JlYXQhCmBgYHtyfQpuaF9zZXBwZWQgJT4lCiAgZmlsdGVyKHdhcm4gPiAwKQpgYGAKCgpMZXQncyBnYXRoZXIgaXQgYW5kIG1ha2UgaW5mZXJyZWRfaHliX2NhdCBhCmZhY3RvciBzbyBpdCBjb21lcyBvdXQgaW4gYSBnb29kIG9yZGVyIGluIHRoZSBwbG90cy4KYGBge3J9Cm5oX3RpZHkgPC0gbmhfc2VwcGVkICU+JSAKICBmaWx0ZXIod2FybiA9PSAwKSAlPiUgCiAgZ2F0aGVyKGtleSA9ICJpbmZlcnJlZF9oeWJfY2F0IiwgdmFsdWUgPSAicG9zdF9wcm9iIiwgcHVyZV9mYXJtZWQ6Ynhfd2lsZCkgJT4lCiAgbXV0YXRlKGluZmVycmVkX2h5Yl9jYXQgPSBmYWN0b3IoaW5mZXJyZWRfaHliX2NhdCwgbGV2ZWxzID0gYygiYnhfd2lsZCIsICJieF9mYXJtIiwgIkYyIiwgIkYxIiwgInB1cmVfd2lsZCIsICJwdXJlX2Zhcm1lZCIpKSkgJT4lCiAgYXJyYW5nZShzcGxpdCwgcG9wLCBudW1fbG9jaSwgdHJ1ZV9oeWJfY2F0LCBpZHgpCmBgYAoKQW5kIHRoYXQgaXMgaXQuICBBbG1vc3QgMS40IG1pbGxpb24gcm93cyBvZiBkYXRhLiBUaGUgY29sdW1ucyBhcmU6CgotIGBzcGxpdGAgLS0tIGEgbnVtYmVyIGJldHdlZW4gMSBhbmQgMTAwLiAgVGhpcyBpcyB3aGljaCAic3BsaXQiIHRoZSByZXN1bHRzIGNvbWUgZnJvbS4KQSBzcGxpdCBpcyBhIHNlcGFyYXRlIHNwbGl0dGluZyBvZiB0aGUgZGF0YSByYW5kb21seSBpbnRvIGEgdHJhaW5pbmcgc2V0IGFuZCBhIHRlc3QKc2V0LiAgRm9yIHRoZSBzbWFsbGVyIHNhbXBsZXMsIHRoZSBvbmx5IHdheSB0byBzaW11bGF0ZSBlbm91Z2ggaW5kaXZpZHVhbHMgd2l0aG91dApyZXBsYWNlbWVudCBpcyBieSBkb2luZyBsb3RzIG9mIGRpZmZlcmVudCBzcGxpdHMuICBXaGljaCBpcyB3aHkgd2UgaGF2ZSB0aGF0IG1hbnkuCi0gYHBvcGAgLS0tIHRoZSBuYW1lIG9mIHRoZSBwb3B1bGF0aW9uIHRoYXQgdGhlIGdlbmVzIHdlIHNhbXBsZWQgY2FtZSBmcm9tLgotIGB0cnVlX2h5Yl9jYXRgIC0tLSB0aGUgdHJ1ZSBoeWJyaWQgY2F0ZWdvcnkgb2YgdGhlIGluZGl2aWR1YWwuICBUaGlzIHdpbGwgYmUgb25lIG9mCiJCWCIsICJGMSIsICJGMiIsICJQdXJlRiIsICJQdXJlVyIuICBCWCBpcyBhIGJhY2tjcm9zcy10by13aWxkLiAgUHVyZUYgaXMgcHVyZSBmYXJtZWQuClB1cmVXIGlzIHB1cmUgd2lsZC4KLSBgbnVtX2xvY2lgIC0tLSBpcyB0aGUgbnVtYmVyIG9mIGxvY2kuCi0gYHdhcm5gIC0tLSB0aGUgbnVtYmVyIG9mIGxpbmVzIGluIHRoZSBzdGFuZGFyZCBlcnJvciBvdXRwdXQgZm9yIG5ld2h5YnJpZHMuICBUaGlzIGlzIDAgZm9yIGV2ZXJ5b25lLAonY3V6IG5ld2h5YmRzIGRpZG4ndCB0aHJvdyBhbnkgY29tcGxhaW50cy4KLSBgaWR4YCAtLS0gdGhpcyBpcyB0aGUgbnVtYmVyIHRoZSBmaXNoIGdvdCBpbiB0aGUgbmV3aHlicmlkcyBkYXRhIHNldC4gIFByb2JhYmx5IHdvbid0IGJlIHVzZWQuCi0gYHNfaWR4YCAtLS0gYSBkaWZmZXJlbnQgaWRlbnRpZmllciBmb3IgdGhlIGZpc2guICBDb3VudGluZyBmcm9tIDEuICBOb3QgdW5pcXVlLiAgTm90IGFsbCB0aGF0IHVzZWZ1bC4KLSBgaW5mZXJyZWRfaHliX2NhdGAgLS0tIHRoZSBoeWJyaWQgY2F0ZWdvcnkgZm9yIHdoaWNoIHRoZSBwb3N0ZXJpb3IgcHJvYiBpbiB0aGUgbmV4dCBjb2x1bW4gaXMgY29tcHV0ZWQuClRoaXMgaXMgYSBmYWN0b3Igd2l0aCBsZXZlbHMgPSBgYygiYnhfd2lsZCIsICJieF9mYXJtIiwgIkYyIiwgIkYxIiwgInB1cmVfd2lsZCIsICJwdXJlX2Zhcm1lZCIpYC4gIEZvciBzb21lCnJlYXNvbiwgSSBkZWVtZWQgdGhhdCBhIGdvb2Qgb3JkZXIgZm9yIG1ha2luZyB0aGUgcGxvdHMgSSBwcmV2aW91c2x5IG1hZGUuLi4KLSBgcG9zdF9wcm9iYCAtLS0gdGhlIHBvc3RlcmlvciBwcm9iIGZvciBlYWNoIHNpbXVsYXRlZCBpbmRpdmlkdWFsIGZvciB0aGUgYGluZmVycmVkX2h5Yl9jYXRgLgoKU28sIGxldCdzIHNhdmUgdGhhdCBhbmQgc2VuZCBpdCB0byBCcmVuZGFuLgoKYGBge3J9CnNhdmVSRFMobmhfdGlkeSwgZmlsZSA9ICJvdXRwdXRzL05GX2xvbmdfcnVuc190aWJibGUucmRzIiwgY29tcHJlc3MgPSAieHoiKQpgYGAKCgo=