教学资源

在线练习网址:https://regex101.com/r/2ITLQ4/1

Regex cheatsheet.pdf

# 加载显示包
library(htmltools)
library(htmlwidgets)

两个有用的教学函数:

  • str_view(string, pattern, match = NA), 匹配到一次 pattern 即停止
  • str_view_all(string, pattern, match = NA),匹配所有符合 pattern 的字符串子集

终极解决方案

inferregex::infer_regex()

## remotes::install_github("daranzolin/inferregex")
library(inferregex)

s <- "abcd-9999-ab9"
infer_regex(s)$regex
#> [1] "^[a-z]{4}-\\d{4}-[a-z]{2}\\d$"

基础知识

字符串通常包含的是非结构化或半结构化数据,正则表达式 (regular expression) 可以用简练的语言来描述字符串中的模式

正则表达式主要依赖于元字符。元字符不代表他们本身的字面意思,他们都有特殊的含义。以下是一些元字符的介绍:

元字符 描述
字符集
[ ] 字符种类. 匹配方括号内的任意字符,[ ]可以理解为或
[^ ] 否定的字符种类,匹配除了方括号里的任意字符
重复次数
* 等价于{0, },匹配 >= 0个重复的在*号之前的字符,默认贪婪匹配
+ 等价于{1, },匹配 >=1 个重复的+号前的字符,默认贪婪匹配
? 等价于{0, 1},匹配 0 或 1 个?之前的字符;但若跟在*+?{m, n}后面,则表示更改匹配方式为”懒惰的”1
{n,m} 匹配 num 个大括号之前的字符 (n <= num <= m)。可以用{n,}表示至少n个,但没有{,m}的写法,因为用{0,m}就可以了
特定群
(xyz) 字符集,匹配与 xyz 完全相等的字符串.
| 或运算符,匹配符号前或后的字符(串)。[]的或只涉及单个字符,这是它与|的区别。
转义 注意:在 R 的字符串中转义字符必须写成 \\,即正则表达式的字符串形式。若要匹配'\',则需在字符串中写成 \\\\
\ 用于匹配一些在正则表达式中具有特殊意义的符号 [ ] ( ) { } . * + ? ^ $ \ |
锚点
^ 仅从开端开始匹配.
$ 仅从末端开始匹配.
^...$ 从头到尾严格匹配,模式必须与目标字符串完全相等
前后预查 有点像条件匹配
...(?=...) 正先行断言,条件:后面存在
...(?!...) 负先行断言,条件:后面不存在
(?<=...)... 正后发断言,条件:前面存在
(?<!...)... 负后发断言,条件:前面不存在
标志 JavaScript 中的正则表达式
/.../g 搜索全部,而不是第一个匹配的
/.../i 忽略大小写
/.../m 多行匹配
简写
. 匹配任意单个字符,除了换行符.
\w word的缩写,匹配所有字母、数字和下划线, 等同于 [a-zA-Z0-9_]
\W 匹配所有非字母数字下划线,等同于: [^\w],==很适合作为分隔符用来分词==
\d digital缩写,匹配数字: [0-9]
\D 匹配非数字: [^\d]
\s space缩写,匹配所有空格字符, 等同于: [\t\n\f\r\p{Z}]
\S 匹配所有非空格字符: [^\s]
\f 匹配一个换页符
\n 匹配一个换行符
\r 匹配一个回车符
\t 匹配一个制表符
\v 匹配一个垂直制表符
\p 匹配 CR/LF (等同于 ),用来匹配 DOS 行终止符

元字符

[ ]

# 匹配The或the
str_view_all("The car parked in the garage.", "[Tt]he")
# 匹配ar加.(没有转义,就是句号)
str_view_all("A garage is a good place to park a car.", "ar[.]")

[^ ]

# 非c开头加ar
str_view_all("The car parked in the garage.", "[^c]ar")

*, +, ?

# 任意长度的(>=0个)小写英文字母
str_view_all("The car parked in the garage.", "[a-z]*")
# 以0或更多个空格开头并以0或更多个空格结尾、中间是cat的字符串
str_view_all("The fat cat sat on the concatenation.", "\\s*cat\\s*")
# c开头t结尾,中间一个或多个任意字符
str_view_all("The fat cat sat on the mat.", "c.+t")
# 0个或1个T,即有无均可。The或he
str_view_all("The car is parked in the garage.", "[T]?he")
# 匹配'u'0次或1次
str_view(c("color", "colour", "colo", "r"), "colou?r")

{}

# 仅限2-3位的纯数字
str_view_all("The number was 9.9997 but we rounded it off to 10.0.", "[0-9]{2,3}")
# 2至无穷大位
str_view_all("The number was 9.9997 but we rounded it off to 10.0.", "[0-9]{2,}")
# 3位
str_view_all("The number was 9.9997 but we rounded it off to 10.0.", "[0-9]{3}")

? 懒惰匹配

# 尽量长
str_view("The cat sat on cat.", ".+at")
# 尽量短
str_view("The cat sat on cat.", ".+?at")
x <- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII."
# 匹配第二个'C'0次或1次,贪婪匹配尽量长
str_view(x, "CC?")
# 懒惰匹配
str_view(x, "CC??")
# 匹配第二个'C'1次或多次、尽量长的字符串
str_view(x, "CC+")
# 匹配'CC'后面有'C'或'L'1次或多次、尽量长的字符串
str_view(x, "CC[CL]+")
# 匹配2个'C'
str_view(x, "C{2}")
# 匹配2个以上'C'
str_view(x, "C{2,}")
# 匹配2到3个'C'
str_view(x, "C{2,3}")
# 匹配2到3个'C'、尽量短
str_view(x, "C{2,3}?")
# 匹配'C'后面有'L'或'X'1次(默认)
str_view(x, "C[LX]")
# 匹配'C'后面有'L'或'X'1次或多次、尽量长的字符串
str_view(x, "C[LX]+")
# 匹配'C'后面有'L'或'X'1次或多次、尽量短的字符串
str_view(x, "C[LX]+?")

() 群组

群,将多个字符组成一个单位。可以在 () 中用 | 表示或

str_view(c("grey", "gray"), "gr(e|a)y")
# 匹配car或gar或par
str_view_all("The car is parked in the garage.", "(c|g|p)ar")
# 匹配The或the或car
str_view_all("The car is parked in the garage.", "(T|t)he|car")

\\ 转义

# a\.是一个正则表达式,'a\\.'是该正则表达式的字符串形式
str_view(c("abc", "a.c", "bef"), "a\\.")
# fat或cat或mat后接'.'0次或1次
str_view_all("The fat cat sat on the mat.", "(f|c|m)at\\.?")
# 左管道打印字符串,右管道显示如何匹配。
# 字符串\\\\,相当于正则表达式的\\,经过转义才能匹配'\'
"a\\b" %T>% writeLines() %>% str_view("\\\\")
#> a\b

^$

# 从字符串开端匹配The或the
str_view_all("The car is parked in the garage.", "^(T|t)he")
# 从字符串末端匹配at.
str_view_all("The fat cat. sat. on the mat.", "(at\\.)$")
x <- c("apple pie", "apple", "apple cake")
str_view(x, "apple")
# 从头到尾严格匹配字符串的全部,不再是找到部分即可
str_view(x, "^apple$")

断言

()里面是一个判断条件。

# 匹配后面紧随着空格和fat的The或the
str_view_all("The fat cat sat on the mat.", "(T|t)he(?=\\sfat)")
# 匹配其后不跟随者空格和fat的The或the
str_view_all("The fat cat sat on the mat.", "(T|t)he(?!\\sfat)")
# 匹配其前紧跟The或the加空格的fat或mat
str_view_all("The fat cat sat on the mat.", "(?<=(T|t)he\\s)(fat|mat)")
# 匹配其前没有The或the加空格的cat
str_view_all("The cat sat on cat.", "(?<!(T|t)he\\s)(cat)")

分组与回溯引用

括号还可以定义”分组”,可以通过回溯引用(如\1、\2等)来引用这些分组。例如,以下的正则表达式可以找出名称中有重复的一对字母的所有水果:

# .为任意字符,\1为引用1次括号中的分组
# 该正则表达式表示abab型字符串
str_view(fruit, "(..)\\1", match = TRUE)
# abcab型
str_view(fruit, "(..)(.)\\1", match = TRUE)
# abcc型
str_view(fruit, "(..)(.)\\2", match = TRUE)

应用正则表达式

匹配检测

str_detect()返回与输入向量具有同样长度的逻辑向量

x <- c("apple", "banana", "pear")
str_detect(x, "e")
#> [1]  TRUE FALSE  TRUE

# 有多少个以t开头的常用单词?
sum(str_detect(words, "^t"))
#> [1] 65

# 以元音字母结尾的常用单词的比例是多少?
mean(str_detect(words, "[aeiou]$"))
#> [1] 0.2765306

当逻辑条件非常复杂时(例如,匹配 a 或 b,但不匹配 c,除非 d 成立),应该将其分解为几个更小的子表达式,将每个子表达式使用str_detect()的匹配结果赋给一个变量,并使用逻辑运算组合起来。

## 寻找不含aeiou的单词

# 找出常用词中至少包含一个元音字母的所有单词,然后取反
no_vowels_1 <- !str_detect(words, "[aeiou]")
# 展示不包含aeiou的常用单词
words[no_vowels_1]
#> [1] "by"  "dry" "fly" "mrs" "try" "why"

# 找出仅包含辅音字母(非元音字母)的所有单词
# [^aeiou]表示非aeiou,即辅音字母
# ^辅音字母+$,表示从头到尾1个或多个辅音字母的完全匹配
no_vowels_2 <- str_detect(words, "^[^aeiou]+$")
words[no_vowels_2]
#> [1] "by"  "dry" "fly" "mrs" "try" "why"

identical(no_vowels_1, no_vowels_2)
#> [1] TRUE
words[str_detect(words, "x$")]
#> [1] "box" "sex" "six" "tax"
str_subset(words, "x$")
#> [1] "box" "sex" "six" "tax"

df <- tibble(
  word = words,
  i = seq_along(word)
)
df %>%
  filter(str_detect(words, "x$"))
#> # A tibble: 4 x 2
#>   word      i
#>   <chr> <int>
#> 1 box     108
#> 2 sex     747
#> 3 six     772
#> 4 tax     841

