Overview
This vignette demonstrates how to construct a machine learning tactical asset allocation strategy using public data and open source. The proposed strategy rotates between two ETFs: high risk such as the SPY ETF and low risk such as 7-10 years IEF Treasury ETF. The strategy returns an improved performance over a naive strategy that allocates 60-40 between the SPY and IEF over time. We provide a detailed description of the implementation, data collection, feature space selection, training, and back-testing. While the literature on return predictability is extensive, it is still important to understand such predictability from the investor’s perspective rather than the econometrician’s alone. We hope that this vignette would encourage further research and reproducibility.
Getting the Data
We refer to the quantmod package to download data from Yahoo Finance. In particular, we focus on a number of market indicators: the SPY ETFs which tracks the S&P 500 index, the VIX index known as the fear gauge which indicates the investors view about the market volatility, the GLD gold ETF, the 7-10 years treasury bond ETF, and XLF the financial sector ETF. Note that all of which are tradable except for the VIX. While VIX can be traded using an exchange traded note, our analysis here does not include its volume. Additionally, the VXX became available in early 2009, the inclusion of which would limit the sample period.
The implementation is conducted mainly using R. In particular, we refer to a number of R packages. We rely on the quantmod package to download market data from Yahoo Finance, the lubridate library to manipulate date formats, plyr for general data manipulation, the glmnet for training and implementation of the machine learning algorithm, the PerformanceAnalytics for financial analytics/summary, and both ggplot2 and plotly for visualizations.
library(quantmod)
library(lubridate)
library(plyr)
library(glmnet)
library(PerformanceAnalytics)
library(ggplot2)
library(plotly)
library(parallel)
rm(list = ls())
t1 <- "1990-01-01"
v <- c("SPY","GLD","IEF","XLF")
P.list <- lapply(v, function(sym) get(getSymbols(sym,from = t1)) )
getSymbols("^VIX",from = t1)
[1] "^VIX"
P.list <- c(P.list,list(VIX))
The P.list returns a list of data for each symbol containing 6 columns. For each item, there are different number of observations. The reason of which is that each ETF/index dates to a different time period.
sapply(P.list,dim)
[,1] [,2] [,3] [,4] [,5]
[1,] 6836 3861 4443 5346 7615
[2,] 6 6 6 6 6
To see the starting date of each, we can run the following command:
lapply(P.list, function(x) first(date(x)) )
[[1]]
[1] "1993-01-29"
[[2]]
[1] "2004-11-18"
[[3]]
[1] "2002-07-30"
[[4]]
[1] "1998-12-22"
[[5]]
[1] "1990-01-02"
We note that the SPY ETF has the earliest inception date, whereas the GLD has the latest one.
Feature Space
To construct the main feature space, we focus on the adjusted prices (sixth column) along with volume (fifth column). As mentioned above, note that volume is available for each symbol except the VIX since it is not tradable.
P.list5 <- lapply(P.list, function(x) x[,5])
P.list6 <- lapply(P.list, function(x) x[,6])
Since we are dealing with xts and zoo objects, it is straightforward to merge the time series along:
P5 <- na.omit(Reduce(function(...) merge(...),P.list5 ))
P6 <- na.omit(Reduce(function(...) merge(...),P.list6 ))
# adjust names
names(P5) <- names(P6) <- c("SPY","GLD","IEF","XLF","VIX")
names(P5) <- paste(names(P5),"vol",sep = "_")
summary(P5$VIX_vol)
Index VIX_vol
Min. :2004-11-18 Min. :0
1st Qu.:2008-09-19 1st Qu.:0
Median :2012-07-19 Median :0
Mean :2012-07-20 Mean :0
3rd Qu.:2016-05-20 3rd Qu.:0
Max. :2020-03-23 Max. :0
P5$VIX_vol <- NULL
For each adjusted price, we compute the returns. In addition, we compute the moving average of each return/change over the last 25 days. Our feature space mainly constitutes of the daily return of each symbol, the deviation of each from its MA, and the daily volume of each ETF. Note that R6_roll is a technical analysis tool that demonstrates whether the returns today are too high/low with respect to the corresponding MA. We stack the feature space in the R object. The variable of interest we are trying to model is the next day change in the SPY price
R6 <- Return.calculate(P6)
# add rolling difference
R6_roll <- R6 - rollapply(R6,25,mean)
names(R6_roll) <- paste(names(R6_roll),"_roll",sep="")
R <- na.omit(merge(R6,R6_roll,P5))
SPY_next <- stats::lag(R$SPY,-1)
names(SPY_next) <- "SPY_next"
R <- na.omit(merge(SPY_next,R))
Finally, the data ranges between
range(date(R))
[1] "2004-12-27" "2020-03-20"
Let’s take a look at the contemporaneous correlation of each feature with the SPY return
cor(R)[,"SPY"]
SPY_next SPY GLD IEF XLF VIX SPY_roll GLD_roll IEF_roll
-0.11907331 1.00000000 0.02411089 -0.42064985 0.83082759 -0.73426497 0.98736461 0.02890102 -0.41768268
XLF_roll VIX_roll SPY_vol GLD_vol IEF_vol XLF_vol
0.81905191 -0.71892022 -0.13650049 -0.08940766 -0.05839356 -0.05589110
We observe that the VIX is highly negatively correlated with the same day SPY return. The same holds true for IEF, with -41% correlation. We also note that there is a high positive correlation between the SPY and XLF. Since, the SPY_roll is a function of the same day return, it exhibits high correlation. For the volume, we observe a weak negative correlation. Nonetheless, what matters the more is how the feature space correlates with the next day SPY return than the same return. The following command provides us a perspective on such:
cor(R)[,"SPY_next"]
SPY_next SPY GLD IEF XLF VIX SPY_roll GLD_roll
1.000000000 -0.119073310 -0.045545477 0.053076235 -0.090127108 0.070958696 -0.117080655 -0.041258439
IEF_roll XLF_roll VIX_roll SPY_vol GLD_vol IEF_vol XLF_vol
0.048699628 -0.089478329 0.073908555 0.001087906 0.008234174 -0.017657529 0.008429678
Mainly, the market shows a reversal behavior, where today’s return has a negative 8% correlation with next day. This is also evident for the other indicators. Overall, we witness a weaker correlation between the next day return and the feature space. Obviously, predicting the next day return as a much more challenging task.
Response Variable
Rather than focusing on the next day return, we relate to the change in the SPY price as the response variable with two levels: down (-1) and up (+1). Specifically, we define
R$CHANGE_next <- 1
R$CHANGE_next[R$SPY_next < -0.01] <- -1
table(R$CHANGE)
-1 1
455 3380
# stack into a dataset rather than an xts object
ds <- data.frame(date = date(R),R)
rownames(ds) <- NULL
ds$SPY_next <- NULL # drop the next day return
# define features
features <- names(ds)[!names(ds) %in% c("date","CHANGE_next")]
Mainly the SPY exhibits 12% of the time a daily drop that is less than -1%. To see whether there is any heterogeneity across the feature space for each level, let’s take a look at the average of the feature space with respect to each level:
sum_change <- dlply(ds,"CHANGE_next",function(x) apply(x[,features],2,mean) )
(sum_change[[2]] - sum_change[[1]])/abs(sum_change[[1]])
SPY GLD IEF XLF VIX SPY_roll GLD_roll IEF_roll XLF_roll
2.11145747 -0.86021267 -0.45423073 0.02854905 -0.18182749 -1.22943200 -1.15739654 1.23701150 -1.19965445
VIX_roll SPY_vol GLD_vol IEF_vol XLF_vol
1.19411229 -0.37144701 -0.24487036 -0.09114107 -0.41105495
Relatively, we observe that the above feature space provides some discrepancy between the two levels. For instance, the previous day return tends to be larger when the market goes up. The opposite is true for gold and bonds.
Machine Learning Application
After defining the feature space and the response variable, we need to predict the probability of the market going up or down on a daily basis. Given this probability we, eventually, will construct our tactical asset allocation strategy.
To get started, we fit a binomial model with an elastic penalty on weekly basis to find the optimal weights to map the feature space into the next day change. Since we use an elastic net, it combines between two penalties. The first is the LASSO which acts as a constraint on the first norm of the weights and serves as an elimination process. The second is the ridge regression which is a constraint on the second norm and serves as a shrinkage approach toward zero. By design, \(\alpha=0.5\) according to the glmnet package acts as in-between penalty of the two approaches. The Lagrangian or the magnitude attributed to the penalty of each constraint, denoted by \(\lambda\), is determined using 10 folds cross validation (henceforth CV).
In the code below, we run a loop in which we train the model using 50 weeks of history and predict the next day change using the following week. Given the 50 weeks of training set, we determine the optimal model using 10 folds CV. Given the test set, we predict the probability of the SPY going either up or down. To avoid data leakage into the test set, we drop the last observation in the training set, as it may contain knowledge about the following week price change.
weeks <- date(unique(floor_date(ds$date,"week")))
weeks <- c(weeks, last(weeks) + weeks(1))
W <- 50
al <- 0.5 # net elastic
ds_predict <- data.frame()
ds_beta <- list()
w_seq <- W:(length(weeks)-2)
ds_predict_f <- function(w) {
#cat("This is week ",w, " out of ",length(weeks),"\n")
# training set consists of relatively 250 daily observations
train.weeks <- weeks[(w-W+1):(w+1)]
train.index <- which((ds$date > train.weeks[1]) & (ds$date <= train.weeks[W+1]))
# the weekly is around 5 days
test.weeks <- weeks[w+1:2]
test.index <- which((ds$date > test.weeks[1]) & (ds$date < test.weeks[2]))
# drop the last obs from the train set to avoid leakage
DS <- ds[train.index[-length(train.index)],]
x_train <- model.matrix( ~ .-1, DS[,features])
# use CV
set.seed(17)
try_error <- try(lm <- cv.glmnet(x=x_train,y = as.factor(DS$CHANGE_next), intercept=FALSE,
family = "multinomial", alpha=al, nfolds=10,parallel = T),silent = TRUE)
i <- 1
while(inherits(try_error,"try-error")) {
#cat("Error in CV","\n")
try_error <- try(lm <- cv.glmnet(x=x_train,y = as.factor(DS$CHANGE_next),
intercept=FALSE, family = "multinomial", alpha=al, nfolds=10,
parallel = T),silent = TRUE)
i <- i + 1
if (i == 10)
lm <- lm
}
# assign the lambda
best_lambda <- lm$lambda.min
# find the optimal model
lm.star = glmnet(x=x_train,y = as.factor(DS$CHANGE_next), intercept=FALSE ,
family = "multinomial", alpha=al, lambda = best_lambda)
# fit the test sample
DS_test <- ds[test.index,]
x_test <- model.matrix( ~ .-1, DS_test[,features])
DS_predict <- predict(lm.star,x_test,type = "response")
# stack in data
DS_predict <- data.frame(DS_predict)
names(DS_predict) <- c("dn","up")
DS_predict$date <- ds[test.index,"date"]
#ds_predict <- rbind(ds_predict,DS_predict)
# finally keep track of the glmnet results in a list
ds_beta <- c(ds_beta,list(lm.star))
list(DS_predict = DS_predict,lm_list = list(lm.star))
}
mclapply_list <- mclapply(w_seq,ds_predict_f,mc.cores = detectCores())
ds_predict_l <- lapply(mclapply_list, function(x) x$DS_predict )
ds_predict <- ldply(ds_predict_l,data.frame)
head(ds_predict)
The above code stacks the predicted probabilities in a data frame object named ds_predict. From each iteration, we keep track of the optimal model weights, which allows us to investigate the important features over time. Due to the rolling window nature, which is not recursive in this case, we can refer to the mclapply function for parallelization
summary(ds_predict)
dn up date
Min. :0.0000006 Min. :0.0000156 Min. :2005-12-12
1st Qu.:0.0506136 1st Qu.:0.7717289 1st Qu.:2009-07-08
Median :0.1213678 Median :0.8786322 Median :2013-01-31
Mean :0.1558049 Mean :0.8441951 Mean :2013-01-30
3rd Qu.:0.2282711 3rd Qu.:0.9493864 3rd Qu.:2016-08-24
Max. :0.9999844 Max. :0.9999994 Max. :2020-03-20
Tactical Asset Allocation Strategy
In this section, we discuss how we implement the tactical allocation strategy using the above extracted signal. In order to demonstrate this, we refer to some mathematical notation. Let
\(\hat{\pi}_{t+1}\) denote the forecasted probability that the SPY at
\(t+1\) will go up. The hat (
\(\hat{\cdot}\)) implies that the probability is a forecast established at time
\(t\) rather than
\(t+1\). Depending on the intensity of signal, we create a portfolio that either longs the SPY or the IEF. To put formally, the return of the strategy at time
\(t+1\) is given by
\[\begin{equation}
r_{s,t+1} = I_{\left[ \hat{\pi}_{t+1} > a \right]} r^{e}_{t+1} + I_{\left[ \hat{\pi}_{t+1} \leq a \right]} r^{b}_{t+1}
\end{equation}\]
where \(r^{e}_{t+1}\) and \(r^{b}_{t+1}\) denote the return on SPY and IEF at \(t+1\), respectively. The parameter \(a\) is a predetermined minimum level of confidence for the investor to participate in the equity market. The strategy implies that the investor goes long in the equity alone, if and only if the confidence level that the market will go up the next day is high enough. If not, the strategy goes $1 in the IEF ETF alone.
Backtesting
As a benchmark, we compare our results with respect to a 60-40 strategy that invests 60% in SPY and 40% in IEF. While the benchmark is re-balanced on a daily basis, it does not incorporate any market information. In other words, regardless of what happens in the market, the benchmark will always maintain a 60-40 allocation. Additionally, we compare the strategy with each ETF alone. As a performance summary, we create a summary function that takes \(a\) as the main input and reports the performance of the strategy with respect to each.
next_f <- function(x) c(x[-1],NA)
ds3$IEF_next <- next_f(ds3$IEF)
ds3$SPY_next <- next_f(ds3$SPY)
ds3$BENCHMARK <- with(ds3,0.6*SPY_next + 0.4*IEF_next)
plot_performance <- function(a) {
# round the probabilities to nearest 0.05
ds3$up_roll <- round(ds3$up_roll/0.05)*0.05
ds3$dn_roll <- round(ds3$dn_roll/0.05)*0.05
ds3$PORT <- with(ds3, (up_roll >= a)*(SPY_next) + (up_roll < a)*IEF_next )
# load ds3 into a ggplot friendly data
ds_plot <- data.frame(Date = ds3$date, return = cumsum(ds3$PORT), Type = "Strategy")
ds_plot <- rbind(ds_plot,
data.frame(Date = ds3$date, return = cumsum(ds3$BENCHMARK), Type = "Benchmark"))
ds_plot <- rbind(ds_plot,
data.frame(Date = ds3$date, return = cumsum(ds3$SPY_next), Type = "SPY"))
ds_plot <- rbind(ds_plot,
data.frame(Date = ds3$date, return = cumsum(ds3$IEF_next), Type = "IEF"))
ds_plot <- rbind(ds_plot,
data.frame(Date = ds3$date, return = ds3$dn_roll, Type = "Probability Down"))
ds_plot <- na.omit(ds_plot)
p <- ggplot(ds_plot) + geom_line(aes(x = Date,y = return,colour = Type))
p <- p + geom_abline(intercept =0,linetype = "dashed")
p <- ggplotly(p,height = 500, width = 900)
# also return the data for performance comparison
ds_perf <- ds3[,c("IEF_next","SPY_next","BENCHMARK","PORT","SPY_next")]
rownames(ds_perf) <- ds3$date
ds_perf <- as.xts(ds_perf)
list(plot_perf = p,data_perf = ds_perf)
}
Illustration
Let’s take a look the performance over time when \(a=90\%\). This represents a situation in which the investor allocates his wealth mainly to equity if the confidence level that the SPY is going up the following day is at least 90%. Otherwise, the investor is conservative and allocates his wealth to the Treasury ETF mainly.
perf1 <- plot_performance(0.9)
perf1$plot_perf
In the above plot, we observe that the proposed strategy dominates the other candidates in terms of cumulative return over the period, while at the same time trailing the SPY. Moreover, we observe that the strategy avoids the market crash during the 2007-09 financial crisis, indicating that it has a lower downside risk than the benchmark as well as the SPY. Additionally, given the market turmoil in late 2018, we observe that the the proposed strategy is successful in executing a flight to quality during times of uncertainty.
Suppose that the investor is more conservative, such he only invests in the SPY if the probability is at least 95%. In this case, we have
perf2 <- plot_performance(0.95)
perf2$plot_perf
We note that the proposed strategy avoids both the 2007-09 market crash and the recent market drop in late 2018. However, we observe that the strategy yields almost the same cumulative return as the benchmark.
Suppose that the investor is more risk tolerant, such he is willing to invest in the SPY with a level of confidence of 85%. In this case, we observe that the strategy does well in terms of cumulative return.
perf3 <- plot_performance(0.85)
perf3$plot_perf
However, at the same time, we observe that the strategy fails to shift to Treasury bonds during the recent market turmoils, draining the performance of the strategy over time.
Risk-Adjusted Returns
In terms of risk adjusted returns, we consider a number of statistics. The first is the Jensen’s alpha that captures the abnormal return above the benchmark. The second is Sortino ratio, which adjusts the mean return of the strategy with respect to a downside risk (standard deviation). Additionally, we report the beta of each strategy.
P1 <- perf1$data_perf$PORT
P2 <- perf2$data_perf$PORT
P3 <- perf3$data_perf$PORT
B <- perf1$data_perf$BENCHMARK
B2 <- perf1$data_perf$SPY_next
port_all <- list(P2,P1,P3,B,B2)
port_all <- na.omit(Reduce(merge,port_all))
names(port_all)[1:3] <- c("Portfolio_95","Portfolio_90","Portfolio_85")
table.CAPM(port_all[,1:3],port_all[,4])[c(6,2),]
In the above table, we observe that the more conservative strategies yield an annual alpha of 8%, at the same time, while being beta-neutral On the other hand, we note that when \(a\) decreases, the alpha decreases, and the strategy becomes less market beta-neutral.
In terms of downside risk, we compute the Sortino’s ratio for each strategy. Similar to the Sharpe ratio, we estimate the Sortino’s ratio as the ratio between the mean return and the downside risk. By downside risk, we refer to the semi-standard deviation in which we eliminate the positive returns in calculating the volatility. Finally, we scale the ratio into an annual basis using a scale of \(\sqrt{252}\). As an additional perspective, we report the Sharpe-ratio for comparison:
Sort_ratio <- function(x) sqrt(252)*mean(x)/sd(x[x < 0])
SR <- function(x) sqrt(252)*mean(x)/sd(x)
Sortino <- apply(port_all,2,Sort_ratio)
Sharpe <- apply(port_all,2,SR)
data.frame(rbind(Sortino,Sharpe))
In all cases, except for the 85% level confidence, we observe that the proposed strategy yields a higher risk-adjusted return than the benchmark. This is more evident for the Sortino than the Sharpe. Hence, this indicates that the strategy outperforms the benchmark by bearing less downside risk, while, at the same time, achieving a better or comparable return.
Summary
This vignette provides a simple cost-efficient trading strategy that deploys machine learning using public data and open source software. While the performance of the strategy depends on a couple of specifications, such as the level of confidence \(a\) or what determines a price change in the market, the strategy is mainly data-driven. One may consider other approaches using cross-validation to determine these inputs automatically. Nonetheless, a similar strategy can be also deployed to screen different stocks or ETFs. For instance, one may consider a similar approach to perform as tactical asset allocation across sector ETFs. i.e. sector rotation strategy. We leave this for future investigation.
Disclaimer
The above vigentte is a short illustration of an ongoing research co-authored with Kris Boudt (Vrije Universiteit Brussel, University of Amsterdam and Finvex), Muzafer Cela (Vrije Universiteit Brussel), and Majeed Simaan (Stevens Institute of Technology). The research is titled “In Search of Return Predictability: Evidence from Machine Learning and Tactical Allocation”. For further application, see the implication of the this analysis into constructing machine learning frontiers via this paper.
LS0tCnRpdGxlOiAiVGFjdGljYWwgQXNzZXQgQWxsb2NhdGlvbiB1c2luZyBNYWNoaW5lIExlYXJuaW5nIgphdXRob3I6ICJLcmlzIEJvdWR0LCAgTXV6YWZlciBDZWxhLCBhbmQgTWFqZWVkIFNpbWFhbiIKZGF0ZTogTWFyY2ggMjR0aCwgMjAyMApvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdApmaWdfd2lkdGg6IDIwMAotLS0KCiMjIE92ZXJ2aWV3ClRoaXMgdmlnbmV0dGUgZGVtb25zdHJhdGVzIGhvdyB0byBjb25zdHJ1Y3QgYSBtYWNoaW5lIGxlYXJuaW5nIHRhY3RpY2FsIGFzc2V0IGFsbG9jYXRpb24gc3RyYXRlZ3kgdXNpbmcgcHVibGljIGRhdGEgYW5kIG9wZW4gc291cmNlLiBUaGUgcHJvcG9zZWQgc3RyYXRlZ3kgcm90YXRlcyBiZXR3ZWVuIHR3byBFVEZzOiBoaWdoIHJpc2sgc3VjaCBhcyB0aGUgU1BZIEVURiBhbmQgbG93IHJpc2sgc3VjaCBhcyA3LTEwIHllYXJzIElFRiBUcmVhc3VyeSBFVEYuIFRoZSBzdHJhdGVneSByZXR1cm5zIGFuIGltcHJvdmVkIHBlcmZvcm1hbmNlIG92ZXIgYSBuYWl2ZSBzdHJhdGVneSB0aGF0IGFsbG9jYXRlcyA2MC00MCBiZXR3ZWVuIHRoZSBTUFkgYW5kIElFRiBvdmVyIHRpbWUuIFdlIHByb3ZpZGUgYSBkZXRhaWxlZCBkZXNjcmlwdGlvbiBvZiB0aGUgaW1wbGVtZW50YXRpb24sIGRhdGEgY29sbGVjdGlvbiwgZmVhdHVyZSBzcGFjZSBzZWxlY3Rpb24sIHRyYWluaW5nLCBhbmQgYmFjay10ZXN0aW5nLiBXaGlsZSB0aGUgbGl0ZXJhdHVyZSBvbiByZXR1cm4gcHJlZGljdGFiaWxpdHkgaXMgZXh0ZW5zaXZlLCBpdCBpcyBzdGlsbCBpbXBvcnRhbnQgdG8gdW5kZXJzdGFuZCBzdWNoIHByZWRpY3RhYmlsaXR5IGZyb20gdGhlIGludmVzdG9yJ3MgcGVyc3BlY3RpdmUgcmF0aGVyIHRoYW4gdGhlIGVjb25vbWV0cmljaWFuJ3MgYWxvbmUuIFdlIGhvcGUgdGhhdCB0aGlzIHZpZ25ldHRlIHdvdWxkIGVuY291cmFnZSBmdXJ0aGVyIHJlc2VhcmNoIGFuZCByZXByb2R1Y2liaWxpdHkuCgoKIyMgR2V0dGluZyB0aGUgRGF0YQpXZSByZWZlciB0byB0aGUgYHF1YW50bW9kYCBwYWNrYWdlIHRvIGRvd25sb2FkIGRhdGEgZnJvbSBZYWhvbyBGaW5hbmNlLiBJbiBwYXJ0aWN1bGFyLCB3ZSBmb2N1cyBvbiBhIG51bWJlciBvZiBtYXJrZXQgaW5kaWNhdG9yczogdGhlIFNQWSBFVEZzIHdoaWNoIHRyYWNrcyB0aGUgU1wmUCA1MDAgaW5kZXgsIHRoZSBWSVggaW5kZXgga25vd24gYXMgdGhlIGZlYXIgZ2F1Z2Ugd2hpY2ggaW5kaWNhdGVzIHRoZSBpbnZlc3RvcnMgdmlldyBhYm91dCB0aGUgbWFya2V0IHZvbGF0aWxpdHksIHRoZSBHTEQgZ29sZCBFVEYsIHRoZSA3LTEwIHllYXJzIHRyZWFzdXJ5IGJvbmQgRVRGLCBhbmQgWExGIHRoZSBmaW5hbmNpYWwgc2VjdG9yIEVURi4gTm90ZSB0aGF0IGFsbCBvZiB3aGljaCBhcmUgdHJhZGFibGUgZXhjZXB0IGZvciB0aGUgVklYLiBXaGlsZSBWSVggY2FuIGJlIHRyYWRlZCB1c2luZyBhbiBleGNoYW5nZSB0cmFkZWQgbm90ZSwgb3VyIGFuYWx5c2lzIGhlcmUgZG9lcyBub3QgaW5jbHVkZSBpdHMgdm9sdW1lLiBBZGRpdGlvbmFsbHksIHRoZSBWWFggYmVjYW1lIGF2YWlsYWJsZSBpbiBlYXJseSAyMDA5LCB0aGUgaW5jbHVzaW9uIG9mIHdoaWNoIHdvdWxkIGxpbWl0IHRoZSBzYW1wbGUgcGVyaW9kLgoKVGhlIGltcGxlbWVudGF0aW9uIGlzIGNvbmR1Y3RlZCBtYWlubHkgdXNpbmcgUi4gSW4gcGFydGljdWxhciwgd2UgcmVmZXIgdG8gYSBudW1iZXIgb2YgUiBwYWNrYWdlcy4gV2UgcmVseSBvbiB0aGUgYHF1YW50bW9kYCBwYWNrYWdlIHRvIGRvd25sb2FkIG1hcmtldCBkYXRhIGZyb20gWWFob28gRmluYW5jZSwgdGhlIGBsdWJyaWRhdGVgIGxpYnJhcnkgdG8gbWFuaXB1bGF0ZSBkYXRlIGZvcm1hdHMsIGBwbHlyYCBmb3IgZ2VuZXJhbCBkYXRhIG1hbmlwdWxhdGlvbiwgdGhlIGBnbG1uZXRgIGZvciB0cmFpbmluZyBhbmQgaW1wbGVtZW50YXRpb24gb2YgdGhlIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtLCB0aGUgYFBlcmZvcm1hbmNlQW5hbHl0aWNzYCBmb3IgZmluYW5jaWFsIGFuYWx5dGljcy9zdW1tYXJ5LCBhbmQgYm90aCBgZ2dwbG90MmAgYW5kIGBwbG90bHlgIGZvciAgdmlzdWFsaXphdGlvbnMuIApgYGB7cix3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkocXVhbnRtb2QpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHBseXIpCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHBhcmFsbGVsKQoKcm0obGlzdCA9IGxzKCkpCgp0MSA8LSAiMTk5MC0wMS0wMSIKdiA8LSBjKCJTUFkiLCJHTEQiLCJJRUYiLCJYTEYiKQpQLmxpc3QgPC0gbGFwcGx5KHYsIGZ1bmN0aW9uKHN5bSkgZ2V0KGdldFN5bWJvbHMoc3ltLGZyb20gPSB0MSkpICkKZ2V0U3ltYm9scygiXlZJWCIsZnJvbSA9IHQxKQpQLmxpc3QgPC0gYyhQLmxpc3QsbGlzdChWSVgpKQpgYGAKVGhlIGBQLmxpc3RgIHJldHVybnMgYSBsaXN0IG9mIGRhdGEgZm9yIGVhY2ggc3ltYm9sIGNvbnRhaW5pbmcgNiBjb2x1bW5zLiBGb3IgZWFjaCBpdGVtLCB0aGVyZSBhcmUgZGlmZmVyZW50IG51bWJlciBvZiBvYnNlcnZhdGlvbnMuIFRoZSByZWFzb24gb2Ygd2hpY2ggaXMgdGhhdCBlYWNoIEVURi9pbmRleCBkYXRlcyB0byBhIGRpZmZlcmVudCB0aW1lIHBlcmlvZC4KCmBgYHtyfQpzYXBwbHkoUC5saXN0LGRpbSkKYGBgClRvIHNlZSB0aGUgc3RhcnRpbmcgZGF0ZSBvZiBlYWNoLCB3ZSBjYW4gcnVuIHRoZSBmb2xsb3dpbmcgY29tbWFuZDoKYGBge3J9CmxhcHBseShQLmxpc3QsIGZ1bmN0aW9uKHgpICBmaXJzdChkYXRlKHgpKSApCmBgYApXZSBub3RlIHRoYXQgdGhlIFNQWSBFVEYgaGFzIHRoZSBlYXJsaWVzdCBpbmNlcHRpb24gZGF0ZSwgd2hlcmVhcyB0aGUgR0xEIGhhcyB0aGUgbGF0ZXN0IG9uZS4gCgojIyMgRmVhdHVyZSBTcGFjZQpUbyBjb25zdHJ1Y3QgdGhlIG1haW4gZmVhdHVyZSBzcGFjZSwgd2UgZm9jdXMgb24gdGhlIGFkanVzdGVkIHByaWNlcyAoc2l4dGggY29sdW1uKSBhbG9uZyB3aXRoIHZvbHVtZSAoZmlmdGggY29sdW1uKS4gQXMgbWVudGlvbmVkIGFib3ZlLCBub3RlIHRoYXQgdm9sdW1lIGlzIGF2YWlsYWJsZSBmb3IgZWFjaCBzeW1ib2wgZXhjZXB0IHRoZSBWSVggc2luY2UgaXQgaXMgbm90IHRyYWRhYmxlLiAKYGBge3J9ClAubGlzdDUgPC0gbGFwcGx5KFAubGlzdCwgZnVuY3Rpb24oeCkgeFssNV0pClAubGlzdDYgPC0gbGFwcGx5KFAubGlzdCwgZnVuY3Rpb24oeCkgeFssNl0pCmBgYApTaW5jZSB3ZSBhcmUgZGVhbGluZyB3aXRoIGB4dHNgIGFuZCBgem9vYCBvYmplY3RzLCBpdCBpcyBzdHJhaWdodGZvcndhcmQgdG8gbWVyZ2UgdGhlIHRpbWUgc2VyaWVzIGFsb25nOgpgYGB7cn0KUDUgPC0gbmEub21pdChSZWR1Y2UoZnVuY3Rpb24oLi4uKSBtZXJnZSguLi4pLFAubGlzdDUgKSkKUDYgPC0gbmEub21pdChSZWR1Y2UoZnVuY3Rpb24oLi4uKSBtZXJnZSguLi4pLFAubGlzdDYgKSkKCiMgYWRqdXN0IG5hbWVzCm5hbWVzKFA1KSA8LSBuYW1lcyhQNikgPC0gYygiU1BZIiwiR0xEIiwiSUVGIiwiWExGIiwiVklYIikKbmFtZXMoUDUpIDwtIHBhc3RlKG5hbWVzKFA1KSwidm9sIixzZXAgPSAiXyIpCnN1bW1hcnkoUDUkVklYX3ZvbCkKUDUkVklYX3ZvbCA8LSBOVUxMCmBgYAoKRm9yIGVhY2ggYWRqdXN0ZWQgcHJpY2UsIHdlIGNvbXB1dGUgdGhlIHJldHVybnMuIEluIGFkZGl0aW9uLCB3ZSBjb21wdXRlIHRoZSBtb3ZpbmcgYXZlcmFnZSBvZiBlYWNoIHJldHVybi9jaGFuZ2Ugb3ZlciB0aGUgbGFzdCAyNSBkYXlzLiBPdXIgZmVhdHVyZSBzcGFjZSBtYWlubHkgY29uc3RpdHV0ZXMgb2YgdGhlIGRhaWx5IHJldHVybiBvZiBlYWNoIHN5bWJvbCwgdGhlIGRldmlhdGlvbiBvZiBlYWNoIGZyb20gaXRzIE1BLCBhbmQgdGhlIGRhaWx5IHZvbHVtZSBvZiBlYWNoIEVURi4gTm90ZSB0aGF0IGBSNl9yb2xsYCBpcyBhIHRlY2huaWNhbCBhbmFseXNpcyB0b29sIHRoYXQgZGVtb25zdHJhdGVzIHdoZXRoZXIgdGhlIHJldHVybnMgdG9kYXkgYXJlIHRvbyBoaWdoL2xvdyB3aXRoIHJlc3BlY3QgdG8gdGhlIGNvcnJlc3BvbmRpbmcgTUEuIFdlIHN0YWNrIHRoZSBmZWF0dXJlIHNwYWNlIGluIHRoZSBgUmAgb2JqZWN0LiBUaGUgdmFyaWFibGUgb2YgaW50ZXJlc3Qgd2UgYXJlIHRyeWluZyB0byBtb2RlbCBpcyB0aGUgbmV4dCBkYXkgY2hhbmdlIGluIHRoZSBTUFkgcHJpY2UKYGBge3J9ClI2IDwtIFJldHVybi5jYWxjdWxhdGUoUDYpCiMgYWRkIHJvbGxpbmcgZGlmZmVyZW5jZSAKUjZfcm9sbCA8LSBSNiAtIHJvbGxhcHBseShSNiwyNSxtZWFuKQpuYW1lcyhSNl9yb2xsKSA8LSBwYXN0ZShuYW1lcyhSNl9yb2xsKSwiX3JvbGwiLHNlcD0iIikKClIgPC0gbmEub21pdChtZXJnZShSNixSNl9yb2xsLFA1KSkKClNQWV9uZXh0IDwtIHN0YXRzOjpsYWcoUiRTUFksLTEpCm5hbWVzKFNQWV9uZXh0KSA8LSAiU1BZX25leHQiClIgPC0gbmEub21pdChtZXJnZShTUFlfbmV4dCxSKSkKYGBgCkZpbmFsbHksIHRoZSBkYXRhIHJhbmdlcyBiZXR3ZWVuCmBgYHtyfQpyYW5nZShkYXRlKFIpKQpgYGAKCgoKTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGNvbnRlbXBvcmFuZW91cyBjb3JyZWxhdGlvbiBvZiBlYWNoIGZlYXR1cmUgd2l0aCB0aGUgU1BZIHJldHVybgpgYGB7cn0KY29yKFIpWywiU1BZIl0KYGBgCiBXZSBvYnNlcnZlIHRoYXQgdGhlIFZJWCBpcyBoaWdobHkgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggdGhlIHNhbWUgZGF5IFNQWSByZXR1cm4uIFRoZSBzYW1lIGhvbGRzIHRydWUgZm9yIElFRiwgd2l0aCAtNDFcJSBjb3JyZWxhdGlvbi4gV2UgYWxzbyBub3RlIHRoYXQgdGhlcmUgaXMgYSBoaWdoIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIFNQWSBhbmQgWExGLiBTaW5jZSwgdGhlIGBTUFlfcm9sbGAgaXMgYSBmdW5jdGlvbiBvZiB0aGUgc2FtZSBkYXkgcmV0dXJuLCBpdCBleGhpYml0cyBoaWdoIGNvcnJlbGF0aW9uLiBGb3IgdGhlIHZvbHVtZSwgd2Ugb2JzZXJ2ZSBhIHdlYWsgbmVnYXRpdmUgY29ycmVsYXRpb24uICBOb25ldGhlbGVzcywgd2hhdCBtYXR0ZXJzIHRoZSBtb3JlIGlzIGhvdyB0aGUgZmVhdHVyZSBzcGFjZSBjb3JyZWxhdGVzIHdpdGggdGhlIG5leHQgZGF5IFNQWSByZXR1cm4gdGhhbiB0aGUgc2FtZSByZXR1cm4uIFRoZSBmb2xsb3dpbmcgY29tbWFuZCBwcm92aWRlcyB1cyBhIHBlcnNwZWN0aXZlIG9uIHN1Y2g6CmBgYHtyfQpjb3IoUilbLCJTUFlfbmV4dCJdCmBgYAoKTWFpbmx5LCB0aGUgbWFya2V0IHNob3dzIGEgcmV2ZXJzYWwgYmVoYXZpb3IsIHdoZXJlIHRvZGF5J3MgcmV0dXJuIGhhcyBhIG5lZ2F0aXZlIDhcJSBjb3JyZWxhdGlvbiB3aXRoIG5leHQgZGF5LiBUaGlzIGlzIGFsc28gZXZpZGVudCBmb3IgdGhlIG90aGVyIGluZGljYXRvcnMuIE92ZXJhbGwsIHdlIHdpdG5lc3MgYSB3ZWFrZXIgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgbmV4dCBkYXkgcmV0dXJuIGFuZCB0aGUgZmVhdHVyZSBzcGFjZS4gT2J2aW91c2x5LCBwcmVkaWN0aW5nIHRoZSBuZXh0IGRheSByZXR1cm4gYXMgYSBtdWNoIG1vcmUgY2hhbGxlbmdpbmcgdGFzay4gCgojIyMgUmVzcG9uc2UgVmFyaWFibGUKUmF0aGVyIHRoYW4gZm9jdXNpbmcgb24gdGhlIG5leHQgZGF5IHJldHVybiwgd2UgcmVsYXRlIHRvIHRoZSBjaGFuZ2UgaW4gdGhlIFNQWSBwcmljZSBhcyB0aGUgcmVzcG9uc2UgdmFyaWFibGUgd2l0aCB0d28gbGV2ZWxzOiBkb3duICgtMSkgYW5kIHVwICgrMSkuIFNwZWNpZmljYWxseSwgd2UgZGVmaW5lCmBgYHtyfQpSJENIQU5HRV9uZXh0IDwtIDEKUiRDSEFOR0VfbmV4dFtSJFNQWV9uZXh0IDwgLTAuMDFdIDwtIC0xCnRhYmxlKFIkQ0hBTkdFKQoKIyBzdGFjayBpbnRvIGEgZGF0YXNldCByYXRoZXIgdGhhbiBhbiB4dHMgb2JqZWN0CmRzIDwtIGRhdGEuZnJhbWUoZGF0ZSA9IGRhdGUoUiksUikKcm93bmFtZXMoZHMpIDwtIE5VTEwKZHMkU1BZX25leHQgPC0gTlVMTCAjIGRyb3AgdGhlIG5leHQgZGF5IHJldHVybgoKIyBkZWZpbmUgZmVhdHVyZXMKZmVhdHVyZXMgPC0gbmFtZXMoZHMpWyFuYW1lcyhkcykgJWluJSBjKCJkYXRlIiwiQ0hBTkdFX25leHQiKV0KYGBgCk1haW5seSB0aGUgU1BZIGV4aGliaXRzIDEyXCUgb2YgdGhlIHRpbWUgYSBkYWlseSBkcm9wIHRoYXQgaXMgbGVzcyB0aGFuIC0xXCUuICBUbyBzZWUgd2hldGhlciB0aGVyZSBpcyBhbnkgaGV0ZXJvZ2VuZWl0eSBhY3Jvc3MgdGhlIGZlYXR1cmUgc3BhY2UgZm9yIGVhY2ggbGV2ZWwsIGxldCdzIHRha2UgYSBsb29rIGF0IHRoZSBhdmVyYWdlIG9mIHRoZSBmZWF0dXJlIHNwYWNlIHdpdGggcmVzcGVjdCB0byBlYWNoIGxldmVsOiAKYGBge3J9CnN1bV9jaGFuZ2UgPC0gZGxwbHkoZHMsIkNIQU5HRV9uZXh0IixmdW5jdGlvbih4KSAgYXBwbHkoeFssZmVhdHVyZXNdLDIsbWVhbikgKQooc3VtX2NoYW5nZVtbMl1dIC0gc3VtX2NoYW5nZVtbMV1dKS9hYnMoc3VtX2NoYW5nZVtbMV1dKQpgYGAKCgpSZWxhdGl2ZWx5LCB3ZSBvYnNlcnZlIHRoYXQgdGhlIGFib3ZlIGZlYXR1cmUgc3BhY2UgcHJvdmlkZXMgc29tZSBkaXNjcmVwYW5jeSBiZXR3ZWVuIHRoZSB0d28gbGV2ZWxzLiBGb3IgaW5zdGFuY2UsIHRoZSBwcmV2aW91cyBkYXkgcmV0dXJuIHRlbmRzIHRvIGJlIGxhcmdlciB3aGVuIHRoZSBtYXJrZXQgZ29lcyB1cC4gVGhlIG9wcG9zaXRlIGlzIHRydWUgZm9yIGdvbGQgYW5kIGJvbmRzLiAKCiMjIE1hY2hpbmUgTGVhcm5pbmcgQXBwbGljYXRpb24KQWZ0ZXIgZGVmaW5pbmcgdGhlIGZlYXR1cmUgc3BhY2UgYW5kIHRoZSByZXNwb25zZSB2YXJpYWJsZSwgd2UgbmVlZCB0byBwcmVkaWN0IHRoZSBwcm9iYWJpbGl0eSBvZiB0aGUgbWFya2V0IGdvaW5nIHVwIG9yIGRvd24gb24gYSBkYWlseSBiYXNpcy4gR2l2ZW4gdGhpcyBwcm9iYWJpbGl0eSB3ZSwgZXZlbnR1YWxseSwgd2lsbCBjb25zdHJ1Y3Qgb3VyIHRhY3RpY2FsIGFzc2V0IGFsbG9jYXRpb24gc3RyYXRlZ3kuIAoKVG8gZ2V0IHN0YXJ0ZWQsIHdlIGZpdCBhIGJpbm9taWFsIG1vZGVsIHdpdGggYW4gZWxhc3RpYyBwZW5hbHR5IG9uIHdlZWtseSBiYXNpcyB0byBmaW5kIHRoZSBvcHRpbWFsIHdlaWdodHMgdG8gbWFwIHRoZSBmZWF0dXJlIHNwYWNlIGludG8gdGhlIG5leHQgZGF5IGNoYW5nZS4gU2luY2Ugd2UgdXNlIGFuIGVsYXN0aWMgbmV0LCBpdCBjb21iaW5lcyBiZXR3ZWVuIHR3byBwZW5hbHRpZXMuIFRoZSBmaXJzdCBpcyB0aGUgTEFTU08gd2hpY2ggYWN0cyBhcyBhIGNvbnN0cmFpbnQgb24gdGhlIGZpcnN0IG5vcm0gb2YgdGhlIHdlaWdodHMgYW5kIHNlcnZlcyBhcyBhbiBlbGltaW5hdGlvbiBwcm9jZXNzLiBUaGUgIHNlY29uZCBpcyB0aGUgcmlkZ2UgcmVncmVzc2lvbiB3aGljaCBpcyBhIGNvbnN0cmFpbnQgb24gdGhlIHNlY29uZCBub3JtIGFuZCBzZXJ2ZXMgYXMgYSBzaHJpbmthZ2UgYXBwcm9hY2ggdG93YXJkIHplcm8uIEJ5IGRlc2lnbiwgICRcYWxwaGE9MC41JCBhY2NvcmRpbmcgdG8gdGhlIGBnbG1uZXRgIHBhY2thZ2UgYWN0cyBhcyBpbi1iZXR3ZWVuIHBlbmFsdHkgb2YgdGhlIHR3byBhcHByb2FjaGVzLiBUaGUgTGFncmFuZ2lhbiBvciB0aGUgbWFnbml0dWRlIGF0dHJpYnV0ZWQgdG8gdGhlIHBlbmFsdHkgb2YgZWFjaCBjb25zdHJhaW50LCBkZW5vdGVkIGJ5ICRcbGFtYmRhJCwgaXMgZGV0ZXJtaW5lZCB1c2luZyAxMCBmb2xkcyBjcm9zcyB2YWxpZGF0aW9uIChoZW5jZWZvcnRoIENWKS4gCgpJbiB0aGUgY29kZSBiZWxvdywgd2UgcnVuIGEgbG9vcCBpbiB3aGljaCB3ZSB0cmFpbiB0aGUgbW9kZWwgdXNpbmcgNTAgd2Vla3Mgb2YgaGlzdG9yeSBhbmQgcHJlZGljdCB0aGUgbmV4dCBkYXkgY2hhbmdlIHVzaW5nIHRoZSBmb2xsb3dpbmcgd2Vlay4gR2l2ZW4gdGhlIDUwIHdlZWtzIG9mIHRyYWluaW5nIHNldCwgd2UgZGV0ZXJtaW5lIHRoZSBvcHRpbWFsIG1vZGVsIHVzaW5nIDEwIGZvbGRzIENWLiBHaXZlbiB0aGUgdGVzdCBzZXQsIHdlIHByZWRpY3QgdGhlIHByb2JhYmlsaXR5IG9mIHRoZSBTUFkgZ29pbmcgZWl0aGVyIHVwIG9yIGRvd24uIFRvIGF2b2lkIGRhdGEgbGVha2FnZSBpbnRvIHRoZSB0ZXN0IHNldCwgd2UgZHJvcCB0aGUgbGFzdCBvYnNlcnZhdGlvbiBpbiB0aGUgdHJhaW5pbmcgc2V0LCBhcyBpdCBtYXkgY29udGFpbiBrbm93bGVkZ2UgYWJvdXQgdGhlIGZvbGxvd2luZyB3ZWVrIHByaWNlIGNoYW5nZS4KCmBgYHtyLHdhcm5pbmc9RkFMU0V9CndlZWtzIDwtIGRhdGUodW5pcXVlKGZsb29yX2RhdGUoZHMkZGF0ZSwid2VlayIpKSkKd2Vla3MgPC0gYyh3ZWVrcywgbGFzdCh3ZWVrcykgKyB3ZWVrcygxKSkKClcgPC0gNTAKYWwgPC0gMC41ICMgbmV0IGVsYXN0aWMKZHNfcHJlZGljdCA8LSBkYXRhLmZyYW1lKCkKZHNfYmV0YSA8LSBsaXN0KCkKCndfc2VxIDwtIFc6KGxlbmd0aCh3ZWVrcyktMikKCmRzX3ByZWRpY3RfZiA8LSBmdW5jdGlvbih3KSB7CiAgI2NhdCgiVGhpcyBpcyB3ZWVrICIsdywgIiBvdXQgb2YgIixsZW5ndGgod2Vla3MpLCJcbiIpCiAgIyB0cmFpbmluZyBzZXQgY29uc2lzdHMgb2YgcmVsYXRpdmVseSAyNTAgZGFpbHkgb2JzZXJ2YXRpb25zCiAgdHJhaW4ud2Vla3MgPC0gd2Vla3NbKHctVysxKToodysxKV0KICB0cmFpbi5pbmRleCA8LSB3aGljaCgoZHMkZGF0ZSA+IHRyYWluLndlZWtzWzFdKSAmIChkcyRkYXRlIDw9IHRyYWluLndlZWtzW1crMV0pKQogIAogICMgdGhlIHdlZWtseSBpcyBhcm91bmQgNSBkYXlzCiAgdGVzdC53ZWVrcyA8LSAgd2Vla3NbdysxOjJdCiAgdGVzdC5pbmRleCA8LSB3aGljaCgoZHMkZGF0ZSA+IHRlc3Qud2Vla3NbMV0pICYgKGRzJGRhdGUgPCB0ZXN0LndlZWtzWzJdKSkKICAKICAjIGRyb3AgdGhlIGxhc3Qgb2JzIGZyb20gdGhlIHRyYWluIHNldCB0byBhdm9pZCBsZWFrYWdlCiAgRFMgPC0gZHNbdHJhaW4uaW5kZXhbLWxlbmd0aCh0cmFpbi5pbmRleCldLF0KICB4X3RyYWluIDwtIG1vZGVsLm1hdHJpeCggfiAuLTEsIERTWyxmZWF0dXJlc10pCiAgCiAgIyB1c2UgQ1YKICBzZXQuc2VlZCgxNykKICB0cnlfZXJyb3IgPC0gdHJ5KGxtIDwtIGN2LmdsbW5ldCh4PXhfdHJhaW4seSA9IGFzLmZhY3RvcihEUyRDSEFOR0VfbmV4dCksIGludGVyY2VwdD1GQUxTRSwKICAgICAgICAgICAgICAgICBmYW1pbHkgPSAgICJtdWx0aW5vbWlhbCIsIGFscGhhPWFsLCBuZm9sZHM9MTAscGFyYWxsZWwgPSBUKSxzaWxlbnQgPSBUUlVFKQogIAogIGkgPC0gMQogIHdoaWxlKGluaGVyaXRzKHRyeV9lcnJvciwidHJ5LWVycm9yIikpIHsKICAgICNjYXQoIkVycm9yIGluIENWIiwiXG4iKQogICAgdHJ5X2Vycm9yIDwtIHRyeShsbSA8LSBjdi5nbG1uZXQoeD14X3RyYWluLHkgPSBhcy5mYWN0b3IoRFMkQ0hBTkdFX25leHQpLAogICAgICAgICAgICAgICAgICBpbnRlcmNlcHQ9RkFMU0UsIGZhbWlseSA9ICAgIm11bHRpbm9taWFsIiwgYWxwaGE9YWwsIG5mb2xkcz0xMCwKICAgICAgICAgICAgICAgICAgcGFyYWxsZWwgPSBUKSxzaWxlbnQgPSBUUlVFKQogICAgaSA8LSBpICsgMQogICAgaWYgKGkgPT0gMTApCiAgICAgICAgbG0gPC0gbG0KICAgIH0KICAKICAjIGFzc2lnbiB0aGUgbGFtYmRhCiAgYmVzdF9sYW1iZGEgPC0gbG0kbGFtYmRhLm1pbgogIAogICMgZmluZCB0aGUgb3B0aW1hbCBtb2RlbAogIGxtLnN0YXIgPSBnbG1uZXQoeD14X3RyYWluLHkgPSBhcy5mYWN0b3IoRFMkQ0hBTkdFX25leHQpLCBpbnRlcmNlcHQ9RkFMU0UgLAogICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gICAibXVsdGlub21pYWwiLCBhbHBoYT1hbCwgbGFtYmRhID0gYmVzdF9sYW1iZGEpCiAgCiAgIyBmaXQgdGhlIHRlc3Qgc2FtcGxlCiAgRFNfdGVzdCA8LSBkc1t0ZXN0LmluZGV4LF0KICB4X3Rlc3QgPC0gbW9kZWwubWF0cml4KCB+IC4tMSwgRFNfdGVzdFssZmVhdHVyZXNdKQogIERTX3ByZWRpY3QgPC0gcHJlZGljdChsbS5zdGFyLHhfdGVzdCx0eXBlID0gInJlc3BvbnNlIikKCiAgIyBzdGFjayBpbiBkYXRhCiAgRFNfcHJlZGljdCA8LSBkYXRhLmZyYW1lKERTX3ByZWRpY3QpCiAgbmFtZXMoRFNfcHJlZGljdCkgPC0gYygiZG4iLCJ1cCIpCiAgRFNfcHJlZGljdCRkYXRlIDwtIGRzW3Rlc3QuaW5kZXgsImRhdGUiXQogICNkc19wcmVkaWN0IDwtIHJiaW5kKGRzX3ByZWRpY3QsRFNfcHJlZGljdCkKICAKICAjIGZpbmFsbHkga2VlcCB0cmFjayBvZiB0aGUgZ2xtbmV0IHJlc3VsdHMgaW4gYSBsaXN0CiAgZHNfYmV0YSA8LSBjKGRzX2JldGEsbGlzdChsbS5zdGFyKSkKICAKICBsaXN0KERTX3ByZWRpY3QgPSBEU19wcmVkaWN0LGxtX2xpc3QgPSBsaXN0KGxtLnN0YXIpKQogIH0KCm1jbGFwcGx5X2xpc3QgPC0gbWNsYXBwbHkod19zZXEsZHNfcHJlZGljdF9mLG1jLmNvcmVzID0gZGV0ZWN0Q29yZXMoKSkKCmRzX3ByZWRpY3RfbCA8LSBsYXBwbHkobWNsYXBwbHlfbGlzdCwgZnVuY3Rpb24oeCkgeCREU19wcmVkaWN0ICkKZHNfcHJlZGljdCA8LSBsZHBseShkc19wcmVkaWN0X2wsZGF0YS5mcmFtZSkKaGVhZChkc19wcmVkaWN0KQpgYGAKClRoZSBhYm92ZSBjb2RlIHN0YWNrcyB0aGUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMgaW4gYSBkYXRhIGZyYW1lIG9iamVjdCBuYW1lZCBgZHNfcHJlZGljdGAuIEZyb20gZWFjaCBpdGVyYXRpb24sIHdlIGtlZXAgdHJhY2sgb2YgdGhlIG9wdGltYWwgbW9kZWwgd2VpZ2h0cywgd2hpY2ggYWxsb3dzIHVzIHRvIGludmVzdGlnYXRlIHRoZSBpbXBvcnRhbnQgZmVhdHVyZXMgb3ZlciB0aW1lLiBEdWUgdG8gdGhlIHJvbGxpbmcgd2luZG93IG5hdHVyZSwgd2hpY2ggaXMgbm90IHJlY3Vyc2l2ZSBpbiB0aGlzIGNhc2UsIHdlIGNhbiByZWZlciB0byB0aGUgYG1jbGFwcGx5YCBmdW5jdGlvbiBmb3IgcGFyYWxsZWxpemF0aW9uIAoKCmBgYHtyfQpzdW1tYXJ5KGRzX3ByZWRpY3QpCmBgYAoKIyMjIFNpZ25hbCBFeHRyYWN0aW9uCldlIG1lcmdlIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyB3aXRoIHRoZSBvcmlnaW5hbCBkYXRhIHRvIGNvbnN0cnVjdCB0aGUgbWFpbiBkYXRhc2hlZXQsIHdoaWNoIHdlIHdpbGwgcmVmZXIgdG8gZm9yIHRoZSB0cmFkaW5nIHN0cmF0ZWd5IGFuZCwgaGVuY2UsIHRoZSBiYWNrLXRlc3RpbmcuIApgYGB7cn0KZHMyIDwtIG1lcmdlKGRzLGRzX3ByZWRpY3QsYnkgPSAiZGF0ZSIpCnVwZG4gPC0gZGF0YS5mcmFtZShkbiA9IGRzMiRkbix1cCA9ZHMyJHVwKQpyb3duYW1lcyh1cGRuKSA8LSBkczIkZGF0ZQp1cGRuIDwtIGFzLnh0cyh1cGRuKQp1cGRuX3JvbGwgPC0gbmEub21pdChyb2xsYXBwbHkodXBkbiwyNSxtZWFuKSkKbmFtZXModXBkbl9yb2xsKSA8LSBjKCJkbl9yb2xsIiwidXBfcm9sbCIpCmRzLnVwZG4gPC0gZGF0YS5mcmFtZShkYXRlID0gZGF0ZSh1cGRuX3JvbGwpLHVwZG5fcm9sbCkKcm93bmFtZXMoZHMudXBkbikgPC0gTlVMTApkczMgPC0gbWVyZ2UoZHMyLGRzLnVwZG4sIGJ5ID0gImRhdGUiKQpgYGAKClRvIGNyZWF0ZSBhIHNtb290aCBzaWduYWwsIHdlIGNvbnNpZGVyIHRoZSAyNSBkYXlzIG1vdmluZyBhdmVyYWdlIG9mIHRoZSBwcmVkaWN0ZWQgcHJvYmFiaWxpdHkuIFdlIG1lcmdlIHRoZSBzbW9vdGhlZCBwcm9iYWJpbGl0aWVzIGFsb25nIHdpdGggdGhlIEVURiByZXR1cm5zIGluIGEgIG5ldyBkYXRhIGZyYW1lIG5hbWVkIGBkczNgLgoKVG8gYWdhaW4gYW4gaW5pdGlhbCBwZXJzcGVjdGl2ZSBvbiB0aGUgZXh0cmFjdGVkIHNpZ25hbCwgd2UgcGxvdCB0aGUgY3VtdWxhdGl2ZSByZXR1cm4gb24gdGhlIFNQWSBhbG9uZyB3aXRoIHRoZSBwcm9iYWJpbGl0eSBnb2luZyBkb3duIG92ZXIgdGltZS4gSW50ZXJlc3RpbmdseSwgd2Ugb2JzZXJ2ZSB0aGF0IHRoZSBzaWduYWwgKHJlZCBsaW5lKSwgd2hpY2ggaXMgZGF0YSBkcml2ZW4sIHJlYWNoZXMgdGhlIGhpZ2hlc3QgbGV2ZWwgZHVyaW5nIHRoZSAyMDA3LTA5IGZpbmFuY2lhbCBjcmlzaXMsIGR1cmluZyB3aGljaCB0aGUgU1BZIChibGFjayBsaW5lKSBleGhpYml0cyB0aGUgbGFyZ2VzdCBvdmVyIHRoZSB3aG9sZSBzYW1wbGUuIEFkZGl0aW9uYWxseSwgd2hpbGUgdGhlIHByb2JhYmlsaXR5IHNlZW1zIGRlY2xpbmluZyB0aGUgbGFzdCBjb3VwbGUgb2YgeWVhciwgd2Ugb2JzZXJ2ZSB0aGF0IGl0IHBpY2tzIHVwIGR1cmluZyB0aGUgcmVjZW50IG1hcmtldCB0dXJtb2lscyBpbiBsYXRlIDIwMTguIE92ZXJhbGwsIHdlIG9ic2VydmUgYSBuZWdhdGl2ZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvLgoKYGBge3IsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGxvdChjdW1zdW0oU1BZKSB+IGRhdGUsIGRhdGEgPSBkczMsIHR5cGUgPSAibCIseWxhYiA9ICIiLCBtYWluICA9ICJjdW11bGF0aXZlIHJldHVybiBvZiBTUFkgdmVyc3VzIHByb2JhYmlsaXR5IGRvd24iKQpsaW5lcyhkbl9yb2xsIH4gZGF0ZSwgZGF0YSA9IGRzMyxjb2wgPSAyKQphYmxpbmUoaCA9IDAsIGx0eT0yKQpsZWdlbmQoInRvcGxlZnQiLGMoIlNQWSIsIlByb2JhYmlsaXR5IERvd24iKSxjb2wgPSAxOjIsIGx0eSA9IDEpCmdyaWQoMTApCmBgYApUaGUgYWJvdmUgc3VnZ2VzdHMgdGhhdCB0aGUgcHJvYmFiaWxpdHkgb2YgZG93bi91cCBjYW4gYmUgdXRpbGl6ZSB0byBhdm9pZCBzaWduaWZpY2FudCBtYXJrZXQgZHJvcHMuIFRoZSBxdWVzdGlvbiBpcyBob3cgY2FuIHdlIHV0aWxpemUgdGhpcz8gSW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uLCB3ZSBwcm9wb3NlIGFuIGF1dG9tYXRlZCB0cmFkaW5nIHN0cmF0ZWd5IHRoYXQgdHJpZXMgdG8gbWF0ZXJpYWxpemUgdGhlIGFib3ZlIGluc2lnaHRzLgoKIyMgVGFjdGljYWwgQXNzZXQgQWxsb2NhdGlvbiBTdHJhdGVneSAKSW4gdGhpcyBzZWN0aW9uLCB3ZSBkaXNjdXNzIGhvdyB3ZSBpbXBsZW1lbnQgdGhlIHRhY3RpY2FsIGFsbG9jYXRpb24gc3RyYXRlZ3kgdXNpbmcgdGhlIGFib3ZlIGV4dHJhY3RlZCBzaWduYWwuIEluIG9yZGVyIHRvIGRlbW9uc3RyYXRlIHRoaXMsIHdlIHJlZmVyIHRvIHNvbWUgbWF0aGVtYXRpY2FsIG5vdGF0aW9uLiBMZXQgJFxoYXR7XHBpfV97dCsxfSQgZGVub3RlIHRoZSBmb3JlY2FzdGVkIHByb2JhYmlsaXR5IHRoYXQgdGhlIFNQWSBhdCAkdCsxJCB3aWxsIGdvIHVwLiBUaGUgaGF0ICgkXGhhdHtcY2RvdH0kKSBpbXBsaWVzIHRoYXQgdGhlIHByb2JhYmlsaXR5IGlzIGEgZm9yZWNhc3QgZXN0YWJsaXNoZWQgYXQgdGltZSAkdCQgcmF0aGVyIHRoYW4gJHQrMSQuIERlcGVuZGluZyBvbiB0aGUgaW50ZW5zaXR5IG9mIHNpZ25hbCwgd2UgY3JlYXRlIGEgcG9ydGZvbGlvICB0aGF0IGVpdGhlciBsb25ncyB0aGUgU1BZIG9yIHRoZSBJRUYuIFRvIHB1dCBmb3JtYWxseSwgdGhlIHJldHVybiBvZiB0aGUgc3RyYXRlZ3kgYXQgdGltZSAkdCsxJCBpcyBnaXZlbiBieQogXGJlZ2lue2VxdWF0aW9ufQpyX3tzLHQrMX0gPSAgSV97XGxlZnRbIFxoYXR7XHBpfV97dCsxfSA+IGEgXHJpZ2h0XX0gcl57ZX1fe3QrMX0gKyAgSV97XGxlZnRbIFxoYXR7XHBpfV97dCsxfSBcbGVxIGEgXHJpZ2h0XX0gcl57Yn1fe3QrMX0KXGVuZHtlcXVhdGlvbn0Kd2hlcmUgJHJee2V9X3t0KzF9JCBhbmQgICRyXntifV97dCsxfSQgZGVub3RlIHRoZSByZXR1cm4gb24gU1BZIGFuZCBJRUYgYXQgJHQrMSQsIHJlc3BlY3RpdmVseS4gVGhlIHBhcmFtZXRlciAkYSQgaXMgYSBwcmVkZXRlcm1pbmVkIG1pbmltdW0gbGV2ZWwgb2YgY29uZmlkZW5jZSBmb3IgdGhlICBpbnZlc3RvciB0byBwYXJ0aWNpcGF0ZSBpbiB0aGUgZXF1aXR5IG1hcmtldC4gVGhlIHN0cmF0ZWd5IGltcGxpZXMgdGhhdCB0aGUgaW52ZXN0b3IgZ29lcyBsb25nIGluIHRoZSBlcXVpdHkgYWxvbmUsIGlmIGFuZCBvbmx5IGlmIHRoZSAgY29uZmlkZW5jZSBsZXZlbCB0aGF0IHRoZSBtYXJrZXQgd2lsbCBnbyB1cCB0aGUgbmV4dCBkYXkgaXMgaGlnaCBlbm91Z2guIElmIG5vdCwgdGhlIHN0cmF0ZWd5IGdvZXMgXCQxIGluIHRoZSBJRUYgRVRGIGFsb25lLiAKCiMjIyBCYWNrdGVzdGluZwpBcyBhIGJlbmNobWFyaywgd2UgY29tcGFyZSBvdXIgcmVzdWx0cyB3aXRoIHJlc3BlY3QgdG8gYSA2MC00MCBzdHJhdGVneSB0aGF0IGludmVzdHMgNjBcJSBpbiBTUFkgYW5kIDQwXCUgaW4gSUVGLiBXaGlsZSB0aGUgYmVuY2htYXJrIGlzIHJlLWJhbGFuY2VkIG9uIGEgZGFpbHkgYmFzaXMsIGl0IGRvZXMgbm90IGluY29ycG9yYXRlIGFueSBtYXJrZXQgaW5mb3JtYXRpb24uIEluIG90aGVyIHdvcmRzLCByZWdhcmRsZXNzIG9mIHdoYXQgaGFwcGVucyBpbiB0aGUgbWFya2V0LCB0aGUgYmVuY2htYXJrIHdpbGwgYWx3YXlzIG1haW50YWluIGEgNjAtNDAgYWxsb2NhdGlvbi4gQWRkaXRpb25hbGx5LCB3ZSBjb21wYXJlIHRoZSBzdHJhdGVneSB3aXRoIGVhY2ggRVRGIGFsb25lLiBBcyBhIHBlcmZvcm1hbmNlIHN1bW1hcnksIHdlIGNyZWF0ZSBhIHN1bW1hcnkgZnVuY3Rpb24gdGhhdCB0YWtlcyAkYSQgYXMgdGhlIG1haW4gaW5wdXQgYW5kIHJlcG9ydHMgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBzdHJhdGVneSB3aXRoIHJlc3BlY3QgdG8gZWFjaC4gCmBgYHtyLCB3YXJuaW5nPUZ9Cm5leHRfZiA8LSBmdW5jdGlvbih4KSBjKHhbLTFdLE5BKQpkczMkSUVGX25leHQgPC0gbmV4dF9mKGRzMyRJRUYpCmRzMyRTUFlfbmV4dCA8LSBuZXh0X2YoZHMzJFNQWSkKCmRzMyRCRU5DSE1BUksgPC0gd2l0aChkczMsMC42KlNQWV9uZXh0ICsgMC40KklFRl9uZXh0KQoKcGxvdF9wZXJmb3JtYW5jZSA8LSBmdW5jdGlvbihhKSB7CiAgCiAgIyByb3VuZCB0aGUgcHJvYmFiaWxpdGllcyB0byBuZWFyZXN0IDAuMDUKICBkczMkdXBfcm9sbCA8LSByb3VuZChkczMkdXBfcm9sbC8wLjA1KSowLjA1CiAgZHMzJGRuX3JvbGwgPC0gcm91bmQoZHMzJGRuX3JvbGwvMC4wNSkqMC4wNQoKCiAgZHMzJFBPUlQgPC0gd2l0aChkczMsICh1cF9yb2xsID49IGEpKihTUFlfbmV4dCkgICsgICh1cF9yb2xsIDwgYSkqSUVGX25leHQgKQogIAogIAogICMgbG9hZCBkczMgaW50byBhIGdncGxvdCBmcmllbmRseSBkYXRhCiAgZHNfcGxvdCA8LSBkYXRhLmZyYW1lKERhdGUgPSBkczMkZGF0ZSwgcmV0dXJuID0gY3Vtc3VtKGRzMyRQT1JUKSwgVHlwZSA9ICJTdHJhdGVneSIpCiAgZHNfcGxvdCA8LSByYmluZChkc19wbG90LCAKICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoRGF0ZSA9IGRzMyRkYXRlLCByZXR1cm4gPSBjdW1zdW0oZHMzJEJFTkNITUFSSyksIFR5cGUgPSAiQmVuY2htYXJrIikpCiAgZHNfcGxvdCA8LSByYmluZChkc19wbG90LCAKICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoRGF0ZSA9IGRzMyRkYXRlLCByZXR1cm4gPSBjdW1zdW0oZHMzJFNQWV9uZXh0KSwgVHlwZSA9ICJTUFkiKSkKICBkc19wbG90IDwtIHJiaW5kKGRzX3Bsb3QsIAogICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShEYXRlID0gZHMzJGRhdGUsIHJldHVybiA9IGN1bXN1bShkczMkSUVGX25leHQpLCBUeXBlID0gIklFRiIpKQogIGRzX3Bsb3QgPC0gcmJpbmQoZHNfcGxvdCwgCiAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKERhdGUgPSBkczMkZGF0ZSwgcmV0dXJuID0gZHMzJGRuX3JvbGwsIFR5cGUgPSAiUHJvYmFiaWxpdHkgRG93biIpKQogIAogIGRzX3Bsb3QgPC0gbmEub21pdChkc19wbG90KQogIAogIHAgPC0gZ2dwbG90KGRzX3Bsb3QpICsgZ2VvbV9saW5lKGFlcyh4ID0gRGF0ZSx5ID0gcmV0dXJuLGNvbG91ciA9IFR5cGUpKSAKICBwIDwtIHAgKyBnZW9tX2FibGluZShpbnRlcmNlcHQgPTAsbGluZXR5cGUgPSAiZGFzaGVkIikKICBwIDwtIGdncGxvdGx5KHAsaGVpZ2h0ID0gIDUwMCwgd2lkdGggPSA5MDApCiAgCiAgIyBhbHNvIHJldHVybiB0aGUgZGF0YSBmb3IgcGVyZm9ybWFuY2UgY29tcGFyaXNvbgogIGRzX3BlcmYgPC0gZHMzWyxjKCJJRUZfbmV4dCIsIlNQWV9uZXh0IiwiQkVOQ0hNQVJLIiwiUE9SVCIsIlNQWV9uZXh0IildCiAgcm93bmFtZXMoZHNfcGVyZikgPC0gZHMzJGRhdGUKICBkc19wZXJmIDwtIGFzLnh0cyhkc19wZXJmKQoKICBsaXN0KHBsb3RfcGVyZiA9IHAsZGF0YV9wZXJmID0gZHNfcGVyZikKfQpgYGAKCgojIyMjIElsbHVzdHJhdGlvbgpMZXQncyB0YWtlIGEgbG9vayB0aGUgcGVyZm9ybWFuY2Ugb3ZlciB0aW1lIHdoZW4gJGE9OTBcJSQuIFRoaXMgcmVwcmVzZW50cyBhIHNpdHVhdGlvbiBpbiB3aGljaCB0aGUgaW52ZXN0b3IgYWxsb2NhdGVzIGhpcyB3ZWFsdGggbWFpbmx5IHRvIGVxdWl0eSBpZiB0aGUgY29uZmlkZW5jZSBsZXZlbCB0aGF0IHRoZSBTUFkgaXMgZ29pbmcgdXAgdGhlIGZvbGxvd2luZyBkYXkgaXMgYXQgbGVhc3QgOTBcJS4gT3RoZXJ3aXNlLCB0aGUgaW52ZXN0b3IgaXMgY29uc2VydmF0aXZlIGFuZCBhbGxvY2F0ZXMgaGlzIHdlYWx0aCB0byB0aGUgVHJlYXN1cnkgRVRGIG1haW5seS4gCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBlcmYxIDwtIHBsb3RfcGVyZm9ybWFuY2UoMC45KQpwZXJmMSRwbG90X3BlcmYKYGBgCkluIHRoZSBhYm92ZSBwbG90LCB3ZSBvYnNlcnZlIHRoYXQgdGhlIHByb3Bvc2VkIHN0cmF0ZWd5IGRvbWluYXRlcyB0aGUgb3RoZXIgY2FuZGlkYXRlcyBpbiB0ZXJtcyBvZiBjdW11bGF0aXZlIHJldHVybiBvdmVyIHRoZSBwZXJpb2QsIHdoaWxlIGF0IHRoZSBzYW1lIHRpbWUgdHJhaWxpbmcgdGhlIFNQWS4gTW9yZW92ZXIsIHdlIG9ic2VydmUgdGhhdCB0aGUgc3RyYXRlZ3kgYXZvaWRzIHRoZSBtYXJrZXQgY3Jhc2ggZHVyaW5nIHRoZSAyMDA3LTA5IGZpbmFuY2lhbCBjcmlzaXMsIGluZGljYXRpbmcgdGhhdCBpdCBoYXMgYSBsb3dlciBkb3duc2lkZSByaXNrIHRoYW4gdGhlIGJlbmNobWFyayBhcyB3ZWxsIGFzIHRoZSBTUFkuIEFkZGl0aW9uYWxseSwgZ2l2ZW4gdGhlIG1hcmtldCB0dXJtb2lsIGluIGxhdGUgMjAxOCwgd2Ugb2JzZXJ2ZSB0aGF0IHRoZSB0aGUgcHJvcG9zZWQgc3RyYXRlZ3kgaXMgc3VjY2Vzc2Z1bCBpbiBleGVjdXRpbmcgYSBmbGlnaHQgdG8gcXVhbGl0eSBkdXJpbmcgdGltZXMgb2YgdW5jZXJ0YWludHkuCgoKU3VwcG9zZSB0aGF0IHRoZSBpbnZlc3RvciBpcyBtb3JlIGNvbnNlcnZhdGl2ZSwgc3VjaCBoZSBvbmx5IGludmVzdHMgaW4gdGhlIFNQWSBpZiB0aGUgcHJvYmFiaWxpdHkgaXMgYXQgbGVhc3QgOTVcJS4gSW4gdGhpcyBjYXNlLCB3ZSBoYXZlCmBgYHtyLCBmaWcuYWxpZ249ImNlbnRlciJ9CnBlcmYyIDwtIHBsb3RfcGVyZm9ybWFuY2UoMC45NSkKcGVyZjIkcGxvdF9wZXJmCmBgYApXZSBub3RlIHRoYXQgdGhlIHByb3Bvc2VkIHN0cmF0ZWd5IGF2b2lkcyBib3RoIHRoZSAyMDA3LTA5IG1hcmtldCBjcmFzaCBhbmQgdGhlIHJlY2VudCBtYXJrZXQgZHJvcCBpbiBsYXRlIDIwMTguIEhvd2V2ZXIsIHdlIG9ic2VydmUgdGhhdCB0aGUgc3RyYXRlZ3kgeWllbGRzIGFsbW9zdCB0aGUgc2FtZSBjdW11bGF0aXZlIHJldHVybiBhcyB0aGUgYmVuY2htYXJrLiAKClN1cHBvc2UgdGhhdCB0aGUgaW52ZXN0b3IgaXMgbW9yZSByaXNrIHRvbGVyYW50LCBzdWNoIGhlIGlzIHdpbGxpbmcgdG8gaW52ZXN0IGluIHRoZSBTUFkgd2l0aCBhIGxldmVsIG9mIGNvbmZpZGVuY2Ugb2YgODVcJS4gSW4gdGhpcyBjYXNlLCB3ZSBvYnNlcnZlIHRoYXQgdGhlIHN0cmF0ZWd5IGRvZXMgd2VsbCBpbiB0ZXJtcyBvZiBjdW11bGF0aXZlIHJldHVybi4KYGBge3IsIGZpZy5hbGlnbj0iY2VudGVyIn0KcGVyZjMgPC0gcGxvdF9wZXJmb3JtYW5jZSgwLjg1KQpwZXJmMyRwbG90X3BlcmYKYGBgCkhvd2V2ZXIsIGF0IHRoZSBzYW1lIHRpbWUsIHdlIG9ic2VydmUgdGhhdCB0aGUgc3RyYXRlZ3kgZmFpbHMgdG8gc2hpZnQgdG8gVHJlYXN1cnkgYm9uZHMgZHVyaW5nIHRoZSByZWNlbnQgbWFya2V0IHR1cm1vaWxzLCBkcmFpbmluZyB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIHN0cmF0ZWd5IG92ZXIgdGltZS4gCgoKIyMjIFJpc2stQWRqdXN0ZWQgUmV0dXJucwpJbiB0ZXJtcyBvZiByaXNrIGFkanVzdGVkIHJldHVybnMsIHdlIGNvbnNpZGVyIGEgbnVtYmVyIG9mIHN0YXRpc3RpY3MuIFRoZSBmaXJzdCBpcyB0aGUgSmVuc2VuJ3MgYWxwaGEgdGhhdCBjYXB0dXJlcyB0aGUgYWJub3JtYWwgcmV0dXJuIGFib3ZlIHRoZSBiZW5jaG1hcmsuIFRoZSBzZWNvbmQgaXMgU29ydGlubyByYXRpbywgd2hpY2ggYWRqdXN0cyB0aGUgbWVhbiByZXR1cm4gb2YgdGhlIHN0cmF0ZWd5IHdpdGggcmVzcGVjdCB0byBhIGRvd25zaWRlIHJpc2sgKHN0YW5kYXJkIGRldmlhdGlvbikuIEFkZGl0aW9uYWxseSwgd2UgcmVwb3J0IHRoZSBiZXRhIG9mIGVhY2ggc3RyYXRlZ3kuIAoKYGBge3J9ClAxIDwtIHBlcmYxJGRhdGFfcGVyZiRQT1JUClAyIDwtIHBlcmYyJGRhdGFfcGVyZiRQT1JUClAzIDwtIHBlcmYzJGRhdGFfcGVyZiRQT1JUCkIgPC0gcGVyZjEkZGF0YV9wZXJmJEJFTkNITUFSSwpCMiA8LSBwZXJmMSRkYXRhX3BlcmYkU1BZX25leHQKCnBvcnRfYWxsIDwtIGxpc3QoUDIsUDEsUDMsQixCMikKcG9ydF9hbGwgPC0gbmEub21pdChSZWR1Y2UobWVyZ2UscG9ydF9hbGwpKQpuYW1lcyhwb3J0X2FsbClbMTozXSA8LSBjKCJQb3J0Zm9saW9fOTUiLCJQb3J0Zm9saW9fOTAiLCJQb3J0Zm9saW9fODUiKQoKCnRhYmxlLkNBUE0ocG9ydF9hbGxbLDE6M10scG9ydF9hbGxbLDRdKVtjKDYsMiksXQoKYGBgCgpJbiB0aGUgYWJvdmUgdGFibGUsIHdlIG9ic2VydmUgdGhhdCB0aGUgbW9yZSBjb25zZXJ2YXRpdmUgc3RyYXRlZ2llcyB5aWVsZCBhbiBhbm51YWwgYWxwaGEgb2YgOFwlLCBhdCB0aGUgc2FtZSB0aW1lLCB3aGlsZSBiZWluZyBiZXRhLW5ldXRyYWwgT24gdGhlIG90aGVyIGhhbmQsIHdlIG5vdGUgdGhhdCB3aGVuICRhJCBkZWNyZWFzZXMsIHRoZSBhbHBoYSBkZWNyZWFzZXMsIGFuZCB0aGUgc3RyYXRlZ3kgYmVjb21lcyBsZXNzIG1hcmtldCBiZXRhLW5ldXRyYWwuIAoKSW4gdGVybXMgb2YgZG93bnNpZGUgcmlzaywgd2UgY29tcHV0ZSB0aGUgU29ydGlubydzIHJhdGlvICBmb3IgZWFjaCBzdHJhdGVneS4gU2ltaWxhciB0byB0aGUgU2hhcnBlIHJhdGlvLCB3ZSBlc3RpbWF0ZSB0aGUgU29ydGlubydzIHJhdGlvIGFzIHRoZSByYXRpbyBiZXR3ZWVuIHRoZSBtZWFuIHJldHVybiBhbmQgdGhlIGRvd25zaWRlIHJpc2suIEJ5IGRvd25zaWRlIHJpc2ssIHdlIHJlZmVyIHRvIHRoZSBzZW1pLXN0YW5kYXJkIGRldmlhdGlvbiBpbiB3aGljaCB3ZSBlbGltaW5hdGUgdGhlIHBvc2l0aXZlIHJldHVybnMgaW4gY2FsY3VsYXRpbmcgdGhlIHZvbGF0aWxpdHkuIEZpbmFsbHksIHdlIHNjYWxlIHRoZSByYXRpbyBpbnRvIGFuIGFubnVhbCBiYXNpcyB1c2luZyBhIHNjYWxlIG9mICRcc3FydHsyNTJ9JC4gQXMgYW4gYWRkaXRpb25hbCBwZXJzcGVjdGl2ZSwgd2UgcmVwb3J0IHRoZSBTaGFycGUtcmF0aW8gZm9yIGNvbXBhcmlzb246CmBgYHtyfQpTb3J0X3JhdGlvIDwtIGZ1bmN0aW9uKHgpIHNxcnQoMjUyKSptZWFuKHgpL3NkKHhbeCA8IDBdKQpTUiA8LSAgZnVuY3Rpb24oeCkgc3FydCgyNTIpKm1lYW4oeCkvc2QoeCkKU29ydGlubyA8LSBhcHBseShwb3J0X2FsbCwyLFNvcnRfcmF0aW8pClNoYXJwZSA8LSBhcHBseShwb3J0X2FsbCwyLFNSKQpkYXRhLmZyYW1lKHJiaW5kKFNvcnRpbm8sU2hhcnBlKSkKYGBgCkluIGFsbCBjYXNlcywgZXhjZXB0IGZvciB0aGUgODVcJSBsZXZlbCBjb25maWRlbmNlLCB3ZSBvYnNlcnZlIHRoYXQgdGhlIHByb3Bvc2VkIHN0cmF0ZWd5IHlpZWxkcyBhIGhpZ2hlciByaXNrLWFkanVzdGVkIHJldHVybiB0aGFuIHRoZSBiZW5jaG1hcmsuIFRoaXMgaXMgbW9yZSBldmlkZW50IGZvciB0aGUgU29ydGlubyB0aGFuIHRoZSBTaGFycGUuIEhlbmNlLCB0aGlzIGluZGljYXRlcyB0aGF0IHRoZSBzdHJhdGVneSBvdXRwZXJmb3JtcyB0aGUgYmVuY2htYXJrIGJ5IGJlYXJpbmcgbGVzcyBkb3duc2lkZSByaXNrLCB3aGlsZSwgYXQgdGhlIHNhbWUgdGltZSwgYWNoaWV2aW5nIGEgYmV0dGVyIG9yIGNvbXBhcmFibGUgcmV0dXJuLiAgCgoKIyMgU3VtbWFyeQpUaGlzIHZpZ25ldHRlIHByb3ZpZGVzIGEgc2ltcGxlIGNvc3QtZWZmaWNpZW50IHRyYWRpbmcgc3RyYXRlZ3kgdGhhdCBkZXBsb3lzIG1hY2hpbmUgbGVhcm5pbmcgdXNpbmcgcHVibGljIGRhdGEgYW5kIG9wZW4gc291cmNlIHNvZnR3YXJlLiBXaGlsZSB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIHN0cmF0ZWd5IGRlcGVuZHMgb24gYSBjb3VwbGUgb2Ygc3BlY2lmaWNhdGlvbnMsIHN1Y2ggYXMgdGhlIGxldmVsIG9mIGNvbmZpZGVuY2UgJGEkIG9yIHdoYXQgZGV0ZXJtaW5lcyBhIHByaWNlIGNoYW5nZSBpbiB0aGUgbWFya2V0LCB0aGUgc3RyYXRlZ3kgaXMgbWFpbmx5IGRhdGEtZHJpdmVuLiBPbmUgbWF5IGNvbnNpZGVyIG90aGVyIGFwcHJvYWNoZXMgdXNpbmcgY3Jvc3MtdmFsaWRhdGlvbiB0byBkZXRlcm1pbmUgdGhlc2UgaW5wdXRzIGF1dG9tYXRpY2FsbHkuIE5vbmV0aGVsZXNzLCBhIHNpbWlsYXIgc3RyYXRlZ3kgY2FuIGJlIGFsc28gZGVwbG95ZWQgdG8gc2NyZWVuIGRpZmZlcmVudCBzdG9ja3Mgb3IgRVRGcy4gRm9yIGluc3RhbmNlLCBvbmUgbWF5IGNvbnNpZGVyIGEgc2ltaWxhciBhcHByb2FjaCB0byBwZXJmb3JtIGFzIHRhY3RpY2FsIGFzc2V0IGFsbG9jYXRpb24gYWNyb3NzIHNlY3RvciBFVEZzLiBpLmUuIHNlY3RvciByb3RhdGlvbiBzdHJhdGVneS4gV2UgbGVhdmUgdGhpcyBmb3IgZnV0dXJlIGludmVzdGlnYXRpb24uIAoKIyMgRGlzY2xhaW1lciAKKlRoZSBhYm92ZSB2aWdlbnR0ZSBpcyBhIHNob3J0IGlsbHVzdHJhdGlvbiBvZiBhbiBvbmdvaW5nIHJlc2VhcmNoIGNvLWF1dGhvcmVkIHdpdGggS3JpcyBCb3VkdCAoVnJpamUgVW5pdmVyc2l0ZWl0IEJydXNzZWwsIFVuaXZlcnNpdHkgb2YgQW1zdGVyZGFtIGFuZCBGaW52ZXgpLCBNdXphZmVyIENlbGEgKFZyaWplIFVuaXZlcnNpdGVpdCBCcnVzc2VsKSwgYW5kIE1hamVlZCBTaW1hYW4gKFN0ZXZlbnMgSW5zdGl0dXRlIG9mIFRlY2hub2xvZ3kpLiBUaGUgcmVzZWFyY2ggaXMgdGl0bGVkICJJbiBTZWFyY2ggb2YgUmV0dXJuIFByZWRpY3RhYmlsaXR5OiBFdmlkZW5jZSBmcm9tIE1hY2hpbmUgTGVhcm5pbmcgYW5kIFRhY3RpY2FsIEFsbG9jYXRpb24iLiBGb3IgZnVydGhlciBhcHBsaWNhdGlvbiwgc2VlIHRoZSBpbXBsaWNhdGlvbiBvZiB0aGUgdGhpcyBhbmFseXNpcyBpbnRvIGNvbnN0cnVjdGluZyBtYWNoaW5lIGxlYXJuaW5nIGZyb250aWVycyB2aWEgdGhpcyBbcGFwZXJdKGh0dHBzOi8vcGFwZXJzLnNzcm4uY29tL3NvbDMvcGFwZXJzLmNmbT9hYnN0cmFjdF9pZD0zNTQxMzg3KSouCgoK