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

3.1 分布の特徴をとらえる

3.1.1 基本的な分布の特徴

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

my_plot <- function(data, title){
  data |>
    ggplot(aes(x = val, fill = name)) +
    geom_histogram(position = "identity",alpha=0.5, color = "black") +
    labs(title = title) +
    theme(
      legend.position="none", 
      axis.title.x=element_blank(), 
      axis.title.y=element_blank()
    )
}

# 中央傾向:平均値が0と10の二つの分布
# 平均0、標準偏差1の正規分布に従う乱数を1000個生成
# 平均5、標準偏差1の正規分布に従う乱数を1000個生成
set.seed(0)
p1 <- bind_rows(
  data.frame(val = rnorm(1000, mean = 0, sd = 1), name = "a"),
  data.frame(val = rnorm(1000, mean = 5, sd = 1), name = "b")
  ) |>
  my_plot("ピークの位置")

# 分散:分散が大きい分布と小さい分布
# 平均5、標準偏差1の正規分布に従う乱数を1000個生成
# 平均5、標準偏差3の正規分布に従う乱数を1000個生成
set.seed(0)
p2 <- bind_rows(
  data.frame(val = rnorm(1000, mean = 5, sd = 1), name = "a"),
  data.frame(val = rnorm(1000, mean = 5, sd = 3), name = "b")
  ) |>
  my_plot("広がりの度合い")

# 形状:正規分布、左寄せの分布
# 平均5、標準偏差1の正規分布に従う乱数を1000個生成
# 形状パラメータ2、尺度パラメータ2のガンマ分布に従う乱数を1000個生成
set.seed(0)
p3 <- bind_rows(
  data.frame(val = rnorm(1000, mean = 5, sd = 1) , name = "a"),
  data.frame(val = rgamma(1000, shape = 2, scale = 2) , name = "b")
) |>
  my_plot("分布の形状")

# ピーク:ピーク一つとピーク二つの分布
# 平均5、標準偏差1の正規分布に従う乱数を1000個生成
# 平均3、標準偏差1の正規分布と平均7、標準偏差1の正規分布に従う乱数を500個ずつ生成
set.seed(0)
p4 <- bind_rows(
  data.frame(val = rnorm(1000, mean = 5, sd = 1) , name = "a"),
  data.frame(val = c(rnorm(500, mean = 3, sd = 1), rnorm(500, mean = 7, sd = 1)), name = "b")
) |>
  my_plot("ピークの数と位置")

# 外れ値:ごく少量の大きい外れ値が含まれている分布と含まれていない分布
# 平均5、標準偏差1の正規分布に従う乱数を995個と平均50、標準偏差5の正規分布に従う乱数を5個生成
# 平均5、標準偏差1の正規分布に従う乱数を1000個生成
set.seed(0)
p5 <- bind_rows(
  data.frame(val = c(rnorm(995, mean = 5, sd = 1), rnorm(5, mean = 50, sd = 5)), name = "a"),
  data.frame(val = rnorm(1000, mean = 5, sd = 1), name = "b")
) |>
  my_plot("外れ値")

# 外れ値:外れ値が多少ある分布とほぼない分布
# 平均5、標準偏差1の正規分布に従う乱数を1000個生成
# 平均5、標準偏差1の正規分布に従う乱数を975個と平均15、標準偏差1の正規分布に従う乱数を25個生成
set.seed(0)
p6 <- bind_rows(
  data.frame(val = rnorm(1000, mean = 5, sd = 1) , name = "a"),
  data.frame(val = c(rnorm(975, mean = 5, sd = 1), rnorm(25, mean = 15, sd = 1)), name = "b")
) |>
  my_plot("外れ値?")

(p1 + p2 + p3) / (p4 + p5 + p6)

3.1.2 ヒストグラムの形状とビンの定義

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

set.seed(0)
data_df <- data.frame(data = rnorm(100, 0, 1)) # 観測結果をデータフレームに変換

my_plot2 <- function(data, title, binwidth = 0.4, boundary = 0){
  data |>
    ggplot(aes(x=data)) +
    geom_histogram(
      aes(y = after_stat(density)), fill = "blue",
      binwidth = binwidth, boundary = boundary
      ) +
    stat_function(fun=dnorm, color="red", linewidth = 1, linetype=2, args=list(mean=0)) + 
    labs(title = title) +
    theme(
      legend.position="none", 
      axis.title.x=element_blank(), 
      axis.title.y=element_blank(),
      aspect.ratio = 1
    )
}

p1 <- data_df |>
  my_plot2("ビン幅 = 0.8", binwidth =0.8)

p2 <- data_df |>
  my_plot2("ビン幅 = 0.4", binwidth =0.4)

p3 <- data_df |>
  my_plot2("ビン幅 = 0.08", binwidth =0.08)

p4 <- data_df |>
  my_plot2("boundary = 0", boundary = 0)

p5 <- data_df |>
  my_plot2("boundary = 0.1", boundary = 0.1)

p6 <- data_df |>
  my_plot2("boundary = 0.2", binwidth =0.2)

(p1 + p2 + p3) / (p4 + p5 + p6)

3.1.3 正規分布との比較

元は1000回となっているがキレイな分布にならないので10万回に増やしてます。

library(conflicted)
library(tidyverse)
library(mclust)
library(MASS)
library(patchwork)

# サイコロを100回振った時の出目の総和を10万回計算
roll_dice <- function(n = 100, repeat_num = 100000) {
  rowSums(
    matrix(sample.int(6, size = n*repeat_num, replace = TRUE), ncol = n)
    )
}

# ヒストグラムの作成
set.seed(0)
hist_data <- roll_dice()

p1 <- data.frame(x = hist_data) |>
  ggplot(aes(x=x)) +
  geom_histogram(aes(y=after_stat(density)), binwidth=1) +
  stat_function(fun=dnorm, args=list(mean=mean(hist_data), sd=sd(hist_data)),
                color='red', linetype="dashed") +
  labs(title = "データを正規分布でフィッティング",
       x="サイコロを100回振って出た目の総和", y="相対頻度") +
  theme(aspect.ratio = 4/5)

# ピーク一つとピーク二つの分布
#平均3, 標準偏差1 と 平均7, 標準偏差1 の正規分布に従う乱数を500個ずつ生成
set.seed(0)
data4_b <- c(
  rnorm(500, mean = 3, sd = 1), 
  rnorm(500, mean = 7, sd = 1)
  )

# 混合ガウシアンモデルのパラメータを推定
gmm <- Mclust(data4_b, G=2)

# データと混合ガウシアンモデルの結果をプロット
p2 <- data.frame(x = data4_b) |>
  ggplot(aes(x=x)) +
  geom_histogram(aes(y=after_stat(density)), binwidth=0.2, colour="black", fill="greenyellow") +
  stat_function(
    fun = function(x) gmm$parameters$pro[1] * dnorm(x, gmm$parameters$mean[1], sqrt(gmm$parameters$variance$sigma)),
    linetype = "dashed",
    color = "red"
    ) +
  stat_function(
    fun = function(x) gmm$parameters$pro[2]*dnorm(x, gmm$parameters$mean[2], sqrt(gmm$parameters$variance$sigma)),
    linetype = "dashed",
    color = "blue"
    ) +
  labs(
    title = "データから2つの正規分布を推定",
    x = expression(paste("観測値 ", italic("X"))),
    y = "相対頻度"
    ) +
  theme(
    aspect.ratio = 4/5,
    axis.title.y=element_blank()
    )

# サンプルを生成
set.seed(0)
lognorm_samples <- rlnorm(10000, meanlog = 0.954, sdlog = 0.65)
log_lognorm_samples <- log(lognorm_samples) # 対数変換

# 平均と標準偏差を推定
lognorm_fit <- fitdistr(log_lognorm_samples, densfun = "normal")

p3 <- data.frame(x = lognorm_samples) |>
  ggplot(aes(x = x)) +
  geom_histogram(aes(y = after_stat(density)), fill = "purple", colour="black") +
  labs(x = "観測値 X", y = "相対頻度") +
  theme(aspect.ratio = 4/5)
  
p4 <- data.frame(x = log_lognorm_samples) |>
  ggplot(aes(x = x)) +
  geom_histogram(aes(y = after_stat(density)), fill = "purple", colour="black") +
  labs(x = "log X", y = "相対頻度") +
  stat_function(fun=dnorm, color="red", size = 1, linetype=2,
    args=list(mean=lognorm_fit$estimate[1], sd=lognorm_fit$estimate[2])
    )  +
  theme(aspect.ratio = 4/5)

(p1 + p2) / (p3 + p4)

3.1.4 様々な理論分布

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

# 描画用一時関数
my_plot_line <- function(df, color, title) {
    ggplot(df, aes(x = x, y = y)) +
    geom_line(color = color, linewidth = 2) +
    labs(title = title) +
    theme(
      axis.title.x=element_blank(),
      axis.title.y=element_blank()
    )
}

my_plot_point <- function(df, color, title) {
  ggplot(df, aes(x = x, y = y)) +
    geom_point(color = color) +
    labs(title = title) +
    theme(
      axis.title.x=element_blank(),
      axis.title.y=element_blank()
    )
}

# 一様分布
uni_p <- tibble(
  x = seq(-1, 1, length.out = 1000),
  y = dunif(x, min = -1, max = 1)
  ) |>
  my_plot_line("orange", "一様分布") 

# 正規分布
norm_p <- tibble(
  x = seq(-4, 4, length.out = 1000),
  y = dnorm(x)
)  |>
  my_plot_line("red", "正規分布") 

# 二項分布
bin_p <- tibble(
  x = 0:10,
  y = dbinom(0:10, size=10, prob=0.5)
  ) |>
  my_plot_point("blue", "二項分布") 

# ポアソン分布
poi_p <- tibble(
  x = 0:9, 
  y = dpois(0:9, lambda = 3)
) |>
  my_plot_point("forestgreen", "ポアソン分布") 

# 指数分布
exp_p <- tibble(
  x = seq(0, 10, length.out = 1000),
  y = dexp(x)
)  |>
  my_plot_point("purple", "指数分布") 

# ガンマ分布
gam_p <- tibble(
  x = seq(0, 10, length.out = 1000),
  y = dgamma(x, 5)
) |>
  my_plot_point("brown", "ガンマ分布") 

# ワイブル分布
wei_p <- tibble(
  x = seq(0.01, 2, length.out = 1000),
  y = dweibull(x, 1.5)
)  |>
  my_plot_point("grey", "ワイブル分布") 
  
# 対数正規分布
ln_p <- tibble(
  x = seq(0.01, 10, length.out = 1000),
  y = dlnorm(x)
)  |>
  my_plot_line("pink", "対数正規分布") 

# パレート分布
paretofunc <- function(x, alpha = 5, xm = 1) alpha * (xm^alpha) / (x^(alpha+1))
pare_p <- tibble(
  x = seq(1, 4, length.out = 1000),
  y = paretofunc(x)
)  |>
  my_plot_line("cyan", "パレート分布") 

# グラフを並べて表示
uni_p + norm_p + bin_p + poi_p + exp_p + gam_p + wei_p + ln_p + pare_p +
  plot_layout(ncol = 2)

3.1.5 累積分布でデータを見る

library(conflicted)
library(tidyverse)

# 正規分布から100点と500点のサンプルを生成
set.seed(0)
samples <- rnorm(600)

p1 <- tibble(
  x = samples[1:100],
  fill = if_else(x >= quantile(x, 0.8), "80%点以上", "80%点未満")
  ) |>
  ggplot(aes(x = x, fill = fill)) +
  geom_histogram() +
  labs(x = "観測値", y = "頻度") +
  theme(legend.position = "none")
 
p2 <- tibble(x = samples[1:100]) |>
  ggplot(aes(x = x)) +
  stat_ecdf(pad = FALSE, color = "blue") +
  stat_function(fun=pnorm, color = "red", linetype = 2) +
  labs(title = "サンプルサイズ=100", x = "観測値", y = "累積相対頻度")

p3 <- tibble(x = samples[101:500]) |>
  ggplot(aes(x = x)) +
  stat_ecdf(pad = FALSE, color = "blue") +
  stat_function(fun=pnorm, color = "red", linetype = 2) +
  labs(title = "サンプルサイズ=500", x = "観測値", y = "累積相対頻度")

(p1 + plot_spacer()) / (p2 + p3)

3.2 線で特徴をとらえる

3.2.1 時間推移を可視化する

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

