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.
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:
- Vectorization
- 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.
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.
# List all files in the source and count the extensions.
library(data.table)
src_dir <- file.path(tools::file_path_sans_ext(downloaded_file, compression=TRUE), "src")
src_files <- list.files(src_dir, recursive=TRUE, full.names=TRUE)
src_exts <- tools::file_ext(src_files)
src_exts <- src_exts[src_exts != ""]
ext_counts <- as.data.table(sort(table(src_exts), decreasing=TRUE), keep.rownames=TRUE)
setnames(ext_counts, c("ext", "count"))
ext_counts[, ext:=factor(ext, levels=rev(ext))]
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.
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.
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.
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.
# Benchmark the native R code with varying file sizes.
# Since it may take too long for the redundant tasks we'll use parallel computing.
# Another more clever way is to time on-the-fly when we parse the file and hence parse only once.
# In that case we need to modify the original function.
# Here we go for the brutal but simple way.
library(parallel)
cl <- makeCluster(detectCores() / 2)
clusterExport(cl, c("big_hex_c"))
n_size <- 10
sizes <- seq(1e3, length(big_hex_c), length.out=n_size)
time_ngram <- function(func, s, n) {
st <- proc.time()
func(big_hex_c[1:s], n)
et <- proc.time()
(et - st)["elapsed"]
}
# This could take a while.
time_r <- parLapplyLB(cl, sizes, time_ngram, func=ngram_r, n=2)
stopCluster(cl)
# Benchmark the Rcpp code with varying file sizes.
time_rcpp <- numeric(n_size)
for ( i in 1:length(sizes) ) {
time_rcpp[i] <- time_ngram(ngram_rcpp, sizes[i], 2)
}
out_time <- data.table(r=unlist(time_r), rcpp=unlist(time_rcpp), size=sizes)
out_time <- melt(out_time, id.vars="size", variable.name="impl", value.name="time")
ggplot(out_time, aes(x=size, y=time, group=impl, color=impl)) +
geom_line() +
geom_point() +
labs(x="File Size (Bytes)", y="Seconds",
title="Speed on Bigram Generation of Varying Input Length")
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"
# For quanteda we need to convert text to its `tokens` API.
# This may introduce some overhead so we will create two functions for fair comparison latter.
hex_s <- paste(hex_c, collapse=" ")
hex_t <- tokens(hex_s)
ngram_quanteda_1 <- function(x, n) {
# Tokenization included.
ngrams <- tokens_ngrams(tokens(x), n=n)[[1]]
sort(table(ngrams), decreasing=TRUE)
}
ngram_quanteda_2 <- function(tok, n) {
# Assume pre-tokenized.
ngrams <- tokens_ngrams(tok, n=n)[[1]]
sort(table(ngrams), decreasing=TRUE)
}
out_quanteda <- ngram_quanteda_1(hex_s, n=2)
all.equal(out_quanteda, out_r, check.attributes=FALSE)
[1] TRUE
text2vec
is also a high-performance package implemented with Rcpp
:
[1] "0.5.1"
# Again to use text2vec we need to follow its API:
# Converting text into `itoken` iterator.
# Similar to the case of quanteda, we use two functions where one excludes the conversion overhead.
hex_i <- itoken(hex_s, progressbar=FALSE)
ngram_text2vec_1 <- function(x, n=n) {
it <- itoken(x, progressbar=FALSE)
create_vocabulary(it, ngram=c(n, n))
}
ngram_text2vec_2 <- function(it, n=n) {
create_vocabulary(it, ngram=c(n, n))
}
out_text2vec <- ngram_text2vec_1(hex_s, n=2)
# Tidy the result to align with our previous functions.
out_text2vec <- setNames(out_text2vec$term_count, out_text2vec$term)
all.equal(as.table(sort(out_text2vec, decreasing=TRUE)), out_r, check.attributes=FALSE)
[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:
benchmark_all <- function(x) {
x_s <- paste(x, collapse=" ")
x_t <- tokens(x_s)
x_i <- itoken(x_s, progressbar=FALSE)
microbenchmark(
ngram_r(x, 2),
ngram_rcpp(x, 2),
ngram_quanteda_1(x_s, 2),
ngram_quanteda_2(x_t, 2),
ngram_text2vec_1(x_s, 2),
ngram_text2vec_2(x_i, 2),
ngram_ngram(x_s, 2),
times=3
)
}
x <- big_hex_c[1:as.integer(length(big_hex_c) / 10)] # Smaller.
ben_all <- benchmark_all(x)
print(ben_all)
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
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:
mw_count_r_2 <- function(DT, wsize, inter, select=NULL) {
require(data.table)
all_secs <- data.table(sec=min(DT$sec):max(DT$sec))
if ( !is.null(select) )
DT <- DT[type %in% select]
res <- lapply(split(DT, DT$type),
function(x) {
x <- merge(x, all_secs, by="sec", all=TRUE)
sec_unit_cnt <- x[, .(cnt=sum(!is.na(type))), by="sec"]
sec_unit_cumcnt <- c(rep(0, wsize), cumsum(sec_unit_cnt$cnt))
as.integer(tail(sec_unit_cumcnt, -wsize) - head(sec_unit_cumcnt, -wsize))
})
lapply(res, function(x) x[seq(1, length(x), inter)])
}
system.time(mw_res_r_2 <- mw_count_r_2(apache_logs, 300, 60))
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:
- 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
- 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)
- 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
cppFunction("
NumericVector MWCountRcpp(NumericVector ts, int wsize, int inter, int sts, int nt) {
NumericVector out(nt);
for (int i = 0; i < nt; i++) {
NumericVector cnts = ts[(ts <= sts) & (ts > sts - wsize)];
out(i) = cnts.size();
sts += inter;
}
return out;
}")
mw_count_rcpp <- function(DT, wsize, inter, select=NULL) {
require(data.table)
sts <- min(DT$sec)
ninter <- ceiling(diff(range(DT$sec)) / inter)
if ( !is.null(select) )
DT <- DT[type %in% select]
lapply(split(DT, DT$type),
function(x) as.integer(MWCountRcpp(x$sec, wsize, inter, sts, ninter)))
}
system.time(mw_res_rcpp <- mw_count_rcpp(apache_logs, 300, 60))
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.
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:
# Assume user is encoded by integer and class is encoded by a single English letter.
create_mooc_data <- function(nlog, nuser, seed=528491) {
set.seed(seed)
dat <- data.table(user=sample(1:nuser, nlog, replace=TRUE,
prob=rbeta(1:nuser, .1, 2)),
course=sample(letters, nlog, replace=TRUE, prob=1:26),
ts=runif(nlog, as.integer(as.POSIXct("2015-01-01")),
as.integer(as.POSIXct("2015-06-30"))))
dat[, t:=as.POSIXct(ts, origin="1970-01-01")]
setorder(dat, user, course, ts)
dat
}
mdata <- create_mooc_data(nlog=1e6, nuser=1e3)
head(mdata)
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:
- The naive R approach
- The improved R approach using
data.table
API
- The naive approach but written in Rcpp
Native R
First the naive approach in native R code:
naive_r_func <- function(DT) {
require(data.table)
DT <- copy(DT)
DT[, `:=`(tsmin=min(ts), tsmax=max(ts)), by="user,course"]
DT <- unique(DT[, list(user, course, tsmin, tsmax)])
res <- integer(uniqueN(DT$user) + uniqueN(DT$course))
cnt <- 1
for ( i in unique(DT$user) ) {
tmpdat <- DT[user == i]
for ( j in tmpdat$course ) {
res[cnt] <- sum(tmpdat$tsmin <= tmpdat[course == j, tsmax])
cnt <- cnt + 1
}
}
cbind(DT[, list(user, course)], res)
}
system.time(fe_res_r1 <- naive_r_func(mdata))
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:
better_r_func <- function(DT) {
require(data.table)
DT <- copy(DT)
DT[, `:=`(tsmin=min(ts), tsmax=max(ts)), by="user,course"]
tmpdat1 <- unique(DT[, list(user, course, tsmin)])
tmpdat2 <- unique(DT[, list(user, course, tsmax)])
setkey(tmpdat1, user)
setkey(tmpdat2, user)
cbind(tmpdat1[, list(user, course)],
res=tmpdat1[tmpdat2, list(res=sum(tsmin <= tsmax)), by=.EACHI][, res])
}
system.time(fe_res_r2 <- better_r_func(mdata))
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?
cppFunction("
NumericVector rcppFunc(NumericVector first, NumericVector last) {
int len = first.size();
NumericVector out(len);
for (int i = 0; i < len; i++) {
out(i) = 0;
for (int j = 0; j < len; j++) {
if (first(j) <= last(i)) {
out(i) += 1;
}
}
}
return out;
}")
rcpp_func <- function(DT) {
require(data.table)
DT <- copy(DT)
DT[, `:=`(tsmin=min(ts), tsmax=max(ts)), by="user,course"]
DT <- unique(DT[, list(user, course, tsmin, tsmax)])
cbind(DT[, list(user, course)],
res=as.integer(DT[, list(res=rcppFunc(.SD$tsmin, .SD$tsmax)), by="user"][, res]))
}
system.time(fe_res_cpp <- rcpp_func(mdata))
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.
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:
- Re-work the original problem with an alternative algorithm that can be vectorized
- Survey any existing package that already handles such case efficiently
- 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.
References
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.
Selivanov, Dmitriy, and Qing Wang. 2018. Text2vec: Modern Text Mining Framework for R. https://CRAN.R-project.org/package=text2vec.
LS0tCnRpdGxlOiAiSGlnaCBQZXJmb3JtYW5jZSBDb21wdXRpbmcgaW4gUiB1c2luZyBgUmNwcGAiCnN1YnRpdGxlOiAiIgphdXRob3I6Ci0gbmFtZTogS3lsZSBDaHVuZwogIGFmZmlsaWF0aW9uOgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlYiAlWScpYCBMYXN0IFVwZGF0ZWQgKDMwIFNlcCAyMDE5IEZpcnN0IFVwbG9hZGVkKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA0CiAgICB0b2NfZmxvYXQ6IHllcwogICAgaW5jbHVkZXM6CiAgICAgIGluX2hlYWRlcjogL3RtcC9tZXRhX2hlYWRlci5odG1sCiAgY29kZV9kb3dubG9hZDogdHJ1ZQpiaWJsaW9ncmFwaHk6IHJjcHAuYmliCm5vY2l0ZTogfAogIEBSLnV0aWxzCiAgQGRhdGEudGFibGUKICBAZ2dwbG90MgogIEB3b3JkY2xvdWQyCiAgQHJjcHAxCiAgQHJjcHAyCiAgQHJjcHAzCiAgQHF1YW50ZWRhCiAgQHRleHQydmVjCiAgQG5ncmFtCiAgQG1pY3JvYmVuY2htYXJrCiAgQHN0cmluZ3IKICBAbHVicmlkYXRlCmFic3RyYWN0OiB8CiAgUiBpcyBpbmhlcmVudGx5IGEgaGlnaC1wZXJmb3JtYW5jZSBzY3JpcHRpbmcgbGFuZ3VhZ2Ugc2luY2UgaXRzIGNvcmUgaXMgd3JpdHRlbiBpbiBDLiBXcml0aW5nIGEgaGlnaC1wZXJmb3JtYW5jZSBSIHByb2dyYW0sIGhvd2V2ZXIsIG1heSBub3QgYmUgYXMgc3RyYWlnaHRmb3J3YXJkIGFzIGl0IGlzIGZvciBzb21lIG90aGVyIGdlbmVyYWwgcHVycG9zZSBsYW5ndWFnZXMuIFRoaXMgbm90ZW5vb2sgZGVtb25zdHJhdGVzIHF1aWNrbHkgdGhlIGtleSBjb25jZXB0IGluIHdyaXRpbmcgYSBnb29kIFIgcHJvZ3JhbSBhbmQgYSBmYWN0IGNoZWNrIGFib3V0IHRoZSBkZWNvbXBvc2l0aW9uIG9mIHRoZSBsYW5ndWFnZSBzb3VyY2UgY29kZS4gSXQgYWxzbyBkZW1vbnN0cmF0ZXMgaW4gbW9yZSBkZXRhaWxzIHdpdGggc2V2ZXJhbCB3b3JraW5nIGV4YW1wbGVzIGFuZCBiZW5jaG1hcmtzIG9uIGhvdyB3ZSBjYW4gYm9vc3QgdGhlIHBlcmZvcm1hbmNlIHdoZW4gdGhlIHVuZGVybHlpbmcgdGFzayBpcyBub3QgZWFzeSB0byBvcHRpbWl6ZSBpbiBuYXRpdmUgUiBjb2RlOiBieSB1c2luZyBSY3BwLgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpvcHRpb25zKHdpZHRoPTEyMCkKYGBgCgpgYGB7ciBtZXRhLCBpbmNsdWRlPUZBTFNFfQpkaXIuY3JlYXRlKCIvdG1wIiwgc2hvd1dhcm5pbmdzPUZBTFNFKQptZXRhX2hlYWRlcl9maWxlIDwtIGZpbGUoIi90bXAvbWV0YV9oZWFkZXIuaHRtbCIpCgojIEFkZCBvcGVuIGdyYXBoIG1ldGEuCm1ldGEgPC0gYygKICAgICc8bWV0YSBuYW1lPSJhdXRob3IiIGNvbnRlbnQ9Ikt5bGUgQ2h1bmciPicsCiAgICAnPG1ldGEgcHJvcGVydHk9Im9nOnRpdGxlIiBjb250ZW50PSJIaWdoIFBlcmZvcm1hbmNlIENvbXB1dGluZyBpbiBSIHVzaW5nIFJjcHAiPicsCiAgICAnPG1ldGEgcHJvcGVydHk9Im9nOnR5cGUiIGNvbnRlbnQ9ImFydGljbGUiPicsCiAgICAnPG1ldGEgcHJvcGVydHk9Im9nOnVybCIgY29udGVudD0iaHR0cHM6Ly9ldmVyZGFyay5naXRodWIuaW8vazkvcHJvZ3JhbW1pbmcvcmNwcC9yY3BwLm5iLmh0bWwiPicsCiAgICAnPG1ldGEgcHJvcGVydHk9Im9nOmltYWdlIiBjb250ZW50PSJodHRwczovL2V2ZXJkYXJrLmdpdGh1Yi5pby9rOS9hc3NldHMvcmxhbmcucG5nIj4nLAogICAgJzxtZXRhIHByb3BlcnR5PSJvZzpkZXNjcmlwdGlvbiIgY29udGVudD0iQSBTaG93Y2FzZSBvZiBoaWdoIHBlcmZvcm1hbmNlIGNvbXB1dGluZyBpbiBSIHVzaW5nIFJjcHAuIj4nCikKY29udGVudHMgPC0gbWV0YQoKIyBBZGQgR2l0aHViIGNvcm5lci4KZ2l0aHViX2Nvcm5lcl9zdmcgPC0gIi4uLy4uLy4uL2Fzc2V0cy9naXRodWJfY29ybmVyLmh0bWwiCmdpdGh1Yl9jb3JuZXJfY29uZiA8LSBsaXN0KGdpdGh1Yl9saW5rPSJodHRwczovL2dpdGh1Yi5jb20vZXZlcmRhcmsvazkvdHJlZS9tYXN0ZXIvcHJvZ3JhbW1pbmcvci9yY3BwIikKY29udGVudHMgPC0gYyhjb250ZW50cywgc3RyaW5ncjo6c3RyX2ludGVycChyZWFkTGluZXMoZ2l0aHViX2Nvcm5lcl9zdmcpLCBnaXRodWJfY29ybmVyX2NvbmYpKQp3cml0ZUxpbmVzKGNvbnRlbnRzLCBtZXRhX2hlYWRlcl9maWxlKQoKY2xvc2UobWV0YV9oZWFkZXJfZmlsZSkKYGBgCgotLS0KClRoZSBub3RlYm9vayBpcyBhIGZ1bGwgcmUtd3JpdGluZyBvZiBpdHMgW29yaWdpbmFsIHZlcnNpb25dKGh0dHA6Ly9ldmVyZGFyay5naXRodWIuaW8vcmNwcF9saWdodG5pbmdfZHNjMjAxNS8pIGZvciBhIGxpZ2h0bmluZyB0YWxrIGF0IERhdGEgU2NpZW5jZSBDb25mZXJlbmNlIDIwMTUgYXQgVGFpcGVpLgpUaGUgY29kaW5nIGV4YW1wbGVzIGluIHRoZSBvcmlnaW5hbCBub3RlYm9vayBhcmUgZm9yIExpbnV4L21hY09TIHVzZXJzIG9ubHkuClRoZSByZXZpc2VkIG5vdGVib29rIGhlcmUgaXMgcGxhdGZvcm0taW5kZXBlbmRlbnQuCgotLS0KCiMgTW90aXZhdGlvbgoKVGhlIHBlcmZvcm1hbmNlIG9mIGEgUiBwcm9ncmFtIGNhbiB2YXJ5IGEgbG90LApkZXBlbmRpbmcgb24gd2hldGhlciBpdCBpcyB3cml0dGVuIGJ5IGFuIGV4cGVyaWVuY2VkIFIgcHJvZ3JhbW1lciBvciBhIG5ld2JpZSB3aG8gZG9uJ3Qga25vdyBob3cgdG8gcHJvcGVybHkgd3JpdGUgUi4KVW5mb3J0dW5hdGVseSwKdW5saWtlIG90aGVyIGdlbmVyYWwgcHVycG9zZSBsYW5nYXVnZXMsCml0IGlzIG11Y2ggbW9yZSBsaWtlbHkgdG8gd3JpdGUgaW1wcm9wZXIgUiBjb2RlcyB3aGljaCBydW4gZW1iYXJyYXNzaW5nbHkgc2xvdy4KVHdvIGltcG9ydGFudCBhbmQgY29tbW9uIGZhY3RvcnMgdGhhdCBhZmZlY3QgdGhlIHBlcmZvcm1hbmNlIG9mIFIgY29kZToKCjEuIFZlY3Rvcml6YXRpb24KMi4gTWVtb3J5IENvcHkKCkluIGdlbmVyYWwsCndlIHNob3VsZCB1c2UgdmVjdG9yaXphdGlvbiBhcyBtdWNoIGFzIHBvc3NpYmxlIGFuZCAqYXZvaWQqIG1lbW9yeSBjb3B5IGFzIG11Y2ggYXMgcG9zc2libGUuCkZvciBleGFtcGxlIHRoZSBmb2xsb3dpbmcgY29kZSBpcyBURVJSSUJMRToKCmBgYHtyIHRlcnJpYmxlfQpudW1iZXJzIDwtIDE6MWU1Cgpwb3dfYWxsXzEgPC0gZnVuY3Rpb24obnVtYmVycykgewogIG91dCA8LSBjKCkKICBmb3IgKCBuIGluIG51bWJlcnMgKSB7CiAgICBvdXQgPC0gYyhvdXQsIG5eMikgICMgTWVtb3J5IGNvcHkgaXMgaGFwcGVuaW5nIGhlcmUuCiAgfQogIG91dAp9CgpzeXN0ZW0udGltZShvdXRfMSA8LSBwb3dfYWxsXzEobnVtYmVycykpCmBgYAoKV2UgYXJlIGp1c3QgdHJ5aW5nIHRvIGNhbGN1bGF0ZSB0aGUgYmFzZS0yIHBvd2VyIG9mIGEgc2VxdWVuY2Ugb2YgbnVtYmVycy4KSXQgd2lsbCBiZSB2ZXJ5IHN0cmFnaHRmb3J3YXJkIHRvIGp1c3QgbG9vcCBvdmVyIHRoZSBudW1iZXJzIGFuZCBjb21wdXRlIHRoZSByZXN1bHQgZm9yIGEgZ2VuZXJhbCBwdXJwb3NlIGxhbmd1YWdlLgoKQnV0IE5PVCBmb3IgUi4KClRoZSBhYm92ZSBjb2RlIGlzIGp1c3QgV1JPTkcgYmVjYXVzZSBpdCB2aW9sYXRlcyBib3RoIG91ciB0d28gcHJpbmNpcGxlczoKaXQgZGlkbid0IHZlY3Rvcml6ZSB3aGVuIGl0IGNvdWxkLCBhbmQgaXQgZGlkbid0IGF2b2lkIG1lbW9yeSBjb3B5LgpMZXQncyBmaXJzdCBmaXggdGhlIHNlY29uZCBpc3N1ZSBhYm91dCB0aGUgbWVtb3J5IGNvcHk6CgpgYGB7ciBzdGlsbF90ZXJyaWJsZX0KcG93X2FsbF8yIDwtIGZ1bmN0aW9uKG51bWJlcnMpIHsKICBvdXQgPC0gbnVtZXJpYyhsZW5ndGgobnVtYmVycykpICAjIFByZS1hbGxvY2F0ZSBtZW1vcnkuCiAgZm9yICggaSBpbiAxOmxlbmd0aChudW1iZXJzKSApIHsKICAgIG91dFtpXSA8LSBudW1iZXJzW2ldXjIKICB9CiAgb3V0Cn0KCnN5c3RlbS50aW1lKG91dF8yIDwtIHBvd19hbGxfMihudW1iZXJzKSkKc3RvcGlmbm90KGFsbC5lcXVhbChvdXRfMSwgb3V0XzIpKQpgYGAKClRoZSBvdXRwdXQgdmVjdG9yIHNpemUgaXMgcHJlLWRldGVybWluZWQgZm9yIG91ciBzZWNvbmQgYXR0ZW1wdCwKd2hpY2ggYXZvaWQgY29waWVzIGhhcHBlbmluZyBhZ2FpbiBhbmQgYWdhaW4gZHVyaW5nIHRoZSB1bm5lY2Vzc2FyeSBjb25jYXRlbmF0aW9uIGluIHRoZSBsb29wLgoKU3RpbGwgdGhlIGNvZGUgaXMgdGVycmlibGUsCmJlY2F1c2UgaXQgZGlkbid0ICp2ZWN0b3JpemUqIHdoZW4gaXQgYWN0dWFsbHkgY291bGQ6CgpgYGB7ciBnb29kfQpwb3dfYWxsXzMgPC0gZnVuY3Rpb24obnVtYmVycykgewogIG51bWJlcnNeMgp9CgpzeXN0ZW0udGltZShvdXRfMyA8LSBwb3dfYWxsXzMobnVtYmVycykpCnN0b3BpZm5vdChhbGwuZXF1YWwob3V0XzEsIG91dF8zKSkKYGBgCgpUaGUgZGlmZmVyZW5jZSBtYXkgbm90IGJlIG9idmlvdXMgYXQgdGhlIGZpcnN0IGdsYW5jZSBiZWNhdXNlIHRoZSB0YXNrIGlzIHRvbyB0cml2aWFsLgpOb3cgZm9yIGEgc2VyaW91cyBiZW5jaG1hcmtpbmcgYXQgbWljcm9zZWNvbmQtbGV2ZWw6CgpgYGB7ciBiZW5jaG1hcmtfdmVjdG9yaXphdGlvbiwgcGFnZWQucHJpbnQ9RkFMU0V9CmxpYnJhcnkobWljcm9iZW5jaG1hcmspCgpudW1iZXJzIDwtIDE6MWU0ICAjIE1ha2UgaXQgc21hbGxlciBmb3IgZmFzdGVyIGJlbmNobWFya2luZy4KCmJlbiA8LSBtaWNyb2JlbmNobWFyaygKICBwb3dfYWxsXzEobnVtYmVycyksCiAgcG93X2FsbF8yKG51bWJlcnMpLAogIHBvd19hbGxfMyhudW1iZXJzKSwKICB0aW1lcz0xMAopCgpwcmludChiZW4pCmBgYAoKQSBwcm9wZXJseSB3cml0dGVuIFIgcHJvZ3JhbSBjYW4gYmUgb2Ygc2V2ZXJhbCBvcmRlcnMgb2YgbWFnbml0dWRlIGZhc3Rlci4KCkV2ZW4gaWYgd2UgYWxyZWFkeSBjYXJlZnVsbHkgd3JpdGUgb3VyIFIgY29kZSwKaXQgbWF5IHN0aWxsIG9jY3VyIHRoYXQgd2Ugd2FudCBldmVuIGZhc3RlciBzcGVlZC4KVGhpcyBpcyBlc3BlY2lhbGx5IHRydWUgd2hlbiB0aGVyZSBpcyBubyB0cml2aWFsIHdheSBvZiB2ZWN0b3JpemF0aW9uIGZvciB0aGUgaW1wbGVtZW50YXRpb24gZGVzaXJlZC4KVGhpcyBpcyB0aGUgbWFpbiB0b3BpYyBvZiB0aGlzIG5vdGVib29rOgp3ZSBjYW4gc3RpbGwgYmUgZmFzdGVyLApieSB1c2luZyBSJ3MgQyBpbnRlcmZhY2UuCgpgUmNwcGAgaXMgdGhlIG1vc3QgZG9taW5hbnQgd2F5IG9mIGFjaGlldmluZyB0aGF0LgpCdXQgYmVmb3JlIG91ciBkZWVwIGRpdmUsCmxldCdzIHVuZGVyc3RhbmQgKndoYXQgaXMgUiogYSBiaXQgbW9yZS4KCiMgRGVjb21wb3NlIHRoZSBSIFNvdXJjZSBDb2RlCgpMZXQncyBleGFtaW5lIHRoZSBzb3VyY2UgY29kZSBvZiB0aGUgUiBsYW5ndWFnZS4KCmBgYHtyIGRvd25sb2FkX3Jfc291cmNlLCByZXN1bHRzPSJoaWRlIiwgd2FybmluZz1GQUxTRX0KIyBEb3dubG9hZCB0aGUgc291cmNlIGNvZGUgb2YgUiBsYW5nLgojIFRoaXMgbWF5IHRha2UgYSB3aGlsZS4KClRFTVBESVIgPC0gdGVtcGRpcigpCnRhcmdldF9maWxlIDwtICJodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9zcmMvYmFzZS9SLTMvUi0zLjYuMS50YXIuZ3oiCmRvd25sb2FkZWRfZmlsZSA8LSBmaWxlLnBhdGgoVEVNUERJUiwgYmFzZW5hbWUodGFyZ2V0X2ZpbGUpKQoKaWYgKCAhZmlsZS5leGlzdHMoZG93bmxvYWRlZF9maWxlKSApIHsKICBkb3dubG9hZC5maWxlKHRhcmdldF9maWxlLCBkb3dubG9hZGVkX2ZpbGUpCn0KCnVudGFyKGRvd25sb2FkZWRfZmlsZSwgZXhkaXI9VEVNUERJUikgICMgVGhpcyBtYXkgZ2VuZXJhdGUgd2FybmluZ3MgdW5kZXIgV2luZG93cy4KYGBgCgpXZSdkIGxpa2UgdG8ga25vdyB0aGUgZmlsZSB0eXBlIGRpc3RyaWJ1dGlvbiBpbiB0aGUgc291cmNlIGNvZGUuCgpgYGB7ciByZWFkX3Jfc291cmNlfQojIExpc3QgYWxsIGZpbGVzIGluIHRoZSBzb3VyY2UgYW5kIGNvdW50IHRoZSBleHRlbnNpb25zLgoKbGlicmFyeShkYXRhLnRhYmxlKQoKc3JjX2RpciA8LSBmaWxlLnBhdGgodG9vbHM6OmZpbGVfcGF0aF9zYW5zX2V4dChkb3dubG9hZGVkX2ZpbGUsIGNvbXByZXNzaW9uPVRSVUUpLCAic3JjIikKc3JjX2ZpbGVzIDwtIGxpc3QuZmlsZXMoc3JjX2RpciwgcmVjdXJzaXZlPVRSVUUsIGZ1bGwubmFtZXM9VFJVRSkKc3JjX2V4dHMgPC0gdG9vbHM6OmZpbGVfZXh0KHNyY19maWxlcykKc3JjX2V4dHMgPC0gc3JjX2V4dHNbc3JjX2V4dHMgIT0gIiJdCgpleHRfY291bnRzIDwtIGFzLmRhdGEudGFibGUoc29ydCh0YWJsZShzcmNfZXh0cyksIGRlY3JlYXNpbmc9VFJVRSksIGtlZXAucm93bmFtZXM9VFJVRSkKc2V0bmFtZXMoZXh0X2NvdW50cywgYygiZXh0IiwgImNvdW50IikpCmV4dF9jb3VudHNbLCBleHQ6PWZhY3RvcihleHQsIGxldmVscz1yZXYoZXh0KSldCmBgYAoKYGBge3IgZXh0X3dvcmRjbG91ZH0KbGlicmFyeSh3b3JkY2xvdWQyKQoKIyBTY2FsZSBkb3duIHRvIGF2b2lkIG92ZXItc2l6aW5nLgp3b3JkY2xvdWQyKHNxcnQodGFibGUoc3JjX2V4dHMpKSkKYGBgCgpgYGB7ciB0b3Bfbl9leHR9CmxpYnJhcnkoZ2dwbG90MikKCnRvcF9uIDwtIDEwCmdncGxvdChleHRfY291bnRzWzE6dG9wX25dLCBhZXMoeD1leHQsIHk9Y291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZT1zcHJpbnRmKCJUb3AgJXMgRmlsZSBFeHRlbnNpb25zIGluIFIgU291cmNlIENvZGUiLCB0b3BfbikpICsKICBjb29yZF9mbGlwKCkKYGBgCgpIZXJlIGFyZSB0aGUgbW9zdCBjb21tb24gZmlsZSB0eXBlcyBmb3VuZCBpbiB0aGUgc291cmNlIGNvZGUgb2YgUi4KCisgYC5SZGA6IERvY3VtZW50IGZpbGUKKyBgLlJgOiBSIHNvdXJjZSBmaWxlCisgYC5jYDogQyBzb3VyY2UgZmlsZQorIGAubW9gOiBCaW5hcnkgZGF0YSBmaWxlCisgYC5wb2A6IExhbmd1YWdlIHRyYW5zbGF0aW9uIGZpbGUKKyBgLmhgOiBIZWFkZXIgZmlsZSBmb3IgQworIGAuYWZtYDogRm9udCBmaWxlCisgYC5pbmA6IFRlbXBsYXRlIGNvbmZpZ3VyYXRpb24gZmlsZSBmb3Igc29tZSBtYWNybyBwcmVwcm9jZXNzb3IKKyBgLndpbmA6IFNhbWUgYWJvdmUgYnV0IHNwZWNpZmljYWxseSBmb3IgV2luZG93cworIGAuZmA6IEZvcnRyYW4gc291cmNlCgpJZiB3ZSBmdXJ0aGVyIGxpbWl0IHRvIG9ubHkgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgcmVsYXRlZCBmaWxlczoKCmBgYHtyIGxhbmdfZXh0X2NvdW50LCByZXN1bHRzPSJob2xkIn0KbGFuZ19leHQgPC0gYygiYyIsICJoIiwgImYiLCAiUiIpCmxhbmdfZXh0X2NvdW50cyA8LSBleHRfY291bnRzW2V4dCAlaW4lIGxhbmdfZXh0XQpsYW5nX2V4dF9jb3VudHNbLCBwY3Q6PWNvdW50IC8gc3VtKGNvdW50KV0KbGFuZ19leHRfY291bnRzWywgZXh0Oj1mYWN0b3IoZXh0LCBsZXZlbHM9bGFuZ19leHQpXQoKZ2dwbG90KGxhbmdfZXh0X2NvdW50cywgYWVzKHg9ZXh0LCB5PWNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIGxhYnMoeT0iTnVtYmVyIG9mIEZpbGVzIiwgeD0iRmlsZSBFeHRlbnNpb24iLAogICAgICAgdGl0bGU9Ikxhbmd1YWdlIEZpbGUgRGlzdHJpYnV0aW9uIGluIFIgU291cmNlIENvZGUiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1zY2FsZXM6OnBlcmNlbnQocGN0KSwgeT1jb3VudCksCiAgICAgICAgICAgIHZqdXN0PTEsIHNpemU9NSwgY29sb3I9IndoaXRlIikKYGBgCgpUaGUgZmFjdCBpcyB0aGF0LAoqUiBpcyBoZWF2aWx5IHdyaXR0ZW4gaW4gQy4qCmBQcmltaXRpdmVgIGZ1bmN0aW9ucyBhcmUgd3JpdHRpbiBpbiBDLgpNb3N0IG9mIHRoZW0gYXJlIHZlY3Rvcml6ZWQgc28gaXQgaXMgdmVyeSBmYXN0IHRvIGFwcGx5IHRoZSBmdW5jdGlvbiBkaXJlY3RseSB0byBhIHZlY3Rvci4KRm9yIGV4YW1wbGUgdGhlIHBvd2VyIGZ1bmN0aW9uIHdlIGp1c3QgZXhhbWluZWQgcHJldmlvdXNseSBpcyBhIGBwcmltaXRpdmVgIGZ1bmN0aW9uOgoKYGBge3IgcHJpbWl0aXZlfQpgXmAgICMgQSBwcmltaXRpdmUuCgooMToxMCleMiAgIyBBIHZlY3Rvcml6ZWQgcHJpbWl0aXZlIGZ1bmN0aW9uIGNhbGwuCmBgYAoKSW5zdGVhZCBvZiBjb3VudGluZyBmaWxlcywKd2UgY2FuIGZ1cnRoZXIgY291bnQgdGhlIG51bWJlciBvZiBsaW5lcyBpbiB0aG9zZSBsYW5ndWFnZSBmaWxlcyB0byBhcnJpdmUgYXQgdGhlIGRpc3RyaWJ1dGlvbiBhdCBmaWxlIGxpbmUgbGV2ZWw6CgpgYGB7ciBsYW5nX2xpbmVfY291bnQsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9ImhvbGQifQpsYW5nX3NyY19maWxlcyA8LSBzcmNfZmlsZXNbdG9vbHM6OmZpbGVfZXh0KHNyY19maWxlcykgJWluJSBsYW5nX2V4dF0KbGFuZ19zcmNfZmlsZV9sZW5zIDwtIHNhcHBseShsYW5nX3NyY19maWxlcywgUi51dGlsczo6Y291bnRMaW5lcykKCmxhbmdfbGVuIDwtIGRhdGEudGFibGUobGFuZz1nc3ViKCJeLipcXC4iLCAiIiwgbmFtZXMobGFuZ19zcmNfZmlsZV9sZW5zKSksIGxlbj1sYW5nX3NyY19maWxlX2xlbnMpCmxhbmdfbGVuX2NvdW50cyA8LSBsYW5nX2xlblssIC4odG90X2xpbmVzPXN1bShsZW4pKSwgYnk9ImxhbmciXQpsYW5nX2xlbl9jb3VudHNbLCBwY3Q6PXRvdF9saW5lcyAvIHN1bSh0b3RfbGluZXMpXQpsYW5nX2xlbl9jb3VudHNbLCBleHQ6PWZhY3RvcihsYW5nLCBsZXZlbHM9bGFuZ19leHQpXQoKZ2dwbG90KGxhbmdfbGVuX2NvdW50cywgYWVzKHg9bGFuZywgeT10b3RfbGluZXMpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgbGFicyh5PSJOdW1iZXIgb2YgTGluZXMiLCB4PSJGaWxlIEV4dGVuc2lvbiIsCiAgICAgICB0aXRsZT0iTGFuZ3VhZ2UgRmlsZSBMaW5lIERpc3RyaWJ1dGlvbiBpbiBSIFNvdXJjZSBDb2RlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWw9c2NhbGVzOjpwZXJjZW50KHBjdCksIHk9dG90X2xpbmVzKSwKICAgICAgICAgICAgdmp1c3Q9MS4yNSwgc2l6ZT01LCBjb2xvcj0id2hpdGUiKQpgYGAKClRoZSB0YWtlLWF3YXkgaXMgdGhhdCwKUiBzaW1wbHkgKmNhbm5vdCogYmUgc2xvdyBmb3IgbW9zdCBvZiB0aGUgZnVuZGFtZW50YWxseSBkZW1hbmRpbmcgY29tcHV0YXRpbmcgdGFza3MuCkl0cyBjb3JlIGlzIHdyaXR0ZW4gaW4gQyBhbmQgQyBpcyBzdXBlciBmYXN0LgoKIyBXb3JraW5nIEV4YW1wbGVzIG9mIFVzaW5nIGBSY3BwYAoKVGhlcmUgd2lsbCBiZSBjYXNlcyB3aGVyZSB0aGUgbmF0aXZlIFIgY29kZSBpcyBub3Qgc3VpdGFibGUgZm9yIHRoZSBkZXNpcmVkIGltcGxlbWVudGF0aW9uLgpUaGlzIGlzIHVzdWFsbHkgYmVjYXVzZSB0aGUgYnVpbHQtaW4gYHByaW1pdGl2ZWAgZnVuY3Rpb25zIGFyZSBub3QgYXZhaWxhYmxlIGZvciBhIHBhcnRpY3VsYXIga2luZCBvZiBhbGdvcml0aG0uCkluIHN1Y2ggc2NlbmFyaW8gd2UgY2FuIHVzZSBgUmNwcGAgdG8gcmUtd3JpdGUgb25seSB0aGUgbW9zdCBkZW1hbmRpbmcgcGFydCBvZiBvdXIgaW1wbGVtZW50YXRpb24gaW4gQysrIGFuZCBwb3J0IGl0IHRvIG91ciBSIHByb2dyYW0gc2VhbWxlc3NseS4KSW4gdGhpcyBzZWN0aW9uIHdlIGFyZSBnb2luZyB0byBpbGx1c3RyYXRlIHNldmVyYWwgc3VjaCB1c2UgY2FzZXMuCgojIyBOLUdyYW0gR2VuZXJhdGlvbgoKTi1ncmFtIGdlbmVyYXRpb24gaXMgYSBjb21tb24gdGFzayBmb3IgbmF0dXJhbCBsYW5ndWFnZSBwcm9jZXNzaW5nIGFuZCB1bmRlcnN0YW5kaW5nLgpEZXNwaXRlIGl0cyBzaW1wbGljaXR5LApzdXJwcmlzaW5nbHksCml0IGlzIG5vdCBlYXN5IGZvciBuYXRpdmUgUiBjb2RlIHRvIGZ1bGZpbGwgdGhlIHRhc2sgaW4gYW4gZWZmaWNpZW50IG1hbm5lci4KCkxldCdzIGFzc3VtZSB3ZSdkIGxpa2UgdG8gY291bnQgdGhlIGJpZ3JhbXMgb2YgdGhlIFtoZXggZHVtcF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvSGV4X2R1bXApIG9mIGEgKHBvdGVudGlhbGx5IGJpbmFyeSkgZmlsZS4KVGhlIGJ5dGUgY29kZSBpbmZvcm1hdGlvbiBjb3VsZCBiZSB1c2VmdWwgZm9yIGEgZG93bnN0cmVhbSBtYWNoaW5lIGxlYXJuaW5nIHRhc2sgc3VjaCBhcyBmaWxlIGNhdGVnb3J5IGNsYXNzaWZpY2F0aW9uLgpCdXQgdGhhdCBpcyBub3Qgb3VyIGNvbmNlcm4gZm9yIG5vdy4KV2UganVzdCB3YW50IHRvIGltcGxlbWVudCBhIGZ1bmN0aW9uIHRoYXQgY2FuIGV4dHJhY3QgbmdyYW1zIGdpdmVuIGEgbG9uZyBzdHJpbmcsCmFuZCBjb3VudCB0aGVpciBmcmVxdWVuY2llcy4KCmBgYHtyIG5ncmFtX3JlYWRfdGV4dH0KIyBQcmVwYXJlIGhleCBkdW1wIG9mIGEgZmlsZS4KIyBXZSB3aWxsIHVzZSB0aGUgUm1kIGZpbGUgaXRzZWxmIGFzIGFuIGV4YW1wbGUuCmluZmlsZSA8LSAicmNwcC5SbWQiCmNvbiA8LSBmaWxlKGluZmlsZSwgInJiIikKaGV4IDwtIHJlYWRCaW4oY29uLCB3aGF0PSJyYXciLCBuPWZpbGUuaW5mbyhpbmZpbGUpJHNpemUpCmhleF9jIDwtIGFzLmNoYXJhY3RlcihoZXgpCmNsb3NlKGNvbikKCnN0cihoZXhfYykgICMgQSBjaGFyYWN0ZXIgdmVjdG9yIG9mIGhleGFkZWNpbWFscy4KYGBgCgojIyMgTmF0aXZlIFIgey19CgpOb3cgd2UgaW1wbGVtZW50IHRoZSBtaW5pbXVtIG5ncmFtIGNvdW50ZXIgZ2l2ZW4gYSBjaGFyYWN0ZXIgdmVjdG9yLAphc3N1bWluZyBlYWNoIGVsZW1lbnQgaXMgYSB0b2tlbjoKCmBgYHtyIG5ncmFtX3J9Cm5ncmFtX3IgPC0gZnVuY3Rpb24oeCwgbikgewogICAgbGVuIDwtIGxlbmd0aCh4KSAtIChuIC0gMSkKICAgIG91dCA8LSBjaGFyYWN0ZXIobGVuKSAgIyBQcmUtYWxsb2NhdGUgc2l6ZS4KICAgIGZvciAoIGkgaW4gMTpsZW4gKSB7CiAgICAgICAgb3V0W2ldIDwtIHhbaV0KICAgICAgICBpZiAoIG4gPiAxICkKICAgICAgICAgICAgZm9yICggaiBpbiAxOihuLTEpICkKICAgICAgICAgICAgICAgIG91dFtpXSA8LSBwYXN0ZTAob3V0W2ldLCB4W2kral0pCiAgICB9CiAgICBzb3J0KHRhYmxlKG91dCksIGRlY3JlYXNpbmc9VFJVRSkKfQoKb3V0X3IgPC0gbmdyYW1fcihoZXhfYywgMikKCnByaW50KGxlbmd0aChvdXRfcikpICAjIFRvdGFsIG5ncmFtcy4KCnByaW50KG91dF9yWzFdKSAjIFRoZSBtb3N0IGZyZXF1ZW50IG5ncmFtLgpgYGAKClRoZSBhYm92ZSBmdW5jdGlvbiwKdGhvdWdoIGltcGxlbWVudGF0aW9uIGlzIHN0cmFpZ2h0Zm9yd2FyZCwKaXMgdGVycmlibHkgc2xvdyBiZWNhdXNlIGl0IGRpZG4ndCB1dGlsaXplIHZlY3Rvcml6YXRpb24uCkFuZCB0aGVyZSBkb2Vzbid0IHNlZW0gdG8gaGF2ZSBhIHJlYWRpbHkgYXZhaWxhYmxlIGBwcmltaXRpdmVgIGZ1bmN0aW9uIGZvciB0aGlzIHZlcnkgcHVycG9zZS4KVGhpcyBpcyB0aGUgdXN1YWxseSBmcnVzdHJhdGVkIGV4cGVyaWVuY2Ugd2hlbiBwZW9wbGUgZmFtaWxpYXIgd2l0aCBvdGhlciBnZW5lcmFsIHB1cnBvc2UgbGFuZ3VhZ2VzIGZpcnN0IGNvbWUgdG8gdXNlIFIuCgojIyMgUmNwcCB7LX0KCkZvciBhIGZ1bmN0aW9uIHJlcXVpcmluZyBhcmJpdHJhaWx5IGxhcmdlIGV4cGxpY2l0IGxvb3AgYW5kIHRoZXJlIGlzIG5vIG9idmlvdXMgd2F5IHRvIHZlY3Rvcml6YXRpb24sCndlIGhhdmUgYFJjcHBgIGNvbWUgdG8gdGhlIHJlc2N1ZS5eW0FuZCBhbHNvIHN1cnByaXNpbmdseSwgd2UgZG9uJ3QgcmVhbGx5IGhhdmUgbWFueSBzdWNoIGNhc2VzIGluIFIgcHJvZ3JhbW1pbmcgd2hlcmUgd2UgcmVhbGx5IG5lZWQgdG8gcmVzb3J0IHRvIGl0cyBDIGludGVyZmFjZS4gVGhlIGxhbmd1YW5nZSBpcyBkb2luZyBhIHZlcnkgZ29vZCBqb2IgaW4gdmVjdG9yaXppbmcgdGhlIG1vc3QgdXRpbGl6ZWQgZnVuY3Rpb25zIGZvciB0aGUgbWFqb3JpdHkgb2YgcHJvZ3JhbW1pbmcgb3BlcmF0aW9ucywgZXNwZWNpYWxseSBpbiB0aGUgZGF0YSBhbmFseXRpY2FsIGZpZWxkLl0KCkZvciBleGFjdGx5IHRoZSBzYW1lIG1pbmltdW0gaW1wbGVtZW50YXRpb24gd2UgY2FuIHJlLXdyaXRlIGl0IGluIEMrKyB1c2luZyB0aGUgYFJjcHBgIEFQSToKCmBgYHtyIGltcG9ydF9yY3BwLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KFJjcHApCnByaW50KGluc3RhbGxlZC5wYWNrYWdlcygpWyJSY3BwIiwgIlZlcnNpb24iXSkKYGBgCgpgYGB7ciBuZ3JhbV9yY3BwfQpjcHBGdW5jdGlvbigiCiAgQ2hhcmFjdGVyVmVjdG9yIG5ncmFtUmNwcCAoQ2hhcmFjdGVyVmVjdG9yIGhleHZlY3RvciwgaW50IG5ncmFtKSB7CiAgICBpbnQgbGVuID0gaGV4dmVjdG9yLnNpemUoKSAtIChuZ3JhbSAtIDEpOwogICAgQ2hhcmFjdGVyVmVjdG9yIG91dChsZW4pOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBsZW47IGkrKykgewogICAgICBvdXQoaSkgPSBoZXh2ZWN0b3JbaV07CiAgICAgIGZvciAoaW50IGogPSAxOyBqIDwgbmdyYW07IGorKykgewogICAgICAgIG91dChpKSArPSBoZXh2ZWN0b3JbaStqXTsKICAgICAgfQogICAgfQogICAgcmV0dXJuIG91dDsKfSIpCgpuZ3JhbVJjcHAgICMgQSBwcmltaXRpdmUgZnVuY3Rpb24uCgpuZ3JhbV9yY3BwIDwtIGZ1bmN0aW9uKHgsIG4pIHsKICBuZ3JhbXMgPC0gbmdyYW1SY3BwKHgsIDIpCiAgc29ydCh0YWJsZShuZ3JhbXMpLCBkZWNyZWFzaW5nPVRSVUUpCn0KCm91dF9yY3BwIDwtIG5ncmFtX3JjcHAoaGV4X2MsIDIpCgphbGwuZXF1YWwob3V0X3JjcHAsIG91dF9yLCBjaGVjay5hdHRyaWJ1dGVzPUZBTFNFKQpgYGAKCk5vdGljZSB0aGF0IHdlIG9ubHkgaW1wbGVtZW50IHRoZSBuZ3JhbSBleHRyYWN0aW9uIGxvb3AgYnV0IGxlYXZlIHRoZSBmcmVxdWVuY3kgY291bnQgZm9yIG5hdGl2ZSBSLgpUaGlzIGlzIGJlY2F1c2UgdGhlIGB0YWJsZWAgZnVuY3Rpb24gaXMgbm90IGEgYm90dGxlbmVjayBvZiBvdXIgdGFzay4KCkluIHRoaXMgZXhhbXBsZSB3ZSB1c2UgdGhlIGBDaGFyYWN0ZXJWZWN0b3JgIGNsYXNzIGltcGxlbWVudGVkIGluIGBSY3BwYCB3aGljaCBtaW1pY3MgdGhlIGBjaGFyYWN0ZXJgIHZlY3RvciB3ZSB1c3VhbGx5IHVzZSBpbiBuYXRpdmUgUiBjb2RlLgpUaGVyZSBhcmUgbWFueSBzdWNoIGhpZ2gtbGV2ZWwgY2xhc3NlcyB3ZSBjYW4gZmluZCBpbiB0aGUgYFJjcHBgIGxpYnJhcnkuClRoZXNlIGNsYXNzZXMgd2lsbCBzYXZlIG91ciBkZXZlbG9wbWVudCB0aW1lIHdoZW4gd3JpdGluZyB0aGUgQysrIGNvZGUgc2luY2Ugd2Ugd29uJ3QgbGltaXQgb3Vyc2VsdmVzIHRvIG9ubHkgQysrIHN0YW5kYXJkIGxpYnJhcnkuCgpgYGB7ciBuZ3JhbV9iZW5jaG1hcmssIHBhZ2VkLnByaW50PUZBTFNFfQpwcmludChtaWNyb2JlbmNobWFyaygKICBuZ3JhbV9yKGhleF9jLCAyKSwKICBuZ3JhbV9yY3BwKGhleF9jLCAyKSwKICB0aW1lcz0xMAopKQpgYGAKClRoZSBgUmNwcGAgaXMgbXVjaCBmYXN0ZXIgdGhhbiB0aGUgbmF0aXZlIFIgaW1wbGVtZW50YXRpb24uCkFuZCBpdCB3aWxsIGJlIGV2ZW4gZmFzdGVyIGlmIHRoZSBkYXRhIGlzIGdldHRpbmcgbGFyZ2VyLgoKYGBge3IgcmVhZF9iaWdfZmlsZX0KIyBOb3cgdGFrZSB0aGUgZW50aXJlIFIgc291cmNlIGNvZGUgYXMgb25lIGJpZyBmaWxlLgpjb24gPC0gZmlsZShkb3dubG9hZGVkX2ZpbGUsICJyYiIpCmJpZ19oZXggPC0gcmVhZEJpbihjb24sIHdoYXQ9InJhdyIsIG49ZmlsZS5pbmZvKGRvd25sb2FkZWRfZmlsZSkkc2l6ZSkKYmlnX2hleF9jIDwtIGFzLmNoYXJhY3RlcihiaWdfaGV4KQpjbG9zZShjb24pCgpzdHIoYmlnX2hleF9jKQpgYGAKClRoaXMgdGltZSB3ZSBiZW5jaG1hcmsgd2l0aCBkaWZmZXJlbnQgZmlsZSBzaXplLAp3ZSdsbCBzZWUgdGhhdCB0aGVvcmV0aWNhbGx5IGBSY3BwYCBjYW4gYmUgaHVuZHJlZHMgb2YgdGltZXMgZmFzdGVyIHRoYW4gbmF0aXZlIFIuCgpgYGB7ciBuZ3JhbV9iZW5jaG1hcmtfc2l6ZX0KIyBCZW5jaG1hcmsgdGhlIG5hdGl2ZSBSIGNvZGUgd2l0aCB2YXJ5aW5nIGZpbGUgc2l6ZXMuCiMgU2luY2UgaXQgbWF5IHRha2UgdG9vIGxvbmcgZm9yIHRoZSByZWR1bmRhbnQgdGFza3Mgd2UnbGwgdXNlIHBhcmFsbGVsIGNvbXB1dGluZy4KIyBBbm90aGVyIG1vcmUgY2xldmVyIHdheSBpcyB0byB0aW1lIG9uLXRoZS1mbHkgd2hlbiB3ZSBwYXJzZSB0aGUgZmlsZSBhbmQgaGVuY2UgcGFyc2Ugb25seSBvbmNlLgojIEluIHRoYXQgY2FzZSB3ZSBuZWVkIHRvIG1vZGlmeSB0aGUgb3JpZ2luYWwgZnVuY3Rpb24uCiMgSGVyZSB3ZSBnbyBmb3IgdGhlIGJydXRhbCBidXQgc2ltcGxlIHdheS4KbGlicmFyeShwYXJhbGxlbCkKCmNsIDwtIG1ha2VDbHVzdGVyKGRldGVjdENvcmVzKCkgLyAyKQpjbHVzdGVyRXhwb3J0KGNsLCBjKCJiaWdfaGV4X2MiKSkKCm5fc2l6ZSA8LSAxMApzaXplcyA8LSBzZXEoMWUzLCBsZW5ndGgoYmlnX2hleF9jKSwgbGVuZ3RoLm91dD1uX3NpemUpCgp0aW1lX25ncmFtIDwtIGZ1bmN0aW9uKGZ1bmMsIHMsIG4pIHsKICBzdCA8LSBwcm9jLnRpbWUoKQogIGZ1bmMoYmlnX2hleF9jWzE6c10sIG4pCiAgZXQgPC0gcHJvYy50aW1lKCkKICAoZXQgLSBzdClbImVsYXBzZWQiXQp9CgojIFRoaXMgY291bGQgdGFrZSBhIHdoaWxlLgp0aW1lX3IgPC0gcGFyTGFwcGx5TEIoY2wsIHNpemVzLCB0aW1lX25ncmFtLCBmdW5jPW5ncmFtX3IsIG49MikKCnN0b3BDbHVzdGVyKGNsKQoKCiMgQmVuY2htYXJrIHRoZSBSY3BwIGNvZGUgd2l0aCB2YXJ5aW5nIGZpbGUgc2l6ZXMuCnRpbWVfcmNwcCA8LSBudW1lcmljKG5fc2l6ZSkKZm9yICggaSBpbiAxOmxlbmd0aChzaXplcykgKSB7CiAgdGltZV9yY3BwW2ldIDwtIHRpbWVfbmdyYW0obmdyYW1fcmNwcCwgc2l6ZXNbaV0sIDIpCn0KCm91dF90aW1lIDwtIGRhdGEudGFibGUocj11bmxpc3QodGltZV9yKSwgcmNwcD11bmxpc3QodGltZV9yY3BwKSwgc2l6ZT1zaXplcykKb3V0X3RpbWUgPC0gbWVsdChvdXRfdGltZSwgaWQudmFycz0ic2l6ZSIsIHZhcmlhYmxlLm5hbWU9ImltcGwiLCB2YWx1ZS5uYW1lPSJ0aW1lIikKZ2dwbG90KG91dF90aW1lLCBhZXMoeD1zaXplLCB5PXRpbWUsIGdyb3VwPWltcGwsIGNvbG9yPWltcGwpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4PSJGaWxlIFNpemUgKEJ5dGVzKSIsIHk9IlNlY29uZHMiLAogICAgICAgdGl0bGU9IlNwZWVkIG9uIEJpZ3JhbSBHZW5lcmF0aW9uIG9mIFZhcnlpbmcgSW5wdXQgTGVuZ3RoIikKYGBgCgojIyMgT3RoZXIgUGFja2FnZXMgey19CgpGb3IgY29tcGxldGVuZXNzLAppbiB0aGlzIHNlY3Rpb24gd2UgaW5jbHVkZSB0aGUgYmVuY2htYXJrIGZvciBzb21lIG90aGVyIHdlbGwtZGV2ZWxvcGVkIHBhY2thZ2VzIGZvciB0ZXh0IHByb2Nlc3NpbmcuClRoZXNlIHBhY2thZ2VzIGFyZSB1c3VhbGx5IHVzaW5nIFIncyBuYXRpdmUgQyBpbnRlcmZhY2Ugb3IgYFJjcHBgIHRvIGltcGxlbWVudCB0aGUgY3JpdGljYWwgcGFydHMgb2YgdGhlIGNvbXB1dGF0aW9uIGZvciB0aGVpciBpbnRlbmRlZCB0YXNrcy4KV2Ugd2lsbCBleHBlY3QgdGhlbSB0byBiZSBhbHNvIG11Y2ggbW9yZSBlZmZpY2llbnQgdGhhbiBhIG5haXZlIGltcGxlbWVudGF0aW9uIGluIG5hdGl2ZSBSIGNvZGUuCgpUaGUgZmlyc3QgcGFja2FnZSB3ZSBleHBsb3JlIGlzIFtgcXVhbnRlZGFgXShodHRwczovL2dpdGh1Yi5jb20vcXVhbnRlZGEvcXVhbnRlZGEpOgoKYGBge3IgaW1wb3J0X3F1YW50ZWRhLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHF1YW50ZWRhKQpwcmludChpbnN0YWxsZWQucGFja2FnZXMoKVsicXVhbnRlZGEiLCAiVmVyc2lvbiJdKQpgYGAKCmBgYHtyIG5ncmFtX3F1YW50ZWRhfQojIEZvciBxdWFudGVkYSB3ZSBuZWVkIHRvIGNvbnZlcnQgdGV4dCB0byBpdHMgYHRva2Vuc2AgQVBJLgojIFRoaXMgbWF5IGludHJvZHVjZSBzb21lIG92ZXJoZWFkIHNvIHdlIHdpbGwgY3JlYXRlIHR3byBmdW5jdGlvbnMgZm9yIGZhaXIgY29tcGFyaXNvbiBsYXR0ZXIuCmhleF9zIDwtIHBhc3RlKGhleF9jLCBjb2xsYXBzZT0iICIpCmhleF90IDwtIHRva2VucyhoZXhfcykKCm5ncmFtX3F1YW50ZWRhXzEgPC0gZnVuY3Rpb24oeCwgbikgewogICMgVG9rZW5pemF0aW9uIGluY2x1ZGVkLgogIG5ncmFtcyA8LSB0b2tlbnNfbmdyYW1zKHRva2Vucyh4KSwgbj1uKVtbMV1dCiAgc29ydCh0YWJsZShuZ3JhbXMpLCBkZWNyZWFzaW5nPVRSVUUpCn0KCm5ncmFtX3F1YW50ZWRhXzIgPC0gZnVuY3Rpb24odG9rLCBuKSB7CiAgIyBBc3N1bWUgcHJlLXRva2VuaXplZC4KICBuZ3JhbXMgPC0gdG9rZW5zX25ncmFtcyh0b2ssIG49bilbWzFdXQogIHNvcnQodGFibGUobmdyYW1zKSwgZGVjcmVhc2luZz1UUlVFKQp9CgpvdXRfcXVhbnRlZGEgPC0gbmdyYW1fcXVhbnRlZGFfMShoZXhfcywgbj0yKQoKYWxsLmVxdWFsKG91dF9xdWFudGVkYSwgb3V0X3IsIGNoZWNrLmF0dHJpYnV0ZXM9RkFMU0UpCmBgYAoKW2B0ZXh0MnZlY2BdKGh0dHBzOi8vZ2l0aHViLmNvbS9kc2VsaXZhbm92L3RleHQydmVjKSBpcyBhbHNvIGEgaGlnaC1wZXJmb3JtYW5jZSBwYWNrYWdlIGltcGxlbWVudGVkIHdpdGggYFJjcHBgOgoKYGBge3IgaW1wb3J0X3RleHQydmVjLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRleHQydmVjKQpwcmludChpbnN0YWxsZWQucGFja2FnZXMoKVsidGV4dDJ2ZWMiLCAiVmVyc2lvbiJdKQpgYGAKCmBgYHtyIG5ncmFtX3RleHQydmVjfQojIEFnYWluIHRvIHVzZSB0ZXh0MnZlYyB3ZSBuZWVkIHRvIGZvbGxvdyBpdHMgQVBJOgojIENvbnZlcnRpbmcgdGV4dCBpbnRvIGBpdG9rZW5gIGl0ZXJhdG9yLgojIFNpbWlsYXIgdG8gdGhlIGNhc2Ugb2YgcXVhbnRlZGEsIHdlIHVzZSB0d28gZnVuY3Rpb25zIHdoZXJlIG9uZSBleGNsdWRlcyB0aGUgY29udmVyc2lvbiBvdmVyaGVhZC4KaGV4X2kgPC0gaXRva2VuKGhleF9zLCBwcm9ncmVzc2Jhcj1GQUxTRSkKCm5ncmFtX3RleHQydmVjXzEgPC0gZnVuY3Rpb24oeCwgbj1uKSB7CiAgaXQgPC0gaXRva2VuKHgsIHByb2dyZXNzYmFyPUZBTFNFKQogIGNyZWF0ZV92b2NhYnVsYXJ5KGl0LCBuZ3JhbT1jKG4sIG4pKQp9CgpuZ3JhbV90ZXh0MnZlY18yIDwtIGZ1bmN0aW9uKGl0LCBuPW4pIHsKICBjcmVhdGVfdm9jYWJ1bGFyeShpdCwgbmdyYW09YyhuLCBuKSkKfQoKb3V0X3RleHQydmVjIDwtIG5ncmFtX3RleHQydmVjXzEoaGV4X3MsIG49MikKCiMgVGlkeSB0aGUgcmVzdWx0IHRvIGFsaWduIHdpdGggb3VyIHByZXZpb3VzIGZ1bmN0aW9ucy4Kb3V0X3RleHQydmVjIDwtIHNldE5hbWVzKG91dF90ZXh0MnZlYyR0ZXJtX2NvdW50LCBvdXRfdGV4dDJ2ZWMkdGVybSkKCmFsbC5lcXVhbChhcy50YWJsZShzb3J0KG91dF90ZXh0MnZlYywgZGVjcmVhc2luZz1UUlVFKSksIG91dF9yLCBjaGVjay5hdHRyaWJ1dGVzPUZBTFNFKQpgYGAKCkxhc3RseSwKdW5saWtlIHRoZSBwcmV2aW91cyB0d28gcGFja2FnZXMgYm90aCBhaW0gYXQgYSBsYXJnZXIgc2NvcGUgb2YgbmF0dXJhbCBsYW5ndWFnZSBwcm9jZXNzaW5nIHRhc2tzLAp0aGUgcGFja2FnZSBbYG5ncmFtYF0oaHR0cHM6Ly9naXRodWIuY29tL3dyYXRoZW1hdGljcy9uZ3JhbSkgZGVkaWNhdGVzIG9ubHkgYXQgbmdyYW0gZ2VuZXJhdGlvbiB0YXNrLAp3cml0dGVuIG1haW5seSBpbiBDOgoKYGBge3IgaW1wb3J0X25ncmFtLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KG5ncmFtKQpwcmludChpbnN0YWxsZWQucGFja2FnZXMoKVsibmdyYW0iLCAiVmVyc2lvbiJdKQpgYGAKCmBgYHtyIG5ncmFtX25ncmFtfQpuZ3JhbV9uZ3JhbSA8LSBmdW5jdGlvbih4LCBuKSB7CiAgbmdyYW0oeCwgbj1uKQp9CgpvdXRfbmdyYW0gPC0gbmdyYW0oaGV4X3MpCgojIFRpZHkgdGhlIHJlc3VsdC4Kb3V0X25ncmFtIDwtIGdldC5waHJhc2V0YWJsZShvdXRfbmdyYW0pCm91dF9uZ3JhbSA8LSBzZXROYW1lcyhvdXRfbmdyYW0kZnJlcSwgb3V0X25ncmFtJG5ncmFtcykKCmFsbC5lcXVhbChhcy50YWJsZShvdXRfbmdyYW0pLCBvdXRfciwgY2hlY2suYXR0cmlidXRlcz1GQUxTRSkKYGBgCgpOb3cgd2UgYmVuY2htYXJrIGFsbCB0aGUgYWJvdmUgaW1wbGVtZW50YXRpb25zIHVzaW5nIHRoZSBsYXJnZSBmaWxlOgoKYGBge3IgbmdyYW1fYmVuY2htYXJrX2FsbCwgcGFnZWQucHJpbnQ9RkFMU0V9CmJlbmNobWFya19hbGwgPC0gZnVuY3Rpb24oeCkgewogIAogIHhfcyA8LSBwYXN0ZSh4LCBjb2xsYXBzZT0iICIpCiAgeF90IDwtIHRva2Vucyh4X3MpCiAgeF9pIDwtIGl0b2tlbih4X3MsIHByb2dyZXNzYmFyPUZBTFNFKQogIAogIG1pY3JvYmVuY2htYXJrKAogICAgbmdyYW1fcih4LCAyKSwKICAgIG5ncmFtX3JjcHAoeCwgMiksCiAgICBuZ3JhbV9xdWFudGVkYV8xKHhfcywgMiksCiAgICBuZ3JhbV9xdWFudGVkYV8yKHhfdCwgMiksCiAgICBuZ3JhbV90ZXh0MnZlY18xKHhfcywgMiksCiAgICBuZ3JhbV90ZXh0MnZlY18yKHhfaSwgMiksCiAgICBuZ3JhbV9uZ3JhbSh4X3MsIDIpLAogICAgdGltZXM9MwogICkKfQoKeCA8LSBiaWdfaGV4X2NbMTphcy5pbnRlZ2VyKGxlbmd0aChiaWdfaGV4X2MpIC8gMTApXSAgIyBTbWFsbGVyLgpiZW5fYWxsIDwtIGJlbmNobWFya19hbGwoeCkKcHJpbnQoYmVuX2FsbCkKYGBgCgojIyBNb3ZpbmctV2luZG93IENhbGN1bGF0aW9uCgpUYXNrIGxpa2Ugbi1ncmFtIGdlbmVyYXRpb24gaXMgYSB0eXBlIG9mIG1vdmluZy13aW5kb3cgb3BlcmF0aW9uLgpTdWNoIHRhc2sgaXMgaW4gZ2VuZXJhbCBoYXJkZXIgdG8gdmVjdG9yaXplIGFuZCBoZW5jZSB3aWxsIHVzdWFsbHkgcmVzdWx0IGluIHBlcmZvcm1hbmNlIGlzc3VlIGluIFIuCkluIHRoaXMgc2VjdGlvbiB3ZSBnZW5lcmFsaXplIHRoZSBkaXNjdXNzaW9uIHRvIGEgZ2VuZXJpYyBtb3Zpbmcgd2luZG93IG9wZXJhdGlvbi4KRm9yIGlsbHVzdHJhdGlvbiB3ZSB3aWxsIHVzZSBhIHNhbXBsZSBsb2cgZmlsZSBmcm9tIGEgQXBhY2hlIHdlYiBzZXJ2ZXIgcHVibGljbHkgYXZhaWxhYmxlIGZyb20gW0xvZ2h1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2xvZ3BhaS9sb2dodWIpIChAemh1MjAxOXRvb2xzKS4KVGhlIGxvZyB3aWxsIGxvb2sgbGlrZSB0aGUgZm9sbG93aW5nIHRleHQgbGluZXM6CgpgYGB7ciBtd19kb3dubG9hZF9sb2d9CiMgaHR0cHM6Ly9naXRodWIuY29tL2xvZ3BhaS9sb2dodWIvdHJlZS9tYXN0ZXIvQXBhY2hlCnRhcmdldF9maWxlIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbG9ncGFpL2xvZ2h1Yi9tYXN0ZXIvQXBhY2hlL0FwYWNoZV8yay5sb2ciCmRvd25sb2FkZWRfZmlsZSA8LSBmaWxlLnBhdGgoVEVNUERJUiwgYmFzZW5hbWUodGFyZ2V0X2ZpbGUpKQoKaWYgKCAhZmlsZS5leGlzdHMoZG93bmxvYWRlZF9maWxlKSApIHsKICBkb3dubG9hZC5maWxlKHRhcmdldF9maWxlLCBkb3dubG9hZGVkX2ZpbGUpCn0KCmxvZ3MgPC0gcmVhZExpbmVzKGRvd25sb2FkZWRfZmlsZSwgd2Fybj1GQUxTRSkKcHJpbnQoaGVhZChsb2dzKSkKcHJpbnQobGVuZ3RoKGxvZ3MpKQpgYGAKClN1cHBvc2UgdGhlIHRhc2sgaXMgdG8gY291bnQgbnVtYmVyIG9mIHJlY29yZHMgYnkgbG9nIHR5cGUgKGBub3RpY2VgIG9yIGBlcnJvcmApIHdpdGhpbiB0aGUgbGFzdCA1IG1pbnV0ZXMgZm9yIGV2ZXJ5IDEgbWludXRlLgpUaGlzIGlzIGFuIG92ZXJsYXBwaW5nIG1vdmluZyB3aW5kb3cgb3BlcmF0aW9uIHdoZXJlIHRoZSBhY3R1YWwgd2luZG93IHNpemUgKG51bWJlciBvZiByZWNvcmRzIGludm9sdmVkIGluIGVhY2ggdGltZS1maXhlZCB3aW5kb3cpIG11c3QgYmUgZnVydGhlciBkZXRlcm1pbmVkIGJ5IHRoZSBvcmlnaW5hbCB0aW1lc3RhbXAuClRoZSBpZGVhIGlzIHJhdGhlciBzaW1wbGUgYnV0IHRoZSBhY3R1YWwgaW1wbGVtZW50YXRpb24gcHJlc2VudHMgc2V2ZXJhbCBjaGFsbGFuZ2VzIGZyb20gdGhlIGFuZ2xlIG9mIHBlcmZvcm1hbmNlIG9wdGltaXphdGlvbiBpbiBuYXRpdmUgUiBjb2RlLgoKTGV0J3MgdGlkeSB1cCB0aGUgbG9nIGxpbmVzIGZpcnN0IHRvIGhhdmUgYSBjbGVhbmVyIGlucHV0IHRvIG91ciB0YXNrOgoKYGBge3IgbXdfcGFyc2VfbG9nfQphcGFjaGVfbG9ncyA8LSBhcy5kYXRhLnRhYmxlKHN0cmluZ3I6OnN0cl9tYXRjaChsb2dzLCAiXlxcWyhbYS16QS1aMC05OiBdKylcXF0gXFxbKFthLXpdKylcXF0iKVssLTFdKQpzZXRuYW1lcyhhcGFjaGVfbG9ncywgYygidHMiLCAidHlwZSIpKQphcGFjaGVfbG9ncyA8LSBhcGFjaGVfbG9nc1ssIHRzOj1hcy5QT1NJWGN0KHRzLCBmb3JtYXQ9IiVhICViICVkICVIOiVNOiVTICV5IiwgdHo9IlVUQyIpXQphcGFjaGVfbG9ncyA8LSBhcGFjaGVfbG9nc1ssIHNlYzo9YXMuaW50ZWdlcih0cyldCgpoZWFkKGFwYWNoZV9sb2dzKQpgYGAKCiMjIyBOYXRpdmUgUiB7LX0KCkFnYWluIGxldCdzIHRyeSB0aGUgbmFpdmUgYXBwcm9hY2ggdXNpbmcgcGxhaW4gUiBjb2RlIGZpcnN0LgoKYGBge3IgbXdfcn0KbXdfY291bnRfciA8LSBmdW5jdGlvbihEVCwgd3NpemUsIGludGVyLCBzZWxlY3Q9TlVMTCkgewogICAgcmVxdWlyZShkYXRhLnRhYmxlKQogICAgc3RzIDwtIG1pbihEVCRzZWMpCiAgICBuaW50ZXIgPC0gY2VpbGluZyhkaWZmKHJhbmdlKERUJHNlYykpIC8gaW50ZXIpCiAgICBpZiAoICFpcy5udWxsKHNlbGVjdCkgKQogICAgICAgIERUIDwtIERUW3R5cGUgJWluJSBzZWxlY3RdCiAgICByZXMgPC0gbGlzdCgpCiAgICBmb3IgKCBjYSBpbiB1bmlxdWUoRFQkdHlwZSkgKSB7CiAgICAgICAgc3RzXyA8LSBzdHMKICAgICAgICBjbnQgPC0gaW50ZWdlcihuaW50ZXIpICAjIFByZS1hbGxvY2F0ZSBtZW1vcnkuCiAgICAgICAgZm9yICggaSBpbiAxOm5pbnRlciApIHsKICAgICAgICAgICAgY250W2ldIDwtIG5yb3coRFRbdHlwZSA9PSBjYV1bc2VjIDw9IHN0c18gJiBzZWMgPiBzdHNfIC0gd3NpemVdKQogICAgICAgICAgICBzdHNfIDwtIHN0c18gKyBpbnRlcgogICAgICAgIH0KICAgICAgICByZXNbW2NhXV0gPC0gY250CiAgICB9CiAgICByZXMKfQoKc3lzdGVtLnRpbWUobXdfcmVzX3IgPC0gbXdfY291bnRfcihhcGFjaGVfbG9ncywgMzAwLCA2MCkpCmBgYAoKVGhlIHJlc3VsdHMgYXJlIHR3byBudW1lcmljIHNlcmllcywKd2hpY2ggY2FuIGJlIHBsb3R0ZWQgYXMgYmVsb3cuCgpgYGB7ciBtd19wbG90fQptdl9yZXMgPC0gYXMuZGF0YS50YWJsZShtd19yZXNfcikKbXZfcmVzIDwtIG12X3Jlc1ssIGk6PS5JXQptdl9yZXMgPC0gbWVsdChtdl9yZXMsIGlkLnZhcnM9ImkiLCB2YXJpYWJsZS5uYW1lPSJsb2dfdHlwZSIsIHZhbHVlLm5hbWU9ImNvdW50IikKCmdncGxvdChtdl9yZXMsIGFlcyh4PWksIHk9Y291bnQsIGdyb3VwPWxvZ190eXBlLCBjb2xvcj1sb2dfdHlwZSkpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh4PSI1LU1pbiBNb3ZpbmcgV2luZG93IHdpdGggMS1NaW4gSW50ZXJ2YWwiLAogICAgICAgeT0iTnVtYmVyIG9mIExvZ3MiKQpgYGAKCk9uZSBzaG91bGQgbm90aWNlIHRoYXQgdGhpcyBpcyBhIHZlcnkgc21hbGwgZGF0YXNldCBidXQgb3VyIGZ1bmN0aW9uIGFscmVhZHkgZXhoaWJpdHMgYm90dGxlbmVja2VkLgpCZWZvcmUgd2UgcHJvY2VlZCB0byBpbXBsZW1lbnQgdGhlIHNhbWUgaWRlYSBidXQgaW4gUmNwcCwKbGV0J3Mgc3RydWdnbGUgYSBiaXQgbW9yZToKCmBgYHtyIG13X3JfMn0KbXdfY291bnRfcl8yIDwtIGZ1bmN0aW9uKERULCB3c2l6ZSwgaW50ZXIsIHNlbGVjdD1OVUxMKSB7CiAgICByZXF1aXJlKGRhdGEudGFibGUpCiAgICBhbGxfc2VjcyA8LSBkYXRhLnRhYmxlKHNlYz1taW4oRFQkc2VjKTptYXgoRFQkc2VjKSkKICAgIGlmICggIWlzLm51bGwoc2VsZWN0KSApCiAgICAgICAgRFQgPC0gRFRbdHlwZSAlaW4lIHNlbGVjdF0KICAgIHJlcyA8LSBsYXBwbHkoc3BsaXQoRFQsIERUJHR5cGUpLCAKICAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgewogICAgICAgICAgICAgICAgICAgICAgeCA8LSBtZXJnZSh4LCBhbGxfc2VjcywgYnk9InNlYyIsIGFsbD1UUlVFKQogICAgICAgICAgICAgICAgICAgICAgc2VjX3VuaXRfY250IDwtIHhbLCAuKGNudD1zdW0oIWlzLm5hKHR5cGUpKSksIGJ5PSJzZWMiXQogICAgICAgICAgICAgICAgICAgICAgc2VjX3VuaXRfY3VtY250IDwtIGMocmVwKDAsIHdzaXplKSwgY3Vtc3VtKHNlY191bml0X2NudCRjbnQpKQogICAgICAgICAgICAgICAgICAgICAgYXMuaW50ZWdlcih0YWlsKHNlY191bml0X2N1bWNudCwgLXdzaXplKSAtIGhlYWQoc2VjX3VuaXRfY3VtY250LCAtd3NpemUpKQogICAgICAgICAgICAgICAgICB9KQogICAgbGFwcGx5KHJlcywgZnVuY3Rpb24oeCkgeFtzZXEoMSwgbGVuZ3RoKHgpLCBpbnRlcildKQp9CgpzeXN0ZW0udGltZShtd19yZXNfcl8yIDwtIG13X2NvdW50X3JfMihhcGFjaGVfbG9ncywgMzAwLCA2MCkpCmBgYAoKYGBge3IgbXdfY2hlY2tfcmVzfQppZGVudGljYWwobXdfcmVzX3Ikbm90aWNlLCBtd19yZXNfcl8yJG5vdGljZSkgJiYgCiAgaWRlbnRpY2FsKG13X3Jlc19yJGVycm9yLCBtd19yZXNfcl8yJGVycm9yKQpgYGAKCkFwcGFyZW50bHkgd2UgY2FuIGltcHJvdmUgZHJhc3RpY2FsbHkgd2l0aCBuYXRpdmUgUiBjb2RlIGlmIHdlIGRvIHNvbWUgY3JlYXRpdmUgdHdpc3Rpbmcgb2YgdGhlIG9yaWdpbmFsIHRhc2suCkhlcmUgd2UgcmUtd29yayB0aGUgb3JpZ2luYWwgcHJvYmxlbSBieSBmb3JjZWx5IGV4cGFuZGluZyB0aGUgaW5wdXQgYGRhdGEuZnJhbWVgIHRvIGhhdmUgYXQgbGVhc3Qgb25lIHJvdyBmb3IgZXZlcnkgc2Vjb25kIGluIHRoZSBjb3ZlcmVkIHBlcmlvZCwKYW5kIHdlIGRvIGEgY3VtdWxhdGl2ZSBzdW0gYnkgc2Vjb25kIHRvIGFycml2ZSBhdCBhIHNlY29uZC1sZXZlbCBtb3Zpbmcgd2luZG93IGFuZCB0aGVuIHN1YnNldCBpdHMgbWludXRlLWxldmVsIHJlc3VsdHMuCkluIGRvaW5nIHNvIHdlIGVmZmVjdGl2ZWx5IHJlbW92ZSB0aGUgbmVlZCB0byBkbyB0aGUgbW92aW5nIHdpbmRvdyBsb29wICh3aGljaCBSIGlzIG5vdCBnb29kIGF0KSBhbmQgdHJhbnNmb3JtIHRoZSB0YXNrIHRvIHVzZSB0aGUgYnVpbHQtaW4gdmVjdG9yaXplZCBwcmltaXRpdmUgYGN1bXN1bWAgZnVuY3Rpb24uCgpUaGUgaW1wcm92ZWQgc29sdXRpb24gc3RpbGwgaGFzIHNldmVyYWwgZmxhd3M6CgoxLiBJdCB1c2VzIG1vcmUgbWVtb3J5IHRoYW4gaXQgYWN0dWFsbHkgbmVlZHMgYnkgZXhwYW5kaW5nIHRoZSBgZGF0YS5mcmFtZWAgZnJvbSBhIHNwYXJzZSByZXByZXNlbnRhdGlvbiAoYSBzZWNvbmQgd2l0aG91dCBsb2cgd2lsbCBub3QgcHJlc2VudCkgaW50byBhIGRlbnNlIG9uZTsgdGhpcyBjYW4gYmUgYSBib3R0bGVuZWNrIGZvciByZWFsbHkgaHVnZSBkYXRhc2V0cwoyLiBJdCBjb21wdXRlcyB0aGluZ3MgbW9yZSB0aGFuIHdlIG5lZWQ6IG1vdmluZyB3aW5kb3cgb3BlcmF0aW9uIGlzIHBlcmZvcm1lZCBhdCBzZWNvbmQgbGV2ZWwgYnV0IHdlIG9ubHkgbmVlZCBhIG1pbnV0ZSBsZXZlbCAoVGhpcyBjYW4gYmUgc29sdmVkIGlmIHdlIHJlZmFjdG9yIHRoZSBjb2RlIGEgYml0LCBzbyBsZXNzIG9mIGFuIGlzc3VlIGFuZCBhbHNvIGl0cyBwZXJmb3JtYW5jZSBpbXBhY3QgaXMgcXVpdGUgbGltaXRlZCkKMy4gSXQgaXMgbXVjaCBoYXJkZXIgdG8gcmVhZCBhbmQgdW5kZXJzdGFuZCB0aGUgaW50ZW50IG9mIHRoZSBjb2RlCgpBbmQsCmFzIGEgbWF0dGVyIG9mIGZhY3QsCml0IGlzIHN0aWxsIG5vdCAqc3VwZXIqIGZhc3QuCgpMZXQncyBzZWUgaWYgd2UgY2FuIGFjaGlldmUgZXZlbiBmYXN0ZXIgY29tcHV0aW5nIHRpbWUgYnkgdXNpbmcgUmNwcC4KCiMjIyBSY3BwIHstfQoKYGBge3IgbXdfcmNwcH0KY3BwRnVuY3Rpb24oIgogIE51bWVyaWNWZWN0b3IgTVdDb3VudFJjcHAoTnVtZXJpY1ZlY3RvciB0cywgaW50IHdzaXplLCBpbnQgaW50ZXIsIGludCBzdHMsIGludCBudCkgewogICAgTnVtZXJpY1ZlY3RvciBvdXQobnQpOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBudDsgaSsrKSB7CiAgICAgIE51bWVyaWNWZWN0b3IgY250cyA9IHRzWyh0cyA8PSBzdHMpICYgKHRzID4gc3RzIC0gd3NpemUpXTsKICAgICAgb3V0KGkpID0gY250cy5zaXplKCk7CiAgICAgIHN0cyArPSBpbnRlcjsKICAgIH0KICAgIHJldHVybiBvdXQ7CiAgfSIpCgptd19jb3VudF9yY3BwIDwtIGZ1bmN0aW9uKERULCB3c2l6ZSwgaW50ZXIsIHNlbGVjdD1OVUxMKSB7CiAgICByZXF1aXJlKGRhdGEudGFibGUpCiAgICBzdHMgPC0gbWluKERUJHNlYykKICAgIG5pbnRlciA8LSBjZWlsaW5nKGRpZmYocmFuZ2UoRFQkc2VjKSkgLyBpbnRlcikKICAgIGlmICggIWlzLm51bGwoc2VsZWN0KSApCiAgICAgICAgRFQgPC0gRFRbdHlwZSAlaW4lIHNlbGVjdF0KICAgIGxhcHBseShzcGxpdChEVCwgRFQkdHlwZSksIAogICAgICAgICAgIGZ1bmN0aW9uKHgpIGFzLmludGVnZXIoTVdDb3VudFJjcHAoeCRzZWMsIHdzaXplLCBpbnRlciwgc3RzLCBuaW50ZXIpKSkKfQoKc3lzdGVtLnRpbWUobXdfcmVzX3JjcHAgPC0gbXdfY291bnRfcmNwcChhcGFjaGVfbG9ncywgMzAwLCA2MCkpCmBgYAoKYGBge3IgbXdfY2hlY2tfcmVzX3JjcHB9CmlkZW50aWNhbChtd19yZXNfciRub3RpY2UsIG13X3Jlc19yY3BwJG5vdGljZSkgJiYgCiAgaWRlbnRpY2FsKG13X3Jlc19yJGVycm9yLCBtd19yZXNfcmNwcCRlcnJvcikKYGBgCgpgYGB7ciBtd19iZW5jaG1hcmssIHBhZ2VkLnByaW50PUZBTFNFfQpwcmludChtaWNyb2JlbmNobWFyaygKICBtd19jb3VudF9yKGFwYWNoZV9sb2dzLCAzMDAsIDYwKSwKICBtd19jb3VudF9yXzIoYXBhY2hlX2xvZ3MsIDMwMCwgNjApLAogIG13X2NvdW50X3JjcHAoYXBhY2hlX2xvZ3MsIDMwMCwgNjApLAogIHRpbWVzPTMKKSkKYGBgCgpBcyBvbmUgY2FuIHNlZSwKdXNpbmcgUmNwcCBpcyBicnV0YWwgYW5kIHNpbXBsZSBhbmQgZXh0cmVtZWx5IGZhc3QuCkl0IGFsc28gc3VwcG9ydHMgdmVjdG9yIHNsaWNpbmcgYXMgaW4gUiBzbyB3cml0aW5nIFJjcHAgaXMgaW5kZWVkIG1vcmUgbGlrZSB3cml0aW5nIFIgYW5kIGxlc3MgbGlrZSB3cml0aW5nIEMrKy4KCkluIHRoaXMgdG95IGV4YW1wbGUgb3VyIFJjcHAgc29sdXRpb24gaXMgbW9yZSB0aGFuIDMgdGltZXMgZmFzdGVyIHRoYW4gb3VyIGNyZWF0aXZlIHR3aXN0ZWQgYXBwcm9hY2guCkFuZCBpdCBjZXJydGFpbmx5IGNhbiBiZSBldmVuIGZhc3RlciB3aGVuIGl0IGNvbWVzIHRvIGxhcmdlciBhcHBsaWNhdGlvbi4KCiMjIFRpbWUtRGVwZW5kZW50IEZlYXR1cmUgR2VuZXJhdGlvbgoKQW5vdGhlciBjb21tb24gdXNlIGNhc2Ugc2ltaWxhciB0byBhIG1vdmluZyB3aW5kb3cgb3BlcmF0aW9uIGlzIHdoZW4gd2UgbmVlZCB0byBjYWxjdWxhdGUgZGVyaXZlZCBmZWF0dXJlcyBiYXNlZCBvbiBhIGxvb2stYmFjayB0aW1lIHdpbmRvdy4KVGhpcyBpcyB1c3VhbGx5IGZvciBhIG1hY2hpbmUgbGVhcm5pbmcgZGF0YXNldCB3aGVyZSBhbiBlbnRpdHkgY2FuIGhhdmUgdGltZSBkZXBlbmRlbnQgYmVoYXZpb3Igc3RhdGlzdGljcyBzdWNoIGFzIG51bWJlciBvZiBhIGNlcnRhaW4gYWN0aXZpdHkgaW4gdGhlIHBhc3QgMzAgZGF5cyBhdCBhbnkgZ2l2ZW4gcG9pbnQgd2hlbiBhIG1vZGVsIG5lZWRzIHRvIG1ha2UgYSBwcmVkaWN0aW9uIGFib3V0IHRoZSBmdXR1cmUuCgpGb3IgdGhpcyBzY2VuYXJpbyB3ZSB3aWxsIGNyZWF0ZSBhIFtNT09DXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9NYXNzaXZlX29wZW5fb25saW5lX2NvdXJzZSktbGlrZSBzdHVkZW50LWNvdXJzZSBkYXRhc2V0IGFzIG91ciB3b3JraW5nIGV4YW1wbGU6CgpgYGB7ciBmZV9mYWtlX2RhdGF9CiMgQXNzdW1lIHVzZXIgaXMgZW5jb2RlZCBieSBpbnRlZ2VyIGFuZCBjbGFzcyBpcyBlbmNvZGVkIGJ5IGEgc2luZ2xlIEVuZ2xpc2ggbGV0dGVyLgpjcmVhdGVfbW9vY19kYXRhIDwtIGZ1bmN0aW9uKG5sb2csIG51c2VyLCBzZWVkPTUyODQ5MSkgewogIHNldC5zZWVkKHNlZWQpCiAgZGF0IDwtIGRhdGEudGFibGUodXNlcj1zYW1wbGUoMTpudXNlciwgbmxvZywgcmVwbGFjZT1UUlVFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9iPXJiZXRhKDE6bnVzZXIsIC4xLCAyKSksCiAgICAgICAgICAgICAgICAgICAgY291cnNlPXNhbXBsZShsZXR0ZXJzLCBubG9nLCByZXBsYWNlPVRSVUUsIHByb2I9MToyNiksCiAgICAgICAgICAgICAgICAgICAgdHM9cnVuaWYobmxvZywgYXMuaW50ZWdlcihhcy5QT1NJWGN0KCIyMDE1LTAxLTAxIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmludGVnZXIoYXMuUE9TSVhjdCgiMjAxNS0wNi0zMCIpKSkpCiAgZGF0WywgdDo9YXMuUE9TSVhjdCh0cywgb3JpZ2luPSIxOTcwLTAxLTAxIildCiAgc2V0b3JkZXIoZGF0LCB1c2VyLCBjb3Vyc2UsIHRzKQogIGRhdAp9CgptZGF0YSA8LSBjcmVhdGVfbW9vY19kYXRhKG5sb2c9MWU2LCBudXNlcj0xZTMpCmhlYWQobWRhdGEpCmBgYAoKVG8gaW50ZXJwcmV0IHRoZSBkYXRhc2V0LAphIHJvdyByZXByZXNlbnRzIGEgcGFpciBvZiBzdHVkaWVudC1jbGFzcyBpbnRlcmFjdGlvbiBhdCBhIGdpdmVuIHRpbWVzdGFtcC4KCk5vdyBhc3N1bWUgd2UnZCBsaWtlIHRvIGV4dHJhY3QgYSBmZWF0dXJlOgpIb3cgbWFueSB1bmlxdWUgY291cnNlcyBpbiB0b3RhbCBkaWQgYSBzdHVkZW50IHJlZ2lzdGVyIGF0IHRoZSB0aW1lIG9mIGhpcy9oZXIgbGFzdCBhY3Rpdml0eSByZWNvcmQgZm9yIGVhY2ggY291cnNlPwpGb3IgZXhhbXBsZSwKaWYgc3R1ZGVudCAxJ3MgbGFzdCBhY3Rpdml0eSBmb3IgY291cnNlIGEgaXMgdG9kYXksCnRoZW4gaG93IG1hbnkgY291cnNlcyBpbiB0b3RhbCAoaW5jbHVkaW5nIGNvdXJzZSBhKSBkaWQgc2hlIGFsc28gaGF2ZSBhdCBsZWFzdCBvbmUgaW50ZXJhY3Rpb24gcmVjb3JkPwpXZSBtYXkgd2FudCB0byB1c2UgdGhpcyBpbmZvcm1hdGlvbiBhcyBvbmUgb2YgdGhlIGZlYXR1cmUgdG8gYnVpbGQgYSBtb2RlbCB0byBwcmVkaWN0IHdoZXRoZXIgYSB1c2VyIHdpbGwgZHJvcCBvdXQgZnJvbSBhIHJlZ2lzdGVyZWQgY2xhc3MuCgpGb3IgdGhpcyB1c2UgY2FzZSB3ZSB3aWxsIGFnYWluIGJlbmNobWFyayB3aXRoIDMgZGlmZmVyZW50IGFwcHJvYWNoZXM6CgoxLiBUaGUgbmFpdmUgUiBhcHByb2FjaAoyLiBUaGUgaW1wcm92ZWQgUiBhcHByb2FjaCB1c2luZyBgZGF0YS50YWJsZWAgQVBJCjMuIFRoZSBuYWl2ZSBhcHByb2FjaCBidXQgd3JpdHRlbiBpbiBSY3BwCgojIyMgTmF0aXZlIFIgey19CgpGaXJzdCB0aGUgbmFpdmUgYXBwcm9hY2ggaW4gbmF0aXZlIFIgY29kZToKCmBgYHtyIGZlX25haXZlX3J9Cm5haXZlX3JfZnVuYyA8LSBmdW5jdGlvbihEVCkgewogIHJlcXVpcmUoZGF0YS50YWJsZSkKICBEVCA8LSBjb3B5KERUKQogIERUWywgYDo9YCh0c21pbj1taW4odHMpLCB0c21heD1tYXgodHMpKSwgYnk9InVzZXIsY291cnNlIl0KICBEVCA8LSB1bmlxdWUoRFRbLCBsaXN0KHVzZXIsIGNvdXJzZSwgdHNtaW4sIHRzbWF4KV0pCiAgcmVzIDwtIGludGVnZXIodW5pcXVlTihEVCR1c2VyKSArIHVuaXF1ZU4oRFQkY291cnNlKSkKICBjbnQgPC0gMQogIGZvciAoIGkgaW4gdW5pcXVlKERUJHVzZXIpICkgewogICAgdG1wZGF0IDwtIERUW3VzZXIgPT0gaV0KICAgIGZvciAoIGogaW4gdG1wZGF0JGNvdXJzZSApIHsKICAgICAgcmVzW2NudF0gPC0gc3VtKHRtcGRhdCR0c21pbiA8PSB0bXBkYXRbY291cnNlID09IGosIHRzbWF4XSkKICAgICAgY250IDwtIGNudCArIDEKICAgIH0KICB9CiAgY2JpbmQoRFRbLCBsaXN0KHVzZXIsIGNvdXJzZSldLCByZXMpCn0KCnN5c3RlbS50aW1lKGZlX3Jlc19yMSA8LSBuYWl2ZV9yX2Z1bmMobWRhdGEpKQpgYGAKClRoZSBpZGVhIGlzIHNpbXBsZToKd2UgbG9vcCBvdmVyIGVhY2ggc3R1ZGVudCBhbmQgaW5uZXItbG9vcCBvdmVyIGVhY2ggY291cnNlcyB0byBjYWxjdWxhdGUgdGhlIHJlcXVpcmVkIG1ldHJpYy4KU3VjaCBodWdlIG5lc3RlZCBsb29wIGlzIGRvb21lZCB0byBmYWlsIGZvciBsYXJnZSBhcHBsaWNhdGlvbi4KClRoZSByZXN1bHQgd2lsbCBsb29rIHNvbWV0aGluZyBsaWtlOgoKYGBge3IgZmVfcmVzfQpmZV9yZXNfcjFbc2FtcGxlKC5OLCAxMCldCmBgYAoKU28gZm9yIGV4YW1wbGUgdXNlciA0OTEgaGFzIGludGVyYWN0ZWQgd2l0aCAxOCBjb3Vyc2VzIChvciAxNyAqb3RoZXIqIGNvdXJzZXMpIGluIHRvdGFsIHdoZW4gc2hlIGxhc3QgaW50ZXJhY3RlZCB3aXRoIGNvdXJzZSB0IGluIHRoZSByZWNvcmRzLgoKVG8gc3RpY2sgd2l0aCBSLApsZXQncyBpbXByb3ZlIHRoZSBwZXJmb3JtYW5jZSBieSB1c2luZyBgZGF0YS50YWJsZWAncyBzcGVjaWFsICpncm91cCBieSBlYWNoIGkqIGZ1bmN0aW9uYWxpdHk6CgpgYGB7ciBmZV9iZXR0ZXJfcn0KYmV0dGVyX3JfZnVuYyA8LSBmdW5jdGlvbihEVCkgewogICAgcmVxdWlyZShkYXRhLnRhYmxlKQogICAgRFQgPC0gY29weShEVCkKICAgIERUWywgYDo9YCh0c21pbj1taW4odHMpLCB0c21heD1tYXgodHMpKSwgYnk9InVzZXIsY291cnNlIl0KICAgIHRtcGRhdDEgPC0gdW5pcXVlKERUWywgbGlzdCh1c2VyLCBjb3Vyc2UsIHRzbWluKV0pCiAgICB0bXBkYXQyIDwtIHVuaXF1ZShEVFssIGxpc3QodXNlciwgY291cnNlLCB0c21heCldKQogICAgc2V0a2V5KHRtcGRhdDEsIHVzZXIpCiAgICBzZXRrZXkodG1wZGF0MiwgdXNlcikKICAgIGNiaW5kKHRtcGRhdDFbLCBsaXN0KHVzZXIsIGNvdXJzZSldLCAKICAgICAgICAgIHJlcz10bXBkYXQxW3RtcGRhdDIsIGxpc3QocmVzPXN1bSh0c21pbiA8PSB0c21heCkpLCBieT0uRUFDSEldWywgcmVzXSkKfQoKc3lzdGVtLnRpbWUoZmVfcmVzX3IyIDwtIGJldHRlcl9yX2Z1bmMobWRhdGEpKQoKc2V0a2V5KGZlX3Jlc19yMSwgdXNlcikKaWRlbnRpY2FsKGZlX3Jlc19yMSwgZmVfcmVzX3IyKQpgYGAKClRoZSBzb2x1dGlvbiBpcyBpbmRlZWQgdmVyeSBmYXN0LgpUaGUgb25seSBkcmF3YmFjayBpcyB0aGF0IHRoZSBjb2RlIGlzIGhhcmQgdG8gcmVhZCBhbmQgdW5kZXJzdGFuZCwKYXMgaXQgYWx3YXlzIHdpbGwgYmUgd2hlbiB3ZSB0cnkgdG8gcmUtd29yayBhIHByb2JsZW0gZnJvbSBpdHMgb3JpZ2luYWwgcmVwcmVzZW50YXRpb24uCgojIyMgUmNwcCB7LX0KCkNhbiB3ZSBiZWF0IHRoZSBwZXJmb3JtYW5jZSBvZiBhIHB1cmUgYGRhdGEudGFibGVgIGFwcHJvYWNoIGJ5IHNpbXBseSBpbXBsZW1lbnQgdGhlIG5lc3RlZCBsb29wIHVzaW5nIFJjcHA/CgpgYGB7ciBmZV9yY3BwfQpjcHBGdW5jdGlvbigiCiAgTnVtZXJpY1ZlY3RvciByY3BwRnVuYyhOdW1lcmljVmVjdG9yIGZpcnN0LCBOdW1lcmljVmVjdG9yIGxhc3QpIHsKICAgIGludCBsZW4gPSBmaXJzdC5zaXplKCk7CiAgICBOdW1lcmljVmVjdG9yIG91dChsZW4pOwogICAgZm9yIChpbnQgaSA9IDA7IGkgPCBsZW47IGkrKykgewogICAgICBvdXQoaSkgPSAwOwogICAgICBmb3IgKGludCBqID0gMDsgaiA8IGxlbjsgaisrKSB7CiAgICAgICAgaWYgKGZpcnN0KGopIDw9IGxhc3QoaSkpIHsKICAgICAgICAgIG91dChpKSArPSAxOwogICAgICAgIH0KICAgICAgfQogICAgfQogICAgcmV0dXJuIG91dDsKICB9IikKCnJjcHBfZnVuYyA8LSBmdW5jdGlvbihEVCkgewogIHJlcXVpcmUoZGF0YS50YWJsZSkKICBEVCA8LSBjb3B5KERUKQogIERUWywgYDo9YCh0c21pbj1taW4odHMpLCB0c21heD1tYXgodHMpKSwgYnk9InVzZXIsY291cnNlIl0KICBEVCA8LSB1bmlxdWUoRFRbLCBsaXN0KHVzZXIsIGNvdXJzZSwgdHNtaW4sIHRzbWF4KV0pCiAgY2JpbmQoRFRbLCBsaXN0KHVzZXIsIGNvdXJzZSldLAogICAgICAgIHJlcz1hcy5pbnRlZ2VyKERUWywgbGlzdChyZXM9cmNwcEZ1bmMoLlNEJHRzbWluLCAuU0QkdHNtYXgpKSwgYnk9InVzZXIiXVssIHJlc10pKQp9CgpzeXN0ZW0udGltZShmZV9yZXNfY3BwIDwtIHJjcHBfZnVuYyhtZGF0YSkpCgpzZXRrZXkoZmVfcmVzX2NwcCwgdXNlcikKaWRlbnRpY2FsKGZlX3Jlc19jcHAsIGZlX3Jlc19yMSkKYGBgCgpgYGB7ciwgcGFnZWQucHJpbnQ9RkFMU0V9CnByaW50KG1pY3JvYmVuY2htYXJrKGJldHRlcl9yX2Z1bmMobWRhdGEpLAogICAgICAgICAgICAgICAgICAgICByY3BwX2Z1bmMobWRhdGEpLCAKICAgICAgICAgICAgICAgICAgICAgdGltZXM9MTApKQpgYGAKCkl0IHR1cm5zIG91dCB0aGF0LApSY3BwIGlzIHN0aWxsIHRoZSBmYXN0ZXN0IQpCdXQgdGhlIGRpZmZlcmVuY2UgaXMgbm90IHNpZ25pZmljYW50LgpXZSBzaG91bGRuJ3QgYmUgc3VycHJpc2VkIGlmIHdlIGtub3cgdGhhdCBgZGF0YS50YWJsZWAgaXRzZWxmIGlzIGluZGVlZCB3cml0dGVuIGluIEMuCgojIEZpbmFsIFdyYXAtVXAKCkluIHRoaXMgbm90ZWJvb2sgd2UndmUgZGVtb25zdHJhdGVkIHRoZSBrZXkgY29uY2VwdCB0byB3cml0ZSBhIGhpZ2gtcGVyZm9ybWFuY2UgUiBwcm9ncmFtOgp0byB2ZWN0b3JpemUgYW5kIHRvIGF2b2lkIG1lbW9yeSBjb3B5LgpUaGV5IHdpbGwgd29yayA5MCUgb2YgdGhlIHRpbWUgdG8gbWFrZSBzdXJlIG91ciBSIHByb2dyYW0gaXMgZmFzdCBlbm91Z2guCkJ1dCB0aGVyZSBhcmUgdXNlIGNhc2VzIHdoZXJlIHRoZSBjb25jZXB0LAplc3BlY2lhbGx5IHZlY3Rvcml6YXRpb24sCmlzIGhhcmQgdG8gYXBwbHkuClRvIG92ZXJjb21lIHN1Y2ggcHJvYmxlbSB3ZSBjYW46CgoxLiBSZS13b3JrIHRoZSBvcmlnaW5hbCBwcm9ibGVtIHdpdGggYW4gYWx0ZXJuYXRpdmUgYWxnb3JpdGhtIHRoYXQgY2FuIGJlIHZlY3Rvcml6ZWQKMi4gU3VydmV5IGFueSBleGlzdGluZyBwYWNrYWdlIHRoYXQgYWxyZWFkeSBoYW5kbGVzIHN1Y2ggY2FzZSBlZmZpY2llbnRseQozLiBJbXBsZW1lbnQgdGhlIGNvcmUgb2Ygb3VyIGFsZ29yaXRobSBpbiBSY3BwCgpPYnZpb3VzbHkgdGhlIGxhc3Qgb3B0aW9uIGlzIHRoZSBtb3N0IGZsZXhpYmxlIG9uZS4KQW5kIHdlJ3ZlIGFsc28gZGVtb25zdHJhdGVkIDMgZGlmZmVyZW50IGNvbW1vbiB1c2UgY2FzZXMgd2hlcmUgUmNwcCBjYW4gZ2FpbiB0cmVtZW5kb3VzIGltcHJvdmVtZW50IGluIGNvbXB1dGluZyB0aW1lLAp3aXRoIG5vdCByZWFsbHkgbXVjaCBlZmZvcnQgdG8gaW1wbGVtZW50LgoKVGhlIGtleSB0byB1c2luZyBSY3BwIHdpdGggZWFzZSBpcyB0byBpZGVudGlmeSBvbmx5IHRoZSBib3R0bGVuZWNrIGluIHRoZSBhbGdvcml0aG0gKHdoaWNoIGlzIHVzdWFsbHkgYSB2ZXJ5IHNtYWxsIHBhcnQgb2YgaXQpIGFuZCBpbXBsZW1lbnQgb25seSB0aGUgYm90dGxlbmVjayBwYXJ0IGluIFJjcHAuCgojIFJlZmVyZW5jZXMK