1 Introduction and Portfolio Theory

1.1 Welcome

1.1.1 Load the PortfolioAnalytics package

The package PortfolioAnalytics will be used throughout this course for portfolio optimization and analysis. We will use the indexes dataset that is included with the PortfolioAnalytics package for the remaining exercises in this chapter. In this exercise, we will load the package and prepare the data for the portfolio optimization problem in the next exercise.

# Load the package
library(PortfolioAnalytics)

# Load the data
data(indexes)

# Subset the data
index_returns <- indexes[, c(1:4)]

# Print the head of the data
head(index_returns)
           US Bonds US Equities Int'l Equities Commodities
1980-01-31  -0.0272      0.0610         0.0462      0.0568
1980-02-29  -0.0669      0.0031        -0.0040     -0.0093
1980-03-31   0.0053     -0.0987        -0.1188     -0.1625
1980-04-30   0.0992      0.0429         0.0864      0.0357
1980-05-31   0.0000      0.0562         0.0446      0.0573
1980-06-30   0.0605      0.0296         0.0600      0.0533

1.1.2 Solve a simple portfolio optimization problem

This first exercise will teach you how to solve a simple portfolio optimization problem using PortfolioAnalytics. You will learn how to create a portfolio specification object, add constraints and objectives, and solve the optimization problem. The portfolio problem is to form a minimum variance portfolio subject to full investment and long only constraints. The objective is to minimize portfolio variance. There are two constraints in this problem: the full investment constraint means that the weights must sum to 1, and the long only constraint means that all weights must be greater than or equal to 0 (i.e. no short positions are allowed).

You need to install below libraries for ROI to work:

  • ROI
  • ROI.plugin.quadprog
  • ROI.plugin.glpk
# Create the portfolio specification
port_spec <- portfolio.spec(colnames(index_returns))

# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio =port_spec, type = "full_investment")

# Add a long only constraint such that the weight of an asset is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only")

# Add an objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")

# Solve the optimization problem
opt <- optimize.portfolio(index_returns, portfolio = port_spec, optimize_method = "ROI")

As you will see, the modular nature of the contraints and objectives makes this very flexible!

1.1.3 Visualize results

Now that we have run the optimization, we would like to take a look at the output and results. Recall that the optimization output is in a variable named opt. In our case, for the portfolio optimization in the previous exercise, we are interested in the optimal weights and estimated objective value. The weights are considered optimal in the sense that the set of weights minimizes the objective value, portfolio standard deviation, and satisfies the full investment and long only constraints based on historical data.

# Print the results of the optimization
print(opt)
***********************************
PortfolioAnalytics Optimization
***********************************

Call:
optimize.portfolio(R = index_returns, portfolio = port_spec, 
    optimize_method = "ROI")

Optimal Weights:
      US Bonds    US Equities Int'l Equities    Commodities 
        0.8544         0.0587         0.0000         0.0869 

Objective Measure:
 StdDev 
0.01668 
# Extract the optimal weights
extractWeights(opt)
      US Bonds    US Equities Int'l Equities    Commodities 
    0.85441777     0.05872385     0.00000000     0.08685838 
# Chart the optimal weights
chart.Weights(opt)

Visualization is important for any analysis, especially portfolio optimization. Modern Portfolio Theory (MPT) is all about maximizing gain while simultaneously controlling for risk. Academically, risk and gain are defined as standard deviation and mean return. In practice, many other measures of risk and gain are also used!

1.2 Challenges of portfolio optimization

1.2.1 Quadratic utility

The formulation of quadratic utility is:

Maximize \(w^T∗μ−λ∗w^T∗Σ∗w\)

This formula maximizes the expected return while penalizing for the standard deviation scaled by the risk aversion parameter, lambda!

1.2.2 Maximize quadratic utility function

In the video on challenges of portfolio optimization, you saw how to solve a quadratic utility optimization problem with the package quadprog. This exercise will show you how to solve a quadratic utility problem using the PortfolioAnalytics package. Recall the quadratic utility formulation has two terms, one for portfolio mean return and another for portfolio variance with a risk aversion parameter, lambda.

# Create the portfolio specification
port_spec <- portfolio.spec(assets = colnames(index_returns))

# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio = port_spec, type = "full_investment")

# Add a long only constraint such that the weight of an asset is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only")

# Add an objective to maximize portfolio mean return
port_spec <- add.objective(portfolio = port_spec, type = "return", name = "mean")

# Add an objective to minimize portfolio variance
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "var", risk_aversion = 10)

# Solve the optimization problem
opt <- optimize.portfolio(R = index_returns, portfolio = port_spec, optimize_method = "ROI")

1.3 Introduction to PortfolioAnalytics

1.3.1 Key design goals

Modular constraints and objectives to add, remove, and combine multiple constraint and objective types very easily

Support for user defined moment functions

Visualizations to build intuition about the problem and understand the feasible space of portfolios

PortfolioAnalytics switches seamlessly through a number of solvers.

2 Portfolio Optimization Workflow

2.1 Portfolio specification, constraints, and objectives

2.1.1 Create a portfolio specification

The first step in the workflow of PortfolioAnalytics is to create the portfolio specification object. The portfolio specification holds portfolio level data, constraints and objectives. The only required argument to portfolio.spec() is assets. assets can be the number of assets, a named vector of seed weights, or a character vector of the asset names. The category_labels argument is used for labeling assets by a category such as sector, industry, region, asset class, or currency. The weight_seq argument allows you to specify a seed sequence of weights that are used by the random portfolios algorithms. Common practice is to pass in the column names of the returns object for the assets argument.

data(edhec)
asset_returns <- edhec
# Get the column names of the returns data
asset_names <- colnames(asset_returns)

# Create a portfolio specification object using asset_names
port_spec <- portfolio.spec(assets = asset_names)

# Get the class of the portfolio specification object
class(port_spec)
[1] "portfolio.spec" "portfolio"     
# Print the portfolio specification object
print(port_spec)
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = asset_names)

Number of assets: 13 
Asset Names
 [1] "Convertible Arbitrage"  "CTA Global"            
 [3] "Distressed Securities"  "Emerging Markets"      
 [5] "Equity Market Neutral"  "Event Driven"          
 [7] "Fixed Income Arbitrage" "Global Macro"          
 [9] "Long/Short Equity"      "Merger Arbitrage"      
More than 10 assets, only printing the first 10

Now that you have the base specification, let’s add some constraints.

2.1.2 Add constraints

Constraints are added to the portfolio specification object with the add.constraint() function. Each constraint added is a separate object and stored in the constraints slot in the portfolio object. In this way, the constraints are modular and one can easily add, remove, or modify the constraints in the portfolio object. The required arguments for add.constraint() are the portfolio the constraint is added to, the constraint type, and named arguments passed via … to the constructor of the constraint type. Basic constraint types:

  • Specify the constraint on the sum of the weights
    • weight_sum, weight, leverage
    • full_investment is a special case that sets min_sum = max_sum = 1
    • dollar_neutral is a special case that sets min_sum = max_sum = 0
  • Specify constraints for the individual asset weights
    • box
    • long_only is a special case that sets min = 0 and max = 1
  • Specify the constraint for the sum of weights of assets by group (sector, region, asset class, etc.)
    • group
  • Specify a constraint on the target mean return
    • return

In this exercise, you will add a few of the more common constraint types. In addition to the basic constraint types listed above, PortfolioAnalytics also supports position limit, turnover, diversification, factor exposure, and leverage exposure constraint types. If you are interested in the other constraint types, look at the help files for the constraint constructors. The help files include a description of the constraint type as well as example code.

# Add the weight sum constraint
port_spec <- add.constraint(portfolio = port_spec, type = "weight_sum", min_sum = 1, max_sum = 1)

# Add the box constraint
port_spec <- add.constraint(portfolio = port_spec, type = "box", min = c(0.1, 0.1, 0.1, 0.1, 0.1, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05), max = 0.4)

# Add the group constraint
port_spec <- add.constraint(portfolio = port_spec, type = "group", groups = list(c(1, 5, 7, 9, 10, 11), c(2, 3, 4, 6, 8, 12)), group_min = 0.4, group_max = 0.6)


# Print the portfolio specification object
print(port_spec)
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = asset_names)

Number of assets: 13 
Asset Names
 [1] "Convertible Arbitrage"  "CTA Global"            
 [3] "Distressed Securities"  "Emerging Markets"      
 [5] "Equity Market Neutral"  "Event Driven"          
 [7] "Fixed Income Arbitrage" "Global Macro"          
 [9] "Long/Short Equity"      "Merger Arbitrage"      
More than 10 assets, only printing the first 10

Constraints
Enabled constraint types
        - weight_sum 
        - box 
        - group 

There are a number of different constraint types, check them out using ?add.constraint

Objectives are added to the portfolio object with the add.objective() function. Each objective added is a separate object and stored in the objectives slot in the portfolio specification object. In this way, the objectives are modular and one can easily add, remove, or modify the objective objects. The name argument must be a valid R function. Several functions are available in the PerformanceAnalytics package, but user defined functions can also be used as objective functions. The required arguments for add.objective() are the portfolio the objective is added to, the objective type, the objective name, and named arguments passed via … to the constructor of the objective type. Arguments for the objective function are specified as a named list to arguments.

Basic objective types:

  • return: This objective type seeks to maximize the objective.
  • risk: This objective type seeks to minimize the objective.
  • risk_budget: This objective type seeks to minimize risk concentration or penalize contribution to risk that exceeds the minimum or maximum allowable percentage contribution to risk.

In addition to the objective types listed above, PortfolioAnalytics also supports quadratic utility and weight concentration objective types.

# Add a return objective to maximize mean return
port_spec <- add.objective(portfolio = port_spec, type = "return", name = "mean")

# Add a risk objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")

# Add a risk budget objective
port_spec <- add.objective(portfolio = port_spec, type = "risk_budget", name = "StdDev", min_prisk = 0.05, max_prisk = 0.1)

# Print the portfolio specification object
print(port_spec)
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = asset_names)

Number of assets: 13 
Asset Names
 [1] "Convertible Arbitrage"  "CTA Global"            
 [3] "Distressed Securities"  "Emerging Markets"      
 [5] "Equity Market Neutral"  "Event Driven"          
 [7] "Fixed Income Arbitrage" "Global Macro"          
 [9] "Long/Short Equity"      "Merger Arbitrage"      
More than 10 assets, only printing the first 10

Constraints
Enabled constraint types
        - weight_sum 
        - box 
        - group 

Objectives:
Enabled objective names
        - mean 
        - StdDev 
        - StdDev 

2.2 Running optimizations

2.2.1 Single-Period optimization

There are two functions for running the optimization, optimize.portfolio() and optimize.portfolio.rebalancing(). This exercise will focus on single period optimization and the next exercise will use optimize.portfolio.rebalancing() for optimization with periodic rebalancing. optimize.portfolio() supports single-period optimization. Key arguments include R for the asset returns, portfolio for the portfolio specification object, and optimize_method to specify the optimization method used to solve the problem. In many cases, it is useful to specify trace = TRUE to store additional information for each iteration/trial of the optimization.

The following optimization methods are supported:

  • DEoptim: Differential evolution
  • random: Random portfolios
  • GenSA: Generalized Simulated Annealing
  • pso: Particle swarm optimization
  • ROI: R Optimization Infrastructure for linear and quadratic programming solvers

The optimization method you choose should be based on the type of problem you are solving. For example, a problem that can be formulated as a quadratic programming problem should be solved using a quadratic programming solver, whereas a non-convex problem should be solved using a global solver such as DEoptim.

In this exercise, we will define the portfolio optimization problem to maximize mean return and minimize portfolio standard deviation with a standard deviation risk budget where the minimum percentage risk is 5% and the maximum percentage risk is 10%, subject to full investment and long only constraints. The risk budget objective requires a global solver so we will solve the problem using random portfolios. The set of random portfolios, rp, is generated using 500 permutations for this exercise.

rp <- random_portfolios(portfolio=port_spec, permutations = 500, rp_method ='simplex')
#rp <- random_portfolios(portfolio=port_spec, permutations=500, #rp_method='sample')
#rp <- random_portfolios(portfolio=port_spec, permutations=500, #rp_method='grid')
# Run a single period optimization using random portfolios as the optimization method
opt <- optimize.portfolio(R = asset_returns, portfolio = port_spec, optimize_method = "random", rp = rp, trace = TRUE)
Leverage constraint min_sum and max_sum are restrictive, 
              consider relaxing. e.g. 'full_investment' constraint should be min_sum=0.99 and max_sum=1.01
# Print the output of the single-period optimization
print(opt)
***********************************
PortfolioAnalytics Optimization
***********************************

Call:
optimize.portfolio(R = asset_returns, portfolio = port_spec, 
    optimize_method = "random", trace = TRUE, rp = rp)

Optimal Weights:
 Convertible Arbitrage             CTA Global  Distressed Securities 
                  0.10                   0.10                   0.10 
      Emerging Markets  Equity Market Neutral           Event Driven 
                  0.10                   0.10                   0.05 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
                  0.05                   0.05                   0.05 
      Merger Arbitrage         Relative Value          Short Selling 
                  0.05                   0.05                   0.15 
        Funds of Funds 
                  0.05 

Objective Measures:
    mean 
0.004265 


  StdDev 
0.008989 

contribution :
 Convertible Arbitrage             CTA Global  Distressed Securities 
             0.0010590              0.0010412              0.0010417 
      Emerging Markets  Equity Market Neutral           Event Driven 
             0.0018036              0.0004904              0.0005022 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
             0.0003724              0.0005020              0.0005084 
      Merger Arbitrage         Relative Value          Short Selling 
             0.0002266              0.0003762              0.0005633 
        Funds of Funds 
             0.0005016 

pct_contrib_StdDev :
 Convertible Arbitrage             CTA Global  Distressed Securities 
               0.11782                0.11584                0.11589 
      Emerging Markets  Equity Market Neutral           Event Driven 
               0.20065                0.05456                0.05587 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
               0.04144                0.05585                0.05656 
      Merger Arbitrage         Relative Value          Short Selling 
               0.02521                0.04185                0.06267 
        Funds of Funds 
               0.05580 

You just optimized a portfolio that minimized the standard deviation and maximized the mean return!

2.2.2 Optimization with periodic rebalancing

Running the optimization with periodic rebalancing and analyzing the out-of-sample results of the backtest is an important step to better understand and potentially refine the constraints and objectives. optimize.portfolio.rebalancing() supports optimization with periodic rebalancing (backtesting) to examine out of sample performance. In addition to the arguments for optimize.portfolio(), a periodic rebalancing frequency must be specified with rebalance_on, training_period to specify the number of periods to use as the training data for the initial optimization, and rolling_window to specify the number of periods for the window width of the optimization. If rolling_window is set to NULL each optimization will use all data available at the given period the optimization is run.

To reduce computation time for this exercise, the set of random portfolios, rp, is generated using 50 permutations, and search_size, how many portfolios to test, is set to 1000. If you are actually optimizing portfolios yourself, you’ll probably want to test more portfolios (the default value for search_size is 20,000)!

Run the optimization with quarterly rebalancing. Set the training period and rolling window to 60 periods. The dataset is monthly data so we are using 5 years of historical data. Assign the optimization output to a variable named opt_rebal.

# Create a portfolio specification object using asset_names
port_spec <- portfolio.spec(assets = asset_names)
# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio =port_spec, type = "long_only")
# Add the weight sum constraint
port_spec <- add.constraint(portfolio = port_spec, type = "weight_sum", min_sum = 0.99, max_sum = 1.01)
# Add a return objective to maximize mean return
port_spec <- add.objective(portfolio = port_spec, type = "return", name = "mean")

# Add a risk objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")

# Add a risk budget objective
port_spec <- add.objective(portfolio = port_spec, 
        type = "risk_budget", name = "StdDev", min_prisk = 0.05, 
        max_prisk = 0.1)
rp <- random_portfolios(portfolio=port_spec,
                        permutations = 50,
                        rp_method='simplex')
# Run the optimization backtest with quarterly rebalancing
opt_rebal <- optimize.portfolio.rebalancing(R = asset_returns, portfolio = port_spec, optimize_method = "random", rp = rp, trace = TRUE, search_size = 1000, rebalance_on = "quarters", training_period = 60, rolling_window = 60)


# Print the output of the optimization backtest
print(opt_rebal)
**************************************************
PortfolioAnalytics Optimization with Rebalancing
**************************************************

Call:
optimize.portfolio.rebalancing(R = asset_returns, portfolio = port_spec, 
    optimize_method = "random", search_size = 1000, trace = TRUE, 
    rp = rp, rebalance_on = "quarters", training_period = 60, 
    rolling_window = 60)

Number of rebalancing dates:  73 
First rebalance date:
[1] "2001-12-31"
Last rebalance date:
[1] "2019-11-30"

Annualized Portfolio Rebalancing Return:
[1] 0.04308334

Annualized Portfolio Standard Deviation:
[1] 0.03502795

Using backtesting can give useful insights on how your optimization setup performs under different circumstances.

2.3 Analyzing optimization results

2.3.1 Objective measure values

This exercise is a continuation of the last exercises analysing the output of opt and opt_rebal. All objectives in the portfolio specification are evaluated during the optimization and the values can be extracted with extractObjectiveMeasures(). The objective measures are an important component of the optimization output to analyze. An important use case looks at the result of each iteration/trial of the optimizer. This can give you insight into how the optimizer approaches the optimal solution and potentially tune parameters. We can also analyse the distribution of the in-sample objective values of the optimal portfolios and compare to out-of-sample performance.

# Extract the objective measures for the single period optimization
extractObjectiveMeasures(opt)
$mean
       mean 
0.004265327 

$StdDev
$StdDev$StdDev
[1] 0.008988572

$StdDev$contribution
 Convertible Arbitrage             CTA Global  Distressed Securities 
          0.0010590433           0.0010411982           0.0010416617 
      Emerging Markets  Equity Market Neutral           Event Driven 
          0.0018035800           0.0004903910           0.0005022044 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
          0.0003724447           0.0005019871           0.0005084311 
      Merger Arbitrage         Relative Value          Short Selling 
          0.0002265651           0.0003761851           0.0005632954 
        Funds of Funds 
          0.0005015844 

$StdDev$pct_contrib_StdDev
 Convertible Arbitrage             CTA Global  Distressed Securities 
            0.11782108             0.11583578             0.11588735 
      Emerging Markets  Equity Market Neutral           Event Driven 
            0.20065257             0.05455717             0.05587143 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
            0.04143536             0.05584726             0.05656417 
      Merger Arbitrage         Relative Value          Short Selling 
            0.02520591             0.04185149             0.06266795 
        Funds of Funds 
            0.05580246 
# Extract the objective measures for the optimization backtest
head(extractObjectiveMeasures(opt_rebal))
                  mean      StdDev
2001-12-31 0.008784722 0.007922754
2002-03-31 0.008271627 0.007722497
2002-06-30 0.008328058 0.007720750
2002-09-30 0.007900130 0.007430129
2002-12-31 0.007571272 0.007584567
2003-03-31 0.007347474 0.007692301
           StdDev.contribution.Convertible Arbitrage
2001-12-31                              0.0003401262
2002-03-31                              0.0003596149
2002-06-30                              0.0003634403
2002-09-30                              0.0003805327
2002-12-31                              0.0003708623
2003-03-31                              0.0003861341
           StdDev.contribution.CTA Global
2001-12-31                   0.0010331223
2002-03-31                   0.0009535650
2002-06-30                   0.0008925825
2002-09-30                   0.0005477816
2002-12-31                   0.0007286846
2003-03-31                   0.0009089203
           StdDev.contribution.Distressed Securities
2001-12-31                              5.456254e-05
2002-03-31                              5.452174e-05
2002-06-30                              5.618569e-05
2002-09-30                              5.667865e-05
2002-12-31                              5.390294e-05
2003-03-31                              5.284363e-05
           StdDev.contribution.Emerging Markets
2001-12-31                         1.033830e-04
2002-03-31                         6.934062e-05
2002-06-30                         7.350974e-05
2002-09-30                         5.277680e-05
2002-12-31                         4.523016e-05
2003-03-31                         3.776604e-05
           StdDev.contribution.Equity Market Neutral
2001-12-31                              2.182620e-05
2002-03-31                              2.169650e-05
2002-06-30                              2.250135e-05
2002-09-30                              1.817923e-05
2002-12-31                              1.836090e-05
2003-03-31                              1.895013e-05
           StdDev.contribution.Event Driven
2001-12-31                     0.0002727352
2002-03-31                     0.0002730606
2002-06-30                     0.0002842180
2002-09-30                     0.0002840013
2002-12-31                     0.0002491465
2003-03-31                     0.0002296462
           StdDev.contribution.Fixed Income Arbitrage
