library(RColorBrewer)
#library(h2o)
#h2o.init()
#prosPath = system.file("extdata", "prostate.csv", package = "h2o")
#head(prosPath)
#str(prosPath)
# brewer.pal.info["Blues",]
#
# display.brewer.all(type="seq")
#
# display.brewer.all(type="div")
#
# brewer.pal(7,"Spectral")
#
# display.brewer.pal(7,"Spectral")
library(ROML); library(ROML.portfolio)
################################################################################
## **Install Rmetrics packages**
##
## This script installs Rmetrics packages either from source or from
## remote server (i.e. R-Forge). It ensures that all dependent
## Rmetrics packages are installed from the same location, i.e. remote
## server. This is important to avoid compatibility problem between
## development packages and packages available on CRAN.
##
## *An Example with fSeries*
##
## _Local packages_
##
## Open an R process and set its working directory to this directory.
## Then type the following :
##
## > source("installRmetrics.R")
## > installRmetrics("timeDate")
##
## _Packages at R-Forge_
##
## > source("installRmetrics.R")
## > installRmetrics("timeDate", repos="http://R-Forge.R-project.org")
##
################################################################################
# pacman::p_load( c("fUtilities",
# "fEcofin",
# "fCalendar",
# "fSeries",
# "timeDate",
# "timeSeries",
# "fImport",
# "fBasics",
# "fArma",
# "fGarch",
# "fNonlinear",
# "fUnitRoots",
# "fTrading",
# "fMultivar",
# "fRegression",
# "fExtremes",
# "fCopulae",
# "fBonds",
# "fOptions",
# "fExoticOptions",
# "fAsianOptions",
# "fAssets",
# "fPortfolio"))
#
#source("http://www.rmetrics.org/Rmetrics.R")
#install.Rmetrics()
pacman::p_load(matrixcalc,knitr,dygraphs,ggthemes,highcharter,viridis,tibbletime,timetk,tidyquant,tidyverse,
fPortfolio,xts)
# The symbols vector holds our tickers.
tickers <- c("FB", "AAPL", "MSFT","GOOGL","TSLA","INTC")
# The prices object will hold our raw price data throughout this book.
AdjustedPrices<-
getSymbols(tickers,src="yahoo", from = "2012-06-01",
auto.assign = TRUE, warnings = FALSE) %>%
map(~Ad(get(.))) %>% #Extract (transformed) data from a suitable OHLC object.
reduce(merge) %>% #reduce() combines from the left, reduce_right() combines from the right
`colnames<-`(tickers )
AdjustedPrices<-tk_zoo(AdjustedPrices)%>%na.omit()
# Calculate returns
returns.data <- sapply(AdjustedPrices, CalculateReturns)
returns.data <- na.omit(returns.data)
returns.data %>%tk_tbl()%>%head()
#AdjustedPrices%>%head()
#tk_zoo(AdjustedPrices)%>%head()
#na.omit(), na.approx(), na.fill()
# Get dimensions and check if positive definite
dim(returns.data)
[1] 1380 6
is.positive.definite(cov(returns.data))
[1] TRUE
start="2017-01-01"
end=today()
#dateWindow <- c("2017-01-01","2017-11-01")
#put quotation on items in paste
dateWindow <-cat(paste(sQuote("2016-06-01"),sQuote(end),sep=","), "\n")
‘2016-06-01’,‘2017-11-27’
dygraph(returns.data, main = "Value", group = "stock") %>%
dyRebase(value = 100) %>%
dyRangeSelector(dateWindow = dateWindow)
# Next, we analyze this data set, i.e. check the ticker symbols by using the R base function names() and determine the amount of assets and scenarios by checking the numbers of rows (dimension 1: scenarios) and columns (dimension 2: assets) using the R base function dim.
# analyze scenario data
names(returns.data)
NULL
scenarios <-dim(returns.data)[1]
assets <- dim(returns.data)[2]
#The package financeR uses different time-series objects than fPortfolio, so we have to convert the xts object into an TimeSeries object.
# convert xts to TimeSeries for fPortfolio
returns_ts <- as.timeSeries(returns.data)
#Now we create the specification for the portfolio optimization: We select an appropriate optimization solver (quadprog) and define that our efficient frontier should contain 6 portfolios.
# fPortfolio specification: solver and efficient fronier
spec <- portfolioSpec()
setSolver(spec) <- "solveRquadprog"
setNFrontierPoints(spec) <-dim(returns.data)[2]
#Then we specify the constraints. Everything we add for our first calculation is that we want long-only portfolios without any other restrictions. We can check the constraints in detail by spec and the constraint set constraints to the function portfolioConstraints().
# fPortfolio constraints
constraints <- c('LongOnly')
portfolioConstraints(returns_ts, spec, constraints)
Title:
Portfolio Constraints
Lower/Upper Bounds:
FB AAPL MSFT GOOGL TSLA INTC
Lower 0 0 0 0 0 0
Upper 1 1 1 1 1 1
Equal Matrix Constraints:
ceq FB AAPL MSFT GOOGL TSLA INTC
Budget -1 -1 -1 -1 -1 -1 -1
attr(,"na.action")
Return
1
attr(,"class")
[1] "omit"
Cardinality Constraints:
FB AAPL MSFT GOOGL TSLA INTC
Lower 0 0 0 0 0 0
Upper 1 1 1 1 1 1
#Now we are ready to start the optmization and pass data, specification and constraints to the function portfolioFrontier(). We can examine the result using a standard print() command.
# perform optimization
frontier <- portfolioFrontier(returns_ts, spec, constraints)
print(frontier)
Title:
MV Portfolio Frontier
Estimator: covEstimator
Solver: solveRquadprog
Optimize: minRisk
Constraints: LongOnly
Portfolio Points: 5 of 6
Portfolio Weights:
FB AAPL MSFT GOOGL TSLA INTC
1 0.0000 0.0000 0.0000 0.0000 0.0000 1.0000
2 0.0589 0.1959 0.1762 0.2730 0.0382 0.2578
3 0.1723 0.1010 0.2585 0.2617 0.1589 0.0476
4 0.3066 0.0000 0.2040 0.1722 0.3171 0.0000
6 0.0000 0.0000 0.0000 0.0000 1.0000 0.0000
Covariance Risk Budgets:
FB AAPL MSFT GOOGL TSLA INTC
1 0.0000 0.0000 0.0000 0.0000 0.0000 1.0000
2 0.0641 0.1910 0.1769 0.2762 0.0445 0.2472
3 0.2151 0.0662 0.2086 0.2217 0.2611 0.0272
4 0.3203 0.0000 0.0939 0.0885 0.4973 0.0000
6 0.0000 0.0000 0.0000 0.0000 1.0000 0.0000
Target Returns and Risks:
mean Cov CVaR VaR
1 0.0006 0.0137 0.0311 0.0201
2 0.0010 0.0104 0.0233 0.0160
3 0.0013 0.0119 0.0264 0.0185
4 0.0016 0.0154 0.0332 0.0241
6 0.0022 0.0300 0.0631 0.0429
Description:
Mon Nov 27 19:22:53 2017 by user:
#fPortfolio generally produces really nice graphs. To plot a colorful efficient frontier, we just need to pass our frontier to the function tailoredFrontierPlot().
# plot efficient frontier
tailoredFrontierPlot(object=frontier)

