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


Chap11:多图组合

往期要点回顾

本章要点目录

## 本章所需R包
library(bruceR)
# library(ggplot2)  # 加载bruceR时已默认加载ggplot2
# library(cowplot)  # 加载bruceR时已默认加载cowplot

分面小图

【实践1】facet_wrap():拆解分面小图

## 数据准备
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 ...
## 基础散点图
ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_point()

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

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

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

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point(show.legend=FALSE) +
  facet_wrap(~ Month, nrow=1)

ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point(show.legend=FALSE) +
  geom_smooth(method="lm", se=FALSE, show.legend=FALSE) +
  facet_wrap(~ Month, nrow=1)
`geom_smooth()` using formula = 'y ~ x'

## 修改因子标签
data$Months = factor(
  data$Month,
  levels = 5:9,
  labels = c("May", "June", "July", "August", "September")
)
ggplot(data, aes(x=Wind, y=Temp.C, color=Months)) +
  geom_point(show.legend=FALSE) +
  geom_smooth(method="lm", show.legend=FALSE) +
  facet_wrap(~ Months, nrow=1)
`geom_smooth()` using formula = 'y ~ x'

ggplot(data, aes(x=Wind, y=Temp.C, color=Months)) +
  geom_point(show.legend=FALSE) +
  geom_smooth(method="lm", show.legend=FALSE) +
  facet_wrap(~ Months, nrow=1, scales="free")
`geom_smooth()` using formula = 'y ~ x'

【实践2】facet_grid():两个分类变量的交叉组合分面

  • facet_wrap():线性排列(卷饼🌯)
    • ~ 分组变量
  • facet_grid():两因素交叉组合排列(华夫饼🧇)
    • 行分组变量 ~ 列分组变量

## 两个分类变量的情况
## 数据变量计算复习(Chap 05 & 06)
data.bfi = as.data.table(psych::bfi)
data.bfi[, let(
  Gender = factor(gender, levels=1:2, labels=c("Male", "Female")),
  Age = as.numeric(age),
  Edu = as.factor(education),
  E = MEAN(data.bfi, "E", 1:5, rev=c(1,2), range=1:6),
  A = MEAN(data.bfi, "A", 1:5, rev=1, range=1:6),
  C = MEAN(data.bfi, "C", 1:5, rev=c(4,5), range=1:6),
  N = MEAN(data.bfi, "N", 1:5, rev=NULL, range=1:6),
  O = MEAN(data.bfi, "O", 1:5, rev=c(2,5), range=1:6)
)]
str(data.bfi)
Classes 'data.table' and 'data.frame':  2800 obs. of  36 variables:
 $ A1       : int  2 2 5 4 2 6 2 4 4 2 ...
 $ A2       : int  4 4 4 4 3 6 5 3 3 5 ...
 $ A3       : int  3 5 5 6 3 5 5 1 6 6 ...
 $ A4       : int  4 2 4 5 4 6 3 5 3 6 ...
 $ A5       : int  4 5 4 5 5 5 5 1 3 5 ...
 $ C1       : int  2 5 4 4 4 6 5 3 6 6 ...
 $ C2       : int  3 4 5 4 4 6 4 2 6 5 ...
 $ C3       : int  3 4 4 3 5 6 4 4 3 6 ...
 $ C4       : int  4 3 2 5 3 1 2 2 4 2 ...
 $ C5       : int  4 4 5 5 2 3 3 4 5 1 ...
 $ E1       : int  3 1 2 5 2 2 4 3 5 2 ...
 $ E2       : int  3 1 4 3 2 1 3 6 3 2 ...
 $ E3       : int  3 6 4 4 5 6 4 4 NA 4 ...
 $ E4       : int  4 4 4 4 4 5 5 2 4 5 ...
 $ E5       : int  4 3 5 4 5 6 5 1 3 5 ...
 $ N1       : int  3 3 4 2 2 3 1 6 5 5 ...
 $ N2       : int  4 3 5 5 3 5 2 3 5 5 ...
 $ N3       : int  2 3 4 2 4 2 2 2 2 5 ...
 $ N4       : int  2 5 2 4 4 2 1 6 3 2 ...
 $ N5       : int  3 5 3 1 3 3 1 4 3 4 ...
 $ O1       : int  3 4 4 3 3 4 5 3 6 5 ...
 $ O2       : int  6 2 2 3 3 3 2 2 6 1 ...
 $ O3       : int  3 4 5 4 4 5 5 4 6 5 ...
 $ O4       : int  4 3 5 3 3 6 6 5 6 5 ...
 $ O5       : int  3 3 2 5 3 1 1 3 1 2 ...
 $ gender   : int  1 2 2 2 1 2 1 1 1 2 ...
 $ education: int  NA NA NA NA NA 3 NA 2 1 NA ...
 $ age      : int  16 18 17 17 17 21 18 19 19 17 ...
 $ Gender   : Factor w/ 2 levels "Male","Female": 1 2 2 2 1 2 1 1 1 2 ...
 $ Age      : num  16 18 17 17 17 21 18 19 19 17 ...
 $ Edu      : Factor w/ 5 levels "1","2","3","4",..: NA NA NA NA NA 3 NA 2 1 NA ...
 $ E        : num  3.8 5 4.2 3.6 4.8 5.6 4.2 2.4 3.25 4.8 ...
 $ A        : num  4 4.2 3.8 4.6 4 4.6 4.6 2.6 3.6 5.4 ...
 $ C        : num  2.8 4 4 3 4.4 5.6 4.4 3.4 4 5.6 ...
 $ N        : num  2.8 3.8 3.6 2.8 3.2 3 1.4 4.2 3.6 4.2 ...
 $ O        : num  3 4 4.8 3.2 3.6 5 5.4 4.2 5 5.2 ...
 - attr(*, ".internal.selfref")=<externalptr> 
