作業三

download.file('https://github.com/ywchiu/rtibame/raw/master/Data/purchase.csv', 'purchase.csv')
trying URL 'https://github.com/ywchiu/rtibame/raw/master/Data/purchase.csv'
Content type 'text/plain; charset=utf-8' length 3497968 bytes (3.3 MB)
downloaded 3.3 MB
purchase <- read.csv('purchase.csv', header = TRUE, stringsAsFactors = FALSE)
#View(purchase)
str(purchase)
'data.frame':   54772 obs. of  7 variables:
 $ X       : int  0 1 2 3 4 5 6 7 8 9 ...
 $ Time    : chr  "2015-07-01 00:00:01" "2015-07-01 00:00:03" "2015-07-01 00:00:19" "2015-07-01 00:01:10" ...
 $ Action  : chr  "order" "order" "order" "order" ...
 $ User    : chr  "U312622727" "U239012343" "U10007697373" "U296328517" ...
 $ Product : chr  "P0006944501" "P0006018073" "P0002267974" "P0016144236" ...
 $ Quantity: int  1 1 1 1 1 1 1 1 1 1 ...
 $ Price   : num  1069 1680 285 550 249 ...
purchase$Time <- as.POSIXct(purchase$Time)
head(purchase$Time)
[1] "2015-07-01 00:00:01 CST" "2015-07-01 00:00:03 CST"
[3] "2015-07-01 00:00:19 CST" "2015-07-01 00:01:10 CST"
[5] "2015-07-01 00:01:36 CST" "2015-07-01 00:01:48 CST"
?strftime
#strftime(purchase$Time, '%a %A %b')
## Question 1
buyhour <- strftime(purchase$Time, '%H')
buyhourtrend <- table(buyhour)
plot(buyhourtrend, type='l')
## Question 2
library(dplyr)
vip <- purchase %>% 
  select(User, Quantity, Price) %>% 
  mutate(total_price = Quantity * Price) %>%
  group_by(User) %>% 
  summarise(final_price = sum(total_price)) %>%
  arrange(desc(final_price)) %>% 
  head(3) %>%
  select(User)
vip
## Question 3
vip <- purchase %>% 
  select(User, Quantity, Price) %>% 
  mutate(total_price = Quantity * Price) %>%
  group_by(User) %>% 
  summarise(final_price = sum(total_price)) %>%
  arrange(desc(final_price)) %>% 
  head(10) 
?barplot
vip
barplot(height = vip$final_price, names.arg =vip$User, col=factor(vip$User))

Missing Value

a <- c(1,2,3,4,5, NA)
?sum
sum(a, na.rm=TRUE)
[1] 15
#install.packages('Amelia')
library(Amelia)
#AmeliaView()
which(is.na(purchase$Price))
 [1]   109  1207  1751  2427  2489  2925  3338  3350
 [9]  3411  3507  3624  3672  3978  4278  4343  4624
[17]  4819  7034  7185  9479  9973 10921 14387 15008
[25] 15216 15452 18566 20291 20490 20687 22680 25090
[33] 27972 28036 28056 30810 31004 31016 31049 33704
[41] 34226 37989 40096 42762 42831 45121 46596 47345
[49] 47737 51506 52224
length(which(is.na(purchase$Price))) / nrow(pruchase)
[1] 0.0009311327
purchase[which(is.na(purchase$Price)), 'Product']
 [1] "P0012242731"    "P0012242760003" "P0013365715"   
 [4] "P0012242820026" "P0013293660004" "P0012242731"   
 [7] "P0012242731"    "P0012242731"    "P0013254695"   
[10] "P0012242820026" "P0022457780004" "P0012242731"   
[13] "P0012242820015" "P0012242731"    "P0013365715"   
[16] "P0021903670003" "P0012242820004" "P0021903460003"
[19] "P0012242753"    "P0012242820026" "P0013293660004"
[22] "P0012242716"    "P0022827125"    "P0013034600014"
[25] "P0000096850014" "P0012242760003" "P0022822973"   
[28] "P0022780330005" "P0022822984"    "P0013898776"   
[31] "P0013365693"    "P0022822984"    "P0013365715"   
[34] "P0004629950010" "P0013036790001" "P0022822973"   
[37] "P0013898791"    "P0022457770005" "P0000387100000"
[40] "P0022457770016" "P0022822984"    "P0013898791"   
[43] "P0023532655"    "P0001238112"    "P0024243450"   
[46] "P0022457770016" "P0013034500006" "P0012242700002"
[49] "P0005664850004" "P0022822973"    "P0025213134"   
purchase[purchase$Product == 'P0012242731', ]
purchase2 <- na.omit(purchase)
str(purchase)
'data.frame':   54772 obs. of  7 variables:
 $ X       : int  0 1 2 3 4 5 6 7 8 9 ...
 $ Time    : POSIXct, format: "2015-07-01 00:00:01" ...
 $ Action  : chr  "order" "order" "order" "order" ...
 $ User    : chr  "U312622727" "U239012343" "U10007697373" "U296328517" ...
 $ Product : chr  "P0006944501" "P0006018073" "P0002267974" "P0016144236" ...
 $ Quantity: int  1 1 1 1 1 1 1 1 1 1 ...
 $ Price   : num  1069 1680 285 550 249 ...
missmap(purchase)

library(dplyr)
vip <- purchase %>% 
  select(User, Quantity, Price) %>% 
  filter(!is.na(Price)) %>%
  mutate(total_price = Quantity * Price) %>%
  group_by(User) %>% 
  summarise(final_price = sum(total_price)) %>%
  arrange(desc(final_price)) %>% 
  head(3) %>%
  select(User)

Anscombe Dataset

Line Chart

x <- seq(1,6)
y <- x
plot(x, y, type='l', col="red")

types =c("p","l","o","b","c","s", "h", "n")
types[3]
[1] "o"
plot(x, y, type=types[3], col="red")
par(mfrow=c(2,4))

plot(x, y, type=types[1], col="red")
plot(x, y, type=types[2], col="red")
plot(x, y, type=types[3], col="red")
par(mfrow=c(2,4))

for (i in 1:length(types)){
  plot(x, y, type=types[i], col="red")
}
par(mfrow=c(2,4))

for (i in 1:length(types)){
  plot(x, y, type='n')
  lines(x, y, type=types[i], col="red")
}
par(mfrow=c(2,4))

for (i in 1:length(types)){
  title <- paste('type:', types[i])
  plot(x, y, type='n', main= title)
  lines(x, y, type=types[i], col="red")
}
par(mfrow=c(1,1))

