Vector 的分类

  1. atomic vector, matrix, array 都要求单一数据类型 (homogeneous data type),根基是 atomic vector
    1. atomic vector 添加维度属性可生成 matrix, array
    2. atomic vector 添加 class 属性可生成 factor, date 和 date-time
  2. list, data.frame 可以装载不同数据类型 (heterogeneous data types),根基是 list
    1. 列表提高了存储的灵活性,但降低了存储和运算效率
    2. 数据框是一种特殊的列表
  3. atomic vector 和 list 可统称为 vector

data types of atomic vector

logical

TRUE/TFALSE/F

一些被设定接收逻辑输入的函数也可以接收非逻辑向量,例如数值向量。此时,非逻辑向量会被强制转换成逻辑向量:非零数值会被强制转换成 TRUE,只有 0 会被强制转换成 FALSE;字符串不能被强制转换成逻辑值,会报错。

numeric

统称数值型

  • 整数,integer,形如 2L
  • 双精度浮点数,double
    • 三种特殊情况:Inf, -Inf, NaN.
    • 只要表达式中有正负无穷的项出现,结果就很可能为 Inf, -Inf, NaN
exp(1000) # R 中数字的上限为 1.8*10^38,超过这个规模就会出现 Inf 
#> [1] Inf
-10 / 0 # -Inf 负无穷
#> [1] -Inf
exp(1000) / exp(990) # NaN, not a number
#> [1] NaN

charactor

字符串

complex

复数,形如 1+2i

空值 NULL 和缺失值 NA

NULL,零长向量,一般表示参数未被赋值,以及函数没有明确返回值时的返回。所属类、数据类型均为 “NULL”

NA,长度为 1 的逻辑向量,表示数据(元素)的缺失值。所属类、数据类型均为 “logical”

判断 NULL 和 NA 不能用 ==,只能用 is.na()is.null()

a <- NULL
a # 注意控制台的输出没有 [1],长度连1都没有
#> NULL
length(a)
#> [1] 0
class(a)
#> [1] "NULL"
typeof(a)
#> [1] "NULL"
a == NULL # 返回长度为0的向量,既不是 TRUE 也不是 FALSE
#> logical(0)
is.na(a)
#> logical(0)
b <- NA
b
#> [1] NA
length(b)
#> [1] 1
class(b)
#> [1] "logical"
typeof(b)
#> [1] "logical"
b == NA # NA 进行任何运算都返回 NA
#> [1] NA
is.null(b)
#> [1] FALSE

查看 data type

typeof()

  1. vector、matrix 返回其中元素的 data type
  2. list、data.frame 返回 “list”

combine data

组合(combine, 一般使用c(..., use.names = TRUE))不同数据类型的向量或列表时,会强制转换为兼容所有元素的、更灵活的数据类型

一般顺序为:logical<integer<numeric<complex<character<list

a <- 1:2
b <- letters[1:2]
c(a, b)
#> [1] "1" "2" "a" "b"
l4 <- list(list(1, 2), c(3, 4))
l5 <- c(list(1, 2), c(3, 4))
str(l4)
#> List of 2
#>  $ :List of 2
#>   ..$ : num 1
#>   ..$ : num 2
#>  $ : num [1:2] 3 4
str(l5)
#> List of 4
#>  $ : num 1
#>  $ : num 2
#>  $ : num 3
#>  $ : num 4

attributes

属性即作为 metadata 的键值对,附加到对象上

常用属性

  1. 长度,,length()
    1. vector、matrix 返回其中元素的个数
    2. list 返回键值对个数
    3. 数据框返回列数,因为每列(列名:值向量)是 list 的一个键值对
  2. 矩阵、数组和数据框的维度,dim()
    1. dim(vector) 不是1, 而是 NULL
  3. names,给每个元素一个 name 的数值向量
    1. names()[<-]/setNames() 获取/设定 names 属性,设置为 NULL 可删除 names 属性
    2. unname() 去掉 names 属性(该语句也会生成一个新变量)
    3. 从源代码可知,setNames()unname()是为了配合管道而设计的语法糖,内部调用的还是 names()<-

处理函数

  • attr(x, 'a')[<-] get and modify 属性
  • attributes(x) get 全部属性
  • structure(.Data = x, attr1 = values1, attr2 = values2, ...) 整体设置全部属性。但要注意:该语句会生成一个新变量。
attr(a, "roman_index") <- c("I", "II")
a
#> [1] 1 2
#> attr(,"roman_index")
#> [1] "I"  "II"
attr(a, "roman_index") <- NULL
a
#> [1] 1 2
a <- structure(.Data = a, roman_index = c("I", "II"))
a
#> [1] 1 2
#> attr(,"roman_index")
#> [1] "I"  "II"
names(a) <- b
attributes(a)
#> $roman_index
#> [1] "I"  "II"
#> 
#> $names
#> [1] "a" "b"
str(a)
#>  Named int [1:2] 1 2
#>  - attr(*, "roman_index")= chr [1:2] "I" "II"
#>  - attr(*, "names")= chr [1:2] "a" "b"
a
#> a b 
#> 1 2 
#> attr(,"roman_index")
#> [1] "I"  "II"
unname(a)
#> [1] 1 2
#> attr(,"roman_index")
#> [1] "I"  "II"
a
#> a b 
#> 1 2 
#> attr(,"roman_index")
#> [1] "I"  "II"
a <- unname(a)
a
#> [1] 1 2
#> attr(,"roman_index")
#> [1] "I"  "II"

class 属性与 S3 对象

添加 class 属性后,对象将被转换为 S3 对象,传递给泛型函数时将具有独特的行为

Factor

x <- factor(c('a', 'b', 'b', 'a'))
x
#> [1] a b b a
#> Levels: a b
typeof(x)
#> [1] "integer"
attributes(x)
#> $levels
#> [1] "a" "b"
#> 
#> $class
#> [1] "factor"
table(x)
#> x
#> a b 
#> 2 2

Date

class 属性:Date

today <- Sys.Date()
typeof(today)
#> [1] "double"
attributes(today)
#> $class
#> [1] "Date"
unclass(today) # 从1970年1月1日算起
#> [1] 19114

Date-time

class 属性:POSIXct

now_ct <- as.POSIXct("2018-08-01 22:00", tz = "UTC")
now_ct
#> [1] "2018-08-01 22:00:00 UTC"
typeof(now_ct)
#> [1] "double"
attributes(now_ct)
#> $class
#> [1] "POSIXct" "POSIXt" 
#> 
#> $tzone
#> [1] "UTC"
unclass(now_ct) # 从1970年1月1日算起的秒数
#> [1] 1533160800
#> attr(,"tzone")
#> [1] "UTC"
structure(now_ct, tzone = "Asia/Tokyo")
#> [1] "2018-08-02 07:00:00 JST"
structure(now_ct, tzone = "America/New_York")
#> [1] "2018-08-01 18:00:00 EDT"
structure(now_ct, tzone = "Australia/Lord_Howe")
#> [1] "2018-08-02 08:30:00 +1030"
structure(now_ct, tzone = "Europe/Paris")
#> [1] "2018-08-02 CEST"

Duration

class 属性:difftime

one_week_1 <- as.difftime(1, units = "weeks")
one_week_1
#> Time difference of 1 weeks
typeof(one_week_1)
#> [1] "double"
attributes(one_week_1)
#> $class
#> [1] "difftime"
#> 
#> $units
#> [1] "weeks"
one_week_2 <- as.difftime(7, units = "days")
one_week_2
#> Time difference of 7 days
typeof(one_week_2)
#> [1] "double"
attributes(one_week_2)
#> $class
#> [1] "difftime"
#> 
#> $units
#> [1] "days"

Atomic Vector

注意

  1. R中无标量,标量被默认为长度为1的向量,所以在控制台显示一个数时,前面仍会有表示向量维度的 [1]
  2. R中的向量默认为列向量,不存在行向量。对列向量转置,得到的是一个\(1\times n\)的矩阵
  3. R中的基础运算都是向量化的

Create

vector()

  • vector(mode = 'double', length = N),生成长度为 N 的空向量
    • 参数 mode 可以为 “integer”/“double”/“numeric”/“logical”/“character” 等,为 “list” 则生成列表
    • 每项的值为 0/FALSE/""/NULL(生成列表时)
  • 对于特定数据类型的向量,可以简写为:numeric(N), logical(N), character(N)

全 1 向量的写法为vector('numeric', N) + 1numeric(N) + 1,比 rep(1, N) 的可读性更好

y <- numeric(6) + 1
y
#> [1] 1 1 1 1 1 1
z <- logical(7)
z
#> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
w <- character(2)
w
#> [1] "" ""
w <- c(w, "a")
w
#> [1] ""  ""  "a"

c()

组合向量(将多个单元素向量和多元素向量连接起来构成一个向量),其他语言一般用[]

c() 中即使嵌套,也会被自动展开为 atomic vector

c(1, 2, c(3, 4, 5:7))
#> [1] 1 2 3 4 5 6 7

create 向量时可以命名

x <- c(a = 1, b = 2, c = 3)
x
#> a b c 
#> 1 2 3
str(x) # 可见属性 names 保存着元素的名称
#>  Named num [1:3] 1 2 3
#>  - attr(*, "names")= chr [1:3] "a" "b" "c"

m:n

连续整数向量,类型为 integer vector

typeof(1:3)
#> [1] "integer"

seq*()

  • seq(from, to, by=1, length.out),等差数列,给出三个参数即可
  • seq_len(n),等价于1:n
  • seq_along(x),一般比 1:length(x) 更安全
    • 若 x 的长度为0,seq_along(x)会返回integer(0),而非c(1, 0)
seq_along(LETTERS)
#>  [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

rep(x, ...)

… 中的参数包括:

  1. times, 重复对象 x 整体的次数,一般为标量
    1. 该参数也可以是与 x 长度一致的向量,表示对应元素重复对应次数
  2. each, 对 x 中每个元素重复的次数
  3. length.out, 重复若干次后结果的长度
rep(1:3, times = 2)
#> [1] 1 2 3 1 2 3
rep(1:3, times = c(2, 4, 1))
#> [1] 1 1 2 2 2 2 3
rep(1:3, each = 2)
#> [1] 1 1 2 2 3 3
rep(1:3, length.out = 9)
#> [1] 1 2 3 1 2 3 1 2 3
rep(1:3, length.out = 10)
#>  [1] 1 2 3 1 2 3 1 2 3 1

Type

  • class()typeof() 查看类型
  • is.*() 判断类型
  • 可以使用 is.logical(), is.integer(), is.double(), is.character()
  • 尽量不要使用 is.vector(), is.atomic(), is.numeric()
  • as.*() 转换类型
  • 尽量不要使用as.vector()
class(1:5)
#> [1] "integer"
class(c(T, F, T))
#> [1] "logical"
is.character(c("h", "e", "l", "l", "o"))
#> [1] TRUE
as.character(1:5)
#> [1] "1" "2" "3" "4" "5"
strings <- c("1", "2", "3")
try(strings + 10)
#> Error in strings + 10 : non-numeric argument to binary operator
try(as.numeric(strings) + 10)
#> [1] 11 12 13

Size

length()

length(w)
#> [1] 3

Label

names()[<-] 提取/赋值

赋值为 NULL 时移除 names 属性,或通过 x <- unname(x)

Subset

选择器[]和提取器[[]]

[]为子集选择器,返回的是原数据的一个切片,不改变数据结构

[[]]为元素提取器,返回的是原数据一个长度为1的切片中装载的内容

对向量操作,[][[]]的差别很小;但对更复杂的数据类型,二者的差别会很明显:

str(iris) # 数据框
#> 'data.frame':    150 obs. of  5 variables:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
#>  $ Sepal.Width : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
#>  $ Petal.Length: num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
#>  $ Petal.Width : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
#>  $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
str(iris[1]) # 只含一个变量的数据框
#> 'data.frame':    150 obs. of  1 variable:
#>  $ Sepal.Length: num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
str(iris[[1]]) # 向量
#>  num [1:150] 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...

[]选取的三种方式

  • 位置 index,x[4]; x[-2:-4]; x[c(1, 3)]
    • 其中正数表示选择,复数表示剔除,[]中不能同时有正数和负数
    • index 为 0 将返回长度为 0 的向量
    • index 为空表示保留这一维度的所有元素
  • logical vector, 选出相应位置为 TRUE 的元素
    • 通过 condition 获得逻辑向量,x[x == 10]; x[x %in% c(1, 2, 5)]
  • names 属性,x["apple"]
x <- c(1, 2, 3, 4, 5, 6, 7)
names(x) <- letters[1:7]

# index
x[3:5] # x向量中的第3-5个元素
#> c d e 
#> 3 4 5
x[c(1, 4)] <- c(100, 400) # 第1个和第4个元素
x
#>   a   b   c   d   e   f   g 
#> 100   2   3 400   5   6   7
x[6:9] # 超出index时返回 NA
#>    f    g <NA> <NA> 
#>    6    7   NA   NA
x
#>   a   b   c   d   e   f   g 
#> 100   2   3 400   5   6   7
x[10] <- 10 # 对一个并不存在的元素重新赋值,将自动用 NA 填充未被指定的位置
x
#>   a   b   c   d   e   f   g             
#> 100   2   3 400   5   6   7  NA  NA  10
x[-2] # 除了第二个以外的元素
#>   a   c   d   e   f   g             
#> 100   3 400   5   6   7  NA  NA  10
x[-2:-4] # 等价于 x[c(-2, -3, -4)],即去掉第2、3、4个元素
#>   a   e   f   g             
#> 100   5   6   7  NA  NA  10
# condition
x[x %in% c(1, 2, 5)]
#> b e 
#> 2 5
# names
x["e"]
#> e 
#> 5
x[c("e", "j")] # 访问不存在的name,返回 NA
#>    e <NA> 
#>    5   NA

Application: lookup table

x <- c("m", "f", "u", "f", "f", "m", "m")
lookup <- c(m = "Male", f = "Female", u = NA) # 键值对
lookup[x] %>% unname()
#> [1] "Male"   "Female" NA       "Female" "Female" "Male"   "Male"

[[]]忽略 names

[[]] 只能提取一个元素,其中也不能出现负整数或访问不存在的元素(下标越界)

x <- c(a = 1, b = 2, c = 3)
x["a"]
#> a 
#> 1
x[["a"]]
#> [1] 1
try(x[[-1]])
#> Error in x[[-1]] : invalid negative subscript in get1index <real>
try(x[[4]])
#> Error in x[[4]] : subscript out of bounds
try(x[["d"]])
#> Error in x[["d"]] : subscript out of bounds

下标越界

返回 NA

Index

which*

  • which(逻辑表达式)
  • which.min()
  • which.max()
x
#> a b c 
#> 1 2 3
which.max(x)
#> c 
#> 3
which.min(x)
#> a 
#> 1
which(x == 2)
#> b 
#> 2
which(x > 4)
#> named integer(0)

*match

  • match(vector1, vector2)返回第一个向量中各元素在第二个向量中的位置序列,每一个都从头开始匹配,只要匹配到就结束。

  • pmatch(vector1, vector2),若第一个向量中某元素在第二个向量中匹配上了,则认为第二个向量中相应元素已被占用,再匹配第一个向量中的下一个元素时,跳过第二个向量中曾被匹配过的元素。

match(rep(1, 3), rep(1, 5))
#> [1] 1 1 1
pmatch(rep(1, 3), rep(1, 5))
#> [1] 1 2 3

Operation

R 对向量的操作都要在原向量的拷贝上进行,不改变原向量(只有直接赋值能改变原向量)。好处是安全,坏处是慢。

增删改

  • append() 返回插入元素后的拷贝. 注意,R 中一般不直接修改原对象,所以如果要保留这个操作,一定要记得赋值给原变量。
  • x <- x[-index] 删除选集
  • replace() 批量替换
x <- c(1, 2, 3, 4, 5, 7)
x <- replace(x, c(3, 4), c(5, 5)) # 替换:将x向量中第3个和第4个元素替换为5
x
#> [1] 1 2 5 5 5 7
append(x, 100, after = 2)
#> [1]   1   2 100   5   5   5   7
x
#> [1] 1 2 5 5 5 7
a <- c(0, 0, 1, 2, 0, 3) # 删除零值项
b <- which(a == 0)
b
#> [1] 1 2 5
a <- a[-b]
a
#> [1] 1 2 3

Sort

  • rank(x)返回x中各元素相对大小的排序(默认升序)
  • order(x)返回按照递增/递减的次序重排列x后,新序列各元素在原序列x中的位置
  • sort(x)返回排序后的向量,对于数字默认升序,对于字符串默认字典顺序
  • rev(x)逆序排列
x
#> [1] 1 2 5 5 5 7
rank(x)
#> [1] 1 2 4 4 4 6
sort(x) # 默认升序排序
#> [1] 1 2 5 5 5 7
x
#> [1] 1 2 5 5 5 7
rev(x) # 逆序
#> [1] 7 5 5 5 2 1
x
#> [1] 1 2 5 5 5 7

去重、偏移、差分

  • unique() 去重
  • lead(x, n=1), lag(x, n=1) 偏移,返回一个序列的领先序列和滞后序列
  • diff(x, lag=1) 差分,lag 用以指定滞后几项。默认的 lag 值为 1
unique(x)
#> [1] 1 2 5 7
x
#> [1] 1 2 5 5 5 7
lead(x, 2)
#> [1]  5  5  5  7 NA NA
x
#> [1] 1 2 5 5 5 7
diff(x)
#> [1] 1 3 0 0 2
x
#> [1] 1 2 5 5 5 7

