We’ll call multiple symbols at once by creating a vector of symbols and then pass the basket to the getSymbols function.
Summary will show us basics; notice we have to use `` for funny charcters.
End(Not run)
Add MACD
candleChart(BRKB, TA=c(addMACD(),addVo()), subset = '2019')

?addMACD
?addMACD()
addMACD(fast = 12, slow = 26, signal = 9, type = “EMA”, histogram = TRUE, col)
Various ways to dictate time, in this case everything after… Also added in ADX
candleChart(BRKB, TA=c(addMACD(),addADX()), subset = '2018-01::')

Various ways to dictate time, in this case everything in between… Background is the ‘Theme’
candleChart(BRKB , TA=c(addMACD()), subset = '2018-01::2018-05', theme = 'white')

Various options for theme’s overall
candleChart(BRKB , TA=c(addMACD()), subset = '2019-01::', theme = chartTheme('white', up.col='green',dn.col='darkred'))

chartSeries is another cool way to do this same stuff
?chartSeries
chartSeries(BRKB,
type = c("auto", "candlesticks"),
subset = '2019-01::',
show.grid = TRUE,
major.ticks='auto', minor.ticks=TRUE,
TA=c(addMACD(),addVo()))

chartSeries(BRKB,
type = c("auto", "candlesticks"),
subset = '2018-01::',
show.grid = TRUE,
major.ticks='auto', minor.ticks=TRUE,
multi.col = TRUE,
TA=c(addMACD(),addVo()))

chartSeries(BRKB,
type = c("auto", "matchsticks"),
subset = '2018-01::',
show.grid = TRUE,
major.ticks='auto', minor.ticks=TRUE,
multi.col = TRUE,
TA=c(addMACD(),addVo()))

Adding indicators starts here - although we jumped ahead up above
We’ll use TTR, which is installed with quantmod, if not you can install TTR TTR - Technical Trading Rules So Cool!
?TTR
Let’s do a Simple Moving Average
chartSeries(BRKB,
type = c("auto", "matchsticks"),
subset = '2018-01::',
show.grid = TRUE,
major.ticks='auto', minor.ticks=TRUE,
multi.col = FALSE,
TA=c(addMACD(),addVo(),addSMA(n=200,col = 'blue'),addSMA(n=50,col = 'red'),addSMA(n=22,col = 'green'),
addROC(n=200,col = 'blue'),addROC(n=50,col = 'red'),addROC(n=22,col = 'green'))) # rate of change

Bollinger bands Basics; addBBands(n = 20, sd = 2, maType = “SMA”, draw = ‘bands’, on = -1)
?addBBands
Experimental BBands “The primary addition to this function call over the TTR version is in the draw argument. ‘bands’ will draw standard Bollinger Bands, ‘percent’ will draw Bollinger %b and ‘width’ will draw Bolinger Bands Width. The last two will be drawn in new figure regions.”
?add_BBands
chartSeries(BRKB, theme="white",
TA="addVo();addBBands();addCCI()", subset = '2018-01::')

chartSeries(BRKB, theme="white",
TA="addVo();addBBands();addCCI()", subset = '2018-01::')

Create a Custom Indicator
Start with a simple chart.
chartSeries(BRKB, theme=chartTheme('white'), up.col="black",
dn.col="black")

CREATE two vectors as a time series to make a Exponential Moving Average. EMA is like a SMA except it gives more weight to the recent activity and that means it tends to mimic the market a little better.
BRKB exponential moving averages
BRKB.EMA.20<- EMA(BRKB$BRKB.Close, n=20)
BRKB.EMA.50<- EMA(BRKB$BRKB.Close, n=50)
BRKB.EMA.100<- EMA(BRKB$BRKB.Close, n=100)
BRKB.EMA.200<- EMA(BRKB$BRKB.Close, n=200)
chartSeries(BRKB, theme=chartTheme('white'),
type = c("auto", "matchsticks"),
subset = '2018-01::',
show.grid = TRUE,
major.ticks='auto', minor.ticks=TRUE,
multi.col = FALSE,
TA=c(addMACD(),addVo(),addADX(n = 14, maType = "EMA")))

addTA(BRKB.EMA.20, on=1, col = "green")

addTA(BRKB.EMA.50, on=1, col = "blue")

addTA(BRKB.EMA.100, on=1, col = "yellow")

addTA(BRKB.EMA.200, on=1, col = "red")

addTA(BRKB.EMA.20 - BRKB.EMA.200, col = "black",
type = 'h', legend = "100-200 EMA")

