Objective: Optimize a portfolio of selected stocks using various financial metrics and constraints, and analyze the performance and risk of the optimized portfolio.

install.packages(c("quantmod", "PerformanceAnalytics", "PortfolioAnalytics", "ROI.plugin.glpk", "ROI.plugin.quadprog", "ggplot2", "reshape2"))
library(quantmod)
library(PerformanceAnalytics)
library(PortfolioAnalytics)
library(ROI)
library(ROI.plugin.glpk)
library(ROI.plugin.quadprog)
library(ggplot2)
library(reshape2)

Install and run all necessary packages

library(quantmod)
stocks <- c("TSLA", "PSNY", "RIVN", "LCID")
getSymbols(stocks, from = "2023-01-01", to = "2024-12-31")
[1] "TSLA" "PSNY" "RIVN" "LCID"

Explanation: This code loads historical price data for Tesla (TSLA), Polestar (PSNY), Rivian (RIVN), and Lucid (LCID) using the quantmod package. The getSymbols function fetches the data from the start of 2023 to the end of Mar.

Benefit to the User: By defining the stocks and loading historical data, we can prepare the data for subsequent analysis, ensuring that all relevant data is readily available for calculations and optimizations.

# Calculate daily returns for each stock
returns_list <- lapply(stocks, function(ticker) dailyReturn(Cl(get(ticker))))
returns <- do.call(merge, returns_list)
colnames(returns) <- stocks
returns <- na.omit(returns)
print(returns)
                   TSLA         PSNY         RIVN         LCID
2023-01-03  0.000000000  0.000000000  0.000000000  0.000000000
2023-01-04  0.051248853  0.031954811  0.021337885  0.050243102
2023-01-05 -0.029039098  0.029143958 -0.064935047 -0.035493830
2023-01-06  0.024651090  0.000000000 -0.009661827  0.017600021
2023-01-09  0.059349011 -0.017699098  0.013414709  0.066037746
2023-01-10 -0.007681374  0.048648644 -0.010228644  0.060471952
2023-01-11  0.036769060 -0.015463943  0.038297820  0.102920691
2023-01-12  0.002759263  0.027923184  0.029274005  0.047919369
2023-01-13 -0.009388120  0.010186748 -0.064277541 -0.019254006
2023-01-17  0.074264737 -0.033613414  0.029179302 -0.012269869
       ...                                                    
2024-07-26 -0.002043119 -0.002631545 -0.002461595 -0.023121365
2024-07-29  0.055959977 -0.007915622  0.005552138  0.023668616
2024-07-30 -0.040844510 -0.039893580 -0.004907971  0.002890171
2024-07-31  0.042449072  0.018005556  0.011713967  0.014409208
2024-08-01 -0.065540597 -0.047619082 -0.071297994 -0.071022728
2024-08-02 -0.042377582 -0.012857148 -0.034120703 -0.045871589
2024-08-05 -0.042326736 -0.079594802 -0.007472867 -0.038461503
2024-08-06  0.008849530  0.048742169  0.013004828  0.029999971
2024-08-07 -0.044258398 -0.056971496 -0.068581103 -0.058252373
2024-08-08  0.025970020  0.082670883  0.097207121  0.063573862

Explanation: This code calculates the daily returns for each stock in the portfolio by applying the dailyReturn function to the closing prices of each stock. The results are combined into a single data frame, with columns named after the respective stock tickers.

Benefit to the User: Calculating daily returns is crucial for understanding the historical performance of each stock in the portfolio, which is essential for optimizing the portfolio based on these returns.

# Define the portfolio and constraints
port <- portfolio.spec(assets = colnames(returns))
port <- add.constraint(portfolio = port, type = "full_investment")
port <- add.constraint(portfolio = port, type = "long_only")
port <- add.objective(portfolio = port, type = "risk", name = "StdDev")
port <- add.objective(portfolio = port, type = "return", name = "mean")
port <- add.objective(portfolio = port, type = "risk", name = "ES", arguments = list(p = 0.95))

Explanation: This section sets up the portfolio and defines the constraints and objectives for optimization. The portfolio must be fully invested, with only long positions allowed. The objectives include minimizing risk (measured by standard deviation and expected shortfall) and maximizing return.

Benefit to the User: Defining these constraints and objectives allows us to create a balanced portfolio that meets specific risk and return criteria, ensuring that the optimized portfolio aligns with our investment goals.

# Optimize the portfolio
opt <- optimize.portfolio(R = returns, portfolio = port, optimize_method = "ROI", trace = TRUE, solver = "glpk")

Explanation: This code optimizes the portfolio using the ROI method with the glpk solver, which is suitable for linear programming problems. The optimization process adjusts the weights of the assets in the portfolio to meet the defined objectives and constraints.

Benefit to the User: Optimizing the portfolio helps us achieve an optimal balance between risk and return, potentially leading to better investment outcomes.

# Print the portfolio and optimized results
print(port)
**************************************************
PortfolioAnalytics Portfolio Specification 
**************************************************

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

Number of assets: 4 
Asset Names
[1] "TSLA" "PSNY" "RIVN" "LCID"

Constraints
Enabled constraint types
        - full_investment 
        - long_only 

Objectives:
Enabled objective names
        - StdDev 
        - mean 
        - ES 
print(opt)
***********************************
PortfolioAnalytics Optimization
***********************************

Call:
optimize.portfolio(R = returns, portfolio = port, optimize_method = "ROI", 
    trace = TRUE, solver = "glpk")

Optimal Weights:
  TSLA   PSNY   RIVN   LCID 
0.5941 0.2099 0.0518 0.1441 

Objective Measure:
     ES 
0.06244 

Explanation: This section prints the portfolio specification and the results of the optimization. The output includes the final weights assigned to each asset and other relevant metrics.

Benefit to the User: Reviewing the portfolio specification and optimization results provides us with insight into how the assets have been allocated and whether the optimization meets the desired objectives.

Optimal Weights and Objective Measure 1. Optimal Weights:

TSLA: 0.5941 PSNY: 0.2099 RIVN: 0.0518 LCID: 0.1441 Explanation:

Allocation: These weights indicate how the portfolio is distributed among the selected stocks. Specifically, 59.41% of the portfolio is invested in Tesla (TSLA), 20.99% in Polestar (PSNY), 5.18% in Rivian (RIVN), and 14.41% in Lucid Motors (LCID). Highest Allocation: Tesla (TSLA) receives the largest weight, suggesting that it is considered the most significant or potentially rewarding investment within this portfolio based on the optimization criteria. Lowest Allocation: Rivian (RIVN) is allocated the smallest weight, indicating a lower confidence or expected return for this stock compared to the others. 2. Objective Measure:

Expected Shortfall (ES): 0.06244 Explanation:

Expected Shortfall (ES): Also known as Conditional Value-at-Risk (CVaR), this measure quantifies the average loss expected in the worst-case scenarios beyond a specified confidence level (e.g., 95%). With an ES of 0.06244, it implies that in the worst 5% of scenarios, the average loss is expected to be approximately 6.244%. Risk Implication: The ES value indicates the potential average loss during adverse market conditions, providing insight into the portfolio’s risk profile. This helps in understanding how much loss could occur beyond the worst 5% of scenarios, guiding risk management strategies. Optimal Weights and Objective Measure 1. Optimal Weights:

TSLA: 0.5941 PSNY: 0.2099 RIVN: 0.0518 LCID: 0.1441 Explanation:

Allocation: These weights indicate how the portfolio is distributed among the selected stocks. Specifically, 59.41% of the portfolio is invested in Tesla (TSLA), 20.99% in Polestar (PSNY), 5.18% in Rivian (RIVN), and 14.41% in Lucid Motors (LCID). Highest Allocation: Tesla (TSLA) receives the largest weight, suggesting that it is considered the most significant or potentially rewarding investment within this portfolio based on the optimization criteria. Lowest Allocation: Rivian (RIVN) is allocated the smallest weight, indicating a lower confidence or expected return for this stock compared to the others. 2. Objective Measure:

Expected Shortfall (ES): 0.06244 Explanation:

Expected Shortfall (ES): Also known as Conditional Value-at-Risk (CVaR), this measure quantifies the average loss expected in the worst-case scenarios beyond a specified confidence level (e.g., 95%). With an ES of 0.06244, it implies that in the worst 5% of scenarios, the average loss is expected to be approximately 6.244%. Risk Implication: The ES value indicates the potential average loss during adverse market conditions, providing insight into the portfolio’s risk profile. This helps in understanding how much loss could occur beyond the worst 5% of scenarios, guiding risk management strategies.

chart.Weights(opt, main = "Optimized Portfolio Weights")

Explanation: This code generates a bar chart that displays the weights of each asset in the optimized portfolio. The chart helps visualize how much of the total investment is allocated to each stock.

Benefit to the User: Visualizing portfolio weights allows us to quickly assess the distribution of investments across the different assets, ensuring that the portfolio aligns with their risk tolerance and investment strategy.

