ggplot2 cheatsheet.pdf
Labs(绘图区外部的各种元素的标题)
labs(..., title = , subtitle = , caption = , tag = )
图形标题、副标题和说明
title, 主标题。使用标题的目的是概括主要成果。尽量不要使用那些只对图形进行描述的标题,如”发动机排量与燃油效率散点图”。
subtitle, 副标题。在标题下以更小的字体添加更多附加信息。
caption, 说明,在图形下方添加文本,常用于描述数据来源。
ggplot (mpg, aes (displ, hwy)) +
geom_point (aes (color = class)) +
geom_smooth (se = FALSE ) +
labs (
title = paste ("Fuel efficiency generally decreases with" , "engine size" ),
subtitle = paste (
"Two seaters (sports cars) are an exception" ,
"because of their light weight"
),
caption = "Data from fueleconomy.gov"
)
坐标轴和图例的标题
简短的变量名称加单位
# 气泡图
ggplot (mtcars, aes (x = wt, y = mpg, size = disp)) +
# 将disp变量映射为图形的size属性,本例中即为散点的大小。同时生成size图例
geom_point (shape = 21 , color = "black" , fill = "cornsilk" ) +
# shape=21表示有框圆形(不同于单一颜色的小圆点)
labs (
x = "Weight (1000 lbs)" , y = "Miles/(US) gallon" ,
title = "Bubble Chart" ,
size = "Engine \n Displacement (cu.in.)"
# size意为对图例添加标签
)
使用数学公式代替字符串文本
quote()
使用?plotmath命令查看可用选项
df <- tibble (
x = runif (10 ),
y = runif (10 )
)
ggplot (df, aes (x, y)) +
geom_point () +
labs (
x = quote (sum (x[i]^ 2 , i == 1 , n)),
y = quote (alpha + beta + frac (delta, theta))
)
绘图区内部的文本标签和说明
geom_label()
和ggrepel::geom_label_repel()
在数据旁添加文本标签
geom_text()
和ggrepel::geom_text_repel()
在图内添加文本说明
数据点旁的标签
# 先选取出每类汽车中效率最高的型号,然后在图形中标记出来
best_in_class <- mpg %>%
group_by (class) %>%
filter (row_number (desc (hwy)) == 1 ) # desc()排序
ggplot (mpg, aes (displ, hwy)) + # 全局映射和全局数据
geom_point (aes (color = class)) +
geom_text (aes (label = model), data = best_in_class) # 用局部数据,将model变量映射为文本标签
ggplot (mpg, aes (displ, hwy)) +
geom_point (aes (color = class)) +
geom_label (aes (label = model),
data = best_in_class,
nudge_y = 2 , alpha = 0.5
) # nudge_y参数可以调整标签相对于数据点的位置
# ggrepel包可以自动调整标签的位置,使它们免于重叠
ggplot (mpg, aes (displ, hwy)) +
geom_point (aes (color = class)) +
geom_point (size = 3 , shape = 1 , data = best_in_class) +
# 添加了一个图层,用较大的空心圆来强调添加了标签的数据点
ggrepel:: geom_label_repel (aes (label = model), data = best_in_class)
# 将标签直接放在图形上,以替代图例
# 分组,对每组取数据分布的中位数,在该处放置组名
class_avg <- mpg %>%
group_by (class) %>%
summarize (displ = median (displ), hwy = median (hwy))
ggplot (mpg, aes (displ, hwy, color = class)) +
ggrepel:: geom_label_repel (
aes (label = class),
data = class_avg, size = 6 ,
label.size = 0 , segment.color = NA
) +
geom_point () +
theme (legend.position = "none" ) # 不显示图例
图中空白处的说明
创建一个数据点,位置在空白处,借这个数据添加标签
# 创建只有一个观测的数据框,用以保存标签的坐标
label <- mpg %>%
summarize (
displ = max (displ), hwy = max (hwy),
label = paste (
"Increasing engine size is \n related to" ,
"decreasing fuel economy."
)
)
ggplot (mpg, aes (displ, hwy)) +
geom_point () +
geom_text (
aes (label = label),
data = label,
vjust = "top" , hjust = "right"
)
# 如果想让标签紧贴着图形的边界,可以使用+Inf和-Inf值(因为坐标轴的实际范围要比数据范围大一些)
label <- tibble (
displ = Inf , hwy = Inf ,
label = paste (
"Increasing engine size is \n related to" ,
"decreasing fuel economy."
)
)
ggplot (mpg, aes (displ, hwy)) +
geom_point () +
geom_text (aes (label = label), data = label, vjust = "top" , hjust = "right" )
# 自动为标签断行的方法
"Increasing engine size related to decreasing fuel economy." %>%
stringr:: str_wrap (width = 40 ) %>%
writeLines ()
#> Increasing engine size related to
#> decreasing fuel economy.
vjust和hjust参数
设置标签相对于坐标的对齐方式
参考线、箭头和方框
可以使用geom_hline(yintercept = 0)
和geom_vline(xintercept = 0)
添加参考线。我们经常使用加粗(size = 2)和白色(color = white)的直线作为参考线,并将它们绘制在基本数据层的下面。这样的参考线既清晰可见,又不至于喧宾夺主,影响我们查看数据。
可以使用geom_rect()
在我们感兴趣的数据点周围绘制一个矩形。矩形的边界由图形属性 xmin、 xmax、 ymin 和 ymax 确定。
可以使用geom_segment()
及arrow
参数绘制箭头,指向需要关注的数据点。使用 x 和 y 属性来定义开始位置,使用 xend 和 yend 属性来定义结束位置。
geom_segment (
x = 6 , y = 4 , xend = 3 , yend = 1 , colour = "blue" ,
arrow = arrow (angle = 30 , length = unit (0.5 , "cm" ), type = "open" )
)
# angle为箭头所张角度,type决定箭头是空心还是实心
图例 Legends
guides()
要想控制单个图例的显示,可以配合guide_legend()(负责离散型图例)或guide_colorbar()(负责连续型图例)来使用guides()
ggplot (mpg, aes (displ, hwy)) +
geom_point (aes (color = class)) +
geom_smooth (se = FALSE ) +
theme (legend.position = "bottom" ) +
guides (color = guide_legend (nrow = 1 , override.aes = list (size = 4 )))
# nrow使图例排列为1行,size = 4使图例中的数据点更大一些
图例的位置
theme()中的legend.position参数可以控制图例的整体位置:
base <- ggplot (mpg, aes (displ, hwy)) +
geom_point (aes (color = class))
base + theme (legend.position = "left" )
base + theme (legend.position = "top" )
base + theme (legend.position = "bottom" )
base + theme (legend.position = "right" ) # 默认设置
base + theme (legend.position = "none" )
也可以指定一个二元素向量,表示图例的中心相对于整张图左下角的距离(都用比例表示)
data (Salaries, package = "car" )
Salaries %>%
ggplot (aes (x = rank, y = salary, fill = sex)) +
geom_boxplot () +
scale_x_discrete (
breaks = c ("AsstProf" , "AssocProf" , "Prof" ),
labels = c (
"Assistant \n Professor" ,
"Associate \n Professor" ,
"Full \n Professor"
)
) +
scale_y_continuous (
breaks = c (50000 , 100000 , 150000 , 200000 ),
labels = c ("$50K" , "$100K" , "$150K" , "$200K" )
) +
labs (
title = "Faculty Salary by Rank and Gender" ,
x = "" , y = "" , fill = "Gender"
) +
theme (legend.position = c (0.1 , 0.8 ))
# 图例中心相对于整张图左下角的距离,0.1和0.8都是相对于整个画面的比例
主题
theme()可以定制图形中的非数据元素,使图表有自己的风格,在做图表时不考虑格式,最后一次性应用主题。
ggplot2内置的八种主题
默认主题使用灰色背景,这样可以在网格线可见的情况下更加突出数据。白色网格线既是可见的(这非常重要,因为它们非常有助于位置判定),又对视觉没有什么严重影响,我们完全可以对其视而不见。图表的灰色背景不像白色背景那么突兀,与文本印刷颜色非常相近,保证了图形与文档其他部分浑然一体。最后,灰色背景可以创建一片连续的颜色区域,使图形成为形象鲜明的一个独立视觉实体。
自定义主题
所有可定义的参数可查询ggplot2::theme()
的帮助文档
plot.title
图标题
plot.caption
图标注(一般是资料来源)
axis.title
轴标题
axis.text
轴须标签
panel.background
画图区域(fill填充,color边框)
panel.grid.major.y
y轴(水平)主要网格线
panel.grid.minor.y
y轴(水平)次要网格线
panel.grid.major.x
x轴(竖直)主要网格线
panel.grid.minor.x
x轴(竖直)次要网格线
legend.position
图例的位置
legend.title
图例的标题
legend.text
图例各项的文字
data (Salaries, package = "car" )
# 定义自己的主题mytheme
mytheme <-
theme (
plot.title = element_text (
face = "bold.italic" ,
size = 14 , color = "brown"
),
axis.title = element_text (
face = "bold.italic" , size = 10 ,
color = "brown"
),
axis.text = element_text (
face = "bold" , size = 9 ,
color = "darkblue"
),
panel.background = element_rect (
fill = "white" ,
color = "darkblue"
),
panel.grid.major.y = element_line (color = "grey" , linetype = 1 ),
panel.grid.minor.y = element_line (color = "grey" , linetype = 2 ),
panel.grid.major.x = element_blank (),
panel.grid.minor.x = element_blank (),
legend.position = "top"
)
# 在画图时,应用自己的主题mytheme
ggplot (Salaries, aes (x = rank, y = salary, fill = sex)) +
geom_boxplot () +
labs (title = "Salary by Rank and Sex" , x = "Rank" , y = "Salary" ) +
mytheme
套用现成主题:ggthemes包
ggplot (Salaries, aes (x = rank, y = salary, fill = sex)) +
geom_boxplot () +
labs (title = "Salary by Rank and Sex" , x = "Rank" , y = "Salary" ) +
theme_wsj () +
scale_fill_wsj ()
ggplot (Salaries, aes (x = rank, y = salary, fill = sex)) +
geom_boxplot () +
labs (title = "Salary by Rank and Sex" , x = "Rank" , y = "Salary" ) +
theme_wsj () +
scale_fill_wsj ("rgby" , "" ) +
theme (axis.ticks.length = unit (0.5 , "cm" )) +
guides (fill = guide_legend (title = NULL ))
ggplot (Salaries, aes (x = rank, y = salary, fill = sex)) +
geom_boxplot () +
labs (title = "Salary by Rank and Sex" , x = "Rank" , y = "Salary" ) +
theme_economist (base_size = 14 ) +
scale_fill_economist () +
theme (axis.ticks.length = unit (0.5 , "cm" )) +
guides (fill = guide_legend (title = NULL ))
多图拼接
ggplot2包认为它提供的刻面功能已经足够了。为了组合ggplot图形,需要使用其他包。
patchwork 包
(p1/p2)|p3
表示p1和p2居左,分别上下;p3居右,合并成一幅图
LS0tDQp0aXRsZTogImdncGxvdDItMiINCnN1YnRpdGxlOiAn5qC35byP576O5YyWJw0KYXV0aG9yOiAiSHVtb29uIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIGNzczogLi4vY3NzL3N0eWxlLmNzcw0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICB0aGVtZTogdW5pdGVkDQogICAgaGlnaGxpZ2h0OiBoYWRkb2NrDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiB5ZXMNCiAgICAgIHNtb290aF9zY3JvbGw6IHllcw0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQpzb3VyY2UoIi4uL1JtYXJrZG93bi10ZW1wbGF0ZS9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywgDQogIGV2YWwgPSBjb25maWckZXZhbCwgDQogIHRpZHkgPSBjb25maWckdGlkeSwgDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwgDQogIGNvbGxhcHNlID0gY29uZmlnJGNvbGxhcHNlLCANCiAgY2FjaGUgPSBjb25maWckY2FjaGUsDQogIGNhY2hlLmNvbW1lbnRzID0gY29uZmlnJGNhY2hlLmNvbW1lbnRzLA0KICBhdXRvZGVwID0gY29uZmlnJGF1dG9kZXANCikNCg0KIyMgdXNlIG5lY2Vzc2FyeSBwYWNrYWdlcyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShodG1sd2lkZ2V0cykNCg0KcGFjbWFuOjpwX2xvYWQoDQogIGx1YnJpZGF0ZSwNCiAgc2hvd3RleHQsDQogIERULA0KICBqc29ubGl0ZSwNCiAgcmV0aWN1bGF0ZSwNCiAgY2FyLCANCiAgcmVhZHhsLCANCiAgcmVzaGFwZTIsIA0KICBSQ29sb3JCcmV3ZXINCikNCg0KZGF0YShjYXI6OlNhbGFyaWVzKQ0KDQojIyBwbG90dGluZyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KIyDljIXlkKvlm77nmoTku6PnoIHlnZfpnIDopoFmaWcuc2hvd3RleHQgPSBUUlVF6YCJ6aG5DQpzaG93dGV4dF9hdXRvKGVuYWJsZSA9IFRSVUUpDQp3aW5kb3dzRm9udHMoSCA9IHdpbmRvd3NGb250KCJNaWNyb3NvZnQgWWFIZWkiKSkNCmBgYA0KDQo8YSBocmVmPSIuLi9wZGYvY2hlYXRzaGVldC1nZ3Bsb3QucGRmIj4qZ2dwbG90MiBjaGVhdHNoZWV0LnBkZio8L2E+DQoNCjxvYmplY3QgZGF0YT0iLi4vcGRmL2NoZWF0c2hlZXQtZ2dwbG90LnBkZiIgdHlwZT0iYXBwbGljYXRpb24vcGRmIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIj48L29iamVjdD4NCg0KIyMgTGFicyjnu5jlm77ljLrlpJbpg6jnmoTlkITnp43lhYPntKDnmoTmoIfpopgpDQoNCmBsYWJzKC4uLiwgdGl0bGUgPSAsIHN1YnRpdGxlID0gLCBjYXB0aW9uID0gLCB0YWcgPSApYA0KDQojIyMg5Zu+5b2i5qCH6aKY44CB5Ymv5qCH6aKY5ZKM6K+05piODQoNCnRpdGxlLCDkuLvmoIfpopjjgILkvb/nlKjmoIfpopjnmoTnm67nmoTmmK/mpoLmi6zkuLvopoHmiJDmnpzjgILlsL3ph4/kuI3opoHkvb/nlKjpgqPkupvlj6rlr7nlm77lvaLov5vooYzmj4/ov7DnmoTmoIfpopjvvIzlpoIi5Y+R5Yqo5py65o6S6YeP5LiO54eD5rK55pWI546H5pWj54K55Zu+IuOAgg0KDQpzdWJ0aXRsZSwg5Ymv5qCH6aKY44CC5Zyo5qCH6aKY5LiL5Lul5pu05bCP55qE5a2X5L2T5re75Yqg5pu05aSa6ZmE5Yqg5L+h5oGv44CCDQoNCmNhcHRpb24sIOivtOaYju+8jOWcqOWbvuW9ouS4i+aWuea3u+WKoOaWh+acrO+8jOW4uOeUqOS6juaPj+i/sOaVsOaNruadpea6kOOAgg0KDQpgYGB7cn0NCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKw0KICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSBwYXN0ZSgiRnVlbCBlZmZpY2llbmN5IGdlbmVyYWxseSBkZWNyZWFzZXMgd2l0aCIsICJlbmdpbmUgc2l6ZSIpLA0KICAgIHN1YnRpdGxlID0gcGFzdGUoDQogICAgICAiVHdvIHNlYXRlcnMgKHNwb3J0cyBjYXJzKSBhcmUgYW4gZXhjZXB0aW9uIiwNCiAgICAgICJiZWNhdXNlIG9mIHRoZWlyIGxpZ2h0IHdlaWdodCINCiAgICApLA0KICAgIGNhcHRpb24gPSAiRGF0YSBmcm9tIGZ1ZWxlY29ub215LmdvdiINCiAgKQ0KYGBgDQoNCiMjIyDlnZDmoIfovbTlkozlm77kvovnmoTmoIfpopgNCg0K566A55+t55qE5Y+Y6YeP5ZCN56ew5Yqg5Y2V5L2NDQoNCmBgYHtyLCBmaWcuc2hvdz0nYXNpcyd9DQojIOawlOazoeWbvg0KZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwgeSA9IG1wZywgc2l6ZSA9IGRpc3ApKSArDQogICMg5bCGZGlzcOWPmOmHj+aYoOWwhOS4uuWbvuW9oueahHNpemXlsZ7mgKfvvIzmnKzkvovkuK3ljbPkuLrmlaPngrnnmoTlpKflsI/jgILlkIzml7bnlJ/miJBzaXpl5Zu+5L6LDQogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImNvcm5zaWxrIikgKw0KICAjIHNoYXBlPTIx6KGo56S65pyJ5qGG5ZyG5b2i77yI5LiN5ZCM5LqO5Y2V5LiA6aKc6Imy55qE5bCP5ZyG54K577yJDQogIGxhYnMoDQogICAgeCA9ICJXZWlnaHQgKDEwMDAgbGJzKSIsIHkgPSAiTWlsZXMvKFVTKSBnYWxsb24iLA0KICAgIHRpdGxlID0gIkJ1YmJsZSBDaGFydCIsDQogICAgc2l6ZSA9ICJFbmdpbmVcbkRpc3BsYWNlbWVudCAoY3UuaW4uKSINCiAgICAjIHNpemXmhI/kuLrlr7nlm77kvovmt7vliqDmoIfnrb4NCiAgKQ0KYGBgDQoNCiMjIyDkvb/nlKjmlbDlrablhazlvI/ku6Pmm7/lrZfnrKbkuLLmlofmnKwNCg0KYHF1b3RlKClgDQoNCuS9v+eUqD9wbG90bWF0aOWRveS7pOafpeeci+WPr+eUqOmAiemhuQ0KDQpgYGB7cn0NCmRmIDwtIHRpYmJsZSgNCiAgeCA9IHJ1bmlmKDEwKSwNCiAgeSA9IHJ1bmlmKDEwKQ0KKQ0KDQpnZ3Bsb3QoZGYsIGFlcyh4LCB5KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKA0KICAgIHggPSBxdW90ZShzdW0oeFtpXV4yLCBpID09IDEsIG4pKSwNCiAgICB5ID0gcXVvdGUoYWxwaGEgKyBiZXRhICsgZnJhYyhkZWx0YSwgdGhldGEpKQ0KICApDQpgYGANCg0KIyMg57uY5Zu+5Yy65YaF6YOo55qE5paH5pys5qCH562+5ZKM6K+05piODQoNCmBnZW9tX2xhYmVsKClg5ZKMYGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoKWDlnKjmlbDmja7ml4Hmt7vliqDmlofmnKzmoIfnrb4NCg0KYGdlb21fdGV4dCgpYOWSjGBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoKWDlnKjlm77lhoXmt7vliqDmlofmnKzor7TmmI4NCg0KIyMjIOaVsOaNrueCueaXgeeahOagh+etvg0KDQpgYGB7ciwgZmlnLnNob3c9J2FzaXMnfQ0KIyDlhYjpgInlj5blh7rmr4/nsbvmsb3ovabkuK3mlYjnjofmnIDpq5jnmoTlnovlj7fvvIznhLblkI7lnKjlm77lvaLkuK3moIforrDlh7rmnaUNCmJlc3RfaW5fY2xhc3MgPC0gbXBnICU+JQ0KICBncm91cF9ieShjbGFzcykgJT4lDQogIGZpbHRlcihyb3dfbnVtYmVyKGRlc2MoaHd5KSkgPT0gMSkgIyBkZXNjKCnmjpLluo8NCg0KDQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsgIyDlhajlsYDmmKDlsITlkozlhajlsYDmlbDmja4NCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbGFzcykpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG1vZGVsKSwgZGF0YSA9IGJlc3RfaW5fY2xhc3MpICMg55So5bGA6YOo5pWw5o2u77yM5bCGbW9kZWzlj5jph4/mmKDlsITkuLrmlofmnKzmoIfnrb4NCg0KZ2dwbG90KG1wZywgYWVzKGRpc3BsLCBod3kpKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gY2xhc3MpKSArDQogIGdlb21fbGFiZWwoYWVzKGxhYmVsID0gbW9kZWwpLA0KICAgIGRhdGEgPSBiZXN0X2luX2NsYXNzLA0KICAgIG51ZGdlX3kgPSAyLCBhbHBoYSA9IDAuNQ0KICApICMgbnVkZ2VfeeWPguaVsOWPr+S7peiwg+aVtOagh+etvuebuOWvueS6juaVsOaNrueCueeahOS9jee9rg0KDQojIGdncmVwZWzljIXlj6/ku6Xoh6rliqjosIPmlbTmoIfnrb7nmoTkvY3nva7vvIzkvb/lroPku6zlhY3kuo7ph43lj6ANCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNsYXNzKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzLCBzaGFwZSA9IDEsIGRhdGEgPSBiZXN0X2luX2NsYXNzKSArDQogICMg5re75Yqg5LqG5LiA5Liq5Zu+5bGC77yM55So6L6D5aSn55qE56m65b+D5ZyG5p2l5by66LCD5re75Yqg5LqG5qCH562+55qE5pWw5o2u54K5DQogIGdncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsID0gbW9kZWwpLCBkYXRhID0gYmVzdF9pbl9jbGFzcykNCg0KIyDlsIbmoIfnrb7nm7TmjqXmlL7lnKjlm77lvaLkuIrvvIzku6Xmm7/ku6Plm77kvosNCiMg5YiG57uE77yM5a+55q+P57uE5Y+W5pWw5o2u5YiG5biD55qE5Lit5L2N5pWw77yM5Zyo6K+l5aSE5pS+572u57uE5ZCNDQpjbGFzc19hdmcgPC0gbXBnICU+JQ0KICBncm91cF9ieShjbGFzcykgJT4lDQogIHN1bW1hcml6ZShkaXNwbCA9IG1lZGlhbihkaXNwbCksIGh3eSA9IG1lZGlhbihod3kpKQ0KDQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSwgY29sb3IgPSBjbGFzcykpICsNCiAgZ2dyZXBlbDo6Z2VvbV9sYWJlbF9yZXBlbCgNCiAgICBhZXMobGFiZWwgPSBjbGFzcyksDQogICAgZGF0YSA9IGNsYXNzX2F2Zywgc2l6ZSA9IDYsDQogICAgbGFiZWwuc2l6ZSA9IDAsIHNlZ21lbnQuY29sb3IgPSBOQQ0KICApICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAjIOS4jeaYvuekuuWbvuS+iw0KYGBgDQoNCiMjIyDlm77kuK3nqbrnmb3lpITnmoTor7TmmI4NCg0K5Yib5bu65LiA5Liq5pWw5o2u54K577yM5L2N572u5Zyo56m655m95aSE77yM5YCf6L+Z5Liq5pWw5o2u5re75Yqg5qCH562+DQoNCmBgYHtyLCBmaWcuc2hvdz0nYXNpcyd9DQojIOWIm+W7uuWPquacieS4gOS4quingua1i+eahOaVsOaNruahhu+8jOeUqOS7peS/neWtmOagh+etvueahOWdkOaghw0KbGFiZWwgPC0gbXBnICU+JQ0KICBzdW1tYXJpemUoDQogICAgZGlzcGwgPSBtYXgoZGlzcGwpLCBod3kgPSBtYXgoaHd5KSwNCiAgICBsYWJlbCA9IHBhc3RlKA0KICAgICAgIkluY3JlYXNpbmcgZW5naW5lIHNpemUgaXMgXG5yZWxhdGVkIHRvIiwNCiAgICAgICJkZWNyZWFzaW5nIGZ1ZWwgZWNvbm9teS4iDQogICAgKQ0KICApDQoNCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3RleHQoDQogICAgYWVzKGxhYmVsID0gbGFiZWwpLA0KICAgIGRhdGEgPSBsYWJlbCwNCiAgICB2anVzdCA9ICJ0b3AiLCBoanVzdCA9ICJyaWdodCINCiAgKQ0KDQojIOWmguaenOaDs+iuqeagh+etvue0p+i0tOedgOWbvuW9oueahOi+ueeVjO+8jOWPr+S7peS9v+eUqCtJbmblkowtSW5m5YC877yI5Zug5Li65Z2Q5qCH6L2055qE5a6e6ZmF6IyD5Zu06KaB5q+U5pWw5o2u6IyD5Zu05aSn5LiA5Lqb77yJDQpsYWJlbCA8LSB0aWJibGUoDQogIGRpc3BsID0gSW5mLCBod3kgPSBJbmYsDQogIGxhYmVsID0gcGFzdGUoDQogICAgIkluY3JlYXNpbmcgZW5naW5lIHNpemUgaXMgXG5yZWxhdGVkIHRvIiwNCiAgICAiZGVjcmVhc2luZyBmdWVsIGVjb25vbXkuIg0KICApDQopDQoNCmdncGxvdChtcGcsIGFlcyhkaXNwbCwgaHd5KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbGFiZWwpLCBkYXRhID0gbGFiZWwsIHZqdXN0ID0gInRvcCIsIGhqdXN0ID0gInJpZ2h0IikNCg0KIyDoh6rliqjkuLrmoIfnrb7mlq3ooYznmoTmlrnms5UNCiJJbmNyZWFzaW5nIGVuZ2luZSBzaXplIHJlbGF0ZWQgdG8gZGVjcmVhc2luZyBmdWVsIGVjb25vbXkuIiAlPiUNCiAgc3RyaW5ncjo6c3RyX3dyYXAod2lkdGggPSA0MCkgJT4lDQogIHdyaXRlTGluZXMoKQ0KYGBgDQoNCiMjIyB2anVzdOWSjGhqdXN05Y+C5pWwDQoNCuiuvue9ruagh+etvuebuOWvueS6juWdkOagh+eahOWvuem9kOaWueW8jw0KDQohWzIwMjIwMzIzLeagh+etvuebuOWvueS6juWdkOagh+eahOWvuem9kOaWueW8j10oaHR0cDovL2h1bW9vbi1pbWFnZS1ob3N0aW5nLXNlcnZpY2Uub3NzLWNuLWJlaWppbmcuYWxpeXVuY3MuY29tL2ltZy90eXBvcmEvMjAyMi8yMDIyMDMyMy3moIfnrb7nm7jlr7nkuo7lnZDmoIfnmoTlr7npvZDmlrnlvI8ucG5nKQ0KDQojIyDlj4LogIPnur/jgIHnrq3lpLTlkozmlrnmoYYNCg0KLSAgIOWPr+S7peS9v2DnlKhnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwKWDlkoxgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMClg5re75Yqg5Y+C6ICD57q/44CC5oiR5Lus57uP5bi45L2/55So5Yqg57KX77yIc2l6ZSA9IDLvvInlkoznmb3oibLvvIhjb2xvciA9IHdoaXRl77yJ55qE55u057q/5L2c5Li65Y+C6ICD57q/77yM5bm25bCG5a6D5Lus57uY5Yi25Zyo5Z+65pys5pWw5o2u5bGC55qE5LiL6Z2i44CC6L+Z5qC355qE5Y+C6ICD57q/5pei5riF5pmw5Y+v6KeB77yM5Y+I5LiN6Iez5LqO5Zan5a6+5aS65Li777yM5b2x5ZON5oiR5Lus5p+l55yL5pWw5o2u44CCDQoNCi0gICDlj6/ku6Xkvb/nlKhgZ2VvbV9yZWN0KClg5Zyo5oiR5Lus5oSf5YW06Laj55qE5pWw5o2u54K55ZGo5Zu057uY5Yi25LiA5Liq55+p5b2i44CC55+p5b2i55qE6L6555WM55Sx5Zu+5b2i5bGe5oCnIHhtaW7jgIEgeG1heOOAgSB5bWluIOWSjCB5bWF4IOehruWumuOAgg0KDQotICAg5Y+v5Lul5L2/55SoYGdlb21fc2VnbWVudCgpYOWPimBhcnJvd2Dlj4LmlbDnu5jliLbnrq3lpLTvvIzmjIflkJHpnIDopoHlhbPms6jnmoTmlbDmja7ngrnjgILkvb/nlKggeCDlkowgeSDlsZ7mgKfmnaXlrprkuYnlvIDlp4vkvY3nva7vvIzkvb/nlKggeGVuZCDlkowgeWVuZCDlsZ7mgKfmnaXlrprkuYnnu5PmnZ/kvY3nva7jgIINCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpnZW9tX3NlZ21lbnQoDQogIHggPSA2LCB5ID0gNCwgeGVuZCA9IDMsIHllbmQgPSAxLCBjb2xvdXIgPSAiYmx1ZSIsDQogIGFycm93ID0gYXJyb3coYW5nbGUgPSAzMCwgbGVuZ3RoID0gdW5pdCgwLjUsICJjbSIpLCB0eXBlID0gIm9wZW4iKQ0KKQ0KIyBhbmdsZeS4uueureWktOaJgOW8oOinkuW6pu+8jHR5cGXlhrPlrprnrq3lpLTmmK/nqbrlv4Pov5jmmK/lrp7lv4MNCmBgYA0KDQojIyDlm77kvosgTGVnZW5kcw0KDQojIyMgZ3VpZGVzKCkNCg0K6KaB5oOz5o6n5Yi25Y2V5Liq5Zu+5L6L55qE5pi+56S677yM5Y+v5Lul6YWN5ZCIZ3VpZGVfbGVnZW5kKCnvvIjotJ/otKPnprvmlaPlnovlm77kvovvvInmiJZndWlkZV9jb2xvcmJhcigp77yI6LSf6LSj6L+e57ut5Z6L5Zu+5L6L77yJ5p2l5L2/55SoZ3VpZGVzKCkNCg0KYGBge3J9DQpnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbGFzcykpICsNCiAgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKw0KICBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDEsIG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkNCiMgbnJvd+S9v+WbvuS+i+aOkuWIl+S4ujHooYzvvIxzaXplID0gNOS9v+WbvuS+i+S4reeahOaVsOaNrueCueabtOWkp+S4gOS6mw0KYGBgDQoNCiMjIyDlm77kvovnmoTkvY3nva4NCg0KdGhlbWUoKeS4reeahGxlZ2VuZC5wb3NpdGlvbuWPguaVsOWPr+S7peaOp+WItuWbvuS+i+eahOaVtOS9k+S9jee9ru+8mg0KDQpgYGB7ciwgZmlnLnNob3c9J2FzaXMnfQ0KYmFzZSA8LSBnZ3Bsb3QobXBnLCBhZXMoZGlzcGwsIGh3eSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbGFzcykpDQoNCmJhc2UgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibGVmdCIpDQpiYXNlICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpDQpiYXNlICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpiYXNlICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgIyDpu5jorqTorr7nva4NCmJhc2UgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0K5Lmf5Y+v5Lul5oyH5a6a5LiA5Liq5LqM5YWD57Sg5ZCR6YeP77yM6KGo56S65Zu+5L6L55qE5Lit5b+D55u45a+55LqO5pW05byg5Zu+5bem5LiL6KeS55qE6Led56a777yI6YO955So5q+U5L6L6KGo56S677yJDQoNCmBgYHtyfQ0KZGF0YShTYWxhcmllcywgcGFja2FnZSA9ICJjYXIiKQ0KDQpTYWxhcmllcyAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcmFuaywgeSA9IHNhbGFyeSwgZmlsbCA9IHNleCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKA0KICAgIGJyZWFrcyA9IGMoIkFzc3RQcm9mIiwgIkFzc29jUHJvZiIsICJQcm9mIiksDQogICAgbGFiZWxzID0gYygNCiAgICAgICJBc3Npc3RhbnRcblByb2Zlc3NvciIsDQogICAgICAiQXNzb2NpYXRlXG5Qcm9mZXNzb3IiLA0KICAgICAgIkZ1bGxcblByb2Zlc3NvciINCiAgICApDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgYnJlYWtzID0gYyg1MDAwMCwgMTAwMDAwLCAxNTAwMDAsIDIwMDAwMCksDQogICAgbGFiZWxzID0gYygiJDUwSyIsICIkMTAwSyIsICIkMTUwSyIsICIkMjAwSyIpDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkZhY3VsdHkgU2FsYXJ5IGJ5IFJhbmsgYW5kIEdlbmRlciIsDQogICAgeCA9ICIiLCB5ID0gIiIsIGZpbGwgPSAiR2VuZGVyIg0KICApICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYygwLjEsIDAuOCkpDQojIOWbvuS+i+S4reW/g+ebuOWvueS6juaVtOW8oOWbvuW3puS4i+inkueahOi3neemu++8jDAuMeWSjDAuOOmDveaYr+ebuOWvueS6juaVtOS4queUu+mdoueahOavlOS+iw0KYGBgDQoNCiMjIOS4u+mimA0KDQp0aGVtZSgp5Y+v5Lul5a6a5Yi25Zu+5b2i5Lit55qE6Z2e5pWw5o2u5YWD57Sg77yM5L2/5Zu+6KGo5pyJ6Ieq5bex55qE6aOO5qC877yM5Zyo5YGa5Zu+6KGo5pe25LiN6ICD6JmR5qC85byP77yM5pyA5ZCO5LiA5qyh5oCn5bqU55So5Li76aKY44CCDQoNCiMjIyBnZ3Bsb3Qy5YaF572u55qE5YWr56eN5Li76aKYDQoNCiFbMjAyMjAzMjMt5YaF572u5Li76aKYXShodHRwOi8vaHVtb29uLWltYWdlLWhvc3Rpbmctc2VydmljZS5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vaW1nL3R5cG9yYS8yMDIyLzIwMjIwMzIzLeWGhee9ruS4u+mimC5wbmcpIA0KDQrpu5jorqTkuLvpopjkvb/nlKjngbDoibLog4zmma/vvIzov5nmoLflj6/ku6XlnKjnvZHmoLznur/lj6/op4HnmoTmg4XlhrXkuIvmm7TliqDnqoHlh7rmlbDmja7jgILnmb3oibLnvZHmoLznur/ml6LmmK/lj6/op4HnmoTvvIjov5npnZ7luLjph43opoHvvIzlm6DkuLrlroPku6zpnZ7luLjmnInliqnkuo7kvY3nva7liKTlrpop77yM5Y+I5a+56KeG6KeJ5rKh5pyJ5LuA5LmI5Lil6YeN5b2x5ZON77yM5oiR5Lus5a6M5YWo5Y+v5Lul5a+55YW26KeG6ICM5LiN6KeB44CC5Zu+6KGo55qE54Gw6Imy6IOM5pmv5LiN5YOP55m96Imy6IOM5pmv6YKj5LmI56qB5YWA77yM5LiO5paH5pys5Y2w5Yi36aKc6Imy6Z2e5bi455u46L+R77yM5L+d6K+B5LqG5Zu+5b2i5LiO5paH5qGj5YW25LuW6YOo5YiG5rWR54S25LiA5L2T44CC5pyA5ZCO77yM54Gw6Imy6IOM5pmv5Y+v5Lul5Yib5bu65LiA54mH6L+e57ut55qE6aKc6Imy5Yy65Z+f77yM5L2/5Zu+5b2i5oiQ5Li65b2i6LGh6bKc5piO55qE5LiA5Liq54us56uL6KeG6KeJ5a6e5L2T44CCDQoNCiMjIyDoh6rlrprkuYnkuLvpopgNCg0K5omA5pyJ5Y+v5a6a5LmJ55qE5Y+C5pWw5Y+v5p+l6K+iYGdncGxvdDI6OnRoZW1lKClg55qE5biu5Yqp5paH5qGjDQoNCnwg5Y+C5pWwICAgICAgICAgICAgICAgfCDorr7nva7nmoTlr7nosaEgICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgcGxvdC50aXRsZSAgICAgICAgIHwg5Zu+5qCH6aKYICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHBsb3QuY2FwdGlvbiAgICAgICB8IOWbvuagh+azqO+8iOS4gOiIrOaYr+i1hOaWmeadpea6kO+8iSAgICAgICAgfA0KfCBheGlzLnRpdGxlICAgICAgICAgfCDovbTmoIfpopggICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYXhpcy50ZXh0ICAgICAgICAgIHwg6L206aG75qCH562+ICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHBhbmVsLmJhY2tncm91bmQgICB8IOeUu+WbvuWMuuWfn++8iGZpbGzloavlhYXvvIxjb2xvcui+ueahhu+8iSB8DQp8IHBhbmVsLmdyaWQubWFqb3IueSB8IHnovbTvvIjmsLTlubPvvInkuLvopoHnvZHmoLznur8gICAgICAgICAgIHwNCnwgcGFuZWwuZ3JpZC5taW5vci55IHwgeei9tO+8iOawtOW5s++8ieasoeimgee9keagvOe6vyAgICAgICAgICAgICAgIHwNCnwgcGFuZWwuZ3JpZC5tYWpvci54IHwgeOi9tO+8iOerluebtO+8ieS4u+imgee9keagvOe6vyAgICAgICAgICAgICAgIHwNCnwgcGFuZWwuZ3JpZC5taW5vci54IHwgeOi9tO+8iOerluebtO+8ieasoeimgee9keagvOe6vyAgICAgICAgICAgICAgIHwNCnwgbGVnZW5kLnBvc2l0aW9uICAgIHwg5Zu+5L6L55qE5L2N572uICAgICAgICAgICAgICAgICAgICAgIHwNCnwgbGVnZW5kLnRpdGxlICAgICAgIHwg5Zu+5L6L55qE5qCH6aKYICAgICAgICAgICAgICAgICAgICAgIHwNCnwgbGVnZW5kLnRleHQgICAgICAgIHwg5Zu+5L6L5ZCE6aG555qE5paH5a2XICAgICAgICAgICAgICAgICAgfA0KDQpgYGB7cn0NCmRhdGEoU2FsYXJpZXMsIHBhY2thZ2UgPSAiY2FyIikNCg0KIyDlrprkuYnoh6rlt7HnmoTkuLvpophteXRoZW1lDQpteXRoZW1lIDwtDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoDQogICAgICBmYWNlID0gImJvbGQuaXRhbGljIiwNCiAgICAgIHNpemUgPSAxNCwgY29sb3IgPSAiYnJvd24iDQogICAgKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KA0KICAgICAgZmFjZSA9ICJib2xkLml0YWxpYyIsIHNpemUgPSAxMCwNCiAgICAgIGNvbG9yID0gImJyb3duIg0KICAgICksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KA0KICAgICAgZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDksDQogICAgICBjb2xvciA9ICJkYXJrYmx1ZSINCiAgICApLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoDQogICAgICBmaWxsID0gIndoaXRlIiwNCiAgICAgIGNvbG9yID0gImRhcmtibHVlIg0KICAgICksDQogICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyZXkiLCBsaW5ldHlwZSA9IDEpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5IiwgbGluZXR5cGUgPSAyKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiDQogICkNCg0KIyDlnKjnlLvlm77ml7bvvIzlupTnlKjoh6rlt7HnmoTkuLvpophteXRoZW1lDQpnZ3Bsb3QoU2FsYXJpZXMsIGFlcyh4ID0gcmFuaywgeSA9IHNhbGFyeSwgZmlsbCA9IHNleCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHRpdGxlID0gIlNhbGFyeSBieSBSYW5rIGFuZCBTZXgiLCB4ID0gIlJhbmsiLCB5ID0gIlNhbGFyeSIpICsNCiAgbXl0aGVtZQ0KYGBgDQoNCiMjIyDlpZfnlKjnjrDmiJDkuLvpopjvvJpnZ3RoZW1lc+WMhQ0KDQpgYGB7ciwgZmlnLnNob3c9J2FzaXMnfQ0KZ2dwbG90KFNhbGFyaWVzLCBhZXMoeCA9IHJhbmssIHkgPSBzYWxhcnksIGZpbGwgPSBzZXgpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgbGFicyh0aXRsZSA9ICJTYWxhcnkgYnkgUmFuayBhbmQgU2V4IiwgeCA9ICJSYW5rIiwgeSA9ICJTYWxhcnkiKSArDQogIHRoZW1lX3dzaigpICsNCiAgc2NhbGVfZmlsbF93c2ooKQ0KDQpnZ3Bsb3QoU2FsYXJpZXMsIGFlcyh4ID0gcmFuaywgeSA9IHNhbGFyeSwgZmlsbCA9IHNleCkpICsNCiAgZ2VvbV9ib3hwbG90KCkgKw0KICBsYWJzKHRpdGxlID0gIlNhbGFyeSBieSBSYW5rIGFuZCBTZXgiLCB4ID0gIlJhbmsiLCB5ID0gIlNhbGFyeSIpICsNCiAgdGhlbWVfd3NqKCkgKw0KICBzY2FsZV9maWxsX3dzaigicmdieSIsICIiKSArDQogIHRoZW1lKGF4aXMudGlja3MubGVuZ3RoID0gdW5pdCgwLjUsICJjbSIpKSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpDQoNCmdncGxvdChTYWxhcmllcywgYWVzKHggPSByYW5rLCB5ID0gc2FsYXJ5LCBmaWxsID0gc2V4KSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIGxhYnModGl0bGUgPSAiU2FsYXJ5IGJ5IFJhbmsgYW5kIFNleCIsIHggPSAiUmFuayIsIHkgPSAiU2FsYXJ5IikgKw0KICB0aGVtZV9lY29ub21pc3QoYmFzZV9zaXplID0gMTQpICsNCiAgc2NhbGVfZmlsbF9lY29ub21pc3QoKSArDQogIHRoZW1lKGF4aXMudGlja3MubGVuZ3RoID0gdW5pdCgwLjUsICJjbSIpKSArDQogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gTlVMTCkpDQpgYGANCg0KIyMg5aSa5Zu+5ou85o6lDQoNCmdncGxvdDLljIXorqTkuLrlroPmj5DkvpvnmoTliLvpnaLlip/og73lt7Lnu4/otrPlpJ/kuobjgILkuLrkuobnu4TlkIhnZ3Bsb3Tlm77lvaLvvIzpnIDopoHkvb/nlKjlhbbku5bljIXjgIINCg0KIyMjIHBhdGNod29yayDljIUNCg0KYChwMS9wMil8cDNgIOihqOekunAx5ZKMcDLlsYXlt6bvvIzliIbliKvkuIrkuIvvvJtwM+WxheWPs++8jOWQiOW5tuaIkOS4gOW5heWbvg0KDQojIyMgZ3JpZEV4dHJhIOWMhQ0KDQpgZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoKWDlj6/ku6Xnu4TlkIjlpJrluYXlm74NCg0KYGBge3J9DQpkYXRhKFNhbGFyaWVzLCBwYWNrYWdlID0gImNhciIpDQojIGdncGxvdCgp55S755qE5Zu+5Y+v5Lul5L+d5a2Y5Li65LiA5Liq5a+56LGh77yBDQpwMSA8LSBnZ3Bsb3QoZGF0YSA9IFNhbGFyaWVzLCBhZXMoeCA9IHJhbmspKSArDQogIGdlb21fYmFyKCkNCnAyIDwtIGdncGxvdChkYXRhID0gU2FsYXJpZXMsIGFlcyh4ID0gc2V4KSkgKw0KICBnZW9tX2JhcigpDQpwMyA8LSBnZ3Bsb3QoZGF0YSA9IFNhbGFyaWVzLCBhZXMoeCA9IHlycy5zaW5jZS5waGQsIHkgPSBzYWxhcnkpKSArDQogIGdlb21fcG9pbnQoKQ0KDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmdyaWQuYXJyYW5nZShwMSwgcDIsIHAzLCBuY29sID0gMykNCmBgYA0KDQpgYXJyYW5nZUdyb2IoKWDliJnlj6/ku6XlpITnkIbkuIDkuKrnlLHlm77lr7nosaHnu4TmiJDnmoTliJfooajvvIjlvqrnjq/kvZzlm77ml7blvojmnInnlKjvvIkNCg0KYGBge3J9DQpwIDwtIGxpc3QocDEsIHAyLCBwMykNCmdyYXBoIDwtIGFycmFuZ2VHcm9iKGdyb2JzID0gcCwgbmNvbCA9IDMpDQpncmlkLmFycmFuZ2UoZ3JhcGgpDQpgYGANCg==