图形默认值适用于快速的数据探索,但是当你希望将结果发布到博客、论文、文章或海报时,您可能需要自定义结果。定制可以提高图形的清晰度和吸引力。
本章将描述如何自定义图形的轴、网格线、颜色、字体、标签和图例。它还描述了如何添加注释(文本和行)。
9.1 坐标轴
x轴和y轴表示连续、分类或日期值。您可以使用下面的函数修改默认的刻度和标签。
9.1.1 数值轴
使用scale_x_continuous
或scale_y_continuous
函数修改定量轴。
选项包括
- breaks-位置限制的数值向量
- limits-带有刻度的最小值和最大值的数值向量
library(pacman)
p_load(tidyverse,DT,patchwork)
mpg %>% datatable()
mpg %>%
ggplot(aes(displ,hwy)) +
geom_point()->p1
mpg %>%
ggplot(aes(displ,hwy)) +
geom_point() +
scale_x_continuous(breaks = seq(1,7,1),limits = c(1,7),expand = c(0,0)) +
scale_y_continuous(breaks = seq(10,45,5),limits = c(10,45),expand = c(0,0))->p2
p1 + p2

9.1.1.1 数值格式
scales包提供了许多用于格式化数字标签的函数。其中最有用的是
让我们用一些合成数据来演示这些函数。
# create some data
set.seed(1234)
df <- data.frame(xaxis = rnorm(50, 100000, 50000),
yaxis = runif(50, 0, 1),
pointsize = rnorm(50, 1000, 1000))
library(ggplot2)
df %>% datatable()
df %>%
ggplot(aes(xaxis,yaxis,size = pointsize)) +
geom_point(color = "steelblue",
alpha = .6) +
scale_x_continuous(labels = scales::comma) +
scale_y_continuous(labels = scales::percent) +
scale_size_continuous(range = c(0,10),labels = scales::dollar)

