版权声明:本套课程材料开源,使用和分享必须遵守「创作共用许可协议 CC BY-NC-SA」(来源引用-非商业用途使用-以相同方式共享)。


数据可视化的总体思路

【探索发现】数据有了,图怎么画?

  • R Graph Gallery R绘图美术馆
    • 绘制变量分布
      • 直方图(Histogram)
      • 密度图(Density)
      • 箱线图(Boxplot)
      • 提琴图(Violin)
      • 山脊图(Ridgeline)
    • 绘制变量大小
      • 柱形图(Barplot)
      • 点线图(Dotted line)
    • 绘制变量关系
      • 散点图(Scatterplot)
      • 气泡图(Bubble)
      • 热力图(Heatmap)
    • 绘制变量趋势
      • 折线图(Line chart)
      • 面积图(Area chart)

【知识点】ggplot2“搭积木”式绘图语法

ggplot2绘图语法的逻辑:

  1. 准备数据(data),将变量映射到美学属性(aes),定义XY轴、颜色、大小等
  2. 添加几何对象(geom),绘制点、线、面等,多个图层可通过加号依次叠加(+
  3. 可做分面小图(facet),调整标度(scale),投影到不同坐标系(coord
  4. 设置标题标签(labs),调整图例(guides),根据喜好选择主题方案(theme

绘制变量分布

直方图

【实践1】直方图

## 数据准备
data = airquality
data$Month = as.factor(data$Month)
data$Temp.C = (data$Temp - 32) / 1.8  # 摄氏度 = (华氏度 - 32) / 1.8
str(data)
'data.frame':   153 obs. of  7 variables:
 $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
 $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
 $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
 $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
 $ Month  : Factor w/ 5 levels "5","6","7","8",..: 1 1 1 1 1 1 1 1 1 1 ...
 $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...
 $ Temp.C : num  19.4 22.2 23.3 16.7 13.3 ...
## R基础作图函数:直方图
hist(data$Temp.C)

## ggplot2搭积木作图
ggplot()  # 空画板

ggplot(data, aes(x=Temp.C))  # 有了坐标系

ggplot(data, aes(x=Temp.C)) +
  geom_histogram()  # 直方图
`stat_bin()` using `bins = 30`. Pick better value `binwidth`.

ggplot(data, aes(x=Temp.C)) +
  geom_histogram(bins=10,
                 color="black",
                 fill="grey")

ggplot(data, aes(x=Temp.C)) +
  geom_histogram(binwidth=5,
                 color="black",
                 fill="grey")

ggplot(data, aes(x=Temp.C)) +
  geom_histogram(
    aes(y=after_stat(density)),
    ## density, integrate to 1
    binwidth=5,
    color="black",
    fill="grey")

ggplot(data, aes(x=Temp.C)) +
  geom_histogram(
    aes(y=after_stat(ndensity)),
    ## density, maximum to 1
    binwidth=5,
    color="black",
    fill="grey")

密度图

【实践2】密度图

ggplot(data, aes(x=Temp.C)) +
  geom_density()

ggplot(data, aes(x=Temp.C)) +
  geom_density(adjust=0.5)

ggplot(data, aes(x=Temp.C)) +
  geom_density(adjust=2)

ggplot(data, aes(x=Temp.C)) +
  geom_density(adjust=2,
               linewidth=2,
               color="darkblue",
               fill="lightblue")

ggplot(data, aes(x=Temp.C, y=after_stat(ndensity))) +
  geom_histogram(binwidth=5) +
  geom_density(adjust=2,
               linewidth=2,
               color="darkblue",
               fill="lightblue",
               alpha=0.5)

【探索发现】颜色的RGB十六进制码

箱线图

【知识点】箱线图的统计含义

  • 四分位数(quartile):把数据四等分的界限
    • Q1:0~25%(前25%界限)
    • Q2:25~50%(中位数50%界限)
    • Q3:50~75%(后25%界限)
    • Q4:75~100%(垫底)
  • IQR(interquartile range):四分位距(分布中Q1和Q3之间的变量值范围)
    • IQR = Q3 – Q1

【实践3】箱线图

## R基础作图函数:箱线图
boxplot(data$Temp.C)

boxplot(Temp.C ~ Month, data=data)

## ggplot2搭积木作图
ggplot(data, aes(y=Temp.C)) +
  geom_boxplot()

ggplot(data, aes(x=Month, y=Temp.C)) +
  geom_boxplot()

ggplot(data, aes(x=Month, y=Temp.C)) +
  geom_boxplot(
    fill="blue",
    alpha=0.3,
    outlier.color="red",
    outlier.size=2)

ggplot(data, aes(x=Month, y=Temp.C)) +
  geom_boxplot(aes(fill=Month)) +
  scale_y_continuous(
    limits=c(10, 40)) +
  labs(x="Month",
       y="Temperature",
       fill="Month")

提琴图

【实践4】提琴图

ggplot(data, aes(x=Month, y=Temp.C)) +
  geom_violin(aes(fill=Month)) +
  scale_y_continuous(limits=c(10, 40)) +
  labs(x="Month",
       y="Temperature",
       fill="Month")

ggplot(data, aes(x=Month, y=Temp.C)) +
  geom_violin(aes(fill=Month)) +
  geom_boxplot(fill="white", width=0.3) +
  scale_y_continuous(limits=c(10, 40)) +
  labs(x="Month",
       y="Temperature",
       fill="Month")

山脊图

【探索发现】山脊图

## install.packages("ggridges")
library(ggridges)

ggplot(data, aes(x=Temp.C, y=Month)) +
  geom_density_ridges() +
  theme_ridges()
Picking joint bandwidth of 1.47

ggplot(data, aes(x=Temp.C, y=Month, fill=after_stat(x))) +
  geom_density_ridges_gradient() +
  labs(x="Temperature", y="Month") +
  theme_ridges()
Picking joint bandwidth of 1.47

ggplot(data, aes(x=Temp.C, y=Month, fill=after_stat(x))) +
  geom_density_ridges_gradient(
    scale=0.95,        # 最高峰高度缩放到95%
    show.legend=FALSE  # 不显示图例
  ) +
  scale_fill_viridis_c(option="C") +  # Viridis配色方案
  labs(x="Temperature", y="Month") +
  theme_ridges()
Picking joint bandwidth of 1.47

ggplot(data, aes(x=Temp.C, y=Month, fill=after_stat(x))) +
  geom_density_ridges_gradient(
    stat="binline",    # 直方图统计转换
    bins=20,           # 20个直方图分段
    scale=0.8,         # 最高峰高度缩放到95%
    show.legend=FALSE  # 不显示图例
  ) +
  scale_x_continuous(limits=c(10, 40)) +
  scale_fill_viridis_c(option="C") +
  labs(x="Temperature", y="Month") +
  theme_ridges()

绘制变量大小

柱形图

【实践5】柱形图

## 数据准备:每月气温的估计边际均值(emmeans)
model = lm(Temp.C ~ Month, data)
# summary(emmeans(model, "Month"))
means = model %>% emmeans("Month") %>% summary()
means
 Month emmean    SE  df lower.CL upper.CL
 5       18.6 0.664 148     17.3     19.9
 6       26.2 0.675 148     24.8     27.5
 7       28.8 0.664 148     27.5     30.1
 8       28.9 0.664 148     27.6     30.2
 9       24.9 0.675 148     23.6     26.3

Confidence level used: 0.95 
ggplot(means, aes(x=Month, y=emmean)) +
  geom_col(color="black",
           fill="grey",
           width=0.6)

ggplot(means, aes(x=Month, y=emmean)) +
  geom_col(color="black",
           fill="grey",
           width=0.6) +
  geom_errorbar(aes(ymin=lower.CL,
                    ymax=upper.CL),
                width=0.1) +
  labs(y="Mean Temperature",
       title="Air Quality Data") +
  theme_classic()

## 数据准备:两因素组间ANOVA的估计边际均值(emmeans)
d = between.2
d$A = as.factor(d$A)
d$B = as.factor(d$B)
model = lm(SCORE ~ A * B, data=d)
means = model %>% emmeans("A", by="B") %>% summary()
means
B = 1:
 A emmean    SE df lower.CL upper.CL
 1   4.00 0.682 18     2.57     5.43
 2   3.75 0.682 18     2.32     5.18

B = 2:
 A emmean    SE df lower.CL upper.CL
 1   4.00 0.682 18     2.57     5.43
 2   8.00 0.682 18     6.57     9.43

B = 3:
 A emmean    SE df lower.CL upper.CL
 1   4.75 0.682 18     3.32     6.18
 2  12.00 0.682 18    10.57    13.43

Confidence level used: 0.95 
ggplot(means, aes(x=A, y=emmean, fill=B)) +
  geom_col(position="dodge",  # 调整水平位置,躲避重叠图形
           width=0.6)

ggplot(means, aes(x=A, y=emmean, fill=B)) +
  geom_col(position="dodge",  # 调整水平位置,躲避重叠图形
           width=0.6) +
  geom_errorbar(aes(ymin=lower.CL,
                    ymax=upper.CL),
                position=position_dodge(0.6),
                width=0.15,
                color="black")

ggplot(means, aes(x=A, y=emmean, fill=B)) +
  geom_col(position="dodge", width=0.6) +
  geom_errorbar(aes(ymin=lower.CL,
                    ymax=upper.CL),
                position=position_dodge(0.6),
                width=0.15,
                color="black") +
  scale_y_continuous(expand=expansion(add=0),
                     limits=c(0, 15),
                     breaks=seq(0, 15, 3)) +
  scale_fill_brewer(palette="Set1") +
  labs(x="A", y="SCORE") +
  theme_classic()

点线图

【实践6】点线图

## 数据准备:每月气温的估计边际均值(emmeans)
model = lm(Temp.C ~ Month, data)
# summary(emmeans(model, "Month"))
means = model %>% emmeans("Month") %>% summary()
means
 Month emmean    SE  df lower.CL upper.CL
 5       18.6 0.664 148     17.3     19.9
 6       26.2 0.675 148     24.8     27.5
 7       28.8 0.664 148     27.5     30.1
 8       28.9 0.664 148     27.6     30.2
 9       24.9 0.675 148     23.6     26.3

Confidence level used: 0.95 
emmip(model, ~ Month, CIs=TRUE)  # 返回ggplot对象

emmip(model, ~ Month, CIs=TRUE) +
  labs(x="Month",
       y="Mean Temperature",
       title="Air Quality Data",
       subtitle="Daily Temperature",
       caption="* Error bar = 95% CI") +
  theme_classic()


绘制变量关系

散点图

【实践7】散点图

## R基础作图函数:散点图
plot(x=data$Wind, y=data$Temp.C)

## ggplot2搭积木作图
ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_point()

ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_point(color="grey") +
  geom_smooth()  # 后画的geom图层在最上面!
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_smooth() +
  geom_point(color="grey")  # 后画的geom图层在最上面!
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_point(color="grey") +
  geom_smooth(method="lm", color="black")
`geom_smooth()` using formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_point(color="grey") +
  geom_smooth(method="lm", color="black") +
  geom_hline(yintercept=22, linetype="dashed", color="red")
`geom_smooth()` using formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_hline(yintercept=22, linetype="dashed", color="red") +
  geom_point(color="grey") +
  geom_smooth(method="lm", color="black")
`geom_smooth()` using formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_hline(yintercept=22, linetype="dashed", color="red") +
  geom_vline(xintercept=9, linetype="dashed", color="blue") +
  geom_point(color="grey") +
  geom_smooth(method="lm", color="black")
`geom_smooth()` using formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point()

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point() +
  scale_color_brewer(palette="Set1")

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point() +
  geom_smooth(method="lm", se=FALSE) +
  scale_color_brewer(palette="Set1") +
  labs(title="Temperature & Wind Speed")
`geom_smooth()` using formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point() +
  geom_smooth(method="lm", se=FALSE) +
  geom_smooth(method="lm", color="black") +
  scale_x_continuous(limits=c(0, 21)) +
  scale_y_continuous(limits=c(10, 40)) +
  scale_color_brewer(palette="Set1") +
  labs(title="Temperature & Wind Speed")
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'

气泡图

【实践8】气泡图

ggplot(data, aes(x=Wind, y=Temp.C, color=Month, size=Solar.R)) +
  geom_point(shape=21) +
  scale_color_brewer(palette="Set1")
Warning: Removed 7 rows containing missing values or values outside the scale range
(`geom_point()`).

ggplot(data, aes(x=Wind, y=Temp.C, color=Month, size=Solar.R)) +
  geom_point(shape=21) +
  scale_color_brewer(palette="Set1") +
  scale_size_continuous(breaks=seq(50, 300, 50))
Warning: Removed 7 rows containing missing values or values outside the scale range
(`geom_point()`).

ggplot(data, aes(x=Wind, y=Temp.C, color=Month, size=Solar.R)) +
  geom_point(aes(fill=Month), alpha=0.2, shape=21) +
  geom_point(shape=21) +
  scale_color_brewer(palette="Set1") +
  scale_fill_brewer(palette="Set1") +
  scale_size_continuous(breaks=seq(50, 300, 50))
Warning: Removed 7 rows containing missing values or values outside the scale range
(`geom_point()`).
Removed 7 rows containing missing values or values outside the scale range
(`geom_point()`).

热力图

【探索发现】热力图

cor = Corr(airquality)
Pearson's r and 95% confidence intervals:
─────────────────────────────────────────────────
                   r       [95% CI]     p       N
─────────────────────────────────────────────────
Ozone-Solar.R   0.35 [ 0.17,  0.50] <.001 *** 111
Ozone-Wind     -0.60 [-0.71, -0.47] <.001 *** 116
Ozone-Temp      0.70 [ 0.59,  0.78] <.001 *** 116
Ozone-Month     0.16 [-0.02,  0.34]  .078 .   116
Ozone-Day      -0.01 [-0.20,  0.17]  .888     116
Solar.R-Wind   -0.06 [-0.22,  0.11]  .496     146
Solar.R-Temp    0.28 [ 0.12,  0.42] <.001 *** 146
Solar.R-Month  -0.08 [-0.23,  0.09]  .366     146
Solar.R-Day    -0.15 [-0.31,  0.01]  .070 .   146
Wind-Temp      -0.46 [-0.57, -0.32] <.001 *** 153
Wind-Month     -0.18 [-0.33, -0.02]  .027 *   153
Wind-Day        0.03 [-0.13,  0.19]  .739     153
Temp-Month      0.42 [ 0.28,  0.54] <.001 *** 153
Temp-Day       -0.13 [-0.28,  0.03]  .108     153
Month-Day      -0.01 [-0.17,  0.15]  .922     153
─────────────────────────────────────────────────

cor$plot + labs(title="Correlation Plot")

cor$plot +
  labs(title="Correlation Plot") +
  scale_fill_fermenter(
    palette="RdBu",
    direction=1,
    limits=c(-1, 1),
    breaks=seq(-1, 1, 0.2),
    guide=guide_colorsteps(
      barwidth=0.5,
      barheight=10))
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.

cor$plot +
  labs(title="Correlation Plot") +
  scale_fill_fermenter(
    palette="Spectral",
    direction=1,
    limits=c(-1, 1),
    breaks=seq(-1, 1, 0.2),
    guide=guide_colorsteps(
      barwidth=0.5,
      barheight=10))
Scale for fill is already present.
Adding another scale for fill, which will replace the existing scale.

绘制变量趋势

折线图

【实践9】折线图

data = as.data.table(airquality)
data[, Date := as.Date(sprintf("1973-%02d-%02d", Month, Day))]
data[, Temp.C := (Temp - 32) / 1.8]
data
     Ozone Solar.R  Wind  Temp Month   Day       Date Temp.C
     <int>   <int> <num> <int> <int> <int>     <Date>  <num>
  1:    41     190   7.4    67     5     1 1973-05-01  19.44
  2:    36     118   8.0    72     5     2 1973-05-02  22.22
  3:    12     149  12.6    74     5     3 1973-05-03  23.33
  4:    18     313  11.5    62     5     4 1973-05-04  16.67
  5:    NA      NA  14.3    56     5     5 1973-05-05  13.33
 ---                                                        
149:    30     193   6.9    70     9    26 1973-09-26  21.11
150:    NA     145  13.2    77     9    27 1973-09-27  25.00
151:    14     191  14.3    75     9    28 1973-09-28  23.89
152:    18     131   8.0    76     9    29 1973-09-29  24.44
153:    20     223  11.5    68     9    30 1973-09-30  20.00
ggplot(data, aes(x=Date, y=Temp.C)) +
  geom_line()

ggplot(data, aes(x=Date, y=Temp.C)) +
  geom_line() +
  geom_smooth()
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

ggplot(data, aes(x=Date, y=Temp.C)) +
  geom_line() +
  geom_point() +
  geom_smooth()
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

ggplot(data, aes(x=Date, y=Temp.C)) +
  geom_line(color="grey") +
  geom_point(aes(color=Temp.C)) +
  geom_smooth(color="black") +
  scale_color_distiller(palette="RdYlBu")
`geom_smooth()` using method = 'loess' and formula = 'y ~ x'

ggplot(data, aes(x=Date, y=Temp.C)) +
  geom_line(linewidth=1) +
  geom_point(aes(fill=Temp.C), shape=21) +
  scale_x_date(date_labels="%m-%d",  # "%Y-%m-%d"
               date_breaks="1 month",
               date_minor_breaks="7 days") +
  scale_y_continuous(limits=c(10, 40)) +
  scale_fill_distiller(palette="RdYlBu") +
  labs(x=NULL,
       y="Temperature",
       title="Daily Temperature")

【作业9】基础绘图练习

作业要求:

  • 基于期末自选数据,运用本章所学的ggplot2绘图代码,练习绘制变量的分布(直方图)、大小(柱形图)、关系(散点图)、 趋势(折线图),每种图绘制一个即可
  • 使用R Markdown完成,对关键代码及结果要有注释说明

平台提交:

  • 运行得到HTML网页文件的关键部分截图(只需上传截图)

返回课程主页

© 包寒吴霜

LS0tDQp0aXRsZTogIuOAilLor63oqIDjgIvnrKwxMOeroO+8muWfuuehgOe7mOWbviINCnN1YnRpdGxlOiA8YSBocmVmPSJodHRwczovL3BzeWNoYnJ1Y2UuZ2l0aHViLmlvL1JDb3Vyc2UvIj7ov5Tlm57or77nqIvkuLvpobU8L2E+DQphdXRob3I6ICLmjojor77mlZnluIjvvJrljIXlr5LlkLTpnJwiDQojIGRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBhbmNob3Jfc2VjdGlvbnM6IHRydWUNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgY3NzOiBSbWRDU1MuY3NzDQotLS0NCg0KYGBgez1odG1sfQ0KPHAgc3R5bGU9ImZvbnQtc2l6ZTogMTJweCI+54mI5p2D5aOw5piO77ya5pys5aWX6K++56iL5p2Q5paZ5byA5rqQ77yM5L2/55So5ZKM5YiG5Lqr5b+F6aG76YG15a6I44CM5Yib5L2c5YWx55So6K645Y+v5Y2P6K6uIENDIEJZLU5DLVNB44CN77yI5p2l5rqQ5byV55SoLemdnuWVhuS4mueUqOmAlOS9v+eUqC3ku6Xnm7jlkIzmlrnlvI/lhbHkuqvvvInjgII8aW1nIHNyYz0iaW1nL0NDLUJZLU5DLVNBLmpwZyIgd2lkdGg9IjEyMHB4IiBoZWlnaHQ9IjQycHgiIHN0eWxlPSJmbG9hdDogcmlnaHQiIC8+PC9wPg0KYGBgDQoNCmBgYHtyIENvbmZpZywgaW5jbHVkZT1GQUxTRX0NCm9wdGlvbnMoDQogIGtuaXRyLmthYmxlLk5BID0gIiIsDQogIGRpZ2l0cyA9IDQNCikNCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgY29tbWVudCA9ICIiLA0KICBmaWcud2lkdGggPSA2LA0KICBmaWcuaGVpZ2h0ID0gNCwNCiAgZHBpID0gMzAwDQopDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgQ2hhcDEw77ya5Z+656GA57uY5Zu+DQoNCiMjIyMg5b6A5pyf6KaB54K55Zue6aG+DQoNCi0gW0NoYXAwOSBcIyDlm57lvZLmqKHlnovlu7rnq4vkuI7nu5PmnpzmiqXlkYpdKGh0dHBzOi8vcHN5Y2hicnVjZS5naXRodWIuaW8vUkNvdXJzZS9DaGFwMDkjJUU1JTlCJTlFJUU1JUJEJTkyJUU2JUE4JUExJUU1JTlFJThCJUU1JUJCJUJBJUU3JUFCJThCJUU0JUI4JThFJUU3JUJCJTkzJUU2JTlFJTlDJUU2JThBJUE1JUU1JTkxJThBKXsudXJpfQ0KLSBbQ2hhcDA5IFwjIOWkmuS4quaooeWei+axh+aAu+S4juihqOagvOi+k+WHul0oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAwOSMlRTUlQTQlOUElRTQlQjglQUElRTYlQTglQTElRTUlOUUlOEIlRTYlQjElODclRTYlODAlQkIlRTQlQjglOEUlRTglQTElQTglRTYlQTAlQkMlRTglQkUlOTMlRTUlODclQkEpey51cml9DQoNCiMjIyMg5pys56ug6KaB54K555uu5b2VDQoNCi0gW+OAkOaOoue0ouWPkeeOsOOAkeaVsOaNruacieS6hu+8jOWbvuaAjuS5iOeUu++8n10oI+aOoue0ouWPkeeOsOaVsOaNruacieS6huWbvuaAjuS5iOeUuykNCi0gW+OAkOefpeivhueCueOAkWdncGxvdDLigJzmkK3np6/mnKjigJ3lvI/nu5jlm77or63ms5VdKCPnn6Xor4bngrlnZ3Bsb3Qy5pCt56ev5pyo5byP57uY5Zu+6K+t5rOVKe+8iOmHjeeCue+8iQ0KLSBb44CQ5a6e6Le1MeOAkeebtOaWueWbvl0oI+Wunui3tTHnm7Tmlrnlm74pDQotIFvjgJDlrp7ot7Uy44CR5a+G5bqm5Zu+XSgj5a6e6Le1MuWvhuW6puWbvikNCi0gW+OAkOaOoue0ouWPkeeOsOOAkeminOiJsueahFJHQuWNgeWFrei/m+WItueggV0oI+aOoue0ouWPkeeOsOminOiJsueahHJnYuWNgeWFrei/m+WItueggSkNCi0gW+OAkOefpeivhueCueOAkeeusee6v+WbvueahOe7n+iuoeWQq+S5iV0oI+efpeivhueCueeusee6v+WbvueahOe7n+iuoeWQq+S5iSkNCi0gW+OAkOWunui3tTPjgJHnrrHnur/lm75dKCPlrp7ot7Uz566x57q/5Zu+KQ0KLSBb44CQ5a6e6Le1NOOAkeaPkOeQtOWbvl0oI+Wunui3tTTmj5DnkLTlm74pDQotIFvjgJDmjqLntKLlj5HnjrDjgJHlsbHohIrlm75dKCPmjqLntKLlj5HnjrDlsbHohIrlm74pDQotIFvjgJDlrp7ot7U144CR5p+x5b2i5Zu+XSgj5a6e6Le1NeafseW9ouWbvinvvIjph43ngrnvvIkNCi0gW+OAkOWunui3tTbjgJHngrnnur/lm75dKCPlrp7ot7U254K557q/5Zu+KQ0KLSBb44CQ5a6e6Le1N+OAkeaVo+eCueWbvl0oI+Wunui3tTfmlaPngrnlm74p77yI6YeN54K577yJDQotIFvjgJDlrp7ot7U444CR5rCU5rOh5Zu+XSgj5a6e6Le1OOawlOazoeWbvikNCi0gW+OAkOaOoue0ouWPkeeOsOOAkeeDreWKm+Wbvl0oI+aOoue0ouWPkeeOsOeDreWKm+WbvikNCi0gW+OAkOWunui3tTnjgJHmipjnur/lm75dKCPlrp7ot7U55oqY57q/5Zu+Ke+8iOmHjeeCue+8iQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIOacrOeroOaJgOmcgFLljIUNCmxpYnJhcnkoYnJ1Y2VSKQ0KIyBsaWJyYXJ5KGdncGxvdDIpICAjIOWKoOi9vWJydWNlUuaXtuW3sum7mOiupOWKoOi9vWdncGxvdDINCmBgYA0KDQojIOaVsOaNruWPr+inhuWMlueahOaAu+S9k+aAnei3rw0KDQojIyMjIOOAkOaOoue0ouWPkeeOsOOAkeaVsOaNruacieS6hu+8jOWbvuaAjuS5iOeUu++8nyB7I+aOoue0ouWPkeeOsOaVsOaNruacieS6huWbvuaAjuS5iOeUu30NCg0KLSBbUiBHcmFwaCBHYWxsZXJ5IFLnu5jlm77nvo7mnK/ppoZdKGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8pey51cml9DQogIC0g57uY5Yi25Y+Y6YeP5YiG5biDDQogICAgLSDnm7Tmlrnlm77vvIhIaXN0b2dyYW3vvIkNCiAgICAtIOWvhuW6puWbvu+8iERlbnNpdHnvvIkNCiAgICAtIOeusee6v+Wbvu+8iEJveHBsb3TvvIkNCiAgICAtIOaPkOeQtOWbvu+8iFZpb2xpbu+8iQ0KICAgIC0g5bGx6ISK5Zu+77yIUmlkZ2VsaW5l77yJDQogIC0g57uY5Yi25Y+Y6YeP5aSn5bCPDQogICAgLSDmn7HlvaLlm77vvIhCYXJwbG9077yJDQogICAgLSDngrnnur/lm77vvIhEb3R0ZWQgbGluZe+8iQ0KICAtIOe7mOWItuWPmOmHj+WFs+ezuw0KICAgIC0g5pWj54K55Zu+77yIU2NhdHRlcnBsb3TvvIkNCiAgICAtIOawlOazoeWbvu+8iEJ1YmJsZe+8iQ0KICAgIC0g54Ot5Yqb5Zu+77yISGVhdG1hcO+8iQ0KICAtIOe7mOWItuWPmOmHj+i2i+WKvw0KICAgIC0g5oqY57q/5Zu+77yITGluZSBjaGFydO+8iQ0KICAgIC0g6Z2i56ev5Zu+77yIQXJlYSBjaGFydO+8iQ0KDQojIyMjIOOAkOefpeivhueCueOAkWdncGxvdDLigJzmkK3np6/mnKjigJ3lvI/nu5jlm77or63ms5UgeyPnn6Xor4bngrlnZ3Bsb3Qy5pCt56ev5pyo5byP57uY5Zu+6K+t5rOVfQ0KDQpgZ2dwbG90MmDnu5jlm77or63ms5XnmoTpgLvovpHvvJoNCg0KMS4gIOWHhuWkh+aVsOaNru+8iGBkYXRhYO+8ie+8jOWwhuWPmOmHj+aYoOWwhOWIsOe+juWtpuWxnuaAp++8iGBhZXNg77yJ77yM5a6a5LmJWFnovbTjgIHpopzoibLjgIHlpKflsI/nrYkNCjIuICDmt7vliqDlh6DkvZXlr7nosaHvvIhgZ2VvbWDvvInvvIznu5jliLbngrnjgIHnur/jgIHpnaLnrYnvvIzlpJrkuKrlm77lsYLlj6/pgJrov4fliqDlj7fkvp3mrKHlj6DliqDvvIhgK2DvvIkNCjMuICDlj6/lgZrliIbpnaLlsI/lm77vvIhgZmFjZXRg77yJ77yM6LCD5pW05qCH5bqm77yIYHNjYWxlYO+8ie+8jOaKleW9seWIsOS4jeWQjOWdkOagh+ezu++8iGBjb29yZGDvvIkNCjQuICDorr7nva7moIfpopjmoIfnrb7vvIhgbGFic2DvvInvvIzosIPmlbTlm77kvovvvIhgZ3VpZGVzYO+8ie+8jOagueaNruWWnOWlvemAieaLqeS4u+mimOaWueahiO+8iGB0aGVtZWDvvIkNCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMTg2NzU2MTk3Mi5wbmcpDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTE2MDUwMzg4NDkucG5nKQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0yMjEzNzQ0ODI4LnBuZykNCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMTg2Mzk4MjQzMy5wbmcpDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTQ4NTIxMjMwNS5wbmcpDQoNCiMg57uY5Yi25Y+Y6YeP5YiG5biDDQoNCiMjIOebtOaWueWbvg0KDQojIyMjIOOAkOWunui3tTHjgJHnm7Tmlrnlm74geyPlrp7ot7Ux55u05pa55Zu+fQ0KDQpgYGB7cn0NCiMjIOaVsOaNruWHhuWkhw0KZGF0YSA9IGFpcnF1YWxpdHkNCmRhdGEkTW9udGggPSBhcy5mYWN0b3IoZGF0YSRNb250aCkNCmRhdGEkVGVtcC5DID0gKGRhdGEkVGVtcCAtIDMyKSAvIDEuOCAgIyDmkYTmsI/luqYgPSAo5Y2O5rCP5bqmIC0gMzIpIC8gMS44DQpzdHIoZGF0YSkNCg0KIyMgUuWfuuehgOS9nOWbvuWHveaVsO+8muebtOaWueWbvg0KaGlzdChkYXRhJFRlbXAuQykNCg0KIyMgZ2dwbG90MuaQreenr+acqOS9nOWbvg0KZ2dwbG90KCkgICMg56m655S75p2/DQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgICMg5pyJ5LqG5Z2Q5qCH57O7DQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICAjIOebtOaWueWbvg0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zPTEwLA0KICAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siLA0KICAgICAgICAgICAgICAgICBmaWxsPSJncmV5IikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9NSwNCiAgICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwNCiAgICAgICAgICAgICAgICAgZmlsbD0iZ3JleSIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21faGlzdG9ncmFtKA0KICAgIGFlcyh5PWFmdGVyX3N0YXQoZGVuc2l0eSkpLA0KICAgICMjIGRlbnNpdHksIGludGVncmF0ZSB0byAxDQogICAgYmlud2lkdGg9NSwNCiAgICBjb2xvcj0iYmxhY2siLA0KICAgIGZpbGw9ImdyZXkiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgNCiAgICBhZXMoeT1hZnRlcl9zdGF0KG5kZW5zaXR5KSksDQogICAgIyMgZGVuc2l0eSwgbWF4aW11bSB0byAxDQogICAgYmlud2lkdGg9NSwNCiAgICBjb2xvcj0iYmxhY2siLA0KICAgIGZpbGw9ImdyZXkiKQ0KYGBgDQoNCiMjIOWvhuW6puWbvg0KDQojIyMjIOOAkOWunui3tTLjgJHlr4bluqblm74geyPlrp7ot7Uy5a+G5bqm5Zu+fQ0KDQpgYGB7cn0NCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21fZGVuc2l0eSgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21fZGVuc2l0eShhZGp1c3Q9MC41KQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2RlbnNpdHkoYWRqdXN0PTIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21fZGVuc2l0eShhZGp1c3Q9MiwNCiAgICAgICAgICAgICAgIGxpbmV3aWR0aD0yLA0KICAgICAgICAgICAgICAgY29sb3I9ImRhcmtibHVlIiwNCiAgICAgICAgICAgICAgIGZpbGw9ImxpZ2h0Ymx1ZSIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMsIHk9YWZ0ZXJfc3RhdChuZGVuc2l0eSkpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTUpICsNCiAgZ2VvbV9kZW5zaXR5KGFkanVzdD0yLA0KICAgICAgICAgICAgICAgbGluZXdpZHRoPTIsDQogICAgICAgICAgICAgICBjb2xvcj0iZGFya2JsdWUiLA0KICAgICAgICAgICAgICAgZmlsbD0ibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgIGFscGhhPTAuNSkNCmBgYA0KDQojIyMjIOOAkOaOoue0ouWPkeeOsOOAkeminOiJsueahFJHQuWNgeWFrei/m+WItueggSB7I+aOoue0ouWPkeeOsOminOiJsueahHJnYuWNgeWFrei/m+WItueggX0NCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMzczODM2MjQ2MS5wbmcpDQoNCiMjIOeusee6v+Wbvg0KDQojIyMjIOOAkOefpeivhueCueOAkeeusee6v+WbvueahOe7n+iuoeWQq+S5iSB7I+efpeivhueCueeusee6v+WbvueahOe7n+iuoeWQq+S5iX0NCg0KLSDlm5vliIbkvY3mlbDvvIhxdWFydGlsZe+8ie+8muaKiuaVsOaNruWbm+etieWIhueahOeVjOmZkA0KICAtIFEx77yaMFx+MjUl77yI5YmNMjUl55WM6ZmQ77yJDQogIC0gUTLvvJoyNVx+NTAl77yI5Lit5L2N5pWwNTAl55WM6ZmQ77yJDQogIC0gUTPvvJo1MFx+NzUl77yI5ZCOMjUl55WM6ZmQ77yJDQogIC0gUTTvvJo3NVx+MTAwJe+8iOWeq+W6le+8iQ0KLSBJUVLvvIhpbnRlcnF1YXJ0aWxlIHJhbmdl77yJ77ya5Zub5YiG5L2N6Led77yI5YiG5biD5LitUTHlkoxRM+S5i+mXtOeahOWPmOmHj+WAvOiMg+WbtO+8iQ0KICAtIElRUiA9IFEzIOKAkyBRMQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0yNTEwNDQ2MjQucG5nKQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0xODI3NjQzOTY2LnBuZykNCg0KIyMjIyDjgJDlrp7ot7Uz44CR566x57q/5Zu+IHsj5a6e6Le1M+eusee6v+Wbvn0NCg0KYGBge3J9DQojIyBS5Z+656GA5L2c5Zu+5Ye95pWw77ya566x57q/5Zu+DQpib3hwbG90KGRhdGEkVGVtcC5DKQ0KYm94cGxvdChUZW1wLkMgfiBNb250aCwgZGF0YT1kYXRhKQ0KDQojIyBnZ3Bsb3Qy5pCt56ev5pyo5L2c5Zu+DQpnZ3Bsb3QoZGF0YSwgYWVzKHk9VGVtcC5DKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9TW9udGgsIHk9VGVtcC5DKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9TW9udGgsIHk9VGVtcC5DKSkgKw0KICBnZW9tX2JveHBsb3QoDQogICAgZmlsbD0iYmx1ZSIsDQogICAgYWxwaGE9MC4zLA0KICAgIG91dGxpZXIuY29sb3I9InJlZCIsDQogICAgb3V0bGllci5zaXplPTIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1Nb250aCwgeT1UZW1wLkMpKSArDQogIGdlb21fYm94cGxvdChhZXMoZmlsbD1Nb250aCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIGxpbWl0cz1jKDEwLCA0MCkpICsNCiAgbGFicyh4PSJNb250aCIsDQogICAgICAgeT0iVGVtcGVyYXR1cmUiLA0KICAgICAgIGZpbGw9Ik1vbnRoIikNCmBgYA0KDQojIyDmj5DnkLTlm74NCg0KIyMjIyDjgJDlrp7ot7U044CR5o+Q55C05Zu+IHsj5a6e6Le1NOaPkOeQtOWbvn0NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHg9TW9udGgsIHk9VGVtcC5DKSkgKw0KICBnZW9tX3Zpb2xpbihhZXMoZmlsbD1Nb250aCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDEwLCA0MCkpICsNCiAgbGFicyh4PSJNb250aCIsDQogICAgICAgeT0iVGVtcGVyYXR1cmUiLA0KICAgICAgIGZpbGw9Ik1vbnRoIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PU1vbnRoLCB5PVRlbXAuQykpICsNCiAgZ2VvbV92aW9saW4oYWVzKGZpbGw9TW9udGgpKSArDQogIGdlb21fYm94cGxvdChmaWxsPSJ3aGl0ZSIsIHdpZHRoPTAuMykgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMTAsIDQwKSkgKw0KICBsYWJzKHg9Ik1vbnRoIiwNCiAgICAgICB5PSJUZW1wZXJhdHVyZSIsDQogICAgICAgZmlsbD0iTW9udGgiKQ0KYGBgDQoNCiMjIOWxseiEiuWbvg0KDQojIyMjIOOAkOaOoue0ouWPkeeOsOOAkeWxseiEiuWbviB7I+aOoue0ouWPkeeOsOWxseiEiuWbvn0NCg0KYGBge3J9DQojIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JpZGdlcyIpDQpsaWJyYXJ5KGdncmlkZ2VzKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DLCB5PU1vbnRoKSkgKw0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzKCkgKw0KICB0aGVtZV9yaWRnZXMoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DLCB5PU1vbnRoLCBmaWxsPWFmdGVyX3N0YXQoeCkpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXNfZ3JhZGllbnQoKSArDQogIGxhYnMoeD0iVGVtcGVyYXR1cmUiLCB5PSJNb250aCIpICsNCiAgdGhlbWVfcmlkZ2VzKCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQywgeT1Nb250aCwgZmlsbD1hZnRlcl9zdGF0KHgpKSkgKw0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzX2dyYWRpZW50KA0KICAgIHNjYWxlPTAuOTUsICAgICAgICAjIOacgOmrmOWzsOmrmOW6pue8qeaUvuWIsDk1JQ0KICAgIHNob3cubGVnZW5kPUZBTFNFICAjIOS4jeaYvuekuuWbvuS+iw0KICApICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uPSJDIikgKyAgIyBWaXJpZGlz6YWN6Imy5pa55qGIDQogIGxhYnMoeD0iVGVtcGVyYXR1cmUiLCB5PSJNb250aCIpICsNCiAgdGhlbWVfcmlkZ2VzKCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQywgeT1Nb250aCwgZmlsbD1hZnRlcl9zdGF0KHgpKSkgKw0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzX2dyYWRpZW50KA0KICAgIHN0YXQ9ImJpbmxpbmUiLCAgICAjIOebtOaWueWbvue7n+iuoei9rOaNog0KICAgIGJpbnM9MjAsICAgICAgICAgICAjIDIw5Liq55u05pa55Zu+5YiG5q61DQogICAgc2NhbGU9MC44LCAgICAgICAgICMg5pyA6auY5bOw6auY5bqm57yp5pS+5YiwOTUlDQogICAgc2hvdy5sZWdlbmQ9RkFMU0UgICMg5LiN5pi+56S65Zu+5L6LDQogICkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoMTAsIDQwKSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb249IkMiKSArDQogIGxhYnMoeD0iVGVtcGVyYXR1cmUiLCB5PSJNb250aCIpICsNCiAgdGhlbWVfcmlkZ2VzKCkNCmBgYA0KDQojIOe7mOWItuWPmOmHj+Wkp+Wwjw0KDQojIyDmn7HlvaLlm74NCg0KIyMjIyDjgJDlrp7ot7U144CR5p+x5b2i5Zu+IHsj5a6e6Le1NeafseW9ouWbvn0NCg0KYGBge3J9DQojIyDmlbDmja7lh4blpIfvvJrmr4/mnIjmsJTmuKnnmoTkvLDorqHovrnpmYXlnYflgLzvvIhlbW1lYW5z77yJDQptb2RlbCA9IGxtKFRlbXAuQyB+IE1vbnRoLCBkYXRhKQ0KIyBzdW1tYXJ5KGVtbWVhbnMobW9kZWwsICJNb250aCIpKQ0KbWVhbnMgPSBtb2RlbCAlPiUgZW1tZWFucygiTW9udGgiKSAlPiUgc3VtbWFyeSgpDQptZWFucw0KDQpnZ3Bsb3QobWVhbnMsIGFlcyh4PU1vbnRoLCB5PWVtbWVhbikpICsNCiAgZ2VvbV9jb2woY29sb3I9ImJsYWNrIiwNCiAgICAgICAgICAgZmlsbD0iZ3JleSIsDQogICAgICAgICAgIHdpZHRoPTAuNikNCg0KZ2dwbG90KG1lYW5zLCBhZXMoeD1Nb250aCwgeT1lbW1lYW4pKSArDQogIGdlb21fY29sKGNvbG9yPSJibGFjayIsDQogICAgICAgICAgIGZpbGw9ImdyZXkiLA0KICAgICAgICAgICB3aWR0aD0wLjYpICsNCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1sb3dlci5DTCwNCiAgICAgICAgICAgICAgICAgICAgeW1heD11cHBlci5DTCksDQogICAgICAgICAgICAgICAgd2lkdGg9MC4xKSArDQogIGxhYnMoeT0iTWVhbiBUZW1wZXJhdHVyZSIsDQogICAgICAgdGl0bGU9IkFpciBRdWFsaXR5IERhdGEiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQojIyDmlbDmja7lh4blpIfvvJrkuKTlm6DntKDnu4Tpl7RBTk9WQeeahOS8sOiuoei+uemZheWdh+WAvO+8iGVtbWVhbnPvvIkNCmQgPSBiZXR3ZWVuLjINCmQkQSA9IGFzLmZhY3RvcihkJEEpDQpkJEIgPSBhcy5mYWN0b3IoZCRCKQ0KbW9kZWwgPSBsbShTQ09SRSB+IEEgKiBCLCBkYXRhPWQpDQptZWFucyA9IG1vZGVsICU+JSBlbW1lYW5zKCJBIiwgYnk9IkIiKSAlPiUgc3VtbWFyeSgpDQptZWFucw0KDQpnZ3Bsb3QobWVhbnMsIGFlcyh4PUEsIHk9ZW1tZWFuLCBmaWxsPUIpKSArDQogIGdlb21fY29sKHBvc2l0aW9uPSJkb2RnZSIsICAjIOiwg+aVtOawtOW5s+S9jee9ru+8jOi6sumBv+mHjeWPoOWbvuW9og0KICAgICAgICAgICB3aWR0aD0wLjYpDQoNCmdncGxvdChtZWFucywgYWVzKHg9QSwgeT1lbW1lYW4sIGZpbGw9QikpICsNCiAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIiwgICMg6LCD5pW05rC05bmz5L2N572u77yM6Lqy6YG/6YeN5Y+g5Zu+5b2iDQogICAgICAgICAgIHdpZHRoPTAuNikgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPWxvd2VyLkNMLA0KICAgICAgICAgICAgICAgICAgICB5bWF4PXVwcGVyLkNMKSwNCiAgICAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgwLjYpLA0KICAgICAgICAgICAgICAgIHdpZHRoPTAuMTUsDQogICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIikNCg0KZ2dwbG90KG1lYW5zLCBhZXMoeD1BLCB5PWVtbWVhbiwgZmlsbD1CKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiLCB3aWR0aD0wLjYpICsNCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1sb3dlci5DTCwNCiAgICAgICAgICAgICAgICAgICAgeW1heD11cHBlci5DTCksDQogICAgICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC42KSwNCiAgICAgICAgICAgICAgICB3aWR0aD0wLjE1LA0KICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1leHBhbnNpb24oYWRkPTApLA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzPWMoMCwgMTUpLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzPXNlcSgwLCAxNSwgMykpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgbGFicyh4PSJBIiwgeT0iU0NPUkUiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCiMjIOeCuee6v+Wbvg0KDQojIyMjIOOAkOWunui3tTbjgJHngrnnur/lm74geyPlrp7ot7U254K557q/5Zu+fQ0KDQpgYGB7cn0NCiMjIOaVsOaNruWHhuWkh++8muavj+aciOawlOa4qeeahOS8sOiuoei+uemZheWdh+WAvO+8iGVtbWVhbnPvvIkNCm1vZGVsID0gbG0oVGVtcC5DIH4gTW9udGgsIGRhdGEpDQojIHN1bW1hcnkoZW1tZWFucyhtb2RlbCwgIk1vbnRoIikpDQptZWFucyA9IG1vZGVsICU+JSBlbW1lYW5zKCJNb250aCIpICU+JSBzdW1tYXJ5KCkNCm1lYW5zDQoNCmVtbWlwKG1vZGVsLCB+IE1vbnRoLCBDSXM9VFJVRSkgICMg6L+U5ZueZ2dwbG905a+56LGhDQoNCmVtbWlwKG1vZGVsLCB+IE1vbnRoLCBDSXM9VFJVRSkgKw0KICBsYWJzKHg9Ik1vbnRoIiwNCiAgICAgICB5PSJNZWFuIFRlbXBlcmF0dXJlIiwNCiAgICAgICB0aXRsZT0iQWlyIFF1YWxpdHkgRGF0YSIsDQogICAgICAgc3VidGl0bGU9IkRhaWx5IFRlbXBlcmF0dXJlIiwNCiAgICAgICBjYXB0aW9uPSIqIEVycm9yIGJhciA9IDk1JSBDSSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMg57uY5Yi25Y+Y6YeP5YWz57O7DQoNCiMjIOaVo+eCueWbvg0KDQojIyMjIOOAkOWunui3tTfjgJHmlaPngrnlm74geyPlrp7ot7U35pWj54K55Zu+fQ0KDQpgYGB7cn0NCiMjIFLln7rnoYDkvZzlm77lh73mlbDvvJrmlaPngrnlm74NCnBsb3QoeD1kYXRhJFdpbmQsIHk9ZGF0YSRUZW1wLkMpDQoNCiMjIGdncGxvdDLmkK3np6/mnKjkvZzlm74NCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludCgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgoKSAgIyDlkI7nlLvnmoRnZW9t5Zu+5bGC5Zyo5pyA5LiK6Z2i77yBDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGdlb21fcG9pbnQoY29sb3I9ImdyZXkiKSAgIyDlkI7nlLvnmoRnZW9t5Zu+5bGC5Zyo5pyA5LiK6Z2i77yBDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIyLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9InJlZCIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIyLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9InJlZCIpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIyLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9InJlZCIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTksIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvcj0iYmx1ZSIpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgpKSArDQogIGdlb21fcG9pbnQoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZT1GQUxTRSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgbGFicyh0aXRsZT0iVGVtcGVyYXR1cmUgJiBXaW5kIFNwZWVkIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBjb2xvcj0iYmxhY2siKSArDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygwLCAyMSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDEwLCA0MCkpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiKSArDQogIGxhYnModGl0bGU9IlRlbXBlcmF0dXJlICYgV2luZCBTcGVlZCIpDQpgYGANCg0KIyMg5rCU5rOh5Zu+DQoNCiMjIyMg44CQ5a6e6Le1OOOAkeawlOazoeWbviB7I+Wunui3tTjmsJTms6Hlm759DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCwgc2l6ZT1Tb2xhci5SKSkgKw0KICBnZW9tX3BvaW50KHNoYXBlPTIxKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCwgc2l6ZT1Tb2xhci5SKSkgKw0KICBnZW9tX3BvaW50KHNoYXBlPTIxKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICBzY2FsZV9zaXplX2NvbnRpbnVvdXMoYnJlYWtzPXNlcSg1MCwgMzAwLCA1MCkpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgsIHNpemU9U29sYXIuUikpICsNCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1Nb250aCksIGFscGhhPTAuMiwgc2hhcGU9MjEpICsNCiAgZ2VvbV9wb2ludChzaGFwZT0yMSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKGJyZWFrcz1zZXEoNTAsIDMwMCwgNTApKQ0KYGBgDQoNCiMjIOeDreWKm+Wbvg0KDQojIyMjIOOAkOaOoue0ouWPkeeOsOOAkeeDreWKm+WbviB7I+aOoue0ouWPkeeOsOeDreWKm+Wbvn0NCg0KYGBge3J9DQpjb3IgPSBDb3JyKGFpcnF1YWxpdHkpDQoNCmNvciRwbG90ICsgbGFicyh0aXRsZT0iQ29ycmVsYXRpb24gUGxvdCIpDQoNCmNvciRwbG90ICsNCiAgbGFicyh0aXRsZT0iQ29ycmVsYXRpb24gUGxvdCIpICsNCiAgc2NhbGVfZmlsbF9mZXJtZW50ZXIoDQogICAgcGFsZXR0ZT0iUmRCdSIsDQogICAgZGlyZWN0aW9uPTEsDQogICAgbGltaXRzPWMoLTEsIDEpLA0KICAgIGJyZWFrcz1zZXEoLTEsIDEsIDAuMiksDQogICAgZ3VpZGU9Z3VpZGVfY29sb3JzdGVwcygNCiAgICAgIGJhcndpZHRoPTAuNSwNCiAgICAgIGJhcmhlaWdodD0xMCkpDQoNCmNvciRwbG90ICsNCiAgbGFicyh0aXRsZT0iQ29ycmVsYXRpb24gUGxvdCIpICsNCiAgc2NhbGVfZmlsbF9mZXJtZW50ZXIoDQogICAgcGFsZXR0ZT0iU3BlY3RyYWwiLA0KICAgIGRpcmVjdGlvbj0xLA0KICAgIGxpbWl0cz1jKC0xLCAxKSwNCiAgICBicmVha3M9c2VxKC0xLCAxLCAwLjIpLA0KICAgIGd1aWRlPWd1aWRlX2NvbG9yc3RlcHMoDQogICAgICBiYXJ3aWR0aD0wLjUsDQogICAgICBiYXJoZWlnaHQ9MTApKQ0KYGBgDQoNCiMg57uY5Yi25Y+Y6YeP6LaL5Yq/DQoNCiMjIOaKmOe6v+Wbvg0KDQojIyMjIOOAkOWunui3tTnjgJHmipjnur/lm74geyPlrp7ot7U55oqY57q/5Zu+fQ0KDQpgYGB7cn0NCmRhdGEgPSBhcy5kYXRhLnRhYmxlKGFpcnF1YWxpdHkpDQpkYXRhWywgRGF0ZSA6PSBhcy5EYXRlKHNwcmludGYoIjE5NzMtJTAyZC0lMDJkIiwgTW9udGgsIERheSkpXQ0KZGF0YVssIFRlbXAuQyA6PSAoVGVtcCAtIDMyKSAvIDEuOF0NCmRhdGENCg0KZ2dwbG90KGRhdGEsIGFlcyh4PURhdGUsIHk9VGVtcC5DKSkgKw0KICBnZW9tX2xpbmUoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9zbW9vdGgoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZShjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9VGVtcC5DKSkgKw0KICBnZW9tX3Ntb290aChjb2xvcj0iYmxhY2siKSArDQogIHNjYWxlX2NvbG9yX2Rpc3RpbGxlcihwYWxldHRlPSJSZFlsQnUiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZShsaW5ld2lkdGg9MSkgKw0KICBnZW9tX3BvaW50KGFlcyhmaWxsPVRlbXAuQyksIHNoYXBlPTIxKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscz0iJW0tJWQiLCAgIyAiJVktJW0tJWQiDQogICAgICAgICAgICAgICBkYXRlX2JyZWFrcz0iMSBtb250aCIsDQogICAgICAgICAgICAgICBkYXRlX21pbm9yX2JyZWFrcz0iNyBkYXlzIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMTAsIDQwKSkgKw0KICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlPSJSZFlsQnUiKSArDQogIGxhYnMoeD1OVUxMLA0KICAgICAgIHk9IlRlbXBlcmF0dXJlIiwNCiAgICAgICB0aXRsZT0iRGFpbHkgVGVtcGVyYXR1cmUiKQ0KYGBgDQoNCiMg44CQ5L2c5LiaOeOAkeWfuuehgOe7mOWbvue7g+S5oA0KDQrkvZzkuJropoHmsYLvvJoNCg0KLSDln7rkuo7mnJ/mnKvoh6rpgInmlbDmja7vvIzov5DnlKjmnKznq6DmiYDlrabnmoRgZ2dwbG90MmDnu5jlm77ku6PnoIHvvIznu4PkuaDnu5jliLblj5jph4/nmoTliIbluIPvvIjnm7Tmlrnlm77vvInjgIHlpKflsI/vvIjmn7HlvaLlm77vvInjgIHlhbPns7vvvIjmlaPngrnlm77vvInjgIEg6LaL5Yq/77yI5oqY57q/5Zu+77yJ77yM5q+P56eN5Zu+57uY5Yi25LiA5Liq5Y2z5Y+vDQotIOS9v+eUqFIgTWFya2Rvd27lrozmiJDvvIzlr7nlhbPplK7ku6PnoIHlj4rnu5PmnpzopoHmnInms6jph4ror7TmmI4NCg0K5bmz5Y+w5o+Q5Lqk77yaDQoNCi0g6L+Q6KGM5b6X5YiwSFRNTOe9kemhteaWh+S7tueahCoq5YWz6ZSu6YOo5YiG5oiq5Zu+77yI5Y+q6ZyA5LiK5Lyg5oiq5Zu+77yJKioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNClvov5Tlm57or77nqIvkuLvpobVdKGh0dHBzOi8vcHN5Y2hicnVjZS5naXRodWIuaW8vUkNvdXJzZS8pDQoNCsKpIOWMheWvkuWQtOmcnA0K