统计条件频数

sum(vector中元素要符合的条件)

# 1 的个数
vector <- c(1, -1, 2, -2, 3, 4, 0, 2, 3, 4, 1, 1)
sum(vector == 1)
#> [1] 3

交叉循环遍历

expand.grid(..., stringsAsFactors = TRUE),…参数为若干个向量,比如数值向量 x 和 y,效果为对二元数值对 (x, y) 所有可能取值的遍历(返回数据框)

若 … 为字符串向量,最好将 stringsAsFactors 参数设置为 TRUE

x <- 1:3
y <- letters[1:3]
z <- 1:3
expand.grid(x, y)
#>   Var1 Var2
#> 1    1    a
#> 2    2    a
#> 3    3    a
#> 4    1    b
#> 5    2    b
#> 6    3    b
#> 7    1    c
#> 8    2    c
#> 9    3    c
expand.grid(x, z)
#>   Var1 Var2
#> 1    1    1
#> 2    2    1
#> 3    3    1
#> 4    1    2
#> 5    2    2
#> 6    3    2
#> 7    1    3
#> 8    2    3
#> 9    3    3

算数运算

向量化运算:对应元素分别运算,较短的向量自动循环直至长度与较长向量匹配

向量元素的 names 不参与运算,但运算符左侧向量的 names 会保留在结果中,右侧向量的 names 被忽略

## 四则运算(+-*/)、幂(^)、取整(%/%)、取余(%%)
1 / (1:6) # 自动将标量(长度为 1 的向量)扩展为长度为 6 的向量后再运算
#> [1] 1.0000000 0.5000000 0.3333333 0.2500000 0.2000000 0.1666667
1 / matrix(1:4, nrow = 2) # 自动将标量扩展为对应维度的矩阵后再运算
#>      [,1]      [,2]
#> [1,]  1.0 0.3333333
#> [2,]  0.5 0.2500000
x <- 1:5
y <- 2:6
x * y # 哈达马积
#> [1]  2  6 12 20 30
## 向量内积(以下写法等价)
1 * 2 + 2 * 3 + 3 * 4 + 4 * 5 + 5 * 6
#> [1] 70
sum(x * y)
#> [1] 70
t(x) %*% y # 利用矩阵乘法,注意 R 中向量默认为列向量
#>      [,1]
#> [1,]   70
crossprod(x, y) # 利用矩阵乘法,得到一个 1*1 矩阵
#>      [,1]
#> [1,]   70
## 向量外积(以下写法等价)
x %*% t(y)
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    2    3    4    5    6
#> [2,]    4    6    8   10   12
#> [3,]    6    9   12   15   18
#> [4,]    8   12   16   20   24
#> [5,]   10   15   20   25   30
x %o% y
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    2    3    4    5    6
#> [2,]    4    6    8   10   12
#> [3,]    6    9   12   15   18
#> [4,]    8   12   16   20   24
#> [5,]   10   15   20   25   30
kronecker(x, t(y)) # 克罗内克积
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    2    3    4    5    6
#> [2,]    4    6    8   10   12
#> [3,]    6    9   12   15   18
#> [4,]    8   12   16   20   24
#> [5,]   10   15   20   25   30
outer(x, y)
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    2    3    4    5    6
#> [2,]    4    6    8   10   12
#> [3,]    6    9   12   15   18
#> [4,]    8   12   16   20   24
#> [5,]   10   15   20   25   30
tcrossprod(x, y)
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    2    3    4    5    6
#> [2,]    4    6    8   10   12
#> [3,]    6    9   12   15   18
#> [4,]    8   12   16   20   24
#> [5,]   10   15   20   25   30

逻辑运算

x <- 1:6
x < 4
#> [1]  TRUE  TRUE  TRUE FALSE FALSE FALSE
all(x > 5) # 一个向量的所有元素是否满足某条件
#> [1] FALSE
any(x > 5) # 一个向量的部分元素是否满足某条件
#> [1] TRUE
matrix(x, 2, byrow = T) < 4
#>       [,1]  [,2]  [,3]
#> [1,]  TRUE  TRUE  TRUE
#> [2,] FALSE FALSE FALSE

集合运算

intersect(c(1, 2, 3, 3, 12, 4, 123, 12), c(1, 2, 3)) # 交
#> [1] 1 2 3
union(c("狗熊会", "聚数据英才"), c("狗熊会", "助产业振兴")) # 并
#> [1] "狗熊会"     "聚数据英才" "助产业振兴"
setdiff(10:2, 5:3) # 差
#> [1] 10  9  8  7  6  2

Matrix

Create

向量转化为矩阵

  1. matrix(vector, nrow, ncol, byrow = FALSE, dimnames = NULL),默认按列填充矩阵,byrow 设为 TRUE 可按行

  2. 为向量添加维度属性 dim(vector) <- c(nrow,ncol)

## 生成 3*3 的全 1 矩阵

# 第一种
rep(1, 9) %>% matrix(nrow = 3)
#>      [,1] [,2] [,3]
#> [1,]    1    1    1
#> [2,]    1    1    1
#> [3,]    1    1    1
# 第二种
x <- rep(1, 9)
dim(x) <- c(3, 3)
x # 这种方式改变了变量 x 的值,不是很推荐,还是第一种更好
#>      [,1] [,2] [,3]
#> [1,]    1    1    1
#> [2,]    1    1    1
#> [3,]    1    1    1

diag()

  1. diag(x = 1, nrow, ncol, names = TRUE)
    1. x 为标量或向量时,diag()表示根据对角元 create 矩阵
    2. x 为矩阵时,diag()为提取对角元,返回向量
    3. x 为标量且只有这一个参数时,返回 \(n\) 阶单位阵 \(I_n\)
  2. diag(x) <- value, value 可以是标量或向量
diag(4) # n 阶单位阵
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    0    0    0
#> [2,]    0    1    0    0
#> [3,]    0    0    1    0
#> [4,]    0    0    0    1
diag(0, 4) # 全 0 四阶方阵
#>      [,1] [,2] [,3] [,4]
#> [1,]    0    0    0    0
#> [2,]    0    0    0    0
#> [3,]    0    0    0    0
#> [4,]    0    0    0    0
diag(10, 3, 4) # nrow 和 ncol 可以不相等
#>      [,1] [,2] [,3] [,4]
#> [1,]   10    0    0    0
#> [2,]    0   10    0    0
#> [3,]    0    0   10    0
diag(0, 3, 4) # 任意 size 的全 0 矩阵
#>      [,1] [,2] [,3] [,4]
#> [1,]    0    0    0    0
#> [2,]    0    0    0    0
#> [3,]    0    0    0    0
diag(0, 3, 4) + 1 # 任意 size 的全 1 矩阵
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    1    1    1
#> [2,]    1    1    1    1
#> [3,]    1    1    1    1
matrix(numeric(12), nrow = 3) + 1 # 任意 size 全 1 矩阵的另一种写法
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    1    1    1
#> [2,]    1    1    1    1
#> [3,]    1    1    1    1
x <- diag(1:5) # create 对角矩阵
x
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    1    0    0    0    0
#> [2,]    0    2    0    0    0
#> [3,]    0    0    3    0    0
#> [4,]    0    0    0    4    0
#> [5,]    0    0    0    0    5
diag(x) <- 2
x
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,]    2    0    0    0    0
#> [2,]    0    2    0    0    0
#> [3,]    0    0    2    0    0
#> [4,]    0    0    0    2    0
#> [5,]    0    0    0    0    2
diag(x) <- letters[1:5]
x
#>      [,1] [,2] [,3] [,4] [,5]
#> [1,] "a"  "0"  "0"  "0"  "0" 
#> [2,] "0"  "b"  "0"  "0"  "0" 
#> [3,] "0"  "0"  "c"  "0"  "0" 
#> [4,] "0"  "0"  "0"  "d"  "0" 
#> [5,] "0"  "0"  "0"  "0"  "e"

矩阵转化为向量

as.vector() 按照一列一列地顺序将矩阵展开为向量

Type

  • is.matrix() 判断
  • as.matrix(x) 转换,x为数据框时,自动转换为兼容所有元素的、更灵活的数据类型
  • data.matrix(x) 转换数据框,自动将所有元素转换为数字

Size

nrow(A), ncol(A) 作用于向量时返回 NULL,其变体 NROW(), NCOL() 可作用于向量(将其视为 \(n\times1\) 矩阵) dim(A),返回长度为 2 的向量 length(A) 返回矩阵元素的个数(矩阵展开为向量的 size)

Label

在默认情况下,创建矩阵时不会自动分配行名和列名。当不同的行列有不同的含义时, 为其命名就显得必要且直观。

创建矩阵时为行列命名

matrix() 的 dimnames 参数,取值为 list(row_name_vector, col_name_vector)

创建矩阵后为行列命名

rownames(A) [<-] 提取/赋值

colnames(A) [<-] 提取/赋值

Subset

二维选择器

[行选择器, 列选择器] 每个选择器都可以是index、condition 和 name;若一个维度的参数空缺,则该维度的所有值都会被选出来

矩阵的[]选择器会返回维度尽可能低的结果,因此A[2, ]返回的是向量而非\(1\times n\)的矩阵。如果不想降维(即使选择一列也要保持 class 属性为矩阵),需要设置 drop 参数 [行选择器, 列选择器, drop = FALSE]

A <- matrix(1:16, 4)

A[2, ] # A的第2行
#> [1]  2  6 10 14
A[, -2] # A排除第2列
#>      [,1] [,2] [,3]
#> [1,]    1    9   13
#> [2,]    2   10   14
#> [3,]    3   11   15
#> [4,]    4   12   16
A[3, 2] # A的第3行第2列
#> [1] 7
A[3, c(2, 4)] # A的第3行第2列和第4列
#> [1]  7 15

一维选择器

矩阵本质上是一个向量,所以适用向量的一维选择器

A <- matrix(1:9, 3)
A[A > 3]
#> [1] 4 5 6 7 8 9

矩阵选择器

使用矩阵作为选择器,矩阵的每一行提供多维 index,选择一个元素;列数决定选择元素的个数

vals <- outer(1:5, 1:5, FUN = "paste", sep = ",")
vals
#>      [,1]  [,2]  [,3]  [,4]  [,5] 
#> [1,] "1,1" "1,2" "1,3" "1,4" "1,5"
#> [2,] "2,1" "2,2" "2,3" "2,4" "2,5"
#> [3,] "3,1" "3,2" "3,3" "3,4" "3,5"
#> [4,] "4,1" "4,2" "4,3" "4,4" "4,5"
#> [5,] "5,1" "5,2" "5,3" "5,4" "5,5"
select <- matrix(ncol = 2, byrow = TRUE, c(
  1, 1,
  3, 1,
  2, 4
))
select
#>      [,1] [,2]
#> [1,]    1    1
#> [2,]    3    1
#> [3,]    2    4
vals[select]
#> [1] "1,1" "3,1" "2,4"

Index

row(A), col(A) 返回两个矩阵,元素分别为行列下标。

A <- matrix(1:16, 4)
A
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    5    9   13
#> [2,]    2    6   10   14
#> [3,]    3    7   11   15
#> [4,]    4    8   12   16
row(A)
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    1    1    1
#> [2,]    2    2    2    2
#> [3,]    3    3    3    3
#> [4,]    4    4    4    4
col(A)
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    2    3    4
#> [2,]    1    2    3    4
#> [3,]    1    2    3    4
#> [4,]    1    2    3    4
# 提取下三角矩阵算法
A[row(A) < col(A)] <- 0
A
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    0    0    0
#> [2,]    2    6    0    0
#> [3,]    3    7   11    0
#> [4,]    4    8   12   16

Operation

转置 t(A)

向量没有维度属性,不是矩阵。要把向量转化为 \(1 \times n\)\(n \times 1\) 的矩阵,可以用 dim(),也可以用 t(), as.matrix()

x <- 1:6

as.matrix(x) # 最直观
#>      [,1]
#> [1,]    1
#> [2,]    2
#> [3,]    3
#> [4,]    4
#> [5,]    5
#> [6,]    6
x %>% `dim<-`(c(6, 1))
#>      [,1]
#> [1,]    1
#> [2,]    2
#> [3,]    3
#> [4,]    4
#> [5,]    5
#> [6,]    6
t(t(x))
#>      [,1]
#> [1,]    1
#> [2,]    2
#> [3,]    3
#> [4,]    4
#> [5,]    5
#> [6,]    6
x %>% `dim<-`(c(1, 6))
#>      [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,]    1    2    3    4    5    6
t(x)
#>      [,1] [,2] [,3] [,4] [,5] [,6]
#> [1,]    1    2    3    4    5    6

对数据框使用 t(),会自动将其转换为矩阵

矩阵乘法

  • A%*%B
    • 一个例外:若 A 和 B 均为列向量,则运算时自动对 A 转置1,执行 t(A)%*%B,返回 A 与 B 的内积
    • crossprod(A, B)t(A) %*% B 的简写
    • tcrossprod(A, B)A %*% t(B) 的简写
  • *, 哈达马积
  • kronecker(), 克罗内克积
# 矩阵乘法
m1 <- matrix(1:9, 3)
m1 %*% m1
#>      [,1] [,2] [,3]
#> [1,]   30   66  102
#> [2,]   36   81  126
#> [3,]   42   96  150

对角元素

diag(A) 返回对角元素组成的向量

矩阵的逆;线性方程组的解

  • solve(A, B), 返回A %*% X = B的解。
  • solve(A), 返回矩阵A的逆,即Solve(A, I)

特征值和特征向量

A.eigen <- eigen(A, symmetric=T)

行列式

det(A)

提取上、下三角矩阵

lower.tri(A,diag=T/F)upper.tri(A,diag=T/F),这两个函数返回逻辑矩阵

A <- matrix(1:16, 4)
A
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    5    9   13
#> [2,]    2    6   10   14
#> [3,]    3    7   11   15
#> [4,]    4    8   12   16
A[lower.tri(A)] <- 0 # A[lower.tri(A)] 为一维选择器,返回向量而非矩阵
A
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    5    9   13
#> [2,]    0    6   10   14
#> [3,]    0    0   11   15
#> [4,]    0    0    0   16

正定矩阵的Choleskey分解

chol(A)

即返回满足 \(A=P{^T}P\) 的P,其中P为上三角矩阵

矩阵合并

rbind(), cbind()

rbind(A, B)
cbind(A, B)
dplyr::bind_rows(A, B)
dplyr::bind_cols(A, B)

对行、列分别操作

  • apply(matrix, 1, f) 将行依次传递给 f
  • apply(matrix, 2, f) 将列依次传递给 f
  • rowSums()
  • rowMeans()
  • colSums()
  • colMeans()
A <- matrix(1:16, 4)
A
#>      [,1] [,2] [,3] [,4]
#> [1,]    1    5    9   13
#> [2,]    2    6   10   14
#> [3,]    3    7   11   15
#> [4,]    4    8   12   16
rowSums(A)
#> [1] 28 32 36 40
apply(A, 1, sum)
#> [1] 28 32 36 40
colMeans(A)
#> [1]  2.5  6.5 10.5 14.5
apply(A, 2, mean)
#> [1]  2.5  6.5 10.5 14.5

Array

张量,或更高维度的向量

Create

z<-array(vector, dimensions, dimnames = list(v1, v2, v3, ...))

Label

dimnames() [<-] 提取/赋值

z <- array(1:24, dim = c(2, 3, 4), dimnames = list(c("r1", "r2"), c("c1", "c2", "c3"), c("p1", "p2", "p3", "p4")))
z
#> , , p1
#> 
#>    c1 c2 c3
#> r1  1  3  5
#> r2  2  4  6
#> 
#> , , p2
#> 
#>    c1 c2 c3
#> r1  7  9 11
#> r2  8 10 12
#> 
#> , , p3
#> 
#>    c1 c2 c3
#> r1 13 15 17
#> r2 14 16 18
#> 
#> , , p4
#> 
#>    c1 c2 c3
#> r1 19 21 23
#> r2 20 22 24
dimnames(z)
#> [[1]]
#> [1] "r1" "r2"
#> 
#> [[2]]
#> [1] "c1" "c2" "c3"
#> 
#> [[3]]
#> [1] "p1" "p2" "p3" "p4"

Subset

[第1维选择器, 第2维选择器, 第3维选择器, ...]

List

list 逻辑上是一组有序键值对的集合(就像 JS 中的对象)

但实现上是广义向量(作为 values)附加了 index 或 names 属性(作为 keys)

Create

直接定义列表内容

list(name1 = value1, name2 = value2, ...),可以省略 names

长度为 N 的空列表

vector(mode = 'list', length = N)

R 中的 vector 天然体现着向量的“广义”

Type

  • is.list() 判断
  • as.list() 转换
    • as.vector()对list无效,因为 list 是广义向量,“vector”包含了”list”
  • unlist() 将列表强制转换为向量。由于规则过于复杂,最好不要使用该函数
    • 按分量顺序依次排列,强制不再嵌套
    • 自动保证同样的数据类型。比如,强制转换混合了数值和文本的列表,会得到字符串向量
l <- list(1, 2, 3)
l
#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [1] 2
#> 
#> [[3]]
#> [1] 3
as.vector(l)
#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [1] 2
#> 
#> [[3]]
#> [1] 3
as.integer(l)
#> [1] 1 2 3
unlist(l)
#> [1] 1 2 3

Size

dim(list)<-可以创建列表矩阵

