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

1.1 データを可視化するということ

1.1.3 日本の総人口の推移

library(conflicted)
library(tidyverse)

data.frame(
  year = 1990:2022,
  population = c(
    1.23611, 1.24101, 1.24567, 1.24938, 1.25265, 1.2557, 1.25859, 1.26157,
    1.26472, 1.26667, 1.26926, 1.27316, 1.27486, 1.27694, 1.27787, 1.27768,
    1.27901, 1.28033, 1.28084, 1.28032, 1.28057, 1.27834, 1.27593, 1.27414,
    1.27237, 1.27095, 1.27042, 1.26919, 1.26749, 1.26555, 1.26146, 1.25502,
    1.24947
    )
  ) |>
  ggplot(aes(x = year, y = population)) +
  geom_line() +
  geom_point() +
  labs(x = "西暦", y = "日本の総人口 [億人]",
       title = "時間遷移を折れ線グラフで描画する") +
  theme(aspect.ratio = 3/5) #縦横比

1.1.4 散布図でみる総人口

library(conflicted)
library(tidyverse)

data.frame(
  year = 1990:2022,
  population = c(
    1.23611, 1.24101, 1.24567, 1.24938, 1.25265, 1.2557, 1.25859, 1.26157,
    1.26472, 1.26667, 1.26926, 1.27316, 1.27486, 1.27694, 1.27787, 1.27768,
    1.27901, 1.28033, 1.28084, 1.28032, 1.28057, 1.27834, 1.27593, 1.27414,
    1.27237, 1.27095, 1.27042, 1.26919, 1.26749, 1.26555, 1.26146, 1.25502,
    1.24947
    )
  ) |>
  mutate(next_year_population = lead(population)) |> # 次の年の人口列を追加
  drop_na() |> # 最後の行を削除(次の年の値がないため
  ggplot(aes(x = population, y = next_year_population)) +
  geom_point() +
  labs(
    x = "ある年の日本の総人口 [億人]",
    y = "次の年の日本の総人口 [億人]",
    title = "日本の総人口の前年比較"
    ) +
  coord_cartesian(xlim = c(1.23, 1.29), ylim = c(1.23, 1.29)) +
  theme(aspect.ratio = 1) #縦横比

1.1.5 折れ線グラフと散布図による可視化例その 2

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

# データの定義
data <- data.frame(
  t = 0:100,
  x_t = c(
    0.2,         0.64,        0.9216,      0.28901376,  0.821939226,
    0.585420539, 0.970813326, 0.113339247, 0.401973849, 0.961563495,
    0.14783656,  0.503923646, 0.99993842,  0.000246305, 0.000984976,
    0.003936025, 0.015682131, 0.061744808, 0.231729548, 0.712123859,
    0.820013873, 0.590364483, 0.967337041, 0.126384362, 0.441645421,
    0.986378972, 0.053741981, 0.203415122, 0.648149641, 0.912206736,
    0.320342428, 0.870892628, 0.449754634, 0.989901613, 0.039985639,
    0.153547151, 0.519881693, 0.998418873, 0.006314507, 0.025098538,
    0.097874404, 0.35318002,  0.913775574, 0.315159096, 0.863335361,
    0.471949661, 0.996852714, 0.012549522, 0.049568127, 0.188444511,
    0.611732709, 0.950063207, 0.189772438, 0.61503544,  0.94706739,
    0.200522995, 0.641254094, 0.920189124, 0.293764402, 0.829867512,
    0.564749697, 0.983229907, 0.065955428, 0.246421239, 0.742791249,
    0.764209638, 0.720773069, 0.805037009, 0.627809693, 0.934658729,
    0.244287156, 0.738443765, 0.772578283, 0.702804318, 0.835481634,
    0.549808293, 0.990076536, 0.039299956, 0.151021877, 0.512857079,
    0.999338782, 0.002643123, 0.010544547, 0.041733437, 0.15996703, 
    0.537510318, 0.994371904, 0.022385682, 0.087538253, 0.319501229,
    0.869680775, 0.4533445,   0.991293057, 0.034524528, 0.13333034, 
    0.462213442, 0.994288704, 0.022714708, 0.088795,    0.323641792,
    0.87559113
    )
  ) |>
  mutate(lag_x_t = dplyr::lag(x_t))

p1 <- data |>
  ggplot(aes(x = t, y = x_t)) +
  geom_line() +
  geom_point() +
  labs(
    x = expression(italic(t)),
    y = expression(italic(x[t])),
    title = "折れ線グラフによる可視化"
    ) +
  theme(aspect.ratio = 4/5) #縦横比

p2 <- data |>
  drop_na() |>
  ggplot(aes(x = lag_x_t, y = x_t)) +
  geom_point() +
  labs(
    x = expression(italic(x[t])),
    y = expression(italic(x[t+1])),
    title = "散布図による可視化"
    ) +
  theme(aspect.ratio = 4/5) #縦横比

# 描画レイアウトの設定と表示
p1 + p2

1.1.6 2個体間の類似度のスコア化の仮想的な例

1.1.6.1

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

# 個体の位置データ
set.seed(0)
individual <- matrix(abs(rnorm(200)), ncol = 2)

# 個体1のデータ生成
individual1 <- individual[,1] / sum(individual[, 1]) 

# 個体2のデータ生成
individual2 <- individual[, 1] * 0.6 + individual[, 2] * 0.4
individual2 <- individual2 / sum(individual2)

df <- data.frame(個体X = individual1, 個体Y = individual2)

p1 <- df |>
  pivot_longer(everything()) |>
  mutate(X2 = rep(c(1:100), 2)) |>
  ggplot(aes(x = X2, y = value, fill = name)) +
  geom_col() +
  labs(
    x = "滞在した場所ID",
    y = "滞在時間割合",
    title = "2個体の「行動」がどれだけ似ているか"
    ) +
  theme(
    aspect.ratio = 1/2,
    legend.title = element_blank(),
    legend.direction = "horizontal",
    legend.position = c(0.5, 0.85)
    )

p2 <- df |>
  ggplot(aes(x = 個体X, y = 個体Y)) +
  geom_point(alpha = 0.5) +
  geom_smooth(method = "lm", formula = y ~ x, se = FALSE) +
  labs(x = "個体Xの行動データ", y = "個体Yの行動データ") +
  theme(aspect.ratio = 1) #縦横比

p1 + p2

1.1.6.2

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

# ヒートマップを生成
# 2変量正規分布からデータ生成
x <- rmvnorm(
  n = 100,
  mean = c(0, 0),
  sigma = matrix(c(1, 0.8, 0.8, 1), nrow = 2)
  ) 

# 正規化関数
my_std <- function(x) {
  (x - min(x)) / (max(x) - min(x))
}

# 正規化実行
x1 <- my_std(x[, 1])
x2 <- my_std(x[, 2])

# ヒートマップ用のデータ分割
heatmap1 <- matrix(x1, nrow = 10, ncol = 10)
heatmap2 <- matrix(x2, nrow = 10, ncol = 10)

# ヒートマップ描画
hm1 <- heatmap1 |>
  as.data.frame() |>
  pivot_longer(everything()) |>
  mutate(name2 = paste0("W", sort(rep(1:10, 10)))) |>
  ggplot(aes(x = name, y = name2, fill = value)) +
  geom_tile() +
  scale_fill_gradientn("value", colours = gray.colors(12, start = 0, end = 1, gamma = 2.2, alpha = 1)) +
  theme(
    aspect.ratio = 1,
    axis.text = element_blank(),
    axis.title = element_blank(), 
    axis.ticks = element_blank(),     
    legend.position = "none"
  ) +
  labs(title = "個体Xの見た目データ")

hm2 <- heatmap2 |>
  as.data.frame() |>
  pivot_longer(everything()) |>
  mutate(name2 = paste0("W", sort(rep(1:10, 10)))) |>
  ggplot(aes(x = name, y = name2, fill = value)) +
  geom_tile() +
  scale_fill_gradientn("value", colours = gray.colors(12, start = 0, end = 1, gamma = 2.2, alpha = 1)) +
  theme(
    aspect.ratio = 1,
    axis.text = element_blank(),
    axis.title = element_blank(), 
    axis.ticks = element_blank(),     
    legend.position = "none"
  ) +
  labs(title = "個体Yの見た目データ")

p3 <- data.frame(x1, x2) |>
  ggplot(aes(x = x1, y = x2)) +
  geom_point(alpha = 0.5) +
  geom_smooth(method = "lm",formula = y ~ x, se = FALSE, color = "blue") +
  labs(x = "個体Xの見た目データ", y = "個体Yの見た目データ") +
  coord_fixed() + # アスペクト比を等しく設定 
  scale_x_continuous(limits = c(0,1)) +
  scale_y_continuous(limits = c(0,1))  

hm1 + hm2 + p3

1.1.7 二つの類似度スコアの関係

library(conflicted)
library(tidyverse)
library(MASS)

n <- 190     # データ点の数
low <- -0.45 # データの範囲の下限
high <- 0.95 # データの範囲の上限
rho <- 0.7   # 相関係数
mean <- c(0, 0) # 平均(二変数)

Sigma <- matrix(c(1, rho, rho, 1), nrow=2) # 共分散行列

set.seed(0)
data <- mvrnorm(n = n-1, mu = mean, Sigma = Sigma) # 多変量正規分布からデータ生成

x <- data[,1]
y <- data[,2]

x <- low + (high - low) * (x - min(x)) / (max(x) - min(x)) # xデータの正規化
y <- low + (high - low) * (y - min(y)) / (max(y) - min(y)) # yデータの正規化

x <- c(x, 0.80) # 特定の点を追加
y <- c(y, 0.72) # 特定の点を追加

df <- data.frame(x = x, y = y)

