版权声明:本套课程材料开源,使用和分享必须遵守「创作共用许可协议 CC BY-NC-SA」(来源引用-非商业用途使用-以相同方式共享)。


Chap02:函数对象

往期要点回顾

本章要点目录

## 本章所需R包
library(bruceR)

bruceR (v2026.1)
Broadly Useful Convenient and Efficient R functions

Packages also loaded:
✔ dplyr         ✔ data.table
✔ tidyr         ✔ emmeans
✔ stringr       ✔ lmerTest
✔ forcats       ✔ effectsize
✔ ggplot2       ✔ performance
✔ cowplot       ✔ interactions

Main functions of `bruceR`:
cc()            Describe()  TTEST()
add()           Freq()      MANOVA()
.mean()         Corr()      EMMEANS()
set.wd()        Alpha()     PROCESS()
import()        EFA()       model_summary()
print_table()   CFA()       lavaan_summary()

For full functionality, please install all dependencies:
install.packages("bruceR", dep=TRUE)

Online documentation:
https://psychbruce.github.io/bruceR

To use this package in publications, please cite:
Bao, H. W. S. (2021). bruceR: Broadly useful convenient and efficient R functions (Version 2026.1) [Computer software]. https://doi.org/10.32614/CRAN.package.bruceR

R对象类型与相互转换

【知识点】R对象与函数

“万物皆对象,万事皆函数。” —— John Chambers(R语言创始人之一)

  • “Everything that exists in R is an object.”
    (所用的一切都是对象)
    • 向量(vector)⭐️
      • 数值向量(numeric vector)
      • 字符向量(character vector)
      • 逻辑向量(logical vector)
    • 矩阵(matrix)
    • 数组(array)
    • 列表(list)⭐️
      • 混合存储多种类型的对象和数据
    • 数据框(data frame)⭐️
      • R里90%的分析都是围绕data.frame(包括data.table)展开的
      • 相当于Excel表格在R里的样子(每一列是一个变量,每一行是一个观测值)
  • “Everything that happens in R is the result of a function call.”
    (所做的一切都是函数)

向量(vector)

  • 单维、单类型数据

(1)数值向量

【实践1】数值向量

函数知识点

  • c():生成任何类型向量(合并一组同类型的数据)⭐️
    • “c”表示“combine”
  • seq():生成数值序列
  • rep():重复向量元素
  • as.factor():将任意类型向量转换为因子型向量⭐️
    • 因子(factor)本质上是带有“类别/分组信息”的向量(将向量中的不重复元素排序后作为类别选项)
    • 因子水平(levels)是所有可能的类别(当涉及类别间的对比时,默认以第一类为参照组)
