Let’s do this with Newfoundland as that is all I have the simulations for yet.
Let’s slurp up the results, and let’s make the names of the inferred and true hyb cats the same
Verifying Accuracy and Efficiency Calculations
Brendan threw done some good pipes and mutates and group_by()’s to compute the accuracy and efficiency for each population at different numbers of loci. I just want to confirm those results and see if we can do the same with a little dplyr::do()
(since I just recently started using that…and it is wickedly powerful!)
Our starting point for this will be the nf_res
tibble, which are all the results after I standardized the hybrid categories, and made them all characters rather than factors (just like Brendan did in his first step!).
Accuracy
This is defined in the paper as “the proportion of an identified group that truly belongs to that category”. We will say that individuals are inferred to be a certain category if their posterior for that category is maximal. And when we talk about applying a posterior probability threshold, then the only way that makes sense is if you will not be assigning individuals to a category if their posterior probability is less than that threshold.
We filter the data set to just those inferred categories with maximal posterior probability:
max_cats <- nf_res %>%
group_by(pop, split, true_hyb_cat, num_loci, idx) %>%
top_n(n = 1, wt = post_prob) %>%
ungroup()
Now, we want to get the fraction of these inferred categories that are correct for each value of post_prob from 0.5 to 1.0 in steps of 0.01. Of course, we want to make that calculation within each pop and inferred_hyb_cat and num_loci. (Note that we don’t group by true_hyb_cat, because we are interested in seeing how well we have done amongst all individuals inferred to be of a particular hybrid category.)
Once again, this is a job for dplyr::do()
. But we will want to write a function that returns a tibble for it.
#' @param pp is the posterior prob
#' @param ihc is the inferred hybrid category
#' @param thc is the active ingredient in marijuana, but here also the "true hybrid category"
accu_func <- function(pp, ihc, thc) {
crit_post_prob <- seq(0.5, 0.99, by = 0.01) # crititcal posterior probs to compute for. Don't do 1.0...
accuracy <- sapply(crit_post_prob, function(x) {
mean((thc == ihc)[pp >= x]) # fraction of the individuals with a pp > the critical value that were inferred correctly
})
tibble(crit_post_prob = crit_post_prob,
accuracy = accuracy)
}
Then, group by pop, inferred_hyb_cat, and num_loci and compute it.
accu_df <- max_cats %>%
group_by(pop, inferred_hyb_cat, num_loci) %>%
dplyr::do(accu_func(pp = .$post_prob,
ihc = .$inferred_hyb_cat,
thc = .$true_hyb_cat))
And we can plot those all at once on a single big page in a format kind of like what Brendan did:
ggplot(accu_df, aes(x = crit_post_prob, y = accuracy, colour = inferred_hyb_cat)) +
geom_line() +
facet_grid(pop ~ num_loci)

I find it easier to look at and interpet if we color by number of loci and facet on inferred hybrid category.
ggplot(accu_df, aes(x = crit_post_prob, y = accuracy, colour = factor(num_loci))) +
geom_line() +
facet_grid(pop ~ inferred_hyb_cat)

Efficiency
Efficiency is defined in the paper as “the proportion of individuals in a group that were correctly identified”. So, it should be the case that we will be grouping by the true hybrid category. And we will need a function to compute the efficiency. But what is interesting here, is that the fraction should drop off as the posterior prob threshold gets higher (because more individuals are being left unassigned).
#' @param pp is the posterior prob
#' @param ihc is the inferred hybrid category
#' @param thc is the active ingredient in marijuana, but here also the "true hybrid category"
effi_func <- function(pp, ihc, thc) {
crit_post_prob <- seq(0.5, 0.99, by = 0.01) # critical posterior probs to compute for. Don't do 1.0...
efficiency <- sapply(crit_post_prob, function(x) {
mean(thc == ihc & pp >= x) # fraction of _all_ individuals that had pp > crit and thc == ihc
})
tibble(crit_post_prob = crit_post_prob,
efficiency = efficiency)
}
Now, go for it:
effi_df <- max_cats %>%
group_by(pop, true_hyb_cat, num_loci) %>%
dplyr::do(effi_func(pp = .$post_prob,
ihc = .$inferred_hyb_cat,
thc = .$true_hyb_cat))
And plot that dude:
ggplot(effi_df, aes(x = crit_post_prob, y = efficiency, colour = factor(num_loci))) +
geom_line() +
facet_grid(pop ~ true_hyb_cat)

Just inferring hybrid or non-hybrid…
This is worth looking at. We will infer each individual to just be hybrid or non-hybrid and then look at the accuracy of those. We do that with a summarise, then take the max posterior of it.
nf_squashed_cats <- nf_res %>%
mutate(inferred_hyb_v_non_hyb = ifelse(stringr::str_detect(inferred_hyb_cat, "pure"), "pure", "hybrid"),
actual_hyb_v_non_hyb = ifelse(stringr::str_detect(true_hyb_cat, "pure"), "pure", "hybrid")) %>%
group_by(pop, split, num_loci, true_hyb_cat, idx, actual_hyb_v_non_hyb, inferred_hyb_v_non_hyb) %>%
summarise(post_prob = sum(post_prob)) %>%
top_n(n = 1, post_prob) %>% # "assign" them to hyb or non-hyb category
ungroup()
And now we can compute accuracy and efficiency.
Accuracy
squashed_accu_df <- nf_squashed_cats %>%
group_by(pop, inferred_hyb_v_non_hyb, num_loci) %>%
dplyr::do(accu_func(pp = .$post_prob,
ihc = .$inferred_hyb_v_non_hyb,
thc = .$actual_hyb_v_non_hyb))
Plot it:
ggplot(squashed_accu_df, aes(x = crit_post_prob, y = accuracy, colour = factor(num_loci))) +
geom_line() +
facet_grid(pop ~ inferred_hyb_v_non_hyb)

Efficiency
squashed_effi_df <- nf_squashed_cats %>%
group_by(pop, true_hyb_cat, num_loci) %>%
dplyr::do(effi_func(pp = .$post_prob,
ihc = .$inferred_hyb_v_non_hyb,
thc = .$actual_hyb_v_non_hyb))
Plot it:
ggplot(squashed_effi_df, aes(x = crit_post_prob, y = efficiency, colour = factor(num_loci))) +
geom_line() +
facet_grid(pop ~ true_hyb_cat)

