Загружу необходимые пакеты.

options(repos = c(CRAN = "https://cran.rstudio.com/"))
install.packages(c("class", "gmodels"))
G2;WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/g
пробую URL 'https://cran.rstudio.com/bin/windows/contrib/4.6/gtools_3.9.5.zip'
пробую URL 'https://cran.rstudio.com/bin/windows/contrib/4.6/gdata_3.0.1.zip'
пробую URL 'https://cran.rstudio.com/bin/windows/contrib/4.6/class_7.3-23.zip'
пробую URL 'https://cran.rstudio.com/bin/windows/contrib/4.6/gmodels_2.19.1.zip'
package ‘gtools’ successfully unpacked and MD5 sums checked
package ‘gdata’ successfully unpacked and MD5 sums checked
package ‘class’ successfully unpacked and MD5 sums checked
package ‘gmodels’ successfully unpacked and MD5 sums checked

Скачанные бинарные пакеты находятся в
    C:\Users\oleg3\AppData\Local\Temp\RtmpkrdKXA\downloaded_packages
library(class) # class - содержит функцию knn() для классификации методом k-ближайших соседей
library(gmodels) # gmodels - содержит функцию CrossTable() для создания перекрестных таблиц

Задание 2

Ход работы

Выполню классификацию k-ближайших соседей с использованием функции knn() из пакета class на наборе данных iris.

Для начала загружу данные iris.

data(iris)
head(iris)

Проведите нормализацию данных - произведу масштабирование в диапазон [0, 1]

normalize <- function(x) {
  return((x - min(x)) / (max(x) - min(x)))
}

iris_norm <- as.data.frame(lapply(iris[, 1:4], normalize))

iris_norm$Species <- iris$Species

Разделю выборку на обучающую (70%) и тестовую (30%).

set.seed(123)
# sample() случайным образом выбирает 70% индексов строк
train_idx <- sample(1:nrow(iris_norm), 0.7 * nrow(iris_norm))
train_data <- iris_norm[train_idx, ] # 70%
test_data <- iris_norm[-train_idx, ] # 30%

Извлеку признаки и метки для обучения и тестирования

train_X <- train_data[, 1:4] # Признаки для обучения (первые 4 колонки)
train_y <- train_data$Species # Метки классов для обучения
test_X <- test_data[, 1:4] # Признаки для тестирования
test_y <- test_data$Species # Фактические метки классов тестовой выборки

Произведу классификацию k-NN: фунция knn() находит k ближайших объектов из train_X для каждого объекта test_X и присваивает класс, наиболее часто встречающийся среди этих соседей.

Здесь train - обучающие данные, test - тестовые данные, cl - метки классов, k - количество соседей.

knn_pred <- knn(train = train_X, test = test_X, cl = train_y, k = 3)

Оценю построенную модель с использованием функции CrossTable() из пакета gmodels.

CrossTable(x = test_y, y = knn_pred, prop.chisq = FALSE)

 
   Cell Contents
|-------------------------|
|                       N |
|           N / Row Total |
|           N / Col Total |
|         N / Table Total |
|-------------------------|

 
Total Observations in Table:  45 

 
             | knn_pred 
      test_y |     setosa | versicolor |  virginica |  Row Total | 
-------------|------------|------------|------------|------------|
      setosa |         14 |          0 |          0 |         14 | 
             |      1.000 |      0.000 |      0.000 |      0.311 | 
             |      1.000 |      0.000 |      0.000 |            | 
             |      0.311 |      0.000 |      0.000 |            | 
-------------|------------|------------|------------|------------|
  versicolor |          0 |         17 |          1 |         18 | 
             |      0.000 |      0.944 |      0.056 |      0.400 | 
             |      0.000 |      0.944 |      0.077 |            | 
             |      0.000 |      0.378 |      0.022 |            | 
-------------|------------|------------|------------|------------|
   virginica |          0 |          1 |         12 |         13 | 
             |      0.000 |      0.077 |      0.923 |      0.289 | 
             |      0.000 |      0.056 |      0.923 |            | 
             |      0.000 |      0.022 |      0.267 |            | 
-------------|------------|------------|------------|------------|
Column Total |         14 |         18 |         13 |         45 | 
             |      0.311 |      0.400 |      0.289 |            | 
-------------|------------|------------|------------|------------|

 
     
   Cell Contents 
|-------------------------| 
|                       N | 
|           N / Row Total | 
|           N / Col Total | 
|         N / Table Total | 
|-------------------------|    

Total Observations in Table:  45

              | knn_pred      
       test_y |     setosa | versicolor |  virginica |  Row Total 
|-------------|------------|------------|------------|------------|
       setosa |         14 |          0 |          0 |         14 |
              |      1.000 |      0.000 |      0.000 |      0.311 |
              |      1.000 |      0.000 |      0.000 |            |
              |      0.311 |      0.000 |      0.000 |            |
 -------------|------------|------------|------------|------------|
   versicolor |          0 |         17 |          1 |         18 |
              |      0.000 |      0.944 |      0.056 |      0.400 |
              |      0.000 |      0.944 |      0.077 |            |
              |      0.000 |      0.378 |      0.022 |            |
 -------------|------------|------------|------------|------------|
    virginica |          0 |          1 |         12 |         13 |
              |      0.000 |      0.077 |      0.923 |      0.289 |
              |      0.000 |      0.056 |      0.923 |            |
              |      0.000 |      0.022 |      0.267 |            |
 -------------|------------|------------|------------|------------|
 Column Total |         14 |         18 |         13 |         45 |
              |      0.311 |      0.400 |      0.289 |            |
 -------------|------------|------------|------------|------------|   

Построю матрицу ошибок:

conf_matrix <- table(test_y, knn_pred)
print("Матрица ошибок (Confusion Matrix):")
[1] "Матрица ошибок (Confusion Matrix):"
print(conf_matrix)
            knn_pred
test_y       setosa versicolor virginica
  setosa         14          0         0
  versicolor      0         17         1
  virginica       0          1        12
[1] "Матрица ошибок (Confusion Matrix):"
                  knn_pred 
test_y       setosa versicolor virginica   
  setosa         14          0         0   
  versicolor      0         17         1   
  virginica       0          1        12

Построю диагональную оценку качества прогноза (diagonal mark quality prediction):

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
print(round(accuracy * 100, 2))
[1] 95.56
[1] 95.56 

Вывод

Модель допустила всего 2 ошибки, точность классификации составила 95.56%.

Задание 3

Ход работы

Реализую метод опорных векторов с использованием функции svm() из пакета e1071. Загружу необходимый пакет.

install.packages("e1071")
library(e1071)

Загружу данные:

data(iris)
head(iris)

Для простоты классификации оставлю только два класса: setosa и versicolor. Преобразую Species в фактор с двумя уровнями (setosa, versicolor).

iris_binary <- iris[iris$Species != "virginica", ]

iris_binary$Species <- factor(iris_binary$Species)

Разделю на признаки и метки.

