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.
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(24, 48, 144, 192, 480, 720, 1000) # number of loci
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 <- "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, 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("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 = "para-comms.txt")
I synced all that to our 24 core box and then put the runs on 22 cores:
2017-06-20 14:44 /nh_reps_directory/--% (master) pwd
/Users/eriq/Documents/git-repos/SalarHybPower/nh_reps_directory
# then put it on 22 cores:
2017-06-20 14:45 /nh_reps_directory/--% (master) cat ../para-comms.txt | ../bin/parallel -P22 > ../BIG_LOG.txt &
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-06-21 05:57 /nh_reps_directory/--% (master) pwd
/Users/eriq/Documents/git-repos/SalarHybPower/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 > ../3_splits_output.txt
Then I gzipped that and brought it over to the outputs
folder in the repository (but which I won’t commit…)
Reading in the output, and making a quick plot
First, a job for readr:
nh_output <- read_table2("outputs/3_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)
OK, all of them have num_loci = 1000.
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/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/first_look_", n, ".pdf"),
width = 10,
height = 10)})
I will send those figures to Brendan.
From my first looking over of them, there are a couple of trends it seems to me:
Populations with fewer individuals have fewer simulated individuals—that is why there are gaps in there. (though some of the big gaps at 1000 loci are due to the fact that newhybrids has underflow problems with lots of loci.)
It is important to understand that I simulated individuals from separate populations rather than slurrying their alleles together to make a mean-wild-allele-freq pool. From the results, it is clear that different populations must have different allele frequencies, such that when all the training individuals are used as reference wild and farmed fish with the z
option in NewHybrids, some wild fish of some populations end up looking like bx_wilds, etc. and some pure Farmed fish look like bx_farms. The latter is not so bad, I would think, because you don’t really expect bx_farmed to occur, as often in the populations.
It is interesting that the truly-pure farmed fish are incorrectly inferred to be bx_farm fish at a higher rate with more loci. I have checked my simulation programs and I don’t think they are incorrect. I think it might have to do with using a mixture of populations in the fish with the z
option.
It might be better to run newhybrids with wild training individuals being included and getting the z
option only if they are from the same population as the sampled (simulated) hybrids. This is the next thing I need to get on, but wanted to send these results on for now.
For some of the populations, like BDN, CNR, and LHR it looks like hybrids/farmed fish can be identified pretty reliably. For GRR and LPR, it is a little tougher as there is a lot of overlap between pure wild and backcrossed fish.
LS0tCnRpdGxlOiAiTWFraW5nIE11bHRpcGxlIERhdGEgU2V0cyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgc2V0IHRoZSB3b3JraW5nIGRpcmVjdG9yeSBhbHdheXMgdG8gdGhlIHByb2plY3QgZGlyZWN0b3J5IChvbmUgbGV2ZWwgdXApCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gbm9ybWFsaXplUGF0aChycHJvanJvb3Q6OmZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSkpIApgYGAKCgojIyBJbnRyby9PdmVydmlldwoKSnVzdCBnb2luZyB0byBzaG93IGhvdyB0byBjcmVhdGUgbG90cyBvZiBkYXRhIHNldHMgdGhhdCBjYW4gdGhlbiBiZSBydW4gdGhyb3VnaCBuZXcgaHlicmlkcy4KV2UgY3JlYXRlIG91ciBvd24gbm9tZW5jbGF0dXJlIHNvIHRoYXQgdGhlIGRpcmVjdG9yeSBuYW1lIHRlbGxzIHVzIGFib3V0IHRoZSBzaW11bGF0aW9uLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoU2FsYXJIeWJQb3dlcikKYGBgCgpXZSBhbHNvIHdhbnQgdG8gaGF2ZSBzb21lIGV4YW1wbGUgZGF0YS4gIFdlIHdpbGwganVzdCB1c2UgdGhlIE5vdmEgU2NvdGlhIGRhdGEuIEJ1dCB3ZSB3aWxsCmNhbGwgdGhpcyBkYXQ6CmBgYHtyfQpkYXQgPC0gcmVhZFJEUygiaW50ZXJtZWRpYXRlcy8wMS90aWR5LXdlc3QucmRzIikgJT4lCiAgbXV0YXRlKHBvcCA9IHN0cl9zdWIoaWQsIDEsIDMpLAogICAgICAgICBncm91cCA9IGlmZWxzZShwb3AgPT0gIkFRVSIgfCBwb3AgPT0gIldMTiIsICJmYXJtZWQiLCAid2lsZCIpKQpgYGAKCkZpcnN0LCBjb3VudCBob3cgbWFueSBpbmRpdmlkdWFscyB3ZSBoYXZlIGZyb20gZWFjaCB3aWxkIHBvcHVsYXRpb246CmBgYHtyfQpkYXQgJT4lCiAgZ3JvdXBfYnkocG9wKSAlPiUKICBzdW1tYXJpemUobnVtID0gbl9kaXN0aW5jdChpZCkpCmBgYAoKVGhpcyBtZWFucyB0aGF0IGlmIHdlIGFyZSBkb2luZyBGMidzIGZvciBMSFIgd2Ugd2lsbCBvbmx5IGJlIGFibGUgdG8gbWFrZSA0IG9yIDUgcGVyIHRpbWUsCmFuZCBmb3IgYmFja2Nyb3NzZXMsIHdlIHdpbGwgb25seSBnZXQgYWJvdXQgNCBwZXIgZGF0YSBzZXQgb3V0IG9mIHRoZW0uICBPSy4gIFdlbGwsIGxldCB1cyAKbm90IHdvcnJ5IGFib3V0IHRoZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgd2UgZ2V0IGZyb20gZWFjaCBwb3B1bGF0aW9uLiAgTGV0J3MganVzdCBkbyBTUExJVFMgCmRpZmZlcmVudCBzcGxpdHMgb2YgdGhlIGRhdGEgaW50byB0cmFpbmluZyBhbmQgaG9sZG91dC4uLgoKClNldCB1cCBzb21lIGluZm8gZm9yIHRoZSBzaW1zOgpgYGB7cn0KU1BMSVRTIDwtIDMgIyB0aGUgbnVtYmVyIG9mIHRpbWVzIHRvIHNwbGl0IHRoZSBkYXRhIGFuZCByYW5rIG1hcmtlcnMKTE9DUyA8LSBjKDI0LCA0OCwgMTQ0LCAxOTIsIDQ4MCwgNzIwLCAxMDAwKSAgIyBudW1iZXIgb2YgbG9jaQpQT1BMSVNUIDwtIGMoIkJETiIsICJDTlIiLCAiR1JSIiwgIkxIUiIsICJMUFIiKSAgIyBuYW1lcyBvZiB3aWxkIHBvcHVsYXRpb25zCkhZQl9DQVRTIDwtIGMoIlB1cmVXIiwgIlB1cmVGIiwgIkYxIiwgIkYyIiwgIkJYIikKYGBgCgpUaGVuIGp1c3QgY3ljbGUgb3ZlciB0aGluZ3MgYW5kIGRvIGl0CmBgYHtyLCBldmFsPUZBTFNFfQptYWluX291dF9kaXIgPC0gIm5oX3JlcHNfZGlyZWN0b3J5IgpkaXIuY3JlYXRlKG1haW5fb3V0X2RpcikKc2V0LnNlZWQoNTU1KQpmb3IgKHMgaW4gMTpTUExJVFMpIHsKICBTQVIgPC0gc3BsaXRfYW5kX3JhbmsoZGF0KQogIGZvciAocG9wIGluIFBPUExJU1QpIHsKICAgIGZvciAoaGMgaW4gSFlCX0NBVFMpIHsKICAgICAgZm9yIChsb2NzIGluIExPQ1MpIHsKICAgICAgICBkaXJuYW1lIDwtIHBhc3RlKHMscG9wLGhjLGxvY3MsIHNlcCA9ICJfIikKICAgICAgICBjcmVhdGVfaHlicmlkX2RhdGFzZXQoU0FSID0gU0FSLCB3aWxkX3BvcCA9IHBvcCwgaHliX2NhdCA9IGhjLCBMID0gbG9jcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpciA9IGZpbGUucGF0aChtYWluX291dF9kaXIsIGRpcm5hbWUpKQogICAgICB9CiAgICB9CiAgfQogIAp9CgpgYGAKClRoYXQgdGFrZXMgYSBmZXcgbWludXRlcyBhbmQgY3JlYXRlcyBhIGxhcmdlIG51bWJlciBvZiBkaXJlY3RvcmllcywgKGxpa2UgNTI1IG9yIHNvKSwgZWFjaCB3aXRoIGEgc2luZ2xlIHNpbXVsYXRlZCBkYXRhIHNldCBpbiBpdC4gIApUaGUgZGlyZWN0b3JpZXMgYXJlIG5hbWVkIGxpa2UgdGhpczogYDFfR1JSX1B1cmVGXzQ4MGAgIHdoaWNoIGRlbm90ZXM6CgotIFNwbGl0ID0gMQotIFBvcHVsYXRpb24gPSBHUlIKLSBTaW11bGF0ZWQgaHlicmlkIGNhdGVnb3J5ID0gUHVyZSBGYXJtZWQKLSBOdW1iZXIgb2YgbG9jaSA9IDQ4MAoKCgoKTm93IEkganVzdCBuZWVkIHRvIHJ1biBOZXdIeWJyaWRzIG92ZXIgZWFjaCBsaWtlIHRoaXM6CmBgYHNoCn4vRG9jdW1lbnRzL2dpdC1yZXBvcy9uZXdoeWJyaWRzL25ld2h5YnMgLWQgbmhfZGF0YS50eHQgLWcgUDAgMSAwIDAgLWcgcDEgMCAwIDEgLWcgRjEgMCAxIDAgLWcgRjIgLjI1IC41IC4yNSAtZyBCWDAgLjUgLjUgMCAtZyBCWDEgMCAuNSAuNSAtLXBpLXByaW9yIGZpeGVkICAxIDEgMSAxIDEgMQpgYGAKCldlIHdpbGwgZG8gdGhhdCB1c2luZyB0aGUgR05VIHBhcmFsbGVsIHNjcmlwdC4KCiMjIENyZWF0aW5nIGEgR05VIHBhcmFsbGVsIHNjcmlwdAoKV2UgY291bGQgdXNlIHRoZSBgcGFyYWxsZWxgIFIgcGFja2FnZSwgYnV0IEkgaGF2ZSBoYWQgYmV0dGVyIHN1Y2Nlc3MgdXNpbmcgR05VIHBhcmFsbGVsIC0tIGEgUGVybCBzY3JpcHQuICBJIGhhdmUgaW5jbHVkZWQgaXQgaW4gdGhlIApgYmluYCBkaXJlY3RvcnkgaW4gdGhlIHJlcG8uCgpXZSBiYXNpY2FsbHkgbmVlZCB0byB3cml0ZSBhIHNlcmllcyBvZiBzaGVsbCBjb21tYW5kcywgZWFjaCBvbmUgbGF1bmNoaW5nIG5ld2h5YnJpZHMgb24gYSBkaWZmZXJlbnQgZGF0YSBzZXQuCgpXZSBjYW4gdXNlIFIgdG8gd3JpdGUgdGhvc2Ugb3V0LiAgTm90ZSB0aGF0IHRoaXMgYWxsIGFzc3VtZXMgdGhhdCB0aGUgYG5oX3JlcHNfZGlyZWN0b3J5YCBpcyBpbiB0aGUgdG9wIGxldmVsCm9mIHRoZSByZXBvc2l0b3J5LCBhbmQgdGhhdCB0aGUgY29tbWFuZCB0byBydW4gcGFyYWxsZWwgd2lsbCBiZSBnaXZlbiBpbiB0aGF0IG5oX3JlcHNfZGlyZWN0b3J5LgpOb3RlIHRoYXQgaXQgaXMgaW1wb3JhbnQgdG8gZGl2ZXJ0IHN0ZGVyciB0byBhIG5ld2h5YnNfc3RkZXJyIGZpbGUgc28gd2UgY2FuIHNlYXJjaCBmb3IgY2FzZXMKdGhhdCBoYWQgdW5kZXJmbG93IGlzc3Vlcy4gIEkgYW0gZ29pbmcgdG8gZG8gMTAwIGJ1cm4gaW4gYW5kIDUwMCBzd2VlcHMsIGN1eiB3ZSBjYW4gZG8gc2hvcnQgcnVucwp3aGVuIHdlIGFyZSB3b3JraW5nIHdpdGggaGF2aW5nIHNvbWUgaW5kaXZzIG9mIGtub3duIG9yaWdpbi4KYGBge3J9CmNvbW1zIDwtIGxhcHBseShkaXIoIm5oX3JlcHNfZGlyZWN0b3J5IiksIGZ1bmN0aW9uKHgpIHsKICBwYXN0ZTAoImVjaG8gXCJTdGFydGluZyAiLCAKICAgICAgICAgeCwgCiAgICAgICAgICIgYXQgJChkYXRlKVwiOyBjZCAiLCAKICAgICAgICAgeCwgCiAgICAgICAgICI7IC4uLy4uL2Jpbi9uZXdoeWJzIC1kIG5oX2RhdGEudHh0IC1nIFAwIDEgMCAwIC1nIHAxIDAgMCAxIC1nIEYxIDAgMSAwIC1nIEYyIC4yNSAuNSAuMjUgLWcgQlgwIC41IC41IDAgLWcgQlgxIDAgLjUgLjUgLS1waS1wcmlvciBmaXhlZCAgMSAxIDEgMSAxIDEgLS1uby1ndWkgLS1idXJuLWluIDEwMCAtLW51bS1zd2VlcHMgNTAwIC0tc2VlZHMgIiwKICAgICAgICAgcGFzdGUoY2VpbGluZyhydW5pZigyLCBtaW4gPSAxMDAsIG1heCA9IDEwMDAwMDAwKSksIGNvbGxhcHNlID0gIiAiKSwgCiAgICAgICAgICIgPiBuZXdoeWJzX3N0ZG91dC50eHQgMj4gbmV3aHlic19zdGRlcnIudHh0OyBjZCAuLjsgZWNobyBcIkRvbmUgd2l0aCAiLCB4LCAiIGF0ICQoZGF0ZSlcIiIpCn0pCmNhdCh1bmxpc3QoY29tbXMpLCBzZXAgPSAiXG4iLCBmaWxlID0gInBhcmEtY29tbXMudHh0IikKYGBgCgpJIHN5bmNlZCBhbGwgdGhhdCB0byBvdXIgMjQgY29yZSBib3ggYW5kIHRoZW4gcHV0IHRoZSBydW5zIG9uIDIyIGNvcmVzOgpgYGBzaAoyMDE3LTA2LTIwIDE0OjQ0IC9uaF9yZXBzX2RpcmVjdG9yeS8tLSUgKG1hc3RlcikgcHdkCi9Vc2Vycy9lcmlxL0RvY3VtZW50cy9naXQtcmVwb3MvU2FsYXJIeWJQb3dlci9uaF9yZXBzX2RpcmVjdG9yeQoKIyB0aGVuIHB1dCBpdCBvbiAyMiBjb3JlczoKMjAxNy0wNi0yMCAxNDo0NSAvbmhfcmVwc19kaXJlY3RvcnkvLS0lIChtYXN0ZXIpIGNhdCAuLi9wYXJhLWNvbW1zLnR4dCB8IC4uL2Jpbi9wYXJhbGxlbCAtUDIyID4gLi4vQklHX0xPRy50eHQgJgoKYGBgCgpXaGVuIGRvaW5nIHN1Y2ggc2hvcnQgcnVucywgdGhpcyBzZWVtcyB0byB0YWtlIGxlc3MgdGhhbiBhbiBob3VyLi4uCgojIyBTbHVycGluZyB1cCB0aGUgb3V0cHV0CgpTaW5jZSB0aGlzIGlzIG9uIHRoZSBiaWcgbWFjaGluZSBhdCB3b3JrLCBpdCB3aWxsIGJlIGVhc2llc3QgZm9yIG1lIHRvIGdyYWIKdGhlIG91dHB1dCB1c2luZyBhd2sgb3ZlciBzc2g6CmBgYHNoCjIwMTctMDYtMjEgMDU6NTcgL25oX3JlcHNfZGlyZWN0b3J5Ly0tJSAobWFzdGVyKSBwd2QKL1VzZXJzL2VyaXEvRG9jdW1lbnRzL2dpdC1yZXBvcy9TYWxhckh5YlBvd2VyL25oX3JlcHNfZGlyZWN0b3J5Cgpmb3IgaSBpbiAqOyBkbyAKICB3YXJuPSQod2MgLWwgJGkvbmV3aHlic19zdGRlcnIudHh0IHwgYXdrICd7cHJpbnQgJDF9Jyk7IAogIGF3ayAtdiB3PSR3YXJuIC12IGk9JGkgJ05SPjEgJiYgIS90cmFpbl8vIHtwcmludCBpLCB3LCAkMH0nICRpL2FhLVBvZloudHh0Owpkb25lICA+IC4uLzNfc3BsaXRzX291dHB1dC50eHQKCgpgYGAKVGhlbiBJIGd6aXBwZWQgdGhhdCBhbmQgYnJvdWdodCBpdCBvdmVyIHRvIHRoZSBgb3V0cHV0c2AgZm9sZGVyIGluIHRoZSByZXBvc2l0b3J5IChidXQgd2hpY2ggCkkgd29uJ3QgY29tbWl0Li4uKQoKIyMgUmVhZGluZyBpbiB0aGUgb3V0cHV0LCBhbmQgbWFraW5nIGEgcXVpY2sgcGxvdAoKRmlyc3QsIGEgam9iIGZvciByZWFkcjoKYGBge3J9Cm5oX291dHB1dCA8LSByZWFkX3RhYmxlMigib3V0cHV0cy8zX3NwbGl0c19vdXRwdXQudHh0Lmd6IiwgICAjIHJlcXVpcmVzIHJlYWRyIDEuMS4xCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfbmFtZXMgPSBjKCJzaW0iLCAid2FybiIsICJpZHgiLCAiaW5hbWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInB1cmVfZmFybWVkIiwgInB1cmVfd2lsZCIsICJGMSIsICJGMiIsICJieF9mYXJtIiwgImJ4X3dpbGQiKSkKYGBgCgpUaGVuIHNlcGFyYXRlIHNvbWUgY29sdW1ucyBhbmQgdGhlbiBnYXRoZXIKYGBge3J9Cm5oX3NlcHBlZCA8LSBuaF9vdXRwdXQgJT4lCiAgc2VwYXJhdGUoY29sID0gc2ltLCBpbnRvID0gYygic3BsaXQiLCAicG9wIiwgInRydWVfaHliX2NhdCIsICJudW1fbG9jaSIpLCBzZXAgPSAiXyIsIGNvbnZlcnQgPSBUUlVFKSAlPiUKICBzZXBhcmF0ZShjb2wgPSBpbmFtZSwgaW50byA9IGMoImRyb3AiLCAic19pZHgiKSwgc2VwID0gIl8iLCBjb252ZXJ0ID0gVFJVRSkgJT4lIAogIG11dGF0ZShzX2lkeCA9IHBhc3RlKCJzcGxpdCIsIHNwbGl0LCBzX2lkeCwgc2VwID0gIl8iKSkgJT4lCiAgc2VsZWN0KC1kcm9wKSAKCgpgYGAKClJlYWxseSBxdWlja2x5LCBjaGVjayB3aGljaCBydW5zIHJlY2VpdmVkIHdhcm5pbmdzOgpgYGB7cn0Kbmhfc2VwcGVkICU+JQogIGZpbHRlcih3YXJuID4gMCkKYGBgCgpPSywgYWxsIG9mIHRoZW0gaGF2ZSBudW1fbG9jaSA9IDEwMDAuCgpMZXQncyBqdXN0IGZpbHRlciBvdXQgdGhlIG9uZXMgdGhhdCBoYWQgd2FybmluZ3MgYW5kIGdhdGhlciBpdCwgYW5kIG1ha2UgYSBpbmZmZXJyZWRfaHliX2NhdCBhCmZhY3RvciBzbyBpdCBjb21lcyBvdXQgaW4gYSBnb29kIG9yZGVyIGluIHRoZSBwbG90cy4KYGBge3J9Cm5oX3RpZHkgPC0gbmhfc2VwcGVkICU+JSAKICBmaWx0ZXIod2FybiA9PSAwKSAlPiUgCiAgZ2F0aGVyKGtleSA9ICJpbmZlcnJlZF9oeWJfY2F0IiwgdmFsdWUgPSAicG9zdF9wcm9iIiwgcHVyZV9mYXJtZWQ6Ynhfd2lsZCkgJT4lCiAgbXV0YXRlKGluZmVycmVkX2h5Yl9jYXQgPSBmYWN0b3IoaW5mZXJyZWRfaHliX2NhdCwgbGV2ZWxzID0gYygiYnhfd2lsZCIsICJieF9mYXJtIiwgIkYyIiwgIkYxIiwgInB1cmVfd2lsZCIsICJwdXJlX2Zhcm1lZCIpKSkKCiMgYW5kIHNhdmUgdGhpcwpzYXZlUkRTKG5oX3RpZHksIGZpbGUgPSAib3V0cHV0cy8zX3NwbGl0c19uaF90aWR5LnJkcyIsIGNvbXByZXNzID0gInh6IikKYGBgCgpBbmQgbm93IHdlIHNob3VsZCBiZSBhYmxlIHRvIHBsb3QgaXQuCmBgYHtyfQojIHNvbWUgY29sb3JzIHRoYXQgbWlnaHQgd29yayBvdXQgYmV0dGVyIHRoYW4gZ2dwbG90cyBkZWZhdWx0cwpoY19jb2xvcnMgPC0gYygKICBwdXJlX2Zhcm1lZCA9IHJnYigxNTIsIDc4LCAxNjMsIG1heENvbG9yVmFsdWUgPSAyNTUpLCAjIHB1cnBsZQogIHB1cmVfd2lsZCA9IHJnYig3NywgMTc1LCA3NCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksICMgZ3JlZW4KICBGMSA9IHJnYigyNTUsIDI1NSwgNTEsIG1heENvbG9yVmFsdWUgPSAyNTUpLCAgIyB5ZWxsb3cKICBGMiA9IHJnYigyNDcsIDEyOSwgMTkxLCBtYXhDb2xvclZhbHVlID0gMjU1KSwgIyBwaW5rCiAgYnhfd2lsZCA9IHJnYigyNTUsIDEyNywgMCwgbWF4Q29sb3JWYWx1ZSA9IDI1NSksICAjIG9yYW5nZQogIGJ4X2Zhcm0gPSByZ2IoMjI4LCAyNiwgMjgsIG1heENvbG9yVmFsdWUgPSAyNTUpICMgcmVkCikKCm5oX2xpc3QgPC0gbmhfdGlkeSAlPiUgCiAgc3BsaXQoLiwgLiR0cnVlX2h5Yl9jYXQpCgpnZ19saXN0IDwtIGxhcHBseShuaF9saXN0LCBmdW5jdGlvbih4KSB7CiAgZyA8LSBnZ3Bsb3QoeCwgYWVzKHggPSBzX2lkeCwgeSA9IHBvc3RfcHJvYiwgZmlsbCA9IGluZmVycmVkX2h5Yl9jYXQpKSArCiAgICBnZW9tX2NvbCgpICsKICAgIGZhY2V0X2dyaWQobnVtX2xvY2kgfiBwb3ApICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGhjX2NvbG9ycykKICAKICBnCn0pCgpkdW1wIDwtIGxhcHBseShuYW1lcyhnZ19saXN0KSwgZnVuY3Rpb24obikgewogIGdnc2F2ZShnZ19saXN0W1tuXV0sIAogICAgICAgICBmaWxlbmFtZSA9IHBhc3RlMCgib3V0cHV0cy9maXJzdF9sb29rXyIsIG4sICIucGRmIiksCiAgICAgICAgIHdpZHRoID0gMTAsIAogICAgICAgICBoZWlnaHQgPSAxMCl9KQpgYGAKCkkgd2lsbCBzZW5kIHRob3NlIGZpZ3VyZXMgdG8gQnJlbmRhbi4KCkZyb20gbXkgZmlyc3QgbG9va2luZyBvdmVyIG9mIHRoZW0sIHRoZXJlIGFyZSBhIGNvdXBsZSBvZiB0cmVuZHMgaXQgc2VlbXMgdG8gbWU6CgotIFBvcHVsYXRpb25zIHdpdGggZmV3ZXIgaW5kaXZpZHVhbHMgaGF2ZSBmZXdlciBzaW11bGF0ZWQgaW5kaXZpZHVhbHMtLS10aGF0IGlzIHdoeSB0aGVyZSBhcmUgZ2FwcyBpbiB0aGVyZS4KKHRob3VnaCBzb21lIG9mIHRoZSBiaWcgZ2FwcyBhdCAxMDAwIGxvY2kgYXJlIGR1ZSB0byB0aGUgZmFjdCB0aGF0IG5ld2h5YnJpZHMgaGFzIHVuZGVyZmxvdyBwcm9ibGVtcyB3aXRoIApsb3RzIG9mIGxvY2kuKQoKLSBJdCBpcyBpbXBvcnRhbnQgdG8gdW5kZXJzdGFuZCB0aGF0IEkgc2ltdWxhdGVkIGluZGl2aWR1YWxzIGZyb20gc2VwYXJhdGUgcG9wdWxhdGlvbnMgcmF0aGVyCnRoYW4gc2x1cnJ5aW5nIHRoZWlyIGFsbGVsZXMgdG9nZXRoZXIgdG8gbWFrZSBhIG1lYW4td2lsZC1hbGxlbGUtZnJlcSBwb29sLiAgRnJvbSB0aGUgcmVzdWx0cywgaXQgaXMKY2xlYXIgdGhhdCBkaWZmZXJlbnQgcG9wdWxhdGlvbnMgbXVzdCBoYXZlIGRpZmZlcmVudCBhbGxlbGUgZnJlcXVlbmNpZXMsIHN1Y2ggdGhhdCB3aGVuIGFsbCB0aGUgdHJhaW5pbmcgaW5kaXZpZHVhbHMKYXJlIHVzZWQgYXMgcmVmZXJlbmNlIHdpbGQgYW5kIGZhcm1lZCBmaXNoIHdpdGggdGhlIGB6YCBvcHRpb24gaW4gTmV3SHlicmlkcywgc29tZSB3aWxkIGZpc2ggb2Ygc29tZSBwb3B1bGF0aW9ucwplbmQgdXAgbG9va2luZyBsaWtlIGJ4X3dpbGRzLCBldGMuICBfYW5kXyBzb21lIHB1cmUgRmFybWVkIGZpc2ggbG9vayBsaWtlIGJ4X2Zhcm1zLiAgVGhlIGxhdHRlciBpcyBub3Qgc28gYmFkLCBJIHdvdWxkIHRoaW5rLApiZWNhdXNlIHlvdSBkb24ndCByZWFsbHkgZXhwZWN0IGJ4X2Zhcm1lZCB0byBvY2N1ciwgYXMgb2Z0ZW4gaW4gdGhlIHBvcHVsYXRpb25zLgoKLSBJdCBpcyBpbnRlcmVzdGluZyB0aGF0IHRoZSB0cnVseS1wdXJlIGZhcm1lZCBmaXNoIGFyZSBpbmNvcnJlY3RseSBpbmZlcnJlZCB0byBiZSBieF9mYXJtIGZpc2ggYXQgYSBoaWdoZXIgcmF0ZSB3aXRoCm1vcmUgbG9jaS4gIEkgaGF2ZSBjaGVja2VkIG15IHNpbXVsYXRpb24gcHJvZ3JhbXMgYW5kIEkgZG9uJ3QgdGhpbmsgdGhleSBhcmUgaW5jb3JyZWN0LiAgSSB0aGluayBpdCBtaWdodCBoYXZlIHRvIGRvIAp3aXRoIHVzaW5nIGEgbWl4dHVyZSBvZiBwb3B1bGF0aW9ucyBpbiB0aGUgZmlzaCB3aXRoIHRoZSBgemAgb3B0aW9uLgoKLSBJdCBtaWdodCBiZSBiZXR0ZXIgdG8gcnVuIG5ld2h5YnJpZHMgd2l0aCB3aWxkIHRyYWluaW5nIGluZGl2aWR1YWxzIGJlaW5nIGluY2x1ZGVkIGFuZCBnZXR0aW5nIHRoZSBgemAgb3B0aW9uCl9vbmx5IGlmIHRoZXkgYXJlIGZyb20gdGhlIHNhbWUgcG9wdWxhdGlvbiBhcyB0aGUgc2FtcGxlZCAoc2ltdWxhdGVkKSBoeWJyaWRzLl8gIFRoaXMgaXMgdGhlIG5leHQgdGhpbmcgSSBuZWVkIAp0byBnZXQgb24sIGJ1dCB3YW50ZWQgdG8gc2VuZCB0aGVzZSByZXN1bHRzIG9uIGZvciBub3cuCgotIEZvciBzb21lIG9mIHRoZSBwb3B1bGF0aW9ucywgbGlrZSBCRE4sIENOUiwgYW5kIExIUiBpdCBsb29rcyBsaWtlIGh5YnJpZHMvZmFybWVkIGZpc2ggY2FuIGJlIGlkZW50aWZpZWQKcHJldHR5IHJlbGlhYmx5LiAgRm9yIEdSUiBhbmQgTFBSLCBpdCBpcyBhIGxpdHRsZSB0b3VnaGVyIGFzIHRoZXJlIGlzIGEgbG90IG9mIG92ZXJsYXAgYmV0d2VlbiBwdXJlIHdpbGQgYW5kIApiYWNrY3Jvc3NlZCBmaXNoLiAgCg==