plot(x, y, type='l', col="red")
lines(x,y, type='p', col="blue")
par(mfrow=c(1,1))

taipei <-c(92.5,132.6,168.8,159.1, 218.7)
tainan <-c(21.2, 30.6, 37.3, 84.6, 184.3)
plot(taipei, type="o", col="blue", ylim=c(0,220), xlab="Month", ylab="Rainfall")
lines(tainan, type="o", pch=22, lty=2, col="red")

Bar Chart

download.file('https://raw.githubusercontent.com/ywchiu/rtibame/master/data/house-prices.csv', destfile = 'house-price.csv')
trying URL 'https://raw.githubusercontent.com/ywchiu/rtibame/master/data/house-prices.csv'
Content type 'text/plain; charset=utf-8' length 3867 bytes
downloaded 3867 bytes
housePrice <- read.csv('house-price.csv', header = TRUE)
#View(housePrice)
bedrooms      <- housePrice$Bedrooms
bedroomsTable <- table(bedrooms)
?barplot
barplot(bedroomsTable)

barplot(height = bedroomsTable, names.arg= names(bedroomsTable), col =c("blue", "orange", "yellow", "red"))

barplot(height = bedroomsTable, names.arg= names(bedroomsTable), col = factor(names(bedroomsTable) ))

barplot(height = bedroomsTable, names.arg= names(bedroomsTable), col = factor(names(bedroomsTable) ), main = "Bedroom Type Calculate", xlab = "bedroom type", ylab = "count")

Histogram

str(cdc)
'data.frame':   20000 obs. of  9 variables:
 $ genhlth : Factor w/ 5 levels "excellent","very good",..: 3 3 3 3 2 2 2 2 3 3 ...
 $ exerany : num  0 0 1 1 0 1 1 0 0 1 ...
 $ hlthplan: num  1 1 1 1 1 1 1 1 1 1 ...
 $ smoke100: num  0 1 1 0 0 0 0 0 1 0 ...
 $ height  : num  70 64 60 66 61 64 71 67 65 70 ...
 $ weight  : int  175 125 105 132 150 114 194 170 150 180 ...
 $ wtdesire: int  175 115 105 124 130 114 185 160 130 170 ...
 $ age     : int  77 33 49 42 55 55 31 45 27 44 ...
 $ gender  : Factor w/ 2 levels "m","f": 1 2 2 2 2 2 1 1 2 1 ...
cdc
weigths <- cdc$weight
hist(weigths,breaks=500)
table(weigths %% 10)

   0    1    2    3    4    5    6    7    8 
9421  207  919  545  525 5865  481  543 1159 
   9 
 335 
par(mfrow=c(2,1))

hist(weigths,breaks=500, xlim=c(70,380))
barplot(table(cdc$weight),xlab="weight",ylab="Frequency")

PIE Chart

Scatter Plot

plot(cdc$weight, cdc$wtdesire)
Warning message:
In strsplit(code, "\n", fixed = TRUE) :
  input string 1 is invalid in this locale

str(cdc)
'data.frame':   20000 obs. of  9 variables:
 $ genhlth : Factor w/ 5 levels "excellent","very good",..: 3 3 3 3 2 2 2 2 3 3 ...
 $ exerany : num  0 0 1 1 0 1 1 0 0 1 ...
 $ hlthplan: num  1 1 1 1 1 1 1 1 1 1 ...
 $ smoke100: num  0 1 1 0 0 0 0 0 1 0 ...
 $ height  : num  70 64 60 66 61 64 71 67 65 70 ...
 $ weight  : int  175 125 105 132 150 114 194 170 150 180 ...
 $ wtdesire: int  175 115 105 124 130 114 185 160 130 170 ...
 $ age     : int  77 33 49 42 55 55 31 45 27 44 ...
 $ gender  : Factor w/ 2 levels "m","f": 1 2 2 2 2 2 1 1 2 1 ...
plot(cdc$weight, cdc$wtdesire, col =cdc$genhlth)

data(iris)
iris
plot(iris$Petal.Width, iris$Petal.Length, col=iris$Species)

# plot  + point
plot(iris$Petal.Width, iris$Petal.Length, type = 'n')
setosa <- iris[iris$Species == 'setosa',]
versicolor <- iris[iris$Species == 'versicolor',]
points(setosa$Petal.Width, setosa$Petal.Length, col="blue")
points(versicolor$Petal.Width, versicolor$Petal.Length, col="green")

plot(cdc$weight, cdc$wtdesire,xlab="weigth",ylab="weight desire",main="Scatter of Weight")
fit <- lm(cdc$wtdesire~cdc$weight)
abline(fit,col="red")

lvr_prices<- lvr_prices_mac[(lvr_prices_mac$total_price > 0)  & (lvr_prices_mac$trading_target == '<e6><e5>(<e5><9c>+撱箇)'), ]
plot(log(lvr_prices$total_price) ~ log(lvr_prices$building_sqmeter))
fit <- lm(log(total_price) ~ log(building_sqmeter), data = lvr_prices)
abline(fit, col="red")

fit2 <- lm(total_price ~ building_sqmeter, data = lvr_prices)
fit2

Call:
lm(formula = total_price ~ building_sqmeter, data = lvr_prices)

Coefficients:
     (Intercept)  building_sqmeter  
          859604            176640  
176640 / 0.3025
[1] 583933.9

Mosaic PLot

str(cdc)
'data.frame':   20000 obs. of  9 variables:
 $ genhlth : Factor w/ 5 levels "excellent","very good",..: 3 3 3 3 2 2 2 2 3 3 ...
 $ exerany : num  0 0 1 1 0 1 1 0 0 1 ...
 $ hlthplan: num  1 1 1 1 1 1 1 1 1 1 ...
 $ smoke100: num  0 1 1 0 0 0 0 0 1 0 ...
 $ height  : num  70 64 60 66 61 64 71 67 65 70 ...
 $ weight  : int  175 125 105 132 150 114 194 170 150 180 ...
 $ wtdesire: int  175 115 105 124 130 114 185 160 130 170 ...
 $ age     : int  77 33 49 42 55 55 31 45 27 44 ...
 $ gender  : Factor w/ 2 levels "m","f": 1 2 2 2 2 2 1 1 2 1 ...
smokers_gender <- table(cdc$gender, cdc$smoke100)
colnames(smokers_gender) <- c('no', 'yes')
mosaicplot(smokers_gender, col=rainbow(length(colnames(smokers_gender))))

boxplot

boxplot(cdc$height,ylab="Height",main="Box Plot of Height")

