1 Creating and manipulating matrices

We will create a \(4 \times 2\) matrix, starting from a vector of values.

## Define the matrix X, filling matrix "byrow": 4 x 2
X <- matrix(c(3, 6, 1, 2, 0.23, 0.46, -2, -4), nrow = 4, byrow = TRUE)
X
#>       [,1]  [,2]
#> [1,]  3.00  6.00
#> [2,]  1.00  2.00
#> [3,]  0.23  0.46
#> [4,] -2.00 -4.00

## Alternative way to define the matrix, `byrow = FALSE` is the default
X <- matrix(c(3, 1, 0.23, -2, 6, 2, 0.46, -4), ncol = 2, byrow = FALSE)
X
#>       [,1]  [,2]
#> [1,]  3.00  6.00
#> [2,]  1.00  2.00
#> [3,]  0.23  0.46
#> [4,] -2.00 -4.00

# Creating a second matrix: 2 x 4, using the square of `X`
Y <- matrix(X^2, nrow = 2, ncol = 4)
Y
#>      [,1]   [,2] [,3]    [,4]
#> [1,]    9 0.0529   36  0.2116
#> [2,]    1 4.0000    4 16.0000

## Check that we really have a matrix
class(X)
#> [1] "matrix" "array"

Generally you can check the arguments of a function, e.g. matrix, in R with ?matrix.

The dim function tells you the dimensions of the matrix, nrow and ncol give you the number of rows and columns, respectively.

dim(X)  # nr. of rows, nr. of columns
#> [1] 4 2
nrow(X)
#> [1] 4
ncol(X)
#> [1] 2

dim(Y)
#> [1] 2 4

1.1 Subsetting

Note that R sees a matrix as X[row, column], so you can subset X with

X[1, ] # first row of X
#> [1] 3 6
X[, 2] # second column of X
#> [1]  6.00  2.00  0.46 -4.00
X[1:2, ] # first 2 rows of X
#>      [,1] [,2]
#> [1,]    3    6
#> [2,]    1    2

Note that, by default, subsetting a single row or column from a matrix returns a vector, not a column- or row-matrix as might be expected. We can see this from the fact that the subset does not have dimensions anymore (an attribute only defined for matrices in R):

dim(X[, 1]) # NULL = non-existent
#> NULL
class(X[, 1]) # not matrix but numeric vector
#> [1] "numeric"

This can lead to unexpected behavior when doing matrix multiplication (see further below). To avoid this, we can set drop = FALSE in the subsetting brackets.

## This creates a "column-vector"
X[, 1, drop = FALSE]
#>       [,1]
#> [1,]  3.00
#> [2,]  1.00
#> [3,]  0.23
#> [4,] -2.00
dim(X[, 1, drop = FALSE])
#> [1] 4 1

1.2 Row- and column-binding

You can also create matrices by ‘binding’ together vectors (of the same length) column-wise or row-wise with cbind() and rbind(), respectively.

# Create vectors from the rows of X
(x1 <- X[1, ])
#> [1] 3 6
(x2 <- X[2, ])
#> [1] 1 2
(x3 <- X[3, ])
#> [1] 0.23 0.46
(x4 <- X[4, ])
#> [1] -2 -4

# Bind them back together row-wise: re-creates X
rbind(x1, x2, x3, x4)
#>     [,1]  [,2]
#> x1  3.00  6.00
#> x2  1.00  2.00
#> x3  0.23  0.46
#> x4 -2.00 -4.00

# Bind them back together column-wise: (essentially the transpose of X)
cbind(x1, x2, x3, x4)
#>      x1 x2   x3 x4
#> [1,]  3  1 0.23 -2
#> [2,]  6  2 0.46 -4

Binding together 2 matrices also works (but be mindful of their dimensions!).

# Row-binding two matrices
# X and Y don't have the same dimensions (4x2 vs. 2x4),
# so binding them directly doesn't work
rbind(X, Y)
#> Error in rbind(X, Y): number of columns of matrices must match (see arg 2)

# For rbind we need the same number of columns
rbind(X, Y[, 1:2]) # works
#>       [,1]    [,2]
#> [1,]  3.00  6.0000
#> [2,]  1.00  2.0000
#> [3,]  0.23  0.4600
#> [4,] -2.00 -4.0000
#> [5,]  9.00  0.0529
#> [6,]  1.00  4.0000

# For cbind, same number of rows
cbind(X[1:2, ], Y)
#>      [,1] [,2] [,3]   [,4] [,5]    [,6]
#> [1,]    3    6    9 0.0529   36  0.2116
#> [2,]    1    2    1 4.0000    4 16.0000

Note that R automatically creates row names or column names from the variable names when using rbind() and cbind().

To add row and columns names to an existing matrix, use rownames(), colnames() or the more general dimnames().

rownames(X) <- c("row_1", "row_2", "row_3", "row_4")
colnames(X) <- c("col_1", "col_2")
X
#>       col_1 col_2
#> row_1  3.00  6.00
#> row_2  1.00  2.00
#> row_3  0.23  0.46
#> row_4 -2.00 -4.00

## Using `NULL` removes the dimnames again
dimnames(X) <- NULL
X
#>       [,1]  [,2]
#> [1,]  3.00  6.00
#> [2,]  1.00  2.00
#> [3,]  0.23  0.46
#> [4,] -2.00 -4.00

1.2.1 Diagonal elements and diagonal matrices: diag()

## Get diagonal elements from X, returns vector
diag(X)
#> [1] 3 2

