Задание 1

Я возьму датасет mtcars. Установлю необходимые библиотеки и импортирую его.

library(reticulate)

py_install("pandas")
py_install("seaborn")

import pandas as pd
import seaborn as sns

df = pd.read_csv(
    "https://raw.githubusercontent.com/selva86/datasets/master/mtcars.csv"
)

Задание 2

Разведочный анализ

Общая информация:

print(df.shape)
print(df.info())
(32, 14)
<class 'pandas.DataFrame'> 
RangeIndex: 32 entries, 0 to 31 
Data columns (total 14 columns):  
 #   Column   Non-Null Count  Dtype 
---  ------   --------------  -----
 0   mpg      32 non-null     float64
 1   cyl      32 non-null     int64    
 2   disp     32 non-null     float64  
 3   hp       32 non-null     int64    
 4   drat     32 non-null     float64  
 5   wt       32 non-null     float64  
 6   qsec     32 non-null     float64  
 7   vs       32 non-null     int64    
 8   am       32 non-null     int64    
 9   gear     32 non-null     int64    
 10  carb     32 non-null     int64    
 11  fast     32 non-null     int64    
 12  cars     32 non-null     str      
 13  carname  32 non-null     str     
dtypes: float64(5), int64(7), str(2) 
memory usage: 3.6 KB 
None

Таким образом, имеем таблицу с 14 столбцами, 5 с вещественными значениями, 7 с целочисленными, 2 со строковыми.

Описательная статистика:

print(df.describe())
             mpg        cyl        disp  ...       gear     carb       fast
count  32.000000  32.000000   32.000000  ...  32.000000  32.0000  32.000000 
mean    4.434770   6.187500  230.721875  ...   3.687500   2.8125   0.687500 
std     0.661137   1.785922  123.938694  ...   0.737804   1.6152   0.470929 
min     3.224903   4.000000   71.100000  ...   3.000000   1.0000   0.000000 
25%     3.927432   4.000000  120.825000  ...   3.000000   2.0000   0.000000 
50%     4.381780   6.000000  196.300000  ...   4.000000   2.0000   1.000000 
75%     4.774935   8.000000  326.000000  ...   4.000000   4.0000   1.000000 
max     5.822371   8.000000  472.000000  ...   5.000000   8.0000   1.000000 

[8 rows x 12 columns]

Всего имеем 32 строки, показаны среднее, стандартное отклонение, квартили, минимумы и максимумы.

Проверка пропусков:

print(df.isnull().sum())

Как видно, пропусков нет.

Проаназирую конкретные показатели.

Исследую распредения расхода топлива mpg (сколько миль может проехать автомобиль на 1 галлоне топлива).

import matplotlib.pyplot as plt

plt.figure(figsize=(8,5))
sns.histplot(df["mpg"], bins=8, kde=True)

plt.title("Распределение расхода топлива")
plt.show()

Вывод: большинство автомобилей проезжают 4,5-4,8 миль за галлон.

Исследую зависимость расхода автомобиля от его массы.

plt.figure(figsize=(8,5))

sns.scatterplot(
    data=df,
    x="wt",
    y="mpg"
)

plt.title("Зависимость расхода топлива от массы автомобиля")
plt.show()

Вывод: с увеличением массы автомобиля ухудшается его расход.

Исследую связь кол-ва циллиндров с расходом топлива:

plt.figure(figsize=(8,5))

sns.boxplot(
    data=df,
    x="cyl",
    y="mpg"
)

plt.title("Расход топлива для автомобилей с разным числом цилиндров")
plt.show()

Вывод: чем меньше циллиндров, тем меньше расход.

Исследую корреляционную матрицу.

plt.figure(figsize=(12,10))

sns.heatmap(
    df.corr(numeric_only=True),
    annot=True,
    cmap="coolwarm",
    fmt=".2f"
)

plt.xticks(rotation=45, ha='right')
plt.yticks(rotation=0)

plt.show()

Вывод: расход (mpg) отрицательно коррелирует с массой (wt), расход (mpg) также отрицательно коррелирует с количеством лошадиных сил (hp), а лошадиных сил (hp) положительно коррелирует с количеством цилиндров (cyl).

Задание 3

Бустрэп - это метод исследования распределения статистик, основанный на многократной генерации выборок на базе имеющейся выборки.

Начну с реализации бутстрэпа на Python.

В моём датасете я разделю автомобили на две группы (с 4 цилиндрами и 8 цилиндрами), сравню расход топлива (mpg), и бутстрапом оценю доверительный интервал для разницы.

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# Загрузка данных
df = pd.read_csv(
    "https://raw.githubusercontent.com/selva86/datasets/master/mtcars.csv"
)

# Группы для сравнения
values_a = df[df["cyl"] == 4]["mpg"].values
values_b = df[df["cyl"] == 8]["mpg"].values

# Функция построения доверительного интервала
def get_percentile_ci(bootstrap_stats, alpha):
    
    left, right = np.quantile(
        bootstrap_stats,
        [alpha / 2, 1 - alpha / 2]
    )
    
    return left, right
    
# параметры бутстрапа
B = 10000
alpha = 0.05

# разница средних
pe = np.mean(values_b) - np.mean(values_a)

bootstrap_stats = []

for _ in range(B):
    
    sample_a = np.random.choice(
        values_a,
        size=len(values_a),
        replace=True
    )
    
    sample_b = np.random.choice(
        values_b,
        size=len(values_b),
        replace=True
    )
    
    stat = np.mean(sample_b) - np.mean(sample_a)
    
    bootstrap_stats.append(stat)
    
bootstrap_stats = np.array(bootstrap_stats)

ci = get_percentile_ci(
    bootstrap_stats,
    alpha
)

has_effect = not (ci[0] < 0 < ci[1])

print(f"Среднее mpg для 4 цилиндров:, {np.mean(values_a):.2f}")
print(f"Среднее mpg для 8 цилиндров: {np.mean(values_b):.2f}")
print(f"Разница средних mpg: {pe:.2f}")
print(f"95% ДИ: ({ci[0]:.2f}; {ci[1]:.2f})")
print(f"Различия статистически значимы: {has_effect}")

print(f"\nДополнительная информация:")
print(f"Стандартная ошибка (SE): {bootstrap_stats.std():.3f}")
print(f"Смещение (bias): {bootstrap_stats.mean() - pe:.3f}")
print(f"Коэффициент вариации: {bootstrap_stats.std() / abs(pe):.3f}")
Среднее mpg для 4 цилиндров:, 5.15
Среднее mpg для 8 цилиндров: 3.87
Разница средних mpg: -1.27
95% ДИ: (-1.57; -0.99)
Различия статистически значимы: True

Дополнительная информация:
Стандартная ошибка (SE): 0.151
Смещение (bias): 0.000
Коэффициент вариации: 0.118
plt.figure(figsize=(8,6))

