Executive Summary

      It is widely known the business of portfolio management extends from separating the wheat from the chaff, when it comes to identifying good investments, to the choice of finding the optimal combinations of those identified investments. Arguably, what makes a good portfolio manager is not how well they identify investment opportunities but rather how they assign the investments relative weights so that they make sense in a portfolio.
      To answer the question of which weighting scheme provides the best risk-adjusted return in a portfolio, a comparison of four(4) popular weighting schemes is explored where it is found the Black-Litterman Approach performs best for a hypothetical ETF portfolio among the Equal Weight, Markowitz, Black-Litterman, and Covariance-Robust optimization weighting schemes.

Assembling A Portfolio

      After identifying attractive securities for portfolio construction, it is typical for a portfolio manager to consider how these securities might fit in a portfolio. This part of the investment process often reduces to finding the “best” weights for the various securities in the portfolio. The exact portfolio allocation however, differs from one manager to the next with some preferring to use ad hoc approaches for determining portfolio weights and others relying on more structured risk-return analysis that often employs some form of optimization modelling - a methodology for selecting an optimal strategy given an objective and a set of constraints.
      In terms of ad hoc approaches, the most widely used is the equal weight approach where the amount invested in N candidate securities is 1/N of the available capital. For those who use optimization as a weighting method, the general idea is to determine the weights of securities in a portfolio that accomplish specific targets, usually in the form of desired risk and return combinations, in the face of a set of constraints and limitations faced by the portfolio manager. In both of these approaches, however, it is generally assumed investors take a single-period view of investing where the goal of portfolio allocation is to invest optimally over some single predetermined period of time.
      As a case study for this article, the goal is to construct a portfolio consisting of 9 ETFs: SPY(S&P 500), IWM(Russell 2000), FEZ(Euro Stoxx 50), ACWI (MSCI All-Country World), AGG(Barclays Aggregate US Bond), IAGG(iShares Core International Aggregate Bond), IYR(Dow Jones US Real Estate), REET(iShares Global REIT), and GSG(iShares GSCI Commodity Index); where it is explored how different allocation procedures affect portfolio performance. The data used is monthly price data gathered from the 2016-2021 period from Yahoo Finance, which is evaluated on the 2022 price data. The weighting schemes explored are
  1. Ad hoc:
    • Equal Weight
  2. Optimization:
    • Markowitz
    • Black-Litterman
    • Covariance-Robust
      To make the analysis realistic, an extended form of the above weighting schemes is explored where extended is taken to mean accounting for possible constraints imposed on the portfolio manager by either clients or regulators. The investing period is assumed to be a year where it is assumed the investor never rebalances their portfolio (i.e, does not change the allocation between securities). While the assumption of no-rebalancing might seem restricting, it is worth mentioning for such a short investment time frame, taxes resulting from transactions could have a substantial impact on portfolio performance.
      The rest of the article, however, is organized as follows:

Intuition Behind Optimization Methods

Markowitz(Mean-Variance Framework)

      The classic Markowitz Mean-Variance framework assumes investors make their allocation decisions based on both the expected return and risk arising from their investments. Of course, Markowitz defined risk as the variance of future returns which for a portfolio was consistent of two parts - the variance of the returns of individual assets as well as the covariances(equivalently, the correlations) between those returns. In this framework, investing all your money in assets that are strongly correlated is not a prudent strategy even if the individual assets appear to be the equivalent of “investment diamonds.” The reason is simple, if one asset performs worse than expected, it is likely the other assets will also perform poorly due to their high correlation.
      Using this framework of future asset returns and variance from N securities, the Markowitz Mean-Variance framework seeks to compare the various portfolios that could be built from the N securities where the portfolio with the lowest risk for a given level of expected return is said to be optimal.
      Of course, there are an infinite number of potential portfolios that can be constructed from the various risk and return combinations. Consequently, the problem is often reduced to using quadratic programming to find the minimum-risk portfolio without explicitly calculating every portfolio’s risk and return.

Black-Litterman Framework

      Before covering the Black-Litterman approach, it would be useful to note it is based on a Bayesian estimation approach, i.e subjective interpretations of future probabilities. For Bayesian approaches, a probability distribution known as the prior is used to represent the investor’s knowledge about the behavior of an asset’s returns before any data is observed. To complement this view, observations of the asset’s returns behavior are recorded to compute a new probability distribution known as the ‘posterior distribution’ of the asset’s returns future behavior.
      In this context, the Black-Litterman approach, uses the same framework of quadratic programming to find the minimum-risk portfolio with the difference being the expected returns and variance are generated from a combined view of observed data and the investor’s subjective view of the behavior of future returns. Of course the investor’s view can be expressed in either absolute terms - where the investor has a view on individual asset performance or relative terms - where the investor has a view on how an individual asset performs in comparison to another. This ability to incorporate subjective views into a portfolio expected return and variance is perhaps the most valuable part of the framework.
      In practice, the subjective views are often generated from a Vector Autoregressive model of order p, a basic econometric model to represent the returns time series of N assets.

Covariance-Robust Framework

      An important omission in the explanation of the Markowitz and Black Litterman Optimization frameworks is they do not account for the anomalies that may exist in the input data, i.e the portfolio returns and variance estimates. Of course, anomalies in this case is a reference to those points that do not necessarily ‘stick’ out in the fitted empirical mean, variance, and covariance estimates but have substantial influence on those estimates from the returns data set.
      The importance of detecting and dealing with anomalies cannot be underestimated because anomalies in the data can distort parameter estimates, with the effect in the case of finding the optimal risk-return combination for a portfolio, being the combinations more appropriately describe the anomalies.
      To see why this happens, an example would be useful. For example, outliers, which are not picked up in the sample mean and covariance estimates, pull the ordinary sample mean toward themselves, and artificially inflate the ordinary sample covariance matrix. If we knew a priori which points were anomalous, we could simply exclude them when estimating the parameters. But, this information is often unavailable.
      This idea of minimizing the influence of anomalies in estimating the mean and covariance matrix is what robust optimization seeks to explore. The Minimum Covariance Determinant (MCD), a popular robust optimization method, answers the question of estimating the right empirical mean and covariance by taking all possible subsets of the data, of a specified size, and estimates the mean and covariance matrix for each subset. It then keeps the estimates for the subset whose covariance matrix has the smallest determinant and scales them by a ‘consistency’ factor.
      The underlying idea here is since the determinant of the covariance matrix measures - roughly speaking - the spread of a returns distribution, minimizing it is akin to selecting a subset of the data that has the tightest distribution and consequently is less likely to be affected by outliers.

Portfolio Constraints

      In order to mimic a realistic portfolio with realistic taken to mean constraints imposed by either clients or regulators on a portfolio manager, additional constraints are made for the optimization problem. In this article, the additional constraints are
  1. Long-Only Constraints - where the short selling is not allowed in the portfolio.

  2. Holding constraints - where a single ETF can be at most 40% of the portfolio and at least 5% of the portfolio.

      While imposing limits on the maximum exposure on a single ETF is intuitive, it is worthwhile explaining the lower bound. The underlying reason is to allow a holding size small yet substantial enough that the position in the ETF can contribute to portfolio performance. A more detailed reason however is that assets, particularly stocks, are often traded in multiples of minimum transaction costs or rounds and consequently, very small positions cannot be realistically acquired.
# Library Imports - Package names
packages <- c("MASS", "xts", "quantmod", "PortfolioAnalytics",
              "ROI", "ROI.plugin.glpk", "ROI.plugin.quadprog", "tidyverse", 
              "highcharter", "PerformanceAnalytics")

# Install packages if not yet installed - Neat trick from Antoine Soetewey's blog
installed_packages <- packages %in% rownames(installed.packages())
if (any(installed_packages == FALSE)) {
  install.packages(packages[!installed_packages])
}

# Packages loading
suppressMessages(lapply(packages, library, character.only = TRUE))

#' import data for each symbol and convert to monthly prices
#' Note conversion to monthly automatically removes NA-VALUES
etf.symbols <- c("SPY", "IWM", "FEZ", "ACWI","AGG","IAGG", "IYR","REET","GSG")
adj.close <- 6  # 6th field is adjusted close
etf.prices <- getSymbols(etf.symbols[1], source="yahoo",
                         auto.assign=FALSE, return.class="xts")[,adj.close]
for (i in 2:length(etf.symbols)) {
  etf.tmp <- getSymbols(etf.symbols[i], source="yahoo",
                        auto.assign=FALSE, return.class="xts")[,adj.close]
  etf.prices <- cbind(etf.prices, etf.tmp)
}
colnames(etf.prices) <- etf.symbols

etf.prices<- suppressWarnings(to.monthly(etf.prices,
                        indexAt='lastof',
                        OHLC=FALSE))

# Calculate Returns and remove na value in first row
etf.rets.data <- Return.calculate(na.omit(etf.prices),method='log') %>%
                na.omit()

# Keep Returns from 2016-2021 for in-sample estimation
etf.rets<- etf.rets.data["2016/2021"]

# Keep Returns from 2022 for out-of-sample testing
etf.rets.test<- etf.rets.data["2022"]

###### Portfolio Weighting

#  Portfolio Object Specification - Set up portfolio with objective and constraints(Long Only)

#'  GMV = Global Minimum Variance  ,    EW  = Equally Weight
#'  RO  = Robust Optimization ,   BL  = Black Litterman

port.spec <- portfolio.spec(assets = colnames(etf.rets)) %>%
                add.objective( type="risk", name="StdDev") %>%
                add.objective( type="return", name="mean") %>%
                add.constraint(type="full_investment")  %>%
                add.constraint( type="long_only")  %>%
                add.constraint(type = "box", min=0.05, max=0.4)

port.spec.EW <- portfolio.spec(assets = colnames(etf.rets)) %>%
                  add.constraint(type="full_investment")  %>%
                  add.constraint( type="long_only")  %>%
                  add.constraint(type = "box", min=0.05, max=0.4)

# Set Portfolio Moments for Optimization Functions
samp.moments<-  function(R){
    out<-set.portfolio.moments(R = R, portfolio=port.spec, method= 'sample')
    return(out)
}


#' Views are generated from a VAR-2 model from the MTS package
#' R is an xts return object
bl.moments<- function(R){
  data<- fortify.zoo(R)[,-1]
  m1<- MTS::VAR(data,p=2,output = F) # From MTS package
  pred<- MTS::VARpred(m1,1,output = F) # 1 period ahead forecast
  pred<- pred$pred   
  n.assets<-ncol(R)
  pick<-diag(n.assets)
  out<- black.litterman(R= R,P = pick,Views = pred)
  out$mu<- out$BLMu
  out$sigma<- out$BLSigma
  return(out)
}

#' Find Optimal Weights
#' Rf= 0 
GMV.weights<- optimize.portfolio(R = etf.rets,portfolio= port.spec,maxSR=TRUE,
                         optimize_method = "ROI",trace = TRUE,momentFUN='samp.moments')
 print("Optimal Markowitz weights")
 print(sprintf("%s : %s", etf.symbols,
              scales::percent(as.numeric(GMV.weights$weights))))

#' The following function is obtained from Ross Bennett's (2018) Paper
#' Estimates Covariance Matrix
sigma.robust<- function(R){
  out<- list()
  set.seed(1234)
  out$sigma<- cov.rob(R,method='mcd',nsamp = 'best')$cov
  return(out)
}

RO.weights <-optimize.portfolio(R = etf.rets,portfolio= port.spec,maxSR=TRUE,
                                 optimize_method = "ROI",trace = TRUE,momentFUN='sigma.robust')

 print("Covariance-Robust Optimization Weights")
 print(sprintf("%s : %s", etf.symbols,
              scales::percent(as.numeric(RO.weights$weights))))


EW.weights <- equal.weight(etf.rets, portfolio= port.spec.EW)
 print("Equal weights")
 print(sprintf("%s : %s", etf.symbols,
              scales::percent(as.numeric(EW.weights$weights))))
 
#' The optimizer is not able to find a unique 'tangential' solution, so remove 
#' Max Sharpe ratio objective.
#' This 'loosened' optimization implementation is then focused on maximizing only portfolio
#' return subject to constraints on weights.
BL.weights<-invisible(optimize.portfolio(R = etf.rets,portfolio= port.spec,
                               optimize_method = "ROI",trace = TRUE,momentFUN='bl.moments'))
 print("Black-Litterman Weights")
 print(sprintf("%s : %s", etf.symbols,
              scales::percent(as.numeric(BL.weights$weights))))

Portfolio Weights

      Applying the various weighting schemes, a bar plot of portfolio weights is produced along with a table containing the same information in detail. Broadly speaking, it can be seen except for the Black-Litterman approach, the optimization approaches are concentrated in the bond ETFs which one can argue heuristically is a reasonable allocation given the market turbulence of 2020 and 2021. The Black-Litterman approach however, appears to be concentrated in the Commodity ETF.
# Create List of portfolio objects and display their weights in a barplot
port.list <- list(GMV.weights,EW.weights, RO.weights, BL.weights)
names(port.list) <- c("Markowitz","Equal-Weight", 'Covariance Robust Optimization', 
                      'Black-Litterman')
chart.Weights(combine.optimizations(port.list), plot.type = "barplot", 
              main="Weights stacked by Asset (Long-only)")

# Put Weights in a data frame(Scale by 100 to get it in percentages)
weights<- cbind(GMV.weights$weights,EW.weights$weights,
                RO.weights$weights, BL.weights$weights)
weights<- round(weights,4)*100
colnames(weights)<- names(port.list)

knitr::kable(weights,caption= "Table 1 - Weights in (%)")
Table 1 - Weights in (%)
Markowitz Equal-Weight Covariance Robust Optimization Black-Litterman
SPY 22.11 11.11 13.36 5
IWM 5.00 11.11 10.92 25
FEZ 5.00 11.11 5.00 5
ACWI 5.00 11.11 5.00 5
AGG 40.00 11.11 10.73 5
IAGG 7.89 11.11 40.00 5
IYR 5.00 11.11 5.00 5
REET 5.00 11.11 5.00 5
GSG 5.00 11.11 5.00 40

Overview of Portfolio Performance Metrics

      Of course, it would be pointless to construct portfolios from the various weighting schemes without assessing how each scheme affected the portfolio’s performance. To this end, a brief summary of popular performance metrics is presented to assess the value of each weighting scheme.

Annualized Return: Geometric average of annual returns generated by a portfolio over a given investment period.

Active Return: Excess portfolio returns over some specified benchmark. Note how it differs from excess return.

Excess Return: Excess portfolio returns over some specified risk-free asset.

Annualized Standard Deviation: Geometric average of variability of a portfolio’s annual returns over a given investment period. The higher the standard deviation of returns, the riskier the portfolio weighting scheme is.