ROC Curves
I have a harder time interpreting the “power = accuracy * efficiency” summary than I do looking at the receiver operating characteristic (ROC) curve. The ROC curve is another summary that includes both the type 1 and type 2 errors and which I find easier to get my head around, so I will see what those look like in this case.
On another note, the “accuracy” statistic is hard to interpret because it really depends on what other types of hybrid categories are “in the mix.” For example, if you are looking at the “accuracy” of inferred F2 individuals, that quantity depends on the relative amounts of F2, F1, and backcross individuals. For example, if your simulated data set includes 10 F2s, 10000 F1s, and 10000 bx-s, then the accuracy for inferred F2’s might look really bad if it is hard to distinguish F1s from F2s, etc. On the other hand, if your simulated data includes 10 F2s and 10 PureWilds, then you will come out with a higher value for accuracy. One of the things I like about the ROC curves is that they show you a type 1 and type 2 error rate summary between each category.
I think the ROC curve should be pretty easy to compute, using a sequence of scaled likelihoods from 0 to 1 in steps of 0.005, say. One important point to keep in mind is that the false positive rate must be a function of the true hybrid category. We will honor that by making a multipanel plot, creating ROC curves for all the different kinds of situations (i.e. inferring F2’s when the truth is BX, inferring F2’s when the truth is Pure Wild, etc).
We can compute these ROC curves in the tidyverse by throwing down some dplyr::do()
along with the ecdf()
function.
options(dplyr.show_progress = FALSE)
pos_rates <- nf_res %>%
group_by(pop, true_hyb_cat, inferred_hyb_cat, num_loci) %>%
dplyr::do(
tibble(
pthresh = seq(0, 1, by = 0.005),
pgreater = 1 - ecdf(.$post_prob)(seq(0, 1, by = 0.005))
)
) %>%
ungroup()
Now, all we need to do is a quick left join to get ready to plot some of these:
rocdf <- pos_rates %>%
filter(true_hyb_cat == inferred_hyb_cat) %>%
rename(true_pos_rate = pgreater,
focal_hyb_cat = true_hyb_cat) %>%
select(-inferred_hyb_cat) %>%
left_join(., pos_rates) %>%
filter(focal_hyb_cat != true_hyb_cat) %>% # to give us an empty plot rather than a y = x line on the diagonal
filter(focal_hyb_cat == inferred_hyb_cat) %>% # these are the values we care about
rename(false_pos_rate = pgreater) %>%
mutate(num_loci = factor(num_loci)) %>%
mutate(true_hyb_cat = paste("true", true_hyb_cat),
focal_hyb_cat = paste("infd", focal_hyb_cat))
Joining, by = c("pop", "num_loci", "pthresh")
Let’s see how these things plot out:
dump <- lapply(unique(rocdf$pop), function(x) {
g <- ggplot(rocdf %>% filter(pop == x), aes(x = false_pos_rate, y = true_pos_rate, colour = num_loci)) +
geom_line() +
facet_grid(true_hyb_cat ~ focal_hyb_cat) +
ggtitle(x) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))
ggsave(g, filename = paste0("outputs/roc_NF_", x, ".pdf"))
})
Saving 7 x 7 in image
I made PDFs of all of those, but here is how it looks for BDN:
ggplot(rocdf %>% filter(pop == "BDN"), aes(x = false_pos_rate, y = true_pos_rate, colour = num_loci)) +
geom_line() +
facet_grid(true_hyb_cat ~ focal_hyb_cat) +
ggtitle("BDN") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))

I saved these in the files roc_NF_XXX.pdf
. Note that “infd” means “inferred” in the legend, and “true” is referring to the true hybrid categories.
The way to interpret these is this: when you are looking at the columns you are assessing what the true positive rate is when inferring the hybrid category that is listed at the top of the column as a function of the false positive rate for the hybrid category that is listed to the right. So, for example, if you are trying to identify F2s in the BDN population, and you have 48 loci, then when you set your F2 posterior probability to \(T\), such that 75% of the F2s you encounter are expected to have a posterior \(\geq T\), you expect that 25% of bx_wild fish will also have an F2 posterior \(\geq T\). This information can be gleaned from the graph in the first row (“true bx_wild”) and the third column (“infd F2”).
Or, if we were to look at the results for LPR we would find that when things are truly pure_wild (“true pure_wild”), and you are inferring bx_wilds (“infd bx_wild”), things are not looking so good. There is not cutoff \(T\) that makes us unlikely to incorrectly infer that pure wild fish are bx_wild fish. We saw that already in the preliminary results I sent a while ago.
That is pretty cool, but if we wanted to also try to show what the scaled likelihood cutoff levels are, we can do that too.
dump <- lapply(unique(rocdf$pop), function(x) {
g <- ggplot(rocdf %>% filter(pop == x), aes(x = false_pos_rate, y = true_pos_rate, colour = pthresh,
group = num_loci)) +
geom_line() +
facet_grid(true_hyb_cat ~ focal_hyb_cat) +
ggtitle(x) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5)) +
scale_colour_viridis()
ggsave(g, filename = paste0("outputs/roc_NF_pcolor", x, ".pdf"))
})
Saving 7 x 7 in image
These are saved in the files, “roc_NF_pcolorXXX.pdf”. Here is what it looks like for BDN:
ggplot(rocdf %>% filter(pop == "BDN"), aes(x = false_pos_rate, y = true_pos_rate, colour = pthresh,
group = num_loci)) +
geom_line() +
facet_grid(true_hyb_cat ~ focal_hyb_cat) +
ggtitle("BDN") +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5)) +
scale_colour_viridis()

