中文分词学习笔记 R_Text_Mining_Chinese_Word_Segmentation

http://blog.fens.me/r-word-jiebar/

jiebaR中文分词快速入门

文本挖掘是数据挖掘中一个非常重要的部分,有非常广阔的使用场景,比如我们可以对新闻事件进行分析,了解国家大事;也可以对微博信息进行分析,通过社交舆情看看大家的关注点。通过文本挖掘找到文章中的隐藏信息,对文章的结构进行分析,判断是不是同一个作者写文章;同时可以对邮件分析,结合bayes算法判断哪些是垃圾邮件,哪些是有用的邮件。

文本挖掘的第一步,就是要进行分词,分词将直接影响文本挖掘的效果。R语言在分词方面有很好的支持,接下来就给大家介绍一个不错的R语言中文分词包“结巴分词”(jiebaR)。

1. 简介与安装-JiebaR Introduction & Installation

1.1 简介

JiebaR is a package for Chinese text segmentation, keyword extraction and speech tagging. 结巴分词(jiebaR),是一款高效的R语言中文分词包,底层使用的是C++,通过Rcpp进行调用很高效。结巴分词基于MIT协议,就是免费和开源的,感谢国人作者的给力支持,让R的可以方便的处理中文文本。

官方Github的地址:https://github.com/qinwf/jiebaR

# Install the latest development version from GitHub:
# 同时还可以通过Github安装[开发版],
# 建议使用 gcc >= 4.9 编译,Windows需要安装 Rtools :
library(devtools)
install_github("qinwf/jiebaRD")
install_github("qinwf/jiebaR")
library("jiebaR")
# devtools::install_github("qinwf/jiebaR")

# Install from CRAN:
install.packages("jiebaR")
library("jiebaR")

1.2 上手案例

# import package in a safe way
if(!suppressWarnings(require('jiebaR'))) {
  install.packages('jiebaR')
  require('jiebaR')
}

#initial 
wk = worker()

wk["jiebaR提供了四种分词模式,可以通过函数worker()来初始化分词引擎,使用函数segment()进行分词。具体使用?worker查看帮助"]
#  [1] "jiebaR"  "提供"    "了"      "四种"   
#  [5] "分"      "词模式"  "可以"    "通过"   
#  [9] "函数"    "worker"  "来"      "初始化" 
# [13] "分词"    "引擎"    "使用"    "函数"   
# [17] "segment" "进行"    "分词"    "具体"   
# [21] "使用"    "worker"  "查看"    "帮助"   

wk["结巴分词(jiebaR),是一款高效的R语言中文分词包"]
#  [1] "结巴"   "分词"   "jiebaR" "是"     "一款"
#  [6] "高效"   "的"     "R"      "语言"   "中文"
# [11] "分词"   "包"

jiebaR提供了3种分词语句的写法,例子上面的用[]符号的语法,还可以使用<=符合语法,或者使用segment()函数。虽然形式不同,但是分词效果是一样的。

# 使用<=符号的语法,如下
wk <= '使用小于等于的另一种符合的语法'
# [1] "使用" "小于" "等于" "的"   "另"   "一种"
# [7] "符合" "的"   "语法"

# 使用segment()函数的语法,如下
segment( "使用segment()函数的语法,如下" , wk )
# [1] "使用"    "segment" "函数"    "的"     
# [5] "语法"    "如下"

我们也可以直接对文本文件进行分词,在当前目录新建一个文本文件file_segmentation.txt。

# 在调用worker()函数时,我们实际是在加载jiebaR库的分词引擎。jiebaR库提供了7种分词引擎。
# 
# 混合模型(MixSegment):是四个分词引擎里面分词效果较好的类,结它合使用最大概率法和隐式马尔科夫模型。
# 最大概率法(MPSegment) :负责根据Trie树构建有向无环图和进行动态规划算法,是分词算法的核心。
# 隐式马尔科夫模型(HMMSegment):是根据基于人民日报等语料库构建的HMM模型来进行分词,主要算法思路是根据(B,E,M,S)四个状态来代表每个字的隐藏状态。 HMM模型由dict/hmm_model.utf8提供。分词算法即viterbi算法。
# 索引模型(QuerySegment):先使用混合模型进行切词,再对于切出来的较长的词,枚举句子中所有可能成词的情况,找出词库里存在。
# 标记模型(tag)
# Simhash模型(simhash)
# 关键词模型(keywods)

wk['E:/03-Download/Github/xiangxing98.github.io/R_Learning/file_segmentation.txt']
# "E:/03-Download/Github/xiangxing98.github.io/R_Learning/file_segmentation.segment.2017-07-17_01_42_20.txt"

jiebaR提供了四种分词模式,可以通过函数worker()来初始化分词引擎,使用函数segment()进行分词。具体使用?worker查看帮助

# import package in a safe way
if(!suppressWarnings(require('jiebaR'))) {
  install.packages('jiebaR')
  require('jiebaR')
}

text <- '你要明白,这仅仅是一个测试文本'
mixseg <- worker() #使用默认参数,混合模型(MixSegment)

segment(text, mixseg)
#等价于mixseg[text]
#也等价于mixseg <= text
# [1] "你"   "要"   "明白" "这"   "仅仅" "是"   "一个" "测试" "文本"

# 直接输入mixseg命令,可以查看此worker的配置
mixseg
# Worker Type:  Jieba Segment
# 
# Default Method  :  mix
# Detect Encoding :  TRUE
# Default Encoding:  UTF-8
# Keep Symbols    :  FALSE
# Output Path     :  
# Write File      :  TRUE
# By Lines        :  FALSE
# Max Word Length :  20
# Max Read Lines  :  1e+05
# 
# Fixed Model Components:  
# 
# $dict
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/jieba.dict.utf8"
# 
# $user
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/user.dict.utf8"
# 
# $hmm
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/hmm_model.utf8"
# 
# $stop_word
# NULL
# 
# $user_weight
# [1] "max"
# 
# $timestamp
# [1] 1500174243
# 
# $default $detect $encoding $symbol $output $write $lines $bylines can be reset.

# another example 
library(jiebaR)
#  接受默认参数,建立分词引擎 
mixseg = worker()
# 相当于:
# worker( type = "mix", dict = "inst/dict/jieba.dict.utf8",
#         hmm  = "inst/dict/hmm_model.utf8",    # HMM模型数据
#         user = "inst/dict/user.dict.utf8")    # 用户自定义词库
# Initialize jiebaR worker 初始化worker

# This function can initialize jiebaR workers. 
# You can initialize different kinds of workers including mix, mp, hmm, query, tag, simhash, and keywords.

mixseg <= "广东省深圳市联通"    # <= 分词运算符
# 相当于segment函数,看起来还是用segment函数顺眼一些

segment(code= "广东省深圳市联通" , jiebar = mixseg)
# code A Chinese sentence or the path of a text file.
# jiebar jiebaR Worker

# 分词结果
# [1] "广东省" "深圳市" "联通" 

mixseg <= "你知道我不知道"
# [1] "你"   "知道" "我"   "不"   "知道"

mixseg <= "我昨天参加了同学婚礼"
# [1] "我"   "昨天" "参加" "了"   "同学" "婚礼"

mixseg <= "你知道吗我昨天参加了同学的婚礼"
 # [1] "你"   "知道" "吗"   "我"   "昨天" "参加" "了"   "同学"
 # [9] "的"   "婚礼"
# 呵呵:分词结果还算不错

可以通过R语言常用的 $ 符号重设一些worker的参数设置,如 WorkerName $symbol = T,在输出中保留标点符号。

一些参数在初始化的时候已经确定,无法修改, 可以通过WorkerName$PrivateVarible来获得这些信息。

#自动判断输入文件编码模式,默认文件输出在同目录下。
segment('D:/test.txt', mixseg) 
#等价于mixseg['D:/test.txt']
#也等价于mixseg <= 'D:/test.txt'

mixseg <= 'E:\\03-Download\\Github\\红楼梦.txt'
#GB2312 CODING, NO OUTPUT

mixseg <= 'E:/03-Download/Github/红楼梦-UTF-8.txt'
#STILL NO OUTPUT

segment('你今天要到哪里去?', mixseg) 

segment('E:/03-Download/Github/红楼梦-UTF-8.txt', mixseg)
# [1] "E:/03-Download/Github/红楼梦-UTF-8.segment.2017-07-16_11_42_23.txt"

1.3 Examples from tutorial PDF

Examples from tutorial PDF

### Note: Can not display Chinese characters here.
## Not run:
words = "hello world"
engine1 = worker()
segment(words, engine1)

# "./temp.txt" is a file path
segment("./temp.txt", engine1)
engine2 = worker("hmm")
segment("./temp.txt", engine2)
engine2$write = T
segment("./temp.txt", engine2)
engine3 = worker(type = "mix", dict = "dict_path",symbol = T)
segment("./temp.txt", engine3)

## End(Not run)
## Not run:
### Keyword Extraction
engine = worker("keywords", topn = 1)
keywords(words, engine)

### Speech Tagging
tagger = worker("tag")
tagging(words, tagger)

### Simhash
simhasher = worker("simhash", topn = 1)
simhash(words, simhasher)
distance("hello world" , "hello world!" , simhasher)
show_dictpath()
## End(Not run)

1.4 file segmentation

wk <- worker(encoding="UTF-8")
wk[file.choose()]

# 词频分析
#这里选择分词之后的文件(不需要先分析,直接选择原文件即可)
f <- scan(file.choose(),sep="\n",what="",encoding="UTF-8")
# Read 1 item
df <- freq(wk[f])
df <- df[order(-df$freq),]

install.packages("sqldf")

library("sqldf")
#用View(df)看一下里面字段名,不然怎么写SQL都不知道,提取前100项,不然做词云的时候很卡~
df <- sqldf("select [char],[freq] from df where length([char])>1 limit 100")
#这里的SQL语句可以随自己的实际需求填写,反正都一样,再用View(df)看一下数据,成功了!其实排序也是可以直接使用SQL进行排序的,只是,只是~在写这文章的时候忘记了。。。
#update at 2017-3-9 数据框方法,蛋疼的SQL~
df <- df[nchar(df$char)>1,]

2. 四种分词算法-four kinds of segmentation model

2.1 最大概率法(MPSegment):

负责根据Trie树构建有向无环图和进行动态规划算法,是分词算法的核心。

Maximum probability segmentation model uses Trie tree to construct a directed acyclic graph and uses dynamic programming algorithm. It is the core segmentation algorithm. dict and user should be provided when initializing jiebaR worker.

text <- '你要明白,这仅仅是一个测试文本'
mpseg <- worker('mp') #最大概率法(MPSegment)
mpseg[text]
# [1] "你"   "要"   "明白" "这"   "仅仅" "是"   "一个" "测试" "文本"

2.2 隐式马尔科夫模型(HMMSegment):

是根据基于人民日报等语料库构建的HMM模型来进行分词,主要算法思路是根据(B,E,M,S)四个状态来代表每个字的隐藏状态。 HMM模型由dict/hmm_model.utf8提供。分词算法即viterbi算法。

Hidden Markov Model uses HMM model to determine status set and observed set of words. The default HMM model is based on People’s Daily language library. hmm should be provided when initializing jiebaR worker.

text <- '你要明白,这仅仅是一个测试文本'
hmmseg <- worker('hmm') #隐式马尔科夫模型(HMMSegment)
hmmseg[text]
# [1] "你"   "要"   "明白" "这仅" "仅"   "是"   "一个" "测试" "文本"

2.3 混合模型(MixSegment):

是四个分词引擎里面分词效果较好的类,结它合使用最大概率法和隐式马尔科夫模型。

MixSegment model uses both Maximum probability segmentation model and Hidden Markov Model to construct segmentation. dict, hmm and user should be provided when initializing jiebaR worker.

text <- '你要明白,这仅仅是一个测试文本'
mixseg <- worker('mix') #混合模型(MixSegment)
mixseg[text]
# [1] "你"   "要"   "明白" "这"   "仅仅" "是"   "一个" "测试" "文本"

2.4 索引模型(QuerySegment):

先使用混合模型进行切词,再对于切出来的较长的词,枚举句子中所有可能成词的情况,找出词库里存在。

QuerySegment model uses MixSegment to construct segmentation and then enumerates all the possible long words in the dictionary. dict, hmm and qmax should be provided when initializing jiebaR worker.

There is a symbol <= for this function.

text <- '你要明白,这仅仅是一个测试文本'
queryseg <- worker('query') #索引模型(QuerySegment)
queryseg[text]
# [1] "你"   "要"   "明白" "这"   "仅仅" "是"   "一个" "测试" "文本"

2.5 worker()函数的定义

worker(type = "mix", dict = DICTPATH, hmm = HMMPATH, user = USERPATH,
  idf = IDFPATH, stop_word = STOPPATH, write = T, qmax = 20, topn = 5,
  encoding = "UTF-8", detect = T, symbol = F, lines = 1e+05,
  output = NULL, bylines = F, user_weight = "max")

# 参数列表:
# 
# type, 引擎类型
# dict, 系统词典
# hmm, HMM模型路径
# user, 用户词典
# idf, IDF词典
# stop_word, 关键词用停止词库
# write, 是否将文件分词结果写入文件,默认FALSE
# qmax, 最大成词的字符数,默认20个字符
# topn, 关键词数,默认5个
# encoding, 输入文件的编码,默认UTF-8
# detect, 是否编码检查,默认TRUE
# symbol, 是否保留符号,默认FALSE
# lines, 每次读取文件的最大行数,用于控制读取文件的长度。大文件则会分次读取。
# output, 输出路径
# bylines, 按行输出
# user_weight, 用户权重

我们在调用worker()时,就加载了分词引擎,可以打印出来,查看分词的引擎的配置。

wk = worker()
wk
# Worker Type:  Jieba Segment
# 
# Default Method  :  mix
# Detect Encoding :  TRUE
# Default Encoding:  UTF-8
# Keep Symbols    :  FALSE
# Output Path     :  
# Write File      :  TRUE
# By Lines        :  FALSE
# Max Word Length :  20
# Max Read Lines  :  1e+05
# 
# Fixed Model Components:  
# 
# $dict
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/jieba.dict.utf8"
# 
# $user
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/user.dict.utf8"
# 
# $hmm
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/hmm_model.utf8"
# 
# $stop_word
# NULL
# 
# $user_weight
# [1] "max"
# 
# $timestamp
# [1] 1500226127
# 
# $default $detect $encoding $symbol $output $write $lines $bylines can be reset.

如果我们想改变分词引擎的配置项,可以在调用worker()创建分词引擎时,也可以通过wk$XX来进行设置。如果想了解wk是什么类型的对象,我们通过pryr包的otype的函数来检查wk对象的类型。关于pryr包的详细使用,请参考文章撬动R内核的高级工具包pryr

# 加载 pryr包
library(pryr)
otype(wk)  # 面向对象的类型检查
# [1] "S3"

class(wk)  # 查看class是属性
# [1] "jiebar"  "segment" "jieba" 

2.6 apply_list


cutter = worker()
apply_list(list("this is test", "that is not test"), cutter)
apply_list(list("this is test", list("that is not test","ab c")), cutter)

2.7 file_coding


file_coding(file)
filecoding(file)

Dictionary Path


DICTPATH;HMMPATH;USERPATH;IDFPATH;STOPPATH;DICTPATH
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/jieba.dict.utf8"
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/hmm_model.utf8"
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/user.dict.utf8"
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/idf.utf8"
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/stop_words.utf8"

# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/jieba.dict.utf8"

HMMPATH
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/hmm_model.utf8"

USERPATH
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/user.dict.utf8"

IDFPATH
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/idf.utf8"

STOPPATH
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/stop_words.utf8"

2.7 Hamming distance of words


distance(codel, coder, jiebar)
vector_distance(codel, coder, jiebar)


words = "hello world"
simhasher = worker("simhash", topn = 1)
simhasher <= words
distance("hello world" , "hello world!" , simhasher)
vector_distance(c("hello","world") , c("hello", "world","!") , simhasher)

3. 标注词性-Speech Tagging

Speech Tagging worker uses MixSegment model to cut word and tag each word after segmentation using labels compatible with ictclas.

dict, hmm and user should be provided when initializing jiebaR worker.

可以使用<=.tagger 或者tag 来进行分词和词性标注,词性标注使用混合模型模型分词,标注采用和 ictclas 兼容的标记法。

jiebaR包关于词典词性标记,采用ictclas的标记方法。ICTCLAS 汉语词性标注集。

代码 名称 帮助记忆的诠释 Ag 形语素 形容词性语素。形容词代码为a,语素代码g前面置以A。 a 形容词 取英语形容词adjective的第1个字母。 ad 副形词 直接作状语的形容词。形容词代码a和副词代码d并在一起。 an 名形词 具有名词功能的形容词。形容词代码a和名词代码n并在一起。 b 区别词 取汉字“别”的声母。 c 连词 取英语连词conjunction的第1个字母。 Dg 副语素 副词性语素。副词代码为d,语素代码g前面置以D。 d 副词 取adverb的第2个字母,因其第1个字母已用于形容词。 e 叹词 取英语叹词exclamation的第1个字母。 f 方位词 取汉字“方”的声母。 g 语素 绝大多数语素都能作为合成词的“词根”,取汉字“根”的声母。 h 前接成分 取英语head的第1个字母。 i 成语 取英语成语idiom的第1个字母。 j 简称略语 取汉字“简”的声母。 k 后接成分
l 习用语 习用语尚未成为成语,有点“临时性”,取“临”的声母。 m 数词 取英语numeral的第3个字母,n,u已有他用。 Ng 名语素 名词性语素。名词代码为n,语素代码g前面置以N。 n 名词 取英语名词noun的第1个字母。 nr 人名 名词代码n和“人(ren)”的声母并在一起。 ns 地名 名词代码n和处所词代码s并在一起。 nt 机构团体 “团”的声母为t,名词代码n和t并在一起。 nz 其他专名 “专”的声母的第1个字母为z,名词代码n和z并在一起。 o 拟声词 取英语拟声词onomatopoeia的第1个字母。 p 介词 取英语介词prepositional的第1个字母。 q 量词 取英语quantity的第1个字母。 r 代词 取英语代词pronoun的第2个字母,因p已用于介词。 s 处所词 取英语space的第1个字母。 Tg 时语素 时间词性语素。时间词代码为t,在语素的代码g前面置以T。 t 时间词 取英语time的第1个字母。 u 助词 取英语助词auxiliary 的第2个字母,因a已用于形容词。 Vg 动语素 动词性语素。动词代码为v。在语素的代码g前面置以V。 v 动词 取英语动词verb的第一个字母。 vd 副动词 直接作状语的动词。动词和副词的代码并在一起。 vn 名动词 指具有名词功能的动词。动词和名词的代码并在一起。 w 标点符号
x 非语素字 非语素字只是一个符号,字母x通常用于代表未知数、符号。 y 语气词 取汉字“语”的声母。 z 状态词 取汉字“状”的声母的前一个字母。

text <- '你要明白,这仅仅是一个测试文本'
tagseg <- worker('tag')

tagseg[text]
  #    r      v     nr      r      d      v      m     vn      n 
  # "你"   "要" "明白"   "这" "仅仅"   "是" "一个" "测试" "文本" 

# same results
tagging(text, tagseg)
  #    r      v     nr      r      d      v      m     vn      n 
  # "你"   "要" "明白"   "这" "仅仅"   "是" "一个" "测试" "文本" 
# another example
cutter = worker(type = "tag")
cutter_words <- cutter <= "我爱北京天安门"
cutter_words
#        r        v       ns       ns 
#      "我"     "爱"     "北京"     "天安门" 
# # "我"  反身代词; "爱" 动词; "北京" 名词

4. 关键词提取-Keyword Extraction

关键词提取是文本处理非常重要的一个环节,一个经典算法是TF-IDF算法。其中,TF(Term Frequency)代表词频,IDF(Inverse Document Frequency)表示逆文档频率。如果某个词在文章中多次出现,而且不是停止词,那么它很可能就反应了这段文章的特性,这就是我们要找的关键词。再通过IDF来算出每个词的权重,不常见的词出现的频率越高,则权重越大。计算TF-IDF的公式为:TF-IDF = TF(词频) * 逆文档频率(IDF)

对文档中每个词计算TF-IDF的值,把结果从大到小排序,就得到了这篇文档的关键性排序列表。关于IF-IDF的解释,参考了文章TF-IDF与余弦相似性的应用(一):自动提取关键词。

jiebaR包的关键词提取提取的实现,也是使用了TF-IDF的算法。在安装目录中的idf.utf8文件,为IDF的语料库。查看idf.utf8内容。

show_dictpath()
# D:/Program Files/R/R-3.3.3/library/jiebaRD/dict
dir(show_dictpath())
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict"
#  [1] "backup.rda"      "hmm_model.utf8"  "hmm_model.zip"  
#  [4] "idf.utf8"        "idf.zip"         "jieba.dict.utf8"
#  [7] "jieba.dict.zip"  "model.rda"       "README.md"      
# [10] "stop_words.utf8" "user.dict.utf8" 

scan(file="D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/idf.utf8",
      what='character',nlines=50,sep='\n',
      encoding='UTF-8',fileEncoding='UTF-8')
# invalid input found on input connection 'D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/idf.utf8'Read 1 item

#delete fileEncoding='UTF-8'
scan(file="D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/idf.utf8",
      what='character',nlines=50,sep='\n',
      encoding='UTF-8')

idf.utf8文件每一行有2列,第一列是词项,第二列为权重。然后,我通过计算文档的词频(TF),与语料库的IDF值相乘,就可以得到TF-IDF值,从而提取文档的关键词。

比如,我们对下面的文本内容进行关键词的提取。

wk = worker()
segment<-wk["R的极客理想系列文章,涵盖了R的思想,使用,工具,创新等的一系列要点,以我个人的学习和体验去诠释R的强大。"]

# 计算词频
freq(segment)

# 取TF-IDF的前5的关键词
keys = worker("keywords",topn=5)

# 计算关键词
vector_keywords(segment,keys)
# 11.7392 8.97342 8.23425  8.2137 7.43298 
 # "极客"  "诠释"  "要点"  "涵盖"  "体验" 

使用jiebaR包处理分词确实简单,几行的代码就能实现分词的各种算法操作。有了这个工具,我们就可以文档中,发现各种语言规则进行文本挖掘了

Keyword Extraction worker uses MixSegment model to cut word and use TF-IDF algorithm to find the keywords. dict ,hmm, idf, stop_word and topn should be provided when initializing jiebaR worker.