Create a SMA from scratch to understand
Lecture 7 (https://www.udemy.com/practical-data-science-analyzing-stock-market-data-with-r/learn/lecture/3410012#questions)
library(quantmod)
getSymbols(c('QQQ'), src='yahoo')
[1] "QQQ"
Plot the graph of the triple Q’s
plot(QQQ$QQQ.Close)

We choose a period, which sets the number of points of data that will be required to create one point of raw data (averaged) to get a point of data in our visualization. The price_vector is the group of data we want to use.
period <- 100
price_vector <- QQQ$QQQ.Close
length(price_vector)
[1] 3111
Now we need a vector to put our values into, so we’ll define an empty vector that we’ll then make a loop through our price_vector to fille the vector
moving_average_vector <- c()
Let’s look at sequence to understand : and ,
seq(5:10) # note that sequence gives us the number of values between 5 and 10 ;)
[1] 1 2 3 4 5 6
seq(5,10) # this sequence gives us from 5 to 10
[1] 5 6 7 8 9 10
The ‘Loop’. Let’s have a look at how this comes together.
for (ind in seq(period:length(price_vector))) { # so, period starts at 1 due to :
print(ind)
break # to stop it at the first go
}
[1] 1
Let’s fix that with a comma
for (ind in seq(period,length(price_vector))) { # period is 100
print(ind)
break # to stop it at the first go
}
[1] 100
Now, because we want to use 100 values as the period we need to start at 100 + 1, and we need parens. And we now can start the code to assign the values to the new empty vector.
for (ind in seq((period + 1),length(price_vector))) {
moving_average_vector <- c(moving_average_vector, # here we are saying add the mean to
# the moving_average_vector
mean(price_vector[(ind - period):ind])) #
}
head(moving_average_vector)
[1] 44.66208 44.70119 44.73436 44.76990 44.80653 44.84099
tail(moving_average_vector)
[1] 172.0040 172.2334 172.4528 172.6480 172.8450 173.0183
summary(moving_average_vector)
Min. 1st Qu. Median Mean 3rd Qu. Max.
29.24 47.79 68.48 84.66 108.86 178.97
moving_average_vector[1:100] # show 100 items
[1] 44.66208 44.70119 44.73436 44.76990 44.80653 44.84099 44.86525 44.87772
[9] 44.89356 44.90881 44.92495 44.95436 44.98594 45.02624 45.06772 45.10198
[17] 45.13743 45.17871 45.21515 45.24941 45.27663 45.31168 45.34495 45.37941
[25] 45.41871 45.45683 45.49851 45.54782 45.59911 45.64485 45.68653 45.73307
[33] 45.78446 45.83198 45.88188 45.92891 45.97901 46.02861 46.09673 46.15614
[41] 46.21822 46.28257 46.34040 46.39683 46.44485 46.49366 46.54723 46.58891
[49] 46.64436 46.69703 46.75594 46.80426 46.84396 46.88376 46.90723 46.92347
[57] 46.93663 46.95653 46.98168 47.01634 47.05683 47.09644 47.14208 47.17891
[65] 47.20267 47.23752 47.27594 47.31743 47.37158 47.41733 47.46178 47.49317
[73] 47.52327 47.56208 47.59960 47.63703 47.67416 47.70495 47.74188 47.77772
[81] 47.81198 47.85554 47.90178 47.94782 47.99465 48.04366 48.09099 48.14317
[89] 48.19446 48.24851 48.29792 48.36089 48.43069 48.49832 48.56941 48.62832
[97] 48.69287 48.75297 48.81277 48.88594
We can see from above what has happened. We have, started with the first 100 recalculated the mean with each 100 previous values, all the way to the end.
Let’s graph a few things.
par(mfrow=c(2,1)) # gives us two graphs, one on top of another
plot(QQQ$QQQ.Close)
plot(moving_average_vector, type = 'l', col = 'red', lwd=3,
main = paste('SMA', period))

Let’s check the length. We can see that we gave up that 100 to calculate the mean given the period of 100. This means however that we cannot graph the two against one another because they are different sizes.
length(price_vector)
[1] 3111
length(moving_average_vector)
[1] 3011
Let’s fix that; We’ll play a little trick and fill in those first 100 characters with NA using the repeat function. Here we say give me 100 NA’s
rep(NA, period)
[1] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[27] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[53] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
[79] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
So, instead of starting moving_average_vector as a blank vector we’ll start with 100 NA’s, or whatever the period is.
period <- 100
price_vector <- QQQ$QQQ.Close
moving_average_vector <- c(rep(NA, period))
for (ind in seq((period + 1),length(price_vector))) {
moving_average_vector <- c(moving_average_vector, # here we are saying add the mean to
# the moving_average_vector
mean(price_vector[(ind - period):ind])) #
}
Check the length. Bingo.
length(moving_average_vector)
[1] 3111
length(price_vector)
[1] 3111
Now we can add our moving average to the XTS object of the QQQ’s.
QQQ$QQQ.Close.SMA <- moving_average_vector
names(QQQ)
[1] "QQQ.Open" "QQQ.High" "QQQ.Low" "QQQ.Close" "QQQ.Volume"
[6] "QQQ.Adjusted" "QQQ.Close.SMA"
Let’s graph the close and the new SMA.
plot(QQQ$QQQ.Close)

lines(QQQ$QQQ.Close.SMA, type = 'l', col = 'red', lwd = 6)

Let’s call the same thing from TTR package ;) We’ll use fresh data. Same thing 2 lines of code.
getSymbols(c('QQQ'), src = 'yahoo')
[1] "QQQ"
chartSeries(QQQ, theme='white', TA="addSMA(100)")

Basics of code behind what is shown.
Not run:
```{r, echo=TRUE, fig.width=10, fig.height=7}
plot(BRKB$BRKB.Open)
```
End(Not run)
Multiple Moving Averages
Following along with class. Most of this I’ve already delved into along the way above.
library(quantmod)
getSymbols(c('EWP', 'SPY'), src='yahoo')
[1] "EWP" "SPY"
chartSeries(EWP$EWP.Close, theme="white", TA="addEMA(50, col='black');addEMA(200, col='blue')")

chartSeries(SPY$SPY.Close, theme="white", TA="addEMA(50, col='black');addEMA(200, col='blue')")

Let’s pull in TTR package
library('TTR')
chartSeries(SPY$SPY.Close, theme="white", TA="addEMA(50, col='black');addEMA(200, col='blue')")

SPY.EMA.50<- EMA(SPY$SPY.Close, n=50, )
SPY.EMA.200<- EMA(SPY$SPY.Close, n=200, )
addTA(SPY.EMA.50 - SPY.EMA.200,col='blue', type='h',legend="50-200 MA")

chartSeries(EWP$EWP.Close, theme="white", TA="addEMA(50, col='black');addEMA(200, col='blue')")

EWP.EMA.50<- EMA(SPY$SPY.Close, n=50 )
EWP.EMA.200<- EMA(SPY$SPY.Close, n=200 )
# add Technical Analysis the fast minus the slow EMA
addTA(EWP.EMA.50 - EWP.EMA.200,col='blue', type='h',legend="50-200 MA")

What this fast - slow tells us is that when it is above 0 (positive) things are bullish and when things are negative things are bullish.
chartSeries(SPY$SPY.Close, theme="white", TA="addEMA(50, col='black');addEMA(200, col='blue')")

SPY.EMA.10 <- EMA(SPY$SPY.Close, n=10 )
SPY.EMA.50 <- EMA(SPY$SPY.Close, n=50 )
SPY.EMA.200 <- EMA(SPY$SPY.Close, n=200 )
Fast.Diff <- SPY.EMA.10 - SPY.EMA.50
Slow.Diff <- SPY.EMA.50 - SPY.EMA.200
addTA(Fast.Diff, col='blue', type='h',legend="10-50 MA used for in-out of market")

addTA(Slow.Diff, col='red', type='h',legend="50-200 MA give trending sense")

