• 1 The building blocks
    • 1.1 Introduction
    • 1.2 The portfolio weights
    • 1.3 The portfolio return
    • 1.4 PerformanceAnalytics
  • 2 Analyzing performance
  • 3 Performance drivers
  • 4 Optimizing the portfolio

1 The building blocks

1.1 Introduction

1.1.1 Getting a grasp of the basics

The goal of every investor is to make profits while limiting the amount of risk involved. Investors create strategies to reduce these risks. Diversified portfolios reduce risk by offsetting loss with a potential gain in another asset!

1.1.2 Get a feel for the data

The choice of investment matters even when the underlying risky assets are similar. As a first example, let us consider the stock price of the Coca Cola Company and the PepsiCo company from January 2003 until the end of August 2016.

The time series plot shows you the value evolution of one dollar invested in each company. As an exercise, plot the time series showing the relative value of an investment in the Coca Cola company, compared to the value of an investment in PepsiCo. To do this exercise, you can use the corresponding price series, available as the variables ko and pep in your workspace.

Three important packages that you will use in this course are the xts and zoo packages (these are popular when working with time series data), and the PerformanceAnalytics package. Define ko_pep as the ratio expressing the value of the share price of the Coca Cola company in terms of the share price of PepsiCo.

Use plot.zoo() to visualize the variation in this ratio over time. Note that when the value of the ratio is larger than 1, the performance of ko since January 2003 is higher than that of pep.

As a reference line, use abline() to include a horizontal line at h=1. Note that where the value of the ratio is larger than one, the Coca Cola Company outperforms Pepsico and vice versa.

library(zoo)
package 㤼㸱zoo㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱zoo㤼㸲

The following objects are masked from 㤼㸱package:base㤼㸲:

    as.Date, as.Date.numeric
library(xts)
package 㤼㸱xts㤼㸲 was built under R version 4.0.3
stock01 <- readRDS("data/re_prices.RData")
stock02 <- readRDS("data/eq_prices.RData")
plot(stock01)
lines(stock02, col = "red")

# Define ko_pep 
ko_pep <- stock02/stock01

# Make a time series plot of ko_pep
plot.zoo(ko_pep)  
  
# Add as a reference, a horizontal line at 1
abline(h = 1)  

The horizontal reference indicates when Coca Cola Company and PepsiCo are performing equally.

1.2 The portfolio weights

1.2.1 Calculating portfolio weights when component values are given

In the video, it was shown that you can easily compute portfolio weights if you have a given amount of money invested in certain assets. If you want to start investing in a portfolio, but you have budget restraints, you can also impose weights yourself. Depending on what these are, you will invest a certain amount of money in each of the assets based on their weight.

When given asset values, calculating the weights is quite simple. Recall from the video that weights are calculated by taking the value of an asset divided by the sum of values from all assets.

In this exercise, you will learn to calculate weights when individual asset values are given! For this example, an investor has 4000 USD invested in equities, 4000 USD invested in bonds, and 2000 USD invested in commodities. Compute the weights as the proportion invested in each of those three assets.

# Define the vector values
values <- c(4000, 4000, 2000)

# Define the vector weights
weights <- values/sum(values)

# Print the resulting weights
print(weights)
[1] 0.4 0.4 0.2

Remember that the weight of an asset is calculated by taking the value of the asset divided by the sum of values of all assets. You can also pre-define weights as a vector. For example weights <- c(0.2, 0.2, 0.6).

1.2.2 The weights of a market capitalization-weighted portfolio

In a market capitalization-weighted portfolio, the weights are given by the individual assets’ market capitalization (or market value), divided by the sum of the market capitalizations of all assets. A typical example is the S&P 500 portfolio invested in the 500 largest companies listed on the US stock exchanges (NYSE, Nasdaq). Note that by dividing by the sum of asset values across all portfolio assets, the portfolio weights sum to unity (one).

As an exercise, inspect the distribution of market capitalization based weights when the portfolio is invested in 10 stocks. For this exercise, you can use market capitalizations of 5, 8, 9, 20, 25, 100, 100, 500, 700, and 2000 million USD.

# Define marketcaps
marketcaps <- c(5, 8, 9, 20, 25, 100, 100, 500, 700, 2000)
  
# Compute the weights
weights <- marketcaps/sum(marketcaps)
  
# Inspect summary statistics
summary(weights)
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
0.001442 0.003389 0.018027 0.100000 0.115374 0.576868 
  
# Create a bar plot of weights
barplot(weights)

1.3 The portfolio return

1.3.1 Calculation of portfolio returns

For your first exercise on calculating portfolio returns, you will verify that a portfolio return can be computed as the weighted average of the portfolio component returns. In other words, this means that a portfolio return is calculated by taking the sum of simple returns multiplied by the portfolio weights. Remember that simple returns are calculated as the final value minus the initial value, divided by the initial value.

Assume that you invested in three assets. Their initial values are 1000 USD, 5000 USD, 2000 USD, respectively. Over time, the values change to 1100 USD, 4500 USD, and 3000 USD.

# Vector of initial value of the assets
in_values <- c(1000, 5000, 2000)
  
# Vector of final values of the assets
fin_values <- c(1100, 4500, 3000)

# Weights as the proportion of total value invested in each asset
weights <- in_values/sum(in_values)

# Vector of simple returns of the assets 
returns <- (fin_values - in_values)/in_values
returns
[1]  0.1 -0.1  0.5
# Compute portfolio return using the portfolio return formula
preturns <- sum(weights*returns)
preturns
[1] 0.075

Note that the weighted average portfolio return equals the percentage change in the total portfolio value. Remember that portfolio returns are calculated by taking the sum of simple returns, multiplied by the portfolio weights.

1.3.2 From simple to gross and multi-period returns

The simple return R expresses the percentage change in the value of an investment. The corresponding so-called “gross” return is defined as the future value of 1 USD invested in the asset for one period and is thus equal to 1+R.

The gross return over two periods is obtained similarly. Let R1 be the return in the first period and R2 the return in the second period. Then the end-value of a 1 USD investment is (1+R1)∗(1+R2).

The corresponding simple return over those two periods is: (1+R1)∗(1+R2)−1.

Suppose that you have an investment horizon of two periods. In the first period, you make a 10% return. But in the second period, you take a loss of 5%.

1000*((1 + 0.1)*(1 - 0.05) - 1)
[1] 45

End-value = 1045

1.3.3 The asymmetric impact of gains and losses

It is important to be aware of the fact that a positive and negative return of the same relative magnitude do not compensate each other in terms of terminal wealth. Mathematically, this can be seen from the identity (1+x)∗(1−x)=1−x2, which is less than one. A 50% loss is thus not compensated by a 50% gain. After a loss of 50%, what is the return needed to be at par again? 100%

1.4 PerformanceAnalytics

1.4.1 Buy-and-hold versus (daily) rebalancing

The choice of investment matters, even when the underlying risky assets are similar. As an example, you will now consider the stock price of Apple and Microsoft from January 2006 until the end of August 2016. The time series plot shows you the value evolution of one dollar invested in each of them. For this exercise, your portfolio approach will be to invest half of your budget in Apple stock, and the other half of your budget in Microsoft stock. Over time, the portfolio weights will change. You will have two choices as an investor. The first choice is to be passive and not trade any further. This is called a buy and hold strategy. The second choice is to buy and trade at the close of each day that results in a rebalance of the portfolio such that your portfolio is equally invested in shares of Microsoft and Apple. This is a rebalanced portfolio.

  • By investing in a portfolio of risky assets, the portfolio payoff will be in between the minimum and maximum of the payoff of the underlying risky assets.
  • By investing in a portfolio of risky assets, the risk of the portfolio payoff will be less than the maximum risk of the underlying risky assets.
  • Because the prices of Microsoft and Apple have evolved differently, the investor who spent initially half of his wealth on Microsoft and Apple ends up with a portfolio that is no longer equally invested.

1.4.2 The time series of asset returns

Calculating the returns for one period is pretty straightforward to do in R. When the returns need to be calculated for different dates the functions Return.calculate() and Return.portfolio(), in the R package PerformanceAnalytics are extremely helpful. They require the input data to be of the xts-time series class, which is pre-loaded. You will explore the functionality of the PerformanceAnalytics package in this exercise.

The object prices which contains the Apple (aapl) and Microsoft (msft) stocks is available in the workspace.

prices <- readRDS("data/aapl_msft.RData")
plot(prices$AdjClose.aapl)
lines(prices$AdjClose.msft, col = "red")

# Load package PerformanceAnalytics 
library(PerformanceAnalytics)
package 㤼㸱PerformanceAnalytics㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱PerformanceAnalytics㤼㸲

The following objects are masked _by_ 㤼㸱.GlobalEnv㤼㸲:

    prices, weights

The following object is masked from 㤼㸱package:graphics㤼㸲:

    legend
# Print the first six rows and last six rows of prices
head(prices)
           AdjClose.aapl AdjClose.msft
2006-01-03     1.0000000      1.000000
2006-01-04     1.0029432      1.004843
2006-01-05     0.9950502      1.005589
2006-01-06     1.0207357      1.002608
2006-01-09     1.0173913      1.000745
2006-01-10     1.0817391      1.005961
tail(prices)
           AdjClose.aapl AdjClose.msft
2016-08-24      11.04996      2.766979
2016-08-25      11.00291      2.777483
2016-08-26      10.93847      2.770798
2016-08-29      10.92620      2.774141
2016-08-30      10.84232      2.764114
2016-08-31      10.85255      2.743582
# Create the variable returns using Return.calculate()  
returns <- Return.calculate(prices)
  
# Print the first six rows of returns. Note that the first observation is NA because there is no prior price.
head(returns)
           AdjClose.aapl AdjClose.msft
2006-01-03            NA            NA
2006-01-04   0.002943179  0.0048434781
2006-01-05  -0.007869844  0.0007415587
2006-01-06   0.025813336 -0.0029640370
2006-01-09  -0.003276507 -0.0018580304
2006-01-10   0.063247893  0.0052122172
  
# Remove the first row of returns
returns <- returns[-1, ]
head(returns)
           AdjClose.aapl AdjClose.msft
2006-01-04   0.002943179  0.0048434781
2006-01-05  -0.007869844  0.0007415587
2006-01-06   0.025813336 -0.0029640370
2006-01-09  -0.003276507 -0.0018580304
2006-01-10   0.063247893  0.0052122172
2006-01-11   0.037595789  0.0107407490

Notice that you removed the first row of your returns because they were NA values!

1.4.3 The time series of portfolio returns

In the previous exercise, you created a variable called returns from the daily prices of stocks of Apple and Microsoft. In this exercise, you will create two portfolios using the return series you previously created. The two portfolios will differ in one way, and that is the weighting of the assets.

In the last video, you were introduced to two weighting strategies: the buy and hold strategy, and a monthly rebalancing strategy. In this exercise, you will create a portfolio in which you don’t rebalance, and one where you rebalance monthly. You will then visualize the portfolio returns of both.

You will use the function Return.portfolio() for your calculations. For this function, you will provide three arguments: R, weights, and rebalance_on. R is a time series of returns, weights is a vector containing asset weights, and rebalance_on specifies which calendar-period to rebalance on. If you need help, be sure to check the documentation.

# Create the weights
eq_weights <- c(0.5, 0.5)

# Create a portfolio using buy and hold
pf_bh <- Return.portfolio(R = returns, weights = eq_weights)

# Create a portfolio rebalancing monthly 
pf_rebal <- Return.portfolio(R = returns, weights = eq_weights, rebalance_on = "months")

# Plot the time-series
par(mfrow = c(2, 1), mar = c(2, 4, 2, 2))
plot.zoo(pf_bh)
plot.zoo(pf_rebal)

You will see based on the plots that rebalancing a portfolio can lead to less extreme returns in either direction.

1.4.4 The time series of weights

In the previous exercise, you explored the functionality of the Return.portfolio() function and created portfolios using two strategies. However, by setting the argument verbose = TRUE in Return.portfolio() you can create a list of beginning of period (BOP) and end of period (EOP) weights and values in addition to the portfolio returns, and contributions.

You can access these from the resultant list-object created from Return.portfolio(). The resultant list contains $returns, $contributions, $BOP.Weight, $EOP.Weight, $BOP.Value, and $EOP.Value.

In this exercise, you will recreate the portfolios you made in the last exercise but extend it by creating a list of calculations using verbose = TRUE. You will then visualize the end of period weights of Apple.

# Create the weights
eq_weights <- c(0.5, 0.5)

# Create a portfolio using buy and hold
pf_bh <- Return.portfolio(returns, weights = eq_weights, verbose = TRUE )

# Create a portfolio that rebalances monthly
pf_rebal <- Return.portfolio(returns, weights = eq_weights, rebalance_on = "months", verbose = TRUE  )

# Create eop_weight_bh
eop_weight_bh <- pf_bh$EOP.Weight

# Create eop_weight_rebal
eop_weight_rebal <- pf_rebal$EOP.Weight

# Plot end of period weights
par(mfrow = c(2, 1), mar=c(2, 4, 2, 2))
plot.zoo(eop_weight_bh$AdjClose.aapl)
plot.zoo(eop_weight_rebal$AdjClose.aapl)

Look at the two plots. Notice how the weights the rebalanced portfolio stay close to the initial weight of 50%, and how they buy and hold strategy has Apple stock with about 80% weight

2 Analyzing performance

2.1 Dimensions of portfolio performance

2.1.1 Exploring the monthly S&P 500 returns

For the upcoming exercises, you will examine the monthly performance of the S&P 500. A picture is worth a thousand words. This is why most analyses of performance start by studying the time series plot of the value of an investment.

A plot of the S&P 500 for the period of 1986 until August 2016 is shown to your right. Each observation on this plot is an end-of-day value. This chart shows a number of booms and busts. Take a look at the chart, do you see why the 2000s are so often called the lost decade of investing? Your job is to describe the monthly performance of the S&P 500. To do so, you will first need to aggregate daily price series to end-of-month prices. You will then calculate the monthly returns and visualize them in a table.

sp500 <- readRDS("data/sp500.RData")
sp500 <- as.xts(sp500)
plot(sp500)

# Convert the daily frequency of sp500 to monthly frequency: sp500_monthly
#sp500_monthly <- to.monthly(sp500, indexAt = "endof")

# Print the first six rows of sp500_monthly
#head(sp500_monthly)

# Create sp500_returns using Return.calculate using the closing prices
sp500_returns <- Return.calculate(sp500) 
sp500_returns <- sp500_returns[-1,]
# Time series plot
plot.zoo(sp500_returns)


# Produce the year x month table
table.CalendarReturns(sp500_returns)
ABCDEFGHIJ0123456789
 
 
Jan
<dbl>
Feb
<dbl>
Mar
<dbl>
Apr
<dbl>
May
<dbl>
Jun
<dbl>
Jul
<dbl>
Aug
<dbl>
Sep
<dbl>
19860.27.15.3-1.45.01.4-5.97.1-8.5
198713.23.72.6-1.10.64.84.83.5-2.4
19884.04.2-3.30.90.34.3-0.5-3.94.0
19897.1-2.92.15.03.5-0.88.81.6-0.7
1990-6.90.92.4-2.79.2-0.9-0.5-9.4-5.1
19914.26.72.20.03.9-4.84.52.0-1.9
1992-2.01.0-2.22.80.1-1.73.9-2.40.9
19930.71.01.9-2.52.30.1-0.53.4-1.0
19943.3-3.0-4.61.21.2-2.73.13.8-2.7
19952.43.62.72.83.62.13.20.04.0

2.1.2 The monthly mean and volatility

You have now created month to month returns for the S&P 500. Next, you will need to do a descriptive analysis of the returns.

In this exercise, you will calculate the mean (arithmetic and geometric) and volatility (standard deviation) of the returns. These returns are available in your workspace as the variable sp500_returns.

# Compute the mean monthly returns
mean(sp500_returns)
[1] 0.007320322
# Compute the geometric mean of monthly returns
mean.geometric(sp500_returns)
                  AdjClose
Geometric Mean 0.006350886
# Compute the standard deviation
sd(sp500_returns)
[1] 0.04364617

The volatility and mean return are very important metrics in every investor’s decision making!

2.2 The (annualized) Sharpe ratio

2.2.1 Excess returns and the portfolio’s Sharpe ratio

You just learned how to create descriptive statistics of your portfolio returns. Now you will learn how to evaluate your portfolio’s performance!

Performance evaluation involves comparing your investment choices with an alternative investment choice. Most often, the performance is compared with investing in an (almost) risk-free asset, such as the U.S. Treasury Bill. The return from a U.S. Treasury Bill is known as a risk-free rate because Treasury Bills (T-Bills) are backed by the U.S. Government.

In this exercise, you will be asked to annualize the risk-free rate by using the compound interest formula. The yearly compounded interest rate is given by (1+y)121. The annual rate is used to estimate a yearly return and is very useful for forecasting.

The Sharpe Ratio is an important metric that tells us the return-to-volatility ratio. It is calculated by taking the mean of excess returns (returns - risk-free rate), divided by the volatility of the returns.

#download 13 week Ttreasury Bill (ticker: ^IRX)
library(tseries)
package 㤼㸱tseries㤼㸲 was built under R version 4.0.3Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 

    㤼㸱tseries㤼㸲 version: 0.10-47

    㤼㸱tseries㤼㸲 is a package for time series analysis and computational finance.

    See 㤼㸱library(help="tseries")㤼㸲 for details.
con <- url("https://finance.yahoo.com")
if(!inherits(try(open(con), silent = TRUE), "try-error")) {
  close(con)
  tb <- get.hist.quote(instrument = "^IRX", start = "1986-01-01", end = "2016-08-31",
                      quote = "Close", compression = "d")
  plot(tb)
}
㤼㸱getSymbols㤼㸲 currently uses auto.assign=TRUE by default, but will
use auto.assign=FALSE in 0.5-0. You will still be able to use
㤼㸱loadSymbols㤼㸲 to automatically load data. getOption("getSymbols.env")
and getOption("getSymbols.auto.assign") will still be checked for
alternate defaults.

This message is shown once per session and may be disabled by setting 
options("getSymbols.warning4.0"=FALSE). See ?getSymbols for details.

^IRX 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.
time series starts 1986-01-02
time series ends   2016-08-30

tb_monthly <- to.monthly(tb, indexAt = "startof")
missing values removed from data
# monthly interest for risk free purposes
rf <- tb_monthly$tb.Close/12/100
rf <- as.xts(rf)
# Compute the annualized risk free rate
annualized_rf <- (1 + rf)^12 - 1

# Plot the annualized risk-free rate
plot(annualized_rf)


# Compute the series of excess portfolio returns 
sp500_excess <- sp500_returns - rf

# Compare the mean of sp500_excess and sp500_returns 
mean(sp500_excess)
[1] 0.004555307
mean(sp500_returns)
[1] 0.007320322
# Compute the Sharpe ratio
sp500_sharpe <- mean(sp500_excess) / sd(sp500_returns)
sp500_sharpe
[1] 0.104369

Remember that the Sharpe Ratio is calculated by taking the mean of excess returns and dividing by the volatility of those returns.

2.2.2 Annualized mean and volatility

The mean and volatility of monthly returns correspond to the average and standard deviation over a monthly investment horizon. Investors annualize those statistics to show the performance over an annual investment horizon.

To do so, the package PerformanceAnalytics has the function Return.annualized() and StdDev.annualized() to compute the (geometrically) annualized mean return and annualized standard deviation for you. Remember that the Sharpe ratio is found by taking the mean excess returns subtracted by the risk-free rate, and then divided by the volatility!

# Compute the annualized mean
Return.annualized(sp500_returns)
                    AdjClose
Annualized Return 0.07892983
# Compute the annualized standard deviation
StdDev.annualized(sp500_returns)
                               AdjClose
Annualized Standard Deviation 0.1511948
# Compute the annualized Sharpe ratio: ann_sharpe
ann_sharpe <- Return.annualized(sp500_returns) / StdDev.annualized(sp500_returns)

# Compute all of the above at once using table.AnnualizedReturns()
table.AnnualizedReturns(sp500_returns)
ABCDEFGHIJ0123456789
 
 
AdjClose
<dbl>
Annualized Return0.0789
Annualized Std Dev0.1512
Annualized Sharpe (Rf=0%)0.5220

Annualizing returns allows you to look at performance over a longer period of time.

2.3 Rolling annualized mean and volatility

In previous exercises, you have already familiarized yourself with the Return.annualized() and StdDev.annualized() functions. In this exercise, you will also use the function SharpeRatio.annualized() to calculate the annualized Sharpe Ratio. This function takes the arguments R, and Rf. The R argument takes an xts, vector, matrix, data.frame, timeSeries, or zoo object of asset returns. The Rf argument is necessary in SharpeRatio.annualized(), as it takes into account the risk-free rate in the same period of your returns. For this example, you can use the object rf to fulfill the Rf argument.

The function chart.RollingPerformance() makes it easy to visualize the rolling estimates of performance in R. Familiarize yourself first with the syntax of this function. It requires you to specify the time series of portfolio returns (by setting the argument R), the length of the window (width), and the function used to compute the performance (argument FUN). To see all three plots together, PerformanceAnalytics provides a shortcut function charts.RollingPerformance(). Note the charts instead of chart. This function creates all of the previous charts at once and does not use the argument FUN!

# Calculate the mean, volatility, and Sharpe ratio of sp500_returns
returns_ann <- Return.annualized(sp500_returns)
sd_ann <- StdDev.annualized(sp500_returns)
sharpe_ann <-SharpeRatio.annualized(sp500_returns, Rf = rf)

# Plotting the 12-month rolling annualized mean
chart.RollingPerformance(R = sp500_returns, width = 12, FUN = "Return.annualized")


# Plotting the 12-month rolling annualized standard deviation
chart.RollingPerformance(R = sp500_returns, width = 12, FUN = "StdDev.annualized")


# Plotting the 12-month rolling annualized Sharpe ratio
chart.RollingPerformance(R = sp500_returns, width = 12, FUN = "SharpeRatio.annualized", Rf = rf)

Note that the dynamics in the return, risk, and risk-adjusted returns are recurrent. Investors can thus compare current rolling risk and return statistics with past values to pinpoint the past periods in which performance has been similar.

2.3.1 Subperiod performance analysis and the function window

In the previous exercise, you computed the performance measure on each possible sample of a fixed size by rolling through time. Often investors are interested in the performance of a specific subwindow. You can create subsets of a time series in R using the function window(). The first argument is the return series that needs subsetting. The second argument is the starting date of the subset in the form “YYYY-MM-DD”, and the third argument is the ending date in the same format.

con <- url("https://finance.yahoo.com")
if(!inherits(try(open(con), silent = TRUE), "try-error")) {
  close(con)
  sp500 <- get.hist.quote(instrument = "^GSPC", start = "1986-01-01", end = "2016-08-31",
                      quote = "Close", compression = "d")
}
time series starts 1986-01-02
time series ends   2016-08-30
head(sp500)
            Close
1986-01-02 209.59
1986-01-03 210.88
1986-01-06 210.65
1986-01-07 213.80
1986-01-08 207.97
1986-01-09 206.11
sp500_returns  <- Return.calculate(sp500$Close)
sp500_returns <- sp500_returns[-1,]
# Fill in window for 2008
sp500_2008 <- window(sp500_returns, start = "2008-01-01", end = "2008-12-31")

# Create window for 2014
sp500_2014 <- window(sp500_returns, start = "2014-01-01", end = "2014-12-31")

# Plotting settings
par(mfrow = c(1, 2) , mar=c(3, 2, 2, 2))
names(sp500_2008) <- "sp500_2008"
names(sp500_2014) <- "sp500_2014"

Plot histogram of returns in 2008 using the function chart.Histogram(). Set the argument methods = c(“add.density”, “add.normal”) to visualize the non-parametric estimate of the density and the density under an assumed normal distribution.

# Plot histogram of 2008
chart.Histogram(sp500_2008, methods = c("add.density", "add.normal"))


# Plot histogram of 2014
chart.Histogram(sp500_2014, methods = c("add.density", "add.normal"))

2.4 Non-normality of the return distribution

2.4.1 Balancing risk and reward

The x-axis corresponds to the return of an asset, and the y-axis is the corresponding density, indicating how likely that return value is. Take a moment to look over these charts.

Notice that the distributions are all symmetric, and the center of the distribution corresponds to the average return.

Return distributions A and B have the same expected return. But distribution B has higher volatility and therefore has a wider range of possible outcomes. This indicates that the risk of a large deviation from the mean return is higher for investing in B than for investing in A. Distribution C has the same level of volatility as Distribution A but a higher mean return. Distribution C has the highest average return with the lowest volatility.

2.4.2 Detecting non-normality using skewness and kurtosis

Returns are most often non-normal in nature. Two metrics key to understanding the distribution of non-normal returns are skewness and kurtosis. The skewness will help you identify whether or not negative or positive returns occur more frequently. Negative skewness indicates that large negative returns occur more often than large positive ones, and vice versa.

Kurtosis will be positive if there are fat tails in your distribution. This means that large positive or negative returns will happen more often than can be assumed under a normal distribution.

sp500_daily <- sp500_returns
sp500_monthly <- to.monthly(sp500$Close, indexAt = "endof")
sp500_monthly <- CalculateReturns(sp500_monthly)
sp500_monthly <- sp500_monthly[-1, 4]
par(mfrow = c(1, 2) , mar=c(3, 2, 2, 2))
# Plot histogram of 2008
chart.Histogram(sp500_daily, methods = c("add.density", "add.normal"))
# Plot histogram of 2008
chart.Histogram(sp500_monthly, methods = c("add.density", "add.normal"))

The histograms in the plot environment compare the daily and monthly returns of the S&P 500 over the period of 1986 until today. There seems to be a negative skewness() in these plots, and a somewhat greater than normal kurtosis(). Note that, by default, kurtosis() reports the excess kurtosis (that is, the kurtosis minus three). Let’s see if the numbers match our observations!

#  Compute the skewness 
skewness(sp500_daily)
[1] -0.8254765
skewness(sp500_monthly) 
[1] -0.7919844
# Compute the excess kurtosis 
kurtosis(sp500_daily)
[1] 20.88271
kurtosis(sp500_monthly)
[1] 2.423106

The excess kurtosis can be thought of as how fat the tails of the return distribution are compared to the normal distribution.

2.4.3 Downside risk measures

The standard deviation gives equal weight to positive and negative returns in calculating the return variability. When the return distribution is asymmetric (skewed), investors use additional risk measures that focus on describing the potential losses. One such measure of downside risk is the Semi-Deviation. The Semi-Deviation is the calculation of the variability of returns below the mean return.

Another more popular measure is the so-called Value-at-Risk (or VaR). Loosely speaking, the VaR corresponds to the 5% quantile of the return distribution, meaning that a more negative return can only happen with a probability of 5%. For example you might ask: “what is the largest loss I could potentially take within the next quarter such that I only have 5% probability of observing an even larger loss?”

The expected shortfall is another measure of risk that focuses on the average loss below the 5% VaR quantile.

In this exercise, you will examine the potential risk of the monthly returns of the S&P 500. You will use the functions SemiDeviation(), VaR(), and ES(). These functions all require the argument R, which is an xts, vector, matrix, data.frame, timeSeries, or zoo object of asset returns. However the VaR(), and ES() require another argument p, or loss probability level, which is p = 0.05 in case of the 5% VaR and ES.

Use the function VaR() to estimate the 5% and 2.5% value-at-risk of a monthly investment in S&P 500 returns.

Use the function ES() to estimate the 5% and 2.5% expected shortfall of a monthly investment in S&P 500 returns.

# Calculate the SemiDeviation
SemiDeviation(sp500_monthly)
               sp500$Close.Close
Semi-Deviation        0.03339926
# Calculate the value at risk
VaR(sp500_monthly, p = 0.05)
    sp500$Close.Close
VaR       -0.07162533
VaR(sp500_monthly, p = 0.025)
    sp500$Close.Close
VaR       -0.09783902
# Calculate the expected shortfall
ES(sp500_monthly, p = 0.05)
   sp500$Close.Close
ES        -0.1180273
ES(sp500_monthly, p = 0.025)
   sp500$Close.Close
ES        -0.1653079

You now know how to calculate the potential risk of your investments! Each statistic tells you something slightly different. It is important that you remember what your risk measures are!

2.4.4 Drawdowns due to buying high, selling low

The volatility, semi-deviation, value-at-risk, and expected shortfall are all measures that describe risk over one period. These metrics do not do a great job at describing the worst-case risk of buying at a peak and selling at a trough. This sort of risk can be quantified by analyzing the portfolio’s drawdowns, or peak-to-trough decline in cumulative returns. The function table.Drawdowns() in PerformanceAnalytics reports the five largest drawdown episodes over a sample period. The package also has another function chart.Drawdown() to visualize the evolution of the drawdowns from each peak over time. You will now explore the historic drawdown episodes of the monthly returns of the S&P 500. The returns are loaded into your workspace as sp500_monthly.

# Table of drawdowns
table.Drawdowns(sp500_monthly)
ABCDEFGHIJ0123456789
From
<date>
Trough
<date>
To
<date>
Depth
<dbl>
Length
<dbl>
To Trough
<dbl>
Recovery
<dbl>
2007-11-302009-02-272013-03-28-0.5256651649
2000-09-292002-09-302007-05-31-0.4628812556
1987-09-301987-11-301989-07-31-0.301723320
1990-06-291990-10-311991-02-28-0.1584954
1998-07-311998-08-311998-11-30-0.1557523

# Plot of drawdowns
chart.Drawdown(sp500_monthly)

3 Performance drivers

3.1 Drivers in the case of two assets

3.1.1 Driver 1: The assets’ individual performance

