版本

查看版本、操作系统和已加载包

sessionInfo()

自动升级最新版本

installr::updateR()
# 出现一个对话框,是否将 libraries 复制到新版 R 中,选 yes

MRO

如果有大 size 的向量和矩阵运算,建议安装和使用 Microsoft R Open. 该版本对向量和矩阵的向量化运算(对所有元素分别执行)进行了大幅优化,无需引入任何包,就能自动调用多核并行计算,可提高运算速度数十倍。

这也意味着,向量化运算是 R 最基本、最主要的代码风格

但是,Microsoft R Open 的某些包更新不够及时(如 rmarkdown)。为了 Knit 的方便,可以 Rstudio 选择 R 的最新版,在 VSCode 中用 Microsoft R Open 日常加速计算。

System interaction

Environment

R 中词法作用域的机理通过环境实现。

# 从内到外,显示所有环境层级
pryr::parenvs(all = TRUE)
#>    label                               name                  
#> 1  <environment: R_GlobalEnv>          ""                    
#> 2  <environment: package:cranlogs>     "package:cranlogs"    
#> 3  <environment: package:ggthemes>     "package:ggthemes"    
#> 4  <environment: package:xfun>         "package:xfun"        
#> 5  <environment: package:rootSolve>    "package:rootSolve"   
#> 6  <environment: package:numDeriv>     "package:numDeriv"    
#> 7  <environment: package:ivreg>        "package:ivreg"       
#> 8  <environment: package:zeallot>      "package:zeallot"     
#> 9  <environment: package:downloadthis> "package:downloadthis"
#> 10 <environment: package:htmlwidgets>  "package:htmlwidgets" 
#> 11 <environment: package:plotly>       "package:plotly"      
#> 12 <environment: package:kableExtra>   "package:kableExtra"  
#> 13 <environment: package:magrittr>     "package:magrittr"    
#> 14 <environment: package:see>          "package:see"         
#> 15 <environment: package:cowplot>      "package:cowplot"     
#> 16 <environment: package:ggtext>       "package:ggtext"      
#> 17 <environment: package:lmerTest>     "package:lmerTest"    
#> 18 <environment: package:lme4>         "package:lme4"        
#> 19 <environment: package:Matrix>       "package:Matrix"      
#> 20 <environment: package:performance>  "package:performance" 
#> 21 <environment: package:effectsize>   "package:effectsize"  
#> 22 <environment: package:emmeans>      "package:emmeans"     
#> 23 <environment: package:data.table>   "package:data.table"  
#> 24 <environment: package:bruceR>       "package:bruceR"      
#> 25 <environment: package:forcats>      "package:forcats"     
#> 26 <environment: package:stringr>      "package:stringr"     
#> 27 <environment: package:dplyr>        "package:dplyr"       
#> 28 <environment: package:purrr>        "package:purrr"       
#> 29 <environment: package:readr>        "package:readr"       
#> 30 <environment: package:tidyr>        "package:tidyr"       
#> 31 <environment: package:tibble>       "package:tibble"      
#> 32 <environment: package:ggplot2>      "package:ggplot2"     
#> 33 <environment: package:tidyverse>    "package:tidyverse"   
#> 34 <environment: package:stats>        "package:stats"       
#> 35 <environment: package:graphics>     "package:graphics"    
#> 36 <environment: package:grDevices>    "package:grDevices"   
#> 37 <environment: package:utils>        "package:utils"       
#> 38 <environment: package:datasets>     "package:datasets"    
#> 39 <environment: package:methods>      "package:methods"     
#> 40 <environment: 0x00000218b98f7538>   "Autoloads"           
#> 41 <environment: base>                 ""                    
#> 42 <environment: R_EmptyEnv>           ""

最外层的 R_EmptyEnv 是唯一没有父环境的环境。

每加载一个扩展包,这个包的环境都会插入搜索路径,成为全局环境新的父级环境,并覆盖之前加载的包的同名函数。

调用函数时,首先创造一个 runtime 环境,函数优先在 runtime 环境搜索用到的变量,搜索不到时,就到上一级环境搜索,直至最外层的 R_EmptyEnv

函数 runtime 环境的父环境,是函数第一次创建时所在的环境。如果函数是全局创建的(即使是通过 .R 脚本导入),其父环境就是全局环境。

函数储存变量,也储存在 runtime 环境中,只有返回值能传入调用它的环境。

这里有两个例外:

  1. x <<- 5 能将数据 5 储存到父环境的变量 x 中,如果父环境中不存在变量 x,会一直向上直至全局环境进行变量查找。若在查找过程中寻找到该名称的变量,就会进行赋值操作。否则,将在全局环境中创建变量并赋值。
  2. assign("var_name", value, envio = as.environment(pos)) 可以直接指定将 value 储存到哪个环境中。该函数的第一个参数是字符串,也可以是字符串向量,一次性赋值多个变量。
环境交互函数
pryr::parenvs(all = TRUE) 显示当前环境的层级,返回一个列表,从当前环境到 R_EmptyEnv
as.environment() 接受一个字符串环境名,返回对应的环境
environment() 返回当前 active 环境
globalenv()/.GlobalEnv 返回全局环境 R_GlobalEnv,这是最活跃、默认储存对象的环境
baseenv() 返回基环境 base
emptyenv() 返回空环境 R_EmptyEnv
parent.env() 返回参数环境的父环境
ls()/objects() 返回字符串向量,列出当前环境的所有对象
ls.str(mode="list", pattern="^\\w$") 显示当前环境的结构。mode 参数过滤对象类型,pattern 参数为正则表达式,过滤对象名
remove()/rm() 删除环境中一个或更多个对象。
rm(list=ls()) 删除环境中所有对象

全局选项

getOption(x),查看全局选项

options(...),设定全局选项。如 digits=7, 设定显示数字的位数。warn=0, 可以改为1,在警告产生时立即显示。

文件系统管理

Working Directory
getwd()/setwd() 查看/设定当前工作目录
R.home() R 软件的目录
list.dirs() 查看当前目录的所有子孙目录(递归查看)
dir([path, ][pattern, ][all.files = FALSE]) 查看当前/指定目录中的文件和子目录,pattern为正则,all.files 控制是否显示隐藏文件
system("tree") 查看当前目录的树状层级结构
dir.create()dir.create(path="a1/b2/c3",recursive = TRUE) 在当前目录下创建一个子目录
递归创建深层子目录
file.create() 创建文件
file.exists(path) 路径是否存在
file.rename(from, to) 路径重命名
file.remove() 删除
file.append(file1, file2) 合并
file.copy(file1, file2) 用 file1 覆盖 file2 原本的内容
file.path(…) 根据操作系统的不同,将参数列表中的各项用/\ 串成一个路径(拼接目录字符串)
dirname(path) 返回路径的上级路径
basename(path) 返回路径的本级目录
path.expand() 转换~为用户目录
normalizePath() 返回绝对路径

使用 RStudio 创建 project 的一个优点在于,它的自动补全功能使编写文件路径变得更加高效。当你输入绝对路径或相对路径中的字符时,按下 Tab 键, RStudio 将会列出该目录中的所有文件,

Memory

tracemem()查看对象的内存地址

Time

Sys.sleep() 使程序暂停若干秒,一般用于某些循环(爬虫、动画等),故意降低运行频率

Sys.time() 返回系统时间;

system.time() 参数为大括号括起来的代码段,运行代码段并打印其运行时间

t1 <- Sys.time()
...
t2 <- Sys.time()
print(t2 - t1) # 中间代码的运行时间

system.time({
  ...
}) # 代码段运行时间
A <- matrix(1:1000000, 1000)

system.time({
  A %*% A
})
#>    user  system elapsed 
#>    0.30    0.00    0.31

Help

Help
help(functionName)?functionName 查看一个函数的用法
example(functionName) 运行帮主文件中的例子
help.search(keyword)??keyword 所有包的文档中搜索关键词
RSiteSearch("xxx") 搜索 CRAN 上包含关键词的包,自动打开一个网页,显示搜索结果
help(package = "") 查看一个包的帮助
help(options) 显示可用选项的说明
options() 显示或设置当前选项

History

History
history(#) 显示最近使用过的#个命令(默认值为25)
savehistory("myfile") 保存命令历史到文件myfile中(默认值为.Rhistory)
loadhistory("myfile") 载入一个命令历史文件(默认值为.Rhistory)

Modularization

R Package

Function 作用
install.packages("package") 安装包
remove.packages("xxx") 卸载包
installed.packages() 返回一个字符串矩阵,内含所有已安装的包的多项信息
packageVersion() 返回包的版本信息
library() 无参数时,显示库中已经安装了哪些包
library(package) 有参数时,载入包.
require(package)

类似于 library(),但 require() 会返回一个逻辑值来判断是否加载成功。

若已安装扩展包,则加载包;若尚未安装,则安装包:

{r} if (!require(moments)){ install.packages("moments") library(moments) }

unloadNamespace(package) 解除载入一个包
search() 查看环境中已经加载了哪些包
getOption('defaultPackages') 查看默认加载的 R 包
update.packages("package") 升级包。若不填入参数,则自动升级所有可以升级的包
package::function() 调用相应包的函数(有时多个包用同一个名字命名不同的函数,会发生冲突,只能这样引用)。这种方式不会修改环境(搜索路径),所以如果一个函数只使用一两次,最好用这种方式调用。
data() 查看所有预先提供的数据
data(package="") 查看某个包所有预先提供的数据
data(dataset_name, package=) 读入包中数据
installed.packages() %>% View()

新装 R 时安装常用包

# 重装 R 前把已安装包的名称(第一列)存为一个 .rds 文件
saveRDS(installed.packages()[, 1], "./download-packages.rds")
# 重装 R 后读取该文件,并安装这些 packages
install.packages(readRDS("./download-packages.rds"))

查看包的流行程度

# 查看 R 包的下载次数
library(cranlogs)

fashion <- function(package_name) {
  d <- cran_downloads(
    package = package_name,
    from = "2022-01-01",
    to = "2022-03-31"
  )
  sum(d$count)
}

fashion("ggplot2")
#> [1] 6591799
fashion("tidyverse")
#> [1] 2414103
fashion("data.table")
#> [1] 2512297
fashion("plotly")
#> [1] 916778

函数屏蔽 (masking)

后加载包的同名函数会将先加载包的函数屏蔽掉

如果两个都要用,则只能使用 package::function 语法

由此要注意,自己写包时,最好不要与已经被广泛使用的包中的函数重名

Create a Package

《R in Action 2nd Edition》 第21章有介绍。

代码见网盘中 R/Books/Programming/R-in-Action/Code/Ch21 Creating a package/ 文件夹

更好的资源,见 Hadly 的《R packages》一书,讲授了良好的 R 软件项目实践,科学地创建 R 包:打包文件、生成文档、测试 代码

R Script

Base 语法

source('xxx.R') 不好,更推荐 modules 包

modules 包

Modules in R

install.packages("modules")
module.R
foo <- function() "foo"
main.R
m <- modules::use("module.R") # 注意路径设定
m$foo()

最佳实践

由于 R 的懒加载特性,模块中的代码不会运行,故 .R 脚本文件作为模块时,不必加载配置常量和包,纯写函数即可,所有的包和配置由主文件加载。

右键菜单新建 R Script

新建一个文本文件,写入

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.R]
@="rstudio.exe"

[HKEY_CLASSES_ROOT\.R\ShellNew]
"FileName"="C:\Users\humoo\OneDrive\ICT\Programming-Language\R\Best-Practice\template.R"

[HKEY_CLASSES_ROOT\rstudio.exe]
@="R Script"

另存为 register-R.reg,然后双击运行即可

二进制数据文件

数据处理的中间结果一般保存为 .rda 或 .rds 即可;有其他用途的数据转换结果,才有必要保存为 .csv 或 .json

二者区别:

  1. saveRDS/readRDS 仅处理单个 R 对象。但是,它们比多对象存储方法更灵活,因为还原对象的对象名称不必与存储对象时的对象名称相同
  2. .rda 不对数据进行压缩,读写速度较快;.rds 会先对文件进行压缩再保存,速度相对稍慢。

Code Style

R Style

The tidyverse style guide

一般性规则

  • R 中函数默认返回最后一个表达式的值,故一般不用再写 return();只有在条件、循环等复杂逻辑中,需要提前返回的值,才用 return() 显式返回
  • 变量以名词开头,函数以动词开头,_分隔单词,尽量使对象名 concise and meaningful
  • 一行之内的简短函数用匿名函数写,超过一行的函数最好给它起个名字(便于调试)
  • Error 应该使用Stop()来抛出
  • 不要使用attach(), detach()

注释

  • 注释以#开头,后加一个空格
  • 对变量和函数的说明写在它们的上方(紧邻),VSCode 的 R 插件能够自动识别
  • Internal structure: 用形如 # string ---------------- 的注释分节行,帮助理清逻辑
    • 在 Rstudio 中编写 R Script 时(Rmarkdown 无效),快捷键 Ctrl+Shift+R 可以生成这样一个分节行
  • documents,即解释说明自定义函数的内容,每行用#'开头,辅以若干标记:@title, @param, @inheritParams, @return, @description, @details, @examples, @seealso, @export
    • 这些在注释中是结构化信息,Rstudio 和 VSCode 的 R 插件都可以识别
