図の右上のshowボタンを押すとRのコードが表示されます。

7.1 時間的なパターンをとらえる

7.1.1 じゃんけんマシンの分析

7.1.1.1

library(conflicted)
library(tidyverse)
library(patchwork)

# シード値を固定
set.seed(42)

# データ生成関数
generate_data <- function(transition_prob, n=1000) {
  initial_state <- sample.int(3, 1) # 初期状態をランダムに選択
  # 遷移確率に従って次の状態を選択
  states <- accumulate(
    .init = initial_state,
    .x = rep(1, n-1),
    .f = function(state, .) {
      sample.int(3, size = 1, prob = transition_prob[[state]])
      }
    )
  return(states)
}

# じゃんけんマシンAの遷移確率
transition_prob_A <- list(c(0.2, 0.5, 0.3), c(0.4, 0.1, 0.5), c(0.3, 0.6, 0.1))

# じゃんけんマシンBの遷移確率
transition_prob_B <- list(c(0.3, 0.4, 0.3), c(0.2, 0.3, 0.5), c(0.4, 0.2, 0.4))

# データ生成
data_A <- generate_data(transition_prob_A)
data_B <- generate_data(transition_prob_B)

# 最初の50回のじゃんけんの時系列を折れ線グラフで描画
df_long <- data.frame(Round=1:50, Machine_A=data_A[1:50], Machine_B=data_B[1:50]) |>
  pivot_longer(!Round)

p1 <- df_long |>
  ggplot(aes(x=Round, y=value, color=name)) +
  geom_line(alpha = 0.5) +
  geom_point(alpha = 0.5) +  
  scale_y_continuous(breaks=1:3, labels=c("グー", "チョキ", "パー")) +
  scale_color_discrete(labels = c("じゃんけんマシンA", "じゃんけんマシンB")) +
  labs(title="分析対象のジャンケン時系列(最初の50ラウンド)") +
  theme(
    axis.title = element_blank(),
    legend.title = element_blank(),
    legend.position = "bottom"
  )

# 各手の連続回数の平均値を計算する関数
calculate_run_mean <- function(data) {
  rle_data <- rle(data)  # 要素が連続する部分の値と長さを計算
  counts <- split(rle_data$lengths, rle_data$values)  # 各手の連続回数を取得
  return(sapply(counts, mean, na.rm=TRUE))  # 各手の連続回数の平均値を計算
}

# じゃんけんマシンAとBの各手の連続回数の平均値を計算
mean_run_A <- calculate_run_mean(data_A)
mean_run_B <- calculate_run_mean(data_B)

# 集団棒グラフで各手の出現割合を描画
p2 <- rbind(
  table(data_A) / length(data_A),
  table(data_B) / length(data_B)
  ) |>
  as.data.frame() |>
  mutate(machine = c("じゃんけんマシンA", "じゃんけんマシンB")) |>
  pivot_longer(!machine) |>
  ggplot(aes(x = name, y = value, fill = machine)) +
  geom_col(position = "dodge") +
  scale_x_discrete(labels = c("1" = "グー", "2" = "チョキ", "3" = "パー")) +
  theme(legend.position = "none", axis.title = element_blank()) +
  labs(title = "出現頻度")

# 集団棒グラフで各手の連続回数の平均値を描画
p3 <-rbind(mean_run_A, mean_run_B) |>
  as.data.frame() |>
  mutate(machine = c("じゃんけんマシンA", "じゃんけんマシンB")) |>
  pivot_longer(!machine) |>
  ggplot(aes(x = name, y = value, fill = machine)) +
  geom_col(position = "dodge") +
  scale_x_discrete(labels = c("1" = "グー", "2" = "チョキ", "3" = "パー")) +
  theme(legend.title = element_blank(), axis.title = element_blank()) +
  labs(title = "持続回数の平均値")

p1 / {p2 | p3}

7.1.1.2

ggraphで書き直したい

library(conflicted)
library(tidyverse)
library(igraph)
library(patchwork)
library(viridisLite)

# シード値を固定
set.seed(42)

# データ生成関数
generate_data <- function(transition_prob, n=1000) {
  initial_state <- sample.int(3, 1) # 初期状態をランダムに選択
  # 遷移確率に従って次の状態を選択
  states <- accumulate(
    .init = initial_state,
    .x = rep(1, n-1),
    .f = function(state, .) {
      sample.int(3, size = 1, prob = transition_prob[[state]])
      }
    )
  return(states)
}

# じゃんけんマシンAの遷移確率
transition_prob_A <- list(c(0.2, 0.5, 0.3), c(0.4, 0.1, 0.5), c(0.3, 0.6, 0.1))

# じゃんけんマシンBの遷移確率
transition_prob_B <- list(c(0.3, 0.4, 0.3), c(0.2, 0.3, 0.5), c(0.4, 0.2, 0.4))

# データ生成
data_A <- generate_data(transition_prob_A)
data_B <- generate_data(transition_prob_B)

# データから遷移確率を計算する関数
calculate_empirical_transition_prob <- function(data) {
  # 現在の状態と次の状態のペアを作成、遷移回数をカウント、遷移確率を計算
  transition_prob <- paste0(data[-length(data)], "_", data[-1]) |>
    table() |>
    prop.table() |>
    matrix(nrow = 3, byrow = TRUE)
  return(transition_prob)
}

# 実際のデータから遷移確率を計算
transition_prob_A <- calculate_empirical_transition_prob(data_A)
transition_prob_B <- calculate_empirical_transition_prob(data_B)

# ネットワーク生成関数
plot_transition_graph <- function(prob_matrix, title) {
  normalize_vector <- function(x) {  # ベクトルを標準化(最小値1、最大値10)
    min_x <- min(x)
    max_x <- max(x)
    normalized_x <- 1 + 99 * ((x - min_x) / (max_x - min_x))
    return(normalized_x)
    }

  my_cols <- turbo(100)
  
  combinations <- expand.grid(i = 1:3, j = 1:3) # 全ての組み合わせを生成
  g <- graph.empty(n=3, directed=TRUE) |>       # 空の有向グラフを作成
    add_edges(as.vector(t(combinations)))       # エッジを追加
  V(g)$name <- c("グー", "チョキ", "パー")      # ノードの名前を設定
  V(g)$size <- 40
  # エッジのラベルと太さを計算
  edge_labels <- sapply(
    1:nrow(combinations),
    function(x) sprintf("%.2f", prob_matrix[combinations[x, 1], combinations[x, 2]])
    )
  edge_widths <- sapply(
    1:nrow(combinations),
    function(x) prob_matrix[combinations[x, 1], combinations[x, 2]] * 20
    )
  edge_colors <- my_cols[normalize_vector(edge_widths)]
  E(g)$curved <- 0.5
  E(g)$label <- edge_labels
  E(g)$width <- edge_widths
  E(g)$color <- edge_colors
  g |>
    plot(main=title)  # ネットワークを描画
}

par(mfrow = c(1, 2), mar = c(0,1,1,1))
plot_transition_graph(transition_prob_A, "遷移確率(じゃんけんマシンA)")
plot_transition_graph(transition_prob_B, "遷移確率(じゃんけんマシンB)")

par(mfrow = c(1, 1), mar = c(5.1, 4.1, 4.1, 2.1))

7.1.2 自己相関で周期的なパターンを検出する

自己相関プロットの再現がイマイチ。Bartlett’s formulaを用いた95%信頼区間の表示が同じようにできない。

library(conflicted)
library(tidyverse)
library(patchwork)
library(forecast)

set.seed(42)
n <- 100
x <- seq(0, 100, length.out = n)

