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

5.1 分布と統計量

5.1.1 サンプリング実験の概要

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

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

sample_size <- 20 # サンプルサイズ

# 正規分布からのサンプリング
df_normal <- data.frame(value = rnorm(sample_size), type = "正規分布")

# 対数正規分布からのサンプリング
df_lognormal <- data.frame(value = rlnorm(sample_size), type = "対数正規分布")

# 正規分布箱ひげ図
p1 <- df_normal |>
  ggplot(aes(x = type, y = value)) +
  geom_boxplot(outlier.shape = NA) + # 外れ値非表示
  geom_dotplot(binaxis = "y", stackdir = "center", dotsize = 0.5, fill = "deeppink") +
  coord_cartesian(ylim = c(-2, 2)) +
  stat_summary(fun = "mean", geom = "point", color = "black", shape = 4, size = 4) +
  theme(
    axis.title.x = element_blank(),  # 軸のラベルを消す
    axis.line = element_blank()   # 軸の線を消す
  ) +
  labs(y = "観測値", title = "正規分布からのサンプリング")

# 正規分布理論分布
p2 <- data.frame(x = c(-2, 2)) |>
  ggplot(aes(x = x)) +
  stat_function(fun=dnorm) +
  coord_flip() +
  theme(
    axis.ticks = element_blank(),  # tickの線を消す
    axis.text = element_blank(),   # tickの数字を消す
    axis.title = element_blank(),  # 軸のラベルを消す
    axis.line = element_blank()   # 軸の線を消す
  )
# 対数正規分布箱ひげ図
p3 <- df_lognormal |>
  ggplot(aes(x = type, y = value)) +
  geom_boxplot(outlier.shape = NA) + # 外れ値非表示
  geom_dotplot(binaxis = "y", stackdir = "center", dotsize = 0.5, fill = "blue") +
  coord_cartesian(ylim = c(0, 7)) +
  stat_summary(fun = "mean", geom = "point", color = "black", shape = 4, size = 4) +
  theme(
    axis.title.x = element_blank(),  # 軸のラベルを消す
    axis.line = element_blank()   # 軸の線を消す
  ) +
  labs(y = "観測値", title = "対数正規分布からのサンプリング")

# 対数正規分布理論分布
p4 <- data.frame(x = c(0, 7)) |>
  ggplot(aes(x = x)) +
  stat_function(fun=dlnorm) +
  coord_flip() +
  theme(
    axis.ticks = element_blank(),  # tickの線を消す
    axis.text = element_blank(),   # tickの数字を消す
    axis.title = element_blank(),  # 軸のラベルを消す
    axis.line = element_blank()   # 軸の線を消す
  )

p1 + p2 + p3 + p4 +
  plot_layout(ncol = 4, widths = c(2, 1, 2, 1))

5.1.2 サンプリングごとの統計量の振る舞い

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

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

# サンプルサイズ 
n <- (20 + 100 + 500) * 100

# 組み合わせ
eg <- expand_grid(
  n = rep(c(rep("20",20), rep("100", 100), rep("500", 500))),
  sample = 1:100
  )

df <- bind_rows(
  bind_cols(data.frame(dist = "正規分布", value = rnorm(n)), eg),
  bind_cols(data.frame(dist = "対数正規分布", value = rlnorm(n)), eg)
  ) |>
  summarise(
    means = mean(value),
    medians = median(value),
    max = max(value),
    p95 = quantile(value, 0.95),
    sd = sd(value),
    iqr = IQR(value),
    .by = c(dist, n, sample)
    ) |>
  dplyr::select(!sample) |>
  pivot_longer(!c(dist, n), names_to = "metric") |>
  mutate(dist = factor(dist, levels = c("正規分布", "対数正規分布")))

df_means_medians <- df |>
  dplyr::filter(metric %in% c("means","medians")) |>
  mutate(n_metric = factor(
    paste0(metric, n), 
    levels = c("means20", "medians20", "means100", "medians100", "means500", "medians500")
    ))

df_sd_iqr <- df |>
  dplyr::filter(metric %in% c("sd","iqr")) |>
  mutate(n_metric = factor(
    paste0(metric, n), 
    levels = c("sd20", "iqr20", "sd100", "iqr100", "sd500", "iqr500")
  ))

df_max_p95 <- df |>
  dplyr::filter(metric %in% c("max","p95")) |>
  mutate(n_metric = factor(
    paste0(metric, n), 
    levels = c("max20", "p9520", "max100", "p95100", "max500", "p95500")
  ))

p1 <- df_means_medians |>
  ggplot(aes(x = n_metric, y = value, color = n)) +
  geom_quasirandom(width=0.4, method = "quasirandom", size = 0.75)  +
  theme(axis.text.x = element_text(angle = 0, hjust = 1)) +
  facet_wrap(vars(dist), scales = "free") +
  theme(
    legend.position="none",
    axis.title = element_blank()  # 軸のラベルを消す
  ) +
  scale_x_discrete(
    labels = c(
      means20 =    "N = 20\n平均",
      medians20 =  "N = 20\n中央値",      
      means100 =   "N = 100\n平均",
      medians100 = "N = 100\n中央値",
      means500 =   "N = 500\n平均",
      medians500 = "N = 500\n中央値"
      )
    )

p2 <- df_sd_iqr |>
  ggplot(aes(x = n_metric, y = value, color = n)) +
  geom_quasirandom(width=0.4, method = "quasirandom", size = 0.75)  + 
  theme(axis.text.x = element_text(angle = 0, hjust = 1)) +
  facet_wrap(vars(dist), scales = "free") +
  theme(
    legend.position="none",
    axis.title = element_blank()  # 軸のラベルを消す
  ) +
  scale_x_discrete(
    labels = c(
      sd20 =   "N = 20\n標準偏差",
      iqr20 =  "N = 20\nIQR",
      sd100 =  "N = 100\n標準偏差",
      iqr100 = "N = 100\nIQR",
      sd500 =  "N = 500\n標準偏差",
      iqr500 = "N = 500\nIQR"
    )
  )

p3 <- df_max_p95 |>
  ggplot(aes(x = n_metric, y = value, color = n)) +
  geom_quasirandom(width=0.4, method = "quasirandom", size = 0.75)  + 
  theme(axis.text.x = element_text(angle = 0, hjust = 1)) +
  facet_wrap(vars(dist), scales = "free") +
  theme(
    legend.position="none",
    axis.title = element_blank()  # 軸のラベルを消す
    ) +
  scale_x_discrete(
    labels = c(
      max20 =  "N = 20\n最大値",
      p9520 =  "N = 20\n95%点",
      max100 = "N = 100\n最大値",
      p95100 = "N = 100\n95%点",
      max500 = "N = 500\n最大値",
      p95500 = "N = 500\n95%点"
    )
  )

p1 / p2 / p3

5.1.3 統計指標と分布の形状

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

# 乱数のシード設定
set.seed(42)

# 単峰性の分布
single_peak_distribution <- rnorm(1000, mean=0, sd=1)

# 二峰性の分布
bimodal_distribution <- c(rnorm(500, mean=-1, sd=0.2), rnorm(500, mean=1, sd=0.2))

# 単峰性の分布の平均値と標準偏差
single_peak_mean <- mean(single_peak_distribution)
single_peak_std <- sd(single_peak_distribution)

# 二峰性の分布の平均値と標準偏差
bimodal_mean <- mean(bimodal_distribution)
bimodal_std <- sd(bimodal_distribution)

# 単峰性の分布のヒストグラム
p1 <- data.frame(x=single_peak_distribution) |>
  ggplot(aes(x=x)) +
  geom_histogram(color="gold", fill="gold", binwidth=0.2) +
  labs(x="観測値", y="頻度") +
  labs(
    title = paste(
      "単峰性の分布\n(平均=", round(single_peak_mean,2),
      ", 標準偏差=", round(single_peak_std, 2),
      ")"
      )) +
  theme(aspect.ratio = 3/4)

# 二峰性の分布のヒストグラム
p2 <- ggplot(data.frame(x=bimodal_distribution), aes(x=x)) +
  geom_histogram(color="blue", fill="blue", binwidth=0.2) +
  labs(x="観測値", y="頻度") +
  labs(
    title = paste(
      "二峰性の分布\n(平均=", round(bimodal_mean,2),
      ", 標準偏差=", round(bimodal_std, 2),
      ")"
    )) +
  theme(aspect.ratio = 3/4)

p1 + p2

5.2 ばらつきをとらえる

5.2.2 ワインデータのZスコア化

library(conflicted)
library(tidyverse)
library(ggbeeswarm)

# Wineデータセットのロード
col_names <- c(
    "ブドウの品種", "アルコール度数", "リンゴ酸", "ミネラル分", 
    "ミネラル分のアルカリ度", "マグネシウム", "全フェノール類", "フラバノイド",
    "非フラバノイドフェノール類", "プロアントシアニン", "色の強さ", "色相",
    "OD280/OD315値", "プロリン"
  )

original_data <- read_csv(
  "https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data",
  col_names = col_names,
  col_types = "f"
)

# Zスコア化したデータフレームの作成
zscored_data <- original_data |>
  mutate(across(where(is.numeric), \(x) scale(x))) #標準化

# オリジナルのスウォームプロット
p1 <- original_data |>
  pivot_longer(!c(ブドウの品種)) |>
  mutate(name = factor(name, levels = col_names)) |>
  ggplot(aes(x = name, y = value, color = ブドウの品種)) +
  geom_quasirandom(width=0.4, method = "quasirandom", size = 0.75)  + 
  theme(
    axis.text.x = element_text(angle = 20, hjust = 1),
    legend.position = c(0.1, 0.8),
    axis.title.x = element_blank()
    ) +
  scale_color_hue(name = "ブドウ品種", labels = c("1" = "品種1", "2" ="品種2", "3" ="品種3")) +
  labs(title = "変数ごとに取る値の範囲が異なる", y = "元の値")

# zスコア化したスウォームプロット
p2 <- zscored_data |>
  pivot_longer(!c(ブドウの品種)) |>
  mutate(name = factor(name, levels = col_names)) |>
  ggplot(aes(x = name, y = value, color = ブドウの品種)) +
  geom_quasirandom(width=0.4, method = "quasirandom", size = 0.75)  + 
  theme(
    axis.text.x = element_text(angle = 20, hjust = 1),
    legend.position = "none"
    ) +
  labs(title = "値の範囲が揃っている", x = "", y = "Zスコア")

p1 / p2

5.2.4 絶対偏差指標の振る舞い

library(conflicted)
library(tidyverse)

# サンプルサイズ 
n <- (20 + 100 + 500) * 100

# 組み合わせ
eg <- expand_grid(
  n = rep(c(rep("20",20), rep("100", 100), rep("500", 500))),
  sample = 1:100
)

