1 R with big data

1.1 R is not good at coping with data larger than 10% of the RAM.

R is not well-suited for working with data larger than 10%-20% of a computer’s RAM

— The R installation and administration Manual.

1.2 Swapping is not efficient

  1. If the RAM is not enough, the data will be moved to disk. If the disk is not enough, the program will crash.

  2. Also, since the disk is much slow than the RAM, execution time increases.

1.3 Scalable solutions

  1. Move a subset into RAM

  2. Process the subset

  3. Keep the result and discard the subset


2 Overview of the bigmemory package

2.1 When to use big.matrix?

  1. 20% of the size of RAM

  2. Dense matrices

2.2 An overview of bigmemory

  1. bigmemory implements the big.matrix data type, which is used to create, store, access, and manipulate matrices stored on the disk

  2. Data are kept on the disk and moved to RAM implicitly

  3. A big.matrix object:

  • Only needs to be imported once
  • “backing” file: binary representation of the matrix on the disk
  • "descriptor file: holds metadata, such as number of rows, columns, names, etc.

2.3 Simimarites between big.matrix and matrix

  • subset
  • assign

2.4 Differences between big.matrix and matrix

  • big.matrix is stored on the disk
  • big.matrix can be shared with multiple R sessions

2.5 big.matrix is reference type

  • big.matrix object will not have a copy when assign to a variable
  • use deepcopy to copy big.matrix object explicitly

3 The Workflow of the bigmemory package

3.1 Read a big.matrix object

3.2 Attaching a big.matrix object

Now that the big.matrix object is on the disk, we can use the information stored in the descriptor file to instantly make it available during an R session. This means that you don’t have to reimport the data set, which takes more time for larger files. You can simply point the bigmemory package at the existing structures on the disk and begin accessing data without the wait.

big.iris <- attach.big.matrix("iris.desc")
Error in attach.big.matrix("iris.desc") : 
  could not find function "attach.big.matrix"

3.3 Copying matrices and big matrices

If you want to copy a big.matrix object, then you need to use the deepcopy() function. This can be useful, especially if you want to create smaller big.matrix objects.

big.iris[1,1]
[1] 5.1

3.4 Slicing the big.matrix

index.2 <- which(big.iris[, 1] == 5.0 & big.iris[, 4] < 0.3)
big.iris[index.2, ]
 Sepal.Length Sepal.Width Petal.Length Petal.Width Species
[1,]            5         3.6          1.4         0.2       1
[2,]            5         3.4          1.5         0.2       1
[3,]            5         3.0          1.6         0.2       1
[4,]            5         3.2          1.2         0.2       1
[5,]            5         3.3          1.4         0.2       1

which will extract the corresponding columns, then do the logical comparison, finally return the logical values whose length is equal to the column length. mwhich() requires no memory overhead and returns only a vector of indices of length.

4 Associated packages

  • Tables and summaries
    • biganalytics: summarizing
    • bigtabulate: splitting and tabulating
  • Linear Algebra
    • bigalgebra
  • Fit models
    • bigpca: pca
    • bigFastLM: linear models
    • biglasso: penalized linear regression and logistic regression
    • bigrf: random forest

4.1 Creating tables with big.matrix objects

A final advantage to using big.matrix is that if you know how to use R’s matrices, then you know how to use a big.matrix. You can subset columns and rows just as you would a regular matrix, using a numeric or character vector and the object returned is an R matrix. Likewise, assignments are the same as with R matrices and after those assignments are made they are stored on disk and can be used in the current and future R sessions.

One thing to remember is that $ is not valid for getting a column of either a matrix or a big.matrix.

big.iris[1:3, ]
     Sepal.Length Sepal.Width Petal.Length Petal.Width Species
[1,]          5.1         3.5          1.4         0.2       1
[2,]          4.9         3.0          1.4         0.2       1
[3,]          4.7         3.2          1.3         0.2       1
table(big.iris[, "Petal.Width"])

0.1 0.2 0.3 0.4 0.5 0.6   1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9   2 2.1 2.2 2.3 2.4 2.5 
  5  29   7   7   1   1   7   3   5  13   8  12   4   2  12   5   6   6   3   8   3   3 

4.2 Data summary using biganalytics

# colMeans(big.iris)

biganalytics::colmean(big.iris)
Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
    5.843333     3.057333     3.758000     1.199333     2.000000 
biganalytics::summary(big.iris)
                  min      max     mean      NAs
Sepal.Length 4.300000 7.900000 5.843333 0.000000
Sepal.Width  2.000000 4.400000 3.057333 0.000000
Petal.Length 1.000000 6.900000 3.758000 0.000000
Petal.Width  0.100000 2.500000 1.199333 0.000000
Species      1.000000 3.000000 2.000000 0.000000

