Introduction

The objective of this work is to create a model that can identify a number written by hand and recognize which is the number it represents. In order to tran the model I used the Mnist Dataset, more information is here: http://yann.lecun.com/exdb/mnist/.

The MNIST database of handwritten digits, has a training set of 60,000 examples, and a test set of 10,000 examples. It is a subset of a larger set available from NIST. The digits have been size-normalized and centered in a fixed-size image.

The model I used to recognize caracters is a prototype of a Digit Recognizer using a specific Fourier Transform, called Walsh Hadamard Transform, a connected component transform, some Kpi’s from each training image, principal component analysis and a feed forward Neural Nework with a hidden Layer that taes all the inputs and trains the weights of its connections to improve the predicted numbers.

In the following chunks I’ll describe step by step the process.

Binarization of the input

Each image in the dataset is a bunch of pixels joint together in a matrix. This matrix can be represented by 28 rows to 28 columns of pixels. As the trainging pictures are black and white, each pixel is a number that goes from 0 to 255, it could be said that each pixel is in the grey space. In order to be able to work with the data we need to binarize the input, in other words, take those values from 0 to 255 and decide pixel by pixel if it is a 0 or a 1.

In this two set of images we can see and example of how the binarization takes place. In the upper images the raw data, and in the second set of pizels the binarized data.

Original Images

Binarized Images

As we can see I had to choose a threshold for every pixel in order to be a 1 or a 0. After several tryouts, the choosen number was 100 out of 255. This number gave me a good start to binarizate each image without a major information loss.

KPI Calculation

In the following lines I will explain all the inputs used for the Neural Network. Is important to clarify that the neural network cannot take as an input the raw images, we need to constract a set of key performance indicators, that differenciate each image from the rest and be different enough not to duplicate information.

Sums per row and per column

In these 2 kpi’s we generate per image (hich as 28 pixels by 28 pixels), the sum of pixels per column and per row. As an Example we can see the sum of columns and rows for an example. In the axes, the sum of rows and the sum of columns is represented, transformed into a vector of colors for better comprehension. When the color is closer to the white, the sum for that specific column or row is bigger, when the color is darker, the sum is lower.

Dual Images

In the following 2 KPI’s we take each number and we duplicate the a part of it, like making a mirror. This Kpi will help us identify characters which its first half is the same as the second half. For example, the 0 and the 8 in both directions or the 3 in the vertical mirror.

As an example we see the original picture and the dual picture of a character 3.

With this KPI’s already generated we are ready now to move into the Neural Network parameter generation process.

Parameters Generation

1: H30

For this parameter we generate the sum of pixels for the first 30% of rows of each number.

For this image the Row sum H30 is 34.

2. H50

For this parameter we generate the sum of pixels for the first 50% of rows of each number.

For this image the Row sum H50 is 48.

3. H80

For this parameter we generate the sum of pixels for the first 80% of rows of each number.

For this image the Row sum H80 is 117.

4. V30

For this parameter we generate the sum of pixels for the first 30% of columns of each number.

For this image the column sum V80 is 11.

5. V50

For this parameter we generate the sum of pixels for the first 50% of columns of each number.

For this image the column sum V50 is 46.

6. V80

For this parameter we generate the sum of pixels for the first 80% of columns of each number.

For this image the column sum V80 is 69.

7. Horizontal Similarity

Correlation between an input character sample ‘I’ with its horizontal mirror ‘X’. It should be noted that ‘X’ is generated from ‘I’ where lower half of ‘X’ is same as that of ‘I’ and upper half of ‘X’ is the mirror image of its lower half.

For example, here we plot 2 binarized imageS and its correspondant mirrorS. For the image in the top the correlation is 0.73 and for the image in the botton the correlation is 0.67.

8. Vertical Similarity

Correlation between an input character sample (I) and its Vertical mirror (X). It should be noted that ‘X’ is generated from ‘I’ where left half of ‘X’ is same as that of ‘I’ and right half of ‘X’ is the mirror image of its left half.

For example, here we plot 2 binarized images and its correspondant mirrors. For the image in the top the correlation is 0.75 and for the image in the botton the correlation is 0.5.

##9.Walsh-Hadamard transform##

WHT DataBase Generation

In order to be able to use the Walsh-Hadamard Transformation we need to compare each image with defailt number images. Because of this I created 10 default number images and generated the Walsh-Hadamard Transformation to each of them.

WHT Compare

I need to compare de correlation of the denoted image with the WHT DataBase. For example for a given image, I show the image and the 10 correlations to each of the default characters.

For this image the correlation to each character is:

zero one two three four five six seven eight nine
0.41 0.13 0.32 0.22 0.3 0.28 0.44 0.14 0.29 0.27

As we can see the WHT get a good approximation to the number we are looking for. In tihs case it puts the zero as a second choice, but very close to the first place, where the number 6 is.

10. Connected Component Transform

It gives a measure of the number of closed areas in a character. In an image, all the connected pixels are given same labels. Thus if there are two sets of such pixels, as in ‘8’, they would be labeled as ‘1’ and ‘2’. Thus the highest value of ‘label’ gives an idea of closed areas present in the character.

We use the connected component transform (Rosenfeld and Pfalz, 1966) and we show its result for 10 numbers in little black letters in the top of each image.

Which is actually the numbers of components inside the numbers.

11.Principal Component Analysis

We have 28x28 pixels per image. Most of them are 0 for each image. So we can say that most of them give us no information regarding which number weneed to predict. We will enhage a Principal Component Analysis to try to figure out which are the principal Components of the dataset that absorb the maximum variance. After this we will add this prdictors to the Nnet Input.

We apply PCA to the coviariance matrix and check how many components should we keep.

Acording to the plot we keep the first 15 components, which represent almost 90% of the variance, to fit the model.

train_score <- as.matrix(training) %*% train_pc$rotation[,1:15]

Final Data Set

Data: Dataset of all the calculated kpi’s per photo.

We show the names of the variables of the Data set.

In the following graphs we can see a density plot per varable taking into account the number it represents.

Train and Test

In this step we scale all the variables and we center them so that there is no weight difference between them, so that all variables have the same initial importance. This is a key step for the neural network training to succeed. We also create train and test partitions of the data.

I show a couple of rows to see the final Data set in action.

Neural Network

In this step we train the Neural network.

For this specific training set, I’ve choosen a neural network with 1 hidden layer with 22 nodes. This election was done after testing the nnet with a several diferent numbers of nodes for the hidden layer. I’ve tried with 10 nodes, to 28 and the best accuracy for the model was obteined with 24 nodes.

Model Results:

my.grid6 <- expand.grid(.decay = c(0.5), .size = c(22))
train_control <- trainControl(method="repeatedcv", number=10, repeats=3)
numbers.fit6 <- train(label ~ ., data = numbers.traintotal,method = "nnet", maxit = 1000, trContor=train_control,tuneGrid = my.grid6, trace = F, linout = 1) 

Plot Neural Network

Neural Network 

42000 samples
   34 predictor
   10 classes: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' 

No pre-processing
Resampling: Bootstrapped (25 reps) 
Summary of sample sizes: 42000, 42000, 42000, 42000, 42000, 42000, ... 
Resampling results:

  Accuracy   Kappa    
  0.9566663  0.9518345

Tuning parameter 'size' was held constant at a value of 22
Tuning parameter 'decay'
 was held constant at a value of 0.5
 

Test Set Predictions table

For the test set we have a 97.1 % prediction accuracy.

Here we show the confusion matrix for the test set.

Good preditions

I’ll like to show some images of good predictions:

Bad preditions

And also some pictures of bad predicted caracters with the predicted character in little black font.

As we can see in the previuos graph, the badly predicted characters are not easy to distinguish and even for a person its hard to recognize.

