第4章の内容

4.3.2 自動読み込み例2:細菌群集の炭素代謝データ(エコプレート)

#ファイル名一覧の冒頭部分を確認する
head(read.csv("format_xitou_pattern.csv", header = T))
#paste関数を使って、複数の文字列をつなぐことができる
paste("2022Dec", "Biwako", sep = "-")
[1] "2022Dec-Biwako"
#この仕組みを使えばファイル読み込みのための相対パスを作る
paste("./text_file/", "20141216Eco_N1.TXT", sep = "")
[1] "./text_file/20141216Eco_N1.TXT"

自動化のためにfor loopを利用するコード

#Date, treatment, and file information from Xitou data sets
metadata_ecoplate <- read.csv("format_xitou_pattern.csv", header = T) #メタデータの読み込み

dat_list <- list() #データ格納用に空のリストを作成する
no_sample <- length(metadata_ecoplate$data_file) #サンプルの数だけループを回すため、サンプル数をカウントする
for(j in 1:no_sample){
  print(j) #今何回目のループか分かるようにjの値をコンソールに表示する
  file_path <- paste("./text_file/", metadata_ecoplate$data_file[j], sep = "") #相対パスを作る
  dat_list[[j]] <- read.table(file_path, skip = 5) #相対パスにしたがってファイルを読み込んでリストの各要素に順に代入する
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
[1] 21
[1] 22
[1] 23
[1] 24

読み込んだ内容確認

dat_list[[3]]

4.3.3 自動読み込み過程を関数化する

関数の定義

#最初の部分は関数の説明
####Function to load ecoplate data
#Parameter list(パラメータリスト)
#relative_path(相対パス): relative path of data folder from the folder where the R script is saved
#file_list(ファイルリスト): the vector of file names that we intend to load 
#no_skip(スキップする行数): the number of skip rows in ecoplate text file

load_ecoplate_data <- function(relative_path, file_list, no_skip = 5)    
{ #関数化する前とだいたい同じコード
  data_list <- list()   #すでに使っているオブジェクト名(dat_list)は使いまわさない
  for(i in 1:length(file_list)) {
    file_name <- paste(relative_path, file_list[i], sep = "")
    data_list[[i]] <- read.table(file_name, skip = no_skip)  
  }
  return(data_list)  #output (return value) of this function リスト全体を出力する
}

実際に関数を読み出して読み込みをやってみる

#関数をそのまま読み出す場合、返り値のリストそのものが出力される
load_ecoplate_data(relative_path = "./text_file/", file_list = metadata_ecoplate$data_file, no_skip = 5)
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

[[8]]

[[9]]

[[10]]

[[11]]

[[12]]

[[13]]

[[14]]

[[15]]

[[16]]

[[17]]

[[18]]

[[19]]

[[20]]

[[21]]

[[22]]

[[23]]

[[24]]
NA

新しいリストに代入するのが実用的である

#関数の返り値をdata_ecoplateというオブジェクトに代入する場合
data_ecoplate <- load_ecoplate_data(relative_path = "./text_file/", file_list = metadata_ecoplate$data_file, no_skip = 5)
#二つ目の要素を確認
data_ecoplate[[2]]

第5章の内容

5.1.3 存在しないファイルからデータを読み込もうとしてしまうミス

試しにエラーがおきるように用意したメタファイルを使ってファイル読み込みを実行してみる

#メタデータ(エラー含む)の読み込み
metadata_ecoplate_e1 <- read.csv("format_xitou_pattern_e1.csv", header = T)
#関数の呼び出し:エラーが途中で生じるので、何も出力されない
load_ecoplate_data(relative_path = "./text_file/", file_list = metadata_ecoplate_e1$data_file, no_skip = 5)
Warning: cannot open file './text_file/NA': No such file or directoryError in file(file, "rt") : cannot open the connection

このエラーを回避するための修正版

#Function with error management
load_ecoplate_data2 <-  function(relative_path, file_list, no_skip = 5)    
{
  data_list <- list() 
  for(i in 1:length(file_list)) {
    file_name <- paste(relative_path, file_list[i], sep = "")
    #try関数は引数に指定した関数がエラーを返すかどうか判定するときに使うと便利
    e <- try(read.table(file_name, skip = no_skip), silent = FALSE)   #error management
    #try関数が何らかのエラーを返した場合には,オブジェクトeのタイプが"try-error"になるので、エラーが生じたかどうかの判定に使える
    if(class(e) == "try-error") next  #エラーが起きたiを飛ばして次のiのループに進む
    else data_list[[i]] <- read.table(file_name, skip = no_skip) #エラーが出なかった場合は、ファイルの中身を読み込む
  }
  return(data_list)  #output (return value) of this function
}

この新しい関数を使えばエラーが出ない(警告のみ)

data_ecoplate2 <- load_ecoplate_data2(relative_path = "./text_file/", file_list = metadata_ecoplate_e1$data_file, no_skip = 5)
Warning: cannot open file './text_file/NA': No such file or directoryError in file(file, "rt") : cannot open the connection
#15番の要素が空(NULL)であることがわかるはず
data_ecoplate2[[15]]
NULL
#それでも16番目以降の要素にもちゃんとファイルは読み込まれている
data_ecoplate2[[16]]

5.2.2 エコプレートデータを整頓する

3繰り返しの平均値を計算する

試しに1列目、5列目、9列目の平均値を計算してみる

(data_ecoplate[[1]]$X1 + data_ecoplate[[1]]$X5 + data_ecoplate[[1]]$X9) / 3  
[1] 0.1413333 1.4510000 2.5583333 2.6163333 1.6183333 1.2356667 2.2463333 1.6516667

全ての平均値を一括で計算するための関数の定義

####Function to calculate the averages and standardization by control values
ave_ecoplate <- function(data_f){
  #1列目・5列目・9列目の平均値
  data_ave1 <- (data_f$X1 + data_f$X5 + data_f$X9) / 3.0  
  #2列目・6列目・10列目の平均値
  data_ave2 <- (data_f$X2 + data_f$X6 + data_f$X10) / 3.0
  #3列目・7列目・11列目の平均値
  data_ave3 <- (data_f$X3 + data_f$X7 + data_f$X11) / 3.0
  #4列目・8列目・12列目の平均値
  data_ave4 <- (data_f$X4 + data_f$X8 + data_f$X12) / 3.0
  #append関数を繰り返し使って4つのベクトルを一つにまとめる:内側のappendから順に実行されることに注意(=まずdata_ave1, data_ave2がappendされ、それとdata_ave3が次にアペンドされ、、、という順)
  data_sum <- append(append(append(data_ave1,data_ave2),data_ave3),data_ave4)  
  #対象区(water well)の平均値をすべての要素から差し引くという標準化を行なう
  data_sum_nor <- data_sum - data_sum[1] #normalizing by water well
  return(data_sum_nor) #output, 標準化後のベクトルを出力する
}

観測ごとのベクトルをすべてまとめて一つのデータフレームにする

関数の定義

stat_summary_ecoplate <- function(data_f, sample_name, variable_name)
{ 
  #一つ目のデータから平均値を計算し、データフレームに代入
  data_summary <- ave_ecoplate(data_f[[1]]) 
  for(i in 2:length(data_f)) {
    # 空のデータがあった場合は、次のループに飛ぶ
    if(is.null(data_f[[i]])) next  #error management, skipping the non-measured dates
     #次のデータから平均値を計算し、それまでのループで作ったデータフレームに追加して上書きする
      data_summary <- rbind.data.frame(data_summary, ave_ecoplate(data_f[[i]]))
    }#end of for i
  data_summary <- data_summary[,-1] #一列目のデータはすべてゼロなので削除してよい
  data_summary[data_summary < 0] <- 0 #標準化後に負の値となるものはゼロに置き換えてよい
  colnames(data_summary) <- variable_name #列名を付ける
  rownames(data_summary) <- sample_name #行名を付ける
  return(data_summary) #データフレームを出力する
}

列名として基質(Substrates)名を当てたいが、具体的な名前を使うとコードが長くなるので、S1, S2,…という感じで名前を付ける

substrate_name <- c("s01", "s02","s03","s04","s05","s06","s07","s08","s09","s10","s11", "s12","s13","s14","s15","s16","s17","s18","s19","s20","s21", "s22","s23","s24","s25","s26","s27","s28","s29","s30", "S31")

実際に上で作った関数を呼び出し、サンプル個データのメタデータファイル(metadata_ecoplate)とデータファイル(data_ecoplate)に対して、データフレームにデータをまとめる

#関数の呼び出し
summary_ecoplate <- stat_summary_ecoplate(data_f = data_ecoplate, sample_name = metadata_ecoplate$sample, variable_name = substrate_name)
#結果の一部の確認
head(summary_ecoplate)
LS0tDQp0aXRsZTogImVjb3BsYXRlX3Rlc3QuUuOBq+WvvuW/nOOBl+OBn1IgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyDnrKw056ug44Gu5YaF5a65DQoNCiMjIyA0LjMuMiDoh6rli5Xoqq3jgb/ovrzjgb/kvosy77ya57Sw6I+M576k6ZuG44Gu54Kt57Sg5Luj6Kyd44OH44O844K/77yI44Ko44Kz44OX44Os44O844OI77yJDQoNCmBgYHtyfQ0KI+ODleOCoeOCpOODq+WQjeS4gOimp+OBruWGkumgremDqOWIhuOCkueiuuiqjeOBmeOCiw0KaGVhZChyZWFkLmNzdigiZm9ybWF0X3hpdG91X3BhdHRlcm4uY3N2IiwgaGVhZGVyID0gVCkpDQpgYGANCg0KYGBge3J9DQojcGFzdGXplqLmlbDjgpLkvb/jgaPjgabjgIHopIfmlbDjga7mloflrZfliJfjgpLjgaTjgarjgZDjgZPjgajjgYzjgafjgY3jgosNCnBhc3RlKCIyMDIyRGVjIiwgIkJpd2FrbyIsIHNlcCA9ICItIikNCiPjgZPjga7ku5XntYTjgb/jgpLkvb/jgYjjgbDjg5XjgqHjgqTjg6voqq3jgb/ovrzjgb/jga7jgZ/jgoHjga7nm7jlr77jg5HjgrnjgpLkvZzjgosNCnBhc3RlKCIuL3RleHRfZmlsZS8iLCAiMjAxNDEyMTZFY29fTjEuVFhUIiwgc2VwID0gIiIpDQpgYGANCg0K6Ieq5YuV5YyW44Gu44Gf44KB44GrZm9yIGxvb3DjgpLliKnnlKjjgZnjgovjgrPjg7zjg4kNCg0KYGBge3J9DQojRGF0ZSwgdHJlYXRtZW50LCBhbmQgZmlsZSBpbmZvcm1hdGlvbiBmcm9tIFhpdG91IGRhdGEgc2V0cw0KbWV0YWRhdGFfZWNvcGxhdGUgPC0gcmVhZC5jc3YoImZvcm1hdF94aXRvdV9wYXR0ZXJuLmNzdiIsIGhlYWRlciA9IFQp44CAI+ODoeOCv+ODh+ODvOOCv+OBruiqreOBv+i+vOOBvw0KDQpkYXRfbGlzdCA8LSBsaXN0KCnjgIAj44OH44O844K/5qC857SN55So44Gr56m644Gu44Oq44K544OI44KS5L2c5oiQ44GZ44KLDQpub19zYW1wbGUgPC0gbGVuZ3RoKG1ldGFkYXRhX2Vjb3BsYXRlJGRhdGFfZmlsZSnjgIAj44K144Oz44OX44Or44Gu5pWw44Gg44GR44Or44O844OX44KS5Zue44GZ44Gf44KB44CB44K144Oz44OX44Or5pWw44KS44Kr44Km44Oz44OI44GZ44KLDQpmb3IoaiBpbiAxOm5vX3NhbXBsZSl7DQogIHByaW50KGopICPku4rkvZXlm57nm67jga7jg6vjg7zjg5fjgYvliIbjgYvjgovjgojjgYbjgatq44Gu5YCk44KS44Kz44Oz44K944O844Or44Gr6KGo56S644GZ44KLDQogIGZpbGVfcGF0aCA8LSBwYXN0ZSgiLi90ZXh0X2ZpbGUvIiwgbWV0YWRhdGFfZWNvcGxhdGUkZGF0YV9maWxlW2pdLCBzZXAgPSAiIinjgIAj55u45a++44OR44K544KS5L2c44KLDQogIGRhdF9saXN0W1tqXV0gPC0gcmVhZC50YWJsZShmaWxlX3BhdGgsIHNraXAgPSA1KeOAgCPnm7jlr77jg5HjgrnjgavjgZfjgZ/jgYzjgaPjgabjg5XjgqHjgqTjg6vjgpLoqq3jgb/ovrzjgpPjgafjg6rjgrnjg4jjga7lkITopoHntKDjgavpoIbjgavku6PlhaXjgZnjgosNCn0NCmBgYA0KDQroqq3jgb/ovrzjgpPjgaDlhoXlrrnnorroqo0NCg0KYGBge3J9DQpkYXRfbGlzdFtbM11dDQpgYGANCg0KIyMjIDQuMy4zIOiHquWLleiqreOBv+i+vOOBv+mBjueoi+OCkumWouaVsOWMluOBmeOCiw0KDQrplqLmlbDjga7lrprnvqkNCg0KYGBge3J9DQoj5pyA5Yid44Gu6YOo5YiG44Gv6Zai5pWw44Gu6Kqs5piODQojIyMjRnVuY3Rpb24gdG8gbG9hZCBlY29wbGF0ZSBkYXRhDQojUGFyYW1ldGVyIGxpc3Qo44OR44Op44Oh44O844K/44Oq44K544OIKQ0KI3JlbGF0aXZlX3BhdGgo55u45a++44OR44K5KTogcmVsYXRpdmUgcGF0aCBvZiBkYXRhIGZvbGRlciBmcm9tIHRoZSBmb2xkZXIgd2hlcmUgdGhlIFIgc2NyaXB0IGlzIHNhdmVkDQojZmlsZV9saXN0KOODleOCoeOCpOODq+ODquOCueODiCk6IHRoZSB2ZWN0b3Igb2YgZmlsZSBuYW1lcyB0aGF0IHdlIGludGVuZCB0byBsb2FkIA0KI25vX3NraXAo44K544Kt44OD44OX44GZ44KL6KGM5pWwKTogdGhlIG51bWJlciBvZiBza2lwIHJvd3MgaW4gZWNvcGxhdGUgdGV4dCBmaWxlDQoNCmxvYWRfZWNvcGxhdGVfZGF0YSA8LSBmdW5jdGlvbihyZWxhdGl2ZV9wYXRoLCBmaWxlX2xpc3QsIG5vX3NraXAgPSA1KSAgICANCnsgI+mWouaVsOWMluOBmeOCi+WJjeOBqOOBoOOBhOOBn+OBhOWQjOOBmOOCs+ODvOODiQ0KICBkYXRhX2xpc3QgPC0gbGlzdCgpICAgI+OBmeOBp+OBq+S9v+OBo+OBpuOBhOOCi+OCquODluOCuOOCp+OCr+ODiOWQjShkYXRfbGlzdCnjga/kvb/jgYTjgb7jgo/jgZXjgarjgYQNCiAgZm9yKGkgaW4gMTpsZW5ndGgoZmlsZV9saXN0KSkgew0KICAgIGZpbGVfbmFtZSA8LSBwYXN0ZShyZWxhdGl2ZV9wYXRoLCBmaWxlX2xpc3RbaV0sIHNlcCA9ICIiKQ0KICAgIGRhdGFfbGlzdFtbaV1dIDwtIHJlYWQudGFibGUoZmlsZV9uYW1lLCBza2lwID0gbm9fc2tpcCkgIA0KICB9DQogIHJldHVybihkYXRhX2xpc3QpICAjb3V0cHV0IChyZXR1cm4gdmFsdWUpIG9mIHRoaXMgZnVuY3Rpb24g44Oq44K544OI5YWo5L2T44KS5Ye65Yqb44GZ44KLDQp9DQpgYGANCg0K5a6f6Zqb44Gr6Zai5pWw44KS6Kqt44G/5Ye644GX44Gm6Kqt44G/6L6844G/44KS44KE44Gj44Gm44G/44KLDQoNCmBgYHtyfQ0KI+mWouaVsOOCkuOBneOBruOBvuOBvuiqreOBv+WHuuOBmeWgtOWQiOOAgei/lOOCiuWApOOBruODquOCueODiOOBneOBruOCguOBruOBjOWHuuWKm+OBleOCjOOCiw0KbG9hZF9lY29wbGF0ZV9kYXRhKHJlbGF0aXZlX3BhdGggPSAiLi90ZXh0X2ZpbGUvIiwgZmlsZV9saXN0ID0gbWV0YWRhdGFfZWNvcGxhdGUkZGF0YV9maWxlLCBub19za2lwID0gNSkNCmBgYA0KDQrmlrDjgZfjgYTjg6rjgrnjg4jjgavku6PlhaXjgZnjgovjga7jgYzlrp/nlKjnmoTjgafjgYLjgosNCg0KYGBge3J9DQoj6Zai5pWw44Gu6L+U44KK5YCk44KSZGF0YV9lY29wbGF0ZeOBqOOBhOOBhuOCquODluOCuOOCp+OCr+ODiOOBq+S7o+WFpeOBmeOCi+WgtOWQiA0KZGF0YV9lY29wbGF0ZSA8LSBsb2FkX2Vjb3BsYXRlX2RhdGEocmVsYXRpdmVfcGF0aCA9ICIuL3RleHRfZmlsZS8iLCBmaWxlX2xpc3QgPSBtZXRhZGF0YV9lY29wbGF0ZSRkYXRhX2ZpbGUsIG5vX3NraXAgPSA1KQ0KI+S6jOOBpOebruOBruimgee0oOOCkueiuuiqjQ0KZGF0YV9lY29wbGF0ZVtbMl1dDQpgYGANCiMjIOesrDXnq6Djga7lhoXlrrkNCiMjIyA1LjEuMyDlrZjlnKjjgZfjgarjgYTjg5XjgqHjgqTjg6vjgYvjgonjg4fjg7zjgr/jgpLoqq3jgb/ovrzjgoLjgYbjgajjgZfjgabjgZfjgb7jgYbjg5/jgrkNCuippuOBl+OBq+OCqOODqeODvOOBjOOBiuOBjeOCi+OCiOOBhuOBq+eUqOaEj+OBl+OBn+ODoeOCv+ODleOCoeOCpOODq+OCkuS9v+OBo+OBpuODleOCoeOCpOODq+iqreOBv+i+vOOBv+OCkuWun+ihjOOBl+OBpuOBv+OCiw0KYGBge3IsIGVycm9yPVRSVUV9DQoj44Oh44K/44OH44O844K/KOOCqOODqeODvOWQq+OCgCnjga7oqq3jgb/ovrzjgb8NCm1ldGFkYXRhX2Vjb3BsYXRlX2UxIDwtIHJlYWQuY3N2KCJmb3JtYXRfeGl0b3VfcGF0dGVybl9lMS5jc3YiLCBoZWFkZXIgPSBUKQ0KI+mWouaVsOOBruWRvOOBs+WHuuOBl++8muOCqOODqeODvOOBjOmAlOS4reOBp+eUn+OBmOOCi+OBruOBp+OAgeS9leOCguWHuuWKm+OBleOCjOOBquOBhA0KbG9hZF9lY29wbGF0ZV9kYXRhKHJlbGF0aXZlX3BhdGggPSAiLi90ZXh0X2ZpbGUvIiwgZmlsZV9saXN0ID0gbWV0YWRhdGFfZWNvcGxhdGVfZTEkZGF0YV9maWxlLCBub19za2lwID0gNSkNCmBgYA0K44GT44Gu44Ko44Op44O844KS5Zue6YG/44GZ44KL44Gf44KB44Gu5L+u5q2j54mIDQpgYGB7cn0NCiNGdW5jdGlvbiB3aXRoIGVycm9yIG1hbmFnZW1lbnQNCmxvYWRfZWNvcGxhdGVfZGF0YTIgPC0gIGZ1bmN0aW9uKHJlbGF0aXZlX3BhdGgsIGZpbGVfbGlzdCwgbm9fc2tpcCA9IDUpICAgIA0Kew0KICBkYXRhX2xpc3QgPC0gbGlzdCgpIA0KICBmb3IoaSBpbiAxOmxlbmd0aChmaWxlX2xpc3QpKSB7DQogICAgZmlsZV9uYW1lIDwtIHBhc3RlKHJlbGF0aXZlX3BhdGgsIGZpbGVfbGlzdFtpXSwgc2VwID0gIiIpDQogICAgI3RyeemWouaVsOOBr+W8leaVsOOBq+aMh+WumuOBl+OBn+mWouaVsOOBjOOCqOODqeODvOOCkui/lOOBmeOBi+OBqeOBhuOBi+WIpOWumuOBmeOCi+OBqOOBjeOBq+S9v+OBhuOBqOS+v+WIqQ0KICAgIGUgPC0gdHJ5KHJlYWQudGFibGUoZmlsZV9uYW1lLCBza2lwID0gbm9fc2tpcCksIHNpbGVudCA9IEZBTFNFKSAgICNlcnJvciBtYW5hZ2VtZW50DQogICAgI3RyeemWouaVsOOBjOS9leOCieOBi+OBruOCqOODqeODvOOCkui/lOOBl+OBn+WgtOWQiOOBq+OBr++8jOOCquODluOCuOOCp+OCr+ODiGXjga7jgr/jgqTjg5fjgYwidHJ5LWVycm9yIuOBq+OBquOCi+OBruOBp+OAgeOCqOODqeODvOOBjOeUn+OBmOOBn+OBi+OBqeOBhuOBi+OBruWIpOWumuOBq+S9v+OBiOOCiw0KICAgIGlmKGNsYXNzKGUpID09ICJ0cnktZXJyb3IiKSBuZXh0ICAj44Ko44Op44O844GM6LW344GN44GfaeOCkumjm+OBsOOBl+OBpuasoeOBrmnjga7jg6vjg7zjg5fjgavpgLLjgoANCiAgICBlbHNlIGRhdGFfbGlzdFtbaV1dIDwtIHJlYWQudGFibGUoZmlsZV9uYW1lLCBza2lwID0gbm9fc2tpcCkgI+OCqOODqeODvOOBjOWHuuOBquOBi+OBo+OBn+WgtOWQiOOBr+OAgeODleOCoeOCpOODq+OBruS4rei6q+OCkuiqreOBv+i+vOOCgA0KICB9DQogIHJldHVybihkYXRhX2xpc3QpICAjb3V0cHV0IChyZXR1cm4gdmFsdWUpIG9mIHRoaXMgZnVuY3Rpb24NCn0NCmBgYA0K44GT44Gu5paw44GX44GE6Zai5pWw44KS5L2/44GI44Gw44Ko44Op44O844GM5Ye644Gq44GEKOitpuWRiuOBruOBvykNCmBgYHtyfQ0KZGF0YV9lY29wbGF0ZTIgPC0gbG9hZF9lY29wbGF0ZV9kYXRhMihyZWxhdGl2ZV9wYXRoID0gIi4vdGV4dF9maWxlLyIsIGZpbGVfbGlzdCA9IG1ldGFkYXRhX2Vjb3BsYXRlX2UxJGRhdGFfZmlsZSwgbm9fc2tpcCA9IDUpDQojMTXnlarjga7opoHntKDjgYznqbooTlVMTCnjgafjgYLjgovjgZPjgajjgYzjgo/jgYvjgovjga/jgZoNCmRhdGFfZWNvcGxhdGUyW1sxNV1dDQoj44Gd44KM44Gn44KCMTbnlarnm67ku6XpmY3jga7opoHntKDjgavjgoLjgaHjgoPjgpPjgajjg5XjgqHjgqTjg6vjga/oqq3jgb/ovrzjgb7jgozjgabjgYTjgosNCmRhdGFfZWNvcGxhdGUyW1sxNl1dDQpgYGANCiMjIyA1LjIuMiDjgqjjgrPjg5fjg6zjg7zjg4jjg4fjg7zjgr/jgpLmlbTpoJPjgZnjgosNCiMjIyMgM+e5sOOCiui/lOOBl+OBruW5s+Wdh+WApOOCkuioiOeul+OBmeOCiw0K6Kmm44GX44GrMeWIl+ebruOAgTXliJfnm67jgIE55YiX55uu44Gu5bmz5Z2H5YCk44KS6KiI566X44GX44Gm44G/44KLDQpgYGB7cn0NCihkYXRhX2Vjb3BsYXRlW1sxXV0kWDEgKyBkYXRhX2Vjb3BsYXRlW1sxXV0kWDUgKyBkYXRhX2Vjb3BsYXRlW1sxXV0kWDkpIC8gMyAgDQpgYGANCg0K5YWo44Gm44Gu5bmz5Z2H5YCk44KS5LiA5ous44Gn6KiI566X44GZ44KL44Gf44KB44Gu6Zai5pWw44Gu5a6a576pDQpgYGB7cn0NCiMjIyNGdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2VzIGFuZCBzdGFuZGFyZGl6YXRpb24gYnkgY29udHJvbCB2YWx1ZXMNCmF2ZV9lY29wbGF0ZSA8LSBmdW5jdGlvbihkYXRhX2Ypew0KICAjMeWIl+ebruODuzXliJfnm67jg7s55YiX55uu44Gu5bmz5Z2H5YCkDQogIGRhdGFfYXZlMSA8LSAoZGF0YV9mJFgxICsgZGF0YV9mJFg1ICsgZGF0YV9mJFg5KSAvIDMuMCAgDQogICMy5YiX55uu44O7NuWIl+ebruODuzEw5YiX55uu44Gu5bmz5Z2H5YCkDQogIGRhdGFfYXZlMiA8LSAoZGF0YV9mJFgyICsgZGF0YV9mJFg2ICsgZGF0YV9mJFgxMCkgLyAzLjANCiAgIzPliJfnm67jg7s35YiX55uu44O7MTHliJfnm67jga7lubPlnYflgKQNCiAgZGF0YV9hdmUzIDwtIChkYXRhX2YkWDMgKyBkYXRhX2YkWDcgKyBkYXRhX2YkWDExKSAvIDMuMA0KICAjNOWIl+ebruODuzjliJfnm67jg7sxMuWIl+ebruOBruW5s+Wdh+WApA0KICBkYXRhX2F2ZTQgPC0gKGRhdGFfZiRYNCArIGRhdGFfZiRYOCArIGRhdGFfZiRYMTIpIC8gMy4wDQogICNhcHBlbmTplqLmlbDjgpLnubDjgorov5TjgZfkvb/jgaPjgaY044Gk44Gu44OZ44Kv44OI44Or44KS5LiA44Gk44Gr44G+44Go44KB44KL77ya5YaF5YG044GuYXBwZW5k44GL44KJ6aCG44Gr5a6f6KGM44GV44KM44KL44GT44Go44Gr5rOo5oSPKD3jgb7jgZpkYXRhX2F2ZTEsIGRhdGFfYXZlMuOBjGFwcGVuZOOBleOCjOOAgeOBneOCjOOBqGRhdGFfYXZlM+OBjOasoeOBq+OCouODmuODs+ODieOBleOCjOOAgeOAgeOAgeOBqOOBhOOBhumghikNCiAgZGF0YV9zdW0gPC0gYXBwZW5kKGFwcGVuZChhcHBlbmQoZGF0YV9hdmUxLGRhdGFfYXZlMiksZGF0YV9hdmUzKSxkYXRhX2F2ZTQpICANCiAgI+WvvuixoeWMuih3YXRlciB3ZWxsKeOBruW5s+Wdh+WApOOCkuOBmeOBueOBpuOBruimgee0oOOBi+OCieW3ruOBl+W8leOBj+OBqOOBhOOBhuaomea6luWMluOCkuihjOOBquOBhg0KICBkYXRhX3N1bV9ub3IgPC0gZGF0YV9zdW0gLSBkYXRhX3N1bVsxXSAjbm9ybWFsaXppbmcgYnkgd2F0ZXIgd2VsbA0KICByZXR1cm4oZGF0YV9zdW1fbm9yKSAjb3V0cHV0LCDmqJnmupbljJblvozjga7jg5njgq/jg4jjg6vjgpLlh7rlipvjgZnjgosNCn0NCmBgYA0KIyMjIyDoprPmuKzjgZTjgajjga7jg5njgq/jg4jjg6vjgpLjgZnjgbnjgabjgb7jgajjgoHjgabkuIDjgaTjga7jg4fjg7zjgr/jg5Xjg6zjg7zjg6DjgavjgZnjgosNCumWouaVsOOBruWumue+qQ0KDQpgYGB7cn0NCnN0YXRfc3VtbWFyeV9lY29wbGF0ZSA8LSBmdW5jdGlvbihkYXRhX2YsIHNhbXBsZV9uYW1lLCB2YXJpYWJsZV9uYW1lKQ0KeyANCiAgI+S4gOOBpOebruOBruODh+ODvOOCv+OBi+OCieW5s+Wdh+WApOOCkuioiOeul+OBl+OAgeODh+ODvOOCv+ODleODrOODvOODoOOBq+S7o+WFpQ0KICBkYXRhX3N1bW1hcnkgPC0gYXZlX2Vjb3BsYXRlKGRhdGFfZltbMV1dKSANCiAgZm9yKGkgaW4gMjpsZW5ndGgoZGF0YV9mKSkgew0KICAgICMg56m644Gu44OH44O844K/44GM44GC44Gj44Gf5aC05ZCI44Gv44CB5qyh44Gu44Or44O844OX44Gr6aOb44G2DQogICAgaWYoaXMubnVsbChkYXRhX2ZbW2ldXSkpIG5leHQgICNlcnJvciBtYW5hZ2VtZW50LCBza2lwcGluZyB0aGUgbm9uLW1lYXN1cmVkIGRhdGVzDQogICAg44CAI+asoeOBruODh+ODvOOCv+OBi+OCieW5s+Wdh+WApOOCkuioiOeul+OBl+OAgeOBneOCjOOBvuOBp+OBruODq+ODvOODl+OBp+S9nOOBo+OBn+ODh+ODvOOCv+ODleODrOODvOODoOOBq+i/veWKoOOBl+OBpuS4iuabuOOBjeOBmeOCiw0KICAgICAgZGF0YV9zdW1tYXJ5IDwtIHJiaW5kLmRhdGEuZnJhbWUoZGF0YV9zdW1tYXJ5LCBhdmVfZWNvcGxhdGUoZGF0YV9mW1tpXV0pKQ0KICAgIH0jZW5kIG9mIGZvciBpDQogIGRhdGFfc3VtbWFyeSA8LSBkYXRhX3N1bW1hcnlbLC0xXSAj5LiA5YiX55uu44Gu44OH44O844K/44Gv44GZ44G544Gm44K844Ot44Gq44Gu44Gn5YmK6Zmk44GX44Gm44KI44GEDQogIGRhdGFfc3VtbWFyeVtkYXRhX3N1bW1hcnkgPCAwXSA8LSAw44CAI+aomea6luWMluW+jOOBq+iyoOOBruWApOOBqOOBquOCi+OCguOBruOBr+OCvOODreOBq+e9ruOBjeaPm+OBiOOBpuOCiOOBhA0KICBjb2xuYW1lcyhkYXRhX3N1bW1hcnkpIDwtIHZhcmlhYmxlX25hbWXjgIAj5YiX5ZCN44KS5LuY44GR44KLDQogIHJvd25hbWVzKGRhdGFfc3VtbWFyeSkgPC0gc2FtcGxlX25hbWXjgIAj6KGM5ZCN44KS5LuY44GR44KLDQogIHJldHVybihkYXRhX3N1bW1hcnkp44CAI+ODh+ODvOOCv+ODleODrOODvOODoOOCkuWHuuWKm+OBmeOCiw0KfQ0KYGBgDQrliJflkI3jgajjgZfjgabln7ros6ooU3Vic3RyYXRlcynlkI3jgpLlvZPjgabjgZ/jgYTjgYzjgIHlhbfkvZPnmoTjgarlkI3liY3jgpLkvb/jgYbjgajjgrPjg7zjg4njgYzplbfjgY/jgarjgovjga7jgafjgIFTMSwgUzIsLi4u44Go44GE44GG5oSf44GY44Gn5ZCN5YmN44KS5LuY44GR44KLDQpgYGB7cn0NCnN1YnN0cmF0ZV9uYW1lIDwtIGMoInMwMSIsICJzMDIiLCJzMDMiLCJzMDQiLCJzMDUiLCJzMDYiLCJzMDciLCJzMDgiLCJzMDkiLCJzMTAiLCJzMTEiLCAiczEyIiwiczEzIiwiczE0IiwiczE1IiwiczE2IiwiczE3IiwiczE4IiwiczE5IiwiczIwIiwiczIxIiwgInMyMiIsInMyMyIsInMyNCIsInMyNSIsInMyNiIsInMyNyIsInMyOCIsInMyOSIsInMzMCIsICJTMzEiKQ0KYGBgDQoNCuWun+mam+OBq+S4iuOBp+S9nOOBo+OBn+mWouaVsOOCkuWRvOOBs+WHuuOBl+OAgeOCteODs+ODl+ODq+WAi+ODh+ODvOOCv+OBruODoeOCv+ODh+ODvOOCv+ODleOCoeOCpOODqyhtZXRhZGF0YV9lY29wbGF0ZSnjgajjg4fjg7zjgr/jg5XjgqHjgqTjg6soZGF0YV9lY29wbGF0ZSnjgavlr77jgZfjgabjgIHjg4fjg7zjgr/jg5Xjg6zjg7zjg6Djgavjg4fjg7zjgr/jgpLjgb7jgajjgoHjgosNCmBgYHtyfQ0KI+mWouaVsOOBruWRvOOBs+WHuuOBlw0Kc3VtbWFyeV9lY29wbGF0ZSA8LSBzdGF0X3N1bW1hcnlfZWNvcGxhdGUoZGF0YV9mID0gZGF0YV9lY29wbGF0ZSwgc2FtcGxlX25hbWUgPSBtZXRhZGF0YV9lY29wbGF0ZSRzYW1wbGUsIHZhcmlhYmxlX25hbWUgPSBzdWJzdHJhdGVfbmFtZSkNCiPntZDmnpzjga7kuIDpg6jjga7norroqo0NCmhlYWQoc3VtbWFyeV9lY29wbGF0ZSkNCmBgYA0KDQoNCg==