three types of drivers for a portfolio’s performance: (i) the individual performance of the assets in terms of risk and return, (ii) the portfolio weight of each of the assets, (iii) the correlation between the asset returns.

Now let’s analyze some data to get a better grasp of this result! In this example, you will consider investing – with monthly frequency – in U.S. equities and U.S. bonds. Each assets’ returns are stored in your workspace as returns_equities, and returns_bonds. In addition, the portfolio is invested 60/40, meaning that every month you invest 60% in equities and 40% bonds. These portfolio returns are stored as returns_6040.

eq_prices <- readRDS("data/eq_prices.RData")
head(eq_prices)
           AdjClose
2006-07-03 1.000000
2006-08-01 1.021822
2006-09-01 1.049413
2006-10-02 1.082487
2006-11-01 1.104013
2006-12-01 1.118775
bond_prices <- readRDS("data/bond_prices.RData")
head(bond_prices)
           AdjClose
2006-07-03 1.000000
2006-08-01 1.015910
2006-09-01 1.026186
2006-10-02 1.033223
2006-11-01 1.044195
2006-12-01 1.037925
returns <- merge(eq_prices, bond_prices)
weights <- c(0.60, 0.40)
portfolio <- eq_prices * 0.60 + bond_prices *0.40
head(portfolio)
           AdjClose
2006-07-03 1.000000
2006-08-01 1.019457
2006-09-01 1.040122
2006-10-02 1.062781
2006-11-01 1.080086
2006-12-01 1.086435
plot(eq_prices)
lines(bond_prices, col = "red")
lines(as.zoo(portfolio), col = "blue")
legend(x = "topleft", legend = c("Equities", "Bonds", "60/40 Portfolio"),
       col = c("black", "red", "blue"),
       lty = c(1, 1, 1))

The portfolio mean return equals the position weighted sum of the component mean returns. You can test this using: all.equal(mean(returns_6040), 0.6 * mean(returns_equities) + 0.4 * mean(returns_bonds))

3.1.2 Driver 2: The choice of portfolio weights

Investors can optimize the choice of weight to obtain the highest risk-adjusted return, as measured by the portfolio Sharpe ratio.

In the special case of investing the total portfolio value in only two assets, there is only one weight to determine, because the weight on the second asset equals one minus the weight of the first asset.

Let us do this in the case of a portfolio invested in U.S. equities and U.S. bonds. We will be using the brute force approach of trying a large number of possible weights and keeping the weight that yields the highest value for the portfolio Sharpe ratio (assuming a zero risk-free rate).

Create a vector called grid using seq() that begins at 0, ends at 1, by an increment of 0.01. Initialize empty vector vsharpe with the same length as grid. A popular way of doing this is by creating a vector that contains NA’s using function rep(). You will replace these NA’s in the loop that you will create next. In the for loop, you will compute the Sharpe ratio for each of the possible weights in grid. The first command in the for-loop selects the i-th element of grid and stores it in object weight, which changes in each iteration. You want to see how the portfolio return changes with a changing weight. Create an object preturns that equals the sum of weight times returns_equities, and (1-weight) times returns_bonds. Next, you will replace the NAs in vsharpe with the annualized Sharpe ratio (SharpeRatio.annualized()) of preturns. Fill in the plot function where potential weights (grid) is plotted on the x-axis and the Sharpe ratios on the y-axis.

returns_equities <- Return.calculate(eq_prices)
returns_bonds <- Return.calculate(bond_prices)
# Create a grid
grid <- seq(from = 0, to = 1, by = 0.01)

# Initialize an empty vector for Sharpe ratios
vsharpe <- rep(NA, times = length(grid))

# Create a for loop to calculate Sharpe ratios
for(i in 1:length(grid)) {
    weight <- grid[i]
    preturns <- weight * returns_equities + (1 - weight) * returns_bonds
    vsharpe[i] <- SharpeRatio.annualized(preturns)
}

# Plot weights and Sharpe ratio
plot(grid, vsharpe, xlab = "Weights", ylab= "Ann. Sharpe ratio")
abline(v = grid[vsharpe == max(vsharpe)], lty = 3)

The portfolio that is 11% invested in equities, 89% in bonds has the largest possible Sharpe ratio.

3.1.3 Driver 3: The correlation between the asset returns

The third driver of portfolio performance is the correlation between asset returns. Generally speaking, the correlation tells you how two asset returns tend to move together.

The correlation of assets has important consequences in overall portfolio performance. This correlation is important because it can reduce volatility through diversification, or reducing overall correlation. In fact, the lower the correlation, the more successful the portfolio tends to be in regards to partially offsetting large losses in one asset with only a minor loss, or even a gain, in another asset.

In the extreme case of two identical asset returns, the correlation will be 1, and there is no diversification potential. In the other extreme case where, if one asset return is above average, and the other is almost always below average, the correlation is negative. The correlation is 0 when the asset returns are linearly independent of each other. Note that interdependency can still exist on a non-linear level even when the correlation is 0. As an exercise, suppose you have an equally weighted portfolio of two assets. Their correlation jumps from 0 to 0.5. What happens with the variance?

returns_equities <- Return.calculate(eq_prices)
returns_equities <- returns_equities[-1,]
returns_bonds <- Return.calculate(bond_prices)
returns_bonds <- returns_bonds[-1,]

pf_var <- function (x){
    return((0.5^2) * var(returns_equities) + (0.5^2) * var(returns_bonds) + 
        2 * 0.5 * 0.5 * sd(returns_equities) * sd(returns_bonds) * x)
}

function pf_var(x) that calculates the portfolio variance for the equally weighted portfolio of equities and bonds, when their correlation is assumed to be equal to x. Having correlated assets can increase potential risk. Correlated asset prices rise and fall together. So when you suffer a loss in one asset, there is a greater chance of suffering a loss on the other asset!

pf_var(0)
[1] 0.0005106223
pf_var(0.5)
[1] 0.0006317595

3.1.4 Interpreting correlation

Now you will learn how to compute the correlation between bond returns and equity returns. Just like volatilities, these correlations are dynamic. Therefore you need to distinguish between a static analysis that calculates correlations over a complete sample and a dynamic analysis that calculates correlations over a rolling sample. This is a similar analysis as you did for the time-varying performance evaluation in terms of mean return and volatility.

In this exercise you will learn 3 new functions from the PerformanceAnalytics package: chart.Scatter(), chart.Correlation(), and chart.RollingCorrelation().

# Create a scatter plot
chart.Scatter(returns_bonds, returns_equities, xlab = "bond returns", ylab = "equity returns", main = "bond-equity returns")


# Find the correlation
cor(returns_bonds, returns_equities)
[1] 0.06211472
# Merge returns_bonds and returns_equities 
returns <- merge(returns_bonds, returns_equities)

# Find and visualize the correlation using chart.Correlation
chart.Correlation(returns) 


# Visualize the rolling estimates using chart.RollingCorrelation
chart.RollingCorrelation(returns_bonds, returns_equities, width = 24)

3.2 Using matrix notation

3.2.1 Making a risk-reward scatter diagram

In this example, you have decided to extend your investment opportunity by creating a portfolio that consists of US equities ETF (SPY), US bonds ETF (AGG), a real estate investment trust (VNQ), and an ETF tracking in the GSCI commodities index (GSG). The plot in the environment displays the performance of these investments.

comm_prices <- readRDS("data/comm_prices.RData")
re_prices <- readRDS("data/re_prices.RData")
plot(eq_prices)
lines(bond_prices, col = "red")
lines(re_prices, col = "blue")
lines(comm_prices, col = "purple")
legend(x = "topleft", legend = c("Equities", "Bonds", "Real estate", "Commodities"),
       col = c("black", "red", "blue", "purple"),
       lty = c(1, 1, 1, 1))

You can also visualize “relative” attractiveness of the investments by making a scatter plot of the average returns against the portfolio volatilities. In order to do this, you need to calculate the averages and volatilities for each asset. This corresponds to each column in the return series returns.

These calculations are made easy by using the function apply() with the first argument as the return data, the second argument is the value of 2 indicating that the calculation should be column-wise, while the third argument is the name of the function that needs to be applied on each column.

eq_returns <- Return.calculate(eq_prices)
eq_returns <- eq_returns[-1,]
bond_returns <- Return.calculate(bond_prices)
bond_returns <- bond_returns[-1,]
comm_returns <- Return.calculate(comm_prices)
comm_returns <- comm_returns[-1,]
re_returns <- Return.calculate(re_prices)
re_returns <- re_returns[-1,]
returns <- merge(eq_returns, bond_returns, comm_returns, re_returns)
colnames(returns) <- c("Equities", "Bonds", "Commodities", "Real Estate")
# Create a vector of returns 
means <- apply(returns, 2, "mean")
  
# Create a vector of standard deviation
sds <- apply(returns, 2, "sd")

# Create a scatter plot
plot(sds, means)
text(sds, means, labels = colnames(returns), cex = 0.7)
abline(h = 0, lty = 3)

3.2.2 The covariance matrix

The covariance matrix is crucial in determining the portfolio variance in the general case of N assets. Remember that an element on row i and column j correspond to the covariance of the i th and j th return. Recall also that the covariance of two return series is the product between their volatilities and their correlation, and that the covariance of an asset return with itself is its variance.

In this exercise, you will compute and analyze the covariance and correlation matrix on the monthly returns of the four asset classes from the previous exercise. To refresh, these asset classes are equities, bonds, real estate, and commodities. To create these matrices, you will use the standard functions cov() and cor().

In your workspace are the monthly investments as returns, and the vector of standard deviations sds that you created previously. Create a diagonal matrix that contains the variances on the diagonal. You can use the function diag() to do this, using a squared sds^2 as the only argument. Call this diag_cov.

sds <- c(sd(returns[,1]), sd(returns[,2]), sd(returns[,3]), sd(returns[,4]))
# Create a matrix with variances on the diagonal
diag_cov <- diag(sds^2)
diag_cov
            [,1]         [,2]        [,3]        [,4]
[1,] 0.001920218 0.0000000000 0.000000000 0.000000000
[2,] 0.000000000 0.0001222713 0.000000000 0.000000000
[3,] 0.000000000 0.0000000000 0.004797972 0.000000000
[4,] 0.000000000 0.0000000000 0.000000000 0.005447762
# Create a covariance matrix of returns
cov_matrix <- cov(returns)
cov_matrix
                Equities         Bonds   Commodities  Real Estate
Equities    1.920218e-03  3.009761e-05  1.510994e-03 0.0024063778
Bonds       3.009761e-05  1.222713e-04 -6.860821e-05 0.0002632232
Commodities 1.510994e-03 -6.860821e-05  4.797972e-03 0.0013291423
Real Estate 2.406378e-03  2.632232e-04  1.329142e-03 0.0054477625
# Create a correlation matrix of returns
cor_matrix <- cor(returns)
cor_matrix
              Equities       Bonds Commodities Real Estate
Equities    1.00000000  0.06211472  0.49780426   0.7440112
Bonds       0.06211472  1.00000000 -0.08957463   0.3225172
Commodities 0.49780426 -0.08957463  1.00000000   0.2599762
Real Estate 0.74401117  0.32251718  0.25997618   1.0000000
# Verify covariances equal the product of standard deviations and correlation
all.equal(cov_matrix[1,2], cor_matrix[1,2] * sds[1] * sds[2])
[1] TRUE

These matrices can help you in your calculations, and are also a good reference to see the relationship between your assets.

3.2.3 Matrix-based calculation of portfolio mean and variance

When w is the column-matrix of portfolio weights, μ the column-matrix of expected returns, and Σ the return covariance matrix. Then the portfolio expected return is wμ, and the portfolio variance is wΣw. Remember that the portfolio’s volatility is the square root of its variance.

You will practice matrix multiplication in R using the %*% function, instead of the standard *. In addition, you will transpose the matrices by using the standard function t(). Remember that transposing a matrix is simply changing the rows of the matrix to the columns.

The weights, vector of means, and the covariance matrix are pre-loaded in your workspace as weights, vmeans, and sigma, respectively.

weights <- c(0.4, 0.4, 0.1, 0.1)
vmeans <- c(c(equities = 0.00706924247456836, bonds = 0.00400950651113212, 
realestate = 0.00894954808678432, commodities = -0.0082960697150902
))
sigma <- matrix(c(0.00192021805592667, 3.00976104465011e-05, 0.00240637780809405, 
0.00151099397208086, 3.00976104465011e-05, 0.000122271264776651, 
0.000263223185975344, -6.86082072578803e-05, 0.00240637780809405, 
0.000263223185975344, 0.00544776248878884, 0.0013291423299705, 
0.00151099397208086, -6.86082072578803e-05, 0.0013291423299705, 
0.00479797179115701), ncol = 4)
# Create a weight matrix w
w <- as.matrix(weights)

# Create a matrix of returns
mu <- as.matrix(vmeans)

# Calculate portfolio mean monthly returns
t(w) %*% mu

# Calculate portfolio volatility
sqrt(t(w) %*% sigma %*% w)

3.3 Portfolio risk budget

3.3.1 Who did it?

You saw the difference between a capital allocation budget and a risk budget. In this exercise, you will construct a risk budget, and discover how large each asset’s percent risk contribution is in the total portfolio volatility. For this last exercise, you will calculate the risk contributions for a portfolio that is again invested 40% in equities, 40% in bonds, 10% in real estate, and 10% in commodities. The function StdDev() plays an important role in this exercise. The StdDev() function creates a list of the assets’ standard deviation ($StdDev), their risk contribution ($contribution), and their percent risk contribution ($pct_contrib_StdDev). You will be using three arguments in the StdDev() function to do this calculation. The first is R, a vector, matrix, data frame, time series, or zoo object of returns. The second is portfolio_method, which you will set to component, and the third is weights.

# Create portfolio weights
weights <- c(0.4, 0.4, 0.1, 0.1)

# Create volatility budget
vol_budget <- StdDev(returns, portfolio_method = "component", weights = weights)

# Make a table of weights and risk contribution
weights_percrisk <- cbind(weights, vol_budget$pct_contrib_StdDev)
colnames(weights_percrisk) <- c("weights", "perc vol contrib")

# Print the table
weights_percrisk
            weights perc vol contrib
Equities        0.4       0.59004087
Bonds           0.4       0.04048648
Commodities     0.1       0.14975111
Real Estate     0.1       0.21972154

Note that the percentage volatility risk caused by the bonds is much less than their portfolio weight.

4 Optimizing the portfolio

4.1 Modern portfolio theory of Harry Markowitz

Mean-variance based investing in DJIA stocks In this chapter, you will explore portfolio optimization. For the upcoming exercises, you will focus on the Dow Jones Industrial Average (DJIA) stocks. Like the S&P 500 portfolio studied in Chapter 2, the Dow Jones Industrial Average portfolio is an important reference portfolio for the U.S. equity market. It is invested in 30 large publicly owned companies based in the United States.

You will be using the sample of monthly returns from January 1991 until December 2015. These return data are available in the workspace as the variable returns.

Assume you are an investor who likes high returns and dislikes risk. You are also constrained to be fully invested in the 30 DJIA stocks, and only positive weights are allowed. Which of the following statements is false:

djia <- readRDS("data/prices.rds")
head(djia)
                 AA     AAPL      AXP       BA      BAC      CAT      CVX        DD      DIS       GE
1990-12-31 4.534478 1.345204 3.445906 13.77601 2.740819 3.259666 7.440307  7.680170 6.428398 2.355960
1991-01-31 5.115716 1.736251 3.800936 14.99043 3.369860 3.473104 7.312247  7.575678 6.866336 2.627997
1991-02-28 5.095843 1.794772 4.009782 14.71930 3.504654 3.813431 7.795698  7.953312 7.825717 2.812929
1991-03-31 5.205114 2.131782 4.824266 14.33797 4.199830 3.342211 7.976993  7.847620 7.548209 2.880146
1991-04-30 5.399917 1.724235 4.210390 13.95664 4.472743 3.346168 8.102942  8.798847 7.313112 2.926684
1991-05-31 5.689909 1.477213 4.315652 15.02803 5.109541 3.635996 7.736412 10.086981 7.368695 3.217015
                 HD      HPQ     INTC      IBM      JNJ      JPM       KO      MCD      MMM      MRK
1990-12-31 2.042869 1.004518 0.841387 18.27009 5.103177 1.651136 3.398343 4.583788 10.70052 6.468881
1991-01-31 2.287483 1.225118 0.999835 20.49322 5.441018 2.015923 3.562779 4.485423 10.59133 6.585841
1991-02-28 2.578379 1.469351 1.043544 21.01702 5.866060 2.534304 3.827706 4.977246 11.13890 7.350589
1991-03-31 2.857695 1.579714 1.021690 18.58884 6.848207 2.706823 3.983028 5.483410 11.13890 7.635325
1991-04-30 3.042916 1.615256 1.076322 16.81362 6.749990 3.193658 3.872898 5.286165 11.21757 7.798163
1991-05-31 3.486123 1.713990 1.218378 17.52841 6.501354 3.329975 4.203288 5.537626 12.09569 8.603312
               MSFT      NKE      PFE       PG      TRV      UTX       VZ      WMT      XOM        T
1990-12-31 0.723928 1.024851 1.674406 5.991295 7.683583 3.471237 7.835626 5.455634 6.233811 4.643250
1991-01-31 0.943993 1.215822 1.863621 5.515581 7.806031 3.444045 7.167901 5.951600 6.218753 4.387233
1991-02-28 0.998104 1.222185 2.191377 5.654775 8.112150 3.641321 7.241796 6.379935 6.728324 4.513183
1991-03-31 1.020955 1.174849 2.233116 5.924462 8.607386 3.549830 7.592801 6.996564 7.140262 4.712602
1991-04-30 0.952409 1.209873 2.274859 5.871020 8.715557 3.366849 7.480882 7.312538 7.262318 4.539377
1991-05-31 1.055830 1.012474 2.471658 5.958511 8.004714 3.466449 7.050730 7.741360 7.191589 4.401176
returns <- Return.calculate(djia)
returns <- returns[-1,]
# Create a vector of returns 
means <- apply(returns, 2, "mean")
  
# Create a vector of standard deviation
sds <- apply(returns, 2, "sd")

# Create a scatter plot
plot(sds, means)
text(sds, means, labels = colnames(returns), cex = 0.7)
abline(h = 0, lty = 3)

You would rather invest all of your capital in Home Depot (ticker: HD) than in Caterpillar (ticker: CAT) because Home Depot has a higher average return and lower risk.

By combining investments, it may be possible to obtain a portfolio with lower variance than the variance of the Exxon Mobil Corporation’s stock (ticker: XOM).

4.1.1 Exploring monthly returns of the 30 DJIA stocks

The 1991-2015 monthly returns on the 30 DJIA stocks are available in the workspace as the variable returns. This exercise will help you get comfortable with the data that you will be using for the remainder of the exercises.

Recall that if we compute the means column by column using colMeans(returns), or apply(returns, 2, “mean”), you then obtain the average return per asset. In this exercise, you will be computing the mean per row. You can do this similarly using the function rowMeans() and supplying the argument 1 instead of two to indicate row-wise calculations in the apply() function. By doing so, you obtain the time series of returns for the equally weighted portfolio.

# Verify the class of returns 
class(returns)
[1] "xts" "zoo"
# Investigate the dimensions of returns
dim(returns)
[1] 300  30
# Create a vector of row means
ew_preturns <- rowMeans(returns)

# Cast the numeric vector back to an xts object
ew_preturns <- xts(ew_preturns, order.by = time(returns))

# Plot ew_preturns
plot.zoo(ew_preturns)

The chart you created visualizes the returns for an equally weighted portfolio.

4.1.2 Finding the mean-variance efficient portfolio

A mean-variance efficient portfolio can be obtained as the solution of minimizing the portfolio variance under the constraint that the portfolio expected return equals a target return. A convenient R function for doing so is the function portfolio.optim() in the R package tseries. Its default implementation finds the mean-variance efficient portfolio weights under the constraint that the portfolio return equals the return on the equally-weighted portfolio. The only argument needed is the monthly return data on the portfolio components for which the weights need to be determined.

The variable returns containing the monthly returns of the DJIA stocks is already loaded in the console.

Create a mean-variance efficient portfolio of monthly returns using the default of portfolio.optim() targeting the equally-weighted portfolio return, and assign the output to the variable opt.

# Load tseries
library(tseries)

# Create an optimized portfolio of returns
opt <- portfolio.optim(returns)

# Create pf_weights
pf_weights <- opt$pw

# Assign asset names
names(pf_weights) <- colnames(returns)

# Select optimum weights opt_weights
opt_weights <- pf_weights[pf_weights >= 0.01]

# Bar plot of opt_weights
barplot(opt_weights)


# Print expected portfolio return and volatility
opt$pm
[1] 0.01249652
opt$ps
[1] 0.0355865

The portfolio you created has an average return of 1.2%!

4.1.3 Effect of the return target

This exercise will show the effect of increasing your target return on the volatility of your mean-variance efficient portfolio.

The function portfolio.optim has arguments that allow for more general specifications. The arguments are as follows:

portfolio.optim(x, pm = mean(x), shorts = FALSE, reshigh = NULL)

The argument pm sets the target return, the argument reshigh specifies the upper constraints on the portfolio weights, and the argument shorts is a logical statement specifying whether negative weights are forbidden or not, by default shorts = FALSE.

You will create a portfolio that is optimized for a target return that equals the average value of the return series returns. Then you will create a portfolio that has a target return that is 10% higher than the mean return series and calculate the proportion change in risk.

# Create portfolio with target return of average returns 
pf_mean <- portfolio.optim(returns, pm = mean(returns))

# Create portfolio with target return 10% greater than average returns
pf_10plus <- portfolio.optim(returns, pm = 1.1 * mean(returns))

# Print the standard deviations of both portfolios
print(pf_mean$ps)
[1] 0.0355865
print(pf_10plus$ps)
[1] 0.03842654
# Calculate the proportion increase in standard deviation
(pf_10plus$ps - pf_mean$ps) / (pf_mean$ps)
[1] 0.07980684

By increasing your target return, your portfolio’s volatility increased by 8%!

4.1.4 Imposing weight constraints

Investors are often constrained by the maximum values allowed for the portfolio weights. These constraints can actually be an advantage. The advantage of a maximum weight constraint is that the subsequent portfolio will be less concentrated in certain assets. There is a disadvantage to this, though. The disadvantage is that the same target return may no longer be possible or will be obtained at the expense of higher volatility.

Remember from the previous exercise that the function portfolio.optim() allows you to set weight constraints within the reshigh argument. reshigh requires a vector of maximum weights for each asset.

In this exercise, you will create three portfolios with different maximum weight constraints. For this exercise, it is important to know the output of the portfolio.optim() function. This function creates a list containing four components: (i) $pw: the portfolio weights, (ii) $px: the returns of the overall portfolio, (iii) $pm: the expected return portfolio, (iv) $ps: the standard deviation of the portfolio returns. Create three vectors of maximum weights for each asset (column) in returns using the rep() function. The first vector will contain maximum weights of 100%, the second 10%, and the third 5%. Call these max_weights1, max_weights2, max_weights3, respectively.

# Create vectors of maximum weights
max_weights1 <- rep(1, ncol(returns))
max_weights2 <- rep(0.1, ncol(returns))
max_weights3 <- rep(0.05, ncol(returns))

# Create an optimum portfolio with max weights of 100%
opt1 <- portfolio.optim(returns, reshigh = max_weights1)

# Create an optimum portfolio with max weights of 10%
opt2 <- portfolio.optim(returns, reshigh = max_weights2)

# Create an optimum portfolio with max weights of 5%
opt3 <- portfolio.optim(returns, reshigh = max_weights3)

# Calculate how many assets have a weight that is greater than 1% for each portfolio
sum(opt1$pw > .01)
[1] 15
sum(opt2$pw > .01)
[1] 17
sum(opt3$pw > .01)
[1] 22
# Print portfolio volatilites 
opt1$ps
[1] 0.0355865
opt2$ps
[1] 0.03616089
opt3$ps
[1] 0.03798736

By imposing weight constraints you can ensure that your portfolio isn’t completely dominated by just a few assets.

4.2 The efficient frontier

4.2.1 Computing the efficient frontier using a grid of target returns

As you have seen, one approach to compute the efficient frontier is to first define the grid of target returns and then, for each target return, find the portfolio that has an expected return equal to the target return at the lowest possible variance.

But what is a reasonable grid of target returns? You will set the maximum target return to the maximum average return of the stocks. Ideally, you would set the minimum target return to the return of the minimum variance portfolio. Because you don’t know this minimum variance portfolio return yet, you create a grid using the minimum of average returns from all stocks.

In this exercise, you will use a for loop to calculate your grid of potential portfolio means, deviations, and weights.

# Calculate each stocks mean returns
stockmu <- colMeans(returns)

# Create a grid of target values
grid <- seq(from = 0.01, to = max(stockmu), length.out = 50)

# Create empty vectors to store means and deviations
vpm <- vpsd <- rep(NA, length(grid))

# Create an empty matrix to store weights
mweights <- matrix(NA, 50, 30)

# Create your for loop
for(i in 1:length(grid)) {
  opt <- portfolio.optim(x = returns, pm = grid[i])
  vpm[i] <- opt$pm
  vpsd[i] <- opt$ps
  mweights[i, ] <- opt$pw
}

The for loop that you just created is extremely helpful in being able to explore how target returns can affect weights, volatility, and portfolio returns.

4.2.2 Interpreting the efficient frontier

efficient frontier

The portfolio with the greatest excess return per unit of portfolio volatility is the tangency portfolio.

4.2.3 Properties of the efficient frontier

The curve shown in the previous plot shows the solution of minimizing the variance under the constraint that the portfolio expected return equals the target return. When the target return is below the minimum variance return, the obtained portfolio is not efficient because a higher return at lower risk can be obtained by investing in the minimum variance portfolio. The optimized portfolios often are also not realistic because they tend to invest large weights in only a few assets. A practical solution to avoid this is to impose weight constraints.

The effect of such a weight constraint is shown in the workspace, where you can see the efficient frontier plots obtained for the DJIA stocks under a maximum 100% weight constraint (black line), a maximum 10% weight constraint (red line) and a maximum 5% weight constraint (blue).

  • Imposing a more strict weight constraint leads to a shift to the right of the efficient frontier.
  • The portfolios on the dashed lines are not mean-variance efficient.

4.2.4 The minimum variance and maximum Sharpe ratio portfolio

In the previous exercises, you computed the efficient frontier using a grid of target returns. The output of your calculation of the efficient frontier was a series of two vectors, vpm (vector of portfolio means), and vpsd (vector of standard deviations, or volatilities), and a matrix of weights called mweights. You will use these outputs to identify the portfolios with the least volatility, and the greatest Sharpe ratio, and then plot their weight allocation.

As a reminder, the Sharpe Ratio is found by taking the excess returns less than the risk-free rate, divided by the portfolio volatility.

colnames(mweights) <- colnames(returns)
# Create weights_minvar as the portfolio with the least risk
weights_minvar <- mweights[vpsd == min(vpsd), ]

# Calculate the Sharpe ratio
vsr <- (vpm - 0.0075) / vpsd

# Create weights_max_sr as the portfolio with the maximum Sharpe ratio
weights_max_sr <- mweights[vsr == max(vsr)]

# Create bar plot of weights_minvar and weights_max_sr
par(mfrow = c(2, 1), mar = c(3, 2, 2, 1))
barplot(weights_minvar[weights_minvar > 0.01])
barplot(weights_max_sr[weights_max_sr > 0.01])

4.3 In-sample vs. out-of-sample evaluation

4.3.1 Split-sample evaluation

In chapter 2, you used the function window() to subset your returns for graphical purposes. In this exercise, you will use the window() to create two samples: an estimation sample and an evaluation sample. This exercise will illustrate how portfolio weights can differ when changing the estimation window.

To remind you, the function window() has argument of x, start, and end. Where start and end are in the format “YYYY-MM-DD”.

# Create returns_estim 
returns_estim <- window(returns, start = "1991-01-01", end = "2003-12-31")

# Create returns_eval
returns_eval <- window(returns, start = "2004-01-01", end = "2015-12-31")

# Create vector of max weights
max_weights <- rep(0.1, ncol(returns))

# Create portfolio with estimation sample 
pf_estim <- portfolio.optim(returns_estim, reshigh = max_weights)

# Create portfolio with evaluation sample
pf_eval <- portfolio.optim(returns_eval, reshigh = max_weights)

# Create a scatter plot with evaluation portfolio weights on the vertical axis
plot(pf_estim$pw, pf_eval$pw)
abline(a = 0, b = 1, lty = 3)

If portfolio weights are identical, they should be on the 45-degree line. By creating two samples using the window() function you can backtest, or evaluate the performance of a portfolio on historical data!

4.3.2 Out of sample performance evaluation

This example will illustrate how your returns can change based on the weighting created by an optimized portfolio. You will use the estimation portfolio (pf_estim) to evaluate the performance of your portfolio on the estimation sample of returns (returns_eval).

How severe is the optimality loss? Let’s compare, for the portfolio weights in pf_estim, the performance you expected using the evaluation sample (returns_estim) with the actual return on the out-of-sample period (returns_eval).

Calculate the returns of the portfolio with monthly rebalance weights pf_estim$pw on the estimation sample returns_estim. Call this returns_pf_estim

# Create returns_pf_estim
returns_pf_estim <- Return.portfolio(returns_estim, pf_estim$pw, rebalance_on = "months")


# Create returns_pf_eval
returns_pf_eval <- Return.portfolio(returns_eval, pf_estim$pw, rebalance_on = "months")

# Print a table for your estimation portfolio
table.AnnualizedReturns(returns_pf_estim)
ABCDEFGHIJ0123456789
 
 
portfolio.returns
<dbl>
Annualized Return0.1940
Annualized Std Dev0.1325
Annualized Sharpe (Rf=0%)1.4634

