Julius Schmid
At the beginning of the chapter, I said that a matrix is just a
vector but with two additional attributes: the number of rows and the
number of columns. Here, we’ll take a closer look at the vector nature
of matrices. Consider this example:
z <- matrix(1:15,nrow=5)
z
[,1] [,2] [,3]
[1,] 1 6 11
[2,] 2 7 12
[3,] 3 8 13
[4,] 4 9 14
[5,] 5 10 15
Return the length of z. Since z is still a vector, we can query its
length:
length(z)
[1] 15
Let us print out the class of z:
class(z)
[1] "matrix" "array"
We get two outputs! This is because z can be interpreted as a matrix
as well as an array.
When printing the dimension, z is interpreted as the above matrix
with 5 rows and 3 columns.
dim(z)
[1] 5 3
Avoiding Unintended Dimension Reduction In the world
of statistics, dimension reduction is a good thing, with many
statistical procedures aimed to do it well. If we are working with, say,
10 variables and can reduce that number to 3 that still capture the
essence of our data, we’re happy. However, in R, something else might
merit the name dimension reduction that we may sometimes wish to avoid.
Say we have a five-row matrix and extract a row from it:
First, le tus visualize the matrix z again:
z
[,1] [,2] [,3]
[1,] 1 6 11
[2,] 2 7 12
[3,] 3 8 13
[4,] 4 9 14
[5,] 5 10 15
Now, return the second row of z and save the result as a new vector
called r:
r <- z[2,]
r
[1] 2 7 12
This seems innocuous, but note the format in which R has displayed r.
It’s a vector format, not a matrix format. In other words, r is a vector
of length 3, rather than a 1-by-3 matrix. We can confirm this in a
couple of ways:
The funtion attributes returns the dimension of matrices.
When entering the matrix z as an input, we get its number of rows and
columns:
attributes(z)
$dim
[1] 5 3
However, when setting r as an argument, there seems to be no
dimension at all.
attributes(r)
NULL
In a similar way, we can confirm our assumptions by using the
structure function str().
Input z:
str(z)
int [1:5, 1:3] 1 2 3 4 5 6 7 8 9 10 ...
We can access values of z with
two-dimensional indexing.
On the other hand, let us set r as an input :
str(r)
int [1:3] 2 7 12
We can access values of r with
one-dimensional indexing.
Fortunately, R has a way to suppress this dimension reduction: the
drop argument. Here’s an example, using the matrix z from above:
r <- z[2,, drop=FALSE]
r
[,1] [,2] [,3]
[1,] 2 7 12
Setting drop=FALSE, r will be interpreted as a matrix again. We can
confirm this by applying more functions to our new r:
First, apply the dim() function:
dim(r)
[1] 1 3
Now, r is interpreted as matrix with 1 row and 3 columns.
Applay the class() function next:
class(r)
[1] "matrix" "array"
This is exactly the output that we got above when we applied the
class() function to z. Indeed, r is now interpreted as a matrix as
well.
Naming Matrix Rows and Columns
The natural way to refer to rows and columns in a matrix is via the
row and column numbers. However, you can also give names to these
entities. Here’s an example:
Let us display z again:
z
[,1] [,2] [,3]
[1,] 1 6 11
[2,] 2 7 12
[3,] 3 8 13
[4,] 4 9 14
[5,] 5 10 15
Apply the function colnames():
colnames(z)
NULL
We observe that z has no column names. We can add them by assigning a
vector to the variable colnames(z):
colnames(z) <- c("First Column","Second Column", "Third Column")
Let us see whta happens if we return z now:
z
First Column Second Column Third Column
[1,] 1 6 11
[2,] 2 7 12
[3,] 3 8 13
[4,] 4 9 14
[5,] 5 10 15
Instead of the the indices [,1], [,2], and [,3] our assigned column
names are now displayed above each column.
We can also return just the column names without the specific column
values of the matrix:
colnames(z)
[1] "First Column" "Second Column" "Third Column"
Also, we can return a specific column, not using the column indices
1, 2, and 3, but rather the colum names instead:
z[,"Third Column"]
[1] 11 12 13 14 15
Higher-Dimensional Arrays
In a statistical context, a typical matrix in R has rows
corresponding to observations, say on various people, and columns
corresponding to variables, such as weight and blood pressure. The
matrix is then a two-dimensional data structure. But suppose we also
have data taken at different times, one data point per person per
variable per time. Time then becomes the third dimension, in addition to
rows and columns. In R, such data sets are called arrays. As a simple
example, consider students and test scores. Say each test consists of
two parts, so we record two scores for a student for each test. Now
suppose that we have two tests, and to keep the example small, assume we
have only three students. Here’s the data for the first test:
firsttest <- matrix(nrow=3,ncol=4)
firsttest[1,1] <- 12
firsttest[2,1] <- 24
firsttest[1,2] <- 31
firsttest[2,2] <- 39
firsttest[3,1] <- 21
firsttest[3,2] <- 35
firsttest[1,3] <- 4
firsttest[2,3] <- 2
firsttest[1,4] <- 30
firsttest[2,4] <- 25
firsttest[3,3] <- 5
firsttest[3,4] <- 22
Let us print the newly created matrix:
firsttest
[,1] [,2] [,3] [,4]
[1,] 12 31 4 30
[2,] 24 39 2 25
[3,] 21 35 5 22
Student 1 had scores of 12, 31, 4, and 30 on the first test, student
2 scored 24, 39, 2, and 25 and so on. Here are the scores for the same
students on the second test:
secondtest <- matrix(nrow=3,ncol=4)
secondtest[1,1] <- 14
secondtest[2,1] <- 22
secondtest[1,2] <- 36
secondtest[2,2] <- 37
secondtest[3,1] <- 19
secondtest[3,2] <- 35
secondtest[1,3] <- 5
secondtest[2,3] <- 6
secondtest[1,4] <- 28
secondtest[2,4] <- 22
secondtest[3,3] <- 4
secondtest[3,4] <- 27
Let us print the results for the second test:
secondtest
[,1] [,2] [,3] [,4]
[1,] 14 36 5 28
[2,] 22 37 6 22
[3,] 19 35 4 27
Now let’s put both tests into one data structure, which we’ll name
tests. We’ll arrange it to have two “layers”—one layer per test—with
three rows and four columns within each layer. We’ll store firsttest in
the first layer and secondtest in the second. In layer 1, there will be
three rows for the three students’ scores on the first test, with four
columns per row for the four portions of a test. We use R’s array
function to create the data structure:
tests <- array(data=c(firsttest,secondtest),dim=c(3,4,2))
In the argument dim=c(3,4,2), we are specifying two layers (this is
the 2 in the third argument), each consisting of three rows and four
columns. This then becomes an attribute of the data structure:
attributes(tests)
$dim
[1] 3 4 2
Each element of tests now has three subscripts, rather than two as in
the matrix case. The first subscript corresponds to the first element in
the $dim vector, the second subscript corresponds to the second element
in the vector, and so on. For instance, the score on the second portion
of test 1 for student 3 is retrieved as follows:
tests[3,2,1]
[1] 35
If we now print the new array tests, we get firsttest in the first
layer [,,1] and secondtest in the second layer [,,2]:
tests
, , 1
[,1] [,2] [,3] [,4]
[1,] 12 31 4 30
[2,] 24 39 2 25
[3,] 21 35 5 22
, , 2
[,1] [,2] [,3] [,4]
[1,] 14 36 5 28
[2,] 22 37 6 22
[3,] 19 35 4 27
Just as we built our three-dimensional array by combining two
matrices, we can build four-dimensional arrays by combining two or more
three dimensional arrays, and so on.
LS0tCnRpdGxlOiAiSW4tY2xhc3MgYWN0aXZpdHkgNyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSnVsaXVzIFNjaG1pZAoKQXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgY2hhcHRlciwgSSBzYWlkIHRoYXQgYSBtYXRyaXggaXMganVzdCBhIHZlY3RvciBidXQgd2l0aCB0d28gYWRkaXRpb25hbCBhdHRyaWJ1dGVzOiB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIHRoZSBudW1iZXIgb2YgY29sdW1ucy4gSGVyZSwgd2XigJlsbCB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhlIHZlY3RvciBuYXR1cmUgb2YgbWF0cmljZXMuIENvbnNpZGVyIHRoaXMgZXhhbXBsZToKCmBgYHtyfQp6IDwtIG1hdHJpeCgxOjE1LG5yb3c9NSkKegpgYGAKClJldHVybiB0aGUgbGVuZ3RoIG9mIHouIFNpbmNlIHogaXMgc3RpbGwgYSB2ZWN0b3IsIHdlIGNhbiBxdWVyeSBpdHMgbGVuZ3RoOgpgYGB7cn0KbGVuZ3RoKHopCmBgYAoKTGV0IHVzIHByaW50IG91dCB0aGUgY2xhc3Mgb2YgejoKYGBge3J9CmNsYXNzKHopCmBgYApXZSBnZXQgdHdvIG91dHB1dHMhIFRoaXMgaXMgYmVjYXVzZSB6IGNhbiBiZSBpbnRlcnByZXRlZCBhcyBhIG1hdHJpeCBhcyB3ZWxsIGFzIGFuIGFycmF5LgoKV2hlbiBwcmludGluZyB0aGUgZGltZW5zaW9uLCB6IGlzIGludGVycHJldGVkIGFzIHRoZSBhYm92ZSBtYXRyaXggd2l0aCA1IHJvd3MgYW5kIDMgY29sdW1ucy4KYGBge3J9CmRpbSh6KQpgYGAKCgoqKkF2b2lkaW5nIFVuaW50ZW5kZWQgRGltZW5zaW9uIFJlZHVjdGlvbioqCkluIHRoZSB3b3JsZCBvZiBzdGF0aXN0aWNzLCBkaW1lbnNpb24gcmVkdWN0aW9uIGlzIGEgZ29vZCB0aGluZywgd2l0aCBtYW55IHN0YXRpc3RpY2FsIHByb2NlZHVyZXMgYWltZWQgdG8gZG8gaXQgd2VsbC4gSWYgd2UgYXJlIHdvcmtpbmcgd2l0aCwgc2F5LCAxMCB2YXJpYWJsZXMgYW5kIGNhbiByZWR1Y2UgdGhhdCBudW1iZXIgdG8gMyB0aGF0IHN0aWxsIGNhcHR1cmUgdGhlIGVzc2VuY2Ugb2Ygb3VyCmRhdGEsIHdl4oCZcmUgaGFwcHkuIEhvd2V2ZXIsIGluIFIsIHNvbWV0aGluZyBlbHNlIG1pZ2h0IG1lcml0IHRoZSBuYW1lIGRpbWVuc2lvbiByZWR1Y3Rpb24gdGhhdCB3ZSBtYXkgc29tZXRpbWVzIHdpc2ggdG8gYXZvaWQuIFNheSB3ZSBoYXZlIGEgZml2ZS1yb3cgbWF0cml4IGFuZCBleHRyYWN0IGEgcm93IGZyb20gaXQ6CgpGaXJzdCwgbGUgdHVzIHZpc3VhbGl6ZSB0aGUgbWF0cml4IHogYWdhaW46CmBgYHtyfQp6CmBgYAoKTm93LCByZXR1cm4gdGhlIHNlY29uZCByb3cgb2YgeiBhbmQgc2F2ZSB0aGUgcmVzdWx0IGFzIGEgbmV3IHZlY3RvciBjYWxsZWQgcjoKYGBge3J9CnIgPC0gelsyLF0KcgpgYGAKCgpUaGlzIHNlZW1zIGlubm9jdW91cywgYnV0IG5vdGUgdGhlIGZvcm1hdCBpbiB3aGljaCBSIGhhcyBkaXNwbGF5ZWQgci4gSXTigJlzIGEgdmVjdG9yIGZvcm1hdCwgbm90IGEgbWF0cml4IGZvcm1hdC4gSW4gb3RoZXIgd29yZHMsIHIgaXMgYSB2ZWN0b3Igb2YgbGVuZ3RoIDMsIHJhdGhlciB0aGFuIGEgMS1ieS0zIG1hdHJpeC4gV2UgY2FuIGNvbmZpcm0gdGhpcyBpbiBhIGNvdXBsZSBvZiB3YXlzOgoKVGhlIGZ1bnRpb24gYXR0cmlidXRlcyByZXR1cm5zIHRoZSBkaW1lbnNpb24gb2YgbWF0cmljZXMuCgpXaGVuIGVudGVyaW5nIHRoZSBtYXRyaXggeiBhcyBhbiBpbnB1dCwgd2UgZ2V0IGl0cyBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1uczoKYGBge3J9CmF0dHJpYnV0ZXMoeikKYGBgCgpIb3dldmVyLCB3aGVuIHNldHRpbmcgciBhcyBhbiBhcmd1bWVudCwgdGhlcmUgc2VlbXMgdG8gYmUgbm8gZGltZW5zaW9uIGF0IGFsbC4KYGBge3J9CmF0dHJpYnV0ZXMocikKYGBgCgpJbiBhIHNpbWlsYXIgd2F5LCB3ZSBjYW4gY29uZmlybSBvdXIgYXNzdW1wdGlvbnMgYnkgdXNpbmcgdGhlIHN0cnVjdHVyZSBmdW5jdGlvbiBzdHIoKS4KCklucHV0IHo6CmBgYHtyfQpzdHIoeikKYGBgCldlIGNhbiBhY2Nlc3MgdmFsdWVzIG9mIHogd2l0aCAqKip0d28tZGltZW5zaW9uYWwqKiogaW5kZXhpbmcuCgpPbiB0aGUgb3RoZXIgaGFuZCwgbGV0IHVzIHNldCByIGFzIGFuIGlucHV0IDoKYGBge3J9CnN0cihyKQpgYGAKV2UgY2FuIGFjY2VzcyB2YWx1ZXMgb2YgciB3aXRoICoqKm9uZS1kaW1lbnNpb25hbCoqKiBpbmRleGluZy4KCgpGb3J0dW5hdGVseSwgUiBoYXMgYSB3YXkgdG8gc3VwcHJlc3MgdGhpcyBkaW1lbnNpb24gcmVkdWN0aW9uOiB0aGUgZHJvcCBhcmd1bWVudC4gSGVyZeKAmXMgYW4gZXhhbXBsZSwgdXNpbmcgdGhlIG1hdHJpeCB6IGZyb20gYWJvdmU6CgpgYGB7cn0KciA8LSB6WzIsLCBkcm9wPUZBTFNFXQpyCmBgYAoKU2V0dGluZyBkcm9wPUZBTFNFLCByIHdpbGwgYmUgaW50ZXJwcmV0ZWQgYXMgYSBtYXRyaXggYWdhaW4uIFdlIGNhbiBjb25maXJtIHRoaXMgYnkgYXBwbHlpbmcgbW9yZSBmdW5jdGlvbnMgdG8gb3VyIG5ldyByOgoKRmlyc3QsIGFwcGx5IHRoZSBkaW0oKSBmdW5jdGlvbjoKYGBge3J9CmRpbShyKQpgYGAKTm93LCByIGlzIGludGVycHJldGVkIGFzIG1hdHJpeCB3aXRoIDEgcm93IGFuZCAzIGNvbHVtbnMuCgpBcHBsYXkgdGhlIGNsYXNzKCkgZnVuY3Rpb24gbmV4dDoKYGBge3J9CmNsYXNzKHIpCmBgYApUaGlzIGlzIGV4YWN0bHkgdGhlIG91dHB1dCB0aGF0IHdlIGdvdCBhYm92ZSB3aGVuIHdlIGFwcGxpZWQgdGhlIGNsYXNzKCkgZnVuY3Rpb24gdG8gei4gSW5kZWVkLCByIGlzIG5vdyBpbnRlcnByZXRlZCBhcyBhIG1hdHJpeCBhcyB3ZWxsLgoKCgoqKk5hbWluZyBNYXRyaXggUm93cyBhbmQgQ29sdW1ucyoqCgpUaGUgbmF0dXJhbCB3YXkgdG8gcmVmZXIgdG8gcm93cyBhbmQgY29sdW1ucyBpbiBhIG1hdHJpeCBpcyB2aWEgdGhlIHJvdyBhbmQgY29sdW1uIG51bWJlcnMuIEhvd2V2ZXIsIHlvdSBjYW4gYWxzbyBnaXZlIG5hbWVzIHRvIHRoZXNlIGVudGl0aWVzLiBIZXJl4oCZcyBhbiBleGFtcGxlOgoKTGV0IHVzIGRpc3BsYXkgeiBhZ2FpbjoKYGBge3J9CnoKYGBgCgpBcHBseSB0aGUgZnVuY3Rpb24gY29sbmFtZXMoKToKYGBge3J9CmNvbG5hbWVzKHopCmBgYApXZSBvYnNlcnZlIHRoYXQgeiBoYXMgbm8gY29sdW1uIG5hbWVzLiBXZSBjYW4gYWRkIHRoZW0gYnkgYXNzaWduaW5nIGEgdmVjdG9yIHRvIHRoZSB2YXJpYWJsZSBjb2xuYW1lcyh6KToKYGBge3J9CmNvbG5hbWVzKHopIDwtIGMoIkZpcnN0IENvbHVtbiIsIlNlY29uZCBDb2x1bW4iLCAiVGhpcmQgQ29sdW1uIikKYGBgCgpMZXQgdXMgc2VlIHdodGEgaGFwcGVucyBpZiB3ZSByZXR1cm4geiBub3c6CmBgYHtyfQp6CmBgYApJbnN0ZWFkIG9mIHRoZSB0aGUgaW5kaWNlcyBbLDFdLCBbLDJdLCBhbmQgWywzXSBvdXIgYXNzaWduZWQgY29sdW1uIG5hbWVzIGFyZSBub3cgZGlzcGxheWVkIGFib3ZlIGVhY2ggY29sdW1uLgoKV2UgY2FuIGFsc28gcmV0dXJuIGp1c3QgdGhlIGNvbHVtbiBuYW1lcyB3aXRob3V0IHRoZSBzcGVjaWZpYyBjb2x1bW4gdmFsdWVzIG9mIHRoZSBtYXRyaXg6CmBgYHtyfQpjb2xuYW1lcyh6KQpgYGAKCkFsc28sIHdlIGNhbiByZXR1cm4gYSBzcGVjaWZpYyBjb2x1bW4sIG5vdCB1c2luZyB0aGUgY29sdW1uIGluZGljZXMgMSwgMiwgYW5kIDMsIGJ1dCByYXRoZXIgdGhlIGNvbHVtIG5hbWVzIGluc3RlYWQ6CmBgYHtyfQp6WywiVGhpcmQgQ29sdW1uIl0KYGBgCgoqKkhpZ2hlci1EaW1lbnNpb25hbCBBcnJheXMqKgoKSW4gYSBzdGF0aXN0aWNhbCBjb250ZXh0LCBhIHR5cGljYWwgbWF0cml4IGluIFIgaGFzIHJvd3MgY29ycmVzcG9uZGluZyB0byBvYnNlcnZhdGlvbnMsIHNheSBvbiB2YXJpb3VzIHBlb3BsZSwgYW5kIGNvbHVtbnMgY29ycmVzcG9uZGluZyB0byB2YXJpYWJsZXMsIHN1Y2ggYXMgd2VpZ2h0IGFuZCBibG9vZCBwcmVzc3VyZS4gVGhlIG1hdHJpeCBpcyB0aGVuIGEgdHdvLWRpbWVuc2lvbmFsIGRhdGEgc3RydWN0dXJlLiBCdXQgc3VwcG9zZSB3ZSBhbHNvIGhhdmUgZGF0YSB0YWtlbiBhdCBkaWZmZXJlbnQgdGltZXMsIG9uZSBkYXRhIHBvaW50IHBlciBwZXJzb24gcGVyIHZhcmlhYmxlIHBlciB0aW1lLiBUaW1lIHRoZW4gYmVjb21lcyB0aGUgdGhpcmQgZGltZW5zaW9uLCBpbiBhZGRpdGlvbiB0byByb3dzIGFuZCBjb2x1bW5zLiBJbiBSLCBzdWNoIGRhdGEgc2V0cyBhcmUgY2FsbGVkIGFycmF5cy4gQXMgYSBzaW1wbGUgZXhhbXBsZSwgY29uc2lkZXIgc3R1ZGVudHMgYW5kIHRlc3Qgc2NvcmVzLiBTYXkgZWFjaCB0ZXN0IGNvbnNpc3RzIG9mIHR3byBwYXJ0cywgc28gd2UgcmVjb3JkIHR3byBzY29yZXMgZm9yIGEgc3R1ZGVudCBmb3IgZWFjaCB0ZXN0LiBOb3cKc3VwcG9zZSB0aGF0IHdlIGhhdmUgdHdvIHRlc3RzLCBhbmQgdG8ga2VlcCB0aGUgZXhhbXBsZSBzbWFsbCwgYXNzdW1lIHdlIGhhdmUgb25seSB0aHJlZSBzdHVkZW50cy4gSGVyZeKAmXMgdGhlIGRhdGEgZm9yIHRoZSBmaXJzdCB0ZXN0OgoKYGBge3J9CmZpcnN0dGVzdCA8LSBtYXRyaXgobnJvdz0zLG5jb2w9NCkKZmlyc3R0ZXN0WzEsMV0gPC0gMTIKZmlyc3R0ZXN0WzIsMV0gPC0gMjQKZmlyc3R0ZXN0WzEsMl0gPC0gMzEKZmlyc3R0ZXN0WzIsMl0gPC0gMzkKZmlyc3R0ZXN0WzMsMV0gPC0gMjEKZmlyc3R0ZXN0WzMsMl0gPC0gMzUKZmlyc3R0ZXN0WzEsM10gPC0gNApmaXJzdHRlc3RbMiwzXSA8LSAyCmZpcnN0dGVzdFsxLDRdIDwtIDMwCmZpcnN0dGVzdFsyLDRdIDwtIDI1CmZpcnN0dGVzdFszLDNdIDwtIDUKZmlyc3R0ZXN0WzMsNF0gPC0gMjIKCmBgYAoKTGV0IHVzIHByaW50IHRoZSBuZXdseSBjcmVhdGVkIG1hdHJpeDoKYGBge3J9CmZpcnN0dGVzdApgYGAKCgpTdHVkZW50IDEgaGFkIHNjb3JlcyBvZiAxMiwgMzEsIDQsIGFuZCAzMCBvbiB0aGUgZmlyc3QgdGVzdCwgc3R1ZGVudCAyIHNjb3JlZCAyNCwgMzksIDIsIGFuZCAyNSBhbmQgc28gb24uIEhlcmUgYXJlIHRoZSBzY29yZXMgZm9yIHRoZSBzYW1lIHN0dWRlbnRzIG9uIHRoZSBzZWNvbmQgdGVzdDoKCmBgYHtyfQpzZWNvbmR0ZXN0IDwtIG1hdHJpeChucm93PTMsbmNvbD00KQpzZWNvbmR0ZXN0WzEsMV0gPC0gMTQKc2Vjb25kdGVzdFsyLDFdIDwtIDIyCnNlY29uZHRlc3RbMSwyXSA8LSAzNgpzZWNvbmR0ZXN0WzIsMl0gPC0gMzcKc2Vjb25kdGVzdFszLDFdIDwtIDE5CnNlY29uZHRlc3RbMywyXSA8LSAzNQpzZWNvbmR0ZXN0WzEsM10gPC0gNQpzZWNvbmR0ZXN0WzIsM10gPC0gNgpzZWNvbmR0ZXN0WzEsNF0gPC0gMjgKc2Vjb25kdGVzdFsyLDRdIDwtIDIyCnNlY29uZHRlc3RbMywzXSA8LSA0CnNlY29uZHRlc3RbMyw0XSA8LSAyNwpgYGAKCkxldCB1cyBwcmludCB0aGUgcmVzdWx0cyBmb3IgdGhlIHNlY29uZCB0ZXN0OgpgYGB7cn0Kc2Vjb25kdGVzdApgYGAKCk5vdyBsZXTigJlzIHB1dCBib3RoIHRlc3RzIGludG8gb25lIGRhdGEgc3RydWN0dXJlLCB3aGljaCB3ZeKAmWxsIG5hbWUgdGVzdHMuIFdl4oCZbGwgYXJyYW5nZSBpdCB0byBoYXZlIHR3byDigJxsYXllcnPigJ3igJRvbmUgbGF5ZXIgcGVyIHRlc3TigJR3aXRoIHRocmVlIHJvd3MgYW5kIGZvdXIgY29sdW1ucyB3aXRoaW4gZWFjaCBsYXllci4gV2XigJlsbCBzdG9yZSBmaXJzdHRlc3QgaW4gdGhlIGZpcnN0IGxheWVyIGFuZCBzZWNvbmR0ZXN0IGluIHRoZSBzZWNvbmQuIEluIGxheWVyIDEsIHRoZXJlIHdpbGwgYmUgdGhyZWUgcm93cyBmb3IgdGhlIHRocmVlIHN0dWRlbnRz4oCZIHNjb3JlcyBvbiB0aGUgZmlyc3QgdGVzdCwgd2l0aCBmb3VyIGNvbHVtbnMgcGVyIHJvdyBmb3IgdGhlIGZvdXIgcG9ydGlvbnMgb2YgYSB0ZXN0LiBXZSB1c2UgUuKAmXMgYXJyYXkgZnVuY3Rpb24gdG8gY3JlYXRlIHRoZSBkYXRhIHN0cnVjdHVyZToKCmBgYHtyfQp0ZXN0cyA8LSBhcnJheShkYXRhPWMoZmlyc3R0ZXN0LHNlY29uZHRlc3QpLGRpbT1jKDMsNCwyKSkKYGBgCgpJbiB0aGUgYXJndW1lbnQgZGltPWMoMyw0LDIpLCB3ZSBhcmUgc3BlY2lmeWluZyB0d28gbGF5ZXJzICh0aGlzIGlzIHRoZSAyIGluIHRoZSB0aGlyZCBhcmd1bWVudCksIGVhY2ggY29uc2lzdGluZyBvZiB0aHJlZSByb3dzIGFuZCBmb3VyIGNvbHVtbnMuIFRoaXMgdGhlbiBiZWNvbWVzIGFuIGF0dHJpYnV0ZSBvZiB0aGUgZGF0YSBzdHJ1Y3R1cmU6CgpgYGB7cn0KYXR0cmlidXRlcyh0ZXN0cykKYGBgCgpFYWNoIGVsZW1lbnQgb2YgdGVzdHMgbm93IGhhcyB0aHJlZSBzdWJzY3JpcHRzLCByYXRoZXIgdGhhbiB0d28gYXMgaW4gdGhlIG1hdHJpeCBjYXNlLiBUaGUgZmlyc3Qgc3Vic2NyaXB0IGNvcnJlc3BvbmRzIHRvIHRoZSBmaXJzdCBlbGVtZW50IGluIHRoZSAkZGltIHZlY3RvciwgdGhlIHNlY29uZCBzdWJzY3JpcHQgY29ycmVzcG9uZHMgdG8gdGhlIHNlY29uZCBlbGVtZW50IGluIHRoZSB2ZWN0b3IsIGFuZCBzbyBvbi4gRm9yIGluc3RhbmNlLCB0aGUgc2NvcmUgb24gdGhlIHNlY29uZCBwb3J0aW9uIG9mIHRlc3QgMSBmb3Igc3R1ZGVudCAzIGlzIHJldHJpZXZlZCBhcyBmb2xsb3dzOgoKYGBge3J9CnRlc3RzWzMsMiwxXQpgYGAKCklmIHdlIG5vdyBwcmludCB0aGUgbmV3IGFycmF5IHRlc3RzLCB3ZSBnZXQgZmlyc3R0ZXN0IGluIHRoZSBmaXJzdCBsYXllciBbLCwxXSBhbmQgc2Vjb25kdGVzdCBpbiB0aGUgc2Vjb25kIGxheWVyIFssLDJdOgpgYGB7cn0KdGVzdHMKYGBgCgpKdXN0IGFzIHdlIGJ1aWx0IG91ciB0aHJlZS1kaW1lbnNpb25hbCBhcnJheSBieSBjb21iaW5pbmcgdHdvIG1hdHJpY2VzLCB3ZSBjYW4gYnVpbGQgZm91ci1kaW1lbnNpb25hbCBhcnJheXMgYnkgY29tYmluaW5nIHR3byBvciBtb3JlIHRocmVlIGRpbWVuc2lvbmFsIGFycmF5cywgYW5kIHNvIG9uLg==