The notebook is a full re-writing of its original version for a lightning talk at Data Science Conference 2015 at Taipei. The coding examples in the original notebook are for Linux/macOS users only. The revised notebook here is platform-independent.


1 Motivation

The performance of a R program can vary a lot, depending on whether it is written by an experienced R programmer or a newbie who don’t know how to properly write R. Unfortunately, unlike other general purpose langauges, it is much more likely to write improper R codes which run embarrassingly slow. Two important and common factors that affect the performance of R code:

  1. Vectorization
  2. Memory Copy

In general, we should use vectorization as much as possible and avoid memory copy as much as possible. For example the following code is TERRIBLE:

   user  system elapsed
  8.593   0.328   8.923 

We are just trying to calculate the base-2 power of a sequence of numbers. It will be very straghtforward to just loop over the numbers and compute the result for a general purpose language.

But NOT for R.

The above code is just WRONG because it violates both our two principles: it didn’t vectorize when it could, and it didn’t avoid memory copy. Let’s first fix the second issue about the memory copy:

   user  system elapsed
  0.015   0.000   0.010 

The output vector size is pre-determined for our second attempt, which avoid copies happening again and again during the unnecessary concatenation in the loop.

Still the code is terrible, because it didn’t vectorize when it actually could:

   user  system elapsed
      0       0       0 

The difference may not be obvious at the first glance because the task is too trivial. Now for a serious benchmarking at microsecond-level:

Unit: microseconds
               expr     min      lq     mean   median      uq      max neval
 pow_all_1(numbers) 85837.5 86857.6 92445.84 87417.35 88266.0 138826.5    10
 pow_all_2(numbers)   624.8   627.5   632.62   628.45   637.7    645.7    10
 pow_all_3(numbers)    15.9    17.0   100.34    17.35    24.0    833.2    10

A properly written R program can be of several orders of magnitude faster.

Even if we already carefully write our R code, it may still occur that we want even faster speed. This is especially true when there is no trivial way of vectorization for the implementation desired. This is the main topic of this notebook: we can still be faster, by using R’s C interface.

Rcpp is the most dominant way of achieving that. But before our deep dive, let’s understand what is R a bit more.

2 Decompose the R Source Code

Let’s examine the source code of the R language.

We’d like to know the file type distribution in the source code.

Here are the most common file types found in the source code of R.

  • .Rd: Document file
  • .R: R source file
  • .c: C source file
  • .mo: Binary data file
  • .po: Language translation file
  • .h: Header file for C
  • .afm: Font file
  • .in: Template configuration file for some macro preprocessor
  • .win: Same above but specifically for Windows
  • .f: Fortran source

If we further limit to only programming language related files:

The fact is that, R is heavily written in C. Primitive functions are writtin in C. Most of them are vectorized so it is very fast to apply the function directly to a vector. For example the power function we just examined previously is a primitive function:

function (e1, e2)  .Primitive("^")
 [1]   1   4   9  16  25  36  49  64  81 100

Instead of counting files, we can further count the number of lines in those language files to arrive at the distribution at file line level:

The take-away is that, R simply cannot be slow for most of the fundamentally demanding computating tasks. Its core is written in C and C is super fast.

3 Working Examples of Using Rcpp

There will be cases where the native R code is not suitable for the desired implementation. This is usually because the built-in primitive functions are not available for a particular kind of algorithm. In such scenario we can use Rcpp to re-write only the most demanding part of our implementation in C++ and port it to our R program seamlessly. In this section we are going to illustrate several such use cases.

3.1 N-Gram Generation

N-gram generation is a common task for natural language processing and understanding. Despite its simplicity, surprisingly, it is not easy for native R code to fulfill the task in an efficient manner.

Let’s assume we’d like to count the bigrams of the hex dump of a (potentially binary) file. The byte code information could be useful for a downstream machine learning task such as file category classification. But that is not our concern for now. We just want to implement a function that can extract ngrams given a long string, and count their frequencies.

 chr [1:31050] "2d" "2d" "2d" "0a" "74" "69" "74" "6c" "65" "3a" "20" "22" "48" "69" "67" "68" "20" "50" "65" "72" ...

Native R

Now we implement the minimum ngram counter given a character vector, assuming each element is a token:

[1] 1460
2020
 851 

The above function, though implementation is straightforward, is terribly slow because it didn’t utilize vectorization. And there doesn’t seem to have a readily available primitive function for this very purpose. This is the usually frustrated experience when people familiar with other general purpose languages first come to use R.

Rcpp

For a function requiring arbitraily large explicit loop and there is no obvious way to vectorization, we have Rcpp come to the rescue.1

For exactly the same minimum implementation we can re-write it in C++ using the Rcpp API:

[1] "1.0.2"
function (hexvector, ngram)
.Call(<pointer: 0x7fe6ac4864a0>, hexvector, ngram)
[1] TRUE

Notice that we only implement the ngram extraction loop but leave the frequency count for native R. This is because the table function is not a bottleneck of our task.

In this example we use the CharacterVector class implemented in Rcpp which mimics the character vector we usually use in native R code. There are many such high-level classes we can find in the Rcpp library. These classes will save our development time when writing the C++ code since we won’t limit ourselves to only C++ standard library.

Unit: milliseconds
                 expr     min      lq     mean   median      uq     max neval
    ngram_r(hex_c, 2) 59.4615 59.7028 60.16685 59.94985 60.2995 61.8870    10
 ngram_rcpp(hex_c, 2)  5.0451  5.1562  6.02980  5.34610  7.3100  8.7338    10

The Rcpp is much faster than the native R implementation. And it will be even faster if the data is getting larger.

 chr [1:30463021] "1f" "8b" "08" "08" "99" "f6" "1e" "5d" "02" "03" "52" "2d" "33" "2e" "36" "2e" "31" "2e" "74" ...

This time we benchmark with different file size, we’ll see that theoretically Rcpp can be hundreds of times faster than native R.

Other Packages

For completeness, in this section we include the benchmark for some other well-developed packages for text processing. These packages are usually using R’s native C interface or Rcpp to implement the critical parts of the computation for their intended tasks. We will expect them to be also much more efficient than a naive implementation in native R code.

The first package we explore is quanteda:

[1] "1.5.1"
[1] TRUE

text2vec is also a high-performance package implemented with Rcpp:

[1] "0.5.1"
[1] TRUE

Lastly, unlike the previous two packages both aim at a larger scope of natural language processing tasks, the package ngram dedicates only at ngram generation task, written mainly in C:

[1] "3.0.4"
[1] TRUE

Now we benchmark all the above implementations using the large file:

Unit: seconds
                     expr      min       lq     mean   median       uq      max neval
            ngram_r(x, 2) 7.534342 7.548868 7.688973 7.563393 7.766289 7.969184     3
         ngram_rcpp(x, 2) 1.157837 1.261585 1.353972 1.365333 1.452040 1.538746     3
 ngram_quanteda_1(x_s, 2) 3.369109 3.544664 4.206217 3.720219 4.624771 5.529324     3
 ngram_quanteda_2(x_t, 2) 1.936233 1.944721 1.960462 1.953209 1.972577 1.991945     3
 ngram_text2vec_1(x_s, 2) 1.405728 1.438129 1.466014 1.470530 1.496158 1.521786     3
 ngram_text2vec_2(x_i, 2) 1.313886 1.629596 1.965398 1.945306 2.291154 2.637002     3
      ngram_ngram(x_s, 2) 3.727586 3.781383 3.876381 3.835181 3.950779 4.066378     3

3.2 Moving-Window Calculation

Task like n-gram generation is a type of moving-window operation. Such task is in general harder to vectorize and hence will usually result in performance issue in R. In this section we generalize the discussion to a generic moving window operation. For illustration we will use a sample log file from a Apache web server publicly available from Loghub (Zhu et al. (2019)). The log will look like the following text lines:

[1] "[Sun Dec 04 04:47:44 2005] [notice] workerEnv.init() ok /etc/httpd/conf/workers2.properties"
[2] "[Sun Dec 04 04:47:44 2005] [error] mod_jk child workerEnv in error state 6"
[3] "[Sun Dec 04 04:51:08 2005] [notice] jk2_init() Found child 6725 in scoreboard slot 10"
[4] "[Sun Dec 04 04:51:09 2005] [notice] jk2_init() Found child 6726 in scoreboard slot 8"
[5] "[Sun Dec 04 04:51:09 2005] [notice] jk2_init() Found child 6728 in scoreboard slot 6"
[6] "[Sun Dec 04 04:51:14 2005] [notice] workerEnv.init() ok /etc/httpd/conf/workers2.properties"
[1] 2000

Suppose the task is to count number of records by log type (notice or error) within the last 5 minutes for every 1 minute. This is an overlapping moving window operation where the actual window size (number of records involved in each time-fixed window) must be further determined by the original timestamp. The idea is rather simple but the actual implementation presents several challanges from the angle of performance optimization in native R code.

Let’s tidy up the log lines first to have a cleaner input to our task:

Native R

Again let’s try the naive approach using plain R code first.

   user  system elapsed
 38.547   0.078   6.459 

The results are two numeric series, which can be plotted as below.

One should notice that this is a very small dataset but our function already exhibits bottlenecked. Before we proceed to implement the same idea but in Rcpp, let’s struggle a bit more:

   user  system elapsed
  0.484   0.000   0.248 
[1] TRUE

Apparently we can improve drastically with native R code if we do some creative twisting of the original task. Here we re-work the original problem by forcely expanding the input data.frame to have at least one row for every second in the covered period, and we do a cumulative sum by second to arrive at a second-level moving window and then subset its minute-level results. In doing so we effectively remove the need to do the moving window loop (which R is not good at) and transform the task to use the built-in vectorized primitive cumsum function.

The improved solution still has several flaws:

  1. It uses more memory than it actually needs by expanding the data.frame from a sparse representation (a second without log will not present) into a dense one; this can be a bottleneck for really huge datasets
  2. It computes things more than we need: moving window operation is performed at second level but we only need a minute level (This can be solved if we refactor the code a bit, so less of an issue and also its performance impact is quite limited)
  3. It is much harder to read and understand the intent of the code

And, as a matter of fact, it is still not super fast.

Let’s see if we can achieve even faster computing time by using Rcpp.

Rcpp

   user  system elapsed
  0.141   0.016   0.085 
[1] TRUE
Unit: milliseconds
                                expr       min         lq      mean    median        uq       max neval
    mw_count_r(apache_logs, 300, 60) 7081.7981 7213.51185 7525.0865 7345.2256 7746.7306 8148.2357     3
  mw_count_r_2(apache_logs, 300, 60)  257.9965  263.29445  267.7426  268.5924  272.6157  276.6390     3
 mw_count_rcpp(apache_logs, 300, 60)   84.1526   86.41185   90.0316   88.6711   92.9711   97.2711     3

As one can see, using Rcpp is brutal and simple and extremely fast. It also supports vector slicing as in R so writing Rcpp is indeed more like writing R and less like writing C++.

In this toy example our Rcpp solution is more than 3 times faster than our creative twisted approach. And it cerrtainly can be even faster when it comes to larger application.

3.3 Time-Dependent Feature Generation

Another common use case similar to a moving window operation is when we need to calculate derived features based on a look-back time window. This is usually for a machine learning dataset where an entity can have time dependent behavior statistics such as number of a certain activity in the past 30 days at any given point when a model needs to make a prediction about the future.

For this scenario we will create a MOOC-like student-course dataset as our working example:

To interpret the dataset, a row represents a pair of studient-class interaction at a given timestamp.

Now assume we’d like to extract a feature: How many unique courses in total did a student register at the time of his/her last activity record for each course? For example, if student 1’s last activity for course a is today, then how many courses in total (including course a) did she also have at least one interaction record? We may want to use this information as one of the feature to build a model to predict whether a user will drop out from a registered class.

For this use case we will again benchmark with 3 different approaches:

  1. The naive R approach
  2. The improved R approach using data.table API
  3. The naive approach but written in Rcpp

Native R

First the naive approach in native R code:

   user  system elapsed
 99.000   0.110  16.598 

The idea is simple: we loop over each student and inner-loop over each courses to calculate the required metric. Such huge nested loop is doomed to fail for large application.

The result will look something like:

So for example user 491 has interacted with 18 courses (or 17 other courses) in total when she last interacted with course t in the records.

To stick with R, let’s improve the performance by using data.table’s special group by each i functionality:

   user  system elapsed
  0.594   0.000   0.124 
[1] TRUE

The solution is indeed very fast. The only drawback is that the code is hard to read and understand, as it always will be when we try to re-work a problem from its original representation.

Rcpp

Can we beat the performance of a pure data.table approach by simply implement the nested loop using Rcpp?

   user  system elapsed
  0.516   0.110   0.165 
[1] TRUE
Unit: milliseconds
                 expr     min      lq      mean   median       uq      max neval
 better_r_func(mdata) 90.9778 94.6369  98.63532  98.3861 102.4294 108.4087    10
     rcpp_func(mdata) 83.8232 85.3324 131.85706 137.3354 160.0626 174.7365    10

It turns out that, Rcpp is still the fastest! But the difference is not significant. We shouldn’t be surprised if we know that data.table itself is indeed written in C.

4 Final Wrap-Up

In this notebook we’ve demonstrated the key concept to write a high-performance R program: to vectorize and to avoid memory copy. They will work 90% of the time to make sure our R program is fast enough. But there are use cases where the concept, especially vectorization, is hard to apply. To overcome such problem we can:

  1. Re-work the original problem with an alternative algorithm that can be vectorized
  2. Survey any existing package that already handles such case efficiently
  3. Implement the core of our algorithm in Rcpp

Obviously the last option is the most flexible one. And we’ve also demonstrated 3 different common use cases where Rcpp can gain tremendous improvement in computing time, with not really much effort to implement.

The key to using Rcpp with ease is to identify only the bottleneck in the algorithm (which is usually a very small part of it) and implement only the bottleneck part in Rcpp.

