版权声明:本套课程材料开源,使用和分享必须遵守「创作共用许可协议 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,         # 最高峰高度缩放到80%
    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+DQphdXRob3I6ICLmjojor77mlZnluIjvvJrljIXlr5LlkLTpnJwiDQojIGRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGFuY2hvcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCiAgICBjc3M6IFJtZENTUy5jc3MNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8cCBzdHlsZT0iZm9udC1zaXplOiAxMnB4Ij7niYjmnYPlo7DmmI7vvJrmnKzlpZfor77nqIvmnZDmlpnlvIDmupDvvIzkvb/nlKjlkozliIbkuqvlv4XpobvpgbXlrojjgIzliJvkvZzlhbHnlKjorrjlj6/ljY/orq4gQ0MgQlktTkMtU0HjgI3vvIjmnaXmupDlvJXnlKgt6Z2e5ZWG5Lia55So6YCU5L2/55SoLeS7peebuOWQjOaWueW8j+WFseS6q++8ieOAgjxpbWcgc3JjPSJpbWcvQ0MtQlktTkMtU0EuanBnIiB3aWR0aD0iMTIwcHgiIGhlaWdodD0iNDJweCIgc3R5bGU9ImZsb2F0OiByaWdodCIgLz48L3A+DQpgYGANCg0KYGBge3IgQ29uZmlnLCBpbmNsdWRlPUZBTFNFfQ0Kb3B0aW9ucygNCiAga25pdHIua2FibGUuTkEgPSAiIiwNCiAgZGlnaXRzID0gNA0KKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBjb21tZW50ID0gIiIsDQogIGZpZy53aWR0aCA9IDYsDQogIGZpZy5oZWlnaHQgPSA0LA0KICBkcGkgPSAzMDANCikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBDaGFwMTDvvJrln7rnoYDnu5jlm74NCg0KIyMjIyDlvoDmnJ/opoHngrnlm57pob4NCg0KLSBbQ2hhcDA5IFwjIOWbnuW9kuaooeWei+W7uueri+S4jue7k+aenOaKpeWRil0oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAwOSMlRTUlOUIlOUUlRTUlQkQlOTIlRTYlQTglQTElRTUlOUUlOEIlRTUlQkIlQkElRTclQUIlOEIlRTQlQjglOEUlRTclQkIlOTMlRTYlOUUlOUMlRTYlOEElQTUlRTUlOTElOEEpey51cml9DQotIFtDaGFwMDkgXCMg5aSa5Liq5qih5Z6L5rGH5oC75LiO6KGo5qC86L6T5Ye6XShodHRwczovL3BzeWNoYnJ1Y2UuZ2l0aHViLmlvL1JDb3Vyc2UvQ2hhcDA5IyVFNSVBNCU5QSVFNCVCOCVBQSVFNiVBOCVBMSVFNSU5RSU4QiVFNiVCMSU4NyVFNiU4MCVCQiVFNCVCOCU4RSVFOCVBMSVBOCVFNiVBMCVCQyVFOCVCRSU5MyVFNSU4NyVCQSl7LnVyaX0NCg0KIyMjIyDmnKznq6DopoHngrnnm67lvZUNCg0KLSBb44CQ5o6i57Si5Y+R546w44CR5pWw5o2u5pyJ5LqG77yM5Zu+5oCO5LmI55S777yfXSgj5o6i57Si5Y+R546w5pWw5o2u5pyJ5LqG5Zu+5oCO5LmI55S7KQ0KLSBb44CQ55+l6K+G54K544CRZ2dwbG90MuKAnOaQreenr+acqOKAneW8j+e7mOWbvuivreazlV0oI+efpeivhueCuWdncGxvdDLmkK3np6/mnKjlvI/nu5jlm77or63ms5Up77yI6YeN54K577yJDQotIFvjgJDlrp7ot7Ux44CR55u05pa55Zu+XSgj5a6e6Le1MeebtOaWueWbvikNCi0gW+OAkOWunui3tTLjgJHlr4bluqblm75dKCPlrp7ot7Uy5a+G5bqm5Zu+KQ0KLSBb44CQ5o6i57Si5Y+R546w44CR6aKc6Imy55qEUkdC5Y2B5YWt6L+b5Yi256CBXSgj5o6i57Si5Y+R546w6aKc6Imy55qEcmdi5Y2B5YWt6L+b5Yi256CBKQ0KLSBb44CQ55+l6K+G54K544CR566x57q/5Zu+55qE57uf6K6h5ZCr5LmJXSgj55+l6K+G54K5566x57q/5Zu+55qE57uf6K6h5ZCr5LmJKQ0KLSBb44CQ5a6e6Le1M+OAkeeusee6v+Wbvl0oI+Wunui3tTPnrrHnur/lm74pDQotIFvjgJDlrp7ot7U044CR5o+Q55C05Zu+XSgj5a6e6Le1NOaPkOeQtOWbvikNCi0gW+OAkOaOoue0ouWPkeeOsOOAkeWxseiEiuWbvl0oI+aOoue0ouWPkeeOsOWxseiEiuWbvikNCi0gW+OAkOWunui3tTXjgJHmn7HlvaLlm75dKCPlrp7ot7U15p+x5b2i5Zu+Ke+8iOmHjeeCue+8iQ0KLSBb44CQ5a6e6Le1NuOAkeeCuee6v+Wbvl0oI+Wunui3tTbngrnnur/lm74pDQotIFvjgJDlrp7ot7U344CR5pWj54K55Zu+XSgj5a6e6Le1N+aVo+eCueWbvinvvIjph43ngrnvvIkNCi0gW+OAkOWunui3tTjjgJHmsJTms6Hlm75dKCPlrp7ot7U45rCU5rOh5Zu+KQ0KLSBb44CQ5o6i57Si5Y+R546w44CR54Ot5Yqb5Zu+XSgj5o6i57Si5Y+R546w54Ot5Yqb5Zu+KQ0KLSBb44CQ5a6e6Le1OeOAkeaKmOe6v+Wbvl0oI+Wunui3tTnmipjnur/lm74p77yI6YeN54K577yJDQoNCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMg5pys56ug5omA6ZyAUuWMhQ0KbGlicmFyeShicnVjZVIpDQojIGxpYnJhcnkoZ2dwbG90MikgICMg5Yqg6L29YnJ1Y2VS5pe25bey6buY6K6k5Yqg6L29Z2dwbG90Mg0KYGBgDQoNCiMg5pWw5o2u5Y+v6KeG5YyW55qE5oC75L2T5oCd6LevDQoNCiMjIyMg44CQ5o6i57Si5Y+R546w44CR5pWw5o2u5pyJ5LqG77yM5Zu+5oCO5LmI55S777yfIHsj5o6i57Si5Y+R546w5pWw5o2u5pyJ5LqG5Zu+5oCO5LmI55S7fQ0KDQotIFtSIEdyYXBoIEdhbGxlcnkgUue7mOWbvue+juacr+mmhl0oaHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tLyl7LnVyaX0NCiAgLSDnu5jliLblj5jph4/liIbluIMNCiAgICAtIOebtOaWueWbvu+8iEhpc3RvZ3Jhbe+8iQ0KICAgIC0g5a+G5bqm5Zu+77yIRGVuc2l0ee+8iQ0KICAgIC0g566x57q/5Zu+77yIQm94cGxvdO+8iQ0KICAgIC0g5o+Q55C05Zu+77yIVmlvbGlu77yJDQogICAgLSDlsbHohIrlm77vvIhSaWRnZWxpbmXvvIkNCiAgLSDnu5jliLblj5jph4/lpKflsI8NCiAgICAtIOafseW9ouWbvu+8iEJhcnBsb3TvvIkNCiAgICAtIOeCuee6v+Wbvu+8iERvdHRlZCBsaW5l77yJDQogIC0g57uY5Yi25Y+Y6YeP5YWz57O7DQogICAgLSDmlaPngrnlm77vvIhTY2F0dGVycGxvdO+8iQ0KICAgIC0g5rCU5rOh5Zu+77yIQnViYmxl77yJDQogICAgLSDng63lipvlm77vvIhIZWF0bWFw77yJDQogIC0g57uY5Yi25Y+Y6YeP6LaL5Yq/DQogICAgLSDmipjnur/lm77vvIhMaW5lIGNoYXJ077yJDQogICAgLSDpnaLnp6/lm77vvIhBcmVhIGNoYXJ077yJDQoNCiMjIyMg44CQ55+l6K+G54K544CRZ2dwbG90MuKAnOaQreenr+acqOKAneW8j+e7mOWbvuivreazlSB7I+efpeivhueCuWdncGxvdDLmkK3np6/mnKjlvI/nu5jlm77or63ms5V9DQoNCmBnZ3Bsb3QyYOe7mOWbvuivreazleeahOmAu+i+ke+8mg0KDQoxLiAg5YeG5aSH5pWw5o2u77yIYGRhdGFg77yJ77yM5bCG5Y+Y6YeP5pig5bCE5Yiw576O5a2m5bGe5oCn77yIYGFlc2DvvInvvIzlrprkuYlYWei9tOOAgeminOiJsuOAgeWkp+Wwj+etiQ0KMi4gIOa3u+WKoOWHoOS9leWvueixoe+8iGBnZW9tYO+8ie+8jOe7mOWItueCueOAgee6v+OAgemdouetie+8jOWkmuS4quWbvuWxguWPr+mAmui/h+WKoOWPt+S+neasoeWPoOWKoO+8iGArYO+8iQ0KMy4gIOWPr+WBmuWIhumdouWwj+Wbvu+8iGBmYWNldGDvvInvvIzosIPmlbTmoIfluqbvvIhgc2NhbGVg77yJ77yM5oqV5b2x5Yiw5LiN5ZCM5Z2Q5qCH57O777yIYGNvb3JkYO+8iQ0KNC4gIOiuvue9ruagh+mimOagh+etvu+8iGBsYWJzYO+8ie+8jOiwg+aVtOWbvuS+i++8iGBndWlkZXNg77yJ77yM5qC55o2u5Zac5aW96YCJ5oup5Li76aKY5pa55qGI77yIYHRoZW1lYO+8iQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0xODY3NTYxOTcyLnBuZykNCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMTYwNTAzODg0OS5wbmcpDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTIyMTM3NDQ4MjgucG5nKQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0xODYzOTgyNDMzLnBuZykNCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtNDg1MjEyMzA1LnBuZykNCg0KIyDnu5jliLblj5jph4/liIbluIMNCg0KIyMg55u05pa55Zu+DQoNCiMjIyMg44CQ5a6e6Le1MeOAkeebtOaWueWbviB7I+Wunui3tTHnm7Tmlrnlm759DQoNCmBgYHtyfQ0KIyMg5pWw5o2u5YeG5aSHDQpkYXRhID0gYWlycXVhbGl0eQ0KZGF0YSRNb250aCA9IGFzLmZhY3RvcihkYXRhJE1vbnRoKQ0KZGF0YSRUZW1wLkMgPSAoZGF0YSRUZW1wIC0gMzIpIC8gMS44ICAjIOaRhOawj+W6piA9ICjljY7msI/luqYgLSAzMikgLyAxLjgNCnN0cihkYXRhKQ0KDQojIyBS5Z+656GA5L2c5Zu+5Ye95pWw77ya55u05pa55Zu+DQpoaXN0KGRhdGEkVGVtcC5DKQ0KDQojIyBnZ3Bsb3Qy5pCt56ev5pyo5L2c5Zu+DQpnZ3Bsb3QoKSAgIyDnqbrnlLvmnb8NCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSAgIyDmnInkuoblnZDmoIfns7sNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21faGlzdG9ncmFtKCkgICMg55u05pa55Zu+DQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnM9MTAsDQogICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIsDQogICAgICAgICAgICAgICAgIGZpbGw9ImdyZXkiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD01LA0KICAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siLA0KICAgICAgICAgICAgICAgICBmaWxsPSJncmV5IikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oDQogICAgYWVzKHk9YWZ0ZXJfc3RhdChkZW5zaXR5KSksDQogICAgIyMgZGVuc2l0eSwgaW50ZWdyYXRlIHRvIDENCiAgICBiaW53aWR0aD01LA0KICAgIGNvbG9yPSJibGFjayIsDQogICAgZmlsbD0iZ3JleSIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21faGlzdG9ncmFtKA0KICAgIGFlcyh5PWFmdGVyX3N0YXQobmRlbnNpdHkpKSwNCiAgICAjIyBkZW5zaXR5LCBtYXhpbXVtIHRvIDENCiAgICBiaW53aWR0aD01LA0KICAgIGNvbG9yPSJibGFjayIsDQogICAgZmlsbD0iZ3JleSIpDQpgYGANCg0KIyMg5a+G5bqm5Zu+DQoNCiMjIyMg44CQ5a6e6Le1MuOAkeWvhuW6puWbviB7I+Wunui3tTLlr4bluqblm759DQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9kZW5zaXR5KCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9kZW5zaXR5KGFkanVzdD0wLjUpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMpKSArDQogIGdlb21fZGVuc2l0eShhZGp1c3Q9MikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9kZW5zaXR5KGFkanVzdD0yLA0KICAgICAgICAgICAgICAgbGluZXdpZHRoPTIsDQogICAgICAgICAgICAgICBjb2xvcj0iZGFya2JsdWUiLA0KICAgICAgICAgICAgICAgZmlsbD0ibGlnaHRibHVlIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQywgeT1hZnRlcl9zdGF0KG5kZW5zaXR5KSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9NSkgKw0KICBnZW9tX2RlbnNpdHkoYWRqdXN0PTIsDQogICAgICAgICAgICAgICBsaW5ld2lkdGg9MiwNCiAgICAgICAgICAgICAgIGNvbG9yPSJkYXJrYmx1ZSIsDQogICAgICAgICAgICAgICBmaWxsPSJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgYWxwaGE9MC41KQ0KYGBgDQoNCiMjIyMg44CQ5o6i57Si5Y+R546w44CR6aKc6Imy55qEUkdC5Y2B5YWt6L+b5Yi256CBIHsj5o6i57Si5Y+R546w6aKc6Imy55qEcmdi5Y2B5YWt6L+b5Yi256CBfQ0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0zNzM4MzYyNDYxLnBuZykNCg0KIyMg566x57q/5Zu+DQoNCiMjIyMg44CQ55+l6K+G54K544CR566x57q/5Zu+55qE57uf6K6h5ZCr5LmJIHsj55+l6K+G54K5566x57q/5Zu+55qE57uf6K6h5ZCr5LmJfQ0KDQotIOWbm+WIhuS9jeaVsO+8iHF1YXJ0aWxl77yJ77ya5oqK5pWw5o2u5Zub562J5YiG55qE55WM6ZmQDQogIC0gUTHvvJowXH4yNSXvvIjliY0yNSXnlYzpmZDvvIkNCiAgLSBRMu+8mjI1XH41MCXvvIjkuK3kvY3mlbA1MCXnlYzpmZDvvIkNCiAgLSBRM++8mjUwXH43NSXvvIjlkI4yNSXnlYzpmZDvvIkNCiAgLSBRNO+8mjc1XH4xMDAl77yI5Z6r5bqV77yJDQotIElRUu+8iGludGVycXVhcnRpbGUgcmFuZ2XvvInvvJrlm5vliIbkvY3ot53vvIjliIbluIPkuK1RMeWSjFEz5LmL6Ze055qE5Y+Y6YeP5YC86IyD5Zu077yJDQogIC0gSVFSID0gUTMg4oCTIFExDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTI1MTA0NDYyNC5wbmcpDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTE4Mjc2NDM5NjYucG5nKQ0KDQojIyMjIOOAkOWunui3tTPjgJHnrrHnur/lm74geyPlrp7ot7Uz566x57q/5Zu+fQ0KDQpgYGB7cn0NCiMjIFLln7rnoYDkvZzlm77lh73mlbDvvJrnrrHnur/lm74NCmJveHBsb3QoZGF0YSRUZW1wLkMpDQpib3hwbG90KFRlbXAuQyB+IE1vbnRoLCBkYXRhPWRhdGEpDQoNCiMjIGdncGxvdDLmkK3np6/mnKjkvZzlm74NCmdncGxvdChkYXRhLCBhZXMoeT1UZW1wLkMpKSArDQogIGdlb21fYm94cGxvdCgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1Nb250aCwgeT1UZW1wLkMpKSArDQogIGdlb21fYm94cGxvdCgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1Nb250aCwgeT1UZW1wLkMpKSArDQogIGdlb21fYm94cGxvdCgNCiAgICBmaWxsPSJibHVlIiwNCiAgICBhbHBoYT0wLjMsDQogICAgb3V0bGllci5jb2xvcj0icmVkIiwNCiAgICBvdXRsaWVyLnNpemU9MikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PU1vbnRoLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9ib3hwbG90KGFlcyhmaWxsPU1vbnRoKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGltaXRzPWMoMTAsIDQwKSkgKw0KICBsYWJzKHg9Ik1vbnRoIiwNCiAgICAgICB5PSJUZW1wZXJhdHVyZSIsDQogICAgICAgZmlsbD0iTW9udGgiKQ0KYGBgDQoNCiMjIOaPkOeQtOWbvg0KDQojIyMjIOOAkOWunui3tTTjgJHmj5DnkLTlm74geyPlrp7ot7U05o+Q55C05Zu+fQ0KDQpgYGB7cn0NCmdncGxvdChkYXRhLCBhZXMoeD1Nb250aCwgeT1UZW1wLkMpKSArDQogIGdlb21fdmlvbGluKGFlcyhmaWxsPU1vbnRoKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMTAsIDQwKSkgKw0KICBsYWJzKHg9Ik1vbnRoIiwNCiAgICAgICB5PSJUZW1wZXJhdHVyZSIsDQogICAgICAgZmlsbD0iTW9udGgiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9TW9udGgsIHk9VGVtcC5DKSkgKw0KICBnZW9tX3Zpb2xpbihhZXMoZmlsbD1Nb250aCkpICsNCiAgZ2VvbV9ib3hwbG90KGZpbGw9IndoaXRlIiwgd2lkdGg9MC4zKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygxMCwgNDApKSArDQogIGxhYnMoeD0iTW9udGgiLA0KICAgICAgIHk9IlRlbXBlcmF0dXJlIiwNCiAgICAgICBmaWxsPSJNb250aCIpDQpgYGANCg0KIyMg5bGx6ISK5Zu+DQoNCiMjIyMg44CQ5o6i57Si5Y+R546w44CR5bGx6ISK5Zu+IHsj5o6i57Si5Y+R546w5bGx6ISK5Zu+fQ0KDQpgYGB7cn0NCiMjIGluc3RhbGwucGFja2FnZXMoImdncmlkZ2VzIikNCmxpYnJhcnkoZ2dyaWRnZXMpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMsIHk9TW9udGgpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArDQogIHRoZW1lX3JpZGdlcygpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1UZW1wLkMsIHk9TW9udGgsIGZpbGw9YWZ0ZXJfc3RhdCh4KSkpICsNCiAgZ2VvbV9kZW5zaXR5X3JpZGdlc19ncmFkaWVudCgpICsNCiAgbGFicyh4PSJUZW1wZXJhdHVyZSIsIHk9Ik1vbnRoIikgKw0KICB0aGVtZV9yaWRnZXMoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DLCB5PU1vbnRoLCBmaWxsPWFmdGVyX3N0YXQoeCkpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXNfZ3JhZGllbnQoDQogICAgc2NhbGU9MC45NSwgICAgICAgICMg5pyA6auY5bOw6auY5bqm57yp5pS+5YiwOTUlDQogICAgc2hvdy5sZWdlbmQ9RkFMU0UgICMg5LiN5pi+56S65Zu+5L6LDQogICkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb249IkMiKSArICAjIFZpcmlkaXPphY3oibLmlrnmoYgNCiAgbGFicyh4PSJUZW1wZXJhdHVyZSIsIHk9Ik1vbnRoIikgKw0KICB0aGVtZV9yaWRnZXMoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9VGVtcC5DLCB5PU1vbnRoLCBmaWxsPWFmdGVyX3N0YXQoeCkpKSArDQogIGdlb21fZGVuc2l0eV9yaWRnZXNfZ3JhZGllbnQoDQogICAgc3RhdD0iYmlubGluZSIsICAgICMg55u05pa55Zu+57uf6K6h6L2s5o2iDQogICAgYmlucz0yMCwgICAgICAgICAgICMgMjDkuKrnm7Tmlrnlm77liIbmrrUNCiAgICBzY2FsZT0wLjgsICAgICAgICAgIyDmnIDpq5jls7Dpq5jluqbnvKnmlL7liLA4MCUNCiAgICBzaG93LmxlZ2VuZD1GQUxTRSAgIyDkuI3mmL7npLrlm77kvosNCiAgKSArDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygxMCwgNDApKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbj0iQyIpICsNCiAgbGFicyh4PSJUZW1wZXJhdHVyZSIsIHk9Ik1vbnRoIikgKw0KICB0aGVtZV9yaWRnZXMoKQ0KYGBgDQoNCiMg57uY5Yi25Y+Y6YeP5aSn5bCPDQoNCiMjIOafseW9ouWbvg0KDQojIyMjIOOAkOWunui3tTXjgJHmn7HlvaLlm74geyPlrp7ot7U15p+x5b2i5Zu+fQ0KDQpgYGB7cn0NCiMjIOaVsOaNruWHhuWkh++8muavj+aciOawlOa4qeeahOS8sOiuoei+uemZheWdh+WAvO+8iGVtbWVhbnPvvIkNCm1vZGVsID0gbG0oVGVtcC5DIH4gTW9udGgsIGRhdGEpDQojIHN1bW1hcnkoZW1tZWFucyhtb2RlbCwgIk1vbnRoIikpDQptZWFucyA9IG1vZGVsICU+JSBlbW1lYW5zKCJNb250aCIpICU+JSBzdW1tYXJ5KCkNCm1lYW5zDQoNCmdncGxvdChtZWFucywgYWVzKHg9TW9udGgsIHk9ZW1tZWFuKSkgKw0KICBnZW9tX2NvbChjb2xvcj0iYmxhY2siLA0KICAgICAgICAgICBmaWxsPSJncmV5IiwNCiAgICAgICAgICAgd2lkdGg9MC42KQ0KDQpnZ3Bsb3QobWVhbnMsIGFlcyh4PU1vbnRoLCB5PWVtbWVhbikpICsNCiAgZ2VvbV9jb2woY29sb3I9ImJsYWNrIiwNCiAgICAgICAgICAgZmlsbD0iZ3JleSIsDQogICAgICAgICAgIHdpZHRoPTAuNikgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPWxvd2VyLkNMLA0KICAgICAgICAgICAgICAgICAgICB5bWF4PXVwcGVyLkNMKSwNCiAgICAgICAgICAgICAgICB3aWR0aD0wLjEpICsNCiAgbGFicyh5PSJNZWFuIFRlbXBlcmF0dXJlIiwNCiAgICAgICB0aXRsZT0iQWlyIFF1YWxpdHkgRGF0YSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMjIOaVsOaNruWHhuWkh++8muS4pOWboOe0oOe7hOmXtEFOT1ZB55qE5Lyw6K6h6L656ZmF5Z2H5YC877yIZW1tZWFuc++8iQ0KZCA9IGJldHdlZW4uMg0KZCRBID0gYXMuZmFjdG9yKGQkQSkNCmQkQiA9IGFzLmZhY3RvcihkJEIpDQptb2RlbCA9IGxtKFNDT1JFIH4gQSAqIEIsIGRhdGE9ZCkNCm1lYW5zID0gbW9kZWwgJT4lIGVtbWVhbnMoIkEiLCBieT0iQiIpICU+JSBzdW1tYXJ5KCkNCm1lYW5zDQoNCmdncGxvdChtZWFucywgYWVzKHg9QSwgeT1lbW1lYW4sIGZpbGw9QikpICsNCiAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIiwgICMg6LCD5pW05rC05bmz5L2N572u77yM6Lqy6YG/6YeN5Y+g5Zu+5b2iDQogICAgICAgICAgIHdpZHRoPTAuNikNCg0KZ2dwbG90KG1lYW5zLCBhZXMoeD1BLCB5PWVtbWVhbiwgZmlsbD1CKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiLCAgIyDosIPmlbTmsLTlubPkvY3nva7vvIzourLpgb/ph43lj6Dlm77lvaINCiAgICAgICAgICAgd2lkdGg9MC42KSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW49bG93ZXIuQ0wsDQogICAgICAgICAgICAgICAgICAgIHltYXg9dXBwZXIuQ0wpLA0KICAgICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuNiksDQogICAgICAgICAgICAgICAgd2lkdGg9MC4xNSwNCiAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siKQ0KDQpnZ3Bsb3QobWVhbnMsIGFlcyh4PUEsIHk9ZW1tZWFuLCBmaWxsPUIpKSArDQogIGdlb21fY29sKHBvc2l0aW9uPSJkb2RnZSIsIHdpZHRoPTAuNikgKw0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluPWxvd2VyLkNMLA0KICAgICAgICAgICAgICAgICAgICB5bWF4PXVwcGVyLkNMKSwNCiAgICAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgwLjYpLA0KICAgICAgICAgICAgICAgIHdpZHRoPTAuMTUsDQogICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kPWV4cGFuc2lvbihhZGQ9MCksDQogICAgICAgICAgICAgICAgICAgICBsaW1pdHM9YygwLCAxNSksDQogICAgICAgICAgICAgICAgICAgICBicmVha3M9c2VxKDAsIDE1LCAzKSkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICBsYWJzKHg9IkEiLCB5PSJTQ09SRSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KIyMg54K557q/5Zu+DQoNCiMjIyMg44CQ5a6e6Le1NuOAkeeCuee6v+WbviB7I+Wunui3tTbngrnnur/lm759DQoNCmBgYHtyfQ0KIyMg5pWw5o2u5YeG5aSH77ya5q+P5pyI5rCU5rip55qE5Lyw6K6h6L656ZmF5Z2H5YC877yIZW1tZWFuc++8iQ0KbW9kZWwgPSBsbShUZW1wLkMgfiBNb250aCwgZGF0YSkNCiMgc3VtbWFyeShlbW1lYW5zKG1vZGVsLCAiTW9udGgiKSkNCm1lYW5zID0gbW9kZWwgJT4lIGVtbWVhbnMoIk1vbnRoIikgJT4lIHN1bW1hcnkoKQ0KbWVhbnMNCg0KZW1taXAobW9kZWwsIH4gTW9udGgsIENJcz1UUlVFKSAgIyDov5Tlm55nZ3Bsb3Tlr7nosaENCg0KZW1taXAobW9kZWwsIH4gTW9udGgsIENJcz1UUlVFKSArDQogIGxhYnMoeD0iTW9udGgiLA0KICAgICAgIHk9Ik1lYW4gVGVtcGVyYXR1cmUiLA0KICAgICAgIHRpdGxlPSJBaXIgUXVhbGl0eSBEYXRhIiwNCiAgICAgICBzdWJ0aXRsZT0iRGFpbHkgVGVtcGVyYXR1cmUiLA0KICAgICAgIGNhcHRpb249IiogRXJyb3IgYmFyID0gOTUlIENJIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyDnu5jliLblj5jph4/lhbPns7sNCg0KIyMg5pWj54K55Zu+DQoNCiMjIyMg44CQ5a6e6Le1N+OAkeaVo+eCueWbviB7I+Wunui3tTfmlaPngrnlm759DQoNCmBgYHtyfQ0KIyMgUuWfuuehgOS9nOWbvuWHveaVsO+8muaVo+eCueWbvg0KcGxvdCh4PWRhdGEkV2luZCwgeT1kYXRhJFRlbXAuQykNCg0KIyMgZ2dwbG90MuaQreenr+acqOS9nOWbvg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DKSkgKw0KICBnZW9tX3BvaW50KCkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yPSJncmV5IikgKw0KICBnZW9tX3Ntb290aCgpICAjIOWQjueUu+eahGdlb23lm77lsYLlnKjmnIDkuIrpnaLvvIENCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DKSkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleSIpICAjIOWQjueUu+eahGdlb23lm77lsYLlnKjmnIDkuIrpnaLvvIENCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yPSJncmV5IikgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgY29sb3I9ImJsYWNrIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yPSJncmV5IikgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgY29sb3I9ImJsYWNrIikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MjIsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvcj0icmVkIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DKSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MjIsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvcj0icmVkIikgKw0KICBnZW9tX3BvaW50KGNvbG9yPSJncmV5IikgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgY29sb3I9ImJsYWNrIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DKSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQ9MjIsIGxpbmV0eXBlPSJkYXNoZWQiLCBjb2xvcj0icmVkIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9OSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yPSJibHVlIikgKw0KICBnZW9tX3BvaW50KGNvbG9yPSJncmV5IikgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgY29sb3I9ImJsYWNrIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCkpICsNCiAgZ2VvbV9wb2ludCgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIHNlPUZBTFNFKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICBsYWJzKHRpdGxlPSJUZW1wZXJhdHVyZSAmIFdpbmQgU3BlZWQiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2U9RkFMU0UpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsIDIxKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzPWMoMTAsIDQwKSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MSIpICsNCiAgbGFicyh0aXRsZT0iVGVtcGVyYXR1cmUgJiBXaW5kIFNwZWVkIikNCmBgYA0KDQojIyDmsJTms6Hlm74NCg0KIyMjIyDjgJDlrp7ot7U444CR5rCU5rOh5Zu+IHsj5a6e6Le1OOawlOazoeWbvn0NCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoLCBzaXplPVNvbGFyLlIpKSArDQogIGdlb21fcG9pbnQoc2hhcGU9MjEpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoLCBzaXplPVNvbGFyLlIpKSArDQogIGdlb21fcG9pbnQoc2hhcGU9MjEpICsNCiAgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDEiKSArDQogIHNjYWxlX3NpemVfY29udGludW91cyhicmVha3M9c2VxKDUwLCAzMDAsIDUwKSkNCg0KZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCwgc2l6ZT1Tb2xhci5SKSkgKw0KICBnZW9tX3BvaW50KGFlcyhmaWxsPU1vbnRoKSwgYWxwaGE9MC4yLCBzaGFwZT0yMSkgKw0KICBnZW9tX3BvaW50KHNoYXBlPTIxKSArDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJTZXQxIikgKw0KICBzY2FsZV9zaXplX2NvbnRpbnVvdXMoYnJlYWtzPXNlcSg1MCwgMzAwLCA1MCkpDQpgYGANCg0KIyMg54Ot5Yqb5Zu+DQoNCiMjIyMg44CQ5o6i57Si5Y+R546w44CR54Ot5Yqb5Zu+IHsj5o6i57Si5Y+R546w54Ot5Yqb5Zu+fQ0KDQpgYGB7cn0NCmNvciA9IENvcnIoYWlycXVhbGl0eSkNCg0KY29yJHBsb3QgKyBsYWJzKHRpdGxlPSJDb3JyZWxhdGlvbiBQbG90IikNCg0KY29yJHBsb3QgKw0KICBsYWJzKHRpdGxlPSJDb3JyZWxhdGlvbiBQbG90IikgKw0KICBzY2FsZV9maWxsX2Zlcm1lbnRlcigNCiAgICBwYWxldHRlPSJSZEJ1IiwNCiAgICBkaXJlY3Rpb249MSwNCiAgICBsaW1pdHM9YygtMSwgMSksDQogICAgYnJlYWtzPXNlcSgtMSwgMSwgMC4yKSwNCiAgICBndWlkZT1ndWlkZV9jb2xvcnN0ZXBzKA0KICAgICAgYmFyd2lkdGg9MC41LA0KICAgICAgYmFyaGVpZ2h0PTEwKSkNCg0KY29yJHBsb3QgKw0KICBsYWJzKHRpdGxlPSJDb3JyZWxhdGlvbiBQbG90IikgKw0KICBzY2FsZV9maWxsX2Zlcm1lbnRlcigNCiAgICBwYWxldHRlPSJTcGVjdHJhbCIsDQogICAgZGlyZWN0aW9uPTEsDQogICAgbGltaXRzPWMoLTEsIDEpLA0KICAgIGJyZWFrcz1zZXEoLTEsIDEsIDAuMiksDQogICAgZ3VpZGU9Z3VpZGVfY29sb3JzdGVwcygNCiAgICAgIGJhcndpZHRoPTAuNSwNCiAgICAgIGJhcmhlaWdodD0xMCkpDQpgYGANCg0KIyDnu5jliLblj5jph4/otovlir8NCg0KIyMg5oqY57q/5Zu+DQoNCiMjIyMg44CQ5a6e6Le1OeOAkeaKmOe6v+WbviB7I+Wunui3tTnmipjnur/lm759DQoNCmBgYHtyfQ0KZGF0YSA9IGFzLmRhdGEudGFibGUoYWlycXVhbGl0eSkNCmRhdGFbLCBEYXRlIDo9IGFzLkRhdGUoc3ByaW50ZigiMTk3My0lMDJkLSUwMmQiLCBNb250aCwgRGF5KSldDQpkYXRhWywgVGVtcC5DIDo9IChUZW1wIC0gMzIpIC8gMS44XQ0KZGF0YQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9RGF0ZSwgeT1UZW1wLkMpKSArDQogIGdlb21fbGluZSgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1EYXRlLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3Ntb290aCgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1EYXRlLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1EYXRlLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9saW5lKGNvbG9yPSJncmV5IikgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvcj1UZW1wLkMpKSArDQogIGdlb21fc21vb3RoKGNvbG9yPSJibGFjayIpICsNCiAgc2NhbGVfY29sb3JfZGlzdGlsbGVyKHBhbGV0dGU9IlJkWWxCdSIpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1EYXRlLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9saW5lKGxpbmV3aWR0aD0xKSArDQogIGdlb21fcG9pbnQoYWVzKGZpbGw9VGVtcC5DKSwgc2hhcGU9MjEpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzPSIlbS0lZCIsICAjICIlWS0lbS0lZCINCiAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzPSIxIG1vbnRoIiwNCiAgICAgICAgICAgICAgIGRhdGVfbWlub3JfYnJlYWtzPSI3IGRheXMiKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygxMCwgNDApKSArDQogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGU9IlJkWWxCdSIpICsNCiAgbGFicyh4PU5VTEwsDQogICAgICAgeT0iVGVtcGVyYXR1cmUiLA0KICAgICAgIHRpdGxlPSJEYWlseSBUZW1wZXJhdHVyZSIpDQpgYGANCg0KIyDjgJDkvZzkuJo544CR5Z+656GA57uY5Zu+57uD5LmgDQoNCuS9nOS4muimgeaxgu+8mg0KDQotIOWfuuS6juacn+acq+iHqumAieaVsOaNru+8jOi/kOeUqOacrOeroOaJgOWtpueahGBnZ3Bsb3QyYOe7mOWbvuS7o+egge+8jOe7g+S5oOe7mOWItuWPmOmHj+eahOWIhuW4g++8iOebtOaWueWbvu+8ieOAgeWkp+Wwj++8iOafseW9ouWbvu+8ieOAgeWFs+ezu++8iOaVo+eCueWbvu+8ieOAgSDotovlir/vvIjmipjnur/lm77vvInvvIzmr4/np43lm77nu5jliLbkuIDkuKrljbPlj68NCi0g5L2/55SoUiBNYXJrZG93buWujOaIkO+8jOWvueWFs+mUruS7o+eggeWPiue7k+aenOimgeacieazqOmHiuivtOaYjg0KDQrlubPlj7Dmj5DkuqTvvJoNCg0KLSDov5DooYzlvpfliLBIVE1M572R6aG15paH5Lu255qEKirlhbPplK7pg6jliIbmiKrlm77vvIjlj6rpnIDkuIrkvKDmiKrlm77vvIkqKg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KW+i/lOWbnuivvueoi+S4u+mhtV0oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlLykNCg0Kwqkg5YyF5a+S5ZC06ZycDQo=