Alberto Dorantes Dosamantes, Ph.D. Monterrey Tech, Queretaro Campus
Sep 1st, 2022
2.1 Data management of financial data
2.1.1 Data collection
We will use the quantmod package to import real online data from
Yahoo Finance. This package contains the getSymbols() function, which
creates an xts (extensible time series) object in the environment with
the downloaded data from the Internet. :
library(quantmod)
Loading required package: xts
Loading required package: zoo
Attaching package: ‘zoo’
The following objects are masked from ‘package:base’:
as.Date, as.Date.numeric
Loading required package: TTR
Registered S3 method overwritten by 'quantmod':
method from
as.zoo.data.frame zoo
The getSymbols() function download online and up-to-date financial
data, such as stock prices, ETF prices, interest rates, exchange rates,
etc. getSymbols() allows to download this data from multiple sources:
Yahoo Finance, FRED database and Oanda. These sources have thousands of
finance and economic data series from many market exchanges and other
macroeconomic variables of most of the countries.
You can type ?function in the console or the R Script and run it to
know more about the syntax of any function. This will display the R
documentation of the function in the bottom-right pane:
?getSymbols
Now, we will work with historical data of the Bitcoin cryptocurrency
and the TESLA stock. We download daily quotations of these instruments
from January 1, 2019 to date from Yahoo Finance:
getSymbols(c("BTC-USD","TSLA"), from="2019-01-01", src="yahoo", periodicity="daily")
Warning: BTC-USD contains missing values. Some functions will not work if objects contain missing values in the middle of the series. Consider using na.omit(), na.approx(), na.fill(), etc to remove or replace them.
[1] "BTC-USD" "TSLA"
This function will create an xts-zoo R object for each ticker. Each
object has the corresponding historical daily prices. xts stands for
extensible time-series. An xts-zoo object is designed to easily
manipulate time series data.
BTC-USD and TSLA are the ticker names in Yahoo Finance. The from
argument is used to indicate the initial date from which you want to
bring data. The to argument is the end date of the series you want to
download. In this case we omit the to argument in order to download the
most recent data. The src argument indicates the source of the data, in
this case it is Yahoo Finance. Finally, the periodicity argument
specifies the granularity of the data (daily, weekly, monthly,
quarterly) also as a character vector.
You can check the content of any of these dataset with View(). When
tickers have special characters, we have to make reference to the object
with simple quotes (``):
You can list the FIRST 5 rows of a dataset by using head():
head(`BTC-USD`,5)
BTC-USD.Open BTC-USD.High BTC-USD.Low BTC-USD.Close
2019-01-01 3746.713 3850.914 3707.231 3843.520
2019-01-02 3849.216 3947.981 3817.409 3943.409
2019-01-03 3931.049 3935.685 3826.223 3836.741
2019-01-04 3832.040 3865.935 3783.854 3857.718
2019-01-05 3851.974 3904.903 3836.900 3845.195
BTC-USD.Volume BTC-USD.Adjusted
2019-01-01 4324200990 3843.520
2019-01-02 5244856836 3943.409
2019-01-03 4530215219 3836.741
2019-01-04 4847965467 3857.718
2019-01-05 5137609824 3845.195
Also, you can list the LAST 5 rows of a data set. Note that you can
change number of rows you want to display.
tail(`BTC-USD`, 5)
BTC-USD.Open BTC-USD.High BTC-USD.Low BTC-USD.Close
2022-09-01 20050.50 20198.39 19653.97 20127.14
2022-09-02 20126.07 20401.57 19814.77 19969.77
2022-09-03 19969.72 20037.01 19698.36 19832.09
2022-09-04 NA NA NA NA
2022-09-05 20027.30 20027.30 19840.12 19853.44
BTC-USD.Volume BTC-USD.Adjusted
2022-09-01 30182031010 20127.14
2022-09-02 29123998928 19969.77
2022-09-03 23613051457 19832.09
2022-09-04 NA NA
2022-09-05 26419550208 19853.44
For each period, Yahoo Finance keeps track of the open, high, low,
close (OHLC) and adjusted prices. Also, it keeps track of volume that
was traded in every specific period. The adjusted prices are used for
stocks, not for currencies. Adjusted prices considers dividend payments
and also stock splits. Then, for the Bitcoin series we can use close of
adjusted price to calculate daily returns.
Let’s see some of the benefits of using xts-zoo objects. We can, for
example, select columns using any of the following functions, where x
represents a generic xts zoo object:
Op(x): Extract the Opening prices of the period. Hi(x): Extract the
Highest price of the period. Lo(x): Extract the Lowest price of the
period. Cl(x): Extract the closing prices of the period. Vo(x): Extract
the volume traded of the period. Ad(x): Extract the Adjusted prices of
the period.
2.1.2 Merging and cleaning financial datasets:
We can use the merge() function to create a consolidated dataset of
one or more xts-zoo objects:
prices <- merge(`BTC-USD`, TSLA)
# I can select only the adjusted prices in order to calculate returns:
adjprices <-Ad(prices)
Now we have an xts-zoo objects with 2 columns. I can change the names
of the columns with simple names:
names(adjprices)<-c("bitcoin","tesla")
Now I can make reference to the adjusted prices using these
names.
In Finance, when managing daily data it is very common to have gaps
in the series. What does this mean? It means that the contains some
missing days. For example, for stock series there is no data for
weekends or holidays. However, R deals with gaps because it recognizes
that we are working with a time series object. Thus, we have a time
variable with NO GAPS, which avoids problems when computing returns.
However, R does not deal automatically with empty values (called NA’s).
It is a good idea to have a data set free of NA’s. So, I can use the
function na.omit:
adjprices <- na.omit(adjprices)
2.2 Return calculation
We can create xts-zoo objects for simple and continuously compounded
returns of both instruments:
# Calculating continuously compounded daily returns:
ccreturns <- diff(log(adjprices))
# Calculating simple daily returns:
returns <- adjprices / lag(adjprices,n=1) - 1
# We can also calculate simple returns using the diff function:
returns2<- diff(adjprices) / lag(adjprices,n=1)
The diff function works with xts to calculate the difference between
the value of one period minus the previous one. The lag function gets
the previous value of the time series.
We can calculate holding returns for any time period for each
instrument (HPR). For example, if we want to calculate the whole period
return from the first day of the series to the most recent one, we can
do the following:
HPR1<- 100* as.vector(last(adjprices)) / as.vector(first(adjprices)) - 1
HPR1
[1] 505.4088 1305.9618
cat("The holding period return for Bitcoin is: ", HPR1[1], "%")
The holding period return for Bitcoin is: 505.4088 %
cat("The holding period return for Bitcoin is: ", HPR1[2], "%")
The holding period return for Bitcoin is: 1305.962 %
We could also use the continuously compounded returns to calculate
the same holding simple returns:
HPR2<-100*exp(colSums(ccreturns,na.rm=TRUE)) - 1
HPR2
bitcoin tesla
505.4088 1305.9618
2.3 Visualization of financial data
One of the advantages of the quantmod package is that it has some
built-in data visualization capabilities. For example, the chartSeries
function is a plotting tool designed to create standard financial charts
given a time series object. The chartSeries function also includes some
arguments that allows the user to modify the cosmetics of the plot such
as the theme argument.
Let’s see the performance of Bitcoin prices:
chartSeries(`BTC-USD`, theme = ("white"))

Let’s see the performance of TESLA prices:
chartSeries(TSLA, theme = ("black"))

We can see exponential growth of both, the Bitcoin prices and Tesla
prices for the last days. If you had invested in Bitcoin or Tesla in
July 2020 and has sold your position early January 2021, you would have
multiplied your investment by around 4 times (300% period return)!
We can visualize the daily returns over time:
plot(returns$bitcoin)

plot(returns$tesla)

With this plot we can appreciate the daily volatility of each
instrument over time. Volatility is a measure of how disperse the
returns move up and down from its mean; it is basically the standard
deviation of returns.
2.4 Basics of Portfolio analysis
We will use the PerformanceAnalytics and related packages for this
section:
# Load the packages:
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ─────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.3.6 ✔ purrr 0.3.4
✔ tibble 3.1.8 ✔ dplyr 1.0.10
✔ tidyr 1.2.0 ✔ stringr 1.4.1
✔ readr 2.1.2 ✔ forcats 0.5.2 ── Conflicts ────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::first() masks xts::first()
✖ dplyr::lag() masks stats::lag()
✖ dplyr::last() masks xts::last()
library(PerformanceAnalytics)
Attaching package: ‘PerformanceAnalytics’
The following object is masked _by_ ‘.GlobalEnv’:
prices
The following object is masked from ‘package:graphics’:
legend
library(highcharter)
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Highcharts (www.highcharts.com) is a Highsoft software product which is
not free for commercial and Governmental use
We will do an exercise of a simple portfolio composed by 5 US stocks:
Tesla, Microsoft, Nextera, WalMart and Pfizer. You can change the
tickers of the stocks as you wish as long as Yahoo Finance has data for
your tickers. I selected stocks from the industries: high-tech, clean
energies, pharmaceutical, and retail.
2.4.1 Automation of data collection and data management
With the following code we will automatically download data from the
5 stocks, merge the data and calculate returns:
symbols <- c("TSLA","MSFT","NEE","WMT","PFE")
prices <- getSymbols(symbols,src = 'yahoo',
periodicity = "monthly",
from = "2018-01-01",
auto.assign = TRUE,
warnings = FALSE) %>%
map(~Ad(get(.))) %>%
reduce(merge) %>%
`colnames<-`(symbols)
If you see I am doing several data management process in sequential
process. The %>% is used to indicate the separation of each data
management process. The previous code does the following:
Download price data of the 5 stocks
Apply (map) the adjusted function to all xts-zoo datasets in order to
get the adjusted stock prices
Do the merge of all the xts-zoo objects into one object
Finally rename the columns of the integrated object, and return the
object as prices.
2.4.2 Return calculations
We calculate returns of all stocks using the Return.calculate
function:
Returns<-Return.calculate(prices) %>%
na.omit()
The na.omit function was also applied to clean the dataset and drop
the rows with NA values.
We calculate the continuous compounded return (also called log
returns) of all stocks:
returns<-Return.calculate(prices,method = "log") %>%
na.omit()
# We could calculate this using the diff and log functions:
returns2<-na.omit(diff(log(prices)))
# The returns and returns2 objects will have exactly the same returns
2.4.3 Descriptive statistics of returns
We calculate descriptive statistics for the returns of all
stocks:
table.Stats(returns)
We can see arithmetic, geometric mean, median of returns. Also we can
see risk measures such as standard deviation, variance. Finally we can
see skewness and kurtosis, which are important financial measures to
understand the data.
2.4.4 Visualization of risk and return of stocks
We can do a Box plot of the returns to better appreciate historical
median return and risk by looking at the median, the quartiles Q1 and
Q3, volatility and extreme values of these returns:
chart.Boxplot(returns)