#' @title 老虎机的返还规则
#' @param symbols 长度为3的字符串向量,表示老虎机的一次运行结果
#' @return 中奖金额
#' @export
score <- function(symbols) {
  diamonds <- sum(symbols == "DD") # 有几个钻石
  cherries <- sum(symbols == "C") # 有几个樱桃

  ## 识别模式
  slots <- symbols[symbols != "DD"] # 考虑钻石以外的其他符号
  same <- length(unique(slots)) == 1 # 是否其他符号相同,包含有且仅有2钻情形
  all_bars <- all(slots %in% c("B", "BB", "BBB")) # 是否其他符号都是条

  ## 计算中奖金额
  if (diamonds == 3) {
    prize <- 100
  } else if (same) {
    # 哈希表
    payouts <- c("7" = 80, "BBB" = 40, "BB" = 25, "B" = 10, "C" = 10, "0" = 0)
    prize <- unname(payouts[slots[1]]) # 只要金额不要符号
  } else if (all_bars) { # 0或1钻,其他全是条(一条、二条或三条)的金额
    prize <- 5
  } else if (cherries > 0) {
    # 0或1钻,1-2个樱桃,且不能是1钻两樱桃
    prize <- c(0, 2, 5)[cherries + diamonds + 1] # 如果只有一个樱桃,则将钻石当作樱桃
  } else {
    prize <- 0
  }

  ## 根据钻石个数翻倍中奖金额
  prize * 2^diamonds
}

R Script 总体布局与顺序

  1. 版权声明
  2. 作者信息
  3. 文件说明, 包括程序的目的,输入以及输出
  4. library() 和 source() 说明
  5. 函数定义
  6. 可执行语句

单元测试应在另一个独立的的文件中进行

格式化

styler 包

styler - A non-invasive source code formatter for R (lorenzwalthert.github.io)

install.packages("styler")
  • style_text() styles a string
  • style_file() styles R and Rmd files
  • style_dir() styles all R and/or Rmd files in a directory.
  • 最常用
    • RStudio Addins 菜单,styles the active file R or Rmd file, or the highlighted code.
    • 或在 console 中输入styler:::style_active_file()

高性能 R 代码

Vectorization

向量化运算,即操作对象为向量或矩阵,对其所有元素执行同样的运算。向量化运算的速度不仅远快于显式循环(快两个数量级),也比 map() 等高阶函数要快得多。

如果运算符两边的变量 size 不相同,size 较小的变量会自动循环扩展,与 size 较大的变量保持一致。返回值的 size 将和较大的变量保持一致。

在计算矩阵的哈达马积、或对向量/矩阵的所有元素执行同样的变换时,向量化运算非常方便。

R 中的运算符和函数默认支持向量化运算,这是 R 的一大特色,Microsoft R Open 还对向量和矩阵的向量化运算进行了并行优化。因此,用好向量化运算、矩阵代数,才是符合 R 风格的代码。

编程经验丰富后,会产生一种下意识的直觉(或许写一写 julia 和 MATLAB 有明确语法形式的向量化运算有助于这种经验的养成),敏锐地意识到哪些函数支持向量化运算,并将代码尽量都写成向量式的。

如计算 \(\begin{aligned}w=\frac{1}{n} \sum_{i=1}^n|x_i-\hat{m}|\end{aligned}\),其中 \(\hat{m}\) 为中位数

# 为了表明这里是向量化的并行操作,将自变量写为大写的X
mean(abs(X - median(X)))

# 由于 R 的向量化特性,这个自定义函数天然接受向量输入
f3 <- function(X) ifelse(X >= 0, 1, 0)

条件选择用子集选择器 []

#' 本函数中的操作,逻辑判断、取子集、数乘、赋值,全部是向量化的
abs_set <- function(vec) {
  vec[vec < 0] <- vec[vec < 0] * -1
  vec
}
# 哈希表
vec <- c("DD", "BB", "C", "0")
tb <- c("DD" = "joker", "C" = "ace", "7" = "king", "B" = "queen", "BB" = "jack", "BBB" = "ten", "0" = "nine") # 键值对

vec
#> [1] "DD" "BB" "C"  "0"
tb[vec] # 用键选择值
#>      DD      BB       C       0 
#> "joker"  "jack"   "ace"  "nine"
unname(tb[vec])
#> [1] "joker" "jack"  "ace"   "nine"

多用内置函数

R base 的很多内置函数是用 C 实现的,效率很高,尽量使用。

  • sum() 向量、矩阵所有元素的和,比用循环累加快得多
  • prod() 所有元素的积
  • cumsum(),返回累计和的序列
  • cumprod(),返回累计积的序列
cumsum(1:10)
#>  [1]  1  3  6 10 15 21 28 36 45 55
cumprod(1:10)
#>  [1]       1       2       6      24     120     720    5040   40320  362880
#> [10] 3628800

避免制作副本

多用 data.table 数据框在数据上就地修改

为 for 循环提前准备储存空间

如果不得不用 for 循环,最好提前准备后储存空间。

system.time({
  output <- NA
  for (i in 1:1000000) {
    output[i] <- i + 1
  }
})
#>    user  system elapsed 
#>    0.16    0.03    0.21

如果没有提前确定足够大的空间储存 for 循环产生的大 size 数据 output,R 每次循环都需要在内存中找到一个新的位置以存放更大的对象。这意味着频繁地在内存中删除旧版 output,再找到新的地方存放该向量的新版本。因此,在循环结束的时候,R 已经将 output 在内存中反复重写了100万次。

system.time({
  output <- rep(NA, 1000000)
  for (i in 1:1000000) {
    output[i] <- i + 1
  }
})
#>    user  system elapsed 
#>    0.03    0.00    0.03

多核并行运算

Exception and Debugging

参考《Advanced R》

Exception

几个函数

  • stop("!")/stopifnot(),终止程序并打印错误信息:“Error: !”
  • warning("?!"),打印警告信息 “Warning message: ?!”
  • message("?"),打印信息 “?”
# stop("!") # Error: !
try(stop("!"))
#> Error in try(stop("!")) : !

try(..., silent = FALSE)

  1. 如果…中出现错误,使用该函数可以打印错误信息(silent = TRUE 时,不打印错误信息),然后跳过错误继续执行程序。try()的返回值为一个 “try-error” 类的对象。
  2. 若无错误,则返回 … 表达式的值
success <- try(1 + 2)
failure <- try("a" + "b", silent = TRUE)
class(success)
#> [1] "numeric"
class(failure)
#> [1] "try-error"

对 list 列表中所有元素进行批量操作时,使用 try() 非常有用,可以有效避免个别元素不能计算引起的错误。

elements <- list(1:10, c(-1, 10), c(T, F), letters)
# results <- elements %>% map(~ log(.x)) # 报错,会阻止程序运行
result_list <- elements %>%
  map(~ try(log(.x))) %>%
  discard(function(result) {
    "try-error" %in% class(result) # 去掉运算报错的项
  })
#> Warning in log(.x): NaNs produced
#> Error in log(.x) : non-numeric argument to mathematical function
result_list
#> [[1]]
#>  [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
#>  [8] 2.0794415 2.1972246 2.3025851
#> 
#> [[2]]
#> [1]      NaN 2.302585
#> 
#> [[3]]
#> [1]    0 -Inf
# 求解逆矩阵-错误处理
set.seed(1)
inverses <- vector(mode = "list", length = 5) # 预先分配空间
for (i in 1:5) {
  x <- matrix(sample(0:2, 4, replace = T), nrow = 2)
  x.inv <- try(solve(x), silent = TRUE)
  if ("try-error" %in% class(x.inv)) {
    # x.inv 是一个矩阵,class(x.inv) 返回长度为2的字符串向量
    # 若用 class(x.inv) == 'try-error',返回长度为2的逻辑向量
    # if() 无法处理长度大于 1 的条件
    next
  } else {
    inverses[[i]] <- x.inv
  }
}
inverses
#> [[1]]
#> NULL
#> 
#> [[2]]
#>       [,1] [,2]
#> [1,] -0.25  0.5
#> [2,]  0.50  0.0
#> 
#> [[3]]
#>      [,1]  [,2]
#> [1,]  0.0  0.50
#> [2,]  0.5 -0.25
#> 
#> [[4]]
#> NULL
#> 
#> [[5]]
#>      [,1] [,2]
#> [1,]  0.0  1.0
#> [2,]  0.5 -0.5
inverses %>% discard(~ is.null(.x)) # 去掉运算报错的项
#> [[1]]
#>       [,1] [,2]
#> [1,] -0.25  0.5
#> [2,]  0.50  0.0
#> 
#> [[2]]
#>      [,1]  [,2]
#> [1,]  0.0  0.50
#> [2,]  0.5 -0.25
#> 
#> [[3]]
#>      [,1] [,2]
#> [1,]  0.0  1.0
#> [2,]  0.5 -0.5
# 避免读取文件失败
default <- NULL
try({
  default <- read.csv("possibly-bad-input.csv")
})
#> Warning in file(file, "rt"): cannot open file 'possibly-bad-input.csv': No such
#> file or directory
#> Error in file(file, "rt") : cannot open the connection

tryCatch()

指定控制条件,进行异常捕捉,然后采用对应的函数处理异常和错误。

result <- tryCatch(
  {
    # 正常的逻辑
    ...
  },
  warning = function(w) {
    # 出现warning的处理逻辑
    ...
  },
  error = function(e) {
    # 出现error的处理逻辑
    ...
  },
  finally = {
    # 不管出现异常还是正常都会执行的代码模块,
    # 一般用来处理清理操作,例如关闭连接资源等。
    ...
  }
)


# 例
get.msg <- function(path) {
  con <- file(path, open = "rt", encoding = "latin1")
  text <- readLines(con)
  msg <- tryCatch(
    {
      text[seq(which(text == "")[1] + 1, length(text), 1)]
    },
    error = function(e) {
      ""
    }
  )
  close(con)
  return(paste(msg, collapse = "\n"))
}
show_condition <- function(code) {
  tryCatch(code,
    error = function(c) "error",
    warning = function(c) "warning",
    message = function(c) "message"
  )
}
show_condition(stop("!"))
#> [1] "error"
show_condition(warning("?!"))
#> [1] "warning"
show_condition(message("?"))
#> [1] "message"
show_condition(10) # 没有警告、错误时,返回第一个参数(代码段)的运行结果
#> [1] 10

自定义发生错误时返回的信息

read.csv3 <- function(file, ...) {
  tryCatch(
    read.csv(file, ...),
    error = function(c) {
      c$message <- paste0(c$message, ' in "', file, '"')
      stop(c) # 将文件路径加到错误信息中
    }
  )
}
try(read.csv("code/dummy.csv"))
#> Warning in file(file, "rt"): cannot open file 'code/dummy.csv': No such file or
#> directory
#> Error in file(file, "rt") : cannot open the connection
try(read.csv3("code/dummy.csv"))
#> Warning in file(file, "rt"): cannot open file 'code/dummy.csv': No such file or
#> directory
#> Error in file(file, "rt") : 
#>   cannot open the connection in "code/dummy.csv"

Debugging

Testing

library(testthat)

IDE

Rstudio

R Studio cheatsheet 预览:

downloadthis::download_file(
  path = "../pdf/cheatsheet-rstudio-ide.pdf",
  output_name = "cheatsheet-rstudio-ide",
  button_label = "Download cheatsheet",
  button_type = "success",
  self_contained = FALSE
)

Shortcuts

Shortcuts
Ctrl + Shift + A 选中部分行后,格式化代码
Ctrl + Alt + I Insert chunk
Alt + - 插入 <-
Ctrl + Shift + M 插入 %>%|>
Alt + Shift + K 显示快捷键
Ctrl + Shift + N 新建脚本 .r文件
Ctrl + Enter
Ctrl + Shift + Enter
Ctrl + Alt + R
运行一行代码
运行代码块
运行全部代码
Shift + Home/End 选中光标到行首/末之间的部分
Tab / Ctrl+Space 自动补齐
输入完函数名,按tab,自动添加开括号(和闭括号)。
Ctrl + Shift + C 注释/取消注释
F1 查看帮助
Ctrl+ ↑ 在 Console 中输入”xxx”,然后按 Ctrl+ ↑。就可以列出所有输入过的以”xxx”开头的命令。

代码高亮

GitHub 上可搜索、安装 rscodeio 主题

anthonynorth/rscodeio: An RStudio theme inspired by Visual Studio Code. (github.com)

VSCode

优点

  • 鼠标悬停,即可显示变量的定义信息和函数的帮助文档(仅限 R 包中函数的官方文档和本文件中自定义函数的定义,无法显示引入模块中的自定义函数的定义),省去了查阅文档的大量时间
  • 保存(Ctrl+S)时自动格式化
  • Ctrl+Enter自动运行一行,Ctrl+Shift+Enter自动运行当前文件