p = ggplot(data.bfi[Age>=18 & Age<60 & !is.na(Edu)],
           aes(x=Age, y=E)) +
  geom_point(alpha=0.1) +
  geom_smooth(method="loess", color="firebrick", fill="orange") +
  labs(y="Extraversion")
p
`geom_smooth()` using formula = 'y ~ x'

p + facet_wrap(~ Gender)
`geom_smooth()` using formula = 'y ~ x'

p + facet_wrap(~ Gender + Edu)  # 并不理想
`geom_smooth()` using formula = 'y ~ x'

p + facet_wrap(~ Gender * Edu)  # 并不理想
`geom_smooth()` using formula = 'y ~ x'

p + facet_grid(~ Gender)  # . ~ 列分组变量(.点可以省略)
`geom_smooth()` using formula = 'y ~ x'

p + facet_grid(Gender ~ .)  # 行分组变量 ~ .(.点不能省略)
`geom_smooth()` using formula = 'y ~ x'

p + facet_grid(Gender ~ Edu)  # 行变量 ~ 列变量
`geom_smooth()` using formula = 'y ~ x'

p + facet_grid(Edu ~ Gender)  # 行变量 ~ 列变量
`geom_smooth()` using formula = 'y ~ x'

多图组合

【实践3】cowplot::plot_grid():多图组合

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

p2 = ggplot(data, aes(x=Month, y=Temp.C)) +
  geom_boxplot() +
  labs(y="Temperature")

p12 = cowplot::plot_grid(p1, p2, nrow=1, labels="AUTO")
p12

p3 = ggplot(data, aes(x=Wind, y=Temp.C)) +
  geom_point() +
  geom_smooth(method="lm") +
  labs(y="Temperature")

p4 = ggplot(data, aes(x=Wind, y=Temp.C, color=Month)) +
  geom_point(show.legend=FALSE) +
  geom_smooth(method="lm", se=FALSE, show.legend=FALSE) +
  facet_wrap(~ Month, nrow=1) +
  labs(y="Temperature")

p1234 = cowplot::plot_grid(p1, p2, p3, p4, ncol=2, labels="AUTO")
`geom_smooth()` using formula = 'y ~ x'
`geom_smooth()` using formula = 'y ~ x'
p1234

图形文件保存

【实践4】ggsave():图形文件保存

ggsave():保存ggplot对象到文件

  • file:可以是.png.jpg.pdf等文件格式
    • PDF格式是矢量图,无限放大依然清晰
  • width:宽度(英寸inch)
  • height:高度(英寸inch)
  • dpi(dots per inch):每英寸像素点数
ggsave(p1234, file="Fig1.png", width=8, height=6, dpi=300)
# 分辨率:2400 * 1800(比例合适,分辨率高)

ggsave(p1234, file="Fig2.png", width=4, height=3, dpi=300)
# 分辨率:1200 * 900(比例不合适,虽然分辨率高)