# データフレームにデータを格納
df <- data.frame(
  x = 1:n,
  y = sin(pi / 5 * x) + 0.8 * rnorm(n)
  )

# 元の時系列データをプロット
p1 <- df |>
  ggplot(aes(x = x, y = y)) +
  geom_line() +
  labs(
    x = expression(paste("時刻", italic(t))), 
    y = expression(italic(x(t))),
    title = "時系列データ"
    ) +
  theme(aspect.ratio = 1/3)

# 自己相関プロットを作成
p2 <- ggAcf(df$y, lag.max = 40) +
  labs(title = "自己相関プロット", x = "ラグ(ずれ幅)", y = "自己相関係数") +
  theme(aspect.ratio = 1/3)

# ラグ10のラグプロット
y <- df$y
y_lag10 <- dplyr::lead(y, 10)  # ラグ10を取る

# 相関係数を計算
corr <- cor(y, y_lag10, use = "pairwise.complete.obs")

# ラグプロットの作成
p3 <- data.frame(y, y_lag10) |>
  drop_na() |>
  ggplot(aes(x = y, y = y_lag10)) +
  geom_point() +
  geom_smooth(formula = y ~ x, method = "lm", color = "darkblue", fill = "darkblue") +
  annotate("text", x=-2.5, y=2, label=paste0("r = ", round(corr, 2))) +
  labs(x = expression(italic(x(t))), y = expression(italic(x(t+10))), title = "ラグ10での相関") +
  theme(aspect.ratio = 1)

(p1 + plot_spacer() + plot_layout(ncol = 2, widths = c(.7, .3))) / 
  (p2 + p3 + plot_layout(ncol = 2, widths = c(.7, .3)))

7.1.3 フーリエ変換で周波数の情報を取り出す

音声データの取り扱いが分からないので割愛

7.1.4 周波数の時間変化を見る

音声データの取り扱いが分からないので割愛

7.1.5 心電図データの分析

心電図データの取り扱いが分からないので割愛

7.2 空間データのパターンをとらえる

7.2.1 ボロノイ図による空間の分割

最大距離を制限した場合の各領域の面積の出し方が分からない

library(conflicted)
library(tidyverse)
library(deldir)
library(ggforce)
library(viridis)
library(patchwork)

set.seed(42)
df <- as.data.frame(
  rbind(
    matrix(c(0, 0, 0, 1, 1, 0, 1, 1), ncol = 2), #四隅
    matrix(runif(40), ncol = 2)
    )
  ) |>
  distinct()

# 最短距離
df2 <- df |>
  dist() |>
  as.matrix() |>
  as.data.frame() |>
  rowid_to_column() |>
  pivot_longer(!rowid) |>
  dplyr::filter(value > 0) |>
  slice_min(value, by = rowid)

df3 <- df2 |>
  left_join(
    df |>
      rowid_to_column(),
    by = join_by(rowid == rowid)
    ) |>
  left_join(
    df |>
      rowid_to_column() |>
      mutate(rowid = as.character(rowid)),
    by = join_by(name == rowid)
    ) |>
  mutate(
    x = V1.x, y = V2.x, xend = V1.y, yend = V2.y, .keep = "unused"
  )

res <- deldir(x = df3$x, y = df3$y)

df_area <- df3 |>
  mutate(area = res$summary$dir.area)

p1 <- df_area |>
  ggplot(aes(x = x, y = y, fill = area)) +
  geom_voronoi_tile(colour = "black") +
  geom_point() +
  geom_segment(
    aes(x = x, y = y, xend = xend, yend = yend),
    arrow = arrow(
      type = "closed",
      length = unit(0.1, "inches")
      ),
    color = "red"
    ) +
  scale_fill_viridis(option = "turbo") + 
  coord_cartesian(xlim = c(0,1), ylim = c(0,1)) +
  theme(aspect.ratio = 1) +
  labs(title = "ボロノイ図", fill = "面積")

p2 <- df_area |>
  ggplot(aes(x = x, y = y, fill = area)) +
  geom_voronoi_tile(colour = "black", max.radius = 0.2) +
  geom_point() +
  scale_fill_viridis(option = "turbo") + 
  coord_cartesian(xlim = c(0,1), ylim = c(0,1))+
  theme(aspect.ratio = 1) +
  labs(title = "ボロノイ図(最大距離制限付き)", fill = "面積")

p3 <- df2 |>
  ggplot(aes(x = value)) +
  geom_histogram(bins = 10, fill = "blue", alpha = 0.7, color = "black") +
  xlab("最近傍点までの最短") +
  ylab("頻度")

p4 <- df_area |>
  ggplot(aes(x = area)) +
  geom_histogram(bins = 10, alpha = 0.7, color = "black") +
  xlab("各領域の面積") +
  ylab("頻度")

{p1 | p2 } / {p3 | p4}

7.2.2 カーネル密度推定による空間の利用密度の定量化

library(conflicted)
library(tidyverse)
library(deldir)
library(ggforce)
library(viridis)
library(patchwork)

set.seed(0)
df <- as.data.frame(
  rbind(
    matrix(c(0, 0, 0, 1, 1, 0, 1, 1), ncol = 2), #四隅
    matrix(runif(40), ncol = 2)
    )
  ) |>
  distinct()

# 新しいボロノイ図を計算
res <- deldir(x = df$V1, y = df$V2)

p1 <- df |>
  mutate(area = res$summary$dir.area) |>
  ggplot(aes(x = V1, y = V2, fill = area)) +
  geom_voronoi_tile(colour = "black") +
  geom_point() +
  scale_fill_viridis(option = "turbo") + 
  coord_cartesian(xlim = c(0,1), ylim = c(0,1)) +
  theme(aspect.ratio = 1, legend.position = "none", axis.title = element_blank()) +
  labs(title = "ボロノイ図")

p2 <- df |>
  ggplot(aes(x = V1, y = V2)) +
  geom_density_2d_filled(h = 0.25) +
  geom_point(alpha = 0.5) +
  scale_fill_manual(values = turbo(7)) + 
  coord_cartesian(xlim = c(0,1), ylim = c(0,1)) +
  theme(aspect.ratio = 1, legend.position = "none", axis.title = element_blank()) +
  labs(title = "KDEバンド幅=0.25")

p3 <- df |>
  ggplot(aes(x = V1, y = V2)) +
  geom_density_2d_filled(h = 0.5) +
  geom_point(alpha = 0.5) + 
  scale_fill_manual(values = turbo(8)) + 
  coord_cartesian(xlim = c(0,1), ylim = c(0,1)) +
  theme(aspect.ratio = 1, legend.position = "none", axis.title = element_blank()) +
  labs(title = "KDEバンド幅=0.50")

p4 <- df |>
  ggplot(aes(x = V1, y = V2)) +
  geom_density_2d_filled(h = 0.75) +
  geom_point(alpha = 0.5) +
  scale_fill_manual(values = turbo(9)) + 
  coord_cartesian(xlim = c(0,1), ylim = c(0,1)) +
  theme(aspect.ratio = 1, legend.position = "none", axis.title = element_blank()) +
  labs(title = "KDEバンド幅=0.75")


{p1|p2}/{p3|p4}

7.2.3 グレイレベル共起行列を用いた分析

画像の取り扱いがわからないので割愛

7.3 ネットワークのパターンをとらえる

7.3.1 様々な中心性指標

library(conflicted)
library(tidyverse)
library(igraph)
library(viridis)
library(ggraph)
library(tidygraph)

# 適当なネットワークを生成
set.seed(3)  # シード値を固定
G <- sample_gnp(30, 0.1)