# The other useful plot is a weights plot (function weightsPlot()), which shows the asset weights of the different portfolios on the efficient frontier.
# plot weights
weightsPlot(frontier, col=rainbow(dim(returns.data)[2]))

# We may now repeat the optimization with different constraints. Let's start by still considering long-only portfolios, but restricting the upper investment limit to 50%.
# extended constraints: add upper investment limits
constraints <- c('minW[1:assets]=0', 'maxW[1:assets]=0.5')
portfolioConstraints(returns_ts, spec, constraints)
Title:
Portfolio Constraints
Lower/Upper Bounds:
FB AAPL MSFT GOOGL TSLA INTC
Lower 0.0 0.0 0.0 0.0 0.0 0.0
Upper 0.5 0.5 0.5 0.5 0.5 0.5
Equal Matrix Constraints:
ceq FB AAPL MSFT GOOGL TSLA INTC
Budget -1 -1 -1 -1 -1 -1 -1
attr(,"na.action")
Return
1
attr(,"class")
[1] "omit"
Cardinality Constraints:
FB AAPL MSFT GOOGL TSLA INTC
Lower 0 0 0 0 0 0
Upper 1 1 1 1 1 1
frontier <- portfolioFrontier(returns_ts, spec, constraints)
frontierPlot(object=frontier,type='l')

tailoredFrontierPlot(object=frontier)

weightsPlot(frontier, col=rainbow(assets))

#############################################################################
##### 3. Package - fPortfolio
library(fPortfolio)
# convert time series format
return.ts <- as.timeSeries(returns.data)
# specification: solver and efficient fronier
spec <- portfolioSpec()
setSolver(spec) <- "solveRquadprog"
setNFrontierPoints(spec) <- 6
# constraints
constraints <- c('LongOnly')
portfolioConstraints(return.ts, spec, constraints)
Title:
Portfolio Constraints
Lower/Upper Bounds:
FB AAPL MSFT GOOGL TSLA INTC
Lower 0 0 0 0 0 0
Upper 1 1 1 1 1 1
Equal Matrix Constraints:
ceq FB AAPL MSFT GOOGL TSLA INTC
Budget -1 -1 -1 -1 -1 -1 -1
attr(,"na.action")
Return
1
attr(,"class")
[1] "omit"
Cardinality Constraints:
FB AAPL MSFT GOOGL TSLA INTC
Lower 0 0 0 0 0 0
Upper 1 1 1 1 1 1
# optimization
frontier <- portfolioFrontier(return.ts, spec, constraints)
print(frontier)
Title:
MV Portfolio Frontier
Estimator: covEstimator
Solver: solveRquadprog
Optimize: minRisk
Constraints: LongOnly
Portfolio Points: 5 of 6
Portfolio Weights:
FB AAPL MSFT GOOGL TSLA INTC
1 0.0000 0.0000 0.0000 0.0000 0.0000 1.0000
2 0.0589 0.1959 0.1762 0.2730 0.0382 0.2578
3 0.1723 0.1010 0.2585 0.2617 0.1589 0.0476
4 0.3066 0.0000 0.2040 0.1722 0.3171 0.0000
6 0.0000 0.0000 0.0000 0.0000 1.0000 0.0000
Covariance Risk Budgets:
FB AAPL MSFT GOOGL TSLA INTC
1 0.0000 0.0000 0.0000 0.0000 0.0000 1.0000
2 0.0641 0.1910 0.1769 0.2762 0.0445 0.2472
3 0.2151 0.0662 0.2086 0.2217 0.2611 0.0272
4 0.3203 0.0000 0.0939 0.0885 0.4973 0.0000
6 0.0000 0.0000 0.0000 0.0000 1.0000 0.0000
Target Returns and Risks:
mean Cov CVaR VaR
1 0.0006 0.0137 0.0311 0.0201
2 0.0010 0.0104 0.0233 0.0160
3 0.0013 0.0119 0.0264 0.0185
4 0.0016 0.0154 0.0332 0.0241
6 0.0022 0.0300 0.0631 0.0429
Description:
Mon Nov 27 19:23:08 2017 by user:
# plotting
tailoredFrontierPlot(frontier)

weightsPlot(frontier)

weightsPlot(frontier, col=rainbow(ncol(return.ts)))

# adding and changing constraints
constraints <- c('minW[1:assets]=0', 'maxW[1:assets]=0.5')
portfolioConstraints(return.ts, spec, constraints)
Title:
Portfolio Constraints
Lower/Upper Bounds:
FB AAPL MSFT GOOGL TSLA INTC
Lower 0.0 0.0 0.0 0.0 0.0 0.0
Upper 0.5 0.5 0.5 0.5 0.5 0.5
Equal Matrix Constraints:
ceq FB AAPL MSFT GOOGL TSLA INTC
Budget -1 -1 -1 -1 -1 -1 -1
attr(,"na.action")
Return
1
attr(,"class")
[1] "omit"
Cardinality Constraints:
FB AAPL MSFT GOOGL TSLA INTC
Lower 0 0 0 0 0 0
Upper 1 1 1 1 1 1
frontier <- portfolioFrontier(return.ts, spec, constraints)
weightsPlot(frontier)

