精神科後期研修のおすすめ書籍アンケートの前調査ですが、現時点で収集したデータについて、csvファイルの読み込み・データクリーニング・図示までをRで操作してみました。この文章は R Notebookで作成しています。コードは右上の CODEからダウンロードすることができます。

初期設定

  • Rを始めるときは、その都度使用する道具(パッケージ)を選択する必要があります。今回使用するパッケージ(群)は tidyverse のみです。
if (!require("tidyverse")) install.packages("tidyverse")
library(tidyverse) 

データの読み込み

  • read_csv() で .csv ファイルを読み込みます。
  • 読みこんだ .csv ファイルを <- で dat と名前をつけます。
dat <- read_csv("../materials/list_210423.csv")
Missing column names filled in: 'X2' [2], 'X5' [5]
─ Column specification ──────────────
cols(
  Timestamp = col_character(),
  X2 = col_logical(),
  年齢をお選びください = col_character(),
  `オススメ書籍はどれですか?(複数回答可)` = col_character(),
  X5 = col_character()
)
dat

と入力することで、読み込んだ csv ファイルを表示します。

dat

列名の変更

rename() を使って扱いやすい列名に変更しておきます。

dat <- dat %>% 
  rename(age = "年齢をお選びください",
         book = "オススメ書籍はどれですか?(複数回答可)",
         comment = "X5")
  • 必要の無い列を select() で選択して外します。
  • 行の番号を rownames_to_column() で追加します。
  • これを dat1 とします。
dat1 <- dat %>%
  select(-Timestamp, -X2, -comment) %>% 
  rownames_to_column() 
dat1

最初に比べて必要な列が抽出でき、すっきりとしました。

列を整える

book列は複数選択したものが セミコロン(;)でつながっています。

dat1 %>% select(book)

これを、 separate() で ; を区切りに別の列に展開していきます。


col_list <- 1:20 %>% str_c("f", .)

dat1 <- dat1 %>% separate(book,
                 into =col_list,
                 sep =";",
                 extra ="merge")
Expected 20 pieces. Missing pieces filled with `NA` in 13 rows [1, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 17].
dat1

横長の列ができました。

  • これを、データで扱いやすいように、pivot_longer()で、縦長のデータセットに変形します。
  • 本の名前を book 列に集めます。
  • 併せて、filter(!is.na(book)) とすることで、book 列が欠損値である行を除きます。
dat1 <- dat1 %>% 
  pivot_longer(
  f1:f20,
  names_to = "f",
  values_to = "book"
) %>% 
  filter(!is.na(book))

dat1

データの集計

  • group_by(book)として、本の種類毎に集計(count列)していきます。 その後に、arrange()を使って、1. count の大きい順、2. bookの文字数が多い順、に並べて、行番号を rowname_to_column() で挿入し、mutate()で整数にデータ型を指定します。
  • この行番号は、図の作成のときに使用します。
dat2 <- dat1 %>% 
  group_by(book) %>% 
  summarise(count = n())

dat2 <- dat2 %>% 
  arrange(desc(count), desc(str_length(book))) %>% 
  rownames_to_column() %>% 
  mutate(across(rowname, ~ as.integer(.x)))

dat2

棒グラフの図示

  • これから ggplot() を使って、棒グラフを作成していきます。
  • aes() で x軸を、book列、 y軸を count列で指定します。
  • x軸の順番を reorder() の X で先程作成した rowname の降順 desc() として指定します。
g1 <- dat2 %>% 
  ggplot() +
  aes(x = reorder( x = book, X = desc(rowname)), y = count, fill = count) +
  geom_col()

g1

  • x軸の文字が潰れて見えなくなっています。x軸の傾きを調整することもできますが、今回は coord_flip() 縦と横を変形します。
  • ggplot() は 先程作成したオブジェクト g1 に + で続きを入力することで、図の設定を書き足していくことができます。
g2 <- g1 + 
  coord_flip() 

g2

  • x軸とy軸の位置を scale_x_discrete(), scale_y_continuous() で調整します。
  • scale_fill_continuous() を使って、任意の色でグラデーションを付けてみます。
  • labs()を使って x軸とy軸を非表示にします。
g3 <- g2 + 
  scale_x_discrete(position = "top") +
  scale_y_continuous(position = "right") +
  scale_fill_continuous(low = "#DD8A90", high = "#BD4670") +
  labs(x = "", y = "") +
  theme_minimal() + 
  theme(legend.position = "none")

g3

関数を作成する

これまでの dat2 以降の一連の流れを関数にしてみます

  • データの集計を、make_aggregate()
  • グラフの作成を、make_graph()

とします。

make_aggregate <- function(df) {
  df %>%
    group_by(book) %>% 
    summarise(count = n()) %>% 
    arrange(desc(count), desc(str_length(book))) %>% 
    rownames_to_column() %>% 
    mutate(across(rowname, ~ as.integer(.x)))
}

