This is an R-code companion to Session 2, around pages 9 to 11. We are going to make a simple function to simulate the Wright-Fisher population, and then show how this can be used to approximate \(t\)-step transition probabilities (i.e., the probability that the population goes from \(X_0\) copies of the \(A\) allele to \(X_t\) copies of the \(A\) allele in \(t\) generations.)

Let’s load Hadley’s tidyverse and other packages we need before we get going. The following will also install them if you don’t have them.

packs <- c("tidyverse")
# install any that are not here
install_em <- setdiff(packs, rownames(installed.packages()))
if (length(install_em) > 0) install.packages(pkgs = install_em)
# load em up
dump <- lapply(packs, function(x) library(x, character.only = TRUE))
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Need help? Try the ggplot2 mailing list: http://groups.google.com/group/ggplot2.
Conflicts with tidy packages --------------------------------------------------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats

A simple W-F function

This will take a population size (as \(2N\)) and initial number of \(A\) alleles. Each generation, gene copies are drawn with replacement from the previous generation, which corresponds to binomial sampling with a success probability given be the fraction of \(A\) genes in the previous generation.

#' simple function
#' 
#' @param TwoN the population size as the number of gene copies (assume a diploid pop)
#' @param X0 the starting number of copies of the A allele
#' @param gens the number of generations to simulate
wf_sim_single <- function(TwoN, X0, gens) {
  # make sure input is acceptable
  stopifnot(X0 >= 0 && X0 <= TwoN)
  stopifnot(TwoN >= 2)
  
  # initialize a vector to return the values. We will include 
  # X0 in it as the first element
  ret <- rep(NA_integer_, gens + 1)
  ret[1] <- X0
  for (i in 2:(gens + 1)) {
    ret[i] <- rbinom(n = 1, size = TwoN, prob = ret[i - 1] / TwoN)
  }
  
  names(ret) <- c(0,1:gens)
  ret
  
}

That function is useful to get one instance of a Wright-Fisher population at a time. For example, if \(N=50\) and \(X_0 = 40\), we can do twenty generations of W-F reproduction like this:

set.seed(5)
result <- wf_sim_single(TwoN = 100, X0 = 40, gens = 20)
result
 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 
40 39 51 55 54 53 50 50 41 43 42 37 31 36 33 26 30 29 29 36 31 

A function to do multiple replicates

That is all well and good, but, when we are doing Monte Carlo we are going to want to simulate things over and over again. So, we will make another function that returns a matrix of allele frequency trajectories. Each row is a generation and each colums will be a different simulation. We just have to twiddle the original function a little.

#' simple function
#' 
#' @param TwoN the population size as the number of gene copies (assume a diploid pop)
#' @param X0 the starting number of copies of the A allele
#' @param gens the number of generations to simulate
#' @param reps the number of times to simulate the population
wf_sim_multi <- function(TwoN, X0, gens, reps) {
  # make sure input is acceptable
  stopifnot(X0 >= 0 && X0 <= TwoN)
  stopifnot(TwoN >= 2)
  
  # initialize a matrix to return the values. We will include 
  # X0 in it as the first row
  ret <- matrix(NA_integer_, nrow = gens + 1, ncol = reps)
  ret[1, ] <- X0
  
  for (i in 2:(gens + 1)) {
    ret[i, ] <- rbinom(n = reps, size = TwoN, prob = ret[i - 1, ] / TwoN)
  }
  
  dimnames(ret) <- list(generation = c(0,1:gens), replicate = 1:reps)
  ret
  
}

So, now we can quickly look at 10 replicates of a simulation of 2,000 generations with \(2N = 1000\) and \(X_0 = 400\), like so:

res2 <- wf_sim_multi(TwoN = 1000, X0 = 400, gens = 1000, reps = 10)
head(res2)
          replicate
