This is an R Markdown Notebook. RMD available here (https://github.com/ghettocounselor/R_Investing/blob/master/PracticalDataScience_AnalyzingStocks_IndicatorsPUB.Rmd)

using quantmod from Cran (https://cran.r-project.org/web/packages/quantmod/)

# install.packages('quantmod')
library(quantmod)

For various elements have a look at quantmod

?quantmod

We’ll use Yahoo here but Google Finance is also supported. Syntax is slightly different. (https://finance.yahoo.com/lookup/)

We’ll call multiple symbols at once by creating a vector of symbols and then pass the basket to the getSymbols function.

symbolBasket <- c('AAPL', 'AMZN', 'BRK-B', 'SPY')
getSymbols(symbolBasket , src='yahoo')
[1] "AAPL"  "AMZN"  "BRK-B" "SPY"  

Summary will show us basics; notice we have to use `` for funny charcters.

summary(`BRK-B`)
     Index              BRK-B.Open       BRK-B.High      BRK-B.Low     
 Min.   :2007-01-03   Min.   : 45.08   Min.   : 47.7   Min.   : 44.82  
 1st Qu.:2010-02-03   1st Qu.: 78.66   1st Qu.: 79.3   1st Qu.: 77.94  
 Median :2013-03-08   Median :102.18   Median :102.7   Median :101.91  
 Mean   :2013-03-06   Mean   :116.25   Mean   :117.0   Mean   :115.34  
 3rd Qu.:2016-04-09   3rd Qu.:145.12   3rd Qu.:145.7   3rd Qu.:144.21  
 Max.   :2019-05-13   Max.   :224.00   Max.   :224.1   Max.   :221.30  
  BRK-B.Close      BRK-B.Volume       BRK-B.Adjusted  
 Min.   : 46.00   Min.   :   175000   Min.   : 46.00  
 1st Qu.: 78.78   1st Qu.:  2240650   1st Qu.: 78.78  
 Median :102.34   Median :  3296000   Median :102.34  
 Mean   :116.23   Mean   :  3707934   Mean   :116.23  
 3rd Qu.:144.94   3rd Qu.:  4392600   3rd Qu.:144.94  
 Max.   :223.76   Max.   :316134200   Max.   :223.76  

Data Prep - CLEANUP is never ending

NOTE: BRK-B IS ODD so we cannot get away from data prep ;-) ‘rename’ the object BRK-B to BRKB, not really rename but in essence. Then change names of ‘names’ in new BRKB object

BRKB <- as.xts(`BRK-B`)
names(BRKB)
[1] "BRK-B.Open"     "BRK-B.High"     "BRK-B.Low"      "BRK-B.Close"   
[5] "BRK-B.Volume"   "BRK-B.Adjusted"
names(BRKB) <- c("BRKB.Open"   ,  "BRKB.High"   ,  "BRKB.Low"   ,   "BRKB.Close"  ,  "BRKB.Volume",  "BRKB.Adjusted")
names(BRKB)
[1] "BRKB.Open"     "BRKB.High"     "BRKB.Low"      "BRKB.Close"    "BRKB.Volume"  
[6] "BRKB.Adjusted"

Charting begins here

plot(BRKB$BRKB.Open)

To view some variation on this a bit we can use this code;

lineChart(BRKB$BRKB.Open, line.type = 'h', theme = 'white', TA = NULL)

To see volumes; remove the TA

lineChart(BRKB, line.type = 'h', theme = 'white')

Barchart, type allows for high, low, close ;)

barChart(BRKB, bar.type = 'hlc', TA = NULL)

Now just a subset and in candle sticks

candleChart(BRKB, TA=NULL, subset = '2019')

Give a look to candleChart for features;

?candleChart

Not run:

getSymbols(“YHOO”) chartSeries(YHOO) chartSeries(YHOO, subset=‘last 4 months’) chartSeries(YHOO, subset=‘2007::2008-01’) chartSeries(YHOO,theme=chartTheme(‘white’)) chartSeries(YHOO,TA=NULL) #no volume chartSeries(YHOO,TA=c(addVo(),addBBands())) #add volume and Bollinger Bands from TTR

NOTE there are a ton of add… items that are very slick

addMACD() # add MACD indicator to current chart

setTA() chartSeries(YHOO) #draws chart again, this time will all indicators present

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")

moved to docucment “TradingWithTrend”

LS0tCnRpdGxlOiAiQW5hbHl6aW5nIFN0b2NrIE1hcmtldCBEYXRhIHdpdGggUiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIApSTUQgYXZhaWxhYmxlIGhlcmUgKGh0dHBzOi8vZ2l0aHViLmNvbS9naGV0dG9jb3Vuc2Vsb3IvUl9JbnZlc3RpbmcvYmxvYi9tYXN0ZXIvUHJhY3RpY2FsRGF0YVNjaWVuY2VfQW5hbHl6aW5nU3RvY2tzX0luZGljYXRvcnNQVUIuUm1kKQoKdXNpbmcgcXVhbnRtb2QgZnJvbSBDcmFuCihodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcXVhbnRtb2QvKQoKCmBgYHtyLCBlY2hvPVRSVUV9CiMgaW5zdGFsbC5wYWNrYWdlcygncXVhbnRtb2QnKQpsaWJyYXJ5KHF1YW50bW9kKQpgYGAKCkZvciB2YXJpb3VzIGVsZW1lbnRzIGhhdmUgYSBsb29rIGF0IHF1YW50bW9kCmBgYHtyIGVjaG89VFJVRX0KP3F1YW50bW9kCmBgYAoKV2UnbGwgdXNlIFlhaG9vIGhlcmUgYnV0IEdvb2dsZSBGaW5hbmNlIGlzIGFsc28gc3VwcG9ydGVkLiBTeW50YXggaXMgc2xpZ2h0bHkgZGlmZmVyZW50LiAoaHR0cHM6Ly9maW5hbmNlLnlhaG9vLmNvbS9sb29rdXAvKQoKV2UnbGwgY2FsbCBtdWx0aXBsZSBzeW1ib2xzIGF0IG9uY2UgYnkgY3JlYXRpbmcgYSB2ZWN0b3Igb2Ygc3ltYm9scwphbmQgdGhlbiBwYXNzIHRoZSBiYXNrZXQgdG8gdGhlIGdldFN5bWJvbHMgZnVuY3Rpb24uCgpgYGB7ciAgLCBlY2hvPVRSVUV9CnN5bWJvbEJhc2tldCA8LSBjKCdBQVBMJywgJ0FNWk4nLCAnQlJLLUInLCAnU1BZJykKZ2V0U3ltYm9scyhzeW1ib2xCYXNrZXQgLCBzcmM9J3lhaG9vJykKYGBgCgpTdW1tYXJ5IHdpbGwgc2hvdyB1cyBiYXNpY3M7IG5vdGljZSB3ZSBoYXZlIHRvIHVzZSBgYCBmb3IgZnVubnkgY2hhcmN0ZXJzLgpgYGB7ciAsIGluY2x1ZGU9VFJVRX0Kc3VtbWFyeShgQlJLLUJgKQpgYGAKIyMjIERhdGEgUHJlcCAtIENMRUFOVVAgaXMgbmV2ZXIgZW5kaW5nCgpOT1RFOiBCUkstQiBJUyBPREQgc28gd2UgY2Fubm90IGdldCBhd2F5IGZyb20gZGF0YSBwcmVwIDstKQoncmVuYW1lJyB0aGUgb2JqZWN0IEJSSy1CIHRvIEJSS0IsIG5vdCByZWFsbHkgcmVuYW1lIGJ1dCBpbiBlc3NlbmNlLiAKVGhlbiBjaGFuZ2UgbmFtZXMgb2YgJ25hbWVzJyBpbiBuZXcgQlJLQiBvYmplY3QKYGBgIHtyICwgZWNobz1UUlVFfQpCUktCIDwtIGFzLnh0cyhgQlJLLUJgKQpuYW1lcyhCUktCKQpgYGAKCmBgYCB7ciAsIGVjaG89VFJVRX0KbmFtZXMoQlJLQikgPC0gYygiQlJLQi5PcGVuIiAgICwgICJCUktCLkhpZ2giICAgLCAgIkJSS0IuTG93IiAgICwgICAiQlJLQi5DbG9zZSIgICwgICJCUktCLlZvbHVtZSIsICAiQlJLQi5BZGp1c3RlZCIpCm5hbWVzKEJSS0IpCmBgYAoKIyMjIENoYXJ0aW5nIGJlZ2lucyBoZXJlCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBsb3QoQlJLQiRCUktCLk9wZW4pCmBgYAoKVG8gdmlldyBzb21lIHZhcmlhdGlvbiBvbiB0aGlzIGEgYml0IHdlIGNhbiB1c2UgdGhpcyBjb2RlOwoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpsaW5lQ2hhcnQoQlJLQiRCUktCLk9wZW4sIGxpbmUudHlwZSA9ICdoJywgdGhlbWUgPSAnd2hpdGUnLCBUQSA9IE5VTEwpCmBgYAoKVG8gc2VlIHZvbHVtZXM7CnJlbW92ZSB0aGUgVEEKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmxpbmVDaGFydChCUktCLCBsaW5lLnR5cGUgPSAnaCcsIHRoZW1lID0gJ3doaXRlJykKYGBgCgpCYXJjaGFydCwgdHlwZSBhbGxvd3MgZm9yIGhpZ2gsIGxvdywgY2xvc2UgOykgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTcgfQpiYXJDaGFydChCUktCLCBiYXIudHlwZSA9ICdobGMnLCBUQSA9IE5VTEwpCmBgYAoKTm93IGp1c3QgYSBzdWJzZXQgYW5kIGluIGNhbmRsZSBzdGlja3MKYGBge3IgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KY2FuZGxlQ2hhcnQoQlJLQiwgVEE9TlVMTCwgc3Vic2V0ID0gJzIwMTknKQpgYGAKCkdpdmUgYSBsb29rIHRvIGNhbmRsZUNoYXJ0IGZvciBmZWF0dXJlczsKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03IH0KP2NhbmRsZUNoYXJ0CmBgYAoKIyMgTm90IHJ1bjogCmdldFN5bWJvbHMoIllIT08iKQpjaGFydFNlcmllcyhZSE9PKQpjaGFydFNlcmllcyhZSE9PLCBzdWJzZXQ9J2xhc3QgNCBtb250aHMnKQpjaGFydFNlcmllcyhZSE9PLCBzdWJzZXQ9JzIwMDc6OjIwMDgtMDEnKQpjaGFydFNlcmllcyhZSE9PLHRoZW1lPWNoYXJ0VGhlbWUoJ3doaXRlJykpCmNoYXJ0U2VyaWVzKFlIT08sVEE9TlVMTCkgICAjbm8gdm9sdW1lCmNoYXJ0U2VyaWVzKFlIT08sVEE9YyhhZGRWbygpLGFkZEJCYW5kcygpKSkgICNhZGQgdm9sdW1lIGFuZCBCb2xsaW5nZXIgQmFuZHMgZnJvbSBUVFIKCk5PVEUgdGhlcmUgYXJlIGEgdG9uIG9mIGFkZC4uLiBpdGVtcyB0aGF0IGFyZSB2ZXJ5IHNsaWNrCgphZGRNQUNEKCkgICAjICBhZGQgTUFDRCBpbmRpY2F0b3IgdG8gY3VycmVudCBjaGFydAoKc2V0VEEoKQpjaGFydFNlcmllcyhZSE9PKSAgICNkcmF3cyBjaGFydCBhZ2FpbiwgdGhpcyB0aW1lIHdpbGwgYWxsIGluZGljYXRvcnMgcHJlc2VudAoKIyMgRW5kKE5vdCBydW4pCgpBZGQgTUFDRCAKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmNhbmRsZUNoYXJ0KEJSS0IsIFRBPWMoYWRkTUFDRCgpLGFkZFZvKCkpLCBzdWJzZXQgPSAnMjAxOScpCmBgYAoKP2FkZE1BQ0QKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQo/YWRkTUFDRCgpCmBgYAoKYWRkTUFDRChmYXN0ID0gMTIsIHNsb3cgPSAyNiwgc2lnbmFsID0gOSwgdHlwZSA9ICJFTUEiLCBoaXN0b2dyYW0gPSBUUlVFLCBjb2wpCgpWYXJpb3VzIHdheXMgdG8gZGljdGF0ZSB0aW1lLCBpbiB0aGlzIGNhc2UgZXZlcnl0aGluZyBhZnRlci4uLgpBbHNvIGFkZGVkIGluIEFEWApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTcgfQpjYW5kbGVDaGFydChCUktCLCBUQT1jKGFkZE1BQ0QoKSxhZGRBRFgoKSksIHN1YnNldCA9ICcyMDE4LTAxOjonKQpgYGAKClZhcmlvdXMgd2F5cyB0byBkaWN0YXRlIHRpbWUsIGluIHRoaXMgY2FzZSBldmVyeXRoaW5nIGluIGJldHdlZW4uLi4KQmFja2dyb3VuZCBpcyB0aGUgJ1RoZW1lJwoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03IH0KY2FuZGxlQ2hhcnQoQlJLQiAsIFRBPWMoYWRkTUFDRCgpKSwgc3Vic2V0ID0gJzIwMTgtMDE6OjIwMTgtMDUnLCB0aGVtZSA9ICd3aGl0ZScpCmBgYAoKVmFyaW91cyBvcHRpb25zIGZvciB0aGVtZSdzIG92ZXJhbGwKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmNhbmRsZUNoYXJ0KEJSS0IgLCBUQT1jKGFkZE1BQ0QoKSksIHN1YnNldCA9ICcyMDE5LTAxOjonLCB0aGVtZSA9IGNoYXJ0VGhlbWUoJ3doaXRlJywgdXAuY29sPSdncmVlbicsZG4uY29sPSdkYXJrcmVkJykpCmBgYAoKY2hhcnRTZXJpZXMgaXMgYW5vdGhlciBjb29sIHdheSB0byBkbyB0aGlzIHNhbWUgc3R1ZmYKCmBgYHtyICwgZWNobz1UUlVFIH0KP2NoYXJ0U2VyaWVzCmBgYAoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpjaGFydFNlcmllcyhCUktCLCAKICAgICAgICAgICAgdHlwZSA9IGMoImF1dG8iLCAiY2FuZGxlc3RpY2tzIiksIAogICAgICAgICAgICBzdWJzZXQgPSAnMjAxOS0wMTo6JywKICAgICAgICAgICAgc2hvdy5ncmlkID0gVFJVRSwKICAgICAgICAgICAgbWFqb3IudGlja3M9J2F1dG8nLCBtaW5vci50aWNrcz1UUlVFLAogICAgICAgICAgICBUQT1jKGFkZE1BQ0QoKSxhZGRWbygpKSkKYGBgCgoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpjaGFydFNlcmllcyhCUktCLCAKICAgICAgICAgICAgdHlwZSA9IGMoImF1dG8iLCAiY2FuZGxlc3RpY2tzIiksIAogICAgICAgICAgICBzdWJzZXQgPSAnMjAxOC0wMTo6JywKICAgICAgICAgICAgc2hvdy5ncmlkID0gVFJVRSwKICAgICAgICAgICAgbWFqb3IudGlja3M9J2F1dG8nLCBtaW5vci50aWNrcz1UUlVFLAogICAgICAgICAgICBtdWx0aS5jb2wgPSBUUlVFLAogICAgICAgICAgICBUQT1jKGFkZE1BQ0QoKSxhZGRWbygpKSkKYGBgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmNoYXJ0U2VyaWVzKEJSS0IsIAogICAgICAgICAgICB0eXBlID0gYygiYXV0byIsICJtYXRjaHN0aWNrcyIpLCAKICAgICAgICAgICAgc3Vic2V0ID0gJzIwMTgtMDE6OicsCiAgICAgICAgICAgIHNob3cuZ3JpZCA9IFRSVUUsCiAgICAgICAgICAgIG1ham9yLnRpY2tzPSdhdXRvJywgbWlub3IudGlja3M9VFJVRSwKICAgICAgICAgICAgbXVsdGkuY29sID0gVFJVRSwKICAgICAgICAgICAgVEE9YyhhZGRNQUNEKCksYWRkVm8oKSkpCmBgYAoKCiMjIyBBZGRpbmcgaW5kaWNhdG9ycyBzdGFydHMgaGVyZSAtIGFsdGhvdWdoIHdlIGp1bXBlZCBhaGVhZCB1cCBhYm92ZQoKV2UnbGwgdXNlIFRUUiwgd2hpY2ggaXMgaW5zdGFsbGVkIHdpdGggcXVhbnRtb2QsIGlmIG5vdCB5b3UgY2FuIGluc3RhbGwgVFRSClRUUiAtIFRlY2huaWNhbCBUcmFkaW5nIFJ1bGVzClNvIENvb2whCgpgYGB7ciAsIGVjaG89VFJVRSB9Cj9UVFIKYGBgCgpMZXQncyBkbyBhIFNpbXBsZSBNb3ZpbmcgQXZlcmFnZQoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpjaGFydFNlcmllcyhCUktCLCAKICAgICAgICAgICAgdHlwZSA9IGMoImF1dG8iLCAibWF0Y2hzdGlja3MiKSwgCiAgICAgICAgICAgIHN1YnNldCA9ICcyMDE4LTAxOjonLAogICAgICAgICAgICBzaG93LmdyaWQgPSBUUlVFLAogICAgICAgICAgICBtYWpvci50aWNrcz0nYXV0bycsIG1pbm9yLnRpY2tzPVRSVUUsCiAgICAgICAgICAgIG11bHRpLmNvbCA9IEZBTFNFLAogICAgICAgICAgICBUQT1jKGFkZE1BQ0QoKSxhZGRWbygpLGFkZFNNQShuPTIwMCxjb2wgPSAnYmx1ZScpLGFkZFNNQShuPTUwLGNvbCA9ICdyZWQnKSxhZGRTTUEobj0yMixjb2wgPSAnZ3JlZW4nKSwKICAgICAgICAgICAgYWRkUk9DKG49MjAwLGNvbCA9ICdibHVlJyksYWRkUk9DKG49NTAsY29sID0gJ3JlZCcpLGFkZFJPQyhuPTIyLGNvbCA9ICdncmVlbicpKSkgIyByYXRlIG9mIGNoYW5nZQpgYGAKCkJvbGxpbmdlciBiYW5kcwpCYXNpY3M7CmFkZEJCYW5kcyhuID0gMjAsIHNkID0gMiwgbWFUeXBlID0gIlNNQSIsIGRyYXcgPSAnYmFuZHMnLCBvbiA9IC0xKQoKYGBgIHtyICwgZWNobz1UUlVFIH0KP2FkZEJCYW5kcwpgYGAKCkV4cGVyaW1lbnRhbCBCQmFuZHMgIlRoZSBwcmltYXJ5IGFkZGl0aW9uIHRvIHRoaXMgZnVuY3Rpb24gY2FsbCBvdmVyIHRoZSBUVFIgdmVyc2lvbiBpcyBpbiB0aGUgZHJhdyBhcmd1bWVudC4g4oCYYmFuZHPigJkgd2lsbCBkcmF3IHN0YW5kYXJkIEJvbGxpbmdlciBCYW5kcywg4oCYcGVyY2VudOKAmSB3aWxsIGRyYXcgQm9sbGluZ2VyICViIGFuZCDigJh3aWR0aOKAmSB3aWxsIGRyYXcgQm9saW5nZXIgQmFuZHMgV2lkdGguIFRoZSBsYXN0IHR3byB3aWxsIGJlIGRyYXduIGluIG5ldyBmaWd1cmUgcmVnaW9ucy4iCmBgYCB7ciAgLCBlY2hvPVRSVUV9Cj9hZGRfQkJhbmRzCmBgYAoKYGBgIHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmNoYXJ0U2VyaWVzKEJSS0IsIHRoZW1lPSJ3aGl0ZSIsCiBUQT0iYWRkVm8oKTthZGRCQmFuZHMoKTthZGRDQ0koKSIsIHN1YnNldCA9ICcyMDE4LTAxOjonKQpgYGAKCgpgYGAge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03IH0KY2hhcnRTZXJpZXMoQlJLQiwgdGhlbWU9IndoaXRlIiwKIFRBPSJhZGRWbygpO2FkZEJCYW5kcygpO2FkZENDSSgpIiwgc3Vic2V0ID0gJzIwMTgtMDE6OicpCmBgYAoKIyMjIENyZWF0ZSBhIEN1c3RvbSBJbmRpY2F0b3IgClN0YXJ0IHdpdGggYSBzaW1wbGUgY2hhcnQuIAoKYGBgIHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NyB9CmNoYXJ0U2VyaWVzKEJSS0IsIHRoZW1lPWNoYXJ0VGhlbWUoJ3doaXRlJyksIHVwLmNvbD0iYmxhY2siLAogZG4uY29sPSJibGFjayIpCmBgYAoKQ1JFQVRFIHR3byB2ZWN0b3JzIGFzIGEgdGltZSBzZXJpZXMgdG8gbWFrZSBhIEV4cG9uZW50aWFsIE1vdmluZyBBdmVyYWdlLiAKRU1BIGlzIGxpa2UgYSBTTUEgZXhjZXB0IGl0IGdpdmVzIG1vcmUgd2VpZ2h0IHRvIHRoZSByZWNlbnQgYWN0aXZpdHkgYW5kIAp0aGF0IG1lYW5zIGl0IHRlbmRzIHRvIG1pbWljIHRoZSBtYXJrZXQgYSBsaXR0bGUgYmV0dGVyLiAKCkJSS0IgZXhwb25lbnRpYWwgbW92aW5nIGF2ZXJhZ2VzCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KCkJSS0IuRU1BLjIwPC0gRU1BKEJSS0IkQlJLQi5DbG9zZSwgbj0yMCkKQlJLQi5FTUEuNTA8LSBFTUEoQlJLQiRCUktCLkNsb3NlLCBuPTUwKQpCUktCLkVNQS4xMDA8LSBFTUEoQlJLQiRCUktCLkNsb3NlLCBuPTEwMCkKQlJLQi5FTUEuMjAwPC0gRU1BKEJSS0IkQlJLQi5DbG9zZSwgbj0yMDApCgpjaGFydFNlcmllcyhCUktCLCB0aGVtZT1jaGFydFRoZW1lKCd3aGl0ZScpLAogICAgICAgICAgICB0eXBlID0gYygiYXV0byIsICJtYXRjaHN0aWNrcyIpLCAKICAgICAgICAgICAgc3Vic2V0ID0gJzIwMTgtMDE6OicsCiAgICAgICAgICAgIHNob3cuZ3JpZCA9IFRSVUUsCiAgICAgICAgICAgIG1ham9yLnRpY2tzPSdhdXRvJywgbWlub3IudGlja3M9VFJVRSwKICAgICAgICAgICAgbXVsdGkuY29sID0gRkFMU0UsCiAgICAgICAgICAgIFRBPWMoYWRkTUFDRCgpLGFkZFZvKCksYWRkQURYKG4gPSAxNCwgbWFUeXBlID0gIkVNQSIpKSkKCiAgICAgICAgICAgIGFkZFRBKEJSS0IuRU1BLjIwLCBvbj0xLCBjb2wgPSAiZ3JlZW4iKQogICAgICAgICAgICBhZGRUQShCUktCLkVNQS41MCwgb249MSwgY29sID0gImJsdWUiKQogICAgICAgICAgICBhZGRUQShCUktCLkVNQS4xMDAsIG9uPTEsIGNvbCA9ICJ5ZWxsb3ciKQogICAgICAgICAgICBhZGRUQShCUktCLkVNQS4yMDAsIG9uPTEsIGNvbCA9ICJyZWQiKQogICAgICAgICAgICBhZGRUQShCUktCLkVNQS4yMCAtIEJSS0IuRU1BLjIwMCwgY29sID0gImJsYWNrIiwKICAgICAgICAgICAgICAgICAgdHlwZSA9ICdoJywgbGVnZW5kID0gIjEwMC0yMDAgRU1BIikKYGBgCgoKIyMjIENyZWF0ZSBhIFNNQSBmcm9tIHNjcmF0Y2ggdG8gdW5kZXJzdGFuZAoKTGVjdHVyZSA3IChodHRwczovL3d3dy51ZGVteS5jb20vcHJhY3RpY2FsLWRhdGEtc2NpZW5jZS1hbmFseXppbmctc3RvY2stbWFya2V0LWRhdGEtd2l0aC1yL2xlYXJuL2xlY3R1cmUvMzQxMDAxMiNxdWVzdGlvbnMpCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmxpYnJhcnkocXVhbnRtb2QpCmdldFN5bWJvbHMoYygnUVFRJyksIHNyYz0neWFob28nKQpgYGAKClBsb3QgdGhlIGdyYXBoIG9mIHRoZSB0cmlwbGUgUSdzCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcGxvdChRUVEkUVFRLkNsb3NlKQpgYGAKCldlIGNob29zZSBhIHBlcmlvZCwgd2hpY2ggc2V0cyB0aGUgbnVtYmVyIG9mIHBvaW50cyBvZiBkYXRhIHRoYXQgd2lsbCBiZSByZXF1aXJlZCB0byBjcmVhdGUgb25lIHBvaW50IG9mIHJhdyBkYXRhIChhdmVyYWdlZCkgdG8gZ2V0IGEgcG9pbnQgb2YgZGF0YSBpbiBvdXIgdmlzdWFsaXphdGlvbi4gVGhlIHByaWNlX3ZlY3RvciBpcyB0aGUgZ3JvdXAgb2YgZGF0YSB3ZSB3YW50IHRvIHVzZS4gCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcGVyaW9kIDwtIDEwMApwcmljZV92ZWN0b3IgPC0gUVFRJFFRUS5DbG9zZQpsZW5ndGgocHJpY2VfdmVjdG9yKQpgYGAKCk5vdyB3ZSBuZWVkIGEgdmVjdG9yIHRvIHB1dCBvdXIgdmFsdWVzIGludG8sIHNvIHdlJ2xsIGRlZmluZSBhbiBlbXB0eSB2ZWN0b3IgdGhhdCB3ZSdsbCB0aGVuIG1ha2UgYSBsb29wIHRocm91Z2ggb3VyIHByaWNlX3ZlY3RvciB0byBmaWxsZSB0aGUgdmVjdG9yCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIDwtIGMoKQpgYGAKCkxldCdzIGxvb2sgYXQgc2VxdWVuY2UgdG8gdW5kZXJzdGFuZCA6IGFuZCAsCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30Kc2VxKDU6MTApICMgbm90ZSB0aGF0IHNlcXVlbmNlIGdpdmVzIHVzIHRoZSBudW1iZXIgb2YgdmFsdWVzIGJldHdlZW4gNSBhbmQgMTAgOykgCnNlcSg1LDEwKSAjIHRoaXMgc2VxdWVuY2UgZ2l2ZXMgdXMgZnJvbSA1IHRvIDEwIApgYGAKClRoZSAnTG9vcCcuIExldCdzIGhhdmUgYSBsb29rIGF0IGhvdyB0aGlzIGNvbWVzIHRvZ2V0aGVyLgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmZvciAoaW5kIGluIHNlcShwZXJpb2Q6bGVuZ3RoKHByaWNlX3ZlY3RvcikpKSB7ICMgc28sIHBlcmlvZCBzdGFydHMgYXQgMSBkdWUgdG8gOgogICAgICAgIHByaW50KGluZCkKICAgICAgICBicmVhayAjIHRvIHN0b3AgaXQgYXQgdGhlIGZpcnN0IGdvCn0KYGBgCgpMZXQncyBmaXggdGhhdCB3aXRoIGEgY29tbWEKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpmb3IgKGluZCBpbiBzZXEocGVyaW9kLGxlbmd0aChwcmljZV92ZWN0b3IpKSkgeyAjIHBlcmlvZCBpcyAxMDAKICAgICAgICBwcmludChpbmQpCiAgICAgICAgYnJlYWsgIyB0byBzdG9wIGl0IGF0IHRoZSBmaXJzdCBnbwp9CmBgYAoKTm93LCBiZWNhdXNlIHdlIHdhbnQgdG8gdXNlIDEwMCB2YWx1ZXMgYXMgdGhlIHBlcmlvZCB3ZSBuZWVkIHRvIHN0YXJ0IGF0IDEwMCArIDEsIGFuZCB3ZSBuZWVkIHBhcmVucy4gQW5kIHdlIG5vdyBjYW4gc3RhcnQgdGhlIGNvZGUgdG8gYXNzaWduIHRoZSB2YWx1ZXMgdG8gdGhlIG5ldyBlbXB0eSB2ZWN0b3IuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmZvciAoaW5kIGluIHNlcSgocGVyaW9kICsgMSksbGVuZ3RoKHByaWNlX3ZlY3RvcikpKSB7CiAgICBtb3ZpbmdfYXZlcmFnZV92ZWN0b3IgPC0gYyhtb3ZpbmdfYXZlcmFnZV92ZWN0b3IsICMgaGVyZSB3ZSBhcmUgc2F5aW5nIGFkZCB0aGUgbWVhbiB0byAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgbW92aW5nX2F2ZXJhZ2VfdmVjdG9yCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuKHByaWNlX3ZlY3RvclsoaW5kIC0gcGVyaW9kKTppbmRdKSkgIyAKfQpgYGAKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KaGVhZChtb3ZpbmdfYXZlcmFnZV92ZWN0b3IpCnRhaWwobW92aW5nX2F2ZXJhZ2VfdmVjdG9yKQpzdW1tYXJ5KG1vdmluZ19hdmVyYWdlX3ZlY3RvcikKbW92aW5nX2F2ZXJhZ2VfdmVjdG9yWzE6MTAwXSAjIHNob3cgMTAwIGl0ZW1zCmBgYAoKV2UgY2FuIHNlZSBmcm9tIGFib3ZlIHdoYXQgaGFzIGhhcHBlbmVkLiBXZSBoYXZlLCBzdGFydGVkIHdpdGggdGhlIGZpcnN0IDEwMCByZWNhbGN1bGF0ZWQgdGhlIG1lYW4gd2l0aCBlYWNoIDEwMCBwcmV2aW91cyB2YWx1ZXMsIGFsbCB0aGUgd2F5IHRvIHRoZSBlbmQuIAoKTGV0J3MgZ3JhcGggYSBmZXcgdGhpbmdzLiAKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpwYXIobWZyb3c9YygyLDEpKSAjIGdpdmVzIHVzIHR3byBncmFwaHMsIG9uZSBvbiB0b3Agb2YgYW5vdGhlcgpwbG90KFFRUSRRUVEuQ2xvc2UpCnBsb3QobW92aW5nX2F2ZXJhZ2VfdmVjdG9yLCB0eXBlID0gJ2wnLCBjb2wgPSAncmVkJywgbHdkPTMsCiAgICAgIG1haW4gPSBwYXN0ZSgnU01BJywgcGVyaW9kKSkKYGBgCgpMZXQncyBjaGVjayB0aGUgbGVuZ3RoLiBXZSBjYW4gc2VlIHRoYXQgd2UgZ2F2ZSB1cCB0aGF0IDEwMCB0byBjYWxjdWxhdGUgdGhlIG1lYW4gZ2l2ZW4gdGhlIHBlcmlvZCBvZiAxMDAuIFRoaXMgbWVhbnMgaG93ZXZlciB0aGF0IHdlIGNhbm5vdCBncmFwaCB0aGUgdHdvIGFnYWluc3Qgb25lIGFub3RoZXIgYmVjYXVzZSB0aGV5IGFyZSBkaWZmZXJlbnQgc2l6ZXMuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9Cmxlbmd0aChwcmljZV92ZWN0b3IpCmxlbmd0aChtb3ZpbmdfYXZlcmFnZV92ZWN0b3IpCmBgYAoKTGV0J3MgZml4IHRoYXQ7IFdlJ2xsIHBsYXkgYSBsaXR0bGUgdHJpY2sgYW5kIGZpbGwgaW4gdGhvc2UgZmlyc3QgMTAwIGNoYXJhY3RlcnMgd2l0aCBOQSB1c2luZyB0aGUgcmVwZWF0IGZ1bmN0aW9uLiBIZXJlIHdlIHNheSBnaXZlIG1lIDEwMCBOQSdzCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcmVwKE5BLCBwZXJpb2QpCmBgYAoKU28sIGluc3RlYWQgb2Ygc3RhcnRpbmcgbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIGFzIGEgYmxhbmsgdmVjdG9yIHdlJ2xsIHN0YXJ0IHdpdGggMTAwIE5BJ3MsIG9yIHdoYXRldmVyIHRoZSBwZXJpb2QgaXMuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CnBlcmlvZCA8LSAxMDAKcHJpY2VfdmVjdG9yIDwtIFFRUSRRUVEuQ2xvc2UKbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIDwtIGMocmVwKE5BLCBwZXJpb2QpKQpmb3IgKGluZCBpbiBzZXEoKHBlcmlvZCArIDEpLGxlbmd0aChwcmljZV92ZWN0b3IpKSkgewogICAgbW92aW5nX2F2ZXJhZ2VfdmVjdG9yIDwtIGMobW92aW5nX2F2ZXJhZ2VfdmVjdG9yLCAjIGhlcmUgd2UgYXJlIHNheWluZyBhZGQgdGhlIG1lYW4gdG8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG1vdmluZ19hdmVyYWdlX3ZlY3RvcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihwcmljZV92ZWN0b3JbKGluZCAtIHBlcmlvZCk6aW5kXSkpICMgCn0KYGBgCgpDaGVjayB0aGUgbGVuZ3RoLiBCaW5nby4gCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KbGVuZ3RoKG1vdmluZ19hdmVyYWdlX3ZlY3RvcikKbGVuZ3RoKHByaWNlX3ZlY3RvcikKYGBgCgpOb3cgd2UgY2FuIGFkZCBvdXIgbW92aW5nIGF2ZXJhZ2UgdG8gdGhlIFhUUyBvYmplY3Qgb2YgdGhlIFFRUSdzLgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9ClFRUSRRUVEuQ2xvc2UuU01BIDwtIG1vdmluZ19hdmVyYWdlX3ZlY3RvcgpuYW1lcyhRUVEpCmBgYAoKTGV0J3MgZ3JhcGggdGhlIGNsb3NlIGFuZCB0aGUgbmV3IFNNQS4gCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KcGxvdChRUVEkUVFRLkNsb3NlKQpsaW5lcyhRUVEkUVFRLkNsb3NlLlNNQSwgdHlwZSA9ICdsJywgY29sID0gJ3JlZCcsIGx3ZCA9IDYpCmBgYAoKTGV0J3MgY2FsbCB0aGUgc2FtZSB0aGluZyBmcm9tIFRUUiBwYWNrYWdlIDspIApXZSdsbCB1c2UgZnJlc2ggZGF0YS4gU2FtZSB0aGluZyAyIGxpbmVzIG9mIGNvZGUuIApgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmdldFN5bWJvbHMoYygnUVFRJyksIHNyYyA9ICd5YWhvbycpCmNoYXJ0U2VyaWVzKFFRUSwgdGhlbWU9J3doaXRlJywgVEE9ImFkZFNNQSgxMDApIikKYGBgCgpCYXNpY3Mgb2YgY29kZSBiZWhpbmQgd2hhdCBpcyBzaG93bi4KCiMjIyBOb3QgcnVuOgoKIyMjIyMgYGBge3IsICBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQoKIyMjIyMgcGxvdChCUktCJEJSS0IuT3BlbikKCiMjIyMjIGBgYAoKIyMjIEVuZChOb3QgcnVuKQoKIyMjIE11bHRpcGxlIE1vdmluZyBBdmVyYWdlcwpGb2xsb3dpbmcgYWxvbmcgd2l0aCBjbGFzcy4gTW9zdCBvZiB0aGlzIEkndmUgYWxyZWFkeSBkZWx2ZWQgaW50byBhbG9uZyB0aGUgd2F5IGFib3ZlLgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmxpYnJhcnkocXVhbnRtb2QpCmdldFN5bWJvbHMoYygnRVdQJywgJ1NQWScpLCBzcmM9J3lhaG9vJykKYGBgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmNoYXJ0U2VyaWVzKEVXUCRFV1AuQ2xvc2UsIHRoZW1lPSJ3aGl0ZSIsIFRBPSJhZGRFTUEoNTAsIGNvbD0nYmxhY2snKTthZGRFTUEoMjAwLCBjb2w9J2JsdWUnKSIpCmNoYXJ0U2VyaWVzKFNQWSRTUFkuQ2xvc2UsIHRoZW1lPSJ3aGl0ZSIsIFRBPSJhZGRFTUEoNTAsIGNvbD0nYmxhY2snKTthZGRFTUEoMjAwLCBjb2w9J2JsdWUnKSIpCmBgYAoKTGV0J3MgcHVsbCBpbiBUVFIgcGFja2FnZQpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmxpYnJhcnkoJ1RUUicpCmBgYAoKYGBge3IgLCBlY2hvPVRSVUUsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD03fQpjaGFydFNlcmllcyhTUFkkU1BZLkNsb3NlLCB0aGVtZT0id2hpdGUiLCBUQT0iYWRkRU1BKDUwLCBjb2w9J2JsYWNrJyk7YWRkRU1BKDIwMCwgY29sPSdibHVlJykiKQpTUFkuRU1BLjUwPC0gRU1BKFNQWSRTUFkuQ2xvc2UsIG49NTAsICkgClNQWS5FTUEuMjAwPC0gRU1BKFNQWSRTUFkuQ2xvc2UsIG49MjAwLCApICAKYWRkVEEoU1BZLkVNQS41MCAtIFNQWS5FTUEuMjAwLGNvbD0nYmx1ZScsIHR5cGU9J2gnLGxlZ2VuZD0iNTAtMjAwIE1BIikKYGBgCgpgYGB7ciAsIGVjaG89VFJVRSwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTd9CmNoYXJ0U2VyaWVzKEVXUCRFV1AuQ2xvc2UsIHRoZW1lPSJ3aGl0ZSIsIFRBPSJhZGRFTUEoNTAsIGNvbD0nYmxhY2snKTthZGRFTUEoMjAwLCBjb2w9J2JsdWUnKSIpCkVXUC5FTUEuNTA8LSBFTUEoU1BZJFNQWS5DbG9zZSwgbj01MCApIApFV1AuRU1BLjIwMDwtIEVNQShTUFkkU1BZLkNsb3NlLCBuPTIwMCApICAKIyBhZGQgVGVjaG5pY2FsIEFuYWx5c2lzIHRoZSBmYXN0IG1pbnVzIHRoZSBzbG93IEVNQQphZGRUQShFV1AuRU1BLjUwIC0gRVdQLkVNQS4yMDAsY29sPSdibHVlJywgdHlwZT0naCcsbGVnZW5kPSI1MC0yMDAgTUEiKQpgYGAKV2hhdCB0aGlzIGZhc3QgLSBzbG93IHRlbGxzIHVzIGlzIHRoYXQgd2hlbiBpdCBpcyBhYm92ZSAwIChwb3NpdGl2ZSkgdGhpbmdzIGFyZSBidWxsaXNoIGFuZCB3aGVuIHRoaW5ncyBhcmUgbmVnYXRpdmUgdGhpbmdzIGFyZSBidWxsaXNoLiAKCmBgYHtyICwgZWNobz1UUlVFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9N30KY2hhcnRTZXJpZXMoU1BZJFNQWS5DbG9zZSwgdGhlbWU9IndoaXRlIiwgVEE9ImFkZEVNQSg1MCwgY29sPSdibGFjaycpO2FkZEVNQSgyMDAsIGNvbD0nYmx1ZScpIikKU1BZLkVNQS4xMCA8LSBFTUEoU1BZJFNQWS5DbG9zZSwgbj0xMCApIApTUFkuRU1BLjUwIDwtIEVNQShTUFkkU1BZLkNsb3NlLCBuPTUwICkgClNQWS5FTUEuMjAwIDwtIEVNQShTUFkkU1BZLkNsb3NlLCBuPTIwMCApIApGYXN0LkRpZmYgPC0gU1BZLkVNQS4xMCAtIFNQWS5FTUEuNTAKU2xvdy5EaWZmIDwtIFNQWS5FTUEuNTAgLSBTUFkuRU1BLjIwMAphZGRUQShGYXN0LkRpZmYsIGNvbD0nYmx1ZScsIHR5cGU9J2gnLGxlZ2VuZD0iMTAtNTAgTUEgdXNlZCBmb3IgaW4tb3V0IG9mIG1hcmtldCIpCmFkZFRBKFNsb3cuRGlmZiwgY29sPSdyZWQnLCB0eXBlPSdoJyxsZWdlbmQ9IjUwLTIwMCBNQSBnaXZlIHRyZW5kaW5nIHNlbnNlIikKYGBgCgojIG1vdmVkIHRvIGRvY3VjbWVudCAiVHJhZGluZ1dpdGhUcmVuZCIKCgoK