df |>
  ggplot(aes(x = x, y = y)) +
  geom_point(alpha = 0.5) + # 散布図の描画
  geom_smooth(method = "lm", formula = y ~ x, se = FALSE) + # 回帰直線の描画
  geom_point(aes(x = 0.80, y = 0.72), colour = "red", size = 2) + # 特定の点を赤色で描画
  coord_fixed() + # アスペクト比を等しく設定
  annotate("text", x = -0.25, y = 0.75, size = 4,
           label=paste0("r = ", round(cor(df$x, df$y), 2))) +
  labs(
    x = "個体間の見た目類似度スコア",
    y = "個体間の行動類似度スコア",
    title = "2つの類似度スコアの関係"
  )

1.2 可視化の効果を考える

1.2.1 文字情報と視覚情報による提示の違い

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

# データフレームの作成
df <- data.frame(
  Country = c("日本", "ブラジル", "米国", "中国"), # 国
  Population = c(124620000, 215802222, 335540000, 1425849288) / 100000000 # 人口
  )

# 米国と日本のみを含む棒グラフの描画
p1 <- df |>
  dplyr::filter(Country %in% c("米国","日本")) |>
  arrange(desc(Population)) |>
  ggplot(aes(x = Population, y = reorder(Country, Population), fill = Country)) +
  geom_col() +
  labs(x = "人口[億人]", y = "国名")

# 四か国を含む棒グラフの描画
p2 <-  df |>
  arrange(desc(Population)) |>
  ggplot(aes(x = Population, y = reorder(Country, Population), fill = Country)) +
  geom_col() +
  labs(x = "人口[億人]", y = "国名")

p1 / p2

1.2.2 2変数データにおけるパターンの発見

library(conflicted)
library(tidyverse)

data <- data.frame(
  x = c(0.204, 1.07, -0.296, 0.57, 0.637, 0.82, 0.137, -0.046),
  y = c(0.07, 0.57, 0.936, 1.436, 0.32, 1.003, 1.186, 0.503)
  )

square_points <- data.frame(
  x = c(-0.296, 0.57, 1.07, 0.204, -0.296),
  y = c(0.936, 1.436,0.57,0.07, 0.936)
)

data |>
  ggplot(aes(x = x, y = y)) +
  geom_point() +
  geom_path(square_points, mapping = aes(x = x, y = y), color = "red",
            linetype = "dashed") +
  coord_fixed() +
  labs(title = "視覚情報による提示")

1.2.3 重要なつながりだけ抜き出す

エッジが張られていないノードがネットワーク図で消えてしまう。

kohskeさんのご指摘ですべてのノードを表示できるようになりました!

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

pref_ja <- c(
  "北海道", "青森", "岩手", "宮城", "秋田", "山形", "福島", "茨城", "栃木",
    "群馬", "埼玉", "千葉", "東京", "神奈川", "新潟", "富山", "石川", "福井",
    "山梨", "長野", "岐阜", "静岡", "愛知", "三重", "滋賀", "京都", "大阪",
    "兵庫", "奈良", "和歌山", "鳥取", "島根", "岡山", "広島", "山口", "徳島",
    "香川", "愛媛", "高知", "福岡", "佐賀", "長崎", "熊本", "大分", "宮崎",
    "鹿児島", "沖縄"
    )

pref_ja <- factor(pref_ja, levels = pref_ja)

# データをロード
data_df <- read_csv(
  "https://raw.githubusercontent.com/tkEzaki/data_visualization/main/1%E7%AB%A0/data/matrix.csv"
  )

# 前処理
data_matrix <- data_df[, -1] |>
  as.matrix(nrow = 47)
diag(data_matrix) <- 0
data_matrix <- log1p(data_matrix)
rownames(data_matrix) <- colnames(data_matrix) <- pref_ja

# しきい値を計算
threshold <- quantile(data_matrix, .95)

# しきい値をもとにグラフデータを作る
gData <- data_matrix |>
  as.data.frame() |>
  rownames_to_column(var = "pref") |>
  pivot_longer(!pref) |>
  rename(
    from = pref,
    to = name,
    weight = value
  ) # |> dplyr::filter(weight >= threshold)

# グラフオブジェクトを生成
g <- graph_from_data_frame(gData, directed=TRUE)
g <- delete_edges(g, which(edge_attr(g)$weight<threshold))

E(g)$width <- E(g)$weight/5
# E(g)$color <- rainbow(100, alpha = 0.5)[round(E(g)$weight/max(E(g)$weight) * 100, 0)]
E(g)$color <- round(E(g)$weight/max(E(g)$weight) * 100, 0)

V(g)$label.cex <- 0.8
V(g)$size <- 12

# par(mar = c(0,0,0,0))
# g |>
#  plot(
#    layout = layout_in_circle(g, order = pref_ja),
#    edge.curved = 1.1, #0.5,
#    edge.arrow.size = 0.5
#    )

# tidygraphへ変換
g_tidy <- as_tbl_graph(g)

g_tidy |>
  ggraph(layout = "linear", circular = TRUE) +
  geom_edge_arc(
    aes(width = width, color = color),
    arrow = arrow(length = unit(4, "mm"), type = "closed"),
    strength = 2,
    start_cap = circle(5, "mm"),
    end_cap = circle(5, "mm")
    ) +
  scale_edge_color_viridis(option = "turbo", alpha = 0.5) +
  geom_node_label(aes(label = name), repel = FALSE) +
  theme_void() +
  theme(legend.position = "none")

1.2.4 全体の関係性パターンを見つける

library(conflicted)
library(tidyverse)
library(pheatmap)
library(viridisLite)

pref_ja <- c(
  "北海道", "青森", "岩手", "宮城", "秋田", "山形", "福島", "茨城", "栃木",
  "群馬", "埼玉", "千葉", "東京", "神奈川", "新潟", "富山", "石川", "福井",
  "山梨", "長野", "岐阜", "静岡", "愛知", "三重", "滋賀", "京都", "大阪",
  "兵庫", "奈良", "和歌山", "鳥取", "島根", "岡山", "広島", "山口", "徳島",
  "香川", "愛媛", "高知", "福岡", "佐賀", "長崎", "熊本", "大分", "宮崎",
  "鹿児島", "沖縄"
)

# データをロード
data_df <- read_csv(
  "https://raw.githubusercontent.com/tkEzaki/data_visualization/main/1%E7%AB%A0/data/matrix.csv"
)

# 前処理
data_matrix <- data_df[, -1] |>
  as.matrix(nrow = 47)
data_matrix <- log1p(data_matrix)
rownames(data_matrix) <- colnames(data_matrix) <- pref_ja

# ヒートマップ
pheatmap(
  data_matrix,
  clustering_distance_rows = "euclidean",
  clustering_method = "ward.D2",
  border_color = NA,
  color = turbo(50)
  )

1.2.5 様々なデータの並べ方

1.2.5.1

library(tidyverse)
library(viridis)
library(patchwork)

# データ
data_df <- data.frame(
  code = 1:47,
  prefectures = c(
    "北海道", "青森", "岩手", "宮城", "秋田", "山形", "福島", "茨城", "栃木",
    "群馬", "埼玉", "千葉", "東京", "神奈川", "新潟", "富山", "石川", "福井",
    "山梨", "長野", "岐阜", "静岡", "愛知", "三重", "滋賀", "京都", "大阪",
    "兵庫", "奈良", "和歌山", "鳥取", "島根", "岡山", "広島", "山口", "徳島",
    "香川", "愛媛", "高知", "福岡", "佐賀", "長崎", "熊本", "大分", "宮崎",
    "鹿児島", "沖縄"
    ),
  shipments = c(
    42334, 31897, 56667, 177842, 31295, 39663, 110027, 307752, 173267, 186811,
    579061, 299455, 580563, 368026, 83590, 79298, 42741, 53484, 99220, 136407,
    190720, 240332, 568222, 159106, 136137, 110115, 432778, 291430, 136335,
    35777, 37678, 22612, 108003, 176715, 65910, 29745, 48454, 49767, 20554,
    181309, 95824, 34208, 66604, 41907, 37888, 44466, 5240
    )/1000 # 桁が大きいので千t単位に調整
  ) 

p1 <- data_df |>
  ggplot(aes(x = reorder(prefectures, code), y = shipments, fill = shipments)) +
  geom_col() +
  labs(x = "", y = "出荷量[千t]", title = "都道府県コード順") +
  scale_fill_viridis(option = "turbo") +
  theme(
    axis.text.x = element_text(angle = 270, hjust = 1),
    aspect.ratio = 1/3,     
    legend.position = "none"
    ) #縦横比

p2 <- data_df |>
  ggplot(aes(x = reorder(prefectures, desc(shipments)), y = shipments, fill = shipments)) +
  geom_col() +
  labs(x = "", y = "出荷量[千t]", title = "出荷量順") +
  scale_fill_viridis(option = "turbo") +
  theme(
    axis.text.x = element_text(angle = 270, hjust = 1),
    aspect.ratio = 1/3,     
    legend.position = "none"
    )

p1 / p2

1.2.5.2

参考:

可視化(2) : ggplot2による地図作成

ジオメトリの移動による日本地図の可視化

library(conflicted)
library(tidyverse)
library(sf)
library(NipponMap)
library(viridisLite)

data_df <- data.frame(
  code = 1:47,
  prefectures = c(
    "北海道", "青森", "岩手", "宮城", "秋田", "山形", "福島", "茨城", "栃木",
    "群馬", "埼玉", "千葉", "東京", "神奈川", "新潟", "富山", "石川", "福井",
    "山梨", "長野", "岐阜", "静岡", "愛知", "三重", "滋賀", "京都", "大阪",
    "兵庫", "奈良", "和歌山", "鳥取", "島根", "岡山", "広島", "山口", "徳島",
    "香川", "愛媛", "高知", "福岡", "佐賀", "長崎", "熊本", "大分", "宮崎",
    "鹿児島", "沖縄"
    ),
  shipments = c(
    42334, 31897, 56667, 177842, 31295, 39663, 110027, 307752, 173267, 186811,
    579061, 299455, 580563, 368026, 83590, 79298, 42741, 53484, 99220, 136407,
    190720, 240332, 568222, 159106, 136137, 110115, 432778, 291430, 136335,
    35777, 37678, 22612, 108003, 176715, 65910, 29745, 48454, 49767, 20554,
    181309, 95824, 34208, 66604, 41907, 37888, 44466, 5240
    )/1000 # 桁が大きいので千t単位に調整
  ) 