sns.boxplot(
    data=df[df["cyl"].isin([4, 8])],
    x="cyl",
    y="mpg"
)

plt.title(
    "Расход топлива у автомобилей с 4 и 8 цилиндрами"
)

plt.xlabel("Число цилиндров")

plt.ylabel("MPG")

plt.show()

Вывод: В качестве исследовалась переменная mpg. Выполнено 10000 бутстрап-перевыборок с возвращением, после чего для каждой выборки вычислялась разность средних значений.

Полученная оценка разности средних составила -1.27. Построенный 95%-й доверительный интервал равен (-1.57; -0.99). Поскольку доверительный интервал не содержит нулевое значение, различия между группами являются статистически значимыми.

Стандартная ошибка оценки составила 0.151, что свидетельствует о достаточно высокой устойчивости результата. Смещение (bias) отсутствует и равно 0.000, следовательно бутстрап-оценка не демонстрирует систематического отклонения. Коэффициент вариации составил 0.118, что указывает на умеренную изменчивость полученных оценок.

Задание 4

Реализую теперь бутстрэп на R.

Линейная регрессия позволяет оценить зависимость одной переменной от другой.

Коэффициент детерминации R^2 показывает, какую долю вариации зависимой переменной объясняет модель.

Бутстрап позволяет оценить устойчивость оценки R^2, стандартную ошибку, а также доверительный интервал.

Производить буду бутстрапирование коэффициента детерминации для модели mpg~disp.

set.seed(0)

library(boot)

# функция расчета R^2
rsq_function <- function(formula, data, indices){

  d <- data[indices, ]

  fit <- lm(formula, data = d)

  return(summary(fit)$r.squared)
}

# бутстрап
reps <- boot(
  data = mtcars,
  statistic = rsq_function,
  R = 5000,
  formula = mpg ~ disp
)

reps

ORDINARY NONPARAMETRIC BOOTSTRAP


Call:
boot(data = mtcars, statistic = rsq_function, R = 5000, formula = mpg ~ 
    disp)


Bootstrap Statistics :
     original      bias    std. error
t1* 0.7183433 0.003270593  0.06413799
ORDINARY NONPARAMETRIC BOOTSTRAP   

Call: 
boot(data = mtcars, statistic = rsq_function, R = 5000, formula = mpg ~
    disp)   

Bootstrap Statistics :
      original      bias    std. error
 t1* 0.7183433 0.003270593  0.06413799

График №1. Распределение бутстрап-оценок

hist(
  reps$t,
  breaks = 30,
  col = "lightblue",
  main = "Bootstrap distribution of R²",
  xlab = "R²"
)

abline(
  v = reps$t0,
  col = "red",
  lwd = 3
)

Красная линия показывает исходное значение R^2.
Красная линия показывает исходное значение R^2.

График №2. QQ-plot

qqnorm(reps$t)

qqline(
  reps$t,
  col = "red",
  lwd = 2
)

График показывает близость распределения бутстрап-оценок к нормальному.

Выведу доверительный интервал и дополнительные показатели.

boot.ci(
  reps,
  type = "bca"
)

bias <- mean(reps$t) - reps$t0

se <- sd(reps$t)

cv <- se / mean(reps$t)

cat("Bias =", bias, "\n")
cat("SE =", se, "\n")
cat("CV =", cv, "\n")
BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 5000 bootstrap replicates  

CALL :  
boot.ci(boot.out = reps, type = "bca")  

Intervals :  
Level       BCa           
95%   ( 0.5499,  0.8195 ) 

Calculations and Intervals on Original Scale

Bias = 0.003270593  
SE = 0.06413799  
CV = 0.08888131 

Вывод

Для модели mpg∼disp выполнено 5000 бутстрап-перевыборок. Получен 95%-й BCa-доверительный интервал для коэффициента детерминации R^2: (0.5499; 0.8195). Смещение мало (Bias = 0.0033), стандартная ошибка составляет 0.0641. Это подтверждает устойчивость оценки и наличие достаточно сильной связи между объёмом двигателя и расходом топлива.

Выводы по работе

Проведён разведочный анализ датасета автомобилей и реализован метод бутстрапа в Python и R. В Python подтверждены статистически значимые различия между исследуемыми группами автомобилей. В R оценена устойчивость коэффициента детерминации регрессионной модели и построен доверительный интервал для R^2. Результаты показывают, что бутстрап позволяет надёжно оценивать статистические характеристики и их доверительные интервалы без предположения о нормальности исходных данных.

