Intro/Overview
Going to show how I cranked through all the different replicates for Maritimes. 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 Maritimes data. But we will call this dat
. Please read dev-01-initial-data-maneuvers.Rmd
to see how we processed the data to this point.
dat <- readRDS("intermediates/01/tidy-ns.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 LAH and MED we will only be able to make 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 500 simulated BXs for the smallest sample pop
LOCS <- c(48, 96, 144, 192, 240, 480, 720, 1000) # number of loci. Some bigger numbers of loci for the Maritimes
POPLIST <- c("BSR", "GAK", "LAH", "MED", "SMA", "STW", "TOB") # names of wild populations
HYB_CATS <- c("PureW", "PureF", "F1", "F2", "BX")
Then just cycle over things and do it
main_out_dir <- "/tmp/MAR_long_runs"
dir.create(main_out_dir)
set.seed(777)
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 6.4 Gb of output in about 28000 directories, each with a single simulated data set in it.
The directories are named like this: 1_BSR_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 = "MAR-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 MAR-run-script.sh
in the top level of the repo. Its contents look like:
cat MAR-long-run-para-comms.txt | ./bin/parallel -P22 > MAR_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 MAR-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 63 Gb of output, total…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-20 13:50 /MAR_long_runs/--% pwd
/tmp/MAR_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 > ../MAR_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/MAR_long_output.txt.gz
.
Reading in the output, and making a data frame for Brendan
First, a job for readr:
mar_output <- read_table2("outputs/MAR_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"))
Then separate some columns
mar_sepped <- mar_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…A good handful of them. OK.
mar_sepped %>%
filter(warn > 0)
We just toss out the results from any runs that had warnings (those were from that underflow issue in newhybrids). Let’s gather it and make inferred_hyb_cat a factor so it comes out in a good order in the plots.
mar_tidy <- mar_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 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(mar_tidy, file = "outputs/MAR_long_runs_tibble.rds", compress = "xz")
LS0tCnRpdGxlOiAiTWFyaXRpbWVzIEJpZyBSdW5zIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIHNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgYWx3YXlzIHRvIHRoZSBwcm9qZWN0IGRpcmVjdG9yeSAob25lIGxldmVsIHVwKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IG5vcm1hbGl6ZVBhdGgocnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpKSAKYGBgCgoKIyMgSW50cm8vT3ZlcnZpZXcKCkdvaW5nIHRvIHNob3cgaG93IEkgY3JhbmtlZCB0aHJvdWdoIGFsbCB0aGUgZGlmZmVyZW50IHJlcGxpY2F0ZXMgZm9yIE1hcml0aW1lcy4KV2UgYXJlLCBpbiB0aGlzIGNhc2UsIHVzaW5nIGFsbCB0aGUgdHJhaW5pbmcgaW5kaXZpZHVhbHMgZnJvbSB3aWxkIGFuZCBmYXJtZWQgYXMgCnJlZmVyZW5jZSBzYW1wbGVzICh1c2luZyB0aGUgWiBvcHRpb24pIGluIE5ld0h5YnJpZHMuCgpXZSBjcmVhdGUgb3VyIG93biBub21lbmNsYXR1cmUgc28gdGhhdCB0aGUgZGlyZWN0b3J5IG5hbWUgdGVsbHMgdXMgYWJvdXQgdGhlIHNpbXVsYXRpb24uCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShTYWxhckh5YlBvd2VyKQpgYGAKCkhlcmUgd2UgdXNlIHRoZSBNYXJpdGltZXMgZGF0YS4gQnV0IHdlIHdpbGwKY2FsbCB0aGlzIGBkYXRgLiAgUGxlYXNlIHJlYWQgYGRldi0wMS1pbml0aWFsLWRhdGEtbWFuZXV2ZXJzLlJtZGAgdG8gc2VlCmhvdyB3ZSBwcm9jZXNzZWQgdGhlIGRhdGEgdG8gdGhpcyBwb2ludC4gCmBgYHtyfQpkYXQgPC0gcmVhZFJEUygiaW50ZXJtZWRpYXRlcy8wMS90aWR5LW5zLnJkcyIpICU+JQogIG11dGF0ZShwb3AgPSBzdHJfc3ViKGlkLCAxLCAzKSwKICAgICAgICAgZ3JvdXAgPSBpZmVsc2UocG9wID09ICJBUVUiIHwgcG9wID09ICJXTE4iLCAiZmFybWVkIiwgIndpbGQiKSkKYGBgCgpGaXJzdCwgY291bnQgaG93IG1hbnkgaW5kaXZpZHVhbHMgd2UgaGF2ZSBmcm9tIGVhY2ggd2lsZCBwb3B1bGF0aW9uOgpgYGB7cn0KZGF0ICU+JQogIGdyb3VwX2J5KHBvcCkgJT4lCiAgc3VtbWFyaXplKG51bSA9IG5fZGlzdGluY3QoaWQpKQpgYGAKClRoaXMgbWVhbnMgdGhhdCBpZiB3ZSBhcmUgZG9pbmcgRjIncyBmb3IgTEFIIGFuZCBNRUQgd2Ugd2lsbCBvbmx5IGJlIGFibGUgdG8gbWFrZSA1IHBlciB0aW1lLAphbmQgZm9yIGJhY2tjcm9zc2VzLCB3ZSB3aWxsIG9ubHkgZ2V0IGFib3V0IDQgcGVyIGRhdGEgc2V0IG91dCBvZiB0aGVtLiAgT0suICBXZWxsLCBsZXQgdXMgCm5vdCB3b3JyeSBhYm91dCB0aGUgbnVtYmVyIG9mIGluZGl2aWR1YWxzIHdlIGdldCBmcm9tIGVhY2ggcG9wdWxhdGlvbi4gIExldCdzIGp1c3QgZG8gU1BMSVRTIApkaWZmZXJlbnQgc3BsaXRzIG9mIHRoZSBkYXRhIGludG8gdHJhaW5pbmcgYW5kIGhvbGRvdXQuLi4KCgpTZXQgdXAgc29tZSBpbmZvIGZvciB0aGUgc2ltczoKYGBge3J9ClNQTElUUyA8LSAxMDAgIyB0aGUgbnVtYmVyIG9mIHRpbWVzIHRvIHNwbGl0IHRoZSBkYXRhIGFuZCByYW5rIG1hcmtlcnMuICAxMDAgd2lsbCBnaXZlIHVzIGEgbWluaW11bSBvZiA1MDAgc2ltdWxhdGVkIEJYcyBmb3IgdGhlIHNtYWxsZXN0IHNhbXBsZSBwb3AKTE9DUyA8LSBjKDQ4LCA5NiwgMTQ0LCAxOTIsIDI0MCwgNDgwLCA3MjAsIDEwMDApICAjIG51bWJlciBvZiBsb2NpLiAgU29tZSBiaWdnZXIgbnVtYmVycyBvZiBsb2NpIGZvciB0aGUgTWFyaXRpbWVzClBPUExJU1QgPC0gYygiQlNSIiwgIkdBSyIsICJMQUgiLCAiTUVEIiwgIlNNQSIsICJTVFciLCAiVE9CIikgICMgbmFtZXMgb2Ygd2lsZCBwb3B1bGF0aW9ucwpIWUJfQ0FUUyA8LSBjKCJQdXJlVyIsICJQdXJlRiIsICJGMSIsICJGMiIsICJCWCIpCmBgYAoKVGhlbiBqdXN0IGN5Y2xlIG92ZXIgdGhpbmdzIGFuZCBkbyBpdApgYGB7ciwgZXZhbD1GQUxTRX0KbWFpbl9vdXRfZGlyIDwtICIvdG1wL01BUl9sb25nX3J1bnMiCmRpci5jcmVhdGUobWFpbl9vdXRfZGlyKQpzZXQuc2VlZCg3NzcpCmZvciAocyBpbiAxOlNQTElUUykgewogIG1lc3NhZ2UoIlN0YXJ0aW5nIHNwbGl0IG51bWJlciAiLCBzKQogIFNBUiA8LSBzcGxpdF9hbmRfcmFuayhkYXQpCiAgZm9yIChwb3AgaW4gUE9QTElTVCkgewogICAgZm9yIChoYyBpbiBIWUJfQ0FUUykgewogICAgICBmb3IgKGxvY3MgaW4gTE9DUykgewogICAgICAgIGRpcm5hbWUgPC0gcGFzdGUocyxwb3AsaGMsbG9jcywgc2VwID0gIl8iKQogICAgICAgIGNyZWF0ZV9oeWJyaWRfZGF0YXNldChTQVIgPSBTQVIsIHdpbGRfcG9wID0gcG9wLCBoeWJfY2F0ID0gaGMsIEwgPSBsb2NzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlyID0gZmlsZS5wYXRoKG1haW5fb3V0X2RpciwgZGlybmFtZSkpCiAgICAgIH0KICAgIH0KICB9CiAgCn0KCmBgYAoKVGhhdCB0YWtlcyBhYm91dCBmb3VyIGhvdXJzIGFuZCBjcmVhdGVzIGFib3V0IDYuNCBHYiBvZiBvdXRwdXQgaW4gYWJvdXQgMjgwMDAgZGlyZWN0b3JpZXMsIGVhY2ggd2l0aCBhIHNpbmdsZSBzaW11bGF0ZWQgZGF0YSBzZXQgaW4gaXQuICAKClRoZSBkaXJlY3RvcmllcyBhcmUgbmFtZWQgbGlrZSB0aGlzOiBgMV9CU1JfUHVyZUZfNDgwYCAgd2hpY2ggZGVub3RlczoKCi0gU3BsaXQgPSAxCi0gUG9wdWxhdGlvbiA9IEdSUgotIFNpbXVsYXRlZCBoeWJyaWQgY2F0ZWdvcnkgPSBQdXJlIEZhcm1lZAotIE51bWJlciBvZiBsb2NpID0gNDgwCgoKCgpOb3cgSSBqdXN0IG5lZWQgdG8gcnVuIE5ld0h5YnJpZHMgb3ZlciBlYWNoIGxpa2UgdGhpczoKYGBgc2gKfi9Eb2N1bWVudHMvZ2l0LXJlcG9zL25ld2h5YnJpZHMvbmV3aHlicyAtZCBuaF9kYXRhLnR4dCAtZyBQMCAxIDAgMCAtZyBwMSAwIDAgMSAtZyBGMSAwIDEgMCAtZyBGMiAuMjUgLjUgLjI1IC1nIEJYMCAuNSAuNSAwIC1nIEJYMSAwIC41IC41IC0tcGktcHJpb3IgZml4ZWQgIDEgMSAxIDEgMSAxCmBgYAoKV2Ugd2lsbCBkbyB0aGF0IHVzaW5nIHRoZSBHTlUgcGFyYWxsZWwgc2NyaXB0LgoKIyMgQ3JlYXRpbmcgYSBHTlUgcGFyYWxsZWwgc2NyaXB0CgpXZSBjb3VsZCB1c2UgdGhlIGBwYXJhbGxlbGAgUiBwYWNrYWdlLCBidXQgSSBoYXZlIGhhZCBiZXR0ZXIgc3VjY2VzcyB1c2luZyBHTlUgcGFyYWxsZWwgLS0gYSBQZXJsIHNjcmlwdC4gIEkgaGF2ZSBpbmNsdWRlZCBpdCBpbiB0aGUgCmBiaW5gIGRpcmVjdG9yeSBpbiB0aGUgcmVwby4KCldlIGJhc2ljYWxseSBuZWVkIHRvIHdyaXRlIGEgc2VyaWVzIG9mIHNoZWxsIGNvbW1hbmRzLCBlYWNoIG9uZSBsYXVuY2hpbmcgbmV3aHlicmlkcyBvbiBhIGRpZmZlcmVudCBkYXRhIHNldC4KCldlIGNhbiB1c2UgUiB0byB3cml0ZSB0aG9zZSBvdXQuICBOb3RlIHRoYXQgdGhpcyBhbGwgYXNzdW1lcyB0aGF0IHRoZSBgbmhfcmVwc19kaXJlY3RvcnlgIGlzIGluIHRoZSB0b3AgbGV2ZWwKb2YgdGhlIHJlcG9zaXRvcnksIGFuZCB0aGF0IHRoZSBjb21tYW5kIHRvIHJ1biBwYXJhbGxlbCB3aWxsIGJlIGdpdmVuIGluIHRoYXQgbmhfcmVwc19kaXJlY3RvcnkuCk5vdGUgdGhhdCBpdCBpcyBpbXBvcmFudCB0byBkaXZlcnQgc3RkZXJyIHRvIGEgbmV3aHlic19zdGRlcnIgZmlsZSBzbyB3ZSBjYW4gc2VhcmNoIGZvciBjYXNlcwp0aGF0IGhhZCB1bmRlcmZsb3cgaXNzdWVzLiAgSSBhbSBnb2luZyB0byBkbyAxMDAgYnVybiBpbiBhbmQgNTAwIHN3ZWVwcywgY3V6IHdlIGNhbiBkbyBzaG9ydCBydW5zCndoZW4gd2UgYXJlIHdvcmtpbmcgd2l0aCBoYXZpbmcgc29tZSBpbmRpdnMgb2Yga25vd24gb3JpZ2luLgpgYGB7cn0KIyBmaXJzdCwgZ2V0IHRoZSBhYnNvbHV0ZSBwYXRoIG9mIE5FV0hZQlMgdG8gcGFzcyBpbnRvIHRoZSBwYXJhbGxlbCBzY3JpcHQKTkVXSFlCUyA8LSBub3JtYWxpemVQYXRoKCJiaW4vbmV3aHlicyIpCgojIHNldCB0aGUgc2VlZCBhZ2FpbiBmb3IgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDEwMCkKCmNvbW1zIDwtIGxhcHBseShkaXIobWFpbl9vdXRfZGlyLCBmdWxsLm5hbWVzID0gVFJVRSksIGZ1bmN0aW9uKHgpIHsKICBwYXN0ZTAoImVjaG8gXCJTdGFydGluZyAiLCAKICAgICAgICAgeCwgCiAgICAgICAgICIgYXQgJChkYXRlKVwiOyBjZCAiLCAKICAgICAgICAgeCwgCiAgICAgICAgICI7ICIsCiAgICAgICAgIE5FV0hZQlMsCiAgICAgICAgICIgLWQgbmhfZGF0YS50eHQgLWcgUDAgMSAwIDAgLWcgcDEgMCAwIDEgLWcgRjEgMCAxIDAgLWcgRjIgLjI1IC41IC4yNSAtZyBCWDAgLjUgLjUgMCAtZyBCWDEgMCAuNSAuNSAtLXBpLXByaW9yIGZpeGVkICAxIDEgMSAxIDEgMSAtLW5vLWd1aSAtLWJ1cm4taW4gMjAwIC0tbnVtLXN3ZWVwcyA1MDAgLS1zZWVkcyAiLAogICAgICAgICBwYXN0ZShjZWlsaW5nKHJ1bmlmKDIsIG1pbiA9IDEwMCwgbWF4ID0gMTAwMDAwMDApKSwgY29sbGFwc2UgPSAiICIpLCAKICAgICAgICAgIiA+IG5ld2h5YnNfc3Rkb3V0LnR4dCAyPiBuZXdoeWJzX3N0ZGVyci50eHQ7IGNkIC4uOyBlY2hvIFwiRG9uZSB3aXRoICIsIHgsICIgYXQgJChkYXRlKVwiIikKfSkKY2F0KHVubGlzdChjb21tcyksIHNlcCA9ICJcbiIsIGZpbGUgPSAiTUFSLWxvbmctcnVuLXBhcmEtY29tbXMudHh0IikKYGBgCgpJIHN5bmNlZCBhbGwgdGhhdCB0byBvdXIgMjQgY29yZSBib3ggYW5kIHRoZW4gcHV0IHRoZSBydW5zIG9uIDIyIGNvcmVzLiAgSSBtYWRlIGEgc2NyaXB0IApjYWxsZWQgYE1BUi1ydW4tc2NyaXB0LnNoYCBpbiB0aGUgdG9wIGxldmVsIG9mIHRoZSByZXBvLiBJdHMgY29udGVudHMgbG9vayBsaWtlOgpgYGBzaApjYXQgTUFSLWxvbmctcnVuLXBhcmEtY29tbXMudHh0IHwgLi9iaW4vcGFyYWxsZWwgLVAyMiA+IE1BUl9sb25nX3J1bl9CSUdfTE9HLnR4dApgYGAKSSBjYW4gcnVuIHRoYXQgdW5kZXIgbm9odXA6CmBgYHNoCjIwMTctMDgtMDIgMTA6NTUgL1NhbGFySHliUG93ZXIvLS0lIChtYXN0ZXIpIHB3ZAovVXNlcnMvZXJpcS9Eb2N1bWVudHMvZ2l0LXJlcG9zL1NhbGFySHliUG93ZXIKCjIwMTctMDgtMDIgMTE6MDAgL1NhbGFySHliUG93ZXIvLS0lIChtYXN0ZXIpIG5vaHVwIE1BUi1ydW4tc2NyaXB0LnNoICYKWzFdIDM2NDAzCmFwcGVuZGluZyBvdXRwdXQgdG8gbm9odXAub3V0CmBgYAoKSXQgb25seSB0b28gMi41IGhvdXJzIHRvIGdldCB0aHJvdWdoIHRoYXQuICBUaGF0IHdhcyBsZXNzIHRpbWUgdGhhbiBpdCB0b29rCnRvIGdlbmVyYXRlIHRoZSBkYXRhIHNldHMgb24gbXkgbGFwdG9wLiAgQ29vbC4gIEl0IGRvZXMgZ2VuZXJhdGUgNjMgR2Igb2YKb3V0cHV0LCB0b3RhbC4uLmJ1dCBtb3N0IG9mIHRoYXQgd2UgY2FuIGRpdGNoIGV2ZW50dWFsbHkuCgojIyBTbHVycGluZyB1cCB0aGUgb3V0cHV0CgpTaW5jZSB0aGlzIGlzIG9uIHRoZSBiaWcgbWFjaGluZSBhdCB3b3JrLCBpdCB3aWxsIGJlIGVhc2llc3QgZm9yIG1lIHRvIGdyYWIKdGhlIG91dHB1dCB1c2luZyBgYXdrYCBvdmVyIGBzc2hgOgpgYGBzaAoyMDE3LTA4LTIwIDEzOjUwIC9NQVJfbG9uZ19ydW5zLy0tJSBwd2QKL3RtcC9NQVJfbG9uZ19ydW5zCgpmb3IgaSBpbiAqOyBkbyAKICB3YXJuPSQod2MgLWwgJGkvbmV3aHlic19zdGRlcnIudHh0IHwgYXdrICd7cHJpbnQgJDF9Jyk7IAogIGF3ayAtdiB3PSR3YXJuIC12IGk9JGkgJ05SPjEgJiYgIS90cmFpbl8vIHtwcmludCBpLCB3LCAkMH0nICRpL2FhLVBvZloudHh0Owpkb25lICA+IC4uL01BUl9sb25nX291dHB1dC50eHQKCmBgYApUaGF0IHRvb2sgYSBjb3VwbGUgbWludXRlcy4KVGhlbiBJIGd6aXBwZWQgdGhhdCBhbmQgYnJvdWdodCBpdCBvdmVyIHRvIHRoZSBgb3V0cHV0c2AgZm9sZGVyIGluIHRoZSByZXBvc2l0b3J5CmluIGAvb3V0cHV0cy9NQVJfbG9uZ19vdXRwdXQudHh0Lmd6YC4gCgojIyBSZWFkaW5nIGluIHRoZSBvdXRwdXQsIGFuZCBtYWtpbmcgYSBkYXRhIGZyYW1lIGZvciBCcmVuZGFuCgpGaXJzdCwgYSBqb2IgZm9yIHJlYWRyOgpgYGB7cn0KbWFyX291dHB1dCA8LSByZWFkX3RhYmxlMigib3V0cHV0cy9NQVJfbG9uZ19vdXRwdXQudHh0Lmd6IiwgICAjIHJlcXVpcmVzIHJlYWRyIDEuMS4xCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfbmFtZXMgPSBjKCJzaW0iLCAid2FybiIsICJpZHgiLCAiaW5hbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInB1cmVfZmFybWVkIiwgInB1cmVfd2lsZCIsICJGMSIsICJGMiIsICJieF9mYXJtIiwgImJ4X3dpbGQiKSkKYGBgCgpUaGVuIHNlcGFyYXRlIHNvbWUgY29sdW1ucwpgYGB7cn0KbWFyX3NlcHBlZCA8LSBtYXJfb3V0cHV0ICU+JQogIHNlcGFyYXRlKGNvbCA9IHNpbSwgaW50byA9IGMoInNwbGl0IiwgInBvcCIsICJ0cnVlX2h5Yl9jYXQiLCAibnVtX2xvY2kiKSwgc2VwID0gIl8iLCBjb252ZXJ0ID0gVFJVRSkgJT4lCiAgc2VwYXJhdGUoY29sID0gaW5hbWUsIGludG8gPSBjKCJkcm9wIiwgInNfaWR4IiksIHNlcCA9ICJfIiwgY29udmVydCA9IFRSVUUpICU+JSAKICBtdXRhdGUoc19pZHggPSBwYXN0ZSgic3BsaXQiLCBzcGxpdCwgc19pZHgsIHNlcCA9ICJfIikpICU+JQogIHNlbGVjdCgtZHJvcCkgCgoKYGBgCgpSZWFsbHkgcXVpY2tseSwgY2hlY2sgd2hpY2ggcnVucyByZWNlaXZlZCB3YXJuaW5ncy4uLkEgZ29vZCBoYW5kZnVsIG9mIHRoZW0uICBPSy4gCmBgYHtyfQptYXJfc2VwcGVkICU+JQogIGZpbHRlcih3YXJuID4gMCkKYGBgCgoKV2UganVzdCB0b3NzIG91dCB0aGUgcmVzdWx0cyBmcm9tIGFueSBydW5zIHRoYXQgaGFkIHdhcm5pbmdzICh0aG9zZSB3ZXJlIGZyb20KdGhhdCB1bmRlcmZsb3cgaXNzdWUgaW4gbmV3aHlicmlkcykuICBMZXQncyBnYXRoZXIgaXQgYW5kIG1ha2UgaW5mZXJyZWRfaHliX2NhdCBhCmZhY3RvciBzbyBpdCBjb21lcyBvdXQgaW4gYSBnb29kIG9yZGVyIGluIHRoZSBwbG90cy4KYGBge3J9Cm1hcl90aWR5IDwtIG1hcl9zZXBwZWQgJT4lIAogIGZpbHRlcih3YXJuID09IDApICU+JSAKICBnYXRoZXIoa2V5ID0gImluZmVycmVkX2h5Yl9jYXQiLCB2YWx1ZSA9ICJwb3N0X3Byb2IiLCBwdXJlX2Zhcm1lZDpieF93aWxkKSAlPiUKICBtdXRhdGUoaW5mZXJyZWRfaHliX2NhdCA9IGZhY3RvcihpbmZlcnJlZF9oeWJfY2F0LCBsZXZlbHMgPSBjKCJieF93aWxkIiwgImJ4X2Zhcm0iLCAiRjIiLCAiRjEiLCAicHVyZV93aWxkIiwgInB1cmVfZmFybWVkIikpKSAlPiUKICBhcnJhbmdlKHNwbGl0LCBwb3AsIG51bV9sb2NpLCB0cnVlX2h5Yl9jYXQsIGlkeCkKYGBgCgpBbmQgdGhhdCBpcyBpdC4gIEFsbW9zdCAxLjQgbWlsbGlvbiByb3dzIG9mIGRhdGEuIFRoZSBjb2x1bW5zIGFyZToKCi0gYHNwbGl0YCAtLS0gYSBudW1iZXIgYmV0d2VlbiAxIGFuZCAxMDAuICBUaGlzIGlzIHdoaWNoICJzcGxpdCIgdGhlIHJlc3VsdHMgY29tZSBmcm9tLgpBIHNwbGl0IGlzIGEgc2VwYXJhdGUgc3BsaXR0aW5nIG9mIHRoZSBkYXRhIHJhbmRvbWx5IGludG8gYSB0cmFpbmluZyBzZXQgYW5kIGEgdGVzdApzZXQuICBGb3IgdGhlIHNtYWxsZXIgc2FtcGxlcywgdGhlIG9ubHkgd2F5IHRvIHNpbXVsYXRlIGVub3VnaCBpbmRpdmlkdWFscyB3aXRob3V0CnJlcGxhY2VtZW50IGlzIGJ5IGRvaW5nIGxvdHMgb2YgZGlmZmVyZW50IHNwbGl0cy4gIFdoaWNoIGlzIHdoeSB3ZSBoYXZlIHRoYXQgbWFueS4KLSBgcG9wYCAtLS0gdGhlIG5hbWUgb2YgdGhlIHBvcHVsYXRpb24gdGhhdCB0aGUgZ2VuZXMgd2Ugc2FtcGxlZCBjYW1lIGZyb20uCi0gYHRydWVfaHliX2NhdGAgLS0tIHRoZSB0cnVlIGh5YnJpZCBjYXRlZ29yeSBvZiB0aGUgaW5kaXZpZHVhbC4gIFRoaXMgd2lsbCBiZSBvbmUgb2YKIkJYIiwgIkYxIiwgIkYyIiwgIlB1cmVGIiwgIlB1cmVXIi4gIEJYIGlzIGEgYmFja2Nyb3NzLXRvLXdpbGQuICBQdXJlRiBpcyBwdXJlIGZhcm1lZC4KUHVyZVcgaXMgcHVyZSB3aWxkLgotIGBudW1fbG9jaWAgLS0tIGlzIHRoZSBudW1iZXIgb2YgbG9jaS4KLSBgd2FybmAgLS0tIHRoZSBudW1iZXIgb2YgbGluZXMgaW4gdGhlIHN0YW5kYXJkIGVycm9yIG91dHB1dCBmb3IgbmV3aHlicmlkcy4gIFRoaXMgaXMgMCBmb3IgZXZlcnlvbmUsCidjdXogbmV3aHliZHMgZGlkbid0IHRocm93IGFueSBjb21wbGFpbnRzLgotIGBpZHhgIC0tLSB0aGlzIGlzIHRoZSBudW1iZXIgdGhlIGZpc2ggZ290IGluIHRoZSBuZXdoeWJyaWRzIGRhdGEgc2V0LiAgUHJvYmFibHkgd29uJ3QgYmUgdXNlZC4KLSBgc19pZHhgIC0tLSBhIGRpZmZlcmVudCBpZGVudGlmaWVyIGZvciB0aGUgZmlzaC4gIENvdW50aW5nIGZyb20gMS4gIE5vdCB1bmlxdWUuICBOb3QgYWxsIHRoYXQgdXNlZnVsLgotIGBpbmZlcnJlZF9oeWJfY2F0YCAtLS0gdGhlIGh5YnJpZCBjYXRlZ29yeSBmb3Igd2hpY2ggdGhlIHBvc3RlcmlvciBwcm9iIGluIHRoZSBuZXh0IGNvbHVtbiBpcyBjb21wdXRlZC4KVGhpcyBpcyBhIGZhY3RvciB3aXRoIGxldmVscyA9IGBjKCJieF93aWxkIiwgImJ4X2Zhcm0iLCAiRjIiLCAiRjEiLCAicHVyZV93aWxkIiwgInB1cmVfZmFybWVkIilgLiAgRm9yIHNvbWUKcmVhc29uLCBJIGRlZW1lZCB0aGF0IGEgZ29vZCBvcmRlciBmb3IgbWFraW5nIHRoZSBwbG90cyBJIHByZXZpb3VzbHkgbWFkZS4uLgotIGBwb3N0X3Byb2JgIC0tLSB0aGUgcG9zdGVyaW9yIHByb2IgZm9yIGVhY2ggc2ltdWxhdGVkIGluZGl2aWR1YWwgZm9yIHRoZSBgaW5mZXJyZWRfaHliX2NhdGAuCgpTbywgbGV0J3Mgc2F2ZSB0aGF0IGFuZCBzZW5kIGl0IHRvIEJyZW5kYW4uCgpgYGB7cn0Kc2F2ZVJEUyhtYXJfdGlkeSwgZmlsZSA9ICJvdXRwdXRzL01BUl9sb25nX3J1bnNfdGliYmxlLnJkcyIsIGNvbXByZXNzID0gInh6IikKYGBgCgoK