字符串基础
readLines()
在R中读入一个文本文件可以用readLines()
函数,它返回一个字符型向量,文件中每一行都是向量中对应的一个元素。这个文件可以是本地文件,也可以是一个网址。
index <- readLines ("https://www.baidu.com" ) # 我的主页
#> Warning in readLines("https://www.baidu.com"): incomplete final line found on
#> 'https://www.baidu.com'
#> [1] "<html>"
#> [2] "<head>"
#> [3] "\t<script>"
#> [4] "\t\tlocation.replace(location.href.replace(\"https://\",\"http://\"));"
#> [5] "\t</script>"
#> [6] "</head>"
readline()
支持从键盘直接输入一行文本,显然,R必须在交互式模式下运行,否则人没法输入任何东西。它有一个常用的地方,就是在代码运行过程中要求用户输入一些回答,例如:
x <- readline ("Answer yes or no: " )
当R运行到这一行时会停下来等待用户输入(回车表示输入结束),然后根据输入的结果,代码可以接着执行。
在Windows系统中,readLines()
读utf-8编码的txt文件会导致乱码,只能顺利读取ANSI编码的txt文件。
writeLines()
注意,字符串的打印形式与其本身的内容不是相同的,因为打印形式中会显示出转义字符。如果想要查看字符串的真正内容,可以使用writeLines()
writeLines(text, con = stdout(), sep = “”, useBytes = FALSE)
Windows系统中writeLines()
写入txt文件会导致乱码,替代方案是用writeBin(charToRaw(string), file)
写入文件
字符串包含引文时引号的使用
若一个字符串包含引文,为了符合行文习惯(双引号),并使代码引号有层次的区别,外层应用单引号。
# string1 <- 'When putting a 'quote' inside a string, use single quotation marks.'
# string1 <- "When putting a "quote" inside a string, use double quotation marks."
string3 <- "When putting a 'quote' inside a string, use single quotation marks inside and double quotation marks outside."
string4 <- 'When putting a "quote" inside a string, use double quotation marks inside and single quotation marks outside.'
# 字符串的打印形式
string3
string4
#> [1] "When putting a 'quote' inside a string, use single quotation marks inside and double quotation marks outside."
#> [1] "When putting a \"quote\" inside a string, use double quotation marks inside and single quotation marks outside."
# 字符串编译后导出的形式
writeLines (string3)
writeLines (string4)
#> When putting a 'quote' inside a string, use single quotation marks inside and double quotation marks outside.
#> When putting a "quote" inside a string, use double quotation marks inside and single quotation marks outside.
string1和string2有语法错误,前后两个字符串中间夹了一个编译器不认识的quote。
string3和string4在语法上没问题,但编译时效果不同。string4才是我们想要的。
转义符号”\”
反斜杠”\“可以将R语言及R Markdown的语法部件转义为字符。
如果想要在字符串中包含一个单引号或双引号,可以使用”\’“和”\““对其进行转义。
string5 <- 'I don \' t know why they called us "Chinese".'
string6 <- "When putting a \" quote \" inside a string, you can use backslash as well."
writeLines (string5)
writeLines (string6)
#> I don't know why they called us "Chinese".
#> When putting a "quote" inside a string, you can use backslash as well.
如果想要在字符串中包含一个反斜杠,就需要使用两个反斜杠:“\\”
string7 <- "I think the backslash \"\\\" is amazing!"
print (string7) # 或 string7
writeLines (string7)
#> [1] "I think the backslash \"\\\" is amazing!"
#> I think the backslash "\" is amazing!
特殊字符
其他几种特殊字符。最常用的是换行符”\n”、制表符”\t”和退格”\b”,你可以使用
?‘“’ 或 ?”’” 调出帮助文件来查看完整的特殊字符列表。
string8 <- 'The special characters " \n ", " \t " and " \b " are very interesing.'
string9 <- "The special characters \"\\ n \" , \"\\ t \" and \"\\ b \" are very interesing."
writeLines (string8)
writeLines (string9)
#> The special characters "
#> ", " " and "" are very interesing.
#> The special characters "\n", "\t" and "\b" are very interesing.
生成的html文件中”\b”变成了一个奇怪的方框,控制台显示的则是正确的。可能R
Markdown与R语言中”\b”的含义不同。
字符串格式化
base::sprintf()
sprintf(包含占位符的字符串,欲填充的字符串)
%
如果要在占位符内部使用%,需要写成%%
转换符
类型
d
十进制整数
o
八进制整数
x, X
小/大写十六进制整数
f
十进制浮点数
e, E
科学计数法表示的数,e小写或大写
g, G
智能选择使用浮点数还是科学计数法(g对应e,G对应E),默认显示6位有效数字
s
字符串
浮点数格式
width.precision
输出的最小宽度(不足以空格补齐).输出精度(对f和e/E意味着小数点后几位,对g/G意味着有效数字位数)
-
指定左对齐(默认右对齐),即宽度不足时从右边补充空格
+
永远带符号(默认只有负数带一个负号,正数不带符号)
空格
首字符并非负号时,空一格
0
宽度不足时补0,而非默认的空格
sprintf ("The ratio is %d%%" , 10 )
#> [1] "The ratio is 10%"
#> [1] "3.141593"
sprintf ("%.3f" , pi) # 保留小数点后3位
#> [1] "3.142"
sprintf ("%.0f" , pi) # 保留小数点后0位
#> [1] "3"
sprintf ("%5.1f" , pi) # 最小宽度为5,保留小数点后1位,不足补空格
#> [1] " 3.1"
sprintf ("%05.1f" , pi) # 用0补齐
#> [1] "003.1"
sprintf ("%+f" , pi) # 永远带符号
#> [1] "+3.141593"
#> [1] " 3.141593"
sprintf ("%-10f" , pi) # 左对齐,宽度为10
#> [1] "3.141593 "
#> [1] "3.141593e+00"
#> [1] "3.141593E+00"
#> [1] "3.14159"
#> [1] "3.14159e+06"
sprintf ("%.9g" , 1e6 * pi)
#> [1] "3141592.65"
#> [1] "24"
sprintf (
"min 10-char string '%10s'" ,
c ("a" , "ABC" , "and an even longer one" )
)
#> [1] "min 10-char string ' a'"
#> [2] "min 10-char string ' ABC'"
#> [3] "min 10-char string 'and an even longer one'"
借助 python
加入 python 代码块,使用 python 中的字符串格式化方法 .format()
模板字符串.format(以逗号分隔的填充参数序列)
其中,{}
为模板中的槽,槽内部定义每个槽的格式化样式,形如:
id:[fill][align][sign][width][,][.precision][type]
fill
宽度不足时填充的字符
align
对齐方式. <
左对齐;>
右对齐;=
内容右对齐,符号放置在填充字符(如”0”)的左侧;^
居中
sign
+
正数加+
,负数加-
空格,正数加空格,负数加-
,
为数字添加千位分隔符
width
输出的最小宽度(不足以 fill 补齐)
.precision
小数点后保留精度
type
格式化类型
s 字符串(不指定type时默认)
d 十进制整数
o 八进制整数
x, X 小/大写十六进制整数
f 十进制浮点数
e, E 科学计数法表示的数,e小写或大写
g, G
智能选择使用浮点数还是科学计数法(g对应e,G对应E),默认显示6位有效数字
% 百分比,默认显示小数点后6位
b 二进制整数
c 10进制整数转换为 Unicode 字符
这种方法的缺点在于,输出宽度、精度等参数必须是确定的常数,而不能是变量。因此当这些数值必须取变量时,仍要使用.rjust()
、.ljust()
和.center()
方法。
示例:
str1 = " {0:=^20} " .format ("Python" )
str1 # 用 '=' 填充,居中对齐,槽宽度为20
#> '=======Python======='
str2 = " {:=^20} " .format ("Python" )
str2 # 参数索引0可以省略
#> '=======Python======='
str3 = " {:*>20} " .format ("BIT" )
str3 # 用 '*' 填充,右对齐,槽宽度为20
#> '*****************BIT'
str4 = " {:10} " .format ("BIT" )
str4 # 默认左对齐,空格填充
#> 'BIT '
str5 = "{:,.2f}" .format (12345.6789 )
str5 # 使用千位分隔符,保留两位小数的浮点型
#> '12,345.68'
str6 = " {0:b} , {0:c} , {0:d} , {0:o} , {0:x} , {0:X} " .format (426 )
str6 # 一个参数填到所有槽中,分别转换为:二进制,Unicode 编码,十进制,八进制,十六进制,大写的十六进制
#> '110101010, ƪ, 426, 652, 1aa, 1AA'
str7 = " {0:.2e} , {0:.2E} , {0:.2f} , {0:.2%} " .format (3.14 )
str7 # 分别转换为:科学记数法,大写科学记数法,浮点数,百分数
#> '3.14e+00, 3.14E+00, 3.14, 314.00%'
str8 = "I am {:s} , age {:d} , money {:.2f} " .format ("seven" , 18 , 88888.1 )
str8 # 槽与填充参数的顺序恰好对应时,也可以省略参数索引
#> 'I am seven, age 18, money 88888.10'
formattable 包
percent()
comma()
currency()
accounting(),会计格式,如负数用括号表示
scientific()
library (formattable)
df <-
data.frame (
id = 1 : 10 ,
name = c (
"Bob" ,
"Ashley" ,
"James" ,
"David" ,
"Jenny" ,
"Hans" ,
"Leo" ,
"John" ,
"Emily" ,
"Lee"
),
age = c (28 , 27 , 30 , 28 , 29 , 29 , 27 , 27 , 31 , 30 ),
grade = c ("C" , "A" , "A" , "C" , "B" , "B" , "B" , "A" , "C" , "C" ),
test1_score = c (8.9 , 9.5 , 9.6 , 8.9 , 9.1 , 9.3 , 9.3 , 9.9 , 8.5 , 8.6 ),
test2_score = c (9.1 , 9.1 , 9.2 , 9.1 , 8.9 , 8.5 , 9.2 , 9.3 , 9.1 , 8.8 ),
final_score = c (9 , 9.3 , 9.4 , 9 , 9 , 8.9 , 9.25 , 9.6 , 8.8 , 8.7 ),
registered = c (TRUE , FALSE , TRUE , FALSE , TRUE , TRUE , TRUE , FALSE , FALSE , FALSE ),
stringsAsFactors = FALSE
)
df
#> id name age grade test1_score test2_score final_score registered
#> 1 1 Bob 28 C 8.9 9.1 9.00 TRUE
#> 2 2 Ashley 27 A 9.5 9.1 9.30 FALSE
#> 3 3 James 30 A 9.6 9.2 9.40 TRUE
#> 4 4 David 28 C 8.9 9.1 9.00 FALSE
#> 5 5 Jenny 29 B 9.1 8.9 9.00 TRUE
#> 6 6 Hans 29 B 9.3 8.5 8.90 TRUE
#> 7 7 Leo 27 B 9.3 9.2 9.25 TRUE
#> 8 8 John 27 A 9.9 9.3 9.60 FALSE
#> 9 9 Emily 31 C 8.5 9.1 8.80 FALSE
#> 10 10 Lee 30 C 8.6 8.8 8.70 FALSE
formattable (
df,
list (
age = color_tile ("white" , "orange" ),
grade = formatter ("span" , style = x ~ ifelse (
x == "A" , style (color = "green" , font.weight = "bold" ), NA
)),
area (col = c (test1_score, test2_score)) ~ normalize_bar ("pink" , 0.2 ),
final_score = formatter (
"span" ,
style = x ~ style (color = ifelse (rank (- x) <= 3 , "green" , "gray" )),
x ~ sprintf ("%.2f (rank: %02d)" , x, rank (- x))
),
registered = formatter (
"span" ,
style = x ~ style (color = ifelse (x, "green" , "red" )),
x ~ icontext (ifelse (x, "ok" , "remove" ), ifelse (x, "Yes" , "No" ))
)
)
)
id
name
age
grade
test1_score
test2_score
final_score
registered
1
Bob
28
C
8.9
9.1
9.00 (rank: 06)
Yes
2
Ashley
27
A
9.5
9.1
9.30 (rank: 03)
No
3
James
30
A
9.6
9.2
9.40 (rank: 02)
Yes
4
David
28
C
8.9
9.1
9.00 (rank: 06)
No
5
Jenny
29
B
9.1
8.9
9.00 (rank: 06)
Yes
6
Hans
29
B
9.3
8.5
8.90 (rank: 08)
Yes
7
Leo
27
B
9.3
9.2
9.25 (rank: 04)
Yes
8
John
27
A
9.9
9.3
9.60 (rank: 01)
No
9
Emily
31
C
8.5
9.1
8.80 (rank: 09)
No
10
Lee
30
C
8.6
8.8
8.70 (rank: 10)
No
文字格式自定义
、文字背景自定义
、文本自定义
三种自定义可视化类型:
color_tile函数用于输出按照数值量级进行颜色背景填充的列。
formatter函数提供字体显示格式的自定义,grade列自定义了值为A的记录显示绿色,并将字体加粗,否则忽略。
test1_score,
test2_score两列通过area函数在对应字体背景位置使用条形图来代表指标量级大小,颜色填充粉色。
final_score列对指标按照top3显示绿色,其余显示灰色,同时将内容显示格式自定义为浮点型+(rank:名次)进行显示。
registered列则在对填充颜色按照对应布尔值进行显示(TRUE显示绿色、FALSE显示红色)之外,在左侧添加了对用的icon文本(TRUE显示绿色对号,FALSE显示红色叉号)。
stringr::
stringr
cheatsheet.pdf
str_length()
字符串长度
同 base::nchar()
使用 RStudio,那么通用前缀 str_ 会特别有用,因为输入 str_
后会触发自动完成功能,你可以看到所有的字符串函数:
str_length (c ("a" , "R for data science" ))
#> [1] 1 18
str_length (c ("a" , "R for data science" )) %>% sum ()
#> [1] 19
str_c()
字符串组合
str_c(..., sep = "", collapse = NULL)
合并若干个字符串或字符向量的对应元素,默认无间隔符
# 向量化操作,自动循环短向量
str_c ("prefix-" , c ("a" , "b" , "c" ), "-suffix" )
#> [1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"
# 合并一个字符向量的各元素为一个长字符串,使用参数collapse,将向量“塌缩”为一个字符串
str_c (c ("x" , "y" , "z" ), collapse = "+" )
#> [1] "x+y+z"
str_replace_na(string, replacement = "NA")
,默认将 na
转化为字符串”NA”
x <- c ("abc" , NA )
str_c ("|-" , x, "-|" )
#> [1] "|-abc-|" NA
str_c ("|-" , str_replace_na (x), "-|" )
#> [1] "|-abc-|" "|-NA-|"
str_trim()
去掉字符串首尾的空格和制表符(\t)
str_trim(string, side = c(“both”, “left”, “right”))
side: 过滤方式,both两边都过滤,left左边过滤,right右边过滤
str_pad()
补充字符串的长度
str_pad(string, width, side = c(“left”, “right”, “both”), pad = ”
“)
width: 字符串填充后的长度
side: 填充方向,both两边都填充,left左边填充,right右边填充
pad: 用于填充的字符
str_dup()
复制字符串
str_dup(string, times)
times: 复制次数
val <- c ("abca4" , 123 , "cba2" )
# 复制2次
str_dup (val, 2 )
#> [1] "abca4abca4" "123123" "cba2cba2"
# 按位置复制
str_dup (val, 1 : 3 )
#> [1] "abca4" "123123" "cba2cba2cba2"
str_wrap()
控制长字符串输出排版
str_wrap(string, width = 80, indent = 0, exdent = 0)
width: 设置一行所占的宽度。
indent: 段落首行的缩进值
exdent: 段落非首行的缩进值
# 这段代码可以在 console 中运行,但不能在 Rmarkdown 中直接运行
str <- "R语言作为统计学一门语言,一直在小众领域闪耀着光芒。直到大数据的爆发,R语言变成了一门炙手可热的数据分析的利器。随着越来越多的工程背景的人的加入,R语言的社区在迅速扩大成长。现在已不仅仅是统计领域,教育,银行,电商,互联网….都在使用R语言。"
# 设置宽度为40个字符
# cat(str_wrap(str, width = 40), "\n")
# 设置宽度为60字符,首行缩进4字符
# cat(str_wrap(txt, width = 60, indent = 4), "\n")
# 设置宽度为10字符,非首行缩进4字符
# cat(str_wrap(txt, width = 10, exdent = 4), "\n")
str_split()
拆分字符串
str_split(string, pattern, n = Inf) str_split_fixed(string, pattern,
n)
pattern: 匹配的字符。
n: 分割个数
val <- "abc,123,234,iuuu"
# 以,进行分割
s1 <- str_split (val, "," )
s1
#> [[1]]
#> [1] "abc" "123" "234" "iuuu"
# 以,进行分割,且只保留2块
s2 <- str_split (val, "," , 2 )
s2
#> [[1]]
#> [1] "abc" "123,234,iuuu"
# 查看str_split()函数操作的结果类型list
class (s1)
#> [1] "list"
# 用str_split_fixed()函数分割,结果类型是matrix
s3 <- str_split_fixed (val, "," , 2 )
s3
#> [,1] [,2]
#> [1,] "abc" "123,234,iuuu"
#> [1] "matrix" "array"
fruits <- c (
"apples and oranges and pears and bananas" ,
"pineapples and mangos and guavas"
)
str_split (fruits, " and " )
#> [[1]]
#> [1] "apples" "oranges" "pears" "bananas"
#>
#> [[2]]
#> [1] "pineapples" "mangos" "guavas"
gpl <- readLines (file.path (R.home (), "COPYING" ))
words <- unlist (strsplit (gpl, " \\ W" )) # \\W 意为非单词字符
words <- words[words != "" ] # 去掉空字符
tail (sort (table (tolower (words))), 10 ) # 频数最大的10个单词
#>
#> this is a program and you or of to the
#> 49 53 57 71 72 76 77 104 108 194
str_sub()
字符串取子集
str_sub(string, start = 1L, end = -1L)
str_sub(string, start = 1L, end = -1L) <- value
两个参数分别为始末位置,符号代表从后往前数第多少个字符
txt <- "I am Conan."
# 分2段截取字符串
str_sub (txt, c (1 , 4 ), c (6 , 8 ))
#> [1] "I am C" "m Con"
# 通过负坐标截取字符串
str_sub (txt, - 3 )
#> [1] "an."
#> [1] "I am Cona"
# 赋值
x <- c ("Apple" , "Banana" , "Pear" )
# str_sub()对x向量化操作;str_to_lower全部转换为小写字母
str_sub (x, 1 , 1 ) <- str_to_lower (str_sub (x, 1 , 1 ))
x
#> [1] "apple" "banana" "pear"
str_to_*()
大小写函数族
str_to_upper(string, locale = "")
转换为全大写字母
str_to_lower(string, locale = "")
转换为全小写字母
str_to_title(string, locale = "")
单词首字母大写
str_to_sentence()
全句首字母大写
locale:按哪种语言习惯排序
dog <- "Look the quick brown dog!"
str_to_upper (dog)
#> [1] "LOOK THE QUICK BROWN DOG!"
#> [1] "look the quick brown dog!"
#> [1] "Look The Quick Brown Dog!"
str_to_sentence ("look the quick brown dog!" )
#> [1] "Look the quick brown dog!"
str_count()
字符串计数
str_count(string, pattern = ““)
# 注意正则表达式的特殊字符
str_count (c ("a." , "." , ".a." , NA ), "." )
#> [1] 2 1 3 NA
# 用fixed匹配字符
str_count (c ("a." , "." , ".a." , NA ), fixed ("." ))
#> [1] 1 1 2 NA
# 用\\匹配字符
str_count (c ("a." , "." , ".a." , NA ), " \\ ." )
#> [1] 1 1 2 NA
str_sort()
字符串排序
str_sort(x, decreasing = FALSE, na_last = TRUE, locale = ““, …)
str_order(x, decreasing = FALSE, na_last = TRUE, locale =”“, …)
x: 字符串,字符串向量。 decreasing: 排序方向。 na_last:
NA值的存放位置,一共3个值,TRUE放到最后,FALSE放到最前,NA过滤处理
locale: 按哪种语言习惯排序
# 按ASCII字母排序
str_sort (c ("a" , 1 , 2 , "11" ), locale = "en" )
#> [1] "1" "11" "2" "a"
# 倒序排序
str_sort (letters, decreasing = TRUE )
#> [1] "z" "y" "x" "w" "v" "u" "t" "s" "r" "q" "p" "o" "n" "m" "l" "k" "j" "i" "h"
#> [20] "g" "f" "e" "d" "c" "b" "a"
# 按拼音排序
str_sort (c ("你" , "好" , "粉" , "丝" , "日" , "志" ), locale = "zh" )
#> [1] "粉" "好" "你" "日" "丝" "志"
对NA的排序处理:
# 把NA放最后面
str_sort (c (NA , "1" , NA ), na_last = TRUE )
#> [1] "1" NA NA
# 把NA放最前面
str_sort (c (NA , "1" , NA ), na_last = FALSE )
#> [1] NA NA "1"
# 去掉NA值
str_sort (c (NA , "1" , NA ), na_last = NA )
#> [1] "1"
str_detect()
检测是否匹配,返回T/F
val <- c ("abca4" , 123 , "cba2" )
# 检查字符串向量,是否包括a
str_detect (val, "a" )
#> [1] TRUE FALSE TRUE
# 检查字符串向量,是否以a为开头
str_detect (val, "^a" )
#> [1] TRUE FALSE FALSE
# 检查字符串向量,是否以a为结尾
str_detect (val, "a$" )
#> [1] FALSE FALSE FALSE
str_subset()
返回成功匹配的字符串
val <- c ("abc" , 123 , "cba" )
# 全文匹配
str_subset (val, "a" )
#> [1] "abc" "cba"
# 开头匹配
str_subset (val, "^a" )
#> [1] "abc"
# 结尾匹配
str_subset (val, "a$" )
#> [1] "cba"
word()
从文本中提取单词(以单词为单位看待字符串)
word(string, start = 1L, end = start, sep = fixed(” “))
start: 从第几个词开始。 end: 到第几个词结束。 sep:
分词间隔字符,默认为空格。
val <- c ("I am Conan." , "http://fens.me, ok" )
# 默认以空格分割,取第一个位置的字符串
word (val, 1 )
#> [1] "I" "http://fens.me,"
#> [1] "Conan." "ok"
#> [1] "am Conan." "ok"
# 以,分割,取第一个位置的字符串
val <- "111,222,333,444"
word (val, 1 , sep = fixed ("," ))
#> [1] "111"
word (val, 3 , sep = fixed ("," ))
#> [1] "333"
str_replace()
字符串替换
str_replace(string, pattern, replacement)
str_replace_all(string, pattern, replacement)
str_replace_na(string, replacement = “NA”), 将NA(默认)替换为’NA’
参数 replacement
可以是一个函数,使替换的规则更加复杂
val <- c ("abc" , 123 , "cba" )
# 把目标字符串第一个出现的a或b,替换为-
str_replace (val, "[ab]" , "-" )
#> [1] "-bc" "123" "c-a"
# 把目标字符串所有出现的a或b,替换为-
str_replace_all (val, "[ab]" , "-" )
#> [1] "--c" "123" "c--"
# 把目标字符串所有出现的a,替换为被转义的字符
str_replace_all (val, "[a]" , " \\ " )
#> [1] "bc" "123" "cb"
pigIt <- function (string) {
str_replace_all (string, " \\ w+" , function (word) {
str_c (str_sub (word, 2 ), str_sub (word, 1 , 1 ), "ay" )
})
}
library (testthat)
#>
#> Attaching package: 'testthat'
#> The following objects are masked from 'package:magrittr':
#>
#> equals, is_less_than, not
#> The following object is masked from 'package:dplyr':
#>
#> matches
#> The following object is masked from 'package:purrr':
#>
#> is_null
#> The following objects are masked from 'package:readr':
#>
#> edition_get, local_edition
#> The following object is masked from 'package:tidyr':
#>
#> matches
test_that ("Sample Tests" , {
expect_equal (pigIt ("Pig latin is cool" ), "igPay atinlay siay oolcay" )
expect_equal (pigIt ("Hello world !" ), "elloHay orldway !" )
})
#> Test passed 😀
str_locate()
找到模式在字符串中的始末位置
str_locate(string, pattern) str_locate_all(string, pattern)
val <- c ("abca" , 123 , "cba" )
# 匹配a在字符串中的位置
str_locate (val, "a" )
#> start end
#> [1,] 1 1
#> [2,] NA NA
#> [3,] 3 3
# 用向量匹配
str_locate (val, c ("a" , 12 , "b" ))
#> start end
#> [1,] 1 1
#> [2,] 1 2
#> [3,] 2 2
# 以字符串matrix格式返回
str_locate_all (val, "a" )
#> [[1]]
#> start end
#> [1,] 1 1
#> [2,] 4 4
#>
#> [[2]]
#> start end
#>
#> [[3]]
#> start end
#> [1,] 3 3
# 匹配a或b字符,以字符串matrix格式返回
str_locate_all (val, "[ab]" )
#> [[1]]
#> start end
#> [1,] 1 1
#> [2,] 2 2
#> [3,] 4 4
#>
#> [[2]]
#> start end
#>
#> [[3]]
#> start end
#> [1,] 2 2
#> [2,] 3 3
str_conv
字符编码转换
str_conv(string, encoding)
# 把中文字符字节化
x <- charToRaw ("你好" )
x
#> [1] e4 bd a0 e5 a5 bd
# 默认win系统字符集为GBK,GB2312为GBK字集,转码正常
str_conv (x, "GBK" )
#> [1] "浣犲ソ"
#> Warning in stri_conv(string, encoding, "UTF-8"): input data \xffffffa0 in the
#> current source encoding could not be converted to Unicode
#> Warning in stri_conv(string, encoding, "UTF-8"): input data \xffffffbd in the
#> current source encoding could not be converted to Unicode
#> [1] "浣\032濂\032"
# 转UTF-8失败
str_conv (x, "UTF-8" )
#> [1] "你好"
# 把unicode转UTF-8
str_conv ("\u5317\u4eac" , "UTF-8" )
#> [1] "北京"
str_glue()
模板字符串
name <- "Fred"
age <- 50
anniversary <- as.Date ("1991-10-12" )
str_glue (
"My name is {name}, " ,
"my age next year is {age + 1}, " ,
"and my anniversary is {format(anniversary, '%A, %B %d, %Y')}."
)
#> My name is Fred, my age next year is 51, and my anniversary is 星期六, 十月 12, 1991.
str_glue ("My name is {name}, not {{name}}." )
#> My name is Fred, not {name}.
LS0tDQp0aXRsZTogIlN0cmluZyINCnN1YnRpdGxlOiAnJw0KYXV0aG9yOiAiSHVtb29uIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY3NzOiBbIi4uL2Nzcy9zdHlsZS5jc3MiXQ0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICB0aGVtZTogdW5pdGVkDQogICAgaGlnaGxpZ2h0OiBoYWRkb2NrDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiB5ZXMNCiAgICAgIHNtb290aF9zY3JvbGw6IHllcw0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQpzb3VyY2UoIi4uL1JtYXJrZG93bi10ZW1wbGF0ZS9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywgDQogIGV2YWwgPSBjb25maWckZXZhbCwgDQogIHRpZHkgPSBjb25maWckdGlkeSwgDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwgDQogIGNvbGxhcHNlID0gY29uZmlnJGNvbGxhcHNlLCANCiAgY2FjaGUgPSBjb25maWckY2FjaGUsDQogIGNhY2hlLmNvbW1lbnRzID0gY29uZmlnJGNhY2hlLmNvbW1lbnRzLA0KICBhdXRvZGVwID0gY29uZmlnJGF1dG9kZXANCikNCg0KIyMgdXNlIG5lY2Vzc2FyeSBwYWNrYWdlcyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeShodG1sd2lkZ2V0cykNCmBgYA0KDQoqKuWtl+espuS4suWHveaVsOS4gOiIrOmDveaYr+WQkemHj+WMlueahO+8jOWvuei+k+WFpeeahOWtl+espuS4suWQkemHj+eahOavj+S4quWFg+e0oOi/m+ihjOaTjeS9nCoq44CCDQoNCiMjIOWtl+espuS4suWfuuehgA0KDQojIyMgcmVhZExpbmVzKCkNCg0KMS4gIOWcqFLkuK3or7vlhaXkuIDkuKrmlofmnKzmlofku7blj6/ku6XnlKhgcmVhZExpbmVzKClg5Ye95pWw77yM5a6D6L+U5Zue5LiA5Liq5a2X56ym5Z6L5ZCR6YeP77yM5paH5Lu25Lit5q+P5LiA6KGM6YO95piv5ZCR6YeP5Lit5a+55bqU55qE5LiA5Liq5YWD57Sg44CC6L+Z5Liq5paH5Lu25Y+v5Lul5piv5pys5Zyw5paH5Lu277yM5Lmf5Y+v5Lul5piv5LiA5Liq572R5Z2A44CCDQoNCmBgYHtyfQ0KaW5kZXggPC0gcmVhZExpbmVzKCJodHRwczovL3d3dy5iYWlkdS5jb20iKSAjIOaIkeeahOS4u+mhtQ0KaGVhZChpbmRleCkgIyBIVE1M5Luj56CBDQpgYGANCg0KMi4gIGByZWFkbGluZSgpYOaUr+aMgeS7jumUruebmOebtOaOpei+k+WFpeS4gOihjOaWh+acrO+8jOaYvueEtu+8jFLlv4XpobvlnKjkuqTkupLlvI/mqKHlvI/kuIvov5DooYzvvIzlkKbliJnkurrmsqHms5XovpPlhaXku7vkvZXkuJzopb/jgILlroPmnInkuIDkuKrluLjnlKjnmoTlnLDmlrnvvIzlsLHmmK/lnKjku6PnoIHov5DooYzov4fnqIvkuK3opoHmsYLnlKjmiLfovpPlhaXkuIDkupvlm57nrZTvvIzkvovlpoLvvJoNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQp4IDwtIHJlYWRsaW5lKCJBbnN3ZXIgeWVzIG9yIG5vOiAiKQ0KYGBgDQoNCuW9k1Lov5DooYzliLDov5nkuIDooYzml7bkvJrlgZzkuIvmnaXnrYnlvoXnlKjmiLfovpPlhaXvvIjlm57ovabooajnpLrovpPlhaXnu5PmnZ/vvInvvIznhLblkI7moLnmja7ovpPlhaXnmoTnu5PmnpzvvIzku6PnoIHlj6/ku6XmjqXnnYDmiafooYzjgIINCg0KMy4gIOWcqFdpbmRvd3Pns7vnu5/kuK3vvIxgcmVhZExpbmVzKClg6K+7dXRmLTjnvJbnoIHnmoR0eHTmlofku7bkvJrlr7zoh7TkubHnoIHvvIzlj6rog73pobrliKnor7vlj5ZBTlNJ57yW56CB55qEdHh05paH5Lu244CCDQoNCiMjIyB3cml0ZUxpbmVzKCkNCg0KMS4gIOazqOaEj++8jOWtl+espuS4sueahOaJk+WNsOW9ouW8j+S4juWFtuacrOi6q+eahOWGheWuueS4jeaYr+ebuOWQjOeahO+8jOWboOS4uuaJk+WNsOW9ouW8j+S4reS8muaYvuekuuWHuui9rOS5ieWtl+espuOAgioq5aaC5p6c5oOz6KaB5p+l55yL5a2X56ym5Liy55qE55yf5q2j5YaF5a6577yM5Y+v5Lul5L2/55SoYHdyaXRlTGluZXMoKWAqKg0KDQo+IHdyaXRlTGluZXModGV4dCwgY29uID0gc3Rkb3V0KCksIHNlcCA9ICJcbiIsIHVzZUJ5dGVzID0gRkFMU0UpDQoNCjIuICBXaW5kb3dz57O757uf5LitYHdyaXRlTGluZXMoKWDlhpnlhaV0eHTmlofku7bkvJrlr7zoh7TkubHnoIHvvIzmm7/ku6PmlrnmoYjmmK/nlKhgd3JpdGVCaW4oY2hhclRvUmF3KHN0cmluZyksIGZpbGUpYOWGmeWFpeaWh+S7tg0KDQojIyMg5a2X56ym5Liy5YyF5ZCr5byV5paH5pe25byV5Y+355qE5L2/55SoDQoNCuiLpeS4gOS4quWtl+espuS4suWMheWQq+W8leaWh++8jOS4uuS6huespuWQiOihjOaWh+S5oOaDryjlj4zlvJXlj7cp77yM5bm25L2/5Luj56CB5byV5Y+35pyJ5bGC5qyh55qE5Yy65Yir77yM5aSW5bGC5bqU55So5Y2V5byV5Y+344CCDQoNCmBgYHtyIHF1b3RlLCByZXN1bHRzPSdob2xkJ30NCiMgc3RyaW5nMSA8LSAnV2hlbiBwdXR0aW5nIGEgJ3F1b3RlJyBpbnNpZGUgYSBzdHJpbmcsIHVzZSBzaW5nbGUgcXVvdGF0aW9uIG1hcmtzLicNCiMgc3RyaW5nMSA8LSAiV2hlbiBwdXR0aW5nIGEgInF1b3RlIiBpbnNpZGUgYSBzdHJpbmcsIHVzZSBkb3VibGUgcXVvdGF0aW9uIG1hcmtzLiINCnN0cmluZzMgPC0gIldoZW4gcHV0dGluZyBhICdxdW90ZScgaW5zaWRlIGEgc3RyaW5nLCB1c2Ugc2luZ2xlIHF1b3RhdGlvbiBtYXJrcyBpbnNpZGUgYW5kIGRvdWJsZSBxdW90YXRpb24gbWFya3Mgb3V0c2lkZS4iDQpzdHJpbmc0IDwtICdXaGVuIHB1dHRpbmcgYSAicXVvdGUiIGluc2lkZSBhIHN0cmluZywgdXNlIGRvdWJsZSBxdW90YXRpb24gbWFya3MgaW5zaWRlIGFuZCBzaW5nbGUgcXVvdGF0aW9uIG1hcmtzIG91dHNpZGUuJw0KDQojIOWtl+espuS4sueahOaJk+WNsOW9ouW8jw0Kc3RyaW5nMw0Kc3RyaW5nNA0KYGBgDQoNCmBgYHtyLCByZXN1bHRzPSdob2xkJ30NCiMg5a2X56ym5Liy57yW6K+R5ZCO5a+85Ye655qE5b2i5byPDQp3cml0ZUxpbmVzKHN0cmluZzMpDQp3cml0ZUxpbmVzKHN0cmluZzQpDQpgYGANCg0Kc3RyaW5nMeWSjHN0cmluZzLmnInor63ms5XplJnor6/vvIzliY3lkI7kuKTkuKrlrZfnrKbkuLLkuK3pl7TlpLnkuobkuIDkuKrnvJbor5HlmajkuI3orqTor4bnmoRxdW90ZeOAgg0KDQpzdHJpbmcz5ZKMc3RyaW5nNOWcqOivreazleS4iuayoemXrumimO+8jOS9hue8luivkeaXtuaViOaenOS4jeWQjOOAgnN0cmluZzTmiY3mmK/miJHku6zmg7PopoHnmoTjgIINCg0KIyMjIOi9rOS5ieespuWPtyJcXCINCg0K5Y+N5pac5p2gIlxcIuWPr+S7peWwhlLor63oqIDlj4pSIE1hcmtkb3du55qE6K+t5rOV6YOo5Lu26L2s5LmJ5Li65a2X56ym44CCXA0K5aaC5p6c5oOz6KaB5Zyo5a2X56ym5Liy5Lit5YyF5ZCr5LiA5Liq5Y2V5byV5Y+35oiW5Y+M5byV5Y+377yM5Y+v5Lul5L2/55SoIlxcJyLlkowiXFwiIuWvueWFtui/m+ihjOi9rOS5ieOAgg0KDQpgYGB7ciwgcmVzdWx0cz0naG9sZCd9DQpzdHJpbmc1IDwtICdJIGRvblwndCBrbm93IHdoeSB0aGV5IGNhbGxlZCB1cyAiQ2hpbmVzZSIuJw0Kc3RyaW5nNiA8LSAiV2hlbiBwdXR0aW5nIGEgXCJxdW90ZVwiIGluc2lkZSBhIHN0cmluZywgeW91IGNhbiB1c2UgYmFja3NsYXNoIGFzIHdlbGwuIg0Kd3JpdGVMaW5lcyhzdHJpbmc1KQ0Kd3JpdGVMaW5lcyhzdHJpbmc2KQ0KYGBgDQoNCuWmguaenOaDs+imgeWcqOWtl+espuS4suS4reWMheWQq+S4gOS4quWPjeaWnOadoO+8jOWwsemcgOimgeS9v+eUqOS4pOS4quWPjeaWnOadoO+8miJcXFxcIg0KDQpgYGB7ciwgcmVzdWx0cz0naG9sZCd9DQpzdHJpbmc3IDwtICJJIHRoaW5rIHRoZSBiYWNrc2xhc2ggXCJcXFwiIGlzIGFtYXppbmchIg0KcHJpbnQoc3RyaW5nNykgIyDmiJYgc3RyaW5nNw0Kd3JpdGVMaW5lcyhzdHJpbmc3KQ0KYGBgDQoNCiMjIyDnibnmrorlrZfnrKYNCg0K5YW25LuW5Yeg56eN54m55q6K5a2X56ym44CC5pyA5bi455So55qE5piv5o2i6KGM56ymIlxcbiLjgIHliLbooajnrKYiXFx0IuWSjOmAgOagvCJcXGIi77yM5L2g5Y+v5Lul5L2/55SoID8nIicg5oiWID8iJyIg6LCD5Ye65biu5Yqp5paH5Lu25p2l5p+l55yL5a6M5pW055qE54m55q6K5a2X56ym5YiX6KGo44CCDQoNCmBgYHtyLCByZXN1bHRzPSdob2xkJ30NCnN0cmluZzggPC0gJ1RoZSBzcGVjaWFsIGNoYXJhY3RlcnMgIlxuIiwgIlx0IiBhbmQgIlxiIiBhcmUgdmVyeSBpbnRlcmVzaW5nLicNCnN0cmluZzkgPC0gIlRoZSBzcGVjaWFsIGNoYXJhY3RlcnMgXCJcXG5cIiwgXCJcXHRcIiBhbmQgXCJcXGJcIiBhcmUgdmVyeSBpbnRlcmVzaW5nLiINCndyaXRlTGluZXMoc3RyaW5nOCkNCndyaXRlTGluZXMoc3RyaW5nOSkNCmBgYA0KDQrnlJ/miJDnmoRodG1s5paH5Lu25LitIlxcYiLlj5jmiJDkuobkuIDkuKrlpYfmgKrnmoTmlrnmoYbvvIzmjqfliLblj7DmmL7npLrnmoTliJnmmK/mraPnoa7nmoTjgILlj6/og71SIE1hcmtkb3du5LiOUuivreiogOS4rSJcXGIi55qE5ZCr5LmJ5LiN5ZCM44CCDQoNCiMjIOWtl+espuS4suagvOW8j+WMlg0KDQojIyMgYmFzZTo6c3ByaW50ZigpDQoNCmBzcHJpbnRmKOWMheWQq+WNoOS9jeespueahOWtl+espuS4su+8jOassuWhq+WFheeahOWtl+espuS4silgDQoNCnwgKirljaDkvY3nrKYqKiAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgXCUgICAgICAgICAgICAgIHwg5aaC5p6c6KaB5Zyo5Y2g5L2N56ym5YaF6YOo5L2/55SoJe+8jOmcgOimgeWGmeaIkCUlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICoq6L2s5o2i56ymKiogICAgICB8ICoq57G75Z6LKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZCAgICAgICAgICAgICAgIHwg5Y2B6L+b5Yi25pW05pWwICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBvICAgICAgICAgICAgICAgfCDlhavov5vliLbmlbTmlbAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHgsIFggICAgICAgICAgICB8IOWwjy/lpKflhpnljYHlha3ov5vliLbmlbTmlbAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGYgICAgICAgICAgICAgICB8IOWNgei/m+WItua1rueCueaVsCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGUsIEUgICAgICAgICAgICB8IOenkeWtpuiuoeaVsOazleihqOekuueahOaVsO+8jGXlsI/lhpnmiJblpKflhpkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGcsIEcgICAgICAgICAgICB8IOaZuuiDvemAieaLqeS9v+eUqOa1rueCueaVsOi/mOaYr+enkeWtpuiuoeaVsOazle+8iGflr7nlupRl77yMR+WvueW6lEXvvInvvIzpu5jorqTmmL7npLo25L2N5pyJ5pWI5pWw5a2XICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHMgICAgICAgICAgICAgICB8IOWtl+espuS4siAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICoq5rWu54K55pWw5qC85byPKiogIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgd2lkdGgucHJlY2lzaW9uIHwg6L6T5Ye655qE5pyA5bCP5a695bqm77yI5LiN6Laz5Lul56m65qC86KGl6b2Q77yJLui+k+WHuueyvuW6pu+8iOWvuWblkoxlL0XmhI/lkbPnnYDlsI/mlbDngrnlkI7lh6DkvY3vvIzlr7lnL0fmhI/lkbPnnYDmnInmlYjmlbDlrZfkvY3mlbDvvIkgfA0KfCBcLSAgICAgICAgICAgICAgfCDmjIflrprlt6blr7npvZDvvIjpu5jorqTlj7Plr7npvZDvvInvvIzljbPlrr3luqbkuI3otrPml7bku47lj7PovrnooaXlhYXnqbrmoLwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IFwrICAgICAgICAgICAgICB8IOawuOi/nOW4puespuWPt++8iOm7mOiupOWPquaciei0n+aVsOW4puS4gOS4qui0n+WPt++8jOato+aVsOS4jeW4puespuWPt++8iSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCDnqbrmoLwgICAgICAgICAgICB8IOmmluWtl+espuW5tumdnui0n+WPt+aXtu+8jOepuuS4gOagvCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IDAgICAgICAgICAgICAgICB8IOWuveW6puS4jei2s+aXtuihpTDvvIzogIzpnZ7pu5jorqTnmoTnqbrmoLwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KYGBge3J9DQpzcHJpbnRmKCJUaGUgcmF0aW8gaXMgJWQlJSIsIDEwKQ0KDQoNCnNwcmludGYoIiVmIiwgcGkpICMg5rWu54K55pWwDQpzcHJpbnRmKCIlLjNmIiwgcGkpICMg5L+d55WZ5bCP5pWw54K55ZCOM+S9jQ0Kc3ByaW50ZigiJS4wZiIsIHBpKSAjIOS/neeVmeWwj+aVsOeCueWQjjDkvY0NCnNwcmludGYoIiU1LjFmIiwgcGkpICMg5pyA5bCP5a695bqm5Li6Ne+8jOS/neeVmeWwj+aVsOeCueWQjjHkvY3vvIzkuI3otrPooaXnqbrmoLwNCnNwcmludGYoIiUwNS4xZiIsIHBpKSAjIOeUqDDooaXpvZANCnNwcmludGYoIiUrZiIsIHBpKSAjIOawuOi/nOW4puespuWPtw0Kc3ByaW50ZigiJSBmIiwgcGkpDQpzcHJpbnRmKCIlLTEwZiIsIHBpKSAjIOW3puWvuem9kO+8jOWuveW6puS4ujEwDQpzcHJpbnRmKCIlZSIsIHBpKQ0Kc3ByaW50ZigiJUUiLCBwaSkNCnNwcmludGYoIiVnIiwgcGkpDQpzcHJpbnRmKCIlZyIsIDFlNiAqIHBpKQ0Kc3ByaW50ZigiJS45ZyIsIDFlNiAqIHBpKQ0Kc3ByaW50ZigiJS4yZyIsIDIzLjc1KQ0KDQpzcHJpbnRmKA0KICAibWluIDEwLWNoYXIgc3RyaW5nICclMTBzJyIsDQogIGMoImEiLCAiQUJDIiwgImFuZCBhbiBldmVuIGxvbmdlciBvbmUiKQ0KKQ0KYGBgDQoNCiMjIyDlgJ/liqkgcHl0aG9uDQoNCuWKoOWFpSBweXRob24g5Luj56CB5Z2X77yM5L2/55SoIHB5dGhvbiDkuK3nmoTlrZfnrKbkuLLmoLzlvI/ljJbmlrnms5UgLmZvcm1hdCgpDQoNCmDmqKHmnb/lrZfnrKbkuLIuZm9ybWF0KOS7pemAl+WPt+WIhumalOeahOWhq+WFheWPguaVsOW6j+WIlylgDQoNCuWFtuS4re+8jGB7fWDkuLrmqKHmnb/kuK3nmoTmp73vvIzmp73lhoXpg6jlrprkuYnmr4/kuKrmp73nmoTmoLzlvI/ljJbmoLflvI/vvIzlvaLlpoLvvJoNCg0KYGlkOltmaWxsXVthbGlnbl1bc2lnbl1bd2lkdGhdWyxdWy5wcmVjaXNpb25dW3R5cGVdYA0KDQorLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCDnsbvliKsgICAgICAgfCDlkKvkuYkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorPT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCBmaWxsICAgICAgIHwg5a695bqm5LiN6Laz5pe25aGr5YWF55qE5a2X56ymICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBhbGlnbiAgICAgIHwg5a+56b2Q5pa55byPLiBgPGAg5bem5a+56b2Q77ybYD5gIOWPs+Wvuem9kO+8m2A9YCDlhoXlrrnlj7Plr7npvZDvvIznrKblj7fmlL7nva7lnKjloavlhYXlrZfnrKbvvIjlpoIiMCLvvInnmoTlt6bkvqfvvJtgXmAg5bGF5LitIHwNCistLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IHNpZ24gICAgICAgfCBgK2Ag5q2j5pWw5YqgYCtg77yM6LSf5pWw5YqgYC1gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgfCDnqbrmoLzvvIzmraPmlbDliqDnqbrmoLzvvIzotJ/mlbDliqBgLWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgLCAgICAgICAgICB8IOS4uuaVsOWtl+a3u+WKoOWNg+S9jeWIhumalOespiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgd2lkdGggICAgICB8IOi+k+WHuueahOacgOWwj+WuveW6pu+8iOS4jei2s+S7pSBmaWxsIOihpem9kO+8iSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IC5wcmVjaXNpb24gfCDlsI/mlbDngrnlkI7kv53nlZnnsr7luqYgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCB0eXBlICAgICAgIHwg5qC85byP5YyW57G75Z6LICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgICAgICAgICAgICB8IHMg5a2X56ym5Liy77yI5LiN5oyH5a6adHlwZeaXtum7mOiupO+8iSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICAgICAgICAgICAgfCBkIOWNgei/m+WItuaVtOaVsCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAgICAgICAgICAgIHwgbyDlhavov5vliLbmlbTmlbAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgICAgICAgICAgICB8IHgsIFgg5bCPL+Wkp+WGmeWNgeWFrei/m+WItuaVtOaVsCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAgICAgICAgICAgIHwgZiDljYHov5vliLbmta7ngrnmlbAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICAgICAgICAgICAgfCBlLCBFIOenkeWtpuiuoeaVsOazleihqOekuueahOaVsO+8jGXlsI/lhpnmiJblpKflhpkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgICAgICAgICAgICB8IGcsIEcg5pm66IO96YCJ5oup5L2/55So5rWu54K55pWw6L+Y5piv56eR5a2m6K6h5pWw5rOV77yIZ+WvueW6lGXvvIxH5a+55bqURe+8ie+8jOm7mOiupOaYvuekujbkvY3mnInmlYjmlbDlrZcgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAgICAgICAgICAgIHwgXCUg55m+5YiG5q+U77yM6buY6K6k5pi+56S65bCP5pWw54K55ZCONuS9jSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAgICAgICAgICAgIHwgYiDkuozov5vliLbmlbTmlbAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgICAgICAgICAgICB8IGMgMTDov5vliLbmlbTmlbDovazmjaLkuLogVW5pY29kZSDlrZfnrKYgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQrov5nnp43mlrnms5XnmoTnvLrngrnlnKjkuo7vvIzovpPlh7rlrr3luqbjgIHnsr7luqbnrYnlj4LmlbDlv4XpobvmmK/noa7lrprnmoTluLjmlbDvvIzogIzkuI3og73mmK/lj5jph4/jgILlm6DmraTlvZPov5nkupvmlbDlgLzlv4Xpobvlj5blj5jph4/ml7bvvIzku43opoHkvb/nlKhgLnJqdXN0KClg44CBYC5sanVzdCgpYOWSjGAuY2VudGVyKClg5pa55rOV44CCDQoNCuekuuS+i++8mg0KDQpgYGB7cHl0aG9ufQ0Kc3RyMSA9ICJ7MDo9XjIwfSIuZm9ybWF0KCJQeXRob24iKSANCnN0cjEgIyDnlKggJz0nIOWhq+WFhe+8jOWxheS4reWvuem9kO+8jOanveWuveW6puS4ujIwDQoNCnN0cjIgPSAiezo9XjIwfSIuZm9ybWF0KCJQeXRob24iKSANCnN0cjIgIyDlj4LmlbDntKLlvJUw5Y+v5Lul55yB55WlDQoNCnN0cjMgPSAiezoqPjIwfSIuZm9ybWF0KCJCSVQiKSANCnN0cjMgIyDnlKggJyonIOWhq+WFhe+8jOWPs+Wvuem9kO+8jOanveWuveW6puS4ujIwDQoNCnN0cjQgPSAiezoxMH0iLmZvcm1hdCgiQklUIikNCnN0cjQgIyDpu5jorqTlt6blr7npvZDvvIznqbrmoLzloavlhYUNCg0Kc3RyNSA9ICJ7OiwuMmZ9Ii5mb3JtYXQoMTIzNDUuNjc4OSkNCnN0cjUgIyDkvb/nlKjljYPkvY3liIbpmpTnrKbvvIzkv53nlZnkuKTkvY3lsI/mlbDnmoTmta7ngrnlnosNCg0Kc3RyNiA9ICJ7MDpifSwgezA6Y30sIHswOmR9LCB7MDpvfSwgezA6eH0sIHswOlh9Ii5mb3JtYXQoNDI2KQ0Kc3RyNiAjIOS4gOS4quWPguaVsOWhq+WIsOaJgOacieanveS4re+8jOWIhuWIq+i9rOaNouS4uu+8muS6jOi/m+WItu+8jFVuaWNvZGUg57yW56CB77yM5Y2B6L+b5Yi277yM5YWr6L+b5Yi277yM5Y2B5YWt6L+b5Yi277yM5aSn5YaZ55qE5Y2B5YWt6L+b5Yi2DQoNCnN0cjcgPSAiezA6LjJlfSwgezA6LjJFfSwgezA6LjJmfSwgezA6LjIlfSIuZm9ybWF0KDMuMTQpDQpzdHI3ICMg5YiG5Yir6L2s5o2i5Li677ya56eR5a2m6K6w5pWw5rOV77yM5aSn5YaZ56eR5a2m6K6w5pWw5rOV77yM5rWu54K55pWw77yM55m+5YiG5pWwDQoNCnN0cjggID0gIkkgYW0gezpzfSwgYWdlIHs6ZH0sIG1vbmV5IHs6LjJmfSIuZm9ybWF0KCJzZXZlbiIsIDE4LCA4ODg4OC4xKQ0Kc3RyOCAjIOanveS4juWhq+WFheWPguaVsOeahOmhuuW6j+aBsOWlveWvueW6lOaXtu+8jOS5n+WPr+S7peecgeeVpeWPguaVsOe0ouW8lQ0KYGBgDQoNCiMjIyBmb3JtYXR0YWJsZSDljIUNCg0KLSBwZXJjZW50KCkNCi0gY29tbWEoKQ0KLSBjdXJyZW5jeSgpDQotIGFjY291bnRpbmcoKe+8jOS8muiuoeagvOW8j++8jOWmgui0n+aVsOeUqOaLrOWPt+ihqOekug0KLSBzY2llbnRpZmljKCkNCg0KDQpgYGB7cn0NCmxpYnJhcnkoZm9ybWF0dGFibGUpDQoNCmRmIDwtDQogIGRhdGEuZnJhbWUoDQogICAgaWQgPSAxOjEwLA0KICAgIG5hbWUgPSBjKA0KICAgICAgIkJvYiIsDQogICAgICAiQXNobGV5IiwNCiAgICAgICJKYW1lcyIsDQogICAgICAiRGF2aWQiLA0KICAgICAgIkplbm55IiwNCiAgICAgICJIYW5zIiwNCiAgICAgICJMZW8iLA0KICAgICAgIkpvaG4iLA0KICAgICAgIkVtaWx5IiwNCiAgICAgICJMZWUiDQogICAgKSwNCiAgICBhZ2UgPSBjKDI4LCAyNywgMzAsIDI4LCAyOSwgMjksIDI3LCAyNywgMzEsIDMwKSwNCiAgICBncmFkZSA9IGMoIkMiLCAiQSIsICJBIiwgIkMiLCAiQiIsICJCIiwgIkIiLCAiQSIsICJDIiwgIkMiKSwNCiAgICB0ZXN0MV9zY29yZSA9IGMoOC45LCA5LjUsIDkuNiwgOC45LCA5LjEsIDkuMywgOS4zLCA5LjksIDguNSwgOC42KSwNCiAgICB0ZXN0Ml9zY29yZSA9IGMoOS4xLCA5LjEsIDkuMiwgOS4xLCA4LjksIDguNSwgOS4yLCA5LjMsIDkuMSwgOC44KSwNCiAgICBmaW5hbF9zY29yZSA9IGMoOSwgOS4zLCA5LjQsIDksIDksIDguOSwgOS4yNSwgOS42LCA4LjgsIDguNyksDQogICAgcmVnaXN0ZXJlZCA9IGMoVFJVRSwgRkFMU0UsIFRSVUUsIEZBTFNFLCBUUlVFLCBUUlVFLCBUUlVFLCBGQUxTRSwgRkFMU0UsIEZBTFNFKSwNCiAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgKQ0KZGYNCg0KZm9ybWF0dGFibGUoDQogIGRmLA0KICBsaXN0KA0KICAgIGFnZSA9IGNvbG9yX3RpbGUoIndoaXRlIiwgIm9yYW5nZSIpLA0KICAgIGdyYWRlID0gZm9ybWF0dGVyKCJzcGFuIiwgc3R5bGUgPSB4IH4gaWZlbHNlKA0KICAgICAgeCA9PSAiQSIsIHN0eWxlKGNvbG9yID0gImdyZWVuIiwgZm9udC53ZWlnaHQgPSAiYm9sZCIpLCBOQQ0KICAgICkpLA0KICAgIGFyZWEoY29sID0gYyh0ZXN0MV9zY29yZSwgdGVzdDJfc2NvcmUpKSB+IG5vcm1hbGl6ZV9iYXIoInBpbmsiLCAwLjIpLA0KICAgIGZpbmFsX3Njb3JlID0gZm9ybWF0dGVyKA0KICAgICAgInNwYW4iLA0KICAgICAgc3R5bGUgPSB4IH4gc3R5bGUoY29sb3IgPSBpZmVsc2UocmFuaygteCkgPD0gMywgImdyZWVuIiwgImdyYXkiKSksDQogICAgICB4IH4gc3ByaW50ZigiJS4yZiAocmFuazogJTAyZCkiLCB4LCByYW5rKC14KSkNCiAgICApLA0KICAgIHJlZ2lzdGVyZWQgPSBmb3JtYXR0ZXIoDQogICAgICAic3BhbiIsDQogICAgICBzdHlsZSA9IHggfiBzdHlsZShjb2xvciA9IGlmZWxzZSh4LCAiZ3JlZW4iLCAicmVkIikpLA0KICAgICAgeCB+IGljb250ZXh0KGlmZWxzZSh4LCAib2siLCAicmVtb3ZlIiksIGlmZWxzZSh4LCAiWWVzIiwgIk5vIikpDQogICAgKQ0KICApDQopDQpgYGANCmDmloflrZfmoLzlvI/oh6rlrprkuYlg44CBYOaWh+Wtl+iDjOaZr+iHquWumuS5iWDjgIFg5paH5pys6Ieq5a6a5LmJYOS4ieenjeiHquWumuS5ieWPr+inhuWMluexu+Wei++8mg0KDQotIGNvbG9yX3RpbGXlh73mlbDnlKjkuo7ovpPlh7rmjInnhafmlbDlgLzph4/nuqfov5vooYzpopzoibLog4zmma/loavlhYXnmoTliJfjgIINCi0gZm9ybWF0dGVy5Ye95pWw5o+Q5L6b5a2X5L2T5pi+56S65qC85byP55qE6Ieq5a6a5LmJ77yMZ3JhZGXliJfoh6rlrprkuYnkuoblgLzkuLpB55qE6K6w5b2V5pi+56S657u/6Imy77yM5bm25bCG5a2X5L2T5Yqg57KX77yM5ZCm5YiZ5b+955Wl44CCDQotIHRlc3QxX3Njb3JlLCB0ZXN0Ml9zY29yZeS4pOWIl+mAmui/h2FyZWHlh73mlbDlnKjlr7nlupTlrZfkvZPog4zmma/kvY3nva7kvb/nlKjmnaHlvaLlm77mnaXku6PooajmjIfmoIfph4/nuqflpKflsI/vvIzpopzoibLloavlhYXnsonoibLjgIINCi0gZmluYWxfc2NvcmXliJflr7nmjIfmoIfmjInnhad0b3Az5pi+56S657u/6Imy77yM5YW25L2Z5pi+56S654Gw6Imy77yM5ZCM5pe25bCG5YaF5a655pi+56S65qC85byP6Ieq5a6a5LmJ5Li65rWu54K55Z6LKyhyYW5rOuWQjeasoSnov5vooYzmmL7npLrjgIINCi0gcmVnaXN0ZXJlZOWIl+WImeWcqOWvueWhq+WFheminOiJsuaMieeFp+WvueW6lOW4g+WwlOWAvOi/m+ihjOaYvuekuu+8iFRSVUXmmL7npLrnu7/oibLjgIFGQUxTReaYvuekuue6ouiJsu+8ieS5i+Wklu+8jOWcqOW3puS+p+a3u+WKoOS6huWvueeUqOeahGljb27mlofmnKzvvIhUUlVF5pi+56S657u/6Imy5a+55Y+377yMRkFMU0XmmL7npLrnuqLoibLlj4nlj7fvvInjgIINCg0KDQojIyDlh73mlbDliIbnsbsNCg0KKirlrZfnrKbkuLLmi7zmjqXlh73mlbAqKg0KDQotICAgc3RyX2M6IOWtl+espuS4suaLvOaOpeOAgg0KLSAgIHN0cl90cmltOiDljrvmjonlrZfnrKbkuLLnmoTnmb3nqbrmoLwNCi0gICBzdHJfcGFkOiDooaXlhYXlrZfnrKbkuLLnmoTplb/luqYNCi0gICBgc3RyX3NxdWlzaGDvvJrliKDpmaTlrZfnrKbkuLLkuK3lpJrkuo4x5Liq55qE56m65qC844CC6Z2e5bi45pyJ55So55qE5Ye95pWw77yBDQotICAgc3RyX2R1cDog5aSN5Yi25a2X56ym5LiyDQotICAgc3RyX3N1Yjog5a2X56ym5Liy5YiH54mHDQotICAgc3RyX3N1Ylw8LSDlrZfnrKbkuLLliIfniYfvvIzlubbotYvlgLzvvIzlkIxzdHJfc3ViDQoNCioq5a2X56ym5Liy6K6h566X5Ye95pWwKioNCg0KLSAgIHN0cl9jb3VudDog5a2X56ym5Liy6K6h5pWwDQotICAgYHN0cl9sZW5ndGgoKWA6IOWtl+espuS4sumVv+W6pu+8jOWQjGBuY2hhcigpYA0KLSAgIHN0cl9zb3J0OiDlrZfnrKbkuLLlgLzmjpLluo8NCi0gICBzdHJfb3JkZXI6IOWtl+espuS4sue0ouW8leaOkuW6j++8jOinhOWImeWQjHN0cl9zb3J0DQoNCioq5a2X56ym5Liy5Yy56YWN5Ye95pWwKioNCg0KLSAgIGBzdHJfdmlldygpL3N0cl92aWV3X2FsbCgpYCDmn6XnnItzdHJpbmfmmK/lkKbljLnphY1wYXR0ZXJu77yM5aaC5p6c5Yy56YWN77yM5bCx6auY5Lqu5pi+56S6DQotICAgc3RyX3NwbGl0OiDlrZfnrKbkuLLliIblibINCi0gICBzdHJfc3BsaXRfZml4ZWQ6IOWtl+espuS4suWIhuWJsu+8jOWQjHN0cl9zcGxpdA0KLSAgIHN0cl9zdWJzZXQ6IOi/lOWbnuWMuemFjeeahOWtl+espuS4sg0KLSAgIHdvcmQ6IOS7juaWh+acrOS4reaPkOWPluWNleivjQ0KLSAgIGBzdHJfZGV0ZWN0KClgL2BzdHJfdmlldygpYDog5qOA5p+l5Yy56YWN5a2X56ym5Liy55qE5a2X56ymDQotICAgc3RyX21hdGNoOiDku47lrZfnrKbkuLLkuK3mj5Dlj5bljLnphY3nu4TjgIINCi0gICBzdHJfbWF0Y2hfYWxsOiDku47lrZfnrKbkuLLkuK3mj5Dlj5bljLnphY3nu4TvvIzlkIxzdHJfbWF0Y2gNCi0gICBgY2hhcnRyKG9sZCwgbmV3LCBzdHJpbmcpYDog5LuOIG9sZCDliLAgbmV3IOeahOS4gOS4gOWvueW6lOeahOabv+aNou+8jOS9nOeUqOWcqCBzdHJpbmcg5LiKDQotICAgc3RyX3JlcGxhY2U6IOWtl+espuS4suabv+aNog0KLSAgIHN0cl9yZXBsYWNlX2FsbDog5a2X56ym5Liy5pu/5o2i77yM5ZCMc3RyX3JlcGxhY2UNCi0gICBzdHJfcmVwbGFjZV9uYTog5oqKTkHmm7/mjaLkuLrlrZfnrKbkuLIiTkEiDQotICAgc3RyX3JlbW92Ze+8muWIoOmZpOWMuemFjeeahOWtl+espuS4sg0KLSAgIHN0cl9yZW1vdmVfYWxs77ya5Yig6Zmk5omA5pyJ5Yy56YWN55qE5a2X56ymDQotICAgc3RyX2xvY2F0ZTog5om+5Yiw5Yy56YWN55qE5a2X56ym5Liy55qE5L2N572u44CCDQotICAgc3RyX2xvY2F0ZV9hbGw6IOaJvuWIsOWMuemFjeeahOWtl+espuS4sueahOS9jee9rizlkIxzdHJfbG9jYXRlDQotICAgc3RyX2V4dHJhY3Q6IOS7juWtl+espuS4suS4reaPkOWPluWMuemFjeWtl+espg0KLSAgIHN0cl9leHRyYWN0X2FsbDog5LuO5a2X56ym5Liy5Lit5o+Q5Y+W5Yy56YWN5a2X56ym77yM5ZCMc3RyX2V4dHJhY3QNCg0KKirlrZfnrKbkuLLmoLzlvI/ljJYqKg0KDQotIGBiYXNlOjpzcHJpbnRmKClg77yM6K+m6KeB5LiK5LiA6IqCDQotICAgYHN0cl9nbHVlKClg77ya5Y+C5pWw6YeM6Z2i55qEYHt9YOWNoOS9jeespu+8jOWQjGphdmFzY3JpcHTnmoRgJHt9YA0KLSAgIHN0cl93cmFwOiDmjqfliLbplb/lrZfnrKbkuLLovpPlh7rmjpLniYgNCi0gICBzdHJfY29udjog5a2X56ym57yW56CB6L2s5o2iDQotICAgYHN0cl90b191cHBlcigpYC9gdG91cHBlcigpYDog5a2X56ym5Liy6L2s5oiQ5aSn5YaZDQotICAgYHN0cl90b19sb3dlcigpYC9gdG9sb3dlcigpYDog5a2X56ym5Liy6L2s5oiQ5bCP5YaZLOinhOWImeWQjHN0cl90b191cHBlcg0KLSAgIHN0cl90b190aXRsZSgpOiDlrZfnrKbkuLLovazmiJDpppblrZfmr43lpKflhpks6KeE5YiZ5ZCMc3RyX3RvX3VwcGVyDQotICAgc3RyX3RvX3NlbnRlbmNlKCk6IOWPquacieS4gOWPpeeahOmmluWtl+avjeWkp+WGmQ0KDQoqKuWPguaVsOaOp+WItuWHveaVsO+8jOS7heeUqOS6juaehOmAoOWKn+iDveeahOWPguaVsO+8jOS4jeiDveeLrOeri+S9v+eUqOOAgioqDQoNCi0gICBib3VuZGFyeTog5a6a5LmJ5L2/55So6L6555WMDQotICAgY29sbDog5a6a5LmJ5a2X56ym5Liy5qCH5YeG5o6S5bqP6KeE5YiZ44CCDQotICAgZml4ZWQ6IOWumuS5ieeUqOS6juWMuemFjeeahOWtl+espu+8jOWMheaLrOato+WImeihqOi+vuW8j+S4reeahOi9rOS5ieespg0KLSAgIHJlZ2V4OiDlrprkuYnmraPliJnooajovr7lvI8NCg0KDQojIyBzdHJpbmdyOjoNCg0KPGEgaHJlZj0iLi4vcGRmL2NoZWF0c2hlZXQtc3RyaW5ncy5wZGYiPjxzdHJvbmc+c3RyaW5nciBjaGVhdHNoZWV0LnBkZjwvc3Ryb25nPjwvYT4NCg0KPG9iamVjdCBkYXRhPSIuLi9wZGYvY2hlYXRzaGVldC1zdHJpbmdzLnBkZiIgdHlwZT0iYXBwbGljYXRpb24vcGRmIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIj48L29iamVjdD4NCg0KDQojIyMgYHN0cl9sZW5ndGgoKWAg5a2X56ym5Liy6ZW/5bqmDQoNCuWQjCBgYmFzZTo6bmNoYXIoKWANCg0K5L2/55SoIFJTdHVkaW/vvIzpgqPkuYjpgJrnlKjliY3nvIAgc3RyXF8g5Lya54m55Yir5pyJ55So77yM5Zug5Li66L6T5YWlIHN0clxfIOWQjuS8muinpuWPkeiHquWKqOWujOaIkOWKn+iDve+8jOS9oOWPr+S7peeci+WIsOaJgOacieeahOWtl+espuS4suWHveaVsO+8mg0KDQpgYGB7cn0NCnN0cl9sZW5ndGgoYygiYSIsICJSIGZvciBkYXRhIHNjaWVuY2UiKSkNCnN0cl9sZW5ndGgoYygiYSIsICJSIGZvciBkYXRhIHNjaWVuY2UiKSkgJT4lIHN1bSgpDQpgYGANCg0KIyMjIGBzdHJfYygpYOWtl+espuS4sue7hOWQiA0KDQpgc3RyX2MoLi4uLCBzZXAgPSAiIiwgY29sbGFwc2UgPSBOVUxMKWANCg0K5ZCI5bm26Iul5bmy5Liq5a2X56ym5Liy5oiW5a2X56ym5ZCR6YeP55qE5a+55bqU5YWD57Sg77yM6buY6K6k5peg6Ze06ZqU56ymDQoNCmBgYHtyfQ0KIyDlkJHph4/ljJbmk43kvZzvvIzoh6rliqjlvqrnjq/nn63lkJHph48NCnN0cl9jKCJwcmVmaXgtIiwgYygiYSIsICJiIiwgImMiKSwgIi1zdWZmaXgiKQ0KYGBgDQoNCmBgYHtyfQ0KIyDlkIjlubbkuIDkuKrlrZfnrKblkJHph4/nmoTlkITlhYPntKDkuLrkuIDkuKrplb/lrZfnrKbkuLLvvIzkvb/nlKjlj4LmlbBjb2xsYXBzZe+8jOWwhuWQkemHj+KAnOWhjOe8qeKAneS4uuS4gOS4quWtl+espuS4sg0Kc3RyX2MoYygieCIsICJ5IiwgInoiKSwgY29sbGFwc2UgPSAiKyIpDQpgYGANCg0KDQpgc3RyX3JlcGxhY2VfbmEoc3RyaW5nLCByZXBsYWNlbWVudCA9ICJOQSIpYO+8jOm7mOiupOWwhiBuYSDovazljJbkuLrlrZfnrKbkuLIiTkEiDQoNCmBgYHtyfQ0KeCA8LSBjKCJhYmMiLCBOQSkNCnN0cl9jKCJ8LSIsIHgsICItfCIpDQoNCnN0cl9jKCJ8LSIsIHN0cl9yZXBsYWNlX25hKHgpLCAiLXwiKQ0KYGBgDQoNCiMjIyBgc3RyX3RyaW0oKWAg5Y675o6J5a2X56ym5Liy6aaW5bC+55qE56m65qC85ZKM5Yi26KGo56ymKFxcdCkNCg0KPiBzdHJfdHJpbShzdHJpbmcsIHNpZGUgPSBjKCJib3RoIiwgImxlZnQiLCAicmlnaHQiKSkNCg0Kc2lkZTog6L+H5ruk5pa55byP77yMYm90aOS4pOi+uemDvei/h+a7pO+8jGxlZnTlt6bovrnov4fmu6TvvIxyaWdodOWPs+i+uei/h+a7pA0KDQojIyMgYHN0cl9wYWQoKWAg6KGl5YWF5a2X56ym5Liy55qE6ZW/5bqmDQoNCj4gc3RyX3BhZChzdHJpbmcsIHdpZHRoLCBzaWRlID0gYygibGVmdCIsICJyaWdodCIsICJib3RoIiksIHBhZCA9ICIgIikNCg0Kd2lkdGg6IOWtl+espuS4suWhq+WFheWQjueahOmVv+W6plwNCnNpZGU6IOWhq+WFheaWueWQke+8jGJvdGjkuKTovrnpg73loavlhYXvvIxsZWZ05bem6L655aGr5YWF77yMcmlnaHTlj7PovrnloavlhYVcDQpwYWQ6IOeUqOS6juWhq+WFheeahOWtl+espg0KDQojIyMgYHN0cl9kdXAoKWAg5aSN5Yi25a2X56ym5LiyDQoNCj4gc3RyX2R1cChzdHJpbmcsIHRpbWVzKQ0KDQp0aW1lczog5aSN5Yi25qyh5pWwDQoNCmBgYHtyfQ0KdmFsIDwtIGMoImFiY2E0IiwgMTIzLCAiY2JhMiIpDQoNCiMg5aSN5Yi2MuasoQ0Kc3RyX2R1cCh2YWwsIDIpDQoNCiMg5oyJ5L2N572u5aSN5Yi2DQpzdHJfZHVwKHZhbCwgMTozKQ0KYGBgDQoNCiMjIyBgc3RyX3dyYXAoKWAg5o6n5Yi26ZW/5a2X56ym5Liy6L6T5Ye65o6S54mIDQoNCj4gc3RyX3dyYXAoc3RyaW5nLCB3aWR0aCA9IDgwLCBpbmRlbnQgPSAwLCBleGRlbnQgPSAwKQ0KDQp3aWR0aDog6K6+572u5LiA6KGM5omA5Y2g55qE5a695bqm44CCXA0KaW5kZW50OiDmrrXokL3pppbooYznmoTnvKnov5vlgLxcDQpleGRlbnQ6IOauteiQvemdnummluihjOeahOe8qei/m+WAvA0KDQoNCmBgYHtyfQ0KIyDov5nmrrXku6PnoIHlj6/ku6XlnKggY29uc29sZSDkuK3ov5DooYzvvIzkvYbkuI3og73lnKggUm1hcmtkb3duIOS4reebtOaOpei/kOihjA0Kc3RyIDwtICJS6K+t6KiA5L2c5Li657uf6K6h5a2m5LiA6Zeo6K+t6KiA77yM5LiA55u05Zyo5bCP5LyX6aKG5Z+f6Zeq6ICA552A5YWJ6IqS44CC55u05Yiw5aSn5pWw5o2u55qE54iG5Y+R77yMUuivreiogOWPmOaIkOS6huS4gOmXqOeCmeaJi+WPr+eDreeahOaVsOaNruWIhuaekOeahOWIqeWZqOOAgumaj+edgOi2iuadpei2iuWkmueahOW3peeoi+iDjOaZr+eahOS6uueahOWKoOWFpe+8jFLor63oqIDnmoTnpL7ljLrlnKjov4XpgJ/mianlpKfmiJDplb/jgILnjrDlnKjlt7LkuI3ku4Xku4XmmK/nu5/orqHpoobln5/vvIzmlZnogrLvvIzpk7booYzvvIznlLXllYbvvIzkupLogZTnvZHigKYu6YO95Zyo5L2/55SoUuivreiogOOAgiINCg0KDQojIOiuvue9ruWuveW6puS4ujQw5Liq5a2X56ymDQojIGNhdChzdHJfd3JhcChzdHIsIHdpZHRoID0gNDApLCAiXG4iKQ0KDQojIOiuvue9ruWuveW6puS4ujYw5a2X56ym77yM6aaW6KGM57yp6L+bNOWtl+espg0KIyBjYXQoc3RyX3dyYXAodHh0LCB3aWR0aCA9IDYwLCBpbmRlbnQgPSA0KSwgIlxuIikNCg0KIyDorr7nva7lrr3luqbkuLoxMOWtl+espu+8jOmdnummluihjOe8qei/mzTlrZfnrKYNCiMgY2F0KHN0cl93cmFwKHR4dCwgd2lkdGggPSAxMCwgZXhkZW50ID0gNCksICJcbiIpDQpgYGANCg0KIyMjIGBzdHJfc3BsaXQoKWAg5ouG5YiG5a2X56ym5LiyDQoNCj4gc3RyX3NwbGl0KHN0cmluZywgcGF0dGVybiwgbiA9IEluZikgc3RyX3NwbGl0X2ZpeGVkKHN0cmluZywgcGF0dGVybiwgbikNCg0KcGF0dGVybjog5Yy56YWN55qE5a2X56ym44CCXA0Kbjog5YiG5Ymy5Liq5pWwDQoNCmBgYHtyfQ0KdmFsIDwtICJhYmMsMTIzLDIzNCxpdXV1Ig0KDQojIOS7pSzov5vooYzliIblibINCnMxIDwtIHN0cl9zcGxpdCh2YWwsICIsIikNCnMxDQoNCiMg5LulLOi/m+ihjOWIhuWJsu+8jOS4lOWPquS/neeVmTLlnZcNCnMyIDwtIHN0cl9zcGxpdCh2YWwsICIsIiwgMikNCnMyDQoNCiMg5p+l55yLc3RyX3NwbGl0KCnlh73mlbDmk43kvZznmoTnu5PmnpznsbvlnotsaXN0DQpjbGFzcyhzMSkNCg0KIyDnlKhzdHJfc3BsaXRfZml4ZWQoKeWHveaVsOWIhuWJsu+8jOe7k+aenOexu+Wei+aYr21hdHJpeA0KczMgPC0gc3RyX3NwbGl0X2ZpeGVkKHZhbCwgIiwiLCAyKQ0KczMNCmNsYXNzKHMzKQ0KDQoNCmZydWl0cyA8LSBjKA0KICAiYXBwbGVzIGFuZCBvcmFuZ2VzIGFuZCBwZWFycyBhbmQgYmFuYW5hcyIsDQogICJwaW5lYXBwbGVzIGFuZCBtYW5nb3MgYW5kIGd1YXZhcyINCikNCnN0cl9zcGxpdChmcnVpdHMsICIgYW5kICIpDQoNCg0KZ3BsIDwtIHJlYWRMaW5lcyhmaWxlLnBhdGgoUi5ob21lKCksICJDT1BZSU5HIikpDQp3b3JkcyA8LSB1bmxpc3Qoc3Ryc3BsaXQoZ3BsLCAiXFxXIikpICMgXFxXIOaEj+S4uumdnuWNleivjeWtl+espg0Kd29yZHMgPC0gd29yZHNbd29yZHMgIT0gIiJdICMg5Y675o6J56m65a2X56ymDQp0YWlsKHNvcnQodGFibGUodG9sb3dlcih3b3JkcykpKSwgMTApICMg6aKR5pWw5pyA5aSn55qEMTDkuKrljZXor40NCmBgYA0KDQojIyMgYHN0cl9zdWIoKWAg5a2X56ym5Liy5Y+W5a2Q6ZuGDQoNCj4gc3RyX3N1YihzdHJpbmcsIHN0YXJ0ID0gMUwsIGVuZCA9IC0xTCkNCg0KPiBzdHJfc3ViKHN0cmluZywgc3RhcnQgPSAxTCwgZW5kID0gLTFMKSBcPC0gdmFsdWUNCg0K5Lik5Liq5Y+C5pWw5YiG5Yir5Li65aeL5pyr5L2N572u77yM56ym5Y+35Luj6KGo5LuO5ZCO5b6A5YmN5pWw56ys5aSa5bCR5Liq5a2X56ymDQoNCmBgYHtyfQ0KdHh0IDwtICJJIGFtIENvbmFuLiINCg0KIyDliIYy5q615oiq5Y+W5a2X56ym5LiyDQpzdHJfc3ViKHR4dCwgYygxLCA0KSwgYyg2LCA4KSkNCg0KIyDpgJrov4fotJ/lnZDmoIfmiKrlj5blrZfnrKbkuLINCnN0cl9zdWIodHh0LCAtMykNCnN0cl9zdWIodHh0LCBlbmQgPSAtMykNCg0KIyDotYvlgLwNCnggPC0gYygiQXBwbGUiLCAiQmFuYW5hIiwgIlBlYXIiKQ0KIyBzdHJfc3ViKCnlr7l45ZCR6YeP5YyW5pON5L2c77ybc3RyX3RvX2xvd2Vy5YWo6YOo6L2s5o2i5Li65bCP5YaZ5a2X5q+NDQpzdHJfc3ViKHgsIDEsIDEpIDwtIHN0cl90b19sb3dlcihzdHJfc3ViKHgsIDEsIDEpKQ0KeA0KYGBgDQoNCiMjIyBgc3RyX3RvXyooKWAg5aSn5bCP5YaZ5Ye95pWw5pePDQoNCmBzdHJfdG9fdXBwZXIoc3RyaW5nLCBsb2NhbGUgPSAiIilgIOi9rOaNouS4uuWFqOWkp+WGmeWtl+avjQ0KDQpgc3RyX3RvX2xvd2VyKHN0cmluZywgbG9jYWxlID0gIiIpYCDovazmjaLkuLrlhajlsI/lhpnlrZfmr40NCg0KYHN0cl90b190aXRsZShzdHJpbmcsIGxvY2FsZSA9ICIiKWAg5Y2V6K+N6aaW5a2X5q+N5aSn5YaZDQoNCmBzdHJfdG9fc2VudGVuY2UoKWAg5YWo5Y+l6aaW5a2X5q+N5aSn5YaZDQoNCmxvY2FsZTrmjInlk6rnp43or63oqIDkuaDmg6/mjpLluo8NCg0KYGBge3J9DQpkb2cgPC0gIkxvb2sgdGhlIHF1aWNrIGJyb3duIGRvZyEiDQpzdHJfdG9fdXBwZXIoZG9nKQ0Kc3RyX3RvX2xvd2VyKGRvZykNCnN0cl90b190aXRsZShkb2cpDQpzdHJfdG9fc2VudGVuY2UoImxvb2sgdGhlIHF1aWNrIGJyb3duIGRvZyEiKQ0KYGBgDQoNCiMjIyBgc3RyX2NvdW50KClgIOWtl+espuS4suiuoeaVsA0KDQo+IHN0cl9jb3VudChzdHJpbmcsIHBhdHRlcm4gPSAiIikNCg0KYGBge3J9DQojIOazqOaEj+ato+WImeihqOi+vuW8j+eahOeJueauiuWtl+espg0Kc3RyX2NvdW50KGMoImEuIiwgIi4iLCAiLmEuIiwgTkEpLCAiLiIpDQoNCiMg55SoZml4ZWTljLnphY3lrZfnrKYNCnN0cl9jb3VudChjKCJhLiIsICIuIiwgIi5hLiIsIE5BKSwgZml4ZWQoIi4iKSkNCg0KIyDnlKhcXOWMuemFjeWtl+espg0Kc3RyX2NvdW50KGMoImEuIiwgIi4iLCAiLmEuIiwgTkEpLCAiXFwuIikNCmBgYA0KDQojIyMgYHN0cl9zb3J0KClgIOWtl+espuS4suaOkuW6jw0KDQo+IHN0cl9zb3J0KHgsIGRlY3JlYXNpbmcgPSBGQUxTRSwgbmFfbGFzdCA9IFRSVUUsIGxvY2FsZSA9ICIiLCAuLi4pIHN0cl9vcmRlcih4LCBkZWNyZWFzaW5nID0gRkFMU0UsIG5hX2xhc3QgPSBUUlVFLCBsb2NhbGUgPSAiIiwgLi4uKQ0KDQp4OiDlrZfnrKbkuLLvvIzlrZfnrKbkuLLlkJHph4/jgIIgZGVjcmVhc2luZzog5o6S5bqP5pa55ZCR44CCIG5hX2xhc3Q6IE5B5YC855qE5a2Y5pS+5L2N572u77yM5LiA5YWxM+S4quWAvO+8jFRSVUXmlL7liLDmnIDlkI7vvIxGQUxTReaUvuWIsOacgOWJje+8jE5B6L+H5ruk5aSE55CGIGxvY2FsZTog5oyJ5ZOq56eN6K+t6KiA5Lmg5oOv5o6S5bqPDQoNCmBgYHtyfQ0KIyDmjIlBU0NJSeWtl+avjeaOkuW6jw0Kc3RyX3NvcnQoYygiYSIsIDEsIDIsICIxMSIpLCBsb2NhbGUgPSAiZW4iKQ0KDQojIOWAkuW6j+aOkuW6jw0Kc3RyX3NvcnQobGV0dGVycywgZGVjcmVhc2luZyA9IFRSVUUpDQoNCiMg5oyJ5ou86Z+z5o6S5bqPDQpzdHJfc29ydChjKCLkvaAiLCAi5aW9IiwgIueyiSIsICLkuJ0iLCAi5pelIiwgIuW/lyIpLCBsb2NhbGUgPSAiemgiKQ0KYGBgDQoNCuWvuU5B55qE5o6S5bqP5aSE55CG77yaDQoNCmBgYHtyfQ0KIyDmiopOQeaUvuacgOWQjumdog0Kc3RyX3NvcnQoYyhOQSwgIjEiLCBOQSksIG5hX2xhc3QgPSBUUlVFKQ0KDQojIOaKik5B5pS+5pyA5YmN6Z2iDQpzdHJfc29ydChjKE5BLCAiMSIsIE5BKSwgbmFfbGFzdCA9IEZBTFNFKQ0KDQojIOWOu+aOiU5B5YC8DQpzdHJfc29ydChjKE5BLCAiMSIsIE5BKSwgbmFfbGFzdCA9IE5BKQ0KYGBgDQoNCiMjIyBgc3RyX2RldGVjdCgpYCDmo4DmtYvmmK/lkKbljLnphY3vvIzov5Tlm55UL0YNCg0KYGBge3J9DQp2YWwgPC0gYygiYWJjYTQiLCAxMjMsICJjYmEyIikNCg0KIyDmo4Dmn6XlrZfnrKbkuLLlkJHph4/vvIzmmK/lkKbljIXmi6xhDQpzdHJfZGV0ZWN0KHZhbCwgImEiKQ0KDQojIOajgOafpeWtl+espuS4suWQkemHj++8jOaYr+WQpuS7pWHkuLrlvIDlpLQNCnN0cl9kZXRlY3QodmFsLCAiXmEiKQ0KDQoNCiMg5qOA5p+l5a2X56ym5Liy5ZCR6YeP77yM5piv5ZCm5LulYeS4uue7k+Wwvg0Kc3RyX2RldGVjdCh2YWwsICJhJCIpDQpgYGANCg0KIyMjIGBzdHJfc3Vic2V0KClgIOi/lOWbnuaIkOWKn+WMuemFjeeahOWtl+espuS4sg0KDQpgYGB7cn0NCnZhbCA8LSBjKCJhYmMiLCAxMjMsICJjYmEiKQ0KDQojIOWFqOaWh+WMuemFjQ0Kc3RyX3N1YnNldCh2YWwsICJhIikNCg0KDQojIOW8gOWktOWMuemFjQ0Kc3RyX3N1YnNldCh2YWwsICJeYSIpDQoNCg0KIyDnu5PlsL7ljLnphY0NCnN0cl9zdWJzZXQodmFsLCAiYSQiKQ0KYGBgDQoNCiMjIyBgc3RyX21hdGNoKCkvc3RyX2V4dHJhY3QoKWAg5LuO5a2X56ym5Liy5Lit5o+Q5Y+W5Yy56YWN5qih5byPDQoNCuS4u+imgeS4juato+WImeihqOi+vuW8j+mFjeWQiOS9v+eUqA0KDQo+IHN0cl9tYXRjaChzdHJpbmcsIHBhdHRlcm4pIA0KPiBzdHJfbWF0Y2hfYWxsKHN0cmluZywgcGF0dGVybikgDQoNCuS7peWtl+espuS4sm1hdHJpeOagvOW8j+i/lOWbng0KDQpgYGB7cn0NCnZhbCA8LSBjKCJhYmMiLCAxMjMsICJjYmEiKQ0KDQojIOWMuemFjeWtl+espmHvvIzlubbov5Tlm57lr7nlupTnmoTlrZfnrKYNCnN0cl9tYXRjaCh2YWwsICJhIikNCg0KIyDljLnphY3lrZfnrKYwLTnvvIzpmZAx5Liq77yM5bm26L+U5Zue5a+55bqU55qE5a2X56ymDQpzdHJfbWF0Y2godmFsLCAiWzAtOV0iKQ0KDQojIOWMuemFjeWtl+espjAtOe+8jOS4jemZkOaVsOmHj++8jOW5tui/lOWbnuWvueW6lOeahOWtl+espg0Kc3RyX21hdGNoKHZhbCwgIlswLTldKiIpDQoNCiMg6L+U5ZuebWF0cml4DQpzdHJfbWF0Y2hfYWxsKHZhbCwgImEiKQ0Kc3RyX21hdGNoX2FsbCh2YWwsICJbMC05XSIpDQpgYGANCg0KPiBzdHJfZXh0cmFjdChzdHJpbmcsIHBhdHRlcm4pIA0KPiBzdHJfZXh0cmFjdF9hbGwoc3RyaW5nLCBwYXR0ZXJuLCBzaW1wbGlmeSA9IEZBTFNFKQ0KDQpzaW1wbGlmeTogVFJVRei/lOWbnm1hdHJpeO+8jEZBTFNF6L+U5Zue5YiX6KGoDQoNCmBgYHtyfQ0KdmFsIDwtIGMoImFiY2E0IiwgMTIzLCAiY2JhMiIpDQoNCiMg6L+U5Zue5Yy56YWN55qE5pWw5a2XDQpzdHJfZXh0cmFjdCh2YWwsICJcXGQiKQ0KDQojIOi/lOWbnuWMuemFjeeahOWtl+espg0Kc3RyX2V4dHJhY3QodmFsLCAiW2Etel0rIikNCg0KdmFsIDwtIGMoImFiY2E0IiwgMTIzLCAiY2JhMiIpDQpzdHJfZXh0cmFjdF9hbGwodmFsLCAiXFxkIikNCg0Kc3RyX2V4dHJhY3RfYWxsKHZhbCwgIlthLXpdKyIpDQpgYGANCg0KDQojIyMgYHdvcmQoKWAg5LuO5paH5pys5Lit5o+Q5Y+W5Y2V6K+N77yI5Lul5Y2V6K+N5Li65Y2V5L2N55yL5b6F5a2X56ym5Liy77yJDQoNCj4gd29yZChzdHJpbmcsIHN0YXJ0ID0gMUwsIGVuZCA9IHN0YXJ0LCBzZXAgPSBmaXhlZCgiICIpKQ0KDQpzdGFydDog5LuO56ys5Yeg5Liq6K+N5byA5aeL44CCIGVuZDog5Yiw56ys5Yeg5Liq6K+N57uT5p2f44CCIHNlcDog5YiG6K+N6Ze06ZqU5a2X56ym77yM6buY6K6k5Li656m65qC844CCDQoNCmBgYHtyfQ0KdmFsIDwtIGMoIkkgYW0gQ29uYW4uIiwgImh0dHA6Ly9mZW5zLm1lLCBvayIpDQoNCiMg6buY6K6k5Lul56m65qC85YiG5Ymy77yM5Y+W56ys5LiA5Liq5L2N572u55qE5a2X56ym5LiyDQp3b3JkKHZhbCwgMSkNCndvcmQodmFsLCAtMSkNCndvcmQodmFsLCAyLCAtMSkNCg0KIyDku6Us5YiG5Ymy77yM5Y+W56ys5LiA5Liq5L2N572u55qE5a2X56ym5LiyDQp2YWwgPC0gIjExMSwyMjIsMzMzLDQ0NCINCndvcmQodmFsLCAxLCBzZXAgPSBmaXhlZCgiLCIpKQ0Kd29yZCh2YWwsIDMsIHNlcCA9IGZpeGVkKCIsIikpDQpgYGANCg0KIyMjIGBzdHJfcmVwbGFjZSgpYCDlrZfnrKbkuLLmm7/mjaINCg0KPiBzdHJfcmVwbGFjZShzdHJpbmcsIHBhdHRlcm4sIHJlcGxhY2VtZW50KSAgDQo+IHN0cl9yZXBsYWNlX2FsbChzdHJpbmcsIHBhdHRlcm4sIHJlcGxhY2VtZW50KSAgDQo+IHN0cl9yZXBsYWNlX25hKHN0cmluZywgcmVwbGFjZW1lbnQgPSAiTkEiKSwg5bCGTkHvvIjpu5jorqTvvInmm7/mjaLkuLonTkEnDQoNCioq5Y+C5pWwIHJlcGxhY2VtZW50IOWPr+S7peaYr+S4gOS4quWHveaVsO+8jOS9v+abv+aNoueahOinhOWImeabtOWKoOWkjeadgioqDQoNCmBgYHtyfQ0KdmFsIDwtIGMoImFiYyIsIDEyMywgImNiYSIpDQoNCiMg5oqK55uu5qCH5a2X56ym5Liy56ys5LiA5Liq5Ye6546w55qEYeaIlmLvvIzmm7/mjaLkuLotDQpzdHJfcmVwbGFjZSh2YWwsICJbYWJdIiwgIi0iKQ0KDQojIOaKiuebruagh+Wtl+espuS4suaJgOacieWHuueOsOeahGHmiJZi77yM5pu/5o2i5Li6LQ0Kc3RyX3JlcGxhY2VfYWxsKHZhbCwgIlthYl0iLCAiLSIpDQoNCiMg5oqK55uu5qCH5a2X56ym5Liy5omA5pyJ5Ye6546w55qEYe+8jOabv+aNouS4uuiiq+i9rOS5ieeahOWtl+espg0Kc3RyX3JlcGxhY2VfYWxsKHZhbCwgIlthXSIsICJcXCIpDQoNCg0KDQpwaWdJdCA8LSBmdW5jdGlvbihzdHJpbmcpIHsNCiAgc3RyX3JlcGxhY2VfYWxsKHN0cmluZywgIlxcdysiLCBmdW5jdGlvbih3b3JkKSB7DQogICAgc3RyX2Moc3RyX3N1Yih3b3JkLCAyKSwgc3RyX3N1Yih3b3JkLCAxLCAxKSwgImF5IikNCiAgfSkNCn0NCg0KbGlicmFyeSh0ZXN0dGhhdCkNCnRlc3RfdGhhdCgiU2FtcGxlIFRlc3RzIiwgew0KICBleHBlY3RfZXF1YWwocGlnSXQoIlBpZyBsYXRpbiBpcyBjb29sIiksICJpZ1BheSBhdGlubGF5IHNpYXkgb29sY2F5IikNCiAgZXhwZWN0X2VxdWFsKHBpZ0l0KCJIZWxsbyB3b3JsZCAhIiksICJlbGxvSGF5IG9ybGR3YXkgISIpDQp9KQ0KYGBgDQoNCiMjIyBgc3RyX2xvY2F0ZSgpYCDmib7liLDmqKHlvI/lnKjlrZfnrKbkuLLkuK3nmoTlp4vmnKvkvY3nva4NCg0KPiBzdHJfbG9jYXRlKHN0cmluZywgcGF0dGVybikgc3RyX2xvY2F0ZV9hbGwoc3RyaW5nLCBwYXR0ZXJuKQ0KDQpgYGB7cn0NCnZhbCA8LSBjKCJhYmNhIiwgMTIzLCAiY2JhIikNCg0KIyDljLnphY1h5Zyo5a2X56ym5Liy5Lit55qE5L2N572uDQpzdHJfbG9jYXRlKHZhbCwgImEiKQ0KDQojIOeUqOWQkemHj+WMuemFjQ0Kc3RyX2xvY2F0ZSh2YWwsIGMoImEiLCAxMiwgImIiKSkNCg0KIyDku6XlrZfnrKbkuLJtYXRyaXjmoLzlvI/ov5Tlm54NCnN0cl9sb2NhdGVfYWxsKHZhbCwgImEiKQ0KDQojIOWMuemFjWHmiJZi5a2X56ym77yM5Lul5a2X56ym5LiybWF0cml45qC85byP6L+U5ZueDQpzdHJfbG9jYXRlX2FsbCh2YWwsICJbYWJdIikNCmBgYA0KDQojIyMgYHN0cl9jb252YCDlrZfnrKbnvJbnoIHovazmjaINCg0KPiBzdHJfY29udihzdHJpbmcsIGVuY29kaW5nKQ0KDQpgYGB7cn0NCiMg5oqK5Lit5paH5a2X56ym5a2X6IqC5YyWDQp4IDwtIGNoYXJUb1Jhdygi5L2g5aW9IikNCngNCg0KIyDpu5jorqR3aW7ns7vnu5/lrZfnrKbpm4bkuLpHQkvvvIxHQjIzMTLkuLpHQkvlrZfpm4bvvIzovaznoIHmraPluLgNCnN0cl9jb252KHgsICJHQksiKQ0Kc3RyX2NvbnYoeCwgIkdCMjMxMiIpDQoNCiMg6L2sVVRGLTjlpLHotKUNCnN0cl9jb252KHgsICJVVEYtOCIpDQoNCiMg5oqKdW5pY29kZei9rFVURi04DQpzdHJfY29udigiXHU1MzE3XHU0ZWFjIiwgIlVURi04IikNCmBgYA0KDQojIyMgYHN0cl9nbHVlKClgIOaooeadv+Wtl+espuS4sg0KDQpgYGB7cn0NCm5hbWUgPC0gIkZyZWQiDQphZ2UgPC0gNTANCmFubml2ZXJzYXJ5IDwtIGFzLkRhdGUoIjE5OTEtMTAtMTIiKQ0Kc3RyX2dsdWUoDQogICJNeSBuYW1lIGlzIHtuYW1lfSwgIiwNCiAgIm15IGFnZSBuZXh0IHllYXIgaXMge2FnZSArIDF9LCAiLA0KICAiYW5kIG15IGFubml2ZXJzYXJ5IGlzIHtmb3JtYXQoYW5uaXZlcnNhcnksICclQSwgJUIgJWQsICVZJyl9LiINCikNCnN0cl9nbHVlKCJNeSBuYW1lIGlzIHtuYW1lfSwgbm90IHt7bmFtZX19LiIpDQpgYGANCg0KDQoNCiMjIGJhc2U6Og0KDQojIyMgYGNoYXJ0cigpYCDlrZfnrKbmm7/mjaINCg0KYGNoYXJ0cihvbGQsIG5ldywgeClg77yM6Z2e5bi45aW955So55qE5Ye95pWw77yM55u45b2T5LqO5oyJ5LiA5Liq5ZOI5biM6KGo6L+b6KGM5a2X56ym6L2s5o2iDQoNCmBgYHtyIGNoYXJ0cn0NCmNoYXJ0cihvbGQgPSAiYSIsIG5ldyA9ICJjIiwgeCA9ICJhMTIzIikNCiMg5rOo5oSP77yMb2xk5ZKMbmV35Y+C5pWw5Y+v5Lul6YO95piv5a2X56ym5Liy77yMaW5kZXgg5a+55bqU55qE5a2X56ym6L+b6KGM5Lu75oSP5qyh5pWw55qE5pu/5o2iDQpjaGFydHIoIlR0IiwgIlV1IiwgIkF0R0N0dHRBQ0MiKQ0KYGBgDQoNCiMjIyDlrZfnrKbkuLLliIblibINCg0KYHN0cnNwbGl0KHgsIHNwbGl0LCBmaXhlZCA9IEZBTFNFKWANCg0KYGBge3Igc3Ryc3BsaXR9DQojIGZpeGVkPUYsIOesrDLkuKrlj4LmlbDkuLrmraPliJnooajovr7lvI/jgIInLiflj6/ljLnphY3pmaTigJxcbuKAneWSjCJcciLkuYvlpJbnmoTku7vkvZXljZXkuKrlrZfnrKbjgIINCnNwbGl0ZWQgPC0gdW5saXN0KHN0cnNwbGl0KCJhLmIuYyIsICIuIikpDQpzcGxpdGVkDQojIGZpeGVkPVTvvIznrKwy5Liq5Y+C5pWw5Li65paH5pys5a2X56ym5LiyDQpzcGxpdGVkIDwtIHVubGlzdChzdHJzcGxpdCgiYS5iLmMiLCAiLiIsIGZpeGVkID0gVCkpDQpzcGxpdGVkDQpgYGANCg0KIyMjIOWtl+espuS4suWMuemFjQ0KDQrlrZfnrKbkuLLljLnphY3lh73mlbAgZ3JlcChwYXR0ZXJuLCB4LCBmaXhlZD1GQUxTRSnlkoxncmVwbCgpDQoNCmBgYHtyIGdyZXB9DQpjc3RyIDwtIGMoImIiLCAiQSIsICJjIiwgImEuYSIpDQojIGZpeGVkPVTvvIznrKwx5Liq5Y+C5pWw5Li65paH5pys5a2X56ym5LiyDQpncmVwKCIuIiwgY3N0ciwgZml4ZWQgPSBUUlVFKQ0KIyBmaXhlZD1G77yM56ysMeS4quWPguaVsOS4uuato+WImeihqOi+vuW8jw0KZ3JlcCgiLiIsIGNzdHIsIGZpeGVkID0gRikNCiMgZ3JlcOS7hei/lOWbnuWMuemFjemhueeahOS4i+agh++8m+iAjGdyZXBs6L+U5Zue6YC76L6R5ZCR6YeP6KGo56S65pyJ5rKh5pyJ5om+5Yiw5Yy56YWNDQpncmVwbCgiLiIsIGNzdHIsIGZpeGVkID0gVFJVRSkNCiMg55So5LqO5o+Q5Y+W5a2Q6ZuG5piv5LiA5qC355qEDQpjc3RyW2dyZXAoIi4iLCBjc3RyLCBmaXhlZCA9IFRSVUUpXQ0KY3N0cltncmVwbCgiLiIsIGNzdHIsIGZpeGVkID0gVFJVRSldDQpgYGANCg0KIyMjIOWtl+espuS4suabv+aNog0KDQrlrZfnrKbkuLLmm7/mjaLlh73mlbAgc3ViKHBhdHRlcm4sIHJlcGxhY2VtZW50LCB4LCBmaXhlZD1GQUxTRSkNCg0KYGBge3Igc3VifQ0KIyBmaXhlZD1GLCDnrKwx5Liq5Y+C5pWw5Li65q2j5YiZ6KGo6L6+5byP77yMJ1xccyfooajnpLrnqbrnmb0NCnN1YigiXFxzIiwgIi4iLCAiSGVsbG8gVGhlcmUiKQ0KIyBmaXhlZD1ULCDnrKwx5Liq5Y+C5pWw5Li65paH5pys5a2X56ym5Liy77yM5ZyoJ0hlbGxvIFRoZXJlJ+S4reaJvuS4jeWIsCdcXHMnDQpzdWIoIlxccyIsICIuIiwgIkhlbGxvIFRoZXJlIiwgZml4ZWQgPSBUKQ0KIyBzdWIoKeS4jmdzdWIoKeeahOWMuuWIq++8jOabv+aNouS4gOasoeWSjOabv+aNouS7u+aEj+asoe+8iOWPquimgeWMuemFjeaIkOWKn++8iQ0Kc3ViKCJlIiwgIkUiLCAiSGVsbG8gVGhlcmUiLCBmaXhlZCA9IFQpDQpnc3ViKCJlIiwgIkUiLCAiSGVsbG8gVGhlcmUiLCBmaXhlZCA9IFQpDQpgYGA=