【GGPlot2】
Sys.setlocale("LC_ALL",'C') #清除國家偵測設定
[1] "C"
library(dslabs) # 內建資料
library(dplyr)
library(ggplot2)
library(ggthemes) # 套用主題
library(ggrepel) # 標籤不重疊
【Basic To Know】
常用:
ggplot() +
geom_point() # 散布圖
geom_line()
geom_bar() # 長條圖
geom_histogram() # 直方圖
geom_density() # 機率密度圖
geom_boxplot()
補充:
geom_histogram(binwidth = 1) # 決定直方圖每根寬度
scale_x_continuous(trans = "log2") # 改變尺度
【加標籤】
geom_label(aes(label = abb)) # 把點變成標籤狀
geom_text(aes(label = abb)) # 在點上方加名稱
# 若已在ggplot裡加了label的參數,想要增加顏色只需在label裡指定
murders %>% ggplot(aes(population, total, label=abb)) +
geom_label(color="blue")

# 若想改用region作為顏色類別,則要用aes來包起來,不然會與abb衝突
murders %>% ggplot(aes(population, total, label=abb)) +
geom_label(aes(color=region))

【散布圖 Scatter Diagram】
gapminder_Africa_2010 <- gapminder %>%
filter(continent == "Africa" & year == 2010 & !is.na(gdp)) %>%
mutate(dollars_per_day = gdp/population/365)
gapminder_Africa_2010 %>%
ggplot(aes(dollars_per_day, infant_mortality, color = region)) +
geom_point() +
scale_x_continuous(trans = "log2") +
geom_text(aes(label = country)) +
facet_grid(.~year)

【機率密度圖 Density】
heights %>%
ggplot(aes(height)) +
geom_density(aes(group=sex))

heights %>%
ggplot(aes(height, color = sex)) + # 以color直接取代group
geom_density() # color是線(外框)的顏色

heights %>%
ggplot(aes(height, fill = sex)) + # fill是填滿顏色
geom_density(alpha = 0.2) # alpha用來調整透明度

daydollars <- gapminder %>%
filter(continent == "Africa" & year %in% c(1970,2010) & !is.na(gdp)) %>%
mutate(dollars_per_day = gdp/population/365)
daydollars %>%
ggplot(aes(dollars_per_day, fill = region)) + # fill看所有region的密度分布
geom_density(alpha = 0.2, bw = 0.5, position = "stack") + # bw是平滑的程度(常態/心電圖那樣的陡) # 用stack疊各region
scale_x_continuous(trans = "log2") +
facet_grid(.~year) # 左右並排

gapminder %>%
filter(continent == "Africa" & year %in% c(1970,2010) & !is.na(gdp) & !is.na(year) & !is.na(infant_mortality)) %>%
mutate(dollars_per_day = gdp/population/365) %>%
ggplot(aes(dollars_per_day, infant_mortality, color = region)) +
geom_point() +
scale_x_continuous(trans = "log2") +
geom_text(aes(label = country)) +
facet_grid(year~.) # 上下並排

【分位圖 qq-plot】
p <- heights %>% filter(sex=="Male") %>%
ggplot(aes(sample = height))
params <- heights %>% filter(sex=="Male") %>%
summarize(mean = mean(height), sd = sd(height))
p + geom_qq(dparams = params)

heights %>%
filter(sex=="Male") %>%
ggplot(aes(sample = scale(height))) +
geom_qq() +
geom_abline()

【箱形圖 Boxplot】
murders %>% mutate(rate = total/population*100000) %>%
mutate(region = reorder(region, rate, FUN = median)) %>%
ggplot(aes(region,rate)) +
geom_boxplot() +
geom_point() + # boxplot加上線上的觀察點
geom_jitter(width = 0.1, alpha = 0.2) # 線外的觀察點(分布樣態)
Warning message:
In strsplit(code, "\n", fixed = TRUE) :
input string 1 is invalid in this locale

【Comparing Distribution】
facet_grid(year ~ group) # 兩張圖排在一起 # 前者:上下;後者:左右
geom_boxplot(aes(region,dollars_per_day, fill = facor(year)))
範例:
daydollars <- gapminder %>%
filter(continent == "Africa" & year %in% c(1970,2010) & !is.na(gdp)) %>%
mutate(dollars_per_day = gdp/population/365)
daydollars %>%
ggplot(aes(dollars_per_day)) +
geom_density() +
scale_x_continuous(trans = "log2") +
facet_grid(.~year)

【調整技巧】
重新排序(default is alphabetical)
region = factor(c("Asia","Asia","West","West","West"))
levels(region)
[1] "Asia" "West"
value = c(10, 11, 12, 6, 4)
region = reorder(region, value, FUN = mean)
levels(region)
[1] "West" "Asia"
mutate(region = reorder(region, value, FUN = median))
mutate(group = factor(group, levels = c("Others", "Latin America", "East Asia")))
【美觀技巧】
themes_economist() # 背景主題
themes_fivethirtyeight() # 背景主題
geom_text_repel() # 讓圖表上的各點標籤不互相重疊
theme(axis.text.x = element_text(angle = 30, hjust = 1)) # 旋轉x軸標籤使之不重疊且好閱讀
範例:
# First define the slope of line
r = murders %>% summarize(rate = sum(total)/sum(population)*10^6) %>% .$rate
# Make the plot
murders %>% ggplot(aes(population/10^6, total, label=abb)) +
geom_abline(intercept = log10(r), lty = 2, color = "darkgrey") +
geom_point(aes(col=region), size = 3) +
geom_text_repel() +
scale_x_log10() + scale_y_log10() +
xlab("Population in millions (log scale)") + ylab("Total number of murders (log scale)") +
ggtitle("US Gun Murders in US 2010") +
scale_color_discrete(name = "Region") +
theme_economist()

