This notebook contains the code samples found in Chapter 2, Section 1 of Deep Learning with R. Note that the original text features far more content, in particular further explanations and figures: in this notebook, you will only find source code and related comments.


Let’s look at a concrete example of a neural network that uses the Keras R package to learn to classify hand-written digits. Unless you already have experience with Keras or similar libraries, you will not understand everything about this first example right away. You probably haven’t even installed Keras yet. Don’t worry, that is perfectly fine. In the next chapter, we will review each element in our example and explain them in detail. So don’t worry if some steps seem arbitrary or look like magic to you! We’ve got to start somewhere.

The problem we’re trying to solve here is to classify grayscale images of handwritten digits (28 pixels by 28 pixels) into their 10 categories (0 to 9). We’ll use the MNIST dataset, a classic dataset in the machine-learning community, which has been around almost as long as the field itself and has been intensively studied. It’s a set of 60,000 training images, plus 10,000 test images, assembled by the National Institute of Standards and Technology (the NIST in MNIST) in the 1980s. You can think of “solving” MNIST as the “Hello World” of deep learning—it’s what you do to verify that your algorithms are working as expected. As you become a machine-learning practitioner, you’ll see MNIST come up over and over again, in scientific papers, blog posts, and so on.

The MNIST dataset comes preloaded in Keras, in the form of train and test lists, each of which includes a set of images (x) and associated labels (y):

library(keras)
mnist <- dataset_mnist()
train_images <- mnist$train$x
train_labels <- mnist$train$y
test_images <- mnist$test$x
test_labels <- mnist$test$y

train_images and train_labels form the training set, the data that the model will learn from. The model will then be tested on the test set, test_images and test_labels. The images are encoded as as 3D arrays, and the labels are a 1D array of digits, ranging from 0 to 9. There is a one-to-one correspondence between the images and the labels.

head(train_images[1,,])
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
[1,]    0    0    0    0    0    0    0    0    0     0     0     0     0     0     0     0
[2,]    0    0    0    0    0    0    0    0    0     0     0     0     0     0     0     0
[3,]    0    0    0    0    0    0    0    0    0     0     0     0     0     0     0     0
[4,]    0    0    0    0    0    0    0    0    0     0     0     0     0     0     0     0
[5,]    0    0    0    0    0    0    0    0    0     0     0     0     0     0     0     0
[6,]    0    0    0    0    0    0    0    0    0     0     0     0     3    18    18    18
     [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26] [,27] [,28]
[1,]     0     0     0     0     0     0     0     0     0     0     0     0
[2,]     0     0     0     0     0     0     0     0     0     0     0     0
[3,]     0     0     0     0     0     0     0     0     0     0     0     0
[4,]     0     0     0     0     0     0     0     0     0     0     0     0
[5,]     0     0     0     0     0     0     0     0     0     0     0     0
[6,]   126   136   175    26   166   255   247   127     0     0     0     0
tail(train_images[1,,])
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16]
[23,]    0    0    0    0    0    0   18  171  219   253   253   253   253   195    80     9
[24,]    0    0    0    0   55  172  226  253  253   253   253   244   133    11     0     0
[25,]    0    0    0    0  136  253  253  253  212   135   132    16     0     0     0     0
[26,]    0    0    0    0    0    0    0    0    0     0     0     0     0     0     0     0
[27,]    0    0    0    0    0    0    0    0    0     0     0     0     0     0     0     0
[28,]    0    0    0    0    0    0    0    0    0     0     0     0     0     0     0     0
      [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26] [,27] [,28]
[23,]     0     0     0     0     0     0     0     0     0     0     0     0
[24,]     0     0     0     0     0     0     0     0     0     0     0     0
[25,]     0     0     0     0     0     0     0     0     0     0     0     0
[26,]     0     0     0     0     0     0     0     0     0     0     0     0
[27,]     0     0     0     0     0     0     0     0     0     0     0     0
[28,]     0     0     0     0     0     0     0     0     0     0     0     0
train_labels[1]
[1] 5
# visualize the digits
par(mfcol=c(6,6))
par(mar=c(0, 0, 3, 0), xaxs='i', yaxs='i')
for (idx in 1:36) { 
    im <- train_images[idx,,]
    im <- t(apply(im, 2, rev)) 
    image(1:28, 1:28, im, col=gray((0:255)/255), 
          xaxt='n', main=paste(train_labels[idx]))
}

The R str() function is a convenient way to get a quick glimpse at the structure of an array. Let’s use it to have a look at the training data:

str(train_images)
 int [1:60000, 1:28, 1:28] 0 0 0 0 0 0 0 0 0 0 ...
str(train_labels)
 int [1:60000(1d)] 5 0 4 1 9 2 1 3 1 4 ...

Let’s have a look at the test data:

# visualize the digits
par(mfcol=c(6,6))
par(mar=c(0, 0, 3, 0), xaxs='i', yaxs='i')
for (idx in 1:36) { 
    im <- test_images[idx,,]
    im <- t(apply(im, 2, rev)) 
    image(1:28, 1:28, im, col=gray((0:255)/255), 
          xaxt='n', main=paste(test_labels[idx]))
}

str(test_images)
 int [1:10000, 1:28, 1:28] 0 0 0 0 0 0 0 0 0 0 ...
str(test_labels)
 int [1:10000(1d)] 7 2 1 0 4 1 4 9 5 9 ...

The workflow will be as follows: first we’ll feed the neural network the training data, train_images and train_labels. The network will then learn to associate images and labels. Finally, we’ll ask the network to produce predictions for test_images, and we’ll verify whether these predictions match the labels from test_labels.

Let’s build the network – again, remember that you aren’t supposed to understand everything about this example yet.

network <- keras_model_sequential() %>% 
  layer_dense(units = 512, activation = "relu", input_shape = c(28 * 28)) %>%
  layer_dense(units = 10, activation = "softmax")

The core building block of neural networks is the layer, a data-processing module that you can think of as a filter for data. Some data comes in, and it comes out in a more useful form. Specifically, layers extract representations out of the data fed into them—hopefully representations that are more meaningful for the problem at hand. Most of deep learning consists of chaining together simple layers that will implement a form of progressive data distillation. A deep-learning model is like a sieve for data processing, made of a succession of increasingly refined data filters—the layers.

Here our network consists of a sequence of two layers, which are densely connected (also called fully connected) neural layers. The second (and last) layer is a 10-way softmax layer, which means it will return an array of 10 probability scores (summing to 1). Each score will be the probability that the current digit image belongs to one of our 10 digit classes.

To make the network ready for training, we need to pick three more things, as part of the compilation step:

The exact purpose of the loss function and the optimizer will be made clear throughout the next two chapters.

network %>% compile(
  optimizer = "rmsprop",
  loss = "categorical_crossentropy",
  metrics = c("accuracy")
)

Before training, we’ll preprocess the data by reshaping it into the shape the network expects and scaling it so that all values are in the [0, 1] interval. Previously, our training images, for instance, were stored in an array of shape (60000, 28, 28) of type integer with values in the [0, 255] interval. We transform it into a double array of shape (60000, 28 * 28) with values between 0 and 1.

train_images <- array_reshape(train_images, c(60000, 28 * 28))
train_images <- train_images / 255
test_images <- array_reshape(test_images, c(10000, 28 * 28))
test_images <- test_images / 255

We also need to categorically encode the labels, a step which we explain in chapter 3:

train_labels <- to_categorical(train_labels)
test_labels <- to_categorical(test_labels)
dim(train_labels)
[1] 60000    10
dim(test_labels)
[1] 10000    10

We are now ready to train our network, which in Keras is done via a call to the fit method of the network: we “fit” the model to its training data.

network %>% fit(train_images, train_labels, epochs = 5, batch_size = 128)

Two quantities are being displayed during training: the “loss” of the network over the training data, and the accuracy of the network over the training data.

We quickly reach an accuracy of 0.989 (i.e. 98.9%) on the training data. Now let’s check that our model performs well on the test set too:

metrics <- network %>% evaluate(test_images, test_labels, verbose = 0)
metrics
$loss
[1] 0.06909309015

$acc
[1] 0.9789

Our test set accuracy turns out to be 98.1% – that’s quite a bit lower than the training set accuracy. This gap between training accuracy and test accuracy is an example of “overfitting”, the fact that machine learning models tend to perform worse on new data than on their training data. Overfitting will be a central topic in chapter 3.

network %>% predict(test_images[1:2,]) 
                      [,1]                  [,2]               [,3]                [,4]
[1,] 0.0000000113172147209 0.0000000001540189504 0.0000006918451163 0.00001876589158201
[2,] 0.0000000001648566283 0.0000011847574796775 0.9999988079071045 0.00000001398635785
                                [,5]                 [,6]                     [,7]
[1,] 0.00000000000331923486092400299 0.000000020260108258 0.0000000000005563463644
[2,] 0.00000000000000000001033303278 0.000000005367103206 0.0000000001793472315104
                             [,8]                 [,9]                        [,10]
[1,] 0.99997997283935546875000000 0.000000042501305586 0.00000057479508086544228718
[2,] 0.00000000000000000400510761 0.000000001388301474 0.00000000000000001659488587

Make the Confussion Matrix.

test_labels <- mnist$test$y  # We need to recreate test_labels because of the to_categorical()
test_labels[1:10]

test_images_prediction <- network %>% predict_classes(test_images)
test_images_prediction[1:10]