2001-12-31                               0.0009139324
2002-03-31                               0.0008734091
2002-06-30                               0.0008824052
2002-09-30                               0.0008965251
2002-12-31                               0.0009065155
2003-03-31                               0.0008966847
           StdDev.contribution.Global Macro
2001-12-31                     0.0009543666
2002-03-31                     0.0008605850
2002-06-30                     0.0008751872
2002-09-30                     0.0006867022
2002-12-31                     0.0006883180
2003-03-31                     0.0007050678
           StdDev.contribution.Long/Short Equity
2001-12-31                          1.088709e-05
2002-03-31                          1.170751e-05
2002-06-30                          1.327361e-05
2002-09-30                          1.214673e-05
2002-12-31                          4.444858e-06
2003-03-31                          1.884263e-06
           StdDev.contribution.Merger Arbitrage
2001-12-31                         0.0004516729
2002-03-31                         0.0004842594
2002-06-30                         0.0004961724
2002-09-30                         0.0005077632
2002-12-31                         0.0004669544
2003-03-31                         0.0004389722
           StdDev.contribution.Relative Value
2001-12-31                        0.001566572
2002-03-31                        0.001539066
2002-06-30                        0.001596227
2002-09-30                        0.001707684
2002-12-31                        0.001527948
2003-03-31                        0.001489758
           StdDev.contribution.Short Selling
2001-12-31                       0.002145965
2002-03-31                       0.002172074
2002-06-30                       0.002114347
2002-09-30                       0.002236516
2002-12-31                       0.002484087
2003-03-31                       0.002487312
           StdDev.contribution.Funds of Funds
2001-12-31                       5.360247e-05
2002-03-31                       4.959677e-05
2002-06-30                       5.069969e-05
2002-09-30                       4.284070e-05
2002-12-31                       4.011111e-05
2003-03-31                       3.836093e-05
           StdDev.pct_contrib_StdDev.Convertible Arbitrage
2001-12-31                                      0.04293030
2002-03-31                                      0.04656718
2002-06-30                                      0.04707319
2002-09-30                                      0.05121482
2002-12-31                                      0.04889697
2003-03-31                                      0.05019748
           StdDev.pct_contrib_StdDev.CTA Global
2001-12-31                           0.13039939
2002-03-31                           0.12347885
2002-06-30                           0.11560827
2002-09-30                           0.07372437
2002-12-31                           0.09607464
2003-03-31                           0.11815974
           StdDev.pct_contrib_StdDev.Distressed Securities
2001-12-31                                     0.006886815
2002-03-31                                     0.007060118
2002-06-30                                     0.007277233
2002-09-30                                     0.007628219
2002-12-31                                     0.007106924
2003-03-31                                     0.006869678
           StdDev.pct_contrib_StdDev.Emerging Markets
2001-12-31                                0.013048873
2002-03-31                                0.008979042
2002-06-30                                0.009521063
2002-09-30                                0.007103080
2002-12-31                                0.005963446
2003-03-31                                0.004909589
           StdDev.pct_contrib_StdDev.Equity Market Neutral
2001-12-31                                     0.002754876
2002-03-31                                     0.002809519
2002-06-30                                     0.002914400
2002-09-30                                     0.002446692
2002-12-31                                     0.002420824
2003-03-31                                     0.002463519
           StdDev.pct_contrib_StdDev.Event Driven
2001-12-31                             0.03442429
2002-03-31                             0.03535911
2002-06-30                             0.03681223
2002-09-30                             0.03822293
2002-12-31                             0.03284914
2003-03-31                             0.02985403
           StdDev.pct_contrib_StdDev.Fixed Income Arbitrage
2001-12-31                                        0.1153554
2002-03-31                                        0.1130993
2002-06-30                                        0.1142901
2002-09-30                                        0.1206608
2002-12-31                                        0.1195211
2003-03-31                                        0.1165691
           StdDev.pct_contrib_StdDev.Global Macro
2001-12-31                             0.12045894
2002-03-31                             0.11143870
2002-06-30                             0.11335521
2002-09-30                             0.09242131
2002-12-31                             0.09075245
2003-03-31                             0.09165890
           StdDev.pct_contrib_StdDev.Long/Short Equity
2001-12-31                                0.0013741542
2002-03-31                                0.0015160259
2002-06-30                                0.0017192129
2002-09-30                                0.0016347937
2002-12-31                                0.0005860398
2003-03-31                                0.0002449543
           StdDev.pct_contrib_StdDev.Merger Arbitrage
2001-12-31                                 0.05700958
2002-03-31                                 0.06270761
2002-06-30                                 0.06426479
2002-09-30                                 0.06833841
2002-12-31                                 0.06156639
2003-03-31                                 0.05706644
           StdDev.pct_contrib_StdDev.Relative Value
2001-12-31                                0.1977307
2002-03-31                                0.1992964
2002-06-30                                0.2067450
2002-09-30                                0.2298324
2002-12-31                                0.2014549
2003-03-31                                0.1936688
           StdDev.pct_contrib_StdDev.Short Selling
2001-12-31                               0.2708610
2002-03-31                               0.2812658
2002-06-30                               0.2738526
2002-09-30                               0.3010064
2002-12-31                               0.3275186
2003-03-31                               0.3233509
           StdDev.pct_contrib_StdDev.Funds of Funds
2001-12-31                              0.006765636
2002-03-31                              0.006422375
2002-06-30                              0.006566680
2002-09-30                              0.005765809
2002-12-31                              0.005288517
2003-03-31                              0.004986925

Studing the objective measures for a rebalanced portfolio can provide insights into how it changed over time.

2.3.2 Optimal weights

This exercise is a continuation of the last exercises analysing the the output of opt and opt_rebal. Extracting and visualizing the optimal weights is an important component of the optimization. The optimal weights can be extracted with extractWeights() and charted with chart.Weights(). This is particularly useful for backtests to understand the evolution of weights over time. We can then answer questions about how allocations change through time.

# Extract the optimal weights for the single period optimization
extractWeights(opt)
 Convertible Arbitrage             CTA Global  Distressed Securities 
                  0.10                   0.10                   0.10 
      Emerging Markets  Equity Market Neutral           Event Driven 
                  0.10                   0.10                   0.05 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
                  0.05                   0.05                   0.05 
      Merger Arbitrage         Relative Value          Short Selling 
                  0.05                   0.05                   0.15 
        Funds of Funds 
                  0.05 
# Chart the weights for the single period optimization
chart.Weights(opt)


# Extract the optimal weights for the optimization backtest
head(extractWeights(opt_rebal))
           Convertible Arbitrage CTA Global Distressed Securities
2001-12-31            0.06825613   0.105605            0.01061614
2002-03-31            0.06825613   0.105605            0.01061614
2002-06-30            0.06825613   0.105605            0.01061614
2002-09-30            0.06825613   0.105605            0.01061614
2002-12-31            0.06825613   0.105605            0.01061614
2003-03-31            0.06825613   0.105605            0.01061614
           Emerging Markets Equity Market Neutral Event Driven
2001-12-31       0.01130702            0.00941978   0.04783457
2002-03-31       0.01130702            0.00941978   0.04783457
2002-06-30       0.01130702            0.00941978   0.04783457
2002-09-30       0.01130702            0.00941978   0.04783457
2002-12-31       0.01130702            0.00941978   0.04783457
2003-03-31       0.01130702            0.00941978   0.04783457
           Fixed Income Arbitrage Global Macro Long/Short Equity
2001-12-31             0.09509155   0.08732321       0.006100986
2002-03-31             0.09509155   0.08732321       0.006100986
2002-06-30             0.09509155   0.08732321       0.006100986
2002-09-30             0.09509155   0.08732321       0.006100986
2002-12-31             0.09509155   0.08732321       0.006100986
2003-03-31             0.09509155   0.08732321       0.006100986
           Merger Arbitrage Relative Value Short Selling Funds of Funds
2001-12-31        0.1163051      0.3366911     0.0953108     0.01013861
2002-03-31        0.1163051      0.3366911     0.0953108     0.01013861
2002-06-30        0.1163051      0.3366911     0.0953108     0.01013861
2002-09-30        0.1163051      0.3366911     0.0953108     0.01013861
2002-12-31        0.1163051      0.3366911     0.0953108     0.01013861
2003-03-31        0.1163051      0.3366911     0.0953108     0.01013861
# Chart the weights for the optimization backtest
chart.Weights(opt_rebal)

Visualizing a rebalanced portfolio’s weights over time can help you identify potential problems in your model.

3 Objective Functions and Moment Estimation

3.1 Introduction to moments

3.1.1 Sample moment estimates

The default method for estimating portfolio moments is the sample method. The moments are calculated in optimize.portfolio() by evaluating the function passed to the momentFUN argument. The default for momentFUN is set.portfolio.moments() which defaults to calculating the sample moments. The moments are then used as inputs to the objective functions. The moments that must be estimated depend on the objectives. For example, an objective to minimize portfolio standard deviation requires only an estimate of the second moment. Compare that to the objective to maximize Sharpe Ratio which requires the first and second moments to be estimated. Sample estimates of the moments have disadvantages including estimation error and the curse of dimensionality. There is an increased risk of estimation error as the dimension of assets and parameters to estimate increase.

# Add a return objective with "mean" as the objective name
port_spec <- add.objective(portfolio = port_spec, type = "return", name = "mean")

# Calculate the sample moments
moments <- set.portfolio.moments(R = asset_returns, portfolio = port_spec)

# Check if moments$mu is equal to the sample estimate of mean returns
moments$mu == colMeans(asset_returns)
      [,1]
 [1,] TRUE
 [2,] TRUE
 [3,] TRUE
 [4,] TRUE
 [5,] TRUE
 [6,] TRUE
 [7,] TRUE
 [8,] TRUE
 [9,] TRUE
[10,] TRUE
[11,] TRUE
[12,] TRUE
[13,] TRUE
# Add a risk objective with "StdDev" as the objective name
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")

# Calculate the sample moments using set.portfolio.moments. Assign to a variable named moments.
moments <- set.portfolio.moments(R = asset_returns, portfolio = port_spec)

# Check if moments$sigma is equal to the sample estimate of the variance-covariance matrix
moments$sigma == cov(asset_returns)
                       Convertible Arbitrage CTA Global
Convertible Arbitrage                   TRUE       TRUE
CTA Global                              TRUE       TRUE
Distressed Securities                   TRUE       TRUE
Emerging Markets                        TRUE       TRUE
Equity Market Neutral                   TRUE       TRUE
Event Driven                            TRUE       TRUE
Fixed Income Arbitrage                  TRUE       TRUE
Global Macro                            TRUE       TRUE
Long/Short Equity                       TRUE       TRUE
Merger Arbitrage                        TRUE       TRUE
Relative Value                          TRUE       TRUE
Short Selling                           TRUE       TRUE
Funds of Funds                          TRUE       TRUE
                       Distressed Securities Emerging Markets
Convertible Arbitrage                   TRUE             TRUE
CTA Global                              TRUE             TRUE
Distressed Securities                   TRUE             TRUE
Emerging Markets                        TRUE             TRUE
Equity Market Neutral                   TRUE             TRUE
Event Driven                            TRUE             TRUE
Fixed Income Arbitrage                  TRUE             TRUE
Global Macro                            TRUE             TRUE
Long/Short Equity                       TRUE             TRUE
Merger Arbitrage                        TRUE             TRUE
Relative Value                          TRUE             TRUE
Short Selling                           TRUE             TRUE
Funds of Funds                          TRUE             TRUE
                       Equity Market Neutral Event Driven
Convertible Arbitrage                   TRUE         TRUE
CTA Global                              TRUE         TRUE
Distressed Securities                   TRUE         TRUE
Emerging Markets                        TRUE         TRUE
Equity Market Neutral                   TRUE         TRUE
Event Driven                            TRUE         TRUE
Fixed Income Arbitrage                  TRUE         TRUE
Global Macro                            TRUE         TRUE
Long/Short Equity                       TRUE         TRUE
Merger Arbitrage                        TRUE         TRUE
Relative Value                          TRUE         TRUE
Short Selling                           TRUE         TRUE
Funds of Funds                          TRUE         TRUE
                       Fixed Income Arbitrage Global Macro
Convertible Arbitrage                    TRUE         TRUE
CTA Global                               TRUE         TRUE
Distressed Securities                    TRUE         TRUE
Emerging Markets                         TRUE         TRUE
Equity Market Neutral                    TRUE         TRUE
Event Driven                             TRUE         TRUE
Fixed Income Arbitrage                   TRUE         TRUE
Global Macro                             TRUE         TRUE
Long/Short Equity                        TRUE         TRUE
Merger Arbitrage                         TRUE         TRUE
Relative Value                           TRUE         TRUE
Short Selling                            TRUE         TRUE
Funds of Funds                           TRUE         TRUE
                       Long/Short Equity Merger Arbitrage Relative Value
Convertible Arbitrage               TRUE             TRUE           TRUE
CTA Global                          TRUE             TRUE           TRUE
Distressed Securities               TRUE             TRUE           TRUE
Emerging Markets                    TRUE             TRUE           TRUE
Equity Market Neutral               TRUE             TRUE           TRUE
Event Driven                        TRUE             TRUE           TRUE
Fixed Income Arbitrage              TRUE             TRUE           TRUE
Global Macro                        TRUE             TRUE           TRUE
Long/Short Equity                   TRUE             TRUE           TRUE
Merger Arbitrage                    TRUE             TRUE           TRUE
Relative Value                      TRUE             TRUE           TRUE
Short Selling                       TRUE             TRUE           TRUE
Funds of Funds                      TRUE             TRUE           TRUE
                       Short Selling Funds of Funds
Convertible Arbitrage           TRUE           TRUE
CTA Global                      TRUE           TRUE
Distressed Securities           TRUE           TRUE
Emerging Markets                TRUE           TRUE
Equity Market Neutral           TRUE           TRUE
Event Driven                    TRUE           TRUE
Fixed Income Arbitrage          TRUE           TRUE
Global Macro                    TRUE           TRUE
Long/Short Equity               TRUE           TRUE
Merger Arbitrage                TRUE           TRUE
Relative Value                  TRUE           TRUE
Short Selling                   TRUE           TRUE
Funds of Funds                  TRUE           TRUE

Simple sample moments might not always be a good estimate. Let’s look at some other methods for estimating moments. ### Advanced moment estimates PortfolioAnalytics supports the “sample” method as well as three more advanced methods for estimating portfolio moments.

  1. “sample”: Basic sample estimate of first four moments.
  2. “boudt”: The first four moments are estimated by fitting a statistical factor model based on the work of Boudt et al., 2014.
  3. “black_litterman”: The first two moments are estimated using the Black-Litterman framework.
  4. “Meucci”: The first two moments are estimated using the Fully Flexible Views framework.

In this exercise, you will estimate the second moment using the “boudt” method. A portfolio specification object named port_spec with a “StdDev” objective has already been created.

# Print the portfolio specification object
print(port_spec)
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = asset_names)

Number of assets: 13 
Asset Names
 [1] "Convertible Arbitrage"  "CTA Global"            
 [3] "Distressed Securities"  "Emerging Markets"      
 [5] "Equity Market Neutral"  "Event Driven"          
 [7] "Fixed Income Arbitrage" "Global Macro"          
 [9] "Long/Short Equity"      "Merger Arbitrage"      
More than 10 assets, only printing the first 10

Constraints
Enabled constraint types
        - long_only 
        - weight_sum 

Objectives:
Enabled objective names
        - mean 
        - StdDev 
        - StdDev 
        - mean 
        - StdDev 
# Fit a statistical factor model to the asset returns
fit <- statistical.factor.model(R = asset_returns, k = 3)

# Estimate the portfolio moments using the "boudt" method with 3 factors
moments_boudt <- set.portfolio.moments(R = asset_returns, portfolio = port_spec, method = "boudt", k = 3)

# Check if the covariance matrix extracted from the model fit is equal to the estimate in `moments_boudt`
moments_boudt$sigma == extractCovariance(fit)
                       Convertible Arbitrage CTA Global
Convertible Arbitrage                   TRUE       TRUE
CTA Global                              TRUE       TRUE
Distressed Securities                   TRUE       TRUE
Emerging Markets                        TRUE       TRUE
Equity Market Neutral                   TRUE       TRUE
Event Driven                            TRUE       TRUE
Fixed Income Arbitrage                  TRUE       TRUE
Global Macro                            TRUE       TRUE
Long/Short Equity                       TRUE       TRUE
Merger Arbitrage                        TRUE       TRUE
Relative Value                          TRUE       TRUE
Short Selling                           TRUE       TRUE
Funds of Funds                          TRUE       TRUE
                       Distressed Securities Emerging Markets
Convertible Arbitrage                   TRUE             TRUE
CTA Global                              TRUE             TRUE
Distressed Securities                   TRUE             TRUE
Emerging Markets                        TRUE             TRUE
Equity Market Neutral                   TRUE             TRUE
Event Driven                            TRUE             TRUE
Fixed Income Arbitrage                  TRUE             TRUE
Global Macro                            TRUE             TRUE
Long/Short Equity                       TRUE             TRUE
Merger Arbitrage                        TRUE             TRUE
Relative Value                          TRUE             TRUE
Short Selling                           TRUE             TRUE
Funds of Funds                          TRUE             TRUE
                       Equity Market Neutral Event Driven
Convertible Arbitrage                   TRUE         TRUE
CTA Global                              TRUE         TRUE
Distressed Securities                   TRUE         TRUE
Emerging Markets                        TRUE         TRUE
Equity Market Neutral                   TRUE         TRUE
Event Driven                            TRUE         TRUE
Fixed Income Arbitrage                  TRUE         TRUE
Global Macro                            TRUE         TRUE
Long/Short Equity                       TRUE         TRUE
Merger Arbitrage                        TRUE         TRUE
Relative Value                          TRUE         TRUE
Short Selling                           TRUE         TRUE
Funds of Funds                          TRUE         TRUE
                       Fixed Income Arbitrage Global Macro
Convertible Arbitrage                    TRUE         TRUE
CTA Global                               TRUE         TRUE
Distressed Securities                    TRUE         TRUE
Emerging Markets                         TRUE         TRUE
Equity Market Neutral                    TRUE         TRUE
Event Driven                             TRUE         TRUE
Fixed Income Arbitrage                   TRUE         TRUE
Global Macro                             TRUE         TRUE
Long/Short Equity                        TRUE         TRUE
Merger Arbitrage                         TRUE         TRUE
Relative Value                           TRUE         TRUE
Short Selling                            TRUE         TRUE
Funds of Funds                           TRUE         TRUE
                       Long/Short Equity Merger Arbitrage Relative Value
Convertible Arbitrage               TRUE             TRUE           TRUE
CTA Global                          TRUE             TRUE           TRUE
Distressed Securities               TRUE             TRUE           TRUE
Emerging Markets                    TRUE             TRUE           TRUE
Equity Market Neutral               TRUE             TRUE           TRUE
Event Driven                        TRUE             TRUE           TRUE
Fixed Income Arbitrage              TRUE             TRUE           TRUE
Global Macro                        TRUE             TRUE           TRUE
Long/Short Equity                   TRUE             TRUE           TRUE
Merger Arbitrage                    TRUE             TRUE           TRUE
Relative Value                      TRUE             TRUE           TRUE
Short Selling                       TRUE             TRUE           TRUE
Funds of Funds                      TRUE             TRUE           TRUE
                       Short Selling Funds of Funds
Convertible Arbitrage           TRUE           TRUE
CTA Global                      TRUE           TRUE
Distressed Securities           TRUE           TRUE
Emerging Markets                TRUE           TRUE
Equity Market Neutral           TRUE           TRUE
Event Driven                    TRUE           TRUE
Fixed Income Arbitrage          TRUE           TRUE
Global Macro                    TRUE           TRUE
Long/Short Equity               TRUE           TRUE
Merger Arbitrage                TRUE           TRUE
Relative Value                  TRUE           TRUE
Short Selling                   TRUE           TRUE
Funds of Funds                  TRUE           TRUE

Some of the pitfalls of sample moments are avoided using these other estimation methods.

3.2 Custom moment functions

3.2.1 Define a custom moment function

In many cases for constrained optimization problems, the portfolio manager or analyst may want to estimate moments for a specific technique and/or further extend the idea of set.portfolio.moments(). A user defined custom moment function can have any arbitrary named arguments. However, arguments named R for the asset returns and portfolio for the portfolio object will be detected automatically and handled in an efficient manner. Because of this, it is strongly encouraged to use R for the asset returns object and portfolio for the portfolio object.