l <- list(1:3, "a", TRUE, 1.0)
dim(l) <- c(2, 2)
l
#>      [,1]      [,2]
#> [1,] integer,3 TRUE
#> [2,] "a"       1
l[[1, 1]]
#> [1] 1 2 3

Label

names(list) [<-] 提取/赋值

赋值为 NULL 时移除 names 属性,或通过 x <- unname(x)

Subset

第一种语法,用[]符号,里面可以是 index/condition/key

第二种语法,用 `[`() 或 magrittr::extract()函数

l <- list("a", 2, TRUE)
l[1]
#> [[1]]
#> [1] "a"
`[`(l, 1)
#> [[1]]
#> [1] "a"
magrittr::extract(l, 1)
#> [[1]]
#> [1] "a"

下标越界时,返回 NULL

Extract

  • 第一种语法,用[[]]符号,里面可以是 index/condition(logical vector)/key
  • 第二种语法,用list$key,是list[['key']]的语法糖
    • 若 “key” 储存在一个变量 var 中,df$var等价于df[["var"]],是无效的,此时只有df[[var]]才能生效。所以,df[["key"]]总是比df$key更保险一些
    • $能部分匹配变量名,而[[]]总是完全精准地匹配
  • 第三种语法,用 `[[`() 或 magrittr::extract2()函数
  • 第四种语法,用purrr::pluck(.x, ..., .default = NULL)purrr::chuck(.x, ...)
    • …可以是用逗号间隔的多个name/index,适用于对深度嵌套列表(读取 JSON 文件时很常见)的提取
    • 要搜索的元素不存在时,pluck() 默认返回 NULL,而 chuck() 会抛出错误
l[[1]]
#> [1] "a"
`[[`(l, 1)
#> [1] "a"
magrittr::extract2(l, 1)
#> [1] "a"
x <- list(
  a = list(1, 2, 3),
  b = list(3, 4, 5)
)
purrr::pluck(x, "a", 1)
#> [1] 1
purrr::pluck(x, "c", 1)
#> NULL
purrr::pluck(x, "c", 1, .default = NA)
#> [1] NA

Operation

改变元素

对相应元素直接赋值

删除元素

赋值为 NULL 即为删除

添加元素

第一种语法,对新元素赋值。

# 添加元素的最佳写法,无需知道 index
l <- list(...)
l[[length(l) + 1]] <- new_element

第二种语法,append(list, value, after = length(list)),默认加到列表的末尾。但若插入的元素本身是一个列表(很多对象本质上都是一个列表),则会破坏层级结构,将所有的元素依次插入到前一个列表。所以若要保存列表的层级结构,还是第一种语法更保险。

l[[4]] <- data.table(x = 1, y = 2)
l$z <- c("one", "two", "three")
l
#> [[1]]
#> [1] "a"
#> 
#> [[2]]
#> [1] 2
#> 
#> [[3]]
#> [1] TRUE
#> 
#> [[4]]
#>    x y
#> 1: 1 2
#> 
#> $z
#> [1] "one"   "two"   "three"
l <- append(l, 6)
l
#> [[1]]
#> [1] "a"
#> 
#> [[2]]
#> [1] 2
#> 
#> [[3]]
#> [1] TRUE
#> 
#> [[4]]
#>    x y
#> 1: 1 2
#> 
#> $z
#> [1] "one"   "two"   "three"
#> 
#> [[6]]
#> [1] 6
l <- append(l, list(7, 8))
l
#> [[1]]
#> [1] "a"
#> 
#> [[2]]
#> [1] 2
#> 
#> [[3]]
#> [1] TRUE
#> 
#> [[4]]
#>    x y
#> 1: 1 2
#> 
#> $z
#> [1] "one"   "two"   "three"
#> 
#> [[6]]
#> [1] 6
#> 
#> [[7]]
#> [1] 7
#> 
#> [[8]]
#> [1] 8
l[[9]] <- list(9, 10)
l
#> [[1]]
#> [1] "a"
#> 
#> [[2]]
#> [1] 2
#> 
#> [[3]]
#> [1] TRUE
#> 
#> [[4]]
#>    x y
#> 1: 1 2
#> 
#> $z
#> [1] "one"   "two"   "three"
#> 
#> [[6]]
#> [1] 6
#> 
#> [[7]]
#> [1] 7
#> 
#> [[8]]
#> [1] 8
#> 
#> [[9]]
#> [[9]][[1]]
#> [1] 9
#> 
#> [[9]][[2]]
#> [1] 10

Data.Frame

data.frame 是长度相同的列向量(此处向量是广义的,即可以是 atomic vector 或 list)组成的 List.

R 原生支持向量化操作,因此 R 操作数据框中的向量有先天优势,语法简洁。

同时,data.frame 可以像矩阵一样,以二维表方式呈现。因此,R 中操作矩阵的一些(泛型)函数兼容了 data.frame 对象,即可以像操作矩阵一样操作 data.frame

Create

data.frame(var1 = col1, var2 = col2, …, row.names = NULL, check.names = TRUE, stringsAsFactors = FALSE)

  1. R 4.0 之前,构建R的原生数据框时,参数 stringsAsFactors 的默认值为 TRUE,字符向量默认被转换成因子(因子的 level 相比字符串节省储存空间),数据类型变为 numeric,无法再用字符串函数处理。4.0 后更改了这一默认值。

  2. check.names 默认为 TRUE,建立数据框时会使用函数 make.names() 检查变量名,将 R 不允许出现在变量名中的特殊字符转换为.,如将列名 Population(before) 自动转换为 Population.before.

    1. 若选 FALSE,会最大限度地保留原本的列名。但访问这些在 R 中本为非法的列时,必须用反引号将列名括起来,形如 results$`Population(before)`

list columns

# 创建数据框后添加
df <- data.frame(x = 1:3)
df$y <- list(1:2, 1:3, 1:4)
df
#>   x          y
#> 1 1       1, 2
#> 2 2    1, 2, 3
#> 3 3 1, 2, 3, 4
# 或用 I() 包裹,表示不会自动转换
data.frame(
  x = 1:3, 
  y = I(list(1:2, 1:3, 1:4))
)
#>   x          y
#> 1 1       1, 2
#> 2 2    1, 2, 3
#> 3 3 1, 2, 3, 4

Matrix and data frame columns

虽然 R 允许将矩阵和 data frame 作为列,但最好不要这样使用,因为很多函数默认每一列都是向量,不兼容这种罕见的情况

dfm <- data.frame(
  x = 1:3 * 10
)
dfm$y <- matrix(1:9, nrow = 3)
dfm$z <- data.frame(a = 3:1, b = letters[1:3], stringsAsFactors = FALSE)

str(dfm)
#> 'data.frame':    3 obs. of  3 variables:
#>  $ x: num  10 20 30
#>  $ y: int [1:3, 1:3] 1 2 3 4 5 6 7 8 9
#>  $ z:'data.frame':   3 obs. of  2 variables:
#>   ..$ a: int  3 2 1
#>   ..$ b: chr  "a" "b" "c"
dfm
#>    x y.1 y.2 y.3 z.a z.b
#> 1 10   1   4   7   3   a
#> 2 20   2   5   8   2   b
#> 3 30   3   6   9   1   c

Type

  • as.data.frame() 将矩阵、列表强制转换为数据框
    • 如果矩阵已有行、列名,会在转换过程中保留

Label

适用于 matrix 的rownames()colnames()也适用于数据框。

rownames 不是一个好的设计,完全可以将其作为数据框的一列,尽量不要用

a <- list(id = 1:5, lower = letters[1:5], upper = LETTERS[1:5])
class(a)
#> [1] "list"
colnames(a)
#> NULL
b <- as.data.frame(a)
class(b)
#> [1] "data.frame"
colnames(b)
#> [1] "id"    "lower" "upper"

Subset

既可以用列表风格的[],也可以用矩阵风格的[rowSelector, columnSelector]

  1. 矩阵风格选择器的好处在于,可以灵活地筛选行
df <- data.frame(
  id = 1:5,
  lower = letters[1:5],
  upper = LETTERS[1:5]
)

# 矩阵风格的选择器
df[df$id > 3, c(2, 3)]
#>   lower upper
#> 4     d     D
#> 5     e     E
  1. 用矩阵风格的二维选择器时,跟矩阵一样会尽可能降维
  2. 如果纵向只取了一列,则返回值会自动简化为向量,而非数据框。
  3. 若要避免降维,保留数据框结构,可以结合两种风格的选择器,也可以增加选项drop = FALSE
  4. 最好是用 dplyr 中的函数替代选择器,用 select() 时不会降维,如果希望降维再用 pull() 即可,语义很明晰。
## 保留数据框
df[2:3, 2, drop = FALSE]
#>   lower
#> 2     b
#> 3     c
df[2:3, ][2] # 混合两种选择器
#>   lower
#> 2     b
#> 3     c
df[2:3, ] %>% select(2)
#>   lower
#> 2     b
#> 3     c
# 不保留数据框
df[2:3, 2]
#> [1] "b" "c"
df[2:3, ] %>% select(2) %>% pull()
#> [1] "b" "c"
  1. subset(df, conditions, select)

函数而非操作符,便利 Pipeline 的连续操作

第一个参数为数据集,第二个参数选择符合条件的行,第三个参数select选择列,可以在列名前加’-’删除之

subset(leadership, age >= 35 | age < 24, select = c(q1, q2, q3, q4))

Extract

  • df$colname
  • df[[index]]
  • df[["colname"]]
  • 矩阵风格选择器,选择一列
  • dplyr::pull(df, colname)
df$lower
#> [1] "a" "b" "c" "d" "e"
df[[2]]
#> [1] "a" "b" "c" "d" "e"
df[["lower"]]
#> [1] "a" "b" "c" "d" "e"
df[, 2]
#> [1] "a" "b" "c" "d" "e"
dplyr::pull(df, lower)
#> [1] "a" "b" "c" "d" "e"

Operation

对数据框的精细操作是 R 最核心的内容,之后单列一章说明。

增删改

类似 list、matrix,以赋值为核心。

数据框合并

rbind(), cbind()

描述统计

summary() 描述统计各列数据

summary(mtcars)
#>       mpg             cyl             disp             hp       
#>  Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0  
#>  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5  
#>  Median :19.20   Median :6.000   Median :196.3   Median :123.0  
#>  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7  
#>  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0  
#>  Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0  
#>       drat             wt             qsec             vs        
#>  Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000  
#>  1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000  
#>  Median :3.695   Median :3.325   Median :17.71   Median :0.0000  
#>  Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375  
#>  3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000  
#>  Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000  
#>        am              gear            carb      
#>  Min.   :0.0000   Min.   :3.000   Min.   :1.000  
#>  1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
#>  Median :0.0000   Median :4.000   Median :2.000  
#>  Mean   :0.4062   Mean   :3.688   Mean   :2.812  
#>  3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
#>  Max.   :1.0000   Max.   :5.000   Max.   :8.000

tibble

data.frame 的优化替代类型,但已有更好的替代类型 data.table,所以 tibble 不再重要

tibble 打印时

  • 列出了变量的类型
  • 只显示10行
  • 只显示有限的列数(与屏幕适应的)
  • 高亮 NAs

Create

tibble 有一个很突出的优点:创建时,写在后面的变量可以立即使用前面的变量

# 这种写法如果用 data.table,会报错,表示不知道 b 定义式中的 a 是什么
tibble(
  a = runif(1000, 0, 5),
  b = 4 + rnorm(1000, mean = 3.2 * a, sd = 1)
) %>% head()
#> # A tibble: 6 × 2
#>       a     b
#>   <dbl> <dbl>
#> 1 0.140  4.69
#> 2 4.95  19.4 
#> 3 2.24  10.6 
#> 4 1.67   8.40
#> 5 3.41  15.8 
#> 6 4.75  20.2

数据量不大时,可以用tribble()创建

df <- tribble(
  ~x, ~y, ~z,
  "a", 2, 3.6,
  "b", 1, 8.5
)

Transfer

as_tibble()

Subset

subset 时,即使选取单行或单列,结果也不会降维,仍然是 tibble

Operation

add_column(df, w = 0:1) # 增加一列
#> # A tibble: 2 × 4
#>   x         y     z     w
#>   <chr> <dbl> <dbl> <int>
#> 1 a         2   3.6     0
#> 2 b         1   8.5     1
add_row(df, x = "c", y = 9, z = 2.1) # 增加一行
#> # A tibble: 3 × 3
#>   x         y     z
#>   <chr> <dbl> <dbl>
#> 1 a         2   3.6
#> 2 b         1   8.5
#> 3 c         9   2.1
add_row(df, x = "c", y = 9, z = 2.1, .before = 2) # 在第二行,增加一行
#> # A tibble: 3 × 3
#>   x         y     z
#>   <chr> <dbl> <dbl>
#> 1 a         2   3.6
#> 2 c         9   2.1
#> 3 b         1   8.5
  1. print(tibble, n = 10, width = Inf)可以自定义打印的长度和宽度。默认只显示10行,避免输出太长.
    1. 也可以用options(tibble.print_max=n, tibble.print_min=m)进行全局性设置:如果多于m行,则最多打印出n行。
  2. 对于很大的数据表,也可以用%>% View()%T>% View()查看
  3. tibble 不支持行名。tibble::rownames_to_column(df, var = "rowname")可以将行名转化为一列,tibble::rowid_to_column(df, var = "rowid")将行 index 转化为一列。

  1. 若不转置,这个运算根本无法进行。↩︎

