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

  1. The pixel is black and has eight neighbours
  2. 2 <= B(i,j) <= 6
  3. A(i,j) = 1
  4. At least one of the north, east, and south neighbors is white
  5. At least one of east, south, and west neighbors is white

Pass 2

  1. The pixel is black and has eight neighbours
  2. 2 <= B(i,j) <= 6
  3. A(i,j) = 1
  4. At least one of the north, east, and west neighbors is white
  5. 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