The custom moment function should return a named list where the elements represent the moments:

  • $mu: first moment (expected returns vector)
  • $sigma: second moment (variance-covariance matrix)
  • $m3: third moment (coskewness matrix)
  • $m4: fourth moment (cokurtosis matrix)

In this exercise, you will write a custom moment function to estimate the variance-covariance matrix using a robust method. We will use the cov.rob() function from the MASS package. The function signature should have arguments named R for the asset returns and portfolio for the specification object. The function should return a named list. Because you are only estimating the second moment, you only need to return a list with one element appropriately named. You can apply these rules to write custom moment functions for other models such as factor models, GARCH models, or any other class of models that theoretically should be a better estimate than the sample estimate.

  • Define a function named moments_robust that estimates the variance-covariance matrix of the asset returns using the “mcd” method.
  • Estimate the portfolio moments you just defined. Assign it to a variable named moments. You are doing this as a check to ensure that your custom moment function is working as expected.
  • Compute the variance-covariance matrix directly using cov.rob() and check if it is equal to moments$sigma
library(MASS)
# Define custom moment function
moments_robust <- function(R, portfolio){
  out <- list()
  out$sigma <- cov.rob(R, method = "mcd")$cov
  out
}

# Estimate the portfolio moments using the function you just defined 
moments <- moments_robust(R = asset_returns, portfolio = port_spec)

# Check the moment estimate
cov.rob(asset_returns, method = "mcd")$cov == moments$sigma
                       Convertible Arbitrage CTA Global
Convertible Arbitrage                  FALSE      FALSE
CTA Global                             FALSE      FALSE
Distressed Securities                  FALSE      FALSE
Emerging Markets                       FALSE      FALSE
Equity Market Neutral                  FALSE      FALSE
Event Driven                           FALSE      FALSE
Fixed Income Arbitrage                 FALSE      FALSE
Global Macro                           FALSE      FALSE
Long/Short Equity                      FALSE      FALSE
Merger Arbitrage                       FALSE      FALSE
Relative Value                         FALSE      FALSE
Short Selling                          FALSE      FALSE
Funds of Funds                         FALSE      FALSE
                       Distressed Securities Emerging Markets
Convertible Arbitrage                  FALSE            FALSE
CTA Global                             FALSE            FALSE
Distressed Securities                  FALSE            FALSE
Emerging Markets                       FALSE            FALSE
Equity Market Neutral                  FALSE            FALSE
Event Driven                           FALSE            FALSE
Fixed Income Arbitrage                 FALSE            FALSE
Global Macro                           FALSE            FALSE
Long/Short Equity                      FALSE            FALSE
Merger Arbitrage                       FALSE            FALSE
Relative Value                         FALSE            FALSE
Short Selling                          FALSE            FALSE
Funds of Funds                         FALSE            FALSE
                       Equity Market Neutral Event Driven
Convertible Arbitrage                  FALSE        FALSE
CTA Global                             FALSE        FALSE
Distressed Securities                  FALSE        FALSE
Emerging Markets                       FALSE        FALSE
Equity Market Neutral                  FALSE        FALSE
Event Driven                           FALSE        FALSE
Fixed Income Arbitrage                 FALSE        FALSE
Global Macro                           FALSE        FALSE
Long/Short Equity                      FALSE        FALSE
Merger Arbitrage                       FALSE        FALSE
Relative Value                         FALSE        FALSE
Short Selling                          FALSE        FALSE
Funds of Funds                         FALSE        FALSE
                       Fixed Income Arbitrage Global Macro
Convertible Arbitrage                   FALSE        FALSE
CTA Global                              FALSE        FALSE
Distressed Securities                   FALSE        FALSE
Emerging Markets                        FALSE        FALSE
Equity Market Neutral                   FALSE        FALSE
Event Driven                            FALSE        FALSE
Fixed Income Arbitrage                  FALSE        FALSE
Global Macro                            FALSE        FALSE
Long/Short Equity                       FALSE        FALSE
Merger Arbitrage                        FALSE        FALSE
Relative Value                          FALSE        FALSE
Short Selling                           FALSE        FALSE
Funds of Funds                          FALSE        FALSE
                       Long/Short Equity Merger Arbitrage Relative Value
Convertible Arbitrage              FALSE            FALSE          FALSE
CTA Global                         FALSE            FALSE          FALSE
Distressed Securities              FALSE            FALSE          FALSE
Emerging Markets                   FALSE            FALSE          FALSE
Equity Market Neutral              FALSE            FALSE          FALSE
Event Driven                       FALSE            FALSE          FALSE
Fixed Income Arbitrage             FALSE            FALSE          FALSE
Global Macro                       FALSE            FALSE          FALSE
Long/Short Equity                  FALSE            FALSE          FALSE
Merger Arbitrage                   FALSE            FALSE          FALSE
Relative Value                     FALSE            FALSE          FALSE
Short Selling                      FALSE            FALSE          FALSE
Funds of Funds                     FALSE            FALSE          FALSE
                       Short Selling Funds of Funds
Convertible Arbitrage          FALSE          FALSE
CTA Global                     FALSE          FALSE
Distressed Securities          FALSE          FALSE
Emerging Markets               FALSE          FALSE
Equity Market Neutral          FALSE          FALSE
Event Driven                   FALSE          FALSE
Fixed Income Arbitrage         FALSE          FALSE
Global Macro                   FALSE          FALSE
Long/Short Equity              FALSE          FALSE
Merger Arbitrage               FALSE          FALSE
Relative Value                 FALSE          FALSE
Short Selling                  FALSE          FALSE
Funds of Funds                 FALSE          FALSE

3.2.2 Optimization with custom moment function

Now we would like to run the optimization using our custom moment function. Recall that the portfolio moments are set in optimize.portfolio() when the moment function is evaluated. We use the custom moment function by passing in the name to the momentFUN argument in optimize.portfolio(). Note how we can use PortfolioAnalytics to easily run optimizations using different methods for estimating moments, which will allow us to evaluate different techniques for moment estimates and refine those estimates by analyzing the optimization results.

# Create the portfolio specification
port_spec <- portfolio.spec(colnames(asset_returns))

# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio =port_spec, type = "full_investment")

# Add a long only constraint such that the weight of an asset is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only")

# Add an objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")
rp <- random_portfolios(portfolio=port_spec,
                        permutations = 50,
                        rp_method='sample')
# Run the optimization with custom moment estimates
opt_custom <- optimize.portfolio(R = asset_returns, portfolio = port_spec, optimize_method = "random", rp = rp, momentFUN = "moments_robust")
Leverage constraint min_sum and max_sum are restrictive, 
              consider relaxing. e.g. 'full_investment' constraint should be min_sum=0.99 and max_sum=1.01
# Print the results of the optimization with custom moment estimates
print(opt_custom)
***********************************
PortfolioAnalytics Optimization
***********************************

Call:
optimize.portfolio(R = asset_returns, portfolio = port_spec, 
    optimize_method = "random", rp = rp, momentFUN = "moments_robust")

Optimal Weights:
 Convertible Arbitrage             CTA Global  Distressed Securities 
                 0.178                  0.050                  0.014 
      Emerging Markets  Equity Market Neutral           Event Driven 
                 0.000                  0.042                  0.000 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
                 0.420                  0.046                  0.170 
      Merger Arbitrage         Relative Value          Short Selling 
                 0.058                  0.004                  0.014 
        Funds of Funds 
                 0.004 

Objective Measures:
  StdDev 
0.006572 
# Run the optimization with sample moment estimates
opt_sample <- optimize.portfolio(R = asset_returns, portfolio = port_spec, optimize_method = "random", rp = rp)
Leverage constraint min_sum and max_sum are restrictive, 
              consider relaxing. e.g. 'full_investment' constraint should be min_sum=0.99 and max_sum=1.01
# Print the results of the optimization with sample moment estimates
print(opt_sample)
***********************************
PortfolioAnalytics Optimization
***********************************

Call:
optimize.portfolio(R = asset_returns, portfolio = port_spec, 
    optimize_method = "random", rp = rp)

Optimal Weights:
 Convertible Arbitrage             CTA Global  Distressed Securities 
                0.0769                 0.0769                 0.0769 
      Emerging Markets  Equity Market Neutral           Event Driven 
                0.0769                 0.0769                 0.0769 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
                0.0769                 0.0769                 0.0769 
      Merger Arbitrage         Relative Value          Short Selling 
                0.0769                 0.0769                 0.0769 
        Funds of Funds 
                0.0769 

Objective Measures:
  StdDev 
0.009955 

Looks like our custom moment function had a lower standard deviation than the sample method.

3.3 Objective functions

3.3.1 Custom objective function

A key feature of PortfolioAnalytics is that the name for an objective is a valid R function. The package was designed to be flexible and modular, and custom objective functions are a great example of this. A few guidelines should be followed for defining a custom moment function:

The objective function must return a single value for the optimizer to minimize or maximize. It is strongly encouraged to use R for the asset returns and weights for the portfolio weights. These argument names are detected automatically and handled in an efficient manner. Any other arguments for the objective function can be passed in as a named list to arguments in the add.objective() function.

# Custom annualized portfolio standard deviation
pasd <- function(R, weights, sigma, scale = 12){
  sqrt(as.numeric(t(weights) %*% sigma %*% weights)) * sqrt(scale)
}

How would you use this to optimize your portfolio?

3.3.2 Optimization with custom objective function

This exercise builds on the previous exercise and we will run the optimization with the custom objective function that computes portfolio annualized standard deviation. Because an objective function can be any valid R function, we add a risk objective for the pasd() function. The set.portfolio.moments() function will not recognize the pasd() objective name, so we need to create a custom moment function to calculate the second moment, sigma. We will solve the problem using random portfolios as the optimization method.

set_sigma <- function(R){
  out <- list()
  out$sigma <- cov(R)
  out
}
# Create the portfolio specification
port_spec <- portfolio.spec(colnames(asset_returns))

# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio =port_spec, type = "full_investment")

# Add a long only constraint such that the weight of an asset is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only", )
rp <- random_portfolios(portfolio=port_spec,
                        permutations = 40,
                        rp_method='simplex')
# Add custom objective to portfolio specification
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "pasd")

# Print the portfolio specificaton object
print(port_spec)
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = colnames(asset_returns))

Number of assets: 13 
Asset Names
 [1] "Convertible Arbitrage"  "CTA Global"            
 [3] "Distressed Securities"  "Emerging Markets"      
 [5] "Equity Market Neutral"  "Event Driven"          
 [7] "Fixed Income Arbitrage" "Global Macro"          
 [9] "Long/Short Equity"      "Merger Arbitrage"      
More than 10 assets, only printing the first 10

Constraints
Enabled constraint types
        - full_investment 
        - long_only 

Objectives:
Enabled objective names
        - pasd 
# Run the optimization
opt <- optimize.portfolio(R = asset_returns, portfolio = port_spec, momentFUN = set_sigma, optimize_method = "random", rp = rp)
Leverage constraint min_sum and max_sum are restrictive, 
              consider relaxing. e.g. 'full_investment' constraint should be min_sum=0.99 and max_sum=1.01
# Print the results of the optimization
print(opt)
***********************************
PortfolioAnalytics Optimization
***********************************

Call:
optimize.portfolio(R = asset_returns, portfolio = port_spec, 
    optimize_method = "random", rp = rp, momentFUN = set_sigma)

Optimal Weights:
 Convertible Arbitrage             CTA Global  Distressed Securities 
                0.2647                 0.0142                 0.1670 
      Emerging Markets  Equity Market Neutral           Event Driven 
                0.0986                 0.1576                 0.0036 
Fixed Income Arbitrage           Global Macro      Long/Short Equity 
                0.0389                 0.0161                 0.0100 
      Merger Arbitrage         Relative Value          Short Selling 
                0.0339                 0.0978                 0.0806 
        Funds of Funds 
                0.0169 

Objective Measures:
   pasd 
0.03656 

4 Application

4.1 Intro

4.1.1 Compute benchmark returns

In this exercise, we will create a benchmark to evaluate the performance of the optimization models in later exercises. An equal weight benchmark is a simple weighting scheme to construct the benchmark portfolio. The intuition of an equal weight approach is that there is no preference for any given asset. We are setting this up to answer the question, “Can optimization outperform a simple weighting scheme to construct a portfolio?”

# Load the package
library(PortfolioAnalytics)

# Load the data
data(edhec)

# Assign the data to a variable
asset_returns <- edhec

# Create a vector of equal weights
equal_weights <- rep(1 / ncol(edhec), ncol(edhec))

# Compute the benchmark returns
r_benchmark <- Return.portfolio(R = asset_returns, weights = equal_weights, rebalance_on = "quarters")
colnames(r_benchmark) <- "benchmark"

# Plot the benchmark returns
plot(r_benchmark)

4.1.2 Define the portfolio optimization problem

We define the portfolio optimization problem to minimize portfolio standard deviation subject to full investment and long only constraints. In this problem, we will set up the portfolio specification based on the defined problem. The following exercises in this chapter will build on the initial portfolio specification set up here.

# Create the portfolio specification
port_spec <- portfolio.spec(assets = colnames(asset_returns))

# Add a full investment constraint such that the weights sum to 1
port_spec <- add.constraint(portfolio = port_spec, type = "full_investment")

port_spec <- add.constraint(portfolio = port_spec, type = "weight_sum_constraint", min_sum=0.98, max_sum=1.02)
# Add a long only constraint such that the weight of an asset is between 0 and 1
port_spec <- add.constraint(portfolio = port_spec, type = "long_only")

# Add an objective to minimize portfolio standard deviation
port_spec <- add.objective(portfolio = port_spec, type = "risk", name = "StdDev")

# Print the portfolio specification
print(port_spec)
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

Call:
portfolio.spec(assets = colnames(asset_returns))

Number of assets: 13 
Asset Names
 [1] "Convertible Arbitrage"  "CTA Global"            
 [3] "Distressed Securities"  "Emerging Markets"      
 [5] "Equity Market Neutral"  "Event Driven"          
 [7] "Fixed Income Arbitrage" "Global Macro"          
 [9] "Long/Short Equity"      "Merger Arbitrage"      
More than 10 assets, only printing the first 10

Constraints
Enabled constraint types
        - full_investment 
        - long_only 

Objectives:
Enabled objective names
        - StdDev 

4.1.3 Benchmark

A reasonable benchmark is important for accurately measuring the relative performance of a portfolio The benchmark should reflect the universe of assets and/or style of the portfolio. Benchmarks should never be purposefully designed to have poor performance, as this would skew your results.

4.2 Optimization backtest

4.2.1 Backtest with periodic rebalancing

Now we will run the backtest using the portfolio specification created in the last exercise with quarterly rebalancing to evaluate out-of-sample performance. The other backtest parameters we need to set are the training period and rolling window. The training period sets the number of data points to use for the initial optimization. The rolling window sets the number of periods to use in the window. This problem can be solved with a quadratic programming solver so we will use “ROI” for the optimization method.

# Run the optimization
opt_rebal_base <- optimize.portfolio.rebalancing(R = asset_returns, 
                                                 portfolio = port_spec, 
                                                 optimize_method = "ROI", 
                                                 rebalance_on = "quarters", 
                                                 training_period = 60,
                                                 rolling_window = 60)

# Print the results
print(opt_rebal_base)
**************************************************
PortfolioAnalytics Optimization with Rebalancing
**************************************************

Call:
optimize.portfolio.rebalancing(R = asset_returns, portfolio = port_spec, 
    optimize_method = "ROI", rebalance_on = "quarters", training_period = 60, 
    rolling_window = 60)

Number of rebalancing dates:  73 
First rebalance date:
[1] "2001-12-31"
Last rebalance date:
[1] "2019-11-30"

Annualized Portfolio Rebalancing Return:
[1] 0.03124747

Annualized Portfolio Standard Deviation:
[1] 0.01725335
# Chart the weights
chart.Weights(opt_rebal_base)


# Compute the portfolio returns
returns_base <- Return.portfolio(R = asset_returns, weights = extractWeights(opt_rebal_base))
colnames(returns_base) <- "base"

4.2.2 Refine constraints and objectives

Here we hypothesize that refining constraints and/or objectives will improve performance. Let us add a risk budget objective to set a minimum and maximum percentage contribution to risk for each asset. We will be building on the portfolio specification we created. This is a more complex optimization problem and will require a global solver so we will use random portfolios as the optimization method.

rp <- readRDS("data/rp_fi_lo_ret.rds")
# Add a risk budge objective
port_spec <- add.objective(portfolio = port_spec, 
                           type = "risk_budget", 
                           name = "StdDev", 
                           min_prisk = 0.05, 
                           max_prisk = 0.1)

# Run the optimization
opt_rebal_rb <- optimize.portfolio.rebalancing(R = asset_returns, 
                                               portfolio = port_spec, 
                                               optimize_method = "random", rp = rp,
                                               trace = TRUE,
                                               rebalance_on = "quarters", 
                                               training_period = 60,
                                               rolling_window = 60)

# Chart the weights
chart.Weights(opt_rebal_rb)


# Chart the percentage contribution to risk
chart.RiskBudget(opt_rebal_rb, match.col = "StdDev", risk.type = "percentage")


# Compute the portfolio returns
returns_rb <- Return.portfolio(R = asset_returns, weights = extractWeights(opt_rebal_rb))
colnames(returns_rb) <- "risk_budget"

4.2.3 Do improved estimates lead to improved performance?

Let us hypothesize that using a robust estimate of the variance-covariance matrix will outperform the sample variance covariance matrix. In theory, better estimates should lead to better results. We will use the moments_robust() function that was defined in chapter 3 and the portfolio specification from the last exercise.

# Run the optimization
opt_rebal_rb_robust <- optimize.portfolio.rebalancing(R = asset_returns, 
                                                      momentFUN = "moments_robust",
                                                      portfolio = port_spec, 
                                                      optimize_method = "random", rp = rp,
                                                      trace = TRUE,
                                                      rebalance_on = "quarters", 
                                                      training_period = 60,
                                                      rolling_window = 60)

# Chart the weights
chart.Weights(opt_rebal_rb_robust)


# Chart the percentage contribution to risk
chart.RiskBudget(opt_rebal_rb_robust, match.col = "StdDev", risk.type = "percentage")


# Compute the portfolio returns
returns_rb_robust <- Return.portfolio(R = asset_returns, weights = extractWeights(opt_rebal_rb_robust))
colnames(returns_rb_robust) <- "rb_robust"

4.2.4 Analyze results and compare to benchmark

n the previous exercises of the chapter, we created an equal weight benchmark r_benchmark and ran the following optimizations:

  • Minimize portfolio standard deviation with sample estimates (returns stored in returns_base).
  • Minimize portfolio standard deviation with percentage contribution to risk using sample estimates (returns stored in returns_rb).
  • Minimize portfolio standard deviation with percentage contribution to risk using robust estimates (returns stored in returns_rb_robust). Now we wish to analyze the performance of the optimization backtests and compare with the benchmark.
# Combine the returns
ret <- cbind(r_benchmark, returns_base, returns_rb, returns_rb_robust)

# Compute annualized returns
table.AnnualizedReturns(R = ret)

# Chart the performance summary
charts.PerformanceSummary(R = ret)

