Overview

This is a reproduction of the the Functional Programming chapter, which comes from Chapter 10 of Advanced R. The purpose was to learn the concepts. I deviate a little from the book to assist in my learning. Just note that what is here has some differences than the chapter, mostly extending it for my learning benefit. I skipped the Numericial Integration section, so refer to the book if interested.

Prerequisites

library(tidyverse)
library(pryr)

Motivation

Many advantages to functional programming including:

Adaptability

Example is a function that can adapt depending on needs of user.

missing_fixer <- function(na_value) {
    function(x) {
        x[x == na_value] <- NA
        x
    }
}

Now, can adapt the function depending on the needs.

fix_missing_99 <- missing_fixer(-99)
fix_missing_99(c(-99, -999))
[1]   NA -999
fix_missing_999 <- missing_fixer(-999)
fix_missing_999(c(-99, -999))
[1] -99  NA

Reduction in Duplication

Another use is to reduce duplication. The code below loops through a list of functions using the lapply() function. We use lapply() to reduce calls to each individual function and an anonymous function to reduce the number of arguments (e.g. na.rm = TRUE statements). Note how each function in funs gets passed as a function f.

summary2 <- function(x) {
    funs <- c(mean = mean, median = median, sd = sd, mad = mad, IQR = IQR)
    lapply(funs, function(f) f(x, na.rm = TRUE))
}

Here’s the result:

mtcars$mpg %>%
    summary2() %>% 
    unlist()
     mean    median        sd       mad       IQR 
20.090625 19.200000  6.026948  5.411490  7.375000 

Anonymous Functions

Useful when it’s not worth the effort to give a function a name.

mtcars %>%
    lapply(function(x) x %>% unique() %>% length()) %>%
    unlist()
 mpg  cyl disp   hp drat   wt qsec   vs   am gear carb 
  25    3   27   22   22   29   30    2    2    3    6 

Structure of Functions

All functions have formals(), body() and parent environment().

formals(function(x = 4) g(x) + h(x))
$x
[1] 4
body(function(x = 4) g(x) + h(x))
g(x) + h(x)
environment(function(x = 4) g(x) + h(x))
<environment: R_GlobalEnv>

Use of Parenthesis

See how can replace the function with an anonymous function with the use of parenthesis.

f <- function(x) x + 3
f(2)
[1] 5

Replace f with (function(X) x + 3):

(function(x) x + 3)(2)
[1] 5

Exercises

  1. Given a function name, match.fun() lets you find the function. Given a function, can you find its name? Why doesn’t that make sense in R?

Get function from a character string using match.fun():

match.fun("mean")
function (x, ...) 
UseMethod("mean")
<bytecode: 0x0000000015e4d358>
<environment: namespace:base>

Convert function to a character string using quote() and as.character():

quote(mean) %>% 
    as.character()
[1] "mean"
  1. Use lapply() and an anonymous function to find the coefficient of variation for all the columns of mtcars.
mtcars %>%
    lapply(function(x) mean(x) / sd(x)) %>%
    unlist() %>%
    round(2)
 mpg  cyl disp   hp drat   wt qsec   vs   am gear carb 
3.33 3.46 1.86 2.14 6.73 3.29 9.99 0.87 0.81 5.00 1.74 
  1. Use integrate() and an anonymous function to find the area under the curve.
integrate(function(x) x ^ 2 - x, 0, 10)
283.3333 with absolute error < 3.1e-12
integrate(function(x) sin(x) - cos(x), -pi, pi)
-2.615901e-16 with absolute error < 6.3e-14
integrate(function(x) exp(x) / x, 10, 20)
25613160 with absolute error < 2.8e-07

Closures

Closures are functions written by functions. Closures get their name because they enclose the environment of the parent function and can access all of its variables. This is useful because it allows two levels of parameters:

  1. parent level that controls operation
  2. child level that does the work
power2 <- function(exponent) {
    function(x) {
        x ^ exponent
    }
}

Can use the closure, power2 to create functions square() and cube().

square <- power2(2)
square(4)
[1] 16
cube <- power2(3)
cube(4)
[1] 64

Use pryr::unenclose() to see what’s going on in a Closure, which replaces variables defined in the enclosing environment with values.

cube
function(x) {
        x ^ exponent
    }
<environment: 0x000000001599d648>

See the difference:

unenclose(cube)
function (x) 
{
    x^3
}

Function Factories

Use to create many versions of the same general function. power2() and missing_fixer() are examples. Function factories are most useful to solve problems like maximum likelihood.

Mutable State

Can use the double arrow assignment operator (<<-) to maintain state across function calls.

Can create a new_counter() closure function, which is used to create :

new_counter <- function() {
    i <- 0
    function() {
        i <<- i + 1
        i
    }
}
counter_one <- new_counter()
counter_two <- new_counter()

Because counter_one() and counter_two() each get their own enclosing environments, they can keep track of their own respective counts.

counter_one()
[1] 1
counter_one()
[1] 2
counter_two()
[1] 1

The counters get around the “fresh start” limitation by not modifying variables in their local environment but rather making changes in the parent environment. The parent environment changes are preserved across function calls.

Exercises

  1. Why are functions created by other functions called closures?

Because the parent function encloses the child function and can access the variables in the parent’s environment.

  1. What does the following statistical function do? What would be a better name for it?