# Разделение на признаки (X) и целевую переменную (y) для наглядности
X <- iris_binary[, 1:4] # признаки (первые 4 колонки)
y <- iris_binary$Species # метки классов

Построю линейный классификатор для прогнозирования. Для подбора параметров модели выполним перекрестную проверку с делением исходной выборки на 10 равных частей (cross=10).

# svm() - функция для создания модели опорных векторов
# Параметры:
#   Species ~ . - формула
#   data = iris_binary - используем бинарный набор данных
#   kernel = "linear" - линейное ядро (для линейно разделимых данных)
#   cost = 1 - параметр стоимости ошибки (штраф за неверную классификацию)
#     Чем больше cost, тем меньше допускается ошибок, но выше риск переобучения
#   cross = 10 - 10-кратная перекрестная проверка:
#     Данные делятся на 10 частей, 9 используются для обучения, 1 для проверки
#     Процесс повторяется 10 раз, каждый раз с новой проверочной частью
svm_model <- svm(Species ~ ., data = iris_binary, kernel = "linear", 
                 cost = 1, cross = 10)

Выведу сводку модели.

# summary показывает:
#   - параметры модели (kernel, cost, gamma и др.)
#   - количество опорных векторов
#   - результаты перекрестной проверки по каждой из 10 итераций
#   - среднюю точность перекрестной проверки
print("Сводка модели SVM")
[1] "Сводка модели SVM"
summary(svm_model)

Call:
svm(formula = Species ~ ., data = iris_binary, kernel = "linear", cost = 1, cross = 10)


Parameters:
   SVM-Type:  C-classification 
 SVM-Kernel:  linear 
       cost:  1 

Number of Support Vectors:  4

 ( 2 2 )


Number of Classes:  2 

Levels: 
 setosa versicolor

10-fold cross-validation on training data:

Total Accuracy: 100 
Single Accuracies:
 100 100 100 100 100 100 100 100 100 100 
[1] "Сводка модели SVM"  
Call: 
svm(formula = Species ~ ., data = iris_binary, kernel = "linear", cost = 1, cross = 10)   

Parameters:
    SVM-Type:  C-classification   
    SVM-Kernel:  linear         
    cost:  1   
    
Number of Support Vectors:  4   
( 2 2 )   

Number of Classes:  2   
Levels:   
 setosa versicolor  

10-fold cross-validation on training data:  

Total Accuracy: 100  
Single Accuracies:  
 100 100 100 100 100 100 100 100 100 100  

Оценю точность перекрестной проверки.

# svm_model$tot.accuracy хранит среднюю точность по всем 10 проверкам
# Это более объективная оценка качества модели, чем точность на обучающей выборке
print(round(svm_model$tot.accuracy, 2))
[1] 100
[1] 100

Применю модель к данным X и выведу матрицу ошибок.

svm_pred <- predict(svm_model, X)
conf_matrix <- table(True = y, Predicted = svm_pred)

print("Матрица ошибок")
[1] "Матрица ошибок"
print(conf_matrix)
            Predicted
True         setosa versicolor
  setosa         50          0
  versicolor      0         50
[1] "Матрица ошибок"
             Predicted
True         setosa versicolor
  setosa         50          0   
  versicolor      0         50

Выведу точность на полном наборе.

accuracy <- sum(diag(conf_matrix)) / sum(conf_matrix)
print(round(accuracy * 100, 2))
[1] 100
[1] 100

Вывод

Модель безошибочно определила все типы на полной выборке.

Задание 4

Ход работы

Выполню расчет главных компонент с использованием пакета vegan() и его функции rda().

Установлю пакет.

install.packages("vegan")
library(vegan)

Загружу данные.

data(iris)
head(iris)

Выделю числовые признаки для метода главных компонент (PCA).

iris_features <- iris[, 1:4] # Sepal.Length, Sepal.Width, Petal.Length, Petal.Width

Выполню PCA с помощью rda().

# scale = TRUE - стандартизация данных (приводит все признаки к единому масштабу)
pca_result <- rda(iris_features, scale = TRUE)

print("Сводка результатов PCA")
[1] "Сводка результатов PCA"
summary(pca_result)

Call:
rda(X = iris_features, scale = TRUE) 

Partitioning of correlations:
              Inertia Proportion
Total               4          1
Unconstrained       4          1

Eigenvalues, and their contribution to the correlations 

Importance of components:
                         PC1    PC2     PC3      PC4
Eigenvalue            2.9185 0.9140 0.14676 0.020715
Proportion Explained  0.7296 0.2285 0.03669 0.005179
Cumulative Proportion 0.7296 0.9581 0.99482 1.000000
[1] "Сводка результатов PCA"

Call: 
rda(X = iris_features, scale = TRUE)

Partitioning of correlations:               Inertia Proportion Total               4          1 Unconstrained       4          1  Eigenvalues, and their contribution to the correlations   Importance of components:                          PC1    PC2     PC3      PC4 Eigenvalue            2.9185 0.9140 0.14676 0.020715 Proportion Explained  0.7296 0.2285 0.03669 0.005179 Cumulative Proportion 0.7296 0.9581 0.99482 1.000000 

Построю ординационную диаграмму.

# biplot отображает одновременно:
# - Объекты (точки) - 150 ирисов в пространстве первых двух главных компонент
# - Признаки (стрелки) - показывают направление максимального изменения признаков
# scaling = "species" - масштабирование для лучшего отображения признаков
# main - заголовок графика
biplot(pca_result, scaling = "species", main = "PCA для Iris")

Выведу долю объясненной дисперсии.

prop_var <- pca_result$CA$eig / sum(pca_result$CA$eig)
print(round(prop_var * 100, 2))
  PC1   PC2   PC3   PC4 
72.96 22.85  3.67  0.52 
  PC1   PC2   PC3   PC4
72.96 22.85  3.67  0.52 
  1. PC1 (первая главная компонента) объясняет 72,96 % всей дисперсии в данных. Это направление в пространстве признаков, вдоль которого данные имеют наибольшую вариативность. Первая компонента «ухватила» основную часть информации из исходных переменных.

  2. PC2 (вторая главная компонента) объясняет ещё 22,85 % дисперсии. Она ортогональна (некоррелирована) PC1 и захватывает следующую по величине часть вариативности, которую не объяснила PC1. Вместе они объясняют 95.81% вариации — достаточно для анализа.

  3. PC3 добавляет всего 3,67 % объяснённой дисперсии. Вклад этой компоненты уже невелик — она описывает относительно малые закономерности в данных.

  4. PC4 объясняет лишь 0,52 % дисперсии. Это очень малая доля, которая, скорее всего, соответствует шуму или крайне незначительным паттернам в данных.