What we see there is that with 48 loci, if you want a true positive rate of 75% for F2s, you are going to have to use a posterior probability cutoff that looks to be around 30%, which makes it clear why you’d suffer pretty high false positive rates from the bx_wild category.
Hybrid vs non-hybrid
In addition to the above, it would be nice to see for each true hybrid category, how well one can do with just declaring a fish a hybrid (of some sort) or not. One would think this shouldn’t be too hard to get at. We just want to collapse the inferred hybrid category down to hybrid or non-hybrid. But then you don’t want to squash the correct categories down to that. It ends up being a little harder to do that.
Fortunately, what can be seen from these ROC curve plots is that there is almost no chance that a pure fish of either wild or farmed origin will be misidentified as F1 or F2 in any population. In GRR and LPR is there some appreciable difficulty with incorrectly identifying pure individuals as bx_wilds. And, in LPR, that can’t really be controlled by a suitable cutoff. We saw this in the preliminary runs I did too. Perhaps that population has experienced some previous introgression?
LS0tCnRpdGxlOiAiU3VtbWFyaXppbmcgdGhlIFJ1bnMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgc2V0IHRoZSB3b3JraW5nIGRpcmVjdG9yeSBhbHdheXMgdG8gdGhlIHByb2plY3QgZGlyZWN0b3J5IChvbmUgbGV2ZWwgdXApCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gbm9ybWFsaXplUGF0aChycHJvanJvb3Q6OmZpbmRfcnN0dWRpb19yb290X2ZpbGUoKSkpIApgYGAKCgoKCkxldCdzIGRvIHRoaXMgd2l0aCBOZXdmb3VuZGxhbmQgYXMgdGhhdCBpcyBhbGwgSSBoYXZlIHRoZSBzaW11bGF0aW9ucyBmb3IgeWV0LgoKTGV0J3Mgc2x1cnAgdXAgdGhlIHJlc3VsdHMsIGFuZCBsZXQncyBtYWtlIHRoZSBuYW1lcyBvZiB0aGUgaW5mZXJyZWQgYW5kIAp0cnVlIGh5YiBjYXRzIHRoZSBzYW1lCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh2aXJpZGlzKQoKIyBnZXQgcmVwbGFjZW1lbnRzIGZvciB0aGUgdHJ1ZV9oeWJfY2F0IGNvbHVtbgp0aGMgPC0gYygKICBQdXJlRiA9ICJwdXJlX2Zhcm1lZCIsCiAgUHVyZVcgPSAicHVyZV93aWxkIiwKICBGMSA9ICJGMSIsCiAgRjIgPSAiRjIiLAogIEJYID0gImJ4X3dpbGQiCikKCm5mX3JlcyA8LSByZWFkX3Jkcygib3V0cHV0cy9ORl9sb25nX3J1bnNfdGliYmxlLnJkcyIpICU+JQogIG11dGF0ZShpbmZlcnJlZF9oeWJfY2F0ID0gYXMuY2hhcmFjdGVyKGluZmVycmVkX2h5Yl9jYXQpLAogICAgICAgICB0cnVlX2h5Yl9jYXQgPSB0aGNbdHJ1ZV9oeWJfY2F0XSkKYGBgCgoKCgojIFZlcmlmeWluZyBBY2N1cmFjeSBhbmQgRWZmaWNpZW5jeSBDYWxjdWxhdGlvbnMKCkJyZW5kYW4gdGhyZXcgZG9uZSBzb21lIGdvb2QgcGlwZXMgYW5kIG11dGF0ZXMgYW5kIGdyb3VwX2J5KCkncyB0byBjb21wdXRlIHRoZSBhY2N1cmFjeQphbmQgZWZmaWNpZW5jeSBmb3IgZWFjaCBwb3B1bGF0aW9uIGF0IGRpZmZlcmVudCBudW1iZXJzIG9mIGxvY2kuICBJIGp1c3Qgd2FudCB0byBjb25maXJtIAp0aG9zZSByZXN1bHRzIGFuZCBzZWUgaWYgd2UgY2FuIGRvIHRoZSBzYW1lIHdpdGggYSBsaXR0bGUgYGRwbHlyOjpkbygpYCAoc2luY2UgSSBqdXN0IHJlY2VudGx5CnN0YXJ0ZWQgdXNpbmcgdGhhdC4uLmFuZCBpdCBpcyB3aWNrZWRseSBwb3dlcmZ1bCEpCgpPdXIgc3RhcnRpbmcgcG9pbnQgZm9yIHRoaXMgd2lsbCBiZSB0aGUgYG5mX3Jlc2AgdGliYmxlLCB3aGljaCBhcmUgYWxsIHRoZSByZXN1bHRzCmFmdGVyIEkgc3RhbmRhcmRpemVkIHRoZSBoeWJyaWQgY2F0ZWdvcmllcywgYW5kIG1hZGUgdGhlbSBhbGwgY2hhcmFjdGVycyByYXRoZXIgdGhhbgpmYWN0b3JzIChqdXN0IGxpa2UgQnJlbmRhbiBkaWQgaW4gaGlzIGZpcnN0IHN0ZXAhKS4gIAoKIyMgQWNjdXJhY3kKClRoaXMgaXMgZGVmaW5lZCBpbiB0aGUgcGFwZXIgYXMgInRoZSBwcm9wb3J0aW9uIG9mIGFuIGlkZW50aWZpZWQgZ3JvdXAgCnRoYXQgdHJ1bHkgYmVsb25ncyB0byB0aGF0IGNhdGVnb3J5Ii4gIFdlIHdpbGwgc2F5IHRoYXQgaW5kaXZpZHVhbHMgYXJlIGluZmVycmVkIHRvIGJlCmEgY2VydGFpbiBjYXRlZ29yeSBpZiB0aGVpciBwb3N0ZXJpb3IgZm9yIHRoYXQgY2F0ZWdvcnkgaXMgbWF4aW1hbC4gQW5kIHdoZW4gd2UgdGFsayAKYWJvdXQgYXBwbHlpbmcgYSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgdGhyZXNob2xkLCB0aGVuIHRoZSBvbmx5IHdheSB0aGF0IG1ha2VzIHNlbnNlIGlzIGlmIHlvdSB3aWxsIG5vdApiZSBhc3NpZ25pbmcgaW5kaXZpZHVhbHMgdG8gYSBjYXRlZ29yeSBpZiB0aGVpciBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgaXMgbGVzcyB0aGFuIHRoYXQKdGhyZXNob2xkLiAgCgpXZSBmaWx0ZXIgdGhlIGRhdGEgc2V0IHRvIGp1c3QgdGhvc2UgaW5mZXJyZWQgY2F0ZWdvcmllcyB3aXRoIG1heGltYWwgcG9zdGVyaW9yIHByb2JhYmlsaXR5OgpgYGB7cn0KbWF4X2NhdHMgPC0gbmZfcmVzICU+JQogIGdyb3VwX2J5KHBvcCwgc3BsaXQsIHRydWVfaHliX2NhdCwgbnVtX2xvY2ksIGlkeCkgJT4lCiAgdG9wX24obiA9IDEsIHd0ID0gcG9zdF9wcm9iKSAlPiUKICB1bmdyb3VwKCkKYGBgCgpOb3csIHdlIHdhbnQgdG8gZ2V0IHRoZSBmcmFjdGlvbiBvZiB0aGVzZSBpbmZlcnJlZCBjYXRlZ29yaWVzIHRoYXQgYXJlIGNvcnJlY3QKZm9yIGVhY2ggdmFsdWUgb2YgcG9zdF9wcm9iIGZyb20gMC41IHRvIDEuMCBpbiBzdGVwcyBvZiAwLjAxLiBPZiBjb3Vyc2UsIHdlIHdhbnQKdG8gbWFrZSB0aGF0IGNhbGN1bGF0aW9uIHdpdGhpbiBlYWNoIHBvcCBhbmQgaW5mZXJyZWRfaHliX2NhdCBhbmQgbnVtX2xvY2kuCihOb3RlIHRoYXQgd2UgX2Rvbid0XyBncm91cCBieSB0cnVlX2h5Yl9jYXQsIGJlY2F1c2Ugd2UgYXJlIGludGVyZXN0ZWQgaW4gc2VlaW5nCmhvdyB3ZWxsIHdlIGhhdmUgZG9uZSBhbW9uZ3N0IGFsbCBpbmRpdmlkdWFscyBpbmZlcnJlZCB0byBiZSBvZiBhIHBhcnRpY3VsYXIgaHlicmlkCmNhdGVnb3J5LikKCk9uY2UgYWdhaW4sIHRoaXMgaXMgYSBqb2IgZm9yIGBkcGx5cjo6ZG8oKWAuIEJ1dCB3ZSB3aWxsIHdhbnQgdG8gd3JpdGUgYSBmdW5jdGlvbgp0aGF0IHJldHVybnMgYSB0aWJibGUgZm9yIGl0LgpgYGB7cn0KIycgQHBhcmFtIHBwIGlzIHRoZSBwb3N0ZXJpb3IgcHJvYgojJyBAcGFyYW0gaWhjIGlzIHRoZSBpbmZlcnJlZCBoeWJyaWQgY2F0ZWdvcnkKIycgQHBhcmFtIHRoYyBpcyB0aGUgYWN0aXZlIGluZ3JlZGllbnQgaW4gbWFyaWp1YW5hLCBidXQgaGVyZSBhbHNvIHRoZSAidHJ1ZSBoeWJyaWQgY2F0ZWdvcnkiCmFjY3VfZnVuYyA8LSBmdW5jdGlvbihwcCwgaWhjLCB0aGMpIHsKICBjcml0X3Bvc3RfcHJvYiA8LSBzZXEoMC41LCAwLjk5LCBieSA9IDAuMDEpICAjIGNyaXRpdGNhbCBwb3N0ZXJpb3IgcHJvYnMgdG8gY29tcHV0ZSBmb3IuIERvbid0IGRvIDEuMC4uLgogIGFjY3VyYWN5IDwtIHNhcHBseShjcml0X3Bvc3RfcHJvYiwgZnVuY3Rpb24oeCkgewogICAgbWVhbigodGhjID09IGloYylbcHAgPj0geF0pICAjIGZyYWN0aW9uIG9mIHRoZSBpbmRpdmlkdWFscyB3aXRoIGEgcHAgPiB0aGUgY3JpdGljYWwgdmFsdWUgdGhhdCB3ZXJlIGluZmVycmVkIGNvcnJlY3RseQogIH0pCiAgdGliYmxlKGNyaXRfcG9zdF9wcm9iID0gY3JpdF9wb3N0X3Byb2IsCiAgICAgICAgIGFjY3VyYWN5ID0gYWNjdXJhY3kpCn0KYGBgCgpUaGVuLCBncm91cCBieSBwb3AsIGluZmVycmVkX2h5Yl9jYXQsIGFuZCBudW1fbG9jaSBhbmQgY29tcHV0ZSBpdC4KYGBge3J9CmFjY3VfZGYgPC0gbWF4X2NhdHMgJT4lCiAgZ3JvdXBfYnkocG9wLCBpbmZlcnJlZF9oeWJfY2F0LCBudW1fbG9jaSkgJT4lCiAgZHBseXI6OmRvKGFjY3VfZnVuYyhwcCA9IC4kcG9zdF9wcm9iLCAKICAgICAgICAgICAgICAgICAgICAgIGloYyA9IC4kaW5mZXJyZWRfaHliX2NhdCwKICAgICAgICAgICAgICAgICAgICAgIHRoYyA9IC4kdHJ1ZV9oeWJfY2F0KSkKYGBgCgpBbmQgd2UgY2FuIHBsb3QgdGhvc2UgYWxsIGF0IG9uY2Ugb24gYSBzaW5nbGUgYmlnIHBhZ2UgaW4gYSBmb3JtYXQga2luZCBvZiBsaWtlIHdoYXQgQnJlbmRhbiBkaWQ6CmBgYHtyLCBmaWcud2lkdGg9MTEsIGZpZy5oZWlnaHQ9MTF9CmdncGxvdChhY2N1X2RmLCBhZXMoeCA9IGNyaXRfcG9zdF9wcm9iLCB5ID0gYWNjdXJhY3ksIGNvbG91ciA9IGluZmVycmVkX2h5Yl9jYXQpKSArCiAgZ2VvbV9saW5lKCkgKwogIGZhY2V0X2dyaWQocG9wIH4gbnVtX2xvY2kpCmBgYAoKSSBmaW5kIGl0IGVhc2llciB0byBsb29rIGF0IGFuZCBpbnRlcnBldCBpZiB3ZSBjb2xvciBieSBudW1iZXIgb2YgbG9jaSBhbmQgZmFjZXQgb24gCmluZmVycmVkIGh5YnJpZCBjYXRlZ29yeS4KYGBge3IsIGZpZy53aWR0aD0xMSwgZmlnLmhlaWdodD0xMX0KCmdncGxvdChhY2N1X2RmLCBhZXMoeCA9IGNyaXRfcG9zdF9wcm9iLCB5ID0gYWNjdXJhY3ksIGNvbG91ciA9IGZhY3RvcihudW1fbG9jaSkpKSArCiAgZ2VvbV9saW5lKCkgKwogIGZhY2V0X2dyaWQocG9wIH4gaW5mZXJyZWRfaHliX2NhdCkKYGBgCgojIyBFZmZpY2llbmN5CgpFZmZpY2llbmN5IGlzIGRlZmluZWQgaW4gdGhlIHBhcGVyIGFzICJ0aGUgcHJvcG9ydGlvbiBvZiBpbmRpdmlkdWFscyBpbiBhIGdyb3VwIHRoYXQgd2VyZSBjb3JyZWN0bHkgaWRlbnRpZmllZCIuClNvLCBpdCBzaG91bGQgYmUgdGhlIGNhc2UgdGhhdCB3ZSB3aWxsIGJlIGdyb3VwaW5nIGJ5IHRoZSB0cnVlIGh5YnJpZCBjYXRlZ29yeS4gIEFuZCB3ZSB3aWxsIG5lZWQgYQpmdW5jdGlvbiB0byBjb21wdXRlIHRoZSBlZmZpY2llbmN5LiAgQnV0IHdoYXQgaXMgaW50ZXJlc3RpbmcgaGVyZSwgaXMgdGhhdCB0aGUgZnJhY3Rpb24gc2hvdWxkCmRyb3Agb2ZmIGFzIHRoZSBwb3N0ZXJpb3IgcHJvYiB0aHJlc2hvbGQgZ2V0cyBoaWdoZXIgKGJlY2F1c2UgbW9yZSBpbmRpdmlkdWFscyBhcmUgYmVpbmcgbGVmdCAKdW5hc3NpZ25lZCkuIApgYGB7cn0KIycgQHBhcmFtIHBwIGlzIHRoZSBwb3N0ZXJpb3IgcHJvYgojJyBAcGFyYW0gaWhjIGlzIHRoZSBpbmZlcnJlZCBoeWJyaWQgY2F0ZWdvcnkKIycgQHBhcmFtIHRoYyBpcyB0aGUgYWN0aXZlIGluZ3JlZGllbnQgaW4gbWFyaWp1YW5hLCBidXQgaGVyZSBhbHNvIHRoZSAidHJ1ZSBoeWJyaWQgY2F0ZWdvcnkiCmVmZmlfZnVuYyA8LSBmdW5jdGlvbihwcCwgaWhjLCB0aGMpIHsKICBjcml0X3Bvc3RfcHJvYiA8LSBzZXEoMC41LCAwLjk5LCBieSA9IDAuMDEpICAjIGNyaXRpY2FsIHBvc3RlcmlvciBwcm9icyB0byBjb21wdXRlIGZvci4gRG9uJ3QgZG8gMS4wLi4uCiAgZWZmaWNpZW5jeSA8LSBzYXBwbHkoY3JpdF9wb3N0X3Byb2IsIGZ1bmN0aW9uKHgpIHsKICAgIG1lYW4odGhjID09IGloYyAmIHBwID49IHgpICAjIGZyYWN0aW9uIG9mIF9hbGxfIGluZGl2aWR1YWxzIHRoYXQgaGFkIHBwID4gY3JpdCBhbmQgdGhjID09IGloYyAKICB9KQogIHRpYmJsZShjcml0X3Bvc3RfcHJvYiA9IGNyaXRfcG9zdF9wcm9iLAogICAgICAgICBlZmZpY2llbmN5ID0gZWZmaWNpZW5jeSkKfQpgYGAKCk5vdywgZ28gZm9yIGl0OgpgYGB7cn0KZWZmaV9kZiA8LSBtYXhfY2F0cyAlPiUKICBncm91cF9ieShwb3AsIHRydWVfaHliX2NhdCwgbnVtX2xvY2kpICU+JQogIGRwbHlyOjpkbyhlZmZpX2Z1bmMocHAgPSAuJHBvc3RfcHJvYiwgCiAgICAgICAgICAgICAgICAgICAgICBpaGMgPSAuJGluZmVycmVkX2h5Yl9jYXQsCiAgICAgICAgICAgICAgICAgICAgICB0aGMgPSAuJHRydWVfaHliX2NhdCkpCmBgYAoKQW5kIHBsb3QgdGhhdCBkdWRlOgpgYGB7ciwgZmlnLndpZHRoPTExLCBmaWcuaGVpZ2h0PTExfQpnZ3Bsb3QoZWZmaV9kZiwgYWVzKHggPSBjcml0X3Bvc3RfcHJvYiwgeSA9IGVmZmljaWVuY3ksIGNvbG91ciA9IGZhY3RvcihudW1fbG9jaSkpKSArCiAgZ2VvbV9saW5lKCkgKwogIGZhY2V0X2dyaWQocG9wIH4gdHJ1ZV9oeWJfY2F0KQpgYGAKCgoKCiMjIEp1c3QgaW5mZXJyaW5nIGh5YnJpZCBvciBub24taHlicmlkLi4uCgpUaGlzIGlzIHdvcnRoIGxvb2tpbmcgYXQuICBXZSB3aWxsIGluZmVyIGVhY2ggaW5kaXZpZHVhbCB0byBqdXN0IGJlIGh5YnJpZCBvciBub24taHlicmlkIGFuZCB0aGVuIApsb29rIGF0IHRoZSBhY2N1cmFjeSBvZiB0aG9zZS4gIFdlIGRvIHRoYXQgd2l0aCBhIHN1bW1hcmlzZSwgdGhlbiB0YWtlIHRoZQptYXggcG9zdGVyaW9yIG9mIGl0LgpgYGB7cn0KCm5mX3NxdWFzaGVkX2NhdHMgPC0gbmZfcmVzICU+JQogIG11dGF0ZShpbmZlcnJlZF9oeWJfdl9ub25faHliID0gaWZlbHNlKHN0cmluZ3I6OnN0cl9kZXRlY3QoaW5mZXJyZWRfaHliX2NhdCwgInB1cmUiKSwgInB1cmUiLCAiaHlicmlkIiksCiAgICAgICAgIGFjdHVhbF9oeWJfdl9ub25faHliID0gaWZlbHNlKHN0cmluZ3I6OnN0cl9kZXRlY3QodHJ1ZV9oeWJfY2F0LCAicHVyZSIpLCAicHVyZSIsICJoeWJyaWQiKSkgJT4lCiAgZ3JvdXBfYnkocG9wLCBzcGxpdCwgbnVtX2xvY2ksIHRydWVfaHliX2NhdCwgaWR4LCBhY3R1YWxfaHliX3Zfbm9uX2h5YiwgaW5mZXJyZWRfaHliX3Zfbm9uX2h5YikgJT4lCiAgc3VtbWFyaXNlKHBvc3RfcHJvYiA9IHN1bShwb3N0X3Byb2IpKSAlPiUKICB0b3BfbihuID0gMSwgcG9zdF9wcm9iKSAlPiUgIyAiYXNzaWduIiB0aGVtIHRvIGh5YiBvciBub24taHliIGNhdGVnb3J5CiAgdW5ncm91cCgpCgpgYGAKCkFuZCBub3cgd2UgY2FuIGNvbXB1dGUgYWNjdXJhY3kgYW5kIGVmZmljaWVuY3kuCgojIyMgQWNjdXJhY3kKYGBge3J9CnNxdWFzaGVkX2FjY3VfZGYgPC0gbmZfc3F1YXNoZWRfY2F0cyAlPiUKICBncm91cF9ieShwb3AsIGluZmVycmVkX2h5Yl92X25vbl9oeWIsIG51bV9sb2NpKSAlPiUKICBkcGx5cjo6ZG8oYWNjdV9mdW5jKHBwID0gLiRwb3N0X3Byb2IsIAogICAgICAgICAgICAgICAgICAgICAgaWhjID0gLiRpbmZlcnJlZF9oeWJfdl9ub25faHliLAogICAgICAgICAgICAgICAgICAgICAgdGhjID0gLiRhY3R1YWxfaHliX3Zfbm9uX2h5YikpCmBgYApQbG90IGl0OgpgYGB7ciwgZmlnLndpZHRoPTExLCBmaWcuaGVpZ2h0PTExfQoKZ2dwbG90KHNxdWFzaGVkX2FjY3VfZGYsIGFlcyh4ID0gY3JpdF9wb3N0X3Byb2IsIHkgPSBhY2N1cmFjeSwgY29sb3VyID0gZmFjdG9yKG51bV9sb2NpKSkpICsKICBnZW9tX2xpbmUoKSArCiAgZmFjZXRfZ3JpZChwb3AgfiBpbmZlcnJlZF9oeWJfdl9ub25faHliKQpgYGAKCiMjIyBFZmZpY2llbmN5CgpgYGB7cn0Kc3F1YXNoZWRfZWZmaV9kZiA8LSBuZl9zcXVhc2hlZF9jYXRzICU+JQogIGdyb3VwX2J5KHBvcCwgdHJ1ZV9oeWJfY2F0LCBudW1fbG9jaSkgJT4lCiAgZHBseXI6OmRvKGVmZmlfZnVuYyhwcCA9IC4kcG9zdF9wcm9iLCAKICAgICAgICAgICAgICAgICAgICAgIGloYyA9IC4kaW5mZXJyZWRfaHliX3Zfbm9uX2h5YiwKICAgICAgICAgICAgICAgICAgICAgIHRoYyA9IC4kYWN0dWFsX2h5Yl92X25vbl9oeWIpKQpgYGAKUGxvdCBpdDoKYGBge3IsIGZpZy53aWR0aD0xMSwgZmlnLmhlaWdodD0xMX0KCmdncGxvdChzcXVhc2hlZF9lZmZpX2RmLCBhZXMoeCA9IGNyaXRfcG9zdF9wcm9iLCB5ID0gZWZmaWNpZW5jeSwgY29sb3VyID0gZmFjdG9yKG51bV9sb2NpKSkpICsKICBnZW9tX2xpbmUoKSArCiAgZmFjZXRfZ3JpZChwb3AgfiB0cnVlX2h5Yl9jYXQpCmBgYAoKIyBST0MgQ3VydmVzCgpJIGhhdmUgYSBoYXJkZXIgdGltZSBpbnRlcnByZXRpbmcgdGhlICJwb3dlciA9IGFjY3VyYWN5ICogIGVmZmljaWVuY3kiIHN1bW1hcnkgdGhhbgpJIGRvIGxvb2tpbmcgYXQgdGhlIHJlY2VpdmVyIG9wZXJhdGluZyBjaGFyYWN0ZXJpc3RpYyAoUk9DKSBjdXJ2ZS4KVGhlIFJPQyBjdXJ2ZSBpcyBhbm90aGVyIHN1bW1hcnkKdGhhdCBpbmNsdWRlcyBib3RoIHRoZSB0eXBlIDEgYW5kIHR5cGUgMiBlcnJvcnMgYW5kIHdoaWNoIEkgZmluZCBlYXNpZXIgdG8gZ2V0IG15IGhlYWQKYXJvdW5kLCBzbyBJIHdpbGwgc2VlIHdoYXQgdGhvc2UgbG9vayBsaWtlIGluIHRoaXMgY2FzZS4KCk9uIGFub3RoZXIgbm90ZSwgdGhlICJhY2N1cmFjeSIgc3RhdGlzdGljIGlzIGhhcmQgdG8gaW50ZXJwcmV0IGJlY2F1c2UgaXQgcmVhbGx5CmRlcGVuZHMgb24gd2hhdCBvdGhlciB0eXBlcyBvZiBoeWJyaWQgY2F0ZWdvcmllcyBhcmUgImluIHRoZSBtaXguIiAgRm9yIGV4YW1wbGUsIGlmIHlvdQphcmUgbG9va2luZyBhdCB0aGUgImFjY3VyYWN5IiBvZiBpbmZlcnJlZCBGMiBpbmRpdmlkdWFscywgdGhhdCBxdWFudGl0eSBkZXBlbmRzIG9uIHRoZQpyZWxhdGl2ZSBhbW91bnRzIG9mIEYyLCBGMSwgYW5kIGJhY2tjcm9zcyBpbmRpdmlkdWFscy4gIEZvciBleGFtcGxlLCBpZiB5b3VyIHNpbXVsYXRlZCBkYXRhCnNldCBpbmNsdWRlcyAxMCBGMnMsIDEwMDAwIEYxcywgYW5kIDEwMDAwIGJ4LXMsIHRoZW4gdGhlIGFjY3VyYWN5IGZvciBpbmZlcnJlZCBGMidzIG1pZ2h0Cmxvb2sgcmVhbGx5IGJhZCBpZiBpdCBpcyBoYXJkIHRvIGRpc3Rpbmd1aXNoIEYxcyBmcm9tIEYycywgZXRjLiAgT24gdGhlIG90aGVyIGhhbmQsIAppZiB5b3VyIHNpbXVsYXRlZCBkYXRhIGluY2x1ZGVzIDEwIEYycyBhbmQgMTAgUHVyZVdpbGRzLCB0aGVuIHlvdSB3aWxsIGNvbWUgb3V0IHdpdGggCmEgaGlnaGVyIHZhbHVlIGZvciBhY2N1cmFjeS4gIE9uZSBvZiB0aGUgdGhpbmdzIEkgbGlrZSBhYm91dCB0aGUgUk9DIGN1cnZlcyBpcyB0aGF0IHRoZXkKc2hvdyB5b3UgYSB0eXBlIDEgYW5kIHR5cGUgMiBlcnJvciByYXRlIHN1bW1hcnkgYmV0d2VlbiBlYWNoIGNhdGVnb3J5LiAKCgpJIHRoaW5rIHRoZSBST0MgY3VydmUgc2hvdWxkIGJlIHByZXR0eSBlYXN5IHRvIGNvbXB1dGUsIHVzaW5nIGEgc2VxdWVuY2Ugb2Ygc2NhbGVkIApsaWtlbGlob29kcyBmcm9tIDAgdG8gMSBpbiBzdGVwcyBvZiAwLjAwNSwgc2F5LgpPbmUgaW1wb3J0YW50IHBvaW50IHRvIGtlZXAgaW4gbWluZCBpcyB0aGF0IHRoZSBmYWxzZSBwb3NpdGl2ZSByYXRlCm11c3QgYmUgYSBmdW5jdGlvbiBvZiB0aGUgdHJ1ZSBoeWJyaWQgY2F0ZWdvcnkuICBXZSB3aWxsIGhvbm9yIHRoYXQgYnkKbWFraW5nIGEgbXVsdGlwYW5lbCBwbG90LCBjcmVhdGluZyBST0MgY3VydmVzIGZvciBhbGwgdGhlIGRpZmZlcmVudCBraW5kcyBvZgpzaXR1YXRpb25zIChpLmUuIGluZmVycmluZyBGMidzIHdoZW4gdGhlIHRydXRoIGlzIEJYLCBpbmZlcnJpbmcgRjIncyB3aGVuIHRoZSAKdHJ1dGggaXMgUHVyZSBXaWxkLCBldGMpLgoKV2UgY2FuIGNvbXB1dGUgdGhlc2UgUk9DIGN1cnZlcyBpbiB0aGUgdGlkeXZlcnNlIGJ5IHRocm93aW5nIGRvd24gc29tZSBgZHBseXI6OmRvKClgCmFsb25nIHdpdGggdGhlIGBlY2RmKClgIGZ1bmN0aW9uLiAgCgpgYGB7cn0Kb3B0aW9ucyhkcGx5ci5zaG93X3Byb2dyZXNzID0gRkFMU0UpCnBvc19yYXRlcyA8LSBuZl9yZXMgJT4lCiAgZ3JvdXBfYnkocG9wLCB0cnVlX2h5Yl9jYXQsIGluZmVycmVkX2h5Yl9jYXQsIG51bV9sb2NpKSAlPiUgCiAgZHBseXI6OmRvKAogICAgdGliYmxlKAogICAgICBwdGhyZXNoID0gc2VxKDAsIDEsIGJ5ID0gMC4wMDUpLCAKICAgICAgcGdyZWF0ZXIgPSAxIC0gZWNkZiguJHBvc3RfcHJvYikoc2VxKDAsIDEsIGJ5ID0gMC4wMDUpKQogICAgICApCiAgICApICU+JQogIHVuZ3JvdXAoKQpgYGAKCk5vdywgYWxsIHdlIG5lZWQgdG8gZG8gaXMgYSBxdWljayBsZWZ0IGpvaW4gdG8gZ2V0IHJlYWR5IHRvIHBsb3Qgc29tZSBvZiB0aGVzZToKYGBge3J9CnJvY2RmIDwtIHBvc19yYXRlcyAlPiUKICBmaWx0ZXIodHJ1ZV9oeWJfY2F0ID09IGluZmVycmVkX2h5Yl9jYXQpICU+JQogIHJlbmFtZSh0cnVlX3Bvc19yYXRlID0gcGdyZWF0ZXIsCiAgICAgICAgIGZvY2FsX2h5Yl9jYXQgPSB0cnVlX2h5Yl9jYXQpICU+JQogIHNlbGVjdCgtaW5mZXJyZWRfaHliX2NhdCkgJT4lCiAgbGVmdF9qb2luKC4sIHBvc19yYXRlcykgJT4lCiAgZmlsdGVyKGZvY2FsX2h5Yl9jYXQgIT0gdHJ1ZV9oeWJfY2F0KSAlPiUgICMgdG8gZ2l2ZSB1cyBhbiBlbXB0eSBwbG90IHJhdGhlciB0aGFuIGEgeSA9IHggbGluZSBvbiB0aGUgZGlhZ29uYWwKICBmaWx0ZXIoZm9jYWxfaHliX2NhdCA9PSBpbmZlcnJlZF9oeWJfY2F0KSAlPiUgIyB0aGVzZSBhcmUgdGhlIHZhbHVlcyB3ZSBjYXJlIGFib3V0CiAgcmVuYW1lKGZhbHNlX3Bvc19yYXRlID0gcGdyZWF0ZXIpICU+JQogIG11dGF0ZShudW1fbG9jaSA9IGZhY3RvcihudW1fbG9jaSkpICU+JQogIG11dGF0ZSh0cnVlX2h5Yl9jYXQgPSBwYXN0ZSgidHJ1ZSIsIHRydWVfaHliX2NhdCksCiAgICAgICAgIGZvY2FsX2h5Yl9jYXQgPSBwYXN0ZSgiaW5mZCIsIGZvY2FsX2h5Yl9jYXQpKQogIApgYGAKCkxldCdzIHNlZSBob3cgdGhlc2UgdGhpbmdzIHBsb3Qgb3V0OgpgYGB7cn0KZHVtcCA8LSBsYXBwbHkodW5pcXVlKHJvY2RmJHBvcCksIGZ1bmN0aW9uKHgpIHsKICBnIDwtIGdncGxvdChyb2NkZiAlPiUgZmlsdGVyKHBvcCA9PSB4KSwgYWVzKHggPSBmYWxzZV9wb3NfcmF0ZSwgeSA9IHRydWVfcG9zX3JhdGUsIGNvbG91ciA9IG51bV9sb2NpKSkgKwogICAgZ2VvbV9saW5lKCkgKwogICAgZmFjZXRfZ3JpZCh0cnVlX2h5Yl9jYXQgfiBmb2NhbF9oeWJfY2F0KSArIAogICAgZ2d0aXRsZSh4KSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpCiAgCiAgZ2dzYXZlKGcsIGZpbGVuYW1lID0gcGFzdGUwKCJvdXRwdXRzL3JvY19ORl8iLCB4LCAiLnBkZiIpKQp9KQpgYGAKCkkgbWFkZSBQREZzIG9mIGFsbCBvZiB0aG9zZSwgYnV0IGhlcmUgaXMgaG93IGl0IGxvb2tzIGZvciBCRE46CmBgYHtyIGJkbl9wbmcxLCBmaWcud2lkdGg9MTEsIGZpZy5oZWlnaHQ9MTF9CmdncGxvdChyb2NkZiAlPiUgZmlsdGVyKHBvcCA9PSAiQkROIiksIGFlcyh4ID0gZmFsc2VfcG9zX3JhdGUsIHkgPSB0cnVlX3Bvc19yYXRlLCBjb2xvdXIgPSBudW1fbG9jaSkpICsKICAgIGdlb21fbGluZSgpICsKICAgIGZhY2V0X2dyaWQodHJ1ZV9oeWJfY2F0IH4gZm9jYWxfaHliX2NhdCkgKyAKICAgIGdndGl0bGUoIkJETiIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41KSkKYGBgCkkgc2F2ZWQgdGhlc2UgaW4gdGhlIGZpbGVzIGByb2NfTkZfWFhYLnBkZmAuICBOb3RlIHRoYXQgImluZmQiIG1lYW5zICJpbmZlcnJlZCIgaW4gdGhlCmxlZ2VuZCwgYW5kICJ0cnVlIiBpcyByZWZlcnJpbmcgdG8gdGhlIHRydWUgaHlicmlkIGNhdGVnb3JpZXMuCgoKVGhlIHdheSB0byBpbnRlcnByZXQgdGhlc2UgaXMgdGhpczogd2hlbiB5b3UgYXJlIGxvb2tpbmcgYXQgdGhlIGNvbHVtbnMKeW91IGFyZSBhc3Nlc3Npbmcgd2hhdCB0aGUgdHJ1ZSBwb3NpdGl2ZSByYXRlIGlzIHdoZW4gaW5mZXJyaW5nIHRoZSBoeWJyaWQgY2F0ZWdvcnkgdGhhdCBpcwpsaXN0ZWQgYXQgdGhlIHRvcCBvZiB0aGUgY29sdW1uIF9hcyBhIGZ1bmN0aW9uIG9mXyB0aGUgZmFsc2UgcG9zaXRpdmUgcmF0ZSBmb3IgdGhlIApoeWJyaWQgY2F0ZWdvcnkgdGhhdCBpcyBsaXN0ZWQgdG8gdGhlIHJpZ2h0LiAgU28sIGZvciBleGFtcGxlLCBpZiB5b3UgYXJlIHRyeWluZyAKdG8gaWRlbnRpZnkgRjJzIGluIHRoZSBCRE4gcG9wdWxhdGlvbiwgYW5kIHlvdSBoYXZlIDQ4IGxvY2ksIHRoZW4gd2hlbiB5b3Ugc2V0IHlvdXIKRjIgcG9zdGVyaW9yIHByb2JhYmlsaXR5IHRvICRUJCwgc3VjaCB0aGF0IDc1JSBvZiB0aGUgRjJzIHlvdSBlbmNvdW50ZXIgYXJlIGV4cGVjdGVkIHRvIGhhdmUKYSBwb3N0ZXJpb3IgJFxnZXEgVCQsIHlvdSBleHBlY3QgdGhhdCAyNSUgb2YgYnhfd2lsZCBmaXNoIHdpbGwgYWxzbyBoYXZlIGFuCkYyIHBvc3RlcmlvciAkXGdlcSBUJC4gIFRoaXMgaW5mb3JtYXRpb24gY2FuIGJlIGdsZWFuZWQgZnJvbSB0aGUgZ3JhcGggaW4gdGhlIApmaXJzdCByb3cgKCJ0cnVlIGJ4X3dpbGQiKSBhbmQgdGhlIHRoaXJkIGNvbHVtbiAoImluZmQgRjIiKS4gCgpPciwgaWYgd2Ugd2VyZSB0byBsb29rIGF0IHRoZSByZXN1bHRzIGZvciBMUFIgd2Ugd291bGQgZmluZCB0aGF0IHdoZW4gdGhpbmdzIGFyZQp0cnVseSBwdXJlX3dpbGQgKCJ0cnVlIHB1cmVfd2lsZCIpLCBhbmQgeW91IGFyZSBpbmZlcnJpbmcgYnhfd2lsZHMgKCJpbmZkIGJ4X3dpbGQiKSwKdGhpbmdzIGFyZSBub3QgbG9va2luZyBzbyBnb29kLiAgVGhlcmUgaXMgbm90IGN1dG9mZiAkVCQgdGhhdCBtYWtlcyB1cyB1bmxpa2VseSB0bwppbmNvcnJlY3RseSBpbmZlciB0aGF0IHB1cmUgd2lsZCBmaXNoIGFyZSBieF93aWxkIGZpc2guICBXZSBzYXcgdGhhdCBhbHJlYWR5IGluIHRoZSAKcHJlbGltaW5hcnkgcmVzdWx0cyBJIHNlbnQgYSB3aGlsZSBhZ28uCgpUaGF0IGlzIHByZXR0eSBjb29sLCBidXQgaWYgd2Ugd2FudGVkIHRvIGFsc28gdHJ5IHRvIHNob3cgd2hhdCB0aGUgc2NhbGVkIGxpa2VsaWhvb2QKY3V0b2ZmIGxldmVscyBhcmUsIHdlIGNhbiBkbyB0aGF0IHRvby4KYGBge3J9CmR1bXAgPC0gbGFwcGx5KHVuaXF1ZShyb2NkZiRwb3ApLCBmdW5jdGlvbih4KSB7CiAgZyA8LSBnZ3Bsb3Qocm9jZGYgJT4lIGZpbHRlcihwb3AgPT0geCksIGFlcyh4ID0gZmFsc2VfcG9zX3JhdGUsIHkgPSB0cnVlX3Bvc19yYXRlLCBjb2xvdXIgPSBwdGhyZXNoLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBudW1fbG9jaSkpICsKICAgIGdlb21fbGluZSgpICsKICAgIGZhY2V0X2dyaWQodHJ1ZV9oeWJfY2F0IH4gZm9jYWxfaHliX2NhdCkgKyAKICAgIGdndGl0bGUoeCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUpKSArCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpcygpCiAgCiAgZ2dzYXZlKGcsIGZpbGVuYW1lID0gcGFzdGUwKCJvdXRwdXRzL3JvY19ORl9wY29sb3IiLCB4LCAiLnBkZiIpKQp9KQpgYGAKClRoZXNlIGFyZSBzYXZlZCBpbiB0aGUgZmlsZXMsICJyb2NfTkZfcGNvbG9yWFhYLnBkZiIuIEhlcmUgaXMgd2hhdCBpdCBsb29rcyBsaWtlCmZvciBCRE46CmBgYHtyIGJkbl9wbmcyLCBmaWcud2lkdGg9MTEsIGZpZy5oZWlnaHQ9MTF9CiBnZ3Bsb3Qocm9jZGYgJT4lIGZpbHRlcihwb3AgPT0gIkJETiIpLCBhZXMoeCA9IGZhbHNlX3Bvc19yYXRlLCB5ID0gdHJ1ZV9wb3NfcmF0ZSwgY29sb3VyID0gcHRocmVzaCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gbnVtX2xvY2kpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBmYWNldF9ncmlkKHRydWVfaHliX2NhdCB+IGZvY2FsX2h5Yl9jYXQpICsgCiAgICBnZ3RpdGxlKCJCRE4iKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpICsKICAgIHNjYWxlX2NvbG91cl92aXJpZGlzKCkKYGBgCgpXaGF0IHdlIHNlZSB0aGVyZSBpcyB0aGF0IHdpdGggNDggbG9jaSwgaWYgeW91IHdhbnQgYSB0cnVlIHBvc2l0aXZlIHJhdGUgb2YKNzUlIGZvciBGMnMsIHlvdSBhcmUgZ29pbmcgdG8gaGF2ZSB0byB1c2UgYSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgY3V0b2ZmIHRoYXQgbG9va3MKdG8gYmUgYXJvdW5kIDMwJSwgd2hpY2ggbWFrZXMgaXQgY2xlYXIgd2h5IHlvdSdkIHN1ZmZlciBwcmV0dHkgaGlnaCBmYWxzZSBwb3NpdGl2ZQpyYXRlcyBmcm9tIHRoZSBieF93aWxkIGNhdGVnb3J5LiAgCgojIyBIeWJyaWQgdnMgbm9uLWh5YnJpZAoKSW4gYWRkaXRpb24gdG8gdGhlIGFib3ZlLCBpdCB3b3VsZCBiZSBuaWNlIHRvIHNlZSBmb3IgZWFjaCB0cnVlIGh5YnJpZCBjYXRlZ29yeSwgaG93IHdlbGwgb25lCmNhbiBkbyB3aXRoIGp1c3QgZGVjbGFyaW5nIGEgZmlzaCBhIGh5YnJpZCAob2Ygc29tZSBzb3J0KSBvciBub3QuICBPbmUgd291bGQgdGhpbmsgdGhpcyBzaG91bGRuJ3QgYmUgdG9vIGhhcmQgdG8gCmdldCBhdC4gIFdlIGp1c3Qgd2FudCB0byBjb2xsYXBzZSB0aGUgaW5mZXJyZWQgaHlicmlkIGNhdGVnb3J5IGRvd24gdG8gaHlicmlkIG9yIG5vbi1oeWJyaWQuICBCdXQgdGhlbgp5b3UgZG9uJ3Qgd2FudCB0byBzcXVhc2ggdGhlIGNvcnJlY3QgY2F0ZWdvcmllcyBkb3duIHRvIHRoYXQuICBJdCBlbmRzIHVwIGJlaW5nIGEgbGl0dGxlIGhhcmRlciB0byBkbwp0aGF0LgoKRm9ydHVuYXRlbHksIHdoYXQgY2FuIGJlIHNlZW4gZnJvbSB0aGVzZSBST0MgY3VydmUgcGxvdHMgaXMgdGhhdCB0aGVyZSBpcyBhbG1vc3Qgbm8gY2hhbmNlIHRoYXQgYSAKcHVyZSBmaXNoIG9mIGVpdGhlciB3aWxkIG9yIGZhcm1lZCBvcmlnaW4gd2lsbCBiZSBtaXNpZGVudGlmaWVkIGFzIEYxIG9yIEYyIGluIGFueSBwb3B1bGF0aW9uLiAgSW4gR1JSIGFuZCBMUFIKaXMgdGhlcmUgc29tZSBhcHByZWNpYWJsZSBkaWZmaWN1bHR5IHdpdGggaW5jb3JyZWN0bHkgaWRlbnRpZnlpbmcgcHVyZSBpbmRpdmlkdWFscyBhcyBieF93aWxkcy4gQW5kLCBpbiBMUFIsCnRoYXQgY2FuJ3QgcmVhbGx5IGJlIGNvbnRyb2xsZWQgYnkgYSBzdWl0YWJsZSBjdXRvZmYuICBXZSBzYXcgdGhpcyBpbiB0aGUgcHJlbGltaW5hcnkgcnVucyBJIGRpZCB0b28uClBlcmhhcHMgdGhhdCBwb3B1bGF0aW9uIGhhcyBleHBlcmllbmNlZCBzb21lIHByZXZpb3VzIGludHJvZ3Jlc3Npb24/CgoK