LS0tCnRpdGxlOiAiQW5hbHl6aW5nIFN0b2NrIE1hcmtldCBEYXRhIHdpdGggUiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIApSTUQgYXZhaWxhYmxlIGhlcmUgKGh0dHBzOi8vZ2l0aHViLmNvbS9naGV0dG9jb3Vuc2Vsb3IvUl9JbnZlc3RpbmcvYmxvYi9tYXN0ZXIvUHJhY3RpY2FsRGF0YVNjaWVuY2VfQW5hbHl6aW5nU3RvY2tzX0luZGljYXRvcnNQVUIuUm1kKQoKdXNpbmcgcXVhbnRtb2QgZnJvbSBDcmFuCihodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcXVhbnRtb2QvKQoKCmBgYHtyLCBlY2hvPVRSVUV9CiMgaW5zdGFsbC5wYWNrYWdlcygncXVhbnRtb2QnKQpsaWJyYXJ5KHF1YW50bW9kKQpgYGAKCkZvciB2YXJpb3VzIGVsZW1lbnRzIGhhdmUgYSBsb29rIGF0IHF1YW50bW9kCmBgYHtyIGVjaG89VFJVRX0KP3F1YW50bW9kCmBgYAoKV2UnbGwgdXNlIFlhaG9vIGhlcmUgYnV0IEdvb2dsZSBGaW5hbmNlIGlzIGFsc28gc3VwcG9ydGVkLiBTeW50YXggaXMgc2xpZ2h0bHkgZGlmZmVyZW50LiAoaHR0cHM6Ly9maW5hbmNlLnlhaG9vLmNvbS9sb29rdXAvKQoKV2UnbGwgY2FsbCBtdWx0aXBsZSBzeW1ib2xzIGF0IG9uY2UgYnkgY3JlYXRpbmcgYSB2ZWN0b3Igb2Ygc3ltYm9scwphbmQgdGhlbiBwYXNzIHRoZSBiYXNrZXQgdG8gdGhlIGdldFN5bWJvbHMgZnVuY3Rpb24uCgpgYGB7ciAgLCBlY2hvPVRSVUV9CnN5bWJvbEJhc2tldCA8LSBjKCdBQVBMJywgJ0FNWk4nLCAnQlJLLUInLCAnU1BZJykKZ2V0U3ltYm9scyhzeW1ib2xCYXNrZXQgLCBzcmM9J3lhaG9vJykKYGBgCgpTdW1tYXJ5IHdpbGwgc2hvdyB1cyBiYXNpY3M7IG5vdGljZSB3ZSBoYXZlIHRvIHVzZSBgYCBmb3IgZnVubnkgY2hhcmN0ZXJzLgpgYGB7ciAsIGluY2x1ZGU9VFJVRX0Kc3VtbWFyeShgQlJLLUJgKQpgYGAKIyMjIERhdGEgUHJlcCAtIENMRUFOVVAgaXMgbmV2ZXIgZW5kaW5nCgpOT1RFOiBCUkstQiBJUyBPREQgc28gd2UgY2Fubm90IGdldCBhd2F5IGZyb20gZGF0YSBwcmVwIDstKQoncmVuYW1lJyB0aGUgb2JqZWN0IEJSSy1CIHRvIEJSS0IsIG5vdCByZWFsbHkgcmVuYW1lIGJ1dCBpbiBlc3NlbmNlLiAKVGhlbiBjaGFuZ2UgbmFtZXMgb2YgJ25hbWVzJyBpbiBuZXcgQlJLQiBvYmplY3QKYGBgIHtyICwgZWNobz1UUlVFfQpCUktCIDwtIGFzLnh0cyhgQlJLLUJgKQpuYW1lcyhCUktCKQpgYGAKCmBgYCB7ciAsIGVjaG89VFJVRX0KbmFtZXMoQlJLQikgPC0gYygiQlJLQi5PcGVuIiAgICwgICJCUktCLkhpZ2giICAgLCAgIkJSS0IuTG93IiAgICwgICAiQlJLQi5DbG9zZSIgICwgICJCUktCLlZvbHVtZSIsICAiQlJLQi5BZGp1c3RlZCIpCm5hbWVzKEJSS0IpCmBgYAoKIyMjIENoYXJ0aW5nIGJlZ2lucyBoZXJlCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBsb3QoQlJLQiRCUktCLk9wZW4pCmBgYAoKVG8gdmlldyBzb21lIHZhcmlhdGlvbiBvbiB0aGlzIGEgYml0IHdlIGNhbiB1c2UgdGhpcyBjb2RlOwoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpsaW5lQ2hhcnQoQlJLQiRCUktCLk9wZW4sIGxpbmUudHlwZSA9ICdoJywgdGhlbWUgPSAnd2hpdGUnLCBUQSA9IE5VTEwpCmBgYAoKVG8gc2VlIHZvbHVtZXM7CnJlbW92ZSB0aGUgVEEKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmxpbmVDaGFydChCUktCLCBsaW5lLnR5cGUgPSAnaCcsIHRoZW1lID0gJ3doaXRlJykKYGBgCgpCYXJjaGFydCwgdHlwZSBhbGxvd3MgZm9yIGhpZ2gsIGxvdywgY2xvc2UgOykgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTcgfQpiYXJDaGFydChCUktCLCBiYXIudHlwZSA9ICdobGMnLCBUQSA9IE5VTEwpCmBgYAoKTm93IGp1c3QgYSBzdWJzZXQgYW5kIGluIGNhbmRsZSBzdGlja3MKYGBge3IgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KY2FuZGxlQ2hhcnQoQlJLQiwgVEE9TlVMTCwgc3Vic2V0ID0gJzIwMTknKQpgYGAKCkdpdmUgYSBsb29rIHRvIGNhbmRsZUNoYXJ0IGZvciBmZWF0dXJlczsKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03IH0KP2NhbmRsZUNoYXJ0CmBgYAoKIyMgTm90IHJ1bjogCmdldFN5bWJvbHMoIllIT08iKQpjaGFydFNlcmllcyhZSE9PKQpjaGFydFNlcmllcyhZSE9PLCBzdWJzZXQ9J2xhc3QgNCBtb250aHMnKQpjaGFydFNlcmllcyhZSE9PLCBzdWJzZXQ9JzIwMDc6OjIwMDgtMDEnKQpjaGFydFNlcmllcyhZSE9PLHRoZW1lPWNoYXJ0VGhlbWUoJ3doaXRlJykpCmNoYXJ0U2VyaWVzKFlIT08sVEE9TlVMTCkgICAjbm8gdm9sdW1lCmNoYXJ0U2VyaWVzKFlIT08sVEE9YyhhZGRWbygpLGFkZEJCYW5kcygpKSkgICNhZGQgdm9sdW1lIGFuZCBCb2xsaW5nZXIgQmFuZHMgZnJvbSBUVFIKCk5PVEUgdGhlcmUgYXJlIGEgdG9uIG9mIGFkZC4uLiBpdGVtcyB0aGF0IGFyZSB2ZXJ5IHNsaWNrCgphZGRNQUNEKCkgICAjICBhZGQgTUFDRCBpbmRpY2F0b3IgdG8gY3VycmVudCBjaGFydAoKc2V0VEEoKQpjaGFydFNlcmllcyhZSE9PKSAgICNkcmF3cyBjaGFydCBhZ2FpbiwgdGhpcyB0aW1lIHdpbGwgYWxsIGluZGljYXRvcnMgcHJlc2VudAoKIyMgRW5kKE5vdCBydW4pCgpBZGQgTUFDRCAKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmNhbmRsZUNoYXJ0KEJSS0IsIFRBPWMoYWRkTUFDRCgpLGFkZFZvKCkpLCBzdWJzZXQgPSAnMjAxOScpCmBgYAoKP2FkZE1BQ0QKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQo/YWRkTUFDRCgpCmBgYAoKYWRkTUFDRChmYXN0ID0gMTIsIHNsb3cgPSAyNiwgc2lnbmFsID0gOSwgdHlwZSA9ICJFTUEiLCBoaXN0b2dyYW0gPSBUUlVFLCBjb2wpCgpWYXJpb3VzIHdheXMgdG8gZGljdGF0ZSB0aW1lLCBpbiB0aGlzIGNhc2UgZXZlcnl0aGluZyBhZnRlci4uLgpBbHNvIGFkZGVkIGluIEFEWApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTcgfQpjYW5kbGVDaGFydChCUktCLCBUQT1jKGFkZE1BQ0QoKSxhZGRBRFgoKSksIHN1YnNldCA9ICcyMDE4LTAxOjonKQpgYGAKClZhcmlvdXMgd2F5cyB0byBkaWN0YXRlIHRpbWUsIGluIHRoaXMgY2FzZSBldmVyeXRoaW5nIGluIGJldHdlZW4uLi4KQmFja2dyb3VuZCBpcyB0aGUgJ1RoZW1lJwoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03IH0KY2FuZGxlQ2hhcnQoQlJLQiAsIFRBPWMoYWRkTUFDRCgpKSwgc3Vic2V0ID0gJzIwMTgtMDE6OjIwMTgtMDUnLCB0aGVtZSA9ICd3aGl0ZScpCmBgYAoKVmFyaW91cyBvcHRpb25zIGZvciB0aGVtZSdzIG92ZXJhbGwKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmNhbmRsZUNoYXJ0KEJSS0IgLCBUQT1jKGFkZE1BQ0QoKSksIHN1YnNldCA9ICcyMDE5LTAxOjonLCB0aGVtZSA9IGNoYXJ0VGhlbWUoJ3doaXRlJywgdXAuY29sPSdncmVlbicsZG4uY29sPSdkYXJrcmVkJykpCmBgYAoKY2hhcnRTZXJpZXMgaXMgYW5vdGhlciBjb29sIHdheSB0byBkbyB0aGlzIHNhbWUgc3R1ZmYKCmBgYHtyICwgZWNobz1UUlVFIH0KP2NoYXJ0U2VyaWVzCmBgYAoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpjaGFydFNlcmllcyhCUktCLCAKICAgICAgICAgICAgdHlwZSA9IGMoImF1dG8iLCAiY2FuZGxlc3RpY2tzIiksIAogICAgICAgICAgICBzdWJzZXQgPSAnMjAxOS0wMTo6JywKICAgICAgICAgICAgc2hvdy5ncmlkID0gVFJVRSwKICAgICAgICAgICAgbWFqb3IudGlja3M9J2F1dG8nLCBtaW5vci50aWNrcz1UUlVFLAogICAgICAgICAgICBUQT1jKGFkZE1BQ0QoKSxhZGRWbygpKSkKYGBgCgoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpjaGFydFNlcmllcyhCUktCLCAKICAgICAgICAgICAgdHlwZSA9IGMoImF1dG8iLCAiY2FuZGxlc3RpY2tzIiksIAogICAgICAgICAgICBzdWJzZXQgPSAnMjAxOC0wMTo6JywKICAgICAgICAgICAgc2hvdy5ncmlkID0gVFJVRSwKICAgICAgICAgICAgbWFqb3IudGlja3M9J2F1dG8nLCBtaW5vci50aWNrcz1UUlVFLAogICAgICAgICAgICBtdWx0aS5jb2wgPSBUUlVFLAogICAgICAgICAgICBUQT1jKGFkZE1BQ0QoKSxhZGRWbygpKSkKYGBgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmNoYXJ0U2VyaWVzKEJSS0IsIAogICAgICAgICAgICB0eXBlID0gYygiYXV0byIsICJtYXRjaHN0aWNrcyIpLCAKICAgICAgICAgICAgc3Vic2V0ID0gJzIwMTgtMDE6OicsCiAgICAgICAgICAgIHNob3cuZ3JpZCA9IFRSVUUsCiAgICAgICAgICAgIG1ham9yLnRpY2tzPSdhdXRvJywgbWlub3IudGlja3M9VFJVRSwKICAgICAgICAgICAgbXVsdGkuY29sID0gVFJVRSwKICAgICAgICAgICAgVEE9YyhhZGRNQUNEKCksYWRkVm8oKSkpCmBgYAoKCiMjIyBBZGRpbmcgaW5kaWNhdG9ycyBzdGFydHMgaGVyZSAtIGFsdGhvdWdoIHdlIGp1bXBlZCBhaGVhZCB1cCBhYm92ZQoKV2UnbGwgdXNlIFRUUiwgd2hpY2ggaXMgaW5zdGFsbGVkIHdpdGggcXVhbnRtb2QsIGlmIG5vdCB5b3UgY2FuIGluc3RhbGwgVFRSClRUUiAtIFRlY2huaWNhbCBUcmFkaW5nIFJ1bGVzClNvIENvb2whCgpgYGB7ciAsIGVjaG89VFJVRSB9Cj9UVFIKYGBgCgpMZXQncyBkbyBhIFNpbXBsZSBNb3ZpbmcgQXZlcmFnZQoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpjaGFydFNlcmllcyhCUktCLCAKICAgICAgICAgICAgdHlwZSA9IGMoImF1dG8iLCAibWF0Y2hzdGlja3MiKSwgCiAgICAgICAgICAgIHN1YnNldCA9ICcyMDE4LTAxOjonLAogICAgICAgICAgICBzaG93LmdyaWQgPSBUUlVFLAogICAgICAgICAgICBtYWpvci50aWNrcz0nYXV0bycsIG1pbm9yLnRpY2tzPVRSVUUsCiAgICAgICAgICAgIG11bHRpLmNvbCA9IEZBTFNFLAogICAgICAgICAgICBUQT1jKGFkZE1BQ0QoKSxhZGRWbygpLGFkZFNNQShuPTIwMCxjb2wgPSAnYmx1ZScpLGFkZFNNQShuPTUwLGNvbCA9ICdyZWQnKSxhZGRTTUEobj0yMixjb2wgPSAnZ3JlZW4nKSwKICAgICAgICAgICAgYWRkUk9DKG49MjAwLGNvbCA9ICdibHVlJyksYWRkUk9DKG49NTAsY29sID0gJ3JlZCcpLGFkZFJPQyhuPTIyLGNvbCA9ICdncmVlbicpKSkgIyByYXRlIG9mIGNoYW5nZQpgYGAKCkJvbGxpbmdlciBiYW5kcwpCYXNpY3M7CmFkZEJCYW5kcyhuID0gMjAsIHNkID0gMiwgbWFUeXBlID0gIlNNQSIsIGRyYXcgPSAnYmFuZHMnLCBvbiA9IC0xKQoKYGBgIHtyICwgZWNobz1UUlVFIH0KP2FkZEJCYW5kcwpgYGAKCkV4cGVyaW1lbnRhbCBCQmFuZHMgIlRoZSBwcmltYXJ5IGFkZGl0aW9uIHRvIHRoaXMgZnVuY3Rpb24gY2FsbCBvdmVyIHRoZSBUVFIgdmVyc2lvbiBpcyBpbiB0aGUgZHJhdyBhcmd1bWVudC4g4oCYYmFuZHPigJkgd2lsbCBkcmF3IHN0YW5kYXJkIEJvbGxpbmdlciBCYW5kcywg4oCYcGVyY2VudOKAmSB3aWxsIGRyYXcgQm9sbGluZ2VyICViIGFuZCDigJh3aWR0aOKAmSB3aWxsIGRyYXcgQm9saW5nZXIgQmFuZHMgV2lkdGguIFRoZSBsYXN0IHR3byB3aWxsIGJlIGRyYXduIGluIG5ldyBmaWd1cmUgcmVnaW9ucy4iCmBgYCB7ciAgLCBlY2hvPVRSVUV9Cj9hZGRfQkJhbmRzCmBgYAoKYGBgIHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmNoYXJ0U2VyaWVzKEJSS0IsIHRoZW1lPSJ3aGl0ZSIsCiBUQT0iYWRkVm8oKTthZGRCQmFuZHMoKTthZGRDQ0koKSIsIHN1YnNldCA9ICcyMDE4LTAxOjonKQpgYGAKCgpgYGAge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03IH0KY2hhcnRTZXJpZXMoQlJLQiwgdGhlbWU9IndoaXRlIiwKIFRBPSJhZGRWbygpO2FkZEJCYW5kcygpO2FkZENDSSgpIiwgc3Vic2V0ID0gJzIwMTgtMDE6OicpCmBgYAoKIyMjIENyZWF0ZSBhIEN1c3RvbSBJbmRpY2F0b3IgClN0YXJ0IHdpdGggYSBzaW1wbGUgY2hhcnQuIAoKYGBgIHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmNoYXJ0U2VyaWVzKEJSS0IsIHRoZW1lPWNoYXJ0VGhlbWUoJ3doaXRlJyksIHVwLmNvbD0iYmxhY2siLAogZG4uY29sPSJibGFjayIpCmBgYAoKQ1JFQVRFIHR3byB2ZWN0b3JzIGFzIGEgdGltZSBzZXJpZXMgdG8gbWFrZSBhIEV4cG9uZW50aWFsIE1vdmluZyBBdmVyYWdlLiAKRU1BIGlzIGxpa2UgYSBTTUEgZXhjZXB0IGl0IGdpdmVzIG1vcmUgd2VpZ2h0IHRvIHRoZSByZWNlbnQgYWN0aXZpdHkgYW5kIAp0aGF0IG1lYW5zIGl0IHRlbmRzIHRvIG1pbWljIHRoZSBtYXJrZXQgYSBsaXR0bGUgYmV0dGVyLiAKCkJSS0IgZXhwb25lbnRpYWwgbW92aW5nIGF2ZXJhZ2VzCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KCkJSS0IuRU1BLjIwPC0gRU1BKEJSS0IkQlJLQi5DbG9zZSwgbj0yMCkKQlJLQi5FTUEuNTA8LSBFTUEoQlJLQiRCUktCLkNsb3NlLCBuPTUwKQpCUktCLkVNQS4xMDA8LSBFTUEoQlJLQiRCUktCLkNsb3NlLCBuPTEwMCkKQlJLQi5FTUEuMjAwPC0gRU1BKEJSS0IkQlJLQi5DbG9zZSwgbj0yMDApCgpjaGFydFNlcmllcyhCUktCLCB0aGVtZT1jaGFydFRoZW1lKCd3aGl0ZScpLAogICAgICAgICAgICB0eXBlID0gYygiYXV0byIsICJtYXRjaHN0aWNrcyIpLCAKICAgICAgICAgICAgc3Vic2V0ID0gJzIwMTgtMDE6OicsCiAgICAgICAgICAgIHNob3cuZ3JpZCA9IFRSVUUsCiAgICAgICAgICAgIG1ham9yLnRpY2tzPSdhdXRvJywgbWlub3IudGlja3M9VFJVRSwKICAgICAgICAgICAgbXVsdGkuY29sID0gRkFMU0UsCiAgICAgICAgICAgIFRBPWMoYWRkTUFDRCgpLGFkZFZvKCksYWRkQURYKG4gPSAxNCwgbWFUeXBlID0gIkVNQSIpKSkKCiAgICAgICAgICAgIGFkZFRBKEJSS0IuRU1BLjIwLCBvbj0xLCBjb2wgPSAiZ3JlZW4iKQogICAgICAgICAgICBhZGRUQShCUktCLkVNQS41MCwgb249MSwgY29sID0gImJsdWUiKQogICAgICAgICAgICBhZGRUQShCUktCLkVNQS4xMDAsIG9uPTEsIGNvbCA9ICJ5ZWxsb3ciKQogICAgICAgICAgICBhZGRUQShCUktCLkVNQS4yMDAsIG9uPTEsIGNvbCA9ICJyZWQiKQogICAgICAgICAgICBhZGRUQShCUktCLkVNQS4yMCAtIEJSS0IuRU1BLjIwMCwgY29sID0gImJsYWNrIiwKICAgICAgICAgICAgICAgICAgdHlwZSA9ICdoJywgbGVnZW5kID0gIjEwMC0yMDAgRU1BIikKYGBgCgoKIyMjIENyZWF0ZSBhIFNNQSBmcm9tIHNjcmF0Y2ggdG8gdW5kZXJzdGFuZAoKTGVjdHVyZSA3IChodHRwczovL3d3dy51ZGVteS5jb20vcHJhY3RpY2FsLWRhdGEtc2NpZW5jZS1hbmFseXppbmctc3RvY2stbWFya2V0LWRhdGEtd2l0aC1yL2xlYXJuL2xlY3R1cmUvMzQxMDAxMiNxdWVzdGlvbnMpCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmxpYnJhcnkocXVhbnRtb2QpCmdldFN5bWJvbHMoYygnUVFRJyksIHNyYz0neWFob28nKQpgYGAKClBsb3QgdGhlIGdyYXBoIG9mIHRoZSB0cmlwbGUgUSdzCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcGxvdChRUVEkUVFRLkNsb3NlKQpgYGAKCldlIGNob29zZSBhIHBlcmlvZCwgd2hpY2ggc2V0cyB0aGUgbnVtYmVyIG9mIHBvaW50cyBvZiBkYXRhIHRoYXQgd2lsbCBiZSByZXF1aXJlZCB0byBjcmVhdGUgb25lIHBvaW50IG9mIHJhdyBkYXRhIChhdmVyYWdlZCkgdG8gZ2V0IGEgcG9pbnQgb2YgZGF0YSBpbiBvdXIgdmlzdWFsaXphdGlvbi4gVGhlIHByaWNlX3ZlY3RvciBpcyB0aGUgZ3JvdXAgb2YgZGF0YSB3ZSB3YW50IHRvIHVzZS4gCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcGVyaW9kIDwtIDEwMApwcmljZV92ZWN0b3IgPC0gUVFRJFFRUS5DbG9zZQpsZW5ndGgocHJpY2VfdmVjdG9yKQpgYGAKCk5vdyB3ZSBuZWVkIGEgdmVjdG9yIHRvIHB1dCBvdXIgdmFsdWVzIGludG8sIHNvIHdlJ2xsIGRlZmluZSBhbiBlbXB0eSB2ZWN0b3IgdGhhdCB3ZSdsbCB0aGVuIG1ha2UgYSBsb29wIHRocm91Z2ggb3VyIHByaWNlX3ZlY3RvciB0byBmaWxsZSB0aGUgdmVjdG9yCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIDwtIGMoKQpgYGAKCkxldCdzIGxvb2sgYXQgc2VxdWVuY2UgdG8gdW5kZXJzdGFuZCA6IGFuZCAsCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30Kc2VxKDU6MTApICMgbm90ZSB0aGF0IHNlcXVlbmNlIGdpdmVzIHVzIHRoZSBudW1iZXIgb2YgdmFsdWVzIGJldHdlZW4gNSBhbmQgMTAgOykgCnNlcSg1LDEwKSAjIHRoaXMgc2VxdWVuY2UgZ2l2ZXMgdXMgZnJvbSA1IHRvIDEwIApgYGAKClRoZSAnTG9vcCcuIExldCdzIGhhdmUgYSBsb29rIGF0IGhvdyB0aGlzIGNvbWVzIHRvZ2V0aGVyLgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmZvciAoaW5kIGluIHNlcShwZXJpb2Q6bGVuZ3RoKHByaWNlX3ZlY3RvcikpKSB7ICMgc28sIHBlcmlvZCBzdGFydHMgYXQgMSBkdWUgdG8gOgogICAgICAgIHByaW50KGluZCkKICAgICAgICBicmVhayAjIHRvIHN0b3AgaXQgYXQgdGhlIGZpcnN0IGdvCn0KYGBgCgpMZXQncyBmaXggdGhhdCB3aXRoIGEgY29tbWEKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpmb3IgKGluZCBpbiBzZXEocGVyaW9kLGxlbmd0aChwcmljZV92ZWN0b3IpKSkgeyAjIHBlcmlvZCBpcyAxMDAKICAgICAgICBwcmludChpbmQpCiAgICAgICAgYnJlYWsgIyB0byBzdG9wIGl0IGF0IHRoZSBmaXJzdCBnbwp9CmBgYAoKTm93LCBiZWNhdXNlIHdlIHdhbnQgdG8gdXNlIDEwMCB2YWx1ZXMgYXMgdGhlIHBlcmlvZCB3ZSBuZWVkIHRvIHN0YXJ0IGF0IDEwMCArIDEsIGFuZCB3ZSBuZWVkIHBhcmVucy4gQW5kIHdlIG5vdyBjYW4gc3RhcnQgdGhlIGNvZGUgdG8gYXNzaWduIHRoZSB2YWx1ZXMgdG8gdGhlIG5ldyBlbXB0eSB2ZWN0b3IuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmZvciAoaW5kIGluIHNlcSgocGVyaW9kICsgMSksbGVuZ3RoKHByaWNlX3ZlY3RvcikpKSB7CiAgICBtb3ZpbmdfYXZlcmFnZV92ZWN0b3IgPC0gYyhtb3ZpbmdfYXZlcmFnZV92ZWN0b3IsICMgaGVyZSB3ZSBhcmUgc2F5aW5nIGFkZCB0aGUgbWVhbiB0byAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgbW92aW5nX2F2ZXJhZ2VfdmVjdG9yCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuKHByaWNlX3ZlY3RvclsoaW5kIC0gcGVyaW9kKTppbmRdKSkgIyAKfQpgYGAKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KaGVhZChtb3ZpbmdfYXZlcmFnZV92ZWN0b3IpCnRhaWwobW92aW5nX2F2ZXJhZ2VfdmVjdG9yKQpzdW1tYXJ5KG1vdmluZ19hdmVyYWdlX3ZlY3RvcikKbW92aW5nX2F2ZXJhZ2VfdmVjdG9yWzE6MTAwXSAjIHNob3cgMTAwIGl0ZW1zCmBgYAoKV2UgY2FuIHNlZSBmcm9tIGFib3ZlIHdoYXQgaGFzIGhhcHBlbmVkLiBXZSBoYXZlLCBzdGFydGVkIHdpdGggdGhlIGZpcnN0IDEwMCByZWNhbGN1bGF0ZWQgdGhlIG1lYW4gd2l0aCBlYWNoIDEwMCBwcmV2aW91cyB2YWx1ZXMsIGFsbCB0aGUgd2F5IHRvIHRoZSBlbmQuIAoKTGV0J3MgZ3JhcGggYSBmZXcgdGhpbmdzLiAKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpwYXIobWZyb3c9YygyLDEpKSAjIGdpdmVzIHVzIHR3byBncmFwaHMsIG9uZSBvbiB0b3Agb2YgYW5vdGhlcgpwbG90KFFRUSRRUVEuQ2xvc2UpCnBsb3QobW92aW5nX2F2ZXJhZ2VfdmVjdG9yLCB0eXBlID0gJ2wnLCBjb2wgPSAncmVkJywgbHdkPTMsCiAgICAgIG1haW4gPSBwYXN0ZSgnU01BJywgcGVyaW9kKSkKYGBgCgpMZXQncyBjaGVjayB0aGUgbGVuZ3RoLiBXZSBjYW4gc2VlIHRoYXQgd2UgZ2F2ZSB1cCB0aGF0IDEwMCB0byBjYWxjdWxhdGUgdGhlIG1lYW4gZ2l2ZW4gdGhlIHBlcmlvZCBvZiAxMDAuIFRoaXMgbWVhbnMgaG93ZXZlciB0aGF0IHdlIGNhbm5vdCBncmFwaCB0aGUgdHdvIGFnYWluc3Qgb25lIGFub3RoZXIgYmVjYXVzZSB0aGV5IGFyZSBkaWZmZXJlbnQgc2l6ZXMuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9Cmxlbmd0aChwcmljZV92ZWN0b3IpCmxlbmd0aChtb3ZpbmdfYXZlcmFnZV92ZWN0b3IpCmBgYAoKTGV0J3MgZml4IHRoYXQ7IFdlJ2xsIHBsYXkgYSBsaXR0bGUgdHJpY2sgYW5kIGZpbGwgaW4gdGhvc2UgZmlyc3QgMTAwIGNoYXJhY3RlcnMgd2l0aCBOQSB1c2luZyB0aGUgcmVwZWF0IGZ1bmN0aW9uLiBIZXJlIHdlIHNheSBnaXZlIG1lIDEwMCBOQSdzCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcmVwKE5BLCBwZXJpb2QpCmBgYAoKU28sIGluc3RlYWQgb2Ygc3RhcnRpbmcgbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIGFzIGEgYmxhbmsgdmVjdG9yIHdlJ2xsIHN0YXJ0IHdpdGggMTAwIE5BJ3MsIG9yIHdoYXRldmVyIHRoZSBwZXJpb2QgaXMuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBlcmlvZCA8LSAxMDAKcHJpY2VfdmVjdG9yIDwtIFFRUSRRUVEuQ2xvc2UKbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIDwtIGMocmVwKE5BLCBwZXJpb2QpKQpmb3IgKGluZCBpbiBzZXEoKHBlcmlvZCArIDEpLGxlbmd0aChwcmljZV92ZWN0b3IpKSkgewogICAgbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIDwtIGMobW92aW5nX2F2ZXJhZ2VfdmVjdG9yLCAjIGhlcmUgd2UgYXJlIHNheWluZyBhZGQgdGhlIG1lYW4gdG8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG1vdmluZ19hdmVyYWdlX3ZlY3RvcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihwcmljZV92ZWN0b3JbKGluZCAtIHBlcmlvZCk6aW5kXSkpICMgCn0KYGBgCgpDaGVjayB0aGUgbGVuZ3RoLiBCaW5nby4gCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KbGVuZ3RoKG1vdmluZ19hdmVyYWdlX3ZlY3RvcikKbGVuZ3RoKHByaWNlX3ZlY3RvcikKYGBgCgpOb3cgd2UgY2FuIGFkZCBvdXIgbW92aW5nIGF2ZXJhZ2UgdG8gdGhlIFhUUyBvYmplY3Qgb2YgdGhlIFFRUSdzLgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9ClFRUSRRUVEuQ2xvc2UuU01BIDwtIG1vdmluZ19hdmVyYWdlX3ZlY3RvcgpuYW1lcyhRUVEpCmBgYAoKTGV0J3MgZ3JhcGggdGhlIGNsb3NlIGFuZCB0aGUgbmV3IFNNQS4gCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcGxvdChRUVEkUVFRLkNsb3NlKQpsaW5lcyhRUVEkUVFRLkNsb3NlLlNNQSwgdHlwZSA9ICdsJywgY29sID0gJ3JlZCcsIGx3ZCA9IDYpCmBgYAoKTGV0J3MgY2FsbCB0aGUgc2FtZSB0aGluZyBmcm9tIFRUUiBwYWNrYWdlIDspIApXZSdsbCB1c2UgZnJlc2ggZGF0YS4gU2FtZSB0aGluZyAyIGxpbmVzIG9mIGNvZGUuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmdldFN5bWJvbHMoYygnUVFRJyksIHNyYyA9ICd5YWhvbycpCmNoYXJ0U2VyaWVzKFFRUSwgdGhlbWU9J3doaXRlJywgVEE9ImFkZFNNQSgxMDApIikKYGBgCgpCYXNpY3Mgb2YgY29kZSBiZWhpbmQgd2hhdCBpcyBzaG93bi4KCiMjIyBOb3QgcnVuOgoKIyMjIyMgYGBge3IsICBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQoKIyMjIyMgcGxvdChCUktCJEJSS0IuT3BlbikKCiMjIyMjIGBgYAoKIyMjIEVuZChOb3QgcnVuKQoKIyMjIE11bHRpcGxlIE1vdmluZyBBdmVyYWdlcwpGb2xsb3dpbmcgYWxvbmcgd2l0aCBjbGFzcy4gTW9zdCBvZiB0aGlzIEkndmUgYWxyZWFkeSBkZWx2ZWQgaW50byBhbG9uZyB0aGUgd2F5IGFib3ZlLgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmxpYnJhcnkocXVhbnRtb2QpCmdldFN5bWJvbHMoYygnRVdQJywgJ1NQWScpLCBzcmM9J3lhaG9vJykKYGBgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmNoYXJ0U2VyaWVzKEVXUCRFV1AuQ2xvc2UsIHRoZW1lPSJ3aGl0ZSIsIFRBPSJhZGRFTUEoNTAsIGNvbD0nYmxhY2snKTthZGRFTUEoMjAwLCBjb2w9J2JsdWUnKSIpCmNoYXJ0U2VyaWVzKFNQWSRTUFkuQ2xvc2UsIHRoZW1lPSJ3aGl0ZSIsIFRBPSJhZGRFTUEoNTAsIGNvbD0nYmxhY2snKTthZGRFTUEoMjAwLCBjb2w9J2JsdWUnKSIpCmBgYAoKTGV0J3MgcHVsbCBpbiBUVFIgcGFja2FnZQpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmxpYnJhcnkoJ1RUUicpCmBgYAoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpjaGFydFNlcmllcyhTUFkkU1BZLkNsb3NlLCB0aGVtZT0id2hpdGUiLCBUQT0iYWRkRU1BKDUwLCBjb2w9J2JsYWNrJyk7YWRkRU1BKDIwMCwgY29sPSdibHVlJykiKQpTUFkuRU1BLjUwPC0gRU1BKFNQWSRTUFkuQ2xvc2UsIG49NTAsICkgClNQWS5FTUEuMjAwPC0gRU1BKFNQWSRTUFkuQ2xvc2UsIG49MjAwLCApICAKYWRkVEEoU1BZLkVNQS41MCAtIFNQWS5FTUEuMjAwLGNvbD0nYmx1ZScsIHR5cGU9J2gnLGxlZ2VuZD0iNTAtMjAwIE1BIikKYGBgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmNoYXJ0U2VyaWVzKEVXUCRFV1AuQ2xvc2UsIHRoZW1lPSJ3aGl0ZSIsIFRBPSJhZGRFTUEoNTAsIGNvbD0nYmxhY2snKTthZGRFTUEoMjAwLCBjb2w9J2JsdWUnKSIpCkVXUC5FTUEuNTA8LSBFTUEoU1BZJFNQWS5DbG9zZSwgbj01MCApIApFV1AuRU1BLjIwMDwtIEVNQShTUFkkU1BZLkNsb3NlLCBuPTIwMCApICAKIyBhZGQgVGVjaG5pY2FsIEFuYWx5c2lzIHRoZSBmYXN0IG1pbnVzIHRoZSBzbG93IEVNQQphZGRUQShFV1AuRU1BLjUwIC0gRVdQLkVNQS4yMDAsY29sPSdibHVlJywgdHlwZT0naCcsbGVnZW5kPSI1MC0yMDAgTUEiKQpgYGAKV2hhdCB0aGlzIGZhc3QgLSBzbG93IHRlbGxzIHVzIGlzIHRoYXQgd2hlbiBpdCBpcyBhYm92ZSAwIChwb3NpdGl2ZSkgdGhpbmdzIGFyZSBidWxsaXNoIGFuZCB3aGVuIHRoaW5ncyBhcmUgbmVnYXRpdmUgdGhpbmdzIGFyZSBidWxsaXNoLiAKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KY2hhcnRTZXJpZXMoU1BZJFNQWS5DbG9zZSwgdGhlbWU9IndoaXRlIiwgVEE9ImFkZEVNQSg1MCwgY29sPSdibGFjaycpO2FkZEVNQSgyMDAsIGNvbD0nYmx1ZScpIikKU1BZLkVNQS4xMCA8LSBFTUEoU1BZJFNQWS5DbG9zZSwgbj0xMCApIApTUFkuRU1BLjUwIDwtIEVNQShTUFkkU1BZLkNsb3NlLCBuPTUwICkgClNQWS5FTUEuMjAwIDwtIEVNQShTUFkkU1BZLkNsb3NlLCBuPTIwMCApIApGYXN0LkRpZmYgPC0gU1BZLkVNQS4xMCAtIFNQWS5FTUEuNTAKU2xvdy5EaWZmIDwtIFNQWS5FTUEuNTAgLSBTUFkuRU1BLjIwMAphZGRUQShGYXN0LkRpZmYsIGNvbD0nYmx1ZScsIHR5cGU9J2gnLGxlZ2VuZD0iMTAtNTAgTUEgdXNlZCBmb3IgaW4tb3V0IG9mIG1hcmtldCIpCmFkZFRBKFNsb3cuRGlmZiwgY29sPSdyZWQnLCB0eXBlPSdoJyxsZWdlbmQ9IjUwLTIwMCBNQSBnaXZlIHRyZW5kaW5nIHNlbnNlIikKYGBgCgojIG1vdmVkIHRvIGRvY3VjbWVudCAiVHJhZGluZ1dpdGhUcmVuZCIKCgoK