g_tidy <- as_tbl_graph(G, directed = FALSE) |>
  activate(nodes) |>
  mutate(
    betweenness = centrality_betweenness(),
    closeness = centrality_closeness(),
    eigen = centrality_eigen(),
    pagerank = centrality_pagerank()
    )

# 描画
p1 <- g_tidy |>
  ggraph() +
  geom_edge_link(color = "black", alpha = 0.5) +
  geom_node_point(aes(color = betweenness), size = 5) +
  scale_color_viridis(option = "turbo") +
  theme_void() +
  theme(aspect.ratio = 1, legend.position = "none") +
  labs(title = "媒介中心性")

p2 <- g_tidy |>
  ggraph() +
  geom_edge_link(color = "black", alpha = 0.5) +
  geom_node_point(aes(color = closeness), size = 5) +
  scale_color_viridis(option = "turbo") +
  theme_void() +
  theme(aspect.ratio = 1, legend.position = "none") +
  labs(title = "近接中心性")

p3 <- g_tidy |>
  ggraph() +
  geom_edge_link(color = "black", alpha = 0.5) +
  geom_node_point(aes(color = eigen), size = 5) +
  scale_color_viridis(option = "turbo") +
  theme_void() +
  theme(aspect.ratio = 1, legend.position = "none") +
  labs(title = "固有値中心性")

p4 <- g_tidy |>
  ggraph() +
  geom_edge_link(color = "black", alpha = 0.5) +
  geom_node_point(aes(color = pagerank), size = 5) +
  scale_color_viridis(option = "turbo") +
  theme_void() +
  theme(aspect.ratio = 1, legend.position = "none") +
  labs(title = "ページランク")

{p1|p2}/{p3|p4}

7.3.2 様々なネットワーク指標の計算例

7.3.2.1

library(conflicted)
library(tidyverse)
library(igraph)
library(viridis)
library(ggraph)
library(tidygraph)

# ネットワークを生成
n <- 49
m <- 1

dim <- sqrt(n)
G_square <- graph.lattice(length = dim, dim = 2, directed = FALSE) |>
  as_tbl_graph(directed = FALSE)
G_random <- erdos.renyi.game(n, 0.2, directed = FALSE) |>
  as_tbl_graph(directed = FALSE)
G_ba <- barabasi.game(n, m, directed = FALSE) |>
  as_tbl_graph(directed = FALSE)

p1 <- G_square |>
  ggraph(layout = "igraph", algorithm ="grid") +
  geom_edge_link(color = "black", alpha = 0.5) +
  geom_node_point(color = "#F8766D", size = 5) +
  theme_void() +
  theme(aspect.ratio = 1, legend.position = "none") +
  labs(title = "正方格子")
  
p2 <- G_random |>
  ggraph(layout = "igraph", algorithm ="randomly") +
  geom_edge_link(color = "black", alpha = 0.5) +
  geom_node_point(color = "#00BA38", size = 5) +
  theme_void() +
  theme(aspect.ratio = 1, legend.position = "none") +
  labs(title = "ランダムネットワーク")

p3 <- G_ba |>
  ggraph(layout = "igraph", algorithm ="fr") +
  geom_edge_link(color = "black", alpha = 0.5) +
  geom_node_point(color = "#619CFF", size = 5) +
  theme_void() +
  theme(aspect.ratio = 1, legend.position = "none") +
  labs(title = "BAネットワーク")

p1 + p2 + p3

7.3.2.2

library(conflicted)
library(tidyverse)
library(igraph)
# library(ggraph)

# ネットワークを生成
n <- 49
m <- 1
dim <- sqrt(n)
G_square <- graph.lattice(length = dim, dim = 2, directed = FALSE)
G_random <- erdos.renyi.game(n, 0.2, directed = FALSE)
G_ba <- barabasi.game(n, m, directed = FALSE)

# ネットワークの指標を計算
compute_network_metrics <- function(G) {
  return(data.frame(
      平均次数 = mean(degree(G)),
      平均最短経路長 = mean_distance(G, directed = FALSE),
      クラスター係数 = transitivity(G, type = "average"),
      同類選択性 = assortativity_degree(G, directed = FALSE)
    ))
}

# 描画
network_names <- c("格子", "ランダム", "BA")

list(G_square, G_random, G_ba) |>
  map(\(x) compute_network_metrics(x)) |>
  list_rbind() |>
  mutate(network_names = factor(network_names, levels = network_names)) |>
  pivot_longer(!network_names) |>
  mutate(name = factor(name, levels = c("平均次数", "平均最短経路長", "クラスター係数", "同類選択性"))) |>
  ggplot(aes(x = network_names, y = value, fill = network_names, label = round(value, 2))) +
  geom_col() +
  geom_text(vjust = -0.5) +
  facet_wrap(vars(name), ncol = 4, scale = "free") +
  theme(legend.position="none", axis.title = element_blank())

第7章はここまで。