df <- read_csv(
  "https://www.esri.cao.go.jp/jp/sna/data/data_list/sokuhou/files/2023/qe233_2/tables/gaku-jfy2332.csv",
  col_names = FALSE, skip = 7, col_types = "cnn__nn_nn_______________________"
  ) |>
  drop_na() |>
  mutate( # 各金額を兆円単位に変換
    年度 = str_remove(X1, "/4-3.") |> as.integer(),
    `国内総生産(支出側)` = X2 / 1000,
    民間最終消費支出 = X3 / 1000,
    民間住宅 = X6 / 1000,
    民間企業設備 = X7 / 1000,
    政府最終消費支出 = X9 / 1000,
    公的固定資本形成 = X10 / 1000,
    .keep = "unused"
    )

p1 <- df |>
  ggplot(aes(x = 年度, y = `国内総生産(支出側)`)) +
  geom_line(color="skyblue") +
  labs(title = "折れ線グラフ", y = "国内総生産(支出側) [兆円]") +
  theme(axis.title.x=element_blank())

p2 <- df |>
  ggplot(aes(x = 年度, y = `国内総生産(支出側)`)) +
  geom_area(fill="skyblue") +
  labs(title = "エリアチャート", x = "年度") +
  theme(axis.title.y=element_blank())

p3 <- df |>
  dplyr::select(!`国内総生産(支出側)`) |>
  pivot_longer(!年度, names_to = "sector", values_to = "val") |>
  mutate(
    sector = factor(sector, levels = rev(c("民間最終消費支出", "民間住宅", "民間企業設備", "政府最終消費支出", "公的固定資本形成")))
    ) |>
  ggplot(aes(x = 年度, y = val, fill = sector)) +
  geom_area() +
  labs(title = "積み上げエリアチャート", x = "年度", y = "国内総生産(支出側) [兆円]") +
  theme(
    axis.title=element_blank(),
    legend.title = element_blank(),
    legend.position = c(0.5, 0.25)
    )

p1 + p2 + p3

3.2.2 複数の時系列データの描画

library(conflicted)
library(tidyverse)

n_days <- 30  # 30日間
n_individuals <- 30  # 個体数
half_n <- as.integer(n_individuals / 2)  # 個体数の半分

# 各個体の活動量期待値を生成
set.seed(0)
expected_values <- c(
  rnorm(half_n, mean = 50, sd = 5),# 期待値50、標準偏差5の正規分布に従う乱数を生成
  rnorm(half_n, mean = 70, sd = 5)# 期待値70、標準偏差5の正規分布に従う乱数を生成
  )

# 各個体の30日間の活動量を生成
# 期待値expected_values、標準偏差10の正規分布に従う乱数を生成
set.seed(0)
activity_data <- purrr::map(expected_values, \(x) rnorm(n_days, mean = x, sd = 10)) |>
  list_c() |>
  matrix(ncol = 30) |>
  as.data.frame() |>
  set_names(paste0("v", formatC(1:30, width=2, flag="0"))) |>
  mutate(days = 1:30) |>
  pivot_longer(!days) |>
  group_by(days) |>
  mutate(
    ev = expected_values,
    group = c(rep("a", 15), rep("b", 15))
    ) |>
  ungroup()

# 全個体をそれぞれ違う色で描画
p1 <- activity_data |>
  ggplot(aes(x = days, y = value, color = name)) +
  geom_line() +
  labs(title = "すべての個体を異なる色で描画", x = "経過日数", y = "活動量スコア") +
  theme(
    legend.position="none"
    , axis.title.x=element_blank()
    , axis.title.y=element_blank()
  )

# 期待値最大の個体を強調
p2 <- activity_data |>
  mutate(color = if_else(ev == max(expected_values), "a", "b")) |>
  ggplot(aes(x = days, y = value, color = color, group=name)) +
  geom_line() +
  scale_color_manual(values = c("red", "grey")) +
  labs(title = "着目する個体だけ異なる色で描画", x = "経過日数", y = "活動量スコア") +
  theme(
    legend.position="none"
    , axis.title.x=element_blank()
    , axis.title.y=element_blank()
  )

# 15個体のグループごとに描画
p3 <- activity_data |>
  ggplot(aes(x = days, y = value, color = group, group=name)) +
  geom_line() +
  labs(title = "グループごとに異なる色で描画", x = "経過日数", y = "活動量スコア") +
  theme(
    legend.position="none"
    , axis.title.x=element_blank()
    , axis.title.y=element_blank()
  )

# 15個体のグループごとに平均と標準偏差で描画
p4 <- activity_data |>
  summarise(
    mean = mean(value),
    sd = sd(value),
    .by = c(group, days)
  ) |>
  ggplot(aes(x = days, y = mean, color = group, group=group)) +
  geom_ribbon(aes(ymin = mean - sd, ymax = mean + sd, fill = group), alpha=0.2) +
  geom_line() +
  labs(title = "グループごとに平均と標準偏差で描画", x = "経過日数", y = "活動量スコア") +
  theme(
    legend.position="none"
    , axis.title.x=element_blank()
    , axis.title.y=element_blank()
  ) +
  coord_cartesian(ylim = c(20,100))

(p1 + p2 )/(p3 + p4)

3.2.3 スロープグラフで個々の変化をとらえる

library(conflicted)
library(tidyverse)

# ランダムな体重データを生成
set.seed(1)
weights_1 <- rnorm(50, 60, 2) # 平均60、分散4の正規分布
weights_2 <- weights_1 + rnorm(50, -1, 0.5) # 平均-1、分散0.25の正規分布
weights_3 <- weights_1 + rnorm(50, 0, 0.5) # 平均0、分散0.25の正規分布(追加)

# データフレームを作成
df <- data.frame(
  Weight = c(weights_1, weights_2),
  Group = factor(
    c(rep("実験開始時", 50), rep("一か月後",50)),
    levels=c("実験開始時", "一か月後")
    ),
  Person = c(seq(50), seq(50))
  )
  
# 点のみプロット
p1 <- df |>
  ggplot(aes(x = Group, y = Weight)) +
  geom_jitter(width = 0.05, height = 0, alpha = 0.5) +
  labs(x = "期間", y = "体重 [kg]") +
  coord_cartesian(ylim = c(54, 64)) +
  labs(title = "ストリッププロット") +
  theme(axis.title.x=element_blank()) 

df2 <- df |>
  pivot_wider(names_from = Group, values_from = Weight) |>
  mutate(diff = 一か月後 - 実験開始時) |>
  pivot_longer(!c(Person, diff), names_to = "Group", values_to = "Weight") |>
  mutate(Group = factor(Group, levels=c("実験開始時", "一か月後"))) 

# スロープグラフ
p2 <- df2 |>
  ggplot(aes(x = Group, y = Weight, group = Person, color = diff)) +
  geom_line(alpha = 0.5) +
  geom_point(alpha = 0.5) +
  scale_color_gradient(
    limit = c(-2,2),
    low = "blue",
    high = "red",
    guide = "colorbar"
  ) +
  coord_cartesian(ylim = c(54, 64)) +
  labs(title = "スロープグラフ", color = "差[kg]") +
  theme(
    axis.title.x=element_blank()
    , axis.title.y=element_blank()
  ) 

# スロープグラフ2
p3 <- data.frame(
  Weight = c(weights_1, weights_3),
  Group = factor(
    c(rep("実験開始時", 50), rep("一か月後",50)),
    levels=c("実験開始時", "一か月後")
  ),
  Person = c(seq(50), seq(50))
) |>
  pivot_wider(names_from = Group, values_from = Weight) |>
  mutate(diff = 一か月後 - 実験開始時) |>
  pivot_longer(!c(Person, diff), names_to = "Group", values_to = "Weight") |>
  mutate(Group = factor(Group, levels=c("実験開始時", "一か月後"))) |>
  ggplot(aes(x = Group, y = Weight, group = Person, color = diff)) +
  geom_line(alpha = 0.5) +
  geom_point(alpha = 0.5) +
  scale_color_gradient(
    limit = c(-2,2),
    low = "blue",
    high = "red",
    guide = "colorbar"
    ) +
  coord_cartesian(ylim = c(54, 64)) +
  labs(title = "スロープグラフ") +
  theme(
    legend.position="none"
    , axis.title.x=element_blank()
    , axis.title.y=element_blank()
  ) 

p1 + p3 + p2

3.2.4 医薬品の月間販売額を要素分解する

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

# データの読み込み
df <- read_csv('https://raw.githubusercontent.com/selva86/datasets/master/a10.csv')

# 時系列データに変換
ts_df <- ts(
  df$value,
  start=c(year(df$date[1]), month(df$date[1])),
  frequency=12
  )

# 時系列データを分解
decomposed_add <- decompose(ts_df, type="additive") #加算
decomposed_mlt <- decompose(ts_df, type="multiplicative") #乗算

# プロット作成
p1 <- decomposed_add |>
  autoplot() +
  labs(title = "足し算で分解")

p2 <- decomposed_mlt |>
  autoplot() +
  labs(title = "掛け算で分解")

p1 + p2

3.3 2変数の関係をとらえる

3.3.1 ペアプロットによる分布の可視化

ペアプロットはGGallyパッケージのggpairs()関数が便利。

library(conflicted)
library(tidyverse)
library(GGally)
library(palmerpenguins)

penguins |>
  ggpairs(
    columns = c("bill_length_mm", "bill_depth_mm", "flipper_length_mm", "body_mass_g"),
    columnLabels = c("くちばしの長さ [mm]", "くちばしの厚さ [mm]", "ひれの長さ [mm]", "体重 [g]"),
    aes(color = species),
    diag = list(continuous = wrap("densityDiag", alpha = .5))
    )

3.3.2 相関を見るための散布図

library(conflicted)
library(tidyverse)
library(palmerpenguins)

penguins |>
  ggplot(aes(x = bill_length_mm, y = body_mass_g, color = species, fill = species)) +
  geom_point(alpha=0.3) +
  geom_smooth(formula = y ~ x, method = "lm") +
  labs(x = "くちばしの長さ [mm]", y = "体重 [g]") +
  theme(aspect.ratio = 1)

3.3.3 y = x との比較

library(conflicted)
library(tidyverse)
library(palmerpenguins)
library(rsample)
library(MLmetrics)

# データの準備
pen <- penguins |>
  dplyr::select(body_mass_g, bill_length_mm, bill_depth_mm, flipper_length_mm, species) |>
  drop_na()

pen_list <- split(pen, pen$species)

# 分割
pen_split <- pen_list |>
  purrr::map(\(df) initial_split(df, prop = 0.8, strata = "body_mass_g"))
  
pen_train <-  pen_split |>
  purrr::map(\(df) training(df))

pen_test <-  pen_split |>
  purrr::map(\(df) testing(df))

# 学習
pen_lm <- pen_train |>
  purrr::map(\(df) lm(body_mass_g ~ bill_length_mm + bill_depth_mm + flipper_length_mm, data = df))

# 検証
pen_pred <- pen_lm |>
  purrr::map2(pen_test, \(x, y) predict(x, newdata = y))

# 評価(RMSE)
pen_true <- pen_test |>
  purrr::map(pluck("body_mass_g"))
pen_pred |>
  purrr::map2_dbl(pen_true, \(y_pred, y_true) RMSE(y_pred, y_true))
##    Adelie Chinstrap    Gentoo 
##  316.8150  267.0557  324.3572
# Adelie Chinstrap    Gentoo 
# 336.5512  305.2450  313.6054 

# 散布図の作成
data.frame(
  y_true = pen_true |> unlist(),
  y_pred = pen_pred |> unlist()
  ) |>
  rownames_to_column(var = "species") |>
  mutate(species = str_remove(species, "[0-9]+")) |>
  ggplot(aes(x = y_true, y = y_pred, color = species)) +
    geom_point(alpha = 0.6) +
    labs(x = "実際の体重 [g]", y = "モデルによる予測 [g]", title = "線形回帰による予測") +
    geom_abline(intercept = 0, slope = 1) +
  coord_cartesian(xlim = c(3000, 6000), ylim = c(3000, 6000)) +
  theme(aspect.ratio = 1)

3.3.4 y = x との比較:その他の例

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

# SNSデータの読み込み
df_sns <- read_csv("https://raw.githubusercontent.com/morimotoosamu/data_visualization/main/data/sns.csv")

# 体重データを生成
df_weights <- tibble(
  Weight1 = rnorm(50, mean = 60, sd = sqrt(2)), # 平均60、分散2の正規分布,
  Weight2 = Weight1 + rnorm(50, mean = -1, sd = sqrt(0.5)) # 平均-1、分散0.5の正規分布
)

# ggplotを用いたグラフ描画
p1 <- df_weights |>
  ggplot(aes(x = Weight1, y = Weight2)) + 
  geom_point(color = "orange") +
  geom_abline(intercept = 0, slope = 1, color = "black") +
  labs(
    title = "同じ量の2時点の比較",
    x = "実験開始時体重 [kg]",
    y = "一か月後体重 [kg]") +
  coord_cartesian(xlim = c(56, 64), ylim = c(56, 64)) +
  theme(aspect.ratio = 1)