关键词提取所使用逆向文件频率(IDF)文本语料库可以切换成自定义语料库的路径,使用方法与分词类似。topn参数为关键词的个数。

text <- '你要明白,这仅仅是一个测试文本'

keys = worker('keywords', topn = 2) #参数topn表示提取排在最前的关键词个数
keys <= text
# 8.94485 7.14724 
#  "文本"  "测试" 

#同样的,也可以对文件进行关键词提取
keys <= "一个文件路径.txt"
keys <= "E:/03-Download/Github/红楼梦-UTF-8.txt" 
# 26138.9 9266.45 
#  "宝玉"  "贾母" 

## another example
cutter = worker(type = "keywords", topn = 2)
cutter_words <- cutter <= "我爱北京天安门"
cutter_words
  # 8.9954   4.6674 
  # "天安门"   "北京"
# 根据IDF算法,"我" "爱" 的逆文本频率过低,topn=2,就被过滤掉了

5. simhash计算-分词-关键词提取-文本去重

Simhash worker uses the keyword extraction worker to find the keywords and uses simhash algorithm to compute simhash. dict hmm, idf and stop_word should be provided when initializing jiebaR worker.

对中文文档计算出对应的simhash值。simhash是谷歌用来进行文本去重的算法,现在广泛应用在文本处理中。Simhash引擎先进行分词和关键词提取,后计算Simhash值和海明距离。

text <- '你要明白,这仅仅是一个测试文本'

simhasher = worker("simhash", topn = 2)
simhasher <= text
# $simhash
# [1] "10014870797707624170"
# 
# $keyword
# 8.94485 7.14724 
#  "文本"  "测试" 

#看看红楼梦的关键词是什么
# "宝玉"   "贾母"   "凤姐" "王夫人" "老太太"   "太太" 
simhasher = worker("simhash", topn = 10)
simhasher <= "E:/03-Download/Github/红楼梦-UTF-8.txt"
# $simhash
# [1] "14760579568630162737"
# 
# $keyword
#  26138.9  9266.45  8269.94  7909.09  7020.27  5631.21 
#   "宝玉"   "贾母"   "凤姐" "王夫人" "老太太"   "太太" 
#   5603.1  5512.09  5451.25  5062.17 
#   "姑娘"   "贾琏"   "奶奶"   "众人" 

# another example
cutter = worker(type = "simhash", topn = 2)
cutter_words <- cutter <= "我爱北京天安门"
cutter_words
# $simhash
# [1] "4352745221754575559"
# 
# $keyword
#   8.9954   4.6674 
# "天安门"   "北京" 

6. 快速模式-Not Recomended

无需使用函数worker(),使用默认参数启动引擎,并立即进行分词。使用qseg(quick segmentation),使用默认分词模式,自动建立分词引擎,类似于ggplot2包里面的qplot函数。

text <- '你要明白,这仅仅是一个测试文本'
qseg <= text
# Quick mode is depreciated, and is scheduled to be remove in v0.11.0. If you want to keep this feature, please submit a issue on GitHub page to let me know.
# [1] "你"   "要"   "明白" "这"   "仅仅" "是"   "一个" "测试" "文本"

worker('mix') #查看worker('mix')参数配置
qseg #查看qseg参数配置,与上面一样都得到以下结果
# 实际上,第一次运行时,会启动默认引擎 quick_worker,相当于先运行了一遍代码:qseg = worker('mix')

# Worker Type:  Jieba Segment
# 
# Default Method  :  mix
# Detect Encoding :  TRUE
# Default Encoding:  UTF-8
# Keep Symbols    :  FALSE
# Output Path     :  
# Write File      :  TRUE
# By Lines        :  FALSE
# Max Word Length :  20
# Max Read Lines  :  1e+05
# 
# Fixed Model Components:  
# 
# $dict
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/jieba.dict.utf8"
# 
# $user
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/user.dict.utf8"
# 
# $hmm
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/hmm_model.utf8"
# 
# $stop_word
# NULL
# 
# $user_weight
# [1] "max"
# 
# $timestamp
# [1] 1500192552
# 
# $default $detect $encoding $symbol $output $write $lines $bylines can be reset.

可以通过qseg$重设模型参数,重设模型参数将会修改以后每次默认启动的默认参数;

如果只是想临时修改模型参数,可以使用非快速模式的修改方式quick_worker$。

# 重设模型参数的同时,重新启动引擎;下次重新启动包时,现有的设置不会改变。
qseg$type = "mp" 

# 临时修改,下次重新启动包时,会恢复原来的默认设置。
quick_worker$detect = T 

# 获得当前快速模式的默认参数
get_qsegmodel()         

7. 加载词库

常用的分词包有两种加载词库的方法,就是加载包时读取默认的词典和数据模型,或者在分词前加载词典和模型数据。在早期的版本中,jiebaR也使用过这两种方式进行加载。

第一种方式,就像一个铁笼子,加载包时一次性加载了词库,封装在一起。

第二种方式灵活,可以动态地加载词库和模型数据,适时进行修改,但是每次分词前,加载词库都十分耗费时间,对于小的任务不合适。

有了Rcpp Modules,jiebaR可以把C++中的分词类映射到R语言中的RC类,把这样原本C++中静态的类的操作,带到了R里面,可以动态地运行。在jiebaR里,你可以动态地生成分词器,使用不同的分词器,对不同类型的文本进行操作,分词就像切菜时选不同的菜刀一样。

对于分词的结果好坏的关键因素是词典,jiebaR默认有配置标准的词典。对于我们的使用来说,不同行业或不同的文字类型,最好用专门的分词词典。在jiebaR中通过show_dictpath()函数可以查看默认的标准词典,可以通过上一小节介绍的配置项,来指定我们自己的词典。日常对话的常用词典,比如搜狗输入法的词库。

library(jiebaR)加载包时,没有启动任何分词引擎,启动引擎很简单,就是一句赋值语句就可以了。cutter <- worker()

软件默认设定非常重要,jiebaR默认参数为绝大多数任务调整到了最好的状态(哈哈,我的自我感觉)。初始化分词简单,分词就更简单了。为了让大家少一些待在电脑前的时间,多一些配家人和朋友的时间,少敲一些键盘,jiebaR重载了<=这个不太常用的符号,当然还有==,你在项目README里可以看到。分词就是一个类似赋值的过程,足够简单粗暴:

# start segmentation engine
# import package in a safe way
if(!suppressWarnings(require('jiebaR'))) {
  install.packages('jiebaR')
  require('jiebaR')
}

cutter <- worker()

# cut what
cutter <= "江州市长江大桥,参加了长江大桥的通车仪式。"
# perfect results
# [1] "江州"     "市长"     "江大桥"   "参加"     "了"      
# [6] "长江大桥" "的"       "通车"     "仪式"   

# 或者Pipe一个文件路径
cutter <= "weibo.txt"

当然,如果你喜欢打字,也可以使用segment()函数。正如之前说的,可以同时初始化和使用多个分词器。可以添加一些参数来初始化,可用参数列表很长很长,但是一般你不会全用到它们,具体可以参考帮助文档?worker():

cutter2 <- worker( user = "e:/Path for User defined Dictionary") ### 初始化第二个引擎

ShowDictPath() 
 ### 可以显示默认词典路径


# 查看默认的词库位置
show_dictpath()
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict"

# 查看目录
dir(show_dictpath())
# [1] "D:/Program Files/R/R-3.3.3/library/jiebaRD/dict"
#  [1] "backup.rda"      "hmm_model.utf8"  "hmm_model.zip"  
#  [4] "idf.utf8"        "idf.zip"         "jieba.dict.utf8"
#  [7] "jieba.dict.zip"  "model.rda"       "README.md"      
# [10] "stop_words.utf8" "user.dict.utf8" 

# 看到词典目录中,包括了多个文件。
# 
# jieba.dict.utf8, 系统词典文件,最大概率法,utf8编码的
# hmm_model.utf8, 系统词典文件,隐式马尔科夫模型,utf8编码的
# user.dict.utf8, 用户词典文件,utf8编码的
# stop_words.utf8,停止词文件,utf8编码的
# idf.utf8,IDF语料库,utf8编码的
# jieba.dict.zip,jieba.dict.utf8的压缩包
# hmm_model.zip,hmm_model.utf8的压缩包
# idf.zip,idf.utf8的压缩包
# backup.rda,无注释
# model.rda,无注释
# README.md,说明文件

打开系统词典文件jieba.dict.utf8,并打印前50行

#delete fileEncoding='UTF-8'
scan(file="D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/jieba.dict.utf8",
           what=character(),nlines=50,sep='\n',
           encoding='utf-8')
# 系统词典每一行都有三列,并以空格分割,第一列为词项,第二列为词频,第三列为词性标记。

#delete fileEncoding='UTF-8'
scan(file="D:/Program Files/R/R-3.3.3/library/jiebaRD/dict/user.dict.utf8",
      what=character(),nlines=50,sep='\n',
      encoding='utf-8')
# 用户词典第一行有二列,,第一列为词项,第二列为词性标记,没有词频的列。用户词典默认词频为系统词库中的最大词频。

这时R的环境里同时有两个加载了不同词库的分词引擎。

如果需要了解这两个不同的引擎的区别只需要print一下就可以了。

每个worker都有一些参数设置,如cutter中的$detect参数决定了引擎是否自动判断输入文件的编码,在引擎加载时可以通过worker(detect = F )进行参数设置,也可以在加载后通过cutter$detect = F进行设置。其实 worker()函数返回的是一个环境(environment),里面封装了真正的分词引擎,你可以通过cutter$worker来查看真正的“引擎”。cutter$worker <pointer: 0x0805b990>

cutter$workercutter都是环境,在传递时是传址,而不是传值,效率是比较高的。jiebaR的分词速度是其他R语言分词包的5-20倍。

分词结束后,对于不需要的引擎只需要用rm()进行删除,R有自动的垃圾回收机制,为你解决内存管理的后顾之忧。

分词已经分好,统计分析才是最重要的任务。剃刀已经磨砺,接下来就可以用R来处理中文字符了。

mixseg2 = worker(type  = "mix", 
                 dict = "/home/rstudio/R/x86_64-pc-linux-gnu-library/3.1/jiebaRD/dict/jieba.dict.utf8", 
                 hmm   = "/home/rstudio/R/x86_64-pc-linux-gnu-library/3.1/jiebaRD/dict/hmm_model.utf8", 
                 user  = "/home/rstudio/R/x86_64-pc-linux-gnu-library/3.1/jiebaRD/dict/user.dict.utf8", 
                 detect=T,  symbol = F, 
                 lines = 1e+05, output = NULL
                 )  
# detect 自动检查文件编码,lines一次读取文件的行数


#修改 user.utf8
# R语言
# R的极客理想
# 大数据
# 数据

#加载user.utf8
# wk = worker(user='user.utf8')

# 输出worker的设置
mixseg2
#输出结果如下:略

可以自定义用户词库

install.packages("devtools")
install.packages("stringi")
install.packages("pbapply")
install.packages("Rcpp")
install.packages("RcppProgress")
library(devtools)
install_github("qinwf/cidian")


# decode scel dictionary
decode_scel(scel = "细胞词库路径", output = "输出文件路径", cpp =  TRUE)

decode_scel(scel = "细胞词库路径",output = "输出文件路径",cpp =  FALSE, progress = TRUE)

# 输出调试信息
decode_scel(scel = "细胞词库路径", output = "输出文件路径", cpp = FALSE, progress = TRUE, rdebug = TRUE)

# system dict with frequency
decode_scel("细胞词库路径", output = "输出文件路径", sysdict_freq = 1)

# load/add user dictionary
## 读取用户词典

load_user_dict(filePath = "用户词典路径", default_tag = "默认标记")

## 读取系统词典
load_sys_dict(filePath = "系统词典路径")

## 增加用户词典词

add_user_words(dict = "load_user_dict 读取的词典", words = "UTF-8 编码文本向量", tags = "标记")

## 增加系统词典词

add_sys_words(dict = "load_sys_dict 读取的词典", words = "UTF-8 编码文本向量", freq = "词频", tags = "标记")

## 删除词典词

remove_words(dict = "load_user_dict 或 load_sys_dict 读取的词典", words = "UTF-8 编码文本向量")

## 写入

write_dict(dict = "load_user_dict 或 load_sys_dict 读取的词典", output = "输出路径")

(userd = load_user_dict(jiebaR::USERPATH))

userd = add_user_words(userd, enc2utf8("测试"), "v")

write_dict(userd, jiebaR::USERPATH)

(userd = load_user_dict(jiebaR::USERPATH))
userd = remove_words(userd, enc2utf8(c("测试","蓝翔")))

write_dict(userd, jiebaR::USERPATH)

(userd = load_user_dict(jiebaR::USERPATH))


ShowDictPath()  # 显示词典路径
EditDict()      # 编辑用户词典
?EditDict()     # 打开帮助系统

# Usage 使用方法
edit_dict(name = "user") # 这个方法过时了
EditDict(name = "user") 

# Arguments 参数
# name    
# the name of dictionary including user, system, stop_word.

搜狗词典

在实际使用中,jiebaR默认提供的用户词典只有5个单词,太简单了,肯定是不够用的。我们可以用搜狗词典,来丰富用户自己的词库。接下来,让我们配置搜狗词典。你需要安装一个搜狗输入法,具体的安装过程不再解释。

我安装的是搜狗五笔输入法,找到搜狗的安装目录,并找到词典文件。我的搜狗词典,在下面的安装位置。

C:\Program Files (x86)\SogouWBInput\2.1.0.1288\scd\17960.scel

把17960.scel文件复制到自己的项目目录里,用文本编辑器打开文件,发现是二进制的。那么我需要用工具进行转换,把二进制的词典转成我们可以使用的文本文件。jiebaR包的作者,同时开发了一个cidian项目,可以转换搜狗的词典,那么我们只需要安装cidian包即可。

安装cidian项目

install.packages("devtools")
install.packages("stringi")
install.packages("pbapply")
install.packages("Rcpp")
install.packages("RcppProgress")
library(devtools)
install_github("qinwf/cidian")
library(cidian)

# 转换二进制词典到文本文件。
# 转换
decode_scel(scel = "./17960.scel",cpp = TRUE)
# output file: ./17960.scel_2016-07-21_00_22_11.dict

# 查看生成的词典文件
#delete fileEncoding='UTF-8'
scan(file="./17960.scel_2016-07-21_00_22_11.dict",
      what=character(),nlines=50,sep='\n',
      encoding='utf-8')

接下来,直接把搜狗词典配置到我们的分词库中,就可以直接使用了。

把搜狗词典文件改名,从17960.scel_2016-07-21_00_22_11.dictuser.dict.utf8,然后替换D:\tool\R-3.2.3\library\jiebaRD\dict目录下面的user.dict.utf8。这样默认的用户词典,就是搜狗词典了。很酷吧!

停止词过滤

停止词就是分词过程中,我们不需要作为结果的词,像英文的语句中有很多的a,the,or,and等,中文语言中也有很多,比如 的,地,得,我,你,他。这些词因为使用频率过高,会大量出现在一段文本中,对于分词后的结果,在统计词频的时候会增加很多的噪音,所以我们通常都会将这些词进行过滤。

在jiebaR中,过滤停止词有2种方法,一种是通过配置stop_word文件,另一种是使用filter_segment()函数。

首先我们先来看,通过配置stop_word文件的方法。新建一个stop_word.txt文件。

# 我
# 我是


# 加载分词引擎,并配置停止词过滤。
wk = worker(stop_word='stop_word.txt')
segment<-wk["我是《R的极客理想》图书作者"]
segment
# [1] "R"    "的"   "极客" "理想" "图书" "作者"

上面的文本,我们把“我是”通过停止词进行了过滤。如果还想过滤“作者”一词,可以动态的调用filter_segment()函数

filter <-c ("作者")
filter_segment(segment,filter)
# [1] "R"    "的"   "极客" "理想" "图书"

8. 词云图-word cloud

8.1 红楼梦

library(jiebaR)
library(wordcloud2)

#读入数据分隔符是‘\n’,字符编码是‘UTF-8’,what=''表示以字符串类型读入
f <- scan('E:/03-Download/Github/红楼梦-UTF-8.txt',sep='\n',what='',encoding="UTF-8")

# 初始化
mixseg <- worker()
segment(head(f), mixseg)
#  [1] "红楼梦"   "曹雪芹"   "著"       "手机"     "电子书"  
#  [6] "大学生"   "小说网"   "Txt"      "版"       "阅读"    
# [11] "阅读"     "作品"     "更"       "多"       "请"      
# [16] "访问"     "http"     "www"      "dxsxs"    "com"     
# [21] "书籍"     "介绍"     "中国"     "古代"     "四大名著"
# [26] "之一"     "章节"     "内容"     "开始"     "上卷"    
# [31] "第一回"   "甄士隐"   "梦幻"     "识通灵"   " "      
# [36] "贾雨村"   "风尘"     "怀"       "闺秀"    

# Data Manupulation
# 全文分词
seg <- segment(f, mixseg)
# 查看分词后的向量的长度
length(seg)
# [1] 470995

# 查看分词后的向量的前50的词频统计
sort(table(seg), decreasing = T)[1:50]
#     了     的     我     他     你     也     是     说       
#  17775  13505   7268   6077   5810   5745   5624   5372   5158 
#     又     道   宝玉     着     来     这     人     不     去 
#   4955   4425   3748   3651   3254   3002   3001   2956   2950 
#     便     在     有     都     就     叫     那     听     贾 
#   2927   2784   2597   2533   2398   1899   1818   1748   1646 
#   什么     见     等     要     还   一个   笑道     好     呢 
#   1613   1581   1565   1521   1505   1450   1447   1442   1412 
#     只     和   我们   那里     上     到     儿     倒     因 
#   1295   1222   1221   1174   1128   1122   1107   1085   1042 
# 王夫人     才   你们   如今     们 
#   1011   1010   1009    999    985

#单个的字太多,没有意义

seg <- seg[nchar(seg)>1 & nchar(seg) < 7] #去除字符长度小于2的词语,&<7
sort(table(seg), decreasing = T)[1:50]
# 宝玉   什么   一个   笑道   我们   那里 王夫人   你们   如今 
#   3748   1613   1450   1447   1221   1174   1011   1009    999 
#   说道   知道 老太太   起来   姑娘   这里   出来   他们   众人 
#    973    967    966    944    941    935    922    895    870 
#   自己   一面   太太   只见   怎么   奶奶   两个   没有   不是 
#    836    829    825    789    777    772    769    761    729 
#   不知   这个   听见   这样   贾母   进来   咱们   告诉   就是 
#    708    697    689    646    632    632    605    602    601 
#   东西   平儿   回来   只是   大家   老爷   只得   丫头   这些 
#    599    588    566    544    543    540    531    509    504 
#   不敢   凤姐   出去 凤姐儿   所以 
#    496    492    483    470    466 

#seg <- table(seg) 统计词频

seg <- seg[!grepl('[0-9]+',names(seg))] #去除数字
length(seg) #查看处理完后剩余的词数


#降序排序,并提取出现次数最多的前100个词语, 查看100个词频最高的
Top100 <- sort(table(seg), decreasing = TRUE)[1:100];Top100
# 宝玉   什么   一个   笑道   我们   那里 王夫人   你们   如今 
#   3748   1613   1450   1447   1221   1174   1011   1009    999 
#   说道   知道 老太太   起来   姑娘   这里   出来   他们   众人 
#    973    967    966    944    941    935    922    895    870 
#   自己   一面   太太   只见   怎么   奶奶   两个   没有   不是 
#    836    829    825    789    777    772    769    761    729 
#   不知   这个   听见   这样   贾母   进来   咱们   告诉   就是 
#    708    697    689    646    632    632    605    602    601 
#   东西   平儿   回来   只是   大家   老爷   只得   丫头   这些 
#    599    588    566    544    543    540    531    509    504 
#   不敢   凤姐   出去 凤姐儿   所以 薛姨妈   不过   的话   不好 
#    496    492    483    470    466    453    448    445    444 
#   姐姐   探春   鸳鸯   一时   不能   过来   去了   心里   二爷 
#    442    432    425    421    420    420    404    402    399 
#   如此   今日   银子   几个   答应   二人   宝钗   还有   只管 
#    376    370    366    358    358    356    356    350    343 
#   这么   黛玉   说话   一回   晴雯   那边   湘云   外头   这话 
#    343    342    340    338    332    330    324    317    317 
#   贾琏   打发   自然   袭人   今儿   罢了   屋里 刘姥姥   那些 
#    313    310    306    298    297    296    295    293    293 
#   听说 小丫头 邢夫人 林黛玉   如何   问道   看见   妹妹   人家 
#    290    287    284    280    279    277    274    272    269 
#   不用 
#    264 



# Top100做词云
jpeg("E:/03-Download/Github/RedChamber_Top100.jpg", width = 500, height = 500)
par(bg = "black")
wordcloud2(Top100, size =1, color = 'random-light', shape = 'cardioid')
# ?wordcloud2()
dev.off()

8.2 wordcloud2 example demo

library("wordcloud2")
wordcloud2(demoFreq, size = 1,shape = 'star')

wordcloud2(demoFreq, size = 2, minRotation = -pi/2, maxRotation = -pi/2)

wordcloud2(demoFreq, size = 2, minRotation = -pi/6, maxRotation = -pi/6,
  rotateRatio = 1)

wordcloud2(demoFreqC, size = 2, fontFamily = "微软雅黑",
           color = "random-light", backgroundColor = "grey")

## Sys.setlocale("LC_CTYPE","eng")
wordcloud2(demoFreqC, size = 2, fontFamily = "微软雅黑",
           color = "random-light", backgroundColor = "grey")