library(gmodels)

CrossTable(test_images_prediction, test_labels, prop.r = TRUE, prop.c = FALSE, prop.t = FALSE, prop.chisq = FALSE, format = "SAS")

This concludes our first example – you just saw how you can build and a train a neural network to classify handwritten digits in less than 20 lines of R code. In the next chapter, we’ll go into detail about every moving piece we just previewed and clarify what’s going on behind the scenes. You’ll learn about tensors, the data-storing objects going into the network; about tensor operations, which layers are made of; and about gradient descent, which allows your network to learn from its training examples.

LS0tCnRpdGxlOiAiQSBmaXJzdCBsb29rIGF0IGEgbmV1cmFsIG5ldHdvcmsiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0aGVtZTogY2VydWxlYW4KICAgIGhpZ2hsaWdodDogdGV4dG1hdGUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQpgYGAKCioqKgoKVGhpcyBub3RlYm9vayBjb250YWlucyB0aGUgY29kZSBzYW1wbGVzIGZvdW5kIGluIENoYXB0ZXIgMiwgU2VjdGlvbiAxIG9mIFtEZWVwIExlYXJuaW5nIHdpdGggUl0oaHR0cHM6Ly93d3cubWFubmluZy5jb20vYm9va3MvZGVlcC1sZWFybmluZy13aXRoLXIpLiBOb3RlIHRoYXQgdGhlIG9yaWdpbmFsIHRleHQgZmVhdHVyZXMgZmFyIG1vcmUgY29udGVudCwgaW4gcGFydGljdWxhciBmdXJ0aGVyIGV4cGxhbmF0aW9ucyBhbmQgZmlndXJlczogaW4gdGhpcyBub3RlYm9vaywgeW91IHdpbGwgb25seSBmaW5kIHNvdXJjZSBjb2RlIGFuZCByZWxhdGVkIGNvbW1lbnRzLgoKKioqCgpMZXQncyBsb29rIGF0IGEgY29uY3JldGUgZXhhbXBsZSBvZiBhIG5ldXJhbCBuZXR3b3JrIHRoYXQgdXNlcyB0aGUgS2VyYXMgUiBwYWNrYWdlIHRvIGxlYXJuIHRvIGNsYXNzaWZ5IGhhbmQtd3JpdHRlbiBkaWdpdHMuIFVubGVzcyB5b3UgYWxyZWFkeSBoYXZlIGV4cGVyaWVuY2Ugd2l0aCBLZXJhcyBvciBzaW1pbGFyIGxpYnJhcmllcywgeW91IHdpbGwgbm90IHVuZGVyc3RhbmQgZXZlcnl0aGluZyBhYm91dCB0aGlzIGZpcnN0IGV4YW1wbGUgcmlnaHQgYXdheS4gWW91IHByb2JhYmx5IGhhdmVuJ3QgZXZlbiBpbnN0YWxsZWQgS2VyYXMgeWV0LiBEb24ndCB3b3JyeSwgdGhhdCBpcyBwZXJmZWN0bHkgZmluZS4gSW4gdGhlIG5leHQgY2hhcHRlciwgd2Ugd2lsbCByZXZpZXcgZWFjaCBlbGVtZW50IGluIG91ciBleGFtcGxlIGFuZCBleHBsYWluIHRoZW0gaW4gZGV0YWlsLiBTbyBkb24ndCB3b3JyeSBpZiBzb21lIHN0ZXBzIHNlZW0gYXJiaXRyYXJ5IG9yIGxvb2sgbGlrZSBtYWdpYyB0byB5b3UhIFdlJ3ZlIGdvdCB0byBzdGFydCBzb21ld2hlcmUuCgpUaGUgcHJvYmxlbSB3ZSdyZSB0cnlpbmcgdG8gc29sdmUgaGVyZSBpcyB0byBjbGFzc2lmeSBncmF5c2NhbGUgaW1hZ2VzIG9mIGhhbmR3cml0dGVuIGRpZ2l0cyAoMjggcGl4ZWxzIGJ5IDI4IHBpeGVscykgaW50byB0aGVpciAxMCBjYXRlZ29yaWVzICgwIHRvIDkpLiBXZSdsbCB1c2UgdGhlIE1OSVNUIGRhdGFzZXQsIGEgY2xhc3NpYyBkYXRhc2V0IGluIHRoZSBtYWNoaW5lLWxlYXJuaW5nIGNvbW11bml0eSwgd2hpY2ggaGFzIGJlZW4gYXJvdW5kIGFsbW9zdCBhcyBsb25nIGFzIHRoZSBmaWVsZCBpdHNlbGYgYW5kIGhhcyBiZWVuIGludGVuc2l2ZWx5IHN0dWRpZWQuIEl0J3MgYSBzZXQgb2YgNjAsMDAwIHRyYWluaW5nIGltYWdlcywgcGx1cyAxMCwwMDAgdGVzdCBpbWFnZXMsIGFzc2VtYmxlZCBieSB0aGUgTmF0aW9uYWwgSW5zdGl0dXRlIG9mIFN0YW5kYXJkcyBhbmQgVGVjaG5vbG9neSAodGhlIE5JU1QgaW4gTU5JU1QpIGluIHRoZSAxOTgwcy4gWW91IGNhbiB0aGluayBvZiAic29sdmluZyIgTU5JU1QgYXMgdGhlICJIZWxsbyBXb3JsZCIgb2YgZGVlcCBsZWFybmluZ+KAlGl0J3Mgd2hhdCB5b3UgZG8gdG8gdmVyaWZ5IHRoYXQgeW91ciBhbGdvcml0aG1zIGFyZSB3b3JraW5nIGFzIGV4cGVjdGVkLiBBcyB5b3UgYmVjb21lIGEgbWFjaGluZS1sZWFybmluZyBwcmFjdGl0aW9uZXIsIHlvdSdsbCBzZWUgTU5JU1QgY29tZSB1cCBvdmVyIGFuZCBvdmVyIGFnYWluLCBpbiBzY2llbnRpZmljIHBhcGVycywgYmxvZyBwb3N0cywgYW5kIHNvIG9uLiAKClRoZSBNTklTVCBkYXRhc2V0IGNvbWVzIHByZWxvYWRlZCBpbiBLZXJhcywgaW4gdGhlIGZvcm0gb2YgYHRyYWluYCBhbmQgYHRlc3RgIGxpc3RzLCBlYWNoIG9mIHdoaWNoIGluY2x1ZGVzIGEgc2V0IG9mIGltYWdlcyAoYHhgKSBhbmQgYXNzb2NpYXRlZCBsYWJlbHMgKGB5YCk6CgpgYGB7ciwgcmVzdWx0cz0naGlkZSd9CmxpYnJhcnkoa2VyYXMpCgptbmlzdCA8LSBkYXRhc2V0X21uaXN0KCkKdHJhaW5faW1hZ2VzIDwtIG1uaXN0JHRyYWluJHgKdHJhaW5fbGFiZWxzIDwtIG1uaXN0JHRyYWluJHkKdGVzdF9pbWFnZXMgPC0gbW5pc3QkdGVzdCR4CnRlc3RfbGFiZWxzIDwtIG1uaXN0JHRlc3QkeQpgYGAKCmB0cmFpbl9pbWFnZXNgIGFuZCBgdHJhaW5fbGFiZWxzYCBmb3JtIHRoZSBfdHJhaW5pbmcgc2V0XywgdGhlIGRhdGEgdGhhdCB0aGUgbW9kZWwgd2lsbCBsZWFybiBmcm9tLiBUaGUgbW9kZWwgd2lsbCB0aGVuIGJlIHRlc3RlZCBvbiB0aGUgIF90ZXN0IHNldF8sIGB0ZXN0X2ltYWdlc2AgYW5kIGB0ZXN0X2xhYmVsc2AuIFRoZSBpbWFnZXMgYXJlIGVuY29kZWQgYXMgYXMgM0QgYXJyYXlzLCBhbmQgdGhlIGxhYmVscyBhcmUgYSAxRCBhcnJheSBvZiBkaWdpdHMsIHJhbmdpbmcgZnJvbSAwIHRvIDkuIFRoZXJlIGlzIGEgb25lLXRvLW9uZSBjb3JyZXNwb25kZW5jZSBiZXR3ZWVuIHRoZSBpbWFnZXMgYW5kIHRoZSBsYWJlbHMuCgpgYGB7cn0KaGVhZCh0cmFpbl9pbWFnZXNbMSwsXSkKdGFpbCh0cmFpbl9pbWFnZXNbMSwsXSkKCnRyYWluX2xhYmVsc1sxXQpgYGAKCgpgYGB7cn0KIyB2aXN1YWxpemUgdGhlIGRpZ2l0cwpwYXIobWZjb2w9Yyg2LDYpKQpwYXIobWFyPWMoMCwgMCwgMywgMCksIHhheHM9J2knLCB5YXhzPSdpJykKZm9yIChpZHggaW4gMTozNikgeyAKICAgIGltIDwtIHRyYWluX2ltYWdlc1tpZHgsLF0KICAgIGltIDwtIHQoYXBwbHkoaW0sIDIsIHJldikpIAogICAgaW1hZ2UoMToyOCwgMToyOCwgaW0sIGNvbD1ncmF5KCgwOjI1NSkvMjU1KSwgCiAgICAgICAgICB4YXh0PSduJywgbWFpbj1wYXN0ZSh0cmFpbl9sYWJlbHNbaWR4XSkpCn0KYGBgCgoKVGhlIFIgYHN0cigpYCBmdW5jdGlvbiBpcyBhIGNvbnZlbmllbnQgd2F5IHRvIGdldCBhIHF1aWNrIGdsaW1wc2UgYXQgdGhlIHN0cnVjdHVyZSBvZiBhbiBhcnJheS4gTGV0J3MgdXNlIGl0IHRvIGhhdmUgYSBsb29rIGF0IHRoZSB0cmFpbmluZyBkYXRhOgoKYGBge3J9CnN0cih0cmFpbl9pbWFnZXMpCmBgYAoKYGBge3J9CnN0cih0cmFpbl9sYWJlbHMpCmBgYAoKTGV0J3MgaGF2ZSBhIGxvb2sgYXQgdGhlIHRlc3QgZGF0YToKCmBgYHtyfQojIHZpc3VhbGl6ZSB0aGUgZGlnaXRzCnBhcihtZmNvbD1jKDYsNikpCnBhcihtYXI9YygwLCAwLCAzLCAwKSwgeGF4cz0naScsIHlheHM9J2knKQpmb3IgKGlkeCBpbiAxOjM2KSB7IAogICAgaW0gPC0gdGVzdF9pbWFnZXNbaWR4LCxdCiAgICBpbSA8LSB0KGFwcGx5KGltLCAyLCByZXYpKSAKICAgIGltYWdlKDE6MjgsIDE6MjgsIGltLCBjb2w9Z3JheSgoMDoyNTUpLzI1NSksIAogICAgICAgICAgeGF4dD0nbicsIG1haW49cGFzdGUodGVzdF9sYWJlbHNbaWR4XSkpCn0KYGBgCgpgYGB7cn0Kc3RyKHRlc3RfaW1hZ2VzKQpgYGAKCmBgYHtyfQpzdHIodGVzdF9sYWJlbHMpCmBgYAoKVGhlIHdvcmtmbG93IHdpbGwgYmUgYXMgZm9sbG93czogZmlyc3Qgd2UnbGwgZmVlZCB0aGUgbmV1cmFsIG5ldHdvcmsgdGhlIHRyYWluaW5nIGRhdGEsIGB0cmFpbl9pbWFnZXNgIGFuZCBgdHJhaW5fbGFiZWxzYC4gVGhlIG5ldHdvcmsgd2lsbCB0aGVuIGxlYXJuIHRvIGFzc29jaWF0ZSBpbWFnZXMgYW5kIGxhYmVscy4gRmluYWxseSwgd2UnbGwgYXNrIHRoZSBuZXR3b3JrIHRvIHByb2R1Y2UgcHJlZGljdGlvbnMgZm9yIGB0ZXN0X2ltYWdlc2AsIGFuZCB3ZSdsbCB2ZXJpZnkgd2hldGhlciB0aGVzZSBwcmVkaWN0aW9ucyBtYXRjaCB0aGUgbGFiZWxzIGZyb20gYHRlc3RfbGFiZWxzYC4KCkxldCdzIGJ1aWxkIHRoZSBuZXR3b3JrIC0tIGFnYWluLCByZW1lbWJlciB0aGF0IHlvdSBhcmVuJ3Qgc3VwcG9zZWQgdG8gdW5kZXJzdGFuZCBldmVyeXRoaW5nIGFib3V0IHRoaXMgZXhhbXBsZSB5ZXQuCgpgYGB7cn0KbmV0d29yayA8LSBrZXJhc19tb2RlbF9zZXF1ZW50aWFsKCkgJT4lIAogIGxheWVyX2RlbnNlKHVuaXRzID0gNTEyLCBhY3RpdmF0aW9uID0gInJlbHUiLCBpbnB1dF9zaGFwZSA9IGMoMjggKiAyOCkpICU+JQogIGxheWVyX2RlbnNlKHVuaXRzID0gMTAsIGFjdGl2YXRpb24gPSAic29mdG1heCIpCmBgYAoKVGhlIGNvcmUgYnVpbGRpbmcgYmxvY2sgb2YgbmV1cmFsIG5ldHdvcmtzIGlzIHRoZSBfbGF5ZXJfLCBhIGRhdGEtcHJvY2Vzc2luZyBtb2R1bGUgdGhhdCB5b3UgY2FuIHRoaW5rIG9mIGFzIGEgZmlsdGVyIGZvciBkYXRhLiBTb21lIGRhdGEgY29tZXMgaW4sIGFuZCBpdCBjb21lcyBvdXQgaW4gYSBtb3JlIHVzZWZ1bCBmb3JtLiBTcGVjaWZpY2FsbHksIGxheWVycyBleHRyYWN0IF9yZXByZXNlbnRhdGlvbnNfIG91dCBvZiB0aGUgZGF0YSBmZWQgaW50byB0aGVt4oCUaG9wZWZ1bGx5IHJlcHJlc2VudGF0aW9ucyB0aGF0IGFyZSBtb3JlIG1lYW5pbmdmdWwgZm9yIHRoZSBwcm9ibGVtIGF0IGhhbmQuIE1vc3Qgb2YgZGVlcCBsZWFybmluZyBjb25zaXN0cyBvZiBjaGFpbmluZyB0b2dldGhlciBzaW1wbGUgbGF5ZXJzIHRoYXQgd2lsbCBpbXBsZW1lbnQgYSBmb3JtIG9mIHByb2dyZXNzaXZlIF9kYXRhIGRpc3RpbGxhdGlvbl8uIEEgZGVlcC1sZWFybmluZyBtb2RlbCBpcyBsaWtlIGEgc2lldmUgZm9yIGRhdGEgcHJvY2Vzc2luZywgbWFkZSBvZiBhIHN1Y2Nlc3Npb24gb2YgaW5jcmVhc2luZ2x5IHJlZmluZWQgZGF0YSBmaWx0ZXJz4oCUdGhlIGxheWVycy4KCkhlcmUgb3VyIG5ldHdvcmsgY29uc2lzdHMgb2YgYSBzZXF1ZW5jZSBvZiB0d28gbGF5ZXJzLCB3aGljaCBhcmUgZGVuc2VseSBjb25uZWN0ZWQgKGFsc28gY2FsbGVkIF9mdWxseSBjb25uZWN0ZWRfKSBuZXVyYWwgbGF5ZXJzLiBUaGUgc2Vjb25kIChhbmQgbGFzdCkgbGF5ZXIgaXMgYSAxMC13YXkgX3NvZnRtYXhfIGxheWVyLCB3aGljaCBtZWFucyBpdCB3aWxsIHJldHVybiBhbiBhcnJheSBvZiAxMCBwcm9iYWJpbGl0eSBzY29yZXMgKHN1bW1pbmcgdG8gMSkuIEVhY2ggc2NvcmUgd2lsbCBiZSB0aGUgcHJvYmFiaWxpdHkgdGhhdCB0aGUgY3VycmVudCBkaWdpdCBpbWFnZSBiZWxvbmdzIHRvIG9uZSBvZiBvdXIgMTAgZGlnaXQgY2xhc3Nlcy4KClRvIG1ha2UgdGhlIG5ldHdvcmsgcmVhZHkgZm9yIHRyYWluaW5nLCB3ZSBuZWVkIHRvIHBpY2sgdGhyZWUgbW9yZSB0aGluZ3MsIGFzIHBhcnQgb2YgdGhlIF9jb21waWxhdGlvbl8gc3RlcDoKCiogX0EgbG9zcyBmdW5jdGlvbl/igJRIb3cgdGhlIG5ldHdvcmsgd2lsbCBiZSBhYmxlIHRvIG1lYXN1cmUgaG93IGdvb2QgYSBqb2IgaXQncyBkb2luZyBvbiBpdHMgdHJhaW5pbmcgZGF0YSwgYW5kIHRodXMgaG93IGl0IHdpbGwgYmUgYWJsZSB0byBzdGVlciBpdHNlbGYgaW4gdGhlIHJpZ2h0IGRpcmVjdGlvbi4KKiBfQW4gb3B0aW1pemVyX+KAlFRoZSBtZWNoYW5pc20gdGhyb3VnaCB3aGljaCB0aGUgbmV0d29yayB3aWxsIHVwZGF0ZSBpdHNlbGYgYmFzZWQgb24gdGhlIGRhdGEgaXQgc2VlcyBhbmQgaXRzIGxvc3MgZnVuY3Rpb24uCiogX01ldHJpY3MgdG8gbW9uaXRvciBkdXJpbmcgdHJhaW5pbmcgYW5kIHRlc3Rpbmdf4oCUSGVyZSB3ZSdsbCBvbmx5IGNhcmUgYWJvdXQgYWNjdXJhY3kgKHRoZSBmcmFjdGlvbiBvZiB0aGUgaW1hZ2VzIHRoYXQgd2VyZSBjb3JyZWN0bHkgY2xhc3NpZmllZCkuCgpUaGUgZXhhY3QgcHVycG9zZSBvZiB0aGUgbG9zcyBmdW5jdGlvbiBhbmQgdGhlIG9wdGltaXplciB3aWxsIGJlIG1hZGUgY2xlYXIgdGhyb3VnaG91dCB0aGUgbmV4dCB0d28gY2hhcHRlcnMuCgpgYGB7cn0KbmV0d29yayAlPiUgY29tcGlsZSgKICBvcHRpbWl6ZXIgPSAicm1zcHJvcCIsCiAgbG9zcyA9ICJjYXRlZ29yaWNhbF9jcm9zc2VudHJvcHkiLAogIG1ldHJpY3MgPSBjKCJhY2N1cmFjeSIpCikKYGBgCgpCZWZvcmUgdHJhaW5pbmcsIHdlJ2xsIHByZXByb2Nlc3MgdGhlIGRhdGEgYnkgcmVzaGFwaW5nIGl0IGludG8gdGhlIHNoYXBlIHRoZSBuZXR3b3JrIGV4cGVjdHMgYW5kIHNjYWxpbmcgaXQgc28gdGhhdCBhbGwgdmFsdWVzIGFyZSBpbiB0aGUgYFswLCAxXWAgaW50ZXJ2YWwuIFByZXZpb3VzbHksIG91ciB0cmFpbmluZyBpbWFnZXMsIGZvciBpbnN0YW5jZSwgd2VyZSBzdG9yZWQgaW4gYW4gYXJyYXkgb2Ygc2hhcGUgYCg2MDAwMCwgMjgsIDI4KWAgb2YgdHlwZSBpbnRlZ2VyIHdpdGggdmFsdWVzIGluIHRoZSBgWzAsIDI1NV1gIGludGVydmFsLiBXZSB0cmFuc2Zvcm0gaXQgaW50byBhIGRvdWJsZSBhcnJheSBvZiBzaGFwZSBgKDYwMDAwLCAyOCAqIDI4KWAgd2l0aCB2YWx1ZXMgYmV0d2VlbiAwIGFuZCAxLgoKYGBge3J9CnRyYWluX2ltYWdlcyA8LSBhcnJheV9yZXNoYXBlKHRyYWluX2ltYWdlcywgYyg2MDAwMCwgMjggKiAyOCkpCnRyYWluX2ltYWdlcyA8LSB0cmFpbl9pbWFnZXMgLyAyNTUKCnRlc3RfaW1hZ2VzIDwtIGFycmF5X3Jlc2hhcGUodGVzdF9pbWFnZXMsIGMoMTAwMDAsIDI4ICogMjgpKQp0ZXN0X2ltYWdlcyA8LSB0ZXN0X2ltYWdlcyAvIDI1NQpgYGAKCldlIGFsc28gbmVlZCB0byBjYXRlZ29yaWNhbGx5IGVuY29kZSB0aGUgbGFiZWxzLCBhIHN0ZXAgd2hpY2ggd2UgZXhwbGFpbiBpbiBjaGFwdGVyIDM6CgpgYGB7cn0KdHJhaW5fbGFiZWxzIDwtIHRvX2NhdGVnb3JpY2FsKHRyYWluX2xhYmVscykKdGVzdF9sYWJlbHMgPC0gdG9fY2F0ZWdvcmljYWwodGVzdF9sYWJlbHMpCgpkaW0odHJhaW5fbGFiZWxzKQpkaW0odGVzdF9sYWJlbHMpCmBgYAoKV2UgYXJlIG5vdyByZWFkeSB0byB0cmFpbiBvdXIgbmV0d29yaywgd2hpY2ggaW4gS2VyYXMgaXMgZG9uZSB2aWEgYSBjYWxsIHRvIHRoZSBgZml0YCBtZXRob2Qgb2YgdGhlIG5ldHdvcms6IHdlICJmaXQiIHRoZSBtb2RlbCB0byBpdHMgdHJhaW5pbmcgZGF0YS4KCmBgYHtyLCBlY2hvPVRSVUUsIHJlc3VsdHM9J2hpZGUnfQpuZXR3b3JrICU+JSBmaXQodHJhaW5faW1hZ2VzLCB0cmFpbl9sYWJlbHMsIGVwb2NocyA9IDUsIGJhdGNoX3NpemUgPSAxMjgpCmBgYAoKVHdvIHF1YW50aXRpZXMgYXJlIGJlaW5nIGRpc3BsYXllZCBkdXJpbmcgdHJhaW5pbmc6IHRoZSAibG9zcyIgb2YgdGhlIG5ldHdvcmsgb3ZlciB0aGUgdHJhaW5pbmcgZGF0YSwgYW5kIHRoZSBhY2N1cmFjeSBvZiB0aGUgbmV0d29yayBvdmVyIHRoZSB0cmFpbmluZyBkYXRhLgoKV2UgcXVpY2tseSByZWFjaCBhbiBhY2N1cmFjeSBvZiAwLjk4OSAoaS5lLiA5OC45JSkgb24gdGhlIHRyYWluaW5nIGRhdGEuIE5vdyBsZXQncyBjaGVjayB0aGF0IG91ciBtb2RlbCBwZXJmb3JtcyB3ZWxsIG9uIHRoZSB0ZXN0IHNldCB0b286CgpgYGB7cn0KbWV0cmljcyA8LSBuZXR3b3JrICU+JSBldmFsdWF0ZSh0ZXN0X2ltYWdlcywgdGVzdF9sYWJlbHMsIHZlcmJvc2UgPSAwKQptZXRyaWNzCmBgYAoKT3VyIHRlc3Qgc2V0IGFjY3VyYWN5IHR1cm5zIG91dCB0byBiZSA5OC4xJSAtLSB0aGF0J3MgcXVpdGUgYSBiaXQgbG93ZXIgdGhhbiB0aGUgdHJhaW5pbmcgc2V0IGFjY3VyYWN5LiBUaGlzIGdhcCBiZXR3ZWVuIHRyYWluaW5nIGFjY3VyYWN5IGFuZCB0ZXN0IGFjY3VyYWN5IGlzIGFuIGV4YW1wbGUgb2YgIm92ZXJmaXR0aW5nIiwgdGhlIGZhY3QgdGhhdCBtYWNoaW5lIGxlYXJuaW5nIG1vZGVscyB0ZW5kIHRvIHBlcmZvcm0gd29yc2Ugb24gbmV3IGRhdGEgdGhhbiBvbiB0aGVpciB0cmFpbmluZyBkYXRhLiBPdmVyZml0dGluZyB3aWxsIGJlIGEgY2VudHJhbCB0b3BpYyBpbiBjaGFwdGVyIDMuCgpgYGB7cn0KbmV0d29yayAlPiUgcHJlZGljdCh0ZXN0X2ltYWdlc1sxOjIsXSkgCgpgYGAKCk1ha2UgdGhlIENvbmZ1c3Npb24gTWF0cml4LgoKYGBge3J9CnRlc3RfbGFiZWxzIDwtIG1uaXN0JHRlc3QkeSAgIyBXZSBuZWVkIHRvIHJlY3JlYXRlIHRlc3RfbGFiZWxzIGJlY2F1c2Ugb2YgdGhlIHRvX2NhdGVnb3JpY2FsKCkKdGVzdF9sYWJlbHNbMToxMF0KCnRlc3RfaW1hZ2VzX3ByZWRpY3Rpb24gPC0gbmV0d29yayAlPiUgcHJlZGljdF9jbGFzc2VzKHRlc3RfaW1hZ2VzKQp0ZXN0X2ltYWdlc19wcmVkaWN0aW9uWzE6MTBdCgpsaWJyYXJ5KGdtb2RlbHMpCgpDcm9zc1RhYmxlKHRlc3RfaW1hZ2VzX3ByZWRpY3Rpb24sIHRlc3RfbGFiZWxzLCBwcm9wLnIgPSBUUlVFLCBwcm9wLmMgPSBGQUxTRSwgcHJvcC50ID0gRkFMU0UsIHByb3AuY2hpc3EgPSBGQUxTRSwgZm9ybWF0ID0gIlNBUyIpCmBgYAoKVGhpcyBjb25jbHVkZXMgb3VyIGZpcnN0IGV4YW1wbGUgLS0geW91IGp1c3Qgc2F3IGhvdyB5b3UgY2FuIGJ1aWxkIGFuZCBhIHRyYWluIGEgbmV1cmFsIG5ldHdvcmsgdG8gY2xhc3NpZnkgaGFuZHdyaXR0ZW4gZGlnaXRzIGluIGxlc3MgdGhhbiAyMCBsaW5lcyBvZiBSIGNvZGUuIEluIHRoZSBuZXh0IGNoYXB0ZXIsIHdlJ2xsIGdvIGludG8gZGV0YWlsIGFib3V0IGV2ZXJ5IG1vdmluZyBwaWVjZSB3ZSBqdXN0IHByZXZpZXdlZCBhbmQgY2xhcmlmeSB3aGF0J3MgZ29pbmcgb24gYmVoaW5kIHRoZSBzY2VuZXMuIFlvdSdsbCBsZWFybiBhYm91dCB0ZW5zb3JzLCB0aGUgZGF0YS1zdG9yaW5nIG9iamVjdHMgZ29pbmcgaW50byB0aGUgbmV0d29yazsgYWJvdXQgdGVuc29yIG9wZXJhdGlvbnMsIHdoaWNoIGxheWVycyBhcmUgbWFkZSBvZjsgYW5kIGFib3V0IGdyYWRpZW50IGRlc2NlbnQsIHdoaWNoIGFsbG93cyB5b3VyIG5ldHdvcmsgdG8gbGVhcm4gZnJvbSBpdHMgdHJhaW5pbmcgZXhhbXBsZXMuCg==