It is easy to see that Tesla is the stock with the highest risk. The
red circles show the mean, the mid line is the median (50 percentile),
the boxes include the 50% of the data from the Q1 (25 percentile) to the
Q3 (75 percentile). The dots are considered extreme values for in the
context of its own distribution.
Now we can start evaluating the performance over time for each stock
by looking at how much $ we would have made if we had invested $1.00 in
each stock at the beginning of the time periods:
charts.PerformanceSummary(Returns,
main = "Performance of $1.00 over time",
wealth.index = TRUE)

Since Tesla has had an extraordinary performance for the last months,
it is hard to appreciate the performance of the rest of the stocks. So,
we can drop Tesla from the plot:
charts.PerformanceSummary(Returns[,2:5],
main = "Performance of $1.00 over time",
wealth.index = TRUE)

2.6 Portfolio return calculation
We calculate the historical return of the equally-weighted portfolio
using the Return.portfolio function:
portfolio_returns_ew <-
Return.portfolio(Returns,
weights = w_ew) %>%
`colnames<-`("returns")
We calculate the historical return of the aggressive portfolio using
the Return.portfolio function:
portfolio_returns_ag <-
Return.portfolio(Returns,
weights = w_aggressive) %>%
`colnames<-`("returns")
With the portfolio returns I can plot a performance chart of each
portfolio:
charts.PerformanceSummary(portfolio_returns_ew,
main = "Equally-weighted Portfolio")

charts.PerformanceSummary(portfolio_returns_ag,
main = "Aggressive Portfolio Performance")