4.3 Tabulating using bigtable

The bigtabulate package provides optimized routines for creating tables and splitting the rows of big.matrix objects.

library(bigtabulate)
Loading required package: biganalytics
Loading required package: foreach
Loading required package: biglm
Loading required package: DBI
bigtable(big.iris, "Sepal.Width")
  2 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9   3 3.1 3.2 3.3 3.4 3.5 3.6 3.7 4.1 4.2 4.4 
  1   3   4   3   8   5   9  14  10  26  11  13   6  12   6   4   3   6   2   1   1   1   1 
bigtable(big.iris[1:20,], c("Sepal.Length", "Petal.Width"))
    0.1 0.2 0.3 0.4
4.3   1   0
4.4   0   1   0   0
4.6   0   1   1   0
4.7   0   1   0   0
4.8   1   1   0   0
4.9   1   1   0   0
5     0   2   0   0
5.1   0   1   2   0
5.4   0   1   0   2
5.7   0   0   1   1
5.8   0   1   0   0

5 Split-Apply-Combine (All are base functions)

5.1 Split

spl <- split(1:nrow(big.iris), big.iris[, 5])
str(spl)
List of 3
 $ 1: int [1:50] 1 2 3 4 5 6 7 8 9 10 ...
 $ 2: int [1:50] 51 52 53 54 55 56 57 58 59 60 ...
 $ 3: int [1:50] 101 102 103 104 105 106 107 108 109 110 ...

5.2 Apply

spe.mean.and.median <- Map(function(rows) c(mean(big.iris[rows, 1]), median(big.iris[rows, 1])), spl)


str(spe.mean.and.median)
List of 3
 $ 1: num [1:2] 5.01 5
 $ 2: num [1:2] 5.94 5.9
 $ 3: num [1:2] 6.59 6.5

5.3 Combine

spe.summary <- Reduce(rbind, spe.mean.and.median)

dimnames(spe.summary) <- list(unique(levels(iris[,5])), c("mean", "median"))

spe.summary
            mean median
setosa     5.006    5.0
versicolor 5.936    5.9
virginica  6.588    6.5
fit <- biglm.big.matrix(Sepal.Length ~ Sepal.Width + Petal.Length, data = big.iris)
summary(fit)
Large data regression model: biglm(formula = formula, data = data, ...)
Sample size =  150 
               Coef   (95%    CI)     SE p
(Intercept)  2.2491 1.7532 2.7451 0.2480 0
Sepal.Width  0.5955 0.4569 0.7342 0.0693 0
Petal.Length 0.4719 0.4377 0.5062 0.0171 0
summary(lm(Sepal.Length ~ Sepal.Width + Petal.Length, data = iris))

Call:
lm(formula = Sepal.Length ~ Sepal.Width + Petal.Length, data = iris)

Residuals:
     Min 
-0.96159 -0.23489  0.00077  0.21453  0.78557 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   2.24914    0.24797    9.07 7.04e-16 ***
Sepal.Width   0.59552    0.06933    8.59 1.16e-14 ***
Petal.Length  0.47192    0.01712   27.57  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3333 on 147 degrees of freedom
Multiple R-squared:  0.8402,    Adjusted R-squared:  0.838 
F-statistic: 386.4 on 2  147 DF,  p-value: < 2.2e-16
library(oem)
y <- rnorm(nrows) + bigmat[,1] - bigmat[,2]

x
x <- deepcopy(big.iris, cols = 1:3)

y <-  rnorm(150)

fit <- big.oem(x = x, y = y,
               penalty = c("lasso"))

fit$beta

fit2 <- oem(x = bigmat[,], y = y,
            penalty = c("lasso", "grp.lasso"),
            groups = rep(1:20, each = 5))
big.iris
An object of class 

6 Limitations of bigmemory

6.1 Where can you use bigmemory?

  • You can use bigmemory when your data are

    • matrices
    • dense
      • new package bigsparser
    • numeric
  • Underlying data structures are compatible with low-level linear algebra libraries for fast model fitting. (Use c/c++ libraries directly)

  • if you have different column types, you could try the ff pacakge, which is similar to bigmemory but includes a data.frame like data structure.

6.2 Understanding disk access

A big.matrix object is a data structure designed for random access.

  • If the entries required are in the RAM, then they will be returnbe immediately.
  • If the entries required are not in the RAM, then they will be imported from disk to the RAM.
  • Since the retreating is almost equally fast, this data structure is called random access data structure.

6.3 Disadvantages of random access

  • can’t add rows or columns to an existing big.matrix object
  • you need to have enough disk space to hold the entire matrix in one big block (in a single disk)

7 The Design of the big.matrix class