Beta(CAPM): Reflects risk of a portfolio in relation to some benchmark. Statistically, it is the regression coefficient reflecting how much a portfolio’s returns change for each unit change in the benchmark’s returns. Higher betas mean the portfolio’s returns are more sensitive to the benchmark’s returns.

Sharpe Ratio: Portfolio excess returns per unit of risk. Excess returns here is the excess portfolio returns over some specified risk-free asset while risk is the portfolio standard deviation. In general, higher Sharpe ratios mean the portfolio provides greater returns over the risk free asset while offering the same or less variability of returns. Higher Sharpe ratios are thus usually better.

Treynor Ratio: Portfolio excess returns per unit of beta/benchmark risk. Like the Sharpe, excess returns here is the excess portfolio returns over some specified risk-free asset. In general, higher Treynor ratios mean the portfolio provides greater returns over the risk free asset while offering less exposure to the benchmark. Higher Treynor ratios are thus usually better.

Modigliani \(M^2\): Adjusts each portfolio’s returns to what it would have been had the portfolio manager taken the same amount of risk as the benchmark. In general, higher Modigliani \(M^2\) measures mean the portfolio provides greater returns while taking the same amount risk as the benchmark where risk is the benchmark’s standard deviation. Higher Modigliani \(M^2\) measures are thus usually better.

Information Ratio: Ratio of excess portfolio returns over the benchmark to the standard of the excess portfolio returns. In general, higher information ratios mean the portfolio provides greater excess returns over the benchmark per unit of risk. Higher information ratios are thus usually better.

Tracking Error: Standard Deviation of excess portfolio returns over the benchmark. The greater the tracking error, the more the portfolio returns deviate from the benchmark.

Diversification Ratio: Ratio of the weighted average of volatility individual assets to the overall portfolio volatility. Here, weights are the portfolio weights. For a well diversified long-only portfolio, the ratio should be greater than 1.

Concentration Ratio:

      Ratio of the weighted sum of squared individual asset volatility to weighted sum of individual asset volatility squared. While confusing, the idea for the numerator is to square the individual weighted volatilities before you sum, where weights are the portfolio weights.
      For the denominator, you take the weighted sum of individual asset volatility before you take the square. For a well diversified, long-only portfolio, the ratio should be less than 1. Additionally, portfolio allocations with higher concentration ratios are judged to be more concentrated than their weighting counterparts. Put simply, a lower concentration ratio is usually better.

Customizing the Benchmark:

      Since most investments establish a benchmark to evaluate a portfolio manager’s performance, a customized index is created for this purpose. The customized index is created to better reflect the investor’s objective which for this article is a desired exposure to Index ETFs.
      Of course, this investor is assumed to desire exposure to indices that track stocks, bonds, commodities, and real estate. To make the analysis tractable, a price-weighted index is constructed which averages equally the value of each Index ETF in the portfolio.

Performance Evaluation

      Table 2 shows the historical performance of each portfolio weighting scheme. It can be seen - generally speaking - the Black-Litterman approach performs best for the investment period. In particular, it produced better active and excess returns, higher sharpe, treynor and information ratios among others. It is worth noting while the Black-Litterman portfolio was well diversified, i.e in terms of its diversification ratio, it had the most concentrated portfolio in comparison to the other ETF portfolios.(from its concentration ratio)
      To show what this performance means for the investor, a line graph is presented showing the growth of $10,000 in each portfolio weighting scheme.
weights <- cbind(GMV.weights$weights,EW.weights$weights,
                 RO.weights$weights,BL.weights$weights)
# rets = returns
rets<-c()

#' Calculate Returns on Out-of-Sample Data - With Wealth Progression
#' Combine returns of each portfolio in a single data frame
for (i in 1:ncol(weights)) {
  rets.values <- Return.portfolio(etf.rets.test, weights  = weights[,i],
                                  wealth.index = TRUE)
  rets<-cbind(rets,rets.values)
}

#' Combine Returns into a single data frame
amount<- 10000
rets.scale<- rets*amount
date <- as.Date("2021-12-31")
init.investment <- xts(matrix(amount, nrow=1,ncol=4), order.by=date)
rets.scale<- rbind(init.investment, rets.scale)
names(rets.scale) <- c("Markowitz","Equal-Weight","Covariance-Robust Optimization",
                          "Black Litterman")

#' Performance - Cumulative Returns Chart
cum.rets.chart<-highchart(type='stock')%>%
  hc_title(text='Hypothetical Growth of $10,000 ') %>%
  hc_add_series(round(rets.scale[,1],2), 
                name=colnames(rets.scale)[1])%>%
  hc_add_series(round(rets.scale[,2],2), 
                name=colnames(rets.scale)[2])%>%
  hc_add_series(round(rets.scale[,3],2), 
                name=colnames(rets.scale)[3])%>%
  hc_add_series(round(rets.scale[,4],2), 
                name=colnames(rets.scale)[4])%>%
  hc_navigator(enabled=FALSE)%>%
  hc_scrollbar(enabled=FALSE) %>%
  hc_add_theme(hc_theme_flat())%>%
  hc_exporting(enabled=TRUE)%>%
  hc_legend(enabled=TRUE) %>% 
  hc_plotOptions(series = list(animation = FALSE))
cum.rets.chart

Cumulated Portoflio Returns

key.measures<- function( Ra, port.weight, bm, scale = 12,Sigma = cov(etf.rets), Rf= 0){
  ret<-     Return.annualized(Ra,scale = scale) *100
  act.ret<-  ActiveReturn(Ra ,Rb= bm,scale = scale) *100
  ex.ret<-   sum(Return.excess(Ra))*100   # Rf= 0 
  #' Note ret = ex.ret since Rf=0
  #' That is, total returns should be roughly equal to excess returns
  #' Where total returns = annualized returns if the investment period < or = 1 year
  
  st.dev<-  StdDev.annualized(Ra,weights=port.weight,scale = scale) *100
  beta<- CAPM.beta(Ra,bm)
  sharpe<-  SharpeRatio.annualized(Ra, Rf= Rf, scale = scale)
  treynor<- TreynorRatio(Ra, Rb = bm,scale=scale)
  i.ratio<- InformationRatio(Ra, Rb = bm,scale=scale)
  m.square<- Modigliani(Ra, Rb = bm,scale=scale)
  t.error<- TrackingError(Ra, Rb = bm,scale = scale)
  dr <-     FRAPO::dr(port.weight, Sigma ) # Function obtained from FRAPO package
  cr <-     FRAPO::cr(port.weight, Sigma)  # Function obtained from FRAPO package
  res<-     round(rbind(ret,act.ret,ex.ret, st.dev,beta,sharpe, treynor,m.square,i.ratio, t.error, dr,cr),3)

  rownames(res) <- c("Annualized Return(%)","Active Return(%)","Excess Return(%)",
                     'Annualized Standard Deviation(%)',"Beta",'Sharpe','Treynor',
                  "M^2 of Modigliani", "Information Ratio","Tracking Error",
                  "Diversification Ratio","Concentration Ratio")
  return(res)
}

#'   Note the portfolio by construction is invested in the overall market.
#'   The Benchmark used here is a Price-Weighted index.
#'   Price- Weighted indices by construction average prices equally.
#'   comp.ind = composite index returns

#'   The motivation for this multi-asset composite index is the
#'   FTSE Multi-Asset Composite Index Series which 
#'   according to FTSE Russell
#'   is designed to measure cross-asset market returns for a range of risk exposures.

etf.prices$comp<-rowMeans(etf.prices)
#' Calculate Returns and remove na value in first row
#' Select Returns from 2022 
comp.ind <- Return.calculate(etf.prices$comp,method='log') %>%
            na.omit()
comp.ind<- comp.ind['2022']

# rets = returns
rets<-c()

#' Calculate Returns on Out-of-Sample Data - Without Wealth Progression
#' Combine returns of each portfolio in a single data frame
for (i in 1:ncol(weights)) {
  rets.values <- Return.portfolio(etf.rets.test, weights  = weights[,i])
  rets<-cbind(rets,rets.values)
}

# ports = portfolios
ports<-c()
# Combine performance measures of each portfolio in a single data frame
for (i in 1:ncol(weights)) {
  measures <- key.measures(port.weight = weights[,i], 
                 Ra = rets[,i], bm =  comp.ind)
  ports<-cbind(ports,measures)
}

#'  Note for the benchmark, all we need are the annualized
#'  Returns, StDev, and Excess Returns
#'  Since the benchmark is price-weighted, the weights used in the function is the Equal Weight
bm <- key.measures(port.weight = EW.weights$weights, 
                  Ra = comp.ind, bm =  comp.ind)

keep.rows<- c("Annualized Return(%)","Excess Return(%)",
         'Annualized Standard Deviation(%)')
# Assign NA to those values not in keep.rows
bm[!(bm %in% bm[keep.rows,]),]<- NA  
all.ports <- cbind(ports,bm)


colnames(all.ports) <- append(names(rets.scale),"Benchmark")
knitr::kable(all.ports,
             caption="Table 2 - Key Portfolio Performance Measures")
Table 2 - Key Portfolio Performance Measures
Markowitz Equal-Weight Covariance-Robust Optimization Black Litterman Benchmark
Annualized Return(%) -12.817 -12.955 -12.087 -3.541 -16.231
Active Return(%) 3.414 3.275 4.143 12.690 NA
Excess Return(%) -12.776 -12.515 -12.046 -2.342 -15.766
Annualized Standard Deviation(%) 13.645 16.604 12.901 16.405 19.814
Beta 0.675 0.823 0.645 0.669 NA
Sharpe -0.939 -0.780 -0.937 -0.216 NA
Treynor -0.190 -0.157 -0.187 -0.053 NA
M^2 of Modigliani -0.015 -0.012 -0.015 -0.002 NA
Information Ratio 0.489 0.699 0.570 1.087 NA
Tracking Error 0.070 0.047 0.073 0.117 NA
Diversification Ratio 1.282 1.189 1.282 1.145 NA
Concentration Ratio 0.163 0.135 0.138 0.336 NA

Risk Decomposition

      As a conclusion to this article, it would be useful to examine how exposures to individual ETFs affect the variability portfolio returns for each weighting scheme. This breakdown of portfolio risk into contributions by individual ETFs when combined with the portfolio weights shows whether historically the portfolio has been over or underexposed to specific ETFs.
      Should our hypothetical investor desire to re-allocate their capital among the ETFs for a given weighting scheme, they can use this risk decomposition information to increase or decrease portfolio allocation to specific ETFs. For example, while the SPY makes up 22% of the Markowitz ETF portfolio, it accounts for 35% of the portfolio return volatility. Our investor might consider reducing exposure to the SPY on this basis.
      Table 3 shows the risk decomposition of each portfolio weighting scheme.
##### Component Contribution to Portfolio Volatility/ Standard Deviation

#   What percentage of portfolio St.Dev did each position account for?

all.comps.sd<-c()
# Combine component contributions of each portfolio in a single data frame
for (i in 1:ncol(weights)) {
  # Scale by 100 to get percent from decimals like say 0.2 to 20%
  Std <- (StdDev(etf.rets.test, weights = weights[,i], portfolio_method=  
                  "component")$pct_contrib_StdDev)*100
  all.comps.sd<-cbind(all.comps.sd,Std)
}

colnames(all.comps.sd) <- names(rets.scale)
knitr::kable(all.comps.sd,
             caption ="Table 3 - Component Contribution to Portfolio Volatility/ Standard Deviation(%)")
Table 3 - Component Contribution to Portfolio Volatility/ Standard Deviation(%)
Markowitz Equal-Weight Covariance-Robust Optimization Black Litterman
SPY 34.394427 14.230269 22.081628 5.827865
IWM 7.918145 14.507161 18.708315 30.750616
FEZ 9.392703 16.798717 9.741107 6.280780
ACWI 7.506255 13.570762 7.877084 5.389567
AGG 20.240645 4.039012 5.381124 1.197094
IAGG 3.379680 3.653431 17.929786 1.244528
IYR 7.518222 13.891722 7.936467 5.348458
REET 7.618834 14.050253 8.049772 5.618056
GSG 2.031089 5.258672 2.294717 38.343036

Resources Used

Bacon, Carl R. Practical Risk-Adjusted Performance Measurement. Second edition. Wiley Finance Series. Hoboken, NJ: Wiley, 2022.

Fabozzi, Frank J., and Dessislava A. Pachamanova. Portfolio Construction and Analytics. Frank J. Fabozzi Series. Hoboken, New Jersey: John Wiley & Sons, Inc, 2016.

Black F, Litterman R (1992) Global portfolio optimization. Financial Analysts J. 48(5):28–43.

Rosenthal, Dale W. R. A Quantitative Primer on Investments with r. Chicago, IL: Q36 LLC, 2018.

Harry Markowitz, 1952. “Portfolio Selection,” Journal of Finance, American Finance Association, vol. 7(1), pages 77-91, March.

Bennett Ross, 2018. “Custom Moment and Objective Functions”

Ali Pardhan, “Intuitive Explanation of Minimum Covariance Determinant (MCD).” Cross Validated, July 5, 2020. https://stats.stackexchange.com/questions/475636/intuitive-explanation-of-minimum-covariance-determinant-mcd.

Brian G. Peterson and Peter Carl (2018). PortfolioAnalytics: Portfolio Analysis, Including Numerical Methods for Optimization of Portfolios. R package version 1.1.0. https://CRAN.R-project.org/package=PortfolioAnalytics

Brian G. Peterson and Peter Carl (2020). PerformanceAnalytics: Econometric Tools for Performance and Risk Analysis. R package version 2.0.4. https://CRAN.R-project.org/package=PerformanceAnalytics