5 References

Bengtsson, Henrik. 2019. R.utils: Various Programming Utilities. https://CRAN.R-project.org/package=R.utils.

Benoit, Kenneth, Kohei Watanabe, Haiyan Wang, Paul Nulty, Adam Obeng, Stefan Müller, and Akitaka Matsuo. 2018. “Quanteda: An R Package for the Quantitative Analysis of Textual Data.” Journal of Open Source Software 3 (30): 774. https://doi.org/10.21105/joss.00774.

Dowle, Matt, and Arun Srinivasan. 2019. Data.table: Extension of ‘Data.frame‘. https://CRAN.R-project.org/package=data.table.

Eddelbuettel, Dirk. 2013. Seamless R and C++ Integration with Rcpp. New York: Springer. https://doi.org/10.1007/978-1-4614-6868-4.

Eddelbuettel, Dirk, and James Joseph Balamuta. 2017. “Extending extitR with extitC++: A Brief Introduction to extitRcpp.” PeerJ Preprints 5 (August): e3188v1. https://doi.org/10.7287/peerj.preprints.3188v1.

Eddelbuettel, Dirk, and Romain François. 2011. “Rcpp: Seamless R and C++ Integration.” Journal of Statistical Software 40 (8): 1–18. https://doi.org/10.18637/jss.v040.i08.

Lang, Dawei, and Guan-tin Chien. 2018. Wordcloud2: Create Word Cloud by ’Htmlwidget’. https://CRAN.R-project.org/package=wordcloud2.

Mersmann, Olaf. 2018. Microbenchmark: Accurate Timing Functions. https://CRAN.R-project.org/package=microbenchmark.

Schmidt, Drew, and Christian Heckendorf. 2017. “ngram: Fast N-Gram Tokenization.” https://cran.r-project.org/package=ngram.

Selivanov, Dmitriy, and Qing Wang. 2018. Text2vec: Modern Text Mining Framework for R. https://CRAN.R-project.org/package=text2vec.

Wickham, Hadley. 2016. Ggplot2: Elegant Graphics for Data Analysis. Springer-Verlag New York. https://ggplot2.tidyverse.org.

———. 2019. Stringr: Simple, Consistent Wrappers for Common String Operations. https://CRAN.R-project.org/package=stringr.

Zhu, Jieming, Shilin He, Jinyang Liu, Pinjia He, Qi Xie, Zibin Zheng, and Michael R Lyu. 2019. “Tools and Benchmarks for Automated Log Parsing.” In Proceedings of the 41st International Conference on Software Engineering: Software Engineering in Practice, 121–30. IEEE Press.


  1. And also surprisingly, we don’t really have many such cases in R programming where we really need to resort to its C interface. The languange is doing a very good job in vectorizing the most utilized functions for the majority of programming operations, especially in the data analytical field.