## Create diagonal matrix, vector as input, returns matrix
diag(c(1, 2, 3 ,4))
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    0    0    0
#> [2,]    0    2    0    0
#> [3,]    0    0    3    0
#> [4,]    0    0    0    4

1.2.2 Identity matrices

The diag function can also be used to create identity matrices with specific dimensions

diag(1, nrow = 4, ncol = 4)  # 4 x 4
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    0    0    0
#> [2,]    0    1    0    0
#> [3,]    0    0    1    0
#> [4,]    0    0    0    1
diag(1, nrow = 2, ncol = 4)  # 2 x 4
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    0    0    0
#> [2,]    0    1    0    0
diag(1, nrow = 4, ncol = 2)  # 4 x 2
#>      [,1] [,2]
#> [1,]    1    0
#> [2,]    0    1
#> [3,]    0    0
#> [4,]    0    0

2 Math operations

2.1 Scalars and vectors

Scalar operations occur element-wise for matrices.

X # for reference
#>       [,1]  [,2]
#> [1,]  3.00  6.00
#> [2,]  1.00  2.00
#> [3,]  0.23  0.46
#> [4,] -2.00 -4.00
X + 1
#>       [,1]  [,2]
#> [1,]  4.00  7.00
#> [2,]  2.00  3.00
#> [3,]  1.23  1.46
#> [4,] -1.00 -3.00
X - 100
#>         [,1]    [,2]
#> [1,]  -97.00  -94.00
#> [2,]  -99.00  -98.00
#> [3,]  -99.77  -99.54
#> [4,] -102.00 -104.00
2 * X
#>       [,1]  [,2]
#> [1,]  6.00 12.00
#> [2,]  2.00  4.00
#> [3,]  0.46  0.92
#> [4,] -4.00 -8.00
X / 3
#>             [,1]       [,2]
#> [1,]  1.00000000  2.0000000
#> [2,]  0.33333333  0.6666667
#> [3,]  0.07666667  0.1533333
#> [4,] -0.66666667 -1.3333333
X^2
#>        [,1]    [,2]
#> [1,] 9.0000 36.0000
#> [2,] 1.0000  4.0000
#> [3,] 0.0529  0.2116
#> [4,] 4.0000 16.0000

Note that ^2 also occurs element-wise, not using matrix multiplication (see further below)!

You can also do operations with vectors, though you should be careful with the dimensions. R will add the vector to each column of the matrix. When using a vector with a different length than the number of rows (elements in a column), the vector elements will be recycled. When the longer object is not a multiple of the shorter, the operation still occurs but a warning is produced (technically a message).

X
#>       [,1]  [,2]
#> [1,]  3.00  6.00
#> [2,]  1.00  2.00
#> [3,]  0.23  0.46
#> [4,] -2.00 -4.00
v4 <- c(1, 2, 3, 4)  # vector of length 4
X + v4
#>      [,1] [,2]
#> [1,] 4.00 7.00
#> [2,] 3.00 4.00
#> [3,] 3.23 3.46
#> [4,] 2.00 0.00

v2 <- c(1, 2)  # vector of length 2, will be recycled
X + v2
#>      [,1]  [,2]
#> [1,] 4.00  7.00
#> [2,] 3.00  4.00
#> [3,] 1.23  1.46
#> [4,] 0.00 -2.00

v3 <- c(1, 2, 3) # vector or length 3, also recycled with warning
X + v3
#> Warning in X + v3: longer object length is not a multiple of shorter object
#> length
#>       [,1]  [,2]
#> [1,]  4.00  8.00
#> [2,]  3.00  5.00
#> [3,]  3.23  1.46
#> [4,] -1.00 -2.00

v5 <- c(1, 2, 3, 4, 5) # vector or length 5, also recycled with warning
X + v5
#> Warning in X + v5: longer object length is not a multiple of shorter object
#> length
#>      [,1]  [,2]
#> [1,] 4.00 11.00
#> [2,] 3.00  3.00
#> [3,] 3.23  2.46
#> [4,] 2.00 -1.00

2.1.0.1 Two matrices

Define a second matrix Y with same dimensions as X.

# We will generate a matrix Y with 2s and 4s at random places (using `sample`)
# Setting a seed allows reproducibility
set.seed(1)
Y <- matrix(sample(c(2, 4), size = nrow(X) * ncol(X), replace = TRUE),
            nrow = nrow(X), ncol = ncol(X))
Y
#>      [,1] [,2]
#> [1,]    2    4
#> [2,]    4    2
#> [3,]    2    2
#> [4,]    2    2

Using the standard math operators occurs element-wise.

X + Y
#>      [,1]  [,2]
#> [1,] 5.00 10.00
#> [2,] 5.00  4.00
#> [3,] 2.23  2.46
#> [4,] 0.00 -2.00
X - Y
#>       [,1]  [,2]
#> [1,]  1.00  2.00
#> [2,] -3.00  0.00
#> [3,] -1.77 -1.54
#> [4,] -4.00 -6.00
X * Y
#>       [,1]  [,2]
#> [1,]  6.00 24.00
#> [2,]  4.00  4.00
#> [3,]  0.46  0.92
#> [4,] -4.00 -8.00
X / Y
#>        [,1]  [,2]
#> [1,]  1.500  1.50
#> [2,]  0.250  1.00
#> [3,]  0.115  0.23
#> [4,] -1.000 -2.00
X ^ Y
#>        [,1]      [,2]
#> [1,] 9.0000 1296.0000
#> [2,] 1.0000    4.0000
#> [3,] 0.0529    0.2116
#> [4,] 4.0000   16.0000