LS0tDQp0aXRsZTogIkludGVybWVkaWF0ZSB0byBQb3J0Zm9saW8gQW5hbHlzaXMgaW4gUiINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICB0b2NfY29sbGFwc2VkOiBmYWxzZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIA0KdG9jX2RlcHRoOiAzDQotLS0NCiMgSW50cm9kdWN0aW9uIGFuZCBQb3J0Zm9saW8gVGhlb3J5DQoNCiMjIFdlbGNvbWUNCg0KIyMjIExvYWQgdGhlIFBvcnRmb2xpb0FuYWx5dGljcyBwYWNrYWdlDQoNClRoZSBwYWNrYWdlIFBvcnRmb2xpb0FuYWx5dGljcyB3aWxsIGJlIHVzZWQgdGhyb3VnaG91dCB0aGlzIGNvdXJzZSBmb3IgcG9ydGZvbGlvIG9wdGltaXphdGlvbiBhbmQgYW5hbHlzaXMuIFdlIHdpbGwgdXNlIHRoZSBpbmRleGVzIGRhdGFzZXQgdGhhdCBpcyBpbmNsdWRlZCB3aXRoIHRoZSBQb3J0Zm9saW9BbmFseXRpY3MgcGFja2FnZSBmb3IgdGhlIHJlbWFpbmluZyBleGVyY2lzZXMgaW4gdGhpcyBjaGFwdGVyLiBJbiB0aGlzIGV4ZXJjaXNlLCB3ZSB3aWxsIGxvYWQgdGhlIHBhY2thZ2UgYW5kIHByZXBhcmUgdGhlIGRhdGEgZm9yIHRoZSBwb3J0Zm9saW8gb3B0aW1pemF0aW9uIHByb2JsZW0gaW4gdGhlIG5leHQgZXhlcmNpc2UuDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KIyBMb2FkIHRoZSBwYWNrYWdlDQpsaWJyYXJ5KFBvcnRmb2xpb0FuYWx5dGljcykNCg0KIyBMb2FkIHRoZSBkYXRhDQpkYXRhKGluZGV4ZXMpDQoNCiMgU3Vic2V0IHRoZSBkYXRhDQppbmRleF9yZXR1cm5zIDwtIGluZGV4ZXNbLCBjKDE6NCldDQoNCiMgUHJpbnQgdGhlIGhlYWQgb2YgdGhlIGRhdGENCmhlYWQoaW5kZXhfcmV0dXJucykNCmBgYA0KIyMjIFNvbHZlIGEgc2ltcGxlIHBvcnRmb2xpbyBvcHRpbWl6YXRpb24gcHJvYmxlbQ0KDQpUaGlzIGZpcnN0IGV4ZXJjaXNlIHdpbGwgdGVhY2ggeW91IGhvdyB0byBzb2x2ZSBhIHNpbXBsZSBwb3J0Zm9saW8gb3B0aW1pemF0aW9uIHByb2JsZW0gdXNpbmcgUG9ydGZvbGlvQW5hbHl0aWNzLiBZb3Ugd2lsbCBsZWFybiBob3cgdG8gY3JlYXRlIGEgcG9ydGZvbGlvIHNwZWNpZmljYXRpb24gb2JqZWN0LCBhZGQgY29uc3RyYWludHMgYW5kIG9iamVjdGl2ZXMsIGFuZCBzb2x2ZSB0aGUgb3B0aW1pemF0aW9uIHByb2JsZW0uIFRoZSBwb3J0Zm9saW8gcHJvYmxlbSBpcyB0byBmb3JtIGEgbWluaW11bSB2YXJpYW5jZSBwb3J0Zm9saW8gc3ViamVjdCB0byBmdWxsIGludmVzdG1lbnQgYW5kIGxvbmcgb25seSBjb25zdHJhaW50cy4gVGhlIG9iamVjdGl2ZSBpcyB0byBtaW5pbWl6ZSBwb3J0Zm9saW8gdmFyaWFuY2UuIFRoZXJlIGFyZSB0d28gY29uc3RyYWludHMgaW4gdGhpcyBwcm9ibGVtOiB0aGUgZnVsbCBpbnZlc3RtZW50IGNvbnN0cmFpbnQgbWVhbnMgdGhhdCB0aGUgd2VpZ2h0cyBtdXN0IHN1bSB0byAxLCBhbmQgdGhlIGxvbmcgb25seSBjb25zdHJhaW50IG1lYW5zIHRoYXQgYWxsIHdlaWdodHMgbXVzdCBiZSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gMCAoaS5lLiBubyBzaG9ydCBwb3NpdGlvbnMgYXJlIGFsbG93ZWQpLg0KDQoqWW91IG5lZWQgdG8gaW5zdGFsbCBiZWxvdyBsaWJyYXJpZXMgZm9yIFJPSSB0byB3b3JrOioNCg0KLSBST0kNCi0gUk9JLnBsdWdpbi5xdWFkcHJvZw0KLSBST0kucGx1Z2luLmdscGsNCg0KYGBge3Igd2FybmluZz1GQUxTRX0NCiMgQ3JlYXRlIHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbg0KcG9ydF9zcGVjIDwtIHBvcnRmb2xpby5zcGVjKGNvbG5hbWVzKGluZGV4X3JldHVybnMpKQ0KDQojIEFkZCBhIGZ1bGwgaW52ZXN0bWVudCBjb25zdHJhaW50IHN1Y2ggdGhhdCB0aGUgd2VpZ2h0cyBzdW0gdG8gMQ0KcG9ydF9zcGVjIDwtIGFkZC5jb25zdHJhaW50KHBvcnRmb2xpbyA9cG9ydF9zcGVjLCB0eXBlID0gImZ1bGxfaW52ZXN0bWVudCIpDQoNCiMgQWRkIGEgbG9uZyBvbmx5IGNvbnN0cmFpbnQgc3VjaCB0aGF0IHRoZSB3ZWlnaHQgb2YgYW4gYXNzZXQgaXMgYmV0d2VlbiAwIGFuZCAxDQpwb3J0X3NwZWMgPC0gYWRkLmNvbnN0cmFpbnQocG9ydGZvbGlvID0gcG9ydF9zcGVjLCB0eXBlID0gImxvbmdfb25seSIpDQoNCiMgQWRkIGFuIG9iamVjdGl2ZSB0byBtaW5pbWl6ZSBwb3J0Zm9saW8gc3RhbmRhcmQgZGV2aWF0aW9uDQpwb3J0X3NwZWMgPC0gYWRkLm9iamVjdGl2ZShwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAicmlzayIsIG5hbWUgPSAiU3RkRGV2IikNCg0KIyBTb2x2ZSB0aGUgb3B0aW1pemF0aW9uIHByb2JsZW0NCm9wdCA8LSBvcHRpbWl6ZS5wb3J0Zm9saW8oaW5kZXhfcmV0dXJucywgcG9ydGZvbGlvID0gcG9ydF9zcGVjLCBvcHRpbWl6ZV9tZXRob2QgPSAiUk9JIikNCmBgYA0KQXMgeW91IHdpbGwgc2VlLCB0aGUgbW9kdWxhciBuYXR1cmUgb2YgdGhlIGNvbnRyYWludHMgYW5kIG9iamVjdGl2ZXMgbWFrZXMgdGhpcyB2ZXJ5IGZsZXhpYmxlIQ0KDQojIyMgVmlzdWFsaXplIHJlc3VsdHMNCg0KTm93IHRoYXQgd2UgaGF2ZSBydW4gdGhlIG9wdGltaXphdGlvbiwgd2Ugd291bGQgbGlrZSB0byB0YWtlIGEgbG9vayBhdCB0aGUgb3V0cHV0IGFuZCByZXN1bHRzLiBSZWNhbGwgdGhhdCB0aGUgb3B0aW1pemF0aW9uIG91dHB1dCBpcyBpbiBhIHZhcmlhYmxlIG5hbWVkIG9wdC4gSW4gb3VyIGNhc2UsIGZvciB0aGUgcG9ydGZvbGlvIG9wdGltaXphdGlvbiBpbiB0aGUgcHJldmlvdXMgZXhlcmNpc2UsIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBvcHRpbWFsIHdlaWdodHMgYW5kIGVzdGltYXRlZCBvYmplY3RpdmUgdmFsdWUuIFRoZSB3ZWlnaHRzIGFyZSBjb25zaWRlcmVkIG9wdGltYWwgaW4gdGhlIHNlbnNlIHRoYXQgdGhlIHNldCBvZiB3ZWlnaHRzIG1pbmltaXplcyB0aGUgb2JqZWN0aXZlIHZhbHVlLCBwb3J0Zm9saW8gc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgc2F0aXNmaWVzIHRoZSBmdWxsIGludmVzdG1lbnQgYW5kIGxvbmcgb25seSBjb25zdHJhaW50cyBiYXNlZCBvbiBoaXN0b3JpY2FsIGRhdGEuDQpgYGB7cn0NCiMgUHJpbnQgdGhlIHJlc3VsdHMgb2YgdGhlIG9wdGltaXphdGlvbg0KcHJpbnQob3B0KQ0KDQojIEV4dHJhY3QgdGhlIG9wdGltYWwgd2VpZ2h0cw0KZXh0cmFjdFdlaWdodHMob3B0KQ0KDQojIENoYXJ0IHRoZSBvcHRpbWFsIHdlaWdodHMNCmNoYXJ0LldlaWdodHMob3B0KQ0KYGBgDQpWaXN1YWxpemF0aW9uIGlzIGltcG9ydGFudCBmb3IgYW55IGFuYWx5c2lzLCBlc3BlY2lhbGx5IHBvcnRmb2xpbyBvcHRpbWl6YXRpb24uDQpNb2Rlcm4gUG9ydGZvbGlvIFRoZW9yeSAoTVBUKSBpcyBhbGwgYWJvdXQgbWF4aW1pemluZyBnYWluIHdoaWxlIHNpbXVsdGFuZW91c2x5IGNvbnRyb2xsaW5nIGZvciByaXNrLiBBY2FkZW1pY2FsbHksIHJpc2sgYW5kIGdhaW4gYXJlIGRlZmluZWQgYXMgc3RhbmRhcmQgZGV2aWF0aW9uIGFuZCBtZWFuIHJldHVybi4gSW4gcHJhY3RpY2UsIG1hbnkgb3RoZXIgbWVhc3VyZXMgb2YgcmlzayBhbmQgZ2FpbiBhcmUgYWxzbyB1c2VkIQ0KDQojIyBDaGFsbGVuZ2VzIG9mIHBvcnRmb2xpbyBvcHRpbWl6YXRpb24NCg0KIyMjIFF1YWRyYXRpYyB1dGlsaXR5DQoNClRoZSBmb3JtdWxhdGlvbiBvZiBxdWFkcmF0aWMgdXRpbGl0eSBpczoNCg0KTWF4aW1pemUgJHdeVOKIl8684oiSzrviiJd3XlTiiJfOo+KIl3ckDQoNClRoaXMgZm9ybXVsYSBtYXhpbWl6ZXMgdGhlIGV4cGVjdGVkIHJldHVybiB3aGlsZSBwZW5hbGl6aW5nIGZvciB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIHNjYWxlZCBieSB0aGUgcmlzayBhdmVyc2lvbiBwYXJhbWV0ZXIsIGxhbWJkYSENCg0KIyMjIE1heGltaXplIHF1YWRyYXRpYyB1dGlsaXR5IGZ1bmN0aW9uDQoNCkluIHRoZSB2aWRlbyBvbiBjaGFsbGVuZ2VzIG9mIHBvcnRmb2xpbyBvcHRpbWl6YXRpb24sIHlvdSBzYXcgaG93IHRvIHNvbHZlIGEgcXVhZHJhdGljIHV0aWxpdHkgb3B0aW1pemF0aW9uIHByb2JsZW0gd2l0aCB0aGUgcGFja2FnZSBxdWFkcHJvZy4gVGhpcyBleGVyY2lzZSB3aWxsIHNob3cgeW91IGhvdyB0byBzb2x2ZSBhIHF1YWRyYXRpYyB1dGlsaXR5IHByb2JsZW0gdXNpbmcgdGhlIFBvcnRmb2xpb0FuYWx5dGljcyBwYWNrYWdlLiBSZWNhbGwgdGhlIHF1YWRyYXRpYyB1dGlsaXR5IGZvcm11bGF0aW9uIGhhcyB0d28gdGVybXMsIG9uZSBmb3IgcG9ydGZvbGlvIG1lYW4gcmV0dXJuIGFuZCBhbm90aGVyIGZvciBwb3J0Zm9saW8gdmFyaWFuY2Ugd2l0aCBhIHJpc2sgYXZlcnNpb24gcGFyYW1ldGVyLCBsYW1iZGEuDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KIyBDcmVhdGUgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uDQpwb3J0X3NwZWMgPC0gcG9ydGZvbGlvLnNwZWMoYXNzZXRzID0gY29sbmFtZXMoaW5kZXhfcmV0dXJucykpDQoNCiMgQWRkIGEgZnVsbCBpbnZlc3RtZW50IGNvbnN0cmFpbnQgc3VjaCB0aGF0IHRoZSB3ZWlnaHRzIHN1bSB0byAxDQpwb3J0X3NwZWMgPC0gYWRkLmNvbnN0cmFpbnQocG9ydGZvbGlvID0gcG9ydF9zcGVjLCB0eXBlID0gImZ1bGxfaW52ZXN0bWVudCIpDQoNCiMgQWRkIGEgbG9uZyBvbmx5IGNvbnN0cmFpbnQgc3VjaCB0aGF0IHRoZSB3ZWlnaHQgb2YgYW4gYXNzZXQgaXMgYmV0d2VlbiAwIGFuZCAxDQpwb3J0X3NwZWMgPC0gYWRkLmNvbnN0cmFpbnQocG9ydGZvbGlvID0gcG9ydF9zcGVjLCB0eXBlID0gImxvbmdfb25seSIpDQoNCiMgQWRkIGFuIG9iamVjdGl2ZSB0byBtYXhpbWl6ZSBwb3J0Zm9saW8gbWVhbiByZXR1cm4NCnBvcnRfc3BlYyA8LSBhZGQub2JqZWN0aXZlKHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgdHlwZSA9ICJyZXR1cm4iLCBuYW1lID0gIm1lYW4iKQ0KDQojIEFkZCBhbiBvYmplY3RpdmUgdG8gbWluaW1pemUgcG9ydGZvbGlvIHZhcmlhbmNlDQpwb3J0X3NwZWMgPC0gYWRkLm9iamVjdGl2ZShwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAicmlzayIsIG5hbWUgPSAidmFyIiwgcmlza19hdmVyc2lvbiA9IDEwKQ0KDQojIFNvbHZlIHRoZSBvcHRpbWl6YXRpb24gcHJvYmxlbQ0Kb3B0IDwtIG9wdGltaXplLnBvcnRmb2xpbyhSID0gaW5kZXhfcmV0dXJucywgcG9ydGZvbGlvID0gcG9ydF9zcGVjLCBvcHRpbWl6ZV9tZXRob2QgPSAiUk9JIikNCmBgYA0KIyMgSW50cm9kdWN0aW9uIHRvIFBvcnRmb2xpb0FuYWx5dGljcw0KDQojIyMgS2V5IGRlc2lnbiBnb2Fscw0KDQpNb2R1bGFyIGNvbnN0cmFpbnRzIGFuZCBvYmplY3RpdmVzIHRvIGFkZCwgcmVtb3ZlLCBhbmQgY29tYmluZSBtdWx0aXBsZSBjb25zdHJhaW50IGFuZCBvYmplY3RpdmUgdHlwZXMgdmVyeSBlYXNpbHkNCg0KU3VwcG9ydCBmb3IgdXNlciBkZWZpbmVkIG1vbWVudCBmdW5jdGlvbnMNCg0KVmlzdWFsaXphdGlvbnMgdG8gYnVpbGQgaW50dWl0aW9uIGFib3V0IHRoZSBwcm9ibGVtIGFuZCB1bmRlcnN0YW5kIHRoZSBmZWFzaWJsZSBzcGFjZSBvZiBwb3J0Zm9saW9zDQoNClBvcnRmb2xpb0FuYWx5dGljcyBzd2l0Y2hlcyBzZWFtbGVzc2x5IHRocm91Z2ggYSBudW1iZXIgb2Ygc29sdmVycy4NCg0KIyBQb3J0Zm9saW8gT3B0aW1pemF0aW9uIFdvcmtmbG93DQoNCiMjIFBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uLCBjb25zdHJhaW50cywgYW5kIG9iamVjdGl2ZXMNCg0KIyMjIENyZWF0ZSBhIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uDQoNClRoZSBmaXJzdCBzdGVwIGluIHRoZSB3b3JrZmxvdyBvZiBQb3J0Zm9saW9BbmFseXRpY3MgaXMgdG8gY3JlYXRlIHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBvYmplY3QuIFRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBob2xkcyBwb3J0Zm9saW8gbGV2ZWwgZGF0YSwgY29uc3RyYWludHMgYW5kIG9iamVjdGl2ZXMuIFRoZSBvbmx5IHJlcXVpcmVkIGFyZ3VtZW50IHRvIHBvcnRmb2xpby5zcGVjKCkgaXMgYXNzZXRzLiBhc3NldHMgY2FuIGJlIHRoZSBudW1iZXIgb2YgYXNzZXRzLCBhIG5hbWVkIHZlY3RvciBvZiBzZWVkIHdlaWdodHMsIG9yIGEgY2hhcmFjdGVyIHZlY3RvciBvZiB0aGUgYXNzZXQgbmFtZXMuIFRoZSBjYXRlZ29yeV9sYWJlbHMgYXJndW1lbnQgaXMgdXNlZCBmb3IgbGFiZWxpbmcgYXNzZXRzIGJ5IGEgY2F0ZWdvcnkgc3VjaCBhcyBzZWN0b3IsIGluZHVzdHJ5LCByZWdpb24sIGFzc2V0IGNsYXNzLCBvciBjdXJyZW5jeS4gVGhlIHdlaWdodF9zZXEgYXJndW1lbnQgYWxsb3dzIHlvdSB0byBzcGVjaWZ5IGEgc2VlZCBzZXF1ZW5jZSBvZiB3ZWlnaHRzIHRoYXQgYXJlIHVzZWQgYnkgdGhlIHJhbmRvbSBwb3J0Zm9saW9zIGFsZ29yaXRobXMuIENvbW1vbiBwcmFjdGljZSBpcyB0byBwYXNzIGluIHRoZSBjb2x1bW4gbmFtZXMgb2YgdGhlIHJldHVybnMgb2JqZWN0IGZvciB0aGUgYXNzZXRzIGFyZ3VtZW50Lg0KYGBge3J9DQpkYXRhKGVkaGVjKQ0KYXNzZXRfcmV0dXJucyA8LSBlZGhlYw0KYGBgDQoNCmBgYHtyfQ0KIyBHZXQgdGhlIGNvbHVtbiBuYW1lcyBvZiB0aGUgcmV0dXJucyBkYXRhDQphc3NldF9uYW1lcyA8LSBjb2xuYW1lcyhhc3NldF9yZXR1cm5zKQ0KDQojIENyZWF0ZSBhIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uIG9iamVjdCB1c2luZyBhc3NldF9uYW1lcw0KcG9ydF9zcGVjIDwtIHBvcnRmb2xpby5zcGVjKGFzc2V0cyA9IGFzc2V0X25hbWVzKQ0KDQojIEdldCB0aGUgY2xhc3Mgb2YgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uIG9iamVjdA0KY2xhc3MocG9ydF9zcGVjKQ0KDQojIFByaW50IHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBvYmplY3QNCnByaW50KHBvcnRfc3BlYykNCmBgYA0KTm93IHRoYXQgeW91IGhhdmUgdGhlIGJhc2Ugc3BlY2lmaWNhdGlvbiwgbGV0J3MgYWRkIHNvbWUgY29uc3RyYWludHMuDQoNCiMjIyBBZGQgY29uc3RyYWludHMNCg0KQ29uc3RyYWludHMgYXJlIGFkZGVkIHRvIHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBvYmplY3Qgd2l0aCB0aGUgYWRkLmNvbnN0cmFpbnQoKSBmdW5jdGlvbi4gRWFjaCBjb25zdHJhaW50IGFkZGVkIGlzIGEgc2VwYXJhdGUgb2JqZWN0IGFuZCBzdG9yZWQgaW4gdGhlIGNvbnN0cmFpbnRzIHNsb3QgaW4gdGhlIHBvcnRmb2xpbyBvYmplY3QuIEluIHRoaXMgd2F5LCB0aGUgY29uc3RyYWludHMgYXJlIG1vZHVsYXIgYW5kIG9uZSBjYW4gZWFzaWx5IGFkZCwgcmVtb3ZlLCBvciBtb2RpZnkgdGhlIGNvbnN0cmFpbnRzIGluIHRoZSBwb3J0Zm9saW8gb2JqZWN0LiBUaGUgcmVxdWlyZWQgYXJndW1lbnRzIGZvciBhZGQuY29uc3RyYWludCgpIGFyZSB0aGUgcG9ydGZvbGlvIHRoZSBjb25zdHJhaW50IGlzIGFkZGVkIHRvLCB0aGUgY29uc3RyYWludCB0eXBlLCBhbmQgbmFtZWQgYXJndW1lbnRzIHBhc3NlZCB2aWEgLi4uIHRvIHRoZSBjb25zdHJ1Y3RvciBvZiB0aGUgY29uc3RyYWludCB0eXBlLg0KQmFzaWMgY29uc3RyYWludCB0eXBlczoNCg0KKglTcGVjaWZ5IHRoZSBjb25zdHJhaW50IG9uIHRoZSBzdW0gb2YgdGhlIHdlaWdodHMNCiAgICArIHdlaWdodF9zdW0sIHdlaWdodCwgbGV2ZXJhZ2UNCiAgICArIGZ1bGxfaW52ZXN0bWVudCBpcyBhIHNwZWNpYWwgY2FzZSB0aGF0IHNldHMgbWluX3N1bSA9IG1heF9zdW0gPSAxDQogICAgKyBkb2xsYXJfbmV1dHJhbCBpcyBhIHNwZWNpYWwgY2FzZSB0aGF0IHNldHMgbWluX3N1bSA9IG1heF9zdW0gPSAwDQoqCVNwZWNpZnkgY29uc3RyYWludHMgZm9yIHRoZSBpbmRpdmlkdWFsIGFzc2V0IHdlaWdodHMNCiAgICArIGJveA0KICAgICsgbG9uZ19vbmx5IGlzIGEgc3BlY2lhbCBjYXNlIHRoYXQgc2V0cyBtaW4gPSAwIGFuZCBtYXggPSAxDQoqCVNwZWNpZnkgdGhlIGNvbnN0cmFpbnQgZm9yIHRoZSBzdW0gb2Ygd2VpZ2h0cyBvZiBhc3NldHMgYnkgZ3JvdXAgKHNlY3RvciwgcmVnaW9uLCBhc3NldCBjbGFzcywgZXRjLikNCiAgICArIGdyb3VwDQoqCVNwZWNpZnkgYSBjb25zdHJhaW50IG9uIHRoZSB0YXJnZXQgbWVhbiByZXR1cm4NCiAgICArIHJldHVybg0KDQpJbiB0aGlzIGV4ZXJjaXNlLCB5b3Ugd2lsbCBhZGQgYSBmZXcgb2YgdGhlIG1vcmUgY29tbW9uIGNvbnN0cmFpbnQgdHlwZXMuIEluIGFkZGl0aW9uIHRvIHRoZSBiYXNpYyBjb25zdHJhaW50IHR5cGVzIGxpc3RlZCBhYm92ZSwgUG9ydGZvbGlvQW5hbHl0aWNzIGFsc28gc3VwcG9ydHMgcG9zaXRpb24gbGltaXQsIHR1cm5vdmVyLCBkaXZlcnNpZmljYXRpb24sIGZhY3RvciBleHBvc3VyZSwgYW5kIGxldmVyYWdlIGV4cG9zdXJlIGNvbnN0cmFpbnQgdHlwZXMuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgb3RoZXIgY29uc3RyYWludCB0eXBlcywgbG9vayBhdCB0aGUgaGVscCBmaWxlcyBmb3IgdGhlIGNvbnN0cmFpbnQgY29uc3RydWN0b3JzLiBUaGUgaGVscCBmaWxlcyBpbmNsdWRlIGEgZGVzY3JpcHRpb24gb2YgdGhlIGNvbnN0cmFpbnQgdHlwZSBhcyB3ZWxsIGFzIGV4YW1wbGUgY29kZS4NCmBgYHtyfQ0KIyBBZGQgdGhlIHdlaWdodCBzdW0gY29uc3RyYWludA0KcG9ydF9zcGVjIDwtIGFkZC5jb25zdHJhaW50KHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgdHlwZSA9ICJ3ZWlnaHRfc3VtIiwgbWluX3N1bSA9IDEsIG1heF9zdW0gPSAxKQ0KDQojIEFkZCB0aGUgYm94IGNvbnN0cmFpbnQNCnBvcnRfc3BlYyA8LSBhZGQuY29uc3RyYWludChwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAiYm94IiwgbWluID0gYygwLjEsIDAuMSwgMC4xLCAwLjEsIDAuMSwgMC4wNSwgMC4wNSwgMC4wNSwgMC4wNSwgMC4wNSwgMC4wNSwgMC4wNSwgMC4wNSksIG1heCA9IDAuNCkNCg0KIyBBZGQgdGhlIGdyb3VwIGNvbnN0cmFpbnQNCnBvcnRfc3BlYyA8LSBhZGQuY29uc3RyYWludChwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAiZ3JvdXAiLCBncm91cHMgPSBsaXN0KGMoMSwgNSwgNywgOSwgMTAsIDExKSwgYygyLCAzLCA0LCA2LCA4LCAxMikpLCBncm91cF9taW4gPSAwLjQsIGdyb3VwX21heCA9IDAuNikNCg0KDQojIFByaW50IHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBvYmplY3QNCnByaW50KHBvcnRfc3BlYykNCmBgYA0KVGhlcmUgYXJlIGEgbnVtYmVyIG9mIGRpZmZlcmVudCBjb25zdHJhaW50IHR5cGVzLCBjaGVjayB0aGVtIG91dCB1c2luZyA/YWRkLmNvbnN0cmFpbnQNCg0KT2JqZWN0aXZlcyBhcmUgYWRkZWQgdG8gdGhlIHBvcnRmb2xpbyBvYmplY3Qgd2l0aCB0aGUgYWRkLm9iamVjdGl2ZSgpIGZ1bmN0aW9uLiBFYWNoIG9iamVjdGl2ZSBhZGRlZCBpcyBhIHNlcGFyYXRlIG9iamVjdCBhbmQgc3RvcmVkIGluIHRoZSBvYmplY3RpdmVzIHNsb3QgaW4gdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uIG9iamVjdC4gSW4gdGhpcyB3YXksIHRoZSBvYmplY3RpdmVzIGFyZSBtb2R1bGFyIGFuZCBvbmUgY2FuIGVhc2lseSBhZGQsIHJlbW92ZSwgb3IgbW9kaWZ5IHRoZSBvYmplY3RpdmUgb2JqZWN0cy4gVGhlIG5hbWUgYXJndW1lbnQgbXVzdCBiZSBhIHZhbGlkIFIgZnVuY3Rpb24uIFNldmVyYWwgZnVuY3Rpb25zIGFyZSBhdmFpbGFibGUgaW4gdGhlIFBlcmZvcm1hbmNlQW5hbHl0aWNzIHBhY2thZ2UsIGJ1dCB1c2VyIGRlZmluZWQgZnVuY3Rpb25zIGNhbiBhbHNvIGJlIHVzZWQgYXMgb2JqZWN0aXZlIGZ1bmN0aW9ucy4gVGhlIHJlcXVpcmVkIGFyZ3VtZW50cyBmb3IgYWRkLm9iamVjdGl2ZSgpIGFyZSB0aGUgcG9ydGZvbGlvIHRoZSBvYmplY3RpdmUgaXMgYWRkZWQgdG8sIHRoZSBvYmplY3RpdmUgdHlwZSwgdGhlIG9iamVjdGl2ZSBuYW1lLCBhbmQgbmFtZWQgYXJndW1lbnRzIHBhc3NlZCB2aWEgLi4uIHRvIHRoZSBjb25zdHJ1Y3RvciBvZiB0aGUgb2JqZWN0aXZlIHR5cGUuIEFyZ3VtZW50cyBmb3IgdGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBhcmUgc3BlY2lmaWVkIGFzIGEgbmFtZWQgbGlzdCB0byBhcmd1bWVudHMuDQoNCkJhc2ljIG9iamVjdGl2ZSB0eXBlczoNCg0KKglyZXR1cm46IFRoaXMgb2JqZWN0aXZlIHR5cGUgc2Vla3MgdG8gbWF4aW1pemUgdGhlIG9iamVjdGl2ZS4NCioJcmlzazogVGhpcyBvYmplY3RpdmUgdHlwZSBzZWVrcyB0byBtaW5pbWl6ZSB0aGUgb2JqZWN0aXZlLg0KKiByaXNrX2J1ZGdldDogVGhpcyBvYmplY3RpdmUgdHlwZSBzZWVrcyB0byBtaW5pbWl6ZSByaXNrIGNvbmNlbnRyYXRpb24gb3IgcGVuYWxpemUgY29udHJpYnV0aW9uIHRvIHJpc2sgdGhhdCBleGNlZWRzIHRoZSBtaW5pbXVtIG9yIG1heGltdW0gYWxsb3dhYmxlIHBlcmNlbnRhZ2UgY29udHJpYnV0aW9uIHRvIHJpc2suDQoNCkluIGFkZGl0aW9uIHRvIHRoZSBvYmplY3RpdmUgdHlwZXMgbGlzdGVkIGFib3ZlLCBQb3J0Zm9saW9BbmFseXRpY3MgYWxzbyBzdXBwb3J0cyBxdWFkcmF0aWMgdXRpbGl0eSBhbmQgd2VpZ2h0IGNvbmNlbnRyYXRpb24gb2JqZWN0aXZlIHR5cGVzLiANCmBgYHtyfQ0KIyBBZGQgYSByZXR1cm4gb2JqZWN0aXZlIHRvIG1heGltaXplIG1lYW4gcmV0dXJuDQpwb3J0X3NwZWMgPC0gYWRkLm9iamVjdGl2ZShwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAicmV0dXJuIiwgbmFtZSA9ICJtZWFuIikNCg0KIyBBZGQgYSByaXNrIG9iamVjdGl2ZSB0byBtaW5pbWl6ZSBwb3J0Zm9saW8gc3RhbmRhcmQgZGV2aWF0aW9uDQpwb3J0X3NwZWMgPC0gYWRkLm9iamVjdGl2ZShwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAicmlzayIsIG5hbWUgPSAiU3RkRGV2IikNCg0KIyBBZGQgYSByaXNrIGJ1ZGdldCBvYmplY3RpdmUNCnBvcnRfc3BlYyA8LSBhZGQub2JqZWN0aXZlKHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgdHlwZSA9ICJyaXNrX2J1ZGdldCIsIG5hbWUgPSAiU3RkRGV2IiwgbWluX3ByaXNrID0gMC4wNSwgbWF4X3ByaXNrID0gMC4xKQ0KDQojIFByaW50IHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBvYmplY3QNCnByaW50KHBvcnRfc3BlYykNCmBgYA0KIyMgUnVubmluZyBvcHRpbWl6YXRpb25zDQoNCiMjIyBTaW5nbGUtUGVyaW9kIG9wdGltaXphdGlvbg0KDQpUaGVyZSBhcmUgdHdvIGZ1bmN0aW9ucyBmb3IgcnVubmluZyB0aGUgb3B0aW1pemF0aW9uLCBvcHRpbWl6ZS5wb3J0Zm9saW8oKSBhbmQgb3B0aW1pemUucG9ydGZvbGlvLnJlYmFsYW5jaW5nKCkuIFRoaXMgZXhlcmNpc2Ugd2lsbCBmb2N1cyBvbiBzaW5nbGUgcGVyaW9kIG9wdGltaXphdGlvbiBhbmQgdGhlIG5leHQgZXhlcmNpc2Ugd2lsbCB1c2Ugb3B0aW1pemUucG9ydGZvbGlvLnJlYmFsYW5jaW5nKCkgZm9yIG9wdGltaXphdGlvbiB3aXRoIHBlcmlvZGljIHJlYmFsYW5jaW5nLiBvcHRpbWl6ZS5wb3J0Zm9saW8oKSBzdXBwb3J0cyBzaW5nbGUtcGVyaW9kIG9wdGltaXphdGlvbi4gS2V5IGFyZ3VtZW50cyBpbmNsdWRlIFIgZm9yIHRoZSBhc3NldCByZXR1cm5zLCBwb3J0Zm9saW8gZm9yIHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBvYmplY3QsIGFuZCBvcHRpbWl6ZV9tZXRob2QgdG8gc3BlY2lmeSB0aGUgb3B0aW1pemF0aW9uIG1ldGhvZCB1c2VkIHRvIHNvbHZlIHRoZSBwcm9ibGVtLiBJbiBtYW55IGNhc2VzLCBpdCBpcyB1c2VmdWwgdG8gc3BlY2lmeSB0cmFjZSA9IFRSVUUgdG8gc3RvcmUgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBmb3IgZWFjaCBpdGVyYXRpb24vdHJpYWwgb2YgdGhlIG9wdGltaXphdGlvbi4NCg0KVGhlIGZvbGxvd2luZyBvcHRpbWl6YXRpb24gbWV0aG9kcyBhcmUgc3VwcG9ydGVkOg0KDQotCURFb3B0aW06IERpZmZlcmVudGlhbCBldm9sdXRpb24NCi0JcmFuZG9tOiBSYW5kb20gcG9ydGZvbGlvcw0KLQlHZW5TQTogR2VuZXJhbGl6ZWQgU2ltdWxhdGVkIEFubmVhbGluZw0KLQlwc286IFBhcnRpY2xlIHN3YXJtIG9wdGltaXphdGlvbg0KLQlST0k6IFIgT3B0aW1pemF0aW9uIEluZnJhc3RydWN0dXJlIGZvciBsaW5lYXIgYW5kIHF1YWRyYXRpYyBwcm9ncmFtbWluZyBzb2x2ZXJzDQoNClRoZSBvcHRpbWl6YXRpb24gbWV0aG9kIHlvdSBjaG9vc2Ugc2hvdWxkIGJlIGJhc2VkIG9uIHRoZSB0eXBlIG9mIHByb2JsZW0geW91IGFyZSBzb2x2aW5nLiBGb3IgZXhhbXBsZSwgYSBwcm9ibGVtIHRoYXQgY2FuIGJlIGZvcm11bGF0ZWQgYXMgYSBxdWFkcmF0aWMgcHJvZ3JhbW1pbmcgcHJvYmxlbSBzaG91bGQgYmUgc29sdmVkIHVzaW5nIGEgcXVhZHJhdGljIHByb2dyYW1taW5nIHNvbHZlciwgd2hlcmVhcyBhIG5vbi1jb252ZXggcHJvYmxlbSBzaG91bGQgYmUgc29sdmVkIHVzaW5nIGEgZ2xvYmFsIHNvbHZlciBzdWNoIGFzIERFb3B0aW0uDQoNCkluIHRoaXMgZXhlcmNpc2UsIHdlIHdpbGwgZGVmaW5lIHRoZSBwb3J0Zm9saW8gb3B0aW1pemF0aW9uIHByb2JsZW0gdG8gbWF4aW1pemUgbWVhbiByZXR1cm4gYW5kIG1pbmltaXplIHBvcnRmb2xpbyBzdGFuZGFyZCBkZXZpYXRpb24gd2l0aCBhIHN0YW5kYXJkIGRldmlhdGlvbiByaXNrIGJ1ZGdldCB3aGVyZSB0aGUgbWluaW11bSBwZXJjZW50YWdlIHJpc2sgaXMgNSUgYW5kIHRoZSBtYXhpbXVtIHBlcmNlbnRhZ2UgcmlzayBpcyAxMCUsIHN1YmplY3QgdG8gZnVsbCBpbnZlc3RtZW50IGFuZCBsb25nIG9ubHkgY29uc3RyYWludHMuIFRoZSByaXNrIGJ1ZGdldCBvYmplY3RpdmUgcmVxdWlyZXMgYSBnbG9iYWwgc29sdmVyIHNvIHdlIHdpbGwgc29sdmUgdGhlIHByb2JsZW0gdXNpbmcgcmFuZG9tIHBvcnRmb2xpb3MuIFRoZSBzZXQgb2YgcmFuZG9tIHBvcnRmb2xpb3MsIHJwLCBpcyBnZW5lcmF0ZWQgdXNpbmcgNTAwIHBlcm11dGF0aW9ucyBmb3IgdGhpcyBleGVyY2lzZS4NCmBgYHtyfQ0KcnAgPC0gcmFuZG9tX3BvcnRmb2xpb3MocG9ydGZvbGlvPXBvcnRfc3BlYywgcGVybXV0YXRpb25zID0gNTAwLCBycF9tZXRob2QgPSdzaW1wbGV4JykNCiNycCA8LSByYW5kb21fcG9ydGZvbGlvcyhwb3J0Zm9saW89cG9ydF9zcGVjLCBwZXJtdXRhdGlvbnM9NTAwLCAjcnBfbWV0aG9kPSdzYW1wbGUnKQ0KI3JwIDwtIHJhbmRvbV9wb3J0Zm9saW9zKHBvcnRmb2xpbz1wb3J0X3NwZWMsIHBlcm11dGF0aW9ucz01MDAsICNycF9tZXRob2Q9J2dyaWQnKQ0KYGBgDQoNCg0KYGBge3J9DQojIFJ1biBhIHNpbmdsZSBwZXJpb2Qgb3B0aW1pemF0aW9uIHVzaW5nIHJhbmRvbSBwb3J0Zm9saW9zIGFzIHRoZSBvcHRpbWl6YXRpb24gbWV0aG9kDQpvcHQgPC0gb3B0aW1pemUucG9ydGZvbGlvKFIgPSBhc3NldF9yZXR1cm5zLCBwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIG9wdGltaXplX21ldGhvZCA9ICJyYW5kb20iLCBycCA9IHJwLCB0cmFjZSA9IFRSVUUpDQoNCiMgUHJpbnQgdGhlIG91dHB1dCBvZiB0aGUgc2luZ2xlLXBlcmlvZCBvcHRpbWl6YXRpb24NCnByaW50KG9wdCkNCmBgYA0KWW91IGp1c3Qgb3B0aW1pemVkIGEgcG9ydGZvbGlvIHRoYXQgbWluaW1pemVkIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gYW5kIG1heGltaXplZCB0aGUgbWVhbiByZXR1cm4hDQoNCiMjIyBPcHRpbWl6YXRpb24gd2l0aCBwZXJpb2RpYyByZWJhbGFuY2luZw0KDQpSdW5uaW5nIHRoZSBvcHRpbWl6YXRpb24gd2l0aCBwZXJpb2RpYyByZWJhbGFuY2luZyBhbmQgYW5hbHl6aW5nIHRoZSBvdXQtb2Ytc2FtcGxlIHJlc3VsdHMgb2YgdGhlIGJhY2t0ZXN0IGlzIGFuIGltcG9ydGFudCBzdGVwIHRvIGJldHRlciB1bmRlcnN0YW5kIGFuZCBwb3RlbnRpYWxseSByZWZpbmUgdGhlIGNvbnN0cmFpbnRzIGFuZCBvYmplY3RpdmVzLiBvcHRpbWl6ZS5wb3J0Zm9saW8ucmViYWxhbmNpbmcoKSBzdXBwb3J0cyBvcHRpbWl6YXRpb24gd2l0aCBwZXJpb2RpYyByZWJhbGFuY2luZyAoYmFja3Rlc3RpbmcpIHRvIGV4YW1pbmUgb3V0IG9mIHNhbXBsZSBwZXJmb3JtYW5jZS4gSW4gYWRkaXRpb24gdG8gdGhlIGFyZ3VtZW50cyBmb3Igb3B0aW1pemUucG9ydGZvbGlvKCksIGEgcGVyaW9kaWMgcmViYWxhbmNpbmcgZnJlcXVlbmN5IG11c3QgYmUgc3BlY2lmaWVkIHdpdGggcmViYWxhbmNlX29uLCB0cmFpbmluZ19wZXJpb2QgdG8gc3BlY2lmeSB0aGUgbnVtYmVyIG9mIHBlcmlvZHMgdG8gdXNlIGFzIHRoZSB0cmFpbmluZyBkYXRhIGZvciB0aGUgaW5pdGlhbCBvcHRpbWl6YXRpb24sIGFuZCByb2xsaW5nX3dpbmRvdyB0byBzcGVjaWZ5IHRoZSBudW1iZXIgb2YgcGVyaW9kcyBmb3IgdGhlIHdpbmRvdyB3aWR0aCBvZiB0aGUgb3B0aW1pemF0aW9uLiBJZiByb2xsaW5nX3dpbmRvdyBpcyBzZXQgdG8gTlVMTCBlYWNoIG9wdGltaXphdGlvbiB3aWxsIHVzZSBhbGwgZGF0YSBhdmFpbGFibGUgYXQgdGhlIGdpdmVuIHBlcmlvZCB0aGUgb3B0aW1pemF0aW9uIGlzIHJ1bi4NCg0KVG8gcmVkdWNlIGNvbXB1dGF0aW9uIHRpbWUgZm9yIHRoaXMgZXhlcmNpc2UsIHRoZSBzZXQgb2YgcmFuZG9tIHBvcnRmb2xpb3MsIHJwLCBpcyBnZW5lcmF0ZWQgdXNpbmcgNTAgcGVybXV0YXRpb25zLCBhbmQgc2VhcmNoX3NpemUsIGhvdyBtYW55IHBvcnRmb2xpb3MgdG8gdGVzdCwgaXMgc2V0IHRvIDEwMDAuIElmIHlvdSBhcmUgYWN0dWFsbHkgb3B0aW1pemluZyBwb3J0Zm9saW9zIHlvdXJzZWxmLCB5b3UnbGwgcHJvYmFibHkgd2FudCB0byB0ZXN0IG1vcmUgcG9ydGZvbGlvcyAodGhlIGRlZmF1bHQgdmFsdWUgZm9yIHNlYXJjaF9zaXplIGlzIDIwLDAwMCkhDQoNClJ1biB0aGUgb3B0aW1pemF0aW9uIHdpdGggcXVhcnRlcmx5IHJlYmFsYW5jaW5nLiBTZXQgdGhlIHRyYWluaW5nIHBlcmlvZCBhbmQgcm9sbGluZyB3aW5kb3cgdG8gNjAgcGVyaW9kcy4gVGhlIGRhdGFzZXQgaXMgbW9udGhseSBkYXRhIHNvIHdlIGFyZSB1c2luZyA1IHllYXJzIG9mIGhpc3RvcmljYWwgZGF0YS4gQXNzaWduIHRoZSBvcHRpbWl6YXRpb24gb3V0cHV0IHRvIGEgdmFyaWFibGUgbmFtZWQgb3B0X3JlYmFsLg0KYGBge3J9DQojIENyZWF0ZSBhIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uIG9iamVjdCB1c2luZyBhc3NldF9uYW1lcw0KcG9ydF9zcGVjIDwtIHBvcnRmb2xpby5zcGVjKGFzc2V0cyA9IGFzc2V0X25hbWVzKQ0KIyBBZGQgYSBmdWxsIGludmVzdG1lbnQgY29uc3RyYWludCBzdWNoIHRoYXQgdGhlIHdlaWdodHMgc3VtIHRvIDENCnBvcnRfc3BlYyA8LSBhZGQuY29uc3RyYWludChwb3J0Zm9saW8gPXBvcnRfc3BlYywgdHlwZSA9ICJsb25nX29ubHkiKQ0KIyBBZGQgdGhlIHdlaWdodCBzdW0gY29uc3RyYWludA0KcG9ydF9zcGVjIDwtIGFkZC5jb25zdHJhaW50KHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgdHlwZSA9ICJ3ZWlnaHRfc3VtIiwgbWluX3N1bSA9IDAuOTksIG1heF9zdW0gPSAxLjAxKQ0KIyBBZGQgYSByZXR1cm4gb2JqZWN0aXZlIHRvIG1heGltaXplIG1lYW4gcmV0dXJuDQpwb3J0X3NwZWMgPC0gYWRkLm9iamVjdGl2ZShwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAicmV0dXJuIiwgbmFtZSA9ICJtZWFuIikNCg0KIyBBZGQgYSByaXNrIG9iamVjdGl2ZSB0byBtaW5pbWl6ZSBwb3J0Zm9saW8gc3RhbmRhcmQgZGV2aWF0aW9uDQpwb3J0X3NwZWMgPC0gYWRkLm9iamVjdGl2ZShwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAicmlzayIsIG5hbWUgPSAiU3RkRGV2IikNCg0KIyBBZGQgYSByaXNrIGJ1ZGdldCBvYmplY3RpdmUNCnBvcnRfc3BlYyA8LSBhZGQub2JqZWN0aXZlKHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgDQogICAgICAgIHR5cGUgPSAicmlza19idWRnZXQiLCBuYW1lID0gIlN0ZERldiIsIG1pbl9wcmlzayA9IDAuMDUsIA0KICAgICAgICBtYXhfcHJpc2sgPSAwLjEpDQpgYGANCg0KYGBge3J9DQpycCA8LSByYW5kb21fcG9ydGZvbGlvcyhwb3J0Zm9saW89cG9ydF9zcGVjLA0KICAgICAgICAgICAgICAgICAgICAgICAgcGVybXV0YXRpb25zID0gNTAsDQogICAgICAgICAgICAgICAgICAgICAgICBycF9tZXRob2Q9J3NpbXBsZXgnKQ0KYGBgDQpgYGB7cn0NCiMgUnVuIHRoZSBvcHRpbWl6YXRpb24gYmFja3Rlc3Qgd2l0aCBxdWFydGVybHkgcmViYWxhbmNpbmcNCm9wdF9yZWJhbCA8LSBvcHRpbWl6ZS5wb3J0Zm9saW8ucmViYWxhbmNpbmcoUiA9IGFzc2V0X3JldHVybnMsIHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgb3B0aW1pemVfbWV0aG9kID0gInJhbmRvbSIsIHJwID0gcnAsIHRyYWNlID0gVFJVRSwgc2VhcmNoX3NpemUgPSAxMDAwLCByZWJhbGFuY2Vfb24gPSAicXVhcnRlcnMiLCB0cmFpbmluZ19wZXJpb2QgPSA2MCwgcm9sbGluZ193aW5kb3cgPSA2MCkNCg0KDQojIFByaW50IHRoZSBvdXRwdXQgb2YgdGhlIG9wdGltaXphdGlvbiBiYWNrdGVzdA0KcHJpbnQob3B0X3JlYmFsKQ0KYGBgDQpVc2luZyBiYWNrdGVzdGluZyBjYW4gZ2l2ZSB1c2VmdWwgaW5zaWdodHMgb24gaG93IHlvdXIgb3B0aW1pemF0aW9uIHNldHVwIHBlcmZvcm1zIHVuZGVyIGRpZmZlcmVudCBjaXJjdW1zdGFuY2VzLg0KDQojIyBBbmFseXppbmcgb3B0aW1pemF0aW9uIHJlc3VsdHMNCg0KIyMjIE9iamVjdGl2ZSBtZWFzdXJlIHZhbHVlcw0KDQpUaGlzIGV4ZXJjaXNlIGlzIGEgY29udGludWF0aW9uIG9mIHRoZSBsYXN0IGV4ZXJjaXNlcyBhbmFseXNpbmcgdGhlIG91dHB1dCBvZiBvcHQgYW5kIG9wdF9yZWJhbC4gQWxsIG9iamVjdGl2ZXMgaW4gdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uIGFyZSBldmFsdWF0ZWQgZHVyaW5nIHRoZSBvcHRpbWl6YXRpb24gYW5kIHRoZSB2YWx1ZXMgY2FuIGJlIGV4dHJhY3RlZCB3aXRoIGV4dHJhY3RPYmplY3RpdmVNZWFzdXJlcygpLiBUaGUgb2JqZWN0aXZlIG1lYXN1cmVzIGFyZSBhbiBpbXBvcnRhbnQgY29tcG9uZW50IG9mIHRoZSBvcHRpbWl6YXRpb24gb3V0cHV0IHRvIGFuYWx5emUuIEFuIGltcG9ydGFudCB1c2UgY2FzZSBsb29rcyBhdCB0aGUgcmVzdWx0IG9mIGVhY2ggaXRlcmF0aW9uL3RyaWFsIG9mIHRoZSBvcHRpbWl6ZXIuIFRoaXMgY2FuIGdpdmUgeW91IGluc2lnaHQgaW50byBob3cgdGhlIG9wdGltaXplciBhcHByb2FjaGVzIHRoZSBvcHRpbWFsIHNvbHV0aW9uIGFuZCBwb3RlbnRpYWxseSB0dW5lIHBhcmFtZXRlcnMuIFdlIGNhbiBhbHNvIGFuYWx5c2UgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgaW4tc2FtcGxlIG9iamVjdGl2ZSB2YWx1ZXMgb2YgdGhlIG9wdGltYWwgcG9ydGZvbGlvcyBhbmQgY29tcGFyZSB0byBvdXQtb2Ytc2FtcGxlIHBlcmZvcm1hbmNlLg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIG9iamVjdGl2ZSBtZWFzdXJlcyBmb3IgdGhlIHNpbmdsZSBwZXJpb2Qgb3B0aW1pemF0aW9uDQpleHRyYWN0T2JqZWN0aXZlTWVhc3VyZXMob3B0KQ0KDQojIEV4dHJhY3QgdGhlIG9iamVjdGl2ZSBtZWFzdXJlcyBmb3IgdGhlIG9wdGltaXphdGlvbiBiYWNrdGVzdA0KaGVhZChleHRyYWN0T2JqZWN0aXZlTWVhc3VyZXMob3B0X3JlYmFsKSkNCmBgYA0KU3R1ZGluZyB0aGUgb2JqZWN0aXZlIG1lYXN1cmVzIGZvciBhIHJlYmFsYW5jZWQgcG9ydGZvbGlvIGNhbiBwcm92aWRlIGluc2lnaHRzIGludG8gaG93IGl0IGNoYW5nZWQgb3ZlciB0aW1lLg0KDQojIyMgT3B0aW1hbCB3ZWlnaHRzDQoNClRoaXMgZXhlcmNpc2UgaXMgYSBjb250aW51YXRpb24gb2YgdGhlIGxhc3QgZXhlcmNpc2VzIGFuYWx5c2luZyB0aGUgdGhlIG91dHB1dCBvZiBvcHQgYW5kIG9wdF9yZWJhbC4gRXh0cmFjdGluZyBhbmQgdmlzdWFsaXppbmcgdGhlIG9wdGltYWwgd2VpZ2h0cyBpcyBhbiBpbXBvcnRhbnQgY29tcG9uZW50IG9mIHRoZSBvcHRpbWl6YXRpb24uIFRoZSBvcHRpbWFsIHdlaWdodHMgY2FuIGJlIGV4dHJhY3RlZCB3aXRoIGV4dHJhY3RXZWlnaHRzKCkgYW5kIGNoYXJ0ZWQgd2l0aCBjaGFydC5XZWlnaHRzKCkuIFRoaXMgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3IgYmFja3Rlc3RzIHRvIHVuZGVyc3RhbmQgdGhlIGV2b2x1dGlvbiBvZiB3ZWlnaHRzIG92ZXIgdGltZS4gV2UgY2FuIHRoZW4gYW5zd2VyIHF1ZXN0aW9ucyBhYm91dCBob3cgYWxsb2NhdGlvbnMgY2hhbmdlIHRocm91Z2ggdGltZS4NCmBgYHtyfQ0KIyBFeHRyYWN0IHRoZSBvcHRpbWFsIHdlaWdodHMgZm9yIHRoZSBzaW5nbGUgcGVyaW9kIG9wdGltaXphdGlvbg0KZXh0cmFjdFdlaWdodHMob3B0KQ0KDQojIENoYXJ0IHRoZSB3ZWlnaHRzIGZvciB0aGUgc2luZ2xlIHBlcmlvZCBvcHRpbWl6YXRpb24NCmNoYXJ0LldlaWdodHMob3B0KQ0KDQojIEV4dHJhY3QgdGhlIG9wdGltYWwgd2VpZ2h0cyBmb3IgdGhlIG9wdGltaXphdGlvbiBiYWNrdGVzdA0KaGVhZChleHRyYWN0V2VpZ2h0cyhvcHRfcmViYWwpKQ0KDQojIENoYXJ0IHRoZSB3ZWlnaHRzIGZvciB0aGUgb3B0aW1pemF0aW9uIGJhY2t0ZXN0DQpjaGFydC5XZWlnaHRzKG9wdF9yZWJhbCkNCmBgYA0KVmlzdWFsaXppbmcgYSByZWJhbGFuY2VkIHBvcnRmb2xpbydzIHdlaWdodHMgb3ZlciB0aW1lIGNhbiBoZWxwIHlvdSBpZGVudGlmeSBwb3RlbnRpYWwgcHJvYmxlbXMgaW4geW91ciBtb2RlbC4NCg0KIyBPYmplY3RpdmUgRnVuY3Rpb25zIGFuZCBNb21lbnQgRXN0aW1hdGlvbg0KDQojIyBJbnRyb2R1Y3Rpb24gdG8gbW9tZW50cw0KDQojIyMgU2FtcGxlIG1vbWVudCBlc3RpbWF0ZXMNCg0KVGhlIGRlZmF1bHQgbWV0aG9kIGZvciBlc3RpbWF0aW5nIHBvcnRmb2xpbyBtb21lbnRzIGlzIHRoZSBzYW1wbGUgbWV0aG9kLiBUaGUgbW9tZW50cyBhcmUgY2FsY3VsYXRlZCBpbiBvcHRpbWl6ZS5wb3J0Zm9saW8oKSBieSBldmFsdWF0aW5nIHRoZSBmdW5jdGlvbiBwYXNzZWQgdG8gdGhlIG1vbWVudEZVTiBhcmd1bWVudC4gVGhlIGRlZmF1bHQgZm9yIG1vbWVudEZVTiBpcyBzZXQucG9ydGZvbGlvLm1vbWVudHMoKSB3aGljaCBkZWZhdWx0cyB0byBjYWxjdWxhdGluZyB0aGUgc2FtcGxlIG1vbWVudHMuIFRoZSBtb21lbnRzIGFyZSB0aGVuIHVzZWQgYXMgaW5wdXRzIHRvIHRoZSBvYmplY3RpdmUgZnVuY3Rpb25zLiBUaGUgbW9tZW50cyB0aGF0IG11c3QgYmUgZXN0aW1hdGVkIGRlcGVuZCBvbiB0aGUgb2JqZWN0aXZlcy4gRm9yIGV4YW1wbGUsIGFuIG9iamVjdGl2ZSB0byBtaW5pbWl6ZSBwb3J0Zm9saW8gc3RhbmRhcmQgZGV2aWF0aW9uIHJlcXVpcmVzIG9ubHkgYW4gZXN0aW1hdGUgb2YgdGhlIHNlY29uZCBtb21lbnQuIENvbXBhcmUgdGhhdCB0byB0aGUgb2JqZWN0aXZlIHRvIG1heGltaXplIFNoYXJwZSBSYXRpbyB3aGljaCByZXF1aXJlcyB0aGUgZmlyc3QgYW5kIHNlY29uZCBtb21lbnRzIHRvIGJlIGVzdGltYXRlZC4gU2FtcGxlIGVzdGltYXRlcyBvZiB0aGUgbW9tZW50cyBoYXZlIGRpc2FkdmFudGFnZXMgaW5jbHVkaW5nIGVzdGltYXRpb24gZXJyb3IgYW5kIHRoZSBjdXJzZSBvZiBkaW1lbnNpb25hbGl0eS4gVGhlcmUgaXMgYW4gaW5jcmVhc2VkIHJpc2sgb2YgZXN0aW1hdGlvbiBlcnJvciBhcyB0aGUgZGltZW5zaW9uIG9mIGFzc2V0cyBhbmQgcGFyYW1ldGVycyB0byBlc3RpbWF0ZSBpbmNyZWFzZS4NCmBgYHtyfQ0KIyBBZGQgYSByZXR1cm4gb2JqZWN0aXZlIHdpdGggIm1lYW4iIGFzIHRoZSBvYmplY3RpdmUgbmFtZQ0KcG9ydF9zcGVjIDwtIGFkZC5vYmplY3RpdmUocG9ydGZvbGlvID0gcG9ydF9zcGVjLCB0eXBlID0gInJldHVybiIsIG5hbWUgPSAibWVhbiIpDQoNCiMgQ2FsY3VsYXRlIHRoZSBzYW1wbGUgbW9tZW50cw0KbW9tZW50cyA8LSBzZXQucG9ydGZvbGlvLm1vbWVudHMoUiA9IGFzc2V0X3JldHVybnMsIHBvcnRmb2xpbyA9IHBvcnRfc3BlYykNCg0KIyBDaGVjayBpZiBtb21lbnRzJG11IGlzIGVxdWFsIHRvIHRoZSBzYW1wbGUgZXN0aW1hdGUgb2YgbWVhbiByZXR1cm5zDQptb21lbnRzJG11ID09IGNvbE1lYW5zKGFzc2V0X3JldHVybnMpDQoNCiMgQWRkIGEgcmlzayBvYmplY3RpdmUgd2l0aCAiU3RkRGV2IiBhcyB0aGUgb2JqZWN0aXZlIG5hbWUNCnBvcnRfc3BlYyA8LSBhZGQub2JqZWN0aXZlKHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgdHlwZSA9ICJyaXNrIiwgbmFtZSA9ICJTdGREZXYiKQ0KDQojIENhbGN1bGF0ZSB0aGUgc2FtcGxlIG1vbWVudHMgdXNpbmcgc2V0LnBvcnRmb2xpby5tb21lbnRzLiBBc3NpZ24gdG8gYSB2YXJpYWJsZSBuYW1lZCBtb21lbnRzLg0KbW9tZW50cyA8LSBzZXQucG9ydGZvbGlvLm1vbWVudHMoUiA9IGFzc2V0X3JldHVybnMsIHBvcnRmb2xpbyA9IHBvcnRfc3BlYykNCg0KIyBDaGVjayBpZiBtb21lbnRzJHNpZ21hIGlzIGVxdWFsIHRvIHRoZSBzYW1wbGUgZXN0aW1hdGUgb2YgdGhlIHZhcmlhbmNlLWNvdmFyaWFuY2UgbWF0cml4DQptb21lbnRzJHNpZ21hID09IGNvdihhc3NldF9yZXR1cm5zKQ0KYGBgDQpTaW1wbGUgc2FtcGxlIG1vbWVudHMgbWlnaHQgbm90IGFsd2F5cyBiZSBhIGdvb2QgZXN0aW1hdGUuIExldCdzIGxvb2sgYXQgc29tZSBvdGhlciBtZXRob2RzIGZvciBlc3RpbWF0aW5nIG1vbWVudHMuDQojIyMgQWR2YW5jZWQgbW9tZW50IGVzdGltYXRlcw0KUG9ydGZvbGlvQW5hbHl0aWNzIHN1cHBvcnRzIHRoZSAic2FtcGxlIiBtZXRob2QgYXMgd2VsbCBhcyB0aHJlZSBtb3JlIGFkdmFuY2VkIG1ldGhvZHMgZm9yIGVzdGltYXRpbmcgcG9ydGZvbGlvIG1vbWVudHMuDQoNCjEuCSJzYW1wbGUiOiBCYXNpYyBzYW1wbGUgZXN0aW1hdGUgb2YgZmlyc3QgZm91ciBtb21lbnRzLg0KMi4JImJvdWR0IjogVGhlIGZpcnN0IGZvdXIgbW9tZW50cyBhcmUgZXN0aW1hdGVkIGJ5IGZpdHRpbmcgYSBzdGF0aXN0aWNhbCBmYWN0b3IgbW9kZWwgYmFzZWQgb24gdGhlIHdvcmsgb2YgW0JvdWR0IGV0IGFsLiwgMjAxNF0oIGh0dHBzOi8vcGFwZXJzLnNzcm4uY29tL3NvbDMvcGFwZXJzLmNmbT9hYnN0cmFjdF9pZD0yNDA5NjAzKS4NCjMuCSJibGFja19saXR0ZXJtYW4iOiBUaGUgZmlyc3QgdHdvIG1vbWVudHMgYXJlIGVzdGltYXRlZCB1c2luZyB0aGUgW0JsYWNrLUxpdHRlcm1hbiBmcmFtZXdvcmtdKCBodHRwczovL3BhcGVycy5zc3JuLmNvbS9zb2wzL3BhcGVycy5jZm0/YWJzdHJhY3RfaWQ9MTExNzU3NCkuDQo0LgkiTWV1Y2NpIjogVGhlIGZpcnN0IHR3byBtb21lbnRzIGFyZSBlc3RpbWF0ZWQgdXNpbmcgdGhlIFtGdWxseSBGbGV4aWJsZSBWaWV3cyBmcmFtZXdvcmtdKCBodHRwczovL3BhcGVycy5zc3JuLmNvbS9zb2wzL3BhcGVycy5jZm0/YWJzdHJhY3RfaWQ9MTIxMzMyNSkuDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIGVzdGltYXRlIHRoZSBzZWNvbmQgbW9tZW50IHVzaW5nIHRoZSAiYm91ZHQiIG1ldGhvZC4gQSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBvYmplY3QgbmFtZWQgcG9ydF9zcGVjIHdpdGggYSAiU3RkRGV2IiBvYmplY3RpdmUgaGFzIGFscmVhZHkgYmVlbiBjcmVhdGVkLg0KDQpgYGB7cn0NCiMgUHJpbnQgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uIG9iamVjdA0KcHJpbnQocG9ydF9zcGVjKQ0KDQojIEZpdCBhIHN0YXRpc3RpY2FsIGZhY3RvciBtb2RlbCB0byB0aGUgYXNzZXQgcmV0dXJucw0KZml0IDwtIHN0YXRpc3RpY2FsLmZhY3Rvci5tb2RlbChSID0gYXNzZXRfcmV0dXJucywgayA9IDMpDQoNCiMgRXN0aW1hdGUgdGhlIHBvcnRmb2xpbyBtb21lbnRzIHVzaW5nIHRoZSAiYm91ZHQiIG1ldGhvZCB3aXRoIDMgZmFjdG9ycw0KbW9tZW50c19ib3VkdCA8LSBzZXQucG9ydGZvbGlvLm1vbWVudHMoUiA9IGFzc2V0X3JldHVybnMsIHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgbWV0aG9kID0gImJvdWR0IiwgayA9IDMpDQoNCiMgQ2hlY2sgaWYgdGhlIGNvdmFyaWFuY2UgbWF0cml4IGV4dHJhY3RlZCBmcm9tIHRoZSBtb2RlbCBmaXQgaXMgZXF1YWwgdG8gdGhlIGVzdGltYXRlIGluIGBtb21lbnRzX2JvdWR0YA0KbW9tZW50c19ib3VkdCRzaWdtYSA9PSBleHRyYWN0Q292YXJpYW5jZShmaXQpDQpgYGANClNvbWUgb2YgdGhlIHBpdGZhbGxzIG9mIHNhbXBsZSBtb21lbnRzIGFyZSBhdm9pZGVkIHVzaW5nIHRoZXNlIG90aGVyIGVzdGltYXRpb24gbWV0aG9kcy4NCg0KIyMgQ3VzdG9tIG1vbWVudCBmdW5jdGlvbnMNCg0KIyMjIERlZmluZSBhIGN1c3RvbSBtb21lbnQgZnVuY3Rpb24NCg0KSW4gbWFueSBjYXNlcyBmb3IgY29uc3RyYWluZWQgb3B0aW1pemF0aW9uIHByb2JsZW1zLCB0aGUgcG9ydGZvbGlvIG1hbmFnZXIgb3IgYW5hbHlzdCBtYXkgd2FudCB0byBlc3RpbWF0ZSBtb21lbnRzIGZvciBhIHNwZWNpZmljIHRlY2huaXF1ZSBhbmQvb3IgZnVydGhlciBleHRlbmQgdGhlIGlkZWEgb2Ygc2V0LnBvcnRmb2xpby5tb21lbnRzKCkuIEEgdXNlciBkZWZpbmVkIGN1c3RvbSBtb21lbnQgZnVuY3Rpb24gY2FuIGhhdmUgYW55IGFyYml0cmFyeSBuYW1lZCBhcmd1bWVudHMuIEhvd2V2ZXIsIGFyZ3VtZW50cyBuYW1lZCBSIGZvciB0aGUgYXNzZXQgcmV0dXJucyBhbmQgcG9ydGZvbGlvIGZvciB0aGUgcG9ydGZvbGlvIG9iamVjdCB3aWxsIGJlIGRldGVjdGVkIGF1dG9tYXRpY2FsbHkgYW5kIGhhbmRsZWQgaW4gYW4gZWZmaWNpZW50IG1hbm5lci4gQmVjYXVzZSBvZiB0aGlzLCBpdCBpcyBzdHJvbmdseSBlbmNvdXJhZ2VkIHRvIHVzZSBSIGZvciB0aGUgYXNzZXQgcmV0dXJucyBvYmplY3QgYW5kIHBvcnRmb2xpbyBmb3IgdGhlIHBvcnRmb2xpbyBvYmplY3QuDQoNClRoZSBjdXN0b20gbW9tZW50IGZ1bmN0aW9uIHNob3VsZCByZXR1cm4gYSBuYW1lZCBsaXN0IHdoZXJlIHRoZSBlbGVtZW50cyByZXByZXNlbnQgdGhlIG1vbWVudHM6DQoNCi0JJG11OiBmaXJzdCBtb21lbnQgKGV4cGVjdGVkIHJldHVybnMgdmVjdG9yKQ0KLQkkc2lnbWE6IHNlY29uZCBtb21lbnQgKHZhcmlhbmNlLWNvdmFyaWFuY2UgbWF0cml4KQ0KLQkkbTM6IHRoaXJkIG1vbWVudCAoY29za2V3bmVzcyBtYXRyaXgpDQotCSRtNDogZm91cnRoIG1vbWVudCAoY29rdXJ0b3NpcyBtYXRyaXgpDQoNCkluIHRoaXMgZXhlcmNpc2UsIHlvdSB3aWxsIHdyaXRlIGEgY3VzdG9tIG1vbWVudCBmdW5jdGlvbiB0byBlc3RpbWF0ZSB0aGUgdmFyaWFuY2UtY292YXJpYW5jZSBtYXRyaXggdXNpbmcgYSByb2J1c3QgbWV0aG9kLiBXZSB3aWxsIHVzZSB0aGUgY292LnJvYigpIGZ1bmN0aW9uIGZyb20gdGhlIE1BU1MgcGFja2FnZS4gVGhlIGZ1bmN0aW9uIHNpZ25hdHVyZSBzaG91bGQgaGF2ZSBhcmd1bWVudHMgbmFtZWQgUiBmb3IgdGhlIGFzc2V0IHJldHVybnMgYW5kIHBvcnRmb2xpbyBmb3IgdGhlIHNwZWNpZmljYXRpb24gb2JqZWN0LiBUaGUgZnVuY3Rpb24gc2hvdWxkIHJldHVybiBhIG5hbWVkIGxpc3QuIEJlY2F1c2UgeW91IGFyZSBvbmx5IGVzdGltYXRpbmcgdGhlIHNlY29uZCBtb21lbnQsIHlvdSBvbmx5IG5lZWQgdG8gcmV0dXJuIGEgbGlzdCB3aXRoIG9uZSBlbGVtZW50IGFwcHJvcHJpYXRlbHkgbmFtZWQuIFlvdSBjYW4gYXBwbHkgdGhlc2UgcnVsZXMgdG8gd3JpdGUgY3VzdG9tIG1vbWVudCBmdW5jdGlvbnMgZm9yIG90aGVyIG1vZGVscyBzdWNoIGFzIGZhY3RvciBtb2RlbHMsIEdBUkNIIG1vZGVscywgb3IgYW55IG90aGVyIGNsYXNzIG9mIG1vZGVscyB0aGF0IHRoZW9yZXRpY2FsbHkgc2hvdWxkIGJlIGEgYmV0dGVyIGVzdGltYXRlIHRoYW4gdGhlIHNhbXBsZSBlc3RpbWF0ZS4NCg0KLSBEZWZpbmUgYSBmdW5jdGlvbiBuYW1lZCBtb21lbnRzX3JvYnVzdCB0aGF0IGVzdGltYXRlcyB0aGUgdmFyaWFuY2UtY292YXJpYW5jZSBtYXRyaXggb2YgdGhlIGFzc2V0IHJldHVybnMgdXNpbmcgdGhlICJtY2QiIG1ldGhvZC4NCi0gCUVzdGltYXRlIHRoZSBwb3J0Zm9saW8gbW9tZW50cyB5b3UganVzdCBkZWZpbmVkLiBBc3NpZ24gaXQgdG8gYSB2YXJpYWJsZSBuYW1lZCBtb21lbnRzLiBZb3UgYXJlIGRvaW5nIHRoaXMgYXMgYSBjaGVjayB0byBlbnN1cmUgdGhhdCB5b3VyIGN1c3RvbSBtb21lbnQgZnVuY3Rpb24gaXMgd29ya2luZyBhcyBleHBlY3RlZC4NCi0gQ29tcHV0ZSB0aGUgdmFyaWFuY2UtY292YXJpYW5jZSBtYXRyaXggZGlyZWN0bHkgdXNpbmcgY292LnJvYigpIGFuZCBjaGVjayBpZiBpdCBpcyBlcXVhbCB0byBtb21lbnRzJHNpZ21hDQoNCmBgYHtyfQ0KbGlicmFyeShNQVNTKQ0KIyBEZWZpbmUgY3VzdG9tIG1vbWVudCBmdW5jdGlvbg0KbW9tZW50c19yb2J1c3QgPC0gZnVuY3Rpb24oUiwgcG9ydGZvbGlvKXsNCiAgb3V0IDwtIGxpc3QoKQ0KICBvdXQkc2lnbWEgPC0gY292LnJvYihSLCBtZXRob2QgPSAibWNkIikkY292DQogIG91dA0KfQ0KDQojIEVzdGltYXRlIHRoZSBwb3J0Zm9saW8gbW9tZW50cyB1c2luZyB0aGUgZnVuY3Rpb24geW91IGp1c3QgZGVmaW5lZCANCm1vbWVudHMgPC0gbW9tZW50c19yb2J1c3QoUiA9IGFzc2V0X3JldHVybnMsIHBvcnRmb2xpbyA9IHBvcnRfc3BlYykNCg0KIyBDaGVjayB0aGUgbW9tZW50IGVzdGltYXRlDQpjb3Yucm9iKGFzc2V0X3JldHVybnMsIG1ldGhvZCA9ICJtY2QiKSRjb3YgPT0gbW9tZW50cyRzaWdtYQ0KYGBgDQojIyMgT3B0aW1pemF0aW9uIHdpdGggY3VzdG9tIG1vbWVudCBmdW5jdGlvbg0KDQpOb3cgd2Ugd291bGQgbGlrZSB0byBydW4gdGhlIG9wdGltaXphdGlvbiB1c2luZyBvdXIgY3VzdG9tIG1vbWVudCBmdW5jdGlvbi4gUmVjYWxsIHRoYXQgdGhlIHBvcnRmb2xpbyBtb21lbnRzIGFyZSBzZXQgaW4gb3B0aW1pemUucG9ydGZvbGlvKCkgd2hlbiB0aGUgbW9tZW50IGZ1bmN0aW9uIGlzIGV2YWx1YXRlZC4gV2UgdXNlIHRoZSBjdXN0b20gbW9tZW50IGZ1bmN0aW9uIGJ5IHBhc3NpbmcgaW4gdGhlIG5hbWUgdG8gdGhlIG1vbWVudEZVTiBhcmd1bWVudCBpbiBvcHRpbWl6ZS5wb3J0Zm9saW8oKS4gTm90ZSBob3cgd2UgY2FuIHVzZSBQb3J0Zm9saW9BbmFseXRpY3MgdG8gZWFzaWx5IHJ1biBvcHRpbWl6YXRpb25zIHVzaW5nIGRpZmZlcmVudCBtZXRob2RzIGZvciBlc3RpbWF0aW5nIG1vbWVudHMsIHdoaWNoIHdpbGwgYWxsb3cgdXMgdG8gZXZhbHVhdGUgZGlmZmVyZW50IHRlY2huaXF1ZXMgZm9yIG1vbWVudCBlc3RpbWF0ZXMgYW5kIHJlZmluZSB0aG9zZSBlc3RpbWF0ZXMgYnkgYW5hbHl6aW5nIHRoZSBvcHRpbWl6YXRpb24gcmVzdWx0cy4NCmBgYHtyfQ0KIyBDcmVhdGUgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uDQpwb3J0X3NwZWMgPC0gcG9ydGZvbGlvLnNwZWMoY29sbmFtZXMoYXNzZXRfcmV0dXJucykpDQoNCiMgQWRkIGEgZnVsbCBpbnZlc3RtZW50IGNvbnN0cmFpbnQgc3VjaCB0aGF0IHRoZSB3ZWlnaHRzIHN1bSB0byAxDQpwb3J0X3NwZWMgPC0gYWRkLmNvbnN0cmFpbnQocG9ydGZvbGlvID1wb3J0X3NwZWMsIHR5cGUgPSAiZnVsbF9pbnZlc3RtZW50IikNCg0KIyBBZGQgYSBsb25nIG9ubHkgY29uc3RyYWludCBzdWNoIHRoYXQgdGhlIHdlaWdodCBvZiBhbiBhc3NldCBpcyBiZXR3ZWVuIDAgYW5kIDENCnBvcnRfc3BlYyA8LSBhZGQuY29uc3RyYWludChwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAibG9uZ19vbmx5IikNCg0KIyBBZGQgYW4gb2JqZWN0aXZlIHRvIG1pbmltaXplIHBvcnRmb2xpbyBzdGFuZGFyZCBkZXZpYXRpb24NCnBvcnRfc3BlYyA8LSBhZGQub2JqZWN0aXZlKHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgdHlwZSA9ICJyaXNrIiwgbmFtZSA9ICJTdGREZXYiKQ0KYGBgDQoNCmBgYHtyfQ0KcnAgPC0gcmFuZG9tX3BvcnRmb2xpb3MocG9ydGZvbGlvPXBvcnRfc3BlYywNCiAgICAgICAgICAgICAgICAgICAgICAgIHBlcm11dGF0aW9ucyA9IDUwLA0KICAgICAgICAgICAgICAgICAgICAgICAgcnBfbWV0aG9kPSdzYW1wbGUnKQ0KYGBgDQoNCmBgYHtyfQ0KIyBSdW4gdGhlIG9wdGltaXphdGlvbiB3aXRoIGN1c3RvbSBtb21lbnQgZXN0aW1hdGVzDQpvcHRfY3VzdG9tIDwtIG9wdGltaXplLnBvcnRmb2xpbyhSID0gYXNzZXRfcmV0dXJucywgcG9ydGZvbGlvID0gcG9ydF9zcGVjLCBvcHRpbWl6ZV9tZXRob2QgPSAicmFuZG9tIiwgcnAgPSBycCwgbW9tZW50RlVOID0gIm1vbWVudHNfcm9idXN0IikNCg0KIyBQcmludCB0aGUgcmVzdWx0cyBvZiB0aGUgb3B0aW1pemF0aW9uIHdpdGggY3VzdG9tIG1vbWVudCBlc3RpbWF0ZXMNCnByaW50KG9wdF9jdXN0b20pDQoNCiMgUnVuIHRoZSBvcHRpbWl6YXRpb24gd2l0aCBzYW1wbGUgbW9tZW50IGVzdGltYXRlcw0Kb3B0X3NhbXBsZSA8LSBvcHRpbWl6ZS5wb3J0Zm9saW8oUiA9IGFzc2V0X3JldHVybnMsIHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgb3B0aW1pemVfbWV0aG9kID0gInJhbmRvbSIsIHJwID0gcnApDQoNCiMgUHJpbnQgdGhlIHJlc3VsdHMgb2YgdGhlIG9wdGltaXphdGlvbiB3aXRoIHNhbXBsZSBtb21lbnQgZXN0aW1hdGVzDQpwcmludChvcHRfc2FtcGxlKQ0KYGBgDQpMb29rcyBsaWtlIG91ciBjdXN0b20gbW9tZW50IGZ1bmN0aW9uIGhhZCBhIGxvd2VyIHN0YW5kYXJkIGRldmlhdGlvbiB0aGFuIHRoZSBzYW1wbGUgbWV0aG9kLg0KDQojIyBPYmplY3RpdmUgZnVuY3Rpb25zDQoNCiMjIyBDdXN0b20gb2JqZWN0aXZlIGZ1bmN0aW9uDQoNCkEga2V5IGZlYXR1cmUgb2YgUG9ydGZvbGlvQW5hbHl0aWNzIGlzIHRoYXQgdGhlIG5hbWUgZm9yIGFuIG9iamVjdGl2ZSBpcyBhIHZhbGlkIFIgZnVuY3Rpb24uIFRoZSBwYWNrYWdlIHdhcyBkZXNpZ25lZCB0byBiZSBmbGV4aWJsZSBhbmQgbW9kdWxhciwgYW5kIGN1c3RvbSBvYmplY3RpdmUgZnVuY3Rpb25zIGFyZSBhIGdyZWF0IGV4YW1wbGUgb2YgdGhpcy4gQSBmZXcgZ3VpZGVsaW5lcyBzaG91bGQgYmUgZm9sbG93ZWQgZm9yIGRlZmluaW5nIGEgY3VzdG9tIG1vbWVudCBmdW5jdGlvbjoNCg0KVGhlIG9iamVjdGl2ZSBmdW5jdGlvbiBtdXN0IHJldHVybiBhIHNpbmdsZSB2YWx1ZSBmb3IgdGhlIG9wdGltaXplciB0byBtaW5pbWl6ZSBvciBtYXhpbWl6ZS4NCkl0IGlzIHN0cm9uZ2x5IGVuY291cmFnZWQgdG8gdXNlIFIgZm9yIHRoZSBhc3NldCByZXR1cm5zIGFuZCB3ZWlnaHRzIGZvciB0aGUgcG9ydGZvbGlvIHdlaWdodHMuDQpUaGVzZSBhcmd1bWVudCBuYW1lcyBhcmUgZGV0ZWN0ZWQgYXV0b21hdGljYWxseSBhbmQgaGFuZGxlZCBpbiBhbiBlZmZpY2llbnQgbWFubmVyLiBBbnkgb3RoZXIgYXJndW1lbnRzIGZvciB0aGUgb2JqZWN0aXZlIGZ1bmN0aW9uIGNhbiBiZSBwYXNzZWQgaW4gYXMgYSBuYW1lZCBsaXN0IHRvIGFyZ3VtZW50cyBpbiB0aGUgYWRkLm9iamVjdGl2ZSgpIGZ1bmN0aW9uLg0KYGBge3J9DQojIEN1c3RvbSBhbm51YWxpemVkIHBvcnRmb2xpbyBzdGFuZGFyZCBkZXZpYXRpb24NCnBhc2QgPC0gZnVuY3Rpb24oUiwgd2VpZ2h0cywgc2lnbWEsIHNjYWxlID0gMTIpew0KICBzcXJ0KGFzLm51bWVyaWModCh3ZWlnaHRzKSAlKiUgc2lnbWEgJSolIHdlaWdodHMpKSAqIHNxcnQoc2NhbGUpDQp9DQpgYGANCkhvdyB3b3VsZCB5b3UgdXNlIHRoaXMgdG8gb3B0aW1pemUgeW91ciBwb3J0Zm9saW8/DQoNCiMjIyBPcHRpbWl6YXRpb24gd2l0aCBjdXN0b20gb2JqZWN0aXZlIGZ1bmN0aW9uDQoNClRoaXMgZXhlcmNpc2UgYnVpbGRzIG9uIHRoZSBwcmV2aW91cyBleGVyY2lzZSBhbmQgd2Ugd2lsbCBydW4gdGhlIG9wdGltaXphdGlvbiB3aXRoIHRoZSBjdXN0b20gb2JqZWN0aXZlIGZ1bmN0aW9uIHRoYXQgY29tcHV0ZXMgcG9ydGZvbGlvIGFubnVhbGl6ZWQgc3RhbmRhcmQgZGV2aWF0aW9uLiBCZWNhdXNlIGFuIG9iamVjdGl2ZSBmdW5jdGlvbiBjYW4gYmUgYW55IHZhbGlkIFIgZnVuY3Rpb24sIHdlIGFkZCBhIHJpc2sgb2JqZWN0aXZlIGZvciB0aGUgcGFzZCgpIGZ1bmN0aW9uLiBUaGUgc2V0LnBvcnRmb2xpby5tb21lbnRzKCkgZnVuY3Rpb24gd2lsbCBub3QgcmVjb2duaXplIHRoZSBwYXNkKCkgb2JqZWN0aXZlIG5hbWUsIHNvIHdlIG5lZWQgdG8gY3JlYXRlIGEgY3VzdG9tIG1vbWVudCBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIHNlY29uZCBtb21lbnQsIHNpZ21hLiBXZSB3aWxsIHNvbHZlIHRoZSBwcm9ibGVtIHVzaW5nIHJhbmRvbSBwb3J0Zm9saW9zIGFzIHRoZSBvcHRpbWl6YXRpb24gbWV0aG9kLg0KYGBge3J9DQpzZXRfc2lnbWEgPC0gZnVuY3Rpb24oUil7DQogIG91dCA8LSBsaXN0KCkNCiAgb3V0JHNpZ21hIDwtIGNvdihSKQ0KICBvdXQNCn0NCmBgYA0KYGBge3J9DQojIENyZWF0ZSB0aGUgcG9ydGZvbGlvIHNwZWNpZmljYXRpb24NCnBvcnRfc3BlYyA8LSBwb3J0Zm9saW8uc3BlYyhjb2xuYW1lcyhhc3NldF9yZXR1cm5zKSkNCg0KIyBBZGQgYSBmdWxsIGludmVzdG1lbnQgY29uc3RyYWludCBzdWNoIHRoYXQgdGhlIHdlaWdodHMgc3VtIHRvIDENCnBvcnRfc3BlYyA8LSBhZGQuY29uc3RyYWludChwb3J0Zm9saW8gPXBvcnRfc3BlYywgdHlwZSA9ICJmdWxsX2ludmVzdG1lbnQiKQ0KDQojIEFkZCBhIGxvbmcgb25seSBjb25zdHJhaW50IHN1Y2ggdGhhdCB0aGUgd2VpZ2h0IG9mIGFuIGFzc2V0IGlzIGJldHdlZW4gMCBhbmQgMQ0KcG9ydF9zcGVjIDwtIGFkZC5jb25zdHJhaW50KHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgdHlwZSA9ICJsb25nX29ubHkiLCApDQpgYGANCmBgYHtyfQ0KcnAgPC0gcmFuZG9tX3BvcnRmb2xpb3MocG9ydGZvbGlvPXBvcnRfc3BlYywNCiAgICAgICAgICAgICAgICAgICAgICAgIHBlcm11dGF0aW9ucyA9IDQwLA0KICAgICAgICAgICAgICAgICAgICAgICAgcnBfbWV0aG9kPSdzaW1wbGV4JykNCmBgYA0KYGBge3J9DQojIEFkZCBjdXN0b20gb2JqZWN0aXZlIHRvIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uDQpwb3J0X3NwZWMgPC0gYWRkLm9iamVjdGl2ZShwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAicmlzayIsIG5hbWUgPSAicGFzZCIpDQoNCiMgUHJpbnQgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0b24gb2JqZWN0DQpwcmludChwb3J0X3NwZWMpDQoNCiMgUnVuIHRoZSBvcHRpbWl6YXRpb24NCm9wdCA8LSBvcHRpbWl6ZS5wb3J0Zm9saW8oUiA9IGFzc2V0X3JldHVybnMsIHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgbW9tZW50RlVOID0gc2V0X3NpZ21hLCBvcHRpbWl6ZV9tZXRob2QgPSAicmFuZG9tIiwgcnAgPSBycCkNCg0KIyBQcmludCB0aGUgcmVzdWx0cyBvZiB0aGUgb3B0aW1pemF0aW9uDQpwcmludChvcHQpDQpgYGANCiMgQXBwbGljYXRpb24NCg0KIyMgSW50cm8NCg0KIyMjIENvbXB1dGUgYmVuY2htYXJrIHJldHVybnMNCg0KSW4gdGhpcyBleGVyY2lzZSwgd2Ugd2lsbCBjcmVhdGUgYSBiZW5jaG1hcmsgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBvcHRpbWl6YXRpb24gbW9kZWxzIGluIGxhdGVyIGV4ZXJjaXNlcy4gQW4gZXF1YWwgd2VpZ2h0IGJlbmNobWFyayBpcyBhIHNpbXBsZSB3ZWlnaHRpbmcgc2NoZW1lIHRvIGNvbnN0cnVjdCB0aGUgYmVuY2htYXJrIHBvcnRmb2xpby4gVGhlIGludHVpdGlvbiBvZiBhbiBlcXVhbCB3ZWlnaHQgYXBwcm9hY2ggaXMgdGhhdCB0aGVyZSBpcyBubyBwcmVmZXJlbmNlIGZvciBhbnkgZ2l2ZW4gYXNzZXQuIFdlIGFyZSBzZXR0aW5nIHRoaXMgdXAgdG8gYW5zd2VyIHRoZSBxdWVzdGlvbiwgIkNhbiBvcHRpbWl6YXRpb24gb3V0cGVyZm9ybSBhIHNpbXBsZSB3ZWlnaHRpbmcgc2NoZW1lIHRvIGNvbnN0cnVjdCBhIHBvcnRmb2xpbz8iDQpgYGB7cn0NCiMgTG9hZCB0aGUgcGFja2FnZQ0KbGlicmFyeShQb3J0Zm9saW9BbmFseXRpY3MpDQoNCiMgTG9hZCB0aGUgZGF0YQ0KZGF0YShlZGhlYykNCg0KIyBBc3NpZ24gdGhlIGRhdGEgdG8gYSB2YXJpYWJsZQ0KYXNzZXRfcmV0dXJucyA8LSBlZGhlYw0KDQojIENyZWF0ZSBhIHZlY3RvciBvZiBlcXVhbCB3ZWlnaHRzDQplcXVhbF93ZWlnaHRzIDwtIHJlcCgxIC8gbmNvbChlZGhlYyksIG5jb2woZWRoZWMpKQ0KDQojIENvbXB1dGUgdGhlIGJlbmNobWFyayByZXR1cm5zDQpyX2JlbmNobWFyayA8LSBSZXR1cm4ucG9ydGZvbGlvKFIgPSBhc3NldF9yZXR1cm5zLCB3ZWlnaHRzID0gZXF1YWxfd2VpZ2h0cywgcmViYWxhbmNlX29uID0gInF1YXJ0ZXJzIikNCmNvbG5hbWVzKHJfYmVuY2htYXJrKSA8LSAiYmVuY2htYXJrIg0KDQojIFBsb3QgdGhlIGJlbmNobWFyayByZXR1cm5zDQpwbG90KHJfYmVuY2htYXJrKQ0KYGBgDQojIyMgRGVmaW5lIHRoZSBwb3J0Zm9saW8gb3B0aW1pemF0aW9uIHByb2JsZW0NCg0KV2UgZGVmaW5lIHRoZSBwb3J0Zm9saW8gb3B0aW1pemF0aW9uIHByb2JsZW0gdG8gbWluaW1pemUgcG9ydGZvbGlvIHN0YW5kYXJkIGRldmlhdGlvbiBzdWJqZWN0IHRvIGZ1bGwgaW52ZXN0bWVudCBhbmQgbG9uZyBvbmx5IGNvbnN0cmFpbnRzLiBJbiB0aGlzIHByb2JsZW0sIHdlIHdpbGwgc2V0IHVwIHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBiYXNlZCBvbiB0aGUgZGVmaW5lZCBwcm9ibGVtLiBUaGUgZm9sbG93aW5nIGV4ZXJjaXNlcyBpbiB0aGlzIGNoYXB0ZXIgd2lsbCBidWlsZCBvbiB0aGUgaW5pdGlhbCBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBzZXQgdXAgaGVyZS4NCmBgYHtyfQ0KIyBDcmVhdGUgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uDQpwb3J0X3NwZWMgPC0gcG9ydGZvbGlvLnNwZWMoYXNzZXRzID0gY29sbmFtZXMoYXNzZXRfcmV0dXJucykpDQoNCiMgQWRkIGEgZnVsbCBpbnZlc3RtZW50IGNvbnN0cmFpbnQgc3VjaCB0aGF0IHRoZSB3ZWlnaHRzIHN1bSB0byAxDQpwb3J0X3NwZWMgPC0gYWRkLmNvbnN0cmFpbnQocG9ydGZvbGlvID0gcG9ydF9zcGVjLCB0eXBlID0gImZ1bGxfaW52ZXN0bWVudCIpDQoNCnBvcnRfc3BlYyA8LSBhZGQuY29uc3RyYWludChwb3J0Zm9saW8gPSBwb3J0X3NwZWMsIHR5cGUgPSAid2VpZ2h0X3N1bV9jb25zdHJhaW50IiwgbWluX3N1bT0wLjk4LCBtYXhfc3VtPTEuMDIpDQojIEFkZCBhIGxvbmcgb25seSBjb25zdHJhaW50IHN1Y2ggdGhhdCB0aGUgd2VpZ2h0IG9mIGFuIGFzc2V0IGlzIGJldHdlZW4gMCBhbmQgMQ0KcG9ydF9zcGVjIDwtIGFkZC5jb25zdHJhaW50KHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgdHlwZSA9ICJsb25nX29ubHkiKQ0KDQojIEFkZCBhbiBvYmplY3RpdmUgdG8gbWluaW1pemUgcG9ydGZvbGlvIHN0YW5kYXJkIGRldmlhdGlvbg0KcG9ydF9zcGVjIDwtIGFkZC5vYmplY3RpdmUocG9ydGZvbGlvID0gcG9ydF9zcGVjLCB0eXBlID0gInJpc2siLCBuYW1lID0gIlN0ZERldiIpDQoNCiMgUHJpbnQgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uDQpwcmludChwb3J0X3NwZWMpDQpgYGANCiMjIyBCZW5jaG1hcmsNCg0KQSByZWFzb25hYmxlIGJlbmNobWFyayBpcyBpbXBvcnRhbnQgZm9yIGFjY3VyYXRlbHkgbWVhc3VyaW5nIHRoZSByZWxhdGl2ZSBwZXJmb3JtYW5jZSBvZiBhIHBvcnRmb2xpbw0KVGhlIGJlbmNobWFyayBzaG91bGQgcmVmbGVjdCB0aGUgdW5pdmVyc2Ugb2YgYXNzZXRzIGFuZC9vciBzdHlsZSBvZiB0aGUgcG9ydGZvbGlvLg0KQmVuY2htYXJrcyBzaG91bGQgbmV2ZXIgYmUgcHVycG9zZWZ1bGx5IGRlc2lnbmVkIHRvIGhhdmUgcG9vciBwZXJmb3JtYW5jZSwgYXMgdGhpcyB3b3VsZCBza2V3IHlvdXIgcmVzdWx0cy4NCg0KIyMgT3B0aW1pemF0aW9uIGJhY2t0ZXN0DQoNCiMjIyBCYWNrdGVzdCB3aXRoIHBlcmlvZGljIHJlYmFsYW5jaW5nDQoNCk5vdyB3ZSB3aWxsIHJ1biB0aGUgYmFja3Rlc3QgdXNpbmcgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uIGNyZWF0ZWQgaW4gdGhlIGxhc3QgZXhlcmNpc2Ugd2l0aCBxdWFydGVybHkgcmViYWxhbmNpbmcgdG8gZXZhbHVhdGUgb3V0LW9mLXNhbXBsZSBwZXJmb3JtYW5jZS4gVGhlIG90aGVyIGJhY2t0ZXN0IHBhcmFtZXRlcnMgd2UgbmVlZCB0byBzZXQgYXJlIHRoZSB0cmFpbmluZyBwZXJpb2QgYW5kIHJvbGxpbmcgd2luZG93LiBUaGUgdHJhaW5pbmcgcGVyaW9kIHNldHMgdGhlIG51bWJlciBvZiBkYXRhIHBvaW50cyB0byB1c2UgZm9yIHRoZSBpbml0aWFsIG9wdGltaXphdGlvbi4gVGhlIHJvbGxpbmcgd2luZG93IHNldHMgdGhlIG51bWJlciBvZiBwZXJpb2RzIHRvIHVzZSBpbiB0aGUgd2luZG93LiBUaGlzIHByb2JsZW0gY2FuIGJlIHNvbHZlZCB3aXRoIGEgcXVhZHJhdGljIHByb2dyYW1taW5nIHNvbHZlciBzbyB3ZSB3aWxsIHVzZSAiUk9JIiBmb3IgdGhlIG9wdGltaXphdGlvbiBtZXRob2QuDQpgYGB7cn0NCiMgUnVuIHRoZSBvcHRpbWl6YXRpb24NCm9wdF9yZWJhbF9iYXNlIDwtIG9wdGltaXplLnBvcnRmb2xpby5yZWJhbGFuY2luZyhSID0gYXNzZXRfcmV0dXJucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9ydGZvbGlvID0gcG9ydF9zcGVjLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpbWl6ZV9tZXRob2QgPSAiUk9JIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmViYWxhbmNlX29uID0gInF1YXJ0ZXJzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfcGVyaW9kID0gNjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9sbGluZ193aW5kb3cgPSA2MCkNCg0KIyBQcmludCB0aGUgcmVzdWx0cw0KcHJpbnQob3B0X3JlYmFsX2Jhc2UpDQoNCiMgQ2hhcnQgdGhlIHdlaWdodHMNCmNoYXJ0LldlaWdodHMob3B0X3JlYmFsX2Jhc2UpDQoNCiMgQ29tcHV0ZSB0aGUgcG9ydGZvbGlvIHJldHVybnMNCnJldHVybnNfYmFzZSA8LSBSZXR1cm4ucG9ydGZvbGlvKFIgPSBhc3NldF9yZXR1cm5zLCB3ZWlnaHRzID0gZXh0cmFjdFdlaWdodHMob3B0X3JlYmFsX2Jhc2UpKQ0KY29sbmFtZXMocmV0dXJuc19iYXNlKSA8LSAiYmFzZSINCmBgYA0KIyMjIFJlZmluZSBjb25zdHJhaW50cyBhbmQgb2JqZWN0aXZlcw0KDQpIZXJlIHdlIGh5cG90aGVzaXplIHRoYXQgcmVmaW5pbmcgY29uc3RyYWludHMgYW5kL29yIG9iamVjdGl2ZXMgd2lsbCBpbXByb3ZlIHBlcmZvcm1hbmNlLiBMZXQgdXMgYWRkIGEgcmlzayBidWRnZXQgb2JqZWN0aXZlIHRvIHNldCBhIG1pbmltdW0gYW5kIG1heGltdW0gcGVyY2VudGFnZSBjb250cmlidXRpb24gdG8gcmlzayBmb3IgZWFjaCBhc3NldC4gV2Ugd2lsbCBiZSBidWlsZGluZyBvbiB0aGUgcG9ydGZvbGlvIHNwZWNpZmljYXRpb24gd2UgY3JlYXRlZC4gVGhpcyBpcyBhIG1vcmUgY29tcGxleCBvcHRpbWl6YXRpb24gcHJvYmxlbSBhbmQgd2lsbCByZXF1aXJlIGEgZ2xvYmFsIHNvbHZlciBzbyB3ZSB3aWxsIHVzZSByYW5kb20gcG9ydGZvbGlvcyBhcyB0aGUgb3B0aW1pemF0aW9uIG1ldGhvZC4NCmBgYHtyfQ0KcnAgPC0gcmVhZFJEUygiZGF0YS9ycF9maV9sb19yZXQucmRzIikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBBZGQgYSByaXNrIGJ1ZGdlIG9iamVjdGl2ZQ0KcG9ydF9zcGVjIDwtIGFkZC5vYmplY3RpdmUocG9ydGZvbGlvID0gcG9ydF9zcGVjLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAicmlza19idWRnZXQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiU3RkRGV2IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fcHJpc2sgPSAwLjA1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heF9wcmlzayA9IDAuMSkNCg0KIyBSdW4gdGhlIG9wdGltaXphdGlvbg0Kb3B0X3JlYmFsX3JiIDwtIG9wdGltaXplLnBvcnRmb2xpby5yZWJhbGFuY2luZyhSID0gYXNzZXRfcmV0dXJucywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdGltaXplX21ldGhvZCA9ICJyYW5kb20iLCBycCA9IHJwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFjZSA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYmFsYW5jZV9vbiA9ICJxdWFydGVycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpbmluZ19wZXJpb2QgPSA2MCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9sbGluZ193aW5kb3cgPSA2MCkNCg0KIyBDaGFydCB0aGUgd2VpZ2h0cw0KY2hhcnQuV2VpZ2h0cyhvcHRfcmViYWxfcmIpDQoNCiMgQ2hhcnQgdGhlIHBlcmNlbnRhZ2UgY29udHJpYnV0aW9uIHRvIHJpc2sNCmNoYXJ0LlJpc2tCdWRnZXQob3B0X3JlYmFsX3JiLCBtYXRjaC5jb2wgPSAiU3RkRGV2Iiwgcmlzay50eXBlID0gInBlcmNlbnRhZ2UiKQ0KDQojIENvbXB1dGUgdGhlIHBvcnRmb2xpbyByZXR1cm5zDQpyZXR1cm5zX3JiIDwtIFJldHVybi5wb3J0Zm9saW8oUiA9IGFzc2V0X3JldHVybnMsIHdlaWdodHMgPSBleHRyYWN0V2VpZ2h0cyhvcHRfcmViYWxfcmIpKQ0KY29sbmFtZXMocmV0dXJuc19yYikgPC0gInJpc2tfYnVkZ2V0Ig0KYGBgDQojIyMgRG8gaW1wcm92ZWQgZXN0aW1hdGVzIGxlYWQgdG8gaW1wcm92ZWQgcGVyZm9ybWFuY2U/DQoNCkxldCB1cyBoeXBvdGhlc2l6ZSB0aGF0IHVzaW5nIGEgcm9idXN0IGVzdGltYXRlIG9mIHRoZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCB3aWxsIG91dHBlcmZvcm0gdGhlIHNhbXBsZSB2YXJpYW5jZSBjb3ZhcmlhbmNlIG1hdHJpeC4gSW4gdGhlb3J5LCBiZXR0ZXIgZXN0aW1hdGVzIHNob3VsZCBsZWFkIHRvIGJldHRlciByZXN1bHRzLiBXZSB3aWxsIHVzZSB0aGUgbW9tZW50c19yb2J1c3QoKSBmdW5jdGlvbiB0aGF0IHdhcyBkZWZpbmVkIGluIGNoYXB0ZXIgMyBhbmQgdGhlIHBvcnRmb2xpbyBzcGVjaWZpY2F0aW9uIGZyb20gdGhlIGxhc3QgZXhlcmNpc2UuDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KIyBSdW4gdGhlIG9wdGltaXphdGlvbg0Kb3B0X3JlYmFsX3JiX3JvYnVzdCA8LSBvcHRpbWl6ZS5wb3J0Zm9saW8ucmViYWxhbmNpbmcoUiA9IGFzc2V0X3JldHVybnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9tZW50RlVOID0gIm1vbWVudHNfcm9idXN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvcnRmb2xpbyA9IHBvcnRfc3BlYywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpbWl6ZV9tZXRob2QgPSAicmFuZG9tIiwgcnAgPSBycCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWNlID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYmFsYW5jZV9vbiA9ICJxdWFydGVycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaW5pbmdfcGVyaW9kID0gNjAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb2xsaW5nX3dpbmRvdyA9IDYwKQ0KDQojIENoYXJ0IHRoZSB3ZWlnaHRzDQpjaGFydC5XZWlnaHRzKG9wdF9yZWJhbF9yYl9yb2J1c3QpDQoNCiMgQ2hhcnQgdGhlIHBlcmNlbnRhZ2UgY29udHJpYnV0aW9uIHRvIHJpc2sNCmNoYXJ0LlJpc2tCdWRnZXQob3B0X3JlYmFsX3JiX3JvYnVzdCwgbWF0Y2guY29sID0gIlN0ZERldiIsIHJpc2sudHlwZSA9ICJwZXJjZW50YWdlIikNCg0KIyBDb21wdXRlIHRoZSBwb3J0Zm9saW8gcmV0dXJucw0KcmV0dXJuc19yYl9yb2J1c3QgPC0gUmV0dXJuLnBvcnRmb2xpbyhSID0gYXNzZXRfcmV0dXJucywgd2VpZ2h0cyA9IGV4dHJhY3RXZWlnaHRzKG9wdF9yZWJhbF9yYl9yb2J1c3QpKQ0KY29sbmFtZXMocmV0dXJuc19yYl9yb2J1c3QpIDwtICJyYl9yb2J1c3QiDQpgYGANCiMjIyBBbmFseXplIHJlc3VsdHMgYW5kIGNvbXBhcmUgdG8gYmVuY2htYXJrDQoNCm4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlcyBvZiB0aGUgY2hhcHRlciwgd2UgY3JlYXRlZCBhbiBlcXVhbCB3ZWlnaHQgYmVuY2htYXJrIHJfYmVuY2htYXJrIGFuZCByYW4gdGhlIGZvbGxvd2luZyBvcHRpbWl6YXRpb25zOg0KDQotCU1pbmltaXplIHBvcnRmb2xpbyBzdGFuZGFyZCBkZXZpYXRpb24gd2l0aCBzYW1wbGUgZXN0aW1hdGVzIChyZXR1cm5zIHN0b3JlZCBpbiByZXR1cm5zX2Jhc2UpLg0KLQlNaW5pbWl6ZSBwb3J0Zm9saW8gc3RhbmRhcmQgZGV2aWF0aW9uIHdpdGggcGVyY2VudGFnZSBjb250cmlidXRpb24gdG8gcmlzayB1c2luZyBzYW1wbGUgZXN0aW1hdGVzIChyZXR1cm5zIHN0b3JlZCBpbiByZXR1cm5zX3JiKS4NCi0JTWluaW1pemUgcG9ydGZvbGlvIHN0YW5kYXJkIGRldmlhdGlvbiB3aXRoIHBlcmNlbnRhZ2UgY29udHJpYnV0aW9uIHRvIHJpc2sgdXNpbmcgcm9idXN0IGVzdGltYXRlcyAocmV0dXJucyBzdG9yZWQgaW4gcmV0dXJuc19yYl9yb2J1c3QpLg0KTm93IHdlIHdpc2ggdG8gYW5hbHl6ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgdGhlIG9wdGltaXphdGlvbiBiYWNrdGVzdHMgYW5kIGNvbXBhcmUgd2l0aCB0aGUgYmVuY2htYXJrLg0KDQpgYGB7cn0NCiMgQ29tYmluZSB0aGUgcmV0dXJucw0KcmV0IDwtIGNiaW5kKHJfYmVuY2htYXJrLCByZXR1cm5zX2Jhc2UsIHJldHVybnNfcmIsIHJldHVybnNfcmJfcm9idXN0KQ0KDQojIENvbXB1dGUgYW5udWFsaXplZCByZXR1cm5zDQp0YWJsZS5Bbm51YWxpemVkUmV0dXJucyhSID0gcmV0KQ0KDQojIENoYXJ0IHRoZSBwZXJmb3JtYW5jZSBzdW1tYXJ5DQpjaGFydHMuUGVyZm9ybWFuY2VTdW1tYXJ5KFIgPSByZXQpDQpgYGANCg0K