原生 R 环境处理 factor

R 中储存 factor 的步骤

  1. 建立 levels 与整数的映射关系
  2. 按照映射关系,将 factor 转换为整数向量存储

levels 的排序

  1. 默认按字母顺序排序
class <- factor(c("Poor", "Improved", "Excellent"), ordered = T)
class
#> [1] Poor      Improved  Excellent
#> Levels: Excellent < Improved < Poor
  1. 通过 levels 参数人工设定因子型数据各水平的顺序
class <- factor(
  c("Poor", "Improved", "Excellent"),
  levels = c("Poor", "Improved", "Excellent"),
  ordered = T
)
class
#> [1] Poor      Improved  Excellent
#> Levels: Poor < Improved < Excellent
  1. 与 levels 出现的顺序保持一致

在创建因子时,将水平设置为unique(x);或者在创建因子后再对其使用fct_inorder()函数,需要forcats包(包含在tidyverse全家桶中)。

x <- c("A", "T", "T", "A", "C", "K")
y <- factor(x, levels = unique(x))
y
#> [1] A T T A C K
#> Levels: A T C K

x %>%
  factor() %>%
  fct_inorder()
#> [1] A T T A C K
#> Levels: A T C K

使用forcats处理factor

想要以非字母表顺序显示字符串向量时(如一个轴为离散变量的绘图),就需要用到因子。forcats 包中含有大量处理因子的函数。

当字符串向量中有很多重复元素时,用 factor (本质是整数)储存比用字符串节省空间。因此,R 基础包中的很多函数都自动将字符串转换为因子。这意味着因子经常出现在并不真正适合它们的地方。好在不用担心 tidyverse 中会出现这种问题。

library(forcats)

创建因子

要想创建一个因子,必须先创建 levels 向量:

month_levels <- c(
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
)

因子的排序

生成因子时的顺序

## 非因子的排序遵循字母表排序
x1 <- c("Dec", "Apr", "Jan", "Mar", "Mar", "Apr")
x1
#> [1] "Dec" "Apr" "Jan" "Mar" "Mar" "Apr"
sort(x1)
#> [1] "Apr" "Apr" "Dec" "Jan" "Mar" "Mar"

## 因子:按预先声明的因子顺序排序
y1 <- factor(x1, levels = month_levels)
sort(y1)
#> [1] Jan Mar Mar Apr Apr Dec
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

## 因子:字母表排序
y2 <- factor(x1) # 如果省略了定义水平的这个步骤直接创建因子
sort(y2) # 会按字母顺序排序
#> [1] Apr Apr Dec Jan Mar Mar
#> Levels: Apr Dec Jan Mar

## 因子:以初始数据的出现顺序作为默认排序
# 令levels参数为unique(x),或在创建因子后使用fct_inorder()函数
y3 <- factor(x1, levels = unique(x1))
sort(y3)
#> [1] Dec Apr Apr Jan Mar Mar
#> Levels: Dec Apr Jan Mar

y4 <- factor(x1) %>% fct_inorder()
sort(y4)
#> [1] Dec Apr Apr Jan Mar Mar
#> Levels: Dec Apr Jan Mar

fct_relevel()

随时重新手动设定顺序

income <- c("low", "high", "medium", "medium", "low", "high", "high")
x <- factor(income)
x %>% fct_relevel(levels = c("high", "medium", "low"))
#> [1] low    high   medium medium low    high   high  
#> Levels: high medium low
x %>% fct_relevel(levels = c("medium"))
#> [1] low    high   medium medium low    high   high  
#> Levels: medium high low
x %>% fct_relevel("medium", after = Inf)
#> [1] low    high   medium medium low    high   high  
#> Levels: high low medium

fct_rev()

对因子逆序

d <- tibble(
  x = c("a", "a", "b", "b", "c", "c"),
  y = c(2, 2, 1, 5, 0, 3)
)

d %>%
  mutate(x = fct_rev(x)) %>%
  ggplot(aes(x = x, y = y)) +
  geom_point()