LS0tDQp0aXRsZTogIkRpZ2l0IFJlY29nbml6ZXIiDQphdXRob3I6ICJGYWN1bmRvIENhbGNhZ25vIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiANCiAgICBoaWdobGlnaHQ6IGthdGUNCiAgICB0aGVtZTogY29zbW8NCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMQ0KICBodG1sX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KI0ludHJvZHVjdGlvbiMNCg0KVGhlIG9iamVjdGl2ZSBvZiB0aGlzIHdvcmsgaXMgdG8gY3JlYXRlIGEgbW9kZWwgdGhhdCBjYW4gaWRlbnRpZnkgYSBudW1iZXIgd3JpdHRlbiBieSBoYW5kIGFuZCByZWNvZ25pemUgd2hpY2ggaXMgdGhlIG51bWJlciBpdCByZXByZXNlbnRzLiBJbiBvcmRlciB0byB0cmFuIHRoZSBtb2RlbCBJIHVzZWQgdGhlIE1uaXN0IERhdGFzZXQsIG1vcmUgaW5mb3JtYXRpb24gaXMgaGVyZTogaHR0cDovL3lhbm4ubGVjdW4uY29tL2V4ZGIvbW5pc3QvLg0KDQpUaGUgTU5JU1QgZGF0YWJhc2Ugb2YgaGFuZHdyaXR0ZW4gZGlnaXRzLCAgaGFzIGEgdHJhaW5pbmcgc2V0IG9mIDYwLDAwMCBleGFtcGxlcywgYW5kIGEgdGVzdCBzZXQgb2YgMTAsMDAwIGV4YW1wbGVzLiBJdCBpcyBhIHN1YnNldCBvZiBhIGxhcmdlciBzZXQgYXZhaWxhYmxlIGZyb20gTklTVC4gVGhlIGRpZ2l0cyBoYXZlIGJlZW4gc2l6ZS1ub3JtYWxpemVkIGFuZCBjZW50ZXJlZCBpbiBhIGZpeGVkLXNpemUgaW1hZ2UuIA0KDQpUaGUgbW9kZWwgSSB1c2VkIHRvIHJlY29nbml6ZSBjYXJhY3RlcnMgaXMgYSBwcm90b3R5cGUgb2YgYSBEaWdpdCBSZWNvZ25pemVyIHVzaW5nIGEgc3BlY2lmaWMgRm91cmllciBUcmFuc2Zvcm0sIGNhbGxlZCBXYWxzaCBIYWRhbWFyZCBUcmFuc2Zvcm0sIGEgY29ubmVjdGVkIGNvbXBvbmVudCB0cmFuc2Zvcm0sIHNvbWUgS3BpJ3MgZnJvbSBlYWNoIHRyYWluaW5nIGltYWdlLCBwcmluY2lwYWwgY29tcG9uZW50IGFuYWx5c2lzIGFuZCBhIGZlZWQgZm9yd2FyZCBOZXVyYWwgTmV3b3JrIHdpdGggYSBoaWRkZW4gTGF5ZXIgdGhhdCB0YWVzIGFsbCB0aGUgaW5wdXRzIGFuZCB0cmFpbnMgdGhlIHdlaWdodHMgb2YgaXRzIGNvbm5lY3Rpb25zIHRvIGltcHJvdmUgdGhlIHByZWRpY3RlZCBudW1iZXJzLg0KDQpJbiB0aGUgZm9sbG93aW5nIGNodW5rcyBJJ2xsIGRlc2NyaWJlIHN0ZXAgYnkgc3RlcCB0aGUgcHJvY2Vzcy4NCg0KICANCmBgYHtyIGRhdGFpLCBldmFsPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBjYWNoZT1UUlVFLCBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShwcm90bykNCmxpYnJhcnkocmVhZHIpDQp0cmFpbiA8LSBkYXRhLmZyYW1lKHJlYWRfY3N2KCJkYXRhL3RyYWluLmNzdiIpKQ0KYGBgDQoNCiNCaW5hcml6YXRpb24gb2YgdGhlIGlucHV0DQoNCkVhY2ggaW1hZ2UgaW4gdGhlIGRhdGFzZXQgaXMgYSBidW5jaCBvZiBwaXhlbHMgam9pbnQgdG9nZXRoZXIgaW4gYSBtYXRyaXguIFRoaXMgbWF0cml4IGNhbiBiZSByZXByZXNlbnRlZCBieSAyOCByb3dzIHRvIDI4IGNvbHVtbnMgb2YgcGl4ZWxzLiBBcyB0aGUgdHJhaW5naW5nIHBpY3R1cmVzIGFyZSBibGFjayBhbmQgd2hpdGUsICBlYWNoIHBpeGVsIGlzIGEgbnVtYmVyIHRoYXQgZ29lcyBmcm9tIDAgdG8gMjU1LCBpdCBjb3VsZCBiZSBzYWlkIHRoYXQgZWFjaCBwaXhlbCBpcyBpbiB0aGUgZ3JleSBzcGFjZS4gSW4gb3JkZXIgdG8gYmUgYWJsZSB0byB3b3JrIHdpdGggdGhlIGRhdGEgd2UgbmVlZCB0byBiaW5hcml6ZSB0aGUgaW5wdXQsIGluIG90aGVyIHdvcmRzLCB0YWtlIHRob3NlIHZhbHVlcyBmcm9tIDAgdG8gMjU1IGFuZCBkZWNpZGUgcGl4ZWwgYnkgcGl4ZWwgaWYgaXQgaXMgYSAwIG9yIGEgMS4gICANCg0KYGBge3IgaW5wdXQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0UsIGNhY2hlPVRSVUV9DQojVmlzdWFsaXphdGlvbiANCnNldC5zZWVkKDcxKQ0KbjE9bnJvdyh0cmFpbikNCmRhdGE8LXRyYWluDQpwYXIobWZyb3c9YygxMCwxMCksbWFyPWMoMC4xLDAuMSwwLjEsMC4xKSkNCmRhdG9zPC1saXN0KCkNCm9yaWdpbmFsPC1saXN0KCkNCg0KbD0wDQpmb3IgKGsgaW4gMTpuMSkNCnsNCiAgICBsPWwrMQ0KICAgcm93IDwtIE5VTEwNCiAgICBmb3IgKG4gaW4gMjo3ODUpDQogICAgICAgIHJvd1tuLTFdIDwtIHRyYWluW2ssbi0xXQ0KICAgICBtYXRyaXgxIDwtIG1hdHJpeChyb3csMjgsMjgsYnlyb3c9RkFMU0UpDQogICAgIG1hdHJpeDIgPC0gbWF0cml4KHJlcCgwLDc4NCksMjgsMjgpDQogICAgIG1hdHJpeDMgPC0gbWF0cml4KHJlcCgwLDc4NCksMjgsMjgpDQogICANCiAgICBmb3IgKGkgaW4gMToyOCkNCiAgICAgICAgZm9yIChqIGluIDE6MjgpDQogICAgICAgIHsNCiAgICAgICAgICAgbWF0cml4M1tpLDI4LWorMV08LW1hdHJpeDFbaSxqXQ0KICAgICAgICAgIGlmIChtYXRyaXgxW2ksal0+MTAwKSBtYXRyaXgyW2ksMjgtaisxXTwtMSANCiAgICAgICAgfQ0KICAgICAgICAgZGF0b3NbW2xdXTwtbWF0cml4Mg0KICAgICAgICAgb3JpZ2luYWxbW2xdXTwtbWF0cml4Mw0KICAgIA0KDQp9DQoNCmBgYA0KDQpJbiB0aGlzIHR3byBzZXQgb2YgaW1hZ2VzIHdlIGNhbiBzZWUgYW5kIGV4YW1wbGUgb2YgaG93IHRoZSBiaW5hcml6YXRpb24gdGFrZXMgcGxhY2UuIEluIHRoZSB1cHBlciBpbWFnZXMgdGhlIHJhdyBkYXRhLCBhbmQgaW4gdGhlIHNlY29uZCBzZXQgb2YgcGl6ZWxzIHRoZSBiaW5hcml6ZWQgZGF0YS4NCg0KIyMjT3JpZ2luYWwgSW1hZ2VzIyMjDQpgYGB7ciwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBhcihtZnJvdz1jKDUsMTApLG1hcj1jKC4xLC4xLC4xLC4xKSkNCmZvciAoaSBpbiAxOjUwKSB7DQogIGltYWdlKG9yaWdpbmFsW1tpXV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygxMCkpDQp9DQpgYGANCiMjI0JpbmFyaXplZCBJbWFnZXMjIyMNCmBgYHtyIGJpbmFyZSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBhcihtZnJvdz1jKDUsMTApLG1hcj1jKC4xLC4xLC4xLC4xKSkNCmZvciAoaSBpbiAxOjUwKSB7DQogIGltYWdlKGRhdG9zW1tpXV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygxMCkpDQp9DQpgYGANCg0KDQpBcyB3ZSBjYW4gc2VlIEkgaGFkIHRvIGNob29zZSBhIHRocmVzaG9sZCAgZm9yIGV2ZXJ5IHBpeGVsIGluIG9yZGVyIHRvIGJlIGEgMSBvciBhIDAuIEFmdGVyIHNldmVyYWwgdHJ5b3V0cywgdGhlIGNob29zZW4gbnVtYmVyIHdhcyAxMDAgb3V0IG9mIDI1NS4gVGhpcyBudW1iZXIgZ2F2ZSBtZSBhIGdvb2Qgc3RhcnQgdG8gYmluYXJpemF0ZSBlYWNoIGltYWdlIHdpdGhvdXQgYSBtYWpvciBpbmZvcm1hdGlvbiBsb3NzLg0KDQojS1BJIENhbGN1bGF0aW9uIw0KDQpJbiB0aGUgZm9sbG93aW5nIGxpbmVzIEkgd2lsbCBleHBsYWluIGFsbCB0aGUgaW5wdXRzIHVzZWQgZm9yIHRoZSBOZXVyYWwgTmV0d29yay4gSXMgaW1wb3J0YW50IHRvIGNsYXJpZnkgdGhhdCB0aGUgbmV1cmFsIG5ldHdvcmsgY2Fubm90IHRha2UgYXMgYW4gaW5wdXQgdGhlIHJhdyBpbWFnZXMsIHdlIG5lZWQgdG8gY29uc3RyYWN0IGEgc2V0IG9mIGtleSBwZXJmb3JtYW5jZSBpbmRpY2F0b3JzLCB0aGF0IGRpZmZlcmVuY2lhdGUgZWFjaCBpbWFnZSBmcm9tIHRoZSByZXN0IGFuZCBiZSBkaWZmZXJlbnQgZW5vdWdoIG5vdCB0byBkdXBsaWNhdGUgaW5mb3JtYXRpb24uIA0KDQojI1N1bXMgcGVyIHJvdyBhbmQgcGVyIGNvbHVtbiMjDQpJbiB0aGVzZSAyIGtwaSdzIHdlIGdlbmVyYXRlIHBlciBpbWFnZSAoaGljaCBhcyAyOCBwaXhlbHMgYnkgMjggcGl4ZWxzKSwgdGhlIHN1bSBvZiBwaXhlbHMgcGVyIGNvbHVtbiBhbmQgcGVyIHJvdy4gQXMgYW4gRXhhbXBsZSB3ZSBjYW4gc2VlIHRoZSBzdW0gb2YgY29sdW1ucyBhbmQgcm93cyBmb3IgYW4gZXhhbXBsZS4NCkluIHRoZSBheGVzLCB0aGUgc3VtIG9mIHJvd3MgYW5kIHRoZSBzdW0gb2YgY29sdW1ucyBpcyByZXByZXNlbnRlZCwgdHJhbnNmb3JtZWQgaW50byBhIHZlY3RvciBvZiBjb2xvcnMgZm9yIGJldHRlciBjb21wcmVoZW5zaW9uLiANCldoZW4gdGhlIGNvbG9yIGlzIGNsb3NlciB0byB0aGUgd2hpdGUsIHRoZSBzdW0gZm9yIHRoYXQgc3BlY2lmaWMgY29sdW1uIG9yIHJvdyBpcyBiaWdnZXIsIHdoZW4gdGhlIGNvbG9yIGlzIGRhcmtlciwgdGhlIHN1bSAgaXMgbG93ZXIuDQoNCmBgYHtyIHN1bSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KIyNzdW0gcGVyIGNvbHVtbg0KDQpzdW0xPC1tYXRyaXgocmVwKDAsMjgpLDI4KQ0Kcm93czwtbGlzdCgpDQoNCmZvciAoayBpbiAxOm4xKQ0Kew0KICBpbWFnZTwtZGF0b3NbW2tdXQ0KICAgZm9yIChpIGluIDE6MjgpDQogIHsNCiAgICAgICAgICBzdW0xW2ldPC0wDQogICAgICAgICAgIGZvciAoaiBpbiAxOjI4KQ0KICAgICAgICAgIHsNCiAgICAgICAgICAgICBzdW0xW2ldPC1zdW0xW2ldK2ltYWdlW2ksal0NCiAgICAgICAgICANCiAgICAgICAgICB9DQogICB9DQogIHJvd3NbW2tdXTwtc3VtMQ0KfQ0KDQojI3N1bSBwZXIgUm93DQoNCnN1bTE8LW1hdHJpeChyZXAoMCwyOCksMjgpDQpjb2x1bW5zPC1saXN0KCkNCg0KZm9yIChrIGluIDE6bjEpDQp7DQogIGltYWdlPC1kYXRvc1tba11dDQogICBmb3IgKGkgaW4gMToyOCkNCiAgew0KICAgICAgICAgIHN1bTFbaV08LTANCiAgICAgICAgICAgZm9yIChqIGluIDE6MjgpDQogICAgICAgICAgew0KICAgICAgICAgICAgIHN1bTFbaV08LXN1bTFbaV0raW1hZ2VbaixpXQ0KICAgICAgICAgIA0KICAgICAgICAgIH0NCiAgIH0NCiAgY29sdW1uc1tba11dPC1zdW0xDQp9DQpwYXIobWZyb3c9YygyLDIpLG1hcj1jKC4xLC4xLC41LC41KSkNCmxheW91dChtYXRyaXgoYygxLDIsMyw0KSwgMiwgMiwgYnlyb3cgPSBUUlVFKSx3aWR0aHM9YygxLDYpLCBoZWlnaHRzPWMoMSw2KSkNCmltYWdlKG1hdHJpeChyZXAoMCwyKSksY29sPWdyZXkuY29sb3JzKDEwKSkNCmltYWdlKG1hdHJpeChyb3dzW1sxMF1dLCBuY29sPTEpLGNvbD1ncmV5LmNvbG9ycygxMCkpDQppbWFnZShtYXRyaXgoY29sdW1uc1tbMTBdXSwgbnJvdz0xKSxjb2w9Z3JleS5jb2xvcnMoMTApKQ0KaW1hZ2UoZGF0b3NbWzEwXV0sIGF4ZXM9VFJVRSwgY29sPWdyZXkuY29sb3JzKDEwKSkgDQoNCg0KYGBgDQoNCiMjIER1YWwgSW1hZ2VzIyMNCkluIHRoZSBmb2xsb3dpbmcgMiBLUEkncyB3ZSB0YWtlIGVhY2ggbnVtYmVyIGFuZCB3ZSBkdXBsaWNhdGUgdGhlIGEgcGFydCBvZiBpdCwgbGlrZSBtYWtpbmcgYSBtaXJyb3IuIA0KVGhpcyBLcGkgd2lsbCBoZWxwIHVzIGlkZW50aWZ5IGNoYXJhY3RlcnMgd2hpY2ggaXRzIGZpcnN0IGhhbGYgaXMgdGhlIHNhbWUgYXMgdGhlIHNlY29uZCBoYWxmLiBGb3IgZXhhbXBsZSwgdGhlIDAgYW5kIHRoZSA4IGluIGJvdGggZGlyZWN0aW9ucyBvciB0aGUgMyBpbiB0aGUgdmVydGljYWwgbWlycm9yLg0KDQpBcyBhbiBleGFtcGxlIHdlIHNlZSB0aGUgb3JpZ2luYWwgcGljdHVyZSBhbmQgdGhlIGR1YWwgcGljdHVyZSBvZiBhIGNoYXJhY3RlciAzLg0KDQpgYGB7ciBEdWFsLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQojR2VuZXJhdGUgZHVhbCBpbWFnZSBieSByb3cNCmR1YWxfcm93PC1saXN0KCkNCmZvciAoayBpbiAxOm4xKQ0Kew0KICBpbWFnZTwtZGF0b3NbW2tdXQ0KICBkdWFsX2ltYWdlPC1pbWFnZQ0KICBmb3IgKGkgaW4gMToyOCkgew0KICAgIGZvciAoaiBpbiAxOjE0KSB7DQogICAgICBkdWFsX2ltYWdlW2ksal08LWltYWdlW2ksal0NCiAgICAgIGR1YWxfaW1hZ2VbaSwyOS1qXTwtaW1hZ2VbaSxqXQ0KICAgIH0NCiAgfQ0KICBkdWFsX3Jvd1tba11dPC1kdWFsX2ltYWdlDQp9DQoNCmR1YWxfY29sPC1saXN0KCkNCmZvciAoayBpbiAxOm4xKQ0Kew0KICBpbWFnZTwtZGF0b3NbW2tdXQ0KICBkdWFsX2ltYWdlPC1pbWFnZQ0KICBmb3IgKGogaW4gMToyOCkgew0KICAgIGZvciAoaSBpbiAxOjE0KSB7DQogICAgICBkdWFsX2ltYWdlW2ksal08LWltYWdlW2ksal0NCiAgICAgIGR1YWxfaW1hZ2VbMjktaSxqXTwtaW1hZ2VbaSxqXQ0KICAgIH0NCiAgfQ0KICBkdWFsX2NvbFtba11dPC1kdWFsX2ltYWdlDQp9DQpwYXIobWZyb3c9YygxLDMpKQ0KaW1hZ2UoZGF0b3NbWzhdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KdGl0bGUoc3ViID0gIk9yaWdpbmFsIiwgZm9udC5zdWIgPSAyLCBjb2wuc3ViID0gImJsYWNrIixjZXguc3ViID0gMikNCmltYWdlKGR1YWxfY29sW1s4XV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKHN1YiA9ICJDb2x1bW4gbWlycm9yIiwgZm9udC5zdWIgPSAyICwgY29sLnN1YiA9ICJibGFjayIsY2V4LnN1YiA9IDIpDQppbWFnZShkdWFsX3Jvd1tbOF1dLCBheGVzPUZBTFNFLCBjb2w9Z3JleS5jb2xvcnMoMikpDQp0aXRsZShzdWIgPSAiUm93IG1pcnJvciIsIGZvbnQuc3ViID0gMiwgY29sLnN1YiA9ICJibGFjayIsY2V4LnN1YiA9IDIpDQpgYGANCg0KV2l0aCB0aGlzIEtQSSdzIGFscmVhZHkgZ2VuZXJhdGVkIHdlIGFyZSByZWFkeSBub3cgdG8gbW92ZSBpbnRvIHRoZSBOZXVyYWwgTmV0d29yayBwYXJhbWV0ZXIgZ2VuZXJhdGlvbiBwcm9jZXNzLg0KDQoNCiNQYXJhbWV0ZXJzIEdlbmVyYXRpb24jDQoNCiMjMTogSDMwIyMNCg0KRm9yIHRoaXMgcGFyYW1ldGVyIHdlIGdlbmVyYXRlIHRoZSBzdW0gb2YgcGl4ZWxzIGZvciB0aGUgZmlyc3QgMzAlIG9mIHJvd3Mgb2YgZWFjaCBudW1iZXIuDQoNCmBgYHtyIEgzMCwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCkgzMDwtbGlzdCgpDQphPSByb3VuZCgyOC8zKQ0KZm9yIChrIGluIDE6bjEpDQp7DQogIHN1bT0wDQogIGZvciAoaiBpbiAxOmEpDQogIHsNCiAgIHN1bT1zdW0rY29sdW1uc1tba11dW2pdDQogIH0NCiAgSDMwW1trXV08LXN1bQ0KfQ0KcGFyKG1mcm93PWMoMSwyKSkNCmltYWdlKGRhdG9zW1s4XV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCiNsYXlvdXQobWF0cml4KGMoMSwyLDMsNCksIDIsIDIsIGJ5cm93ID0gVFJVRSksd2lkdGhzPWMoMSwxKSwgaGVpZ2h0cz1jKDMsMSkpDQp0aXRsZShzdWIgPSAiT3JpZ2luYWwiLCBmb250LnN1YiA9IDIsIGNvbC5zdWIgPSAiYmxhY2siLGNleC5zdWIgPSAyKQ0KI2ltYWdlKG1hdHJpeChyZXAoMCwyKSksY29sPWdyZXkuY29sb3JzKDEwKSkNCmltYWdlKGRhdG9zW1s4XV1bLDE6YV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKHN1YiA9ICJSb3cgc3VtIEgzMCIsIGZvbnQuc3ViID0gMiAsIGNvbC5zdWIgPSAiYmxhY2siLGNleC5zdWIgPSAyKQ0KYGBgDQpGb3IgdGhpcyBpbWFnZSB0aGUgUm93IHN1bSBIMzAgaXMgYHIgSDMwW1s4XV1gLg0KDQojIzIuIEg1MCMjDQoNCkZvciB0aGlzIHBhcmFtZXRlciB3ZSBnZW5lcmF0ZSB0aGUgc3VtIG9mIHBpeGVscyBmb3IgdGhlIGZpcnN0IDUwJSBvZiByb3dzIG9mIGVhY2ggbnVtYmVyLg0KYGBge3IgSDUwLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KSDUwPC1saXN0KCkNCmE9IHJvdW5kKDI4LzIpDQpmb3IgKGsgaW4gMTpuMSkNCnsNCiAgc3VtPTANCiAgZm9yIChqIGluIDE6YSkNCiAgew0KICAgc3VtPXN1bStjb2x1bW5zW1trXV1bal0NCiAgfQ0KICBINTBbW2tdXTwtc3VtDQp9DQoNCnBhcihtZnJvdz1jKDEsMikpDQppbWFnZShkYXRvc1tbMjBdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KdGl0bGUoc3ViID0gIk9yaWdpbmFsIiwgZm9udC5zdWIgPSAyLCBjb2wuc3ViID0gImJsYWNrIixjZXguc3ViID0gMikNCmltYWdlKGRhdG9zW1syMF1dWywxOmFdLCBheGVzPUZBTFNFLCBjb2w9Z3JleS5jb2xvcnMoMikpDQp0aXRsZShzdWIgPSAiUm93IHN1bSBINTAiLCBmb250LnN1YiA9IDIgLCBjb2wuc3ViID0gImJsYWNrIixjZXguc3ViID0gMikNCmBgYA0KRm9yIHRoaXMgaW1hZ2UgdGhlIFJvdyBzdW0gSDUwIGlzIGByIEg1MFtbOF1dYC4NCg0KIyMzLiBIODAjIw0KDQpGb3IgdGhpcyBwYXJhbWV0ZXIgd2UgZ2VuZXJhdGUgdGhlIHN1bSBvZiBwaXhlbHMgZm9yIHRoZSBmaXJzdCA4MCUgb2Ygcm93cyBvZiBlYWNoIG51bWJlci4NCmBgYHtyIEg4MCwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCkg4MDwtbGlzdCgpDQphPSByb3VuZCgyOC8xLjI1KQ0KZm9yIChrIGluIDE6bjEpDQp7DQogIHN1bT0wDQogIGZvciAoaiBpbiAxOmEpDQogIHsNCiAgIHN1bT1zdW0rY29sdW1uc1tba11dW2pdDQogIH0NCiAgSDgwW1trXV08LXN1bQ0KfQ0KcGFyKG1mcm93PWMoMSwyKSkNCmltYWdlKGRhdG9zW1syNV1dLCBheGVzPUZBTFNFLCBjb2w9Z3JleS5jb2xvcnMoMikpDQp0aXRsZShzdWIgPSAiT3JpZ2luYWwiLCBmb250LnN1YiA9IDIsIGNvbC5zdWIgPSAiYmxhY2siLGNleC5zdWIgPSAyKQ0KaW1hZ2UoZGF0b3NbWzI1XV1bLDE6YV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKHN1YiA9ICJSb3cgc3VtIEg4MCIsIGZvbnQuc3ViID0gMiAsIGNvbC5zdWIgPSAiYmxhY2siLGNleC5zdWIgPSAyKQ0KDQpgYGANCkZvciB0aGlzIGltYWdlIHRoZSBSb3cgc3VtIEg4MCBpcyBgciBIODBbWzI1XV1gLg0KDQojIzQuIFYzMCMjDQoNCkZvciB0aGlzIHBhcmFtZXRlciB3ZSBnZW5lcmF0ZSB0aGUgc3VtIG9mIHBpeGVscyBmb3IgdGhlIGZpcnN0IDMwJSBvZiBjb2x1bW5zIG9mIGVhY2ggbnVtYmVyLg0KYGBge3IgVjMwLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVjMwPC1saXN0KCkNCmE9IHJvdW5kKDI4LzMpDQpmb3IgKGsgaW4gMTpuMSkNCnsNCiAgc3VtPTANCiAgZm9yIChqIGluIDE6YSkNCiAgew0KICAgc3VtPXN1bStyb3dzW1trXV1bal0NCiAgfQ0KICBWMzBbW2tdXTwtc3VtDQp9DQpwYXIobWZyb3c9YygxLDIpKQ0KaW1hZ2UoZGF0b3NbWzI1XV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKHN1YiA9ICJPcmlnaW5hbCIsIGZvbnQuc3ViID0gMiwgY29sLnN1YiA9ICJibGFjayIsY2V4LnN1YiA9IDIpDQppbWFnZShkYXRvc1tbMjVdXVsxOmEsXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KdGl0bGUoc3ViID0gIkNvbHVtbiBzdW0gVjMwIiwgZm9udC5zdWIgPSAyICwgY29sLnN1YiA9ICJibGFjayIsY2V4LnN1YiA9IDIpDQoNCmBgYA0KRm9yIHRoaXMgaW1hZ2UgdGhlIGNvbHVtbiBzdW0gVjgwIGlzIGByIFYzMFtbMjVdXWAuDQoNCiMjNS4gVjUwIyMNCg0KRm9yIHRoaXMgcGFyYW1ldGVyIHdlIGdlbmVyYXRlIHRoZSBzdW0gb2YgcGl4ZWxzIGZvciB0aGUgZmlyc3QgNTAlIG9mIGNvbHVtbnMgb2YgZWFjaCBudW1iZXIuDQpgYGB7ciBWNTAsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpWNTA8LWxpc3QoKQ0KYT0gcm91bmQoMjgvMikNCmZvciAoayBpbiAxOm4xKQ0Kew0KICBzdW09MA0KICBmb3IgKGogaW4gMTphKQ0KICB7DQogICBzdW09c3VtK3Jvd3NbW2tdXVtqXQ0KICB9DQogIFY1MFtba11dPC1zdW0NCn0NCg0KcGFyKG1mcm93PWMoMSwyKSkNCmltYWdlKGRhdG9zW1szNV1dLCBheGVzPUZBTFNFLCBjb2w9Z3JleS5jb2xvcnMoMikpDQp0aXRsZShzdWIgPSAiT3JpZ2luYWwiLCBmb250LnN1YiA9IDIsIGNvbC5zdWIgPSAiYmxhY2siLGNleC5zdWIgPSAyKQ0KaW1hZ2UoZGF0b3NbWzM1XV1bMTphLF0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKHN1YiA9ICJDb2x1bW4gc3VtIFY1MCIsIGZvbnQuc3ViID0gMiAsIGNvbC5zdWIgPSAiYmxhY2siLGNleC5zdWIgPSAyKQ0KDQpgYGANCkZvciB0aGlzIGltYWdlIHRoZSBjb2x1bW4gc3VtIFY1MCBpcyBgciBWNTBbWzM1XV1gLg0KDQojIzYuIFY4MCMjDQoNCkZvciB0aGlzIHBhcmFtZXRlciB3ZSBnZW5lcmF0ZSB0aGUgc3VtIG9mIHBpeGVscyBmb3IgdGhlIGZpcnN0IDgwJSBvZiBjb2x1bW5zIG9mIGVhY2ggbnVtYmVyLg0KYGBge3IgVjgwLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KVjgwPC1saXN0KCkNCmE9IHJvdW5kKDI4LzEuMjUpDQpmb3IgKGsgaW4gMTpuMSkNCnsNCiAgc3VtPTANCiAgZm9yIChqIGluIDE6YSkNCiAgew0KICAgc3VtPXN1bStyb3dzW1trXV1bal0NCiAgfQ0KICBWODBbW2tdXTwtc3VtDQp9DQoNCnBhcihtZnJvdz1jKDEsMikpDQppbWFnZShkYXRvc1tbODFdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KdGl0bGUoc3ViID0gIk9yaWdpbmFsIiwgZm9udC5zdWIgPSAyLCBjb2wuc3ViID0gImJsYWNrIixjZXguc3ViID0gMikNCmltYWdlKGRhdG9zW1s4MV1dWzE6YSxdLCBheGVzPUZBTFNFLCBjb2w9Z3JleS5jb2xvcnMoMikpDQp0aXRsZShzdWIgPSAiQ29sdW1uIHN1bSBWODAiLCBmb250LnN1YiA9IDIgLCBjb2wuc3ViID0gImJsYWNrIixjZXguc3ViID0gMikNCg0KYGBgDQpGb3IgdGhpcyBpbWFnZSB0aGUgY29sdW1uIHN1bSBWODAgaXMgYHIgVjgwW1s4MV1dYC4NCg0KDQojIzcuIEhvcml6b250YWwgU2ltaWxhcml0eSMjDQpDb3JyZWxhdGlvbiBiZXR3ZWVuIGFuIGlucHV0IGNoYXJhY3RlciBzYW1wbGUg4oCYSeKAmSB3aXRoIGl0cyBob3Jpem9udGFsIG1pcnJvciDigJhY4oCZLiBJdCBzaG91bGQgYmUgbm90ZWQgdGhhdCDigJhY4oCZIGlzIGdlbmVyYXRlZCBmcm9tIOKAmEnigJkgd2hlcmUgbG93ZXIgaGFsZiBvZiDigJhY4oCZIGlzIHNhbWUgYXMgdGhhdCBvZiDigJhJ4oCZIGFuZCB1cHBlciBoYWxmIG9mIOKAmFjigJkgaXMgdGhlIG1pcnJvciBpbWFnZSBvZiBpdHMgbG93ZXIgaGFsZi4NCg0KYGBge3IgSHN5bSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNXZSBoYXZlIHRvIGxvb2sgZm9yIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBtYXRyaXggZGF0b3MgYW5kIGR1YWxfcm93DQoNCkhzeW08LWxpc3QoKQ0KYT0gcm91bmQoMjgvMS4yNSkNCmZvciAoayBpbiAxOm4xKQ0Kew0KICBIc3ltW1trXV08LWNvcihjKGRhdG9zW1trXV0pLCBjKGR1YWxfcm93W1trXV0pKQ0KfQ0KYTwtZGF0YS5mcmFtZShkYXRvcz1jKGRhdG9zKSxkdWFsPWMoZHVhbF9yb3cpKQ0KDQpwYXIobWZyb3c9YygyLDIpKQ0KaW1hZ2UoZGF0b3NbWzE1XV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKHN1YiA9ICJPcmlnaW5hbCIsIGZvbnQuc3ViID0gMiwgY29sLnN1YiA9ICJibGFjayIsY2V4LnN1YiA9IDIpDQppbWFnZShkdWFsX3Jvd1tbMTVdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KdGl0bGUoc3ViID0gIlJvdyBtaXJyb3IiLCBmb250LnN1YiA9IDIgLCBjb2wuc3ViID0gImJsYWNrIixjZXguc3ViID0gMikNCmltYWdlKGRhdG9zW1syMV1dLCBheGVzPUZBTFNFLCBjb2w9Z3JleS5jb2xvcnMoMikpDQp0aXRsZShzdWIgPSAiT3JpZ2luYWwiLCBmb250LnN1YiA9IDIsIGNvbC5zdWIgPSAiYmxhY2siLGNleC5zdWIgPSAyKQ0KaW1hZ2UoZHVhbF9yb3dbWzIxXV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKHN1YiA9ICJSb3cgbWlycm9yIiwgZm9udC5zdWIgPSAyICwgY29sLnN1YiA9ICJibGFjayIsY2V4LnN1YiA9IDIpDQpgYGANCkZvciBleGFtcGxlLCBoZXJlIHdlIHBsb3QgMiBiaW5hcml6ZWQgaW1hZ2VTIGFuZCBpdHMgY29ycmVzcG9uZGFudCBtaXJyb3JTLiBGb3IgdGhlIGltYWdlIGluIHRoZSB0b3AgdGhlIGNvcnJlbGF0aW9uIGlzICBgciByb3VuZChIc3ltW1sxNV1dLDIpYCAgYW5kIGZvciB0aGUgaW1hZ2UgaW4gdGhlIGJvdHRvbiB0aGUgY29ycmVsYXRpb24gaXMgYHIgcm91bmQoSHN5bVtbMjFdXSwyKWAuDQoNCiMjOC4gVmVydGljYWwgU2ltaWxhcml0eSMjDQpDb3JyZWxhdGlvbiBiZXR3ZWVuIGFuIGlucHV0IGNoYXJhY3RlciBzYW1wbGUgKEkpIGFuZCBpdHMgVmVydGljYWwgbWlycm9yIChYKS4gIEl0IHNob3VsZCBiZSBub3RlZCB0aGF0IOKAmFjigJkgaXMgZ2VuZXJhdGVkIGZyb20g4oCYSeKAmSB3aGVyZSBsZWZ0IGhhbGYgb2Yg4oCYWOKAmSBpcyBzYW1lIGFzIHRoYXQgb2Yg4oCYSeKAmSBhbmQgcmlnaHQgaGFsZiBvZiDigJhY4oCZIGlzIHRoZSBtaXJyb3IgaW1hZ2Ugb2YgaXRzIGxlZnQgaGFsZi4NCg0KYGBge3IgdnN5bSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNXZSBoYXZlIHRvIGxvb2sgZm9yIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBtYXRyaXggZGF0b3MgYW5kIGR1YWxfcm93DQpWc3ltPC1saXN0KCkNCmZvciAoayBpbiAxOm4xKQ0Kew0KICBWc3ltW1trXV08LWNvcihjKGRhdG9zW1trXV0pLCBjKGR1YWxfY29sW1trXV0pKQ0KfQ0KDQpwYXIobWZyb3c9YygyLDIpKQ0KaW1hZ2UoZGF0b3NbWzVdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KdGl0bGUoc3ViID0gIk9yaWdpbmFsIiwgZm9udC5zdWIgPSAyLCBjb2wuc3ViID0gImJsYWNrIixjZXguc3ViID0gMikNCmltYWdlKGR1YWxfY29sW1s1XV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKHN1YiA9ICJDb2x1bW4gbWlycm9yIiwgZm9udC5zdWIgPSAyICwgY29sLnN1YiA9ICJibGFjayIsY2V4LnN1YiA9IDIpDQppbWFnZShkYXRvc1tbMjFdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KdGl0bGUoc3ViID0gIk9yaWdpbmFsIiwgZm9udC5zdWIgPSAyLCBjb2wuc3ViID0gImJsYWNrIixjZXguc3ViID0gMikNCmltYWdlKGR1YWxfY29sW1syMV1dLCBheGVzPUZBTFNFLCBjb2w9Z3JleS5jb2xvcnMoMikpDQp0aXRsZShzdWIgPSAiQ29sdW1uIG1pcnJvciIsIGZvbnQuc3ViID0gMiAsIGNvbC5zdWIgPSAiYmxhY2siLGNleC5zdWIgPSAyKQ0KYGBgDQpGb3IgZXhhbXBsZSwgaGVyZSB3ZSBwbG90IDIgYmluYXJpemVkIGltYWdlcyBhbmQgaXRzIGNvcnJlc3BvbmRhbnQgbWlycm9ycy4gRm9yIHRoZSBpbWFnZSBpbiB0aGUgdG9wIHRoZSBjb3JyZWxhdGlvbiBpcyAgYHIgcm91bmQoVnN5bVtbNV1dLDIpYCAgYW5kIGZvciB0aGUgaW1hZ2UgaW4gdGhlIGJvdHRvbiB0aGUgY29ycmVsYXRpb24gaXMgYHIgcm91bmQoVnN5bVtbMjFdXSwyKWAuDQoNCg0KICAjIzkuV2Fsc2gtSGFkYW1hcmQgdHJhbnNmb3JtIyMNCg0KIyMjV0hUIERhdGFCYXNlIEdlbmVyYXRpb24jIyMNCg0KSW4gb3JkZXIgdG8gYmUgYWJsZSB0byB1c2UgdGhlIFdhbHNoLUhhZGFtYXJkIFRyYW5zZm9ybWF0aW9uIHdlIG5lZWQgdG8gY29tcGFyZSBlYWNoIGltYWdlIHdpdGggZGVmYWlsdCBudW1iZXIgaW1hZ2VzLiBCZWNhdXNlIG9mIHRoaXMgSSBjcmVhdGVkIDEwIGRlZmF1bHQgbnVtYmVyIGltYWdlcyBhbmQgZ2VuZXJhdGVkIHRoZSBXYWxzaC1IYWRhbWFyZCBUcmFuc2Zvcm1hdGlvbiB0byBlYWNoIG9mIHRoZW0uDQoNCg0KYGBge3IgaG0sIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHJlYWRiaXRtYXApDQpsaWJyYXJ5KHByb3RvKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoYmljbHVzdCkNCmxpYnJhcnkoYm1wKQ0KDQpudW1lcm9zPC1saXN0KCkNCnJvdGF0ZSA8LSBmdW5jdGlvbih4KSB0KGFwcGx5KHgsIDIsIHJldikpDQpmb3IgKGsgaW4gMToxMCkgDQp7DQp0PWstMQ0KbnVtZXJvc1tba11dIDwtIHJvdGF0ZShhYnMoYmluYXJpemUocmVhZC5ibXAocGFzdGUocGFzdGUoJ251bWJlcnMvJyx0LHNlcCA9ICIiKSwnLmJtcCcsc2VwID0gIiIpKVssLDFdLHRocmVzaG9sZD0xMDApLTEpKQ0KfQ0KDQoNCmxpYnJhcnkocGhhbmdvcm4pDQpobTwtbGlzdCgpDQoNCmhtW1sxXV08LWZobShudW1lcm9zW1sxXV0pDQpobVtbMl1dPC1maG0obnVtZXJvc1tbMl1dKQ0KaG1bWzNdXTwtZmhtKG51bWVyb3NbWzNdXSkNCmhtW1s0XV08LWZobShudW1lcm9zW1s0XV0pDQpobVtbNV1dPC1maG0obnVtZXJvc1tbNV1dKQ0KaG1bWzZdXTwtZmhtKG51bWVyb3NbWzZdXSkNCmhtW1s3XV08LWZobShudW1lcm9zW1s3XV0pDQpobVtbOF1dPC1maG0obnVtZXJvc1tbOF1dKQ0KaG1bWzldXTwtZmhtKG51bWVyb3NbWzldXSkNCmhtW1sxMF1dPC1maG0obnVtZXJvc1tbMTBdXSkNCg0KcGFyKG1mcm93PWMoMiw1KSAsbWFyPWMoLjEsLjEsLjEsLjEpKQ0KZm9yIChrIGluIDE6MTApIGltYWdlKG51bWVyb3NbW2tdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KDQpgYGANCg0KIyMjV0hUIENvbXBhcmUjIyMNCkkgbmVlZCB0byBjb21wYXJlIGRlIGNvcnJlbGF0aW9uIG9mIHRoZSBkZW5vdGVkIGltYWdlIHdpdGggdGhlIFdIVCBEYXRhQmFzZS4NCkZvciBleGFtcGxlIGZvciBhIGdpdmVuIGltYWdlLCBJIHNob3cgdGhlIGltYWdlIGFuZCB0aGUgMTAgY29ycmVsYXRpb25zIHRvIGVhY2ggb2YgdGhlIGRlZmF1bHQgY2hhcmFjdGVycy4NCg0KYGBge3Igd2h0LCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQpsaWJyYXJ5KHBoYW5nb3JuKQ0KV0hUMDwtbGlzdCgpDQpXSFQxPC1saXN0KCkNCldIVDI8LWxpc3QoKQ0KV0hUMzwtbGlzdCgpDQpXSFQ0PC1saXN0KCkNCldIVDU8LWxpc3QoKQ0KV0hUNjwtbGlzdCgpDQpXSFQ3PC1saXN0KCkNCldIVDg8LWxpc3QoKQ0KV0hUOTwtbGlzdCgpDQoNCmNvcnJlbDwtbGlzdCgpDQpmb3IgKGsgaW4gMTpuMSkNCnsNCmhtX251bTwtZmhtKGRhdG9zW1trXV0pDQpXSFQwW1trXV08LWNvcihobV9udW0saG1bWzFdXSkNCldIVDFbW2tdXTwtY29yKGhtX251bSxobVtbMl1dKQ0KV0hUMltba11dPC1jb3IoaG1fbnVtLGhtW1szXV0pDQpXSFQzW1trXV08LWNvcihobV9udW0saG1bWzRdXSkNCldIVDRbW2tdXTwtY29yKGhtX251bSxobVtbNV1dKQ0KV0hUNVtba11dPC1jb3IoaG1fbnVtLGhtW1s2XV0pDQpXSFQ2W1trXV08LWNvcihobV9udW0saG1bWzddXSkNCldIVDdbW2tdXTwtY29yKGhtX251bSxobVtbOF1dKQ0KV0hUOFtba11dPC1jb3IoaG1fbnVtLGhtW1s5XV0pDQpXSFQ5W1trXV08LWNvcihobV9udW0saG1bWzEwXV0pDQogIH0gIA0KDQoNCiBXSFQwPC1jKGRvLmNhbGwoImNiaW5kIixXSFQwKSkgDQogV0hUMTwtYyhkby5jYWxsKCJjYmluZCIsV0hUMSkpIA0KIFdIVDI8LWMoZG8uY2FsbCgiY2JpbmQiLFdIVDIpKSANCiBXSFQzPC1jKGRvLmNhbGwoImNiaW5kIixXSFQzKSkgDQogV0hUNDwtYyhkby5jYWxsKCJjYmluZCIsV0hUNCkpIA0KIFdIVDU8LWMoZG8uY2FsbCgiY2JpbmQiLFdIVDUpKSANCiBXSFQ2PC1jKGRvLmNhbGwoImNiaW5kIixXSFQ2KSkgDQogV0hUNzwtYyhkby5jYWxsKCJjYmluZCIsV0hUNykpIA0KIFdIVDg8LWMoZG8uY2FsbCgiY2JpbmQiLFdIVDgpKQ0KIFdIVDk8LWMoZG8uY2FsbCgiY2JpbmQiLFdIVDkpKSANCiANCnBhcihtZnJvdz1jKDEsMSksbWFyPWMoLjEsLjEsLjEsLjEpKQ0KaW1hZ2UoZGF0b3NbWzJdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDIpKQ0KDQpgYGANCkZvciB0aGlzIGltYWdlIHRoZSBjb3JyZWxhdGlvbiB0byBlYWNoIGNoYXJhY3RlciBpczoNCg0KYGBge3IgY29ycmVsYXRpb25zLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTMwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Ka2FibGUoZGF0YS5mcmFtZSh6ZXJvPXJvdW5kKFdIVDBbWzJdXSwyKSxvbmU9cm91bmQoV0hUMVtbMl1dLDIpLHR3bz1yb3VuZChXSFQyW1syXV0sMiksdGhyZWU9cm91bmQoV0hUM1tbMl1dLDIpLGZvdXI9cm91bmQoV0hUNFtbMl1dLDIpLGZpdmU9cm91bmQoV0hUNVtbMl1dLDIpLHNpeD1yb3VuZChXSFQ2W1syXV0sMiksc2V2ZW49cm91bmQoV0hUN1tbMl1dLDIpLGVpZ2h0PXJvdW5kKFdIVDhbWzJdXSwyKSxuaW5lPXJvdW5kKFdIVDlbWzJdXSwyKSkpDQpgYGANCg0KDQpBcyB3ZSBjYW4gc2VlIHRoZSBXSFQgZ2V0IGEgZ29vZCBhcHByb3hpbWF0aW9uIHRvIHRoZSBudW1iZXIgd2UgYXJlIGxvb2tpbmcgZm9yLiBJbiB0aWhzIGNhc2UgaXQgcHV0cyB0aGUgemVybyBhcyBhIHNlY29uZCBjaG9pY2UsIGJ1dCB2ZXJ5IGNsb3NlIHRvIHRoZSBmaXJzdCBwbGFjZSwgd2hlcmUgdGhlIG51bWJlciA2IGlzLiANCg0KDQojIzEwLiBDb25uZWN0ZWQgQ29tcG9uZW50IFRyYW5zZm9ybSAjIw0KSXQgZ2l2ZXMgYSBtZWFzdXJlIG9mIHRoZSBudW1iZXIgb2YgY2xvc2VkIGFyZWFzIGluIGEgY2hhcmFjdGVyLiBJbiBhbiBpbWFnZSwgYWxsIHRoZQ0KY29ubmVjdGVkIHBpeGVscyBhcmUgZ2l2ZW4gc2FtZSBsYWJlbHMuIFRodXMgaWYgdGhlcmUgYXJlIHR3byBzZXRzIG9mIHN1Y2ggcGl4ZWxzLCBhcyBpbiDigJg44oCZLA0KdGhleSB3b3VsZCBiZSBsYWJlbGVkIGFzIOKAmDHigJkgYW5kIOKAmDLigJkuIFRodXMgdGhlIGhpZ2hlc3QgdmFsdWUgb2Yg4oCYbGFiZWzigJkgZ2l2ZXMgYW4gaWRlYSBvZg0KY2xvc2VkIGFyZWFzIHByZXNlbnQgaW4gdGhlIGNoYXJhY3Rlci4NCg0KV2UgdXNlIHRoZSBjb25uZWN0ZWQgY29tcG9uZW50IHRyYW5zZm9ybSAoUm9zZW5mZWxkIGFuZCBQZmFseiwgMTk2NikgYW5kIHdlIHNob3cgaXRzIHJlc3VsdCBmb3IgMTAgbnVtYmVycyBpbiBsaXR0bGUgYmxhY2sgbGV0dGVycyBpbiB0aGUgdG9wIG9mIGVhY2ggaW1hZ2UuDQoNCmBgYHtyIENOQ1AsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KHNwYXRzdGF0KQ0KIA0KQ05DUDwtbGlzdCgpDQogDQpmb3IgKGsgaW4gMTpuMSkNCnsNCiAgeDIgPC0gaW0oZGF0b3NbW2tdXSwgeGNvbD1zZXEoMSxsZW5ndGg9MjgpLCB5cm93PXNlcSgxLGxlbmd0aD0yOCkpDQogIFggPC0gbGV2ZWxzZXQoeDIsIDAuMDYpDQogICNwbG90KFgpDQogIFogPC0gY29ubmVjdGVkKFgpDQogICNwbG90KFopDQogICMgbnVtYmVyIG9mIGNvbXBvbmVudHMNCiAgbmMgPC0gbGVuZ3RoKGxldmVscyhaKSkNCiAgIyBwbG90IHdpdGggcmFuZG9taXNlZCBjb2xvdXIgbWFwDQogICMgIHBsb3QoWiwgY29sPWhzdihoPXNhbXBsZShzZXEoMCwxLGxlbmd0aD1uYyksIG5jKSkpDQogIENOQ1BbW2tdXTwtbmMtMQ0KfQ0KDQpzYW08LXNhbXBsZShuMSw1MCkNCnBhcihtZnJvdz1jKDUsMTApLG1hcj1jKC4xLC4xLDEuNCwuMSkpDQpmb3IgKGkgaW4gc2FtKQ0Kew0KICAgIGltYWdlKGRhdG9zW1tpXV0sIGF4ZXM9RkFMU0UsIGNvbD1ncmV5LmNvbG9ycygyKSkNCnRpdGxlKG1haW4gPSBDTkNQW1tpXV0sIGZvbnQubWFpbiA9IDMgLCBjb2wubWFpbiA9ICJibGFjayIpDQp9DQoNCmBgYA0KDQpXaGljaCBpcyBhY3R1YWxseSB0aGUgbnVtYmVycyBvZiBjb21wb25lbnRzIGluc2lkZSB0aGUgbnVtYmVycy4NCg0KDQojIzExLlByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMjIw0KDQpXZSBoYXZlIDI4eDI4IHBpeGVscyBwZXIgaW1hZ2UuIE1vc3Qgb2YgdGhlbSBhcmUgMCBmb3IgZWFjaCBpbWFnZS4gU28gd2UgY2FuIHNheSB0aGF0IG1vc3Qgb2YgdGhlbSBnaXZlIHVzIG5vIGluZm9ybWF0aW9uIHJlZ2FyZGluZyB3aGljaCBudW1iZXIgd2VuZWVkIHRvIHByZWRpY3QuIFdlIHdpbGwgZW5oYWdlIGEgUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpcyB0byB0cnkgdG8gZmlndXJlIG91dCB3aGljaCBhcmUgdGhlIHByaW5jaXBhbCBDb21wb25lbnRzIG9mIHRoZSBkYXRhc2V0IHRoYXQgYWJzb3JiIHRoZSBtYXhpbXVtIHZhcmlhbmNlLiBBZnRlciB0aGlzIHdlIHdpbGwgYWRkIHRoaXMgcHJkaWN0b3JzIHRvIHRoZSBObmV0IElucHV0Lg0KYGBge3IgUENBMSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkoY2FyZXQpDQpuenIgPC0gbmVhclplcm9WYXIodHJhaW5bLC0xXSxzYXZlTWV0cmljcz1ULGZyZXFDdXQ9MTAwMDAvMSx1bmlxdWVDdXQ9MS83KQ0KI3N1bShuenIkemVyb1ZhcikNCg0KI3N1bShuenIkbnp2KSAjMjA4IHByZWRpY3RvcnMgd2l0aCB6ZXJvIHZhcmlhbmNlDQoNCmN1dHZhciA8LSByb3duYW1lcyhuenJbbnpyJG56dj09VFJVRSxdKQ0KdmFyIDwtIHNldGRpZmYobmFtZXModHJhaW4pLGN1dHZhcikNCnRyYWluaW5nIDwtIHRyYWluWyx2YXJdDQoNCmxhYmVsIDwtIGFzLmZhY3Rvcih0cmFpbmluZ1tbMV1dKQ0KdHJhaW5pbmckbGFiZWwgPC0gTlVMTA0KdHJhaW5pbmcgPC0gdHJhaW5pbmcvMjU1DQpjb3Z0cmFpbiA8LSBjb3YodHJhaW5pbmcpDQpgYGANCldlIGFwcGx5IFBDQSB0byB0aGUgY292aWFyaWFuY2UgbWF0cml4IGFuZCBjaGVjayBob3cgbWFueSBjb21wb25lbnRzIHNob3VsZCB3ZSBrZWVwLg0KYGBge3IgUENBMiwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnRyYWluX3BjIDwtIHByY29tcChjb3Z0cmFpbikNCnZhcmV4IDwtIHRyYWluX3BjJHNkZXZeMi9zdW0odHJhaW5fcGMkc2Rldl4yKQ0KdmFyY3VtIDwtIGN1bXN1bSh2YXJleCkNCnJlc3VsdCA8LSBkYXRhLmZyYW1lKG51bT0xOmxlbmd0aCh0cmFpbl9wYyRzZGV2KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBleD12YXJleCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBjdW09dmFyY3VtKQ0KDQpwbG90KHJlc3VsdCRudW0scmVzdWx0JGN1bSx0eXBlPSJiIix4bGltPWMoMCwxMDApLA0KICAgICBtYWluPSJWYXJpYW5jZSBFeHBsYWluZWQgYnkgVG9wIDEwMCBDb21wb25lbnRzIiwNCiAgICAgeGxhYj0iTnVtYmVyIG9mIENvbXBvbmVudHMiLHlsYWI9IlZhcmlhbmNlIEV4cGxhaW5lZCIpDQphYmxpbmUodj0xNSxsdHk9MikNCmBgYA0KDQpBY29yZGluZyB0byB0aGUgcGxvdCB3ZSBrZWVwIHRoZSBmaXJzdCAxNSBjb21wb25lbnRzLCB3aGljaCByZXByZXNlbnQgYWxtb3N0IDkwJSBvZiB0aGUgdmFyaWFuY2UsIHRvIGZpdCB0aGUgbW9kZWwuIA0KDQpgYGB7ciBQQ0FGaW5hbCwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdHJhaW5fc2NvcmUgPC0gYXMubWF0cml4KHRyYWluaW5nKSAlKiUgdHJhaW5fcGMkcm90YXRpb25bLDE6MTVdDQpgYGANCg0KI0ZpbmFsIERhdGEgU2V0ICMNCkRhdGE6IERhdGFzZXQgb2YgYWxsIHRoZSBjYWxjdWxhdGVkIGtwaSdzIHBlciBwaG90by4gDQoNCldlIHNob3cgdGhlIG5hbWVzIG9mIHRoZSB2YXJpYWJsZXMgb2YgdGhlIERhdGEgc2V0Lg0KDQpgYGB7ciBmaW5hbGRhdGEsIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQpIMzBfbSA8LSBtYXRyaXgodW5saXN0KEgzMCksIG5yb3c9bjEpDQpINTBfbSA8LSBtYXRyaXgodW5saXN0KEg1MCksIG5yb3c9bjEpDQpIODBfbSA8LSBtYXRyaXgodW5saXN0KEg4MCksIG5yb3c9bjEpDQpWMzBfbSA8LSBtYXRyaXgodW5saXN0KFYzMCksIG5yb3c9bjEpDQpWNTBfbSA8LSBtYXRyaXgodW5saXN0KFY1MCksIG5yb3c9bjEpDQpWODBfbSA8LSBtYXRyaXgodW5saXN0KFY4MCksIG5yb3c9bjEpDQpIc3ltX20gPC0gbWF0cml4KHVubGlzdChIc3ltKSwgbnJvdz1uMSkNClZzeW1fbSA8LSBtYXRyaXgodW5saXN0KFZzeW0pLCBucm93PW4xKQ0KVnN5bV9tW2lzLm5hKFZzeW1fbSldIDwtMA0KV0hUMF9tIDwtIG1hdHJpeChXSFQwLCBucm93PW4xKQ0KV0hUMV9tIDwtIG1hdHJpeChXSFQxLCBucm93PW4xKQ0KV0hUMl9tIDwtIG1hdHJpeChXSFQyLCBucm93PW4xKQ0KV0hUM19tIDwtIG1hdHJpeChXSFQzLCBucm93PW4xKQ0KV0hUNF9tIDwtIG1hdHJpeChXSFQ0LCBucm93PW4xKQ0KV0hUNV9tIDwtIG1hdHJpeChXSFQ1LCBucm93PW4xKQ0KV0hUNl9tIDwtIG1hdHJpeChXSFQ2LCBucm93PW4xKQ0KV0hUN19tIDwtIG1hdHJpeChXSFQ3LCBucm93PW4xKQ0KV0hUOF9tIDwtIG1hdHJpeChXSFQ4LCBucm93PW4xKQ0KV0hUOV9tIDwtIG1hdHJpeChXSFQ5LCBucm93PW4xKSANCkNOQ1BfbSA8LSBtYXRyaXgodW5saXN0KENOQ1ApLCBucm93PW4xKQ0KUEMxPC10cmFpbl9zY29yZVssMV0NClBDMjwtdHJhaW5fc2NvcmVbLDJdDQpQQzM8LXRyYWluX3Njb3JlWywzXQ0KUEM0PC10cmFpbl9zY29yZVssNF0NClBDNTwtdHJhaW5fc2NvcmVbLDVdDQpQQzY8LXRyYWluX3Njb3JlWyw2XQ0KUEM3PC10cmFpbl9zY29yZVssN10NClBDODwtdHJhaW5fc2NvcmVbLDhdDQpQQzk8LXRyYWluX3Njb3JlWyw5XQ0KUEMxMDwtdHJhaW5fc2NvcmVbLDEwXQ0KUEMxMTwtdHJhaW5fc2NvcmVbLDExXQ0KUEMxMjwtdHJhaW5fc2NvcmVbLDEyXQ0KUEMxMzwtdHJhaW5fc2NvcmVbLDEzXQ0KUEMxNDwtdHJhaW5fc2NvcmVbLDE0XQ0KUEMxNTwtdHJhaW5fc2NvcmVbLDE1XQ0KDQpsYWJlbDwtZmFjdG9yKGRhdGEkbGFiZWwpDQoNCiNOb3JtYWxpemUgdGhlIGlucHV0cw0KDQp0cmFpbl9maW5hbDwtZGF0YS5mcmFtZShIMzBfbSxINTBfbSxIODBfbSxWMzBfbSxWNTBfbSxWODBfbSxIc3ltX20sVnN5bV9tLFdIVDBfbSxXSFQxX20sV0hUMl9tLFdIVDNfbSxXSFQ0X20sV0hUNV9tLFdIVDZfbSxXSFQ3X20sV0hUOF9tLFdIVDlfbSxDTkNQX20sUEMxLFBDMixQQzMsUEM0LFBDNSxQQzYsUEM3LFBDOCxQQzksUEMxMCxQQzExLFBDMTIsUEMxMyxQQzE0LFBDMTUsbGFiZWwpDQoNCmBgYA0KDQpgYGB7ciBtdWx0aXBsb3QsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQptdWx0aXBsb3QgPC0gZnVuY3Rpb24oLi4uLCBwbG90bGlzdD1OVUxMLCBmaWxlLCBjb2xzPTEsIGxheW91dD1OVUxMKSB7DQogIGxpYnJhcnkoZ3JpZCkNCiAgDQogICMgTWFrZSBhIGxpc3QgZnJvbSB0aGUgLi4uIGFyZ3VtZW50cyBhbmQgcGxvdGxpc3QNCiAgcGxvdHMgPC0gYyhsaXN0KC4uLiksIHBsb3RsaXN0KQ0KICANCiAgbnVtUGxvdHMgPSBsZW5ndGgocGxvdHMpDQogIA0KICAjIElmIGxheW91dCBpcyBOVUxMLCB0aGVuIHVzZSAnY29scycgdG8gZGV0ZXJtaW5lIGxheW91dA0KICBpZiAoaXMubnVsbChsYXlvdXQpKSB7DQogICAgIyBNYWtlIHRoZSBwYW5lbA0KICAgICMgbmNvbDogTnVtYmVyIG9mIGNvbHVtbnMgb2YgcGxvdHMNCiAgICAjIG5yb3c6IE51bWJlciBvZiByb3dzIG5lZWRlZCwgY2FsY3VsYXRlZCBmcm9tICMgb2YgY29scw0KICAgIGxheW91dCA8LSBtYXRyaXgoc2VxKDEsIGNvbHMgKiBjZWlsaW5nKG51bVBsb3RzL2NvbHMpKSwNCiAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSBjb2xzLCBucm93ID0gY2VpbGluZyhudW1QbG90cy9jb2xzKSkNCiAgfQ0KICANCiAgaWYgKG51bVBsb3RzPT0xKSB7DQogICAgcHJpbnQocGxvdHNbWzFdXSkNCiAgICANCiAgfSBlbHNlIHsNCiAgICAjIFNldCB1cCB0aGUgcGFnZQ0KICAgIGdyaWQubmV3cGFnZSgpDQogICAgcHVzaFZpZXdwb3J0KHZpZXdwb3J0KGxheW91dCA9IGdyaWQubGF5b3V0KG5yb3cobGF5b3V0KSwgbmNvbChsYXlvdXQpKSkpDQogICAgDQogICAgIyBNYWtlIGVhY2ggcGxvdCwgaW4gdGhlIGNvcnJlY3QgbG9jYXRpb24NCiAgICBmb3IgKGkgaW4gMTpudW1QbG90cykgew0KICAgICAgIyBHZXQgdGhlIGksaiBtYXRyaXggcG9zaXRpb25zIG9mIHRoZSByZWdpb25zIHRoYXQgY29udGFpbiB0aGlzIHN1YnBsb3QNCiAgICAgIG1hdGNoaWR4IDwtIGFzLmRhdGEuZnJhbWUod2hpY2gobGF5b3V0ID09IGksIGFyci5pbmQgPSBUUlVFKSkNCiAgICAgIA0KICAgICAgcHJpbnQocGxvdHNbW2ldXSwgdnAgPSB2aWV3cG9ydChsYXlvdXQucG9zLnJvdyA9IG1hdGNoaWR4JHJvdywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0LnBvcy5jb2wgPSBtYXRjaGlkeCRjb2wpKQ0KICAgIH0NCiAgfQ0KfQ0KYGBgDQoNCg0KSW4gdGhlIGZvbGxvd2luZyBncmFwaHMgd2UgY2FuIHNlZSBhIGRlbnNpdHkgcGxvdCBwZXIgdmFyYWJsZSB0YWtpbmcgaW50byBhY2NvdW50IHRoZSBudW1iZXIgaXQgcmVwcmVzZW50cy4NCmBgYHtyIGdyYXBoLCBlY2hvPUZBTFNFLCBmaWcuc2hvdz0nYW5pbWF0ZScsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGFuaW9wdHM9ImNvbnRyb2xzIiwgaW50ZXJ2YWw9MC4yfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KcDE8LWdncGxvdChkYXRhPXRyYWluX2ZpbmFsLCBhZXMoeD1sYWJlbCwgeT1IMzBfbSwgZmlsbD1sYWJlbCkpICsgZ2VvbV9ib3hwbG90KCkrdGhlbWVfYncoKSsgZ3VpZGVzKGZpbGw9RkFMU0UpDQpwMjwtZ2dwbG90KGRhdGE9dHJhaW5fZmluYWwsIGFlcyh4PWxhYmVsLCB5PUg1MF9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnAzPC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9SDgwX20sIGZpbGw9bGFiZWwpKSArIGdlb21fYm94cGxvdCgpK3RoZW1lX2J3KCkrIGd1aWRlcyhmaWxsPUZBTFNFKQ0KcDQ8LWdncGxvdChkYXRhPXRyYWluX2ZpbmFsLCBhZXMoeD1sYWJlbCwgeT1WMzBfbSwgZmlsbD1sYWJlbCkpICsgZ2VvbV9ib3hwbG90KCkrdGhlbWVfYncoKSsgZ3VpZGVzKGZpbGw9RkFMU0UpDQpwNTwtZ2dwbG90KGRhdGE9dHJhaW5fZmluYWwsIGFlcyh4PWxhYmVsLCB5PVY1MF9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnA2PC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9VjgwX20sIGZpbGw9bGFiZWwpKSArIGdlb21fYm94cGxvdCgpK3RoZW1lX2J3KCkrIGd1aWRlcyhmaWxsPUZBTFNFKQ0KcDc8LWdncGxvdChkYXRhPXRyYWluX2ZpbmFsLCBhZXMoeD1sYWJlbCwgeT1Ic3ltX20sIGZpbGw9bGFiZWwpKSArIGdlb21fYm94cGxvdCgpK3RoZW1lX2J3KCkrIGd1aWRlcyhmaWxsPUZBTFNFKQ0KcDg8LWdncGxvdChkYXRhPXRyYWluX2ZpbmFsLCBhZXMoeD1sYWJlbCwgeT1Wc3ltX20sIGZpbGw9bGFiZWwpKSArIGdlb21fYm94cGxvdCgpK3RoZW1lX2J3KCkrIGd1aWRlcyhmaWxsPUZBTFNFKQ0KcDk8LWdncGxvdChkYXRhPXRyYWluX2ZpbmFsLCBhZXMoeD1sYWJlbCwgeT1DTkNQX20sIGZpbGw9bGFiZWwpKSArIGdlb21fYm94cGxvdCgpK3RoZW1lX2J3KCkrIGd1aWRlcyhmaWxsPUZBTFNFKQ0KcDEwPC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9UEMxLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnAxMTwtZ2dwbG90KGRhdGE9dHJhaW5fZmluYWwsIGFlcyh4PWxhYmVsLCB5PVBDMiwgZmlsbD1sYWJlbCkpICsgZ2VvbV9ib3hwbG90KCkrdGhlbWVfYncoKSsgZ3VpZGVzKGZpbGw9RkFMU0UpDQpwMTI8LWdncGxvdChkYXRhPXRyYWluX2ZpbmFsLCBhZXMoeD1sYWJlbCwgeT1QQzMsIGZpbGw9bGFiZWwpKSArIGdlb21fYm94cGxvdCgpK3RoZW1lX2J3KCkrIGd1aWRlcyhmaWxsPUZBTFNFKQ0KDQoNCmxheW91dCA8LSBtYXRyaXgoYygxLCAyLCAzLCA0LCA1LCA2KSwgbnJvdyA9IDIsIGJ5cm93ID0gVFJVRSkNCg0KbXVsdGlwbG90KHAxLCBwMiwgcDMsIHA0LHA1LHA2LCBsYXlvdXQgPSBsYXlvdXQpDQoNCm11bHRpcGxvdChwNyxwOCxwOSxwMTAscDExLHAxMixsYXlvdXQgPSBsYXlvdXQpDQoNCnAwPC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUMF9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnAxPC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUMV9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnAyPC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUMl9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnAzPC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUM19tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnA0PC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUNF9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnA1PC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUNV9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnA2PC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUNl9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnA3PC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUN19tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnA4PC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUOF9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCnA5PC1nZ3Bsb3QoZGF0YT10cmFpbl9maW5hbCwgYWVzKHg9bGFiZWwsIHk9V0hUOV9tLCBmaWxsPWxhYmVsKSkgKyBnZW9tX2JveHBsb3QoKSt0aGVtZV9idygpKyBndWlkZXMoZmlsbD1GQUxTRSkNCmxheW91dCA8LSBtYXRyaXgoYygxLCAyLCAzLCA0LCA1LCA2ICksIG5yb3cgPSAyLCBieXJvdyA9IFRSVUUpDQptdWx0aXBsb3QocDAscDEsIHAyLCBwMywgcDQscDUsIGxheW91dCA9IGxheW91dCkNCmxheW91dCA8LSBtYXRyaXgoYygxLCAyLCAzLCA0ICksIG5yb3cgPSAyLCBieXJvdyA9IFRSVUUpDQptdWx0aXBsb3QocDYscDcscDgscDksbGF5b3V0PWxheW91dCkNCmBgYA0KDQoNCiNUcmFpbiBhbmQgVGVzdCMNCg0KSW4gdGhpcyBzdGVwIHdlIHNjYWxlIGFsbCB0aGUgdmFyaWFibGVzIGFuZCB3ZSBjZW50ZXIgdGhlbSBzbyB0aGF0IHRoZXJlIGlzIG5vIHdlaWdodCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlbSwgc28gdGhhdCBhbGwgdmFyaWFibGVzIGhhdmUgdGhlIHNhbWUgaW5pdGlhbCBpbXBvcnRhbmNlLiBUaGlzIGlzIGEga2V5IHN0ZXAgZm9yIHRoZSBuZXVyYWwgbmV0d29yayB0cmFpbmluZyB0byBzdWNjZWVkLiBXZSBhbHNvIGNyZWF0ZSB0cmFpbiBhbmQgdGVzdCBwYXJ0aXRpb25zIG9mIHRoZSBkYXRhLiANCg0KSSBzaG93IGEgY291cGxlIG9mIHJvd3MgdG8gc2VlIHRoZSBmaW5hbCBEYXRhIHNldCBpbiBhY3Rpb24uDQoNCmBgYHtyIHRyYWluLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShjYXJldCkNCg0KcHJlT2JqIDwtIHByZVByb2Nlc3MoeD10cmFpbl9maW5hbCwgbWV0aG9kPWMoImNlbnRlciIsICJzY2FsZSIpKQ0KdHJhaW5JbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKHRyYWluX2ZpbmFsJGxhYmVsLCBwPS43LCBsaXN0PUYpDQoNCm51bWJlcnMudHJhaW50b3RhbDwtcHJlZGljdChwcmVPYmosdHJhaW5fZmluYWwpDQpudW1iZXJzLnRyYWluPC0gcHJlZGljdChwcmVPYmosIHRyYWluX2ZpbmFsW3RyYWluSW5kZXgsIF0pDQpudW1iZXJzLnRlc3QgICAgIDwtIHByZWRpY3QocHJlT2JqLCB0cmFpbl9maW5hbFstdHJhaW5JbmRleCwgXSkNCg0KbnVtYmVycy50cmFpbnRvdGFsWzE6NSxdDQpgYGANCg0KI05ldXJhbCBOZXR3b3JrIw0KSW4gdGhpcyBzdGVwIHdlIHRyYWluIHRoZSBOZXVyYWwgbmV0d29yay4NCg0KRm9yIHRoaXMgc3BlY2lmaWMgdHJhaW5pbmcgc2V0LCBJJ3ZlIGNob29zZW4gYSBuZXVyYWwgbmV0d29yayB3aXRoIDEgaGlkZGVuIGxheWVyIHdpdGggMjIgbm9kZXMuDQpUaGlzIGVsZWN0aW9uIHdhcyBkb25lIGFmdGVyIHRlc3RpbmcgdGhlIG5uZXQgd2l0aCBhIHNldmVyYWwgZGlmZXJlbnQgbnVtYmVycyBvZiBub2RlcyBmb3IgdGhlIGhpZGRlbiBsYXllci4gSSd2ZSB0cmllZCB3aXRoIDEwIG5vZGVzLCB0byAyOCBhbmQgdGhlIGJlc3QgYWNjdXJhY3kgZm9yIHRoZSBtb2RlbCB3YXMgb2J0ZWluZWQgd2l0aCAyNCBub2Rlcy4NCg0KIyNNb2RlbCBSZXN1bHRzOiMjDQoNCmBgYHtyIE1vZGVsLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCm15LmdyaWQ2IDwtIGV4cGFuZC5ncmlkKC5kZWNheSA9IGMoMC41KSwgLnNpemUgPSBjKDIyKSkNCnRyYWluX2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZD0icmVwZWF0ZWRjdiIsIG51bWJlcj0xMCwgcmVwZWF0cz0zKQ0KbnVtYmVycy5maXQ2IDwtIHRyYWluKGxhYmVsIH4gLiwgZGF0YSA9IG51bWJlcnMudHJhaW50b3RhbCxtZXRob2QgPSAibm5ldCIsIG1heGl0ID0gMTAwMCwgdHJDb250b3I9dHJhaW5fY29udHJvbCx0dW5lR3JpZCA9IG15LmdyaWQ2LCB0cmFjZSA9IEYsIGxpbm91dCA9IDEpIA0KDQoja2FibGUobnVtYmVycy5maXQ2JHJlc3VsdHMpDQpgYGANClBsb3QgTmV1cmFsIE5ldHdvcmsNCg0KYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmxpYnJhcnkoZGV2dG9vbHMpDQpzb3VyY2VfdXJsKCdodHRwczovL2dpc3QuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1BlcXVlLzQxYTllMjBkNjY4N2YyZjMxMDhkL3Jhdy84NWUxNGYzYTI5MmUxMjZmMTQ1NDg2NDQyN2UzYTE4OWMyZmUzM2YzL25uZXRfcGxvdF91cGRhdGUucicpDQoNCiNwbG90IGVhY2ggbW9kZWwNCg0KDQpwbG90Lm5uZXQobnVtYmVycy5maXQ2LGFsbC5vdXQgPSBGLCBhbHBoYS52YWwgPSAwLjMsIGNpcmNsZS5jb2wgPSBsaXN0KCdsaWdodGdyYXknLCAnd2hpdGUnKSwgYm9yZC5jb2wgPSAnYmxhY2snLGNpcmNsZS5jZXggPSAxLGxpbm91dD1ULGNleC52YWwgPSAwLjQscG9zLmNvbD0iYmx1ZSIsbmVnLmNvbD0ib3JhbmdlIixyZWwucnNjPTIsbWF4LnNwPTApDQoNCmBgYA0KDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbnVtYmVycy5maXQ2DQpgYGANCg0KI1Rlc3QgU2V0IFByZWRpY3Rpb25zIHRhYmxlIw0KYGBge3IgcHJlZGljdGlvbnMsIGV2YWw9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9RkFMU0V9DQoNCg0KbGlicmFyeShrbml0cikNCmZpcnN0PC1wcmVkaWN0KG51bWJlcnMuZml0NixudW1iZXJzLnRlc3QpDQpkaTwtdGFibGUoZmlyc3QsbnVtYmVycy50ZXN0JGxhYmVsKQ0KYWNjdXJhY3k8LXN1bShkaWFnKGRpKSkvc3VtKG5yb3cobnVtYmVycy50ZXN0KSkNCmBgYA0KRm9yIHRoZSB0ZXN0IHNldCB3ZSBoYXZlIGEgX19gciByb3VuZChhY2N1cmFjeSw0KSAqMTAwYF9fICUgcHJlZGljdGlvbiBhY2N1cmFjeS4NCg0KSGVyZSB3ZSBzaG93IHRoZSBjb25mdXNpb24gbWF0cml4IGZvciB0aGUgdGVzdCBzZXQuDQpgYGB7ciBjb25mdXNpb24sIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCmNvbHVtIDwtIGMoJzAnLCAnMScsICcyJywgJzMnLCAnNCcsICc1JywgJzYnLCAnNycsICc4JywgJzknICkNCnJvIDwtIGMoJzAnLCAnMScsICcyJywgJzMnLCAnNCcsICc1JywgJzYnLCAnNycsICc4JywgJzknKQ0KZGYgPC0gZXhwYW5kLmdyaWQoY29sdW0sIHJvKQ0KZGYkdmFsdWUgPC0gYyh0YWJsZShmaXJzdCxudW1iZXJzLnRlc3QkbGFiZWwpICkNCg0KZyA8LSBnZ3Bsb3QoZGYsIGFlcyhWYXIxLCBWYXIyKSkgKyBnZW9tX3BvaW50KGFlcyhzaXplID0gdmFsdWUpLCBjb2xvdXIgPSAiZ3JleSIpICsgdGhlbWVfYncoKSArIHhsYWIoIlByZWRpY3RlZCIpICsgeWxhYigiT3JpZ2luYWwiKQ0KZyArIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZT1jKDIsMjApKSArIGdlb21fdGV4dChhZXMobGFiZWwgPSB2YWx1ZSkpDQpgYGANCg0KIyNHb29kIHByZWRpdGlvbnMjIw0KSSdsbCBsaWtlIHRvIHNob3cgc29tZSBpbWFnZXMgb2YgZ29vZCBwcmVkaWN0aW9uczoNCg0KYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpidWVuYXM8LW51bWJlcnMudGVzdCRsYWJlbD09Zmlyc3QNCnBhcihtZnJvdz1jKDUsMTApLG1hcj1jKC4xLC4xLC4xLC4xKSkNCmk9MA0Kaj0wDQpwcmVkaWN0aW9uPC1OVUxMDQp3aGlsZSAoaTw1MCkNCnsNCiAgajwtaisxDQogIGlmIChudW1iZXJzLnRyYWludG90YWxbaixdJGxhYmVsPT1maXJzdFtqXSkgew0KICAgICAgaW1hZ2Uob3JpZ2luYWxbW2pdXSwgYXhlcz1GQUxTRSwgY29sPWdyZXkuY29sb3JzKDEwKSkNCiAgICAgIGk8LWkrMQ0KICB9Ow0KfQ0KDQpgYGANCg0KIyNCYWQgcHJlZGl0aW9ucyMjDQpBbmQgYWxzbyBzb21lIHBpY3R1cmVzIG9mIGJhZCBwcmVkaWN0ZWQgY2FyYWN0ZXJzIHdpdGggdGhlIHByZWRpY3RlZCBjaGFyYWN0ZXIgaW4gbGl0dGxlIGJsYWNrIGZvbnQuDQoNCmBgYHtyLCBlY2hvPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcGFyKG1mcm93PWMoNSwxMCksbWFyPWMoLjEsMCwxLjQsLjEpKQ0KaT0wDQpqPTANCnByZWRpY3Rpb248LU5VTEwNCg0KI3N1bW1hcnkobnVtYmVycy50ZXN0WzEsXSRsYWJlbCkNCndoaWxlIChpPDUwKQ0Kew0KICBqPC1qKzENCiAgaWYgKG51bWJlcnMudHJhaW50b3RhbFtqLF0kbGFiZWwhPWZpcnN0W2pdKSB7DQogICAgICBpbWFnZShvcmlnaW5hbFtbal1dLCBheGVzPUZBTFNFLCBjb2w9Z3JleS5jb2xvcnMoMTApKQ0KICAgICAgdGl0bGUobWFpbiA9IGZpcnN0W2pdLCBmb250Lm1haW4gPSAzICwgY29sLm1haW4gPSAiYmxhY2siKQ0KICAgICAgDQogICAgICBpPC1pKzENCiAgfTsNCn0NCg0KDQpgYGANCkFzIHdlIGNhbiBzZWUgaW4gdGhlIHByZXZpdW9zIGdyYXBoLCB0aGUgYmFkbHkgcHJlZGljdGVkIGNoYXJhY3RlcnMgYXJlIG5vdCBlYXN5IHRvIGRpc3Rpbmd1aXNoIGFuZCBldmVuIGZvciBhIHBlcnNvbiBpdHMgaGFyZCB0byByZWNvZ25pemUuIA0K