The goal of thinning algorithms is to take a binary image and draw a 1 pixel wide skeleton of that image while retaining the shape and structure of the full image.
The Zhang-Suen Thinning algorithm is probably the most used thinning algorithm. Devised in 1984, the algorithm is what is called a 2-pass algorithm, meaning that for each iteration it performs two sets of checks to remove pixels from the image. The checks are devised so that the first set removes from the south east (bottom right) corner of the image, and the second set removes from the north west (top left) corner. Before we define the checks, we’ll make a couple of definitions.
A(i,j) = the number of transitions from white to black, in the sequence of the eight neighbors around pixel (i,j), where the sequence starts and ends at the same neighbor (making a complete circle)
B(i,j) = the number of black pixels among the eight neighbors around pixel (i,j).
Mark a pixel for removal if it satisfies all of the following conditions:
Pass 1
- The pixel is black and has eight neighbours
- 2 <= B(i,j) <= 6
- A(i,j) = 1
- At least one of the north, east, and south neighbors is white
- At least one of east, south, and west neighbors is white
Pass 2
- The pixel is black and has eight neighbours
- 2 <= B(i,j) <= 6
- A(i,j) = 1
- At least one of the north, east, and west neighbors is white
- At least one of north, south, and west neighbors is white
Notice that only steps 3 and 4 change between passes. If a pixel is chosen for removal by either Pass 1 or Pass 2, then it is removed. These passes are both repeated until there is not a pixel chosen for removal by either.
Implementation of the Algorithm
The algorithm is designed for parallelization, which I haven’t done, but I have made modifications to make the run time as fast as I could without parallelization.
Naive Implementation
A naive implementation of the algorithm is to iterate over every pixel for each pass of the algorithm. In the simplest terms, this is how the algorithm is designed, but there is no need to ever do this.
One Step Up
Noticing that Step (0) in both passes allows only black pixels to be looked at we can, right off the bat, narrow our set of pixels to check down to the pixels that are black in the original image. This presents a considerable speed up, as we aren’t doing calculations on white space in the images.
More than just looking at pixels that are black in the original image, we can look at only pixels that are black in the current state of the thinned image. This means that as the image gets thinned, the iterations get faster, which is great! This version of the algorithm is what is currently implemented by almost everyone that claims to have a `fast’ iteration of the algorithm.
My Implementation
My implementation is uniformly better than the previously described algorithm. It provides identical results, and performs the same exact checks, but does a better job at choosing where to look. The first iteration is the same. We check every black pixel in the image, which has to be done. After the first iteration, though, we do better in every subsequent iteration. After the first iteration, a pixel only needs to be looked at if one of its neighbors was previously removed. If a neighbor wasn’t removed, and the pixel is still black, then nothing has changed around it, so it will stay black. This results in not only us having to look at all black pixels of the thin image, but us having to look at only the parts of the image that are still being thinned. Once a region of the image is thinned adequately, we no longer even look at that section. This drastically reduces the computations necessary at the tail end of the algorithm when small regions are still being thinned, but most of the image is done.
Simulation Checks
In actuality, the first cases of the algorithm are special cases of the code that I wrote, meaning that I will only have to change 1 line to recreate them. This is great for the timing of the algorithm, as we are comparing apples to nearly identical apples. I am choosing to output a lot of information here, so that we can look at what is being done by the algorithms.
We will be thinning on a handwritten cursive word, namely, London. The london image is a 148 x 481 pixel iamge. The time to thin is reasonable, which is good because the Naive implementation is slow. The image is shown below.
* installing *source* package ‘handwriter’ ...
** R
** data
*** moving datasets to lazyload DB
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
* DONE (handwriter)

Naive Implementation
Iteration 1 done: 3280 changes.
Iteration Time: 11.46619
Iteration 2 done: 3238 changes.
Iteration Time: 11.5177
Iteration 3 done: 3108 changes.
Iteration Time: 11.69531
Iteration 4 done: 1994 changes.
Iteration Time: 12.11762
Iteration 5 done: 732 changes.
Iteration Time: 11.62585
Iteration 6 done: 186 changes.
Iteration Time: 11.58064
Iteration 7 done: 44 changes.
Iteration Time: 11.60776
Iteration 8 done: 3 changes.
Iteration Time: 11.58745
Iteration 9 done: 0 changes.
Iteration Time: 11.58955
------------------------
Total Run Time: 104.796
------------------------
Notice in the output above that every iteration takes about 11 or so seconds. This is the amount of time that it takes to check every pixel in the image. A better algorithm will get faster as the image gets cut down.

Better but not great implementation
Iteration 1 done: 3280 changes.
Iteration Time: 2.294975
Iteration 2 done: 3238 changes.
Iteration Time: 1.832447
Iteration 3 done: 3108 changes.
Iteration Time: 1.264412
Iteration 4 done: 1994 changes.
Iteration Time: 0.768136
Iteration 5 done: 732 changes.
Iteration Time: 0.422735
Iteration 6 done: 186 changes.
Iteration Time: 0.27233
Iteration 7 done: 44 changes.
Iteration Time: 0.2565901
Iteration 8 done: 3 changes.
Iteration Time: 0.2250042
Iteration 9 done: 0 changes.
Iteration Time: 0.228173
------------------------
Total Run Time: 7.571932
------------------------
My Implementation
It’s important to note that each of these algorithms is performing the exact same procedure, down to the pixel. There is no randomness involved, so we can verify by looking at the number of changes made that each of the speeds is returning the exact same thing.
Iteration 1 done: 3280 changes.
Left to check: 9791
Iteration Time: 2.454494
Iteration 2 done: 3238 changes.
Left to check: 9697
Iteration Time: 0.5625272
Iteration 3 done: 3108 changes.
Left to check: 8984
Iteration Time: 0.569011
Iteration 4 done: 1994 changes.
Left to check: 6085
Iteration Time: 0.471854
Iteration 5 done: 732 changes.
Left to check: 2353
Iteration Time: 0.362812
Iteration 6 done: 186 changes.
Left to check: 662
Iteration Time: 0.1182289
Iteration 7 done: 44 changes.
Left to check: 150
Iteration Time: 0.03037906
Iteration 8 done: 3 changes.
Left to check: 27
Iteration Time: 0.008361101
Iteration 9 done: 0 changes.
Left to check: 0
Iteration Time: 0.003755093
------------------------
Total Run Time: 4.587909
------------------------
Lets look at a bigger image (but just for the last 2 implementations)
We’ll thin the following paragraph. It is a 1262 by 1162 image.

Better than naive, but not good implementation
The thing to notice about this algorithm is at the end, when there are only double digit changes to be made, there is a lower bound to how fast the algorithm can run. About 10 seconds for this image. For my implementation, these stages will run more quickly.
Iteration 1 done: 57146 changes.
Iteration Time: 39.17735
Iteration 2 done: 14800 changes.
Iteration Time: 15.52687
Iteration 3 done: 585 changes.
Iteration Time: 9.433958
My Implementation
The first iteration is about the same for the two methods, we have to look at all of the dark pixels. Subsequent iterations will be smaller, especially the iterations when a small number of changes is made. We no longer have to look at the parts of the message that have already been thinned, just the parts that are actively being thinned. This results in a substantial speed boost here (about 2 times), and will get even faster as the image size increases.
Iteration 1 done: 57146 changes.
Left to check: 155357
Iteration Time: 36.96026
Iteration 2 done: 14795 changes.
Left to check: 58542
Iteration Time: 13.63521
Iteration 3 done: 585 changes.
Left to check: 3195
Iteration Time: 5.383498
Iteration 4 done: 44 changes.
Left to check: 299
Iteration Time: 0.3652899
Iteration 5 done: 6 changes.
Left to check: 42
Iteration Time: 0.04328609
Iteration 6 done: 2 changes.
Left to check: 12
Iteration Time: 0.01724195
Iteration 7 done: 0 changes.
Left to check: 0
Iteration Time: 0.0277741
------------------------
Total Run Time: 56.44643
------------------------
So, there is already a substantial speed increase. I also think that a large part of the run time (the first iteration) could be eliminated if I picked a more creative starting set to look at. Something like, look at every point that has at least 2 and not more than 6 neighbors. This would only check that condition for all black points, but not remove any, and then would narrow down the set for when we need to check every condition. We could reduce the long iteration time a couple times over by doing this.
LS0tCnRpdGxlOiAiWmhhbmctU3VlbiBUaGlubmluZyBBbGdvcml0aG0iCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgpUaGUgZ29hbCBvZiB0aGlubmluZyBhbGdvcml0aG1zIGlzIHRvIHRha2UgYSBiaW5hcnkgaW1hZ2UgYW5kIGRyYXcgYSAxIHBpeGVsIHdpZGUgc2tlbGV0b24gb2YgdGhhdCBpbWFnZSB3aGlsZSByZXRhaW5pbmcgdGhlIHNoYXBlIGFuZCBzdHJ1Y3R1cmUgb2YgdGhlIGZ1bGwgaW1hZ2UuCgpUaGUgWmhhbmctU3VlbiBUaGlubmluZyBhbGdvcml0aG0gaXMgcHJvYmFibHkgdGhlIG1vc3QgdXNlZCB0aGlubmluZyBhbGdvcml0aG0uIERldmlzZWQgaW4gMTk4NCwgdGhlIGFsZ29yaXRobSBpcyB3aGF0IGlzIGNhbGxlZCBhIDItcGFzcyBhbGdvcml0aG0sIG1lYW5pbmcgdGhhdCBmb3IgZWFjaCBpdGVyYXRpb24gaXQgcGVyZm9ybXMgdHdvIHNldHMgb2YgY2hlY2tzIHRvIHJlbW92ZSBwaXhlbHMgZnJvbSB0aGUgaW1hZ2UuIFRoZSBjaGVja3MgYXJlIGRldmlzZWQgc28gdGhhdCB0aGUgZmlyc3Qgc2V0IHJlbW92ZXMgZnJvbSB0aGUgc291dGggZWFzdCAoYm90dG9tIHJpZ2h0KSBjb3JuZXIgb2YgdGhlIGltYWdlLCBhbmQgdGhlIHNlY29uZCBzZXQgcmVtb3ZlcyBmcm9tIHRoZSBub3J0aCB3ZXN0ICh0b3AgbGVmdCkgY29ybmVyLiBCZWZvcmUgd2UgZGVmaW5lIHRoZSBjaGVja3MsIHdlJ2xsIG1ha2UgYSBjb3VwbGUgb2YgZGVmaW5pdGlvbnMuCgo+IEEoaSxqKSA9IHRoZSBudW1iZXIgb2YgdHJhbnNpdGlvbnMgZnJvbSB3aGl0ZSB0byBibGFjaywgaW4gdGhlIHNlcXVlbmNlIG9mIHRoZSBlaWdodCBuZWlnaGJvcnMgYXJvdW5kIHBpeGVsIChpLGopLCB3aGVyZSB0aGUgc2VxdWVuY2Ugc3RhcnRzIGFuZCBlbmRzIGF0IHRoZSBzYW1lIG5laWdoYm9yIChtYWtpbmcgYSBjb21wbGV0ZSBjaXJjbGUpCgo+IEIoaSxqKSA9IHRoZSBudW1iZXIgb2YgYmxhY2sgcGl4ZWxzIGFtb25nIHRoZSBlaWdodCBuZWlnaGJvcnMgYXJvdW5kIHBpeGVsIChpLGopLgoKTWFyayBhIHBpeGVsIGZvciByZW1vdmFsIGlmIGl0IHNhdGlzZmllcyBhbGwgb2YgdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOgoKIyMjIyBQYXNzIDEKMC4gVGhlIHBpeGVsIGlzIGJsYWNrIGFuZCBoYXMgZWlnaHQgbmVpZ2hib3VycwoxLiAyIDw9IEIoaSxqKSA8PSA2CjIuIEEoaSxqKSA9IDEKMy4gQXQgbGVhc3Qgb25lIG9mIHRoZSBub3J0aCwgZWFzdCwgYW5kIHNvdXRoIG5laWdoYm9ycyBpcyAqd2hpdGUqCjQuIEF0IGxlYXN0IG9uZSBvZiBlYXN0LCBzb3V0aCwgYW5kIHdlc3QgbmVpZ2hib3JzIGlzICp3aGl0ZSoKCiMjIyMgUGFzcyAyCgowLiBUaGUgcGl4ZWwgaXMgYmxhY2sgYW5kIGhhcyBlaWdodCBuZWlnaGJvdXJzCjEuIDIgPD0gQihpLGopIDw9IDYKMi4gQShpLGopID0gMQozLiBBdCBsZWFzdCBvbmUgb2YgdGhlIG5vcnRoLCBlYXN0LCBhbmQgd2VzdCBuZWlnaGJvcnMgaXMgKndoaXRlKgo0LiBBdCBsZWFzdCBvbmUgb2Ygbm9ydGgsIHNvdXRoLCBhbmQgd2VzdCBuZWlnaGJvcnMgaXMgKndoaXRlKgoKKioqCk5vdGljZSB0aGF0IG9ubHkgc3RlcHMgMyBhbmQgNCBjaGFuZ2UgYmV0d2VlbiBwYXNzZXMuIElmIGEgcGl4ZWwgaXMgY2hvc2VuIGZvciByZW1vdmFsIGJ5IGVpdGhlciBQYXNzIDEgb3IgUGFzcyAyLCB0aGVuIGl0IGlzIHJlbW92ZWQuIFRoZXNlIHBhc3NlcyBhcmUgYm90aCByZXBlYXRlZCB1bnRpbCB0aGVyZSBpcyBub3QgYSBwaXhlbCBjaG9zZW4gZm9yIHJlbW92YWwgYnkgZWl0aGVyLgoKIyMjIEltcGxlbWVudGF0aW9uIG9mIHRoZSBBbGdvcml0aG0KClRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQgZm9yIHBhcmFsbGVsaXphdGlvbiwgd2hpY2ggSSBoYXZlbid0IGRvbmUsIGJ1dCBJIGhhdmUgbWFkZSBtb2RpZmljYXRpb25zIHRvIG1ha2UgdGhlIHJ1biB0aW1lIGFzIGZhc3QgYXMgSSBjb3VsZCB3aXRob3V0IHBhcmFsbGVsaXphdGlvbi4KCiMjIyMgTmFpdmUgSW1wbGVtZW50YXRpb24KCkEgbmFpdmUgaW1wbGVtZW50YXRpb24gb2YgdGhlIGFsZ29yaXRobSBpcyB0byBpdGVyYXRlIG92ZXIgZXZlcnkgcGl4ZWwgZm9yIGVhY2ggcGFzcyBvZiB0aGUgYWxnb3JpdGhtLiBJbiB0aGUgc2ltcGxlc3QgdGVybXMsIHRoaXMgaXMgaG93IHRoZSBhbGdvcml0aG0gaXMgZGVzaWduZWQsIGJ1dCB0aGVyZSBpcyBubyBuZWVkIHRvIGV2ZXIgZG8gdGhpcy4KCiMjIyMgT25lIFN0ZXAgVXAKCk5vdGljaW5nIHRoYXQgU3RlcCAoMCkgaW4gYm90aCBwYXNzZXMgYWxsb3dzIG9ubHkgYmxhY2sgcGl4ZWxzIHRvIGJlIGxvb2tlZCBhdCB3ZSBjYW4sIHJpZ2h0IG9mZiB0aGUgYmF0LCBuYXJyb3cgb3VyIHNldCBvZiBwaXhlbHMgdG8gY2hlY2sgZG93biB0byB0aGUgcGl4ZWxzIHRoYXQgYXJlIGJsYWNrIGluIHRoZSBvcmlnaW5hbCBpbWFnZS4gVGhpcyBwcmVzZW50cyBhIGNvbnNpZGVyYWJsZSBzcGVlZCB1cCwgYXMgd2UgYXJlbid0IGRvaW5nIGNhbGN1bGF0aW9ucyBvbiB3aGl0ZSBzcGFjZSBpbiB0aGUgaW1hZ2VzLgoKTW9yZSB0aGFuIGp1c3QgbG9va2luZyBhdCBwaXhlbHMgdGhhdCBhcmUgYmxhY2sgaW4gdGhlIG9yaWdpbmFsIGltYWdlLCB3ZSBjYW4gbG9vayBhdCBvbmx5IHBpeGVscyB0aGF0IGFyZSBibGFjayBpbiB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgdGhpbm5lZCBpbWFnZS4gVGhpcyBtZWFucyB0aGF0IGFzIHRoZSBpbWFnZSBnZXRzIHRoaW5uZWQsIHRoZSBpdGVyYXRpb25zIGdldCBmYXN0ZXIsIHdoaWNoIGlzIGdyZWF0ISBUaGlzIHZlcnNpb24gb2YgdGhlIGFsZ29yaXRobSBpcyB3aGF0IGlzIGN1cnJlbnRseSBpbXBsZW1lbnRlZCBieSBhbG1vc3QgZXZlcnlvbmUgdGhhdCBjbGFpbXMgdG8gaGF2ZSBhIGBmYXN0JyBpdGVyYXRpb24gb2YgdGhlIGFsZ29yaXRobS4KCiMjIyMgTXkgSW1wbGVtZW50YXRpb24KCk15IGltcGxlbWVudGF0aW9uIGlzIHVuaWZvcm1seSBiZXR0ZXIgdGhhbiB0aGUgcHJldmlvdXNseSBkZXNjcmliZWQgYWxnb3JpdGhtLiBJdCBwcm92aWRlcyBpZGVudGljYWwgcmVzdWx0cywgYW5kIHBlcmZvcm1zIHRoZSBzYW1lIGV4YWN0IGNoZWNrcywgYnV0IGRvZXMgYSBiZXR0ZXIgam9iIGF0IGNob29zaW5nIHdoZXJlIHRvIGxvb2suIFRoZSBmaXJzdCBpdGVyYXRpb24gaXMgdGhlIHNhbWUuIFdlIGNoZWNrIGV2ZXJ5IGJsYWNrIHBpeGVsIGluIHRoZSBpbWFnZSwgd2hpY2ggaGFzIHRvIGJlIGRvbmUuIEFmdGVyIHRoZSBmaXJzdCBpdGVyYXRpb24sIHRob3VnaCwgd2UgZG8gYmV0dGVyIGluIGV2ZXJ5IHN1YnNlcXVlbnQgaXRlcmF0aW9uLiBBZnRlciB0aGUgZmlyc3QgaXRlcmF0aW9uLCBhIHBpeGVsIG9ubHkgbmVlZHMgdG8gYmUgbG9va2VkIGF0IGlmIG9uZSBvZiBpdHMgbmVpZ2hib3JzIHdhcyBwcmV2aW91c2x5IHJlbW92ZWQuIElmIGEgbmVpZ2hib3Igd2Fzbid0IHJlbW92ZWQsIGFuZCB0aGUgcGl4ZWwgaXMgc3RpbGwgYmxhY2ssIHRoZW4gbm90aGluZyBoYXMgY2hhbmdlZCBhcm91bmQgaXQsIHNvIGl0IHdpbGwgc3RheSBibGFjay4gVGhpcyByZXN1bHRzIGluIG5vdCBvbmx5IHVzIGhhdmluZyB0byBsb29rIGF0IGFsbCBibGFjayBwaXhlbHMgb2YgdGhlIHRoaW4gaW1hZ2UsIGJ1dCB1cyBoYXZpbmcgdG8gbG9vayBhdCBvbmx5IHRoZSBwYXJ0cyBvZiB0aGUgaW1hZ2UgdGhhdCBhcmUgc3RpbGwgYmVpbmcgdGhpbm5lZC4gT25jZSBhIHJlZ2lvbiBvZiB0aGUgaW1hZ2UgaXMgdGhpbm5lZCBhZGVxdWF0ZWx5LCB3ZSBubyBsb25nZXIgZXZlbiBsb29rIGF0IHRoYXQgc2VjdGlvbi4gVGhpcyBkcmFzdGljYWxseSByZWR1Y2VzIHRoZSBjb21wdXRhdGlvbnMgbmVjZXNzYXJ5IGF0IHRoZSB0YWlsIGVuZCBvZiB0aGUgYWxnb3JpdGhtIHdoZW4gc21hbGwgcmVnaW9ucyBhcmUgc3RpbGwgYmVpbmcgdGhpbm5lZCwgYnV0IG1vc3Qgb2YgdGhlIGltYWdlIGlzIGRvbmUuCgojIyNTaW11bGF0aW9uIENoZWNrcwoKSW4gYWN0dWFsaXR5LCB0aGUgZmlyc3QgY2FzZXMgb2YgdGhlIGFsZ29yaXRobSBhcmUgc3BlY2lhbCBjYXNlcyBvZiB0aGUgY29kZSB0aGF0IEkgd3JvdGUsIG1lYW5pbmcgdGhhdCBJIHdpbGwgb25seSBoYXZlIHRvIGNoYW5nZSAxIGxpbmUgdG8gcmVjcmVhdGUgdGhlbS4gVGhpcyBpcyBncmVhdCBmb3IgdGhlIHRpbWluZyBvZiB0aGUgYWxnb3JpdGhtLCBhcyB3ZSBhcmUgY29tcGFyaW5nIGFwcGxlcyB0byBuZWFybHkgaWRlbnRpY2FsIGFwcGxlcy4gSSBhbSBjaG9vc2luZyB0byBvdXRwdXQgYSBsb3Qgb2YgaW5mb3JtYXRpb24gaGVyZSwgc28gdGhhdCB3ZSBjYW4gbG9vayBhdCB3aGF0IGlzIGJlaW5nIGRvbmUgYnkgdGhlIGFsZ29yaXRobXMuCgpXZSB3aWxsIGJlIHRoaW5uaW5nIG9uIGEgaGFuZHdyaXR0ZW4gY3Vyc2l2ZSB3b3JkLCBuYW1lbHksIExvbmRvbi4gVGhlIGxvbmRvbiBpbWFnZSBpcyBhIDE0OCB4IDQ4MSBwaXhlbCBpYW1nZS4gVGhlIHRpbWUgdG8gdGhpbiBpcyByZWFzb25hYmxlLCB3aGljaCBpcyBnb29kIGJlY2F1c2UgdGhlIE5haXZlIGltcGxlbWVudGF0aW9uIGlzIHNsb3cuIFRoZSBpbWFnZSBpcyBzaG93biBiZWxvdy4KCmBgYHtyIFNob3cgRGF0YSwgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJDU0FGRS1JU1UvaGFuZHdyaXRlciIpCmxpYnJhcnkoaGFuZHdyaXRlcikKZGF0YShsb25kb24pCnBsb3RJbWFnZShsb25kb24pCmBgYApgYGB7ciwgcmVkZWZpbmVJbnRlcm5hbHMsIGVjaG8gPSBGQUxTRX0KbmVpZ2hib3JDaGFuZ2VzID0gZnVuY3Rpb24oY29vcmRzLCBpbWcpCnsKICByciA9IGNvb3Jkc1sxXQogIGNjID0gY29vcmRzWzJdCiAgaWYocnI+MSAmIGNjPjEgJiBycjxkaW0oaW1nKVsxXSAmIGNjPGRpbShpbWcpWzJdKQogIHsKICAgIG5laWdoYnMgPSBjKHQoaW1nWyhyci0xKToocnIrMSksXVssKGNjLTEpOihjYysxKV0pKVtjKDIsMyw2LDksOCw3LDQsMSwyKV0KICAgIHJldHVybihzdW0obmVpZ2hicyA9PSAxICYgYyhuZWlnaGJzWy0xXSwgbmVpZ2hic1sxXSkgPT0gMCkpCiAgfQogIGVsc2UKICAgIHJldHVybigwKQp9CgojJyBudW1OZWlnaGJvcnMKIycKIycgSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoaW5JbWFnZS4gQ291bnRzIG5laWdoYm9ycyB3aXRoIHZhbHVlIG9mIDAuCiMnIEBwYXJhbSBjb29yZHMgUG9pbnQgbG9jYXRpb24uCiMnIEBwYXJhbSBpbWcgSW1hZ2Ugb2JqZWN0LgojJyBAcmV0dXJuIFJldHVybnMgbnVtYmVyIG9mIDAgbmVpZ2hib3JzLgoKbnVtTmVpZ2hib3JzID0gZnVuY3Rpb24oY29vcmRzLCBpbWcpCnsKICByciA9IGNvb3Jkc1sxXQogIGNjID0gY29vcmRzWzJdCiAgaWYocnI+MSAmIGNjPjEgJiBycjxkaW0oaW1nKVsxXSAmIGNjPGRpbShpbWcpWzJdKQogIHsKICAgIG5laWdoYnMgPSBjKHQoaW1nWyhyci0xKToocnIrMSksXVssKGNjLTEpOihjYysxKV0pKVtjKDIsMyw2LDksOCw3LDQsMSldCiAgICByZXR1cm4oc3VtKG5laWdoYnMgPT0gMCkpCiAgfQogIGVsc2UKICAgIHJldHVybigwKQp9CgojJyBuZWlnaGJzMjQ2NDY4CiMnCiMnIEludGVybmFsIGZ1bmN0aW9uIGZvciB0aGluSW1hZ2UuIFJldHVybnMgMSBpZiBvbmUgb2YgdGhlIHBvaW50cyB0byB0aGUgdG9wLCBib3R0b20gYW5kIGxlZnQgQU5EIG9uZSBvZiByaWdodCwgYm90dG9tLCBsZWZ0IGFyZSBub24temVyby4gMCBvdGhlcndpc2UuCiMnIEBwYXJhbSBjb29yZHMgUG9pbnQgbG9jYXRpb24uCiMnIEBwYXJhbSBpbWcgSW1hZ2Ugb2JqZWN0LgojJyBAcmV0dXJuIFJldHVybnMgMSBvciAwIGZvciBjb25kaXRpb24gbWVudGlvbmVkIGFib3ZlLgojJyAKbmVpZ2hiczI0NjQ2OCA9IGZ1bmN0aW9uKGNvb3JkcywgaW1nKQp7CiAgcnIgPSBjb29yZHNbMV0KICBjYyA9IGNvb3Jkc1syXQogIGlmKHJyPjEgJiBjYz4xICYgcnI8ZGltKGltZylbMV0gJiBjYzxkaW0oaW1nKVsyXSkKICB7CiAgICBuZWlnaGJzMjQ2ID0gYyh0KGltZ1socnItMSk6KHJyKzEpLF1bLChjYy0xKTooY2MrMSldKSlbYygyLDYsOCldCiAgICBuZWlnaGJzNDY4ID0gYyh0KGltZ1socnItMSk6KHJyKzEpLF1bLChjYy0xKTooY2MrMSldKSlbYyg0LDYsOCldCiAgICByZXR1cm4oYXMubnVtZXJpYyhhbnkobmVpZ2hiczI0NiA9PSAxKSAmIGFueShuZWlnaGJzNDY4ID09IDEpKSkKICB9CiAgZWxzZQogICAgcmV0dXJuKDApCn0KCiMnIG5laWdoYnMyNDY0NjgKIycKIycgSW50ZXJuYWwgZnVuY3Rpb24gZm9yIHRoaW5JbWFnZS4gUmV0dXJucyAxIGlmIGF0IGxlYXN0IG9uZSBvZiB0aGUgcG9pbnRzIHRvIHRoZSB0b3AsIHJpZ2h0IGFuZCBib3R0b20gQU5EIGF0IGxlYXN0IG9uZSBvZiB0b3AsIHJpZ2h0LCBsZWZ0IGFyZSBub24temVyby4gMCBvdGhlcndpc2UuCiMnIEBwYXJhbSBjb29yZHMgUG9pbnQgbG9jYXRpb24uCiMnIEBwYXJhbSBpbWcgSW1hZ2Ugb2JqZWN0LgojJyBAcmV0dXJuIFJldHVybnMgMSBvciAwIGZvciBjb25kaXRpb24gbWVudGlvbmVkIGFib3ZlLgoKbmVpZ2hiczI0ODI2OCA9IGZ1bmN0aW9uKGNvb3JkcywgaW1nKQp7CiAgcnIgPSBjb29yZHNbMV0KICBjYyA9IGNvb3Jkc1syXQogIGlmKHJyPjEgJiBjYz4xICYgcnI8ZGltKGltZylbMV0gJiBjYzxkaW0oaW1nKVsyXSkKICB7CiAgICBuZWlnaGJzMjQ4ID0gYyh0KGltZ1socnItMSk6KHJyKzEpLF1bLChjYy0xKTooY2MrMSldKSlbYygyLDQsNildCiAgICBuZWlnaGJzMjY4ID0gYyh0KGltZ1socnItMSk6KHJyKzEpLF1bLChjYy0xKTooY2MrMSldKSlbYygyLDQsOCldCiAgICByZXR1cm4oYXMubnVtZXJpYyhhbnkobmVpZ2hiczI0OCA9PSAxKSAmIGFueShuZWlnaGJzMjY4ID09IDEpKSkKICB9CiAgZWxzZQogICAgcmV0dXJuKDApCn0KCiMnIHN0ZXBBCiMnCiMnIEludGVybmFsIGZ1bmN0aW9uIGZvciB0aGluSW1hZ2UuIEZpcnN0IGl0ZXJhdGlvbiBvZiBjaGVja3MgZm9yIHRoaW5uaW5nIGFsZ29yaXRobQojJyBAcGFyYW0gaW1nIEltYWdlIG9iamVjdC4KIycgQHBhcmFtIGNvb3JkcyBMb2NhdGlvbnMgb2YgcG9pbnRzIHRvIGNoZWNrLgojJyBAcmV0dXJuIFRSVUUgb3IgRkFMU0UgZm9yIGVhY2ggcG9pbnQgaW4gY29vcmRzLCBiYXNlZCBvbiBpZiB0aGV5IHBhc3MgdGhlIGNoZWNrcy4KCnN0ZXBBID0gZnVuY3Rpb24oaW1nLCBjb29yZHMpCnsKICBpbWdBID0gbWF0cml4KGFwcGx5KFggPSBjb29yZHMsIE1BUkdJTiA9IDEsIEZVTiA9IG5laWdoYm9yQ2hhbmdlcywgaW1nID0gaW1nKSwgYnlyb3cgPSBGLCBucm93ID0gMSkKICBpbWdCID0gbWF0cml4KGFwcGx5KFggPSBjb29yZHMsIE1BUkdJTiA9IDEsIEZVTiA9IG51bU5laWdoYm9ycywgaW1nID0gaW1nKSwgYnlyb3cgPSBGLCBucm93ID0gMSkKICBpbWcyNDY0NjggPSBtYXRyaXgoYXBwbHkoWCA9IGNvb3JkcywgTUFSR0lOID0gMSwgRlVOID0gIG5laWdoYnMyNDY0NjgsIGltZyA9IGltZyksIGJ5cm93ID0gRiwgbnJvdyA9IDEpCiAgaW1nRmxhZyA9IGFzLm51bWVyaWMoaW1nQiA+PSAyICYgaW1nQiA8PSA2ICYgaW1nQSA9PSAxICYgaW1nMjQ2NDY4ID09IDEpCiAgcmV0dXJuKGltZ0ZsYWcpCn0KCiMnIHN0ZXBBCiMnCiMnIEludGVybmFsIGZ1bmN0aW9uIGZvciB0aGluSW1hZ2UuIFNlY29uZCBpdGVyYXRpb24gb2YgY2hlY2tzIGZvciB0aGlubmluZyBhbGdvcml0aG0KIycgQHBhcmFtIGltZyBJbWFnZSBvYmplY3QuCiMnIEBwYXJhbSBjb29yZHMgTG9jYXRpb25zIG9mIHBvaW50cyB0byBjaGVjay4KIycgQHJldHVybiBUUlVFIG9yIEZBTFNFIGZvciBlYWNoIHBvaW50IGluIGNvb3JkcywgYmFzZWQgb24gaWYgdGhleSBwYXNzIHRoZSBjaGVja3MuCgpzdGVwQiA9IGZ1bmN0aW9uKGltZywgY29vcmRzKQp7CiAgaW1nQSA9IG1hdHJpeChhcHBseShYID0gY29vcmRzLCBNQVJHSU4gPSAxLCBGVU4gPSBuZWlnaGJvckNoYW5nZXMsIGltZyA9IGltZyksIGJ5cm93ID0gRiwgbnJvdyA9IDEpCiAgaW1nQiA9IG1hdHJpeChhcHBseShYID0gY29vcmRzLCBNQVJHSU4gPSAxLCBGVU4gPSBudW1OZWlnaGJvcnMsIGltZyA9IGltZyksIGJ5cm93ID0gRiwgbnJvdyA9IDEpCiAgaW1nMjQ4MjY4ID0gbWF0cml4KGFwcGx5KFggPSBjb29yZHMsIE1BUkdJTiA9IDEsIEZVTiA9ICBuZWlnaGJzMjQ4MjY4LCBpbWcgPSBpbWcpLCBieXJvdyA9IEYsIG5yb3cgPSAxKQogIGltZ0ZsYWcgPSBhcy5udW1lcmljKGltZ0IgPj0gMiAmIGltZ0IgPD0gNiAmIGltZ0EgPT0gMSAmIGltZzI0ODI2OCA9PSAxKQogIHJldHVybihpbWdGbGFnKQp9CmBgYAoKCiMjIyMgTmFpdmUgSW1wbGVtZW50YXRpb24KYGBge3IgTmFpdmVDb2RlLCBpbmNsdWRlPUZBTFNFfQpuYWl2ZVRoaW5uaW5nID0gZnVuY3Rpb24oaW1nLCB2ZXJib3NlID0gRkFMU0UpIAp7CiAgICBmbGFnID0gVFJVRQogICAgaWYgKHZlcmJvc2UpIAogICAgewogICAgICBpdGVyQ291bnQgPSAxCiAgICAgIHRvdGFsLnRpbWUuc3RhcnQgPC0gU3lzLnRpbWUoKQogICAgfQogICAgdGhpbm5lZCA9IGltZwogICAjIGNoYW5nZSA9IHdoaWNoKGltZyA9PSAwKQogICAgd2hpbGUgKGZsYWcgPT0gVFJVRSkgewogICAgICAgIGlmICh2ZXJib3NlICE9IEZBTFNFKSAKICAgICAgICAgICAgc3RhcnQudGltZSA8LSBTeXMudGltZSgpCiAgICAgICAgI2luZGV4ID0gY2hhbmdlW3RoaW5uZWRbY2hhbmdlXSA9PSAwXQogICAgICAgIGluZGV4ID0gMTpwcm9kKGRpbShpbWcpKQogICAgICAgIGltZy5tID0gY2JpbmQoKChpbmRleCAtIDEpJSVkaW0oaW1nKVsxXSkgKyAxLCAoKGluZGV4IC0gMSklLyVkaW0oaW1nKVsxXSkgKyAxKQogICAgICAgIGZsYWdBID0gc3RlcEEodGhpbm5lZCwgaW1nLm0pICYgdGhpbm5lZFtpbmRleF0gPT0gMAogICAgICAgIHRoaW5uZWRbaW5kZXhdID0gaWZlbHNlKGModGhpbm5lZFtpbmRleF0gPT0gMCkgJiBmbGFnQSA9PSAwLCAwLCAxKQogICAgICAgIGZsYWdCID0gc3RlcEIodGhpbm5lZCwgaW1nLm0pICYgdGhpbm5lZFtpbmRleF0gPT0gMAogICAgICAgIHRoaW5uZWRbaW5kZXhdID0gaWZlbHNlKGModGhpbm5lZFtpbmRleF0gPT0gMCkgJiBmbGFnQiA9PSAwLCAwLCAxKQogICAgICAgIGlmIChzdW0oZmxhZ0EgKyBmbGFnQiwgbmEucm0gPSBUKSA9PSAwKSB7CiAgICAgICAgICAgIGZsYWcgPSBGQUxTRQogICAgICAgIH0KICAgICAgICBlbHNlIHsKICAgICAgICAgICAgI2ltYWdlKG1hdHJpeCgxLWFzLm51bWVyaWMoZmxhZ0F8ZmxhZ0IpLCBuY29sID0gZGltKGltZylbMl0pKQogICAgICAgICAgICAjY2hhbmdlID0gaW5kZXhbKGZsYWdBIHwgZmxhZ0IpXQogICAgICAgICAgICAjY2hhbmdlID0gdW5pcXVlKHJlcChjaGFuZ2UsIGVhY2ggPSA5KSArIHJlcChjKDAsIDEsIC0xLCBkaW0oaW1nKVsxXSAtIDEsIGRpbShpbWcpWzFdICsgMSwgLWRpbShpbWcpWzFdICsgMSwgLWRpbShpbWcpWzFdIC0gMSwgZGltKGltZylbMV0sIC1kaW0oaW1nKVsxXSksIGxlbmd0aChjaGFuZ2UpKSkKICAgICAgICAgICAgI2NoYW5nZSA9IGNoYW5nZVtjaGFuZ2UgPj0gMSAmIGNoYW5nZSA8PSBwcm9kKGRpbShpbWcpKV0KICAgICAgICB9CiAgICAgICAgaWYgKHZlcmJvc2UgPT0gVFJVRSkgewogICAgICAgICAgICBjYXQoIlxuSXRlcmF0aW9uIiwgaXRlckNvdW50LCAiZG9uZToiLCBzdW0oZmxhZ0EgfCBmbGFnQiksICJjaGFuZ2VzLiIpCiAgICAgICAgICAgIyBjYXQoIlxuTGVmdCB0byBjaGVjazoiLCBpZmVsc2Uoc3VtKGZsYWdBIHwgZmxhZ0IpICE9IDAsIGxlbmd0aChjaGFuZ2UpLCAwKSkKICAgICAgICAgICAgaXRlckNvdW50ID0gaXRlckNvdW50ICsgMQogICAgICAgIH0KICAgICAgICBmbGFnQVtdID0gMAogICAgICAgIGZsYWdCW10gPSAwCiAgICAgICAgaWYgKHZlcmJvc2UgPT0gVFJVRSkgewogICAgICAgICAgICBlbmQudGltZSA8LSBTeXMudGltZSgpCiAgICAgICAgICAgIGNhdCgiXG5JdGVyYXRpb24gVGltZToiLCBlbmQudGltZSAtIHN0YXJ0LnRpbWUsICJcbiIpCiAgICAgICAgfQogICAgfQogICAgaWYodmVyYm9zZSkKICAgIHsKICAgICAgdG90YWwudGltZS5lbmQgPC0gU3lzLnRpbWUoKQogICAgICBjYXQoIlxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5Ub3RhbCBSdW4gVGltZToiLCBkaWZmdGltZSh0b3RhbC50aW1lLmVuZCwgdG90YWwudGltZS5zdGFydCwgdW5pdHMgPSAic2VjcyIpLCAiXG4tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiIpCiAgICB9CiAgICByZXR1cm4odGhpbm5lZCkKfQpgYGAKCmBgYHtyIE5haXZlUnVuLCBlY2hvPUZBTFNFfQpsb25kb25fbmFpdmUgPSBuYWl2ZVRoaW5uaW5nKGxvbmRvbiwgdmVyYm9zZSA9IFRSVUUpCmBgYAoKTm90aWNlIGluIHRoZSBvdXRwdXQgYWJvdmUgdGhhdCBldmVyeSBpdGVyYXRpb24gdGFrZXMgYWJvdXQgMTEgb3Igc28gc2Vjb25kcy4gVGhpcyBpcyB0aGUgYW1vdW50IG9mIHRpbWUgdGhhdCBpdCB0YWtlcyB0byBjaGVjayBldmVyeSBwaXhlbCBpbiB0aGUgaW1hZ2UuIEEgYmV0dGVyIGFsZ29yaXRobSB3aWxsIGdldCBmYXN0ZXIgYXMgdGhlIGltYWdlIGdldHMgY3V0IGRvd24uCmBgYHtyIHBsb3ROYWl2ZSwgZWNobyA9IEZBTFNFfQpwbG90SW1hZ2VUaGlubmVkKGxvbmRvbiwgbG9uZG9uX25haXZlKQpgYGAKCiMjIyMgQmV0dGVyIGJ1dCBub3QgZ3JlYXQgaW1wbGVtZW50YXRpb24KYGBge3IgT25lU3RlcFVwLCBlY2hvPUZBTFNFfQpiZXR0ZXJUaGlubmluZyA9IGZ1bmN0aW9uKGltZywgdmVyYm9zZSA9IEZBTFNFKSAKewogICAgZmxhZyA9IFRSVUUKICAgIGlmICh2ZXJib3NlKSAKICAgIHsKICAgICAgICBpdGVyQ291bnQgPSAxCiAgICAgICAgdG90YWwudGltZS5zdGFydCA9IFN5cy50aW1lKCkKICAgIH0KICAgIHRoaW5uZWQgPSBpbWcKICAgIyBjaGFuZ2UgPSB3aGljaChpbWcgPT0gMCkKICAgIHdoaWxlIChmbGFnID09IFRSVUUpIHsKICAgICAgICBpZiAodmVyYm9zZSAhPSBGQUxTRSkgCiAgICAgICAgICAgIHN0YXJ0LnRpbWUgPC0gU3lzLnRpbWUoKQogICAgICAgICNpbmRleCA9IGNoYW5nZVt0aGlubmVkW2NoYW5nZV0gPT0gMF0KICAgICAgICBpbmRleCA9IHdoaWNoKHRoaW5uZWQgPT0gMCkKICAgICAgICBpbWcubSA9IGNiaW5kKCgoaW5kZXggLSAxKSUlZGltKGltZylbMV0pICsgMSwgKChpbmRleCAtIDEpJS8lZGltKGltZylbMV0pICsgMSkKICAgICAgICBmbGFnQSA9IHN0ZXBBKHRoaW5uZWQsIGltZy5tKQogICAgICAgIHRoaW5uZWRbaW5kZXhdID0gaWZlbHNlKGModGhpbm5lZFtpbmRleF0gPT0gMCkgJiBmbGFnQSA9PSAwLCAwLCAxKQogICAgICAgIGZsYWdCID0gc3RlcEIodGhpbm5lZCwgaW1nLm0pCiAgICAgICAgdGhpbm5lZFtpbmRleF0gPSBpZmVsc2UoYyh0aGlubmVkW2luZGV4XSA9PSAwKSAmIGZsYWdCID09IDAsIDAsIDEpCiAgICAgICAgaWYgKHN1bShmbGFnQSArIGZsYWdCLCBuYS5ybSA9IFQpID09IDApIHsKICAgICAgICAgICAgZmxhZyA9IEZBTFNFCiAgICAgICAgfQogICAgICAgIGVsc2UgewogICAgICAgICAgICAjaW1hZ2UobWF0cml4KDEtYXMubnVtZXJpYyhmbGFnQXxmbGFnQiksIG5jb2wgPSBkaW0oaW1nKVsyXSkpCiAgICAgICAgICAgICNjaGFuZ2UgPSBpbmRleFsoZmxhZ0EgfCBmbGFnQildCiAgICAgICAgICAgICNjaGFuZ2UgPSB1bmlxdWUocmVwKGNoYW5nZSwgZWFjaCA9IDkpICsgcmVwKGMoMCwgMSwgLTEsIGRpbShpbWcpWzFdIC0gMSwgZGltKGltZylbMV0gKyAxLCAtZGltKGltZylbMV0gKyAxLCAtZGltKGltZylbMV0gLSAxLCBkaW0oaW1nKVsxXSwgLWRpbShpbWcpWzFdKSwgbGVuZ3RoKGNoYW5nZSkpKQogICAgICAgICAgICAjY2hhbmdlID0gY2hhbmdlW2NoYW5nZSA+PSAxICYgY2hhbmdlIDw9IHByb2QoZGltKGltZykpXQogICAgICAgIH0KICAgICAgICBpZiAodmVyYm9zZSA9PSBUUlVFKSB7CiAgICAgICAgICAgIGNhdCgiXG5JdGVyYXRpb24iLCBpdGVyQ291bnQsICJkb25lOiIsIHN1bShmbGFnQSB8IGZsYWdCKSwgImNoYW5nZXMuIikKICAgICAgICAgICAjIGNhdCgiXG5MZWZ0IHRvIGNoZWNrOiIsIGlmZWxzZShzdW0oZmxhZ0EgfCBmbGFnQikgIT0gMCwgbGVuZ3RoKGNoYW5nZSksIDApKQogICAgICAgICAgICBpdGVyQ291bnQgPSBpdGVyQ291bnQgKyAxCiAgICAgICAgfQogICAgICAgIGZsYWdBW10gPSAwCiAgICAgICAgZmxhZ0JbXSA9IDAKICAgICAgICBpZiAodmVyYm9zZSA9PSBUUlVFKSB7CiAgICAgICAgICAgIGVuZC50aW1lIDwtIFN5cy50aW1lKCkKICAgICAgICAgICAgY2F0KCJcbkl0ZXJhdGlvbiBUaW1lOiIsIGVuZC50aW1lIC0gc3RhcnQudGltZSwgIlxuIikKICAgICAgICB9CiAgICB9CiAgICBpZih2ZXJib3NlKQogICAgewogICAgICB0b3RhbC50aW1lLmVuZCA9IFN5cy50aW1lKCkKICAgICAgY2F0KCJcbi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuVG90YWwgUnVuIFRpbWU6IiwgZGlmZnRpbWUodG90YWwudGltZS5lbmQsIHRvdGFsLnRpbWUuc3RhcnQsIHVuaXRzID0gInNlY3MiKSwgIlxuLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iKQogICAgfQogICAgcmV0dXJuKHRoaW5uZWQpCn0KYGBgCgpgYGB7ciBCZXR0ZXJFeCwgZWNobz1GQUxTRX0KbG9uZG9uX2JldHRlciA9IGJldHRlclRoaW5uaW5nKGxvbmRvbiwgdmVyYm9zZSA9IFRSVUUpCmBgYAoKCiMjIyMgTXkgSW1wbGVtZW50YXRpb24KCkl0J3MgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBlYWNoIG9mIHRoZXNlIGFsZ29yaXRobXMgaXMgcGVyZm9ybWluZyB0aGUgZXhhY3Qgc2FtZSBwcm9jZWR1cmUsIGRvd24gdG8gdGhlIHBpeGVsLiBUaGVyZSBpcyBubyByYW5kb21uZXNzIGludm9sdmVkLCBzbyB3ZSBjYW4gdmVyaWZ5IGJ5IGxvb2tpbmcgYXQgdGhlIG51bWJlciBvZiBjaGFuZ2VzIG1hZGUgdGhhdCBlYWNoIG9mIHRoZSBzcGVlZHMgaXMgcmV0dXJuaW5nIHRoZSBleGFjdCBzYW1lIHRoaW5nLgpgYGB7ciBGYXN0VGhpbm5pbmcsIGVjaG8gPSBGQUxTRX0KbG9uZG9uX2Zhc3QgPSB0aGluSW1hZ2UobG9uZG9uLCB2ZXJib3NlID0gVFJVRSkKYGBgCgojIyMgTGV0cyBsb29rIGF0IGEgYmlnZ2VyIGltYWdlIChidXQganVzdCBmb3IgdGhlIGxhc3QgMiBpbXBsZW1lbnRhdGlvbnMpCgpXZSdsbCB0aGluIHRoZSBmb2xsb3dpbmcgcGFyYWdyYXBoLiBJdCBpcyBhIDEyNjIgYnkgMTE2MiBpbWFnZS4KCmBgYHtyIE1lc3NhZ2UsIGVjaG89RkFMU0V9CmRhdGEobWVzc2FnZSkKbWVzc2FnZSA9IGNyb3AobWVzc2FnZSkKcGxvdEltYWdlKG1lc3NhZ2UpCmBgYAoKIyMjIyBCZXR0ZXIgdGhhbiBuYWl2ZSwgYnV0IG5vdCBnb29kIGltcGxlbWVudGF0aW9uCgpUaGUgdGhpbmcgdG8gbm90aWNlIGFib3V0IHRoaXMgYWxnb3JpdGhtIGlzIGF0IHRoZSBlbmQsIHdoZW4gdGhlcmUgYXJlIG9ubHkgZG91YmxlIGRpZ2l0IGNoYW5nZXMgdG8gYmUgbWFkZSwgdGhlcmUgaXMgYSBsb3dlciBib3VuZCB0byBob3cgZmFzdCB0aGUgYWxnb3JpdGhtIGNhbiBydW4uIEFib3V0IDEwIHNlY29uZHMgZm9yIHRoaXMgaW1hZ2UuIEZvciBteSBpbXBsZW1lbnRhdGlvbiwgdGhlc2Ugc3RhZ2VzIHdpbGwgcnVuIG1vcmUgcXVpY2tseS4KCmBgYHtyIE1lc3NhZ2VCZXR0ZXIsIGVjaG8gPSBGQUxTRX0KbWVzc2FnZV9iZXR0ZXIgPSBiZXR0ZXJUaGlubmluZyhtZXNzYWdlLCB2ZXJib3NlID0gVFJVRSkKYGBgCgojIyMjIE15IEltcGxlbWVudGF0aW9uCgpUaGUgZmlyc3QgaXRlcmF0aW9uIGlzIGFib3V0IHRoZSBzYW1lIGZvciB0aGUgdHdvIG1ldGhvZHMsIHdlIGhhdmUgdG8gbG9vayBhdCBhbGwgb2YgdGhlIGRhcmsgcGl4ZWxzLiBTdWJzZXF1ZW50IGl0ZXJhdGlvbnMgd2lsbCBiZSBzbWFsbGVyLCBlc3BlY2lhbGx5IHRoZSBpdGVyYXRpb25zIHdoZW4gYSBzbWFsbCBudW1iZXIgb2YgY2hhbmdlcyBpcyBtYWRlLiBXZSBubyBsb25nZXIgaGF2ZSB0byBsb29rIGF0IHRoZSBwYXJ0cyBvZiB0aGUgbWVzc2FnZSB0aGF0IGhhdmUgYWxyZWFkeSBiZWVuIHRoaW5uZWQsIGp1c3QgdGhlIHBhcnRzIHRoYXQgYXJlIGFjdGl2ZWx5IGJlaW5nIHRoaW5uZWQuIFRoaXMgcmVzdWx0cyBpbiBhIHN1YnN0YW50aWFsIHNwZWVkIGJvb3N0IGhlcmUgKGFib3V0IDIgdGltZXMpLCBhbmQgd2lsbCBnZXQgZXZlbiBmYXN0ZXIgYXMgdGhlIGltYWdlIHNpemUgaW5jcmVhc2VzLgoKYGBge3IgTWVzc2FnZUZhc3QsIGVjaG89RkFMU0V9Cm1lc3NhZ2VfZmFzdCA9IHRoaW5JbWFnZShtZXNzYWdlLCB2ZXJib3NlID0gVFJVRSkKYGBgCgpTbywgdGhlcmUgaXMgYWxyZWFkeSBhIHN1YnN0YW50aWFsIHNwZWVkIGluY3JlYXNlLiBJIGFsc28gdGhpbmsgdGhhdCBhIGxhcmdlIHBhcnQgb2YgdGhlIHJ1biB0aW1lICh0aGUgZmlyc3QgaXRlcmF0aW9uKSBjb3VsZCBiZSBlbGltaW5hdGVkIGlmIEkgcGlja2VkIGEgbW9yZSBjcmVhdGl2ZSBzdGFydGluZyBzZXQgdG8gbG9vayBhdC4gU29tZXRoaW5nIGxpa2UsIGxvb2sgYXQgZXZlcnkgcG9pbnQgdGhhdCBoYXMgYXQgbGVhc3QgMiBhbmQgbm90IG1vcmUgdGhhbiA2IG5laWdoYm9ycy4gVGhpcyB3b3VsZCBvbmx5IGNoZWNrIHRoYXQgY29uZGl0aW9uIGZvciBhbGwgYmxhY2sgcG9pbnRzLCBidXQgbm90IHJlbW92ZSBhbnksIGFuZCB0aGVuIHdvdWxkIG5hcnJvdyBkb3duIHRoZSBzZXQgZm9yIHdoZW4gd2UgbmVlZCB0byBjaGVjayBldmVyeSBjb25kaXRpb24uIFdlIGNvdWxkIHJlZHVjZSB0aGUgbG9uZyBpdGVyYXRpb24gdGltZSBhIGNvdXBsZSB0aW1lcyBvdmVyIGJ5IGRvaW5nIHRoaXMuCgoK