1
[1] 1
1:5
[1] 1 2 3 4 5
c(1, 2, 3, 4, 5)
[1] 1 2 3 4 5
c(1:5, 10, 15)
[1]  1  2  3  4  5 10 15
seq(1, 5, 0.5)
[1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
seq(from=1, to=5, by=0.5)
[1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0
rep(1:2, 3)
[1] 1 2 1 2 1 2
rep(1:2, each=3)
[1] 1 1 1 2 2 2
a = c(2, 0, 0, 0, 6, 2)
a
[1] 2 0 0 0 6 2
unique(a)
[1] 2 0 6
a = as.factor(a)
a
[1] 2 0 0 0 6 2
Levels: 0 2 6
levels(a)  # 因子水平:所有可能的类别
[1] "0" "2" "6"

(2)字符向量

【实践2】字符向量

函数知识点

  • length():计算对象内的元素个数
  • nchar():计算字符串的字符个数
  • str():显示R对象的内部结构(常用于探索数据结构)⭐️
  • factor():比as.factor()更完整的因子型向量设定⭐️
    • levels:定义因子水平顺序
    • labels:定义因子取值标签
  • cc():比c()更方便的字符向量设定
    • 自动根据分隔符号(英文逗号、换行符等)拆解字符串
c(1:5, 10.5, "next")  # 如果混着放,统统变成字符型!
[1] "1"    "2"    "3"    "4"    "5"    "10.5" "next"
"Psychology"  # 一个字符串,本质上是一个长度为1的字符向量
[1] "Psychology"
"Psychology"[1]  # 向量的第1个元素
[1] "Psychology"
"Psychology"[0]  # 向量的第0个元素(没有)
character(0)
"Psychology"[2]  # 向量的第2个元素(缺失)
[1] NA
nchar("Psychology")  # 字符个数为10
[1] 10
a = c("Psychology", "ECNU", "Shanghai", "China")
a
[1] "Psychology" "ECNU"       "Shanghai"   "China"     
length(a)
[1] 4
nchar(a)  # 向量化计算
[1] 10  4  8  5
class(a)
[1] "character"
str(a)  # structure:显示R对象的结构
 chr [1:4] "Psychology" "ECNU" "Shanghai" "China"
as.factor(a)
[1] Psychology ECNU       Shanghai   China     
Levels: China ECNU Psychology Shanghai
factor(a, levels=c("China", "Shanghai", "ECNU", "Psychology"))
[1] Psychology ECNU       Shanghai   China     
Levels: China Shanghai ECNU Psychology
factor(
  a,
  levels = c("China", "Shanghai", "ECNU", "Psychology"),
  labels = c("CN", "SH", "ECNU", "Psy")
)
[1] Psy  ECNU SH   CN  
Levels: CN SH ECNU Psy
## cc() 用法:必须是英文逗号或换行符
cc("心理学, 社会学, 教育学, 管理学, 计算机, 人工智能")
[1] "心理学"   "社会学"   "教育学"   "管理学"   "计算机"   "人工智能"
cc("
心理学
社会学
教育学
管理学
计算机
人工智能
")
[1] "心理学"   "社会学"   "教育学"   "管理学"   "计算机"   "人工智能"

(3)逻辑向量

【实践3】逻辑向量

函数知识点

  • %in%:向量化判断前一个对象的每个元素是否在后一个对象中
  • ifelse():根据条件语句,向量化逻辑判断
c(TRUE, TRUE, FALSE, TRUE, FALSE, FALSE)
[1]  TRUE  TRUE FALSE  TRUE FALSE FALSE
1:5 > 2
[1] FALSE FALSE  TRUE  TRUE  TRUE
c(0, 5, 10, 15) %in% 1:10
[1] FALSE  TRUE  TRUE FALSE
ifelse(1:10 >= 5, "Yes", "No")  # ifelse(test, yes, no)
 [1] "No"  "No"  "No"  "No"  "Yes" "Yes" "Yes" "Yes" "Yes" "Yes"

【知识点】条件判断运算符与对象类型判断函数

## 条件判断运算符
a == b
a != b
a > b
a < b
a >= b
a <= b
a %in% b

## 对象类型判断函数
is.na(x)
is.null(x)
is.numeric(x)
is.character(x)
is.factor(x)
# 还有非常多其他 is.xxx() 系列函数!

向量相互转换

【实践4】逻辑向量—数值向量—字符向量的相互转换

函数知识点

  • as.logical()0转换为FALSE,非0转换为TRUE
  • as.character():转换为字符向量
  • as.numeric():转换为数值向量(无法转换的记为NA缺失值)
  • %>%:管道操作符⭐️(tidyverse系列包的通用函数)
    • 将前一个运行结果传递给后一个函数的第一个参数位置
    • 如果需要传递给后一个函数的其他参数位置,需要用点.占位
    • 快捷键:Ctrl + Shift + M(注意检查其他软件的快捷键冲突)
    • 如果提示没有"%>%"这个函数,请library(bruceR)library(tidyverse)
as.logical(0:5)
[1] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
as.character(0:5)
[1] "0" "1" "2" "3" "4" "5"
as.numeric(c("-1.5", "0", "1.5"))
[1] -1.5  0.0  1.5
as.numeric(c("1.2", "-.12", "1.2.3"))  # NA表示缺失值
Warning: 强制改变过程中产生了NA
[1]  1.20 -0.12    NA
a = c("Psychology", "ECNU", "Shanghai", "China")
as.factor(a)
[1] Psychology ECNU       Shanghai   China     
Levels: China ECNU Psychology Shanghai
a %>% as.factor()
[1] Psychology ECNU       Shanghai   China     
Levels: China ECNU Psychology Shanghai
a %>% as.factor() %>% as.numeric()
[1] 3 2 4 1
a %>% as.factor() %>% as.numeric() %>% c(200062)
[1]      3      2      4      1 200062
a %>% as.factor() %>% as.numeric() %>% c(., 200062)
[1]      3      2      4      1 200062
a %>% as.factor() %>% as.numeric() %>% c(200062, .)
[1] 200062      3      2      4      1

向量元素操作

【实践5】向量元素命名与提取

函数知识点

  • names():对象元素名称或命名
    • 直接使用:返回对象元素名称
    • 赋值使用:赋予对象元素名称(将会改变当前对象)
  • x[...][]操作符取对象元素内容
    • x[逻辑/数值向量]:取对应位置元素(若为负数,则删除对应元素)
    • x[字符向量]:取对应名称元素
  • rev():向量反向排列
  • sort():按照数值大小或字母序重新排列
  • unique():取向量中的所有独特元素(去重复值)
  • table():向量元素频次统计
LETTERS  # 26个英文字母
 [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"
a = 1:5
names(a)  # 直接用法(刚刚出生,还没名字)
NULL
names(a) = LETTERS[1:5]  # 赋值用法,a已改变
a
A B C D E 
1 2 3 4 5 
names(a)  # 现在上好户口,有名字了!
[1] "A" "B" "C" "D" "E"
a["E"]
E 
5 
a[c("A", "C", "E")]
A C E 
1 3 5 
a[-1]  # 删除元素,但不改变a
B C D E 
2 3 4 5 
a[-(2:4)]  # 删除元素,但不改变a
A E 
1 5 
x = c(2, 0, 0, 0, 6, 2)
rev(x)
[1] 2 6 0 0 0 2
sort(x)
[1] 0 0 0 2 2 6
unique(x)
[1] 2 0 6
x %>% unique() %>% sort() %>% rev()
[1] 6 2 0
table(x)
x
0 2 6 
3 2 1 
x %in% 1:5
[1]  TRUE FALSE FALSE FALSE FALSE  TRUE
x[x %in% 1:5]
[1] 2 2
x[x %in% 1:5] = NA
x
[1] NA  0  0  0  6 NA

【实践6】向量代数运算

函数知识点

  • sum()mean():对一个向量对象求和、求平均值
  • paste():把向量元素拼接在一起
    • sep参数:多个输入之间的黏合字符
    • collaspe参数:向量不同元素之间的黏合字符
a = 1:5
a + 1    # 1:5 + 1 【思考:a + 1:2 的结果?】
[1] 2 3 4 5 6
a * 2    # 1:5 * 2
[1]  2  4  6  8 10
a^2      # (1:5)^2
[1]  1  4  9 16 25
a * 5:1  # 对应位置相乘,长度必须相等
[1] 5 8 9 8 5
a %% 2   # 取余数
[1] 1 0 1 0 1
sum(1:100)  # 高斯求和
[1] 5050
mean(1:100)  # 求平均值
[1] 50.5
paste(1, 2, 3, 4, 5, sep=" ")  # 多个输入之间
[1] "1 2 3 4 5"
paste(1:5, collapse=" ")  # 向量不同元素之间
[1] "1 2 3 4 5"
paste(1:10, collapse="+")  # 打印计算公式
[1] "1+2+3+4+5+6+7+8+9+10"
cat(paste(1:10, collapse="+"), "=", sum(1:10))
1+2+3+4+5+6+7+8+9+10 = 55
abs(log(sqrt(pi)))
[1] 0.5724
pi |> sqrt() |> log() |> abs()     # 新版R内置管道操作符
[1] 0.5724
pi %>% sqrt() %>% log() %>% abs()  # tidy系列管道操作符(建议使用)
[1] 0.5724

列表(list)

  • 单维、多类型数据

【实践7】列表构建与操作

函数知识点

  • list():建立一个列表对象,可存储任意类型、任意结构的对象
    • $:根据元素名称,取列表中的元素内容(Tab键弹出所有元素名称)
    • [[...]]:取列表中对应位置的元素内容(不同于[...]
  • unlist():解除列表结构,还原为向量
  • lapply():批量应用函数,返回为列表
输入后按Tab键弹出所有元素名称
输入后按Tab键弹出所有元素名称
l = list(Year=2000:2015, Month=1:12, Day=1:31, Letter=LETTERS)
l
$Year
 [1] 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014
[16] 2015

$Month
 [1]  1  2  3  4  5  6  7  8  9 10 11 12

$Day
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
[26] 26 27 28 29 30 31

$Letter
 [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "O" "P" "Q" "R" "S"
[20] "T" "U" "V" "W" "X" "Y" "Z"
names(l)
[1] "Year"   "Month"  "Day"    "Letter"
length(l)
[1] 4
l$Year
 [1] 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014
[16] 2015
l[2]    # 取第2个子集,返回list子集
$Month
 [1]  1  2  3  4  5  6  7  8  9 10 11 12
l[[2]]  # 取第2个元素,返回元素本身,相当于 l$Month
 [1]  1  2  3  4  5  6  7  8  9 10 11 12
unlist(l)  # 解除列表结构,强行统一对象类型(数值 -> 字符)
   Year1    Year2    Year3    Year4    Year5    Year6    Year7    Year8 
  "2000"   "2001"   "2002"   "2003"   "2004"   "2005"   "2006"   "2007" 
   Year9   Year10   Year11   Year12   Year13   Year14   Year15   Year16 
  "2008"   "2009"   "2010"   "2011"   "2012"   "2013"   "2014"   "2015" 
  Month1   Month2   Month3   Month4   Month5   Month6   Month7   Month8 
     "1"      "2"      "3"      "4"      "5"      "6"      "7"      "8" 
  Month9  Month10  Month11  Month12     Day1     Day2     Day3     Day4 
     "9"     "10"     "11"     "12"      "1"      "2"      "3"      "4" 
    Day5     Day6     Day7     Day8     Day9    Day10    Day11    Day12 
     "5"      "6"      "7"      "8"      "9"     "10"     "11"     "12" 
   Day13    Day14    Day15    Day16    Day17    Day18    Day19    Day20 
    "13"     "14"     "15"     "16"     "17"     "18"     "19"     "20" 
   Day21    Day22    Day23    Day24    Day25    Day26    Day27    Day28 
    "21"     "22"     "23"     "24"     "25"     "26"     "27"     "28" 
   Day29    Day30    Day31  Letter1  Letter2  Letter3  Letter4  Letter5 
    "29"     "30"     "31"      "A"      "B"      "C"      "D"      "E" 
 Letter6  Letter7  Letter8  Letter9 Letter10 Letter11 Letter12 Letter13 
     "F"      "G"      "H"      "I"      "J"      "K"      "L"      "M" 
Letter14 Letter15 Letter16 Letter17 Letter18 Letter19 Letter20 Letter21 
     "N"      "O"      "P"      "Q"      "R"      "S"      "T"      "U" 
Letter22 Letter23 Letter24 Letter25 Letter26 
     "V"      "W"      "X"      "Y"      "Z" 
## lapply(向量或列表, 函数)
lapply(l, length)  # 每个元素批量应用length()函数
$Year
[1] 16

$Month
[1] 12

$Day
[1] 31

$Letter
[1] 26
lapply(l, mean)    # 每个元素批量应用mean()函数
Warning in mean.default(X[[i]], ...): 参数不是数值也不是逻辑值:返回NA
$Year
[1] 2008

$Month
[1] 6.5

$Day
[1] 16

$Letter
[1] NA
a = 1:5
a^2
[1]  1  4  9 16 25
lapply(a, function(x) x^2)  # x是形参(形式参数)
[[1]]
[1] 1

[[2]]
[1] 4

[[3]]
[1] 9

[[4]]
[1] 16

[[5]]
[1] 25
lapply(a, function(x) x^2) %>% unlist()
[1]  1  4  9 16 25

R函数使用与参数设置

【知识点】R函数体的结构

  • function(...):函数接收输入参数,内部操作后,返回输出结果
    • { }:大括号内定义所有形式参数(形参)的操作过程
    • 返回结果可用两种方式
      • return(...):显式返回
      • invisible(...):隐式返回
func = function(param1, param2, ...) {
  ## 函数内部操作
  results = param1 + param2
  cat(param1, "+", param2, "=", results, "\n")
  
  ## 函数返回结果
  return(results)
  # 也可以隐式返回:
  # invisible(results)
}
func(16, 9)
16 + 9 = 25 
[1] 25

常用R函数使用

【实践8】文件夹里有什么?

## 请自己运行
library(bruceR)
set.wd()  # 设置工作路径为当前打开文件的路径

list.files()          # 列出当前路径的所有文件/文件夹
list.files("../")     # 列出上一级路径的所有文件/文件夹
list.files("../../")  # 列出上两级路径的所有文件/文件夹

【实践9】今天晚上吃什么?

## 请自己运行
choice = c("河西食堂", "河东食堂", "环球港", "点外卖")
sample(choice, size=1)  # 随机抽样

# 设置随机种子,使结果可重复
set.seed(123)  # 随机种子可以是任意整数,没有特定含义
sample(choice, size=1)

【实践10】正态分布随机数的平均值和标准差?

set.seed(123)  # 设置随机种子,使结果可重复
x = rnorm(30)  # 生成30个随机数(正态分布)

mean(x)  # 求平均值
[1] -0.0471
sd(x)    # 求标准差
[1] 0.981
hist(x)  # 画直方图

x > mean(x) + sd(x)         # 返回TRUE/FALSE向量
 [1] FALSE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE
[13] FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[25] FALSE FALSE FALSE FALSE FALSE  TRUE
x[x > mean(x) + sd(x)]      # 返回(取出)符合条件的数值
[1] 1.559 1.715 1.224 1.787 1.254
which(x > mean(x) + sd(x))  # 返回符合条件数值所在位置
[1]  3  6 11 16 30
set.seed(123)  # 设置随机种子,使结果可重复
x = rnorm(1000)  # 生成1000个随机数(正态分布)

mean(x)  # 求平均值
[1] 0.01613
sd(x)    # 求标准差
[1] 0.9917
hist(x)  # 画直方图

【实践11】“Hello World”的几种打印方法?

函数知识点

  • cli是命令行交互的专业R包,系统性提供了各种输出文本设定函数
print("Hello World!")
[1] "Hello World!"
cat("Hello World!")
Hello World!
cat("Hello", "World!")
Hello World!
cli::cli_text("Hello World!")
Hello World!
cli::cli_h1("Hello World!")
── Hello World! ────────────────────────────────────────────────────────────────
cli::cli_h2("Hello World!")
── Hello World! ──
cli::cli_h3("Hello World!")
── Hello World! 
cli::cli_alert("Hello World!")
→ Hello World!
cli::cli_alert_success("Hello World!")
✔ Hello World!
cli::cli_alert_info("Hello World!")
ℹ Hello World!
cli::cli_alert_warning("Hello World!")
! Hello World!
cli::cli_alert_danger("Hello World!")
✖ Hello World!

R逻辑控制语句

if判断语句

if (condition) {
  # Do something
} else if {
  # Do something
} else {
  # Do sth. else
}
x = 3
if (x %in% 1:5) {
  cat("Yes")
} else {
  cat("No")
}
Yes

while循环语句

while (condition) {
  # Do something
}
x = 1
while (x < 5) {
  cat(x, "is smaller than 5\n")
  x = x + 1
}
1 is smaller than 5
2 is smaller than 5
3 is smaller than 5
4 is smaller than 5

for循环语句

for (var in seq) {
  # Do something
}
for (x in 1:5) {
  y = x * 2
  print(y)
}
[1] 2
[1] 4
[1] 6
[1] 8
[1] 10

【作业3】编程解决抛硬币问题

问题情境:一枚硬币,连续抛了N次都是正面朝上,再抛一次反面朝上的概率是多少?

  • 比如,一枚硬币,连续抛了3次都是正面朝上,再抛一次反面朝上的概率是多少?
  • 连续10次呢?连续1000次呢?连续1亿次呢?……
  • 思路提示:用假设检验的思想,判断这枚硬币是不是普通硬币(H0:硬币是普通的)

作业要求:

  • 编写一段R程序,计算并判断至少连续多少次正面朝上,可以认定硬币不再是普通硬币(拒绝零假设)
  • 尽可能用R Markdown完成

平台提交:

  • R代码和运行结果截图
LS0tDQp0aXRsZTogIuOAilLor63oqIDjgIvnrKwy56ug77ya5Ye95pWw5a+56LGhIg0Kc3VidGl0bGU6IDxhIGhyZWY9Imh0dHBzOi8vcHN5Y2hicnVjZS5naXRodWIuaW8vUkNvdXJzZS8iPui/lOWbnuivvueoi+S4u+mhtTwvYT4NCmF1dGhvcjogIuaOiOivvuaVmeW4iO+8muWMheWvkuWQtOmcnCINCiMgZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGFuY2hvcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCiAgICBjc3M6IFJtZENTUy5jc3MNCi0tLQ0KDQpgYGB7PWh0bWx9DQo8cCBzdHlsZT0iZm9udC1zaXplOiAxMnB4Ij7niYjmnYPlo7DmmI7vvJrmnKzlpZfor77nqIvmnZDmlpnlvIDmupDvvIzkvb/nlKjlkozliIbkuqvlv4XpobvpgbXlrojjgIzliJvkvZzlhbHnlKjorrjlj6/ljY/orq4gQ0MgQlktTkMtU0HjgI3vvIjmnaXmupDlvJXnlKgt6Z2e5ZWG5Lia55So6YCU5L2/55SoLeS7peebuOWQjOaWueW8j+WFseS6q++8ieOAgjxpbWcgc3JjPSJpbWcvQ0MtQlktTkMtU0EuanBnIiB3aWR0aD0iMTIwcHgiIGhlaWdodD0iNDJweCIgc3R5bGU9ImZsb2F0OiByaWdodCIgLz48L3A+DQpgYGANCg0KYGBge3IgQ29uZmlnLCBpbmNsdWRlPUZBTFNFfQ0Kb3B0aW9ucygNCiAga25pdHIua2FibGUuTkEgPSAiIiwNCiAgZGlnaXRzID0gNA0KKQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBjb21tZW50ID0gIiIsDQogIGZpZy53aWR0aCA9IDYsDQogIGZpZy5oZWlnaHQgPSA0LA0KICBkcGkgPSAzMDANCikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBDaGFwMDLvvJrlh73mlbDlr7nosaENCg0KIyMjIyDlvoDmnJ/opoHngrnlm57pob4NCg0KLSAgIFtDaGFwMDEgXCMgUuWMheWuieijheS4jueuoeeQhl0oaHR0cHM6Ly9wc3ljaGJydWNlLmdpdGh1Yi5pby9SQ291cnNlL0NoYXAwMSNyJUU1JThDJTg1JUU1JUFFJTg5JUU4JUEzJTg1JUU0JUI4JThFJUU3JUFFJUExJUU3JTkwJTg2KXsudXJpfQ0KDQojIyMjIOacrOeroOimgeeCueebruW9lQ0KDQotICAgW+OAkOefpeivhueCueOAkVLlr7nosaHkuI7lh73mlbBdKCPnn6Xor4bngrly5a+56LGh5LiO5Ye95pWwKQ0KLSAgIFvjgJDlrp7ot7Ux44CR5pWw5YC85ZCR6YePXSgj5a6e6Le1MeaVsOWAvOWQkemHjykNCi0gICBb44CQ5a6e6Le1MuOAkeWtl+espuWQkemHj10oI+Wunui3tTLlrZfnrKblkJHph48pDQotICAgW+OAkOWunui3tTPjgJHpgLvovpHlkJHph49dKCPlrp7ot7Uz6YC76L6R5ZCR6YePKQ0KLSAgIFvjgJDnn6Xor4bngrnjgJHmnaHku7bliKTmlq3ov5DnrpfnrKbkuI7lr7nosaHnsbvlnovliKTmlq3lh73mlbBdKCPnn6Xor4bngrnmnaHku7bliKTmlq3ov5DnrpfnrKbkuI7lr7nosaHnsbvlnovliKTmlq3lh73mlbApDQotICAgW+OAkOWunui3tTTjgJHpgLvovpHlkJHph4/igJTmlbDlgLzlkJHph4/igJTlrZfnrKblkJHph4/nmoTnm7jkupLovazmjaJdKCPlrp7ot7U06YC76L6R5ZCR6YeP5pWw5YC85ZCR6YeP5a2X56ym5ZCR6YeP55qE55u45LqS6L2s5o2iKe+8iOmHjeeCue+8iQ0KLSAgIFvjgJDlrp7ot7U144CR5ZCR6YeP5YWD57Sg5ZG95ZCN5LiO5o+Q5Y+WXSgj5a6e6Le1NeWQkemHj+WFg+e0oOWRveWQjeS4juaPkOWPlikNCi0gICBb44CQ5a6e6Le1NuOAkeWQkemHj+S7o+aVsOi/kOeul10oI+Wunui3tTblkJHph4/ku6PmlbDov5DnrpcpDQotICAgW+OAkOWunui3tTfjgJHliJfooajmnoTlu7rkuI7mk43kvZxdKCPlrp7ot7U35YiX6KGo5p6E5bu65LiO5pON5L2cKe+8iOmHjeeCue+8iQ0KLSAgIFvjgJDnn6Xor4bngrnjgJFS5Ye95pWw5L2T55qE57uT5p6EXSgj55+l6K+G54K5cuWHveaVsOS9k+eahOe7k+aehCnvvIjph43ngrnvvIkNCi0gICBb44CQ5a6e6Le1OOOAkeaWh+S7tuWkuemHjOacieS7gOS5iO+8n10oI+Wunui3tTjmlofku7blpLnph4zmnInku4DkuYgpDQotICAgW+OAkOWunui3tTnjgJHku4rlpKnmmZrkuIrlkIPku4DkuYjvvJ9dKCPlrp7ot7U55LuK5aSp5pma5LiK5ZCD5LuA5LmIKQ0KLSAgIFvjgJDlrp7ot7UxMOOAkeato+aAgeWIhuW4g+maj+acuuaVsOeahOW5s+Wdh+WAvOWSjOagh+WHhuW3ru+8n10oI+Wunui3tTEw5q2j5oCB5YiG5biD6ZqP5py65pWw55qE5bmz5Z2H5YC85ZKM5qCH5YeG5beuKQ0KLSAgIFvjgJDlrp7ot7UxMeOAkeKAnEhlbGxvIFdvcmxk4oCd55qE5Yeg56eN5omT5Y2w5pa55rOV77yfXSgj5a6e6Le1MTFoZWxsby13b3JsZOeahOWHoOenjeaJk+WNsOaWueazlSkNCg0KYGBge3J9DQojIyDmnKznq6DmiYDpnIBS5YyFDQpsaWJyYXJ5KGJydWNlUikNCmBgYA0KDQojIFLlr7nosaHnsbvlnovkuI7nm7jkupLovazmjaINCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtNDIyNzE4OTE4Ny5wbmcpDQoNCiMjIyMg44CQ55+l6K+G54K544CRUuWvueixoeS4juWHveaVsCB7I+efpeivhueCuXLlr7nosaHkuI7lh73mlbB9DQoNCj4g4oCc5LiH54mp55qG5a+56LGh77yM5LiH5LqL55qG5Ye95pWw44CC4oCdIOKAlOKAlCBKb2huIENoYW1iZXJz77yIUuivreiogOWIm+Wni+S6uuS5i+S4gO+8iQ0KDQotICAg4oCcRXZlcnl0aGluZyB0aGF0IGV4aXN0cyBpbiBSIGlzIGFuIG9iamVjdC7igJ1cDQogICAg77yI5omA55So55qE5LiA5YiH6YO95piv5a+56LGh77yJDQogICAgLSAgIOWQkemHj++8iHZlY3Rvcu+8ieKtkO+4jw0KICAgICAgICAtICAg5pWw5YC85ZCR6YeP77yIbnVtZXJpYyB2ZWN0b3LvvIkNCiAgICAgICAgLSAgIOWtl+espuWQkemHj++8iGNoYXJhY3RlciB2ZWN0b3LvvIkNCiAgICAgICAgLSAgIOmAu+i+keWQkemHj++8iGxvZ2ljYWwgdmVjdG9y77yJDQogICAgLSAgIOefqemYte+8iG1hdHJpeO+8iQ0KICAgIC0gICDmlbDnu4TvvIhhcnJhee+8iQ0KICAgIC0gICDliJfooajvvIhsaXN077yJ4q2Q77iPDQogICAgICAgIC0gICDmt7flkIjlrZjlgqjlpJrnp43nsbvlnovnmoTlr7nosaHlkozmlbDmja4NCiAgICAtICAg5pWw5o2u5qGG77yIZGF0YSBmcmFtZe+8ieKtkO+4jw0KICAgICAgICAtICAgUumHjDkwJeeahOWIhuaekOmDveaYr+WbtOe7lWBkYXRhLmZyYW1lYO+8iOWMheaLrGBkYXRhLnRhYmxlYO+8ieWxleW8gOeahA0KICAgICAgICAtICAg55u45b2T5LqORXhjZWzooajmoLzlnKhS6YeM55qE5qC35a2Q77yI5q+P5LiA5YiX5piv5LiA5Liq5Y+Y6YeP77yM5q+P5LiA6KGM5piv5LiA5Liq6KeC5rWL5YC877yJDQotICAg4oCcRXZlcnl0aGluZyB0aGF0IGhhcHBlbnMgaW4gUiBpcyB0aGUgcmVzdWx0IG9mIGEgZnVuY3Rpb24gY2FsbC7igJ1cDQogICAg77yI5omA5YGa55qE5LiA5YiH6YO95piv5Ye95pWw77yJDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTI0ODI1NDM5NjYucG5nKQ0KDQojIyDlkJHph4/vvIh2ZWN0b3LvvIkNCg0KLSAgIOWNlee7tOOAgeWNleexu+Wei+aVsOaNrg0KDQojIyMg77yIMe+8ieaVsOWAvOWQkemHjw0KDQojIyMjIOOAkOWunui3tTHjgJHmlbDlgLzlkJHph48geyPlrp7ot7Ux5pWw5YC85ZCR6YePfQ0KDQrlh73mlbDnn6Xor4bngrkNCg0KLSAgIGBjKClg77ya55Sf5oiQ5Lu75L2V57G75Z6L5ZCR6YeP77yI5ZCI5bm25LiA57uE5ZCM57G75Z6L55qE5pWw5o2u77yJ4q2Q77iPDQogICAgLSAgIOKAnGPigJ3ooajnpLrigJxjb21iaW5l4oCdDQotICAgYHNlcSgpYO+8mueUn+aIkOaVsOWAvOW6j+WIlw0KLSAgIGByZXAoKWDvvJrph43lpI3lkJHph4/lhYPntKANCi0gICBgYXMuZmFjdG9yKClg77ya5bCG5Lu75oSP57G75Z6L5ZCR6YeP6L2s5o2i5Li65Zug5a2Q5Z6L5ZCR6YeP4q2Q77iPDQogICAgLSAgIOWboOWtkO+8iGZhY3Rvcu+8ieacrOi0qOS4iuaYr+W4puacieKAnOexu+WIqy/liIbnu4Tkv6Hmga/igJ3nmoTlkJHph4/vvIjlsIblkJHph4/kuK3nmoTkuI3ph43lpI3lhYPntKDmjpLluo/lkI7kvZzkuLrnsbvliKvpgInpobnvvIkNCiAgICAtICAg5Zug5a2Q5rC05bmz77yIbGV2ZWxz77yJ5piv5omA5pyJ5Y+v6IO955qE57G75Yir77yI5b2T5raJ5Y+K57G75Yir6Ze055qE5a+55q+U5pe277yM6buY6K6k5Lul56ys5LiA57G75Li65Y+C54Wn57uE77yJDQoNCmBgYHtyfQ0KMQ0KMTo1DQpjKDEsIDIsIDMsIDQsIDUpDQpjKDE6NSwgMTAsIDE1KQ0KDQpzZXEoMSwgNSwgMC41KQ0Kc2VxKGZyb209MSwgdG89NSwgYnk9MC41KQ0KDQpyZXAoMToyLCAzKQ0KcmVwKDE6MiwgZWFjaD0zKQ0KDQphID0gYygyLCAwLCAwLCAwLCA2LCAyKQ0KYQ0KdW5pcXVlKGEpDQoNCmEgPSBhcy5mYWN0b3IoYSkNCmENCmxldmVscyhhKSAgIyDlm6DlrZDmsLTlubPvvJrmiYDmnInlj6/og73nmoTnsbvliKsNCmBgYA0KDQojIyMg77yIMu+8ieWtl+espuWQkemHjw0KDQojIyMjIOOAkOWunui3tTLjgJHlrZfnrKblkJHph48geyPlrp7ot7Uy5a2X56ym5ZCR6YePfQ0KDQrlh73mlbDnn6Xor4bngrkNCg0KLSAgIGBsZW5ndGgoKWDvvJrorqHnrpflr7nosaHlhoXnmoTlhYPntKDkuKrmlbANCi0gICBgbmNoYXIoKWDvvJrorqHnrpflrZfnrKbkuLLnmoTlrZfnrKbkuKrmlbANCi0gICBgc3RyKClg77ya5pi+56S6UuWvueixoeeahOWGhemDqOe7k+aehO+8iOW4uOeUqOS6juaOoue0ouaVsOaNrue7k+aehO+8ieKtkO+4jw0KLSAgIGBmYWN0b3IoKWDvvJrmr5RgYXMuZmFjdG9yKClg5pu05a6M5pW055qE5Zug5a2Q5Z6L5ZCR6YeP6K6+5a6a4q2Q77iPDQogICAgLSAgIGBsZXZlbHNg77ya5a6a5LmJ5Zug5a2Q5rC05bmz6aG65bqPDQogICAgLSAgIGBsYWJlbHNg77ya5a6a5LmJ5Zug5a2Q5Y+W5YC85qCH562+DQotICAgYGNjKClg77ya5q+UYGMoKWDmm7Tmlrnkvr/nmoTlrZfnrKblkJHph4/orr7lrpoNCiAgICAtICAg6Ieq5Yqo5qC55o2u5YiG6ZqU56ym5Y+377yI6Iux5paH6YCX5Y+344CB5o2i6KGM56ym562J77yJ5ouG6Kej5a2X56ym5LiyDQoNCmBgYHtyfQ0KYygxOjUsIDEwLjUsICJuZXh0IikgICMg5aaC5p6c5re3552A5pS+77yM57uf57uf5Y+Y5oiQ5a2X56ym5Z6L77yBDQoNCiJQc3ljaG9sb2d5IiAgIyDkuIDkuKrlrZfnrKbkuLLvvIzmnKzotKjkuIrmmK/kuIDkuKrplb/luqbkuLox55qE5a2X56ym5ZCR6YePDQoiUHN5Y2hvbG9neSJbMV0gICMg5ZCR6YeP55qE56ysMeS4quWFg+e0oA0KIlBzeWNob2xvZ3kiWzBdICAjIOWQkemHj+eahOesrDDkuKrlhYPntKDvvIjmsqHmnInvvIkNCiJQc3ljaG9sb2d5IlsyXSAgIyDlkJHph4/nmoTnrKwy5Liq5YWD57Sg77yI57y65aSx77yJDQpuY2hhcigiUHN5Y2hvbG9neSIpICAjIOWtl+espuS4quaVsOS4ujEwDQoNCmEgPSBjKCJQc3ljaG9sb2d5IiwgIkVDTlUiLCAiU2hhbmdoYWkiLCAiQ2hpbmEiKQ0KYQ0KbGVuZ3RoKGEpDQpuY2hhcihhKSAgIyDlkJHph4/ljJborqHnrpcNCmNsYXNzKGEpDQpzdHIoYSkgICMgc3RydWN0dXJl77ya5pi+56S6UuWvueixoeeahOe7k+aehA0KDQphcy5mYWN0b3IoYSkNCmZhY3RvcihhLCBsZXZlbHM9YygiQ2hpbmEiLCAiU2hhbmdoYWkiLCAiRUNOVSIsICJQc3ljaG9sb2d5IikpDQpmYWN0b3IoDQogIGEsDQogIGxldmVscyA9IGMoIkNoaW5hIiwgIlNoYW5naGFpIiwgIkVDTlUiLCAiUHN5Y2hvbG9neSIpLA0KICBsYWJlbHMgPSBjKCJDTiIsICJTSCIsICJFQ05VIiwgIlBzeSIpDQopDQoNCiMjIGNjKCkg55So5rOV77ya5b+F6aG75piv6Iux5paH6YCX5Y+35oiW5o2i6KGM56ymDQpjYygi5b+D55CG5a2mLCDnpL7kvJrlraYsIOaVmeiCsuWtpiwg566h55CG5a2mLCDorqHnrpfmnLosIOS6uuW3peaZuuiDvSIpDQpjYygiDQrlv4PnkIblraYNCuekvuS8muWtpg0K5pWZ6IKy5a2mDQrnrqHnkIblraYNCuiuoeeul+acug0K5Lq65bel5pm66IO9DQoiKQ0KYGBgDQoNCiMjIyDvvIgz77yJ6YC76L6R5ZCR6YePDQoNCiMjIyMg44CQ5a6e6Le1M+OAkemAu+i+keWQkemHjyB7I+Wunui3tTPpgLvovpHlkJHph499DQoNCuWHveaVsOefpeivhueCuQ0KDQotICAgYCVpbiVg77ya5ZCR6YeP5YyW5Yik5pat5YmN5LiA5Liq5a+56LGh55qE5q+P5Liq5YWD57Sg5piv5ZCm5Zyo5ZCO5LiA5Liq5a+56LGh5LitDQotICAgYGlmZWxzZSgpYO+8muagueaNruadoeS7tuivreWPpe+8jOWQkemHj+WMlumAu+i+keWIpOaWrQ0KDQpgYGB7cn0NCmMoVFJVRSwgVFJVRSwgRkFMU0UsIFRSVUUsIEZBTFNFLCBGQUxTRSkNCjE6NSA+IDINCmMoMCwgNSwgMTAsIDE1KSAlaW4lIDE6MTANCmlmZWxzZSgxOjEwID49IDUsICJZZXMiLCAiTm8iKSAgIyBpZmVsc2UodGVzdCwgeWVzLCBubykNCmBgYA0KDQojIyMjIOOAkOefpeivhueCueOAkeadoeS7tuWIpOaWrei/kOeul+espuS4juWvueixoeexu+Wei+WIpOaWreWHveaVsCB7I+efpeivhueCueadoeS7tuWIpOaWrei/kOeul+espuS4juWvueixoeexu+Wei+WIpOaWreWHveaVsH0NCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojIyDmnaHku7bliKTmlq3ov5DnrpfnrKYNCmEgPT0gYg0KYSAhPSBiDQphID4gYg0KYSA8IGINCmEgPj0gYg0KYSA8PSBiDQphICVpbiUgYg0KDQojIyDlr7nosaHnsbvlnovliKTmlq3lh73mlbANCmlzLm5hKHgpDQppcy5udWxsKHgpDQppcy5udW1lcmljKHgpDQppcy5jaGFyYWN0ZXIoeCkNCmlzLmZhY3Rvcih4KQ0KIyDov5jmnInpnZ7luLjlpJrlhbbku5YgaXMueHh4KCkg57O75YiX5Ye95pWw77yBDQpgYGANCg0KIyMjIOWQkemHj+ebuOS6kui9rOaNog0KDQojIyMjIOOAkOWunui3tTTjgJHpgLvovpHlkJHph4/igJTmlbDlgLzlkJHph4/igJTlrZfnrKblkJHph4/nmoTnm7jkupLovazmjaIgeyPlrp7ot7U06YC76L6R5ZCR6YeP5pWw5YC85ZCR6YeP5a2X56ym5ZCR6YeP55qE55u45LqS6L2s5o2ifQ0KDQrlh73mlbDnn6Xor4bngrkNCg0KLSAgIGBhcy5sb2dpY2FsKClg77yaYDBg6L2s5o2i5Li6YEZBTFNFYO+8jOmdnmAwYOi9rOaNouS4umBUUlVFYA0KLSAgIGBhcy5jaGFyYWN0ZXIoKWDvvJrovazmjaLkuLrlrZfnrKblkJHph48NCi0gICBgYXMubnVtZXJpYygpYO+8mui9rOaNouS4uuaVsOWAvOWQkemHj++8iOaXoOazlei9rOaNoueahOiusOS4umBOQWDnvLrlpLHlgLzvvIkNCi0gICBgJT4lYO+8mueuoemBk+aTjeS9nOespuKtkO+4j++8iHRpZHl2ZXJzZeezu+WIl+WMheeahOmAmueUqOWHveaVsO+8iQ0KICAgIC0gICDlsIbliY3kuIDkuKrov5DooYznu5PmnpzkvKDpgJLnu5nlkI7kuIDkuKrlh73mlbDnmoTnrKzkuIDkuKrlj4LmlbDkvY3nva4NCiAgICAtICAg5aaC5p6c6ZyA6KaB5Lyg6YCS57uZ5ZCO5LiA5Liq5Ye95pWw55qE5YW25LuW5Y+C5pWw5L2N572u77yM6ZyA6KaB55So54K5YC5g5Y2g5L2NDQogICAgLSAgIOW/q+aNt+mUru+8mkN0cmwgKyBTaGlmdCArIE3vvIjms6jmhI/mo4Dmn6Xlhbbku5bova/ku7bnmoTlv6vmjbfplK7lhrLnqoHvvIkNCiAgICAtICAg5aaC5p6c5o+Q56S6YOayoeaciSIlPiUi6L+Z5Liq5Ye95pWwYO+8jOivt2BsaWJyYXJ5KGJydWNlUilg5oiWYGxpYnJhcnkodGlkeXZlcnNlKWANCg0KYGBge3J9DQphcy5sb2dpY2FsKDA6NSkNCmFzLmNoYXJhY3RlcigwOjUpDQphcy5udW1lcmljKGMoIi0xLjUiLCAiMCIsICIxLjUiKSkNCmFzLm51bWVyaWMoYygiMS4yIiwgIi0uMTIiLCAiMS4yLjMiKSkgICMgTkHooajnpLrnvLrlpLHlgLwNCg0KYSA9IGMoIlBzeWNob2xvZ3kiLCAiRUNOVSIsICJTaGFuZ2hhaSIsICJDaGluYSIpDQphcy5mYWN0b3IoYSkNCmEgJT4lIGFzLmZhY3RvcigpDQphICU+JSBhcy5mYWN0b3IoKSAlPiUgYXMubnVtZXJpYygpDQphICU+JSBhcy5mYWN0b3IoKSAlPiUgYXMubnVtZXJpYygpICU+JSBjKDIwMDA2MikNCmEgJT4lIGFzLmZhY3RvcigpICU+JSBhcy5udW1lcmljKCkgJT4lIGMoLiwgMjAwMDYyKQ0KYSAlPiUgYXMuZmFjdG9yKCkgJT4lIGFzLm51bWVyaWMoKSAlPiUgYygyMDAwNjIsIC4pDQpgYGANCg0KIyMjIOWQkemHj+WFg+e0oOaTjeS9nA0KDQojIyMjIOOAkOWunui3tTXjgJHlkJHph4/lhYPntKDlkb3lkI3kuI7mj5Dlj5YgeyPlrp7ot7U15ZCR6YeP5YWD57Sg5ZG95ZCN5LiO5o+Q5Y+WfQ0KDQrlh73mlbDnn6Xor4bngrkNCg0KLSAgIGBuYW1lcygpYO+8muWvueixoeWFg+e0oOWQjeensOaIluWRveWQjQ0KICAgIC0gICDnm7TmjqXkvb/nlKjvvJrov5Tlm57lr7nosaHlhYPntKDlkI3np7ANCiAgICAtICAg6LWL5YC85L2/55So77ya6LWL5LqI5a+56LGh5YWD57Sg5ZCN56ew77yI5bCG5Lya5pS55Y+Y5b2T5YmN5a+56LGh77yJDQotICAgYHhbLi4uXWDvvJpgW11g5pON5L2c56ym5Y+W5a+56LGh5YWD57Sg5YaF5a65DQogICAgLSAgIGB4W+mAu+i+kS/mlbDlgLzlkJHph49dYO+8muWPluWvueW6lOS9jee9ruWFg+e0oO+8iOiLpeS4uui0n+aVsO+8jOWImeWIoOmZpOWvueW6lOWFg+e0oO+8iQ0KICAgIC0gICBgeFvlrZfnrKblkJHph49dYO+8muWPluWvueW6lOWQjeensOWFg+e0oA0KLSAgIGByZXYoKWDvvJrlkJHph4/lj43lkJHmjpLliJcNCi0gICBgc29ydCgpYO+8muaMieeFp+aVsOWAvOWkp+Wwj+aIluWtl+avjeW6j+mHjeaWsOaOkuWIlw0KLSAgIGB1bmlxdWUoKWDvvJrlj5blkJHph4/kuK3nmoTmiYDmnInni6znibnlhYPntKDvvIjljrvph43lpI3lgLzvvIkNCi0gICBgdGFibGUoKWDvvJrlkJHph4/lhYPntKDpopHmrKHnu5/orqENCg0KYGBge3J9DQpMRVRURVJTICAjIDI25Liq6Iux5paH5a2X5q+NDQphID0gMTo1DQpuYW1lcyhhKSAgIyDnm7TmjqXnlKjms5XvvIjliJrliJrlh7rnlJ/vvIzov5jmsqHlkI3lrZfvvIkNCg0KbmFtZXMoYSkgPSBMRVRURVJTWzE6NV0gICMg6LWL5YC855So5rOV77yMYeW3suaUueWPmA0KYQ0KbmFtZXMoYSkgICMg546w5Zyo5LiK5aW95oi35Y+j77yM5pyJ5ZCN5a2X5LqG77yBDQphWyJFIl0NCmFbYygiQSIsICJDIiwgIkUiKV0NCg0KYVstMV0gICMg5Yig6Zmk5YWD57Sg77yM5L2G5LiN5pS55Y+YYQ0KYVstKDI6NCldICAjIOWIoOmZpOWFg+e0oO+8jOS9huS4jeaUueWPmGENCg0KeCA9IGMoMiwgMCwgMCwgMCwgNiwgMikNCnJldih4KQ0Kc29ydCh4KQ0KdW5pcXVlKHgpDQp4ICU+JSB1bmlxdWUoKSAlPiUgc29ydCgpICU+JSByZXYoKQ0KdGFibGUoeCkNCg0KeCAlaW4lIDE6NQ0KeFt4ICVpbiUgMTo1XQ0KeFt4ICVpbiUgMTo1XSA9IE5BDQp4DQpgYGANCg0KIyMjIyDjgJDlrp7ot7U244CR5ZCR6YeP5Luj5pWw6L+Q566XIHsj5a6e6Le1NuWQkemHj+S7o+aVsOi/kOeul30NCg0K5Ye95pWw55+l6K+G54K5DQoNCi0gICBgc3VtKClg44CBYG1lYW4oKWDvvJrlr7nkuIDkuKrlkJHph4/lr7nosaHmsYLlkozjgIHmsYLlubPlnYflgLwNCi0gICBgcGFzdGUoKWDvvJrmiorlkJHph4/lhYPntKDmi7zmjqXlnKjkuIDotbcNCiAgICAtICAgYHNlcGDlj4LmlbDvvJrlpJrkuKrovpPlhaXkuYvpl7TnmoTpu4/lkIjlrZfnrKYNCiAgICAtICAgYGNvbGxhc3BlYOWPguaVsO+8muWQkemHj+S4jeWQjOWFg+e0oOS5i+mXtOeahOm7j+WQiOWtl+espg0KDQpgYGB7cn0NCmEgPSAxOjUNCmEgKyAxICAgICMgMTo1ICsgMSDjgJDmgJ3ogIPvvJphICsgMToyIOeahOe7k+aenO+8n+OAkQ0KYSAqIDIgICAgIyAxOjUgKiAyDQphXjIgICAgICAjICgxOjUpXjINCmEgKiA1OjEgICMg5a+55bqU5L2N572u55u45LmY77yM6ZW/5bqm5b+F6aG755u4562JDQphICUlIDIgICAjIOWPluS9meaVsA0KDQpzdW0oMToxMDApICAjIOmrmOaWr+axguWSjA0KbWVhbigxOjEwMCkgICMg5rGC5bmz5Z2H5YC8DQoNCnBhc3RlKDEsIDIsIDMsIDQsIDUsIHNlcD0iICIpICAjIOWkmuS4qui+k+WFpeS5i+mXtA0KcGFzdGUoMTo1LCBjb2xsYXBzZT0iICIpICAjIOWQkemHj+S4jeWQjOWFg+e0oOS5i+mXtA0KcGFzdGUoMToxMCwgY29sbGFwc2U9IisiKSAgIyDmiZPljbDorqHnrpflhazlvI8NCmNhdChwYXN0ZSgxOjEwLCBjb2xsYXBzZT0iKyIpLCAiPSIsIHN1bSgxOjEwKSkNCg0KYWJzKGxvZyhzcXJ0KHBpKSkpDQpwaSB8PiBzcXJ0KCkgfD4gbG9nKCkgfD4gYWJzKCkgICAgICMg5paw54mIUuWGhee9rueuoemBk+aTjeS9nOespg0KcGkgJT4lIHNxcnQoKSAlPiUgbG9nKCkgJT4lIGFicygpICAjIHRpZHnns7vliJfnrqHpgZPmk43kvZznrKbvvIjlu7rorq7kvb/nlKjvvIkNCmBgYA0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0zNjI1NTg5NTA3LnBuZykNCg0KIyMg5YiX6KGo77yIbGlzdO+8iQ0KDQotICAg5Y2V57u044CB5aSa57G75Z6L5pWw5o2uDQoNCiMjIyMg44CQ5a6e6Le1N+OAkeWIl+ihqOaehOW7uuS4juaTjeS9nCB7I+Wunui3tTfliJfooajmnoTlu7rkuI7mk43kvZx9DQoNCuWHveaVsOefpeivhueCuQ0KDQotICAgYGxpc3QoKWDvvJrlu7rnq4vkuIDkuKrliJfooajlr7nosaHvvIzlj6/lrZjlgqjku7vmhI/nsbvlnovjgIHku7vmhI/nu5PmnoTnmoTlr7nosaENCiAgICAtICAgYCRg77ya5qC55o2u5YWD57Sg5ZCN56ew77yM5Y+W5YiX6KGo5Lit55qE5YWD57Sg5YaF5a6577yIVGFi6ZSu5by55Ye65omA5pyJ5YWD57Sg5ZCN56ew77yJDQogICAgLSAgIGBbWy4uLl1dYO+8muWPluWIl+ihqOS4reWvueW6lOS9jee9rueahOWFg+e0oOWGheWuue+8iOS4jeWQjOS6jmBbLi4uXWDvvIkNCi0gICBgdW5saXN0KClg77ya6Kej6Zmk5YiX6KGo57uT5p6E77yM6L+Y5Y6f5Li65ZCR6YePDQotICAgYGxhcHBseSgpYO+8muaJuemHj+W6lOeUqOWHveaVsO+8jOi/lOWbnuS4uuWIl+ihqA0KDQohW+i+k+WFpeWQjuaMiVRhYumUruW8ueWHuuaJgOacieWFg+e0oOWQjeensF0oaW1hZ2VzL2NsaXBib2FyZC0zMTMxNDgxMzA4LnBuZykNCg0KYGBge3J9DQpsID0gbGlzdChZZWFyPTIwMDA6MjAxNSwgTW9udGg9MToxMiwgRGF5PTE6MzEsIExldHRlcj1MRVRURVJTKQ0KbA0KbmFtZXMobCkNCmxlbmd0aChsKQ0KbCRZZWFyDQpsWzJdICAgICMg5Y+W56ysMuS4quWtkOmbhu+8jOi/lOWbnmxpc3TlrZDpm4YNCmxbWzJdXSAgIyDlj5bnrKwy5Liq5YWD57Sg77yM6L+U5Zue5YWD57Sg5pys6Lqr77yM55u45b2T5LqOIGwkTW9udGgNCg0KdW5saXN0KGwpICAjIOino+mZpOWIl+ihqOe7k+aehO+8jOW8uuihjOe7n+S4gOWvueixoeexu+Wei++8iOaVsOWAvCAtPiDlrZfnrKbvvIkNCg0KIyMgbGFwcGx5KOWQkemHj+aIluWIl+ihqCwg5Ye95pWwKQ0KbGFwcGx5KGwsIGxlbmd0aCkgICMg5q+P5Liq5YWD57Sg5om56YeP5bqU55SobGVuZ3RoKCnlh73mlbANCmxhcHBseShsLCBtZWFuKSAgICAjIOavj+S4quWFg+e0oOaJuemHj+W6lOeUqG1lYW4oKeWHveaVsA0KDQphID0gMTo1DQphXjINCmxhcHBseShhLCBmdW5jdGlvbih4KSB4XjIpICAjIHjmmK/lvaLlj4LvvIjlvaLlvI/lj4LmlbDvvIkNCmxhcHBseShhLCBmdW5jdGlvbih4KSB4XjIpICU+JSB1bmxpc3QoKQ0KYGBgDQoNCiMgUuWHveaVsOS9v+eUqOS4juWPguaVsOiuvue9rg0KDQojIyMjIOOAkOefpeivhueCueOAkVLlh73mlbDkvZPnmoTnu5PmnoQgeyPnn6Xor4bngrly5Ye95pWw5L2T55qE57uT5p6EfQ0KDQotICAgYGZ1bmN0aW9uKC4uLilg77ya5Ye95pWw5o6l5pS26L6T5YWl5Y+C5pWw77yM5YaF6YOo5pON5L2c5ZCO77yM6L+U5Zue6L6T5Ye657uT5p6cDQogICAgLSAgIGB7IH1g77ya5aSn5ous5Y+35YaF5a6a5LmJ5omA5pyJ5b2i5byP5Y+C5pWw77yI5b2i5Y+C77yJ55qE5pON5L2c6L+H56iLDQogICAgLSAgIOi/lOWbnue7k+aenOWPr+eUqOS4pOenjeaWueW8jw0KICAgICAgICAtICAgYHJldHVybiguLi4pYO+8muaYvuW8j+i/lOWbng0KICAgICAgICAtICAgYGludmlzaWJsZSguLi4pYO+8mumakOW8j+i/lOWbng0KDQpgYGB7cn0NCmZ1bmMgPSBmdW5jdGlvbihwYXJhbTEsIHBhcmFtMiwgLi4uKSB7DQogICMjIOWHveaVsOWGhemDqOaTjeS9nA0KICByZXN1bHRzID0gcGFyYW0xICsgcGFyYW0yDQogIGNhdChwYXJhbTEsICIrIiwgcGFyYW0yLCAiPSIsIHJlc3VsdHMsICJcbiIpDQogIA0KICAjIyDlh73mlbDov5Tlm57nu5PmnpwNCiAgcmV0dXJuKHJlc3VsdHMpDQogICMg5Lmf5Y+v5Lul6ZqQ5byP6L+U5Zue77yaDQogICMgaW52aXNpYmxlKHJlc3VsdHMpDQp9DQpmdW5jKDE2LCA5KQ0KYGBgDQoNCiMjIOW4uOeUqFLlh73mlbDkvb/nlKgNCg0KIyMjIyDjgJDlrp7ot7U444CR5paH5Lu25aS56YeM5pyJ5LuA5LmI77yfIHsj5a6e6Le1OOaWh+S7tuWkuemHjOacieS7gOS5iH0NCg0KYGBge3IsIGV2YWw9RkFMU0V9DQojIyDor7foh6rlt7Hov5DooYwNCmxpYnJhcnkoYnJ1Y2VSKQ0Kc2V0LndkKCkgICMg6K6+572u5bel5L2c6Lev5b6E5Li65b2T5YmN5omT5byA5paH5Lu255qE6Lev5b6EDQoNCmxpc3QuZmlsZXMoKSAgICAgICAgICAjIOWIl+WHuuW9k+WJjei3r+W+hOeahOaJgOacieaWh+S7ti/mlofku7blpLkNCmxpc3QuZmlsZXMoIi4uLyIpICAgICAjIOWIl+WHuuS4iuS4gOe6p+i3r+W+hOeahOaJgOacieaWh+S7ti/mlofku7blpLkNCmxpc3QuZmlsZXMoIi4uLy4uLyIpICAjIOWIl+WHuuS4iuS4pOe6p+i3r+W+hOeahOaJgOacieaWh+S7ti/mlofku7blpLkNCmBgYA0KDQojIyMjIOOAkOWunui3tTnjgJHku4rlpKnmmZrkuIrlkIPku4DkuYjvvJ8geyPlrp7ot7U55LuK5aSp5pma5LiK5ZCD5LuA5LmIfQ0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMjIOivt+iHquW3sei/kOihjA0KY2hvaWNlID0gYygi5rKz6KW/6aOf5aCCIiwgIuays+S4nOmjn+WggiIsICLnjq/nkIPmuK8iLCAi54K55aSW5Y2WIikNCnNhbXBsZShjaG9pY2UsIHNpemU9MSkgICMg6ZqP5py65oq95qC3DQoNCiMg6K6+572u6ZqP5py656eN5a2Q77yM5L2/57uT5p6c5Y+v6YeN5aSNDQpzZXQuc2VlZCgxMjMpICAjIOmaj+acuuenjeWtkOWPr+S7peaYr+S7u+aEj+aVtOaVsO+8jOayoeacieeJueWumuWQq+S5iQ0Kc2FtcGxlKGNob2ljZSwgc2l6ZT0xKQ0KYGBgDQoNCiMjIyMg44CQ5a6e6Le1MTDjgJHmraPmgIHliIbluIPpmo/mnLrmlbDnmoTlubPlnYflgLzlkozmoIflh4blt67vvJ8geyPlrp7ot7UxMOato+aAgeWIhuW4g+maj+acuuaVsOeahOW5s+Wdh+WAvOWSjOagh+WHhuW3rn0NCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpICAjIOiuvue9rumaj+acuuenjeWtkO+8jOS9v+e7k+aenOWPr+mHjeWkjQ0KeCA9IHJub3JtKDMwKSAgIyDnlJ/miJAzMOS4qumaj+acuuaVsO+8iOato+aAgeWIhuW4g++8iQ0KDQptZWFuKHgpICAjIOaxguW5s+Wdh+WAvA0Kc2QoeCkgICAgIyDmsYLmoIflh4blt64NCmhpc3QoeCkgICMg55S755u05pa55Zu+DQoNCnggPiBtZWFuKHgpICsgc2QoeCkgICAgICAgICAjIOi/lOWbnlRSVUUvRkFMU0XlkJHph48NCnhbeCA+IG1lYW4oeCkgKyBzZCh4KV0gICAgICAjIOi/lOWbnijlj5blh7op56ym5ZCI5p2h5Lu255qE5pWw5YC8DQp3aGljaCh4ID4gbWVhbih4KSArIHNkKHgpKSAgIyDov5Tlm57nrKblkIjmnaHku7bmlbDlgLzmiYDlnKjkvY3nva4NCg0Kc2V0LnNlZWQoMTIzKSAgIyDorr7nva7pmo/mnLrnp43lrZDvvIzkvb/nu5Pmnpzlj6/ph43lpI0NCnggPSBybm9ybSgxMDAwKSAgIyDnlJ/miJAxMDAw5Liq6ZqP5py65pWw77yI5q2j5oCB5YiG5biD77yJDQoNCm1lYW4oeCkgICMg5rGC5bmz5Z2H5YC8DQpzZCh4KSAgICAjIOaxguagh+WHhuW3rg0KaGlzdCh4KSAgIyDnlLvnm7Tmlrnlm74NCmBgYA0KDQojIyMjIOOAkOWunui3tTEx44CR4oCcSGVsbG8gV29ybGTigJ3nmoTlh6Dnp43miZPljbDmlrnms5XvvJ8geyPlrp7ot7UxMWhlbGxvLXdvcmxk55qE5Yeg56eN5omT5Y2w5pa55rOVfQ0KDQrlh73mlbDnn6Xor4bngrkNCg0KLSAgIGBjbGlg5piv5ZG95Luk6KGM5Lqk5LqS55qE5LiT5LiaUuWMhe+8jOezu+e7n+aAp+aPkOS+m+S6huWQhOenjei+k+WHuuaWh+acrOiuvuWumuWHveaVsA0KICAgIC0gICDor6bop4HvvJo8aHR0cHM6Ly9jbGkuci1saWIub3JnLz4NCg0KYGBge3J9DQpwcmludCgiSGVsbG8gV29ybGQhIikNCmNhdCgiSGVsbG8gV29ybGQhIikNCmNhdCgiSGVsbG8iLCAiV29ybGQhIikNCmNsaTo6Y2xpX3RleHQoIkhlbGxvIFdvcmxkISIpDQpjbGk6OmNsaV9oMSgiSGVsbG8gV29ybGQhIikNCmNsaTo6Y2xpX2gyKCJIZWxsbyBXb3JsZCEiKQ0KY2xpOjpjbGlfaDMoIkhlbGxvIFdvcmxkISIpDQpjbGk6OmNsaV9hbGVydCgiSGVsbG8gV29ybGQhIikNCmNsaTo6Y2xpX2FsZXJ0X3N1Y2Nlc3MoIkhlbGxvIFdvcmxkISIpDQpjbGk6OmNsaV9hbGVydF9pbmZvKCJIZWxsbyBXb3JsZCEiKQ0KY2xpOjpjbGlfYWxlcnRfd2FybmluZygiSGVsbG8gV29ybGQhIikNCmNsaTo6Y2xpX2FsZXJ0X2RhbmdlcigiSGVsbG8gV29ybGQhIikNCmBgYA0KDQojIFLpgLvovpHmjqfliLbor63lj6UNCg0KIyMgaWbliKTmlq3or63lj6UNCg0KYGBge3IsIGV2YWw9RkFMU0V9DQppZiAoY29uZGl0aW9uKSB7DQogICMgRG8gc29tZXRoaW5nDQp9IGVsc2UgaWYgew0KICAjIERvIHNvbWV0aGluZw0KfSBlbHNlIHsNCiAgIyBEbyBzdGguIGVsc2UNCn0NCmBgYA0KDQpgYGB7cn0NCnggPSAzDQppZiAoeCAlaW4lIDE6NSkgew0KICBjYXQoIlllcyIpDQp9IGVsc2Ugew0KICBjYXQoIk5vIikNCn0NCmBgYA0KDQojIyB3aGlsZeW+queOr+ivreWPpQ0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCndoaWxlIChjb25kaXRpb24pIHsNCiAgIyBEbyBzb21ldGhpbmcNCn0NCmBgYA0KDQpgYGB7cn0NCnggPSAxDQp3aGlsZSAoeCA8IDUpIHsNCiAgY2F0KHgsICJpcyBzbWFsbGVyIHRoYW4gNVxuIikNCiAgeCA9IHggKyAxDQp9DQpgYGANCg0KIyMgZm9y5b6q546v6K+t5Y+lDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KZm9yICh2YXIgaW4gc2VxKSB7DQogICMgRG8gc29tZXRoaW5nDQp9DQpgYGANCg0KYGBge3J9DQpmb3IgKHggaW4gMTo1KSB7DQogIHkgPSB4ICogMg0KICBwcmludCh5KQ0KfQ0KYGBgDQoNCiMg44CQ5L2c5LiaM+OAkee8lueoi+ino+WGs+aKm+ehrOW4gemXrumimA0KDQrpl67popjmg4XlooPvvJrkuIDmnprnoazluIHvvIzov57nu63mipvkuoZO5qyh6YO95piv5q2j6Z2i5pyd5LiK77yM5YaN5oqb5LiA5qyh5Y+N6Z2i5pyd5LiK55qE5qaC546H5piv5aSa5bCR77yfDQoNCi0gICDmr5TlpoLvvIzkuIDmnprnoazluIHvvIzov57nu63mipvkuoYz5qyh6YO95piv5q2j6Z2i5pyd5LiK77yM5YaN5oqb5LiA5qyh5Y+N6Z2i5pyd5LiK55qE5qaC546H5piv5aSa5bCR77yfDQotICAg6L+e57utMTDmrKHlkaLvvJ/ov57nu60xMDAw5qyh5ZGi77yf6L+e57utMeS6v+asoeWRou+8n+KApuKApg0KLSAgIOaAnei3r+aPkOekuu+8mueUqOWBh+iuvuajgOmqjOeahOaAneaDs++8jOWIpOaWrei/meaemuehrOW4geaYr+S4jeaYr+aZrumAmuehrOW4ge+8iEgw77ya56Gs5biB5piv5pmu6YCa55qE77yJDQoNCuS9nOS4muimgeaxgu+8mg0KDQotICAg57yW5YaZ5LiA5q61Uueoi+W6j++8jOiuoeeul+W5tuWIpOaWreiHs+Wwkei/nue7reWkmuWwkeasoeato+mdouacneS4iu+8jOWPr+S7peiupOWumuehrOW4geS4jeWGjeaYr+aZrumAmuehrOW4ge+8iOaLkue7nembtuWBh+iuvu+8iQ0KLSAgIOWwveWPr+iDveeUqFIgTWFya2Rvd27lrozmiJANCg0K5bmz5Y+w5o+Q5Lqk77yaDQoNCi0gICBS5Luj56CB5ZKM6L+Q6KGM57uT5p6c5oiq5Zu+DQo=