boxplot(cdc$height ~ cdc$gender,ylab="Height",main="Box Plot of Height")

?sample.int
set.seed(2)
temp <- sample.int(40, 100, replace=TRUE)
mean(temp)
[1] 20.18
temp <- c(temp, 999,999,999)
mean(temp)
[1] 48.68932
hist(temp)

boxplot(temp)

boxplot(temp[temp< 100])

legend

par(mfrow=c(1,1))
taipei <-c(92.5,132.6,168.8,159.1, 218.7)
tainan <-c(21.2, 30.6, 37.3, 84.6, 184.3)
plot(taipei, type="o", col="blue", ylim=c(0,220), xlab="Month", ylab="Rainfall")
lines(tainan, type="o", pch=22, lty=2, col="red")
legend(1,200, c("taipei","tainan"), lwd=c(2.5,2.5),col=c("blue","red"), title="Rainfall")
legend("center", c("taipei","tainan"), lwd=c(2.5,2.5),col=c("blue","red"), title="Rainfall")

bedroomsTable2
bedrooms
 3  2  4  5 
67 30 29  2 
label <- c('3 unit', '2 unit', '4 unit', '5 unit')
pie(bedroomsTable2, col = rainbow(length(label)), init.angle = 90, clockwise = TRUE)
legend("bottomleft", label,fill=rainbow(length(label)), title="units", cex=0.8)

par

showLayout=function(n){
  for(i in 1:n){
    plot(1,type="n",xaxt="n",yaxt="n",xlab="",ylab="")
    text(1, 1, labels=i, cex=10)
  }
}
par(mar=c(1,1,1,1),mfrow=c(3,2))
showLayout(6)
par(mar=c(3,3,3,3),mfrow=c(3,2))

showLayout(6)
par(mar=c(3,3,3,3),mfcol=c(3,2))

showLayout(6)

export figure

bedroomsTable2
bedrooms
 3  2  4  5 
67 30 29  2 
label <- c('3 unit', '2 unit', '4 unit', '5 unit')
png('pie.png')
pie(bedroomsTable2, col = rainbow(length(label)), init.angle = 90, clockwise = TRUE)
legend("bottomleft", label,fill=rainbow(length(label)), title="units", cex=0.8)
dev.off()
null device 
          1 
getwd()
[1] "C:/Users/USER/Desktop"

plotly

area chart

library(plotly)
taipei<-c(92.5,132.6,168.8,159.1,218.7)
tainan <-c(21.2, 30.6, 37.3, 84.6, 184.3)
plot_ly(x = month, y = taipei, type='scatter', mode='lines',name="taipei")  %>% add_trace(x = month, y = tainan ,name="tainan")

y <-list(title="Rainfall")
plot_ly(x = month, y = taipei, fill = "tozeroy", name="taipei", type='scatter', mode= 'markers')  %>% add_trace(x = month, y = tainan, fill = "tozeroy" ,name="tainan") %>% layout(yaxis= y)

total <- taipei + tainan
plot_ly(x = month, y = taipei, fill = "tozeroy", name="taipei", type='scatter', mode= 'markers')  %>% add_trace(x = month, y = total, fill = "tonexty" ,name="tainan") %>% layout(yaxis= y)

Bubble Chart

library(plotly)
d <-diamonds[sample(nrow(diamonds),1000), ]
plot_ly(d, x =d$carat, y =d$price, text=paste("Clarity: ", d$clarity),mode="markers", color =d$clarity, size =d$carat)
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter
No trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plot.ly/r/reference/#scatter

Heat Map

m <-matrix(rnorm(9), nrow=3, ncol=3)
m
           [,1]       [,2]       [,3]
[1,]  0.2518793 -1.4133258 -0.3171303
[2,]  0.6016700  0.6394737 -1.4489802
[3,] -2.4642367 -0.1220622 -1.5154701
plot_ly(z =m,x =c("a", "b", "c"), y =c("d", "e", "f"),type ="heatmap")

m <- cor(housePrice[,2:6])
plot_ly(z =m,x =colnames(m), y =colnames(m),type ="heatmap")

plot_ly(z =volcano, colorscale="Hot", type ="heatmap")

df<-read.csv("https://raw.githubusercontent.com/plotly/datasets/master/2011_us_ag_exports.csv")
df$hover <- with(df, paste(state, '<br>', "Beef", beef, "Dairy", dairy, "<br>",
"Fruits", total.fruits, "Veggies", total.veggies,
"<br>", "Wheat", wheat, "Corn", corn))
# give state boundaries a white border
l <- list(color = toRGB("white"), width = 2)
# specify some map projection/options
g <- list(
  scope = 'usa',
  projection = list(type = 'albers usa'),
  showlakes = TRUE,
  lakecolor = toRGB('white')
)
plot_geo(df, locationmode = 'USA-states') %>%
  add_trace(
    z = ~total.exports, 
    text = ~hover, 
    locations = ~code,
    color = ~total.exports, colors = 'Purples'
  ) %>%
  colorbar(title = "Millions USD") %>%
  layout(
    title = '2011 US Agriculture Exports by State<br>(Hover for breakdown)',
    geo = g
  )

subplot

data("economics")
p <-subplot(
  plot_ly(economics, x =economics$date, y =economics$uempmed, type = 'scatter', mode='line'),
  plot_ly(economics, x =economics$date, y =economics$unemploy, type = 'scatter', mode='line'),
  margin =0.05,nrows=1
  )%>%
  layout(showlegend=FALSE)
A line object has been specified, but lines is not in the mode
Adding lines to the mode...
A line object has been specified, but lines is not in the mode
Adding lines to the mode...
p

p <-subplot(
  plot_ly(economics, x =economics$date, y =economics$uempmed, type = 'scatter', mode='line'),
  plot_ly(economics, x =economics$date, y =economics$unemploy, type = 'scatter', mode='line'),
  margin =0.05,nrows=2
  )%>%
  layout(showlegend=FALSE)
A line object has been specified, but lines is not in the mode
Adding lines to the mode...
A line object has been specified, but lines is not in the mode
Adding lines to the mode...
p

使用Tableau 做財經資訊視覺化