Nippon_map <- read_sf(
  system.file("shapes/jpn.shp", package = "NipponMap")[1],
  crs = "+proj=longlat +datum=WGS84"
  )

Nippon_map$geometry[1] <- Nippon_map$geometry[1] + c(-11, -4) 
Nippon_map$geometry[47] <- Nippon_map$geometry[47] + c(12, 5)

ggplot()+ 
  geom_sf(data=Nippon_map, aes(fill = data_df$shipments)) +
  scale_fill_viridis(option = "turbo", trans = "log10") +
  annotate("segment", x=129, xend=134.2, y=37, yend=37, color="gray", linewidth = 1) +
  annotate("segment", x=134.2, xend=138.5, y=37, yend=41, color="gray", linewidth = 1) +
  annotate("segment", x=139.8, xend=141, y=32.2, yend=32.2, color="gray", linewidth = 1) +
  annotate("segment", x=138.5, xend=139.8, y=31, yend=32.2, color="gray", linewidth = 1) +
  theme(aspect.ratio = 1, axis.text = element_blank(),
        axis.title = element_blank(), axis.ticks = element_blank()) +
  labs(title = "地図で提示", fill="出荷量[千t](対数軸)", x="", y="", caption="Nippomap")

1.3 可視化で読み取れるロジック

1.3.2 変数間の関係

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

# データ生成
set.seed(0)
df <- tibble(x1 = runif(50), y1 = runif(50), x2 = runif(50),
             y2 = 0.8 * (x2 - 0.5) + 0.5 + 0.1 * rnorm(50))

p1 <- df |>
  ggplot(aes(x=x1, y=y1)) +
  geom_point(color="#0072B2") +
  geom_smooth(method=lm, formula = y ~ x, color="#0072B2") +
  labs(x = "変数X", y = "変数Y", title = "相関のない二つの変数") +
  coord_fixed()

p2 <- df |>
  ggplot(aes(x=x2, y=y2)) +
  geom_point(color="#009E73") +
  geom_smooth(method=lm, formula = y ~ x, color="#009E73") +
  labs(x = "変数X", y = "変数Y", title = "相関のある二つの変数") +
  coord_fixed()

p1 + p2

1.3.4 性質の異なる分布の例

「分布の裾の様子」の図はよく理解できなかったので描いていません。

library(conflicted)
library(tidyverse)

# 平均0、標準偏差1の正規分布から10000個のサンプルを生成
mu <- 0
sigma <- 1
set.seed(0)
p1 <- data.frame(val = rnorm(10000, mean = mu, sd = sigma)) |>
  ggplot(aes(x=val)) + 
  geom_histogram(aes(y=after_stat(density)), bins=30, color = "black", fill = "#999999") + 
  stat_function(fun = dnorm, args = list(mean=mu, sd=sigma), colour = "red") +
  coord_cartesian(ylim = c(0, 0.45)) +
  labs(title = "正規分布", x = "", y = "相対度数") +
  theme(aspect.ratio = 3/5) #縦横比

# 標準コーシー分布から10000個のサンプルを生成
set.seed(0)
p2 <- data.frame(val = rcauchy(10000)) |>
  dplyr::filter(val > -10 & val < 10) |> # 外れ値を捨てる
  ggplot(aes(x=val)) +
  geom_histogram(aes(y=after_stat(density)), bins=50, color = "black", fill = "#999999") +
  stat_function(fun = dcauchy, colour = "blue") +
  coord_cartesian(ylim = c(0, 0.45)) +
  labs(title = "コーシー分布", x = "", y = "相対度数") +
  theme(aspect.ratio = 3/5) #縦横比

p1 + p2

1.3.5 初期の感染症の拡大

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

# データフレームの生成
set.seed(0)
df <- tibble(
  x = seq(0, 30, 1),# 0から30までの範囲で等間隔に30個の数を生成
  y = exp(0.1 * x) * 100 * (1 + rnorm(31, 0, 0.08)) # 指数関数にランダムなノイズを付加する
  )

p1 <- df |>
  ggplot(aes(x = x, y = y)) +
  geom_point() +
  labs(x="経過日数", y="新規感染者数", title="リニアスケールでの表示") +
  theme(aspect.ratio = 1) #縦横比

p2 <- df |>
  ggplot(aes(x = x, y = y)) +
  geom_point() +
  scale_y_log10() +
  labs(x="経過日数", y="新規感染者数(対数軸)", title="片対数での表示") +
  theme(aspect.ratio = 1) #縦横比

p1 + p2

第1章はここまで。

