Iteration with Purrr

In functions, we talked about how important it is to reduce duplication in your code by creating functions instead of copying-and-pasting. Reducing code duplication has three main benefits:

It’s easier to see the intent of your code, because your eyes are drawn to what’s different, not what stays the same.

It’s easier to respond to changes in requirements. As your needs change, you only need to make changes in one place, rather than remembering to change every place that you copied-and-pasted the code.

You’re likely to have fewer bugs because each line of code is used in more places.

Another tool for reducing duplication is iteration, which helps you when you need to do the same thing to multiple inputs: repeating the same operation on different columns, or on different datasets. In this chapter you’ll learn about two important iteration paradigms: imperative programming and functional programming. N/p>

On the imperative side you have tools like for loops and while loops, which are a great place to start because they make iteration very explicit, so it’s obvious what’s happening. However, for loops are quite verbose, and require quite a bit of bookkeeping code that is duplicated for every for loop.

Functional programming (FP) offers tools to extract out this duplicated code, so each common for loop pattern gets its own function. Once you master the vocabulary of FP, you can solve many common iteration problems with less code, more ease, and fewer errors

Prerequisites

library(tidyverse)
package <U+393C><U+3E31>tidyverse<U+393C><U+3E32> was built under R version 3.3.3Loading 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

for Loops

df <- tibble(
  a = rnorm(10),
  b = rnorm(10),
  c = rnorm(10),
  d = rnorm(10)
)
df

We want to compute the median of each column. You could do with copy-and-paste:

median(df$a)
[1] 0.05681125
#> [1] -0.246
median(df$b)
[1] 0.5622278
#> [1] -0.287
median(df$c)
[1] 0.07211131
#> [1] -0.0567
median(df$d)
[1] 0.3104609
#> [1] 0.144

But that breaks our rule of thumb: never copy and paste more than twice. Instead, we could use a for loop:

output <- vector("double", ncol(df))
for (i in seq_along(df)) {            
  output[[i]] <- median(df[[i]])      
}
output
[1] 0.05681125 0.56222775 0.07211131 0.31046093

Every for loop has three components:

The output: output <- vector(“double”, length(x)). Before you start the loop, you must always allocate sufficient space for the output. This is very important for efficiency: if you grow the [for loop] at each iteration using c() (for example), your for loop will be very slow.

A general way of creating an empty vector of given length is the vector() function. It has two arguments: the type of the vector (“logical”, “integer”, “double”, “character”, etc) and the length of the vector.

The sequence: i in seq_along(df). This determines what to loop over: each run of the for loop will assign i to a different value from seq_along(df). It’s useful to think of i as a pronoun, like “it”.

You might not have seen seq_along() before. It’s a safe version of the familiar 1:length(l), with an important difference: if you have a zero-length vector, seq_along() does the right thing:

y <- vector("double", 0)
seq_along(y)
integer(0)
1:length(y)
[1] 1 0

You probably won’t create a zero-length vector deliberately, but it’s easy to create them accidentally. If you use 1:length(x) instead of seq_along(x), you’re likely to get a confusing error message.

The body: output[[i]] <- median(df[[i]]). This is the code that does the work. It’s run repeatedly, each time with a different value for i. The first iteration will run output[[1]] <- median(df[[1]]), the second will run output[[2]] <- median(df[[2]]), and so on.

Exercises

Write for loops to:

Compute the mean of every column in mtcars.

output <-  vector("double", ncol(mtcars))
names(output) <-  names(mtcars)
for (i in names(mtcars)) {
  output[[1]] <-  mean(mtcars[[i]])
}
output
   mpg    cyl   disp     hp   drat     wt   qsec     vs     am   gear   carb 
2.8125 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 

Determine the type of each column in nycflights13::flights.

data("flights", package = "nycflights13")
output <-  vector("list", ncol(flights))
names(output) <-  names(flights)
for (i in names(flights)){
  output[[i]] <-  class(flights[[i]])
}
output
$year
[1] "integer"

$month
[1] "integer"

$day
[1] "integer"

$dep_time
[1] "integer"

$sched_dep_time
[1] "integer"

$dep_delay
[1] "numeric"

$arr_time
[1] "integer"

$sched_arr_time
[1] "integer"

$arr_delay
[1] "numeric"

$carrier
[1] "character"

$flight
[1] "integer"

$tailnum
[1] "character"

$origin
[1] "character"

$dest
[1] "character"

$air_time
[1] "numeric"

$distance
[1] "numeric"

$hour
[1] "numeric"

$minute
[1] "numeric"

$time_hour
[1] "POSIXct" "POSIXt" 

Compute the number of unique values in each column of iris.

data(iris)
iris_uniq <- vector("double", ncol(iris))
names(iris_uniq) <- names(iris)
for (i in names(iris)) {
  iris_uniq[i] <- length(unique(iris[[i]]))
}
iris_uniq
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
          35           23           43           22            3 

Generate 10 random normals for each of
??=???10 , 0 , 10 and 100 Think about the output, sequence, and body before you start writing the loop.

# number to draw
n <- 10
# values of the mean
mu <- c(-10, 0, 10, 100)
normals <- vector("list", length(mu))
for (i in seq_along(normals)) {
  normals[[i]] <- rnorm(n, mean = mu[i])
}
normals
[[1]]
 [1]  -9.891964  -9.872125  -9.409878 -11.415893  -9.431410 -10.563044 -10.467774
 [8]  -8.692471  -8.636785  -9.666098

[[2]]
 [1]  0.9846059  0.2957479 -0.3794097  0.9516208 -0.1972844 -0.9125486 -2.2860760
 [8]  1.2632451  0.3307835 -0.9994685

[[3]]
 [1]  9.224396  9.529922  9.197843  9.946666 10.476068 10.076336 10.540870
 [8]  9.657992  9.790516  9.796507

[[4]]
 [1] 101.26619 100.57311  99.28083  98.09691  99.41012 101.47679 100.87982
 [8]  99.39403 100.61694  99.94372

Eliminate the for loop in each of the following examples by taking advantage of an existing function that works with vectors:

out <- ""
letters
 [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s"
[20] "t" "u" "v" "w" "x" "y" "z"
for (x in letters) {
  out <- stringr::str_c(out, x)
}
out
[1] "abcdefghijklmnopqrstuvwxyz"

Solution: str_c already works with vectors, so simply use str_c with the collapse argument to return a single string.

stringr::str_c(letters, collapse = "")
[1] "abcdefghijklmnopqrstuvwxyz"
#> [1] "abcdefghijklmnopqrstuvwxyz"
Write a for loop that prints() the lyrics to the children’s song “Alice the camel”.

humps <- c("five", "four", "three", "two", "one", "no")
for (i in humps) {
  cat(stringr::str_c("Alice the camel has ", rep(i, 3), " humps.",
             collapse = "\n"), "\n")
  if (i == "no") {
    cat("Now Alice is a horse.\n")
  } else {
    cat("So go, Alice, go.\n")
  }
  cat("\n")
}
Alice the camel has five humps.
Alice the camel has five humps.
Alice the camel has five humps. 
So go, Alice, go.

Alice the camel has four humps.
Alice the camel has four humps.
Alice the camel has four humps. 
So go, Alice, go.

Alice the camel has three humps.
Alice the camel has three humps.
Alice the camel has three humps. 
So go, Alice, go.

Alice the camel has two humps.
Alice the camel has two humps.
Alice the camel has two humps. 
So go, Alice, go.

Alice the camel has one humps.
Alice the camel has one humps.
Alice the camel has one humps. 
So go, Alice, go.

Alice the camel has no humps.
Alice the camel has no humps.
Alice the camel has no humps. 
Now Alice is a horse.
Convert the nursery rhyme “ten in the bed” to a function. Generalise it to any number of people in any sleeping structure.

numbers <- c("ten", "nine", "eight", "seven", "six", "five",
             "four", "three", "two", "one")
for (i in numbers) {
  cat(stringr::str_c("There were ", i, " in the bed\n"))
  cat("and the little one said\n")
  if (i == "one") {
    cat("I'm lonely...")
    } else {
    cat("Roll over, roll over\n")
    cat("So they all rolled over and one fell out.\n")
      }
      cat("\n")
  }
There were ten in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were nine in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were eight in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were seven in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were six in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were five in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were four in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were three in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were two in the bed
and the little one said
Roll over, roll over
So they all rolled over and one fell out.

There were one in the bed
and the little one said
I'm lonely...
Convert the song “99 bottles of beer on the wall” to a function. Generalise to any number of any vessel containing any liquid on any surface.

bottles <- function(i) {
  if (i > 2) {
   bottles <- stringr::str_c(i - 1, " bottles")
  } else if (i == 2) {
   bottles <- stringr::str_c(1," bottles")
  } else {
   bottles <- stringr::str_c("No more bottles")
  }
  bottles
}
beer_bottles <- function(n) {
  # should test whether n >= 1.
  for (i in seq(n, 1)) {
     cat(stringr::str_c(bottles(i), " of beer on the wall, ", bottles(i), " of beer.\n"))
     cat(stringr::str_c("Take one down and pass it around, ", bottles(i - 1),
                " of beer on the wall.\n\n"))
  }
  cat("No more bottles of beer on the wall, no more bottles of beer.\n")
  cat(stringr::str_c("Go to the store and buy some more, ", bottles(n), " of beer on the wall.\n"))
}
beer_bottles(4)
3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottles of beer on the wall.

1 bottles of beer on the wall, 1 bottles of beer.
Take one down and pass it around, No more bottles of beer on the wall.

No more bottles of beer on the wall, No more bottles of beer.
Take one down and pass it around, No more bottles of beer on the wall.

No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 3 bottles of beer on the wall.

It’s common to see for loops that don’t preallocate the output and instead increase the length of a vector at each step:

output <- vector("integer", 0)
for (i in seq_along(x)) {
  output <- c(output, lengths(x[[i]]))
}
output
[1] 1

I’ll use the package microbenchmark to time this. Microbenchmark will run an R expression a number of times and time it.

Define a function that appends to an integer vector.

library(microbenchmark)
package <U+393C><U+3E31>microbenchmark<U+393C><U+3E32> was built under R version 3.3.3
add_to_vector <- function(n) {
  output <- vector("integer", 0)
  for (i in seq_len(n)) {
    output <- c(output, i)
  }
  output  
}
microbenchmark(add_to_vector(10000), times = 3)
Unit: milliseconds
                 expr      min      lq     mean  median       uq      max neval
 add_to_vector(10000) 257.1821 271.989 293.0816 286.796 311.0313 335.2667     3

And one that pre-allocates it.

add_to_vector_2 <- function(n) {
  output <- vector("integer", n)
  for (i in seq_len(n)) {
    output[[i]] <- i
  }
  output
}
microbenchmark(add_to_vector_2(10000), times = 3)
Unit: milliseconds
                   expr      min       lq     mean   median       uq      max
 add_to_vector_2(10000) 26.80056 26.90097 27.38167 27.00137 27.67223 28.34308
 neval
     3

The pre-allocated vector is about 100 times faster! YMMV, but the longer the vector and the bigger the objects, the more that pre-allocation will outperform appending.

For Loop Variations

Once you have the basic for loop under your belt, there are some variations that you should be aware of. These variations are important regardless of how you do iteration, so don’t forget about them once you’ve master the FP techniques you’ll learn about in the next section.

There are four variations on the basic theme of the for loop:

  • Modifying an existing object, instead of creating a new object.
  • Looping over names or values, instead of indices.
  • Handling outputs of unknown length.
  • Handling sequences of unknown length.

    Modifying an existing object

    Sometimes you want to use a for loop to modify an existing object. For example, remember our challenge from functions. We wanted to rescale every column in a data frame:

    df <- tibble(
      a = rnorm(10),
      b = rnorm(10),
      c = rnorm(10),
      d = rnorm(10)
    )
    rescale01 <- function(x) {
      rng <- range(x, na.rm = TRUE)
      (x - rng[1]) / (rng[2] - rng[1])
    }
    df$a <- rescale01(df$a)
    df$b <- rescale01(df$b)
    df$c <- rescale01(df$c)
    df$d <- rescale01(df$d)
    To solve this with a for loop we again think about the three components:

    Output: we already have the output - it’s the same as the input!
    Sequence: we can think about a data frame as a list of columns, so we can iterate over each column with seq_along(df).
    Body: apply rescale01().

    This gives us:

    for (i in seq_along(df)) {
      df[[i]] <- rescale01(df[[i]])
    }
    df
    Typically you’ll be modifying a list or data frame with this sort of loop, so remember to use [[, not [. You might have spotted that I used [[ in all my for loops: I think it’s better to use [[ even for atomic vectors because it makes it clear that I want to work with a single element.

    Looping Patterns

    There are three basic ways to loop over a vector. So far I’ve shown you the most general: looping over the numeric indices with for (i in seq_along(xs)), and extracting the value with x[[i]]. There are two other forms:

    Loop over the elements: for (x in xs). This is most useful if you only care about side-effects, like plotting or saving a file, because it’s difficult to save the output efficiently.

    Loop over the names: for (nm in names(xs)). This gives you name, which you can use to access the value with x[[nm]]. This is useful if you want to use the name in a plot title or a file name. If you’re creating named output, make sure to name the results vector like so:

    results <- vector("list", length(x))
    names(results) <- names(x)
    names
    function (x)  .Primitive("names")

    Unknown Output Length

    Sometimes you might not know how long the output will be. For example, imagine you want to simulate some random vectors of random lengths. You might be tempted to solve this problem by progressively growing the vector:

    means <- c(0, 1, 2)
    output <- double()
    for (i in seq_along(means)) {
      n <- sample(100, 1)
      output <- c(output, rnorm(n, means[[i]]))
    }
    str(output)
     num [1:207] -1.3229 2.1883 0.2413 0.0751 -1.9295 ...
    But this is not very efficient because in each iteration, R has to copy all the data from the previous iterations. In technical terms you get “quadratic” ( O(n2) ) behaviour which means that a loop with three times as many elements would take nine times as long to run.

    A better solution to save the results in a list, and then combine into a single vector after the loop is done:

    out <- vector("list", length(means))
    for (i in seq_along(means)) {
      n <- sample(100, 1)
      out[[i]] <- rnorm(n, means[[i]])
    }
    str(out)
    List of 3
     $ : num [1:50] -0.0931 0.0333 0.3342 -0.0619 1.4863 ...
     $ : num [1:100] 0.437 0.643 0.651 1.468 0.681 ...
     $ : num [1:94] 2.868 1.49 1.013 2.033 0.656 ...
    str(unlist(out))
     num [1:244] -0.0931 0.0333 0.3342 -0.0619 1.4863 ...
    Here I’ve used unlist() to flatten a list of vectors into a single vector. A stricter option is to use purrr::flatten_dbl() - it will throw an error if the input isn’t a list of doubles.

    This pattern occurs in other places too:

    You might be generating a long string. Instead of paste()ing together each iteration with the previous, save the output in a character vector and then combine that vector into a single string with paste(output, collapse = “”).

    You might be generating a big data frame. Instead of sequentially rbind()ing in each iteration, save the output in a list, then use dplyr::bind_rows(output) to combine the output into a single data frame.

    Watch out for this pattern. Whenever you see it, switch to a more complex result object, and then combine in one step at the end.

    Unknown Sequence Length

    Sometimes you don’t even know how long the input sequence should run for. This is common when doing simulations. For example, you might want to loop until you get three heads in a row. You can’t do that sort of iteration with the for loop. Instead, you can use a while loop. A while loop is simpler than for loop because it only has two components, a condition and a body.

    A while loop is also more general than a for loop, because you can rewrite any for loop as a while loop, but you can’t rewrite every while loop as a for loop:

    for (i in seq_along(x)) {
      # body
    }
    # Equivalent to
    i <- 1
    while (i <= length(x)) {
      # body
      i <- i + 1 
    }

    Here’s how we could use a while loop to find how many tries it takes to get three heads in a row:

    flip <- function() sample(c("T", "H"), 1)
    flips <- 0
    nheads <- 0
    while (nheads < 3) {
      if (flip() == "H") {
        nheads <- nheads + 1
      } else {
        nheads <- 0
      }
      flips <- flips + 1
    }
    flips
    [1] 3

    I mention while loops only briefly, because I hardly ever use them. They’re most often used for simulation, which is outside the scope of this book. However, it is good to know they exist so that you’re prepared for problems where the number of iterations is not known in advance.

    Exercises

    Imagine you have a directory full of CSV files that you want to read in. You have their paths in a vector, files <- dir(“data/”, pattern = “\.csv$”, full.names = TRUE), and now want to read each one with read_csv(). Write the for loop that will load them into a single data frame.

    df <- vector("list", length(files))
    for (fname in seq_along(files)) {
      df[[i]] <- read_csv(files[[i]])
    }
    df <- bind_rows(df)

    What happens if you use for (nm in names(x)) and x has no names? What if only some of the elements are named? What if the names are not unique?

    When there are no names for the vector, it does not run the code in the loop (it runs zero iterations of the loop):

    x <- 1:3
    print(names(x))
    NULL
    #> NULL
    for (nm in names(x)) {
      print(nm)
      print(x[[nm]])
    }

    If there only some names, then we get an error if we try to access an element without a name. However, oddly, nm == “” when there is no name.

    x <- c(a = 1, 2, c = 3)
    names(x)
    [1] "a" ""  "c"
    for (nm in names(x)) {
      print(nm)
      print(x[[nm]])
    }
    [1] "a"
    [1] 1
    [1] ""
    Error in x[[nm]] : subscript out of bounds

    Finally, if there are duplicate names, then x[[nm]] will give the first element with that name. There is no way to access duplicately named elements by name.

    x <- c(a = 1, a = 2, c = 3)
    names(x)
    [1] "a" "a" "c"
    for (nm in names(x)) {
      print(nm)
      print(x[[nm]])
    }
    [1] "a"
    [1] 1
    [1] "a"
    [1] 1
    [1] "c"
    [1] 3

    Write a function that prints the mean of each numeric column in a data frame, along with its name. For example, show_mean(iris) would print:

    show_mean <- function(df, digits = 2) {
      # Get max length of any variable in the dataset
      maxstr <- max(stringr::str_length(names(df)))
      for (nm in names(df)) {
        if (is.numeric(df[[nm]])) {
          cat(stringr::str_c(stringr::str_pad(stringr::str_c(nm, ":"), maxstr + 1L, side = "right"),
                    format(mean(df[[nm]]), digits = digits, nsmall = digits),
                    sep = " "),
              "\n")
        }
      }
    }
    show_mean(iris)
    Sepal.Length: 5.84 
    Sepal.Width:  3.06 
    Petal.Length: 3.76 
    Petal.Width:  1.20 

    What does this code do? How does it work?

    trans <- list( 
      disp = function(x) x * 0.0163871,
      am = function(x) {
        factor(x, labels = c("auto", "manual"))
      }
    )
    for (var in names(trans)) {
      mtcars[[var]] <- trans[[var]](mtcars[[var]])
    }
    

    This code mutates the disp and am columns.

    disp is multiplied by 0.0163871 am is replaced by a factor variable. The code works by looping over a named list of functions. It calls the named function in the list on the column of mtcars with the same name, and replaces the values of that column.

    For Loops vs functionals

    For loops are not as important in R as they are in other languages because R is a functional programming language. This means that it’s possible to wrap up for loops in a function, and call that function instead of using the for loop directly.

    To see why this is important, consider (again) this simple data frame:

    df <- tibble(
      a = rnorm(10),
      b = rnorm(10),
      c = rnorm(10),
      d = rnorm(10)
    )

    Imagine you want to compute the mean of every column. You could do that with a for loop:

    output <- vector("double", length(df))
    for (i in seq_along(df)) {
      output[[i]] <- mean(df[[i]])
    }
    output
    [1] -0.2436047  0.6860735 -0.3421132  0.6683193

    You realise that you’re going to want to compute the means of every column pretty frequently, so you extract it out into a function:

    col_mean <- function(df) {
      output <- vector("double", length(df))
      for (i in seq_along(df)) {
        output[i] <- mean(df[[i]])
      }
      output
    }
    

    But then you think it’d also be helpful to be able to compute the median, and the standard deviation, so you copy and paste your col_mean() function and replace the mean() with median() and sd():

    col_median <- function(df) {
      output <- vector("double", length(df))
      for (i in seq_along(df)) {
        output[i] <- median(df[[i]])
      }
      output
    }
    col_sd <- function(df) {
      output <- vector("double", length(df))
      for (i in seq_along(df)) {
        output[i] <- sd(df[[i]])
      }
      output
    }

    Uh oh! You’ve copied-and-pasted this code twice, so it’s time to think about how to generalise it. Notice that most of this code is for-loop boilerplate and it’s hard to see the one thing (mean(), median(), sd()) that is different between the functions.

    We can do exactly the same thing with col_mean(), col_median() and col_sd() by adding an argument that supplies the function to apply to each column:

    col_summary <- function(df, fun) {
      out <- vector("double", length(df))
      for (i in seq_along(df)) {
        out[i] <- fun(df[[i]])
      }
      out
    }
    col_summary(df, median)
    [1] -0.1096523  0.5112549 -0.3405418  0.6707342
    col_summary(df, mean)
    [1] -0.2436047  0.6860735 -0.3421132  0.6683193
    The idea of passing a function to another function is extremely powerful idea, and it’s one of the behaviours that makes R a functional programming language. It might take you a while to wrap your head around the idea, but it’s worth the investment. In the rest of the chapter, you’ll learn about and use the purrr package, which provides functions that eliminate the need for many common for loops. The apply family of functions in base R (apply(), lapply(), tapply(), etc) solve a similar problem, but purrr is more consistent and thus is easier to learn.

    The goal of using purrr functions instead of for loops is to allow you break common list manipulation challenges into independent pieces:

    How can you solve the problem for a single element of the list? Once you’ve solved that problem, purrr takes care of generalising your solution to every element in the list.

    If you’re solving a complex problem, how can you break it down into bite-sized pieces that allow you to advance one small step towards a solution? With purrr, you get lots of small pieces that you can compose together with the pipe.

    This structure makes it easier to solve new problems. It also makes it easier to understand your solutions to old problems when you re-read your old code.

    Exercises

    Read the documentation for apply(). In the 2d case, what two for loops does it generalise.

    It generalises looping over the rows or columns of a matrix or data-frame.

    Adapt col_summary() so that it only applies to numeric columns You might want to start with an is_numeric() function that returns a logical vector that has a TRUE corresponding to each numeric column.

    col_summary2 <- function(df, fun) {
      # test whether each colum is numeric
      numeric_cols <- vector("logical", length(df))
      for (i in seq_along(df)) {
        numeric_cols[[i]] <- is.numeric(df[[i]])
      }
      # indexes of numeric columns
      idxs <- seq_along(df)[numeric_cols]
      # number of numeric columns
      n <- sum(numeric_cols)
      out <- vector("double", n)
      for (i in idxs) {
        out[i] <- fun(df[[i]])
      }
      out
    }
    df <- tibble(
      a = rnorm(10),
      b = rnorm(10),
      c = letters[1:10],
      d = rnorm(10)
    )
    col_summary2(df, mean)
    [1] -0.02891776  0.52704384  0.00000000 -0.45001795

    The Map functions

    There is one function for each type of output:

  • map() makes a list.
  • map_lgl() makes a logical vector.
  • map_int() makes an integer vector.
  • map_dbl() makes a double vector.
  • map_chr() makes a character vector.
  • Each function takes a vector as input, applies a function to each piece, and then returns a new vector that’s the same length (and has the same names) as the input. The type of the vector is determined by the suffix to the map function.

    Once you master these functions, you’ll find it takes much less time to solve iteration problems. But you should never feel bad about using a for loop instead of a map function. The map functions are a step up a tower of abstraction, and it can take a long time to get your head around how they work. The important thing is that you solve the problem that you’re working on, not write the most concise and elegant code (although that’s definitely something you want to strive towards!).

    Some people will tell you to avoid for loops because they are slow. They’re wrong! (Well at least they’re rather out of date, as for loops haven’t been slow for many years). The chief benefits of using functions like map() is not speed, but clarity: they make your code easier to write and to read.

    We can use these functions to perform the same computations as the last for loop. Those summary functions returned doubles, so we need to use map_dbl():

    map_dbl(df, mean)
    argument is not numeric or logical: returning NA
              a           b           c           d 
    -0.02891776  0.52704384          NA -0.45001795 
    map_dbl(df, median)
    argument is not numeric or logical: returning NA
             a          b          c          d 
    -0.2058552  0.7948718         NA -0.3792446 
    map_dbl(df, sd)
    NAs introduced by coercion
           a        b        c        d 
    1.133854 1.388394       NA 0.785551 

    Compared to using a for loop, focus is on the operation being performed (i.e. mean(), median(), sd()), not the bookkeeping required to loop over every element and store the output. This is even more apparent if we use the pipe:

    df %>% map_dbl(mean)
    argument is not numeric or logical: returning NA
              a           b           c           d 
    -0.02891776  0.52704384          NA -0.45001795 
    df %>% map_dbl(median)
    argument is not numeric or logical: returning NA
             a          b          c          d 
    -0.2058552  0.7948718         NA -0.3792446 
    df %>% map_dbl(sd)
    NAs introduced by coercion
           a        b        c        d 
    1.133854 1.388394       NA 0.785551 

    There are a few differences between map_*() and col_summary():

    All purrr functions are implemented in C. This makes them a little faster at the expense of readability.

    The second argument, .f, the function to apply, can be a formula, a character vector, or an integer vector. You’ll learn about those handy shortcuts in the next section. map_*() uses . ([dot dot dot]) to pass along additional arguments to .f each time it’s called:

    map_dbl(df, mean, trim = 0.5)
    argument is not numeric or logical: returning NA
             a          b          c          d 
    -0.2058552  0.7948718         NA -0.3792446 

    The map function also preserves names:

    z <- list(x = 1:3, y = 4:5)
    map_int(z, length)
    x y 
    3 2 

    There are a few shortcuts that you can use with .f in order to save a little typing. Imagine you want to fit a linear model to each group in a dataset. The following toy example splits the up the mtcars dataset in to three pieces (one for each value of cylinder) and fits the same linear model to each piece:

    models <- mtcars %>% 
      split(.$cyl) %>% 
      map(function(df) lm(mpg ~ wt, data = df))
    models
    $`4`
    
    Call:
    lm(formula = mpg ~ wt, data = df)
    
    Coefficients:
    (Intercept)           wt  
         39.571       -5.647  
    
    
    $`6`
    
    Call:
    lm(formula = mpg ~ wt, data = df)
    
    Coefficients:
    (Intercept)           wt  
          28.41        -2.78  
    
    
    $`8`
    
    Call:
    lm(formula = mpg ~ wt, data = df)
    
    Coefficients:
    (Intercept)           wt  
         23.868       -2.192  
    The syntax for creating an anonymous function in R is quite verbose so purrr provides a convenient shortcut: a one-sided formula. Note: The lm() function runs a linear regression. It is covered in the Model Basics chapter.

    From r-bloggers.coms
    In FP, naming and applying a function are two separate operations, you don’t need to give your functions names in order to call them. So, calling this function

    powfun <- function(x, pow) {
        x^pow
    }
    powfun(2, 10)
    [1] 1024

    to the interpreter is exactly the same as applying variables to the anonymous function:

    #anonymouse equivalent 
    (function(x, pow) {
        x^pow
    })(2, 10)
    [1] 1024
    models <- mtcars %>% 
      split(.$cyl) %>% 
      map(~lm(mpg ~ wt, data = .))
    models
    $`4`
    
    Call:
    lm(formula = mpg ~ wt, data = .)
    
    Coefficients:
    (Intercept)           wt  
         39.571       -5.647  
    
    
    $`6`
    
    Call:
    lm(formula = mpg ~ wt, data = .)
    
    Coefficients:
    (Intercept)           wt  
          28.41        -2.78  
    
    
    $`8`
    
    Call:
    lm(formula = mpg ~ wt, data = .)
    
    Coefficients:
    (Intercept)           wt  
         23.868       -2.192  
    Here I’ve used . as a pronoun: it refers to the current list element (in the same way that i referred to the current index in the for loop).

    When you’re looking at many models, you might want to extract a summary statistic like the R2 . To do that we need to first run summary() and then extract the component called r.squared. We could do that using the shorthand for anonymous functions:

    models %>% 
      map(summary) %>% 
      map_dbl(~.$r.squared)
            4         6         8 
    0.5086326 0.4645102 0.4229655 

    But extracting named components is a common operation, so purrr provides an even shorter shortcut: you can use a string.

    models %>% 
      map(summary) %>% 
      map_dbl("r.squared")

    You can also use an integer to select elements by position:

    x <- list(list(1, 2, 3), list(4, 5, 6), list(7, 8, 9))
    x %>% map_dbl(2)
    [1] 2 5 8

    Base R

    If you’re familiar with the apply family of functions in base R, you might have noticed some similarities with the purrr functions:

    lapply() is basically identical to map(), except that map() is consistent with all the other functions in purrr, and you can use the shortcuts for .f.

    Base sapply() is a wrapper around lapply() that automatically simplifies the output. This is useful for interactive work but is problematic in a function because you never know what sort of output you’ll get:

    x1 <- list(
      c(0.27, 0.37, 0.57, 0.91, 0.20),
      c(0.90, 0.94, 0.66, 0.63, 0.06), 
      c(0.21, 0.18, 0.69, 0.38, 0.77)
    )
    x2 <- list(
      c(0.50, 0.72, 0.99, 0.38, 0.78), 
      c(0.93, 0.21, 0.65, 0.13, 0.27), 
      c(0.39, 0.01, 0.38, 0.87, 0.34)
    )
    threshold <- function(x, cutoff = 0.8) x[x > cutoff]
    x1 %>% sapply(threshold) %>% str()
    List of 3
     $ : num 0.91
     $ : num [1:2] 0.9 0.94
     $ : num(0) 
    x2 %>% sapply(threshold) %>% str()
     num [1:3] 0.99 0.93 0.87
    vapply() is a safe alternative to sapply() because you supply an additional argument that defines the type. The only problem with vapply() is that it’s a lot of typing: vapply(df, is.numeric, logical(1)) is equivalent to map_lgl(df, is.numeric). One advantage of vapply() over purrr’s map functions is that it can also produce matrices - the map functions only ever produce vectors.

    Exercises

    Write code that uses one of the map functions to:

    Compute the mean of every column in mtcars.

    map_dbl(mtcars, mean)
           mpg        cyl       disp         hp       drat         wt       qsec 
     20.090625   6.187500 230.721875 146.687500   3.596563   3.217250  17.848750 
            vs         am       gear       carb 
      0.437500   0.406250   3.687500   2.812500 

    Determine the type of each column in nycflights13::flights.

    map(nycflights13::flights, class)
    $year
    [1] "integer"
    
    $month
    [1] "integer"
    
    $day
    [1] "integer"
    
    $dep_time
    [1] "integer"
    
    $sched_dep_time
    [1] "integer"
    
    $dep_delay
    [1] "numeric"
    
    $arr_time
    [1] "integer"
    
    $sched_arr_time
    [1] "integer"
    
    $arr_delay
    [1] "numeric"
    
    $carrier
    [1] "character"
    
    $flight
    [1] "integer"
    
    $tailnum
    [1] "character"
    
    $origin
    [1] "character"
    
    $dest
    [1] "character"
    
    $air_time
    [1] "numeric"
    
    $distance
    [1] "numeric"
    
    $hour
    [1] "numeric"
    
    $minute
    [1] "numeric"
    
    $time_hour
    [1] "POSIXct" "POSIXt" 

    I had to use map rather than map_chr since the class Though if by type, typeof is meant.

    Compute the number of unique values in each column of iris.

    map_int(iris, ~ length(unique(.)))
    Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
              35           23           43           22            3 

    Generate 10 random normals for each of \(\mu = -10\), \(0\), \(10\), and \(100\).

    map(c(-10, 0, 10, 100), rnorm, n = 10)
    [[1]]
     [1] -12.319979  -9.515587  -7.726388  -9.017238 -10.437171  -8.877139  -9.526525
     [8]  -9.290557  -9.901963  -9.491832
    
    [[2]]
     [1] -0.2397833  1.3283208  0.7593858  1.1392236 -1.1767585 -0.3524574 -0.7176960
     [8]  0.2637527  0.3423694  0.9854774
    
    [[3]]
     [1]  9.464330 10.941647  9.479644  9.312160  8.755509 11.193252  7.601449
     [8]  8.920083  8.864263 10.266669
    
    [[4]]
     [1]  99.48009  99.73046 100.14210  99.92110 100.07910 100.11577 101.17778
     [8] 100.83154  99.90260  99.41518

    How can you create a single vector that for each column in a data frame indicates whether or not it’s a factor?

    Use map_lgl (Apply a function to each element of a vector) with the function is.factor

    map_lgl(mtcars, is.factor)
      mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb 
    FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE 

    What happens when you use the map functions on vectors that aren’t lists? What does map(1:5, runif) do? Why? The function map applies the function to each element of the vector.

    map(1:5, runif)
    [[1]]
    [1] 0.9602394
    
    [[2]]
    [1] 0.9062845 0.3908404
    
    [[3]]
    [1] 0.7121115 0.6018702 0.2382705
    
    [[4]]
    [1] 0.4452197 0.4119878 0.5517169 0.9214921
    
    [[5]]
    [1] 0.7207585 0.2955939 0.5625326 0.9537604 0.7442990

    What does map(-2:2, rnorm, n = 5) do? Why?

    map(-2:2, rnorm, n = 5)
    [[1]]
    [1] -1.893329 -2.439700 -1.148182 -3.201482 -1.696993
    
    [[2]]
    [1]  0.0234227 -1.2800125 -0.5399861 -0.4143731 -1.0108234
    
    [[3]]
    [1] 1.0361046 1.6727639 1.8509029 0.5924434 0.3846546
    
    [[4]]
    [1] 0.3298516 0.9574716 0.9820513 2.0092424 1.0857084
    
    [[5]]
    [1] 0.7368190 4.1516435 2.0429288 3.5349912 0.9127184
    This takes samples of n = 5 from normal distributions of means -2, -1, 0, 1, and 2, and returns a list with each element a numeric vectors of length 5.

    What does map_dbl(-2:2, rnorm, n = 5) do? Why?

    map_dbl(-2:2, rnorm, n = 5)
    Error: Result 1 is not a length 1 atomic vector

    However, if we use map_dbl it throws an error. map_dbl expects the function to return a numeric vector of length one. If we wanted a numeric vector, we could use map followed by flatten_dbl

    flatten_dbl(map(-2:2, rnorm, n = 5))
     [1] -2.7346927 -3.0902229 -2.3386143 -3.1495769 -1.9779044 -2.4560725 -2.3933565
     [8] -0.8794021 -1.8144065 -1.3836109 -2.0641389 -1.2068642 -1.1286256 -1.2974258
    [15]  3.4572218  3.1948030  0.8738478  0.3903348  0.7967609  3.7033513  1.7926023
    [22]  1.4496549  0.7881413  1.4840187  2.4098862

    Rewrite map(x, function(df) lm(mpg ~ wt, data = df)) to eliminate the anonymous function.

    map(list(mtcars), ~ lm(mpg ~ wt, data = .))
    [[1]]
    
    Call:
    lm(formula = mpg ~ wt, data = .)
    
    Coefficients:
    (Intercept)           wt  
         37.285       -5.344  

    Dealing with Failure

    When you use the map functions to repeat many operations, the chances are much higher that one of those operations will fail. When this happens, you’ll get an error message, and no output. This is annoying: why does one failure prevent you from accessing all the other successes? How do you ensure that one bad apple doesn’t ruin the whole barrel?

    In this section you’ll learn how to deal this situation with a new function: safely(). safely() is an adverb: it takes a function (a verb) and returns a modified version. In this case, the modified function will never throw an error. Instead, it always returns a list with two elements:

    result is the original result. If there was an error, this will be NULL.

    error is an error object. If the operation was successful, this will be NULL

    Let’s illustrate this with a simple example: log():

    safe_log <- safely(log)
    str(safe_log(10))
    List of 2
     $ result: num 2.3
     $ error : NULL
    str(safe_log("a"))
    List of 2
     $ result: NULL
     $ error :List of 2
      ..$ message: chr "non-numeric argument to mathematical function"
      ..$ call   : language .f(...)
      ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
    When the function succeeds, the result element contains the result and the error element is NULL. When the function fails, the result element is NULL and the error element contains an error object.

    safely() is designed to work with map:

    x <- list(1, 10, "a")
    y <- x %>% map(safely(log))
    str(y)
    List of 3
     $ :List of 2
      ..$ result: num 0
      ..$ error : NULL
     $ :List of 2
      ..$ result: num 2.3
      ..$ error : NULL
     $ :List of 2
      ..$ result: NULL
      ..$ error :List of 2
      .. ..$ message: chr "non-numeric argument to mathematical function"
      .. ..$ call   : language .f(...)
      .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

    This would be easier to work with if we had two lists: one of all the errors and one of all the output. That’s easy to get with purrr::transpose():

    y <- y %>% transpose()
    str(y)
    List of 2
     $ result:List of 3
      ..$ : num 0
      ..$ : num 2.3
      ..$ : NULL
     $ error :List of 3
      ..$ : NULL
      ..$ : NULL
      ..$ :List of 2
      .. ..$ message: chr "non-numeric argument to mathematical function"
      .. ..$ call   : language .f(...)
      .. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"

    It’s up to you how to deal with the errors, but typically you’ll either look at the values of x where y is an error, or work with the values of y that are ok:

    is_ok <- y$error %>% map_lgl(is_null)
    x[!is_ok]
    [[1]]
    [1] "a"
    y$result[is_ok] %>% flatten_dbl()
    [1] 0.000000 2.302585

    Purrr provides two other useful adverbs:

    Like safely(), possibly() always succeeds. It’s simpler than safely(), because you give it a default value to return when there is an error.

    x <- list(1, 10, "a")
    x %>% map_dbl(possibly(log, NA_real_))
    [1] 0.000000 2.302585       NA

    quietly() performs a similar role to safely(), but instead of capturing errors, it captures printed output, messages, and warnings:

    x <- list(1, -1)
    x %>% map(quietly(log)) %>% str()
    List of 2
     $ :List of 4
      ..$ result  : num 0
      ..$ output  : chr ""
      ..$ warnings: chr(0) 
      ..$ messages: chr(0) 
     $ :List of 4
      ..$ result  : num NaN
      ..$ output  : chr ""
      ..$ warnings: chr "NaNs produced"
      ..$ messages: chr(0) 

    Mapping over multiple arguments

    So far we’ve mapped along a single input. But often you have multiple related inputs that you need iterate along in parallel. That’s the job of the map2() and pmap() functions. For example, imagine you want to simulate some random normals with different means. You know how to do that with map():

    mu <- list(5, 10, -3)
    mu %>% 
      map(rnorm, n = 5) %>% 
      str()
    List of 3
     $ : num [1:5] 5.79 5.33 4.87 6.87 5.43
     $ : num [1:5] 11.74 8.58 9.09 10.38 9.83
     $ : num [1:5] -2.44 -3.47 -3.42 -3.77 -2.71

    What if you also want to vary the standard deviation? One way to do that would be to iterate over the indices and index into vectors of means and sds:

    sigma <- list(1, 5, 10)
    seq_along(mu) %>% 
      map(~rnorm(5, mu[[.]], sigma[[.]])) %>% 
      str()
    List of 3
     $ : num [1:5] 4.34 6.5 4.39 5.3 4.07
     $ : num [1:5] 11.85 0.901 13.068 16.143 7.794
     $ : num [1:5] -11.01 -5.97 -4.67 -15.62 -14.86

    But that obfuscates the intent of the code. Instead we could use map2() which iterates over two vectors in parallel:

    map2(mu, sigma, rnorm, n = 5) %>% str()
    List of 3
     $ : num [1:5] 5.18 3.86 3.88 5.8 5.15
     $ : num [1:5] 11.5 1.1 8.3 10.5 15.7
     $ : num [1:5] -2.15 1.42 -8.27 -7.34 -11.78

    map2() generates this series of function calls:

    Note that the arguments that vary for each call come before the function; arguments that are the same for every call come after. Like map(), map2() is just a wrapper around a for loop:

    map2 <- function(x, y, f, ...) {
      out <- vector("list", length(x))
      for (i in seq_along(x)) {
        out[[i]] <- f(x[[i]], y[[i]], ...)
      }
      out
    }

    You could also imagine map3(), map4(), map5(), map6() etc, but that would get tedious quickly. Instead, purrr provides pmap() which takes a list of arguments. You might use that if you wanted to vary the mean, standard deviation, and number of samples:

    n <- list(1, 3, 5)
    args1 <- list(n, mu, sigma)
    args1 %>%
      pmap(rnorm) %>% 
      str()
    List of 3
     $ : num 5.47
     $ : num [1:3] 14.45 1.39 5.61
     $ : num [1:5] -9.07 -17.17 -1.43 9.34 -3.74

    That looks like:

    If you don’t name the elements of list, pmap() will use positional matching when calling the function. That’s a little fragile, and makes the code harder to read, so it’s better to name the arguments:

    args2 <- list(mean = mu, sd = sigma, n = n)
    args2 %>% 
      pmap(rnorm) %>% 
      str()
    List of 3
     $ : num 4.36
     $ : num [1:3] 19.1 3.25 14
     $ : num [1:5] 1.57 9.64 24.07 3.58 -5.16

    That generates longer, but safer, calls:

    Since the arguments are all the same length, it makes sense to store them in a data frame:

    params <- tribble(
      ~mean, ~sd, ~n,
        5,     1,  1,
       10,     5,  3,
       -3,    10,  5
    )
    params %>% 
      pmap(rnorm)
    [[1]]
    [1] 7.035716
    
    [[2]]
    [1]  1.485603  6.994151 15.032192
    
    [[3]]
    [1] -14.558272 -13.772941 -18.629398   2.936900  -9.220714

    Invoking Different functions

    There’s one more step up in complexity - as well as varying the arguments to the function you might also vary the function itself:

    f <- c("runif", "rnorm", "rpois")
    param <- list(
      list(min = -1, max = 1), 
      list(sd = 5), 
      list(lambda = 10)
    )

    To handle this case, you can use invoke_map():

    invoke_map(f, param, n = 5) %>% str()
    List of 3
     $ : num [1:5] -0.0417 -0.7741 -0.5943 -0.3098 0.7438
     $ : num [1:5] -2.36 -6.97 -9.15 7.47 -12.81
     $ : int [1:5] 14 15 10 4 7

    The first argument is a list of functions or character vector of function names. The second argument is a list of lists giving the arguments that vary for each function. The subsequent arguments are passed on to every function.

    And again, you can use tribble() to make creating these matching pairs a little easier:

    LS0tDQp0aXRsZTogIlIgZm9yIERhdGEgU2NpZW5jZSBDaGFwdGVyIDE3Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KPGgxPiBJdGVyYXRpb24gd2l0aCBQdXJyciA8L2gxPg0KDQpJbiBmdW5jdGlvbnMsIHdlIHRhbGtlZCBhYm91dCBob3cgaW1wb3J0YW50IGl0IGlzIHRvIHJlZHVjZSBkdXBsaWNhdGlvbiBpbiB5b3VyIGNvZGUgYnkgY3JlYXRpbmcgZnVuY3Rpb25zIGluc3RlYWQgb2YgY29weWluZy1hbmQtcGFzdGluZy4gUmVkdWNpbmcgY29kZSBkdXBsaWNhdGlvbiBoYXMgdGhyZWUgbWFpbiBiZW5lZml0czogPC9icj4NCg0KSXQncyBlYXNpZXIgdG8gc2VlIHRoZSBpbnRlbnQgb2YgeW91ciBjb2RlLCBiZWNhdXNlIHlvdXIgZXllcyBhcmUgZHJhd24gdG8gd2hhdCdzIGRpZmZlcmVudCwgbm90IHdoYXQgc3RheXMgdGhlIHNhbWUuPC9icj4NCg0KSXQncyBlYXNpZXIgdG8gcmVzcG9uZCB0byBjaGFuZ2VzIGluIHJlcXVpcmVtZW50cy4gQXMgeW91ciBuZWVkcyBjaGFuZ2UsIHlvdSBvbmx5IG5lZWQgdG8gbWFrZSBjaGFuZ2VzIGluIG9uZSBwbGFjZSwgcmF0aGVyIHRoYW4gcmVtZW1iZXJpbmcgdG8gY2hhbmdlIGV2ZXJ5IHBsYWNlIHRoYXQgeW91IGNvcGllZC1hbmQtcGFzdGVkIHRoZSBjb2RlLjwvYnI+DQoNCllvdSdyZSBsaWtlbHkgdG8gaGF2ZSBmZXdlciBidWdzIGJlY2F1c2UgZWFjaCBsaW5lIG9mIGNvZGUgaXMgdXNlZCBpbiBtb3JlIHBsYWNlcy48L3A+DQoNCkFub3RoZXIgdG9vbCBmb3IgcmVkdWNpbmcgZHVwbGljYXRpb24gaXMgaXRlcmF0aW9uLCB3aGljaCBoZWxwcyB5b3Ugd2hlbiB5b3UgbmVlZCB0byBkbyB0aGUgc2FtZSB0aGluZyB0byBtdWx0aXBsZSBpbnB1dHM6IHJlcGVhdGluZyB0aGUgc2FtZSBvcGVyYXRpb24gb24gZGlmZmVyZW50IGNvbHVtbnMsIG9yIG9uIGRpZmZlcmVudCBkYXRhc2V0cy4gSW4gdGhpcyBjaGFwdGVyIHlvdSdsbCBsZWFybiBhYm91dCB0d28gaW1wb3J0YW50IGl0ZXJhdGlvbiBwYXJhZGlnbXM6IGltcGVyYXRpdmUgcHJvZ3JhbW1pbmcgYW5kIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcuIE4vcD4NCg0KT24gdGhlIGltcGVyYXRpdmUgc2lkZSB5b3UgaGF2ZSB0b29scyBsaWtlIGZvciBsb29wcyBhbmQgd2hpbGUgbG9vcHMsIHdoaWNoIGFyZSBhIGdyZWF0IHBsYWNlIHRvIHN0YXJ0IGJlY2F1c2UgdGhleSBtYWtlIGl0ZXJhdGlvbiB2ZXJ5IGV4cGxpY2l0LCBzbyBpdCdzIG9idmlvdXMgd2hhdCdzIGhhcHBlbmluZy4gSG93ZXZlciwgZm9yIGxvb3BzIGFyZSBxdWl0ZSB2ZXJib3NlLCBhbmQgcmVxdWlyZSBxdWl0ZSBhIGJpdCBvZiBib29ra2VlcGluZyBjb2RlIHRoYXQgaXMgZHVwbGljYXRlZCBmb3IgZXZlcnkgZm9yIGxvb3AuPC9wPg0KDQpGdW5jdGlvbmFsIHByb2dyYW1taW5nIChGUCkgb2ZmZXJzIHRvb2xzIHRvIGV4dHJhY3Qgb3V0IHRoaXMgZHVwbGljYXRlZCBjb2RlLCBzbyBlYWNoIGNvbW1vbiBmb3IgbG9vcCBwYXR0ZXJuIGdldHMgaXRzIG93biBmdW5jdGlvbi4gT25jZSB5b3UgbWFzdGVyIHRoZSB2b2NhYnVsYXJ5IG9mIEZQLCB5b3UgY2FuIHNvbHZlIG1hbnkgY29tbW9uIGl0ZXJhdGlvbiBwcm9ibGVtcyB3aXRoIGxlc3MgY29kZSwgbW9yZSBlYXNlLCBhbmQgZmV3ZXIgZXJyb3JzIDwvcD4NCg0KPGgyPiBQcmVyZXF1aXNpdGVzIDwvaDI+DQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCg0KPGgyPiBmb3IgTG9vcHMgPC9oMj4NCg0KYGBge3J9DQpkZiA8LSB0aWJibGUoDQogIGEgPSBybm9ybSgxMCksDQogIGIgPSBybm9ybSgxMCksDQogIGMgPSBybm9ybSgxMCksDQogIGQgPSBybm9ybSgxMCkNCikNCmRmDQpgYGANCg0KV2Ugd2FudCB0byBjb21wdXRlIHRoZSBtZWRpYW4gb2YgZWFjaCBjb2x1bW4uIFlvdSBjb3VsZCBkbyB3aXRoIGNvcHktYW5kLXBhc3RlOg0KDQpgYGB7cn0NCm1lZGlhbihkZiRhKQ0KDQptZWRpYW4oZGYkYikNCg0KbWVkaWFuKGRmJGMpDQoNCm1lZGlhbihkZiRkKQ0KDQoNCmBgYA0KQnV0IHRoYXQgYnJlYWtzIG91ciBydWxlIG9mIHRodW1iOiBuZXZlciBjb3B5IGFuZCBwYXN0ZSBtb3JlIHRoYW4gdHdpY2UuIEluc3RlYWQsIHdlIGNvdWxkIHVzZSBhIGZvciBsb29wOg0KDQpgYGB7cn0NCm91dHB1dCA8LSB2ZWN0b3IoImRvdWJsZSIsIG5jb2woZGYpKQ0KZm9yIChpIGluIHNlcV9hbG9uZyhkZikpIHsgICAgICAgICAgICANCiAgb3V0cHV0W1tpXV0gPC0gbWVkaWFuKGRmW1tpXV0pICAgICAgDQp9DQpvdXRwdXQNCmBgYA0KDQpFdmVyeSBmb3IgbG9vcCBoYXMgdGhyZWUgY29tcG9uZW50czogPC9icj4NCg0KVGhlIG91dHB1dDogb3V0cHV0IDwtIHZlY3RvcigiZG91YmxlIiwgbGVuZ3RoKHgpKS4gQmVmb3JlIHlvdSBzdGFydCB0aGUgbG9vcCwgeW91IG11c3QgYWx3YXlzIGFsbG9jYXRlIHN1ZmZpY2llbnQgc3BhY2UgZm9yIHRoZSBvdXRwdXQuIFRoaXMgaXMgdmVyeSBpbXBvcnRhbnQgZm9yIGVmZmljaWVuY3k6IGlmIHlvdSBncm93IHRoZSBbZm9yIGxvb3BdIGF0IGVhY2ggaXRlcmF0aW9uIHVzaW5nIGMoKSAoZm9yIGV4YW1wbGUpLCB5b3VyIGZvciBsb29wIHdpbGwgYmUgdmVyeSBzbG93LiA8L3A+DQoNCkEgZ2VuZXJhbCB3YXkgb2YgY3JlYXRpbmcgYW4gZW1wdHkgdmVjdG9yIG9mIGdpdmVuIGxlbmd0aCBpcyB0aGUgdmVjdG9yKCkgZnVuY3Rpb24uIEl0IGhhcyB0d28gYXJndW1lbnRzOiB0aGUgdHlwZSBvZiB0aGUgdmVjdG9yICgibG9naWNhbCIsICJpbnRlZ2VyIiwgImRvdWJsZSIsICJjaGFyYWN0ZXIiLCBldGMpIGFuZCB0aGUgbGVuZ3RoIG9mIHRoZSB2ZWN0b3IuIDwvcD4NCg0KVGhlIHNlcXVlbmNlOiBpIGluIHNlcV9hbG9uZyhkZikuIFRoaXMgZGV0ZXJtaW5lcyB3aGF0IHRvIGxvb3Agb3ZlcjogZWFjaCBydW4gb2YgdGhlIGZvciBsb29wIHdpbGwgYXNzaWduIGkgdG8gYSBkaWZmZXJlbnQgdmFsdWUgZnJvbSBzZXFfYWxvbmcoZGYpLiBJdCdzIHVzZWZ1bCB0byB0aGluayBvZiBpIGFzIGEgcHJvbm91biwgbGlrZSAiaXQiLiA8L3A+DQoNCllvdSBtaWdodCBub3QgaGF2ZSBzZWVuIHNlcV9hbG9uZygpIGJlZm9yZS4gSXQncyBhIHNhZmUgdmVyc2lvbiBvZiB0aGUgZmFtaWxpYXIgMTpsZW5ndGgobCksIHdpdGggYW4gaW1wb3J0YW50IGRpZmZlcmVuY2U6IGlmIHlvdSBoYXZlIGEgemVyby1sZW5ndGggdmVjdG9yLCBzZXFfYWxvbmcoKSBkb2VzIHRoZSByaWdodCB0aGluZzogDQpgYGB7cn0NCnkgPC0gdmVjdG9yKCJkb3VibGUiLCAwKQ0Kc2VxX2Fsb25nKHkpDQoxOmxlbmd0aCh5KQ0KYGBgDQpZb3UgcHJvYmFibHkgd29uJ3QgY3JlYXRlIGEgemVyby1sZW5ndGggdmVjdG9yIGRlbGliZXJhdGVseSwgYnV0IGl0J3MgZWFzeSB0byBjcmVhdGUgdGhlbSBhY2NpZGVudGFsbHkuIElmIHlvdSB1c2UgMTpsZW5ndGgoeCkgaW5zdGVhZCBvZiBzZXFfYWxvbmcoeCksIHlvdSdyZSBsaWtlbHkgdG8gZ2V0IGEgY29uZnVzaW5nIGVycm9yIG1lc3NhZ2UuDQoNClRoZSBib2R5OiBvdXRwdXRbW2ldXSA8LSBtZWRpYW4oZGZbW2ldXSkuIFRoaXMgaXMgdGhlIGNvZGUgdGhhdCBkb2VzIHRoZSB3b3JrLiBJdCdzIHJ1biByZXBlYXRlZGx5LCBlYWNoIHRpbWUgd2l0aCBhIGRpZmZlcmVudCB2YWx1ZSBmb3IgaS4gVGhlIGZpcnN0IGl0ZXJhdGlvbiB3aWxsIHJ1biBvdXRwdXRbWzFdXSA8LSBtZWRpYW4oZGZbWzFdXSksIHRoZSBzZWNvbmQgd2lsbCBydW4gb3V0cHV0W1syXV0gPC0gbWVkaWFuKGRmW1syXV0pLCBhbmQgc28gb24uDQo8aDM+IEV4ZXJjaXNlcyA8L2gzPg0KDQpXcml0ZSBmb3IgbG9vcHMgdG86DQoNCkNvbXB1dGUgdGhlIG1lYW4gb2YgZXZlcnkgY29sdW1uIGluIG10Y2Fycy4NCmBgYHtyfQ0Kb3V0cHV0IDwtICB2ZWN0b3IoImRvdWJsZSIsIG5jb2wobXRjYXJzKSkNCm5hbWVzKG91dHB1dCkgPC0gIG5hbWVzKG10Y2FycykNCmZvciAoaSBpbiBuYW1lcyhtdGNhcnMpKSB7DQogIG91dHB1dFtbMV1dIDwtICBtZWFuKG10Y2Fyc1tbaV1dKQ0KfQ0Kb3V0cHV0DQpgYGANCg0KRGV0ZXJtaW5lIHRoZSB0eXBlIG9mIGVhY2ggY29sdW1uIGluIG55Y2ZsaWdodHMxMzo6ZmxpZ2h0cy4NCg0KDQpgYGB7cn0NCmRhdGEoImZsaWdodHMiLCBwYWNrYWdlID0gIm55Y2ZsaWdodHMxMyIpDQpvdXRwdXQgPC0gIHZlY3RvcigibGlzdCIsIG5jb2woZmxpZ2h0cykpDQpuYW1lcyhvdXRwdXQpIDwtICBuYW1lcyhmbGlnaHRzKQ0KZm9yIChpIGluIG5hbWVzKGZsaWdodHMpKXsNCiAgb3V0cHV0W1tpXV0gPC0gIGNsYXNzKGZsaWdodHNbW2ldXSkNCn0NCm91dHB1dA0KYGBgDQoNCkNvbXB1dGUgdGhlIG51bWJlciBvZiB1bmlxdWUgdmFsdWVzIGluIGVhY2ggY29sdW1uIG9mIGlyaXMuDQoNCmBgYHtyfQ0KZGF0YShpcmlzKQ0KaXJpc191bmlxIDwtIHZlY3RvcigiZG91YmxlIiwgbmNvbChpcmlzKSkNCm5hbWVzKGlyaXNfdW5pcSkgPC0gbmFtZXMoaXJpcykNCmZvciAoaSBpbiBuYW1lcyhpcmlzKSkgew0KICBpcmlzX3VuaXFbaV0gPC0gbGVuZ3RoKHVuaXF1ZShpcmlzW1tpXV0pKQ0KfQ0KaXJpc191bmlxDQpgYGANCg0KDQpHZW5lcmF0ZSAxMCByYW5kb20gbm9ybWFscyBmb3IgZWFjaCBvZiAgDQo/Pz0/Pz8xMCAsICAwICwgMTAgYW5kICAxMDANClRoaW5rIGFib3V0IHRoZSBvdXRwdXQsIHNlcXVlbmNlLCBhbmQgYm9keSBiZWZvcmUgeW91IHN0YXJ0IHdyaXRpbmcgdGhlIGxvb3AuDQoNCmBgYHtyfQ0KIyBudW1iZXIgdG8gZHJhdw0KbiA8LSAxMA0KIyB2YWx1ZXMgb2YgdGhlIG1lYW4NCm11IDwtIGMoLTEwLCAwLCAxMCwgMTAwKQ0Kbm9ybWFscyA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGgobXUpKQ0KZm9yIChpIGluIHNlcV9hbG9uZyhub3JtYWxzKSkgew0KICBub3JtYWxzW1tpXV0gPC0gcm5vcm0obiwgbWVhbiA9IG11W2ldKQ0KfQ0Kbm9ybWFscw0KYGBgDQoNCkVsaW1pbmF0ZSB0aGUgZm9yIGxvb3AgaW4gZWFjaCBvZiB0aGUgZm9sbG93aW5nIGV4YW1wbGVzIGJ5IHRha2luZyBhZHZhbnRhZ2Ugb2YgYW4gZXhpc3RpbmcgZnVuY3Rpb24gdGhhdCB3b3JrcyB3aXRoIHZlY3RvcnM6DQoNCmBgYHtyfQ0Kb3V0IDwtICIiDQpsZXR0ZXJzDQpmb3IgKHggaW4gbGV0dGVycykgew0KICBvdXQgPC0gc3RyaW5ncjo6c3RyX2Mob3V0LCB4KQ0KfQ0Kb3V0DQpgYGANCg0KU29sdXRpb246IHN0cl9jIGFscmVhZHkgd29ya3Mgd2l0aCB2ZWN0b3JzLCBzbyBzaW1wbHkgdXNlIHN0cl9jIHdpdGggdGhlIGNvbGxhcHNlIGFyZ3VtZW50IHRvIHJldHVybiBhIHNpbmdsZSBzdHJpbmcuDQoNCmBgYHtyfQ0Kc3RyaW5ncjo6c3RyX2MobGV0dGVycywgY29sbGFwc2UgPSAiIikNCiM+IFsxXSAiYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoiDQpgYGANCg0KV3JpdGUgYSBmb3IgbG9vcCB0aGF0IGBwcmludHMoKWAgdGhlIGx5cmljcyB0byB0aGUgY2hpbGRyZW4ncyBzb25nIA0KICAgIkFsaWNlIHRoZSBjYW1lbCIuIDwvcD4NCg0KDQogICANCmBgYHtyfQ0KaHVtcHMgPC0gYygiZml2ZSIsICJmb3VyIiwgInRocmVlIiwgInR3byIsICJvbmUiLCAibm8iKQ0KZm9yIChpIGluIGh1bXBzKSB7DQogIGNhdChzdHJpbmdyOjpzdHJfYygiQWxpY2UgdGhlIGNhbWVsIGhhcyAiLCByZXAoaSwgMyksICIgaHVtcHMuIiwNCiAgICAgICAgICAgICBjb2xsYXBzZSA9ICJcbiIpLCAiXG4iKQ0KICBpZiAoaSA9PSAibm8iKSB7DQogICAgY2F0KCJOb3cgQWxpY2UgaXMgYSBob3JzZS5cbiIpDQogIH0gZWxzZSB7DQogICAgY2F0KCJTbyBnbywgQWxpY2UsIGdvLlxuIikNCiAgfQ0KICBjYXQoIlxuIikNCn0NCmBgYA0KDQpDb252ZXJ0IHRoZSBudXJzZXJ5IHJoeW1lICJ0ZW4gaW4gdGhlIGJlZCIgdG8gYSBmdW5jdGlvbi4gR2VuZXJhbGlzZSANCiAgIGl0IHRvIGFueSBudW1iZXIgb2YgcGVvcGxlIGluIGFueSBzbGVlcGluZyBzdHJ1Y3R1cmUuIDwvcD4NCiAgIA0KICAgDQpgYGB7cn0NCm51bWJlcnMgPC0gYygidGVuIiwgIm5pbmUiLCAiZWlnaHQiLCAic2V2ZW4iLCAic2l4IiwgImZpdmUiLA0KICAgICAgICAgICAgICJmb3VyIiwgInRocmVlIiwgInR3byIsICJvbmUiKQ0KZm9yIChpIGluIG51bWJlcnMpIHsNCiAgY2F0KHN0cmluZ3I6OnN0cl9jKCJUaGVyZSB3ZXJlICIsIGksICIgaW4gdGhlIGJlZFxuIikpDQogIGNhdCgiYW5kIHRoZSBsaXR0bGUgb25lIHNhaWRcbiIpDQogIGlmIChpID09ICJvbmUiKSB7DQogICAgY2F0KCJJJ20gbG9uZWx5Li4uIikNCiAgICB9IGVsc2Ugew0KICAgIGNhdCgiUm9sbCBvdmVyLCByb2xsIG92ZXJcbiIpDQogICAgY2F0KCJTbyB0aGV5IGFsbCByb2xsZWQgb3ZlciBhbmQgb25lIGZlbGwgb3V0LlxuIikNCiAgICAgIH0NCiAgICAgIGNhdCgiXG4iKQ0KICB9DQpgYGANCg0KQ29udmVydCB0aGUgc29uZyAiOTkgYm90dGxlcyBvZiBiZWVyIG9uIHRoZSB3YWxsIiB0byBhIGZ1bmN0aW9uLiBHZW5lcmFsaXNlIHRvIGFueSBudW1iZXIgb2YgYW55IHZlc3NlbCBjb250YWluaW5nIGFueSBsaXF1aWQgb24gYW55IHN1cmZhY2UuIDwvcD4NCmBgYHtyfQ0KYm90dGxlcyA8LSBmdW5jdGlvbihpKSB7DQogIGlmIChpID4gMikgew0KICAgYm90dGxlcyA8LSBzdHJpbmdyOjpzdHJfYyhpIC0gMSwgIiBib3R0bGVzIikNCiAgfSBlbHNlIGlmIChpID09IDIpIHsNCiAgIGJvdHRsZXMgPC0gc3RyaW5ncjo6c3RyX2MoMSwiIGJvdHRsZXMiKQ0KICB9IGVsc2Ugew0KICAgYm90dGxlcyA8LSBzdHJpbmdyOjpzdHJfYygiTm8gbW9yZSBib3R0bGVzIikNCiAgfQ0KICBib3R0bGVzDQp9DQoNCmJlZXJfYm90dGxlcyA8LSBmdW5jdGlvbihuKSB7DQogICMgc2hvdWxkIHRlc3Qgd2hldGhlciBuID49IDEuDQogIGZvciAoaSBpbiBzZXEobiwgMSkpIHsNCiAgICAgY2F0KHN0cmluZ3I6OnN0cl9jKGJvdHRsZXMoaSksICIgb2YgYmVlciBvbiB0aGUgd2FsbCwgIiwgYm90dGxlcyhpKSwgIiBvZiBiZWVyLlxuIikpDQogICAgIGNhdChzdHJpbmdyOjpzdHJfYygiVGFrZSBvbmUgZG93biBhbmQgcGFzcyBpdCBhcm91bmQsICIsIGJvdHRsZXMoaSAtIDEpLA0KICAgICAgICAgICAgICAgICIgb2YgYmVlciBvbiB0aGUgd2FsbC5cblxuIikpDQogIH0NCiAgY2F0KCJObyBtb3JlIGJvdHRsZXMgb2YgYmVlciBvbiB0aGUgd2FsbCwgbm8gbW9yZSBib3R0bGVzIG9mIGJlZXIuXG4iKQ0KICBjYXQoc3RyaW5ncjo6c3RyX2MoIkdvIHRvIHRoZSBzdG9yZSBhbmQgYnV5IHNvbWUgbW9yZSwgIiwgYm90dGxlcyhuKSwgIiBvZiBiZWVyIG9uIHRoZSB3YWxsLlxuIikpDQp9DQpiZWVyX2JvdHRsZXMoNCkNCg0KYGBgDQoNCkl0J3MgY29tbW9uIHRvIHNlZSBmb3IgbG9vcHMgdGhhdCBkb24ndCBwcmVhbGxvY2F0ZSB0aGUgb3V0cHV0IGFuZCBpbnN0ZWFkIGluY3JlYXNlIHRoZSBsZW5ndGggb2YgYSB2ZWN0b3IgYXQgZWFjaCBzdGVwOg0KDQpgYGB7cn0NCm91dHB1dCA8LSB2ZWN0b3IoImludGVnZXIiLCAwKQ0KZm9yIChpIGluIHNlcV9hbG9uZyh4KSkgew0KICBvdXRwdXQgPC0gYyhvdXRwdXQsIGxlbmd0aHMoeFtbaV1dKSkNCn0NCm91dHB1dA0KDQpgYGANCg0KSSdsbCB1c2UgdGhlIHBhY2thZ2UgbWljcm9iZW5jaG1hcmsgdG8gdGltZSB0aGlzLiBNaWNyb2JlbmNobWFyayB3aWxsIHJ1biBhbiBSIGV4cHJlc3Npb24gYSBudW1iZXIgb2YgdGltZXMgYW5kIHRpbWUgaXQuDQoNCkRlZmluZSBhIGZ1bmN0aW9uIHRoYXQgYXBwZW5kcyB0byBhbiBpbnRlZ2VyIHZlY3Rvci4NCg0KYGBge3J9DQpsaWJyYXJ5KG1pY3JvYmVuY2htYXJrKQ0KYWRkX3RvX3ZlY3RvciA8LSBmdW5jdGlvbihuKSB7DQogIG91dHB1dCA8LSB2ZWN0b3IoImludGVnZXIiLCAwKQ0KICBmb3IgKGkgaW4gc2VxX2xlbihuKSkgew0KICAgIG91dHB1dCA8LSBjKG91dHB1dCwgaSkNCiAgfQ0KICBvdXRwdXQgIA0KfQ0KbWljcm9iZW5jaG1hcmsoYWRkX3RvX3ZlY3RvcigxMDAwMCksIHRpbWVzID0gMykNCmBgYA0KQW5kIG9uZSB0aGF0IHByZS1hbGxvY2F0ZXMgaXQuDQoNCmBgYHtyfQ0KDQphZGRfdG9fdmVjdG9yXzIgPC0gZnVuY3Rpb24obikgew0KICBvdXRwdXQgPC0gdmVjdG9yKCJpbnRlZ2VyIiwgbikNCiAgZm9yIChpIGluIHNlcV9sZW4obikpIHsNCiAgICBvdXRwdXRbW2ldXSA8LSBpDQogIH0NCiAgb3V0cHV0DQp9DQptaWNyb2JlbmNobWFyayhhZGRfdG9fdmVjdG9yXzIoMTAwMDApLCB0aW1lcyA9IDMpDQoNCg0KYGBgDQoNClRoZSBwcmUtYWxsb2NhdGVkIHZlY3RvciBpcyBhYm91dCAxMDAgdGltZXMgZmFzdGVyISBZTU1WLCBidXQgdGhlIGxvbmdlciB0aGUgdmVjdG9yIGFuZCB0aGUgYmlnZ2VyIHRoZSBvYmplY3RzLCB0aGUgbW9yZSB0aGF0IHByZS1hbGxvY2F0aW9uIHdpbGwgb3V0cGVyZm9ybSBhcHBlbmRpbmcuDQoNCg0KPGgyPiBGb3IgTG9vcCBWYXJpYXRpb25zPC9oMj4NCg0KT25jZSB5b3UgaGF2ZSB0aGUgYmFzaWMgZm9yIGxvb3AgdW5kZXIgeW91ciBiZWx0LCB0aGVyZSBhcmUgc29tZSB2YXJpYXRpb25zIHRoYXQgeW91IHNob3VsZCBiZSBhd2FyZSBvZi4gVGhlc2UgdmFyaWF0aW9ucyBhcmUgaW1wb3J0YW50IHJlZ2FyZGxlc3Mgb2YgaG93IHlvdSBkbyBpdGVyYXRpb24sIHNvIGRvbid0IGZvcmdldCBhYm91dCB0aGVtIG9uY2UgeW91J3ZlIG1hc3RlciB0aGUgRlAgdGVjaG5pcXVlcyB5b3UnbGwgbGVhcm4gYWJvdXQgaW4gdGhlIG5leHQgc2VjdGlvbi4gPC9wPg0KDQpUaGVyZSBhcmUgZm91ciB2YXJpYXRpb25zIG9uIHRoZSBiYXNpYyB0aGVtZSBvZiB0aGUgZm9yIGxvb3A6IDwvYnI+DQoNCjxsaT5Nb2RpZnlpbmcgYW4gZXhpc3Rpbmcgb2JqZWN0LCBpbnN0ZWFkIG9mIGNyZWF0aW5nIGEgbmV3IG9iamVjdC4gPC9saT4NCjxsaT5Mb29waW5nIG92ZXIgbmFtZXMgb3IgdmFsdWVzLCBpbnN0ZWFkIG9mIGluZGljZXMuPC9saT4NCjxsaT5IYW5kbGluZyBvdXRwdXRzIG9mIHVua25vd24gbGVuZ3RoLjwvbGk+DQo8bGk+SGFuZGxpbmcgc2VxdWVuY2VzIG9mIHVua25vd24gbGVuZ3RoLjwvcD4NCg0KPGgzPiBNb2RpZnlpbmcgYW4gZXhpc3Rpbmcgb2JqZWN0IDwvaDM+DQpTb21ldGltZXMgeW91IHdhbnQgdG8gdXNlIGEgZm9yIGxvb3AgdG8gbW9kaWZ5IGFuIGV4aXN0aW5nIG9iamVjdC4gRm9yIGV4YW1wbGUsIHJlbWVtYmVyIG91ciBjaGFsbGVuZ2UgZnJvbSBmdW5jdGlvbnMuIFdlIHdhbnRlZCB0byByZXNjYWxlIGV2ZXJ5IGNvbHVtbiBpbiBhIGRhdGEgZnJhbWU6DQoNCmBgYHtyfQ0KZGYgPC0gdGliYmxlKA0KICBhID0gcm5vcm0oMTApLA0KICBiID0gcm5vcm0oMTApLA0KICBjID0gcm5vcm0oMTApLA0KICBkID0gcm5vcm0oMTApDQopDQpyZXNjYWxlMDEgPC0gZnVuY3Rpb24oeCkgew0KICBybmcgPC0gcmFuZ2UoeCwgbmEucm0gPSBUUlVFKQ0KICAoeCAtIHJuZ1sxXSkgLyAocm5nWzJdIC0gcm5nWzFdKQ0KfQ0KDQpkZiRhIDwtIHJlc2NhbGUwMShkZiRhKQ0KZGYkYiA8LSByZXNjYWxlMDEoZGYkYikNCmRmJGMgPC0gcmVzY2FsZTAxKGRmJGMpDQpkZiRkIDwtIHJlc2NhbGUwMShkZiRkKQ0KDQpgYGANClRvIHNvbHZlIHRoaXMgd2l0aCBhIGZvciBsb29wIHdlIGFnYWluIHRoaW5rIGFib3V0IHRoZSB0aHJlZSBjb21wb25lbnRzOiA8L3A+DQoNCk91dHB1dDogd2UgYWxyZWFkeSBoYXZlIHRoZSBvdXRwdXQgLSBpdCdzIHRoZSBzYW1lIGFzIHRoZSBpbnB1dCEgPC9icj4NClNlcXVlbmNlOiB3ZSBjYW4gdGhpbmsgYWJvdXQgYSBkYXRhIGZyYW1lIGFzIGEgbGlzdCBvZiBjb2x1bW5zLCBzbyB3ZSBjYW4gaXRlcmF0ZSBvdmVyIGVhY2ggY29sdW1uIHdpdGggc2VxX2Fsb25nKGRmKS4gPC9icj4NCkJvZHk6IGFwcGx5IHJlc2NhbGUwMSgpLiA8L3A+DQoNClRoaXMgZ2l2ZXMgdXM6DQpgYGB7cn0NCg0KZm9yIChpIGluIHNlcV9hbG9uZyhkZikpIHsNCiAgZGZbW2ldXSA8LSByZXNjYWxlMDEoZGZbW2ldXSkNCn0NCg0KZGYNCmBgYA0KDQpUeXBpY2FsbHkgeW91J2xsIGJlIG1vZGlmeWluZyBhIGxpc3Qgb3IgZGF0YSBmcmFtZSB3aXRoIHRoaXMgc29ydCBvZiBsb29wLCBzbyByZW1lbWJlciB0byB1c2UgW1ssIG5vdCBbLiBZb3UgbWlnaHQgaGF2ZSBzcG90dGVkIHRoYXQgSSB1c2VkIFtbIGluIGFsbCBteSBmb3IgbG9vcHM6IEkgdGhpbmsgaXQncyBiZXR0ZXIgdG8gdXNlIFtbIGV2ZW4gZm9yIGF0b21pYyB2ZWN0b3JzIGJlY2F1c2UgaXQgbWFrZXMgaXQgY2xlYXIgdGhhdCBJIHdhbnQgdG8gd29yayB3aXRoIGEgc2luZ2xlIGVsZW1lbnQuIDwvcD4NCg0KPGgzPiBMb29waW5nIFBhdHRlcm5zIDwvaDM+DQpUaGVyZSBhcmUgdGhyZWUgYmFzaWMgd2F5cyB0byBsb29wIG92ZXIgYSB2ZWN0b3IuIFNvIGZhciBJJ3ZlIHNob3duIHlvdSB0aGUgbW9zdCBnZW5lcmFsOiBsb29waW5nIG92ZXIgdGhlIG51bWVyaWMgaW5kaWNlcyB3aXRoIGZvciAoaSBpbiBzZXFfYWxvbmcoeHMpKSwgYW5kIGV4dHJhY3RpbmcgdGhlIHZhbHVlIHdpdGggeFtbaV1dLiBUaGVyZSBhcmUgdHdvIG90aGVyIGZvcm1zOiA8L3A+DQoNCkxvb3Agb3ZlciB0aGUgZWxlbWVudHM6IGZvciAoeCBpbiB4cykuIFRoaXMgaXMgbW9zdCB1c2VmdWwgaWYgeW91IG9ubHkgY2FyZSBhYm91dCBzaWRlLWVmZmVjdHMsIGxpa2UgcGxvdHRpbmcgb3Igc2F2aW5nIGEgZmlsZSwgYmVjYXVzZSBpdCdzIGRpZmZpY3VsdCB0byBzYXZlIHRoZSBvdXRwdXQgZWZmaWNpZW50bHkuIDwvcD4NCg0KTG9vcCBvdmVyIHRoZSBuYW1lczogZm9yIChubSBpbiBuYW1lcyh4cykpLiBUaGlzIGdpdmVzIHlvdSBuYW1lLCB3aGljaCB5b3UgY2FuIHVzZSB0byBhY2Nlc3MgdGhlIHZhbHVlIHdpdGggeFtbbm1dXS4gVGhpcyBpcyB1c2VmdWwgaWYgeW91IHdhbnQgdG8gdXNlIHRoZSBuYW1lIGluIGEgcGxvdCB0aXRsZSBvciBhIGZpbGUgbmFtZS4gSWYgeW91J3JlIGNyZWF0aW5nIG5hbWVkIG91dHB1dCwgbWFrZSBzdXJlIHRvIG5hbWUgdGhlIHJlc3VsdHMgdmVjdG9yIGxpa2Ugc286IA0KDQpgYGB7cn0NCnJlc3VsdHMgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoKHgpKQ0KbmFtZXMocmVzdWx0cykgPC0gbmFtZXMoeCkNCm5hbWVzDQpgYGANCg0KPGgzPiBVbmtub3duIE91dHB1dCBMZW5ndGggPC9oMz4NCg0KU29tZXRpbWVzIHlvdSBtaWdodCBub3Qga25vdyBob3cgbG9uZyB0aGUgb3V0cHV0IHdpbGwgYmUuIEZvciBleGFtcGxlLCBpbWFnaW5lIHlvdSB3YW50IHRvIHNpbXVsYXRlIHNvbWUgcmFuZG9tIHZlY3RvcnMgb2YgcmFuZG9tIGxlbmd0aHMuIFlvdSBtaWdodCBiZSB0ZW1wdGVkIHRvIHNvbHZlIHRoaXMgcHJvYmxlbSBieSBwcm9ncmVzc2l2ZWx5IGdyb3dpbmcgdGhlIHZlY3RvcjoNCmBgYHtyfQ0KbWVhbnMgPC0gYygwLCAxLCAyKQ0KDQpvdXRwdXQgPC0gZG91YmxlKCkNCmZvciAoaSBpbiBzZXFfYWxvbmcobWVhbnMpKSB7DQogIG4gPC0gc2FtcGxlKDEwMCwgMSkNCiAgb3V0cHV0IDwtIGMob3V0cHV0LCBybm9ybShuLCBtZWFuc1tbaV1dKSkNCn0NCnN0cihvdXRwdXQpDQoNCg0KYGBgDQpCdXQgdGhpcyBpcyBub3QgdmVyeSBlZmZpY2llbnQgYmVjYXVzZSBpbiBlYWNoIGl0ZXJhdGlvbiwgUiBoYXMgdG8gY29weSBhbGwgdGhlIGRhdGEgZnJvbSB0aGUgcHJldmlvdXMgaXRlcmF0aW9ucy4gSW4gdGVjaG5pY2FsIHRlcm1zIHlvdSBnZXQgInF1YWRyYXRpYyIgKCBPKG4yKSApIGJlaGF2aW91ciB3aGljaCBtZWFucyB0aGF0IGEgbG9vcCB3aXRoIHRocmVlIHRpbWVzIGFzIG1hbnkgZWxlbWVudHMgd291bGQgdGFrZSBuaW5lIHRpbWVzIGFzIGxvbmcgdG8gcnVuLiA8L3A+DQoNCkEgYmV0dGVyIHNvbHV0aW9uIHRvIHNhdmUgdGhlIHJlc3VsdHMgaW4gYSBsaXN0LCBhbmQgdGhlbiBjb21iaW5lIGludG8gYSBzaW5nbGUgdmVjdG9yIGFmdGVyIHRoZSBsb29wIGlzIGRvbmU6DQoNCmBgYHtyfQ0Kb3V0IDwtIHZlY3RvcigibGlzdCIsIGxlbmd0aChtZWFucykpDQpmb3IgKGkgaW4gc2VxX2Fsb25nKG1lYW5zKSkgew0KICBuIDwtIHNhbXBsZSgxMDAsIDEpDQogIG91dFtbaV1dIDwtIHJub3JtKG4sIG1lYW5zW1tpXV0pDQp9DQpzdHIob3V0KQ0Kc3RyKHVubGlzdChvdXQpKQ0KYGBgDQoNCkhlcmUgSSd2ZSB1c2VkIHVubGlzdCgpIHRvIGZsYXR0ZW4gYSBsaXN0IG9mIHZlY3RvcnMgaW50byBhIHNpbmdsZSB2ZWN0b3IuIEEgc3RyaWN0ZXIgb3B0aW9uIGlzIHRvIHVzZSBwdXJycjo6ZmxhdHRlbl9kYmwoKSAtIGl0IHdpbGwgdGhyb3cgYW4gZXJyb3IgaWYgdGhlIGlucHV0IGlzbid0IGEgbGlzdCBvZiBkb3VibGVzLiA8L3A+DQoNClRoaXMgcGF0dGVybiBvY2N1cnMgaW4gb3RoZXIgcGxhY2VzIHRvbzogPC9icj4NCg0KWW91IG1pZ2h0IGJlIGdlbmVyYXRpbmcgYSBsb25nIHN0cmluZy4gSW5zdGVhZCBvZiBwYXN0ZSgpaW5nIHRvZ2V0aGVyIGVhY2ggaXRlcmF0aW9uIHdpdGggdGhlIHByZXZpb3VzLCBzYXZlIHRoZSBvdXRwdXQgaW4gYSBjaGFyYWN0ZXIgdmVjdG9yIGFuZCB0aGVuIGNvbWJpbmUgdGhhdCB2ZWN0b3IgaW50byBhIHNpbmdsZSBzdHJpbmcgd2l0aCBwYXN0ZShvdXRwdXQsIGNvbGxhcHNlID0gIiIpLiA8L3A+DQoNCllvdSBtaWdodCBiZSBnZW5lcmF0aW5nIGEgYmlnIGRhdGEgZnJhbWUuIEluc3RlYWQgb2Ygc2VxdWVudGlhbGx5IHJiaW5kKClpbmcgaW4gZWFjaCBpdGVyYXRpb24sIHNhdmUgdGhlIG91dHB1dCBpbiBhIGxpc3QsIHRoZW4gdXNlIGRwbHlyOjpiaW5kX3Jvd3Mob3V0cHV0KSB0byBjb21iaW5lIHRoZSBvdXRwdXQgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lLiA8L3A+DQoNCldhdGNoIG91dCBmb3IgdGhpcyBwYXR0ZXJuLiBXaGVuZXZlciB5b3Ugc2VlIGl0LCBzd2l0Y2ggdG8gYSBtb3JlIGNvbXBsZXggcmVzdWx0IG9iamVjdCwgYW5kIHRoZW4gY29tYmluZSBpbiBvbmUgc3RlcCBhdCB0aGUgZW5kLiA8L3A+DQoNCg0KPGgzPiBVbmtub3duIFNlcXVlbmNlIExlbmd0aCA8L2gzPg0KDQpTb21ldGltZXMgeW91IGRvbid0IGV2ZW4ga25vdyBob3cgbG9uZyB0aGUgaW5wdXQgc2VxdWVuY2Ugc2hvdWxkIHJ1biBmb3IuIFRoaXMgaXMgY29tbW9uIHdoZW4gZG9pbmcgc2ltdWxhdGlvbnMuIEZvciBleGFtcGxlLCB5b3UgbWlnaHQgd2FudCB0byBsb29wIHVudGlsIHlvdSBnZXQgdGhyZWUgaGVhZHMgaW4gYSByb3cuIFlvdSBjYW4ndCBkbyB0aGF0IHNvcnQgb2YgaXRlcmF0aW9uIHdpdGggdGhlIGZvciBsb29wLiBJbnN0ZWFkLCB5b3UgY2FuIHVzZSBhIHdoaWxlIGxvb3AuIEEgd2hpbGUgbG9vcCBpcyBzaW1wbGVyIHRoYW4gZm9yIGxvb3AgYmVjYXVzZSBpdCBvbmx5IGhhcyB0d28gY29tcG9uZW50cywgYSBjb25kaXRpb24gYW5kIGEgYm9keS4gIDwvcD4NCg0KQSB3aGlsZSBsb29wIGlzIGFsc28gbW9yZSBnZW5lcmFsIHRoYW4gYSBmb3IgbG9vcCwgYmVjYXVzZSB5b3UgY2FuIHJld3JpdGUgYW55IGZvciBsb29wIGFzIGEgd2hpbGUgbG9vcCwgYnV0IHlvdSBjYW4ndCByZXdyaXRlIGV2ZXJ5IHdoaWxlIGxvb3AgYXMgYSBmb3IgbG9vcDoNCg0KYGBge3J9DQpmb3IgKGkgaW4gc2VxX2Fsb25nKHgpKSB7DQogICMgYm9keQ0KfQ0KDQojIEVxdWl2YWxlbnQgdG8NCmkgPC0gMQ0Kd2hpbGUgKGkgPD0gbGVuZ3RoKHgpKSB7DQogICMgYm9keQ0KICBpIDwtIGkgKyAxIA0KfQ0KYGBgDQpIZXJlJ3MgaG93IHdlIGNvdWxkIHVzZSBhIHdoaWxlIGxvb3AgdG8gZmluZCBob3cgbWFueSB0cmllcyBpdCB0YWtlcyB0byBnZXQgdGhyZWUgaGVhZHMgaW4gYSByb3c6DQoNCmBgYHtyfQ0KZmxpcCA8LSBmdW5jdGlvbigpIHNhbXBsZShjKCJUIiwgIkgiKSwgMSkNCg0KZmxpcHMgPC0gMA0KbmhlYWRzIDwtIDANCg0Kd2hpbGUgKG5oZWFkcyA8IDMpIHsNCiAgaWYgKGZsaXAoKSA9PSAiSCIpIHsNCiAgICBuaGVhZHMgPC0gbmhlYWRzICsgMQ0KICB9IGVsc2Ugew0KICAgIG5oZWFkcyA8LSAwDQogIH0NCiAgZmxpcHMgPC0gZmxpcHMgKyAxDQp9DQpmbGlwcw0KDQoNCmBgYA0KSSBtZW50aW9uIHdoaWxlIGxvb3BzIG9ubHkgYnJpZWZseSwgYmVjYXVzZSBJIGhhcmRseSBldmVyIHVzZSB0aGVtLiBUaGV5J3JlIG1vc3Qgb2Z0ZW4gdXNlZCBmb3Igc2ltdWxhdGlvbiwgd2hpY2ggaXMgb3V0c2lkZSB0aGUgc2NvcGUgb2YgdGhpcyBib29rLiBIb3dldmVyLCBpdCBpcyBnb29kIHRvIGtub3cgdGhleSBleGlzdCBzbyB0aGF0IHlvdSdyZSBwcmVwYXJlZCBmb3IgcHJvYmxlbXMgd2hlcmUgdGhlIG51bWJlciBvZiBpdGVyYXRpb25zIGlzIG5vdCBrbm93biBpbiBhZHZhbmNlLg0KDQo8aDM+IEV4ZXJjaXNlcyA8L2gzPg0KSW1hZ2luZSB5b3UgaGF2ZSBhIGRpcmVjdG9yeSBmdWxsIG9mIENTViBmaWxlcyB0aGF0IHlvdSB3YW50IHRvIHJlYWQgaW4uIFlvdSBoYXZlIHRoZWlyIHBhdGhzIGluIGEgdmVjdG9yLCBmaWxlcyA8LSBkaXIoImRhdGEvIiwgcGF0dGVybiA9ICJcXC5jc3YkIiwgZnVsbC5uYW1lcyA9IFRSVUUpLCBhbmQgbm93IHdhbnQgdG8gcmVhZCBlYWNoIG9uZSB3aXRoIHJlYWRfY3N2KCkuIFdyaXRlIHRoZSBmb3IgbG9vcCB0aGF0IHdpbGwgbG9hZCB0aGVtIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZS4NCg0KYGBge3J9DQpkZiA8LSB2ZWN0b3IoImxpc3QiLCBsZW5ndGgoZmlsZXMpKQ0KZm9yIChmbmFtZSBpbiBzZXFfYWxvbmcoZmlsZXMpKSB7DQogIGRmW1tpXV0gPC0gcmVhZF9jc3YoZmlsZXNbW2ldXSkNCn0NCmRmIDwtIGJpbmRfcm93cyhkZikNCmBgYA0KDQpXaGF0IGhhcHBlbnMgaWYgeW91IHVzZSBmb3IgKG5tIGluIG5hbWVzKHgpKSBhbmQgeCBoYXMgbm8gbmFtZXM/IFdoYXQgaWYgb25seSBzb21lIG9mIHRoZSBlbGVtZW50cyBhcmUgbmFtZWQ/IFdoYXQgaWYgdGhlIG5hbWVzIGFyZSBub3QgdW5pcXVlPw0KDQpXaGVuIHRoZXJlIGFyZSBubyBuYW1lcyBmb3IgdGhlIHZlY3RvciwgaXQgZG9lcyBub3QgcnVuIHRoZSBjb2RlIGluIHRoZSBsb29wIChpdCBydW5zIHplcm8gaXRlcmF0aW9ucyBvZiB0aGUgbG9vcCk6DQpgYGB7cn0NCnggPC0gMTozDQpwcmludChuYW1lcyh4KSkNCiM+IE5VTEwNCmZvciAobm0gaW4gbmFtZXMoeCkpIHsNCiAgcHJpbnQobm0pDQogIHByaW50KHhbW25tXV0pDQp9DQpgYGANCklmIHRoZXJlIG9ubHkgc29tZSBuYW1lcywgdGhlbiB3ZSBnZXQgYW4gZXJyb3IgaWYgd2UgdHJ5IHRvIGFjY2VzcyBhbiBlbGVtZW50IHdpdGhvdXQgYSBuYW1lLiBIb3dldmVyLCBvZGRseSwgbm0gPT0gIiIgd2hlbiB0aGVyZSBpcyBubyBuYW1lLg0KDQpgYGB7cn0NCnggPC0gYyhhID0gMSwgMiwgYyA9IDMpDQpuYW1lcyh4KQ0KDQpmb3IgKG5tIGluIG5hbWVzKHgpKSB7DQogIHByaW50KG5tKQ0KICBwcmludCh4W1tubV1dKQ0KfQ0KDQpgYGANCkZpbmFsbHksIGlmIHRoZXJlIGFyZSBkdXBsaWNhdGUgbmFtZXMsIHRoZW4geFtbbm1dXSB3aWxsIGdpdmUgdGhlIGZpcnN0IGVsZW1lbnQgd2l0aCB0aGF0IG5hbWUuIFRoZXJlIGlzIG5vIHdheSB0byBhY2Nlc3MgZHVwbGljYXRlbHkgbmFtZWQgZWxlbWVudHMgYnkgbmFtZS4NCg0KYGBge3J9DQp4IDwtIGMoYSA9IDEsIGEgPSAyLCBjID0gMykNCm5hbWVzKHgpDQoNCmBgYA0KDQpgYGB7cn0NCmZvciAobm0gaW4gbmFtZXMoeCkpIHsNCiAgcHJpbnQobm0pDQogIHByaW50KHhbW25tXV0pDQp9DQpgYGANCg0KV3JpdGUgYSBmdW5jdGlvbiB0aGF0IHByaW50cyB0aGUgbWVhbiBvZiBlYWNoIG51bWVyaWMgY29sdW1uIGluIGEgZGF0YSBmcmFtZSwgYWxvbmcgd2l0aCBpdHMgbmFtZS4gRm9yIGV4YW1wbGUsIHNob3dfbWVhbihpcmlzKSB3b3VsZCBwcmludDoNCg0KYGBge3J9DQpzaG93X21lYW4gPC0gZnVuY3Rpb24oZGYsIGRpZ2l0cyA9IDIpIHsNCiAgIyBHZXQgbWF4IGxlbmd0aCBvZiBhbnkgdmFyaWFibGUgaW4gdGhlIGRhdGFzZXQNCiAgbWF4c3RyIDwtIG1heChzdHJpbmdyOjpzdHJfbGVuZ3RoKG5hbWVzKGRmKSkpDQogIGZvciAobm0gaW4gbmFtZXMoZGYpKSB7DQogICAgaWYgKGlzLm51bWVyaWMoZGZbW25tXV0pKSB7DQogICAgICBjYXQoc3RyaW5ncjo6c3RyX2Moc3RyaW5ncjo6c3RyX3BhZChzdHJpbmdyOjpzdHJfYyhubSwgIjoiKSwgbWF4c3RyICsgMUwsIHNpZGUgPSAicmlnaHQiKSwNCiAgICAgICAgICAgICAgICBmb3JtYXQobWVhbihkZltbbm1dXSksIGRpZ2l0cyA9IGRpZ2l0cywgbnNtYWxsID0gZGlnaXRzKSwNCiAgICAgICAgICAgICAgICBzZXAgPSAiICIpLA0KICAgICAgICAgICJcbiIpDQogICAgfQ0KICB9DQp9DQpzaG93X21lYW4oaXJpcykNCg0KDQpgYGANCg0KV2hhdCBkb2VzIHRoaXMgY29kZSBkbz8gSG93IGRvZXMgaXQgd29yaz8NCmBgYHtyfQ0KdHJhbnMgPC0gbGlzdCggDQogIGRpc3AgPSBmdW5jdGlvbih4KSB4ICogMC4wMTYzODcxLA0KICBhbSA9IGZ1bmN0aW9uKHgpIHsNCiAgICBmYWN0b3IoeCwgbGFiZWxzID0gYygiYXV0byIsICJtYW51YWwiKSkNCiAgfQ0KKQ0KZm9yICh2YXIgaW4gbmFtZXModHJhbnMpKSB7DQogIG10Y2Fyc1tbdmFyXV0gPC0gdHJhbnNbW3Zhcl1dKG10Y2Fyc1tbdmFyXV0pDQp9DQoNCmBgYA0KDQpUaGlzIGNvZGUgbXV0YXRlcyB0aGUgZGlzcCBhbmQgYW0gY29sdW1ucy4gPC9icj4NCg0KZGlzcCBpcyBtdWx0aXBsaWVkIGJ5IDAuMDE2Mzg3MQ0KYW0gaXMgcmVwbGFjZWQgYnkgYSBmYWN0b3IgdmFyaWFibGUuDQpUaGUgY29kZSB3b3JrcyBieSBsb29waW5nIG92ZXIgYSBuYW1lZCBsaXN0IG9mIGZ1bmN0aW9ucy4gSXQgY2FsbHMgdGhlIG5hbWVkIGZ1bmN0aW9uIGluIHRoZSBsaXN0IG9uIHRoZSBjb2x1bW4gb2YgbXRjYXJzIHdpdGggdGhlIHNhbWUgbmFtZSwgYW5kIHJlcGxhY2VzIHRoZSB2YWx1ZXMgb2YgdGhhdCBjb2x1bW4uIDwvcD4NCg0KPGgyPiBGb3IgTG9vcHMgdnMgZnVuY3Rpb25hbHMgPC9oMz4NCkZvciBsb29wcyBhcmUgbm90IGFzIGltcG9ydGFudCBpbiBSIGFzIHRoZXkgYXJlIGluIG90aGVyIGxhbmd1YWdlcyBiZWNhdXNlIFIgaXMgYSBmdW5jdGlvbmFsIHByb2dyYW1taW5nIGxhbmd1YWdlLiBUaGlzIG1lYW5zIHRoYXQgaXQncyBwb3NzaWJsZSB0byB3cmFwIHVwIGZvciBsb29wcyBpbiBhIGZ1bmN0aW9uLCBhbmQgY2FsbCB0aGF0IGZ1bmN0aW9uIGluc3RlYWQgb2YgdXNpbmcgdGhlIGZvciBsb29wIGRpcmVjdGx5Lg0KDQpUbyBzZWUgd2h5IHRoaXMgaXMgaW1wb3J0YW50LCBjb25zaWRlciAoYWdhaW4pIHRoaXMgc2ltcGxlIGRhdGEgZnJhbWU6DQoNCmBgYHtyfQ0KZGYgPC0gdGliYmxlKA0KICBhID0gcm5vcm0oMTApLA0KICBiID0gcm5vcm0oMTApLA0KICBjID0gcm5vcm0oMTApLA0KICBkID0gcm5vcm0oMTApDQopDQoNCmBgYA0KDQpJbWFnaW5lIHlvdSB3YW50IHRvIGNvbXB1dGUgdGhlIG1lYW4gb2YgZXZlcnkgY29sdW1uLiBZb3UgY291bGQgZG8gdGhhdCB3aXRoIGEgZm9yIGxvb3A6DQoNCmBgYHtyfQ0Kb3V0cHV0IDwtIHZlY3RvcigiZG91YmxlIiwgbGVuZ3RoKGRmKSkNCmZvciAoaSBpbiBzZXFfYWxvbmcoZGYpKSB7DQogIG91dHB1dFtbaV1dIDwtIG1lYW4oZGZbW2ldXSkNCn0NCm91dHB1dA0KYGBgDQoNCllvdSByZWFsaXNlIHRoYXQgeW91J3JlIGdvaW5nIHRvIHdhbnQgdG8gY29tcHV0ZSB0aGUgbWVhbnMgb2YgZXZlcnkgY29sdW1uIHByZXR0eSBmcmVxdWVudGx5LCBzbyB5b3UgZXh0cmFjdCBpdCBvdXQgaW50byBhIGZ1bmN0aW9uOg0KYGBge3J9DQpjb2xfbWVhbiA8LSBmdW5jdGlvbihkZikgew0KICBvdXRwdXQgPC0gdmVjdG9yKCJkb3VibGUiLCBsZW5ndGgoZGYpKQ0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKGRmKSkgew0KICAgIG91dHB1dFtpXSA8LSBtZWFuKGRmW1tpXV0pDQogIH0NCiAgb3V0cHV0DQp9DQoNCmBgYA0KDQpCdXQgdGhlbiB5b3UgdGhpbmsgaXQnZCBhbHNvIGJlIGhlbHBmdWwgdG8gYmUgYWJsZSB0byBjb21wdXRlIHRoZSBtZWRpYW4sIGFuZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uLCBzbyB5b3UgY29weSBhbmQgcGFzdGUgeW91ciBjb2xfbWVhbigpIGZ1bmN0aW9uIGFuZCByZXBsYWNlIHRoZSBtZWFuKCkgd2l0aCBtZWRpYW4oKSBhbmQgc2QoKToNCg0KDQpgYGB7cn0NCmNvbF9tZWRpYW4gPC0gZnVuY3Rpb24oZGYpIHsNCiAgb3V0cHV0IDwtIHZlY3RvcigiZG91YmxlIiwgbGVuZ3RoKGRmKSkNCiAgZm9yIChpIGluIHNlcV9hbG9uZyhkZikpIHsNCiAgICBvdXRwdXRbaV0gPC0gbWVkaWFuKGRmW1tpXV0pDQogIH0NCiAgb3V0cHV0DQp9DQpjb2xfc2QgPC0gZnVuY3Rpb24oZGYpIHsNCiAgb3V0cHV0IDwtIHZlY3RvcigiZG91YmxlIiwgbGVuZ3RoKGRmKSkNCiAgZm9yIChpIGluIHNlcV9hbG9uZyhkZikpIHsNCiAgICBvdXRwdXRbaV0gPC0gc2QoZGZbW2ldXSkNCiAgfQ0KICBvdXRwdXQNCn0NCmBgYA0KVWggb2ghIFlvdSd2ZSBjb3BpZWQtYW5kLXBhc3RlZCB0aGlzIGNvZGUgdHdpY2UsIHNvIGl0J3MgdGltZSB0byB0aGluayBhYm91dCBob3cgdG8gZ2VuZXJhbGlzZSBpdC4gTm90aWNlIHRoYXQgbW9zdCBvZiB0aGlzIGNvZGUgaXMgZm9yLWxvb3AgYm9pbGVycGxhdGUgYW5kIGl0J3MgaGFyZCB0byBzZWUgdGhlIG9uZSB0aGluZyAobWVhbigpLCBtZWRpYW4oKSwgc2QoKSkgdGhhdCBpcyBkaWZmZXJlbnQgYmV0d2VlbiB0aGUgZnVuY3Rpb25zLg0KDQpXZSBjYW4gZG8gZXhhY3RseSB0aGUgc2FtZSB0aGluZyB3aXRoIGNvbF9tZWFuKCksIGNvbF9tZWRpYW4oKSBhbmQgY29sX3NkKCkgYnkgYWRkaW5nIGFuIGFyZ3VtZW50IHRoYXQgc3VwcGxpZXMgdGhlIGZ1bmN0aW9uIHRvIGFwcGx5IHRvIGVhY2ggY29sdW1uOg0KDQpgYGB7cn0NCmNvbF9zdW1tYXJ5IDwtIGZ1bmN0aW9uKGRmLCBmdW4pIHsNCiAgb3V0IDwtIHZlY3RvcigiZG91YmxlIiwgbGVuZ3RoKGRmKSkNCiAgZm9yIChpIGluIHNlcV9hbG9uZyhkZikpIHsNCiAgICBvdXRbaV0gPC0gZnVuKGRmW1tpXV0pDQogIH0NCiAgb3V0DQp9DQpjb2xfc3VtbWFyeShkZiwgbWVkaWFuKQ0KY29sX3N1bW1hcnkoZGYsIG1lYW4pDQpgYGANCg0KVGhlIGlkZWEgb2YgcGFzc2luZyBhIGZ1bmN0aW9uIHRvIGFub3RoZXIgZnVuY3Rpb24gaXMgZXh0cmVtZWx5IHBvd2VyZnVsIGlkZWEsIGFuZCBpdCdzIG9uZSBvZiB0aGUgYmVoYXZpb3VycyB0aGF0IG1ha2VzIFIgYSBmdW5jdGlvbmFsIHByb2dyYW1taW5nIGxhbmd1YWdlLiBJdCBtaWdodCB0YWtlIHlvdSBhIHdoaWxlIHRvIHdyYXAgeW91ciBoZWFkIGFyb3VuZCB0aGUgaWRlYSwgYnV0IGl0J3Mgd29ydGggdGhlIGludmVzdG1lbnQuIEluIHRoZSByZXN0IG9mIHRoZSBjaGFwdGVyLCB5b3UnbGwgbGVhcm4gYWJvdXQgYW5kIHVzZSB0aGUgcHVycnIgcGFja2FnZSwgd2hpY2ggcHJvdmlkZXMgZnVuY3Rpb25zIHRoYXQgZWxpbWluYXRlIHRoZSBuZWVkIGZvciBtYW55IGNvbW1vbiBmb3IgbG9vcHMuIFRoZSBhcHBseSBmYW1pbHkgb2YgZnVuY3Rpb25zIGluIGJhc2UgUiAoYXBwbHkoKSwgbGFwcGx5KCksIHRhcHBseSgpLCBldGMpIHNvbHZlIGEgc2ltaWxhciBwcm9ibGVtLCBidXQgcHVycnIgaXMgbW9yZSBjb25zaXN0ZW50IGFuZCB0aHVzIGlzIGVhc2llciB0byBsZWFybi4gPC9wPg0KDQpUaGUgZ29hbCBvZiB1c2luZyBwdXJyciBmdW5jdGlvbnMgaW5zdGVhZCBvZiBmb3IgbG9vcHMgaXMgdG8gYWxsb3cgeW91IGJyZWFrIGNvbW1vbiBsaXN0IG1hbmlwdWxhdGlvbiBjaGFsbGVuZ2VzIGludG8gaW5kZXBlbmRlbnQgcGllY2VzOiA8L2JyPg0KDQpIb3cgY2FuIHlvdSBzb2x2ZSB0aGUgcHJvYmxlbSBmb3IgYSBzaW5nbGUgZWxlbWVudCBvZiB0aGUgbGlzdD8gT25jZSB5b3UndmUgc29sdmVkIHRoYXQgcHJvYmxlbSwgcHVycnIgdGFrZXMgY2FyZSBvZiBnZW5lcmFsaXNpbmcgeW91ciBzb2x1dGlvbiB0byBldmVyeSBlbGVtZW50IGluIHRoZSBsaXN0LiA8L3A+DQoNCklmIHlvdSdyZSBzb2x2aW5nIGEgY29tcGxleCBwcm9ibGVtLCBob3cgY2FuIHlvdSBicmVhayBpdCBkb3duIGludG8gYml0ZS1zaXplZCBwaWVjZXMgdGhhdCBhbGxvdyB5b3UgdG8gYWR2YW5jZSBvbmUgc21hbGwgc3RlcCB0b3dhcmRzIGEgc29sdXRpb24/IFdpdGggcHVycnIsIHlvdSBnZXQgbG90cyBvZiBzbWFsbCBwaWVjZXMgdGhhdCB5b3UgY2FuIGNvbXBvc2UgdG9nZXRoZXIgd2l0aCB0aGUgcGlwZS4gPC9wPg0KDQpUaGlzIHN0cnVjdHVyZSBtYWtlcyBpdCBlYXNpZXIgdG8gc29sdmUgbmV3IHByb2JsZW1zLiBJdCBhbHNvIG1ha2VzIGl0IGVhc2llciB0byB1bmRlcnN0YW5kIHlvdXIgc29sdXRpb25zIHRvIG9sZCBwcm9ibGVtcyB3aGVuIHlvdSByZS1yZWFkIHlvdXIgb2xkIGNvZGUuIDwvcD4NCg0KPGgzPiBFeGVyY2lzZXMgPC9oMz4NClJlYWQgdGhlIGRvY3VtZW50YXRpb24gZm9yIGFwcGx5KCkuIEluIHRoZSAyZCBjYXNlLCB3aGF0IHR3byBmb3IgbG9vcHMgZG9lcyBpdCBnZW5lcmFsaXNlLiA8L2JyPg0KDQpJdCBnZW5lcmFsaXNlcyBsb29waW5nIG92ZXIgdGhlIHJvd3Mgb3IgY29sdW1ucyBvZiBhIG1hdHJpeCBvciBkYXRhLWZyYW1lLiA8L3A+DQoNCkFkYXB0IGNvbF9zdW1tYXJ5KCkgc28gdGhhdCBpdCBvbmx5IGFwcGxpZXMgdG8gbnVtZXJpYyBjb2x1bW5zIFlvdSBtaWdodCB3YW50IHRvIHN0YXJ0IHdpdGggYW4gaXNfbnVtZXJpYygpIGZ1bmN0aW9uIHRoYXQgcmV0dXJucyBhIGxvZ2ljYWwgdmVjdG9yIHRoYXQgaGFzIGEgVFJVRSBjb3JyZXNwb25kaW5nIHRvIGVhY2ggbnVtZXJpYyBjb2x1bW4uIDwvYnI+DQoNCmBgYHtyfQ0KY29sX3N1bW1hcnkyIDwtIGZ1bmN0aW9uKGRmLCBmdW4pIHsNCiAgIyB0ZXN0IHdoZXRoZXIgZWFjaCBjb2x1bSBpcyBudW1lcmljDQogIG51bWVyaWNfY29scyA8LSB2ZWN0b3IoImxvZ2ljYWwiLCBsZW5ndGgoZGYpKQ0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKGRmKSkgew0KICAgIG51bWVyaWNfY29sc1tbaV1dIDwtIGlzLm51bWVyaWMoZGZbW2ldXSkNCiAgfQ0KICAjIGluZGV4ZXMgb2YgbnVtZXJpYyBjb2x1bW5zDQogIGlkeHMgPC0gc2VxX2Fsb25nKGRmKVtudW1lcmljX2NvbHNdDQogICMgbnVtYmVyIG9mIG51bWVyaWMgY29sdW1ucw0KICBuIDwtIHN1bShudW1lcmljX2NvbHMpDQogIG91dCA8LSB2ZWN0b3IoImRvdWJsZSIsIG4pDQogIGZvciAoaSBpbiBpZHhzKSB7DQogICAgb3V0W2ldIDwtIGZ1bihkZltbaV1dKQ0KICB9DQogIG91dA0KfQ0KDQojIExldCdzIHRlc3QgdGhhdCBpdCB3b3Jrcw0KZGYgPC0gdGliYmxlKA0KICBhID0gcm5vcm0oMTApLA0KICBiID0gcm5vcm0oMTApLA0KICBjID0gbGV0dGVyc1sxOjEwXSwNCiAgZCA9IHJub3JtKDEwKQ0KKQ0KY29sX3N1bW1hcnkyKGRmLCBtZWFuKQ0KYGBgDQoNCjxoMj4gVGhlIE1hcCBmdW5jdGlvbnMgPC9oMj4NClRoZXJlIGlzIG9uZSBmdW5jdGlvbiBmb3IgZWFjaCB0eXBlIG9mIG91dHB1dDogPC9icj4NCg0KPGxpPm1hcCgpIG1ha2VzIGEgbGlzdC4gPC9saT4NCjxsaT5tYXBfbGdsKCkgbWFrZXMgYSBsb2dpY2FsIHZlY3Rvci4gPC9saT4NCjxsaT5tYXBfaW50KCkgbWFrZXMgYW4gaW50ZWdlciB2ZWN0b3IuIDwvbGk+DQo8bGk+bWFwX2RibCgpIG1ha2VzIGEgZG91YmxlIHZlY3Rvci4gPC9saT4NCjxsaT5tYXBfY2hyKCkgbWFrZXMgYSBjaGFyYWN0ZXIgdmVjdG9yLjwvbGk+DQoNCkVhY2ggZnVuY3Rpb24gdGFrZXMgYSB2ZWN0b3IgYXMgaW5wdXQsIGFwcGxpZXMgYSBmdW5jdGlvbiB0byBlYWNoIHBpZWNlLCBhbmQgdGhlbiByZXR1cm5zIGEgbmV3IHZlY3RvciB0aGF0J3MgdGhlIHNhbWUgbGVuZ3RoIChhbmQgaGFzIHRoZSBzYW1lIG5hbWVzKSBhcyB0aGUgaW5wdXQuIFRoZSB0eXBlIG9mIHRoZSB2ZWN0b3IgaXMgZGV0ZXJtaW5lZCBieSB0aGUgc3VmZml4IHRvIHRoZSBtYXAgZnVuY3Rpb24uIDwvcD4NCg0KT25jZSB5b3UgbWFzdGVyIHRoZXNlIGZ1bmN0aW9ucywgeW91J2xsIGZpbmQgaXQgdGFrZXMgbXVjaCBsZXNzIHRpbWUgdG8gc29sdmUgaXRlcmF0aW9uIHByb2JsZW1zLiBCdXQgeW91IHNob3VsZCBuZXZlciBmZWVsIGJhZCBhYm91dCB1c2luZyBhIGZvciBsb29wIGluc3RlYWQgb2YgYSBtYXAgZnVuY3Rpb24uIFRoZSBtYXAgZnVuY3Rpb25zIGFyZSBhIHN0ZXAgdXAgYSB0b3dlciBvZiBhYnN0cmFjdGlvbiwgYW5kIGl0IGNhbiB0YWtlIGEgbG9uZyB0aW1lIHRvIGdldCB5b3VyIGhlYWQgYXJvdW5kIGhvdyB0aGV5IHdvcmsuIFRoZSBpbXBvcnRhbnQgdGhpbmcgaXMgdGhhdCB5b3Ugc29sdmUgdGhlIHByb2JsZW0gdGhhdCB5b3UncmUgd29ya2luZyBvbiwgbm90IHdyaXRlIHRoZSBtb3N0IGNvbmNpc2UgYW5kIGVsZWdhbnQgY29kZSAoYWx0aG91Z2ggdGhhdCdzIGRlZmluaXRlbHkgc29tZXRoaW5nIHlvdSB3YW50IHRvIHN0cml2ZSB0b3dhcmRzISkuIDwvcD4NCg0KU29tZSBwZW9wbGUgd2lsbCB0ZWxsIHlvdSB0byBhdm9pZCBmb3IgbG9vcHMgYmVjYXVzZSB0aGV5IGFyZSBzbG93LiBUaGV5J3JlIHdyb25nISAoV2VsbCBhdCBsZWFzdCB0aGV5J3JlIHJhdGhlciBvdXQgb2YgZGF0ZSwgYXMgZm9yIGxvb3BzIGhhdmVuJ3QgYmVlbiBzbG93IGZvciBtYW55IHllYXJzKS4gVGhlIGNoaWVmIGJlbmVmaXRzIG9mIHVzaW5nIGZ1bmN0aW9ucyBsaWtlIG1hcCgpIGlzIG5vdCBzcGVlZCwgYnV0IGNsYXJpdHk6IHRoZXkgbWFrZSB5b3VyIGNvZGUgZWFzaWVyIHRvIHdyaXRlIGFuZCB0byByZWFkLiA8L3A+DQoNCldlIGNhbiB1c2UgdGhlc2UgZnVuY3Rpb25zIHRvIHBlcmZvcm0gdGhlIHNhbWUgY29tcHV0YXRpb25zIGFzIHRoZSBsYXN0IGZvciBsb29wLiBUaG9zZSBzdW1tYXJ5IGZ1bmN0aW9ucyByZXR1cm5lZCBkb3VibGVzLCBzbyB3ZSBuZWVkIHRvIHVzZSBtYXBfZGJsKCk6DQoNCg0KDQpgYGB7cn0NCm1hcF9kYmwoZGYsIG1lYW4pDQoNCm1hcF9kYmwoZGYsIG1lZGlhbikNCg0KbWFwX2RibChkZiwgc2QpDQoNCmBgYA0KDQpDb21wYXJlZCB0byB1c2luZyBhIGZvciBsb29wLCBmb2N1cyBpcyBvbiB0aGUgb3BlcmF0aW9uIGJlaW5nIHBlcmZvcm1lZCAoaS5lLiBtZWFuKCksIG1lZGlhbigpLCBzZCgpKSwgbm90IHRoZSBib29ra2VlcGluZyByZXF1aXJlZCB0byBsb29wIG92ZXIgZXZlcnkgZWxlbWVudCBhbmQgc3RvcmUgdGhlIG91dHB1dC4gVGhpcyBpcyBldmVuIG1vcmUgYXBwYXJlbnQgaWYgd2UgdXNlIHRoZSBwaXBlOg0KDQpgYGB7cn0NCmRmICU+JSBtYXBfZGJsKG1lYW4pDQoNCmRmICU+JSBtYXBfZGJsKG1lZGlhbikNCg0KZGYgJT4lIG1hcF9kYmwoc2QpDQpgYGANCg0KDQpUaGVyZSBhcmUgYSBmZXcgZGlmZmVyZW5jZXMgYmV0d2VlbiBtYXBfKigpIGFuZCBjb2xfc3VtbWFyeSgpOiA8L2JyPg0KDQpBbGwgcHVycnIgZnVuY3Rpb25zIGFyZSBpbXBsZW1lbnRlZCBpbiBDLiBUaGlzIG1ha2VzIHRoZW0gYSBsaXR0bGUgZmFzdGVyIGF0IHRoZSBleHBlbnNlIG9mIHJlYWRhYmlsaXR5LiA8L3A+DQoNClRoZSBzZWNvbmQgYXJndW1lbnQsIC5mLCB0aGUgZnVuY3Rpb24gdG8gYXBwbHksIGNhbiBiZSBhIGZvcm11bGEsIGEgY2hhcmFjdGVyIHZlY3Rvciwgb3IgYW4gaW50ZWdlciB2ZWN0b3IuIFlvdSdsbCBsZWFybiBhYm91dCB0aG9zZSBoYW5keSBzaG9ydGN1dHMgaW4gdGhlIG5leHQgc2VjdGlvbi4gbWFwXyooKSB1c2VzIC4gKFtkb3QgZG90IGRvdF0pIHRvIHBhc3MgYWxvbmcgYWRkaXRpb25hbCBhcmd1bWVudHMgdG8gLmYgZWFjaCB0aW1lIGl0J3MgY2FsbGVkOg0KDQpgYGB7cn0NCm1hcF9kYmwoZGYsIG1lYW4sIHRyaW0gPSAwLjUpDQpgYGANCg0KVGhlIG1hcCBmdW5jdGlvbiBhbHNvIHByZXNlcnZlcyBuYW1lczoNCg0KYGBge3J9DQp6IDwtIGxpc3QoeCA9IDE6MywgeSA9IDQ6NSkNCm1hcF9pbnQoeiwgbGVuZ3RoKQ0KDQpgYGANCg0KVGhlcmUgYXJlIGEgZmV3IHNob3J0Y3V0cyB0aGF0IHlvdSBjYW4gdXNlIHdpdGggLmYgaW4gb3JkZXIgdG8gc2F2ZSBhIGxpdHRsZSB0eXBpbmcuIEltYWdpbmUgeW91IHdhbnQgdG8gZml0IGEgbGluZWFyIG1vZGVsIHRvIGVhY2ggZ3JvdXAgaW4gYSBkYXRhc2V0LiBUaGUgZm9sbG93aW5nIHRveSBleGFtcGxlIHNwbGl0cyB0aGUgdXAgdGhlIG10Y2FycyBkYXRhc2V0IGluIHRvIHRocmVlIHBpZWNlcyAob25lIGZvciBlYWNoIHZhbHVlIG9mIGN5bGluZGVyKSBhbmQgZml0cyB0aGUgc2FtZSBsaW5lYXIgbW9kZWwgdG8gZWFjaCBwaWVjZToNCg0KYGBge3J9DQptb2RlbHMgPC0gbXRjYXJzICU+JSANCiAgc3BsaXQoLiRjeWwpICU+JSANCiAgbWFwKGZ1bmN0aW9uKGRmKSBsbShtcGcgfiB3dCwgZGF0YSA9IGRmKSkNCg0KbW9kZWxzDQoNCmBgYA0KVGhlIHN5bnRheCBmb3IgY3JlYXRpbmcgYW4gYW5vbnltb3VzIGZ1bmN0aW9uIGluIFIgaXMgcXVpdGUgdmVyYm9zZSBzbyBwdXJyciBwcm92aWRlcyBhIGNvbnZlbmllbnQgc2hvcnRjdXQ6IGEgb25lLXNpZGVkIGZvcm11bGEuIE5vdGU6IFRoZSBsbSgpIGZ1bmN0aW9uIHJ1bnMgYSBsaW5lYXIgcmVncmVzc2lvbi4gSXQgaXMgY292ZXJlZCBpbiB0aGUgTW9kZWwgQmFzaWNzIGNoYXB0ZXIuIDwvcD4NCg0KRnJvbSByLWJsb2dnZXJzLmNvbXMgPC9icj4NCkluIEZQLCBuYW1pbmcgYW5kIGFwcGx5aW5nIGEgZnVuY3Rpb24gYXJlIHR3byBzZXBhcmF0ZSBvcGVyYXRpb25zLCB5b3UgZG9uJ3QgbmVlZCB0byBnaXZlIHlvdXIgZnVuY3Rpb25zIG5hbWVzIGluIG9yZGVyIHRvIGNhbGwgdGhlbS4gU28sIGNhbGxpbmcgdGhpcyBmdW5jdGlvbiA8L2JyPg0KDQpgYGB7cn0NCnBvd2Z1biA8LSBmdW5jdGlvbih4LCBwb3cpIHsNCiAgICB4XnBvdw0KfQ0KcG93ZnVuKDIsIDEwKQ0KDQpgYGANCnRvIHRoZSBpbnRlcnByZXRlciBpcyBleGFjdGx5IHRoZSBzYW1lIGFzIGFwcGx5aW5nIHZhcmlhYmxlcyB0byB0aGUgYW5vbnltb3VzIGZ1bmN0aW9uOg0KDQpgYGB7cn0NCiNhbm9ueW1vdXNlIGVxdWl2YWxlbnQgDQooZnVuY3Rpb24oeCwgcG93KSB7DQogICAgeF5wb3cNCn0pKDIsIDEwKQ0KDQoNCmBgYA0KDQoNCmBgYHtyfQ0KbW9kZWxzIDwtIG10Y2FycyAlPiUgDQogIHNwbGl0KC4kY3lsKSAlPiUgDQogIG1hcCh+bG0obXBnIH4gd3QsIGRhdGEgPSAuKSkNCm1vZGVscw0KYGBgDQoNCkhlcmUgSSd2ZSB1c2VkIC4gYXMgYSBwcm9ub3VuOiBpdCByZWZlcnMgdG8gdGhlIGN1cnJlbnQgbGlzdCBlbGVtZW50IChpbiB0aGUgc2FtZSB3YXkgdGhhdCBpIHJlZmVycmVkIHRvIHRoZSBjdXJyZW50IGluZGV4IGluIHRoZSBmb3IgbG9vcCkuIDwvcD4NCg0KDQpXaGVuIHlvdSdyZSBsb29raW5nIGF0IG1hbnkgbW9kZWxzLCB5b3UgbWlnaHQgd2FudCB0byBleHRyYWN0IGEgc3VtbWFyeSBzdGF0aXN0aWMgbGlrZSB0aGUgIFIyIC4gVG8gZG8gdGhhdCB3ZSBuZWVkIHRvIGZpcnN0IHJ1biBzdW1tYXJ5KCkgYW5kIHRoZW4gZXh0cmFjdCB0aGUgY29tcG9uZW50IGNhbGxlZCByLnNxdWFyZWQuIFdlIGNvdWxkIGRvIHRoYXQgdXNpbmcgdGhlIHNob3J0aGFuZCBmb3IgYW5vbnltb3VzIGZ1bmN0aW9uczoNCg0KYGBge3J9DQptb2RlbHMgJT4lIA0KICBtYXAoc3VtbWFyeSkgJT4lIA0KICBtYXBfZGJsKH4uJHIuc3F1YXJlZCkNCmBgYA0KQnV0IGV4dHJhY3RpbmcgbmFtZWQgY29tcG9uZW50cyBpcyBhIGNvbW1vbiBvcGVyYXRpb24sIHNvIHB1cnJyIHByb3ZpZGVzIGFuIGV2ZW4gc2hvcnRlciBzaG9ydGN1dDogeW91IGNhbiB1c2UgYSBzdHJpbmcuDQoNCmBgYHtyfQ0KbW9kZWxzICU+JSANCiAgbWFwKHN1bW1hcnkpICU+JSANCiAgbWFwX2RibCgici5zcXVhcmVkIikNCmBgYA0KDQpZb3UgY2FuIGFsc28gdXNlIGFuIGludGVnZXIgdG8gc2VsZWN0IGVsZW1lbnRzIGJ5IHBvc2l0aW9uOg0KYGBge3J9DQp4IDwtIGxpc3QobGlzdCgxLCAyLCAzKSwgbGlzdCg0LCA1LCA2KSwgbGlzdCg3LCA4LCA5KSkNCnggJT4lIG1hcF9kYmwoMikNCg0KYGBgDQoNCjxoMj4gQmFzZSBSIDwvaDI+DQpJZiB5b3UncmUgZmFtaWxpYXIgd2l0aCB0aGUgYXBwbHkgZmFtaWx5IG9mIGZ1bmN0aW9ucyBpbiBiYXNlIFIsIHlvdSBtaWdodCBoYXZlIG5vdGljZWQgc29tZSBzaW1pbGFyaXRpZXMgd2l0aCB0aGUgcHVycnIgZnVuY3Rpb25zOiA8L3A+DQoNCmxhcHBseSgpIGlzIGJhc2ljYWxseSBpZGVudGljYWwgdG8gbWFwKCksIGV4Y2VwdCB0aGF0IG1hcCgpIGlzIGNvbnNpc3RlbnQgd2l0aCBhbGwgdGhlIG90aGVyIGZ1bmN0aW9ucyBpbiBwdXJyciwgYW5kIHlvdSBjYW4gdXNlIHRoZSBzaG9ydGN1dHMgZm9yIC5mLiA8L3A+DQoNCkJhc2Ugc2FwcGx5KCkgaXMgYSB3cmFwcGVyIGFyb3VuZCBsYXBwbHkoKSB0aGF0IGF1dG9tYXRpY2FsbHkgc2ltcGxpZmllcyB0aGUgb3V0cHV0LiBUaGlzIGlzIHVzZWZ1bCBmb3IgaW50ZXJhY3RpdmUgd29yayBidXQgaXMgcHJvYmxlbWF0aWMgaW4gYSBmdW5jdGlvbiBiZWNhdXNlIHlvdSBuZXZlciBrbm93IHdoYXQgc29ydCBvZiBvdXRwdXQgeW91J2xsIGdldDogPC9wPg0KDQpgYGB7cn0NCngxIDwtIGxpc3QoDQogIGMoMC4yNywgMC4zNywgMC41NywgMC45MSwgMC4yMCksDQogIGMoMC45MCwgMC45NCwgMC42NiwgMC42MywgMC4wNiksIA0KICBjKDAuMjEsIDAuMTgsIDAuNjksIDAuMzgsIDAuNzcpDQopDQp4MiA8LSBsaXN0KA0KICBjKDAuNTAsIDAuNzIsIDAuOTksIDAuMzgsIDAuNzgpLCANCiAgYygwLjkzLCAwLjIxLCAwLjY1LCAwLjEzLCAwLjI3KSwgDQogIGMoMC4zOSwgMC4wMSwgMC4zOCwgMC44NywgMC4zNCkNCikNCg0KdGhyZXNob2xkIDwtIGZ1bmN0aW9uKHgsIGN1dG9mZiA9IDAuOCkgeFt4ID4gY3V0b2ZmXQ0KeDEgJT4lIHNhcHBseSh0aHJlc2hvbGQpICU+JSBzdHIoKQ0KeDIgJT4lIHNhcHBseSh0aHJlc2hvbGQpICU+JSBzdHIoKQ0KYGBgDQoNCg0KdmFwcGx5KCkgaXMgYSBzYWZlIGFsdGVybmF0aXZlIHRvIHNhcHBseSgpIGJlY2F1c2UgeW91IHN1cHBseSBhbiBhZGRpdGlvbmFsIGFyZ3VtZW50IHRoYXQgZGVmaW5lcyB0aGUgdHlwZS4gVGhlIG9ubHkgcHJvYmxlbSB3aXRoIHZhcHBseSgpIGlzIHRoYXQgaXQncyBhIGxvdCBvZiB0eXBpbmc6IHZhcHBseShkZiwgaXMubnVtZXJpYywgbG9naWNhbCgxKSkgaXMgZXF1aXZhbGVudCB0byBtYXBfbGdsKGRmLCBpcy5udW1lcmljKS4gT25lIGFkdmFudGFnZSBvZiB2YXBwbHkoKSBvdmVyIHB1cnJyJ3MgbWFwIGZ1bmN0aW9ucyBpcyB0aGF0IGl0IGNhbiBhbHNvIHByb2R1Y2UgbWF0cmljZXMgLSB0aGUgbWFwIGZ1bmN0aW9ucyBvbmx5IGV2ZXIgcHJvZHVjZSB2ZWN0b3JzLiA8L3A+DQoNCjxoMz4gRXhlcmNpc2VzIDwvaDM+DQpXcml0ZSBjb2RlIHRoYXQgdXNlcyBvbmUgb2YgdGhlIG1hcCBmdW5jdGlvbnMgdG86IDwvYnI+DQoNCkNvbXB1dGUgdGhlIG1lYW4gb2YgZXZlcnkgY29sdW1uIGluIGBtdGNhcnNgLiA8L2JyPg0KYGBge3J9DQptYXBfZGJsKG10Y2FycywgbWVhbikNCmBgYA0KDQpEZXRlcm1pbmUgdGhlIHR5cGUgb2YgZWFjaCBjb2x1bW4gaW4gYG55Y2ZsaWdodHMxMzo6ZmxpZ2h0c2AuIDwvYnI+DQpgYGB7cn0NCm1hcChueWNmbGlnaHRzMTM6OmZsaWdodHMsIGNsYXNzKQ0KYGBgDQpJIGhhZCB0byB1c2UgbWFwIHJhdGhlciB0aGFuIG1hcF9jaHIgc2luY2UgdGhlIGNsYXNzIFRob3VnaCBpZiBieSB0eXBlLCB0eXBlb2YgaXMgbWVhbnQuDQoNCkNvbXB1dGUgdGhlIG51bWJlciBvZiB1bmlxdWUgdmFsdWVzIGluIGVhY2ggY29sdW1uIG9mIGBpcmlzYC4gPC9icj4gDQoNCmBgYHtyfQ0KbWFwX2ludChpcmlzLCB+IGxlbmd0aCh1bmlxdWUoLikpKQ0KYGBgDQoNCkdlbmVyYXRlIDEwIHJhbmRvbSBub3JtYWxzIGZvciBlYWNoIG9mICRcbXUgPSAtMTAkLCAkMCQsICQxMCQsIGFuZCAkMTAwJC4gPC9icj4NCmBgYHtyfQ0KbWFwKGMoLTEwLCAwLCAxMCwgMTAwKSwgcm5vcm0sIG4gPSAxMCkNCmBgYA0KDQpIb3cgY2FuIHlvdSBjcmVhdGUgYSBzaW5nbGUgdmVjdG9yIHRoYXQgZm9yIGVhY2ggY29sdW1uIGluIGEgZGF0YSBmcmFtZSBpbmRpY2F0ZXMgd2hldGhlciBvciBub3QgaXQncyBhIGZhY3Rvcj8gPC9icj4NCg0KVXNlIG1hcF9sZ2wgKEFwcGx5IGEgZnVuY3Rpb24gdG8gZWFjaCBlbGVtZW50IG9mIGEgdmVjdG9yKSAgd2l0aCB0aGUgZnVuY3Rpb24gaXMuZmFjdG9yDQpgYGB7cn0NCm1hcF9sZ2wobXRjYXJzLCBpcy5mYWN0b3IpDQpgYGANCldoYXQgaGFwcGVucyB3aGVuIHlvdSB1c2UgdGhlIG1hcCBmdW5jdGlvbnMgb24gdmVjdG9ycyB0aGF0IGFyZW4ndCBsaXN0cz8gV2hhdCBkb2VzIG1hcCgxOjUsIHJ1bmlmKSBkbz8gV2h5Pw0KVGhlIGZ1bmN0aW9uIG1hcCBhcHBsaWVzIHRoZSBmdW5jdGlvbiB0byBlYWNoIGVsZW1lbnQgb2YgdGhlIHZlY3Rvci4NCmBgYHtyfQ0KbWFwKDE6NSwgcnVuaWYpDQpgYGANCldoYXQgZG9lcyBtYXAoLTI6Miwgcm5vcm0sIG4gPSA1KSBkbz8gV2h5PyANCg0KYGBge3J9DQptYXAoLTI6Miwgcm5vcm0sIG4gPSA1KQ0KYGBgDQoNClRoaXMgdGFrZXMgc2FtcGxlcyBvZiBuID0gNSBmcm9tIG5vcm1hbCBkaXN0cmlidXRpb25zIG9mIG1lYW5zIC0yLCAtMSwgMCwgMSwgYW5kIDIsIGFuZCByZXR1cm5zIGEgbGlzdCB3aXRoIGVhY2ggZWxlbWVudCBhIG51bWVyaWMgdmVjdG9ycyBvZiBsZW5ndGggNS4gPC9wPg0KDQoNCldoYXQgZG9lcyBtYXBfZGJsKC0yOjIsIHJub3JtLCBuID0gNSkgZG8/IFdoeT8NCg0KYGBge3J9DQptYXBfZGJsKC0yOjIsIHJub3JtLCBuID0gNSkNCmBgYA0KSG93ZXZlciwgaWYgd2UgdXNlIG1hcF9kYmwgaXQgdGhyb3dzIGFuIGVycm9yLiBtYXBfZGJsIGV4cGVjdHMgdGhlIGZ1bmN0aW9uIHRvIHJldHVybiBhIG51bWVyaWMgdmVjdG9yIG9mIGxlbmd0aCBvbmUuIElmIHdlIHdhbnRlZCBhIG51bWVyaWMgdmVjdG9yLCB3ZSBjb3VsZCB1c2UgbWFwIGZvbGxvd2VkIGJ5IGZsYXR0ZW5fZGJsDQoNCmBgYHtyfQ0KZmxhdHRlbl9kYmwobWFwKC0yOjIsIHJub3JtLCBuID0gNSkpDQoNCmBgYA0KDQpSZXdyaXRlIG1hcCh4LCBmdW5jdGlvbihkZikgbG0obXBnIH4gd3QsIGRhdGEgPSBkZikpIHRvIGVsaW1pbmF0ZSB0aGUgYW5vbnltb3VzIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCm1hcChsaXN0KG10Y2FycyksIH4gbG0obXBnIH4gd3QsIGRhdGEgPSAuKSkNCmBgYA0KDQo8aDI+IERlYWxpbmcgd2l0aCBGYWlsdXJlIDwvaDI+DQoNCldoZW4geW91IHVzZSB0aGUgbWFwIGZ1bmN0aW9ucyB0byByZXBlYXQgbWFueSBvcGVyYXRpb25zLCB0aGUgY2hhbmNlcyBhcmUgbXVjaCBoaWdoZXIgdGhhdCBvbmUgb2YgdGhvc2Ugb3BlcmF0aW9ucyB3aWxsIGZhaWwuIFdoZW4gdGhpcyBoYXBwZW5zLCB5b3UnbGwgZ2V0IGFuIGVycm9yIG1lc3NhZ2UsIGFuZCBubyBvdXRwdXQuIFRoaXMgaXMgYW5ub3lpbmc6IHdoeSBkb2VzIG9uZSBmYWlsdXJlIHByZXZlbnQgeW91IGZyb20gYWNjZXNzaW5nIGFsbCB0aGUgb3RoZXIgc3VjY2Vzc2VzPyBIb3cgZG8geW91IGVuc3VyZSB0aGF0IG9uZSBiYWQgYXBwbGUgZG9lc24ndCBydWluIHRoZSB3aG9sZSBiYXJyZWw/IDwvcD4NCg0KSW4gdGhpcyBzZWN0aW9uIHlvdSdsbCBsZWFybiBob3cgdG8gZGVhbCB0aGlzIHNpdHVhdGlvbiB3aXRoIGEgbmV3IGZ1bmN0aW9uOiBzYWZlbHkoKS4gc2FmZWx5KCkgaXMgYW4gYWR2ZXJiOiBpdCB0YWtlcyBhIGZ1bmN0aW9uIChhIHZlcmIpIGFuZCByZXR1cm5zIGEgbW9kaWZpZWQgdmVyc2lvbi4gSW4gdGhpcyBjYXNlLCB0aGUgbW9kaWZpZWQgZnVuY3Rpb24gd2lsbCBuZXZlciB0aHJvdyBhbiBlcnJvci4gSW5zdGVhZCwgaXQgYWx3YXlzIHJldHVybnMgYSBsaXN0IHdpdGggdHdvIGVsZW1lbnRzOiA8L2JyPg0KDQpyZXN1bHQgaXMgdGhlIG9yaWdpbmFsIHJlc3VsdC4gSWYgdGhlcmUgd2FzIGFuIGVycm9yLCB0aGlzIHdpbGwgYmUgTlVMTC4gPC9icj4NCg0KZXJyb3IgaXMgYW4gZXJyb3Igb2JqZWN0LiBJZiB0aGUgb3BlcmF0aW9uIHdhcyBzdWNjZXNzZnVsLCB0aGlzIHdpbGwgYmUgTlVMTCA8L2JyPg0KDQpMZXQncyBpbGx1c3RyYXRlIHRoaXMgd2l0aCBhIHNpbXBsZSBleGFtcGxlOiBsb2coKToNCg0KYGBge3J9DQpzYWZlX2xvZyA8LSBzYWZlbHkobG9nKQ0Kc3RyKHNhZmVfbG9nKDEwKSkNCnN0cihzYWZlX2xvZygiYSIpKQ0KYGBgDQoNCldoZW4gdGhlIGZ1bmN0aW9uIHN1Y2NlZWRzLCB0aGUgcmVzdWx0IGVsZW1lbnQgY29udGFpbnMgdGhlIHJlc3VsdCBhbmQgdGhlIGVycm9yIGVsZW1lbnQgaXMgTlVMTC4gV2hlbiB0aGUgZnVuY3Rpb24gZmFpbHMsIHRoZSByZXN1bHQgZWxlbWVudCBpcyBOVUxMIGFuZCB0aGUgZXJyb3IgZWxlbWVudCBjb250YWlucyBhbiBlcnJvciBvYmplY3QuIDwvcD4NCnNhZmVseSgpIGlzIGRlc2lnbmVkIHRvIHdvcmsgd2l0aCBtYXA6DQoNCmBgYHtyfQ0KeCA8LSBsaXN0KDEsIDEwLCAiYSIpDQp5IDwtIHggJT4lIG1hcChzYWZlbHkobG9nKSkNCnN0cih5KQ0KYGBgDQoNClRoaXMgd291bGQgYmUgZWFzaWVyIHRvIHdvcmsgd2l0aCBpZiB3ZSBoYWQgdHdvIGxpc3RzOiBvbmUgb2YgYWxsIHRoZSBlcnJvcnMgYW5kIG9uZSBvZiBhbGwgdGhlIG91dHB1dC4gVGhhdCdzIGVhc3kgdG8gZ2V0IHdpdGggcHVycnI6OnRyYW5zcG9zZSgpOg0KDQpgYGB7cn0NCnkgPC0geSAlPiUgdHJhbnNwb3NlKCkNCnN0cih5KQ0KDQpgYGANCg0KDQpJdCdzIHVwIHRvIHlvdSBob3cgdG8gZGVhbCB3aXRoIHRoZSBlcnJvcnMsIGJ1dCB0eXBpY2FsbHkgeW91J2xsIGVpdGhlciBsb29rIGF0IHRoZSB2YWx1ZXMgb2YgeCB3aGVyZSB5IGlzIGFuIGVycm9yLCBvciB3b3JrIHdpdGggdGhlIHZhbHVlcyBvZiB5IHRoYXQgYXJlIG9rOg0KDQpgYGB7cn0NCmlzX29rIDwtIHkkZXJyb3IgJT4lIG1hcF9sZ2woaXNfbnVsbCkNCnhbIWlzX29rXQ0KeSRyZXN1bHRbaXNfb2tdICU+JSBmbGF0dGVuX2RibCgpDQpgYGANCg0KUHVycnIgcHJvdmlkZXMgdHdvIG90aGVyIHVzZWZ1bCBhZHZlcmJzOiA8L2JyPg0KDQpMaWtlIHNhZmVseSgpLCBwb3NzaWJseSgpIGFsd2F5cyBzdWNjZWVkcy4gSXQncyBzaW1wbGVyIHRoYW4gc2FmZWx5KCksIGJlY2F1c2UgeW91IGdpdmUgaXQgYSBkZWZhdWx0IHZhbHVlIHRvIHJldHVybiB3aGVuIHRoZXJlIGlzIGFuIGVycm9yLg0KYGBge3J9DQoNCnggPC0gbGlzdCgxLCAxMCwgImEiKQ0KeCAlPiUgbWFwX2RibChwb3NzaWJseShsb2csIE5BX3JlYWxfKSkNCmBgYA0KDQoNCnF1aWV0bHkoKSBwZXJmb3JtcyBhIHNpbWlsYXIgcm9sZSB0byBzYWZlbHkoKSwgYnV0IGluc3RlYWQgb2YgY2FwdHVyaW5nIGVycm9ycywgaXQgY2FwdHVyZXMgcHJpbnRlZCBvdXRwdXQsIG1lc3NhZ2VzLCBhbmQgd2FybmluZ3M6DQoNCmBgYHtyfQ0KeCA8LSBsaXN0KDEsIC0xKQ0KeCAlPiUgbWFwKHF1aWV0bHkobG9nKSkgJT4lIHN0cigpDQoNCmBgYA0KDQo8aDI+IE1hcHBpbmcgb3ZlciBtdWx0aXBsZSBhcmd1bWVudHMgPC9oMj4NClNvIGZhciB3ZSd2ZSBtYXBwZWQgYWxvbmcgYSBzaW5nbGUgaW5wdXQuIEJ1dCBvZnRlbiB5b3UgaGF2ZSBtdWx0aXBsZSByZWxhdGVkIGlucHV0cyB0aGF0IHlvdSBuZWVkIGl0ZXJhdGUgYWxvbmcgaW4gcGFyYWxsZWwuIFRoYXQncyB0aGUgam9iIG9mIHRoZSBtYXAyKCkgYW5kIHBtYXAoKSBmdW5jdGlvbnMuIEZvciBleGFtcGxlLCBpbWFnaW5lIHlvdSB3YW50IHRvIHNpbXVsYXRlIHNvbWUgcmFuZG9tIG5vcm1hbHMgd2l0aCBkaWZmZXJlbnQgbWVhbnMuIFlvdSBrbm93IGhvdyB0byBkbyB0aGF0IHdpdGggbWFwKCk6DQoNCmBgYHtyfQ0KbXUgPC0gbGlzdCg1LCAxMCwgLTMpDQptdSAlPiUgDQogIG1hcChybm9ybSwgbiA9IDUpICU+JSANCiAgc3RyKCkNCmBgYA0KV2hhdCBpZiB5b3UgYWxzbyB3YW50IHRvIHZhcnkgdGhlIHN0YW5kYXJkIGRldmlhdGlvbj8gT25lIHdheSB0byBkbyB0aGF0IHdvdWxkIGJlIHRvIGl0ZXJhdGUgb3ZlciB0aGUgaW5kaWNlcyBhbmQgaW5kZXggaW50byB2ZWN0b3JzIG9mIG1lYW5zIGFuZCBzZHM6DQoNCmBgYHtyfQ0Kc2lnbWEgPC0gbGlzdCgxLCA1LCAxMCkNCnNlcV9hbG9uZyhtdSkgJT4lIA0KICBtYXAofnJub3JtKDUsIG11W1suXV0sIHNpZ21hW1suXV0pKSAlPiUgDQogIHN0cigpDQoNCmBgYA0KDQpCdXQgdGhhdCBvYmZ1c2NhdGVzIHRoZSBpbnRlbnQgb2YgdGhlIGNvZGUuIEluc3RlYWQgd2UgY291bGQgdXNlIG1hcDIoKSB3aGljaCBpdGVyYXRlcyBvdmVyIHR3byB2ZWN0b3JzIGluIHBhcmFsbGVsOg0KDQoNCmBgYHtyfQ0KbWFwMihtdSwgc2lnbWEsIHJub3JtLCBuID0gNSkgJT4lIHN0cigpDQpgYGANCg0KbWFwMigpIGdlbmVyYXRlcyB0aGlzIHNlcmllcyBvZiBmdW5jdGlvbiBjYWxsczoNCjxpbWcgc3JjPSJodHRwOi8vcjRkcy5oYWQuY28ubnovZGlhZ3JhbXMvbGlzdHMtbWFwMi5wbmciIC8+DQoNCk5vdGUgdGhhdCB0aGUgYXJndW1lbnRzIHRoYXQgdmFyeSBmb3IgZWFjaCBjYWxsIGNvbWUgYmVmb3JlIHRoZSBmdW5jdGlvbjsgYXJndW1lbnRzIHRoYXQgYXJlIHRoZSBzYW1lIGZvciBldmVyeSBjYWxsIGNvbWUgYWZ0ZXIuIExpa2UgbWFwKCksIG1hcDIoKSBpcyBqdXN0IGEgd3JhcHBlciBhcm91bmQgYSBmb3IgbG9vcDoNCg0KYGBge3J9DQptYXAyIDwtIGZ1bmN0aW9uKHgsIHksIGYsIC4uLikgew0KICBvdXQgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoKHgpKQ0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKHgpKSB7DQogICAgb3V0W1tpXV0gPC0gZih4W1tpXV0sIHlbW2ldXSwgLi4uKQ0KICB9DQogIG91dA0KfQ0KYGBgDQoNCllvdSBjb3VsZCBhbHNvIGltYWdpbmUgbWFwMygpLCBtYXA0KCksIG1hcDUoKSwgbWFwNigpIGV0YywgYnV0IHRoYXQgd291bGQgZ2V0IHRlZGlvdXMgcXVpY2tseS4gSW5zdGVhZCwgcHVycnIgcHJvdmlkZXMgcG1hcCgpIHdoaWNoIHRha2VzIGEgbGlzdCBvZiBhcmd1bWVudHMuIFlvdSBtaWdodCB1c2UgdGhhdCBpZiB5b3Ugd2FudGVkIHRvIHZhcnkgdGhlIG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIG51bWJlciBvZiBzYW1wbGVzOg0KDQpgYGB7cn0NCm4gPC0gbGlzdCgxLCAzLCA1KQ0KYXJnczEgPC0gbGlzdChuLCBtdSwgc2lnbWEpDQphcmdzMSAlPiUNCiAgcG1hcChybm9ybSkgJT4lIA0KICBzdHIoKQ0KYGBgDQoNClRoYXQgbG9va3MgbGlrZToNCg0KPGltZyBzcmM9Imh0dHA6Ly9yNGRzLmhhZC5jby5uei9kaWFncmFtcy9saXN0cy1wbWFwLXVubmFtZWQucG5nIiAvPg0KDQpJZiB5b3UgZG9uJ3QgbmFtZSB0aGUgZWxlbWVudHMgb2YgbGlzdCwgcG1hcCgpIHdpbGwgdXNlIHBvc2l0aW9uYWwgbWF0Y2hpbmcgd2hlbiBjYWxsaW5nIHRoZSBmdW5jdGlvbi4gVGhhdCdzIGEgbGl0dGxlIGZyYWdpbGUsIGFuZCBtYWtlcyB0aGUgY29kZSBoYXJkZXIgdG8gcmVhZCwgc28gaXQncyBiZXR0ZXIgdG8gbmFtZSB0aGUgYXJndW1lbnRzOg0KDQpgYGB7cn0NCmFyZ3MyIDwtIGxpc3QobWVhbiA9IG11LCBzZCA9IHNpZ21hLCBuID0gbikNCmFyZ3MyICU+JSANCiAgcG1hcChybm9ybSkgJT4lIA0KICBzdHIoKQ0KYGBgDQoNClRoYXQgZ2VuZXJhdGVzIGxvbmdlciwgYnV0IHNhZmVyLCBjYWxsczoNCg0KPGltZyBzcmM9Imh0dHA6Ly9yNGRzLmhhZC5jby5uei9kaWFncmFtcy9saXN0cy1wbWFwLW5hbWVkLnBuZyIgLz4NCg0KU2luY2UgdGhlIGFyZ3VtZW50cyBhcmUgYWxsIHRoZSBzYW1lIGxlbmd0aCwgaXQgbWFrZXMgc2Vuc2UgdG8gc3RvcmUgdGhlbSBpbiBhIGRhdGEgZnJhbWU6DQoNCmBgYHtyfQ0KcGFyYW1zIDwtIHRyaWJibGUoDQogIH5tZWFuLCB+c2QsIH5uLA0KICAgIDUsICAgICAxLCAgMSwNCiAgIDEwLCAgICAgNSwgIDMsDQogICAtMywgICAgMTAsICA1DQopDQpwYXJhbXMgJT4lIA0KICBwbWFwKHJub3JtKQ0KDQpgYGANCg0KPGgzPiBJbnZva2luZyBEaWZmZXJlbnQgZnVuY3Rpb25zIDwvaDM+DQoNClRoZXJlJ3Mgb25lIG1vcmUgc3RlcCB1cCBpbiBjb21wbGV4aXR5IC0gYXMgd2VsbCBhcyB2YXJ5aW5nIHRoZSBhcmd1bWVudHMgdG8gdGhlIGZ1bmN0aW9uIHlvdSBtaWdodCBhbHNvIHZhcnkgdGhlIGZ1bmN0aW9uIGl0c2VsZjoNCg0KYGBge3J9DQpmIDwtIGMoInJ1bmlmIiwgInJub3JtIiwgInJwb2lzIikNCnBhcmFtIDwtIGxpc3QoDQogIGxpc3QobWluID0gLTEsIG1heCA9IDEpLCANCiAgbGlzdChzZCA9IDUpLCANCiAgbGlzdChsYW1iZGEgPSAxMCkNCikNCg0KYGBgDQpUbyBoYW5kbGUgdGhpcyBjYXNlLCB5b3UgY2FuIHVzZSBpbnZva2VfbWFwKCk6DQoNCmBgYHtyfQ0KaW52b2tlX21hcChmLCBwYXJhbSwgbiA9IDUpICU+JSBzdHIoKQ0KYGBgDQoNCjxpbWcgc3JjPSJodHRwOi8vcjRkcy5oYWQuY28ubnovZGlhZ3JhbXMvbGlzdHMtaW52b2tlLnBuZyIgLz4NCg0KVGhlIGZpcnN0IGFyZ3VtZW50IGlzIGEgbGlzdCBvZiBmdW5jdGlvbnMgb3IgY2hhcmFjdGVyIHZlY3RvciBvZiBmdW5jdGlvbiBuYW1lcy4gVGhlIHNlY29uZCBhcmd1bWVudCBpcyBhIGxpc3Qgb2YgbGlzdHMgZ2l2aW5nIHRoZSBhcmd1bWVudHMgdGhhdCB2YXJ5IGZvciBlYWNoIGZ1bmN0aW9uLiBUaGUgc3Vic2VxdWVudCBhcmd1bWVudHMgYXJlIHBhc3NlZCBvbiB0byBldmVyeSBmdW5jdGlvbi4NCg0KQW5kIGFnYWluLCB5b3UgY2FuIHVzZSB0cmliYmxlKCkgdG8gbWFrZSBjcmVhdGluZyB0aGVzZSBtYXRjaGluZyBwYWlycyBhIGxpdHRsZSBlYXNpZXI6