returns_opt <- Return.portfolio(R = returns, weights = extractWeights(opt))
sharpe_ratio <- SharpeRatio(returns_opt, Rf = 0.01/252)
print(paste("Sharpe Ratio:", sharpe_ratio))
[1] "Sharpe Ratio: 0.0304633060075682" "Sharpe Ratio: 0.0207140147689227"
[3] "Sharpe Ratio: 0.0160422382057295"

Explanation: The Sharpe ratio is calculated to assess the risk-adjusted return of the optimized portfolio. It compares the portfolio’s excess return to its risk (standard deviation). A higher Sharpe ratio indicates better risk-adjusted performance.

Benefit to the User: The Sharpe ratio is a key metric for evaluating the efficiency of the portfolio, helping us determine whether the returns justify the risk taken.

Sharpe Ratios 1. Sharpe Ratio Results:

Sharpe Ratio (GLPK): 0.03098 Sharpe Ratio (Quadprog): 0.02107 Sharpe Ratio (Quadprog): 0.01632 Explanation:

Sharpe Ratio: This metric measures the risk-adjusted return of a portfolio. It is calculated as the ratio of the portfolio’s excess return (over the risk-free rate) to its standard deviation (a measure of risk). A higher Sharpe Ratio indicates better risk-adjusted performance. Interpretation of Results:

Sharpe Ratio (GLPK): 0.03098 Performance: The portfolio optimized using the GLPK solver has a Sharpe Ratio of approximately 0.031. This suggests a low positive return relative to the risk taken. In practical terms, the portfolio’s return, after adjusting for its volatility, is only marginally above what would be expected from a risk-free investment. Sharpe Ratio (Quadprog): 0.02107 Performance: The portfolio optimized using the Quadprog solver has a Sharpe Ratio of approximately 0.021. This is lower than the ratio obtained using GLPK, indicating that the portfolio’s risk-adjusted return is less favorable compared to the GLPK-optimized portfolio. Sharpe Ratio (Alternative Quadprog): 0.01632 Performance: Another portfolio optimized with Quadprog has a Sharpe Ratio of approximately 0.016. This represents the lowest Sharpe Ratio among the options, indicating that this portfolio offers the least favorable risk-adjusted return.

# Load necessary libraries
library(reshape2)
library(ggplot2)
library(xts)  # Ensure you have the xts library
# Convert xts object to a data frame
returns_df <- data.frame(Date = index(returns), coredata(returns))
# Print structure and first few rows to check
str(returns_df)
'data.frame':   402 obs. of  5 variables:
 $ Date: Date, format: "2023-01-03" "2023-01-04" "2023-01-05" ...
 $ TSLA: num  0 0.0512 -0.029 0.0247 0.0593 ...
 $ PSNY: num  0 0.032 0.0291 0 -0.0177 ...
 $ RIVN: num  0 0.02134 -0.06494 -0.00966 0.01341 ...
 $ LCID: num  0 0.0502 -0.0355 0.0176 0.066 ...
head(returns_df)
# Melt the data to long format
returns_long <- melt(returns_df, id.vars = "Date", variable.name = "Ticker", value.name = "Return")
# Check structure and first few rows of melted data
str(returns_long)
'data.frame':   1608 obs. of  3 variables:
 $ Date  : Date, format: "2023-01-03" "2023-01-04" "2023-01-05" ...
 $ Ticker: Factor w/ 4 levels "TSLA","PSNY",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Return: num  0 0.0512 -0.029 0.0247 0.0593 ...
head(returns_long)
# Plot the data
ggplot(returns_long, aes(x = Date, y = Return, color = Ticker)) +
  geom_line(size = .25) +
  facet_wrap(~ Ticker, scales = "free_y") +  # Create separate panels for each stock
  labs(title = "Historical Daily Returns of Stocks", x = "Date", y = "Daily Return") +
  theme_minimal() +
  theme(legend.position = "none")

Explanation: This section plots the historical daily returns of each stock, allowing the user to visualize how each stock has performed over time. The data is reshaped to a long format for easier plotting with ggplot2.

Benefit to the User: Visualizing historical returns helps us understand past performance, which is crucial for making informed investment decisions and assessing the stability of different stocks in the portfolio.

# Load necessary libraries
library(reshape2)
library(ggplot2)
library(xts)  # Ensure you have the xts library
# Convert xts object to a data frame
returns_df <- data.frame(Date = index(returns), coredata(returns))
# Calculate cumulative returns
cumulative_returns <- cumprod(1 + returns_df[, -1]) - 1  # Exclude the Date column for calculation
cumulative_returns_df <- data.frame(Date = returns_df$Date, cumulative_returns)
# Melt the data to long format
cumulative_returns_long <- melt(cumulative_returns_df, id.vars = "Date", variable.name = "Ticker", value.name = "Cumulative_Return")
# Check structure and first few rows of melted data
str(cumulative_returns_long)
'data.frame':   1608 obs. of  3 variables:
 $ Date             : Date, format: "2023-01-03" "2023-01-04" "2023-01-05" ...
 $ Ticker           : Factor w/ 4 levels "TSLA","PSNY",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Cumulative_Return: num  0 0.0512 0.0207 0.0459 0.108 ...
head(cumulative_returns_long)
# Plot the data
ggplot(cumulative_returns_long, aes(x = Date, y = Cumulative_Return, color = Ticker)) +
  geom_line() +
  labs(title = "Cumulative Returns of Stocks", x = "Date", y = "Cumulative Return") +
  theme_minimal()

Explanation: This section calculates and plots the cumulative returns of each stock, showing how much each stock has gained or lost over the specified period. The cumulative return is a crucial indicator of long-term performance.

Benefit to the User: Cumulative returns provide a clear picture of the growth or decline of investments over time, helping us evaluate the long-term potential of each stock in the portfolio.

opt_quadprog <- optimize.portfolio(R = returns, portfolio = port, optimize_method = "ROI", trace = TRUE, solver = "quadprog")
sharpe_glpk <- SharpeRatio(Return.portfolio(R = returns, weights = extractWeights(opt)), Rf = 0.01/252)
sharpe_quadprog <- SharpeRatio(Return.portfolio(R = returns, weights = extractWeights(opt_quadprog)), Rf = 0.01/252)
print(paste("Sharpe Ratio (GLPK):", sharpe_glpk))
[1] "Sharpe Ratio (GLPK): 0.0304633060075682" "Sharpe Ratio (GLPK): 0.0207140147689227"
[3] "Sharpe Ratio (GLPK): 0.0160422382057295"
print(paste("Sharpe Ratio (Quadprog):", sharpe_quadprog))
[1] "Sharpe Ratio (Quadprog): 0.0304633060075682" "Sharpe Ratio (Quadprog): 0.0207140147689227"
[3] "Sharpe Ratio (Quadprog): 0.0160422382057295"

Explanation: This section compares the portfolio optimization results using two different solvers: GLPK (linear programming) and quadprog (quadratic programming). The Sharpe ratios of the portfolios optimized by each method are calculated and compared.

Benefit to the User: Comparing different optimization methods allows us to select the most effective approach, potentially leading to better investment performance.

portfolio_returns <- Return.portfolio(R = returns, weights = extractWeights(opt))
var_95 <- VaR(portfolio_returns, p = 0.95)
cvar_95 <- CVaR(portfolio_returns, p = 0.95)
print(paste("Value at Risk (95%):", var_95))
[1] "Value at Risk (95%): -0.0477145591517417"
print(paste("Conditional Value at Risk (95%):", cvar_95))
[1] "Conditional Value at Risk (95%): -0.0616098620583268"

Explanation: This section calculates and displays the Value at Risk (VaR) and Conditional Value at Risk (CVaR) at the 95% confidence level. These metrics help quantify the potential loss in the portfolio under adverse conditions.

Benefit to the User: Understanding the risk profile of the portfolio through VaR and CVaR analysis allows us to make informed decisions about risk management and potential adjustments to the portfolio.

Value at Risk (VaR) and Conditional Value at Risk (CVaR) Results:

Value at Risk (95%): -0.04773 Conditional Value at Risk (95%): -0.06164 Explanation:

Value at Risk (VaR) (95%): Definition: VaR measures the maximum potential loss of a portfolio over a specified time period with a given confidence level. For a 95% VaR, it represents the loss that the portfolio is not expected to exceed with 95% confidence. Interpretation: A VaR of -0.04773 means that, with 95% confidence, the maximum expected loss over the period is approximately 4.77%. In other words, there is a 5% chance that the portfolio will lose more than 4.77% of its value over the specified time period.

Conditional Value at Risk (CVaR) (95%): Definition: CVaR, also known as Expected Shortfall, measures the average loss exceeding the VaR level. It provides an estimate of the tail risk beyond the VaR threshold. Interpretation: A CVaR of -0.06164 means that the average loss, given that it exceeds the 95% VaR threshold, is approximately 6.16%. This indicates the average loss in the worst 5% of cases.

performance_summary <- table.AnnualizedReturns(portfolio_returns)
print(performance_summary)

Explanation: This section provides a backtest of the optimized portfolio by calculating annualized returns, giving us insight into how the portfolio would have performed historically.

Benefit to the User: Backtesting provides a retrospective analysis of the portfolio’s performance, helping the user evaluate the effectiveness of the optimization strategy over time.