generation   1   2   3   4   5   6   7   8   9  10
         0 400 400 400 400 400 400 400 400 400 400
         1 417 397 432 409 406 387 381 432 378 376
         2 423 424 403 394 409 377 354 424 384 385
         3 431 438 395 404 418 386 354 423 387 372
         4 435 471 409 384 405 390 361 431 384 380
         5 449 469 418 357 407 376 367 445 398 365

So, if we wanted to plot these 10 different population trajectories, using ggplot we could do this:

# make a tibble of the results
rtib2 <- tibble(X = as.vector(res2),
                generation = rep(rownames(res2), ncol(res2)),
                replicate = rep(colnames(res2), each = nrow(res2))) %>%
  mutate(generation = as.integer(generation),
         replicate = as.integer(replicate)) %>%
  select(replicate, generation, X)
# looks like this:
rtib2
# then plot it:
ggplot(rtib2, aes(x = generation, y = X)) +
  geom_line() +
  facet_wrap(~ replicate)

The 14-generation example from the lecture

Now, let’s replicate the example from the lecture: \(2N = 200\), \(X_0 = 60\) for 14 generations of drift, with \(n = 50,000\) replicates.

lect_examp <- wf_sim_multi(TwoN = 200, X0 = 60, gens = 14, reps = 50000)
# and to get the state at t = 14 we need to grab that row.  Note that 
# we use a character "14" because it starts at 0
t14 <- lect_examp["14", ]

We can plot a histogram of that quickly:

hist(t14)

Now, in order to see that the histogram is a series of little indicator functions we can construct what was done in the notes using the cut function.

As it says in the lecture notes: the distribution of \(X_{14}\) can be approximated by a histogram. Each histogram column is an approximation of an expectation: \[ \begin{aligned} P(a\leq X<a+2) & = \mathbb{E}[I_{\{x:a\leq x<a+2\}}(X)] \\ &\approx \displaystyle \frac{1}{n} \sum_{i=1}^n I_{\{x:a\leq x<a+2\}}(x^{(i)}) \end{aligned} \] for \(a=0,2,\ldots,200\), where each \(x^{(i)}\) is an independent realization of the number of \(A\) alleles at \(t=14\) in the Wright-Fisher model.

To do that in R code, we will cut the t14 variable into groups of the form [a, a+2):

a <- seq(0, 202, by = 2)
# this puts each observations into an interval
cnts <- cut(x = t14, breaks = a, right = FALSE) %>%
  tibble(interval = .) %>%  # this makes a tibble of it
  count(interval)  # this counts how many are in each interval
cnts

Now, let’s plot that dude:

cnts %>%
  separate(interval, into = c("left", "right"), sep = ",", remove = FALSE) %>%
  mutate(left = parse_number(left),
         right = parse_number(right)) %>%
  mutate(bin_midpoint = (left + right) / 2) %>%
  ggplot(., aes(x = bin_midpoint, y = n)) + 
  geom_col(fill = NA, colour = "black", size = 0.2) +
  theme_bw()

Session Information

sessionInfo()
R version 3.3.3 (2017-03-06)
Platform: x86_64-apple-darwin13.4.0 (64-bit)
Running under: OS X El Capitan 10.11.6

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] dplyr_0.5.0        purrr_0.2.2        readr_1.1.0        tidyr_0.6.1        tibble_1.3.3      
[6] ggplot2_2.2.1.9000 tidyverse_1.0.0   

loaded via a namespace (and not attached):
 [1] Rcpp_0.12.11     assertthat_0.1   grid_3.3.3       plyr_1.8.4       R6_2.2.0         DBI_0.6-1       
 [7] gtable_0.2.0     magrittr_1.5     scales_0.4.1     stringi_1.1.3    rlang_0.1.1      lazyeval_0.2.0  