LS0tDQp0aXRsZTogIjIwMTYxMjAzIERlbW8iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KIyDkvZzmpa3kuIkNCg0KYGBge3J9DQpkb3dubG9hZC5maWxlKCdodHRwczovL2dpdGh1Yi5jb20veXdjaGl1L3J0aWJhbWUvcmF3L21hc3Rlci9EYXRhL3B1cmNoYXNlLmNzdicsICdwdXJjaGFzZS5jc3YnKQ0KcHVyY2hhc2UgPC0gcmVhZC5jc3YoJ3B1cmNoYXNlLmNzdicsIGhlYWRlciA9IFRSVUUsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCiNWaWV3KHB1cmNoYXNlKQ0Kc3RyKHB1cmNoYXNlKQ0KDQpwdXJjaGFzZSRUaW1lIDwtIGFzLlBPU0lYY3QocHVyY2hhc2UkVGltZSkNCmhlYWQocHVyY2hhc2UkVGltZSkNCj9zdHJmdGltZQ0KI3N0cmZ0aW1lKHB1cmNoYXNlJFRpbWUsICclYSAlQSAlYicpDQoNCg0KIyMgUXVlc3Rpb24gMQ0KYnV5aG91ciA8LSBzdHJmdGltZShwdXJjaGFzZSRUaW1lLCAnJUgnKQ0KYnV5aG91cnRyZW5kIDwtIHRhYmxlKGJ1eWhvdXIpDQpwbG90KGJ1eWhvdXJ0cmVuZCwgdHlwZT0nbCcpDQoNCg0KIyMgUXVlc3Rpb24gMg0KbGlicmFyeShkcGx5cikNCnZpcCA8LSBwdXJjaGFzZSAlPiUgDQogIHNlbGVjdChVc2VyLCBRdWFudGl0eSwgUHJpY2UpICU+JSANCiAgbXV0YXRlKHRvdGFsX3ByaWNlID0gUXVhbnRpdHkgKiBQcmljZSkgJT4lDQogIGdyb3VwX2J5KFVzZXIpICU+JSANCiAgc3VtbWFyaXNlKGZpbmFsX3ByaWNlID0gc3VtKHRvdGFsX3ByaWNlKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhmaW5hbF9wcmljZSkpICU+JSANCiAgaGVhZCgzKSAlPiUNCiAgc2VsZWN0KFVzZXIpDQp2aXANCg0KIyMgUXVlc3Rpb24gMw0KdmlwIDwtIHB1cmNoYXNlICU+JSANCiAgc2VsZWN0KFVzZXIsIFF1YW50aXR5LCBQcmljZSkgJT4lIA0KICBtdXRhdGUodG90YWxfcHJpY2UgPSBRdWFudGl0eSAqIFByaWNlKSAlPiUNCiAgZ3JvdXBfYnkoVXNlcikgJT4lIA0KICBzdW1tYXJpc2UoZmluYWxfcHJpY2UgPSBzdW0odG90YWxfcHJpY2UpKSAlPiUNCiAgYXJyYW5nZShkZXNjKGZpbmFsX3ByaWNlKSkgJT4lIA0KICBoZWFkKDEwKSANCg0KP2JhcnBsb3QNCnZpcA0KYmFycGxvdChoZWlnaHQgPSB2aXAkZmluYWxfcHJpY2UsIG5hbWVzLmFyZyA9dmlwJFVzZXIsIGNvbD1mYWN0b3IodmlwJFVzZXIpKQ0KDQoNCg0KYGBgDQoNCiMjIE1pc3NpbmcgVmFsdWUNCmBgYHtyfQ0KDQphIDwtIGMoMSwyLDMsNCw1LCBOQSkNCj9zdW0NCnN1bShhLCBuYS5ybT1UUlVFKQ0KDQojaW5zdGFsbC5wYWNrYWdlcygnQW1lbGlhJykNCmxpYnJhcnkoQW1lbGlhKQ0KI0FtZWxpYVZpZXcoKQ0Kd2hpY2goaXMubmEocHVyY2hhc2UkUHJpY2UpKQ0KbGVuZ3RoKHdoaWNoKGlzLm5hKHB1cmNoYXNlJFByaWNlKSkpIC8gbnJvdyhwcnVjaGFzZSkNCnB1cmNoYXNlW3doaWNoKGlzLm5hKHB1cmNoYXNlJFByaWNlKSksICdQcm9kdWN0J10NCg0KcHVyY2hhc2VbcHVyY2hhc2UkUHJvZHVjdCA9PSAnUDAwMTIyNDI3MzEnLCBdDQoNCnB1cmNoYXNlMiA8LSBuYS5vbWl0KHB1cmNoYXNlKQ0Kc3RyKHB1cmNoYXNlKQ0KbWlzc21hcChwdXJjaGFzZSkNCg0KDQoNCmxpYnJhcnkoZHBseXIpDQp2aXAgPC0gcHVyY2hhc2UgJT4lIA0KICBzZWxlY3QoVXNlciwgUXVhbnRpdHksIFByaWNlKSAlPiUgDQogIGZpbHRlcighaXMubmEoUHJpY2UpKSAlPiUNCiAgbXV0YXRlKHRvdGFsX3ByaWNlID0gUXVhbnRpdHkgKiBQcmljZSkgJT4lDQogIGdyb3VwX2J5KFVzZXIpICU+JSANCiAgc3VtbWFyaXNlKGZpbmFsX3ByaWNlID0gc3VtKHRvdGFsX3ByaWNlKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhmaW5hbF9wcmljZSkpICU+JSANCiAgaGVhZCgzKSAlPiUNCiAgc2VsZWN0KFVzZXIpDQpgYGANCg0KIyMgQW5zY29tYmUgRGF0YXNldA0KYGBge3J9DQpkYXRhKGFuc2NvbWJlKQ0KYW5zY29tYmUNCnBsb3QoeTEgfiB4MSwgZGF0YSA9IGFuc2NvbWJlKQ0KZml0IDwtIGxtKHkxIH4geDEsIGRhdGEgPSBhbnNjb21iZSkNCmFibGluZShmaXQsIGNvbD0icmVkIikNCg0KYGBgDQoNCg0KYGBge3J9DQpncmFkZSA8LSBjKDgyLDg0LDg2LDg4KQ0KYmFycGxvdChncmFkZSwgeWxpbSA9IGMoNzgsIDkwKSkNCg0KYGBgDQojIyBMaW5lIENoYXJ0DQpgYGB7cn0NCnggPC0gc2VxKDEsNikNCnkgPC0geA0KcGxvdCh4LCB5LCB0eXBlPSdsJywgY29sPSJyZWQiKQ0KDQp0eXBlcyA9YygicCIsImwiLCJvIiwiYiIsImMiLCJzIiwgImgiLCAibiIpDQp0eXBlc1szXQ0KcGxvdCh4LCB5LCB0eXBlPXR5cGVzWzNdLCBjb2w9InJlZCIpDQoNCg0KcGFyKG1mcm93PWMoMiw0KSkNCnBsb3QoeCwgeSwgdHlwZT10eXBlc1sxXSwgY29sPSJyZWQiKQ0KcGxvdCh4LCB5LCB0eXBlPXR5cGVzWzJdLCBjb2w9InJlZCIpDQpwbG90KHgsIHksIHR5cGU9dHlwZXNbM10sIGNvbD0icmVkIikNCg0KDQpwYXIobWZyb3c9YygyLDQpKQ0KZm9yIChpIGluIDE6bGVuZ3RoKHR5cGVzKSl7DQogIHBsb3QoeCwgeSwgdHlwZT10eXBlc1tpXSwgY29sPSJyZWQiKQ0KfQ0KDQoNCnBhcihtZnJvdz1jKDIsNCkpDQpmb3IgKGkgaW4gMTpsZW5ndGgodHlwZXMpKXsNCiAgcGxvdCh4LCB5LCB0eXBlPSduJykNCiAgbGluZXMoeCwgeSwgdHlwZT10eXBlc1tpXSwgY29sPSJyZWQiKQ0KfQ0KDQpwYXIobWZyb3c9YygyLDQpKQ0KZm9yIChpIGluIDE6bGVuZ3RoKHR5cGVzKSl7DQogIHRpdGxlIDwtIHBhc3RlKCd0eXBlOicsIHR5cGVzW2ldKQ0KICBwbG90KHgsIHksIHR5cGU9J24nLCBtYWluPSB0aXRsZSkNCiAgbGluZXMoeCwgeSwgdHlwZT10eXBlc1tpXSwgY29sPSJyZWQiKQ0KfQ0KDQpwYXIobWZyb3c9YygxLDEpKQ0KcGxvdCh4LCB5LCB0eXBlPSdsJywgY29sPSJyZWQiKQ0KbGluZXMoeCx5LCB0eXBlPSdwJywgY29sPSJibHVlIikNCg0KcGFyKG1mcm93PWMoMSwxKSkNCnRhaXBlaSA8LWMoOTIuNSwxMzIuNiwxNjguOCwxNTkuMSwgMjE4LjcpDQp0YWluYW4gPC1jKDIxLjIsIDMwLjYsIDM3LjMsIDg0LjYsIDE4NC4zKQ0KcGxvdCh0YWlwZWksIHR5cGU9Im8iLCBjb2w9ImJsdWUiLCB5bGltPWMoMCwyMjApLCB4bGFiPSJNb250aCIsIHlsYWI9IlJhaW5mYWxsIikNCmxpbmVzKHRhaW5hbiwgdHlwZT0ibyIsIHBjaD0yMiwgbHR5PTIsIGNvbD0icmVkIikNCmBgYA0KIyMgQmFyIENoYXJ0DQpgYGB7cn0NCmRvd25sb2FkLmZpbGUoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS95d2NoaXUvcnRpYmFtZS9tYXN0ZXIvZGF0YS9ob3VzZS1wcmljZXMuY3N2JywgZGVzdGZpbGUgPSAnaG91c2UtcHJpY2UuY3N2JykNCmhvdXNlUHJpY2UgPC0gcmVhZC5jc3YoJ2hvdXNlLXByaWNlLmNzdicsIGhlYWRlciA9IFRSVUUpDQojVmlldyhob3VzZVByaWNlKQ0KYmVkcm9vbXMgICAgICA8LSBob3VzZVByaWNlJEJlZHJvb21zDQpiZWRyb29tc1RhYmxlIDwtIHRhYmxlKGJlZHJvb21zKQ0KP2JhcnBsb3QNCmJhcnBsb3QoYmVkcm9vbXNUYWJsZSkNCmJhcnBsb3QoaGVpZ2h0ID0gYmVkcm9vbXNUYWJsZSwgbmFtZXMuYXJnPSBuYW1lcyhiZWRyb29tc1RhYmxlKSwgY29sID1jKCJibHVlIiwgIm9yYW5nZSIsICJ5ZWxsb3ciLCAicmVkIikpDQoNCmJhcnBsb3QoaGVpZ2h0ID0gYmVkcm9vbXNUYWJsZSwgbmFtZXMuYXJnPSBuYW1lcyhiZWRyb29tc1RhYmxlKSwgY29sID0gZmFjdG9yKG5hbWVzKGJlZHJvb21zVGFibGUpICkpDQoNCg0KYmFycGxvdChoZWlnaHQgPSBiZWRyb29tc1RhYmxlLCBuYW1lcy5hcmc9IG5hbWVzKGJlZHJvb21zVGFibGUpLCBjb2wgPSBmYWN0b3IobmFtZXMoYmVkcm9vbXNUYWJsZSkgKSwgbWFpbiA9ICJCZWRyb29tIFR5cGUgQ2FsY3VsYXRlIiwgeGxhYiA9ICJiZWRyb29tIHR5cGUiLCB5bGFiID0gImNvdW50IikNCg0KYGBgDQojIyBIaXN0b2dyYW0NCmBgYHtyfQ0KDQpzdHIoY2RjKQ0KY2RjDQp3ZWlndGhzIDwtIGNkYyR3ZWlnaHQNCg0KaGlzdCh3ZWlndGhzLGJyZWFrcz01MDApDQp0YWJsZSh3ZWlndGhzICUlIDEwKQ0KDQoNCg0KcGFyKG1mcm93PWMoMiwxKSkNCmhpc3Qod2VpZ3RocyxicmVha3M9NTAwLCB4bGltPWMoNzAsMzgwKSkNCg0KYmFycGxvdCh0YWJsZShjZGMkd2VpZ2h0KSx4bGFiPSJ3ZWlnaHQiLHlsYWI9IkZyZXF1ZW5jeSIpDQoNCmBgYA0KIyMgUElFIENoYXJ0DQpgYGB7cn0NCmhvdXNlUHJpY2UgPC0gcmVhZC5jc3YoJ2hvdXNlLXByaWNlLmNzdicsIGhlYWRlciA9IFRSVUUpDQoNCmJlZHJvb21zICAgICAgPC0gaG91c2VQcmljZSRCZWRyb29tcw0KYmVkcm9vbXNUYWJsZSA8LSB0YWJsZShiZWRyb29tcykNCg0KbGFiZWwgPC0gYygnMiB1bml0JywgJzMgdW5pdCcsICc0IHVuaXQnLCAnNSB1bml0JykNCnBpZShiZWRyb29tc1RhYmxlLCBsYWJlbHMgPSBsYWJlbCwgY29sID0gcmFpbmJvdyhsZW5ndGgobGFiZWwpKSkNCj9waWUNCiNyYWluYm93KGxlbmd0aChsYWJlbCkpDQoNCmJlZHJvb21zVGFibGUyIDwtIHNvcnQodGFibGUoYmVkcm9vbXMpLCBkZWNyZWFzaW5nID0gVFJVRSkNCmJlZHJvb21zVGFibGUyDQoNCnBpZShiZWRyb29tc1RhYmxlMiwgY29sID0gcmFpbmJvdyhsZW5ndGgobGFiZWwpKSwgaW5pdC5hbmdsZSA9IDkwLCBjbG9ja3dpc2UgPSBUUlVFKQ0KYGBgDQoNCiMjIFNjYXR0ZXIgUGxvdA0KYGBge3J9DQpwbG90KGNkYyR3ZWlnaHQsIGNkYyR3dGRlc2lyZSkNCnN0cihjZGMpDQpwbG90KGNkYyR3ZWlnaHQsIGNkYyR3dGRlc2lyZSwgY29sID1jZGMkZ2VuaGx0aCkNCg0KDQpkYXRhKGlyaXMpDQppcmlzDQoNCnBsb3QoaXJpcyRQZXRhbC5XaWR0aCwgaXJpcyRQZXRhbC5MZW5ndGgsIGNvbD1pcmlzJFNwZWNpZXMpDQoNCg0KIyBwbG90ICArIHBvaW50DQoNCnBsb3QoaXJpcyRQZXRhbC5XaWR0aCwgaXJpcyRQZXRhbC5MZW5ndGgsIHR5cGUgPSAnbicpDQoNCnNldG9zYSA8LSBpcmlzW2lyaXMkU3BlY2llcyA9PSAnc2V0b3NhJyxdDQp2ZXJzaWNvbG9yIDwtIGlyaXNbaXJpcyRTcGVjaWVzID09ICd2ZXJzaWNvbG9yJyxdDQpwb2ludHMoc2V0b3NhJFBldGFsLldpZHRoLCBzZXRvc2EkUGV0YWwuTGVuZ3RoLCBjb2w9ImJsdWUiKQ0KDQpwb2ludHModmVyc2ljb2xvciRQZXRhbC5XaWR0aCwgdmVyc2ljb2xvciRQZXRhbC5MZW5ndGgsIGNvbD0iZ3JlZW4iKQ0KDQoNCg0KcGxvdChjZGMkd2VpZ2h0LCBjZGMkd3RkZXNpcmUseGxhYj0id2VpZ3RoIix5bGFiPSJ3ZWlnaHQgZGVzaXJlIixtYWluPSJTY2F0dGVyIG9mIFdlaWdodCIpDQoNCmZpdCA8LSBsbShjZGMkd3RkZXNpcmV+Y2RjJHdlaWdodCkNCg0KYWJsaW5lKGZpdCxjb2w9InJlZCIpDQoNCg0KDQpsdnJfcHJpY2VzPC0gbHZyX3ByaWNlc19tYWNbKGx2cl9wcmljZXNfbWFjJHRvdGFsX3ByaWNlID4gMCkgICYgKGx2cl9wcmljZXNfbWFjJHRyYWRpbmdfdGFyZ2V0ID09ICfmiL/lnLAo5Zyf5ZywK+W7uueJqSknKSwgXQ0KDQpwbG90KGxvZyhsdnJfcHJpY2VzJHRvdGFsX3ByaWNlKSB+IGxvZyhsdnJfcHJpY2VzJGJ1aWxkaW5nX3NxbWV0ZXIpKQ0KDQpmaXQgPC0gbG0obG9nKHRvdGFsX3ByaWNlKSB+IGxvZyhidWlsZGluZ19zcW1ldGVyKSwgZGF0YSA9IGx2cl9wcmljZXMpDQoNCmFibGluZShmaXQsIGNvbD0icmVkIikNCg0KZml0MiA8LSBsbSh0b3RhbF9wcmljZSB+IGJ1aWxkaW5nX3NxbWV0ZXIsIGRhdGEgPSBsdnJfcHJpY2VzKQ0KZml0Mg0KMTc2NjQwIC8gMC4zMDI1DQoNCmBgYA0KDQojIyBNb3NhaWMgUExvdA0KYGBge3J9DQpzdHIoY2RjKQ0Kc21va2Vyc19nZW5kZXIgPC0gdGFibGUoY2RjJGdlbmRlciwgY2RjJHNtb2tlMTAwKQ0KY29sbmFtZXMoc21va2Vyc19nZW5kZXIpIDwtIGMoJ25vJywgJ3llcycpDQptb3NhaWNwbG90KHNtb2tlcnNfZ2VuZGVyLCBjb2w9cmFpbmJvdyhsZW5ndGgoY29sbmFtZXMoc21va2Vyc19nZW5kZXIpKSkpDQpgYGANCiMjIGJveHBsb3QNCmBgYHtyfQ0KYm94cGxvdChjZGMkaGVpZ2h0LHlsYWI9IkhlaWdodCIsbWFpbj0iQm94IFBsb3Qgb2YgSGVpZ2h0IikNCg0KYm94cGxvdChjZGMkaGVpZ2h0IH4gY2RjJGdlbmRlcix5bGFiPSJIZWlnaHQiLG1haW49IkJveCBQbG90IG9mIEhlaWdodCIpDQoNCj9zYW1wbGUuaW50DQpzZXQuc2VlZCgyKQ0KdGVtcCA8LSBzYW1wbGUuaW50KDQwLCAxMDAsIHJlcGxhY2U9VFJVRSkNCm1lYW4odGVtcCkNCg0KdGVtcCA8LSBjKHRlbXAsIDk5OSw5OTksOTk5KQ0KbWVhbih0ZW1wKQ0KDQpoaXN0KHRlbXApDQpib3hwbG90KHRlbXApDQpib3hwbG90KHRlbXBbdGVtcDwgMTAwXSkNCg0KYGBgDQojIyBsZWdlbmQNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwxKSkNCnRhaXBlaSA8LWMoOTIuNSwxMzIuNiwxNjguOCwxNTkuMSwgMjE4LjcpDQp0YWluYW4gPC1jKDIxLjIsIDMwLjYsIDM3LjMsIDg0LjYsIDE4NC4zKQ0KcGxvdCh0YWlwZWksIHR5cGU9Im8iLCBjb2w9ImJsdWUiLCB5bGltPWMoMCwyMjApLCB4bGFiPSJNb250aCIsIHlsYWI9IlJhaW5mYWxsIikNCmxpbmVzKHRhaW5hbiwgdHlwZT0ibyIsIHBjaD0yMiwgbHR5PTIsIGNvbD0icmVkIikNCmxlZ2VuZCgxLDIwMCwgYygidGFpcGVpIiwidGFpbmFuIiksIGx3ZD1jKDIuNSwyLjUpLGNvbD1jKCJibHVlIiwicmVkIiksIHRpdGxlPSJSYWluZmFsbCIpDQoNCmxlZ2VuZCgiY2VudGVyIiwgYygidGFpcGVpIiwidGFpbmFuIiksIGx3ZD1jKDIuNSwyLjUpLGNvbD1jKCJibHVlIiwicmVkIiksIHRpdGxlPSJSYWluZmFsbCIpDQoNCg0KDQoNCmJlZHJvb21zVGFibGUyDQpsYWJlbCA8LSBjKCczIHVuaXQnLCAnMiB1bml0JywgJzQgdW5pdCcsICc1IHVuaXQnKQ0KcGllKGJlZHJvb21zVGFibGUyLCBjb2wgPSByYWluYm93KGxlbmd0aChsYWJlbCkpLCBpbml0LmFuZ2xlID0gOTAsIGNsb2Nrd2lzZSA9IFRSVUUpDQoNCmxlZ2VuZCgiYm90dG9tbGVmdCIsIGxhYmVsLGZpbGw9cmFpbmJvdyhsZW5ndGgobGFiZWwpKSwgdGl0bGU9InVuaXRzIiwgY2V4PTAuOCkNCg0KYGBgDQojIyBwYXINCmBgYHtyfQ0KDQpzaG93TGF5b3V0PWZ1bmN0aW9uKG4pew0KICBmb3IoaSBpbiAxOm4pew0KICAgIHBsb3QoMSx0eXBlPSJuIix4YXh0PSJuIix5YXh0PSJuIix4bGFiPSIiLHlsYWI9IiIpDQogICAgdGV4dCgxLCAxLCBsYWJlbHM9aSwgY2V4PTEwKQ0KICB9DQp9DQoNCnBhcihtYXI9YygxLDEsMSwxKSxtZnJvdz1jKDMsMikpDQpzaG93TGF5b3V0KDYpDQoNCnBhcihtYXI9YygzLDMsMywzKSxtZnJvdz1jKDMsMikpDQpzaG93TGF5b3V0KDYpDQoNCnBhcihtYXI9YygzLDMsMywzKSxtZmNvbD1jKDMsMikpDQpzaG93TGF5b3V0KDYpDQoNCmBgYA0KIyMgZXhwb3J0IGZpZ3VyZQ0KYGBge3J9DQpiZWRyb29tc1RhYmxlMg0KbGFiZWwgPC0gYygnMyB1bml0JywgJzIgdW5pdCcsICc0IHVuaXQnLCAnNSB1bml0JykNCg0KcG5nKCdwaWUucG5nJykNCnBpZShiZWRyb29tc1RhYmxlMiwgY29sID0gcmFpbmJvdyhsZW5ndGgobGFiZWwpKSwgaW5pdC5hbmdsZSA9IDkwLCBjbG9ja3dpc2UgPSBUUlVFKQ0KDQpsZWdlbmQoImJvdHRvbWxlZnQiLCBsYWJlbCxmaWxsPXJhaW5ib3cobGVuZ3RoKGxhYmVsKSksIHRpdGxlPSJ1bml0cyIsIGNleD0wLjgpDQoNCmRldi5vZmYoKQ0KZ2V0d2QoKQ0KYGBgDQojIyBwbG90bHkNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoJ3Bsb3RseScpDQpsaWJyYXJ5KHBsb3RseSkNCg0KZHMgPC1kYXRhLmZyYW1lKGxhYmVscz1jKCJBIiwgIkIiLCAiQyIpLHZhbHVlcyA9YygxMCwgMjAsIDMwKSkNCg0KP3Bsb3RfbHkNCnBsb3RfbHkoZHMsIGxhYmVscyA9IGRzJGxhYmVscywgdmFsdWVzID0gZHMkdmFsdWVzLCB0eXBlID0gJ3BpZScpDQoNCnBsb3RfbHkoZHMsIGxhYmVscyA9IGRzJGxhYmVscywgdmFsdWVzID0gZHMkdmFsdWVzLCB0eXBlID0gJ3BpZScsIGhvbGUgPSAwLjYpICU+JSBsYXlvdXQodGl0bGUgPSAiRG9udXQgQ2hhcnQiKQ0KDQpgYGANCiMjIGFyZWEgY2hhcnQNCmBgYHtyfQ0KbGlicmFyeShwbG90bHkpDQoNCnRhaXBlaTwtYyg5Mi41LDEzMi42LDE2OC44LDE1OS4xLDIxOC43KQ0KdGFpbmFuIDwtYygyMS4yLCAzMC42LCAzNy4zLCA4NC42LCAxODQuMykNCg0KcGxvdF9seSh4ID0gbW9udGgsIHkgPSB0YWlwZWksIHR5cGU9J3NjYXR0ZXInLCBtb2RlPSdsaW5lcycsbmFtZT0idGFpcGVpIikgICU+JSBhZGRfdHJhY2UoeCA9IG1vbnRoLCB5ID0gdGFpbmFuICxuYW1lPSJ0YWluYW4iKQ0KDQp5IDwtbGlzdCh0aXRsZT0iUmFpbmZhbGwiKQ0KcGxvdF9seSh4ID0gbW9udGgsIHkgPSB0YWlwZWksIGZpbGwgPSAidG96ZXJveSIsIG5hbWU9InRhaXBlaSIsIHR5cGU9J3NjYXR0ZXInLCBtb2RlPSAnbWFya2VycycpICAlPiUgYWRkX3RyYWNlKHggPSBtb250aCwgeSA9IHRhaW5hbiwgZmlsbCA9ICJ0b3plcm95IiAsbmFtZT0idGFpbmFuIikgJT4lIGxheW91dCh5YXhpcz0geSkNCg0KdG90YWwgPC0gdGFpcGVpICsgdGFpbmFuDQpwbG90X2x5KHggPSBtb250aCwgeSA9IHRhaXBlaSwgZmlsbCA9ICJ0b3plcm95IiwgbmFtZT0idGFpcGVpIiwgdHlwZT0nc2NhdHRlcicsIG1vZGU9ICdtYXJrZXJzJykgICU+JSBhZGRfdHJhY2UoeCA9IG1vbnRoLCB5ID0gdG90YWwsIGZpbGwgPSAidG9uZXh0eSIgLG5hbWU9InRhaW5hbiIpICU+JSBsYXlvdXQoeWF4aXM9IHkpDQoNCg0KYGBgDQoNCiMjIEJ1YmJsZSBDaGFydA0KYGBge3J9DQpsaWJyYXJ5KHBsb3RseSkNCmQgPC1kaWFtb25kc1tzYW1wbGUobnJvdyhkaWFtb25kcyksMTAwMCksIF0NCnBsb3RfbHkoZCwgeCA9ZCRjYXJhdCwgeSA9ZCRwcmljZSwgdGV4dD1wYXN0ZSgiQ2xhcml0eTogIiwgZCRjbGFyaXR5KSxtb2RlPSJtYXJrZXJzIiwgY29sb3IgPWQkY2xhcml0eSwgc2l6ZSA9ZCRjYXJhdCkNCg0KDQpgYGANCg0KIyMgSGVhdCBNYXANCmBgYHtyfQ0KDQptIDwtbWF0cml4KHJub3JtKDkpLCBucm93PTMsIG5jb2w9MykNCm0NCg0KcGxvdF9seSh6ID1tLHggPWMoImEiLCAiYiIsICJjIiksIHkgPWMoImQiLCAiZSIsICJmIiksdHlwZSA9ImhlYXRtYXAiKQ0KDQoNCm0gPC0gY29yKGhvdXNlUHJpY2VbLDI6Nl0pDQoNCnBsb3RfbHkoeiA9bSx4ID1jb2xuYW1lcyhtKSwgeSA9Y29sbmFtZXMobSksdHlwZSA9ImhlYXRtYXAiKQ0KcGxvdF9seSh6ID12b2xjYW5vLCBjb2xvcnNjYWxlPSJIb3QiLCB0eXBlID0iaGVhdG1hcCIpDQoNCmRmPC1yZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Bsb3RseS9kYXRhc2V0cy9tYXN0ZXIvMjAxMV91c19hZ19leHBvcnRzLmNzdiIpDQoNCmRmJGhvdmVyIDwtIHdpdGgoZGYsIHBhc3RlKHN0YXRlLCAnPGJyPicsICJCZWVmIiwgYmVlZiwgIkRhaXJ5IiwgZGFpcnksICI8YnI+IiwNCiJGcnVpdHMiLCB0b3RhbC5mcnVpdHMsICJWZWdnaWVzIiwgdG90YWwudmVnZ2llcywNCiI8YnI+IiwgIldoZWF0Iiwgd2hlYXQsICJDb3JuIiwgY29ybikpDQoNCiMgZ2l2ZSBzdGF0ZSBib3VuZGFyaWVzIGEgd2hpdGUgYm9yZGVyDQpsIDwtIGxpc3QoY29sb3IgPSB0b1JHQigid2hpdGUiKSwgd2lkdGggPSAyKQ0KDQojIHNwZWNpZnkgc29tZSBtYXAgcHJvamVjdGlvbi9vcHRpb25zDQpnIDwtIGxpc3QoDQogIHNjb3BlID0gJ3VzYScsDQogIHByb2plY3Rpb24gPSBsaXN0KHR5cGUgPSAnYWxiZXJzIHVzYScpLA0KICBzaG93bGFrZXMgPSBUUlVFLA0KICBsYWtlY29sb3IgPSB0b1JHQignd2hpdGUnKQ0KKQ0KDQpwbG90X2dlbyhkZiwgbG9jYXRpb25tb2RlID0gJ1VTQS1zdGF0ZXMnKSAlPiUNCiAgYWRkX3RyYWNlKA0KICAgIHogPSB+dG90YWwuZXhwb3J0cywgDQogICAgdGV4dCA9IH5ob3ZlciwgDQogICAgbG9jYXRpb25zID0gfmNvZGUsDQogICAgY29sb3IgPSB+dG90YWwuZXhwb3J0cywgY29sb3JzID0gJ1B1cnBsZXMnDQogICkgJT4lDQogIGNvbG9yYmFyKHRpdGxlID0gIk1pbGxpb25zIFVTRCIpICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAnMjAxMSBVUyBBZ3JpY3VsdHVyZSBFeHBvcnRzIGJ5IFN0YXRlPGJyPihIb3ZlciBmb3IgYnJlYWtkb3duKScsDQogICAgZ2VvID0gZw0KICApDQoNCmBgYA0KIyMgc3VicGxvdA0KYGBge3J9DQpkYXRhKCJlY29ub21pY3MiKQ0KcCA8LXN1YnBsb3QoDQogIHBsb3RfbHkoZWNvbm9taWNzLCB4ID1lY29ub21pY3MkZGF0ZSwgeSA9ZWNvbm9taWNzJHVlbXBtZWQsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGU9J2xpbmUnKSwNCiAgcGxvdF9seShlY29ub21pY3MsIHggPWVjb25vbWljcyRkYXRlLCB5ID1lY29ub21pY3MkdW5lbXBsb3ksIHR5cGUgPSAnc2NhdHRlcicsIG1vZGU9J2xpbmUnKSwNCiAgbWFyZ2luID0wLjA1LG5yb3dzPTENCiAgKSU+JQ0KICBsYXlvdXQoc2hvd2xlZ2VuZD1GQUxTRSkNCnANCg0KDQpwIDwtc3VicGxvdCgNCiAgcGxvdF9seShlY29ub21pY3MsIHggPWVjb25vbWljcyRkYXRlLCB5ID1lY29ub21pY3MkdWVtcG1lZCwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZT0nbGluZScpLA0KICBwbG90X2x5KGVjb25vbWljcywgeCA9ZWNvbm9taWNzJGRhdGUsIHkgPWVjb25vbWljcyR1bmVtcGxveSwgdHlwZSA9ICdzY2F0dGVyJywgbW9kZT0nbGluZScpLA0KICBtYXJnaW4gPTAuMDUsbnJvd3M9Mg0KICApJT4lDQogIGxheW91dChzaG93bGVnZW5kPUZBTFNFKQ0KcA0KDQoNCmBgYA0KDQoNCiMjIOS9v+eUqFRhYmxlYXUg5YGa6LKh57aT6LOH6KiK6KaW6Ka65YyWDQotIGh0dHBzOi8veW91dHUuYmUvQzB4Y1h2dGU4TjANCg0KDQo=