LS0tDQp0aXRsZTogIlZlY3Rvcu+8iOW5v+S5ieWQkemHj++8iSINCnN1YnRpdGxlOiAnQXRvbWljIFZlY3RvciwgTWF0cml4LCBMaXN0IGFuZCBEYXRhZnJhbWUnDQphdXRob3I6ICJIdW1vb24iDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IGh0bWxfZG9jdW1lbnQNCmRvY3VtZW50Y2xhc3M6IGN0ZXhhcnQNCmNsYXNzb3B0aW9uOiBoeXBlcnJlZiwNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQ0Kc291cmNlKCIuLi9SbWFya2Rvd24tdGVtcGxhdGUvUm1hcmtkb3duX2NvbmZpZy5SIikNCg0KIyMgZ2xvYmFsIG9wdGlvbnMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgd2lkdGggPSBjb25maWckd2lkdGgsDQogIGZpZy53aWR0aCA9IGNvbmZpZyRmaWcud2lkdGgsDQogIGZpZy5hc3AgPSBjb25maWckZmlnLmFzcCwNCiAgb3V0LndpZHRoID0gY29uZmlnJG91dC53aWR0aCwNCiAgZmlnLmFsaWduID0gY29uZmlnJGZpZy5hbGlnbiwNCiAgZmlnLnBhdGggPSBjb25maWckZmlnLnBhdGgsDQogIGZpZy5zaG93ID0gY29uZmlnJGZpZy5zaG93LA0KICB3YXJuID0gY29uZmlnJHdhcm4sDQogIHdhcm5pbmcgPSBjb25maWckd2FybmluZywNCiAgbWVzc2FnZSA9IGNvbmZpZyRtZXNzYWdlLA0KICBlY2hvID0gY29uZmlnJGVjaG8sDQogIGV2YWwgPSBjb25maWckZXZhbCwNCiAgdGlkeSA9IGNvbmZpZyR0aWR5LA0KICBjb21tZW50ID0gY29uZmlnJGNvbW1lbnQsDQogIGNvbGxhcHNlID0gY29uZmlnJGNvbGxhcHNlLA0KICBjYWNoZSA9IGNvbmZpZyRjYWNoZSwNCiAgY2FjaGUuY29tbWVudHMgPSBjb25maWckY2FjaGUuY29tbWVudHMsDQogIGF1dG9kZXAgPSBjb25maWckYXV0b2RlcA0KKQ0KYGBgDQoNCiMjIFZlY3RvciDnmoTliIbnsbsNCg0KMS4gYXRvbWljIHZlY3RvciwgbWF0cml4LCBhcnJheSDpg73opoHmsYLljZXkuIDmlbDmja7nsbvlnosgKGhvbW9nZW5lb3VzIGRhdGEgdHlwZSnvvIwqKuagueWfuuaYryBhdG9taWMgdmVjdG9yKioNCiAgIDEuIGF0b21pYyB2ZWN0b3Ig5re75Yqg57u05bqm5bGe5oCn5Y+v55Sf5oiQIG1hdHJpeCwgYXJyYXkNCiAgIDIuIGF0b21pYyB2ZWN0b3Ig5re75YqgIGNsYXNzIOWxnuaAp+WPr+eUn+aIkCBmYWN0b3IsIGRhdGUg5ZKMIGRhdGUtdGltZQ0KMi4gbGlzdCwgZGF0YS5mcmFtZSDlj6/ku6Xoo4Xovb3kuI3lkIzmlbDmja7nsbvlnosgKGhldGVyb2dlbmVvdXMgZGF0YSB0eXBlcynvvIwqKuagueWfuuaYryBsaXN0KioNCiAgIDEuIOWIl+ihqCoq5o+Q6auY5LqG5a2Y5YKo55qE54G15rS75oCnKirvvIzkvYYqKumZjeS9juS6huWtmOWCqOWSjOi/kOeul+aViOeOhyoqDQogICAyLiDmlbDmja7moYbmmK/kuIDnp43nibnmrornmoTliJfooagNCjMuIGF0b21pYyB2ZWN0b3Ig5ZKMIGxpc3Qg5Y+v57uf56ew5Li6IHZlY3Rvcg0KDQojIyMgZGF0YSB0eXBlcyBvZiBhdG9taWMgdmVjdG9yDQoNCiMjIyMgbG9naWNhbA0KDQpgVFJVRWAvYFRgIOWSjCBgRkFMU0VgL2BGYA0KDQrkuIDkupvooqvorr7lrprmjqXmlLbpgLvovpHovpPlhaXnmoTlh73mlbDkuZ/lj6/ku6XmjqXmlLbpnZ7pgLvovpHlkJHph4/vvIzkvovlpoLmlbDlgLzlkJHph4/jgILmraTml7bvvIzpnZ7pgLvovpHlkJHph4/kvJrooqvlvLrliLbovazmjaLmiJDpgLvovpHlkJHph4/vvJrpnZ7pm7bmlbDlgLzkvJrooqvlvLrliLbovazmjaLmiJAgVFJVRe+8jOWPquaciSAwIOS8muiiq+W8uuWItui9rOaNouaIkCBGQUxTRe+8m+Wtl+espuS4suS4jeiDveiiq+W8uuWItui9rOaNouaIkOmAu+i+keWAvO+8jOS8muaKpemUmeOAgg0KDQojIyMjIG51bWVyaWMNCg0K57uf56ew5pWw5YC85Z6LDQoNCi0g5pW05pWw77yMaW50ZWdlcu+8jOW9ouWmgiBgMkxgDQotIOWPjOeyvuW6pua1rueCueaVsO+8jGRvdWJsZQ0KICAgLSDkuInnp43nibnmrormg4XlhrXvvJpJbmYsIC1JbmYsIE5hTi4gDQogICAtIOWPquimgeihqOi+vuW8j+S4reacieato+i0n+aXoOept+eahOmhueWHuueOsO+8jOe7k+aenOWwseW+iOWPr+iDveS4uiBJbmYsIC1JbmYsIE5hTg0KDQpgYGB7cn0NCmV4cCgxMDAwKSAjIFIg5Lit5pWw5a2X55qE5LiK6ZmQ5Li6IDEuOCoxMF4zOO+8jOi2hei/h+i/meS4quinhOaooeWwseS8muWHuueOsCBJbmYgDQotMTAgLyAwICMgLUluZiDotJ/ml6DnqbcNCmV4cCgxMDAwKSAvIGV4cCg5OTApICMgTmFOLCBub3QgYSBudW1iZXINCmBgYA0KDQojIyMjIGNoYXJhY3Rvcg0KDQrlrZfnrKbkuLINCg0KIyMjIyBjb21wbGV4DQoNCuWkjeaVsO+8jOW9ouWmgiBgMSsyaWANCg0KIyMjIyDnqbrlgLwgYE5VTExgIOWSjOe8uuWkseWAvCBgTkFgDQoNCmBOVUxMYO+8jOmbtumVvyoq5ZCR6YePKirvvIzkuIDoiKzooajnpLrlj4LmlbDmnKrooqvotYvlgLzvvIzku6Xlj4rlh73mlbDmsqHmnInmmI7noa7ov5Tlm57lgLzml7bnmoTov5Tlm57jgILmiYDlsZ7nsbvjgIHmlbDmja7nsbvlnovlnYfkuLogIk5VTEwiDQoNCmBOQWDvvIzplb/luqbkuLogMSDnmoTpgLvovpHlkJHph4/vvIzooajnpLrmlbDmja7vvIjlhYPntKDvvInnmoTnvLrlpLHlgLzjgILmiYDlsZ7nsbvjgIHmlbDmja7nsbvlnovlnYfkuLogImxvZ2ljYWwiDQoNCuWIpOaWrSBOVUxMIOWSjCBOQSDkuI3og73nlKggYD09YO+8jOWPquiDveeUqCBgaXMubmEoKWAg5ZKMIGBpcy5udWxsKClgDQoNCmBgYHtyfQ0KYSA8LSBOVUxMDQphICMg5rOo5oSP5o6n5Yi25Y+w55qE6L6T5Ye65rKh5pyJIFsxXe+8jOmVv+W6pui/njHpg73msqHmnIkNCmxlbmd0aChhKQ0KY2xhc3MoYSkNCnR5cGVvZihhKQ0KYSA9PSBOVUxMICMg6L+U5Zue6ZW/5bqm5Li6MOeahOWQkemHj++8jOaXouS4jeaYryBUUlVFIOS5n+S4jeaYryBGQUxTRQ0KaXMubmEoYSkNCg0KYiA8LSBOQQ0KYg0KbGVuZ3RoKGIpDQpjbGFzcyhiKQ0KdHlwZW9mKGIpDQpiID09IE5BICMgTkEg6L+b6KGM5Lu75L2V6L+Q566X6YO96L+U5ZueIE5BDQppcy5udWxsKGIpDQpgYGANCg0KIyMjIyDmn6XnnIsgZGF0YSB0eXBlDQoNCmB0eXBlb2YoKWANCg0KMS4gdmVjdG9y44CBbWF0cml4IOi/lOWbnuWFtuS4reWFg+e0oOeahCBkYXRhIHR5cGUNCjIuIGxpc3TjgIFkYXRhLmZyYW1lIOi/lOWbniAibGlzdCINCg0KIyMjIGNvbWJpbmUgZGF0YQ0KDQrnu4TlkIgoY29tYmluZSwg5LiA6Iis5L2/55SoYGMoLi4uLCB1c2UubmFtZXMgPSBUUlVFKWAp5LiN5ZCM5pWw5o2u57G75Z6L55qE5ZCR6YeP5oiW5YiX6KGo5pe277yM5Lya5by65Yi26L2s5o2i5Li65YW85a655omA5pyJ5YWD57Sg55qE44CB5pu054G15rS755qE5pWw5o2u57G75Z6LDQoNCuS4gOiIrOmhuuW6j+S4uu+8mmxvZ2ljYWw8aW50ZWdlcjxudW1lcmljPGNvbXBsZXg8Y2hhcmFjdGVyPGxpc3QNCg0KYGBge3J9DQphIDwtIDE6Mg0KYiA8LSBsZXR0ZXJzWzE6Ml0NCmMoYSwgYikNCg0KbDQgPC0gbGlzdChsaXN0KDEsIDIpLCBjKDMsIDQpKQ0KbDUgPC0gYyhsaXN0KDEsIDIpLCBjKDMsIDQpKQ0Kc3RyKGw0KQ0Kc3RyKGw1KQ0KYGBgDQoNCiFbXShodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0L2ViODFjNzA3MjM2NjRmMDkyMGQwZmQ0MTRlM2YzMjZiOWFkMWZhNmUvYzA2YWUvZGlhZ3JhbXMvdmVjdG9ycy9saXN0LWMucG5nKQ0KDQojIyMgYXR0cmlidXRlcw0KDQrlsZ7mgKfljbPkvZzkuLogbWV0YWRhdGEg55qE6ZSu5YC85a+577yM6ZmE5Yqg5Yiw5a+56LGh5LiKDQoNCiMjIyMg5bi455So5bGe5oCnDQoNCjEuIOmVv+W6pizvvIxgbGVuZ3RoKClgDQogICAxLiB2ZWN0b3LjgIFtYXRyaXgg6L+U5Zue5YW25Lit5YWD57Sg55qE5Liq5pWwDQogICAyLiBsaXN0IOi/lOWbnumUruWAvOWvueS4quaVsA0KICAgMy4g5pWw5o2u5qGG6L+U5Zue5YiX5pWw77yM5Zug5Li65q+P5YiX77yI5YiX5ZCNOuWAvOWQkemHj++8ieaYryBsaXN0IOeahOS4gOS4qumUruWAvOWvuQ0KMi4g55+p6Zi144CB5pWw57uE5ZKM5pWw5o2u5qGG55qE57u05bqm77yMYGRpbSgpYA0KICAgMS4gZGltKHZlY3Rvcikg5LiN5pivMe+8jCDogIzmmK8gTlVMTA0KMy4gbmFtZXPvvIznu5nmr4/kuKrlhYPntKDkuIDkuKogbmFtZSDnmoTmlbDlgLzlkJHph48NCiAgIDEuIGBuYW1lcygpWzwtXWAvYHNldE5hbWVzKClgIOiOt+WPli/orr7lrpogbmFtZXMg5bGe5oCn77yM6K6+572u5Li6IE5VTEwg5Y+v5Yig6ZmkIG5hbWVzIOWxnuaApw0KICAgMi4gYHVubmFtZSgpYCDljrvmjokgbmFtZXMg5bGe5oCn77yI6K+l6K+t5Y+l5Lmf5Lya55Sf5oiQ5LiA5Liq5paw5Y+Y6YeP77yJDQogICAzLiDku47mupDku6PnoIHlj6/nn6XvvIxgc2V0TmFtZXMoKWDlkoxgdW5uYW1lKClg5piv5Li65LqG6YWN5ZCI566h6YGT6ICM6K6+6K6h55qE6K+t5rOV57OW77yM5YaF6YOo6LCD55So55qE6L+Y5pivIGBuYW1lcygpPC1gDQoNCiMjIyMg5aSE55CG5Ye95pWwDQoNCi0gYGF0dHIoeCwgJ2EnKVs8LV1gIGdldCBhbmQgbW9kaWZ5IOWxnuaApw0KLSBgYXR0cmlidXRlcyh4KWAgZ2V0IOWFqOmDqOWxnuaApw0KLSBgc3RydWN0dXJlKC5EYXRhID0geCwgYXR0cjEgPSB2YWx1ZXMxLCBhdHRyMiA9IHZhbHVlczIsIC4uLilgIOaVtOS9k+iuvue9ruWFqOmDqOWxnuaAp+OAguS9huimgeazqOaEj++8muivpeivreWPpeS8mueUn+aIkOS4gOS4quaWsOWPmOmHj+OAgg0KDQpgYGB7cn0NCmF0dHIoYSwgInJvbWFuX2luZGV4IikgPC0gYygiSSIsICJJSSIpDQphDQphdHRyKGEsICJyb21hbl9pbmRleCIpIDwtIE5VTEwNCmENCmEgPC0gc3RydWN0dXJlKC5EYXRhID0gYSwgcm9tYW5faW5kZXggPSBjKCJJIiwgIklJIikpDQphDQoNCm5hbWVzKGEpIDwtIGINCmF0dHJpYnV0ZXMoYSkNCnN0cihhKQ0KYQ0KdW5uYW1lKGEpDQphDQphIDwtIHVubmFtZShhKQ0KYQ0KYGBgDQoNCiMjIyBjbGFzcyDlsZ7mgKfkuI4gUzMg5a+56LGhDQoNCua3u+WKoCBjbGFzcyDlsZ7mgKflkI7vvIzlr7nosaHlsIbooqvovazmjaLkuLogUzMg5a+56LGh77yM5Lyg6YCS57uZ5rOb5Z6L5Ye95pWw5pe25bCG5YW35pyJ54us54m555qE6KGM5Li6DQoNCiFbXShodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0L2JhYTE5ZDBlYmY5Yjk3OTQ5YTdhZDI1OWIyOWExYzRhZTAzMWM4ZTIvOGU5YjgvZGlhZ3JhbXMvdmVjdG9ycy9zdW1tYXJ5LXRyZWUtczMtMS5wbmcpDQoNCiMjIyMgRmFjdG9yDQoNCmBgYHtyfQ0KeCA8LSBmYWN0b3IoYygnYScsICdiJywgJ2InLCAnYScpKQ0KeA0KdHlwZW9mKHgpDQphdHRyaWJ1dGVzKHgpDQp0YWJsZSh4KQ0KYGBgDQoNCiFbXShodHRwczovL2QzM3d1YnJma2kwbDY4LmNsb3VkZnJvbnQubmV0LzFjMTllYWJjNTlkNDQ1Y2FiNzhmYzYwNTA1M2M3MzkzZWM2MzBlMTUvNTg0M2IvZGlhZ3JhbXMvdmVjdG9ycy9mYWN0b3IucG5nKQ0KDQojIyMjIERhdGUNCg0KY2xhc3Mg5bGe5oCn77yaRGF0ZQ0KDQpgYGB7cn0NCnRvZGF5IDwtIFN5cy5EYXRlKCkNCnR5cGVvZih0b2RheSkNCmF0dHJpYnV0ZXModG9kYXkpDQp1bmNsYXNzKHRvZGF5KSAjIOS7jjE5NzDlubQx5pyIMeaXpeeul+i1tw0KYGBgDQoNCiMjIyMgRGF0ZS10aW1lDQoNCmNsYXNzIOWxnuaAp++8mlBPU0lYY3QNCg0KYGBge3J9DQpub3dfY3QgPC0gYXMuUE9TSVhjdCgiMjAxOC0wOC0wMSAyMjowMCIsIHR6ID0gIlVUQyIpDQpub3dfY3QNCnR5cGVvZihub3dfY3QpDQphdHRyaWJ1dGVzKG5vd19jdCkNCnVuY2xhc3Mobm93X2N0KSAjIOS7jjE5NzDlubQx5pyIMeaXpeeul+i1t+eahOenkuaVsA0KDQpzdHJ1Y3R1cmUobm93X2N0LCB0em9uZSA9ICJBc2lhL1Rva3lvIikNCnN0cnVjdHVyZShub3dfY3QsIHR6b25lID0gIkFtZXJpY2EvTmV3X1lvcmsiKQ0Kc3RydWN0dXJlKG5vd19jdCwgdHpvbmUgPSAiQXVzdHJhbGlhL0xvcmRfSG93ZSIpDQpzdHJ1Y3R1cmUobm93X2N0LCB0em9uZSA9ICJFdXJvcGUvUGFyaXMiKQ0KYGBgDQoNCiMjIyMgRHVyYXRpb24NCg0KY2xhc3Mg5bGe5oCn77yaZGlmZnRpbWUNCg0KYGBge3J9DQpvbmVfd2Vla18xIDwtIGFzLmRpZmZ0aW1lKDEsIHVuaXRzID0gIndlZWtzIikNCm9uZV93ZWVrXzENCnR5cGVvZihvbmVfd2Vla18xKQ0KYXR0cmlidXRlcyhvbmVfd2Vla18xKQ0KDQpvbmVfd2Vla18yIDwtIGFzLmRpZmZ0aW1lKDcsIHVuaXRzID0gImRheXMiKQ0Kb25lX3dlZWtfMg0KdHlwZW9mKG9uZV93ZWVrXzIpDQphdHRyaWJ1dGVzKG9uZV93ZWVrXzIpDQpgYGANCg0KIyMgQXRvbWljIFZlY3Rvcg0KDQoqKuazqOaEjyoqDQoNCjEuICBS5Lit5peg5qCH6YeP77yM5qCH6YeP6KKr6buY6K6k5Li66ZW/5bqm5Li6MeeahOWQkemHj++8jOaJgOS7peWcqOaOp+WItuWPsOaYvuekuuS4gOS4quaVsOaXtu+8jOWJjemdouS7jeS8muacieihqOekuuWQkemHj+e7tOW6pueahCBgWzFdYA0KMi4gIFLkuK3nmoTlkJHph4/pu5jorqTkuLrliJflkJHph4/vvIzkuI3lrZjlnKjooYzlkJHph4/jgILlr7nliJflkJHph4/ovaznva7vvIzlvpfliLDnmoTmmK/kuIDkuKokMVx0aW1lcyBuJOeahOefqemYtQ0KMy4gUuS4reeahOWfuuehgOi/kOeul+mDveaYr+WQkemHj+WMlueahA0KDQojIyMgQ3JlYXRlDQoNCiMjIyMgYHZlY3RvcigpYA0KDQotIGB2ZWN0b3IobW9kZSA9ICdkb3VibGUnLCBsZW5ndGggPSBOKWDvvIznlJ/miJDplb/luqbkuLogTiDnmoTnqbrlkJHph48NCiAgIC0g5Y+C5pWwIG1vZGUg5Y+v5Lul5Li6ICJpbnRlZ2VyIi8iZG91YmxlIi8ibnVtZXJpYyIvImxvZ2ljYWwiLyJjaGFyYWN0ZXIiIOetie+8jOS4uiAibGlzdCIg5YiZ55Sf5oiQ5YiX6KGoDQogICAtIOavj+mhueeahOWAvOS4uiBgMGAvYEZBTFNFYC9gIiJgL2BOVUxMYO+8iOeUn+aIkOWIl+ihqOaXtu+8iQ0KLSDlr7nkuo7nibnlrprmlbDmja7nsbvlnovnmoTlkJHph4/vvIzlj6/ku6XnroDlhpnkuLrvvJpgbnVtZXJpYyhOKWAsIGBsb2dpY2FsKE4pYCwgYGNoYXJhY3RlcihOKWANCg0K5YWoIDEg5ZCR6YeP55qE5YaZ5rOV5Li6YHZlY3RvcignbnVtZXJpYycsIE4pICsgMWDmiJZgbnVtZXJpYyhOKSArIDFg77yM5q+UIGByZXAoMSwgTilgIOeahOWPr+ivu+aAp+abtOWlvQ0KDQpgYGB7cn0NCnkgPC0gbnVtZXJpYyg2KSArIDENCnkNCnogPC0gbG9naWNhbCg3KQ0Keg0KdyA8LSBjaGFyYWN0ZXIoMikNCncNCncgPC0gYyh3LCAiYSIpDQp3DQpgYGANCg0KDQojIyMjIGBjKClgDQoNCue7hOWQiOWQkemHj++8iOWwhuWkmuS4quWNleWFg+e0oOWQkemHj+WSjOWkmuWFg+e0oOWQkemHj+i/nuaOpei1t+adpeaehOaIkOS4gOS4quWQkemHj++8ie+8jOWFtuS7luivreiogOS4gOiIrOeUqGBbXWANCg0KYGMoKWAg5Lit5Y2z5L2/5bWM5aWX77yM5Lmf5Lya6KKr6Ieq5Yqo5bGV5byA5Li6IGF0b21pYyB2ZWN0b3INCg0KYGBge3J9DQpjKDEsIDIsIGMoMywgNCwgNTo3KSkNCmBgYA0KDQpjcmVhdGUg5ZCR6YeP5pe25Y+v5Lul5ZG95ZCNDQoNCmBgYHtyfQ0KeCA8LSBjKGEgPSAxLCBiID0gMiwgYyA9IDMpDQp4DQpzdHIoeCkgIyDlj6/op4HlsZ7mgKcgbmFtZXMg5L+d5a2Y552A5YWD57Sg55qE5ZCN56ewDQpgYGANCg0KIyMjIyBgbTpuYA0KDQrov57nu63mlbTmlbDlkJHph4/vvIznsbvlnovkuLogaW50ZWdlciB2ZWN0b3INCg0KYGBge3J9DQp0eXBlb2YoMTozKQ0KYGBgDQoNCiMjIyMgYHNlcSooKWANCg0KLSBgc2VxKGZyb20sIHRvLCBieT0xLCBsZW5ndGgub3V0KWDvvIznrYnlt67mlbDliJfvvIznu5nlh7rkuInkuKrlj4LmlbDljbPlj68NCi0gYHNlcV9sZW4obilg77yM562J5Lu35LqOYDE6bmANCi0gKipgc2VxX2Fsb25nKHgpYO+8jOS4gOiIrOavlCBgMTpsZW5ndGgoeClgIOabtOWuieWFqCoqDQogICAtIOiLpSB4IOeahOmVv+W6puS4ujDvvIxgc2VxX2Fsb25nKHgpYOS8mui/lOWbnmBpbnRlZ2VyKDApYO+8jOiAjOmdnmBjKDEsIDApYA0KDQpgYGB7cn0NCnNlcV9hbG9uZyhMRVRURVJTKQ0KYGBgDQoNCiMjIyMgYHJlcCh4LCAuLi4pYA0KDQouLi4g5Lit55qE5Y+C5pWw5YyF5ous77yaDQoNCjEuIHRpbWVzLCDph43lpI3lr7nosaEgeCDmlbTkvZPnmoTmrKHmlbDvvIzkuIDoiKzkuLrmoIfph48NCiAgIDEuIOivpeWPguaVsOS5n+WPr+S7peaYr+S4jiB4IOmVv+W6puS4gOiHtOeahOWQkemHj++8jOihqOekuuWvueW6lOWFg+e0oOmHjeWkjeWvueW6lOasoeaVsA0KMi4gZWFjaCwg5a+5IHgg5Lit5q+P5Liq5YWD57Sg6YeN5aSN55qE5qyh5pWwDQozLiBsZW5ndGgub3V0LCDph43lpI3oi6XlubLmrKHlkI7nu5PmnpznmoTplb/luqYNCg0KYGBge3J9DQpyZXAoMTozLCB0aW1lcyA9IDIpDQpyZXAoMTozLCB0aW1lcyA9IGMoMiwgNCwgMSkpDQpyZXAoMTozLCBlYWNoID0gMikNCnJlcCgxOjMsIGxlbmd0aC5vdXQgPSA5KQ0KcmVwKDE6MywgbGVuZ3RoLm91dCA9IDEwKQ0KYGBgDQoNCiMjIyBUeXBlDQoNCi0gICBgY2xhc3MoKWAg5oiWIGB0eXBlb2YoKWAg5p+l55yL57G75Z6LDQotICAgYGlzLiooKWAg5Yik5pat57G75Z6LDQogICAtIOWPr+S7peS9v+eUqCBgaXMubG9naWNhbCgpYCwgYGlzLmludGVnZXIoKWAsIGBpcy5kb3VibGUoKWAsIGBpcy5jaGFyYWN0ZXIoKWAgDQogICAtIOWwvemHj+S4jeimgeS9v+eUqCBgaXMudmVjdG9yKClgLCBgaXMuYXRvbWljKClgLCBgaXMubnVtZXJpYygpYA0KLSAgIGBhcy4qKClgIOi9rOaNouexu+Weiw0KICAgLSDlsL3ph4/kuI3opoHkvb/nlKhgYXMudmVjdG9yKClgDQoNCmBgYHtyfQ0KY2xhc3MoMTo1KQ0KDQpjbGFzcyhjKFQsIEYsIFQpKQ0KDQppcy5jaGFyYWN0ZXIoYygiaCIsICJlIiwgImwiLCAibCIsICJvIikpDQoNCmFzLmNoYXJhY3RlcigxOjUpDQoNCnN0cmluZ3MgPC0gYygiMSIsICIyIiwgIjMiKQ0KdHJ5KHN0cmluZ3MgKyAxMCkNCnRyeShhcy5udW1lcmljKHN0cmluZ3MpICsgMTApDQpgYGANCg0KIyMjIFNpemUNCg0KYGxlbmd0aCgpYA0KDQpgYGB7cn0NCmxlbmd0aCh3KQ0KYGBgDQoNCiMjIyBMYWJlbA0KDQpgbmFtZXMoKVs8LV1gIOaPkOWPli/otYvlgLwNCg0K6LWL5YC85Li6IE5VTEwg5pe256e76ZmkIG5hbWVzIOWxnuaAp++8jOaIlumAmui/hyBgeCA8LSB1bm5hbWUoeClgDQoNCiMjIyBTdWJzZXQNCg0KIyMjIyDpgInmi6nlmahgW11g5ZKM5o+Q5Y+W5ZmoYFtbXV1gDQoNCmBbXWDkuLrlrZDpm4bpgInmi6nlmajvvIzov5Tlm57nmoTmmK/ljp/mlbDmja7nmoTkuIDkuKrliIfniYfvvIzkuI3mlLnlj5jmlbDmja7nu5PmnoQNCg0KYFtbXV1g5Li65YWD57Sg5o+Q5Y+W5Zmo77yM6L+U5Zue55qE5piv5Y6f5pWw5o2u5LiA5LiqKirplb/luqbkuLox55qE5YiH54mHKirkuK3oo4Xovb3nmoTlhoXlrrkNCg0K5a+55ZCR6YeP5pON5L2c77yMYFtdYOWSjGBbW11dYOeahOW3ruWIq+W+iOWwj++8m+S9huWvueabtOWkjeadgueahOaVsOaNruexu+Wei++8jOS6jOiAheeahOW3ruWIq+S8muW+iOaYjuaYvu+8mg0KDQpgYGB7cn0NCnN0cihpcmlzKSAjIOaVsOaNruahhg0KDQpzdHIoaXJpc1sxXSkgIyDlj6rlkKvkuIDkuKrlj5jph4/nmoTmlbDmja7moYYNCg0Kc3RyKGlyaXNbWzFdXSkgIyDlkJHph48NCmBgYA0KDQojIyMjIGBbXWDpgInlj5bnmoTkuInnp43mlrnlvI8NCg0KLSDkvY3nva4gaW5kZXjvvIxgeFs0XTsgeFstMjotNF07IHhbYygxLCAzKV1gDQogICAtIOWFtuS4reato+aVsOihqOekuumAieaLqe+8jOWkjeaVsOihqOekuuWJlOmZpO+8jGBbXWDkuK3kuI3og73lkIzml7bmnInmraPmlbDlkozotJ/mlbANCiAgIC0gaW5kZXgg5Li6IDAg5bCG6L+U5Zue6ZW/5bqm5Li6IDAg55qE5ZCR6YePDQogICAtIGluZGV4IOS4uuepuuihqOekuuS/neeVmei/meS4gOe7tOW6pueahOaJgOacieWFg+e0oA0KLSBsb2dpY2FsIHZlY3Rvciwg6YCJ5Ye655u45bqU5L2N572u5Li6IFRSVUUg55qE5YWD57SgDQogICAtIOmAmui/hyBjb25kaXRpb24g6I635b6X6YC76L6R5ZCR6YeP77yMYHhbeCA9PSAxMF07IHhbeCAlaW4lIGMoMSwgMiwgNSldYA0KLSBuYW1lcyDlsZ7mgKfvvIxgeFsiYXBwbGUiXWANCg0KYGBge3J9DQp4IDwtIGMoMSwgMiwgMywgNCwgNSwgNiwgNykNCm5hbWVzKHgpIDwtIGxldHRlcnNbMTo3XQ0KDQojIGluZGV4DQp4WzM6NV0gIyB45ZCR6YeP5Lit55qE56ysMy015Liq5YWD57SgDQp4W2MoMSwgNCldIDwtIGMoMTAwLCA0MDApICMg56ysMeS4quWSjOesrDTkuKrlhYPntKANCngNCnhbNjo5XSAjIOi2heWHumluZGV45pe26L+U5ZueIE5BDQp4DQp4WzEwXSA8LSAxMCAjIOWvueS4gOS4quW5tuS4jeWtmOWcqOeahOWFg+e0oOmHjeaWsOi1i+WAvO+8jOWwhuiHquWKqOeUqCBOQSDloavlhYXmnKrooqvmjIflrprnmoTkvY3nva4NCngNCnhbLTJdICMg6Zmk5LqG56ys5LqM5Liq5Lul5aSW55qE5YWD57SgDQp4Wy0yOi00XSAjIOetieS7t+S6jiB4W2MoLTIsIC0zLCAtNCld77yM5Y2z5Y675o6J56ysMuOAgTPjgIE05Liq5YWD57SgDQoNCiMgY29uZGl0aW9uDQp4W3ggJWluJSBjKDEsIDIsIDUpXQ0KDQojIG5hbWVzDQp4WyJlIl0NCnhbYygiZSIsICJqIildICMg6K6/6Zeu5LiN5a2Y5Zyo55qEbmFtZe+8jOi/lOWbniBOQQ0KYGBgDQoNCiMjIyMgQXBwbGljYXRpb246IGxvb2t1cCB0YWJsZQ0KDQpgYGB7cn0NCnggPC0gYygibSIsICJmIiwgInUiLCAiZiIsICJmIiwgIm0iLCAibSIpDQpsb29rdXAgPC0gYyhtID0gIk1hbGUiLCBmID0gIkZlbWFsZSIsIHUgPSBOQSkgIyDplK7lgLzlr7kNCmxvb2t1cFt4XSAlPiUgdW5uYW1lKCkNCmBgYA0KDQojIyMjIGBbW11dYOW/veeVpSBuYW1lcw0KDQpgW1tdXWAg5Y+q6IO95o+Q5Y+W5LiA5Liq5YWD57Sg77yM5YW25Lit5Lmf5LiN6IO95Ye6546w6LSf5pW05pWw5oiW6K6/6Zeu5LiN5a2Y5Zyo55qE5YWD57Sg77yI5LiL5qCH6LaK55WM77yJDQoNCmBgYHtyfQ0KeCA8LSBjKGEgPSAxLCBiID0gMiwgYyA9IDMpDQp4WyJhIl0NCnhbWyJhIl1dDQoNCnRyeSh4W1stMV1dKQ0KdHJ5KHhbWzRdXSkNCnRyeSh4W1siZCJdXSkNCmBgYA0KDQojIyMjIOS4i+agh+i2iueVjA0KDQrov5Tlm54gTkENCg0KIyMjIEluZGV4DQoNCiMjIyMgYHdoaWNoKmANCg0KLSAgIGB3aGljaCjpgLvovpHooajovr7lvI8pYA0KLSAgIGB3aGljaC5taW4oKWANCi0gICBgd2hpY2gubWF4KClgDQoNCmBgYHtyfQ0KeA0Kd2hpY2gubWF4KHgpDQp3aGljaC5taW4oeCkNCndoaWNoKHggPT0gMikNCndoaWNoKHggPiA0KQ0KYGBgDQoNCiMjIyMgYCptYXRjaGANCg0KLSAgIGBtYXRjaCh2ZWN0b3IxLCB2ZWN0b3IyKWDov5Tlm57nrKzkuIDkuKrlkJHph4/kuK3lkITlhYPntKDlnKjnrKzkuozkuKrlkJHph4/kuK3nmoTkvY3nva7luo/liJfvvIzmr4/kuIDkuKrpg73ku47lpLTlvIDlp4vljLnphY3vvIzlj6ropoHljLnphY3liLDlsLHnu5PmnZ/jgIINCg0KLSAgIGBwbWF0Y2godmVjdG9yMSwgdmVjdG9yMilg77yM6Iul56ys5LiA5Liq5ZCR6YeP5Lit5p+Q5YWD57Sg5Zyo56ys5LqM5Liq5ZCR6YeP5Lit5Yy56YWN5LiK5LqG77yM5YiZ6K6k5Li656ys5LqM5Liq5ZCR6YeP5Lit55u45bqU5YWD57Sg5bey6KKr5Y2g55So77yM5YaN5Yy56YWN56ys5LiA5Liq5ZCR6YeP5Lit55qE5LiL5LiA5Liq5YWD57Sg5pe277yM6Lez6L+H56ys5LqM5Liq5ZCR6YeP5Lit5pu+6KKr5Yy56YWN6L+H55qE5YWD57Sg44CCDQoNCmBgYHtyfQ0KbWF0Y2gocmVwKDEsIDMpLCByZXAoMSwgNSkpDQpwbWF0Y2gocmVwKDEsIDMpLCByZXAoMSwgNSkpDQpgYGANCg0KDQoNCiMjIyBPcGVyYXRpb24NCg0KUiDlr7nlkJHph4/nmoTmk43kvZzpg73opoHlnKjljp/lkJHph4/nmoTmi7fotJ3kuIrov5vooYzvvIzkuI3mlLnlj5jljp/lkJHph4/vvIjlj6rmnInnm7TmjqXotYvlgLzog73mlLnlj5jljp/lkJHph4/vvInjgILlpb3lpITmmK/lronlhajvvIzlnY/lpITmmK/mhaLjgIINCg0KDQojIyMjIOWinuWIoOaUuQ0KDQotICAgYGFwcGVuZCgpYCDov5Tlm57mj5LlhaXlhYPntKDlkI7nmoTmi7fotJ0uIDxzcGFuIHN0eWxlPSdjb2xvcjpyZWQnPioq5rOo5oSP77yMUiDkuK3kuIDoiKzkuI3nm7TmjqXkv67mlLnljp/lr7nosaHvvIzmiYDku6XlpoLmnpzopoHkv53nlZnov5nkuKrmk43kvZzvvIzkuIDlrpropoHorrDlvpfotYvlgLznu5nljp/lj5jph4/jgIIqKjwvc3Bhbj4NCi0gICBgeCA8LSB4Wy1pbmRleF1gIOWIoOmZpOmAiembhg0KLSAgIGByZXBsYWNlKClgIOaJuemHj+abv+aNog0KDQpgYGB7cn0NCnggPC0gYygxLCAyLCAzLCA0LCA1LCA3KQ0KeCA8LSByZXBsYWNlKHgsIGMoMywgNCksIGMoNSwgNSkpICMg5pu/5o2i77ya5bCGeOWQkemHj+S4reesrDPkuKrlkoznrKw05Liq5YWD57Sg5pu/5o2i5Li6NQ0KeA0KDQphcHBlbmQoeCwgMTAwLCBhZnRlciA9IDIpDQp4DQoNCmEgPC0gYygwLCAwLCAxLCAyLCAwLCAzKSAjIOWIoOmZpOmbtuWAvOmhuQ0KYiA8LSB3aGljaChhID09IDApDQpiDQphIDwtIGFbLWJdDQphDQpgYGANCg0KIyMjIyBTb3J0DQoNCi0gYHJhbmsoeClg6L+U5ZueeOS4reWQhOWFg+e0oOebuOWvueWkp+Wwj+eahOaOkuW6j++8iOm7mOiupOWNh+W6j++8iQ0KLSBgb3JkZXIoeClg6L+U5Zue5oyJ54Wn6YCS5aKeL+mAkuWHj+eahOasoeW6j+mHjeaOkuWIl3jlkI7vvIzmlrDluo/liJflkITlhYPntKDlnKjljp/luo/liJd45Lit55qE5L2N572uDQotIGBzb3J0KHgpYOi/lOWbnuaOkuW6j+WQjueahOWQkemHj++8jOWvueS6juaVsOWtl+m7mOiupOWNh+W6j++8jOWvueS6juWtl+espuS4sum7mOiupOWtl+WFuOmhuuW6jw0KLSBgcmV2KHgpYOmAhuW6j+aOkuWIlw0KDQpgYGB7cn0NCngNCnJhbmsoeCkNCg0Kc29ydCh4KSAjIOm7mOiupOWNh+W6j+aOkuW6jw0KeA0KDQpyZXYoeCkgIyDpgIbluo8NCngNCmBgYA0KDQojIyMjIOWOu+mHjeOAgeWBj+enu+OAgeW3ruWIhg0KDQotICAgYHVuaXF1ZSgpYCDljrvph40NCi0gICBgbGVhZCh4LCBuPTEpYCwgYGxhZyh4LCBuPTEpYCDlgY/np7vvvIzov5Tlm57kuIDkuKrluo/liJfnmoTpooblhYjluo/liJflkozmu57lkI7luo/liJcNCi0gICBgZGlmZih4LCBsYWc9MSlgIOW3ruWIhu+8jGxhZyDnlKjku6XmjIflrprmu57lkI7lh6DpobnjgILpu5jorqTnmoQgbGFnIOWAvOS4uiAxDQoNCmBgYHtyfQ0KdW5pcXVlKHgpDQp4DQoNCmxlYWQoeCwgMikNCngNCg0KZGlmZih4KQ0KeA0KYGBgDQoNCiMjIyMg57uf6K6h5p2h5Lu26aKR5pWwDQoNCmBzdW0odmVjdG9y5Lit5YWD57Sg6KaB56ym5ZCI55qE5p2h5Lu2KWANCg0KYGBge3J9DQojIDEg55qE5Liq5pWwDQp2ZWN0b3IgPC0gYygxLCAtMSwgMiwgLTIsIDMsIDQsIDAsIDIsIDMsIDQsIDEsIDEpDQpzdW0odmVjdG9yID09IDEpDQpgYGANCg0KIyMjIyDkuqTlj4nlvqrnjq/pgY3ljoYNCg0KYGV4cGFuZC5ncmlkKC4uLiwgc3RyaW5nc0FzRmFjdG9ycyA9IFRSVUUpYO+8jC4uLuWPguaVsOS4uuiLpeW5suS4quWQkemHj++8jOavlOWmguaVsOWAvOWQkemHjyB4IOWSjCB577yM5pWI5p6c5Li6Kirlr7nkuozlhYPmlbDlgLzlr7kgKHgsIHkpIOaJgOacieWPr+iDveWPluWAvOeahOmBjeWOhu+8iOi/lOWbnuaVsOaNruahhu+8iSoq44CCDQoNCuiLpSAuLi4g5Li65a2X56ym5Liy5ZCR6YeP77yM5pyA5aW95bCGIHN0cmluZ3NBc0ZhY3RvcnMg5Y+C5pWw6K6+572u5Li6IFRSVUUNCg0KYGBge3J9DQp4IDwtIDE6Mw0KeSA8LSBsZXR0ZXJzWzE6M10NCnogPC0gMTozDQpleHBhbmQuZ3JpZCh4LCB5KQ0KZXhwYW5kLmdyaWQoeCwgeikNCmBgYA0KDQojIyMjIOeul+aVsOi/kOeulw0KDQrlkJHph4/ljJbov5DnrpfvvJrlr7nlupTlhYPntKDliIbliKvov5DnrpfvvIzovoPnn63nmoTlkJHph4/oh6rliqjlvqrnjq/nm7Toh7Pplb/luqbkuI7ovoPplb/lkJHph4/ljLnphY0NCg0K5ZCR6YeP5YWD57Sg55qEIG5hbWVzIOS4jeWPguS4jui/kOeul++8jOS9hui/kOeul+espuW3puS+p+WQkemHj+eahCBuYW1lcyDkvJrkv53nlZnlnKjnu5PmnpzkuK3vvIzlj7PkvqflkJHph4/nmoQgbmFtZXMg6KKr5b+955WlDQoNCmBgYHtyfQ0KIyMg5Zub5YiZ6L+Q566XKCstKi8p44CB5bmCKF4p44CB5Y+W5pW0KCUvJSnjgIHlj5bkvZkoJSUpDQoxIC8gKDE6NikgIyDoh6rliqjlsIbmoIfph4/vvIjplb/luqbkuLogMSDnmoTlkJHph4/vvInmianlsZXkuLrplb/luqbkuLogNiDnmoTlkJHph4/lkI7lho3ov5DnrpcNCjEgLyBtYXRyaXgoMTo0LCBucm93ID0gMikgIyDoh6rliqjlsIbmoIfph4/mianlsZXkuLrlr7nlupTnu7TluqbnmoTnn6npmLXlkI7lho3ov5DnrpcNCg0KeCA8LSAxOjUNCnkgPC0gMjo2DQp4ICogeSAjIOWTiOi+vumprOenrw0KDQoNCiMjIOWQkemHj+WGheenr++8iOS7peS4i+WGmeazleetieS7t++8iQ0KMSAqIDIgKyAyICogMyArIDMgKiA0ICsgNCAqIDUgKyA1ICogNg0Kc3VtKHggKiB5KQ0KdCh4KSAlKiUgeSAjIOWIqeeUqOefqemYteS5mOazle+8jOazqOaEjyBSIOS4reWQkemHj+m7mOiupOS4uuWIl+WQkemHjw0KY3Jvc3Nwcm9kKHgsIHkpICMg5Yip55So55+p6Zi15LmY5rOV77yM5b6X5Yiw5LiA5LiqIDEqMSDnn6npmLUNCg0KDQojIyDlkJHph4/lpJbnp6/vvIjku6XkuIvlhpnms5XnrYnku7fvvIkNCnggJSolIHQoeSkNCnggJW8lIHkNCmtyb25lY2tlcih4LCB0KHkpKSAjIOWFi+e9l+WGheWFi+enrw0Kb3V0ZXIoeCwgeSkNCnRjcm9zc3Byb2QoeCwgeSkNCmBgYA0KDQojIyMjIOmAu+i+kei/kOeulw0KDQpgYGB7cn0NCnggPC0gMTo2DQp4IDwgNA0KYWxsKHggPiA1KSAjIOS4gOS4quWQkemHj+eahOaJgOacieWFg+e0oOaYr+WQpua7oei2s+afkOadoeS7tg0KYW55KHggPiA1KSAjIOS4gOS4quWQkemHj+eahOmDqOWIhuWFg+e0oOaYr+WQpua7oei2s+afkOadoeS7tg0KDQptYXRyaXgoeCwgMiwgYnlyb3cgPSBUKSA8IDQNCmBgYA0KDQojIyMjIOmbhuWQiOi/kOeulw0KDQpgYGB7cn0NCmludGVyc2VjdChjKDEsIDIsIDMsIDMsIDEyLCA0LCAxMjMsIDEyKSwgYygxLCAyLCAzKSkgIyDkuqQNCg0KdW5pb24oYygi54uX54aK5LyaIiwgIuiBmuaVsOaNruiLseaJjSIpLCBjKCLni5fnhorkvJoiLCAi5Yqp5Lqn5Lia5oyv5YW0IikpICMg5bm2DQoNCnNldGRpZmYoMTA6MiwgNTozKSAjIOW3rg0KYGBgDQoNCiMjIE1hdHJpeA0KDQojIyMgQ3JlYXRlDQoNCiMjIyMg5ZCR6YeP6L2s5YyW5Li655+p6Zi1DQoNCjEuIGBtYXRyaXgodmVjdG9yLCBucm93LCBuY29sLCBieXJvdyA9IEZBTFNFLCBkaW1uYW1lcyA9IE5VTEwpYO+8jOm7mOiupOaMieWIl+Whq+WFheefqemYte+8jGJ5cm93IOiuvuS4uiBUUlVFIOWPr+aMieihjA0KDQoyLiAg5Li65ZCR6YeP5re75Yqg57u05bqm5bGe5oCnIGBkaW0odmVjdG9yKSA8LSBjKG5yb3csbmNvbClgDQoNCmBgYHtyfQ0KIyMg55Sf5oiQIDMqMyDnmoTlhaggMSDnn6npmLUNCg0KIyDnrKzkuIDnp40NCnJlcCgxLCA5KSAlPiUgbWF0cml4KG5yb3cgPSAzKQ0KDQojIOesrOS6jOenjQ0KeCA8LSByZXAoMSwgOSkNCmRpbSh4KSA8LSBjKDMsIDMpDQp4ICMg6L+Z56eN5pa55byP5pS55Y+Y5LqG5Y+Y6YePIHgg55qE5YC877yM5LiN5piv5b6I5o6o6I2Q77yM6L+Y5piv56ys5LiA56eN5pu05aW9DQpgYGANCg0KIyMjIyBgZGlhZygpYA0KDQoxLiBgZGlhZyh4ID0gMSwgbnJvdywgbmNvbCwgbmFtZXMgPSBUUlVFKWANCiAgIDEuIHgg5Li65qCH6YeP5oiW5ZCR6YeP5pe277yMYGRpYWcoKWDooajnpLrmoLnmja7lr7nop5LlhYMgY3JlYXRlIOefqemYtQ0KICAgMi4geCDkuLrnn6npmLXml7bvvIxgZGlhZygpYOS4uuaPkOWPluWvueinkuWFg++8jOi/lOWbnuWQkemHjw0KICAgMy4geCDkuLrmoIfph4/kuJTlj6rmnInov5nkuIDkuKrlj4LmlbDml7bvvIzov5Tlm54gJG4kIOmYtuWNleS9jemYtSAkSV9uJA0KMi4gYGRpYWcoeCkgPC0gdmFsdWVgLCB2YWx1ZSDlj6/ku6XmmK/moIfph4/miJblkJHph48NCg0KYGBge3J9DQpkaWFnKDQpICMgbiDpmLbljZXkvY3pmLUNCmRpYWcoMCwgNCkgIyDlhaggMCDlm5vpmLbmlrnpmLUNCmRpYWcoMTAsIDMsIDQpICMgbnJvdyDlkowgbmNvbCDlj6/ku6XkuI3nm7jnrYkNCmRpYWcoMCwgMywgNCkgIyDku7vmhI8gc2l6ZSDnmoTlhaggMCDnn6npmLUNCmRpYWcoMCwgMywgNCkgKyAxICMg5Lu75oSPIHNpemUg55qE5YWoIDEg55+p6Zi1DQptYXRyaXgobnVtZXJpYygxMiksIG5yb3cgPSAzKSArIDEgIyDku7vmhI8gc2l6ZSDlhaggMSDnn6npmLXnmoTlj6bkuIDnp43lhpnms5UNCg0KeCA8LSBkaWFnKDE6NSkgIyBjcmVhdGUg5a+56KeS55+p6Zi1DQp4DQpkaWFnKHgpIDwtIDINCngNCmRpYWcoeCkgPC0gbGV0dGVyc1sxOjVdDQp4DQpgYGANCg0KIyMjIyDnn6npmLXovazljJbkuLrlkJHph48NCg0KYGFzLnZlY3RvcigpYCDmjInnhafkuIDliJfkuIDliJflnLDpobrluo/lsIbnn6npmLXlsZXlvIDkuLrlkJHph48NCg0KIyMjIFR5cGUNCg0KLSBgaXMubWF0cml4KClgIOWIpOaWrQ0KLSBgYXMubWF0cml4KHgpYCDovazmjaLvvIx45Li65pWw5o2u5qGG5pe277yM6Ieq5Yqo6L2s5o2i5Li65YW85a655omA5pyJ5YWD57Sg55qE44CB5pu054G15rS755qE5pWw5o2u57G75Z6LDQotIGBkYXRhLm1hdHJpeCh4KWAg6L2s5o2i5pWw5o2u5qGG77yM6Ieq5Yqo5bCG5omA5pyJ5YWD57Sg6L2s5o2i5Li65pWw5a2XDQoNCiMjIyBTaXplDQoNCmBucm93KEEpYCwgYG5jb2woQSlgIOS9nOeUqOS6juWQkemHj+aXtui/lOWbniBOVUxM77yM5YW25Y+Y5L2TIGBOUk9XKClgLCBgTkNPTCgpYCDlj6/kvZznlKjkuo7lkJHph4/vvIjlsIblhbbop4bkuLogJG5cdGltZXMxJCDnn6npmLXvvIkNCmBkaW0oQSlg77yM6L+U5Zue6ZW/5bqm5Li6IDIg55qE5ZCR6YePDQpgbGVuZ3RoKEEpYCDov5Tlm57nn6npmLXlhYPntKDnmoTkuKrmlbDvvIjnn6npmLXlsZXlvIDkuLrlkJHph4/nmoQgc2l6Ze+8iQ0KDQojIyMgTGFiZWwNCg0K5Zyo6buY6K6k5oOF5Ya15LiL77yM5Yib5bu655+p6Zi15pe25LiN5Lya6Ieq5Yqo5YiG6YWN6KGM5ZCN5ZKM5YiX5ZCN44CC5b2T5LiN5ZCM55qE6KGM5YiX5pyJ5LiN5ZCM55qE5ZCr5LmJ5pe277yMDQrkuLrlhbblkb3lkI3lsLHmmL7lvpflv4XopoHkuJTnm7Top4LjgIINCg0KIyMjIyDliJvlu7rnn6npmLXml7bkuLrooYzliJflkb3lkI0NCg0KYG1hdHJpeCgpYCDnmoQgZGltbmFtZXMg5Y+C5pWw77yM5Y+W5YC85Li6IGxpc3Qocm93X25hbWVfdmVjdG9yLCBjb2xfbmFtZV92ZWN0b3IpDQoNCiMjIyMg5Yib5bu655+p6Zi15ZCO5Li66KGM5YiX5ZG95ZCNDQoNCmByb3duYW1lcyhBKSBbPC1dYCDmj5Dlj5Yv6LWL5YC8DQoNCmBjb2xuYW1lcyhBKSBbPC1dYCDmj5Dlj5Yv6LWL5YC8DQoNCiMjIyBTdWJzZXQNCg0KIyMjIyDkuoznu7TpgInmi6nlmagNCg0KYFvooYzpgInmi6nlmagsIOWIl+mAieaLqeWZqF1gIOavj+S4qumAieaLqeWZqOmDveWPr+S7peaYr2luZGV444CBY29uZGl0aW9uIOWSjCBuYW1l77yb6Iul5LiA5Liq57u05bqm55qE5Y+C5pWw56m657y677yM5YiZ6K+l57u05bqm55qE5omA5pyJ5YC86YO95Lya6KKr6YCJ5Ye65p2lDQoNCioq55+p6Zi155qEYFtdYOmAieaLqeWZqOS8mui/lOWbnue7tOW6puWwveWPr+iDveS9jueahOe7k+aenCoq77yM5Zug5q2kYEFbMiwgXWDov5Tlm57nmoTmmK/lkJHph4/ogIzpnZ4kMVx0aW1lcyBuJOeahOefqemYteOAguWmguaenOS4jeaDs+mZjee7tO+8iOWNs+S9v+mAieaLqeS4gOWIl+S5n+imgeS/neaMgSBjbGFzcyDlsZ7mgKfkuLrnn6npmLXvvInvvIzpnIDopoHorr7nva4gZHJvcCDlj4LmlbAgYFvooYzpgInmi6nlmagsIOWIl+mAieaLqeWZqCwgZHJvcCA9IEZBTFNFXWANCg0KYGBge3J9DQpBIDwtIG1hdHJpeCgxOjE2LCA0KQ0KDQpBWzIsIF0gIyBB55qE56ysMuihjA0KDQpBWywgLTJdICMgQeaOkumZpOesrDLliJcNCg0KQVszLCAyXSAjIEHnmoTnrKwz6KGM56ysMuWIlw0KDQpBWzMsIGMoMiwgNCldICMgQeeahOesrDPooYznrKwy5YiX5ZKM56ysNOWIlw0KYGBgDQoNCiMjIyMg5LiA57u06YCJ5oup5ZmoDQoNCuefqemYteacrOi0qOS4iuaYr+S4gOS4quWQkemHj++8jOaJgOS7pemAgueUqOWQkemHj+eahOS4gOe7tOmAieaLqeWZqA0KDQpgYGB7cn0NCkEgPC0gbWF0cml4KDE6OSwgMykNCkFbQSA+IDNdDQpgYGANCg0KIyMjIyDnn6npmLXpgInmi6nlmagNCg0K5L2/55So55+p6Zi15L2c5Li66YCJ5oup5Zmo77yM55+p6Zi155qE5q+P5LiA6KGM5o+Q5L6b5aSa57u0IGluZGV477yM6YCJ5oup5LiA5Liq5YWD57Sg77yb5YiX5pWw5Yaz5a6a6YCJ5oup5YWD57Sg55qE5Liq5pWwDQoNCmBgYHtyfQ0KdmFscyA8LSBvdXRlcigxOjUsIDE6NSwgRlVOID0gInBhc3RlIiwgc2VwID0gIiwiKQ0KdmFscw0Kc2VsZWN0IDwtIG1hdHJpeChuY29sID0gMiwgYnlyb3cgPSBUUlVFLCBjKA0KICAxLCAxLA0KICAzLCAxLA0KICAyLCA0DQopKQ0Kc2VsZWN0DQp2YWxzW3NlbGVjdF0NCmBgYA0KDQojIyMgSW5kZXgNCg0KYHJvdyhBKWAsIGBjb2woQSlgIOi/lOWbnuS4pOS4quefqemYte+8jOWFg+e0oOWIhuWIq+S4uuihjOWIl+S4i+agh+OAgg0KDQpgYGB7cn0NCkEgPC0gbWF0cml4KDE6MTYsIDQpDQpBDQpyb3coQSkNCmNvbChBKQ0KDQojIOaPkOWPluS4i+S4ieinkuefqemYteeul+azlQ0KQVtyb3coQSkgPCBjb2woQSldIDwtIDANCkENCmBgYA0KDQojIyMgT3BlcmF0aW9uDQoNCiMjIyMg6L2s572uIGB0KEEpYA0KDQrlkJHph4/msqHmnInnu7TluqblsZ7mgKfvvIzkuI3mmK/nn6npmLXjgILopoHmiorlkJHph4/ovazljJbkuLogJDEgXHRpbWVzIG4kIOWSjCAkbiBcdGltZXMgMSQg55qE55+p6Zi177yM5Y+v5Lul55SoIGBkaW0oKWDvvIzkuZ/lj6/ku6XnlKggYHQoKWAsIGBhcy5tYXRyaXgoKWANCg0KYGBge3J9DQp4IDwtIDE6Ng0KDQphcy5tYXRyaXgoeCkgIyDmnIDnm7Top4INCnggJT4lIGBkaW08LWAoYyg2LCAxKSkNCnQodCh4KSkNCg0KeCAlPiUgYGRpbTwtYChjKDEsIDYpKQ0KdCh4KQ0KYGBgDQoNCuWvueaVsOaNruahhuS9v+eUqCBgdCgpYO+8jOS8muiHquWKqOWwhuWFtui9rOaNouS4uuefqemYtQ0KDQojIyMjIOefqemYteS5mOazlQ0KDQotICAgYEElKiVCYA0KICAgIC0gICDkuIDkuKrkvovlpJbvvJroi6UgQSDlkowgQiDlnYfkuLrliJflkJHph4/vvIzliJnov5Dnrpfml7boh6rliqjlr7kgQSDovaznva5bXui9rOe9rl3vvIzmiafooYwgYHQoQSklKiVCYO+8jOi/lOWbniBBIOS4jiBCIOeahOWGheenrw0KICAgIC0gYGNyb3NzcHJvZChBLCBCKWAg5pivIGB0KEEpICUqJSBCYCDnmoTnroDlhpkNCiAgICAtIGB0Y3Jvc3Nwcm9kKEEsIEIpYCDmmK8gYEEgJSolIHQoQilgIOeahOeugOWGmQ0KLSBgKmAsIOWTiOi+vumprOenrw0KLSBga3JvbmVja2VyKClgLCDlhYvnvZflhoXlhYvnp68NCg0KDQpgYGB7cn0NCiMg55+p6Zi15LmY5rOVDQptMSA8LSBtYXRyaXgoMTo5LCAzKQ0KbTEgJSolIG0xDQpgYGANCg0KW17ovaznva5dOiDoi6XkuI3ovaznva7vvIzov5nkuKrov5DnrpfmoLnmnKzml6Dms5Xov5vooYzjgIINCg0KIyMjIyDlr7nop5LlhYPntKANCg0KYGRpYWcoQSlgIOi/lOWbnuWvueinkuWFg+e0oOe7hOaIkOeahOWQkemHjw0KDQojIyMjIOefqemYteeahOmAhu+8m+e6v+aAp+aWueeoi+e7hOeahOinow0KDQotICAgYHNvbHZlKEEsIEIpYCwg6L+U5ZueYEEgJSolIFggPSBCYOeahOino+OAgg0KLSAgIGBzb2x2ZShBKWAsIOi/lOWbnuefqemYtUHnmoTpgIbvvIzljbNgU29sdmUoQSwgSSlgDQoNCiMjIyMg54m55b6B5YC85ZKM54m55b6B5ZCR6YePDQoNCmBBLmVpZ2VuIDwtIGVpZ2VuKEEsIHN5bW1ldHJpYz1UKWANCg0KIyMjIyDooYzliJflvI8NCg0KYGRldChBKWANCg0KIyMjIyDmj5Dlj5bkuIrjgIHkuIvkuInop5Lnn6npmLUNCg0KYGxvd2VyLnRyaShBLGRpYWc9VC9GKWDlkoxgdXBwZXIudHJpKEEsZGlhZz1UL0YpYO+8jOi/meS4pOS4quWHveaVsOi/lOWbnumAu+i+keefqemYtQ0KDQpgYGB7cn0NCkEgPC0gbWF0cml4KDE6MTYsIDQpDQpBDQpBW2xvd2VyLnRyaShBKV0gPC0gMCAjIEFbbG93ZXIudHJpKEEpXSDkuLrkuIDnu7TpgInmi6nlmajvvIzov5Tlm57lkJHph4/ogIzpnZ7nn6npmLUNCkENCmBgYA0KDQojIyMjIOato+WumuefqemYteeahENob2xlc2tleeWIhuinow0KDQpgY2hvbChBKWANCg0K5Y2z6L+U5Zue5ruh6LazICRBPVB7XlR9UCQg55qEUO+8jOWFtuS4rVDkuLrkuIrkuInop5Lnn6npmLUNCg0KIyMjIyDnn6npmLXlkIjlubYNCg0KYHJiaW5kKClgLCBgY2JpbmQoKWANCg0KYGBge3IsIGV2YWw9RkFMU0V9DQpyYmluZChBLCBCKQ0KY2JpbmQoQSwgQikNCmRwbHlyOjpiaW5kX3Jvd3MoQSwgQikNCmRwbHlyOjpiaW5kX2NvbHMoQSwgQikNCmBgYA0KDQojIyMjIOWvueihjOOAgeWIl+WIhuWIq+aTjeS9nA0KDQotIGBhcHBseShtYXRyaXgsIDEsIGYpYCDlsIbooYzkvp3mrKHkvKDpgJLnu5kgZg0KLSBgYXBwbHkobWF0cml4LCAyLCBmKWAg5bCG5YiX5L6d5qyh5Lyg6YCS57uZIGYNCi0gYHJvd1N1bXMoKWANCi0gYHJvd01lYW5zKClgDQotIGBjb2xTdW1zKClgDQotIGBjb2xNZWFucygpYA0KDQpgYGB7cn0NCkEgPC0gbWF0cml4KDE6MTYsIDQpDQpBDQoNCnJvd1N1bXMoQSkNCmFwcGx5KEEsIDEsIHN1bSkNCg0KY29sTWVhbnMoQSkNCmFwcGx5KEEsIDIsIG1lYW4pDQpgYGANCg0KIyMgQXJyYXkNCg0K5byg6YeP77yM5oiW5pu06auY57u05bqm55qE5ZCR6YePDQoNCiMjIyBDcmVhdGUNCg0KYHo8LWFycmF5KHZlY3RvciwgZGltZW5zaW9ucywgZGltbmFtZXMgPSBsaXN0KHYxLCB2MiwgdjMsIC4uLikpYA0KDQojIyMgTGFiZWwNCg0KYGRpbW5hbWVzKCkgWzwtXWAg5o+Q5Y+WL+i1i+WAvA0KDQpgYGB7cn0NCnogPC0gYXJyYXkoMToyNCwgZGltID0gYygyLCAzLCA0KSwgZGltbmFtZXMgPSBsaXN0KGMoInIxIiwgInIyIiksIGMoImMxIiwgImMyIiwgImMzIiksIGMoInAxIiwgInAyIiwgInAzIiwgInA0IikpKQ0Keg0KZGltbmFtZXMoeikNCmBgYA0KDQojIyMgU3Vic2V0DQoNCmBb56ysMee7tOmAieaLqeWZqCwg56ysMue7tOmAieaLqeWZqCwg56ysM+e7tOmAieaLqeWZqCwgLi4uXWANCg0KIyMgTGlzdA0KDQpsaXN0IOmAu+i+keS4iuaYr+S4gOe7hDxzcGFuIHN0eWxlID0gJ2NvbG9yOiBvcmFuZ2VyZWQnPioq5pyJ5bqPPC9zcGFuPumUruWAvOWvuSoq55qE6ZuG5ZCI77yI5bCx5YOPIEpTIOS4reeahOWvueixoe+8iQ0KDQrkvYblrp7njrDkuIrmmK/lub/kuYnlkJHph4/vvIjkvZzkuLogdmFsdWVz77yJ6ZmE5Yqg5LqGIGluZGV4IOaIliBuYW1lcyDlsZ7mgKfvvIjkvZzkuLoga2V5c++8iQ0KDQojIyMgQ3JlYXRlDQoNCiMjIyMg55u05o6l5a6a5LmJ5YiX6KGo5YaF5a65DQoNCmBsaXN0KG5hbWUxID0gdmFsdWUxLCBuYW1lMiA9IHZhbHVlMiwgLi4uKWDvvIzlj6/ku6XnnIHnlaUgbmFtZXMNCg0KIyMjIyDplb/luqbkuLogTiDnmoTnqbrliJfooagNCg0KYHZlY3Rvcihtb2RlID0gJ2xpc3QnLCBsZW5ndGggPSBOKWANCg0KUiDkuK3nmoQgdmVjdG9yIOWkqeeEtuS9k+eOsOedgOWQkemHj+eahOKAnOW5v+S5ieKAnQ0KDQojIyMgVHlwZQ0KDQotIGBpcy5saXN0KClgIOWIpOaWrQ0KLSBgYXMubGlzdCgpYCDovazmjaINCiAgIC0gYGFzLnZlY3RvcigpYOWvuWxpc3Tml6DmlYjvvIzlm6DkuLogbGlzdCDmmK/lub/kuYnlkJHph4/vvIwidmVjdG9yIuWMheWQq+S6hiJsaXN0Ig0KLSBgdW5saXN0KClgIOWwhuWIl+ihqOW8uuWItui9rOaNouS4uuWQkemHj+OAgioq55Sx5LqO6KeE5YiZ6L+H5LqO5aSN5p2C77yM5pyA5aW95LiN6KaB5L2/55So6K+l5Ye95pWwKioNCiAgIC0g5oyJ5YiG6YeP6aG65bqP5L6d5qyh5o6S5YiX77yMKirlvLrliLbkuI3lho3ltYzlpZcqKu+8gQ0KICAgLSDoh6rliqjkv53or4HlkIzmoLfnmoTmlbDmja7nsbvlnovjgILmr5TlpoLvvIzlvLrliLbovazmjaLmt7flkIjkuobmlbDlgLzlkozmlofmnKznmoTliJfooajvvIzkvJrlvpfliLDlrZfnrKbkuLLlkJHph48NCg0KYGBge3J9DQpsIDwtIGxpc3QoMSwgMiwgMykNCmwNCmFzLnZlY3RvcihsKQ0KYXMuaW50ZWdlcihsKQ0KdW5saXN0KGwpDQpgYGANCg0KIyMjIFNpemUNCg0KYGRpbShsaXN0KTwtYOWPr+S7peWIm+W7uuWIl+ihqOefqemYtQ0KDQpgYGB7cn0NCmwgPC0gbGlzdCgxOjMsICJhIiwgVFJVRSwgMS4wKQ0KZGltKGwpIDwtIGMoMiwgMikNCmwNCmxbWzEsIDFdXQ0KYGBgDQoNCiMjIyBMYWJlbA0KDQpgbmFtZXMobGlzdCkgWzwtXWAg5o+Q5Y+WL+i1i+WAvA0KDQrotYvlgLzkuLogTlVMTCDml7bnp7vpmaQgbmFtZXMg5bGe5oCn77yM5oiW6YCa6L+HIGB4IDwtIHVubmFtZSh4KWANCg0KIyMjIFN1YnNldA0KDQrnrKzkuIDnp43or63ms5XvvIznlKhgW11g56ym5Y+377yM6YeM6Z2i5Y+v5Lul5pivIGluZGV4L2NvbmRpdGlvbi9rZXkNCg0K56ys5LqM56eN6K+t5rOV77yM55SoIFxgW1xgKCkgIOaIliBgbWFncml0dHI6OmV4dHJhY3QoKWDlh73mlbANCg0KYGBge3J9DQpsIDwtIGxpc3QoImEiLCAyLCBUUlVFKQ0KbFsxXQ0KYFtgKGwsIDEpDQptYWdyaXR0cjo6ZXh0cmFjdChsLCAxKQ0KYGBgDQoNCuS4i+agh+i2iueVjOaXtu+8jOi/lOWbniBOVUxMDQoNCiMjIyBFeHRyYWN0DQoNCi0g56ys5LiA56eN6K+t5rOV77yM55SoYFtbXV1g56ym5Y+377yM6YeM6Z2i5Y+v5Lul5pivIGluZGV4L2NvbmRpdGlvbihsb2dpY2FsIHZlY3Rvcikva2V5DQotIOesrOS6jOenjeivreazle+8jOeUqGBsaXN0JGtleWDvvIzmmK9gbGlzdFtbJ2tleSddXWDnmoTor63ms5Xns5YNCiAgIC0g6IulICJrZXkiIOWCqOWtmOWcqOS4gOS4quWPmOmHjyB2YXIg5Lit77yMYGRmJHZhcmDnrYnku7fkuo5gZGZbWyJ2YXIiXV1g77yM5piv5peg5pWI55qE77yM5q2k5pe25Y+q5pyJYGRmW1t2YXJdXWDmiY3og73nlJ/mlYjjgILmiYDku6XvvIxgZGZbWyJrZXkiXV1g5oC75piv5q+UYGRmJGtleWDmm7Tkv53pmankuIDkupsNCiAgIC0gYCRg6IO96YOo5YiG5Yy56YWN5Y+Y6YeP5ZCN77yM6ICMYFtbXV1g5oC75piv5a6M5YWo57K+5YeG5Zyw5Yy56YWNDQotIOesrOS4ieenjeivreazle+8jOeUqCBcYFtbXGAoKSAg5oiWIGBtYWdyaXR0cjo6ZXh0cmFjdDIoKWDlh73mlbANCi0g56ys5Zub56eN6K+t5rOV77yM55SoYHB1cnJyOjpwbHVjaygueCwgLi4uLCAuZGVmYXVsdCA9IE5VTEwpYOaIlmBwdXJycjo6Y2h1Y2soLngsIC4uLilgDQogICAtIC4uLuWPr+S7peaYr+eUqOmAl+WPt+mXtOmalOeahOWkmuS4qm5hbWUvaW5kZXjvvIzpgILnlKjkuo7lr7nmt7HluqbltYzlpZfliJfooajvvIgqKuivu+WPliBKU09OIOaWh+S7tuaXtuW+iOW4uOingSoq77yJ55qE5o+Q5Y+WDQogICAtIOimgeaQnOe0oueahOWFg+e0oOS4jeWtmOWcqOaXtu+8jHBsdWNrKCkg6buY6K6k6L+U5ZueIE5VTEzvvIzogIwgY2h1Y2soKSDkvJrmipvlh7rplJnor68NCg0KYGBge3J9DQpsW1sxXV0NCmBbW2AobCwgMSkNCm1hZ3JpdHRyOjpleHRyYWN0MihsLCAxKQ0KDQoNCnggPC0gbGlzdCgNCiAgYSA9IGxpc3QoMSwgMiwgMyksDQogIGIgPSBsaXN0KDMsIDQsIDUpDQopDQpwdXJycjo6cGx1Y2soeCwgImEiLCAxKQ0KcHVycnI6OnBsdWNrKHgsICJjIiwgMSkNCnB1cnJyOjpwbHVjayh4LCAiYyIsIDEsIC5kZWZhdWx0ID0gTkEpDQpgYGANCg0KIyMjIE9wZXJhdGlvbg0KDQojIyMjIOaUueWPmOWFg+e0oA0KDQrlr7nnm7jlupTlhYPntKDnm7TmjqXotYvlgLwNCg0KIyMjIyDliKDpmaTlhYPntKANCg0K6LWL5YC85Li6IGBOVUxMYCDljbPkuLrliKDpmaQNCg0KIyMjIyDmt7vliqDlhYPntKANCg0K56ys5LiA56eN6K+t5rOV77yM5a+55paw5YWD57Sg6LWL5YC844CCDQoNCmBgYFINCiMg5re75Yqg5YWD57Sg55qE5pyA5L2z5YaZ5rOV77yM5peg6ZyA55+l6YGTIGluZGV4DQpsIDwtIGxpc3QoLi4uKQ0KbFtbbGVuZ3RoKGwpICsgMV1dIDwtIG5ld19lbGVtZW50DQpgYGANCg0K56ys5LqM56eN6K+t5rOV77yMYGFwcGVuZChsaXN0LCB2YWx1ZSwgYWZ0ZXIgPSBsZW5ndGgobGlzdCkpYO+8jOm7mOiupOWKoOWIsOWIl+ihqOeahOacq+WwvuOAguS9huiLpeaPkuWFpeeahOWFg+e0oOacrOi6q+aYr+S4gOS4quWIl+ihqO+8iOW+iOWkmuWvueixoeacrOi0qOS4iumDveaYr+S4gOS4quWIl+ihqO+8ie+8jOWImeS8mioq56C05Z2P5bGC57qn57uT5p6E77yM5bCG5omA5pyJ55qE5YWD57Sg5L6d5qyh5o+S5YWl5Yiw5YmN5LiA5Liq5YiX6KGoKirjgILmiYDku6Xoi6XopoHkv53lrZjliJfooajnmoTlsYLnuqfnu5PmnoTvvIzov5jmmK/nrKzkuIDnp43or63ms5Xmm7Tkv53pmanjgIINCg0KYGBge3J9DQpsW1s0XV0gPC0gZGF0YS50YWJsZSh4ID0gMSwgeSA9IDIpDQpsJHogPC0gYygib25lIiwgInR3byIsICJ0aHJlZSIpDQpsDQpsIDwtIGFwcGVuZChsLCA2KQ0KbA0KbCA8LSBhcHBlbmQobCwgbGlzdCg3LCA4KSkNCmwNCmxbWzldXSA8LSBsaXN0KDksIDEwKQ0KbA0KYGBgDQoNCiMjIERhdGEuRnJhbWUNCg0KIVtdKGh0dHBzOi8vZDMzd3VicmZraTBsNjguY2xvdWRmcm9udC5uZXQvYzVmZDI1NTUwYWEyYTgyZDI0ODMwNjljMWViNGRlNmEzYTFmNzYzZi9jODIxZi9kaWFncmFtcy92ZWN0b3JzL2RhdGEtZnJhbWUtMS5wbmcpDQoNCmRhdGEuZnJhbWUg5piv6ZW/5bqm55u45ZCM55qE5YiXKirlkJHph48qKu+8iOatpOWkhOWQkemHj+aYr+W5v+S5ieeahO+8jOWNs+WPr+S7peaYryBhdG9taWMgdmVjdG9yIOaIliBsaXN077yJ57uE5oiQ55qEIExpc3QuIA0KDQpSIOWOn+eUn+aUr+aMgeWQkemHj+WMluaTjeS9nO+8jOWboOatpCBSIOaTjeS9nOaVsOaNruahhuS4reeahOWQkemHj+acieWFiOWkqeS8mOWKv++8jOivreazleeugOa0geOAgg0KDQrlkIzml7bvvIxkYXRhLmZyYW1lIOWPr+S7peWDj+efqemYteS4gOagt++8jOS7peS6jOe7tOihqOaWueW8j+WRiOeOsOOAguWboOatpO+8jCoqUiDkuK3mk43kvZznn6npmLXnmoTkuIDkupvvvIjms5vlnovvvInlh73mlbDlhbzlrrnkuoYgZGF0YS5mcmFtZSDlr7nosaEqKu+8jOWNs+WPr+S7peWDj+aTjeS9nOefqemYteS4gOagt+aTjeS9nCBkYXRhLmZyYW1lDQoNCiMjIyBDcmVhdGUNCg0KYGRhdGEuZnJhbWUodmFyMSA9IGNvbDEsIHZhcjIgPSBjb2wyLCDigKYsIHJvdy5uYW1lcyA9IE5VTEwsIGNoZWNrLm5hbWVzID0gVFJVRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKWANCg0KMS4gUiA0LjAg5LmL5YmN77yM5p6E5bu6UueahOWOn+eUn+aVsOaNruahhuaXtu+8jOWPguaVsCBzdHJpbmdzQXNGYWN0b3JzIOeahOm7mOiupOWAvOS4uiBUUlVF77yM5a2X56ym5ZCR6YeP6buY6K6k6KKr6L2s5o2i5oiQ5Zug5a2Q77yI5Zug5a2Q55qEIGxldmVsIOebuOavlOWtl+espuS4suiKguecgeWCqOWtmOepuumXtO+8ie+8jOaVsOaNruexu+Wei+WPmOS4uiBudW1lcmlj77yM5peg5rOV5YaN55So5a2X56ym5Liy5Ye95pWw5aSE55CG44CCNC4wIOWQjuabtOaUueS6hui/meS4gOm7mOiupOWAvOOAgg0KDQoyLiBjaGVjay5uYW1lcyDpu5jorqTkuLogVFJVRe+8jOW7uueri+aVsOaNruahhuaXtuS8muS9v+eUqOWHveaVsCBgbWFrZS5uYW1lcygpYCDmo4Dmn6Xlj5jph4/lkI3vvIzlsIYgUiDkuI3lhYHorrjlh7rnjrDlnKjlj5jph4/lkI3kuK3nmoTnibnmrorlrZfnrKbovazmjaLkuLpgLmDvvIzlpoLlsIbliJflkI0gYFBvcHVsYXRpb24oYmVmb3JlKWAg6Ieq5Yqo6L2s5o2i5Li6IGBQb3B1bGF0aW9uLmJlZm9yZS5gDQogICAxLiDoi6XpgIkgRkFMU0XvvIzkvJrmnIDlpKfpmZDluqblnLDkv53nlZnljp/mnKznmoTliJflkI3jgILkvYborr/pl67ov5nkupvlnKggUiDkuK3mnKzkuLrpnZ7ms5XnmoTliJfml7bvvIzlv4XpobvnlKjlj43lvJXlj7flsIbliJflkI3mi6zotbfmnaXvvIzlvaLlpoIgcmVzdWx0cyRcYFBvcHVsYXRpb24oYmVmb3JlKVxgDQoNCiMjIyMgbGlzdCBjb2x1bW5zDQoNCmBgYHtyfQ0KIyDliJvlu7rmlbDmja7moYblkI7mt7vliqANCmRmIDwtIGRhdGEuZnJhbWUoeCA9IDE6MykNCmRmJHkgPC0gbGlzdCgxOjIsIDE6MywgMTo0KQ0KZGYNCg0KIyDmiJbnlKggSSgpIOWMheijue+8jOihqOekuuS4jeS8muiHquWKqOi9rOaNog0KZGF0YS5mcmFtZSgNCiAgeCA9IDE6MywgDQogIHkgPSBJKGxpc3QoMToyLCAxOjMsIDE6NCkpDQopDQpgYGANCg0KIyMjIyBNYXRyaXggYW5kIGRhdGEgZnJhbWUgY29sdW1ucw0KDQromb3nhLYgUiDlhYHorrjlsIbnn6npmLXlkowgZGF0YSBmcmFtZSDkvZzkuLrliJfvvIzkvYbmnIDlpb3kuI3opoHov5nmoLfkvb/nlKjvvIzlm6DkuLrlvojlpJrlh73mlbDpu5jorqTmr4/kuIDliJfpg73mmK/lkJHph4/vvIzkuI3lhbzlrrnov5nnp43nvZXop4HnmoTmg4XlhrUNCg0KYGBge3J9DQpkZm0gPC0gZGF0YS5mcmFtZSgNCiAgeCA9IDE6MyAqIDEwDQopDQpkZm0keSA8LSBtYXRyaXgoMTo5LCBucm93ID0gMykNCmRmbSR6IDwtIGRhdGEuZnJhbWUoYSA9IDM6MSwgYiA9IGxldHRlcnNbMTozXSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQpzdHIoZGZtKQ0KZGZtDQpgYGANCg0KIyMjIFR5cGUNCg0KLSBgYXMuZGF0YS5mcmFtZSgpYCDlsIbnn6npmLXjgIHliJfooajlvLrliLbovazmjaLkuLrmlbDmja7moYYNCiAgIC0g5aaC5p6c55+p6Zi15bey5pyJ6KGM44CB5YiX5ZCN77yM5Lya5Zyo6L2s5o2i6L+H56iL5Lit5L+d55WZDQoNCiMjIyBMYWJlbA0KDQrpgILnlKjkuo4gbWF0cml4IOeahGByb3duYW1lcygpYOWSjGBjb2xuYW1lcygpYOS5n+mAgueUqOS6juaVsOaNruahhuOAgg0KDQrkvYYqKnJvd25hbWVzIOS4jeaYr+S4gOS4quWlveeahOiuvuiuoe+8jOWujOWFqOWPr+S7peWwhuWFtuS9nOS4uuaVsOaNruahhueahOS4gOWIl++8jOWwvemHj+S4jeimgeeUqCoq44CCDQoNCmBgYHtyfQ0KYSA8LSBsaXN0KGlkID0gMTo1LCBsb3dlciA9IGxldHRlcnNbMTo1XSwgdXBwZXIgPSBMRVRURVJTWzE6NV0pDQpjbGFzcyhhKQ0KY29sbmFtZXMoYSkNCg0KYiA8LSBhcy5kYXRhLmZyYW1lKGEpDQpjbGFzcyhiKQ0KY29sbmFtZXMoYikNCmBgYA0KDQojIyMgU3Vic2V0DQoNCuaXouWPr+S7peeUqOWIl+ihqOmjjuagvOeahGBbXWDvvIzkuZ/lj6/ku6XnlKjnn6npmLXpo47moLznmoRgW3Jvd1NlbGVjdG9yLCBjb2x1bW5TZWxlY3Rvcl1gDQoNCjEuICDnn6npmLXpo47moLzpgInmi6nlmajnmoTlpb3lpITlnKjkuo7vvIzlj6/ku6XngbXmtLvlnLAqKuetm+mAieihjCoqDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZSgNCiAgaWQgPSAxOjUsDQogIGxvd2VyID0gbGV0dGVyc1sxOjVdLA0KICB1cHBlciA9IExFVFRFUlNbMTo1XQ0KKQ0KDQojIOefqemYtemjjuagvOeahOmAieaLqeWZqA0KZGZbZGYkaWQgPiAzLCBjKDIsIDMpXQ0KYGBgDQoNCjIuICDnlKjnn6npmLXpo47moLznmoTkuoznu7TpgInmi6nlmajml7bvvIzot5/nn6npmLXkuIDmoLfkvJrlsL3lj6/og73pmY3nu7QNCiAgIDEuIOWmguaenOe6teWQkeWPquWPluS6huS4gOWIl++8jOWImei/lOWbnuWAvOS8muiHquWKqOeugOWMluS4uuWQkemHj++8jOiAjOmdnuaVsOaNruahhuOAgg0KICAgMi4g6Iul6KaB6YG/5YWN6ZmN57u077yM5L+d55WZ5pWw5o2u5qGG57uT5p6E77yM5Y+v5Lul57uT5ZCI5Lik56eN6aOO5qC855qE6YCJ5oup5Zmo77yM5Lmf5Y+v5Lul5aKe5Yqg6YCJ6aG5YGRyb3AgPSBGQUxTRWDjgIINCiAgIDMuICoq5pyA5aW95piv55SoIGRwbHlyIOS4reeahOWHveaVsOabv+S7o+mAieaLqeWZqCoq77yM55SoIGBzZWxlY3QoKWAg5pe25LiN5Lya6ZmN57u077yM5aaC5p6c5biM5pyb6ZmN57u05YaN55SoIGBwdWxsKClgIOWNs+WPr++8jOivreS5ieW+iOaYjuaZsOOAgg0KDQpgYGB7cn0NCiMjIOS/neeVmeaVsOaNruahhg0KZGZbMjozLCAyLCBkcm9wID0gRkFMU0VdDQpkZlsyOjMsIF1bMl0gIyDmt7flkIjkuKTnp43pgInmi6nlmagNCmRmWzI6MywgXSAlPiUgc2VsZWN0KDIpDQoNCiMg5LiN5L+d55WZ5pWw5o2u5qGGDQpkZlsyOjMsIDJdDQpkZlsyOjMsIF0gJT4lIHNlbGVjdCgyKSAlPiUgcHVsbCgpDQpgYGANCg0KMy4gIGBzdWJzZXQoZGYsIGNvbmRpdGlvbnMsIHNlbGVjdClgDQoNCioq5Ye95pWw6ICM6Z2e5pON5L2c56ym77yM5L6/5YipIFBpcGVsaW5lIOeahOi/nue7reaTjeS9nCoqDQoNCuesrOS4gOS4quWPguaVsOS4uuaVsOaNrumbhu+8jOesrOS6jOS4quWPguaVsOmAieaLqeespuWQiOadoeS7tueahOihjO+8jOesrOS4ieS4quWPguaVsHNlbGVjdOmAieaLqeWIl++8jOWPr+S7peWcqOWIl+WQjeWJjeWKoCctJ+WIoOmZpOS5iw0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCnN1YnNldChsZWFkZXJzaGlwLCBhZ2UgPj0gMzUgfCBhZ2UgPCAyNCwgc2VsZWN0ID0gYyhxMSwgcTIsIHEzLCBxNCkpDQpgYGANCg0KIyMjIEV4dHJhY3QNCg0KLSBgZGYkY29sbmFtZWANCi0gYGRmW1tpbmRleF1dYA0KLSBgZGZbWyJjb2xuYW1lIl1dYA0KLSDnn6npmLXpo47moLzpgInmi6nlmajvvIzpgInmi6nkuIDliJcNCi0gYGRwbHlyOjpwdWxsKGRmLCBjb2xuYW1lKWANCg0KYGBge3J9DQpkZiRsb3dlcg0KDQpkZltbMl1dDQoNCmRmW1sibG93ZXIiXV0NCg0KZGZbLCAyXQ0KDQpkcGx5cjo6cHVsbChkZiwgbG93ZXIpDQpgYGANCg0KIyMjIE9wZXJhdGlvbg0KDQrlr7nmlbDmja7moYbnmoTnsr7nu4bmk43kvZzmmK8gUiDmnIDmoLjlv4PnmoTlhoXlrrnvvIzkuYvlkI7ljZXliJfkuIDnq6Dor7TmmI7jgIINCg0KIyMjIyDlop7liKDmlLkNCg0K57G75Ly8IGxpc3TjgIFtYXRyaXjvvIzku6XotYvlgLzkuLrmoLjlv4PjgIINCg0KIyMjIyDmlbDmja7moYblkIjlubYNCg0KYHJiaW5kKClgLCBgY2JpbmQoKWANCg0KIyMjIyDmj4/ov7Dnu5/orqENCg0KYHN1bW1hcnkoKWAg5o+P6L+w57uf6K6h5ZCE5YiX5pWw5o2uDQoNCmBgYHtyfQ0Kc3VtbWFyeShtdGNhcnMpDQpgYGANCg0KIyMgdGliYmxlDQoNCmRhdGEuZnJhbWUg55qE5LyY5YyW5pu/5Luj57G75Z6L77yM5L2GKirlt7LmnInmm7Tlpb3nmoTmm7/ku6PnsbvlnosgZGF0YS50YWJsZe+8jOaJgOS7pSB0aWJibGUg5LiN5YaN6YeN6KaBKioNCg0KdGliYmxlIOaJk+WNsOaXtg0KDQotIOWIl+WHuuS6huWPmOmHj+eahOexu+Weiw0KLSDlj6rmmL7npLoxMOihjA0KLSDlj6rmmL7npLrmnInpmZDnmoTliJfmlbDvvIjkuI7lsY/luZXpgILlupTnmoTvvIkNCi0g6auY5LquIGBOQXNgDQoNCiMjIyBDcmVhdGUNCg0KdGliYmxlIOacieS4gOS4quW+iOeqgeWHuueahOS8mOeCue+8muWIm+W7uuaXtu+8jOWGmeWcqOWQjumdoueahOWPmOmHj+WPr+S7peeri+WNs+S9v+eUqOWJjemdoueahOWPmOmHjw0KDQpgYGB7cn0NCiMg6L+Z56eN5YaZ5rOV5aaC5p6c55SoIGRhdGEudGFibGXvvIzkvJrmiqXplJnvvIzooajnpLrkuI3nn6XpgZMgYiDlrprkuYnlvI/kuK3nmoQgYSDmmK/ku4DkuYgNCnRpYmJsZSgNCiAgYSA9IHJ1bmlmKDEwMDAsIDAsIDUpLA0KICBiID0gNCArIHJub3JtKDEwMDAsIG1lYW4gPSAzLjIgKiBhLCBzZCA9IDEpDQopICU+JSBoZWFkKCkNCmBgYA0KDQrmlbDmja7ph4/kuI3lpKfml7bvvIzlj6/ku6XnlKhgdHJpYmJsZSgpYOWIm+W7ug0KDQpgYGB7cn0NCmRmIDwtIHRyaWJibGUoDQogIH54LCB+eSwgfnosDQogICJhIiwgMiwgMy42LA0KICAiYiIsIDEsIDguNQ0KKQ0KYGBgDQoNCiMjIyBUcmFuc2Zlcg0KDQpgYXNfdGliYmxlKClgDQoNCiMjIyBTdWJzZXQNCg0Kc3Vic2V0IOaXtu+8jOWNs+S9v+mAieWPluWNleihjOaIluWNleWIl++8jOe7k+aenOS5n+S4jeS8mumZjee7tO+8jOS7jeeEtuaYryB0aWJibGUNCg0KIyMjIE9wZXJhdGlvbg0KDQpgYGB7cn0NCmFkZF9jb2x1bW4oZGYsIHcgPSAwOjEpICMg5aKe5Yqg5LiA5YiXDQphZGRfcm93KGRmLCB4ID0gImMiLCB5ID0gOSwgeiA9IDIuMSkgIyDlop7liqDkuIDooYwNCmFkZF9yb3coZGYsIHggPSAiYyIsIHkgPSA5LCB6ID0gMi4xLCAuYmVmb3JlID0gMikgIyDlnKjnrKzkuozooYzvvIzlop7liqDkuIDooYwNCmBgYA0KDQoxLiBgcHJpbnQodGliYmxlLCBuID0gMTAsIHdpZHRoID0gSW5mKWDlj6/ku6Xoh6rlrprkuYnmiZPljbDnmoTplb/luqblkozlrr3luqbjgILpu5jorqTlj6rmmL7npLoxMOihjO+8jOmBv+WFjei+k+WHuuWkqumVvy4NCiAgIDEuIOS5n+WPr+S7peeUqGBvcHRpb25zKHRpYmJsZS5wcmludF9tYXg9biwgdGliYmxlLnByaW50X21pbj1tKWDov5vooYzlhajlsYDmgKforr7nva7vvJrlpoLmnpzlpJrkuo5t6KGM77yM5YiZ5pyA5aSa5omT5Y2w5Ye6buihjOOAgg0KMi4g5a+55LqO5b6I5aSn55qE5pWw5o2u6KGo77yM5Lmf5Y+v5Lul55SoYCU+JSBWaWV3KClg5oiWYCVUPiUgVmlldygpYOafpeeciw0KMy4gdGliYmxlIOS4jeaUr+aMgeihjOWQjeOAgmB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbihkZiwgdmFyID0gInJvd25hbWUiKWDlj6/ku6XlsIbooYzlkI3ovazljJbkuLrkuIDliJfvvIxgdGliYmxlOjpyb3dpZF90b19jb2x1bW4oZGYsIHZhciA9ICJyb3dpZCIpYOWwhuihjCBpbmRleCDovazljJbkuLrkuIDliJfjgII=