LS0tCnRpdGxlOiAiSGlnaCBQZXJmb3JtYW5jZSBDb21wdXRpbmcgaW4gUiB1c2luZyBgUmNwcGAiCnN1YnRpdGxlOiAiIgphdXRob3I6Ci0gbmFtZTogS3lsZSBDaHVuZwogIGFmZmlsaWF0aW9uOgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlYiAlWScpYCBMYXN0IFVwZGF0ZWQgKDMwIFNlcCAyMDE5IEZpcnN0IFVwbG9hZGVkKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6IHllcwogICAgaW5jbHVkZXM6CiAgICAgIGluX2hlYWRlcjogL3RtcC9tZXRhX2hlYWRlci5odG1sCiAgY29kZV9kb3dubG9hZDogdHJ1ZQpiaWJsaW9ncmFwaHk6IHJjcHAuYmliCm5vY2l0ZTogfAogIEBSLnV0aWxzCiAgQGRhdGEudGFibGUKICBAZ2dwbG90MgogIEB3b3JkY2xvdWQyCiAgQHJjcHAxCiAgQHJjcHAyCiAgQHJjcHAzCiAgQHF1YW50ZWRhCiAgQHRleHQydmVjCiAgQG5ncmFtCiAgQG1pY3JvYmVuY2htYXJrCiAgQHN0cmluZ3IKICBAbHVicmlkYXRlCmFic3RyYWN0OiB8CiAgUiBpcyBpbmhlcmVudGx5IGEgaGlnaC1wZXJmb3JtYW5jZSBzY3JpcHRpbmcgbGFuZ3VhZ2Ugc2luY2UgaXRzIGNvcmUgaXMgd3JpdHRlbiBpbiBDLiBXcml0aW5nIGEgaGlnaC1wZXJmb3JtYW5jZSBSIHByb2dyYW0sIGhvd2V2ZXIsIG1heSBub3QgYmUgYXMgc3RyYWlnaHRmb3J3YXJkIGFzIGl0IGlzIGZvciBzb21lIG90aGVyIGdlbmVyYWwgcHVycG9zZSBsYW5ndWFnZXMuIFRoaXMgbm90ZW5vb2sgZGVtb25zdHJhdGVzIHF1aWNrbHkgdGhlIGtleSBjb25jZXB0IGluIHdyaXRpbmcgYSBnb29kIFIgcHJvZ3JhbSBhbmQgYSBmYWN0IGNoZWNrIGFib3V0IHRoZSBkZWNvbXBvc2l0aW9uIG9mIHRoZSBsYW5ndWFnZSBzb3VyY2UgY29kZS4gSXQgYWxzbyBkZW1vbnN0cmF0ZXMgaW4gbW9yZSBkZXRhaWxzIHdpdGggc2V2ZXJhbCB3b3JraW5nIGV4YW1wbGVzIGFuZCBiZW5jaG1hcmtzIG9uIGhvdyB3ZSBjYW4gYm9vc3QgdGhlIHBlcmZvcm1hbmNlIHdoZW4gdGhlIHVuZGVybHlpbmcgdGFzayBpcyBub3QgZWFzeSB0byBvcHRpbWl6ZSBpbiBuYXRpdmUgUiBjb2RlOiBieSB1c2luZyBSY3BwLgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpvcHRpb25zKHdpZHRoPTEyMCkKYGBgCgpgYGB7ciBtZXRhLCBpbmNsdWRlPUZBTFNFfQpkaXIuY3JlYXRlKCIvdG1wIiwgc2hvd1dhcm5pbmdzPUZBTFNFKQptZXRhX2hlYWRlcl9maWxlIDwtIGZpbGUoIi90bXAvbWV0YV9oZWFkZXIuaHRtbCIpCgojIEFkZCBvcGVuIGdyYXBoIG1ldGEuCm1ldGEgPC0gYygKICAgICc8bWV0YSBuYW1lPSJhdXRob3IiIGNvbnRlbnQ9Ikt5bGUgQ2h1bmciPicsCiAgICAnPG1ldGEgcHJvcGVydHk9Im9nOnRpdGxlIiBjb250ZW50PSJIaWdoIFBlcmZvcm1hbmNlIENvbXB1dGluZyBpbiBSIHVzaW5nIFJjcHAiPicsCiAgICAnPG1ldGEgcHJvcGVydHk9Im9nOnR5cGUiIGNvbnRlbnQ9ImFydGljbGUiPicsCiAgICAnPG1ldGEgcHJvcGVydHk9Im9nOnVybCIgY29udGVudD0iaHR0cHM6Ly9ldmVyZGFyay5naXRodWIuaW8vazkvcHJvZ3JhbW1pbmcvcmNwcC9yY3BwLm5iLmh0bWwiPicsCiAgICAnPG1ldGEgcHJvcGVydHk9Im9nOmltYWdlIiBjb250ZW50PSJodHRwczovL2V2ZXJkYXJrLmdpdGh1Yi5pby9rOS9hc3NldHMvcmxhbmcucG5nIj4nLAogICAgJzxtZXRhIHByb3BlcnR5PSJvZzpkZXNjcmlwdGlvbiIgY29udGVudD0iQSBTaG93Y2FzZSBvZiBoaWdoIHBlcmZvcm1hbmNlIGNvbXB1dGluZyBpbiBSIHVzaW5nIFJjcHAuIj4nCikKY29udGVudHMgPC0gbWV0YQoKIyBBZGQgR2l0aHViIGNvcm5lci4KZ2l0aHViX2Nvcm5lcl9zdmcgPC0gIi4uLy4uLy4uL2Fzc2V0cy9naXRodWJfY29ybmVyLmh0bWwiCmdpdGh1Yl9jb3JuZXJfY29uZiA8LSBsaXN0KGdpdGh1Yl9saW5rPSJodHRwczovL2dpdGh1Yi5jb20vZXZlcmRhcmsvazkvdHJlZS9tYXN0ZXIvcHJvZ3JhbW1pbmcvci9yY3BwIikKY29udGVudHMgPC0gYyhjb250ZW50cywgc3RyaW5ncjo6c3RyX2ludGVycChyZWFkTGluZXMoZ2l0aHViX2Nvcm5lcl9zdmcpLCBnaXRodWJfY29ybmVyX2NvbmYpKQp3cml0ZUxpbmVzKGNvbnRlbnRzLCBtZXRhX2hlYWRlcl9maWxlKQoKY2xvc2UobWV0YV9oZWFkZXJfZmlsZSkKYGBgCgotLS0KClRoZSBub3RlYm9vayBpcyBhIGZ1bGwgcmUtd3JpdGluZyBvZiBpdHMgW29yaWdpbmFsIHZlcnNpb25dKGh0dHA6Ly9ldmVyZGFyay5naXRodWIuaW8vcmNwcF9saWdodG5pbmdfZHNjMjAxNS8pIGZvciBhIGxpZ2h0bmluZyB0YWxrIGF0IERhdGEgU2NpZW5jZSBDb25mZXJlbmNlIDIwMTUgYXQgVGFpcGVpLgpUaGUgY29kaW5nIGV4YW1wbGVzIGluIHRoZSBvcmlnaW5hbCBub3RlYm9vayBhcmUgZm9yIExpbnV4L21hY09TIHVzZXJzIG9ubHkuClRoZSByZXZpc2VkIG5vdGVib29rIGhlcmUgaXMgcGxhdGZvcm0taW5kZXBlbmRlbnQuCgotLS0KCiMgTW90aXZhdGlvbgoKVGhlIHBlcmZvcm1hbmNlIG9mIGEgUiBwcm9ncmFtIGNhbiB2YXJ5IGEgbG90LApkZXBlbmRpbmcgb24gd2hldGhlciBpdCBpcyB3cml0dGVuIGJ5IGFuIGV4cGVyaWVuY2VkIFIgcHJvZ3JhbW1lciBvciBhIG5ld2JpZSB3aG8gZG9uJ3Qga25vdyBob3cgdG8gcHJvcGVybHkgd3JpdGUgUi4KVW5mb3J0dW5hdGVseSwKdW5saWtlIG90aGVyIGdlbmVyYWwgcHVycG9zZSBsYW5nYXVnZXMsCml0IGlzIG11Y2ggbW9yZSBsaWtlbHkgdG8gd3JpdGUgaW1wcm9wZXIgUiBjb2RlcyB3aGljaCBydW4gZW1iYXJyYXNzaW5nbHkgc2xvdy4KVHdvIGltcG9ydGFudCBhbmQgY29tbW9uIGZhY3RvcnMgdGhhdCBhZmZlY3QgdGhlIHBlcmZvcm1hbmNlIG9mIFIgY29kZToKCjEuIFZlY3Rvcml6YXRpb24KMi4gTWVtb3J5IENvcHkKCkluIGdlbmVyYWwsCndlIHNob3VsZCB1c2UgdmVjdG9yaXphdGlvbiBhcyBtdWNoIGFzIHBvc3NpYmxlIGFuZCAqYXZvaWQqIG1lbW9yeSBjb3B5IGFzIG11Y2ggYXMgcG9zc2libGUuCkZvciBleGFtcGxlIHRoZSBmb2xsb3dpbmcgY29kZSBpcyBURVJSSUJMRToKCmBgYHtyIHRlcnJpYmxlfQpudW1iZXJzIDwtIDE6MWU1Cgpwb3dfYWxsXzEgPC0gZnVuY3Rpb24obnVtYmVycykgewogIG91dCA8LSBjKCkKICBmb3IgKCBuIGluIG51bWJlcnMgKSB7CiAgICBvdXQgPC0gYyhvdXQsIG5eMikgICMgTWVtb3J5IGNvcHkgaXMgaGFwcGVuaW5nIGhlcmUuCiAgfQogIG91dAp9CgpzeXN0ZW0udGltZShvdXRfMSA8LSBwb3dfYWxsXzEobnVtYmVycykpCmBgYAoKV2UgYXJlIGp1c3QgdHJ5aW5nIHRvIGNhbGN1bGF0ZSB0aGUgYmFzZS0yIHBvd2VyIG9mIGEgc2VxdWVuY2Ugb2YgbnVtYmVycy4KSXQgd2lsbCBiZSB2ZXJ5IHN0cmFnaHRmb3J3YXJkIHRvIGp1c3QgbG9vcCBvdmVyIHRoZSBudW1iZXJzIGFuZCBjb21wdXRlIHRoZSByZXN1bHQgZm9yIGEgZ2VuZXJhbCBwdXJwb3NlIGxhbmd1YWdlLgoKQnV0IE5PVCBmb3IgUi4KClRoZSBhYm92ZSBjb2RlIGlzIGp1c3QgV1JPTkcgYmVjYXVzZSBpdCB2aW9sYXRlcyBib3RoIG91ciB0d28gcHJpbmNpcGxlczoKaXQgZGlkbid0IHZlY3Rvcml6ZSB3aGVuIGl0IGNvdWxkLCBhbmQgaXQgZGlkbid0IGF2b2lkIG1lbW9yeSBjb3B5LgpMZXQncyBmaXJzdCBmaXggdGhlIHNlY29uZCBpc3N1ZSBhYm91dCB0aGUgbWVtb3J5IGNvcHk6CgpgYGB7ciBzdGlsbF90ZXJyaWJsZX0KcG93X2FsbF8yIDwtIGZ1bmN0aW9uKG51bWJlcnMpIHsKICBvdXQgPC0gbnVtZXJpYyhsZW5ndGgobnVtYmVycykpICAjIFByZS1hbGxvY2F0ZSBtZW1vcnkuCiAgZm9yICggaSBpbiAxOmxlbmd0aChudW1iZXJzKSApIHsKICAgIG91dFtpXSA8LSBudW1iZXJzW2ldXjIKICB9CiAgb3V0Cn0KCnN5c3RlbS50aW1lKG91dF8yIDwtIHBvd19hbGxfMihudW1iZXJzKSkKc3RvcGlmbm90KGFsbC5lcXVhbChvdXRfMSwgb3V0XzIpKQpgYGAKClRoZSBvdXRwdXQgdmVjdG9yIHNpemUgaXMgcHJlLWRldGVybWluZWQgZm9yIG91ciBzZWNvbmQgYXR0ZW1wdCwKd2hpY2ggYXZvaWQgY29waWVzIGhhcHBlbmluZyBhZ2FpbiBhbmQgYWdhaW4gZHVyaW5nIHRoZSB1bm5lY2Vzc2FyeSBjb25jYXRlbmF0aW9uIGluIHRoZSBsb29wLgoKU3RpbGwgdGhlIGNvZGUgaXMgdGVycmlibGUsCmJlY2F1c2UgaXQgZGlkbid0ICp2ZWN0b3JpemUqIHdoZW4gaXQgYWN0dWFsbHkgY291bGQ6CgpgYGB7ciBnb29kfQpwb3dfYWxsXzMgPC0gZnVuY3Rpb24obnVtYmVycykgewogIG51bWJlcnNeMgp9CgpzeXN0ZW0udGltZShvdXRfMyA8LSBwb3dfYWxsXzMobnVtYmVycykpCnN0b3BpZm5vdChhbGwuZXF1YWwob3V0XzEsIG91dF8zKSkKYGBgCgpUaGUgZGlmZmVyZW5jZSBtYXkgbm90IGJlIG9idmlvdXMgYXQgdGhlIGZpcnN0IGdsYW5jZSBiZWNhdXNlIHRoZSB0YXNrIGlzIHRvbyB0cml2aWFsLgpOb3cgZm9yIGEgc2VyaW91cyBiZW5jaG1hcmtpbmcgYXQgbWljcm9zZWNvbmQtbGV2ZWw6CgpgYGB7ciBiZW5jaG1hcmtfdmVjdG9yaXphdGlvbiwgcGFnZWQucHJpbnQ9RkFMU0V9CmxpYnJhcnkobWljcm9iZW5jaG1hcmspCgpudW1iZXJzIDwtIDE6MWU0ICAjIE1ha2UgaXQgc21hbGxlciBmb3IgZmFzdGVyIGJlbmNobWFya2luZy4KCmJlbiA8LSBtaWNyb2JlbmNobWFyaygKICBwb3dfYWxsXzEobnVtYmVycyksCiAgcG93X2FsbF8yKG51bWJlcnMpLAogIHBvd19hbGxfMyhudW1iZXJzKSwKICB0aW1lcz0xMAopCgpwcmludChiZW4pCmBgYAoKQSBwcm9wZXJseSB3cml0dGVuIFIgcHJvZ3JhbSBjYW4gYmUgb2Ygc2V2ZXJhbCBvcmRlcnMgb2YgbWFnbml0dWRlIGZhc3Rlci4KCkV2ZW4gaWYgd2UgYWxyZWFkeSBjYXJlZnVsbHkgd3JpdGUgb3VyIFIgY29kZSwKaXQgbWF5IHN0aWxsIG9jY3VyIHRoYXQgd2Ugd2FudCBldmVuIGZhc3RlciBzcGVlZC4KVGhpcyBpcyBlc3BlY2lhbGx5IHRydWUgd2hlbiB0aGVyZSBpcyBubyB0cml2aWFsIHdheSBvZiB2ZWN0b3JpemF0aW9uIGZvciB0aGUgaW1wbGVtZW50YXRpb24gZGVzaXJlZC4KVGhpcyBpcyB0aGUgbWFpbiB0b3BpYyBvZiB0aGlzIG5vdGVib29rOgp3ZSBjYW4gc3RpbGwgYmUgZmFzdGVyLApieSB1c2luZyBSJ3MgQyBpbnRlcmZhY2UuCgpgUmNwcGAgaXMgdGhlIG1vc3QgZG9taW5hbnQgd2F5IG9mIGFjaGlldmluZyB0aGF0LgpCdXQgYmVmb3JlIG91ciBkZWVwIGRpdmUsCmxldCdzIHVuZGVyc3RhbmQgKndoYXQgaXMgUiogYSBiaXQgbW9yZS4KCiMgRGVjb21wb3NlIHRoZSBSIFNvdXJjZSBDb2RlCgpMZXQncyBleGFtaW5lIHRoZSBzb3VyY2UgY29kZSBvZiB0aGUgUiBsYW5ndWFnZS4KCmBgYHtyIGRvd25sb2FkX3Jfc291cmNlLCByZXN1bHRzPSJoaWRlIiwgd2FybmluZz1GQUxTRX0KIyBEb3dubG9hZCB0aGUgc291cmNlIGNvZGUgb2YgUiBsYW5nLgojIFRoaXMgbWF5IHRha2UgYSB3aGlsZS4KClRFTVBESVIgPC0gdGVtcGRpcigpCnRhcmdldF9maWxlIDwtICJodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9zcmMvYmFzZS9SLTMvUi0zLjYuMS50YXIuZ3oiCmRvd25sb2FkZWRfZmlsZSA8LSBmaWxlLnBhdGgoVEVNUERJUiwgYmFzZW5hbWUodGFyZ2V0X2ZpbGUpKQoKaWYgKCAhZmlsZS5leGlzdHMoZG93bmxvYWRlZF9maWxlKSApIHsKICBkb3dubG9hZC5maWxlKHRhcmdldF9maWxlLCBkb3dubG9hZGVkX2ZpbGUpCn0KCnVudGFyKGRvd25sb2FkZWRfZmlsZSwgZXhkaXI9VEVNUERJUikgICMgVGhpcyBtYXkgZ2VuZXJhdGUgd2FybmluZ3MgdW5kZXIgV2luZG93cy4KYGBgCgpXZSdkIGxpa2UgdG8ga25vdyB0aGUgZmlsZSB0eXBlIGRpc3RyaWJ1dGlvbiBpbiB0aGUgc291cmNlIGNvZGUuCgpgYGB7ciByZWFkX3Jfc291cmNlfQojIExpc3QgYWxsIGZpbGVzIGluIHRoZSBzb3VyY2UgYW5kIGNvdW50IHRoZSBleHRlbnNpb25zLgoKbGlicmFyeShkYXRhLnRhYmxlKQoKc3JjX2RpciA8LSBmaWxlLnBhdGgodG9vbHM6OmZpbGVfcGF0aF9zYW5zX2V4dChkb3dubG9hZGVkX2ZpbGUsIGNvbXByZXNzaW9uPVRSVUUpLCAic3JjIikKc3JjX2ZpbGVzIDwtIGxpc3QuZmlsZXMoc3JjX2RpciwgcmVjdXJzaXZlPVRSVUUsIGZ1bGwubmFtZXM9VFJVRSkKc3JjX2V4dHMgPC0gdG9vbHM6OmZpbGVfZXh0KHNyY19maWxlcykKc3JjX2V4dHMgPC0gc3JjX2V4dHNbc3JjX2V4dHMgIT0gIiJdCgpleHRfY291bnRzIDwtIGFzLmRhdGEudGFibGUoc29ydCh0YWJsZShzcmNfZXh0cyksIGRlY3JlYXNpbmc9VFJVRSksIGtlZXAucm93bmFtZXM9VFJVRSkKc2V0bmFtZXMoZXh0X2NvdW50cywgYygiZXh0IiwgImNvdW50IikpCmV4dF9jb3VudHNbLCBleHQ6PWZhY3RvcihleHQsIGxldmVscz1yZXYoZXh0KSldCmBgYAoKYGBge3IgZXh0X3dvcmRjbG91ZH0KbGlicmFyeSh3b3JkY2xvdWQyKQoKIyBTY2FsZSBkb3duIHRvIGF2b2lkIG92ZXItc2l6aW5nLgp3b3JkY2xvdWQyKHNxcnQodGFibGUoc3JjX2V4dHMpKSkKYGBgCgpgYGB7ciB0b3Bfbl9leHR9CmxpYnJhcnkoZ2dwbG90MikKCnRvcF9uIDwtIDEwCmdncGxvdChleHRfY291bnRzWzE6dG9wX25dLCBhZXMoeD1leHQsIHk9Y291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZT1zcHJpbnRmKCJUb3AgJXMgRmlsZSBFeHRlbnNpb25zIGluIFIgU291cmNlIENvZGUiLCB0b3BfbikpICsKICBjb29yZF9mbGlwKCkKYGBgCgpIZXJlIGFyZSB0aGUgbW9zdCBjb21tb24gZmlsZSB0eXBlcyBmb3VuZCBpbiB0aGUgc291cmNlIGNvZGUgb2YgUi4KCisgYC5SZGA6IERvY3VtZW50IGZpbGUKKyBgLlJgOiBSIHNvdXJjZSBmaWxlCisgYC5jYDogQyBzb3VyY2UgZmlsZQorIGAubW9gOiBCaW5hcnkgZGF0YSBmaWxlCisgYC5wb2A6IExhbmd1YWdlIHRyYW5zbGF0aW9uIGZpbGUKKyBgLmhgOiBIZWFkZXIgZmlsZSBmb3IgQworIGAuYWZtYDogRm9udCBmaWxlCisgYC5pbmA6IFRlbXBsYXRlIGNvbmZpZ3VyYXRpb24gZmlsZSBmb3Igc29tZSBtYWNybyBwcmVwcm9jZXNzb3IKKyBgLndpbmA6IFNhbWUgYWJvdmUgYnV0IHNwZWNpZmljYWxseSBmb3IgV2luZG93cworIGAuZmA6IEZvcnRyYW4gc291cmNlCgpJZiB3ZSBmdXJ0aGVyIGxpbWl0IHRvIG9ubHkgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgcmVsYXRlZCBmaWxlczoKCmBgYHtyIGxhbmdfZXh0X2NvdW50LCByZXN1bHRzPSJob2xkIn0KbGFuZ19leHQgPC0gYygiYyIsICJoIiwgImYiLCAiUiIpCmxhbmdfZXh0X2NvdW50cyA8LSBleHRfY291bnRzW2V4dCAlaW4lIGxhbmdfZXh0XQpsYW5nX2V4dF9jb3VudHNbLCBwY3Q6PWNvdW50IC8gc3VtKGNvdW50KV0KbGFuZ19leHRfY291bnRzWywgZXh0Oj1mYWN0b3IoZXh0LCBsZXZlbHM9bGFuZ19leHQpXQoKZ2dwbG90KGxhbmdfZXh0X2NvdW50cywgYWVzKHg9ZXh0LCB5PWNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIGxhYnMoeT0iTnVtYmVyIG9mIEZpbGVzIiwgeD0iRmlsZSBFeHRlbnNpb24iLAogICAgICAgdGl0bGU9Ikxhbmd1YWdlIEZpbGUgRGlzdHJpYnV0aW9uIGluIFIgU291cmNlIENvZGUiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1zY2FsZXM6OnBlcmNlbnQocGN0KSwgeT1jb3VudCksCiAgICAgICAgICAgIHZqdXN0PTEsIHNpemU9NSwgY29sb3I9IndoaXRlIikKYGBgCgpUaGUgZmFjdCBpcyB0aGF0LAoqUiBpcyBoZWF2aWx5IHdyaXR0ZW4gaW4gQy4qCmBQcmltaXRpdmVgIGZ1bmN0aW9ucyBhcmUgd3JpdHRpbiBpbiBDLgpNb3N0IG9mIHRoZW0gYXJlIHZlY3Rvcml6ZWQgc28gaXQgaXMgdmVyeSBmYXN0IHRvIGFwcGx5IHRoZSBmdW5jdGlvbiBkaXJlY3RseSB0byBhIHZlY3Rvci4KRm9yIGV4YW1wbGUgdGhlIHBvd2VyIGZ1bmN0aW9uIHdlIGp1c3QgZXhhbWluZWQgcHJldmlvdXNseSBpcyBhIGBwcmltaXRpdmVgIGZ1bmN0aW9uOgoKYGBge3IgcHJpbWl0aXZlfQpgXmAgICMgQSBwcmltaXRpdmUuCgooMToxMCleMiAgIyBBIHZlY3Rvcml6ZWQgcHJpbWl0aXZlIGZ1bmN0aW9uIGNhbGwuCmBgYAoKSW5zdGVhZCBvZiBjb3VudGluZyBmaWxlcywKd2UgY2FuIGZ1cnRoZXIgY291bnQgdGhlIG51bWJlciBvZiBsaW5lcyBpbiB0aG9zZSBsYW5ndWFnZSBmaWxlcyB0byBhcnJpdmUgYXQgdGhlIGRpc3RyaWJ1dGlvbiBhdCBmaWxlIGxpbmUgbGV2ZWw6CgpgYGB7ciBsYW5nX2xpbmVfY291bnQsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9ImhvbGQifQpsYW5nX3NyY19maWxlcyA8LSBzcmNfZmlsZXNbdG9vbHM6OmZpbGVfZXh0KHNyY19maWxlcykgJWluJSBsYW5nX2V4dF0KbGFuZ19zcmNfZmlsZV9sZW5zIDwtIHNhcHBseShsYW5nX3NyY19maWxlcywgUi51dGlsczo6Y291bnRMaW5lcykKCmxhbmdfbGVuIDwtIGRhdGEudGFibGUobGFuZz1nc3ViKCJeLipcXC4iLCAiIiwgbmFtZXMobGFuZ19zcmNfZmlsZV9sZW5zKSksIGxlbj1sYW5nX3NyY19maWxlX2xlbnMpCmxhbmdfbGVuX2NvdW50cyA8LSBsYW5nX2xlblssIC4odG90X2xpbmVzPXN1bShsZW4pKSwgYnk9ImxhbmciXQpsYW5nX2xlbl9jb3VudHNbLCBwY3Q6PXRvdF9saW5lcyAvIHN1bSh0b3RfbGluZXMpXQpsYW5nX2xlbl9jb3VudHNbLCBleHQ6PWZhY3RvcihsYW5nLCBsZXZlbHM9bGFuZ19leHQpXQoKZ2dwbG90KGxhbmdfbGVuX2NvdW50cywgYWVzKHg9bGFuZywgeT10b3RfbGluZXMpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgbGFicyh5PSJOdW1iZXIgb2YgTGluZXMiLCB4PSJGaWxlIEV4dGVuc2lvbiIsCiAgICAgICB0aXRsZT0iTGFuZ3VhZ2UgRmlsZSBMaW5lIERpc3RyaWJ1dGlvbiBpbiBSIFNvdXJjZSBDb2RlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWw9c2NhbGVzOjpwZXJjZW50KHBjdCksIHk9dG90X2xpbmVzKSwKICAgICAgICAgICAgdmp1c3Q9MS4yNSwgc2l6ZT01LCBjb2xvcj0id2hpdGUiKQpgYGAKClRoZSB0YWtlLWF3YXkgaXMgdGhhdCwKUiBzaW1wbHkgKmNhbm5vdCogYmUgc2xvdyBmb3IgbW9zdCBvZiB0aGUgZnVuZGFtZW50YWxseSBkZW1hbmRpbmcgY29tcHV0YXRpbmcgdGFza3MuCkl0cyBjb3JlIGlzIHdyaXR0ZW4gaW4gQyBhbmQgQyBpcyBzdXBlciBmYXN0LgoKIyBXb3JraW5nIEV4YW1wbGVzIG9mIFVzaW5nIGBSY3BwYAoKVGhlcmUgd2lsbCBiZSBjYXNlcyB3aGVyZSB0aGUgbmF0aXZlIFIgY29kZSBpcyBub3Qgc3VpdGFibGUgZm9yIHRoZSBkZXNpcmVkIGltcGxlbWVudGF0aW9uLgpUaGlzIGlzIHVzdWFsbHkgYmVjYXVzZSB0aGUgYnVpbHQtaW4gYHByaW1pdGl2ZWAgZnVuY3Rpb25zIGFyZSBub3QgYXZhaWxhYmxlIGZvciBhIHBhcnRpY3VsYXIga2luZCBvZiBhbGdvcml0aG0uCkluIHN1Y2ggc2NlbmFyaW8gd2UgY2FuIHVzZSBgUmNwcGAgdG8gcmUtd3JpdGUgb25seSB0aGUgbW9zdCBkZW1hbmRpbmcgcGFydCBvZiBvdXIgaW1wbGVtZW50YXRpb24gaW4gQysrIGFuZCBwb3J0IGl0IHRvIG91ciBSIHByb2dyYW0gc2VhbWxlc3NseS4KSW4gdGhpcyBzZWN0aW9uIHdlIGFyZSBnb2luZyB0byBpbGx1c3RyYXRlIHNldmVyYWwgc3VjaCB1c2UgY2FzZXMuCgojIyBOLUdyYW0gR2VuZXJhdGlvbgoKTi1ncmFtIGdlbmVyYXRpb24gaXMgYSBjb21tb24gdGFzayBmb3IgbmF0dXJhbCBsYW5ndWFnZSBwcm9jZXNzaW5nIGFuZCB1bmRlcnN0YW5kaW5nLgpEZXNwaXRlIGl0cyBzaW1wbGljaXR5LApzdXJwcmlzaW5nbHksCml0IGlzIG5vdCBlYXN5IGZvciBuYXRpdmUgUiBjb2RlIHRvIGZ1bGZpbGwgdGhlIHRhc2sgaW4gYW4gZWZmaWNpZW50IG1hbm5lci4KCkxldCdzIGFzc3VtZSB3ZSdkIGxpa2UgdG8gY291bnQgdGhlIGJpZ3JhbXMgb2YgdGhlIFtoZXggZHVtcF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSGV4X2R1bXApIG9mIGEgKHBvdGVudGlhbGx5IGJpbmFyeSkgZmlsZS4KVGhlIGJ5dGUgY29kZSBpbmZvcm1hdGlvbiBjb3VsZCBiZSB1c2VmdWwgZm9yIGEgZG93bnN0cmVhbSBtYWNoaW5lIGxlYXJuaW5nIHRhc2sgc3VjaCBhcyBmaWxlIGNhdGVnb3J5IGNsYXNzaWZpY2F0aW9uLgpCdXQgdGhhdCBpcyBub3Qgb3VyIGNvbmNlcm4gZm9yIG5vdy4KV2UganVzdCB3YW50IHRvIGltcGxlbWVudCBhIGZ1bmN0aW9uIHRoYXQgY2FuIGV4dHJhY3QgbmdyYW1zIGdpdmVuIGEgbG9uZyBzdHJpbmcsCmFuZCBjb3VudCB0aGVpciBmcmVxdWVuY2llcy4KCmBgYHtyIG5ncmFtX3JlYWRfdGV4dH0KIyBQcmVwYXJlIGhleCBkdW1wIG9mIGEgZmlsZS4KIyBXZSB3aWxsIHVzZSB0aGUgUm1kIGZpbGUgaXRzZWxmIGFzIGFuIGV4YW1wbGUuCmluZmlsZSA8LSAicmNwcC5SbWQiCmNvbiA8LSBmaWxlKGluZmlsZSwgInJiIikKaGV4IDwtIHJlYWRCaW4oY29uLCB3aGF0PSJyYXciLCBuPWZpbGUuaW5mbyhpbmZpbGUpJHNpemUpCmhleF9jIDwtIGFzLmNoYXJhY3RlcihoZXgpCmNsb3NlKGNvbikKCnN0cihoZXhfYykgICMgQSBjaGFyYWN0ZXIgdmVjdG9yIG9mIGhleGFkZWNpbWFscy4KYGBgCgojIyMgTmF0aXZlIFIgey19CgpOb3cgd2UgaW1wbGVtZW50IHRoZSBtaW5pbXVtIG5ncmFtIGNvdW50ZXIgZ2l2ZW4gYSBjaGFyYWN0ZXIgdmVjdG9yLAphc3N1bWluZyBlYWNoIGVsZW1lbnQgaXMgYSB0b2tlbjoKCmBgYHtyIG5ncmFtX3J9Cm5ncmFtX3IgPC0gZnVuY3Rpb24oeCwgbikgewogICAgbGVuIDwtIGxlbmd0aCh4KSAtIChuIC0gMSkKICAgIG91dCA8LSBjaGFyYWN0ZXIobGVuKSAgIyBQcmUtYWxsb2NhdGUgc2l6ZS4KICAgIGZvciAoIGkgaW4gMTpsZW4gKSB7CiAgICAgICAgb3V0W2ldIDwtIHhbaV0KICAgICAgICBpZiAoIG4gPiAxICkKICAgICAgICAgICAgZm9yICggaiBpbiAxOihuLTEpICkKICAgICAgICAgICAgICAgIG91dFtpXSA8LSBwYXN0ZTAob3V0W2ldLCB4W2kral0pCiAgICB9CiAgICBzb3J0KHRhYmxlKG91dCksIGRlY3JlYXNpbmc9VFJVRSkKfQoKb3V0X3IgPC0gbmdyYW1fcihoZXhfYywgMikKCnByaW50KGxlbmd0aChvdXRfcikpICAjIFRvdGFsIG5ncmFtcy4KCnByaW50KG91dF9yWzFdKSAjIFRoZSBtb3N0IGZyZXF1ZW50IG5ncmFtLgpgYGAKClRoZSBhYm92ZSBmdW5jdGlvbiwKdGhvdWdoIGltcGxlbWVudGF0aW9uIGlzIHN0cmFpZ2h0Zm9yd2FyZCwKaXMgdGVycmlibHkgc2xvdyBiZWNhdXNlIGl0IGRpZG4ndCB1dGlsaXplIHZlY3Rvcml6YXRpb24uCkFuZCB0aGVyZSBkb2Vzbid0IHNlZW0gdG8gaGF2ZSBhIHJlYWRpbHkgYXZhaWxhYmxlIGBwcmltaXRpdmVgIGZ1bmN0aW9uIGZvciB0aGlzIHZlcnkgcHVycG9zZS4KVGhpcyBpcyB0aGUgdXN1YWxseSBmcnVzdHJhdGVkIGV4cGVyaWVuY2Ugd2hlbiBwZW9wbGUgZmFtaWxpYXIgd2l0aCBvdGhlciBnZW5lcmFsIHB1cnBvc2UgbGFuZ3VhZ2VzIGZpcnN0IGNvbWUgdG8gdXNlIFIuCgojIyMgUmNwcCB7LX0KCkZvciBhIGZ1bmN0aW9uIHJlcXVpcmluZyBhcmJpdHJhaWx5IGxhcmdlIGV4cGxpY2l0IGxvb3AgYW5kIHRoZXJlIGlzIG5vIG9idmlvdXMgd2F5IHRvIHZlY3Rvcml6YXRpb24sCndlIGhhdmUgYFJjcHBgIGNvbWUgdG8gdGhlIHJlc2N1ZS5eW0FuZCBhbHNvIHN1cnByaXNpbmdseSwgd2UgZG9uJ3QgcmVhbGx5IGhhdmUgbWFueSBzdWNoIGNhc2VzIGluIFIgcHJvZ3JhbW1pbmcgd2hlcmUgd2UgcmVhbGx5IG5lZWQgdG8gcmVzb3J0IHRvIGl0cyBDIGludGVyZmFjZS4gVGhlIGxhbmd1YW5nZSBpcyBkb2luZyBhIHZlcnkgZ29vZCBqb2IgaW4gdmVjdG9yaXppbmcgdGhlIG1vc3QgdXRpbGl6ZWQgZnVuY3Rpb25zIGZvciB0aGUgbWFqb3JpdHkgb2YgcHJvZ3JhbW1pbmcgb3BlcmF0aW9ucywgZXNwZWNpYWxseSBpbiB0aGUgZGF0YSBhbmFseXRpY2FsIGZpZWxkLl0KCkZvciBleGFjdGx5IHRoZSBzYW1lIG1pbmltdW0gaW1wbGVtZW50YXRpb24gd2UgY2FuIHJlLXdyaXRlIGl0IGluIEMrKyB1c2luZyB0aGUgYFJjcHBgIEFQSToKCmBgYHtyIGltcG9ydF9yY3BwLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KFJjcHApCnByaW50KGluc3RhbGxlZC5wYWNrYWdlcygpWyJSY3BwIiwgIlZlcnNpb24iXSkKYGBgCgpgYGB7ciBuZ3JhbV9yY3BwfQpjcHBGdW5jdGlvbigiCiAgQ2hhcmFjdGVyVmVjdG9yIG5ncmFtUmNwcCAoQ2hhcmFjdGVyVmVjdG9yIGhleHZlY3RvciwgaW50IG5ncmFtKSB7CiAgICBpbnQgbGVuID0gaGV4dmVjdG9yLnNpemUoKSAtIChuZ3JhbSAtIDEpOwogICAgQ2hhcmFjdGVyVmVjdG9yIG91dChsZW4pOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBsZW47IGkrKykgewogICAgICBvdXQoaSkgPSBoZXh2ZWN0b3JbaV07CiAgICAgIGZvciAoaW50IGogPSAxOyBqIDwgbmdyYW07IGorKykgewogICAgICAgIG91dChpKSArPSBoZXh2ZWN0b3JbaStqXTsKICAgICAgfQogICAgfQogICAgcmV0dXJuIG91dDsKfSIpCgpuZ3JhbVJjcHAgICMgQSBwcmltaXRpdmUgZnVuY3Rpb24uCgpuZ3JhbV9yY3BwIDwtIGZ1bmN0aW9uKHgsIG4pIHsKICBuZ3JhbXMgPC0gbmdyYW1SY3BwKHgsIDIpCiAgc29ydCh0YWJsZShuZ3JhbXMpLCBkZWNyZWFzaW5nPVRSVUUpCn0KCm91dF9yY3BwIDwtIG5ncmFtX3JjcHAoaGV4X2MsIDIpCgphbGwuZXF1YWwob3V0X3JjcHAsIG91dF9yLCBjaGVjay5hdHRyaWJ1dGVzPUZBTFNFKQpgYGAKCk5vdGljZSB0aGF0IHdlIG9ubHkgaW1wbGVtZW50IHRoZSBuZ3JhbSBleHRyYWN0aW9uIGxvb3AgYnV0IGxlYXZlIHRoZSBmcmVxdWVuY3kgY291bnQgZm9yIG5hdGl2ZSBSLgpUaGlzIGlzIGJlY2F1c2UgdGhlIGB0YWJsZWAgZnVuY3Rpb24gaXMgbm90IGEgYm90dGxlbmVjayBvZiBvdXIgdGFzay4KCkluIHRoaXMgZXhhbXBsZSB3ZSB1c2UgdGhlIGBDaGFyYWN0ZXJWZWN0b3JgIGNsYXNzIGltcGxlbWVudGVkIGluIGBSY3BwYCB3aGljaCBtaW1pY3MgdGhlIGBjaGFyYWN0ZXJgIHZlY3RvciB3ZSB1c3VhbGx5IHVzZSBpbiBuYXRpdmUgUiBjb2RlLgpUaGVyZSBhcmUgbWFueSBzdWNoIGhpZ2gtbGV2ZWwgY2xhc3NlcyB3ZSBjYW4gZmluZCBpbiB0aGUgYFJjcHBgIGxpYnJhcnkuClRoZXNlIGNsYXNzZXMgd2lsbCBzYXZlIG91ciBkZXZlbG9wbWVudCB0aW1lIHdoZW4gd3JpdGluZyB0aGUgQysrIGNvZGUgc2luY2Ugd2Ugd29uJ3QgbGltaXQgb3Vyc2VsdmVzIHRvIG9ubHkgQysrIHN0YW5kYXJkIGxpYnJhcnkuCgpgYGB7ciBuZ3JhbV9iZW5jaG1hcmssIHBhZ2VkLnByaW50PUZBTFNFfQpwcmludChtaWNyb2JlbmNobWFyaygKICBuZ3JhbV9yKGhleF9jLCAyKSwKICBuZ3JhbV9yY3BwKGhleF9jLCAyKSwKICB0aW1lcz0xMAopKQpgYGAKClRoZSBgUmNwcGAgaXMgbXVjaCBmYXN0ZXIgdGhhbiB0aGUgbmF0aXZlIFIgaW1wbGVtZW50YXRpb24uCkFuZCBpdCB3aWxsIGJlIGV2ZW4gZmFzdGVyIGlmIHRoZSBkYXRhIGlzIGdldHRpbmcgbGFyZ2VyLgoKYGBge3IgcmVhZF9iaWdfZmlsZX0KIyBOb3cgdGFrZSB0aGUgZW50aXJlIFIgc291cmNlIGNvZGUgYXMgb25lIGJpZyBmaWxlLgpjb24gPC0gZmlsZShkb3dubG9hZGVkX2ZpbGUsICJyYiIpCmJpZ19oZXggPC0gcmVhZEJpbihjb24sIHdoYXQ9InJhdyIsIG49ZmlsZS5pbmZvKGRvd25sb2FkZWRfZmlsZSkkc2l6ZSkKYmlnX2hleF9jIDwtIGFzLmNoYXJhY3RlcihiaWdfaGV4KQpjbG9zZShjb24pCgpzdHIoYmlnX2hleF9jKQpgYGAKClRoaXMgdGltZSB3ZSBiZW5jaG1hcmsgd2l0aCBkaWZmZXJlbnQgZmlsZSBzaXplLAp3ZSdsbCBzZWUgdGhhdCB0aGVvcmV0aWNhbGx5IGBSY3BwYCBjYW4gYmUgaHVuZHJlZHMgb2YgdGltZXMgZmFzdGVyIHRoYW4gbmF0aXZlIFIuCgpgYGB7ciBuZ3JhbV9iZW5jaG1hcmtfc2l6ZX0KIyBCZW5jaG1hcmsgdGhlIG5hdGl2ZSBSIGNvZGUgd2l0aCB2YXJ5aW5nIGZpbGUgc2l6ZXMuCiMgU2luY2UgaXQgbWF5IHRha2UgdG9vIGxvbmcgZm9yIHRoZSByZWR1bmRhbnQgdGFza3Mgd2UnbGwgdXNlIHBhcmFsbGVsIGNvbXB1dGluZy4KIyBBbm90aGVyIG1vcmUgY2xldmVyIHdheSBpcyB0byB0aW1lIG9uLXRoZS1mbHkgd2hlbiB3ZSBwYXJzZSB0aGUgZmlsZSBhbmQgaGVuY2UgcGFyc2Ugb25seSBvbmNlLgojIEluIHRoYXQgY2FzZSB3ZSBuZWVkIHRvIG1vZGlmeSB0aGUgb3JpZ2luYWwgZnVuY3Rpb24uCiMgSGVyZSB3ZSBnbyBmb3IgdGhlIGJydXRhbCBidXQgc2ltcGxlIHdheS4KbGlicmFyeShwYXJhbGxlbCkKCmNsIDwtIG1ha2VDbHVzdGVyKGRldGVjdENvcmVzKCkgLyAyKQpjbHVzdGVyRXhwb3J0KGNsLCBjKCJiaWdfaGV4X2MiKSkKCm5fc2l6ZSA8LSAxMApzaXplcyA8LSBzZXEoMWUzLCBsZW5ndGgoYmlnX2hleF9jKSwgbGVuZ3RoLm91dD1uX3NpemUpCgp0aW1lX25ncmFtIDwtIGZ1bmN0aW9uKGZ1bmMsIHMsIG4pIHsKICBzdCA8LSBwcm9jLnRpbWUoKQogIGZ1bmMoYmlnX2hleF9jWzE6c10sIG4pCiAgZXQgPC0gcHJvYy50aW1lKCkKICAoZXQgLSBzdClbImVsYXBzZWQiXQp9CgojIFRoaXMgY291bGQgdGFrZSBhIHdoaWxlLgp0aW1lX3IgPC0gcGFyTGFwcGx5TEIoY2wsIHNpemVzLCB0aW1lX25ncmFtLCBmdW5jPW5ncmFtX3IsIG49MikKCnN0b3BDbHVzdGVyKGNsKQoKCiMgQmVuY2htYXJrIHRoZSBSY3BwIGNvZGUgd2l0aCB2YXJ5aW5nIGZpbGUgc2l6ZXMuCnRpbWVfcmNwcCA8LSBudW1lcmljKG5fc2l6ZSkKZm9yICggaSBpbiAxOmxlbmd0aChzaXplcykgKSB7CiAgdGltZV9yY3BwW2ldIDwtIHRpbWVfbmdyYW0obmdyYW1fcmNwcCwgc2l6ZXNbaV0sIDIpCn0KCm91dF90aW1lIDwtIGRhdGEudGFibGUocj11bmxpc3QodGltZV9yKSwgcmNwcD11bmxpc3QodGltZV9yY3BwKSwgc2l6ZT1zaXplcykKb3V0X3RpbWUgPC0gbWVsdChvdXRfdGltZSwgaWQudmFycz0ic2l6ZSIsIHZhcmlhYmxlLm5hbWU9ImltcGwiLCB2YWx1ZS5uYW1lPSJ0aW1lIikKZ2dwbG90KG91dF90aW1lLCBhZXMoeD1zaXplLCB5PXRpbWUsIGdyb3VwPWltcGwsIGNvbG9yPWltcGwpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4PSJGaWxlIFNpemUgKEJ5dGVzKSIsIHk9IlNlY29uZHMiLAogICAgICAgdGl0bGU9IlNwZWVkIG9uIEJpZ3JhbSBHZW5lcmF0aW9uIG9mIFZhcnlpbmcgSW5wdXQgTGVuZ3RoIikKYGBgCgojIyMgT3RoZXIgUGFja2FnZXMgey19CgpGb3IgY29tcGxldGVuZXNzLAppbiB0aGlzIHNlY3Rpb24gd2UgaW5jbHVkZSB0aGUgYmVuY2htYXJrIGZvciBzb21lIG90aGVyIHdlbGwtZGV2ZWxvcGVkIHBhY2thZ2VzIGZvciB0ZXh0IHByb2Nlc3NpbmcuClRoZXNlIHBhY2thZ2VzIGFyZSB1c3VhbGx5IHVzaW5nIFIncyBuYXRpdmUgQyBpbnRlcmZhY2Ugb3IgYFJjcHBgIHRvIGltcGxlbWVudCB0aGUgY3JpdGljYWwgcGFydHMgb2YgdGhlIGNvbXB1dGF0aW9uIGZvciB0aGVpciBpbnRlbmRlZCB0YXNrcy4KV2Ugd2lsbCBleHBlY3QgdGhlbSB0byBiZSBhbHNvIG11Y2ggbW9yZSBlZmZpY2llbnQgdGhhbiBhIG5haXZlIGltcGxlbWVudGF0aW9uIGluIG5hdGl2ZSBSIGNvZGUuCgpUaGUgZmlyc3QgcGFja2FnZSB3ZSBleHBsb3JlIGlzIFtgcXVhbnRlZGFgXShodHRwczovL2dpdGh1Yi5jb20vcXVhbnRlZGEvcXVhbnRlZGEpOgoKYGBge3IgaW1wb3J0X3F1YW50ZWRhLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHF1YW50ZWRhKQpwcmludChpbnN0YWxsZWQucGFja2FnZXMoKVsicXVhbnRlZGEiLCAiVmVyc2lvbiJdKQpgYGAKCmBgYHtyIG5ncmFtX3F1YW50ZWRhfQojIEZvciBxdWFudGVkYSB3ZSBuZWVkIHRvIGNvbnZlcnQgdGV4dCB0byBpdHMgYHRva2Vuc2AgQVBJLgojIFRoaXMgbWF5IGludHJvZHVjZSBzb21lIG92ZXJoZWFkIHNvIHdlIHdpbGwgY3JlYXRlIHR3byBmdW5jdGlvbnMgZm9yIGZhaXIgY29tcGFyaXNvbiBsYXR0ZXIuCmhleF9zIDwtIHBhc3RlKGhleF9jLCBjb2xsYXBzZT0iICIpCmhleF90IDwtIHRva2VucyhoZXhfcykKCm5ncmFtX3F1YW50ZWRhXzEgPC0gZnVuY3Rpb24oeCwgbikgewogICMgVG9rZW5pemF0aW9uIGluY2x1ZGVkLgogIG5ncmFtcyA8LSB0b2tlbnNfbmdyYW1zKHRva2Vucyh4KSwgbj1uKVtbMV1dCiAgc29ydCh0YWJsZShuZ3JhbXMpLCBkZWNyZWFzaW5nPVRSVUUpCn0KCm5ncmFtX3F1YW50ZWRhXzIgPC0gZnVuY3Rpb24odG9rLCBuKSB7CiAgIyBBc3N1bWUgcHJlLXRva2VuaXplZC4KICBuZ3JhbXMgPC0gdG9rZW5zX25ncmFtcyh0b2ssIG49bilbWzFdXQogIHNvcnQodGFibGUobmdyYW1zKSwgZGVjcmVhc2luZz1UUlVFKQp9CgpvdXRfcXVhbnRlZGEgPC0gbmdyYW1fcXVhbnRlZGFfMShoZXhfcywgbj0yKQoKYWxsLmVxdWFsKG91dF9xdWFudGVkYSwgb3V0X3IsIGNoZWNrLmF0dHJpYnV0ZXM9RkFMU0UpCmBgYAoKW2B0ZXh0MnZlY2BdKGh0dHBzOi8vZ2l0aHViLmNvbS9kc2VsaXZhbm92L3RleHQydmVjKSBpcyBhbHNvIGEgaGlnaC1wZXJmb3JtYW5jZSBwYWNrYWdlIGltcGxlbWVudGVkIHdpdGggYFJjcHBgOgoKYGBge3IgaW1wb3J0X3RleHQydmVjLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRleHQydmVjKQpwcmludChpbnN0YWxsZWQucGFja2FnZXMoKVsidGV4dDJ2ZWMiLCAiVmVyc2lvbiJdKQpgYGAKCmBgYHtyIG5ncmFtX3RleHQydmVjfQojIEFnYWluIHRvIHVzZSB0ZXh0MnZlYyB3ZSBuZWVkIHRvIGZvbGxvdyBpdHMgQVBJOgojIENvbnZlcnRpbmcgdGV4dCBpbnRvIGBpdG9rZW5gIGl0ZXJhdG9yLgojIFNpbWlsYXIgdG8gdGhlIGNhc2Ugb2YgcXVhbnRlZGEsIHdlIHVzZSB0d28gZnVuY3Rpb25zIHdoZXJlIG9uZSBleGNsdWRlcyB0aGUgY29udmVyc2lvbiBvdmVyaGVhZC4KaGV4X2kgPC0gaXRva2VuKGhleF9zLCBwcm9ncmVzc2Jhcj1GQUxTRSkKCm5ncmFtX3RleHQydmVjXzEgPC0gZnVuY3Rpb24oeCwgbj1uKSB7CiAgaXQgPC0gaXRva2VuKHgsIHByb2dyZXNzYmFyPUZBTFNFKQogIGNyZWF0ZV92b2NhYnVsYXJ5KGl0LCBuZ3JhbT1jKG4sIG4pKQp9CgpuZ3JhbV90ZXh0MnZlY18yIDwtIGZ1bmN0aW9uKGl0LCBuPW4pIHsKICBjcmVhdGVfdm9jYWJ1bGFyeShpdCwgbmdyYW09YyhuLCBuKSkKfQoKb3V0X3RleHQydmVjIDwtIG5ncmFtX3RleHQydmVjXzEoaGV4X3MsIG49MikKCiMgVGlkeSB0aGUgcmVzdWx0IHRvIGFsaWduIHdpdGggb3VyIHByZXZpb3VzIGZ1bmN0aW9ucy4Kb3V0X3RleHQydmVjIDwtIHNldE5hbWVzKG91dF90ZXh0MnZlYyR0ZXJtX2NvdW50LCBvdXRfdGV4dDJ2ZWMkdGVybSkKCmFsbC5lcXVhbChhcy50YWJsZShzb3J0KG91dF90ZXh0MnZlYywgZGVjcmVhc2luZz1UUlVFKSksIG91dF9yLCBjaGVjay5hdHRyaWJ1dGVzPUZBTFNFKQpgYGAKCkxhc3RseSwKdW5saWtlIHRoZSBwcmV2aW91cyB0d28gcGFja2FnZXMgYm90aCBhaW0gYXQgYSBsYXJnZXIgc2NvcGUgb2YgbmF0dXJhbCBsYW5ndWFnZSBwcm9jZXNzaW5nIHRhc2tzLAp0aGUgcGFja2FnZSBbYG5ncmFtYF0oaHR0cHM6Ly9naXRodWIuY29tL3dyYXRoZW1hdGljcy9uZ3JhbSkgZGVkaWNhdGVzIG9ubHkgYXQgbmdyYW0gZ2VuZXJhdGlvbiB0YXNrLAp3cml0dGVuIG1haW5seSBpbiBDOgoKYGBge3IgaW1wb3J0X25ncmFtLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KG5ncmFtKQpwcmludChpbnN0YWxsZWQucGFja2FnZXMoKVsibmdyYW0iLCAiVmVyc2lvbiJdKQpgYGAKCmBgYHtyIG5ncmFtX25ncmFtfQpuZ3JhbV9uZ3JhbSA8LSBmdW5jdGlvbih4LCBuKSB7CiAgbmdyYW0oeCwgbj1uKQp9CgpvdXRfbmdyYW0gPC0gbmdyYW0oaGV4X3MpCgojIFRpZHkgdGhlIHJlc3VsdC4Kb3V0X25ncmFtIDwtIGdldC5waHJhc2V0YWJsZShvdXRfbmdyYW0pCm91dF9uZ3JhbSA8LSBzZXROYW1lcyhvdXRfbmdyYW0kZnJlcSwgb3V0X25ncmFtJG5ncmFtcykKCmFsbC5lcXVhbChhcy50YWJsZShvdXRfbmdyYW0pLCBvdXRfciwgY2hlY2suYXR0cmlidXRlcz1GQUxTRSkKYGBgCgpOb3cgd2UgYmVuY2htYXJrIGFsbCB0aGUgYWJvdmUgaW1wbGVtZW50YXRpb25zIHVzaW5nIHRoZSBsYXJnZSBmaWxlOgoKYGBge3IgbmdyYW1fYmVuY2htYXJrX2FsbCwgcGFnZWQucHJpbnQ9RkFMU0V9CmJlbmNobWFya19hbGwgPC0gZnVuY3Rpb24oeCkgewogIAogIHhfcyA8LSBwYXN0ZSh4LCBjb2xsYXBzZT0iICIpCiAgeF90IDwtIHRva2Vucyh4X3MpCiAgeF9pIDwtIGl0b2tlbih4X3MsIHByb2dyZXNzYmFyPUZBTFNFKQogIAogIG1pY3JvYmVuY2htYXJrKAogICAgbmdyYW1fcih4LCAyKSwKICAgIG5ncmFtX3JjcHAoeCwgMiksCiAgICBuZ3JhbV9xdWFudGVkYV8xKHhfcywgMiksCiAgICBuZ3JhbV9xdWFudGVkYV8yKHhfdCwgMiksCiAgICBuZ3JhbV90ZXh0MnZlY18xKHhfcywgMiksCiAgICBuZ3JhbV90ZXh0MnZlY18yKHhfaSwgMiksCiAgICBuZ3JhbV9uZ3JhbSh4X3MsIDIpLAogICAgdGltZXM9MwogICkKfQoKeCA8LSBiaWdfaGV4X2NbMTphcy5pbnRlZ2VyKGxlbmd0aChiaWdfaGV4X2MpIC8gMTApXSAgIyBTbWFsbGVyLgpiZW5fYWxsIDwtIGJlbmNobWFya19hbGwoeCkKcHJpbnQoYmVuX2FsbCkKYGBgCgojIyBNb3ZpbmctV2luZG93IENhbGN1bGF0aW9uCgpUYXNrIGxpa2Ugbi1ncmFtIGdlbmVyYXRpb24gaXMgYSB0eXBlIG9mIG1vdmluZy13aW5kb3cgb3BlcmF0aW9uLgpTdWNoIHRhc2sgaXMgaW4gZ2VuZXJhbCBoYXJkZXIgdG8gdmVjdG9yaXplIGFuZCBoZW5jZSB3aWxsIHVzdWFsbHkgcmVzdWx0IGluIHBlcmZvcm1hbmNlIGlzc3VlIGluIFIuCkluIHRoaXMgc2VjdGlvbiB3ZSBnZW5lcmFsaXplIHRoZSBkaXNjdXNzaW9uIHRvIGEgZ2VuZXJpYyBtb3Zpbmcgd2luZG93IG9wZXJhdGlvbi4KRm9yIGlsbHVzdHJhdGlvbiB3ZSB3aWxsIHVzZSBhIHNhbXBsZSBsb2cgZmlsZSBmcm9tIGEgQXBhY2hlIHdlYiBzZXJ2ZXIgcHVibGljbHkgYXZhaWxhYmxlIGZyb20gW0xvZ2h1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2xvZ3BhaS9sb2dodWIpIChAemh1MjAxOXRvb2xzKS4KVGhlIGxvZyB3aWxsIGxvb2sgbGlrZSB0aGUgZm9sbG93aW5nIHRleHQgbGluZXM6CgpgYGB7ciBtd19kb3dubG9hZF9sb2d9CiMgaHR0cHM6Ly9naXRodWIuY29tL2xvZ3BhaS9sb2dodWIvdHJlZS9tYXN0ZXIvQXBhY2hlCnRhcmdldF9maWxlIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbG9ncGFpL2xvZ2h1Yi9tYXN0ZXIvQXBhY2hlL0FwYWNoZV8yay5sb2ciCmRvd25sb2FkZWRfZmlsZSA8LSBmaWxlLnBhdGgoVEVNUERJUiwgYmFzZW5hbWUodGFyZ2V0X2ZpbGUpKQoKaWYgKCAhZmlsZS5leGlzdHMoZG93bmxvYWRlZF9maWxlKSApIHsKICBkb3dubG9hZC5maWxlKHRhcmdldF9maWxlLCBkb3dubG9hZGVkX2ZpbGUpCn0KCmxvZ3MgPC0gcmVhZExpbmVzKGRvd25sb2FkZWRfZmlsZSwgd2Fybj1GQUxTRSkKcHJpbnQoaGVhZChsb2dzKSkKcHJpbnQobGVuZ3RoKGxvZ3MpKQpgYGAKClN1cHBvc2UgdGhlIHRhc2sgaXMgdG8gY291bnQgbnVtYmVyIG9mIHJlY29yZHMgYnkgbG9nIHR5cGUgKGBub3RpY2VgIG9yIGBlcnJvcmApIHdpdGhpbiB0aGUgbGFzdCA1IG1pbnV0ZXMgZm9yIGV2ZXJ5IDEgbWludXRlLgpUaGlzIGlzIGFuIG92ZXJsYXBwaW5nIG1vdmluZyB3aW5kb3cgb3BlcmF0aW9uIHdoZXJlIHRoZSBhY3R1YWwgd2luZG93IHNpemUgKG51bWJlciBvZiByZWNvcmRzIGludm9sdmVkIGluIGVhY2ggdGltZS1maXhlZCB3aW5kb3cpIG11c3QgYmUgZnVydGhlciBkZXRlcm1pbmVkIGJ5IHRoZSBvcmlnaW5hbCB0aW1lc3RhbXAuClRoZSBpZGVhIGlzIHJhdGhlciBzaW1wbGUgYnV0IHRoZSBhY3R1YWwgaW1wbGVtZW50YXRpb24gcHJlc2VudHMgc2V2ZXJhbCBjaGFsbGFuZ2VzIGZyb20gdGhlIGFuZ2xlIG9mIHBlcmZvcm1hbmNlIG9wdGltaXphdGlvbiBpbiBuYXRpdmUgUiBjb2RlLgoKTGV0J3MgdGlkeSB1cCB0aGUgbG9nIGxpbmVzIGZpcnN0IHRvIGhhdmUgYSBjbGVhbmVyIGlucHV0IHRvIG91ciB0YXNrOgoKYGBge3IgbXdfcGFyc2VfbG9nfQphcGFjaGVfbG9ncyA8LSBhcy5kYXRhLnRhYmxlKHN0cmluZ3I6OnN0cl9tYXRjaChsb2dzLCAiXlxcWyhbYS16QS1aMC05OiBdKylcXF0gXFxbKFthLXpdKylcXF0iKVssLTFdKQpzZXRuYW1lcyhhcGFjaGVfbG9ncywgYygidHMiLCAidHlwZSIpKQphcGFjaGVfbG9ncyA8LSBhcGFjaGVfbG9nc1ssIHRzOj1hcy5QT1NJWGN0KHRzLCBmb3JtYXQ9IiVhICViICVkICVIOiVNOiVTICV5IiwgdHo9IlVUQyIpXQphcGFjaGVfbG9ncyA8LSBhcGFjaGVfbG9nc1ssIHNlYzo9YXMuaW50ZWdlcih0cyldCgpoZWFkKGFwYWNoZV9sb2dzKQpgYGAKCiMjIyBOYXRpdmUgUiB7LX0KCkFnYWluIGxldCdzIHRyeSB0aGUgbmFpdmUgYXBwcm9hY2ggdXNpbmcgcGxhaW4gUiBjb2RlIGZpcnN0LgoKYGBge3IgbXdfcn0KbXdfY291bnRfciA8LSBmdW5jdGlvbihEVCwgd3NpemUsIGludGVyLCBzZWxlY3Q9TlVMTCkgewogICAgcmVxdWlyZShkYXRhLnRhYmxlKQogICAgc3RzIDwtIG1pbihEVCRzZWMpCiAgICBuaW50ZXIgPC0gY2VpbGluZyhkaWZmKHJhbmdlKERUJHNlYykpIC8gaW50ZXIpCiAgICBpZiAoICFpcy5udWxsKHNlbGVjdCkgKQogICAgICAgIERUIDwtIERUW3R5cGUgJWluJSBzZWxlY3RdCiAgICByZXMgPC0gbGlzdCgpCiAgICBmb3IgKCBjYSBpbiB1bmlxdWUoRFQkdHlwZSkgKSB7CiAgICAgICAgc3RzXyA8LSBzdHMKICAgICAgICBjbnQgPC0gaW50ZWdlcihuaW50ZXIpICAjIFByZS1hbGxvY2F0ZSBtZW1vcnkuCiAgICAgICAgZm9yICggaSBpbiAxOm5pbnRlciApIHsKICAgICAgICAgICAgY250W2ldIDwtIG5yb3coRFRbdHlwZSA9PSBjYV1bc2VjIDw9IHN0c18gJiBzZWMgPiBzdHNfIC0gd3NpemVdKQogICAgICAgICAgICBzdHNfIDwtIHN0c18gKyBpbnRlcgogICAgICAgIH0KICAgICAgICByZXNbW2NhXV0gPC0gY250CiAgICB9CiAgICByZXMKfQoKc3lzdGVtLnRpbWUobXdfcmVzX3IgPC0gbXdfY291bnRfcihhcGFjaGVfbG9ncywgMzAwLCA2MCkpCmBgYAoKVGhlIHJlc3VsdHMgYXJlIHR3byBudW1lcmljIHNlcmllcywKd2hpY2ggY2FuIGJlIHBsb3R0ZWQgYXMgYmVsb3cuCgpgYGB7ciBtd19wbG90fQptdl9yZXMgPC0gYXMuZGF0YS50YWJsZShtd19yZXNfcikKbXZfcmVzIDwtIG12X3Jlc1ssIGk6PS5JXQptdl9yZXMgPC0gbWVsdChtdl9yZXMsIGlkLnZhcnM9ImkiLCB2YXJpYWJsZS5uYW1lPSJsb2dfdHlwZSIsIHZhbHVlLm5hbWU9ImNvdW50IikKCmdncGxvdChtdl9yZXMsIGFlcyh4PWksIHk9Y291bnQsIGdyb3VwPWxvZ190eXBlLCBjb2xvcj1sb2dfdHlwZSkpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh4PSI1LU1pbiBNb3ZpbmcgV2luZG93IHdpdGggMS1NaW4gSW50ZXJ2YWwiLAogICAgICAgeT0iTnVtYmVyIG9mIExvZ3MiKQpgYGAKCk9uZSBzaG91bGQgbm90aWNlIHRoYXQgdGhpcyBpcyBhIHZlcnkgc21hbGwgZGF0YXNldCBidXQgb3VyIGZ1bmN0aW9uIGFscmVhZHkgZXhoaWJpdHMgYm90dGxlbmVja2VkLgpCZWZvcmUgd2UgcHJvY2VlZCB0byBpbXBsZW1lbnQgdGhlIHNhbWUgaWRlYSBidXQgaW4gUmNwcCwKbGV0J3Mgc3RydWdnbGUgYSBiaXQgbW9yZToKCmBgYHtyIG13X3JfMn0KbXdfY291bnRfcl8yIDwtIGZ1bmN0aW9uKERULCB3c2l6ZSwgaW50ZXIsIHNlbGVjdD1OVUxMKSB7CiAgICByZXF1aXJlKGRhdGEudGFibGUpCiAgICBhbGxfc2VjcyA8LSBkYXRhLnRhYmxlKHNlYz1taW4oRFQkc2VjKTptYXgoRFQkc2VjKSkKICAgIGlmICggIWlzLm51bGwoc2VsZWN0KSApCiAgICAgICAgRFQgPC0gRFRbdHlwZSAlaW4lIHNlbGVjdF0KICAgIHJlcyA8LSBsYXBwbHkoc3BsaXQoRFQsIERUJHR5cGUpLCAKICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgeCA8LSBtZXJnZSh4LCBhbGxfc2VjcywgYnk9InNlYyIsIGFsbD1UUlVFKQogICAgICAgICAgICAgICAgICAgICAgc2VjX3VuaXRfY250IDwtIHhbLCAuKGNudD1zdW0oIWlzLm5hKHR5cGUpKSksIGJ5PSJzZWMiXQogICAgICAgICAgICAgICAgICAgICAgc2VjX3VuaXRfY3VtY250IDwtIGMocmVwKDAsIHdzaXplKSwgY3Vtc3VtKHNlY191bml0X2NudCRjbnQpKQogICAgICAgICAgICAgICAgICAgICAgYXMuaW50ZWdlcih0YWlsKHNlY191bml0X2N1bWNudCwgLXdzaXplKSAtIGhlYWQoc2VjX3VuaXRfY3VtY250LCAtd3NpemUpKQogICAgICAgICAgICAgICAgICB9KQogICAgbGFwcGx5KHJlcywgZnVuY3Rpb24oeCkgeFtzZXEoMSwgbGVuZ3RoKHgpLCBpbnRlcildKQp9CgpzeXN0ZW0udGltZShtd19yZXNfcl8yIDwtIG13X2NvdW50X3JfMihhcGFjaGVfbG9ncywgMzAwLCA2MCkpCmBgYAoKYGBge3IgbXdfY2hlY2tfcmVzfQppZGVudGljYWwobXdfcmVzX3Ikbm90aWNlLCBtd19yZXNfcl8yJG5vdGljZSkgJiYgCiAgaWRlbnRpY2FsKG13X3Jlc19yJGVycm9yLCBtd19yZXNfcl8yJGVycm9yKQpgYGAKCkFwcGFyZW50bHkgd2UgY2FuIGltcHJvdmUgZHJhc3RpY2FsbHkgd2l0aCBuYXRpdmUgUiBjb2RlIGlmIHdlIGRvIHNvbWUgY3JlYXRpdmUgdHdpc3Rpbmcgb2YgdGhlIG9yaWdpbmFsIHRhc2suCkhlcmUgd2UgcmUtd29yayB0aGUgb3JpZ2luYWwgcHJvYmxlbSBieSBmb3JjZWx5IGV4cGFuZGluZyB0aGUgaW5wdXQgYGRhdGEuZnJhbWVgIHRvIGhhdmUgYXQgbGVhc3Qgb25lIHJvdyBmb3IgZXZlcnkgc2Vjb25kIGluIHRoZSBjb3ZlcmVkIHBlcmlvZCwKYW5kIHdlIGRvIGEgY3VtdWxhdGl2ZSBzdW0gYnkgc2Vjb25kIHRvIGFycml2ZSBhdCBhIHNlY29uZC1sZXZlbCBtb3Zpbmcgd2luZG93IGFuZCB0aGVuIHN1YnNldCBpdHMgbWludXRlLWxldmVsIHJlc3VsdHMuCkluIGRvaW5nIHNvIHdlIGVmZmVjdGl2ZWx5IHJlbW92ZSB0aGUgbmVlZCB0byBkbyB0aGUgbW92aW5nIHdpbmRvdyBsb29wICh3aGljaCBSIGlzIG5vdCBnb29kIGF0KSBhbmQgdHJhbnNmb3JtIHRoZSB0YXNrIHRvIHVzZSB0aGUgYnVpbHQtaW4gdmVjdG9yaXplZCBwcmltaXRpdmUgYGN1bXN1bWAgZnVuY3Rpb24uCgpUaGUgaW1wcm92ZWQgc29sdXRpb24gc3RpbGwgaGFzIHNldmVyYWwgZmxhd3M6CgoxLiBJdCB1c2VzIG1vcmUgbWVtb3J5IHRoYW4gaXQgYWN0dWFsbHkgbmVlZHMgYnkgZXhwYW5kaW5nIHRoZSBgZGF0YS5mcmFtZWAgZnJvbSBhIHNwYXJzZSByZXByZXNlbnRhdGlvbiAoYSBzZWNvbmQgd2l0aG91dCBsb2cgd2lsbCBub3QgcHJlc2VudCkgaW50byBhIGRlbnNlIG9uZTsgdGhpcyBjYW4gYmUgYSBib3R0bGVuZWNrIGZvciByZWFsbHkgaHVnZSBkYXRhc2V0cwoyLiBJdCBjb21wdXRlcyB0aGluZ3MgbW9yZSB0aGFuIHdlIG5lZWQ6IG1vdmluZyB3aW5kb3cgb3BlcmF0aW9uIGlzIHBlcmZvcm1lZCBhdCBzZWNvbmQgbGV2ZWwgYnV0IHdlIG9ubHkgbmVlZCBhIG1pbnV0ZSBsZXZlbCAoVGhpcyBjYW4gYmUgc29sdmVkIGlmIHdlIHJlZmFjdG9yIHRoZSBjb2RlIGEgYml0LCBzbyBsZXNzIG9mIGFuIGlzc3VlIGFuZCBhbHNvIGl0cyBwZXJmb3JtYW5jZSBpbXBhY3QgaXMgcXVpdGUgbGltaXRlZCkKMy4gSXQgaXMgbXVjaCBoYXJkZXIgdG8gcmVhZCBhbmQgdW5kZXJzdGFuZCB0aGUgaW50ZW50IG9mIHRoZSBjb2RlCgpBbmQsCmFzIGEgbWF0dGVyIG9mIGZhY3QsCml0IGlzIHN0aWxsIG5vdCAqc3VwZXIqIGZhc3QuCgpMZXQncyBzZWUgaWYgd2UgY2FuIGFjaGlldmUgZXZlbiBmYXN0ZXIgY29tcHV0aW5nIHRpbWUgYnkgdXNpbmcgUmNwcC4KCiMjIyBSY3BwIHstfQoKYGBge3IgbXdfcmNwcH0KY3BwRnVuY3Rpb24oIgogIE51bWVyaWNWZWN0b3IgTVdDb3VudFJjcHAoTnVtZXJpY1ZlY3RvciB0cywgaW50IHdzaXplLCBpbnQgaW50ZXIsIGludCBzdHMsIGludCBudCkgewogICAgTnVtZXJpY1ZlY3RvciBvdXQobnQpOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBudDsgaSsrKSB7CiAgICAgIE51bWVyaWNWZWN0b3IgY250cyA9IHRzWyh0cyA8PSBzdHMpICYgKHRzID4gc3RzIC0gd3NpemUpXTsKICAgICAgb3V0KGkpID0gY250cy5zaXplKCk7CiAgICAgIHN0cyArPSBpbnRlcjsKICAgIH0KICAgIHJldHVybiBvdXQ7CiAgfSIpCgptd19jb3VudF9yY3BwIDwtIGZ1bmN0aW9uKERULCB3c2l6ZSwgaW50ZXIsIHNlbGVjdD1OVUxMKSB7CiAgICByZXF1aXJlKGRhdGEudGFibGUpCiAgICBzdHMgPC0gbWluKERUJHNlYykKICAgIG5pbnRlciA8LSBjZWlsaW5nKGRpZmYocmFuZ2UoRFQkc2VjKSkgLyBpbnRlcikKICAgIGlmICggIWlzLm51bGwoc2VsZWN0KSApCiAgICAgICAgRFQgPC0gRFRbdHlwZSAlaW4lIHNlbGVjdF0KICAgIGxhcHBseShzcGxpdChEVCwgRFQkdHlwZSksIAogICAgICAgICAgIGZ1bmN0aW9uKHgpIGFzLmludGVnZXIoTVdDb3VudFJjcHAoeCRzZWMsIHdzaXplLCBpbnRlciwgc3RzLCBuaW50ZXIpKSkKfQoKc3lzdGVtLnRpbWUobXdfcmVzX3JjcHAgPC0gbXdfY291bnRfcmNwcChhcGFjaGVfbG9ncywgMzAwLCA2MCkpCmBgYAoKYGBge3IgbXdfY2hlY2tfcmVzX3JjcHB9CmlkZW50aWNhbChtd19yZXNfciRub3RpY2UsIG13X3Jlc19yY3BwJG5vdGljZSkgJiYgCiAgaWRlbnRpY2FsKG13X3Jlc19yJGVycm9yLCBtd19yZXNfcmNwcCRlcnJvcikKYGBgCgpgYGB7ciBtd19iZW5jaG1hcmssIHBhZ2VkLnByaW50PUZBTFNFfQpwcmludChtaWNyb2JlbmNobWFyaygKICBtd19jb3VudF9yKGFwYWNoZV9sb2dzLCAzMDAsIDYwKSwKICBtd19jb3VudF9yXzIoYXBhY2hlX2xvZ3MsIDMwMCwgNjApLAogIG13X2NvdW50X3JjcHAoYXBhY2hlX2xvZ3MsIDMwMCwgNjApLAogIHRpbWVzPTMKKSkKYGBgCgpBcyBvbmUgY2FuIHNlZSwKdXNpbmcgUmNwcCBpcyBicnV0YWwgYW5kIHNpbXBsZSBhbmQgZXh0cmVtZWx5IGZhc3QuCkl0IGFsc28gc3VwcG9ydHMgdmVjdG9yIHNsaWNpbmcgYXMgaW4gUiBzbyB3cml0aW5nIFJjcHAgaXMgaW5kZWVkIG1vcmUgbGlrZSB3cml0aW5nIFIgYW5kIGxlc3MgbGlrZSB3cml0aW5nIEMrKy4KCkluIHRoaXMgdG95IGV4YW1wbGUgb3VyIFJjcHAgc29sdXRpb24gaXMgbW9yZSB0aGFuIDMgdGltZXMgZmFzdGVyIHRoYW4gb3VyIGNyZWF0aXZlIHR3aXN0ZWQgYXBwcm9hY2guCkFuZCBpdCBjZXJydGFpbmx5IGNhbiBiZSBldmVuIGZhc3RlciB3aGVuIGl0IGNvbWVzIHRvIGxhcmdlciBhcHBsaWNhdGlvbi4KCiMjIFRpbWUtRGVwZW5kZW50IEZlYXR1cmUgR2VuZXJhdGlvbgoKQW5vdGhlciBjb21tb24gdXNlIGNhc2Ugc2ltaWxhciB0byBhIG1vdmluZyB3aW5kb3cgb3BlcmF0aW9uIGlzIHdoZW4gd2UgbmVlZCB0byBjYWxjdWxhdGUgZGVyaXZlZCBmZWF0dXJlcyBiYXNlZCBvbiBhIGxvb2stYmFjayB0aW1lIHdpbmRvdy4KVGhpcyBpcyB1c3VhbGx5IGZvciBhIG1hY2hpbmUgbGVhcm5pbmcgZGF0YXNldCB3aGVyZSBhbiBlbnRpdHkgY2FuIGhhdmUgdGltZSBkZXBlbmRlbnQgYmVoYXZpb3Igc3RhdGlzdGljcyBzdWNoIGFzIG51bWJlciBvZiBhIGNlcnRhaW4gYWN0aXZpdHkgaW4gdGhlIHBhc3QgMzAgZGF5cyBhdCBhbnkgZ2l2ZW4gcG9pbnQgd2hlbiBhIG1vZGVsIG5lZWRzIHRvIG1ha2UgYSBwcmVkaWN0aW9uIGFib3V0IHRoZSBmdXR1cmUuCgpGb3IgdGhpcyBzY2VuYXJpbyB3ZSB3aWxsIGNyZWF0ZSBhIFtNT09DXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9NYXNzaXZlX29wZW5fb25saW5lX2NvdXJzZSktbGlrZSBzdHVkZW50LWNvdXJzZSBkYXRhc2V0IGFzIG91ciB3b3JraW5nIGV4YW1wbGU6CgpgYGB7ciBmZV9mYWtlX2RhdGF9CiMgQXNzdW1lIHVzZXIgaXMgZW5jb2RlZCBieSBpbnRlZ2VyIGFuZCBjbGFzcyBpcyBlbmNvZGVkIGJ5IGEgc2luZ2xlIEVuZ2xpc2ggbGV0dGVyLgpjcmVhdGVfbW9vY19kYXRhIDwtIGZ1bmN0aW9uKG5sb2csIG51c2VyLCBzZWVkPTUyODQ5MSkgewogIHNldC5zZWVkKHNlZWQpCiAgZGF0IDwtIGRhdGEudGFibGUodXNlcj1zYW1wbGUoMTpudXNlciwgbmxvZywgcmVwbGFjZT1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9iPXJiZXRhKDE6bnVzZXIsIC4xLCAyKSksCiAgICAgICAgICAgICAgICAgICAgY291cnNlPXNhbXBsZShsZXR0ZXJzLCBubG9nLCByZXBsYWNlPVRSVUUsIHByb2I9MToyNiksCiAgICAgICAgICAgICAgICAgICAgdHM9cnVuaWYobmxvZywgYXMuaW50ZWdlcihhcy5QT1NJWGN0KCIyMDE1LTAxLTAxIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmludGVnZXIoYXMuUE9TSVhjdCgiMjAxNS0wNi0zMCIpKSkpCiAgZGF0WywgdDo9YXMuUE9TSVhjdCh0cywgb3JpZ2luPSIxOTcwLTAxLTAxIildCiAgc2V0b3JkZXIoZGF0LCB1c2VyLCBjb3Vyc2UsIHRzKQogIGRhdAp9CgptZGF0YSA8LSBjcmVhdGVfbW9vY19kYXRhKG5sb2c9MWU2LCBudXNlcj0xZTMpCmhlYWQobWRhdGEpCmBgYAoKVG8gaW50ZXJwcmV0IHRoZSBkYXRhc2V0LAphIHJvdyByZXByZXNlbnRzIGEgcGFpciBvZiBzdHVkaWVudC1jbGFzcyBpbnRlcmFjdGlvbiBhdCBhIGdpdmVuIHRpbWVzdGFtcC4KCk5vdyBhc3N1bWUgd2UnZCBsaWtlIHRvIGV4dHJhY3QgYSBmZWF0dXJlOgpIb3cgbWFueSB1bmlxdWUgY291cnNlcyBpbiB0b3RhbCBkaWQgYSBzdHVkZW50IHJlZ2lzdGVyIGF0IHRoZSB0aW1lIG9mIGhpcy9oZXIgbGFzdCBhY3Rpdml0eSByZWNvcmQgZm9yIGVhY2ggY291cnNlPwpGb3IgZXhhbXBsZSwKaWYgc3R1ZGVudCAxJ3MgbGFzdCBhY3Rpdml0eSBmb3IgY291cnNlIGEgaXMgdG9kYXksCnRoZW4gaG93IG1hbnkgY291cnNlcyBpbiB0b3RhbCAoaW5jbHVkaW5nIGNvdXJzZSBhKSBkaWQgc2hlIGFsc28gaGF2ZSBhdCBsZWFzdCBvbmUgaW50ZXJhY3Rpb24gcmVjb3JkPwpXZSBtYXkgd2FudCB0byB1c2UgdGhpcyBpbmZvcm1hdGlvbiBhcyBvbmUgb2YgdGhlIGZlYXR1cmUgdG8gYnVpbGQgYSBtb2RlbCB0byBwcmVkaWN0IHdoZXRoZXIgYSB1c2VyIHdpbGwgZHJvcCBvdXQgZnJvbSBhIHJlZ2lzdGVyZWQgY2xhc3MuCgpGb3IgdGhpcyB1c2UgY2FzZSB3ZSB3aWxsIGFnYWluIGJlbmNobWFyayB3aXRoIDMgZGlmZmVyZW50IGFwcHJvYWNoZXM6CgoxLiBUaGUgbmFpdmUgUiBhcHByb2FjaAoyLiBUaGUgaW1wcm92ZWQgUiBhcHByb2FjaCB1c2luZyBgZGF0YS50YWJsZWAgQVBJCjMuIFRoZSBuYWl2ZSBhcHByb2FjaCBidXQgd3JpdHRlbiBpbiBSY3BwCgojIyMgTmF0aXZlIFIgey19CgpGaXJzdCB0aGUgbmFpdmUgYXBwcm9hY2ggaW4gbmF0aXZlIFIgY29kZToKCmBgYHtyIGZlX25haXZlX3J9Cm5haXZlX3JfZnVuYyA8LSBmdW5jdGlvbihEVCkgewogIHJlcXVpcmUoZGF0YS50YWJsZSkKICBEVCA8LSBjb3B5KERUKQogIERUWywgYDo9YCh0c21pbj1taW4odHMpLCB0c21heD1tYXgodHMpKSwgYnk9InVzZXIsY291cnNlIl0KICBEVCA8LSB1bmlxdWUoRFRbLCBsaXN0KHVzZXIsIGNvdXJzZSwgdHNtaW4sIHRzbWF4KV0pCiAgcmVzIDwtIGludGVnZXIodW5pcXVlTihEVCR1c2VyKSArIHVuaXF1ZU4oRFQkY291cnNlKSkKICBjbnQgPC0gMQogIGZvciAoIGkgaW4gdW5pcXVlKERUJHVzZXIpICkgewogICAgdG1wZGF0IDwtIERUW3VzZXIgPT0gaV0KICAgIGZvciAoIGogaW4gdG1wZGF0JGNvdXJzZSApIHsKICAgICAgcmVzW2NudF0gPC0gc3VtKHRtcGRhdCR0c21pbiA8PSB0bXBkYXRbY291cnNlID09IGosIHRzbWF4XSkKICAgICAgY250IDwtIGNudCArIDEKICAgIH0KICB9CiAgY2JpbmQoRFRbLCBsaXN0KHVzZXIsIGNvdXJzZSldLCByZXMpCn0KCnN5c3RlbS50aW1lKGZlX3Jlc19yMSA8LSBuYWl2ZV9yX2Z1bmMobWRhdGEpKQpgYGAKClRoZSBpZGVhIGlzIHNpbXBsZToKd2UgbG9vcCBvdmVyIGVhY2ggc3R1ZGVudCBhbmQgaW5uZXItbG9vcCBvdmVyIGVhY2ggY291cnNlcyB0byBjYWxjdWxhdGUgdGhlIHJlcXVpcmVkIG1ldHJpYy4KU3VjaCBodWdlIG5lc3RlZCBsb29wIGlzIGRvb21lZCB0byBmYWlsIGZvciBsYXJnZSBhcHBsaWNhdGlvbi4KClRoZSByZXN1bHQgd2lsbCBsb29rIHNvbWV0aGluZyBsaWtlOgoKYGBge3IgZmVfcmVzfQpmZV9yZXNfcjFbc2FtcGxlKC5OLCAxMCldCmBgYAoKU28gZm9yIGV4YW1wbGUgdXNlciA0OTEgaGFzIGludGVyYWN0ZWQgd2l0aCAxOCBjb3Vyc2VzIChvciAxNyAqb3RoZXIqIGNvdXJzZXMpIGluIHRvdGFsIHdoZW4gc2hlIGxhc3QgaW50ZXJhY3RlZCB3aXRoIGNvdXJzZSB0IGluIHRoZSByZWNvcmRzLgoKVG8gc3RpY2sgd2l0aCBSLApsZXQncyBpbXByb3ZlIHRoZSBwZXJmb3JtYW5jZSBieSB1c2luZyBgZGF0YS50YWJsZWAncyBzcGVjaWFsICpncm91cCBieSBlYWNoIGkqIGZ1bmN0aW9uYWxpdHk6CgpgYGB7ciBmZV9iZXR0ZXJfcn0KYmV0dGVyX3JfZnVuYyA8LSBmdW5jdGlvbihEVCkgewogICAgcmVxdWlyZShkYXRhLnRhYmxlKQogICAgRFQgPC0gY29weShEVCkKICAgIERUWywgYDo9YCh0c21pbj1taW4odHMpLCB0c21heD1tYXgodHMpKSwgYnk9InVzZXIsY291cnNlIl0KICAgIHRtcGRhdDEgPC0gdW5pcXVlKERUWywgbGlzdCh1c2VyLCBjb3Vyc2UsIHRzbWluKV0pCiAgICB0bXBkYXQyIDwtIHVuaXF1ZShEVFssIGxpc3QodXNlciwgY291cnNlLCB0c21heCldKQogICAgc2V0a2V5KHRtcGRhdDEsIHVzZXIpCiAgICBzZXRrZXkodG1wZGF0MiwgdXNlcikKICAgIGNiaW5kKHRtcGRhdDFbLCBsaXN0KHVzZXIsIGNvdXJzZSldLCAKICAgICAgICAgIHJlcz10bXBkYXQxW3RtcGRhdDIsIGxpc3QocmVzPXN1bSh0c21pbiA8PSB0c21heCkpLCBieT0uRUFDSEldWywgcmVzXSkKfQoKc3lzdGVtLnRpbWUoZmVfcmVzX3IyIDwtIGJldHRlcl9yX2Z1bmMobWRhdGEpKQoKc2V0a2V5KGZlX3Jlc19yMSwgdXNlcikKaWRlbnRpY2FsKGZlX3Jlc19yMSwgZmVfcmVzX3IyKQpgYGAKClRoZSBzb2x1dGlvbiBpcyBpbmRlZWQgdmVyeSBmYXN0LgpUaGUgb25seSBkcmF3YmFjayBpcyB0aGF0IHRoZSBjb2RlIGlzIGhhcmQgdG8gcmVhZCBhbmQgdW5kZXJzdGFuZCwKYXMgaXQgYWx3YXlzIHdpbGwgYmUgd2hlbiB3ZSB0cnkgdG8gcmUtd29yayBhIHByb2JsZW0gZnJvbSBpdHMgb3JpZ2luYWwgcmVwcmVzZW50YXRpb24uCgojIyMgUmNwcCB7LX0KCkNhbiB3ZSBiZWF0IHRoZSBwZXJmb3JtYW5jZSBvZiBhIHB1cmUgYGRhdGEudGFibGVgIGFwcHJvYWNoIGJ5IHNpbXBseSBpbXBsZW1lbnQgdGhlIG5lc3RlZCBsb29wIHVzaW5nIFJjcHA/CgpgYGB7ciBmZV9yY3BwfQpjcHBGdW5jdGlvbigiCiAgTnVtZXJpY1ZlY3RvciByY3BwRnVuYyhOdW1lcmljVmVjdG9yIGZpcnN0LCBOdW1lcmljVmVjdG9yIGxhc3QpIHsKICAgIGludCBsZW4gPSBmaXJzdC5zaXplKCk7CiAgICBOdW1lcmljVmVjdG9yIG91dChsZW4pOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBsZW47IGkrKykgewogICAgICBvdXQoaSkgPSAwOwogICAgICBmb3IgKGludCBqID0gMDsgaiA8IGxlbjsgaisrKSB7CiAgICAgICAgaWYgKGZpcnN0KGopIDw9IGxhc3QoaSkpIHsKICAgICAgICAgIG91dChpKSArPSAxOwogICAgICAgIH0KICAgICAgfQogICAgfQogICAgcmV0dXJuIG91dDsKICB9IikKCnJjcHBfZnVuYyA8LSBmdW5jdGlvbihEVCkgewogIHJlcXVpcmUoZGF0YS50YWJsZSkKICBEVCA8LSBjb3B5KERUKQogIERUWywgYDo9YCh0c21pbj1taW4odHMpLCB0c21heD1tYXgodHMpKSwgYnk9InVzZXIsY291cnNlIl0KICBEVCA8LSB1bmlxdWUoRFRbLCBsaXN0KHVzZXIsIGNvdXJzZSwgdHNtaW4sIHRzbWF4KV0pCiAgY2JpbmQoRFRbLCBsaXN0KHVzZXIsIGNvdXJzZSldLAogICAgICAgIHJlcz1hcy5pbnRlZ2VyKERUWywgbGlzdChyZXM9cmNwcEZ1bmMoLlNEJHRzbWluLCAuU0QkdHNtYXgpKSwgYnk9InVzZXIiXVssIHJlc10pKQp9CgpzeXN0ZW0udGltZShmZV9yZXNfY3BwIDwtIHJjcHBfZnVuYyhtZGF0YSkpCgpzZXRrZXkoZmVfcmVzX2NwcCwgdXNlcikKaWRlbnRpY2FsKGZlX3Jlc19jcHAsIGZlX3Jlc19yMSkKYGBgCgpgYGB7ciwgcGFnZWQucHJpbnQ9RkFMU0V9CnByaW50KG1pY3JvYmVuY2htYXJrKGJldHRlcl9yX2Z1bmMobWRhdGEpLAogICAgICAgICAgICAgICAgICAgICByY3BwX2Z1bmMobWRhdGEpLCAKICAgICAgICAgICAgICAgICAgICAgdGltZXM9MTApKQpgYGAKCkl0IHR1cm5zIG91dCB0aGF0LApSY3BwIGlzIHN0aWxsIHRoZSBmYXN0ZXN0IQpCdXQgdGhlIGRpZmZlcmVuY2UgaXMgbm90IHNpZ25pZmljYW50LgpXZSBzaG91bGRuJ3QgYmUgc3VycHJpc2VkIGlmIHdlIGtub3cgdGhhdCBgZGF0YS50YWJsZWAgaXRzZWxmIGlzIGluZGVlZCB3cml0dGVuIGluIEMuCgojIEZpbmFsIFdyYXAtVXAKCkluIHRoaXMgbm90ZWJvb2sgd2UndmUgZGVtb25zdHJhdGVkIHRoZSBrZXkgY29uY2VwdCB0byB3cml0ZSBhIGhpZ2gtcGVyZm9ybWFuY2UgUiBwcm9ncmFtOgp0byB2ZWN0b3JpemUgYW5kIHRvIGF2b2lkIG1lbW9yeSBjb3B5LgpUaGV5IHdpbGwgd29yayA5MCUgb2YgdGhlIHRpbWUgdG8gbWFrZSBzdXJlIG91ciBSIHByb2dyYW0gaXMgZmFzdCBlbm91Z2guCkJ1dCB0aGVyZSBhcmUgdXNlIGNhc2VzIHdoZXJlIHRoZSBjb25jZXB0LAplc3BlY2lhbGx5IHZlY3Rvcml6YXRpb24sCmlzIGhhcmQgdG8gYXBwbHkuClRvIG92ZXJjb21lIHN1Y2ggcHJvYmxlbSB3ZSBjYW46CgoxLiBSZS13b3JrIHRoZSBvcmlnaW5hbCBwcm9ibGVtIHdpdGggYW4gYWx0ZXJuYXRpdmUgYWxnb3JpdGhtIHRoYXQgY2FuIGJlIHZlY3Rvcml6ZWQKMi4gU3VydmV5IGFueSBleGlzdGluZyBwYWNrYWdlIHRoYXQgYWxyZWFkeSBoYW5kbGVzIHN1Y2ggY2FzZSBlZmZpY2llbnRseQozLiBJbXBsZW1lbnQgdGhlIGNvcmUgb2Ygb3VyIGFsZ29yaXRobSBpbiBSY3BwCgpPYnZpb3VzbHkgdGhlIGxhc3Qgb3B0aW9uIGlzIHRoZSBtb3N0IGZsZXhpYmxlIG9uZS4KQW5kIHdlJ3ZlIGFsc28gZGVtb25zdHJhdGVkIDMgZGlmZmVyZW50IGNvbW1vbiB1c2UgY2FzZXMgd2hlcmUgUmNwcCBjYW4gZ2FpbiB0cmVtZW5kb3VzIGltcHJvdmVtZW50IGluIGNvbXB1dGluZyB0aW1lLAp3aXRoIG5vdCByZWFsbHkgbXVjaCBlZmZvcnQgdG8gaW1wbGVtZW50LgoKVGhlIGtleSB0byB1c2luZyBSY3BwIHdpdGggZWFzZSBpcyB0byBpZGVudGlmeSBvbmx5IHRoZSBib3R0bGVuZWNrIGluIHRoZSBhbGdvcml0aG0gKHdoaWNoIGlzIHVzdWFsbHkgYSB2ZXJ5IHNtYWxsIHBhcnQgb2YgaXQpIGFuZCBpbXBsZW1lbnQgb25seSB0aGUgYm90dGxlbmVjayBwYXJ0IGluIFJjcHAuCgojIFJlZmVyZW5jZXMK