pipeline operator

命令式编程容易出现大量的中间变量,污染全局环境。

可如果写成一系列函数嵌套的形式,又会造成可读性下降

pipeline operator 能够解决这个痛点,使代码更加清晰易读

|>

这是 4.1.0 版本新发布的 base R 管道操作符。

4.2.0 为其设计了 placeholder _,但这个 placeholder 不能单独使用,必须在其出现的参数位置写上 paramter = _

mtcars |> lm(mpg ~ disp, data = _) # data = 不可省略,否则会报错
#> 
#> Call:
#> lm(formula = mpg ~ disp, data = mtcars)
#> 
#> Coefficients:
#> (Intercept)         disp  
#>    29.59985     -0.04122

|>_ 这一套的推广,需要 Rstudio 和 VSCode-R 等 IDE 中快捷键和语法检查的匹配——但它们的更新往往要慢于 R base

%>% 右向管道操作符

传递对象给右边的函数或表达式。当对象在右边函数的第一个参数时,可以省略这个参数;不是第一个参数时,则可以用.代替。如果是一元函数,甚至可以连()也省掉,只保留函数名。

管道操作符

set.seed(123)
a <- rnorm(10)
a
#>  [1] -0.56047565 -0.23017749  1.55870831  0.07050839  0.12928774  1.71506499
#>  [7]  0.46091621 -1.26506123 -0.68685285 -0.44566197
# 由此可见:`*`和`+`其实是两个函数!接收传来的数据流作为自己的第一个参数
a %>%
  `*`(5) %>%
  `+`(5)
#>  [1]  2.197622  3.849113 12.793542  5.352542  5.646439 13.575325  7.304581
#>  [8] -1.325306  1.565736  2.771690

%T>% 左向管道操作符

必须与%>%配合使用。

T管道操作符

例1:在数据处理的中间过程,需要打印输出或图片输出(如观察代码是否达到了预设的目的),这时整个过程就会被中断,用向左操作符就可以解决这样的问题。

# 为了提高可读性,%T>% 不换行
rnorm(100) %>%
  matrix(ncol = 2) %T>% plot %>%
  str()

#>  num [1:50, 1:2] 1.224 0.36 0.401 0.111 -0.556 ...

%$% 爆炸操作符

一些基础函数只接受向量作为参数,而不接受数据框,爆炸操作符可以绑定数据框,让右侧的函数可以直接使用列名

mtcars %$% cor(disp, mpg)
#> [1] -0.8475514

相当于

cor(mtcars$disp, mtcars$mpg)
#> [1] -0.8475514

%<>% 复合赋值操作符

功能与%>%基本一样,多了一项额外的操作,就是把结果写回左侧对象。所以其功能就是运算且更新。

比如,我们需要对一个数据集进行排序,同时需要获得排序的结果,用%<>%就是非常方便的。

现实原理如下图所示,使用%<>%把左侧的数据集A传递右侧的B函数,B函数的结果数据集再向右侧传递给C函数,C函数结果的数据集再重新赋值给A,完成整个过程。

复合赋值操作符

例:定义符合正态分布的100个随机数,计算绝对值,并按从小到大的顺序排序,获得并取前10个数字赋值给x。

set.seed(1)
x <- rnorm(100)
a <- x %>%
  abs() %>%
  sort() %>%
  head(10)
a
#>  [1] 0.001105352 0.016190263 0.028002159 0.039240003 0.044933609 0.053805041
#>  [7] 0.056128740 0.059313397 0.074341324 0.074564983
x %<>% abs %>%
  sort() %>%
  head(10)
x
#>  [1] 0.001105352 0.016190263 0.028002159 0.039240003 0.044933609 0.053805041
#>  [7] 0.056128740 0.059313397 0.074341324 0.074564983

%<>%的主要作用是省略一步<-,但会造成变量的改变,程序的稳健性会因此削弱,最好不要用。

流式操作的使用场景

在函数间传递数据

匿名函数function(parameter){...}单独出现时,外面要用括号()括起来

iris %>%
  (function(x) {
    if (nrow(x) > 2) {
      bind_rows(head(x, 1), tail(x, 1)) %>% return()
    } else {
      return(x)
    }
  })
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
#> 1          5.1         3.5          1.4         0.2    setosa
#> 2          5.9         3.0          5.1         1.8 virginica