LS0tDQp0aXRsZTogItCQ0L3QsNC70LjQtyDQtNCw0L3QvdGL0YUsINC70LDQsdC+0YDQsNGC0L7RgNC90LDRjyDRgNCw0LHQvtGC0LAg4oSWNi4g0KDQtdCw0LvQuNC30LDRhtC40Y8g0LHRg9GC0YHRgtGA0LDQv9CwINC90LAg0Y/Qt9GL0LrQsNGFIFIg0LggUHl0aG9uIg0KYXV0aG9yOiAi0KHQvNC10LvQutC+0LIg0J7Qu9C10LMsIDIzMS0zMzMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyDQl9Cw0LTQsNC90LjQtSAxDQoNCtCvINCy0L7Qt9GM0LzRgyDQtNCw0YLQsNGB0LXRgiBtdGNhcnMuINCj0YHRgtCw0L3QvtCy0LvRjiDQvdC10L7QsdGF0L7QtNC40LzRi9C1INCx0LjQsdC70LjQvtGC0LXQutC4INC4INC40LzQv9C+0YDRgtC40YDRg9GOINC10LPQvi4NCg0KYGBge3J9DQpsaWJyYXJ5KHJldGljdWxhdGUpDQoNCnB5X2luc3RhbGwoInBhbmRhcyIpDQpweV9pbnN0YWxsKCJzZWFib3JuIikNCmBgYA0KDQpgYGB7cHl0aG9ufQ0KDQppbXBvcnQgcGFuZGFzIGFzIHBkDQppbXBvcnQgc2VhYm9ybiBhcyBzbnMNCg0KZGYgPSBwZC5yZWFkX2NzdigNCiAgICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3NlbHZhODYvZGF0YXNldHMvbWFzdGVyL210Y2Fycy5jc3YiDQopDQpgYGANCg0KIyMg0JfQsNC00LDQvdC40LUgMg0KDQojIyMg0KDQsNC30LLQtdC00L7Rh9C90YvQuSDQsNC90LDQu9C40LcNCg0K0J7QsdGJ0LDRjyDQuNC90YTQvtGA0LzQsNGG0LjRjzoNCg0KYGBge3B5dGhvbn0NCnByaW50KGRmLnNoYXBlKQ0KcHJpbnQoZGYuaW5mbygpKQ0KYGBgDQoNCmBgYCAgICAgICAgIA0KKDMyLCAxNCkNCjxjbGFzcyAncGFuZGFzLkRhdGFGcmFtZSc+IA0KUmFuZ2VJbmRleDogMzIgZW50cmllcywgMCB0byAzMSANCkRhdGEgY29sdW1ucyAodG90YWwgMTQgY29sdW1ucyk6ICANCiAjICAgQ29sdW1uICAgTm9uLU51bGwgQ291bnQgIER0eXBlIA0KLS0tICAtLS0tLS0gICAtLS0tLS0tLS0tLS0tLSAgLS0tLS0NCiAwICAgbXBnICAgICAgMzIgbm9uLW51bGwgICAgIGZsb2F0NjQNCiAxICAgY3lsICAgICAgMzIgbm9uLW51bGwgICAgIGludDY0ICAgIA0KIDIgICBkaXNwICAgICAzMiBub24tbnVsbCAgICAgZmxvYXQ2NCAgDQogMyAgIGhwICAgICAgIDMyIG5vbi1udWxsICAgICBpbnQ2NCAgICANCiA0ICAgZHJhdCAgICAgMzIgbm9uLW51bGwgICAgIGZsb2F0NjQgIA0KIDUgICB3dCAgICAgICAzMiBub24tbnVsbCAgICAgZmxvYXQ2NCAgDQogNiAgIHFzZWMgICAgIDMyIG5vbi1udWxsICAgICBmbG9hdDY0ICANCiA3ICAgdnMgICAgICAgMzIgbm9uLW51bGwgICAgIGludDY0ICAgIA0KIDggICBhbSAgICAgICAzMiBub24tbnVsbCAgICAgaW50NjQgICAgDQogOSAgIGdlYXIgICAgIDMyIG5vbi1udWxsICAgICBpbnQ2NCAgICANCiAxMCAgY2FyYiAgICAgMzIgbm9uLW51bGwgICAgIGludDY0ICAgIA0KIDExICBmYXN0ICAgICAzMiBub24tbnVsbCAgICAgaW50NjQgICAgDQogMTIgIGNhcnMgICAgIDMyIG5vbi1udWxsICAgICBzdHIgICAgICANCiAxMyAgY2FybmFtZSAgMzIgbm9uLW51bGwgICAgIHN0ciAgICAgDQpkdHlwZXM6IGZsb2F0NjQoNSksIGludDY0KDcpLCBzdHIoMikgDQptZW1vcnkgdXNhZ2U6IDMuNiBLQiANCk5vbmUNCmBgYA0KDQrQotCw0LrQuNC8INC+0LHRgNCw0LfQvtC8LCDQuNC80LXQtdC8INGC0LDQsdC70LjRhtGDINGBIDE0INGB0YLQvtC70LHRhtCw0LzQuCwgNSDRgSDQstC10YnQtdGB0YLQstC10L3QvdGL0LzQuCDQt9C90LDRh9C10L3QuNGP0LzQuCwgNyDRgSDRhtC10LvQvtGH0LjRgdC70LXQvdC90YvQvNC4LCAyINGB0L4g0YHRgtGA0L7QutC+0LLRi9C80LguDQoNCtCe0L/QuNGB0LDRgtC10LvRjNC90LDRjyDRgdGC0LDRgtC40YHRgtC40LrQsDoNCg0KYGBge3B5dGhvbn0NCnByaW50KGRmLmRlc2NyaWJlKCkpDQpgYGANCg0KYGBgICAgICAgICAgDQogICAgICAgICAgICAgbXBnICAgICAgICBjeWwgICAgICAgIGRpc3AgIC4uLiAgICAgICBnZWFyICAgICBjYXJiICAgICAgIGZhc3QNCmNvdW50ICAzMi4wMDAwMDAgIDMyLjAwMDAwMCAgIDMyLjAwMDAwMCAgLi4uICAzMi4wMDAwMDAgIDMyLjAwMDAgIDMyLjAwMDAwMCANCm1lYW4gICAgNC40MzQ3NzAgICA2LjE4NzUwMCAgMjMwLjcyMTg3NSAgLi4uICAgMy42ODc1MDAgICAyLjgxMjUgICAwLjY4NzUwMCANCnN0ZCAgICAgMC42NjExMzcgICAxLjc4NTkyMiAgMTIzLjkzODY5NCAgLi4uICAgMC43Mzc4MDQgICAxLjYxNTIgICAwLjQ3MDkyOSANCm1pbiAgICAgMy4yMjQ5MDMgICA0LjAwMDAwMCAgIDcxLjEwMDAwMCAgLi4uICAgMy4wMDAwMDAgICAxLjAwMDAgICAwLjAwMDAwMCANCjI1JSAgICAgMy45Mjc0MzIgICA0LjAwMDAwMCAgMTIwLjgyNTAwMCAgLi4uICAgMy4wMDAwMDAgICAyLjAwMDAgICAwLjAwMDAwMCANCjUwJSAgICAgNC4zODE3ODAgICA2LjAwMDAwMCAgMTk2LjMwMDAwMCAgLi4uICAgNC4wMDAwMDAgICAyLjAwMDAgICAxLjAwMDAwMCANCjc1JSAgICAgNC43NzQ5MzUgICA4LjAwMDAwMCAgMzI2LjAwMDAwMCAgLi4uICAgNC4wMDAwMDAgICA0LjAwMDAgICAxLjAwMDAwMCANCm1heCAgICAgNS44MjIzNzEgICA4LjAwMDAwMCAgNDcyLjAwMDAwMCAgLi4uICAgNS4wMDAwMDAgICA4LjAwMDAgICAxLjAwMDAwMCANCg0KWzggcm93cyB4IDEyIGNvbHVtbnNdDQpgYGANCg0K0JLRgdC10LPQviDQuNC80LXQtdC8IDMyINGB0YLRgNC+0LrQuCwg0L/QvtC60LDQt9Cw0L3RiyDRgdGA0LXQtNC90LXQtSwg0YHRgtCw0L3QtNCw0YDRgtC90L7QtSDQvtGC0LrQu9C+0L3QtdC90LjQtSwg0LrQstCw0YDRgtC40LvQuCwg0LzQuNC90LjQvNGD0LzRiyDQuCDQvNCw0LrRgdC40LzRg9C80YsuDQoNCtCf0YDQvtCy0LXRgNC60LAg0L/RgNC+0L/Rg9GB0LrQvtCyOg0KDQpgYGB7cHl0aG9ufQ0KcHJpbnQoZGYuaXNudWxsKCkuc3VtKCkpDQpgYGANCg0K0JrQsNC6INCy0LjQtNC90L4sINC/0YDQvtC/0YPRgdC60L7QsiDQvdC10YIuDQoNCtCf0YDQvtCw0L3QsNC30LjRgNGD0Y4g0LrQvtC90LrRgNC10YLQvdGL0LUg0L/QvtC60LDQt9Cw0YLQtdC70LguDQoNCtCY0YHRgdC70LXQtNGD0Y4g0YDQsNGB0L/RgNC10LTQtdC90LjRjyDRgNCw0YHRhdC+0LTQsCDRgtC+0L/Qu9C40LLQsCBtcGcgKNGB0LrQvtC70YzQutC+INC80LjQu9GMINC80L7QttC10YIg0L/RgNC+0LXRhdCw0YLRjCDQsNCy0YLQvtC80L7QsdC40LvRjCDQvdCwIDEg0LPQsNC70LvQvtC90LUg0YLQvtC/0LvQuNCy0LApLg0KDQpgYGB7cHl0aG9ufQ0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KDQpwbHQuZmlndXJlKGZpZ3NpemU9KDgsNSkpDQpzbnMuaGlzdHBsb3QoZGZbIm1wZyJdLCBiaW5zPTgsIGtkZT1UcnVlKQ0KDQpwbHQudGl0bGUoItCg0LDRgdC/0YDQtdC00LXQu9C10L3QuNC1INGA0LDRgdGF0L7QtNCwINGC0L7Qv9C70LjQstCwIikNCnBsdC5zaG93KCkNCmBgYA0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0xMTY1OTI0ODcwLnBuZykNCg0KKirQktGL0LLQvtC0OioqINCx0L7Qu9GM0YjQuNC90YHRgtCy0L4g0LDQstGC0L7QvNC+0LHQuNC70LXQuSDQv9GA0L7QtdC30LbQsNGO0YIgNCw1LTQsOCDQvNC40LvRjCDQt9CwINCz0LDQu9C70L7QvS4NCg0K0JjRgdGB0LvQtdC00YPRjiDQt9Cw0LLQuNGB0LjQvNC+0YHRgtGMINGA0LDRgdGF0L7QtNCwINCw0LLRgtC+0LzQvtCx0LjQu9GPINC+0YIg0LXQs9C+INC80LDRgdGB0YsuDQoNCmBgYHtweXRob259DQpwbHQuZmlndXJlKGZpZ3NpemU9KDgsNSkpDQoNCnNucy5zY2F0dGVycGxvdCgNCiAgICBkYXRhPWRmLA0KICAgIHg9Ind0IiwNCiAgICB5PSJtcGciDQopDQoNCnBsdC50aXRsZSgi0JfQsNCy0LjRgdC40LzQvtGB0YLRjCDRgNCw0YHRhdC+0LTQsCDRgtC+0L/Qu9C40LLQsCDQvtGCINC80LDRgdGB0Ysg0LDQstGC0L7QvNC+0LHQuNC70Y8iKQ0KcGx0LnNob3coKQ0KYGBgDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTE5MDIxODkzOTUucG5nKQ0KDQoqKtCS0YvQstC+0LQ6Kiog0YEg0YPQstC10LvQuNGH0LXQvdC40LXQvCDQvNCw0YHRgdGLINCw0LLRgtC+0LzQvtCx0LjQu9GPINGD0YXRg9C00YjQsNC10YLRgdGPINC10LPQviDRgNCw0YHRhdC+0LQuDQoNCtCY0YHRgdC70LXQtNGD0Y4g0YHQstGP0LfRjCDQutC+0Lst0LLQsCDRhtC40LvQu9C40L3QtNGA0L7QsiDRgSDRgNCw0YHRhdC+0LTQvtC8INGC0L7Qv9C70LjQstCwOg0KDQpgYGB7cHl0aG9ufQ0KcGx0LmZpZ3VyZShmaWdzaXplPSg4LDUpKQ0KDQpzbnMuYm94cGxvdCgNCiAgICBkYXRhPWRmLA0KICAgIHg9ImN5bCIsDQogICAgeT0ibXBnIg0KKQ0KDQpwbHQudGl0bGUoItCg0LDRgdGF0L7QtCDRgtC+0L/Qu9C40LLQsCDQtNC70Y8g0LDQstGC0L7QvNC+0LHQuNC70LXQuSDRgSDRgNCw0LfQvdGL0Lwg0YfQuNGB0LvQvtC8INGG0LjQu9C40L3QtNGA0L7QsiIpDQpwbHQuc2hvdygpDQpgYGANCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMzIzNDYwNTc2OC5wbmcpDQoNCioq0JLRi9Cy0L7QtDoqKiDRh9C10Lwg0LzQtdC90YzRiNC1INGG0LjQu9C70LjQvdC00YDQvtCyLCDRgtC10Lwg0LzQtdC90YzRiNC1INGA0LDRgdGF0L7QtC4NCg0K0JjRgdGB0LvQtdC00YPRjiDQutC+0YDRgNC10LvRj9GG0LjQvtC90L3Rg9GOINC80LDRgtGA0LjRhtGDLg0KDQpgYGB7cHl0aG9ufQ0KcGx0LmZpZ3VyZShmaWdzaXplPSgxMiwxMCkpDQoNCnNucy5oZWF0bWFwKA0KICAgIGRmLmNvcnIobnVtZXJpY19vbmx5PVRydWUpLA0KICAgIGFubm90PVRydWUsDQogICAgY21hcD0iY29vbHdhcm0iLA0KICAgIGZtdD0iLjJmIg0KKQ0KDQpwbHQueHRpY2tzKHJvdGF0aW9uPTQ1LCBoYT0ncmlnaHQnKQ0KcGx0Lnl0aWNrcyhyb3RhdGlvbj0wKQ0KDQpwbHQuc2hvdygpDQpgYGANCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMjc0MDE4ODYyMC5wbmcpDQoNCioq0JLRi9Cy0L7QtDoqKiDRgNCw0YHRhdC+0LQgKG1wZykg0L7RgtGA0LjRhtCw0YLQtdC70YzQvdC+INC60L7RgNGA0LXQu9C40YDRg9C10YIg0YEg0LzQsNGB0YHQvtC5ICh3dCksINGA0LDRgdGF0L7QtCAobXBnKSDRgtCw0LrQttC1INC+0YLRgNC40YbQsNGC0LXQu9GM0L3QviDQutC+0YDRgNC10LvQuNGA0YPQtdGCINGBINC60L7Qu9C40YfQtdGB0YLQstC+0Lwg0LvQvtGI0LDQtNC40L3Ri9GFINGB0LjQuyAoaHApLCDQsCDQu9C+0YjQsNC00LjQvdGL0YUg0YHQuNC7IChocCkg0L/QvtC70L7QttC40YLQtdC70YzQvdC+INC60L7RgNGA0LXQu9C40YDRg9C10YIg0YEg0LrQvtC70LjRh9C10YHRgtCy0L7QvCDRhtC40LvQuNC90LTRgNC+0LIgKGN5bCkuDQoNCiMjINCX0LDQtNCw0L3QuNC1IDMNCg0KKirQkdGD0YHRgtGA0Y3QvyoqIC0g0Y3RgtC+INC80LXRgtC+0LQg0LjRgdGB0LvQtdC00L7QstCw0L3QuNGPINGA0LDRgdC/0YDQtdC00LXQu9C10L3QuNGPINGB0YLQsNGC0LjRgdGC0LjQuiwg0L7RgdC90L7QstCw0L3QvdGL0Lkg0L3QsCDQvNC90L7Qs9C+0LrRgNCw0YLQvdC+0Lkg0LPQtdC90LXRgNCw0YbQuNC4INCy0YvQsdC+0YDQvtC6INC90LAg0LHQsNC30LUg0LjQvNC10Y7RidC10LnRgdGPINCy0YvQsdC+0YDQutC4Lg0KDQrQndCw0YfQvdGDINGBINGA0LXQsNC70LjQt9Cw0YbQuNC4INCx0YPRgtGB0YLRgNGN0L/QsCDQvdCwIFB5dGhvbi4NCg0K0JIg0LzQvtGR0Lwg0LTQsNGC0LDRgdC10YLQtSDRjyDRgNCw0LfQtNC10LvRjiDQsNCy0YLQvtC80L7QsdC40LvQuCDQvdCwINC00LLQtSDQs9GA0YPQv9C/0YsgKNGBIDQg0YbQuNC70LjQvdC00YDQsNC80Lgg0LggOCDRhtC40LvQuNC90LTRgNCw0LzQuCksINGB0YDQsNCy0L3RjiDRgNCw0YHRhdC+0LQg0YLQvtC/0LvQuNCy0LAgKG1wZyksINC4INCx0YPRgtGB0YLRgNCw0L/QvtC8INC+0YbQtdC90Y4g0LTQvtCy0LXRgNC40YLQtdC70YzQvdGL0Lkg0LjQvdGC0LXRgNCy0LDQuyDQtNC70Y8g0YDQsNC30L3QuNGG0YsuDQoNCmBgYHtweXRob259DQppbXBvcnQgbnVtcHkgYXMgbnANCmltcG9ydCBwYW5kYXMgYXMgcGQNCmltcG9ydCBzZWFib3JuIGFzIHNucw0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KDQojINCX0LDQs9GA0YPQt9C60LAg0LTQsNC90L3Ri9GFDQpkZiA9IHBkLnJlYWRfY3N2KA0KICAgICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc2VsdmE4Ni9kYXRhc2V0cy9tYXN0ZXIvbXRjYXJzLmNzdiINCikNCg0KIyDQk9GA0YPQv9C/0Ysg0LTQu9GPINGB0YDQsNCy0L3QtdC90LjRjw0KdmFsdWVzX2EgPSBkZltkZlsiY3lsIl0gPT0gNF1bIm1wZyJdLnZhbHVlcw0KdmFsdWVzX2IgPSBkZltkZlsiY3lsIl0gPT0gOF1bIm1wZyJdLnZhbHVlcw0KDQojINCk0YPQvdC60YbQuNGPINC/0L7RgdGC0YDQvtC10L3QuNGPINC00L7QstC10YDQuNGC0LXQu9GM0L3QvtCz0L4g0LjQvdGC0LXRgNCy0LDQu9CwDQpkZWYgZ2V0X3BlcmNlbnRpbGVfY2koYm9vdHN0cmFwX3N0YXRzLCBhbHBoYSk6DQoNCiAgICBsZWZ0LCByaWdodCA9IG5wLnF1YW50aWxlKA0KICAgICAgICBib290c3RyYXBfc3RhdHMsDQogICAgICAgIFthbHBoYSAvIDIsIDEgLSBhbHBoYSAvIDJdDQogICAgKQ0KDQogICAgcmV0dXJuIGxlZnQsIHJpZ2h0DQoNCiMg0L/QsNGA0LDQvNC10YLRgNGLINCx0YPRgtGB0YLRgNCw0L/QsA0KQiA9IDEwMDAwDQphbHBoYSA9IDAuMDUNCg0KIyDRgNCw0LfQvdC40YbQsCDRgdGA0LXQtNC90LjRhQ0KcGUgPSBucC5tZWFuKHZhbHVlc19iKSAtIG5wLm1lYW4odmFsdWVzX2EpDQoNCmJvb3RzdHJhcF9zdGF0cyA9IFtdDQoNCmZvciBfIGluIHJhbmdlKEIpOg0KDQogICAgc2FtcGxlX2EgPSBucC5yYW5kb20uY2hvaWNlKA0KICAgICAgICB2YWx1ZXNfYSwNCiAgICAgICAgc2l6ZT1sZW4odmFsdWVzX2EpLA0KICAgICAgICByZXBsYWNlPVRydWUNCiAgICApDQoNCiAgICBzYW1wbGVfYiA9IG5wLnJhbmRvbS5jaG9pY2UoDQogICAgICAgIHZhbHVlc19iLA0KICAgICAgICBzaXplPWxlbih2YWx1ZXNfYiksDQogICAgICAgIHJlcGxhY2U9VHJ1ZQ0KICAgICkNCg0KICAgIHN0YXQgPSBucC5tZWFuKHNhbXBsZV9iKSAtIG5wLm1lYW4oc2FtcGxlX2EpDQoNCiAgICBib290c3RyYXBfc3RhdHMuYXBwZW5kKHN0YXQpDQoNCmJvb3RzdHJhcF9zdGF0cyA9IG5wLmFycmF5KGJvb3RzdHJhcF9zdGF0cykNCg0KY2kgPSBnZXRfcGVyY2VudGlsZV9jaSgNCiAgICBib290c3RyYXBfc3RhdHMsDQogICAgYWxwaGENCikNCg0KaGFzX2VmZmVjdCA9IG5vdCAoY2lbMF0gPCAwIDwgY2lbMV0pDQoNCnByaW50KGYi0KHRgNC10LTQvdC10LUgbXBnINC00LvRjyA0INGG0LjQu9C40L3QtNGA0L7QsjosIHtucC5tZWFuKHZhbHVlc19hKTouMmZ9IikNCnByaW50KGYi0KHRgNC10LTQvdC10LUgbXBnINC00LvRjyA4INGG0LjQu9C40L3QtNGA0L7Qsjoge25wLm1lYW4odmFsdWVzX2IpOi4yZn0iKQ0KcHJpbnQoZiLQoNCw0LfQvdC40YbQsCDRgdGA0LXQtNC90LjRhSBtcGc6IHtwZTouMmZ9IikNCnByaW50KGYiOTUlINCU0Jg6ICh7Y2lbMF06LjJmfTsge2NpWzFdOi4yZn0pIikNCnByaW50KGYi0KDQsNC30LvQuNGH0LjRjyDRgdGC0LDRgtC40YHRgtC40YfQtdGB0LrQuCDQt9C90LDRh9C40LzRizoge2hhc19lZmZlY3R9IikNCg0KcHJpbnQoZiJcbtCU0L7Qv9C+0LvQvdC40YLQtdC70YzQvdCw0Y8g0LjQvdGE0L7RgNC80LDRhtC40Y86IikNCnByaW50KGYi0KHRgtCw0L3QtNCw0YDRgtC90LDRjyDQvtGI0LjQsdC60LAgKFNFKToge2Jvb3RzdHJhcF9zdGF0cy5zdGQoKTouM2Z9IikNCnByaW50KGYi0KHQvNC10YnQtdC90LjQtSAoYmlhcyk6IHtib290c3RyYXBfc3RhdHMubWVhbigpIC0gcGU6LjNmfSIpDQpwcmludChmItCa0L7RjdGE0YTQuNGG0LjQtdC90YIg0LLQsNGA0LjQsNGG0LjQuDoge2Jvb3RzdHJhcF9zdGF0cy5zdGQoKSAvIGFicyhwZSk6LjNmfSIpDQpgYGANCg0KYGBgICAgICAgICAgDQrQodGA0LXQtNC90LXQtSBtcGcg0LTQu9GPIDQg0YbQuNC70LjQvdC00YDQvtCyOiwgNS4xNQ0K0KHRgNC10LTQvdC10LUgbXBnINC00LvRjyA4INGG0LjQu9C40L3QtNGA0L7QsjogMy44Nw0K0KDQsNC30L3QuNGG0LAg0YHRgNC10LTQvdC40YUgbXBnOiAtMS4yNw0KOTUlINCU0Jg6ICgtMS41NzsgLTAuOTkpDQrQoNCw0LfQu9C40YfQuNGPINGB0YLQsNGC0LjRgdGC0LjRh9C10YHQutC4INC30L3QsNGH0LjQvNGLOiBUcnVlDQoNCtCU0L7Qv9C+0LvQvdC40YLQtdC70YzQvdCw0Y8g0LjQvdGE0L7RgNC80LDRhtC40Y86DQrQodGC0LDQvdC00LDRgNGC0L3QsNGPINC+0YjQuNCx0LrQsCAoU0UpOiAwLjE1MQ0K0KHQvNC10YnQtdC90LjQtSAoYmlhcyk6IDAuMDAwDQrQmtC+0Y3RhNGE0LjRhtC40LXQvdGCINCy0LDRgNC40LDRhtC40Lg6IDAuMTE4DQpgYGANCg0KYGBge3B5dGhvbn0NCnBsdC5maWd1cmUoZmlnc2l6ZT0oOCw2KSkNCg0Kc25zLmJveHBsb3QoDQogICAgZGF0YT1kZltkZlsiY3lsIl0uaXNpbihbNCwgOF0pXSwNCiAgICB4PSJjeWwiLA0KICAgIHk9Im1wZyINCikNCg0KcGx0LnRpdGxlKA0KICAgICLQoNCw0YHRhdC+0LQg0YLQvtC/0LvQuNCy0LAg0YMg0LDQstGC0L7QvNC+0LHQuNC70LXQuSDRgSA0INC4IDgg0YbQuNC70LjQvdC00YDQsNC80LgiDQopDQoNCnBsdC54bGFiZWwoItCn0LjRgdC70L4g0YbQuNC70LjQvdC00YDQvtCyIikNCg0KcGx0LnlsYWJlbCgiTVBHIikNCg0KcGx0LnNob3coKQ0KYGBgDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTMzODYyNDY0MjEucG5nKQ0KDQoqKtCS0YvQstC+0LQ6Kiog0JIg0LrQsNGH0LXRgdGC0LLQtSDQuNGB0YHQu9C10LTQvtCy0LDQu9Cw0YHRjCDQv9C10YDQtdC80LXQvdC90LDRjyBtcGcuINCS0YvQv9C+0LvQvdC10L3QviAxMDAwMCDQsdGD0YLRgdGC0YDQsNC/LdC/0LXRgNC10LLRi9Cx0L7RgNC+0Log0YEg0LLQvtC30LLRgNCw0YnQtdC90LjQtdC8LCDQv9C+0YHQu9C1INGH0LXQs9C+INC00LvRjyDQutCw0LbQtNC+0Lkg0LLRi9Cx0L7RgNC60Lgg0LLRi9GH0LjRgdC70Y/Qu9Cw0YHRjCDRgNCw0LfQvdC+0YHRgtGMINGB0YDQtdC00L3QuNGFINC30L3QsNGH0LXQvdC40LkuDQoNCtCf0L7Qu9GD0YfQtdC90L3QsNGPINC+0YbQtdC90LrQsCDRgNCw0LfQvdC+0YHRgtC4INGB0YDQtdC00L3QuNGFINGB0L7RgdGC0LDQstC40LvQsCAtMS4yNy4g0J/QvtGB0YLRgNC+0LXQvdC90YvQuSA5NSUt0Lkg0LTQvtCy0LXRgNC40YLQtdC70YzQvdGL0Lkg0LjQvdGC0LXRgNCy0LDQuyDRgNCw0LLQtdC9ICgtMS41NzsgLTAuOTkpLiDQn9C+0YHQutC+0LvRjNC60YMg0LTQvtCy0LXRgNC40YLQtdC70YzQvdGL0Lkg0LjQvdGC0LXRgNCy0LDQuyDQvdC1INGB0L7QtNC10YDQttC40YIg0L3Rg9C70LXQstC+0LUg0LfQvdCw0YfQtdC90LjQtSwg0YDQsNC30LvQuNGH0LjRjyDQvNC10LbQtNGDINCz0YDRg9C/0L/QsNC80Lgg0Y/QstC70Y/RjtGC0YHRjyDRgdGC0LDRgtC40YHRgtC40YfQtdGB0LrQuCDQt9C90LDRh9C40LzRi9C80LguDQoNCtCh0YLQsNC90LTQsNGA0YLQvdCw0Y8g0L7RiNC40LHQutCwINC+0YbQtdC90LrQuCDRgdC+0YHRgtCw0LLQuNC70LAgMC4xNTEsINGH0YLQviDRgdCy0LjQtNC10YLQtdC70YzRgdGC0LLRg9C10YIg0L4g0LTQvtGB0YLQsNGC0L7Rh9C90L4g0LLRi9GB0L7QutC+0Lkg0YPRgdGC0L7QudGH0LjQstC+0YHRgtC4INGA0LXQt9GD0LvRjNGC0LDRgtCwLiDQodC80LXRidC10L3QuNC1IChiaWFzKSDQvtGC0YHRg9GC0YHRgtCy0YPQtdGCINC4INGA0LDQstC90L4gMC4wMDAsINGB0LvQtdC00L7QstCw0YLQtdC70YzQvdC+INCx0YPRgtGB0YLRgNCw0L8t0L7RhtC10L3QutCwINC90LUg0LTQtdC80L7QvdGB0YLRgNC40YDRg9C10YIg0YHQuNGB0YLQtdC80LDRgtC40YfQtdGB0LrQvtCz0L4g0L7RgtC60LvQvtC90LXQvdC40Y8uINCa0L7RjdGE0YTQuNGG0LjQtdC90YIg0LLQsNGA0LjQsNGG0LjQuCDRgdC+0YHRgtCw0LLQuNC7IDAuMTE4LCDRh9GC0L4g0YPQutCw0LfRi9Cy0LDQtdGCINC90LAg0YPQvNC10YDQtdC90L3Rg9GOINC40LfQvNC10L3Rh9C40LLQvtGB0YLRjCDQv9C+0LvRg9GH0LXQvdC90YvRhSDQvtGG0LXQvdC+0LouDQoNCiMjINCX0LDQtNCw0L3QuNC1IDQNCg0K0KDQtdCw0LvQuNC30YPRjiDRgtC10L/QtdGA0Ywg0LHRg9GC0YHRgtGA0Y3QvyDQvdCwIFIuDQoNCtCb0LjQvdC10LnQvdCw0Y8g0YDQtdCz0YDQtdGB0YHQuNGPINC/0L7Qt9Cy0L7Qu9GP0LXRgiDQvtGG0LXQvdC40YLRjCDQt9Cw0LLQuNGB0LjQvNC+0YHRgtGMINC+0LTQvdC+0Lkg0L/QtdGA0LXQvNC10L3QvdC+0Lkg0L7RgiDQtNGA0YPQs9C+0LkuDQoNCtCa0L7RjdGE0YTQuNGG0LjQtdC90YIg0LTQtdGC0LXRgNC80LjQvdCw0YbQuNC4IFJcXjIg0L/QvtC60LDQt9GL0LLQsNC10YIsINC60LDQutGD0Y4g0LTQvtC70Y4g0LLQsNGA0LjQsNGG0LjQuCDQt9Cw0LLQuNGB0LjQvNC+0Lkg0L/QtdGA0LXQvNC10L3QvdC+0Lkg0L7QsdGK0Y/RgdC90Y/QtdGCINC80L7QtNC10LvRjC4NCg0K0JHRg9GC0YHRgtGA0LDQvyDQv9C+0LfQstC+0LvRj9C10YIg0L7RhtC10L3QuNGC0Ywg0YPRgdGC0L7QudGH0LjQstC+0YHRgtGMINC+0YbQtdC90LrQuCBSXF4yLCDRgdGC0LDQvdC00LDRgNGC0L3Rg9GOINC+0YjQuNCx0LrRgywg0LAg0YLQsNC60LbQtSDQtNC+0LLQtdGA0LjRgtC10LvRjNC90YvQuSDQuNC90YLQtdGA0LLQsNC7Lg0KDQrQn9GA0L7QuNC30LLQvtC00LjRgtGMINCx0YPQtNGDINCx0YPRgtGB0YLRgNCw0L/QuNGA0L7QstCw0L3QuNC1INC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDQtNC10YLQtdGA0LzQuNC90LDRhtC40Lgg0LTQu9GPINC80L7QtNC10LvQuCBtcGdcfmRpc3AuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMCkNCg0KbGlicmFyeShib290KQ0KDQojINGE0YPQvdC60YbQuNGPINGA0LDRgdGH0LXRgtCwIFJeMg0KcnNxX2Z1bmN0aW9uIDwtIGZ1bmN0aW9uKGZvcm11bGEsIGRhdGEsIGluZGljZXMpew0KDQogIGQgPC0gZGF0YVtpbmRpY2VzLCBdDQoNCiAgZml0IDwtIGxtKGZvcm11bGEsIGRhdGEgPSBkKQ0KDQogIHJldHVybihzdW1tYXJ5KGZpdCkkci5zcXVhcmVkKQ0KfQ0KDQojINCx0YPRgtGB0YLRgNCw0L8NCnJlcHMgPC0gYm9vdCgNCiAgZGF0YSA9IG10Y2FycywNCiAgc3RhdGlzdGljID0gcnNxX2Z1bmN0aW9uLA0KICBSID0gNTAwMCwNCiAgZm9ybXVsYSA9IG1wZyB+IGRpc3ANCikNCg0KcmVwcw0KYGBgDQoNCmBgYCAgICAgICAgIA0KT1JESU5BUlkgTk9OUEFSQU1FVFJJQyBCT09UU1RSQVAgICANCg0KQ2FsbDogDQpib290KGRhdGEgPSBtdGNhcnMsIHN0YXRpc3RpYyA9IHJzcV9mdW5jdGlvbiwgUiA9IDUwMDAsIGZvcm11bGEgPSBtcGcgfg0KICAgIGRpc3ApICAgDQoNCkJvb3RzdHJhcCBTdGF0aXN0aWNzIDoNCiAgICAgIG9yaWdpbmFsICAgICAgYmlhcyAgICBzdGQuIGVycm9yDQogdDEqIDAuNzE4MzQzMyAwLjAwMzI3MDU5MyAgMC4wNjQxMzc5OQ0KYGBgDQoNCtCT0YDQsNGE0LjQuiDihJYxLiDQoNCw0YHQv9GA0LXQtNC10LvQtdC90LjQtSDQsdGD0YLRgdGC0YDQsNC/LdC+0YbQtdC90L7Qug0KDQpgYGB7cn0NCmhpc3QoDQogIHJlcHMkdCwNCiAgYnJlYWtzID0gMzAsDQogIGNvbCA9ICJsaWdodGJsdWUiLA0KICBtYWluID0gIkJvb3RzdHJhcCBkaXN0cmlidXRpb24gb2YgUsKyIiwNCiAgeGxhYiA9ICJSwrIiDQopDQoNCmFibGluZSgNCiAgdiA9IHJlcHMkdDAsDQogIGNvbCA9ICJyZWQiLA0KICBsd2QgPSAzDQopDQpgYGANCg0KIVvQmtGA0LDRgdC90LDRjyDQu9C40L3QuNGPINC/0L7QutCw0LfRi9Cy0LDQtdGCINC40YHRhdC+0LTQvdC+0LUg0LfQvdCw0YfQtdC90LjQtSBSXF4yLl0oaW1hZ2VzL2NsaXBib2FyZC0yNjEzOTkyMzc5LnBuZykNCg0K0JPRgNCw0YTQuNC6IOKEljIuIFFRLXBsb3QNCg0KYGBge3J9DQpxcW5vcm0ocmVwcyR0KQ0KDQpxcWxpbmUoDQogIHJlcHMkdCwNCiAgY29sID0gInJlZCIsDQogIGx3ZCA9IDINCikNCmBgYA0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0xOTYwOTk3NzA4LnBuZykNCg0K0JPRgNCw0YTQuNC6INC/0L7QutCw0LfRi9Cy0LDQtdGCINCx0LvQuNC30L7RgdGC0Ywg0YDQsNGB0L/RgNC10LTQtdC70LXQvdC40Y8g0LHRg9GC0YHRgtGA0LDQvy3QvtGG0LXQvdC+0Log0Log0L3QvtGA0LzQsNC70YzQvdC+0LzRgy4NCg0K0JLRi9Cy0LXQtNGDINC00L7QstC10YDQuNGC0LXQu9GM0L3Ri9C5INC40L3RgtC10YDQstCw0Lsg0Lgg0LTQvtC/0L7Qu9C90LjRgtC10LvRjNC90YvQtSDQv9C+0LrQsNC30LDRgtC10LvQuC4NCg0KYGBge3J9DQpib290LmNpKA0KICByZXBzLA0KICB0eXBlID0gImJjYSINCikNCg0KYmlhcyA8LSBtZWFuKHJlcHMkdCkgLSByZXBzJHQwDQoNCnNlIDwtIHNkKHJlcHMkdCkNCg0KY3YgPC0gc2UgLyBtZWFuKHJlcHMkdCkNCg0KY2F0KCJCaWFzID0iLCBiaWFzLCAiXG4iKQ0KY2F0KCJTRSA9Iiwgc2UsICJcbiIpDQpjYXQoIkNWID0iLCBjdiwgIlxuIikNCmBgYA0KDQpgYGAgICAgICAgICANCkJPT1RTVFJBUCBDT05GSURFTkNFIElOVEVSVkFMIENBTENVTEFUSU9OUw0KQmFzZWQgb24gNTAwMCBib290c3RyYXAgcmVwbGljYXRlcyAgDQoNCkNBTEwgOiAgDQpib290LmNpKGJvb3Qub3V0ID0gcmVwcywgdHlwZSA9ICJiY2EiKSAgDQoNCkludGVydmFscyA6ICANCkxldmVsICAgICAgIEJDYSAgICAgICAgICAgDQo5NSUgICAoIDAuNTQ5OSwgIDAuODE5NSApIA0KDQpDYWxjdWxhdGlvbnMgYW5kIEludGVydmFscyBvbiBPcmlnaW5hbCBTY2FsZQ0KDQpCaWFzID0gMC4wMDMyNzA1OTMgIA0KU0UgPSAwLjA2NDEzNzk5ICANCkNWID0gMC4wODg4ODEzMSANCmBgYA0KDQojIyMg0JLRi9Cy0L7QtA0KDQrQlNC70Y8g0LzQvtC00LXQu9C4IG1wZ+KIvGRpc3Ag0LLRi9C/0L7Qu9C90LXQvdC+IDUwMDAg0LHRg9GC0YHRgtGA0LDQvy3Qv9C10YDQtdCy0YvQsdC+0YDQvtC6LiDQn9C+0LvRg9GH0LXQvSA5NSUt0LkgQkNhLdC00L7QstC10YDQuNGC0LXQu9GM0L3Ri9C5INC40L3RgtC10YDQstCw0Lsg0LTQu9GPINC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDQtNC10YLQtdGA0LzQuNC90LDRhtC40LggUlxeMjogKDAuNTQ5OTsgMC44MTk1KS4g0KHQvNC10YnQtdC90LjQtSDQvNCw0LvQviAoQmlhcyA9IDAuMDAzMyksINGB0YLQsNC90LTQsNGA0YLQvdCw0Y8g0L7RiNC40LHQutCwINGB0L7RgdGC0LDQstC70Y/QtdGCIDAuMDY0MS4g0K3RgtC+INC/0L7QtNGC0LLQtdGA0LbQtNCw0LXRgiDRg9GB0YLQvtC50YfQuNCy0L7RgdGC0Ywg0L7RhtC10L3QutC4INC4INC90LDQu9C40YfQuNC1INC00L7RgdGC0LDRgtC+0YfQvdC+INGB0LjQu9GM0L3QvtC5INGB0LLRj9C30Lgg0LzQtdC20LTRgyDQvtCx0YrRkdC80L7QvCDQtNCy0LjQs9Cw0YLQtdC70Y8g0Lgg0YDQsNGB0YXQvtC00L7QvCDRgtC+0L/Qu9C40LLQsC4NCg0KIyMg0JLRi9Cy0L7QtNGLINC/0L4g0YDQsNCx0L7RgtC1DQoNCtCf0YDQvtCy0LXQtNGR0L0g0YDQsNC30LLQtdC00L7Rh9C90YvQuSDQsNC90LDQu9C40Lcg0LTQsNGC0LDRgdC10YLQsCDQsNCy0YLQvtC80L7QsdC40LvQtdC5INC4INGA0LXQsNC70LjQt9C+0LLQsNC9INC80LXRgtC+0LQg0LHRg9GC0YHRgtGA0LDQv9CwINCyIFB5dGhvbiDQuCBSLiDQkiBQeXRob24g0L/QvtC00YLQstC10YDQttC00LXQvdGLINGB0YLQsNGC0LjRgdGC0LjRh9C10YHQutC4INC30L3QsNGH0LjQvNGL0LUg0YDQsNC30LvQuNGH0LjRjyDQvNC10LbQtNGDINC40YHRgdC70LXQtNGD0LXQvNGL0LzQuCDQs9GA0YPQv9C/0LDQvNC4INCw0LLRgtC+0LzQvtCx0LjQu9C10LkuINCSIFIg0L7RhtC10L3QtdC90LAg0YPRgdGC0L7QudGH0LjQstC+0YHRgtGMINC60L7RjdGE0YTQuNGG0LjQtdC90YLQsCDQtNC10YLQtdGA0LzQuNC90LDRhtC40Lgg0YDQtdCz0YDQtdGB0YHQuNC+0L3QvdC+0Lkg0LzQvtC00LXQu9C4INC4INC/0L7RgdGC0YDQvtC10L0g0LTQvtCy0LXRgNC40YLQtdC70YzQvdGL0Lkg0LjQvdGC0LXRgNCy0LDQuyDQtNC70Y8gUlxeMi4g0KDQtdC30YPQu9GM0YLQsNGC0Ysg0L/QvtC60LDQt9GL0LLQsNGO0YIsINGH0YLQviDQsdGD0YLRgdGC0YDQsNC/INC/0L7Qt9Cy0L7Qu9GP0LXRgiDQvdCw0LTRkdC20L3QviDQvtGG0LXQvdC40LLQsNGC0Ywg0YHRgtCw0YLQuNGB0YLQuNGH0LXRgdC60LjQtSDRhdCw0YDQsNC60YLQtdGA0LjRgdGC0LjQutC4INC4INC40YUg0LTQvtCy0LXRgNC40YLQtdC70YzQvdGL0LUg0LjQvdGC0LXRgNCy0LDQu9GLINCx0LXQtyDQv9GA0LXQtNC/0L7Qu9C+0LbQtdC90LjRjyDQviDQvdC+0YDQvNCw0LvRjNC90L7RgdGC0Lgg0LjRgdGF0L7QtNC90YvRhSDQtNCw0L3QvdGL0YUuDQo=