fct_reorder()

fct_reorder(x, y, .fun = , .desc = FALSE)

按 x 分类,根据每个分类变量对应 y 值的向量经过 .fun 运算后的结果来对 x 排序

d %>%
  # x轴因子的顺序为字母顺序
  ggplot(aes(x = x, y = y)) +
  geom_point()


d %>%
  # x因子的顺序变为按照每组成员的y值的中位数的降序来排序
  mutate(x = fct_reorder(x, y, .fun = median, .desc = TRUE)) %>%
  ggplot(aes(x = x, y = y)) +
  geom_point()


d %>%
  # x因子的顺序变为按照每组成员的y值的最小值的降序来排序
  mutate(x = fct_reorder(x, y, .fun = median, .desc = TRUE)) %>%
  ggplot(aes(x = x, y = y)) +
  geom_point()

排查输入错误

x2 <- c("Dec", "Apr", "Jam", "Mar")
y5 <- factor(x2, levels = month_levels)
y5 # 不再水平范围内的值自动转换为NA
#> [1] Dec  Apr  <NA> Mar 
#> Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec

查看水平:levels()

levels(y1)
#>  [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
levels(y2)
#> [1] "Apr" "Dec" "Jan" "Mar"
levels(y3)
#> [1] "Dec" "Apr" "Jan" "Mar"

例:综合社会调查

forcats::gss_cat %>% head(n = 5) # 发现一些字段是factor
#> # A tibble: 5 x 9
#>    year marital         age race  rincome        partyid     relig denom tvhours
#>   <int> <fct>         <int> <fct> <fct>          <fct>       <fct> <fct>   <int>
#> 1  2000 Never married    26 White $8000 to 9999  Ind,near r~ Prot~ Sout~      12
#> 2  2000 Divorced         48 White $8000 to 9999  Not str re~ Prot~ Bapt~      NA
#> 3  2000 Widowed          67 White Not applicable Independent Prot~ No d~       2
#> 4  2000 Never married    39 White Not applicable Ind,near r~ Orth~ Not ~       4
#> 5  2000 Divorced         25 White Not applicable Not str de~ None  Not ~       1

## 查看因子水平
gss_cat %>% count(race) # count()函数
#> # A tibble: 3 x 2
#>   race      n
#>   <fct> <int>
#> 1 Other  1959
#> 2 Black  3129
#> 3 White 16395
ggplot(gss_cat, aes(race)) +
  geom_bar() # 条形图,不显示记数为0的水平

ggplot(gss_cat, aes(race)) +
  geom_bar() +
  scale_x_discrete(drop = FALSE) # 显示记数为0的水平

修改因子的水平

修改水平不仅可以使得图形标签更美观清晰,以满足出版发行的要求,还可以将水平汇集成更高层次的显示。

修改水平最常用、最强大的工具是 fct_recode() 函数,它可以对每个水平进行修改或重新编码,让没有明确提及的水平保持原样,如果不小心修改了一个不存在的水平,它也会给出警告。

gss_cat %>% count(partyid)
#> # A tibble: 10 x 2
#>    partyid                n
#>    <fct>              <int>
#>  1 No answer            154
#>  2 Don't know             1
#>  3 Other party          393
#>  4 Strong republican   2314
#>  5 Not str republican  3032
#>  6 Ind,near rep        1791
#>  7 Independent         4119
#>  8 Ind,near dem        2499
#>  9 Not str democrat    3690
#> 10 Strong democrat     3490

gss_cat %>%
  mutate(
    partyid = fct_recode(
      partyid,
      "Republican, strong" = "Strong republican",
      "Republican, weak" = "Not str republican",
      "Independent, near rep" = "Ind,near rep",
      "Independent, near dem" = "Ind,near dem",
      "Democrat, weak" = "Not str democrat",
      "Democrat, strong" = "Strong democrat"
    )
  ) %>%
  count(partyid)