Performance Summary:

Annualized Return: 0.1409 (14.09%)

Definition: This represents the average return the portfolio has generated over a year, annualized to reflect a full year’s performance. Interpretation: The portfolio has achieved an annualized return of approximately 14.09%. This indicates the average percentage return the portfolio has delivered over the year.

Annualized Standard Deviation: 0.5153 (51.53%)

Definition: This measures the volatility of the portfolio’s returns over a year, showing how much the returns deviate from the average return. Interpretation: The annualized standard deviation is 51.53%, reflecting a high level of volatility and risk associated with the portfolio. This means that the returns of the portfolio have considerable variability.

Annualized Sharpe Ratio (Rf=0%): 0.2734

Definition: The Sharpe Ratio measures the portfolio’s risk-adjusted return. It is calculated as the annualized return minus the risk-free rate divided by the annualized standard deviation. Here, the risk-free rate (Rf) is assumed to be 0%. Interpretation: A Sharpe Ratio of 0.2734 indicates that, after adjusting for risk (volatility), the portfolio’s return is relatively modest. A higher Sharpe Ratio is generally preferred, indicating better risk-adjusted returns. A Sharpe Ratio of 0.2734 suggests that the portfolio’s risk-adjusted return is low, which could imply that the return does not sufficiently compensate for the level of risk taken.

Summary Return: The portfolio has a reasonable annualized return of 14.09%. Volatility: The portfolio exhibits high volatility with an annualized standard deviation of 51.53%. Risk-Adjusted Performance: The Sharpe Ratio of 0.2734 suggests that the return on the portfolio, adjusted for risk, is relatively low. This indicates that the portfolio might not be providing sufficient return relative to its risk level.