LS0tCnRpdGxlOiAiUG9ydGZvbGlvIENvbnN0cnVjdGlvbiBhbmQgQW5hbHl0aWNzIgphdXRob3I6IEtvYmVuYSBBbW9haApkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgICAgdG9jOiB0cnVlCiAgICAgIHRvY19kZXB0aDogMgogICAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgICBrZWVwX21kOiB0cnVlIAphbHdheXNfYWxsb3dfaHRtbDogeWVzCi0tLQojIyBFeGVjdXRpdmUgU3VtbWFyeQoKfCAgICAgICBJdCBpcyB3aWRlbHkga25vd24gdGhlIGJ1c2luZXNzIG9mIHBvcnRmb2xpbyBtYW5hZ2VtZW50IGV4dGVuZHMgZnJvbSBzZXBhcmF0aW5nIHRoZSB3aGVhdCBmcm9tIHRoZSBjaGFmZiwgd2hlbiBpdCBjb21lcyB0byBpZGVudGlmeWluZyBnb29kIGludmVzdG1lbnRzLCB0byB0aGUgY2hvaWNlIG9mIGZpbmRpbmcgdGhlIG9wdGltYWwgY29tYmluYXRpb25zIG9mIHRob3NlIGlkZW50aWZpZWQgaW52ZXN0bWVudHMuIEFyZ3VhYmx5LCB3aGF0IG1ha2VzIGEgZ29vZCBwb3J0Zm9saW8gbWFuYWdlciBpcyBub3QgaG93IHdlbGwgdGhleSBpZGVudGlmeSBpbnZlc3RtZW50IG9wcG9ydHVuaXRpZXMgYnV0IHJhdGhlciBob3cgdGhleSBhc3NpZ24gdGhlIGludmVzdG1lbnRzIHJlbGF0aXZlIHdlaWdodHMgc28gdGhhdCB0aGV5IG1ha2Ugc2Vuc2UgaW4gYSBwb3J0Zm9saW8uCnwgICAgICAgVG8gYW5zd2VyIHRoZSBxdWVzdGlvbiBvZiB3aGljaCB3ZWlnaHRpbmcgc2NoZW1lIHByb3ZpZGVzIHRoZSBiZXN0IHJpc2stYWRqdXN0ZWQgcmV0dXJuIGluIGEgcG9ydGZvbGlvLCBhIGNvbXBhcmlzb24gb2YgZm91cig0KSBwb3B1bGFyIHdlaWdodGluZyBzY2hlbWVzIGlzIGV4cGxvcmVkIHdoZXJlIGl0IGlzIGZvdW5kIHRoZSBCbGFjay1MaXR0ZXJtYW4gQXBwcm9hY2ggcGVyZm9ybXMgYmVzdCBmb3IgYSBoeXBvdGhldGljYWwgRVRGIHBvcnRmb2xpbyBhbW9uZyB0aGUgRXF1YWwgV2VpZ2h0LCBNYXJrb3dpdHosIEJsYWNrLUxpdHRlcm1hbiwgYW5kIENvdmFyaWFuY2UtUm9idXN0IG9wdGltaXphdGlvbiB3ZWlnaHRpbmcgc2NoZW1lcy4KCiMjIEFzc2VtYmxpbmcgQSBQb3J0Zm9saW8KfCAgICAgICBBZnRlciBpZGVudGlmeWluZyBhdHRyYWN0aXZlIHNlY3VyaXRpZXMgZm9yIHBvcnRmb2xpbyBjb25zdHJ1Y3Rpb24sIGl0IGlzIHR5cGljYWwgZm9yIGEgcG9ydGZvbGlvIG1hbmFnZXIgdG8gY29uc2lkZXIgaG93IHRoZXNlIHNlY3VyaXRpZXMgbWlnaHQgZml0IGluIGEgcG9ydGZvbGlvLiBUaGlzIHBhcnQgb2YgdGhlIGludmVzdG1lbnQgcHJvY2VzcyBvZnRlbiByZWR1Y2VzIHRvIGZpbmRpbmcgdGhlICJiZXN0IiB3ZWlnaHRzIGZvciB0aGUgdmFyaW91cyBzZWN1cml0aWVzIGluIHRoZSBwb3J0Zm9saW8uIFRoZSBleGFjdCBwb3J0Zm9saW8gYWxsb2NhdGlvbiBob3dldmVyLCBkaWZmZXJzIGZyb20gb25lIG1hbmFnZXIgdG8gdGhlIG5leHQgd2l0aCBzb21lIHByZWZlcnJpbmcgdG8gdXNlIGFkIGhvYyBhcHByb2FjaGVzIGZvciBkZXRlcm1pbmluZyBwb3J0Zm9saW8gd2VpZ2h0cyBhbmQgb3RoZXJzIHJlbHlpbmcgb24gbW9yZSBzdHJ1Y3R1cmVkIHJpc2stcmV0dXJuIGFuYWx5c2lzIHRoYXQgb2Z0ZW4gZW1wbG95cyBzb21lIGZvcm0gb2Ygb3B0aW1pemF0aW9uIG1vZGVsbGluZyAtIGEgbWV0aG9kb2xvZ3kgZm9yIHNlbGVjdGluZyBhbiBvcHRpbWFsIHN0cmF0ZWd5IGdpdmVuIGFuIG9iamVjdGl2ZSBhbmQgYSBzZXQgb2YgY29uc3RyYWludHMuIAp8ICAgICAgIEluIHRlcm1zIG9mIGFkIGhvYyBhcHByb2FjaGVzLCB0aGUgbW9zdCB3aWRlbHkgdXNlZCBpcyB0aGUgZXF1YWwgd2VpZ2h0IGFwcHJvYWNoIHdoZXJlIHRoZSBhbW91bnQgaW52ZXN0ZWQgaW4gTiBjYW5kaWRhdGUgc2VjdXJpdGllcyBpcyAxL04gb2YgdGhlIGF2YWlsYWJsZSBjYXBpdGFsLiBGb3IgdGhvc2Ugd2hvIHVzZSBvcHRpbWl6YXRpb24gYXMgYSB3ZWlnaHRpbmcgbWV0aG9kLCB0aGUgZ2VuZXJhbCBpZGVhIGlzIHRvIGRldGVybWluZSB0aGUgd2VpZ2h0cyBvZiBzZWN1cml0aWVzIGluIGEgcG9ydGZvbGlvIHRoYXQgYWNjb21wbGlzaCBzcGVjaWZpYyB0YXJnZXRzLCB1c3VhbGx5IGluIHRoZSBmb3JtIG9mIGRlc2lyZWQgcmlzayBhbmQgcmV0dXJuIGNvbWJpbmF0aW9ucywgaW4gdGhlIGZhY2Ugb2YgYSBzZXQgb2YgY29uc3RyYWludHMgYW5kIGxpbWl0YXRpb25zIGZhY2VkIGJ5IHRoZSBwb3J0Zm9saW8gbWFuYWdlci4gSW4gYm90aCBvZiB0aGVzZSBhcHByb2FjaGVzLCBob3dldmVyLCBpdCBpcyBnZW5lcmFsbHkgYXNzdW1lZCBpbnZlc3RvcnMgdGFrZSBhIHNpbmdsZS1wZXJpb2QgdmlldyBvZiBpbnZlc3Rpbmcgd2hlcmUgdGhlIGdvYWwgb2YgcG9ydGZvbGlvIGFsbG9jYXRpb24gaXMgdG8gaW52ZXN0IG9wdGltYWxseSBvdmVyIHNvbWUgc2luZ2xlIHByZWRldGVybWluZWQgcGVyaW9kIG9mIHRpbWUuIAp8ICAgICAgIEFzIGEgY2FzZSBzdHVkeSBmb3IgdGhpcyBhcnRpY2xlLCB0aGUgZ29hbCBpcyB0byBjb25zdHJ1Y3QgYSBwb3J0Zm9saW8gY29uc2lzdGluZyBvZiA5IEVURnM6IFNQWShTJlAgNTAwKSwgSVdNKFJ1c3NlbGwgMjAwMCksIEZFWihFdXJvIFN0b3h4IDUwKSwgQUNXSSAoTVNDSSBBbGwtQ291bnRyeSBXb3JsZCksIEFHRyhCYXJjbGF5cyBBZ2dyZWdhdGUgVVMgQm9uZCksIElBR0coaVNoYXJlcyBDb3JlICBJbnRlcm5hdGlvbmFsIEFnZ3JlZ2F0ZSBCb25kKSwgSVlSKERvdyBKb25lcyBVUyBSZWFsIEVzdGF0ZSksIFJFRVQoaVNoYXJlcyBHbG9iYWwgUkVJVCksIGFuZCBHU0coaVNoYXJlcyBHU0NJIENvbW1vZGl0eSBJbmRleCk7IHdoZXJlIGl0IGlzIGV4cGxvcmVkIGhvdyBkaWZmZXJlbnQgYWxsb2NhdGlvbiBwcm9jZWR1cmVzIGFmZmVjdCBwb3J0Zm9saW8gcGVyZm9ybWFuY2UuIFRoZSBkYXRhIHVzZWQgaXMgbW9udGhseSBwcmljZSBkYXRhIGdhdGhlcmVkIGZyb20gdGhlIDIwMTYtMjAyMSBwZXJpb2QgZnJvbSBZYWhvbyBGaW5hbmNlLCB3aGljaCBpcyBldmFsdWF0ZWQgb24gdGhlIDIwMjIgcHJpY2UgZGF0YS4gVGhlIHdlaWdodGluZyBzY2hlbWVzIGV4cGxvcmVkIGFyZQoKYSkgQWQgaG9jOgogICAtIEVxdWFsIFdlaWdodAogICAKYikgT3B0aW1pemF0aW9uOiAKICAgLSBNYXJrb3dpdHoKICAgLSBCbGFjay1MaXR0ZXJtYW4KICAgLSBDb3ZhcmlhbmNlLVJvYnVzdAogICAKfCAgICAgICBUbyBtYWtlIHRoZSBhbmFseXNpcyByZWFsaXN0aWMsIGFuIGV4dGVuZGVkIGZvcm0gb2YgdGhlIGFib3ZlIHdlaWdodGluZyBzY2hlbWVzIGlzIGV4cGxvcmVkIHdoZXJlIGV4dGVuZGVkIGlzIHRha2VuIHRvIG1lYW4gYWNjb3VudGluZyBmb3IgcG9zc2libGUgY29uc3RyYWludHMgaW1wb3NlZCBvbiB0aGUgcG9ydGZvbGlvIG1hbmFnZXIgYnkgZWl0aGVyIGNsaWVudHMgb3IgcmVndWxhdG9ycy4gVGhlIGludmVzdGluZyBwZXJpb2QgaXMgYXNzdW1lZCB0byBiZSBhIHllYXIgd2hlcmUgaXQgaXMgYXNzdW1lZCB0aGUgaW52ZXN0b3IgbmV2ZXIgcmViYWxhbmNlcyB0aGVpciBwb3J0Zm9saW8gKGkuZSwgZG9lcyBub3QgY2hhbmdlIHRoZSBhbGxvY2F0aW9uIGJldHdlZW4gc2VjdXJpdGllcykuIFdoaWxlIHRoZSBhc3N1bXB0aW9uIG9mIG5vLXJlYmFsYW5jaW5nIG1pZ2h0IHNlZW0gcmVzdHJpY3RpbmcsIGl0IGlzIHdvcnRoIG1lbnRpb25pbmcgZm9yIHN1Y2ggYSBzaG9ydCBpbnZlc3RtZW50IHRpbWUgZnJhbWUsIHRheGVzIHJlc3VsdGluZyBmcm9tIHRyYW5zYWN0aW9ucyBjb3VsZCBoYXZlIGEgc3Vic3RhbnRpYWwgaW1wYWN0IG9uIHBvcnRmb2xpbyBwZXJmb3JtYW5jZS4gCnwgICAgICAgVGhlIHJlc3Qgb2YgdGhlIGFydGljbGUsIGhvd2V2ZXIsIGlzIG9yZ2FuaXplZCBhcyBmb2xsb3dzOgotIEludHVpdGlvbiBCZWhpbmQgT3B0aW1pemF0aW9uIE1ldGhvZHMKLSBQb3J0Zm9saW8gQ29uc3RyYWludHMKLSBQb3J0Zm9saW8gV2VpZ2h0cwotIE92ZXJ2aWV3IG9mIFBvcnRmb2xpbyBQZXJmb3JtYW5jZSBNZXRyaWNzCi0gUGVyZm9ybWFuY2UgRXZhbHVhdGlvbgotIFJpc2sgRGVjb21wb3NpdGlvbgogCiMjIEludHVpdGlvbiBCZWhpbmQgT3B0aW1pemF0aW9uIE1ldGhvZHMKCioqTWFya293aXR6KE1lYW4tVmFyaWFuY2UgRnJhbWV3b3JrKSoqCgp8ICAgICAgIFRoZSBjbGFzc2ljIE1hcmtvd2l0eiBNZWFuLVZhcmlhbmNlIGZyYW1ld29yayBhc3N1bWVzIGludmVzdG9ycyBtYWtlIHRoZWlyIGFsbG9jYXRpb24gZGVjaXNpb25zIGJhc2VkIG9uIGJvdGggdGhlIGV4cGVjdGVkIHJldHVybiBhbmQgcmlzayBhcmlzaW5nIGZyb20gdGhlaXIgaW52ZXN0bWVudHMuIE9mIGNvdXJzZSwgTWFya293aXR6IGRlZmluZWQgcmlzayBhcyB0aGUgdmFyaWFuY2Ugb2YgZnV0dXJlIHJldHVybnMgd2hpY2ggZm9yIGEgcG9ydGZvbGlvIHdhcyBjb25zaXN0ZW50IG9mIHR3byBwYXJ0cyAtIHRoZSB2YXJpYW5jZSBvZiB0aGUgcmV0dXJucyBvZiBpbmRpdmlkdWFsIGFzc2V0cyBhcyB3ZWxsIGFzIHRoZSBjb3ZhcmlhbmNlcyhlcXVpdmFsZW50bHksIHRoZSBjb3JyZWxhdGlvbnMpIGJldHdlZW4gdGhvc2UgcmV0dXJucy4gSW4gdGhpcyBmcmFtZXdvcmssIGludmVzdGluZyBhbGwgeW91ciBtb25leSBpbiBhc3NldHMgdGhhdCBhcmUgc3Ryb25nbHkgY29ycmVsYXRlZCBpcyBub3QgYSBwcnVkZW50IHN0cmF0ZWd5IGV2ZW4gaWYgdGhlIGluZGl2aWR1YWwgYXNzZXRzIGFwcGVhciB0byBiZSB0aGUgZXF1aXZhbGVudCBvZiAiaW52ZXN0bWVudCBkaWFtb25kcy4iIFRoZSByZWFzb24gaXMgc2ltcGxlLCBpZiBvbmUgYXNzZXQgcGVyZm9ybXMgd29yc2UgdGhhbiBleHBlY3RlZCwgaXQgaXMgbGlrZWx5IHRoZSBvdGhlciBhc3NldHMgd2lsbCBhbHNvIHBlcmZvcm0gcG9vcmx5IGR1ZSB0byB0aGVpciBoaWdoIGNvcnJlbGF0aW9uLgp8ICAgICAgIFVzaW5nIHRoaXMgZnJhbWV3b3JrIG9mIGZ1dHVyZSBhc3NldCByZXR1cm5zIGFuZCB2YXJpYW5jZSBmcm9tIE4gc2VjdXJpdGllcywgdGhlICBNYXJrb3dpdHogTWVhbi1WYXJpYW5jZSBmcmFtZXdvcmsgc2Vla3MgdG8gY29tcGFyZSB0aGUgdmFyaW91cyBwb3J0Zm9saW9zIHRoYXQgY291bGQgYmUgYnVpbHQgZnJvbSB0aGUgTiBzZWN1cml0aWVzIHdoZXJlIHRoZSBwb3J0Zm9saW8gd2l0aCB0aGUgbG93ZXN0IHJpc2sgZm9yIGEgZ2l2ZW4gbGV2ZWwgb2YgZXhwZWN0ZWQgcmV0dXJuIGlzIHNhaWQgdG8gYmUgb3B0aW1hbC4KfCAgICAgICBPZiBjb3Vyc2UsIHRoZXJlIGFyZSBhbiBpbmZpbml0ZSBudW1iZXIgb2YgcG90ZW50aWFsIHBvcnRmb2xpb3MgdGhhdCBjYW4gYmUgY29uc3RydWN0ZWQgZnJvbSB0aGUgdmFyaW91cyByaXNrIGFuZCByZXR1cm4gY29tYmluYXRpb25zLiBDb25zZXF1ZW50bHksIHRoZSBwcm9ibGVtIGlzIG9mdGVuIHJlZHVjZWQgdG8gdXNpbmcgcXVhZHJhdGljIHByb2dyYW1taW5nIHRvIGZpbmQgdGhlIG1pbmltdW0tcmlzayBwb3J0Zm9saW8gd2l0aG91dCBleHBsaWNpdGx5IGNhbGN1bGF0aW5nIGV2ZXJ5IHBvcnRmb2xpbydzIHJpc2sgYW5kIHJldHVybi4KICAKKipCbGFjay1MaXR0ZXJtYW4gRnJhbWV3b3JrKioKCnwgICAgICAgQmVmb3JlIGNvdmVyaW5nIHRoZSBCbGFjay1MaXR0ZXJtYW4gYXBwcm9hY2gsIGl0IHdvdWxkIGJlIHVzZWZ1bCB0byBub3RlIGl0IGlzIGJhc2VkIG9uIGEgQmF5ZXNpYW4gZXN0aW1hdGlvbiBhcHByb2FjaCwgaS5lIHN1YmplY3RpdmUgaW50ZXJwcmV0YXRpb25zIG9mIGZ1dHVyZSBwcm9iYWJpbGl0aWVzLiBGb3IgQmF5ZXNpYW4gYXBwcm9hY2hlcywgYSBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb24ga25vd24gYXMgdGhlIHByaW9yIGlzIHVzZWQgdG8gcmVwcmVzZW50IHRoZSBpbnZlc3RvcidzIGtub3dsZWRnZSBhYm91dCB0aGUgYmVoYXZpb3Igb2YgYW4gYXNzZXQncyByZXR1cm5zIGJlZm9yZSBhbnkgZGF0YSBpcyBvYnNlcnZlZC4gVG8gY29tcGxlbWVudCB0aGlzIHZpZXcsIG9ic2VydmF0aW9ucyBvZiB0aGUgYXNzZXQncyByZXR1cm5zIGJlaGF2aW9yIGFyZSByZWNvcmRlZCB0byBjb21wdXRlIGEgbmV3IHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBrbm93biBhcyB0aGUgJ3Bvc3RlcmlvciBkaXN0cmlidXRpb24nIG9mIHRoZSBhc3NldCdzIHJldHVybnMgZnV0dXJlIGJlaGF2aW9yLgp8ICAgICAgIEluIHRoaXMgY29udGV4dCwgdGhlIEJsYWNrLUxpdHRlcm1hbiBhcHByb2FjaCwgdXNlcyB0aGUgc2FtZSBmcmFtZXdvcmsgb2YgcXVhZHJhdGljIHByb2dyYW1taW5nIHRvIGZpbmQgdGhlIG1pbmltdW0tcmlzayBwb3J0Zm9saW8gd2l0aCB0aGUgZGlmZmVyZW5jZSBiZWluZyB0aGUgZXhwZWN0ZWQgcmV0dXJucyBhbmQgdmFyaWFuY2UgYXJlIGdlbmVyYXRlZCBmcm9tIGEgY29tYmluZWQgdmlldyBvZiBvYnNlcnZlZCBkYXRhIGFuZCB0aGUgaW52ZXN0b3IncyBzdWJqZWN0aXZlIHZpZXcgb2YgdGhlIGJlaGF2aW9yIG9mIGZ1dHVyZSByZXR1cm5zLiBPZiBjb3Vyc2UgdGhlIGludmVzdG9yJ3MgdmlldyBjYW4gYmUgZXhwcmVzc2VkIGluIGVpdGhlciBhYnNvbHV0ZSB0ZXJtcyAtIHdoZXJlIHRoZSBpbnZlc3RvciBoYXMgYSB2aWV3IG9uIGluZGl2aWR1YWwgYXNzZXQgcGVyZm9ybWFuY2Ugb3IgcmVsYXRpdmUgdGVybXMgLSB3aGVyZSB0aGUgaW52ZXN0b3IgaGFzIGEgdmlldyBvbiBob3cgYW4gaW5kaXZpZHVhbCBhc3NldCBwZXJmb3JtcyBpbiBjb21wYXJpc29uIHRvIGFub3RoZXIuIFRoaXMgYWJpbGl0eSB0byBpbmNvcnBvcmF0ZSBzdWJqZWN0aXZlIHZpZXdzIGludG8gYSBwb3J0Zm9saW8gZXhwZWN0ZWQgcmV0dXJuIGFuZCB2YXJpYW5jZSBpcyBwZXJoYXBzIHRoZSBtb3N0IHZhbHVhYmxlIHBhcnQgb2YgdGhlIGZyYW1ld29yay4KfCAgICAgICBJbiBwcmFjdGljZSwgdGhlIHN1YmplY3RpdmUgdmlld3MgYXJlIG9mdGVuIGdlbmVyYXRlZCBmcm9tIGEgVmVjdG9yIEF1dG9yZWdyZXNzaXZlIG1vZGVsIG9mIG9yZGVyIHAsIGEgYmFzaWMgZWNvbm9tZXRyaWMgbW9kZWwgdG8gcmVwcmVzZW50IHRoZSByZXR1cm5zIHRpbWUgc2VyaWVzIG9mIE4gYXNzZXRzLgoKKipDb3ZhcmlhbmNlLVJvYnVzdCBGcmFtZXdvcmsqKgoKfCAgICAgICBBbiBpbXBvcnRhbnQgb21pc3Npb24gaW4gdGhlIGV4cGxhbmF0aW9uIG9mIHRoZSBNYXJrb3dpdHogYW5kIEJsYWNrIExpdHRlcm1hbiBPcHRpbWl6YXRpb24gZnJhbWV3b3JrcyBpcyB0aGV5IGRvIG5vdCBhY2NvdW50IGZvciB0aGUgYW5vbWFsaWVzIHRoYXQgbWF5IGV4aXN0IGluIHRoZSBpbnB1dCBkYXRhLCBpLmUgdGhlIHBvcnRmb2xpbyByZXR1cm5zIGFuZCB2YXJpYW5jZSBlc3RpbWF0ZXMuIE9mIGNvdXJzZSwgYW5vbWFsaWVzIGluIHRoaXMgY2FzZSBpcyBhIHJlZmVyZW5jZSB0byB0aG9zZSBwb2ludHMgdGhhdCBkbyBub3QgbmVjZXNzYXJpbHkgJ3N0aWNrJyBvdXQgaW4gdGhlIGZpdHRlZCBlbXBpcmljYWwgbWVhbiwgdmFyaWFuY2UsIGFuZCBjb3ZhcmlhbmNlIGVzdGltYXRlcyBidXQgaGF2ZSBzdWJzdGFudGlhbCBpbmZsdWVuY2Ugb24gdGhvc2UgZXN0aW1hdGVzIGZyb20gdGhlIHJldHVybnMgZGF0YSBzZXQuCnwgICAgICAgVGhlIGltcG9ydGFuY2Ugb2YgZGV0ZWN0aW5nIGFuZCBkZWFsaW5nIHdpdGggYW5vbWFsaWVzIGNhbm5vdCBiZSB1bmRlcmVzdGltYXRlZCBiZWNhdXNlIGFub21hbGllcyBpbiB0aGUgZGF0YSBjYW4gZGlzdG9ydCBwYXJhbWV0ZXIgZXN0aW1hdGVzLCB3aXRoIHRoZSBlZmZlY3QgaW4gdGhlIGNhc2Ugb2YgZmluZGluZyB0aGUgb3B0aW1hbCByaXNrLXJldHVybiBjb21iaW5hdGlvbiBmb3IgYSBwb3J0Zm9saW8sIGJlaW5nIHRoZSBjb21iaW5hdGlvbnMgbW9yZSBhcHByb3ByaWF0ZWx5IGRlc2NyaWJlIHRoZSBhbm9tYWxpZXMuCnwgICAgICAgVG8gc2VlIHdoeSB0aGlzIGhhcHBlbnMsIGFuIGV4YW1wbGUgd291bGQgYmUgdXNlZnVsLiBGb3IgZXhhbXBsZSwgb3V0bGllcnMsIHdoaWNoIGFyZSBub3QgcGlja2VkIHVwIGluIHRoZSBzYW1wbGUgbWVhbiBhbmQgY292YXJpYW5jZSBlc3RpbWF0ZXMsIHB1bGwgdGhlIG9yZGluYXJ5IHNhbXBsZSBtZWFuIHRvd2FyZCB0aGVtc2VsdmVzLCBhbmQgYXJ0aWZpY2lhbGx5IGluZmxhdGUgdGhlIG9yZGluYXJ5IHNhbXBsZSBjb3ZhcmlhbmNlIG1hdHJpeC4gSWYgd2Uga25ldyBhIHByaW9yaSB3aGljaCBwb2ludHMgd2VyZSBhbm9tYWxvdXMsIHdlIGNvdWxkIHNpbXBseSBleGNsdWRlIHRoZW0gd2hlbiBlc3RpbWF0aW5nIHRoZSBwYXJhbWV0ZXJzLiBCdXQsIHRoaXMgaW5mb3JtYXRpb24gaXMgb2Z0ZW4gdW5hdmFpbGFibGUuCnwgICAgICAgVGhpcyBpZGVhIG9mIG1pbmltaXppbmcgdGhlIGluZmx1ZW5jZSBvZiBhbm9tYWxpZXMgaW4gZXN0aW1hdGluZyB0aGUgbWVhbiBhbmQgY292YXJpYW5jZSBtYXRyaXggaXMgd2hhdCByb2J1c3Qgb3B0aW1pemF0aW9uIHNlZWtzIHRvIGV4cGxvcmUuIFRoZSBNaW5pbXVtIENvdmFyaWFuY2UgRGV0ZXJtaW5hbnQgKE1DRCksIGEgcG9wdWxhciByb2J1c3Qgb3B0aW1pemF0aW9uIG1ldGhvZCwgYW5zd2VycyB0aGUgcXVlc3Rpb24gb2YgZXN0aW1hdGluZyB0aGUgcmlnaHQgZW1waXJpY2FsIG1lYW4gYW5kIGNvdmFyaWFuY2UgYnkgdGFraW5nIGFsbCBwb3NzaWJsZSBzdWJzZXRzIG9mIHRoZSBkYXRhLCBvZiBhIHNwZWNpZmllZCBzaXplLCBhbmQgZXN0aW1hdGVzIHRoZSBtZWFuIGFuZCBjb3ZhcmlhbmNlIG1hdHJpeCBmb3IgZWFjaCBzdWJzZXQuIEl0IHRoZW4ga2VlcHMgdGhlIGVzdGltYXRlcyBmb3IgdGhlIHN1YnNldCB3aG9zZSBjb3ZhcmlhbmNlIG1hdHJpeCBoYXMgdGhlIHNtYWxsZXN0IGRldGVybWluYW50IGFuZCBzY2FsZXMgdGhlbSBieSBhICdjb25zaXN0ZW5jeScgZmFjdG9yLgp8ICAgICAgIFRoZSB1bmRlcmx5aW5nIGlkZWEgaGVyZSBpcyBzaW5jZSB0aGUgZGV0ZXJtaW5hbnQgb2YgdGhlIGNvdmFyaWFuY2UgbWF0cml4IG1lYXN1cmVzIC0gcm91Z2hseSBzcGVha2luZyAtIHRoZSBzcHJlYWQgb2YgYSByZXR1cm5zIGRpc3RyaWJ1dGlvbiwgbWluaW1pemluZyBpdCBpcyBha2luIHRvIHNlbGVjdGluZyBhIHN1YnNldCBvZiB0aGUgZGF0YSB0aGF0IGhhcyB0aGUgdGlnaHRlc3QgZGlzdHJpYnV0aW9uIGFuZCBjb25zZXF1ZW50bHkgaXMgbGVzcyBsaWtlbHkgdG8gYmUgYWZmZWN0ZWQgYnkgb3V0bGllcnMuCgojIyBQb3J0Zm9saW8gQ29uc3RyYWludHMKCnwgICAgICAgSW4gb3JkZXIgdG8gbWltaWMgYSByZWFsaXN0aWMgcG9ydGZvbGlvIHdpdGggcmVhbGlzdGljIHRha2VuIHRvIG1lYW4gY29uc3RyYWludHMgaW1wb3NlZCBieSBlaXRoZXIgY2xpZW50cyBvciByZWd1bGF0b3JzIG9uIGEgcG9ydGZvbGlvIG1hbmFnZXIsIGFkZGl0aW9uYWwgY29uc3RyYWludHMgYXJlIG1hZGUgZm9yIHRoZSBvcHRpbWl6YXRpb24gcHJvYmxlbS4gSW4gdGhpcyBhcnRpY2xlLCB0aGUgYWRkaXRpb25hbCBjb25zdHJhaW50cyBhcmUKCmEpIExvbmctT25seSBDb25zdHJhaW50cyAtIHdoZXJlIHRoZSBzaG9ydCBzZWxsaW5nIGlzIG5vdCBhbGxvd2VkIGluIHRoZSBwb3J0Zm9saW8uCgpiKSBIb2xkaW5nIGNvbnN0cmFpbnRzIC0gd2hlcmUgYSBzaW5nbGUgRVRGIGNhbiBiZSBhdCBtb3N0IDQwJSBvZiB0aGUgcG9ydGZvbGlvIGFuZCBhdCBsZWFzdCA1JSBvZiB0aGUgcG9ydGZvbGlvLgoKfCAgICAgICBXaGlsZSBpbXBvc2luZyBsaW1pdHMgb24gdGhlIG1heGltdW0gZXhwb3N1cmUgb24gYSBzaW5nbGUgRVRGIGlzIGludHVpdGl2ZSwgaXQgaXMgd29ydGh3aGlsZSBleHBsYWluaW5nIHRoZSBsb3dlciBib3VuZC4gVGhlIHVuZGVybHlpbmcgcmVhc29uIGlzIHRvIGFsbG93IGEgaG9sZGluZyBzaXplIHNtYWxsIHlldCBzdWJzdGFudGlhbCBlbm91Z2ggdGhhdCB0aGUgcG9zaXRpb24gaW4gdGhlIEVURiBjYW4gY29udHJpYnV0ZSB0byBwb3J0Zm9saW8gcGVyZm9ybWFuY2UuIEEgbW9yZSBkZXRhaWxlZCByZWFzb24gaG93ZXZlciBpcyB0aGF0IGFzc2V0cywgcGFydGljdWxhcmx5IHN0b2NrcywgYXJlIG9mdGVuIHRyYWRlZCBpbiBtdWx0aXBsZXMgb2YgbWluaW11bSB0cmFuc2FjdGlvbiBjb3N0cyBvciByb3VuZHMgYW5kIGNvbnNlcXVlbnRseSwgdmVyeSBzbWFsbCBwb3NpdGlvbnMgY2Fubm90IGJlIHJlYWxpc3RpY2FsbHkgYWNxdWlyZWQuCgpgYGB7ciBzZXR1cCx3YXJuaW5nPUZBTFNFLGNvbW1lbnQ9TkEscmVzdWx0cz0naGlkZSd9CiMgTGlicmFyeSBJbXBvcnRzIC0gUGFja2FnZSBuYW1lcwpwYWNrYWdlcyA8LSBjKCJNQVNTIiwgInh0cyIsICJxdWFudG1vZCIsICJQb3J0Zm9saW9BbmFseXRpY3MiLAogICAgICAgICAgICAgICJST0kiLCAiUk9JLnBsdWdpbi5nbHBrIiwgIlJPSS5wbHVnaW4ucXVhZHByb2ciLCAidGlkeXZlcnNlIiwgCiAgICAgICAgICAgICAgImhpZ2hjaGFydGVyIiwgIlBlcmZvcm1hbmNlQW5hbHl0aWNzIikKCiMgSW5zdGFsbCBwYWNrYWdlcyBpZiBub3QgeWV0IGluc3RhbGxlZCAtIE5lYXQgdHJpY2sgZnJvbSBBbnRvaW5lIFNvZXRld2V5J3MgYmxvZwppbnN0YWxsZWRfcGFja2FnZXMgPC0gcGFja2FnZXMgJWluJSByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkKaWYgKGFueShpbnN0YWxsZWRfcGFja2FnZXMgPT0gRkFMU0UpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlc1shaW5zdGFsbGVkX3BhY2thZ2VzXSkKfQoKIyBQYWNrYWdlcyBsb2FkaW5nCnN1cHByZXNzTWVzc2FnZXMobGFwcGx5KHBhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKQoKIycgaW1wb3J0IGRhdGEgZm9yIGVhY2ggc3ltYm9sIGFuZCBjb252ZXJ0IHRvIG1vbnRobHkgcHJpY2VzCiMnIE5vdGUgY29udmVyc2lvbiB0byBtb250aGx5IGF1dG9tYXRpY2FsbHkgcmVtb3ZlcyBOQS1WQUxVRVMKZXRmLnN5bWJvbHMgPC0gYygiU1BZIiwgIklXTSIsICJGRVoiLCAiQUNXSSIsIkFHRyIsIklBR0ciLCAiSVlSIiwiUkVFVCIsIkdTRyIpCmFkai5jbG9zZSA8LSA2ICAjIDZ0aCBmaWVsZCBpcyBhZGp1c3RlZCBjbG9zZQpldGYucHJpY2VzIDwtIGdldFN5bWJvbHMoZXRmLnN5bWJvbHNbMV0sIHNvdXJjZT0ieWFob28iLAogICAgICAgICAgICAgICAgICAgICAgICAgYXV0by5hc3NpZ249RkFMU0UsIHJldHVybi5jbGFzcz0ieHRzIilbLGFkai5jbG9zZV0KZm9yIChpIGluIDI6bGVuZ3RoKGV0Zi5zeW1ib2xzKSkgewogIGV0Zi50bXAgPC0gZ2V0U3ltYm9scyhldGYuc3ltYm9sc1tpXSwgc291cmNlPSJ5YWhvbyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGF1dG8uYXNzaWduPUZBTFNFLCByZXR1cm4uY2xhc3M9Inh0cyIpWyxhZGouY2xvc2VdCiAgZXRmLnByaWNlcyA8LSBjYmluZChldGYucHJpY2VzLCBldGYudG1wKQp9CmNvbG5hbWVzKGV0Zi5wcmljZXMpIDwtIGV0Zi5zeW1ib2xzCgpldGYucHJpY2VzPC0gc3VwcHJlc3NXYXJuaW5ncyh0by5tb250aGx5KGV0Zi5wcmljZXMsCiAgICAgICAgICAgICAgICAgICAgICAgIGluZGV4QXQ9J2xhc3RvZicsCiAgICAgICAgICAgICAgICAgICAgICAgIE9ITEM9RkFMU0UpKQoKIyBDYWxjdWxhdGUgUmV0dXJucyBhbmQgcmVtb3ZlIG5hIHZhbHVlIGluIGZpcnN0IHJvdwpldGYucmV0cy5kYXRhIDwtIFJldHVybi5jYWxjdWxhdGUobmEub21pdChldGYucHJpY2VzKSxtZXRob2Q9J2xvZycpICU+JQogICAgICAgICAgICAgICAgbmEub21pdCgpCgojIEtlZXAgUmV0dXJucyBmcm9tIDIwMTYtMjAyMSBmb3IgaW4tc2FtcGxlIGVzdGltYXRpb24KZXRmLnJldHM8LSBldGYucmV0cy5kYXRhWyIyMDE2LzIwMjEiXQoKIyBLZWVwIFJldHVybnMgZnJvbSAyMDIyIGZvciBvdXQtb2Ytc2FtcGxlIHRlc3RpbmcKZXRmLnJldHMudGVzdDwtIGV0Zi5yZXRzLmRhdGFbIjIwMjIiXQoKIyMjIyMjIFBvcnRmb2xpbyBXZWlnaHRpbmcKCiMgIFBvcnRmb2xpbyBPYmplY3QgU3BlY2lmaWNhdGlvbiAtIFNldCB1cCBwb3J0Zm9saW8gd2l0aCBvYmplY3RpdmUgYW5kIGNvbnN0cmFpbnRzKExvbmcgT25seSkKCiMnICBHTVYgPSBHbG9iYWwgTWluaW11bSBWYXJpYW5jZSAgLCAgICBFVyAgPSBFcXVhbGx5IFdlaWdodAojJyAgUk8gID0gUm9idXN0IE9wdGltaXphdGlvbiAsICAgQkwgID0gQmxhY2sgTGl0dGVybWFuCgpwb3J0LnNwZWMgPC0gcG9ydGZvbGlvLnNwZWMoYXNzZXRzID0gY29sbmFtZXMoZXRmLnJldHMpKSAlPiUKICAgICAgICAgICAgICAgIGFkZC5vYmplY3RpdmUoIHR5cGU9InJpc2siLCBuYW1lPSJTdGREZXYiKSAlPiUKICAgICAgICAgICAgICAgIGFkZC5vYmplY3RpdmUoIHR5cGU9InJldHVybiIsIG5hbWU9Im1lYW4iKSAlPiUKICAgICAgICAgICAgICAgIGFkZC5jb25zdHJhaW50KHR5cGU9ImZ1bGxfaW52ZXN0bWVudCIpICAlPiUKICAgICAgICAgICAgICAgIGFkZC5jb25zdHJhaW50KCB0eXBlPSJsb25nX29ubHkiKSAgJT4lCiAgICAgICAgICAgICAgICBhZGQuY29uc3RyYWludCh0eXBlID0gImJveCIsIG1pbj0wLjA1LCBtYXg9MC40KQoKcG9ydC5zcGVjLkVXIDwtIHBvcnRmb2xpby5zcGVjKGFzc2V0cyA9IGNvbG5hbWVzKGV0Zi5yZXRzKSkgJT4lCiAgICAgICAgICAgICAgICAgIGFkZC5jb25zdHJhaW50KHR5cGU9ImZ1bGxfaW52ZXN0bWVudCIpICAlPiUKICAgICAgICAgICAgICAgICAgYWRkLmNvbnN0cmFpbnQoIHR5cGU9Imxvbmdfb25seSIpICAlPiUKICAgICAgICAgICAgICAgICAgYWRkLmNvbnN0cmFpbnQodHlwZSA9ICJib3giLCBtaW49MC4wNSwgbWF4PTAuNCkKCiMgU2V0IFBvcnRmb2xpbyBNb21lbnRzIGZvciBPcHRpbWl6YXRpb24gRnVuY3Rpb25zCnNhbXAubW9tZW50czwtICBmdW5jdGlvbihSKXsKICAgIG91dDwtc2V0LnBvcnRmb2xpby5tb21lbnRzKFIgPSBSLCBwb3J0Zm9saW89cG9ydC5zcGVjLCBtZXRob2Q9ICdzYW1wbGUnKQogICAgcmV0dXJuKG91dCkKfQoKCiMnIFZpZXdzIGFyZSBnZW5lcmF0ZWQgZnJvbSBhIFZBUi0yIG1vZGVsIGZyb20gdGhlIE1UUyBwYWNrYWdlCiMnIFIgaXMgYW4geHRzIHJldHVybiBvYmplY3QKYmwubW9tZW50czwtIGZ1bmN0aW9uKFIpewogIGRhdGE8LSBmb3J0aWZ5LnpvbyhSKVssLTFdCiAgbTE8LSBNVFM6OlZBUihkYXRhLHA9MixvdXRwdXQgPSBGKSAjIEZyb20gTVRTIHBhY2thZ2UKICBwcmVkPC0gTVRTOjpWQVJwcmVkKG0xLDEsb3V0cHV0ID0gRikgIyAxIHBlcmlvZCBhaGVhZCBmb3JlY2FzdAogIHByZWQ8LSBwcmVkJHByZWQgICAKICBuLmFzc2V0czwtbmNvbChSKQogIHBpY2s8LWRpYWcobi5hc3NldHMpCiAgb3V0PC0gYmxhY2subGl0dGVybWFuKFI9IFIsUCA9IHBpY2ssVmlld3MgPSBwcmVkKQogIG91dCRtdTwtIG91dCRCTE11CiAgb3V0JHNpZ21hPC0gb3V0JEJMU2lnbWEKICByZXR1cm4ob3V0KQp9CgojJyBGaW5kIE9wdGltYWwgV2VpZ2h0cwojJyBSZj0gMCAKR01WLndlaWdodHM8LSBvcHRpbWl6ZS5wb3J0Zm9saW8oUiA9IGV0Zi5yZXRzLHBvcnRmb2xpbz0gcG9ydC5zcGVjLG1heFNSPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpbWl6ZV9tZXRob2QgPSAiUk9JIix0cmFjZSA9IFRSVUUsbW9tZW50RlVOPSdzYW1wLm1vbWVudHMnKQogcHJpbnQoIk9wdGltYWwgTWFya293aXR6IHdlaWdodHMiKQogcHJpbnQoc3ByaW50ZigiJXMgOiAlcyIsIGV0Zi5zeW1ib2xzLAogICAgICAgICAgICAgIHNjYWxlczo6cGVyY2VudChhcy5udW1lcmljKEdNVi53ZWlnaHRzJHdlaWdodHMpKSkpCgojJyBUaGUgZm9sbG93aW5nIGZ1bmN0aW9uIGlzIG9idGFpbmVkIGZyb20gUm9zcyBCZW5uZXR0J3MgKDIwMTgpIFBhcGVyCiMnIEVzdGltYXRlcyBDb3ZhcmlhbmNlIE1hdHJpeApzaWdtYS5yb2J1c3Q8LSBmdW5jdGlvbihSKXsKICBvdXQ8LSBsaXN0KCkKICBzZXQuc2VlZCgxMjM0KQogIG91dCRzaWdtYTwtIGNvdi5yb2IoUixtZXRob2Q9J21jZCcsbnNhbXAgPSAnYmVzdCcpJGNvdgogIHJldHVybihvdXQpCn0KClJPLndlaWdodHMgPC1vcHRpbWl6ZS5wb3J0Zm9saW8oUiA9IGV0Zi5yZXRzLHBvcnRmb2xpbz0gcG9ydC5zcGVjLG1heFNSPVRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9wdGltaXplX21ldGhvZCA9ICJST0kiLHRyYWNlID0gVFJVRSxtb21lbnRGVU49J3NpZ21hLnJvYnVzdCcpCgogcHJpbnQoIkNvdmFyaWFuY2UtUm9idXN0IE9wdGltaXphdGlvbiBXZWlnaHRzIikKIHByaW50KHNwcmludGYoIiVzIDogJXMiLCBldGYuc3ltYm9scywKICAgICAgICAgICAgICBzY2FsZXM6OnBlcmNlbnQoYXMubnVtZXJpYyhSTy53ZWlnaHRzJHdlaWdodHMpKSkpCgoKRVcud2VpZ2h0cyA8LSBlcXVhbC53ZWlnaHQoZXRmLnJldHMsIHBvcnRmb2xpbz0gcG9ydC5zcGVjLkVXKQogcHJpbnQoIkVxdWFsIHdlaWdodHMiKQogcHJpbnQoc3ByaW50ZigiJXMgOiAlcyIsIGV0Zi5zeW1ib2xzLAogICAgICAgICAgICAgIHNjYWxlczo6cGVyY2VudChhcy5udW1lcmljKEVXLndlaWdodHMkd2VpZ2h0cykpKSkKIAojJyBUaGUgb3B0aW1pemVyIGlzIG5vdCBhYmxlIHRvIGZpbmQgYSB1bmlxdWUgJ3RhbmdlbnRpYWwnIHNvbHV0aW9uLCBzbyByZW1vdmUgCiMnIE1heCBTaGFycGUgcmF0aW8gb2JqZWN0aXZlLgojJyBUaGlzICdsb29zZW5lZCcgb3B0aW1pemF0aW9uIGltcGxlbWVudGF0aW9uIGlzIHRoZW4gZm9jdXNlZCBvbiBtYXhpbWl6aW5nIG9ubHkgcG9ydGZvbGlvCiMnIHJldHVybiBzdWJqZWN0IHRvIGNvbnN0cmFpbnRzIG9uIHdlaWdodHMuCkJMLndlaWdodHM8LWludmlzaWJsZShvcHRpbWl6ZS5wb3J0Zm9saW8oUiA9IGV0Zi5yZXRzLHBvcnRmb2xpbz0gcG9ydC5zcGVjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3B0aW1pemVfbWV0aG9kID0gIlJPSSIsdHJhY2UgPSBUUlVFLG1vbWVudEZVTj0nYmwubW9tZW50cycpKQogcHJpbnQoIkJsYWNrLUxpdHRlcm1hbiBXZWlnaHRzIikKIHByaW50KHNwcmludGYoIiVzIDogJXMiLCBldGYuc3ltYm9scywKICAgICAgICAgICAgICBzY2FsZXM6OnBlcmNlbnQoYXMubnVtZXJpYyhCTC53ZWlnaHRzJHdlaWdodHMpKSkpCmBgYAogCiMjIFBvcnRmb2xpbyBXZWlnaHRzCgp8ICAgICAgIEFwcGx5aW5nIHRoZSB2YXJpb3VzIHdlaWdodGluZyBzY2hlbWVzLCBhIGJhciBwbG90IG9mIHBvcnRmb2xpbyB3ZWlnaHRzIGlzIHByb2R1Y2VkIGFsb25nIHdpdGggYSB0YWJsZSBjb250YWluaW5nIHRoZSBzYW1lIGluZm9ybWF0aW9uIGluIGRldGFpbC4gQnJvYWRseSBzcGVha2luZywgaXQgY2FuIGJlIHNlZW4gZXhjZXB0IGZvciB0aGUgQmxhY2stTGl0dGVybWFuIGFwcHJvYWNoLCB0aGUgb3B0aW1pemF0aW9uIGFwcHJvYWNoZXMgYXJlIGNvbmNlbnRyYXRlZCBpbiB0aGUgYm9uZCBFVEZzIHdoaWNoIG9uZSBjYW4gYXJndWUgaGV1cmlzdGljYWxseSBpcyBhIHJlYXNvbmFibGUgYWxsb2NhdGlvbiBnaXZlbiB0aGUgbWFya2V0IHR1cmJ1bGVuY2Ugb2YgMjAyMCBhbmQgMjAyMS4gVGhlIEJsYWNrLUxpdHRlcm1hbiBhcHByb2FjaCBob3dldmVyLCBhcHBlYXJzIHRvIGJlIGNvbmNlbnRyYXRlZCBpbiB0aGUgQ29tbW9kaXR5IEVURi4KYGBge3IgMm5kLGNvbW1lbnQ9TkEsIGZpZy5kaW09Yyg4LDYpLHdhcm5pbmc9RkFMU0V9CiMgQ3JlYXRlIExpc3Qgb2YgcG9ydGZvbGlvIG9iamVjdHMgYW5kIGRpc3BsYXkgdGhlaXIgd2VpZ2h0cyBpbiBhIGJhcnBsb3QKcG9ydC5saXN0IDwtIGxpc3QoR01WLndlaWdodHMsRVcud2VpZ2h0cywgUk8ud2VpZ2h0cywgQkwud2VpZ2h0cykKbmFtZXMocG9ydC5saXN0KSA8LSBjKCJNYXJrb3dpdHoiLCJFcXVhbC1XZWlnaHQiLCAnQ292YXJpYW5jZSBSb2J1c3QgT3B0aW1pemF0aW9uJywgCiAgICAgICAgICAgICAgICAgICAgICAnQmxhY2stTGl0dGVybWFuJykKY2hhcnQuV2VpZ2h0cyhjb21iaW5lLm9wdGltaXphdGlvbnMocG9ydC5saXN0KSwgcGxvdC50eXBlID0gImJhcnBsb3QiLCAKICAgICAgICAgICAgICBtYWluPSJXZWlnaHRzIHN0YWNrZWQgYnkgQXNzZXQgKExvbmctb25seSkiKQoKIyBQdXQgV2VpZ2h0cyBpbiBhIGRhdGEgZnJhbWUoU2NhbGUgYnkgMTAwIHRvIGdldCBpdCBpbiBwZXJjZW50YWdlcykKd2VpZ2h0czwtIGNiaW5kKEdNVi53ZWlnaHRzJHdlaWdodHMsRVcud2VpZ2h0cyR3ZWlnaHRzLAogICAgICAgICAgICAgICAgUk8ud2VpZ2h0cyR3ZWlnaHRzLCBCTC53ZWlnaHRzJHdlaWdodHMpCndlaWdodHM8LSByb3VuZCh3ZWlnaHRzLDQpKjEwMApjb2xuYW1lcyh3ZWlnaHRzKTwtIG5hbWVzKHBvcnQubGlzdCkKCmtuaXRyOjprYWJsZSh3ZWlnaHRzLGNhcHRpb249ICJUYWJsZSAxIC0gV2VpZ2h0cyBpbiAoJSkiKQpgYGAKCiMjIE92ZXJ2aWV3IG9mIFBvcnRmb2xpbyBQZXJmb3JtYW5jZSBNZXRyaWNzCgp8ICAgICAgIE9mIGNvdXJzZSwgaXQgd291bGQgYmUgcG9pbnRsZXNzIHRvIGNvbnN0cnVjdCBwb3J0Zm9saW9zIGZyb20gdGhlIHZhcmlvdXMgd2VpZ2h0aW5nIHNjaGVtZXMgd2l0aG91dCBhc3Nlc3NpbmcgaG93IGVhY2ggc2NoZW1lIGFmZmVjdGVkIHRoZSBwb3J0Zm9saW8ncyBwZXJmb3JtYW5jZS4gVG8gdGhpcyBlbmQsIGEgYnJpZWYgc3VtbWFyeSBvZiBwb3B1bGFyIHBlcmZvcm1hbmNlIG1ldHJpY3MgaXMgcHJlc2VudGVkIHRvIGFzc2VzcyB0aGUgdmFsdWUgb2YgZWFjaCB3ZWlnaHRpbmcgc2NoZW1lLgoKKipBbm51YWxpemVkIFJldHVybjoqKiBHZW9tZXRyaWMgYXZlcmFnZSBvZiBhbm51YWwgcmV0dXJucyBnZW5lcmF0ZWQgYnkgYSBwb3J0Zm9saW8gb3ZlciBhIGdpdmVuIGludmVzdG1lbnQgcGVyaW9kLgoKKipBY3RpdmUgUmV0dXJuOioqIEV4Y2VzcyBwb3J0Zm9saW8gcmV0dXJucyBvdmVyIHNvbWUgc3BlY2lmaWVkIGJlbmNobWFyay4gTm90ZSBob3cgaXQgZGlmZmVycyBmcm9tIGV4Y2VzcyByZXR1cm4uCgoqKkV4Y2VzcyBSZXR1cm46KiogRXhjZXNzIHBvcnRmb2xpbyByZXR1cm5zIG92ZXIgc29tZSBzcGVjaWZpZWQgcmlzay1mcmVlIGFzc2V0LiAKCioqQW5udWFsaXplZCBTdGFuZGFyZCBEZXZpYXRpb246KiogR2VvbWV0cmljIGF2ZXJhZ2Ugb2YgdmFyaWFiaWxpdHkgb2YgYSBwb3J0Zm9saW8ncyBhbm51YWwgcmV0dXJucyBvdmVyIGEgZ2l2ZW4gaW52ZXN0bWVudCBwZXJpb2QuIFRoZSBoaWdoZXIgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiByZXR1cm5zLCB0aGUgcmlza2llciB0aGUgcG9ydGZvbGlvIHdlaWdodGluZyBzY2hlbWUgaXMuCgoqKkJldGEoQ0FQTSk6KiogUmVmbGVjdHMgcmlzayBvZiBhIHBvcnRmb2xpbyBpbiByZWxhdGlvbiB0byBzb21lIGJlbmNobWFyay4gU3RhdGlzdGljYWxseSwgaXQgaXMgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgcmVmbGVjdGluZyBob3cgbXVjaCBhIHBvcnRmb2xpbydzIHJldHVybnMgY2hhbmdlIGZvciBlYWNoIHVuaXQgY2hhbmdlIGluIHRoZSBiZW5jaG1hcmsncyByZXR1cm5zLiBIaWdoZXIgYmV0YXMgbWVhbiB0aGUgcG9ydGZvbGlvJ3MgcmV0dXJucyBhcmUgbW9yZSBzZW5zaXRpdmUgdG8gdGhlIGJlbmNobWFyaydzIHJldHVybnMuCgoqKlNoYXJwZSBSYXRpbzoqKiBQb3J0Zm9saW8gZXhjZXNzIHJldHVybnMgcGVyIHVuaXQgb2Ygcmlzay4gRXhjZXNzIHJldHVybnMgaGVyZSBpcyB0aGUgZXhjZXNzIHBvcnRmb2xpbyByZXR1cm5zIG92ZXIgc29tZSBzcGVjaWZpZWQgcmlzay1mcmVlIGFzc2V0IHdoaWxlIHJpc2sgaXMgdGhlIHBvcnRmb2xpbyBzdGFuZGFyZCBkZXZpYXRpb24uIEluIGdlbmVyYWwsIGhpZ2hlciBTaGFycGUgcmF0aW9zIG1lYW4gdGhlIHBvcnRmb2xpbyBwcm92aWRlcyBncmVhdGVyIHJldHVybnMgb3ZlciB0aGUgcmlzayBmcmVlIGFzc2V0IHdoaWxlIG9mZmVyaW5nIHRoZSBzYW1lIG9yIGxlc3MgdmFyaWFiaWxpdHkgb2YgcmV0dXJucy4gSGlnaGVyIFNoYXJwZSByYXRpb3MgYXJlIHRodXMgdXN1YWxseSBiZXR0ZXIuCgoqKlRyZXlub3IgUmF0aW86KiogUG9ydGZvbGlvIGV4Y2VzcyByZXR1cm5zIHBlciB1bml0IG9mIGJldGEvYmVuY2htYXJrIHJpc2suIExpa2UgdGhlIFNoYXJwZSwgZXhjZXNzIHJldHVybnMgaGVyZSBpcyB0aGUgZXhjZXNzIHBvcnRmb2xpbyByZXR1cm5zIG92ZXIgc29tZSBzcGVjaWZpZWQgcmlzay1mcmVlIGFzc2V0LiBJbiBnZW5lcmFsLCBoaWdoZXIgVHJleW5vciByYXRpb3MgbWVhbiB0aGUgcG9ydGZvbGlvIHByb3ZpZGVzIGdyZWF0ZXIgcmV0dXJucyBvdmVyIHRoZSByaXNrIGZyZWUgYXNzZXQgd2hpbGUgb2ZmZXJpbmcgbGVzcyBleHBvc3VyZSB0byB0aGUgYmVuY2htYXJrLiBIaWdoZXIgVHJleW5vciByYXRpb3MgYXJlIHRodXMgdXN1YWxseSBiZXR0ZXIuCgoqKk1vZGlnbGlhbmkqKiAkTV4yJDogIEFkanVzdHMgZWFjaCBwb3J0Zm9saW8ncyByZXR1cm5zIHRvIHdoYXQgaXQgd291bGQgaGF2ZSBiZWVuIGhhZCB0aGUgcG9ydGZvbGlvIG1hbmFnZXIgdGFrZW4gdGhlIHNhbWUgYW1vdW50IG9mIHJpc2sgYXMgdGhlIGJlbmNobWFyay4gSW4gZ2VuZXJhbCwgaGlnaGVyIE1vZGlnbGlhbmkgJE1eMiQgbWVhc3VyZXMgbWVhbiB0aGUgcG9ydGZvbGlvIHByb3ZpZGVzIGdyZWF0ZXIgcmV0dXJucyB3aGlsZSB0YWtpbmcgdGhlIHNhbWUgYW1vdW50IHJpc2sgYXMgdGhlIGJlbmNobWFyayB3aGVyZSByaXNrIGlzIHRoZSBiZW5jaG1hcmsncyBzdGFuZGFyZCBkZXZpYXRpb24uIEhpZ2hlciBNb2RpZ2xpYW5pICRNXjIkIG1lYXN1cmVzIGFyZSB0aHVzIHVzdWFsbHkgYmV0dGVyLgoKKipJbmZvcm1hdGlvbiBSYXRpbzoqKiBSYXRpbyBvZiBleGNlc3MgcG9ydGZvbGlvIHJldHVybnMgb3ZlciB0aGUgYmVuY2htYXJrIHRvIHRoZSBzdGFuZGFyZCBvZiB0aGUgZXhjZXNzIHBvcnRmb2xpbyByZXR1cm5zLiBJbiBnZW5lcmFsLCBoaWdoZXIgaW5mb3JtYXRpb24gcmF0aW9zIG1lYW4gdGhlIHBvcnRmb2xpbyBwcm92aWRlcyBncmVhdGVyIGV4Y2VzcyByZXR1cm5zIG92ZXIgdGhlIGJlbmNobWFyayBwZXIgdW5pdCBvZiByaXNrLiBIaWdoZXIgaW5mb3JtYXRpb24gcmF0aW9zIGFyZSB0aHVzIHVzdWFsbHkgYmV0dGVyLgoKKipUcmFja2luZyBFcnJvcjoqKiBTdGFuZGFyZCBEZXZpYXRpb24gb2YgZXhjZXNzIHBvcnRmb2xpbyByZXR1cm5zIG92ZXIgdGhlIGJlbmNobWFyay4gVGhlIGdyZWF0ZXIgdGhlIHRyYWNraW5nIGVycm9yLCB0aGUgbW9yZSB0aGUgcG9ydGZvbGlvIHJldHVybnMgZGV2aWF0ZSBmcm9tIHRoZSBiZW5jaG1hcmsuCgoqKkRpdmVyc2lmaWNhdGlvbiBSYXRpbzoqKiBSYXRpbyBvZiB0aGUgd2VpZ2h0ZWQgYXZlcmFnZSBvZiB2b2xhdGlsaXR5IGluZGl2aWR1YWwgYXNzZXRzIHRvIHRoZSBvdmVyYWxsIHBvcnRmb2xpbyB2b2xhdGlsaXR5LiBIZXJlLCB3ZWlnaHRzIGFyZSB0aGUgcG9ydGZvbGlvIHdlaWdodHMuIEZvciBhIHdlbGwgZGl2ZXJzaWZpZWQgbG9uZy1vbmx5IHBvcnRmb2xpbywgdGhlIHJhdGlvIHNob3VsZCBiZSBncmVhdGVyIHRoYW4gMS4KCioqQ29uY2VudHJhdGlvbiBSYXRpbzoqKiAKCnwgICAgICAgUmF0aW8gb2YgdGhlIHdlaWdodGVkIHN1bSBvZiBzcXVhcmVkIGluZGl2aWR1YWwgYXNzZXQgdm9sYXRpbGl0eSB0byB3ZWlnaHRlZCBzdW0gb2YgaW5kaXZpZHVhbCBhc3NldCB2b2xhdGlsaXR5IHNxdWFyZWQuIFdoaWxlIGNvbmZ1c2luZywgdGhlIGlkZWEgZm9yIHRoZSBudW1lcmF0b3IgaXMgdG8gc3F1YXJlIHRoZSBpbmRpdmlkdWFsIHdlaWdodGVkIHZvbGF0aWxpdGllcyBiZWZvcmUgeW91IHN1bSwgd2hlcmUgd2VpZ2h0cyBhcmUgdGhlIHBvcnRmb2xpbyB3ZWlnaHRzLgp8ICAgICAgIEZvciB0aGUgZGVub21pbmF0b3IsIHlvdSB0YWtlIHRoZSB3ZWlnaHRlZCBzdW0gb2YgaW5kaXZpZHVhbCBhc3NldCB2b2xhdGlsaXR5IGJlZm9yZSB5b3UgdGFrZSB0aGUgc3F1YXJlLiBGb3IgYSB3ZWxsIGRpdmVyc2lmaWVkLCBsb25nLW9ubHkgcG9ydGZvbGlvLCB0aGUgcmF0aW8gc2hvdWxkIGJlIGxlc3MgdGhhbiAxLiBBZGRpdGlvbmFsbHksIHBvcnRmb2xpbyBhbGxvY2F0aW9ucyB3aXRoIGhpZ2hlciBjb25jZW50cmF0aW9uIHJhdGlvcyBhcmUganVkZ2VkIHRvIGJlIG1vcmUgY29uY2VudHJhdGVkIHRoYW4gdGhlaXIgd2VpZ2h0aW5nIGNvdW50ZXJwYXJ0cy4gUHV0IHNpbXBseSwgYSBsb3dlciBjb25jZW50cmF0aW9uIHJhdGlvIGlzIHVzdWFsbHkgYmV0dGVyLgoKKipDdXN0b21pemluZyB0aGUgQmVuY2htYXJrOioqCgp8ICAgICAgIFNpbmNlIG1vc3QgaW52ZXN0bWVudHMgZXN0YWJsaXNoIGEgYmVuY2htYXJrIHRvIGV2YWx1YXRlIGEgcG9ydGZvbGlvIG1hbmFnZXIncyBwZXJmb3JtYW5jZSwgYSBjdXN0b21pemVkIGluZGV4IGlzIGNyZWF0ZWQgZm9yIHRoaXMgcHVycG9zZS4gVGhlIGN1c3RvbWl6ZWQgaW5kZXggaXMgY3JlYXRlZCB0byBiZXR0ZXIgcmVmbGVjdCB0aGUgaW52ZXN0b3IncyBvYmplY3RpdmUgd2hpY2ggZm9yIHRoaXMgYXJ0aWNsZSBpcyBhIGRlc2lyZWQgZXhwb3N1cmUgdG8gSW5kZXggRVRGcy4KfCAgICAgICBPZiBjb3Vyc2UsIHRoaXMgaW52ZXN0b3IgaXMgYXNzdW1lZCB0byBkZXNpcmUgZXhwb3N1cmUgdG8gaW5kaWNlcyB0aGF0IHRyYWNrIHN0b2NrcywgYm9uZHMsIGNvbW1vZGl0aWVzLCBhbmQgcmVhbCBlc3RhdGUuIFRvIG1ha2UgdGhlIGFuYWx5c2lzIHRyYWN0YWJsZSwgYSBwcmljZS13ZWlnaHRlZCBpbmRleCBpcyBjb25zdHJ1Y3RlZCB3aGljaCBhdmVyYWdlcyBlcXVhbGx5IHRoZSB2YWx1ZSBvZiBlYWNoIEluZGV4IEVURiBpbiB0aGUgcG9ydGZvbGlvLgoKIyMgUGVyZm9ybWFuY2UgRXZhbHVhdGlvbgoKfCAgICAgICBUYWJsZSAyIHNob3dzIHRoZSBoaXN0b3JpY2FsIHBlcmZvcm1hbmNlIG9mIGVhY2ggcG9ydGZvbGlvIHdlaWdodGluZyBzY2hlbWUuIEl0IGNhbiBiZSBzZWVuIC0gZ2VuZXJhbGx5IHNwZWFraW5nIC0gdGhlIEJsYWNrLUxpdHRlcm1hbiBhcHByb2FjaCBwZXJmb3JtcyBiZXN0IGZvciB0aGUgaW52ZXN0bWVudCBwZXJpb2QuIEluIHBhcnRpY3VsYXIsIGl0IHByb2R1Y2VkIGJldHRlciBhY3RpdmUgYW5kIGV4Y2VzcyByZXR1cm5zLCBoaWdoZXIgc2hhcnBlLCB0cmV5bm9yIGFuZCBpbmZvcm1hdGlvbiByYXRpb3MgYW1vbmcgb3RoZXJzLiBJdCBpcyB3b3J0aCBub3Rpbmcgd2hpbGUgdGhlIEJsYWNrLUxpdHRlcm1hbiBwb3J0Zm9saW8gd2FzIHdlbGwgZGl2ZXJzaWZpZWQsIGkuZSBpbiB0ZXJtcyBvZiBpdHMgZGl2ZXJzaWZpY2F0aW9uIHJhdGlvLCBpdCBoYWQgdGhlIG1vc3QgY29uY2VudHJhdGVkIHBvcnRmb2xpbyBpbiBjb21wYXJpc29uIHRvIHRoZSBvdGhlciBFVEYgcG9ydGZvbGlvcy4oZnJvbSBpdHMgY29uY2VudHJhdGlvbiByYXRpbykgCnwgICAgICAgVG8gc2hvdyB3aGF0IHRoaXMgcGVyZm9ybWFuY2UgbWVhbnMgZm9yIHRoZSBpbnZlc3RvciwgYSBsaW5lIGdyYXBoIGlzIHByZXNlbnRlZCBzaG93aW5nIHRoZSBncm93dGggb2YgJDEwLDAwMCBpbiBlYWNoIHBvcnRmb2xpbyB3ZWlnaHRpbmcgc2NoZW1lLgpgYGB7ciAzcmQsZmlnLmNhcD0iQ3VtdWxhdGVkIFBvcnRvZmxpbyBSZXR1cm5zIixjb21tZW50PU5BLGZpZy5kaW09Yyg4LDYpLHdhcm5pbmc9RkFMU0UsZGV2PSdwbmcnfQp3ZWlnaHRzIDwtIGNiaW5kKEdNVi53ZWlnaHRzJHdlaWdodHMsRVcud2VpZ2h0cyR3ZWlnaHRzLAogICAgICAgICAgICAgICAgIFJPLndlaWdodHMkd2VpZ2h0cyxCTC53ZWlnaHRzJHdlaWdodHMpCiMgcmV0cyA9IHJldHVybnMKcmV0czwtYygpCgojJyBDYWxjdWxhdGUgUmV0dXJucyBvbiBPdXQtb2YtU2FtcGxlIERhdGEgLSBXaXRoIFdlYWx0aCBQcm9ncmVzc2lvbgojJyBDb21iaW5lIHJldHVybnMgb2YgZWFjaCBwb3J0Zm9saW8gaW4gYSBzaW5nbGUgZGF0YSBmcmFtZQpmb3IgKGkgaW4gMTpuY29sKHdlaWdodHMpKSB7CiAgcmV0cy52YWx1ZXMgPC0gUmV0dXJuLnBvcnRmb2xpbyhldGYucmV0cy50ZXN0LCB3ZWlnaHRzICA9IHdlaWdodHNbLGldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VhbHRoLmluZGV4ID0gVFJVRSkKICByZXRzPC1jYmluZChyZXRzLHJldHMudmFsdWVzKQp9CgojJyBDb21iaW5lIFJldHVybnMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCmFtb3VudDwtIDEwMDAwCnJldHMuc2NhbGU8LSByZXRzKmFtb3VudApkYXRlIDwtIGFzLkRhdGUoIjIwMjEtMTItMzEiKQppbml0LmludmVzdG1lbnQgPC0geHRzKG1hdHJpeChhbW91bnQsIG5yb3c9MSxuY29sPTQpLCBvcmRlci5ieT1kYXRlKQpyZXRzLnNjYWxlPC0gcmJpbmQoaW5pdC5pbnZlc3RtZW50LCByZXRzLnNjYWxlKQpuYW1lcyhyZXRzLnNjYWxlKSA8LSBjKCJNYXJrb3dpdHoiLCJFcXVhbC1XZWlnaHQiLCJDb3ZhcmlhbmNlLVJvYnVzdCBPcHRpbWl6YXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICJCbGFjayBMaXR0ZXJtYW4iKQoKIycgUGVyZm9ybWFuY2UgLSBDdW11bGF0aXZlIFJldHVybnMgQ2hhcnQKY3VtLnJldHMuY2hhcnQ8LWhpZ2hjaGFydCh0eXBlPSdzdG9jaycpJT4lCiAgaGNfdGl0bGUodGV4dD0nSHlwb3RoZXRpY2FsIEdyb3d0aCBvZiAkMTAsMDAwICcpICU+JQogIGhjX2FkZF9zZXJpZXMocm91bmQocmV0cy5zY2FsZVssMV0sMiksIAogICAgICAgICAgICAgICAgbmFtZT1jb2xuYW1lcyhyZXRzLnNjYWxlKVsxXSklPiUKICBoY19hZGRfc2VyaWVzKHJvdW5kKHJldHMuc2NhbGVbLDJdLDIpLCAKICAgICAgICAgICAgICAgIG5hbWU9Y29sbmFtZXMocmV0cy5zY2FsZSlbMl0pJT4lCiAgaGNfYWRkX3Nlcmllcyhyb3VuZChyZXRzLnNjYWxlWywzXSwyKSwgCiAgICAgICAgICAgICAgICBuYW1lPWNvbG5hbWVzKHJldHMuc2NhbGUpWzNdKSU+JQogIGhjX2FkZF9zZXJpZXMocm91bmQocmV0cy5zY2FsZVssNF0sMiksIAogICAgICAgICAgICAgICAgbmFtZT1jb2xuYW1lcyhyZXRzLnNjYWxlKVs0XSklPiUKICBoY19uYXZpZ2F0b3IoZW5hYmxlZD1GQUxTRSklPiUKICBoY19zY3JvbGxiYXIoZW5hYmxlZD1GQUxTRSkgJT4lCiAgaGNfYWRkX3RoZW1lKGhjX3RoZW1lX2ZsYXQoKSklPiUKICBoY19leHBvcnRpbmcoZW5hYmxlZD1UUlVFKSU+JQogIGhjX2xlZ2VuZChlbmFibGVkPVRSVUUpICU+JSAKICBoY19wbG90T3B0aW9ucyhzZXJpZXMgPSBsaXN0KGFuaW1hdGlvbiA9IEZBTFNFKSkKY3VtLnJldHMuY2hhcnQKCgprZXkubWVhc3VyZXM8LSBmdW5jdGlvbiggUmEsIHBvcnQud2VpZ2h0LCBibSwgc2NhbGUgPSAxMixTaWdtYSA9IGNvdihldGYucmV0cyksIFJmPSAwKXsKICByZXQ8LSAgICAgUmV0dXJuLmFubnVhbGl6ZWQoUmEsc2NhbGUgPSBzY2FsZSkgKjEwMAogIGFjdC5yZXQ8LSAgQWN0aXZlUmV0dXJuKFJhICxSYj0gYm0sc2NhbGUgPSBzY2FsZSkgKjEwMAogIGV4LnJldDwtICAgc3VtKFJldHVybi5leGNlc3MoUmEpKSoxMDAgICAjIFJmPSAwIAogICMnIE5vdGUgcmV0ID0gZXgucmV0IHNpbmNlIFJmPTAKICAjJyBUaGF0IGlzLCB0b3RhbCByZXR1cm5zIHNob3VsZCBiZSByb3VnaGx5IGVxdWFsIHRvIGV4Y2VzcyByZXR1cm5zCiAgIycgV2hlcmUgdG90YWwgcmV0dXJucyA9IGFubnVhbGl6ZWQgcmV0dXJucyBpZiB0aGUgaW52ZXN0bWVudCBwZXJpb2QgPCBvciA9IDEgeWVhcgogIAogIHN0LmRldjwtICBTdGREZXYuYW5udWFsaXplZChSYSx3ZWlnaHRzPXBvcnQud2VpZ2h0LHNjYWxlID0gc2NhbGUpICoxMDAKICBiZXRhPC0gQ0FQTS5iZXRhKFJhLGJtKQogIHNoYXJwZTwtICBTaGFycGVSYXRpby5hbm51YWxpemVkKFJhLCBSZj0gUmYsIHNjYWxlID0gc2NhbGUpCiAgdHJleW5vcjwtIFRyZXlub3JSYXRpbyhSYSwgUmIgPSBibSxzY2FsZT1zY2FsZSkKICBpLnJhdGlvPC0gSW5mb3JtYXRpb25SYXRpbyhSYSwgUmIgPSBibSxzY2FsZT1zY2FsZSkKICBtLnNxdWFyZTwtIE1vZGlnbGlhbmkoUmEsIFJiID0gYm0sc2NhbGU9c2NhbGUpCiAgdC5lcnJvcjwtIFRyYWNraW5nRXJyb3IoUmEsIFJiID0gYm0sc2NhbGUgPSBzY2FsZSkKICBkciA8LSAgICAgRlJBUE86OmRyKHBvcnQud2VpZ2h0LCBTaWdtYSApICMgRnVuY3Rpb24gb2J0YWluZWQgZnJvbSBGUkFQTyBwYWNrYWdlCiAgY3IgPC0gICAgIEZSQVBPOjpjcihwb3J0LndlaWdodCwgU2lnbWEpICAjIEZ1bmN0aW9uIG9idGFpbmVkIGZyb20gRlJBUE8gcGFja2FnZQogIHJlczwtICAgICByb3VuZChyYmluZChyZXQsYWN0LnJldCxleC5yZXQsIHN0LmRldixiZXRhLHNoYXJwZSwgdHJleW5vcixtLnNxdWFyZSxpLnJhdGlvLCB0LmVycm9yLCBkcixjciksMykKCiAgcm93bmFtZXMocmVzKSA8LSBjKCJBbm51YWxpemVkIFJldHVybiglKSIsIkFjdGl2ZSBSZXR1cm4oJSkiLCJFeGNlc3MgUmV0dXJuKCUpIiwKICAgICAgICAgICAgICAgICAgICAgJ0FubnVhbGl6ZWQgU3RhbmRhcmQgRGV2aWF0aW9uKCUpJywiQmV0YSIsJ1NoYXJwZScsJ1RyZXlub3InLAogICAgICAgICAgICAgICAgICAiTV4yIG9mIE1vZGlnbGlhbmkiLCAiSW5mb3JtYXRpb24gUmF0aW8iLCJUcmFja2luZyBFcnJvciIsCiAgICAgICAgICAgICAgICAgICJEaXZlcnNpZmljYXRpb24gUmF0aW8iLCJDb25jZW50cmF0aW9uIFJhdGlvIikKICByZXR1cm4ocmVzKQp9CgojJyAgIE5vdGUgdGhlIHBvcnRmb2xpbyBieSBjb25zdHJ1Y3Rpb24gaXMgaW52ZXN0ZWQgaW4gdGhlIG92ZXJhbGwgbWFya2V0LgojJyAgIFRoZSBCZW5jaG1hcmsgdXNlZCBoZXJlIGlzIGEgUHJpY2UtV2VpZ2h0ZWQgaW5kZXguCiMnICAgUHJpY2UtIFdlaWdodGVkIGluZGljZXMgYnkgY29uc3RydWN0aW9uIGF2ZXJhZ2UgcHJpY2VzIGVxdWFsbHkuCiMnICAgY29tcC5pbmQgPSBjb21wb3NpdGUgaW5kZXggcmV0dXJucwoKIycgICBUaGUgbW90aXZhdGlvbiBmb3IgdGhpcyBtdWx0aS1hc3NldCBjb21wb3NpdGUgaW5kZXggaXMgdGhlCiMnICAgRlRTRSBNdWx0aS1Bc3NldCBDb21wb3NpdGUgSW5kZXggU2VyaWVzIHdoaWNoIAojJyAgIGFjY29yZGluZyB0byBGVFNFIFJ1c3NlbGwKIycgICBpcyBkZXNpZ25lZCB0byBtZWFzdXJlIGNyb3NzLWFzc2V0IG1hcmtldCByZXR1cm5zIGZvciBhIHJhbmdlIG9mIHJpc2sgZXhwb3N1cmVzLgoKZXRmLnByaWNlcyRjb21wPC1yb3dNZWFucyhldGYucHJpY2VzKQojJyBDYWxjdWxhdGUgUmV0dXJucyBhbmQgcmVtb3ZlIG5hIHZhbHVlIGluIGZpcnN0IHJvdwojJyBTZWxlY3QgUmV0dXJucyBmcm9tIDIwMjIgCmNvbXAuaW5kIDwtIFJldHVybi5jYWxjdWxhdGUoZXRmLnByaWNlcyRjb21wLG1ldGhvZD0nbG9nJykgJT4lCiAgICAgICAgICAgIG5hLm9taXQoKQpjb21wLmluZDwtIGNvbXAuaW5kWycyMDIyJ10KCiMgcmV0cyA9IHJldHVybnMKcmV0czwtYygpCgojJyBDYWxjdWxhdGUgUmV0dXJucyBvbiBPdXQtb2YtU2FtcGxlIERhdGEgLSBXaXRob3V0IFdlYWx0aCBQcm9ncmVzc2lvbgojJyBDb21iaW5lIHJldHVybnMgb2YgZWFjaCBwb3J0Zm9saW8gaW4gYSBzaW5nbGUgZGF0YSBmcmFtZQpmb3IgKGkgaW4gMTpuY29sKHdlaWdodHMpKSB7CiAgcmV0cy52YWx1ZXMgPC0gUmV0dXJuLnBvcnRmb2xpbyhldGYucmV0cy50ZXN0LCB3ZWlnaHRzICA9IHdlaWdodHNbLGldKQogIHJldHM8LWNiaW5kKHJldHMscmV0cy52YWx1ZXMpCn0KCiMgcG9ydHMgPSBwb3J0Zm9saW9zCnBvcnRzPC1jKCkKIyBDb21iaW5lIHBlcmZvcm1hbmNlIG1lYXN1cmVzIG9mIGVhY2ggcG9ydGZvbGlvIGluIGEgc2luZ2xlIGRhdGEgZnJhbWUKZm9yIChpIGluIDE6bmNvbCh3ZWlnaHRzKSkgewogIG1lYXN1cmVzIDwtIGtleS5tZWFzdXJlcyhwb3J0LndlaWdodCA9IHdlaWdodHNbLGldLCAKICAgICAgICAgICAgICAgICBSYSA9IHJldHNbLGldLCBibSA9ICBjb21wLmluZCkKICBwb3J0czwtY2JpbmQocG9ydHMsbWVhc3VyZXMpCn0KCiMnICBOb3RlIGZvciB0aGUgYmVuY2htYXJrLCBhbGwgd2UgbmVlZCBhcmUgdGhlIGFubnVhbGl6ZWQKIycgIFJldHVybnMsIFN0RGV2LCBhbmQgRXhjZXNzIFJldHVybnMKIycgIFNpbmNlIHRoZSBiZW5jaG1hcmsgaXMgcHJpY2Utd2VpZ2h0ZWQsIHRoZSB3ZWlnaHRzIHVzZWQgaW4gdGhlIGZ1bmN0aW9uIGlzIHRoZSBFcXVhbCBXZWlnaHQKYm0gPC0ga2V5Lm1lYXN1cmVzKHBvcnQud2VpZ2h0ID0gRVcud2VpZ2h0cyR3ZWlnaHRzLCAKICAgICAgICAgICAgICAgICAgUmEgPSBjb21wLmluZCwgYm0gPSAgY29tcC5pbmQpCgprZWVwLnJvd3M8LSBjKCJBbm51YWxpemVkIFJldHVybiglKSIsIkV4Y2VzcyBSZXR1cm4oJSkiLAogICAgICAgICAnQW5udWFsaXplZCBTdGFuZGFyZCBEZXZpYXRpb24oJSknKQojIEFzc2lnbiBOQSB0byB0aG9zZSB2YWx1ZXMgbm90IGluIGtlZXAucm93cwpibVshKGJtICVpbiUgYm1ba2VlcC5yb3dzLF0pLF08LSBOQSAgCmFsbC5wb3J0cyA8LSBjYmluZChwb3J0cyxibSkKCgpjb2xuYW1lcyhhbGwucG9ydHMpIDwtIGFwcGVuZChuYW1lcyhyZXRzLnNjYWxlKSwiQmVuY2htYXJrIikKa25pdHI6OmthYmxlKGFsbC5wb3J0cywKICAgICAgICAgICAgIGNhcHRpb249IlRhYmxlIDIgLSBLZXkgUG9ydGZvbGlvIFBlcmZvcm1hbmNlIE1lYXN1cmVzIikKYGBgCgoKIyMgUmlzayBEZWNvbXBvc2l0aW9uCnwgICAgICAgQXMgYSBjb25jbHVzaW9uIHRvIHRoaXMgYXJ0aWNsZSwgaXQgd291bGQgYmUgdXNlZnVsIHRvIGV4YW1pbmUgaG93IGV4cG9zdXJlcyB0byBpbmRpdmlkdWFsIEVURnMgYWZmZWN0IHRoZSB2YXJpYWJpbGl0eSBwb3J0Zm9saW8gcmV0dXJucyBmb3IgZWFjaCB3ZWlnaHRpbmcgc2NoZW1lLiBUaGlzIGJyZWFrZG93biBvZiBwb3J0Zm9saW8gcmlzayBpbnRvIGNvbnRyaWJ1dGlvbnMgYnkgaW5kaXZpZHVhbCBFVEZzIHdoZW4gY29tYmluZWQgd2l0aCB0aGUgcG9ydGZvbGlvIHdlaWdodHMgc2hvd3Mgd2hldGhlciBoaXN0b3JpY2FsbHkgdGhlIHBvcnRmb2xpbyBoYXMgYmVlbiBvdmVyIG9yIHVuZGVyZXhwb3NlZCB0byBzcGVjaWZpYyBFVEZzLiAKfCAgICAgICBTaG91bGQgb3VyIGh5cG90aGV0aWNhbCBpbnZlc3RvciBkZXNpcmUgdG8gcmUtYWxsb2NhdGUgdGhlaXIgY2FwaXRhbCBhbW9uZyB0aGUgRVRGcyBmb3IgYSBnaXZlbiB3ZWlnaHRpbmcgc2NoZW1lLCB0aGV5IGNhbiB1c2UgdGhpcyByaXNrIGRlY29tcG9zaXRpb24gaW5mb3JtYXRpb24gdG8gaW5jcmVhc2Ugb3IgZGVjcmVhc2UgcG9ydGZvbGlvIGFsbG9jYXRpb24gdG8gc3BlY2lmaWMgRVRGcy4gRm9yIGV4YW1wbGUsIHdoaWxlIHRoZSBTUFkgbWFrZXMgdXAgMjIlIG9mIHRoZSBNYXJrb3dpdHogRVRGIHBvcnRmb2xpbywgaXQgYWNjb3VudHMgZm9yIDM1JSBvZiB0aGUgcG9ydGZvbGlvIHJldHVybiB2b2xhdGlsaXR5LiBPdXIgaW52ZXN0b3IgbWlnaHQgY29uc2lkZXIgcmVkdWNpbmcgZXhwb3N1cmUgdG8gdGhlIFNQWSBvbiB0aGlzIGJhc2lzLgp8ICAgICAgIFRhYmxlIDMgc2hvd3MgdGhlIHJpc2sgZGVjb21wb3NpdGlvbiBvZiBlYWNoIHBvcnRmb2xpbyB3ZWlnaHRpbmcgc2NoZW1lLgpgYGB7ciA0dGgsIGNvbW1lbnQ9TkEsZmlnLmRpbT1jKDgsNiksd2FybmluZz1GQUxTRX0KIyMjIyMgQ29tcG9uZW50IENvbnRyaWJ1dGlvbiB0byBQb3J0Zm9saW8gVm9sYXRpbGl0eS8gU3RhbmRhcmQgRGV2aWF0aW9uCgojICAgV2hhdCBwZXJjZW50YWdlIG9mIHBvcnRmb2xpbyBTdC5EZXYgZGlkIGVhY2ggcG9zaXRpb24gYWNjb3VudCBmb3I/CgphbGwuY29tcHMuc2Q8LWMoKQojIENvbWJpbmUgY29tcG9uZW50IGNvbnRyaWJ1dGlvbnMgb2YgZWFjaCBwb3J0Zm9saW8gaW4gYSBzaW5nbGUgZGF0YSBmcmFtZQpmb3IgKGkgaW4gMTpuY29sKHdlaWdodHMpKSB7CiAgIyBTY2FsZSBieSAxMDAgdG8gZ2V0IHBlcmNlbnQgZnJvbSBkZWNpbWFscyBsaWtlIHNheSAwLjIgdG8gMjAlCiAgU3RkIDwtIChTdGREZXYoZXRmLnJldHMudGVzdCwgd2VpZ2h0cyA9IHdlaWdodHNbLGldLCBwb3J0Zm9saW9fbWV0aG9kPSAgCiAgICAgICAgICAgICAgICAgICJjb21wb25lbnQiKSRwY3RfY29udHJpYl9TdGREZXYpKjEwMAogIGFsbC5jb21wcy5zZDwtY2JpbmQoYWxsLmNvbXBzLnNkLFN0ZCkKfQoKY29sbmFtZXMoYWxsLmNvbXBzLnNkKSA8LSBuYW1lcyhyZXRzLnNjYWxlKQprbml0cjo6a2FibGUoYWxsLmNvbXBzLnNkLAogICAgICAgICAgICAgY2FwdGlvbiA9IlRhYmxlIDMgLSBDb21wb25lbnQgQ29udHJpYnV0aW9uIHRvIFBvcnRmb2xpbyBWb2xhdGlsaXR5LyBTdGFuZGFyZCBEZXZpYXRpb24oJSkiKQpgYGAKCiMjIFJlc291cmNlcyBVc2VkCgpCYWNvbiwgQ2FybCBSLiBQcmFjdGljYWwgUmlzay1BZGp1c3RlZCBQZXJmb3JtYW5jZSBNZWFzdXJlbWVudC4gU2Vjb25kIGVkaXRpb24uIFdpbGV5IEZpbmFuY2UgU2VyaWVzLiBIb2Jva2VuLCBOSjogV2lsZXksIDIwMjIuCgpGYWJvenppLCBGcmFuayBKLiwgYW5kIERlc3Npc2xhdmEgQS4gUGFjaGFtYW5vdmEuIFBvcnRmb2xpbyBDb25zdHJ1Y3Rpb24gYW5kIEFuYWx5dGljcy4gRnJhbmsgSi4gRmFib3p6aSBTZXJpZXMuIEhvYm9rZW4sIE5ldyBKZXJzZXk6IEpvaG4gV2lsZXkgJiBTb25zLCBJbmMsIDIwMTYuCgpCbGFjayBGLCBMaXR0ZXJtYW4gUiAoMTk5MikgR2xvYmFsIHBvcnRmb2xpbyBvcHRpbWl6YXRpb24uIEZpbmFuY2lhbCBBbmFseXN0cyBKLiA0OCg1KToyOOKAkzQzLgoKUm9zZW50aGFsLCBEYWxlIFcuIFIuIEEgUXVhbnRpdGF0aXZlIFByaW1lciBvbiBJbnZlc3RtZW50cyB3aXRoIHIuIENoaWNhZ28sIElMOiBRMzYgTExDLCAyMDE4LgoKSGFycnkgTWFya293aXR6LCAxOTUyLiAiUG9ydGZvbGlvIFNlbGVjdGlvbiwiIEpvdXJuYWwgb2YgRmluYW5jZSwgQW1lcmljYW4gRmluYW5jZSBBc3NvY2lhdGlvbiwgdm9sLiA3KDEpLCBwYWdlcyA3Ny05MSwgTWFyY2guCgpCZW5uZXR0IFJvc3MsIDIwMTguICJDdXN0b20gTW9tZW50IGFuZCBPYmplY3RpdmUgRnVuY3Rpb25zIgoKQWxpIFBhcmRoYW4sIOKAnEludHVpdGl2ZSBFeHBsYW5hdGlvbiBvZiBNaW5pbXVtIENvdmFyaWFuY2UgRGV0ZXJtaW5hbnQgKE1DRCku4oCdIENyb3NzIFZhbGlkYXRlZCwgSnVseSA1LCAyMDIwLiBodHRwczovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy80NzU2MzYvaW50dWl0aXZlLWV4cGxhbmF0aW9uLW9mLW1pbmltdW0tY292YXJpYW5jZS1kZXRlcm1pbmFudC1tY2QuIAoKQnJpYW4gRy4gUGV0ZXJzb24gYW5kIFBldGVyIENhcmwgKDIwMTgpLiBQb3J0Zm9saW9BbmFseXRpY3M6IFBvcnRmb2xpbyBBbmFseXNpcywKICBJbmNsdWRpbmcgTnVtZXJpY2FsIE1ldGhvZHMgZm9yIE9wdGltaXphdGlvbiBvZiBQb3J0Zm9saW9zLiBSIHBhY2thZ2UgdmVyc2lvbiAxLjEuMC4KICBodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPVBvcnRmb2xpb0FuYWx5dGljcwoKQnJpYW4gRy4gUGV0ZXJzb24gYW5kIFBldGVyIENhcmwgKDIwMjApLiBQZXJmb3JtYW5jZUFuYWx5dGljczogRWNvbm9tZXRyaWMgVG9vbHMgZm9yCiAgUGVyZm9ybWFuY2UgYW5kIFJpc2sgQW5hbHlzaXMuIFIgcGFja2FnZSB2ZXJzaW9uIDIuMC40LgogIGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9UGVyZm9ybWFuY2VBbmFseXRpY3M=