#> # A tibble: 10 x 2
#>    partyid                   n
#>    <fct>                 <int>
#>  1 No answer               154
#>  2 Don't know                1
#>  3 Other party             393
#>  4 Republican, strong     2314
#>  5 Republican, weak       3032
#>  6 Independent, near rep  1791
#>  7 Independent            4119
#>  8 Independent, near dem  2499
#>  9 Democrat, weak         3690
#> 10 Democrat, strong       3490

可以将多个原水平赋给同一个新水平,这样就可以合并原来的分类:

gss_cat %>%
  mutate(
    partyid = fct_recode(
      partyid,
      "Republican, strong" = "Strong republican",
      "Republican, weak" = "Not str republican",
      "Independent, near rep" = "Ind,near rep",
      "Independent, near dem" = "Ind,near dem",
      "Democrat, weak" = "Not str democrat",
      "Democrat, strong" = "Strong democrat",
      "Other" = "No answer",
      "Other" = "Don't know",
      "Other" = "Other party"
    )
  ) %>%
  count(partyid)
#> # A tibble: 8 x 2
#>   partyid                   n
#>   <fct>                 <int>
#> 1 Other                   548
#> 2 Republican, strong     2314
#> 3 Republican, weak       3032
#> 4 Independent, near rep  1791
#> 5 Independent            4119
#> 6 Independent, near dem  2499
#> 7 Democrat, weak         3690
#> 8 Democrat, strong       3490

也可以使用 fct_recode() 函数的变体 fct_collapse() 函数。对于每个新水平,你都可以提供一个包含原水平的向量:

gss_cat %>%
  mutate(partyid = fct_collapse(
    partyid,
    other = c("No answer", "Don't know", "Other party"),
    rep = c("Strong republican", "Not str republican"),
    ind = c("Ind,near rep", "Independent", "Ind,near dem"),
    dem = c("Not str democrat", "Strong democrat")
  )) %>%
  count(partyid)