2.1.1 Matrix algebra

https://www.statmethods.net/advstats/matrix.html

2.1.1.1 Transpose: t()

\(\mathbf{X}^T\)

t(X)
#>      [,1] [,2] [,3] [,4]
#> [1,]    3    1 0.23   -2
#> [2,]    6    2 0.46   -4

2.1.1.2 Matrix multiplication: %*%

Note that X and Y have the same dimensions, so multiplying them straight away doesn’t work (incompatible dimensions)

## This produces an error because the dimensions are not compatible
X %*% Y
#> Error in X %*% Y: non-conformable arguments

Instead, we can transpose X and compute \(\mathbf{X}^T \cdot \mathbf{Y}\):

t(X) %*% Y
#>       [,1]  [,2]
#> [1,]  6.46 10.46
#> [2,] 12.92 20.92

## `crossprod()` is a shortcut for this operation, and is slightly faster
crossprod(X, Y)
#>       [,1]  [,2]
#> [1,]  6.46 10.46
#> [2,] 12.92 20.92

## `tcrossprod()` calculates X.Y^T
X %*% t(Y)
#>       [,1]   [,2]   [,3]   [,4]
#> [1,]  30.0  24.00  18.00  18.00
#> [2,]  10.0   8.00   6.00   6.00
#> [3,]   2.3   1.84   1.38   1.38
#> [4,] -20.0 -16.00 -12.00 -12.00
tcrossprod(X, Y)
#>       [,1]   [,2]   [,3]   [,4]
#> [1,]  30.0  24.00  18.00  18.00
#> [2,]  10.0   8.00   6.00   6.00
#> [3,]   2.3   1.84   1.38   1.38
#> [4,] -20.0 -16.00 -12.00 -12.00

The crossprod() and tcrossprod() functions also work with a single matrix as input, in which case they calculate \(\mathbf{X}^T\mathbf{X}\) and \(\mathbf{X}\mathbf{X}^T\), respectively:

## X^TX
crossprod(X)
#>         [,1]    [,2]
#> [1,] 14.0529 28.1058
#> [2,] 28.1058 56.2116
## XX^T
tcrossprod(X)
#>        [,1]   [,2]    [,3]  [,4]
#> [1,]  45.00  15.00  3.4500 -30.0
#> [2,]  15.00   5.00  1.1500 -10.0
#> [3,]   3.45   1.15  0.2645  -2.3
#> [4,] -30.00 -10.00 -2.3000  20.0