8 Reference

  • Scalable Data Processing in R, datacamp
LS0tDQp0aXRsZTogImJpZ21lbW9yeSBQYWNrYWdlIg0KYXV0aG9yOiAiU2hhbmdjaGVuIFNvbmciDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIG51bWJlcl9zZWN0aW9uczogVFJVRQ0KICAgIHRvYzogVFJVRQ0KICAgIHRvY19mbG9hdDogVFJVRQ0KICAgIHRoZW1lOiB1bml0ZWQNCiAgICBoaWdobGlnaHQ6IHRhbmdvDQotLS0NCg0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCmJvZHl7DQogIGZvbnQtZmFtaWx5OiBIZWx2ZXRpY2E7DQogIGZvbnQtc2l6ZTogMTJwdDsNCn0NCmgxLGgyLGgzLGg0LGg1LGg2ew0KICBmb250LWZhbWlseTogSGVsdmV0aWNhOw0KfQ0KPC9zdHlsZT4NCg0KIyBSIHdpdGggYmlnIGRhdGENCg0KIyMgUiBpcyBub3QgZ29vZCBhdCBjb3Bpbmcgd2l0aCBkYXRhIGxhcmdlciB0aGFuIDEwJSBvZiB0aGUgUkFNLg0KDQo+IFIgaXMgbm90IHdlbGwtc3VpdGVkIGZvciB3b3JraW5nIHdpdGggZGF0YSBsYXJnZXIgdGhhbiAxMCUtMjAlIG9mIGEgY29tcHV0ZXIncyBSQU0NCj4NCj4gLS0tIFRoZSBSIGluc3RhbGxhdGlvbiBhbmQgYWRtaW5pc3RyYXRpb24gTWFudWFsLg0KDQojIyBTd2FwcGluZyBpcyBub3QgZWZmaWNpZW50DQoNCjEuIElmIHRoZSBSQU0gaXMgbm90IGVub3VnaCwgdGhlIGRhdGEgd2lsbCBiZSBtb3ZlZCB0byBkaXNrLiBJZiB0aGUgZGlzayBpcyBub3QgZW5vdWdoLCB0aGUgcHJvZ3JhbSB3aWxsIGNyYXNoLg0KDQoyLiBBbHNvLCBzaW5jZSB0aGUgZGlzayBpcyBtdWNoIHNsb3cgdGhhbiB0aGUgUkFNLCBleGVjdXRpb24gdGltZSBpbmNyZWFzZXMuDQoNCiMjIFNjYWxhYmxlIHNvbHV0aW9ucw0KDQoxLiBNb3ZlIGEgc3Vic2V0IGludG8gUkFNDQoNCjIuIFByb2Nlc3MgdGhlIHN1YnNldA0KDQozLiBLZWVwIHRoZSByZXN1bHQgYW5kIGRpc2NhcmQgdGhlIHN1YnNldA0KDQoqKioNCg0KIyBPdmVydmlldyBvZiB0aGUgYmlnbWVtb3J5IHBhY2thZ2UNCg0KIyMgV2hlbiB0byB1c2UgYGJpZy5tYXRyaXhgPw0KDQoxLiAyMCUgb2YgdGhlIHNpemUgb2YgUkFNDQoNCjIuIERlbnNlIG1hdHJpY2VzDQoNCiMjIEFuIG92ZXJ2aWV3IG9mIGJpZ21lbW9yeQ0KDQoxLiBiaWdtZW1vcnkgaW1wbGVtZW50cyB0aGUgYGJpZy5tYXRyaXhgIGRhdGEgdHlwZSwgd2hpY2ggaXMgdXNlZCB0byBjcmVhdGUsIHN0b3JlLCBhY2Nlc3MsIGFuZCBtYW5pcHVsYXRlIG1hdHJpY2VzIHN0b3JlZCBvbiB0aGUgZGlzaw0KDQoyLiBEYXRhIGFyZSBrZXB0IG9uIHRoZSBkaXNrIGFuZCBtb3ZlZCB0byBSQU0gaW1wbGljaXRseQ0KDQozLiBBIGBiaWcubWF0cml4YCBvYmplY3Q6IA0KICAtIE9ubHkgbmVlZHMgdG8gYmUgaW1wb3J0ZWQgb25jZQ0KICAtICJiYWNraW5nIiBmaWxlOiBiaW5hcnkgcmVwcmVzZW50YXRpb24gb2YgdGhlIG1hdHJpeCBvbiB0aGUgZGlzaw0KICAtICJkZXNjcmlwdG9yIGZpbGU6IGhvbGRzIG1ldGFkYXRhLCBzdWNoIGFzIG51bWJlciBvZiByb3dzLCBjb2x1bW5zLCBuYW1lcywgZXRjLg0KDQojIyBTaW1pbWFyaXRlcyBiZXR3ZWVuIGBiaWcubWF0cml4YCBhbmQgYG1hdHJpeGANCg0KLSBzdWJzZXQNCi0gYXNzaWduDQoNCg0KIyMgRGlmZmVyZW5jZXMgYmV0d2VlbiBgYmlnLm1hdHJpeGAgYW5kIGBtYXRyaXhgDQoNCi0gYGJpZy5tYXRyaXhgIGlzIHN0b3JlZCBvbiB0aGUgZGlzaw0KLSBgYmlnLm1hdHJpeGAgY2FuIGJlIHNoYXJlZCB3aXRoIG11bHRpcGxlIFIgc2Vzc2lvbnMNCg0KIyMgYGJpZy5tYXRyaXhgIGlzIHJlZmVyZW5jZSB0eXBlDQoNCi0gYGJpZy5tYXRyaXhgIG9iamVjdCB3aWxsIG5vdCBoYXZlIGEgY29weSB3aGVuIGFzc2lnbiB0byBhIHZhcmlhYmxlDQotIHVzZSBgZGVlcGNvcHlgIHRvIGNvcHkgYGJpZy5tYXRyaXhgIG9iamVjdCBleHBsaWNpdGx5DQoNCg0KKioqDQoNCiMgVGhlIFdvcmtmbG93IG9mIHRoZSBiaWdtZW1vcnkgcGFja2FnZQ0KDQojIyBSZWFkIGEgYGJpZy5tYXRyaXhgIG9iamVjdA0KDQpgYGB7cn0NCg0KaXJpcyRTcGVjaWVzIDwtIGFzLmludGVnZXIoaXJpcyRTcGVjaWVzKQ0KDQp3cml0ZS5jc3YoaXJpcywgImlyaXMuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQoNCnJlbW92ZSgiaXJpcyIpDQpsaWJyYXJ5KGJpZ21lbW9yeSkNCnggPC0gcmVhZC5iaWcubWF0cml4KCJpcmlzLmNzdiIsIGhlYWRlciA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICB0eXBlID0gImRvdWJsZSIsDQogICAgICAgICAgICAgICAgICAgICBiYWNraW5nZmlsZSA9ICJpcmlzLmJpbiIsDQogICAgICAgICAgICAgICAgICAgICBkZXNjcmlwdG9yZmlsZSA9ICJpcmlzLmRlc2MiKQ0KDQpjbGFzcyh4KQ0KZGltKHgpDQpoZWFkKHgpDQp4DQoNCnhbLF0NCmBgYA0KDQojIyBBdHRhY2hpbmcgYSBgYmlnLm1hdHJpeGAgb2JqZWN0DQoNCk5vdyB0aGF0IHRoZSBgYmlnLm1hdHJpeGAgb2JqZWN0IGlzIG9uIHRoZSBkaXNrLCB3ZSBjYW4gdXNlIHRoZSBpbmZvcm1hdGlvbiBzdG9yZWQgaW4gdGhlIGRlc2NyaXB0b3IgZmlsZSB0byBpbnN0YW50bHkgbWFrZSBpdCBhdmFpbGFibGUgZHVyaW5nIGFuIFIgc2Vzc2lvbi4gVGhpcyBtZWFucyB0aGF0IHlvdSBkb24ndCBoYXZlIHRvIHJlaW1wb3J0IHRoZSBkYXRhIHNldCwgd2hpY2ggdGFrZXMgbW9yZSB0aW1lIGZvciBsYXJnZXIgZmlsZXMuIFlvdSBjYW4gc2ltcGx5IHBvaW50IHRoZSBgYmlnbWVtb3J5YCBwYWNrYWdlIGF0IHRoZSBleGlzdGluZyBzdHJ1Y3R1cmVzIG9uIHRoZSBkaXNrIGFuZCBiZWdpbiBhY2Nlc3NpbmcgZGF0YSB3aXRob3V0IHRoZSB3YWl0Lg0KDQpgYGB7cn0NCmJpZy5pcmlzIDwtIGF0dGFjaC5iaWcubWF0cml4KCJpcmlzLmRlc2MiKQ0KZGltKGJpZy5pcmlzKQ0KaGVhZChiaWcuaXJpcykNCmBgYA0KDQoNCiMjIENvcHlpbmcgbWF0cmljZXMgYW5kIGJpZyBtYXRyaWNlcw0KDQpJZiB5b3Ugd2FudCB0byBjb3B5IGEgYGJpZy5tYXRyaXhgIG9iamVjdCwgdGhlbiB5b3UgbmVlZCB0byB1c2UgdGhlIGBkZWVwY29weSgpYCBmdW5jdGlvbi4gVGhpcyBjYW4gYmUgdXNlZnVsLCBlc3BlY2lhbGx5IGlmIHlvdSB3YW50IHRvIGNyZWF0ZSBzbWFsbGVyIGBiaWcubWF0cml4YCBvYmplY3RzLiANCg0KYGBge3J9DQpiaWcuaXJpcy5zbWFsbCA8LSBkZWVwY29weShiaWcuaXJpcywgY29scyA9IDE6Miwgcm93cyA9IDE6NSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhY2tpbmdmaWxlID0gImlyaXNfc21hbGwuYmluIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2NyaXB0b3JmaWxlID0gImlyaXNfc21hbGwuZGVzYyIpDQoNCmJpZy5pcmlzLnNtYWxsLmNvcHkgPC0gYmlnLmlyaXMuc21hbGwNCmJpZy5pcmlzLnNtYWxsLmNvcHlbMSwgMV0gPC0gTkENCmJpZy5pcmlzLnNtYWxsWzEsMV0NCmJpZy5pcmlzLnNtYWxsLmNvcHlbMSwxXQ0KDQpiaWcuaXJpc1sxLDFdDQpgYGANCg0KIyMgU2xpY2luZyB0aGUgYGJpZy5tYXRyaXhgDQoNCmBgYHtyfQ0KIyMgd2hpY2ggaW4gYmFzZQ0KaW5kZXguMSA8LSB3aGljaChiaWcuaXJpc1ssIDFdID09IDUuMCkNCmJpZy5pcmlzW2luZGV4LjEsIF0NCg0KaW5kZXguMiA8LSB3aGljaChiaWcuaXJpc1ssIDFdID09IDUuMCAmIGJpZy5pcmlzWywgNF0gPCAwLjMpDQpiaWcuaXJpc1tpbmRleC4yLCBdDQoNCg0KIyMgbXdoaWNoLCBtdWx0aS13aGljaA0KDQppbmRleC4zIDwtIG13aGljaChiaWcuaXJpcywgMSwgNS4wLCAiZXEiKQ0KYmlnLmlyaXNbaW5kZXguMywgXQ0KDQppbmRleC40IDwtIG13aGljaChiaWcuaXJpcywgIlNlcGFsLkxlbmd0aCIsIDUuMCwgImVxIikNCmJpZy5pcmlzW2luZGV4LjQsIF0NCg0KDQppbmRleC41IDwtIG13aGljaChiaWcuaXJpcywgDQogICAgICAgICAgICAgICAgICBjKCJTZXBhbC5MZW5ndGgiLCAiUGV0YWwuV2lkdGgiKSwgDQogICAgICAgICAgICAgICAgICBsaXN0KDUuMCwgMC4zKSwgYygiZXEiLCAibHQiKSwgIkFORCIpDQpiaWcuaXJpc1tpbmRleC41LCAgXQ0KDQoNCmluZGV4LjYgPC0gbXdoaWNoKGJpZy5pcmlzLCANCiAgICAgICAgICAgICAgICAgIGMoIlNlcGFsLkxlbmd0aCIsICJQZXRhbC5XaWR0aCIpLCANCiAgICAgICAgICAgICAgICAgIGxpc3QoNS4wLCBjKDAuMiwgMC4zKSksIGxpc3QoImVxIiwgYygiZ2UiLCAibHQiKSksICJBTkQiKQ0KYmlnLmlyaXNbaW5kZXguNiwgIF0NCg0KYGBgDQoNCg0KYHdoaWNoYCB3aWxsIGV4dHJhY3QgdGhlIGNvcnJlc3BvbmRpbmcgY29sdW1ucywgdGhlbiBkbyB0aGUgbG9naWNhbCBjb21wYXJpc29uLCBmaW5hbGx5IHJldHVybiB0aGUgbG9naWNhbCB2YWx1ZXMgd2hvc2UgbGVuZ3RoIGlzIGVxdWFsIHRvIHRoZSBjb2x1bW4gbGVuZ3RoLiANCmBtd2hpY2goKWAgcmVxdWlyZXMgbm8gbWVtb3J5IG92ZXJoZWFkIGFuZCByZXR1cm5zIG9ubHkgYSB2ZWN0b3Igb2YgaW5kaWNlcyBvZiBsZW5ndGguDQoNCg0KDQojIEFzc29jaWF0ZWQgcGFja2FnZXMNCg0KLSBUYWJsZXMgYW5kIHN1bW1hcmllcw0KICAtIGBiaWdhbmFseXRpY3NgOiBzdW1tYXJpemluZw0KICAtIGBiaWd0YWJ1bGF0ZWA6IHNwbGl0dGluZyBhbmQgdGFidWxhdGluZw0KICANCi0gTGluZWFyIEFsZ2VicmENCiAgLSBgYmlnYWxnZWJyYWANCiAgDQotIEZpdCBtb2RlbHMNCiAgLSBgYmlncGNhYDogcGNhDQogIC0gYGJpZ0Zhc3RMTWA6IGxpbmVhciBtb2RlbHMNCiAgLSBgYmlnbGFzc29gOiBwZW5hbGl6ZWQgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIGxvZ2lzdGljIHJlZ3Jlc3Npb24NCiAgLSBgYmlncmZgOiByYW5kb20gZm9yZXN0DQoNCg0KDQojIyBDcmVhdGluZyB0YWJsZXMgd2l0aCBiaWcubWF0cml4IG9iamVjdHMNCg0KQSBmaW5hbCBhZHZhbnRhZ2UgdG8gdXNpbmcgYGJpZy5tYXRyaXhgIGlzIHRoYXQgaWYgeW91IGtub3cgaG93IHRvIHVzZSBSJ3MgbWF0cmljZXMsIHRoZW4geW91IGtub3cgaG93IHRvIHVzZSBhIGBiaWcubWF0cml4LmAgWW91IGNhbiBzdWJzZXQgY29sdW1ucyBhbmQgcm93cyBqdXN0IGFzIHlvdSB3b3VsZCBhIHJlZ3VsYXIgbWF0cml4LCB1c2luZyBhIG51bWVyaWMgb3IgY2hhcmFjdGVyIHZlY3RvciBhbmQgdGhlIG9iamVjdCByZXR1cm5lZCBpcyBhbiBSIGBtYXRyaXguYCBMaWtld2lzZSwgYXNzaWdubWVudHMgYXJlIHRoZSBzYW1lIGFzIHdpdGggUiBtYXRyaWNlcyBhbmQgYWZ0ZXIgdGhvc2UgYXNzaWdubWVudHMgYXJlIG1hZGUgdGhleSBhcmUgc3RvcmVkIG9uIGRpc2sgYW5kIGNhbiBiZSB1c2VkIGluIHRoZSBjdXJyZW50IGFuZCBmdXR1cmUgUiBzZXNzaW9ucy4NCg0KT25lIHRoaW5nIHRvIHJlbWVtYmVyIGlzIHRoYXQgYCRgIGlzIG5vdCB2YWxpZCBmb3IgZ2V0dGluZyBhIGNvbHVtbiBvZiBlaXRoZXIgYSBgbWF0cml4YCBvciBhIGBiaWcubWF0cml4LmANCg0KYGBge3J9DQpiaWcuaXJpc1sxOjMsIF0NCg0KdGFibGUoYmlnLmlyaXNbLCAiUGV0YWwuV2lkdGgiXSkNCmBgYA0KIyMgRGF0YSBzdW1tYXJ5IHVzaW5nIGJpZ2FuYWx5dGljcw0KDQpgYGB7cn0NCiMgY29sTWVhbnMoYmlnLmlyaXMpDQoNCmJpZ2FuYWx5dGljczo6Y29sbWVhbihiaWcuaXJpcykNCmJpZ2FuYWx5dGljczo6c3VtbWFyeShiaWcuaXJpcykNCmBgYA0KDQoNCiMjIFRhYnVsYXRpbmcgdXNpbmcgYmlndGFibGUNCg0KDQpUaGUgYGJpZ3RhYnVsYXRlYCBwYWNrYWdlIHByb3ZpZGVzIG9wdGltaXplZCByb3V0aW5lcyBmb3IgY3JlYXRpbmcgdGFibGVzIGFuZCBzcGxpdHRpbmcgdGhlIHJvd3Mgb2YgYmlnLm1hdHJpeCBvYmplY3RzLg0KDQpgYGB7cn0NCmxpYnJhcnkoYmlndGFidWxhdGUpDQpiaWd0YWJsZShiaWcuaXJpcywgIlNlcGFsLldpZHRoIikNCmJpZ3RhYmxlKGJpZy5pcmlzWzE6MjAsXSwgYygiU2VwYWwuTGVuZ3RoIiwgIlBldGFsLldpZHRoIikpDQpgYGANCg0KIyBTcGxpdC1BcHBseS1Db21iaW5lIChBbGwgYXJlIGJhc2UgZnVuY3Rpb25zKQ0KDQojIyBTcGxpdA0KDQpgYGB7cn0NCnNwbCA8LSBzcGxpdCgxOm5yb3coYmlnLmlyaXMpLCBiaWcuaXJpc1ssIDVdKQ0Kc3RyKHNwbCkNCmBgYA0KIyMgQXBwbHkNCg0KYGBge3J9DQpzcGUubWVhbi5hbmQubWVkaWFuIDwtIE1hcChmdW5jdGlvbihyb3dzKSBjKG1lYW4oYmlnLmlyaXNbcm93cywgMV0pLCBtZWRpYW4oYmlnLmlyaXNbcm93cywgMV0pKSwgc3BsKQ0KDQoNCnN0cihzcGUubWVhbi5hbmQubWVkaWFuKQ0KYGBgDQoNCiMjIENvbWJpbmUNCg0KYGBge3J9DQpzcGUuc3VtbWFyeSA8LSBSZWR1Y2UocmJpbmQsIHNwZS5tZWFuLmFuZC5tZWRpYW4pDQoNCmRpbW5hbWVzKHNwZS5zdW1tYXJ5KSA8LSBsaXN0KHVuaXF1ZShsZXZlbHMoaXJpc1ssNV0pKSwgYygibWVhbiIsICJtZWRpYW4iKSkNCg0Kc3BlLnN1bW1hcnkNCmBgYA0KDQoNCmBgYHtyfQ0KZml0IDwtIGJpZ2xtLmJpZy5tYXRyaXgoU2VwYWwuTGVuZ3RoIH4gU2VwYWwuV2lkdGggKyBQZXRhbC5MZW5ndGgsIGRhdGEgPSBiaWcuaXJpcykNCnN1bW1hcnkoZml0KQ0KDQpzdW1tYXJ5KGxtKFNlcGFsLkxlbmd0aCB+IFNlcGFsLldpZHRoICsgUGV0YWwuTGVuZ3RoLCBkYXRhID0gaXJpcykpDQpgYGANCg0KDQoNCmBgYHtyfQ0KbGlicmFyeShvZW0pDQp5IDwtIHJub3JtKG5yb3dzKSArIGJpZ21hdFssMV0gLSBiaWdtYXRbLDJdDQoNCngNCnggPC0gZGVlcGNvcHkoYmlnLmlyaXMsIGNvbHMgPSAxOjMpDQoNCnkgPC0gIHJub3JtKDE1MCkNCg0KZml0IDwtIGJpZy5vZW0oeCA9IHgsIHkgPSB5LA0KICAgICAgICAgICAgICAgcGVuYWx0eSA9IGMoImxhc3NvIikpDQoNCmZpdCRiZXRhDQoNCmZpdDIgPC0gb2VtKHggPSBiaWdtYXRbLF0sIHkgPSB5LA0KICAgICAgICAgICAgcGVuYWx0eSA9IGMoImxhc3NvIiwgImdycC5sYXNzbyIpLA0KICAgICAgICAgICAgZ3JvdXBzID0gcmVwKDE6MjAsIGVhY2ggPSA1KSkNCmBgYA0KDQoNCg0KYGBge3J9DQppZiAoVCkgew0Kc2V0LnNlZWQoMTIzKQ0KbnJvd3MgPC0gNTAwMDANCm5jb2xzIDwtIDEwMA0KYmtGaWxlIDwtICJiaWdtYXQuYmsiDQpkZXNjRmlsZSA8LSAiYmlnbWF0ay5kZXNjIg0KYmlnbWF0IDwtIGZpbGViYWNrZWQuYmlnLm1hdHJpeChucm93PW5yb3dzLCBuY29sPW5jb2xzLCB0eXBlPSJkb3VibGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYWNraW5nZmlsZT1ia0ZpbGUsIGJhY2tpbmdwYXRoPSIuIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzY3JpcHRvcmZpbGU9ZGVzY0ZpbGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzPWMoTlVMTCxOVUxMKSkNCg0KIyBFYWNoIGNvbHVtbiB2YWx1ZSB3aXRoIGJlIHRoZSBjb2x1bW4gbnVtYmVyIG11bHRpcGxpZWQgYnkNCiMgc2FtcGxlcyBmcm9tIGEgc3RhbmRhcmQgbm9ybWFsIGRpc3RyaWJ1dGlvbi4NCnNldC5zZWVkKDEyMykNCmZvciAoaSBpbiAxOm5jb2xzKSBiaWdtYXRbLGldID0gcm5vcm0obnJvd3MpKmkNCg0KeSA8LSBybm9ybShucm93cykgKyBiaWdtYXRbLDFdIC0gYmlnbWF0WywyXQ0KDQpmaXQgPC0gYmlnLm9lbSh4ID0gYmlnbWF0LCB5ID0geSwNCiAgICAgICAgICAgICAgIHBlbmFsdHkgPSBjKCJtY3AiKSkNCnN0cihmaXQpDQoNCmJpZ21hdA0KYmlnLmlyaXMNCmZpdDIgPC0gb2VtKHggPSBiaWdtYXRbLF0sIHkgPSB5LA0KICAgICAgICAgICAgcGVuYWx0eSA9IGMoImxhc3NvIiwgImdycC5sYXNzbyIpLA0KICAgICAgICAgICAgZ3JvdXBzID0gcmVwKDE6MjAsIGVhY2ggPSA1KSkNCg0KbWF4KGFicyhmaXQkYmV0YVtbMV1dIC0gZml0MiRiZXRhW1sxXV0pKQ0KDQpsYXlvdXQobWF0cml4KDE6MiwgbmNvbCA9IDIpKQ0KcGxvdChmaXQpDQpwbG90KGZpdCwgd2hpY2gubW9kZWwgPSAyKQ0KfQ0KYGBgDQoNCiMgTGltaXRhdGlvbnMgb2YgYmlnbWVtb3J5DQoNCiMjIFdoZXJlIGNhbiB5b3UgdXNlIGJpZ21lbW9yeT8NCi0gWW91IGNhbiB1c2UgYmlnbWVtb3J5IHdoZW4geW91ciBkYXRhIGFyZQ0KICAtIG1hdHJpY2VzDQogIC0gZGVuc2UNCiAgICAtIG5ldyBwYWNrYWdlIGBiaWdzcGFyc2VyYA0KICAtIG51bWVyaWMNCiAgDQotIFVuZGVybHlpbmcgZGF0YSBzdHJ1Y3R1cmVzIGFyZSBjb21wYXRpYmxlIHdpdGggbG93LWxldmVsIGxpbmVhciBhbGdlYnJhIGxpYnJhcmllcyBmb3IgZmFzdCBtb2RlbCBmaXR0aW5nLiAoVXNlIGMvYysrIGxpYnJhcmllcyBkaXJlY3RseSkNCg0KLSBpZiB5b3UgaGF2ZSBkaWZmZXJlbnQgY29sdW1uIHR5cGVzLCB5b3UgY291bGQgdHJ5IHRoZSBgZmZgIHBhY2FrZ2UsIHdoaWNoIGlzIHNpbWlsYXIgdG8gYmlnbWVtb3J5IGJ1dCBpbmNsdWRlcyBhIGRhdGEuZnJhbWUgbGlrZSBkYXRhIHN0cnVjdHVyZS4NCg0KIyMgVW5kZXJzdGFuZGluZyBkaXNrIGFjY2Vzcw0KDQo+IEEgYmlnLm1hdHJpeCBvYmplY3QgaXMgYSBkYXRhIHN0cnVjdHVyZSBkZXNpZ25lZCBmb3IgcmFuZG9tIGFjY2Vzcy4NCg0KLSBJZiB0aGUgZW50cmllcyByZXF1aXJlZCBhcmUgaW4gdGhlIFJBTSwgdGhlbiB0aGV5IHdpbGwgYmUgcmV0dXJuYmUgaW1tZWRpYXRlbHkuDQotIElmIHRoZSBlbnRyaWVzIHJlcXVpcmVkIGFyZSBub3QgaW4gdGhlIFJBTSwgdGhlbiB0aGV5IHdpbGwgYmUgaW1wb3J0ZWQgZnJvbSBkaXNrIHRvIHRoZSBSQU0uDQotIFNpbmNlIHRoZSByZXRyZWF0aW5nIGlzIGFsbW9zdCBlcXVhbGx5IGZhc3QsIHRoaXMgZGF0YSBzdHJ1Y3R1cmUgaXMgY2FsbGVkIHJhbmRvbSBhY2Nlc3MgZGF0YSBzdHJ1Y3R1cmUuDQogIA0KDQojIyBEaXNhZHZhbnRhZ2VzIG9mIHJhbmRvbSBhY2Nlc3MNCg0KLSBjYW4ndCBhZGQgcm93cyBvciBjb2x1bW5zIHRvIGFuIGV4aXN0aW5nIGBiaWcubWF0cml4YCBvYmplY3QNCi0geW91IG5lZWQgdG8gaGF2ZSBlbm91Z2ggZGlzayBzcGFjZSB0byBob2xkIHRoZSBlbnRpcmUgbWF0cml4IGluIG9uZSBiaWcgYmxvY2sgKGluIGEgc2luZ2xlIGRpc2spDQogIA0KDQojIFRoZSBEZXNpZ24gb2YgdGhlIGJpZy5tYXRyaXggY2xhc3MNCg0KDQoNCg0KIyBSZWZlcmVuY2UNCg0KLSBTY2FsYWJsZSBEYXRhIFByb2Nlc3NpbmcgaW4gUiwgZGF0YWNhbXANCg==