set.seed(0)
df <- bind_rows(
  bind_cols(data.frame(dist = "正規分布からサンプリング", value = rnorm(n)), eg),
  bind_cols(data.frame(dist = "対数正規分布からサンプリング", value = rlnorm(n)), eg)
  ) |>
  summarise(
    SD = sd(value),# 標準偏差
    MeanAD = mad(value, center = mean(value)),  # 平均絶対偏差
    MedianAD = mad(value, center = median(value)), # 中央絶対偏差
    .by = c(dist, n, sample)
  ) |>
  dplyr::select(!sample) |>
  pivot_longer(!c(dist, n), names_to = "metric") |>
  mutate(dist = factor(dist, levels = c("正規分布からサンプリング", "対数正規分布からサンプリング")))

df |>
  mutate(n_metric = factor(
    paste0(metric, n), 
    levels = c("SD20", "SD100", "SD500",
               "MeanAD20", "MeanAD100", "MeanAD500",
               "MedianAD20", "MedianAD100", "MedianAD500"))) |>
  ggplot(aes(x = n_metric, y = value, color = metric)) +
  geom_quasirandom(width=0.4, method = "quasirandom", size = 0.75)  + 
  theme(axis.text.x = element_text(angle = 0, hjust = 1)) +
  facet_wrap(vars(dist), scales = "free", nrow = 2) +
  theme(
    legend.position="none",
    axis.title = element_blank()  # 軸のラベルを消す
  ) +
  scale_x_discrete(
    labels = c(
      SD20        = "N = 20\n標準偏差",
      SD100       = "N = 100\n標準偏差",
      SD500       = "N = 500\n標準偏差",
      MeanAD20    = "N = 20\n平均絶対偏差",
      MeanAD100   = "N = 100\n平均絶対偏差",
      MeanAD500   = "N = 500\n平均絶対偏差",
      MedianAD20  = "N = 20\n中央絶対偏差",
      MedianAD100 = "N = 100\n中央絶対偏差",
      MedianAD500 = "N = 500\n中央絶対偏差"
      )
  )

5.3 分布の形をとらえる

5.3.1 歪度と尖度

library(conflicted)
library(tidyverse)
library(e1071)
library(sn) #rsn()
library(VGAM) #rlaplace
library(patchwork)

set.seed(42)
normal_dist <- rnorm(1000, mean = 0, sd = 1)# 正規分布
skewed_dist <- rsn(1000, xi = 0, omega = 2, alpha=4)# 歪正規分布
laplace_dist <- rlaplace(1000, location = 0, scale = 1)# ラプラス分布(歪度=0, 尖度>3)

# ヒストグラムの描画
p1 <- ggplot() +
  geom_histogram(aes(x = normal_dist), fill = "#8DA0CB") +
  labs(title = "分布1(正規分布)", x = "", y = "")

p2 <- ggplot() +
  geom_histogram(aes(x = skewed_dist), fill = "#66C2A5") +
  labs(title = "分布2(歪正規分布)", x = "", y = "")

p3 <- ggplot() +
  geom_histogram(aes(x = laplace_dist), fill = "#FC8D62") +
  labs(title = "分布3(ラプラス分布)", x = "", y = "")

# 統計量の計算
median_skewness <- function(x) {
  (mean(x) - median(x)) / sd(x)
  }

quartile_skewness <- function(x) {
  q3 <- quantile(x, 0.75)
  q1 <- quantile(x, 0.25)
  as.numeric((q3 + q1 - 2 * median(x)) / (q3 - q1))
}

dists <- list(normal_dist, skewed_dist, laplace_dist)

fills <- c("#8DA0CB", "#66C2A5", "#FC8D62")

p4 <- data.frame(
  name = paste0("分布", 1:3),
  value = map_dbl(dists, skewness)
) |>
  ggplot(aes(x = name, y = value)) +
  geom_col(fill = fills) +
  labs(title = "歪度", x = "", y = "")

p5 <- data.frame(
  name = paste0("分布", 1:3),
  value = map_dbl(dists, median_skewness)
) |>
  ggplot(aes(x = name, y = value)) +
  geom_col(fill = fills) +
  labs(title = "中央値歪度", x = "", y = "")

p6 <- data.frame(
  name = paste0("分布", 1:3),
  value = map_dbl(dists, quartile_skewness)
) |>
  ggplot(aes(x = name, y = value)) +
  geom_col(fill = fills) +
  labs(title = "四分位歪度", x = "", y = "")

p7 <- data.frame(
  name = paste0("分布", 1:3),
  value = map_dbl(dists, kurtosis)
) |>
  ggplot(aes(x = name, y = value)) +
  geom_col(fill = fills) +
  labs(title = "尖度", x = "", y = "")

# グラフの表示
{p1 | p2 | p3} / { p4 | p5 | p6 | p7 }

#(p1 + p2 + p3 + plot_layout(ncol = 3)) /
#  (p4 + p5+ p6 + p7 + plot_layout(ncol = 4))

5.3.2 ヒストグラムに対して情報エントロピー指標を計算する

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

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

# サンプルサイズ
n_samples <- 500

# ビンの設定
bins <- seq(-5, 5, length.out = 40)

# 一様分布
uniform_samples <- runif(n_samples, min = -5, max = 5)
uniform_hist <- hist(uniform_samples, breaks = bins, plot = FALSE)
uniform_entropy <- entropy.empirical(uniform_hist$counts)

# 正規分布
normal_samples <- rnorm(n_samples, mean = 0, sd = 1)
normal_hist <- hist(normal_samples, breaks = bins, plot = FALSE)
normal_entropy <- entropy.empirical(normal_hist$counts)

# 2つの正規分布
gmm_samples <- c(
  rnorm(n_samples / 2, mean = -2, sd = 0.25),
  rnorm(n_samples / 2, mean = 2, sd = 0.25)
  )
gmm_hist <- hist(gmm_samples, breaks = bins, plot = FALSE)
gmm_entropy <- entropy.empirical(gmm_hist$counts)

# ヒストグラムの描画
p1 <- ggplot() +
  geom_histogram(aes(x = uniform_samples), bins = length(bins) - 1, fill = "skyblue", color = "black") +
  labs(title = paste0("一様分布\nエントロピー: ", round(uniform_entropy, 2))) +
  theme(aspect.ratio = 1, axis.title = element_blank())
p2 <- ggplot() +
  geom_histogram(aes(x = normal_samples), bins = length(bins) - 1, fill = "salmon", color = "black") +
  labs(title = paste0("正規分布\nエントロピー: ", round(normal_entropy, 2))) +
  theme(aspect.ratio = 1, axis.title = element_blank())
p3 <- ggplot() +
  geom_histogram(aes(x = gmm_samples), bins = length(bins) - 1, fill = "lightgreen", color = "black") +
  labs(title = paste0("2つの正規分布の混合\nエントロピー: ", round(gmm_entropy, 2))) +
  theme(aspect.ratio = 1, axis.title = element_blank())

# グラフの表示
p1 + p2 + p3

5.3.3 ビンの幅とエントロピー

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

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

# サンプルサイズ
n_samples <- 50

small_bins <- seq(-5, 5, length.out = 400)# 小さなビン(400分割)
large_bins <- seq(-5, 5, length.out = 5)# 大きなビン(5分割)

# 一様分布
uniform_samples <- runif(n_samples, min = -5, max = 5)
uniform_entropy_small <- entropy.empirical(hist(uniform_samples, breaks = small_bins, plot = FALSE)$counts)
uniform_entropy_large <- entropy.empirical(hist(uniform_samples, breaks = large_bins, plot = FALSE)$counts)

# 正規分布
normal_samples <- rnorm(n_samples, mean = 0, sd = 1)
normal_entropy_small <- entropy.empirical(hist(normal_samples, breaks = small_bins, plot = FALSE)$counts)
normal_entropy_large <- entropy.empirical(hist(normal_samples, breaks = large_bins, plot = FALSE)$counts)

# 2つの正規分布
gmm_samples <- c(rnorm(n_samples / 2, mean = -2, sd = 0.25), rnorm(n_samples / 2, mean = 2, sd = 0.25))
gmm_entropy_small <- entropy.empirical(hist(gmm_samples, breaks = small_bins, plot = FALSE)$counts)
gmm_entropy_large <- entropy.empirical(hist(gmm_samples, breaks = large_bins, plot = FALSE)$counts)

my_plot <- function(samples, bins, entropy, fill_color, title) {
  ggplot() +
    geom_histogram(aes(x = samples), bins = length(bins) - 1, fill = fill_color) +
    labs(title = paste0(title, round(entropy, 2))) +
    theme(axis.title = element_blank(), aspect.ratio = 1) +
    coord_cartesian(xlim = c(-5, 5))
}

p1 <- my_plot(uniform_samples, small_bins, uniform_entropy_small, "skyblue", "一様分布\nエントロピー: ")
p2 <- my_plot(normal_samples, small_bins, normal_entropy_small, "salmon", "正規分布\nエントロピー: ")
p3 <- my_plot(gmm_samples, small_bins, gmm_entropy_small, "lightgreen", "2つの正規分布の混合\nエントロピー: ")
p4 <- my_plot(uniform_samples, large_bins, uniform_entropy_large, "skyblue", "一様分布\nエントロピー: ")
p5 <- my_plot(normal_samples, large_bins, normal_entropy_large, "salmon", "正規分布\nエントロピー: ")
p6 <- my_plot(gmm_samples, large_bins, gmm_entropy_large, "lightgreen", "2つの正規分布の混合\nエントロピー: ")

# グラフの表示
(p1 + p2 + p3) / (p4 + p5 + p6)

5.3.4 ジニ係数の計算

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

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

# サンプルサイズ
n_samples <- 100

# 正規分布からのサンプリング(平均400万、標準偏差20万)
normal_samples <- rnorm(n_samples, mean = 400, sd = 20)

# 平均400万の対数正規分布からのサンプリング
mean_lognormal <- 400
sigma_lognormal <- 0.5  # 任意の標準偏差
lognormal_samples <- rlnorm(
  n_samples,
  meanlog = log(mean_lognormal) - 0.5 * sigma_lognormal^2,
  sdlog = sigma_lognormal
  )

# ローレンツ曲線とジニ係数の計算を行う関数
calculate_lorenz_curve <- function(samples) {
  sorted_samples <- sort(samples)  # サンプルを昇順に並び替える
  cum_frequencies <- cumsum(sorted_samples) / sum(sorted_samples)  # 累積相対度数
  cum_population <- seq(1, n_samples) / n_samples  # 累積人口の割合
  gini <- mean(cum_population - cum_frequencies) * 2  # ジニ係数
  return(list(cum_population = cum_population, cum_frequencies = cum_frequencies, gini = gini))
}