As already mentioned before, subsetting a matrix with [ returns a vector by default. Doing matrix multiplication with vectors in R can be ambiguous as R will “guess” itself whether the vector should be treated as a column- or row-vector. See ?`%*%` for details.

The example below illustrates some potentially unexpected results

## Selecting the first column of X, we would expect this to be treated as a column
## vector with dimensions 4 x 1...
X[, 1]
#> [1]  3.00  1.00  0.23 -2.00
dim(X[, 1])
#> NULL
## ...but in R vectors don't have dimensions

## Mathematically, multiplicating the first column of X with X is not possible
## (multiplying a 4x1 with a 4x2 matrix doesn't work)
## So we would expect the following code to throw an error...
X[, 1] %*% X
#>         [,1]    [,2]
#> [1,] 14.0529 28.1058
## ...but it doesn't and instead returns the result as if X[, 1] is a row-vector

To avoid confusion and when you intend to use single rows or vectors from a matrix as they were single-row or single-column matrices, it’s better to use drop = FALSE when subsetting.

X1 <- X[, 1, drop = FALSE]
## Now X1 does have dimensions
dim(X1)
#> [1] 4 1

## And the following code throws an error
X1 %*% X
#> Error in X1 %*% X: non-conformable arguments

You can also do matrix multiplication with 2 vectors, in which case again R will guess if they should be treated as column- or row-vectors.

(a <- seq_len(3))
#> [1] 1 2 3
(b <- a + 1)
#> [1] 2 3 4
a %*% b
#>      [,1]
#> [1,]   20

2.1.1.3 Inverse: solve()

Inverse: \(\mathbf{X}^{-1}\) (only for square matrices)

## Create new square matrix Z
(Z <- matrix(1:4, nrow = 2, ncol = 2))
#>      [,1] [,2]
#> [1,]    1    3
#> [2,]    2    4

## Compute inverse
(Z_inv <- solve(Z))
#>      [,1] [,2]
#> [1,]   -2  1.5
#> [2,]    1 -0.5

## Check that it's indeed the inverse
Z %*% Z_inv  # the 2x2 identity matrix
#>      [,1] [,2]
#> [1,]    1    0
#> [2,]    0    1

Warning: DON’T USE X^-1 to compute a matrix inverse!

The notation might be confusing because of the way we mathematically describe the inverse of a matrix (\(\mathbf{X}^{-1}\)). In R, however, X^-1 computes element-wise inverses of each value.

Z^-1
#>      [,1]      [,2]
#> [1,]  1.0 0.3333333
#> [2,]  0.5 0.2500000

If you’re wondering why the name “solve” is used for the function to compute a matrix inverse, it’s because solve can do more than just that. See ?solve for details.

3 Rank of a matrix

Calculating the rank of the matrix \(\mathbf X\). There are several ways to do this in R. We’ll use the qr() function (see ?qr). An alternative would be to use rankMatrix from the Matrix package: Matrix::rankMatrix().

## Run `qr` and access the `rank` from the results with `$`
rank <- qr(X)$rank
rank
#> [1] 1

We see that the rank of X is lower than its number of columns and rows, meaning the matrix is not of full rank.

Considering the contents of X, can you see why this is?

Hint:

all(X[, 2] == 2 * X[, 1])
#> [1] TRUE
LS0tCnRpdGxlOiAiV29ya2luZyB3aXRoIG1hdHJpY2VzIGluIFIiCmF1dGhvcjogIkFkYXB0ZWQgYnkgTWlsYW4gTWFsZmFpdCIKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJUIgJWQsICVZIilgJwotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBjYWNoZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbGxhcHNlID0gVFJVRSwKICBjb21tZW50ID0gIiM+IgopCmBgYAoKKioqCgojIENyZWF0aW5nIGFuZCBtYW5pcHVsYXRpbmcgbWF0cmljZXMKCldlIHdpbGwgY3JlYXRlIGEgJDQgXHRpbWVzIDIkIG1hdHJpeCwgc3RhcnRpbmcgZnJvbSBhIHZlY3RvciBvZiB2YWx1ZXMuCgpgYGB7ciBjcmVhdGVfbWF0cml4fQojIyBEZWZpbmUgdGhlIG1hdHJpeCBYLCBmaWxsaW5nIG1hdHJpeCAiYnlyb3ciOiA0IHggMgpYIDwtIG1hdHJpeChjKDMsIDYsIDEsIDIsIDAuMjMsIDAuNDYsIC0yLCAtNCksIG5yb3cgPSA0LCBieXJvdyA9IFRSVUUpClgKCiMjIEFsdGVybmF0aXZlIHdheSB0byBkZWZpbmUgdGhlIG1hdHJpeCwgYGJ5cm93ID0gRkFMU0VgIGlzIHRoZSBkZWZhdWx0ClggPC0gbWF0cml4KGMoMywgMSwgMC4yMywgLTIsIDYsIDIsIDAuNDYsIC00KSwgbmNvbCA9IDIsIGJ5cm93ID0gRkFMU0UpClgKCiMgQ3JlYXRpbmcgYSBzZWNvbmQgbWF0cml4OiAyIHggNCwgdXNpbmcgdGhlIHNxdWFyZSBvZiBgWGAKWSA8LSBtYXRyaXgoWF4yLCBucm93ID0gMiwgbmNvbCA9IDQpClkKCiMjIENoZWNrIHRoYXQgd2UgcmVhbGx5IGhhdmUgYSBtYXRyaXgKY2xhc3MoWCkKYGBgCkdlbmVyYWxseSB5b3UgY2FuIGNoZWNrIHRoZSBhcmd1bWVudHMgb2YgYSBmdW5jdGlvbiwgZS5nLiBgbWF0cml4YCwgaW4gUiB3aXRoIGA/bWF0cml4YC4KClRoZSBgZGltYCBmdW5jdGlvbiB0ZWxscyB5b3UgdGhlIF9fZGltZW5zaW9uc19fIG9mIHRoZSBtYXRyaXgsIGBucm93YCBhbmQgYG5jb2xgIGdpdmUgeW91IHRoZSBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucywgcmVzcGVjdGl2ZWx5LgoKYGBge3J9CmRpbShYKSAgIyBuci4gb2Ygcm93cywgbnIuIG9mIGNvbHVtbnMKbnJvdyhYKQpuY29sKFgpCgpkaW0oWSkKYGBgCgoKIyMgU3Vic2V0dGluZwoKTm90ZSB0aGF0IFIgc2VlcyBhIG1hdHJpeCBhcyBgWFtyb3csIGNvbHVtbl1gLCBzbyB5b3UgY2FuIHN1YnNldCBgWGAgd2l0aAoKYGBge3Igc3Vic2V0X21hdHJpeH0KWFsxLCBdICMgZmlyc3Qgcm93IG9mIFgKWFssIDJdICMgc2Vjb25kIGNvbHVtbiBvZiBYClhbMToyLCBdICMgZmlyc3QgMiByb3dzIG9mIFgKYGBgCgpOb3RlIHRoYXQsIGJ5IGRlZmF1bHQsIHN1YnNldHRpbmcgYSBzaW5nbGUgcm93IG9yIGNvbHVtbiBmcm9tIGEgbWF0cml4IF9fcmV0dXJucyBhIHZlY3Rvcl9fLCBub3QgYSBjb2x1bW4tIG9yIHJvdy1tYXRyaXggYXMgbWlnaHQgYmUgZXhwZWN0ZWQuCldlIGNhbiBzZWUgdGhpcyBmcm9tIHRoZSBmYWN0IHRoYXQgdGhlIHN1YnNldCBkb2VzIG5vdCBoYXZlIGRpbWVuc2lvbnMgYW55bW9yZSAoYW4gYXR0cmlidXRlIG9ubHkgZGVmaW5lZCBmb3IgbWF0cmljZXMgaW4gUik6CgpgYGB7cn0KZGltKFhbLCAxXSkgIyBOVUxMID0gbm9uLWV4aXN0ZW50CmNsYXNzKFhbLCAxXSkgIyBub3QgbWF0cml4IGJ1dCBudW1lcmljIHZlY3RvcgpgYGAKClRoaXMgY2FuIGxlYWQgdG8gdW5leHBlY3RlZCBiZWhhdmlvciB3aGVuIGRvaW5nIF9fbWF0cml4IG11bHRpcGxpY2F0aW9uX18gKHNlZSBmdXJ0aGVyIGJlbG93KS4KVG8gYXZvaWQgdGhpcywgd2UgY2FuIHNldCBgZHJvcCA9IEZBTFNFYCBpbiB0aGUgc3Vic2V0dGluZyBicmFja2V0cy4KCmBgYHtyfQojIyBUaGlzIGNyZWF0ZXMgYSAiY29sdW1uLXZlY3RvciIKWFssIDEsIGRyb3AgPSBGQUxTRV0KZGltKFhbLCAxLCBkcm9wID0gRkFMU0VdKQpgYGAKCgojIyBSb3ctIGFuZCBjb2x1bW4tYmluZGluZwoKWW91IGNhbiBhbHNvIGNyZWF0ZSBtYXRyaWNlcyBieSAnYmluZGluZycgdG9nZXRoZXIgdmVjdG9ycyAob2YgdGhlIHNhbWUgbGVuZ3RoKSBjb2x1bW4td2lzZSBvciByb3ctd2lzZSB3aXRoIGBjYmluZCgpYCBhbmQgYHJiaW5kKClgLCByZXNwZWN0aXZlbHkuCgpgYGB7cn0KIyBDcmVhdGUgdmVjdG9ycyBmcm9tIHRoZSByb3dzIG9mIFgKKHgxIDwtIFhbMSwgXSkKKHgyIDwtIFhbMiwgXSkKKHgzIDwtIFhbMywgXSkKKHg0IDwtIFhbNCwgXSkKCiMgQmluZCB0aGVtIGJhY2sgdG9nZXRoZXIgcm93LXdpc2U6IHJlLWNyZWF0ZXMgWApyYmluZCh4MSwgeDIsIHgzLCB4NCkKCiMgQmluZCB0aGVtIGJhY2sgdG9nZXRoZXIgY29sdW1uLXdpc2U6IChlc3NlbnRpYWxseSB0aGUgdHJhbnNwb3NlIG9mIFgpCmNiaW5kKHgxLCB4MiwgeDMsIHg0KQpgYGAKCkJpbmRpbmcgdG9nZXRoZXIgMiBtYXRyaWNlcyBhbHNvIHdvcmtzIChidXQgYmUgbWluZGZ1bCBvZiB0aGVpciBkaW1lbnNpb25zISkuCgpgYGB7ciwgZXJyb3I9VFJVRX0KIyBSb3ctYmluZGluZyB0d28gbWF0cmljZXMKIyBYIGFuZCBZIGRvbid0IGhhdmUgdGhlIHNhbWUgZGltZW5zaW9ucyAoNHgyIHZzLiAyeDQpLAojIHNvIGJpbmRpbmcgdGhlbSBkaXJlY3RseSBkb2Vzbid0IHdvcmsKcmJpbmQoWCwgWSkKCiMgRm9yIHJiaW5kIHdlIG5lZWQgdGhlIHNhbWUgbnVtYmVyIG9mIGNvbHVtbnMKcmJpbmQoWCwgWVssIDE6Ml0pICMgd29ya3MKCiMgRm9yIGNiaW5kLCBzYW1lIG51bWJlciBvZiByb3dzCmNiaW5kKFhbMToyLCBdLCBZKQpgYGAKCk5vdGUgdGhhdCBSIGF1dG9tYXRpY2FsbHkgY3JlYXRlcyByb3cgbmFtZXMgb3IgY29sdW1uIG5hbWVzIGZyb20gdGhlIHZhcmlhYmxlIG5hbWVzIHdoZW4gdXNpbmcgYHJiaW5kKClgIGFuZCBgY2JpbmQoKWAuCgpUbyBhZGQgcm93IGFuZCBjb2x1bW5zIG5hbWVzIHRvIGFuIGV4aXN0aW5nIG1hdHJpeCwgdXNlIGByb3duYW1lcygpYCwgYGNvbG5hbWVzKClgIG9yIHRoZSBtb3JlIGdlbmVyYWwgYGRpbW5hbWVzKClgLgoKYGBge3J9CnJvd25hbWVzKFgpIDwtIGMoInJvd18xIiwgInJvd18yIiwgInJvd18zIiwgInJvd180IikKY29sbmFtZXMoWCkgPC0gYygiY29sXzEiLCAiY29sXzIiKQpYCgojIyBVc2luZyBgTlVMTGAgcmVtb3ZlcyB0aGUgZGltbmFtZXMgYWdhaW4KZGltbmFtZXMoWCkgPC0gTlVMTApYCmBgYAoKCiMjIyBEaWFnb25hbCBlbGVtZW50cyBhbmQgZGlhZ29uYWwgbWF0cmljZXM6IGBkaWFnKClgCgpgYGB7cn0KIyMgR2V0IGRpYWdvbmFsIGVsZW1lbnRzIGZyb20gWCwgcmV0dXJucyB2ZWN0b3IKZGlhZyhYKQoKIyMgQ3JlYXRlIGRpYWdvbmFsIG1hdHJpeCwgdmVjdG9yIGFzIGlucHV0LCByZXR1cm5zIG1hdHJpeApkaWFnKGMoMSwgMiwgMyAsNCkpCmBgYAoKIyMjIElkZW50aXR5IG1hdHJpY2VzCgpUaGUgYGRpYWdgIGZ1bmN0aW9uIGNhbiBhbHNvIGJlIHVzZWQgdG8gY3JlYXRlIGlkZW50aXR5IG1hdHJpY2VzIHdpdGggc3BlY2lmaWMgZGltZW5zaW9ucwoKYGBge3J9CmRpYWcoMSwgbnJvdyA9IDQsIG5jb2wgPSA0KSAgIyA0IHggNApkaWFnKDEsIG5yb3cgPSAyLCBuY29sID0gNCkgICMgMiB4IDQKZGlhZygxLCBucm93ID0gNCwgbmNvbCA9IDIpICAjIDQgeCAyCmBgYAoKCgojIE1hdGggb3BlcmF0aW9ucwoKIyMgU2NhbGFycyBhbmQgdmVjdG9ycwoKU2NhbGFyIG9wZXJhdGlvbnMgb2NjdXIgZWxlbWVudC13aXNlIGZvciBtYXRyaWNlcy4KCmBgYHtyfQpYICMgZm9yIHJlZmVyZW5jZQpYICsgMQpYIC0gMTAwCjIgKiBYClggLyAzClheMgpgYGAKCk5vdGUgdGhhdCBgXjJgIGFsc28gb2NjdXJzIGVsZW1lbnQtd2lzZSwgKm5vdCogdXNpbmcgbWF0cml4IG11bHRpcGxpY2F0aW9uIChzZWUgZnVydGhlciBiZWxvdykhCgpZb3UgY2FuIGFsc28gZG8gb3BlcmF0aW9ucyB3aXRoIHZlY3RvcnMsIHRob3VnaCB5b3Ugc2hvdWxkIGJlIGNhcmVmdWwgd2l0aCB0aGUgZGltZW5zaW9ucy4KYFJgIHdpbGwgYWRkIHRoZSB2ZWN0b3IgdG8gZWFjaCBfX2NvbHVtbl9fIG9mIHRoZSBtYXRyaXguCldoZW4gdXNpbmcgYSB2ZWN0b3Igd2l0aCBhIGRpZmZlcmVudCBsZW5ndGggdGhhbiB0aGUgbnVtYmVyIG9mIHJvd3MgKGVsZW1lbnRzIGluIGEgY29sdW1uKSwgdGhlIHZlY3RvciBlbGVtZW50cyB3aWxsIGJlICpyZWN5Y2xlZCouCldoZW4gdGhlIGxvbmdlciBvYmplY3QgaXMgbm90IGEgbXVsdGlwbGUgb2YgdGhlIHNob3J0ZXIsIHRoZSBvcGVyYXRpb24gc3RpbGwgb2NjdXJzIGJ1dCBhICp3YXJuaW5nKiBpcyBwcm9kdWNlZCAodGVjaG5pY2FsbHkgYSBgbWVzc2FnZWApLgoKYGBge3J9ClgKdjQgPC0gYygxLCAyLCAzLCA0KSAgIyB2ZWN0b3Igb2YgbGVuZ3RoIDQKWCArIHY0Cgp2MiA8LSBjKDEsIDIpICAjIHZlY3RvciBvZiBsZW5ndGggMiwgd2lsbCBiZSByZWN5Y2xlZApYICsgdjIKCnYzIDwtIGMoMSwgMiwgMykgIyB2ZWN0b3Igb3IgbGVuZ3RoIDMsIGFsc28gcmVjeWNsZWQgd2l0aCB3YXJuaW5nClggKyB2MwoKdjUgPC0gYygxLCAyLCAzLCA0LCA1KSAjIHZlY3RvciBvciBsZW5ndGggNSwgYWxzbyByZWN5Y2xlZCB3aXRoIHdhcm5pbmcKWCArIHY1CmBgYAoKIyMjIyBUd28gbWF0cmljZXMKCkRlZmluZSBhIHNlY29uZCBtYXRyaXggYFlgIHdpdGggc2FtZSBkaW1lbnNpb25zIGFzIGBYYC4KCmBgYHtyfQojIFdlIHdpbGwgZ2VuZXJhdGUgYSBtYXRyaXggWSB3aXRoIDJzIGFuZCA0cyBhdCByYW5kb20gcGxhY2VzICh1c2luZyBgc2FtcGxlYCkKIyBTZXR0aW5nIGEgc2VlZCBhbGxvd3MgcmVwcm9kdWNpYmlsaXR5CnNldC5zZWVkKDEpClkgPC0gbWF0cml4KHNhbXBsZShjKDIsIDQpLCBzaXplID0gbnJvdyhYKSAqIG5jb2woWCksIHJlcGxhY2UgPSBUUlVFKSwKICAgICAgICAgICAgbnJvdyA9IG5yb3coWCksIG5jb2wgPSBuY29sKFgpKQpZCmBgYAoKVXNpbmcgdGhlIHN0YW5kYXJkIG1hdGggb3BlcmF0b3JzIG9jY3VycyBlbGVtZW50LXdpc2UuCgpgYGB7cn0KWCArIFkKWCAtIFkKWCAqIFkKWCAvIFkKWCBeIFkKYGBgCgoKIyMjIE1hdHJpeCBhbGdlYnJhCgo8aHR0cHM6Ly93d3cuc3RhdG1ldGhvZHMubmV0L2FkdnN0YXRzL21hdHJpeC5odG1sPgoKIyMjIyBUcmFuc3Bvc2U6IGB0KClgCgokXG1hdGhiZntYfV5UJAoKYGBge3J9CnQoWCkKYGBgCgojIyMjIE1hdHJpeCBtdWx0aXBsaWNhdGlvbjogYCUqJWAKCk5vdGUgdGhhdCBgWGAgYW5kIGBZYCBoYXZlIHRoZSBzYW1lIGRpbWVuc2lvbnMsIHNvIG11bHRpcGx5aW5nIHRoZW0gc3RyYWlnaHQgYXdheSBkb2Vzbid0IHdvcmsgKGluY29tcGF0aWJsZSBkaW1lbnNpb25zKQoKYGBge3IsIGVycm9yPVRSVUV9CiMjIFRoaXMgcHJvZHVjZXMgYW4gZXJyb3IgYmVjYXVzZSB0aGUgZGltZW5zaW9ucyBhcmUgbm90IGNvbXBhdGlibGUKWCAlKiUgWQpgYGAKCkluc3RlYWQsIHdlIGNhbiB0cmFuc3Bvc2UgYFhgIGFuZCBjb21wdXRlICRcbWF0aGJme1h9XlQgXGNkb3QgXG1hdGhiZntZfSQ6CgpgYGB7cn0KdChYKSAlKiUgWQoKIyMgYGNyb3NzcHJvZCgpYCBpcyBhIHNob3J0Y3V0IGZvciB0aGlzIG9wZXJhdGlvbiwgYW5kIGlzIHNsaWdodGx5IGZhc3Rlcgpjcm9zc3Byb2QoWCwgWSkKCiMjIGB0Y3Jvc3Nwcm9kKClgIGNhbGN1bGF0ZXMgWC5ZXlQKWCAlKiUgdChZKQp0Y3Jvc3Nwcm9kKFgsIFkpCmBgYAoKVGhlIGBjcm9zc3Byb2QoKWAgYW5kIGB0Y3Jvc3Nwcm9kKClgIGZ1bmN0aW9ucyBhbHNvIHdvcmsgd2l0aCBhIHNpbmdsZSBtYXRyaXggYXMgaW5wdXQsIGluIHdoaWNoIGNhc2UgdGhleSBjYWxjdWxhdGUgJFxtYXRoYmZ7WH1eVFxtYXRoYmZ7WH0kIGFuZCAkXG1hdGhiZntYfVxtYXRoYmZ7WH1eVCQsIHJlc3BlY3RpdmVseToKCmBgYHtyfQojIyBYXlRYCmNyb3NzcHJvZChYKQojIyBYWF5UCnRjcm9zc3Byb2QoWCkKYGBgCgpBcyBhbHJlYWR5IG1lbnRpb25lZCBiZWZvcmUsIHN1YnNldHRpbmcgYSBtYXRyaXggd2l0aCBgW2AgcmV0dXJucyBhIHZlY3RvciBieSBkZWZhdWx0LgpEb2luZyBtYXRyaXggbXVsdGlwbGljYXRpb24gd2l0aCB2ZWN0b3JzIGluIFIgY2FuIGJlIGFtYmlndW91cyBhcyBSIHdpbGwgImd1ZXNzIiBpdHNlbGYgd2hldGhlciB0aGUgdmVjdG9yIHNob3VsZCBiZSB0cmVhdGVkIGFzIGEgY29sdW1uLSBvciByb3ctdmVjdG9yLgpTZWUgYGAgP2AlKiVgIGBgIGZvciBkZXRhaWxzLgoKVGhlIGV4YW1wbGUgYmVsb3cgaWxsdXN0cmF0ZXMgc29tZSBwb3RlbnRpYWxseSB1bmV4cGVjdGVkIHJlc3VsdHMKCmBgYHtyLCBlcnJvcj1UUlVFfQojIyBTZWxlY3RpbmcgdGhlIGZpcnN0IGNvbHVtbiBvZiBYLCB3ZSB3b3VsZCBleHBlY3QgdGhpcyB0byBiZSB0cmVhdGVkIGFzIGEgY29sdW1uCiMjIHZlY3RvciB3aXRoIGRpbWVuc2lvbnMgNCB4IDEuLi4KWFssIDFdCmRpbShYWywgMV0pCiMjIC4uLmJ1dCBpbiBSIHZlY3RvcnMgZG9uJ3QgaGF2ZSBkaW1lbnNpb25zCgojIyBNYXRoZW1hdGljYWxseSwgbXVsdGlwbGljYXRpbmcgdGhlIGZpcnN0IGNvbHVtbiBvZiBYIHdpdGggWCBpcyBub3QgcG9zc2libGUKIyMgKG11bHRpcGx5aW5nIGEgNHgxIHdpdGggYSA0eDIgbWF0cml4IGRvZXNuJ3Qgd29yaykKIyMgU28gd2Ugd291bGQgZXhwZWN0IHRoZSBmb2xsb3dpbmcgY29kZSB0byB0aHJvdyBhbiBlcnJvci4uLgpYWywgMV0gJSolIFgKIyMgLi4uYnV0IGl0IGRvZXNuJ3QgYW5kIGluc3RlYWQgcmV0dXJucyB0aGUgcmVzdWx0IGFzIGlmIFhbLCAxXSBpcyBhIHJvdy12ZWN0b3IKYGBgCgpUbyBhdm9pZCBjb25mdXNpb24gYW5kIHdoZW4geW91IGludGVuZCB0byB1c2Ugc2luZ2xlIHJvd3Mgb3IgdmVjdG9ycyBmcm9tIGEgbWF0cml4IGFzIHRoZXkgd2VyZSBzaW5nbGUtcm93IG9yIHNpbmdsZS1jb2x1bW4gbWF0cmljZXMsIGl0J3MgYmV0dGVyIHRvIHVzZSBgZHJvcCA9IEZBTFNFYCB3aGVuIHN1YnNldHRpbmcuCgpgYGB7ciwgZXJyb3I9VFJVRX0KWDEgPC0gWFssIDEsIGRyb3AgPSBGQUxTRV0KIyMgTm93IFgxIGRvZXMgaGF2ZSBkaW1lbnNpb25zCmRpbShYMSkKCiMjIEFuZCB0aGUgZm9sbG93aW5nIGNvZGUgdGhyb3dzIGFuIGVycm9yClgxICUqJSBYCmBgYAoKWW91IGNhbiBhbHNvIGRvIG1hdHJpeCBtdWx0aXBsaWNhdGlvbiB3aXRoIDIgdmVjdG9ycywgaW4gd2hpY2ggY2FzZSBhZ2FpbiBSIHdpbGwgZ3Vlc3MgaWYgdGhleSBzaG91bGQgYmUgdHJlYXRlZCBhcyBjb2x1bW4tIG9yIHJvdy12ZWN0b3JzLgoKYGBge3J9CihhIDwtIHNlcV9sZW4oMykpCihiIDwtIGEgKyAxKQphICUqJSBiCmBgYAoKCiMjIyMgSW52ZXJzZTogYHNvbHZlKClgCgpfX0ludmVyc2VfXzogJFxtYXRoYmZ7WH1eey0xfSQgKG9ubHkgZm9yIHNxdWFyZSBtYXRyaWNlcykKCmBgYHtyfQojIyBDcmVhdGUgbmV3IHNxdWFyZSBtYXRyaXggWgooWiA8LSBtYXRyaXgoMTo0LCBucm93ID0gMiwgbmNvbCA9IDIpKQoKIyMgQ29tcHV0ZSBpbnZlcnNlCihaX2ludiA8LSBzb2x2ZShaKSkKCiMjIENoZWNrIHRoYXQgaXQncyBpbmRlZWQgdGhlIGludmVyc2UKWiAlKiUgWl9pbnYgICMgdGhlIDJ4MiBpZGVudGl0eSBtYXRyaXgKYGBgCgpfX1dhcm5pbmc6X18gRE9OJ1QgVVNFIGBYXi0xYCB0byBjb21wdXRlIGEgbWF0cml4IGludmVyc2UhCgpUaGUgbm90YXRpb24gbWlnaHQgYmUgY29uZnVzaW5nIGJlY2F1c2Ugb2YgdGhlIHdheSB3ZSBtYXRoZW1hdGljYWxseSBkZXNjcmliZSB0aGUgaW52ZXJzZSBvZiBhIG1hdHJpeCAoJFxtYXRoYmZ7WH1eey0xfSQpLgpJbiBSLCBob3dldmVyLCBgWF4tMWAgY29tcHV0ZXMgX19lbGVtZW50LXdpc2VfXyBpbnZlcnNlcyBvZiBlYWNoIHZhbHVlLgoKYGBge3J9ClpeLTEKYGBgCgpJZiB5b3UncmUgd29uZGVyaW5nIHdoeSB0aGUgbmFtZSAiYHNvbHZlYCIgaXMgdXNlZCBmb3IgdGhlIGZ1bmN0aW9uIHRvIGNvbXB1dGUgYSBtYXRyaXggaW52ZXJzZSwgaXQncyBiZWNhdXNlIGBzb2x2ZWAgY2FuIGRvIG1vcmUgdGhhbiBqdXN0IHRoYXQuClNlZSBgP3NvbHZlYCBmb3IgZGV0YWlscy4KCgojIFJhbmsgb2YgYSBtYXRyaXgKCkNhbGN1bGF0aW5nIHRoZSByYW5rIG9mIHRoZSBtYXRyaXggJFxtYXRoYmYgWCQuClRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gZG8gdGhpcyBpbiBSLgpXZSdsbCB1c2UgdGhlIGBxcigpYCBmdW5jdGlvbiAoc2VlIGA/cXJgKS4KQW4gYWx0ZXJuYXRpdmUgd291bGQgYmUgdG8gdXNlIGByYW5rTWF0cml4YCBmcm9tIHRoZSBbKk1hdHJpeCpdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9TWF0cml4KSBwYWNrYWdlOiBgTWF0cml4OjpyYW5rTWF0cml4KClgLgoKYGBge3IgcmFua30KIyMgUnVuIGBxcmAgYW5kIGFjY2VzcyB0aGUgYHJhbmtgIGZyb20gdGhlIHJlc3VsdHMgd2l0aCBgJGAKcmFuayA8LSBxcihYKSRyYW5rCnJhbmsKYGBgCgpXZSBzZWUgdGhhdCB0aGUgcmFuayBvZiBgWGAgaXMgbG93ZXIgdGhhbiBpdHMgbnVtYmVyIG9mIGNvbHVtbnMgYW5kIHJvd3MsIG1lYW5pbmcgdGhlIG1hdHJpeCBpcyBbKm5vdCogb2YgZnVsbCByYW5rXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SYW5rXyhsaW5lYXJfYWxnZWJyYSkjTWFpbl9kZWZpbml0aW9ucykuCgpDb25zaWRlcmluZyB0aGUgY29udGVudHMgb2YgYFhgLCBjYW4geW91IHNlZSB3aHkgdGhpcyBpcz8KCl9fSGludF9fOgoKYGBge3J9CmFsbChYWywgMl0gPT0gMiAqIFhbLCAxXSkKYGBgCg==