bc <- function(lambda) {
    if (lambda == 0) {
        function(x) log(x)
    } else {
        function(x) (x ^ lambda - 1) / lambda
    }
}

This function is the Box-Cox transformation equation. A better name might be new_box_cox. It can be used to set Box-Cox transformations:

new_box_cox <- bc

We can create many iterations of the Box-Cox transformation by varying lambda.

bc_0 <- new_box_cox(0)
bc_0(10)
[1] 2.302585
bc_0.5 <- new_box_cox(0.5)
bc_0.5(10)
[1] 4.324555
bc_1 <- new_box_cox(1)
bc_1(10)
[1] 9
  1. What does approxfun() do?

Performs interpolation on a set of values between points in a data set. The function returned can be plotted as a curve to get values in between the data set points.

set.seed(198)
x <- 1:10
y <- rnorm(10)
f <- approxfun(x, y)
plot(f, 0, 11, col = "tomato", add = TRUE, lty = 3, lwd = 2)
points(x, y, col = 1, pch = "*")
points(x = 2.5, y = f(2.5), col = "orange", pch = 6) # Interpolated point

Can use the function, f, to interpolate between the points at x = 2 and x = 3.

f(2.5)
[1] -0.4438546
  1. What does ecdf() do?

Stands for empirical cumulative distribution function. ecdf() ties the values in a list to the cumulative distribution that the value falls into. The function returned can be used to compute the cumulative distribution that the point falls into.

set.seed(12)
x <- rnorm(50)
Fn <- ecdf(x)

If we want to get the cumulative distribution of a new point:

Fn(0)
[1] 0.58
  1. Create a function that creates functions that compute the ith central moment of a numeric vector.
moment <- function(n) {
    function(x) {
        sum((x - mean(x)) ^ n) / length(x)
    }
}

Run Hadley’s code:

m1 <- moment(1)
m2 <- moment(2)
x <- runif(100)
stopifnot(all.equal(m1(x), 0))
stopifnot(all.equal(m2(x), var(x) * 99 / 100))

It works.

  1. Create a function pick() that takes an index, i, as an argument and returns a function with an argument x that subsets x with i.
pick <- function(i) {
    function(x) x[[i]]
}

Run code:

lapply(mtcars, pick(5)) %>% unlist()
   mpg    cyl   disp     hp   drat     wt   qsec     vs     am   gear   carb 
 18.70   8.00 360.00 175.00   3.15   3.44  17.02   0.00   0.00   3.00   2.00 

Check results:

lapply(mtcars, function(x) x[[5]]) %>% unlist()
   mpg    cyl   disp     hp   drat     wt   qsec     vs     am   gear   carb 
 18.70   8.00 360.00 175.00   3.15   3.44  17.02   0.00   0.00   3.00   2.00 

Lists of Functions

Can store functions in lists, which reduces redundancy.

x <- 1:10
funs <- list(
    sum = sum,
    mean = mean,
    median = median
)
lapply(funs, function(f) f(x, na.rm = TRUE)) %>%
    unlist()
   sum   mean median 
  55.0    5.5    5.5 

Exercises

  1. Implement a summary function that works like base::summary(), but uses a list of functions. Modify the function so it returns a closure, making it possible to use it as a function factory.

Here’s the function we need to replicate:

x <- 1:10
summary(x)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1.00    3.25    5.50    5.50    7.75   10.00 

First, recreate base::summary():

summary2 <- function(x) {
    funs <- list(
        "Min."    = min,
        "1st Qu." = function(x) quantile(x, 0.25)[[1]],
        "Median"  = median,
        "Mean"    = mean,
        "3rd Qu." = function(x) quantile(x, 0.75)[[1]],
        "Max"     = max
    )
    lapply(funs, function(f) f(x)) %>%
        unlist()
}
summary2(x)
   Min. 1st Qu.  Median    Mean 3rd Qu.     Max 
   1.00    3.25    5.50    5.50    7.75   10.00 

Now, make a function factory:

make_summary <- function( 
    funs = list(
        "Min."    = min,
        "1st Qu." = function(x) quantile(x, 0.25)[[1]],
        "Median"  = median,
        "Mean"    = mean,
        "3rd Qu." = function(x) quantile(x, 0.75)[[1]],
        "Max"     = max)
    ) {
    function(x) {
        lapply(funs, function(f) f(x)) %>%
            unlist()
        }
    }

We can now use the closure to replicate the base::summary() function…

summary2 <- make_summary()
summary2(x)
   Min. 1st Qu.  Median    Mean 3rd Qu.     Max 
   1.00    3.25    5.50    5.50    7.75   10.00 

…or we can give it our own custom functions to run.

summary3 <- make_summary(funs = list(Mean = mean, 
                                     Std.Dev = sd, 
                                     Quantile = quantile,
                                     IQR = IQR))
summary3(x)
         Mean       Std.Dev   Quantile.0%  Quantile.25%  Quantile.50%  Quantile.75% 
      5.50000       3.02765       1.00000       3.25000       5.50000       7.75000 
Quantile.100%           IQR 
     10.00000       4.50000 

We can even apply our new summary function to a data frame:

lapply(mtcars[,1:4], summary3)
$mpg
         Mean       Std.Dev   Quantile.0%  Quantile.25%  Quantile.50%  Quantile.75% 
    20.090625      6.026948     10.400000     15.425000     19.200000     22.800000 
Quantile.100%           IQR 
    33.900000      7.375000 