LS0tDQp0aXRsZTogItCQ0L3QsNC70LjQtyDQtNCw0L3QvdGL0YUsINC70LDQsdC+0YDQsNGC0L7RgNC90LDRjyDRgNCw0LHQvtGC0LAg4oSWNSDigJzihJbQmtC70LDRgdGC0LXRgNC90YvQuSDQuCDRgNC10LPRgNC10YHRgdC40L7QvdC90YvQuSDQsNC90LDQu9C40Lcg0LTQsNC90L3Ri9GFINGBINC40YHQv9C+0LvRjNC30L7QstCw0L3QuNC10Lwg0Y/Qt9GL0LrQsCBS4oCdIg0KYXV0aG9yOiAi0KHQvNC10LvQutC+0LIg0J7Qu9C10LMsIDIzMS0zMzMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQrQl9Cw0LPRgNGD0LbRgyDQvdC10L7QsdGF0L7QtNC40LzRi9C1INC/0LDQutC10YLRiy4NCg0KYGBge3J9DQpvcHRpb25zKHJlcG9zID0gYyhDUkFOID0gImh0dHBzOi8vY3Jhbi5yc3R1ZGlvLmNvbS8iKSkNCmluc3RhbGwucGFja2FnZXMoYygiY2xhc3MiLCAiZ21vZGVscyIpKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShjbGFzcykgIyBjbGFzcyAtINGB0L7QtNC10YDQttC40YIg0YTRg9C90LrRhtC40Y4ga25uKCkg0LTQu9GPINC60LvQsNGB0YHQuNGE0LjQutCw0YbQuNC4INC80LXRgtC+0LTQvtC8IGst0LHQu9C40LbQsNC50YjQuNGFINGB0L7RgdC10LTQtdC5DQpsaWJyYXJ5KGdtb2RlbHMpICMgZ21vZGVscyAtINGB0L7QtNC10YDQttC40YIg0YTRg9C90LrRhtC40Y4gQ3Jvc3NUYWJsZSgpINC00LvRjyDRgdC+0LfQtNCw0L3QuNGPINC/0LXRgNC10LrRgNC10YHRgtC90YvRhSDRgtCw0LHQu9C40YYNCmBgYA0KDQojIyDQl9Cw0LTQsNC90LjQtSAyDQoNCiMjIyDQpdC+0LQg0YDQsNCx0L7RgtGLDQoNCtCS0YvQv9C+0LvQvdGOINC60LvQsNGB0YHQuNGE0LjQutCw0YbQuNGOIGst0LHQu9C40LbQsNC50YjQuNGFINGB0L7RgdC10LTQtdC5INGBINC40YHQv9C+0LvRjNC30L7QstCw0L3QuNC10Lwg0YTRg9C90LrRhtC40Lgga25uKCkg0LjQtyDQv9Cw0LrQtdGC0LAgY2xhc3Mg0L3QsCDQvdCw0LHQvtGA0LUg0LTQsNC90L3Ri9GFIGlyaXMuDQoNCtCU0LvRjyDQvdCw0YfQsNC70LAg0LfQsNCz0YDRg9C20YMg0LTQsNC90L3Ri9C1IGlyaXMuDQoNCmBgYHtyfQ0KZGF0YShpcmlzKQ0KaGVhZChpcmlzKQ0KYGBgDQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTIyNTcwMDY5NjEucG5nKQ0KDQrQn9GA0L7QstC10LTQuNGC0LUg0L3QvtGA0LzQsNC70LjQt9Cw0YbQuNGOINC00LDQvdC90YvRhSAtINC/0YDQvtC40LfQstC10LTRgyDQvNCw0YHRiNGC0LDQsdC40YDQvtCy0LDQvdC40LUg0LIg0LTQuNCw0L/QsNC30L7QvSBbMCwgMV0NCg0KYGBge3J9DQpub3JtYWxpemUgPC0gZnVuY3Rpb24oeCkgew0KICByZXR1cm4oKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkpDQp9DQoNCmlyaXNfbm9ybSA8LSBhcy5kYXRhLmZyYW1lKGxhcHBseShpcmlzWywgMTo0XSwgbm9ybWFsaXplKSkNCg0KaXJpc19ub3JtJFNwZWNpZXMgPC0gaXJpcyRTcGVjaWVzDQpgYGANCg0K0KDQsNC30LTQtdC70Y4g0LLRi9Cx0L7RgNC60YMg0L3QsCDQvtCx0YPRh9Cw0Y7RidGD0Y4gKDcwJSkg0Lgg0YLQtdGB0YLQvtCy0YPRjiAoMzAlKS4NCg0KYGBge3J9DQpzZXQuc2VlZCgxMjMpDQojIHNhbXBsZSgpINGB0LvRg9GH0LDQudC90YvQvCDQvtCx0YDQsNC30L7QvCDQstGL0LHQuNGA0LDQtdGCIDcwJSDQuNC90LTQtdC60YHQvtCyINGB0YLRgNC+0LoNCnRyYWluX2lkeCA8LSBzYW1wbGUoMTpucm93KGlyaXNfbm9ybSksIDAuNyAqIG5yb3coaXJpc19ub3JtKSkNCnRyYWluX2RhdGEgPC0gaXJpc19ub3JtW3RyYWluX2lkeCwgXSAjIDcwJQ0KdGVzdF9kYXRhIDwtIGlyaXNfbm9ybVstdHJhaW5faWR4LCBdICMgMzAlDQpgYGANCg0K0JjQt9Cy0LvQtdC60YMg0L/RgNC40LfQvdCw0LrQuCDQuCDQvNC10YLQutC4INC00LvRjyDQvtCx0YPRh9C10L3QuNGPINC4INGC0LXRgdGC0LjRgNC+0LLQsNC90LjRjw0KDQpgYGB7cn0NCnRyYWluX1ggPC0gdHJhaW5fZGF0YVssIDE6NF0gIyDQn9GA0LjQt9C90LDQutC4INC00LvRjyDQvtCx0YPRh9C10L3QuNGPICjQv9C10YDQstGL0LUgNCDQutC+0LvQvtC90LrQuCkNCnRyYWluX3kgPC0gdHJhaW5fZGF0YSRTcGVjaWVzICMg0JzQtdGC0LrQuCDQutC70LDRgdGB0L7QsiDQtNC70Y8g0L7QsdGD0YfQtdC90LjRjw0KdGVzdF9YIDwtIHRlc3RfZGF0YVssIDE6NF0gIyDQn9GA0LjQt9C90LDQutC4INC00LvRjyDRgtC10YHRgtC40YDQvtCy0LDQvdC40Y8NCnRlc3RfeSA8LSB0ZXN0X2RhdGEkU3BlY2llcyAjINCk0LDQutGC0LjRh9C10YHQutC40LUg0LzQtdGC0LrQuCDQutC70LDRgdGB0L7QsiDRgtC10YHRgtC+0LLQvtC5INCy0YvQsdC+0YDQutC4DQpgYGANCg0K0J/RgNC+0LjQt9Cy0LXQtNGDINC60LvQsNGB0YHQuNGE0LjQutCw0YbQuNGOIGstTk46INGE0YPQvdGG0LjRjyBrbm4oKSDQvdCw0YXQvtC00LjRgiBrINCx0LvQuNC20LDQudGI0LjRhSDQvtCx0YrQtdC60YLQvtCyINC40LcgdHJhaW5fWCDQtNC70Y8g0LrQsNC20LTQvtCz0L4g0L7QsdGK0LXQutGC0LAgdGVzdF9YINC4INC/0YDQuNGB0LLQsNC40LLQsNC10YIg0LrQu9Cw0YHRgSwg0L3QsNC40LHQvtC70LXQtSDRh9Cw0YHRgtC+INCy0YHRgtGA0LXRh9Cw0Y7RidC40LnRgdGPINGB0YDQtdC00Lgg0Y3RgtC40YUg0YHQvtGB0LXQtNC10LkuDQoNCtCX0LTQtdGB0YwgdHJhaW4gLSDQvtCx0YPRh9Cw0Y7RidC40LUg0LTQsNC90L3Ri9C1LCB0ZXN0IC0g0YLQtdGB0YLQvtCy0YvQtSDQtNCw0L3QvdGL0LUsIGNsIC0g0LzQtdGC0LrQuCDQutC70LDRgdGB0L7QsiwgayAtINC60L7Qu9C40YfQtdGB0YLQstC+INGB0L7RgdC10LTQtdC5Lg0KDQpgYGB7cn0NCmtubl9wcmVkIDwtIGtubih0cmFpbiA9IHRyYWluX1gsIHRlc3QgPSB0ZXN0X1gsIGNsID0gdHJhaW5feSwgayA9IDMpDQpgYGANCg0K0J7RhtC10L3RjiDQv9C+0YHRgtGA0L7QtdC90L3Rg9GOINC80L7QtNC10LvRjCDRgSDQuNGB0L/QvtC70YzQt9C+0LLQsNC90LjQtdC8INGE0YPQvdC60YbQuNC4IENyb3NzVGFibGUoKSDQuNC3INC/0LDQutC10YLQsCBnbW9kZWxzLg0KDQpgYGB7cn0NCkNyb3NzVGFibGUoeCA9IHRlc3RfeSwgeSA9IGtubl9wcmVkLCBwcm9wLmNoaXNxID0gRkFMU0UpDQpgYGANCg0KYGBgICAgICAgICAgDQogICAgIA0KICAgQ2VsbCBDb250ZW50cyANCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfCANCnwgICAgICAgICAgICAgICAgICAgICAgIE4gfCANCnwgICAgICAgICAgIE4gLyBSb3cgVG90YWwgfCANCnwgICAgICAgICAgIE4gLyBDb2wgVG90YWwgfCANCnwgICAgICAgICBOIC8gVGFibGUgVG90YWwgfCANCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfCAgICANCg0KVG90YWwgT2JzZXJ2YXRpb25zIGluIFRhYmxlOiAgNDUNCg0KICAgICAgICAgICAgICB8IGtubl9wcmVkICAgICAgDQogICAgICAgdGVzdF95IHwgICAgIHNldG9zYSB8IHZlcnNpY29sb3IgfCAgdmlyZ2luaWNhIHwgIFJvdyBUb3RhbCANCnwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCiAgICAgICBzZXRvc2EgfCAgICAgICAgIDE0IHwgICAgICAgICAgMCB8ICAgICAgICAgIDAgfCAgICAgICAgIDE0IHwNCiAgICAgICAgICAgICAgfCAgICAgIDEuMDAwIHwgICAgICAwLjAwMCB8ICAgICAgMC4wMDAgfCAgICAgIDAuMzExIHwNCiAgICAgICAgICAgICAgfCAgICAgIDEuMDAwIHwgICAgICAwLjAwMCB8ICAgICAgMC4wMDAgfCAgICAgICAgICAgIHwNCiAgICAgICAgICAgICAgfCAgICAgIDAuMzExIHwgICAgICAwLjAwMCB8ICAgICAgMC4wMDAgfCAgICAgICAgICAgIHwNCiAtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCiAgIHZlcnNpY29sb3IgfCAgICAgICAgICAwIHwgICAgICAgICAxNyB8ICAgICAgICAgIDEgfCAgICAgICAgIDE4IHwNCiAgICAgICAgICAgICAgfCAgICAgIDAuMDAwIHwgICAgICAwLjk0NCB8ICAgICAgMC4wNTYgfCAgICAgIDAuNDAwIHwNCiAgICAgICAgICAgICAgfCAgICAgIDAuMDAwIHwgICAgICAwLjk0NCB8ICAgICAgMC4wNzcgfCAgICAgICAgICAgIHwNCiAgICAgICAgICAgICAgfCAgICAgIDAuMDAwIHwgICAgICAwLjM3OCB8ICAgICAgMC4wMjIgfCAgICAgICAgICAgIHwNCiAtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCiAgICB2aXJnaW5pY2EgfCAgICAgICAgICAwIHwgICAgICAgICAgMSB8ICAgICAgICAgMTIgfCAgICAgICAgIDEzIHwNCiAgICAgICAgICAgICAgfCAgICAgIDAuMDAwIHwgICAgICAwLjA3NyB8ICAgICAgMC45MjMgfCAgICAgIDAuMjg5IHwNCiAgICAgICAgICAgICAgfCAgICAgIDAuMDAwIHwgICAgICAwLjA1NiB8ICAgICAgMC45MjMgfCAgICAgICAgICAgIHwNCiAgICAgICAgICAgICAgfCAgICAgIDAuMDAwIHwgICAgICAwLjAyMiB8ICAgICAgMC4yNjcgfCAgICAgICAgICAgIHwNCiAtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwNCiBDb2x1bW4gVG90YWwgfCAgICAgICAgIDE0IHwgICAgICAgICAxOCB8ICAgICAgICAgMTMgfCAgICAgICAgIDQ1IHwNCiAgICAgICAgICAgICAgfCAgICAgIDAuMzExIHwgICAgICAwLjQwMCB8ICAgICAgMC4yODkgfCAgICAgICAgICAgIHwNCiAtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLXwgICANCmBgYA0KDQrQn9C+0YHRgtGA0L7RjiDQvNCw0YLRgNC40YbRgyDQvtGI0LjQsdC+0Lo6DQoNCmBgYHtyfQ0KY29uZl9tYXRyaXggPC0gdGFibGUodGVzdF95LCBrbm5fcHJlZCkNCnByaW50KCLQnNCw0YLRgNC40YbQsCDQvtGI0LjQsdC+0LogKENvbmZ1c2lvbiBNYXRyaXgpOiIpDQpwcmludChjb25mX21hdHJpeCkNCmBgYA0KDQpgYGAgICAgICAgICANClsxXSAi0JzQsNGC0YDQuNGG0LAg0L7RiNC40LHQvtC6IChDb25mdXNpb24gTWF0cml4KToiDQogICAgICAgICAgICAgICAgICBrbm5fcHJlZCANCnRlc3RfeSAgICAgICBzZXRvc2EgdmVyc2ljb2xvciB2aXJnaW5pY2EgICANCiAgc2V0b3NhICAgICAgICAgMTQgICAgICAgICAgMCAgICAgICAgIDAgICANCiAgdmVyc2ljb2xvciAgICAgIDAgICAgICAgICAxNyAgICAgICAgIDEgICANCiAgdmlyZ2luaWNhICAgICAgIDAgICAgICAgICAgMSAgICAgICAgMTINCmBgYA0KDQrQn9C+0YHRgtGA0L7RjiDQtNC40LDQs9C+0L3QsNC70YzQvdGD0Y4g0L7RhtC10L3QutGDINC60LDRh9C10YHRgtCy0LAg0L/RgNC+0LPQvdC+0LfQsCAoZGlhZ29uYWwgbWFyayBxdWFsaXR5IHByZWRpY3Rpb24pOg0KDQpgYGB7cn0NCmFjY3VyYWN5IDwtIHN1bShkaWFnKGNvbmZfbWF0cml4KSkgLyBzdW0oY29uZl9tYXRyaXgpDQpwcmludChyb3VuZChhY2N1cmFjeSAqIDEwMCwgMikpDQpgYGANCg0KYGBgICAgICAgICAgDQpbMV0gOTUuNTYgDQpgYGANCg0KIyMjINCS0YvQstC+0LQNCg0K0JzQvtC00LXQu9GMINC00L7Qv9GD0YHRgtC40LvQsCDQstGB0LXQs9C+IDIg0L7RiNC40LHQutC4LCDRgtC+0YfQvdC+0YHRgtGMINC60LvQsNGB0YHQuNGE0LjQutCw0YbQuNC4INGB0L7RgdGC0LDQstC40LvQsCA5NS41NiUuDQoNCiMjINCX0LDQtNCw0L3QuNC1IDMNCg0KIyMjINCl0L7QtCDRgNCw0LHQvtGC0YsNCg0K0KDQtdCw0LvQuNC30YPRjiDQvNC10YLQvtC0INC+0L/QvtGA0L3Ri9GFINCy0LXQutGC0L7RgNC+0LIg0YEg0LjRgdC/0L7Qu9GM0LfQvtCy0LDQvdC40LXQvCDRhNGD0L3QutGG0LjQuCBzdm0oKSDQuNC3INC/0LDQutC10YLQsCBlMTA3MS4g0JfQsNCz0YDRg9C20YMg0L3QtdC+0LHRhdC+0LTQuNC80YvQuSDQv9Cw0LrQtdGCLg0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImUxMDcxIikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoZTEwNzEpDQpgYGANCg0K0JfQsNCz0YDRg9C20YMg0LTQsNC90L3Ri9C1Og0KDQpgYGB7cn0NCmRhdGEoaXJpcykNCmBgYA0KDQrQlNC70Y8g0L/RgNC+0YHRgtC+0YLRiyDQutC70LDRgdGB0LjRhNC40LrQsNGG0LjQuCDQvtGB0YLQsNCy0LvRjiDRgtC+0LvRjNC60L4g0LTQstCwINC60LvQsNGB0YHQsDogc2V0b3NhINC4IHZlcnNpY29sb3IuINCf0YDQtdC+0LHRgNCw0LfRg9GOIFNwZWNpZXMg0LIg0YTQsNC60YLQvtGAINGBINC00LLRg9C80Y8g0YPRgNC+0LLQvdGP0LzQuCAoc2V0b3NhLCB2ZXJzaWNvbG9yKS4NCg0KYGBge3J9DQppcmlzX2JpbmFyeSA8LSBpcmlzW2lyaXMkU3BlY2llcyAhPSAidmlyZ2luaWNhIiwgXQ0KDQppcmlzX2JpbmFyeSRTcGVjaWVzIDwtIGZhY3RvcihpcmlzX2JpbmFyeSRTcGVjaWVzKQ0KYGBgDQoNCtCg0LDQt9C00LXQu9GOINC90LAg0L/RgNC40LfQvdCw0LrQuCDQuCDQvNC10YLQutC4Lg0KDQpgYGB7cn0NCiMg0KDQsNC30LTQtdC70LXQvdC40LUg0L3QsCDQv9GA0LjQt9C90LDQutC4IChYKSDQuCDRhtC10LvQtdCy0YPRjiDQv9C10YDQtdC80LXQvdC90YPRjiAoeSkg0LTQu9GPINC90LDQs9C70Y/QtNC90L7RgdGC0LgNClggPC0gaXJpc19iaW5hcnlbLCAxOjRdICMg0L/RgNC40LfQvdCw0LrQuCAo0L/QtdGA0LLRi9C1IDQg0LrQvtC70L7QvdC60LgpDQp5IDwtIGlyaXNfYmluYXJ5JFNwZWNpZXMgIyDQvNC10YLQutC4INC60LvQsNGB0YHQvtCyDQpgYGANCg0K0J/QvtGB0YLRgNC+0Y4g0LvQuNC90LXQudC90YvQuSDQutC70LDRgdGB0LjRhNC40LrQsNGC0L7RgCDQtNC70Y8g0L/RgNC+0LPQvdC+0LfQuNGA0L7QstCw0L3QuNGPLiDQlNC70Y8g0L/QvtC00LHQvtGA0LAg0L/QsNGA0LDQvNC10YLRgNC+0LIg0LzQvtC00LXQu9C4INCy0YvQv9C+0LvQvdC40Lwg0L/QtdGA0LXQutGA0LXRgdGC0L3Rg9GOINC/0YDQvtCy0LXRgNC60YMg0YEg0LTQtdC70LXQvdC40LXQvCDQuNGB0YXQvtC00L3QvtC5INCy0YvQsdC+0YDQutC4INC90LAgMTAg0YDQsNCy0L3Ri9GFINGH0LDRgdGC0LXQuSAoY3Jvc3M9MTApLg0KDQpgYGB7cn0NCiMgc3ZtKCkgLSDRhNGD0L3QutGG0LjRjyDQtNC70Y8g0YHQvtC30LTQsNC90LjRjyDQvNC+0LTQtdC70Lgg0L7Qv9C+0YDQvdGL0YUg0LLQtdC60YLQvtGA0L7Qsg0KIyDQn9Cw0YDQsNC80LXRgtGA0Ys6DQojICAgU3BlY2llcyB+IC4gLSDRhNC+0YDQvNGD0LvQsA0KIyAgIGRhdGEgPSBpcmlzX2JpbmFyeSAtINC40YHQv9C+0LvRjNC30YPQtdC8INCx0LjQvdCw0YDQvdGL0Lkg0L3QsNCx0L7RgCDQtNCw0L3QvdGL0YUNCiMgICBrZXJuZWwgPSAibGluZWFyIiAtINC70LjQvdC10LnQvdC+0LUg0Y/QtNGA0L4gKNC00LvRjyDQu9C40L3QtdC50L3QviDRgNCw0LfQtNC10LvQuNC80YvRhSDQtNCw0L3QvdGL0YUpDQojICAgY29zdCA9IDEgLSDQv9Cw0YDQsNC80LXRgtGAINGB0YLQvtC40LzQvtGB0YLQuCDQvtGI0LjQsdC60LggKNGI0YLRgNCw0YQg0LfQsCDQvdC10LLQtdGA0L3Rg9GOINC60LvQsNGB0YHQuNGE0LjQutCw0YbQuNGOKQ0KIyAgICAg0KfQtdC8INCx0L7Qu9GM0YjQtSBjb3N0LCDRgtC10Lwg0LzQtdC90YzRiNC1INC00L7Qv9GD0YHQutCw0LXRgtGB0Y8g0L7RiNC40LHQvtC6LCDQvdC+INCy0YvRiNC1INGA0LjRgdC6INC/0LXRgNC10L7QsdGD0YfQtdC90LjRjw0KIyAgIGNyb3NzID0gMTAgLSAxMC3QutGA0LDRgtC90LDRjyDQv9C10YDQtdC60YDQtdGB0YLQvdCw0Y8g0L/RgNC+0LLQtdGA0LrQsDoNCiMgICAgINCU0LDQvdC90YvQtSDQtNC10LvRj9GC0YHRjyDQvdCwIDEwINGH0LDRgdGC0LXQuSwgOSDQuNGB0L/QvtC70YzQt9GD0Y7RgtGB0Y8g0LTQu9GPINC+0LHRg9GH0LXQvdC40Y8sIDEg0LTQu9GPINC/0YDQvtCy0LXRgNC60LgNCiMgICAgINCf0YDQvtGG0LXRgdGBINC/0L7QstGC0L7RgNGP0LXRgtGB0Y8gMTAg0YDQsNC3LCDQutCw0LbQtNGL0Lkg0YDQsNC3INGBINC90L7QstC+0Lkg0L/RgNC+0LLQtdGA0L7Rh9C90L7QuSDRh9Cw0YHRgtGM0Y4NCnN2bV9tb2RlbCA8LSBzdm0oU3BlY2llcyB+IC4sIGRhdGEgPSBpcmlzX2JpbmFyeSwga2VybmVsID0gImxpbmVhciIsIA0KICAgICAgICAgICAgICAgICBjb3N0ID0gMSwgY3Jvc3MgPSAxMCkNCmBgYA0KDQrQktGL0LLQtdC00YMg0YHQstC+0LTQutGDINC80L7QtNC10LvQuC4NCg0KYGBge3J9DQojIHN1bW1hcnkg0L/QvtC60LDQt9GL0LLQsNC10YI6DQojICAgLSDQv9Cw0YDQsNC80LXRgtGA0Ysg0LzQvtC00LXQu9C4IChrZXJuZWwsIGNvc3QsIGdhbW1hINC4INC00YAuKQ0KIyAgIC0g0LrQvtC70LjRh9C10YHRgtCy0L4g0L7Qv9C+0YDQvdGL0YUg0LLQtdC60YLQvtGA0L7Qsg0KIyAgIC0g0YDQtdC30YPQu9GM0YLQsNGC0Ysg0L/QtdGA0LXQutGA0LXRgdGC0L3QvtC5INC/0YDQvtCy0LXRgNC60Lgg0L/QviDQutCw0LbQtNC+0Lkg0LjQtyAxMCDQuNGC0LXRgNCw0YbQuNC5DQojICAgLSDRgdGA0LXQtNC90Y7RjiDRgtC+0YfQvdC+0YHRgtGMINC/0LXRgNC10LrRgNC10YHRgtC90L7QuSDQv9GA0L7QstC10YDQutC4DQpwcmludCgi0KHQstC+0LTQutCwINC80L7QtNC10LvQuCBTVk0iKQ0Kc3VtbWFyeShzdm1fbW9kZWwpDQpgYGANCg0KYGBgICAgICAgICAgDQpbMV0gItCh0LLQvtC00LrQsCDQvNC+0LTQtdC70LggU1ZNIiAgDQpDYWxsOiANCnN2bShmb3JtdWxhID0gU3BlY2llcyB+IC4sIGRhdGEgPSBpcmlzX2JpbmFyeSwga2VybmVsID0gImxpbmVhciIsIGNvc3QgPSAxLCBjcm9zcyA9IDEwKSAgIA0KDQpQYXJhbWV0ZXJzOg0KICAgIFNWTS1UeXBlOiAgQy1jbGFzc2lmaWNhdGlvbiAgIA0KICAgIFNWTS1LZXJuZWw6ICBsaW5lYXIgICAgICAgICANCiAgICBjb3N0OiAgMSAgIA0KICAgIA0KTnVtYmVyIG9mIFN1cHBvcnQgVmVjdG9yczogIDQgICANCiggMiAyICkgICANCg0KTnVtYmVyIG9mIENsYXNzZXM6ICAyICAgDQpMZXZlbHM6ICAgDQogc2V0b3NhIHZlcnNpY29sb3IgIA0KDQoxMC1mb2xkIGNyb3NzLXZhbGlkYXRpb24gb24gdHJhaW5pbmcgZGF0YTogIA0KDQpUb3RhbCBBY2N1cmFjeTogMTAwICANClNpbmdsZSBBY2N1cmFjaWVzOiAgDQogMTAwIDEwMCAxMDAgMTAwIDEwMCAxMDAgMTAwIDEwMCAxMDAgMTAwICANCmBgYA0KDQrQntGG0LXQvdGOINGC0L7Rh9C90L7RgdGC0Ywg0L/QtdGA0LXQutGA0LXRgdGC0L3QvtC5INC/0YDQvtCy0LXRgNC60LguDQoNCmBgYHtyfQ0KIyBzdm1fbW9kZWwkdG90LmFjY3VyYWN5INGF0YDQsNC90LjRgiDRgdGA0LXQtNC90Y7RjiDRgtC+0YfQvdC+0YHRgtGMINC/0L4g0LLRgdC10LwgMTAg0L/RgNC+0LLQtdGA0LrQsNC8DQojINCt0YLQviDQsdC+0LvQtdC1INC+0LHRitC10LrRgtC40LLQvdCw0Y8g0L7RhtC10L3QutCwINC60LDRh9C10YHRgtCy0LAg0LzQvtC00LXQu9C4LCDRh9C10Lwg0YLQvtGH0L3QvtGB0YLRjCDQvdCwINC+0LHRg9GH0LDRjtGJ0LXQuSDQstGL0LHQvtGA0LrQtQ0KcHJpbnQocm91bmQoc3ZtX21vZGVsJHRvdC5hY2N1cmFjeSwgMikpDQpgYGANCg0KYGBgICAgICAgICAgDQpbMV0gMTAwDQpgYGANCg0K0J/RgNC40LzQtdC90Y4g0LzQvtC00LXQu9GMINC6INC00LDQvdC90YvQvCBYINC4INCy0YvQstC10LTRgyDQvNCw0YLRgNC40YbRgyDQvtGI0LjQsdC+0LouDQoNCmBgYHtyfQ0Kc3ZtX3ByZWQgPC0gcHJlZGljdChzdm1fbW9kZWwsIFgpDQpjb25mX21hdHJpeCA8LSB0YWJsZShUcnVlID0geSwgUHJlZGljdGVkID0gc3ZtX3ByZWQpDQoNCnByaW50KCLQnNCw0YLRgNC40YbQsCDQvtGI0LjQsdC+0LoiKQ0KcHJpbnQoY29uZl9tYXRyaXgpDQpgYGANCg0KYGBgICAgICAgICAgDQpbMV0gItCc0LDRgtGA0LjRhtCwINC+0YjQuNCx0L7QuiINCiAgICAgICAgICAgICBQcmVkaWN0ZWQNClRydWUgICAgICAgICBzZXRvc2EgdmVyc2ljb2xvcg0KICBzZXRvc2EgICAgICAgICA1MCAgICAgICAgICAwICAgDQogIHZlcnNpY29sb3IgICAgICAwICAgICAgICAgNTANCmBgYA0KDQrQktGL0LLQtdC00YMg0YLQvtGH0L3QvtGB0YLRjCDQvdCwINC/0L7Qu9C90L7QvCDQvdCw0LHQvtGA0LUuDQoNCmBgYHtyfQ0KYWNjdXJhY3kgPC0gc3VtKGRpYWcoY29uZl9tYXRyaXgpKSAvIHN1bShjb25mX21hdHJpeCkNCnByaW50KHJvdW5kKGFjY3VyYWN5ICogMTAwLCAyKSkNCmBgYA0KDQpgYGAgICAgICAgICANClsxXSAxMDANCmBgYA0KDQojIyMg0JLRi9Cy0L7QtA0KDQrQnNC+0LTQtdC70Ywg0LHQtdC30L7RiNC40LHQvtGH0L3QviDQvtC/0YDQtdC00LXQu9C40LvQsCDQstGB0LUg0YLQuNC/0Ysg0L3QsCDQv9C+0LvQvdC+0Lkg0LLRi9Cx0L7RgNC60LUuDQoNCiMjINCX0LDQtNCw0L3QuNC1IDQNCg0KIyMjINCl0L7QtCDRgNCw0LHQvtGC0YsNCg0K0JLRi9C/0L7Qu9C90Y4g0YDQsNGB0YfQtdGCINCz0LvQsNCy0L3Ri9GFINC60L7QvNC/0L7QvdC10L3RgiDRgSDQuNGB0L/QvtC70YzQt9C+0LLQsNC90LjQtdC8INC/0LDQutC10YLQsCB2ZWdhbigpINC4INC10LPQviDRhNGD0L3QutGG0LjQuCByZGEoKS4NCg0K0KPRgdGC0LDQvdC+0LLQu9GOINC/0LDQutC10YIuDQoNCmBgYHtyfQ0KaW5zdGFsbC5wYWNrYWdlcygidmVnYW4iKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeSh2ZWdhbikNCmBgYA0KDQrQl9Cw0LPRgNGD0LbRgyDQtNCw0L3QvdGL0LUuDQoNCmBgYHtyfQ0KZGF0YShpcmlzKQ0KaGVhZChpcmlzKQ0KYGBgDQoNCtCS0YvQtNC10LvRjiDRh9C40YHQu9C+0LLRi9C1INC/0YDQuNC30L3QsNC60Lgg0LTQu9GPINC80LXRgtC+0LTQsCDQs9C70LDQstC90YvRhSDQutC+0LzQv9C+0L3QtdC90YIgKFBDQSkuDQoNCmBgYHtyfQ0KaXJpc19mZWF0dXJlcyA8LSBpcmlzWywgMTo0XSAjIFNlcGFsLkxlbmd0aCwgU2VwYWwuV2lkdGgsIFBldGFsLkxlbmd0aCwgUGV0YWwuV2lkdGgNCmBgYA0KDQrQktGL0L/QvtC70L3RjiBQQ0Eg0YEg0L/QvtC80L7RidGM0Y4gcmRhKCkuDQoNCmBgYHtyfQ0KIyBzY2FsZSA9IFRSVUUgLSDRgdGC0LDQvdC00LDRgNGC0LjQt9Cw0YbQuNGPINC00LDQvdC90YvRhSAo0L/RgNC40LLQvtC00LjRgiDQstGB0LUg0L/RgNC40LfQvdCw0LrQuCDQuiDQtdC00LjQvdC+0LzRgyDQvNCw0YHRiNGC0LDQsdGDKQ0KcGNhX3Jlc3VsdCA8LSByZGEoaXJpc19mZWF0dXJlcywgc2NhbGUgPSBUUlVFKQ0KDQpwcmludCgi0KHQstC+0LTQutCwINGA0LXQt9GD0LvRjNGC0LDRgtC+0LIgUENBIikNCnN1bW1hcnkocGNhX3Jlc3VsdCkNCmBgYA0KDQpgYGAgICAgICAgICANClsxXSAi0KHQstC+0LTQutCwINGA0LXQt9GD0LvRjNGC0LDRgtC+0LIgUENBIg0KDQpDYWxsOiANCnJkYShYID0gaXJpc19mZWF0dXJlcywgc2NhbGUgPSBUUlVFKQ0KDQpQYXJ0aXRpb25pbmcgb2YgY29ycmVsYXRpb25zOiAgICAgICAgICAgICAgIEluZXJ0aWEgUHJvcG9ydGlvbiBUb3RhbCAgICAgICAgICAgICAgIDQgICAgICAgICAgMSBVbmNvbnN0cmFpbmVkICAgICAgIDQgICAgICAgICAgMSAgRWlnZW52YWx1ZXMsIGFuZCB0aGVpciBjb250cmlidXRpb24gdG8gdGhlIGNvcnJlbGF0aW9ucyAgIEltcG9ydGFuY2Ugb2YgY29tcG9uZW50czogICAgICAgICAgICAgICAgICAgICAgICAgIFBDMSAgICBQQzIgICAgIFBDMyAgICAgIFBDNCBFaWdlbnZhbHVlICAgICAgICAgICAgMi45MTg1IDAuOTE0MCAwLjE0Njc2IDAuMDIwNzE1IFByb3BvcnRpb24gRXhwbGFpbmVkICAwLjcyOTYgMC4yMjg1IDAuMDM2NjkgMC4wMDUxNzkgQ3VtdWxhdGl2ZSBQcm9wb3J0aW9uIDAuNzI5NiAwLjk1ODEgMC45OTQ4MiAxLjAwMDAwMCANCmBgYA0KDQrQn9C+0YHRgtGA0L7RjiDQvtGA0LTQuNC90LDRhtC40L7QvdC90YPRjiDQtNC40LDQs9GA0LDQvNC80YMuDQoNCmBgYHtyfQ0KIyBiaXBsb3Qg0L7RgtC+0LHRgNCw0LbQsNC10YIg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+Og0KIyAtINCe0LHRitC10LrRgtGLICjRgtC+0YfQutC4KSAtIDE1MCDQuNGA0LjRgdC+0LIg0LIg0L/RgNC+0YHRgtGA0LDQvdGB0YLQstC1INC/0LXRgNCy0YvRhSDQtNCy0YPRhSDQs9C70LDQstC90YvRhSDQutC+0LzQv9C+0L3QtdC90YINCiMgLSDQn9GA0LjQt9C90LDQutC4ICjRgdGC0YDQtdC70LrQuCkgLSDQv9C+0LrQsNC30YvQstCw0Y7RgiDQvdCw0L/RgNCw0LLQu9C10L3QuNC1INC80LDQutGB0LjQvNCw0LvRjNC90L7Qs9C+INC40LfQvNC10L3QtdC90LjRjyDQv9GA0LjQt9C90LDQutC+0LINCiMgc2NhbGluZyA9ICJzcGVjaWVzIiAtINC80LDRgdGI0YLQsNCx0LjRgNC+0LLQsNC90LjQtSDQtNC70Y8g0LvRg9GH0YjQtdCz0L4g0L7RgtC+0LHRgNCw0LbQtdC90LjRjyDQv9GA0LjQt9C90LDQutC+0LINCiMgbWFpbiAtINC30LDQs9C+0LvQvtCy0L7QuiDQs9GA0LDRhNC40LrQsA0KYmlwbG90KHBjYV9yZXN1bHQsIHNjYWxpbmcgPSAic3BlY2llcyIsIG1haW4gPSAiUENBINC00LvRjyBJcmlzIikNCmBgYA0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0yOTMzNjg5NDQyLnBuZykNCg0K0JLRi9Cy0LXQtNGDINC00L7Qu9GOINC+0LHRitGP0YHQvdC10L3QvdC+0Lkg0LTQuNGB0L/QtdGA0YHQuNC4Lg0KDQpgYGB7cn0NCnByb3BfdmFyIDwtIHBjYV9yZXN1bHQkQ0EkZWlnIC8gc3VtKHBjYV9yZXN1bHQkQ0EkZWlnKQ0KcHJpbnQocm91bmQocHJvcF92YXIgKiAxMDAsIDIpKQ0KYGBgDQoNCmBgYCAgICAgICAgIA0KICBQQzEgICBQQzIgICBQQzMgICBQQzQNCjcyLjk2IDIyLjg1ICAzLjY3ICAwLjUyIA0KYGBgDQoNCjEuICAqKlBDMSAo0L/QtdGA0LLQsNGPINCz0LvQsNCy0L3QsNGPINC60L7QvNC/0L7QvdC10L3RgtCwKSoqINC+0LHRitGP0YHQvdGP0LXRgiAqKjcyLDk2wqAlKiog0LLRgdC10Lkg0LTQuNGB0L/QtdGA0YHQuNC4INCyINC00LDQvdC90YvRhS4g0K3RgtC+INC90LDQv9GA0LDQstC70LXQvdC40LUg0LIg0L/RgNC+0YHRgtGA0LDQvdGB0YLQstC1INC/0YDQuNC30L3QsNC60L7Qsiwg0LLQtNC+0LvRjCDQutC+0YLQvtGA0L7Qs9C+INC00LDQvdC90YvQtSDQuNC80LXRjtGCINC90LDQuNCx0L7Qu9GM0YjRg9GOINCy0LDRgNC40LDRgtC40LLQvdC+0YHRgtGMLiDQn9C10YDQstCw0Y8g0LrQvtC80L/QvtC90LXQvdGC0LAgwqvRg9GF0LLQsNGC0LjQu9Cwwrsg0L7RgdC90L7QstC90YPRjiDRh9Cw0YHRgtGMINC40L3RhNC+0YDQvNCw0YbQuNC4INC40Lcg0LjRgdGF0L7QtNC90YvRhSDQv9C10YDQtdC80LXQvdC90YvRhS4NCg0KMi4gICoqUEMyICjQstGC0L7RgNCw0Y8g0LPQu9Cw0LLQvdCw0Y8g0LrQvtC80L/QvtC90LXQvdGC0LApKiog0L7QsdGK0Y/RgdC90Y/QtdGCINC10YnRkSAqKjIyLDg1wqAlKiog0LTQuNGB0L/QtdGA0YHQuNC4LiDQntC90LAg0L7RgNGC0L7Qs9C+0L3QsNC70YzQvdCwICjQvdC10LrQvtGA0YDQtdC70LjRgNC+0LLQsNC90LApIFBDMSDQuCDQt9Cw0YXQstCw0YLRi9Cy0LDQtdGCINGB0LvQtdC00YPRjtGJ0YPRjiDQv9C+INCy0LXQu9C40YfQuNC90LUg0YfQsNGB0YLRjCDQstCw0YDQuNCw0YLQuNCy0L3QvtGB0YLQuCwg0LrQvtGC0L7RgNGD0Y4g0L3QtSDQvtCx0YrRj9GB0L3QuNC70LAgUEMxLiDQktC80LXRgdGC0LUg0L7QvdC4INC+0LHRitGP0YHQvdGP0Y7RgiA5NS44MSUg0LLQsNGA0LjQsNGG0LjQuCDigJQg0LTQvtGB0YLQsNGC0L7Rh9C90L4g0LTQu9GPINCw0L3QsNC70LjQt9CwLg0KDQozLiAgKipQQzMqKiDQtNC+0LHQsNCy0LvRj9C10YIg0LLRgdC10LPQviAqKjMsNjfCoCUqKiDQvtCx0YrRj9GB0L3RkdC90L3QvtC5INC00LjRgdC/0LXRgNGB0LjQuC4g0JLQutC70LDQtCDRjdGC0L7QuSDQutC+0LzQv9C+0L3QtdC90YLRiyDRg9C20LUg0L3QtdCy0LXQu9C40Log4oCUINC+0L3QsCDQvtC/0LjRgdGL0LLQsNC10YIg0L7RgtC90L7RgdC40YLQtdC70YzQvdC+INC80LDQu9GL0LUg0LfQsNC60L7QvdC+0LzQtdGA0L3QvtGB0YLQuCDQsiDQtNCw0L3QvdGL0YUuDQoNCjQuICAqKlBDNCoqINC+0LHRitGP0YHQvdGP0LXRgiDQu9C40YjRjCAqKjAsNTLCoCUqKiDQtNC40YHQv9C10YDRgdC40LguINCt0YLQviDQvtGH0LXQvdGMINC80LDQu9Cw0Y8g0LTQvtC70Y8sINC60L7RgtC+0YDQsNGPLCDRgdC60L7RgNC10LUg0LLRgdC10LPQviwg0YHQvtC+0YLQstC10YLRgdGC0LLRg9C10YIg0YjRg9C80YMg0LjQu9C4INC60YDQsNC50L3QtSDQvdC10LfQvdCw0YfQuNGC0LXQu9GM0L3Ri9C8INC/0LDRgtGC0LXRgNC90LDQvCDQsiDQtNCw0L3QvdGL0YUuDQo=