若要将货币值格式化为欧元,可以使用
- label = scales::dollar_format(prefix = "“, suffix =”
df %>%
ggplot(aes(xaxis, yaxis, size = pointsize)) +
geom_point(color = "steelblue",
alpha = .6) +
scale_x_continuous(labels = scales::comma) +
scale_y_continuous(labels = scales::percent) +
scale_size_continuous(
range = c(0, 10),
label = scales::dollar_format(prefix = "", suffix = "\u20ac")
)

9.1.2 分类数据轴
使用scale_x_discrete
或scale_y_discrete
函数修改分类轴。
选项包括
- limits-一个字符向量(定量变量按所需顺序的水平)
- labels-标签的字符向量(这些级别的可选标签)
mpg %>%
ggplot(aes(class)) +
geom_bar(fill = "steelblue") +
scale_x_discrete(
limits = c(
"pickup",
"suv",
"minivan",
"midsize",
"compact",
"subcompact",
"2seater"
),
labels = c(
"Pickup\nTruck",
"Sport Utility\nVehicle",
"Minivan",
"Mid-size",
"Compact",
"Subcompact",
"2-Seater"
)
) -> p1
mpg %>%
count(class) %>%
ggplot(aes(x = reorder(class, n), y = n)) +
geom_col(fill = "steelblue") +
scale_x_discrete(
limits = c(
"2seater",
"minivan",
"pickup",
"subcompact",
"midsize",
"compact",
"suv"
),
labels = c(
"2-Seater",
"Minivan",
"Pickup\nTruck",
"Subcompact",
"Mid-size",
"Compact",
"Sport Utility\nVehicle"
)
) +
labs(x = "class")-> p2
p1 / p2

9.1.3 时间轴
使用scale_x_date
或scale_y_date
函数修改日期轴。
选项包括:
- date_breaks -表示间隔时间的字符串,如“2周”或“10年”
- date_labels -给出标签格式规范的字符串
下表给出了日期值的格式规范。
%d |
day as a number |
(0-31) 01-31 |
%a |
abbreviated weekday |
Mon |
%A |
unabbreviated weekday |
Monday |
%m |
month (00-12) |
00-12 |
%b |
abbreviated month |
Jan |
%B |
unabbreviated month |
January |
%y |
2-digit year |
07 |
%Y |
4-digit year |
2007 |
economics %>%
ggplot(aes(date,unemploy)) +
geom_point(col = "red") +
geom_line(col = "steelblue",size = 1) +
scale_x_date(date_breaks = "5 years",date_labels = "%b-%y")

9.2 颜色
ggplot2图形中的默认颜色是功能性的,但通常在视觉上不具有足够的吸引力。幸运的是,这很容易改变。
特定的颜色可以是为点、线、条、区域和文本指定,或映射到数据集中变量的级别。
9.2.1 手动改变颜色
要为点、线或文本指定颜色,请在适当的geom中使用color = "colorname"
选项。要为条形图和区域指定颜色,请使用fill = "colorname"
选项。
- geom_point(color = “blue”)
- geom_bar(fill = “steelblue”)
要为变量的级别分配颜色,可以使用scale_color_manual
和scale_fill_manual
函数。前者用于指定点和线的颜色,而后者用于条形图和区域。
下面是一个示例,使用ggplot2附带的diamonds数据集。数据集包含了54,000颗圆形切割钻石的价格和属性。
diamonds$clarity %>% fct_count()
## # A tibble: 8 x 2
## f n
## <fct> <int>
## 1 I1 741
## 2 SI2 9194
## 3 SI1 13065
## 4 VS2 12258
## 5 VS1 8171
## 6 VVS2 5066
## 7 VVS1 3655
## 8 IF 1790
## 手动指定颜色级别
diamonds %>%
ggplot(aes(cut,fill = clarity)) +
geom_bar(position = "stack") +
scale_fill_manual(values = c("darkred", "steelblue",
"darkgreen", "gold",
"brown", "purple",
"grey", "khaki4"))
diamonds %>%
ggplot(aes(cut,fill = clarity)) +
geom_bar(position = "fill") +
scale_fill_manual(values = c("darkred", "steelblue",
"darkgreen", "gold",
"brown", "purple",
"grey", "khaki4"))
diamonds %>%
ggplot(aes(cut,fill = clarity)) +
geom_bar(position = "dodge") +
scale_fill_manual(values = c("darkred", "steelblue",
"darkgreen", "gold",
"brown", "purple",
"grey", "khaki4"))



如果你和我一样在审美上有困难,另一种选择是使用预定义的调色板。
9.2.2 颜色调色板
在R中有许多预定义的调色板可用。
9.2.2.1 RColorBrewer
最流行的调色板可能是ColorBrewer调色板。
library(RColorBrewer)
RColorBrewer::display.brewer.all()

您可以使用scale_color_brewer
和scale_fill_brewer
函数指定这些调色板。
diamonds %>%
ggplot(aes(cut,fill = clarity)) +
geom_bar() +
scale_fill_brewer(palette = "Dark2")
diamonds %>%
ggplot(aes(cut,fill = clarity)) +
geom_bar() +
scale_fill_brewer(palette = "Dark2") +
scale_x_discrete(expand = c(0,0)) +
scale_y_continuous(expand = c(0,0))


向这些函数添加direction = -1
将翻转调色板中颜色的顺序。
9.2.2.2 Viridis
viridis是另一个流行的调色板。
连续scales:
- scale_fill_viridis_c
- scale_color_viridis_c
离散scales:
- scale_fill_viridis_d
- scale_color_viridis_d
library(viridis)
## Loading required package: viridisLite
ggplot(diamonds,aes(x = cut,fill = clarity)) +
geom_bar() +
scale_fill_viridis_d()

9.2.2.3 其他调色板
其他需要探索的调色板包括dutchmasters
、ggpomological
、LaCroixColoR
、nord
、ochRe
、palettetown
、pals
、 rcartocolor
和wesanderson
。如果您想查看所有选项板选项(或几乎所有选项),请查看paletter包。
要了解有关颜色规范的更多信息,请参阅有关ggplot2颜色的R Cookpage页面。也可以参考这本书中的颜色选择建议这部分内容。
9.3 点线
9.3.1 Points
对于ggplot2图,默认的点是一个填充的圆。要指定不同的形状,请使用geom_point函数
中的shape = #选项。要将形状映射到分类变量的级别,请使用aes函数
中的shape = variablename选项。
例如:
- geom_point(shape = 1)
- geom_point(aes(shape = sex))
可用的形状如下表所示。
knitr::include_graphics("shapes1-1.png")

形状21到26提供了填充色和边框色。
9.3.2 Lines
默认的行类型是实线。要更改linetype,请使用geom_line函数
中的linetype = #选项。要将linetype映射到类别变量的级别,请使用aes函数
中的linetype = variablename选项。
例如:
- geom_line(linetype = 1)
- geom_line(aes(linetype = sex))
knitr::include_graphics("Lines2-1.png")

9.4 字体
R不太支持字体,但是通过一些工作,您可以更改图形中出现的字体。首先,您需要安装和设置extrafont包。
library(extrafont)
## Registering fonts with R
# extrafont::font_import()
# see what fonts are now available
fonts()
## [1] "Agency FB" "Algerian"
## [3] "Arial Black" "Arial"
## [5] "Arial Narrow" "Arial Rounded MT Bold"
## [7] "Arial Unicode MS" "Arvo"
## [9] "Bahnschrift" "Baskerville Old Face"
## [11] "Bauhaus 93" "Bell MT"
## [13] "Berlin Sans FB" "Berlin Sans FB Demi"
## [15] "Bernard MT Condensed" "Blackadder ITC"
## [17] "Bodoni MT" "Bodoni MT Black"
## [19] "Bodoni MT Condensed" "Bodoni MT Poster Compressed"
## [21] "Book Antiqua" "Bookman Old Style"
## [23] "Bookshelf Symbol 7" "Bradley Hand ITC"
## [25] "Britannic Bold" "Broadway"
## [27] "Brush Script MT" "Calibri"
## [29] "Calibri Light" "Californian FB"
## [31] "Calisto MT" "Cambria"
## [33] "Candara" "Candara Light"
## [35] "Castellar" "Centaur"
## [37] "Century" "Century Gothic"
## [39] "Century Schoolbook" "Chiller"
## [41] "Colonna MT" "Comic Sans MS"
## [43] "Consolas" "Constantia"
## [45] "Cooper Black" "Copperplate Gothic Bold"
## [47] "Copperplate Gothic Light" "Corbel"
## [49] "Corbel Light" "Courier New"
## [51] "Curlz MT" "DejaVu Sans Mono"
## [53] "DengXian" "DengXian Light"
## [55] "Droid Serif" "Dubai"
## [57] "Dubai Light" "Dubai Medium"
## [59] "Ebrima" "Edwardian Script ITC"
## [61] "Elephant" "Engravers MT"
## [63] "Eras Bold ITC" "Eras Demi ITC"
## [65] "Eras Light ITC" "Eras Medium ITC"
## [67] "Euclid" "Euclid Extra"
## [69] "Euclid Fraktur" "Euclid Math One"
## [71] "Euclid Math Two" "Euclid Symbol"
## [73] "FangSong" "Felix Titling"
## [75] "Fences" "Footlight MT Light"
## [77] "Forte" "Franklin Gothic Book"
## [79] "Franklin Gothic Demi" "Franklin Gothic Demi Cond"
## [81] "Franklin Gothic Heavy" "Franklin Gothic Medium"
## [83] "Franklin Gothic Medium Cond" "Freestyle Script"
## [85] "French Script MT" "FZShuTi"
## [87] "FZYaoTi" "Gabriola"
## [89] "Gadugi" "Garamond"
## [91] "Georgia" "Gigi"
## [93] "Gill Sans Ultra Bold" "Gill Sans Ultra Bold Condensed"
## [95] "Gill Sans MT" "Gill Sans MT Condensed"
## [97] "Gill Sans MT Ext Condensed Bold" "Gloucester MT Extra Condensed"
## [99] "Goudy Old Style" "Goudy Stout"
## [101] "Haettenschweiler" "Harlow Solid Italic"
## [103] "Harrington" "High Tower Text"
## [105] "HoloLens MDL2 Assets" "Impact"
## [107] "Imprint MT Shadow" "Indie Flower"
## [109] "Informal Roman" "Ink Free"
## [111] "Javanese Text" "Jokerman"
## [113] "Juice ITC" "KaiTi"
## [115] "Kristen ITC" "Kunstler Script"
## [117] "Wide Latin" "Leelawadee"
## [119] "Leelawadee UI" "Leelawadee UI Semilight"
## [121] "LiSu" "Lobster"
## [123] "Lucida Bright" "Lucida Calligraphy"
## [125] "Lucida Console" "Lucida Fax"
## [127] "Lucida Handwriting" "Lucida Sans"
## [129] "Lucida Sans Typewriter" "Lucida Sans Unicode"
## [131] "Magneto" "Maiandra GD"
## [133] "Malgun Gothic" "Malgun Gothic Semilight"
## [135] "Marlett" "Matura MT Script Capitals"
## [137] "Microsoft Himalaya" "Microsoft Yi Baiti"
## [139] "Microsoft New Tai Lue" "Microsoft PhagsPa"
## [141] "Microsoft Sans Serif" "Microsoft Tai Le"
## [143] "Microsoft Uighur" "Microsoft YaHei"
## [145] "Mistral" "Modern No. 20"
## [147] "Mongolian Baiti" "Monotype Corsiva"
## [149] "MS Outlook" "MS Reference Sans Serif"
## [151] "MS Reference Specialty" "MT Extra"
## [153] "MT Extra Tiger" "MV Boli"
## [155] "Myanmar Text" "Niagara Engraved"
## [157] "Niagara Solid" "Nirmala UI"
## [159] "Nirmala UI Semilight" "OCR A Extended"
## [161] "Old English Text MT" "Onyx"
## [163] "Open Sans" "Palace Script MT"
## [165] "Palatino Linotype" "Papyrus"
## [167] "Parchment" "Perpetua"
## [169] "Perpetua Titling MT" "Playbill"
## [171] "Poiret One" "Poor Richard"
## [173] "Pristina" "Rage Italic"
## [175] "Raleway" "Ravie"
## [177] "Roboto" "Roboto Condensed"
## [179] "Roboto Slab" "Rockwell"
## [181] "Rockwell Condensed" "Rockwell Extra Bold"
## [183] "Script MT Bold" "Segoe MDL2 Assets"
## [185] "Segoe Print" "Segoe Script"
## [187] "Segoe UI" "Segoe UI Light"
## [189] "Segoe UI Semibold" "Segoe UI Semilight"
## [191] "Segoe UI Black" "Segoe UI Emoji"
## [193] "Segoe UI Historic" "Segoe UI Symbol"
## [195] "Showcard Gothic" "SimHei"
## [197] "SimSun-ExtB" "Snap ITC"
## [199] "STCaiyun" "Stencil"
## [201] "STFangsong" "STHupo"
## [203] "STKaiti" "STLiti"
## [205] "STSong" "STXihei"
## [207] "STXingkai" "STXinwei"
## [209] "STZhongsong" "Sylfaen"
## [211] "Symbol" "Symbol Tiger"
## [213] "Symbol Tiger Expert" "Tahoma"
## [215] "Tempus Sans ITC" "Tiger"
## [217] "Tiger Expert" "Times New Roman"
## [219] "Trebuchet MS" "Tw Cen MT"
## [221] "Tw Cen MT Condensed" "Tw Cen MT Condensed Extra Bold"
## [223] "Verdana" "Viner Hand ITC"
## [225] "Vivaldi" "Vladimir Script"
## [227] "Webdings" "Wingdings"
## [229] "Wingdings 2" "Wingdings 3"
## [231] "YouYuan" "ZWAdobeF"
使用themes主题函数
中的文本选项应用新字体。
library(extrafont)
ggplot(mpg, aes(x = displ, y=hwy)) +
geom_point() +
labs(title = "Diplacement by Highway Mileage",
subtitle = "MPG dataset") +
theme(text = element_text(size = 13, family = "Times New Roman"),
plot.title = element_text(hjust = 0.5))

9.5 Legend
在ggplot2中,当变量被映射到颜色、填充、线型、形状、大小或alpha时,将自动创建图例。你对这些Legend的外观和感觉有很大的控制权。通常通过主题函数和/或labs函数进行修改。这里有一些最受欢迎的。
9.5.1 Legend位置
图例可以出现在图中的任何地方。默认情况下,它位于右侧。您可以使用:
theme(legend.position = position):
- “top” above the plot area
- “right” right of the plot area
- “bottom” below the plot area
- “left” left of the plot area c(x, y) within the plot area. The x and y values must range between 0 and 1. c(0,0) represents (left, bottom) and c(1,1) represents (right, top).
- “none” suppress the legend
例如,要将图例置于顶部,请使用以下代码。
mpg %>%
ggplot(aes(displ,hwy,col = class)) +
geom_point(size = 4) +
labs(title = "Diplacement by Highway Mileage") +
theme(legend.position = "top",
legend.title.align = 0.5) # Legend title align

9.5.2 Legend标题
您可以通过labs函数
更改图例标题。使用颜色、填充、大小、形状、线条类型和alpha来给相应的图例赋予新的标题。图例标题的对齐是通过legend.title.align
来控制的。对齐选项在主题函数。(0 =左,0.5 =中心,1 = 右)
# change the default legend title
ggplot(mpg,
aes(x = displ, y=hwy, color = class)) +
geom_point(size = 4) +
labs(title = "Diplacement by Highway Mileage",
color = "Automobile\nClass") +
theme_minimal() +
theme(legend.title.align=0.5)
mpg %>%
ggplot(aes(displ,hwy,col = class)) +
geom_point(size = 4) +
scale_color_discrete(name = "Automobile\nClass") +
theme(legend.title.align = 0.5) +
labs(title = "Diplacement by Highway Mileage")


9.6 Labels
标签是使图形易于理解的关键因素,可以使用labs函数
添加。可用的选项如下所示
- title- main title
- subtitle- subtitle
- caption- caption (bottom right by default)
- x- horizontal axis
- y- vertical axis
- color- color legend title
- fill- fill legend title
- size- size legend title
- linetype- linetype legend title
- shape- shape legend title
- alpha- transparency legend title
- size- size legend title
ggplot(mpg,
aes(x = displ,
y=hwy,
color = class,
shape = factor(year))) +
geom_point(size = 3,
alpha = .5) +
labs(title = "Mileage by engine displacement",
subtitle = "Data from 1999 and 2008",
caption = "Source: EPA (http://fueleconomy.gov)",
x = "Engine displacement (litres)",
y = "Highway miles per gallon",
color = "Car Class",
shape = "Year") +
theme(plot.title = element_text(hjust = 0.5),
text = element_text(family = "Times New Roman"))

这不是一个很好的图——它太忙了,使得模式的识别非常困难。最好是将year变量分面。趋势线也会有帮助。
ggplot(mpg,
aes(x =displ,
y=hwy)) +
geom_point(aes(col = class),
size = 3,
alpha = .5) +
geom_smooth(aes(col = class),method = "lm",se = FALSE) +
labs(title = "Mileage by engine displacement",
subtitle = "Data from 1999 and 2008",
caption = "Source: EPA (http://fueleconomy.gov)",
x = "Engine displacement (litres)",
y = "Highway miles per gallon",
color = "Car Class",
shape = "Year") +
theme(plot.title = element_text(hjust = 0.5),
text = element_text(family = "Times New Roman")) +
facet_wrap(~factor(year))
## `geom_smooth()` using formula 'y ~ x'

9.7 Annotations
注释是添加到图中的信息,用于突出重要的点。
9.7.1 添加文本
向图中添加文本有两个主要原因。一个是识别geom的数字质量。例如,我们可能希望在散点图中使用标签来标识点,或者在柱状图中标记条形图的高度。另一个原因是提供额外的信息,我们可能想要添加关于数据的注释,指出异常值等等。
9.7.1.1 Labling值
考虑以下散点图,基于mtcars数据集中的car数据。
mtcars %>%
ggplot(aes(wt,mpg)) +
geom_point(size = 2)

让我们用它所代表的汽车的名称来标记每个点。
ggplot(mtcars, aes(x = wt, y = mpg)) +
geom_point(size = 2) +
geom_text(label = row.names(mtcars)) # 说实话,不好看

# scatterplot with non-overlapping labels
data(mtcars)
library(ggrepel)
ggplot(mtcars, aes(x = wt,
y = mpg)) +
geom_point(size = 2) +
geom_text_repel(label = row.names(mtcars))
ggplot(mtcars, aes(x = wt,
y = mpg)) +
geom_point(size = 2) +
geom_label_repel(label = row.names(mtcars))


9.7.1.2 添加附加信息
我们可以使用注释函数将文本放在图表的任何位置
annotate("text",
x, y,
label = "Some text",
color = "colorname",
size=textsize)
其中x和y是放置文本的坐标。颜色和大小参数是可选的。默认情况下,文本将居中。使用hjust和vjust来更改对齐方式。
- hjust 0 = left justified, 0.5 = centered, and 1 = right centered.
- vjust 0 = above, 0.5 = centered, and 1 = below.
继续前边的例子:
data(mtcars)
library(ggrepel)
txt <- paste("The relationship between car weight",
"and mileage appears to be roughly linear",
sep = "\n")
txt
## [1] "The relationship between car weight\nand mileage appears to be roughly linear"
ggplot(mtcars, aes(x = wt,
y = mpg)) +
geom_point(color = "red",
size = 3) +
geom_label_repel(label = row.names(mtcars),
size=3) +
ggplot2::annotate("text",
6, 30,
label=txt,
color = "red",
hjust = 1)

9.7.2 添加水平和垂直线
水平和垂直的线可以添加使用:
其中a是y轴上的数字,b是x轴上的数字。其他选项包括线型和颜色。
- geom_hline(yintercept = a)
- geom_vline(xintercept = b)
# add annotation line and text label
min_cty <- min(mpg$cty)
mean_hwy <- mean(mpg$hwy)
mean_cty <- mean(mpg$cty)
# 先绘制简单图,再逐渐修改细节
ggplot(mpg,
aes(x = cty,
y=hwy,
color=drv)) +
geom_point(size = 3)+
scale_y_continuous(limits = c(10,45,5),
breaks = seq(10,45,5)) +
labs(color = "Drv") +
scale_color_discrete(labels = c("4","F","R")) +
theme(legend.position = "top") +
geom_hline(yintercept = mean_hwy,
color = "darkred",
linetype = "dashed") +
ggplot2::annotate("text",
min_cty,
mean_hwy + 1.5,
label = "Mean",
color = "darkred") +
geom_vline(xintercept = mean_cty,
color = "darkred",
linetype = "dashed") +
ggplot2::annotate("text",
mean_cty + 1.5,
max(mpg$hwy),
label = "Mean",
color = "darkred") +
labs(title = "Mileage by drive type",
x = "City miles per gallon",
y = "Highway miles per gallon",
color = "Drive") +
theme(plot.title = element_text(hjust = 0.5))

我们还加上一条每加仑平均城市英里数的垂直线。在任何情况下,总是以某种方式标记注释行。否则读者将不知道他们的意思。
9.7.3 高亮某一组
有时您希望在图中突出显示单个组。gghighlight包中的gghighlight函数
就是为此而设计的。
# highlight a set of points
library(ggplot2)
library(gghighlight)
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point(color = "red",
size=3) +
gghighlight(class == "midsize")

mpg %>%
ggplot(aes(x = class)) +
geom_bar()
# highlight a single bar
library(gghighlight)
ggplot(mpg, aes(x = class)) +
geom_bar(fill = "red") +
gghighlight(class == "midsize")
## Warning: Tried to calculate with group_by(), but the calculation failed.
## Falling back to ungrouped filter operation...
## label_key: class


这里没有什么是不能用基本图形来完成的,但是ggplot2更方便。
9.8 主题
ggplot2主题控制情节中所有与数据无关的组件的外观。您可以通过更改主题的元素来更改图形的外观和感觉。
9.8.1 改变主题元素
主题函数用于修改主题的各个组件。主题函数的参数在由在线帮助开发的备忘单中进行了描述。
考虑下图。它显示了2008-2009年某所大学按等级rank和学科discipline划分的男女教员人数。数据来源于Salaries数据集。
# create graph
data(Salaries, package = "carData")
p <- ggplot(Salaries,
aes(x = rank, fill = sex)) +
geom_bar()
p
data(Salaries, package = "carData")
p <- ggplot(Salaries, aes(x = rank, fill = sex)) +
geom_bar() +
facet_wrap(~discipline) +
labs(title = "Academic Rank by Gender and Discipline",
x = "Rank",
y = "Frequency",
fill = "Gender")
p


让我们改变一下主题。
- 将标签文本从黑色更改为海军蓝
- 将面板背景颜色从灰色改为白色
- 为主要的y轴网格线添加纯灰色线
- 添加小y轴网格线的虚线灰线
- 消除x轴网格线
- 将长条背景颜色改为白色,并带有灰色边框
p +
theme(text = element_text(color = "navy"), # 将标签文本从黑色更改为海军蓝
panel.background = element_rect(fill = "white"), # 将面板背景颜色从灰色改为白色
panel.grid.major.y = element_line(color = "grey"), # 为主要的y轴网格线添加纯灰色线
panel.grid.minor.y = element_line(color = "grey",
linetype = "dashed"),
panel.grid.major.x = element_blank(),
panel.grid.minor.x = element_blank(), # 消除x轴网格线
strip.background = element_rect(fill = "white", color="grey"))

哇,这看起来很糟糕,但你明白我的意思。
9.8.1.1 ggThemeAssist
如果您想使用GUI创建自己的主题,请查看ggThemeAssist
。安装包后,RStudio
中的Addins
下将出现一个新菜单项。
突出显示创建图形的代码,然后从Addins下拉菜单中选择ggThemeAssist选项。您可以使用指向-单击来更改主题的许多特性。当您完成时,主题代码将被附加到您的图形代码中。
9.8.2 Pre-packaged主题
我不是一个很好的艺术家(只看最后一个例子),所以我经常寻找可以应用到我的图表中的预先包装好的主题。有很多可用的主题,有些ggplot2附带。其中包括theme_classic
、theme_dark
、theme_gray
、theme_grey
、theme_light
theme_linedraw
、theme_minimum
和theme_void
。在本书中,我们经常使用theme_minimum
。其他的可以通过附加包获得。
9.8.2.1 ggthemes
ggthemes package覆盖19种主题themes.
theme_base |
Theme Base |
theme_calc |
Theme Calc |
theme_economist |
ggplot color theme based on the Economist |
theme_economist_white |
ggplot color theme based on the Economist |
theme_excel |
ggplot color theme based on old Excel plots |
theme_few |
Theme based on Few’s “Practical Rules for Using Color in Charts” |
theme_fivethirtyeight |
Theme inspired by fivethirtyeight.com plots |
theme_foundation |
Foundation Theme |
theme_gdocs |
Theme with Google Docs Chart defaults |
theme_hc |
Highcharts JS theme |
theme_igray |
Inverse gray theme |
theme_map |
Clean theme for maps |
theme_pander |
A ggplot theme originated from the pander package |
theme_par |
Theme which takes its values from the current ‘base’ graphics parameter values in ‘par’. |
theme_solarized |
ggplot color themes based on the Solarized palette |
theme_solarized_2 |
ggplot color themes based on the Solarized palette |
theme_solid |
Theme with nothing other than a background color |
theme_stata |
Themes based on Stata graph schemes |
theme_tufte |
Tufte Maximal Data, Minimal Ink Theme |
theme_wsj Wall |
Street Journal theme |
为了演示它们的用法,我们首先创建并保存一个图。
# create basic plot
library(ggplot2)
p <- ggplot(mpg,
aes(x = displ,
y=hwy,
color = class)) +
geom_point(size = 3,
alpha = .5) +
labs(title = "Mileage by engine displacement",
subtitle = "Data from 1999 and 2008",
caption = "Source: EPA (http://fueleconomy.gov)",
x = "Engine displacement (litres)",
y = "Highway miles per gallon",
color = "Car Class") +
theme(plot.title = element_text(hjust = 0.5))
p
# display graph
p + theme(panel.grid.major = element_line(colour = "green",
linetype = "dotdash"), panel.grid.minor = element_line(linetype = "dotdash"),
panel.background = element_rect(fill = "gray84",
linetype = "dotdash"), plot.background = element_rect(fill = "antiquewhite",
colour = NA))


让我们应用一些主题
# add economist theme
library(ggthemes)
p + theme_economist()

# add fivethirtyeight theme
p + theme_fivethirtyeight()

# add wsj theme
p + theme_wsj(base_size=8)

默认情况下,wsj主题的字体通常太大。更改base_size选项
会有所帮助。
每个主题还提供了颜色和填充比例。在下一个示例中,将同时使用少数几个主题和颜色。
# add few theme
p + theme_few() + scale_color_few()

9.8.2.2 hrbrthemes
hrbrthemes包专注于以排版为中心的主题。结果是图表,往往有一个干净的外观。继续上面的示例图.
# add few theme
library(hrbrthemes)
p + theme_ipsum()

9.8.2.3 ggthemer
ggthemer包提供了广泛的主题(截至打印时为17个主题)。该软件包在CRAN上不可用,必须从GitHub上安装。
library(devtools)
## Loading required package: usethis
# install_github('cttobin/ggthemr')
p_load(ggthemr)
函数的工作方式略有不同。使用ggthemr(“themename”)函数
将未来的图形设置为给定的主题。使用ggthemr_reset()
将未来的图返回到ggplot2默认主题。
主题主要包括:flat, flat dark, camoflauge, chalk, copper, dust, earth, fresh, grape, grass, greyscale, light, lilac, pale, sea, sky, and solarized.
# set graphs to the flat dark theme
library(ggthemr)
ggthemr("flat dark")
p

我不会把这个主题用在这个图上。很难分辨颜色。哪个绿色代表紧凑型汽车,哪个代表微型车?选择一个能最好地将图表信息传达给受众的主题。
ggthemr_reset()
p
p + scale_color_brewer(palette = "Accent")


LS0tDQp0aXRsZTogIuWIqeeUqFLov5vooYzmlbDmja7lj6/op4bljJbigJTigJTnrKzkuZ3nq6Dph43kuK3kuYvph43kuYvoh6rlrprkuYnlm77lvaIiDQphdXRob3I6ICJMSkoiDQpkYXRlOiAiMjAyMC8zLzI1Ig0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLGZpZy5zaG93ID0gImhvbGQiLGZpZy5hbGlnbiA9ICJjZW50ZXIiLGNhY2hlID0gVFJVRSkNCmBgYA0KDQrlm77lvaLpu5jorqTlgLzpgILnlKjkuo7lv6vpgJ/nmoTmlbDmja7mjqLntKLvvIzkvYbmmK/lvZPkvaDluIzmnJvlsIbnu5Pmnpzlj5HluIPliLDljZrlrqLjgIHorrrmlofjgIHmlofnq6DmiJbmtbfmiqXml7bvvIzmgqjlj6/og73pnIDopoHoh6rlrprkuYnnu5PmnpzjgILlrprliLblj6/ku6Xmj5Dpq5jlm77lvaLnmoTmuIXmmbDluqblkozlkLjlvJXlipvjgIINCg0K5pys56ug5bCG5o+P6L+w5aaC5L2V6Ieq5a6a5LmJ5Zu+5b2i55qEKirovbQqKuOAgSoq572R5qC857q/KirjgIEqKuminOiJsioq44CBKirlrZfkvZMqKuOAgSoq5qCH562+KirlkowqKuWbvuS+iyoq44CC5a6D6L+Y5o+P6L+w5LqG5aaC5L2V5re75YqgKirms6jph4oqKijmlofmnKzlkozooYwp44CCDQoNCi0tLQ0KDQojIyA5LjEg5Z2Q5qCH6L20DQoNCnjovbTlkox56L206KGo56S6Kirov57nu60qKuOAgSoq5YiG57G7KirmiJYqKuaXpeacnyoq5YC844CC5oKo5Y+v5Lul5L2/55So5LiL6Z2i55qE5Ye95pWw5L+u5pS56buY6K6k55qE5Yi75bqm5ZKM5qCH562+44CCDQoNCiMjIyA5LjEuMSDmlbDlgLzovbQNCg0K5L2/55SoYHNjYWxlX3hfY29udGludW91c2DmiJZgc2NhbGVfeV9jb250aW51b3VzYOWHveaVsOS/ruaUueWumumHj+i9tOOAgg0KDQrpgInpobnljIXmi6wNCg0KKiBicmVha3Mt5L2N572u6ZmQ5Yi255qE5pWw5YC85ZCR6YePDQoqIGxpbWl0cy3luKbmnInliLvluqbnmoTmnIDlsI/lgLzlkozmnIDlpKflgLznmoTmlbDlgLzlkJHph48NCg0KYGBge3J9DQpsaWJyYXJ5KHBhY21hbikNCnBfbG9hZCh0aWR5dmVyc2UsRFQscGF0Y2h3b3JrKQ0KYGBgDQoNCmBgYHtyfQ0KbXBnICU+JSBkYXRhdGFibGUoKQ0KYGBgDQoNCmBgYHtyfQ0KbXBnICU+JSANCiAgZ2dwbG90KGFlcyhkaXNwbCxod3kpKSArDQogIGdlb21fcG9pbnQoKS0+cDENCg0KbXBnICU+JSANCiAgZ2dwbG90KGFlcyhkaXNwbCxod3kpKSArDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMSw3LDEpLGxpbWl0cyA9IGMoMSw3KSxleHBhbmQgPSBjKDAsMCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgxMCw0NSw1KSxsaW1pdHMgPSBjKDEwLDQ1KSxleHBhbmQgPSBjKDAsMCkpLT5wMg0KDQpwMSArIHAyDQpgYGANCg0KIyMjIyA5LjEuMS4xIOaVsOWAvOagvOW8jw0KDQoqKnNjYWxlc+WMhSoq5o+Q5L6b5LqG6K645aSa55So5LqO5qC85byP5YyW5pWw5a2X5qCH562+55qE5Ye95pWw44CC5YW25Lit5pyA5pyJ55So55qE5pivDQoNCiogZG9sbGFyDQoqIGNvbW1hDQoqIHBlcmNlbnQNCg0K6K6p5oiR5Lus55So5LiA5Lqb5ZCI5oiQ5pWw5o2u5p2l5ryU56S66L+Z5Lqb5Ye95pWw44CCDQoNCmBgYHtyfQ0KIyBjcmVhdGUgc29tZSBkYXRhDQpzZXQuc2VlZCgxMjM0KQ0KZGYgPC0gZGF0YS5mcmFtZSh4YXhpcyA9IHJub3JtKDUwLCAxMDAwMDAsIDUwMDAwKSwNCiAgICAgICAgICAgICAgICAgeWF4aXMgPSBydW5pZig1MCwgMCwgMSksDQogICAgICAgICAgICAgICAgIHBvaW50c2l6ZSA9IHJub3JtKDUwLCAxMDAwLCAxMDAwKSkNCmxpYnJhcnkoZ2dwbG90MikNCmBgYA0KDQpgYGB7cn0NCmRmICU+JSBkYXRhdGFibGUoKQ0KYGBgDQoNCmBgYHtyfQ0KZGYgJT4lIA0KICBnZ3Bsb3QoYWVzKHhheGlzLHlheGlzLHNpemUgPSBwb2ludHNpemUpKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAic3RlZWxibHVlIiwNCiAgICAgICAgICAgICBhbHBoYSA9IC42KSArDQogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OmNvbW1hKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnQpICsNCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLDEwKSxsYWJlbHMgPSBzY2FsZXM6OmRvbGxhcikNCmBgYA0KDQroi6XopoHlsIbotKfluIHlgLzmoLzlvI/ljJbkuLrmrKflhYPvvIzlj6/ku6Xkvb/nlKgNCg0KKiBsYWJlbCA9IHNjYWxlczo6ZG9sbGFyX2Zvcm1hdChwcmVmaXggPSAiIiwgc3VmZml4ID0gIlx1MjBhYyIpDQoNCmBgYHtyfQ0KZGYgJT4lDQogIGdncGxvdChhZXMoeGF4aXMsIHlheGlzLCBzaXplID0gcG9pbnRzaXplKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gInN0ZWVsYmx1ZSIsDQogICAgICAgICAgICAgYWxwaGEgPSAuNikgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHNjYWxlX3NpemVfY29udGludW91cygNCiAgICByYW5nZSA9IGMoMCwgMTApLA0KICAgIGxhYmVsID0gc2NhbGVzOjpkb2xsYXJfZm9ybWF0KHByZWZpeCA9ICIiLCBzdWZmaXggPSAiXHUyMGFjIikNCiAgKQ0KYGBgDQoNCiMjIyA5LjEuMiDliIbnsbvmlbDmja7ovbQNCg0K5L2/55SoYHNjYWxlX3hfZGlzY3JldGVg5oiWYHNjYWxlX3lfZGlzY3JldGVg5Ye95pWw5L+u5pS55YiG57G76L2044CCDQoNCumAiemhueWMheaLrA0KDQoqIGxpbWl0cy3kuIDkuKrlrZfnrKblkJHph48o5a6a6YeP5Y+Y6YeP5oyJ5omA6ZyA6aG65bqP55qE5rC05bmzKQ0KKiBsYWJlbHMt5qCH562+55qE5a2X56ym5ZCR6YePKOi/meS6m+e6p+WIq+eahOWPr+mAieagh+etvikNCg0KYGBge3J9DQptcGcgJT4lDQogIGdncGxvdChhZXMoY2xhc3MpKSArDQogIGdlb21fYmFyKGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKA0KICAgIGxpbWl0cyA9IGMoDQogICAgICAicGlja3VwIiwNCiAgICAgICJzdXYiLA0KICAgICAgIm1pbml2YW4iLA0KICAgICAgIm1pZHNpemUiLA0KICAgICAgImNvbXBhY3QiLA0KICAgICAgInN1YmNvbXBhY3QiLA0KICAgICAgIjJzZWF0ZXIiDQogICAgKSwNCiAgICBsYWJlbHMgPSBjKA0KICAgICAgIlBpY2t1cFxuVHJ1Y2siLA0KICAgICAgIlNwb3J0IFV0aWxpdHlcblZlaGljbGUiLA0KICAgICAgIk1pbml2YW4iLA0KICAgICAgIk1pZC1zaXplIiwNCiAgICAgICJDb21wYWN0IiwNCiAgICAgICJTdWJjb21wYWN0IiwNCiAgICAgICIyLVNlYXRlciINCiAgICApDQogICkgLT4gcDENCg0KbXBnICU+JQ0KICBjb3VudChjbGFzcykgJT4lDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoY2xhc3MsIG4pLCB5ID0gbikpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICJzdGVlbGJsdWUiKSArDQogIHNjYWxlX3hfZGlzY3JldGUoDQogICAgbGltaXRzID0gYygNCiAgICAgICIyc2VhdGVyIiwNCiAgICAgICJtaW5pdmFuIiwNCiAgICAgICJwaWNrdXAiLA0KICAgICAgInN1YmNvbXBhY3QiLA0KICAgICAgIm1pZHNpemUiLA0KICAgICAgImNvbXBhY3QiLA0KICAgICAgInN1diINCiAgICApLA0KICAgIGxhYmVscyA9IGMoDQogICAgICAiMi1TZWF0ZXIiLA0KICAgICAgIk1pbml2YW4iLA0KICAgICAgIlBpY2t1cFxuVHJ1Y2siLA0KICAgICAgIlN1YmNvbXBhY3QiLA0KICAgICAgIk1pZC1zaXplIiwNCiAgICAgICJDb21wYWN0IiwNCiAgICAgICJTcG9ydCBVdGlsaXR5XG5WZWhpY2xlIg0KICAgICkNCiAgKSArDQogIGxhYnMoeCA9ICJjbGFzcyIpLT4gcDINCg0KcDEgLyBwMg0KDQpgYGANCg0KIyMjIDkuMS4zIOaXtumXtOi9tA0KDQrkvb/nlKhgc2NhbGVfeF9kYXRlYOaIlmBzY2FsZV95X2RhdGVg5Ye95pWw5L+u5pS55pel5pyf6L2044CCDQoNCumAiemhueWMheaLrO+8mg0KDQoqIGRhdGVfYnJlYWtzIC3ooajnpLrpl7TpmpTml7bpl7TnmoTlrZfnrKbkuLLvvIzlpoLigJwy5ZGo4oCd5oiW4oCcMTDlubTigJ0NCiogZGF0ZV9sYWJlbHMgLee7meWHuuagh+etvuagvOW8j+inhOiMg+eahOWtl+espuS4sg0KDQrkuIvooajnu5nlh7rkuobml6XmnJ/lgLznmoTmoLzlvI/op4TojIPjgIINCg0KU3ltYm9sICB8CSBNZWFuaW5nICAgICAgICAgICAgICAgICAgfCAgRXhhbXBsZQ0KLS0tLS0tICB8ICAtLS0tLS0gICAgICAgICAgICAgICAgICAgfCAgLS0tLS0tDQolZAkgICAgfCAgZGF5IGFzIGEgbnVtYmVyICAgICAgICAgIHwgICgwLTMxKQkwMS0zMQ0KJWEJICAgIHwgIGFiYnJldmlhdGVkIHdlZWtkYXkgICAgICB8ICBNb24NCiVBCSAgICB8ICB1bmFiYnJldmlhdGVkIHdlZWtkYXkJICB8ICBNb25kYXkNCiVtCSAgICB8ICBtb250aCAoMDAtMTIpCSAgICAgICAgICB8ICAwMC0xMg0KJWIJICAgIHwgIGFiYnJldmlhdGVkIG1vbnRoICAgICAgICB8CSBKYW4NCiVCCSAgICB8ICB1bmFiYnJldmlhdGVkIG1vbnRoICAgICAgfAkgSmFudWFyeQ0KJXkJICAgIHwgIDItZGlnaXQgeWVhcgkgICAgICAgICAgICB8ICAwNw0KJVkJICAgIHwgIDQtZGlnaXQgeWVhcgkgICAgICAgICAgICB8ICAyMDA3DQoNCmBgYHtyfQ0KZWNvbm9taWNzICU+JSANCiAgZ2dwbG90KGFlcyhkYXRlLHVuZW1wbG95KSkgKw0KICBnZW9tX3BvaW50KGNvbCA9ICJyZWQiKSArDQogIGdlb21fbGluZShjb2wgPSAic3RlZWxibHVlIixzaXplID0gMSkgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3MgPSAiNSB5ZWFycyIsZGF0ZV9sYWJlbHMgPSAiJWItJXkiKQ0KYGBgDQoNCi0tLQ0KDQojIyA5LjIg6aKc6ImyDQoNCmdncGxvdDLlm77lvaLkuK3nmoTpu5jorqTpopzoibLmmK/lip/og73mgKfnmoTvvIzkvYbpgJrluLjlnKjop4bop4nkuIrkuI3lhbfmnInotrPlpJ/nmoTlkLjlvJXlipvjgILlubjov5DnmoTmmK/vvIzov5nlvojlrrnmmJPmlLnlj5jjgIINCg0K54m55a6a55qE6aKc6Imy5Y+v5Lul5piv5Li654K544CB57q/44CB5p2h44CB5Yy65Z+f5ZKM5paH5pys5oyH5a6a77yM5oiW5pig5bCE5Yiw5pWw5o2u6ZuG5Lit5Y+Y6YeP55qE57qn5Yir44CCDQoNCiMjIyA5LjIuMSDmiYvliqjmlLnlj5jpopzoibINCg0K6KaB5Li654K544CB57q/5oiW5paH5pys5oyH5a6a6aKc6Imy77yM6K+35Zyo6YCC5b2T55qEZ2VvbeS4reS9v+eUqGBjb2xvciA9ICJjb2xvcm5hbWUiYOmAiemhueOAguimgeS4uuadoeW9ouWbvuWSjOWMuuWfn+aMh+WumuminOiJsu+8jOivt+S9v+eUqGBmaWxsID0gImNvbG9ybmFtZSJg6YCJ6aG544CCDQoNCiogZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIikNCiogZ2VvbV9iYXIoZmlsbCA9ICJzdGVlbGJsdWUiKQ0KDQropoHkuLrlj5jph4/nmoTnuqfliKvliIbphY3popzoibLvvIzlj6/ku6Xkvb/nlKhgc2NhbGVfY29sb3JfbWFudWFsYOWSjGBzY2FsZV9maWxsX21hbnVhbGDlh73mlbDjgILliY3ogIXnlKjkuo7mjIflrpoq54K55ZKM57q/55qE6aKc6ImyKu+8jOiAjOWQjuiAheeUqOS6jirmnaHlvaLlm77lkozljLrln58q44CCDQoNCuS4i+mdouaYr+S4gOS4quekuuS+i++8jOS9v+eUqGdncGxvdDLpmYTluKbnmoQqKmRpYW1vbmRz5pWw5o2u6ZuGKirjgILmlbDmja7pm4bljIXlkKvkuoY1NCwwMDDpopflnIblvaLliIflibLpkrvnn7PnmoTku7fmoLzlkozlsZ7mgKfjgIINCg0KYGBge3J9DQpkaWFtb25kcyRjbGFyaXR5ICU+JSBmY3RfY291bnQoKQ0KYGBgDQoNCmBgYHtyfQ0KIyMg5omL5Yqo5oyH5a6a6aKc6Imy57qn5YirDQpkaWFtb25kcyAlPiUgDQogIGdncGxvdChhZXMoY3V0LGZpbGwgPSBjbGFyaXR5KSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZGFya3JlZCIsICJzdGVlbGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGFya2dyZWVuIiwgImdvbGQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJicm93biIsICJwdXJwbGUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ3JleSIsICJraGFraTQiKSkNCg0KZGlhbW9uZHMgJT4lIA0KICBnZ3Bsb3QoYWVzKGN1dCxmaWxsID0gY2xhcml0eSkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZGFya3JlZCIsICJzdGVlbGJsdWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZGFya2dyZWVuIiwgImdvbGQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJicm93biIsICJwdXJwbGUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ3JleSIsICJraGFraTQiKSkNCg0KZGlhbW9uZHMgJT4lIA0KICBnZ3Bsb3QoYWVzKGN1dCxmaWxsID0gY2xhcml0eSkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoImRhcmtyZWQiLCAic3RlZWxibHVlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRhcmtncmVlbiIsICJnb2xkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYnJvd24iLCAicHVycGxlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdyZXkiLCAia2hha2k0IikpDQpgYGANCg0K5aaC5p6c5L2g5ZKM5oiR5LiA5qC35Zyo5a6h576O5LiK5pyJ5Zuw6Zq+77yM5Y+m5LiA56eN6YCJ5oup5piv5L2/55So6aKE5a6a5LmJ55qE6LCD6Imy5p2/44CCDQoNCiMjIyA5LjIuMiDpopzoibLosIPoibLmnb8NCg0K5ZyoUuS4reacieiuuOWkmumihOWumuS5ieeahOiwg+iJsuadv+WPr+eUqOOAgg0KDQojIyMjIDkuMi4yLjEgUkNvbG9yQnJld2VyDQoNCuacgOa1geihjOeahOiwg+iJsuadv+WPr+iDveaYryoqQ29sb3JCcmV3ZXLosIPoibLmnb8qKuOAgg0KDQpgYGB7cixmaWcuaGVpZ2h0PTZ9DQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNClJDb2xvckJyZXdlcjo6ZGlzcGxheS5icmV3ZXIuYWxsKCkNCmBgYA0KDQrmgqjlj6/ku6Xkvb/nlKhgc2NhbGVfY29sb3JfYnJld2VyYOWSjGBzY2FsZV9maWxsX2JyZXdlcmDlh73mlbDmjIflrprov5nkupvosIPoibLmnb/jgIINCg0KYGBge3J9DQpkaWFtb25kcyAlPiUgDQogIGdncGxvdChhZXMoY3V0LGZpbGwgPSBjbGFyaXR5KSkgKw0KICBnZW9tX2JhcigpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJEYXJrMiIpDQoNCmRpYW1vbmRzICU+JSANCiAgZ2dwbG90KGFlcyhjdXQsZmlsbCA9IGNsYXJpdHkpKSArDQogIGdlb21fYmFyKCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKw0KICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMCwwKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDApKQ0KYGBgDQoNCuWQkei/meS6m+WHveaVsOa3u+WKoGBkaXJlY3Rpb24gPSAtMWDlsIbnv7vovazosIPoibLmnb/kuK3popzoibLnmoTpobrluo/jgIINCg0KIyMjIyA5LjIuMi4yIFZpcmlkaXMNCg0KKip2aXJpZGlzKirmmK/lj6bkuIDkuKrmtYHooYznmoTosIPoibLmnb/jgIINCg0K6L+e57utc2NhbGVz77yaDQoNCiogc2NhbGVfZmlsbF92aXJpZGlzX2MNCiogc2NhbGVfY29sb3JfdmlyaWRpc19jDQoNCuemu+aVo3NjYWxlczoNCg0KKiBzY2FsZV9maWxsX3ZpcmlkaXNfZA0KKiBzY2FsZV9jb2xvcl92aXJpZGlzX2QNCg0KYGBge3J9DQpsaWJyYXJ5KHZpcmlkaXMpDQpnZ3Bsb3QoZGlhbW9uZHMsYWVzKHggPSBjdXQsZmlsbCA9IGNsYXJpdHkpKSArDQogIGdlb21fYmFyKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpDQpgYGANCg0KIyMjIyA5LjIuMi4zIOWFtuS7luiwg+iJsuadvw0KDQrlhbbku5bpnIDopoHmjqLntKLnmoTosIPoibLmnb/ljIXmi6xgZHV0Y2htYXN0ZXJzYOOAgWBnZ3BvbW9sb2dpY2FsYOOAgWBMYUNyb2l4Q29sb1Jg44CBYG5vcmRg44CBYG9jaFJlYOOAgWBwYWxldHRldG93bmDjgIFgcGFsc2DjgIEgYHJjYXJ0b2NvbG9yYOWSjGB3ZXNhbmRlcnNvbmDjgILlpoLmnpzmgqjmg7Pmn6XnnIvmiYDmnInpgInpobnmnb/pgInpobko5oiW5Yeg5LmO5omA5pyJ6YCJ6aG5Ke+8jOivt+afpeeciyoqcGFsZXR0ZXLljIUqKuOAgg0KDQropoHkuobop6PmnInlhbPpopzoibLop4TojIPnmoTmm7TlpJrkv6Hmga/vvIzor7flj4LpmIXmnInlhbNnZ3Bsb3Qy6aKc6Imy55qEW1IgQ29va3BhZ2XpobXpnaJdKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzL0NvbG9yc18oZ2dwbG90MikvKeOAguS5n+WPr+S7peWPguiAg+i/meacrOS5puS4reeahCoq6aKc6Imy6YCJ5oup5bu66K6uKirov5npg6jliIblhoXlrrnjgIINCg0KLS0tDQoNCiMjIDkuMyDngrnnur8NCg0KIyMjIDkuMy4xIFBvaW50cw0KDQrlr7nkuo5nZ3Bsb3Qy5Zu+77yM6buY6K6k55qE54K55piv5LiA5Liq5aGr5YWF55qE5ZyG44CC6KaB5oyH5a6a5LiN5ZCM55qE5b2i54q277yM6K+35L2/55SoYGdlb21fcG9pbnTlh73mlbBg5Lit55qEc2hhcGUgPSAj6YCJ6aG544CC6KaB5bCG5b2i54q25pig5bCE5Yiw5YiG57G75Y+Y6YeP55qE57qn5Yir77yM6K+35L2/55SoYGFlc+WHveaVsGDkuK3nmoRzaGFwZSA9IHZhcmlhYmxlbmFtZemAiemhueOAgg0KDQrkvovlpoLvvJoNCg0KKiBnZW9tX3BvaW50KHNoYXBlID0gMSkNCiogZ2VvbV9wb2ludChhZXMoc2hhcGUgPSBzZXgpKQ0KDQrlj6/nlKjnmoTlvaLnirblpoLkuIvooajmiYDnpLrjgIINCg0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygic2hhcGVzMS0xLnBuZyIpDQpgYGANCg0K5b2i54q2MjHliLAyNuaPkOS+m+S6huWhq+WFheiJsuWSjOi+ueahhuiJsuOAgg0KDQojIyMgOS4zLjIgTGluZXMNCg0K6buY6K6k55qE6KGM57G75Z6L5piv5a6e57q/44CC6KaB5pu05pS5bGluZXR5cGXvvIzor7fkvb/nlKhgZ2VvbV9saW5l5Ye95pWwYOS4reeahGxpbmV0eXBlID0gI+mAiemhueOAguimgeWwhmxpbmV0eXBl5pig5bCE5Yiw57G75Yir5Y+Y6YeP55qE57qn5Yir77yM6K+35L2/55SoYGFlc+WHveaVsGDkuK3nmoRsaW5ldHlwZSA9IHZhcmlhYmxlbmFtZemAiemhueOAgg0KDQrkvovlpoLvvJoNCg0KKiBnZW9tX2xpbmUobGluZXR5cGUgPSAxKQ0KKiBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlID0gc2V4KSkNCg0KYGBge3J9DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiTGluZXMyLTEucG5nIikNCmBgYA0KDQotLS0NCg0KIyMgOS40IOWtl+S9kw0KDQpS5LiN5aSq5pSv5oyB5a2X5L2T77yM5L2G5piv6YCa6L+H5LiA5Lqb5bel5L2c77yM5oKo5Y+v5Lul5pu05pS55Zu+5b2i5Lit5Ye6546w55qE5a2X5L2T44CC6aaW5YWI77yM5oKo6ZyA6KaB5a6J6KOF5ZKM6K6+572uZXh0cmFmb2505YyF44CCDQoNCmBgYHtyfQ0KbGlicmFyeShleHRyYWZvbnQpDQojIGV4dHJhZm9udDo6Zm9udF9pbXBvcnQoKQ0KYGBgDQpgYGB7cn0NCiMgc2VlIHdoYXQgZm9udHMgYXJlIG5vdyBhdmFpbGFibGUNCmZvbnRzKCkNCmBgYA0KDQrkvb/nlKhgdGhlbWVz5Li76aKY5Ye95pWwYOS4reeahOaWh+acrOmAiemhueW6lOeUqCoq5paw5a2X5L2TKirjgIINCg0KYGBge3J9DQpsaWJyYXJ5KGV4dHJhZm9udCkNCmdncGxvdChtcGcsIGFlcyh4ID0gZGlzcGwsIHk9aHd5KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBsYWJzKHRpdGxlID0gIkRpcGxhY2VtZW50IGJ5IEhpZ2h3YXkgTWlsZWFnZSIsDQogICAgICAgc3VidGl0bGUgPSAiTVBHIGRhdGFzZXQiKSArDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLCBmYW1pbHkgPSAiVGltZXMgTmV3IFJvbWFuIiksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KYGBgDQoNCi0tLQ0KDQojIyA5LjUgTGVnZW5kDQoNCuWcqGdncGxvdDLkuK3vvIzlvZPlj5jph4/ooqvmmKDlsITliLAq6aKc6ImyKuOAgSrloavlhYUq44CBKue6v+WeiyrjgIEq5b2i54q2KuOAgSrlpKflsI8q5oiWKmFscGhhKuaXtu+8jOWwhuiHquWKqOWIm+W7uuWbvuS+i+OAguS9oOWvuei/meS6m0xlZ2VuZOeahOWkluinguWSjOaEn+inieacieW+iOWkp+eahOaOp+WItuadg+OAgumAmuW4uOmAmui/h+S4u+mimOWHveaVsOWSjC/miJZsYWJz5Ye95pWw6L+b6KGM5L+u5pS544CC6L+Z6YeM5pyJ5LiA5Lqb5pyA5Y+X5qyi6L+O55qE44CCDQoNCiMjIyA5LjUuMSBMZWdlbmTkvY3nva4NCg0K5Zu+5L6L5Y+v5Lul5Ye6546w5Zyo5Zu+5Lit55qE5Lu75L2V5Zyw5pa544CC6buY6K6k5oOF5Ya15LiL77yMKirlroPkvY3kuo7lj7PkvqcqKuOAguaCqOWPr+S7peS9v+eUqO+8mg0KDQp0aGVtZShsZWdlbmQucG9zaXRpb24gPSBwb3NpdGlvbik6DQoNCiog4oCcdG9w4oCdCWFib3ZlIHRoZSBwbG90IGFyZWENCiog4oCccmlnaHTigJ0JcmlnaHQgb2YgdGhlIHBsb3QgYXJlYQ0KKiDigJxib3R0b23igJ0JYmVsb3cgdGhlIHBsb3QgYXJlYQ0KKiDigJxsZWZ04oCdCWxlZnQgb2YgdGhlIHBsb3QgYXJlYSBjKHgsIHkpCXdpdGhpbiB0aGUgcGxvdCBhcmVhLiBUaGUgeCBhbmQgeSB2YWx1ZXMgbXVzdCByYW5nZSBiZXR3ZWVuIDAgYW5kIDEuIGMoMCwwKSByZXByZXNlbnRzIChsZWZ0LCBib3R0b20pIGFuZCBjKDEsMSkgcmVwcmVzZW50cyAocmlnaHQsIHRvcCkuDQoqIOKAnG5vbmXigJ0Jc3VwcHJlc3MgdGhlIGxlZ2VuZA0KDQrkvovlpoLvvIzopoHlsIblm77kvovnva7kuo7pobbpg6jvvIzor7fkvb/nlKjku6XkuIvku6PnoIHjgIINCg0KYGBge3J9DQptcGcgJT4lIA0KICBnZ3Bsb3QoYWVzKGRpc3BsLGh3eSxjb2wgPSBjbGFzcykpICsNCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKw0KICBsYWJzKHRpdGxlID0gIkRpcGxhY2VtZW50IGJ5IEhpZ2h3YXkgTWlsZWFnZSIpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLA0KICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLjUpICAjIExlZ2VuZCB0aXRsZSBhbGlnbg0KYGBgDQoNCiMjIyA5LjUuMiBMZWdlbmTmoIfpopgNCg0K5oKo5Y+v5Lul6YCa6L+HYGxhYnPlh73mlbBg5pu05pS55Zu+5L6L5qCH6aKY44CC5L2/55SoKuminOiJsirjgIEq5aGr5YWFKuOAgSrlpKflsI8q44CBKuW9oueKtirjgIEq57q/5p2h57G75Z6LKuWSjCphbHBoYSrmnaXnu5nnm7jlupTnmoTlm77kvovotYvkuojmlrDnmoTmoIfpopjjgIIqKuWbvuS+i+agh+mimOeahOWvuem9kCoq5piv6YCa6L+HYGxlZ2VuZC50aXRsZS5hbGlnbmDmnaXmjqfliLbnmoTjgILlr7npvZDpgInpobnlnKjkuLvpopjlh73mlbDjgIIoMCA95bemLDAuNSA95Lit5b+DLDEgPSDlj7MpDQoNCmBgYHtyfQ0KIyBjaGFuZ2UgdGhlIGRlZmF1bHQgbGVnZW5kIHRpdGxlDQpnZ3Bsb3QobXBnLCANCiAgICAgICBhZXMoeCA9IGRpc3BsLCB5PWh3eSwgY29sb3IgPSBjbGFzcykpICsNCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKw0KICBsYWJzKHRpdGxlID0gIkRpcGxhY2VtZW50IGJ5IEhpZ2h3YXkgTWlsZWFnZSIsDQogICAgICAgY29sb3IgPSAiQXV0b21vYmlsZVxuQ2xhc3MiKSArIA0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQudGl0bGUuYWxpZ249MC41KQ0KDQptcGcgJT4lIA0KICBnZ3Bsb3QoYWVzKGRpc3BsLGh3eSxjb2wgPSBjbGFzcykpICsNCiAgZ2VvbV9wb2ludChzaXplID0gNCkgKw0KICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lID0gIkF1dG9tb2JpbGVcbkNsYXNzIikgKw0KICB0aGVtZShsZWdlbmQudGl0bGUuYWxpZ24gPSAwLjUpICsNCiAgbGFicyh0aXRsZSA9ICJEaXBsYWNlbWVudCBieSBIaWdod2F5IE1pbGVhZ2UiKQ0KYGBgDQoNCi0tLQ0KDQojIyA5LjYgTGFiZWxzDQoNCuagh+etvuaYr+S9v+WbvuW9ouaYk+S6jueQhuino+eahOWFs+mUruWboOe0oCzlj6/ku6Xkvb/nlKhgbGFic+WHveaVsGDmt7vliqDjgILlj6/nlKjnmoTpgInpobnlpoLkuIvmiYDnpLoNCg0KKiB0aXRsZS0JICAgIG1haW4gdGl0bGUNCiogc3VidGl0bGUtCXN1YnRpdGxlDQoqIGNhcHRpb24tCWNhcHRpb24gKGJvdHRvbSByaWdodCBieSBkZWZhdWx0KQ0KKiB4LQlob3Jpem9udGFsIGF4aXMNCiogeS0JdmVydGljYWwgYXhpcw0KKiBjb2xvci0JY29sb3IgbGVnZW5kIHRpdGxlDQoqIGZpbGwtCWZpbGwgbGVnZW5kIHRpdGxlDQoqIHNpemUtCXNpemUgbGVnZW5kIHRpdGxlDQoqIGxpbmV0eXBlLQlsaW5ldHlwZSBsZWdlbmQgdGl0bGUNCiogc2hhcGUtCXNoYXBlIGxlZ2VuZCB0aXRsZQ0KKiBhbHBoYS0JdHJhbnNwYXJlbmN5IGxlZ2VuZCB0aXRsZQ0KKiBzaXplLQlzaXplIGxlZ2VuZCB0aXRsZQ0KDQpgYGB7cn0NCmdncGxvdChtcGcsIA0KICAgICAgIGFlcyh4ID0gZGlzcGwsDQogICAgICAgICAgIHk9aHd5LCANCiAgICAgICAgICAgY29sb3IgPSBjbGFzcywNCiAgICAgICAgICAgc2hhcGUgPSBmYWN0b3IoeWVhcikpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIA0KICAgICAgICAgICAgIGFscGhhID0gLjUpICsNCiAgbGFicyh0aXRsZSA9ICJNaWxlYWdlIGJ5IGVuZ2luZSBkaXNwbGFjZW1lbnQiLA0KICAgICAgIHN1YnRpdGxlID0gIkRhdGEgZnJvbSAxOTk5IGFuZCAyMDA4IiwNCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogRVBBIChodHRwOi8vZnVlbGVjb25vbXkuZ292KSIsDQogICAgICAgeCA9ICJFbmdpbmUgZGlzcGxhY2VtZW50IChsaXRyZXMpIiwNCiAgICAgICB5ID0gIkhpZ2h3YXkgbWlsZXMgcGVyIGdhbGxvbiIsDQogICAgICAgY29sb3IgPSAiQ2FyIENsYXNzIiwNCiAgICAgICBzaGFwZSA9ICJZZWFyIikgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwNCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiVGltZXMgTmV3IFJvbWFuIikpDQpgYGANCg0K6L+Z5LiN5piv5LiA5Liq5b6I5aW955qE5Zu+4oCU4oCU5a6D5aSq5b+Z5LqG77yM5L2/5b6X5qih5byP55qE6K+G5Yir6Z2e5bi45Zuw6Zq+44CC5pyA5aW95piv5bCGKip5ZWFy5Y+Y6YeP5YiG6Z2iKirjgILotovlir/nur/kuZ/kvJrmnInluK7liqnjgIINCg0KYGBge3J9DQpnZ3Bsb3QobXBnLCANCiAgICAgICBhZXMoeCA9ZGlzcGwsIA0KICAgICAgICAgICB5PWh3eSkpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sID0gY2xhc3MpLA0KICAgICAgICAgICAgIHNpemUgPSAzLCANCiAgICAgICAgICAgICBhbHBoYSA9IC41KSArDQogIGdlb21fc21vb3RoKGFlcyhjb2wgPSBjbGFzcyksbWV0aG9kID0gImxtIixzZSA9IEZBTFNFKSArDQogIGxhYnModGl0bGUgPSAiTWlsZWFnZSBieSBlbmdpbmUgZGlzcGxhY2VtZW50IiwNCiAgICAgICBzdWJ0aXRsZSA9ICJEYXRhIGZyb20gMTk5OSBhbmQgMjAwOCIsDQogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IEVQQSAoaHR0cDovL2Z1ZWxlY29ub215LmdvdikiLA0KICAgICAgIHggPSAiRW5naW5lIGRpc3BsYWNlbWVudCAobGl0cmVzKSIsDQogICAgICAgeSA9ICJIaWdod2F5IG1pbGVzIHBlciBnYWxsb24iLA0KICAgICAgIGNvbG9yID0gIkNhciBDbGFzcyIsDQogICAgICAgc2hhcGUgPSAiWWVhciIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksDQogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiIpKSArDQogIGZhY2V0X3dyYXAofmZhY3Rvcih5ZWFyKSkNCmBgYA0KDQotLS0NCg0KIyMgOS43IEFubm90YXRpb25zDQoNCioq5rOo6YeKKirmmK/mt7vliqDliLDlm77kuK3nmoTkv6Hmga/vvIznlKjkuo7nqoHlh7rph43opoHnmoTngrnjgIINCg0KIyMjIDkuNy4xIOa3u+WKoOaWh+acrA0KDQrlkJHlm77kuK3mt7vliqDmlofmnKzmnInkuKTkuKrkuLvopoHljp/lm6DjgILkuIDkuKrmmK/or4bliKtnZW9t55qE5pWw5a2X6LSo6YeP44CC5L6L5aaC77yM5oiR5Lus5Y+v6IO95biM5pyb5Zyo5pWj54K55Zu+5Lit5L2/55So5qCH562+5p2l5qCH6K+G54K577yM5oiW6ICF5Zyo5p+x54q25Zu+5Lit5qCH6K6w5p2h5b2i5Zu+55qE6auY5bqm44CC5Y+m5LiA5Liq5Y6f5Zug5piv5o+Q5L6b6aKd5aSW55qE5L+h5oGv77yM5oiR5Lus5Y+v6IO95oOz6KaB5re75Yqg5YWz5LqO5pWw5o2u55qE5rOo6YeK77yM5oyH5Ye65byC5bi45YC8562J562J44CCDQoNCiMjIyMgOS43LjEuMSBMYWJsaW5n5YC8DQoNCuiAg+iZkeS7peS4i+aVo+eCueWbvu+8jOWfuuS6jm10Y2Fyc+aVsOaNrumbhuS4reeahGNhcuaVsOaNruOAgg0KDQpgYGB7cn0NCm10Y2FycyAlPiUgDQogIGdncGxvdChhZXMod3QsbXBnKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKQ0KYGBgDQoNCuiuqeaIkeS7rOeUqOWug+aJgOS7o+ihqOeahOaxvei9pueahOWQjeensOadpeagh+iusOavj+S4queCueOAgg0KDQpgYGB7cn0NCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsNCiAgZ2VvbV90ZXh0KGxhYmVsID0gcm93Lm5hbWVzKG10Y2FycykpICAjIOivtOWunuivne+8jOS4jeWlveeciw0KYGBgDQoNCmBgYHtyfQ0KIyBzY2F0dGVycGxvdCB3aXRoIG5vbi1vdmVybGFwcGluZyBsYWJlbHMNCmRhdGEobXRjYXJzKQ0KbGlicmFyeShnZ3JlcGVsKQ0KZ2dwbG90KG10Y2FycywgYWVzKHggPSB3dCwNCiAgICAgICAgICAgICAgICAgICB5ID0gbXBnKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGdlb21fdGV4dF9yZXBlbChsYWJlbCA9IHJvdy5uYW1lcyhtdGNhcnMpKQ0KDQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCANCiAgICAgICAgICAgICAgICAgICB5ID0gbXBnKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyKSArDQogIGdlb21fbGFiZWxfcmVwZWwobGFiZWwgPSByb3cubmFtZXMobXRjYXJzKSkNCmBgYA0KDQojIyMjIDkuNy4xLjIg5re75Yqg6ZmE5Yqg5L+h5oGvDQoNCuaIkeS7rOWPr+S7peS9v+eUqCoq5rOo6YeK5Ye95pWwKirlsIbmlofmnKzmlL7lnKjlm77ooajnmoTku7vkvZXkvY3nva4NCg0KYGBgDQphbm5vdGF0ZSgidGV4dCIsIA0KICAgICAgICAgeCwgeSwgDQogICAgICAgICBsYWJlbCA9ICJTb21lIHRleHQiLCANCiAgICAgICAgIGNvbG9yID0gImNvbG9ybmFtZSIsIA0KICAgICAgICAgc2l6ZT10ZXh0c2l6ZSkNCmBgYA0K5YW25LiteOWSjHnmmK/mlL7nva7mlofmnKznmoTlnZDmoIfjgIIqKuminOiJsioq5ZKMKirlpKflsI8qKuWPguaVsOaYr+WPr+mAieeahOOAgum7mOiupOaDheWGteS4i++8jOaWh+acrOWwhuWxheS4reOAguS9v+eUqCoqaGp1c3QqKuWSjCoqdmp1c3QqKuadpeabtOaUueWvuem9kOaWueW8j+OAgg0KDQoqIGhqdXN0IDAgPSBsZWZ0IGp1c3RpZmllZCwgMC41ID0gY2VudGVyZWQsIGFuZCAxID0gcmlnaHQgY2VudGVyZWQuDQoqIHZqdXN0IDAgPSBhYm92ZSwgMC41ID0gY2VudGVyZWQsIGFuZCAxID0gYmVsb3cuDQoNCue7p+e7reWJjei+ueeahOS+i+WtkDogDQoNCmBgYHtyfQ0KZGF0YShtdGNhcnMpDQpsaWJyYXJ5KGdncmVwZWwpDQp0eHQgPC0gcGFzdGUoIlRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBjYXIgd2VpZ2h0IiwNCiAgICAgICAgICAgICAgImFuZCBtaWxlYWdlIGFwcGVhcnMgdG8gYmUgcm91Z2hseSBsaW5lYXIiLA0KICAgICAgICAgICAgICBzZXAgPSAiXG4iKQ0KdHh0DQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCANCiAgICAgICAgICAgICAgICAgICB5ID0gbXBnKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gInJlZCIsDQogICAgICAgICAgICAgc2l6ZSA9IDMpICsNCiAgZ2VvbV9sYWJlbF9yZXBlbChsYWJlbCA9IHJvdy5uYW1lcyhtdGNhcnMpLCANCiAgICAgICAgICAgICAgICAgICBzaXplPTMpICsNCiAgZ2dwbG90Mjo6YW5ub3RhdGUoInRleHQiLCANCiAgICAgICAgICAgICAgICAgICAgNiwgMzAsIA0KICAgICAgICAgICAgICAgICAgICBsYWJlbD10eHQsDQogICAgICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsDQogICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMSkNCmBgYA0KDQojIyMgOS43LjIg5re75Yqg5rC05bmz5ZKM5Z6C55u057q/DQoNCioq5rC05bmz5ZKM5Z6C55u055qE57q/Kirlj6/ku6Xmt7vliqDkvb/nlKg6DQoNCuWFtuS4rWHmmK956L205LiK55qE5pWw5a2X77yMYuaYr3jovbTkuIrnmoTmlbDlrZfjgILlhbbku5bpgInpobnljIXmi6znur/lnovlkozpopzoibLjgIINCg0KKiBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBhKQ0KKiBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBiKQ0KDQpgYGB7cn0NCiMgYWRkIGFubm90YXRpb24gbGluZSBhbmQgdGV4dCBsYWJlbA0KbWluX2N0eSA8LSBtaW4obXBnJGN0eSkNCm1lYW5faHd5IDwtIG1lYW4obXBnJGh3eSkNCm1lYW5fY3R5IDwtIG1lYW4obXBnJGN0eSkNCiMg5YWI57uY5Yi2566A5Y2V5Zu+77yM5YaN6YCQ5riQ5L+u5pS557uG6IqCDQpnZ3Bsb3QobXBnLCANCiAgICAgICBhZXMoeCA9IGN0eSwgDQogICAgICAgICAgIHk9aHd5LCANCiAgICAgICAgICAgY29sb3I9ZHJ2KSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMTAsNDUsNSksDQogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMTAsNDUsNSkpICsNCiAgbGFicyhjb2xvciA9ICJEcnYiKSArDQogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKGxhYmVscyA9IGMoIjQiLCJGIiwiUiIpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW5faHd5LA0KICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2dwbG90Mjo6YW5ub3RhdGUoInRleHQiLCANCiAgICAgICAgICAgbWluX2N0eSwgDQogICAgICAgICAgIG1lYW5faHd5ICsgMS41LCANCiAgICAgICAgICAgbGFiZWwgPSAiTWVhbiIsDQogICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiKSArDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW5fY3R5LA0KICAgICAgICAgICAgIGNvbG9yID0gImRhcmtyZWQiLA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2dwbG90Mjo6YW5ub3RhdGUoInRleHQiLCANCiAgICAgICAgICAgbWVhbl9jdHkgKyAxLjUsIA0KICAgICAgICAgICBtYXgobXBnJGh3eSksIA0KICAgICAgICAgICBsYWJlbCA9ICJNZWFuIiwNCiAgICAgICAgICAgY29sb3IgPSAiZGFya3JlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJNaWxlYWdlIGJ5IGRyaXZlIHR5cGUiLA0KICAgICAgIHggPSAiQ2l0eSBtaWxlcyBwZXIgZ2FsbG9uIiwNCiAgICAgICB5ID0gIkhpZ2h3YXkgbWlsZXMgcGVyIGdhbGxvbiIsDQogICAgICAgY29sb3IgPSAiRHJpdmUiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQ0KDQpgYGANCg0K5oiR5Lus6L+Y5Yqg5LiK5LiA5p2h5q+P5Yqg5LuR5bmz5Z2H5Z+O5biC6Iux6YeM5pWw55qE5Z6C55u057q/44CC5Zyo5Lu75L2V5oOF5Ya15LiL77yMKirmgLvmmK/ku6Xmn5Dnp43mlrnlvI/moIforrDms6jph4rooYwqKuOAguWQpuWImeivu+iAheWwhuS4jeefpemBk+S7luS7rOeahOaEj+aAneOAgg0KDQojIyMgOS43LjMg6auY5Lqu5p+Q5LiA57uEDQoNCuacieaXtuaCqOW4jOacm+WcqOWbvuS4reeqgeWHuuaYvuekuuWNleS4que7hOOAgioqZ2doaWdobGlnaHTljIUqKuS4reeahGBnZ2hpZ2hsaWdodOWHveaVsGDlsLHmmK/kuLrmraTogIzorr7orqHnmoTjgIINCg0KYGBge3J9DQojIGhpZ2hsaWdodCBhIHNldCBvZiBwb2ludHMNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ2doaWdobGlnaHQpDQpnZ3Bsb3QobXBnLCBhZXMoeCA9IGN0eSwgeSA9IGh3eSkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgIHNpemU9MykgKw0KICBnZ2hpZ2hsaWdodChjbGFzcyA9PSAibWlkc2l6ZSIpDQpgYGANCg0KYGBge3J9DQptcGcgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBjbGFzcykpICsNCiAgZ2VvbV9iYXIoKQ0KIyBoaWdobGlnaHQgYSBzaW5nbGUgYmFyDQpsaWJyYXJ5KGdnaGlnaGxpZ2h0KQ0KZ2dwbG90KG1wZywgYWVzKHggPSBjbGFzcykpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJyZWQiKSArDQogIGdnaGlnaGxpZ2h0KGNsYXNzID09ICJtaWRzaXplIikNCmBgYA0KDQrov5nph4zmsqHmnInku4DkuYjmmK/kuI3og73nlKjln7rmnKzlm77lvaLmnaXlrozmiJDnmoTvvIzkvYbmmK9nZ3Bsb3Qy5pu05pa55L6/44CCDQoNCi0tLQ0KDQojIyA5Ljgg5Li76aKYDQoNCmdncGxvdDLkuLvpopjmjqfliLbmg4XoioLkuK3miYDmnInkuI7mlbDmja7ml6DlhbPnmoTnu4Tku7bnmoTlpJbop4LjgILmgqjlj6/ku6XpgJrov4fmm7TmlLnkuLvpopjnmoTlhYPntKDmnaXmm7TmlLnlm77lvaLnmoTlpJbop4LlkozmhJ/op4njgIINCg0KIyMjIDkuOC4xIOaUueWPmOS4u+mimOWFg+e0oA0KDQrkuLvpopjlh73mlbDnlKjkuo7kv67mlLnkuLvpopjnmoTlkITkuKrnu4Tku7bjgILkuLvpopjlh73mlbDnmoTlj4LmlbDlnKjnlLHlnKjnur/luK7liqnlvIDlj5HnmoRb5aSH5b+Y5Y2VXShodHRwczovL3JrYWJhY29mZi5naXRodWIuaW8vZGF0YXZpcy9tb2RpZnlpbmd0aGVtZXMucGRmKeS4rei/m+ihjOS6huaPj+i/sOOAgg0KDQrogIPomZHkuIvlm77jgILlroPmmL7npLrkuoYyMDA4LTIwMDnlubTmn5DmiYDlpKflrabmjInnrYnnuqdyYW5r5ZKM5a2m56eRZGlzY2lwbGluZeWIkuWIhueahOeUt+Wls+aVmeWRmOS6uuaVsOOAguaVsOaNruadpea6kOS6jlNhbGFyaWVz5pWw5o2u6ZuG44CCDQoNCmBgYHtyfQ0KIyBjcmVhdGUgZ3JhcGgNCmRhdGEoU2FsYXJpZXMsIHBhY2thZ2UgPSAiY2FyRGF0YSIpDQpwIDwtIGdncGxvdChTYWxhcmllcywgDQogICAgICAgICAgICBhZXMoeCA9IHJhbmssIGZpbGwgPSBzZXgpKSArDQogIGdlb21fYmFyKCkgDQpwDQoNCmRhdGEoU2FsYXJpZXMsIHBhY2thZ2UgPSAiY2FyRGF0YSIpDQpwIDwtIGdncGxvdChTYWxhcmllcywgYWVzKHggPSByYW5rLCBmaWxsID0gc2V4KSkgKw0KICBnZW9tX2JhcigpICsNCiAgZmFjZXRfd3JhcCh+ZGlzY2lwbGluZSkgKw0KICBsYWJzKHRpdGxlID0gIkFjYWRlbWljIFJhbmsgYnkgR2VuZGVyIGFuZCBEaXNjaXBsaW5lIiwNCiAgICAgICB4ID0gIlJhbmsiLA0KICAgICAgIHkgPSAiRnJlcXVlbmN5IiwNCiAgICAgICBmaWxsID0gIkdlbmRlciIpDQpwDQpgYGANCg0K6K6p5oiR5Lus5pS55Y+Y5LiA5LiL5Li76aKY44CCDQoNCiog5bCG5qCH562+5paH5pys5LuO6buR6Imy5pu05pS55Li65rW35Yab6JOdDQoqIOWwhumdouadv+iDjOaZr+minOiJsuS7jueBsOiJsuaUueS4uueZveiJsg0KKiDkuLrkuLvopoHnmoR56L20572R5qC857q/5re75Yqg57qv54Gw6Imy57q/DQoqIOa3u+WKoOWwj3novbTnvZHmoLznur/nmoTomZrnur/ngbDnur8NCiog5raI6ZmkeOi9tOe9keagvOe6vw0KKiDlsIbplb/mnaHog4zmma/popzoibLmlLnkuLrnmb3oibLvvIzlubbluKbmnInngbDoibLovrnmoYYNCg0KYGBge3J9DQpwICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJuYXZ5IiksICMg5bCG5qCH562+5paH5pys5LuO6buR6Imy5pu05pS55Li65rW35Yab6JOdDQogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpLCAjIOWwhumdouadv+iDjOaZr+minOiJsuS7jueBsOiJsuaUueS4uueZveiJsg0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleSIpLCAjIOS4uuS4u+imgeeahHnovbTnvZHmoLznur/mt7vliqDnuq/ngbDoibLnur8NCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyZXkiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwgIyDmtojpmaR46L20572R5qC857q/DQogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yPSJncmV5IikpDQpgYGANCg0K5ZOH77yM6L+Z55yL6LW35p2l5b6I57Of57OV77yM5L2G5L2g5piO55m95oiR55qE5oSP5oCd44CCDQoNCiMjIyMgOS44LjEuMSBnZ1RoZW1lQXNzaXN0DQoNCuWmguaenOaCqOaDs+S9v+eUqEdVSeWIm+W7uuiHquW3seeahOS4u+mimO+8jOivt+afpeeci2BnZ1RoZW1lQXNzaXN0YOOAguWuieijheWMheWQju+8jGBSU3R1ZGlvYOS4reeahGBBZGRpbnNg5LiL5bCG5Ye6546w5LiA5Liq5paw6I+c5Y2V6aG544CCDQoNCueqgeWHuuaYvuekuuWIm+W7uuWbvuW9oueahOS7o+egge+8jOeEtuWQjuS7jkFkZGluc+S4i+aLieiPnOWNleS4remAieaLqSoqZ2dUaGVtZUFzc2lzdOmAiemhuSoq44CC5oKo5Y+v5Lul5L2/55So5oyH5ZCRLeWNleWHu+adpeabtOaUueS4u+mimOeahOiuuOWkmueJueaAp+OAguW9k+aCqOWujOaIkOaXtu+8jOS4u+mimOS7o+eggeWwhuiiq+mZhOWKoOWIsOaCqOeahOWbvuW9ouS7o+eggeS4reOAgg0KDQojIyMgOS44LjIgUHJlLXBhY2thZ2Vk5Li76aKYDQoNCuaIkeS4jeaYr+S4gOS4quW+iOWlveeahOiJuuacr+Wutijlj6rnnIvmnIDlkI7kuIDkuKrkvovlrZAp77yM5omA5Lul5oiR57uP5bi45a+75om+5Y+v5Lul5bqU55So5Yiw5oiR55qE5Zu+6KGo5Lit55qE6aKE5YWI5YyF6KOF5aW955qE5Li76aKY44CC5pyJ5b6I5aSa5Y+v55So55qE5Li76aKYLOacieS6m2dncGxvdDLpmYTluKbjgILlhbbkuK3ljIXmi6xgdGhlbWVfY2xhc3NpY2DjgIFgdGhlbWVfZGFya2DjgIFgdGhlbWVfZ3JheWDjgIFgdGhlbWVfZ3JleWDjgIFgdGhlbWVfbGlnaHRgIGB0aGVtZV9saW5lZHJhd2DjgIFgdGhlbWVfbWluaW11bWDlkoxgdGhlbWVfdm9pZGDjgILlnKjmnKzkuabkuK3vvIzmiJHku6znu4/luLjkvb/nlKhgdGhlbWVfbWluaW11bWDjgILlhbbku5bnmoTlj6/ku6XpgJrov4fpmYTliqDljIXojrflvpfjgIINCg0KIyMjIyA5LjguMi4xIGdndGhlbWVzDQoNCioqZ2d0aGVtZXMgcGFja2FnZSoq6KaG55uWMTnnp43kuLvpoph0aGVtZXMuDQoNClRoZW1lCSAgICAgICAgICAgICAgICB8ICBEZXNjcmlwdGlvbg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0gIHwgIC0tLS0tLS0tLS0tLS0tLS0tLS0tIA0KdGhlbWVfYmFzZQkgICAgICAgICAgfCAgVGhlbWUgQmFzZQ0KdGhlbWVfY2FsYyAgICAgIAkgICAgfCAgVGhlbWUgQ2FsYw0KdGhlbWVfZWNvbm9taXN0CSAgICAgIHwgIGdncGxvdCBjb2xvciB0aGVtZSBiYXNlZCBvbiB0aGUgRWNvbm9taXN0DQp0aGVtZV9lY29ub21pc3Rfd2hpdGUJfCAgZ2dwbG90IGNvbG9yIHRoZW1lIGJhc2VkIG9uIHRoZSBFY29ub21pc3QNCnRoZW1lX2V4Y2VsCSAgICAgICAgICB8ICBnZ3Bsb3QgY29sb3IgdGhlbWUgYmFzZWQgb24gb2xkIEV4Y2VsIHBsb3RzDQp0aGVtZV9mZXcJICAgICAgICAgICAgfCAgVGhlbWUgYmFzZWQgb24gRmV34oCZcyDigJxQcmFjdGljYWwgUnVsZXMgZm9yIFVzaW5nIENvbG9yIGluIENoYXJ0c+KAnQ0KdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0CXwgIFRoZW1lIGluc3BpcmVkIGJ5IGZpdmV0aGlydHllaWdodC5jb20gcGxvdHMNCnRoZW1lX2ZvdW5kYXRpb24gICAgICB8CUZvdW5kYXRpb24gVGhlbWUNCnRoZW1lX2dkb2NzCSAgICAgICAgICB8ICBUaGVtZSB3aXRoIEdvb2dsZSBEb2NzIENoYXJ0IGRlZmF1bHRzDQp0aGVtZV9oYwkgICAgICAgICAgICB8ICBIaWdoY2hhcnRzIEpTIHRoZW1lDQp0aGVtZV9pZ3JheQkgICAgICAgICAgfCAgSW52ZXJzZSBncmF5IHRoZW1lDQp0aGVtZV9tYXAJICAgICAgICAgICAgfCAgQ2xlYW4gdGhlbWUgZm9yIG1hcHMNCnRoZW1lX3BhbmRlcgkgICAgICAgIHwgIEEgZ2dwbG90IHRoZW1lIG9yaWdpbmF0ZWQgZnJvbSB0aGUgcGFuZGVyIHBhY2thZ2UNCnRoZW1lX3BhcgkgICAgICAgICAgICB8ICBUaGVtZSB3aGljaCB0YWtlcyBpdHMgdmFsdWVzIGZyb20gdGhlIGN1cnJlbnQg4oCYYmFzZeKAmSBncmFwaGljcyBwYXJhbWV0ZXIgdmFsdWVzIGluIOKAmHBhcuKAmS4NCnRoZW1lX3NvbGFyaXplZAkgICAgICB8ICBnZ3Bsb3QgY29sb3IgdGhlbWVzIGJhc2VkIG9uIHRoZSBTb2xhcml6ZWQgcGFsZXR0ZQ0KdGhlbWVfc29sYXJpemVkXzIJICAgIHwgIGdncGxvdCBjb2xvciB0aGVtZXMgYmFzZWQgb24gdGhlIFNvbGFyaXplZCBwYWxldHRlDQp0aGVtZV9zb2xpZAkgICAgICAgICAgfCAgVGhlbWUgd2l0aCBub3RoaW5nIG90aGVyIHRoYW4gYSBiYWNrZ3JvdW5kIGNvbG9yDQp0aGVtZV9zdGF0YQkgICAgICAgICAgfCAgVGhlbWVzIGJhc2VkIG9uIFN0YXRhIGdyYXBoIHNjaGVtZXMNCnRoZW1lX3R1ZnRlCSAgICAgICAgICB8ICBUdWZ0ZSBNYXhpbWFsIERhdGEsIE1pbmltYWwgSW5rIFRoZW1lDQp0aGVtZV93c2oJV2FsbCAgICAgICAgfCAgU3RyZWV0IEpvdXJuYWwgdGhlbWUNCg0K5Li65LqG5ryU56S65a6D5Lus55qE55So5rOV77yM5oiR5Lus6aaW5YWI5Yib5bu65bm25L+d5a2Y5LiA5Liq5Zu+44CCDQoNCmBgYHtyfQ0KIyBjcmVhdGUgYmFzaWMgcGxvdA0KbGlicmFyeShnZ3Bsb3QyKQ0KcCA8LSBnZ3Bsb3QobXBnLCANCiAgICAgICAgICAgIGFlcyh4ID0gZGlzcGwsIA0KICAgICAgICAgICAgICAgIHk9aHd5LCANCiAgICAgICAgICAgICAgICBjb2xvciA9IGNsYXNzKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzLCANCiAgICAgICAgICAgICBhbHBoYSA9IC41KSArDQogIGxhYnModGl0bGUgPSAiTWlsZWFnZSBieSBlbmdpbmUgZGlzcGxhY2VtZW50IiwNCiAgICAgICBzdWJ0aXRsZSA9ICJEYXRhIGZyb20gMTk5OSBhbmQgMjAwOCIsDQogICAgICAgY2FwdGlvbiA9ICJTb3VyY2U6IEVQQSAoaHR0cDovL2Z1ZWxlY29ub215LmdvdikiLA0KICAgICAgIHggPSAiRW5naW5lIGRpc3BsYWNlbWVudCAobGl0cmVzKSIsDQogICAgICAgeSA9ICJIaWdod2F5IG1pbGVzIHBlciBnYWxsb24iLA0KICAgICAgIGNvbG9yID0gIkNhciBDbGFzcyIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpDQoNCnANCiMgZGlzcGxheSBncmFwaA0KcCArIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImdyZWVuIiwgDQogICAgbGluZXR5cGUgPSAiZG90ZGFzaCIpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9saW5lKGxpbmV0eXBlID0gImRvdGRhc2giKSwgDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImdyYXk4NCIsIA0KICAgICAgICBsaW5ldHlwZSA9ICJkb3RkYXNoIiksIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gImFudGlxdWV3aGl0ZSIsIA0KICAgICAgICBjb2xvdXIgPSBOQSkpDQpgYGANCg0K6K6p5oiR5Lus5bqU55So5LiA5Lqb5Li76aKYDQoNCmBgYHtyfQ0KIyBhZGQgZWNvbm9taXN0IHRoZW1lDQpsaWJyYXJ5KGdndGhlbWVzKQ0KcCArIHRoZW1lX2Vjb25vbWlzdCgpIA0KYGBgDQoNCmBgYHtyfQ0KIyBhZGQgZml2ZXRoaXJ0eWVpZ2h0IHRoZW1lDQpwICsgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkNCmBgYA0KDQpgYGB7cn0NCiMgYWRkIHdzaiB0aGVtZQ0KcCArIHRoZW1lX3dzaihiYXNlX3NpemU9OCkNCmBgYA0KDQrpu5jorqTmg4XlhrXkuIvvvIwqKndzauS4u+mimOeahOWtl+S9k+mAmuW4uOWkquWkpyoq44CC5pu05pS5KipgYmFzZV9zaXpl6YCJ6aG5YCoq5Lya5pyJ5omA5biu5Yqp44CCDQoNCuavj+S4quS4u+mimOi/mOaPkOS+m+S6huminOiJsuWSjOWhq+WFheavlOS+i+OAguWcqOS4i+S4gOS4quekuuS+i+S4re+8jOWwhuWQjOaXtuS9v+eUqOWwkeaVsOWHoOS4quS4u+mimOWSjOminOiJsuOAgg0KDQpgYGB7cn0NCiMgYWRkIGZldyB0aGVtZQ0KcCArIHRoZW1lX2ZldygpICsgc2NhbGVfY29sb3JfZmV3KCkNCmBgYA0KDQojIyMjIDkuOC4yLjIgaHJicnRoZW1lcw0KDQoqKmhyYnJ0aGVtZXPljIUqKuS4k+azqOS6juS7peaOkueJiOS4uuS4reW/g+eahOS4u+mimOOAgue7k+aenOaYr+WbvuihqO+8jOW+gOW+gOacieS4gOS4quW5suWHgOeahOWkluinguOAgue7p+e7reS4iumdoueahOekuuS+i+Wbvi4NCg0KYGBge3J9DQojIGFkZCBmZXcgdGhlbWUNCmxpYnJhcnkoaHJicnRoZW1lcykNCnAgKyB0aGVtZV9pcHN1bSgpDQpgYGANCg0KIyMjIyA5LjguMi4zIGdndGhlbWVyDQoNCioqZ2d0aGVtZXLljIUqKuaPkOS+m+S6huW5v+azm+eahOS4u+mimCjmiKroh7PmiZPljbDml7bkuLoxN+S4quS4u+mimCnjgILor6Xova/ku7bljIXlnKhDUkFO5LiK5LiN5Y+v55So77yM5b+F6aG75LuOR2l0SHVi5LiK5a6J6KOF44CCDQoNCmBgYHtyfQ0KbGlicmFyeShkZXZ0b29scykNCiMgaW5zdGFsbF9naXRodWIoJ2N0dG9iaW4vZ2d0aGVtcicpDQpwX2xvYWQoZ2d0aGVtcikNCmBgYA0KDQrlh73mlbDnmoTlt6XkvZzmlrnlvI/nlaXmnInkuI3lkIzjgILkvb/nlKhgZ2d0aGVtcijigJx0aGVtZW5hbWXigJ0p5Ye95pWwYOWwhuacquadpeeahOWbvuW9ouiuvue9ruS4uue7meWumueahOS4u+mimOOAguS9v+eUqGBnZ3RoZW1yX3Jlc2V0KClg5bCG5pyq5p2l55qE5Zu+6L+U5Zue5YiwZ2dwbG90Mum7mOiupOS4u+mimOOAgg0KDQoqKuS4u+mimOS4u+imgeWMheaLrCoq77yaZmxhdCwgZmxhdCBkYXJrLCBjYW1vZmxhdWdlLCBjaGFsaywgY29wcGVyLCBkdXN0LCBlYXJ0aCwgZnJlc2gsIGdyYXBlLCBncmFzcywgZ3JleXNjYWxlLCBsaWdodCwgbGlsYWMsIHBhbGUsIHNlYSwgc2t5LCBhbmQgc29sYXJpemVkLg0KDQpgYGB7cn0NCiMgc2V0IGdyYXBocyB0byB0aGUgZmxhdCBkYXJrIHRoZW1lDQpsaWJyYXJ5KGdndGhlbXIpDQpnZ3RoZW1yKCJmbGF0IGRhcmsiKQ0KcA0KYGBgDQoNCuaIkeS4jeS8muaKiui/meS4quS4u+mimOeUqOWcqOi/meS4quWbvuS4iuOAguW+iOmavuWIhui+qOminOiJsuOAguWTquS4que7v+iJsuS7o+ihqOe0p+WHkeWei+axvei9pu+8jOWTquS4quS7o+ihqOW+ruWei+i9pj/pgInmi6nkuIDkuKrog73mnIDlpb3lnLDlsIblm77ooajkv6Hmga/kvKDovr7nu5nlj5fkvJfnmoTkuLvpopjjgIINCg0KYGBge3J9DQpnZ3RoZW1yX3Jlc2V0KCkNCnANCnAgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJBY2NlbnQiKQ0KYGBgDQoNCg0K