$cyl
         Mean       Std.Dev   Quantile.0%  Quantile.25%  Quantile.50%  Quantile.75% 
     6.187500      1.785922      4.000000      4.000000      6.000000      8.000000 
Quantile.100%           IQR 
     8.000000      4.000000 

$disp
         Mean       Std.Dev   Quantile.0%  Quantile.25%  Quantile.50%  Quantile.75% 
     230.7219      123.9387       71.1000      120.8250      196.3000      326.0000 
Quantile.100%           IQR 
     472.0000      205.1750 

$hp
         Mean       Std.Dev   Quantile.0%  Quantile.25%  Quantile.50%  Quantile.75% 
    146.68750      68.56287      52.00000      96.50000     123.00000     180.00000 
Quantile.100%           IQR 
    335.00000      83.50000 

Case Study: Numerical Integration

Section skipped

LS0tDQp0aXRsZTogJ0NoYXB0ZXIgMTA6IEZ1bmN0aW9uYWwgUHJvZ3JhbW1pbmcnDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdGhlbWU6IGZsYXRseQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAzDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdGhlbWU6IGZsYXRseQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAzDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogJzMnDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGtuaXRyKQ0Kb3B0c19jaHVuayRzZXQoZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9MywgZmlnLmFsaWduPSdjZW50ZXInLA0KICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KIyBPdmVydmlldw0KDQpUaGlzIGlzIGEgcmVwcm9kdWN0aW9uIG9mIHRoZSB0aGUgX0Z1bmN0aW9uYWwgUHJvZ3JhbW1pbmdfIGNoYXB0ZXIsIHdoaWNoIGNvbWVzIGZyb20gX0NoYXB0ZXIgMTBfIG9mIF9BZHZhbmNlZCBSXy4gVGhlIHB1cnBvc2Ugd2FzIHRvIGxlYXJuIHRoZSBjb25jZXB0cy4gSSBkZXZpYXRlIGEgbGl0dGxlIGZyb20gdGhlIGJvb2sgdG8gYXNzaXN0IGluIG15IGxlYXJuaW5nLiBKdXN0IG5vdGUgdGhhdCB3aGF0IGlzIGhlcmUgaGFzIHNvbWUgZGlmZmVyZW5jZXMgdGhhbiB0aGUgY2hhcHRlciwgbW9zdGx5IGV4dGVuZGluZyBpdCBmb3IgbXkgbGVhcm5pbmcgYmVuZWZpdC4gSSBza2lwcGVkIHRoZSBfTnVtZXJpY2lhbCBJbnRlZ3JhdGlvbl8gc2VjdGlvbiwgc28gcmVmZXIgdG8gdGhlIGJvb2sgaWYgaW50ZXJlc3RlZC4NCg0KIyBQcmVyZXF1aXNpdGVzDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHByeXIpDQpgYGANCg0KDQojIE1vdGl2YXRpb24NCg0KTWFueSBhZHZhbnRhZ2VzIHRvIGZ1bmN0aW9uYWwgcHJvZ3JhbW1pbmcgaW5jbHVkaW5nOg0KDQogICogTW9yZSBjb21wYWN0IGNvZGUNCiAgKiBXb3JrcyB1bmRlciBtYW55IHNjZW5hcmlvcw0KICAqIEVhc3kgdG8gZ2VuZXJhbGl6ZSB0byBzdWJzZXRzIG9mIGRhdGENCiAgKiBWZXJ5IHVzZWZ1bCBmb3IgY2xlYW5pbmcgYW5kIHN1bW1hcml6aW5nIGRhdGEgcHJpb3IgdG8gYW5hbHlzaXMNCg0KIyMgQWRhcHRhYmlsaXR5DQoNCkV4YW1wbGUgaXMgYSBmdW5jdGlvbiB0aGF0IGNhbiBhZGFwdCBkZXBlbmRpbmcgb24gbmVlZHMgb2YgdXNlci4gDQoNCmBgYHtyfQ0KbWlzc2luZ19maXhlciA8LSBmdW5jdGlvbihuYV92YWx1ZSkgew0KICAgIGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgeFt4ID09IG5hX3ZhbHVlXSA8LSBOQQ0KICAgICAgICB4DQogICAgfQ0KfQ0KYGBgDQoNCk5vdywgY2FuIGFkYXB0IHRoZSBmdW5jdGlvbiBkZXBlbmRpbmcgb24gdGhlIG5lZWRzLg0KDQpgYGB7cn0NCmZpeF9taXNzaW5nXzk5IDwtIG1pc3NpbmdfZml4ZXIoLTk5KQ0KZml4X21pc3NpbmdfOTkoYygtOTksIC05OTkpKQ0KYGBgDQoNCmBgYHtyfQ0KZml4X21pc3NpbmdfOTk5IDwtIG1pc3NpbmdfZml4ZXIoLTk5OSkNCmZpeF9taXNzaW5nXzk5OShjKC05OSwgLTk5OSkpDQpgYGANCg0KIyMgUmVkdWN0aW9uIGluIER1cGxpY2F0aW9uDQoNCkFub3RoZXIgdXNlIGlzIHRvIHJlZHVjZSBkdXBsaWNhdGlvbi4gVGhlIGNvZGUgYmVsb3cgbG9vcHMgdGhyb3VnaCBhIGxpc3Qgb2YgZnVuY3Rpb25zIHVzaW5nIHRoZSBgbGFwcGx5KClgIGZ1bmN0aW9uLiBXZSB1c2UgYGxhcHBseSgpYCB0byByZWR1Y2UgY2FsbHMgdG8gZWFjaCBpbmRpdmlkdWFsIGZ1bmN0aW9uIGFuZCBhbiBhbm9ueW1vdXMgZnVuY3Rpb24gdG8gcmVkdWNlIHRoZSBudW1iZXIgb2YgYXJndW1lbnRzIChlLmcuIGBuYS5ybSA9IFRSVUVgIHN0YXRlbWVudHMpLiBOb3RlIGhvdyBlYWNoIGZ1bmN0aW9uIGluIGBmdW5zYCBnZXRzIHBhc3NlZCBhcyBhIGZ1bmN0aW9uIGBmYC4gDQoNCmBgYHtyfQ0Kc3VtbWFyeTIgPC0gZnVuY3Rpb24oeCkgew0KICAgIGZ1bnMgPC0gYyhtZWFuID0gbWVhbiwgbWVkaWFuID0gbWVkaWFuLCBzZCA9IHNkLCBtYWQgPSBtYWQsIElRUiA9IElRUikNCiAgICBsYXBwbHkoZnVucywgZnVuY3Rpb24oZikgZih4LCBuYS5ybSA9IFRSVUUpKQ0KfQ0KYGBgDQoNCkhlcmUncyB0aGUgcmVzdWx0Og0KDQpgYGB7cn0NCm10Y2FycyRtcGcgJT4lDQogICAgc3VtbWFyeTIoKSAlPiUgDQogICAgdW5saXN0KCkNCmBgYA0KDQojIEFub255bW91cyBGdW5jdGlvbnMNCg0KVXNlZnVsIHdoZW4gaXQncyBub3Qgd29ydGggdGhlIGVmZm9ydCB0byBnaXZlIGEgZnVuY3Rpb24gYSBuYW1lLg0KDQpgYGB7cn0NCm10Y2FycyAlPiUNCiAgICBsYXBwbHkoZnVuY3Rpb24oeCkgeCAlPiUgdW5pcXVlKCkgJT4lIGxlbmd0aCgpKSAlPiUNCiAgICB1bmxpc3QoKQ0KYGBgDQoNCg0KIyMgU3RydWN0dXJlIG9mIEZ1bmN0aW9ucw0KDQpBbGwgZnVuY3Rpb25zIGhhdmUgYGZvcm1hbHMoKWAsIGBib2R5KClgIGFuZCBwYXJlbnQgYGVudmlyb25tZW50KClgLg0KDQpgYGB7cn0NCmZvcm1hbHMoZnVuY3Rpb24oeCA9IDQpIGcoeCkgKyBoKHgpKQ0KYGBgDQoNCmBgYHtyfQ0KYm9keShmdW5jdGlvbih4ID0gNCkgZyh4KSArIGgoeCkpDQpgYGANCg0KYGBge3J9DQplbnZpcm9ubWVudChmdW5jdGlvbih4ID0gNCkgZyh4KSArIGgoeCkpDQpgYGANCg0KIyMgVXNlIG9mIFBhcmVudGhlc2lzDQoNClNlZSBob3cgY2FuIHJlcGxhY2UgdGhlIGZ1bmN0aW9uIHdpdGggYW4gYW5vbnltb3VzIGZ1bmN0aW9uIHdpdGggdGhlIHVzZSBvZiBwYXJlbnRoZXNpcy4NCg0KYGBge3J9DQpmIDwtIGZ1bmN0aW9uKHgpIHggKyAzDQpmKDIpDQpgYGANCg0KUmVwbGFjZSBgZmAgd2l0aCBgKGZ1bmN0aW9uKFgpIHggKyAzKWA6DQoNCmBgYHtyfQ0KKGZ1bmN0aW9uKHgpIHggKyAzKSgyKQ0KYGBgDQoNCiMjIEV4ZXJjaXNlcw0KDQoxLiBHaXZlbiBhIGZ1bmN0aW9uIG5hbWUsIGBtYXRjaC5mdW4oKWAgbGV0cyB5b3UgZmluZCB0aGUgZnVuY3Rpb24uIEdpdmVuIGEgZnVuY3Rpb24sIGNhbiB5b3UgZmluZCBpdHMgbmFtZT8gV2h5IGRvZXNuJ3QgdGhhdCBtYWtlIHNlbnNlIGluIFI/DQoNCkdldCBmdW5jdGlvbiBmcm9tIGEgY2hhcmFjdGVyIHN0cmluZyB1c2luZyBgbWF0Y2guZnVuKClgOg0KDQpgYGB7cn0NCm1hdGNoLmZ1bigibWVhbiIpDQpgYGANCg0KQ29udmVydCBmdW5jdGlvbiB0byBhIGNoYXJhY3RlciBzdHJpbmcgdXNpbmcgYHF1b3RlKClgIGFuZCBgYXMuY2hhcmFjdGVyKClgOg0KDQpgYGB7cn0NCnF1b3RlKG1lYW4pICU+JSANCiAgICBhcy5jaGFyYWN0ZXIoKQ0KYGBgDQoNCg0KMi4gVXNlIGBsYXBwbHkoKWAgYW5kIGFuIGFub255bW91cyBmdW5jdGlvbiB0byBmaW5kIHRoZSBjb2VmZmljaWVudCBvZiB2YXJpYXRpb24gZm9yIGFsbCB0aGUgY29sdW1ucyBvZiBgbXRjYXJzYC4NCg0KYGBge3J9DQptdGNhcnMgJT4lDQogICAgbGFwcGx5KGZ1bmN0aW9uKHgpIG1lYW4oeCkgLyBzZCh4KSkgJT4lDQogICAgdW5saXN0KCkgJT4lDQogICAgcm91bmQoMikNCmBgYA0KDQozLiBVc2UgYGludGVncmF0ZSgpYCBhbmQgYW4gYW5vbnltb3VzIGZ1bmN0aW9uIHRvIGZpbmQgdGhlIGFyZWEgdW5kZXIgdGhlIGN1cnZlLg0KDQpgYGB7cn0NCmludGVncmF0ZShmdW5jdGlvbih4KSB4IF4gMiAtIHgsIDAsIDEwKQ0KYGBgDQoNCmBgYHtyfQ0KaW50ZWdyYXRlKGZ1bmN0aW9uKHgpIHNpbih4KSAtIGNvcyh4KSwgLXBpLCBwaSkNCmBgYA0KDQpgYGB7cn0NCmludGVncmF0ZShmdW5jdGlvbih4KSBleHAoeCkgLyB4LCAxMCwgMjApDQpgYGANCg0KIyBDbG9zdXJlcw0KDQpDbG9zdXJlcyBhcmUgZnVuY3Rpb25zIHdyaXR0ZW4gYnkgZnVuY3Rpb25zLiBDbG9zdXJlcyBnZXQgdGhlaXIgbmFtZSBiZWNhdXNlIHRoZXkgZW5jbG9zZSB0aGUgZW52aXJvbm1lbnQgb2YgdGhlIHBhcmVudCBmdW5jdGlvbiBhbmQgY2FuIGFjY2VzcyBhbGwgb2YgaXRzIHZhcmlhYmxlcy4gVGhpcyBpcyB1c2VmdWwgYmVjYXVzZSBpdCBhbGxvd3MgdHdvIGxldmVscyBvZiBwYXJhbWV0ZXJzOg0KDQoxLiBwYXJlbnQgbGV2ZWwgdGhhdCBjb250cm9scyBvcGVyYXRpb24NCjIuIGNoaWxkIGxldmVsIHRoYXQgZG9lcyB0aGUgd29yaw0KDQpgYGB7cn0NCnBvd2VyMiA8LSBmdW5jdGlvbihleHBvbmVudCkgew0KICAgIGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgeCBeIGV4cG9uZW50DQogICAgfQ0KfQ0KYGBgDQoNCkNhbiB1c2UgdGhlIGNsb3N1cmUsIGBwb3dlcjJgIHRvIGNyZWF0ZSBmdW5jdGlvbnMgYHNxdWFyZSgpYCBhbmQgYGN1YmUoKWAuIA0KDQpgYGB7cn0NCnNxdWFyZSA8LSBwb3dlcjIoMikNCnNxdWFyZSg0KQ0KYGBgDQoNCmBgYHtyfQ0KY3ViZSA8LSBwb3dlcjIoMykNCmN1YmUoNCkNCmBgYA0KDQpVc2UgYHByeXI6OnVuZW5jbG9zZSgpYCB0byBzZWUgd2hhdCdzIGdvaW5nIG9uIGluIGEgQ2xvc3VyZSwgd2hpY2ggcmVwbGFjZXMgdmFyaWFibGVzIGRlZmluZWQgaW4gdGhlIGVuY2xvc2luZyBlbnZpcm9ubWVudCB3aXRoIHZhbHVlcy4NCg0KYGBge3J9DQpjdWJlDQpgYGANCg0KU2VlIHRoZSBkaWZmZXJlbmNlOg0KDQpgYGB7cn0NCnVuZW5jbG9zZShjdWJlKQ0KYGBgDQoNCiMjIEZ1bmN0aW9uIEZhY3Rvcmllcw0KDQpVc2UgdG8gY3JlYXRlIG1hbnkgdmVyc2lvbnMgb2YgdGhlIHNhbWUgZ2VuZXJhbCBmdW5jdGlvbi4gYHBvd2VyMigpYCBhbmQgYG1pc3NpbmdfZml4ZXIoKWAgYXJlIGV4YW1wbGVzLiBGdW5jdGlvbiBmYWN0b3JpZXMgYXJlIG1vc3QgdXNlZnVsIHRvIHNvbHZlIHByb2JsZW1zIGxpa2UgbWF4aW11bSBsaWtlbGlob29kLiANCg0KIyMgTXV0YWJsZSBTdGF0ZQ0KDQpDYW4gdXNlIHRoZSBkb3VibGUgYXJyb3cgYXNzaWdubWVudCBvcGVyYXRvciAoYDw8LWApIHRvIG1haW50YWluIHN0YXRlIGFjcm9zcyBmdW5jdGlvbiBjYWxscy4gDQoNCkNhbiBjcmVhdGUgYSBgbmV3X2NvdW50ZXIoKWAgY2xvc3VyZSBmdW5jdGlvbiwgd2hpY2ggaXMgdXNlZCB0byBjcmVhdGUgOg0KDQpgYGB7cn0NCm5ld19jb3VudGVyIDwtIGZ1bmN0aW9uKCkgew0KICAgIGkgPC0gMA0KICAgIGZ1bmN0aW9uKCkgew0KICAgICAgICBpIDw8LSBpICsgMQ0KICAgICAgICBpDQogICAgfQ0KfQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmNvdW50ZXJfb25lIDwtIG5ld19jb3VudGVyKCkNCmNvdW50ZXJfdHdvIDwtIG5ld19jb3VudGVyKCkNCmBgYA0KDQpCZWNhdXNlIGBjb3VudGVyX29uZSgpYCBhbmQgYGNvdW50ZXJfdHdvKClgIGVhY2ggZ2V0IHRoZWlyIG93biBlbmNsb3NpbmcgZW52aXJvbm1lbnRzLCB0aGV5IGNhbiBrZWVwIHRyYWNrIG9mIHRoZWlyIG93biByZXNwZWN0aXZlIGNvdW50cy4NCg0KYGBge3J9DQpjb3VudGVyX29uZSgpDQpjb3VudGVyX29uZSgpDQpjb3VudGVyX3R3bygpDQpgYGANCg0KVGhlIGNvdW50ZXJzIGdldCBhcm91bmQgdGhlICJmcmVzaCBzdGFydCIgbGltaXRhdGlvbiBieSBub3QgbW9kaWZ5aW5nIHZhcmlhYmxlcyBpbiB0aGVpciBsb2NhbCBlbnZpcm9ubWVudCBidXQgcmF0aGVyIG1ha2luZyBjaGFuZ2VzIGluIHRoZSBwYXJlbnQgZW52aXJvbm1lbnQuIFRoZSBwYXJlbnQgZW52aXJvbm1lbnQgY2hhbmdlcyBhcmUgX19wcmVzZXJ2ZWRfXyBhY3Jvc3MgZnVuY3Rpb24gY2FsbHMuDQoNCiMjIEV4ZXJjaXNlcw0KDQoxLiBXaHkgYXJlIGZ1bmN0aW9ucyBjcmVhdGVkIGJ5IG90aGVyIGZ1bmN0aW9ucyBjYWxsZWQgY2xvc3VyZXM/DQoNCkJlY2F1c2UgdGhlIHBhcmVudCBmdW5jdGlvbiBlbmNsb3NlcyB0aGUgY2hpbGQgZnVuY3Rpb24gYW5kIGNhbiBhY2Nlc3MgdGhlIHZhcmlhYmxlcyBpbiB0aGUgcGFyZW50J3MgZW52aXJvbm1lbnQuDQoNCjIuIFdoYXQgZG9lcyB0aGUgZm9sbG93aW5nIHN0YXRpc3RpY2FsIGZ1bmN0aW9uIGRvPyBXaGF0IHdvdWxkIGJlIGEgYmV0dGVyIG5hbWUgZm9yIGl0Pw0KDQpgYGB7cn0NCmJjIDwtIGZ1bmN0aW9uKGxhbWJkYSkgew0KICAgIGlmIChsYW1iZGEgPT0gMCkgew0KICAgICAgICBmdW5jdGlvbih4KSBsb2coeCkNCiAgICB9IGVsc2Ugew0KICAgICAgICBmdW5jdGlvbih4KSAoeCBeIGxhbWJkYSAtIDEpIC8gbGFtYmRhDQogICAgfQ0KfQ0KYGBgDQoNClRoaXMgZnVuY3Rpb24gaXMgdGhlIFtCb3gtQ294XShodHRwOi8vd3d3LnN0YXQudWNsYS5lZHUvfnJnb3VsZC94NDAxZjAxL3RyYW5zZm9ybXMuaHRtbCkgdHJhbnNmb3JtYXRpb24gZXF1YXRpb24uIEEgYmV0dGVyIG5hbWUgbWlnaHQgYmUgYG5ld19ib3hfY294YC4gSXQgY2FuIGJlIHVzZWQgdG8gc2V0IEJveC1Db3ggdHJhbnNmb3JtYXRpb25zOg0KDQpgYGB7cn0NCm5ld19ib3hfY294IDwtIGJjDQpgYGANCg0KV2UgY2FuIGNyZWF0ZSBtYW55IGl0ZXJhdGlvbnMgb2YgdGhlIEJveC1Db3ggdHJhbnNmb3JtYXRpb24gYnkgdmFyeWluZyBgbGFtYmRhYC4NCg0KYGBge3J9DQpiY18wIDwtIG5ld19ib3hfY294KDApDQpiY18wKDEwKQ0KYGBgDQoNCmBgYHtyfQ0KYmNfMC41IDwtIG5ld19ib3hfY294KDAuNSkNCmJjXzAuNSgxMCkNCmBgYA0KDQpgYGB7cn0NCmJjXzEgPC0gbmV3X2JveF9jb3goMSkNCmJjXzEoMTApDQpgYGANCg0KMy4gV2hhdCBkb2VzIGBhcHByb3hmdW4oKWAgZG8/IA0KDQpQZXJmb3JtcyBpbnRlcnBvbGF0aW9uIG9uIGEgc2V0IG9mIHZhbHVlcyBiZXR3ZWVuIHBvaW50cyBpbiBhIGRhdGEgc2V0LiBUaGUgZnVuY3Rpb24gcmV0dXJuZWQgY2FuIGJlIHBsb3R0ZWQgYXMgYSBjdXJ2ZSB0byBnZXQgdmFsdWVzIGluIGJldHdlZW4gdGhlIGRhdGEgc2V0IHBvaW50cy4gDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTk4KQ0KeCA8LSAxOjEwDQp5IDwtIHJub3JtKDEwKQ0KZiA8LSBhcHByb3hmdW4oeCwgeSkNCnBsb3QoZiwgMCwgMTEsIGNvbCA9ICJ0b21hdG8iLCBhZGQgPSBUUlVFLCBsdHkgPSAzLCBsd2QgPSAyKQ0KcG9pbnRzKHgsIHksIGNvbCA9IDEsIHBjaCA9ICIqIikNCnBvaW50cyh4ID0gMi41LCB5ID0gZigyLjUpLCBjb2wgPSAib3JhbmdlIiwgcGNoID0gNikgIyBJbnRlcnBvbGF0ZWQgcG9pbnQNCmBgYA0KDQpDYW4gdXNlIHRoZSBmdW5jdGlvbiwgYGZgLCB0byBpbnRlcnBvbGF0ZSBiZXR3ZWVuIHRoZSBwb2ludHMgYXQgYHggPSAyYCBhbmQgYHggPSAzYC4gDQoNCmBgYHtyfQ0KZigyLjUpDQpgYGANCg0KDQo0LiBXaGF0IGRvZXMgYGVjZGYoKWAgZG8/DQoNClN0YW5kcyBmb3IgZW1waXJpY2FsIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uLiBgZWNkZigpYCB0aWVzIHRoZSB2YWx1ZXMgaW4gYSBsaXN0IHRvIHRoZSBjdW11bGF0aXZlIGRpc3RyaWJ1dGlvbiB0aGF0IHRoZSB2YWx1ZSBmYWxscyBpbnRvLiBUaGUgZnVuY3Rpb24gcmV0dXJuZWQgY2FuIGJlIHVzZWQgdG8gY29tcHV0ZSB0aGUgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gdGhhdCB0aGUgcG9pbnQgZmFsbHMgaW50by4NCg0KYGBge3J9DQpzZXQuc2VlZCgxMikNCnggPC0gcm5vcm0oNTApDQpGbiA8LSBlY2RmKHgpDQpgYGANCg0KSWYgd2Ugd2FudCB0byBnZXQgdGhlIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIG9mIGEgbmV3IHBvaW50Og0KDQpgYGB7cn0NCkZuKDApDQpgYGANCg0KNS4gQ3JlYXRlIGEgZnVuY3Rpb24gdGhhdCBjcmVhdGVzIGZ1bmN0aW9ucyB0aGF0IGNvbXB1dGUgdGhlIGl0aCBjZW50cmFsIG1vbWVudCBvZiBhIG51bWVyaWMgdmVjdG9yLg0KDQpgYGB7cn0NCm1vbWVudCA8LSBmdW5jdGlvbihuKSB7DQogICAgZnVuY3Rpb24oeCkgew0KICAgICAgICBzdW0oKHggLSBtZWFuKHgpKSBeIG4pIC8gbGVuZ3RoKHgpDQogICAgfQ0KfQ0KYGBgDQoNCg0KUnVuIEhhZGxleSdzIGNvZGU6DQoNCmBgYHtyfQ0KbTEgPC0gbW9tZW50KDEpDQptMiA8LSBtb21lbnQoMikNCg0KeCA8LSBydW5pZigxMDApDQpzdG9waWZub3QoYWxsLmVxdWFsKG0xKHgpLCAwKSkNCnN0b3BpZm5vdChhbGwuZXF1YWwobTIoeCksIHZhcih4KSAqIDk5IC8gMTAwKSkNCmBgYA0KDQpJdCB3b3Jrcy4NCg0KNi4gQ3JlYXRlIGEgZnVuY3Rpb24gYHBpY2soKWAgdGhhdCB0YWtlcyBhbiBpbmRleCwgYGlgLCBhcyBhbiBhcmd1bWVudCBhbmQgcmV0dXJucyBhIGZ1bmN0aW9uIHdpdGggYW4gYXJndW1lbnQgYHhgIHRoYXQgc3Vic2V0cyBgeGAgd2l0aCBgaWAuDQoNCmBgYHtyfQ0KcGljayA8LSBmdW5jdGlvbihpKSB7DQogICAgZnVuY3Rpb24oeCkgeFtbaV1dDQp9DQpgYGANCg0KUnVuIGNvZGU6DQoNCmBgYHtyfQ0KbGFwcGx5KG10Y2FycywgcGljayg1KSkgJT4lIHVubGlzdCgpDQpgYGANCg0KQ2hlY2sgcmVzdWx0czoNCg0KYGBge3J9DQpsYXBwbHkobXRjYXJzLCBmdW5jdGlvbih4KSB4W1s1XV0pICU+JSB1bmxpc3QoKQ0KYGBgDQoNCiMgTGlzdHMgb2YgRnVuY3Rpb25zDQoNCkNhbiBzdG9yZSBmdW5jdGlvbnMgaW4gbGlzdHMsIHdoaWNoIHJlZHVjZXMgcmVkdW5kYW5jeS4NCg0KYGBge3J9DQp4IDwtIDE6MTANCmZ1bnMgPC0gbGlzdCgNCiAgICBzdW0gPSBzdW0sDQogICAgbWVhbiA9IG1lYW4sDQogICAgbWVkaWFuID0gbWVkaWFuDQopDQpsYXBwbHkoZnVucywgZnVuY3Rpb24oZikgZih4LCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgICB1bmxpc3QoKQ0KYGBgDQoNCiMjIEV4ZXJjaXNlcw0KDQoxLiBJbXBsZW1lbnQgYSBzdW1tYXJ5IGZ1bmN0aW9uIHRoYXQgd29ya3MgbGlrZSBgYmFzZTo6c3VtbWFyeSgpYCwgYnV0IHVzZXMgYSBsaXN0IG9mIGZ1bmN0aW9ucy4gTW9kaWZ5IHRoZSBmdW5jdGlvbiBzbyBpdCByZXR1cm5zIGEgY2xvc3VyZSwgbWFraW5nIGl0IHBvc3NpYmxlIHRvIHVzZSBpdCBhcyBhIGZ1bmN0aW9uIGZhY3RvcnkuDQoNCkhlcmUncyB0aGUgZnVuY3Rpb24gd2UgbmVlZCB0byByZXBsaWNhdGU6DQoNCmBgYHtyfQ0KeCA8LSAxOjEwDQpzdW1tYXJ5KHgpDQpgYGANCg0KRmlyc3QsIHJlY3JlYXRlIGBiYXNlOjpzdW1tYXJ5KClgOg0KDQpgYGB7cn0NCnN1bW1hcnkyIDwtIGZ1bmN0aW9uKHgpIHsNCiAgICBmdW5zIDwtIGxpc3QoDQogICAgICAgICJNaW4uIiAgICA9IG1pbiwNCiAgICAgICAgIjFzdCBRdS4iID0gZnVuY3Rpb24oeCkgcXVhbnRpbGUoeCwgMC4yNSlbWzFdXSwNCiAgICAgICAgIk1lZGlhbiIgID0gbWVkaWFuLA0KICAgICAgICAiTWVhbiIgICAgPSBtZWFuLA0KICAgICAgICAiM3JkIFF1LiIgPSBmdW5jdGlvbih4KSBxdWFudGlsZSh4LCAwLjc1KVtbMV1dLA0KICAgICAgICAiTWF4IiAgICAgPSBtYXgNCiAgICApDQogICAgbGFwcGx5KGZ1bnMsIGZ1bmN0aW9uKGYpIGYoeCkpICU+JQ0KICAgICAgICB1bmxpc3QoKQ0KfQ0Kc3VtbWFyeTIoeCkNCmBgYA0KDQpOb3csIG1ha2UgYSBmdW5jdGlvbiBmYWN0b3J5Og0KDQpgYGB7cn0NCm1ha2Vfc3VtbWFyeSA8LSBmdW5jdGlvbiggDQogICAgZnVucyA9IGxpc3QoDQogICAgICAgICJNaW4uIiAgICA9IG1pbiwNCiAgICAgICAgIjFzdCBRdS4iID0gZnVuY3Rpb24oeCkgcXVhbnRpbGUoeCwgMC4yNSlbWzFdXSwNCiAgICAgICAgIk1lZGlhbiIgID0gbWVkaWFuLA0KICAgICAgICAiTWVhbiIgICAgPSBtZWFuLA0KICAgICAgICAiM3JkIFF1LiIgPSBmdW5jdGlvbih4KSBxdWFudGlsZSh4LCAwLjc1KVtbMV1dLA0KICAgICAgICAiTWF4IiAgICAgPSBtYXgpDQogICAgKSB7DQogICAgZnVuY3Rpb24oeCkgew0KICAgICAgICBsYXBwbHkoZnVucywgZnVuY3Rpb24oZikgZih4KSkgJT4lDQogICAgICAgICAgICB1bmxpc3QoKQ0KICAgICAgICB9DQogICAgfQ0KYGBgDQoNCldlIGNhbiBub3cgdXNlIHRoZSBjbG9zdXJlIHRvIHJlcGxpY2F0ZSB0aGUgYGJhc2U6OnN1bW1hcnkoKWAgZnVuY3Rpb24uLi4NCg0KYGBge3J9DQpzdW1tYXJ5MiA8LSBtYWtlX3N1bW1hcnkoKQ0Kc3VtbWFyeTIoeCkNCmBgYA0KDQouLi5vciB3ZSBjYW4gZ2l2ZSBpdCBvdXIgb3duIGN1c3RvbSBmdW5jdGlvbnMgdG8gcnVuLg0KDQpgYGB7cn0NCnN1bW1hcnkzIDwtIG1ha2Vfc3VtbWFyeShmdW5zID0gbGlzdChNZWFuID0gbWVhbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RkLkRldiA9IHNkLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRdWFudGlsZSA9IHF1YW50aWxlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElRUiA9IElRUikpDQpzdW1tYXJ5Myh4KQ0KYGBgDQoNCldlIGNhbiBldmVuIGFwcGx5IG91ciBuZXcgc3VtbWFyeSBmdW5jdGlvbiB0byBhIGRhdGEgZnJhbWU6DQoNCmBgYHtyfQ0KbGFwcGx5KG10Y2Fyc1ssMTo0XSwgc3VtbWFyeTMpDQpgYGANCg0KIyBDYXNlIFN0dWR5OiBOdW1lcmljYWwgSW50ZWdyYXRpb24NCg0KU2VjdGlvbiBza2lwcGVkDQoNCg==