匹配计数

str_count()返回字符串中与pattern匹配的子集的数量

x <- c("apple", "banana", "pear")
str_count(x, "a")
#> [1] 1 3 1

# 平均来看,每个单词中有多少个元音字母?
mean(str_count(words, "[aeiou]"))
#> [1] 1.991837

df %>%
  mutate(
    vowels = str_count(word, "[aeiou]"),
    consonants = str_count(word, "[^aeiou]")
  )
#> # A tibble: 980 x 4
#>    word         i vowels consonants
#>    <chr>    <int>  <int>      <int>
#>  1 a            1      1          0
#>  2 able         2      2          2
#>  3 about        3      3          2
#>  4 absolute     4      4          4
#>  5 accept       5      2          4
#>  6 account      6      3          4
#>  7 achieve      7      4          3
#>  8 across       8      2          4
#>  9 act          9      1          2
#> 10 active      10      3          3
#> # ... with 970 more rows

注意,匹配从来不会重叠。例如,在 “abababa” 中,模式 “aba” 会匹配多少次?正则表达式会告诉你是 2 次,而不是 3 次

str_count("abababa", "aba")
#> [1] 2
str_view("abababa", "aba")
str_view_all("abababa", "aba")

提取匹配内容

str_extract()str_extract()_all

# sentences数据集
head(stringr::sentences)
#> [1] "The birch canoe slid on the smooth planks." 
#> [2] "Glue the sheet to the dark blue background."
#> [3] "It's easy to tell the depth of a well."     
#> [4] "These days a chicken leg is a rare dish."   
#> [5] "Rice is often served in round bowls."       
#> [6] "The juice of lemons makes fine punch."

colors <- c(
  "red", "orange", "yellow", "green", "blue", "purple"
)
color_match <- str_c(colors, collapse = "|")
color_match
#> [1] "red|orange|yellow|green|blue|purple"
# 字符串中的'|'转换为正则表达式是“或”
# 选取含有颜色的句子子集
has_color <- str_subset(sentences, color_match)
matches <- str_extract(has_color, color_match) # 提取匹配内容
matches
#>  [1] "blue"   "blue"   "red"    "red"    "red"    "blue"   "yellow" "red"   
#>  [9] "red"    "green"  "red"    "red"    "blue"   "red"    "red"    "red"   
#> [17] "red"    "blue"   "red"    "blue"   "red"    "green"  "red"    "red"   
#> [25] "red"    "red"    "red"    "red"    "green"  "red"    "green"  "red"   
#> [33] "purple" "green"  "red"    "red"    "red"    "red"    "red"    "blue"  
#> [41] "red"    "blue"   "red"    "red"    "red"    "red"    "green"  "green" 
#> [49] "green"  "red"    "red"    "yellow" "red"    "orange" "red"    "red"   
#> [57] "red"

# 包含多于一种颜色的句子
more <- sentences[str_count(sentences, color_match) > 1]
str_view_all(more, color_match)
str_extract(more, color_match)
#> [1] "blue"   "green"  "orange"

注意,str_extract()只提取第一个匹配,,因为单个匹配可以使用更简单的数据结构。要想得到所有匹配,可以使用str_extract_all()函数,它会返回一个列表:

str_extract_all(more, color_match)
#> [[1]]
#> [1] "blue" "red" 
#> 
#> [[2]]
#> [1] "green" "red"  
#> 
#> [[3]]
#> [1] "orange" "red"

如果设置了参数simplify=TRUE,那么str_extract_all()会返回一个矩阵,其中较短的匹配会扩展到与最长的匹配具有同样的长度:

str_extract_all(more, color_match, simplify = TRUE)
#>      [,1]     [,2] 
#> [1,] "blue"   "red"
#> [2,] "green"  "red"
#> [3,] "orange" "red"

x <- c("a", "a b", "a b c")
str_extract_all(x, "[a-z]")
#> [[1]]
#> [1] "a"
#> 
#> [[2]]
#> [1] "a" "b"
#> 
#> [[3]]
#> [1] "a" "b" "c"
str_extract_all(x, "[a-z]", simplify = TRUE)
#>      [,1] [,2] [,3]
#> [1,] "a"  ""   ""  
#> [2,] "a"  "b"  ""  
#> [3,] "a"  "b"  "c"

配合()分组提取

pattern中用()分好组,便可以用str_match()提取了。

例:我们想从句子中提取出名词。我们先进行一种启发式实验,找出跟在 a 或 the 后面的所有单词。因为使用正则表达式定义”单词”有一点难度,所以我们使用一种简单的近似定义——至少有1个非空格字符的字符序列:

# a或the+空格+一个或多个非空格字符
noun <- "(a|the) ([^ ]+)"
has_noun <- sentences %>%
  str_subset(noun) %>%
  head(10)
has_noun %>%
  str_extract(noun)
#>  [1] "the smooth" "the sheet"  "the depth"  "a chicken"  "the parked"
#>  [6] "the sun"    "the huge"   "the ball"   "the woman"  "a helps"

str_match() 函数可以给出每个独立分组。str_match()返回的不是字符向量,而是一个矩阵,其中一列是完整匹配,后面的列是每个分组的匹配

has_noun %>%
  str_match(noun)
#>       [,1]         [,2]  [,3]     
#>  [1,] "the smooth" "the" "smooth" 
#>  [2,] "the sheet"  "the" "sheet"  
#>  [3,] "the depth"  "the" "depth"  
#>  [4,] "a chicken"  "a"   "chicken"
#>  [5,] "the parked" "the" "parked" 
#>  [6,] "the sun"    "the" "sun"    
#>  [7,] "the huge"   "the" "huge"   
#>  [8,] "the ball"   "the" "ball"   
#>  [9,] "the woman"  "the" "woman"  
#> [10,] "a helps"    "a"   "helps"
has_noun %>%
  str_match_all(noun)
#> [[1]]
#>      [,1]         [,2]  [,3]    
#> [1,] "the smooth" "the" "smooth"
#> 
#> [[2]]
#>      [,1]        [,2]  [,3]   
#> [1,] "the sheet" "the" "sheet"
#> [2,] "the dark"  "the" "dark" 
#> 
#> [[3]]
#>      [,1]        [,2]  [,3]   
#> [1,] "the depth" "the" "depth"
#> [2,] "a well."   "a"   "well."
#> 
#> [[4]]
#>      [,1]        [,2] [,3]     
#> [1,] "a chicken" "a"  "chicken"
#> [2,] "a rare"    "a"  "rare"   
#> 
#> [[5]]
#>      [,1]         [,2]  [,3]    
#> [1,] "the parked" "the" "parked"
#> 
#> [[6]]
#>      [,1]      [,2]  [,3] 
#> [1,] "the sun" "the" "sun"
#> 
#> [[7]]
#>      [,1]        [,2]  [,3]   
#> [1,] "the huge"  "the" "huge" 
#> [2,] "the clear" "the" "clear"
#> 
#> [[8]]
#>      [,1]       [,2]  [,3]  
#> [1,] "the ball" "the" "ball"
#> 
#> [[9]]
#>      [,1]        [,2]  [,3]   
#> [1,] "the woman" "the" "woman"
#> 
#> [[10]]
#>      [,1]           [,2]  [,3]      
#> [1,] "a helps"      "a"   "helps"   
#> [2,] "the evening." "the" "evening."

如果数据是保存在tibble中的,那么使用tidyr::extract()会更容易。这个函数的工作方式与str_match()函数类似,只是要求为每个分组提供一个名称,以作为新列放在 tibble 中

tibble(sentence = sentences) %>%
  tidyr::extract(
    sentence, c("article", "noun"), "(a|the) ([^ ]+)",
    remove = FALSE
  )
#> # A tibble: 720 x 3
#>    sentence                                    article noun   
#>    <chr>                                       <chr>   <chr>  
#>  1 The birch canoe slid on the smooth planks.  the     smooth 
#>  2 Glue the sheet to the dark blue background. the     sheet  
#>  3 It's easy to tell the depth of a well.      the     depth  
#>  4 These days a chicken leg is a rare dish.    a       chicken
#>  5 Rice is often served in round bowls.        <NA>    <NA>   
#>  6 The juice of lemons makes fine punch.       <NA>    <NA>   
#>  7 The box was thrown beside the parked truck. the     parked 
#>  8 The hogs were fed chopped corn and garbage. <NA>    <NA>   
#>  9 Four hours of steady work faced us.         <NA>    <NA>   
#> 10 Large size in stockings is hard to sell.    <NA>    <NA>   
#> # ... with 710 more rows

替换匹配内容

str_replace(), str_replace_all()

x <- c("apple", "pear", "banana")
str_replace(x, "[aeiou]", "-")
#> [1] "-pple"  "p-ar"   "b-nana"
str_replace_all(x, "[aeiou]", "-")
#> [1] "-ppl-"  "p--r"   "b-n-n-"
x <- c("1 house", "2 cars", "3 people")
str_replace_all(x, c("1" = "one", "2" = "two", "3" = "three"))
#> [1] "one house"    "two cars"     "three people"

还可以使用回溯引用来插入匹配中的分组。在下面的代码中,我们交换了第二个单词和第三个单词的顺序:

# [^\\s]+代表多个非空格字符,即一个单词
# '([^\\s]+) ([^\\s]+) ([^\\s]+)'是前三个单词
sentences %>%
  str_replace("([^\\s]+) ([^\\s]+) ([^\\s]+)", "\\1 \\3 \\2") %>%
  head(5)
#> [1] "The canoe birch slid on the smooth planks." 
#> [2] "Glue sheet the to the dark blue background."
#> [3] "It's to easy tell the depth of a well."     
#> [4] "These a days chicken leg is a rare dish."   
#> [5] "Rice often is served in round bowls."

拆分字符串

str_split()

因为字符向量的每个分量会包含不同数量的片段,所以 str_split() 会返回一个列表:

sentences %>%
  head(5) %>%
  str_split(" ")
#> [[1]]
#> [1] "The"     "birch"   "canoe"   "slid"    "on"      "the"     "smooth" 
#> [8] "planks."
#> 
#> [[2]]
#> [1] "Glue"        "the"         "sheet"       "to"          "the"        
#> [6] "dark"        "blue"        "background."
#> 
#> [[3]]
#> [1] "It's"  "easy"  "to"    "tell"  "the"   "depth" "of"    "a"     "well."
#> 
#> [[4]]
#> [1] "These"   "days"    "a"       "chicken" "leg"     "is"      "a"      
#> [8] "rare"    "dish."  
#> 
#> [[5]]
#> [1] "Rice"   "is"     "often"  "served" "in"     "round"  "bowls."