#> # A tibble: 4 x 2
#>   partyid     n
#>   <fct>   <int>
#> 1 other     548
#> 2 rep      5346
#> 3 ind      8409
#> 4 dem      7180
LS0tDQp0aXRsZTogIkZhY3RvciINCnN1YnRpdGxlOiAnJw0KYXV0aG9yOiAiSHVtb29uIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY3NzOiBbIi4uL2Nzcy9zdHlsZS5jc3MiXQ0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICB0aGVtZTogdW5pdGVkDQogICAgaGlnaGxpZ2h0OiBoYWRkb2NrDQogICAgbnVtYmVyX3NlY3Rpb25zOiBubw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiB5ZXMNCiAgICAgIHNtb290aF9zY3JvbGw6IHllcw0KZG9jdW1lbnRjbGFzczogY3RleGFydA0KY2xhc3NvcHRpb246IGh5cGVycmVmLA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9DQpzb3VyY2UoIi4uL1JtYXJrZG93bi10ZW1wbGF0ZS9SbWFya2Rvd25fY29uZmlnLlIiKQ0KDQojIyBnbG9iYWwgb3B0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICB3aWR0aCA9IGNvbmZpZyR3aWR0aCwNCiAgZmlnLndpZHRoID0gY29uZmlnJGZpZy53aWR0aCwNCiAgZmlnLmFzcCA9IGNvbmZpZyRmaWcuYXNwLA0KICBvdXQud2lkdGggPSBjb25maWckb3V0LndpZHRoLA0KICBmaWcuYWxpZ24gPSBjb25maWckZmlnLmFsaWduLA0KICBmaWcucGF0aCA9IGNvbmZpZyRmaWcucGF0aCwNCiAgZmlnLnNob3cgPSBjb25maWckZmlnLnNob3csDQogIHdhcm4gPSBjb25maWckd2FybiwNCiAgd2FybmluZyA9IGNvbmZpZyR3YXJuaW5nLA0KICBtZXNzYWdlID0gY29uZmlnJG1lc3NhZ2UsDQogIGVjaG8gPSBjb25maWckZWNobywNCiAgZXZhbCA9IGNvbmZpZyRldmFsLA0KICB0aWR5ID0gY29uZmlnJHRpZHksDQogIGNvbW1lbnQgPSBjb25maWckY29tbWVudCwNCiAgY29sbGFwc2UgPSBjb25maWckY29sbGFwc2UsDQogIGNhY2hlID0gY29uZmlnJGNhY2hlLA0KICBjYWNoZS5jb21tZW50cyA9IGNvbmZpZyRjYWNoZS5jb21tZW50cywNCiAgYXV0b2RlcCA9IGNvbmZpZyRhdXRvZGVwDQopDQoNCiMjIHVzZSBuZWNlc3NhcnkgcGFja2FnZXMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoaHRtbHdpZGdldHMpDQpgYGANCg0KDQoNCiMjIOWOn+eUnyBSIOeOr+Wig+WkhOeQhiBmYWN0b3INCg0KIyMjIFIg5Lit5YKo5a2YIGZhY3RvciDnmoTmraXpqqQNCg0KMS4g5bu656uLIGxldmVscyDkuI7mlbTmlbDnmoTmmKDlsITlhbPns7sNCjIuIOaMieeFp+aYoOWwhOWFs+ezu++8jOWwhiBmYWN0b3Ig6L2s5o2i5Li65pW05pWw5ZCR6YeP5a2Y5YKoDQoNCiMjIyBsZXZlbHMg55qE5o6S5bqPDQoNCjEuIOm7mOiupOaMieWtl+avjemhuuW6j+aOkuW6jw0KYGBge3J9DQpjbGFzcyA8LSBmYWN0b3IoYygiUG9vciIsICJJbXByb3ZlZCIsICJFeGNlbGxlbnQiKSwgb3JkZXJlZCA9IFQpDQpjbGFzcw0KYGBgDQoNCjIuIOmAmui/hyBsZXZlbHMg5Y+C5pWw5Lq65bel6K6+5a6a5Zug5a2Q5Z6L5pWw5o2u5ZCE5rC05bmz55qE6aG65bqPDQpgYGB7cn0NCmNsYXNzIDwtIGZhY3RvcigNCiAgYygiUG9vciIsICJJbXByb3ZlZCIsICJFeGNlbGxlbnQiKSwNCiAgbGV2ZWxzID0gYygiUG9vciIsICJJbXByb3ZlZCIsICJFeGNlbGxlbnQiKSwNCiAgb3JkZXJlZCA9IFQNCikNCmNsYXNzDQpgYGANCg0KMy4g5LiOIGxldmVscyDlh7rnjrDnmoTpobrluo/kv53mjIHkuIDoh7QNCg0K5Zyo5Yib5bu65Zug5a2Q5pe277yM5bCG5rC05bmz6K6+572u5Li6YHVuaXF1ZSh4KWDvvJvmiJbogIXlnKjliJvlu7rlm6DlrZDlkI7lho3lr7nlhbbkvb/nlKhgZmN0X2lub3JkZXIoKWDlh73mlbDvvIzpnIDopoFmb3JjYXRz5YyF77yI5YyF5ZCr5ZyodGlkeXZlcnNl5YWo5a625qG25Lit77yJ44CCDQoNCmBgYHtyfQ0KeCA8LSBjKCJBIiwgIlQiLCAiVCIsICJBIiwgIkMiLCAiSyIpDQp5IDwtIGZhY3Rvcih4LCBsZXZlbHMgPSB1bmlxdWUoeCkpDQp5DQoNCnggJT4lDQogIGZhY3RvcigpICU+JQ0KICBmY3RfaW5vcmRlcigpDQpgYGANCg0KDQojIyDkvb/nlKhmb3JjYXRz5aSE55CGZmFjdG9yDQoNCuaDs+imgSoq5Lul6Z2e5a2X5q+N6KGo6aG65bqP5pi+56S65a2X56ym5Liy5ZCR6YeP5pe277yI5aaC5LiA5Liq6L205Li656a75pWj5Y+Y6YeP55qE57uY5Zu+77yJ77yM5bCx6ZyA6KaB55So5Yiw5Zug5a2QKirjgIJmb3JjYXRzIOWMheS4reWQq+acieWkp+mHj+WkhOeQhuWboOWtkOeahOWHveaVsOOAgg0KDQrlvZPlrZfnrKbkuLLlkJHph4/kuK3mnInlvojlpJrph43lpI3lhYPntKDml7bvvIznlKggZmFjdG9yIO+8iOacrOi0qOaYr+aVtOaVsO+8ieWCqOWtmOavlOeUqOWtl+espuS4suiKguecgeepuumXtOOAguWboOatpO+8jFIg5Z+656GA5YyF5Lit55qE5b6I5aSa5Ye95pWw6YO96Ieq5Yqo5bCG5a2X56ym5Liy6L2s5o2i5Li65Zug5a2Q44CC6L+Z5oSP5ZGz552A5Zug5a2Q57uP5bi45Ye6546w5Zyo5bm25LiN55yf5q2j6YCC5ZCI5a6D5Lus55qE5Zyw5pa544CC5aW95Zyo5LiN55So5ouF5b+DIHRpZHl2ZXJzZSDkuK3kvJrlh7rnjrDov5nnp43pl67popjjgIINCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmNhdHMpDQpgYGANCg0KIyMjIOWIm+W7uuWboOWtkA0KDQropoHmg7PliJvlu7rkuIDkuKrlm6DlrZDvvIzlv4XpobvlhYjliJvlu7ogbGV2ZWxzIOWQkemHj++8mg0KDQpgYGB7cn0NCm1vbnRoX2xldmVscyA8LSBjKA0KICAiSmFuIiwgIkZlYiIsICJNYXIiLCAiQXByIiwgIk1heSIsICJKdW4iLA0KICAiSnVsIiwgIkF1ZyIsICJTZXAiLCAiT2N0IiwgIk5vdiIsICJEZWMiDQopDQpgYGANCg0KDQojIyMg5Zug5a2Q55qE5o6S5bqPDQoNCiMjIyMg55Sf5oiQ5Zug5a2Q5pe255qE6aG65bqPDQoNCmBgYHtyfQ0KIyMg6Z2e5Zug5a2Q55qE5o6S5bqP6YG15b6q5a2X5q+N6KGo5o6S5bqPDQp4MSA8LSBjKCJEZWMiLCAiQXByIiwgIkphbiIsICJNYXIiLCAiTWFyIiwgIkFwciIpDQp4MQ0Kc29ydCh4MSkNCg0KIyMg5Zug5a2Q77ya5oyJ6aKE5YWI5aOw5piO55qE5Zug5a2Q6aG65bqP5o6S5bqPDQp5MSA8LSBmYWN0b3IoeDEsIGxldmVscyA9IG1vbnRoX2xldmVscykNCnNvcnQoeTEpDQoNCiMjIOWboOWtkO+8muWtl+avjeihqOaOkuW6jw0KeTIgPC0gZmFjdG9yKHgxKSAjIOWmguaenOecgeeVpeS6huWumuS5ieawtOW5s+eahOi/meS4quatpemqpOebtOaOpeWIm+W7uuWboOWtkA0Kc29ydCh5MikgIyDkvJrmjInlrZfmr43pobrluo/mjpLluo8NCg0KIyMg5Zug5a2Q77ya5Lul5Yid5aeL5pWw5o2u55qE5Ye6546w6aG65bqP5L2c5Li66buY6K6k5o6S5bqPDQojIOS7pGxldmVsc+WPguaVsOS4unVuaXF1ZSh4Ke+8jOaIluWcqOWIm+W7uuWboOWtkOWQjuS9v+eUqGZjdF9pbm9yZGVyKCnlh73mlbANCnkzIDwtIGZhY3Rvcih4MSwgbGV2ZWxzID0gdW5pcXVlKHgxKSkNCnNvcnQoeTMpDQoNCnk0IDwtIGZhY3Rvcih4MSkgJT4lIGZjdF9pbm9yZGVyKCkNCnNvcnQoeTQpDQpgYGANCg0KIyMjIyBgZmN0X3JlbGV2ZWwoKWANCg0K6ZqP5pe26YeN5paw5omL5Yqo6K6+5a6a6aG65bqPDQoNCmBgYHtyfQ0KaW5jb21lIDwtIGMoImxvdyIsICJoaWdoIiwgIm1lZGl1bSIsICJtZWRpdW0iLCAibG93IiwgImhpZ2giLCAiaGlnaCIpDQp4IDwtIGZhY3RvcihpbmNvbWUpDQp4ICU+JSBmY3RfcmVsZXZlbChsZXZlbHMgPSBjKCJoaWdoIiwgIm1lZGl1bSIsICJsb3ciKSkNCnggJT4lIGZjdF9yZWxldmVsKGxldmVscyA9IGMoIm1lZGl1bSIpKQ0KeCAlPiUgZmN0X3JlbGV2ZWwoIm1lZGl1bSIsIGFmdGVyID0gSW5mKQ0KYGBgDQoNCiMjIyMgYGZjdF9yZXYoKWANCg0K5a+55Zug5a2Q6YCG5bqPDQoNCmBgYHtyfQ0KZCA8LSB0aWJibGUoDQogIHggPSBjKCJhIiwgImEiLCAiYiIsICJiIiwgImMiLCAiYyIpLA0KICB5ID0gYygyLCAyLCAxLCA1LCAwLCAzKQ0KKQ0KDQpkICU+JQ0KICBtdXRhdGUoeCA9IGZjdF9yZXYoeCkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geSkpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KIyMjIyBgZmN0X3Jlb3JkZXIoKWANCg0KYGZjdF9yZW9yZGVyKHgsIHksIC5mdW4gPSAsIC5kZXNjID0gRkFMU0UpYA0KDQrmjIkgeCDliIbnsbvvvIzmoLnmja7mr4/kuKrliIbnsbvlj5jph4/lr7nlupQgeSDlgLznmoTlkJHph4/nu4/ov4cgLmZ1biDov5DnrpflkI7nmoTnu5PmnpzmnaXlr7kgeCDmjpLluo8NCg0KYGBge3J9DQpkICU+JQ0KICAjIHjovbTlm6DlrZDnmoTpobrluo/kuLrlrZfmr43pobrluo8NCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHkpKSArDQogIGdlb21fcG9pbnQoKQ0KDQpkICU+JQ0KICAjIHjlm6DlrZDnmoTpobrluo/lj5jkuLrmjInnhafmr4/nu4TmiJDlkZjnmoR55YC855qE5Lit5L2N5pWw55qE6ZmN5bqP5p2l5o6S5bqPDQogIG11dGF0ZSh4ID0gZmN0X3Jlb3JkZXIoeCwgeSwgLmZ1biA9IG1lZGlhbiwgLmRlc2MgPSBUUlVFKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IHgsIHkgPSB5KSkgKw0KICBnZW9tX3BvaW50KCkNCg0KZCAlPiUNCiAgIyB45Zug5a2Q55qE6aG65bqP5Y+Y5Li65oyJ54Wn5q+P57uE5oiQ5ZGY55qEeeWAvOeahOacgOWwj+WAvOeahOmZjeW6j+adpeaOkuW6jw0KICBtdXRhdGUoeCA9IGZjdF9yZW9yZGVyKHgsIHksIC5mdW4gPSBtZWRpYW4sIC5kZXNjID0gVFJVRSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geSkpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KIyMjIOaOkuafpei+k+WFpemUmeivrw0KDQpgYGB7cn0NCngyIDwtIGMoIkRlYyIsICJBcHIiLCAiSmFtIiwgIk1hciIpDQp5NSA8LSBmYWN0b3IoeDIsIGxldmVscyA9IG1vbnRoX2xldmVscykNCnk1ICMg5LiN5YaN5rC05bmz6IyD5Zu05YaF55qE5YC86Ieq5Yqo6L2s5o2i5Li6TkENCmBgYA0KDQojIyMg5p+l55yL5rC05bmz77yabGV2ZWxzKCkNCmBgYHtyfQ0KbGV2ZWxzKHkxKQ0KbGV2ZWxzKHkyKQ0KbGV2ZWxzKHkzKQ0KYGBgDQoNCiMjIyDkvovvvJrnu7zlkIjnpL7kvJrosIPmn6UNCg0KYGBge3J9DQpmb3JjYXRzOjpnc3NfY2F0ICU+JSBoZWFkKG4gPSA1KSAjIOWPkeeOsOS4gOS6m+Wtl+auteaYr2ZhY3Rvcg0KDQojIyDmn6XnnIvlm6DlrZDmsLTlubMNCmdzc19jYXQgJT4lIGNvdW50KHJhY2UpICMgY291bnQoKeWHveaVsA0KZ2dwbG90KGdzc19jYXQsIGFlcyhyYWNlKSkgKw0KICBnZW9tX2JhcigpICMg5p2h5b2i5Zu+77yM5LiN5pi+56S66K6w5pWw5Li6MOeahOawtOW5sw0KZ2dwbG90KGdzc19jYXQsIGFlcyhyYWNlKSkgKw0KICBnZW9tX2JhcigpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShkcm9wID0gRkFMU0UpICMg5pi+56S66K6w5pWw5Li6MOeahOawtOW5sw0KYGBgDQoNCiMjIyDkv67mlLnlm6DlrZDnmoTmsLTlubMNCg0K5L+u5pS55rC05bmz5LiN5LuF5Y+v5Lul5L2/5b6X5Zu+5b2i5qCH562+5pu0576O6KeC5riF5pmw77yM5Lul5ruh6Laz5Ye654mI5Y+R6KGM55qE6KaB5rGC77yM6L+Y5Y+v5Lul5bCG5rC05bmz5rGH6ZuG5oiQ5pu06auY5bGC5qyh55qE5pi+56S644CCDQoNCuS/ruaUueawtOW5s+acgOW4uOeUqOOAgeacgOW8uuWkp+eahOW3peWFt+aYryBgZmN0X3JlY29kZSgpYCDlh73mlbDvvIzlroPlj6/ku6Xlr7nmr4/kuKrmsLTlubPov5vooYzkv67mlLnmiJbph43mlrDnvJbnoIHvvIzorqnmsqHmnInmmI7noa7mj5Dlj4rnmoTmsLTlubPkv53mjIHljp/moLfvvIzlpoLmnpzkuI3lsI/lv4Pkv67mlLnkuobkuIDkuKrkuI3lrZjlnKjnmoTmsLTlubPvvIzlroPkuZ/kvJrnu5nlh7rorablkYrjgIINCg0KYGBge3J9DQpnc3NfY2F0ICU+JSBjb3VudChwYXJ0eWlkKQ0KDQpnc3NfY2F0ICU+JQ0KICBtdXRhdGUoDQogICAgcGFydHlpZCA9IGZjdF9yZWNvZGUoDQogICAgICBwYXJ0eWlkLA0KICAgICAgIlJlcHVibGljYW4sIHN0cm9uZyIgPSAiU3Ryb25nIHJlcHVibGljYW4iLA0KICAgICAgIlJlcHVibGljYW4sIHdlYWsiID0gIk5vdCBzdHIgcmVwdWJsaWNhbiIsDQogICAgICAiSW5kZXBlbmRlbnQsIG5lYXIgcmVwIiA9ICJJbmQsbmVhciByZXAiLA0KICAgICAgIkluZGVwZW5kZW50LCBuZWFyIGRlbSIgPSAiSW5kLG5lYXIgZGVtIiwNCiAgICAgICJEZW1vY3JhdCwgd2VhayIgPSAiTm90IHN0ciBkZW1vY3JhdCIsDQogICAgICAiRGVtb2NyYXQsIHN0cm9uZyIgPSAiU3Ryb25nIGRlbW9jcmF0Ig0KICAgICkNCiAgKSAlPiUNCiAgY291bnQocGFydHlpZCkNCmBgYA0KDQrlj6/ku6XlsIblpJrkuKrljp/msLTlubPotYvnu5nlkIzkuIDkuKrmlrDmsLTlubPvvIzov5nmoLflsLHlj6/ku6XlkIjlubbljp/mnaXnmoTliIbnsbvvvJoNCg0KYGBge3J9DQpnc3NfY2F0ICU+JQ0KICBtdXRhdGUoDQogICAgcGFydHlpZCA9IGZjdF9yZWNvZGUoDQogICAgICBwYXJ0eWlkLA0KICAgICAgIlJlcHVibGljYW4sIHN0cm9uZyIgPSAiU3Ryb25nIHJlcHVibGljYW4iLA0KICAgICAgIlJlcHVibGljYW4sIHdlYWsiID0gIk5vdCBzdHIgcmVwdWJsaWNhbiIsDQogICAgICAiSW5kZXBlbmRlbnQsIG5lYXIgcmVwIiA9ICJJbmQsbmVhciByZXAiLA0KICAgICAgIkluZGVwZW5kZW50LCBuZWFyIGRlbSIgPSAiSW5kLG5lYXIgZGVtIiwNCiAgICAgICJEZW1vY3JhdCwgd2VhayIgPSAiTm90IHN0ciBkZW1vY3JhdCIsDQogICAgICAiRGVtb2NyYXQsIHN0cm9uZyIgPSAiU3Ryb25nIGRlbW9jcmF0IiwNCiAgICAgICJPdGhlciIgPSAiTm8gYW5zd2VyIiwNCiAgICAgICJPdGhlciIgPSAiRG9uJ3Qga25vdyIsDQogICAgICAiT3RoZXIiID0gIk90aGVyIHBhcnR5Ig0KICAgICkNCiAgKSAlPiUNCiAgY291bnQocGFydHlpZCkNCmBgYA0KDQrkuZ/lj6/ku6Xkvb/nlKggZmN0X3JlY29kZSgpIOWHveaVsOeahOWPmOS9kyBmY3RfY29sbGFwc2UoKSDlh73mlbDjgILlr7nkuo7mr4/kuKrmlrDmsLTlubPvvIzkvaDpg73lj6/ku6Xmj5DkvpvkuIDkuKrljIXlkKvljp/msLTlubPnmoTlkJHph4/vvJoNCg0KYGBge3J9DQpnc3NfY2F0ICU+JQ0KICBtdXRhdGUocGFydHlpZCA9IGZjdF9jb2xsYXBzZSgNCiAgICBwYXJ0eWlkLA0KICAgIG90aGVyID0gYygiTm8gYW5zd2VyIiwgIkRvbid0IGtub3ciLCAiT3RoZXIgcGFydHkiKSwNCiAgICByZXAgPSBjKCJTdHJvbmcgcmVwdWJsaWNhbiIsICJOb3Qgc3RyIHJlcHVibGljYW4iKSwNCiAgICBpbmQgPSBjKCJJbmQsbmVhciByZXAiLCAiSW5kZXBlbmRlbnQiLCAiSW5kLG5lYXIgZGVtIiksDQogICAgZGVtID0gYygiTm90IHN0ciBkZW1vY3JhdCIsICJTdHJvbmcgZGVtb2NyYXQiKQ0KICApKSAlPiUNCiAgY291bnQocGFydHlpZCkNCmBgYA0K