LS0tCnRpdGxlOiAi56ysMeeroCDjg4fjg7zjgr/lj6/oppbljJbjga7mnKzos6oiCmF1dGhvcjogIk9zYW11LCBNT1JJTU9UTyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0aGVtZTogdW5pdGVkICAgIAogICAgbWRfZXh0ZW5zaW9uczogIi1hc2NpaV9pZGVudGlmaWVycyIKICAgIHRvY19mbG9hdDogeWVzCiAgICBmaWdfd2lkdGg6IDcuNQogICAgZmlnX2hlaWdodDogNS42MjUKICAgIGRldjogcmFnZ19wbmcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgrlm7Pjga7lj7PkuIrjga5gc2hvd2Djg5zjgr/jg7PjgpLmirzjgZnjgahS44Gu44Kz44O844OJ44GM6KGo56S644GV44KM44G+44GZ44CCCgojIyAxLjEg44OH44O844K/44KS5Y+v6KaW5YyW44GZ44KL44Go44GE44GG44GT44GoCgojIyMgMS4xLjMg5pel5pys44Gu57eP5Lq65Y+j44Gu5o6o56e7CgpgYGB7ciBmaWcuaGVpZ2h0PTQuNSwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCmRhdGEuZnJhbWUoCiAgeWVhciA9IDE5OTA6MjAyMiwKICBwb3B1bGF0aW9uID0gYygKICAgIDEuMjM2MTEsIDEuMjQxMDEsIDEuMjQ1NjcsIDEuMjQ5MzgsIDEuMjUyNjUsIDEuMjU1NywgMS4yNTg1OSwgMS4yNjE1NywKICAgIDEuMjY0NzIsIDEuMjY2NjcsIDEuMjY5MjYsIDEuMjczMTYsIDEuMjc0ODYsIDEuMjc2OTQsIDEuMjc3ODcsIDEuMjc3NjgsCiAgICAxLjI3OTAxLCAxLjI4MDMzLCAxLjI4MDg0LCAxLjI4MDMyLCAxLjI4MDU3LCAxLjI3ODM0LCAxLjI3NTkzLCAxLjI3NDE0LAogICAgMS4yNzIzNywgMS4yNzA5NSwgMS4yNzA0MiwgMS4yNjkxOSwgMS4yNjc0OSwgMS4yNjU1NSwgMS4yNjE0NiwgMS4yNTUwMiwKICAgIDEuMjQ5NDcKICAgICkKICApIHw+CiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IHBvcHVsYXRpb24pKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gIuilv+aapiIsIHkgPSAi5pel5pys44Gu57eP5Lq65Y+jIFvlhITkurpdIiwKICAgICAgIHRpdGxlID0gIuaZgumWk+mBt+enu+OCkuaKmOOCjOe3muOCsOODqeODleOBp+aPj+eUu+OBmeOCiyIpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAzLzUpICPnuKbmqKrmr5QKYGBgCgojIyMgMS4xLjQg5pWj5biD5Zuz44Gn44G/44KL57eP5Lq65Y+jCgpgYGB7ciBmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCmRhdGEuZnJhbWUoCiAgeWVhciA9IDE5OTA6MjAyMiwKICBwb3B1bGF0aW9uID0gYygKICAgIDEuMjM2MTEsIDEuMjQxMDEsIDEuMjQ1NjcsIDEuMjQ5MzgsIDEuMjUyNjUsIDEuMjU1NywgMS4yNTg1OSwgMS4yNjE1NywKICAgIDEuMjY0NzIsIDEuMjY2NjcsIDEuMjY5MjYsIDEuMjczMTYsIDEuMjc0ODYsIDEuMjc2OTQsIDEuMjc3ODcsIDEuMjc3NjgsCiAgICAxLjI3OTAxLCAxLjI4MDMzLCAxLjI4MDg0LCAxLjI4MDMyLCAxLjI4MDU3LCAxLjI3ODM0LCAxLjI3NTkzLCAxLjI3NDE0LAogICAgMS4yNzIzNywgMS4yNzA5NSwgMS4yNzA0MiwgMS4yNjkxOSwgMS4yNjc0OSwgMS4yNjU1NSwgMS4yNjE0NiwgMS4yNTUwMiwKICAgIDEuMjQ5NDcKICAgICkKICApIHw+CiAgbXV0YXRlKG5leHRfeWVhcl9wb3B1bGF0aW9uID0gbGVhZChwb3B1bGF0aW9uKSkgfD4gIyDmrKHjga7lubTjga7kurrlj6PliJfjgpLov73liqAKICBkcm9wX25hKCkgfD4gIyDmnIDlvozjga7ooYzjgpLliYrpmaTvvIjmrKHjga7lubTjga7lgKTjgYzjgarjgYTjgZ/jgoEKICBnZ3Bsb3QoYWVzKHggPSBwb3B1bGF0aW9uLCB5ID0gbmV4dF95ZWFyX3BvcHVsYXRpb24pKSArCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKAogICAgeCA9ICLjgYLjgovlubTjga7ml6XmnKzjga7nt4/kurrlj6MgW+WEhOS6ul0iLAogICAgeSA9ICLmrKHjga7lubTjga7ml6XmnKzjga7nt4/kurrlj6MgW+WEhOS6ul0iLAogICAgdGl0bGUgPSAi5pel5pys44Gu57eP5Lq65Y+j44Gu5YmN5bm05q+U6LyDIgogICAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDEuMjMsIDEuMjkpLCB5bGltID0gYygxLjIzLCAxLjI5KSkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICPnuKbmqKrmr5QKYGBgCgojIyMgMS4xLjUg5oqY44KM57ea44Kw44Op44OV44Go5pWj5biD5Zuz44Gr44KI44KL5Y+v6KaW5YyW5L6L44Gd44GuIDIKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCgojIOODh+ODvOOCv+OBruWumue+qQpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgdCA9IDA6MTAwLAogIHhfdCA9IGMoCiAgICAwLjIsICAgICAgICAgMC42NCwgICAgICAgIDAuOTIxNiwgICAgICAwLjI4OTAxMzc2LCAgMC44MjE5MzkyMjYsCiAgICAwLjU4NTQyMDUzOSwgMC45NzA4MTMzMjYsIDAuMTEzMzM5MjQ3LCAwLjQwMTk3Mzg0OSwgMC45NjE1NjM0OTUsCiAgICAwLjE0NzgzNjU2LCAgMC41MDM5MjM2NDYsIDAuOTk5OTM4NDIsICAwLjAwMDI0NjMwNSwgMC4wMDA5ODQ5NzYsCiAgICAwLjAwMzkzNjAyNSwgMC4wMTU2ODIxMzEsIDAuMDYxNzQ0ODA4LCAwLjIzMTcyOTU0OCwgMC43MTIxMjM4NTksCiAgICAwLjgyMDAxMzg3MywgMC41OTAzNjQ0ODMsIDAuOTY3MzM3MDQxLCAwLjEyNjM4NDM2MiwgMC40NDE2NDU0MjEsCiAgICAwLjk4NjM3ODk3MiwgMC4wNTM3NDE5ODEsIDAuMjAzNDE1MTIyLCAwLjY0ODE0OTY0MSwgMC45MTIyMDY3MzYsCiAgICAwLjMyMDM0MjQyOCwgMC44NzA4OTI2MjgsIDAuNDQ5NzU0NjM0LCAwLjk4OTkwMTYxMywgMC4wMzk5ODU2MzksCiAgICAwLjE1MzU0NzE1MSwgMC41MTk4ODE2OTMsIDAuOTk4NDE4ODczLCAwLjAwNjMxNDUwNywgMC4wMjUwOTg1MzgsCiAgICAwLjA5Nzg3NDQwNCwgMC4zNTMxODAwMiwgIDAuOTEzNzc1NTc0LCAwLjMxNTE1OTA5NiwgMC44NjMzMzUzNjEsCiAgICAwLjQ3MTk0OTY2MSwgMC45OTY4NTI3MTQsIDAuMDEyNTQ5NTIyLCAwLjA0OTU2ODEyNywgMC4xODg0NDQ1MTEsCiAgICAwLjYxMTczMjcwOSwgMC45NTAwNjMyMDcsIDAuMTg5NzcyNDM4LCAwLjYxNTAzNTQ0LCAgMC45NDcwNjczOSwKICAgIDAuMjAwNTIyOTk1LCAwLjY0MTI1NDA5NCwgMC45MjAxODkxMjQsIDAuMjkzNzY0NDAyLCAwLjgyOTg2NzUxMiwKICAgIDAuNTY0NzQ5Njk3LCAwLjk4MzIyOTkwNywgMC4wNjU5NTU0MjgsIDAuMjQ2NDIxMjM5LCAwLjc0Mjc5MTI0OSwKICAgIDAuNzY0MjA5NjM4LCAwLjcyMDc3MzA2OSwgMC44MDUwMzcwMDksIDAuNjI3ODA5NjkzLCAwLjkzNDY1ODcyOSwKICAgIDAuMjQ0Mjg3MTU2LCAwLjczODQ0Mzc2NSwgMC43NzI1NzgyODMsIDAuNzAyODA0MzE4LCAwLjgzNTQ4MTYzNCwKICAgIDAuNTQ5ODA4MjkzLCAwLjk5MDA3NjUzNiwgMC4wMzkyOTk5NTYsIDAuMTUxMDIxODc3LCAwLjUxMjg1NzA3OSwKICAgIDAuOTk5MzM4NzgyLCAwLjAwMjY0MzEyMywgMC4wMTA1NDQ1NDcsIDAuMDQxNzMzNDM3LCAwLjE1OTk2NzAzLCAKICAgIDAuNTM3NTEwMzE4LCAwLjk5NDM3MTkwNCwgMC4wMjIzODU2ODIsIDAuMDg3NTM4MjUzLCAwLjMxOTUwMTIyOSwKICAgIDAuODY5NjgwNzc1LCAwLjQ1MzM0NDUsICAgMC45OTEyOTMwNTcsIDAuMDM0NTI0NTI4LCAwLjEzMzMzMDM0LCAKICAgIDAuNDYyMjEzNDQyLCAwLjk5NDI4ODcwNCwgMC4wMjI3MTQ3MDgsIDAuMDg4Nzk1LCAgICAwLjMyMzY0MTc5MiwKICAgIDAuODc1NTkxMTMKICAgICkKICApIHw+CiAgbXV0YXRlKGxhZ194X3QgPSBkcGx5cjo6bGFnKHhfdCkpCgpwMSA8LSBkYXRhIHw+CiAgZ2dwbG90KGFlcyh4ID0gdCwgeSA9IHhfdCkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKAogICAgeCA9IGV4cHJlc3Npb24oaXRhbGljKHQpKSwKICAgIHkgPSBleHByZXNzaW9uKGl0YWxpYyh4W3RdKSksCiAgICB0aXRsZSA9ICLmipjjgoznt5rjgrDjg6njg5Xjgavjgojjgovlj6/oppbljJYiCiAgICApICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSA0LzUpICPnuKbmqKrmr5QKCnAyIDwtIGRhdGEgfD4KICBkcm9wX25hKCkgfD4KICBnZ3Bsb3QoYWVzKHggPSBsYWdfeF90LCB5ID0geF90KSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicygKICAgIHggPSBleHByZXNzaW9uKGl0YWxpYyh4W3RdKSksCiAgICB5ID0gZXhwcmVzc2lvbihpdGFsaWMoeFt0KzFdKSksCiAgICB0aXRsZSA9ICLmlaPluIPlm7Pjgavjgojjgovlj6/oppbljJYiCiAgICApICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSA0LzUpICPnuKbmqKrmr5QKCiMg5o+P55S744Os44Kk44Ki44Km44OI44Gu6Kit5a6a44Go6KGo56S6CnAxICsgcDIKYGBgCgojIyMgMS4xLjYgMuWAi+S9k+mWk+OBrumhnuS8vOW6puOBruOCueOCs+OCouWMluOBruS7ruaDs+eahOOBquS+iwoKIyMjIyAxLjEuNi4xCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobXZ0bm9ybSkKbGlicmFyeShwYXRjaHdvcmspCgojIOWAi+S9k+OBruS9jee9ruODh+ODvOOCvwpzZXQuc2VlZCgwKQppbmRpdmlkdWFsIDwtIG1hdHJpeChhYnMocm5vcm0oMjAwKSksIG5jb2wgPSAyKQoKIyDlgIvkvZMx44Gu44OH44O844K/55Sf5oiQCmluZGl2aWR1YWwxIDwtIGluZGl2aWR1YWxbLDFdIC8gc3VtKGluZGl2aWR1YWxbLCAxXSkgCgojIOWAi+S9kzLjga7jg4fjg7zjgr/nlJ/miJAKaW5kaXZpZHVhbDIgPC0gaW5kaXZpZHVhbFssIDFdICogMC42ICsgaW5kaXZpZHVhbFssIDJdICogMC40CmluZGl2aWR1YWwyIDwtIGluZGl2aWR1YWwyIC8gc3VtKGluZGl2aWR1YWwyKQoKZGYgPC0gZGF0YS5mcmFtZSjlgIvkvZNYID0gaW5kaXZpZHVhbDEsIOWAi+S9k1kgPSBpbmRpdmlkdWFsMikKCnAxIDwtIGRmIHw+CiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSkgfD4KICBtdXRhdGUoWDIgPSByZXAoYygxOjEwMCksIDIpKSB8PgogIGdncGxvdChhZXMoeCA9IFgyLCB5ID0gdmFsdWUsIGZpbGwgPSBuYW1lKSkgKwogIGdlb21fY29sKCkgKwogIGxhYnMoCiAgICB4ID0gIua7nuWcqOOBl+OBn+WgtOaJgElEIiwKICAgIHkgPSAi5rue5Zyo5pmC6ZaT5Ymy5ZCIIiwKICAgIHRpdGxlID0gIjLlgIvkvZPjga7jgIzooYzli5XjgI3jgYzjganjgozjgaDjgZHkvLzjgabjgYTjgovjgYsiCiAgICApICsKICB0aGVtZSgKICAgIGFzcGVjdC5yYXRpbyA9IDEvMiwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuNSwgMC44NSkKICAgICkKCnAyIDwtIGRmIHw+CiAgZ2dwbG90KGFlcyh4ID0g5YCL5L2TWCwgeSA9IOWAi+S9k1kpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4geCwgc2UgPSBGQUxTRSkgKwogIGxhYnMoeCA9ICLlgIvkvZNY44Gu6KGM5YuV44OH44O844K/IiwgeSA9ICLlgIvkvZNZ44Gu6KGM5YuV44OH44O844K/IikgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICPnuKbmqKrmr5QKCnAxICsgcDIKYGBgCgojIyMjIDEuMS42LjIKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCgojIOODkuODvOODiOODnuODg+ODl+OCkueUn+aIkAojIDLlpInph4/mraPopo/liIbluIPjgYvjgonjg4fjg7zjgr/nlJ/miJAKeCA8LSBybXZub3JtKAogIG4gPSAxMDAsCiAgbWVhbiA9IGMoMCwgMCksCiAgc2lnbWEgPSBtYXRyaXgoYygxLCAwLjgsIDAuOCwgMSksIG5yb3cgPSAyKQogICkgCgojIOato+imj+WMlumWouaVsApteV9zdGQgPC0gZnVuY3Rpb24oeCkgewogICh4IC0gbWluKHgpKSAvIChtYXgoeCkgLSBtaW4oeCkpCn0KCiMg5q2j6KaP5YyW5a6f6KGMCngxIDwtIG15X3N0ZCh4WywgMV0pCngyIDwtIG15X3N0ZCh4WywgMl0pCgojIOODkuODvOODiOODnuODg+ODl+eUqOOBruODh+ODvOOCv+WIhuWJsgpoZWF0bWFwMSA8LSBtYXRyaXgoeDEsIG5yb3cgPSAxMCwgbmNvbCA9IDEwKQpoZWF0bWFwMiA8LSBtYXRyaXgoeDIsIG5yb3cgPSAxMCwgbmNvbCA9IDEwKQoKIyDjg5Ljg7zjg4jjg57jg4Pjg5fmj4/nlLsKaG0xIDwtIGhlYXRtYXAxIHw+CiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSkgfD4KICBtdXRhdGUobmFtZTIgPSBwYXN0ZTAoIlciLCBzb3J0KHJlcCgxOjEwLCAxMCkpKSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBuYW1lLCB5ID0gbmFtZTIsIGZpbGwgPSB2YWx1ZSkpICsKICBnZW9tX3RpbGUoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oInZhbHVlIiwgY29sb3VycyA9IGdyYXkuY29sb3JzKDEyLCBzdGFydCA9IDAsIGVuZCA9IDEsIGdhbW1hID0gMi4yLCBhbHBoYSA9IDEpKSArCiAgdGhlbWUoCiAgICBhc3BlY3QucmF0aW8gPSAxLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCAgICAgCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICApICsKICBsYWJzKHRpdGxlID0gIuWAi+S9k1jjga7opovjgZ/nm67jg4fjg7zjgr8iKQoKaG0yIDwtIGhlYXRtYXAyIHw+CiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSkgfD4KICBtdXRhdGUobmFtZTIgPSBwYXN0ZTAoIlciLCBzb3J0KHJlcCgxOjEwLCAxMCkpKSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBuYW1lLCB5ID0gbmFtZTIsIGZpbGwgPSB2YWx1ZSkpICsKICBnZW9tX3RpbGUoKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oInZhbHVlIiwgY29sb3VycyA9IGdyYXkuY29sb3JzKDEyLCBzdGFydCA9IDAsIGVuZCA9IDEsIGdhbW1hID0gMi4yLCBhbHBoYSA9IDEpKSArCiAgdGhlbWUoCiAgICBhc3BlY3QucmF0aW8gPSAxLAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCAgICAgCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICApICsKICBsYWJzKHRpdGxlID0gIuWAi+S9k1njga7opovjgZ/nm67jg4fjg7zjgr8iKQoKcDMgPC0gZGF0YS5mcmFtZSh4MSwgeDIpIHw+CiAgZ2dwbG90KGFlcyh4ID0geDEsIHkgPSB4MikpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIixmb3JtdWxhID0geSB+IHgsIHNlID0gRkFMU0UsIGNvbG9yID0gImJsdWUiKSArCiAgbGFicyh4ID0gIuWAi+S9k1jjga7opovjgZ/nm67jg4fjg7zjgr8iLCB5ID0gIuWAi+S9k1njga7opovjgZ/nm67jg4fjg7zjgr8iKSArCiAgY29vcmRfZml4ZWQoKSArICMg44Ki44K544Oa44Kv44OI5q+U44KS562J44GX44GP6Kit5a6aIAogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLDEpKSAgCgpobTEgKyBobTIgKyBwMwpgYGAKCiMjIyAxLjEuNyDkuozjgaTjga7poZ7kvLzluqbjgrnjgrPjgqLjga7plqLkv4IKCmBgYHtyIGZpZy5oZWlnaHQ9Ny41LCBmaWcud2lkdGg9Ny41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KE1BU1MpCgpuIDwtIDE5MCAgICAgIyDjg4fjg7zjgr/ngrnjga7mlbAKbG93IDwtIC0wLjQ1ICMg44OH44O844K/44Gu56+E5Zuy44Gu5LiL6ZmQCmhpZ2ggPC0gMC45NSAjIOODh+ODvOOCv+OBruevhOWbsuOBruS4iumZkApyaG8gPC0gMC43ICAgIyDnm7jplqLkv4LmlbAKbWVhbiA8LSBjKDAsIDApICMg5bmz5Z2H77yI5LqM5aSJ5pWw77yJCgpTaWdtYSA8LSBtYXRyaXgoYygxLCByaG8sIHJobywgMSksIG5yb3c9MikgIyDlhbHliIbmlaPooYzliJcKCnNldC5zZWVkKDApCmRhdGEgPC0gbXZybm9ybShuID0gbi0xLCBtdSA9IG1lYW4sIFNpZ21hID0gU2lnbWEpICMg5aSa5aSJ6YeP5q2j6KaP5YiG5biD44GL44KJ44OH44O844K/55Sf5oiQCgp4IDwtIGRhdGFbLDFdCnkgPC0gZGF0YVssMl0KCnggPC0gbG93ICsgKGhpZ2ggLSBsb3cpICogKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkgIyB444OH44O844K/44Gu5q2j6KaP5YyWCnkgPC0gbG93ICsgKGhpZ2ggLSBsb3cpICogKHkgLSBtaW4oeSkpIC8gKG1heCh5KSAtIG1pbih5KSkgIyB544OH44O844K/44Gu5q2j6KaP5YyWCgp4IDwtIGMoeCwgMC44MCkgIyDnibnlrprjga7ngrnjgpLov73liqAKeSA8LSBjKHksIDAuNzIpICMg54m55a6a44Gu54K544KS6L+95YqgCgpkZiA8LSBkYXRhLmZyYW1lKHggPSB4LCB5ID0geSkKCmRmIHw+CiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHkpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKyAjIOaVo+W4g+Wbs+OBruaPj+eUuwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4geCwgc2UgPSBGQUxTRSkgKyAjIOWbnuW4sOebtOe3muOBruaPj+eUuwogIGdlb21fcG9pbnQoYWVzKHggPSAwLjgwLCB5ID0gMC43MiksIGNvbG91ciA9ICJyZWQiLCBzaXplID0gMikgKyAjIOeJueWumuOBrueCueOCkui1pOiJsuOBp+aPj+eUuwogIGNvb3JkX2ZpeGVkKCkgKyAjIOOCouOCueODmuOCr+ODiOavlOOCkuetieOBl+OBj+ioreWumgogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IC0wLjI1LCB5ID0gMC43NSwgc2l6ZSA9IDQsCiAgICAgICAgICAgbGFiZWw9cGFzdGUwKCJyID0gIiwgcm91bmQoY29yKGRmJHgsIGRmJHkpLCAyKSkpICsKICBsYWJzKAogICAgeCA9ICLlgIvkvZPplpPjga7opovjgZ/nm67poZ7kvLzluqbjgrnjgrPjgqIiLAogICAgeSA9ICLlgIvkvZPplpPjga7ooYzli5XpoZ7kvLzluqbjgrnjgrPjgqIiLAogICAgdGl0bGUgPSAiMuOBpOOBrumhnuS8vOW6puOCueOCs+OCouOBrumWouS/giIKICApCmBgYAoKIyMgMS4yIOWPr+imluWMluOBruWKueaenOOCkuiAg+OBiOOCiwoKIyMjIDEuMi4xIOaWh+Wtl+aDheWgseOBqOimluimmuaDheWgseOBq+OCiOOCi+aPkOekuuOBrumBleOBhAoKYGBge3IgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyDjg4fjg7zjgr/jg5Xjg6zjg7zjg6Djga7kvZzmiJAKZGYgPC0gZGF0YS5mcmFtZSgKICBDb3VudHJ5ID0gYygi5pel5pysIiwgIuODluODqeOCuOODqyIsICLnsbPlm70iLCAi5Lit5Zu9IiksICMg5Zu9CiAgUG9wdWxhdGlvbiA9IGMoMTI0NjIwMDAwLCAyMTU4MDIyMjIsIDMzNTU0MDAwMCwgMTQyNTg0OTI4OCkgLyAxMDAwMDAwMDAgIyDkurrlj6MKICApCgojIOexs+WbveOBqOaXpeacrOOBruOBv+OCkuWQq+OCgOajkuOCsOODqeODleOBruaPj+eUuwpwMSA8LSBkZiB8PgogIGRwbHlyOjpmaWx0ZXIoQ291bnRyeSAlaW4lIGMoIuexs+WbvSIsIuaXpeacrCIpKSB8PgogIGFycmFuZ2UoZGVzYyhQb3B1bGF0aW9uKSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBQb3B1bGF0aW9uLCB5ID0gcmVvcmRlcihDb3VudHJ5LCBQb3B1bGF0aW9uKSwgZmlsbCA9IENvdW50cnkpKSArCiAgZ2VvbV9jb2woKSArCiAgbGFicyh4ID0gIuS6uuWPo1vlhITkurpdIiwgeSA9ICLlm73lkI0iKQoKIyDlm5vjgYvlm73jgpLlkKvjgoDmo5LjgrDjg6njg5Xjga7mj4/nlLsKcDIgPC0gIGRmIHw+CiAgYXJyYW5nZShkZXNjKFBvcHVsYXRpb24pKSB8PgogIGdncGxvdChhZXMoeCA9IFBvcHVsYXRpb24sIHkgPSByZW9yZGVyKENvdW50cnksIFBvcHVsYXRpb24pLCBmaWxsID0gQ291bnRyeSkpICsKICBnZW9tX2NvbCgpICsKICBsYWJzKHggPSAi5Lq65Y+jW+WEhOS6ul0iLCB5ID0gIuWbveWQjSIpCgpwMSAvIHAyCmBgYAoKIyMjIDEuMi4yIDLlpInmlbDjg4fjg7zjgr/jgavjgYrjgZHjgovjg5Hjgr/jg7zjg7Pjga7nmbroposKCmBgYHtyIGZpZy5oZWlnaHQ9Ny41LCBmaWcud2lkdGg9Ny41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQoKZGF0YSA8LSBkYXRhLmZyYW1lKAogIHggPSBjKDAuMjA0LCAxLjA3LCAtMC4yOTYsIDAuNTcsIDAuNjM3LCAwLjgyLCAwLjEzNywgLTAuMDQ2KSwKICB5ID0gYygwLjA3LCAwLjU3LCAwLjkzNiwgMS40MzYsIDAuMzIsIDEuMDAzLCAxLjE4NiwgMC41MDMpCiAgKQoKc3F1YXJlX3BvaW50cyA8LSBkYXRhLmZyYW1lKAogIHggPSBjKC0wLjI5NiwgMC41NywgMS4wNywgMC4yMDQsIC0wLjI5NiksCiAgeSA9IGMoMC45MzYsIDEuNDM2LDAuNTcsMC4wNywgMC45MzYpCikKCmRhdGEgfD4KICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geSkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fcGF0aChzcXVhcmVfcG9pbnRzLCBtYXBwaW5nID0gYWVzKHggPSB4LCB5ID0geSksIGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBjb29yZF9maXhlZCgpICsKICBsYWJzKHRpdGxlID0gIuimluimmuaDheWgseOBq+OCiOOCi+aPkOekuiIpCmBgYAoKIyMjIDEuMi4zIOmHjeimgeOBquOBpOOBquOBjOOCiuOBoOOBkeaKnOOBjeWHuuOBmQoKfn7jgqjjg4PjgrjjgYzlvLXjgonjgozjgabjgYTjgarjgYTjg47jg7zjg4njgYzjg43jg4Pjg4jjg6/jg7zjgq/lm7PjgafmtojjgYjjgabjgZfjgb7jgYbjgIJ+fgoKW2tvaHNrZV0oaHR0cHM6Ly94LmNvbS9rb2hza2Uvc3RhdHVzLzE3NDQyODIwODM5MDI3ODM4MjU/cz0yMCnjgZXjgpPjga7jgZTmjIfmkZjjgafjgZnjgbnjgabjga7jg47jg7zjg4njgpLooajnpLrjgafjgY3jgovjgojjgYbjgavjgarjgorjgb7jgZfjgZ/vvIEKCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkodGlkeWdyYXBoKQpsaWJyYXJ5KGdncmFwaCkKCnByZWZfamEgPC0gYygKICAi5YyX5rW36YGTIiwgIumdkuajriIsICLlsqnmiYsiLCAi5a6u5Z+OIiwgIueni+eUsCIsICLlsbHlvaIiLCAi56aP5bO2IiwgIuiMqOWfjiIsICLmoIPmnKgiLAogICAgIue+pOmmrCIsICLln7znjokiLCAi5Y2D6JGJIiwgIuadseS6rCIsICLnpZ7lpYjlt50iLCAi5paw5r2fIiwgIuWvjOWxsSIsICLnn7Plt50iLCAi56aP5LqVIiwKICAgICLlsbHmoqgiLCAi6ZW36YeOIiwgIuWykOmYnCIsICLpnZnlsqEiLCAi5oSb55+lIiwgIuS4iemHjSIsICLmu4vos4AiLCAi5Lqs6YO9IiwgIuWkp+mYqiIsCiAgICAi5YW15bqrIiwgIuWliOiJryIsICLlkozmrYzlsbEiLCAi6bOl5Y+WIiwgIuWztuaguSIsICLlsqHlsbEiLCAi5bqD5bO2IiwgIuWxseWPoyIsICLlvrPls7YiLAogICAgIummmeW3nSIsICLmhJvlqpsiLCAi6auY55+lIiwgIuemj+WyoSIsICLkvZDos4AiLCAi6ZW35bSOIiwgIueGiuacrCIsICLlpKfliIYiLCAi5a6u5bSOIiwKICAgICLpub/lhZDls7YiLCAi5rKW57iEIgogICAgKQoKcHJlZl9qYSA8LSBmYWN0b3IocHJlZl9qYSwgbGV2ZWxzID0gcHJlZl9qYSkKCiMg44OH44O844K/44KS44Ot44O844OJCmRhdGFfZGYgPC0gcmVhZF9jc3YoCiAgImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS90a0V6YWtpL2RhdGFfdmlzdWFsaXphdGlvbi9tYWluLzElRTclQUIlQTAvZGF0YS9tYXRyaXguY3N2IgogICkKCiMg5YmN5Yem55CGCmRhdGFfbWF0cml4IDwtIGRhdGFfZGZbLCAtMV0gfD4KICBhcy5tYXRyaXgobnJvdyA9IDQ3KQpkaWFnKGRhdGFfbWF0cml4KSA8LSAwCmRhdGFfbWF0cml4IDwtIGxvZzFwKGRhdGFfbWF0cml4KQpyb3duYW1lcyhkYXRhX21hdHJpeCkgPC0gY29sbmFtZXMoZGF0YV9tYXRyaXgpIDwtIHByZWZfamEKCiMg44GX44GN44GE5YCk44KS6KiI566XCnRocmVzaG9sZCA8LSBxdWFudGlsZShkYXRhX21hdHJpeCwgLjk1KQoKIyDjgZfjgY3jgYTlgKTjgpLjgoLjgajjgavjgrDjg6njg5Xjg4fjg7zjgr/jgpLkvZzjgosKZ0RhdGEgPC0gZGF0YV9tYXRyaXggfD4KICBhcy5kYXRhLmZyYW1lKCkgfD4KICByb3duYW1lc190b19jb2x1bW4odmFyID0gInByZWYiKSB8PgogIHBpdm90X2xvbmdlcighcHJlZikgfD4KICByZW5hbWUoCiAgICBmcm9tID0gcHJlZiwKICAgIHRvID0gbmFtZSwKICAgIHdlaWdodCA9IHZhbHVlCiAgKSAjIHw+IGRwbHlyOjpmaWx0ZXIod2VpZ2h0ID49IHRocmVzaG9sZCkKCiMg44Kw44Op44OV44Kq44OW44K444Kn44Kv44OI44KS55Sf5oiQCmcgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGdEYXRhLCBkaXJlY3RlZD1UUlVFKQpnIDwtIGRlbGV0ZV9lZGdlcyhnLCB3aGljaChlZGdlX2F0dHIoZykkd2VpZ2h0PHRocmVzaG9sZCkpCgpFKGcpJHdpZHRoIDwtIEUoZykkd2VpZ2h0LzUKIyBFKGcpJGNvbG9yIDwtIHJhaW5ib3coMTAwLCBhbHBoYSA9IDAuNSlbcm91bmQoRShnKSR3ZWlnaHQvbWF4KEUoZykkd2VpZ2h0KSAqIDEwMCwgMCldCkUoZykkY29sb3IgPC0gcm91bmQoRShnKSR3ZWlnaHQvbWF4KEUoZykkd2VpZ2h0KSAqIDEwMCwgMCkKClYoZykkbGFiZWwuY2V4IDwtIDAuOApWKGcpJHNpemUgPC0gMTIKCiMgcGFyKG1hciA9IGMoMCwwLDAsMCkpCiMgZyB8PgojICBwbG90KAojICAgIGxheW91dCA9IGxheW91dF9pbl9jaXJjbGUoZywgb3JkZXIgPSBwcmVmX2phKSwKIyAgICBlZGdlLmN1cnZlZCA9IDEuMSwgIzAuNSwKIyAgICBlZGdlLmFycm93LnNpemUgPSAwLjUKIyAgICApCgojIHRpZHlncmFwaOOBuOWkieaPmwpnX3RpZHkgPC0gYXNfdGJsX2dyYXBoKGcpCgpnX3RpZHkgfD4KICBnZ3JhcGgobGF5b3V0ID0gImxpbmVhciIsIGNpcmN1bGFyID0gVFJVRSkgKwogIGdlb21fZWRnZV9hcmMoCiAgICBhZXMod2lkdGggPSB3aWR0aCwgY29sb3IgPSBjb2xvciksCiAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoNCwgIm1tIiksIHR5cGUgPSAiY2xvc2VkIiksCiAgICBzdHJlbmd0aCA9IDIsCiAgICBzdGFydF9jYXAgPSBjaXJjbGUoNSwgIm1tIiksCiAgICBlbmRfY2FwID0gY2lyY2xlKDUsICJtbSIpCiAgICApICsKICBzY2FsZV9lZGdlX2NvbG9yX3ZpcmlkaXMob3B0aW9uID0gInR1cmJvIiwgYWxwaGEgPSAwLjUpICsKICBnZW9tX25vZGVfbGFiZWwoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsID0gRkFMU0UpICsKICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoKIyMjIDEuMi40IOWFqOS9k+OBrumWouS/guaAp+ODkeOCv+ODvOODs+OCkuimi+OBpOOBkeOCiwoKYGBge3IgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkodmlyaWRpc0xpdGUpCgpwcmVmX2phIDwtIGMoCiAgIuWMl+a1t+mBkyIsICLpnZLmo64iLCAi5bKp5omLIiwgIuWuruWfjiIsICLnp4vnlLAiLCAi5bGx5b2iIiwgIuemj+WztiIsICLojKjln44iLCAi5qCD5pyoIiwKICAi576k6aasIiwgIuWfvOeOiSIsICLljYPokYkiLCAi5p2x5LqsIiwgIuelnuWliOW3nSIsICLmlrDmvZ8iLCAi5a+M5bGxIiwgIuefs+W3nSIsICLnpo/kupUiLAogICLlsbHmoqgiLCAi6ZW36YeOIiwgIuWykOmYnCIsICLpnZnlsqEiLCAi5oSb55+lIiwgIuS4iemHjSIsICLmu4vos4AiLCAi5Lqs6YO9IiwgIuWkp+mYqiIsCiAgIuWFteW6qyIsICLlpYjoia8iLCAi5ZKM5q2M5bGxIiwgIumzpeWPliIsICLls7bmoLkiLCAi5bKh5bGxIiwgIuW6g+WztiIsICLlsbHlj6MiLCAi5b6z5bO2IiwKICAi6aaZ5bedIiwgIuaEm+WqmyIsICLpq5jnn6UiLCAi56aP5bKhIiwgIuS9kOizgCIsICLplbfltI4iLCAi54aK5pysIiwgIuWkp+WIhiIsICLlrq7ltI4iLAogICLpub/lhZDls7YiLCAi5rKW57iEIgopCgojIOODh+ODvOOCv+OCkuODreODvOODiQpkYXRhX2RmIDwtIHJlYWRfY3N2KAogICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vdGtFemFraS9kYXRhX3Zpc3VhbGl6YXRpb24vbWFpbi8xJUU3JUFCJUEwL2RhdGEvbWF0cml4LmNzdiIKKQoKIyDliY3lh6bnkIYKZGF0YV9tYXRyaXggPC0gZGF0YV9kZlssIC0xXSB8PgogIGFzLm1hdHJpeChucm93ID0gNDcpCmRhdGFfbWF0cml4IDwtIGxvZzFwKGRhdGFfbWF0cml4KQpyb3duYW1lcyhkYXRhX21hdHJpeCkgPC0gY29sbmFtZXMoZGF0YV9tYXRyaXgpIDwtIHByZWZfamEKCiMg44OS44O844OI44Oe44OD44OXCnBoZWF0bWFwKAogIGRhdGFfbWF0cml4LAogIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJldWNsaWRlYW4iLAogIGNsdXN0ZXJpbmdfbWV0aG9kID0gIndhcmQuRDIiLAogIGJvcmRlcl9jb2xvciA9IE5BLAogIGNvbG9yID0gdHVyYm8oNTApCiAgKQpgYGAKCiMjIyAxLjIuNSDmp5jjgIXjgarjg4fjg7zjgr/jga7kuKbjgbnmlrkKCiMjIyMgMS4yLjUuMQoKYGBge3IgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyDjg4fjg7zjgr8KZGF0YV9kZiA8LSBkYXRhLmZyYW1lKAogIGNvZGUgPSAxOjQ3LAogIHByZWZlY3R1cmVzID0gYygKICAgICLljJfmtbfpgZMiLCAi6Z2S5qOuIiwgIuWyqeaJiyIsICLlrq7ln44iLCAi56eL55SwIiwgIuWxseW9oiIsICLnpo/ls7YiLCAi6Iyo5Z+OIiwgIuagg+acqCIsCiAgICAi576k6aasIiwgIuWfvOeOiSIsICLljYPokYkiLCAi5p2x5LqsIiwgIuelnuWliOW3nSIsICLmlrDmvZ8iLCAi5a+M5bGxIiwgIuefs+W3nSIsICLnpo/kupUiLAogICAgIuWxseaiqCIsICLplbfph44iLCAi5bKQ6ZicIiwgIumdmeWyoSIsICLmhJvnn6UiLCAi5LiJ6YeNIiwgIua7i+izgCIsICLkuqzpg70iLCAi5aSn6ZiqIiwKICAgICLlhbXluqsiLCAi5aWI6ImvIiwgIuWSjOatjOWxsSIsICLps6Xlj5YiLCAi5bO25qC5IiwgIuWyoeWxsSIsICLluoPls7YiLCAi5bGx5Y+jIiwgIuW+s+WztiIsCiAgICAi6aaZ5bedIiwgIuaEm+WqmyIsICLpq5jnn6UiLCAi56aP5bKhIiwgIuS9kOizgCIsICLplbfltI4iLCAi54aK5pysIiwgIuWkp+WIhiIsICLlrq7ltI4iLAogICAgIum5v+WFkOWztiIsICLmspbnuIQiCiAgICApLAogIHNoaXBtZW50cyA9IGMoCiAgICA0MjMzNCwgMzE4OTcsIDU2NjY3LCAxNzc4NDIsIDMxMjk1LCAzOTY2MywgMTEwMDI3LCAzMDc3NTIsIDE3MzI2NywgMTg2ODExLAogICAgNTc5MDYxLCAyOTk0NTUsIDU4MDU2MywgMzY4MDI2LCA4MzU5MCwgNzkyOTgsIDQyNzQxLCA1MzQ4NCwgOTkyMjAsIDEzNjQwNywKICAgIDE5MDcyMCwgMjQwMzMyLCA1NjgyMjIsIDE1OTEwNiwgMTM2MTM3LCAxMTAxMTUsIDQzMjc3OCwgMjkxNDMwLCAxMzYzMzUsCiAgICAzNTc3NywgMzc2NzgsIDIyNjEyLCAxMDgwMDMsIDE3NjcxNSwgNjU5MTAsIDI5NzQ1LCA0ODQ1NCwgNDk3NjcsIDIwNTU0LAogICAgMTgxMzA5LCA5NTgyNCwgMzQyMDgsIDY2NjA0LCA0MTkwNywgMzc4ODgsIDQ0NDY2LCA1MjQwCiAgICApLzEwMDAgIyDmoYHjgYzlpKfjgY3jgYTjga7jgafljYN05Y2Y5L2N44Gr6Kq/5pW0CiAgKSAKCnAxIDwtIGRhdGFfZGYgfD4KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyKHByZWZlY3R1cmVzLCBjb2RlKSwgeSA9IHNoaXBtZW50cywgZmlsbCA9IHNoaXBtZW50cykpICsKICBnZW9tX2NvbCgpICsKICBsYWJzKHggPSAiIiwgeSA9ICLlh7rojbfph49b5Y2DdF0iLCB0aXRsZSA9ICLpg73pgZPlupznnIzjgrPjg7zjg4npoIYiKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJ0dXJibyIpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMjcwLCBoanVzdCA9IDEpLAogICAgYXNwZWN0LnJhdGlvID0gMS8zLCAgICAgCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICAgICkgI+e4puaoquavlAoKcDIgPC0gZGF0YV9kZiB8PgogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIocHJlZmVjdHVyZXMsIGRlc2Moc2hpcG1lbnRzKSksIHkgPSBzaGlwbWVudHMsIGZpbGwgPSBzaGlwbWVudHMpKSArCiAgZ2VvbV9jb2woKSArCiAgbGFicyh4ID0gIiIsIHkgPSAi5Ye66I236YePW+WNg3RdIiwgdGl0bGUgPSAi5Ye66I236YeP6aCGIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI3MCwgaGp1c3QgPSAxKSwKICAgIGFzcGVjdC5yYXRpbyA9IDEvMywgICAgIAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiCiAgICApCgpwMSAvIHAyCmBgYAoKIyMjIyAxLjIuNS4yCgrlj4LogIM6Cgpb5Y+v6KaW5YyWKDIpIDogZ2dwbG90MuOBq+OCiOOCi+WcsOWbs+S9nOaIkF0oaHR0cHM6Ly95YW1hbW90by1tYXNhc2hpLmdpdGh1Yi5pby9EU2xlYy9jaGFwdGVyMDUuaHRtbCkKClvjgrjjgqrjg6Hjg4jjg6rjga7np7vli5Xjgavjgojjgovml6XmnKzlnLDlm7Pjga7lj6/oppbljJZdKGh0dHBzOi8vc2hpbmljaGlyby1pd2F0YS5naXRodWIuaW8vZ2Vvc3BhdGlhbC1kYXRhLXZpc3VhbGl6YXRpb24vcGxkMy12aXN1YWxpemF0aW9uLmh0bWwpCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KE5pcHBvbk1hcCkKbGlicmFyeSh2aXJpZGlzTGl0ZSkKCmRhdGFfZGYgPC0gZGF0YS5mcmFtZSgKICBjb2RlID0gMTo0NywKICBwcmVmZWN0dXJlcyA9IGMoCiAgICAi5YyX5rW36YGTIiwgIumdkuajriIsICLlsqnmiYsiLCAi5a6u5Z+OIiwgIueni+eUsCIsICLlsbHlvaIiLCAi56aP5bO2IiwgIuiMqOWfjiIsICLmoIPmnKgiLAogICAgIue+pOmmrCIsICLln7znjokiLCAi5Y2D6JGJIiwgIuadseS6rCIsICLnpZ7lpYjlt50iLCAi5paw5r2fIiwgIuWvjOWxsSIsICLnn7Plt50iLCAi56aP5LqVIiwKICAgICLlsbHmoqgiLCAi6ZW36YeOIiwgIuWykOmYnCIsICLpnZnlsqEiLCAi5oSb55+lIiwgIuS4iemHjSIsICLmu4vos4AiLCAi5Lqs6YO9IiwgIuWkp+mYqiIsCiAgICAi5YW15bqrIiwgIuWliOiJryIsICLlkozmrYzlsbEiLCAi6bOl5Y+WIiwgIuWztuaguSIsICLlsqHlsbEiLCAi5bqD5bO2IiwgIuWxseWPoyIsICLlvrPls7YiLAogICAgIummmeW3nSIsICLmhJvlqpsiLCAi6auY55+lIiwgIuemj+WyoSIsICLkvZDos4AiLCAi6ZW35bSOIiwgIueGiuacrCIsICLlpKfliIYiLCAi5a6u5bSOIiwKICAgICLpub/lhZDls7YiLCAi5rKW57iEIgogICAgKSwKICBzaGlwbWVudHMgPSBjKAogICAgNDIzMzQsIDMxODk3LCA1NjY2NywgMTc3ODQyLCAzMTI5NSwgMzk2NjMsIDExMDAyNywgMzA3NzUyLCAxNzMyNjcsIDE4NjgxMSwKICAgIDU3OTA2MSwgMjk5NDU1LCA1ODA1NjMsIDM2ODAyNiwgODM1OTAsIDc5Mjk4LCA0Mjc0MSwgNTM0ODQsIDk5MjIwLCAxMzY0MDcsCiAgICAxOTA3MjAsIDI0MDMzMiwgNTY4MjIyLCAxNTkxMDYsIDEzNjEzNywgMTEwMTE1LCA0MzI3NzgsIDI5MTQzMCwgMTM2MzM1LAogICAgMzU3NzcsIDM3Njc4LCAyMjYxMiwgMTA4MDAzLCAxNzY3MTUsIDY1OTEwLCAyOTc0NSwgNDg0NTQsIDQ5NzY3LCAyMDU1NCwKICAgIDE4MTMwOSwgOTU4MjQsIDM0MjA4LCA2NjYwNCwgNDE5MDcsIDM3ODg4LCA0NDQ2NiwgNTI0MAogICAgKS8xMDAwICMg5qGB44GM5aSn44GN44GE44Gu44Gn5Y2DdOWNmOS9jeOBq+iqv+aVtAogICkgCgpOaXBwb25fbWFwIDwtIHJlYWRfc2YoCiAgc3lzdGVtLmZpbGUoInNoYXBlcy9qcG4uc2hwIiwgcGFja2FnZSA9ICJOaXBwb25NYXAiKVsxXSwKICBjcnMgPSAiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQiCiAgKQoKTmlwcG9uX21hcCRnZW9tZXRyeVsxXSA8LSBOaXBwb25fbWFwJGdlb21ldHJ5WzFdICsgYygtMTEsIC00KSAKTmlwcG9uX21hcCRnZW9tZXRyeVs0N10gPC0gTmlwcG9uX21hcCRnZW9tZXRyeVs0N10gKyBjKDEyLCA1KQoKZ2dwbG90KCkrIAogIGdlb21fc2YoZGF0YT1OaXBwb25fbWFwLCBhZXMoZmlsbCA9IGRhdGFfZGYkc2hpcG1lbnRzKSkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb24gPSAidHVyYm8iLCB0cmFucyA9ICJsb2cxMCIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHg9MTI5LCB4ZW5kPTEzNC4yLCB5PTM3LCB5ZW5kPTM3LCBjb2xvcj0iZ3JheSIsIGxpbmV3aWR0aCA9IDEpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHg9MTM0LjIsIHhlbmQ9MTM4LjUsIHk9MzcsIHllbmQ9NDEsIGNvbG9yPSJncmF5IiwgbGluZXdpZHRoID0gMSkgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeD0xMzkuOCwgeGVuZD0xNDEsIHk9MzIuMiwgeWVuZD0zMi4yLCBjb2xvcj0iZ3JheSIsIGxpbmV3aWR0aCA9IDEpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHg9MTM4LjUsIHhlbmQ9MTM5LjgsIHk9MzEsIHllbmQ9MzIuMiwgY29sb3I9ImdyYXkiLCBsaW5ld2lkdGggPSAxKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIuWcsOWbs+OBp+aPkOekuiIsIGZpbGw9IuWHuuiNt+mHj1vljYN0Xe+8iOWvvuaVsOi7uO+8iSIsIHg9IiIsIHk9IiIsIGNhcHRpb249Ik5pcHBvbWFwIikKYGBgCgojIyAxLjMg5Y+v6KaW5YyW44Gn6Kqt44G/5Y+W44KM44KL44Ot44K444OD44KvCgojIyMgMS4zLjIg5aSJ5pWw6ZaT44Gu6Zai5L+CCgpgYGB7ciBmaWcuaGVpZ2h0PTMuNzUsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyDjg4fjg7zjgr/nlJ/miJAKc2V0LnNlZWQoMCkKZGYgPC0gdGliYmxlKHgxID0gcnVuaWYoNTApLCB5MSA9IHJ1bmlmKDUwKSwgeDIgPSBydW5pZig1MCksCiAgICAgICAgICAgICB5MiA9IDAuOCAqICh4MiAtIDAuNSkgKyAwLjUgKyAwLjEgKiBybm9ybSg1MCkpCgpwMSA8LSBkZiB8PgogIGdncGxvdChhZXMoeD14MSwgeT15MSkpICsKICBnZW9tX3BvaW50KGNvbG9yPSIjMDA3MkIyIikgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgZm9ybXVsYSA9IHkgfiB4LCBjb2xvcj0iIzAwNzJCMiIpICsKICBsYWJzKHggPSAi5aSJ5pWwWCIsIHkgPSAi5aSJ5pWwWSIsIHRpdGxlID0gIuebuOmWouOBruOBquOBhOS6jOOBpOOBruWkieaVsCIpICsKICBjb29yZF9maXhlZCgpCgpwMiA8LSBkZiB8PgogIGdncGxvdChhZXMoeD14MiwgeT15MikpICsKICBnZW9tX3BvaW50KGNvbG9yPSIjMDA5RTczIikgKwogIGdlb21fc21vb3RoKG1ldGhvZD1sbSwgZm9ybXVsYSA9IHkgfiB4LCBjb2xvcj0iIzAwOUU3MyIpICsKICBsYWJzKHggPSAi5aSJ5pWwWCIsIHkgPSAi5aSJ5pWwWSIsIHRpdGxlID0gIuebuOmWouOBruOBguOCi+S6jOOBpOOBruWkieaVsCIpICsKICBjb29yZF9maXhlZCgpCgpwMSArIHAyCmBgYAoKIyMjIDEuMy40IOaAp+izquOBrueVsOOBquOCi+WIhuW4g+OBruS+iwoK44CM5YiG5biD44Gu6KO+44Gu5qeY5a2Q44CN44Gu5Zuz44Gv44KI44GP55CG6Kej44Gn44GN44Gq44GL44Gj44Gf44Gu44Gn5o+P44GE44Gm44GE44G+44Gb44KT44CCCgpgYGB7ciBmaWcuaGVpZ2h0PTIuNSwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMg5bmz5Z2HMOOAgeaomea6luWBj+W3rjHjga7mraPopo/liIbluIPjgYvjgokxMDAwMOWAi+OBruOCteODs+ODl+ODq+OCkueUn+aIkAptdSA8LSAwCnNpZ21hIDwtIDEKc2V0LnNlZWQoMCkKcDEgPC0gZGF0YS5mcmFtZSh2YWwgPSBybm9ybSgxMDAwMCwgbWVhbiA9IG11LCBzZCA9IHNpZ21hKSkgfD4KICBnZ3Bsb3QoYWVzKHg9dmFsKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShhZXMoeT1hZnRlcl9zdGF0KGRlbnNpdHkpKSwgYmlucz0zMCwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gIiM5OTk5OTkiKSArIAogIHN0YXRfZnVuY3Rpb24oZnVuID0gZG5vcm0sIGFyZ3MgPSBsaXN0KG1lYW49bXUsIHNkPXNpZ21hKSwgY29sb3VyID0gInJlZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMC40NSkpICsKICBsYWJzKHRpdGxlID0gIuato+imj+WIhuW4gyIsIHggPSAiIiwgeSA9ICLnm7jlr77luqbmlbAiKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMy81KSAj57im5qiq5q+UCgojIOaomea6luOCs+ODvOOCt+ODvOWIhuW4g+OBi+OCiTEwMDAw5YCL44Gu44K144Oz44OX44Or44KS55Sf5oiQCnNldC5zZWVkKDApCnAyIDwtIGRhdGEuZnJhbWUodmFsID0gcmNhdWNoeSgxMDAwMCkpIHw+CiAgZHBseXI6OmZpbHRlcih2YWwgPiAtMTAgJiB2YWwgPCAxMCkgfD4gIyDlpJbjgozlgKTjgpLmjajjgabjgosKICBnZ3Bsb3QoYWVzKHg9dmFsKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5PWFmdGVyX3N0YXQoZGVuc2l0eSkpLCBiaW5zPTUwLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiIzk5OTk5OSIpICsKICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRjYXVjaHksIGNvbG91ciA9ICJibHVlIikgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCAwLjQ1KSkgKwogIGxhYnModGl0bGUgPSAi44Kz44O844K344O85YiG5biDIiwgeCA9ICIiLCB5ID0gIuebuOWvvuW6puaVsCIpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAzLzUpICPnuKbmqKrmr5QKCnAxICsgcDIKYGBgCgojIyMgMS4zLjUg5Yid5pyf44Gu5oSf5p+T55eH44Gu5ouh5aSnCgpgYGB7ciBmaWcuaGVpZ2h0PTMuNzUsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyDjg4fjg7zjgr/jg5Xjg6zjg7zjg6Djga7nlJ/miJAKc2V0LnNlZWQoMCkKZGYgPC0gdGliYmxlKAogIHggPSBzZXEoMCwgMzAsIDEpLCMgMOOBi+OCiTMw44G+44Gn44Gu56+E5Zuy44Gn562J6ZaT6ZqU44GrMzDlgIvjga7mlbDjgpLnlJ/miJAKICB5ID0gZXhwKDAuMSAqIHgpICogMTAwICogKDEgKyBybm9ybSgzMSwgMCwgMC4wOCkpICMg5oyH5pWw6Zai5pWw44Gr44Op44Oz44OA44Og44Gq44OO44Kk44K644KS5LuY5Yqg44GZ44KLCiAgKQoKcDEgPC0gZGYgfD4KICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geSkpICsKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoeD0i57WM6YGO5pel5pWwIiwgeT0i5paw6KaP5oSf5p+T6ICF5pWwIiwgdGl0bGU9IuODquODi+OCouOCueOCseODvOODq+OBp+OBruihqOekuiIpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSAj57im5qiq5q+UCgpwMiA8LSBkZiB8PgogIGdncGxvdChhZXMoeCA9IHgsIHkgPSB5KSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfeV9sb2cxMCgpICsKICBsYWJzKHg9Iue1jOmBjuaXpeaVsCIsIHk9IuaWsOimj+aEn+afk+iAheaVsO+8iOWvvuaVsOi7uO+8iSIsIHRpdGxlPSLniYflr77mlbDjgafjga7ooajnpLoiKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgI+e4puaoquavlAoKcDEgKyBwMgpgYGAKCuesrDHnq6Djga/jgZPjgZPjgb7jgafjgIIK