版权声明:本套课程材料开源,使用和分享必须遵守「创作共用许可协议 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+DQoNCi0gICBbQ2hhcDA5IFwjIOWbnuW9kuaooeWei+W7uueri+S4jue7k+aenOaKpeWRil0oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAwOSMlRTUlOUIlOUUlRTUlQkQlOTIlRTYlQTglQTElRTUlOUUlOEIlRTUlQkIlQkElRTclQUIlOEIlRTQlQjglOEUlRTclQkIlOTMlRTYlOUUlOUMlRTYlOEElQTUlRTUlOTElOEEpey51cml9DQotICAgW0NoYXAwOSBcIyDlpJrkuKrmqKHlnovmsYfmgLvkuI7ooajmoLzovpPlh7pdKGh0dHBzOi8vcHN5Y2hicnVjZS5naXRodWIuaW8vUkNvdXJzZS9DaGFwMDkjJUU1JUE0JTlBJUU0JUI4JUFBJUU2JUE4JUExJUU1JTlFJThCJUU2JUIxJTg3JUU2JTgwJUJCJUU0JUI4JThFJUU4JUExJUE4JUU2JUEwJUJDJUU4JUJFJTkzJUU1JTg3JUJBKXsudXJpfQ0KDQojIyMjIOacrOeroOimgeeCueebruW9lQ0KDQotICAgW+OAkOaOoue0ouWPkeeOsOOAkeaVsOaNruacieS6hu+8jOWbvuaAjuS5iOeUu++8n10oI+aOoue0ouWPkeeOsOaVsOaNruacieS6huWbvuaAjuS5iOeUuykNCi0gICBb44CQ55+l6K+G54K544CRZ2dwbG90MuKAnOaQreenr+acqOKAneW8j+e7mOWbvuivreazlV0oI+efpeivhueCuWdncGxvdDLmkK3np6/mnKjlvI/nu5jlm77or63ms5Up77yI6YeN54K577yJDQotICAgW+OAkOWunui3tTHjgJHnm7Tmlrnlm75dKCPlrp7ot7Ux55u05pa55Zu+KQ0KLSAgIFvjgJDlrp7ot7Uy44CR5a+G5bqm5Zu+XSgj5a6e6Le1MuWvhuW6puWbvikNCi0gICBb44CQ5o6i57Si5Y+R546w44CR6aKc6Imy55qEUkdC5Y2B5YWt6L+b5Yi256CBXSgj5o6i57Si5Y+R546w6aKc6Imy55qEcmdi5Y2B5YWt6L+b5Yi256CBKQ0KLSAgIFvjgJDnn6Xor4bngrnjgJHnrrHnur/lm77nmoTnu5/orqHlkKvkuYldKCPnn6Xor4bngrnnrrHnur/lm77nmoTnu5/orqHlkKvkuYkpDQotICAgW+OAkOWunui3tTPjgJHnrrHnur/lm75dKCPlrp7ot7Uz566x57q/5Zu+KQ0KLSAgIFvjgJDlrp7ot7U044CR5o+Q55C05Zu+XSgj5a6e6Le1NOaPkOeQtOWbvikNCi0gICBb44CQ5o6i57Si5Y+R546w44CR5bGx6ISK5Zu+XSgj5o6i57Si5Y+R546w5bGx6ISK5Zu+KQ0KLSAgIFvjgJDlrp7ot7U144CR5p+x5b2i5Zu+XSgj5a6e6Le1NeafseW9ouWbvinvvIjph43ngrnvvIkNCi0gICBb44CQ5a6e6Le1NuOAkeeCuee6v+Wbvl0oI+Wunui3tTbngrnnur/lm74pDQotICAgW+OAkOWunui3tTfjgJHmlaPngrnlm75dKCPlrp7ot7U35pWj54K55Zu+Ke+8iOmHjeeCue+8iQ0KLSAgIFvjgJDlrp7ot7U444CR5rCU5rOh5Zu+XSgj5a6e6Le1OOawlOazoeWbvikNCi0gICBb44CQ5o6i57Si5Y+R546w44CR54Ot5Yqb5Zu+XSgj5o6i57Si5Y+R546w54Ot5Yqb5Zu+KQ0KLSAgIFvjgJDlrp7ot7U544CR5oqY57q/5Zu+XSgj5a6e6Le1OeaKmOe6v+WbvinvvIjph43ngrnvvIkNCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyDmnKznq6DmiYDpnIBS5YyFDQpsaWJyYXJ5KGJydWNlUikNCiMgbGlicmFyeShnZ3Bsb3QyKSAgIyDliqDovb1icnVjZVLml7blt7Lpu5jorqTliqDovb1nZ3Bsb3QyDQpgYGANCg0KIyDmlbDmja7lj6/op4bljJbnmoTmgLvkvZPmgJ3ot68NCg0KIyMjIyDjgJDmjqLntKLlj5HnjrDjgJHmlbDmja7mnInkuobvvIzlm77mgI7kuYjnlLvvvJ8geyPmjqLntKLlj5HnjrDmlbDmja7mnInkuoblm77mgI7kuYjnlLt9DQoNCi0gICBbUiBHcmFwaCBHYWxsZXJ5IFLnu5jlm77nvo7mnK/ppoZdKGh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8pey51cml9DQogICAgLSAgIOe7mOWItuWPmOmHj+WIhuW4gw0KICAgICAgICAtICAg55u05pa55Zu+77yISGlzdG9ncmFt77yJDQogICAgICAgIC0gICDlr4bluqblm77vvIhEZW5zaXR577yJDQogICAgICAgIC0gICDnrrHnur/lm77vvIhCb3hwbG9077yJDQogICAgICAgIC0gICDmj5DnkLTlm77vvIhWaW9saW7vvIkNCiAgICAgICAgLSAgIOWxseiEiuWbvu+8iFJpZGdlbGluZe+8iQ0KICAgIC0gICDnu5jliLblj5jph4/lpKflsI8NCiAgICAgICAgLSAgIOafseW9ouWbvu+8iEJhcnBsb3TvvIkNCiAgICAgICAgLSAgIOeCuee6v+Wbvu+8iERvdHRlZCBsaW5l77yJDQogICAgLSAgIOe7mOWItuWPmOmHj+WFs+ezuw0KICAgICAgICAtICAg5pWj54K55Zu+77yIU2NhdHRlcnBsb3TvvIkNCiAgICAgICAgLSAgIOawlOazoeWbvu+8iEJ1YmJsZe+8iQ0KICAgICAgICAtICAg54Ot5Yqb5Zu+77yISGVhdG1hcO+8iQ0KICAgIC0gICDnu5jliLblj5jph4/otovlir8NCiAgICAgICAgLSAgIOaKmOe6v+Wbvu+8iExpbmUgY2hhcnTvvIkNCiAgICAgICAgLSAgIOmdouenr+Wbvu+8iEFyZWEgY2hhcnTvvIkNCg0KIyMjIyDjgJDnn6Xor4bngrnjgJFnZ3Bsb3Qy4oCc5pCt56ev5pyo4oCd5byP57uY5Zu+6K+t5rOVIHsj55+l6K+G54K5Z2dwbG90MuaQreenr+acqOW8j+e7mOWbvuivreazlX0NCg0KYGdncGxvdDJg57uY5Zu+6K+t5rOV55qE6YC76L6R77yaDQoNCjEuICDlh4blpIfmlbDmja7vvIhgZGF0YWDvvInvvIzlsIblj5jph4/mmKDlsITliLDnvo7lrablsZ7mgKfvvIhgYWVzYO+8ie+8jOWumuS5iVhZ6L2044CB6aKc6Imy44CB5aSn5bCP562JDQoyLiAg5re75Yqg5Yeg5L2V5a+56LGh77yIYGdlb21g77yJ77yM57uY5Yi254K544CB57q/44CB6Z2i562J77yM5aSa5Liq5Zu+5bGC5Y+v6YCa6L+H5Yqg5Y+35L6d5qyh5Y+g5Yqg77yIYCtg77yJDQozLiAg5Y+v5YGa5YiG6Z2i5bCP5Zu+77yIYGZhY2V0YO+8ie+8jOiwg+aVtOagh+W6pu+8iGBzY2FsZWDvvInvvIzmipXlvbHliLDkuI3lkIzlnZDmoIfns7vvvIhgY29vcmRg77yJDQo0LiAg6K6+572u5qCH6aKY5qCH562+77yIYGxhYnNg77yJ77yM6LCD5pW05Zu+5L6L77yIYGd1aWRlc2DvvInvvIzmoLnmja7llpzlpb3pgInmi6nkuLvpopjmlrnmoYjvvIhgdGhlbWVg77yJDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTE4Njc1NjE5NzIucG5nKQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0xNjA1MDM4ODQ5LnBuZykNCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMjIxMzc0NDgyOC5wbmcpDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTE4NjM5ODI0MzMucG5nKQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC00ODUyMTIzMDUucG5nKQ0KDQojIOe7mOWItuWPmOmHj+WIhuW4gw0KDQojIyDnm7Tmlrnlm74NCg0KIyMjIyDjgJDlrp7ot7Ux44CR55u05pa55Zu+IHsj5a6e6Le1MeebtOaWueWbvn0NCg0KYGBge3J9DQojIyDmlbDmja7lh4blpIcNCmRhdGEgPSBhaXJxdWFsaXR5DQpkYXRhJE1vbnRoID0gYXMuZmFjdG9yKGRhdGEkTW9udGgpDQpkYXRhJFRlbXAuQyA9IChkYXRhJFRlbXAgLSAzMikgLyAxLjggICMg5pGE5rCP5bqmID0gKOWNjuawj+W6piAtIDMyKSAvIDEuOA0Kc3RyKGRhdGEpDQoNCiMjIFLln7rnoYDkvZzlm77lh73mlbDvvJrnm7Tmlrnlm74NCmhpc3QoZGF0YSRUZW1wLkMpDQoNCiMjIGdncGxvdDLmkK3np6/mnKjkvZzlm74NCmdncGxvdCgpICAjIOepuueUu+advw0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICAjIOacieS6huWdkOagh+ezuw0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSAgIyDnm7Tmlrnlm74NCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz0xMCwNCiAgICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwNCiAgICAgICAgICAgICAgICAgZmlsbD0iZ3JleSIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTUsDQogICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIsDQogICAgICAgICAgICAgICAgIGZpbGw9ImdyZXkiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgNCiAgICBhZXMoeT1hZnRlcl9zdGF0KGRlbnNpdHkpKSwNCiAgICAjIyBkZW5zaXR5LCBpbnRlZ3JhdGUgdG8gMQ0KICAgIGJpbndpZHRoPTUsDQogICAgY29sb3I9ImJsYWNrIiwNCiAgICBmaWxsPSJncmV5IikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oDQogICAgYWVzKHk9YWZ0ZXJfc3RhdChuZGVuc2l0eSkpLA0KICAgICMjIGRlbnNpdHksIG1heGltdW0gdG8gMQ0KICAgIGJpbndpZHRoPTUsDQogICAgY29sb3I9ImJsYWNrIiwNCiAgICBmaWxsPSJncmV5IikNCmBgYA0KDQojIyDlr4bluqblm74NCg0KIyMjIyDjgJDlrp7ot7Uy44CR5a+G5bqm5Zu+IHsj5a6e6Le1MuWvhuW6puWbvn0NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2RlbnNpdHkoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2RlbnNpdHkoYWRqdXN0PTAuNSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9kZW5zaXR5KGFkanVzdD0yKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2RlbnNpdHkoYWRqdXN0PTIsDQogICAgICAgICAgICAgICBsaW5ld2lkdGg9MiwNCiAgICAgICAgICAgICAgIGNvbG9yPSJkYXJrYmx1ZSIsDQogICAgICAgICAgICAgICBmaWxsPSJsaWdodGJsdWUiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DLCB5PWFmdGVyX3N0YXQobmRlbnNpdHkpKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD01KSArDQogIGdlb21fZGVuc2l0eShhZGp1c3Q9MiwNCiAgICAgICAgICAgICAgIGxpbmV3aWR0aD0yLA0KICAgICAgICAgICAgICAgY29sb3I9ImRhcmtibHVlIiwNCiAgICAgICAgICAgICAgIGZpbGw9ImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICBhbHBoYT0wLjUpDQpgYGANCg0KIyMjIyDjgJDmjqLntKLlj5HnjrDjgJHpopzoibLnmoRSR0LljYHlha3ov5vliLbnoIEgeyPmjqLntKLlj5HnjrDpopzoibLnmoRyZ2LljYHlha3ov5vliLbnoIF9DQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTM3MzgzNjI0NjEucG5nKQ0KDQojIyDnrrHnur/lm74NCg0KIyMjIyDjgJDnn6Xor4bngrnjgJHnrrHnur/lm77nmoTnu5/orqHlkKvkuYkgeyPnn6Xor4bngrnnrrHnur/lm77nmoTnu5/orqHlkKvkuYl9DQoNCi0gICDlm5vliIbkvY3mlbDvvIhxdWFydGlsZe+8ie+8muaKiuaVsOaNruWbm+etieWIhueahOeVjOmZkA0KICAgIC0gICBRMe+8mjBcfjI1Je+8iOWJjTI1JeeVjOmZkO+8iQ0KICAgIC0gICBRMu+8mjI1XH41MCXvvIjkuK3kvY3mlbA1MCXnlYzpmZDvvIkNCiAgICAtICAgUTPvvJo1MFx+NzUl77yI5ZCOMjUl55WM6ZmQ77yJDQogICAgLSAgIFE077yaNzVcfjEwMCXvvIjlnqvlupXvvIkNCi0gICBJUVLvvIhpbnRlcnF1YXJ0aWxlIHJhbmdl77yJ77ya5Zub5YiG5L2N6Led77yI5YiG5biD5LitUTHlkoxRM+S5i+mXtOeahOWPmOmHj+WAvOiMg+WbtO+8iQ0KICAgIC0gICBJUVIgPSBRMyDigJMgUTENCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMjUxMDQ0NjI0LnBuZykNCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMTgyNzY0Mzk2Ni5wbmcpDQoNCiMjIyMg44CQ5a6e6Le1M+OAkeeusee6v+WbviB7I+Wunui3tTPnrrHnur/lm759DQoNCmBgYHtyfQ0KIyMgUuWfuuehgOS9nOWbvuWHveaVsO+8mueusee6v+Wbvg0KYm94cGxvdChkYXRhJFRlbXAuQykNCmJveHBsb3QoVGVtcC5DIH4gTW9udGgsIGRhdGE9ZGF0YSkNCg0KIyMgZ2dwbG90MuaQreenr+acqOS9nOWbvg0KZ2dwbG90KGRhdGEsIGFlcyh5PVRlbXAuQykpICsNCiAgZ2VvbV9ib3hwbG90KCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PU1vbnRoLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9ib3hwbG90KCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PU1vbnRoLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9ib3hwbG90KA0KICAgIGZpbGw9ImJsdWUiLA0KICAgIGFscGhhPTAuMywNCiAgICBvdXRsaWVyLmNvbG9yPSJyZWQiLA0KICAgIG91dGxpZXIuc2l6ZT0yKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9TW9udGgsIHk9VGVtcC5DKSkgKw0KICBnZW9tX2JveHBsb3QoYWVzKGZpbGw9TW9udGgpKSArDQogIHNjYWxlX3lfY29udGludW91cygNCiAgICBsaW1pdHM9YygxMCwgNDApKSArDQogIGxhYnMoeD0iTW9udGgiLA0KICAgICAgIHk9IlRlbXBlcmF0dXJlIiwNCiAgICAgICBmaWxsPSJNb250aCIpDQpgYGANCg0KIyMg5o+Q55C05Zu+DQoNCiMjIyMg44CQ5a6e6Le1NOOAkeaPkOeQtOWbviB7I+Wunui3tTTmj5DnkLTlm759DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4PU1vbnRoLCB5PVRlbXAuQykpICsNCiAgZ2VvbV92aW9saW4oYWVzKGZpbGw9TW9udGgpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygxMCwgNDApKSArDQogIGxhYnMoeD0iTW9udGgiLA0KICAgICAgIHk9IlRlbXBlcmF0dXJlIiwNCiAgICAgICBmaWxsPSJNb250aCIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1Nb250aCwgeT1UZW1wLkMpKSArDQogIGdlb21fdmlvbGluKGFlcyhmaWxsPU1vbnRoKSkgKw0KICBnZW9tX2JveHBsb3QoZmlsbD0id2hpdGUiLCB3aWR0aD0wLjMpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDEwLCA0MCkpICsNCiAgbGFicyh4PSJNb250aCIsDQogICAgICAgeT0iVGVtcGVyYXR1cmUiLA0KICAgICAgIGZpbGw9Ik1vbnRoIikNCmBgYA0KDQojIyDlsbHohIrlm74NCg0KIyMjIyDjgJDmjqLntKLlj5HnjrDjgJHlsbHohIrlm74geyPmjqLntKLlj5HnjrDlsbHohIrlm759DQoNCmBgYHtyfQ0KIyMgaW5zdGFsbC5wYWNrYWdlcygiZ2dyaWRnZXMiKQ0KbGlicmFyeShnZ3JpZGdlcykNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQywgeT1Nb250aCkpICsNCiAgZ2VvbV9kZW5zaXR5X3JpZGdlcygpICsNCiAgdGhlbWVfcmlkZ2VzKCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQywgeT1Nb250aCwgZmlsbD1hZnRlcl9zdGF0KHgpKSkgKw0KICBnZW9tX2RlbnNpdHlfcmlkZ2VzX2dyYWRpZW50KCkgKw0KICBsYWJzKHg9IlRlbXBlcmF0dXJlIiwgeT0iTW9udGgiKSArDQogIHRoZW1lX3JpZGdlcygpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMsIHk9TW9udGgsIGZpbGw9YWZ0ZXJfc3RhdCh4KSkpICsNCiAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudCgNCiAgICBzY2FsZT0wLjk1LCAgICAgICAgIyDmnIDpq5jls7Dpq5jluqbnvKnmlL7liLA5NSUNCiAgICBzaG93LmxlZ2VuZD1GQUxTRSAgIyDkuI3mmL7npLrlm77kvosNCiAgKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbj0iQyIpICsgICMgVmlyaWRpc+mFjeiJsuaWueahiA0KICBsYWJzKHg9IlRlbXBlcmF0dXJlIiwgeT0iTW9udGgiKSArDQogIHRoZW1lX3JpZGdlcygpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMsIHk9TW9udGgsIGZpbGw9YWZ0ZXJfc3RhdCh4KSkpICsNCiAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudCgNCiAgICBzdGF0PSJiaW5saW5lIiwgICAgIyDnm7Tmlrnlm77nu5/orqHovazmjaINCiAgICBiaW5zPTIwLCAgICAgICAgICAgIyAyMOS4quebtOaWueWbvuWIhuautQ0KICAgIHNjYWxlPTAuOCwgICAgICAgICAjIOacgOmrmOWzsOmrmOW6pue8qeaUvuWIsDk1JQ0KICAgIHNob3cubGVnZW5kPUZBTFNFICAjIOS4jeaYvuekuuWbvuS+iw0KICApICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDEwLCA0MCkpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uPSJDIikgKw0KICBsYWJzKHg9IlRlbXBlcmF0dXJlIiwgeT0iTW9udGgiKSArDQogIHRoZW1lX3JpZGdlcygpDQpgYGANCg0KIyDnu5jliLblj5jph4/lpKflsI8NCg0KIyMg5p+x5b2i5Zu+DQoNCiMjIyMg44CQ5a6e6Le1NeOAkeafseW9ouWbviB7I+Wunui3tTXmn7HlvaLlm759DQoNCmBgYHtyfQ0KIyMg5pWw5o2u5YeG5aSH77ya5q+P5pyI5rCU5rip55qE5Lyw6K6h6L656ZmF5Z2H5YC877yIZW1tZWFuc++8iQ0KbW9kZWwgPSBsbShUZW1wLkMgfiBNb250aCwgZGF0YSkNCiMgc3VtbWFyeShlbW1lYW5zKG1vZGVsLCAiTW9udGgiKSkNCm1lYW5zID0gbW9kZWwgJT4lIGVtbWVhbnMoIk1vbnRoIikgJT4lIHN1bW1hcnkoKQ0KbWVhbnMNCg0KZ2dwbG90KG1lYW5zLCBhZXMoeD1Nb250aCwgeT1lbW1lYW4pKSArDQogIGdlb21fY29sKGNvbG9yPSJibGFjayIsDQogICAgICAgICAgIGZpbGw9ImdyZXkiLA0KICAgICAgICAgICB3aWR0aD0wLjYpDQoNCmdncGxvdChtZWFucywgYWVzKHg9TW9udGgsIHk9ZW1tZWFuKSkgKw0KICBnZW9tX2NvbChjb2xvcj0iYmxhY2siLA0KICAgICAgICAgICBmaWxsPSJncmV5IiwNCiAgICAgICAgICAgd2lkdGg9MC42KSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49bG93ZXIuQ0wsDQogICAgICAgICAgICAgICAgICAgIHltYXg9dXBwZXIuQ0wpLA0KICAgICAgICAgICAgICAgIHdpZHRoPTAuMSkgKw0KICBsYWJzKHk9Ik1lYW4gVGVtcGVyYXR1cmUiLA0KICAgICAgIHRpdGxlPSJBaXIgUXVhbGl0eSBEYXRhIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KIyMg5pWw5o2u5YeG5aSH77ya5Lik5Zug57Sg57uE6Ze0QU5PVkHnmoTkvLDorqHovrnpmYXlnYflgLzvvIhlbW1lYW5z77yJDQpkID0gYmV0d2Vlbi4yDQpkJEEgPSBhcy5mYWN0b3IoZCRBKQ0KZCRCID0gYXMuZmFjdG9yKGQkQikNCm1vZGVsID0gbG0oU0NPUkUgfiBBICogQiwgZGF0YT1kKQ0KbWVhbnMgPSBtb2RlbCAlPiUgZW1tZWFucygiQSIsIGJ5PSJCIikgJT4lIHN1bW1hcnkoKQ0KbWVhbnMNCg0KZ2dwbG90KG1lYW5zLCBhZXMoeD1BLCB5PWVtbWVhbiwgZmlsbD1CKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiLCAgIyDosIPmlbTmsLTlubPkvY3nva7vvIzourLpgb/ph43lj6Dlm77lvaINCiAgICAgICAgICAgd2lkdGg9MC42KQ0KDQpnZ3Bsb3QobWVhbnMsIGFlcyh4PUEsIHk9ZW1tZWFuLCBmaWxsPUIpKSArDQogIGdlb21fY29sKHBvc2l0aW9uPSJkb2RnZSIsICAjIOiwg+aVtOawtOW5s+S9jee9ru+8jOi6sumBv+mHjeWPoOWbvuW9og0KICAgICAgICAgICB3aWR0aD0wLjYpICsNCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1sb3dlci5DTCwNCiAgICAgICAgICAgICAgICAgICAgeW1heD11cHBlci5DTCksDQogICAgICAgICAgICAgICAgcG9zaXRpb249cG9zaXRpb25fZG9kZ2UoMC42KSwNCiAgICAgICAgICAgICAgICB3aWR0aD0wLjE1LA0KICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIpDQoNCmdncGxvdChtZWFucywgYWVzKHg9QSwgeT1lbW1lYW4sIGZpbGw9QikpICsNCiAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIiwgd2lkdGg9MC42KSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49bG93ZXIuQ0wsDQogICAgICAgICAgICAgICAgICAgIHltYXg9dXBwZXIuQ0wpLA0KICAgICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuNiksDQogICAgICAgICAgICAgICAgd2lkdGg9MC4xNSwNCiAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9ZXhwYW5zaW9uKGFkZD0wKSwNCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cz1jKDAsIDE1KSwNCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1zZXEoMCwgMTUsIDMpKSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IlNldDEiKSArDQogIGxhYnMoeD0iQSIsIHk9IlNDT1JFIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyDngrnnur/lm74NCg0KIyMjIyDjgJDlrp7ot7U244CR54K557q/5Zu+IHsj5a6e6Le1NueCuee6v+Wbvn0NCg0KYGBge3J9DQojIyDmlbDmja7lh4blpIfvvJrmr4/mnIjmsJTmuKnnmoTkvLDorqHovrnpmYXlnYflgLzvvIhlbW1lYW5z77yJDQptb2RlbCA9IGxtKFRlbXAuQyB+IE1vbnRoLCBkYXRhKQ0KIyBzdW1tYXJ5KGVtbWVhbnMobW9kZWwsICJNb250aCIpKQ0KbWVhbnMgPSBtb2RlbCAlPiUgZW1tZWFucygiTW9udGgiKSAlPiUgc3VtbWFyeSgpDQptZWFucw0KDQplbW1pcChtb2RlbCwgfiBNb250aCwgQ0lzPVRSVUUpICAjIOi/lOWbnmdncGxvdOWvueixoQ0KDQplbW1pcChtb2RlbCwgfiBNb250aCwgQ0lzPVRSVUUpICsNCiAgbGFicyh4PSJNb250aCIsDQogICAgICAgeT0iTWVhbiBUZW1wZXJhdHVyZSIsDQogICAgICAgdGl0bGU9IkFpciBRdWFsaXR5IERhdGEiLA0KICAgICAgIHN1YnRpdGxlPSJEYWlseSBUZW1wZXJhdHVyZSIsDQogICAgICAgY2FwdGlvbj0iKiBFcnJvciBiYXIgPSA5NSUgQ0kiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCiMg57uY5Yi25Y+Y6YeP5YWz57O7DQoNCiMjIOaVo+eCueWbvg0KDQojIyMjIOOAkOWunui3tTfjgJHmlaPngrnlm74geyPlrp7ot7U35pWj54K55Zu+fQ0KDQpgYGB7cn0NCiMjIFLln7rnoYDkvZzlm77lh73mlbDvvJrmlaPngrnlm74NCnBsb3QoeD1kYXRhJFdpbmQsIHk9ZGF0YSRUZW1wLkMpDQoNCiMjIGdncGxvdDLmkK3np6/mnKjkvZzlm74NCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludCgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgoKSAgIyDlkI7nlLvnmoRnZW9t5Zu+5bGC5Zyo5pyA5LiK6Z2i77yBDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGdlb21fcG9pbnQoY29sb3I9ImdyZXkiKSAgIyDlkI7nlLvnmoRnZW9t5Zu+5bGC5Zyo5pyA5LiK6Z2i77yBDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIyLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9InJlZCIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIyLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9InJlZCIpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PTIyLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9InJlZCIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PTksIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvcj0iYmx1ZSIpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgpKSArDQogIGdlb21fcG9pbnQoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZT1GQUxTRSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgbGFicyh0aXRsZT0iVGVtcGVyYXR1cmUgJiBXaW5kIFNwZWVkIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBjb2xvcj0iYmxhY2siKSArDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygwLCAyMSkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDEwLCA0MCkpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiKSArDQogIGxhYnModGl0bGU9IlRlbXBlcmF0dXJlICYgV2luZCBTcGVlZCIpDQpgYGANCg0KIyMg5rCU5rOh5Zu+DQoNCiMjIyMg44CQ5a6e6Le1OOOAkeawlOazoeWbviB7I+Wunui3tTjmsJTms6Hlm759DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCwgc2l6ZT1Tb2xhci5SKSkgKw0KICBnZW9tX3BvaW50KHNoYXBlPTIxKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCwgc2l6ZT1Tb2xhci5SKSkgKw0KICBnZW9tX3BvaW50KHNoYXBlPTIxKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICBzY2FsZV9zaXplX2NvbnRpbnVvdXMoYnJlYWtzPXNlcSg1MCwgMzAwLCA1MCkpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgsIHNpemU9U29sYXIuUikpICsNCiAgZ2VvbV9wb2ludChhZXMoZmlsbD1Nb250aCksIGFscGhhPTAuMiwgc2hhcGU9MjEpICsNCiAgZ2VvbV9wb2ludChzaGFwZT0yMSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKGJyZWFrcz1zZXEoNTAsIDMwMCwgNTApKQ0KYGBgDQoNCiMjIOeDreWKm+Wbvg0KDQojIyMjIOOAkOaOoue0ouWPkeeOsOOAkeeDreWKm+WbviB7I+aOoue0ouWPkeeOsOeDreWKm+Wbvn0NCg0KYGBge3J9DQpjb3IgPSBDb3JyKGFpcnF1YWxpdHkpDQoNCmNvciRwbG90ICsgbGFicyh0aXRsZT0iQ29ycmVsYXRpb24gUGxvdCIpDQoNCmNvciRwbG90ICsNCiAgbGFicyh0aXRsZT0iQ29ycmVsYXRpb24gUGxvdCIpICsNCiAgc2NhbGVfZmlsbF9mZXJtZW50ZXIoDQogICAgcGFsZXR0ZT0iUmRCdSIsDQogICAgZGlyZWN0aW9uPTEsDQogICAgbGltaXRzPWMoLTEsIDEpLA0KICAgIGJyZWFrcz1zZXEoLTEsIDEsIDAuMiksDQogICAgZ3VpZGU9Z3VpZGVfY29sb3JzdGVwcygNCiAgICAgIGJhcndpZHRoPTAuNSwNCiAgICAgIGJhcmhlaWdodD0xMCkpDQoNCmNvciRwbG90ICsNCiAgbGFicyh0aXRsZT0iQ29ycmVsYXRpb24gUGxvdCIpICsNCiAgc2NhbGVfZmlsbF9mZXJtZW50ZXIoDQogICAgcGFsZXR0ZT0iU3BlY3RyYWwiLA0KICAgIGRpcmVjdGlvbj0xLA0KICAgIGxpbWl0cz1jKC0xLCAxKSwNCiAgICBicmVha3M9c2VxKC0xLCAxLCAwLjIpLA0KICAgIGd1aWRlPWd1aWRlX2NvbG9yc3RlcHMoDQogICAgICBiYXJ3aWR0aD0wLjUsDQogICAgICBiYXJoZWlnaHQ9MTApKQ0KYGBgDQoNCiMg57uY5Yi25Y+Y6YeP6LaL5Yq/DQoNCiMjIOaKmOe6v+Wbvg0KDQojIyMjIOOAkOWunui3tTnjgJHmipjnur/lm74geyPlrp7ot7U55oqY57q/5Zu+fQ0KDQpgYGB7cn0NCmRhdGEgPSBhcy5kYXRhLnRhYmxlKGFpcnF1YWxpdHkpDQpkYXRhWywgRGF0ZSA6PSBhcy5EYXRlKHNwcmludGYoIjE5NzMtJTAyZC0lMDJkIiwgTW9udGgsIERheSkpXQ0KZGF0YVssIFRlbXAuQyA6PSAoVGVtcCAtIDMyKSAvIDEuOF0NCmRhdGENCg0KZ2dwbG90KGRhdGEsIGFlcyh4PURhdGUsIHk9VGVtcC5DKSkgKw0KICBnZW9tX2xpbmUoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9zbW9vdGgoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZShjb2xvcj0iZ3JleSIpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9VGVtcC5DKSkgKw0KICBnZW9tX3Ntb290aChjb2xvcj0iYmxhY2siKSArDQogIHNjYWxlX2NvbG9yX2Rpc3RpbGxlcihwYWxldHRlPSJSZFlsQnUiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZShsaW5ld2lkdGg9MSkgKw0KICBnZW9tX3BvaW50KGFlcyhmaWxsPVRlbXAuQyksIHNoYXBlPTIxKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscz0iJW0tJWQiLCAgIyAiJVktJW0tJWQiDQogICAgICAgICAgICAgICBkYXRlX2JyZWFrcz0iMSBtb250aCIsDQogICAgICAgICAgICAgICBkYXRlX21pbm9yX2JyZWFrcz0iNyBkYXlzIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMTAsIDQwKSkgKw0KICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlPSJSZFlsQnUiKSArDQogIGxhYnMoeD1OVUxMLA0KICAgICAgIHk9IlRlbXBlcmF0dXJlIiwNCiAgICAgICB0aXRsZT0iRGFpbHkgVGVtcGVyYXR1cmUiKQ0KYGBgDQoNCiMg44CQ5L2c5LiaOeOAkeWfuuehgOe7mOWbvue7g+S5oA0KDQrkvZzkuJropoHmsYLvvJoNCg0KLSAgIOWfuuS6juacn+acq+iHqumAieaVsOaNru+8jOi/kOeUqOacrOeroOaJgOWtpueahGBnZ3Bsb3QyYOe7mOWbvuS7o+egge+8jOe7g+S5oOe7mOWItuWPmOmHj+eahOWIhuW4g++8iOebtOaWueWbvu+8ieOAgeWkp+Wwj++8iOafseW9ouWbvu+8ieOAgeWFs+ezu++8iOaVo+eCueWbvu+8ieOAgSDotovlir/vvIjmipjnur/lm77vvInvvIzmr4/np43lm77nu5jliLbkuIDkuKrljbPlj68NCi0gICDkvb/nlKhSIE1hcmtkb3du5a6M5oiQ77yM5a+55YWz6ZSu5Luj56CB5Y+K57uT5p6c6KaB5pyJ5rOo6YeK6K+05piODQoNCuW5s+WPsOaPkOS6pO+8mg0KDQotICAg6L+Q6KGM5b6X5Yiw55qESFRNTOe9kemhte+8jOWPiuWFtuWFs+mUrumDqOWIhuaIquWbvg0K