LS0tCnRpdGxlOiAi56ysN+eroCDjg5Hjgr/jg7zjg7PjgpLjgajjgonjgYjjgovmjIfmqJnljJYiCmF1dGhvcjogIk9zYW11LCBNT1JJTU9UTyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0aGVtZTogdW5pdGVkICAgIAogICAgbWRfZXh0ZW5zaW9uczogIi1hc2NpaV9pZGVudGlmaWVycyIKICAgIHRvY19mbG9hdDogeWVzCiAgICBmaWdfd2lkdGg6IDcuNQogICAgZmlnX2hlaWdodDogNS42MjUKICAgIGRldjogcmFnZ19wbmcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgrlm7Pjga7lj7PkuIrjga5gc2hvd2Djg5zjgr/jg7PjgpLmirzjgZnjgahS44Gu44Kz44O844OJ44GM6KGo56S644GV44KM44G+44GZ44CCCgojIyA3LjEg5pmC6ZaT55qE44Gq44OR44K/44O844Oz44KS44Go44KJ44GI44KLCgojIyMgNy4xLjEg44GY44KD44KT44GR44KT44Oe44K344Oz44Gu5YiG5p6QCgojIyMjIDcuMS4xLjEKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyDjgrfjg7zjg4nlgKTjgpLlm7rlrpoKc2V0LnNlZWQoNDIpCgojIOODh+ODvOOCv+eUn+aIkOmWouaVsApnZW5lcmF0ZV9kYXRhIDwtIGZ1bmN0aW9uKHRyYW5zaXRpb25fcHJvYiwgbj0xMDAwKSB7CiAgaW5pdGlhbF9zdGF0ZSA8LSBzYW1wbGUuaW50KDMsIDEpICMg5Yid5pyf54q25oWL44KS44Op44Oz44OA44Og44Gr6YG45oqeCiAgIyDpgbfnp7vnorrnjofjgavlvpPjgaPjgabmrKHjga7nirbmhYvjgpLpgbjmip4KICBzdGF0ZXMgPC0gYWNjdW11bGF0ZSgKICAgIC5pbml0ID0gaW5pdGlhbF9zdGF0ZSwKICAgIC54ID0gcmVwKDEsIG4tMSksCiAgICAuZiA9IGZ1bmN0aW9uKHN0YXRlLCAuKSB7CiAgICAgIHNhbXBsZS5pbnQoMywgc2l6ZSA9IDEsIHByb2IgPSB0cmFuc2l0aW9uX3Byb2JbW3N0YXRlXV0pCiAgICAgIH0KICAgICkKICByZXR1cm4oc3RhdGVzKQp9CgojIOOBmOOCg+OCk+OBkeOCk+ODnuOCt+ODs0Hjga7pgbfnp7vnorrnjocKdHJhbnNpdGlvbl9wcm9iX0EgPC0gbGlzdChjKDAuMiwgMC41LCAwLjMpLCBjKDAuNCwgMC4xLCAwLjUpLCBjKDAuMywgMC42LCAwLjEpKQoKIyDjgZjjgoPjgpPjgZHjgpPjg57jgrfjg7NC44Gu6YG356e756K6546HCnRyYW5zaXRpb25fcHJvYl9CIDwtIGxpc3QoYygwLjMsIDAuNCwgMC4zKSwgYygwLjIsIDAuMywgMC41KSwgYygwLjQsIDAuMiwgMC40KSkKCiMg44OH44O844K/55Sf5oiQCmRhdGFfQSA8LSBnZW5lcmF0ZV9kYXRhKHRyYW5zaXRpb25fcHJvYl9BKQpkYXRhX0IgPC0gZ2VuZXJhdGVfZGF0YSh0cmFuc2l0aW9uX3Byb2JfQikKCiMg5pyA5Yid44GuNTDlm57jga7jgZjjgoPjgpPjgZHjgpPjga7mmYLns7vliJfjgpLmipjjgoznt5rjgrDjg6njg5Xjgafmj4/nlLsKZGZfbG9uZyA8LSBkYXRhLmZyYW1lKFJvdW5kPTE6NTAsIE1hY2hpbmVfQT1kYXRhX0FbMTo1MF0sIE1hY2hpbmVfQj1kYXRhX0JbMTo1MF0pIHw+CiAgcGl2b3RfbG9uZ2VyKCFSb3VuZCkKCnAxIDwtIGRmX2xvbmcgfD4KICBnZ3Bsb3QoYWVzKHg9Um91bmQsIHk9dmFsdWUsIGNvbG9yPW5hbWUpKSArCiAgZ2VvbV9saW5lKGFscGhhID0gMC41KSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyAgCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcz0xOjMsIGxhYmVscz1jKCLjgrDjg7wiLCAi44OB44On44KtIiwgIuODkeODvCIpKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobGFiZWxzID0gYygi44GY44KD44KT44GR44KT44Oe44K344OzQSIsICLjgZjjgoPjgpPjgZHjgpPjg57jgrfjg7NCIikpICsKICBsYWJzKHRpdGxlPSLliIbmnpDlr77osaHjga7jgrjjg6Pjg7PjgrHjg7PmmYLns7vliJfvvIjmnIDliJ3jga41MOODqeOCpuODs+ODie+8iSIpICsKICB0aGVtZSgKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIgogICkKCiMg5ZCE5omL44Gu6YCj57aa5Zue5pWw44Gu5bmz5Z2H5YCk44KS6KiI566X44GZ44KL6Zai5pWwCmNhbGN1bGF0ZV9ydW5fbWVhbiA8LSBmdW5jdGlvbihkYXRhKSB7CiAgcmxlX2RhdGEgPC0gcmxlKGRhdGEpICAjIOimgee0oOOBjOmAo+e2muOBmeOCi+mDqOWIhuOBruWApOOBqOmVt+OBleOCkuioiOeulwogIGNvdW50cyA8LSBzcGxpdChybGVfZGF0YSRsZW5ndGhzLCBybGVfZGF0YSR2YWx1ZXMpICAjIOWQhOaJi+OBrumAo+e2muWbnuaVsOOCkuWPluW+lwogIHJldHVybihzYXBwbHkoY291bnRzLCBtZWFuLCBuYS5ybT1UUlVFKSkgICMg5ZCE5omL44Gu6YCj57aa5Zue5pWw44Gu5bmz5Z2H5YCk44KS6KiI566XCn0KCiMg44GY44KD44KT44GR44KT44Oe44K344OzQeOBqELjga7lkITmiYvjga7pgKPntprlm57mlbDjga7lubPlnYflgKTjgpLoqIjnrpcKbWVhbl9ydW5fQSA8LSBjYWxjdWxhdGVfcnVuX21lYW4oZGF0YV9BKQptZWFuX3J1bl9CIDwtIGNhbGN1bGF0ZV9ydW5fbWVhbihkYXRhX0IpCgojIOmbhuWbo+ajkuOCsOODqeODleOBp+WQhOaJi+OBruWHuuePvuWJsuWQiOOCkuaPj+eUuwpwMiA8LSByYmluZCgKICB0YWJsZShkYXRhX0EpIC8gbGVuZ3RoKGRhdGFfQSksCiAgdGFibGUoZGF0YV9CKSAvIGxlbmd0aChkYXRhX0IpCiAgKSB8PgogIGFzLmRhdGEuZnJhbWUoKSB8PgogIG11dGF0ZShtYWNoaW5lID0gYygi44GY44KD44KT44GR44KT44Oe44K344OzQSIsICLjgZjjgoPjgpPjgZHjgpPjg57jgrfjg7NCIikpIHw+CiAgcGl2b3RfbG9uZ2VyKCFtYWNoaW5lKSB8PgogIGdncGxvdChhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSwgZmlsbCA9IG1hY2hpbmUpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCIxIiA9ICLjgrDjg7wiLCAiMiIgPSAi44OB44On44KtIiwgIjMiID0gIuODkeODvCIpKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZSA9ICLlh7rnj77poLvluqYiKQoKIyDpm4blm6Pmo5LjgrDjg6njg5XjgaflkITmiYvjga7pgKPntprlm57mlbDjga7lubPlnYflgKTjgpLmj4/nlLsKcDMgPC1yYmluZChtZWFuX3J1bl9BLCBtZWFuX3J1bl9CKSB8PgogIGFzLmRhdGEuZnJhbWUoKSB8PgogIG11dGF0ZShtYWNoaW5lID0gYygi44GY44KD44KT44GR44KT44Oe44K344OzQSIsICLjgZjjgoPjgpPjgZHjgpPjg57jgrfjg7NCIikpIHw+CiAgcGl2b3RfbG9uZ2VyKCFtYWNoaW5lKSB8PgogIGdncGxvdChhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSwgZmlsbCA9IG1hY2hpbmUpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCIxIiA9ICLjgrDjg7wiLCAiMiIgPSAi44OB44On44KtIiwgIjMiID0gIuODkeODvCIpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZSA9ICLmjIHntprlm57mlbDjga7lubPlnYflgKQiKQoKcDEgLyB7cDIgfCBwM30KCmBgYAoKCiMjIyMgNy4xLjEuMgpnZ3JhcGjjgafmm7jjgY3nm7TjgZfjgZ/jgYQKCmBgYHtyfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkodmlyaWRpc0xpdGUpCgojIOOCt+ODvOODieWApOOCkuWbuuWumgpzZXQuc2VlZCg0MikKCiMg44OH44O844K/55Sf5oiQ6Zai5pWwCmdlbmVyYXRlX2RhdGEgPC0gZnVuY3Rpb24odHJhbnNpdGlvbl9wcm9iLCBuPTEwMDApIHsKICBpbml0aWFsX3N0YXRlIDwtIHNhbXBsZS5pbnQoMywgMSkgIyDliJ3mnJ/nirbmhYvjgpLjg6njg7Pjg4Djg6Djgavpgbjmip4KICAjIOmBt+enu+eiuueOh+OBq+W+k+OBo+OBpuasoeOBrueKtuaFi+OCkumBuOaKngogIHN0YXRlcyA8LSBhY2N1bXVsYXRlKAogICAgLmluaXQgPSBpbml0aWFsX3N0YXRlLAogICAgLnggPSByZXAoMSwgbi0xKSwKICAgIC5mID0gZnVuY3Rpb24oc3RhdGUsIC4pIHsKICAgICAgc2FtcGxlLmludCgzLCBzaXplID0gMSwgcHJvYiA9IHRyYW5zaXRpb25fcHJvYltbc3RhdGVdXSkKICAgICAgfQogICAgKQogIHJldHVybihzdGF0ZXMpCn0KCiMg44GY44KD44KT44GR44KT44Oe44K344OzQeOBrumBt+enu+eiuueOhwp0cmFuc2l0aW9uX3Byb2JfQSA8LSBsaXN0KGMoMC4yLCAwLjUsIDAuMyksIGMoMC40LCAwLjEsIDAuNSksIGMoMC4zLCAwLjYsIDAuMSkpCgojIOOBmOOCg+OCk+OBkeOCk+ODnuOCt+ODs0Ljga7pgbfnp7vnorrnjocKdHJhbnNpdGlvbl9wcm9iX0IgPC0gbGlzdChjKDAuMywgMC40LCAwLjMpLCBjKDAuMiwgMC4zLCAwLjUpLCBjKDAuNCwgMC4yLCAwLjQpKQoKIyDjg4fjg7zjgr/nlJ/miJAKZGF0YV9BIDwtIGdlbmVyYXRlX2RhdGEodHJhbnNpdGlvbl9wcm9iX0EpCmRhdGFfQiA8LSBnZW5lcmF0ZV9kYXRhKHRyYW5zaXRpb25fcHJvYl9CKQoKIyDjg4fjg7zjgr/jgYvjgonpgbfnp7vnorrnjofjgpLoqIjnrpfjgZnjgovplqLmlbAKY2FsY3VsYXRlX2VtcGlyaWNhbF90cmFuc2l0aW9uX3Byb2IgPC0gZnVuY3Rpb24oZGF0YSkgewogICMg54++5Zyo44Gu54q25oWL44Go5qyh44Gu54q25oWL44Gu44Oa44Ki44KS5L2c5oiQ44CB6YG356e75Zue5pWw44KS44Kr44Km44Oz44OI44CB6YG356e756K6546H44KS6KiI566XCiAgdHJhbnNpdGlvbl9wcm9iIDwtIHBhc3RlMChkYXRhWy1sZW5ndGgoZGF0YSldLCAiXyIsIGRhdGFbLTFdKSB8PgogICAgdGFibGUoKSB8PgogICAgcHJvcC50YWJsZSgpIHw+CiAgICBtYXRyaXgobnJvdyA9IDMsIGJ5cm93ID0gVFJVRSkKICByZXR1cm4odHJhbnNpdGlvbl9wcm9iKQp9CgojIOWun+mam+OBruODh+ODvOOCv+OBi+OCiemBt+enu+eiuueOh+OCkuioiOeulwp0cmFuc2l0aW9uX3Byb2JfQSA8LSBjYWxjdWxhdGVfZW1waXJpY2FsX3RyYW5zaXRpb25fcHJvYihkYXRhX0EpCnRyYW5zaXRpb25fcHJvYl9CIDwtIGNhbGN1bGF0ZV9lbXBpcmljYWxfdHJhbnNpdGlvbl9wcm9iKGRhdGFfQikKCiMg44ON44OD44OI44Ov44O844Kv55Sf5oiQ6Zai5pWwCnBsb3RfdHJhbnNpdGlvbl9ncmFwaCA8LSBmdW5jdGlvbihwcm9iX21hdHJpeCwgdGl0bGUpIHsKICBub3JtYWxpemVfdmVjdG9yIDwtIGZ1bmN0aW9uKHgpIHsgICMg44OZ44Kv44OI44Or44KS5qiZ5rqW5YyW77yI5pyA5bCP5YCkMeOAgeacgOWkp+WApDEw77yJCiAgICBtaW5feCA8LSBtaW4oeCkKICAgIG1heF94IDwtIG1heCh4KQogICAgbm9ybWFsaXplZF94IDwtIDEgKyA5OSAqICgoeCAtIG1pbl94KSAvIChtYXhfeCAtIG1pbl94KSkKICAgIHJldHVybihub3JtYWxpemVkX3gpCiAgICB9CgogIG15X2NvbHMgPC0gdHVyYm8oMTAwKQogIAogIGNvbWJpbmF0aW9ucyA8LSBleHBhbmQuZ3JpZChpID0gMTozLCBqID0gMTozKSAjIOWFqOOBpuOBrue1hOOBv+WQiOOCj+OBm+OCkueUn+aIkAogIGcgPC0gZ3JhcGguZW1wdHkobj0zLCBkaXJlY3RlZD1UUlVFKSB8PiAgICAgICAjIOepuuOBruacieWQkeOCsOODqeODleOCkuS9nOaIkAogICAgYWRkX2VkZ2VzKGFzLnZlY3Rvcih0KGNvbWJpbmF0aW9ucykpKSAgICAgICAjIOOCqOODg+OCuOOCkui/veWKoAogIFYoZykkbmFtZSA8LSBjKCLjgrDjg7wiLCAi44OB44On44KtIiwgIuODkeODvCIpICAgICAgIyDjg47jg7zjg4njga7lkI3liY3jgpLoqK3lrpoKICBWKGcpJHNpemUgPC0gNDAKICAjIOOCqOODg+OCuOOBruODqeODmeODq+OBqOWkquOBleOCkuioiOeulwogIGVkZ2VfbGFiZWxzIDwtIHNhcHBseSgKICAgIDE6bnJvdyhjb21iaW5hdGlvbnMpLAogICAgZnVuY3Rpb24oeCkgc3ByaW50ZigiJS4yZiIsIHByb2JfbWF0cml4W2NvbWJpbmF0aW9uc1t4LCAxXSwgY29tYmluYXRpb25zW3gsIDJdXSkKICAgICkKICBlZGdlX3dpZHRocyA8LSBzYXBwbHkoCiAgICAxOm5yb3coY29tYmluYXRpb25zKSwKICAgIGZ1bmN0aW9uKHgpIHByb2JfbWF0cml4W2NvbWJpbmF0aW9uc1t4LCAxXSwgY29tYmluYXRpb25zW3gsIDJdXSAqIDIwCiAgICApCiAgZWRnZV9jb2xvcnMgPC0gbXlfY29sc1tub3JtYWxpemVfdmVjdG9yKGVkZ2Vfd2lkdGhzKV0KICBFKGcpJGN1cnZlZCA8LSAwLjUKICBFKGcpJGxhYmVsIDwtIGVkZ2VfbGFiZWxzCiAgRShnKSR3aWR0aCA8LSBlZGdlX3dpZHRocwogIEUoZykkY29sb3IgPC0gZWRnZV9jb2xvcnMKICBnIHw+CiAgICBwbG90KG1haW49dGl0bGUpICAjIOODjeODg+ODiOODr+ODvOOCr+OCkuaPj+eUuwp9CgpwYXIobWZyb3cgPSBjKDEsIDIpLCBtYXIgPSBjKDAsMSwxLDEpKQpwbG90X3RyYW5zaXRpb25fZ3JhcGgodHJhbnNpdGlvbl9wcm9iX0EsICLpgbfnp7vnorrnjofvvIjjgZjjgoPjgpPjgZHjgpPjg57jgrfjg7NB77yJIikKcGxvdF90cmFuc2l0aW9uX2dyYXBoKHRyYW5zaXRpb25fcHJvYl9CLCAi6YG356e756K6546H77yI44GY44KD44KT44GR44KT44Oe44K344OzQu+8iSIpCnBhcihtZnJvdyA9IGMoMSwgMSksIG1hciA9IGMoNS4xLCA0LjEsIDQuMSwgMi4xKSkKYGBgCgojIyMgNy4xLjIg6Ieq5bex55u46Zai44Gn5ZGo5pyf55qE44Gq44OR44K/44O844Oz44KS5qSc5Ye644GZ44KLCgroh6rlt7Hnm7jplqLjg5fjg63jg4Pjg4jjga7lho3nj77jgYzjgqTjg57jgqTjg4HjgIJCYXJ0bGV0dOKAmXMgZm9ybXVsYeOCkueUqOOBhOOBnzk1JeS/oemgvOWMuumWk+OBruihqOekuuOBjOWQjOOBmOOCiOOBhuOBq+OBp+OBjeOBquOBhOOAggoKYGBge3IgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShmb3JlY2FzdCkKCnNldC5zZWVkKDQyKQpuIDwtIDEwMAp4IDwtIHNlcSgwLCAxMDAsIGxlbmd0aC5vdXQgPSBuKQoKIyDjg4fjg7zjgr/jg5Xjg6zjg7zjg6Djgavjg4fjg7zjgr/jgpLmoLzntI0KZGYgPC0gZGF0YS5mcmFtZSgKICB4ID0gMTpuLAogIHkgPSBzaW4ocGkgLyA1ICogeCkgKyAwLjggKiBybm9ybShuKQogICkKCiMg5YWD44Gu5pmC57O75YiX44OH44O844K/44KS44OX44Ot44OD44OICnAxIDwtIGRmIHw+CiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHkpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnMoCiAgICB4ID0gZXhwcmVzc2lvbihwYXN0ZSgi5pmC5Yi7IiwgaXRhbGljKHQpKSksIAogICAgeSA9IGV4cHJlc3Npb24oaXRhbGljKHgodCkpKSwKICAgIHRpdGxlID0gIuaZguezu+WIl+ODh+ODvOOCvyIKICAgICkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEvMykKCiMg6Ieq5bex55u46Zai44OX44Ot44OD44OI44KS5L2c5oiQCnAyIDwtIGdnQWNmKGRmJHksIGxhZy5tYXggPSA0MCkgKwogIGxhYnModGl0bGUgPSAi6Ieq5bex55u46Zai44OX44Ot44OD44OIIiwgeCA9ICLjg6njgrDvvIjjgZrjgozluYXvvIkiLCB5ID0gIuiHquW3seebuOmWouS/guaVsCIpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLzMpCgojIOODqeOCsDEw44Gu44Op44Kw44OX44Ot44OD44OICnkgPC0gZGYkeQp5X2xhZzEwIDwtIGRwbHlyOjpsZWFkKHksIDEwKSAgIyDjg6njgrAxMOOCkuWPluOCiwoKIyDnm7jplqLkv4LmlbDjgpLoqIjnrpcKY29yciA8LSBjb3IoeSwgeV9sYWcxMCwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCgojIOODqeOCsOODl+ODreODg+ODiOOBruS9nOaIkApwMyA8LSBkYXRhLmZyYW1lKHksIHlfbGFnMTApIHw+CiAgZHJvcF9uYSgpIHw+CiAgZ2dwbG90KGFlcyh4ID0geSwgeSA9IHlfbGFnMTApKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChmb3JtdWxhID0geSB+IHgsIG1ldGhvZCA9ICJsbSIsIGNvbG9yID0gImRhcmtibHVlIiwgZmlsbCA9ICJkYXJrYmx1ZSIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHg9LTIuNSwgeT0yLCBsYWJlbD1wYXN0ZTAoInIgPSAiLCByb3VuZChjb3JyLCAyKSkpICsKICBsYWJzKHggPSBleHByZXNzaW9uKGl0YWxpYyh4KHQpKSksIHkgPSBleHByZXNzaW9uKGl0YWxpYyh4KHQrMTApKSksIHRpdGxlID0gIuODqeOCsDEw44Gn44Gu55u46ZaiIikgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCgoocDEgKyBwbG90X3NwYWNlcigpICsgcGxvdF9sYXlvdXQobmNvbCA9IDIsIHdpZHRocyA9IGMoLjcsIC4zKSkpIC8gCiAgKHAyICsgcDMgKyBwbG90X2xheW91dChuY29sID0gMiwgd2lkdGhzID0gYyguNywgLjMpKSkKCmBgYAoKIyMjIDcuMS4zIOODleODvOODquOCqOWkieaPm+OBp+WRqOazouaVsOOBruaDheWgseOCkuWPluOCiuWHuuOBmQrpn7Plo7Djg4fjg7zjgr/jga7lj5bjgormibHjgYTjgYzliIbjgYvjgonjgarjgYTjga7jgaflibLmhJsKCiMjIyA3LjEuNCDlkajms6LmlbDjga7mmYLplpPlpInljJbjgpLopovjgosK6Z+z5aOw44OH44O844K/44Gu5Y+W44KK5omx44GE44GM5YiG44GL44KJ44Gq44GE44Gu44Gn5Ymy5oSbCgojIyMgNy4xLjUg5b+D6Zu75Zuz44OH44O844K/44Gu5YiG5p6QCuW/g+mbu+Wbs+ODh+ODvOOCv+OBruWPluOCiuaJseOBhOOBjOWIhuOBi+OCieOBquOBhOOBruOBp+WJsuaEmwoKCiMjIDcuMiDnqbrplpPjg4fjg7zjgr/jga7jg5Hjgr/jg7zjg7PjgpLjgajjgonjgYjjgosKCiMjIyA3LjIuMSDjg5zjg63jg47jgqTlm7PjgavjgojjgovnqbrplpPjga7liIblibIKCuacgOWkp+i3nembouOCkuWItumZkOOBl+OBn+WgtOWQiOOBruWQhOmgmOWfn+OBrumdouepjeOBruWHuuOBl+aWueOBjOWIhuOBi+OCieOBquOBhAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkZWxkaXIpCmxpYnJhcnkoZ2dmb3JjZSkKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KHBhdGNod29yaykKCnNldC5zZWVkKDQyKQpkZiA8LSBhcy5kYXRhLmZyYW1lKAogIHJiaW5kKAogICAgbWF0cml4KGMoMCwgMCwgMCwgMSwgMSwgMCwgMSwgMSksIG5jb2wgPSAyKSwgI+Wbm+mahQogICAgbWF0cml4KHJ1bmlmKDQwKSwgbmNvbCA9IDIpCiAgICApCiAgKSB8PgogIGRpc3RpbmN0KCkKCiMg5pyA55+t6Led6ZuiCmRmMiA8LSBkZiB8PgogIGRpc3QoKSB8PgogIGFzLm1hdHJpeCgpIHw+CiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgcm93aWRfdG9fY29sdW1uKCkgfD4KICBwaXZvdF9sb25nZXIoIXJvd2lkKSB8PgogIGRwbHlyOjpmaWx0ZXIodmFsdWUgPiAwKSB8PgogIHNsaWNlX21pbih2YWx1ZSwgYnkgPSByb3dpZCkKCmRmMyA8LSBkZjIgfD4KICBsZWZ0X2pvaW4oCiAgICBkZiB8PgogICAgICByb3dpZF90b19jb2x1bW4oKSwKICAgIGJ5ID0gam9pbl9ieShyb3dpZCA9PSByb3dpZCkKICAgICkgfD4KICBsZWZ0X2pvaW4oCiAgICBkZiB8PgogICAgICByb3dpZF90b19jb2x1bW4oKSB8PgogICAgICBtdXRhdGUocm93aWQgPSBhcy5jaGFyYWN0ZXIocm93aWQpKSwKICAgIGJ5ID0gam9pbl9ieShuYW1lID09IHJvd2lkKQogICAgKSB8PgogIG11dGF0ZSgKICAgIHggPSBWMS54LCB5ID0gVjIueCwgeGVuZCA9IFYxLnksIHllbmQgPSBWMi55LCAua2VlcCA9ICJ1bnVzZWQiCiAgKQoKcmVzIDwtIGRlbGRpcih4ID0gZGYzJHgsIHkgPSBkZjMkeSkKCmRmX2FyZWEgPC0gZGYzIHw+CiAgbXV0YXRlKGFyZWEgPSByZXMkc3VtbWFyeSRkaXIuYXJlYSkKCnAxIDwtIGRmX2FyZWEgfD4KICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geSwgZmlsbCA9IGFyZWEpKSArCiAgZ2VvbV92b3Jvbm9pX3RpbGUoY29sb3VyID0gImJsYWNrIikgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zZWdtZW50KAogICAgYWVzKHggPSB4LCB5ID0geSwgeGVuZCA9IHhlbmQsIHllbmQgPSB5ZW5kKSwKICAgIGFycm93ID0gYXJyb3coCiAgICAgIHR5cGUgPSAiY2xvc2VkIiwKICAgICAgbGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKQogICAgICApLAogICAgY29sb3IgPSAicmVkIgogICAgKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIpICsgCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSkpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgbGFicyh0aXRsZSA9ICLjg5zjg63jg47jgqTlm7MiLCBmaWxsID0gIumdouepjSIpCgpwMiA8LSBkZl9hcmVhIHw+CiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSBhcmVhKSkgKwogIGdlb21fdm9yb25vaV90aWxlKGNvbG91ciA9ICJibGFjayIsIG1heC5yYWRpdXMgPSAwLjIpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpKSsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgbGFicyh0aXRsZSA9ICLjg5zjg63jg47jgqTlm7PvvIjmnIDlpKfot53pm6LliLbpmZDku5jjgY3vvIkiLCBmaWxsID0gIumdouepjSIpCgpwMyA8LSBkZjIgfD4KICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAsIGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC43LCBjb2xvciA9ICJibGFjayIpICsKICB4bGFiKCLmnIDov5Hlgo3ngrnjgb7jgafjga7mnIDnn60iKSArCiAgeWxhYigi6aC75bqmIikKCnA0IDwtIGRmX2FyZWEgfD4KICBnZ3Bsb3QoYWVzKHggPSBhcmVhKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMCwgYWxwaGEgPSAwLjcsIGNvbG9yID0gImJsYWNrIikgKwogIHhsYWIoIuWQhOmgmOWfn+OBrumdouepjSIpICsKICB5bGFiKCLpoLvluqYiKQoKe3AxIHwgcDIgfSAvIHtwMyB8IHA0fQpgYGAKCgoKIyMjIDcuMi4yIOOCq+ODvOODjeODq+WvhuW6puaOqOWumuOBq+OCiOOCi+epuumWk+OBruWIqeeUqOWvhuW6puOBruWumumHj+WMlgpgYGB7cn0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkZWxkaXIpCmxpYnJhcnkoZ2dmb3JjZSkKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KHBhdGNod29yaykKCnNldC5zZWVkKDApCmRmIDwtIGFzLmRhdGEuZnJhbWUoCiAgcmJpbmQoCiAgICBtYXRyaXgoYygwLCAwLCAwLCAxLCAxLCAwLCAxLCAxKSwgbmNvbCA9IDIpLCAj5Zub6ZqFCiAgICBtYXRyaXgocnVuaWYoNDApLCBuY29sID0gMikKICAgICkKICApIHw+CiAgZGlzdGluY3QoKQoKIyDmlrDjgZfjgYTjg5zjg63jg47jgqTlm7PjgpLoqIjnrpcKcmVzIDwtIGRlbGRpcih4ID0gZGYkVjEsIHkgPSBkZiRWMikKCnAxIDwtIGRmIHw+CiAgbXV0YXRlKGFyZWEgPSByZXMkc3VtbWFyeSRkaXIuYXJlYSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBWMSwgeSA9IFYyLCBmaWxsID0gYXJlYSkpICsKICBnZW9tX3Zvcm9ub2lfdGlsZShjb2xvdXIgPSAiYmxhY2siKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKyAKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnModGl0bGUgPSAi44Oc44Ot44OO44Kk5ZuzIikKCnAyIDwtIGRmIHw+CiAgZ2dwbG90KGFlcyh4ID0gVjEsIHkgPSBWMikpICsKICBnZW9tX2RlbnNpdHlfMmRfZmlsbGVkKGggPSAwLjI1KSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHR1cmJvKDcpKSArIAogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLDEpLCB5bGltID0gYygwLDEpKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZSA9ICJLREXjg5Djg7Pjg4nluYU9MC4yNSIpCgpwMyA8LSBkZiB8PgogIGdncGxvdChhZXMoeCA9IFYxLCB5ID0gVjIpKSArCiAgZ2VvbV9kZW5zaXR5XzJkX2ZpbGxlZChoID0gMC41KSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB0dXJibyg4KSkgKyAKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnModGl0bGUgPSAiS0RF44OQ44Oz44OJ5bmFPTAuNTAiKQoKcDQgPC0gZGYgfD4KICBnZ3Bsb3QoYWVzKHggPSBWMSwgeSA9IFYyKSkgKwogIGdlb21fZGVuc2l0eV8yZF9maWxsZWQoaCA9IDAuNzUpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gdHVyYm8oOSkpICsgCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsMSksIHlsaW0gPSBjKDAsMSkpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIktEReODkOODs+ODieW5hT0wLjc1IikKCgp7cDF8cDJ9L3twM3xwNH0KYGBgCgoKIyMjIDcuMi4zIOOCsOODrOOCpOODrOODmeODq+WFsei1t+ihjOWIl+OCkueUqOOBhOOBn+WIhuaekArnlLvlg4/jga7lj5bjgormibHjgYTjgYzjgo/jgYvjgonjgarjgYTjga7jgaflibLmhJsKCgojIyA3LjMg44ON44OD44OI44Ov44O844Kv44Gu44OR44K/44O844Oz44KS44Go44KJ44GI44KLCgojIyMjIDcuMy4xIOanmOOAheOBquS4reW/g+aAp+aMh+aomQoKYGBge3IgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dyYXBoKQpsaWJyYXJ5KHRpZHlncmFwaCkKCiMg6YGp5b2T44Gq44ON44OD44OI44Ov44O844Kv44KS55Sf5oiQCnNldC5zZWVkKDMpICAjIOOCt+ODvOODieWApOOCkuWbuuWumgpHIDwtIHNhbXBsZV9nbnAoMzAsIDAuMSkKCmdfdGlkeSA8LSBhc190YmxfZ3JhcGgoRywgZGlyZWN0ZWQgPSBGQUxTRSkgfD4KICBhY3RpdmF0ZShub2RlcykgfD4KICBtdXRhdGUoCiAgICBiZXR3ZWVubmVzcyA9IGNlbnRyYWxpdHlfYmV0d2Vlbm5lc3MoKSwKICAgIGNsb3NlbmVzcyA9IGNlbnRyYWxpdHlfY2xvc2VuZXNzKCksCiAgICBlaWdlbiA9IGNlbnRyYWxpdHlfZWlnZW4oKSwKICAgIHBhZ2VyYW5rID0gY2VudHJhbGl0eV9wYWdlcmFuaygpCiAgICApCgojIOaPj+eUuwpwMSA8LSBnX3RpZHkgfD4KICBnZ3JhcGgoKSArCiAgZ2VvbV9lZGdlX2xpbmsoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBiZXR3ZWVubmVzcyksIHNpemUgPSA1KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIuWqkuS7i+S4reW/g+aApyIpCgpwMiA8LSBnX3RpZHkgfD4KICBnZ3JhcGgoKSArCiAgZ2VvbV9lZGdlX2xpbmsoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3IgPSBjbG9zZW5lc3MpLCBzaXplID0gNSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICLov5HmjqXkuK3lv4PmgKciKQoKcDMgPC0gZ190aWR5IHw+CiAgZ2dyYXBoKCkgKwogIGdlb21fZWRnZV9saW5rKGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gZWlnZW4pLCBzaXplID0gNSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICLlm7rmnInlgKTkuK3lv4PmgKciKQoKcDQgPC0gZ190aWR5IHw+CiAgZ2dyYXBoKCkgKwogIGdlb21fZWRnZV9saW5rKGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gcGFnZXJhbmspLCBzaXplID0gNSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIikgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICLjg5rjg7zjgrjjg6njg7Pjgq8iKQoKe3AxfHAyfS97cDN8cDR9CmBgYAoKIyMjIDcuMy4yIOanmOOAheOBquODjeODg+ODiOODr+ODvOOCr+aMh+aomeOBruioiOeul+S+iwoKIyMjIyA3LjMuMi4xCmBgYHtyfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KGdncmFwaCkKbGlicmFyeSh0aWR5Z3JhcGgpCgojIOODjeODg+ODiOODr+ODvOOCr+OCkueUn+aIkApuIDwtIDQ5Cm0gPC0gMQoKZGltIDwtIHNxcnQobikKR19zcXVhcmUgPC0gZ3JhcGgubGF0dGljZShsZW5ndGggPSBkaW0sIGRpbSA9IDIsIGRpcmVjdGVkID0gRkFMU0UpIHw+CiAgYXNfdGJsX2dyYXBoKGRpcmVjdGVkID0gRkFMU0UpCkdfcmFuZG9tIDwtIGVyZG9zLnJlbnlpLmdhbWUobiwgMC4yLCBkaXJlY3RlZCA9IEZBTFNFKSB8PgogIGFzX3RibF9ncmFwaChkaXJlY3RlZCA9IEZBTFNFKQpHX2JhIDwtIGJhcmFiYXNpLmdhbWUobiwgbSwgZGlyZWN0ZWQgPSBGQUxTRSkgfD4KICBhc190YmxfZ3JhcGgoZGlyZWN0ZWQgPSBGQUxTRSkKCnAxIDwtIEdfc3F1YXJlIHw+CiAgZ2dyYXBoKGxheW91dCA9ICJpZ3JhcGgiLCBhbGdvcml0aG0gPSJncmlkIikgKwogIGdlb21fZWRnZV9saW5rKGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsKICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAiI0Y4NzY2RCIsIHNpemUgPSA1KSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIuato+aWueagvOWtkCIpCiAgCnAyIDwtIEdfcmFuZG9tIHw+CiAgZ2dyYXBoKGxheW91dCA9ICJpZ3JhcGgiLCBhbGdvcml0aG0gPSJyYW5kb21seSIpICsKICBnZW9tX2VkZ2VfbGluayhjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC41KSArCiAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gIiMwMEJBMzgiLCBzaXplID0gNSkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICLjg6njg7Pjg4Djg6Djg43jg4Pjg4jjg6/jg7zjgq8iKQoKcDMgPC0gR19iYSB8PgogIGdncmFwaChsYXlvdXQgPSAiaWdyYXBoIiwgYWxnb3JpdGhtID0iZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNSkgKwogIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICIjNjE5Q0ZGIiwgc2l6ZSA9IDUpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEsIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiQkHjg43jg4Pjg4jjg6/jg7zjgq8iKQoKcDEgKyBwMiArIHAzCmBgYAoKIyMjIyA3LjMuMi4yCgpgYGB7cn0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShpZ3JhcGgpCiMgbGlicmFyeShnZ3JhcGgpCgojIOODjeODg+ODiOODr+ODvOOCr+OCkueUn+aIkApuIDwtIDQ5Cm0gPC0gMQpkaW0gPC0gc3FydChuKQpHX3NxdWFyZSA8LSBncmFwaC5sYXR0aWNlKGxlbmd0aCA9IGRpbSwgZGltID0gMiwgZGlyZWN0ZWQgPSBGQUxTRSkKR19yYW5kb20gPC0gZXJkb3MucmVueWkuZ2FtZShuLCAwLjIsIGRpcmVjdGVkID0gRkFMU0UpCkdfYmEgPC0gYmFyYWJhc2kuZ2FtZShuLCBtLCBkaXJlY3RlZCA9IEZBTFNFKQoKIyDjg43jg4Pjg4jjg6/jg7zjgq/jga7mjIfmqJnjgpLoqIjnrpcKY29tcHV0ZV9uZXR3b3JrX21ldHJpY3MgPC0gZnVuY3Rpb24oRykgewogIHJldHVybihkYXRhLmZyYW1lKAogICAgICDlubPlnYfmrKHmlbAgPSBtZWFuKGRlZ3JlZShHKSksCiAgICAgIOW5s+Wdh+acgOefree1jOi3r+mVtyA9IG1lYW5fZGlzdGFuY2UoRywgZGlyZWN0ZWQgPSBGQUxTRSksCiAgICAgIOOCr+ODqeOCueOCv+ODvOS/guaVsCA9IHRyYW5zaXRpdml0eShHLCB0eXBlID0gImF2ZXJhZ2UiKSwKICAgICAg5ZCM6aGe6YG45oqe5oCnID0gYXNzb3J0YXRpdml0eV9kZWdyZWUoRywgZGlyZWN0ZWQgPSBGQUxTRSkKICAgICkpCn0KCiMg5o+P55S7Cm5ldHdvcmtfbmFtZXMgPC0gYygi5qC85a2QIiwgIuODqeODs+ODgOODoCIsICJCQSIpCgpsaXN0KEdfc3F1YXJlLCBHX3JhbmRvbSwgR19iYSkgfD4KICBtYXAoXCh4KSBjb21wdXRlX25ldHdvcmtfbWV0cmljcyh4KSkgfD4KICBsaXN0X3JiaW5kKCkgfD4KICBtdXRhdGUobmV0d29ya19uYW1lcyA9IGZhY3RvcihuZXR3b3JrX25hbWVzLCBsZXZlbHMgPSBuZXR3b3JrX25hbWVzKSkgfD4KICBwaXZvdF9sb25nZXIoIW5ldHdvcmtfbmFtZXMpIHw+CiAgbXV0YXRlKG5hbWUgPSBmYWN0b3IobmFtZSwgbGV2ZWxzID0gYygi5bmz5Z2H5qyh5pWwIiwgIuW5s+Wdh+acgOefree1jOi3r+mVtyIsICLjgq/jg6njgrnjgr/jg7zkv4LmlbAiLCAi5ZCM6aGe6YG45oqe5oCnIikpKSB8PgogIGdncGxvdChhZXMoeCA9IG5ldHdvcmtfbmFtZXMsIHkgPSB2YWx1ZSwgZmlsbCA9IG5ldHdvcmtfbmFtZXMsIGxhYmVsID0gcm91bmQodmFsdWUsIDIpKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dCh2anVzdCA9IC0wLjUpICsKICBmYWNldF93cmFwKHZhcnMobmFtZSksIG5jb2wgPSA0LCBzY2FsZSA9ICJmcmVlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCgpgYGAKCuesrDfnq6Djga/jgZPjgZPjgb7jgafjgIIK