【Dplyr】
【處理資料】
mean(na_example, na.rm = TRUE) # 移除NA
!is.na(gdp) # 移除NA
X$a = NULL # 移除column
library(dplyr)
library(NHANES)
data(NHANES)
ref_avg <- NHANES %>%
filter(AgeDecade == " 20-29" & Gender == "female") %>% # 篩選要分析的觀察值
summarize(average = mean(BPSysAve, na.rm = TRUE),
standard_deviation = sd(BPSysAve, na.rm=TRUE)) # 在ref_avg底下製作多個column
ref_avg
[38;5;246m# A tibble: 1 x 2[39m
average standard_deviation
[3m[38;5;246m<dbl>[39m[23m [3m[38;5;246m<dbl>[39m[23m
[38;5;250m1[39m 108. 10.1
ref_avg %>% .$average # 因為回傳只有單值, 將table轉換成numeric
[1] 108.4224
NHANES %>%
group_by(AgeDecade, Gender) %>%
summarize(average = mean(BPSysAve, na.rm = TRUE),
standard_deviation = sd(BPSysAve, na.rm = TRUE))
# 使用group_by可以輸出每個分類的值
NHANES %>% top_n(10, BPSys1)
[38;5;246m# A tibble: 14 x 76[39m
ID SurveyYr Gender Age AgeDecade AgeMonths Race1 Race3
[3m[38;5;246m<int>[39m[23m [3m[38;5;246m<fct>[39m[23m [3m[38;5;246m<fct>[39m[23m [3m[38;5;246m<int>[39m[23m [3m[38;5;246m<fct>[39m[23m [3m[38;5;246m<int>[39m[23m [3m[38;5;246m<fct>[39m[23m [3m[38;5;246m<fct>[39m[23m
[38;5;250m 1[39m [4m5[24m[4m3[24m371 2009_10 male 68 [38;5;246m"[39m 60-69[38;5;246m"[39m 817 White [31mNA[39m
[38;5;250m 2[39m [4m5[24m[4m5[24m311 2009_10 female 55 [38;5;246m"[39m 50-59[38;5;246m"[39m 671 Hispanic [31mNA[39m
[38;5;250m 3[39m [4m6[24m[4m5[24m296 2011_12 male 80 [31mNA[39m [31mNA[39m White White
[38;5;250m 4[39m [4m6[24m[4m5[24m296 2011_12 male 80 [31mNA[39m [31mNA[39m White White
[38;5;250m 5[39m [4m6[24m[4m5[24m475 2011_12 female 44 [38;5;246m"[39m 40-49[38;5;246m"[39m [31mNA[39m Black Black
[38;5;250m 6[39m [4m6[24m[4m6[24m873 2011_12 female 54 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m White White
[38;5;250m 7[39m [4m6[24m[4m6[24m873 2011_12 female 54 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m White White
[38;5;250m 8[39m [4m6[24m[4m7[24m957 2011_12 male 50 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m Black Black
[38;5;250m 9[39m [4m6[24m[4m7[24m957 2011_12 male 50 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m Black Black
[38;5;250m10[39m [4m6[24m[4m8[24m301 2011_12 male 59 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m White White
[38;5;250m11[39m [4m6[24m[4m8[24m301 2011_12 male 59 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m White White
[38;5;250m12[39m [4m6[24m[4m8[24m301 2011_12 male 59 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m White White
[38;5;250m13[39m [4m6[24m[4m8[24m301 2011_12 male 59 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m White White
[38;5;250m14[39m [4m6[24m[4m8[24m301 2011_12 male 59 [38;5;246m"[39m 50-59[38;5;246m"[39m [31mNA[39m White White
[38;5;246m# ... with 68 more variables: Education [3m[38;5;246m<fct>[38;5;246m[23m, MaritalStatus [3m[38;5;246m<fct>[38;5;246m[23m,
# HHIncome [3m[38;5;246m<fct>[38;5;246m[23m, HHIncomeMid [3m[38;5;246m<int>[38;5;246m[23m, Poverty [3m[38;5;246m<dbl>[38;5;246m[23m,
# HomeRooms [3m[38;5;246m<int>[38;5;246m[23m, HomeOwn [3m[38;5;246m<fct>[38;5;246m[23m, Work [3m[38;5;246m<fct>[38;5;246m[23m, Weight [3m[38;5;246m<dbl>[38;5;246m[23m,
# Length [3m[38;5;246m<dbl>[38;5;246m[23m, HeadCirc [3m[38;5;246m<dbl>[38;5;246m[23m, Height [3m[38;5;246m<dbl>[38;5;246m[23m, BMI [3m[38;5;246m<dbl>[38;5;246m[23m,
# BMICatUnder20yrs [3m[38;5;246m<fct>[38;5;246m[23m, BMI_WHO [3m[38;5;246m<fct>[38;5;246m[23m, Pulse [3m[38;5;246m<int>[38;5;246m[23m,
# BPSysAve [3m[38;5;246m<int>[38;5;246m[23m, BPDiaAve [3m[38;5;246m<int>[38;5;246m[23m, BPSys1 [3m[38;5;246m<int>[38;5;246m[23m, BPDia1 [3m[38;5;246m<int>[38;5;246m[23m,
# BPSys2 [3m[38;5;246m<int>[38;5;246m[23m, BPDia2 [3m[38;5;246m<int>[38;5;246m[23m, BPSys3 [3m[38;5;246m<int>[38;5;246m[23m, BPDia3 [3m[38;5;246m<int>[38;5;246m[23m,
# Testosterone [3m[38;5;246m<dbl>[38;5;246m[23m, DirectChol [3m[38;5;246m<dbl>[38;5;246m[23m, TotChol [3m[38;5;246m<dbl>[38;5;246m[23m,
# UrineVol1 [3m[38;5;246m<int>[38;5;246m[23m, UrineFlow1 [3m[38;5;246m<dbl>[38;5;246m[23m, UrineVol2 [3m[38;5;246m<int>[38;5;246m[23m,
# UrineFlow2 [3m[38;5;246m<dbl>[38;5;246m[23m, Diabetes [3m[38;5;246m<fct>[38;5;246m[23m, DiabetesAge [3m[38;5;246m<int>[38;5;246m[23m,
# HealthGen [3m[38;5;246m<fct>[38;5;246m[23m, DaysPhysHlthBad [3m[38;5;246m<int>[38;5;246m[23m, DaysMentHlthBad [3m[38;5;246m<int>[38;5;246m[23m,
# LittleInterest [3m[38;5;246m<fct>[38;5;246m[23m, Depressed [3m[38;5;246m<fct>[38;5;246m[23m, nPregnancies [3m[38;5;246m<int>[38;5;246m[23m,
# nBabies [3m[38;5;246m<int>[38;5;246m[23m, Age1stBaby [3m[38;5;246m<int>[38;5;246m[23m, SleepHrsNight [3m[38;5;246m<int>[38;5;246m[23m,
# SleepTrouble [3m[38;5;246m<fct>[38;5;246m[23m, PhysActive [3m[38;5;246m<fct>[38;5;246m[23m, PhysActiveDays [3m[38;5;246m<int>[38;5;246m[23m,
# TVHrsDay [3m[38;5;246m<fct>[38;5;246m[23m, CompHrsDay [3m[38;5;246m<fct>[38;5;246m[23m, TVHrsDayChild [3m[38;5;246m<int>[38;5;246m[23m,
# CompHrsDayChild [3m[38;5;246m<int>[38;5;246m[23m, Alcohol12PlusYr [3m[38;5;246m<fct>[38;5;246m[23m, AlcoholDay [3m[38;5;246m<int>[38;5;246m[23m,
# AlcoholYear [3m[38;5;246m<int>[38;5;246m[23m, SmokeNow [3m[38;5;246m<fct>[38;5;246m[23m, Smoke100 [3m[38;5;246m<fct>[38;5;246m[23m,
# Smoke100n [3m[38;5;246m<fct>[38;5;246m[23m, SmokeAge [3m[38;5;246m<int>[38;5;246m[23m, Marijuana [3m[38;5;246m<fct>[38;5;246m[23m,
# AgeFirstMarij [3m[38;5;246m<int>[38;5;246m[23m, RegularMarij [3m[38;5;246m<fct>[38;5;246m[23m, AgeRegMarij [3m[38;5;246m<int>[38;5;246m[23m,
# HardDrugs [3m[38;5;246m<fct>[38;5;246m[23m, SexEver [3m[38;5;246m<fct>[38;5;246m[23m, SexAge [3m[38;5;246m<int>[38;5;246m[23m,
# SexNumPartnLife [3m[38;5;246m<int>[38;5;246m[23m, SexNumPartYear [3m[38;5;246m<int>[38;5;246m[23m, SameSex [3m[38;5;246m<fct>[38;5;246m[23m,
# SexOrientation [3m[38;5;246m<fct>[38;5;246m[23m, PregnantNow [3m[38;5;246m<fct>[38;5;246m[23m[39m
# 查看前十名
library(dplyr)
library(NHANES)
data(NHANES)
NHANES %>%
filter(AgeDecade == " 40-49", Gender == "male") %>%
group_by(Race1) %>%
summarize(average = mean(BPSysAve, na.rm = TRUE),
standard_deviation = sd(BPSysAve, na.rm = TRUE)) %>%
arrange(desc(average))
[38;5;246m# A tibble: 5 x 3[39m
Race1 average standard_deviation
[3m[38;5;246m<fct>[39m[23m [3m[38;5;246m<dbl>[39m[23m [3m[38;5;246m<dbl>[39m[23m
[38;5;250m1[39m Black 126. 17.1
[38;5;250m2[39m Mexican 122. 13.9
[38;5;250m3[39m Hispanic 122. 11.1
[38;5;250m4[39m Other 120. 16.2
[38;5;250m5[39m White 120. 13.4
NHANES %>% group_by(AgeDecade, Gender) %>% summarise(n=n())
# n=n() means that a variable named n will be assigned the number of rows (think number of observations) in the summarized data.
# summarize that by the total number of rows in each of the new groups.
【Gapminder】
countries = c("Vietnam", "United States")
years = seq(1960, 2010, 1)
tab = gapminder %>%
filter(country %in% countries, year %in% years)
p <- tab %>% ggplot(aes(year, life_expectancy, color = country)) +
geom_line()
daydollars <- gapminder %>%
filter(continent == "Africa" & year == 2010 & !is.na(gdp)) %>%
mutate(dollars_per_day = gdp/population/365)
daydollars %>%
ggplot(aes(dollars_per_day)) +
geom_density() +
scale_x_continuous(trans = "log2")

【Visualization Principles】
- Karl Broman entitled “Creating Effective Figures and Tables”
- 圖表若沒有從0開始,很容易放大(誇飾)對象之間的差距
- 本來可能只差3000多(19399,15780),從圖形上看起來卻差了五倍
- 但若縮小難以比較對象,這時可以取適當的轉換值(log)保持比例
- 長條圖適合體現數值的差距
- Pie Charts are never prefered.
- 人類難以快速辨識角度跟面積的比例,因此能用長條圖就不要用圓餅圖
- 以降冪或升冪排列(R預設是按照字母順序,應使用reorder重新排列)
- box圖適合展現分佈(平均值、標準差、異常值)
- 不要用3D或多維度的圖形,徒增困惑
- 避免過多小數點(R預設7位,事實上只需要1~2位即可)
- 用Table排列數字時,使用column比用row還能夠清楚比較大小
library(RColorBrewer)
data(us_contagious_diseases)
the_disease = "Smallpox"
us_contagious_diseases = subset(us_contagious_diseases, us_contagious_diseases$weeks_reporting >= 10)
# years = us_contagious_diseases$year[which(us_contagious_diseases$weeks_reporting >= 10)]
years = unique(us_contagious_diseases$year)
dat <- us_contagious_diseases %>%
filter(!state%in%c("Hawaii","Alaska") & disease == the_disease & year%in%years) %>%
mutate(rate = count / population * 10000) %>%
mutate(state = reorder(state, rate))
dat %>% ggplot(aes(year, state, fill = rate)) +
geom_tile(color = "grey50") +
scale_x_continuous(expand=c(0,0)) +
scale_fill_gradientn(colors = brewer.pal(9, "Reds"), trans = "sqrt") +
theme_minimal() +
theme(panel.grid = element_blank()) +
ggtitle(the_disease) +
ylab("") +
xlab("")

data(us_contagious_diseases)
us_contagious_diseases = subset(us_contagious_diseases, us_contagious_diseases$weeks_reporting >= 10)
years = unique(us_contagious_diseases$year)
the_disease = "Smallpox"
dat <- us_contagious_diseases %>%
filter(!state%in%c("Hawaii","Alaska") & disease == the_disease & year%in%years) %>%
mutate(rate = count / population * 10000) %>%
mutate(state = reorder(state, rate))
avg <- us_contagious_diseases %>%
filter(disease==the_disease) %>% group_by(year) %>%
summarize(us_rate = sum(count, na.rm=TRUE)/sum(population, na.rm=TRUE)*10000)
dat %>% ggplot() +
geom_line(aes(year, rate, group = state), color = "grey50",
show.legend = FALSE, alpha = 0.2, size = 1) +
geom_line(mapping = aes(year, us_rate), data = avg, size = 1, color = "black") +
scale_y_continuous(trans = "sqrt", breaks = c(5,25,125,300)) +
ggtitle("Cases per 10,000 by state") +
xlab("") +
ylab("") +
geom_text(data = data.frame(x=1955, y=50), mapping = aes(x, y, label="US average"), color="black") +
geom_vline(xintercept=1963, col = "blue")

data(us_contagious_diseases)
Warning message:
In scan(file = file, what = what, sep = sep, quote = quote, dec = dec, :
EOF within quoted string
us_contagious_diseases = subset(us_contagious_diseases, us_contagious_diseases$weeks_reporting >= 10)
years = unique(us_contagious_diseases$year)
us_contagious_diseases %>% filter(state=="California" & year%in%years) %>%
group_by(year, disease) %>%
summarize(rate = sum(count)/sum(population)*10000) %>%
ggplot(aes(year, rate, color = disease)) +
geom_line()

us_contagious_diseases %>%
filter(!is.na(population)) %>%
mutate(rate = count / population * 10000) %>%
mutate(state = reorder(state, rate)) %>%
group_by(year, disease) %>%
summarize(rate = sum(count, na.rm=TRUE)/sum(population, na.rm=TRUE)*10000) %>%
ggplot(aes(year, rate, color = disease)) +
geom_line()

LS0tDQp0aXRsZTogIlZpc3VhbGl6YXRpb24gUiBOb3RlYm9vayINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyDjgJDnm67pjITjgJENCg0KWzEuIEdHUGxvdDJdKCNuMSkgPGJyPg0KWzIuIERwbHlyXSgjbjIpIDxicj4NClszLiBHYXBtaW5kZXJdKCNuMykgPGJyPg0KWzQuIFZpc3VhbGl6YXRpb24gUHJpbmNpcGxlc10oI240KSA8YnI+DQoNCmh0dHBzOi8vcmFmYWxhYi5naXRodWIuaW8vZHNib29rL2ludHJvZHVjdGlvbi0yLmh0bWwNCg0KPGhyPg0KDQojIyMgPGEgaWQ9Im4xIj48L2E+44CQR0dQbG90MuOAkQ0KDQpgYGB7cn0NClN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsJ0MnKSAj5riF6Zmk5ZyL5a625YG15ris6Kit5a6aDQpsaWJyYXJ5KGRzbGFicykgICAgIyDlhaflu7ros4fmlpkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdndGhlbWVzKSAgIyDlpZfnlKjkuLvpoYwNCmxpYnJhcnkoZ2dyZXBlbCkgICAjIOaomeexpOS4jemHjeeWig0KYGBgDQoNCjxjZW50ZXI+DQoNCiFbR0dQbG90MiBDaGVhdCBTaGVldDFdKGdncGxvdDItY2hlYXRzaGVldC5wbmcpDQoNCiFbR0dQbG90MiBDaGVhdCBTaGVldDJdKGdncGxvdDItY2hlYXRzaGVldDIucG5nKQ0KDQo8L2NlbnRlcj4NCg0KIyMjIyDjgJBCYXNpYyBUbyBLbm9344CRDQoNCuW4uOeUqO+8mg0KYGBge3J9DQpnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoKSAgICAgICAjIOaVo+W4g+Wclg0KICBnZW9tX2xpbmUoKSAgICAgICANCiAgZ2VvbV9iYXIoKSAgICAgICAgICMg6ZW35qKd5ZyWDQogIGdlb21faGlzdG9ncmFtKCkgICAjIOebtOaWueWclg0KICBnZW9tX2RlbnNpdHkoKSAgICAgIyDmqZ/njoflr4bluqblnJYNCiAgZ2VvbV9ib3hwbG90KCkgICAgDQpgYGANCg0K6KOc5YWF77yaDQpgYGB7cn0NCmdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgICAjIOaxuuWumuebtOaWueWcluavj+agueWvrOW6pg0Kc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSAgICMg5pS56K6K5bC65bqmDQpgYGANCg0KDQojIyMj44CQ5Yqg5qiZ57Gk44CRDQoNCmBgYHtyfQ0KZ2VvbV9sYWJlbChhZXMobGFiZWwgPSBhYmIpKSAgICMg5oqK6bue6K6K5oiQ5qiZ57Gk54uADQpnZW9tX3RleHQoYWVzKGxhYmVsID0gYWJiKSkgICAgIyDlnKjpu57kuIrmlrnliqDlkI3nqLENCmBgYA0KDQpgYGB7cn0NCiMg6Iul5bey5ZyoZ2dwbG906KOh5Yqg5LqGbGFiZWznmoTlj4Pmlbgs5oOz6KaB5aKe5Yqg6aGP6Imy5Y+q6ZyA5ZyobGFiZWzoo6HmjIflrpoNCm11cmRlcnMgJT4lIGdncGxvdChhZXMocG9wdWxhdGlvbiwgdG90YWwsIGxhYmVsPWFiYikpICsNCiAgZ2VvbV9sYWJlbChjb2xvcj0iYmx1ZSIpDQpgYGANCg0KYGBge3J9DQojIOiLpeaDs+aUueeUqHJlZ2lvbuS9nOeCuumhj+iJsumhnuWIpSzliYfopoHnlKhhZXPkvobljIXotbfkvoYs5LiN54S25pyD6IiHYWJi6KGd56qBDQptdXJkZXJzICU+JSBnZ3Bsb3QoYWVzKHBvcHVsYXRpb24sIHRvdGFsLCBsYWJlbD1hYmIpKSArDQogIGdlb21fbGFiZWwoYWVzKGNvbG9yPXJlZ2lvbikpDQpgYGANCg0KIyMjIyDjgJDmlaPluIPlnJYgU2NhdHRlciBEaWFncmFt44CRDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX0FmcmljYV8yMDEwIDwtIGdhcG1pbmRlciAlPiUNCiAgICBmaWx0ZXIoY29udGluZW50ID09ICJBZnJpY2EiICYgeWVhciA9PSAyMDEwICYgIWlzLm5hKGdkcCkpICU+JQ0KICAgIG11dGF0ZShkb2xsYXJzX3Blcl9kYXkgPSBnZHAvcG9wdWxhdGlvbi8zNjUpDQpnYXBtaW5kZXJfQWZyaWNhXzIwMTAgJT4lDQogICAgZ2dwbG90KGFlcyhkb2xsYXJzX3Blcl9kYXksIGluZmFudF9tb3J0YWxpdHksIGNvbG9yID0gcmVnaW9uKSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGNvdW50cnkpKSArDQogICAgZmFjZXRfZ3JpZCgufnllYXIpDQpgYGANCg0KDQojIyMj44CQ5qmf546H5a+G5bqm5ZyWIERlbnNpdHnjgJENCmBgYHtyfQ0KaGVpZ2h0cyAlPiUgDQogIGdncGxvdChhZXMoaGVpZ2h0KSkgKw0KICBnZW9tX2RlbnNpdHkoYWVzKGdyb3VwPXNleCkpDQpgYGANCg0KYGBge3J9DQpoZWlnaHRzICU+JSANCiAgZ2dwbG90KGFlcyhoZWlnaHQsIGNvbG9yID0gc2V4KSkgKyAgICAjIOS7pWNvbG9y55u05o6l5Y+W5LujZ3JvdXANCiAgZ2VvbV9kZW5zaXR5KCkgICAgICAgICAgICAgICAgICAgICAgICAjIGNvbG9y5piv57eaKOWkluahhinnmoTpoY/oibINCmBgYA0KDQpgYGB7cn0NCmhlaWdodHMgJT4lIA0KICBnZ3Bsb3QoYWVzKGhlaWdodCwgZmlsbCA9IHNleCkpICsgICAgICMgZmlsbOaYr+Whq+a7v+mhj+iJsg0KICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjIpICAgICAgICAgICAgICMgYWxwaGHnlKjkvoboqr/mlbTpgI/mmI7luqYNCmBgYA0KDQpgYGB7cn0NCmRheWRvbGxhcnMgPC0gZ2FwbWluZGVyICU+JQ0KICAgIGZpbHRlcihjb250aW5lbnQgPT0gIkFmcmljYSIgJiB5ZWFyICVpbiUgYygxOTcwLDIwMTApICYgIWlzLm5hKGdkcCkpICU+JQ0KICAgIG11dGF0ZShkb2xsYXJzX3Blcl9kYXkgPSBnZHAvcG9wdWxhdGlvbi8zNjUpDQpkYXlkb2xsYXJzICU+JQ0KICAgIGdncGxvdChhZXMoZG9sbGFyc19wZXJfZGF5LCBmaWxsID0gcmVnaW9uKSkgKyAgICAjIGZpbGznnIvmiYDmnIlyZWdpb27nmoTlr4bluqbliIbluIMNCiAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjIsIGJ3ID0gMC41LCBwb3NpdGlvbiA9ICJzdGFjayIpICsgICAgIyBid+aYr+W5s+a7keeahOeoi+W6pijluLjmhYsv5b+D6Zu75ZyW6YKj5qij55qE6ZmhKSAjIOeUqHN0YWNr55aK5ZCEcmVnaW9uDQogICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogICAgZmFjZXRfZ3JpZCgufnllYXIpICAgICMg5bem5Y+z5Lim5o6SDQpgYGANCg0KYGBge3J9DQpnYXBtaW5kZXIgICU+JQ0KICAgIGZpbHRlcihjb250aW5lbnQgPT0gIkFmcmljYSIgJiB5ZWFyICVpbiUgYygxOTcwLDIwMTApICYgIWlzLm5hKGdkcCkgJiAhaXMubmEoeWVhcikgJiAhaXMubmEoaW5mYW50X21vcnRhbGl0eSkpICU+JQ0KICAgIG11dGF0ZShkb2xsYXJzX3Blcl9kYXkgPSBnZHAvcG9wdWxhdGlvbi8zNjUpICU+JQ0KICAgIGdncGxvdChhZXMoZG9sbGFyc19wZXJfZGF5LCBpbmZhbnRfbW9ydGFsaXR5LCBjb2xvciA9IHJlZ2lvbikpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb3VudHJ5KSkgKw0KICAgIGZhY2V0X2dyaWQoeWVhcn4uKSAgICAjIOS4iuS4i+S4puaOkg0KYGBgDQoNCiMjIyMg44CQ5YiG5L2N5ZyWIHFxLXBsb3TjgJENCg0KYGBge3J9DQpwIDwtIGhlaWdodHMgJT4lIGZpbHRlcihzZXg9PSJNYWxlIikgJT4lDQogIGdncGxvdChhZXMoc2FtcGxlID0gaGVpZ2h0KSkgDQpwYXJhbXMgPC0gaGVpZ2h0cyAlPiUgZmlsdGVyKHNleD09Ik1hbGUiKSAlPiUNCiAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKGhlaWdodCksIHNkID0gc2QoaGVpZ2h0KSkNCnAgICsgIGdlb21fcXEoZHBhcmFtcyA9IHBhcmFtcykNCmBgYA0KDQpgYGB7cn0NCmhlaWdodHMgJT4lIA0KICBmaWx0ZXIoc2V4PT0iTWFsZSIpICU+JQ0KICBnZ3Bsb3QoYWVzKHNhbXBsZSA9IHNjYWxlKGhlaWdodCkpKSArIA0KICBnZW9tX3FxKCkgKyANCiAgZ2VvbV9hYmxpbmUoKQ0KYGBgDQoNCg0KDQojIyMjIOOAkOeuseW9ouWcliBCb3hwbG9044CRDQoNCmBgYHtyfQ0KbXVyZGVycyAlPiUgbXV0YXRlKHJhdGUgPSB0b3RhbC9wb3B1bGF0aW9uKjEwMDAwMCkgJT4lIA0KICAgIG11dGF0ZShyZWdpb24gPSByZW9yZGVyKHJlZ2lvbiwgcmF0ZSwgRlVOID0gbWVkaWFuKSkgJT4lDQogICAgZ2dwbG90KGFlcyhyZWdpb24scmF0ZSkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgZ2VvbV9wb2ludCgpICsgICAgIyDkuK3nt5rkuIrnmoTop4Dlr5/pu54NCiAgICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMSwgYWxwaGEgPSAwLjIpICAgICMg5bGV54++5omA5pyJ6KeA5a+f6bueKOWIhuW4g+aoo+aFiykNCmBgYA0KDQoNCiMjIyMg44CQQ29tcGFyaW5nIERpc3RyaWJ1dGlvbuOAkQ0KDQpgYGB7cn0NCmZhY2V0X2dyaWQoeWVhciB+IGdyb3VwKSAjIOWFqeW8teWcluaOkuWcqOS4gOi1tyAjIOWJjeiAhTrkuIrkuIs75b6M6ICFOuW3puWPsw0KZ2VvbV9ib3hwbG90KGFlcyhyZWdpb24sZG9sbGFyc19wZXJfZGF5LCBmaWxsID0gZmFjb3IoeWVhcikpKQ0KYGBgDQoNCuevhOS+i++8mg0KYGBge3J9DQpkYXlkb2xsYXJzIDwtIGdhcG1pbmRlciAlPiUNCiAgICBmaWx0ZXIoY29udGluZW50ID09ICJBZnJpY2EiICYgeWVhciAlaW4lIGMoMTk3MCwyMDEwKSAmICFpcy5uYShnZHApKSAlPiUNCiAgICBtdXRhdGUoZG9sbGFyc19wZXJfZGF5ID0gZ2RwL3BvcHVsYXRpb24vMzY1KQ0KZGF5ZG9sbGFycyAlPiUNCiAgICBnZ3Bsb3QoYWVzKGRvbGxhcnNfcGVyX2RheSkpICsNCiAgICBnZW9tX2RlbnNpdHkoKSArDQogICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogICAgZmFjZXRfZ3JpZCgufnllYXIpIA0KYGBgDQoNCg0KIyMjI+OAkOiqv+aVtOaKgOW3p+OAkQ0KDQrph43mlrDmjpLluo8oZGVmYXVsdCBpcyBhbHBoYWJldGljYWwpDQpgYGB7cn0NCnJlZ2lvbiA9IGZhY3RvcihjKCJBc2lhIiwiQXNpYSIsIldlc3QiLCJXZXN0IiwiV2VzdCIpKQ0KbGV2ZWxzKHJlZ2lvbikNCnZhbHVlID0gYygxMCwgMTEsIDEyLCA2LCA0KQ0KcmVnaW9uID0gcmVvcmRlcihyZWdpb24sIHZhbHVlLCBGVU4gPSBtZWFuKQ0KbGV2ZWxzKHJlZ2lvbikNCmBgYA0KDQpgYGB7cn0NCm11dGF0ZShyZWdpb24gPSByZW9yZGVyKHJlZ2lvbiwgdmFsdWUsIEZVTiA9IG1lZGlhbikpDQptdXRhdGUoZ3JvdXAgPSBmYWN0b3IoZ3JvdXAsIGxldmVscyA9IGMoIk90aGVycyIsICJMYXRpbiBBbWVyaWNhIiwgIkVhc3QgQXNpYSIpKSkNCmBgYA0KDQoNCiMjIyPjgJDnvo7op4DmioDlt6fjgJENCmBgYHtyfQ0KdGhlbWVzX2Vjb25vbWlzdCgpICAjIOiDjOaZr+S4u+mhjA0KdGhlbWVzX2ZpdmV0aGlydHllaWdodCgpICAjIOiDjOaZr+S4u+mhjA0KZ2VvbV90ZXh0X3JlcGVsKCkgICMg6K6T5ZyW6KGo5LiK55qE5ZCE6bue5qiZ57Gk5LiN5LqS55u46YeN55aKDQp0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDMwLCBoanVzdCA9IDEpKSAjIOaXi+i9iXjou7jmqJnnsaTkvb/kuYvkuI3ph43nlorkuJTlpb3plrHoroANCmBgYA0KDQrnr4TkvovvvJoNCmBgYHtyfQ0KIyBGaXJzdCBkZWZpbmUgdGhlIHNsb3BlIG9mIGxpbmUNCnIgPSBtdXJkZXJzICU+JSBzdW1tYXJpemUocmF0ZSA9IHN1bSh0b3RhbCkvc3VtKHBvcHVsYXRpb24pKjEwXjYpICU+JSAuJHJhdGUNCiMgTWFrZSB0aGUgcGxvdA0KbXVyZGVycyAlPiUgZ2dwbG90KGFlcyhwb3B1bGF0aW9uLzEwXjYsIHRvdGFsLCBsYWJlbD1hYmIpKSArDQogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IGxvZzEwKHIpLCBsdHkgPSAyLCBjb2xvciA9ICJkYXJrZ3JleSIpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sPXJlZ2lvbiksIHNpemUgPSAzKSArDQogIGdlb21fdGV4dF9yZXBlbCgpICsNCiAgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpICsNCiAgeGxhYigiUG9wdWxhdGlvbiBpbiBtaWxsaW9ucyAobG9nIHNjYWxlKSIpICsgeWxhYigiVG90YWwgbnVtYmVyIG9mIG11cmRlcnMgKGxvZyBzY2FsZSkiKSArDQogIGdndGl0bGUoIlVTIEd1biBNdXJkZXJzIGluIFVTIDIwMTAiKSArDQogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiUmVnaW9uIikgKw0KICB0aGVtZV9lY29ub21pc3QoKQ0KYGBgDQoNCjxicj48aHI+DQoNCiMjIyA8YSBpZD0ibjIiPjwvYT7jgJBEcGx5cuOAkQ0KDQrjgJDomZXnkIbos4fmlpnjgJENCmBgYHtyfQ0KbWVhbihuYV9leGFtcGxlLCBuYS5ybSA9IFRSVUUpICAgICAjIOenu+mZpE5BDQohaXMubmEoZ2RwKSAgICAgICAgICAgICAgICAgICAgICAgICMg56e76ZmkTkENClgkYSA9IE5VTEwgICAgICAgICAgICAgICAgICAgICAgICAgIyDnp7vpmaRjb2x1bW4NCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KE5IQU5FUykNCmRhdGEoTkhBTkVTKQ0KcmVmX2F2ZyA8LSBOSEFORVMgJT4lDQogIGZpbHRlcihBZ2VEZWNhZGUgPT0gIiAyMC0yOSIgJiBHZW5kZXIgPT0gImZlbWFsZSIpICU+JSAgIyDnr6npgbjopoHliIbmnpDnmoTop4Dlr5/lgLwNCiAgc3VtbWFyaXplKGF2ZXJhZ2UgPSBtZWFuKEJQU3lzQXZlLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgIHN0YW5kYXJkX2RldmlhdGlvbiA9IHNkKEJQU3lzQXZlLCBuYS5ybT1UUlVFKSkgICMg5ZyocmVmX2F2Z+W6leS4i+ijveS9nOWkmuWAi2NvbHVtbg0KcmVmX2F2Zw0KcmVmX2F2ZyAlPiUgLiRhdmVyYWdlICAjIOWboOeCuuWbnuWCs+WPquacieWWruWAvCwg5bCHdGFibGXovYnmj5vmiJBudW1lcmljDQpgYGANCg0KDQpgYGB7cn0NCk5IQU5FUyAlPiUNCiAgICAgIGdyb3VwX2J5KEFnZURlY2FkZSwgR2VuZGVyKSAlPiUNCiAgICAgIHN1bW1hcml6ZShhdmVyYWdlID0gbWVhbihCUFN5c0F2ZSwgbmEucm0gPSBUUlVFKSwgDQogICAgICBzdGFuZGFyZF9kZXZpYXRpb24gPSBzZChCUFN5c0F2ZSwgbmEucm0gPSBUUlVFKSkNCiMg5L2/55SoZ3JvdXBfYnnlj6/ku6XovLjlh7rmr4/lgIvliIbpoZ7nmoTlgLwNCmBgYA0KDQpgYGB7cn0NCk5IQU5FUyAlPiUgdG9wX24oMTAsIEJQU3lzMSkNCiMg5p+l55yL5YmN5Y2B5ZCNDQpgYGANCg0KDQpgYGB7cn0NCmRhdGEoTkhBTkVTKQ0KTkhBTkVTICU+JSANCiAgICAgIGZpbHRlcihBZ2VEZWNhZGUgPT0gIiA0MC00OSIsIEdlbmRlciA9PSAibWFsZSIpICU+JQ0KICAgICAgZ3JvdXBfYnkoUmFjZTEpICU+JQ0KICAgICAgc3VtbWFyaXplKGF2ZXJhZ2UgPSBtZWFuKEJQU3lzQXZlLCBuYS5ybSA9IFRSVUUpLCANCiAgICAgICAgICAgICAgICBzdGFuZGFyZF9kZXZpYXRpb24gPSBzZChCUFN5c0F2ZSwgbmEucm0gPSBUUlVFKSkgJT4lDQogICAgICBhcnJhbmdlKGRlc2MoYXZlcmFnZSkpDQojIGRlc2PmmK/pmY3lhqrmjpLliJcNCmBgYA0KDQpgYGB7cn0NCk5IQU5FUyAlPiUgZ3JvdXBfYnkoQWdlRGVjYWRlLCBHZW5kZXIpICU+JSBzdW1tYXJpc2Uobj1uKCkpDQojIG49bigpIG1lYW5zIHRoYXQgYSB2YXJpYWJsZSBuYW1lZCBuIHdpbGwgYmUgYXNzaWduZWQgdGhlIG51bWJlciBvZiByb3dzICh0aGluayBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zKSBpbiB0aGUgc3VtbWFyaXplZCBkYXRhLg0KIyBzdW1tYXJpemUgdGhhdCBieSB0aGUgdG90YWwgbnVtYmVyIG9mIHJvd3MgaW4gZWFjaCBvZiB0aGUgbmV3IGdyb3Vwcy4NCmBgYA0KDQoNCjxicj48aHI+DQoNCg0KIyMjIDxhIGlkPSJuMyI+PC9hPuOAkEdhcG1pbmRlcuOAkQ0KDQpgYGB7cn0NCmNvdW50cmllcyA9IGMoIlZpZXRuYW0iLCAiVW5pdGVkIFN0YXRlcyIpDQp5ZWFycyA9IHNlcSgxOTYwLCAyMDEwLCAxKQ0KdGFiID0gZ2FwbWluZGVyICU+JQ0KICAgICBmaWx0ZXIoY291bnRyeSAlaW4lIGNvdW50cmllcywgeWVhciAlaW4lIHllYXJzKQ0KcCA8LSB0YWIgJT4lIGdncGxvdChhZXMoeWVhciwgbGlmZV9leHBlY3RhbmN5LCBjb2xvciA9IGNvdW50cnkpKSArDQogICAgICAgICAgICAgZ2VvbV9saW5lKCkNCmBgYA0KDQpgYGB7cn0NCmRheWRvbGxhcnMgPC0gZ2FwbWluZGVyICU+JQ0KICAgIGZpbHRlcihjb250aW5lbnQgPT0gIkFmcmljYSIgJiB5ZWFyID09IDIwMTAgJiAhaXMubmEoZ2RwKSkgJT4lDQogICAgbXV0YXRlKGRvbGxhcnNfcGVyX2RheSA9IGdkcC9wb3B1bGF0aW9uLzM2NSkNCmRheWRvbGxhcnMgJT4lDQogICAgZ2dwbG90KGFlcyhkb2xsYXJzX3Blcl9kYXkpKSArDQogICAgZ2VvbV9kZW5zaXR5KCkgKw0KICAgIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikNCmBgYA0KDQoNCg0KPGJyPjxocj4NCg0KDQojIyMgPGEgaWQ9Im40Ij48L2E+44CQVmlzdWFsaXphdGlvbiBQcmluY2lwbGVz44CRDQoNCiArIEthcmwgQnJvbWFuIGVudGl0bGVkICJDcmVhdGluZyBFZmZlY3RpdmUgRmlndXJlcyBhbmQgVGFibGVzIg0KICsg5ZyW6KGo6Iul5rKS5pyJ5b6eMOmWi+Wni++8jOW+iOWuueaYk+aUvuWkpyjoqofpo74p5bCN6LGh5LmL6ZaT55qE5beu6LedDQogICAgKyDmnKzkvoblj6/og73lj6rlt64zMDAw5aSaKDE5Mzk5LDE1NzgwKe+8jOW+nuWcluW9ouS4iueci+i1t+S+huWNu+W3ruS6huS6lOWAjQ0KICAgICsg5L2G6Iul57iu5bCP6Zuj5Lul5q+U6LyD5bCN6LGh77yM6YCZ5pmC5Y+v5Lul5Y+W6YGp55W255qE6L2J5o+b5YC8KGxvZynkv53mjIHmr5TkvosNCiArIOmVt+aineWclumBqeWQiOmrlOePvuaVuOWAvOeahOW3rui3nQ0KICAgICArIFBpZSBDaGFydHMgYXJlIG5ldmVyIHByZWZlcmVkLg0KICAgICArIOS6uumhnumbo+S7peW/q+mAn+i+qOitmOinkuW6pui3n+mdouepjeeahOavlOS+i++8jOWboOatpOiDveeUqOmVt+aineWcluWwseS4jeimgeeUqOWck+mkheWclg0KICAgICArIOS7pemZjeWGquaIluWNh+WGquaOkuWIlyhS6aCQ6Kit5piv5oyJ54Wn5a2X5q+N6aCG5bqP77yM5oeJ5L2/55SocmVvcmRlcumHjeaWsOaOkuWIlykNCiArIGJveOWclumBqeWQiOWxleePvuWIhuS9iCjlubPlnYflgLzjgIHmqJnmupblt67jgIHnlbDluLjlgLwpDQogKyDkuI3opoHnlKgzROaIluWkmue2reW6pueahOWcluW9ou+8jOW+kuWinuWbsOaDkQ0KICsg6YG/5YWN6YGO5aSa5bCP5pW46bueKFLpoJDoqK035L2N77yM5LqL5a+m5LiK5Y+q6ZyA6KaBMX4y5L2N5Y2z5Y+vKQ0KICsg55SoVGFibGXmjpLliJfmlbjlrZfmmYLvvIzkvb/nlKhjb2x1bW7mr5TnlKhyb3fpgoTog73lpKDmuIXmpZrmr5TovIPlpKflsI8NCg0KIA0KIVtdKDEuanBnKSANCiANCiFbU2VxdWVudGlhbCBwYWxldHRlcyhoaWdoL2xvdyldKDIuanBnKQ0KDQoNCiFbRGl2ZXJnaW5nIHBhbGV0dGVzKGhpZ2hlcuKGkmNlbnRlcuKGkGxvd2VyKV0oMy5qcGcpDQoNCmBgYHtyfQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpkYXRhKHVzX2NvbnRhZ2lvdXNfZGlzZWFzZXMpDQp0aGVfZGlzZWFzZSA9ICJTbWFsbHBveCINCnVzX2NvbnRhZ2lvdXNfZGlzZWFzZXMgPSBzdWJzZXQodXNfY29udGFnaW91c19kaXNlYXNlcywgdXNfY29udGFnaW91c19kaXNlYXNlcyR3ZWVrc19yZXBvcnRpbmcgPj0gMTApDQojIHllYXJzID0gdXNfY29udGFnaW91c19kaXNlYXNlcyR5ZWFyW3doaWNoKHVzX2NvbnRhZ2lvdXNfZGlzZWFzZXMkd2Vla3NfcmVwb3J0aW5nID49IDEwKV0NCnllYXJzID0gdW5pcXVlKHVzX2NvbnRhZ2lvdXNfZGlzZWFzZXMkeWVhcikNCmRhdCA8LSB1c19jb250YWdpb3VzX2Rpc2Vhc2VzICU+JSANCiAgIGZpbHRlcighc3RhdGUlaW4lYygiSGF3YWlpIiwiQWxhc2thIikgJiBkaXNlYXNlID09IHRoZV9kaXNlYXNlICYgeWVhciVpbiV5ZWFycykgJT4lIA0KICAgbXV0YXRlKHJhdGUgPSBjb3VudCAvIHBvcHVsYXRpb24gKiAxMDAwMCkgJT4lIA0KICAgbXV0YXRlKHN0YXRlID0gcmVvcmRlcihzdGF0ZSwgcmF0ZSkpDQoNCmRhdCAlPiUgZ2dwbG90KGFlcyh5ZWFyLCBzdGF0ZSwgZmlsbCA9IHJhdGUpKSArIA0KICBnZW9tX3RpbGUoY29sb3IgPSAiZ3JleTUwIikgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZD1jKDAsMCkpICsgDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG9ycyA9IGJyZXdlci5wYWwoOSwgIlJlZHMiKSwgdHJhbnMgPSAic3FydCIpICsgDQogIHRoZW1lX21pbmltYWwoKSArIA0KICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpKSArIA0KICBnZ3RpdGxlKHRoZV9kaXNlYXNlKSArIA0KICB5bGFiKCIiKSArIA0KICB4bGFiKCIiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmRhdGEodXNfY29udGFnaW91c19kaXNlYXNlcykNCnVzX2NvbnRhZ2lvdXNfZGlzZWFzZXMgPSBzdWJzZXQodXNfY29udGFnaW91c19kaXNlYXNlcywgdXNfY29udGFnaW91c19kaXNlYXNlcyR3ZWVrc19yZXBvcnRpbmcgPj0gMTApDQp5ZWFycyA9IHVuaXF1ZSh1c19jb250YWdpb3VzX2Rpc2Vhc2VzJHllYXIpDQoNCnRoZV9kaXNlYXNlID0gIlNtYWxscG94Ig0KZGF0IDwtIHVzX2NvbnRhZ2lvdXNfZGlzZWFzZXMgJT4lDQogICBmaWx0ZXIoIXN0YXRlJWluJWMoIkhhd2FpaSIsIkFsYXNrYSIpICYgZGlzZWFzZSA9PSB0aGVfZGlzZWFzZSAmIHllYXIlaW4leWVhcnMpICU+JQ0KICAgbXV0YXRlKHJhdGUgPSBjb3VudCAvIHBvcHVsYXRpb24gKiAxMDAwMCkgJT4lDQogICBtdXRhdGUoc3RhdGUgPSByZW9yZGVyKHN0YXRlLCByYXRlKSkNCg0KYXZnIDwtIHVzX2NvbnRhZ2lvdXNfZGlzZWFzZXMgJT4lDQogIGZpbHRlcihkaXNlYXNlPT10aGVfZGlzZWFzZSkgJT4lIGdyb3VwX2J5KHllYXIpICU+JQ0KICBzdW1tYXJpemUodXNfcmF0ZSA9IHN1bShjb3VudCwgbmEucm09VFJVRSkvc3VtKHBvcHVsYXRpb24sIG5hLnJtPVRSVUUpKjEwMDAwKQ0KDQpkYXQgJT4lIGdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh5ZWFyLCByYXRlLCBncm91cCA9IHN0YXRlKSwgIGNvbG9yID0gImdyZXk1MCIsIA0KICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSwgYWxwaGEgPSAwLjIsIHNpemUgPSAxKSArDQogIGdlb21fbGluZShtYXBwaW5nID0gYWVzKHllYXIsIHVzX3JhdGUpLCAgZGF0YSA9IGF2Zywgc2l6ZSA9IDEsIGNvbG9yID0gImJsYWNrIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAic3FydCIsIGJyZWFrcyA9IGMoNSwyNSwxMjUsMzAwKSkgKyANCiAgZ2d0aXRsZSgiQ2FzZXMgcGVyIDEwLDAwMCBieSBzdGF0ZSIpICsgDQogIHhsYWIoIiIpICsgDQogIHlsYWIoIiIpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBkYXRhLmZyYW1lKHg9MTk1NSwgeT01MCksIG1hcHBpbmcgPSBhZXMoeCwgeSwgbGFiZWw9IlVTIGF2ZXJhZ2UiKSwgY29sb3I9ImJsYWNrIikgKyANCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTE5NjMsIGNvbCA9ICJibHVlIikNCmBgYA0KDQpgYGB7cn0NCmRhdGEodXNfY29udGFnaW91c19kaXNlYXNlcykNCnVzX2NvbnRhZ2lvdXNfZGlzZWFzZXMgPSBzdWJzZXQodXNfY29udGFnaW91c19kaXNlYXNlcywgdXNfY29udGFnaW91c19kaXNlYXNlcyR3ZWVrc19yZXBvcnRpbmcgPj0gMTApDQp5ZWFycyA9IHVuaXF1ZSh1c19jb250YWdpb3VzX2Rpc2Vhc2VzJHllYXIpDQp1c19jb250YWdpb3VzX2Rpc2Vhc2VzICU+JSBmaWx0ZXIoc3RhdGU9PSJDYWxpZm9ybmlhIiAmIHllYXIlaW4leWVhcnMpICU+JSANCiAgZ3JvdXBfYnkoeWVhciwgZGlzZWFzZSkgJT4lDQogIHN1bW1hcml6ZShyYXRlID0gc3VtKGNvdW50KS9zdW0ocG9wdWxhdGlvbikqMTAwMDApICU+JQ0KICBnZ3Bsb3QoYWVzKHllYXIsIHJhdGUsIGNvbG9yID0gZGlzZWFzZSkpICsgDQogIGdlb21fbGluZSgpDQpgYGANCg0KYGBge3J9DQp1c19jb250YWdpb3VzX2Rpc2Vhc2VzICU+JQ0KICAgZmlsdGVyKCFpcy5uYShwb3B1bGF0aW9uKSkgJT4lDQogICBtdXRhdGUocmF0ZSA9IGNvdW50IC8gcG9wdWxhdGlvbiAqIDEwMDAwKSAlPiUNCiAgIG11dGF0ZShzdGF0ZSA9IHJlb3JkZXIoc3RhdGUsIHJhdGUpKSAlPiUgDQogICBncm91cF9ieSh5ZWFyLCBkaXNlYXNlKSAlPiUNCiAgIHN1bW1hcml6ZShyYXRlID0gc3VtKGNvdW50LCBuYS5ybT1UUlVFKS9zdW0ocG9wdWxhdGlvbiwgbmEucm09VFJVRSkqMTAwMDApICU+JQ0KICAgZ2dwbG90KGFlcyh5ZWFyLCByYXRlLCBjb2xvciA9IGRpc2Vhc2UpKSArIA0KICAgZ2VvbV9saW5lKCkNCmBgYA0KDQoNCjxicj48YnI+PGhyPjxicj48YnI+PGJyPg0KICANCg0KPHN0eWxlPg0KcCxsaSB7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQp0aXRsZXsNCiAgICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KYm9keXsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCmgxLGgyLGgzLGg0LGg1ew0KICAgIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KPC9zdHlsZT48YnI+PGJyPjxicj48YnI+PGJyPg0KDQo8c3R5bGU+DQouY2FwdGlvbiB7DQogIGNvbG9yOiAjNzc3Ow0KICBtYXJnaW4tdG9wOiAxMHB4Ow0KfQ0KcCBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwcmUgew0KICB3b3JkLWJyZWFrOiBub3JtYWw7DQogIHdvcmQtd3JhcDogbm9ybWFsOw0KICBsaW5lLWhlaWdodDogMTsNCn0NCnByZSBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwLGxpIHsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCi5yew0KICBsaW5lLWhlaWdodDogMS4yOw0KfQ0KDQp0aXRsZXsNCiAgY29sb3I6ICNjYzAwMDA7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpib2R5ew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDEsaDIsaDMsaDQsaDV7DQogIGNvbG9yOiAjMDA4ODAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDN7DQogIGNvbG9yOiAjYjM2YjAwOw0KICBiYWNrZ3JvdW5kOiAjZmZlMGIzOw0KICBsaW5lLWhlaWdodDogMjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmg1ew0KICBjb2xvcjogIzAwNjAwMDsNCiAgYmFja2dyb3VuZDogI2ZmZmZlMDsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQplbXsNCiAgY29sb3I6ICMwMDAwYzA7DQogIGJhY2tncm91bmQ6ICNmMGYwZjA7DQogIH0NCjwvc3R5bGU+DQoNCg==