# 正規分布のローレンツ曲線とジニ係数
normal_lorenz <- calculate_lorenz_curve(normal_samples)

# 対数正規分布のローレンツ曲線とジニ係数
lognormal_lorenz <- calculate_lorenz_curve(lognormal_samples)

# ヒストグラムの描画
p1 <- ggplot() +
  geom_histogram(aes(x = normal_samples), bins = 20, fill = "skyblue") +
  labs(title = paste0("年収分布その1\nジニ係数: ", round(normal_lorenz$gini, 3)),
       x = "年収(万円)", y = "頻度")

p2 <- ggplot() +
  geom_histogram(aes(x = lognormal_samples), bins = 20, fill = "salmon") +
  labs(title = paste0("年収分布その2\nジニ係数: ", round(lognormal_lorenz$gini, 3)),
       x = "年収(万円)", y = "頻度")

# ローレンツ曲線の描画
df <- data.frame(x1 =c(0, 1), y1 = c(0, 0), x2 = c(1, 1), y2 = c(0, 1))
p3 <- ggplot() +
  geom_line(aes(x = normal_lorenz$cum_population, y = normal_lorenz$cum_frequencies), color = "blue") +
  geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "red") +
  geom_segment(aes(x = x1, y = y1, xend = x2, yend = y2), data = df, linetype="dotted", color="forestgreen", linewidth = 1) +
  labs(title = paste0("ローレンツ曲線(年収分布その1)\nジニ係数: ", round(normal_lorenz$gini, 3)),
       x = "累積人数の割合", y = "累積年収の割合")
p4 <- ggplot() +
  geom_line(aes(x = lognormal_lorenz$cum_population, y = lognormal_lorenz$cum_frequencies), color = "blue") +
  geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "red") +
  geom_segment(aes(x = x1, y = y1, xend = x2, yend = y2), data = df, linetype="dotted", color="forestgreen", linewidth = 1) +
  labs(title = paste0("ローレンツ曲線(年収分布その2)\nジニ係数: ", round(lognormal_lorenz$gini, 3)),
       x = "累積人数の割合", y = "累積年収の割合")

(p1 + p2) / (p3 + p4)

5.3.5 タイル指数を計算する

library(conflicted)
library(tidyverse)

# シードの固定
set.seed(0)

# パラメータ設定
n <- 10
mean_income <- 400  # 平均収入
mean_log <- log(mean_income)  # 対数正規分布の平均(ログスケール)
std_dev_log <- 0.5  # 対数正規分布の標準偏差(ログスケール)

# 対数正規分布から収入をサンプリング
unequal_incomes <- rlnorm(n, meanlog = mean_log, sdlog = std_dev_log)

# 完全均一な収入を作成
mean_income <- mean(unequal_incomes)
equal_incomes <- rep(mean_income, n)

# タイル指数(Theil Index)を計算する関数
calc_theil_index <- function(incomes) {
  mean_income <- mean(incomes)
  return(sum((incomes / mean_income) * log(incomes / mean_income)) / n)
}

# タイル指数を計算
unequal_theil <- calc_theil_index(unequal_incomes)  # 不均一な収入のケース
equal_theil <- calc_theil_index(equal_incomes)  # 均一な収入のケース

# プロット
df <- data.frame(
  Person = rep(LETTERS[1:n], 2),
  Income = c(unequal_incomes, equal_incomes),
  Distribution = factor(rep(c("Unequal", "Equal"), each = n), levels = c("Unequal", "Equal"))
)

df |>
  ggplot(aes(x = Person, y = Income, fill = Distribution)) +
  geom_col() +
  scale_fill_manual(values = c("skyblue", "salmon")) +
  facet_wrap(
    vars(Distribution),
    labeller = as_labeller(c(
      Unequal = paste0("ばらついた収入分布 (タイル指数: ", format(round(unequal_theil, 4), nsmall = 4), ")"),
      Equal = paste0("一様な収入分布 (タイル指数: ", format(round(equal_theil, 4), nsmall = 4), ")")))
    ) +
  labs(x = "", y = "収入 [万円]") +
  theme(legend.position = "none")

第5章はここまで。