LS0tDQp0aXRsZTogIlJfVGV4dF9NaW5pbmdfQ2hpbmVzZV9Xb3JkX1NlZ21lbnRhdGlvbiINCmF1dGhvcjogIlN0b25lX0hvdSINCmRhdGU6ICIyMDE35bm0N+aciDE35pelIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQotLS0NCg0KIyDkuK3mlofliIbor43lrabkuaDnrJTorrAgUl9UZXh0X01pbmluZ19DaGluZXNlX1dvcmRfU2VnbWVudGF0aW9uDQoNCj4gW2h0dHA6Ly9ibG9nLmZlbnMubWUvci13b3JkLWppZWJhci9dKGh0dHA6Ly9ibG9nLmZlbnMubWUvci13b3JkLWppZWJhci8pDQoNCj4gW2ppZWJhUuS4reaWh+WIhuivjeW/q+mAn+WFpemXqF0oaHR0cDovL2Jsb2cuY3Nkbi5uZXQvc29uZ3poaWxpYW4yMi9hcnRpY2xlL2RldGFpbHMvNDkyNTA0ODkpDQoNCuaWh+acrOaMluaOmOaYr+aVsOaNruaMluaOmOS4reS4gOS4qumdnuW4uOmHjeimgeeahOmDqOWIhu+8jOaciemdnuW4uOW5v+mYlOeahOS9v+eUqOWcuuaZr++8jOavlOWmguaIkeS7rOWPr+S7peWvueaWsOmXu+S6i+S7tui/m+ihjOWIhuaekO+8jOS6huino+WbveWutuWkp+S6i++8m+S5n+WPr+S7peWvueW+ruWNmuS/oeaBr+i/m+ihjOWIhuaekO+8jOmAmui/h+ekvuS6pOiIhuaDheeci+eci+Wkp+WutueahOWFs+azqOeCueOAgumAmui/h+aWh+acrOaMluaOmOaJvuWIsOaWh+eroOS4reeahOmakOiXj+S/oeaBr++8jOWvueaWh+eroOeahOe7k+aehOi/m+ihjOWIhuaekO+8jOWIpOaWreaYr+S4jeaYr+WQjOS4gOS4quS9nOiAheWGmeaWh+eroO+8m+WQjOaXtuWPr+S7peWvuemCruS7tuWIhuaekO+8jOe7k+WQiGJheWVz566X5rOV5Yik5pat5ZOq5Lqb5piv5Z6D5Zy+6YKu5Lu277yM5ZOq5Lqb5piv5pyJ55So55qE6YKu5Lu244CCDQoNCuaWh+acrOaMluaOmOeahOesrOS4gOatpe+8jOWwseaYr+imgei/m+ihjOWIhuivje+8jOWIhuivjeWwhuebtOaOpeW9seWTjeaWh+acrOaMluaOmOeahOaViOaenOOAglLor63oqIDlnKjliIbor43mlrnpnaLmnInlvojlpb3nmoTmlK/mjIHvvIzmjqXkuIvmnaXlsLHnu5nlpKflrrbku4vnu43kuIDkuKrkuI3plJnnmoRS6K+t6KiA5Lit5paH5YiG6K+N5YyF4oCc57uT5be05YiG6K+N4oCdKGppZWJhUinjgIINCg0KIyMgMS4g566A5LuL5LiO5a6J6KOFLUppZWJhUiBJbnRyb2R1Y3Rpb24gJiBJbnN0YWxsYXRpb24NCg0KIyMjIDEuMSDnroDku4sNCg0KSmllYmFSIGlzIGEgcGFja2FnZSBmb3IgQ2hpbmVzZSB0ZXh0IHNlZ21lbnRhdGlvbiwga2V5d29yZCBleHRyYWN0aW9uIGFuZCBzcGVlY2ggdGFnZ2luZy4g57uT5be05YiG6K+NKGppZWJhUinvvIzmmK/kuIDmrL7pq5jmlYjnmoRS6K+t6KiA5Lit5paH5YiG6K+N5YyF77yM5bqV5bGC5L2/55So55qE5pivQysr77yM6YCa6L+HUmNwcOi/m+ihjOiwg+eUqOW+iOmrmOaViOOAgue7k+W3tOWIhuivjeWfuuS6jk1JVOWNj+iuru+8jOWwseaYr+WFjei0ueWSjOW8gOa6kOeahO+8jOaEn+iwouWbveS6uuS9nOiAheeahOe7meWKm+aUr+aMge+8jOiuqVLnmoTlj6/ku6Xmlrnkvr/nmoTlpITnkIbkuK3mlofmlofmnKzjgIINCg0K5a6Y5pa5R2l0aHVi55qE5Zyw5Z2A77yaW2h0dHBzOi8vZ2l0aHViLmNvbS9xaW53Zi9qaWViYVJdKGh0dHBzOi8vZ2l0aHViLmNvbS9xaW53Zi9qaWViYVIpDQoNCmBgYHtyIGluc3RhbGwgamllYmFSfQ0KIyBJbnN0YWxsIHRoZSBsYXRlc3QgZGV2ZWxvcG1lbnQgdmVyc2lvbiBmcm9tIEdpdEh1YjoNCiMg5ZCM5pe26L+Y5Y+v5Lul6YCa6L+HR2l0aHVi5a6J6KOFW+W8gOWPkeeJiF3vvIwNCiMg5bu66K6u5L2/55SoIGdjYyA+PSA0Ljkg57yW6K+R77yMV2luZG93c+mcgOimgeWuieijhSBSdG9vbHMg77yaDQpsaWJyYXJ5KGRldnRvb2xzKQ0KaW5zdGFsbF9naXRodWIoInFpbndmL2ppZWJhUkQiKQ0KaW5zdGFsbF9naXRodWIoInFpbndmL2ppZWJhUiIpDQpsaWJyYXJ5KCJqaWViYVIiKQ0KIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoInFpbndmL2ppZWJhUiIpDQoNCiMgSW5zdGFsbCBmcm9tIENSQU46DQppbnN0YWxsLnBhY2thZ2VzKCJqaWViYVIiKQ0KbGlicmFyeSgiamllYmFSIikNCmBgYA0KDQojIyMgMS4yIOS4iuaJi+ahiOS+iw0KDQpgYGB7ciBleDAxfQ0KIyBpbXBvcnQgcGFja2FnZSBpbiBhIHNhZmUgd2F5DQppZighc3VwcHJlc3NXYXJuaW5ncyhyZXF1aXJlKCdqaWViYVInKSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygnamllYmFSJykNCiAgcmVxdWlyZSgnamllYmFSJykNCn0NCg0KI2luaXRpYWwgDQp3ayA9IHdvcmtlcigpDQoNCndrWyJqaWViYVLmj5Dkvpvkuoblm5vnp43liIbor43mqKHlvI/vvIzlj6/ku6XpgJrov4flh73mlbB3b3JrZXIoKeadpeWIneWni+WMluWIhuivjeW8leaTju+8jOS9v+eUqOWHveaVsHNlZ21lbnQoKei/m+ihjOWIhuivjeOAguWFt+S9k+S9v+eUqD93b3JrZXLmn6XnnIvluK7liqkiXQ0KIyAgWzFdICJqaWViYVIiICAi5o+Q5L6bIiAgICAi5LqGIiAgICAgICLlm5vnp40iICAgDQojICBbNV0gIuWIhiIgICAgICAi6K+N5qih5byPIiAgIuWPr+S7pSIgICAgIumAmui/hyIgICANCiMgIFs5XSAi5Ye95pWwIiAgICAid29ya2VyIiAgIuadpSIgICAgICAi5Yid5aeL5YyWIiANCiMgWzEzXSAi5YiG6K+NIiAgICAi5byV5pOOIiAgICAi5L2/55SoIiAgICAi5Ye95pWwIiAgIA0KIyBbMTddICJzZWdtZW50IiAi6L+b6KGMIiAgICAi5YiG6K+NIiAgICAi5YW35L2TIiAgIA0KIyBbMjFdICLkvb/nlKgiICAgICJ3b3JrZXIiICAi5p+l55yLIiAgICAi5biu5YqpIiAgIA0KDQp3a1si57uT5be05YiG6K+NKGppZWJhUinvvIzmmK/kuIDmrL7pq5jmlYjnmoRS6K+t6KiA5Lit5paH5YiG6K+N5YyFIl0NCiMgIFsxXSAi57uT5be0IiAgICLliIbor40iICAgImppZWJhUiIgIuaYryIgICAgICLkuIDmrL4iDQojICBbNl0gIumrmOaViCIgICAi55qEIiAgICAgIlIiICAgICAgIuivreiogCIgICAi5Lit5paHIg0KIyBbMTFdICLliIbor40iICAgIuWMhSINCmBgYA0KDQpqaWViYVLmj5DkvpvkuoYz56eN5YiG6K+N6K+t5Y+l55qE5YaZ5rOV77yM5L6L5a2Q5LiK6Z2i55qE55SoYFtdYOespuWPt+eahOivreazle+8jOi/mOWPr+S7peS9v+eUqGA8PWDnrKblkIjor63ms5XvvIzmiJbogIXkvb/nlKhgc2VnbWVudCgpYOWHveaVsOOAguiZveeEtuW9ouW8j+S4jeWQjO+8jOS9huaYr+WIhuivjeaViOaenOaYr+S4gOagt+eahOOAgg0KDQpgYGB7ciBleDAyIDMgc3ludGF4fQ0KIyDkvb/nlKg8PeespuWPt+eahOivreazle+8jOWmguS4iw0Kd2sgPD0gJ+S9v+eUqOWwj+S6juetieS6jueahOWPpuS4gOenjeespuWQiOeahOivreazlScNCiMgWzFdICLkvb/nlKgiICLlsI/kuo4iICLnrYnkuo4iICLnmoQiICAgIuWPpiIgICAi5LiA56eNIg0KIyBbN10gIuespuWQiCIgIueahCIgICAi6K+t5rOVIg0KDQojIOS9v+eUqHNlZ21lbnQoKeWHveaVsOeahOivreazle+8jOWmguS4iw0Kc2VnbWVudCggIuS9v+eUqHNlZ21lbnQoKeWHveaVsOeahOivreazle+8jOWmguS4iyIgLCB3ayApDQojIFsxXSAi5L2/55SoIiAgICAic2VnbWVudCIgIuWHveaVsCIgICAgIueahCIgICAgIA0KIyBbNV0gIuivreazlSIgICAgIuWmguS4iyINCmBgYA0KDQrmiJHku6zkuZ/lj6/ku6Xnm7TmjqXlr7nmlofmnKzmlofku7bov5vooYzliIbor43vvIzlnKjlvZPliY3nm67lvZXmlrDlu7rkuIDkuKrmlofmnKzmlofku7ZmaWxlX3NlZ21lbnRhdGlvbi50eHTjgIINCmBgYHtyIGZpbGUgc2VnbWVudGF0aW9ufQ0KIyDlnKjosIPnlKh3b3JrZXIoKeWHveaVsOaXtu+8jOaIkeS7rOWunumZheaYr+WcqOWKoOi9vWppZWJhUuW6k+eahOWIhuivjeW8leaTjuOAgmppZWJhUuW6k+aPkOS+m+S6hjfnp43liIbor43lvJXmk47jgIINCiMgDQojIOa3t+WQiOaooeWeiyhNaXhTZWdtZW50KTrmmK/lm5vkuKrliIbor43lvJXmk47ph4zpnaLliIbor43mlYjmnpzovoPlpb3nmoTnsbvvvIznu5PlroPlkIjkvb/nlKjmnIDlpKfmpoLnjofms5XlkozpmpDlvI/pqazlsJTnp5HlpKvmqKHlnovjgIINCiMg5pyA5aSn5qaC546H5rOVKE1QU2VnbWVudCkgOui0n+i0o+agueaNrlRyaWXmoJHmnoTlu7rmnInlkJHml6Dnjq/lm77lkozov5vooYzliqjmgIHop4TliJLnrpfms5XvvIzmmK/liIbor43nrpfms5XnmoTmoLjlv4PjgIINCiMg6ZqQ5byP6ams5bCU56eR5aSr5qih5Z6LKEhNTVNlZ21lbnQpOuaYr+agueaNruWfuuS6juS6uuawkeaXpeaKpeetieivreaWmeW6k+aehOW7uueahEhNTeaooeWei+adpei/m+ihjOWIhuivje+8jOS4u+imgeeul+azleaAnei3r+aYr+agueaNrihCLEUsTSxTKeWbm+S4queKtuaAgeadpeS7o+ihqOavj+S4quWtl+eahOmakOiXj+eKtuaAgeOAgiBITU3mqKHlnovnlLFkaWN0L2htbV9tb2RlbC51dGY45o+Q5L6b44CC5YiG6K+N566X5rOV5Y2zdml0ZXJiaeeul+azleOAgg0KIyDntKLlvJXmqKHlnosoUXVlcnlTZWdtZW50KTrlhYjkvb/nlKjmt7flkIjmqKHlnovov5vooYzliIfor43vvIzlho3lr7nkuo7liIflh7rmnaXnmoTovoPplb/nmoTor43vvIzmnprkuL7lj6XlrZDkuK3miYDmnInlj6/og73miJDor43nmoTmg4XlhrXvvIzmib7lh7ror43lupPph4zlrZjlnKjjgIINCiMg5qCH6K6w5qih5Z6LKHRhZykNCiMgU2ltaGFzaOaooeWeiyhzaW1oYXNoKQ0KIyDlhbPplK7or43mqKHlnosoa2V5d29kcykNCg0Kd2tbJ0U6LzAzLURvd25sb2FkL0dpdGh1Yi94aWFuZ3hpbmc5OC5naXRodWIuaW8vUl9MZWFybmluZy9maWxlX3NlZ21lbnRhdGlvbi50eHQnXQ0KIyAiRTovMDMtRG93bmxvYWQvR2l0aHViL3hpYW5neGluZzk4LmdpdGh1Yi5pby9SX0xlYXJuaW5nL2ZpbGVfc2VnbWVudGF0aW9uLnNlZ21lbnQuMjAxNy0wNy0xN18wMV80Ml8yMC50eHQiDQpgYGANCg0KDQoNCmppZWJhUuaPkOS+m+S6huWbm+enjeWIhuivjeaooeW8j++8jOWPr+S7pemAmui/h+WHveaVsHdvcmtlcigp5p2l5Yid5aeL5YyW5YiG6K+N5byV5pOO77yM5L2/55So5Ye95pWwc2VnbWVudCgp6L+b6KGM5YiG6K+N44CC5YW35L2T5L2/55SoP3dvcmtlcuafpeeci+W4ruWKqQ0KDQpgYGB7ciBsb2FkIGppZWJhUiBwYWNrYWdlIGV4MX0NCiMgaW1wb3J0IHBhY2thZ2UgaW4gYSBzYWZlIHdheQ0KaWYoIXN1cHByZXNzV2FybmluZ3MocmVxdWlyZSgnamllYmFSJykpKSB7DQogIGluc3RhbGwucGFja2FnZXMoJ2ppZWJhUicpDQogIHJlcXVpcmUoJ2ppZWJhUicpDQp9DQoNCnRleHQgPC0gJ+S9oOimgeaYjueZve+8jOi/meS7heS7heaYr+S4gOS4qua1i+ivleaWh+acrCcNCm1peHNlZyA8LSB3b3JrZXIoKSAj5L2/55So6buY6K6k5Y+C5pWw77yM5re35ZCI5qih5Z6L77yITWl4U2VnbWVudO+8iQ0KDQpzZWdtZW50KHRleHQsIG1peHNlZykNCiPnrYnku7fkuo5taXhzZWdbdGV4dF0NCiPkuZ/nrYnku7fkuo5taXhzZWcgPD0gdGV4dA0KIyBbMV0gIuS9oCIgICAi6KaBIiAgICLmmI7nmb0iICLov5kiICAgIuS7heS7hSIgIuaYryIgICAi5LiA5LiqIiAi5rWL6K+VIiAi5paH5pysIg0KDQojIOebtOaOpei+k+WFpW1peHNlZ+WRveS7pO+8jOWPr+S7peafpeeci+atpHdvcmtlcueahOmFjee9rg0KbWl4c2VnDQojIFdvcmtlciBUeXBlOiAgSmllYmEgU2VnbWVudA0KIyANCiMgRGVmYXVsdCBNZXRob2QgIDogIG1peA0KIyBEZXRlY3QgRW5jb2RpbmcgOiAgVFJVRQ0KIyBEZWZhdWx0IEVuY29kaW5nOiAgVVRGLTgNCiMgS2VlcCBTeW1ib2xzICAgIDogIEZBTFNFDQojIE91dHB1dCBQYXRoICAgICA6ICANCiMgV3JpdGUgRmlsZSAgICAgIDogIFRSVUUNCiMgQnkgTGluZXMgICAgICAgIDogIEZBTFNFDQojIE1heCBXb3JkIExlbmd0aCA6ICAyMA0KIyBNYXggUmVhZCBMaW5lcyAgOiAgMWUrMDUNCiMgDQojIEZpeGVkIE1vZGVsIENvbXBvbmVudHM6ICANCiMgDQojICRkaWN0DQojIFsxXSAiRDovUHJvZ3JhbSBGaWxlcy9SL1ItMy4zLjMvbGlicmFyeS9qaWViYVJEL2RpY3QvamllYmEuZGljdC51dGY4Ig0KIyANCiMgJHVzZXINCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC91c2VyLmRpY3QudXRmOCINCiMgDQojICRobW0NCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC9obW1fbW9kZWwudXRmOCINCiMgDQojICRzdG9wX3dvcmQNCiMgTlVMTA0KIyANCiMgJHVzZXJfd2VpZ2h0DQojIFsxXSAibWF4Ig0KIyANCiMgJHRpbWVzdGFtcA0KIyBbMV0gMTUwMDE3NDI0Mw0KIyANCiMgJGRlZmF1bHQgJGRldGVjdCAkZW5jb2RpbmcgJHN5bWJvbCAkb3V0cHV0ICR3cml0ZSAkbGluZXMgJGJ5bGluZXMgY2FuIGJlIHJlc2V0Lg0KDQojIGFub3RoZXIgZXhhbXBsZSANCmxpYnJhcnkoamllYmFSKQ0KIyAg5o6l5Y+X6buY6K6k5Y+C5pWw77yM5bu656uL5YiG6K+N5byV5pOOIA0KbWl4c2VnID0gd29ya2VyKCkNCiMg55u45b2T5LqO77yaDQojIHdvcmtlciggdHlwZSA9ICJtaXgiLCBkaWN0ID0gImluc3QvZGljdC9qaWViYS5kaWN0LnV0ZjgiLA0KIyAgICAgICAgIGhtbSAgPSAiaW5zdC9kaWN0L2htbV9tb2RlbC51dGY4IiwgICAgIyBITU3mqKHlnovmlbDmja4NCiMgICAgICAgICB1c2VyID0gImluc3QvZGljdC91c2VyLmRpY3QudXRmOCIpICAgICMg55So5oi36Ieq5a6a5LmJ6K+N5bqTDQojIEluaXRpYWxpemUgamllYmFSIHdvcmtlciDliJ3lp4vljJZ3b3JrZXINCg0KIyBUaGlzIGZ1bmN0aW9uIGNhbiBpbml0aWFsaXplIGppZWJhUiB3b3JrZXJzLiANCiMgWW91IGNhbiBpbml0aWFsaXplIGRpZmZlcmVudCBraW5kcyBvZiB3b3JrZXJzIGluY2x1ZGluZyBtaXgsIG1wLCBobW0sIHF1ZXJ5LCB0YWcsIHNpbWhhc2gsIGFuZCBrZXl3b3Jkcy4NCg0KbWl4c2VnIDw9ICLlub/kuJznnIHmt7HlnLPluILogZTpgJoiICAgICMgPD0g5YiG6K+N6L+Q566X56ymDQojIOebuOW9k+S6jnNlZ21lbnTlh73mlbDvvIznnIvotbfmnaXov5jmmK/nlKhzZWdtZW505Ye95pWw6aG655y85LiA5LqbDQoNCnNlZ21lbnQoY29kZT0gIuW5v+S4nOecgea3seWcs+W4guiBlOmAmiIgLCBqaWViYXIgPSBtaXhzZWcpDQojIGNvZGUgQSBDaGluZXNlIHNlbnRlbmNlIG9yIHRoZSBwYXRoIG9mIGEgdGV4dCBmaWxlLg0KIyBqaWViYXIgamllYmFSIFdvcmtlcg0KDQojIOWIhuivjee7k+aenA0KIyBbMV0gIuW5v+S4nOecgSIgIua3seWcs+W4giIgIuiBlOmAmiIgDQoNCm1peHNlZyA8PSAi5L2g55+l6YGT5oiR5LiN55+l6YGTIg0KIyBbMV0gIuS9oCIgICAi55+l6YGTIiAi5oiRIiAgICLkuI0iICAgIuefpemBkyINCg0KbWl4c2VnIDw9ICLmiJHmmKjlpKnlj4LliqDkuoblkIzlrablqZrnpLwiDQojIFsxXSAi5oiRIiAgICLmmKjlpKkiICLlj4LliqAiICLkuoYiICAgIuWQjOWtpiIgIuWpmuekvCINCg0KbWl4c2VnIDw9ICLkvaDnn6XpgZPlkJfmiJHmmKjlpKnlj4LliqDkuoblkIzlrabnmoTlqZrnpLwiDQogIyBbMV0gIuS9oCIgICAi55+l6YGTIiAi5ZCXIiAgICLmiJEiICAgIuaYqOWkqSIgIuWPguWKoCIgIuS6hiIgICAi5ZCM5a2mIg0KICMgWzldICLnmoQiICAgIuWpmuekvCINCiMg5ZG15ZG177ya5YiG6K+N57uT5p6c6L+Y566X5LiN6ZSZDQoNCmBgYA0KDQrlj6/ku6XpgJrov4dS6K+t6KiA5bi455So55qEIGAkYCDnrKblj7fph43orr7kuIDkupt3b3JrZXLnmoTlj4LmlbDorr7nva7vvIzlpoIgV29ya2VyTmFtZSBgJHN5bWJvbCA9IFRg77yM5Zyo6L6T5Ye65Lit5L+d55WZ5qCH54K556ym5Y+344CCDQoNCuS4gOS6m+WPguaVsOWcqOWIneWni+WMlueahOaXtuWAmeW3sue7j+ehruWumu+8jOaXoOazleS/ruaUue+8jCDlj6/ku6XpgJrov4dgV29ya2VyTmFtZSRQcml2YXRlVmFyaWJsZWDmnaXojrflvpfov5nkupvkv6Hmga/jgIINCg0KYGBge3IgZmlsZSBzZWdtZW50YXRpb24gZXgxfQ0KI+iHquWKqOWIpOaWrei+k+WFpeaWh+S7tue8lueggeaooeW8j++8jOm7mOiupOaWh+S7tui+k+WHuuWcqOWQjOebruW9leS4i+OAgg0Kc2VnbWVudCgnRDovdGVzdC50eHQnLCBtaXhzZWcpIA0KI+etieS7t+S6jm1peHNlZ1snRDovdGVzdC50eHQnXQ0KI+S5n+etieS7t+S6jm1peHNlZyA8PSAnRDovdGVzdC50eHQnDQoNCm1peHNlZyA8PSAnRTpcXDAzLURvd25sb2FkXFxHaXRodWJcXOe6oualvOaipi50eHQnDQojR0IyMzEyIENPRElORywgTk8gT1VUUFVUDQoNCm1peHNlZyA8PSAnRTovMDMtRG93bmxvYWQvR2l0aHViL+e6oualvOaipi1VVEYtOC50eHQnDQojU1RJTEwgTk8gT1VUUFVUDQoNCnNlZ21lbnQoJ+S9oOS7iuWkqeimgeWIsOWTqumHjOWOu++8nycsIG1peHNlZykgDQoNCnNlZ21lbnQoJ0U6LzAzLURvd25sb2FkL0dpdGh1Yi/nuqLmpbzmoqYtVVRGLTgudHh0JywgbWl4c2VnKQ0KIyBbMV0gIkU6LzAzLURvd25sb2FkL0dpdGh1Yi/nuqLmpbzmoqYtVVRGLTguc2VnbWVudC4yMDE3LTA3LTE2XzExXzQyXzIzLnR4dCINCg0KYGBgDQoNCiMjIyAxLjMgRXhhbXBsZXMgZnJvbSB0dXRvcmlhbCBQREYNCg0KRXhhbXBsZXMgZnJvbSB0dXRvcmlhbCBQREYNCg0KYGBge3IgRXhhbXBsZXMgZnJvbSB0dXRvcmlhbCBQREZ9DQojIyMgTm90ZTogQ2FuIG5vdCBkaXNwbGF5IENoaW5lc2UgY2hhcmFjdGVycyBoZXJlLg0KIyMgTm90IHJ1bjoNCndvcmRzID0gImhlbGxvIHdvcmxkIg0KZW5naW5lMSA9IHdvcmtlcigpDQpzZWdtZW50KHdvcmRzLCBlbmdpbmUxKQ0KDQojICIuL3RlbXAudHh0IiBpcyBhIGZpbGUgcGF0aA0Kc2VnbWVudCgiLi90ZW1wLnR4dCIsIGVuZ2luZTEpDQplbmdpbmUyID0gd29ya2VyKCJobW0iKQ0Kc2VnbWVudCgiLi90ZW1wLnR4dCIsIGVuZ2luZTIpDQplbmdpbmUyJHdyaXRlID0gVA0Kc2VnbWVudCgiLi90ZW1wLnR4dCIsIGVuZ2luZTIpDQplbmdpbmUzID0gd29ya2VyKHR5cGUgPSAibWl4IiwgZGljdCA9ICJkaWN0X3BhdGgiLHN5bWJvbCA9IFQpDQpzZWdtZW50KCIuL3RlbXAudHh0IiwgZW5naW5lMykNCg0KIyMgRW5kKE5vdCBydW4pDQojIyBOb3QgcnVuOg0KIyMjIEtleXdvcmQgRXh0cmFjdGlvbg0KZW5naW5lID0gd29ya2VyKCJrZXl3b3JkcyIsIHRvcG4gPSAxKQ0Ka2V5d29yZHMod29yZHMsIGVuZ2luZSkNCg0KIyMjIFNwZWVjaCBUYWdnaW5nDQp0YWdnZXIgPSB3b3JrZXIoInRhZyIpDQp0YWdnaW5nKHdvcmRzLCB0YWdnZXIpDQoNCiMjIyBTaW1oYXNoDQpzaW1oYXNoZXIgPSB3b3JrZXIoInNpbWhhc2giLCB0b3BuID0gMSkNCnNpbWhhc2god29yZHMsIHNpbWhhc2hlcikNCmRpc3RhbmNlKCJoZWxsbyB3b3JsZCIgLCAiaGVsbG8gd29ybGQhIiAsIHNpbWhhc2hlcikNCnNob3dfZGljdHBhdGgoKQ0KIyMgRW5kKE5vdCBydW4pDQpgYGANCg0KIyMjIDEuNCBmaWxlIHNlZ21lbnRhdGlvbg0KDQpgYGB7ciBmaWxlIHNlZ21lbnRhdGlvbiBleDJ9DQp3ayA8LSB3b3JrZXIoZW5jb2Rpbmc9IlVURi04IikNCndrW2ZpbGUuY2hvb3NlKCldDQoNCiMg6K+N6aKR5YiG5p6QDQoj6L+Z6YeM6YCJ5oup5YiG6K+N5LmL5ZCO55qE5paH5Lu277yI5LiN6ZyA6KaB5YWI5YiG5p6Q77yM55u05o6l6YCJ5oup5Y6f5paH5Lu25Y2z5Y+v77yJDQpmIDwtIHNjYW4oZmlsZS5jaG9vc2UoKSxzZXA9IlxuIix3aGF0PSIiLGVuY29kaW5nPSJVVEYtOCIpDQojIFJlYWQgMSBpdGVtDQpkZiA8LSBmcmVxKHdrW2ZdKQ0KZGYgPC0gZGZbb3JkZXIoLWRmJGZyZXEpLF0NCg0KaW5zdGFsbC5wYWNrYWdlcygic3FsZGYiKQ0KDQpsaWJyYXJ5KCJzcWxkZiIpDQoj55SoVmlldyhkZinnnIvkuIDkuIvph4zpnaLlrZfmrrXlkI3vvIzkuI3nhLbmgI7kuYjlhplTUUzpg73kuI3nn6XpgZPvvIzmj5Dlj5bliY0xMDDpobnvvIzkuI3nhLblgZror43kupHnmoTml7blgJnlvojljaF+DQpkZiA8LSBzcWxkZigic2VsZWN0IFtjaGFyXSxbZnJlcV0gZnJvbSBkZiB3aGVyZSBsZW5ndGgoW2NoYXJdKT4xIGxpbWl0IDEwMCIpDQoj6L+Z6YeM55qEU1FM6K+t5Y+l5Y+v5Lul6ZqP6Ieq5bex55qE5a6e6ZmF6ZyA5rGC5aGr5YaZ77yM5Y+N5q2j6YO95LiA5qC377yM5YaN55SoVmlldyhkZinnnIvkuIDkuIvmlbDmja7vvIzmiJDlip/kuobvvIHlhbblrp7mjpLluo/kuZ/mmK/lj6/ku6Xnm7TmjqXkvb/nlKhTUUzov5vooYzmjpLluo/nmoTvvIzlj6rmmK/vvIzlj6rmmK9+5Zyo5YaZ6L+Z5paH56ug55qE5pe25YCZ5b+Y6K6w5LqG44CC44CC44CCDQojdXBkYXRlIGF0IDIwMTctMy05IOaVsOaNruahhuaWueazlSzom4vnlrznmoRTUUx+DQpkZiA8LSBkZltuY2hhcihkZiRjaGFyKT4xLF0NCg0KYGBgDQoNCg0KDQoNCiMjIDIuIOWbm+enjeWIhuivjeeul+azlS1mb3VyIGtpbmRzIG9mIHNlZ21lbnRhdGlvbiBtb2RlbA0KDQojIyMgMi4xIOacgOWkp+amgueOh+azle+8iE1QU2VnbWVudO+8ie+8miANCg0K6LSf6LSj5qC55o2uVHJpZeagkeaehOW7uuacieWQkeaXoOeOr+WbvuWSjOi/m+ihjOWKqOaAgeinhOWIkueul+azle+8jOaYr+WIhuivjeeul+azleeahOaguOW/g+OAgg0KDQpNYXhpbXVtIHByb2JhYmlsaXR5IHNlZ21lbnRhdGlvbiBtb2RlbCB1c2VzIFRyaWUgdHJlZSB0byBjb25zdHJ1Y3QgYSBkaXJlY3RlZCBhY3ljbGljIGdyYXBoIGFuZCB1c2VzIGR5bmFtaWMgcHJvZ3JhbW1pbmcgYWxnb3JpdGhtLiBJdCBpcyB0aGUgY29yZSBzZWdtZW50YXRpb24gYWxnb3JpdGhtLiBkaWN0IGFuZCB1c2VyIHNob3VsZCBiZSBwcm92aWRlZCB3aGVuIGluaXRpYWxpemluZyBqaWViYVIgd29ya2VyLg0KDQpgYGB7ciBNYXhpbXVtIHByb2JhYmlsaXR5IHNlZ21lbnRhdGlvbiBtb2RlbCBleDF9DQp0ZXh0IDwtICfkvaDopoHmmI7nmb3vvIzov5nku4Xku4XmmK/kuIDkuKrmtYvor5XmlofmnKwnDQptcHNlZyA8LSB3b3JrZXIoJ21wJykgI+acgOWkp+amgueOh+azle+8iE1QU2VnbWVudO+8iQ0KbXBzZWdbdGV4dF0NCiMgWzFdICLkvaAiICAgIuimgSIgICAi5piO55m9IiAi6L+ZIiAgICLku4Xku4UiICLmmK8iICAgIuS4gOS4qiIgIua1i+ivlSIgIuaWh+acrCINCmBgYA0KDQojIyMgMi4yIOmakOW8j+mprOWwlOenkeWkq+aooeWei++8iEhNTVNlZ21lbnTvvInvvJogDQoNCuaYr+agueaNruWfuuS6juS6uuawkeaXpeaKpeetieivreaWmeW6k+aehOW7uueahEhNTeaooeWei+adpei/m+ihjOWIhuivje+8jOS4u+imgeeul+azleaAnei3r+aYr+agueaNrihCLEUsTSxTKeWbm+S4queKtuaAgeadpeS7o+ihqOavj+S4quWtl+eahOmakOiXj+eKtuaAgeOAgiBITU3mqKHlnovnlLFkaWN0L2htbV9tb2RlbC51dGY45o+Q5L6b44CC5YiG6K+N566X5rOV5Y2zdml0ZXJiaeeul+azleOAgg0KDQpIaWRkZW4gTWFya292IE1vZGVsIHVzZXMgSE1NIG1vZGVsIHRvIGRldGVybWluZSBzdGF0dXMgc2V0IGFuZCBvYnNlcnZlZCBzZXQgb2Ygd29yZHMuIFRoZSBkZWZhdWx0IEhNTSBtb2RlbCBpcyBiYXNlZCBvbiBQZW9wbGUncyBEYWlseSBsYW5ndWFnZSBsaWJyYXJ5LiBobW0gc2hvdWxkIGJlIHByb3ZpZGVkIHdoZW4gaW5pdGlhbGl6aW5nIGppZWJhUiB3b3JrZXIuDQoNCmBgYHtyIEhpZGRlbiBNYXJrb3Ygc2VnbWVudGF0aW9uIE1vZGVsIGV4MX0NCnRleHQgPC0gJ+S9oOimgeaYjueZve+8jOi/meS7heS7heaYr+S4gOS4qua1i+ivleaWh+acrCcNCmhtbXNlZyA8LSB3b3JrZXIoJ2htbScpICPpmpDlvI/pqazlsJTnp5HlpKvmqKHlnovvvIhITU1TZWdtZW5077yJDQpobW1zZWdbdGV4dF0NCiMgWzFdICLkvaAiICAgIuimgSIgICAi5piO55m9IiAi6L+Z5LuFIiAi5LuFIiAgICLmmK8iICAgIuS4gOS4qiIgIua1i+ivlSIgIuaWh+acrCINCmBgYA0KDQojIyMgMi4zIOa3t+WQiOaooeWei++8iE1peFNlZ21lbnTvvInvvJogDQoNCuaYr+Wbm+S4quWIhuivjeW8leaTjumHjOmdouWIhuivjeaViOaenOi+g+WlveeahOexu++8jOe7k+Wug+WQiOS9v+eUqOacgOWkp+amgueOh+azleWSjOmakOW8j+mprOWwlOenkeWkq+aooeWei+OAgg0KDQpNaXhTZWdtZW50IG1vZGVsIHVzZXMgYm90aCBNYXhpbXVtIHByb2JhYmlsaXR5IHNlZ21lbnRhdGlvbiBtb2RlbCBhbmQgSGlkZGVuIE1hcmtvdiBNb2RlbCB0byBjb25zdHJ1Y3Qgc2VnbWVudGF0aW9uLiBkaWN0LCBobW0gYW5kIHVzZXIgc2hvdWxkIGJlIHByb3ZpZGVkIHdoZW4gaW5pdGlhbGl6aW5nIGppZWJhUiB3b3JrZXIuDQoNCmBgYHtyIE1peFNlZ21lbnQgc2VnbWVudGF0aW9uIE1vZGVsIGV4MX0NCnRleHQgPC0gJ+S9oOimgeaYjueZve+8jOi/meS7heS7heaYr+S4gOS4qua1i+ivleaWh+acrCcNCm1peHNlZyA8LSB3b3JrZXIoJ21peCcpICPmt7flkIjmqKHlnovvvIhNaXhTZWdtZW5077yJDQptaXhzZWdbdGV4dF0NCiMgWzFdICLkvaAiICAgIuimgSIgICAi5piO55m9IiAi6L+ZIiAgICLku4Xku4UiICLmmK8iICAgIuS4gOS4qiIgIua1i+ivlSIgIuaWh+acrCINCmBgYA0KDQojIyMgMi40IOe0ouW8leaooeWei++8iFF1ZXJ5U2VnbWVudO+8ie+8miANCg0K5YWI5L2/55So5re35ZCI5qih5Z6L6L+b6KGM5YiH6K+N77yM5YaN5a+55LqO5YiH5Ye65p2l55qE6L6D6ZW/55qE6K+N77yM5p6a5Li+5Y+l5a2Q5Lit5omA5pyJ5Y+v6IO95oiQ6K+N55qE5oOF5Ya177yM5om+5Ye66K+N5bqT6YeM5a2Y5Zyo44CCDQoNClF1ZXJ5U2VnbWVudCBtb2RlbCB1c2VzIE1peFNlZ21lbnQgdG8gY29uc3RydWN0IHNlZ21lbnRhdGlvbiBhbmQgdGhlbiBlbnVtZXJhdGVzIGFsbCB0aGUgcG9zc2libGUgbG9uZyB3b3JkcyBpbiB0aGUgZGljdGlvbmFyeS4gZGljdCwgaG1tIGFuZCBxbWF4IHNob3VsZCBiZSBwcm92aWRlZCB3aGVuIGluaXRpYWxpemluZyBqaWViYVIgd29ya2VyLg0KDQpUaGVyZSBpcyBhIHN5bWJvbCA8PSBmb3IgdGhpcyBmdW5jdGlvbi4NCg0KYGBge3IgUXVlcnlTZWdtZW50IHNlZ21lbnRhdGlvbiBNb2RlbCBleDF9DQp0ZXh0IDwtICfkvaDopoHmmI7nmb3vvIzov5nku4Xku4XmmK/kuIDkuKrmtYvor5XmlofmnKwnDQpxdWVyeXNlZyA8LSB3b3JrZXIoJ3F1ZXJ5JykgI+e0ouW8leaooeWei++8iFF1ZXJ5U2VnbWVudO+8iQ0KcXVlcnlzZWdbdGV4dF0NCiMgWzFdICLkvaAiICAgIuimgSIgICAi5piO55m9IiAi6L+ZIiAgICLku4Xku4UiICLmmK8iICAgIuS4gOS4qiIgIua1i+ivlSIgIuaWh+acrCINCg0KYGBgDQoNCiMjIyAyLjUgd29ya2VyKCnlh73mlbDnmoTlrprkuYkNCg0KYGBge3Igd29ya2VyKCl9DQp3b3JrZXIodHlwZSA9ICJtaXgiLCBkaWN0ID0gRElDVFBBVEgsIGhtbSA9IEhNTVBBVEgsIHVzZXIgPSBVU0VSUEFUSCwNCiAgaWRmID0gSURGUEFUSCwgc3RvcF93b3JkID0gU1RPUFBBVEgsIHdyaXRlID0gVCwgcW1heCA9IDIwLCB0b3BuID0gNSwNCiAgZW5jb2RpbmcgPSAiVVRGLTgiLCBkZXRlY3QgPSBULCBzeW1ib2wgPSBGLCBsaW5lcyA9IDFlKzA1LA0KICBvdXRwdXQgPSBOVUxMLCBieWxpbmVzID0gRiwgdXNlcl93ZWlnaHQgPSAibWF4IikNCg0KIyDlj4LmlbDliJfooajvvJoNCiMgDQojIHR5cGUsIOW8leaTjuexu+Weiw0KIyBkaWN0LCDns7vnu5/or43lhbgNCiMgaG1tLCBITU3mqKHlnovot6/lvoQNCiMgdXNlciwg55So5oi36K+N5YW4DQojIGlkZiwgSURG6K+N5YW4DQojIHN0b3Bfd29yZCwg5YWz6ZSu6K+N55So5YGc5q2i6K+N5bqTDQojIHdyaXRlLCDmmK/lkKblsIbmlofku7bliIbor43nu5PmnpzlhpnlhaXmlofku7bvvIzpu5jorqRGQUxTRQ0KIyBxbWF4LCDmnIDlpKfmiJDor43nmoTlrZfnrKbmlbDvvIzpu5jorqQyMOS4quWtl+espg0KIyB0b3BuLCDlhbPplK7or43mlbAs6buY6K6kNeS4qg0KIyBlbmNvZGluZywg6L6T5YWl5paH5Lu255qE57yW56CB77yM6buY6K6kVVRGLTgNCiMgZGV0ZWN0LCDmmK/lkKbnvJbnoIHmo4Dmn6XvvIzpu5jorqRUUlVFDQojIHN5bWJvbCwg5piv5ZCm5L+d55WZ56ym5Y+377yM6buY6K6kRkFMU0UNCiMgbGluZXMsIOavj+asoeivu+WPluaWh+S7tueahOacgOWkp+ihjOaVsO+8jOeUqOS6juaOp+WItuivu+WPluaWh+S7tueahOmVv+W6puOAguWkp+aWh+S7tuWImeS8muWIhuasoeivu+WPluOAgg0KIyBvdXRwdXQsIOi+k+WHuui3r+W+hA0KIyBieWxpbmVzLCDmjInooYzovpPlh7oNCiMgdXNlcl93ZWlnaHQsIOeUqOaIt+adg+mHjQ0KYGBgDQoNCuaIkeS7rOWcqOiwg+eUqHdvcmtlcigp5pe277yM5bCx5Yqg6L295LqG5YiG6K+N5byV5pOO77yM5Y+v5Lul5omT5Y2w5Ye65p2l77yM5p+l55yL5YiG6K+N55qE5byV5pOO55qE6YWN572u44CCDQoNCmBgYHtyIERlZmF1bHQgTWV0aG9kfQ0Kd2sgPSB3b3JrZXIoKQ0Kd2sNCiMgV29ya2VyIFR5cGU6ICBKaWViYSBTZWdtZW50DQojIA0KIyBEZWZhdWx0IE1ldGhvZCAgOiAgbWl4DQojIERldGVjdCBFbmNvZGluZyA6ICBUUlVFDQojIERlZmF1bHQgRW5jb2Rpbmc6ICBVVEYtOA0KIyBLZWVwIFN5bWJvbHMgICAgOiAgRkFMU0UNCiMgT3V0cHV0IFBhdGggICAgIDogIA0KIyBXcml0ZSBGaWxlICAgICAgOiAgVFJVRQ0KIyBCeSBMaW5lcyAgICAgICAgOiAgRkFMU0UNCiMgTWF4IFdvcmQgTGVuZ3RoIDogIDIwDQojIE1heCBSZWFkIExpbmVzICA6ICAxZSswNQ0KIyANCiMgRml4ZWQgTW9kZWwgQ29tcG9uZW50czogIA0KIyANCiMgJGRpY3QNCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC9qaWViYS5kaWN0LnV0ZjgiDQojIA0KIyAkdXNlcg0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L3VzZXIuZGljdC51dGY4Ig0KIyANCiMgJGhtbQ0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L2htbV9tb2RlbC51dGY4Ig0KIyANCiMgJHN0b3Bfd29yZA0KIyBOVUxMDQojIA0KIyAkdXNlcl93ZWlnaHQNCiMgWzFdICJtYXgiDQojIA0KIyAkdGltZXN0YW1wDQojIFsxXSAxNTAwMjI2MTI3DQojIA0KIyAkZGVmYXVsdCAkZGV0ZWN0ICRlbmNvZGluZyAkc3ltYm9sICRvdXRwdXQgJHdyaXRlICRsaW5lcyAkYnlsaW5lcyBjYW4gYmUgcmVzZXQuDQpgYGANCg0K5aaC5p6c5oiR5Lus5oOz5pS55Y+Y5YiG6K+N5byV5pOO55qE6YWN572u6aG577yM5Y+v5Lul5Zyo6LCD55SoYHdvcmtlcigpYOWIm+W7uuWIhuivjeW8leaTjuaXtu+8jOS5n+WPr+S7pemAmui/h2B3ayRYWGDmnaXov5vooYzorr7nva7jgILlpoLmnpzmg7Pkuobop6Ngd2tg5piv5LuA5LmI57G75Z6L55qE5a+56LGh77yM5oiR5Lus6YCa6L+HYHByeXJg5YyF55qEb3R5cGXnmoTlh73mlbDmnaXmo4Dmn6Vgd2tg5a+56LGh55qE57G75Z6L44CC5YWz5LqOYHByeXJg5YyF55qE6K+m57uG5L2/55So77yM6K+35Y+C6ICD5paH56ugW+aSrOWKqFLlhoXmoLjnmoTpq5jnuqflt6XlhbfljIVwcnlyXShodHRwOi8vYmxvZy5mZW5zLm1lL3ItcHJ5ci8pDQpgYGB7ciBwcnlyIHBhY2thZ2V9DQojIOWKoOi9vSBwcnly5YyFDQpsaWJyYXJ5KHByeXIpDQpvdHlwZSh3aykgICMg6Z2i5ZCR5a+56LGh55qE57G75Z6L5qOA5p+lDQojIFsxXSAiUzMiDQoNCmNsYXNzKHdrKSAgIyDmn6XnnItjbGFzc+aYr+WxnuaApw0KIyBbMV0gImppZWJhciIgICJzZWdtZW50IiAiamllYmEiIA0KYGBgDQoNCiMjIyAyLjYgYXBwbHlfbGlzdA0KDQpgYGB7ciBhcHBseV9saXN0fQ0KDQpjdXR0ZXIgPSB3b3JrZXIoKQ0KYXBwbHlfbGlzdChsaXN0KCJ0aGlzIGlzIHRlc3QiLCAidGhhdCBpcyBub3QgdGVzdCIpLCBjdXR0ZXIpDQphcHBseV9saXN0KGxpc3QoInRoaXMgaXMgdGVzdCIsIGxpc3QoInRoYXQgaXMgbm90IHRlc3QiLCJhYiBjIikpLCBjdXR0ZXIpDQoNCmBgYA0KDQojIyMgMi43IGZpbGVfY29kaW5nDQoNCmBgYHtyIGZpbGVfY29kaW5nfQ0KDQpmaWxlX2NvZGluZyhmaWxlKQ0KZmlsZWNvZGluZyhmaWxlKQ0KDQpgYGANCg0KDQoNCg0KIyMjIERpY3Rpb25hcnkgUGF0aA0KDQpgYGB7ciBEaWN0aW9uYXJ5IHBhdGh9DQoNCkRJQ1RQQVRIO0hNTVBBVEg7VVNFUlBBVEg7SURGUEFUSDtTVE9QUEFUSDtESUNUUEFUSA0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L2ppZWJhLmRpY3QudXRmOCINCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC9obW1fbW9kZWwudXRmOCINCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC91c2VyLmRpY3QudXRmOCINCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC9pZGYudXRmOCINCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC9zdG9wX3dvcmRzLnV0ZjgiDQoNCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC9qaWViYS5kaWN0LnV0ZjgiDQoNCkhNTVBBVEgNCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC9obW1fbW9kZWwudXRmOCINCg0KVVNFUlBBVEgNCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC91c2VyLmRpY3QudXRmOCINCg0KSURGUEFUSA0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L2lkZi51dGY4Ig0KDQpTVE9QUEFUSA0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L3N0b3Bfd29yZHMudXRmOCINCg0KYGBgDQojIyMgMi43IEhhbW1pbmcgZGlzdGFuY2Ugb2Ygd29yZHMNCg0KYGBge3IgaGFtbWluZyBkaXN0YW5jZSBvZiB3b3Jkc30NCg0KZGlzdGFuY2UoY29kZWwsIGNvZGVyLCBqaWViYXIpDQp2ZWN0b3JfZGlzdGFuY2UoY29kZWwsIGNvZGVyLCBqaWViYXIpDQoNCg0Kd29yZHMgPSAiaGVsbG8gd29ybGQiDQpzaW1oYXNoZXIgPSB3b3JrZXIoInNpbWhhc2giLCB0b3BuID0gMSkNCnNpbWhhc2hlciA8PSB3b3Jkcw0KZGlzdGFuY2UoImhlbGxvIHdvcmxkIiAsICJoZWxsbyB3b3JsZCEiICwgc2ltaGFzaGVyKQ0KdmVjdG9yX2Rpc3RhbmNlKGMoImhlbGxvIiwid29ybGQiKSAsIGMoImhlbGxvIiwgIndvcmxkIiwiISIpICwgc2ltaGFzaGVyKQ0KDQpgYGANCg0KDQoNCiMjICAzLiDmoIfms6jor43mgKctU3BlZWNoIFRhZ2dpbmcNCg0KU3BlZWNoIFRhZ2dpbmcgd29ya2VyIHVzZXMgTWl4U2VnbWVudCBtb2RlbCB0byBjdXQgd29yZCBhbmQgdGFnIGVhY2ggd29yZCBhZnRlciBzZWdtZW50YXRpb24gdXNpbmcgbGFiZWxzIGNvbXBhdGlibGUgd2l0aCBpY3RjbGFzLg0KDQpkaWN0LCBobW0gYW5kIHVzZXIgc2hvdWxkIGJlIHByb3ZpZGVkIHdoZW4gaW5pdGlhbGl6aW5nIGppZWJhUiB3b3JrZXIuDQoNCuWPr+S7peS9v+eUqGA8PS50YWdnZXJgIOaIluiAhWB0YWdgIOadpei/m+ihjOWIhuivjeWSjOivjeaAp+agh+azqO+8jOivjeaAp+agh+azqOS9v+eUqOa3t+WQiOaooeWei+aooeWei+WIhuivje+8jOagh+azqOmHh+eUqOWSjCBgaWN0Y2xhc2Ag5YW85a6555qE5qCH6K6w5rOV44CCDQoNCmppZWJhUuWMheWFs+S6juivjeWFuOivjeaAp+agh+iusO+8jOmHh+eUqGljdGNsYXPnmoTmoIforrDmlrnms5XjgIJJQ1RDTEFTIOaxieivreivjeaAp+agh+azqOmbhuOAgg0KDQrku6PnoIEJ5ZCN56ewCeW4ruWKqeiusOW/hueahOivoOmHig0KQWcJ5b2i6K+t57SgCeW9ouWuueivjeaAp+ivree0oOOAguW9ouWuueivjeS7o+eggeS4umHvvIzor63ntKDku6PnoIHvvYfliY3pnaLnva7ku6VB44CCDQphCeW9ouWuueivjQnlj5boi7Hor63lvaLlrrnor41hZGplY3RpdmXnmoTnrKwx5Liq5a2X5q+N44CCDQphZAnlia/lvaLor40J55u05o6l5L2c54q26K+t55qE5b2i5a656K+N44CC5b2i5a656K+N5Luj56CBYeWSjOWJr+ivjeS7o+eggWTlubblnKjkuIDotbfjgIINCmFuCeWQjeW9ouivjQnlhbfmnInlkI3or43lip/og73nmoTlvaLlrrnor43jgILlvaLlrrnor43ku6PnoIFh5ZKM5ZCN6K+N5Luj56CBbuW5tuWcqOS4gOi1t+OAgg0KYgnljLrliKvor40J5Y+W5rGJ5a2XIuWIqyLnmoTlo7Dmr43jgIINCmMJ6L+e6K+NCeWPluiLseivrei/nuivjWNvbmp1bmN0aW9u55qE56ysMeS4quWtl+avjeOAgg0KRGcJ5Ymv6K+t57SgCeWJr+ivjeaAp+ivree0oOOAguWJr+ivjeS7o+eggeS4umTvvIzor63ntKDku6PnoIHvvYfliY3pnaLnva7ku6VE44CCDQpkCeWJr+ivjQnlj5ZhZHZlcmLnmoTnrKwy5Liq5a2X5q+N77yM5Zug5YW256ysMeS4quWtl+avjeW3sueUqOS6juW9ouWuueivjeOAgg0KZQnlj7nor40J5Y+W6Iux6K+t5Y+56K+NZXhjbGFtYXRpb27nmoTnrKwx5Liq5a2X5q+N44CCDQpmCeaWueS9jeivjQnlj5bmsYnlrZci5pa5IueahOWjsOavjeOAgg0KZwnor63ntKAJ57ud5aSn5aSa5pWw6K+t57Sg6YO96IO95L2c5Li65ZCI5oiQ6K+N55qEIuivjeaguSLvvIzlj5bmsYnlrZci5qC5IueahOWjsOavjeOAgg0KaAnliY3mjqXmiJDliIYJ5Y+W6Iux6K+taGVhZOeahOesrDHkuKrlrZfmr43jgIINCmkJ5oiQ6K+tCeWPluiLseivreaIkOivrWlkaW9t55qE56ysMeS4quWtl+avjeOAgg0KagnnroDnp7DnlaXor60J5Y+W5rGJ5a2XIueugCLnmoTlo7Dmr43jgIINCmsJ5ZCO5o6l5oiQ5YiGCQ0KbAnkuaDnlKjor60J5Lmg55So6K+t5bCa5pyq5oiQ5Li65oiQ6K+t77yM5pyJ54K5IuS4tOaXtuaApyLvvIzlj5Yi5Li0IueahOWjsOavjeOAgg0KbQnmlbDor40J5Y+W6Iux6K+tbnVtZXJhbOeahOesrDPkuKrlrZfmr43vvIxu77yMdeW3suacieS7lueUqOOAgg0KTmcJ5ZCN6K+t57SgCeWQjeivjeaAp+ivree0oOOAguWQjeivjeS7o+eggeS4um7vvIzor63ntKDku6PnoIHvvYfliY3pnaLnva7ku6VO44CCDQpuCeWQjeivjQnlj5boi7Hor63lkI3or41ub3Vu55qE56ysMeS4quWtl+avjeOAgg0KbnIJ5Lq65ZCNCeWQjeivjeS7o+eggW7lkowi5Lq6KHJlbiki55qE5aOw5q+N5bm25Zyo5LiA6LW344CCDQpucwnlnLDlkI0J5ZCN6K+N5Luj56CBbuWSjOWkhOaJgOivjeS7o+eggXPlubblnKjkuIDotbfjgIINCm50CeacuuaehOWbouS9kwki5ZuiIueahOWjsOavjeS4unTvvIzlkI3or43ku6PnoIFu5ZKMdOW5tuWcqOS4gOi1t+OAgg0KbnoJ5YW25LuW5LiT5ZCNCSLkuJMi55qE5aOw5q+N55qE56ysMeS4quWtl+avjeS4unrvvIzlkI3or43ku6PnoIFu5ZKMeuW5tuWcqOS4gOi1t+OAgg0Kbwnmi5/lo7Dor40J5Y+W6Iux6K+t5ouf5aOw6K+Nb25vbWF0b3BvZWlh55qE56ysMeS4quWtl+avjeOAgg0KcAnku4vor40J5Y+W6Iux6K+t5LuL6K+NcHJlcG9zaXRpb25hbOeahOesrDHkuKrlrZfmr43jgIINCnEJ6YeP6K+NCeWPluiLseivrXF1YW50aXR555qE56ysMeS4quWtl+avjeOAgg0Kcgnku6Por40J5Y+W6Iux6K+t5Luj6K+NcHJvbm91bueahOesrDLkuKrlrZfmr40s5ZugcOW3sueUqOS6juS7i+ivjeOAgg0KcwnlpITmiYDor40J5Y+W6Iux6K+tc3BhY2XnmoTnrKwx5Liq5a2X5q+N44CCDQpUZwnml7bor63ntKAJ5pe26Ze06K+N5oCn6K+t57Sg44CC5pe26Ze06K+N5Luj56CB5Li6dCzlnKjor63ntKDnmoTku6PnoIFn5YmN6Z2i572u5LulVOOAgg0KdAnml7bpl7Tor40J5Y+W6Iux6K+tdGltZeeahOesrDHkuKrlrZfmr43jgIINCnUJ5Yqp6K+NCeWPluiLseivreWKqeivjWF1eGlsaWFyeSDnmoTnrKwy5Liq5a2X5q+NLOWboGHlt7LnlKjkuo7lvaLlrrnor43jgIINClZnCeWKqOivree0oAnliqjor43mgKfor63ntKDjgILliqjor43ku6PnoIHkuLp244CC5Zyo6K+t57Sg55qE5Luj56CBZ+WJjemdoue9ruS7pVbjgIINCnYJ5Yqo6K+NCeWPluiLseivreWKqOivjXZlcmLnmoTnrKzkuIDkuKrlrZfmr43jgIINCnZkCeWJr+WKqOivjQnnm7TmjqXkvZznirbor63nmoTliqjor43jgILliqjor43lkozlia/or43nmoTku6PnoIHlubblnKjkuIDotbfjgIINCnZuCeWQjeWKqOivjQnmjIflhbfmnInlkI3or43lip/og73nmoTliqjor43jgILliqjor43lkozlkI3or43nmoTku6PnoIHlubblnKjkuIDotbfjgIINCncJ5qCH54K556ym5Y+3CQ0KeAnpnZ7or63ntKDlrZcJ6Z2e6K+t57Sg5a2X5Y+q5piv5LiA5Liq56ym5Y+377yM5a2X5q+NeOmAmuW4uOeUqOS6juS7o+ihqOacquefpeaVsOOAgeespuWPt+OAgg0KeQnor63msJTor40J5Y+W5rGJ5a2XIuivrSLnmoTlo7Dmr43jgIINCnoJ54q25oCB6K+NCeWPluaxieWtlyLnirYi55qE5aOw5q+N55qE5YmN5LiA5Liq5a2X5q+N44CCDQoNCg0KYGBge3IgU3BlZWNoIFRhZ2dpbmcgd29ya2VyIGV4MX0NCnRleHQgPC0gJ+S9oOimgeaYjueZve+8jOi/meS7heS7heaYr+S4gOS4qua1i+ivleaWh+acrCcNCnRhZ3NlZyA8LSB3b3JrZXIoJ3RhZycpDQoNCnRhZ3NlZ1t0ZXh0XQ0KICAjICAgIHIgICAgICB2ICAgICBuciAgICAgIHIgICAgICBkICAgICAgdiAgICAgIG0gICAgIHZuICAgICAgbiANCiAgIyAi5L2gIiAgICLopoEiICLmmI7nmb0iICAgIui/mSIgIuS7heS7hSIgICAi5pivIiAi5LiA5LiqIiAi5rWL6K+VIiAi5paH5pysIiANCg0KIyBzYW1lIHJlc3VsdHMNCnRhZ2dpbmcodGV4dCwgdGFnc2VnKQ0KICAjICAgIHIgICAgICB2ICAgICBuciAgICAgIHIgICAgICBkICAgICAgdiAgICAgIG0gICAgIHZuICAgICAgbiANCiAgIyAi5L2gIiAgICLopoEiICLmmI7nmb0iICAgIui/mSIgIuS7heS7hSIgICAi5pivIiAi5LiA5LiqIiAi5rWL6K+VIiAi5paH5pysIiANCiMgYW5vdGhlciBleGFtcGxlDQpjdXR0ZXIgPSB3b3JrZXIodHlwZSA9ICJ0YWciKQ0KY3V0dGVyX3dvcmRzIDwtIGN1dHRlciA8PSAi5oiR54ix5YyX5Lqs5aSp5a6J6ZeoIg0KY3V0dGVyX3dvcmRzDQojICAgICAgICByICAgICAgICB2ICAgICAgIG5zICAgICAgIG5zIA0KIyAgICAgICLmiJEiICAgICAi54ixIiAgICAgIuWMl+S6rCIgICAgICLlpKnlronpl6giIA0KIyAjICLmiJEiICDlj43ouqvku6Por43vvJsgIueIsSIg5Yqo6K+N77ybICLljJfkuqwiIOWQjeivjQ0KDQoNCmBgYA0KDQojIyA0LiDlhbPplK7or43mj5Dlj5YtS2V5d29yZCBFeHRyYWN0aW9uDQoNCuWFs+mUruivjeaPkOWPluaYr+aWh+acrOWkhOeQhumdnuW4uOmHjeimgeeahOS4gOS4queOr+iKgu+8jOS4gOS4que7j+WFuOeul+azleaYr1RGLUlERueul+azleOAguWFtuS4re+8jFRG77yIVGVybSBGcmVxdWVuY3nvvInku6Pooajor43popHvvIxJREbvvIhJbnZlcnNlIERvY3VtZW50IEZyZXF1ZW5jee+8ieihqOekuumAhuaWh+aho+mikeeOh+OAguWmguaenOafkOS4quivjeWcqOaWh+eroOS4reWkmuasoeWHuueOsO+8jOiAjOS4lOS4jeaYr+WBnOatouivje+8jOmCo+S5iOWug+W+iOWPr+iDveWwseWPjeW6lOS6hui/meauteaWh+eroOeahOeJueaAp++8jOi/meWwseaYr+aIkeS7rOimgeaJvueahOWFs+mUruivjeOAguWGjemAmui/h0lERuadpeeul+WHuuavj+S4quivjeeahOadg+mHje+8jOS4jeW4uOingeeahOivjeWHuueOsOeahOmikeeOh+i2iumrmO+8jOWImeadg+mHjei2iuWkp+OAguiuoeeul1RGLUlERueahOWFrOW8j+S4uu+8mmBURi1JREYgPSBURijor43popEpICog6YCG5paH5qGj6aKR546HKElERilgDQoNCuWvueaWh+aho+S4reavj+S4quivjeiuoeeul1RGLUlERueahOWAvO+8jOaKiue7k+aenOS7juWkp+WIsOWwj+aOkuW6j++8jOWwseW+l+WIsOS6hui/meevh+aWh+aho+eahOWFs+mUruaAp+aOkuW6j+WIl+ihqOOAguWFs+S6jklGLUlERueahOino+mHiu+8jOWPguiAg+S6huaWh+eroFtURi1JREbkuI7kvZnlvKbnm7jkvLzmgKfnmoTlupTnlKjvvIjkuIDvvInvvJroh6rliqjmj5Dlj5blhbPplK7or43jgIJdKGh0dHA6Ly93d3cucnVhbnlpZmVuZy5jb20vYmxvZy8yMDEzLzAzL3RmLWlkZi5odG1sKQ0KDQpqaWViYVLljIXnmoTlhbPplK7or43mj5Dlj5bmj5Dlj5bnmoTlrp7njrDvvIzkuZ/mmK/kvb/nlKjkuoZURi1JREbnmoTnrpfms5XjgILlnKjlronoo4Xnm67lvZXkuK3nmoRpZGYudXRmOOaWh+S7tu+8jOS4uklERueahOivreaWmeW6k+OAguafpeeci2lkZi51dGY45YaF5a6544CCDQoNCmBgYHtyfQ0Kc2hvd19kaWN0cGF0aCgpDQojIEQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0DQpkaXIoc2hvd19kaWN0cGF0aCgpKQ0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0Ig0KIyAgWzFdICJiYWNrdXAucmRhIiAgICAgICJobW1fbW9kZWwudXRmOCIgICJobW1fbW9kZWwuemlwIiAgDQojICBbNF0gImlkZi51dGY4IiAgICAgICAgImlkZi56aXAiICAgICAgICAgImppZWJhLmRpY3QudXRmOCINCiMgIFs3XSAiamllYmEuZGljdC56aXAiICAibW9kZWwucmRhIiAgICAgICAiUkVBRE1FLm1kIiAgICAgIA0KIyBbMTBdICJzdG9wX3dvcmRzLnV0ZjgiICJ1c2VyLmRpY3QudXRmOCIgDQoNCnNjYW4oZmlsZT0iRDovUHJvZ3JhbSBGaWxlcy9SL1ItMy4zLjMvbGlicmFyeS9qaWViYVJEL2RpY3QvaWRmLnV0ZjgiLA0KICAgICAgd2hhdD0nY2hhcmFjdGVyJyxubGluZXM9NTAsc2VwPSdcbicsDQogICAgICBlbmNvZGluZz0nVVRGLTgnLGZpbGVFbmNvZGluZz0nVVRGLTgnKQ0KIyBpbnZhbGlkIGlucHV0IGZvdW5kIG9uIGlucHV0IGNvbm5lY3Rpb24gJ0Q6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L2lkZi51dGY4J1JlYWQgMSBpdGVtDQoNCiNkZWxldGUgZmlsZUVuY29kaW5nPSdVVEYtOCcNCnNjYW4oZmlsZT0iRDovUHJvZ3JhbSBGaWxlcy9SL1ItMy4zLjMvbGlicmFyeS9qaWViYVJEL2RpY3QvaWRmLnV0ZjgiLA0KICAgICAgd2hhdD0nY2hhcmFjdGVyJyxubGluZXM9NTAsc2VwPSdcbicsDQogICAgICBlbmNvZGluZz0nVVRGLTgnKQ0KYGBgDQoNCmlkZi51dGY45paH5Lu25q+P5LiA6KGM5pyJMuWIl++8jOesrOS4gOWIl+aYr+ivjemhue+8jOesrOS6jOWIl+S4uuadg+mHjeOAgueEtuWQju+8jOaIkemAmui/h+iuoeeul+aWh+aho+eahOivjemikShURinvvIzkuI7or63mlpnlupPnmoRJREblgLznm7jkuZjvvIzlsLHlj6/ku6XlvpfliLBURi1JREblgLzvvIzku47ogIzmj5Dlj5bmlofmoaPnmoTlhbPplK7or43jgIINCg0K5q+U5aaC77yM5oiR5Lus5a+55LiL6Z2i55qE5paH5pys5YaF5a656L+b6KGM5YWz6ZSu6K+N55qE5o+Q5Y+W44CCDQpgYGB7ciBLZXl3b3JkIEV4dHJhY3Rpb24gZXgxfQ0Kd2sgPSB3b3JrZXIoKQ0Kc2VnbWVudDwtd2tbIlLnmoTmnoHlrqLnkIbmg7Pns7vliJfmlofnq6DvvIzmtrXnm5bkuoZS55qE5oCd5oOz77yM5L2/55So77yM5bel5YW377yM5Yib5paw562J55qE5LiA57O75YiX6KaB54K577yM5Lul5oiR5Liq5Lq655qE5a2m5Lmg5ZKM5L2T6aqM5Y676K+g6YeKUueahOW8uuWkp+OAgiJdDQoNCiMg6K6h566X6K+N6aKRDQpmcmVxKHNlZ21lbnQpDQoNCiMg5Y+WVEYtSURG55qE5YmNNeeahOWFs+mUruivjQ0Ka2V5cyA9IHdvcmtlcigia2V5d29yZHMiLHRvcG49NSkNCg0KIyDorqHnrpflhbPplK7or40NCnZlY3Rvcl9rZXl3b3JkcyhzZWdtZW50LGtleXMpDQojIDExLjczOTIgOC45NzM0MiA4LjIzNDI1ICA4LjIxMzcgNy40MzI5OCANCiAjICLmnoHlrqIiICAi6K+g6YeKIiAgIuimgeeCuSIgICLmtrXnm5YiICAi5L2T6aqMIiANCg0KYGBgDQrkvb/nlKhqaWViYVLljIXlpITnkIbliIbor43noa7lrp7nroDljZXvvIzlh6DooYznmoTku6PnoIHlsLHog73lrp7njrDliIbor43nmoTlkITnp43nrpfms5Xmk43kvZzjgILmnInkuobov5nkuKrlt6XlhbfvvIzmiJHku6zlsLHlj6/ku6XmlofmoaPkuK3vvIzlj5HnjrDlkITnp43or63oqIDop4TliJnov5vooYzmlofmnKzmjJbmjpjkuoYNCg0KS2V5d29yZCBFeHRyYWN0aW9uIHdvcmtlciB1c2VzIE1peFNlZ21lbnQgbW9kZWwgdG8gY3V0IHdvcmQgYW5kIHVzZSBURi1JREYgYWxnb3JpdGhtIHRvIGZpbmQgdGhlIGtleXdvcmRzLiBkaWN0ICxobW0sIGlkZiwgc3RvcF93b3JkIGFuZCB0b3BuIHNob3VsZCBiZSBwcm92aWRlZCB3aGVuIGluaXRpYWxpemluZyBqaWViYVIgd29ya2VyLg0KDQrlhbPplK7or43mj5Dlj5bmiYDkvb/nlKjpgIblkJHmlofku7bpopHnjofvvIhJREbvvInmlofmnKzor63mlpnlupPlj6/ku6XliIfmjaLmiJDoh6rlrprkuYnor63mlpnlupPnmoTot6/lvoTvvIzkvb/nlKjmlrnms5XkuI7liIbor43nsbvkvLzjgIJ0b3Bu5Y+C5pWw5Li65YWz6ZSu6K+N55qE5Liq5pWw44CCDQoNCmBgYHtyIGtleXMgZXgxfQ0KdGV4dCA8LSAn5L2g6KaB5piO55m977yM6L+Z5LuF5LuF5piv5LiA5Liq5rWL6K+V5paH5pysJw0KDQprZXlzID0gd29ya2VyKCdrZXl3b3JkcycsIHRvcG4gPSAyKSAj5Y+C5pWwdG9wbuihqOekuuaPkOWPluaOkuWcqOacgOWJjeeahOWFs+mUruivjeS4quaVsA0Ka2V5cyA8PSB0ZXh0DQojIDguOTQ0ODUgNy4xNDcyNCANCiMgICLmlofmnKwiICAi5rWL6K+VIiANCg0KI+WQjOagt+eahO+8jOS5n+WPr+S7peWvueaWh+S7tui/m+ihjOWFs+mUruivjeaPkOWPlg0Ka2V5cyA8PSAi5LiA5Liq5paH5Lu26Lev5b6ELnR4dCINCmtleXMgPD0gIkU6LzAzLURvd25sb2FkL0dpdGh1Yi/nuqLmpbzmoqYtVVRGLTgudHh0IiANCiMgMjYxMzguOSA5MjY2LjQ1IA0KIyAgIuWuneeOiSIgICLotL7mr40iIA0KDQojIyBhbm90aGVyIGV4YW1wbGUNCmN1dHRlciA9IHdvcmtlcih0eXBlID0gImtleXdvcmRzIiwgdG9wbiA9IDIpDQpjdXR0ZXJfd29yZHMgPC0gY3V0dGVyIDw9ICLmiJHniLHljJfkuqzlpKnlronpl6giDQpjdXR0ZXJfd29yZHMNCiAgIyA4Ljk5NTQgICA0LjY2NzQgDQogICMgIuWkqeWuiemXqCIgICAi5YyX5LqsIg0KIyDmoLnmja5JREbnrpfms5XvvIwi5oiRIiAi54ixIiDnmoTpgIbmlofmnKzpopHnjofov4fkvY7vvIx0b3BuPTLvvIzlsLHooqvov4fmu6TmjonkuoYNCg0KYGBgDQoNCiMjIDUuIHNpbWhhc2jorqHnrpct5YiG6K+NLeWFs+mUruivjeaPkOWPli3mlofmnKzljrvph40NCg0KU2ltaGFzaCB3b3JrZXIgdXNlcyB0aGUga2V5d29yZCBleHRyYWN0aW9uIHdvcmtlciB0byBmaW5kIHRoZSBrZXl3b3JkcyBhbmQgdXNlcyBzaW1oYXNoIGFsZ29yaXRobSB0byBjb21wdXRlIHNpbWhhc2guIGRpY3QgaG1tLCBpZGYgYW5kIHN0b3Bfd29yZCBzaG91bGQgYmUgcHJvdmlkZWQgd2hlbiBpbml0aWFsaXppbmcgamllYmFSIHdvcmtlci4NCg0K5a+55Lit5paH5paH5qGj6K6h566X5Ye65a+55bqU55qEc2ltaGFzaOWAvOOAgnNpbWhhc2jmmK/osLfmrYznlKjmnaXov5vooYzmlofmnKzljrvph43nmoTnrpfms5XvvIznjrDlnKjlub/ms5vlupTnlKjlnKjmlofmnKzlpITnkIbkuK3jgIJTaW1oYXNo5byV5pOO5YWI6L+b6KGM5YiG6K+N5ZKM5YWz6ZSu6K+N5o+Q5Y+W77yM5ZCO6K6h566XU2ltaGFzaOWAvOWSjOa1t+aYjui3neemu+OAgg0KDQpgYGB7ciBzaW1oYXNoZXIgZXgxfQ0KdGV4dCA8LSAn5L2g6KaB5piO55m977yM6L+Z5LuF5LuF5piv5LiA5Liq5rWL6K+V5paH5pysJw0KDQpzaW1oYXNoZXIgPSB3b3JrZXIoInNpbWhhc2giLCB0b3BuID0gMikNCnNpbWhhc2hlciA8PSB0ZXh0DQojICRzaW1oYXNoDQojIFsxXSAiMTAwMTQ4NzA3OTc3MDc2MjQxNzAiDQojIA0KIyAka2V5d29yZA0KIyA4Ljk0NDg1IDcuMTQ3MjQgDQojICAi5paH5pysIiAgIua1i+ivlSIgDQoNCiPnnIvnnIvnuqLmpbzmoqbnmoTlhbPplK7or43mmK/ku4DkuYgNCiMgIuWuneeOiSIgICAi6LS+5q+NIiAgICLlh6Tlp5AiICLnjovlpKvkuroiICLogIHlpKrlpKoiICAgIuWkquWkqiIgDQpzaW1oYXNoZXIgPSB3b3JrZXIoInNpbWhhc2giLCB0b3BuID0gMTApDQpzaW1oYXNoZXIgPD0gIkU6LzAzLURvd25sb2FkL0dpdGh1Yi/nuqLmpbzmoqYtVVRGLTgudHh0Ig0KIyAkc2ltaGFzaA0KIyBbMV0gIjE0NzYwNTc5NTY4NjMwMTYyNzM3Ig0KIyANCiMgJGtleXdvcmQNCiMgIDI2MTM4LjkgIDkyNjYuNDUgIDgyNjkuOTQgIDc5MDkuMDkgIDcwMjAuMjcgIDU2MzEuMjEgDQojICAgIuWuneeOiSIgICAi6LS+5q+NIiAgICLlh6Tlp5AiICLnjovlpKvkuroiICLogIHlpKrlpKoiICAgIuWkquWkqiIgDQojICAgNTYwMy4xICA1NTEyLjA5ICA1NDUxLjI1ICA1MDYyLjE3IA0KIyAgICLlp5HlqJgiICAgIui0vueQjyIgICAi5aW25aW2IiAgICLkvJfkuroiIA0KDQojIGFub3RoZXIgZXhhbXBsZQ0KY3V0dGVyID0gd29ya2VyKHR5cGUgPSAic2ltaGFzaCIsIHRvcG4gPSAyKQ0KY3V0dGVyX3dvcmRzIDwtIGN1dHRlciA8PSAi5oiR54ix5YyX5Lqs5aSp5a6J6ZeoIg0KY3V0dGVyX3dvcmRzDQojICRzaW1oYXNoDQojIFsxXSAiNDM1Mjc0NTIyMTc1NDU3NTU1OSINCiMgDQojICRrZXl3b3JkDQojICAgOC45OTU0ICAgNC42Njc0IA0KIyAi5aSp5a6J6ZeoIiAgICLljJfkuqwiIA0KDQpgYGANCg0KIyMgNi4g5b+r6YCf5qih5byPLU5vdCBSZWNvbWVuZGVkDQoNCuaXoOmcgOS9v+eUqOWHveaVsGB3b3JrZXIoKWDvvIzkvb/nlKjpu5jorqTlj4LmlbDlkK/liqjlvJXmk47vvIzlubbnq4vljbPov5vooYzliIbor43jgILkvb/nlKhgcXNlZyhxdWljayBzZWdtZW50YXRpb24pYO+8jOS9v+eUqOm7mOiupOWIhuivjeaooeW8j++8jOiHquWKqOW7uueri+WIhuivjeW8leaTju+8jOexu+S8vOS6jmBnZ3Bsb3QyYOWMhemHjOmdoueahGBxcGxvdGDlh73mlbDjgIINCg0KYGBge3IgcXVpY2sgc2VnbWVudGF0aW9uIHFzZWcgZXgxfQ0KdGV4dCA8LSAn5L2g6KaB5piO55m977yM6L+Z5LuF5LuF5piv5LiA5Liq5rWL6K+V5paH5pysJw0KcXNlZyA8PSB0ZXh0DQojIFF1aWNrIG1vZGUgaXMgZGVwcmVjaWF0ZWQsIGFuZCBpcyBzY2hlZHVsZWQgdG8gYmUgcmVtb3ZlIGluIHYwLjExLjAuIElmIHlvdSB3YW50IHRvIGtlZXAgdGhpcyBmZWF0dXJlLCBwbGVhc2Ugc3VibWl0IGEgaXNzdWUgb24gR2l0SHViIHBhZ2UgdG8gbGV0IG1lIGtub3cuDQojIFsxXSAi5L2gIiAgICLopoEiICAgIuaYjueZvSIgIui/mSIgICAi5LuF5LuFIiAi5pivIiAgICLkuIDkuKoiICLmtYvor5UiICLmlofmnKwiDQoNCndvcmtlcignbWl4JykgI+afpeeci3dvcmtlcignbWl4Jynlj4LmlbDphY3nva4NCnFzZWcgI+afpeeci3FzZWflj4LmlbDphY3nva7vvIzkuI7kuIrpnaLkuIDmoLfpg73lvpfliLDku6XkuIvnu5PmnpwNCiMg5a6e6ZmF5LiK77yM56ys5LiA5qyh6L+Q6KGM5pe277yM5Lya5ZCv5Yqo6buY6K6k5byV5pOOIHF1aWNrX3dvcmtlcu+8jOebuOW9k+S6juWFiOi/kOihjOS6huS4gOmBjeS7o+egge+8mnFzZWcgPSB3b3JrZXIoJ21peCcpDQoNCiMgV29ya2VyIFR5cGU6ICBKaWViYSBTZWdtZW50DQojIA0KIyBEZWZhdWx0IE1ldGhvZCAgOiAgbWl4DQojIERldGVjdCBFbmNvZGluZyA6ICBUUlVFDQojIERlZmF1bHQgRW5jb2Rpbmc6ICBVVEYtOA0KIyBLZWVwIFN5bWJvbHMgICAgOiAgRkFMU0UNCiMgT3V0cHV0IFBhdGggICAgIDogIA0KIyBXcml0ZSBGaWxlICAgICAgOiAgVFJVRQ0KIyBCeSBMaW5lcyAgICAgICAgOiAgRkFMU0UNCiMgTWF4IFdvcmQgTGVuZ3RoIDogIDIwDQojIE1heCBSZWFkIExpbmVzICA6ICAxZSswNQ0KIyANCiMgRml4ZWQgTW9kZWwgQ29tcG9uZW50czogIA0KIyANCiMgJGRpY3QNCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdC9qaWViYS5kaWN0LnV0ZjgiDQojIA0KIyAkdXNlcg0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L3VzZXIuZGljdC51dGY4Ig0KIyANCiMgJGhtbQ0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L2htbV9tb2RlbC51dGY4Ig0KIyANCiMgJHN0b3Bfd29yZA0KIyBOVUxMDQojIA0KIyAkdXNlcl93ZWlnaHQNCiMgWzFdICJtYXgiDQojIA0KIyAkdGltZXN0YW1wDQojIFsxXSAxNTAwMTkyNTUyDQojIA0KIyAkZGVmYXVsdCAkZGV0ZWN0ICRlbmNvZGluZyAkc3ltYm9sICRvdXRwdXQgJHdyaXRlICRsaW5lcyAkYnlsaW5lcyBjYW4gYmUgcmVzZXQuDQpgYGANCg0KDQrlj6/ku6XpgJrov4dxc2VnJOmHjeiuvuaooeWei+WPguaVsO+8jOmHjeiuvuaooeWei+WPguaVsOWwhuS8muS/ruaUueS7peWQjuavj+asoem7mOiupOWQr+WKqOeahOm7mOiupOWPguaVsO+8mw0KDQrlpoLmnpzlj6rmmK/mg7PkuLTml7bkv67mlLnmqKHlnovlj4LmlbDvvIzlj6/ku6Xkvb/nlKjpnZ7lv6vpgJ/mqKHlvI/nmoTkv67mlLnmlrnlvI9xdWlja193b3JrZXIk44CCDQoNCmBgYHtyIHFzZWcgZXgyfQ0KIyDph43orr7mqKHlnovlj4LmlbDnmoTlkIzml7bvvIzph43mlrDlkK/liqjlvJXmk47vvJvkuIvmrKHph43mlrDlkK/liqjljIXml7bvvIznjrDmnInnmoTorr7nva7kuI3kvJrmlLnlj5jjgIINCnFzZWckdHlwZSA9ICJtcCIgDQoNCiMg5Li05pe25L+u5pS577yM5LiL5qyh6YeN5paw5ZCv5Yqo5YyF5pe277yM5Lya5oGi5aSN5Y6f5p2l55qE6buY6K6k6K6+572u44CCDQpxdWlja193b3JrZXIkZGV0ZWN0ID0gVCANCg0KIyDojrflvpflvZPliY3lv6vpgJ/mqKHlvI/nmoTpu5jorqTlj4LmlbANCmdldF9xc2VnbW9kZWwoKSAgICAgICAgIA0KYGBgDQoNCiMjIDcuIOWKoOi9veivjeW6kw0KDQrluLjnlKjnmoTliIbor43ljIXmnInkuKTnp43liqDovb3or43lupPnmoTmlrnms5XvvIzlsLHmmK/liqDovb3ljIXml7bor7vlj5bpu5jorqTnmoTor43lhbjlkozmlbDmja7mqKHlnovvvIzmiJbogIXlnKjliIbor43liY3liqDovb3or43lhbjlkozmqKHlnovmlbDmja7jgILlnKjml6nmnJ/nmoTniYjmnKzkuK3vvIxqaWViYVLkuZ/kvb/nlKjov4fov5nkuKTnp43mlrnlvI/ov5vooYzliqDovb3jgIINCg0K56ys5LiA56eN5pa55byP77yM5bCx5YOP5LiA5Liq6ZOB56y85a2Q77yM5Yqg6L295YyF5pe25LiA5qyh5oCn5Yqg6L295LqG6K+N5bqT77yM5bCB6KOF5Zyo5LiA6LW344CCDQoNCuesrOS6jOenjeaWueW8j+eBtea0u++8jOWPr+S7peWKqOaAgeWcsOWKoOi9veivjeW6k+WSjOaooeWei+aVsOaNru+8jOmAguaXtui/m+ihjOS/ruaUue+8jOS9huaYr+avj+asoeWIhuivjeWJje+8jOWKoOi9veivjeW6k+mDveWNgeWIhuiAl+i0ueaXtumXtO+8jOWvueS6juWwj+eahOS7u+WKoeS4jeWQiOmAguOAgg0KDQrmnInkuoZSY3BwIE1vZHVsZXPvvIxqaWViYVLlj6/ku6XmiopDKyvkuK3nmoTliIbor43nsbvmmKDlsITliLBS6K+t6KiA5Lit55qEUkPnsbvvvIzmiorov5nmoLfljp/mnKxDKyvkuK3pnZnmgIHnmoTnsbvnmoTmk43kvZzvvIzluKbliLDkuoZS6YeM6Z2i77yM5Y+v5Lul5Yqo5oCB5Zyw6L+Q6KGM44CC5ZyoamllYmFS6YeM77yM5L2g5Y+v5Lul5Yqo5oCB5Zyw55Sf5oiQ5YiG6K+N5Zmo77yM5L2/55So5LiN5ZCM55qE5YiG6K+N5Zmo77yM5a+55LiN5ZCM57G75Z6L55qE5paH5pys6L+b6KGM5pON5L2c77yM5YiG6K+N5bCx5YOP5YiH6I+c5pe26YCJ5LiN5ZCM55qE6I+c5YiA5LiA5qC344CCDQoNCuWvueS6juWIhuivjeeahOe7k+aenOWlveWdj+eahOWFs+mUruWboOe0oOaYr+ivjeWFuO+8jGppZWJhUum7mOiupOaciemFjee9ruagh+WHhueahOivjeWFuOOAguWvueS6juaIkeS7rOeahOS9v+eUqOadpeivtO+8jOS4jeWQjOihjOS4muaIluS4jeWQjOeahOaWh+Wtl+exu+Wei++8jOacgOWlveeUqOS4k+mXqOeahOWIhuivjeivjeWFuOOAguWcqGppZWJhUuS4remAmui/h2BzaG93X2RpY3RwYXRoKClg5Ye95pWw5Y+v5Lul5p+l55yL6buY6K6k55qE5qCH5YeG6K+N5YW477yM5Y+v5Lul6YCa6L+H5LiK5LiA5bCP6IqC5LuL57uN55qE6YWN572u6aG577yM5p2l5oyH5a6a5oiR5Lus6Ieq5bex55qE6K+N5YW444CC5pel5bi45a+56K+d55qE5bi455So6K+N5YW477yM5q+U5aaC5pCc54uX6L6T5YWl5rOV55qE6K+N5bqT44CCDQoNCmBsaWJyYXJ5KGppZWJhUilg5Yqg6L295YyF5pe277yM5rKh5pyJ5ZCv5Yqo5Lu75L2V5YiG6K+N5byV5pOO77yM5ZCv5Yqo5byV5pOO5b6I566A5Y2V77yM5bCx5piv5LiA5Y+l6LWL5YC86K+t5Y+l5bCx5Y+v5Lul5LqG44CCYGN1dHRlciA8LSB3b3JrZXIoKWANCg0K6L2v5Lu26buY6K6k6K6+5a6a6Z2e5bi46YeN6KaB77yMYGppZWJhUmDpu5jorqTlj4LmlbDkuLrnu53lpKflpJrmlbDku7vliqHosIPmlbTliLDkuobmnIDlpb3nmoTnirbmgIHvvIjlk4jlk4jvvIzmiJHnmoToh6rmiJHmhJ/op4nvvInjgILliJ3lp4vljJbliIbor43nroDljZXvvIzliIbor43lsLHmm7TnroDljZXkuobjgILkuLrkuoborqnlpKflrrblsJHkuIDkupvlvoXlnKjnlLXohJHliY3nmoTml7bpl7TvvIzlpJrkuIDkupvphY3lrrbkurrlkozmnIvlj4vnmoTml7bpl7TvvIzlsJHmlbLkuIDkupvplK7nm5jvvIxgamllYmFSYOmHjei9veS6hmA8PWDov5nkuKrkuI3lpKrluLjnlKjnmoTnrKblj7fvvIzlvZPnhLbov5jmnIlgPT1gLOS9oOWcqOmhueebrlJFQURNRemHjOWPr+S7peeci+WIsOOAguWIhuivjeWwseaYr+S4gOS4quexu+S8vOi1i+WAvOeahOi/h+eoi++8jOi2s+Wkn+eugOWNleeyl+aatO+8mg0KYGBge3Igc2VnbWVudGF0aW9uIGV4MX0NCiMgc3RhcnQgc2VnbWVudGF0aW9uIGVuZ2luZQ0KIyBpbXBvcnQgcGFja2FnZSBpbiBhIHNhZmUgd2F5DQppZighc3VwcHJlc3NXYXJuaW5ncyhyZXF1aXJlKCdqaWViYVInKSkpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcygnamllYmFSJykNCiAgcmVxdWlyZSgnamllYmFSJykNCn0NCg0KY3V0dGVyIDwtIHdvcmtlcigpDQoNCiMgY3V0IHdoYXQNCmN1dHRlciA8PSAi5rGf5bee5biC6ZW/5rGf5aSn5qGl77yM5Y+C5Yqg5LqG6ZW/5rGf5aSn5qGl55qE6YCa6L2m5Luq5byP44CCIg0KIyBwZXJmZWN0IHJlc3VsdHMNCiMgWzFdICLmsZ/lt54iICAgICAi5biC6ZW/IiAgICAgIuaxn+Wkp+ahpSIgICAi5Y+C5YqgIiAgICAgIuS6hiIgICAgICANCiMgWzZdICLplb/msZ/lpKfmoaUiICLnmoQiICAgICAgICLpgJrovaYiICAgICAi5Luq5byPIiAgIA0KDQojIOaIluiAhVBpcGXkuIDkuKrmlofku7bot6/lvoQNCmN1dHRlciA8PSAid2VpYm8udHh0Ig0KDQpgYGANCg0K5b2T54S277yM5aaC5p6c5L2g5Zac5qyi5omT5a2X77yM5Lmf5Y+v5Lul5L2/55SoYHNlZ21lbnQoKWDlh73mlbDjgILmraPlpoLkuYvliY3or7TnmoTvvIzlj6/ku6XlkIzml7bliJ3lp4vljJblkozkvb/nlKjlpJrkuKrliIbor43lmajjgILlj6/ku6Xmt7vliqDkuIDkupvlj4LmlbDmnaXliJ3lp4vljJbvvIzlj6/nlKjlj4LmlbDliJfooajlvojplb/lvojplb/vvIzkvYbmmK/kuIDoiKzkvaDkuI3kvJrlhajnlKjliLDlroPku6zvvIzlhbfkvZPlj6/ku6Xlj4LogIPluK7liqnmlofmoaNgP3dvcmtlcigpYDoNCmBgYHtyIHVzZXIgZGVmaW5lZCBkaWN0aW9uYXJ5IGV4MX0NCmN1dHRlcjIgPC0gd29ya2VyKCB1c2VyID0gImU6L1BhdGggZm9yIFVzZXIgZGVmaW5lZCBEaWN0aW9uYXJ5IikgIyMjIOWIneWni+WMluesrOS6jOS4quW8leaTjg0KDQpTaG93RGljdFBhdGgoKSANCiAjIyMg5Y+v5Lul5pi+56S66buY6K6k6K+N5YW46Lev5b6EDQoNCg0KIyDmn6XnnIvpu5jorqTnmoTor43lupPkvY3nva4NCnNob3dfZGljdHBhdGgoKQ0KIyBbMV0gIkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0Ig0KDQojIOafpeeci+ebruW9lQ0KZGlyKHNob3dfZGljdHBhdGgoKSkNCiMgWzFdICJEOi9Qcm9ncmFtIEZpbGVzL1IvUi0zLjMuMy9saWJyYXJ5L2ppZWJhUkQvZGljdCINCiMgIFsxXSAiYmFja3VwLnJkYSIgICAgICAiaG1tX21vZGVsLnV0ZjgiICAiaG1tX21vZGVsLnppcCIgIA0KIyAgWzRdICJpZGYudXRmOCIgICAgICAgICJpZGYuemlwIiAgICAgICAgICJqaWViYS5kaWN0LnV0ZjgiDQojICBbN10gImppZWJhLmRpY3QuemlwIiAgIm1vZGVsLnJkYSIgICAgICAgIlJFQURNRS5tZCIgICAgICANCiMgWzEwXSAic3RvcF93b3Jkcy51dGY4IiAidXNlci5kaWN0LnV0ZjgiIA0KDQojIOeci+WIsOivjeWFuOebruW9leS4re+8jOWMheaLrOS6huWkmuS4quaWh+S7tuOAgg0KIyANCiMgamllYmEuZGljdC51dGY4LCDns7vnu5/or43lhbjmlofku7bvvIzmnIDlpKfmpoLnjofms5XvvIx1dGY457yW56CB55qEDQojIGhtbV9tb2RlbC51dGY4LCDns7vnu5/or43lhbjmlofku7bvvIzpmpDlvI/pqazlsJTnp5HlpKvmqKHlnovvvIx1dGY457yW56CB55qEDQojIHVzZXIuZGljdC51dGY4LCDnlKjmiLfor43lhbjmlofku7bvvIx1dGY457yW56CB55qEDQojIHN0b3Bfd29yZHMudXRmOO+8jOWBnOatouivjeaWh+S7tu+8jHV0ZjjnvJbnoIHnmoQNCiMgaWRmLnV0ZjjvvIxJREbor63mlpnlupPvvIx1dGY457yW56CB55qEDQojIGppZWJhLmRpY3Quemlw77yMamllYmEuZGljdC51dGY455qE5Y6L57yp5YyFDQojIGhtbV9tb2RlbC56aXDvvIxobW1fbW9kZWwudXRmOOeahOWOi+e8qeWMhQ0KIyBpZGYuemlw77yMaWRmLnV0ZjjnmoTljovnvKnljIUNCiMgYmFja3VwLnJkYe+8jOaXoOazqOmHig0KIyBtb2RlbC5yZGHvvIzml6Dms6jph4oNCiMgUkVBRE1FLm1k77yM6K+05piO5paH5Lu2DQpgYGANCg0K5omT5byA57O757uf6K+N5YW45paH5Lu2amllYmEuZGljdC51dGY477yM5bm25omT5Y2w5YmNNTDooYwNCmBgYHtyIGppZWJhLmRpY3QudXRmOH0NCiNkZWxldGUgZmlsZUVuY29kaW5nPSdVVEYtOCcNCnNjYW4oZmlsZT0iRDovUHJvZ3JhbSBGaWxlcy9SL1ItMy4zLjMvbGlicmFyeS9qaWViYVJEL2RpY3QvamllYmEuZGljdC51dGY4IiwNCiAgICAgICAgICAgd2hhdD1jaGFyYWN0ZXIoKSxubGluZXM9NTAsc2VwPSdcbicsDQogICAgICAgICAgIGVuY29kaW5nPSd1dGYtOCcpDQojIOezu+e7n+ivjeWFuOavj+S4gOihjOmDveacieS4ieWIl++8jOW5tuS7peepuuagvOWIhuWJsu+8jOesrOS4gOWIl+S4uuivjemhue+8jOesrOS6jOWIl+S4uuivjemike+8jOesrOS4ieWIl+S4uuivjeaAp+agh+iusOOAgg0KDQojZGVsZXRlIGZpbGVFbmNvZGluZz0nVVRGLTgnDQpzY2FuKGZpbGU9IkQ6L1Byb2dyYW0gRmlsZXMvUi9SLTMuMy4zL2xpYnJhcnkvamllYmFSRC9kaWN0L3VzZXIuZGljdC51dGY4IiwNCiAgICAgIHdoYXQ9Y2hhcmFjdGVyKCksbmxpbmVzPTUwLHNlcD0nXG4nLA0KICAgICAgZW5jb2Rpbmc9J3V0Zi04JykNCiMg55So5oi36K+N5YW456ys5LiA6KGM5pyJ5LqM5YiX77yM77yM56ys5LiA5YiX5Li66K+N6aG577yM56ys5LqM5YiX5Li66K+N5oCn5qCH6K6w77yM5rKh5pyJ6K+N6aKR55qE5YiX44CC55So5oi36K+N5YW46buY6K6k6K+N6aKR5Li657O757uf6K+N5bqT5Lit55qE5pyA5aSn6K+N6aKR44CCDQpgYGANCg0KDQrov5nml7ZS55qE546v5aKD6YeM5ZCM5pe25pyJ5Lik5Liq5Yqg6L295LqG5LiN5ZCM6K+N5bqT55qE5YiG6K+N5byV5pOO44CCDQoNCuWmguaenOmcgOimgeS6huino+i/meS4pOS4quS4jeWQjOeahOW8leaTjueahOWMuuWIq+WPqumcgOimgXByaW505LiA5LiL5bCx5Y+v5Lul5LqG44CCDQoNCuavj+S4qndvcmtlcumDveacieS4gOS6m+WPguaVsOiuvue9ru+8jOWmgmBjdXR0ZXJg5Lit55qEYCRkZXRlY3Rg5Y+C5pWw5Yaz5a6a5LqG5byV5pOO5piv5ZCm6Ieq5Yqo5Yik5pat6L6T5YWl5paH5Lu255qE57yW56CB77yM5Zyo5byV5pOO5Yqg6L295pe25Y+v5Lul6YCa6L+HYHdvcmtlcihkZXRlY3QgPSBGIClg6L+b6KGM5Y+C5pWw6K6+572u77yM5Lmf5Y+v5Lul5Zyo5Yqg6L295ZCO6YCa6L+HYGN1dHRlciRkZXRlY3QgPSBGYOi/m+ihjOiuvue9ruOAguWFtuWuniBgd29ya2VyKClg5Ye95pWw6L+U5Zue55qE5piv5LiA5Liq546v5aKDYChlbnZpcm9ubWVudClg77yM6YeM6Z2i5bCB6KOF5LqG55yf5q2j55qE5YiG6K+N5byV5pOO77yM5L2g5Y+v5Lul6YCa6L+HYGN1dHRlciR3b3JrZXJg5p2l5p+l55yL55yf5q2j55qE4oCc5byV5pOO4oCd44CCYGN1dHRlciR3b3JrZXIgPHBvaW50ZXI6IDB4MDgwNWI5OTA+YA0KDQpgY3V0dGVyJHdvcmtlcmDlkoxgY3V0dGVyYOmDveaYr+eOr+Wig++8jOWcqOS8oOmAkuaXtuaYr+S8oOWdgO+8jOiAjOS4jeaYr+S8oOWAvO+8jOaViOeOh+aYr+avlOi+g+mrmOeahOOAgmppZWJhUueahOWIhuivjemAn+W6puaYr+WFtuS7llLor63oqIDliIbor43ljIXnmoQ1LTIw5YCN44CCDQoNCuWIhuivjee7k+adn+WQju+8jOWvueS6juS4jemcgOimgeeahOW8leaTjuWPqumcgOimgeeUqHJtKCnov5vooYzliKDpmaTvvIxS5pyJ6Ieq5Yqo55qE5Z6D5Zy+5Zue5pS25py65Yi277yM5Li65L2g6Kej5Yaz5YaF5a2Y566h55CG55qE5ZCO6aG+5LmL5b+n44CCDQoNCuWIhuivjeW3sue7j+WIhuWlve+8jOe7n+iuoeWIhuaekOaJjeaYr+acgOmHjeimgeeahOS7u+WKoeOAguWJg+WIgOW3sue7j+ejqOeguu+8jOaOpeS4i+adpeWwseWPr+S7peeUqFLmnaXlpITnkIbkuK3mloflrZfnrKbkuobjgIINCg0KYGBge3IgdXNlckRpY3R9DQptaXhzZWcyID0gd29ya2VyKHR5cGUgID0gIm1peCIsIA0KICAgICAgICAgICAgICAgICBkaWN0ID0gIi9ob21lL3JzdHVkaW8vUi94ODZfNjQtcGMtbGludXgtZ251LWxpYnJhcnkvMy4xL2ppZWJhUkQvZGljdC9qaWViYS5kaWN0LnV0ZjgiLCANCiAgICAgICAgICAgICAgICAgaG1tICAgPSAiL2hvbWUvcnN0dWRpby9SL3g4Nl82NC1wYy1saW51eC1nbnUtbGlicmFyeS8zLjEvamllYmFSRC9kaWN0L2htbV9tb2RlbC51dGY4IiwgDQogICAgICAgICAgICAgICAgIHVzZXIgID0gIi9ob21lL3JzdHVkaW8vUi94ODZfNjQtcGMtbGludXgtZ251LWxpYnJhcnkvMy4xL2ppZWJhUkQvZGljdC91c2VyLmRpY3QudXRmOCIsIA0KICAgICAgICAgICAgICAgICBkZXRlY3Q9VCwgIHN5bWJvbCA9IEYsIA0KICAgICAgICAgICAgICAgICBsaW5lcyA9IDFlKzA1LCBvdXRwdXQgPSBOVUxMDQogICAgICAgICAgICAgICAgICkgIA0KIyBkZXRlY3Qg6Ieq5Yqo5qOA5p+l5paH5Lu257yW56CB77yMbGluZXPkuIDmrKHor7vlj5bmlofku7bnmoTooYzmlbANCg0KDQoj5L+u5pS5IHVzZXIudXRmOA0KIyBS6K+t6KiADQojIFLnmoTmnoHlrqLnkIbmg7MNCiMg5aSn5pWw5o2uDQojIOaVsOaNrg0KDQoj5Yqg6L29dXNlci51dGY4DQojIHdrID0gd29ya2VyKHVzZXI9J3VzZXIudXRmOCcpDQoNCiMg6L6T5Ye6d29ya2Vy55qE6K6+572uDQptaXhzZWcyDQoj6L6T5Ye657uT5p6c5aaC5LiL77ya55WlDQpgYGANCg0KIyMjIOWPr+S7peiHquWumuS5ieeUqOaIt+ivjeW6kw0KDQpgYGB7ciB1c2VyIGRpY3Rpb25hcnl9DQppbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpDQppbnN0YWxsLnBhY2thZ2VzKCJzdHJpbmdpIikNCmluc3RhbGwucGFja2FnZXMoInBiYXBwbHkiKQ0KaW5zdGFsbC5wYWNrYWdlcygiUmNwcCIpDQppbnN0YWxsLnBhY2thZ2VzKCJSY3BwUHJvZ3Jlc3MiKQ0KbGlicmFyeShkZXZ0b29scykNCmluc3RhbGxfZ2l0aHViKCJxaW53Zi9jaWRpYW4iKQ0KDQoNCiMgZGVjb2RlIHNjZWwgZGljdGlvbmFyeQ0KZGVjb2RlX3NjZWwoc2NlbCA9ICLnu4bog57or43lupPot6/lvoQiLCBvdXRwdXQgPSAi6L6T5Ye65paH5Lu26Lev5b6EIiwgY3BwID0gIFRSVUUpDQoNCmRlY29kZV9zY2VsKHNjZWwgPSAi57uG6IOe6K+N5bqT6Lev5b6EIixvdXRwdXQgPSAi6L6T5Ye65paH5Lu26Lev5b6EIixjcHAgPSAgRkFMU0UsIHByb2dyZXNzID0gVFJVRSkNCg0KIyDovpPlh7rosIPor5Xkv6Hmga8NCmRlY29kZV9zY2VsKHNjZWwgPSAi57uG6IOe6K+N5bqT6Lev5b6EIiwgb3V0cHV0ID0gIui+k+WHuuaWh+S7tui3r+W+hCIsIGNwcCA9IEZBTFNFLCBwcm9ncmVzcyA9IFRSVUUsIHJkZWJ1ZyA9IFRSVUUpDQoNCiMgc3lzdGVtIGRpY3Qgd2l0aCBmcmVxdWVuY3kNCmRlY29kZV9zY2VsKCLnu4bog57or43lupPot6/lvoQiLCBvdXRwdXQgPSAi6L6T5Ye65paH5Lu26Lev5b6EIiwgc3lzZGljdF9mcmVxID0gMSkNCg0KIyBsb2FkL2FkZCB1c2VyIGRpY3Rpb25hcnkNCiMjIOivu+WPlueUqOaIt+ivjeWFuA0KDQpsb2FkX3VzZXJfZGljdChmaWxlUGF0aCA9ICLnlKjmiLfor43lhbjot6/lvoQiLCBkZWZhdWx0X3RhZyA9ICLpu5jorqTmoIforrAiKQ0KDQojIyDor7vlj5bns7vnu5/or43lhbgNCmxvYWRfc3lzX2RpY3QoZmlsZVBhdGggPSAi57O757uf6K+N5YW46Lev5b6EIikNCg0KIyMg5aKe5Yqg55So5oi36K+N5YW46K+NDQoNCmFkZF91c2VyX3dvcmRzKGRpY3QgPSAibG9hZF91c2VyX2RpY3Qg6K+75Y+W55qE6K+N5YW4Iiwgd29yZHMgPSAiVVRGLTgg57yW56CB5paH5pys5ZCR6YePIiwgdGFncyA9ICLmoIforrAiKQ0KDQojIyDlop7liqDns7vnu5/or43lhbjor40NCg0KYWRkX3N5c193b3JkcyhkaWN0ID0gImxvYWRfc3lzX2RpY3Qg6K+75Y+W55qE6K+N5YW4Iiwgd29yZHMgPSAiVVRGLTgg57yW56CB5paH5pys5ZCR6YePIiwgZnJlcSA9ICLor43popEiLCB0YWdzID0gIuagh+iusCIpDQoNCiMjIOWIoOmZpOivjeWFuOivjQ0KDQpyZW1vdmVfd29yZHMoZGljdCA9ICJsb2FkX3VzZXJfZGljdCDmiJYgbG9hZF9zeXNfZGljdCDor7vlj5bnmoTor43lhbgiLCB3b3JkcyA9ICJVVEYtOCDnvJbnoIHmlofmnKzlkJHph48iKQ0KDQojIyDlhpnlhaUNCg0Kd3JpdGVfZGljdChkaWN0ID0gImxvYWRfdXNlcl9kaWN0IOaIliBsb2FkX3N5c19kaWN0IOivu+WPlueahOivjeWFuCIsIG91dHB1dCA9ICLovpPlh7rot6/lvoQiKQ0KDQoodXNlcmQgPSBsb2FkX3VzZXJfZGljdChqaWViYVI6OlVTRVJQQVRIKSkNCg0KdXNlcmQgPSBhZGRfdXNlcl93b3Jkcyh1c2VyZCwgZW5jMnV0ZjgoIua1i+ivlSIpLCAidiIpDQoNCndyaXRlX2RpY3QodXNlcmQsIGppZWJhUjo6VVNFUlBBVEgpDQoNCih1c2VyZCA9IGxvYWRfdXNlcl9kaWN0KGppZWJhUjo6VVNFUlBBVEgpKQ0KdXNlcmQgPSByZW1vdmVfd29yZHModXNlcmQsIGVuYzJ1dGY4KGMoIua1i+ivlSIsIuiTnee/lCIpKSkNCg0Kd3JpdGVfZGljdCh1c2VyZCwgamllYmFSOjpVU0VSUEFUSCkNCg0KKHVzZXJkID0gbG9hZF91c2VyX2RpY3QoamllYmFSOjpVU0VSUEFUSCkpDQoNCg0KU2hvd0RpY3RQYXRoKCkgICMg5pi+56S66K+N5YW46Lev5b6EDQpFZGl0RGljdCgpICAgICAgIyDnvJbovpHnlKjmiLfor43lhbgNCj9FZGl0RGljdCgpICAgICAjIOaJk+W8gOW4ruWKqeezu+e7nw0KDQojIFVzYWdlIOS9v+eUqOaWueazlQ0KZWRpdF9kaWN0KG5hbWUgPSAidXNlciIpICMg6L+Z5Liq5pa55rOV6L+H5pe25LqGDQpFZGl0RGljdChuYW1lID0gInVzZXIiKSANCg0KIyBBcmd1bWVudHMg5Y+C5pWwDQojIG5hbWUgICAgDQojIHRoZSBuYW1lIG9mIGRpY3Rpb25hcnkgaW5jbHVkaW5nIHVzZXIsIHN5c3RlbSwgc3RvcF93b3JkLg0KYGBgDQoNCiMjIyDmkJzni5for43lhbgNCg0K5Zyo5a6e6ZmF5L2/55So5Lit77yMamllYmFS6buY6K6k5o+Q5L6b55qE55So5oi36K+N5YW45Y+q5pyJNeS4quWNleivje+8jOWkqueugOWNleS6hu+8jOiCr+WumuaYr+S4jeWkn+eUqOeahOOAguaIkeS7rOWPr+S7peeUqOaQnOeLl+ivjeWFuO+8jOadpeS4sOWvjOeUqOaIt+iHquW3seeahOivjeW6k+OAguaOpeS4i+adpe+8jOiuqeaIkeS7rOmFjee9ruaQnOeLl+ivjeWFuOOAguS9oOmcgOimgeWuieijheS4gOS4quaQnOeLl+i+k+WFpeazle+8jOWFt+S9k+eahOWuieijhei/h+eoi+S4jeWGjeino+mHiuOAgg0KDQrmiJHlronoo4XnmoTmmK/mkJzni5fkupTnrJTovpPlhaXms5XvvIzmib7liLDmkJzni5fnmoTlronoo4Xnm67lvZXvvIzlubbmib7liLDor43lhbjmlofku7bjgILmiJHnmoTmkJzni5for43lhbjvvIzlnKjkuIvpnaLnmoTlronoo4XkvY3nva7jgIINCg0KYEM6XFByb2dyYW0gRmlsZXMgKHg4NilcU29nb3VXQklucHV0XDIuMS4wLjEyODhcc2NkXDE3OTYwLnNjZWxgDQoNCuaKijE3OTYwLnNjZWzmlofku7blpI3liLbliLDoh6rlt7HnmoTpobnnm67nm67lvZXph4zvvIznlKjmlofmnKznvJbovpHlmajmiZPlvIDmlofku7bvvIzlj5HnjrDmmK/kuozov5vliLbnmoTjgILpgqPkuYjmiJHpnIDopoHnlKjlt6Xlhbfov5vooYzovazmjaLvvIzmiorkuozov5vliLbnmoTor43lhbjovazmiJDmiJHku6zlj6/ku6Xkvb/nlKjnmoTmlofmnKzmlofku7bjgIJqaWViYVLljIXnmoTkvZzogIXvvIzlkIzml7blvIDlj5HkuobkuIDkuKpgY2lkaWFuYOmhueebru+8jOWPr+S7pei9rOaNouaQnOeLl+eahOivjeWFuO+8jOmCo+S5iOaIkeS7rOWPqumcgOimgeWuieijhWBjaWRpYW5g5YyF5Y2z5Y+v44CCDQoNCuWuieijhWNpZGlhbumhueebrg0KYGBge3IgY2lkaWFufQ0KaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQ0KaW5zdGFsbC5wYWNrYWdlcygic3RyaW5naSIpDQppbnN0YWxsLnBhY2thZ2VzKCJwYmFwcGx5IikNCmluc3RhbGwucGFja2FnZXMoIlJjcHAiKQ0KaW5zdGFsbC5wYWNrYWdlcygiUmNwcFByb2dyZXNzIikNCmxpYnJhcnkoZGV2dG9vbHMpDQppbnN0YWxsX2dpdGh1YigicWlud2YvY2lkaWFuIikNCmxpYnJhcnkoY2lkaWFuKQ0KDQojIOi9rOaNouS6jOi/m+WItuivjeWFuOWIsOaWh+acrOaWh+S7tuOAgg0KIyDovazmjaINCmRlY29kZV9zY2VsKHNjZWwgPSAiLi8xNzk2MC5zY2VsIixjcHAgPSBUUlVFKQ0KIyBvdXRwdXQgZmlsZTogLi8xNzk2MC5zY2VsXzIwMTYtMDctMjFfMDBfMjJfMTEuZGljdA0KDQojIOafpeeci+eUn+aIkOeahOivjeWFuOaWh+S7tg0KI2RlbGV0ZSBmaWxlRW5jb2Rpbmc9J1VURi04Jw0Kc2NhbihmaWxlPSIuLzE3OTYwLnNjZWxfMjAxNi0wNy0yMV8wMF8yMl8xMS5kaWN0IiwNCiAgICAgIHdoYXQ9Y2hhcmFjdGVyKCksbmxpbmVzPTUwLHNlcD0nXG4nLA0KICAgICAgZW5jb2Rpbmc9J3V0Zi04JykNCmBgYA0KDQrmjqXkuIvmnaXvvIznm7TmjqXmiormkJzni5for43lhbjphY3nva7liLDmiJHku6znmoTliIbor43lupPkuK3vvIzlsLHlj6/ku6Xnm7TmjqXkvb/nlKjkuobjgIINCg0K5oqK5pCc54uX6K+N5YW45paH5Lu25pS55ZCN77yM5LuOYDE3OTYwLnNjZWxfMjAxNi0wNy0yMV8wMF8yMl8xMS5kaWN0YOWIsGB1c2VyLmRpY3QudXRmOGDvvIznhLblkI7mm7/mjaJgRDpcdG9vbFxSLTMuMi4zXGxpYnJhcnlcamllYmFSRFxkaWN0YOebruW9leS4i+mdomDnmoR1c2VyLmRpY3QudXRmOGDjgILov5nmoLfpu5jorqTnmoTnlKjmiLfor43lhbjvvIzlsLHmmK/mkJzni5for43lhbjkuobjgILlvojphbflkKfvvIENCg0KDQojIyMg5YGc5q2i6K+N6L+H5rukDQoNCuWBnOatouivjeWwseaYr+WIhuivjei/h+eoi+S4re+8jOaIkeS7rOS4jemcgOimgeS9nOS4uue7k+aenOeahOivje+8jOWDj+iLseaWh+eahOivreWPpeS4reacieW+iOWkmueahGEsdGhlLG9yLGFuZOetie+8jOS4reaWh+ivreiogOS4reS5n+acieW+iOWkmu+8jOavlOWmgiDnmoTvvIzlnLDvvIzlvpfvvIzmiJHvvIzkvaDvvIzku5bjgILov5nkupvor43lm6DkuLrkvb/nlKjpopHnjofov4fpq5jvvIzkvJrlpKfph4/lh7rnjrDlnKjkuIDmrrXmlofmnKzkuK3vvIzlr7nkuo7liIbor43lkI7nmoTnu5PmnpzvvIzlnKjnu5/orqHor43popHnmoTml7blgJnkvJrlop7liqDlvojlpJrnmoTlmarpn7PvvIzmiYDku6XmiJHku6zpgJrluLjpg73kvJrlsIbov5nkupvor43ov5vooYzov4fmu6TjgIINCg0K5ZyoamllYmFS5Lit77yM6L+H5ruk5YGc5q2i6K+N5pyJMuenjeaWueazle+8jOS4gOenjeaYr+mAmui/h+mFjee9rmBzdG9wX3dvcmRg5paH5Lu277yM5Y+m5LiA56eN5piv5L2/55SoYGZpbHRlcl9zZWdtZW50KClg5Ye95pWw44CCDQoNCummluWFiOaIkeS7rOWFiOadpeeci++8jOmAmui/h+mFjee9rmBzdG9wX3dvcmRg5paH5Lu255qE5pa55rOV44CC5paw5bu65LiA5LiqYHN0b3Bfd29yZC50eHRg5paH5Lu244CCDQpgYGB7ciBzdG9wX3dvcmQudHh0fQ0KIyDmiJENCiMg5oiR5pivDQoNCg0KIyDliqDovb3liIbor43lvJXmk47vvIzlubbphY3nva7lgZzmraLor43ov4fmu6TjgIINCndrID0gd29ya2VyKHN0b3Bfd29yZD0nc3RvcF93b3JkLnR4dCcpDQpzZWdtZW50PC13a1si5oiR5piv44CKUueahOaegeWuoueQhuaDs+OAi+WbvuS5puS9nOiAhSJdDQpzZWdtZW50DQojIFsxXSAiUiIgICAgIueahCIgICAi5p6B5a6iIiAi55CG5oOzIiAi5Zu+5LmmIiAi5L2c6ICFIg0KDQpgYGANCg0K5LiK6Z2i55qE5paH5pys77yM5oiR5Lus5oqKIuaIkeaYryLpgJrov4flgZzmraLor43ov5vooYzkuobov4fmu6TjgILlpoLmnpzov5jmg7Pov4fmu6TigJzkvZzogIXigJ3kuIDor43vvIzlj6/ku6XliqjmgIHnmoTosIPnlKhgZmlsdGVyX3NlZ21lbnQoKWDlh73mlbANCmBgYHtyIGZpbHRlcl9zZWdtZW50fQ0KZmlsdGVyIDwtYyAoIuS9nOiAhSIpDQpmaWx0ZXJfc2VnbWVudChzZWdtZW50LGZpbHRlcikNCiMgWzFdICJSIiAgICAi55qEIiAgICLmnoHlrqIiICLnkIbmg7MiICLlm77kuaYiDQoNCmBgYA0KDQoNCg0KIyMgOC4g6K+N5LqR5Zu+LXdvcmQgY2xvdWQNCg0KIyMjIDguMSDnuqLmpbzmoqYNCmBgYHtyIGppZWJhUiAmIHdvcmRjbG91ZH0NCmxpYnJhcnkoamllYmFSKQ0KbGlicmFyeSh3b3JkY2xvdWQyKQ0KDQoj6K+75YWl5pWw5o2u5YiG6ZqU56ym5piv4oCYXG7igJnvvIzlrZfnrKbnvJbnoIHmmK/igJhVVEYtOOKAme+8jHdoYXQ9JyfooajnpLrku6XlrZfnrKbkuLLnsbvlnovor7vlhaUNCmYgPC0gc2NhbignRTovMDMtRG93bmxvYWQvR2l0aHViL+e6oualvOaipi1VVEYtOC50eHQnLHNlcD0nXG4nLHdoYXQ9JycsZW5jb2Rpbmc9IlVURi04IikNCg0KIyDliJ3lp4vljJYNCm1peHNlZyA8LSB3b3JrZXIoKQ0Kc2VnbWVudChoZWFkKGYpLCBtaXhzZWcpDQojICBbMV0gIue6oualvOaipiIgICAi5pu56Zuq6Iq5IiAgICLokZciICAgICAgICLmiYvmnLoiICAgICAi55S15a2Q5LmmIiAgDQojICBbNl0gIuWkp+WtpueUnyIgICAi5bCP6K+0572RIiAgICJUeHQiICAgICAgIueJiCIgICAgICAgIumYheivuyIgICAgDQojIFsxMV0gIumYheivuyIgICAgICLkvZzlk4EiICAgICAi5pu0IiAgICAgICAi5aSaIiAgICAgICAi6K+3IiAgICAgIA0KIyBbMTZdICLorr/pl64iICAgICAiaHR0cCIgICAgICJ3d3ciICAgICAgImR4c3hzIiAgICAiY29tIiAgICAgDQojIFsyMV0gIuS5puexjSIgICAgICLku4vnu40iICAgICAi5Lit5Zu9IiAgICAgIuWPpOS7oyIgICAgICLlm5vlpKflkI3okZciDQojIFsyNl0gIuS5i+S4gCIgICAgICLnq6DoioIiICAgICAi5YaF5a65IiAgICAgIuW8gOWniyIgICAgICLkuIrljbciICAgIA0KIyBbMzFdICLnrKzkuIDlm54iICAgIueUhOWjq+makCIgICAi5qKm5bm7IiAgICAgIuivhumAmueBtSIgICAi44CAIiAgICAgIA0KIyBbMzZdICLotL7pm6jmnZEiICAgIumjjuWwmCIgICAgICLmgIAiICAgICAgICLpl7rnp4AiICAgIA0KDQojIERhdGEgTWFudXB1bGF0aW9uDQojIOWFqOaWh+WIhuivjQ0Kc2VnIDwtIHNlZ21lbnQoZiwgbWl4c2VnKQ0KIyDmn6XnnIvliIbor43lkI7nmoTlkJHph4/nmoTplb/luqYNCmxlbmd0aChzZWcpDQojIFsxXSA0NzA5OTUNCg0KIyDmn6XnnIvliIbor43lkI7nmoTlkJHph4/nmoTliY01MOeahOivjemikee7n+iuoQ0Kc29ydCh0YWJsZShzZWcpLCBkZWNyZWFzaW5nID0gVClbMTo1MF0NCiMgICAgIOS6hiAgICAg55qEICAgICDmiJEgICAgIOS7liAgICAg5L2gICAgICDkuZ8gICAgIOaYryAgICAg6K+0ICAgICDjgIAgDQojICAxNzc3NSAgMTM1MDUgICA3MjY4ICAgNjA3NyAgIDU4MTAgICA1NzQ1ICAgNTYyNCAgIDUzNzIgICA1MTU4IA0KIyAgICAg5Y+IICAgICDpgZMgICDlrp3njokgICAgIOedgCAgICAg5p2lICAgICDov5kgICAgIOS6uiAgICAg5LiNICAgICDljrsgDQojICAgNDk1NSAgIDQ0MjUgICAzNzQ4ICAgMzY1MSAgIDMyNTQgICAzMDAyICAgMzAwMSAgIDI5NTYgICAyOTUwIA0KIyAgICAg5L6/ICAgICDlnKggICAgIOaciSAgICAg6YO9ICAgICDlsLEgICAgIOWPqyAgICAg6YKjICAgICDlkKwgICAgIOi0viANCiMgICAyOTI3ICAgMjc4NCAgIDI1OTcgICAyNTMzICAgMjM5OCAgIDE4OTkgICAxODE4ICAgMTc0OCAgIDE2NDYgDQojICAg5LuA5LmIICAgICDop4EgICAgIOetiSAgICAg6KaBICAgICDov5ggICDkuIDkuKogICDnrJHpgZMgICAgIOWlvSAgICAg5ZGiIA0KIyAgIDE2MTMgICAxNTgxICAgMTU2NSAgIDE1MjEgICAxNTA1ICAgMTQ1MCAgIDE0NDcgICAxNDQyICAgMTQxMiANCiMgICAgIOWPqiAgICAg5ZKMICAg5oiR5LusICAg6YKj6YeMICAgICDkuIogICAgIOWIsCAgICAg5YS/ICAgICDlgJIgICAgIOWboCANCiMgICAxMjk1ICAgMTIyMiAgIDEyMjEgICAxMTc0ICAgMTEyOCAgIDExMjIgICAxMTA3ICAgMTA4NSAgIDEwNDIgDQojIOeOi+Wkq+S6uiAgICAg5omNICAg5L2g5LusICAg5aaC5LuKICAgICDku6wgDQojICAgMTAxMSAgIDEwMTAgICAxMDA5ICAgIDk5OSAgICA5ODUNCg0KI+WNleS4queahOWtl+WkquWkmu+8jOayoeacieaEj+S5iQ0KDQpzZWcgPC0gc2VnW25jaGFyKHNlZyk+MSAmIG5jaGFyKHNlZykgPCA3XSAj5Y676Zmk5a2X56ym6ZW/5bqm5bCP5LqOMueahOivjeivrSwmPDcNCnNvcnQodGFibGUoc2VnKSwgZGVjcmVhc2luZyA9IFQpWzE6NTBdDQojIOWuneeOiSAgIOS7gOS5iCAgIOS4gOS4qiAgIOeskemBkyAgIOaIkeS7rCAgIOmCo+mHjCDnjovlpKvkurogICDkvaDku6wgICDlpoLku4ogDQojICAgMzc0OCAgIDE2MTMgICAxNDUwICAgMTQ0NyAgIDEyMjEgICAxMTc0ICAgMTAxMSAgIDEwMDkgICAgOTk5IA0KIyAgIOivtOmBkyAgIOefpemBkyDogIHlpKrlpKogICDotbfmnaUgICDlp5HlqJggICDov5nph4wgICDlh7rmnaUgICDku5bku6wgICDkvJfkurogDQojICAgIDk3MyAgICA5NjcgICAgOTY2ICAgIDk0NCAgICA5NDEgICAgOTM1ICAgIDkyMiAgICA4OTUgICAgODcwIA0KIyAgIOiHquW3sSAgIOS4gOmdoiAgIOWkquWkqiAgIOWPquingSAgIOaAjuS5iCAgIOWltuWltiAgIOS4pOS4qiAgIOayoeaciSAgIOS4jeaYryANCiMgICAgODM2ICAgIDgyOSAgICA4MjUgICAgNzg5ICAgIDc3NyAgICA3NzIgICAgNzY5ICAgIDc2MSAgICA3MjkgDQojICAg5LiN55+lICAg6L+Z5LiqICAg5ZCs6KeBICAg6L+Z5qC3ICAg6LS+5q+NICAg6L+b5p2lICAg5ZKx5LusICAg5ZGK6K+JICAg5bCx5pivIA0KIyAgICA3MDggICAgNjk3ICAgIDY4OSAgICA2NDYgICAgNjMyICAgIDYzMiAgICA2MDUgICAgNjAyICAgIDYwMSANCiMgICDkuJzopb8gICDlubPlhL8gICDlm57mnaUgICDlj6rmmK8gICDlpKflrrYgICDogIHniLcgICDlj6rlvpcgICDkuKvlpLQgICDov5nkupsgDQojICAgIDU5OSAgICA1ODggICAgNTY2ICAgIDU0NCAgICA1NDMgICAgNTQwICAgIDUzMSAgICA1MDkgICAgNTA0IA0KIyAgIOS4jeaVoiAgIOWHpOWnkCAgIOWHuuWOuyDlh6Tlp5DlhL8gICDmiYDku6UgDQojICAgIDQ5NiAgICA0OTIgICAgNDgzICAgIDQ3MCAgICA0NjYgDQoNCiNzZWcgPC0gdGFibGUoc2VnKSDnu5/orqHor43popENCg0Kc2VnIDwtIHNlZ1shZ3JlcGwoJ1swLTldKycsbmFtZXMoc2VnKSldICPljrvpmaTmlbDlrZcNCmxlbmd0aChzZWcpICPmn6XnnIvlpITnkIblrozlkI7liankvZnnmoTor43mlbANCg0KDQoj6ZmN5bqP5o6S5bqP77yM5bm25o+Q5Y+W5Ye6546w5qyh5pWw5pyA5aSa55qE5YmNMTAw5Liq6K+N6K+tLCDmn6XnnIsxMDDkuKror43popHmnIDpq5jnmoQNClRvcDEwMCA8LSBzb3J0KHRhYmxlKHNlZyksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjEwMF07VG9wMTAwDQojIOWuneeOiSAgIOS7gOS5iCAgIOS4gOS4qiAgIOeskemBkyAgIOaIkeS7rCAgIOmCo+mHjCDnjovlpKvkurogICDkvaDku6wgICDlpoLku4ogDQojICAgMzc0OCAgIDE2MTMgICAxNDUwICAgMTQ0NyAgIDEyMjEgICAxMTc0ICAgMTAxMSAgIDEwMDkgICAgOTk5IA0KIyAgIOivtOmBkyAgIOefpemBkyDogIHlpKrlpKogICDotbfmnaUgICDlp5HlqJggICDov5nph4wgICDlh7rmnaUgICDku5bku6wgICDkvJfkurogDQojICAgIDk3MyAgICA5NjcgICAgOTY2ICAgIDk0NCAgICA5NDEgICAgOTM1ICAgIDkyMiAgICA4OTUgICAgODcwIA0KIyAgIOiHquW3sSAgIOS4gOmdoiAgIOWkquWkqiAgIOWPquingSAgIOaAjuS5iCAgIOWltuWltiAgIOS4pOS4qiAgIOayoeaciSAgIOS4jeaYryANCiMgICAgODM2ICAgIDgyOSAgICA4MjUgICAgNzg5ICAgIDc3NyAgICA3NzIgICAgNzY5ICAgIDc2MSAgICA3MjkgDQojICAg5LiN55+lICAg6L+Z5LiqICAg5ZCs6KeBICAg6L+Z5qC3ICAg6LS+5q+NICAg6L+b5p2lICAg5ZKx5LusICAg5ZGK6K+JICAg5bCx5pivIA0KIyAgICA3MDggICAgNjk3ICAgIDY4OSAgICA2NDYgICAgNjMyICAgIDYzMiAgICA2MDUgICAgNjAyICAgIDYwMSANCiMgICDkuJzopb8gICDlubPlhL8gICDlm57mnaUgICDlj6rmmK8gICDlpKflrrYgICDogIHniLcgICDlj6rlvpcgICDkuKvlpLQgICDov5nkupsgDQojICAgIDU5OSAgICA1ODggICAgNTY2ICAgIDU0NCAgICA1NDMgICAgNTQwICAgIDUzMSAgICA1MDkgICAgNTA0IA0KIyAgIOS4jeaVoiAgIOWHpOWnkCAgIOWHuuWOuyDlh6Tlp5DlhL8gICDmiYDku6Ug6Jab5aeo5aaIICAg5LiN6L+HICAg55qE6K+dICAg5LiN5aW9IA0KIyAgICA0OTYgICAgNDkyICAgIDQ4MyAgICA0NzAgICAgNDY2ICAgIDQ1MyAgICA0NDggICAgNDQ1ICAgIDQ0NCANCiMgICDlp5Dlp5AgICDmjqLmmKUgICDpuLPpuK8gICDkuIDml7YgICDkuI3og70gICDov4fmnaUgICDljrvkuoYgICDlv4Pph4wgICDkuozniLcgDQojICAgIDQ0MiAgICA0MzIgICAgNDI1ICAgIDQyMSAgICA0MjAgICAgNDIwICAgIDQwNCAgICA0MDIgICAgMzk5IA0KIyAgIOWmguatpCAgIOS7iuaXpSAgIOmTtuWtkCAgIOWHoOS4qiAgIOetlOW6lCAgIOS6jOS6uiAgIOWunemSlyAgIOi/mOaciSAgIOWPqueuoSANCiMgICAgMzc2ICAgIDM3MCAgICAzNjYgICAgMzU4ICAgIDM1OCAgICAzNTYgICAgMzU2ICAgIDM1MCAgICAzNDMgDQojICAg6L+Z5LmIICAg6bub546JICAg6K+06K+dICAg5LiA5ZueICAg5pm06ZuvICAg6YKj6L65ICAg5rmY5LqRICAg5aSW5aS0ICAg6L+Z6K+dIA0KIyAgICAzNDMgICAgMzQyICAgIDM0MCAgICAzMzggICAgMzMyICAgIDMzMCAgICAzMjQgICAgMzE3ICAgIDMxNyANCiMgICDotL7nkI8gICDmiZPlj5EgICDoh6rnhLYgICDooq3kurogICDku4rlhL8gICDnvaLkuoYgICDlsYvph4wg5YiY5ael5aelICAg6YKj5LqbIA0KIyAgICAzMTMgICAgMzEwICAgIDMwNiAgICAyOTggICAgMjk3ICAgIDI5NiAgICAyOTUgICAgMjkzICAgIDI5MyANCiMgICDlkKzor7Qg5bCP5Lir5aS0IOmCouWkq+S6uiDmnpfpu5vnjokgICDlpoLkvZUgICDpl67pgZMgICDnnIvop4EgICDlprnlprkgICDkurrlrrYgDQojICAgIDI5MCAgICAyODcgICAgMjg0ICAgIDI4MCAgICAyNzkgICAgMjc3ICAgIDI3NCAgICAyNzIgICAgMjY5IA0KIyAgIOS4jeeUqCANCiMgICAgMjY0IA0KDQoNCg0KIyBUb3AxMDDlgZror43kupENCmpwZWcoIkU6LzAzLURvd25sb2FkL0dpdGh1Yi9SZWRDaGFtYmVyX1RvcDEwMC5qcGciLCB3aWR0aCA9IDUwMCwgaGVpZ2h0ID0gNTAwKQ0KcGFyKGJnID0gImJsYWNrIikNCndvcmRjbG91ZDIoVG9wMTAwLCBzaXplID0xLCBjb2xvciA9ICdyYW5kb20tbGlnaHQnLCBzaGFwZSA9ICdjYXJkaW9pZCcpDQojID93b3JkY2xvdWQyKCkNCmRldi5vZmYoKQ0KDQpgYGANCg0KIyMjIDguMiB3b3JkY2xvdWQyIGV4YW1wbGUgZGVtbw0KDQpgYGB7ciB3b3JkY2xvdWQyIGV4Mn0NCmxpYnJhcnkoIndvcmRjbG91ZDIiKQ0Kd29yZGNsb3VkMihkZW1vRnJlcSwgc2l6ZSA9IDEsc2hhcGUgPSAnc3RhcicpDQoNCndvcmRjbG91ZDIoZGVtb0ZyZXEsIHNpemUgPSAyLCBtaW5Sb3RhdGlvbiA9IC1waS8yLCBtYXhSb3RhdGlvbiA9IC1waS8yKQ0KDQp3b3JkY2xvdWQyKGRlbW9GcmVxLCBzaXplID0gMiwgbWluUm90YXRpb24gPSAtcGkvNiwgbWF4Um90YXRpb24gPSAtcGkvNiwNCiAgcm90YXRlUmF0aW8gPSAxKQ0KDQp3b3JkY2xvdWQyKGRlbW9GcmVxQywgc2l6ZSA9IDIsIGZvbnRGYW1pbHkgPSAi5b6u6L2v6ZuF6buRIiwNCiAgICAgICAgICAgY29sb3IgPSAicmFuZG9tLWxpZ2h0IiwgYmFja2dyb3VuZENvbG9yID0gImdyZXkiKQ0KDQojIyBTeXMuc2V0bG9jYWxlKCJMQ19DVFlQRSIsImVuZyIpDQp3b3JkY2xvdWQyKGRlbW9GcmVxQywgc2l6ZSA9IDIsIGZvbnRGYW1pbHkgPSAi5b6u6L2v6ZuF6buRIiwNCiAgICAgICAgICAgY29sb3IgPSAicmFuZG9tLWxpZ2h0IiwgYmFja2dyb3VuZENvbG9yID0gImdyZXkiKQ0KYGBgDQoNCg0KDQojIyDlj4LogIPotYTmlpnvvJoNCg0KKiBb57qi5qW85qKm5pCc54uX6K+N5bqTXShodHRwOi8vcGlueWluLnNvZ291LmNvbS9kaWN0L3NlYXJjaC9zZWFyY2hfbGlzdC8lQkElRUMlQzIlQTUlQzMlQ0Uvbm9ybWFsLzEpDQoNCiogW2ppZWJhUuS4reaWh+WIhuivjeW/q+mAn+WFpemXqF0oaHR0cDovL2Jsb2cuY3Nkbi5uZXQvc29uZ3poaWxpYW4yMi9hcnRpY2xlL2RldGFpbHMvNDkyNTA0ODkpDQoNCiogW2h0dHA6Ly9ibG9nLmZlbnMubWUvci13b3JkLWppZWJhci9dKGh0dHA6Ly9ibG9nLmZlbnMubWUvci13b3JkLWppZWJhci8pDQoNCiogW2ppZWJhUl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2ppZWJhUi9pbmRleC5odG1sKQ0KDQoqIFtqaWViYVIgVHV0b3VyaWEtUERGXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvamllYmFSL2ppZWJhUi5wZGYpDQoNCiogW1IgbmV3cyBhbmQgdHV0b3JpYWxzIFIgYmxvZ2dlcnNdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLykNCg0KKiBbQ1JBTiBjbG91ZF0oaHR0cHM6Ly9jbG91ZC5yLXByb2plY3Qub3JnLykNCg0KKiBbR2FycmV0dCAmIEhhZGxleTogUiBGb3IgRGF0YSBTY2llbmNlXShodHRwOi8vcjRkcy5oYWQuY28ubnovKQ0KDQoqIFtXcml0ZSBIVE1MLCBQREYsIGVQdWIsIGFuZCBLaW5kbGUgYm9va3Mgd2l0aCBSIE1hcmtkb3duXShodHRwczovL2Jvb2tkb3duLm9yZy8pDQoNCiogW1RoZSB0aWR5dmVyc2Ugc3R5bGUgZ3VpZGVdKGh0dHA6Ly9zdHlsZS50aWR5dmVyc2Uub3JnL2luZGV4Lmh0bWwpDQoNCiogW2h0dHA6Ly90aWR5dmVyc2Uub3JnL10oaHR0cDovL3RpZHl2ZXJzZS5vcmcvKQ0KDQoNCg==