配置步骤

  1. 安装 R 包 languageserver

    install.packages("languageserver")
  2. 在 VSCode 扩展商店中安装 R 插件。

    1. 安装完成后在 VSCode 配置文件中搜索r.rterm.option,删除--no-save,--no-restore,添加--no-site-file和 R.exe 的路径--r-binary=C:\\Program Files\\R\\R-4.1.3\\bin\\R.exe
  3. 安装 Radian:一款现代的 R console,它是用 Python 编写的

    pip install radian
    1. 安装完成后在 cmd 中输入 radian 查看是否安装成功。

    2. 若出现 “cannot determine R HOME”,可能在系统 PATH 中没有 R 路径,则在 PATH 中添加即可;或存在多个 R 路径(如新安装了某个版本)而 Radian 无法识别,要在 VSCode 中修改r.rterm.option,也可以在设置界面的右上角打开 setting.json文件直接修改:

      "r.rterm.option": [
        "--no-site-file",
        "--r-binary=C:\\Program Files\\R\\R-4.2.2\\bin\\R.exe"
      ]
  4. VSCode 中 Radian 相关设置

    1. 搜索 r.rterm.windows,将其设置为 radian.exe 的路径。在 cmd 中(powershell 不行)输入 where radian 可以获取其路径。
    2. 搜索R: Bracketed Paste并勾选,否则 Radian 不会启用
    3. 搜索r.sessionWatcher并勾选
  5. 定义在 R 脚本中使用的快捷键

    1. 最重要的两个快捷键:输入 <- 的快捷键 Alt+-;输入 %>%|> 的快捷键 Ctrl+Shift+M

    2. 方法:VSCode 中打开 keybindings.json (Ctrl+K Ctrl+S 然后右上角打开配置文件),绑定快捷键覆盖默认值

      [
        {
          "key": "alt+-",
          "command": "type",
          "when": "editorLangId == r || editorLangId == rmd && editorTextFocus",
          "args": {
            "text": " <- "
          }
        },
        {
          "key": "ctrl+shift+m",
          "command": "type",
          "when": "editorLangId == r || editorLangId == rmd && editorTextFocus",
          "args": {
            "text": " %>% "
          }
        }
      ]

如何在 VSCODE 中高效使用 R 语言 (图文详解)_Baimoc-CSDN博客