p2 <- df_sns |>
  ggplot(aes(x = Sent, y = Received)) + 
  geom_point(color = "blue") +
  geom_abline(intercept = 0, slope = 1, color = "black") +
  labs(
    title = "ペア間の双方向の量の比較",
    x = "送信したリプライ数",
    y = "受信したリプライ数"
    ) +
  coord_cartesian(xlim = c(0, 31), ylim = c(0, 31)) +
  theme(aspect.ratio = 1)

p1 + p2

3.3.5 サンプルサイズが大きいケース

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

set.seed(0)

data <- bind_rows(
  tibble(# 相関の少ないデータの生成
    x = rnorm(100, 25, 10), # 平均25、分散10の正規分布
    y = rnorm(100, 75, 10)  # 平均75、分散10の正規分布
    ),
  tibble(# 相関の多いデータの生成
    x = rnorm(500, 50, 30), # 平均50、分散30の正規分布
    y = 0.5 * x + rnorm(500, 0, 10) # y = 0.5x + ノイズ
    ),
  tibble(
     x = rnorm(400, 25, 20), # 平均25、分散20の正規分布
     y = rnorm(400, 25, 30)  # 平均25、分散30の正規分布
     )
  )

# グラフ生成
p1 <- data |>
  ggplot(aes(x =x, y = y)) +
  geom_point() +
  coord_fixed() +
  labs(title = "不透明マーカー利用")

# 中央のサブプロットに統合データの半透明なマーカー散布図をプロット
p2 <- data |>
  ggplot(aes(x =x, y = y)) +
  geom_point(alpha = 0.1) +
  coord_fixed() +
  labs(title = "半透明マーカー利用")

# ヒートマップを生成
#jet.colors <- colorRampPalette(
#  c("#00007F", "blue", "#007FFF", "cyan", "#7FFF7F", "yellow", "#FF7F00", "red",
#    "#7F0000"))

p3 <- data |>
  ggplot(aes(x = x, y = y)) +
  stat_density2d(aes(fill = after_stat(density)), geom="tile", contour=FALSE, n = 20) +
  scale_fill_gradientn(colours = hcl.colors(10)) +
  coord_fixed() +
  labs(title = "ヒートマップ")

p1 + p2 +p3

3.3.6 バブルチャートによる情報提示

library(conflicted)
library(tidyverse)
library(ggrepel)
library(janitor)

# データの読み込み
df <- read_csv(
  "https://raw.githubusercontent.com/tkEzaki/data_visualization/main/3%E7%AB%A0/data/bubble_chart_data.csv"
  ) |>
  clean_names() |>
  dplyr::filter(x2021_yr2021 != "..") |>
  mutate(x2021_yr2021 = as.numeric(x2021_yr2021))

region_df <- read_csv(
  "https://raw.githubusercontent.com/tkEzaki/data_visualization/main/3%E7%AB%A0/data/bubble_chart_region_data.csv"
  ) |>
  clean_names() 

# データの前処理
df_split <- df |>
  split(df$series_name)

names(df_split) <- c("gdp", "life_exp", "population")

# GDP, Life expectancy, Populationのデータをマージ
data <- df_split$gdp |>
  dplyr::select(country_name, country_code, x2021_yr2021) |>
  left_join(
    df_split$life_exp |>
      dplyr::select(country_name, x2021_yr2021),
    by = join_by(country_name),
    suffix = c("_GDP", "_Life_Expectancy")
    ) |>
  left_join(
    df_split$population |>
      dplyr::select(country_name, x2021_yr2021),
    by = join_by(country_name)
    ) |>
  rename(
    x2021_yr2021_Population = x2021_yr2021
  ) |>
  left_join( #regionをCountry Codeでjoin
    region_df |>
      dplyr::select(alpha_3, region),
    by = join_by(country_code == alpha_3)
    ) |>
  left_join( #regionをCountry_Nameでjoin
    region_df |>
      dplyr::select(name, region),
    by = join_by(country_name == name)
  ) |>
  mutate( #region確定
    region = if_else(is.na(region.x), region.y, region.x),
    .keep = "unused"
    ) |>
  drop_na() |> #region無い行を削除
  mutate(# GDP per capita(千ドル)を計算
    GDP_per_capita = x2021_yr2021_GDP / x2021_yr2021_Population / 1000,
    x2021_yr2021_Population = x2021_yr2021_Population / 100000
  )

# 描画
data |>
  ggplot(aes(
    x = GDP_per_capita,
    y = x2021_yr2021_Life_Expectancy,
    color = region,
    size = x2021_yr2021_Population,
    label = country_name
    )) +
  geom_point(alpha = 0.6) +
  scale_size_area(max_size = 11) + #値と面積が比例するように
  geom_text_repel(size = 3) +
  scale_x_log10() +
  labs(
    title = "色や大きさで情報を付加する",
    x = "一人あたりGDP [1,000$](対数軸)",
    y = "平均寿命 [年]", 
    size = "人口(10万人)", 
    color = "地域"
    ) +
  theme(aspect.ratio = 1)

第3章はここまで。