make_graph <- function(df) {
  df %>%
    ggplot() +
    aes(x = reorder( x = book, X = desc(rowname)), y = count, fill = count) +
    geom_col() + 
    coord_flip() +
    scale_x_discrete(position = "top") +
    scale_y_continuous(position = "right") +
    scale_fill_continuous(low = "#DD8A90", high = "#BD4670") +
    labs(x = "", y = "") +
    theme_minimal() + 
    theme(legend.position = "none")
}

こうして、作成した関数にデータを渡すだけで同様の作業が繰り返すことができます。

dat1 %>% 
  make_aggregate() %>%
  make_graph()

年代ごとに集計する

今回集計した age 列を活用してみます。 group_by()に book に加えて age を入力することで、ageとbookで集計する事ができます。

dat3 <- dat1 %>% 
  group_by(book, age) %>% 
  summarise(count = n()) 
`summarise()` has grouped output by 'book'. You can override using the `.groups` argument.
dat3

並び順を先程作成した dat2 と一緒にします。

  • right_join()を使って、先程作成したdat2の並び順 (rowname) をdat3に突合(merge) します。
dat3 <- dat2 %>% 
  select(rowname, book) %>% 
  right_join(., dat3)
Joining, by = "book"
dat3

年代ごとの棒グラフを図示

先程完成した棒グラフ make_graph() を使ってみます。

h1 <- dat3 %>% make_graph()
h1

このままだと、book列が併せて集計されたままになるので、 facet_wrap()を使って、年代ごとに分けて表示します。

h2 <- h1 + facet_wrap(age ~ .) 
h2

図の保存

ここまでで作成したg3とh1をggsave()を使って、.tiffとして保存します。compression = “lzw” を入れることで、サイズを大幅に圧縮して保存することができます。

ggsave(plot = g3, filename = "figure1.tiff", width = 10, height = 10, dpi =300, compression = "lzw")

ggsave(plot = h2, filename = "figure2.tiff", width = 10, height = 10, dpi =300, compression = "lzw")

pdfで保存することもできます。


ggsave(plot = g3, filename = "figure1.pdf",  device = cairo_pdf, width = 10, height = 10, dpi =300)

ggsave(plot = h2, "figure2.pdf",  device = cairo_pdf, width = 10, height = 10, dpi =300)

コメント欄

コメント欄をarrange()で文字数の多い順に並べたあと、select()で抜き出して表示します。

t1 <- dat %>% 
  arrange(desc(str_length(comment))) %>% 
  select(comment) %>% 
  na.omit() 

t1

コメント欄を .png で保存

gt パッケージを使って、コメント欄を .png で保存してみます。 その他、pdf や html などで保存することも可能です。

if (!require("gt")) install.packages("gt")
library(gt) 
t2 <- t1 %>% gt()

gtsave(data = t2, "table1.png")
gtsave(data = t2, "table1.html")
gtsave(data = t2, "table1.pdf")

t2
comment
「ケースでわかる! 精神科治療ガイドラインのトリセツ」 EGUIDEプロジェクトによるもので実臨床で困った時にどう問題を整理するかやガイドラインを元にどう考えるかが分かりやすく解説されています。 「脳波判読オープンキャンパス 誰でも学べる7STEP」 電極の名前などの基礎の基礎から、波の見方、部位の特定、デジタル脳波を読むための基本など古典的教科書を見ても今一つよくわからなかったところもわかりやすく解説されています。 「ポケット臨床脳波」 こちらはいまだ記録紙による脳波が現役のところの方に。必要最低限のメモとして、また今は薬屋さんからもらえなくなった脳波定規が付録で付いてきます。 「精神・心理症状学ハンドブック[第3版]」 精神科症候学について、精神機能別にまとめられているので、わかりやすいと思います。
今日の精神疾患治療指針(いわゆるアンチョコ本ですが、右も左も分からず外来をやるときには役立ちました)、精神・心理機能評価ハンドブック(各種スケールの解釈の仕方が載っていて役立ちます)、メモリークリニック診療マニュアル(認知症外来をやっているときに要点がコンパクトに載っていて役立ちます)、精神科臨床144のQ&A(他の本には載っていないような、かゆいところに手の届くトピックが役立ちます)、精神科リハビリテーション・ワークブック(患者さんに疾病のことについて、わかりやすく説明をするときに役立ちます)
・精神医学入門 西丸四方 今ではなかなか見られない患者さんの写真が載っていてイメージしやすい。 ・健康ライブラリーシリーズ 患者さんが読みやすい、疾患教育にも有用 ・摂食障害 切池信夫 摂食障害のイロハを学ぶのに ・精神科身体合併症マニュアル 当直のお供に ・医学論文のための統計手法の選び方使い方 阿部貴行 読みやすい統計の本
読んだことがない本も多かったのですが、見覚えのある本に関してはほぼ全てをオススメとさせていただきました。精神科レジデントが自前ですべて持っている必要はないと思いますが、きちんとした教科書やDSM-5のマニュアルなどは医局で必ず1冊あった方が良いと思います。
私自身の向き不向きというものもあるでしょうが、やはりDSM-5とガイドラインが基本かと思います。下手にあれこれ手を出すと苦労します。
CBT好きとしては、精神療法の基本: 支持から認知行動療法まで、がおすすめです。
今日の治療薬(各章初めのまとめ文章が良い)、精神科レジデントマニュアル
精神療法家の仕事(どの学年になっても、その時々で学びがあります)
現代臨床精神医学 読みやすい上にある程度網羅されているため。
大人の発達障害ってそういうことだったのか
テスト:テストなので
精神療法の第一歩