用函数替代运算符

# 本质上,R中的运算符都是函数,故以下两行伪代码等价
fun(sym) <- value
`fun<-`(sym, value) # 上一行是这一行的语法糖
新函数 原操作符1
extract(x, index or 'col_name') [,即[(x, index/‘col_name’)
extract2(x, index or 'col_name') [[,即[[(x, index’col_name’)
inset() [<-
inset2() [[<-
set_colnames() colnames()<-
set_rownames() rownames()<-
magrittr::use_series()(可用于list), dplyr::pull()(不适用于list) $
add +
subtract -
multiply_by *
raise_to_power ^
multiply_by_matrix %*%
divide_by /
divide_by_int %/%
mod %%
is_in() %in%
and &
or |
equals ==
is_greater_than >
is_weakly_greater_than >=
is_less_than <
is_weakly_less_than <=
not !
set_names() names()<-
set.seed(1)
x <- rnorm(10)
x
#>  [1] -0.6264538  0.1836433 -0.8356286  1.5952808  0.3295078 -0.8204684
#>  [7]  0.4874291  0.7383247  0.5757814 -0.3053884
x %>%
  multiply_by(5) %>%
  add(5)
#>  [1]  1.8677309  5.9182166  0.8218569 12.9764040  6.6475389  0.8976581
#>  [7]  7.4371453  8.6916235  7.8789068  3.4730581
x %>%
  `*`(5) %>%
  `+`(5)
#>  [1]  1.8677309  5.9182166  0.8218569 12.9764040  6.6475389  0.8976581
#>  [7]  7.4371453  8.6916235  7.8789068  3.4730581
x <- 1:10
x %<>% set_names(letters[1:10])
# 作为参数传入函数时,会自动生成副本,x不会被修改,所以必须加赋值命令
x
#>  a  b  c  d  e  f  g  h  i  j 
#>  1  2  3  4  5  6  7  8  9 10
x <- 1:10
x %<>% `names<-`(letters[1:10]) # names()<- 在R中执行的真正形式
x
#>  a  b  c  d  e  f  g  h  i  j 
#>  1  2  3  4  5  6  7  8  9 10
y <- list(c(1, 2), c("a", "b"))
y %>% `[`(2) # 不能直接用extract(),已被其他包同名函数覆盖
#> [[1]]
#> [1] "a" "b"
y %>% magrittr::extract(2)
#> [[1]]
#> [1] "a" "b"
y %>% extract2(2)
#> [1] "a" "b"
y %>%
  extract2(2) %>%
  class()
#> [1] "character"
y %>%
  extract2(2) %>%
  extract2(2)
#> [1] "b"
y %>%
  map(extract2, 2) %>%
  unlist()
#> [1] "2" "b"
# unlist()拆分成vector,必须是同样的数据类型,那只能是数字退化为字符串

df <- tibble(v1 = 1:26, v2 = letters)
# 完成了列名赋值工作
df %<>% set_colnames(c("id", "name")) %>%
  filter(id %>% is_in(1:10)) # 替代 %in%
df
#> # A tibble: 10 × 2
#>       id name 
#>    <int> <chr>
#>  1     1 a    
#>  2     2 b    
#>  3     3 c    
#>  4     4 d    
#>  5     5 e    
#>  6     6 f    
#>  7     7 g    
#>  8     8 h    
#>  9     9 i    
#> 10    10 j

传递数据给代码块

例:对一个包括10个随机数的向量的先*5再+5,求出向量的均值和标准差,并从小到大排序后返回前5条。

set.seed(1)
a <- rnorm(10)
a %>%
  multiply_by(5) %>%
  add(5) %>%
  {
    cat("Mean:", mean(.), "\nVar:", var(.), "\n")
    sort(.) %>% head(5)
  }
#> Mean: 5.661014 
#> Var: 15.23286
#> [1] 0.8218569 0.8976581 1.8677309 3.4730581 5.9182166
# 等价于定义一个函数。还是应该定义函数,增强可读性
display <- function(d) {
  cat("Mean:", mean(d), "\nVar:", var(d), "\n")
  sort(d) %>%
    head(5) %>%
    print()
}
a %>%
  multiply_by(5) %>%
  add(5) %>%
  display()
#> Mean: 5.661014 
#> Var: 15.23286 
#> [1] 0.8218569 0.8976581 1.8677309 3.4730581 5.9182166

  1. R 中的操作符其实都是函数,只不过是写法上特殊一点的无括号函数。所以在管道操作中,左边函数对右边运算符的替换并不是必须的,因为管道也可以和操作符直接配合,只不过都要用反引号将操作符括起来,显式指明这是一个函数。↩︎

LS0tDQp0aXRsZTogIueuoemBk+S8oOi+k+eahOmTvuW8j+ivreazlSINCnN1YnRpdGxlOiAnJw0KYXV0aG9yOiAiSHVtb29uIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiBodG1sX2RvY3VtZW50DQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0NCnNvdXJjZSgiLi4vUm1hcmtkb3duLXRlbXBsYXRlL1JtYXJrZG93bl9jb25maWcuUiIpDQoNCiMjIGdsb2JhbCBvcHRpb25zID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIHdpZHRoID0gY29uZmlnJHdpZHRoLA0KICBmaWcud2lkdGggPSBjb25maWckZmlnLndpZHRoLA0KICBmaWcuYXNwID0gY29uZmlnJGZpZy5hc3AsDQogIG91dC53aWR0aCA9IGNvbmZpZyRvdXQud2lkdGgsDQogIGZpZy5hbGlnbiA9IGNvbmZpZyRmaWcuYWxpZ24sDQogIGZpZy5wYXRoID0gY29uZmlnJGZpZy5wYXRoLA0KICBmaWcuc2hvdyA9IGNvbmZpZyRmaWcuc2hvdywNCiAgd2FybiA9IGNvbmZpZyR3YXJuLA0KICB3YXJuaW5nID0gY29uZmlnJHdhcm5pbmcsDQogIG1lc3NhZ2UgPSBjb25maWckbWVzc2FnZSwNCiAgZWNobyA9IGNvbmZpZyRlY2hvLA0KICBldmFsID0gY29uZmlnJGV2YWwsDQogIHRpZHkgPSBjb25maWckdGlkeSwNCiAgY29tbWVudCA9IGNvbmZpZyRjb21tZW50LA0KICBjb2xsYXBzZSA9IGNvbmZpZyRjb2xsYXBzZSwNCiAgY2FjaGUgPSBjb25maWckY2FjaGUsDQogIGNhY2hlLmNvbW1lbnRzID0gY29uZmlnJGNhY2hlLmNvbW1lbnRzLA0KICBhdXRvZGVwID0gY29uZmlnJGF1dG9kZXANCikNCmBgYA0KDQoNCg0KIyMgcGlwZWxpbmUgb3BlcmF0b3INCg0K5ZG95Luk5byP57yW56iL5a655piT5Ye6546w5aSn6YeP55qE5Lit6Ze05Y+Y6YeP77yM5rGh5p+T5YWo5bGA546v5aKD44CCDQoNCuWPr+WmguaenOWGmeaIkOS4gOezu+WIl+WHveaVsOW1jOWll+eahOW9ouW8j++8jOWPiOS8mumAoOaIkOWPr+ivu+aAp+S4i+mZjQ0KDQpwaXBlbGluZSBvcGVyYXRvciDog73lpJ/op6PlhrPov5nkuKrnl5vngrnvvIzkvb/ku6PnoIHmm7TliqDmuIXmmbDmmJPor7sNCg0KIyMjIGB8PmANCg0K6L+Z5pivIDQuMS4wIOeJiOacrOaWsOWPkeW4g+eahCBiYXNlIFIg566h6YGT5pON5L2c56ym44CCDQoNCjQuMi4wIOS4uuWFtuiuvuiuoeS6hiBwbGFjZWhvbGRlciBgX2DvvIzkvYbov5nkuKogcGxhY2Vob2xkZXIg5LiN6IO95Y2V54us5L2/55So77yM5b+F6aG75Zyo5YW25Ye6546w55qE5Y+C5pWw5L2N572u5YaZ5LiKIGBwYXJhbXRlciA9IF9gDQoNCmBgYHtyfQ0KbXRjYXJzIHw+IGxtKG1wZyB+IGRpc3AsIGRhdGEgPSBfKSAjIGRhdGEgPSDkuI3lj6/nnIHnlaXvvIzlkKbliJnkvJrmiqXplJkNCmBgYA0KDQpgfD5gIOWSjCBgX2Ag6L+Z5LiA5aWX55qE5o6o5bm/77yM6ZyA6KaBIFJzdHVkaW8g5ZKMIFZTQ29kZS1SIOetiSBJREUg5Lit5b+r5o236ZSu5ZKM6K+t5rOV5qOA5p+l55qE5Yy56YWN4oCU4oCU5L2G5a6D5Lus55qE5pu05paw5b6A5b6A6KaB5oWi5LqOIFIgYmFzZQ0KDQojIyMgYCU+JWAg5Y+z5ZCR566h6YGT5pON5L2c56ymDQoNCuS8oOmAkuWvueixoee7meWPs+i+ueeahOWHveaVsOaIluihqOi+vuW8j+OAguW9k+WvueixoeWcqOWPs+i+ueWHveaVsOeahOesrOS4gOS4quWPguaVsOaXtu+8jOWPr+S7peecgeeVpei/meS4quWPguaVsO+8m+S4jeaYr+esrOS4gOS4quWPguaVsOaXtu+8jOWImeWPr+S7peeUqGAuYOS7o+abv+OAguWmguaenOaYr+S4gOWFg+WHveaVsO+8jOeUmuiHs+WPr+S7pei/nmAoKWDkuZ/nnIHmjonvvIzlj6rkv53nlZnlh73mlbDlkI3jgIINCg0KIVvnrqHpgZPmk43kvZznrKZdKGltZy9waXBlLnBuZykNCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQphIDwtIHJub3JtKDEwKQ0KYQ0KDQojIOeUseatpOWPr+inge+8mmAqYOWSjGArYOWFtuWunuaYr+S4pOS4quWHveaVsO+8geaOpeaUtuS8oOadpeeahOaVsOaNrua1geS9nOS4uuiHquW3seeahOesrOS4gOS4quWPguaVsA0KYSAlPiUNCiAgYCpgKDUpICU+JQ0KICBgK2AoNSkNCmBgYA0KDQojIyMgYCVUPiVgIOW3puWQkeeuoemBk+aTjeS9nOespg0KDQrlv4XpobvkuI5gJT4lYOmFjeWQiOS9v+eUqOOAgg0KDQohW1TnrqHpgZPmk43kvZznrKZdKGltZy9ULXBpcGUucG5nKQ0KDQrkvosx77ya5Zyo5pWw5o2u5aSE55CG55qE5Lit6Ze06L+H56iL77yM6ZyA6KaB5omT5Y2w6L6T5Ye65oiW5Zu+54mH6L6T5Ye677yI5aaC6KeC5a+f5Luj56CB5piv5ZCm6L6+5Yiw5LqG6aKE6K6+55qE55uu55qE77yJ77yM6L+Z5pe25pW05Liq6L+H56iL5bCx5Lya6KKr5Lit5pat77yM55So5ZCR5bem5pON5L2c56ym5bCx5Y+v5Lul6Kej5Yaz6L+Z5qC355qE6Zeu6aKY44CCDQoNCmBgYHtyfQ0KIyDkuLrkuobmj5Dpq5jlj6/or7vmgKfvvIwlVD4lIOS4jeaNouihjA0Kcm5vcm0oMTAwKSAlPiUNCiAgbWF0cml4KG5jb2wgPSAyKSAlVD4lIHBsb3QgJT4lDQogIHN0cigpDQpgYGANCg0KIyMjIGAlJCVgIOeIhueCuOaTjeS9nOespg0KDQrkuIDkupvln7rnoYDlh73mlbDlj6rmjqXlj5flkJHph4/kvZzkuLrlj4LmlbDvvIzogIzkuI3mjqXlj5fmlbDmja7moYbvvIzniIbngrjmk43kvZznrKblj6/ku6Xnu5HlrprmlbDmja7moYbvvIzorqnlj7PkvqfnmoTlh73mlbDlj6/ku6Xnm7TmjqXkvb/nlKjliJflkI0NCg0KYGBge3J9DQptdGNhcnMgJSQlIGNvcihkaXNwLCBtcGcpDQpgYGANCg0K55u45b2T5LqODQoNCmBgYHtyfQ0KY29yKG10Y2FycyRkaXNwLCBtdGNhcnMkbXBnKQ0KYGBgDQoNCiMjIyBgJTw+JWAg5aSN5ZCI6LWL5YC85pON5L2c56ymDQoNCuWKn+iDveS4jmAlPiVg5Z+65pys5LiA5qC377yM5aSa5LqG5LiA6aG56aKd5aSW55qE5pON5L2c77yM5bCx5piv5oqK57uT5p6c5YaZ5Zue5bem5L6n5a+56LGh44CC5omA5Lul5YW25Yqf6IO95bCx5piv6L+Q566X5LiU5pu05paw44CCDQoNCuavlOWmgu+8jOaIkeS7rOmcgOimgeWvueS4gOS4quaVsOaNrumbhui/m+ihjOaOkuW6j++8jOWQjOaXtumcgOimgeiOt+W+l+aOkuW6j+eahOe7k+aenO+8jOeUqGAlPD4lYOWwseaYr+mdnuW4uOaWueS+v+eahOOAgg0KDQrnjrDlrp7ljp/nkIblpoLkuIvlm77miYDnpLrvvIzkvb/nlKhgJTw+JWDmiorlt6bkvqfnmoTmlbDmja7pm4ZB5Lyg6YCS5Y+z5L6n55qEQuWHveaVsO+8jELlh73mlbDnmoTnu5PmnpzmlbDmja7pm4blho3lkJHlj7PkvqfkvKDpgJLnu5lD5Ye95pWw77yMQ+WHveaVsOe7k+aenOeahOaVsOaNrumbhuWGjemHjeaWsOi1i+WAvOe7mUHvvIzlrozmiJDmlbTkuKrov4fnqIvjgIINCg0KIVvlpI3lkIjotYvlgLzmk43kvZznrKZdKGltZy9jb21wb3NpdGUtcGlwZS5wbmcpDQoNCuS+i++8muWumuS5ieespuWQiOato+aAgeWIhuW4g+eahDEwMOS4qumaj+acuuaVsO+8jOiuoeeul+e7neWvueWAvO+8jOW5tuaMieS7juWwj+WIsOWkp+eahOmhuuW6j+aOkuW6j++8jOiOt+W+l+W5tuWPluWJjTEw5Liq5pWw5a2X6LWL5YC857uZeOOAgg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQp4IDwtIHJub3JtKDEwMCkNCmEgPC0geCAlPiUNCiAgYWJzKCkgJT4lDQogIHNvcnQoKSAlPiUNCiAgaGVhZCgxMCkNCmENCg0KeCAlPD4lIGFicyAlPiUNCiAgc29ydCgpICU+JQ0KICBoZWFkKDEwKQ0KeA0KYGBgDQoNCmAlPD4lYOeahOS4u+imgeS9nOeUqOaYr+ecgeeVpeS4gOatpWA8LWDvvIzkvYbkvJrpgKDmiJDlj5jph4/nmoTmlLnlj5jvvIznqIvluo/nmoTnqLPlgaXmgKfkvJrlm6DmraTliYrlvLHvvIzmnIDlpb3kuI3opoHnlKjjgIINCg0KIyMg5rWB5byP5pON5L2c55qE5L2/55So5Zy65pmvDQoNCiMjIyDlnKjlh73mlbDpl7TkvKDpgJLmlbDmja4NCg0K5Yy/5ZCN5Ye95pWwYGZ1bmN0aW9uKHBhcmFtZXRlcil7Li4ufWDljZXni6zlh7rnjrDml7bvvIzlpJbpnaLopoHnlKjmi6zlj7dgKClg5ous6LW35p2lDQoNCmBgYHtyfQ0KaXJpcyAlPiUNCiAgKGZ1bmN0aW9uKHgpIHsNCiAgICBpZiAobnJvdyh4KSA+IDIpIHsNCiAgICAgIGJpbmRfcm93cyhoZWFkKHgsIDEpLCB0YWlsKHgsIDEpKSAlPiUgcmV0dXJuKCkNCiAgICB9IGVsc2Ugew0KICAgICAgcmV0dXJuKHgpDQogICAgfQ0KICB9KQ0KYGBgDQoNCg0KIyMjIOeUqOWHveaVsOabv+S7o+i/kOeul+espg0KDQpgYGAgcg0KIyDmnKzotKjkuIrvvIxS5Lit55qE6L+Q566X56ym6YO95piv5Ye95pWw77yM5pWF5Lul5LiL5Lik6KGM5Lyq5Luj56CB562J5Lu3DQpmdW4oc3ltKSA8LSB2YWx1ZQ0KYGZ1bjwtYChzeW0sIHZhbHVlKSAjIOS4iuS4gOihjOaYr+i/meS4gOihjOeahOivreazleezlg0KYGBgDQoNCnwg5paw5Ye95pWwICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCDljp/mk43kvZznrKZbXjJdICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgYGV4dHJhY3QoeCwgaW5kZXggb3IgJ2NvbF9uYW1lJylgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgW2DvvIzljbNgW2AoeCwgaW5kZXgvJ2NvbF9uYW1lJykgIHwNCnwgYGV4dHJhY3QyKHgsIGluZGV4IG9yICdjb2xfbmFtZScpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgW1tg77yM5Y2zYFtbYCh4LCBpbmRleCdjb2xfbmFtZScpIHwNCnwgYGluc2V0KClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgWzwtYCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGBpbnNldDIoKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYFtbPC1gICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBgc2V0X2NvbG5hbWVzKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGBjb2xuYW1lcygpPC1gICAgICAgICAgICAgICAgICAgIHwNCnwgYHNldF9yb3duYW1lcygpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgcm93bmFtZXMoKTwtYCAgICAgICAgICAgICAgICAgICB8DQp8IGBtYWdyaXR0cjo6dXNlX3NlcmllcygpYO+8iOWPr+eUqOS6jmxpc3TvvIksIGBkcGx5cjo6cHVsbCgpYO+8iOS4jemAgueUqOS6jmxpc3TvvIkgfCBgJGAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFkZCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYCtgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBzdWJ0cmFjdCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGAtYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgbXVsdGlwbHlfYnkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgKmAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHJhaXNlX3RvX3Bvd2VyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYF5gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBtdWx0aXBseV9ieV9tYXRyaXggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGAlKiVgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZGl2aWRlX2J5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgL2AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGRpdmlkZV9ieV9pbnQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYCUvJWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBtb2QgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGAlJWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYGlzX2luKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgJWluJWAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGFuZCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYCZgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBvciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGB8YCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZXF1YWxzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgPT1gICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGlzX2dyZWF0ZXJfdGhhbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYD5gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBpc193ZWFrbHlfZ3JlYXRlcl90aGFuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGA+PWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgaXNfbGVzc190aGFuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgPGAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGlzX3dlYWtseV9sZXNzX3RoYW4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgYDw9YCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBub3QgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IGAhYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgYHNldF9uYW1lcygpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBgbmFtZXMoKTwtYCAgICAgICAgICAgICAgICAgICAgICB8DQoNClteMl06IFIg5Lit55qE5pON5L2c56ym5YW25a6e6YO95piv5Ye95pWw77yM5Y+q5LiN6L+H5piv5YaZ5rOV5LiK54m55q6K5LiA54K555qE5peg5ous5Y+35Ye95pWw44CC5omA5Lul5Zyo566h6YGT5pON5L2c5Lit77yM5bem6L655Ye95pWw5a+55Y+z6L656L+Q566X56ym55qE5pu/5o2i5bm25LiN5piv5b+F6aG755qE77yM5Zug5Li6566h6YGT5Lmf5Y+v5Lul5ZKM5pON5L2c56ym55u05o6l6YWN5ZCI77yM5Y+q5LiN6L+H6YO96KaB55So5Y+N5byV5Y+35bCG5pON5L2c56ym5ous6LW35p2l77yM5pi+5byP5oyH5piO6L+Z5piv5LiA5Liq5Ye95pWw44CCDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkNCnggPC0gcm5vcm0oMTApDQp4DQoNCg0KeCAlPiUNCiAgbXVsdGlwbHlfYnkoNSkgJT4lDQogIGFkZCg1KQ0KeCAlPiUNCiAgYCpgKDUpICU+JQ0KICBgK2AoNSkNCg0KDQp4IDwtIDE6MTANCnggJTw+JSBzZXRfbmFtZXMobGV0dGVyc1sxOjEwXSkNCiMg5L2c5Li65Y+C5pWw5Lyg5YWl5Ye95pWw5pe277yM5Lya6Ieq5Yqo55Sf5oiQ5Ymv5pys77yMeOS4jeS8muiiq+S/ruaUue+8jOaJgOS7peW/hemhu+WKoOi1i+WAvOWRveS7pA0KeA0KDQp4IDwtIDE6MTANCnggJTw+JSBgbmFtZXM8LWAobGV0dGVyc1sxOjEwXSkgIyBuYW1lcygpPC0g5ZyoUuS4reaJp+ihjOeahOecn+ato+W9ouW8jw0KeA0KDQoNCnkgPC0gbGlzdChjKDEsIDIpLCBjKCJhIiwgImIiKSkNCnkgJT4lIGBbYCgyKSAjIOS4jeiDveebtOaOpeeUqGV4dHJhY3QoKe+8jOW3suiiq+WFtuS7luWMheWQjOWQjeWHveaVsOimhueblg0KeSAlPiUgbWFncml0dHI6OmV4dHJhY3QoMikNCg0KeSAlPiUgZXh0cmFjdDIoMikNCnkgJT4lDQogIGV4dHJhY3QyKDIpICU+JQ0KICBjbGFzcygpDQp5ICU+JQ0KICBleHRyYWN0MigyKSAlPiUNCiAgZXh0cmFjdDIoMikNCnkgJT4lDQogIG1hcChleHRyYWN0MiwgMikgJT4lDQogIHVubGlzdCgpDQojIHVubGlzdCgp5ouG5YiG5oiQdmVjdG9y77yM5b+F6aG75piv5ZCM5qC355qE5pWw5o2u57G75Z6L77yM6YKj5Y+q6IO95piv5pWw5a2X6YCA5YyW5Li65a2X56ym5LiyDQoNCmRmIDwtIHRpYmJsZSh2MSA9IDE6MjYsIHYyID0gbGV0dGVycykNCiMg5a6M5oiQ5LqG5YiX5ZCN6LWL5YC85bel5L2cDQpkZiAlPD4lIHNldF9jb2xuYW1lcyhjKCJpZCIsICJuYW1lIikpICU+JQ0KICBmaWx0ZXIoaWQgJT4lIGlzX2luKDE6MTApKSAjIOabv+S7oyAlaW4lDQpkZg0KYGBgDQoNCiMjIyDkvKDpgJLmlbDmja7nu5nku6PnoIHlnZcNCg0K5L6L77ya5a+55LiA5Liq5YyF5ousMTDkuKrpmo/mnLrmlbDnmoTlkJHph4/nmoTlhYhcKjXlho0rNe+8jOaxguWHuuWQkemHj+eahOWdh+WAvOWSjOagh+WHhuW3ru+8jOW5tuS7juWwj+WIsOWkp+aOkuW6j+WQjui/lOWbnuWJjTXmnaHjgIINCg0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KYSA8LSBybm9ybSgxMCkNCmEgJT4lDQogIG11bHRpcGx5X2J5KDUpICU+JQ0KICBhZGQoNSkgJT4lDQogIHsNCiAgICBjYXQoIk1lYW46IiwgbWVhbiguKSwgIlxuVmFyOiIsIHZhciguKSwgIlxuIikNCiAgICBzb3J0KC4pICU+JSBoZWFkKDUpDQogIH0NCg0KIyDnrYnku7fkuo7lrprkuYnkuIDkuKrlh73mlbDjgILov5jmmK/lupTor6XlrprkuYnlh73mlbDvvIzlop7lvLrlj6/or7vmgKcNCmRpc3BsYXkgPC0gZnVuY3Rpb24oZCkgew0KICBjYXQoIk1lYW46IiwgbWVhbihkKSwgIlxuVmFyOiIsIHZhcihkKSwgIlxuIikNCiAgc29ydChkKSAlPiUNCiAgICBoZWFkKDUpICU+JQ0KICAgIHByaW50KCkNCn0NCmEgJT4lDQogIG11bHRpcGx5X2J5KDUpICU+JQ0KICBhZGQoNSkgJT4lDQogIGRpc3BsYXkoKQ0KYGBgDQo=