frontierPlot(object=frontier,type='l')
minvariancePoints(object=frontier, return ="mean",
risk = "CVaR",col="#E6F598",pch=17)
cmlPoints(object=frontier, return = "mean",
risk = "CVaR",col="#99D594",pch=15)
sharpeRatioLines(object=frontier, return ="mean",
risk = "CVaR")
equalWeightsPoints(object=frontier, return = "mean",
risk = "CVaR",col="#3288BD",pch=18)
tangencyPoints(object=frontier, return = "mean",
risk = "Sigma")

frontierPlot(object=frontier,type='l')
tangencyLines(object=frontier,col="#3288BD")
monteCarloPoints(object=frontier,mcSteps = 5000, return = "mean", pch = 19, col="#D53E4F" )

library(lpSolve)
names(returns.data) #to see the contents of the data set
NULL
##Compute the sample mean of the returns of each stock
## and the sample covariance matrix.
stock_returns=tk_ts(returns.data)
covData <- covEstimator(as.timeSeries(returns.data)) ; covData
$mu
FB AAPL MSFT GOOGL TSLA INTC
0.0016236143 0.0007642967 0.0009837748 0.0010421298 0.0021969737 0.0006414635
$Sigma
FB AAPL MSFT GOOGL TSLA INTC
FB 5.275862e-04 8.552754e-05 7.903603e-05 1.138552e-04 1.548297e-04 7.068435e-05
AAPL 8.552754e-05 2.372503e-04 7.446457e-05 6.947419e-05 8.810875e-05 6.929101e-05
MSFT 7.903603e-05 7.446457e-05 1.968119e-04 9.135510e-05 9.001291e-05 9.945371e-05
GOOGL 1.138552e-04 6.947419e-05 9.135510e-05 1.914689e-04 1.094870e-04 6.165328e-05
TSLA 1.548297e-04 8.810875e-05 9.001291e-05 1.094870e-04 9.002804e-04 7.195302e-05
INTC 7.068435e-05 6.929101e-05 9.945371e-05 6.165328e-05 7.195302e-05 1.871909e-04
# convert time series format
return.ts <- as.timeSeries(returns.data)
## Compute the unrestricted (long-short) Mean-Variance (MV) portfolio
shortSpec <- portfolioSpec()
setSolver(shortSpec) <- "solveRshortExact"
shortFrontier <- portfolioFrontier(return.ts,spec=shortSpec,
constraints="Short")
print(shortFrontier) #report results for portfolio:1,13,25,37,50
Title:
MV Portfolio Frontier
Estimator: covEstimator
Solver: solveRshortExact
Optimize: minRisk
Constraints: Short
Portfolio Points: 5 of 50
Portfolio Weights:
FB AAPL MSFT GOOGL TSLA INTC
1 -0.0545 0.2908 0.0939 0.2843 -0.0825 0.4679
13 0.0844 0.1746 0.1947 0.2704 0.0653 0.2106
25 0.2232 0.0583 0.2954 0.2566 0.2131 -0.0467
37 0.3621 -0.0579 0.3962 0.2427 0.3609 -0.3040
50 0.5125 -0.1839 0.5054 0.2276 0.5211 -0.5828
Covariance Risk Budgets:
FB AAPL MSFT GOOGL TSLA INTC
1 -0.0245 0.2709 0.0759 0.2206 -0.0107 0.4679
13 0.0995 0.1612 0.1924 0.2720 0.0882 0.1867
25 0.2614 0.0294 0.1993 0.1847 0.3444 -0.0191
37 0.3184 -0.0135 0.1580 0.1074 0.4728 -0.0431
50 0.3291 -0.0194 0.1226 0.0635 0.5211 -0.0168
Target Returns and Risks:
mean Cov CVaR VaR
1 0.0006 0.0112 0.0247 0.0179
13 0.0010 0.0105 0.0236 0.0159
25 0.0014 0.0132 0.0289 0.0203
37 0.0018 0.0178 0.0384 0.0272
50 0.0022 0.0237 0.0506 0.0347
Description:
Mon Nov 27 19:36:38 2017 by user:
##Plot the Efficient Frontier
Frontier <- shortFrontier
frontierPlot(Frontier,frontier="both",risk="Sigma",type="l")
## Plot some portfolios
minvariancePoints(Frontier,pch=19,col="red") #the MVP point
##Position of each asset in the sigma-mu plane
singleAssetPoints(Frontier,risk="Sigma",pch=19,cex=1.5,
col=topo.colors(6))

## To compute the minimum variance portfolio (MVP),
## and a particular efficient portfolio for a given target return
##MVP: the minimum Variance.
minvariancePortfolio(return.ts)
Title:
MV Minimum Variance Portfolio
Estimator: covEstimator
Solver: solveRquadprog
Optimize: minRisk
Constraints: LongOnly
Portfolio Weights:
FB AAPL MSFT GOOGL TSLA INTC
0.0398 0.2119 0.1623 0.2749 0.0179 0.2932
Covariance Risk Budgets:
FB AAPL MSFT GOOGL TSLA INTC
0.0398 0.2119 0.1623 0.2749 0.0179 0.2932
Target Returns and Risks:
mean Cov CVaR VaR
0.0009 0.0103 0.0232 0.0157
Description:
Mon Nov 27 19:36:38 2017 by user:
##EP(mu): an efficient portfolio for given target return
mu = 0.05; Spec = portfolioSpec()
setSolver(Spec) = "solveRshortExact"
setTargetReturn(Spec) = mu
efficientPortfolio(Data, Spec)
Title:
MV Efficient Portfolio
Estimator: covEstimator
Solver: solveRshortExact
Optimize: minRisk
Constraints: LongOnly
Portfolio Weights:
BKE FCEL GG OII SEB
0.5737 0.4890 0.1788 -0.1093 -0.1323
Covariance Risk Budgets:
BKE FCEL GG OII SEB
0.2459 0.7227 0.0516 -0.0217 0.0015
Target Returns and Risks:
mean Cov CVaR VaR
0.0500 0.1540 0.2913 0.2113
Description:
Mon Nov 27 19:36:38 2017 by user:
##To compute the global maximum return portfolio we use the
##linear programming solver lp to resolve the optimization
## problem for a vector of 5 unknown weights.
##MaxR: Global maximum return portfolio
## maximize: (w1,w2,w3,w4,w5)*covData$mu
## subject to: w1+w2+w3+w4+w5 = 1
##Use the linear programming solver lp from lpSolve:
f.obj <- covData$mu
f.con <- matrix(c(1,1,1,1,1), nrow=1, byrow=TRUE)
f.dir <- "="
f.rhs <- 1
lp ("max", f.obj, f.con, f.dir, f.rhs)
Success: the objective function is 0
lp ("max", f.obj, f.con, f.dir, f.rhs)$solution
[1] 0.0000000 0.0000000 0.0000000 0.0000000 0.6647556 0.0000000
##Result: a portfolio containing only FCEL
## A plot of all 50 portfolios along the Efficient Frontier for
## the given assets. The bold vertical line marks the MVP
weightsPlot(Frontier)