LS0tCnRpdGxlOiAi56ysNeeroCDjg4fjg7zjgr/jga7liIbluIPjgpLjgajjgonjgYjjgovmjIfmqJnljJYiCmF1dGhvcjogIk9zYW11LCBNT1JJTU9UTyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0aGVtZTogdW5pdGVkICAgIAogICAgbWRfZXh0ZW5zaW9uczogIi1hc2NpaV9pZGVudGlmaWVycyIKICAgIHRvY19mbG9hdDogeWVzCiAgICBmaWdfd2lkdGg6IDcuNQogICAgZmlnX2hlaWdodDogNS42MjUKICAgIGRldjogcmFnZ19wbmcKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgrlm7Pjga7lj7PkuIrjga5gc2hvd2Djg5zjgr/jg7PjgpLmirzjgZnjgahS44Gu44Kz44O844OJ44GM6KGo56S644GV44KM44G+44GZ44CCCgojIyA1LjEg5YiG5biD44Go57Wx6KiI6YePCgojIyMgNS4xLjEg44K144Oz44OX44Oq44Oz44Kw5a6f6aiT44Gu5qaC6KaBCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBhdGNod29yaykKCnNldC5zZWVkKDQyKSAjIOOCt+ODvOODieWApOOCkuWbuuWumgoKc2FtcGxlX3NpemUgPC0gMjAgIyDjgrXjg7Pjg5fjg6vjgrXjgqTjgroKCiMg5q2j6KaP5YiG5biD44GL44KJ44Gu44K144Oz44OX44Oq44Oz44KwCmRmX25vcm1hbCA8LSBkYXRhLmZyYW1lKHZhbHVlID0gcm5vcm0oc2FtcGxlX3NpemUpLCB0eXBlID0gIuato+imj+WIhuW4gyIpCgojIOWvvuaVsOato+imj+WIhuW4g+OBi+OCieOBruOCteODs+ODl+ODquODs+OCsApkZl9sb2dub3JtYWwgPC0gZGF0YS5mcmFtZSh2YWx1ZSA9IHJsbm9ybShzYW1wbGVfc2l6ZSksIHR5cGUgPSAi5a++5pWw5q2j6KaP5YiG5biDIikKCiMg5q2j6KaP5YiG5biD566x44Gy44GS5ZuzCnAxIDwtIGRmX25vcm1hbCB8PgogIGdncGxvdChhZXMoeCA9IHR5cGUsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArICMg5aSW44KM5YCk6Z2e6KGo56S6CiAgZ2VvbV9kb3RwbG90KGJpbmF4aXMgPSAieSIsIHN0YWNrZGlyID0gImNlbnRlciIsIGRvdHNpemUgPSAwLjUsIGZpbGwgPSAiZGVlcHBpbmsiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC0yLCAyKSkgKwogIHN0YXRfc3VtbWFyeShmdW4gPSAibWVhbiIsIGdlb20gPSAicG9pbnQiLCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gNCwgc2l6ZSA9IDQpICsKICB0aGVtZSgKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgICMg6Lu444Gu44Op44OZ44Or44KS5raI44GZCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCkgICAjIOi7uOOBrue3muOCkua2iOOBmQogICkgKwogIGxhYnMoeSA9ICLoprPmuKzlgKQiLCB0aXRsZSA9ICLmraPopo/liIbluIPjgYvjgonjga7jgrXjg7Pjg5fjg6rjg7PjgrAiKQoKIyDmraPopo/liIbluIPnkIboq5bliIbluIMKcDIgPC0gZGF0YS5mcmFtZSh4ID0gYygtMiwgMikpIHw+CiAgZ2dwbG90KGFlcyh4ID0geCkpICsKICBzdGF0X2Z1bmN0aW9uKGZ1bj1kbm9ybSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWUoCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLCAgIyB0aWNr44Gu57ea44KS5raI44GZCiAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksICAgIyB0aWNr44Gu5pWw5a2X44KS5raI44GZCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCAgIyDou7jjga7jg6njg5njg6vjgpLmtojjgZkKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSAgICMg6Lu444Gu57ea44KS5raI44GZCiAgKQojIOWvvuaVsOato+imj+WIhuW4g+euseOBsuOBkuWbswpwMyA8LSBkZl9sb2dub3JtYWwgfD4KICBnZ3Bsb3QoYWVzKHggPSB0eXBlLCB5ID0gdmFsdWUpKSArCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSkgKyAjIOWkluOCjOWApOmdnuihqOekugogIGdlb21fZG90cGxvdChiaW5heGlzID0gInkiLCBzdGFja2RpciA9ICJjZW50ZXIiLCBkb3RzaXplID0gMC41LCBmaWxsID0gImJsdWUiKSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDcpKSArCiAgc3RhdF9zdW1tYXJ5KGZ1biA9ICJtZWFuIiwgZ2VvbSA9ICJwb2ludCIsIGNvbG9yID0gImJsYWNrIiwgc2hhcGUgPSA0LCBzaXplID0gNCkgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLCAgIyDou7jjga7jg6njg5njg6vjgpLmtojjgZkKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSAgICMg6Lu444Gu57ea44KS5raI44GZCiAgKSArCiAgbGFicyh5ID0gIuims+a4rOWApCIsIHRpdGxlID0gIuWvvuaVsOato+imj+WIhuW4g+OBi+OCieOBruOCteODs+ODl+ODquODs+OCsCIpCgojIOWvvuaVsOato+imj+WIhuW4g+eQhuirluWIhuW4gwpwNCA8LSBkYXRhLmZyYW1lKHggPSBjKDAsIDcpKSB8PgogIGdncGxvdChhZXMoeCA9IHgpKSArCiAgc3RhdF9mdW5jdGlvbihmdW49ZGxub3JtKSArCiAgY29vcmRfZmxpcCgpICsKICB0aGVtZSgKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksICAjIHRpY2vjga7nt5rjgpLmtojjgZkKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgICAjIHRpY2vjga7mlbDlrZfjgpLmtojjgZkKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAjIOi7uOOBruODqeODmeODq+OCkua2iOOBmQogICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpICAgIyDou7jjga7nt5rjgpLmtojjgZkKICApCgpwMSArIHAyICsgcDMgKyBwNCArCiAgcGxvdF9sYXlvdXQobmNvbCA9IDQsIHdpZHRocyA9IGMoMiwgMSwgMiwgMSkpCmBgYAoKCgoKIyMjIDUuMS4yIOOCteODs+ODl+ODquODs+OCsOOBlOOBqOOBrue1seioiOmHj+OBruaMr+OCi+iInuOBhAoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTcuNX0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ2JlZXN3YXJtKQpsaWJyYXJ5KHBhdGNod29yaykKCiMg44K344O844OJ5YCk44KS5Zu65a6aCnNldC5zZWVkKDQyKQoKIyDjgrXjg7Pjg5fjg6vjgrXjgqTjgrrjgIAKbiA8LSAoMjAgKyAxMDAgKyA1MDApICogMTAwCgojIOe1hOOBv+WQiOOCj+OBmwplZyA8LSBleHBhbmRfZ3JpZCgKICBuID0gcmVwKGMocmVwKCIyMCIsMjApLCByZXAoIjEwMCIsIDEwMCksIHJlcCgiNTAwIiwgNTAwKSkpLAogIHNhbXBsZSA9IDE6MTAwCiAgKQoKZGYgPC0gYmluZF9yb3dzKAogIGJpbmRfY29scyhkYXRhLmZyYW1lKGRpc3QgPSAi5q2j6KaP5YiG5biDIiwgdmFsdWUgPSBybm9ybShuKSksIGVnKSwKICBiaW5kX2NvbHMoZGF0YS5mcmFtZShkaXN0ID0gIuWvvuaVsOato+imj+WIhuW4gyIsIHZhbHVlID0gcmxub3JtKG4pKSwgZWcpCiAgKSB8PgogIHN1bW1hcmlzZSgKICAgIG1lYW5zID0gbWVhbih2YWx1ZSksCiAgICBtZWRpYW5zID0gbWVkaWFuKHZhbHVlKSwKICAgIG1heCA9IG1heCh2YWx1ZSksCiAgICBwOTUgPSBxdWFudGlsZSh2YWx1ZSwgMC45NSksCiAgICBzZCA9IHNkKHZhbHVlKSwKICAgIGlxciA9IElRUih2YWx1ZSksCiAgICAuYnkgPSBjKGRpc3QsIG4sIHNhbXBsZSkKICAgICkgfD4KICBkcGx5cjo6c2VsZWN0KCFzYW1wbGUpIHw+CiAgcGl2b3RfbG9uZ2VyKCFjKGRpc3QsIG4pLCBuYW1lc190byA9ICJtZXRyaWMiKSB8PgogIG11dGF0ZShkaXN0ID0gZmFjdG9yKGRpc3QsIGxldmVscyA9IGMoIuato+imj+WIhuW4gyIsICLlr77mlbDmraPopo/liIbluIMiKSkpCgpkZl9tZWFuc19tZWRpYW5zIDwtIGRmIHw+CiAgZHBseXI6OmZpbHRlcihtZXRyaWMgJWluJSBjKCJtZWFucyIsIm1lZGlhbnMiKSkgfD4KICBtdXRhdGUobl9tZXRyaWMgPSBmYWN0b3IoCiAgICBwYXN0ZTAobWV0cmljLCBuKSwgCiAgICBsZXZlbHMgPSBjKCJtZWFuczIwIiwgIm1lZGlhbnMyMCIsICJtZWFuczEwMCIsICJtZWRpYW5zMTAwIiwgIm1lYW5zNTAwIiwgIm1lZGlhbnM1MDAiKQogICAgKSkKCmRmX3NkX2lxciA8LSBkZiB8PgogIGRwbHlyOjpmaWx0ZXIobWV0cmljICVpbiUgYygic2QiLCJpcXIiKSkgfD4KICBtdXRhdGUobl9tZXRyaWMgPSBmYWN0b3IoCiAgICBwYXN0ZTAobWV0cmljLCBuKSwgCiAgICBsZXZlbHMgPSBjKCJzZDIwIiwgImlxcjIwIiwgInNkMTAwIiwgImlxcjEwMCIsICJzZDUwMCIsICJpcXI1MDAiKQogICkpCgpkZl9tYXhfcDk1IDwtIGRmIHw+CiAgZHBseXI6OmZpbHRlcihtZXRyaWMgJWluJSBjKCJtYXgiLCJwOTUiKSkgfD4KICBtdXRhdGUobl9tZXRyaWMgPSBmYWN0b3IoCiAgICBwYXN0ZTAobWV0cmljLCBuKSwgCiAgICBsZXZlbHMgPSBjKCJtYXgyMCIsICJwOTUyMCIsICJtYXgxMDAiLCAicDk1MTAwIiwgIm1heDUwMCIsICJwOTU1MDAiKQogICkpCgpwMSA8LSBkZl9tZWFuc19tZWRpYW5zIHw+CiAgZ2dwbG90KGFlcyh4ID0gbl9tZXRyaWMsIHkgPSB2YWx1ZSwgY29sb3IgPSBuKSkgKwogIGdlb21fcXVhc2lyYW5kb20od2lkdGg9MC40LCBtZXRob2QgPSAicXVhc2lyYW5kb20iLCBzaXplID0gMC43NSkgICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpICsKICBmYWNldF93cmFwKHZhcnMoZGlzdCksIHNjYWxlcyA9ICJmcmVlIikgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkgICMg6Lu444Gu44Op44OZ44Or44KS5raI44GZCiAgKSArCiAgc2NhbGVfeF9kaXNjcmV0ZSgKICAgIGxhYmVscyA9IGMoCiAgICAgIG1lYW5zMjAgPSAgICAiTiA9IDIwXG7lubPlnYciLAogICAgICBtZWRpYW5zMjAgPSAgIk4gPSAyMFxu5Lit5aSu5YCkIiwgICAgICAKICAgICAgbWVhbnMxMDAgPSAgICJOID0gMTAwXG7lubPlnYciLAogICAgICBtZWRpYW5zMTAwID0gIk4gPSAxMDBcbuS4reWkruWApCIsCiAgICAgIG1lYW5zNTAwID0gICAiTiA9IDUwMFxu5bmz5Z2HIiwKICAgICAgbWVkaWFuczUwMCA9ICJOID0gNTAwXG7kuK3lpK7lgKQiCiAgICAgICkKICAgICkKCnAyIDwtIGRmX3NkX2lxciB8PgogIGdncGxvdChhZXMoeCA9IG5fbWV0cmljLCB5ID0gdmFsdWUsIGNvbG9yID0gbikpICsKICBnZW9tX3F1YXNpcmFuZG9tKHdpZHRoPTAuNCwgbWV0aG9kID0gInF1YXNpcmFuZG9tIiwgc2l6ZSA9IDAuNzUpICArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgaGp1c3QgPSAxKSkgKwogIGZhY2V0X3dyYXAodmFycyhkaXN0KSwgc2NhbGVzID0gImZyZWUiKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSAgIyDou7jjga7jg6njg5njg6vjgpLmtojjgZkKICApICsKICBzY2FsZV94X2Rpc2NyZXRlKAogICAgbGFiZWxzID0gYygKICAgICAgc2QyMCA9ICAgIk4gPSAyMFxu5qiZ5rqW5YGP5beuIiwKICAgICAgaXFyMjAgPSAgIk4gPSAyMFxuSVFSIiwKICAgICAgc2QxMDAgPSAgIk4gPSAxMDBcbuaomea6luWBj+W3riIsCiAgICAgIGlxcjEwMCA9ICJOID0gMTAwXG5JUVIiLAogICAgICBzZDUwMCA9ICAiTiA9IDUwMFxu5qiZ5rqW5YGP5beuIiwKICAgICAgaXFyNTAwID0gIk4gPSA1MDBcbklRUiIKICAgICkKICApCgpwMyA8LSBkZl9tYXhfcDk1IHw+CiAgZ2dwbG90KGFlcyh4ID0gbl9tZXRyaWMsIHkgPSB2YWx1ZSwgY29sb3IgPSBuKSkgKwogIGdlb21fcXVhc2lyYW5kb20od2lkdGg9MC40LCBtZXRob2QgPSAicXVhc2lyYW5kb20iLCBzaXplID0gMC43NSkgICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAwLCBoanVzdCA9IDEpKSArCiAgZmFjZXRfd3JhcCh2YXJzKGRpc3QpLCBzY2FsZXMgPSAiZnJlZSIpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpICAjIOi7uOOBruODqeODmeODq+OCkua2iOOBmQogICAgKSArCiAgc2NhbGVfeF9kaXNjcmV0ZSgKICAgIGxhYmVscyA9IGMoCiAgICAgIG1heDIwID0gICJOID0gMjBcbuacgOWkp+WApCIsCiAgICAgIHA5NTIwID0gICJOID0gMjBcbjk1JeeCuSIsCiAgICAgIG1heDEwMCA9ICJOID0gMTAwXG7mnIDlpKflgKQiLAogICAgICBwOTUxMDAgPSAiTiA9IDEwMFxuOTUl54K5IiwKICAgICAgbWF4NTAwID0gIk4gPSA1MDBcbuacgOWkp+WApCIsCiAgICAgIHA5NTUwMCA9ICJOID0gNTAwXG45NSXngrkiCiAgICApCiAgKQoKcDEgLyBwMiAvIHAzCmBgYAoKCiMjIyA1LjEuMyDntbHoqIjmjIfmqJnjgajliIbluIPjga7lvaLnirYKCmBgYHtyfQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHBhdGNod29yaykKCiMg5Lmx5pWw44Gu44K344O844OJ6Kit5a6aCnNldC5zZWVkKDQyKQoKIyDljZjls7DmgKfjga7liIbluIMKc2luZ2xlX3BlYWtfZGlzdHJpYnV0aW9uIDwtIHJub3JtKDEwMDAsIG1lYW49MCwgc2Q9MSkKCiMg5LqM5bOw5oCn44Gu5YiG5biDCmJpbW9kYWxfZGlzdHJpYnV0aW9uIDwtIGMocm5vcm0oNTAwLCBtZWFuPS0xLCBzZD0wLjIpLCBybm9ybSg1MDAsIG1lYW49MSwgc2Q9MC4yKSkKCiMg5Y2Y5bOw5oCn44Gu5YiG5biD44Gu5bmz5Z2H5YCk44Go5qiZ5rqW5YGP5beuCnNpbmdsZV9wZWFrX21lYW4gPC0gbWVhbihzaW5nbGVfcGVha19kaXN0cmlidXRpb24pCnNpbmdsZV9wZWFrX3N0ZCA8LSBzZChzaW5nbGVfcGVha19kaXN0cmlidXRpb24pCgojIOS6jOWzsOaAp+OBruWIhuW4g+OBruW5s+Wdh+WApOOBqOaomea6luWBj+W3rgpiaW1vZGFsX21lYW4gPC0gbWVhbihiaW1vZGFsX2Rpc3RyaWJ1dGlvbikKYmltb2RhbF9zdGQgPC0gc2QoYmltb2RhbF9kaXN0cmlidXRpb24pCgojIOWNmOWzsOaAp+OBruWIhuW4g+OBruODkuOCueODiOOCsOODqeODoApwMSA8LSBkYXRhLmZyYW1lKHg9c2luZ2xlX3BlYWtfZGlzdHJpYnV0aW9uKSB8PgogIGdncGxvdChhZXMoeD14KSkgKwogIGdlb21faGlzdG9ncmFtKGNvbG9yPSJnb2xkIiwgZmlsbD0iZ29sZCIsIGJpbndpZHRoPTAuMikgKwogIGxhYnMoeD0i6Kaz5ris5YCkIiwgeT0i6aC75bqmIikgKwogIGxhYnMoCiAgICB0aXRsZSA9IHBhc3RlKAogICAgICAi5Y2Y5bOw5oCn44Gu5YiG5biDXG4o5bmz5Z2HPSIsIHJvdW5kKHNpbmdsZV9wZWFrX21lYW4sMiksCiAgICAgICIsIOaomea6luWBj+W3rj0iLCByb3VuZChzaW5nbGVfcGVha19zdGQsIDIpLAogICAgICAiKSIKICAgICAgKSkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDMvNCkKCiMg5LqM5bOw5oCn44Gu5YiG5biD44Gu44OS44K544OI44Kw44Op44OgCnAyIDwtIGdncGxvdChkYXRhLmZyYW1lKHg9Ymltb2RhbF9kaXN0cmlidXRpb24pLCBhZXMoeD14KSkgKwogIGdlb21faGlzdG9ncmFtKGNvbG9yPSJibHVlIiwgZmlsbD0iYmx1ZSIsIGJpbndpZHRoPTAuMikgKwogIGxhYnMoeD0i6Kaz5ris5YCkIiwgeT0i6aC75bqmIikgKwogIGxhYnMoCiAgICB0aXRsZSA9IHBhc3RlKAogICAgICAi5LqM5bOw5oCn44Gu5YiG5biDXG4o5bmz5Z2HPSIsIHJvdW5kKGJpbW9kYWxfbWVhbiwyKSwKICAgICAgIiwg5qiZ5rqW5YGP5beuPSIsIHJvdW5kKGJpbW9kYWxfc3RkLCAyKSwKICAgICAgIikiCiAgICApKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMy80KQoKcDEgKyBwMgpgYGAKCiMjIDUuMiDjgbDjgonjgaTjgY3jgpLjgajjgonjgYjjgosKCiMjIyA1LjIuMiDjg6/jgqTjg7Pjg4fjg7zjgr/jga5a44K544Kz44Ki5YyWCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9Ny41fQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdnYmVlc3dhcm0pCgojIFdpbmXjg4fjg7zjgr/jgrvjg4Pjg4jjga7jg63jg7zjg4kKY29sX25hbWVzIDwtIGMoCiAgICAi44OW44OJ44Km44Gu5ZOB56iuIiwgIuOCouODq+OCs+ODvOODq+W6puaVsCIsICLjg6rjg7PjgrTphbgiLCAi44Of44ON44Op44Or5YiGIiwgCiAgICAi44Of44ON44Op44Or5YiG44Gu44Ki44Or44Kr44Oq5bqmIiwgIuODnuOCsOODjeOCt+OCpuODoCIsICLlhajjg5Xjgqfjg47jg7zjg6vpoZ4iLCAi44OV44Op44OQ44OO44Kk44OJIiwKICAgICLpnZ7jg5Xjg6njg5Djg47jgqTjg4njg5Xjgqfjg47jg7zjg6vpoZ4iLCAi44OX44Ot44Ki44Oz44OI44K344Ki44OL44OzIiwgIuiJsuOBruW8t+OBlSIsICLoibLnm7giLAogICAgIk9EMjgwL09EMzE15YCkIiwgIuODl+ODreODquODsyIKICApCgpvcmlnaW5hbF9kYXRhIDwtIHJlYWRfY3N2KAogICJodHRwczovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvbWFjaGluZS1sZWFybmluZy1kYXRhYmFzZXMvd2luZS93aW5lLmRhdGEiLAogIGNvbF9uYW1lcyA9IGNvbF9uYW1lcywKICBjb2xfdHlwZXMgPSAiZiIKKQoKIyBa44K544Kz44Ki5YyW44GX44Gf44OH44O844K/44OV44Os44O844Og44Gu5L2c5oiQCnpzY29yZWRfZGF0YSA8LSBvcmlnaW5hbF9kYXRhIHw+CiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgXCh4KSBzY2FsZSh4KSkpICPmqJnmupbljJYKCiMg44Kq44Oq44K444OK44Or44Gu44K544Km44Kp44O844Og44OX44Ot44OD44OICnAxIDwtIG9yaWdpbmFsX2RhdGEgfD4KICBwaXZvdF9sb25nZXIoIWMo44OW44OJ44Km44Gu5ZOB56iuKSkgfD4KICBtdXRhdGUobmFtZSA9IGZhY3RvcihuYW1lLCBsZXZlbHMgPSBjb2xfbmFtZXMpKSB8PgogIGdncGxvdChhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSwgY29sb3IgPSDjg5bjg4njgqbjga7lk4HnqK4pKSArCiAgZ2VvbV9xdWFzaXJhbmRvbSh3aWR0aD0wLjQsIG1ldGhvZCA9ICJxdWFzaXJhbmRvbSIsIHNpemUgPSAwLjc1KSAgKyAKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMjAsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMSwgMC44KSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKQogICAgKSArCiAgc2NhbGVfY29sb3JfaHVlKG5hbWUgPSAi44OW44OJ44Km5ZOB56iuIiwgbGFiZWxzID0gYygiMSIgPSAi5ZOB56iuMSIsICIyIiA9IuWTgeeorjIiLCAiMyIgPSLlk4HnqK4zIikpICsKICBsYWJzKHRpdGxlID0gIuWkieaVsOOBlOOBqOOBq+WPluOCi+WApOOBruevhOWbsuOBjOeVsOOBquOCiyIsIHkgPSAi5YWD44Gu5YCkIikKCiMgeuOCueOCs+OCouWMluOBl+OBn+OCueOCpuOCqeODvOODoOODl+ODreODg+ODiApwMiA8LSB6c2NvcmVkX2RhdGEgfD4KICBwaXZvdF9sb25nZXIoIWMo44OW44OJ44Km44Gu5ZOB56iuKSkgfD4KICBtdXRhdGUobmFtZSA9IGZhY3RvcihuYW1lLCBsZXZlbHMgPSBjb2xfbmFtZXMpKSB8PgogIGdncGxvdChhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSwgY29sb3IgPSDjg5bjg4njgqbjga7lk4HnqK4pKSArCiAgZ2VvbV9xdWFzaXJhbmRvbSh3aWR0aD0wLjQsIG1ldGhvZCA9ICJxdWFzaXJhbmRvbSIsIHNpemUgPSAwLjc1KSAgKyAKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMjAsIGhqdXN0ID0gMSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIKICAgICkgKwogIGxhYnModGl0bGUgPSAi5YCk44Gu56+E5Zuy44GM5o+D44Gj44Gm44GE44KLIiwgeCA9ICIiLCB5ID0gIlrjgrnjgrPjgqIiKQoKcDEgLyBwMgpgYGAKCiMjIyA1LjIuNCDntbblr77lgY/lt67mjIfmqJnjga7mjK/jgovoiJ7jgYQKCmBgYHtyIGZpZy5oZWlnaHQ9Ny41LCBmaWcud2lkdGg9Ny41fQpsaWJyYXJ5KGNvbmZsaWN0ZWQpCmxpYnJhcnkodGlkeXZlcnNlKQoKIyDjgrXjg7Pjg5fjg6vjgrXjgqTjgrrjgIAKbiA8LSAoMjAgKyAxMDAgKyA1MDApICogMTAwCgojIOe1hOOBv+WQiOOCj+OBmwplZyA8LSBleHBhbmRfZ3JpZCgKICBuID0gcmVwKGMocmVwKCIyMCIsMjApLCByZXAoIjEwMCIsIDEwMCksIHJlcCgiNTAwIiwgNTAwKSkpLAogIHNhbXBsZSA9IDE6MTAwCikKCnNldC5zZWVkKDApCmRmIDwtIGJpbmRfcm93cygKICBiaW5kX2NvbHMoZGF0YS5mcmFtZShkaXN0ID0gIuato+imj+WIhuW4g+OBi+OCieOCteODs+ODl+ODquODs+OCsCIsIHZhbHVlID0gcm5vcm0obikpLCBlZyksCiAgYmluZF9jb2xzKGRhdGEuZnJhbWUoZGlzdCA9ICLlr77mlbDmraPopo/liIbluIPjgYvjgonjgrXjg7Pjg5fjg6rjg7PjgrAiLCB2YWx1ZSA9IHJsbm9ybShuKSksIGVnKQogICkgfD4KICBzdW1tYXJpc2UoCiAgICBTRCA9IHNkKHZhbHVlKSwjIOaomea6luWBj+W3rgogICAgTWVhbkFEID0gbWFkKHZhbHVlLCBjZW50ZXIgPSBtZWFuKHZhbHVlKSksICAjIOW5s+Wdh+e1tuWvvuWBj+W3rgogICAgTWVkaWFuQUQgPSBtYWQodmFsdWUsIGNlbnRlciA9IG1lZGlhbih2YWx1ZSkpLCAjIOS4reWkrue1tuWvvuWBj+W3rgogICAgLmJ5ID0gYyhkaXN0LCBuLCBzYW1wbGUpCiAgKSB8PgogIGRwbHlyOjpzZWxlY3QoIXNhbXBsZSkgfD4KICBwaXZvdF9sb25nZXIoIWMoZGlzdCwgbiksIG5hbWVzX3RvID0gIm1ldHJpYyIpIHw+CiAgbXV0YXRlKGRpc3QgPSBmYWN0b3IoZGlzdCwgbGV2ZWxzID0gYygi5q2j6KaP5YiG5biD44GL44KJ44K144Oz44OX44Oq44Oz44KwIiwgIuWvvuaVsOato+imj+WIhuW4g+OBi+OCieOCteODs+ODl+ODquODs+OCsCIpKSkKCmRmIHw+CiAgbXV0YXRlKG5fbWV0cmljID0gZmFjdG9yKAogICAgcGFzdGUwKG1ldHJpYywgbiksIAogICAgbGV2ZWxzID0gYygiU0QyMCIsICJTRDEwMCIsICJTRDUwMCIsCiAgICAgICAgICAgICAgICJNZWFuQUQyMCIsICJNZWFuQUQxMDAiLCAiTWVhbkFENTAwIiwKICAgICAgICAgICAgICAgIk1lZGlhbkFEMjAiLCAiTWVkaWFuQUQxMDAiLCAiTWVkaWFuQUQ1MDAiKSkpIHw+CiAgZ2dwbG90KGFlcyh4ID0gbl9tZXRyaWMsIHkgPSB2YWx1ZSwgY29sb3IgPSBtZXRyaWMpKSArCiAgZ2VvbV9xdWFzaXJhbmRvbSh3aWR0aD0wLjQsIG1ldGhvZCA9ICJxdWFzaXJhbmRvbSIsIHNpemUgPSAwLjc1KSAgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDAsIGhqdXN0ID0gMSkpICsKICBmYWNldF93cmFwKHZhcnMoZGlzdCksIHNjYWxlcyA9ICJmcmVlIiwgbnJvdyA9IDIpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpICAjIOi7uOOBruODqeODmeODq+OCkua2iOOBmQogICkgKwogIHNjYWxlX3hfZGlzY3JldGUoCiAgICBsYWJlbHMgPSBjKAogICAgICBTRDIwICAgICAgICA9ICJOID0gMjBcbuaomea6luWBj+W3riIsCiAgICAgIFNEMTAwICAgICAgID0gIk4gPSAxMDBcbuaomea6luWBj+W3riIsCiAgICAgIFNENTAwICAgICAgID0gIk4gPSA1MDBcbuaomea6luWBj+W3riIsCiAgICAgIE1lYW5BRDIwICAgID0gIk4gPSAyMFxu5bmz5Z2H57W25a++5YGP5beuIiwKICAgICAgTWVhbkFEMTAwICAgPSAiTiA9IDEwMFxu5bmz5Z2H57W25a++5YGP5beuIiwKICAgICAgTWVhbkFENTAwICAgPSAiTiA9IDUwMFxu5bmz5Z2H57W25a++5YGP5beuIiwKICAgICAgTWVkaWFuQUQyMCAgPSAiTiA9IDIwXG7kuK3lpK7ntbblr77lgY/lt64iLAogICAgICBNZWRpYW5BRDEwMCA9ICJOID0gMTAwXG7kuK3lpK7ntbblr77lgY/lt64iLAogICAgICBNZWRpYW5BRDUwMCA9ICJOID0gNTAwXG7kuK3lpK7ntbblr77lgY/lt64iCiAgICAgICkKICApCmBgYAoKIyMgNS4zIOWIhuW4g+OBruW9ouOCkuOBqOOCieOBiOOCiwoKIyMjIDUuMy4xIOatquW6puOBqOWwluW6pgoKYGBge3IgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD03LjUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZTEwNzEpCmxpYnJhcnkoc24pICNyc24oKQpsaWJyYXJ5KFZHQU0pICNybGFwbGFjZQpsaWJyYXJ5KHBhdGNod29yaykKCnNldC5zZWVkKDQyKQpub3JtYWxfZGlzdCA8LSBybm9ybSgxMDAwLCBtZWFuID0gMCwgc2QgPSAxKSMg5q2j6KaP5YiG5biDCnNrZXdlZF9kaXN0IDwtIHJzbigxMDAwLCB4aSA9IDAsIG9tZWdhID0gMiwgYWxwaGE9NCkjIOatquato+imj+WIhuW4gwpsYXBsYWNlX2Rpc3QgPC0gcmxhcGxhY2UoMTAwMCwgbG9jYXRpb24gPSAwLCBzY2FsZSA9IDEpIyDjg6njg5fjg6njgrnliIbluIPvvIjmrarluqY9MCwg5bCW5bqmPjPvvIkKCiMg44OS44K544OI44Kw44Op44Og44Gu5o+P55S7CnAxIDwtIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IG5vcm1hbF9kaXN0KSwgZmlsbCA9ICIjOERBMENCIikgKwogIGxhYnModGl0bGUgPSAi5YiG5biD77yR77yI5q2j6KaP5YiG5biD77yJIiwgeCA9ICIiLCB5ID0gIiIpCgpwMiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBza2V3ZWRfZGlzdCksIGZpbGwgPSAiIzY2QzJBNSIpICsKICBsYWJzKHRpdGxlID0gIuWIhuW4g++8ku+8iOatquato+imj+WIhuW4g++8iSIsIHggPSAiIiwgeSA9ICIiKQoKcDMgPC0gZ2dwbG90KCkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gbGFwbGFjZV9kaXN0KSwgZmlsbCA9ICIjRkM4RDYyIikgKwogIGxhYnModGl0bGUgPSAi5YiG5biD77yT77yI44Op44OX44Op44K55YiG5biD77yJIiwgeCA9ICIiLCB5ID0gIiIpCgojIOe1seioiOmHj+OBruioiOeulwptZWRpYW5fc2tld25lc3MgPC0gZnVuY3Rpb24oeCkgewogIChtZWFuKHgpIC0gbWVkaWFuKHgpKSAvIHNkKHgpCiAgfQoKcXVhcnRpbGVfc2tld25lc3MgPC0gZnVuY3Rpb24oeCkgewogIHEzIDwtIHF1YW50aWxlKHgsIDAuNzUpCiAgcTEgPC0gcXVhbnRpbGUoeCwgMC4yNSkKICBhcy5udW1lcmljKChxMyArIHExIC0gMiAqIG1lZGlhbih4KSkgLyAocTMgLSBxMSkpCn0KCmRpc3RzIDwtIGxpc3Qobm9ybWFsX2Rpc3QsIHNrZXdlZF9kaXN0LCBsYXBsYWNlX2Rpc3QpCgpmaWxscyA8LSBjKCIjOERBMENCIiwgIiM2NkMyQTUiLCAiI0ZDOEQ2MiIpCgpwNCA8LSBkYXRhLmZyYW1lKAogIG5hbWUgPSBwYXN0ZTAoIuWIhuW4gyIsIDE6MyksCiAgdmFsdWUgPSBtYXBfZGJsKGRpc3RzLCBza2V3bmVzcykKKSB8PgogIGdncGxvdChhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2NvbChmaWxsID0gZmlsbHMpICsKICBsYWJzKHRpdGxlID0gIuatquW6piIsIHggPSAiIiwgeSA9ICIiKQoKcDUgPC0gZGF0YS5mcmFtZSgKICBuYW1lID0gcGFzdGUwKCLliIbluIMiLCAxOjMpLAogIHZhbHVlID0gbWFwX2RibChkaXN0cywgbWVkaWFuX3NrZXduZXNzKQopIHw+CiAgZ2dwbG90KGFlcyh4ID0gbmFtZSwgeSA9IHZhbHVlKSkgKwogIGdlb21fY29sKGZpbGwgPSBmaWxscykgKwogIGxhYnModGl0bGUgPSAi5Lit5aSu5YCk5q2q5bqmIiwgeCA9ICIiLCB5ID0gIiIpCgpwNiA8LSBkYXRhLmZyYW1lKAogIG5hbWUgPSBwYXN0ZTAoIuWIhuW4gyIsIDE6MyksCiAgdmFsdWUgPSBtYXBfZGJsKGRpc3RzLCBxdWFydGlsZV9za2V3bmVzcykKKSB8PgogIGdncGxvdChhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSkpICsKICBnZW9tX2NvbChmaWxsID0gZmlsbHMpICsKICBsYWJzKHRpdGxlID0gIuWbm+WIhuS9jeatquW6piIsIHggPSAiIiwgeSA9ICIiKQoKcDcgPC0gZGF0YS5mcmFtZSgKICBuYW1lID0gcGFzdGUwKCLliIbluIMiLCAxOjMpLAogIHZhbHVlID0gbWFwX2RibChkaXN0cywga3VydG9zaXMpCikgfD4KICBnZ3Bsb3QoYWVzKHggPSBuYW1lLCB5ID0gdmFsdWUpKSArCiAgZ2VvbV9jb2woZmlsbCA9IGZpbGxzKSArCiAgbGFicyh0aXRsZSA9ICLlsJbluqYiLCB4ID0gIiIsIHkgPSAiIikKCiMg44Kw44Op44OV44Gu6KGo56S6CntwMSB8IHAyIHwgcDN9IC8geyBwNCB8IHA1IHwgcDYgfCBwNyB9CgojKHAxICsgcDIgKyBwMyArIHBsb3RfbGF5b3V0KG5jb2wgPSAzKSkgLwojICAocDQgKyBwNSsgcDYgKyBwNyArIHBsb3RfbGF5b3V0KG5jb2wgPSA0KSkKCgpgYGAKCgojIyMgNS4zLjIg44OS44K544OI44Kw44Op44Og44Gr5a++44GX44Gm5oOF5aCx44Ko44Oz44OI44Ot44OU44O85oyH5qiZ44KS6KiI566X44GZ44KLCgpgYGB7cn0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZW50cm9weSkKCiMg44K344O844OJ5YCk44KS5Zu65a6aCnNldC5zZWVkKDQyKQoKIyDjgrXjg7Pjg5fjg6vjgrXjgqTjgroKbl9zYW1wbGVzIDwtIDUwMAoKIyDjg5Pjg7Pjga7oqK3lrpoKYmlucyA8LSBzZXEoLTUsIDUsIGxlbmd0aC5vdXQgPSA0MCkKCiMg5LiA5qeY5YiG5biDCnVuaWZvcm1fc2FtcGxlcyA8LSBydW5pZihuX3NhbXBsZXMsIG1pbiA9IC01LCBtYXggPSA1KQp1bmlmb3JtX2hpc3QgPC0gaGlzdCh1bmlmb3JtX3NhbXBsZXMsIGJyZWFrcyA9IGJpbnMsIHBsb3QgPSBGQUxTRSkKdW5pZm9ybV9lbnRyb3B5IDwtIGVudHJvcHkuZW1waXJpY2FsKHVuaWZvcm1faGlzdCRjb3VudHMpCgojIOato+imj+WIhuW4gwpub3JtYWxfc2FtcGxlcyA8LSBybm9ybShuX3NhbXBsZXMsIG1lYW4gPSAwLCBzZCA9IDEpCm5vcm1hbF9oaXN0IDwtIGhpc3Qobm9ybWFsX3NhbXBsZXMsIGJyZWFrcyA9IGJpbnMsIHBsb3QgPSBGQUxTRSkKbm9ybWFsX2VudHJvcHkgPC0gZW50cm9weS5lbXBpcmljYWwobm9ybWFsX2hpc3QkY291bnRzKQoKIyAy44Gk44Gu5q2j6KaP5YiG5biDCmdtbV9zYW1wbGVzIDwtIGMoCiAgcm5vcm0obl9zYW1wbGVzIC8gMiwgbWVhbiA9IC0yLCBzZCA9IDAuMjUpLAogIHJub3JtKG5fc2FtcGxlcyAvIDIsIG1lYW4gPSAyLCBzZCA9IDAuMjUpCiAgKQpnbW1faGlzdCA8LSBoaXN0KGdtbV9zYW1wbGVzLCBicmVha3MgPSBiaW5zLCBwbG90ID0gRkFMU0UpCmdtbV9lbnRyb3B5IDwtIGVudHJvcHkuZW1waXJpY2FsKGdtbV9oaXN0JGNvdW50cykKCiMg44OS44K544OI44Kw44Op44Og44Gu5o+P55S7CnAxIDwtIGdncGxvdCgpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHVuaWZvcm1fc2FtcGxlcyksIGJpbnMgPSBsZW5ndGgoYmlucykgLSAxLCBmaWxsID0gInNreWJsdWUiLCBjb2xvciA9ICJibGFjayIpICsKICBsYWJzKHRpdGxlID0gcGFzdGUwKCLkuIDmp5jliIbluINcbuOCqOODs+ODiOODreODlOODvDogIiwgcm91bmQodW5pZm9ybV9lbnRyb3B5LCAyKSkpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxLCBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpwMiA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBub3JtYWxfc2FtcGxlcyksIGJpbnMgPSBsZW5ndGgoYmlucykgLSAxLCBmaWxsID0gInNhbG1vbiIsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoIuato+imj+WIhuW4g1xu44Ko44Oz44OI44Ot44OU44O8OiAiLCByb3VuZChub3JtYWxfZW50cm9weSwgMikpKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKcDMgPC0gZ2dwbG90KCkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gZ21tX3NhbXBsZXMpLCBiaW5zID0gbGVuZ3RoKGJpbnMpIC0gMSwgZmlsbCA9ICJsaWdodGdyZWVuIiwgY29sb3IgPSAiYmxhY2siKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgiMuOBpOOBruato+imj+WIhuW4g+OBrua3t+WQiFxu44Ko44Oz44OI44Ot44OU44O8OiAiLCByb3VuZChnbW1fZW50cm9weSwgMikpKSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSwgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKCiMg44Kw44Op44OV44Gu6KGo56S6CnAxICsgcDIgKyBwMwpgYGAKCiMjIyA1LjMuMyDjg5Pjg7Pjga7luYXjgajjgqjjg7Pjg4jjg63jg5Tjg7wKCgpgYGB7cn0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZW50cm9weSkKCiMg44K344O844OJ5YCk44KS5Zu65a6aCnNldC5zZWVkKDQyKQoKIyDjgrXjg7Pjg5fjg6vjgrXjgqTjgroKbl9zYW1wbGVzIDwtIDUwCgpzbWFsbF9iaW5zIDwtIHNlcSgtNSwgNSwgbGVuZ3RoLm91dCA9IDQwMCkjIOWwj+OBleOBquODk+ODs++8iDQwMOWIhuWJsu+8iQpsYXJnZV9iaW5zIDwtIHNlcSgtNSwgNSwgbGVuZ3RoLm91dCA9IDUpIyDlpKfjgY3jgarjg5Pjg7PvvIg15YiG5Ymy77yJCgojIOS4gOanmOWIhuW4gwp1bmlmb3JtX3NhbXBsZXMgPC0gcnVuaWYobl9zYW1wbGVzLCBtaW4gPSAtNSwgbWF4ID0gNSkKdW5pZm9ybV9lbnRyb3B5X3NtYWxsIDwtIGVudHJvcHkuZW1waXJpY2FsKGhpc3QodW5pZm9ybV9zYW1wbGVzLCBicmVha3MgPSBzbWFsbF9iaW5zLCBwbG90ID0gRkFMU0UpJGNvdW50cykKdW5pZm9ybV9lbnRyb3B5X2xhcmdlIDwtIGVudHJvcHkuZW1waXJpY2FsKGhpc3QodW5pZm9ybV9zYW1wbGVzLCBicmVha3MgPSBsYXJnZV9iaW5zLCBwbG90ID0gRkFMU0UpJGNvdW50cykKCiMg5q2j6KaP5YiG5biDCm5vcm1hbF9zYW1wbGVzIDwtIHJub3JtKG5fc2FtcGxlcywgbWVhbiA9IDAsIHNkID0gMSkKbm9ybWFsX2VudHJvcHlfc21hbGwgPC0gZW50cm9weS5lbXBpcmljYWwoaGlzdChub3JtYWxfc2FtcGxlcywgYnJlYWtzID0gc21hbGxfYmlucywgcGxvdCA9IEZBTFNFKSRjb3VudHMpCm5vcm1hbF9lbnRyb3B5X2xhcmdlIDwtIGVudHJvcHkuZW1waXJpY2FsKGhpc3Qobm9ybWFsX3NhbXBsZXMsIGJyZWFrcyA9IGxhcmdlX2JpbnMsIHBsb3QgPSBGQUxTRSkkY291bnRzKQoKIyAy44Gk44Gu5q2j6KaP5YiG5biDCmdtbV9zYW1wbGVzIDwtIGMocm5vcm0obl9zYW1wbGVzIC8gMiwgbWVhbiA9IC0yLCBzZCA9IDAuMjUpLCBybm9ybShuX3NhbXBsZXMgLyAyLCBtZWFuID0gMiwgc2QgPSAwLjI1KSkKZ21tX2VudHJvcHlfc21hbGwgPC0gZW50cm9weS5lbXBpcmljYWwoaGlzdChnbW1fc2FtcGxlcywgYnJlYWtzID0gc21hbGxfYmlucywgcGxvdCA9IEZBTFNFKSRjb3VudHMpCmdtbV9lbnRyb3B5X2xhcmdlIDwtIGVudHJvcHkuZW1waXJpY2FsKGhpc3QoZ21tX3NhbXBsZXMsIGJyZWFrcyA9IGxhcmdlX2JpbnMsIHBsb3QgPSBGQUxTRSkkY291bnRzKQoKbXlfcGxvdCA8LSBmdW5jdGlvbihzYW1wbGVzLCBiaW5zLCBlbnRyb3B5LCBmaWxsX2NvbG9yLCB0aXRsZSkgewogIGdncGxvdCgpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gc2FtcGxlcyksIGJpbnMgPSBsZW5ndGgoYmlucykgLSAxLCBmaWxsID0gZmlsbF9jb2xvcikgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlMCh0aXRsZSwgcm91bmQoZW50cm9weSwgMikpKSArCiAgICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBhc3BlY3QucmF0aW8gPSAxKSArCiAgICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTUsIDUpKQp9CgpwMSA8LSBteV9wbG90KHVuaWZvcm1fc2FtcGxlcywgc21hbGxfYmlucywgdW5pZm9ybV9lbnRyb3B5X3NtYWxsLCAic2t5Ymx1ZSIsICLkuIDmp5jliIbluINcbuOCqOODs+ODiOODreODlOODvDogIikKcDIgPC0gbXlfcGxvdChub3JtYWxfc2FtcGxlcywgc21hbGxfYmlucywgbm9ybWFsX2VudHJvcHlfc21hbGwsICJzYWxtb24iLCAi5q2j6KaP5YiG5biDXG7jgqjjg7Pjg4jjg63jg5Tjg7w6ICIpCnAzIDwtIG15X3Bsb3QoZ21tX3NhbXBsZXMsIHNtYWxsX2JpbnMsIGdtbV9lbnRyb3B5X3NtYWxsLCAibGlnaHRncmVlbiIsICIy44Gk44Gu5q2j6KaP5YiG5biD44Gu5re35ZCIXG7jgqjjg7Pjg4jjg63jg5Tjg7w6ICIpCnA0IDwtIG15X3Bsb3QodW5pZm9ybV9zYW1wbGVzLCBsYXJnZV9iaW5zLCB1bmlmb3JtX2VudHJvcHlfbGFyZ2UsICJza3libHVlIiwgIuS4gOanmOWIhuW4g1xu44Ko44Oz44OI44Ot44OU44O8OiAiKQpwNSA8LSBteV9wbG90KG5vcm1hbF9zYW1wbGVzLCBsYXJnZV9iaW5zLCBub3JtYWxfZW50cm9weV9sYXJnZSwgInNhbG1vbiIsICLmraPopo/liIbluINcbuOCqOODs+ODiOODreODlOODvDogIikKcDYgPC0gbXlfcGxvdChnbW1fc2FtcGxlcywgbGFyZ2VfYmlucywgZ21tX2VudHJvcHlfbGFyZ2UsICJsaWdodGdyZWVuIiwgIjLjgaTjga7mraPopo/liIbluIPjga7mt7flkIhcbuOCqOODs+ODiOODreODlOODvDogIikKCiMg44Kw44Op44OV44Gu6KGo56S6CihwMSArIHAyICsgcDMpIC8gKHA0ICsgcDUgKyBwNikKYGBgCgojIyMgNS4zLjQg44K444OL5L+C5pWw44Gu6KiI566XCgpgYGB7cn0KbGlicmFyeShjb25mbGljdGVkKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShwYXRjaHdvcmspCgojIOOCt+ODvOODieWApOOCkuWbuuWumgpzZXQuc2VlZCg0MikKCiMg44K144Oz44OX44Or44K144Kk44K6Cm5fc2FtcGxlcyA8LSAxMDAKCiMg5q2j6KaP5YiG5biD44GL44KJ44Gu44K144Oz44OX44Oq44Oz44Kw77yI5bmz5Z2HNDAw5LiH44CB5qiZ5rqW5YGP5beuMjDkuIfvvIkKbm9ybWFsX3NhbXBsZXMgPC0gcm5vcm0obl9zYW1wbGVzLCBtZWFuID0gNDAwLCBzZCA9IDIwKQoKIyDlubPlnYc0MDDkuIfjga7lr77mlbDmraPopo/liIbluIPjgYvjgonjga7jgrXjg7Pjg5fjg6rjg7PjgrAKbWVhbl9sb2dub3JtYWwgPC0gNDAwCnNpZ21hX2xvZ25vcm1hbCA8LSAwLjUgICMg5Lu75oSP44Gu5qiZ5rqW5YGP5beuCmxvZ25vcm1hbF9zYW1wbGVzIDwtIHJsbm9ybSgKICBuX3NhbXBsZXMsCiAgbWVhbmxvZyA9IGxvZyhtZWFuX2xvZ25vcm1hbCkgLSAwLjUgKiBzaWdtYV9sb2dub3JtYWxeMiwKICBzZGxvZyA9IHNpZ21hX2xvZ25vcm1hbAogICkKCiMg44Ot44O844Os44Oz44OE5puy57ea44Go44K444OL5L+C5pWw44Gu6KiI566X44KS6KGM44GG6Zai5pWwCmNhbGN1bGF0ZV9sb3JlbnpfY3VydmUgPC0gZnVuY3Rpb24oc2FtcGxlcykgewogIHNvcnRlZF9zYW1wbGVzIDwtIHNvcnQoc2FtcGxlcykgICMg44K144Oz44OX44Or44KS5piH6aCG44Gr5Lim44Gz5pu/44GI44KLCiAgY3VtX2ZyZXF1ZW5jaWVzIDwtIGN1bXN1bShzb3J0ZWRfc2FtcGxlcykgLyBzdW0oc29ydGVkX3NhbXBsZXMpICAjIOe0r+epjeebuOWvvuW6puaVsAogIGN1bV9wb3B1bGF0aW9uIDwtIHNlcSgxLCBuX3NhbXBsZXMpIC8gbl9zYW1wbGVzICAjIOe0r+epjeS6uuWPo+OBruWJsuWQiAogIGdpbmkgPC0gbWVhbihjdW1fcG9wdWxhdGlvbiAtIGN1bV9mcmVxdWVuY2llcykgKiAyICAjIOOCuOODi+S/guaVsAogIHJldHVybihsaXN0KGN1bV9wb3B1bGF0aW9uID0gY3VtX3BvcHVsYXRpb24sIGN1bV9mcmVxdWVuY2llcyA9IGN1bV9mcmVxdWVuY2llcywgZ2luaSA9IGdpbmkpKQp9CgojIOato+imj+WIhuW4g+OBruODreODvOODrOODs+ODhOabsue3muOBqOOCuOODi+S/guaVsApub3JtYWxfbG9yZW56IDwtIGNhbGN1bGF0ZV9sb3JlbnpfY3VydmUobm9ybWFsX3NhbXBsZXMpCgojIOWvvuaVsOato+imj+WIhuW4g+OBruODreODvOODrOODs+ODhOabsue3muOBqOOCuOODi+S/guaVsApsb2dub3JtYWxfbG9yZW56IDwtIGNhbGN1bGF0ZV9sb3JlbnpfY3VydmUobG9nbm9ybWFsX3NhbXBsZXMpCgojIOODkuOCueODiOOCsOODqeODoOOBruaPj+eUuwpwMSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBub3JtYWxfc2FtcGxlcyksIGJpbnMgPSAyMCwgZmlsbCA9ICJza3libHVlIikgKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoIuW5tOWPjuWIhuW4g+OBneOBru+8kVxu44K444OL5L+C5pWwOiAiLCByb3VuZChub3JtYWxfbG9yZW56JGdpbmksIDMpKSwKICAgICAgIHggPSAi5bm05Y+O77yI5LiH5YaG77yJIiwgeSA9ICLpoLvluqYiKQoKcDIgPC0gZ2dwbG90KCkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gbG9nbm9ybWFsX3NhbXBsZXMpLCBiaW5zID0gMjAsIGZpbGwgPSAic2FsbW9uIikgKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoIuW5tOWPjuWIhuW4g+OBneOBru+8klxu44K444OL5L+C5pWwOiAiLCByb3VuZChsb2dub3JtYWxfbG9yZW56JGdpbmksIDMpKSwKICAgICAgIHggPSAi5bm05Y+O77yI5LiH5YaG77yJIiwgeSA9ICLpoLvluqYiKQoKIyDjg63jg7zjg6zjg7Pjg4Tmm7Lnt5rjga7mj4/nlLsKZGYgPC0gZGF0YS5mcmFtZSh4MSA9YygwLCAxKSwgeTEgPSBjKDAsIDApLCB4MiA9IGMoMSwgMSksIHkyID0gYygwLCAxKSkKcDMgPC0gZ2dwbG90KCkgKwogIGdlb21fbGluZShhZXMoeCA9IG5vcm1hbF9sb3JlbnokY3VtX3BvcHVsYXRpb24sIHkgPSBub3JtYWxfbG9yZW56JGN1bV9mcmVxdWVuY2llcyksIGNvbG9yID0gImJsdWUiKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0geDEsIHkgPSB5MSwgeGVuZCA9IHgyLCB5ZW5kID0geTIpLCBkYXRhID0gZGYsIGxpbmV0eXBlPSJkb3R0ZWQiLCBjb2xvcj0iZm9yZXN0Z3JlZW4iLCBsaW5ld2lkdGggPSAxKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlMCgi44Ot44O844Os44Oz44OE5puy57ea77yI5bm05Y+O5YiG5biD44Gd44Gu77yR77yJXG7jgrjjg4vkv4LmlbA6ICIsIHJvdW5kKG5vcm1hbF9sb3JlbnokZ2luaSwgMykpLAogICAgICAgeCA9ICLntK/nqY3kurrmlbDjga7libLlkIgiLCB5ID0gIue0r+epjeW5tOWPjuOBruWJsuWQiCIpCnA0IDwtIGdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKHggPSBsb2dub3JtYWxfbG9yZW56JGN1bV9wb3B1bGF0aW9uLCB5ID0gbG9nbm9ybWFsX2xvcmVueiRjdW1fZnJlcXVlbmNpZXMpLCBjb2xvciA9ICJibHVlIikgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKwogIGdlb21fc2VnbWVudChhZXMoeCA9IHgxLCB5ID0geTEsIHhlbmQgPSB4MiwgeWVuZCA9IHkyKSwgZGF0YSA9IGRmLCBsaW5ldHlwZT0iZG90dGVkIiwgY29sb3I9ImZvcmVzdGdyZWVuIiwgbGluZXdpZHRoID0gMSkgKwogIGxhYnModGl0bGUgPSBwYXN0ZTAoIuODreODvOODrOODs+ODhOabsue3mu+8iOW5tOWPjuWIhuW4g+OBneOBru+8ku+8iVxu44K444OL5L+C5pWwOiAiLCByb3VuZChsb2dub3JtYWxfbG9yZW56JGdpbmksIDMpKSwKICAgICAgIHggPSAi57Sv56mN5Lq65pWw44Gu5Ymy5ZCIIiwgeSA9ICLntK/nqY3lubTlj47jga7libLlkIgiKQoKKHAxICsgcDIpIC8gKHAzICsgcDQpCmBgYAoKCgoKIyMjIDUuMy41IOOCv+OCpOODq+aMh+aVsOOCkuioiOeul+OBmeOCiwoKYGBge3J9CmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeSh0aWR5dmVyc2UpCgojIOOCt+ODvOODieOBruWbuuWumgpzZXQuc2VlZCgwKQoKIyDjg5Hjg6njg6Hjg7zjgr/oqK3lrpoKbiA8LSAxMAptZWFuX2luY29tZSA8LSA0MDAgICMg5bmz5Z2H5Y+O5YWlCm1lYW5fbG9nIDwtIGxvZyhtZWFuX2luY29tZSkgICMg5a++5pWw5q2j6KaP5YiG5biD44Gu5bmz5Z2H77yI44Ot44Kw44K544Kx44O844Or77yJCnN0ZF9kZXZfbG9nIDwtIDAuNSAgIyDlr77mlbDmraPopo/liIbluIPjga7mqJnmupblgY/lt67vvIjjg63jgrDjgrnjgrHjg7zjg6vvvIkKCiMg5a++5pWw5q2j6KaP5YiG5biD44GL44KJ5Y+O5YWl44KS44K144Oz44OX44Oq44Oz44KwCnVuZXF1YWxfaW5jb21lcyA8LSBybG5vcm0obiwgbWVhbmxvZyA9IG1lYW5fbG9nLCBzZGxvZyA9IHN0ZF9kZXZfbG9nKQoKIyDlrozlhajlnYfkuIDjgarlj47lhaXjgpLkvZzmiJAKbWVhbl9pbmNvbWUgPC0gbWVhbih1bmVxdWFsX2luY29tZXMpCmVxdWFsX2luY29tZXMgPC0gcmVwKG1lYW5faW5jb21lLCBuKQoKIyDjgr/jgqTjg6vmjIfmlbDvvIhUaGVpbCBJbmRleO+8ieOCkuioiOeul+OBmeOCi+mWouaVsApjYWxjX3RoZWlsX2luZGV4IDwtIGZ1bmN0aW9uKGluY29tZXMpIHsKICBtZWFuX2luY29tZSA8LSBtZWFuKGluY29tZXMpCiAgcmV0dXJuKHN1bSgoaW5jb21lcyAvIG1lYW5faW5jb21lKSAqIGxvZyhpbmNvbWVzIC8gbWVhbl9pbmNvbWUpKSAvIG4pCn0KCiMg44K/44Kk44Or5oyH5pWw44KS6KiI566XCnVuZXF1YWxfdGhlaWwgPC0gY2FsY190aGVpbF9pbmRleCh1bmVxdWFsX2luY29tZXMpICAjIOS4jeWdh+S4gOOBquWPjuWFpeOBruOCseODvOOCuQplcXVhbF90aGVpbCA8LSBjYWxjX3RoZWlsX2luZGV4KGVxdWFsX2luY29tZXMpICAjIOWdh+S4gOOBquWPjuWFpeOBruOCseODvOOCuQoKIyDjg5fjg63jg4Pjg4gKZGYgPC0gZGF0YS5mcmFtZSgKICBQZXJzb24gPSByZXAoTEVUVEVSU1sxOm5dLCAyKSwKICBJbmNvbWUgPSBjKHVuZXF1YWxfaW5jb21lcywgZXF1YWxfaW5jb21lcyksCiAgRGlzdHJpYnV0aW9uID0gZmFjdG9yKHJlcChjKCJVbmVxdWFsIiwgIkVxdWFsIiksIGVhY2ggPSBuKSwgbGV2ZWxzID0gYygiVW5lcXVhbCIsICJFcXVhbCIpKQopCgpkZiB8PgogIGdncGxvdChhZXMoeCA9IFBlcnNvbiwgeSA9IEluY29tZSwgZmlsbCA9IERpc3RyaWJ1dGlvbikpICsKICBnZW9tX2NvbCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJza3libHVlIiwgInNhbG1vbiIpKSArCiAgZmFjZXRfd3JhcCgKICAgIHZhcnMoRGlzdHJpYnV0aW9uKSwKICAgIGxhYmVsbGVyID0gYXNfbGFiZWxsZXIoYygKICAgICAgVW5lcXVhbCA9IHBhc3RlMCgi44Gw44KJ44Gk44GE44Gf5Y+O5YWl5YiG5biDICjjgr/jgqTjg6vmjIfmlbA6ICIsIGZvcm1hdChyb3VuZCh1bmVxdWFsX3RoZWlsLCA0KSwgbnNtYWxsID0gNCksICIpIiksCiAgICAgIEVxdWFsID0gcGFzdGUwKCLkuIDmp5jjgarlj47lhaXliIbluIMgKOOCv+OCpOODq+aMh+aVsDogIiwgZm9ybWF0KHJvdW5kKGVxdWFsX3RoZWlsLCA0KSwgbnNtYWxsID0gNCksICIpIikpKQogICAgKSArCiAgbGFicyh4ID0gIiIsIHkgPSAi5Y+O5YWlIFvkuIflhoZdIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgrnrKw156ug44Gv44GT44GT44G+44Gn44CCCgoK