LS0tCnRpdGxlOiAi56ysM+eroCDjg6Hjgqvjg4vjgrrjg6DjgpLjgajjgonjgYjjgovjg4fjg7zjgr/lj6/oppbljJYiCmF1dGhvcjogIk9zYW11LCBNT1JJTU9UTyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0aGVtZTogdW5pdGVkICAgIAogICAgbWRfZXh0ZW5zaW9uczogIi1hc2NpaV9pZGVudGlmaWVycyIKICAgIHRvY19mbG9hdDogeWVzCiAgICBmaWdfd2lkdGg6IDcuNQogICAgZmlnX2hlaWdodDogNS42MjUKICAgIGRldjogcmFnZ19wbmcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgrlm7Pjga7lj7PkuIrjga5gc2hvd2Djg5zjgr/jg7PjgpLmirzjgZnjgahS44Gu44Kz44O844OJ44GM6KGo56S644GV44KM44G+44GZ44CCCgojIyAzLjEg5YiG5biD44Gu54m55b6044KS44Go44KJ44GI44KLCgojIyMgMy4xLjEg5Z+65pys55qE44Gq5YiG5biD44Gu54m55b60CgpgYGB7ciBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQoKbXlfcGxvdCA8LSBmdW5jdGlvbihkYXRhLCB0aXRsZSl7CiAgZGF0YSB8PgogICAgZ2dwbG90KGFlcyh4ID0gdmFsLCBmaWxsID0gbmFtZSkpICsKICAgIGdlb21faGlzdG9ncmFtKHBvc2l0aW9uID0gImlkZW50aXR5IixhbHBoYT0wLjUsIGNvbG9yID0gImJsYWNrIikgKwogICAgbGFicyh0aXRsZSA9IHRpdGxlKSArCiAgICB0aGVtZSgKICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIiwgCiAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksIAogICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpCiAgICApCn0KCiMg5Lit5aSu5YK+5ZCR77ya5bmz5Z2H5YCk44GMMOOBqDEw44Gu5LqM44Gk44Gu5YiG5biDCiMg5bmz5Z2HMOOAgeaomea6luWBj+W3rjHjga7mraPopo/liIbluIPjgavlvpPjgYbkubHmlbDjgpIxMDAw5YCL55Sf5oiQCiMg5bmz5Z2HNeOAgeaomea6luWBj+W3rjHjga7mraPopo/liIbluIPjgavlvpPjgYbkubHmlbDjgpIxMDAw5YCL55Sf5oiQCnNldC5zZWVkKDApCnAxIDwtIGJpbmRfcm93cygKICBkYXRhLmZyYW1lKHZhbCA9IHJub3JtKDEwMDAsIG1lYW4gPSAwLCBzZCA9IDEpLCBuYW1lID0gImEiKSwKICBkYXRhLmZyYW1lKHZhbCA9IHJub3JtKDEwMDAsIG1lYW4gPSA1LCBzZCA9IDEpLCBuYW1lID0gImIiKQogICkgfD4KICBteV9wbG90KCLjg5Tjg7zjgq/jga7kvY3nva4iKQoKIyDliIbmlaPvvJrliIbmlaPjgYzlpKfjgY3jgYTliIbluIPjgajlsI/jgZXjgYTliIbluIMKIyDlubPlnYc144CB5qiZ5rqW5YGP5beuMeOBruato+imj+WIhuW4g+OBq+W+k+OBhuS5seaVsOOCkjEwMDDlgIvnlJ/miJAKIyDlubPlnYc144CB5qiZ5rqW5YGP5beuM+OBruato+imj+WIhuW4g+OBq+W+k+OBhuS5seaVsOOCkjEwMDDlgIvnlJ/miJAKc2V0LnNlZWQoMCkKcDIgPC0gYmluZF9yb3dzKAogIGRhdGEuZnJhbWUodmFsID0gcm5vcm0oMTAwMCwgbWVhbiA9IDUsIHNkID0gMSksIG5hbWUgPSAiYSIpLAogIGRhdGEuZnJhbWUodmFsID0gcm5vcm0oMTAwMCwgbWVhbiA9IDUsIHNkID0gMyksIG5hbWUgPSAiYiIpCiAgKSB8PgogIG15X3Bsb3QoIuW6g+OBjOOCiuOBruW6puWQiOOBhCIpCgojIOW9oueKtu+8muato+imj+WIhuW4g+OAgeW3puWvhOOBm+OBruWIhuW4gwojIOW5s+WdhzXjgIHmqJnmupblgY/lt64x44Gu5q2j6KaP5YiG5biD44Gr5b6T44GG5Lmx5pWw44KSMTAwMOWAi+eUn+aIkAojIOW9oueKtuODkeODqeODoeODvOOCvzLjgIHlsLrluqbjg5Hjg6njg6Hjg7zjgr8y44Gu44Ks44Oz44Oe5YiG5biD44Gr5b6T44GG5Lmx5pWw44KSMTAwMOWAi+eUn+aIkApzZXQuc2VlZCgwKQpwMyA8LSBiaW5kX3Jvd3MoCiAgZGF0YS5mcmFtZSh2YWwgPSBybm9ybSgxMDAwLCBtZWFuID0gNSwgc2QgPSAxKSAsIG5hbWUgPSAiYSIpLAogIGRhdGEuZnJhbWUodmFsID0gcmdhbW1hKDEwMDAsIHNoYXBlID0gMiwgc2NhbGUgPSAyKSAsIG5hbWUgPSAiYiIpCikgfD4KICBteV9wbG90KCLliIbluIPjga7lvaLnirYiKQoKIyDjg5Tjg7zjgq/vvJrjg5Tjg7zjgq/kuIDjgaTjgajjg5Tjg7zjgq/kuozjgaTjga7liIbluIMKIyDlubPlnYc144CB5qiZ5rqW5YGP5beuMeOBruato+imj+WIhuW4g+OBq+W+k+OBhuS5seaVsOOCkjEwMDDlgIvnlJ/miJAKIyDlubPlnYcz44CB5qiZ5rqW5YGP5beuMeOBruato+imj+WIhuW4g+OBqOW5s+WdhzfjgIHmqJnmupblgY/lt64x44Gu5q2j6KaP5YiG5biD44Gr5b6T44GG5Lmx5pWw44KSNTAw5YCL44Ga44Gk55Sf5oiQCnNldC5zZWVkKDApCnA0IDwtIGJpbmRfcm93cygKICBkYXRhLmZyYW1lKHZhbCA9IHJub3JtKDEwMDAsIG1lYW4gPSA1LCBzZCA9IDEpICwgbmFtZSA9ICJhIiksCiAgZGF0YS5mcmFtZSh2YWwgPSBjKHJub3JtKDUwMCwgbWVhbiA9IDMsIHNkID0gMSksIHJub3JtKDUwMCwgbWVhbiA9IDcsIHNkID0gMSkpLCBuYW1lID0gImIiKQopIHw+CiAgbXlfcGxvdCgi44OU44O844Kv44Gu5pWw44Go5L2N572uIikKCiMg5aSW44KM5YCk77ya44GU44GP5bCR6YeP44Gu5aSn44GN44GE5aSW44KM5YCk44GM5ZCr44G+44KM44Gm44GE44KL5YiG5biD44Go5ZCr44G+44KM44Gm44GE44Gq44GE5YiG5biDCiMg5bmz5Z2HNeOAgeaomea6luWBj+W3rjHjga7mraPopo/liIbluIPjgavlvpPjgYbkubHmlbDjgpI5OTXlgIvjgajlubPlnYc1MOOAgeaomea6luWBj+W3rjXjga7mraPopo/liIbluIPjgavlvpPjgYbkubHmlbDjgpI15YCL55Sf5oiQCiMg5bmz5Z2HNeOAgeaomea6luWBj+W3rjHjga7mraPopo/liIbluIPjgavlvpPjgYbkubHmlbDjgpIxMDAw5YCL55Sf5oiQCnNldC5zZWVkKDApCnA1IDwtIGJpbmRfcm93cygKICBkYXRhLmZyYW1lKHZhbCA9IGMocm5vcm0oOTk1LCBtZWFuID0gNSwgc2QgPSAxKSwgcm5vcm0oNSwgbWVhbiA9IDUwLCBzZCA9IDUpKSwgbmFtZSA9ICJhIiksCiAgZGF0YS5mcmFtZSh2YWwgPSBybm9ybSgxMDAwLCBtZWFuID0gNSwgc2QgPSAxKSwgbmFtZSA9ICJiIikKKSB8PgogIG15X3Bsb3QoIuWkluOCjOWApCIpCgojIOWkluOCjOWApO+8muWkluOCjOWApOOBjOWkmuWwkeOBguOCi+WIhuW4g+OBqOOBu+OBvOOBquOBhOWIhuW4gwojIOW5s+WdhzXjgIHmqJnmupblgY/lt64x44Gu5q2j6KaP5YiG5biD44Gr5b6T44GG5Lmx5pWw44KSMTAwMOWAi+eUn+aIkAojIOW5s+WdhzXjgIHmqJnmupblgY/lt64x44Gu5q2j6KaP5YiG5biD44Gr5b6T44GG5Lmx5pWw44KSOTc15YCL44Go5bmz5Z2HMTXjgIHmqJnmupblgY/lt64x44Gu5q2j6KaP5YiG5biD44Gr5b6T44GG5Lmx5pWw44KSMjXlgIvnlJ/miJAKc2V0LnNlZWQoMCkKcDYgPC0gYmluZF9yb3dzKAogIGRhdGEuZnJhbWUodmFsID0gcm5vcm0oMTAwMCwgbWVhbiA9IDUsIHNkID0gMSkgLCBuYW1lID0gImEiKSwKICBkYXRhLmZyYW1lKHZhbCA9IGMocm5vcm0oOTc1LCBtZWFuID0gNSwgc2QgPSAxKSwgcm5vcm0oMjUsIG1lYW4gPSAxNSwgc2QgPSAxKSksIG5hbWUgPSAiYiIpCikgfD4KICBteV9wbG90KCLlpJbjgozlgKTvvJ8iKQoKKHAxICsgcDIgKyBwMykgLyAocDQgKyBwNSArIHA2KQpgYGAKCiMjIyAzLjEuMiDjg5Ljgrnjg4jjgrDjg6njg6Djga7lvaLnirbjgajjg5Pjg7Pjga7lrprnvqkKCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCgpzZXQuc2VlZCgwKQpkYXRhX2RmIDwtIGRhdGEuZnJhbWUoZGF0YSA9IHJub3JtKDEwMCwgMCwgMSkpICMg6Kaz5ris57WQ5p6c44KS44OH44O844K/44OV44Os44O844Og44Gr5aSJ5o+bCgpteV9wbG90MiA8LSBmdW5jdGlvbihkYXRhLCB0aXRsZSwgYmlud2lkdGggPSAwLjQsIGJvdW5kYXJ5ID0gMCl7CiAgZGF0YSB8PgogICAgZ2dwbG90KGFlcyh4PWRhdGEpKSArCiAgICBnZW9tX2hpc3RvZ3JhbSgKICAgICAgYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSwgZmlsbCA9ICJibHVlIiwKICAgICAgYmlud2lkdGggPSBiaW53aWR0aCwgYm91bmRhcnkgPSBib3VuZGFyeQogICAgICApICsKICAgIHN0YXRfZnVuY3Rpb24oZnVuPWRub3JtLCBjb2xvcj0icmVkIiwgbGluZXdpZHRoID0gMSwgbGluZXR5cGU9MiwgYXJncz1saXN0KG1lYW49MCkpICsgCiAgICBsYWJzKHRpdGxlID0gdGl0bGUpICsKICAgIHRoZW1lKAogICAgICBsZWdlbmQucG9zaXRpb249Im5vbmUiLCAKICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksCiAgICAgIGFzcGVjdC5yYXRpbyA9IDEKICAgICkKfQoKcDEgPC0gZGF0YV9kZiB8PgogIG15X3Bsb3QyKCLjg5Pjg7PluYUgPSAwLjgiLCBiaW53aWR0aCA9MC44KQoKcDIgPC0gZGF0YV9kZiB8PgogIG15X3Bsb3QyKCLjg5Pjg7PluYUgPSAwLjQiLCBiaW53aWR0aCA9MC40KQoKcDMgPC0gZGF0YV9kZiB8PgogIG15X3Bsb3QyKCLjg5Pjg7PluYUgPSAwLjA4IiwgYmlud2lkdGggPTAuMDgpCgpwNCA8LSBkYXRhX2RmIHw+CiAgbXlfcGxvdDIoImJvdW5kYXJ5ID0gMCIsIGJvdW5kYXJ5ID0gMCkKCnA1IDwtIGRhdGFfZGYgfD4KICBteV9wbG90MigiYm91bmRhcnkgPSAwLjEiLCBib3VuZGFyeSA9IDAuMSkKCnA2IDwtIGRhdGFfZGYgfD4KICBteV9wbG90MigiYm91bmRhcnkgPSAwLjIiLCBiaW53aWR0aCA9MC4yKQoKKHAxICsgcDIgKyBwMykgLyAocDQgKyBwNSArIHA2KQpgYGAKCiMjIyAzLjEuMyDmraPopo/liIbluIPjgajjga7mr5TovIMKCuWFg+OBrzEwMDDlm57jgajjgarjgaPjgabjgYTjgovjgYzjgq3jg6zjgqTjgarliIbluIPjgavjgarjgonjgarjgYTjga7jgacxMOS4h+WbnuOBq+Wil+OChOOBl+OBpuOBvuOBmeOAggoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Ny41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG1jbHVzdCkKbGlicmFyeShNQVNTKQpsaWJyYXJ5KHBhdGNod29yaykKCiMg44K144Kk44Kz44Ot44KSMTAw5Zue5oyv44Gj44Gf5pmC44Gu5Ye655uu44Gu57eP5ZKM44KSMTDkuIflm57oqIjnrpcKcm9sbF9kaWNlIDwtIGZ1bmN0aW9uKG4gPSAxMDAsIHJlcGVhdF9udW0gPSAxMDAwMDApIHsKICByb3dTdW1zKAogICAgbWF0cml4KHNhbXBsZS5pbnQoNiwgc2l6ZSA9IG4qcmVwZWF0X251bSwgcmVwbGFjZSA9IFRSVUUpLCBuY29sID0gbikKICAgICkKfQoKIyDjg5Ljgrnjg4jjgrDjg6njg6Djga7kvZzmiJAKc2V0LnNlZWQoMCkKaGlzdF9kYXRhIDwtIHJvbGxfZGljZSgpCgpwMSA8LSBkYXRhLmZyYW1lKHggPSBoaXN0X2RhdGEpIHw+CiAgZ2dwbG90KGFlcyh4PXgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9YWZ0ZXJfc3RhdChkZW5zaXR5KSksIGJpbndpZHRoPTEpICsKICBzdGF0X2Z1bmN0aW9uKGZ1bj1kbm9ybSwgYXJncz1saXN0KG1lYW49bWVhbihoaXN0X2RhdGEpLCBzZD1zZChoaXN0X2RhdGEpKSwKICAgICAgICAgICAgICAgIGNvbG9yPSdyZWQnLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIGxhYnModGl0bGUgPSAi44OH44O844K/44KS5q2j6KaP5YiG5biD44Gn44OV44Kj44OD44OG44Kj44Oz44KwIiwKICAgICAgIHg9IuOCteOCpOOCs+ODreOCkjEwMOWbnuaMr+OBo+OBpuWHuuOBn+ebruOBrue3j+WSjCIsIHk9IuebuOWvvumgu+W6piIpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSA0LzUpCgojIOODlOODvOOCr+S4gOOBpOOBqOODlOODvOOCr+S6jOOBpOOBruWIhuW4gwoj5bmz5Z2HMywg5qiZ5rqW5YGP5beuMSDjgagg5bmz5Z2HNywg5qiZ5rqW5YGP5beuMSDjga7mraPopo/liIbluIPjgavlvpPjgYbkubHmlbDjgpI1MDDlgIvjgZrjgaTnlJ/miJAKc2V0LnNlZWQoMCkKZGF0YTRfYiA8LSBjKAogIHJub3JtKDUwMCwgbWVhbiA9IDMsIHNkID0gMSksIAogIHJub3JtKDUwMCwgbWVhbiA9IDcsIHNkID0gMSkKICApCgojIOa3t+WQiOOCrOOCpuOCt+OCouODs+ODouODh+ODq+OBruODkeODqeODoeODvOOCv+OCkuaOqOWumgpnbW0gPC0gTWNsdXN0KGRhdGE0X2IsIEc9MikKCiMg44OH44O844K/44Go5re35ZCI44Ks44Km44K344Ki44Oz44Oi44OH44Or44Gu57WQ5p6c44KS44OX44Ot44OD44OICnAyIDwtIGRhdGEuZnJhbWUoeCA9IGRhdGE0X2IpIHw+CiAgZ2dwbG90KGFlcyh4PXgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9YWZ0ZXJfc3RhdChkZW5zaXR5KSksIGJpbndpZHRoPTAuMiwgY29sb3VyPSJibGFjayIsIGZpbGw9ImdyZWVueWVsbG93IikgKwogIHN0YXRfZnVuY3Rpb24oCiAgICBmdW4gPSBmdW5jdGlvbih4KSBnbW0kcGFyYW1ldGVycyRwcm9bMV0gKiBkbm9ybSh4LCBnbW0kcGFyYW1ldGVycyRtZWFuWzFdLCBzcXJ0KGdtbSRwYXJhbWV0ZXJzJHZhcmlhbmNlJHNpZ21hKSksCiAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLAogICAgY29sb3IgPSAicmVkIgogICAgKSArCiAgc3RhdF9mdW5jdGlvbigKICAgIGZ1biA9IGZ1bmN0aW9uKHgpIGdtbSRwYXJhbWV0ZXJzJHByb1syXSpkbm9ybSh4LCBnbW0kcGFyYW1ldGVycyRtZWFuWzJdLCBzcXJ0KGdtbSRwYXJhbWV0ZXJzJHZhcmlhbmNlJHNpZ21hKSksCiAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLAogICAgY29sb3IgPSAiYmx1ZSIKICAgICkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICLjg4fjg7zjgr/jgYvjgoky44Gk44Gu5q2j6KaP5YiG5biD44KS5o6o5a6aIiwKICAgIHggPSBleHByZXNzaW9uKHBhc3RlKCLoprPmuKzlgKQgIiwgaXRhbGljKCJYIikpKSwKICAgIHkgPSAi55u45a++6aC75bqmIgogICAgKSArCiAgdGhlbWUoCiAgICBhc3BlY3QucmF0aW8gPSA0LzUsCiAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpCiAgICApCgojIOOCteODs+ODl+ODq+OCkueUn+aIkApzZXQuc2VlZCgwKQpsb2dub3JtX3NhbXBsZXMgPC0gcmxub3JtKDEwMDAwLCBtZWFubG9nID0gMC45NTQsIHNkbG9nID0gMC42NSkKbG9nX2xvZ25vcm1fc2FtcGxlcyA8LSBsb2cobG9nbm9ybV9zYW1wbGVzKSAjIOWvvuaVsOWkieaPmwoKIyDlubPlnYfjgajmqJnmupblgY/lt67jgpLmjqjlrpoKbG9nbm9ybV9maXQgPC0gZml0ZGlzdHIobG9nX2xvZ25vcm1fc2FtcGxlcywgZGVuc2Z1biA9ICJub3JtYWwiKQoKcDMgPC0gZGF0YS5mcmFtZSh4ID0gbG9nbm9ybV9zYW1wbGVzKSB8PgogIGdncGxvdChhZXMoeCA9IHgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBhZnRlcl9zdGF0KGRlbnNpdHkpKSwgZmlsbCA9ICJwdXJwbGUiLCBjb2xvdXI9ImJsYWNrIikgKwogIGxhYnMoeCA9ICLoprPmuKzlgKQgWCIsIHkgPSAi55u45a++6aC75bqmIikgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDQvNSkKICAKcDQgPC0gZGF0YS5mcmFtZSh4ID0gbG9nX2xvZ25vcm1fc2FtcGxlcykgfD4KICBnZ3Bsb3QoYWVzKHggPSB4KSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gYWZ0ZXJfc3RhdChkZW5zaXR5KSksIGZpbGwgPSAicHVycGxlIiwgY29sb3VyPSJibGFjayIpICsKICBsYWJzKHggPSAibG9nIFgiLCB5ID0gIuebuOWvvumgu+W6piIpICsKICBzdGF0X2Z1bmN0aW9uKGZ1bj1kbm9ybSwgY29sb3I9InJlZCIsIHNpemUgPSAxLCBsaW5ldHlwZT0yLAogICAgYXJncz1saXN0KG1lYW49bG9nbm9ybV9maXQkZXN0aW1hdGVbMV0sIHNkPWxvZ25vcm1fZml0JGVzdGltYXRlWzJdKQogICAgKSAgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDQvNSkKCihwMSArIHAyKSAvIChwMyArIHA0KQoKYGBgCgojIyMgMy4xLjQg5qeY44CF44Gq55CG6KuW5YiG5biDCgpgYGB7ciBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9Ny41LCAgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCgojIOaPj+eUu+eUqOS4gOaZgumWouaVsApteV9wbG90X2xpbmUgPC0gZnVuY3Rpb24oZGYsIGNvbG9yLCB0aXRsZSkgewogICAgZ2dwbG90KGRmLCBhZXMoeCA9IHgsIHkgPSB5KSkgKwogICAgZ2VvbV9saW5lKGNvbG9yID0gY29sb3IsIGxpbmV3aWR0aCA9IDIpICsKICAgIGxhYnModGl0bGUgPSB0aXRsZSkgKwogICAgdGhlbWUoCiAgICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCkKICAgICkKfQoKbXlfcGxvdF9wb2ludCA8LSBmdW5jdGlvbihkZiwgY29sb3IsIHRpdGxlKSB7CiAgZ2dwbG90KGRmLCBhZXMoeCA9IHgsIHkgPSB5KSkgKwogICAgZ2VvbV9wb2ludChjb2xvciA9IGNvbG9yKSArCiAgICBsYWJzKHRpdGxlID0gdGl0bGUpICsKICAgIHRoZW1lKAogICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpCiAgICApCn0KCiMg5LiA5qeY5YiG5biDCnVuaV9wIDwtIHRpYmJsZSgKICB4ID0gc2VxKC0xLCAxLCBsZW5ndGgub3V0ID0gMTAwMCksCiAgeSA9IGR1bmlmKHgsIG1pbiA9IC0xLCBtYXggPSAxKQogICkgfD4KICBteV9wbG90X2xpbmUoIm9yYW5nZSIsICLkuIDmp5jliIbluIMiKSAKCiMg5q2j6KaP5YiG5biDCm5vcm1fcCA8LSB0aWJibGUoCiAgeCA9IHNlcSgtNCwgNCwgbGVuZ3RoLm91dCA9IDEwMDApLAogIHkgPSBkbm9ybSh4KQopICB8PgogIG15X3Bsb3RfbGluZSgicmVkIiwgIuato+imj+WIhuW4gyIpIAoKIyDkuozpoIXliIbluIMKYmluX3AgPC0gdGliYmxlKAogIHggPSAwOjEwLAogIHkgPSBkYmlub20oMDoxMCwgc2l6ZT0xMCwgcHJvYj0wLjUpCiAgKSB8PgogIG15X3Bsb3RfcG9pbnQoImJsdWUiLCAi5LqM6aCF5YiG5biDIikgCgojIOODneOCouOCveODs+WIhuW4gwpwb2lfcCA8LSB0aWJibGUoCiAgeCA9IDA6OSwgCiAgeSA9IGRwb2lzKDA6OSwgbGFtYmRhID0gMykKKSB8PgogIG15X3Bsb3RfcG9pbnQoImZvcmVzdGdyZWVuIiwgIuODneOCouOCveODs+WIhuW4gyIpIAoKIyDmjIfmlbDliIbluIMKZXhwX3AgPC0gdGliYmxlKAogIHggPSBzZXEoMCwgMTAsIGxlbmd0aC5vdXQgPSAxMDAwKSwKICB5ID0gZGV4cCh4KQopICB8PgogIG15X3Bsb3RfcG9pbnQoInB1cnBsZSIsICLmjIfmlbDliIbluIMiKSAKCiMg44Ks44Oz44Oe5YiG5biDCmdhbV9wIDwtIHRpYmJsZSgKICB4ID0gc2VxKDAsIDEwLCBsZW5ndGgub3V0ID0gMTAwMCksCiAgeSA9IGRnYW1tYSh4LCA1KQopIHw+CiAgbXlfcGxvdF9wb2ludCgiYnJvd24iLCAi44Ks44Oz44Oe5YiG5biDIikgCgojIOODr+OCpOODluODq+WIhuW4gwp3ZWlfcCA8LSB0aWJibGUoCiAgeCA9IHNlcSgwLjAxLCAyLCBsZW5ndGgub3V0ID0gMTAwMCksCiAgeSA9IGR3ZWlidWxsKHgsIDEuNSkKKSAgfD4KICBteV9wbG90X3BvaW50KCJncmV5IiwgIuODr+OCpOODluODq+WIhuW4gyIpIAogIAojIOWvvuaVsOato+imj+WIhuW4gwpsbl9wIDwtIHRpYmJsZSgKICB4ID0gc2VxKDAuMDEsIDEwLCBsZW5ndGgub3V0ID0gMTAwMCksCiAgeSA9IGRsbm9ybSh4KQopICB8PgogIG15X3Bsb3RfbGluZSgicGluayIsICLlr77mlbDmraPopo/liIbluIMiKSAKCiMg44OR44Os44O844OI5YiG5biDCnBhcmV0b2Z1bmMgPC0gZnVuY3Rpb24oeCwgYWxwaGEgPSA1LCB4bSA9IDEpIGFscGhhICogKHhtXmFscGhhKSAvICh4XihhbHBoYSsxKSkKcGFyZV9wIDwtIHRpYmJsZSgKICB4ID0gc2VxKDEsIDQsIGxlbmd0aC5vdXQgPSAxMDAwKSwKICB5ID0gcGFyZXRvZnVuYyh4KQopICB8PgogIG15X3Bsb3RfbGluZSgiY3lhbiIsICLjg5Hjg6zjg7zjg4jliIbluIMiKSAKCiMg44Kw44Op44OV44KS5Lim44G544Gm6KGo56S6CnVuaV9wICsgbm9ybV9wICsgYmluX3AgKyBwb2lfcCArIGV4cF9wICsgZ2FtX3AgKyB3ZWlfcCArIGxuX3AgKyBwYXJlX3AgKwogIHBsb3RfbGF5b3V0KG5jb2wgPSAyKQpgYGAKCiMjIyAzLjEuNSDntK/nqY3liIbluIPjgafjg4fjg7zjgr/jgpLopovjgosKCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMg5q2j6KaP5YiG5biD44GL44KJMTAw54K544GoNTAw54K544Gu44K144Oz44OX44Or44KS55Sf5oiQCnNldC5zZWVkKDApCnNhbXBsZXMgPC0gcm5vcm0oNjAwKQoKcDEgPC0gdGliYmxlKAogIHggPSBzYW1wbGVzWzE6MTAwXSwKICBmaWxsID0gaWZfZWxzZSh4ID49IHF1YW50aWxlKHgsIDAuOCksICI4MCXngrnku6XkuIoiLCAiODAl54K55pyq5rqAIikKICApIHw+CiAgZ2dwbG90KGFlcyh4ID0geCwgZmlsbCA9IGZpbGwpKSArCiAgZ2VvbV9oaXN0b2dyYW0oKSArCiAgbGFicyh4ID0gIuims+a4rOWApCIsIHkgPSAi6aC75bqmIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKIApwMiA8LSB0aWJibGUoeCA9IHNhbXBsZXNbMToxMDBdKSB8PgogIGdncGxvdChhZXMoeCA9IHgpKSArCiAgc3RhdF9lY2RmKHBhZCA9IEZBTFNFLCBjb2xvciA9ICJibHVlIikgKwogIHN0YXRfZnVuY3Rpb24oZnVuPXBub3JtLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9IDIpICsKICBsYWJzKHRpdGxlID0gIuOCteODs+ODl+ODq+OCteOCpOOCuj0xMDAiLCB4ID0gIuims+a4rOWApCIsIHkgPSAi57Sv56mN55u45a++6aC75bqmIikKCnAzIDwtIHRpYmJsZSh4ID0gc2FtcGxlc1sxMDE6NTAwXSkgfD4KICBnZ3Bsb3QoYWVzKHggPSB4KSkgKwogIHN0YXRfZWNkZihwYWQgPSBGQUxTRSwgY29sb3IgPSAiYmx1ZSIpICsKICBzdGF0X2Z1bmN0aW9uKGZ1bj1wbm9ybSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAyKSArCiAgbGFicyh0aXRsZSA9ICLjgrXjg7Pjg5fjg6vjgrXjgqTjgro9NTAwIiwgeCA9ICLoprPmuKzlgKQiLCB5ID0gIue0r+epjeebuOWvvumgu+W6piIpCgoocDEgKyBwbG90X3NwYWNlcigpKSAvIChwMiArIHAzKQpgYGAKCiMjIDMuMiDnt5rjgafnibnlvrTjgpLjgajjgonjgYjjgosKCiMjIyAzLjIuMSDmmYLplpPmjqjnp7vjgpLlj6/oppbljJbjgZnjgosKCmBgYHtyIGZpZy5oZWlnaHQ9NS41LCBmaWcud2lkdGg9OCwgIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGF0Y2h3b3JrKQoKZGYgPC0gcmVhZF9jc3YoCiAgImh0dHBzOi8vd3d3LmVzcmkuY2FvLmdvLmpwL2pwL3NuYS9kYXRhL2RhdGFfbGlzdC9zb2t1aG91L2ZpbGVzLzIwMjMvcWUyMzNfMi90YWJsZXMvZ2FrdS1qZnkyMzMyLmNzdiIsCiAgY29sX25hbWVzID0gRkFMU0UsIHNraXAgPSA3LCBjb2xfdHlwZXMgPSAiY25uX19ubl9ubl9fX19fX19fX19fX19fX19fX19fX19fIgogICkgfD4KICBkcm9wX25hKCkgfD4KICBtdXRhdGUoICMg5ZCE6YeR6aGN44KS5YWG5YaG5Y2Y5L2N44Gr5aSJ5o+bCiAgICDlubTluqYgPSBzdHJfcmVtb3ZlKFgxLCAiLzQtMy4iKSB8PiBhcy5pbnRlZ2VyKCksCiAgICBg5Zu95YaF57eP55Sf55SjKOaUr+WHuuWBtClgID0gWDIgLyAxMDAwLAogICAg5rCR6ZaT5pyA57WC5raI6LK75pSv5Ye6ID0gWDMgLyAxMDAwLAogICAg5rCR6ZaT5L2P5a6FID0gWDYgLyAxMDAwLAogICAg5rCR6ZaT5LyB5qWt6Kit5YKZID0gWDcgLyAxMDAwLAogICAg5pS/5bqc5pyA57WC5raI6LK75pSv5Ye6ID0gWDkgLyAxMDAwLAogICAg5YWs55qE5Zu65a6a6LOH5pys5b2i5oiQID0gWDEwIC8gMTAwMCwKICAgIC5rZWVwID0gInVudXNlZCIKICAgICkKCnAxIDwtIGRmIHw+CiAgZ2dwbG90KGFlcyh4ID0g5bm05bqmLCB5ID0gYOWbveWGhee3j+eUn+eUoyjmlK/lh7rlgbQpYCkpICsKICBnZW9tX2xpbmUoY29sb3I9InNreWJsdWUiKSArCiAgbGFicyh0aXRsZSA9ICLmipjjgoznt5rjgrDjg6njg5UiLCB5ID0gIuWbveWGhee3j+eUn+eUo++8iOaUr+WHuuWBtO+8iSBb5YWG5YaGXSIpICsKICB0aGVtZShheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpKQoKcDIgPC0gZGYgfD4KICBnZ3Bsb3QoYWVzKHggPSDlubTluqYsIHkgPSBg5Zu95YaF57eP55Sf55SjKOaUr+WHuuWBtClgKSkgKwogIGdlb21fYXJlYShmaWxsPSJza3libHVlIikgKwogIGxhYnModGl0bGUgPSAi44Ko44Oq44Ki44OB44Oj44O844OIIiwgeCA9ICLlubTluqYiKSArCiAgdGhlbWUoYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSkKCnAzIDwtIGRmIHw+CiAgZHBseXI6OnNlbGVjdCghYOWbveWGhee3j+eUn+eUoyjmlK/lh7rlgbQpYCkgfD4KICBwaXZvdF9sb25nZXIoIeW5tOW6piwgbmFtZXNfdG8gPSAic2VjdG9yIiwgdmFsdWVzX3RvID0gInZhbCIpIHw+CiAgbXV0YXRlKAogICAgc2VjdG9yID0gZmFjdG9yKHNlY3RvciwgbGV2ZWxzID0gcmV2KGMoIuawkemWk+acgOe1gua2iOiyu+aUr+WHuiIsICLmsJHplpPkvY/lroUiLCAi5rCR6ZaT5LyB5qWt6Kit5YKZIiwgIuaUv+W6nOacgOe1gua2iOiyu+aUr+WHuiIsICLlhaznmoTlm7rlrpros4fmnKzlvaLmiJAiKSkpCiAgICApIHw+CiAgZ2dwbG90KGFlcyh4ID0g5bm05bqmLCB5ID0gdmFsLCBmaWxsID0gc2VjdG9yKSkgKwogIGdlb21fYXJlYSgpICsKICBsYWJzKHRpdGxlID0gIuepjeOBv+S4iuOBkuOCqOODquOCouODgeODo+ODvOODiCIsIHggPSAi5bm05bqmIiwgeSA9ICLlm73lhoXnt4/nlJ/nlKPvvIjmlK/lh7rlgbTvvIkgW+WFhuWGhl0iKSArCiAgdGhlbWUoCiAgICBheGlzLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC41LCAwLjI1KQogICAgKQoKcDEgKyBwMiArIHAzCmBgYAoKIyMjIDMuMi4yIOikh+aVsOOBruaZguezu+WIl+ODh+ODvOOCv+OBruaPj+eUuwoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Ny41LCAgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCm5fZGF5cyA8LSAzMCAgIyAzMOaXpemWkwpuX2luZGl2aWR1YWxzIDwtIDMwICAjIOWAi+S9k+aVsApoYWxmX24gPC0gYXMuaW50ZWdlcihuX2luZGl2aWR1YWxzIC8gMikgICMg5YCL5L2T5pWw44Gu5Y2K5YiGCgojIOWQhOWAi+S9k+OBrua0u+WLlemHj+acn+W+heWApOOCkueUn+aIkApzZXQuc2VlZCgwKQpleHBlY3RlZF92YWx1ZXMgPC0gYygKICBybm9ybShoYWxmX24sIG1lYW4gPSA1MCwgc2QgPSA1KSwjIOacn+W+heWApDUw44CB5qiZ5rqW5YGP5beuNeOBruato+imj+WIhuW4g+OBq+W+k+OBhuS5seaVsOOCkueUn+aIkAogIHJub3JtKGhhbGZfbiwgbWVhbiA9IDcwLCBzZCA9IDUpIyDmnJ/lvoXlgKQ3MOOAgeaomea6luWBj+W3rjXjga7mraPopo/liIbluIPjgavlvpPjgYbkubHmlbDjgpLnlJ/miJAKICApCgojIOWQhOWAi+S9k+OBrjMw5pel6ZaT44Gu5rS75YuV6YeP44KS55Sf5oiQCiMg5pyf5b6F5YCkZXhwZWN0ZWRfdmFsdWVz44CB5qiZ5rqW5YGP5beuMTDjga7mraPopo/liIbluIPjgavlvpPjgYbkubHmlbDjgpLnlJ/miJAKc2V0LnNlZWQoMCkKYWN0aXZpdHlfZGF0YSA8LSBwdXJycjo6bWFwKGV4cGVjdGVkX3ZhbHVlcywgXCh4KSBybm9ybShuX2RheXMsIG1lYW4gPSB4LCBzZCA9IDEwKSkgfD4KICBsaXN0X2MoKSB8PgogIG1hdHJpeChuY29sID0gMzApIHw+CiAgYXMuZGF0YS5mcmFtZSgpIHw+CiAgc2V0X25hbWVzKHBhc3RlMCgidiIsIGZvcm1hdEMoMTozMCwgd2lkdGg9MiwgZmxhZz0iMCIpKSkgfD4KICBtdXRhdGUoZGF5cyA9IDE6MzApIHw+CiAgcGl2b3RfbG9uZ2VyKCFkYXlzKSB8PgogIGdyb3VwX2J5KGRheXMpIHw+CiAgbXV0YXRlKAogICAgZXYgPSBleHBlY3RlZF92YWx1ZXMsCiAgICBncm91cCA9IGMocmVwKCJhIiwgMTUpLCByZXAoImIiLCAxNSkpCiAgICApIHw+CiAgdW5ncm91cCgpCgojIOWFqOWAi+S9k+OCkuOBneOCjOOBnuOCjOmBleOBhuiJsuOBp+aPj+eUuwpwMSA8LSBhY3Rpdml0eV9kYXRhIHw+CiAgZ2dwbG90KGFlcyh4ID0gZGF5cywgeSA9IHZhbHVlLCBjb2xvciA9IG5hbWUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAi44GZ44G544Gm44Gu5YCL5L2T44KS55Ww44Gq44KL6Imy44Gn5o+P55S7IiwgeCA9ICLntYzpgY7ml6XmlbAiLCB5ID0gIua0u+WLlemHj+OCueOCs+OCoiIpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIKICAgICwgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKQogICAgLCBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpCiAgKQoKIyDmnJ/lvoXlgKTmnIDlpKfjga7lgIvkvZPjgpLlvLfoqr8KcDIgPC0gYWN0aXZpdHlfZGF0YSB8PgogIG11dGF0ZShjb2xvciA9IGlmX2Vsc2UoZXYgPT0gbWF4KGV4cGVjdGVkX3ZhbHVlcyksICJhIiwgImIiKSkgfD4KICBnZ3Bsb3QoYWVzKHggPSBkYXlzLCB5ID0gdmFsdWUsIGNvbG9yID0gY29sb3IsIGdyb3VwPW5hbWUpKSArCiAgZ2VvbV9saW5lKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCAiZ3JleSIpKSArCiAgbGFicyh0aXRsZSA9ICLnnYDnm67jgZnjgovlgIvkvZPjgaDjgZHnlbDjgarjgovoibLjgafmj4/nlLsiLCB4ID0gIue1jOmBjuaXpeaVsCIsIHkgPSAi5rS75YuV6YeP44K544Kz44KiIikgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIgogICAgLCBheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpCiAgICAsIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCkKICApCgojIDE15YCL5L2T44Gu44Kw44Or44O844OX44GU44Go44Gr5o+P55S7CnAzIDwtIGFjdGl2aXR5X2RhdGEgfD4KICBnZ3Bsb3QoYWVzKHggPSBkYXlzLCB5ID0gdmFsdWUsIGNvbG9yID0gZ3JvdXAsIGdyb3VwPW5hbWUpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAi44Kw44Or44O844OX44GU44Go44Gr55Ww44Gq44KL6Imy44Gn5o+P55S7IiwgeCA9ICLntYzpgY7ml6XmlbAiLCB5ID0gIua0u+WLlemHj+OCueOCs+OCoiIpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIKICAgICwgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKQogICAgLCBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpCiAgKQoKIyAxNeWAi+S9k+OBruOCsOODq+ODvOODl+OBlOOBqOOBq+W5s+Wdh+OBqOaomea6luWBj+W3ruOBp+aPj+eUuwpwNCA8LSBhY3Rpdml0eV9kYXRhIHw+CiAgc3VtbWFyaXNlKAogICAgbWVhbiA9IG1lYW4odmFsdWUpLAogICAgc2QgPSBzZCh2YWx1ZSksCiAgICAuYnkgPSBjKGdyb3VwLCBkYXlzKQogICkgfD4KICBnZ3Bsb3QoYWVzKHggPSBkYXlzLCB5ID0gbWVhbiwgY29sb3IgPSBncm91cCwgZ3JvdXA9Z3JvdXApKSArCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSBtZWFuIC0gc2QsIHltYXggPSBtZWFuICsgc2QsIGZpbGwgPSBncm91cCksIGFscGhhPTAuMikgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIuOCsOODq+ODvOODl+OBlOOBqOOBq+W5s+Wdh+OBqOaomea6luWBj+W3ruOBp+aPj+eUuyIsIHggPSAi57WM6YGO5pel5pWwIiwgeSA9ICLmtLvli5Xph4/jgrnjgrPjgqIiKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb249Im5vbmUiCiAgICAsIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCkKICAgICwgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKQogICkgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygyMCwxMDApKQoKKHAxICsgcDIgKS8ocDMgKyBwNCkKCmBgYAoKIyMjIDMuMi4zIOOCueODreODvOODl+OCsOODqeODleOBp+WAi+OAheOBruWkieWMluOCkuOBqOOCieOBiOOCiwoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9Ny41LCAgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMg44Op44Oz44OA44Og44Gq5L2T6YeN44OH44O844K/44KS55Sf5oiQCnNldC5zZWVkKDEpCndlaWdodHNfMSA8LSBybm9ybSg1MCwgNjAsIDIpICMg5bmz5Z2HNjDjgIHliIbmlaM044Gu5q2j6KaP5YiG5biDCndlaWdodHNfMiA8LSB3ZWlnaHRzXzEgKyBybm9ybSg1MCwgLTEsIDAuNSkgIyDlubPlnYctMeOAgeWIhuaVozAuMjXjga7mraPopo/liIbluIMKd2VpZ2h0c18zIDwtIHdlaWdodHNfMSArIHJub3JtKDUwLCAwLCAwLjUpICMg5bmz5Z2HMOOAgeWIhuaVozAuMjXjga7mraPopo/liIbluIPvvIjov73liqDvvIkKCiMg44OH44O844K/44OV44Os44O844Og44KS5L2c5oiQCmRmIDwtIGRhdGEuZnJhbWUoCiAgV2VpZ2h0ID0gYyh3ZWlnaHRzXzEsIHdlaWdodHNfMiksCiAgR3JvdXAgPSBmYWN0b3IoCiAgICBjKHJlcCgi5a6f6aiT6ZaL5aeL5pmCIiwgNTApLCByZXAoIuS4gOOBi+aciOW+jCIsNTApKSwKICAgIGxldmVscz1jKCLlrp/pqJPplovlp4vmmYIiLCAi5LiA44GL5pyI5b6MIikKICAgICksCiAgUGVyc29uID0gYyhzZXEoNTApLCBzZXEoNTApKQogICkKICAKIyDngrnjga7jgb/jg5fjg63jg4Pjg4gKcDEgPC0gZGYgfD4KICBnZ3Bsb3QoYWVzKHggPSBHcm91cCwgeSA9IFdlaWdodCkpICsKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMDUsIGhlaWdodCA9IDAsIGFscGhhID0gMC41KSArCiAgbGFicyh4ID0gIuacn+mWkyIsIHkgPSAi5L2T6YeNIFtrZ10iKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDU0LCA2NCkpICsKICBsYWJzKHRpdGxlID0gIuOCueODiOODquODg+ODl+ODl+ODreODg+ODiCIpICsKICB0aGVtZShheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpKSAKCmRmMiA8LSBkZiB8PgogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBHcm91cCwgdmFsdWVzX2Zyb20gPSBXZWlnaHQpIHw+CiAgbXV0YXRlKGRpZmYgPSDkuIDjgYvmnIjlvowgLSDlrp/pqJPplovlp4vmmYIpIHw+CiAgcGl2b3RfbG9uZ2VyKCFjKFBlcnNvbiwgZGlmZiksIG5hbWVzX3RvID0gIkdyb3VwIiwgdmFsdWVzX3RvID0gIldlaWdodCIpIHw+CiAgbXV0YXRlKEdyb3VwID0gZmFjdG9yKEdyb3VwLCBsZXZlbHM9Yygi5a6f6aiT6ZaL5aeL5pmCIiwgIuS4gOOBi+aciOW+jCIpKSkgCgojIOOCueODreODvOODl+OCsOODqeODlQpwMiA8LSBkZjIgfD4KICBnZ3Bsb3QoYWVzKHggPSBHcm91cCwgeSA9IFdlaWdodCwgZ3JvdXAgPSBQZXJzb24sIGNvbG9yID0gZGlmZikpICsKICBnZW9tX2xpbmUoYWxwaGEgPSAwLjUpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41KSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoCiAgICBsaW1pdCA9IGMoLTIsMiksCiAgICBsb3cgPSAiYmx1ZSIsCiAgICBoaWdoID0gInJlZCIsCiAgICBndWlkZSA9ICJjb2xvcmJhciIKICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoNTQsIDY0KSkgKwogIGxhYnModGl0bGUgPSAi44K544Ot44O844OX44Kw44Op44OVIiwgY29sb3IgPSAi5beuW2tnXSIpICsKICB0aGVtZSgKICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCkKICAgICwgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKQogICkgCgojIOOCueODreODvOODl+OCsOODqeODlTIKcDMgPC0gZGF0YS5mcmFtZSgKICBXZWlnaHQgPSBjKHdlaWdodHNfMSwgd2VpZ2h0c18zKSwKICBHcm91cCA9IGZhY3RvcigKICAgIGMocmVwKCLlrp/pqJPplovlp4vmmYIiLCA1MCksIHJlcCgi5LiA44GL5pyI5b6MIiw1MCkpLAogICAgbGV2ZWxzPWMoIuWun+mok+mWi+Wni+aZgiIsICLkuIDjgYvmnIjlvowiKQogICksCiAgUGVyc29uID0gYyhzZXEoNTApLCBzZXEoNTApKQopIHw+CiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IEdyb3VwLCB2YWx1ZXNfZnJvbSA9IFdlaWdodCkgfD4KICBtdXRhdGUoZGlmZiA9IOS4gOOBi+aciOW+jCAtIOWun+mok+mWi+Wni+aZgikgfD4KICBwaXZvdF9sb25nZXIoIWMoUGVyc29uLCBkaWZmKSwgbmFtZXNfdG8gPSAiR3JvdXAiLCB2YWx1ZXNfdG8gPSAiV2VpZ2h0IikgfD4KICBtdXRhdGUoR3JvdXAgPSBmYWN0b3IoR3JvdXAsIGxldmVscz1jKCLlrp/pqJPplovlp4vmmYIiLCAi5LiA44GL5pyI5b6MIikpKSB8PgogIGdncGxvdChhZXMoeCA9IEdyb3VwLCB5ID0gV2VpZ2h0LCBncm91cCA9IFBlcnNvbiwgY29sb3IgPSBkaWZmKSkgKwogIGdlb21fbGluZShhbHBoYSA9IDAuNSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudCgKICAgIGxpbWl0ID0gYygtMiwyKSwKICAgIGxvdyA9ICJibHVlIiwKICAgIGhpZ2ggPSAicmVkIiwKICAgIGd1aWRlID0gImNvbG9yYmFyIgogICAgKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDU0LCA2NCkpICsKICBsYWJzKHRpdGxlID0gIuOCueODreODvOODl+OCsOODqeODlSIpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIKICAgICwgYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKQogICAgLCBheGlzLnRpdGxlLnk9ZWxlbWVudF9ibGFuaygpCiAgKSAKCnAxICsgcDMgKyBwMgoKYGBgCgoKCiMjIyAzLjIuNCDljLvolqzlk4Hjga7mnIjplpPosqnlo7LpoY3jgpLopoHntKDliIbop6PjgZnjgosKCmBgYHtyIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShmb3JlY2FzdCkKbGlicmFyeShwYXRjaHdvcmspCgojIOODh+ODvOOCv+OBruiqreOBv+i+vOOBvwpkZiA8LSByZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3NlbHZhODYvZGF0YXNldHMvbWFzdGVyL2ExMC5jc3YnKQoKIyDmmYLns7vliJfjg4fjg7zjgr/jgavlpInmj5sKdHNfZGYgPC0gdHMoCiAgZGYkdmFsdWUsCiAgc3RhcnQ9Yyh5ZWFyKGRmJGRhdGVbMV0pLCBtb250aChkZiRkYXRlWzFdKSksCiAgZnJlcXVlbmN5PTEyCiAgKQoKIyDmmYLns7vliJfjg4fjg7zjgr/jgpLliIbop6MKZGVjb21wb3NlZF9hZGQgPC0gZGVjb21wb3NlKHRzX2RmLCB0eXBlPSJhZGRpdGl2ZSIpICPliqDnrpcKZGVjb21wb3NlZF9tbHQgPC0gZGVjb21wb3NlKHRzX2RmLCB0eXBlPSJtdWx0aXBsaWNhdGl2ZSIpICPkuZfnrpcKCiMg44OX44Ot44OD44OI5L2c5oiQCnAxIDwtIGRlY29tcG9zZWRfYWRkIHw+CiAgYXV0b3Bsb3QoKSArCiAgbGFicyh0aXRsZSA9ICLotrPjgZfnrpfjgafliIbop6MiKQoKcDIgPC0gZGVjb21wb3NlZF9tbHQgfD4KICBhdXRvcGxvdCgpICsKICBsYWJzKHRpdGxlID0gIuaOm+OBkeeul+OBp+WIhuinoyIpCgpwMSArIHAyCmBgYAoKIyMgMy4zIDLlpInmlbDjga7plqLkv4LjgpLjgajjgonjgYjjgosKCiMjIyAzLjMuMSDjg5rjgqLjg5fjg63jg4Pjg4jjgavjgojjgovliIbluIPjga7lj6/oppbljJYKCuODmuOCouODl+ODreODg+ODiOOBr1tHR2FsbHldKGh0dHBzOi8vZ2dvYmkuZ2l0aHViLmlvL2dnYWxseS9pbmRleC5odG1sKeODkeODg+OCseODvOOCuOOBrltnZ3BhaXJzKCldKGh0dHBzOi8vZ2dvYmkuZ2l0aHViLmlvL2dnYWxseS9hcnRpY2xlcy9nZ3BhaXJzLmh0bWwp6Zai5pWw44GM5L6/5Yip44CCCgpgYGB7ciBmaWcuaGVpZ2h0PTcuNSwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpCgpwZW5ndWlucyB8PgogIGdncGFpcnMoCiAgICBjb2x1bW5zID0gYygiYmlsbF9sZW5ndGhfbW0iLCAiYmlsbF9kZXB0aF9tbSIsICJmbGlwcGVyX2xlbmd0aF9tbSIsICJib2R5X21hc3NfZyIpLAogICAgY29sdW1uTGFiZWxzID0gYygi44GP44Gh44Gw44GX44Gu6ZW344GVIFttbV0iLCAi44GP44Gh44Gw44GX44Gu5Y6a44GVIFttbV0iLCAi44Gy44KM44Gu6ZW344GVIFttbV0iLCAi5L2T6YeNIFtnXSIpLAogICAgYWVzKGNvbG9yID0gc3BlY2llcyksCiAgICBkaWFnID0gbGlzdChjb250aW51b3VzID0gd3JhcCgiZGVuc2l0eURpYWciLCBhbHBoYSA9IC41KSkKICAgICkKYGBgCgojIyMgMy4zLjIg55u46Zai44KS6KaL44KL44Gf44KB44Gu5pWj5biD5ZuzCgpgYGB7ciBmaWcuaGVpZ2h0PTUuNjI1LCBmaWcud2lkdGg9Ny41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBhbG1lcnBlbmd1aW5zKQoKcGVuZ3VpbnMgfD4KICBnZ3Bsb3QoYWVzKHggPSBiaWxsX2xlbmd0aF9tbSwgeSA9IGJvZHlfbWFzc19nLCBjb2xvciA9IHNwZWNpZXMsIGZpbGwgPSBzcGVjaWVzKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC4zKSArCiAgZ2VvbV9zbW9vdGgoZm9ybXVsYSA9IHkgfiB4LCBtZXRob2QgPSAibG0iKSArCiAgbGFicyh4ID0gIuOBj+OBoeOBsOOBl+OBrumVt+OBlSBbbW1dIiwgeSA9ICLkvZPph40gW2ddIikgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmBgYAoKIyMjIDMuMy4zIHkgPSB4IOOBqOOBruavlOi8gwoKYGBge3IgZmlnLmhlaWdodD01LjYyNSwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYWxtZXJwZW5ndWlucykKbGlicmFyeShyc2FtcGxlKQpsaWJyYXJ5KE1MbWV0cmljcykKCiMg44OH44O844K/44Gu5rqW5YKZCnBlbiA8LSBwZW5ndWlucyB8PgogIGRwbHlyOjpzZWxlY3QoYm9keV9tYXNzX2csIGJpbGxfbGVuZ3RoX21tLCBiaWxsX2RlcHRoX21tLCBmbGlwcGVyX2xlbmd0aF9tbSwgc3BlY2llcykgfD4KICBkcm9wX25hKCkKCnBlbl9saXN0IDwtIHNwbGl0KHBlbiwgcGVuJHNwZWNpZXMpCgojIOWIhuWJsgpwZW5fc3BsaXQgPC0gcGVuX2xpc3QgfD4KICBwdXJycjo6bWFwKFwoZGYpIGluaXRpYWxfc3BsaXQoZGYsIHByb3AgPSAwLjgsIHN0cmF0YSA9ICJib2R5X21hc3NfZyIpKQogIApwZW5fdHJhaW4gPC0gIHBlbl9zcGxpdCB8PgogIHB1cnJyOjptYXAoXChkZikgdHJhaW5pbmcoZGYpKQoKcGVuX3Rlc3QgPC0gIHBlbl9zcGxpdCB8PgogIHB1cnJyOjptYXAoXChkZikgdGVzdGluZyhkZikpCgojIOWtpue/kgpwZW5fbG0gPC0gcGVuX3RyYWluIHw+CiAgcHVycnI6Om1hcChcKGRmKSBsbShib2R5X21hc3NfZyB+IGJpbGxfbGVuZ3RoX21tICsgYmlsbF9kZXB0aF9tbSArIGZsaXBwZXJfbGVuZ3RoX21tLCBkYXRhID0gZGYpKQoKIyDmpJzoqLwKcGVuX3ByZWQgPC0gcGVuX2xtIHw+CiAgcHVycnI6Om1hcDIocGVuX3Rlc3QsIFwoeCwgeSkgcHJlZGljdCh4LCBuZXdkYXRhID0geSkpCgojIOipleS+oShSTVNFKQpwZW5fdHJ1ZSA8LSBwZW5fdGVzdCB8PgogIHB1cnJyOjptYXAocGx1Y2soImJvZHlfbWFzc19nIikpCnBlbl9wcmVkIHw+CiAgcHVycnI6Om1hcDJfZGJsKHBlbl90cnVlLCBcKHlfcHJlZCwgeV90cnVlKSBSTVNFKHlfcHJlZCwgeV90cnVlKSkKCiMgQWRlbGllIENoaW5zdHJhcCAgICBHZW50b28gCiMgMzM2LjU1MTIgIDMwNS4yNDUwICAzMTMuNjA1NCAKCiMg5pWj5biD5Zuz44Gu5L2c5oiQCmRhdGEuZnJhbWUoCiAgeV90cnVlID0gcGVuX3RydWUgfD4gdW5saXN0KCksCiAgeV9wcmVkID0gcGVuX3ByZWQgfD4gdW5saXN0KCkKICApIHw+CiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJzcGVjaWVzIikgfD4KICBtdXRhdGUoc3BlY2llcyA9IHN0cl9yZW1vdmUoc3BlY2llcywgIlswLTldKyIpKSB8PgogIGdncGxvdChhZXMoeCA9IHlfdHJ1ZSwgeSA9IHlfcHJlZCwgY29sb3IgPSBzcGVjaWVzKSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKwogICAgbGFicyh4ID0gIuWun+mam+OBruS9k+mHjSBbZ10iLCB5ID0gIuODouODh+ODq+OBq+OCiOOCi+S6iOa4rCBbZ10iLCB0aXRsZSA9ICLnt5rlvaLlm57luLDjgavjgojjgovkuojmuKwiKSArCiAgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMzAwMCwgNjAwMCksIHlsaW0gPSBjKDMwMDAsIDYwMDApKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKCmBgYAoKIyMjIDMuMy40IHkgPSB4IOOBqOOBruavlOi8g++8muOBneOBruS7luOBruS+iwoKYGBge3IgZmlnLmhlaWdodD0zLjc1LCBmaWcud2lkdGg9Ny41LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBhdGNod29yaykKCiMgU05T44OH44O844K/44Gu6Kqt44G/6L6844G/CmRmX3NucyA8LSByZWFkX2NzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL21vcmltb3Rvb3NhbXUvZGF0YV92aXN1YWxpemF0aW9uL21haW4vZGF0YS9zbnMuY3N2IikKCiMg5L2T6YeN44OH44O844K/44KS55Sf5oiQCmRmX3dlaWdodHMgPC0gdGliYmxlKAogIFdlaWdodDEgPSBybm9ybSg1MCwgbWVhbiA9IDYwLCBzZCA9IHNxcnQoMikpLCAjIOW5s+WdhzYw44CB5YiG5pWjMuOBruato+imj+WIhuW4gywKICBXZWlnaHQyID0gV2VpZ2h0MSArIHJub3JtKDUwLCBtZWFuID0gLTEsIHNkID0gc3FydCgwLjUpKSAjIOW5s+Wdhy0x44CB5YiG5pWjMC4144Gu5q2j6KaP5YiG5biDCikKCiMgZ2dwbG9044KS55So44GE44Gf44Kw44Op44OV5o+P55S7CnAxIDwtIGRmX3dlaWdodHMgfD4KICBnZ3Bsb3QoYWVzKHggPSBXZWlnaHQxLCB5ID0gV2VpZ2h0MikpICsgCiAgZ2VvbV9wb2ludChjb2xvciA9ICJvcmFuZ2UiKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJibGFjayIpICsKICBsYWJzKAogICAgdGl0bGUgPSAi5ZCM44GY6YeP44GuMuaZgueCueOBruavlOi8gyIsCiAgICB4ID0gIuWun+mok+mWi+Wni+aZguS9k+mHjSBba2ddIiwKICAgIHkgPSAi5LiA44GL5pyI5b6M5L2T6YeNIFtrZ10iKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDU2LCA2NCksIHlsaW0gPSBjKDU2LCA2NCkpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQoKcDIgPC0gZGZfc25zIHw+CiAgZ2dwbG90KGFlcyh4ID0gU2VudCwgeSA9IFJlY2VpdmVkKSkgKyAKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJibGFjayIpICsKICBsYWJzKAogICAgdGl0bGUgPSAi44Oa44Ki6ZaT44Gu5Y+M5pa55ZCR44Gu6YeP44Gu5q+U6LyDIiwKICAgIHggPSAi6YCB5L+h44GX44Gf44Oq44OX44Op44Kk5pWwIiwKICAgIHkgPSAi5Y+X5L+h44GX44Gf44Oq44OX44Op44Kk5pWwIgogICAgKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDMxKSwgeWxpbSA9IGMoMCwgMzEpKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkKCnAxICsgcDIKCmBgYAoKIyMjIDMuMy41IOOCteODs+ODl+ODq+OCteOCpOOCuuOBjOWkp+OBjeOBhOOCseODvOOCuQoKYGBge3IgZmlnLmhlaWdodD0yLjUsIGZpZy53aWR0aD03LjUsICBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBhdGNod29yaykKCnNldC5zZWVkKDApCgpkYXRhIDwtIGJpbmRfcm93cygKICB0aWJibGUoIyDnm7jplqLjga7lsJHjgarjgYTjg4fjg7zjgr/jga7nlJ/miJAKICAgIHggPSBybm9ybSgxMDAsIDI1LCAxMCksICMg5bmz5Z2HMjXjgIHliIbmlaMxMOOBruato+imj+WIhuW4gwogICAgeSA9IHJub3JtKDEwMCwgNzUsIDEwKSAgIyDlubPlnYc3NeOAgeWIhuaVozEw44Gu5q2j6KaP5YiG5biDCiAgICApLAogIHRpYmJsZSgjIOebuOmWouOBruWkmuOBhOODh+ODvOOCv+OBrueUn+aIkAogICAgeCA9IHJub3JtKDUwMCwgNTAsIDMwKSwgIyDlubPlnYc1MOOAgeWIhuaVozMw44Gu5q2j6KaP5YiG5biDCiAgICB5ID0gMC41ICogeCArIHJub3JtKDUwMCwgMCwgMTApICMgeSA9IDAuNXggKyDjg47jgqTjgroKICAgICksCiAgdGliYmxlKAogICAgIHggPSBybm9ybSg0MDAsIDI1LCAyMCksICMg5bmz5Z2HMjXjgIHliIbmlaMyMOOBruato+imj+WIhuW4gwogICAgIHkgPSBybm9ybSg0MDAsIDI1LCAzMCkgICMg5bmz5Z2HMjXjgIHliIbmlaMzMOOBruato+imj+WIhuW4gwogICAgICkKICApCgojIOOCsOODqeODleeUn+aIkApwMSA8LSBkYXRhIHw+CiAgZ2dwbG90KGFlcyh4ID14LCB5ID0geSkpICsKICBnZW9tX3BvaW50KCkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIGxhYnModGl0bGUgPSAi5LiN6YCP5piO44Oe44O844Kr44O85Yip55SoIikKCiMg5Lit5aSu44Gu44K144OW44OX44Ot44OD44OI44Gr57Wx5ZCI44OH44O844K/44Gu5Y2K6YCP5piO44Gq44Oe44O844Kr44O85pWj5biD5Zuz44KS44OX44Ot44OD44OICnAyIDwtIGRhdGEgfD4KICBnZ3Bsb3QoYWVzKHggPXgsIHkgPSB5KSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjEpICsKICBjb29yZF9maXhlZCgpICsKICBsYWJzKHRpdGxlID0gIuWNiumAj+aYjuODnuODvOOCq+ODvOWIqeeUqCIpCgojIOODkuODvOODiOODnuODg+ODl+OCkueUn+aIkAojamV0LmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKAojICBjKCIjMDAwMDdGIiwgImJsdWUiLCAiIzAwN0ZGRiIsICJjeWFuIiwgIiM3RkZGN0YiLCAieWVsbG93IiwgIiNGRjdGMDAiLCAicmVkIiwKIyAgICAiIzdGMDAwMCIpKQoKcDMgPC0gZGF0YSB8PgogIGdncGxvdChhZXMoeCA9IHgsIHkgPSB5KSkgKwogIHN0YXRfZGVuc2l0eTJkKGFlcyhmaWxsID0gYWZ0ZXJfc3RhdChkZW5zaXR5KSksIGdlb209InRpbGUiLCBjb250b3VyPUZBTFNFLCBuID0gMjApICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gaGNsLmNvbG9ycygxMCkpICsKICBjb29yZF9maXhlZCgpICsKICBsYWJzKHRpdGxlID0gIuODkuODvOODiOODnuODg+ODlyIpCgpwMSArIHAyICtwMwpgYGAKCiMjIyAzLjMuNiDjg5Djg5bjg6vjg4Hjg6Pjg7zjg4jjgavjgojjgovmg4XloLHmj5DnpLoKCmBgYHtyIGZpZy5oZWlnaHQ9NywgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGphbml0b3IpCgojIOODh+ODvOOCv+OBruiqreOBv+i+vOOBvwpkZiA8LSByZWFkX2NzdigKICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3RrRXpha2kvZGF0YV92aXN1YWxpemF0aW9uL21haW4vMyVFNyVBQiVBMC9kYXRhL2J1YmJsZV9jaGFydF9kYXRhLmNzdiIKICApIHw+CiAgY2xlYW5fbmFtZXMoKSB8PgogIGRwbHlyOjpmaWx0ZXIoeDIwMjFfeXIyMDIxICE9ICIuLiIpIHw+CiAgbXV0YXRlKHgyMDIxX3lyMjAyMSA9IGFzLm51bWVyaWMoeDIwMjFfeXIyMDIxKSkKCnJlZ2lvbl9kZiA8LSByZWFkX2NzdigKICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3RrRXpha2kvZGF0YV92aXN1YWxpemF0aW9uL21haW4vMyVFNyVBQiVBMC9kYXRhL2J1YmJsZV9jaGFydF9yZWdpb25fZGF0YS5jc3YiCiAgKSB8PgogIGNsZWFuX25hbWVzKCkgCgojIOODh+ODvOOCv+OBruWJjeWHpueQhgpkZl9zcGxpdCA8LSBkZiB8PgogIHNwbGl0KGRmJHNlcmllc19uYW1lKQoKbmFtZXMoZGZfc3BsaXQpIDwtIGMoImdkcCIsICJsaWZlX2V4cCIsICJwb3B1bGF0aW9uIikKCiMgR0RQLCBMaWZlIGV4cGVjdGFuY3ksIFBvcHVsYXRpb27jga7jg4fjg7zjgr/jgpLjg57jg7zjgrgKZGF0YSA8LSBkZl9zcGxpdCRnZHAgfD4KICBkcGx5cjo6c2VsZWN0KGNvdW50cnlfbmFtZSwgY291bnRyeV9jb2RlLCB4MjAyMV95cjIwMjEpIHw+CiAgbGVmdF9qb2luKAogICAgZGZfc3BsaXQkbGlmZV9leHAgfD4KICAgICAgZHBseXI6OnNlbGVjdChjb3VudHJ5X25hbWUsIHgyMDIxX3lyMjAyMSksCiAgICBieSA9IGpvaW5fYnkoY291bnRyeV9uYW1lKSwKICAgIHN1ZmZpeCA9IGMoIl9HRFAiLCAiX0xpZmVfRXhwZWN0YW5jeSIpCiAgICApIHw+CiAgbGVmdF9qb2luKAogICAgZGZfc3BsaXQkcG9wdWxhdGlvbiB8PgogICAgICBkcGx5cjo6c2VsZWN0KGNvdW50cnlfbmFtZSwgeDIwMjFfeXIyMDIxKSwKICAgIGJ5ID0gam9pbl9ieShjb3VudHJ5X25hbWUpCiAgICApIHw+CiAgcmVuYW1lKAogICAgeDIwMjFfeXIyMDIxX1BvcHVsYXRpb24gPSB4MjAyMV95cjIwMjEKICApIHw+CiAgbGVmdF9qb2luKCAjcmVnaW9u44KSQ291bnRyeSBDb2Rl44Gnam9pbgogICAgcmVnaW9uX2RmIHw+CiAgICAgIGRwbHlyOjpzZWxlY3QoYWxwaGFfMywgcmVnaW9uKSwKICAgIGJ5ID0gam9pbl9ieShjb3VudHJ5X2NvZGUgPT0gYWxwaGFfMykKICAgICkgfD4KICBsZWZ0X2pvaW4oICNyZWdpb27jgpJDb3VudHJ5X05hbWXjgadqb2luCiAgICByZWdpb25fZGYgfD4KICAgICAgZHBseXI6OnNlbGVjdChuYW1lLCByZWdpb24pLAogICAgYnkgPSBqb2luX2J5KGNvdW50cnlfbmFtZSA9PSBuYW1lKQogICkgfD4KICBtdXRhdGUoICNyZWdpb27norrlrpoKICAgIHJlZ2lvbiA9IGlmX2Vsc2UoaXMubmEocmVnaW9uLngpLCByZWdpb24ueSwgcmVnaW9uLngpLAogICAgLmtlZXAgPSAidW51c2VkIgogICAgKSB8PgogIGRyb3BfbmEoKSB8PiAjcmVnaW9u54Sh44GE6KGM44KS5YmK6ZmkCiAgbXV0YXRlKCMgR0RQIHBlciBjYXBpdGHvvIjljYPjg4njg6vvvInjgpLoqIjnrpcKICAgIEdEUF9wZXJfY2FwaXRhID0geDIwMjFfeXIyMDIxX0dEUCAvIHgyMDIxX3lyMjAyMV9Qb3B1bGF0aW9uIC8gMTAwMCwKICAgIHgyMDIxX3lyMjAyMV9Qb3B1bGF0aW9uID0geDIwMjFfeXIyMDIxX1BvcHVsYXRpb24gLyAxMDAwMDAKICApCgojIOaPj+eUuwpkYXRhIHw+CiAgZ2dwbG90KGFlcygKICAgIHggPSBHRFBfcGVyX2NhcGl0YSwKICAgIHkgPSB4MjAyMV95cjIwMjFfTGlmZV9FeHBlY3RhbmN5LAogICAgY29sb3IgPSByZWdpb24sCiAgICBzaXplID0geDIwMjFfeXIyMDIxX1BvcHVsYXRpb24sCiAgICBsYWJlbCA9IGNvdW50cnlfbmFtZQogICAgKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSAxMSkgKyAj5YCk44Go6Z2i56mN44GM5q+U5L6L44GZ44KL44KI44GG44GrCiAgZ2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAi6Imy44KE5aSn44GN44GV44Gn5oOF5aCx44KS5LuY5Yqg44GZ44KLIiwKICAgIHggPSAi5LiA5Lq644GC44Gf44KKR0RQIFsxLDAwMCRd77yI5a++5pWw6Lu477yJIiwKICAgIHkgPSAi5bmz5Z2H5a+/5ZG9IFvlubRdIiwgCiAgICBzaXplID0gIuS6uuWPo++8iDEw5LiH5Lq677yJIiwgCiAgICBjb2xvciA9ICLlnLDln58iCiAgICApICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKQpgYGAKCuesrDPnq6Djga/jgZPjgZPjgb7jgafjgIIK