[13] labeling_0.3     tools_3.3.3      munsell_0.4.3    hms_0.3          colorspace_1.2-6 knitr_1.16.5    
LS0tCnRpdGxlOiAiRXN0aW1hdGluZyBwcm9iYWJpbGl0aWVzIGluIHRoZSBXcmlnaHQtRmlzaGVyIG1vZGVsIgpkZXNjcmlwdGlvbjogV2UgbWFrZSBhIHNpbXBsZSBSIGZ1bmN0aW9uIHRvIHNpbXVsYXRlIHRoZSBXcmlnaHQtRmlzaGVyIG1vZGVsIGF0IGEgc2luZ2xlIGJpYWxsZWxpYyBsb2N1cywgYW5kIHRoZW4gd2UgYXBwcm94aW1hdGUgc29tZSAkdCQtc3RlcCB0cmFuc2l0aW9uIHByb2JhYmlsaXRpZXMgIGJ5IE1vbnRlIENhcmxvLgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KClx1c2VwYWNrYWdle2Jsa2FycmF5fQpcdXNlcGFja2FnZXthbXNtYXRofQpcbmV3Y29tbWFuZHtcYm19e1xib2xkc3ltYm9sfQpcbmV3Y29tbWFuZHtcRXhwfXtcbWF0aGJiYntFfX0KClRoaXMgaXMgYW4gUi1jb2RlIGNvbXBhbmlvbiB0byBTZXNzaW9uIDIsIGFyb3VuZCBwYWdlcyA5IHRvIDExLiAgV2UgYXJlIGdvaW5nIHRvIAptYWtlIGEgc2ltcGxlIGZ1bmN0aW9uIHRvIHNpbXVsYXRlIHRoZSBXcmlnaHQtRmlzaGVyIHBvcHVsYXRpb24sIGFuZCB0aGVuIHNob3cKaG93IHRoaXMgY2FuIGJlIHVzZWQgdG8gYXBwcm94aW1hdGUgJHQkLXN0ZXAgdHJhbnNpdGlvbiBwcm9iYWJpbGl0aWVzIChpLmUuLCAKdGhlIHByb2JhYmlsaXR5IHRoYXQgdGhlIHBvcHVsYXRpb24gZ29lcyBmcm9tICRYXzAkIGNvcGllcyBvZiB0aGUgJEEkIGFsbGVsZSB0bwokWF90JCBjb3BpZXMgb2YgdGhlICRBJCBhbGxlbGUgaW4gJHQkIGdlbmVyYXRpb25zLikKCkxldCdzIGxvYWQgSGFkbGV5J3MgdGlkeXZlcnNlIGFuZCBvdGhlciBwYWNrYWdlcyB3ZSBuZWVkIGJlZm9yZSB3ZSBnZXQgZ29pbmcuICBUaGUgZm9sbG93aW5nCndpbGwgYWxzbyBpbnN0YWxsIHRoZW0gaWYgeW91IGRvbid0IGhhdmUgdGhlbS4gCmBgYHtyfQpwYWNrcyA8LSBjKCJ0aWR5dmVyc2UiKQoKIyBpbnN0YWxsIGFueSB0aGF0IGFyZSBub3QgaGVyZQppbnN0YWxsX2VtIDwtIHNldGRpZmYocGFja3MsIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkKaWYgKGxlbmd0aChpbnN0YWxsX2VtKSA+IDApIGluc3RhbGwucGFja2FnZXMocGtncyA9IGluc3RhbGxfZW0pCgojIGxvYWQgZW0gdXAKZHVtcCA8LSBsYXBwbHkocGFja3MsIGZ1bmN0aW9uKHgpIGxpYnJhcnkoeCwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKSkKYGBgCgojIyBBIHNpbXBsZSBXLUYgZnVuY3Rpb24KClRoaXMgd2lsbCB0YWtlIGEgcG9wdWxhdGlvbiBzaXplIChhcyAkMk4kKSBhbmQgaW5pdGlhbCBudW1iZXIgb2YgJEEkIGFsbGVsZXMuCkVhY2ggZ2VuZXJhdGlvbiwgZ2VuZSBjb3BpZXMgYXJlIGRyYXduIHdpdGggcmVwbGFjZW1lbnQgZnJvbSB0aGUgcHJldmlvdXMgZ2VuZXJhdGlvbiwKd2hpY2ggY29ycmVzcG9uZHMgdG8gYmlub21pYWwgc2FtcGxpbmcgd2l0aCBhIHN1Y2Nlc3MgcHJvYmFiaWxpdHkgZ2l2ZW4gYmUgdGhlIGZyYWN0aW9uIG9mIAokQSQgZ2VuZXMgaW4gdGhlIHByZXZpb3VzIGdlbmVyYXRpb24uICAKCmBgYHtyIHdmLWZ1bmN9CiMnIHNpbXBsZSBmdW5jdGlvbgojJyAKIycgQHBhcmFtIFR3b04gdGhlIHBvcHVsYXRpb24gc2l6ZSBhcyB0aGUgbnVtYmVyIG9mIGdlbmUgY29waWVzIChhc3N1bWUgYSBkaXBsb2lkIHBvcCkKIycgQHBhcmFtIFgwIHRoZSBzdGFydGluZyBudW1iZXIgb2YgY29waWVzIG9mIHRoZSBBIGFsbGVsZQojJyBAcGFyYW0gZ2VucyB0aGUgbnVtYmVyIG9mIGdlbmVyYXRpb25zIHRvIHNpbXVsYXRlCndmX3NpbV9zaW5nbGUgPC0gZnVuY3Rpb24oVHdvTiwgWDAsIGdlbnMpIHsKICAjIG1ha2Ugc3VyZSBpbnB1dCBpcyBhY2NlcHRhYmxlCiAgc3RvcGlmbm90KFgwID49IDAgJiYgWDAgPD0gVHdvTikKICBzdG9waWZub3QoVHdvTiA+PSAyKQogIAogICMgaW5pdGlhbGl6ZSBhIHZlY3RvciB0byByZXR1cm4gdGhlIHZhbHVlcy4gV2Ugd2lsbCBpbmNsdWRlIAogICMgWDAgaW4gaXQgYXMgdGhlIGZpcnN0IGVsZW1lbnQKICByZXQgPC0gcmVwKE5BX2ludGVnZXJfLCBnZW5zICsgMSkKICByZXRbMV0gPC0gWDAKICBmb3IgKGkgaW4gMjooZ2VucyArIDEpKSB7CiAgICByZXRbaV0gPC0gcmJpbm9tKG4gPSAxLCBzaXplID0gVHdvTiwgcHJvYiA9IHJldFtpIC0gMV0gLyBUd29OKQogIH0KICAKICBuYW1lcyhyZXQpIDwtIGMoMCwxOmdlbnMpCiAgcmV0CiAgCn0KYGBgCgpUaGF0IGZ1bmN0aW9uIGlzIHVzZWZ1bCB0byBnZXQgb25lIGluc3RhbmNlIG9mIGEgV3JpZ2h0LUZpc2hlciBwb3B1bGF0aW9uIGF0IGEgdGltZS4KRm9yIGV4YW1wbGUsIGlmICROPTUwJCBhbmQgJFhfMCA9IDQwJCwgd2UgY2FuIGRvIHR3ZW50eSBnZW5lcmF0aW9ucyBvZiBXLUYgcmVwcm9kdWN0aW9uCmxpa2UgdGhpczoKYGBge3IgdHdlbnR5LWdlbnN9CnNldC5zZWVkKDUpCnJlc3VsdCA8LSB3Zl9zaW1fc2luZ2xlKFR3b04gPSAxMDAsIFgwID0gNDAsIGdlbnMgPSAyMCkKcmVzdWx0CmBgYAoKIyMgQSBmdW5jdGlvbiB0byBkbyBtdWx0aXBsZSByZXBsaWNhdGVzCgpUaGF0IGlzIGFsbCB3ZWxsIGFuZCBnb29kLCBidXQsIHdoZW4gd2UgYXJlIGRvaW5nIE1vbnRlIENhcmxvIHdlIGFyZSBnb2luZyB0byB3YW50IHRvCnNpbXVsYXRlIHRoaW5ncyBvdmVyIGFuZCBvdmVyIGFnYWluLiAgU28sIHdlIHdpbGwgbWFrZSBhbm90aGVyIGZ1bmN0aW9uIHRoYXQgcmV0dXJucwphIG1hdHJpeCBvZiBhbGxlbGUgZnJlcXVlbmN5IHRyYWplY3Rvcmllcy4gIEVhY2ggcm93IGlzIGEgZ2VuZXJhdGlvbiBhbmQgZWFjaCBjb2x1bXMgd2lsbApiZSBhIGRpZmZlcmVudCBzaW11bGF0aW9uLiAgV2UganVzdCBoYXZlIHRvIHR3aWRkbGUgdGhlIG9yaWdpbmFsIGZ1bmN0aW9uIGEgbGl0dGxlLgpgYGB7ciB3Zi1mdW5jLW1hdHJpeH0KIycgc2ltcGxlIGZ1bmN0aW9uCiMnIAojJyBAcGFyYW0gVHdvTiB0aGUgcG9wdWxhdGlvbiBzaXplIGFzIHRoZSBudW1iZXIgb2YgZ2VuZSBjb3BpZXMgKGFzc3VtZSBhIGRpcGxvaWQgcG9wKQojJyBAcGFyYW0gWDAgdGhlIHN0YXJ0aW5nIG51bWJlciBvZiBjb3BpZXMgb2YgdGhlIEEgYWxsZWxlCiMnIEBwYXJhbSBnZW5zIHRoZSBudW1iZXIgb2YgZ2VuZXJhdGlvbnMgdG8gc2ltdWxhdGUKIycgQHBhcmFtIHJlcHMgdGhlIG51bWJlciBvZiB0aW1lcyB0byBzaW11bGF0ZSB0aGUgcG9wdWxhdGlvbgp3Zl9zaW1fbXVsdGkgPC0gZnVuY3Rpb24oVHdvTiwgWDAsIGdlbnMsIHJlcHMpIHsKICAjIG1ha2Ugc3VyZSBpbnB1dCBpcyBhY2NlcHRhYmxlCiAgc3RvcGlmbm90KFgwID49IDAgJiYgWDAgPD0gVHdvTikKICBzdG9waWZub3QoVHdvTiA+PSAyKQogIAogICMgaW5pdGlhbGl6ZSBhIG1hdHJpeCB0byByZXR1cm4gdGhlIHZhbHVlcy4gV2Ugd2lsbCBpbmNsdWRlIAogICMgWDAgaW4gaXQgYXMgdGhlIGZpcnN0IHJvdwogIHJldCA8LSBtYXRyaXgoTkFfaW50ZWdlcl8sIG5yb3cgPSBnZW5zICsgMSwgbmNvbCA9IHJlcHMpCiAgcmV0WzEsIF0gPC0gWDAKICAKICBmb3IgKGkgaW4gMjooZ2VucyArIDEpKSB7CiAgICByZXRbaSwgXSA8LSByYmlub20obiA9IHJlcHMsIHNpemUgPSBUd29OLCBwcm9iID0gcmV0W2kgLSAxLCBdIC8gVHdvTikKICB9CiAgCiAgZGltbmFtZXMocmV0KSA8LSBsaXN0KGdlbmVyYXRpb24gPSBjKDAsMTpnZW5zKSwgcmVwbGljYXRlID0gMTpyZXBzKQogIHJldAogIAp9CmBgYAoKU28sIG5vdyB3ZSBjYW4gcXVpY2tseSBsb29rIGF0IDEwIHJlcGxpY2F0ZXMgb2YgYSBzaW11bGF0aW9uIG9mIDIsMDAwIGdlbmVyYXRpb25zCndpdGggJDJOID0gMTAwMCQgYW5kICAkWF8wID0gNDAwJCwgbGlrZSBzbzoKYGBge3J9CnJlczIgPC0gd2Zfc2ltX211bHRpKFR3b04gPSAxMDAwLCBYMCA9IDQwMCwgZ2VucyA9IDEwMDAsIHJlcHMgPSAxMCkKaGVhZChyZXMyKQpgYGAKClNvLCBpZiB3ZSB3YW50ZWQgdG8gcGxvdCB0aGVzZSAxMCBkaWZmZXJlbnQgcG9wdWxhdGlvbiB0cmFqZWN0b3JpZXMsIHVzaW5nIGdncGxvdAp3ZSBjb3VsZCBkbyB0aGlzOgpgYGB7cn0KIyBtYWtlIGEgdGliYmxlIG9mIHRoZSByZXN1bHRzCnJ0aWIyIDwtIHRpYmJsZShYID0gYXMudmVjdG9yKHJlczIpLAogICAgICAgICAgICAgICAgZ2VuZXJhdGlvbiA9IHJlcChyb3duYW1lcyhyZXMyKSwgbmNvbChyZXMyKSksCiAgICAgICAgICAgICAgICByZXBsaWNhdGUgPSByZXAoY29sbmFtZXMocmVzMiksIGVhY2ggPSBucm93KHJlczIpKSkgJT4lCiAgbXV0YXRlKGdlbmVyYXRpb24gPSBhcy5pbnRlZ2VyKGdlbmVyYXRpb24pLAogICAgICAgICByZXBsaWNhdGUgPSBhcy5pbnRlZ2VyKHJlcGxpY2F0ZSkpICU+JQogIHNlbGVjdChyZXBsaWNhdGUsIGdlbmVyYXRpb24sIFgpCgojIGxvb2tzIGxpa2UgdGhpczoKcnRpYjIKCiMgdGhlbiBwbG90IGl0OgpnZ3Bsb3QocnRpYjIsIGFlcyh4ID0gZ2VuZXJhdGlvbiwgeSA9IFgpKSArCiAgZ2VvbV9saW5lKCkgKwogIGZhY2V0X3dyYXAofiByZXBsaWNhdGUpCmBgYAoKIyMgVGhlIDE0LWdlbmVyYXRpb24gZXhhbXBsZSBmcm9tIHRoZSBsZWN0dXJlCgpOb3csIGxldCdzIHJlcGxpY2F0ZSB0aGUgZXhhbXBsZSBmcm9tIHRoZSBsZWN0dXJlOiAkMk4gPSAyMDAkLCAkWF8wID0gNjAkIGZvciAxNCBnZW5lcmF0aW9ucyAKb2YgZHJpZnQsIHdpdGggJG4gPSA1MCwwMDAkIHJlcGxpY2F0ZXMuCmBgYHtyfQpsZWN0X2V4YW1wIDwtIHdmX3NpbV9tdWx0aShUd29OID0gMjAwLCBYMCA9IDYwLCBnZW5zID0gMTQsIHJlcHMgPSA1MDAwMCkKCiMgYW5kIHRvIGdldCB0aGUgc3RhdGUgYXQgdCA9IDE0IHdlIG5lZWQgdG8gZ3JhYiB0aGF0IHJvdy4gIE5vdGUgdGhhdCAKIyB3ZSB1c2UgYSBjaGFyYWN0ZXIgIjE0IiBiZWNhdXNlIGl0IHN0YXJ0cyBhdCAwCnQxNCA8LSBsZWN0X2V4YW1wWyIxNCIsIF0KYGBgCldlIGNhbiBwbG90IGEgaGlzdG9ncmFtIG9mIHRoYXQgcXVpY2tseToKYGBge3J9Cmhpc3QodDE0KQpgYGAKCk5vdywgaW4gb3JkZXIgdG8gc2VlIHRoYXQgdGhlIGhpc3RvZ3JhbSBpcyBhIHNlcmllcyBvZiBsaXR0bGUgaW5kaWNhdG9yIGZ1bmN0aW9ucwp3ZSBjYW4gY29uc3RydWN0IHdoYXQgd2FzIGRvbmUgaW4gdGhlIG5vdGVzIHVzaW5nIHRoZSBjdXQgZnVuY3Rpb24uIAoKQXMgaXQgc2F5cyBpbiB0aGUgbGVjdHVyZSBub3RlczogdGhlIGRpc3RyaWJ1dGlvbiBvZiAkWF97MTR9JCBjYW4gYmUgYXBwcm94aW1hdGVkIGJ5IGEgaGlzdG9ncmFtLgpFYWNoIGhpc3RvZ3JhbSBjb2x1bW4gaXMgYW4gYXBwcm94aW1hdGlvbiBvZiBhbiBleHBlY3RhdGlvbjoKJCQKXGJlZ2lue2FsaWduZWR9CgkJUChhXGxlcSBYPGErMikgJiA9ICBcbWF0aGJie0V9W0lfe1x7eDphXGxlcSB4PGErMlx9fShYKV0gIFxcCgkJJlxhcHByb3ggIFxkaXNwbGF5c3R5bGUgXGZyYWN7MX17bn0KCQlcc3VtX3tpPTF9Xm4gSV97XHt4OmFcbGVxIHg8YSsyXH19KHheeyhpKX0pIApcZW5ke2FsaWduZWR9CiQkCmZvciAkYT0wLDIsXGxkb3RzLDIwMCQsIHdoZXJlIGVhY2ggJHheeyhpKX0kIGlzIGFuIGluZGVwZW5kZW50IHJlYWxpemF0aW9uIG9mIHRoZSAKbnVtYmVyIG9mICRBJCBhbGxlbGVzIGF0ICR0PTE0JCBpbiB0aGUgV3JpZ2h0LUZpc2hlciBtb2RlbC4KClRvIGRvIHRoYXQgaW4gUiBjb2RlLCB3ZSB3aWxsIGBjdXRgIHRoZSBgdDE0YCB2YXJpYWJsZSBpbnRvIGdyb3VwcyBvZiB0aGUgCmZvcm0gYFthLCBhKzIpYDoKYGBge3J9CmEgPC0gc2VxKDAsIDIwMiwgYnkgPSAyKQoKIyB0aGlzIHB1dHMgZWFjaCBvYnNlcnZhdGlvbnMgaW50byBhbiBpbnRlcnZhbApjbnRzIDwtIGN1dCh4ID0gdDE0LCBicmVha3MgPSBhLCByaWdodCA9IEZBTFNFKSAlPiUKICB0aWJibGUoaW50ZXJ2YWwgPSAuKSAlPiUgICMgdGhpcyBtYWtlcyBhIHRpYmJsZSBvZiBpdAogIGNvdW50KGludGVydmFsKSAgIyB0aGlzIGNvdW50cyBob3cgbWFueSBhcmUgaW4gZWFjaCBpbnRlcnZhbAoKY250cwpgYGAKCk5vdywgbGV0J3MgcGxvdCB0aGF0IGR1ZGU6CmBgYHtyfQpjbnRzICU+JQogIHNlcGFyYXRlKGludGVydmFsLCBpbnRvID0gYygibGVmdCIsICJyaWdodCIpLCBzZXAgPSAiLCIsIHJlbW92ZSA9IEZBTFNFKSAlPiUKICBtdXRhdGUobGVmdCA9IHBhcnNlX251bWJlcihsZWZ0KSwKICAgICAgICAgcmlnaHQgPSBwYXJzZV9udW1iZXIocmlnaHQpKSAlPiUKICBtdXRhdGUoYmluX21pZHBvaW50ID0gKGxlZnQgKyByaWdodCkgLyAyKSAlPiUKICBnZ3Bsb3QoLiwgYWVzKHggPSBiaW5fbWlkcG9pbnQsIHkgPSBuKSkgKyAKICBnZW9tX2NvbChmaWxsID0gTkEsIGNvbG91ciA9ICJibGFjayIsIHNpemUgPSAwLjIpICsKICB0aGVtZV9idygpCmBgYAoKIyMgU2Vzc2lvbiBJbmZvcm1hdGlvbgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCg==