Intro/Overview
Just going to show how to create lots of data sets that can then be run through new hybrids. We create our own nomenclature so that the directory name tells us about the simulation. In this instance, we are going to restrict the reference (option-Z) individuals to be the training individuals from the farmed groups, and the wild training individual only from the population being simulated.
library(tidyverse)
library(stringr)
library(SalarHybPower)
We also want to have some example data. We will just use the Nova Scotia data. But we will call this dat:
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 <- 3 # the number of times to split the data and rank markers
LOCS <- c(48, 144, 192, 480) # number of loci Just do a smaller number here
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. This is different than 07 by setting the wild_ref_pop
variable to pop
.
main_out_dir <- "pop_specific_nh_reps_directory"
dir.create(main_out_dir)
set.seed(555)
for (s in 1:SPLITS) {
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, wild_ref_pop = pop, hyb_cat = hc, L = locs,
dir = file.path(main_out_dir, dirname))
}
}
}
}
That takes a few minutes and creates a large number of directories, (like 525 or so), 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.
comms <- lapply(dir("pop_specific_nh_reps_directory"), function(x) {
paste0("echo \"Starting ",
x,
" at $(date)\"; cd ",
x,
"; ../../bin/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 100 --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 = "pop-spec-para-comms.txt")
I synced all that to our 24 core box and then put the runs on 22 cores:
2017-07-05 12:08 /pop_specific_nh_reps_directory/--% (master) pwd
/Users/eriq/Documents/git-repos/SalarHybPower/pop_specific_nh_reps_directory
# then put it on 22 cores:
2017-07-05 12:09 /pop_specific_nh_reps_directory/--% (master) cat ../pop-spec-para-comms.txt | ../bin/parallel -P22 > ../BIG_LOG.txt &
[1] 26671
When doing such short runs, this seems to take less than an hour…
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-07-05 12:21 /pop_specific_nh_reps_directory/--% (master) pwd
/Users/eriq/Documents/git-repos/SalarHybPower/pop_specific_nh_reps_directory
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 > ../pop-spec3_splits_output.txt
Then I gzipped that and brought it over to the outputs
folder in the repository (but which I won’t commit…). And then I deleted the big simulation directory:
2017-07-05 12:24 /SalarHybPower/--% (master) rm -r pop_specific_nh_reps_directory/
Reading in the output, and making a quick plot
First, a job for readr:
nh_output <- read_table2("outputs/pop-spec3_splits_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 and then gather
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:
nh_sepped %>%
filter(warn > 0)
None of them, cuz we never did as many as 1000 loci.
Let’s just filter out the ones that had warnings and gather it, and make a infferred_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")))
# and save this
saveRDS(nh_tidy, file = "outputs/pop_spec-3_splits_nh_tidy.rds", compress = "xz")
And now we should be able to plot it.
# some colors that might work out better than ggplots defaults
hc_colors <- c(
pure_farmed = rgb(152, 78, 163, maxColorValue = 255), # purple
pure_wild = rgb(77, 175, 74, maxColorValue = 255), # green
F1 = rgb(255, 255, 51, maxColorValue = 255), # yellow
F2 = rgb(247, 129, 191, maxColorValue = 255), # pink
bx_wild = rgb(255, 127, 0, maxColorValue = 255), # orange
bx_farm = rgb(228, 26, 28, maxColorValue = 255) # red
)
nh_list <- nh_tidy %>%
split(., .$true_hyb_cat)
gg_list <- lapply(nh_list, function(x) {
g <- ggplot(x, aes(x = s_idx, y = post_prob, fill = inferred_hyb_cat)) +
geom_col() +
facet_grid(num_loci ~ pop) +
scale_fill_manual(values = hc_colors)
g
})
dump <- lapply(names(gg_list), function(n) {
ggsave(gg_list[[n]],
filename = paste0("outputs/pop-spec-first_look_", n, ".pdf"),
width = 10,
height = 6)})
That is cool. But it would be more cool to be able to compare those things more directly. I think we can read the non-pop-specific output in and then stick them in together.
non_spec <- readRDS("outputs/3_splits_nh_tidy.rds") %>%
filter(num_loci %in% LOCS)
nh_combo <- list(spec_pop = nh_tidy,
all_pop = non_spec) %>%
bind_rows(.id = "wild_ref_type")
nh_combo_list <- nh_combo %>%
split(., .$true_hyb_cat)
gg_list2 <- lapply(nh_combo_list, function(x) {
g <- ggplot(x, aes(x = s_idx, y = post_prob, fill = inferred_hyb_cat)) +
geom_col() +
facet_grid(num_loci + wild_ref_type ~ pop) +
scale_fill_manual(values = hc_colors)
g
})
dump <- lapply(names(gg_list2), function(n) {
ggsave(gg_list2[[n]],
filename = paste0("outputs/spec-pop-ref-vs-all-ref", n, ".pdf"),
width = 10,
height = 12)})
Using just the specific population for wild “Z” reference fish improves the identification of truly wild fish for LPR (they are less likely to be identified as BX_wild fish.) On the other hand, the PureFarmed fish are now more likely to be identified as BX_Farmed.
For some populations and some hybrid categories it looks like sometimes all_pop works better than the spec_pop version.
LS0tCnRpdGxlOiAiTWFraW5nIE11bHRpcGxlIERhdGEgU2V0cyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgc2V0IHRoZSB3b3JraW5nIGRpcmVjdG9yeSBhbHdheXMgdG8gdGhlIHByb2plY3QgZGlyZWN0b3J5IChvbmUgbGV2ZWwgdXApCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gbm9ybWFsaXplUGF0aChycHJvanJvb3Q6OmZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSkpIApgYGAKCgojIyBJbnRyby9PdmVydmlldwoKSnVzdCBnb2luZyB0byBzaG93IGhvdyB0byBjcmVhdGUgbG90cyBvZiBkYXRhIHNldHMgdGhhdCBjYW4gdGhlbiBiZSBydW4gdGhyb3VnaCBuZXcgaHlicmlkcy4KV2UgY3JlYXRlIG91ciBvd24gbm9tZW5jbGF0dXJlIHNvIHRoYXQgdGhlIGRpcmVjdG9yeSBuYW1lIHRlbGxzIHVzIGFib3V0IHRoZSBzaW11bGF0aW9uLiAgSW4gdGhpcyAKaW5zdGFuY2UsIHdlIGFyZSBnb2luZyB0byByZXN0cmljdCB0aGUgcmVmZXJlbmNlIChvcHRpb24tWikgaW5kaXZpZHVhbHMgdG8gYmUgdGhlIHRyYWluaW5nIGluZGl2aWR1YWxzCmZyb20gdGhlIGZhcm1lZCBncm91cHMsIGFuZCB0aGUgd2lsZCB0cmFpbmluZyBpbmRpdmlkdWFsIF9vbmx5IGZyb20gdGhlIHBvcHVsYXRpb24gYmVpbmcgc2ltdWxhdGVkXy4KCgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoU2FsYXJIeWJQb3dlcikKYGBgCgpXZSBhbHNvIHdhbnQgdG8gaGF2ZSBzb21lIGV4YW1wbGUgZGF0YS4gIFdlIHdpbGwganVzdCB1c2UgdGhlIE5vdmEgU2NvdGlhIGRhdGEuIEJ1dCB3ZSB3aWxsCmNhbGwgdGhpcyBkYXQ6CmBgYHtyfQpkYXQgPC0gcmVhZFJEUygiaW50ZXJtZWRpYXRlcy8wMS90aWR5LXdlc3QucmRzIikgJT4lCiAgbXV0YXRlKHBvcCA9IHN0cl9zdWIoaWQsIDEsIDMpLAogICAgICAgICBncm91cCA9IGlmZWxzZShwb3AgPT0gIkFRVSIgfCBwb3AgPT0gIldMTiIsICJmYXJtZWQiLCAid2lsZCIpKQpgYGAKCkZpcnN0LCBjb3VudCBob3cgbWFueSBpbmRpdmlkdWFscyB3ZSBoYXZlIGZyb20gZWFjaCB3aWxkIHBvcHVsYXRpb246CmBgYHtyfQpkYXQgJT4lCiAgZ3JvdXBfYnkocG9wKSAlPiUKICBzdW1tYXJpemUobnVtID0gbl9kaXN0aW5jdChpZCkpCmBgYAoKVGhpcyBtZWFucyB0aGF0IGlmIHdlIGFyZSBkb2luZyBGMidzIGZvciBMSFIgd2Ugd2lsbCBvbmx5IGJlIGFibGUgdG8gbWFrZSA0IG9yIDUgcGVyIHRpbWUsCmFuZCBmb3IgYmFja2Nyb3NzZXMsIHdlIHdpbGwgb25seSBnZXQgYWJvdXQgNCBwZXIgZGF0YSBzZXQgb3V0IG9mIHRoZW0uICBPSy4gIFdlbGwsIGxldCB1cyAKbm90IHdvcnJ5IGFib3V0IHRoZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgd2UgZ2V0IGZyb20gZWFjaCBwb3B1bGF0aW9uLiAgTGV0J3MganVzdCBkbyBTUExJVFMgCmRpZmZlcmVudCBzcGxpdHMgb2YgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgaG9sZG91dC4uLgoKClNldCB1cCBzb21lIGluZm8gZm9yIHRoZSBzaW1zOgpgYGB7cn0KU1BMSVRTIDwtIDMgIyB0aGUgbnVtYmVyIG9mIHRpbWVzIHRvIHNwbGl0IHRoZSBkYXRhIGFuZCByYW5rIG1hcmtlcnMKTE9DUyA8LSBjKDQ4LCAxNDQsICAxOTIsIDQ4MCkgICMgbnVtYmVyIG9mIGxvY2kgIEp1c3QgZG8gYSBzbWFsbGVyIG51bWJlciBoZXJlClBPUExJU1QgPC0gYygiQkROIiwgIkNOUiIsICJHUlIiLCAiTEhSIiwgIkxQUiIpICAjIG5hbWVzIG9mIHdpbGQgcG9wdWxhdGlvbnMKSFlCX0NBVFMgPC0gYygiUHVyZVciLCAiUHVyZUYiLCAiRjEiLCAiRjIiLCAiQlgiKQpgYGAKClRoZW4ganVzdCBjeWNsZSBvdmVyIHRoaW5ncyBhbmQgZG8gaXQuICBUaGlzIGlzIGRpZmZlcmVudCB0aGFuIDA3IGJ5IHNldHRpbmcgdGhlIApgd2lsZF9yZWZfcG9wYCB2YXJpYWJsZSB0byBgcG9wYC4gIApgYGB7ciwgZXZhbD1GQUxTRX0KbWFpbl9vdXRfZGlyIDwtICJwb3Bfc3BlY2lmaWNfbmhfcmVwc19kaXJlY3RvcnkiCmRpci5jcmVhdGUobWFpbl9vdXRfZGlyKQpzZXQuc2VlZCg1NTUpCmZvciAocyBpbiAxOlNQTElUUykgewogIFNBUiA8LSBzcGxpdF9hbmRfcmFuayhkYXQpCiAgZm9yIChwb3AgaW4gUE9QTElTVCkgewogICAgZm9yIChoYyBpbiBIWUJfQ0FUUykgewogICAgICBmb3IgKGxvY3MgaW4gTE9DUykgewogICAgICAgIGRpcm5hbWUgPC0gcGFzdGUocyxwb3AsaGMsbG9jcywgc2VwID0gIl8iKQogICAgICAgIGNyZWF0ZV9oeWJyaWRfZGF0YXNldChTQVIgPSBTQVIsIHdpbGRfcG9wID0gcG9wLCB3aWxkX3JlZl9wb3AgPSBwb3AsIGh5Yl9jYXQgPSBoYywgTCA9IGxvY3MsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXIgPSBmaWxlLnBhdGgobWFpbl9vdXRfZGlyLCBkaXJuYW1lKSkKICAgICAgfQogICAgfQogIH0KICAKfQoKYGBgCgpUaGF0IHRha2VzIGEgZmV3IG1pbnV0ZXMgYW5kIGNyZWF0ZXMgYSBsYXJnZSBudW1iZXIgb2YgZGlyZWN0b3JpZXMsIChsaWtlIDUyNSBvciBzbyksIGVhY2ggd2l0aCBhIHNpbmdsZSBzaW11bGF0ZWQgZGF0YSBzZXQgaW4gaXQuICAKVGhlIGRpcmVjdG9yaWVzIGFyZSBuYW1lZCBsaWtlIHRoaXM6IGAxX0dSUl9QdXJlRl80ODBgICB3aGljaCBkZW5vdGVzOgoKLSBTcGxpdCA9IDEKLSBQb3B1bGF0aW9uID0gR1JSCi0gU2ltdWxhdGVkIGh5YnJpZCBjYXRlZ29yeSA9IFB1cmUgRmFybWVkCi0gTnVtYmVyIG9mIGxvY2kgPSA0ODAKCgoKCk5vdyBJIGp1c3QgbmVlZCB0byBydW4gTmV3SHlicmlkcyBvdmVyIGVhY2ggbGlrZSB0aGlzOgpgYGBzaAp+L0RvY3VtZW50cy9naXQtcmVwb3MvbmV3aHlicmlkcy9uZXdoeWJzIC1kIG5oX2RhdGEudHh0IC1nIFAwIDEgMCAwIC1nIHAxIDAgMCAxIC1nIEYxIDAgMSAwIC1nIEYyIC4yNSAuNSAuMjUgLWcgQlgwIC41IC41IDAgLWcgQlgxIDAgLjUgLjUgLS1waS1wcmlvciBmaXhlZCAgMSAxIDEgMSAxIDEKYGBgCgpXZSB3aWxsIGRvIHRoYXQgdXNpbmcgdGhlIEdOVSBwYXJhbGxlbCBzY3JpcHQuCgojIyBDcmVhdGluZyBhIEdOVSBwYXJhbGxlbCBzY3JpcHQKCldlIGNvdWxkIHVzZSB0aGUgYHBhcmFsbGVsYCBSIHBhY2thZ2UsIGJ1dCBJIGhhdmUgaGFkIGJldHRlciBzdWNjZXNzIHVzaW5nIEdOVSBwYXJhbGxlbCAtLSBhIFBlcmwgc2NyaXB0LiAgSSBoYXZlIGluY2x1ZGVkIGl0IGluIHRoZSAKYGJpbmAgZGlyZWN0b3J5IGluIHRoZSByZXBvLgoKV2UgYmFzaWNhbGx5IG5lZWQgdG8gd3JpdGUgYSBzZXJpZXMgb2Ygc2hlbGwgY29tbWFuZHMsIGVhY2ggb25lIGxhdW5jaGluZyBuZXdoeWJyaWRzIG9uIGEgZGlmZmVyZW50IGRhdGEgc2V0LgoKV2UgY2FuIHVzZSBSIHRvIHdyaXRlIHRob3NlIG91dC4gIE5vdGUgdGhhdCB0aGlzIGFsbCBhc3N1bWVzIHRoYXQgdGhlIGBuaF9yZXBzX2RpcmVjdG9yeWAgaXMgaW4gdGhlIHRvcCBsZXZlbApvZiB0aGUgcmVwb3NpdG9yeSwgYW5kIHRoYXQgdGhlIGNvbW1hbmQgdG8gcnVuIHBhcmFsbGVsIHdpbGwgYmUgZ2l2ZW4gaW4gdGhhdCBuaF9yZXBzX2RpcmVjdG9yeS4KTm90ZSB0aGF0IGl0IGlzIGltcG9yYW50IHRvIGRpdmVydCBzdGRlcnIgdG8gYSBuZXdoeWJzX3N0ZGVyciBmaWxlIHNvIHdlIGNhbiBzZWFyY2ggZm9yIGNhc2VzCnRoYXQgaGFkIHVuZGVyZmxvdyBpc3N1ZXMuICBJIGFtIGdvaW5nIHRvIGRvIDEwMCBidXJuIGluIGFuZCA1MDAgc3dlZXBzLCBjdXogd2UgY2FuIGRvIHNob3J0IHJ1bnMKd2hlbiB3ZSBhcmUgd29ya2luZyB3aXRoIGhhdmluZyBzb21lIGluZGl2cyBvZiBrbm93biBvcmlnaW4uCmBgYHtyfQpjb21tcyA8LSBsYXBwbHkoZGlyKCJwb3Bfc3BlY2lmaWNfbmhfcmVwc19kaXJlY3RvcnkiKSwgZnVuY3Rpb24oeCkgewogIHBhc3RlMCgiZWNobyBcIlN0YXJ0aW5nICIsIAogICAgICAgICB4LCAKICAgICAgICAgIiBhdCAkKGRhdGUpXCI7IGNkICIsIAogICAgICAgICB4LCAKICAgICAgICAgIjsgLi4vLi4vYmluL25ld2h5YnMgLWQgbmhfZGF0YS50eHQgLWcgUDAgMSAwIDAgLWcgcDEgMCAwIDEgLWcgRjEgMCAxIDAgLWcgRjIgLjI1IC41IC4yNSAtZyBCWDAgLjUgLjUgMCAtZyBCWDEgMCAuNSAuNSAtLXBpLXByaW9yIGZpeGVkICAxIDEgMSAxIDEgMSAtLW5vLWd1aSAtLWJ1cm4taW4gMTAwIC0tbnVtLXN3ZWVwcyA1MDAgLS1zZWVkcyAiLAogICAgICAgICBwYXN0ZShjZWlsaW5nKHJ1bmlmKDIsIG1pbiA9IDEwMCwgbWF4ID0gMTAwMDAwMDApKSwgY29sbGFwc2UgPSAiICIpLCAKICAgICAgICAgIiA+IG5ld2h5YnNfc3Rkb3V0LnR4dCAyPiBuZXdoeWJzX3N0ZGVyci50eHQ7IGNkIC4uOyBlY2hvIFwiRG9uZSB3aXRoICIsIHgsICIgYXQgJChkYXRlKVwiIikKfSkKY2F0KHVubGlzdChjb21tcyksIHNlcCA9ICJcbiIsIGZpbGUgPSAicG9wLXNwZWMtcGFyYS1jb21tcy50eHQiKQpgYGAKCkkgc3luY2VkIGFsbCB0aGF0IHRvIG91ciAyNCBjb3JlIGJveCBhbmQgdGhlbiBwdXQgdGhlIHJ1bnMgb24gMjIgY29yZXM6CmBgYHNoCjIwMTctMDctMDUgMTI6MDggL3BvcF9zcGVjaWZpY19uaF9yZXBzX2RpcmVjdG9yeS8tLSUgKG1hc3RlcikgcHdkCi9Vc2Vycy9lcmlxL0RvY3VtZW50cy9naXQtcmVwb3MvU2FsYXJIeWJQb3dlci9wb3Bfc3BlY2lmaWNfbmhfcmVwc19kaXJlY3RvcnkKCiMgdGhlbiBwdXQgaXQgb24gMjIgY29yZXM6CjIwMTctMDctMDUgMTI6MDkgL3BvcF9zcGVjaWZpY19uaF9yZXBzX2RpcmVjdG9yeS8tLSUgKG1hc3RlcikgY2F0IC4uL3BvcC1zcGVjLXBhcmEtY29tbXMudHh0IHwgLi4vYmluL3BhcmFsbGVsIC1QMjIgPiAuLi9CSUdfTE9HLnR4dCAmClsxXSAyNjY3MQoKYGBgCgpXaGVuIGRvaW5nIHN1Y2ggc2hvcnQgcnVucywgdGhpcyBzZWVtcyB0byB0YWtlIGxlc3MgdGhhbiBhbiBob3VyLi4uCgojIyBTbHVycGluZyB1cCB0aGUgb3V0cHV0CgpTaW5jZSB0aGlzIGlzIG9uIHRoZSBiaWcgbWFjaGluZSBhdCB3b3JrLCBpdCB3aWxsIGJlIGVhc2llc3QgZm9yIG1lIHRvIGdyYWIKdGhlIG91dHB1dCB1c2luZyBhd2sgb3ZlciBzc2g6CmBgYHNoCjIwMTctMDctMDUgMTI6MjEgL3BvcF9zcGVjaWZpY19uaF9yZXBzX2RpcmVjdG9yeS8tLSUgKG1hc3RlcikgcHdkCi9Vc2Vycy9lcmlxL0RvY3VtZW50cy9naXQtcmVwb3MvU2FsYXJIeWJQb3dlci9wb3Bfc3BlY2lmaWNfbmhfcmVwc19kaXJlY3RvcnkKCmZvciBpIGluICo7IGRvIAogIHdhcm49JCh3YyAtbCAkaS9uZXdoeWJzX3N0ZGVyci50eHQgfCBhd2sgJ3twcmludCAkMX0nKTsgCiAgYXdrIC12IHc9JHdhcm4gLXYgaT0kaSAnTlI+MSAmJiAhL3RyYWluXy8ge3ByaW50IGksIHcsICQwfScgJGkvYWEtUG9mWi50eHQ7CmRvbmUgID4gLi4vcG9wLXNwZWMzX3NwbGl0c19vdXRwdXQudHh0CgoKYGBgClRoZW4gSSBnemlwcGVkIHRoYXQgYW5kIGJyb3VnaHQgaXQgb3ZlciB0byB0aGUgYG91dHB1dHNgIGZvbGRlciBpbiB0aGUgcmVwb3NpdG9yeSAoYnV0IHdoaWNoIApJIHdvbid0IGNvbW1pdC4uLikuICBBbmQgdGhlbiBJIGRlbGV0ZWQgdGhlIGJpZyBzaW11bGF0aW9uIGRpcmVjdG9yeToKYGBgc2gKMjAxNy0wNy0wNSAxMjoyNCAvU2FsYXJIeWJQb3dlci8tLSUgKG1hc3Rlcikgcm0gLXIgcG9wX3NwZWNpZmljX25oX3JlcHNfZGlyZWN0b3J5LwpgYGAKCiMjIFJlYWRpbmcgaW4gdGhlIG91dHB1dCwgYW5kIG1ha2luZyBhIHF1aWNrIHBsb3QKCkZpcnN0LCBhIGpvYiBmb3IgcmVhZHI6CmBgYHtyfQpuaF9vdXRwdXQgPC0gcmVhZF90YWJsZTIoIm91dHB1dHMvcG9wLXNwZWMzX3NwbGl0c19vdXRwdXQudHh0Lmd6IiwgICAjIHJlcXVpcmVzIHJlYWRyIDEuMS4xCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfbmFtZXMgPSBjKCJzaW0iLCAid2FybiIsICJpZHgiLCAiaW5hbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInB1cmVfZmFybWVkIiwgInB1cmVfd2lsZCIsICJGMSIsICJGMiIsICJieF9mYXJtIiwgImJ4X3dpbGQiKSkKYGBgCgpUaGVuIHNlcGFyYXRlIHNvbWUgY29sdW1ucyBhbmQgdGhlbiBnYXRoZXIKYGBge3J9Cm5oX3NlcHBlZCA8LSBuaF9vdXRwdXQgJT4lCiAgc2VwYXJhdGUoY29sID0gc2ltLCBpbnRvID0gYygic3BsaXQiLCAicG9wIiwgInRydWVfaHliX2NhdCIsICJudW1fbG9jaSIpLCBzZXAgPSAiXyIsIGNvbnZlcnQgPSBUUlVFKSAlPiUKICBzZXBhcmF0ZShjb2wgPSBpbmFtZSwgaW50byA9IGMoImRyb3AiLCAic19pZHgiKSwgc2VwID0gIl8iLCBjb252ZXJ0ID0gVFJVRSkgJT4lIAogIG11dGF0ZShzX2lkeCA9IHBhc3RlKCJzcGxpdCIsIHNwbGl0LCBzX2lkeCwgc2VwID0gIl8iKSkgJT4lCiAgc2VsZWN0KC1kcm9wKSAKCgpgYGAKClJlYWxseSBxdWlja2x5LCBjaGVjayB3aGljaCBydW5zIHJlY2VpdmVkIHdhcm5pbmdzOgpgYGB7cn0Kbmhfc2VwcGVkICU+JQogIGZpbHRlcih3YXJuID4gMCkKYGBgCgpOb25lIG9mIHRoZW0sIGN1eiB3ZSBuZXZlciBkaWQgYXMgbWFueSBhcyAxMDAwIGxvY2kuCgpMZXQncyBqdXN0IGZpbHRlciBvdXQgdGhlIG9uZXMgdGhhdCBoYWQgd2FybmluZ3MgYW5kIGdhdGhlciBpdCwgYW5kIG1ha2UgYSBpbmZmZXJyZWRfaHliX2NhdCBhCmZhY3RvciBzbyBpdCBjb21lcyBvdXQgaW4gYSBnb29kIG9yZGVyIGluIHRoZSBwbG90cy4KYGBge3IsIGV2YWw9RkFMU0V9Cm5oX3RpZHkgPC0gbmhfc2VwcGVkICU+JSAKICBmaWx0ZXIod2FybiA9PSAwKSAlPiUgCiAgZ2F0aGVyKGtleSA9ICJpbmZlcnJlZF9oeWJfY2F0IiwgdmFsdWUgPSAicG9zdF9wcm9iIiwgcHVyZV9mYXJtZWQ6Ynhfd2lsZCkgJT4lCiAgbXV0YXRlKGluZmVycmVkX2h5Yl9jYXQgPSBmYWN0b3IoaW5mZXJyZWRfaHliX2NhdCwgbGV2ZWxzID0gYygiYnhfd2lsZCIsICJieF9mYXJtIiwgIkYyIiwgIkYxIiwgInB1cmVfd2lsZCIsICJwdXJlX2Zhcm1lZCIpKSkKCiMgYW5kIHNhdmUgdGhpcwpzYXZlUkRTKG5oX3RpZHksIGZpbGUgPSAib3V0cHV0cy9wb3Bfc3BlYy0zX3NwbGl0c19uaF90aWR5LnJkcyIsIGNvbXByZXNzID0gInh6IikKYGBgCgpBbmQgbm93IHdlIHNob3VsZCBiZSBhYmxlIHRvIHBsb3QgaXQuCmBgYHtyfQojIHNvbWUgY29sb3JzIHRoYXQgbWlnaHQgd29yayBvdXQgYmV0dGVyIHRoYW4gZ2dwbG90cyBkZWZhdWx0cwpoY19jb2xvcnMgPC0gYygKICBwdXJlX2Zhcm1lZCA9IHJnYigxNTIsIDc4LCAxNjMsIG1heENvbG9yVmFsdWUgPSAyNTUpLCAjIHB1cnBsZQogIHB1cmVfd2lsZCA9IHJnYig3NywgMTc1LCA3NCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksICMgZ3JlZW4KICBGMSA9IHJnYigyNTUsIDI1NSwgNTEsIG1heENvbG9yVmFsdWUgPSAyNTUpLCAgIyB5ZWxsb3cKICBGMiA9IHJnYigyNDcsIDEyOSwgMTkxLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgIyBwaW5rCiAgYnhfd2lsZCA9IHJnYigyNTUsIDEyNywgMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksICAjIG9yYW5nZQogIGJ4X2Zhcm0gPSByZ2IoMjI4LCAyNiwgMjgsIG1heENvbG9yVmFsdWUgPSAyNTUpICMgcmVkCikKCm5oX3RpZHkgPC0gcmVhZFJEUygib3V0cHV0cy9wb3Bfc3BlYy0zX3NwbGl0c19uaF90aWR5LnJkcyIpCgpuaF9saXN0IDwtIG5oX3RpZHkgJT4lIAogIHNwbGl0KC4sIC4kdHJ1ZV9oeWJfY2F0KQoKZ2dfbGlzdCA8LSBsYXBwbHkobmhfbGlzdCwgZnVuY3Rpb24oeCkgewogIGcgPC0gZ2dwbG90KHgsIGFlcyh4ID0gc19pZHgsIHkgPSBwb3N0X3Byb2IsIGZpbGwgPSBpbmZlcnJlZF9oeWJfY2F0KSkgKwogICAgZ2VvbV9jb2woKSArCiAgICBmYWNldF9ncmlkKG51bV9sb2NpIH4gcG9wKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBoY19jb2xvcnMpCiAgCiAgZwp9KQoKZHVtcCA8LSBsYXBwbHkobmFtZXMoZ2dfbGlzdCksIGZ1bmN0aW9uKG4pIHsKICBnZ3NhdmUoZ2dfbGlzdFtbbl1dLCAKICAgICAgICAgZmlsZW5hbWUgPSBwYXN0ZTAoIm91dHB1dHMvcG9wLXNwZWMtZmlyc3RfbG9va18iLCBuLCAiLnBkZiIpLAogICAgICAgICB3aWR0aCA9IDEwLCAKICAgICAgICAgaGVpZ2h0ID0gNil9KQpgYGAKClRoYXQgaXMgY29vbC4gIEJ1dCBpdCB3b3VsZCBiZSBtb3JlIGNvb2wgdG8gYmUgYWJsZSB0byBjb21wYXJlIHRob3NlIHRoaW5ncyBtb3JlIGRpcmVjdGx5LgpJIHRoaW5rIHdlIGNhbiByZWFkIHRoZSBub24tcG9wLXNwZWNpZmljIG91dHB1dCBpbiBhbmQgdGhlbiBzdGljayB0aGVtIGluIHRvZ2V0aGVyLgpgYGB7cn0Kbm9uX3NwZWMgPC0gcmVhZFJEUygib3V0cHV0cy8zX3NwbGl0c19uaF90aWR5LnJkcyIpICU+JQogIGZpbHRlcihudW1fbG9jaSAlaW4lIExPQ1MpCgpuaF9jb21ibyA8LSBsaXN0KHNwZWNfcG9wID0gbmhfdGlkeSwKICAgICAgICAgICAgICAgICBhbGxfcG9wID0gbm9uX3NwZWMpICU+JQogIGJpbmRfcm93cyguaWQgPSAid2lsZF9yZWZfdHlwZSIpCgpuaF9jb21ib19saXN0IDwtIG5oX2NvbWJvICU+JSAKICBzcGxpdCguLCAuJHRydWVfaHliX2NhdCkKCmdnX2xpc3QyIDwtIGxhcHBseShuaF9jb21ib19saXN0LCBmdW5jdGlvbih4KSB7CiAgZyA8LSBnZ3Bsb3QoeCwgYWVzKHggPSBzX2lkeCwgeSA9IHBvc3RfcHJvYiwgZmlsbCA9IGluZmVycmVkX2h5Yl9jYXQpKSArCiAgICBnZW9tX2NvbCgpICsKICAgIGZhY2V0X2dyaWQobnVtX2xvY2kgKyB3aWxkX3JlZl90eXBlIH4gcG9wKSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBoY19jb2xvcnMpCiAgCiAgZwp9KQoKZHVtcCA8LSBsYXBwbHkobmFtZXMoZ2dfbGlzdDIpLCBmdW5jdGlvbihuKSB7CiAgZ2dzYXZlKGdnX2xpc3QyW1tuXV0sIAogICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMCgib3V0cHV0cy9zcGVjLXBvcC1yZWYtdnMtYWxsLXJlZiIsIG4sICIucGRmIiksCiAgICAgICAgIHdpZHRoID0gMTAsIAogICAgICAgICBoZWlnaHQgPSAxMil9KQoKYGBgCgpVc2luZyBqdXN0IHRoZSBzcGVjaWZpYyBwb3B1bGF0aW9uIGZvciB3aWxkICJaIiByZWZlcmVuY2UgZmlzaCBpbXByb3ZlcyB0aGUgaWRlbnRpZmljYXRpb24gCm9mIHRydWx5IHdpbGQgZmlzaCBmb3IgTFBSICh0aGV5IGFyZSBsZXNzIGxpa2VseSB0byBiZSBpZGVudGlmaWVkIGFzIEJYX3dpbGQgZmlzaC4pICBPbiB0aGUKb3RoZXIgaGFuZCwgdGhlIFB1cmVGYXJtZWQgZmlzaCBhcmUgbm93IG1vcmUgbGlrZWx5IHRvIGJlIGlkZW50aWZpZWQgYXMgQlhfRmFybWVkLiAgCgpGb3Igc29tZSBwb3B1bGF0aW9ucyBhbmQgc29tZSBoeWJyaWQgY2F0ZWdvcmllcyBpdCBsb29rcyBsaWtlIHNvbWV0aW1lcyBhbGxfcG9wIHdvcmtzIGJldHRlcgp0aGFuIHRoZSBzcGVjX3BvcCB2ZXJzaW9uLiAgCgo=