We finally do a dynamic plot of historical monthly returns of each
portfolio using the highchart function from the highcharter package:
highchart(type = "stock") %>%
hc_title(text = "Equally-weighted Portfolio") %>%
hc_add_series(portfolio_returns_ew$returns,
name = "Monthly Returns",
color = "cornflowerblue") %>%
hc_add_theme(hc_theme_flat()) %>%
hc_navigator(enabled = FALSE) %>%
hc_scrollbar(enabled = FALSE) %>%
hc_legend(enabled = TRUE) %>%
hc_exporting(enabled = TRUE)
We can interact with the plot to zoom in and out, and selecting time
periods.
We do the same for the aggressive portfolio:
highchart(type = "stock") %>%
hc_title(text = "Aggressive Portfolio") %>%
hc_add_series(portfolio_returns_ag$returns,
name = "Monthly Returns",
color = "cornflowerblue") %>%
hc_add_theme(hc_theme_flat()) %>%
hc_navigator(enabled = FALSE) %>%
hc_scrollbar(enabled = FALSE) %>%
hc_legend(enabled = TRUE) %>%
hc_exporting(enabled = TRUE)
3 Introduction to Portfolio Theory
Regardless the approach you use to download and manipulate the source
data, once that your data frame of monthly returns is ready for analysis
you will be in a good position to create the Variance-covariance matrix
which is needed to compute the expected risk of the portfolio.
The first step is to transform the data frame into a matrix using the
as.matrix function. We save the result in a matrix class object called
ret.mat.
ret.mat <- as.matrix(Returns)
We compute the Var-Covariance matrix. The var function receives a
matrix of returns as parameter. The returns of stock 1 are in column 1,
returns of stock 2 are in column 2, and so on. This function calculates
the Variance-Covariance Matrix of stock returns.
Remember that the Variance-Covariance matrix contains Variances of
stock returns in the diagonal, and it contains the covariances of pairs
of stock returns in the non-diagonals. Also, this matrix is symetric,
since Cov(Reti, Retj) = Cov(Retj,Reti). More formally:
For example, the variance-covariance matrix of 2 variables X1 and X2
contains the variance of each variable in its diagonal and covariances
between the variables in its non-diagonal terms. In other words, the
elements in the upright part of the matrix are repeated in the down-left
part of the matrix. More formally:
∑cov=[Var(X1)Cov(X2,X1)Cov(X1,X2)Var(X2)]
COV <- var(ret.mat)
We can see the results of the Var-Covar matrix as shown below:
COV
TSLA MSFT NEE WMT
TSLA 0.044324810 0.007089377 0.0036288008 0.0043913507
MSFT 0.007089377 0.003480521 0.0012656788 0.0012728561
NEE 0.003628801 0.001265679 0.0036179444 0.0008644815
WMT 0.004391351 0.001272856 0.0008644815 0.0031130716
PFE 0.003429573 0.001350195 0.0018184272 0.0011935499
PFE
TSLA 0.003429573
MSFT 0.001350195
NEE 0.001818427
WMT 0.001193550
PFE 0.005696879
We can also calculate the Correlation Matrix to better understand the
relationships of all different pairs of stock returns.
cor(ret.mat)
TSLA MSFT NEE WMT PFE
TSLA 1.0000000 0.5707718 0.2865555 0.3738351 0.2158232
MSFT 0.5707718 1.0000000 0.3566731 0.3866897 0.3032187
NEE 0.2865555 0.3566731 1.0000000 0.2575907 0.4005405
WMT 0.3738351 0.3866897 0.2575907 1.0000000 0.2834181
PFE 0.2158232 0.3032187 0.4005405 0.2834181 1.0000000
Remember that the correlation between 2 stock returns can be any
value between -1 and 1. When the correlation is close to 0, it means
that both stocks have no relationship. If the correlation is close to 1
it means that both stock returns move in a very similar way in the same
direction.
We calculate the expected simple returns of the stocks in another
matrix using the apply function. It is important to double check that
the second parameter is equal to 2. This means that we are applying the
function mean by column. If we want to apply the mean function by raw,
we have to specify the second parameter as 1.
ER<- exp(apply(ret.mat, 2, mean)) - 1
This is the simple way to calculate the expected return of each stock
according to Markowitz theory. Remember that the expected return of one
stock is simply the Geometric average return of its historical
returns.
To calculate this expected return we have 2 methods:
Calculate the products of Gross Returns of each period, substract 1,
and then apply the N root to get the geometric average.
Get the arithmetic average of continuously compounded returns and
then convert this amount to simple returns by raising e to this average
and then substract 1.
We have estimated the geometric average using the method 2, but not
only for one stock, but for all stocks in the the matrix ret.mat. The
result will be a vector of geometric returns that will be saved in er.
We can estimate the vector Mr of expected returns as:
MR=exp[Mr]=exp⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢r1¯r2¯…rN¯⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥−1
We can see the expected simple returns by calling the er object.
ER
TSLA MSFT NEE WMT PFE
0.064287042 0.020448711 0.017360339 0.007022978 0.010445553
We calculate the variance, standard deviation, skewness and kurtosis
of all asset returns (by columns). We do this just to see descriptive
statistics of the stock returns.
apply(ret.mat, 2, var)
TSLA MSFT NEE WMT PFE
0.044324810 0.003480521 0.003617944 0.003113072 0.005696879
apply(ret.mat, 2, sd)
TSLA MSFT NEE WMT PFE
0.21053458 0.05899594 0.06014935 0.05579491 0.07547767
apply(ret.mat, 2, skewness)
TSLA MSFT NEE WMT PFE
1.0645506 0.1130058 -0.6246087 -0.6591064 0.5710369
apply(ret.mat, 2, kurtosis)
TSLA MSFT NEE WMT PFE
0.8277288 -0.2992434 1.5910755 0.8327515 0.2933593
Remember what is skewness and kurtosis. Skewness is a measure of the
shape of the probability distribution of the returns while kurtosis
gives us information about how much the stock has had extreme negative
and positive returns. In other words, kurtosis helps us to identify how
big are the fat-tails of the return distribution. We can complement this
with the histogram of returns to better appreciate the level of risk of
the stock.
4 Calculation of expected variance and risk of a Portfolio
Assume that you invest in a portfolio with the following weights:
Apple 30%, Walmart 10%, Microsoft 30%, GE 10% and Tesla 20%. Create a
vector with these 5 weights:
W <- c(0.30,0.10,0.30,0.10,0.20)
According to Portfolio Theory, the expected risk of a portfolio P is
calculated as the squared root of the Portfolio Variance.
SD(P)=(Var(P))−−−−−−−−√
The portfolio variance can be calculated with the following matrix
multiplication:
Var(P)=W′∗∑cov∗W where:
W is the weight vector of the stocks, and W′ is the tranposed matrix
of weights.
We now can estimate the expected return and expected risk of this
portfolio.
Now I am ready to start estimating the expected return and risk of
the portfolio using Matrix Algebra:
ERP1 <- t(W)%*%ER
ERP1
[,1]
[1,] 0.02933049
The vector ERP1 will have one value with the expected return of the
portfolio. The t function is the transpose function of a vector or
matrix. The result of multiplying the vector transposed times the vector
of expected returns of the stocks is the same as calculating a weighted
average.
Now I can estimate the expected risk of the portfolio using Matrix
Albegra:
EVARPORT <- t(W)%*%COV%*%W
ERISK <- sqrt(EVARPORT)
ERISK
[,1]
[1,] 0.08267686
With this matrix multiplication I am applying Markowitz theory to
estimate the expected variance of the portfolio. You can review why this
is the case in the Notes about Portfolio Theory. Make sure you
understand why we do this matrix multiplication.
LS0tCnRpdGxlOiAiRmluYW5jZSBQcm9ncmFtbWluZyAtIFdvcmtzaG9wIDQiCmF1dGhvcjogU3RlZmFuIFNjaHdlaXR6ZXIgQTAxMjA5NzU1Cm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCkFsYmVydG8gRG9yYW50ZXMgRG9zYW1hbnRlcywgUGguRC4KTW9udGVycmV5IFRlY2gsIFF1ZXJldGFybyBDYW1wdXMKU2VwIDFzdCwgMjAyMgoKIyBBYnN0cmFjdApJbiB0aGlzIHdvcmtzaG9wIHdlIHdpbGwgdXNlIHJlYWwgZmluYW5jaWFsIHRpbWUgc2VyaWVzIGRhdGEgdG8gbGVhcm4gYWJvdXQgYSkgZGF0YSBtYW5hZ2VtZW50IHN1Y2ggYXMgcmV0dXJuIGNhbGN1bGF0aW9uIGFuZCBkYXRhc2V0IG1lcmdpbmcsIGIpIGRhdGEgdmlzdWFsaXphdGlvbiwgYykgYW5kIGFsc28gd2Ugd2lsbCBzdGFydCBsZWFybmluZyBob3cgdG8gZXN0aW1hdGUgZXhwZWN0ZWQgc3RvY2sgcmV0dXJuIGFuZCBleHBlY3RlZCBwb3J0Zm9saW8gcmV0dXJuIGFuZCByaXNrIHVzaW5nIG1hdHJpeCBhbGdlYnJhLgoKIyBEYXRhIG1hbmFnZW1lbnQgYW5kIHZpc3VhbGl6YXRpb24gZm9yIGZpbmFuY2lhbCBwb3J0Zm9saW9zCkluIHRoaXMgc2VjdGlvbiBJIHdpbGwgd29yayBvbiB0aGUgZm9sbG93aW5nOiBhKSBEYXRhIG1hbmFnZW1lbnQgb2YgZmluYW5jaWFsIGRhdGEsIGIpIHJldHVybiBjYWxjdWxhdGlvbnMsIGMpIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgZm9yIGZpbmFuY2UsIGQpIHZpc3VhbGl6YXRpb24gb2YgZmluYW5jaWFsIGRhdGEsIGFuZCBlKSBQb3J0Zm9saW8gZm9ybWF0aW9uIGFuZCBiYXNpYyBlc3RpbWF0aW9ucy4KCiMgMi4xIERhdGEgbWFuYWdlbWVudCBvZiBmaW5hbmNpYWwgZGF0YQojIyAyLjEuMSBEYXRhIGNvbGxlY3Rpb24KV2Ugd2lsbCB1c2UgdGhlIHF1YW50bW9kIHBhY2thZ2UgdG8gaW1wb3J0IHJlYWwgb25saW5lIGRhdGEgZnJvbSBZYWhvbyBGaW5hbmNlLiBUaGlzIHBhY2thZ2UgY29udGFpbnMgdGhlIGdldFN5bWJvbHMoKSBmdW5jdGlvbiwgd2hpY2ggY3JlYXRlcyBhbiB4dHMgKGV4dGVuc2libGUgdGltZSBzZXJpZXMpIG9iamVjdCBpbiB0aGUgZW52aXJvbm1lbnQgd2l0aCB0aGUgZG93bmxvYWRlZCBkYXRhIGZyb20gdGhlIEludGVybmV0LiA6CgpgYGB7cn0KbGlicmFyeShxdWFudG1vZCkKYGBgCgpUaGUgZ2V0U3ltYm9scygpIGZ1bmN0aW9uIGRvd25sb2FkIG9ubGluZSBhbmQgdXAtdG8tZGF0ZSBmaW5hbmNpYWwgZGF0YSwgc3VjaCBhcyBzdG9jayBwcmljZXMsIEVURiBwcmljZXMsIGludGVyZXN0IHJhdGVzLCBleGNoYW5nZSByYXRlcywgZXRjLiBnZXRTeW1ib2xzKCkgYWxsb3dzIHRvIGRvd25sb2FkIHRoaXMgZGF0YSBmcm9tIG11bHRpcGxlIHNvdXJjZXM6IFlhaG9vIEZpbmFuY2UsIEZSRUQgZGF0YWJhc2UgYW5kIE9hbmRhLiBUaGVzZSBzb3VyY2VzIGhhdmUgdGhvdXNhbmRzIG9mIGZpbmFuY2UgYW5kIGVjb25vbWljIGRhdGEgc2VyaWVzIGZyb20gbWFueSBtYXJrZXQgZXhjaGFuZ2VzIGFuZCBvdGhlciBtYWNyb2Vjb25vbWljIHZhcmlhYmxlcyBvZiBtb3N0IG9mIHRoZSBjb3VudHJpZXMuCgpZb3UgY2FuIHR5cGUgP2Z1bmN0aW9uIGluIHRoZSBjb25zb2xlIG9yIHRoZSBSIFNjcmlwdCBhbmQgcnVuIGl0IHRvIGtub3cgbW9yZSBhYm91dCB0aGUgc3ludGF4IG9mIGFueSBmdW5jdGlvbi4gVGhpcyB3aWxsIGRpc3BsYXkgdGhlIFIgZG9jdW1lbnRhdGlvbiBvZiB0aGUgZnVuY3Rpb24gaW4gdGhlIGJvdHRvbS1yaWdodCBwYW5lOgoKYGBge3J9Cj9nZXRTeW1ib2xzCmBgYAoKTm93LCB3ZSB3aWxsIHdvcmsgd2l0aCBoaXN0b3JpY2FsIGRhdGEgb2YgdGhlIEJpdGNvaW4gY3J5cHRvY3VycmVuY3kgYW5kIHRoZSBURVNMQSBzdG9jay4gV2UgZG93bmxvYWQgZGFpbHkgcXVvdGF0aW9ucyBvZiB0aGVzZSBpbnN0cnVtZW50cyBmcm9tIEphbnVhcnkgMSwgMjAxOSB0byBkYXRlIGZyb20gWWFob28gRmluYW5jZToKCmBgYHtyfQpnZXRTeW1ib2xzKGMoIkJUQy1VU0QiLCJUU0xBIiksIGZyb209IjIwMTktMDEtMDEiLCBzcmM9InlhaG9vIiwgcGVyaW9kaWNpdHk9ImRhaWx5IikKYGBgCgpUaGlzIGZ1bmN0aW9uIHdpbGwgY3JlYXRlIGFuIHh0cy16b28gUiBvYmplY3QgZm9yIGVhY2ggdGlja2VyLiBFYWNoIG9iamVjdCBoYXMgdGhlIGNvcnJlc3BvbmRpbmcgaGlzdG9yaWNhbCBkYWlseSBwcmljZXMuIHh0cyBzdGFuZHMgZm9yIGV4dGVuc2libGUgdGltZS1zZXJpZXMuIEFuIHh0cy16b28gb2JqZWN0IGlzIGRlc2lnbmVkIHRvIGVhc2lseSBtYW5pcHVsYXRlIHRpbWUgc2VyaWVzIGRhdGEuCgpCVEMtVVNEIGFuZCBUU0xBIGFyZSB0aGUgdGlja2VyIG5hbWVzIGluIFlhaG9vIEZpbmFuY2UuIFRoZSBmcm9tIGFyZ3VtZW50IGlzIHVzZWQgdG8gaW5kaWNhdGUgdGhlIGluaXRpYWwgZGF0ZSBmcm9tIHdoaWNoIHlvdSB3YW50IHRvIGJyaW5nIGRhdGEuIFRoZSB0byBhcmd1bWVudCBpcyB0aGUgZW5kIGRhdGUgb2YgdGhlIHNlcmllcyB5b3Ugd2FudCB0byBkb3dubG9hZC4gSW4gdGhpcyBjYXNlIHdlIG9taXQgdGhlIHRvIGFyZ3VtZW50IGluIG9yZGVyIHRvIGRvd25sb2FkIHRoZSBtb3N0IHJlY2VudCBkYXRhLiBUaGUgc3JjIGFyZ3VtZW50IGluZGljYXRlcyB0aGUgc291cmNlIG9mIHRoZSBkYXRhLCBpbiB0aGlzIGNhc2UgaXQgaXMgWWFob28gRmluYW5jZS4gRmluYWxseSwgdGhlIHBlcmlvZGljaXR5IGFyZ3VtZW50IHNwZWNpZmllcyB0aGUgZ3JhbnVsYXJpdHkgb2YgdGhlIGRhdGEgKGRhaWx5LCB3ZWVrbHksIG1vbnRobHksIHF1YXJ0ZXJseSkgYWxzbyBhcyBhIGNoYXJhY3RlciB2ZWN0b3IuCgpZb3UgY2FuIGNoZWNrIHRoZSBjb250ZW50IG9mIGFueSBvZiB0aGVzZSBkYXRhc2V0IHdpdGggVmlldygpLiBXaGVuIHRpY2tlcnMgaGF2ZSBzcGVjaWFsIGNoYXJhY3RlcnMsIHdlIGhhdmUgdG8gbWFrZSByZWZlcmVuY2UgdG8gdGhlIG9iamVjdCB3aXRoIHNpbXBsZSBxdW90ZXMgKGBgKToKCllvdSBjYW4gbGlzdCB0aGUgRklSU1QgNSByb3dzIG9mIGEgZGF0YXNldCBieSB1c2luZyBoZWFkKCk6CgpgYGB7cn0KaGVhZChgQlRDLVVTRGAsNSkKYGBgCkFsc28sIHlvdSBjYW4gbGlzdCB0aGUgTEFTVCA1IHJvd3Mgb2YgYSBkYXRhIHNldC4gTm90ZSB0aGF0IHlvdSBjYW4gY2hhbmdlIG51bWJlciBvZiByb3dzIHlvdSB3YW50IHRvIGRpc3BsYXkuCgpgYGB7cn0KdGFpbChgQlRDLVVTRGAsIDUpCmBgYAoKRm9yIGVhY2ggcGVyaW9kLCBZYWhvbyBGaW5hbmNlIGtlZXBzIHRyYWNrIG9mIHRoZSBvcGVuLCBoaWdoLCBsb3csIGNsb3NlIChPSExDKSBhbmQgYWRqdXN0ZWQgcHJpY2VzLiBBbHNvLCBpdCBrZWVwcyB0cmFjayBvZiB2b2x1bWUgdGhhdCB3YXMgdHJhZGVkIGluIGV2ZXJ5IHNwZWNpZmljIHBlcmlvZC4gVGhlIGFkanVzdGVkIHByaWNlcyBhcmUgdXNlZCBmb3Igc3RvY2tzLCBub3QgZm9yIGN1cnJlbmNpZXMuIEFkanVzdGVkIHByaWNlcyBjb25zaWRlcnMgZGl2aWRlbmQgcGF5bWVudHMgYW5kIGFsc28gc3RvY2sgc3BsaXRzLiBUaGVuLCBmb3IgdGhlIEJpdGNvaW4gc2VyaWVzIHdlIGNhbiB1c2UgY2xvc2Ugb2YgYWRqdXN0ZWQgcHJpY2UgdG8gY2FsY3VsYXRlIGRhaWx5IHJldHVybnMuCgpMZXTigJlzIHNlZSBzb21lIG9mIHRoZSBiZW5lZml0cyBvZiB1c2luZyB4dHMtem9vIG9iamVjdHMuIFdlIGNhbiwgZm9yIGV4YW1wbGUsIHNlbGVjdCBjb2x1bW5zIHVzaW5nIGFueSBvZiB0aGUgZm9sbG93aW5nIGZ1bmN0aW9ucywgd2hlcmUgeCByZXByZXNlbnRzIGEgZ2VuZXJpYyB4dHMgem9vIG9iamVjdDoKCk9wKHgpOiBFeHRyYWN0IHRoZSBPcGVuaW5nIHByaWNlcyBvZiB0aGUgcGVyaW9kLgpIaSh4KTogRXh0cmFjdCB0aGUgSGlnaGVzdCBwcmljZSBvZiB0aGUgcGVyaW9kLgpMbyh4KTogRXh0cmFjdCB0aGUgTG93ZXN0IHByaWNlIG9mIHRoZSBwZXJpb2QuCkNsKHgpOiBFeHRyYWN0IHRoZSBjbG9zaW5nIHByaWNlcyBvZiB0aGUgcGVyaW9kLgpWbyh4KTogRXh0cmFjdCB0aGUgdm9sdW1lIHRyYWRlZCBvZiB0aGUgcGVyaW9kLgpBZCh4KTogRXh0cmFjdCB0aGUgQWRqdXN0ZWQgcHJpY2VzIG9mIHRoZSBwZXJpb2QuCgoKIyMgMi4xLjIgTWVyZ2luZyBhbmQgY2xlYW5pbmcgZmluYW5jaWFsIGRhdGFzZXRzOgpXZSBjYW4gdXNlIHRoZSBtZXJnZSgpIGZ1bmN0aW9uIHRvIGNyZWF0ZSBhIGNvbnNvbGlkYXRlZCBkYXRhc2V0IG9mIG9uZSBvciBtb3JlIHh0cy16b28gb2JqZWN0czoKCmBgYHtyfQpwcmljZXMgPC0gbWVyZ2UoYEJUQy1VU0RgLCBUU0xBKQojIEkgY2FuIHNlbGVjdCBvbmx5IHRoZSBhZGp1c3RlZCBwcmljZXMgaW4gb3JkZXIgdG8gY2FsY3VsYXRlIHJldHVybnM6CmFkanByaWNlcyA8LUFkKHByaWNlcykgCmBgYAoKTm93IHdlIGhhdmUgYW4geHRzLXpvbyBvYmplY3RzIHdpdGggMiBjb2x1bW5zLiBJIGNhbiBjaGFuZ2UgdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zIHdpdGggc2ltcGxlIG5hbWVzOgoKYGBge3J9Cm5hbWVzKGFkanByaWNlcyk8LWMoImJpdGNvaW4iLCJ0ZXNsYSIpCmBgYAoKTm93IEkgY2FuIG1ha2UgcmVmZXJlbmNlIHRvIHRoZSBhZGp1c3RlZCBwcmljZXMgdXNpbmcgdGhlc2UgbmFtZXMuCgpJbiBGaW5hbmNlLCB3aGVuIG1hbmFnaW5nIGRhaWx5IGRhdGEgaXQgaXMgdmVyeSBjb21tb24gdG8gaGF2ZSBnYXBzIGluIHRoZSBzZXJpZXMuIFdoYXQgZG9lcyB0aGlzIG1lYW4/IEl0IG1lYW5zIHRoYXQgdGhlIGNvbnRhaW5zIHNvbWUgbWlzc2luZyBkYXlzLiBGb3IgZXhhbXBsZSwgZm9yIHN0b2NrIHNlcmllcyB0aGVyZSBpcyBubyBkYXRhIGZvciB3ZWVrZW5kcyBvciBob2xpZGF5cy4gSG93ZXZlciwgUiBkZWFscyB3aXRoIGdhcHMgYmVjYXVzZSBpdCByZWNvZ25pemVzIHRoYXQgd2UgYXJlIHdvcmtpbmcgd2l0aCBhIHRpbWUgc2VyaWVzIG9iamVjdC4gVGh1cywgd2UgaGF2ZSBhIHRpbWUgdmFyaWFibGUgd2l0aCBOTyBHQVBTLCB3aGljaCBhdm9pZHMgcHJvYmxlbXMgd2hlbiBjb21wdXRpbmcgcmV0dXJucy4gSG93ZXZlciwgUiBkb2VzIG5vdCBkZWFsIGF1dG9tYXRpY2FsbHkgd2l0aCBlbXB0eSB2YWx1ZXMgKGNhbGxlZCBOQeKAmXMpLiBJdCBpcyBhIGdvb2QgaWRlYSB0byBoYXZlIGEgZGF0YSBzZXQgZnJlZSBvZiBOQeKAmXMuIFNvLCBJIGNhbiB1c2UgdGhlIGZ1bmN0aW9uIG5hLm9taXQ6CgpgYGB7cn0KYWRqcHJpY2VzIDwtIG5hLm9taXQoYWRqcHJpY2VzKQpgYGAKCiMjIDIuMiBSZXR1cm4gY2FsY3VsYXRpb24KCldlIGNhbiBjcmVhdGUgeHRzLXpvbyBvYmplY3RzIGZvciBzaW1wbGUgYW5kIGNvbnRpbnVvdXNseSBjb21wb3VuZGVkIHJldHVybnMgb2YgYm90aCBpbnN0cnVtZW50czoKCmBgYHtyfQojIENhbGN1bGF0aW5nIGNvbnRpbnVvdXNseSBjb21wb3VuZGVkIGRhaWx5IHJldHVybnM6CmNjcmV0dXJucyA8LSBkaWZmKGxvZyhhZGpwcmljZXMpKSAKIyBDYWxjdWxhdGluZyBzaW1wbGUgZGFpbHkgcmV0dXJuczoKcmV0dXJucyA8LSBhZGpwcmljZXMgLyBsYWcoYWRqcHJpY2VzLG49MSkgLSAxCiMgV2UgY2FuIGFsc28gY2FsY3VsYXRlIHNpbXBsZSByZXR1cm5zIHVzaW5nIHRoZSBkaWZmIGZ1bmN0aW9uOgpyZXR1cm5zMjwtIGRpZmYoYWRqcHJpY2VzKSAvIGxhZyhhZGpwcmljZXMsbj0xKQpgYGAKClRoZSBkaWZmIGZ1bmN0aW9uIHdvcmtzIHdpdGggeHRzIHRvIGNhbGN1bGF0ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB2YWx1ZSBvZiBvbmUgcGVyaW9kIG1pbnVzIHRoZSBwcmV2aW91cyBvbmUuIFRoZSBsYWcgZnVuY3Rpb24gZ2V0cyB0aGUgcHJldmlvdXMgdmFsdWUgb2YgdGhlIHRpbWUgc2VyaWVzLgoKV2UgY2FuIGNhbGN1bGF0ZSBob2xkaW5nIHJldHVybnMgZm9yIGFueSB0aW1lIHBlcmlvZCBmb3IgZWFjaCBpbnN0cnVtZW50IChIUFIpLiBGb3IgZXhhbXBsZSwgaWYgd2Ugd2FudCB0byBjYWxjdWxhdGUgdGhlIHdob2xlIHBlcmlvZCByZXR1cm4gZnJvbSB0aGUgZmlyc3QgZGF5IG9mIHRoZSBzZXJpZXMgdG8gdGhlIG1vc3QgcmVjZW50IG9uZSwgd2UgY2FuIGRvIHRoZSBmb2xsb3dpbmc6CgpgYGB7cn0KSFBSMTwtIDEwMCogYXMudmVjdG9yKGxhc3QoYWRqcHJpY2VzKSkgLyBhcy52ZWN0b3IoZmlyc3QoYWRqcHJpY2VzKSkgLSAxIApIUFIxCmBgYApgYGB7cn0KY2F0KCJUaGUgaG9sZGluZyBwZXJpb2QgcmV0dXJuIGZvciBCaXRjb2luIGlzOiAiLCBIUFIxWzFdLCAiJSIpCmBgYApgYGB7cn0KY2F0KCJUaGUgaG9sZGluZyBwZXJpb2QgcmV0dXJuIGZvciBCaXRjb2luIGlzOiAiLCBIUFIxWzJdLCAiJSIpCmBgYApXZSBjb3VsZCBhbHNvIHVzZSB0aGUgY29udGludW91c2x5IGNvbXBvdW5kZWQgcmV0dXJucyB0byBjYWxjdWxhdGUgdGhlIHNhbWUgaG9sZGluZyBzaW1wbGUgcmV0dXJuczoKCmBgYHtyfQpIUFIyPC0xMDAqZXhwKGNvbFN1bXMoY2NyZXR1cm5zLG5hLnJtPVRSVUUpKSAtIDEKSFBSMgpgYGAKIyMgMi4zIFZpc3VhbGl6YXRpb24gb2YgZmluYW5jaWFsIGRhdGEKCk9uZSBvZiB0aGUgYWR2YW50YWdlcyBvZiB0aGUgcXVhbnRtb2QgcGFja2FnZSBpcyB0aGF0IGl0IGhhcyBzb21lIGJ1aWx0LWluIGRhdGEgdmlzdWFsaXphdGlvbiBjYXBhYmlsaXRpZXMuIEZvciBleGFtcGxlLCB0aGUgY2hhcnRTZXJpZXMgZnVuY3Rpb24gaXMgYSBwbG90dGluZyB0b29sIGRlc2lnbmVkIHRvIGNyZWF0ZSBzdGFuZGFyZCBmaW5hbmNpYWwgY2hhcnRzIGdpdmVuIGEgdGltZSBzZXJpZXMgb2JqZWN0LiBUaGUgY2hhcnRTZXJpZXMgZnVuY3Rpb24gYWxzbyBpbmNsdWRlcyBzb21lIGFyZ3VtZW50cyB0aGF0IGFsbG93cyB0aGUgdXNlciB0byBtb2RpZnkgdGhlIGNvc21ldGljcyBvZiB0aGUgcGxvdCBzdWNoIGFzIHRoZSB0aGVtZSBhcmd1bWVudC4KCkxldOKAmXMgc2VlIHRoZSBwZXJmb3JtYW5jZSBvZiBCaXRjb2luIHByaWNlczoKCmBgYHtyfQpjaGFydFNlcmllcyhgQlRDLVVTRGAsIHRoZW1lID0gKCJ3aGl0ZSIpKQpgYGAKTGV04oCZcyBzZWUgdGhlIHBlcmZvcm1hbmNlIG9mIFRFU0xBIHByaWNlczoKYGBge3J9CmNoYXJ0U2VyaWVzKFRTTEEsIHRoZW1lID0gKCJibGFjayIpKQpgYGAKV2UgY2FuIHNlZSBleHBvbmVudGlhbCBncm93dGggb2YgYm90aCwgdGhlIEJpdGNvaW4gcHJpY2VzIGFuZCBUZXNsYSBwcmljZXMgZm9yIHRoZSBsYXN0IGRheXMuIElmIHlvdSBoYWQgaW52ZXN0ZWQgaW4gQml0Y29pbiBvciBUZXNsYSBpbiBKdWx5IDIwMjAgYW5kIGhhcyBzb2xkIHlvdXIgcG9zaXRpb24gZWFybHkgSmFudWFyeSAyMDIxLCB5b3Ugd291bGQgaGF2ZSBtdWx0aXBsaWVkIHlvdXIgaW52ZXN0bWVudCBieSBhcm91bmQgNCB0aW1lcyAoMzAwJSBwZXJpb2QgcmV0dXJuKSEKCldlIGNhbiB2aXN1YWxpemUgdGhlIGRhaWx5IHJldHVybnMgb3ZlciB0aW1lOgoKYGBge3J9CnBsb3QocmV0dXJucyRiaXRjb2luKQpgYGAKCmBgYHtyfQpwbG90KHJldHVybnMkdGVzbGEpCmBgYApXaXRoIHRoaXMgcGxvdCB3ZSBjYW4gYXBwcmVjaWF0ZSB0aGUgZGFpbHkgdm9sYXRpbGl0eSBvZiBlYWNoIGluc3RydW1lbnQgb3ZlciB0aW1lLiBWb2xhdGlsaXR5IGlzIGEgbWVhc3VyZSBvZiBob3cgZGlzcGVyc2UgdGhlIHJldHVybnMgbW92ZSB1cCBhbmQgZG93biBmcm9tIGl0cyBtZWFuOyBpdCBpcyBiYXNpY2FsbHkgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiByZXR1cm5zLgoKIyMgMi40IEJhc2ljcyBvZiBQb3J0Zm9saW8gYW5hbHlzaXMKV2Ugd2lsbCB1c2UgdGhlIFBlcmZvcm1hbmNlQW5hbHl0aWNzIGFuZCByZWxhdGVkIHBhY2thZ2VzIGZvciB0aGlzIHNlY3Rpb246CgpgYGB7cn0KIyBMb2FkIHRoZSBwYWNrYWdlczoKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoUGVyZm9ybWFuY2VBbmFseXRpY3MpCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpCmBgYAoKV2Ugd2lsbCBkbyBhbiBleGVyY2lzZSBvZiBhIHNpbXBsZSBwb3J0Zm9saW8gY29tcG9zZWQgYnkgNSBVUyBzdG9ja3M6IFRlc2xhLCBNaWNyb3NvZnQsIE5leHRlcmEsIFdhbE1hcnQgYW5kIFBmaXplci4gWW91IGNhbiBjaGFuZ2UgdGhlIHRpY2tlcnMgb2YgdGhlIHN0b2NrcyBhcyB5b3Ugd2lzaCBhcyBsb25nIGFzIFlhaG9vIEZpbmFuY2UgaGFzIGRhdGEgZm9yIHlvdXIgdGlja2Vycy4gSSBzZWxlY3RlZCBzdG9ja3MgZnJvbSB0aGUgaW5kdXN0cmllczogaGlnaC10ZWNoLCBjbGVhbiBlbmVyZ2llcywgcGhhcm1hY2V1dGljYWwsIGFuZCByZXRhaWwuCgojIyAyLjQuMSBBdXRvbWF0aW9uIG9mIGRhdGEgY29sbGVjdGlvbiBhbmQgZGF0YSBtYW5hZ2VtZW50CldpdGggdGhlIGZvbGxvd2luZyBjb2RlIHdlIHdpbGwgYXV0b21hdGljYWxseSBkb3dubG9hZCBkYXRhIGZyb20gdGhlIDUgc3RvY2tzLCBtZXJnZSB0aGUgZGF0YSBhbmQgY2FsY3VsYXRlIHJldHVybnM6CgpgYGB7cn0Kc3ltYm9scyA8LSBjKCJUU0xBIiwiTVNGVCIsIk5FRSIsIldNVCIsIlBGRSIpCgpwcmljZXMgPC0gZ2V0U3ltYm9scyhzeW1ib2xzLHNyYyA9ICd5YWhvbycsCiAgICAgICAgICAgICBwZXJpb2RpY2l0eSA9ICJtb250aGx5IiwKICAgICAgICAgICAgIGZyb20gPSAiMjAxOC0wMS0wMSIsCiAgICAgICAgICAgICBhdXRvLmFzc2lnbiA9IFRSVUUsCiAgICAgICAgICAgICB3YXJuaW5ncyA9IEZBTFNFKSAlPiUKICBtYXAofkFkKGdldCguKSkpICU+JQogIHJlZHVjZShtZXJnZSkgJT4lCiAgYGNvbG5hbWVzPC1gKHN5bWJvbHMpCmBgYApJZiB5b3Ugc2VlIEkgYW0gZG9pbmcgc2V2ZXJhbCBkYXRhIG1hbmFnZW1lbnQgcHJvY2VzcyBpbiBzZXF1ZW50aWFsIHByb2Nlc3MuIFRoZSAlPiUgaXMgdXNlZCB0byBpbmRpY2F0ZSB0aGUgc2VwYXJhdGlvbiBvZiBlYWNoIGRhdGEgbWFuYWdlbWVudCBwcm9jZXNzLiBUaGUgcHJldmlvdXMgY29kZSBkb2VzIHRoZSBmb2xsb3dpbmc6CgpEb3dubG9hZCBwcmljZSBkYXRhIG9mIHRoZSA1IHN0b2NrcwoKQXBwbHkgKG1hcCkgdGhlIGFkanVzdGVkIGZ1bmN0aW9uIHRvIGFsbCB4dHMtem9vIGRhdGFzZXRzIGluIG9yZGVyIHRvIGdldCB0aGUgYWRqdXN0ZWQgc3RvY2sgcHJpY2VzCgpEbyB0aGUgbWVyZ2Ugb2YgYWxsIHRoZSB4dHMtem9vIG9iamVjdHMgaW50byBvbmUgb2JqZWN0CgpGaW5hbGx5IHJlbmFtZSB0aGUgY29sdW1ucyBvZiB0aGUgaW50ZWdyYXRlZCBvYmplY3QsIGFuZCByZXR1cm4gdGhlIG9iamVjdCBhcyBwcmljZXMuCgojIyAyLjQuMiBSZXR1cm4gY2FsY3VsYXRpb25zCgpXZSBjYWxjdWxhdGUgcmV0dXJucyBvZiBhbGwgc3RvY2tzIHVzaW5nIHRoZSBSZXR1cm4uY2FsY3VsYXRlIGZ1bmN0aW9uOgoKYGBge3J9ClJldHVybnM8LVJldHVybi5jYWxjdWxhdGUocHJpY2VzKSAlPiUKICBuYS5vbWl0KCkgCmBgYAoKVGhlIG5hLm9taXQgZnVuY3Rpb24gd2FzIGFsc28gYXBwbGllZCB0byBjbGVhbiB0aGUgZGF0YXNldCBhbmQgZHJvcCB0aGUgcm93cyB3aXRoIE5BIHZhbHVlcy4KCldlIGNhbGN1bGF0ZSB0aGUgY29udGludW91cyBjb21wb3VuZGVkIHJldHVybiAoYWxzbyBjYWxsZWQgbG9nIHJldHVybnMpIG9mIGFsbCBzdG9ja3M6CgpgYGB7cn0KcmV0dXJuczwtUmV0dXJuLmNhbGN1bGF0ZShwcmljZXMsbWV0aG9kID0gImxvZyIpICU+JQogICAgICAgICBuYS5vbWl0KCkgCiMgV2UgY291bGQgY2FsY3VsYXRlIHRoaXMgdXNpbmcgdGhlIGRpZmYgYW5kIGxvZyBmdW5jdGlvbnM6CnJldHVybnMyPC1uYS5vbWl0KGRpZmYobG9nKHByaWNlcykpKQojIFRoZSByZXR1cm5zIGFuZCByZXR1cm5zMiBvYmplY3RzIHdpbGwgaGF2ZSBleGFjdGx5IHRoZSBzYW1lIHJldHVybnMKYGBgCgojIyAyLjQuMyBEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIG9mIHJldHVybnMKV2UgY2FsY3VsYXRlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgZm9yIHRoZSByZXR1cm5zIG9mIGFsbCBzdG9ja3M6CgpgYGB7cn0KdGFibGUuU3RhdHMocmV0dXJucykgCmBgYApXZSBjYW4gc2VlIGFyaXRobWV0aWMsIGdlb21ldHJpYyBtZWFuLCBtZWRpYW4gb2YgcmV0dXJucy4gQWxzbyB3ZSBjYW4gc2VlIHJpc2sgbWVhc3VyZXMgc3VjaCBhcyBzdGFuZGFyZCBkZXZpYXRpb24sIHZhcmlhbmNlLiBGaW5hbGx5IHdlIGNhbiBzZWUgc2tld25lc3MgYW5kIGt1cnRvc2lzLCB3aGljaCBhcmUgaW1wb3J0YW50IGZpbmFuY2lhbCBtZWFzdXJlcyB0byB1bmRlcnN0YW5kIHRoZSBkYXRhLgoKIyMgMi40LjQgVmlzdWFsaXphdGlvbiBvZiByaXNrIGFuZCByZXR1cm4gb2Ygc3RvY2tzCldlIGNhbiBkbyBhIEJveCBwbG90IG9mIHRoZSByZXR1cm5zIHRvIGJldHRlciBhcHByZWNpYXRlIGhpc3RvcmljYWwgbWVkaWFuIHJldHVybiBhbmQgcmlzayBieSBsb29raW5nIGF0IHRoZSBtZWRpYW4sIHRoZSBxdWFydGlsZXMgUTEgYW5kIFEzLCB2b2xhdGlsaXR5IGFuZCBleHRyZW1lIHZhbHVlcyBvZiB0aGVzZSByZXR1cm5zOgoKYGBge3J9CmNoYXJ0LkJveHBsb3QocmV0dXJucykKYGBgCkl0IGlzIGVhc3kgdG8gc2VlIHRoYXQgVGVzbGEgaXMgdGhlIHN0b2NrIHdpdGggdGhlIGhpZ2hlc3Qgcmlzay4gVGhlIHJlZCBjaXJjbGVzIHNob3cgdGhlIG1lYW4sIHRoZSBtaWQgbGluZSBpcyB0aGUgbWVkaWFuICg1MCBwZXJjZW50aWxlKSwgdGhlIGJveGVzIGluY2x1ZGUgdGhlIDUwJSBvZiB0aGUgZGF0YSBmcm9tIHRoZSBRMSAoMjUgcGVyY2VudGlsZSkgdG8gdGhlIFEzICg3NSBwZXJjZW50aWxlKS4gVGhlIGRvdHMgYXJlIGNvbnNpZGVyZWQgZXh0cmVtZSB2YWx1ZXMgZm9yIGluIHRoZSBjb250ZXh0IG9mIGl0cyBvd24gZGlzdHJpYnV0aW9uLgoKTm93IHdlIGNhbiBzdGFydCBldmFsdWF0aW5nIHRoZSBwZXJmb3JtYW5jZSBvdmVyIHRpbWUgZm9yIGVhY2ggc3RvY2sgYnkgbG9va2luZyBhdCBob3cgbXVjaCAkIHdlIHdvdWxkIGhhdmUgbWFkZSBpZiB3ZSBoYWQgaW52ZXN0ZWQgJDEuMDAgaW4gZWFjaCBzdG9jayBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSB0aW1lIHBlcmlvZHM6CgpgYGB7cn0KY2hhcnRzLlBlcmZvcm1hbmNlU3VtbWFyeShSZXR1cm5zLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluID0gIlBlcmZvcm1hbmNlIG9mICQxLjAwIG92ZXIgdGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2VhbHRoLmluZGV4ID0gVFJVRSkKYGBgCgpTaW5jZSBUZXNsYSBoYXMgaGFkIGFuIGV4dHJhb3JkaW5hcnkgcGVyZm9ybWFuY2UgZm9yIHRoZSBsYXN0IG1vbnRocywgaXQgaXMgaGFyZCB0byBhcHByZWNpYXRlIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgcmVzdCBvZiB0aGUgc3RvY2tzLiBTbywgd2UgY2FuIGRyb3AgVGVzbGEgZnJvbSB0aGUgcGxvdDoKCmBgYHtyfQpjaGFydHMuUGVyZm9ybWFuY2VTdW1tYXJ5KFJldHVybnNbLDI6NV0sIAogICAgICAgICAgICAgICAgICAgICAgICBtYWluID0gIlBlcmZvcm1hbmNlIG9mICQxLjAwIG92ZXIgdGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgd2VhbHRoLmluZGV4ID0gVFJVRSkKYGBgCgojIyAyLjUgUG9ydGZvbGlvIGZvcm1hdGlvbgoKSSBzdGFydCBjcmVhdGluZyB0aGUgd2VpZ2h0cyBvZiAyIHBvcnRmb2xpb3MuIE9uZSB3aWxsIGJlIGFuIGVxdWFsbHktd2VpZ2h0ZWQgcG9ydGZvbGlvLCBhbmQgdGhlIG90aGVyIHdpbGwgYmUgYW4gYWdncmVzc2l2ZSBwb3J0Zm9saW8gYXNzaWduaW5nIGhpZ2ggd2VpZ2h0cyB0byByaXNreSBzdG9ja3MsIGFuZCBsb3cgd2VpZ2h0cyB0byBjb25zZXJ2YXRpdmUgc3RvY2tzLgoKSSBzdGFydCBjcmVhdGluZyBhIHZlY3RvciBvZiB3ZWlnaHRzIGZvciB0aGUgZXF1YWxseSB3ZWlnaHRlZCBwb3J0Zm9saW86CgpgYGB7cn0Kd19ldyA9IHJlcCgwLjIsNSkKIyBUaGUgcmVwIGZ1bmN0aW9uIHJlcGVhdHMgYSB2YWx1ZSBuIHRpbWVzCmBgYAoKTm93IEkgY3JlYXRlIGEgdmVjdG9yIG9mIHdlaWdodHMgZm9yIHRoZSBhZ2dyZXNzaXZlIHBvcnRmb2xpby4gSSB3aWxsIGFzc2lnbiB0aGUgZm9sbG93aW5nIHdlaWdodHM6CgpUZXNsYTogNDAlIE1pY3Jvc29mdDogMzAlIE5leHRlcmE6IDIwJSBXYWxNYXJ0OjAlIFBmaXplcjogMTAlCgpgYGB7cn0Kd19hZ2dyZXNzaXZlID0gYygwLjQsMC4zLDAuMiwwLDAuMSkKYGBgCgojIyAyLjYgUG9ydGZvbGlvIHJldHVybiBjYWxjdWxhdGlvbgoKV2UgY2FsY3VsYXRlIHRoZSBoaXN0b3JpY2FsIHJldHVybiBvZiB0aGUgZXF1YWxseS13ZWlnaHRlZCBwb3J0Zm9saW8gdXNpbmcgdGhlIFJldHVybi5wb3J0Zm9saW8gZnVuY3Rpb246CgpgYGB7cn0KcG9ydGZvbGlvX3JldHVybnNfZXcgPC0KICBSZXR1cm4ucG9ydGZvbGlvKFJldHVybnMsCiAgICAgICAgICAgICAgICAgICB3ZWlnaHRzID0gd19ldykgJT4lCiAgYGNvbG5hbWVzPC1gKCJyZXR1cm5zIikKYGBgCgpXZSBjYWxjdWxhdGUgdGhlIGhpc3RvcmljYWwgcmV0dXJuIG9mIHRoZSBhZ2dyZXNzaXZlIHBvcnRmb2xpbyB1c2luZyB0aGUgUmV0dXJuLnBvcnRmb2xpbyBmdW5jdGlvbjoKCmBgYHtyfQpwb3J0Zm9saW9fcmV0dXJuc19hZyA8LQogIFJldHVybi5wb3J0Zm9saW8oUmV0dXJucywKICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSB3X2FnZ3Jlc3NpdmUpICU+JQogIGBjb2xuYW1lczwtYCgicmV0dXJucyIpCmBgYAoKV2l0aCB0aGUgcG9ydGZvbGlvIHJldHVybnMgSSBjYW4gcGxvdCBhIHBlcmZvcm1hbmNlIGNoYXJ0IG9mIGVhY2ggcG9ydGZvbGlvOgoKYGBge3J9CmNoYXJ0cy5QZXJmb3JtYW5jZVN1bW1hcnkocG9ydGZvbGlvX3JldHVybnNfZXcsIAogICAgICAgICAgICAgICAgICAgICAgICAgIG1haW4gPSAiRXF1YWxseS13ZWlnaHRlZCBQb3J0Zm9saW8iKQpgYGAKCmBgYHtyfQpjaGFydHMuUGVyZm9ybWFuY2VTdW1tYXJ5KHBvcnRmb2xpb19yZXR1cm5zX2FnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBtYWluID0gIkFnZ3Jlc3NpdmUgUG9ydGZvbGlvIFBlcmZvcm1hbmNlIikKYGBgCldlIGZpbmFsbHkgZG8gYSBkeW5hbWljIHBsb3Qgb2YgaGlzdG9yaWNhbCBtb250aGx5IHJldHVybnMgb2YgZWFjaCBwb3J0Zm9saW8gdXNpbmcgdGhlIGhpZ2hjaGFydCBmdW5jdGlvbiBmcm9tIHRoZSBoaWdoY2hhcnRlciBwYWNrYWdlOgoKYGBge3J9CmhpZ2hjaGFydCh0eXBlID0gInN0b2NrIikgJT4lIAogIGhjX3RpdGxlKHRleHQgPSAiRXF1YWxseS13ZWlnaHRlZCBQb3J0Zm9saW8iKSAlPiUgCiAgaGNfYWRkX3Nlcmllcyhwb3J0Zm9saW9fcmV0dXJuc19ldyRyZXR1cm5zLCAKICAgICAgICAgICAgICAgIG5hbWUgPSAiTW9udGhseSBSZXR1cm5zIiwgCiAgICAgICAgICAgICAgICBjb2xvciA9ICJjb3JuZmxvd2VyYmx1ZSIpICU+JSAKICBoY19hZGRfdGhlbWUoaGNfdGhlbWVfZmxhdCgpKSAlPiUgCiAgaGNfbmF2aWdhdG9yKGVuYWJsZWQgPSBGQUxTRSkgJT4lIAogIGhjX3Njcm9sbGJhcihlbmFibGVkID0gRkFMU0UpICU+JSAKICBoY19sZWdlbmQoZW5hYmxlZCA9IFRSVUUpICU+JSAKICBoY19leHBvcnRpbmcoZW5hYmxlZCA9IFRSVUUpCmBgYApXZSBjYW4gaW50ZXJhY3Qgd2l0aCB0aGUgcGxvdCB0byB6b29tIGluIGFuZCBvdXQsIGFuZCBzZWxlY3RpbmcgdGltZSBwZXJpb2RzLgoKV2UgZG8gdGhlIHNhbWUgZm9yIHRoZSBhZ2dyZXNzaXZlIHBvcnRmb2xpbzoKCmBgYHtyfQpoaWdoY2hhcnQodHlwZSA9ICJzdG9jayIpICU+JSAKICBoY190aXRsZSh0ZXh0ID0gIkFnZ3Jlc3NpdmUgUG9ydGZvbGlvIikgJT4lIAogIGhjX2FkZF9zZXJpZXMocG9ydGZvbGlvX3JldHVybnNfYWckcmV0dXJucywgCiAgICAgICAgICAgICAgICBuYW1lID0gIk1vbnRobHkgUmV0dXJucyIsIAogICAgICAgICAgICAgICAgY29sb3IgPSAiY29ybmZsb3dlcmJsdWUiKSAlPiUgCiAgaGNfYWRkX3RoZW1lKGhjX3RoZW1lX2ZsYXQoKSkgJT4lIAogIGhjX25hdmlnYXRvcihlbmFibGVkID0gRkFMU0UpICU+JSAKICBoY19zY3JvbGxiYXIoZW5hYmxlZCA9IEZBTFNFKSAlPiUgCiAgaGNfbGVnZW5kKGVuYWJsZWQgPSBUUlVFKSAlPiUgCiAgaGNfZXhwb3J0aW5nKGVuYWJsZWQgPSBUUlVFKQpgYGAKCiMgMyBJbnRyb2R1Y3Rpb24gdG8gUG9ydGZvbGlvIFRoZW9yeQoKUmVnYXJkbGVzcyB0aGUgYXBwcm9hY2ggeW91IHVzZSB0byBkb3dubG9hZCBhbmQgbWFuaXB1bGF0ZSB0aGUgc291cmNlIGRhdGEsIG9uY2UgdGhhdCB5b3VyIGRhdGEgZnJhbWUgb2YgbW9udGhseSByZXR1cm5zIGlzIHJlYWR5IGZvciBhbmFseXNpcyB5b3Ugd2lsbCBiZSBpbiBhIGdvb2QgcG9zaXRpb24gdG8gY3JlYXRlIHRoZSBWYXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCB3aGljaCBpcyBuZWVkZWQgdG8gY29tcHV0ZSB0aGUgZXhwZWN0ZWQgcmlzayBvZiB0aGUgcG9ydGZvbGlvLgoKVGhlIGZpcnN0IHN0ZXAgaXMgdG8gdHJhbnNmb3JtIHRoZSBkYXRhIGZyYW1lIGludG8gYSBtYXRyaXggdXNpbmcgdGhlIGFzLm1hdHJpeCBmdW5jdGlvbi4gV2Ugc2F2ZSB0aGUgcmVzdWx0IGluIGEgbWF0cml4IGNsYXNzIG9iamVjdCBjYWxsZWQgcmV0Lm1hdC4KCmBgYHtyfQpyZXQubWF0IDwtIGFzLm1hdHJpeChSZXR1cm5zKQpgYGAKCldlIGNvbXB1dGUgdGhlIFZhci1Db3ZhcmlhbmNlIG1hdHJpeC4gVGhlIHZhciBmdW5jdGlvbiByZWNlaXZlcyBhIG1hdHJpeCBvZiByZXR1cm5zIGFzIHBhcmFtZXRlci4gVGhlIHJldHVybnMgb2Ygc3RvY2sgMSBhcmUgaW4gY29sdW1uIDEsIHJldHVybnMgb2Ygc3RvY2sgMiBhcmUgaW4gY29sdW1uIDIsIGFuZCBzbyBvbi4gVGhpcyBmdW5jdGlvbiBjYWxjdWxhdGVzIHRoZSBWYXJpYW5jZS1Db3ZhcmlhbmNlIE1hdHJpeCBvZiBzdG9jayByZXR1cm5zLgoKUmVtZW1iZXIgdGhhdCB0aGUgVmFyaWFuY2UtQ292YXJpYW5jZSBtYXRyaXggY29udGFpbnMgVmFyaWFuY2VzIG9mIHN0b2NrIHJldHVybnMgaW4gdGhlIGRpYWdvbmFsLCBhbmQgaXQgY29udGFpbnMgdGhlIGNvdmFyaWFuY2VzIG9mIHBhaXJzIG9mIHN0b2NrIHJldHVybnMgaW4gdGhlIG5vbi1kaWFnb25hbHMuIEFsc28sIHRoaXMgbWF0cml4IGlzIHN5bWV0cmljLCBzaW5jZSBDb3YoUmV0aSwgUmV0aikgPSBDb3YoUmV0aixSZXRpKS4gTW9yZSBmb3JtYWxseToKCkZvciBleGFtcGxlLCB0aGUgdmFyaWFuY2UtY292YXJpYW5jZSBtYXRyaXggb2YgMiB2YXJpYWJsZXMgWDEgYW5kIFgyIGNvbnRhaW5zIHRoZSB2YXJpYW5jZSBvZiBlYWNoIHZhcmlhYmxlIGluIGl0cyBkaWFnb25hbCBhbmQgY292YXJpYW5jZXMgYmV0d2VlbiB0aGUgdmFyaWFibGVzIGluIGl0cyBub24tZGlhZ29uYWwgdGVybXMuIEluIG90aGVyIHdvcmRzLCB0aGUgZWxlbWVudHMgaW4gdGhlIHVwcmlnaHQgcGFydCBvZiB0aGUgbWF0cml4IGFyZSByZXBlYXRlZCBpbiB0aGUgZG93bi1sZWZ0IHBhcnQgb2YgdGhlIG1hdHJpeC4gTW9yZSBmb3JtYWxseToKCuKIkWNvdj1bVmFyKFgxKUNvdihYMixYMSlDb3YoWDEsWDIpVmFyKFgyKV0KCmBgYHtyfQpDT1YgPC0gdmFyKHJldC5tYXQpCmBgYAoKV2UgY2FuIHNlZSB0aGUgcmVzdWx0cyBvZiB0aGUgVmFyLUNvdmFyIG1hdHJpeCBhcyBzaG93biBiZWxvdzoKCmBgYHtyfQpDT1YKYGBgCgpXZSBjYW4gYWxzbyBjYWxjdWxhdGUgdGhlIENvcnJlbGF0aW9uIE1hdHJpeCB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgcmVsYXRpb25zaGlwcyBvZiBhbGwgZGlmZmVyZW50IHBhaXJzIG9mIHN0b2NrIHJldHVybnMuCgpgYGB7cn0KY29yKHJldC5tYXQpCmBgYAoKUmVtZW1iZXIgdGhhdCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiAyIHN0b2NrIHJldHVybnMgY2FuIGJlIGFueSB2YWx1ZSBiZXR3ZWVuIC0xIGFuZCAxLiBXaGVuIHRoZSBjb3JyZWxhdGlvbiBpcyBjbG9zZSB0byAwLCBpdCBtZWFucyB0aGF0IGJvdGggc3RvY2tzIGhhdmUgbm8gcmVsYXRpb25zaGlwLiBJZiB0aGUgY29ycmVsYXRpb24gaXMgY2xvc2UgdG8gMSBpdCBtZWFucyB0aGF0IGJvdGggc3RvY2sgcmV0dXJucyBtb3ZlIGluIGEgdmVyeSBzaW1pbGFyIHdheSBpbiB0aGUgc2FtZSBkaXJlY3Rpb24uCgpXZSBjYWxjdWxhdGUgdGhlIGV4cGVjdGVkIHNpbXBsZSByZXR1cm5zIG9mIHRoZSBzdG9ja3MgaW4gYW5vdGhlciBtYXRyaXggdXNpbmcgdGhlIGFwcGx5IGZ1bmN0aW9uLiBJdCBpcyBpbXBvcnRhbnQgdG8gZG91YmxlIGNoZWNrIHRoYXQgdGhlIHNlY29uZCBwYXJhbWV0ZXIgaXMgZXF1YWwgdG8gMi4gVGhpcyBtZWFucyB0aGF0IHdlIGFyZSBhcHBseWluZyB0aGUgZnVuY3Rpb24gbWVhbiBieSBjb2x1bW4uIElmIHdlIHdhbnQgdG8gYXBwbHkgdGhlIG1lYW4gZnVuY3Rpb24gYnkgcmF3LCB3ZSBoYXZlIHRvIHNwZWNpZnkgdGhlIHNlY29uZCBwYXJhbWV0ZXIgYXMgMS4KCmBgYHtyfQpFUjwtIGV4cChhcHBseShyZXQubWF0LCAyLCBtZWFuKSkgLSAxCmBgYAoKVGhpcyBpcyB0aGUgc2ltcGxlIHdheSB0byBjYWxjdWxhdGUgdGhlIGV4cGVjdGVkIHJldHVybiBvZiBlYWNoIHN0b2NrIGFjY29yZGluZyB0byBNYXJrb3dpdHogdGhlb3J5LiBSZW1lbWJlciB0aGF0IHRoZSBleHBlY3RlZCByZXR1cm4gb2Ygb25lIHN0b2NrIGlzIHNpbXBseSB0aGUgR2VvbWV0cmljIGF2ZXJhZ2UgcmV0dXJuIG9mIGl0cyBoaXN0b3JpY2FsIHJldHVybnMuCgpUbyBjYWxjdWxhdGUgdGhpcyBleHBlY3RlZCByZXR1cm4gd2UgaGF2ZSAyIG1ldGhvZHM6CgpDYWxjdWxhdGUgdGhlIHByb2R1Y3RzIG9mIEdyb3NzIFJldHVybnMgb2YgZWFjaCBwZXJpb2QsIHN1YnN0cmFjdCAxLCBhbmQgdGhlbiBhcHBseSB0aGUgTiByb290IHRvIGdldCB0aGUgZ2VvbWV0cmljIGF2ZXJhZ2UuCgpHZXQgdGhlIGFyaXRobWV0aWMgYXZlcmFnZSBvZiBjb250aW51b3VzbHkgY29tcG91bmRlZCByZXR1cm5zIGFuZCB0aGVuIGNvbnZlcnQgdGhpcyBhbW91bnQgdG8gc2ltcGxlIHJldHVybnMgYnkgcmFpc2luZyBlIHRvIHRoaXMgYXZlcmFnZSBhbmQgdGhlbiBzdWJzdHJhY3QgMS4KCldlIGhhdmUgZXN0aW1hdGVkIHRoZSBnZW9tZXRyaWMgYXZlcmFnZSB1c2luZyB0aGUgbWV0aG9kIDIsIGJ1dCBub3Qgb25seSBmb3Igb25lIHN0b2NrLCBidXQgZm9yIGFsbCBzdG9ja3MgaW4gdGhlIHRoZSBtYXRyaXggcmV0Lm1hdC4gVGhlIHJlc3VsdCB3aWxsIGJlIGEgdmVjdG9yIG9mIGdlb21ldHJpYyByZXR1cm5zIHRoYXQgd2lsbCBiZSBzYXZlZCBpbiBlci4gV2UgY2FuIGVzdGltYXRlIHRoZSB2ZWN0b3IgTXIgb2YgZXhwZWN0ZWQgcmV0dXJucyBhczoKCk1SPWV4cFtNcl09ZXhw4o6h4o6j4o6i4o6i4o6i4o6i4o6i4o6i4o6i4o6icjHCr3Iywq8uLi5yTsKv4o6k4o6m4o6l4o6l4o6l4o6l4o6l4o6l4o6l4o6l4oiSMQoKV2UgY2FuIHNlZSB0aGUgZXhwZWN0ZWQgc2ltcGxlIHJldHVybnMgYnkgY2FsbGluZyB0aGUgZXIgb2JqZWN0LgoKYGBge3J9CkVSCmBgYAoKV2UgY2FsY3VsYXRlIHRoZSB2YXJpYW5jZSwgc3RhbmRhcmQgZGV2aWF0aW9uLCBza2V3bmVzcyBhbmQga3VydG9zaXMgb2YgYWxsIGFzc2V0IHJldHVybnMgKGJ5IGNvbHVtbnMpLiBXZSBkbyB0aGlzIGp1c3QgdG8gc2VlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgb2YgdGhlIHN0b2NrIHJldHVybnMuCgpgYGB7cn0KYXBwbHkocmV0Lm1hdCwgMiwgdmFyKQpgYGAKYGBge3J9CmFwcGx5KHJldC5tYXQsIDIsIHNkKQpgYGAKYGBge3J9CmFwcGx5KHJldC5tYXQsIDIsIHNrZXduZXNzKQpgYGAKYGBge3J9CmFwcGx5KHJldC5tYXQsIDIsIGt1cnRvc2lzKQpgYGAKUmVtZW1iZXIgd2hhdCBpcyBza2V3bmVzcyBhbmQga3VydG9zaXMuIFNrZXduZXNzIGlzIGEgbWVhc3VyZSBvZiB0aGUgc2hhcGUgb2YgdGhlIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiB0aGUgcmV0dXJucyB3aGlsZSBrdXJ0b3NpcyBnaXZlcyB1cyBpbmZvcm1hdGlvbiBhYm91dCBob3cgbXVjaCB0aGUgc3RvY2sgaGFzIGhhZCBleHRyZW1lIG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSByZXR1cm5zLiBJbiBvdGhlciB3b3Jkcywga3VydG9zaXMgaGVscHMgdXMgdG8gaWRlbnRpZnkgaG93IGJpZyBhcmUgdGhlIGZhdC10YWlscyBvZiB0aGUgcmV0dXJuIGRpc3RyaWJ1dGlvbi4gV2UgY2FuIGNvbXBsZW1lbnQgdGhpcyB3aXRoIHRoZSBoaXN0b2dyYW0gb2YgcmV0dXJucyB0byBiZXR0ZXIgYXBwcmVjaWF0ZSB0aGUgbGV2ZWwgb2YgcmlzayBvZiB0aGUgc3RvY2suCgojIDQgQ2FsY3VsYXRpb24gb2YgZXhwZWN0ZWQgdmFyaWFuY2UgYW5kIHJpc2sgb2YgYSBQb3J0Zm9saW8KQXNzdW1lIHRoYXQgeW91IGludmVzdCBpbiBhIHBvcnRmb2xpbyB3aXRoIHRoZSBmb2xsb3dpbmcgd2VpZ2h0czogQXBwbGUgMzAlLCBXYWxtYXJ0IDEwJSwgTWljcm9zb2Z0IDMwJSwgR0UgMTAlIGFuZCBUZXNsYSAyMCUuIENyZWF0ZSBhIHZlY3RvciB3aXRoIHRoZXNlIDUgd2VpZ2h0czoKCmBgYHtyfQpXIDwtIGMoMC4zMCwwLjEwLDAuMzAsMC4xMCwwLjIwKQpgYGAKCkFjY29yZGluZyB0byBQb3J0Zm9saW8gVGhlb3J5LCB0aGUgZXhwZWN0ZWQgcmlzayBvZiBhIHBvcnRmb2xpbyBQIGlzIGNhbGN1bGF0ZWQgYXMgdGhlIHNxdWFyZWQgcm9vdCBvZiB0aGUgUG9ydGZvbGlvIFZhcmlhbmNlLgoKU0QoUCk9KFZhcihQKSniiJLiiJLiiJLiiJLiiJLiiJLiiJLiiJLiiJoKClRoZSBwb3J0Zm9saW8gdmFyaWFuY2UgY2FuIGJlIGNhbGN1bGF0ZWQgd2l0aCB0aGUgZm9sbG93aW5nIG1hdHJpeCBtdWx0aXBsaWNhdGlvbjoKClZhcihQKT1X4oCy4oiX4oiRY2924oiXVwp3aGVyZToKClcgaXMgdGhlIHdlaWdodCB2ZWN0b3Igb2YgdGhlIHN0b2NrcywgYW5kIFfigLIgaXMgdGhlIHRyYW5wb3NlZCBtYXRyaXggb2Ygd2VpZ2h0cy4KCldlIG5vdyBjYW4gZXN0aW1hdGUgdGhlIGV4cGVjdGVkIHJldHVybiBhbmQgZXhwZWN0ZWQgcmlzayBvZiB0aGlzIHBvcnRmb2xpby4KCk5vdyBJIGFtIHJlYWR5IHRvIHN0YXJ0IGVzdGltYXRpbmcgdGhlIGV4cGVjdGVkIHJldHVybiBhbmQgcmlzayBvZiB0aGUgcG9ydGZvbGlvIHVzaW5nIE1hdHJpeCBBbGdlYnJhOgoKYGBge3J9CkVSUDEgPC0gdChXKSUqJUVSCkVSUDEKYGBgCgpUaGUgdmVjdG9yIEVSUDEgd2lsbCBoYXZlIG9uZSB2YWx1ZSB3aXRoIHRoZSBleHBlY3RlZCByZXR1cm4gb2YgdGhlIHBvcnRmb2xpby4gVGhlIHQgZnVuY3Rpb24gaXMgdGhlIHRyYW5zcG9zZSBmdW5jdGlvbiBvZiBhIHZlY3RvciBvciBtYXRyaXguIFRoZSByZXN1bHQgb2YgbXVsdGlwbHlpbmcgdGhlIHZlY3RvciB0cmFuc3Bvc2VkIHRpbWVzIHRoZSB2ZWN0b3Igb2YgZXhwZWN0ZWQgcmV0dXJucyBvZiB0aGUgc3RvY2tzIGlzIHRoZSBzYW1lIGFzIGNhbGN1bGF0aW5nIGEgd2VpZ2h0ZWQgYXZlcmFnZS4KCk5vdyBJIGNhbiBlc3RpbWF0ZSB0aGUgZXhwZWN0ZWQgcmlzayBvZiB0aGUgcG9ydGZvbGlvIHVzaW5nIE1hdHJpeCBBbGJlZ3JhOgoKYGBge3J9CkVWQVJQT1JUIDwtIHQoVyklKiVDT1YlKiVXCkVSSVNLIDwtIHNxcnQoRVZBUlBPUlQpCkVSSVNLCmBgYApXaXRoIHRoaXMgbWF0cml4IG11bHRpcGxpY2F0aW9uIEkgYW0gYXBwbHlpbmcgTWFya293aXR6IHRoZW9yeSB0byBlc3RpbWF0ZSB0aGUgZXhwZWN0ZWQgdmFyaWFuY2Ugb2YgdGhlIHBvcnRmb2xpby4gWW91IGNhbiByZXZpZXcgd2h5IHRoaXMgaXMgdGhlIGNhc2UgaW4gdGhlIE5vdGVzIGFib3V0IFBvcnRmb2xpbyBUaGVvcnkuIE1ha2Ugc3VyZSB5b3UgdW5kZXJzdGFuZCB3aHkgd2UgZG8gdGhpcyBtYXRyaXggbXVsdGlwbGljYXRpb24uCg==