# Print a table for your evaluation portfolio
table.AnnualizedReturns(returns_pf_eval)
ABCDEFGHIJ0123456789
 
 
portfolio.returns
<dbl>
Annualized Return0.0864
Annualized Std Dev0.1242
Annualized Sharpe (Rf=0%)0.6954

The results from the pf_eval are what you may expect in a real performance. You have now successfully evaluated a split sample, this wouldn’t be possible without the use of historical data

4.3.3 It ain’t over

This journey showed you how to compute returns, analyze portfolio performance, and optimize portfolios. These are important skills since whenever you hold or manage more than one asset, you have a portfolio to consider.

It is now time to compound and get an extra return by exploring other useful R packages for portfolio analysis like PortfolioAnalytics. Instead of optimizing the portfolio by minimizing the variance under a return target constraint, this package allows you to optimize virtually all objective functions under all possible constraints. Even if the solution to the problem is not feasible, it will give you the best possible solution using heuristics, such as differential evolution in the R package DEoptim.

LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBQb3J0Zm9saW8gQW5hbHlzaXMgaW4gUiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfY29sbGFwc2VkOiBmYWxzZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIA0KdG9jX2RlcHRoOiAzDQotLS0NCiMgVGhlIGJ1aWxkaW5nIGJsb2Nrcw0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KIyMjIEdldHRpbmcgYSBncmFzcCBvZiB0aGUgYmFzaWNzDQoNClRoZSBnb2FsIG9mIGV2ZXJ5IGludmVzdG9yIGlzIHRvIG1ha2UgcHJvZml0cyB3aGlsZSBsaW1pdGluZyB0aGUgYW1vdW50IG9mIHJpc2sgaW52b2x2ZWQuIEludmVzdG9ycyBjcmVhdGUgc3RyYXRlZ2llcyB0byByZWR1Y2UgdGhlc2Ugcmlza3MuDQpEaXZlcnNpZmllZCBwb3J0Zm9saW9zIHJlZHVjZSByaXNrIGJ5IG9mZnNldHRpbmcgbG9zcyB3aXRoIGEgcG90ZW50aWFsIGdhaW4gaW4gYW5vdGhlciBhc3NldCENCg0KIyMjIEdldCBhIGZlZWwgZm9yIHRoZSBkYXRhDQoNClRoZSBjaG9pY2Ugb2YgaW52ZXN0bWVudCBtYXR0ZXJzIGV2ZW4gd2hlbiB0aGUgdW5kZXJseWluZyByaXNreSBhc3NldHMgYXJlIHNpbWlsYXIuIEFzIGEgZmlyc3QgZXhhbXBsZSwgbGV0IHVzIGNvbnNpZGVyIHRoZSBzdG9jayBwcmljZSBvZiB0aGUgQ29jYSBDb2xhIENvbXBhbnkgYW5kIHRoZSBQZXBzaUNvIGNvbXBhbnkgZnJvbSBKYW51YXJ5IDIwMDMgdW50aWwgdGhlIGVuZCBvZiBBdWd1c3QgMjAxNi4NCg0KVGhlIHRpbWUgc2VyaWVzIHBsb3Qgc2hvd3MgeW91IHRoZSB2YWx1ZSBldm9sdXRpb24gb2Ygb25lIGRvbGxhciBpbnZlc3RlZCBpbiBlYWNoIGNvbXBhbnkuIEFzIGFuIGV4ZXJjaXNlLCBwbG90IHRoZSB0aW1lIHNlcmllcyBzaG93aW5nIHRoZSByZWxhdGl2ZSB2YWx1ZSBvZiBhbiBpbnZlc3RtZW50IGluIHRoZSBDb2NhIENvbGEgY29tcGFueSwgY29tcGFyZWQgdG8gdGhlIHZhbHVlIG9mIGFuIGludmVzdG1lbnQgaW4gUGVwc2lDby4gVG8gZG8gdGhpcyBleGVyY2lzZSwgeW91IGNhbiB1c2UgdGhlIGNvcnJlc3BvbmRpbmcgcHJpY2Ugc2VyaWVzLCBhdmFpbGFibGUgYXMgdGhlIHZhcmlhYmxlcyBrbyBhbmQgcGVwIGluIHlvdXIgd29ya3NwYWNlLg0KDQpUaHJlZSBpbXBvcnRhbnQgcGFja2FnZXMgdGhhdCB5b3Ugd2lsbCB1c2UgaW4gdGhpcyBjb3Vyc2UgYXJlIHRoZSB4dHMgYW5kIHpvbyBwYWNrYWdlcyAodGhlc2UgYXJlIHBvcHVsYXIgd2hlbiB3b3JraW5nIHdpdGggdGltZSBzZXJpZXMgZGF0YSksIGFuZCB0aGUgUGVyZm9ybWFuY2VBbmFseXRpY3MgcGFja2FnZS4NCkRlZmluZSBrb19wZXAgYXMgdGhlIHJhdGlvIGV4cHJlc3NpbmcgdGhlIHZhbHVlIG9mIHRoZSBzaGFyZSBwcmljZSBvZiB0aGUgQ29jYSBDb2xhIGNvbXBhbnkgaW4gdGVybXMgb2YgdGhlIHNoYXJlIHByaWNlIG9mIFBlcHNpQ28uDQoNClVzZSBwbG90LnpvbygpIHRvIHZpc3VhbGl6ZSB0aGUgdmFyaWF0aW9uIGluIHRoaXMgcmF0aW8gb3ZlciB0aW1lLiBOb3RlIHRoYXQgd2hlbiB0aGUgdmFsdWUgb2YgdGhlIHJhdGlvIGlzIGxhcmdlciB0aGFuIDEsIHRoZSBwZXJmb3JtYW5jZSBvZiBrbyBzaW5jZSBKYW51YXJ5IDIwMDMgaXMgaGlnaGVyIHRoYW4gdGhhdCBvZiBwZXAuDQoNCkFzIGEgcmVmZXJlbmNlIGxpbmUsIHVzZSBhYmxpbmUoKSB0byBpbmNsdWRlIGEgaG9yaXpvbnRhbCBsaW5lIGF0IGg9MS4gTm90ZSB0aGF0IHdoZXJlIHRoZSB2YWx1ZSBvZiB0aGUgcmF0aW8gaXMgbGFyZ2VyIHRoYW4gb25lLCB0aGUgQ29jYSBDb2xhIENvbXBhbnkgb3V0cGVyZm9ybXMgUGVwc2ljbyBhbmQgdmljZSB2ZXJzYS4NCmBgYHtyfQ0KbGlicmFyeSh6b28pDQpsaWJyYXJ5KHh0cykNCmBgYA0KDQpgYGB7cn0NCnN0b2NrMDEgPC0gcmVhZFJEUygiZGF0YS9yZV9wcmljZXMuUkRhdGEiKQ0Kc3RvY2swMiA8LSByZWFkUkRTKCJkYXRhL2VxX3ByaWNlcy5SRGF0YSIpDQpwbG90KHN0b2NrMDEpDQpsaW5lcyhzdG9jazAyLCBjb2wgPSAicmVkIikNCmBgYA0KYGBge3J9DQojIERlZmluZSBrb19wZXAgDQprb19wZXAgPC0gc3RvY2swMi9zdG9jazAxDQoNCiMgTWFrZSBhIHRpbWUgc2VyaWVzIHBsb3Qgb2Yga29fcGVwDQpwbG90Lnpvbyhrb19wZXApICANCiAgDQojIEFkZCBhcyBhIHJlZmVyZW5jZSwgYSBob3Jpem9udGFsIGxpbmUgYXQgMQ0KYWJsaW5lKGggPSAxKSAgDQpgYGANClRoZSBob3Jpem9udGFsIHJlZmVyZW5jZSBpbmRpY2F0ZXMgd2hlbiBDb2NhIENvbGEgQ29tcGFueSBhbmQgUGVwc2lDbyBhcmUgcGVyZm9ybWluZyBlcXVhbGx5Lg0KDQojIyBUaGUgcG9ydGZvbGlvIHdlaWdodHMNCg0KIyMjIENhbGN1bGF0aW5nIHBvcnRmb2xpbyB3ZWlnaHRzIHdoZW4gY29tcG9uZW50IHZhbHVlcyBhcmUgZ2l2ZW4NCg0KSW4gdGhlIHZpZGVvLCBpdCB3YXMgc2hvd24gdGhhdCB5b3UgY2FuIGVhc2lseSBjb21wdXRlIHBvcnRmb2xpbyB3ZWlnaHRzIGlmIHlvdSBoYXZlIGEgZ2l2ZW4gYW1vdW50IG9mIG1vbmV5IGludmVzdGVkIGluIGNlcnRhaW4gYXNzZXRzLiBJZiB5b3Ugd2FudCB0byBzdGFydCBpbnZlc3RpbmcgaW4gYSBwb3J0Zm9saW8sIGJ1dCB5b3UgaGF2ZSBidWRnZXQgcmVzdHJhaW50cywgeW91IGNhbiBhbHNvIGltcG9zZSB3ZWlnaHRzIHlvdXJzZWxmLiBEZXBlbmRpbmcgb24gd2hhdCB0aGVzZSBhcmUsIHlvdSB3aWxsIGludmVzdCBhIGNlcnRhaW4gYW1vdW50IG9mIG1vbmV5IGluIGVhY2ggb2YgdGhlIGFzc2V0cyBiYXNlZCBvbiB0aGVpciB3ZWlnaHQuDQoNCldoZW4gZ2l2ZW4gYXNzZXQgdmFsdWVzLCBjYWxjdWxhdGluZyB0aGUgd2VpZ2h0cyBpcyBxdWl0ZSBzaW1wbGUuIFJlY2FsbCBmcm9tIHRoZSB2aWRlbyB0aGF0IHdlaWdodHMgYXJlIGNhbGN1bGF0ZWQgYnkgdGFraW5nIHRoZSB2YWx1ZSBvZiBhbiBhc3NldCBkaXZpZGVkIGJ5IHRoZSBzdW0gb2YgdmFsdWVzIGZyb20gYWxsIGFzc2V0cy4NCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgbGVhcm4gdG8gY2FsY3VsYXRlIHdlaWdodHMgd2hlbiBpbmRpdmlkdWFsIGFzc2V0IHZhbHVlcyBhcmUgZ2l2ZW4hIEZvciB0aGlzIGV4YW1wbGUsIGFuIGludmVzdG9yIGhhcyA0MDAwIFVTRCBpbnZlc3RlZCBpbiBlcXVpdGllcywgNDAwMCBVU0QgaW52ZXN0ZWQgaW4gYm9uZHMsIGFuZCAyMDAwIFVTRCBpbnZlc3RlZCBpbiBjb21tb2RpdGllcy4gQ29tcHV0ZSB0aGUgd2VpZ2h0cyBhcyB0aGUgcHJvcG9ydGlvbiBpbnZlc3RlZCBpbiBlYWNoIG9mIHRob3NlIHRocmVlIGFzc2V0cy4NCmBgYHtyfQ0KIyBEZWZpbmUgdGhlIHZlY3RvciB2YWx1ZXMNCnZhbHVlcyA8LSBjKDQwMDAsIDQwMDAsIDIwMDApDQoNCiMgRGVmaW5lIHRoZSB2ZWN0b3Igd2VpZ2h0cw0Kd2VpZ2h0cyA8LSB2YWx1ZXMvc3VtKHZhbHVlcykNCg0KIyBQcmludCB0aGUgcmVzdWx0aW5nIHdlaWdodHMNCnByaW50KHdlaWdodHMpDQpgYGANClJlbWVtYmVyIHRoYXQgdGhlIHdlaWdodCBvZiBhbiBhc3NldCBpcyBjYWxjdWxhdGVkIGJ5IHRha2luZyB0aGUgdmFsdWUgb2YgdGhlIGFzc2V0IGRpdmlkZWQgYnkgdGhlIHN1bSBvZiB2YWx1ZXMgb2YgYWxsIGFzc2V0cy4gWW91IGNhbiBhbHNvIHByZS1kZWZpbmUgd2VpZ2h0cyBhcyBhIHZlY3Rvci4gRm9yIGV4YW1wbGUgd2VpZ2h0cyA8LSBjKDAuMiwgMC4yLCAwLjYpLg0KDQojIyMgVGhlIHdlaWdodHMgb2YgYSBtYXJrZXQgY2FwaXRhbGl6YXRpb24td2VpZ2h0ZWQgcG9ydGZvbGlvDQoNCkluIGEgbWFya2V0IGNhcGl0YWxpemF0aW9uLXdlaWdodGVkIHBvcnRmb2xpbywgdGhlIHdlaWdodHMgYXJlIGdpdmVuIGJ5IHRoZSBpbmRpdmlkdWFsIGFzc2V0cycgbWFya2V0IGNhcGl0YWxpemF0aW9uIChvciBtYXJrZXQgdmFsdWUpLCBkaXZpZGVkIGJ5IHRoZSBzdW0gb2YgdGhlIG1hcmtldCBjYXBpdGFsaXphdGlvbnMgb2YgYWxsIGFzc2V0cy4gQSB0eXBpY2FsIGV4YW1wbGUgaXMgdGhlIFMmUCA1MDAgcG9ydGZvbGlvIGludmVzdGVkIGluIHRoZSA1MDAgbGFyZ2VzdCBjb21wYW5pZXMgbGlzdGVkIG9uIHRoZSBVUyBzdG9jayBleGNoYW5nZXMgKE5ZU0UsIE5hc2RhcSkuIE5vdGUgdGhhdCBieSBkaXZpZGluZyBieSB0aGUgc3VtIG9mIGFzc2V0IHZhbHVlcyBhY3Jvc3MgYWxsIHBvcnRmb2xpbyBhc3NldHMsIHRoZSBwb3J0Zm9saW8gd2VpZ2h0cyBzdW0gdG8gdW5pdHkgKG9uZSkuDQoNCkFzIGFuIGV4ZXJjaXNlLCBpbnNwZWN0IHRoZSBkaXN0cmlidXRpb24gb2YgbWFya2V0IGNhcGl0YWxpemF0aW9uIGJhc2VkIHdlaWdodHMgd2hlbiB0aGUgcG9ydGZvbGlvIGlzIGludmVzdGVkIGluIDEwIHN0b2Nrcy4gRm9yIHRoaXMgZXhlcmNpc2UsIHlvdSBjYW4gdXNlIG1hcmtldCBjYXBpdGFsaXphdGlvbnMgb2YgNSwgOCwgOSwgMjAsIDI1LCAxMDAsIDEwMCwgNTAwLCA3MDAsIGFuZCAyMDAwIG1pbGxpb24gVVNELg0KYGBge3J9DQojIERlZmluZSBtYXJrZXRjYXBzDQptYXJrZXRjYXBzIDwtIGMoNSwgOCwgOSwgMjAsIDI1LCAxMDAsIDEwMCwgNTAwLCA3MDAsIDIwMDApDQogIA0KIyBDb21wdXRlIHRoZSB3ZWlnaHRzDQp3ZWlnaHRzIDwtIG1hcmtldGNhcHMvc3VtKG1hcmtldGNhcHMpDQogIA0KIyBJbnNwZWN0IHN1bW1hcnkgc3RhdGlzdGljcw0Kc3VtbWFyeSh3ZWlnaHRzKQ0KICANCiMgQ3JlYXRlIGEgYmFyIHBsb3Qgb2Ygd2VpZ2h0cw0KYmFycGxvdCh3ZWlnaHRzKQ0KYGBgDQojIyBUaGUgcG9ydGZvbGlvIHJldHVybg0KDQojIyMgQ2FsY3VsYXRpb24gb2YgcG9ydGZvbGlvIHJldHVybnMNCg0KRm9yIHlvdXIgZmlyc3QgZXhlcmNpc2Ugb24gY2FsY3VsYXRpbmcgcG9ydGZvbGlvIHJldHVybnMsIHlvdSB3aWxsIHZlcmlmeSB0aGF0IGEgcG9ydGZvbGlvIHJldHVybiBjYW4gYmUgY29tcHV0ZWQgYXMgdGhlIHdlaWdodGVkIGF2ZXJhZ2Ugb2YgdGhlIHBvcnRmb2xpbyBjb21wb25lbnQgcmV0dXJucy4gSW4gb3RoZXIgd29yZHMsIHRoaXMgbWVhbnMgdGhhdCBhIHBvcnRmb2xpbyByZXR1cm4gaXMgY2FsY3VsYXRlZCBieSB0YWtpbmcgdGhlIHN1bSBvZiBzaW1wbGUgcmV0dXJucyBtdWx0aXBsaWVkIGJ5IHRoZSBwb3J0Zm9saW8gd2VpZ2h0cy4gUmVtZW1iZXIgdGhhdCBzaW1wbGUgcmV0dXJucyBhcmUgY2FsY3VsYXRlZCBhcyB0aGUgZmluYWwgdmFsdWUgbWludXMgdGhlIGluaXRpYWwgdmFsdWUsIGRpdmlkZWQgYnkgdGhlIGluaXRpYWwgdmFsdWUuDQoNCkFzc3VtZSB0aGF0IHlvdSBpbnZlc3RlZCBpbiB0aHJlZSBhc3NldHMuIFRoZWlyIGluaXRpYWwgdmFsdWVzIGFyZSAxMDAwIFVTRCwgNTAwMCBVU0QsIDIwMDAgVVNELCByZXNwZWN0aXZlbHkuIE92ZXIgdGltZSwgdGhlIHZhbHVlcyBjaGFuZ2UgdG8gMTEwMCBVU0QsIDQ1MDAgVVNELCBhbmQgMzAwMCBVU0QuDQpgYGB7cn0NCiMgVmVjdG9yIG9mIGluaXRpYWwgdmFsdWUgb2YgdGhlIGFzc2V0cw0KaW5fdmFsdWVzIDwtIGMoMTAwMCwgNTAwMCwgMjAwMCkNCiAgDQojIFZlY3RvciBvZiBmaW5hbCB2YWx1ZXMgb2YgdGhlIGFzc2V0cw0KZmluX3ZhbHVlcyA8LSBjKDExMDAsIDQ1MDAsIDMwMDApDQoNCiMgV2VpZ2h0cyBhcyB0aGUgcHJvcG9ydGlvbiBvZiB0b3RhbCB2YWx1ZSBpbnZlc3RlZCBpbiBlYWNoIGFzc2V0DQp3ZWlnaHRzIDwtIGluX3ZhbHVlcy9zdW0oaW5fdmFsdWVzKQ0KDQojIFZlY3RvciBvZiBzaW1wbGUgcmV0dXJucyBvZiB0aGUgYXNzZXRzIA0KcmV0dXJucyA8LSAoZmluX3ZhbHVlcyAtIGluX3ZhbHVlcykvaW5fdmFsdWVzDQpyZXR1cm5zDQojIENvbXB1dGUgcG9ydGZvbGlvIHJldHVybiB1c2luZyB0aGUgcG9ydGZvbGlvIHJldHVybiBmb3JtdWxhDQpwcmV0dXJucyA8LSBzdW0od2VpZ2h0cypyZXR1cm5zKQ0KcHJldHVybnMNCmBgYA0KTm90ZSB0aGF0IHRoZSB3ZWlnaHRlZCBhdmVyYWdlIHBvcnRmb2xpbyByZXR1cm4gZXF1YWxzIHRoZSBwZXJjZW50YWdlIGNoYW5nZSBpbiB0aGUgdG90YWwgcG9ydGZvbGlvIHZhbHVlLiBSZW1lbWJlciB0aGF0IHBvcnRmb2xpbyByZXR1cm5zIGFyZSBjYWxjdWxhdGVkIGJ5IHRha2luZyB0aGUgc3VtIG9mIHNpbXBsZSByZXR1cm5zLCBtdWx0aXBsaWVkIGJ5IHRoZSBwb3J0Zm9saW8gd2VpZ2h0cy4NCg0KIyMjIEZyb20gc2ltcGxlIHRvIGdyb3NzIGFuZCBtdWx0aS1wZXJpb2QgcmV0dXJucw0KDQpUaGUgc2ltcGxlIHJldHVybiBSIGV4cHJlc3NlcyB0aGUgcGVyY2VudGFnZSBjaGFuZ2UgaW4gdGhlIHZhbHVlIG9mIGFuIGludmVzdG1lbnQuIFRoZSBjb3JyZXNwb25kaW5nIHNvLWNhbGxlZCAiZ3Jvc3MiIHJldHVybiBpcyBkZWZpbmVkIGFzIHRoZSBmdXR1cmUgdmFsdWUgb2YgMSBVU0QgaW52ZXN0ZWQgaW4gdGhlIGFzc2V0IGZvciBvbmUgcGVyaW9kIGFuZCBpcyB0aHVzIGVxdWFsIHRvIDErUi4NCg0KVGhlIGdyb3NzIHJldHVybiBvdmVyIHR3byBwZXJpb2RzIGlzIG9idGFpbmVkIHNpbWlsYXJseS4gTGV0ICRSMSQgYmUgdGhlIHJldHVybiBpbiB0aGUgZmlyc3QgcGVyaW9kIGFuZCAkUjIkIHRoZSByZXR1cm4gaW4gdGhlIHNlY29uZCBwZXJpb2QuIFRoZW4gdGhlIGVuZC12YWx1ZSBvZiBhIDEgVVNEIGludmVzdG1lbnQgaXMgKDErJFIxJCniiJcoMSskUjIkKS4NCg0KVGhlIGNvcnJlc3BvbmRpbmcgc2ltcGxlIHJldHVybiBvdmVyIHRob3NlIHR3byBwZXJpb2RzIGlzOiAoMSskUjEkKeKIlygxKyRSMiQp4oiSMS4NCg0KU3VwcG9zZSB0aGF0IHlvdSBoYXZlIGFuIGludmVzdG1lbnQgaG9yaXpvbiBvZiB0d28gcGVyaW9kcy4gSW4gdGhlIGZpcnN0IHBlcmlvZCwgeW91IG1ha2UgYSAxMCUgcmV0dXJuLiBCdXQgaW4gdGhlIHNlY29uZCBwZXJpb2QsIHlvdSB0YWtlIGEgbG9zcyBvZiA1JS4NCmBgYHtyfQ0KMTAwMCooKDEgKyAwLjEpKigxIC0gMC4wNSkgLSAxKQ0KYGBgDQpFbmQtdmFsdWUgPSAxMDQ1DQoNCiMjIyBUaGUgYXN5bW1ldHJpYyBpbXBhY3Qgb2YgZ2FpbnMgYW5kIGxvc3Nlcw0KDQpJdCBpcyBpbXBvcnRhbnQgdG8gYmUgYXdhcmUgb2YgdGhlIGZhY3QgdGhhdCBhIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSByZXR1cm4gb2YgdGhlIHNhbWUgcmVsYXRpdmUgbWFnbml0dWRlIGRvIG5vdCBjb21wZW5zYXRlIGVhY2ggb3RoZXIgaW4gdGVybXMgb2YgdGVybWluYWwgd2VhbHRoLiBNYXRoZW1hdGljYWxseSwgdGhpcyBjYW4gYmUgc2VlbiBmcm9tIHRoZSBpZGVudGl0eSAoMSt4KeKIlygx4oiSeCk9MeKIkngyLCB3aGljaCBpcyBsZXNzIHRoYW4gb25lLiBBIDUwJSBsb3NzIGlzIHRodXMgbm90IGNvbXBlbnNhdGVkIGJ5IGEgNTAlIGdhaW4uIEFmdGVyIGEgbG9zcyBvZiA1MCUsIHdoYXQgaXMgdGhlIHJldHVybiBuZWVkZWQgdG8gYmUgYXQgcGFyIGFnYWluPyAxMDAlDQoNCiMjIFBlcmZvcm1hbmNlQW5hbHl0aWNzDQoNCiMjIyBCdXktYW5kLWhvbGQgdmVyc3VzIChkYWlseSkgcmViYWxhbmNpbmcNCg0KVGhlIGNob2ljZSBvZiBpbnZlc3RtZW50IG1hdHRlcnMsIGV2ZW4gd2hlbiB0aGUgdW5kZXJseWluZyByaXNreSBhc3NldHMgYXJlIHNpbWlsYXIuIEFzIGFuIGV4YW1wbGUsIHlvdSB3aWxsIG5vdyBjb25zaWRlciB0aGUgc3RvY2sgcHJpY2Ugb2YgQXBwbGUgYW5kIE1pY3Jvc29mdCBmcm9tIEphbnVhcnkgMjAwNiB1bnRpbCB0aGUgZW5kIG9mIEF1Z3VzdCAyMDE2LiBUaGUgdGltZSBzZXJpZXMgcGxvdCBzaG93cyB5b3UgdGhlIHZhbHVlIGV2b2x1dGlvbiBvZiBvbmUgZG9sbGFyIGludmVzdGVkIGluIGVhY2ggb2YgdGhlbS4NCkZvciB0aGlzIGV4ZXJjaXNlLCB5b3VyIHBvcnRmb2xpbyBhcHByb2FjaCB3aWxsIGJlIHRvIGludmVzdCBoYWxmIG9mIHlvdXIgYnVkZ2V0IGluIEFwcGxlIHN0b2NrLCBhbmQgdGhlIG90aGVyIGhhbGYgb2YgeW91ciBidWRnZXQgaW4gTWljcm9zb2Z0IHN0b2NrLiBPdmVyIHRpbWUsIHRoZSBwb3J0Zm9saW8gd2VpZ2h0cyB3aWxsIGNoYW5nZS4gWW91IHdpbGwgaGF2ZSB0d28gY2hvaWNlcyBhcyBhbiBpbnZlc3Rvci4gVGhlIGZpcnN0IGNob2ljZSBpcyB0byBiZSBwYXNzaXZlIGFuZCBub3QgdHJhZGUgYW55IGZ1cnRoZXIuIFRoaXMgaXMgY2FsbGVkIGEgYnV5IGFuZCBob2xkIHN0cmF0ZWd5LiBUaGUgc2Vjb25kIGNob2ljZSBpcyB0byBidXkgYW5kIHRyYWRlIGF0IHRoZSBjbG9zZSBvZiBlYWNoIGRheSB0aGF0IHJlc3VsdHMgaW4gYSByZWJhbGFuY2Ugb2YgdGhlIHBvcnRmb2xpbyBzdWNoIHRoYXQgeW91ciBwb3J0Zm9saW8gaXMgZXF1YWxseSBpbnZlc3RlZCBpbiBzaGFyZXMgb2YgTWljcm9zb2Z0IGFuZCBBcHBsZS4gVGhpcyBpcyBhIHJlYmFsYW5jZWQgcG9ydGZvbGlvLg0KDQotCUJ5IGludmVzdGluZyBpbiBhIHBvcnRmb2xpbyBvZiByaXNreSBhc3NldHMsIHRoZSBwb3J0Zm9saW8gcGF5b2ZmIHdpbGwgYmUgaW4gYmV0d2VlbiB0aGUgbWluaW11bSBhbmQgbWF4aW11bSBvZiB0aGUgcGF5b2ZmIG9mIHRoZSB1bmRlcmx5aW5nIHJpc2t5IGFzc2V0cy4NCi0JQnkgaW52ZXN0aW5nIGluIGEgcG9ydGZvbGlvIG9mIHJpc2t5IGFzc2V0cywgdGhlIHJpc2sgb2YgdGhlIHBvcnRmb2xpbyBwYXlvZmYgd2lsbCBiZSBsZXNzIHRoYW4gdGhlIG1heGltdW0gcmlzayBvZiB0aGUgdW5kZXJseWluZyByaXNreSBhc3NldHMuDQotCUJlY2F1c2UgdGhlIHByaWNlcyBvZiBNaWNyb3NvZnQgYW5kIEFwcGxlIGhhdmUgZXZvbHZlZCBkaWZmZXJlbnRseSwgdGhlIGludmVzdG9yIHdobyBzcGVudCBpbml0aWFsbHkgaGFsZiBvZiBoaXMgd2VhbHRoIG9uIE1pY3Jvc29mdCBhbmQgQXBwbGUgZW5kcyB1cCB3aXRoIGEgcG9ydGZvbGlvIHRoYXQgaXMgbm8gbG9uZ2VyIGVxdWFsbHkgaW52ZXN0ZWQuDQoNCiMjIyBUaGUgdGltZSBzZXJpZXMgb2YgYXNzZXQgcmV0dXJucw0KDQpDYWxjdWxhdGluZyB0aGUgcmV0dXJucyBmb3Igb25lIHBlcmlvZCBpcyBwcmV0dHkgc3RyYWlnaHRmb3J3YXJkIHRvIGRvIGluIFIuIFdoZW4gdGhlIHJldHVybnMgbmVlZCB0byBiZSBjYWxjdWxhdGVkIGZvciBkaWZmZXJlbnQgZGF0ZXMgdGhlIGZ1bmN0aW9ucyBSZXR1cm4uY2FsY3VsYXRlKCkgYW5kIFJldHVybi5wb3J0Zm9saW8oKSwgaW4gdGhlIFIgcGFja2FnZSBQZXJmb3JtYW5jZUFuYWx5dGljcyBhcmUgZXh0cmVtZWx5IGhlbHBmdWwuIFRoZXkgcmVxdWlyZSB0aGUgaW5wdXQgZGF0YSB0byBiZSBvZiB0aGUgeHRzLXRpbWUgc2VyaWVzIGNsYXNzLCB3aGljaCBpcyBwcmUtbG9hZGVkLiBZb3Ugd2lsbCBleHBsb3JlIHRoZSBmdW5jdGlvbmFsaXR5IG9mIHRoZSBQZXJmb3JtYW5jZUFuYWx5dGljcyBwYWNrYWdlIGluIHRoaXMgZXhlcmNpc2UuDQoNClRoZSBvYmplY3QgcHJpY2VzIHdoaWNoIGNvbnRhaW5zIHRoZSBBcHBsZSAoYWFwbCkgYW5kIE1pY3Jvc29mdCAobXNmdCkgc3RvY2tzIGlzIGF2YWlsYWJsZSBpbiB0aGUgd29ya3NwYWNlLg0KYGBge3J9DQpwcmljZXMgPC0gcmVhZFJEUygiZGF0YS9hYXBsX21zZnQuUkRhdGEiKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChwcmljZXMkQWRqQ2xvc2UuYWFwbCkNCmxpbmVzKHByaWNlcyRBZGpDbG9zZS5tc2Z0LCBjb2wgPSAicmVkIikNCmBgYA0KDQpgYGB7cn0NCiMgTG9hZCBwYWNrYWdlIFBlcmZvcm1hbmNlQW5hbHl0aWNzIA0KbGlicmFyeShQZXJmb3JtYW5jZUFuYWx5dGljcykNCg0KIyBQcmludCB0aGUgZmlyc3Qgc2l4IHJvd3MgYW5kIGxhc3Qgc2l4IHJvd3Mgb2YgcHJpY2VzDQpoZWFkKHByaWNlcykNCnRhaWwocHJpY2VzKQ0KDQojIENyZWF0ZSB0aGUgdmFyaWFibGUgcmV0dXJucyB1c2luZyBSZXR1cm4uY2FsY3VsYXRlKCkgIA0KcmV0dXJucyA8LSBSZXR1cm4uY2FsY3VsYXRlKHByaWNlcykNCiAgDQojIFByaW50IHRoZSBmaXJzdCBzaXggcm93cyBvZiByZXR1cm5zLiBOb3RlIHRoYXQgdGhlIGZpcnN0IG9ic2VydmF0aW9uIGlzIE5BIGJlY2F1c2UgdGhlcmUgaXMgbm8gcHJpb3IgcHJpY2UuDQpoZWFkKHJldHVybnMpDQogIA0KIyBSZW1vdmUgdGhlIGZpcnN0IHJvdyBvZiByZXR1cm5zDQpyZXR1cm5zIDwtIHJldHVybnNbLTEsIF0NCmhlYWQocmV0dXJucykNCmBgYA0KTm90aWNlIHRoYXQgeW91IHJlbW92ZWQgdGhlIGZpcnN0IHJvdyBvZiB5b3VyIHJldHVybnMgYmVjYXVzZSB0aGV5IHdlcmUgTkEgdmFsdWVzIQ0KDQojIyMgVGhlIHRpbWUgc2VyaWVzIG9mIHBvcnRmb2xpbyByZXR1cm5zDQoNCkluIHRoZSBwcmV2aW91cyBleGVyY2lzZSwgeW91IGNyZWF0ZWQgYSB2YXJpYWJsZSBjYWxsZWQgcmV0dXJucyBmcm9tIHRoZSBkYWlseSBwcmljZXMgb2Ygc3RvY2tzIG9mIEFwcGxlIGFuZCBNaWNyb3NvZnQuIEluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGNyZWF0ZSB0d28gcG9ydGZvbGlvcyB1c2luZyB0aGUgcmV0dXJuIHNlcmllcyB5b3UgcHJldmlvdXNseSBjcmVhdGVkLiBUaGUgdHdvIHBvcnRmb2xpb3Mgd2lsbCBkaWZmZXIgaW4gb25lIHdheSwgYW5kIHRoYXQgaXMgdGhlIHdlaWdodGluZyBvZiB0aGUgYXNzZXRzLg0KDQpJbiB0aGUgbGFzdCB2aWRlbywgeW91IHdlcmUgaW50cm9kdWNlZCB0byB0d28gd2VpZ2h0aW5nIHN0cmF0ZWdpZXM6IHRoZSBidXkgYW5kIGhvbGQgc3RyYXRlZ3ksIGFuZCBhIG1vbnRobHkgcmViYWxhbmNpbmcgc3RyYXRlZ3kuIEluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGNyZWF0ZSBhIHBvcnRmb2xpbyBpbiB3aGljaCB5b3UgZG9u4oCZdCByZWJhbGFuY2UsIGFuZCBvbmUgd2hlcmUgeW91IHJlYmFsYW5jZSBtb250aGx5LiBZb3Ugd2lsbCB0aGVuIHZpc3VhbGl6ZSB0aGUgcG9ydGZvbGlvIHJldHVybnMgb2YgYm90aC4NCg0KWW91IHdpbGwgdXNlIHRoZSBmdW5jdGlvbiBSZXR1cm4ucG9ydGZvbGlvKCkgZm9yIHlvdXIgY2FsY3VsYXRpb25zLiBGb3IgdGhpcyBmdW5jdGlvbiwgeW91IHdpbGwgcHJvdmlkZSB0aHJlZSBhcmd1bWVudHM6IFIsIHdlaWdodHMsIGFuZCByZWJhbGFuY2Vfb24uIFIgaXMgYSB0aW1lIHNlcmllcyBvZiByZXR1cm5zLCB3ZWlnaHRzIGlzIGEgdmVjdG9yIGNvbnRhaW5pbmcgYXNzZXQgd2VpZ2h0cywgYW5kIHJlYmFsYW5jZV9vbiBzcGVjaWZpZXMgd2hpY2ggY2FsZW5kYXItcGVyaW9kIHRvIHJlYmFsYW5jZSBvbi4gSWYgeW91IG5lZWQgaGVscCwgYmUgc3VyZSB0byBjaGVjayB0aGUgZG9jdW1lbnRhdGlvbi4NCg0KYGBge3J9DQojIENyZWF0ZSB0aGUgd2VpZ2h0cw0KZXFfd2VpZ2h0cyA8LSBjKDAuNSwgMC41KQ0KDQojIENyZWF0ZSBhIHBvcnRmb2xpbyB1c2luZyBidXkgYW5kIGhvbGQNCnBmX2JoIDwtIFJldHVybi5wb3J0Zm9saW8oUiA9IHJldHVybnMsIHdlaWdodHMgPSBlcV93ZWlnaHRzKQ0KDQojIENyZWF0ZSBhIHBvcnRmb2xpbyByZWJhbGFuY2luZyBtb250aGx5IA0KcGZfcmViYWwgPC0gUmV0dXJuLnBvcnRmb2xpbyhSID0gcmV0dXJucywgd2VpZ2h0cyA9IGVxX3dlaWdodHMsIHJlYmFsYW5jZV9vbiA9ICJtb250aHMiKQ0KDQojIFBsb3QgdGhlIHRpbWUtc2VyaWVzDQpwYXIobWZyb3cgPSBjKDIsIDEpLCBtYXIgPSBjKDIsIDQsIDIsIDIpKQ0KcGxvdC56b28ocGZfYmgpDQpwbG90LnpvbyhwZl9yZWJhbCkNCmBgYA0KWW91IHdpbGwgc2VlIGJhc2VkIG9uIHRoZSBwbG90cyB0aGF0IHJlYmFsYW5jaW5nIGEgcG9ydGZvbGlvIGNhbiBsZWFkIHRvIGxlc3MgZXh0cmVtZSByZXR1cm5zIGluIGVpdGhlciBkaXJlY3Rpb24uDQoNCiMjIyBUaGUgdGltZSBzZXJpZXMgb2Ygd2VpZ2h0cw0KDQpJbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHlvdSBleHBsb3JlZCB0aGUgZnVuY3Rpb25hbGl0eSBvZiB0aGUgUmV0dXJuLnBvcnRmb2xpbygpIGZ1bmN0aW9uIGFuZCBjcmVhdGVkIHBvcnRmb2xpb3MgdXNpbmcgdHdvIHN0cmF0ZWdpZXMuIEhvd2V2ZXIsIGJ5IHNldHRpbmcgdGhlIGFyZ3VtZW50IHZlcmJvc2UgPSBUUlVFIGluIFJldHVybi5wb3J0Zm9saW8oKSB5b3UgY2FuIGNyZWF0ZSBhIGxpc3Qgb2YgYmVnaW5uaW5nIG9mIHBlcmlvZCAoQk9QKSBhbmQgZW5kIG9mIHBlcmlvZCAoRU9QKSB3ZWlnaHRzIGFuZCB2YWx1ZXMgaW4gYWRkaXRpb24gdG8gdGhlIHBvcnRmb2xpbyByZXR1cm5zLCBhbmQgY29udHJpYnV0aW9ucy4NCg0KWW91IGNhbiBhY2Nlc3MgdGhlc2UgZnJvbSB0aGUgcmVzdWx0YW50IGxpc3Qtb2JqZWN0IGNyZWF0ZWQgZnJvbSBSZXR1cm4ucG9ydGZvbGlvKCkuIFRoZSByZXN1bHRhbnQgbGlzdCBjb250YWlucyBcJHJldHVybnMsIFwkY29udHJpYnV0aW9ucywgXCRCT1AuV2VpZ2h0LCBcJEVPUC5XZWlnaHQsIFwkQk9QLlZhbHVlLCBhbmQgXCRFT1AuVmFsdWUuDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIHJlY3JlYXRlIHRoZSBwb3J0Zm9saW9zIHlvdSBtYWRlIGluIHRoZSBsYXN0IGV4ZXJjaXNlIGJ1dCBleHRlbmQgaXQgYnkgY3JlYXRpbmcgYSBsaXN0IG9mIGNhbGN1bGF0aW9ucyB1c2luZyB2ZXJib3NlID0gVFJVRS4gWW91IHdpbGwgdGhlbiB2aXN1YWxpemUgdGhlIGVuZCBvZiBwZXJpb2Qgd2VpZ2h0cyBvZiBBcHBsZS4NCmBgYHtyfQ0KIyBDcmVhdGUgdGhlIHdlaWdodHMNCmVxX3dlaWdodHMgPC0gYygwLjUsIDAuNSkNCg0KIyBDcmVhdGUgYSBwb3J0Zm9saW8gdXNpbmcgYnV5IGFuZCBob2xkDQpwZl9iaCA8LSBSZXR1cm4ucG9ydGZvbGlvKHJldHVybnMsIHdlaWdodHMgPSBlcV93ZWlnaHRzLCB2ZXJib3NlID0gVFJVRSApDQoNCiMgQ3JlYXRlIGEgcG9ydGZvbGlvIHRoYXQgcmViYWxhbmNlcyBtb250aGx5DQpwZl9yZWJhbCA8LSBSZXR1cm4ucG9ydGZvbGlvKHJldHVybnMsIHdlaWdodHMgPSBlcV93ZWlnaHRzLCByZWJhbGFuY2Vfb24gPSAibW9udGhzIiwgdmVyYm9zZSA9IFRSVUUgICkNCg0KIyBDcmVhdGUgZW9wX3dlaWdodF9iaA0KZW9wX3dlaWdodF9iaCA8LSBwZl9iaCRFT1AuV2VpZ2h0DQoNCiMgQ3JlYXRlIGVvcF93ZWlnaHRfcmViYWwNCmVvcF93ZWlnaHRfcmViYWwgPC0gcGZfcmViYWwkRU9QLldlaWdodA0KDQojIFBsb3QgZW5kIG9mIHBlcmlvZCB3ZWlnaHRzDQpwYXIobWZyb3cgPSBjKDIsIDEpLCBtYXI9YygyLCA0LCAyLCAyKSkNCnBsb3Quem9vKGVvcF93ZWlnaHRfYmgkQWRqQ2xvc2UuYWFwbCkNCnBsb3Quem9vKGVvcF93ZWlnaHRfcmViYWwkQWRqQ2xvc2UuYWFwbCkNCmBgYA0KTG9vayBhdCB0aGUgdHdvIHBsb3RzLiBOb3RpY2UgaG93IHRoZSB3ZWlnaHRzIHRoZSByZWJhbGFuY2VkIHBvcnRmb2xpbyBzdGF5IGNsb3NlIHRvIHRoZSBpbml0aWFsIHdlaWdodCBvZiA1MCUsIGFuZCBob3cgdGhleSBidXkgYW5kIGhvbGQgc3RyYXRlZ3kgaGFzIEFwcGxlIHN0b2NrIHdpdGggYWJvdXQgODAlIHdlaWdodA0KDQojIEFuYWx5emluZyBwZXJmb3JtYW5jZQ0KDQojIyBEaW1lbnNpb25zIG9mIHBvcnRmb2xpbyBwZXJmb3JtYW5jZQ0KDQojIyMgRXhwbG9yaW5nIHRoZSBtb250aGx5IFMmUCA1MDAgcmV0dXJucw0KDQpGb3IgdGhlIHVwY29taW5nIGV4ZXJjaXNlcywgeW91IHdpbGwgZXhhbWluZSB0aGUgbW9udGhseSBwZXJmb3JtYW5jZSBvZiB0aGUgUyZQIDUwMC4gQSBwaWN0dXJlIGlzIHdvcnRoIGEgdGhvdXNhbmQgd29yZHMuIFRoaXMgaXMgd2h5IG1vc3QgYW5hbHlzZXMgb2YgcGVyZm9ybWFuY2Ugc3RhcnQgYnkgc3R1ZHlpbmcgdGhlIHRpbWUgc2VyaWVzIHBsb3Qgb2YgdGhlIHZhbHVlIG9mIGFuIGludmVzdG1lbnQuDQoNCkEgcGxvdCBvZiB0aGUgUyZQIDUwMCBmb3IgdGhlIHBlcmlvZCBvZiAxOTg2IHVudGlsIEF1Z3VzdCAyMDE2IGlzIHNob3duIHRvIHlvdXIgcmlnaHQuIEVhY2ggb2JzZXJ2YXRpb24gb24gdGhpcyBwbG90IGlzIGFuIGVuZC1vZi1kYXkgdmFsdWUuIFRoaXMgY2hhcnQgc2hvd3MgYSBudW1iZXIgb2YgYm9vbXMgYW5kIGJ1c3RzLiBUYWtlIGEgbG9vayBhdCB0aGUgY2hhcnQsIGRvIHlvdSBzZWUgd2h5IHRoZSAyMDAwcyBhcmUgc28gb2Z0ZW4gY2FsbGVkIHRoZSBsb3N0IGRlY2FkZSBvZiBpbnZlc3Rpbmc/DQpZb3VyIGpvYiBpcyB0byBkZXNjcmliZSB0aGUgbW9udGhseSBwZXJmb3JtYW5jZSBvZiB0aGUgUyZQIDUwMC4gVG8gZG8gc28sIHlvdSB3aWxsIGZpcnN0IG5lZWQgdG8gYWdncmVnYXRlIGRhaWx5IHByaWNlIHNlcmllcyB0byBlbmQtb2YtbW9udGggcHJpY2VzLiBZb3Ugd2lsbCB0aGVuIGNhbGN1bGF0ZSB0aGUgbW9udGhseSByZXR1cm5zIGFuZCB2aXN1YWxpemUgdGhlbSBpbiBhIHRhYmxlLg0KDQpgYGB7cn0NCnNwNTAwIDwtIHJlYWRSRFMoImRhdGEvc3A1MDAuUkRhdGEiKQ0Kc3A1MDAgPC0gYXMueHRzKHNwNTAwKQ0KcGxvdChzcDUwMCkNCmBgYA0KYGBge3J9DQojIENvbnZlcnQgdGhlIGRhaWx5IGZyZXF1ZW5jeSBvZiBzcDUwMCB0byBtb250aGx5IGZyZXF1ZW5jeTogc3A1MDBfbW9udGhseQ0KI3NwNTAwX21vbnRobHkgPC0gdG8ubW9udGhseShzcDUwMCwgaW5kZXhBdCA9ICJlbmRvZiIpDQoNCiMgUHJpbnQgdGhlIGZpcnN0IHNpeCByb3dzIG9mIHNwNTAwX21vbnRobHkNCiNoZWFkKHNwNTAwX21vbnRobHkpDQoNCiMgQ3JlYXRlIHNwNTAwX3JldHVybnMgdXNpbmcgUmV0dXJuLmNhbGN1bGF0ZSB1c2luZyB0aGUgY2xvc2luZyBwcmljZXMNCnNwNTAwX3JldHVybnMgPC0gUmV0dXJuLmNhbGN1bGF0ZShzcDUwMCkgDQpzcDUwMF9yZXR1cm5zIDwtIHNwNTAwX3JldHVybnNbLTEsXQ0KIyBUaW1lIHNlcmllcyBwbG90DQpwbG90LnpvbyhzcDUwMF9yZXR1cm5zKQ0KDQojIFByb2R1Y2UgdGhlIHllYXIgeCBtb250aCB0YWJsZQ0KdGFibGUuQ2FsZW5kYXJSZXR1cm5zKHNwNTAwX3JldHVybnMpDQpgYGANCiMjIyBUaGUgbW9udGhseSBtZWFuIGFuZCB2b2xhdGlsaXR5DQoNCllvdSBoYXZlIG5vdyBjcmVhdGVkIG1vbnRoIHRvIG1vbnRoIHJldHVybnMgZm9yIHRoZSBTJlAgNTAwLiBOZXh0LCB5b3Ugd2lsbCBuZWVkIHRvIGRvIGEgZGVzY3JpcHRpdmUgYW5hbHlzaXMgb2YgdGhlIHJldHVybnMuDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGNhbGN1bGF0ZSB0aGUgbWVhbiAoYXJpdGhtZXRpYyBhbmQgZ2VvbWV0cmljKSBhbmQgdm9sYXRpbGl0eSAoc3RhbmRhcmQgZGV2aWF0aW9uKSBvZiB0aGUgcmV0dXJucy4gVGhlc2UgcmV0dXJucyBhcmUgYXZhaWxhYmxlIGluIHlvdXIgd29ya3NwYWNlIGFzIHRoZSB2YXJpYWJsZSBzcDUwMF9yZXR1cm5zLg0KYGBge3J9DQojIENvbXB1dGUgdGhlIG1lYW4gbW9udGhseSByZXR1cm5zDQptZWFuKHNwNTAwX3JldHVybnMpDQoNCiMgQ29tcHV0ZSB0aGUgZ2VvbWV0cmljIG1lYW4gb2YgbW9udGhseSByZXR1cm5zDQptZWFuLmdlb21ldHJpYyhzcDUwMF9yZXR1cm5zKQ0KDQojIENvbXB1dGUgdGhlIHN0YW5kYXJkIGRldmlhdGlvbg0Kc2Qoc3A1MDBfcmV0dXJucykNCmBgYA0KVGhlIHZvbGF0aWxpdHkgYW5kIG1lYW4gcmV0dXJuIGFyZSB2ZXJ5IGltcG9ydGFudCBtZXRyaWNzIGluIGV2ZXJ5IGludmVzdG9yJ3MgZGVjaXNpb24gbWFraW5nIQ0KDQojIyBUaGUgKGFubnVhbGl6ZWQpIFNoYXJwZSByYXRpbw0KDQojIyMgRXhjZXNzIHJldHVybnMgYW5kIHRoZSBwb3J0Zm9saW8ncyBTaGFycGUgcmF0aW8NCg0KWW91IGp1c3QgbGVhcm5lZCBob3cgdG8gY3JlYXRlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgb2YgeW91ciBwb3J0Zm9saW8gcmV0dXJucy4gTm93IHlvdSB3aWxsIGxlYXJuIGhvdyB0byBldmFsdWF0ZSB5b3VyIHBvcnRmb2xpbydzIHBlcmZvcm1hbmNlIQ0KDQpQZXJmb3JtYW5jZSBldmFsdWF0aW9uIGludm9sdmVzIGNvbXBhcmluZyB5b3VyIGludmVzdG1lbnQgY2hvaWNlcyB3aXRoIGFuIGFsdGVybmF0aXZlIGludmVzdG1lbnQgY2hvaWNlLiBNb3N0IG9mdGVuLCB0aGUgcGVyZm9ybWFuY2UgaXMgY29tcGFyZWQgd2l0aCBpbnZlc3RpbmcgaW4gYW4gKGFsbW9zdCkgcmlzay1mcmVlIGFzc2V0LCBzdWNoIGFzIHRoZSBVLlMuIFRyZWFzdXJ5IEJpbGwuIFRoZSByZXR1cm4gZnJvbSBhIFUuUy4gVHJlYXN1cnkgQmlsbCBpcyBrbm93biBhcyBhIHJpc2stZnJlZSByYXRlIGJlY2F1c2UgVHJlYXN1cnkgQmlsbHMgKFQtQmlsbHMpIGFyZSBiYWNrZWQgYnkgdGhlIFUuUy4gR292ZXJubWVudC4NCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgYmUgYXNrZWQgdG8gYW5udWFsaXplIHRoZSByaXNrLWZyZWUgcmF0ZSBieSB1c2luZyB0aGUgY29tcG91bmQgaW50ZXJlc3QgZm9ybXVsYS4gVGhlIHllYXJseSBjb21wb3VuZGVkIGludGVyZXN0IHJhdGUgaXMgZ2l2ZW4gYnkgJCgxK3kpXnsxMn3iiJIxJC4gVGhlIGFubnVhbCByYXRlIGlzIHVzZWQgdG8gZXN0aW1hdGUgYSB5ZWFybHkgcmV0dXJuIGFuZCBpcyB2ZXJ5IHVzZWZ1bCBmb3IgZm9yZWNhc3RpbmcuDQoNClRoZSBTaGFycGUgUmF0aW8gaXMgYW4gaW1wb3J0YW50IG1ldHJpYyB0aGF0IHRlbGxzIHVzIHRoZSByZXR1cm4tdG8tdm9sYXRpbGl0eSByYXRpby4gSXQgaXMgY2FsY3VsYXRlZCBieSB0YWtpbmcgdGhlIG1lYW4gb2YgZXhjZXNzIHJldHVybnMgKHJldHVybnMgLSByaXNrLWZyZWUgcmF0ZSksIGRpdmlkZWQgYnkgdGhlIHZvbGF0aWxpdHkgb2YgdGhlIHJldHVybnMuDQpgYGB7cn0NCiNkb3dubG9hZCAxMyB3ZWVrIFR0cmVhc3VyeSBCaWxsICh0aWNrZXI6IF5JUlgpDQpsaWJyYXJ5KHRzZXJpZXMpDQpjb24gPC0gdXJsKCJodHRwczovL2ZpbmFuY2UueWFob28uY29tIikNCmlmKCFpbmhlcml0cyh0cnkob3Blbihjb24pLCBzaWxlbnQgPSBUUlVFKSwgInRyeS1lcnJvciIpKSB7DQogIGNsb3NlKGNvbikNCiAgdGIgPC0gZ2V0Lmhpc3QucXVvdGUoaW5zdHJ1bWVudCA9ICJeSVJYIiwgc3RhcnQgPSAiMTk4Ni0wMS0wMSIsIGVuZCA9ICIyMDE2LTA4LTMxIiwNCiAgICAgICAgICAgICAgICAgICAgICBxdW90ZSA9ICJDbG9zZSIsIGNvbXByZXNzaW9uID0gImQiKQ0KICBwbG90KHRiKQ0KfQ0KdGJfbW9udGhseSA8LSB0by5tb250aGx5KHRiLCBpbmRleEF0ID0gInN0YXJ0b2YiKQ0KIyBtb250aGx5IGludGVyZXN0IGZvciByaXNrIGZyZWUgcHVycG9zZXMNCnJmIDwtIHRiX21vbnRobHkkdGIuQ2xvc2UvMTIvMTAwDQpyZiA8LSBhcy54dHMocmYpDQpgYGANCg0KYGBge3J9DQojIENvbXB1dGUgdGhlIGFubnVhbGl6ZWQgcmlzayBmcmVlIHJhdGUNCmFubnVhbGl6ZWRfcmYgPC0gKDEgKyByZileMTIgLSAxDQoNCiMgUGxvdCB0aGUgYW5udWFsaXplZCByaXNrLWZyZWUgcmF0ZQ0KcGxvdChhbm51YWxpemVkX3JmKQ0KDQojIENvbXB1dGUgdGhlIHNlcmllcyBvZiBleGNlc3MgcG9ydGZvbGlvIHJldHVybnMgDQpzcDUwMF9leGNlc3MgPC0gc3A1MDBfcmV0dXJucyAtIHJmDQoNCiMgQ29tcGFyZSB0aGUgbWVhbiBvZiBzcDUwMF9leGNlc3MgYW5kIHNwNTAwX3JldHVybnMgDQptZWFuKHNwNTAwX2V4Y2VzcykNCm1lYW4oc3A1MDBfcmV0dXJucykNCg0KIyBDb21wdXRlIHRoZSBTaGFycGUgcmF0aW8NCnNwNTAwX3NoYXJwZSA8LSBtZWFuKHNwNTAwX2V4Y2VzcykgLyBzZChzcDUwMF9yZXR1cm5zKQ0Kc3A1MDBfc2hhcnBlDQpgYGANClJlbWVtYmVyIHRoYXQgdGhlIFNoYXJwZSBSYXRpbyBpcyBjYWxjdWxhdGVkIGJ5IHRha2luZyB0aGUgbWVhbiBvZiBleGNlc3MgcmV0dXJucyBhbmQgZGl2aWRpbmcgYnkgdGhlIHZvbGF0aWxpdHkgb2YgdGhvc2UgcmV0dXJucy4NCg0KIyMjIEFubnVhbGl6ZWQgbWVhbiBhbmQgdm9sYXRpbGl0eQ0KDQpUaGUgbWVhbiBhbmQgdm9sYXRpbGl0eSBvZiBtb250aGx5IHJldHVybnMgY29ycmVzcG9uZCB0byB0aGUgYXZlcmFnZSBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIG92ZXIgYSBtb250aGx5IGludmVzdG1lbnQgaG9yaXpvbi4gSW52ZXN0b3JzIGFubnVhbGl6ZSB0aG9zZSBzdGF0aXN0aWNzIHRvIHNob3cgdGhlIHBlcmZvcm1hbmNlIG92ZXIgYW4gYW5udWFsIGludmVzdG1lbnQgaG9yaXpvbi4NCg0KVG8gZG8gc28sIHRoZSBwYWNrYWdlIFBlcmZvcm1hbmNlQW5hbHl0aWNzIGhhcyB0aGUgZnVuY3Rpb24gUmV0dXJuLmFubnVhbGl6ZWQoKSBhbmQgU3RkRGV2LmFubnVhbGl6ZWQoKSB0byBjb21wdXRlIHRoZSAoZ2VvbWV0cmljYWxseSkgYW5udWFsaXplZCBtZWFuIHJldHVybiBhbmQgYW5udWFsaXplZCBzdGFuZGFyZCBkZXZpYXRpb24gZm9yIHlvdS4NClJlbWVtYmVyIHRoYXQgdGhlIFNoYXJwZSByYXRpbyBpcyBmb3VuZCBieSB0YWtpbmcgdGhlIG1lYW4gZXhjZXNzIHJldHVybnMgc3VidHJhY3RlZCBieSB0aGUgcmlzay1mcmVlIHJhdGUsIGFuZCB0aGVuIGRpdmlkZWQgYnkgdGhlIHZvbGF0aWxpdHkhDQpgYGB7cn0NCiMgQ29tcHV0ZSB0aGUgYW5udWFsaXplZCBtZWFuDQpSZXR1cm4uYW5udWFsaXplZChzcDUwMF9yZXR1cm5zKQ0KDQojIENvbXB1dGUgdGhlIGFubnVhbGl6ZWQgc3RhbmRhcmQgZGV2aWF0aW9uDQpTdGREZXYuYW5udWFsaXplZChzcDUwMF9yZXR1cm5zKQ0KDQojIENvbXB1dGUgdGhlIGFubnVhbGl6ZWQgU2hhcnBlIHJhdGlvOiBhbm5fc2hhcnBlDQphbm5fc2hhcnBlIDwtIFJldHVybi5hbm51YWxpemVkKHNwNTAwX3JldHVybnMpIC8gU3RkRGV2LmFubnVhbGl6ZWQoc3A1MDBfcmV0dXJucykNCg0KIyBDb21wdXRlIGFsbCBvZiB0aGUgYWJvdmUgYXQgb25jZSB1c2luZyB0YWJsZS5Bbm51YWxpemVkUmV0dXJucygpDQp0YWJsZS5Bbm51YWxpemVkUmV0dXJucyhzcDUwMF9yZXR1cm5zKQ0KYGBgDQpBbm51YWxpemluZyByZXR1cm5zIGFsbG93cyB5b3UgdG8gbG9vayBhdCBwZXJmb3JtYW5jZSBvdmVyIGEgbG9uZ2VyIHBlcmlvZCBvZiB0aW1lLg0KDQojIyBSb2xsaW5nIGFubnVhbGl6ZWQgbWVhbiBhbmQgdm9sYXRpbGl0eQ0KSW4gcHJldmlvdXMgZXhlcmNpc2VzLCB5b3UgaGF2ZSBhbHJlYWR5IGZhbWlsaWFyaXplZCB5b3Vyc2VsZiB3aXRoIHRoZSBSZXR1cm4uYW5udWFsaXplZCgpIGFuZCBTdGREZXYuYW5udWFsaXplZCgpIGZ1bmN0aW9ucy4gSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgYWxzbyB1c2UgdGhlIGZ1bmN0aW9uIFNoYXJwZVJhdGlvLmFubnVhbGl6ZWQoKSB0byBjYWxjdWxhdGUgdGhlIGFubnVhbGl6ZWQgU2hhcnBlIFJhdGlvLiBUaGlzIGZ1bmN0aW9uIHRha2VzIHRoZSBhcmd1bWVudHMgUiwgYW5kIFJmLiBUaGUgUiBhcmd1bWVudCB0YWtlcyBhbiB4dHMsIHZlY3RvciwgbWF0cml4LCBkYXRhLmZyYW1lLCB0aW1lU2VyaWVzLCBvciB6b28gb2JqZWN0IG9mIGFzc2V0IHJldHVybnMuIFRoZSBSZiBhcmd1bWVudCBpcyBuZWNlc3NhcnkgaW4gU2hhcnBlUmF0aW8uYW5udWFsaXplZCgpLCBhcyBpdCB0YWtlcyBpbnRvIGFjY291bnQgdGhlIHJpc2stZnJlZSByYXRlIGluIHRoZSBzYW1lIHBlcmlvZCBvZiB5b3VyIHJldHVybnMuIEZvciB0aGlzIGV4YW1wbGUsIHlvdSBjYW4gdXNlIHRoZSBvYmplY3QgcmYgdG8gZnVsZmlsbCB0aGUgUmYgYXJndW1lbnQuDQoNClRoZSBmdW5jdGlvbiBjaGFydC5Sb2xsaW5nUGVyZm9ybWFuY2UoKSBtYWtlcyBpdCBlYXN5IHRvIHZpc3VhbGl6ZSB0aGUgcm9sbGluZyBlc3RpbWF0ZXMgb2YgcGVyZm9ybWFuY2UgaW4gUi4gRmFtaWxpYXJpemUgeW91cnNlbGYgZmlyc3Qgd2l0aCB0aGUgc3ludGF4IG9mIHRoaXMgZnVuY3Rpb24uIEl0IHJlcXVpcmVzIHlvdSB0byBzcGVjaWZ5IHRoZSB0aW1lIHNlcmllcyBvZiBwb3J0Zm9saW8gcmV0dXJucyAoYnkgc2V0dGluZyB0aGUgYXJndW1lbnQgUiksIHRoZSBsZW5ndGggb2YgdGhlIHdpbmRvdyAod2lkdGgpLCBhbmQgdGhlIGZ1bmN0aW9uIHVzZWQgdG8gY29tcHV0ZSB0aGUgcGVyZm9ybWFuY2UgKGFyZ3VtZW50IEZVTikuIFRvIHNlZSBhbGwgdGhyZWUgcGxvdHMgdG9nZXRoZXIsIFBlcmZvcm1hbmNlQW5hbHl0aWNzIHByb3ZpZGVzIGEgc2hvcnRjdXQgZnVuY3Rpb24gY2hhcnRzLlJvbGxpbmdQZXJmb3JtYW5jZSgpLiBOb3RlIHRoZSBjaGFydHMgaW5zdGVhZCBvZiBjaGFydC4gVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGFsbCBvZiB0aGUgcHJldmlvdXMgY2hhcnRzIGF0IG9uY2UgYW5kIGRvZXMgbm90IHVzZSB0aGUgYXJndW1lbnQgRlVOIQ0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgbWVhbiwgdm9sYXRpbGl0eSwgYW5kIFNoYXJwZSByYXRpbyBvZiBzcDUwMF9yZXR1cm5zDQpyZXR1cm5zX2FubiA8LSBSZXR1cm4uYW5udWFsaXplZChzcDUwMF9yZXR1cm5zKQ0Kc2RfYW5uIDwtIFN0ZERldi5hbm51YWxpemVkKHNwNTAwX3JldHVybnMpDQpzaGFycGVfYW5uIDwtU2hhcnBlUmF0aW8uYW5udWFsaXplZChzcDUwMF9yZXR1cm5zLCBSZiA9IHJmKQ0KDQojIFBsb3R0aW5nIHRoZSAxMi1tb250aCByb2xsaW5nIGFubnVhbGl6ZWQgbWVhbg0KY2hhcnQuUm9sbGluZ1BlcmZvcm1hbmNlKFIgPSBzcDUwMF9yZXR1cm5zLCB3aWR0aCA9IDEyLCBGVU4gPSAiUmV0dXJuLmFubnVhbGl6ZWQiKQ0KDQojIFBsb3R0aW5nIHRoZSAxMi1tb250aCByb2xsaW5nIGFubnVhbGl6ZWQgc3RhbmRhcmQgZGV2aWF0aW9uDQpjaGFydC5Sb2xsaW5nUGVyZm9ybWFuY2UoUiA9IHNwNTAwX3JldHVybnMsIHdpZHRoID0gMTIsIEZVTiA9ICJTdGREZXYuYW5udWFsaXplZCIpDQoNCiMgUGxvdHRpbmcgdGhlIDEyLW1vbnRoIHJvbGxpbmcgYW5udWFsaXplZCBTaGFycGUgcmF0aW8NCmNoYXJ0LlJvbGxpbmdQZXJmb3JtYW5jZShSID0gc3A1MDBfcmV0dXJucywgd2lkdGggPSAxMiwgRlVOID0gIlNoYXJwZVJhdGlvLmFubnVhbGl6ZWQiLCBSZiA9IHJmKQ0KYGBgDQpOb3RlIHRoYXQgdGhlIGR5bmFtaWNzIGluIHRoZSByZXR1cm4sIHJpc2ssIGFuZCByaXNrLWFkanVzdGVkIHJldHVybnMgYXJlIHJlY3VycmVudC4gSW52ZXN0b3JzIGNhbiB0aHVzIGNvbXBhcmUgY3VycmVudCByb2xsaW5nIHJpc2sgYW5kIHJldHVybiBzdGF0aXN0aWNzIHdpdGggcGFzdCB2YWx1ZXMgdG8gcGlucG9pbnQgdGhlIHBhc3QgcGVyaW9kcyBpbiB3aGljaCBwZXJmb3JtYW5jZSBoYXMgYmVlbiBzaW1pbGFyLg0KDQojIyMgU3VicGVyaW9kIHBlcmZvcm1hbmNlIGFuYWx5c2lzIGFuZCB0aGUgZnVuY3Rpb24gd2luZG93DQoNCkluIHRoZSBwcmV2aW91cyBleGVyY2lzZSwgeW91IGNvbXB1dGVkIHRoZSBwZXJmb3JtYW5jZSBtZWFzdXJlIG9uIGVhY2ggcG9zc2libGUgc2FtcGxlIG9mIGEgZml4ZWQgc2l6ZSBieSByb2xsaW5nIHRocm91Z2ggdGltZS4gT2Z0ZW4gaW52ZXN0b3JzIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBwZXJmb3JtYW5jZSBvZiBhIHNwZWNpZmljIHN1YndpbmRvdy4gWW91IGNhbiBjcmVhdGUgc3Vic2V0cyBvZiBhIHRpbWUgc2VyaWVzIGluIFIgdXNpbmcgdGhlIGZ1bmN0aW9uIHdpbmRvdygpLiBUaGUgZmlyc3QgYXJndW1lbnQgaXMgdGhlIHJldHVybiBzZXJpZXMgdGhhdCBuZWVkcyBzdWJzZXR0aW5nLiBUaGUgc2Vjb25kIGFyZ3VtZW50IGlzIHRoZSBzdGFydGluZyBkYXRlIG9mIHRoZSBzdWJzZXQgaW4gdGhlIGZvcm0gIllZWVktTU0tREQiLCBhbmQgdGhlIHRoaXJkIGFyZ3VtZW50IGlzIHRoZSBlbmRpbmcgZGF0ZSBpbiB0aGUgc2FtZSBmb3JtYXQuDQpgYGB7cn0NCmNvbiA8LSB1cmwoImh0dHBzOi8vZmluYW5jZS55YWhvby5jb20iKQ0KaWYoIWluaGVyaXRzKHRyeShvcGVuKGNvbiksIHNpbGVudCA9IFRSVUUpLCAidHJ5LWVycm9yIikpIHsNCiAgY2xvc2UoY29uKQ0KICBzcDUwMCA8LSBnZXQuaGlzdC5xdW90ZShpbnN0cnVtZW50ID0gIl5HU1BDIiwgc3RhcnQgPSAiMTk4Ni0wMS0wMSIsIGVuZCA9ICIyMDE2LTA4LTMxIiwNCiAgICAgICAgICAgICAgICAgICAgICBxdW90ZSA9ICJDbG9zZSIsIGNvbXByZXNzaW9uID0gImQiKQ0KfQ0KaGVhZChzcDUwMCkNCmBgYA0KDQpgYGB7cn0NCnNwNTAwX3JldHVybnMgIDwtIFJldHVybi5jYWxjdWxhdGUoc3A1MDAkQ2xvc2UpDQpzcDUwMF9yZXR1cm5zIDwtIHNwNTAwX3JldHVybnNbLTEsXQ0KIyBGaWxsIGluIHdpbmRvdyBmb3IgMjAwOA0Kc3A1MDBfMjAwOCA8LSB3aW5kb3coc3A1MDBfcmV0dXJucywgc3RhcnQgPSAiMjAwOC0wMS0wMSIsIGVuZCA9ICIyMDA4LTEyLTMxIikNCg0KIyBDcmVhdGUgd2luZG93IGZvciAyMDE0DQpzcDUwMF8yMDE0IDwtIHdpbmRvdyhzcDUwMF9yZXR1cm5zLCBzdGFydCA9ICIyMDE0LTAxLTAxIiwgZW5kID0gIjIwMTQtMTItMzEiKQ0KDQojIFBsb3R0aW5nIHNldHRpbmdzDQpwYXIobWZyb3cgPSBjKDEsIDIpICwgbWFyPWMoMywgMiwgMiwgMikpDQpuYW1lcyhzcDUwMF8yMDA4KSA8LSAic3A1MDBfMjAwOCINCm5hbWVzKHNwNTAwXzIwMTQpIDwtICJzcDUwMF8yMDE0Ig0KYGBgDQoNClBsb3QgaGlzdG9ncmFtIG9mIHJldHVybnMgaW4gMjAwOCB1c2luZyB0aGUgZnVuY3Rpb24gY2hhcnQuSGlzdG9ncmFtKCkuIFNldCB0aGUgYXJndW1lbnQgbWV0aG9kcyA9IGMoImFkZC5kZW5zaXR5IiwgImFkZC5ub3JtYWwiKSB0byB2aXN1YWxpemUgdGhlIG5vbi1wYXJhbWV0cmljIGVzdGltYXRlIG9mIHRoZSBkZW5zaXR5IGFuZCB0aGUgZGVuc2l0eSB1bmRlciBhbiBhc3N1bWVkIG5vcm1hbCBkaXN0cmlidXRpb24uDQoNCmBgYHtyfQ0KIyBQbG90IGhpc3RvZ3JhbSBvZiAyMDA4DQpjaGFydC5IaXN0b2dyYW0oc3A1MDBfMjAwOCwgbWV0aG9kcyA9IGMoImFkZC5kZW5zaXR5IiwgImFkZC5ub3JtYWwiKSkNCg0KIyBQbG90IGhpc3RvZ3JhbSBvZiAyMDE0DQpjaGFydC5IaXN0b2dyYW0oc3A1MDBfMjAxNCwgbWV0aG9kcyA9IGMoImFkZC5kZW5zaXR5IiwgImFkZC5ub3JtYWwiKSkNCmBgYA0KIyMgTm9uLW5vcm1hbGl0eSBvZiB0aGUgcmV0dXJuIGRpc3RyaWJ1dGlvbg0KDQojIyMgQmFsYW5jaW5nIHJpc2sgYW5kIHJld2FyZA0KDQoNClRoZSB4LWF4aXMgY29ycmVzcG9uZHMgdG8gdGhlIHJldHVybiBvZiBhbiBhc3NldCwgYW5kIHRoZSB5LWF4aXMgaXMgdGhlIGNvcnJlc3BvbmRpbmcgZGVuc2l0eSwgaW5kaWNhdGluZyBob3cgbGlrZWx5IHRoYXQgcmV0dXJuIHZhbHVlIGlzLiBUYWtlIGEgbW9tZW50IHRvIGxvb2sgb3ZlciB0aGVzZSBjaGFydHMuDQoNCk5vdGljZSB0aGF0IHRoZSBkaXN0cmlidXRpb25zIGFyZSBhbGwgc3ltbWV0cmljLCBhbmQgdGhlIGNlbnRlciBvZiB0aGUgZGlzdHJpYnV0aW9uIGNvcnJlc3BvbmRzIHRvIHRoZSBhdmVyYWdlIHJldHVybi4NCg0KUmV0dXJuIGRpc3RyaWJ1dGlvbnMgQSBhbmQgQiBoYXZlIHRoZSBzYW1lIGV4cGVjdGVkIHJldHVybi4gQnV0IGRpc3RyaWJ1dGlvbiBCIGhhcyBoaWdoZXIgdm9sYXRpbGl0eSBhbmQgdGhlcmVmb3JlIGhhcyBhIHdpZGVyIHJhbmdlIG9mIHBvc3NpYmxlIG91dGNvbWVzLiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSByaXNrIG9mIGEgbGFyZ2UgZGV2aWF0aW9uIGZyb20gdGhlIG1lYW4gcmV0dXJuIGlzIGhpZ2hlciBmb3IgaW52ZXN0aW5nIGluIEIgdGhhbiBmb3IgaW52ZXN0aW5nIGluIEEuIERpc3RyaWJ1dGlvbiBDIGhhcyB0aGUgc2FtZSBsZXZlbCBvZiB2b2xhdGlsaXR5IGFzIERpc3RyaWJ1dGlvbiBBIGJ1dCBhIGhpZ2hlciBtZWFuIHJldHVybi4NCkRpc3RyaWJ1dGlvbiBDIGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIHJldHVybiB3aXRoIHRoZSBsb3dlc3Qgdm9sYXRpbGl0eS4NCg0KIyMjIERldGVjdGluZyBub24tbm9ybWFsaXR5IHVzaW5nIHNrZXduZXNzIGFuZCBrdXJ0b3Npcw0KDQoNClJldHVybnMgYXJlIG1vc3Qgb2Z0ZW4gbm9uLW5vcm1hbCBpbiBuYXR1cmUuIFR3byBtZXRyaWNzIGtleSB0byB1bmRlcnN0YW5kaW5nIHRoZSBkaXN0cmlidXRpb24gb2Ygbm9uLW5vcm1hbCByZXR1cm5zIGFyZSBza2V3bmVzcyBhbmQga3VydG9zaXMuIFRoZSBza2V3bmVzcyB3aWxsIGhlbHAgeW91IGlkZW50aWZ5IHdoZXRoZXIgb3Igbm90IG5lZ2F0aXZlIG9yIHBvc2l0aXZlIHJldHVybnMgb2NjdXIgbW9yZSBmcmVxdWVudGx5LiBOZWdhdGl2ZSBza2V3bmVzcyBpbmRpY2F0ZXMgdGhhdCBsYXJnZSBuZWdhdGl2ZSByZXR1cm5zIG9jY3VyIG1vcmUgb2Z0ZW4gdGhhbiBsYXJnZSBwb3NpdGl2ZSBvbmVzLCBhbmQgdmljZSB2ZXJzYS4NCg0KS3VydG9zaXMgd2lsbCBiZSBwb3NpdGl2ZSBpZiB0aGVyZSBhcmUgZmF0IHRhaWxzIGluIHlvdXIgZGlzdHJpYnV0aW9uLiBUaGlzIG1lYW5zIHRoYXQgbGFyZ2UgcG9zaXRpdmUgb3IgbmVnYXRpdmUgcmV0dXJucyB3aWxsIGhhcHBlbiBtb3JlIG9mdGVuIHRoYW4gY2FuIGJlIGFzc3VtZWQgdW5kZXIgYSBub3JtYWwgZGlzdHJpYnV0aW9uLg0KDQpgYGB7cn0NCnNwNTAwX2RhaWx5IDwtIHNwNTAwX3JldHVybnMNCnNwNTAwX21vbnRobHkgPC0gdG8ubW9udGhseShzcDUwMCRDbG9zZSwgaW5kZXhBdCA9ICJlbmRvZiIpDQpzcDUwMF9tb250aGx5IDwtIENhbGN1bGF0ZVJldHVybnMoc3A1MDBfbW9udGhseSkNCnNwNTAwX21vbnRobHkgPC0gc3A1MDBfbW9udGhseVstMSwgNF0NCnBhcihtZnJvdyA9IGMoMSwgMikgLCBtYXI9YygzLCAyLCAyLCAyKSkNCiMgUGxvdCBoaXN0b2dyYW0gb2YgMjAwOA0KY2hhcnQuSGlzdG9ncmFtKHNwNTAwX2RhaWx5LCBtZXRob2RzID0gYygiYWRkLmRlbnNpdHkiLCAiYWRkLm5vcm1hbCIpKQ0KIyBQbG90IGhpc3RvZ3JhbSBvZiAyMDA4DQpjaGFydC5IaXN0b2dyYW0oc3A1MDBfbW9udGhseSwgbWV0aG9kcyA9IGMoImFkZC5kZW5zaXR5IiwgImFkZC5ub3JtYWwiKSkNCmBgYA0KVGhlIGhpc3RvZ3JhbXMgaW4gdGhlIHBsb3QgZW52aXJvbm1lbnQgY29tcGFyZSB0aGUgZGFpbHkgYW5kIG1vbnRobHkgcmV0dXJucyBvZiB0aGUgUyZQIDUwMCBvdmVyIHRoZSBwZXJpb2Qgb2YgMTk4NiB1bnRpbCB0b2RheS4gVGhlcmUgc2VlbXMgdG8gYmUgYSBuZWdhdGl2ZSBza2V3bmVzcygpIGluIHRoZXNlIHBsb3RzLCBhbmQgYSBzb21ld2hhdCBncmVhdGVyIHRoYW4gbm9ybWFsIGt1cnRvc2lzKCkuIE5vdGUgdGhhdCwgYnkgZGVmYXVsdCwga3VydG9zaXMoKSByZXBvcnRzIHRoZSBleGNlc3Mga3VydG9zaXMgKHRoYXQgaXMsIHRoZSBrdXJ0b3NpcyBtaW51cyB0aHJlZSkuIExldCdzIHNlZSBpZiB0aGUgbnVtYmVycyBtYXRjaCBvdXIgb2JzZXJ2YXRpb25zIQ0KYGBge3J9DQojICBDb21wdXRlIHRoZSBza2V3bmVzcyANCnNrZXduZXNzKHNwNTAwX2RhaWx5KQ0Kc2tld25lc3Moc3A1MDBfbW9udGhseSkgDQoNCiMgQ29tcHV0ZSB0aGUgZXhjZXNzIGt1cnRvc2lzIA0Ka3VydG9zaXMoc3A1MDBfZGFpbHkpDQprdXJ0b3NpcyhzcDUwMF9tb250aGx5KQ0KYGBgDQpUaGUgZXhjZXNzIGt1cnRvc2lzIGNhbiBiZSB0aG91Z2h0IG9mIGFzIGhvdyBmYXQgdGhlIHRhaWxzIG9mIHRoZSByZXR1cm4gZGlzdHJpYnV0aW9uIGFyZSBjb21wYXJlZCB0byB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbi4NCg0KIyMjIERvd25zaWRlIHJpc2sgbWVhc3VyZXMNCg0KVGhlIHN0YW5kYXJkIGRldmlhdGlvbiBnaXZlcyBlcXVhbCB3ZWlnaHQgdG8gcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHJldHVybnMgaW4gY2FsY3VsYXRpbmcgdGhlIHJldHVybiB2YXJpYWJpbGl0eS4gV2hlbiB0aGUgcmV0dXJuIGRpc3RyaWJ1dGlvbiBpcyBhc3ltbWV0cmljIChza2V3ZWQpLCBpbnZlc3RvcnMgdXNlIGFkZGl0aW9uYWwgcmlzayBtZWFzdXJlcyB0aGF0IGZvY3VzIG9uIGRlc2NyaWJpbmcgdGhlIHBvdGVudGlhbCBsb3NzZXMuIE9uZSBzdWNoIG1lYXN1cmUgb2YgZG93bnNpZGUgcmlzayBpcyB0aGUgU2VtaS1EZXZpYXRpb24uIFRoZSBTZW1pLURldmlhdGlvbiBpcyB0aGUgY2FsY3VsYXRpb24gb2YgdGhlIHZhcmlhYmlsaXR5IG9mIHJldHVybnMgYmVsb3cgdGhlIG1lYW4gcmV0dXJuLg0KDQpBbm90aGVyIG1vcmUgcG9wdWxhciBtZWFzdXJlIGlzIHRoZSBzby1jYWxsZWQgVmFsdWUtYXQtUmlzayAob3IgVmFSKS4gTG9vc2VseSBzcGVha2luZywgdGhlIFZhUiBjb3JyZXNwb25kcyB0byB0aGUgNSUgcXVhbnRpbGUgb2YgdGhlIHJldHVybiBkaXN0cmlidXRpb24sIG1lYW5pbmcgdGhhdCBhIG1vcmUgbmVnYXRpdmUgcmV0dXJuIGNhbiBvbmx5IGhhcHBlbiB3aXRoIGEgcHJvYmFiaWxpdHkgb2YgNSUuIEZvciBleGFtcGxlIHlvdSBtaWdodCBhc2s6ICJ3aGF0IGlzIHRoZSBsYXJnZXN0IGxvc3MgSSBjb3VsZCBwb3RlbnRpYWxseSB0YWtlIHdpdGhpbiB0aGUgbmV4dCBxdWFydGVyIHN1Y2ggdGhhdCBJIG9ubHkgaGF2ZSA1JSBwcm9iYWJpbGl0eSBvZiBvYnNlcnZpbmcgYW4gZXZlbiBsYXJnZXIgbG9zcz8iDQoNClRoZSBleHBlY3RlZCBzaG9ydGZhbGwgaXMgYW5vdGhlciBtZWFzdXJlIG9mIHJpc2sgdGhhdCBmb2N1c2VzIG9uIHRoZSBhdmVyYWdlIGxvc3MgYmVsb3cgdGhlIDUlIFZhUiBxdWFudGlsZS4NCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgZXhhbWluZSB0aGUgcG90ZW50aWFsIHJpc2sgb2YgdGhlIG1vbnRobHkgcmV0dXJucyBvZiB0aGUgUyZQIDUwMC4gWW91IHdpbGwgdXNlIHRoZSBmdW5jdGlvbnMgU2VtaURldmlhdGlvbigpLCBWYVIoKSwgYW5kIEVTKCkuIFRoZXNlIGZ1bmN0aW9ucyBhbGwgcmVxdWlyZSB0aGUgYXJndW1lbnQgUiwgd2hpY2ggaXMgYW4geHRzLCB2ZWN0b3IsIG1hdHJpeCwgZGF0YS5mcmFtZSwgdGltZVNlcmllcywgb3Igem9vIG9iamVjdCBvZiBhc3NldCByZXR1cm5zLiBIb3dldmVyIHRoZSBWYVIoKSwgYW5kIEVTKCkgcmVxdWlyZSBhbm90aGVyIGFyZ3VtZW50IHAsIG9yIGxvc3MgcHJvYmFiaWxpdHkgbGV2ZWwsIHdoaWNoIGlzIHAgPSAwLjA1IGluIGNhc2Ugb2YgdGhlIDUlIFZhUiBhbmQgRVMuDQoNClVzZSB0aGUgZnVuY3Rpb24gVmFSKCkgdG8gZXN0aW1hdGUgdGhlIDUlIGFuZCAyLjUlIHZhbHVlLWF0LXJpc2sgb2YgYSBtb250aGx5IGludmVzdG1lbnQgaW4gUyZQIDUwMCByZXR1cm5zLg0KDQpVc2UgdGhlIGZ1bmN0aW9uIEVTKCkgdG8gZXN0aW1hdGUgdGhlIDUlIGFuZCAyLjUlIGV4cGVjdGVkIHNob3J0ZmFsbCBvZiBhIG1vbnRobHkgaW52ZXN0bWVudCBpbiBTJlAgNTAwIHJldHVybnMuDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBTZW1pRGV2aWF0aW9uDQpTZW1pRGV2aWF0aW9uKHNwNTAwX21vbnRobHkpDQoNCiMgQ2FsY3VsYXRlIHRoZSB2YWx1ZSBhdCByaXNrDQpWYVIoc3A1MDBfbW9udGhseSwgcCA9IDAuMDUpDQpWYVIoc3A1MDBfbW9udGhseSwgcCA9IDAuMDI1KQ0KDQojIENhbGN1bGF0ZSB0aGUgZXhwZWN0ZWQgc2hvcnRmYWxsDQpFUyhzcDUwMF9tb250aGx5LCBwID0gMC4wNSkNCkVTKHNwNTAwX21vbnRobHksIHAgPSAwLjAyNSkNCmBgYA0KWW91IG5vdyBrbm93IGhvdyB0byBjYWxjdWxhdGUgdGhlIHBvdGVudGlhbCByaXNrIG9mIHlvdXIgaW52ZXN0bWVudHMhIEVhY2ggc3RhdGlzdGljIHRlbGxzIHlvdSBzb21ldGhpbmcgc2xpZ2h0bHkgZGlmZmVyZW50LiBJdCBpcyBpbXBvcnRhbnQgdGhhdCB5b3UgcmVtZW1iZXIgd2hhdCB5b3VyIHJpc2sgbWVhc3VyZXMgYXJlIQ0KDQojIyMgRHJhd2Rvd25zIGR1ZSB0byBidXlpbmcgaGlnaCwgc2VsbGluZyBsb3cNCg0KVGhlIHZvbGF0aWxpdHksIHNlbWktZGV2aWF0aW9uLCB2YWx1ZS1hdC1yaXNrLCBhbmQgZXhwZWN0ZWQgc2hvcnRmYWxsIGFyZSBhbGwgbWVhc3VyZXMgdGhhdCBkZXNjcmliZSByaXNrIG92ZXIgb25lIHBlcmlvZC4gVGhlc2UgbWV0cmljcyBkbyBub3QgZG8gYSBncmVhdCBqb2IgYXQgZGVzY3JpYmluZyB0aGUgd29yc3QtY2FzZSByaXNrIG9mIGJ1eWluZyBhdCBhIHBlYWsgYW5kIHNlbGxpbmcgYXQgYSB0cm91Z2guIFRoaXMgc29ydCBvZiByaXNrIGNhbiBiZSBxdWFudGlmaWVkIGJ5IGFuYWx5emluZyB0aGUgcG9ydGZvbGlvJ3MgZHJhd2Rvd25zLCBvciBwZWFrLXRvLXRyb3VnaCBkZWNsaW5lIGluIGN1bXVsYXRpdmUgcmV0dXJucy4NClRoZSBmdW5jdGlvbiB0YWJsZS5EcmF3ZG93bnMoKSBpbiBQZXJmb3JtYW5jZUFuYWx5dGljcyByZXBvcnRzIHRoZSBmaXZlIGxhcmdlc3QgZHJhd2Rvd24gZXBpc29kZXMgb3ZlciBhIHNhbXBsZSBwZXJpb2QuIFRoZSBwYWNrYWdlIGFsc28gaGFzIGFub3RoZXIgZnVuY3Rpb24gY2hhcnQuRHJhd2Rvd24oKSB0byB2aXN1YWxpemUgdGhlIGV2b2x1dGlvbiBvZiB0aGUgZHJhd2Rvd25zIGZyb20gZWFjaCBwZWFrIG92ZXIgdGltZS4NCllvdSB3aWxsIG5vdyBleHBsb3JlIHRoZSBoaXN0b3JpYyBkcmF3ZG93biBlcGlzb2RlcyBvZiB0aGUgbW9udGhseSByZXR1cm5zIG9mIHRoZSBTJlAgNTAwLiBUaGUgcmV0dXJucyBhcmUgbG9hZGVkIGludG8geW91ciB3b3Jrc3BhY2UgYXMgc3A1MDBfbW9udGhseS4NCg0KYGBge3J9DQojIFRhYmxlIG9mIGRyYXdkb3ducw0KdGFibGUuRHJhd2Rvd25zKHNwNTAwX21vbnRobHkpDQoNCiMgUGxvdCBvZiBkcmF3ZG93bnMNCmNoYXJ0LkRyYXdkb3duKHNwNTAwX21vbnRobHkpDQpgYGANCiMgUGVyZm9ybWFuY2UgZHJpdmVycw0KDQojIyBEcml2ZXJzIGluIHRoZSBjYXNlIG9mIHR3byBhc3NldHMNCg0KIyMjIERyaXZlciAxOiBUaGUgYXNzZXRzJyBpbmRpdmlkdWFsIHBlcmZvcm1hbmNlDQoNCnRocmVlIHR5cGVzIG9mIGRyaXZlcnMgZm9yIGEgcG9ydGZvbGlvJ3MgcGVyZm9ybWFuY2U6IChpKSB0aGUgaW5kaXZpZHVhbCBwZXJmb3JtYW5jZSBvZiB0aGUgYXNzZXRzIGluIHRlcm1zIG9mIHJpc2sgYW5kIHJldHVybiwgKGlpKSB0aGUgcG9ydGZvbGlvIHdlaWdodCBvZiBlYWNoIG9mIHRoZSBhc3NldHMsIChpaWkpIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBhc3NldCByZXR1cm5zLg0KDQpOb3cgbGV0J3MgYW5hbHl6ZSBzb21lIGRhdGEgdG8gZ2V0IGEgYmV0dGVyIGdyYXNwIG9mIHRoaXMgcmVzdWx0ISBJbiB0aGlzIGV4YW1wbGUsIHlvdSB3aWxsIGNvbnNpZGVyIGludmVzdGluZyDigJMgd2l0aCBtb250aGx5IGZyZXF1ZW5jeSDigJMgaW4gVS5TLiBlcXVpdGllcyBhbmQgVS5TLiBib25kcy4gRWFjaCBhc3NldHMnIHJldHVybnMgYXJlIHN0b3JlZCBpbiB5b3VyIHdvcmtzcGFjZSBhcyByZXR1cm5zX2VxdWl0aWVzLCBhbmQgcmV0dXJuc19ib25kcy4gSW4gYWRkaXRpb24sIHRoZSBwb3J0Zm9saW8gaXMgaW52ZXN0ZWQgNjAvNDAsIG1lYW5pbmcgdGhhdCBldmVyeSBtb250aCB5b3UgaW52ZXN0IDYwJSBpbiBlcXVpdGllcyBhbmQgNDAlIGJvbmRzLiBUaGVzZSBwb3J0Zm9saW8gcmV0dXJucyBhcmUgc3RvcmVkIGFzIHJldHVybnNfNjA0MC4NCmBgYHtyfQ0KZXFfcHJpY2VzIDwtIHJlYWRSRFMoImRhdGEvZXFfcHJpY2VzLlJEYXRhIikNCmhlYWQoZXFfcHJpY2VzKQ0KYm9uZF9wcmljZXMgPC0gcmVhZFJEUygiZGF0YS9ib25kX3ByaWNlcy5SRGF0YSIpDQpoZWFkKGJvbmRfcHJpY2VzKQ0KcmV0dXJucyA8LSBtZXJnZShlcV9wcmljZXMsIGJvbmRfcHJpY2VzKQ0Kd2VpZ2h0cyA8LSBjKDAuNjAsIDAuNDApDQpwb3J0Zm9saW8gPC0gZXFfcHJpY2VzICogMC42MCArIGJvbmRfcHJpY2VzICowLjQwDQpoZWFkKHBvcnRmb2xpbykNCmBgYA0KYGBge3J9DQpwbG90KGVxX3ByaWNlcykNCmxpbmVzKGJvbmRfcHJpY2VzLCBjb2wgPSAicmVkIikNCmxpbmVzKGFzLnpvbyhwb3J0Zm9saW8pLCBjb2wgPSAiYmx1ZSIpDQpsZWdlbmQoeCA9ICJ0b3BsZWZ0IiwgbGVnZW5kID0gYygiRXF1aXRpZXMiLCAiQm9uZHMiLCAiNjAvNDAgUG9ydGZvbGlvIiksDQogICAgICAgY29sID0gYygiYmxhY2siLCAicmVkIiwgImJsdWUiKSwNCiAgICAgICBsdHkgPSBjKDEsIDEsIDEpKQ0KYGBgDQpUaGUgcG9ydGZvbGlvIG1lYW4gcmV0dXJuIGVxdWFscyB0aGUgcG9zaXRpb24gd2VpZ2h0ZWQgc3VtIG9mIHRoZSBjb21wb25lbnQgbWVhbiByZXR1cm5zLiBZb3UgY2FuIHRlc3QgdGhpcyB1c2luZzogYWxsLmVxdWFsKG1lYW4ocmV0dXJuc182MDQwKSwgMC42ICogbWVhbihyZXR1cm5zX2VxdWl0aWVzKSArIDAuNCAqIG1lYW4ocmV0dXJuc19ib25kcykpDQoNCiMjIyBEcml2ZXIgMjogVGhlIGNob2ljZSBvZiBwb3J0Zm9saW8gd2VpZ2h0cw0KDQpJbnZlc3RvcnMgY2FuIG9wdGltaXplIHRoZSBjaG9pY2Ugb2Ygd2VpZ2h0IHRvIG9idGFpbiB0aGUgaGlnaGVzdCByaXNrLWFkanVzdGVkIHJldHVybiwgYXMgbWVhc3VyZWQgYnkgdGhlIHBvcnRmb2xpbyBTaGFycGUgcmF0aW8uDQoNCkluIHRoZSBzcGVjaWFsIGNhc2Ugb2YgaW52ZXN0aW5nIHRoZSB0b3RhbCBwb3J0Zm9saW8gdmFsdWUgaW4gb25seSB0d28gYXNzZXRzLCB0aGVyZSBpcyBvbmx5IG9uZSB3ZWlnaHQgdG8gZGV0ZXJtaW5lLCBiZWNhdXNlIHRoZSB3ZWlnaHQgb24gdGhlIHNlY29uZCBhc3NldCBlcXVhbHMgb25lIG1pbnVzIHRoZSB3ZWlnaHQgb2YgdGhlIGZpcnN0IGFzc2V0Lg0KDQpMZXQgdXMgZG8gdGhpcyBpbiB0aGUgY2FzZSBvZiBhIHBvcnRmb2xpbyBpbnZlc3RlZCBpbiBVLlMuIGVxdWl0aWVzIGFuZCBVLlMuIGJvbmRzLiBXZSB3aWxsIGJlIHVzaW5nIHRoZSBicnV0ZSBmb3JjZSBhcHByb2FjaCBvZiB0cnlpbmcgYSBsYXJnZSBudW1iZXIgb2YgcG9zc2libGUgd2VpZ2h0cyBhbmQga2VlcGluZyB0aGUgd2VpZ2h0IHRoYXQgeWllbGRzIHRoZSBoaWdoZXN0IHZhbHVlIGZvciB0aGUgcG9ydGZvbGlvIFNoYXJwZSByYXRpbyAoYXNzdW1pbmcgYSB6ZXJvIHJpc2stZnJlZSByYXRlKS4NCg0KQ3JlYXRlIGEgdmVjdG9yIGNhbGxlZCBncmlkIHVzaW5nIHNlcSgpIHRoYXQgYmVnaW5zIGF0IDAsIGVuZHMgYXQgMSwgYnkgYW4gaW5jcmVtZW50IG9mIDAuMDEuDQpJbml0aWFsaXplIGVtcHR5IHZlY3RvciB2c2hhcnBlIHdpdGggdGhlIHNhbWUgbGVuZ3RoIGFzIGdyaWQuIEEgcG9wdWxhciB3YXkgb2YgZG9pbmcgdGhpcyBpcyBieSBjcmVhdGluZyBhIHZlY3RvciB0aGF0IGNvbnRhaW5zIE5BJ3MgdXNpbmcgZnVuY3Rpb24gcmVwKCkuIFlvdSB3aWxsIHJlcGxhY2UgdGhlc2UgTkEncyBpbiB0aGUgbG9vcCB0aGF0IHlvdSB3aWxsIGNyZWF0ZSBuZXh0Lg0KSW4gdGhlIGZvciBsb29wLCB5b3Ugd2lsbCBjb21wdXRlIHRoZSBTaGFycGUgcmF0aW8gZm9yIGVhY2ggb2YgdGhlIHBvc3NpYmxlIHdlaWdodHMgaW4gZ3JpZC4gVGhlIGZpcnN0IGNvbW1hbmQgaW4gdGhlIGZvci1sb29wIHNlbGVjdHMgdGhlIGktdGggZWxlbWVudCBvZiBncmlkIGFuZCBzdG9yZXMgaXQgaW4gb2JqZWN0IHdlaWdodCwgd2hpY2ggY2hhbmdlcyBpbiBlYWNoIGl0ZXJhdGlvbi4NCllvdSB3YW50IHRvIHNlZSBob3cgdGhlIHBvcnRmb2xpbyByZXR1cm4gY2hhbmdlcyB3aXRoIGEgY2hhbmdpbmcgd2VpZ2h0LiBDcmVhdGUgYW4gb2JqZWN0IHByZXR1cm5zIHRoYXQgZXF1YWxzIHRoZSBzdW0gb2Ygd2VpZ2h0IHRpbWVzIHJldHVybnNfZXF1aXRpZXMsIGFuZCAoMS13ZWlnaHQpIHRpbWVzIHJldHVybnNfYm9uZHMuDQpOZXh0LCB5b3Ugd2lsbCByZXBsYWNlIHRoZSBOQXMgaW4gdnNoYXJwZSB3aXRoIHRoZSBhbm51YWxpemVkIFNoYXJwZSByYXRpbyAoU2hhcnBlUmF0aW8uYW5udWFsaXplZCgpKSBvZiBwcmV0dXJucy4NCkZpbGwgaW4gdGhlIHBsb3QgZnVuY3Rpb24gd2hlcmUgcG90ZW50aWFsIHdlaWdodHMgKGdyaWQpIGlzIHBsb3R0ZWQgb24gdGhlIHgtYXhpcyBhbmQgdGhlIFNoYXJwZSByYXRpb3Mgb24gdGhlIHktYXhpcy4NCg0KYGBge3J9DQpyZXR1cm5zX2VxdWl0aWVzIDwtIFJldHVybi5jYWxjdWxhdGUoZXFfcHJpY2VzKQ0KcmV0dXJuc19ib25kcyA8LSBSZXR1cm4uY2FsY3VsYXRlKGJvbmRfcHJpY2VzKQ0KIyBDcmVhdGUgYSBncmlkDQpncmlkIDwtIHNlcShmcm9tID0gMCwgdG8gPSAxLCBieSA9IDAuMDEpDQoNCiMgSW5pdGlhbGl6ZSBhbiBlbXB0eSB2ZWN0b3IgZm9yIFNoYXJwZSByYXRpb3MNCnZzaGFycGUgPC0gcmVwKE5BLCB0aW1lcyA9IGxlbmd0aChncmlkKSkNCg0KIyBDcmVhdGUgYSBmb3IgbG9vcCB0byBjYWxjdWxhdGUgU2hhcnBlIHJhdGlvcw0KZm9yKGkgaW4gMTpsZW5ndGgoZ3JpZCkpIHsNCiAgICB3ZWlnaHQgPC0gZ3JpZFtpXQ0KICAgIHByZXR1cm5zIDwtIHdlaWdodCAqIHJldHVybnNfZXF1aXRpZXMgKyAoMSAtIHdlaWdodCkgKiByZXR1cm5zX2JvbmRzDQogICAgdnNoYXJwZVtpXSA8LSBTaGFycGVSYXRpby5hbm51YWxpemVkKHByZXR1cm5zKQ0KfQ0KDQojIFBsb3Qgd2VpZ2h0cyBhbmQgU2hhcnBlIHJhdGlvDQpwbG90KGdyaWQsIHZzaGFycGUsIHhsYWIgPSAiV2VpZ2h0cyIsIHlsYWI9ICJBbm4uIFNoYXJwZSByYXRpbyIpDQphYmxpbmUodiA9IGdyaWRbdnNoYXJwZSA9PSBtYXgodnNoYXJwZSldLCBsdHkgPSAzKQ0KYGBgDQpUaGUgcG9ydGZvbGlvIHRoYXQgaXMgMTElIGludmVzdGVkIGluIGVxdWl0aWVzLCA4OSUgaW4gYm9uZHMgaGFzIHRoZSBsYXJnZXN0IHBvc3NpYmxlIFNoYXJwZSByYXRpby4NCg0KIyMjIERyaXZlciAzOiBUaGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgYXNzZXQgcmV0dXJucw0KDQpUaGUgdGhpcmQgZHJpdmVyIG9mIHBvcnRmb2xpbyBwZXJmb3JtYW5jZSBpcyB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBhc3NldCByZXR1cm5zLiBHZW5lcmFsbHkgc3BlYWtpbmcsIHRoZSBjb3JyZWxhdGlvbiB0ZWxscyB5b3UgaG93IHR3byBhc3NldCByZXR1cm5zIHRlbmQgdG8gbW92ZSB0b2dldGhlci4NCg0KVGhlIGNvcnJlbGF0aW9uIG9mIGFzc2V0cyBoYXMgaW1wb3J0YW50IGNvbnNlcXVlbmNlcyBpbiBvdmVyYWxsIHBvcnRmb2xpbyBwZXJmb3JtYW5jZS4gVGhpcyBjb3JyZWxhdGlvbiBpcyBpbXBvcnRhbnQgYmVjYXVzZSBpdCBjYW4gcmVkdWNlIHZvbGF0aWxpdHkgdGhyb3VnaCBkaXZlcnNpZmljYXRpb24sIG9yIHJlZHVjaW5nIG92ZXJhbGwgY29ycmVsYXRpb24uIEluIGZhY3QsIHRoZSBsb3dlciB0aGUgY29ycmVsYXRpb24sIHRoZSBtb3JlIHN1Y2Nlc3NmdWwgdGhlIHBvcnRmb2xpbyB0ZW5kcyB0byBiZSBpbiByZWdhcmRzIHRvIHBhcnRpYWxseSBvZmZzZXR0aW5nIGxhcmdlIGxvc3NlcyBpbiBvbmUgYXNzZXQgd2l0aCBvbmx5IGEgbWlub3IgbG9zcywgb3IgZXZlbiBhIGdhaW4sIGluIGFub3RoZXIgYXNzZXQuDQoNCkluIHRoZSBleHRyZW1lIGNhc2Ugb2YgdHdvIGlkZW50aWNhbCBhc3NldCByZXR1cm5zLCB0aGUgY29ycmVsYXRpb24gd2lsbCBiZSAxLCBhbmQgdGhlcmUgaXMgbm8gZGl2ZXJzaWZpY2F0aW9uIHBvdGVudGlhbC4gSW4gdGhlIG90aGVyIGV4dHJlbWUgY2FzZSB3aGVyZSwgaWYgb25lIGFzc2V0IHJldHVybiBpcyBhYm92ZSBhdmVyYWdlLCBhbmQgdGhlIG90aGVyIGlzIGFsbW9zdCBhbHdheXMgYmVsb3cgYXZlcmFnZSwgdGhlIGNvcnJlbGF0aW9uIGlzIG5lZ2F0aXZlLiBUaGUgY29ycmVsYXRpb24gaXMgMCB3aGVuIHRoZSBhc3NldCByZXR1cm5zIGFyZSBsaW5lYXJseSBpbmRlcGVuZGVudCBvZiBlYWNoIG90aGVyLiBOb3RlIHRoYXQgaW50ZXJkZXBlbmRlbmN5IGNhbiBzdGlsbCBleGlzdCBvbiBhIG5vbi1saW5lYXIgbGV2ZWwgZXZlbiB3aGVuIHRoZSBjb3JyZWxhdGlvbiBpcyAwLg0KQXMgYW4gZXhlcmNpc2UsIHN1cHBvc2UgeW91IGhhdmUgYW4gZXF1YWxseSB3ZWlnaHRlZCBwb3J0Zm9saW8gb2YgdHdvIGFzc2V0cy4gVGhlaXIgY29ycmVsYXRpb24ganVtcHMgZnJvbSAwIHRvIDAuNS4gV2hhdCBoYXBwZW5zIHdpdGggdGhlIHZhcmlhbmNlPw0KYGBge3J9DQpyZXR1cm5zX2VxdWl0aWVzIDwtIFJldHVybi5jYWxjdWxhdGUoZXFfcHJpY2VzKQ0KcmV0dXJuc19lcXVpdGllcyA8LSByZXR1cm5zX2VxdWl0aWVzWy0xLF0NCnJldHVybnNfYm9uZHMgPC0gUmV0dXJuLmNhbGN1bGF0ZShib25kX3ByaWNlcykNCnJldHVybnNfYm9uZHMgPC0gcmV0dXJuc19ib25kc1stMSxdDQoNCnBmX3ZhciA8LSBmdW5jdGlvbiAoeCl7DQogICAgcmV0dXJuKCgwLjVeMikgKiB2YXIocmV0dXJuc19lcXVpdGllcykgKyAoMC41XjIpICogdmFyKHJldHVybnNfYm9uZHMpICsgDQogICAgICAgIDIgKiAwLjUgKiAwLjUgKiBzZChyZXR1cm5zX2VxdWl0aWVzKSAqIHNkKHJldHVybnNfYm9uZHMpICogeCkNCn0NCmBgYA0KZnVuY3Rpb24gcGZfdmFyKHgpIHRoYXQgY2FsY3VsYXRlcyB0aGUgcG9ydGZvbGlvIHZhcmlhbmNlIGZvciB0aGUgZXF1YWxseSB3ZWlnaHRlZCBwb3J0Zm9saW8gb2YgZXF1aXRpZXMgYW5kIGJvbmRzLCB3aGVuIHRoZWlyIGNvcnJlbGF0aW9uIGlzIGFzc3VtZWQgdG8gYmUgZXF1YWwgdG8geC4NCkhhdmluZyBjb3JyZWxhdGVkIGFzc2V0cyBjYW4gaW5jcmVhc2UgcG90ZW50aWFsIHJpc2suIENvcnJlbGF0ZWQgYXNzZXQgcHJpY2VzIHJpc2UgYW5kIGZhbGwgdG9nZXRoZXIuIFNvIHdoZW4geW91IHN1ZmZlciBhIGxvc3MgaW4gb25lIGFzc2V0LCB0aGVyZSBpcyBhIGdyZWF0ZXIgY2hhbmNlIG9mIHN1ZmZlcmluZyBhIGxvc3Mgb24gdGhlIG90aGVyIGFzc2V0IQ0KYGBge3J9DQpwZl92YXIoMCkNCnBmX3ZhcigwLjUpDQpgYGANCiMjIyBJbnRlcnByZXRpbmcgY29ycmVsYXRpb24NCg0KTm93IHlvdSB3aWxsIGxlYXJuIGhvdyB0byBjb21wdXRlIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGJvbmQgcmV0dXJucyBhbmQgZXF1aXR5IHJldHVybnMuIEp1c3QgbGlrZSB2b2xhdGlsaXRpZXMsIHRoZXNlIGNvcnJlbGF0aW9ucyBhcmUgZHluYW1pYy4gVGhlcmVmb3JlIHlvdSBuZWVkIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gYSBzdGF0aWMgYW5hbHlzaXMgdGhhdCBjYWxjdWxhdGVzIGNvcnJlbGF0aW9ucyBvdmVyIGEgY29tcGxldGUgc2FtcGxlIGFuZCBhIGR5bmFtaWMgYW5hbHlzaXMgdGhhdCBjYWxjdWxhdGVzIGNvcnJlbGF0aW9ucyBvdmVyIGEgcm9sbGluZyBzYW1wbGUuIFRoaXMgaXMgYSBzaW1pbGFyIGFuYWx5c2lzIGFzIHlvdSBkaWQgZm9yIHRoZSB0aW1lLXZhcnlpbmcgcGVyZm9ybWFuY2UgZXZhbHVhdGlvbiBpbiB0ZXJtcyBvZiBtZWFuIHJldHVybiBhbmQgdm9sYXRpbGl0eS4NCg0KSW4gdGhpcyBleGVyY2lzZSB5b3Ugd2lsbCBsZWFybiAzIG5ldyBmdW5jdGlvbnMgZnJvbSB0aGUgUGVyZm9ybWFuY2VBbmFseXRpY3MgcGFja2FnZTogY2hhcnQuU2NhdHRlcigpLCBjaGFydC5Db3JyZWxhdGlvbigpLCBhbmQgY2hhcnQuUm9sbGluZ0NvcnJlbGF0aW9uKCkuDQpgYGB7cn0NCiMgQ3JlYXRlIGEgc2NhdHRlciBwbG90DQpjaGFydC5TY2F0dGVyKHJldHVybnNfYm9uZHMsIHJldHVybnNfZXF1aXRpZXMsIHhsYWIgPSAiYm9uZCByZXR1cm5zIiwgeWxhYiA9ICJlcXVpdHkgcmV0dXJucyIsIG1haW4gPSAiYm9uZC1lcXVpdHkgcmV0dXJucyIpDQoNCiMgRmluZCB0aGUgY29ycmVsYXRpb24NCmNvcihyZXR1cm5zX2JvbmRzLCByZXR1cm5zX2VxdWl0aWVzKQ0KDQojIE1lcmdlIHJldHVybnNfYm9uZHMgYW5kIHJldHVybnNfZXF1aXRpZXMgDQpyZXR1cm5zIDwtIG1lcmdlKHJldHVybnNfYm9uZHMsIHJldHVybnNfZXF1aXRpZXMpDQoNCiMgRmluZCBhbmQgdmlzdWFsaXplIHRoZSBjb3JyZWxhdGlvbiB1c2luZyBjaGFydC5Db3JyZWxhdGlvbg0KY2hhcnQuQ29ycmVsYXRpb24ocmV0dXJucykgDQoNCiMgVmlzdWFsaXplIHRoZSByb2xsaW5nIGVzdGltYXRlcyB1c2luZyBjaGFydC5Sb2xsaW5nQ29ycmVsYXRpb24NCmNoYXJ0LlJvbGxpbmdDb3JyZWxhdGlvbihyZXR1cm5zX2JvbmRzLCByZXR1cm5zX2VxdWl0aWVzLCB3aWR0aCA9IDI0KQ0KYGBgDQojIyBVc2luZyBtYXRyaXggbm90YXRpb24NCg0KIyMjIE1ha2luZyBhIHJpc2stcmV3YXJkIHNjYXR0ZXIgZGlhZ3JhbQ0KDQpJbiB0aGlzIGV4YW1wbGUsIHlvdSBoYXZlIGRlY2lkZWQgdG8gZXh0ZW5kIHlvdXIgaW52ZXN0bWVudCBvcHBvcnR1bml0eSBieSBjcmVhdGluZyBhIHBvcnRmb2xpbyB0aGF0IGNvbnNpc3RzIG9mIFVTIGVxdWl0aWVzIEVURiAoU1BZKSwgVVMgYm9uZHMgRVRGIChBR0cpLCBhIHJlYWwgZXN0YXRlIGludmVzdG1lbnQgdHJ1c3QgKFZOUSksIGFuZCBhbiBFVEYgdHJhY2tpbmcgaW4gdGhlIEdTQ0kgY29tbW9kaXRpZXMgaW5kZXggKEdTRykuIFRoZSBwbG90IGluIHRoZSBlbnZpcm9ubWVudCBkaXNwbGF5cyB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlc2UgaW52ZXN0bWVudHMuDQpgYGB7cn0NCmNvbW1fcHJpY2VzIDwtIHJlYWRSRFMoImRhdGEvY29tbV9wcmljZXMuUkRhdGEiKQ0KcmVfcHJpY2VzIDwtIHJlYWRSRFMoImRhdGEvcmVfcHJpY2VzLlJEYXRhIikNCmBgYA0KYGBge3J9DQpwbG90KGVxX3ByaWNlcykNCmxpbmVzKGJvbmRfcHJpY2VzLCBjb2wgPSAicmVkIikNCmxpbmVzKHJlX3ByaWNlcywgY29sID0gImJsdWUiKQ0KbGluZXMoY29tbV9wcmljZXMsIGNvbCA9ICJwdXJwbGUiKQ0KbGVnZW5kKHggPSAidG9wbGVmdCIsIGxlZ2VuZCA9IGMoIkVxdWl0aWVzIiwgIkJvbmRzIiwgIlJlYWwgZXN0YXRlIiwgIkNvbW1vZGl0aWVzIiksDQogICAgICAgY29sID0gYygiYmxhY2siLCAicmVkIiwgImJsdWUiLCAicHVycGxlIiksDQogICAgICAgbHR5ID0gYygxLCAxLCAxLCAxKSkNCmBgYA0KWW91IGNhbiBhbHNvIHZpc3VhbGl6ZSDigJxyZWxhdGl2ZeKAnSBhdHRyYWN0aXZlbmVzcyBvZiB0aGUgaW52ZXN0bWVudHMgYnkgbWFraW5nIGEgc2NhdHRlciBwbG90IG9mIHRoZSBhdmVyYWdlIHJldHVybnMgYWdhaW5zdCB0aGUgcG9ydGZvbGlvIHZvbGF0aWxpdGllcy4gSW4gb3JkZXIgdG8gZG8gdGhpcywgeW91IG5lZWQgdG8gY2FsY3VsYXRlIHRoZSBhdmVyYWdlcyBhbmQgdm9sYXRpbGl0aWVzIGZvciBlYWNoIGFzc2V0LiBUaGlzIGNvcnJlc3BvbmRzIHRvIGVhY2ggY29sdW1uIGluIHRoZSByZXR1cm4gc2VyaWVzIHJldHVybnMuDQoNClRoZXNlIGNhbGN1bGF0aW9ucyBhcmUgbWFkZSBlYXN5IGJ5IHVzaW5nIHRoZSBmdW5jdGlvbiBhcHBseSgpIHdpdGggdGhlIGZpcnN0IGFyZ3VtZW50IGFzIHRoZSByZXR1cm4gZGF0YSwgdGhlIHNlY29uZCBhcmd1bWVudCBpcyB0aGUgdmFsdWUgb2YgMiBpbmRpY2F0aW5nIHRoYXQgdGhlIGNhbGN1bGF0aW9uIHNob3VsZCBiZSBjb2x1bW4td2lzZSwgd2hpbGUgdGhlIHRoaXJkIGFyZ3VtZW50IGlzIHRoZSBuYW1lIG9mIHRoZSBmdW5jdGlvbiB0aGF0IG5lZWRzIHRvIGJlIGFwcGxpZWQgb24gZWFjaCBjb2x1bW4uDQoNCmBgYHtyfQ0KZXFfcmV0dXJucyA8LSBSZXR1cm4uY2FsY3VsYXRlKGVxX3ByaWNlcykNCmVxX3JldHVybnMgPC0gZXFfcmV0dXJuc1stMSxdDQpib25kX3JldHVybnMgPC0gUmV0dXJuLmNhbGN1bGF0ZShib25kX3ByaWNlcykNCmJvbmRfcmV0dXJucyA8LSBib25kX3JldHVybnNbLTEsXQ0KY29tbV9yZXR1cm5zIDwtIFJldHVybi5jYWxjdWxhdGUoY29tbV9wcmljZXMpDQpjb21tX3JldHVybnMgPC0gY29tbV9yZXR1cm5zWy0xLF0NCnJlX3JldHVybnMgPC0gUmV0dXJuLmNhbGN1bGF0ZShyZV9wcmljZXMpDQpyZV9yZXR1cm5zIDwtIHJlX3JldHVybnNbLTEsXQ0KcmV0dXJucyA8LSBtZXJnZShlcV9yZXR1cm5zLCBib25kX3JldHVybnMsIGNvbW1fcmV0dXJucywgcmVfcmV0dXJucykNCmNvbG5hbWVzKHJldHVybnMpIDwtIGMoIkVxdWl0aWVzIiwgIkJvbmRzIiwgIkNvbW1vZGl0aWVzIiwgIlJlYWwgRXN0YXRlIikNCiMgQ3JlYXRlIGEgdmVjdG9yIG9mIHJldHVybnMgDQptZWFucyA8LSBhcHBseShyZXR1cm5zLCAyLCAibWVhbiIpDQogIA0KIyBDcmVhdGUgYSB2ZWN0b3Igb2Ygc3RhbmRhcmQgZGV2aWF0aW9uDQpzZHMgPC0gYXBwbHkocmV0dXJucywgMiwgInNkIikNCg0KIyBDcmVhdGUgYSBzY2F0dGVyIHBsb3QNCnBsb3Qoc2RzLCBtZWFucykNCnRleHQoc2RzLCBtZWFucywgbGFiZWxzID0gY29sbmFtZXMocmV0dXJucyksIGNleCA9IDAuNykNCmFibGluZShoID0gMCwgbHR5ID0gMykNCmBgYA0KIyMjIFRoZSBjb3ZhcmlhbmNlIG1hdHJpeA0KDQpUaGUgY292YXJpYW5jZSBtYXRyaXggaXMgY3J1Y2lhbCBpbiBkZXRlcm1pbmluZyB0aGUgcG9ydGZvbGlvIHZhcmlhbmNlIGluIHRoZSBnZW5lcmFsIGNhc2Ugb2YgTiBhc3NldHMuIFJlbWVtYmVyIHRoYXQgYW4gZWxlbWVudCBvbiByb3cgaSBhbmQgY29sdW1uIGogY29ycmVzcG9uZCB0byB0aGUgY292YXJpYW5jZSBvZiB0aGUgaSB0aCBhbmQgaiB0aCByZXR1cm4uIFJlY2FsbCBhbHNvIHRoYXQgdGhlIGNvdmFyaWFuY2Ugb2YgdHdvIHJldHVybiBzZXJpZXMgaXMgdGhlIHByb2R1Y3QgYmV0d2VlbiB0aGVpciB2b2xhdGlsaXRpZXMgYW5kIHRoZWlyIGNvcnJlbGF0aW9uLCBhbmQgdGhhdCB0aGUgY292YXJpYW5jZSBvZiBhbiBhc3NldCByZXR1cm4gd2l0aCBpdHNlbGYgaXMgaXRzIHZhcmlhbmNlLg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjb21wdXRlIGFuZCBhbmFseXplIHRoZSBjb3ZhcmlhbmNlIGFuZCBjb3JyZWxhdGlvbiBtYXRyaXggb24gdGhlIG1vbnRobHkgcmV0dXJucyBvZiB0aGUgZm91ciBhc3NldCBjbGFzc2VzIGZyb20gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLiBUbyByZWZyZXNoLCB0aGVzZSBhc3NldCBjbGFzc2VzIGFyZSBlcXVpdGllcywgYm9uZHMsIHJlYWwgZXN0YXRlLCBhbmQgY29tbW9kaXRpZXMuIFRvIGNyZWF0ZSB0aGVzZSBtYXRyaWNlcywgeW91IHdpbGwgdXNlIHRoZSBzdGFuZGFyZCBmdW5jdGlvbnMgY292KCkgYW5kIGNvcigpLg0KDQpJbiB5b3VyIHdvcmtzcGFjZSBhcmUgdGhlIG1vbnRobHkgaW52ZXN0bWVudHMgYXMgcmV0dXJucywgYW5kIHRoZSB2ZWN0b3Igb2Ygc3RhbmRhcmQgZGV2aWF0aW9ucyBzZHMgdGhhdCB5b3UgY3JlYXRlZCBwcmV2aW91c2x5Lg0KQ3JlYXRlIGEgZGlhZ29uYWwgbWF0cml4IHRoYXQgY29udGFpbnMgdGhlIHZhcmlhbmNlcyBvbiB0aGUgZGlhZ29uYWwuIFlvdSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBkaWFnKCkgdG8gZG8gdGhpcywgdXNpbmcgYSBzcXVhcmVkIHNkc14yIGFzIHRoZSBvbmx5IGFyZ3VtZW50LiBDYWxsIHRoaXMgZGlhZ19jb3YuDQpgYGB7cn0NCnNkcyA8LSBjKHNkKHJldHVybnNbLDFdKSwgc2QocmV0dXJuc1ssMl0pLCBzZChyZXR1cm5zWywzXSksIHNkKHJldHVybnNbLDRdKSkNCiMgQ3JlYXRlIGEgbWF0cml4IHdpdGggdmFyaWFuY2VzIG9uIHRoZSBkaWFnb25hbA0KZGlhZ19jb3YgPC0gZGlhZyhzZHNeMikNCmRpYWdfY292DQoNCiMgQ3JlYXRlIGEgY292YXJpYW5jZSBtYXRyaXggb2YgcmV0dXJucw0KY292X21hdHJpeCA8LSBjb3YocmV0dXJucykNCmNvdl9tYXRyaXgNCg0KIyBDcmVhdGUgYSBjb3JyZWxhdGlvbiBtYXRyaXggb2YgcmV0dXJucw0KY29yX21hdHJpeCA8LSBjb3IocmV0dXJucykNCmNvcl9tYXRyaXgNCg0KIyBWZXJpZnkgY292YXJpYW5jZXMgZXF1YWwgdGhlIHByb2R1Y3Qgb2Ygc3RhbmRhcmQgZGV2aWF0aW9ucyBhbmQgY29ycmVsYXRpb24NCmFsbC5lcXVhbChjb3ZfbWF0cml4WzEsMl0sIGNvcl9tYXRyaXhbMSwyXSAqIHNkc1sxXSAqIHNkc1syXSkNCmBgYA0KVGhlc2UgbWF0cmljZXMgY2FuIGhlbHAgeW91IGluIHlvdXIgY2FsY3VsYXRpb25zLCBhbmQgYXJlIGFsc28gYSBnb29kIHJlZmVyZW5jZSB0byBzZWUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHlvdXIgYXNzZXRzLg0KDQojIyMgTWF0cml4LWJhc2VkIGNhbGN1bGF0aW9uIG9mIHBvcnRmb2xpbyBtZWFuIGFuZCB2YXJpYW5jZQ0KDQpXaGVuIHcgaXMgdGhlIGNvbHVtbi1tYXRyaXggb2YgcG9ydGZvbGlvIHdlaWdodHMsICTOvCQgdGhlIGNvbHVtbi1tYXRyaXggb2YgZXhwZWN0ZWQgcmV0dXJucywgYW5kICTOoyQgdGhlIHJldHVybiBjb3ZhcmlhbmNlIG1hdHJpeC4gVGhlbiB0aGUgcG9ydGZvbGlvIGV4cGVjdGVkIHJldHVybiBpcyAkd+KAss68JCwgYW5kIHRoZSBwb3J0Zm9saW8gdmFyaWFuY2UgaXMgJHfigLLOo3ckLiBSZW1lbWJlciB0aGF0IHRoZSBwb3J0Zm9saW8ncyB2b2xhdGlsaXR5IGlzIHRoZSBzcXVhcmUgcm9vdCBvZiBpdHMgdmFyaWFuY2UuDQoNCllvdSB3aWxsIHByYWN0aWNlIG1hdHJpeCBtdWx0aXBsaWNhdGlvbiBpbiBSIHVzaW5nIHRoZSAlXColIGZ1bmN0aW9uLCBpbnN0ZWFkIG9mIHRoZSBzdGFuZGFyZCBcKi4gSW4gYWRkaXRpb24sIHlvdSB3aWxsIHRyYW5zcG9zZSB0aGUgbWF0cmljZXMgYnkgdXNpbmcgdGhlIHN0YW5kYXJkIGZ1bmN0aW9uIHQoKS4gUmVtZW1iZXIgdGhhdCB0cmFuc3Bvc2luZyBhIG1hdHJpeCBpcyBzaW1wbHkgY2hhbmdpbmcgdGhlIHJvd3Mgb2YgdGhlIG1hdHJpeCB0byB0aGUgY29sdW1ucy4NCg0KVGhlIHdlaWdodHMsIHZlY3RvciBvZiBtZWFucywgYW5kIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBhcmUgcHJlLWxvYWRlZCBpbiB5b3VyIHdvcmtzcGFjZSBhcyB3ZWlnaHRzLCB2bWVhbnMsIGFuZCBzaWdtYSwgcmVzcGVjdGl2ZWx5Lg0KYGBge3J9DQp3ZWlnaHRzIDwtIGMoMC40LCAwLjQsIDAuMSwgMC4xKQ0Kdm1lYW5zIDwtIGMoYyhlcXVpdGllcyA9IDAuMDA3MDY5MjQyNDc0NTY4MzYsIGJvbmRzID0gMC4wMDQwMDk1MDY1MTExMzIxMiwgDQpyZWFsZXN0YXRlID0gMC4wMDg5NDk1NDgwODY3ODQzMiwgY29tbW9kaXRpZXMgPSAtMC4wMDgyOTYwNjk3MTUwOTAyDQopKQ0Kc2lnbWEgPC0gbWF0cml4KGMoMC4wMDE5MjAyMTgwNTU5MjY2NywgMy4wMDk3NjEwNDQ2NTAxMWUtMDUsIDAuMDAyNDA2Mzc3ODA4MDk0MDUsIA0KMC4wMDE1MTA5OTM5NzIwODA4NiwgMy4wMDk3NjEwNDQ2NTAxMWUtMDUsIDAuMDAwMTIyMjcxMjY0Nzc2NjUxLCANCjAuMDAwMjYzMjIzMTg1OTc1MzQ0LCAtNi44NjA4MjA3MjU3ODgwM2UtMDUsIDAuMDAyNDA2Mzc3ODA4MDk0MDUsIA0KMC4wMDAyNjMyMjMxODU5NzUzNDQsIDAuMDA1NDQ3NzYyNDg4Nzg4ODQsIDAuMDAxMzI5MTQyMzI5OTcwNSwgDQowLjAwMTUxMDk5Mzk3MjA4MDg2LCAtNi44NjA4MjA3MjU3ODgwM2UtMDUsIDAuMDAxMzI5MTQyMzI5OTcwNSwgDQowLjAwNDc5Nzk3MTc5MTE1NzAxKSwgbmNvbCA9IDQpDQojIENyZWF0ZSBhIHdlaWdodCBtYXRyaXggdw0KdyA8LSBhcy5tYXRyaXgod2VpZ2h0cykNCg0KIyBDcmVhdGUgYSBtYXRyaXggb2YgcmV0dXJucw0KbXUgPC0gYXMubWF0cml4KHZtZWFucykNCg0KIyBDYWxjdWxhdGUgcG9ydGZvbGlvIG1lYW4gbW9udGhseSByZXR1cm5zDQp0KHcpICUqJSBtdQ0KDQojIENhbGN1bGF0ZSBwb3J0Zm9saW8gdm9sYXRpbGl0eQ0Kc3FydCh0KHcpICUqJSBzaWdtYSAlKiUgdykNCmBgYA0KIyMgUG9ydGZvbGlvIHJpc2sgYnVkZ2V0DQoNCiMjIyBXaG8gZGlkIGl0Pw0KDQpZb3Ugc2F3IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYSBjYXBpdGFsIGFsbG9jYXRpb24gYnVkZ2V0IGFuZCBhIHJpc2sgYnVkZ2V0LiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBjb25zdHJ1Y3QgYSByaXNrIGJ1ZGdldCwgYW5kIGRpc2NvdmVyIGhvdyBsYXJnZSBlYWNoIGFzc2V0J3MgcGVyY2VudCByaXNrIGNvbnRyaWJ1dGlvbiBpcyBpbiB0aGUgdG90YWwgcG9ydGZvbGlvIHZvbGF0aWxpdHkuDQpGb3IgdGhpcyBsYXN0IGV4ZXJjaXNlLCB5b3Ugd2lsbCBjYWxjdWxhdGUgdGhlIHJpc2sgY29udHJpYnV0aW9ucyBmb3IgYSBwb3J0Zm9saW8gdGhhdCBpcyBhZ2FpbiBpbnZlc3RlZCA0MCUgaW4gZXF1aXRpZXMsIDQwJSBpbiBib25kcywgMTAlIGluIHJlYWwgZXN0YXRlLCBhbmQgMTAlIGluIGNvbW1vZGl0aWVzLiBUaGUgZnVuY3Rpb24gU3RkRGV2KCkgcGxheXMgYW4gaW1wb3J0YW50IHJvbGUgaW4gdGhpcyBleGVyY2lzZS4gVGhlIFN0ZERldigpIGZ1bmN0aW9uIGNyZWF0ZXMgYSBsaXN0IG9mIHRoZSBhc3NldHMnIHN0YW5kYXJkIGRldmlhdGlvbiAoXCRTdGREZXYpLCB0aGVpciByaXNrIGNvbnRyaWJ1dGlvbiAoXCRjb250cmlidXRpb24pLCBhbmQgdGhlaXIgcGVyY2VudCByaXNrIGNvbnRyaWJ1dGlvbiAoXCRwY3RfY29udHJpYl9TdGREZXYpLg0KWW91IHdpbGwgYmUgdXNpbmcgdGhyZWUgYXJndW1lbnRzIGluIHRoZSBTdGREZXYoKSBmdW5jdGlvbiB0byBkbyB0aGlzIGNhbGN1bGF0aW9uLiBUaGUgZmlyc3QgaXMgUiwgYSB2ZWN0b3IsIG1hdHJpeCwgZGF0YSBmcmFtZSwgdGltZSBzZXJpZXMsIG9yIHpvbyBvYmplY3Qgb2YgcmV0dXJucy4gVGhlIHNlY29uZCBpcyBwb3J0Zm9saW9fbWV0aG9kLCB3aGljaCB5b3Ugd2lsbCBzZXQgdG8gY29tcG9uZW50LCBhbmQgdGhlIHRoaXJkIGlzIHdlaWdodHMuDQpgYGB7cn0NCiMgQ3JlYXRlIHBvcnRmb2xpbyB3ZWlnaHRzDQp3ZWlnaHRzIDwtIGMoMC40LCAwLjQsIDAuMSwgMC4xKQ0KDQojIENyZWF0ZSB2b2xhdGlsaXR5IGJ1ZGdldA0Kdm9sX2J1ZGdldCA8LSBTdGREZXYocmV0dXJucywgcG9ydGZvbGlvX21ldGhvZCA9ICJjb21wb25lbnQiLCB3ZWlnaHRzID0gd2VpZ2h0cykNCg0KIyBNYWtlIGEgdGFibGUgb2Ygd2VpZ2h0cyBhbmQgcmlzayBjb250cmlidXRpb24NCndlaWdodHNfcGVyY3Jpc2sgPC0gY2JpbmQod2VpZ2h0cywgdm9sX2J1ZGdldCRwY3RfY29udHJpYl9TdGREZXYpDQpjb2xuYW1lcyh3ZWlnaHRzX3BlcmNyaXNrKSA8LSBjKCJ3ZWlnaHRzIiwgInBlcmMgdm9sIGNvbnRyaWIiKQ0KDQojIFByaW50IHRoZSB0YWJsZQ0Kd2VpZ2h0c19wZXJjcmlzaw0KYGBgDQpOb3RlIHRoYXQgdGhlIHBlcmNlbnRhZ2Ugdm9sYXRpbGl0eSByaXNrIGNhdXNlZCBieSB0aGUgYm9uZHMgaXMgbXVjaCBsZXNzIHRoYW4gdGhlaXIgcG9ydGZvbGlvIHdlaWdodC4NCg0KIyBPcHRpbWl6aW5nIHRoZSBwb3J0Zm9saW8NCg0KIyMgTW9kZXJuIHBvcnRmb2xpbyB0aGVvcnkgb2YgSGFycnkgTWFya293aXR6DQoNCk1lYW4tdmFyaWFuY2UgYmFzZWQgaW52ZXN0aW5nIGluIERKSUEgc3RvY2tzDQpJbiB0aGlzIGNoYXB0ZXIsIHlvdSB3aWxsIGV4cGxvcmUgcG9ydGZvbGlvIG9wdGltaXphdGlvbi4gRm9yIHRoZSB1cGNvbWluZyBleGVyY2lzZXMsIHlvdSB3aWxsIGZvY3VzIG9uIHRoZSBEb3cgSm9uZXMgSW5kdXN0cmlhbCBBdmVyYWdlIChESklBKSBzdG9ja3MuIExpa2UgdGhlIFMmUCA1MDAgcG9ydGZvbGlvIHN0dWRpZWQgaW4gQ2hhcHRlciAyLCB0aGUgRG93IEpvbmVzIEluZHVzdHJpYWwgQXZlcmFnZSBwb3J0Zm9saW8gaXMgYW4gaW1wb3J0YW50IHJlZmVyZW5jZSBwb3J0Zm9saW8gZm9yIHRoZSBVLlMuIGVxdWl0eSBtYXJrZXQuIEl0IGlzIGludmVzdGVkIGluIDMwIGxhcmdlIHB1YmxpY2x5IG93bmVkIGNvbXBhbmllcyBiYXNlZCBpbiB0aGUgVW5pdGVkIFN0YXRlcy4NCg0KWW91IHdpbGwgYmUgdXNpbmcgdGhlIHNhbXBsZSBvZiBtb250aGx5IHJldHVybnMgZnJvbSBKYW51YXJ5IDE5OTEgdW50aWwgRGVjZW1iZXIgMjAxNS4gVGhlc2UgcmV0dXJuIGRhdGEgYXJlIGF2YWlsYWJsZSBpbiB0aGUgd29ya3NwYWNlIGFzIHRoZSB2YXJpYWJsZSByZXR1cm5zLg0KDQpBc3N1bWUgeW91IGFyZSBhbiBpbnZlc3RvciB3aG8gbGlrZXMgaGlnaCByZXR1cm5zIGFuZCBkaXNsaWtlcyByaXNrLiBZb3UgYXJlIGFsc28gY29uc3RyYWluZWQgdG8gYmUgZnVsbHkgaW52ZXN0ZWQgaW4gdGhlIDMwIERKSUEgc3RvY2tzLCBhbmQgb25seSBwb3NpdGl2ZSB3ZWlnaHRzIGFyZSBhbGxvd2VkLiBXaGljaCBvZiB0aGUgZm9sbG93aW5nIHN0YXRlbWVudHMgaXMgZmFsc2U6DQoNCmBgYHtyfQ0KZGppYSA8LSByZWFkUkRTKCJkYXRhL3ByaWNlcy5yZHMiKQ0KaGVhZChkamlhKQ0KYGBgDQpgYGB7cn0NCnJldHVybnMgPC0gUmV0dXJuLmNhbGN1bGF0ZShkamlhKQ0KcmV0dXJucyA8LSByZXR1cm5zWy0xLF0NCmBgYA0KYGBge3J9DQojIENyZWF0ZSBhIHZlY3RvciBvZiByZXR1cm5zIA0KbWVhbnMgPC0gYXBwbHkocmV0dXJucywgMiwgIm1lYW4iKQ0KICANCiMgQ3JlYXRlIGEgdmVjdG9yIG9mIHN0YW5kYXJkIGRldmlhdGlvbg0Kc2RzIDwtIGFwcGx5KHJldHVybnMsIDIsICJzZCIpDQoNCiMgQ3JlYXRlIGEgc2NhdHRlciBwbG90DQpwbG90KHNkcywgbWVhbnMpDQp0ZXh0KHNkcywgbWVhbnMsIGxhYmVscyA9IGNvbG5hbWVzKHJldHVybnMpLCBjZXggPSAwLjcpDQphYmxpbmUoaCA9IDAsIGx0eSA9IDMpDQpgYGANCllvdSB3b3VsZCByYXRoZXIgaW52ZXN0IGFsbCBvZiB5b3VyIGNhcGl0YWwgaW4gSG9tZSBEZXBvdCAodGlja2VyOiBIRCkgdGhhbiBpbiBDYXRlcnBpbGxhciAodGlja2VyOiBDQVQpIGJlY2F1c2UgSG9tZSBEZXBvdCBoYXMgYSBoaWdoZXIgYXZlcmFnZSByZXR1cm4gYW5kIGxvd2VyIHJpc2suDQoNCkJ5IGNvbWJpbmluZyBpbnZlc3RtZW50cywgaXQgbWF5IGJlIHBvc3NpYmxlIHRvIG9idGFpbiBhIHBvcnRmb2xpbyB3aXRoIGxvd2VyIHZhcmlhbmNlIHRoYW4gdGhlIHZhcmlhbmNlIG9mIHRoZSBFeHhvbiBNb2JpbCBDb3Jwb3JhdGlvbuKAmXMgc3RvY2sgKHRpY2tlcjogWE9NKS4NCg0KIyMjIEV4cGxvcmluZyBtb250aGx5IHJldHVybnMgb2YgdGhlIDMwIERKSUEgc3RvY2tzDQoNClRoZSAxOTkxLTIwMTUgbW9udGhseSByZXR1cm5zIG9uIHRoZSAzMCBESklBIHN0b2NrcyBhcmUgYXZhaWxhYmxlIGluIHRoZSB3b3Jrc3BhY2UgYXMgdGhlIHZhcmlhYmxlIHJldHVybnMuIFRoaXMgZXhlcmNpc2Ugd2lsbCBoZWxwIHlvdSBnZXQgY29tZm9ydGFibGUgd2l0aCB0aGUgZGF0YSB0aGF0IHlvdSB3aWxsIGJlIHVzaW5nIGZvciB0aGUgcmVtYWluZGVyIG9mIHRoZSBleGVyY2lzZXMuDQoNClJlY2FsbCB0aGF0IGlmIHdlIGNvbXB1dGUgdGhlIG1lYW5zIGNvbHVtbiBieSBjb2x1bW4gdXNpbmcgY29sTWVhbnMocmV0dXJucyksIG9yIGFwcGx5KHJldHVybnMsIDIsICJtZWFuIiksIHlvdSB0aGVuIG9idGFpbiB0aGUgYXZlcmFnZSByZXR1cm4gcGVyIGFzc2V0LiBJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBiZSBjb21wdXRpbmcgdGhlIG1lYW4gcGVyIHJvdy4gWW91IGNhbiBkbyB0aGlzIHNpbWlsYXJseSB1c2luZyB0aGUgZnVuY3Rpb24gcm93TWVhbnMoKSBhbmQgc3VwcGx5aW5nIHRoZSBhcmd1bWVudCAxIGluc3RlYWQgb2YgdHdvIHRvIGluZGljYXRlIHJvdy13aXNlIGNhbGN1bGF0aW9ucyBpbiB0aGUgYXBwbHkoKSBmdW5jdGlvbi4gQnkgZG9pbmcgc28sIHlvdSBvYnRhaW4gdGhlIHRpbWUgc2VyaWVzIG9mIHJldHVybnMgZm9yIHRoZSBlcXVhbGx5IHdlaWdodGVkIHBvcnRmb2xpby4NCmBgYHtyfQ0KIyBWZXJpZnkgdGhlIGNsYXNzIG9mIHJldHVybnMgDQpjbGFzcyhyZXR1cm5zKQ0KDQojIEludmVzdGlnYXRlIHRoZSBkaW1lbnNpb25zIG9mIHJldHVybnMNCmRpbShyZXR1cm5zKQ0KDQojIENyZWF0ZSBhIHZlY3RvciBvZiByb3cgbWVhbnMNCmV3X3ByZXR1cm5zIDwtIHJvd01lYW5zKHJldHVybnMpDQoNCiMgQ2FzdCB0aGUgbnVtZXJpYyB2ZWN0b3IgYmFjayB0byBhbiB4dHMgb2JqZWN0DQpld19wcmV0dXJucyA8LSB4dHMoZXdfcHJldHVybnMsIG9yZGVyLmJ5ID0gdGltZShyZXR1cm5zKSkNCg0KIyBQbG90IGV3X3ByZXR1cm5zDQpwbG90Lnpvbyhld19wcmV0dXJucykNCmBgYA0KVGhlIGNoYXJ0IHlvdSBjcmVhdGVkIHZpc3VhbGl6ZXMgdGhlIHJldHVybnMgZm9yIGFuIGVxdWFsbHkgd2VpZ2h0ZWQgcG9ydGZvbGlvLg0KDQojIyMgRmluZGluZyB0aGUgbWVhbi12YXJpYW5jZSBlZmZpY2llbnQgcG9ydGZvbGlvDQoNCkEgbWVhbi12YXJpYW5jZSBlZmZpY2llbnQgcG9ydGZvbGlvIGNhbiBiZSBvYnRhaW5lZCBhcyB0aGUgc29sdXRpb24gb2YgbWluaW1pemluZyB0aGUgcG9ydGZvbGlvIHZhcmlhbmNlIHVuZGVyIHRoZSBjb25zdHJhaW50IHRoYXQgdGhlIHBvcnRmb2xpbyBleHBlY3RlZCByZXR1cm4gZXF1YWxzIGEgdGFyZ2V0IHJldHVybi4gQSBjb252ZW5pZW50IFIgZnVuY3Rpb24gZm9yIGRvaW5nIHNvIGlzIHRoZSBmdW5jdGlvbiBwb3J0Zm9saW8ub3B0aW0oKSBpbiB0aGUgUiBwYWNrYWdlIHRzZXJpZXMuIEl0cyBkZWZhdWx0IGltcGxlbWVudGF0aW9uIGZpbmRzIHRoZSBtZWFuLXZhcmlhbmNlIGVmZmljaWVudCBwb3J0Zm9saW8gd2VpZ2h0cyB1bmRlciB0aGUgY29uc3RyYWludCB0aGF0IHRoZSBwb3J0Zm9saW8gcmV0dXJuIGVxdWFscyB0aGUgcmV0dXJuIG9uIHRoZSBlcXVhbGx5LXdlaWdodGVkIHBvcnRmb2xpby4gVGhlIG9ubHkgYXJndW1lbnQgbmVlZGVkIGlzIHRoZSBtb250aGx5IHJldHVybiBkYXRhIG9uIHRoZSBwb3J0Zm9saW8gY29tcG9uZW50cyBmb3Igd2hpY2ggdGhlIHdlaWdodHMgbmVlZCB0byBiZSBkZXRlcm1pbmVkLg0KDQpUaGUgdmFyaWFibGUgcmV0dXJucyBjb250YWluaW5nIHRoZSBtb250aGx5IHJldHVybnMgb2YgdGhlIERKSUEgc3RvY2tzIGlzIGFscmVhZHkgbG9hZGVkIGluIHRoZSBjb25zb2xlLg0KDQpDcmVhdGUgYSBtZWFuLXZhcmlhbmNlIGVmZmljaWVudCBwb3J0Zm9saW8gb2YgbW9udGhseSByZXR1cm5zIHVzaW5nIHRoZSBkZWZhdWx0IG9mIHBvcnRmb2xpby5vcHRpbSgpIHRhcmdldGluZyB0aGUgZXF1YWxseS13ZWlnaHRlZCBwb3J0Zm9saW8gcmV0dXJuLCBhbmQgYXNzaWduIHRoZSBvdXRwdXQgdG8gdGhlIHZhcmlhYmxlIG9wdC4NCg0KYGBge3J9DQojIExvYWQgdHNlcmllcw0KbGlicmFyeSh0c2VyaWVzKQ0KDQojIENyZWF0ZSBhbiBvcHRpbWl6ZWQgcG9ydGZvbGlvIG9mIHJldHVybnMNCm9wdCA8LSBwb3J0Zm9saW8ub3B0aW0ocmV0dXJucykNCg0KIyBDcmVhdGUgcGZfd2VpZ2h0cw0KcGZfd2VpZ2h0cyA8LSBvcHQkcHcNCg0KIyBBc3NpZ24gYXNzZXQgbmFtZXMNCm5hbWVzKHBmX3dlaWdodHMpIDwtIGNvbG5hbWVzKHJldHVybnMpDQoNCiMgU2VsZWN0IG9wdGltdW0gd2VpZ2h0cyBvcHRfd2VpZ2h0cw0Kb3B0X3dlaWdodHMgPC0gcGZfd2VpZ2h0c1twZl93ZWlnaHRzID49IDAuMDFdDQoNCiMgQmFyIHBsb3Qgb2Ygb3B0X3dlaWdodHMNCmJhcnBsb3Qob3B0X3dlaWdodHMpDQoNCiMgUHJpbnQgZXhwZWN0ZWQgcG9ydGZvbGlvIHJldHVybiBhbmQgdm9sYXRpbGl0eQ0Kb3B0JHBtDQpvcHQkcHMNCmBgYA0KVGhlIHBvcnRmb2xpbyB5b3UgY3JlYXRlZCBoYXMgYW4gYXZlcmFnZSByZXR1cm4gb2YgMS4yJSENCg0KIyMjIEVmZmVjdCBvZiB0aGUgcmV0dXJuIHRhcmdldA0KDQpUaGlzIGV4ZXJjaXNlIHdpbGwgc2hvdyB0aGUgZWZmZWN0IG9mIGluY3JlYXNpbmcgeW91ciB0YXJnZXQgcmV0dXJuIG9uIHRoZSB2b2xhdGlsaXR5IG9mIHlvdXIgbWVhbi12YXJpYW5jZSBlZmZpY2llbnQgcG9ydGZvbGlvLg0KDQpUaGUgZnVuY3Rpb24gcG9ydGZvbGlvLm9wdGltIGhhcyBhcmd1bWVudHMgdGhhdCBhbGxvdyBmb3IgbW9yZSBnZW5lcmFsIHNwZWNpZmljYXRpb25zLiBUaGUgYXJndW1lbnRzIGFyZSBhcyBmb2xsb3dzOg0KDQogICAgcG9ydGZvbGlvLm9wdGltKHgsIHBtID0gbWVhbih4KSwgc2hvcnRzID0gRkFMU0UsIHJlc2hpZ2ggPSBOVUxMKQ0KICAgIA0KVGhlIGFyZ3VtZW50IHBtIHNldHMgdGhlIHRhcmdldCByZXR1cm4sIHRoZSBhcmd1bWVudCByZXNoaWdoIHNwZWNpZmllcyB0aGUgdXBwZXIgY29uc3RyYWludHMgb24gdGhlIHBvcnRmb2xpbyB3ZWlnaHRzLCBhbmQgdGhlIGFyZ3VtZW50IHNob3J0cyBpcyBhIGxvZ2ljYWwgc3RhdGVtZW50IHNwZWNpZnlpbmcgd2hldGhlciBuZWdhdGl2ZSB3ZWlnaHRzIGFyZSBmb3JiaWRkZW4gb3Igbm90LCBieSBkZWZhdWx0IHNob3J0cyA9IEZBTFNFLg0KDQpZb3Ugd2lsbCBjcmVhdGUgYSBwb3J0Zm9saW8gdGhhdCBpcyBvcHRpbWl6ZWQgZm9yIGEgdGFyZ2V0IHJldHVybiB0aGF0IGVxdWFscyB0aGUgYXZlcmFnZSB2YWx1ZSBvZiB0aGUgcmV0dXJuIHNlcmllcyByZXR1cm5zLiBUaGVuIHlvdSB3aWxsIGNyZWF0ZSBhIHBvcnRmb2xpbyB0aGF0IGhhcyBhIHRhcmdldCByZXR1cm4gdGhhdCBpcyAxMCUgaGlnaGVyIHRoYW4gdGhlIG1lYW4gcmV0dXJuIHNlcmllcyBhbmQgY2FsY3VsYXRlIHRoZSBwcm9wb3J0aW9uIGNoYW5nZSBpbiByaXNrLg0KYGBge3J9DQojIENyZWF0ZSBwb3J0Zm9saW8gd2l0aCB0YXJnZXQgcmV0dXJuIG9mIGF2ZXJhZ2UgcmV0dXJucyANCnBmX21lYW4gPC0gcG9ydGZvbGlvLm9wdGltKHJldHVybnMsIHBtID0gbWVhbihyZXR1cm5zKSkNCg0KIyBDcmVhdGUgcG9ydGZvbGlvIHdpdGggdGFyZ2V0IHJldHVybiAxMCUgZ3JlYXRlciB0aGFuIGF2ZXJhZ2UgcmV0dXJucw0KcGZfMTBwbHVzIDwtIHBvcnRmb2xpby5vcHRpbShyZXR1cm5zLCBwbSA9IDEuMSAqIG1lYW4ocmV0dXJucykpDQoNCiMgUHJpbnQgdGhlIHN0YW5kYXJkIGRldmlhdGlvbnMgb2YgYm90aCBwb3J0Zm9saW9zDQpwcmludChwZl9tZWFuJHBzKQ0KcHJpbnQocGZfMTBwbHVzJHBzKQ0KDQojIENhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBpbmNyZWFzZSBpbiBzdGFuZGFyZCBkZXZpYXRpb24NCihwZl8xMHBsdXMkcHMgLSBwZl9tZWFuJHBzKSAvIChwZl9tZWFuJHBzKQ0KYGBgDQpCeSBpbmNyZWFzaW5nIHlvdXIgdGFyZ2V0IHJldHVybiwgeW91ciBwb3J0Zm9saW8ncyB2b2xhdGlsaXR5IGluY3JlYXNlZCBieSA4JSENCg0KIyMjIEltcG9zaW5nIHdlaWdodCBjb25zdHJhaW50cw0KDQpJbnZlc3RvcnMgYXJlIG9mdGVuIGNvbnN0cmFpbmVkIGJ5IHRoZSBtYXhpbXVtIHZhbHVlcyBhbGxvd2VkIGZvciB0aGUgcG9ydGZvbGlvIHdlaWdodHMuIFRoZXNlIGNvbnN0cmFpbnRzIGNhbiBhY3R1YWxseSBiZSBhbiBhZHZhbnRhZ2UuIFRoZSBhZHZhbnRhZ2Ugb2YgYSBtYXhpbXVtIHdlaWdodCBjb25zdHJhaW50IGlzIHRoYXQgdGhlIHN1YnNlcXVlbnQgcG9ydGZvbGlvIHdpbGwgYmUgbGVzcyBjb25jZW50cmF0ZWQgaW4gY2VydGFpbiBhc3NldHMuIFRoZXJlIGlzIGEgZGlzYWR2YW50YWdlIHRvIHRoaXMsIHRob3VnaC4gVGhlIGRpc2FkdmFudGFnZSBpcyB0aGF0IHRoZSBzYW1lIHRhcmdldCByZXR1cm4gbWF5IG5vIGxvbmdlciBiZSBwb3NzaWJsZSBvciB3aWxsIGJlIG9idGFpbmVkIGF0IHRoZSBleHBlbnNlIG9mIGhpZ2hlciB2b2xhdGlsaXR5Lg0KDQpSZW1lbWJlciBmcm9tIHRoZSBwcmV2aW91cyBleGVyY2lzZSB0aGF0IHRoZSBmdW5jdGlvbiBwb3J0Zm9saW8ub3B0aW0oKSBhbGxvd3MgeW91IHRvIHNldCB3ZWlnaHQgY29uc3RyYWludHMgd2l0aGluIHRoZSByZXNoaWdoIGFyZ3VtZW50LiByZXNoaWdoIHJlcXVpcmVzIGEgdmVjdG9yIG9mIG1heGltdW0gd2VpZ2h0cyBmb3IgZWFjaCBhc3NldC4NCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgY3JlYXRlIHRocmVlIHBvcnRmb2xpb3Mgd2l0aCBkaWZmZXJlbnQgbWF4aW11bSB3ZWlnaHQgY29uc3RyYWludHMuIEZvciB0aGlzIGV4ZXJjaXNlLCBpdCBpcyBpbXBvcnRhbnQgdG8ga25vdyB0aGUgb3V0cHV0IG9mIHRoZSBwb3J0Zm9saW8ub3B0aW0oKSBmdW5jdGlvbi4gVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGEgbGlzdCBjb250YWluaW5nIGZvdXIgY29tcG9uZW50czogKGkpIFwkcHc6IHRoZSBwb3J0Zm9saW8gd2VpZ2h0cywgKGlpKSBcJHB4OiB0aGUgcmV0dXJucyBvZiB0aGUgb3ZlcmFsbCBwb3J0Zm9saW8sIChpaWkpIFwkcG06IHRoZSBleHBlY3RlZCByZXR1cm4gcG9ydGZvbGlvLCAoaXYpIFwkcHM6IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIHBvcnRmb2xpbyByZXR1cm5zLg0KQ3JlYXRlIHRocmVlIHZlY3RvcnMgb2YgbWF4aW11bSB3ZWlnaHRzIGZvciBlYWNoIGFzc2V0IChjb2x1bW4pIGluIHJldHVybnMgdXNpbmcgdGhlIHJlcCgpIGZ1bmN0aW9uLiBUaGUgZmlyc3QgdmVjdG9yIHdpbGwgY29udGFpbiBtYXhpbXVtIHdlaWdodHMgb2YgMTAwJSwgdGhlIHNlY29uZCAxMCUsIGFuZCB0aGUgdGhpcmQgNSUuIENhbGwgdGhlc2UgbWF4X3dlaWdodHMxLCBtYXhfd2VpZ2h0czIsIG1heF93ZWlnaHRzMywgcmVzcGVjdGl2ZWx5Lg0KYGBge3J9DQojIENyZWF0ZSB2ZWN0b3JzIG9mIG1heGltdW0gd2VpZ2h0cw0KbWF4X3dlaWdodHMxIDwtIHJlcCgxLCBuY29sKHJldHVybnMpKQ0KbWF4X3dlaWdodHMyIDwtIHJlcCgwLjEsIG5jb2wocmV0dXJucykpDQptYXhfd2VpZ2h0czMgPC0gcmVwKDAuMDUsIG5jb2wocmV0dXJucykpDQoNCiMgQ3JlYXRlIGFuIG9wdGltdW0gcG9ydGZvbGlvIHdpdGggbWF4IHdlaWdodHMgb2YgMTAwJQ0Kb3B0MSA8LSBwb3J0Zm9saW8ub3B0aW0ocmV0dXJucywgcmVzaGlnaCA9IG1heF93ZWlnaHRzMSkNCg0KIyBDcmVhdGUgYW4gb3B0aW11bSBwb3J0Zm9saW8gd2l0aCBtYXggd2VpZ2h0cyBvZiAxMCUNCm9wdDIgPC0gcG9ydGZvbGlvLm9wdGltKHJldHVybnMsIHJlc2hpZ2ggPSBtYXhfd2VpZ2h0czIpDQoNCiMgQ3JlYXRlIGFuIG9wdGltdW0gcG9ydGZvbGlvIHdpdGggbWF4IHdlaWdodHMgb2YgNSUNCm9wdDMgPC0gcG9ydGZvbGlvLm9wdGltKHJldHVybnMsIHJlc2hpZ2ggPSBtYXhfd2VpZ2h0czMpDQoNCiMgQ2FsY3VsYXRlIGhvdyBtYW55IGFzc2V0cyBoYXZlIGEgd2VpZ2h0IHRoYXQgaXMgZ3JlYXRlciB0aGFuIDElIGZvciBlYWNoIHBvcnRmb2xpbw0Kc3VtKG9wdDEkcHcgPiAuMDEpDQpzdW0ob3B0MiRwdyA+IC4wMSkNCnN1bShvcHQzJHB3ID4gLjAxKQ0KDQojIFByaW50IHBvcnRmb2xpbyB2b2xhdGlsaXRlcyANCm9wdDEkcHMNCm9wdDIkcHMNCm9wdDMkcHMNCmBgYA0KQnkgaW1wb3Npbmcgd2VpZ2h0IGNvbnN0cmFpbnRzIHlvdSBjYW4gZW5zdXJlIHRoYXQgeW91ciBwb3J0Zm9saW8gaXNuJ3QgY29tcGxldGVseSBkb21pbmF0ZWQgYnkganVzdCBhIGZldyBhc3NldHMuDQoNCiMjIFRoZSBlZmZpY2llbnQgZnJvbnRpZXINCg0KIyMjIENvbXB1dGluZyB0aGUgZWZmaWNpZW50IGZyb250aWVyIHVzaW5nIGEgZ3JpZCBvZiB0YXJnZXQgcmV0dXJucw0KDQpBcyB5b3UgaGF2ZSBzZWVuLCBvbmUgYXBwcm9hY2ggdG8gY29tcHV0ZSB0aGUgZWZmaWNpZW50IGZyb250aWVyIGlzIHRvIGZpcnN0IGRlZmluZSB0aGUgZ3JpZCBvZiB0YXJnZXQgcmV0dXJucyBhbmQgdGhlbiwgZm9yIGVhY2ggdGFyZ2V0IHJldHVybiwgZmluZCB0aGUgcG9ydGZvbGlvIHRoYXQgaGFzIGFuIGV4cGVjdGVkIHJldHVybiBlcXVhbCB0byB0aGUgdGFyZ2V0IHJldHVybiBhdCB0aGUgbG93ZXN0IHBvc3NpYmxlIHZhcmlhbmNlLg0KDQpCdXQgd2hhdCBpcyBhIHJlYXNvbmFibGUgZ3JpZCBvZiB0YXJnZXQgcmV0dXJucz8gWW91IHdpbGwgc2V0IHRoZSBtYXhpbXVtIHRhcmdldCByZXR1cm4gdG8gdGhlIG1heGltdW0gYXZlcmFnZSByZXR1cm4gb2YgdGhlIHN0b2Nrcy4gSWRlYWxseSwgeW91IHdvdWxkIHNldCB0aGUgbWluaW11bSB0YXJnZXQgcmV0dXJuIHRvIHRoZSByZXR1cm4gb2YgdGhlIG1pbmltdW0gdmFyaWFuY2UgcG9ydGZvbGlvLiBCZWNhdXNlIHlvdSBkb24ndCBrbm93IHRoaXMgbWluaW11bSB2YXJpYW5jZSBwb3J0Zm9saW8gcmV0dXJuIHlldCwgeW91IGNyZWF0ZSBhIGdyaWQgdXNpbmcgdGhlIG1pbmltdW0gb2YgYXZlcmFnZSByZXR1cm5zIGZyb20gYWxsIHN0b2Nrcy4NCg0KSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgdXNlIGEgZm9yIGxvb3AgdG8gY2FsY3VsYXRlIHlvdXIgZ3JpZCBvZiBwb3RlbnRpYWwgcG9ydGZvbGlvIG1lYW5zLCBkZXZpYXRpb25zLCBhbmQgd2VpZ2h0cy4NCmBgYHtyfQ0KIyBDYWxjdWxhdGUgZWFjaCBzdG9ja3MgbWVhbiByZXR1cm5zDQpzdG9ja211IDwtIGNvbE1lYW5zKHJldHVybnMpDQoNCiMgQ3JlYXRlIGEgZ3JpZCBvZiB0YXJnZXQgdmFsdWVzDQpncmlkIDwtIHNlcShmcm9tID0gMC4wMSwgdG8gPSBtYXgoc3RvY2ttdSksIGxlbmd0aC5vdXQgPSA1MCkNCg0KIyBDcmVhdGUgZW1wdHkgdmVjdG9ycyB0byBzdG9yZSBtZWFucyBhbmQgZGV2aWF0aW9ucw0KdnBtIDwtIHZwc2QgPC0gcmVwKE5BLCBsZW5ndGgoZ3JpZCkpDQoNCiMgQ3JlYXRlIGFuIGVtcHR5IG1hdHJpeCB0byBzdG9yZSB3ZWlnaHRzDQptd2VpZ2h0cyA8LSBtYXRyaXgoTkEsIDUwLCAzMCkNCg0KIyBDcmVhdGUgeW91ciBmb3IgbG9vcA0KZm9yKGkgaW4gMTpsZW5ndGgoZ3JpZCkpIHsNCiAgb3B0IDwtIHBvcnRmb2xpby5vcHRpbSh4ID0gcmV0dXJucywgcG0gPSBncmlkW2ldKQ0KICB2cG1baV0gPC0gb3B0JHBtDQogIHZwc2RbaV0gPC0gb3B0JHBzDQogIG13ZWlnaHRzW2ksIF0gPC0gb3B0JHB3DQp9DQpgYGANClRoZSBmb3IgbG9vcCB0aGF0IHlvdSBqdXN0IGNyZWF0ZWQgaXMgZXh0cmVtZWx5IGhlbHBmdWwgaW4gYmVpbmcgYWJsZSB0byBleHBsb3JlIGhvdyB0YXJnZXQgcmV0dXJucyBjYW4gYWZmZWN0IHdlaWdodHMsIHZvbGF0aWxpdHksIGFuZCBwb3J0Zm9saW8gcmV0dXJucy4NCg0KIyMjIEludGVycHJldGluZyB0aGUgZWZmaWNpZW50IGZyb250aWVyDQoNCiFbZWZmaWNpZW50IGZyb250aWVyXShmcm9udGllcl8wMS5wbmcpDQoNClRoZSBwb3J0Zm9saW8gd2l0aCB0aGUgZ3JlYXRlc3QgZXhjZXNzIHJldHVybiBwZXIgdW5pdCBvZiBwb3J0Zm9saW8gdm9sYXRpbGl0eSBpcyB0aGUgdGFuZ2VuY3kgcG9ydGZvbGlvLg0KDQojIyMgUHJvcGVydGllcyBvZiB0aGUgZWZmaWNpZW50IGZyb250aWVyDQoNCiFbXShmcm9udGllcl8wMi5wbmcpDQpUaGUgY3VydmUgc2hvd24gaW4gdGhlIHByZXZpb3VzIHBsb3Qgc2hvd3MgdGhlIHNvbHV0aW9uIG9mIG1pbmltaXppbmcgdGhlIHZhcmlhbmNlIHVuZGVyIHRoZSBjb25zdHJhaW50IHRoYXQgdGhlIHBvcnRmb2xpbyBleHBlY3RlZCByZXR1cm4gZXF1YWxzIHRoZSB0YXJnZXQgcmV0dXJuLiBXaGVuIHRoZSB0YXJnZXQgcmV0dXJuIGlzIGJlbG93IHRoZSBtaW5pbXVtIHZhcmlhbmNlIHJldHVybiwgdGhlIG9idGFpbmVkIHBvcnRmb2xpbyBpcyBub3QgZWZmaWNpZW50IGJlY2F1c2UgYSBoaWdoZXIgcmV0dXJuIGF0IGxvd2VyIHJpc2sgY2FuIGJlIG9idGFpbmVkIGJ5IGludmVzdGluZyBpbiB0aGUgbWluaW11bSB2YXJpYW5jZSBwb3J0Zm9saW8uIFRoZSBvcHRpbWl6ZWQgcG9ydGZvbGlvcyBvZnRlbiBhcmUgYWxzbyBub3QgcmVhbGlzdGljIGJlY2F1c2UgdGhleSB0ZW5kIHRvIGludmVzdCBsYXJnZSB3ZWlnaHRzIGluIG9ubHkgYSBmZXcgYXNzZXRzLiBBIHByYWN0aWNhbCBzb2x1dGlvbiB0byBhdm9pZCB0aGlzIGlzIHRvIGltcG9zZSB3ZWlnaHQgY29uc3RyYWludHMuDQoNClRoZSBlZmZlY3Qgb2Ygc3VjaCBhIHdlaWdodCBjb25zdHJhaW50IGlzIHNob3duIGluIHRoZSB3b3Jrc3BhY2UsIHdoZXJlIHlvdSBjYW4gc2VlIHRoZSBlZmZpY2llbnQgZnJvbnRpZXIgcGxvdHMgb2J0YWluZWQgZm9yIHRoZSBESklBIHN0b2NrcyB1bmRlciBhIG1heGltdW0gMTAwJSB3ZWlnaHQgY29uc3RyYWludCAoYmxhY2sgbGluZSksIGEgbWF4aW11bSAxMCUgd2VpZ2h0IGNvbnN0cmFpbnQgKHJlZCBsaW5lKSBhbmQgYSBtYXhpbXVtIDUlIHdlaWdodCBjb25zdHJhaW50IChibHVlKS4NCg0KLQlJbXBvc2luZyBhIG1vcmUgc3RyaWN0IHdlaWdodCBjb25zdHJhaW50IGxlYWRzIHRvIGEgc2hpZnQgdG8gdGhlIHJpZ2h0IG9mIHRoZSBlZmZpY2llbnQgZnJvbnRpZXIuDQotCVRoZSBwb3J0Zm9saW9zIG9uIHRoZSBkYXNoZWQgbGluZXMgYXJlIG5vdCBtZWFuLXZhcmlhbmNlIGVmZmljaWVudC4NCg0KIyMjIFRoZSBtaW5pbXVtIHZhcmlhbmNlIGFuZCBtYXhpbXVtIFNoYXJwZSByYXRpbyBwb3J0Zm9saW8NCg0KSW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlcywgeW91IGNvbXB1dGVkIHRoZSBlZmZpY2llbnQgZnJvbnRpZXIgdXNpbmcgYSBncmlkIG9mIHRhcmdldCByZXR1cm5zLiBUaGUgb3V0cHV0IG9mIHlvdXIgY2FsY3VsYXRpb24gb2YgdGhlIGVmZmljaWVudCBmcm9udGllciB3YXMgYSBzZXJpZXMgb2YgdHdvIHZlY3RvcnMsIHZwbSAodmVjdG9yIG9mIHBvcnRmb2xpbyBtZWFucyksIGFuZCB2cHNkICh2ZWN0b3Igb2Ygc3RhbmRhcmQgZGV2aWF0aW9ucywgb3Igdm9sYXRpbGl0aWVzKSwgYW5kIGEgbWF0cml4IG9mIHdlaWdodHMgY2FsbGVkIG13ZWlnaHRzLiBZb3Ugd2lsbCB1c2UgdGhlc2Ugb3V0cHV0cyB0byBpZGVudGlmeSB0aGUgcG9ydGZvbGlvcyB3aXRoIHRoZSBsZWFzdCB2b2xhdGlsaXR5LCBhbmQgdGhlIGdyZWF0ZXN0IFNoYXJwZSByYXRpbywgYW5kIHRoZW4gcGxvdCB0aGVpciB3ZWlnaHQgYWxsb2NhdGlvbi4NCg0KQXMgYSByZW1pbmRlciwgdGhlIFNoYXJwZSBSYXRpbyBpcyBmb3VuZCBieSB0YWtpbmcgdGhlIGV4Y2VzcyByZXR1cm5zIGxlc3MgdGhhbiB0aGUgcmlzay1mcmVlIHJhdGUsIGRpdmlkZWQgYnkgdGhlIHBvcnRmb2xpbyB2b2xhdGlsaXR5Lg0KYGBge3J9DQpjb2xuYW1lcyhtd2VpZ2h0cykgPC0gY29sbmFtZXMocmV0dXJucykNCiMgQ3JlYXRlIHdlaWdodHNfbWludmFyIGFzIHRoZSBwb3J0Zm9saW8gd2l0aCB0aGUgbGVhc3Qgcmlzaw0Kd2VpZ2h0c19taW52YXIgPC0gbXdlaWdodHNbdnBzZCA9PSBtaW4odnBzZCksIF0NCg0KIyBDYWxjdWxhdGUgdGhlIFNoYXJwZSByYXRpbw0KdnNyIDwtICh2cG0gLSAwLjAwNzUpIC8gdnBzZA0KDQojIENyZWF0ZSB3ZWlnaHRzX21heF9zciBhcyB0aGUgcG9ydGZvbGlvIHdpdGggdGhlIG1heGltdW0gU2hhcnBlIHJhdGlvDQp3ZWlnaHRzX21heF9zciA8LSBtd2VpZ2h0c1t2c3IgPT0gbWF4KHZzcildDQoNCiMgQ3JlYXRlIGJhciBwbG90IG9mIHdlaWdodHNfbWludmFyIGFuZCB3ZWlnaHRzX21heF9zcg0KcGFyKG1mcm93ID0gYygyLCAxKSwgbWFyID0gYygzLCAyLCAyLCAxKSkNCmJhcnBsb3Qod2VpZ2h0c19taW52YXJbd2VpZ2h0c19taW52YXIgPiAwLjAxXSkNCmJhcnBsb3Qod2VpZ2h0c19tYXhfc3Jbd2VpZ2h0c19tYXhfc3IgPiAwLjAxXSkNCmBgYA0KIyMgSW4tc2FtcGxlIHZzLiBvdXQtb2Ytc2FtcGxlIGV2YWx1YXRpb24NCg0KIyMjIFNwbGl0LXNhbXBsZSBldmFsdWF0aW9uDQoNCkluIGNoYXB0ZXIgMiwgeW91IHVzZWQgdGhlIGZ1bmN0aW9uIHdpbmRvdygpIHRvIHN1YnNldCB5b3VyIHJldHVybnMgZm9yIGdyYXBoaWNhbCBwdXJwb3Nlcy4gSW4gdGhpcyBleGVyY2lzZSwgeW91IHdpbGwgdXNlIHRoZSB3aW5kb3coKSB0byBjcmVhdGUgdHdvIHNhbXBsZXM6IGFuIGVzdGltYXRpb24gc2FtcGxlIGFuZCBhbiBldmFsdWF0aW9uIHNhbXBsZS4gVGhpcyBleGVyY2lzZSB3aWxsIGlsbHVzdHJhdGUgaG93IHBvcnRmb2xpbyB3ZWlnaHRzIGNhbiBkaWZmZXIgd2hlbiBjaGFuZ2luZyB0aGUgZXN0aW1hdGlvbiB3aW5kb3cuDQoNClRvIHJlbWluZCB5b3UsIHRoZSBmdW5jdGlvbiB3aW5kb3coKSBoYXMgYXJndW1lbnQgb2YgeCwgc3RhcnQsIGFuZCBlbmQuIFdoZXJlIHN0YXJ0IGFuZCBlbmQgYXJlIGluIHRoZSBmb3JtYXQgIllZWVktTU0tREQiLg0KYGBge3J9DQojIENyZWF0ZSByZXR1cm5zX2VzdGltIA0KcmV0dXJuc19lc3RpbSA8LSB3aW5kb3cocmV0dXJucywgc3RhcnQgPSAiMTk5MS0wMS0wMSIsIGVuZCA9ICIyMDAzLTEyLTMxIikNCg0KIyBDcmVhdGUgcmV0dXJuc19ldmFsDQpyZXR1cm5zX2V2YWwgPC0gd2luZG93KHJldHVybnMsIHN0YXJ0ID0gIjIwMDQtMDEtMDEiLCBlbmQgPSAiMjAxNS0xMi0zMSIpDQoNCiMgQ3JlYXRlIHZlY3RvciBvZiBtYXggd2VpZ2h0cw0KbWF4X3dlaWdodHMgPC0gcmVwKDAuMSwgbmNvbChyZXR1cm5zKSkNCg0KIyBDcmVhdGUgcG9ydGZvbGlvIHdpdGggZXN0aW1hdGlvbiBzYW1wbGUgDQpwZl9lc3RpbSA8LSBwb3J0Zm9saW8ub3B0aW0ocmV0dXJuc19lc3RpbSwgcmVzaGlnaCA9IG1heF93ZWlnaHRzKQ0KDQojIENyZWF0ZSBwb3J0Zm9saW8gd2l0aCBldmFsdWF0aW9uIHNhbXBsZQ0KcGZfZXZhbCA8LSBwb3J0Zm9saW8ub3B0aW0ocmV0dXJuc19ldmFsLCByZXNoaWdoID0gbWF4X3dlaWdodHMpDQoNCiMgQ3JlYXRlIGEgc2NhdHRlciBwbG90IHdpdGggZXZhbHVhdGlvbiBwb3J0Zm9saW8gd2VpZ2h0cyBvbiB0aGUgdmVydGljYWwgYXhpcw0KcGxvdChwZl9lc3RpbSRwdywgcGZfZXZhbCRwdykNCmFibGluZShhID0gMCwgYiA9IDEsIGx0eSA9IDMpDQpgYGANCklmIHBvcnRmb2xpbyB3ZWlnaHRzIGFyZSBpZGVudGljYWwsIHRoZXkgc2hvdWxkIGJlIG9uIHRoZSA0NS1kZWdyZWUgbGluZS4NCkJ5IGNyZWF0aW5nIHR3byBzYW1wbGVzIHVzaW5nIHRoZSB3aW5kb3coKSBmdW5jdGlvbiB5b3UgY2FuIGJhY2t0ZXN0LCBvciBldmFsdWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgYSBwb3J0Zm9saW8gb24gaGlzdG9yaWNhbCBkYXRhIQ0KDQojIyMgT3V0IG9mIHNhbXBsZSBwZXJmb3JtYW5jZSBldmFsdWF0aW9uDQoNClRoaXMgZXhhbXBsZSB3aWxsIGlsbHVzdHJhdGUgaG93IHlvdXIgcmV0dXJucyBjYW4gY2hhbmdlIGJhc2VkIG9uIHRoZSB3ZWlnaHRpbmcgY3JlYXRlZCBieSBhbiBvcHRpbWl6ZWQgcG9ydGZvbGlvLiBZb3Ugd2lsbCB1c2UgdGhlIGVzdGltYXRpb24gcG9ydGZvbGlvIChwZl9lc3RpbSkgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIHlvdXIgcG9ydGZvbGlvIG9uIHRoZSBlc3RpbWF0aW9uIHNhbXBsZSBvZiByZXR1cm5zIChyZXR1cm5zX2V2YWwpLg0KDQpIb3cgc2V2ZXJlIGlzIHRoZSBvcHRpbWFsaXR5IGxvc3M/IExldCdzIGNvbXBhcmUsIGZvciB0aGUgcG9ydGZvbGlvIHdlaWdodHMgaW4gcGZfZXN0aW0sIHRoZSBwZXJmb3JtYW5jZSB5b3UgZXhwZWN0ZWQgdXNpbmcgdGhlIGV2YWx1YXRpb24gc2FtcGxlIChyZXR1cm5zX2VzdGltKSB3aXRoIHRoZSBhY3R1YWwgcmV0dXJuIG9uIHRoZSBvdXQtb2Ytc2FtcGxlIHBlcmlvZCAocmV0dXJuc19ldmFsKS4NCg0KQ2FsY3VsYXRlIHRoZSByZXR1cm5zIG9mIHRoZSBwb3J0Zm9saW8gd2l0aCBtb250aGx5IHJlYmFsYW5jZSB3ZWlnaHRzIHBmX2VzdGltJHB3IG9uIHRoZSBlc3RpbWF0aW9uIHNhbXBsZSByZXR1cm5zX2VzdGltLiBDYWxsIHRoaXMgcmV0dXJuc19wZl9lc3RpbQ0KDQpgYGB7cn0NCiMgQ3JlYXRlIHJldHVybnNfcGZfZXN0aW0NCnJldHVybnNfcGZfZXN0aW0gPC0gUmV0dXJuLnBvcnRmb2xpbyhyZXR1cm5zX2VzdGltLCBwZl9lc3RpbSRwdywgcmViYWxhbmNlX29uID0gIm1vbnRocyIpDQoNCg0KIyBDcmVhdGUgcmV0dXJuc19wZl9ldmFsDQpyZXR1cm5zX3BmX2V2YWwgPC0gUmV0dXJuLnBvcnRmb2xpbyhyZXR1cm5zX2V2YWwsIHBmX2VzdGltJHB3LCByZWJhbGFuY2Vfb24gPSAibW9udGhzIikNCg0KIyBQcmludCBhIHRhYmxlIGZvciB5b3VyIGVzdGltYXRpb24gcG9ydGZvbGlvDQp0YWJsZS5Bbm51YWxpemVkUmV0dXJucyhyZXR1cm5zX3BmX2VzdGltKQ0KDQojIFByaW50IGEgdGFibGUgZm9yIHlvdXIgZXZhbHVhdGlvbiBwb3J0Zm9saW8NCnRhYmxlLkFubnVhbGl6ZWRSZXR1cm5zKHJldHVybnNfcGZfZXZhbCkNCmBgYA0KVGhlIHJlc3VsdHMgZnJvbSB0aGUgcGZfZXZhbCBhcmUgd2hhdCB5b3UgbWF5IGV4cGVjdCBpbiBhIHJlYWwgcGVyZm9ybWFuY2UuDQpZb3UgaGF2ZSBub3cgc3VjY2Vzc2Z1bGx5IGV2YWx1YXRlZCBhIHNwbGl0IHNhbXBsZSwgdGhpcyB3b3VsZG4ndCBiZSBwb3NzaWJsZSB3aXRob3V0IHRoZSB1c2Ugb2YgaGlzdG9yaWNhbCBkYXRhDQoNCiMjIyBJdCBhaW4ndCBvdmVyDQoNClRoaXMgam91cm5leSBzaG93ZWQgeW91IGhvdyB0byBjb21wdXRlIHJldHVybnMsIGFuYWx5emUgcG9ydGZvbGlvIHBlcmZvcm1hbmNlLCBhbmQgb3B0aW1pemUgcG9ydGZvbGlvcy4gVGhlc2UgYXJlIGltcG9ydGFudCBza2lsbHMgc2luY2Ugd2hlbmV2ZXIgeW91IGhvbGQgb3IgbWFuYWdlIG1vcmUgdGhhbiBvbmUgYXNzZXQsIHlvdSBoYXZlIGEgcG9ydGZvbGlvIHRvIGNvbnNpZGVyLg0KDQpJdCBpcyBub3cgdGltZSB0byBjb21wb3VuZCBhbmQgZ2V0IGFuIGV4dHJhIHJldHVybiBieSBleHBsb3Jpbmcgb3RoZXIgdXNlZnVsIFIgcGFja2FnZXMgZm9yIHBvcnRmb2xpbyBhbmFseXNpcyBsaWtlIFBvcnRmb2xpb0FuYWx5dGljcy4gSW5zdGVhZCBvZiBvcHRpbWl6aW5nIHRoZSBwb3J0Zm9saW8gYnkgbWluaW1pemluZyB0aGUgdmFyaWFuY2UgdW5kZXIgYSByZXR1cm4gdGFyZ2V0IGNvbnN0cmFpbnQsIHRoaXMgcGFja2FnZSBhbGxvd3MgeW91IHRvIG9wdGltaXplIHZpcnR1YWxseSBhbGwgb2JqZWN0aXZlIGZ1bmN0aW9ucyB1bmRlciBhbGwgcG9zc2libGUgY29uc3RyYWludHMuIEV2ZW4gaWYgdGhlIHNvbHV0aW9uIHRvIHRoZSBwcm9ibGVtIGlzIG5vdCBmZWFzaWJsZSwgaXQgd2lsbCBnaXZlIHlvdSB0aGUgYmVzdCBwb3NzaWJsZSBzb2x1dGlvbiB1c2luZyBoZXVyaXN0aWNzLCBzdWNoIGFzIGRpZmZlcmVudGlhbCBldm9sdXRpb24gaW4gdGhlIFIgcGFja2FnZSBbREVvcHRpbV0oIGh0dHBzOi8vam91cm5hbC5yLXByb2plY3Qub3JnL2FyY2hpdmUvMjAxMS0xL1JKb3VybmFsXzIwMTEtMV9BcmRpYX5ldH5hbC5wZGYpLg0K