如果你拆分的是长度为1的向量,那么只要简单地提取列表的第一个元素即可:

# 字符串为"\\|",正则表达式为\|,编译时的含义为字符'|'
"a|b|c|d" %>%
  str_split("\\|") %>%
  magrittr::extract2(1)
#> [1] "a" "b" "c" "d"

也可以通过设置 simplify = TRUE 返回一个矩阵:

sentences %>%
  head(5) %>%
  str_split(" ", simplify = TRUE)
#>      [,1]    [,2]    [,3]    [,4]      [,5]  [,6]    [,7]     [,8]         
#> [1,] "The"   "birch" "canoe" "slid"    "on"  "the"   "smooth" "planks."    
#> [2,] "Glue"  "the"   "sheet" "to"      "the" "dark"  "blue"   "background."
#> [3,] "It's"  "easy"  "to"    "tell"    "the" "depth" "of"     "a"          
#> [4,] "These" "days"  "a"     "chicken" "leg" "is"    "a"      "rare"       
#> [5,] "Rice"  "is"    "often" "served"  "in"  "round" "bowls." ""           
#>      [,9]   
#> [1,] ""     
#> [2,] ""     
#> [3,] "well."
#> [4,] "dish."
#> [5,] ""

还可以设定拆分片段的最大数量:

fields <- c("Name: Hadley", "Country: NZ", "Age: 35: 1980")
fields %>% str_split(": ", simplify = TRUE)
#>      [,1]      [,2]     [,3]  
#> [1,] "Name"    "Hadley" ""    
#> [2,] "Country" "NZ"     ""    
#> [3,] "Age"     "35"     "1980"
fields %>% str_split(": ", n = 2, simplify = TRUE)
#>      [,1]      [,2]      
#> [1,] "Name"    "Hadley"  
#> [2,] "Country" "NZ"      
#> [3,] "Age"     "35: 1980"

除了模式,你还可以通过字母、行、句子和单词边界(boundary() 函数)来拆分字符串:

x <- "This is a sentence. This is another sentence."
str_view_all(x, boundary("word")) # 匹配word数据集中的常用词
str_split(x, " ")[[1]]
#> [1] "This"      "is"        "a"         "sentence." "This"      "is"       
#> [7] "another"   "sentence."
str_split(x, boundary("word"))[[1]]
#> [1] "This"     "is"       "a"        "sentence" "This"     "is"       "another" 
#> [8] "sentence"

定位匹配内容

str_locate(), str_locate_all()

它们可以给出每个匹配的开始位置和结束位置。你可以使用 str_locate() 函数找出匹配的模式,然后使用 str_sub() 函数来提取或修改匹配的内容。

pattern的其他参数和匹配方法

regex()的参数

当使用一个字符串作为模式时,R会自动调用regex()函数对其进行包装:

# 正常调用:
str_view(fruit, "nana")
# 上面形式是以下形式的简写
str_view(fruit, regex("nana"))

可以使用 regex() 函数的其他参数来控制具体的匹配方式:

大小写

ignore_case = TRUE 既可以匹配大写字母,也可以匹配小写字母

bananas <- c("banana", "Banana", "BANANA")
str_view(bananas, "banana")
str_view(bananas, regex("banana", ignore_case = TRUE))

从每行开始

multiline = TRUE可以使得^和$从每行的开头和末尾开始匹配,而不是从完整字符串的开头和末尾开始匹配:

x <- "Line 1\nLine 2\nLine 3"
str_extract_all(x, "^Line")[[1]]
#> [1] "Line"
str_extract_all(x, regex("^Line", multiline = TRUE))[[1]]
#> [1] "Line" "Line" "Line"

在正则表达式中加入注释

comments = TRUE 可以让你在复杂的正则表达式中加入注释和空白字符,以便更易理解。匹配时会忽略空格和#后面的内容。如果想要匹配一个空格,你需要对其进行转义: “\”:

phone <- regex("
  \\(? # 可选的左小括号
  (\\d{3}) # 地区编码
  [)- ]? # 可选的右小括号、短划线或空格
  (\\d{3}) # 另外3个数字
  [ -]? # 可选的空格或短划线
  (\\d{3}) # 另外3个数字
", comments = TRUE)

str_match("514-791-8141", phone)
#>      [,1]          [,2]  [,3]  [,4] 
#> [1,] "514-791-814" "514" "791" "814"

’.’可以匹配\n

dotall = TRUE 可以使得’.’匹配包括 \n 在内的所有字符。

fixed()

fixed() 函数可以按照字符串的字节形式进行精确匹配,它会忽略正则表达式中的所有特殊字符,并在非常低的层次上进行操作。这样可以不用进行那些复杂的转义操作,而且速度比普通正则表达式要快很多。

但是,在匹配非英语数据时,要慎用 fixed() 函数。它可能会出现问题,因为此时同一个字符经常有多种表达方式。

microbenchmark::microbenchmark(
  fixed = str_detect(sentences, fixed("the")),
  regex = str_detect(sentences, "the"),
  times = 20
)
#> Unit: microseconds
#>   expr     min       lq     mean   median       uq     max neval
#>  fixed  76.001  78.2010  94.0309  79.9010  87.6505 315.701    20
#>  regex 265.001 271.5505 284.1209 275.5015 280.1505 443.301    20

coll()

函数使用标准排序规则来比较字符串,这在进行不区分大小写的匹配时是非常有效的。


  1. 默认的匹配方式是”贪婪的”,意为在符合条件的所有可能的匹配中,正则表达式会匹配尽量长的字符串。通过在正则表达式后面添加一个?,可以将匹配方式更改为”懒惰的”,即匹配尽量短的相应字符串。↩︎

LS0tDQp0aXRsZTogIlJlZ3VsYXIgRXhwcmVzc2lvbiINCnN1YnRpdGxlOiAnJw0KYXV0aG9yOiAiSHVtb29uIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY3NzOiBbIi4uL2Nzcy9zdHlsZS5jc3MiXQ0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICB0aGVtZTogdW5pdGVkDQogICAgaGlnaGxpZ2h0OiBoYWRkb2NrDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiB5ZXMNCiAgICAgIHNtb290aF9zY3JvbGw6IHllcw0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQpzb3VyY2UoIi4uL1JtYXJrZG93bi10ZW1wbGF0ZS9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywgDQogIGV2YWwgPSBjb25maWckZXZhbCwgDQogIHRpZHkgPSBjb25maWckdGlkeSwgDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwgDQogIGNvbGxhcHNlID0gY29uZmlnJGNvbGxhcHNlLCANCiAgY2FjaGUgPSBjb25maWckY2FjaGUsDQogIGNhY2hlLmNvbW1lbnRzID0gY29uZmlnJGNhY2hlLmNvbW1lbnRzLA0KICBhdXRvZGVwID0gY29uZmlnJGF1dG9kZXANCikNCg0KIyMgdXNlIG5lY2Vzc2FyeSBwYWNrYWdlcyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShodG1sd2lkZ2V0cykNCmBgYA0KDQoNCg0KDQojIyDmlZnlrabotYTmupANCg0KDQoNCuWcqOe6v+e7g+S5oOe9keWdgO+8mmh0dHBzOi8vcmVnZXgxMDEuY29tL3IvMklUTFE0LzENCg0KPGEgaHJlZj0iLi4vcGRmL2NoZWF0c2hlZXQtcmVnZXgucGRmIj48c3Ryb25nPlJlZ2V4IGNoZWF0c2hlZXQucGRmPC9zdHJvbmc+PC9hPg0KDQo8b2JqZWN0IGRhdGE9Ii4uL3BkZi9jaGVhdHNoZWV0LXJlZ2V4LnBkZiIgdHlwZT0iYXBwbGljYXRpb24vcGRmIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIj48L29iamVjdD4NCg0KDQpgYGB7cn0NCiMg5Yqg6L295pi+56S65YyFDQpsaWJyYXJ5KGh0bWx0b29scykNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQpgYGANCuS4pOS4quacieeUqOeahOaVmeWtpuWHveaVsO+8mg0KDQotIGBzdHJfdmlldyhzdHJpbmcsIHBhdHRlcm4sIG1hdGNoID0gTkEpYCwg5Yy56YWN5Yiw5LiA5qyhIHBhdHRlcm4g5Y2z5YGc5q2iDQotIGBzdHJfdmlld19hbGwoc3RyaW5nLCBwYXR0ZXJuLCBtYXRjaCA9IE5BKWDvvIzljLnphY3miYDmnInnrKblkIggcGF0dGVybiDnmoTlrZfnrKbkuLLlrZDpm4YNCg0KIyMg57uI5p6B6Kej5Yaz5pa55qGIDQoNCmBpbmZlcnJlZ2V4OjppbmZlcl9yZWdleCgpYA0KDQpgYGB7cn0NCiMjIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJkYXJhbnpvbGluL2luZmVycmVnZXgiKQ0KbGlicmFyeShpbmZlcnJlZ2V4KQ0KDQpzIDwtICJhYmNkLTk5OTktYWI5Ig0KaW5mZXJfcmVnZXgocykkcmVnZXgNCmBgYA0KDQojIyDln7rnoYDnn6Xor4YNCg0K5a2X56ym5Liy6YCa5bi45YyF5ZCr55qE5piv6Z2e57uT5p6E5YyW5oiW5Y2K57uT5p6E5YyW5pWw5o2u77yM5q2j5YiZ6KGo6L6+5byPIChyZWd1bGFyIGV4cHJlc3Npb24pIOWPr+S7peeUqOeugOe7g+eahOivreiogOadpeaPj+i/sCoq5a2X56ym5Liy5Lit55qE5qih5byPKirjgIINCg0K5q2j5YiZ6KGo6L6+5byP5Li76KaB5L6d6LWW5LqO5YWD5a2X56ym44CC5YWD5a2X56ym5LiN5Luj6KGo5LuW5Lus5pys6Lqr55qE5a2X6Z2i5oSP5oCd77yM5LuW5Lus6YO95pyJ54m55q6K55qE5ZCr5LmJ44CC5Lul5LiL5piv5LiA5Lqb5YWD5a2X56ym55qE5LuL57uNOg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IOWFg+Wtl+espiAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5o+P6L+wICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCis9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSsNCnwgKirlrZfnrKbpm4YqKiAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYFsgXWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDlrZfnrKbnp43nsbsuIOWMuemFjeaWueaLrOWPt+WGheeahOS7u+aEj+Wtl+espu+8jGBbIF1g5Y+v5Lul55CG6Kej5Li65oiWICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBbXiBdYCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5ZCm5a6a55qE5a2X56ym56eN57G777yM5Yy56YWN6Zmk5LqG5pa55ous5Y+36YeM55qE5Lu75oSP5a2X56ymICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoq6YeN5aSN5qyh5pWwKiogICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgKmAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOetieS7t+S6jnswLCB977yM5Yy56YWNIFw+PSAw5Liq6YeN5aSN55qE5ZyoYCpg5Y+35LmL5YmN55qE5a2X56ym77yM6buY6K6k6LSq5amq5Yy56YWNICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgK2AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOetieS7t+S6jnsxLCB977yM5Yy56YWNIFw+PTEg5Liq6YeN5aSN55qEYCtg5Y+35YmN55qE5a2X56ym77yM6buY6K6k6LSq5amq5Yy56YWNICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYD9gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDnrYnku7fkuo57MCwgMX3vvIzljLnphY0gMCDmiJYgMSDkuKpgP2DkuYvliY3nmoTlrZfnrKbvvJvkvYboi6Xot5/lnKhgKmDjgIFgK2DjgIFgP2DmiJZge20sIG59YOWQjumdou+8jOWImeihqOekuuabtOaUueWMuemFjeaWueW8j+S4uiLmh5Lmg7DnmoQiW14xXSB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGB7bixtfWAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5Yy56YWNIG51bSDkuKrlpKfmi6zlj7fkuYvliY3nmoTlrZfnrKYgKG4gXDw9IG51bSBcPD0gbSnjgILlj6/ku6XnlKh7bix96KGo56S66Iez5bCRbuS4qu+8jOS9huayoeaciXssbX3nmoTlhpnms5XvvIzlm6DkuLrnlKh7MCxtfeWwseWPr+S7peS6hiAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKirnibnlrprnvqQqKiAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYCh4eXopYCAgICAgICAgICAgICAgICAgICAgICAgICAgfCDlrZfnrKbpm4bvvIzljLnphY3kuI4gYHh5emAg5a6M5YWo55u4562J55qE5a2X56ym5LiyLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYHxgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmiJbov5DnrpfnrKbvvIzljLnphY3nrKblj7fliY3miJblkI7nmoTlrZfnrKbvvIjkuLLvvInjgIJgW11g55qE5oiW5Y+q5raJ5Y+K5Y2V5Liq5a2X56ym77yM6L+Z5piv5a6D5LiOYHxg55qE5Yy65Yir44CCICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoq6L2s5LmJKiogICAgICAgICAgICAgICAgICAgICAgICAgfCDms6jmhI/vvJrlnKggUiDnmoTlrZfnrKbkuLLkuK3ovazkuYnlrZfnrKblv4XpobvlhpnmiJAgYFxcYO+8jOWNs+ato+WImeihqOi+vuW8j+eahOWtl+espuS4suW9ouW8j+OAguiLpeimgeWMuemFjWAnXCdg77yM5YiZ6ZyA5Zyo5a2X56ym5Liy5Lit5YaZ5oiQIGBcXFxcYCAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYFxgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDnlKjkuo7ljLnphY3kuIDkupvlnKjmraPliJnooajovr7lvI/kuK3lhbfmnInnibnmrormhI/kuYnnmoTnrKblj7cgYFtgIGBdYCBgKGAgYClgIGB7YCBgfWAgYC5gIGAqYCBgK2AgYD9gIGBeYCBgJGAgYFxgIGB8YCAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoq6ZSa54K5KiogICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYF5gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDku4Xku47lvIDnq6/lvIDlp4vljLnphY0uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgJGAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOS7heS7juacq+err+W8gOWni+WMuemFjS4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBeLi4uJGAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5LuO5aS05Yiw5bC+5Lil5qC85Yy56YWN77yM5qih5byP5b+F6aG75LiO55uu5qCH5a2X56ym5Liy5a6M5YWo55u4562JICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgKirliY3lkI7pooTmn6UqKiAgICAgICAgICAgICAgICAgICAgIHwg5pyJ54K55YOP5p2h5Lu25Yy56YWNICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgLi4uKD89Li4uKWAgICAgICAgICAgICAgICAgICAgICB8IOato+WFiOihjOaWreiogO+8jOadoeS7tu+8muWQjumdouWtmOWcqCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYC4uLig/IS4uLilgICAgICAgICAgICAgICAgICAgICAgfCDotJ/lhYjooYzmlq3oqIDvvIzmnaHku7bvvJrlkI7pnaLkuI3lrZjlnKggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgKD88PS4uLikuLi5gICAgICAgICAgICAgICAgICAgICB8IOato+WQjuWPkeaWreiogO+8jOadoeS7tu+8muWJjemdouWtmOWcqCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYCg/PCEuLi4pLi4uYCAgICAgICAgICAgICAgICAgICAgfCDotJ/lkI7lj5Hmlq3oqIDvvIzmnaHku7bvvJrliY3pnaLkuI3lrZjlnKggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAqKuagh+W/lyoqICAgICAgICAgICAgICAgICAgICAgICAgIHwgSmF2YVNjcmlwdCDkuK3nmoTmraPliJnooajovr7lvI8gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgLy4uLi9nYCAgICAgICAgICAgICAgICAgICAgICAgICB8IOaQnOe0ouWFqOmDqO+8jOiAjOS4jeaYr+esrOS4gOS4quWMuemFjeeahCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGAvLi4uL2lgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5b+955Wl5aSn5bCP5YaZICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYC8uLi4vbWAgICAgICAgICAgICAgICAgICAgICAgICAgfCDlpJrooYzljLnphY0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICoq566A5YaZKiogICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYC5gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDljLnphY3ku7vmhI/ljZXkuKrlrZfnrKbvvIzpmaTkuobmjaLooYznrKYuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgXHdgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IHdvcmTnmoTnvKnlhpnvvIzljLnphY3miYDmnInlrZfmr43jgIHmlbDlrZflkozkuIvliJLnur8sIOetieWQjOS6jiBgW2EtekEtWjAtOV9dYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBcV2AgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5Yy56YWN5omA5pyJ6Z2e5a2X5q+N5pWw5a2X5LiL5YiS57q/77yM562J5ZCM5LqOOiBgW15cd11g77yMPT3lvojpgILlkIjkvZzkuLrliIbpmpTnrKbnlKjmnaXliIbor409PSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYFxkYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBkaWdpdGFs57yp5YaZ77yM5Yy56YWN5pWw5a2XOiBgWzAtOV1gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBcRGAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5Yy56YWN6Z2e5pWw5a2XOiBgW15cZF1gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYFxzYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBzcGFjZee8qeWGme+8jOWMuemFjeaJgOacieepuuagvOWtl+espiwg562J5ZCM5LqOOiBgW1x0XG5cZlxyXHB7Wn1dYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgXFNgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOWMuemFjeaJgOaciemdnuepuuagvOWtl+espjogYFteXHNdYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgXGZgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOWMuemFjeS4gOS4quaNoumhteespiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYFxuYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDljLnphY3kuIDkuKrmjaLooYznrKYgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBccmAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5Yy56YWN5LiA5Liq5Zue6L2m56ymICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgXHRgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOWMuemFjeS4gOS4quWItuihqOespiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYFx2YCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDljLnphY3kuIDkuKrlnoLnm7TliLbooajnrKYgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYFxwYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDljLnphY0gQ1IvTEYgKOetieWQjOS6jiBcclxuKe+8jOeUqOadpeWMuemFjSBET1Mg6KGM57uI5q2i56ymICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQpbXjFdOiDpu5jorqTnmoTljLnphY3mlrnlvI/mmK8i6LSq5amq55qEIu+8jOaEj+S4uuWcqOespuWQiOadoeS7tueahOaJgOacieWPr+iDveeahOWMuemFjeS4re+8jOato+WImeihqOi+vuW8j+S8muWMuemFjeWwvemHj+mVv+eahOWtl+espuS4suOAgumAmui/h+WcqOato+WImeihqOi+vuW8j+WQjumdoua3u+WKoOS4gOS4qmA/YO+8jOWPr+S7peWwhuWMuemFjeaWueW8j+abtOaUueS4uiLmh5Lmg7DnmoQi77yM5Y2z5Yy56YWN5bC96YeP55+t55qE55u45bqU5a2X56ym5Liy44CCDQoNCiMjIyDlhYPlrZfnrKYNCg0KIyMjIyBgWyBdYA0KDQpgYGB7cn0NCiMg5Yy56YWNVGhl5oiWdGhlDQpzdHJfdmlld19hbGwoIlRoZSBjYXIgcGFya2VkIGluIHRoZSBnYXJhZ2UuIiwgIltUdF1oZSIpDQpgYGANCg0KYGBge3J9DQojIOWMuemFjWFy5YqgLu+8iOayoeaciei9rOS5ie+8jOWwseaYr+WPpeWPt++8iQ0Kc3RyX3ZpZXdfYWxsKCJBIGdhcmFnZSBpcyBhIGdvb2QgcGxhY2UgdG8gcGFyayBhIGNhci4iLCAiYXJbLl0iKQ0KYGBgDQoNCiMjIyMgYFteIF1gDQoNCmBgYHtyfQ0KIyDpnZ5j5byA5aS05YqgYXINCnN0cl92aWV3X2FsbCgiVGhlIGNhciBwYXJrZWQgaW4gdGhlIGdhcmFnZS4iLCAiW15jXWFyIikNCmBgYA0KDQojIyMjIGAqYCwgYCtgLCBgP2ANCg0KYGBge3J9DQojIOS7u+aEj+mVv+W6pueahCg+PTDkuKop5bCP5YaZ6Iux5paH5a2X5q+NDQpzdHJfdmlld19hbGwoIlRoZSBjYXIgcGFya2VkIGluIHRoZSBnYXJhZ2UuIiwgIlthLXpdKiIpDQpgYGANCg0KYGBge3J9DQojIOS7pTDmiJbmm7TlpJrkuKrnqbrmoLzlvIDlpLTlubbku6Uw5oiW5pu05aSa5Liq56m65qC857uT5bC+44CB5Lit6Ze05pivY2F055qE5a2X56ym5LiyDQpzdHJfdmlld19hbGwoIlRoZSBmYXQgY2F0IHNhdCBvbiB0aGUgY29uY2F0ZW5hdGlvbi4iLCAiXFxzKmNhdFxccyoiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBj5byA5aS0dOe7k+Wwvu+8jOS4remXtOS4gOS4quaIluWkmuS4quS7u+aEj+Wtl+espg0Kc3RyX3ZpZXdfYWxsKCJUaGUgZmF0IGNhdCBzYXQgb24gdGhlIG1hdC4iLCAiYy4rdCIpDQpgYGANCg0KYGBge3J9DQojIDDkuKrmiJYx5LiqVO+8jOWNs+acieaXoOWdh+WPr+OAglRoZeaIlmhlDQpzdHJfdmlld19hbGwoIlRoZSBjYXIgaXMgcGFya2VkIGluIHRoZSBnYXJhZ2UuIiwgIltUXT9oZSIpDQpgYGANCg0KYGBge3J9DQojIOWMuemFjSd1JzDmrKHmiJYx5qyhDQpzdHJfdmlldyhjKCJjb2xvciIsICJjb2xvdXIiLCAiY29sbyIsICJyIiksICJjb2xvdT9yIikNCmBgYA0KDQoNCiMjIyMgYHt9YA0KDQpgYGB7cn0NCiMg5LuF6ZmQMi0z5L2N55qE57qv5pWw5a2XDQpzdHJfdmlld19hbGwoIlRoZSBudW1iZXIgd2FzIDkuOTk5NyBidXQgd2Ugcm91bmRlZCBpdCBvZmYgdG8gMTAuMC4iLCAiWzAtOV17MiwzfSIpDQpgYGANCg0KYGBge3J9DQojIDLoh7Pml6DnqbflpKfkvY0NCnN0cl92aWV3X2FsbCgiVGhlIG51bWJlciB3YXMgOS45OTk3IGJ1dCB3ZSByb3VuZGVkIGl0IG9mZiB0byAxMC4wLiIsICJbMC05XXsyLH0iKQ0KYGBgDQoNCmBgYHtyfQ0KIyAz5L2NDQpzdHJfdmlld19hbGwoIlRoZSBudW1iZXIgd2FzIDkuOTk5NyBidXQgd2Ugcm91bmRlZCBpdCBvZmYgdG8gMTAuMC4iLCAiWzAtOV17M30iKQ0KYGBgDQoNCiMjIyMgYD9gIOaHkuaDsOWMuemFjQ0KDQpgYGB7cn0NCiMg5bC96YeP6ZW/DQpzdHJfdmlldygiVGhlIGNhdCBzYXQgb24gY2F0LiIsICIuK2F0IikNCmBgYA0KDQpgYGB7cn0NCiMg5bC96YeP55+tDQpzdHJfdmlldygiVGhlIGNhdCBzYXQgb24gY2F0LiIsICIuKz9hdCIpDQpgYGANCg0KYGBge3J9DQp4IDwtICIxODg4IGlzIHRoZSBsb25nZXN0IHllYXIgaW4gUm9tYW4gbnVtZXJhbHM6IE1EQ0NDTFhYWFZJSUkuIg0KIyDljLnphY3nrKzkuozkuKonQycw5qyh5oiWMeasoe+8jOi0quWpquWMuemFjeWwvemHj+mVvw0Kc3RyX3ZpZXcoeCwgIkNDPyIpDQpgYGANCg0KYGBge3J9DQojIOaHkuaDsOWMuemFjQ0Kc3RyX3ZpZXcoeCwgIkNDPz8iKQ0KYGBgDQoNCmBgYHtyfQ0KIyDljLnphY3nrKzkuozkuKonQycx5qyh5oiW5aSa5qyh44CB5bC96YeP6ZW/55qE5a2X56ym5LiyDQpzdHJfdmlldyh4LCAiQ0MrIikNCmBgYA0KDQpgYGB7cn0NCiMg5Yy56YWNJ0NDJ+WQjumdouaciSdDJ+aIlidMJzHmrKHmiJblpJrmrKHjgIHlsL3ph4/plb/nmoTlrZfnrKbkuLINCnN0cl92aWV3KHgsICJDQ1tDTF0rIikNCmBgYA0KDQoNCmBgYHtyfQ0KIyDljLnphY0y5LiqJ0MnDQpzdHJfdmlldyh4LCAiQ3syfSIpDQpgYGANCg0KDQpgYGB7cn0NCiMg5Yy56YWNMuS4quS7peS4iidDJw0Kc3RyX3ZpZXcoeCwgIkN7Mix9IikNCmBgYA0KDQpgYGB7cn0NCiMg5Yy56YWNMuWIsDPkuKonQycNCnN0cl92aWV3KHgsICJDezIsM30iKQ0KYGBgDQoNCmBgYHtyfQ0KIyDljLnphY0y5YiwM+S4qidDJ+OAgeWwvemHj+efrQ0Kc3RyX3ZpZXcoeCwgIkN7MiwzfT8iKQ0KYGBgDQoNCmBgYHtyfQ0KIyDljLnphY0nQyflkI7pnaLmnIknTCfmiJYnWCcx5qyh77yI6buY6K6k77yJDQpzdHJfdmlldyh4LCAiQ1tMWF0iKQ0KYGBgDQoNCmBgYHtyfQ0KIyDljLnphY0nQyflkI7pnaLmnIknTCfmiJYnWCcx5qyh5oiW5aSa5qyh44CB5bC96YeP6ZW/55qE5a2X56ym5LiyDQpzdHJfdmlldyh4LCAiQ1tMWF0rIikNCmBgYA0KDQpgYGB7cn0NCiMg5Yy56YWNJ0Mn5ZCO6Z2i5pyJJ0wn5oiWJ1gnMeasoeaIluWkmuasoeOAgeWwvemHj+efreeahOWtl+espuS4sg0Kc3RyX3ZpZXcoeCwgIkNbTFhdKz8iKQ0KYGBgDQoNCg0KIyMjIyBgKClgIOe+pOe7hA0KDQrnvqTvvIzlsIblpJrkuKrlrZfnrKbnu4TmiJDkuIDkuKrljZXkvY3jgILlj6/ku6XlnKggYCgpYCDkuK3nlKggYHxgIOihqOekuuaIlg0KDQpgYGB7cn0NCnN0cl92aWV3KGMoImdyZXkiLCAiZ3JheSIpLCAiZ3IoZXxhKXkiKQ0KYGBgDQoNCmBgYHtyfQ0KIyDljLnphY1jYXLmiJZnYXLmiJZwYXINCnN0cl92aWV3X2FsbCgiVGhlIGNhciBpcyBwYXJrZWQgaW4gdGhlIGdhcmFnZS4iLCAiKGN8Z3xwKWFyIikNCmBgYA0KDQpgYGB7cn0NCiMg5Yy56YWNVGhl5oiWdGhl5oiWY2FyDQpzdHJfdmlld19hbGwoIlRoZSBjYXIgaXMgcGFya2VkIGluIHRoZSBnYXJhZ2UuIiwgIihUfHQpaGV8Y2FyIikNCmBgYA0KDQoNCg0KIyMjIyBgXFxgIOi9rOS5iQ0KDQpgYGB7cn0NCiMgYVwu5piv5LiA5Liq5q2j5YiZ6KGo6L6+5byP77yMJ2FcXC4n5piv6K+l5q2j5YiZ6KGo6L6+5byP55qE5a2X56ym5Liy5b2i5byPDQpzdHJfdmlldyhjKCJhYmMiLCAiYS5jIiwgImJlZiIpLCAiYVxcLiIpDQpgYGANCg0KYGBge3J9DQojIGZhdOaIlmNhdOaIlm1hdOWQjuaOpScuJzDmrKHmiJYx5qyhDQpzdHJfdmlld19hbGwoIlRoZSBmYXQgY2F0IHNhdCBvbiB0aGUgbWF0LiIsICIoZnxjfG0pYXRcXC4/IikNCmBgYA0KDQpgYGB7cn0NCiMg5bem566h6YGT5omT5Y2w5a2X56ym5Liy77yM5Y+z566h6YGT5pi+56S65aaC5L2V5Yy56YWN44CCDQojIOWtl+espuS4slxcXFzvvIznm7jlvZPkuo7mraPliJnooajovr7lvI/nmoRcXO+8jOe7j+i/h+i9rOS5ieaJjeiDveWMuemFjSdcJw0KImFcXGIiICVUPiUgd3JpdGVMaW5lcygpICU+JSBzdHJfdmlldygiXFxcXCIpDQpgYGANCg0KDQojIyMjIGBeYOWSjGAkYA0KDQpgYGB7cn0NCiMg5LuO5a2X56ym5Liy5byA56uv5Yy56YWNVGhl5oiWdGhlDQpzdHJfdmlld19hbGwoIlRoZSBjYXIgaXMgcGFya2VkIGluIHRoZSBnYXJhZ2UuIiwgIl4oVHx0KWhlIikNCmBgYA0KDQpgYGB7cn0NCiMg5LuO5a2X56ym5Liy5pyr56uv5Yy56YWNYXQuDQpzdHJfdmlld19hbGwoIlRoZSBmYXQgY2F0LiBzYXQuIG9uIHRoZSBtYXQuIiwgIihhdFxcLikkIikNCmBgYA0KDQpgYGB7cn0NCnggPC0gYygiYXBwbGUgcGllIiwgImFwcGxlIiwgImFwcGxlIGNha2UiKQ0Kc3RyX3ZpZXcoeCwgImFwcGxlIikNCmBgYA0KDQpgYGB7cn0NCiMg5LuO5aS05Yiw5bC+5Lil5qC85Yy56YWN5a2X56ym5Liy55qE5YWo6YOo77yM5LiN5YaN5piv5om+5Yiw6YOo5YiG5Y2z5Y+vDQpzdHJfdmlldyh4LCAiXmFwcGxlJCIpDQpgYGANCg0KDQojIyMjIOaWreiogA0KDQpgKClg6YeM6Z2i5piv5LiA5Liq5Yik5pat5p2h5Lu244CCDQoNCmBgYHtyfQ0KIyDljLnphY3lkI7pnaLntKfpmo/nnYDnqbrmoLzlkoxmYXTnmoRUaGXmiJZ0aGUNCnN0cl92aWV3X2FsbCgiVGhlIGZhdCBjYXQgc2F0IG9uIHRoZSBtYXQuIiwgIihUfHQpaGUoPz1cXHNmYXQpIikNCmBgYA0KDQpgYGB7cn0NCiMg5Yy56YWN5YW25ZCO5LiN6Lef6ZqP6ICF56m65qC85ZKMZmF055qEVGhl5oiWdGhlDQpzdHJfdmlld19hbGwoIlRoZSBmYXQgY2F0IHNhdCBvbiB0aGUgbWF0LiIsICIoVHx0KWhlKD8hXFxzZmF0KSIpDQpgYGANCg0KYGBge3J9DQojIOWMuemFjeWFtuWJjee0p+i3n1RoZeaIlnRoZeWKoOepuuagvOeahGZhdOaIlm1hdA0Kc3RyX3ZpZXdfYWxsKCJUaGUgZmF0IGNhdCBzYXQgb24gdGhlIG1hdC4iLCAiKD88PShUfHQpaGVcXHMpKGZhdHxtYXQpIikNCmBgYA0KDQpgYGB7cn0NCiMg5Yy56YWN5YW25YmN5rKh5pyJVGhl5oiWdGhl5Yqg56m65qC855qEY2F0DQpzdHJfdmlld19hbGwoIlRoZSBjYXQgc2F0IG9uIGNhdC4iLCAiKD88IShUfHQpaGVcXHMpKGNhdCkiKQ0KYGBgDQoNCg0KIyMjIOWIhue7hOS4juWbnua6r+W8leeUqA0KDQrmi6zlj7fov5jlj6/ku6XlrprkuYki5YiG57uEIu+8jOWPr+S7pemAmui/h+Wbnua6r+W8leeUqO+8iOWmglxcMeOAgVxcMuetie+8ieadpeW8leeUqOi/meS6m+WIhue7hOOAguS+i+Wmgu+8jOS7peS4i+eahOato+WImeihqOi+vuW8j+WPr+S7peaJvuWHuuWQjeensOS4reaciemHjeWkjeeahOS4gOWvueWtl+avjeeahOaJgOacieawtOaenO+8mg0KDQpgYGB7cn0NCiMgLuS4uuS7u+aEj+Wtl+espu+8jFwx5Li65byV55SoMeasoeaLrOWPt+S4reeahOWIhue7hA0KIyDor6XmraPliJnooajovr7lvI/ooajnpLphYmFi5Z6L5a2X56ym5LiyDQpzdHJfdmlldyhmcnVpdCwgIiguLilcXDEiLCBtYXRjaCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQojIGFiY2Fi5Z6LDQpzdHJfdmlldyhmcnVpdCwgIiguLikoLilcXDEiLCBtYXRjaCA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQojIGFiY2PlnosNCnN0cl92aWV3KGZydWl0LCAiKC4uKSguKVxcMiIsIG1hdGNoID0gVFJVRSkNCmBgYA0KDQoNCiMjIOW6lOeUqOato+WImeihqOi+vuW8jw0KDQojIyMg5Yy56YWN5qOA5rWLDQoNCmBzdHJfZGV0ZWN0KClg6L+U5Zue5LiO6L6T5YWl5ZCR6YeP5YW35pyJ5ZCM5qC36ZW/5bqm55qE6YC76L6R5ZCR6YePDQoNCmBgYHtyfQ0KeCA8LSBjKCJhcHBsZSIsICJiYW5hbmEiLCAicGVhciIpDQpzdHJfZGV0ZWN0KHgsICJlIikNCg0KIyDmnInlpJrlsJHkuKrku6V05byA5aS055qE5bi455So5Y2V6K+N77yfDQpzdW0oc3RyX2RldGVjdCh3b3JkcywgIl50IikpDQoNCiMg5Lul5YWD6Z+z5a2X5q+N57uT5bC+55qE5bi455So5Y2V6K+N55qE5q+U5L6L5piv5aSa5bCR77yfDQptZWFuKHN0cl9kZXRlY3Qod29yZHMsICJbYWVpb3VdJCIpKQ0KYGBgDQoNCuW9k+mAu+i+keadoeS7tumdnuW4uOWkjeadguaXtu+8iOS+i+Wmgu+8jOWMuemFjSBhIOaIliBi77yM5L2G5LiN5Yy56YWNIGPvvIzpmaTpnZ4gZCDmiJDnq4vvvInvvIzlupTor6XlsIblhbbliIbop6PkuLrlh6DkuKrmm7TlsI/nmoTlrZDooajovr7lvI/vvIzlsIbmr4/kuKrlrZDooajovr7lvI/kvb/nlKhgc3RyX2RldGVjdCgpYOeahOWMuemFjee7k+aenOi1i+e7meS4gOS4quWPmOmHj++8jOW5tuS9v+eUqOmAu+i+kei/kOeul+e7hOWQiOi1t+adpeOAgg0KDQpgYGB7cn0NCiMjIOWvu+aJvuS4jeWQq2FlaW9155qE5Y2V6K+NDQoNCiMg5om+5Ye65bi455So6K+N5Lit6Iez5bCR5YyF5ZCr5LiA5Liq5YWD6Z+z5a2X5q+N55qE5omA5pyJ5Y2V6K+N77yM54S25ZCO5Y+W5Y+NDQpub192b3dlbHNfMSA8LSAhc3RyX2RldGVjdCh3b3JkcywgIlthZWlvdV0iKQ0KIyDlsZXnpLrkuI3ljIXlkKthZWlvdeeahOW4uOeUqOWNleivjQ0Kd29yZHNbbm9fdm93ZWxzXzFdDQoNCiMg5om+5Ye65LuF5YyF5ZCr6L6F6Z+z5a2X5q+N77yI6Z2e5YWD6Z+z5a2X5q+N77yJ55qE5omA5pyJ5Y2V6K+NDQojIFteYWVpb3Vd6KGo56S66Z2eYWVpb3XvvIzljbPovoXpn7PlrZfmr40NCiMgXui+hemfs+Wtl+avjSsk77yM6KGo56S65LuO5aS05Yiw5bC+MeS4quaIluWkmuS4qui+hemfs+Wtl+avjeeahOWujOWFqOWMuemFjQ0Kbm9fdm93ZWxzXzIgPC0gc3RyX2RldGVjdCh3b3JkcywgIl5bXmFlaW91XSskIikNCndvcmRzW25vX3Zvd2Vsc18yXQ0KDQppZGVudGljYWwobm9fdm93ZWxzXzEsIG5vX3Zvd2Vsc18yKQ0KYGBgDQoNCmBgYHtyfQ0Kd29yZHNbc3RyX2RldGVjdCh3b3JkcywgIngkIildDQpzdHJfc3Vic2V0KHdvcmRzLCAieCQiKQ0KDQpkZiA8LSB0aWJibGUoDQogIHdvcmQgPSB3b3JkcywNCiAgaSA9IHNlcV9hbG9uZyh3b3JkKQ0KKQ0KZGYgJT4lDQogIGZpbHRlcihzdHJfZGV0ZWN0KHdvcmRzLCAieCQiKSkNCmBgYA0KDQojIyMg5Yy56YWN6K6h5pWwDQoNCmBzdHJfY291bnQoKWDov5Tlm57lrZfnrKbkuLLkuK3kuI5wYXR0ZXJu5Yy56YWN55qE5a2Q6ZuG55qE5pWw6YePDQoNCmBgYHtyfQ0KeCA8LSBjKCJhcHBsZSIsICJiYW5hbmEiLCAicGVhciIpDQpzdHJfY291bnQoeCwgImEiKQ0KDQojIOW5s+Wdh+adpeeci++8jOavj+S4quWNleivjeS4reacieWkmuWwkeS4quWFg+mfs+Wtl+avje+8nw0KbWVhbihzdHJfY291bnQod29yZHMsICJbYWVpb3VdIikpDQoNCmRmICU+JQ0KICBtdXRhdGUoDQogICAgdm93ZWxzID0gc3RyX2NvdW50KHdvcmQsICJbYWVpb3VdIiksDQogICAgY29uc29uYW50cyA9IHN0cl9jb3VudCh3b3JkLCAiW15hZWlvdV0iKQ0KICApDQpgYGANCg0K5rOo5oSP77yM5Yy56YWN5LuO5p2l5LiN5Lya6YeN5Y+g44CC5L6L5aaC77yM5ZyoICJhYmFiYWJhIiDkuK3vvIzmqKHlvI8gImFiYSIg5Lya5Yy56YWN5aSa5bCR5qyh77yf5q2j5YiZ6KGo6L6+5byP5Lya5ZGK6K+J5L2g5pivIDIg5qyh77yM6ICM5LiN5pivIDMg5qyhDQoNCmBgYHtyfQ0Kc3RyX2NvdW50KCJhYmFiYWJhIiwgImFiYSIpDQpzdHJfdmlldygiYWJhYmFiYSIsICJhYmEiKQ0KYGBgDQoNCmBgYHtyfQ0Kc3RyX3ZpZXdfYWxsKCJhYmFiYWJhIiwgImFiYSIpDQpgYGANCg0KDQojIyMg5o+Q5Y+W5Yy56YWN5YaF5a65DQoNCmBzdHJfZXh0cmFjdCgpYOWSjGBzdHJfZXh0cmFjdCgpX2FsbGANCg0KYGBge3J9DQojIHNlbnRlbmNlc+aVsOaNrumbhg0KaGVhZChzdHJpbmdyOjpzZW50ZW5jZXMpDQoNCmNvbG9ycyA8LSBjKA0KICAicmVkIiwgIm9yYW5nZSIsICJ5ZWxsb3ciLCAiZ3JlZW4iLCAiYmx1ZSIsICJwdXJwbGUiDQopDQpjb2xvcl9tYXRjaCA8LSBzdHJfYyhjb2xvcnMsIGNvbGxhcHNlID0gInwiKQ0KY29sb3JfbWF0Y2gNCmBgYA0KDQpgYGB7cn0NCiMg5a2X56ym5Liy5Lit55qEJ3wn6L2s5o2i5Li65q2j5YiZ6KGo6L6+5byP5piv4oCc5oiW4oCdDQojIOmAieWPluWQq+acieminOiJsueahOWPpeWtkOWtkOmbhg0KaGFzX2NvbG9yIDwtIHN0cl9zdWJzZXQoc2VudGVuY2VzLCBjb2xvcl9tYXRjaCkNCm1hdGNoZXMgPC0gc3RyX2V4dHJhY3QoaGFzX2NvbG9yLCBjb2xvcl9tYXRjaCkgIyDmj5Dlj5bljLnphY3lhoXlrrkNCm1hdGNoZXMNCg0KIyDljIXlkKvlpJrkuo7kuIDnp43popzoibLnmoTlj6XlrZANCm1vcmUgPC0gc2VudGVuY2VzW3N0cl9jb3VudChzZW50ZW5jZXMsIGNvbG9yX21hdGNoKSA+IDFdDQpzdHJfdmlld19hbGwobW9yZSwgY29sb3JfbWF0Y2gpDQpgYGANCg0KYGBge3J9DQpzdHJfZXh0cmFjdChtb3JlLCBjb2xvcl9tYXRjaCkNCmBgYA0KDQrms6jmhI/vvIxgc3RyX2V4dHJhY3QoKWDlj6rmj5Dlj5bnrKzkuIDkuKrljLnphY3vvIzvvIzlm6DkuLrljZXkuKrljLnphY3lj6/ku6Xkvb/nlKjmm7TnroDljZXnmoTmlbDmja7nu5PmnoTjgILopoHmg7PlvpfliLDmiYDmnInljLnphY3vvIzlj6/ku6Xkvb/nlKhgc3RyX2V4dHJhY3RfYWxsKClg5Ye95pWw77yM5a6D5Lya6L+U5Zue5LiA5Liq5YiX6KGo77yaDQoNCmBgYHtyfQ0Kc3RyX2V4dHJhY3RfYWxsKG1vcmUsIGNvbG9yX21hdGNoKQ0KYGBgDQoNCuWmguaenOiuvue9ruS6huWPguaVsHNpbXBsaWZ5PVRSVUXvvIzpgqPkuYhgc3RyX2V4dHJhY3RfYWxsKClg5Lya6L+U5Zue5LiA5Liq55+p6Zi177yM5YW25Lit6L6D55+t55qE5Yy56YWN5Lya5omp5bGV5Yiw5LiO5pyA6ZW/55qE5Yy56YWN5YW35pyJ5ZCM5qC355qE6ZW/5bqm77yaDQoNCmBgYHtyfQ0Kc3RyX2V4dHJhY3RfYWxsKG1vcmUsIGNvbG9yX21hdGNoLCBzaW1wbGlmeSA9IFRSVUUpDQoNCnggPC0gYygiYSIsICJhIGIiLCAiYSBiIGMiKQ0Kc3RyX2V4dHJhY3RfYWxsKHgsICJbYS16XSIpDQpzdHJfZXh0cmFjdF9hbGwoeCwgIlthLXpdIiwgc2ltcGxpZnkgPSBUUlVFKQ0KYGBgDQoNCiMjIyDphY3lkIhgKClg5YiG57uE5o+Q5Y+WDQoNCnBhdHRlcm7kuK3nlKhgKClg5YiG5aW957uE77yM5L6/5Y+v5Lul55SoYHN0cl9tYXRjaCgpYOaPkOWPluS6huOAgg0KDQrkvovvvJrmiJHku6zmg7Pku47lj6XlrZDkuK3mj5Dlj5blh7rlkI3or43jgILmiJHku6zlhYjov5vooYzkuIDnp43lkK/lj5HlvI/lrp7pqozvvIzmib7lh7rot5/lnKggYSDmiJYgdGhlIOWQjumdoueahOaJgOacieWNleivjeOAguWboOS4uuS9v+eUqOato+WImeihqOi+vuW8j+WumuS5iSLljZXor40i5pyJ5LiA54K56Zq+5bqm77yM5omA5Lul5oiR5Lus5L2/55So5LiA56eN566A5Y2V55qE6L+R5Ly85a6a5LmJLS0tLS0t6Iez5bCR5pyJMeS4qumdnuepuuagvOWtl+espueahOWtl+espuW6j+WIl++8mg0KDQpgYGB7cn0NCiMgYeaIlnRoZSvnqbrmoLwr5LiA5Liq5oiW5aSa5Liq6Z2e56m65qC85a2X56ymDQpub3VuIDwtICIoYXx0aGUpIChbXiBdKykiDQpoYXNfbm91biA8LSBzZW50ZW5jZXMgJT4lDQogIHN0cl9zdWJzZXQobm91bikgJT4lDQogIGhlYWQoMTApDQpoYXNfbm91biAlPiUNCiAgc3RyX2V4dHJhY3Qobm91bikNCmBgYA0KDQpzdHJfbWF0Y2goKSDlh73mlbDlj6/ku6Xnu5nlh7rmr4/kuKrni6znq4vliIbnu4TjgIJzdHJfbWF0Y2goKei/lOWbnueahOS4jeaYr+Wtl+espuWQkemHj++8jOiAjOaYr+S4gOS4quefqemYte+8jOWFtuS4reS4gOWIl+aYr+WujOaVtOWMuemFje+8jOWQjumdoueahOWIl+aYr+avj+S4quWIhue7hOeahOWMuemFjQ0KDQpgYGB7cn0NCmhhc19ub3VuICU+JQ0KICBzdHJfbWF0Y2gobm91bikNCmhhc19ub3VuICU+JQ0KICBzdHJfbWF0Y2hfYWxsKG5vdW4pDQpgYGANCg0K5aaC5p6c5pWw5o2u5piv5L+d5a2Y5ZyodGliYmxl5Lit55qE77yM6YKj5LmI5L2/55SodGlkeXI6OmV4dHJhY3QoKeS8muabtOWuueaYk+OAgui/meS4quWHveaVsOeahOW3peS9nOaWueW8j+S4jnN0cl9tYXRjaCgp5Ye95pWw57G75Ly877yM5Y+q5piv6KaB5rGC5Li6Kirmr4/kuKoqKuWIhue7hOaPkOS+m+S4gOS4quWQjeensO+8jOS7peS9nOS4uuaWsOWIl+aUvuWcqCB0aWJibGUg5LitDQoNCmBgYHtyfQ0KdGliYmxlKHNlbnRlbmNlID0gc2VudGVuY2VzKSAlPiUNCiAgdGlkeXI6OmV4dHJhY3QoDQogICAgc2VudGVuY2UsIGMoImFydGljbGUiLCAibm91biIpLCAiKGF8dGhlKSAoW14gXSspIiwNCiAgICByZW1vdmUgPSBGQUxTRQ0KICApDQpgYGANCg0KIyMjIOabv+aNouWMuemFjeWGheWuuQ0KDQpgc3RyX3JlcGxhY2UoKWAsIGBzdHJfcmVwbGFjZV9hbGwoKWANCg0KYGBge3J9DQp4IDwtIGMoImFwcGxlIiwgInBlYXIiLCAiYmFuYW5hIikNCnN0cl9yZXBsYWNlKHgsICJbYWVpb3VdIiwgIi0iKQ0Kc3RyX3JlcGxhY2VfYWxsKHgsICJbYWVpb3VdIiwgIi0iKQ0KYGBgDQoNCmBgYHtyfQ0KeCA8LSBjKCIxIGhvdXNlIiwgIjIgY2FycyIsICIzIHBlb3BsZSIpDQpzdHJfcmVwbGFjZV9hbGwoeCwgYygiMSIgPSAib25lIiwgIjIiID0gInR3byIsICIzIiA9ICJ0aHJlZSIpKQ0KYGBgDQoNCui/mOWPr+S7peS9v+eUqOWbnua6r+W8leeUqOadpeaPkuWFpeWMuemFjeS4reeahOWIhue7hOOAguWcqOS4i+mdoueahOS7o+eggeS4re+8jOaIkeS7rOS6pOaNouS6huesrOS6jOS4quWNleivjeWSjOesrOS4ieS4quWNleivjeeahOmhuuW6j++8mg0KDQpgYGB7cn0NCiMgW15cXHNdK+S7o+ihqOWkmuS4qumdnuepuuagvOWtl+espu+8jOWNs+S4gOS4quWNleivjQ0KIyAnKFteXFxzXSspIChbXlxcc10rKSAoW15cXHNdKykn5piv5YmN5LiJ5Liq5Y2V6K+NDQpzZW50ZW5jZXMgJT4lDQogIHN0cl9yZXBsYWNlKCIoW15cXHNdKykgKFteXFxzXSspIChbXlxcc10rKSIsICJcXDEgXFwzIFxcMiIpICU+JQ0KICBoZWFkKDUpDQpgYGANCg0KIyMjIOaLhuWIhuWtl+espuS4sg0KDQpgc3RyX3NwbGl0KClgDQoNCuWboOS4uuWtl+espuWQkemHj+eahOavj+S4quWIhumHj+S8muWMheWQq+S4jeWQjOaVsOmHj+eahOeJh+aute+8jOaJgOS7pSBzdHJfc3BsaXQoKSDkvJrov5Tlm57kuIDkuKrliJfooag6DQoNCmBgYHtyfQ0Kc2VudGVuY2VzICU+JQ0KICBoZWFkKDUpICU+JQ0KICBzdHJfc3BsaXQoIiAiKQ0KYGBgDQoNCuWmguaenOS9oOaLhuWIhueahOaYr+mVv+W6puS4ujHnmoTlkJHph4/vvIzpgqPkuYjlj6ropoHnroDljZXlnLDmj5Dlj5bliJfooajnmoTnrKzkuIDkuKrlhYPntKDljbPlj6/vvJoNCg0KYGBge3J9DQojIOWtl+espuS4suS4uiJcXHwi77yM5q2j5YiZ6KGo6L6+5byP5Li6XHzvvIznvJbor5Hml7bnmoTlkKvkuYnkuLrlrZfnrKYnfCcNCiJhfGJ8Y3xkIiAlPiUNCiAgc3RyX3NwbGl0KCJcXHwiKSAlPiUNCiAgbWFncml0dHI6OmV4dHJhY3QyKDEpDQpgYGANCg0K5Lmf5Y+v5Lul6YCa6L+H6K6+572uIHNpbXBsaWZ5ID0gVFJVRSDov5Tlm57kuIDkuKrnn6npmLXvvJoNCg0KYGBge3J9DQpzZW50ZW5jZXMgJT4lDQogIGhlYWQoNSkgJT4lDQogIHN0cl9zcGxpdCgiICIsIHNpbXBsaWZ5ID0gVFJVRSkNCmBgYA0KDQrov5jlj6/ku6Xorr7lrprmi4bliIbniYfmrrXnmoTmnIDlpKfmlbDph4/vvJoNCg0KYGBge3J9DQpmaWVsZHMgPC0gYygiTmFtZTogSGFkbGV5IiwgIkNvdW50cnk6IE5aIiwgIkFnZTogMzU6IDE5ODAiKQ0KZmllbGRzICU+JSBzdHJfc3BsaXQoIjogIiwgc2ltcGxpZnkgPSBUUlVFKQ0KZmllbGRzICU+JSBzdHJfc3BsaXQoIjogIiwgbiA9IDIsIHNpbXBsaWZ5ID0gVFJVRSkNCmBgYA0KDQrpmaTkuobmqKHlvI/vvIzkvaDov5jlj6/ku6XpgJrov4flrZfmr43jgIHooYzjgIHlj6XlrZDlkozljZXor43ovrnnlYzvvIhib3VuZGFyeSgpIOWHveaVsO+8ieadpeaLhuWIhuWtl+espuS4su+8mg0KDQpgYGB7cn0NCnggPC0gIlRoaXMgaXMgYSBzZW50ZW5jZS4gVGhpcyBpcyBhbm90aGVyIHNlbnRlbmNlLiINCnN0cl92aWV3X2FsbCh4LCBib3VuZGFyeSgid29yZCIpKSAjIOWMuemFjXdvcmTmlbDmja7pm4bkuK3nmoTluLjnlKjor40NCmBgYA0KDQpgYGB7cn0NCnN0cl9zcGxpdCh4LCAiICIpW1sxXV0NCnN0cl9zcGxpdCh4LCBib3VuZGFyeSgid29yZCIpKVtbMV1dDQpgYGANCg0KIyMjIOWumuS9jeWMuemFjeWGheWuuQ0KDQpgc3RyX2xvY2F0ZSgpYCwgYHN0cl9sb2NhdGVfYWxsKClgDQoNCuWug+S7rOWPr+S7pee7meWHuuavj+S4quWMuemFjeeahOW8gOWni+S9jee9ruWSjOe7k+adn+S9jee9ruOAguS9oOWPr+S7peS9v+eUqCBzdHJfbG9jYXRlKCkg5Ye95pWw5om+5Ye65Yy56YWN55qE5qih5byP77yM54S25ZCO5L2/55SoIHN0cl9zdWIoKSDlh73mlbDmnaXmj5Dlj5bmiJbkv67mlLnljLnphY3nmoTlhoXlrrnjgIINCg0KIyMgcGF0dGVybueahOWFtuS7luWPguaVsOWSjOWMuemFjeaWueazlQ0KDQojIyMgcmVnZXgoKeeahOWPguaVsA0KDQrlvZPkvb/nlKjkuIDkuKrlrZfnrKbkuLLkvZzkuLrmqKHlvI/ml7bvvIxS5Lya6Ieq5Yqo6LCD55SocmVnZXgoKeWHveaVsOWvueWFtui/m+ihjOWMheijhe+8mg0KDQpgYGB7cn0NCiMg5q2j5bi46LCD55So77yaDQpzdHJfdmlldyhmcnVpdCwgIm5hbmEiKQ0KYGBgDQoNCmBgYHtyfQ0KIyDkuIrpnaLlvaLlvI/mmK/ku6XkuIvlvaLlvI/nmoTnroDlhpkNCnN0cl92aWV3KGZydWl0LCByZWdleCgibmFuYSIpKQ0KYGBgDQoNCuWPr+S7peS9v+eUqCByZWdleCgpIOWHveaVsOeahOWFtuS7luWPguaVsOadpeaOp+WItuWFt+S9k+eahOWMuemFjeaWueW8jzoNCg0KIyMjIyDlpKflsI/lhpkNCg0KaWdub3JlX2Nhc2UgPSBUUlVFIOaXouWPr+S7peWMuemFjeWkp+WGmeWtl+avje+8jOS5n+WPr+S7peWMuemFjeWwj+WGmeWtl+avjQ0KDQpgYGB7cn0NCmJhbmFuYXMgPC0gYygiYmFuYW5hIiwgIkJhbmFuYSIsICJCQU5BTkEiKQ0Kc3RyX3ZpZXcoYmFuYW5hcywgImJhbmFuYSIpDQpgYGANCg0KYGBge3J9DQpzdHJfdmlldyhiYW5hbmFzLCByZWdleCgiYmFuYW5hIiwgaWdub3JlX2Nhc2UgPSBUUlVFKSkNCmBgYA0KDQojIyMjIOS7juavj+ihjOW8gOWniw0KDQptdWx0aWxpbmUgPSBUUlVF5Y+v5Lul5L2/5b6XXF7lkoxcJOS7juavj+ihjOeahOW8gOWktOWSjOacq+WwvuW8gOWni+WMuemFje+8jOiAjOS4jeaYr+S7juWujOaVtOWtl+espuS4sueahOW8gOWktOWSjOacq+WwvuW8gOWni+WMuemFje+8mg0KDQpgYGB7cn0NCnggPC0gIkxpbmUgMVxuTGluZSAyXG5MaW5lIDMiDQpzdHJfZXh0cmFjdF9hbGwoeCwgIl5MaW5lIilbWzFdXQ0Kc3RyX2V4dHJhY3RfYWxsKHgsIHJlZ2V4KCJeTGluZSIsIG11bHRpbGluZSA9IFRSVUUpKVtbMV1dDQpgYGANCg0KIyMjIyDlnKjmraPliJnooajovr7lvI/kuK3liqDlhaXms6jph4oNCg0KY29tbWVudHMgPSBUUlVFIOWPr+S7peiuqeS9oOWcqOWkjeadgueahOato+WImeihqOi+vuW8j+S4reWKoOWFpeazqOmHiuWSjOepuueZveWtl+espu+8jOS7peS+v+abtOaYk+eQhuino+OAguWMuemFjeaXtuS8muW/veeVpeepuuagvOWSjCPlkI7pnaLnmoTlhoXlrrnjgILlpoLmnpzmg7PopoHljLnphY3kuIDkuKrnqbrmoLzvvIzkvaDpnIDopoHlr7nlhbbov5vooYzovazkuYnvvJogIlxcICLvvJoNCg0KYGBge3J9DQpwaG9uZSA8LSByZWdleCgiDQogIFxcKD8gIyDlj6/pgInnmoTlt6blsI/mi6zlj7cNCiAgKFxcZHszfSkgIyDlnLDljLrnvJbnoIENCiAgWyktIF0/ICMg5Y+v6YCJ55qE5Y+z5bCP5ous5Y+344CB55+t5YiS57q/5oiW56m65qC8DQogIChcXGR7M30pICMg5Y+m5aSWM+S4quaVsOWtlw0KICBbIC1dPyAjIOWPr+mAieeahOepuuagvOaIluefreWIkue6vw0KICAoXFxkezN9KSAjIOWPpuWkljPkuKrmlbDlrZcNCiIsIGNvbW1lbnRzID0gVFJVRSkNCg0Kc3RyX21hdGNoKCI1MTQtNzkxLTgxNDEiLCBwaG9uZSkNCmBgYA0KDQojIyMjICcuJ+WPr+S7peWMuemFjVxcbg0KDQpkb3RhbGwgPSBUUlVFIOWPr+S7peS9v+W+lycuJ+WMuemFjeWMheaLrCBcXG4g5Zyo5YaF55qE5omA5pyJ5a2X56ym44CCDQoNCg0KIyMjIGZpeGVkKCkNCg0KZml4ZWQoKSDlh73mlbDlj6/ku6XmjInnhaflrZfnrKbkuLLnmoTlrZfoioLlvaLlvI/ov5vooYznsr7noa7ljLnphY3vvIzlroPkvJrlv73nlaXmraPliJnooajovr7lvI/kuK3nmoTmiYDmnInnibnmrorlrZfnrKbvvIzlubblnKjpnZ7luLjkvY7nmoTlsYLmrKHkuIrov5vooYzmk43kvZzjgILov5nmoLflj6/ku6XkuI3nlKjov5vooYzpgqPkupvlpI3mnYLnmoTovazkuYnmk43kvZzvvIzogIzkuJTpgJ/luqbmr5Tmma7pgJrmraPliJnooajovr7lvI/opoHlv6vlvojlpJrjgIINCg0K5L2G5piv77yM5Zyo5Yy56YWN6Z2e6Iux6K+t5pWw5o2u5pe277yM6KaB5oWO55SoIGZpeGVkKCkg5Ye95pWw44CC5a6D5Y+v6IO95Lya5Ye6546w6Zeu6aKY77yM5Zug5Li65q2k5pe25ZCM5LiA5Liq5a2X56ym57uP5bi45pyJ5aSa56eN6KGo6L6+5pa55byP44CCDQoNCmBgYHtyfQ0KbWljcm9iZW5jaG1hcms6Om1pY3JvYmVuY2htYXJrKA0KICBmaXhlZCA9IHN0cl9kZXRlY3Qoc2VudGVuY2VzLCBmaXhlZCgidGhlIikpLA0KICByZWdleCA9IHN0cl9kZXRlY3Qoc2VudGVuY2VzLCAidGhlIiksDQogIHRpbWVzID0gMjANCikNCmBgYA0KDQojIyMgY29sbCgpDQoNCuWHveaVsOS9v+eUqOagh+WHhuaOkuW6j+inhOWImeadpeavlOi+g+Wtl+espuS4su+8jOi/meWcqOi/m+ihjOS4jeWMuuWIhuWkp+Wwj+WGmeeahOWMuemFjeaXtuaYr+mdnuW4uOacieaViOeahOOAgg0K