LS0tDQp0aXRsZTogIlIgRW5naW5lZXJpbmciDQpzdWJ0aXRsZTogJycNCmF1dGhvcjogIkh1bW9vbiINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDogaHRtbF9kb2N1bWVudA0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQpzb3VyY2UoIi4uL1JtYXJrZG93bi10ZW1wbGF0ZS9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywNCiAgZXZhbCA9IGNvbmZpZyRldmFsLA0KICB0aWR5ID0gY29uZmlnJHRpZHksDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwNCiAgY29sbGFwc2UgPSBjb25maWckY29sbGFwc2UsDQogIGNhY2hlID0gY29uZmlnJGNhY2hlLA0KICBjYWNoZS5jb21tZW50cyA9IGNvbmZpZyRjYWNoZS5jb21tZW50cywNCiAgYXV0b2RlcCA9IGNvbmZpZyRhdXRvZGVwDQopDQoNCm9wdGlvbnMoZXJyb3IgPSByZWNvdmVyKSAjIOaKpemUmeaXtui/kOihjCByZWNvdmVyKCkg6LCD6K+VDQpgYGANCg0KIyMg54mI5pysDQoNCiMjIyDmn6XnnIvniYjmnKzjgIHmk43kvZzns7vnu5/lkozlt7LliqDovb3ljIUNCg0KYHNlc3Npb25JbmZvKClgDQoNCiMjIyDoh6rliqjljYfnuqfmnIDmlrDniYjmnKwNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppbnN0YWxscjo6dXBkYXRlUigpDQojIOWHuueOsOS4gOS4quWvueivneahhu+8jOaYr+WQpuWwhiBsaWJyYXJpZXMg5aSN5Yi25Yiw5paw54mIIFIg5Lit77yM6YCJIHllcw0KYGBgDQoNCiMjIyBNUk8NCg0K5aaC5p6c5pyJ5aSnIHNpemUg55qE5ZCR6YeP5ZKM55+p6Zi16L+Q566X77yM5bu66K6u5a6J6KOF5ZKM5L2/55SoIE1pY3Jvc29mdCBSIE9wZW4uIOivpeeJiOacrOWvueWQkemHj+WSjOefqemYteeahOWQkemHj+WMlui/kOeul++8iOWvueaJgOacieWFg+e0oOWIhuWIq+aJp+ihjO+8iei/m+ihjOS6huWkp+W5heS8mOWMlu+8jOaXoOmcgOW8leWFpeS7u+S9leWMhe+8jOWwseiDveiHquWKqOiwg+eUqOWkmuaguOW5tuihjOiuoeeul++8jOWPr+aPkOmrmOi/kOeul+mAn+W6puaVsOWNgeWAjeOAgg0KDQrov5nkuZ/mhI/lkbPnnYDvvIwqKuWQkemHj+WMlui/kOeul+aYryBSIOacgOWfuuacrOOAgeacgOS4u+imgeeahOS7o+eggemjjuagvCoq44CCDQoNCuS9huaYr++8jE1pY3Jvc29mdCBSIE9wZW4g55qE5p+Q5Lqb5YyF5pu05paw5LiN5aSf5Y+K5pe277yI5aaCIHJtYXJrZG93bu+8ieOAguS4uuS6hiBLbml0IOeahOaWueS+v++8jOWPr+S7pSBSc3R1ZGlvIOmAieaLqSBSIOeahOacgOaWsOeJiO+8jOWcqCBWU0NvZGUg5Lit55SoIE1pY3Jvc29mdCBSIE9wZW4g5pel5bi45Yqg6YCf6K6h566X44CCDQoNCiMjIFN5c3RlbSBpbnRlcmFjdGlvbg0KDQojIyMgRW52aXJvbm1lbnQNCg0KUiDkuK3or43ms5XkvZznlKjln5/nmoTmnLrnkIbpgJrov4fnjq/looPlrp7njrDjgIINCg0KYGBge3J9DQojIOS7juWGheWIsOWklu+8jOaYvuekuuaJgOacieeOr+Wig+Wxgue6pw0KcHJ5cjo6cGFyZW52cyhhbGwgPSBUUlVFKQ0KYGBgDQoNCuacgOWkluWxgueahCBgUl9FbXB0eUVudmAg5piv5ZSv5LiA5rKh5pyJ54i2546v5aKD55qE546v5aKD44CCDQoNCuavj+WKoOi9veS4gOS4quaJqeWxleWMhe+8jOi/meS4quWMheeahOeOr+Wig+mDveS8muaPkuWFpSoq5pCc57Si6Lev5b6EKirvvIzmiJDkuLrlhajlsYDnjq/looPmlrDnmoTniLbnuqfnjq/looPvvIzlubbopobnm5bkuYvliY3liqDovb3nmoTljIXnmoTlkIzlkI3lh73mlbDjgIINCg0K6LCD55So5Ye95pWw5pe277yM6aaW5YWI5Yib6YCg5LiA5LiqIHJ1bnRpbWUg546v5aKD77yM5Ye95pWw5LyY5YWI5ZyoIHJ1bnRpbWUg546v5aKD5pCc57Si55So5Yiw55qE5Y+Y6YeP77yM5pCc57Si5LiN5Yiw5pe277yM5bCx5Yiw5LiK5LiA57qn546v5aKD5pCc57Si77yM55u06Iez5pyA5aSW5bGC55qEIGBSX0VtcHR5RW52YOOAgg0KDQrlh73mlbAgcnVudGltZSDnjq/looPnmoTniLbnjq/looPvvIzmmK/lh73mlbDnrKzkuIDmrKHliJvlu7rml7bmiYDlnKjnmoTnjq/looPjgILlpoLmnpzlh73mlbDmmK/lhajlsYDliJvlu7rnmoTvvIjljbPkvb/mmK/pgJrov4cgLlIg6ISa5pys5a+85YWl77yJ77yM5YW254i2546v5aKD5bCx5piv5YWo5bGA546v5aKD44CCDQoNCuWHveaVsOWCqOWtmOWPmOmHj++8jOS5n+WCqOWtmOWcqCBydW50aW1lIOeOr+Wig+S4re+8jOWPquaciei/lOWbnuWAvOiDveS8oOWFpeiwg+eUqOWug+eahOeOr+Wig+OAgg0KDQrov5nph4zmnInkuKTkuKrkvovlpJbvvJoNCg0KMS4gIGB4IDw8LSA1YCDog73lsIbmlbDmja4gYDVgIOWCqOWtmOWIsOeItueOr+Wig+eahOWPmOmHjyBgeGAg5Lit77yM5aaC5p6c54i2546v5aKD5Lit5LiN5a2Y5Zyo5Y+Y6YePIHjvvIzkvJrkuIDnm7TlkJHkuIrnm7Toh7MqKuWFqOWxgCoq546v5aKD6L+b6KGM5Y+Y6YeP5p+l5om+44CC6Iul5Zyo5p+l5om+6L+H56iL5Lit5a+75om+5Yiw6K+l5ZCN56ew55qE5Y+Y6YeP77yM5bCx5Lya6L+b6KGM6LWL5YC85pON5L2c44CC5ZCm5YiZ77yM5bCG5Zyo5YWo5bGA546v5aKD5Lit5Yib5bu65Y+Y6YeP5bm26LWL5YC844CCDQoyLiAgYGFzc2lnbigidmFyX25hbWUiLCB2YWx1ZSwgZW52aW8gPSBhcy5lbnZpcm9ubWVudChwb3MpKWAg5Y+v5Lul55u05o6l5oyH5a6a5bCGIHZhbHVlIOWCqOWtmOWIsOWTquS4queOr+Wig+S4reOAguivpeWHveaVsOeahOesrOS4gOS4quWPguaVsOaYr+Wtl+espuS4su+8jOS5n+WPr+S7peaYr+Wtl+espuS4suWQkemHj++8jOS4gOasoeaAp+i1i+WAvOWkmuS4quWPmOmHj+OAgg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IOeOr+Wig+S6pOS6kuWHveaVsCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSs9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0rDQp8IGBwcnlyOjpwYXJlbnZzKGFsbCA9IFRSVUUpYCAgICAgICAgICAgIHwg5pi+56S65b2T5YmN546v5aKD55qE5bGC57qn77yM6L+U5Zue5LiA5Liq5YiX6KGo77yM5LuO5b2T5YmN546v5aKD5YiwIGBSX0VtcHR5RW52YCAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGFzLmVudmlyb25tZW50KClgICAgICAgICAgICAgICAgICAgICAgfCDmjqXlj5fkuIDkuKrlrZfnrKbkuLLnjq/looPlkI3vvIzov5Tlm57lr7nlupTnmoTnjq/looMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGVudmlyb25tZW50KClgICAgICAgICAgICAgICAgICAgICAgICAgfCDov5Tlm57lvZPliY0gYWN0aXZlIOeOr+WigyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGdsb2JhbGVudigpYC9gLkdsb2JhbEVudmAgICAgICAgICAgICAgfCDov5Tlm57lhajlsYDnjq/looMgUl9HbG9iYWxFbnbvvIzov5nmmK/mnIDmtLvot4PjgIHpu5jorqTlgqjlrZjlr7nosaHnmoTnjq/looMgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBiYXNlZW52KClgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg6L+U5Zue5Z+6546v5aKDIGJhc2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGVtcHR5ZW52KClgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDov5Tlm57nqbrnjq/looMgUl9FbXB0eUVudiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgcGFyZW50LmVudigpYCAgICAgICAgICAgICAgICAgICAgICAgICB8IOi/lOWbnuWPguaVsOeOr+Wig+eahOeItueOr+WigyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGxzKClgL2BvYmplY3RzKClgICAgICAgICAgICAgICAgICAgICAgfCDov5Tlm57lrZfnrKbkuLLlkJHph4/vvIzliJflh7rlvZPliY3njq/looPnmoTmiYDmnInlr7nosaEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBscy5zdHIobW9kZT0ibGlzdCIsIHBhdHRlcm49Il5cXHckIilgIHwg5pi+56S65b2T5YmN546v5aKD55qE57uT5p6E44CCbW9kZSDlj4LmlbDov4fmu6Tlr7nosaHnsbvlnovvvIxwYXR0ZXJuIOWPguaVsOS4uuato+WImeihqOi+vuW8j++8jOi/h+a7pOWvueixoeWQjSB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGByZW1vdmUoKWAvYHJtKClgICAgICAgICAgICAgICAgICAgICAgIHwg5Yig6Zmk546v5aKD5Lit5LiA5Liq5oiW5pu05aSa5Liq5a+56LGh44CCICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYHJtKGxpc3Q9bHMoKSlgICAgICAgICAgICAgICAgICAgICAgICAgfCDliKDpmaTnjq/looPkuK3miYDmnInlr7nosaEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KIyMjIOWFqOWxgOmAiemhuQ0KDQpgZ2V0T3B0aW9uKHgp77yMYOafpeeci+WFqOWxgOmAiemhuQ0KDQpgb3B0aW9ucyguLi4p77yMYOiuvuWumuWFqOWxgOmAiemhueOAguWmgiBkaWdpdHM9Nywg6K6+5a6a5pi+56S65pWw5a2X55qE5L2N5pWw44CCd2Fybj0wLCDlj6/ku6XmlLnkuLox77yM5Zyo6K2m5ZGK5Lqn55Sf5pe256uL5Y2z5pi+56S644CCDQoNCiMjIyDmlofku7bns7vnu5/nrqHnkIYNCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgV29ya2luZyBEaXJlY3RvcnkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCBgZ2V0d2QoKWAvYHNldHdkKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5p+l55yLL+iuvuWumuW9k+WJjeW3peS9nOebruW9lSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgUi5ob21lKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgUiDova/ku7bnmoTnm67lvZUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGxpc3QuZGlycygpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOafpeeci+W9k+WJjeebruW9leeahOaJgOacieWtkOWtmeebruW9le+8iOmAkuW9kuafpeeci++8iSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGRpcihbcGF0aCwgXVtwYXR0ZXJuLCBdW2FsbC5maWxlcyA9IEZBTFNFXSlgICAgICAgICAgICAgICB8IOafpeeci+W9k+WJjS/mjIflrprnm67lvZXkuK3nmoTmlofku7blkozlrZDnm67lvZXvvIxwYXR0ZXJu5Li65q2j5YiZ77yMYWxsLmZpbGVzIOaOp+WItuaYr+WQpuaYvuekuumakOiXj+aWh+S7tiAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYHN5c3RlbSgidHJlZSIpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOafpeeci+W9k+WJjeebruW9leeahOagkeeKtuWxgue6p+e7k+aehCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGRpci5jcmVhdGUoKWRpci5jcmVhdGUocGF0aD0iYTEvYjIvYzMiLHJlY3Vyc2l2ZSA9IFRSVUUpYCB8IOWcqOW9k+WJjeebruW9leS4i+WIm+W7uuS4gOS4quWtkOebruW9lVwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg6YCS5b2S5Yib5bu65rex5bGC5a2Q55uu5b2VICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBmaWxlLmNyZWF0ZSgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDliJvlu7rmlofku7YgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBmaWxlLmV4aXN0cyhwYXRoKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDot6/lvoTmmK/lkKblrZjlnKggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgZmlsZS5yZW5hbWUoZnJvbSwgdG8pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOi3r+W+hOmHjeWRveWQjSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGZpbGUucmVtb3ZlKCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDliKDpmaQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBmaWxlLmFwcGVuZChmaWxlMSwgZmlsZTIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5ZCI5bm2ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgZmlsZS5jb3B5KGZpbGUxLCBmaWxlMikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOeUqCBmaWxlMSDopobnm5YgZmlsZTIg5Y6f5pys55qE5YaF5a65ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGZpbGUucGF0aCguLi4pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmoLnmja7mk43kvZzns7vnu5/nmoTkuI3lkIzvvIzlsIblj4LmlbDliJfooajkuK3nmoTlkITpobnnlKhgL2Ag5oiWIGBcYCDkuLLmiJDkuIDkuKrot6/lvoTvvIjmi7zmjqXnm67lvZXlrZfnrKbkuLLvvIkgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgZGlybmFtZShwYXRoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOi/lOWbnui3r+W+hOeahOS4iue6p+i3r+W+hCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBiYXNlbmFtZShwYXRoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg6L+U5Zue6Lev5b6E55qE5pys57qn55uu5b2VICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IHBhdGguZXhwYW5kKCkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDovazmjaJcfuS4uueUqOaIt+ebruW9lSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IG5vcm1hbGl6ZVBhdGgoKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDov5Tlm57nu53lr7not6/lvoQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0K5L2/55SoIFJTdHVkaW8g5Yib5bu6IHByb2plY3Qg55qE5LiA5Liq5LyY54K55Zyo5LqO77yM5a6D55qE6Ieq5Yqo6KGl5YWo5Yqf6IO95L2/57yW5YaZ5paH5Lu26Lev5b6E5Y+Y5b6X5pu05Yqg6auY5pWI44CC5b2T5L2g6L6T5YWl57ud5a+56Lev5b6E5oiW55u45a+56Lev5b6E5Lit55qE5a2X56ym5pe277yM5oyJ5LiLIFRhYiDplK7vvIwgUlN0dWRpbyDlsIbkvJrliJflh7ror6Xnm67lvZXkuK3nmoTmiYDmnInmlofku7bvvIwNCg0KIyMjIE1lbW9yeQ0KDQpgdHJhY2VtZW0oKWDmn6XnnIvlr7nosaHnmoTlhoXlrZjlnLDlnYANCg0KIyMjIFRpbWUNCg0KYFN5cy5zbGVlcCgpYCDkvb/nqIvluo/mmoLlgZzoi6XlubLnp5LvvIzkuIDoiKznlKjkuo7mn5Dkupvlvqrnjq/vvIjniKzomavjgIHliqjnlLvnrYnvvInvvIzmlYXmhI/pmY3kvY7ov5DooYzpopHnjocNCg0KYFN5cy50aW1lKClgIOi/lOWbnuezu+e7n+aXtumXtO+8mw0KDQpgc3lzdGVtLnRpbWUoKWAg5Y+C5pWw5Li65aSn5ous5Y+35ous6LW35p2l55qE5Luj56CB5q6177yM6L+Q6KGM5Luj56CB5q615bm25omT5Y2w5YW26L+Q6KGM5pe26Ze0DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KdDEgPC0gU3lzLnRpbWUoKQ0KLi4uDQp0MiA8LSBTeXMudGltZSgpDQpwcmludCh0MiAtIHQxKSAjIOS4remXtOS7o+eggeeahOi/kOihjOaXtumXtA0KDQpzeXN0ZW0udGltZSh7DQogIC4uLg0KfSkgIyDku6PnoIHmrrXov5DooYzml7bpl7QNCmBgYA0KDQpgYGB7cn0NCkEgPC0gbWF0cml4KDE6MTAwMDAwMCwgMTAwMCkNCg0Kc3lzdGVtLnRpbWUoew0KICBBICUqJSBBDQp9KQ0KYGBgDQoNCiMjIyBIZWxwDQoNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBIZWxwICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSsNCnwgYGhlbHAoZnVuY3Rpb25OYW1lKWDmiJZgP2Z1bmN0aW9uTmFtZWAgfCDmn6XnnIvkuIDkuKrlh73mlbDnmoTnlKjms5UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgZXhhbXBsZShmdW5jdGlvbk5hbWUpYCAgICAgICAgICAgICAgIHwg6L+Q6KGM5biu5Li75paH5Lu25Lit55qE5L6L5a2QICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBoZWxwLnNlYXJjaChrZXl3b3JkKWDmiJZgPz9rZXl3b3JkYCAgIHwg5ZyoKirmiYDmnInljIUqKueahOaWh+aho+S4reaQnOe0ouWFs+mUruivjSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBSU2l0ZVNlYXJjaCgieHh4IilgICAgICAgICAgICAgICAgICAgfCDmkJzntKIgQ1JBTiDkuIrljIXlkKvlhbPplK7or43nmoTljIXvvIzoh6rliqjmiZPlvIDkuIDkuKrnvZHpobXvvIzmmL7npLrmkJzntKLnu5PmnpwgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBoZWxwKHBhY2thZ2UgPSAiIilgICAgICAgICAgICAgICAgICAgfCDmn6XnnIvkuIDkuKrljIXnmoTluK7liqkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBoZWxwKG9wdGlvbnMpYCAgICAgICAgICAgICAgICAgICAgICAgfCDmmL7npLrlj6/nlKjpgInpobnnmoTor7TmmI4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgb3B0aW9ucygpYCAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5pi+56S65oiW6K6+572u5b2T5YmN6YCJ6aG5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KIyMjIEhpc3RvcnkNCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgSGlzdG9yeSAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorPT09PT09PT09PT09PT09PT09PT09PT09PSs9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCBgaGlzdG9yeSgjKWAgICAgICAgICAgICB8IOaYvuekuuacgOi/keS9v+eUqOi/h+eahCPkuKrlkb3ku6TvvIjpu5jorqTlgLzkuLoyNe+8iSAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYHNhdmVoaXN0b3J5KCJteWZpbGUiKWAgfCDkv53lrZjlkb3ku6Tljoblj7LliLDmlofku7ZteWZpbGXkuK3vvIjpu5jorqTlgLzkuLouUmhpc3RvcnnvvIkgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGxvYWRoaXN0b3J5KCJteWZpbGUiKWAgfCDovb3lhaXkuIDkuKrlkb3ku6Tljoblj7Lmlofku7bvvIjpu5jorqTlgLzkuLouUmhpc3RvcnnvvIkgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KIyMgTW9kdWxhcml6YXRpb24NCg0KIyMjIFIgUGFja2FnZQ0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBGdW5jdGlvbiAgICAgICAgICAgICAgICAgICAgICAgfCDkvZznlKggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSsNCnwgYGluc3RhbGwucGFja2FnZXMoInBhY2thZ2UiKWAgIHwg5a6J6KOF5YyFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgcmVtb3ZlLnBhY2thZ2VzKCJ4eHgiKWAgICAgICAgfCDljbjovb3ljIUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBpbnN0YWxsZWQucGFja2FnZXMoKWAgICAgICAgICB8IOi/lOWbnuS4gOS4quWtl+espuS4suefqemYte+8jOWGheWQq+aJgOacieW3suWuieijheeahOWMheeahOWkmumhueS/oeaBryAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYHBhY2thZ2VWZXJzaW9uKClgICAgICAgICAgICAgIHwg6L+U5Zue5YyF55qE54mI5pys5L+h5oGvICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBsaWJyYXJ5KClgICAgICAgICAgICAgICAgICAgICB8IOaXoOWPguaVsOaXtu+8jOaYvuekuuW6k+S4reW3sue7j+WuieijheS6huWTquS6m+WMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgbGlicmFyeShwYWNrYWdlKWAgICAgICAgICAgICAgfCDmnInlj4LmlbDml7bvvIzovb3lhaXljIUuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYHJlcXVpcmUocGFja2FnZSlgICAgICAgICAgICAgIHwg57G75Ly85LqOIGBsaWJyYXJ5KClg77yM5L2GIGByZXF1aXJlKClgIOS8mui/lOWbnuS4gOS4qumAu+i+keWAvOadpeWIpOaWreaYr+WQpuWKoOi9veaIkOWKn+OAgiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOiLpeW3suWuieijheaJqeWxleWMhe+8jOWImeWKoOi9veWMhe+8m+iLpeWwmuacquWuieijhe+8jOWImeWuieijheWMhe+8miAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYGBgIHtyfSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGlmICghcmVxdWlyZShtb21lbnRzKSl7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgIGluc3RhbGwucGFja2FnZXMoIm1vbWVudHMiKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICBsaWJyYXJ5KG1vbWVudHMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IH0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgYGAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGB1bmxvYWROYW1lc3BhY2UocGFja2FnZSlgICAgICB8IOino+mZpOi9veWFpeS4gOS4quWMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBzZWFyY2goKWAgICAgICAgICAgICAgICAgICAgICB8IOafpeeci+eOr+Wig+S4reW3sue7j+WKoOi9veS6huWTquS6m+WMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBnZXRPcHRpb24oJ2RlZmF1bHRQYWNrYWdlcycpYCB8IOafpeeci+m7mOiupOWKoOi9veeahCBSIOWMhSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgdXBkYXRlLnBhY2thZ2VzKCJwYWNrYWdlIilgICAgfCDljYfnuqfljIXjgILoi6XkuI3loavlhaXlj4LmlbDvvIzliJnoh6rliqjljYfnuqfmiYDmnInlj6/ku6XljYfnuqfnmoTljIUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBwYWNrYWdlOjpmdW5jdGlvbigpYCAgICAgICAgICB8IOiwg+eUqOebuOW6lOWMheeahOWHveaVsO+8iOacieaXtioq5aSa5Liq5YyF55So5ZCM5LiA5Liq5ZCN5a2X5ZG95ZCN5LiN5ZCM55qE5Ye95pWw77yM5Lya5Y+R55Sf5Yay56qB77yM5Y+q6IO96L+Z5qC35byV55SoKirvvInjgILov5nnp43mlrnlvI/kuI3kvJrkv67mlLnnjq/looPvvIjmkJzntKLot6/lvoTvvInvvIzmiYDku6XlpoLmnpzkuIDkuKrlh73mlbDlj6rkvb/nlKjkuIDkuKTmrKHvvIzmnIDlpb3nlKjov5nnp43mlrnlvI/osIPnlKjjgIIgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgYGRhdGEoKWAgICAgICAgICAgICAgICAgICAgICAgIHwg5p+l55yL5omA5pyJ6aKE5YWI5o+Q5L6b55qE5pWw5o2uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IGBkYXRhKHBhY2thZ2U9IiIpYCAgICAgICAgICAgICB8IOafpeeci+afkOS4quWMheaJgOaciemihOWFiOaPkOS+m+eahOaVsOaNriAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBgZGF0YShkYXRhc2V0X25hbWUsIHBhY2thZ2U9KWAgfCDor7vlhaXljIXkuK3mlbDmja4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KaW5zdGFsbGVkLnBhY2thZ2VzKCkgJT4lIFZpZXcoKQ0KYGBgDQoNCiFbXShodHRwOi8vaHVtb29uLWltYWdlLWhvc3Rpbmctc2VydmljZS5vc3MtY24tYmVpamluZy5hbGl5dW5jcy5jb20vaW1nL3R5cG9yYS8yMDIyL2ltYWdlLTIwMjIwNDA0MDU1MzI1MDk5LnBuZykNCg0KIyMjIyDmlrDoo4UgUiDml7blronoo4XluLjnlKjljIUNCg0KYGBge3J9DQojIOmHjeijhSBSIOWJjeaKiuW3suWuieijheWMheeahOWQjeensO+8iOesrOS4gOWIl++8ieWtmOS4uuS4gOS4qiAucmRzIOaWh+S7tg0Kc2F2ZVJEUyhpbnN0YWxsZWQucGFja2FnZXMoKVssIDFdLCAiLi9kb3dubG9hZC1wYWNrYWdlcy5yZHMiKQ0KYGBgDQoNCmBgYCByDQojIOmHjeijhSBSIOWQjuivu+WPluivpeaWh+S7tu+8jOW5tuWuieijhei/meS6myBwYWNrYWdlcw0KaW5zdGFsbC5wYWNrYWdlcyhyZWFkUkRTKCIuL2Rvd25sb2FkLXBhY2thZ2VzLnJkcyIpKQ0KYGBgDQoNCiMjIyMg5p+l55yL5YyF55qE5rWB6KGM56iL5bqmDQoNCmBgYHtyfQ0KIyDmn6XnnIsgUiDljIXnmoTkuIvovb3mrKHmlbANCmxpYnJhcnkoY3JhbmxvZ3MpDQoNCmZhc2hpb24gPC0gZnVuY3Rpb24ocGFja2FnZV9uYW1lKSB7DQogIGQgPC0gY3Jhbl9kb3dubG9hZHMoDQogICAgcGFja2FnZSA9IHBhY2thZ2VfbmFtZSwNCiAgICBmcm9tID0gIjIwMjItMDEtMDEiLA0KICAgIHRvID0gIjIwMjItMDMtMzEiDQogICkNCiAgc3VtKGQkY291bnQpDQp9DQoNCmZhc2hpb24oImdncGxvdDIiKQ0KZmFzaGlvbigidGlkeXZlcnNlIikNCmZhc2hpb24oImRhdGEudGFibGUiKQ0KZmFzaGlvbigicGxvdGx5IikNCmBgYA0KDQojIyMjIOWHveaVsOWxj+iUvSAobWFza2luZykNCg0K5ZCO5Yqg6L295YyF55qE5ZCM5ZCN5Ye95pWw5Lya5bCG5YWI5Yqg6L295YyF55qE5Ye95pWw5bGP6JS95o6JDQoNCuWmguaenOS4pOS4qumDveimgeeUqO+8jOWImeWPquiDveS9v+eUqCBwYWNrYWdlOjpmdW5jdGlvbiDor63ms5UNCg0K55Sx5q2k6KaB5rOo5oSP77yMKiroh6rlt7HlhpnljIXml7bvvIzmnIDlpb3kuI3opoHkuI7lt7Lnu4/ooqvlub/ms5vkvb/nlKjnmoTljIXkuK3nmoTlh73mlbDph43lkI0qKg0KDQojIyMjIENyZWF0ZSBhIFBhY2thZ2UNCg0K44CKUiBpbiBBY3Rpb24gMm5kIEVkaXRpb27jgIsg56ysMjHnq6DmnInku4vnu43jgIINCg0K5Luj56CB6KeB572R55uY5LitIGBSL0Jvb2tzL1Byb2dyYW1taW5nL1ItaW4tQWN0aW9uL0NvZGUvQ2gyMSBDcmVhdGluZyBhIHBhY2thZ2UvYCDmlofku7blpLkNCg0K5pu05aW955qE6LWE5rqQ77yM6KeBIEhhZGx5IOeahOOAilIgcGFja2FnZXPjgIvkuIDkuabvvIzorrLmjojkuoboia/lpb3nmoQgUiDova/ku7bpobnnm67lrp7ot7XvvIznp5HlrablnLDliJvlu7ogUiDljIXvvJrmiZPljIXmlofku7bjgIHnlJ/miJDmlofmoaPjgIHmtYvor5Ug5Luj56CBDQoNCiMjIyBSIFNjcmlwdA0KDQojIyMjIEJhc2Ug6K+t5rOVDQoNCmBzb3VyY2UoJ3h4eC5SJylgIOS4jeWlve+8jOabtOaOqOiNkCBtb2R1bGVzIOWMhQ0KDQojIyMjIG1vZHVsZXMg5YyFDQoNCltNb2R1bGVzIGluIFJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tb2R1bGVzL3ZpZ25ldHRlcy9tb2R1bGVzSW5SLmh0bWwpDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygibW9kdWxlcyIpDQpgYGANCg0KIyMjIyMgbW9kdWxlLlINCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpmb28gPC0gZnVuY3Rpb24oKSAiZm9vIg0KYGBgDQoNCiMjIyMjIG1haW4uUg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCm0gPC0gbW9kdWxlczo6dXNlKCJtb2R1bGUuUiIpICMg5rOo5oSP6Lev5b6E6K6+5a6aDQptJGZvbygpDQpgYGANCg0KIyMjIyDmnIDkvbPlrp7ot7UNCg0K55Sx5LqOIFIg55qE5oeS5Yqg6L2954m55oCn77yM5qih5Z2X5Lit55qE5Luj56CB5LiN5Lya6L+Q6KGM77yM5pWFIC5SIOiEmuacrOaWh+S7tuS9nOS4uuaooeWdl+aXtu+8jOS4jeW/heWKoOi9vemFjee9ruW4uOmHj+WSjOWMhe+8jOe6r+WGmeWHveaVsOWNs+WPr++8jOaJgOacieeahOWMheWSjOmFjee9rueUseS4u+aWh+S7tuWKoOi9veOAgg0KDQojIyMjIOWPs+mUruiPnOWNleaWsOW7uiBSIFNjcmlwdA0KDQrmlrDlu7rkuIDkuKrmlofmnKzmlofku7bvvIzlhpnlhaUNCg0KYGBgIHJlZw0KV2luZG93cyBSZWdpc3RyeSBFZGl0b3IgVmVyc2lvbiA1LjAwDQoNCltIS0VZX0NMQVNTRVNfUk9PVFwuUl0NCkA9InJzdHVkaW8uZXhlIg0KDQpbSEtFWV9DTEFTU0VTX1JPT1RcLlJcU2hlbGxOZXddDQoiRmlsZU5hbWUiPSJDOlxVc2Vyc1xodW1vb1xPbmVEcml2ZVxJQ1RcUHJvZ3JhbW1pbmctTGFuZ3VhZ2VcUlxCZXN0LVByYWN0aWNlXHRlbXBsYXRlLlIiDQoNCltIS0VZX0NMQVNTRVNfUk9PVFxyc3R1ZGlvLmV4ZV0NCkA9IlIgU2NyaXB0Ig0KYGBgDQoNCuWPpuWtmOS4uiByZWdpc3Rlci1SLnJlZ++8jOeEtuWQjuWPjOWHu+i/kOihjOWNs+WPrw0KDQojIyMg5LqM6L+b5Yi25pWw5o2u5paH5Lu2DQoNCuaVsOaNruWkhOeQhueahOS4remXtOe7k+aenOS4gOiIrOS/neWtmOS4uiAucmRhIOaIliAucmRzIOWNs+WPr++8m+acieWFtuS7lueUqOmAlOeahOaVsOaNrui9rOaNoue7k+aenO+8jOaJjeacieW/heimgeS/neWtmOS4uiAuY3N2IOaIliAuanNvbg0KDQrkuozogIXljLrliKvvvJoNCg0KMS4gIHNhdmVSRFMvcmVhZFJEUyDku4XlpITnkIbljZXkuKogUiDlr7nosaHjgILkvYbmmK/vvIzlroPku6zmr5TlpJrlr7nosaHlrZjlgqjmlrnms5Xmm7TngbXmtLvvvIzlm6DkuLrov5jljp/lr7nosaHnmoTlr7nosaHlkI3np7DkuI3lv4XkuI7lrZjlgqjlr7nosaHml7bnmoTlr7nosaHlkI3np7Dnm7jlkIwNCjIuICAucmRhIOS4jeWvueaVsOaNrui/m+ihjOWOi+e8qe+8jOivu+WGmemAn+W6pui+g+W/q++8my5yZHMg5Lya5YWI5a+55paH5Lu26L+b6KGM5Y6L57yp5YaN5L+d5a2Y77yM6YCf5bqm55u45a+556iN5oWi44CCDQoNCiMjIENvZGUgU3R5bGUNCg0KIyMjIFIgU3R5bGUNCg0KW1RoZSB0aWR5dmVyc2Ugc3R5bGUgZ3VpZGVdKGh0dHBzOi8vc3R5bGUudGlkeXZlcnNlLm9yZy8pDQoNCiMjIyMg5LiA6Iis5oCn6KeE5YiZDQoNCi0gICBSIOS4reWHveaVsOm7mOiupOi/lOWbnuacgOWQjuS4gOS4quihqOi+vuW8j+eahOWAvO+8jOaVheS4gOiIrOS4jeeUqOWGjeWGmSBgcmV0dXJuKClg77yb5Y+q5pyJ5Zyo5p2h5Lu244CB5b6q546v562J5aSN5p2C6YC76L6R5Lit77yM6ZyA6KaB5o+Q5YmN6L+U5Zue55qE5YC877yM5omN55SoIGByZXR1cm4oKWAg5pi+5byP6L+U5ZueDQotICAg5Y+Y6YeP5Lul5ZCN6K+N5byA5aS077yM5Ye95pWw5Lul5Yqo6K+N5byA5aS077yMYF9g5YiG6ZqU5Y2V6K+N77yM5bC96YeP5L2/5a+56LGh5ZCNIGNvbmNpc2UgYW5kIG1lYW5pbmdmdWwNCi0gICDkuIDooYzkuYvlhoXnmoTnroDnn63lh73mlbDnlKjljL/lkI3lh73mlbDlhpnvvIzotoXov4fkuIDooYznmoTlh73mlbDmnIDlpb3nu5nlroPotbfkuKrlkI3lrZfvvIjkvr/kuo7osIPor5XvvIkNCi0gICBFcnJvciDlupTor6Xkvb/nlKhgU3RvcCgpYOadpeaKm+WHug0KLSAgICoq5LiN6KaBKirkvb/nlKhgYXR0YWNoKClgLCBgZGV0YWNoKClgDQoNCiMjIyMg5rOo6YeKDQoNCi0gICDms6jph4rku6VgI2DlvIDlpLTvvIzlkI7liqDkuIDkuKrnqbrmoLwNCi0gICAqKuWvueWPmOmHj+WSjOWHveaVsOeahOivtOaYjuWGmeWcqOWug+S7rOeahOS4iuaWue+8iOe0p+mCu++8ie+8jFZTQ29kZSDnmoQgUiDmj5Lku7bog73lpJ/oh6rliqjor4bliKsqKg0KLSAgIEludGVybmFsIHN0cnVjdHVyZTog55So5b2i5aaCIGAjIHN0cmluZyAtLS0tLS0tLS0tLS0tLS0tYCDnmoTms6jph4rliIboioLooYzvvIzluK7liqnnkIbmuIXpgLvovpENCiAgICAtICAg5ZyoIFJzdHVkaW8g5Lit57yW5YaZIFIgU2NyaXB0IOaXtu+8iFJtYXJrZG93biDml6DmlYjvvInvvIzlv6vmjbfplK4gYEN0cmwrU2hpZnQrUmAg5Y+v5Lul55Sf5oiQ6L+Z5qC35LiA5Liq5YiG6IqC6KGMDQotICAgZG9jdW1lbnRz77yM5Y2z6Kej6YeK6K+05piO6Ieq5a6a5LmJ5Ye95pWw55qE5YaF5a6577yM5q+P6KGM55SoYCMnYOW8gOWktO+8jOi+heS7peiLpeW5suagh+iusO+8mmBAdGl0bGVgLCBgQHBhcmFtYCwgYEBpbmhlcml0UGFyYW1zYCwgYEByZXR1cm5gLCBgQGRlc2NyaXB0aW9uYCwgYEBkZXRhaWxzYCwgYEBleGFtcGxlc2AsIGBAc2VlYWxzb2AsIGBAZXhwb3J0YA0KICAgIC0gICAqKui/meS6m+WcqOazqOmHiuS4reaYr+e7k+aehOWMluS/oeaBr++8jFJzdHVkaW8g5ZKMIFZTQ29kZSDnmoQgUiDmj5Lku7bpg73lj6/ku6Xor4bliKsqKg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMnIEB0aXRsZSDogIHomY7mnLrnmoTov5Tov5jop4TliJkNCiMnIEBwYXJhbSBzeW1ib2xzIOmVv+W6puS4ujPnmoTlrZfnrKbkuLLlkJHph4/vvIzooajnpLrogIHomY7mnLrnmoTkuIDmrKHov5DooYznu5PmnpwNCiMnIEByZXR1cm4g5Lit5aWW6YeR6aKdDQojJyBAZXhwb3J0DQpzY29yZSA8LSBmdW5jdGlvbihzeW1ib2xzKSB7DQogIGRpYW1vbmRzIDwtIHN1bShzeW1ib2xzID09ICJERCIpICMg5pyJ5Yeg5Liq6ZK755+zDQogIGNoZXJyaWVzIDwtIHN1bShzeW1ib2xzID09ICJDIikgIyDmnInlh6DkuKrmqLHmoYMNCg0KICAjIyDor4bliKvmqKHlvI8NCiAgc2xvdHMgPC0gc3ltYm9sc1tzeW1ib2xzICE9ICJERCJdICMg6ICD6JmR6ZK755+z5Lul5aSW55qE5YW25LuW56ym5Y+3DQogIHNhbWUgPC0gbGVuZ3RoKHVuaXF1ZShzbG90cykpID09IDEgIyDmmK/lkKblhbbku5bnrKblj7fnm7jlkIzvvIzljIXlkKvmnInkuJTku4XmnIky6ZK75oOF5b2iDQogIGFsbF9iYXJzIDwtIGFsbChzbG90cyAlaW4lIGMoIkIiLCAiQkIiLCAiQkJCIikpICMg5piv5ZCm5YW25LuW56ym5Y+36YO95piv5p2hDQoNCiAgIyMg6K6h566X5Lit5aWW6YeR6aKdDQogIGlmIChkaWFtb25kcyA9PSAzKSB7DQogICAgcHJpemUgPC0gMTAwDQogIH0gZWxzZSBpZiAoc2FtZSkgew0KICAgICMg5ZOI5biM6KGoDQogICAgcGF5b3V0cyA8LSBjKCI3IiA9IDgwLCAiQkJCIiA9IDQwLCAiQkIiID0gMjUsICJCIiA9IDEwLCAiQyIgPSAxMCwgIjAiID0gMCkNCiAgICBwcml6ZSA8LSB1bm5hbWUocGF5b3V0c1tzbG90c1sxXV0pICMg5Y+q6KaB6YeR6aKd5LiN6KaB56ym5Y+3DQogIH0gZWxzZSBpZiAoYWxsX2JhcnMpIHsgIyAw5oiWMemSu++8jOWFtuS7luWFqOaYr+adoe+8iOS4gOadoeOAgeS6jOadoeaIluS4ieadoe+8ieeahOmHkeminQ0KICAgIHByaXplIDwtIDUNCiAgfSBlbHNlIGlmIChjaGVycmllcyA+IDApIHsNCiAgICAjIDDmiJYx6ZK777yMMS0y5Liq5qix5qGD77yM5LiU5LiN6IO95pivMemSu+S4pOaoseahgw0KICAgIHByaXplIDwtIGMoMCwgMiwgNSlbY2hlcnJpZXMgKyBkaWFtb25kcyArIDFdICMg5aaC5p6c5Y+q5pyJ5LiA5Liq5qix5qGD77yM5YiZ5bCG6ZK755+z5b2T5L2c5qix5qGDDQogIH0gZWxzZSB7DQogICAgcHJpemUgPC0gMA0KICB9DQoNCiAgIyMg5qC55o2u6ZK755+z5Liq5pWw57+75YCN5Lit5aWW6YeR6aKdDQogIHByaXplICogMl5kaWFtb25kcw0KfQ0KYGBgDQoNCiMjIyMgUiBTY3JpcHQg5oC75L2T5biD5bGA5LiO6aG65bqPDQoNCjEuICDniYjmnYPlo7DmmI4NCjIuICDkvZzogIXkv6Hmga8NCjMuICDmlofku7bor7TmmI4sIOWMheaLrOeoi+W6j+eahOebrueahO+8jOi+k+WFpeS7peWPiui+k+WHug0KNC4gIGxpYnJhcnkoKSDlkowgc291cmNlKCkg6K+05piODQo1LiAg5Ye95pWw5a6a5LmJDQo2LiAg5Y+v5omn6KGM6K+t5Y+lDQoNCuWNleWFg+a1i+ivleW6lOWcqOWPpuS4gOS4queLrOeri+eahOeahOaWh+S7tuS4rei/m+ihjA0KDQojIyMg5qC85byP5YyWDQoNCiMjIyMgc3R5bGVyIOWMhQ0KDQpbc3R5bGVyIC0gQSBub24taW52YXNpdmUgc291cmNlIGNvZGUgZm9ybWF0dGVyIGZvciBSIChsb3Jlbnp3YWx0aGVydC5naXRodWIuaW8pXShodHRwczovL2xvcmVuendhbHRoZXJ0LmdpdGh1Yi5pby9zdHlsZXJwb3N0LykNCg0KYGBgIHINCmluc3RhbGwucGFja2FnZXMoInN0eWxlciIpDQpgYGANCg0KLSAgIGBzdHlsZV90ZXh0KClgIHN0eWxlcyBhIHN0cmluZw0KLSAgIGBzdHlsZV9maWxlKClgIHN0eWxlcyBSIGFuZCBSbWQgZmlsZXMNCi0gICBgc3R5bGVfZGlyKClgIHN0eWxlcyBhbGwgUiBhbmQvb3IgUm1kIGZpbGVzIGluIGEgZGlyZWN0b3J5Lg0KLSAgICoq5pyA5bi455SoKioNCiAgICAtICAgUlN0dWRpbyBgQWRkaW5zYCDoj5zljZXvvIxzdHlsZXMgdGhlIGFjdGl2ZSBmaWxlIFIgb3IgUm1kIGZpbGUsIG9yIHRoZSBoaWdobGlnaHRlZCBjb2RlLg0KICAgIC0gICDmiJblnKggY29uc29sZSDkuK3ovpPlhaVgc3R5bGVyOjo6c3R5bGVfYWN0aXZlX2ZpbGUoKWANCg0KIyMg6auY5oCn6IO9IFIg5Luj56CBDQoNCiMjIyBWZWN0b3JpemF0aW9uDQoNCuWQkemHj+WMlui/kOeul++8jOWNs+aTjeS9nOWvueixoeS4uuWQkemHj+aIluefqemYte+8jOWvueWFtuaJgOacieWFg+e0oOaJp+ihjOWQjOagt+eahOi/kOeul+OAguWQkemHj+WMlui/kOeul+eahOmAn+W6puS4jeS7hei/nOW/q+S6juaYvuW8j+W+queOr++8iOW/q+S4pOS4quaVsOmHj+e6p++8ie+8jOS5n+avlCBgbWFwKClgIOetiemrmOmYtuWHveaVsOimgeW/q+W+l+WkmuOAgg0KDQrlpoLmnpzov5DnrpfnrKbkuKTovrnnmoTlj5jph48gc2l6ZSDkuI3nm7jlkIzvvIxzaXplIOi+g+Wwj+eahOWPmOmHj+S8muiHquWKqOW+queOr+aJqeWxle+8jOS4jiBzaXplIOi+g+Wkp+eahOWPmOmHj+S/neaMgeS4gOiHtOOAgui/lOWbnuWAvOeahCBzaXplIOWwhuWSjOi+g+Wkp+eahOWPmOmHj+S/neaMgeS4gOiHtOOAgg0KDQrlnKjorqHnrpfnn6npmLXnmoTlk4jovr7pqaznp6/jgIHmiJblr7nlkJHph48v55+p6Zi155qE5omA5pyJ5YWD57Sg5omn6KGM5ZCM5qC355qE5Y+Y5o2i5pe277yM5ZCR6YeP5YyW6L+Q566X6Z2e5bi45pa55L6/44CCDQoNClIg5Lit55qE6L+Q566X56ym5ZKM5Ye95pWw6buY6K6k5pSv5oyB5ZCR6YeP5YyW6L+Q566X77yM6L+Z5pivIFIg55qE5LiA5aSn54m56Imy77yMKipNaWNyb3NvZnQgUiBPcGVuIOi/mOWvueWQkemHj+WSjOefqemYteeahOWQkemHj+WMlui/kOeul+i/m+ihjOS6huW5tuihjOS8mOWMlioq44CC5Zug5q2k77yM55So5aW95ZCR6YeP5YyW6L+Q566X44CB55+p6Zi15Luj5pWw77yM5omN5piv56ym5ZCIIFIg6aOO5qC855qE5Luj56CB44CCDQoNCj4g57yW56iL57uP6aqM5Liw5a+M5ZCO77yM5Lya5Lqn55Sf5LiA56eN5LiL5oSP6K+G55qE55u06KeJ77yI5oiW6K645YaZ5LiA5YaZIGp1bGlhIOWSjCBNQVRMQUIg5pyJ5piO56Gu6K+t5rOV5b2i5byP55qE5ZCR6YeP5YyW6L+Q566X5pyJ5Yqp5LqO6L+Z56eN57uP6aqM55qE5YW75oiQ77yJ77yM5pWP6ZSQ5Zyw5oSP6K+G5Yiw5ZOq5Lqb5Ye95pWw5pSv5oyB5ZCR6YeP5YyW6L+Q566X77yM5bm25bCG5Luj56CB5bC96YeP6YO95YaZ5oiQ5ZCR6YeP5byP55qE44CCDQoNCuWmguiuoeeulyAkXGJlZ2lue2FsaWduZWR9dz1cZnJhY3sxfXtufSBcc3VtX3tpPTF9Xm58eF9pLVxoYXR7bX18XGVuZHthbGlnbmVkfSTvvIzlhbbkuK0gJFxoYXR7bX0kIOS4uuS4reS9jeaVsA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMg5Li65LqG6KGo5piO6L+Z6YeM5piv5ZCR6YeP5YyW55qE5bm26KGM5pON5L2c77yM5bCG6Ieq5Y+Y6YeP5YaZ5Li65aSn5YaZ55qEWA0KbWVhbihhYnMoWCAtIG1lZGlhbihYKSkpDQoNCiMg55Sx5LqOIFIg55qE5ZCR6YeP5YyW54m55oCn77yM6L+Z5Liq6Ieq5a6a5LmJ5Ye95pWw5aSp54S25o6l5Y+X5ZCR6YeP6L6T5YWlDQpmMyA8LSBmdW5jdGlvbihYKSBpZmVsc2UoWCA+PSAwLCAxLCAwKQ0KYGBgDQoNCiMjIyDmnaHku7bpgInmi6nnlKjlrZDpm4bpgInmi6nlmaggYFtdYA0KDQpgYGB7cn0NCiMnIOacrOWHveaVsOS4reeahOaTjeS9nO+8jOmAu+i+keWIpOaWreOAgeWPluWtkOmbhuOAgeaVsOS5mOOAgei1i+WAvO+8jOWFqOmDqOaYr+WQkemHj+WMlueahA0KYWJzX3NldCA8LSBmdW5jdGlvbih2ZWMpIHsNCiAgdmVjW3ZlYyA8IDBdIDwtIHZlY1t2ZWMgPCAwXSAqIC0xDQogIHZlYw0KfQ0KYGBgDQoNCmBgYHtyfQ0KIyDlk4jluIzooagNCnZlYyA8LSBjKCJERCIsICJCQiIsICJDIiwgIjAiKQ0KdGIgPC0gYygiREQiID0gImpva2VyIiwgIkMiID0gImFjZSIsICI3IiA9ICJraW5nIiwgIkIiID0gInF1ZWVuIiwgIkJCIiA9ICJqYWNrIiwgIkJCQiIgPSAidGVuIiwgIjAiID0gIm5pbmUiKSAjIOmUruWAvOWvuQ0KDQp2ZWMNCnRiW3ZlY10gIyDnlKjplK7pgInmi6nlgLwNCnVubmFtZSh0Ylt2ZWNdKQ0KYGBgDQoNCiMjIyDlpJrnlKjlhoXnva7lh73mlbANCg0KUiBiYXNlIOeahOW+iOWkmuWGhee9ruWHveaVsOaYr+eUqCBDIOWunueOsOeahO+8jOaViOeOh+W+iOmrmO+8jOWwvemHj+S9v+eUqOOAgg0KDQotICAgc3VtKCkg5ZCR6YeP44CB55+p6Zi15omA5pyJ5YWD57Sg55qE5ZKM77yM5q+U55So5b6q546v57Sv5Yqg5b+r5b6X5aSaDQotICAgcHJvZCgpIOaJgOacieWFg+e0oOeahOenrw0KLSAgIGN1bXN1bSgp77yM6L+U5Zue57Sv6K6h5ZKM55qE5bqP5YiXDQotICAgY3VtcHJvZCgp77yM6L+U5Zue57Sv6K6h56ev55qE5bqP5YiXDQoNCmBgYHtyfQ0KY3Vtc3VtKDE6MTApDQpjdW1wcm9kKDE6MTApDQpgYGANCg0KIyMjIOmBv+WFjeWItuS9nOWJr+acrA0KDQrlpJrnlKggZGF0YS50YWJsZSDmlbDmja7moYblnKjmlbDmja7kuIrlsLHlnLDkv67mlLkNCg0KIyMjIOS4uiBmb3Ig5b6q546v5o+Q5YmN5YeG5aSH5YKo5a2Y56m66Ze0DQoNCuWmguaenOS4jeW+l+S4jeeUqCBmb3Ig5b6q546v77yM5pyA5aW95o+Q5YmN5YeG5aSH5ZCO5YKo5a2Y56m66Ze044CCDQoNCmBgYHtyfQ0Kc3lzdGVtLnRpbWUoew0KICBvdXRwdXQgPC0gTkENCiAgZm9yIChpIGluIDE6MTAwMDAwMCkgew0KICAgIG91dHB1dFtpXSA8LSBpICsgMQ0KICB9DQp9KQ0KYGBgDQoNCuWmguaenOayoeacieaPkOWJjeehruWumui2s+Wkn+Wkp+eahOepuumXtOWCqOWtmCBmb3Ig5b6q546v5Lqn55Sf55qE5aSnIHNpemUg5pWw5o2uIG91dHB1dO+8jFIg5q+P5qyh5b6q546v6YO96ZyA6KaB5Zyo5YaF5a2Y5Lit5om+5Yiw5LiA5Liq5paw55qE5L2N572u5Lul5a2Y5pS+5pu05aSn55qE5a+56LGh44CC6L+Z5oSP5ZGz552A6aKR57mB5Zyw5Zyo5YaF5a2Y5Lit5Yig6Zmk5pen54mIIG91dHB1dO+8jOWGjeaJvuWIsOaWsOeahOWcsOaWueWtmOaUvuivpeWQkemHj+eahOaWsOeJiOacrOOAguWboOatpO+8jOWcqOW+queOr+e7k+adn+eahOaXtuWAme+8jFIg5bey57uP5bCGIG91dHB1dCDlnKjlhoXlrZjkuK3lj43lpI3ph43lhpnkuoYxMDDkuIfmrKHjgIINCg0KYGBge3J9DQpzeXN0ZW0udGltZSh7DQogIG91dHB1dCA8LSByZXAoTkEsIDEwMDAwMDApDQogIGZvciAoaSBpbiAxOjEwMDAwMDApIHsNCiAgICBvdXRwdXRbaV0gPC0gaSArIDENCiAgfQ0KfSkNCmBgYA0KDQojIyMg5aSa5qC45bm26KGM6L+Q566XDQoNCiMjIEV4Y2VwdGlvbiBhbmQgRGVidWdnaW5nDQoNCuWPguiAg+OAikFkdmFuY2VkIFLjgIsNCg0KIyMjIEV4Y2VwdGlvbg0KDQojIyMjIOWHoOS4quWHveaVsA0KDQotICAgYHN0b3AoIiEiKWAvYHN0b3BpZm5vdCgpYO+8jOe7iOatoueoi+W6j+W5tuaJk+WNsOmUmeivr+S/oeaBr++8miJFcnJvcjogISINCi0gICBgd2FybmluZygiPyEiKWDvvIzmiZPljbDorablkYrkv6Hmga8gIldhcm5pbmcgbWVzc2FnZTogPyEiDQotICAgYG1lc3NhZ2UoIj8iKWDvvIzmiZPljbDkv6Hmga8gIj8iDQoNCmBgYHtyfQ0KIyBzdG9wKCIhIikgIyBFcnJvcjogIQ0KdHJ5KHN0b3AoIiEiKSkNCmBgYA0KDQojIyMjIGB0cnkoLi4uLCBzaWxlbnQgPSBGQUxTRSlgDQoNCjEuICDlpoLmnpwuLi7kuK3lh7rnjrDplJnor6/vvIzkvb/nlKjor6Xlh73mlbDlj6/ku6XmiZPljbDplJnor6/kv6Hmga/vvIhzaWxlbnQgPSBUUlVFIOaXtu+8jOS4jeaJk+WNsOmUmeivr+S/oeaBr++8ie+8jOeEtuWQjui3s+i/h+mUmeivr+e7p+e7reaJp+ihjOeoi+W6j+OAgmB0cnkoKWDnmoTov5Tlm57lgLzkuLrkuIDkuKogInRyeS1lcnJvciIg57G755qE5a+56LGh44CCDQoyLiAg6Iul5peg6ZSZ6K+v77yM5YiZ6L+U5ZueIC4uLiDooajovr7lvI/nmoTlgLwNCg0KYGBge3J9DQpzdWNjZXNzIDwtIHRyeSgxICsgMikNCmZhaWx1cmUgPC0gdHJ5KCJhIiArICJiIiwgc2lsZW50ID0gVFJVRSkNCmNsYXNzKHN1Y2Nlc3MpDQpjbGFzcyhmYWlsdXJlKQ0KYGBgDQoNCuWvuSBsaXN0IOWIl+ihqOS4reaJgOacieWFg+e0oOi/m+ihjOaJuemHj+aTjeS9nOaXtu+8jOS9v+eUqCB0cnkoKSDpnZ7luLjmnInnlKjvvIzlj6/ku6XmnInmlYjpgb/lhY3kuKrliKvlhYPntKDkuI3og73orqHnrpflvJXotbfnmoTplJnor6/jgIINCg0KYGBge3J9DQplbGVtZW50cyA8LSBsaXN0KDE6MTAsIGMoLTEsIDEwKSwgYyhULCBGKSwgbGV0dGVycykNCiMgcmVzdWx0cyA8LSBlbGVtZW50cyAlPiUgbWFwKH4gbG9nKC54KSkgIyDmiqXplJnvvIzkvJrpmLvmraLnqIvluo/ov5DooYwNCnJlc3VsdF9saXN0IDwtIGVsZW1lbnRzICU+JQ0KICBtYXAofiB0cnkobG9nKC54KSkpICU+JQ0KICBkaXNjYXJkKGZ1bmN0aW9uKHJlc3VsdCkgew0KICAgICJ0cnktZXJyb3IiICVpbiUgY2xhc3MocmVzdWx0KSAjIOWOu+aOiei/kOeul+aKpemUmeeahOmhuQ0KICB9KQ0KcmVzdWx0X2xpc3QNCmBgYA0KDQpgYGB7cn0NCiMg5rGC6Kej6YCG55+p6Zi1LemUmeivr+WkhOeQhg0Kc2V0LnNlZWQoMSkNCmludmVyc2VzIDwtIHZlY3Rvcihtb2RlID0gImxpc3QiLCBsZW5ndGggPSA1KSAjIOmihOWFiOWIhumFjeepuumXtA0KZm9yIChpIGluIDE6NSkgew0KICB4IDwtIG1hdHJpeChzYW1wbGUoMDoyLCA0LCByZXBsYWNlID0gVCksIG5yb3cgPSAyKQ0KICB4LmludiA8LSB0cnkoc29sdmUoeCksIHNpbGVudCA9IFRSVUUpDQogIGlmICgidHJ5LWVycm9yIiAlaW4lIGNsYXNzKHguaW52KSkgew0KICAgICMgeC5pbnYg5piv5LiA5Liq55+p6Zi177yMY2xhc3MoeC5pbnYpIOi/lOWbnumVv+W6puS4ujLnmoTlrZfnrKbkuLLlkJHph48NCiAgICAjIOiLpeeUqCBjbGFzcyh4LmludikgPT0gJ3RyeS1lcnJvcifvvIzov5Tlm57plb/luqbkuLoy55qE6YC76L6R5ZCR6YePDQogICAgIyBpZigpIOaXoOazleWkhOeQhumVv+W6puWkp+S6jiAxIOeahOadoeS7tg0KICAgIG5leHQNCiAgfSBlbHNlIHsNCiAgICBpbnZlcnNlc1tbaV1dIDwtIHguaW52DQogIH0NCn0NCmludmVyc2VzDQppbnZlcnNlcyAlPiUgZGlzY2FyZCh+IGlzLm51bGwoLngpKSAjIOWOu+aOiei/kOeul+aKpemUmeeahOmhuQ0KYGBgDQoNCmBgYHtyfQ0KIyDpgb/lhY3or7vlj5bmlofku7blpLHotKUNCmRlZmF1bHQgPC0gTlVMTA0KdHJ5KHsNCiAgZGVmYXVsdCA8LSByZWFkLmNzdigicG9zc2libHktYmFkLWlucHV0LmNzdiIpDQp9KQ0KYGBgDQoNCiMjIyMgYHRyeUNhdGNoKClgDQoNCuaMh+WumuaOp+WItuadoeS7tu+8jOi/m+ihjOW8guW4uOaNleaNie+8jOeEtuWQjumHh+eUqOWvueW6lOeahOWHveaVsOWkhOeQhuW8guW4uOWSjOmUmeivr+OAgg0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnJlc3VsdCA8LSB0cnlDYXRjaCgNCiAgew0KICAgICMg5q2j5bi455qE6YC76L6RDQogICAgLi4uDQogIH0sDQogIHdhcm5pbmcgPSBmdW5jdGlvbih3KSB7DQogICAgIyDlh7rnjrB3YXJuaW5n55qE5aSE55CG6YC76L6RDQogICAgLi4uDQogIH0sDQogIGVycm9yID0gZnVuY3Rpb24oZSkgew0KICAgICMg5Ye6546wZXJyb3LnmoTlpITnkIbpgLvovpENCiAgICAuLi4NCiAgfSwNCiAgZmluYWxseSA9IHsNCiAgICAjIOS4jeeuoeWHuueOsOW8guW4uOi/mOaYr+ato+W4uOmDveS8muaJp+ihjOeahOS7o+eggeaooeWdl++8jA0KICAgICMg5LiA6Iis55So5p2l5aSE55CG5riF55CG5pON5L2c77yM5L6L5aaC5YWz6Zet6L+e5o6l6LWE5rqQ562J44CCDQogICAgLi4uDQogIH0NCikNCg0KDQojIOS+iw0KZ2V0Lm1zZyA8LSBmdW5jdGlvbihwYXRoKSB7DQogIGNvbiA8LSBmaWxlKHBhdGgsIG9wZW4gPSAicnQiLCBlbmNvZGluZyA9ICJsYXRpbjEiKQ0KICB0ZXh0IDwtIHJlYWRMaW5lcyhjb24pDQogIG1zZyA8LSB0cnlDYXRjaCgNCiAgICB7DQogICAgICB0ZXh0W3NlcSh3aGljaCh0ZXh0ID09ICIiKVsxXSArIDEsIGxlbmd0aCh0ZXh0KSwgMSldDQogICAgfSwNCiAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsNCiAgICAgICIiDQogICAgfQ0KICApDQogIGNsb3NlKGNvbikNCiAgcmV0dXJuKHBhc3RlKG1zZywgY29sbGFwc2UgPSAiXG4iKSkNCn0NCmBgYA0KDQpgYGB7cn0NCnNob3dfY29uZGl0aW9uIDwtIGZ1bmN0aW9uKGNvZGUpIHsNCiAgdHJ5Q2F0Y2goY29kZSwNCiAgICBlcnJvciA9IGZ1bmN0aW9uKGMpICJlcnJvciIsDQogICAgd2FybmluZyA9IGZ1bmN0aW9uKGMpICJ3YXJuaW5nIiwNCiAgICBtZXNzYWdlID0gZnVuY3Rpb24oYykgIm1lc3NhZ2UiDQogICkNCn0NCnNob3dfY29uZGl0aW9uKHN0b3AoIiEiKSkNCnNob3dfY29uZGl0aW9uKHdhcm5pbmcoIj8hIikpDQpzaG93X2NvbmRpdGlvbihtZXNzYWdlKCI/IikpDQpzaG93X2NvbmRpdGlvbigxMCkgIyDmsqHmnInorablkYrjgIHplJnor6/ml7bvvIzov5Tlm57nrKzkuIDkuKrlj4LmlbDvvIjku6PnoIHmrrXvvInnmoTov5DooYznu5PmnpwNCmBgYA0KDQroh6rlrprkuYnlj5HnlJ/plJnor6/ml7bov5Tlm57nmoTkv6Hmga8NCg0KYGBge3J9DQpyZWFkLmNzdjMgPC0gZnVuY3Rpb24oZmlsZSwgLi4uKSB7DQogIHRyeUNhdGNoKA0KICAgIHJlYWQuY3N2KGZpbGUsIC4uLiksDQogICAgZXJyb3IgPSBmdW5jdGlvbihjKSB7DQogICAgICBjJG1lc3NhZ2UgPC0gcGFzdGUwKGMkbWVzc2FnZSwgJyBpbiAiJywgZmlsZSwgJyInKQ0KICAgICAgc3RvcChjKSAjIOWwhuaWh+S7tui3r+W+hOWKoOWIsOmUmeivr+S/oeaBr+S4rQ0KICAgIH0NCiAgKQ0KfQ0KdHJ5KHJlYWQuY3N2KCJjb2RlL2R1bW15LmNzdiIpKQ0KdHJ5KHJlYWQuY3N2MygiY29kZS9kdW1teS5jc3YiKSkNCmBgYA0KDQojIyMgRGVidWdnaW5nDQoNCiMjIFRlc3RpbmcNCg0KYGBge3J9DQpsaWJyYXJ5KHRlc3R0aGF0KQ0KYGBgDQoNCiMjIElERQ0KDQojIyMgUnN0dWRpbw0KDQpbUiBTdHVkaW8gY2hlYXRzaGVldF0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvbWFpbi9yc3R1ZGlvLWlkZS5wZGYpIOmihOiniO+8mg0KDQo8b2JqZWN0IGRhdGE9Ii4uL3BkZi9jaGVhdHNoZWV0LXJzdHVkaW8taWRlLnBkZiIgdHlwZT0iYXBwbGljYXRpb24vcGRmIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIj4NCg0KPC9vYmplY3Q+DQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KZG93bmxvYWR0aGlzOjpkb3dubG9hZF9maWxlKA0KICBwYXRoID0gIi4uL3BkZi9jaGVhdHNoZWV0LXJzdHVkaW8taWRlLnBkZiIsDQogIG91dHB1dF9uYW1lID0gImNoZWF0c2hlZXQtcnN0dWRpby1pZGUiLA0KICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgY2hlYXRzaGVldCIsDQogIGJ1dHRvbl90eXBlID0gInN1Y2Nlc3MiLA0KICBzZWxmX2NvbnRhaW5lZCA9IEZBTFNFDQopDQpgYGANCg0KIyMjIyBTaG9ydGN1dHMNCg0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgU2hvcnRjdXRzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCBDdHJsICsgU2hpZnQgKyBBICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg6YCJ5Lit6YOo5YiG6KGM5ZCO77yM5qC85byP5YyW5Luj56CBICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEN0cmwgKyBBbHQgKyBJICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBJbnNlcnQgY2h1bmsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgQWx0ICsgLSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaPkuWFpSBcPC0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEN0cmwgKyBTaGlmdCArIE0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmj5LlhaUgYCU+JWAg5oiWIGB8PmAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgQWx0ICsgU2hpZnQgKyBLICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOaYvuekuuW/q+aNt+mUriAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEN0cmwgKyBTaGlmdCArIE4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDmlrDlu7rohJrmnKwgLnLmlofku7YgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgQ3RybCArIEVudGVyPGJyIC8+Q3RybCArIFNoaWZ0ICsgRW50ZXI8YnIgLz5DdHJsICsgQWx0ICsgUiB8IOi/kOihjOS4gOihjOS7o+eggTxiciAvPui/kOihjOS7o+eggeWdlzxiciAvPui/kOihjOWFqOmDqOS7o+eggSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IFNoaWZ0ICsgSG9tZS9FbmQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDpgInkuK3lhYnmoIfliLDooYzpppYv5pyr5LmL6Ze055qE6YOo5YiGICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IFRhYiAvIEN0cmwrU3BhY2UgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDoh6rliqjooaXpvZA8YnIgLz7ovpPlhaXlrozlh73mlbDlkI3vvIzmjIl0YWLvvIzoh6rliqjmt7vliqDlvIDmi6zlj7co5ZKM6Zet5ous5Y+3KeOAgiAgICAgICAgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEN0cmwgKyBTaGlmdCArIEMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDms6jph4ov5Y+W5raI5rOo6YeKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgRjEgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IOafpeeci+W4ruWKqSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgQ3RybCsg4oaRICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwg5ZyoIENvbnNvbGUg5Lit6L6T5YWlInh4eCLvvIznhLblkI7mjIkgQ3RybCsg4oaR44CC5bCx5Y+v5Lul5YiX5Ye65omA5pyJ6L6T5YWl6L+H55qE5LulInh4eCLlvIDlpLTnmoTlkb3ku6TjgIIgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCg0KIyMjIyDku6PnoIHpq5jkuq4NCg0KR2l0SHViIOS4iuWPr+aQnOe0ouOAgeWuieijhSByc2NvZGVpbyDkuLvpopgNCg0KW2FudGhvbnlub3J0aC9yc2NvZGVpbzogQW4gUlN0dWRpbyB0aGVtZSBpbnNwaXJlZCBieSBWaXN1YWwgU3R1ZGlvIENvZGUuIChnaXRodWIuY29tKV0oaHR0cHM6Ly9naXRodWIuY29tL2FudGhvbnlub3J0aC9yc2NvZGVpbykNCg0KIyMjIFZTQ29kZQ0KDQojIyMjIOS8mOeCuQ0KDQotICAg6byg5qCH5oKs5YGc77yM5Y2z5Y+v5pi+56S65Y+Y6YeP55qE5a6a5LmJ5L+h5oGv5ZKM5Ye95pWw55qE5biu5Yqp5paH5qGj77yI5LuF6ZmQIFIg5YyF5Lit5Ye95pWw55qE5a6Y5pa55paH5qGj5ZKM5pys5paH5Lu25Lit6Ieq5a6a5LmJ5Ye95pWw55qE5a6a5LmJ77yM5peg5rOV5pi+56S65byV5YWl5qih5Z2X5Lit55qE6Ieq5a6a5LmJ5Ye95pWw55qE5a6a5LmJ77yJ77yM55yB5Y675LqG5p+l6ZiF5paH5qGj55qE5aSn6YeP5pe26Ze0DQotICAg5L+d5a2Y77yIYEN0cmwrU2DvvInml7boh6rliqjmoLzlvI/ljJYNCi0gICBgQ3RybCtFbnRlcmDoh6rliqjov5DooYzkuIDooYzvvIxgQ3RybCtTaGlmdCtFbnRlcmDoh6rliqjov5DooYzlvZPliY3mlofku7YNCg0KIyMjIyDphY3nva7mraXpqqQNCg0KMS4gIOWuieijhSBSIOWMhSBsYW5ndWFnZXNlcnZlcg0KDQogICAgYGBgIHINCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJsYW5ndWFnZXNlcnZlciIpDQogICAgYGBgDQoNCjIuICDlnKggVlNDb2RlIOaJqeWxleWVhuW6l+S4reWuieijhSBSIOaPkuS7tuOAgg0KDQogICAgMS4gIOWuieijheWujOaIkOWQjuWcqCBWU0NvZGUg6YWN572u5paH5Lu25Lit5pCc57SiYHIucnRlcm0ub3B0aW9uYO+8jOWIoOmZpGAtLW5vLXNhdmUsLS1uby1yZXN0b3JlYO+8jOa3u+WKoGAtLW5vLXNpdGUtZmlsZWDlkowgUi5leGUg55qE6Lev5b6EYC0tci1iaW5hcnk9QzpcXFByb2dyYW0gRmlsZXNcXFJcXFItNC4xLjNcXGJpblxcUi5leGVgDQoNCjMuICDlronoo4UgUmFkaWFu77ya5LiA5qy+546w5Luj55qEIFIgY29uc29sZe+8jOWug+aYr+eUqCBQeXRob24g57yW5YaZ55qEDQoNCiAgICBgYGAgcG93ZXJzaGVsbA0KICAgIHBpcCBpbnN0YWxsIHJhZGlhbg0KICAgIGBgYA0KDQogICAgMS4gIOWuieijheWujOaIkOWQjuWcqCBjbWQg5Lit6L6T5YWlIGByYWRpYW5gIOafpeeci+aYr+WQpuWuieijheaIkOWKn+OAgg0KDQogICAgMi4gIOiLpeWHuueOsCAiY2Fubm90IGRldGVybWluZSBSIEhPTUUi77yM5Y+v6IO95Zyo57O757ufIFBBVEgg5Lit5rKh5pyJIFIg6Lev5b6E77yM5YiZ5ZyoIFBBVEgg5Lit5re75Yqg5Y2z5Y+v77yb5oiW5a2Y5Zyo5aSa5LiqIFIg6Lev5b6E77yI5aaC5paw5a6J6KOF5LqG5p+Q5Liq54mI5pys77yJ6ICMIFJhZGlhbiDml6Dms5Xor4bliKvvvIzopoHlnKggVlNDb2RlIOS4reS/ruaUuWByLnJ0ZXJtLm9wdGlvbmDvvIzkuZ/lj6/ku6XlnKjorr7nva7nlYzpnaLnmoTlj7PkuIrop5LmiZPlvIAgc2V0dGluZy5qc29u5paH5Lu255u05o6l5L+u5pS577yaDQoNCiAgICAgICAgYGBgIHINCiAgICAgICAgInIucnRlcm0ub3B0aW9uIjogWw0KICAgICAgICAgICItLW5vLXNpdGUtZmlsZSIsDQogICAgICAgICAgIi0tci1iaW5hcnk9QzpcXFByb2dyYW0gRmlsZXNcXFJcXFItNC4yLjJcXGJpblxcUi5leGUiDQogICAgICAgIF0NCiAgICAgICAgYGBgDQoNCjQuICBWU0NvZGUg5LitIFJhZGlhbiDnm7jlhbPorr7nva4NCg0KICAgIDEuICDmkJzntKIgYHIucnRlcm0ud2luZG93c2DvvIzlsIblhbborr7nva7kuLogcmFkaWFuLmV4ZSDnmoTot6/lvoTjgILlnKggY21kIOS4re+8iHBvd2Vyc2hlbGwg5LiN6KGM77yJ6L6T5YWlIGB3aGVyZSByYWRpYW5gIOWPr+S7peiOt+WPluWFtui3r+W+hOOAgg0KICAgIDIuICDmkJzntKJgUjogQnJhY2tldGVkIFBhc3RlYOW5tuWLvumAie+8jOWQpuWImSBSYWRpYW4g5LiN5Lya5ZCv55SoDQogICAgMy4gIOaQnOe0omByLnNlc3Npb25XYXRjaGVyYOW5tuWLvumAiQ0KDQo1LiAg5a6a5LmJ5ZyoIFIg6ISa5pys5Lit5L2/55So55qE5b+r5o236ZSuDQoNCiAgICAxLiAg5pyA6YeN6KaB55qE5Lik5Liq5b+r5o236ZSu77ya6L6T5YWlIGA8LWAg55qE5b+r5o236ZSuIGBBbHRgK2AtYO+8m+i+k+WFpSBgJT4lYCDmiJYgYHw+YCDnmoTlv6vmjbfplK4gYEN0cmxgK2BTaGlmdGArYE1gDQoNCiAgICAyLiAg5pa55rOV77yaVlNDb2RlIOS4reaJk+W8gCBrZXliaW5kaW5ncy5qc29uIO+8iGBDdHJsYCtgS2AgYEN0cmxgK2BTYCDnhLblkI7lj7PkuIrop5LmiZPlvIDphY3nva7mlofku7bvvInvvIznu5Hlrprlv6vmjbfplK7opobnm5bpu5jorqTlgLwNCg0KICAgICAgICBgYGAgcg0KICAgICAgICBbDQogICAgICAgICAgew0KICAgICAgICAgICAgImtleSI6ICJhbHQrLSIsDQogICAgICAgICAgICAiY29tbWFuZCI6ICJ0eXBlIiwNCiAgICAgICAgICAgICJ3aGVuIjogImVkaXRvckxhbmdJZCA9PSByIHx8IGVkaXRvckxhbmdJZCA9PSBybWQgJiYgZWRpdG9yVGV4dEZvY3VzIiwNCiAgICAgICAgICAgICJhcmdzIjogew0KICAgICAgICAgICAgICAidGV4dCI6ICIgPC0gIg0KICAgICAgICAgICAgfQ0KICAgICAgICAgIH0sDQogICAgICAgICAgew0KICAgICAgICAgICAgImtleSI6ICJjdHJsK3NoaWZ0K20iLA0KICAgICAgICAgICAgImNvbW1hbmQiOiAidHlwZSIsDQogICAgICAgICAgICAid2hlbiI6ICJlZGl0b3JMYW5nSWQgPT0gciB8fCBlZGl0b3JMYW5nSWQgPT0gcm1kICYmIGVkaXRvclRleHRGb2N1cyIsDQogICAgICAgICAgICAiYXJncyI6IHsNCiAgICAgICAgICAgICAgInRleHQiOiAiICU+JSAiDQogICAgICAgICAgICB9DQogICAgICAgICAgfQ0KICAgICAgICBdDQogICAgICAgIGBgYA0KDQpb5aaC5L2V5ZyoIFZTQ09ERSDkuK3pq5jmlYjkvb/nlKggUiDor63oqIAg77yI5Zu+5paH6K+m6Kej77yJXF9CYWltb2MtQ1NETuWNmuWuol0oaHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTEyNjIyNTMvYXJ0aWNsZS9kZXRhaWxzLzExMzgzNzcyMCkNCg==