ggsave(p1234, file="Fig3.png", width=8, height=6, dpi=100)
# 分辨率:800 * 600(比例合适,但分辨率低)

ggsave(p1234, file="Fig4.png", width=24, height=18, dpi=100)
# 分辨率:2400 * 1800(比例不合适,分辨率也低)

【作业10】多图组合练习

作业要求:

  • 使用cowplot::plot_grid()函数,把【作业9】完成的4张图拼合在一起,添加A~D标签,最后保存为合适尺寸的高清晰度图形文件(.png格式,清晰度dpi至少为300)
  • 使用R Markdown完成,对关键代码及结果要有注释说明

平台提交:

  • 运行得到的HTML网页,及保存的PNG图形文件(直接上传/粘贴,不用压缩)
LS0tDQp0aXRsZTogIuOAilLor63oqIDjgIvnrKwxMeeroO+8muWkmuWbvue7hOWQiCINCnN1YnRpdGxlOiA8YSBocmVmPSJodHRwczovL3BzeWNoYnJ1Y2UuZ2l0aHViLmlvL1JDb3Vyc2UvIj7ov5Tlm57or77nqIvkuLvpobU8L2E+DQphdXRob3I6ICLmjojor77mlZnluIjvvJrljIXlr5LlkLTpnJwiDQojIGRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDMNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBhbmNob3Jfc2VjdGlvbnM6IHRydWUNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgY3NzOiBSbWRDU1MuY3NzDQotLS0NCg0KYGBgez1odG1sfQ0KPHAgc3R5bGU9ImZvbnQtc2l6ZTogMTJweCI+54mI5p2D5aOw5piO77ya5pys5aWX6K++56iL5p2Q5paZ5byA5rqQ77yM5L2/55So5ZKM5YiG5Lqr5b+F6aG76YG15a6I44CM5Yib5L2c5YWx55So6K645Y+v5Y2P6K6uIENDIEJZLU5DLVNB44CN77yI5p2l5rqQ5byV55SoLemdnuWVhuS4mueUqOmAlOS9v+eUqC3ku6Xnm7jlkIzmlrnlvI/lhbHkuqvvvInjgII8aW1nIHNyYz0iaW1nL0NDLUJZLU5DLVNBLmpwZyIgd2lkdGg9IjEyMHB4IiBoZWlnaHQ9IjQycHgiIHN0eWxlPSJmbG9hdDogcmlnaHQiIC8+PC9wPg0KYGBgDQoNCmBgYHtyIENvbmZpZywgaW5jbHVkZT1GQUxTRX0NCm9wdGlvbnMoDQogIGtuaXRyLmthYmxlLk5BID0gIiIsDQogIGRpZ2l0cyA9IDQNCikNCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgY29tbWVudCA9ICIiLA0KICBmaWcud2lkdGggPSA2LA0KICBmaWcuaGVpZ2h0ID0gNCwNCiAgZHBpID0gMzAwDQopDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgQ2hhcDEx77ya5aSa5Zu+57uE5ZCIDQoNCiMjIyMg5b6A5pyf6KaB54K55Zue6aG+DQoNCi0gICBbQ2hhcDEwIFwjIOe7mOWItuWPmOmHj+WIhuW4g10oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAxMCMlRTclQkIlOTglRTUlODglQjYlRTUlOEYlOTglRTklODclOEYlRTUlODglODYlRTUlQjglODMpey51cml977yI55u05pa55Zu+77yJDQotICAgW0NoYXAxMCBcIyDnu5jliLblj5jph4/lpKflsI9dKGh0dHBzOi8vcHN5Y2hicnVjZS5naXRodWIuaW8vUkNvdXJzZS9DaGFwMTAjJUU3JUJCJTk4JUU1JTg4JUI2JUU1JThGJTk4JUU5JTg3JThGJUU1JUE0JUE3JUU1JUIwJThGKXsudXJpfe+8iOafseW9ouWbvu+8iQ0KLSAgIFtDaGFwMTAgXCMg57uY5Yi25Y+Y6YeP5YWz57O7XShodHRwczovL3BzeWNoYnJ1Y2UuZ2l0aHViLmlvL1JDb3Vyc2UvQ2hhcDEwIyVFNyVCQiU5OCVFNSU4OCVCNiVFNSU4RiU5OCVFOSU4NyU4RiVFNSU4NSVCMyVFNyVCMyVCQil7LnVyaX3vvIjmlaPngrnlm77vvIkNCi0gICBbQ2hhcDEwIFwjIOe7mOWItuWPmOmHj+i2i+WKv10oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAxMCMlRTclQkIlOTglRTUlODglQjYlRTUlOEYlOTglRTklODclOEYlRTglQjYlOEIlRTUlOEElQkYpey51cml977yI5oqY57q/5Zu+77yJDQoNCiMjIyMg5pys56ug6KaB54K555uu5b2VDQoNCi0gICBb44CQ5a6e6Le1MeOAkWZhY2V0X3dyYXAoKe+8muaLhuino+WIhumdouWwj+Wbvl0oI+Wunui3tTFmYWNldF93cmFw5ouG6Kej5YiG6Z2i5bCP5Zu+KQ0KLSAgIFvjgJDlrp7ot7Uy44CRZmFjZXRfZ3JpZCgp77ya5Lik5Liq5YiG57G75Y+Y6YeP55qE5Lqk5Y+J57uE5ZCI5YiG6Z2iXSgj5a6e6Le1MmZhY2V0X2dyaWTkuKTkuKrliIbnsbvlj5jph4/nmoTkuqTlj4nnu4TlkIjliIbpnaIpDQotICAgW+OAkOWunui3tTPjgJFjb3dwbG90OjpwbG90X2dyaWQoKe+8muWkmuWbvue7hOWQiF0oI+Wunui3tTNjb3dwbG90cGxvdF9ncmlk5aSa5Zu+57uE5ZCIKe+8iOmHjeeCue+8iQ0KLSAgIFvjgJDlrp7ot7U044CRZ2dzYXZlKCnvvJrlm77lvaLmlofku7bkv53lrZhdKCPlrp7ot7U0Z2dzYXZl5Zu+5b2i5paH5Lu25L+d5a2YKe+8iOmHjeeCue+8iQ0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIOacrOeroOaJgOmcgFLljIUNCmxpYnJhcnkoYnJ1Y2VSKQ0KIyBsaWJyYXJ5KGdncGxvdDIpICAjIOWKoOi9vWJydWNlUuaXtuW3sum7mOiupOWKoOi9vWdncGxvdDINCiMgbGlicmFyeShjb3dwbG90KSAgIyDliqDovb1icnVjZVLml7blt7Lpu5jorqTliqDovb1jb3dwbG90DQpgYGANCg0KIyDliIbpnaLlsI/lm74NCg0KIyMjIyDjgJDlrp7ot7Ux44CRZmFjZXRfd3JhcCgp77ya5ouG6Kej5YiG6Z2i5bCP5Zu+IHsj5a6e6Le1MWZhY2V0X3dyYXDmi4bop6PliIbpnaLlsI/lm759DQoNCmBgYHtyfQ0KIyMg5pWw5o2u5YeG5aSHDQpkYXRhID0gYWlycXVhbGl0eQ0KZGF0YSRNb250aCA9IGFzLmZhY3RvcihkYXRhJE1vbnRoKQ0KZGF0YSRUZW1wLkMgPSAoZGF0YSRUZW1wIC0gMzIpIC8gMS44ICAjIOaRhOawj+W6piA9ICjljY7msI/luqYgLSAzMikgLyAxLjgNCnN0cihkYXRhKQ0KDQojIyDln7rnoYDmlaPngrnlm74NCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludCgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgpKSArDQogIGdlb21fcG9pbnQoKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBmYWNldF93cmFwKH4gTW9udGgpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGgpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X3dyYXAofiBNb250aCwgbmNvbD01KQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoKSkgKw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kPUZBTFNFKSArDQogIGZhY2V0X3dyYXAofiBNb250aCwgbnJvdz0xKQ0KDQpnZ3Bsb3QoZGF0YSwgYWVzKHg9V2luZCwgeT1UZW1wLkMsIGNvbG9yPU1vbnRoKSkgKw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kPUZBTFNFKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZT1GQUxTRSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+IE1vbnRoLCBucm93PTEpDQoNCiMjIOS/ruaUueWboOWtkOagh+etvg0KZGF0YSRNb250aHMgPSBmYWN0b3IoDQogIGRhdGEkTW9udGgsDQogIGxldmVscyA9IDU6OSwNCiAgbGFiZWxzID0gYygiTWF5IiwgIkp1bmUiLCAiSnVseSIsICJBdWd1c3QiLCAiU2VwdGVtYmVyIikNCikNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGhzKSkgKw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kPUZBTFNFKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzaG93LmxlZ2VuZD1GQUxTRSkgKw0KICBmYWNldF93cmFwKH4gTW9udGhzLCBucm93PTEpDQoNCmdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQywgY29sb3I9TW9udGhzKSkgKw0KICBnZW9tX3BvaW50KHNob3cubGVnZW5kPUZBTFNFKSArDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzaG93LmxlZ2VuZD1GQUxTRSkgKw0KICBmYWNldF93cmFwKH4gTW9udGhzLCBucm93PTEsIHNjYWxlcz0iZnJlZSIpDQpgYGANCg0KIyMjIyDjgJDlrp7ot7Uy44CRZmFjZXRfZ3JpZCgp77ya5Lik5Liq5YiG57G75Y+Y6YeP55qE5Lqk5Y+J57uE5ZCI5YiG6Z2iIHsj5a6e6Le1MmZhY2V0X2dyaWTkuKTkuKrliIbnsbvlj5jph4/nmoTkuqTlj4nnu4TlkIjliIbpnaJ9DQoNCi0gICBgZmFjZXRfd3JhcCgpYO+8mue6v+aAp+aOkuWIl++8iOWNt+mlvPCfjK/vvIkNCiAgICAtICAgYH4g5YiG57uE5Y+Y6YePYA0KLSAgIGBmYWNldF9ncmlkKClg77ya5Lik5Zug57Sg5Lqk5Y+J57uE5ZCI5o6S5YiX77yI5Y2O5aSr6aW88J+nh++8iQ0KICAgIC0gICBg6KGM5YiG57uE5Y+Y6YePIH4g5YiX5YiG57uE5Y+Y6YePYA0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0xNTA1MzcyMzE1LnBuZykNCg0KYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9DQojIyDkuKTkuKrliIbnsbvlj5jph4/nmoTmg4XlhrUNCiMjIOaVsOaNruWPmOmHj+iuoeeul+WkjeS5oO+8iENoYXAgMDUgJiAwNu+8iQ0KZGF0YS5iZmkgPSBhcy5kYXRhLnRhYmxlKHBzeWNoOjpiZmkpDQpkYXRhLmJmaVssIGxldCgNCiAgR2VuZGVyID0gZmFjdG9yKGdlbmRlciwgbGV2ZWxzPTE6MiwgbGFiZWxzPWMoIk1hbGUiLCAiRmVtYWxlIikpLA0KICBBZ2UgPSBhcy5udW1lcmljKGFnZSksDQogIEVkdSA9IGFzLmZhY3RvcihlZHVjYXRpb24pLA0KICBFID0gTUVBTihkYXRhLmJmaSwgIkUiLCAxOjUsIHJldj1jKDEsMiksIHJhbmdlPTE6NiksDQogIEEgPSBNRUFOKGRhdGEuYmZpLCAiQSIsIDE6NSwgcmV2PTEsIHJhbmdlPTE6NiksDQogIEMgPSBNRUFOKGRhdGEuYmZpLCAiQyIsIDE6NSwgcmV2PWMoNCw1KSwgcmFuZ2U9MTo2KSwNCiAgTiA9IE1FQU4oZGF0YS5iZmksICJOIiwgMTo1LCByZXY9TlVMTCwgcmFuZ2U9MTo2KSwNCiAgTyA9IE1FQU4oZGF0YS5iZmksICJPIiwgMTo1LCByZXY9YygyLDUpLCByYW5nZT0xOjYpDQopXQ0Kc3RyKGRhdGEuYmZpKQ0KDQpwID0gZ2dwbG90KGRhdGEuYmZpW0FnZT49MTggJiBBZ2U8NjAgJiAhaXMubmEoRWR1KV0sDQogICAgICAgICAgIGFlcyh4PUFnZSwgeT1FKSkgKw0KICBnZW9tX3BvaW50KGFscGhhPTAuMSkgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxvZXNzIiwgY29sb3I9ImZpcmVicmljayIsIGZpbGw9Im9yYW5nZSIpICsNCiAgbGFicyh5PSJFeHRyYXZlcnNpb24iKQ0KcA0KDQpwICsgZmFjZXRfd3JhcCh+IEdlbmRlcikNCnAgKyBmYWNldF93cmFwKH4gR2VuZGVyICsgRWR1KSAgIyDlubbkuI3nkIbmg7MNCnAgKyBmYWNldF93cmFwKH4gR2VuZGVyICogRWR1KSAgIyDlubbkuI3nkIbmg7MNCnAgKyBmYWNldF9ncmlkKH4gR2VuZGVyKSAgIyAuIH4g5YiX5YiG57uE5Y+Y6YeP77yILueCueWPr+S7peecgeeVpe+8iQ0KcCArIGZhY2V0X2dyaWQoR2VuZGVyIH4gLikgICMg6KGM5YiG57uE5Y+Y6YePIH4gLu+8iC7ngrnkuI3og73nnIHnlaXvvIkNCnAgKyBmYWNldF9ncmlkKEdlbmRlciB+IEVkdSkgICMg6KGM5Y+Y6YePIH4g5YiX5Y+Y6YePDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEyfQ0KcCArIGZhY2V0X2dyaWQoRWR1IH4gR2VuZGVyKSAgIyDooYzlj5jph48gfiDliJflj5jph48NCmBgYA0KDQojIOWkmuWbvue7hOWQiA0KDQojIyMjIOOAkOWunui3tTPjgJFjb3dwbG90OjpwbG90X2dyaWQoKe+8muWkmuWbvue7hOWQiCB7I+Wunui3tTNjb3dwbG90cGxvdF9ncmlk5aSa5Zu+57uE5ZCIfQ0KDQpgYGB7cn0NCnAxID0gZ2dwbG90KGRhdGEsIGFlcyh4PVRlbXAuQykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucz0xMCwgY29sb3I9ImJsYWNrIiwgZmlsbD0iZ3JleSIpICsNCiAgbGFicyh4PSJUZW1wZXJhdHVyZSIsIHk9IkZyZXF1ZW5jeSIpDQoNCnAyID0gZ2dwbG90KGRhdGEsIGFlcyh4PU1vbnRoLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHk9IlRlbXBlcmF0dXJlIikNCg0KcDEyID0gY293cGxvdDo6cGxvdF9ncmlkKHAxLCBwMiwgbnJvdz0xLCBsYWJlbHM9IkFVVE8iKQ0KcDEyDQpgYGANCg0KYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTZ9DQpwMyA9IGdncGxvdChkYXRhLCBhZXMoeD1XaW5kLCB5PVRlbXAuQykpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsNCiAgbGFicyh5PSJUZW1wZXJhdHVyZSIpDQoNCnA0ID0gZ2dwbG90KGRhdGEsIGFlcyh4PVdpbmQsIHk9VGVtcC5DLCBjb2xvcj1Nb250aCkpICsNCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZD1GQUxTRSkgKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2U9RkFMU0UsIHNob3cubGVnZW5kPUZBTFNFKSArDQogIGZhY2V0X3dyYXAofiBNb250aCwgbnJvdz0xKSArDQogIGxhYnMoeT0iVGVtcGVyYXR1cmUiKQ0KDQpwMTIzNCA9IGNvd3Bsb3Q6OnBsb3RfZ3JpZChwMSwgcDIsIHAzLCBwNCwgbmNvbD0yLCBsYWJlbHM9IkFVVE8iKQ0KcDEyMzQNCmBgYA0KDQojIOWbvuW9ouaWh+S7tuS/neWtmA0KDQojIyMjIOOAkOWunui3tTTjgJFnZ3NhdmUoKe+8muWbvuW9ouaWh+S7tuS/neWtmCB7I+Wunui3tTRnZ3NhdmXlm77lvaLmlofku7bkv53lrZh9DQoNCmBnZ3NhdmUoKWDvvJrkv53lrZhgZ2dwbG90YOWvueixoeWIsOaWh+S7tg0KDQotICAgYGZpbGVg77ya5Y+v5Lul5pivYC5wbmdg44CBYC5qcGdg44CBYC5wZGZg562J5paH5Lu25qC85byPDQogICAgLSAgIFBERuagvOW8j+aYr+efoumHj+Wbvu+8jOaXoOmZkOaUvuWkp+S+neeEtua4heaZsA0KLSAgIGB3aWR0aGDvvJrlrr3luqbvvIjoi7Hlr7hpbmNo77yJDQotICAgYGhlaWdodGDvvJrpq5jluqbvvIjoi7Hlr7hpbmNo77yJDQotICAgYGRwaWDvvIhkb3RzIHBlciBpbmNo77yJ77ya5q+P6Iux5a+45YOP57Sg54K55pWwDQoNCmBgYHtyfQ0KZ2dzYXZlKHAxMjM0LCBmaWxlPSJGaWcxLnBuZyIsIHdpZHRoPTgsIGhlaWdodD02LCBkcGk9MzAwKQ0KIyDliIbovqjnjofvvJoyNDAwICogMTgwMO+8iOavlOS+i+WQiOmAgu+8jOWIhui+qOeOh+mrmO+8iQ0KYGBgDQoNCiFbXShpbWcvRmlnMS5wbmcpDQoNCmBgYHtyfQ0KZ2dzYXZlKHAxMjM0LCBmaWxlPSJGaWcyLnBuZyIsIHdpZHRoPTQsIGhlaWdodD0zLCBkcGk9MzAwKQ0KIyDliIbovqjnjofvvJoxMjAwICogOTAw77yI5q+U5L6L5LiN5ZCI6YCC77yM6Jm954S25YiG6L6o546H6auY77yJDQpgYGANCg0KIVtdKGltZy9GaWcyLnBuZykNCg0KYGBge3J9DQpnZ3NhdmUocDEyMzQsIGZpbGU9IkZpZzMucG5nIiwgd2lkdGg9OCwgaGVpZ2h0PTYsIGRwaT0xMDApDQojIOWIhui+qOeOh++8mjgwMCAqIDYwMO+8iOavlOS+i+WQiOmAgu+8jOS9huWIhui+qOeOh+S9ju+8iQ0KYGBgDQoNCiFbXShpbWcvRmlnMy5wbmcpDQoNCmBgYHtyfQ0KZ2dzYXZlKHAxMjM0LCBmaWxlPSJGaWc0LnBuZyIsIHdpZHRoPTI0LCBoZWlnaHQ9MTgsIGRwaT0xMDApDQojIOWIhui+qOeOh++8mjI0MDAgKiAxODAw77yI5q+U5L6L5LiN5ZCI6YCC77yM5YiG6L6o546H5Lmf5L2O77yJDQpgYGANCg0KIVtdKGltZy9GaWc0LnBuZykNCg0KIyDjgJDkvZzkuJoxMOOAkeWkmuWbvue7hOWQiOe7g+S5oA0KDQrkvZzkuJropoHmsYLvvJoNCg0KLSAgIOS9v+eUqGBjb3dwbG90OjpwbG90X2dyaWQoKWDlh73mlbDvvIzmiopb44CQ5L2c5LiaOeOAkV0oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAxMCMlRTQlQkQlOUMlRTQlQjglOUE5JUU1JTlGJUJBJUU3JUExJTgwJUU3JUJCJTk4JUU1JTlCJUJFJUU3JUJCJTgzJUU0JUI5JUEwKXsudXJpfeWujOaIkOeahDTlvKDlm77mi7zlkIjlnKjkuIDotbfvvIzmt7vliqBBXH5E5qCH562+77yM5pyA5ZCO5L+d5a2Y5Li65ZCI6YCC5bC65a+455qE6auY5riF5pmw5bqm5Zu+5b2i5paH5Lu277yIYC5wbmdg5qC85byP77yM5riF5pmw5bqmYGRwaWDoh7PlsJHkuLozMDDvvIkNCi0gICDkvb/nlKhSIE1hcmtkb3du5a6M5oiQ77yM5a+55YWz6ZSu5Luj56CB5Y+K57uT5p6c6KaB5pyJ5rOo6YeK6K+05piODQoNCuW5s+WPsOaPkOS6pO+8mg0KDQotICAg6L+Q6KGM5b6X5Yiw55qESFRNTOe9kemhte+8jOWPiuS/neWtmOeahFBOR+WbvuW9ouaWh+S7tu+8iOebtOaOpeS4iuS8oC/nspjotLTvvIzkuI3nlKjljovnvKnvvIkNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQp1bmxpbmsoYygiRmlnMS5wbmciLCAiRmlnMi5wbmciLCAiRmlnMy5wbmciLCAiRmlnNC5wbmciKSkNCmBgYA0K