图形默认值适用于快速的数据探索,但是当你希望将结果发布到博客、论文、文章或海报时,您可能需要自定义结果。定制可以提高图形的清晰度和吸引力。

本章将描述如何自定义图形的网格线颜色字体标签图例。它还描述了如何添加注释(文本和行)。


9.1 坐标轴

x轴和y轴表示连续分类日期值。您可以使用下面的函数修改默认的刻度和标签。

9.1.1 数值轴

使用scale_x_continuousscale_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包提供了许多用于格式化数字标签的函数。其中最有用的是

  • dollar
  • comma
  • percent

让我们用一些合成数据来演示这些函数。

# 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_discretescale_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_datescale_y_date函数修改日期轴。

选项包括:

  • date_breaks -表示间隔时间的字符串,如“2周”或“10年”
  • date_labels -给出标签格式规范的字符串

下表给出了日期值的格式规范。

Symbol Meaning Example
%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_manualscale_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_brewerscale_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 其他调色板

其他需要探索的调色板包括dutchmastersggpomologicalLaCroixColoRnordochRepalettetownpalsrcartocolorwesanderson。如果您想查看所有选项板选项(或几乎所有选项),请查看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是放置文本的坐标。颜色大小参数是可选的。默认情况下,文本将居中。使用hjustvjust来更改对齐方式。

  • 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_classictheme_darktheme_graytheme_greytheme_light theme_linedrawtheme_minimumtheme_void。在本书中,我们经常使用theme_minimum。其他的可以通过附加包获得。

9.8.2.1 ggthemes

ggthemes package覆盖19种主题themes.

Theme Description
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