tailoredFrontierPlot(object=shortFrontier)

LS0tCnRpdGxlOiBSaXNrLVJldHVybiBQb3J0Zm9saW8gT3B0aW1pemF0aW9uIG1vZGVsCm91dHB1dDogaHRtbF9ub3RlYm9vawp0aGVtZTogY29zbW8KdG9jX2Zsb2F0OiBubwp0b2NfZGVwdGg6IDIKYXV0aG9yOiBOYW5hIEJvYXRlbmcKZGZfcHJpbnQ6IHBhZ2VkClRpbWU6ICdgciBTeXMudGltZSgpYCcKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgotLS0KCgoKYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0KCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIG91dC53aWR0aCA9IjEwMCUiLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2RlZmF1bHQnLCAKICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICBmaWcuY2FwID0iRmlnLiAzMCIsIAogICAgICAgICAgICAgICAgICAgICAgb3V0LndpZHRoPSIxMDAlIikKCmBgYAoKCgpgYGB7cn0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCiNsaWJyYXJ5KGgybykKI2gyby5pbml0KCkKI3Byb3NQYXRoID0gc3lzdGVtLmZpbGUoImV4dGRhdGEiLCAicHJvc3RhdGUuY3N2IiwgcGFja2FnZSA9ICJoMm8iKQoKI2hlYWQocHJvc1BhdGgpCgojc3RyKHByb3NQYXRoKQojIGJyZXdlci5wYWwuaW5mb1siQmx1ZXMiLF0KIyAKIyBkaXNwbGF5LmJyZXdlci5hbGwodHlwZT0ic2VxIikKIyAKIyBkaXNwbGF5LmJyZXdlci5hbGwodHlwZT0iZGl2IikKIyAKIyBicmV3ZXIucGFsKDcsIlNwZWN0cmFsIikKIyAKIyBkaXNwbGF5LmJyZXdlci5wYWwoNywiU3BlY3RyYWwiKQoKYGBgCgoKCgoKYGBge3J9CmxpYnJhcnkoUk9NTCk7IGxpYnJhcnkoUk9NTC5wb3J0Zm9saW8pCmBgYAoKCgpgYGB7cn0KCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMjICoqSW5zdGFsbCBSbWV0cmljcyBwYWNrYWdlcyoqCiMjCiMjIFRoaXMgc2NyaXB0IGluc3RhbGxzIFJtZXRyaWNzIHBhY2thZ2VzIGVpdGhlciBmcm9tIHNvdXJjZSBvciBmcm9tCiMjIHJlbW90ZSBzZXJ2ZXIgKGkuZS4gUi1Gb3JnZSkuIEl0IGVuc3VyZXMgdGhhdCBhbGwgZGVwZW5kZW50CiMjIFJtZXRyaWNzIHBhY2thZ2VzIGFyZSBpbnN0YWxsZWQgZnJvbSB0aGUgc2FtZSBsb2NhdGlvbiwgaS5lLiByZW1vdGUKIyMgc2VydmVyLiBUaGlzIGlzIGltcG9ydGFudCB0byBhdm9pZCBjb21wYXRpYmlsaXR5IHByb2JsZW0gYmV0d2VlbgojIyBkZXZlbG9wbWVudCBwYWNrYWdlcyBhbmQgcGFja2FnZXMgYXZhaWxhYmxlIG9uIENSQU4uCiMjCiMjICpBbiBFeGFtcGxlIHdpdGggZlNlcmllcyoKIyMKIyMgX0xvY2FsIHBhY2thZ2VzXwojIwojIyBPcGVuIGFuIFIgcHJvY2VzcyBhbmQgc2V0IGl0cyB3b3JraW5nIGRpcmVjdG9yeSB0byB0aGlzIGRpcmVjdG9yeS4KIyMgVGhlbiB0eXBlIHRoZSBmb2xsb3dpbmcgOgojIwojIyA+IHNvdXJjZSgiaW5zdGFsbFJtZXRyaWNzLlIiKQojIyA+IGluc3RhbGxSbWV0cmljcygidGltZURhdGUiKQojIwojIyBfUGFja2FnZXMgYXQgUi1Gb3JnZV8KIyMKIyMgPiBzb3VyY2UoImluc3RhbGxSbWV0cmljcy5SIikKIyMgPiBpbnN0YWxsUm1ldHJpY3MoInRpbWVEYXRlIiwgcmVwb3M9Imh0dHA6Ly9SLUZvcmdlLlItcHJvamVjdC5vcmciKQojIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBwYWNtYW46OnBfbG9hZCggICBjKCJmVXRpbGl0aWVzIiwKIyAgICAgICAiZkVjb2ZpbiIsCiMgICAgICAgImZDYWxlbmRhciIsCiMgICAgICAgImZTZXJpZXMiLAojICAgICAgICJ0aW1lRGF0ZSIsCiMgICAgICAgInRpbWVTZXJpZXMiLAojICAgICAgICJmSW1wb3J0IiwKIyAgICAgICAiZkJhc2ljcyIsCiMgICAgICAgImZBcm1hIiwKIyAgICAgICAiZkdhcmNoIiwKIyAgICAgICAiZk5vbmxpbmVhciIsCiMgICAgICAgImZVbml0Um9vdHMiLAojICAgICAgICJmVHJhZGluZyIsCiMgICAgICAgImZNdWx0aXZhciIsCiMgICAgICAgImZSZWdyZXNzaW9uIiwKIyAgICAgICAiZkV4dHJlbWVzIiwKIyAgICAgICAiZkNvcHVsYWUiLAojICAgICAgICJmQm9uZHMiLAojICAgICAgICJmT3B0aW9ucyIsCiMgICAgICAgImZFeG90aWNPcHRpb25zIiwKIyAgICAgICAiZkFzaWFuT3B0aW9ucyIsCiMgICAgICAgImZBc3NldHMiLAojICAgICAgICJmUG9ydGZvbGlvIikpCiMgCgoKI3NvdXJjZSgiaHR0cDovL3d3dy5ybWV0cmljcy5vcmcvUm1ldHJpY3MuUiIpCiNpbnN0YWxsLlJtZXRyaWNzKCkKYGBgCgoKCgoKCmBgYHtyfQoKCgpwYWNtYW46OnBfbG9hZChtYXRyaXhjYWxjLGtuaXRyLGR5Z3JhcGhzLGdndGhlbWVzLGhpZ2hjaGFydGVyLHZpcmlkaXMsdGliYmxldGltZSx0aW1ldGssdGlkeXF1YW50LHRpZHl2ZXJzZSwKICAgICAgICAgICAgICAgZlBvcnRmb2xpbyx4dHMpCgojIFRoZSBzeW1ib2xzIHZlY3RvciBob2xkcyBvdXIgdGlja2Vycy4gCnRpY2tlcnMgPC0gYygiRkIiLCAiQUFQTCIsICJNU0ZUIiwiR09PR0wiLCJUU0xBIiwiSU5UQyIpCgoKCiMgVGhlIHByaWNlcyBvYmplY3Qgd2lsbCBob2xkIG91ciByYXcgcHJpY2UgZGF0YSB0aHJvdWdob3V0IHRoaXMgYm9vay4KQWRqdXN0ZWRQcmljZXM8LSAKICBnZXRTeW1ib2xzKHRpY2tlcnMsc3JjPSJ5YWhvbyIsIGZyb20gPSAiMjAxMi0wNi0wMSIsIAogICAgICAgICAgICAgYXV0by5hc3NpZ24gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFKSAlPiUgCiAgbWFwKH5BZChnZXQoLikpKSAlPiUgICAjRXh0cmFjdCAodHJhbnNmb3JtZWQpIGRhdGEgZnJvbSBhIHN1aXRhYmxlIE9ITEMgb2JqZWN0LiAKICByZWR1Y2UobWVyZ2UpICU+JSAgICNyZWR1Y2UoKSBjb21iaW5lcyBmcm9tIHRoZSBsZWZ0LCByZWR1Y2VfcmlnaHQoKSBjb21iaW5lcyBmcm9tIHRoZSByaWdodAogIGBjb2xuYW1lczwtYCh0aWNrZXJzICkKCgpBZGp1c3RlZFByaWNlczwtdGtfem9vKEFkanVzdGVkUHJpY2VzKSU+JW5hLm9taXQoKQoKCiMgQ2FsY3VsYXRlIHJldHVybnMKcmV0dXJucy5kYXRhIDwtIHNhcHBseShBZGp1c3RlZFByaWNlcywgQ2FsY3VsYXRlUmV0dXJucykKCnJldHVybnMuZGF0YSA8LSBuYS5vbWl0KHJldHVybnMuZGF0YSkKCgpyZXR1cm5zLmRhdGEgJT4ldGtfdGJsKCklPiVoZWFkKCkKCgojQWRqdXN0ZWRQcmljZXMlPiVoZWFkKCkKCiN0a196b28oQWRqdXN0ZWRQcmljZXMpJT4laGVhZCgpCgoKI25hLm9taXQoKSwgbmEuYXBwcm94KCksIG5hLmZpbGwoKQoKCmBgYAoKCgpgYGB7cn0KIyBHZXQgZGltZW5zaW9ucyBhbmQgY2hlY2sgaWYgcG9zaXRpdmUgZGVmaW5pdGUKZGltKHJldHVybnMuZGF0YSkKaXMucG9zaXRpdmUuZGVmaW5pdGUoY292KHJldHVybnMuZGF0YSkpCmBgYAoKCgpgYGB7cn0KCnN0YXJ0PSIyMDE3LTAxLTAxIgoKZW5kPXRvZGF5KCkKCiNkYXRlV2luZG93IDwtIGMoIjIwMTctMDEtMDEiLCIyMDE3LTExLTAxIikKCiNwdXQgcXVvdGF0aW9uIG9uIGl0ZW1zIGluIHBhc3RlCgpkYXRlV2luZG93IDwtY2F0KHBhc3RlKHNRdW90ZSgiMjAxNi0wNi0wMSIpLHNRdW90ZShlbmQpLHNlcD0iLCIpLCAiXG4iKQoKCmR5Z3JhcGgocmV0dXJucy5kYXRhLCBtYWluID0gIlZhbHVlIiwgZ3JvdXAgPSAic3RvY2siKSAlPiUKICBkeVJlYmFzZSh2YWx1ZSA9IDEwMCkgJT4lCiAgZHlSYW5nZVNlbGVjdG9yKGRhdGVXaW5kb3cgPSBkYXRlV2luZG93KQoKYGBgCgoKCgoKCmBgYHtyfQoKIyBOZXh0LCB3ZSBhbmFseXplIHRoaXMgZGF0YSBzZXQsIGkuZS4gY2hlY2sgdGhlIHRpY2tlciBzeW1ib2xzIGJ5IHVzaW5nIHRoZSBSIGJhc2UgZnVuY3Rpb24gbmFtZXMoKSBhbmQgZGV0ZXJtaW5lIHRoZSBhbW91bnQgb2YgYXNzZXRzIGFuZCBzY2VuYXJpb3MgYnkgY2hlY2tpbmcgdGhlIG51bWJlcnMgb2Ygcm93cyAoZGltZW5zaW9uIDE6IHNjZW5hcmlvcykgYW5kIGNvbHVtbnMgKGRpbWVuc2lvbiAyOiBhc3NldHMpIHVzaW5nIHRoZSBSIGJhc2UgZnVuY3Rpb24gZGltLgoKIyBhbmFseXplIHNjZW5hcmlvIGRhdGEKbmFtZXMocmV0dXJucy5kYXRhKQoKc2NlbmFyaW9zIDwtZGltKHJldHVybnMuZGF0YSlbMV0KYXNzZXRzIDwtIGRpbShyZXR1cm5zLmRhdGEpWzJdCgojVGhlIHBhY2thZ2UgZmluYW5jZVIgdXNlcyBkaWZmZXJlbnQgdGltZS1zZXJpZXMgb2JqZWN0cyB0aGFuIGZQb3J0Zm9saW8sIHNvIHdlIGhhdmUgdG8gY29udmVydCB0aGUgeHRzIG9iamVjdCBpbnRvIGFuIFRpbWVTZXJpZXMgb2JqZWN0LgoKIyBjb252ZXJ0IHh0cyB0byBUaW1lU2VyaWVzIGZvciBmUG9ydGZvbGlvCnJldHVybnNfdHMgPC0gYXMudGltZVNlcmllcyhyZXR1cm5zLmRhdGEpCiNOb3cgd2UgY3JlYXRlIHRoZSBzcGVjaWZpY2F0aW9uIGZvciB0aGUgcG9ydGZvbGlvIG9wdGltaXphdGlvbjogV2Ugc2VsZWN0IGFuIGFwcHJvcHJpYXRlIG9wdGltaXphdGlvbiBzb2x2ZXIgKHF1YWRwcm9nKSBhbmQgZGVmaW5lIHRoYXQgb3VyIGVmZmljaWVudCBmcm9udGllciBzaG91bGQgY29udGFpbiA2IHBvcnRmb2xpb3MuCgojIGZQb3J0Zm9saW8gc3BlY2lmaWNhdGlvbjogc29sdmVyIGFuZCBlZmZpY2llbnQgZnJvbmllcgpzcGVjIDwtIHBvcnRmb2xpb1NwZWMoKQpzZXRTb2x2ZXIoc3BlYykgPC0gInNvbHZlUnF1YWRwcm9nIgpzZXRORnJvbnRpZXJQb2ludHMoc3BlYykgPC1kaW0ocmV0dXJucy5kYXRhKVsyXQojVGhlbiB3ZSBzcGVjaWZ5IHRoZSBjb25zdHJhaW50cy4gRXZlcnl0aGluZyB3ZSBhZGQgZm9yIG91ciBmaXJzdCBjYWxjdWxhdGlvbiBpcyB0aGF0IHdlIHdhbnQgbG9uZy1vbmx5IHBvcnRmb2xpb3Mgd2l0aG91dCBhbnkgb3RoZXIgcmVzdHJpY3Rpb25zLiBXZSBjYW4gY2hlY2sgdGhlIGNvbnN0cmFpbnRzIGluIGRldGFpbCBieSBzcGVjIGFuZCB0aGUgY29uc3RyYWludCBzZXQgY29uc3RyYWludHMgdG8gdGhlIGZ1bmN0aW9uIHBvcnRmb2xpb0NvbnN0cmFpbnRzKCkuCgojIGZQb3J0Zm9saW8gY29uc3RyYWludHMKY29uc3RyYWludHMgPC0gYygnTG9uZ09ubHknKQpwb3J0Zm9saW9Db25zdHJhaW50cyhyZXR1cm5zX3RzLCBzcGVjLCBjb25zdHJhaW50cykKCiNOb3cgd2UgYXJlIHJlYWR5IHRvIHN0YXJ0IHRoZSBvcHRtaXphdGlvbiBhbmQgcGFzcyBkYXRhLCBzcGVjaWZpY2F0aW9uIGFuZCBjb25zdHJhaW50cyB0byB0aGUgZnVuY3Rpb24gcG9ydGZvbGlvRnJvbnRpZXIoKS4gV2UgY2FuIGV4YW1pbmUgdGhlIHJlc3VsdCB1c2luZyBhIHN0YW5kYXJkIHByaW50KCkgY29tbWFuZC4KCiMgcGVyZm9ybSBvcHRpbWl6YXRpb24KZnJvbnRpZXIgPC0gcG9ydGZvbGlvRnJvbnRpZXIocmV0dXJuc190cywgc3BlYywgY29uc3RyYWludHMpCnByaW50KGZyb250aWVyKQoKCiNmUG9ydGZvbGlvIGdlbmVyYWxseSBwcm9kdWNlcyByZWFsbHkgbmljZSBncmFwaHMuIFRvIHBsb3QgYSBjb2xvcmZ1bCBlZmZpY2llbnQgZnJvbnRpZXIsIHdlIGp1c3QgbmVlZCB0byBwYXNzIG91ciBmcm9udGllciB0byB0aGUgZnVuY3Rpb24gdGFpbG9yZWRGcm9udGllclBsb3QoKS4KCiMgcGxvdCBlZmZpY2llbnQgZnJvbnRpZXIKdGFpbG9yZWRGcm9udGllclBsb3Qob2JqZWN0PWZyb250aWVyKQojIFRoZSBvdGhlciB1c2VmdWwgcGxvdCBpcyBhIHdlaWdodHMgcGxvdCAoZnVuY3Rpb24gd2VpZ2h0c1Bsb3QoKSksIHdoaWNoIHNob3dzIHRoZSBhc3NldCB3ZWlnaHRzIG9mIHRoZSBkaWZmZXJlbnQgcG9ydGZvbGlvcyBvbiB0aGUgZWZmaWNpZW50IGZyb250aWVyLgoKIyBwbG90IHdlaWdodHMKd2VpZ2h0c1Bsb3QoZnJvbnRpZXIsIGNvbD1yYWluYm93KGRpbShyZXR1cm5zLmRhdGEpWzJdKSkKIyBXZSBtYXkgbm93IHJlcGVhdCB0aGUgb3B0aW1pemF0aW9uIHdpdGggZGlmZmVyZW50IGNvbnN0cmFpbnRzLiBMZXQncyBzdGFydCBieSBzdGlsbCBjb25zaWRlcmluZyBsb25nLW9ubHkgcG9ydGZvbGlvcywgYnV0IHJlc3RyaWN0aW5nIHRoZSB1cHBlciBpbnZlc3RtZW50IGxpbWl0IHRvIDUwJS4KCiMgZXh0ZW5kZWQgY29uc3RyYWludHM6IGFkZCB1cHBlciBpbnZlc3RtZW50IGxpbWl0cwpjb25zdHJhaW50cyA8LSBjKCdtaW5XWzE6YXNzZXRzXT0wJywgJ21heFdbMTphc3NldHNdPTAuNScpCnBvcnRmb2xpb0NvbnN0cmFpbnRzKHJldHVybnNfdHMsIHNwZWMsIGNvbnN0cmFpbnRzKQoKZnJvbnRpZXIgPC0gcG9ydGZvbGlvRnJvbnRpZXIocmV0dXJuc190cywgc3BlYywgY29uc3RyYWludHMpCgoKCmZyb250aWVyUGxvdChvYmplY3Q9ZnJvbnRpZXIsdHlwZT0nbCcpCgp0YWlsb3JlZEZyb250aWVyUGxvdChvYmplY3Q9ZnJvbnRpZXIpCgoKCndlaWdodHNQbG90KGZyb250aWVyLCBjb2w9cmFpbmJvdyhhc3NldHMpKQoKCgoKYGBgCgoKCgoKCgpgYGB7cn0KCgoKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIyAzLiBQYWNrYWdlIC0gZlBvcnRmb2xpbwoKbGlicmFyeShmUG9ydGZvbGlvKQoKIyBjb252ZXJ0IHRpbWUgc2VyaWVzIGZvcm1hdApyZXR1cm4udHMgPC0gYXMudGltZVNlcmllcyhyZXR1cm5zLmRhdGEpCgojIHNwZWNpZmljYXRpb246IHNvbHZlciBhbmQgZWZmaWNpZW50IGZyb25pZXIgCnNwZWMgPC0gcG9ydGZvbGlvU3BlYygpCnNldFNvbHZlcihzcGVjKSA8LSAic29sdmVScXVhZHByb2ciCnNldE5Gcm9udGllclBvaW50cyhzcGVjKSA8LSA2CgojIGNvbnN0cmFpbnRzCmNvbnN0cmFpbnRzIDwtIGMoJ0xvbmdPbmx5JykKcG9ydGZvbGlvQ29uc3RyYWludHMocmV0dXJuLnRzLCBzcGVjLCBjb25zdHJhaW50cykKCiMgb3B0aW1pemF0aW9uCmZyb250aWVyIDwtIHBvcnRmb2xpb0Zyb250aWVyKHJldHVybi50cywgc3BlYywgY29uc3RyYWludHMpCnByaW50KGZyb250aWVyKQoKIyBwbG90dGluZwp0YWlsb3JlZEZyb250aWVyUGxvdChmcm9udGllcikKd2VpZ2h0c1Bsb3QoZnJvbnRpZXIpIAp3ZWlnaHRzUGxvdChmcm9udGllciwgY29sPXJhaW5ib3cobmNvbChyZXR1cm4udHMpKSkgCgojIGFkZGluZyBhbmQgY2hhbmdpbmcgY29uc3RyYWludHMKY29uc3RyYWludHMgPC0gYygnbWluV1sxOmFzc2V0c109MCcsICdtYXhXWzE6YXNzZXRzXT0wLjUnKQpwb3J0Zm9saW9Db25zdHJhaW50cyhyZXR1cm4udHMsIHNwZWMsIGNvbnN0cmFpbnRzKQpmcm9udGllciA8LSBwb3J0Zm9saW9Gcm9udGllcihyZXR1cm4udHMsIHNwZWMsIGNvbnN0cmFpbnRzKQp3ZWlnaHRzUGxvdChmcm9udGllcikgCgpgYGAKCgoKCmBgYHtyfQoKCmZyb250aWVyUGxvdChvYmplY3Q9ZnJvbnRpZXIsdHlwZT0nbCcpIAptaW52YXJpYW5jZVBvaW50cyhvYmplY3Q9ZnJvbnRpZXIsIHJldHVybiA9Im1lYW4iLCAKICAgIHJpc2sgPSAiQ1ZhUiIsY29sPSIjRTZGNTk4IixwY2g9MTcpCgpjbWxQb2ludHMob2JqZWN0PWZyb250aWVyLCByZXR1cm4gPSAibWVhbiIsIAogICAgcmlzayA9ICAiQ1ZhUiIsY29sPSIjOTlENTk0IixwY2g9MTUpCgoKc2hhcnBlUmF0aW9MaW5lcyhvYmplY3Q9ZnJvbnRpZXIsIHJldHVybiA9Im1lYW4iLCAKICAgIHJpc2sgPSAgIkNWYVIiKQoKCmVxdWFsV2VpZ2h0c1BvaW50cyhvYmplY3Q9ZnJvbnRpZXIsIHJldHVybiA9ICJtZWFuIiwgCiAgICByaXNrID0gICJDVmFSIixjb2w9IiMzMjg4QkQiLHBjaD0xOCkKCnRhbmdlbmN5UG9pbnRzKG9iamVjdD1mcm9udGllciwgcmV0dXJuID0gIm1lYW4iLCAKICAgIHJpc2sgPSAgIlNpZ21hIikKCgpgYGAKCgoKYGBge3J9CmZyb250aWVyUGxvdChvYmplY3Q9ZnJvbnRpZXIsdHlwZT0nbCcpIAoKdGFuZ2VuY3lMaW5lcyhvYmplY3Q9ZnJvbnRpZXIsY29sPSIjMzI4OEJEIikKCgptb250ZUNhcmxvUG9pbnRzKG9iamVjdD1mcm9udGllcixtY1N0ZXBzID0gNTAwMCwgcmV0dXJuID0gIm1lYW4iLCBwY2ggPSAxOSwgY29sPSIjRDUzRTRGIiApCgpgYGAKCgoKCgoKYGBge3J9CgpsaWJyYXJ5KGxwU29sdmUpCiAKbmFtZXMocmV0dXJucy5kYXRhKSAgI3RvIHNlZSB0aGUgY29udGVudHMgb2YgdGhlIGRhdGEgc2V0CgojI0NvbXB1dGUgdGhlIHNhbXBsZSBtZWFuIG9mIHRoZSByZXR1cm5zIG9mIGVhY2ggc3RvY2sgCiMjIGFuZCB0aGUgc2FtcGxlIGNvdmFyaWFuY2UgbWF0cml4LgoKCgpjb3ZEYXRhIDwtIGNvdkVzdGltYXRvcihhcy50aW1lU2VyaWVzKHJldHVybnMuZGF0YSkpIDsgY292RGF0YQogCgoKIyBjb252ZXJ0IHRpbWUgc2VyaWVzIGZvcm1hdApyZXR1cm4udHMgPC0gYXMudGltZVNlcmllcyhyZXR1cm5zLmRhdGEpCgoKIyMgQ29tcHV0ZSB0aGUgdW5yZXN0cmljdGVkIChsb25nLXNob3J0KSBNZWFuLVZhcmlhbmNlIChNVikgcG9ydGZvbGlvCnNob3J0U3BlYyA8LSBwb3J0Zm9saW9TcGVjKCkKc2V0U29sdmVyKHNob3J0U3BlYykgPC0gInNvbHZlUnNob3J0RXhhY3QiCnNob3J0RnJvbnRpZXIgPC0gcG9ydGZvbGlvRnJvbnRpZXIocmV0dXJuLnRzLHNwZWM9c2hvcnRTcGVjLAogICAgY29uc3RyYWludHM9IlNob3J0IikKcHJpbnQoc2hvcnRGcm9udGllcikgI3JlcG9ydCByZXN1bHRzIGZvciBwb3J0Zm9saW86MSwxMywyNSwzNyw1MAogCiMjUGxvdCB0aGUgRWZmaWNpZW50IEZyb250aWVyCkZyb250aWVyIDwtIHNob3J0RnJvbnRpZXIgIApmcm9udGllclBsb3QoRnJvbnRpZXIsZnJvbnRpZXI9ImJvdGgiLHJpc2s9IlNpZ21hIix0eXBlPSJsIikKIAojIyBQbG90IHNvbWUgcG9ydGZvbGlvcyAKbWludmFyaWFuY2VQb2ludHMoRnJvbnRpZXIscGNoPTE5LGNvbD0icmVkIikgI3RoZSBNVlAgcG9pbnQKIyNQb3NpdGlvbiBvZiBlYWNoIGFzc2V0IGluIHRoZSBzaWdtYS1tdSBwbGFuZQpzaW5nbGVBc3NldFBvaW50cyhGcm9udGllcixyaXNrPSJTaWdtYSIscGNoPTE5LGNleD0xLjUsCiAgICAgIGNvbD10b3BvLmNvbG9ycyg2KSkKIAojIyBUbyBjb21wdXRlIHRoZSBtaW5pbXVtIHZhcmlhbmNlIHBvcnRmb2xpbyAoTVZQKSwgCiMjIGFuZCBhIHBhcnRpY3VsYXIgZWZmaWNpZW50IHBvcnRmb2xpbyBmb3IgYSBnaXZlbiB0YXJnZXQgcmV0dXJuCiMjTVZQOiB0aGUgbWluaW11bSBWYXJpYW5jZS4gCm1pbnZhcmlhbmNlUG9ydGZvbGlvKHJldHVybi50cykKIyNFUChtdSk6IGFuIGVmZmljaWVudCBwb3J0Zm9saW8gZm9yIGdpdmVuIHRhcmdldCByZXR1cm4KbXUgPSAwLjA1OyBTcGVjID0gcG9ydGZvbGlvU3BlYygpCnNldFNvbHZlcihTcGVjKSA9ICJzb2x2ZVJzaG9ydEV4YWN0IgpzZXRUYXJnZXRSZXR1cm4oU3BlYykgPSBtdSAKZWZmaWNpZW50UG9ydGZvbGlvKERhdGEsIFNwZWMpIAogCiMjVG8gY29tcHV0ZSB0aGUgZ2xvYmFsIG1heGltdW0gcmV0dXJuIHBvcnRmb2xpbyB3ZSB1c2UgdGhlIAojI2xpbmVhciBwcm9ncmFtbWluZyBzb2x2ZXIgbHAgdG8gcmVzb2x2ZSB0aGUgb3B0aW1pemF0aW9uCiMjIHByb2JsZW0gZm9yIGEgdmVjdG9yICBvZiA1IHVua25vd24gd2VpZ2h0cy4KIyNNYXhSOiBHbG9iYWwgbWF4aW11bSByZXR1cm4gcG9ydGZvbGlvIAojIyBtYXhpbWl6ZTogKHcxLHcyLHczLHc0LHc1KSpjb3ZEYXRhJG11CiMjIHN1YmplY3QgdG86IHcxK3cyK3czK3c0K3c1ID0gMQojI1VzZSB0aGUgbGluZWFyIHByb2dyYW1taW5nIHNvbHZlciBscCBmcm9tIGxwU29sdmU6CmYub2JqIDwtIGNvdkRhdGEkbXUKZi5jb24gPC0gbWF0cml4KGMoMSwxLDEsMSwxKSwgbnJvdz0xLCBieXJvdz1UUlVFKQpmLmRpciA8LSAiPSIKZi5yaHMgPC0gMQpscCAoIm1heCIsIGYub2JqLCBmLmNvbiwgZi5kaXIsIGYucmhzKQpscCAoIm1heCIsIGYub2JqLCBmLmNvbiwgZi5kaXIsIGYucmhzKSRzb2x1dGlvbgogCiMjUmVzdWx0OiBhIHBvcnRmb2xpbyBjb250YWluaW5nIG9ubHkgRkNFTAogCiMjIEEgcGxvdCBvZiBhbGwgNTAgcG9ydGZvbGlvcyBhbG9uZyB0aGUgRWZmaWNpZW50IEZyb250aWVyIGZvciAKIyMgdGhlIGdpdmVuIGFzc2V0cy4gVGhlIGJvbGQgdmVydGljYWwgbGluZSBtYXJrcyB0aGUgTVZQCgp3ZWlnaHRzUGxvdChGcm9udGllcikKCgp0YWlsb3JlZEZyb250aWVyUGxvdChvYmplY3Q9c2hvcnRGcm9udGllcikKCgpgYGAKCg==