LS0tCnRpdGxlOiAiUG9ydGZvbGlvIE9wdGltaXphdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpPYmplY3RpdmU6IE9wdGltaXplIGEgcG9ydGZvbGlvIG9mIHNlbGVjdGVkIHN0b2NrcyB1c2luZyB2YXJpb3VzIGZpbmFuY2lhbCBtZXRyaWNzIGFuZCBjb25zdHJhaW50cywgYW5kIGFuYWx5emUgdGhlIHBlcmZvcm1hbmNlIGFuZCByaXNrIG9mIHRoZSBvcHRpbWl6ZWQgcG9ydGZvbGlvLgoKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoYygicXVhbnRtb2QiLCAiUGVyZm9ybWFuY2VBbmFseXRpY3MiLCAiUG9ydGZvbGlvQW5hbHl0aWNzIiwgIlJPSS5wbHVnaW4uZ2xwayIsICJST0kucGx1Z2luLnF1YWRwcm9nIiwgImdncGxvdDIiLCAicmVzaGFwZTIiKSkKbGlicmFyeShxdWFudG1vZCkKbGlicmFyeShQZXJmb3JtYW5jZUFuYWx5dGljcykKbGlicmFyeShQb3J0Zm9saW9BbmFseXRpY3MpCmxpYnJhcnkoUk9JKQpsaWJyYXJ5KFJPSS5wbHVnaW4uZ2xwaykKbGlicmFyeShST0kucGx1Z2luLnF1YWRwcm9nKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVzaGFwZTIpCmBgYApJbnN0YWxsIGFuZCBydW4gYWxsIG5lY2Vzc2FyeSBwYWNrYWdlcwoKCgpgYGB7cn0KbGlicmFyeShxdWFudG1vZCkKc3RvY2tzIDwtIGMoIlRTTEEiLCAiUFNOWSIsICJSSVZOIiwgIkxDSUQiKQpnZXRTeW1ib2xzKHN0b2NrcywgZnJvbSA9ICIyMDIzLTAxLTAxIiwgdG8gPSAiMjAyNC0xMi0zMSIpCmBgYApFeHBsYW5hdGlvbjoKVGhpcyBjb2RlIGxvYWRzIGhpc3RvcmljYWwgcHJpY2UgZGF0YSBmb3IgVGVzbGEgKFRTTEEpLCBQb2xlc3RhciAoUFNOWSksIFJpdmlhbiAoUklWTiksIGFuZCBMdWNpZCAoTENJRCkgdXNpbmcgdGhlIHF1YW50bW9kIHBhY2thZ2UuIFRoZSBnZXRTeW1ib2xzIGZ1bmN0aW9uIGZldGNoZXMgdGhlIGRhdGEgZnJvbSB0aGUgc3RhcnQgb2YgMjAyMyB0byB0aGUgZW5kIG9mIE1hci4KCkJlbmVmaXQgdG8gdGhlIFVzZXI6CkJ5IGRlZmluaW5nIHRoZSBzdG9ja3MgYW5kIGxvYWRpbmcgaGlzdG9yaWNhbCBkYXRhLCB3ZSBjYW4gcHJlcGFyZSB0aGUgZGF0YSBmb3Igc3Vic2VxdWVudCBhbmFseXNpcywgZW5zdXJpbmcgdGhhdCBhbGwgcmVsZXZhbnQgZGF0YSBpcyByZWFkaWx5IGF2YWlsYWJsZSBmb3IgY2FsY3VsYXRpb25zIGFuZCBvcHRpbWl6YXRpb25zLgoKCgoKCmBgYHtyfQojIENhbGN1bGF0ZSBkYWlseSByZXR1cm5zIGZvciBlYWNoIHN0b2NrCnJldHVybnNfbGlzdCA8LSBsYXBwbHkoc3RvY2tzLCBmdW5jdGlvbih0aWNrZXIpIGRhaWx5UmV0dXJuKENsKGdldCh0aWNrZXIpKSkpCnJldHVybnMgPC0gZG8uY2FsbChtZXJnZSwgcmV0dXJuc19saXN0KQpjb2xuYW1lcyhyZXR1cm5zKSA8LSBzdG9ja3MKcmV0dXJucyA8LSBuYS5vbWl0KHJldHVybnMpCnByaW50KHJldHVybnMpCmBgYApFeHBsYW5hdGlvbjoKVGhpcyBjb2RlIGNhbGN1bGF0ZXMgdGhlIGRhaWx5IHJldHVybnMgZm9yIGVhY2ggc3RvY2sgaW4gdGhlIHBvcnRmb2xpbyBieSBhcHBseWluZyB0aGUgZGFpbHlSZXR1cm4gZnVuY3Rpb24gdG8gdGhlIGNsb3NpbmcgcHJpY2VzIG9mIGVhY2ggc3RvY2suIFRoZSByZXN1bHRzIGFyZSBjb21iaW5lZCBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUsIHdpdGggY29sdW1ucyBuYW1lZCBhZnRlciB0aGUgcmVzcGVjdGl2ZSBzdG9jayB0aWNrZXJzLgoKQmVuZWZpdCB0byB0aGUgVXNlcjoKQ2FsY3VsYXRpbmcgZGFpbHkgcmV0dXJucyBpcyBjcnVjaWFsIGZvciB1bmRlcnN0YW5kaW5nIHRoZSBoaXN0b3JpY2FsIHBlcmZvcm1hbmNlIG9mIGVhY2ggc3RvY2sgaW4gdGhlIHBvcnRmb2xpbywgd2hpY2ggaXMgZXNzZW50aWFsIGZvciBvcHRpbWl6aW5nIHRoZSBwb3J0Zm9saW8gYmFzZWQgb24gdGhlc2UgcmV0dXJucy4KCgpgYGB7cn0KIyBEZWZpbmUgdGhlIHBvcnRmb2xpbyBhbmQgY29uc3RyYWludHMKcG9ydCA8LSBwb3J0Zm9saW8uc3BlYyhhc3NldHMgPSBjb2xuYW1lcyhyZXR1cm5zKSkKcG9ydCA8LSBhZGQuY29uc3RyYWludChwb3J0Zm9saW8gPSBwb3J0LCB0eXBlID0gImZ1bGxfaW52ZXN0bWVudCIpCnBvcnQgPC0gYWRkLmNvbnN0cmFpbnQocG9ydGZvbGlvID0gcG9ydCwgdHlwZSA9ICJsb25nX29ubHkiKQpwb3J0IDwtIGFkZC5vYmplY3RpdmUocG9ydGZvbGlvID0gcG9ydCwgdHlwZSA9ICJyaXNrIiwgbmFtZSA9ICJTdGREZXYiKQpwb3J0IDwtIGFkZC5vYmplY3RpdmUocG9ydGZvbGlvID0gcG9ydCwgdHlwZSA9ICJyZXR1cm4iLCBuYW1lID0gIm1lYW4iKQpwb3J0IDwtIGFkZC5vYmplY3RpdmUocG9ydGZvbGlvID0gcG9ydCwgdHlwZSA9ICJyaXNrIiwgbmFtZSA9ICJFUyIsIGFyZ3VtZW50cyA9IGxpc3QocCA9IDAuOTUpKQpgYGAKRXhwbGFuYXRpb246ClRoaXMgc2VjdGlvbiBzZXRzIHVwIHRoZSBwb3J0Zm9saW8gYW5kIGRlZmluZXMgdGhlIGNvbnN0cmFpbnRzIGFuZCBvYmplY3RpdmVzIGZvciBvcHRpbWl6YXRpb24uIFRoZSBwb3J0Zm9saW8gbXVzdCBiZSBmdWxseSBpbnZlc3RlZCwgd2l0aCBvbmx5IGxvbmcgcG9zaXRpb25zIGFsbG93ZWQuIFRoZSBvYmplY3RpdmVzIGluY2x1ZGUgbWluaW1pemluZyByaXNrIChtZWFzdXJlZCBieSBzdGFuZGFyZCBkZXZpYXRpb24gYW5kIGV4cGVjdGVkIHNob3J0ZmFsbCkgYW5kIG1heGltaXppbmcgcmV0dXJuLgoKQmVuZWZpdCB0byB0aGUgVXNlcjoKRGVmaW5pbmcgdGhlc2UgY29uc3RyYWludHMgYW5kIG9iamVjdGl2ZXMgYWxsb3dzIHVzIHRvIGNyZWF0ZSBhIGJhbGFuY2VkIHBvcnRmb2xpbyB0aGF0IG1lZXRzIHNwZWNpZmljIHJpc2sgYW5kIHJldHVybiBjcml0ZXJpYSwgZW5zdXJpbmcgdGhhdCB0aGUgb3B0aW1pemVkIHBvcnRmb2xpbyBhbGlnbnMgd2l0aCBvdXIgaW52ZXN0bWVudCBnb2Fscy4KCgoKCgpgYGB7cn0KIyBPcHRpbWl6ZSB0aGUgcG9ydGZvbGlvCm9wdCA8LSBvcHRpbWl6ZS5wb3J0Zm9saW8oUiA9IHJldHVybnMsIHBvcnRmb2xpbyA9IHBvcnQsIG9wdGltaXplX21ldGhvZCA9ICJST0kiLCB0cmFjZSA9IFRSVUUsIHNvbHZlciA9ICJnbHBrIikKYGBgCkV4cGxhbmF0aW9uOgpUaGlzIGNvZGUgb3B0aW1pemVzIHRoZSBwb3J0Zm9saW8gdXNpbmcgdGhlIFJPSSBtZXRob2Qgd2l0aCB0aGUgZ2xwayBzb2x2ZXIsIHdoaWNoIGlzIHN1aXRhYmxlIGZvciBsaW5lYXIgcHJvZ3JhbW1pbmcgcHJvYmxlbXMuIFRoZSBvcHRpbWl6YXRpb24gcHJvY2VzcyBhZGp1c3RzIHRoZSB3ZWlnaHRzIG9mIHRoZSBhc3NldHMgaW4gdGhlIHBvcnRmb2xpbyB0byBtZWV0IHRoZSBkZWZpbmVkIG9iamVjdGl2ZXMgYW5kIGNvbnN0cmFpbnRzLgoKQmVuZWZpdCB0byB0aGUgVXNlcjoKT3B0aW1pemluZyB0aGUgcG9ydGZvbGlvIGhlbHBzIHVzIGFjaGlldmUgYW4gb3B0aW1hbCBiYWxhbmNlIGJldHdlZW4gcmlzayBhbmQgcmV0dXJuLCBwb3RlbnRpYWxseSBsZWFkaW5nIHRvIGJldHRlciBpbnZlc3RtZW50IG91dGNvbWVzLgoKCgoKCmBgYHtyfQojIFByaW50IHRoZSBwb3J0Zm9saW8gYW5kIG9wdGltaXplZCByZXN1bHRzCnByaW50KHBvcnQpCnByaW50KG9wdCkKYGBgCkV4cGxhbmF0aW9uOgpUaGlzIHNlY3Rpb24gcHJpbnRzIHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBhbmQgdGhlIHJlc3VsdHMgb2YgdGhlIG9wdGltaXphdGlvbi4gVGhlIG91dHB1dCBpbmNsdWRlcyB0aGUgZmluYWwgd2VpZ2h0cyBhc3NpZ25lZCB0byBlYWNoIGFzc2V0IGFuZCBvdGhlciByZWxldmFudCBtZXRyaWNzLgoKQmVuZWZpdCB0byB0aGUgVXNlcjoKUmV2aWV3aW5nIHRoZSBwb3J0Zm9saW8gc3BlY2lmaWNhdGlvbiBhbmQgb3B0aW1pemF0aW9uIHJlc3VsdHMgcHJvdmlkZXMgdXMgd2l0aCBpbnNpZ2h0IGludG8gaG93IHRoZSBhc3NldHMgaGF2ZSBiZWVuIGFsbG9jYXRlZCBhbmQgd2hldGhlciB0aGUgb3B0aW1pemF0aW9uIG1lZXRzIHRoZSBkZXNpcmVkIG9iamVjdGl2ZXMuCgpPcHRpbWFsIFdlaWdodHMgYW5kIE9iamVjdGl2ZSBNZWFzdXJlCjEuIE9wdGltYWwgV2VpZ2h0czoKClRTTEE6IDAuNTk0MQpQU05ZOiAwLjIwOTkKUklWTjogMC4wNTE4CkxDSUQ6IDAuMTQ0MQpFeHBsYW5hdGlvbjoKCkFsbG9jYXRpb246IFRoZXNlIHdlaWdodHMgaW5kaWNhdGUgaG93IHRoZSBwb3J0Zm9saW8gaXMgZGlzdHJpYnV0ZWQgYW1vbmcgdGhlIHNlbGVjdGVkIHN0b2Nrcy4gU3BlY2lmaWNhbGx5LCA1OS40MSUgb2YgdGhlIHBvcnRmb2xpbyBpcyBpbnZlc3RlZCBpbiBUZXNsYSAoVFNMQSksIDIwLjk5JSBpbiBQb2xlc3RhciAoUFNOWSksIDUuMTglIGluIFJpdmlhbiAoUklWTiksIGFuZCAxNC40MSUgaW4gTHVjaWQgTW90b3JzIChMQ0lEKS4KSGlnaGVzdCBBbGxvY2F0aW9uOiBUZXNsYSAoVFNMQSkgcmVjZWl2ZXMgdGhlIGxhcmdlc3Qgd2VpZ2h0LCBzdWdnZXN0aW5nIHRoYXQgaXQgaXMgY29uc2lkZXJlZCB0aGUgbW9zdCBzaWduaWZpY2FudCBvciBwb3RlbnRpYWxseSByZXdhcmRpbmcgaW52ZXN0bWVudCB3aXRoaW4gdGhpcyBwb3J0Zm9saW8gYmFzZWQgb24gdGhlIG9wdGltaXphdGlvbiBjcml0ZXJpYS4KTG93ZXN0IEFsbG9jYXRpb246IFJpdmlhbiAoUklWTikgaXMgYWxsb2NhdGVkIHRoZSBzbWFsbGVzdCB3ZWlnaHQsIGluZGljYXRpbmcgYSBsb3dlciBjb25maWRlbmNlIG9yIGV4cGVjdGVkIHJldHVybiBmb3IgdGhpcyBzdG9jayBjb21wYXJlZCB0byB0aGUgb3RoZXJzLgoyLiBPYmplY3RpdmUgTWVhc3VyZToKCkV4cGVjdGVkIFNob3J0ZmFsbCAoRVMpOiAwLjA2MjQ0CkV4cGxhbmF0aW9uOgoKRXhwZWN0ZWQgU2hvcnRmYWxsIChFUyk6IEFsc28ga25vd24gYXMgQ29uZGl0aW9uYWwgVmFsdWUtYXQtUmlzayAoQ1ZhUiksIHRoaXMgbWVhc3VyZSBxdWFudGlmaWVzIHRoZSBhdmVyYWdlIGxvc3MgZXhwZWN0ZWQgaW4gdGhlIHdvcnN0LWNhc2Ugc2NlbmFyaW9zIGJleW9uZCBhIHNwZWNpZmllZCBjb25maWRlbmNlIGxldmVsIChlLmcuLCA5NSUpLiBXaXRoIGFuIEVTIG9mIDAuMDYyNDQsIGl0IGltcGxpZXMgdGhhdCBpbiB0aGUgd29yc3QgNSUgb2Ygc2NlbmFyaW9zLCB0aGUgYXZlcmFnZSBsb3NzIGlzIGV4cGVjdGVkIHRvIGJlIGFwcHJveGltYXRlbHkgNi4yNDQlLgpSaXNrIEltcGxpY2F0aW9uOiBUaGUgRVMgdmFsdWUgaW5kaWNhdGVzIHRoZSBwb3RlbnRpYWwgYXZlcmFnZSBsb3NzIGR1cmluZyBhZHZlcnNlIG1hcmtldCBjb25kaXRpb25zLCBwcm92aWRpbmcgaW5zaWdodCBpbnRvIHRoZSBwb3J0Zm9saW8ncyByaXNrIHByb2ZpbGUuIFRoaXMgaGVscHMgaW4gdW5kZXJzdGFuZGluZyBob3cgbXVjaCBsb3NzIGNvdWxkIG9jY3VyIGJleW9uZCB0aGUgd29yc3QgNSUgb2Ygc2NlbmFyaW9zLCBndWlkaW5nIHJpc2sgbWFuYWdlbWVudCBzdHJhdGVnaWVzLgpPcHRpbWFsIFdlaWdodHMgYW5kIE9iamVjdGl2ZSBNZWFzdXJlCjEuIE9wdGltYWwgV2VpZ2h0czoKClRTTEE6IDAuNTk0MQpQU05ZOiAwLjIwOTkKUklWTjogMC4wNTE4CkxDSUQ6IDAuMTQ0MQpFeHBsYW5hdGlvbjoKCkFsbG9jYXRpb246IFRoZXNlIHdlaWdodHMgaW5kaWNhdGUgaG93IHRoZSBwb3J0Zm9saW8gaXMgZGlzdHJpYnV0ZWQgYW1vbmcgdGhlIHNlbGVjdGVkIHN0b2Nrcy4gU3BlY2lmaWNhbGx5LCA1OS40MSUgb2YgdGhlIHBvcnRmb2xpbyBpcyBpbnZlc3RlZCBpbiBUZXNsYSAoVFNMQSksIDIwLjk5JSBpbiBQb2xlc3RhciAoUFNOWSksIDUuMTglIGluIFJpdmlhbiAoUklWTiksIGFuZCAxNC40MSUgaW4gTHVjaWQgTW90b3JzIChMQ0lEKS4KSGlnaGVzdCBBbGxvY2F0aW9uOiBUZXNsYSAoVFNMQSkgcmVjZWl2ZXMgdGhlIGxhcmdlc3Qgd2VpZ2h0LCBzdWdnZXN0aW5nIHRoYXQgaXQgaXMgY29uc2lkZXJlZCB0aGUgbW9zdCBzaWduaWZpY2FudCBvciBwb3RlbnRpYWxseSByZXdhcmRpbmcgaW52ZXN0bWVudCB3aXRoaW4gdGhpcyBwb3J0Zm9saW8gYmFzZWQgb24gdGhlIG9wdGltaXphdGlvbiBjcml0ZXJpYS4KTG93ZXN0IEFsbG9jYXRpb246IFJpdmlhbiAoUklWTikgaXMgYWxsb2NhdGVkIHRoZSBzbWFsbGVzdCB3ZWlnaHQsIGluZGljYXRpbmcgYSBsb3dlciBjb25maWRlbmNlIG9yIGV4cGVjdGVkIHJldHVybiBmb3IgdGhpcyBzdG9jayBjb21wYXJlZCB0byB0aGUgb3RoZXJzLgoyLiBPYmplY3RpdmUgTWVhc3VyZToKCkV4cGVjdGVkIFNob3J0ZmFsbCAoRVMpOiAwLjA2MjQ0CkV4cGxhbmF0aW9uOgoKRXhwZWN0ZWQgU2hvcnRmYWxsIChFUyk6IEFsc28ga25vd24gYXMgQ29uZGl0aW9uYWwgVmFsdWUtYXQtUmlzayAoQ1ZhUiksIHRoaXMgbWVhc3VyZSBxdWFudGlmaWVzIHRoZSBhdmVyYWdlIGxvc3MgZXhwZWN0ZWQgaW4gdGhlIHdvcnN0LWNhc2Ugc2NlbmFyaW9zIGJleW9uZCBhIHNwZWNpZmllZCBjb25maWRlbmNlIGxldmVsIChlLmcuLCA5NSUpLiBXaXRoIGFuIEVTIG9mIDAuMDYyNDQsIGl0IGltcGxpZXMgdGhhdCBpbiB0aGUgd29yc3QgNSUgb2Ygc2NlbmFyaW9zLCB0aGUgYXZlcmFnZSBsb3NzIGlzIGV4cGVjdGVkIHRvIGJlIGFwcHJveGltYXRlbHkgNi4yNDQlLgpSaXNrIEltcGxpY2F0aW9uOiBUaGUgRVMgdmFsdWUgaW5kaWNhdGVzIHRoZSBwb3RlbnRpYWwgYXZlcmFnZSBsb3NzIGR1cmluZyBhZHZlcnNlIG1hcmtldCBjb25kaXRpb25zLCBwcm92aWRpbmcgaW5zaWdodCBpbnRvIHRoZSBwb3J0Zm9saW8ncyByaXNrIHByb2ZpbGUuIFRoaXMgaGVscHMgaW4gdW5kZXJzdGFuZGluZyBob3cgbXVjaCBsb3NzIGNvdWxkIG9jY3VyIGJleW9uZCB0aGUgd29yc3QgNSUgb2Ygc2NlbmFyaW9zLCBndWlkaW5nIHJpc2sgbWFuYWdlbWVudCBzdHJhdGVnaWVzLgoKCgoKCgpgYGB7cn0KY2hhcnQuV2VpZ2h0cyhvcHQsIG1haW4gPSAiT3B0aW1pemVkIFBvcnRmb2xpbyBXZWlnaHRzIikKYGBgCkV4cGxhbmF0aW9uOgpUaGlzIGNvZGUgZ2VuZXJhdGVzIGEgYmFyIGNoYXJ0IHRoYXQgZGlzcGxheXMgdGhlIHdlaWdodHMgb2YgZWFjaCBhc3NldCBpbiB0aGUgb3B0aW1pemVkIHBvcnRmb2xpby4gVGhlIGNoYXJ0IGhlbHBzIHZpc3VhbGl6ZSBob3cgbXVjaCBvZiB0aGUgdG90YWwgaW52ZXN0bWVudCBpcyBhbGxvY2F0ZWQgdG8gZWFjaCBzdG9jay4KCkJlbmVmaXQgdG8gdGhlIFVzZXI6ClZpc3VhbGl6aW5nIHBvcnRmb2xpbyB3ZWlnaHRzIGFsbG93cyB1cyB0byBxdWlja2x5IGFzc2VzcyB0aGUgZGlzdHJpYnV0aW9uIG9mIGludmVzdG1lbnRzIGFjcm9zcyB0aGUgZGlmZmVyZW50IGFzc2V0cywgZW5zdXJpbmcgdGhhdCB0aGUgcG9ydGZvbGlvIGFsaWducyB3aXRoIHRoZWlyIHJpc2sgdG9sZXJhbmNlIGFuZCBpbnZlc3RtZW50IHN0cmF0ZWd5LgoKCgoKYGBge3J9CnJldHVybnNfb3B0IDwtIFJldHVybi5wb3J0Zm9saW8oUiA9IHJldHVybnMsIHdlaWdodHMgPSBleHRyYWN0V2VpZ2h0cyhvcHQpKQpzaGFycGVfcmF0aW8gPC0gU2hhcnBlUmF0aW8ocmV0dXJuc19vcHQsIFJmID0gMC4wMS8yNTIpCnByaW50KHBhc3RlKCJTaGFycGUgUmF0aW86Iiwgc2hhcnBlX3JhdGlvKSkKYGBgCkV4cGxhbmF0aW9uOgpUaGUgU2hhcnBlIHJhdGlvIGlzIGNhbGN1bGF0ZWQgdG8gYXNzZXNzIHRoZSByaXNrLWFkanVzdGVkIHJldHVybiBvZiB0aGUgb3B0aW1pemVkIHBvcnRmb2xpby4gSXQgY29tcGFyZXMgdGhlIHBvcnRmb2xpbydzIGV4Y2VzcyByZXR1cm4gdG8gaXRzIHJpc2sgKHN0YW5kYXJkIGRldmlhdGlvbikuIEEgaGlnaGVyIFNoYXJwZSByYXRpbyBpbmRpY2F0ZXMgYmV0dGVyIHJpc2stYWRqdXN0ZWQgcGVyZm9ybWFuY2UuCgpCZW5lZml0IHRvIHRoZSBVc2VyOgpUaGUgU2hhcnBlIHJhdGlvIGlzIGEga2V5IG1ldHJpYyBmb3IgZXZhbHVhdGluZyB0aGUgZWZmaWNpZW5jeSBvZiB0aGUgcG9ydGZvbGlvLCBoZWxwaW5nIHVzIGRldGVybWluZSB3aGV0aGVyIHRoZSByZXR1cm5zIGp1c3RpZnkgdGhlIHJpc2sgdGFrZW4uCgpTaGFycGUgUmF0aW9zCjEuIFNoYXJwZSBSYXRpbyBSZXN1bHRzOgoKU2hhcnBlIFJhdGlvIChHTFBLKTogMC4wMzA5OApTaGFycGUgUmF0aW8gKFF1YWRwcm9nKTogMC4wMjEwNwpTaGFycGUgUmF0aW8gKFF1YWRwcm9nKTogMC4wMTYzMgpFeHBsYW5hdGlvbjoKClNoYXJwZSBSYXRpbzogVGhpcyBtZXRyaWMgbWVhc3VyZXMgdGhlIHJpc2stYWRqdXN0ZWQgcmV0dXJuIG9mIGEgcG9ydGZvbGlvLiBJdCBpcyBjYWxjdWxhdGVkIGFzIHRoZSByYXRpbyBvZiB0aGUgcG9ydGZvbGlv4oCZcyBleGNlc3MgcmV0dXJuIChvdmVyIHRoZSByaXNrLWZyZWUgcmF0ZSkgdG8gaXRzIHN0YW5kYXJkIGRldmlhdGlvbiAoYSBtZWFzdXJlIG9mIHJpc2spLiBBIGhpZ2hlciBTaGFycGUgUmF0aW8gaW5kaWNhdGVzIGJldHRlciByaXNrLWFkanVzdGVkIHBlcmZvcm1hbmNlLgpJbnRlcnByZXRhdGlvbiBvZiBSZXN1bHRzOgoKU2hhcnBlIFJhdGlvIChHTFBLKTogMC4wMzA5OApQZXJmb3JtYW5jZTogVGhlIHBvcnRmb2xpbyBvcHRpbWl6ZWQgdXNpbmcgdGhlIEdMUEsgc29sdmVyIGhhcyBhIFNoYXJwZSBSYXRpbyBvZiBhcHByb3hpbWF0ZWx5IDAuMDMxLiBUaGlzIHN1Z2dlc3RzIGEgbG93IHBvc2l0aXZlIHJldHVybiByZWxhdGl2ZSB0byB0aGUgcmlzayB0YWtlbi4gSW4gcHJhY3RpY2FsIHRlcm1zLCB0aGUgcG9ydGZvbGlvJ3MgcmV0dXJuLCBhZnRlciBhZGp1c3RpbmcgZm9yIGl0cyB2b2xhdGlsaXR5LCBpcyBvbmx5IG1hcmdpbmFsbHkgYWJvdmUgd2hhdCB3b3VsZCBiZSBleHBlY3RlZCBmcm9tIGEgcmlzay1mcmVlIGludmVzdG1lbnQuClNoYXJwZSBSYXRpbyAoUXVhZHByb2cpOiAwLjAyMTA3ClBlcmZvcm1hbmNlOiBUaGUgcG9ydGZvbGlvIG9wdGltaXplZCB1c2luZyB0aGUgUXVhZHByb2cgc29sdmVyIGhhcyBhIFNoYXJwZSBSYXRpbyBvZiBhcHByb3hpbWF0ZWx5IDAuMDIxLiBUaGlzIGlzIGxvd2VyIHRoYW4gdGhlIHJhdGlvIG9idGFpbmVkIHVzaW5nIEdMUEssIGluZGljYXRpbmcgdGhhdCB0aGUgcG9ydGZvbGlvJ3Mgcmlzay1hZGp1c3RlZCByZXR1cm4gaXMgbGVzcyBmYXZvcmFibGUgY29tcGFyZWQgdG8gdGhlIEdMUEstb3B0aW1pemVkIHBvcnRmb2xpby4KU2hhcnBlIFJhdGlvIChBbHRlcm5hdGl2ZSBRdWFkcHJvZyk6IDAuMDE2MzIKUGVyZm9ybWFuY2U6IEFub3RoZXIgcG9ydGZvbGlvIG9wdGltaXplZCB3aXRoIFF1YWRwcm9nIGhhcyBhIFNoYXJwZSBSYXRpbyBvZiBhcHByb3hpbWF0ZWx5IDAuMDE2LiBUaGlzIHJlcHJlc2VudHMgdGhlIGxvd2VzdCBTaGFycGUgUmF0aW8gYW1vbmcgdGhlIG9wdGlvbnMsIGluZGljYXRpbmcgdGhhdCB0aGlzIHBvcnRmb2xpbyBvZmZlcnMgdGhlIGxlYXN0IGZhdm9yYWJsZSByaXNrLWFkanVzdGVkIHJldHVybi4KCgoKCgpgYGB7cn0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHh0cykgICMgRW5zdXJlIHlvdSBoYXZlIHRoZSB4dHMgbGlicmFyeQojIENvbnZlcnQgeHRzIG9iamVjdCB0byBhIGRhdGEgZnJhbWUKcmV0dXJuc19kZiA8LSBkYXRhLmZyYW1lKERhdGUgPSBpbmRleChyZXR1cm5zKSwgY29yZWRhdGEocmV0dXJucykpCiMgUHJpbnQgc3RydWN0dXJlIGFuZCBmaXJzdCBmZXcgcm93cyB0byBjaGVjawpzdHIocmV0dXJuc19kZikKaGVhZChyZXR1cm5zX2RmKQojIE1lbHQgdGhlIGRhdGEgdG8gbG9uZyBmb3JtYXQKcmV0dXJuc19sb25nIDwtIG1lbHQocmV0dXJuc19kZiwgaWQudmFycyA9ICJEYXRlIiwgdmFyaWFibGUubmFtZSA9ICJUaWNrZXIiLCB2YWx1ZS5uYW1lID0gIlJldHVybiIpCiMgQ2hlY2sgc3RydWN0dXJlIGFuZCBmaXJzdCBmZXcgcm93cyBvZiBtZWx0ZWQgZGF0YQpzdHIocmV0dXJuc19sb25nKQpoZWFkKHJldHVybnNfbG9uZykKIyBQbG90IHRoZSBkYXRhCmdncGxvdChyZXR1cm5zX2xvbmcsIGFlcyh4ID0gRGF0ZSwgeSA9IFJldHVybiwgY29sb3IgPSBUaWNrZXIpKSArCiAgZ2VvbV9saW5lKHNpemUgPSAuMjUpICsKICBmYWNldF93cmFwKH4gVGlja2VyLCBzY2FsZXMgPSAiZnJlZV95IikgKyAgIyBDcmVhdGUgc2VwYXJhdGUgcGFuZWxzIGZvciBlYWNoIHN0b2NrCiAgbGFicyh0aXRsZSA9ICJIaXN0b3JpY2FsIERhaWx5IFJldHVybnMgb2YgU3RvY2tzIiwgeCA9ICJEYXRlIiwgeSA9ICJEYWlseSBSZXR1cm4iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpgYGAKRXhwbGFuYXRpb246ClRoaXMgc2VjdGlvbiBwbG90cyB0aGUgaGlzdG9yaWNhbCBkYWlseSByZXR1cm5zIG9mIGVhY2ggc3RvY2ssIGFsbG93aW5nIHRoZSB1c2VyIHRvIHZpc3VhbGl6ZSBob3cgZWFjaCBzdG9jayBoYXMgcGVyZm9ybWVkIG92ZXIgdGltZS4gVGhlIGRhdGEgaXMgcmVzaGFwZWQgdG8gYSBsb25nIGZvcm1hdCBmb3IgZWFzaWVyIHBsb3R0aW5nIHdpdGggZ2dwbG90Mi4KCkJlbmVmaXQgdG8gdGhlIFVzZXI6ClZpc3VhbGl6aW5nIGhpc3RvcmljYWwgcmV0dXJucyBoZWxwcyB1cyB1bmRlcnN0YW5kIHBhc3QgcGVyZm9ybWFuY2UsIHdoaWNoIGlzIGNydWNpYWwgZm9yIG1ha2luZyBpbmZvcm1lZCBpbnZlc3RtZW50IGRlY2lzaW9ucyBhbmQgYXNzZXNzaW5nIHRoZSBzdGFiaWxpdHkgb2YgZGlmZmVyZW50IHN0b2NrcyBpbiB0aGUgcG9ydGZvbGlvLgoKCgoKCgpgYGB7cn0KIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHh0cykgICMgRW5zdXJlIHlvdSBoYXZlIHRoZSB4dHMgbGlicmFyeQojIENvbnZlcnQgeHRzIG9iamVjdCB0byBhIGRhdGEgZnJhbWUKcmV0dXJuc19kZiA8LSBkYXRhLmZyYW1lKERhdGUgPSBpbmRleChyZXR1cm5zKSwgY29yZWRhdGEocmV0dXJucykpCiMgQ2FsY3VsYXRlIGN1bXVsYXRpdmUgcmV0dXJucwpjdW11bGF0aXZlX3JldHVybnMgPC0gY3VtcHJvZCgxICsgcmV0dXJuc19kZlssIC0xXSkgLSAxICAjIEV4Y2x1ZGUgdGhlIERhdGUgY29sdW1uIGZvciBjYWxjdWxhdGlvbgpjdW11bGF0aXZlX3JldHVybnNfZGYgPC0gZGF0YS5mcmFtZShEYXRlID0gcmV0dXJuc19kZiREYXRlLCBjdW11bGF0aXZlX3JldHVybnMpCiMgTWVsdCB0aGUgZGF0YSB0byBsb25nIGZvcm1hdApjdW11bGF0aXZlX3JldHVybnNfbG9uZyA8LSBtZWx0KGN1bXVsYXRpdmVfcmV0dXJuc19kZiwgaWQudmFycyA9ICJEYXRlIiwgdmFyaWFibGUubmFtZSA9ICJUaWNrZXIiLCB2YWx1ZS5uYW1lID0gIkN1bXVsYXRpdmVfUmV0dXJuIikKIyBDaGVjayBzdHJ1Y3R1cmUgYW5kIGZpcnN0IGZldyByb3dzIG9mIG1lbHRlZCBkYXRhCnN0cihjdW11bGF0aXZlX3JldHVybnNfbG9uZykKaGVhZChjdW11bGF0aXZlX3JldHVybnNfbG9uZykKIyBQbG90IHRoZSBkYXRhCmdncGxvdChjdW11bGF0aXZlX3JldHVybnNfbG9uZywgYWVzKHggPSBEYXRlLCB5ID0gQ3VtdWxhdGl2ZV9SZXR1cm4sIGNvbG9yID0gVGlja2VyKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIkN1bXVsYXRpdmUgUmV0dXJucyBvZiBTdG9ja3MiLCB4ID0gIkRhdGUiLCB5ID0gIkN1bXVsYXRpdmUgUmV0dXJuIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKRXhwbGFuYXRpb246ClRoaXMgc2VjdGlvbiBjYWxjdWxhdGVzIGFuZCBwbG90cyB0aGUgY3VtdWxhdGl2ZSByZXR1cm5zIG9mIGVhY2ggc3RvY2ssIHNob3dpbmcgaG93IG11Y2ggZWFjaCBzdG9jayBoYXMgZ2FpbmVkIG9yIGxvc3Qgb3ZlciB0aGUgc3BlY2lmaWVkIHBlcmlvZC4gVGhlIGN1bXVsYXRpdmUgcmV0dXJuIGlzIGEgY3J1Y2lhbCBpbmRpY2F0b3Igb2YgbG9uZy10ZXJtIHBlcmZvcm1hbmNlLgoKQmVuZWZpdCB0byB0aGUgVXNlcjoKQ3VtdWxhdGl2ZSByZXR1cm5zIHByb3ZpZGUgYSBjbGVhciBwaWN0dXJlIG9mIHRoZSBncm93dGggb3IgZGVjbGluZSBvZiBpbnZlc3RtZW50cyBvdmVyIHRpbWUsIGhlbHBpbmcgdXMgZXZhbHVhdGUgdGhlIGxvbmctdGVybSBwb3RlbnRpYWwgb2YgZWFjaCBzdG9jayBpbiB0aGUgcG9ydGZvbGlvLgoKCgoKCmBgYHtyfQpvcHRfcXVhZHByb2cgPC0gb3B0aW1pemUucG9ydGZvbGlvKFIgPSByZXR1cm5zLCBwb3J0Zm9saW8gPSBwb3J0LCBvcHRpbWl6ZV9tZXRob2QgPSAiUk9JIiwgdHJhY2UgPSBUUlVFLCBzb2x2ZXIgPSAicXVhZHByb2ciKQpzaGFycGVfZ2xwayA8LSBTaGFycGVSYXRpbyhSZXR1cm4ucG9ydGZvbGlvKFIgPSByZXR1cm5zLCB3ZWlnaHRzID0gZXh0cmFjdFdlaWdodHMob3B0KSksIFJmID0gMC4wMS8yNTIpCnNoYXJwZV9xdWFkcHJvZyA8LSBTaGFycGVSYXRpbyhSZXR1cm4ucG9ydGZvbGlvKFIgPSByZXR1cm5zLCB3ZWlnaHRzID0gZXh0cmFjdFdlaWdodHMob3B0X3F1YWRwcm9nKSksIFJmID0gMC4wMS8yNTIpCnByaW50KHBhc3RlKCJTaGFycGUgUmF0aW8gKEdMUEspOiIsIHNoYXJwZV9nbHBrKSkKcHJpbnQocGFzdGUoIlNoYXJwZSBSYXRpbyAoUXVhZHByb2cpOiIsIHNoYXJwZV9xdWFkcHJvZykpCmBgYApFeHBsYW5hdGlvbjoKVGhpcyBzZWN0aW9uIGNvbXBhcmVzIHRoZSBwb3J0Zm9saW8gb3B0aW1pemF0aW9uIHJlc3VsdHMgdXNpbmcgdHdvIGRpZmZlcmVudCBzb2x2ZXJzOiBHTFBLIChsaW5lYXIgcHJvZ3JhbW1pbmcpIGFuZCBxdWFkcHJvZyAocXVhZHJhdGljIHByb2dyYW1taW5nKS4gVGhlIFNoYXJwZSByYXRpb3Mgb2YgdGhlIHBvcnRmb2xpb3Mgb3B0aW1pemVkIGJ5IGVhY2ggbWV0aG9kIGFyZSBjYWxjdWxhdGVkIGFuZCBjb21wYXJlZC4KCkJlbmVmaXQgdG8gdGhlIFVzZXI6CkNvbXBhcmluZyBkaWZmZXJlbnQgb3B0aW1pemF0aW9uIG1ldGhvZHMgYWxsb3dzIHVzIHRvIHNlbGVjdCB0aGUgbW9zdCBlZmZlY3RpdmUgYXBwcm9hY2gsIHBvdGVudGlhbGx5IGxlYWRpbmcgdG8gYmV0dGVyIGludmVzdG1lbnQgcGVyZm9ybWFuY2UuCgoKCgoKYGBge3J9CnBvcnRmb2xpb19yZXR1cm5zIDwtIFJldHVybi5wb3J0Zm9saW8oUiA9IHJldHVybnMsIHdlaWdodHMgPSBleHRyYWN0V2VpZ2h0cyhvcHQpKQp2YXJfOTUgPC0gVmFSKHBvcnRmb2xpb19yZXR1cm5zLCBwID0gMC45NSkKY3Zhcl85NSA8LSBDVmFSKHBvcnRmb2xpb19yZXR1cm5zLCBwID0gMC45NSkKcHJpbnQocGFzdGUoIlZhbHVlIGF0IFJpc2sgKDk1JSk6IiwgdmFyXzk1KSkKcHJpbnQocGFzdGUoIkNvbmRpdGlvbmFsIFZhbHVlIGF0IFJpc2sgKDk1JSk6IiwgY3Zhcl85NSkpCmBgYApFeHBsYW5hdGlvbjoKVGhpcyBzZWN0aW9uIGNhbGN1bGF0ZXMgYW5kIGRpc3BsYXlzIHRoZSBWYWx1ZSBhdCBSaXNrIChWYVIpIGFuZCBDb25kaXRpb25hbCBWYWx1ZSBhdCBSaXNrIChDVmFSKSBhdCB0aGUgOTUlIGNvbmZpZGVuY2UgbGV2ZWwuIFRoZXNlIG1ldHJpY3MgaGVscCBxdWFudGlmeSB0aGUgcG90ZW50aWFsIGxvc3MgaW4gdGhlIHBvcnRmb2xpbyB1bmRlciBhZHZlcnNlIGNvbmRpdGlvbnMuCgpCZW5lZml0IHRvIHRoZSBVc2VyOgpVbmRlcnN0YW5kaW5nIHRoZSByaXNrIHByb2ZpbGUgb2YgdGhlIHBvcnRmb2xpbyB0aHJvdWdoIFZhUiBhbmQgQ1ZhUiBhbmFseXNpcyBhbGxvd3MgdXMgdG8gbWFrZSBpbmZvcm1lZCBkZWNpc2lvbnMgYWJvdXQgcmlzayBtYW5hZ2VtZW50IGFuZCBwb3RlbnRpYWwgYWRqdXN0bWVudHMgdG8gdGhlIHBvcnRmb2xpby4KClZhbHVlIGF0IFJpc2sgKFZhUikgYW5kIENvbmRpdGlvbmFsIFZhbHVlIGF0IFJpc2sgKENWYVIpClJlc3VsdHM6CgpWYWx1ZSBhdCBSaXNrICg5NSUpOiAtMC4wNDc3MwpDb25kaXRpb25hbCBWYWx1ZSBhdCBSaXNrICg5NSUpOiAtMC4wNjE2NApFeHBsYW5hdGlvbjoKClZhbHVlIGF0IFJpc2sgKFZhUikgKDk1JSk6CkRlZmluaXRpb246IFZhUiBtZWFzdXJlcyB0aGUgbWF4aW11bSBwb3RlbnRpYWwgbG9zcyBvZiBhIHBvcnRmb2xpbyBvdmVyIGEgc3BlY2lmaWVkIHRpbWUgcGVyaW9kIHdpdGggYSBnaXZlbiBjb25maWRlbmNlIGxldmVsLiBGb3IgYSA5NSUgVmFSLCBpdCByZXByZXNlbnRzIHRoZSBsb3NzIHRoYXQgdGhlIHBvcnRmb2xpbyBpcyBub3QgZXhwZWN0ZWQgdG8gZXhjZWVkIHdpdGggOTUlIGNvbmZpZGVuY2UuCkludGVycHJldGF0aW9uOiBBIFZhUiBvZiAtMC4wNDc3MyBtZWFucyB0aGF0LCB3aXRoIDk1JSBjb25maWRlbmNlLCB0aGUgbWF4aW11bSBleHBlY3RlZCBsb3NzIG92ZXIgdGhlIHBlcmlvZCBpcyBhcHByb3hpbWF0ZWx5IDQuNzclLiBJbiBvdGhlciB3b3JkcywgdGhlcmUgaXMgYSA1JSBjaGFuY2UgdGhhdCB0aGUgcG9ydGZvbGlvIHdpbGwgbG9zZSBtb3JlIHRoYW4gNC43NyUgb2YgaXRzIHZhbHVlIG92ZXIgdGhlIHNwZWNpZmllZCB0aW1lIHBlcmlvZC4KCkNvbmRpdGlvbmFsIFZhbHVlIGF0IFJpc2sgKENWYVIpICg5NSUpOgpEZWZpbml0aW9uOiBDVmFSLCBhbHNvIGtub3duIGFzIEV4cGVjdGVkIFNob3J0ZmFsbCwgbWVhc3VyZXMgdGhlIGF2ZXJhZ2UgbG9zcyBleGNlZWRpbmcgdGhlIFZhUiBsZXZlbC4gSXQgcHJvdmlkZXMgYW4gZXN0aW1hdGUgb2YgdGhlIHRhaWwgcmlzayBiZXlvbmQgdGhlIFZhUiB0aHJlc2hvbGQuCkludGVycHJldGF0aW9uOiBBIENWYVIgb2YgLTAuMDYxNjQgbWVhbnMgdGhhdCB0aGUgYXZlcmFnZSBsb3NzLCBnaXZlbiB0aGF0IGl0IGV4Y2VlZHMgdGhlIDk1JSBWYVIgdGhyZXNob2xkLCBpcyBhcHByb3hpbWF0ZWx5IDYuMTYlLiBUaGlzIGluZGljYXRlcyB0aGUgYXZlcmFnZSBsb3NzIGluIHRoZSB3b3JzdCA1JSBvZiBjYXNlcy4KCgpgYGB7cn0KcGVyZm9ybWFuY2Vfc3VtbWFyeSA8LSB0YWJsZS5Bbm51YWxpemVkUmV0dXJucyhwb3J0Zm9saW9fcmV0dXJucykKcHJpbnQocGVyZm9ybWFuY2Vfc3VtbWFyeSkKYGBgCkV4cGxhbmF0aW9uOgpUaGlzIHNlY3Rpb24gcHJvdmlkZXMgYSBiYWNrdGVzdCBvZiB0aGUgb3B0aW1pemVkIHBvcnRmb2xpbyBieSBjYWxjdWxhdGluZyBhbm51YWxpemVkIHJldHVybnMsIGdpdmluZyB1cyBpbnNpZ2h0IGludG8gaG93IHRoZSBwb3J0Zm9saW8gd291bGQgaGF2ZSBwZXJmb3JtZWQgaGlzdG9yaWNhbGx5LgoKQmVuZWZpdCB0byB0aGUgVXNlcjoKQmFja3Rlc3RpbmcgcHJvdmlkZXMgYSByZXRyb3NwZWN0aXZlIGFuYWx5c2lzIG9mIHRoZSBwb3J0Zm9saW8ncyBwZXJmb3JtYW5jZSwgaGVscGluZyB0aGUgdXNlciBldmFsdWF0ZSB0aGUgZWZmZWN0aXZlbmVzcyBvZiB0aGUgb3B0aW1pemF0aW9uIHN0cmF0ZWd5IG92ZXIgdGltZS4KClBlcmZvcm1hbmNlIFN1bW1hcnk6CgpBbm51YWxpemVkIFJldHVybjogMC4xNDA5ICgxNC4wOSUpCgpEZWZpbml0aW9uOiBUaGlzIHJlcHJlc2VudHMgdGhlIGF2ZXJhZ2UgcmV0dXJuIHRoZSBwb3J0Zm9saW8gaGFzIGdlbmVyYXRlZCBvdmVyIGEgeWVhciwgYW5udWFsaXplZCB0byByZWZsZWN0IGEgZnVsbCB5ZWFyJ3MgcGVyZm9ybWFuY2UuCkludGVycHJldGF0aW9uOiBUaGUgcG9ydGZvbGlvIGhhcyBhY2hpZXZlZCBhbiBhbm51YWxpemVkIHJldHVybiBvZiBhcHByb3hpbWF0ZWx5IDE0LjA5JS4gVGhpcyBpbmRpY2F0ZXMgdGhlIGF2ZXJhZ2UgcGVyY2VudGFnZSByZXR1cm4gdGhlIHBvcnRmb2xpbyBoYXMgZGVsaXZlcmVkIG92ZXIgdGhlIHllYXIuCgpBbm51YWxpemVkIFN0YW5kYXJkIERldmlhdGlvbjogMC41MTUzICg1MS41MyUpCgpEZWZpbml0aW9uOiBUaGlzIG1lYXN1cmVzIHRoZSB2b2xhdGlsaXR5IG9mIHRoZSBwb3J0Zm9saW8ncyByZXR1cm5zIG92ZXIgYSB5ZWFyLCBzaG93aW5nIGhvdyBtdWNoIHRoZSByZXR1cm5zIGRldmlhdGUgZnJvbSB0aGUgYXZlcmFnZSByZXR1cm4uCkludGVycHJldGF0aW9uOiBUaGUgYW5udWFsaXplZCBzdGFuZGFyZCBkZXZpYXRpb24gaXMgNTEuNTMlLCByZWZsZWN0aW5nIGEgaGlnaCBsZXZlbCBvZiB2b2xhdGlsaXR5IGFuZCByaXNrIGFzc29jaWF0ZWQgd2l0aCB0aGUgcG9ydGZvbGlvLiBUaGlzIG1lYW5zIHRoYXQgdGhlIHJldHVybnMgb2YgdGhlIHBvcnRmb2xpbyBoYXZlIGNvbnNpZGVyYWJsZSB2YXJpYWJpbGl0eS4KCkFubnVhbGl6ZWQgU2hhcnBlIFJhdGlvIChSZj0wJSk6IDAuMjczNAoKRGVmaW5pdGlvbjogVGhlIFNoYXJwZSBSYXRpbyBtZWFzdXJlcyB0aGUgcG9ydGZvbGlvJ3Mgcmlzay1hZGp1c3RlZCByZXR1cm4uIEl0IGlzIGNhbGN1bGF0ZWQgYXMgdGhlIGFubnVhbGl6ZWQgcmV0dXJuIG1pbnVzIHRoZSByaXNrLWZyZWUgcmF0ZSBkaXZpZGVkIGJ5IHRoZSBhbm51YWxpemVkIHN0YW5kYXJkIGRldmlhdGlvbi4gSGVyZSwgdGhlIHJpc2stZnJlZSByYXRlIChSZikgaXMgYXNzdW1lZCB0byBiZSAwJS4KSW50ZXJwcmV0YXRpb246IEEgU2hhcnBlIFJhdGlvIG9mIDAuMjczNCBpbmRpY2F0ZXMgdGhhdCwgYWZ0ZXIgYWRqdXN0aW5nIGZvciByaXNrICh2b2xhdGlsaXR5KSwgdGhlIHBvcnRmb2xpbydzIHJldHVybiBpcyByZWxhdGl2ZWx5IG1vZGVzdC4gQSBoaWdoZXIgU2hhcnBlIFJhdGlvIGlzIGdlbmVyYWxseSBwcmVmZXJyZWQsIGluZGljYXRpbmcgYmV0dGVyIHJpc2stYWRqdXN0ZWQgcmV0dXJucy4gQSBTaGFycGUgUmF0aW8gb2YgMC4yNzM0IHN1Z2dlc3RzIHRoYXQgdGhlIHBvcnRmb2xpb+KAmXMgcmlzay1hZGp1c3RlZCByZXR1cm4gaXMgbG93LCB3aGljaCBjb3VsZCBpbXBseSB0aGF0IHRoZSByZXR1cm4gZG9lcyBub3Qgc3VmZmljaWVudGx5IGNvbXBlbnNhdGUgZm9yIHRoZSBsZXZlbCBvZiByaXNrIHRha2VuLgoKU3VtbWFyeQpSZXR1cm46IFRoZSBwb3J0Zm9saW8gaGFzIGEgcmVhc29uYWJsZSBhbm51YWxpemVkIHJldHVybiBvZiAxNC4wOSUuClZvbGF0aWxpdHk6IFRoZSBwb3J0Zm9saW8gZXhoaWJpdHMgaGlnaCB2b2xhdGlsaXR5IHdpdGggYW4gYW5udWFsaXplZCBzdGFuZGFyZCBkZXZpYXRpb24gb2YgNTEuNTMlLgpSaXNrLUFkanVzdGVkIFBlcmZvcm1hbmNlOiBUaGUgU2hhcnBlIFJhdGlvIG9mIDAuMjczNCBzdWdnZXN0cyB0aGF0IHRoZSByZXR1cm4gb24gdGhlIHBvcnRmb2xpbywgYWRqdXN0ZWQgZm9yIHJpc2ssIGlzIHJlbGF0aXZlbHkgbG93LiBUaGlzIGluZGljYXRlcyB0aGF0IHRoZSBwb3J0Zm9saW8gbWlnaHQgbm90IGJlIHByb3ZpZGluZyBzdWZmaWNpZW50IHJldHVybiByZWxhdGl2ZSB0byBpdHMgcmlzayBsZXZlbC4KCg==