作成したファイルの確認

それぞれ作成したファイル(pdf)は下記の通り

comment.pdf は一旦htmlにしたものをブラウザ(Chrome)からpdfに変換しました。

また、アンケート終了後に、再度 R で解析します。

ご意見や質問等あれば、お問い合わせや、twitter からお気軽にご連絡ください。

LS0tCnRpdGxlOiAi57K+56We56eR5b6M5pyf56CU5L+u44GK44GZ44GZ44KB5pu457GNIgpzdWJ0aXRsZTogInBpbG90IOeJiCIKYXV0aG9yOiAiYXV0aG9yOiBbc2hvZWkwNV0oaHR0cHM6Ly9wbGF6YS51bWluLmFjLmpwL3Nob2VpMDUvKXt0YXJnZXQ9J19ibGFuayd9IgpkYXRlOiAiZGF0ZTogMjAyMS8wNS8wNiIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgIHRvYzogeWVzCiAgICAgdG9jX2RlcHRoOiAzCiAgICAgdG9jX2Zsb2F0OiB5ZXMKICAgICBkZl9wcmludDogInBhZ2VkIgotLS0KCioq57K+56We56eR5b6M5pyf56CU5L+u44Gu44GK44GZ44GZ44KB5pu457GN44Ki44Oz44Kx44O844OIKirjga7liY3oqr/mn7vjgafjgZnjgYzjgIHnj77mmYLngrnjgaflj47pm4bjgZfjgZ/jg4fjg7zjgr/jgavjgaTjgYTjgabjgIFjc3bjg5XjgqHjgqTjg6vjga7oqq3jgb/ovrzjgb/jg7vjg4fjg7zjgr/jgq/jg6rjg7zjg4vjg7PjgrDjg7vlm7PnpLrjgb7jgafjgpJS44Gn5pON5L2c44GX44Gm44G/44G+44GX44Gf44CC44GT44Gu5paH56ug44GvIFIgTm90ZWJvb2vjgafkvZzmiJDjgZfjgabjgYTjgb7jgZnjgIIqKuOCs+ODvOODieOBr+WPs+S4iuOBriBDT0RF44GL44KJ44OA44Km44Oz44Ot44O844OJKirjgZnjgovjgZPjgajjgYzjgafjgY3jgb7jgZnjgIIKCiMg5Yid5pyf6Kit5a6aCgotIFLjgpLlp4vjgoHjgovjgajjgY3jga/jgIHjgZ3jga7pg73luqbkvb/nlKjjgZnjgovpgZPlhbfvvIjjg5Hjg4PjgrHjg7zjgrjvvInjgpLpgbjmip7jgZnjgovlv4XopoHjgYzjgYLjgorjgb7jgZnjgILku4rlm57kvb/nlKjjgZnjgovjg5Hjg4PjgrHjg7zjgrgo576kKeOBryAqKnRpZHl2ZXJzZSoqIOOBruOBv+OBp+OBmeOAggoKYGBge3J9CmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmxpYnJhcnkodGlkeXZlcnNlKSAKYGBgCgojIOODh+ODvOOCv+OBruiqreOBv+i+vOOBvwoKLSByZWFkX2NzdigpIOOBpyAuY3N2IOODleOCoeOCpOODq+OCkuiqreOBv+i+vOOBv+OBvuOBmeOAggotIOiqreOBv+OBk+OCk+OBoCAuY3N2IOODleOCoeOCpOODq+OCkiA8LSDjgacgZGF0IOOBqOWQjeWJjeOCkuOBpOOBkeOBvuOBmeOAggoKYGBge3J9CmRhdCA8LSByZWFkX2NzdigiLi4vbWF0ZXJpYWxzL2xpc3RfMjEwNDIzLmNzdiIpCmBgYAoKYGBgCmRhdApgYGAK44Go5YWl5Yqb44GZ44KL44GT44Go44Gn44CB6Kqt44G/6L6844KT44GgIGNzdiDjg5XjgqHjgqTjg6vjgpLooajnpLrjgZfjgb7jgZnjgIIKCmBgYHtyfQpkYXQKYGBgCgojIOWIl+WQjeOBruWkieabtAoKcmVuYW1lKCkg44KS5L2/44Gj44Gm5omx44GE44KE44GZ44GE5YiX5ZCN44Gr5aSJ5pu044GX44Gm44GK44GN44G+44GZ44CCCgpgYGB7cn0KZGF0IDwtIGRhdCAlPiUgCiAgcmVuYW1lKGFnZSA9ICLlubTpvaLjgpLjgYrpgbjjgbPjgY/jgaDjgZXjgYQiLAogICAgICAgICBib29rID0gIuOCquOCueOCueODoeabuOexjeOBr+OBqeOCjOOBp+OBmeOBi++8n++8iOikh+aVsOWbnuetlOWPr++8iSIsCiAgICAgICAgIGNvbW1lbnQgPSAiWDUiKQpgYGAKCi0g5b+F6KaB44Gu54Sh44GE5YiX44KSIHNlbGVjdCgpIOOBp+mBuOaKnuOBl+OBpuWkluOBl+OBvuOBmeOAggotIOihjOOBrueVquWPt+OCkiByb3duYW1lc190b19jb2x1bW4oKSDjgafov73liqDjgZfjgb7jgZnjgIIKLSDjgZPjgozjgpIgZGF0MSDjgajjgZfjgb7jgZnjgIIKCmBgYHtyfQpkYXQxIDwtIGRhdCAlPiUKICBzZWxlY3QoLVRpbWVzdGFtcCwgLVgyLCAtY29tbWVudCkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigpIApgYGAKCmBgYHtyfQpkYXQxCmBgYAoK5pyA5Yid44Gr5q+U44G544Gm5b+F6KaB44Gq5YiX44GM5oq95Ye644Gn44GN44CB44GZ44Gj44GN44KK44Go44GX44G+44GX44Gf44CCCgojIOWIl+OCkuaVtOOBiOOCiwoKYm9va+WIl+OBr+ikh+aVsOmBuOaKnuOBl+OBn+OCguOBruOBjCAqKuOCu+ODn+OCs+ODreODsyg7KSoq44Gn44Gk44Gq44GM44Gj44Gm44GE44G+44GZ44CCCgpgYGB7cn0KZGF0MSAlPiUgc2VsZWN0KGJvb2spCmBgYAoK44GT44KM44KS44CBIHNlcGFyYXRlKCkg44GnICoqOyDjgpLljLrliIfjgorjgavliKXjga7liJfjgavlsZXplosqKuOBl+OBpuOBhOOBjeOBvuOBmeOAggoKYGBge3J9Cgpjb2xfbGlzdCA8LSAxOjIwICU+JSBzdHJfYygiZiIsIC4pCgpkYXQxIDwtIGRhdDEgJT4lIHNlcGFyYXRlKGJvb2ssCiAgICAgICAgICAgICAgICAgaW50byA9Y29sX2xpc3QsCiAgICAgICAgICAgICAgICAgc2VwID0iOyIsCiAgICAgICAgICAgICAgICAgZXh0cmEgPSJtZXJnZSIpCgpkYXQxCmBgYAoK5qiq6ZW344Gu5YiX44GM44Gn44GN44G+44GX44Gf44CCCgotIOOBk+OCjOOCkuOAgeODh+ODvOOCv+OBp+aJseOBhOOChOOBmeOBhOOCiOOBhuOBq+OAgXBpdm90X2xvbmdlcigp44Gn44CB57im6ZW344Gu44OH44O844K/44K744OD44OI44Gr5aSJ5b2i44GX44G+44GZ44CCCi0g5pys44Gu5ZCN5YmN44KSIGJvb2sg5YiX44Gr6ZuG44KB44G+44GZ44CCCi0g5L2144Gb44Gm44CBZmlsdGVyKCFpcy5uYShib29rKSkg44Go44GZ44KL44GT44Go44Gn44CBYm9vayDliJfjgYzmrKDmkI3lgKTjgafjgYLjgovooYzjgpLpmaTjgY3jgb7jgZnjgIIKCmBgYHtyfQpkYXQxIDwtIGRhdDEgJT4lIAogIHBpdm90X2xvbmdlcigKICBmMTpmMjAsCiAgbmFtZXNfdG8gPSAiZiIsCiAgdmFsdWVzX3RvID0gImJvb2siCikgJT4lIAogIGZpbHRlcighaXMubmEoYm9vaykpCgpkYXQxCmBgYAoKIyDjg4fjg7zjgr/jga7pm4boqIgKCi0gZ3JvdXBfYnkoYm9vaynjgajjgZfjgabjgIHmnKzjga7nqK7poZ7mr47jgavpm4boqIjvvIhjb3VudOWIl++8ieOBl+OBpuOBhOOBjeOBvuOBmeOAggrjgZ3jga7lvozjgavjgIFhcnJhbmdlKCnjgpLkvb/jgaPjgabjgIExLiBjb3VudCDjga7lpKfjgY3jgYTpoIbjgIEyLiBib29r44Gu5paH5a2X5pWw44GM5aSa44GE6aCG44CB44Gr5Lim44G544Gm44CB6KGM55Wq5Y+344KSIHJvd25hbWVfdG9fY29sdW1uKCkg44Gn5oy/5YWl44GX44CBbXV0YXRlKCnjgafmlbTmlbDjgavjg4fjg7zjgr/lnovjgpLmjIflrprjgZfjgb7jgZnjgIIKLSDjgZPjga7ooYznlarlj7fjga/jgIHlm7Pjga7kvZzmiJDjga7jgajjgY3jgavkvb/nlKjjgZfjgb7jgZnjgIIKCmBgYHtyfQpkYXQyIDwtIGRhdDEgJT4lIAogIGdyb3VwX2J5KGJvb2spICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpkYXQyIDwtIGRhdDIgJT4lIAogIGFycmFuZ2UoZGVzYyhjb3VudCksIGRlc2Moc3RyX2xlbmd0aChib29rKSkpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUgCiAgbXV0YXRlKGFjcm9zcyhyb3duYW1lLCB+IGFzLmludGVnZXIoLngpKSkKCmRhdDIKYGBgCgojIOajkuOCsOODqeODleOBruWbs+ekugoKLSDjgZPjgozjgYvjgokgZ2dwbG90KCkg44KS5L2/44Gj44Gm44CB5qOS44Kw44Op44OV44KS5L2c5oiQ44GX44Gm44GE44GN44G+44GZ44CCCi0gYWVzKCkg44GnIHjou7jjgpLjgIFib29r5YiX44CBIHnou7jjgpIgY291bnTliJfjgafmjIflrprjgZfjgb7jgZnjgIIKLSB46Lu444Gu6aCG55Wq44KSIHJlb3JkZXIoKSDjga4gWCDjgaflhYjnqIvkvZzmiJDjgZfjgZ8gcm93bmFtZSDjga7pmY3poIYgZGVzYygpIOOBqOOBl+OBpuaMh+WumuOBl+OBvuOBmeOAggoKYGBge3J9CmcxIDwtIGRhdDIgJT4lIAogIGdncGxvdCgpICsKICBhZXMoeCA9IHJlb3JkZXIoIHggPSBib29rLCBYID0gZGVzYyhyb3duYW1lKSksIHkgPSBjb3VudCwgZmlsbCA9IGNvdW50KSArCiAgZ2VvbV9jb2woKQoKZzEKYGBgCgotIHjou7jjga7mloflrZfjgYzmvbDjgozjgabopovjgYjjgarjgY/jgarjgaPjgabjgYTjgb7jgZnjgIJ46Lu444Gu5YK+44GN44KS6Kq/5pW044GZ44KL44GT44Go44KC44Gn44GN44G+44GZ44GM44CB5LuK5Zue44GvIGNvb3JkX2ZsaXAoKSDnuKbjgajmqKrjgpLlpInlvaLjgZfjgb7jgZnjgIIKLSBnZ3Bsb3QoKSDjga8g5YWI56iL5L2c5oiQ44GX44Gf44Kq44OW44K444Kn44Kv44OIIGcxIOOBqyArIOOBp+e2muOBjeOCkuWFpeWKm+OBmeOCi+OBk+OBqOOBp+OAgeWbs+OBruioreWumuOCkuabuOOBjei2s+OBl+OBpuOBhOOBj+OBk+OBqOOBjOOBp+OBjeOBvuOBmeOAggoKYGBge3J9CmcyIDwtIGcxICsgCiAgY29vcmRfZmxpcCgpIAoKZzIKYGBgCgotIHjou7jjgah56Lu444Gu5L2N572u44KSIHNjYWxlX3hfZGlzY3JldGUoKSwgc2NhbGVfeV9jb250aW51b3VzKCkg44Gn6Kq/5pW044GX44G+44GZ44CCCi0gc2NhbGVfZmlsbF9jb250aW51b3VzKCkg44KS5L2/44Gj44Gm44CB5Lu75oSP44Gu6Imy44Gn44Kw44Op44OH44O844K344On44Oz44KS5LuY44GR44Gm44G/44G+44GZ44CCCi0gbGFicygp44KS5L2/44Gj44GmIHjou7jjgah56Lu444KS6Z2e6KGo56S644Gr44GX44G+44GZ44CCCgpgYGB7cn0KZzMgPC0gZzIgKyAKICBzY2FsZV94X2Rpc2NyZXRlKHBvc2l0aW9uID0gInRvcCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMocG9zaXRpb24gPSAicmlnaHQiKSArCiAgc2NhbGVfZmlsbF9jb250aW51b3VzKGxvdyA9ICIjREQ4QTkwIiwgaGlnaCA9ICIjQkQ0NjcwIikgKwogIGxhYnMoeCA9ICIiLCB5ID0gIiIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgpnMwpgYGAKCiMg6Zai5pWw44KS5L2c5oiQ44GZ44KLCgrjgZPjgozjgb7jgafjga4gZGF0MiDku6XpmY3jga7kuIDpgKPjga7mtYHjgozjgpLplqLmlbDjgavjgZfjgabjgb/jgb7jgZkKCi0g44OH44O844K/44Gu6ZuG6KiI44KS44CBbWFrZV9hZ2dyZWdhdGUoKQotIOOCsOODqeODleOBruS9nOaIkOOCkuOAgW1ha2VfZ3JhcGgoKQoK44Go44GX44G+44GZ44CCCgpgYGB7cn0KbWFrZV9hZ2dyZWdhdGUgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiAlPiUKICAgIGdyb3VwX2J5KGJvb2spICU+JSAKICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lIAogICAgYXJyYW5nZShkZXNjKGNvdW50KSwgZGVzYyhzdHJfbGVuZ3RoKGJvb2spKSkgJT4lIAogICAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lIAogICAgbXV0YXRlKGFjcm9zcyhyb3duYW1lLCB+IGFzLmludGVnZXIoLngpKSkKfQoKbWFrZV9ncmFwaCA8LSBmdW5jdGlvbihkZikgewogIGRmICU+JQogICAgZ2dwbG90KCkgKwogICAgYWVzKHggPSByZW9yZGVyKCB4ID0gYm9vaywgWCA9IGRlc2Mocm93bmFtZSkpLCB5ID0gY291bnQsIGZpbGwgPSBjb3VudCkgKwogICAgZ2VvbV9jb2woKSArIAogICAgY29vcmRfZmxpcCgpICsKICAgIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKHBvc2l0aW9uID0gInJpZ2h0IikgKwogICAgc2NhbGVfZmlsbF9jb250aW51b3VzKGxvdyA9ICIjREQ4QTkwIiwgaGlnaCA9ICIjQkQ0NjcwIikgKwogICAgbGFicyh4ID0gIiIsIHkgPSAiIikgKwogICAgdGhlbWVfbWluaW1hbCgpICsgCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCn0KYGBgCgrjgZPjgYbjgZfjgabjgIHkvZzmiJDjgZfjgZ/plqLmlbDjgavjg4fjg7zjgr/jgpLmuKHjgZnjgaDjgZHjgaflkIzmp5jjga7kvZzmpa3jgYznubDjgorov5TjgZnjgZPjgajjgYzjgafjgY3jgb7jgZnjgIIKCmBgYHtyfQpkYXQxICU+JSAKICBtYWtlX2FnZ3JlZ2F0ZSgpICU+JQogIG1ha2VfZ3JhcGgoKQpgYGAKCiMg5bm05Luj44GU44Go44Gr6ZuG6KiI44GZ44KLCgrku4rlm57pm4boqIjjgZfjgZ8gYWdlIOWIl+OCkua0u+eUqOOBl+OBpuOBv+OBvuOBmeOAggpncm91cF9ieSgp44GrIGJvb2sg44Gr5Yqg44GI44GmIGFnZSDjgpLlhaXlipvjgZnjgovjgZPjgajjgafjgIFhZ2Xjgahib29r44Gn6ZuG6KiI44GZ44KL5LqL44GM44Gn44GN44G+44GZ44CCCgpgYGB7cn0KZGF0MyA8LSBkYXQxICU+JSAKICBncm91cF9ieShib29rLCBhZ2UpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpIAoKZGF0MwpgYGAKCuS4puOBs+mghuOCkuWFiOeoi+S9nOaIkOOBl+OBnyBkYXQyIOOBqOS4gOe3kuOBq+OBl+OBvuOBmeOAggoKLSByaWdodF9qb2luKCnjgpLkvb/jgaPjgabjgIHlhYjnqIvkvZzmiJDjgZfjgZ9kYXQy44Gu5Lim44Gz6aCGIChyb3duYW1lKSDjgpJkYXQz44Gr56qB5ZCIKG1lcmdlKSDjgZfjgb7jgZnjgIIKCmBgYHtyfQpkYXQzIDwtIGRhdDIgJT4lIAogIHNlbGVjdChyb3duYW1lLCBib29rKSAlPiUgCiAgcmlnaHRfam9pbiguLCBkYXQzKQoKZGF0MwpgYGAKCiMg5bm05Luj44GU44Go44Gu5qOS44Kw44Op44OV44KS5Zuz56S6CgrlhYjnqIvlrozmiJDjgZfjgZ/mo5LjgrDjg6njg5UgbWFrZV9ncmFwaCgpIOOCkuS9v+OBo+OBpuOBv+OBvuOBmeOAggoKYGBge3J9CmgxIDwtIGRhdDMgJT4lIG1ha2VfZ3JhcGgoKQpoMQpgYGAKCuOBk+OBruOBvuOBvuOBoOOBqOOAgWJvb2vliJfjgYzkvbXjgZvjgabpm4boqIjjgZXjgozjgZ/jgb7jgb7jgavjgarjgovjga7jgafjgIEKZmFjZXRfd3JhcCgp44KS5L2/44Gj44Gm44CB5bm05Luj44GU44Go44Gr5YiG44GR44Gm6KGo56S644GX44G+44GZ44CCIAoKYGBge3J9CmgyIDwtIGgxICsgZmFjZXRfd3JhcChhZ2UgfiAuKSAKaDIKYGBgCgojIOWbs+OBruS/neWtmAoK44GT44GT44G+44Gn44Gn5L2c5oiQ44GX44GfZzPjgahoMeOCkmdnc2F2ZSgp44KS5L2/44Gj44Gm44CBLnRpZmbjgajjgZfjgabkv53lrZjjgZfjgb7jgZnjgIJjb21wcmVzc2lvbiA9ICJsenciIOOCkuWFpeOCjOOCi+OBk+OBqOOBp+OAgeOCteOCpOOCuuOCkuWkp+W5heOBq+Wcp+e4ruOBl+OBpuS/neWtmOOBmeOCi+OBk+OBqOOBjOOBp+OBjeOBvuOBmeOAggoKYGBge3J9Cmdnc2F2ZShwbG90ID0gZzMsIGZpbGVuYW1lID0gImZpZ3VyZTEudGlmZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEwLCBkcGkgPTMwMCwgY29tcHJlc3Npb24gPSAibHp3IikKCmdnc2F2ZShwbG90ID0gaDIsIGZpbGVuYW1lID0gImZpZ3VyZTIudGlmZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEwLCBkcGkgPTMwMCwgY29tcHJlc3Npb24gPSAibHp3IikKYGBgCgpwZGbjgafkv53lrZjjgZnjgovjgZPjgajjgoLjgafjgY3jgb7jgZnjgIIKCmBgYHtyfQoKZ2dzYXZlKHBsb3QgPSBnMywgZmlsZW5hbWUgPSAiZmlndXJlMS5wZGYiLCAgZGV2aWNlID0gY2Fpcm9fcGRmLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCwgZHBpID0zMDApCgpnZ3NhdmUocGxvdCA9IGgyLCAiZmlndXJlMi5wZGYiLCAgZGV2aWNlID0gY2Fpcm9fcGRmLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSAxMCwgZHBpID0zMDApCmBgYAoKIyDjgrPjg6Hjg7Pjg4jmrIQKCuOCs+ODoeODs+ODiOashOOCkmFycmFuZ2UoKeOBp+aWh+Wtl+aVsOOBruWkmuOBhOmghuOBq+S4puOBueOBn+OBguOBqOOAgXNlbGVjdCgp44Gn5oqc44GN5Ye644GX44Gm6KGo56S644GX44G+44GZ44CCCgpgYGB7cn0KdDEgPC0gZGF0ICU+JSAKICBhcnJhbmdlKGRlc2Moc3RyX2xlbmd0aChjb21tZW50KSkpICU+JSAKICBzZWxlY3QoY29tbWVudCkgJT4lIAogIG5hLm9taXQoKSAKCnQxCmBgYAoKCmBgYHtyLCBpbmNsdWRlPUZBTFNFfQojIHNlYXJjaF93b3JkIDwtIGxpc3QoIuOCseODvOOCueOBp+OCj+OBi+OCiyEg57K+56We56eR5rK755mC44Ks44Kk44OJ44Op44Kk44Oz44Gu44OI44Oq44K744OEIiwKIyAgICAgICAgICAgICAgICAgICLlpKfkurrjga7nmbrpgZTpmpzlrrPjgaPjgabjgZ3jgYbjgYTjgYbjgZPjgajjgaDjgaPjgZ/jga7jgYsiLAojICAgICAgICAgICAgICAgICAgIuiEs+azouWIpOiqreOCquODvOODl+ODs+OCreODo+ODs+ODkeOCuSDoqrDjgafjgoLlrabjgbnjgos3U1RFUCIsCiMgICAgICAgICAgICAgICAgICAi44Od44Kx44OD44OI6Ieo5bqK6ISz5rOiIiwKIyAgICAgICAgICAgICAgICAgICLnsr7npZ7jg7vlv4PnkIbnl4fnirblrabjg4/jg7Pjg4njg5bjg4Pjgq9b56ysM+eJiF0iLAojICAgICAgICAgICAgICAgICAgIuS7iuaXpeOBrueyvuelnueWvuaCo+ayu+eZguaMh+mHnSIsCiMgICAgICAgICAgICAgICAgICAi57K+56We44O75b+D55CG5qmf6IO96KmV5L6h44OP44Oz44OJ44OW44OD44KvIiwKIyAgICAgICAgICAgICAgICAgICLjg6Hjg6Ljg6rjg7zjgq/jg6rjg4vjg4Pjgq/oqLrnmYLjg57jg4vjg6XjgqLjg6siLAojICAgICAgICAgICAgICAgICAgIueyvuelnuenkeiHqOW6ijE0NOOBrlEmQSIsCiMgICAgICAgICAgICAgICAgICAi57K+56We56eR44Oq44OP44OT44Oq44OG44O844K344On44Oz44O744Ov44O844Kv44OW44OD44KvIiwKIyAgICAgICAgICAgICAgICAgICLnsr7npZ7ljLvlrablhaXploDjgIDopb/kuLjlm5vmlrkiLAojICAgICAgICAgICAgICAgICAgIuWBpeW6t+ODqeOCpOODluODqeODquODvOOCt+ODquODvOOCuiIsCiMgICAgICAgICAgICAgICAgICAi5pGC6aOf6Zqc5a6z44CA5YiH5rGg5L+h5aSrIiwKIyAgICAgICAgICAgICAgICAgICLnsr7npZ7np5HouqvkvZPlkIjkvbXnl4fjg57jg4vjg6XjgqLjg6siLAojICAgICAgICAgICAgICAgICAgIuWMu+WtpuirluaWh+OBruOBn+OCgeOBrue1seioiOaJi+azleOBrumBuOOBs+aWueS9v+OBhOaWueOAgOmYv+mDqOiytOihjCIsCiMgICAgICAgICAgICAgICAgICAiRFNNLTUiLAojICAgICAgICAgICAgICAgICAgIuOCrOOCpOODieODqeOCpOODsyIsCiMgICAgICAgICAgICAgICAgICAi57K+56We55mC5rOV44Gu5Z+65pysOiDmlK/mjIHjgYvjgonoqo3nn6XooYzli5XnmYLms5Xjgb7jgaciLAojICAgICAgICAgICAgICAgICAgIuS7iuaXpeOBruayu+eZguiWrCIsCiMgICAgICAgICAgICAgICAgICAi57K+56We56eR44Os44K444OH44Oz44OI44Oe44OL44Ol44Ki44OrIiwKIyAgICAgICAgICAgICAgICAgICLnsr7npZ7nmYLms5Xlrrbjga7ku5XkuosiLAojICAgICAgICAgICAgICAgICAgIuePvuS7o+iHqOW6iueyvuelnuWMu+WtpiIsCiMgICAgICAgICAgICAgICAgICAi5aSn5Lq644Gu55m66YGU6Zqc5a6z44Gj44Gm44Gd44GG44GE44GG44GT44Go44Gg44Gj44Gf44Gu44GLIiwKIyAgICAgICAgICAgICAgICAgICLnsr7npZ7nmYLms5Xjga7nrKzkuIDmrakiCiMgICAgICAgICAgICAgICAgICApCmBgYAoKIyDjgrPjg6Hjg7Pjg4jmrITjgpIgLnBuZyDjgafkv53lrZgKCmd0IOODkeODg+OCseODvOOCuOOCkuS9v+OBo+OBpuOAgeOCs+ODoeODs+ODiOashOOCkiAucG5nIOOBp+S/neWtmOOBl+OBpuOBv+OBvuOBmeOAggrjgZ3jga7ku5bjgIFwZGYg44KEIGh0bWwg44Gq44Gp44Gn5L+d5a2Y44GZ44KL44GT44Go44KC5Y+v6IO944Gn44GZ44CCCgpgYGB7cn0KaWYgKCFyZXF1aXJlKCJndCIpKSBpbnN0YWxsLnBhY2thZ2VzKCJndCIpCmxpYnJhcnkoZ3QpIApgYGAKCmBgYHtyfQp0MiA8LSB0MSAlPiUgZ3QoKQoKZ3RzYXZlKGRhdGEgPSB0MiwgInRhYmxlMS5wbmciKQpndHNhdmUoZGF0YSA9IHQyLCAidGFibGUxLmh0bWwiKQpndHNhdmUoZGF0YSA9IHQyLCAidGFibGUxLnBkZiIpCgp0MgpgYGAKCiMg5L2c5oiQ44GX44Gf44OV44Kh44Kk44Or44Gu56K66KqNCgrjgZ3jgozjgZ7jgozkvZzmiJDjgZfjgZ/jg5XjgqHjgqTjg6vvvIhwZGbvvInjga/kuIvoqJjjga7pgJrjgooKCi0gW2ZpZ3VyZTEucGRmXShodHRwczovL3BsYXphLnVtaW4uYWMuanAvc2hvZWkwNS93cC93cC1jb250ZW50L3VwbG9hZHMvMjAyMS8wNS9maWd1cmUxLnBkZil7dGFyZ2V0PSJfYmxhbmsifQotIFtmaWd1cmUyLnBkZl0oaHR0cHM6Ly9wbGF6YS51bWluLmFjLmpwL3Nob2VpMDUvd3Avd3AtY29udGVudC91cGxvYWRzLzIwMjEvMDUvZmlndXJlMi5wZGYpe3RhcmdldD0iX2JsYW5rIn0KLSBbY29tbWVudC5wZGZdKGh0dHBzOi8vcGxhemEudW1pbi5hYy5qcC9zaG9laTA1L3dwL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIxLzA1L2NvbW1lbnQucGRmKXt0YXJnZXQ9Il9ibGFuayJ9Cgpjb21tZW50LnBkZiDjga/kuIDml6ZodG1s44Gr44GX44Gf44KC44Gu44KS44OW44Op44Km44K277yIQ2hyb21l77yJ44GL44KJcGRm44Gr5aSJ5o+b44GX44G+44GX44Gf44CCCgrjgb7jgZ/jgIHjgqLjg7PjgrHjg7zjg4jntYLkuoblvozjgavjgIHlho3luqYgUiDjgafop6PmnpDjgZfjgb7jgZnjgIIKCuOBlOaEj+imi+OChOizquWVj+etieOBguOCjOOBsOOAgVvjgYrllY/jgYTlkIjjgo/jgZtdKGh0dHBzOi8vcGxhemEudW1pbi5hYy5qcC9zaG9laTA1L3dwL2luZGV4LnBocC9jb250YWN0X3VzLyl7dGFyZ2V0PSJfYmxhbmsifeOChOOAgVt0d2l0dGVyXShodHRwczovL3R3aXR0ZXIuY29tL3Nob2VpMDUpe3RhcmdldD0iX2JsYW5rIn0g44GL44KJ44GK5rCX6Lu944Gr44GU6YCj57Wh44GP44Gg44GV44GE44CCCg==