https://stepik.org/course/129/syllabus

1 Предобработка данных

setwd(“~/R/data_analysis_1”)

1.3 Работа с data frame

Video 1.3.1

https://stepik.org/lesson/11481/step/3?unit=2523

Reading data

?read.table

?read.csv

df_evals <- read.csv("https://stepic.org/media/attachments/lesson/11481/evals.csv", stringsAsFactors=TRUE)

Summaries

head(df_evals, 5) # show 5 first rows (default=6)
tail(df_evals, 5) # show 5 last rows (default=6)
summary(df_evals)  # summary information
names(df_evals)  # show columns names
str(df_evals)  # show structure: columns information
View(df_evals)  # show data frame in table format
# The same table will appear if Data will be clicked in Environment (right upper window)

Video 1.3.2

https://stepik.org/lesson/11481/step/4?unit=2523

Variables, indexes

df_evals$score  # score column
df_evals$score[1:10]  # show first 10 values of 'score' column
df_evals[5, 1]  # show value of 1st column and 5th row in data frame
df_evals[c(10, 15, 64), 1]  # show several values of 1st column with row number 10, 15, 64
df_evals[5,]  # show 5th row
df_evals[,1]  # show 1st column: equivalent df_evals$score
head(df_evals[,2:5]) # show head of columns from second to fifth
mean(df_evals$score) # mean score
summary(df_evals$score) # score statistical information

Creating and deleting columns

df_evals$ten_point_scale <- df_evals$score * 2  # create new column 'ten_point_scale'
df_evals$number <- NULL  # delete column 'number'
nrow(df_evals)  # rows number
ncol(df_evals)  # columns number
df_evals$number <- 1 : nrow(df_evals)  # create column with row number

Video 1.3.3

https://stepik.org/lesson/11481/step/5?unit=2523

Subsetting with condition

head(df_evals[, c(1, 3, 4)]) # show 1, 3, 4 columns
head(df_evals[, c("gender", "age")])  # show gender and age columns
df_evals[df_evals$gender == 'female',]  # show rows with gender == 'female'
head(df_evals[df_evals$gender == 'female', 1:5])  # show head of rows with gender == 'female' and columns from 1st to 5th
head(subset(df_evals, gender == 'female'))  # show only rows with female gender
subset(df_evals, gender == 'female' & score > 4.9)  # two conditions of gender and score
df_evals[c(1, 4, 5), c(1, 4)]  # combined indexation

rbind and cbind

# rbind
df_evals_female <- subset(df_evals, gender == 'female' & score > 4.9)
df_evals_male <- subset(df_evals, gender == 'male' & score > 4.9)

df_rbind = rbind(df_evals_female, df_evals_male)  # concatenate to df_evals_female and df_evals_male by rows
df_rbind
# cbind
df_evals_1 <- head(df_evals[,1:3])
df_evals_2 <- head(df_evals[,5:7])

df_evals_3 <- cbind(df_evals_1, df_evals_2) # concatenate df_evals_1 and df_evals_2 by columns
df_evals_3

Standard R datasets

Когда вы запускаете R, автоматически загружаются основные библиотеки для работы. В их числе библиотека *datasets**, в которой хранятся наборы данных из разных исследований. Чтобы просмотреть весь список доступных датасетов, выполните команду:

library(help = "datasets")  # Standard R datasets

Некоторые из этих датасетов мы будем использовать в наших задачах. Они доступны по имени в любой момент работы. Например, если вы захотите использовать датасет mtcars, можете просто выполнить команду mtcars, чтобы увидеть данные в консоли.

data(mtcars)  # добавит датасет в рабочую среду
help(mtcars)  # выведет информацию о датасете
my_data <- mtcars  # запишет датасет в новую переменную

Task 1.3.1

https://stepik.org/lesson/11481/step/7?unit=2523

В этой задаче поработаем со встроенными данными mtcars. В датафрэйме mtcars создайте новую колонку (переменную) под названием even_gear, в которой будут единицы, если значение переменной (gear) четное, и нули если количество нечетное.

data(mtcars)
head(mtcars)
mtcars$even_gear <- (mtcars$gear %% 2 == 0) * 1
mtcars$even_gear[1:5]
[1] 1 1 1 0 0

Task 1.3.2

https://stepik.org/lesson/11481/step/8?unit=2523

Продолжим нашу работу с данными mtcars. Теперь ваша задача создать переменную - вектор mpg_4 и сохранить в нее значения расхода топлива (mpg) для машин с четырьмя цилиндрами (cyl).

mpg_4 <- mtcars$mpg[mtcars$cyl == 4]  # equivalent: mtcars[mtcars$cyl == 4, "mpg"]
mpg_4
 [1] 22.8 24.4 22.8 32.4 30.4 33.9 21.5 27.3 26.0 30.4 21.4

Task 1.3.3

https://stepik.org/lesson/11481/step/9?unit=2523

А теперь научимся отбирать только некоторые строчки из исходных данных.

Ваша задача создать новый dataframe под названием mini_mtcars, в котором будут сохранены только третья, седьмая, десятая, двенадцатая и последняя строчка датафрейма mtcars.

mini_mtcars <- mtcars[c(3, 7, 10, 12, nrow(mtcars)), ]
mini_mtcars

Task 1.3.4

https://stepik.org/lesson/11481/step/10?unit=2523

Укажите какая команда создаст сабсет данных mtcars, только для тех автомобилей, у которых число цилиндров (cyl) не равняется 3, и время разгона автомобиля (qsec) больше среднего по выборке.

s <- subset(mtcars, cyl != 3 & qsec > mean(qsec))
s

1.4 Элементы синтаксиса

Video 1.4.1

https://stepik.org/lesson/11478/step/2?unit=2520

df_evals <- read.csv('https://stepic.org/media/attachments/lesson/11481/evals.csv')  # load dataframe

if

a <- 0

if (a > 0){
  print('positive')
} else if (a < 0) {
  print('negative')
} else {
  print('zero')
}
[1] "zero"

ifelse

ifelse(a > 0, 'positive', 'not positive')  # 'ifelse' is able to work with arrays, see example below
[1] "not positive"
a <- c(1, -1, 2, 3)
ifelse(a > 0, 'positive', 'not positive')
[1] "positive"     "not positive" "positive"     "positive"    

Video 1.4.2

https://stepik.org/lesson/11478/step/3?unit=2520

for

for (i in 1:5) {
  print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5

for in dataset

for (i in 460:nrow(df_evals)){
  print(df_evals$score[i])
}
[1] 3.5
[1] 4.4
[1] 4.4
[1] 4.1

for + if

for (i in 1:nrow(df_evals)){
  if (df_evals$score[i] > 4.9){
    print(df_evals$age[i])
  }
}
[1] 47
[1] 47
[1] 47
[1] 46
[1] 46
[1] 50
[1] 50
[1] 57
[1] 58
[1] 58
[1] 58

for + if vs ifelse

for + if
df_evals$quality <- rep(NA, nrow(df_evals))  # create empty array with length of data set df_evals

for (i in 1:nrow(df_evals)){
  if (df_evals$score[i] > 4){
    df_evals$quality[i] <- 'good'
  } else df_evals$quality[i] <- 'bad'
}

df_evals$quality[1:10]
 [1] "good" "good" "bad"  "good" "good" "good" "bad"  "good" "bad"  "good"
for + ifelse
df_evals$quality <- ifelse(df_evals$score < 4, 'good', 'bad')

while

i <- 1

# first four scores
while (i < 5){
  print(df_evals$score[i])
  i <- i + 1
}
[1] 4.7
[1] 4.1
[1] 3.9
[1] 4.8

Task 1.4.1

https://stepik.org/lesson/11478/step/4?unit=2520

Создайте новую числовую переменную new_var в данных mtcars, которая содержит единицы в строчках, если в машине не меньше четырёх карбюраторов (переменная carb) или больше шести цилиндров (переменная cyl). В строчках, в которых условие не выполняется, должны стоять нули.

df <- mtcars
df$new_var <- ifelse(df$carb >= 4 | df$cyl > 6, 1, 0)
head(df)

Task 1.4.2

https://stepik.org/lesson/11478/step/5?unit=2520

В уже существующей переменной my_vector сохранен вектор из 50 чисел. Если среднее значение вектора my_vector больше 20, в переменную result сохраните “My mean is great”, если среднее значение my_vector меньше или равно 20 то в переменную result сохраните строку “My mean is not so great”.

my_vector = 1:50

if (mean(my_vector) > 20){
  result <- "My mean is great"
} else result <- "My mean is not so great"
print(result)
[1] "My mean is great"

Task 1.4.3

https://stepik.org/lesson/11478/step/6?unit=2520

В этой задаче от вас потребуется узнать некоторую информацию о типах данных в R самостоятельно! Встроенные в R данные AirPassengers - это новый для нас формат данных типа Time-Series. Изучите структуру этих данных, прежде чем начать решение задачи! Например напишите команды:

?AirPassengers      # справка о данных
str(AirPassengers)  # структура данных

В встроенных в R данных AirPassengers хранится 144 значения (количество пассажиров в месяц) с 1949 по 1960 год. Данные Time-Series очень похожи на вектор по своей структуре, например мы можем обратиться к любому из 144 элементов используя уже знакомую нам индексацию AirPassengers[1] или AirPassengers[56].

Можно вообще перевести исходные данные в вектор при помощи команды as.vector(AirPassengers) и продолжить с ними работу как с вектором.

И так ваша задача создать переменную good_months и сохранить в нее число пассажиров только в тех месяцах, в которых это число больше, чем показатель в предыдущем месяце.

Важный момент! В R оператор : для создания последовательности имеет приоритет над арифметическими действиями. Таким образом, если у вас есть переменная i, равная 10, и вы хотите создать вектор от 1 до i - 1, воспользуйтесь скобками, чтобы указать последовательность действий.

good_months <- c()

for (i in 2:length(AirPassengers)){
  if (AirPassengers[i] > AirPassengers[i - 1]){
    good_months <- c(good_months, AirPassengers[i])
  }
}

# one row script 1:
# good_months <- AirPassengers[c(FALSE, AirPassengers[2:144] > AirPassengers[1:143])]

# one row script 2:
# good_months <- AirPassengers[-1][AirPassengers[-1] > AirPassengers[-144]] 

good_months
 [1] 118 132 135 148 118 126 141 149 170 140 145 150 178 172 178 199 166 171 180 193 183 218 230 242 194 196 236
[28] 243 264 272 201 204 235 234 264 302 229 242 267 269 270 315 364 278 284 317 318 374 413 306 315 356 355 422
[55] 465 467 336 340 362 363 435 491 505 337 360 406 420 472 548 559 405 417 419 461 472 535 622 432

Task 1.4.4

https://stepik.org/lesson/11478/step/7?unit=2520

Задачка для супер героев, повышенной сложности!

Для встроенных в R данных AirPassengers рассчитайте скользящее среднее с интервалом сглаживания равным 10. Напечатайте получившийся результат (первым значением в выводе должно быть среднее для элементов 1:10, во втором значении - среднее для элементов 2:11 и т.д., в последнем - среднее для элементов 135:144)

Все полученные значения средних сохраните в переменную moving_average.

moving_average <- numeric(135) # empty array with length 135

for (i in 1:(length(AirPassengers) - 9)){
  moving_average[i] <- mean(AirPassengers[i:(i + 9)])
}

moving_average[1:10]
 [1] 129.8 129.0 129.0 127.3 127.0 129.0 129.0 126.7 126.8 130.2

Можно решить и без цикла при помощи разностей кумулятивных сумм!

n <- 10    
d <- AirPassengers    
cx <- c(0, cumsum(d))    
moving_average <- (cx[(n + 1):length(cx)] - cx[1:(length(cx) - n)]) / n
moving_average[1:10]
 [1] 129.8 129.0 129.0 127.3 127.0 129.0 129.0 126.7 126.8 130.2

1.5 Описательные статистики

Video 1.5.2

https://stepik.org/lesson/11479/step/3?unit=2521

Read and prepare data frame

?mtcars

mtcars_df <- mtcars
str(mtcars_df)  # show data frame structure
# change types of vs and am columns
mtcars_df$vs <- factor(mtcars_df$vs, labels = c("V", "S"))  # "vs" from num type to factor type
mtcars_df$am <- factor(mtcars_df$am, labels = c("Auto", "Manual")) # "am" from num type to factor type
str(mtcars_df)

Video 1.5.3

https://stepik.org/lesson/11479/step/4?unit=2521

median, mean, sd, range

median(mtcars_df$mpg)  # median mpg
mean(mtcars_df$disp)   # mean displacement
sd(mtcars_df$hp)       # standard deviation of horse power
range(mtcars_df$cyl)   # range
# mean mpg of 6 cyl cars with V-engines
mean(mtcars_df$mpg[mtcars_df$cyl == 6 & mtcars_df$vs == "V"])
# standard deviation for cars with automatic transmission and number cylinders is not equal 3
sd(mtcars_df$hp[mtcars_df$cyl != 3 & mtcars_df$am == "Auto"])

Task 1.5.1

https://stepik.org/lesson/11479/step/5?unit=2521

Вновь вернемся к данным mtcars. Рассчитайте среднее значение времени разгона (qsec) для автомобилей, число цилиндров (cyl) у которых не равняется 3 и показатель количества миль на галлон топлива (mpg) больше 20.

Получившийся результат (среднее значение) сохраните в переменную result.

result <- mean(mtcars_df$qsec[mtcars_df$cyl != 3 & mtcars_df$mpg > 20])
result
[1] 18.81643

Video 1.5.4

https://stepik.org/lesson/11479/step/6?unit=2521

aggregate

?aggregate

agg <- aggregate(x = mtcars_df$hp, by = list(mtcars_df$vs), FUN = mean)  # mean horse power for V and S engines
agg
mean_hp_vs <- aggregate(x = mtcars_df$hp, by = list(mtcars_df$vs), FUN = mean)
colnames(mean_hp_vs) <- c("VS", "Mean HP")  # rename columns
mean_hp_vs
# equivalent script (shorter way)
aggregate(hp ~ vs, mtcars_df, mean)
aggregate(hp ~ vs + am, mtcars_df, mean) # with two 'by' categories
# equivalent longer script
# aggregate(x = mtcars_df$hp, by = list(mtcars_df$vs, mtcars_df$am), FUN = mean)
# all median values of num type columns
agg <- aggregate(x = mtcars_df[, -c(8, 9)], by = list(mtcars_df$vs, mtcars_df$am), FUN = median)
agg
# sd values of mpg and disp columns
agg <- aggregate(x = mtcars_df[, c("mpg", "disp")], by = list(mtcars_df$vs, mtcars_df$am), FUN = sd)
agg
# equivalent shorter script
#aggregate(cbind(mpg, disp) ~ am + vs, mtcars_df, sd)

Task 1.5.2

https://stepik.org/lesson/11479/step/7?unit=2521

При помощи функции aggregate рассчитайте стандартное отклонение переменной hp (лошадиные силы) и переменной disp (вместимости двигателя) у машин с автоматической и ручной коробкой передач.

Полученные результаты (результаты выполнения функции aggregate) сохраните в переменную descriptions_stat.

descriptions_stat <- aggregate(cbind(hp, disp) ~ am, mtcars, sd)
descriptions_stat

Video 1.5.5

https://stepik.org/lesson/11479/step/8?unit=2521

describe

?describe

d <- describe(x = mtcars_df[, -c(8, 9)])
d

Video 1.5.6

https://stepik.org/lesson/11479/step/9?unit=2521

describeBy

?describeBy

library(psych)
# 'mat' provides output to one matrix
d <- describeBy(x = mtcars_df[, -c(8, 9)], group = mtcars_df$vs, mat =  T, digits = 2)
d
# 'fast' provides less columns of describe function
d <- describeBy(x = mtcars_df[, -c(8, 9)], group = mtcars_df$vs, mat =  T, digits = 2, fast = T)
d
d <- describeBy(mtcars_df$qsec, group = list(mtcars_df$vs, mtcars_df$am), mat = T, digits = 1, fast = T)
d

Video 1.5.7

https://stepik.org/lesson/11479/step/10?unit=2521

check N/A values

sum(is.na(mtcars_df))  # if 0 it means that all values are available
[1] 0
mean(mtcars_df$mpg, na.rm = T)  # na.rm > na remove: remove all na values
[1] 20.09062
# aggregate function removes na values by default

Task 1.5.3

https://stepik.org/lesson/11479/step/11?unit=2521

Воспользуемся встроенными данными airquality. В новую переменную сохраните subset исходных данных, оставив наблюдения только для месяцев 7, 8 и 9.

При помощи функции aggregate рассчитайте количество непропущенных наблюдений по переменной Ozone в 7, 8 и 9 месяце. Для определения количества наблюдений используйте функцию length().

Результат выполнения функции aggregate сохраните в переменную result.

result <- aggregate(Ozone ~ Month, subset(airquality, Month %in% c(7, 8, 9)), length)
result

Task 1.5.4

https://stepik.org/lesson/11479/step/12?unit=2521

Примените функцию describeBy к количественным переменным данных airquality, группируя наблюдения по переменной Month. Чему равен коэффициент асимметрии (skew) переменной Wind в восьмом месяце?

# str(airquality)
d <- describeBy(airquality$Wind, group = list(airquality$Month), mat = T, digits = 3)
d$skew[d$group1 == 8]
[1] 0.035
# library(psych)
d <- skew(subset(airquality, Month == 8)$Wind)
d
[1] 0.03549581

Task 1.5.5

https://stepik.org/lesson/11479/step/13?unit=2521

Обратимся к встроенным данным iris. Соотнесите значения стандартного отклонения переменных.

res <- describe(iris)
res['sd']

Task 1.5.6

https://stepik.org/lesson/11479/step/14?unit=2521

В данных iris расположите по убыванию значения медиан количественных переменных в группе virginica.

#sort(sapply(iris[iris$Species =='virginica',][-5], FUN=median))
d <- describeBy(iris[,1:4], group = iris$Species)$'virginica'['median']
d

Task 1.5.7

https://stepik.org/lesson/11479/step/15?unit=2521

В переменной my_vector сохранен вектор с пропущенными значениями. Вам нужно создать новый вектор fixed_vector, в котором все пропущенные значения вектора my_vector будут заменены на среднее значение по имеющимся наблюдениям.

При этом исходный вектор оставьте без изменений!

?replace

?aggregate

my_vector <- rnorm(30)
my_vector[sample(1:30, 10)] <- NA  # на десять случайных позиций поместим NA
my_vector_mean = mean(my_vector[!is.na(my_vector)])  # mean value of my_vector elements

fixed_vector = replace(my_vector, is.na(my_vector), my_vector_mean)  # replace na elements with mean value

# another solution
# fixed_vector <- replace(my_vector, is.na(my_vector), mean(my_vector, na.rm = T))

fixed_vector
 [1]  0.162019833  0.162019833  0.162019833 -0.994789932 -0.079637674  0.508381304 -0.393351838  0.162019833
 [9]  0.356677781  0.162019833  0.162019833  0.332249004 -0.633532200 -0.673211856  0.355179160  0.382346243
[17]  2.082183352  1.187997779 -0.358954408  0.162019833 -0.773771710 -0.207107907  0.007899274  0.768779799
[25]  0.162019833  0.671175903  0.893696069  0.162019833  0.162019833 -0.191811483

1.6 Описательные статистики. Графики

Video 1.6.1

https://stepik.org/lesson/11787/step/2?unit=2671

# Read data
df <- mtcars
df$vs <- factor(df$vs, labels = c("V", "S"))
df$am <- factor(df$am, labels = c("Auto", "Manual"))

histogram

?hist

diagram <- hist(df$mpg, breaks = 5, main = "Histogram of MPG", xlab = "MPG")

diagram
$breaks
[1] 10 15 20 25 30 35

$counts
[1]  6 12  8  2  4

$density
[1] 0.0375 0.0750 0.0500 0.0125 0.0250

$mids
[1] 12.5 17.5 22.5 27.5 32.5

$xname
[1] "df$mpg"

$equidist
[1] TRUE

attr(,"class")
[1] "histogram"

boxplot

?boxplot

diagram <- boxplot(mpg ~ am, df, xlab = "Gearbox type", ylab = "MPG")

diagram
$stats
      [,1] [,2]
[1,] 10.40 15.0
[2,] 14.95 21.0
[3,] 17.30 22.8
[4,] 19.20 30.4
[5,] 24.40 33.9

$n
[1] 19 13

$conf
         [,1]    [,2]
[1,] 15.75947 18.6808
[2,] 18.84053 26.9192

$out
numeric(0)

$group
numeric(0)

$names
[1] "Auto"   "Manual"

plot

diagram <- plot(df$mpg, df$hp)

diagram
NULL

Video 1.6.2

https://stepik.org/lesson/11787/step/3?unit=2671

ggplot2

library(ggplot2) # load library

geom_histogram (ggplot2)

ggplot(df, aes(x = mpg))+
  geom_histogram(fill = "white", col = "black", binwidth = 2)

geom_dotplot (ggplot2)

ggplot(df, aes(x = mpg, fill = am))+
  geom_dotplot(binwidth = 1)

geom_density (ggplot2)

ggplot(df, aes(x = mpg, fill = am))+
  geom_density(alpha = 0.3)

Video 1.6.3

https://stepik.org/lesson/11787/step/4?unit=2671

geom_boxplot

ggplot(df, aes(x = am, y = hp, col = vs))+
  geom_boxplot()

geom_point

ggplot(df, aes(x = mpg, y = hp, col = vs, size = qsec))+
  geom_point()

Task 1.6.1

https://stepik.org/lesson/11787/step/5?unit=2671

При помощи функции ggplot() или boxplot() постройте график boxplot, используя встроенные в R данные airquality. По оси x отложите номер месяца, по оси y — значения переменной Ozone.

На графике boxplot отдельными точками отображаются наблюдения, отклоняющиеся от 1 или 3 квартиля больше чем на полтора межквартильных размаха. Сколько таких наблюдений присутствует в сентябре (месяц №9)?

Обратите внимание, что для корректного отображения графика ggplot ожидает факторную переменную по оси x.

df <- airquality
df$Month <- factor(df$Month, labels = 5 : 9)
# library(ggplot2)
ggplot(df, aes(x = Month, y = Ozone))+
  geom_boxplot(na.rm = T)

Task 1.6.2

https://stepik.org/lesson/11787/step/6?unit=2671

Используем знакомые нам данные mtcars.

Нужно построить scatterplot с помощью ggplot из ggplot2, по оси x которого будет mpg, по оси y - disp, а цветом отобразить переменную (hp).

Полученный график нужно сохранить в переменную plot1.

plot1 <- ggplot(mtcars, aes(x = mpg, y = disp, col = hp))+
  geom_point()
plot1

Task 1.6.3

https://stepik.org/lesson/11787/step/7?unit=2671

Укажите, при помощи какого варианта кода мы можем построить следующий график по данным *iris**. Гистограмма распределения переменной Sepal.Length, в которой цвет заполнения столбцов гистограммы зависит от значения переменной Species.

# ggplot(iris, aes(Sepal.Length)) + geom_histogram(aes(fill = Species))
obj <- ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_histogram()
obj
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Task 1.6.4

https://stepik.org/lesson/11787/step/8?unit=2671

Студент Ярослав очень любит строить графики в R. Основываясь на данных *iris** он хочет построить следующий график.

Scatterplot (диаграмма рассеивания), где по оси X будет отложена переменная Sepal.Length, по оси Y переменная Sepal.Width. За цвет точек будет отвечать переменная Species, а за размер точек переменная Petal.Length.

ggplot(iris, aes(Sepal.Length, Sepal.Width, col = Species, size = Petal.Length)) +
  geom_point()

1.7 Сохранение результатов

Video 1.7.1

https://stepik.org/lesson/11480/step/2?unit=2522

getwd()  # show work directory "C:/Users/xxx/Documents"
setwd("~/R/data_analysis_1")  # set work directory

write.csv(mtcars, "mtcars.csv")  # save data frame as csv file
my_mean = mean(10^5 : 10^7)
save(my_mean, "my_mean.RData")  # save value as file RData
LS0tDQp0aXRsZTogItCQ0L3QsNC70LjQtyDQtNCw0L3QvdGL0YUg0LIgUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQpodHRwczovL3N0ZXBpay5vcmcvY291cnNlLzEyOS9zeWxsYWJ1cw0KDQojIDEg0J/RgNC10LTQvtCx0YDQsNCx0L7RgtC60LAg0LTQsNC90L3Ri9GFDQoNCg0Kc2V0d2QoIn4vUi9kYXRhX2FuYWx5c2lzXzEiKQ0KDQoNCiMjIDEuMyDQoNCw0LHQvtGC0LAg0YEgZGF0YSBmcmFtZQ0KDQoNCiMjIyBWaWRlbyAxLjMuMQ0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ4MS9zdGVwLzM/dW5pdD0yNTIzDQoNCiMjIyMgUmVhZGluZyBkYXRhDQoNCj9yZWFkLnRhYmxlDQoNCj9yZWFkLmNzdg0KDQpgYGB7cn0NCmRmX2V2YWxzIDwtIHJlYWQuY3N2KCJodHRwczovL3N0ZXBpYy5vcmcvbWVkaWEvYXR0YWNobWVudHMvbGVzc29uLzExNDgxL2V2YWxzLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnM9VFJVRSkNCmBgYA0KDQojIyMjIFN1bW1hcmllcw0KDQpgYGB7cn0NCmhlYWQoZGZfZXZhbHMsIDUpICMgc2hvdyA1IGZpcnN0IHJvd3MgKGRlZmF1bHQ9NikNCnRhaWwoZGZfZXZhbHMsIDUpICMgc2hvdyA1IGxhc3Qgcm93cyAoZGVmYXVsdD02KQ0Kc3VtbWFyeShkZl9ldmFscykgICMgc3VtbWFyeSBpbmZvcm1hdGlvbg0KbmFtZXMoZGZfZXZhbHMpICAjIHNob3cgY29sdW1ucyBuYW1lcw0Kc3RyKGRmX2V2YWxzKSAgIyBzaG93IHN0cnVjdHVyZTogY29sdW1ucyBpbmZvcm1hdGlvbg0KYGBgDQoNCmBgYHtyfQ0KVmlldyhkZl9ldmFscykgICMgc2hvdyBkYXRhIGZyYW1lIGluIHRhYmxlIGZvcm1hdA0KIyBUaGUgc2FtZSB0YWJsZSB3aWxsIGFwcGVhciBpZiBEYXRhIHdpbGwgYmUgY2xpY2tlZCBpbiBFbnZpcm9ubWVudCAocmlnaHQgdXBwZXIgd2luZG93KQ0KYGBgDQoNCiMjIyBWaWRlbyAxLjMuMg0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ4MS9zdGVwLzQ/dW5pdD0yNTIzDQoNCiMjIyMgVmFyaWFibGVzLCBpbmRleGVzDQoNCmBgYHtyfQ0KZGZfZXZhbHMkc2NvcmUgICMgc2NvcmUgY29sdW1uDQpkZl9ldmFscyRzY29yZVsxOjEwXSAgIyBzaG93IGZpcnN0IDEwIHZhbHVlcyBvZiAnc2NvcmUnIGNvbHVtbg0KZGZfZXZhbHNbNSwgMV0gICMgc2hvdyB2YWx1ZSBvZiAxc3QgY29sdW1uIGFuZCA1dGggcm93IGluIGRhdGEgZnJhbWUNCmRmX2V2YWxzW2MoMTAsIDE1LCA2NCksIDFdICAjIHNob3cgc2V2ZXJhbCB2YWx1ZXMgb2YgMXN0IGNvbHVtbiB3aXRoIHJvdyBudW1iZXIgMTAsIDE1LCA2NA0KZGZfZXZhbHNbNSxdICAjIHNob3cgNXRoIHJvdw0KZGZfZXZhbHNbLDFdICAjIHNob3cgMXN0IGNvbHVtbjogZXF1aXZhbGVudCBkZl9ldmFscyRzY29yZQ0KaGVhZChkZl9ldmFsc1ssMjo1XSkgIyBzaG93IGhlYWQgb2YgY29sdW1ucyBmcm9tIHNlY29uZCB0byBmaWZ0aA0KYGBgDQoNCmBgYHtyfQ0KbWVhbihkZl9ldmFscyRzY29yZSkgIyBtZWFuIHNjb3JlDQpzdW1tYXJ5KGRmX2V2YWxzJHNjb3JlKSAjIHNjb3JlIHN0YXRpc3RpY2FsIGluZm9ybWF0aW9uDQpgYGANCg0KIyMjIyBDcmVhdGluZyBhbmQgZGVsZXRpbmcgY29sdW1ucw0KDQpgYGB7cn0NCmRmX2V2YWxzJHRlbl9wb2ludF9zY2FsZSA8LSBkZl9ldmFscyRzY29yZSAqIDIgICMgY3JlYXRlIG5ldyBjb2x1bW4gJ3Rlbl9wb2ludF9zY2FsZScNCmRmX2V2YWxzJG51bWJlciA8LSBOVUxMICAjIGRlbGV0ZSBjb2x1bW4gJ251bWJlcicNCmBgYA0KDQpgYGB7cn0NCm5yb3coZGZfZXZhbHMpICAjIHJvd3MgbnVtYmVyDQpuY29sKGRmX2V2YWxzKSAgIyBjb2x1bW5zIG51bWJlcg0KYGBgDQoNCmBgYHtyfQ0KZGZfZXZhbHMkbnVtYmVyIDwtIDEgOiBucm93KGRmX2V2YWxzKSAgIyBjcmVhdGUgY29sdW1uIHdpdGggcm93IG51bWJlcg0KYGBgDQoNCiMjIyBWaWRlbyAxLjMuMw0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ4MS9zdGVwLzU/dW5pdD0yNTIzDQoNCiMjIyMgU3Vic2V0dGluZyB3aXRoIGNvbmRpdGlvbg0KDQpgYGB7cn0NCmhlYWQoZGZfZXZhbHNbLCBjKDEsIDMsIDQpXSkgIyBzaG93IDEsIDMsIDQgY29sdW1ucw0KaGVhZChkZl9ldmFsc1ssIGMoImdlbmRlciIsICJhZ2UiKV0pICAjIHNob3cgZ2VuZGVyIGFuZCBhZ2UgY29sdW1ucw0KZGZfZXZhbHNbZGZfZXZhbHMkZ2VuZGVyID09ICdmZW1hbGUnLF0gICMgc2hvdyByb3dzIHdpdGggZ2VuZGVyID09ICdmZW1hbGUnDQpoZWFkKGRmX2V2YWxzW2RmX2V2YWxzJGdlbmRlciA9PSAnZmVtYWxlJywgMTo1XSkgICMgc2hvdyBoZWFkIG9mIHJvd3Mgd2l0aCBnZW5kZXIgPT0gJ2ZlbWFsZScgYW5kIGNvbHVtbnMgZnJvbSAxc3QgdG8gNXRoDQpoZWFkKHN1YnNldChkZl9ldmFscywgZ2VuZGVyID09ICdmZW1hbGUnKSkgICMgc2hvdyBvbmx5IHJvd3Mgd2l0aCBmZW1hbGUgZ2VuZGVyDQpzdWJzZXQoZGZfZXZhbHMsIGdlbmRlciA9PSAnZmVtYWxlJyAmIHNjb3JlID4gNC45KSAgIyB0d28gY29uZGl0aW9ucyBvZiBnZW5kZXIgYW5kIHNjb3JlDQpkZl9ldmFsc1tjKDEsIDQsIDUpLCBjKDEsIDQpXSAgIyBjb21iaW5lZCBpbmRleGF0aW9uDQpgYGANCg0KIyMjIyByYmluZCBhbmQgY2JpbmQNCg0KYGBge3J9DQojIHJiaW5kDQpkZl9ldmFsc19mZW1hbGUgPC0gc3Vic2V0KGRmX2V2YWxzLCBnZW5kZXIgPT0gJ2ZlbWFsZScgJiBzY29yZSA+IDQuOSkNCmRmX2V2YWxzX21hbGUgPC0gc3Vic2V0KGRmX2V2YWxzLCBnZW5kZXIgPT0gJ21hbGUnICYgc2NvcmUgPiA0LjkpDQoNCmRmX3JiaW5kID0gcmJpbmQoZGZfZXZhbHNfZmVtYWxlLCBkZl9ldmFsc19tYWxlKSAgIyBjb25jYXRlbmF0ZSB0byBkZl9ldmFsc19mZW1hbGUgYW5kIGRmX2V2YWxzX21hbGUgYnkgcm93cw0KZGZfcmJpbmQNCmBgYA0KDQpgYGB7cn0NCiMgY2JpbmQNCmRmX2V2YWxzXzEgPC0gaGVhZChkZl9ldmFsc1ssMTozXSkNCmRmX2V2YWxzXzIgPC0gaGVhZChkZl9ldmFsc1ssNTo3XSkNCg0KZGZfZXZhbHNfMyA8LSBjYmluZChkZl9ldmFsc18xLCBkZl9ldmFsc18yKSAjIGNvbmNhdGVuYXRlIGRmX2V2YWxzXzEgYW5kIGRmX2V2YWxzXzIgYnkgY29sdW1ucw0KZGZfZXZhbHNfMw0KDQpgYGANCg0KIyMjIyBTdGFuZGFyZCBSIGRhdGFzZXRzDQoNCtCa0L7Qs9C00LAg0LLRiyDQt9Cw0L/Rg9GB0LrQsNC10YLQtSBSLCDQsNCy0YLQvtC80LDRgtC40YfQtdGB0LrQuCDQt9Cw0LPRgNGD0LbQsNGO0YLRgdGPINC+0YHQvdC+0LLQvdGL0LUg0LHQuNCx0LvQuNC+0YLQtdC60Lgg0LTQu9GPINGA0LDQsdC+0YLRiy4g0JIg0LjRhSDRh9C40YHQu9C1INCx0LjQsdC70LjQvtGC0LXQutCwICpkYXRhc2V0cyoqLCDQsiDQutC+0YLQvtGA0L7QuSDRhdGA0LDQvdGP0YLRgdGPINC90LDQsdC+0YDRiyDQtNCw0L3QvdGL0YUg0LjQtyDRgNCw0LfQvdGL0YUg0LjRgdGB0LvQtdC00L7QstCw0L3QuNC5LiDQp9GC0L7QsdGLINC/0YDQvtGB0LzQvtGC0YDQtdGC0Ywg0LLQtdGB0Ywg0YHQv9C40YHQvtC6INC00L7RgdGC0YPQv9C90YvRhSDQtNCw0YLQsNGB0LXRgtC+0LIsINCy0YvQv9C+0LvQvdC40YLQtSDQutC+0LzQsNC90LTRgzoNCg0KYGBge3J9DQpsaWJyYXJ5KGhlbHAgPSAiZGF0YXNldHMiKSAgIyBTdGFuZGFyZCBSIGRhdGFzZXRzDQpgYGANCg0K0J3QtdC60L7RgtC+0YDRi9C1INC40Lcg0Y3RgtC40YUg0LTQsNGC0LDRgdC10YLQvtCyINC80Ysg0LHRg9C00LXQvCDQuNGB0L/QvtC70YzQt9C+0LLQsNGC0Ywg0LIg0L3QsNGI0LjRhSDQt9Cw0LTQsNGH0LDRhS4g0J7QvdC4INC00L7RgdGC0YPQv9C90Ysg0L/QviDQuNC80LXQvdC4INCyINC70Y7QsdC+0Lkg0LzQvtC80LXQvdGCINGA0LDQsdC+0YLRiy4g0J3QsNC/0YDQuNC80LXRgCwg0LXRgdC70Lgg0LLRiyDQt9Cw0YXQvtGC0LjRgtC1INC40YHQv9C+0LvRjNC30L7QstCw0YLRjCDQtNCw0YLQsNGB0LXRgiAqKm10Y2FycyoqLCDQvNC+0LbQtdGC0LUg0L/RgNC+0YHRgtC+INCy0YvQv9C+0LvQvdC40YLRjCDQutC+0LzQsNC90LTRgyAqbXRjYXJzKiwg0YfRgtC+0LHRiyDRg9Cy0LjQtNC10YLRjCDQtNCw0L3QvdGL0LUg0LIg0LrQvtC90YHQvtC70LguDQoNCmBgYHtyfQ0KZGF0YShtdGNhcnMpICAjINC00L7QsdCw0LLQuNGCINC00LDRgtCw0YHQtdGCINCyINGA0LDQsdC+0YfRg9GOINGB0YDQtdC00YMNCmhlbHAobXRjYXJzKSAgIyDQstGL0LLQtdC00LXRgiDQuNC90YTQvtGA0LzQsNGG0LjRjiDQviDQtNCw0YLQsNGB0LXRgtC1DQpteV9kYXRhIDwtIG10Y2FycyAgIyDQt9Cw0L/QuNGI0LXRgiDQtNCw0YLQsNGB0LXRgiDQsiDQvdC+0LLRg9GOINC/0LXRgNC10LzQtdC90L3Rg9GODQpgYGANCg0KDQojIyMgVGFzayAxLjMuMQ0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ4MS9zdGVwLzc/dW5pdD0yNTIzDQoNCtCSINGN0YLQvtC5INC30LDQtNCw0YfQtSDQv9C+0YDQsNCx0L7RgtCw0LXQvCDRgdC+INCy0YHRgtGA0L7QtdC90L3Ri9C80Lgg0LTQsNC90L3Ri9C80LggKiptdGNhcnMqKi4g0JIg0LTQsNGC0LDRhNGA0Y3QudC80LUgKm10Y2Fycyog0YHQvtC30LTQsNC50YLQtSDQvdC+0LLRg9GOINC60L7Qu9C+0L3QutGDICjQv9C10YDQtdC80LXQvdC90YPRjikg0L/QvtC0INC90LDQt9Cy0LDQvdC40LXQvCAqZXZlbl9nZWFyKiwg0LIg0LrQvtGC0L7RgNC+0Lkg0LHRg9C00YPRgiDQtdC00LjQvdC40YbRiywg0LXRgdC70Lgg0LfQvdCw0YfQtdC90LjQtSDQv9C10YDQtdC80LXQvdC90L7QuSAoKmdlYXIqKSDRh9C10YLQvdC+0LUsINC4INC90YPQu9C4INC10YHQu9C4INC60L7Qu9C40YfQtdGB0YLQstC+INC90LXRh9C10YLQvdC+0LUuDQoNCmBgYHtyfQ0KZGF0YShtdGNhcnMpDQpoZWFkKG10Y2FycykNCmBgYA0KDQpgYGB7cn0NCm10Y2FycyRldmVuX2dlYXIgPC0gKG10Y2FycyRnZWFyICUlIDIgPT0gMCkgKiAxDQptdGNhcnMkZXZlbl9nZWFyWzE6NV0NCmBgYA0KDQojIyMgVGFzayAxLjMuMg0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ4MS9zdGVwLzg/dW5pdD0yNTIzDQoNCtCf0YDQvtC00L7Qu9C20LjQvCDQvdCw0YjRgyDRgNCw0LHQvtGC0YMg0YEg0LTQsNC90L3Ri9C80LggKiptdGNhcnMqKi4g0KLQtdC/0LXRgNGMINCy0LDRiNCwINC30LDQtNCw0YfQsCDRgdC+0LfQtNCw0YLRjCDQv9C10YDQtdC80LXQvdC90YPRjiAtINCy0LXQutGC0L7RgCAqbXBnXzQqINC4INGB0L7RhdGA0LDQvdC40YLRjCDQsiDQvdC10LUg0LfQvdCw0YfQtdC90LjRjyDRgNCw0YHRhdC+0LTQsCDRgtC+0L/Qu9C40LLQsCAoKm1wZyopINC00LvRjyDQvNCw0YjQuNC9INGBINGH0LXRgtGL0YDRjNC80Y8g0YbQuNC70LjQvdC00YDQsNC80LggKCpjeWwqKS4gDQoNCmBgYHtyfQ0KbXBnXzQgPC0gbXRjYXJzJG1wZ1ttdGNhcnMkY3lsID09IDRdICAjIGVxdWl2YWxlbnQ6IG10Y2Fyc1ttdGNhcnMkY3lsID09IDQsICJtcGciXQ0KbXBnXzQNCmBgYA0KDQojIyMgVGFzayAxLjMuMw0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ4MS9zdGVwLzk/dW5pdD0yNTIzDQoNCtCQINGC0LXQv9C10YDRjCDQvdCw0YPRh9C40LzRgdGPINC+0YLQsdC40YDQsNGC0Ywg0YLQvtC70YzQutC+INC90LXQutC+0YLQvtGA0YvQtSDRgdGC0YDQvtGH0LrQuCDQuNC3INC40YHRhdC+0LTQvdGL0YUg0LTQsNC90L3Ri9GFLiANCg0K0JLQsNGI0LAg0LfQsNC00LDRh9CwINGB0L7Qt9C00LDRgtGMINC90L7QstGL0LkgKmRhdGFmcmFtZSog0L/QvtC0INC90LDQt9Cy0LDQvdC40LXQvCAqKm1pbmlfbXRjYXJzKiosINCyINC60L7RgtC+0YDQvtC8INCx0YPQtNGD0YIg0YHQvtGF0YDQsNC90LXQvdGLINGC0L7Qu9GM0LrQviDRgtGA0LXRgtGM0Y8sINGB0LXQtNGM0LzQsNGPLCDQtNC10YHRj9GC0LDRjywg0LTQstC10L3QsNC00YbQsNGC0LDRjyDQuCDQv9C+0YHQu9C10LTQvdGP0Y8g0YHRgtGA0L7Rh9C60LAg0LTQsNGC0LDRhNGA0LXQudC80LAgKiptdGNhcnMqKi4NCg0KYGBge3J9DQptaW5pX210Y2FycyA8LSBtdGNhcnNbYygzLCA3LCAxMCwgMTIsIG5yb3cobXRjYXJzKSksIF0NCm1pbmlfbXRjYXJzDQpgYGANCg0KIyMjIFRhc2sgMS4zLjQNCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE0ODEvc3RlcC8xMD91bml0PTI1MjMNCg0K0KPQutCw0LbQuNGC0LUg0LrQsNC60LDRjyDQutC+0LzQsNC90LTQsCDRgdC+0LfQtNCw0YHRgiDRgdCw0LHRgdC10YIg0LTQsNC90L3Ri9GFICoqbXRjYXJzKiosINGC0L7Qu9GM0LrQviDQtNC70Y8g0YLQtdGFINCw0LLRgtC+0LzQvtCx0LjQu9C10LksINGDINC60L7RgtC+0YDRi9GFINGH0LjRgdC70L4g0YbQuNC70LjQvdC00YDQvtCyICgqY3lsKikg0L3QtSDRgNCw0LLQvdGP0LXRgtGB0Y8gMywg0Lgg0LLRgNC10LzRjyDRgNCw0LfQs9C+0L3QsCDQsNCy0YLQvtC80L7QsdC40LvRjyAoKnFzZWMqKSDQsdC+0LvRjNGI0LUg0YHRgNC10LTQvdC10LPQviDQv9C+INCy0YvQsdC+0YDQutC1Lg0KDQpgYGB7cn0NCnMgPC0gc3Vic2V0KG10Y2FycywgY3lsICE9IDMgJiBxc2VjID4gbWVhbihxc2VjKSkNCnMNCmBgYA0KDQoNCiMjIDEuNCDQrdC70LXQvNC10L3RgtGLINGB0LjQvdGC0LDQutGB0LjRgdCwDQoNCg0KIyMjIFZpZGVvIDEuNC4xDQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNDc4L3N0ZXAvMj91bml0PTI1MjANCg0KYGBge3J9DQpkZl9ldmFscyA8LSByZWFkLmNzdignaHR0cHM6Ly9zdGVwaWMub3JnL21lZGlhL2F0dGFjaG1lbnRzL2xlc3Nvbi8xMTQ4MS9ldmFscy5jc3YnKSAgIyBsb2FkIGRhdGFmcmFtZQ0KYGBgDQoNCiMjIyMgaWYNCg0KYGBge3J9DQphIDwtIDANCg0KaWYgKGEgPiAwKXsNCiAgcHJpbnQoJ3Bvc2l0aXZlJykNCn0gZWxzZSBpZiAoYSA8IDApIHsNCiAgcHJpbnQoJ25lZ2F0aXZlJykNCn0gZWxzZSB7DQogIHByaW50KCd6ZXJvJykNCn0NCmBgYA0KDQojIyMjIGlmZWxzZQ0KDQpgYGB7cn0NCmlmZWxzZShhID4gMCwgJ3Bvc2l0aXZlJywgJ25vdCBwb3NpdGl2ZScpICAjICdpZmVsc2UnIGlzIGFibGUgdG8gd29yayB3aXRoIGFycmF5cywgc2VlIGV4YW1wbGUgYmVsb3cNCmBgYA0KDQpgYGB7cn0NCmEgPC0gYygxLCAtMSwgMiwgMykNCmlmZWxzZShhID4gMCwgJ3Bvc2l0aXZlJywgJ25vdCBwb3NpdGl2ZScpDQpgYGANCg0KIyMjIFZpZGVvIDEuNC4yDQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNDc4L3N0ZXAvMz91bml0PTI1MjANCg0KIyMjIyBmb3INCg0KYGBge3J9DQpmb3IgKGkgaW4gMTo1KSB7DQogIHByaW50KGkpDQp9DQpgYGANCg0KIyMjIyBmb3IgaW4gZGF0YXNldA0KDQpgYGB7cn0NCmZvciAoaSBpbiA0NjA6bnJvdyhkZl9ldmFscykpew0KICBwcmludChkZl9ldmFscyRzY29yZVtpXSkNCn0NCmBgYA0KDQojIyMjIGZvciArIGlmDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6bnJvdyhkZl9ldmFscykpew0KICBpZiAoZGZfZXZhbHMkc2NvcmVbaV0gPiA0Ljkpew0KICAgIHByaW50KGRmX2V2YWxzJGFnZVtpXSkNCiAgfQ0KfQ0KYGBgDQoNCiMjIyMgZm9yICsgaWYgdnMgaWZlbHNlDQoNCiMjIyMjIGZvciArIGlmDQoNCmBgYHtyfQ0KZGZfZXZhbHMkcXVhbGl0eSA8LSByZXAoTkEsIG5yb3coZGZfZXZhbHMpKSAgIyBjcmVhdGUgZW1wdHkgYXJyYXkgd2l0aCBsZW5ndGggb2YgZGF0YSBzZXQgZGZfZXZhbHMNCg0KZm9yIChpIGluIDE6bnJvdyhkZl9ldmFscykpew0KICBpZiAoZGZfZXZhbHMkc2NvcmVbaV0gPiA0KXsNCiAgICBkZl9ldmFscyRxdWFsaXR5W2ldIDwtICdnb29kJw0KICB9IGVsc2UgZGZfZXZhbHMkcXVhbGl0eVtpXSA8LSAnYmFkJw0KfQ0KDQpkZl9ldmFscyRxdWFsaXR5WzE6MTBdDQpgYGANCg0KIyMjIyMgZm9yICsgaWZlbHNlDQoNCmBgYHtyIGVjaG89VFJVRX0NCmRmX2V2YWxzJHF1YWxpdHkgPC0gaWZlbHNlKGRmX2V2YWxzJHNjb3JlIDwgNCwgJ2dvb2QnLCAnYmFkJykNCmBgYA0KDQojIyMjIHdoaWxlDQoNCmBgYHtyfQ0KaSA8LSAxDQoNCiMgZmlyc3QgZm91ciBzY29yZXMNCndoaWxlIChpIDwgNSl7DQogIHByaW50KGRmX2V2YWxzJHNjb3JlW2ldKQ0KICBpIDwtIGkgKyAxDQp9DQpgYGANCg0KIyMjIFRhc2sgMS40LjENCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE0Nzgvc3RlcC80P3VuaXQ9MjUyMA0KDQrQodC+0LfQtNCw0LnRgtC1INC90L7QstGD0Y4g0YfQuNGB0LvQvtCy0YPRjiDQv9C10YDQtdC80LXQvdC90YPRjiAqbmV3X3Zhciog0LIg0LTQsNC90L3Ri9GFICoqbXRjYXJzKiosINC60L7RgtC+0YDQsNGPINGB0L7QtNC10YDQttC40YIg0LXQtNC40L3QuNGG0Ysg0LIg0YHRgtGA0L7Rh9C60LDRhSwg0LXRgdC70Lgg0LIg0LzQsNGI0LjQvdC1INC90LUg0LzQtdC90YzRiNC1ICrRh9C10YLRi9GA0ZHRhSog0LrQsNGA0LHRjtGA0LDRgtC+0YDQvtCyICjQv9C10YDQtdC80LXQvdC90LDRjyAqY2FyYiopINC40LvQuCDQsdC+0LvRjNGI0LUgKtGI0LXRgdGC0LgqINGG0LjQu9C40L3QtNGA0L7QsiAo0L/QtdGA0LXQvNC10L3QvdCw0Y8gKmN5bCopLiDQkiDRgdGC0YDQvtGH0LrQsNGFLCDQsiDQutC+0YLQvtGA0YvRhSDRg9GB0LvQvtCy0LjQtSDQvdC1INCy0YvQv9C+0LvQvdGP0LXRgtGB0Y8sINC00L7Qu9C20L3RiyDRgdGC0L7Rj9GC0Ywg0L3Rg9C70LguDQoNCmBgYHtyfQ0KZGYgPC0gbXRjYXJzDQpkZiRuZXdfdmFyIDwtIGlmZWxzZShkZiRjYXJiID49IDQgfCBkZiRjeWwgPiA2LCAxLCAwKQ0KaGVhZChkZikNCmBgYA0KDQojIyMgVGFzayAxLjQuMg0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ3OC9zdGVwLzU/dW5pdD0yNTIwDQoNCtCSICrRg9C20LUg0YHRg9GJ0LXRgdGC0LLRg9GO0YnQtdC5KiDQv9C10YDQtdC80LXQvdC90L7QuSAqKm15X3ZlY3RvcioqINGB0L7RhdGA0LDQvdC10L0g0LLQtdC60YLQvtGAINC40LcgNTAg0YfQuNGB0LXQuy4g0JXRgdC70Lgg0YHRgNC10LTQvdC10LUg0LfQvdCw0YfQtdC90LjQtSDQstC10LrRgtC+0YDQsCAqKm15X3ZlY3RvcioqINCx0L7Qu9GM0YjQtSAyMCwg0LIg0L/QtdGA0LXQvNC10L3QvdGD0Y4gKnJlc3VsdCog0YHQvtGF0YDQsNC90LjRgtC1ICJNeSBtZWFuIGlzIGdyZWF0IiwgINC10YHQu9C4INGB0YDQtdC00L3QtdC1INC30L3QsNGH0LXQvdC40LUgKm15X3ZlY3Rvciog0LzQtdC90YzRiNC1INC40LvQuCDRgNCw0LLQvdC+IDIwINGC0L4g0LIg0L/QtdGA0LXQvNC10L3QvdGD0Y4gKnJlc3VsdCog0YHQvtGF0YDQsNC90LjRgtC1ICDRgdGC0YDQvtC60YMgIk15IG1lYW4gaXMgbm90IHNvIGdyZWF0Ii4NCg0KYGBge3J9DQpteV92ZWN0b3IgPSAxOjUwDQoNCmlmIChtZWFuKG15X3ZlY3RvcikgPiAyMCl7DQogIHJlc3VsdCA8LSAiTXkgbWVhbiBpcyBncmVhdCINCn0gZWxzZSByZXN1bHQgPC0gIk15IG1lYW4gaXMgbm90IHNvIGdyZWF0Ig0KcHJpbnQocmVzdWx0KQ0KYGBgDQoNCiMjIyBUYXNrIDEuNC4zDQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNDc4L3N0ZXAvNj91bml0PTI1MjANCg0K0JIg0Y3RgtC+0Lkg0LfQsNC00LDRh9C1INC+0YIg0LLQsNGBINC/0L7RgtGA0LXQsdGD0LXRgtGB0Y8g0YPQt9C90LDRgtGMINC90LXQutC+0YLQvtGA0YPRjiDQuNC90YTQvtGA0LzQsNGG0LjRjiDQviDRgtC40L/QsNGFINC00LDQvdC90YvRhSDQsiBSINGB0LDQvNC+0YHRgtC+0Y/RgtC10LvRjNC90L4hINCS0YHRgtGA0L7QtdC90L3Ri9C1INCyIFIg0LTQsNC90L3Ri9C1ICoqQWlyUGFzc2VuZ2VycyoqIC0g0Y3RgtC+INC90L7QstGL0Lkg0LTQu9GPINC90LDRgSDRhNC+0YDQvNCw0YIg0LTQsNC90L3Ri9GFINGC0LjQv9CwICpUaW1lLVNlcmllcyouINCY0LfRg9GH0LjRgtC1INGB0YLRgNGD0LrRgtGD0YDRgyDRjdGC0LjRhSDQtNCw0L3QvdGL0YUsINC/0YDQtdC20LTQtSDRh9C10Lwg0L3QsNGH0LDRgtGMINGA0LXRiNC10L3QuNC1INC30LDQtNCw0YfQuCEg0J3QsNC/0YDQuNC80LXRgCDQvdCw0L/QuNGI0LjRgtC1INC60L7QvNCw0L3QtNGLOg0KDQpgYGB7cn0NCj9BaXJQYXNzZW5nZXJzICAgICAgIyDRgdC/0YDQsNCy0LrQsCDQviDQtNCw0L3QvdGL0YUNCnN0cihBaXJQYXNzZW5nZXJzKSAgIyDRgdGC0YDRg9C60YLRg9GA0LAg0LTQsNC90L3Ri9GFDQpgYGANCg0K0JIg0LLRgdGC0YDQvtC10L3QvdGL0YUg0LIgUiDQtNCw0L3QvdGL0YUgKipBaXJQYXNzZW5nZXJzKiog0YXRgNCw0L3QuNGC0YHRjyAxNDQg0LfQvdCw0YfQtdC90LjRjyAo0LrQvtC70LjRh9C10YHRgtCy0L4g0L/QsNGB0YHQsNC20LjRgNC+0LIg0LIg0LzQtdGB0Y/Rhikg0YEgMTk0OSDQv9C+IDE5NjAg0LPQvtC0LiDQlNCw0L3QvdGL0LUgVGltZS1TZXJpZXMg0L7Rh9C10L3RjCDQv9C+0YXQvtC20Lgg0L3QsCDQstC10LrRgtC+0YAg0L/QviDRgdCy0L7QtdC5INGB0YLRgNGD0LrRgtGD0YDQtSwg0L3QsNC/0YDQuNC80LXRgCDQvNGLINC80L7QttC10Lwg0L7QsdGA0LDRgtC40YLRjNGB0Y8g0Log0LvRjtCx0L7QvNGDINC40LcgMTQ0INGN0LvQtdC80LXQvdGC0L7QsiDQuNGB0L/QvtC70YzQt9GD0Y8g0YPQttC1INC30L3QsNC60L7QvNGD0Y4g0L3QsNC8INC40L3QtNC10LrRgdCw0YbQuNGOIEFpclBhc3NlbmdlcnNbMV0g0LjQu9C4IEFpclBhc3NlbmdlcnNbNTZdLg0KDQrQnNC+0LbQvdC+INCy0L7QvtCx0YnQtSDQv9C10YDQtdCy0LXRgdGC0Lgg0LjRgdGF0L7QtNC90YvQtSDQtNCw0L3QvdGL0LUg0LIg0LLQtdC60YLQvtGAINC/0YDQuCDQv9C+0LzQvtGJ0Lgg0LrQvtC80LDQvdC00YsgYXMudmVjdG9yKEFpclBhc3NlbmdlcnMpINC4INC/0YDQvtC00L7Qu9C20LjRgtGMINGBINC90LjQvNC4INGA0LDQsdC+0YLRgyDQutCw0Log0YEg0LLQtdC60YLQvtGA0L7QvC4NCg0K0Jgg0YLQsNC6INCy0LDRiNCwINC30LDQtNCw0YfQsCDRgdC+0LfQtNCw0YLRjCDQv9C10YDQtdC80LXQvdC90YPRjiAqZ29vZF9tb250aHMqINC4INGB0L7RhdGA0LDQvdC40YLRjCDQsiDQvdC10LUg0YfQuNGB0LvQviDQv9Cw0YHRgdCw0LbQuNGA0L7QsiDRgtC+0LvRjNC60L4g0LIg0YLQtdGFINC80LXRgdGP0YbQsNGFLCDQsiDQutC+0YLQvtGA0YvRhSDRjdGC0L4g0YfQuNGB0LvQviDQsdC+0LvRjNGI0LUsINGH0LXQvCDQv9C+0LrQsNC30LDRgtC10LvRjCDQsiDQv9GA0LXQtNGL0LTRg9GJ0LXQvCDQvNC10YHRj9GG0LUuICANCg0K0JLQsNC20L3Ri9C5INC80L7QvNC10L3RgiEg0JIgUiDQvtC/0LXRgNCw0YLQvtGAIDog0LTQu9GPINGB0L7Qt9C00LDQvdC40Y8g0L/QvtGB0LvQtdC00L7QstCw0YLQtdC70YzQvdC+0YHRgtC4INC40LzQtdC10YIg0L/RgNC40L7RgNC40YLQtdGCINC90LDQtCDQsNGA0LjRhNC80LXRgtC40YfQtdGB0LrQuNC80Lgg0LTQtdC50YHRgtCy0LjRj9C80LguINCi0LDQutC40Lwg0L7QsdGA0LDQt9C+0LwsINC10YHQu9C4INGDINCy0LDRgSDQtdGB0YLRjCDQv9C10YDQtdC80LXQvdC90LDRjyBpLCDRgNCw0LLQvdCw0Y8gMTAsINC4INCy0Ysg0YXQvtGC0LjRgtC1INGB0L7Qt9C00LDRgtGMINCy0LXQutGC0L7RgCDQvtGCIDEg0LTQviBpIC0gMSwg0LLQvtGB0L/QvtC70YzQt9GD0LnRgtC10YHRjCDRgdC60L7QsdC60LDQvNC4LCDRh9GC0L7QsdGLINGD0LrQsNC30LDRgtGMINC/0L7RgdC70LXQtNC+0LLQsNGC0LXQu9GM0L3QvtGB0YLRjCDQtNC10LnRgdGC0LLQuNC5Lg0KDQpgYGB7cn0NCmdvb2RfbW9udGhzIDwtIGMoKQ0KDQpmb3IgKGkgaW4gMjpsZW5ndGgoQWlyUGFzc2VuZ2Vycykpew0KICBpZiAoQWlyUGFzc2VuZ2Vyc1tpXSA+IEFpclBhc3NlbmdlcnNbaSAtIDFdKXsNCiAgICBnb29kX21vbnRocyA8LSBjKGdvb2RfbW9udGhzLCBBaXJQYXNzZW5nZXJzW2ldKQ0KICB9DQp9DQoNCiMgb25lIHJvdyBzY3JpcHQgMToNCiMgZ29vZF9tb250aHMgPC0gQWlyUGFzc2VuZ2Vyc1tjKEZBTFNFLCBBaXJQYXNzZW5nZXJzWzI6MTQ0XSA+IEFpclBhc3NlbmdlcnNbMToxNDNdKV0NCg0KIyBvbmUgcm93IHNjcmlwdCAyOg0KIyBnb29kX21vbnRocyA8LSBBaXJQYXNzZW5nZXJzWy0xXVtBaXJQYXNzZW5nZXJzWy0xXSA+IEFpclBhc3NlbmdlcnNbLTE0NF1dIA0KDQpnb29kX21vbnRocw0KYGBgDQoNCiMjIyBUYXNrIDEuNC40DQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNDc4L3N0ZXAvNz91bml0PTI1MjANCg0K0JfQsNC00LDRh9C60LAg0LTQu9GPINGB0YPQv9C10YAg0LPQtdGA0L7QtdCyLCDQv9C+0LLRi9GI0LXQvdC90L7QuSDRgdC70L7QttC90L7RgdGC0LghDQoNCtCU0LvRjyDQstGB0YLRgNC+0LXQvdC90YvRhSDQsiBSINC00LDQvdC90YvRhSAqKkFpclBhc3NlbmdlcnMqKiDRgNCw0YHRgdGH0LjRgtCw0LnRgtC1INGB0LrQvtC70YzQt9GP0YnQtdC1INGB0YDQtdC00L3QtdC1INGBINC40L3RgtC10YDQstCw0LvQvtC8INGB0LPQu9Cw0LbQuNCy0LDQvdC40Y8g0YDQsNCy0L3Ri9C8IDEwLiDQndCw0L/QtdGH0LDRgtCw0LnRgtC1INC/0L7Qu9GD0YfQuNCy0YjQuNC50YHRjyDRgNC10LfRg9C70YzRgtCw0YIgKNC/0LXRgNCy0YvQvCDQt9C90LDRh9C10L3QuNC10Lwg0LIg0LLRi9Cy0L7QtNC1INC00L7Qu9C20L3QviDQsdGL0YLRjCDRgdGA0LXQtNC90LXQtSDQtNC70Y8g0Y3Qu9C10LzQtdC90YLQvtCyIDE6MTAsINCy0L4g0LLRgtC+0YDQvtC8INC30L3QsNGH0LXQvdC40LggLSDRgdGA0LXQtNC90LXQtSDQtNC70Y8g0Y3Qu9C10LzQtdC90YLQvtCyIDI6MTEg0Lgg0YIu0LQuLCDQsiDQv9C+0YHQu9C10LTQvdC10LwgIC0g0YHRgNC10LTQvdC10LUg0LTQu9GPINGN0LvQtdC80LXQvdGC0L7QsiAxMzU6MTQ0KQ0KDQrQktGB0LUg0L/QvtC70YPRh9C10L3QvdGL0LUg0LfQvdCw0YfQtdC90LjRjyDRgdGA0LXQtNC90LjRhSDRgdC+0YXRgNCw0L3QuNGC0LUg0LIg0L/QtdGA0LXQvNC10L3QvdGD0Y4gKm1vdmluZ19hdmVyYWdlKi4NCg0KYGBge3J9DQptb3ZpbmdfYXZlcmFnZSA8LSBudW1lcmljKDEzNSkgIyBlbXB0eSBhcnJheSB3aXRoIGxlbmd0aCAxMzUNCg0KZm9yIChpIGluIDE6KGxlbmd0aChBaXJQYXNzZW5nZXJzKSAtIDkpKXsNCiAgbW92aW5nX2F2ZXJhZ2VbaV0gPC0gbWVhbihBaXJQYXNzZW5nZXJzW2k6KGkgKyA5KV0pDQp9DQoNCm1vdmluZ19hdmVyYWdlWzE6MTBdDQpgYGANCg0K0JzQvtC20L3QviDRgNC10YjQuNGC0Ywg0Lgg0LHQtdC3INGG0LjQutC70LAg0L/RgNC4INC/0L7QvNC+0YnQuCDRgNCw0LfQvdC+0YHRgtC10Lkg0LrRg9C80YPQu9GP0YLQuNCy0L3Ri9GFINGB0YPQvNC8IQ0KDQpgYGB7cn0NCm4gPC0gMTAgICAgDQpkIDwtIEFpclBhc3NlbmdlcnMgICAgDQpjeCA8LSBjKDAsIGN1bXN1bShkKSkgICAgDQptb3ZpbmdfYXZlcmFnZSA8LSAoY3hbKG4gKyAxKTpsZW5ndGgoY3gpXSAtIGN4WzE6KGxlbmd0aChjeCkgLSBuKV0pIC8gbg0KbW92aW5nX2F2ZXJhZ2VbMToxMF0NCmBgYA0KDQoNCiMjIDEuNSDQntC/0LjRgdCw0YLQtdC70YzQvdGL0LUg0YHRgtCw0YLQuNGB0YLQuNC60LgNCg0KDQojIyMgVmlkZW8gMS41LjINCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE0Nzkvc3RlcC8zP3VuaXQ9MjUyMQ0KDQojIyMjIFJlYWQgYW5kIHByZXBhcmUgZGF0YSBmcmFtZQ0KDQo/bXRjYXJzDQoNCmBgYHtyfQ0KbXRjYXJzX2RmIDwtIG10Y2Fycw0Kc3RyKG10Y2Fyc19kZikgICMgc2hvdyBkYXRhIGZyYW1lIHN0cnVjdHVyZQ0KYGBgDQoNCmBgYHtyfQ0KIyBjaGFuZ2UgdHlwZXMgb2YgdnMgYW5kIGFtIGNvbHVtbnMNCm10Y2Fyc19kZiR2cyA8LSBmYWN0b3IobXRjYXJzX2RmJHZzLCBsYWJlbHMgPSBjKCJWIiwgIlMiKSkgICMgInZzIiBmcm9tIG51bSB0eXBlIHRvIGZhY3RvciB0eXBlDQptdGNhcnNfZGYkYW0gPC0gZmFjdG9yKG10Y2Fyc19kZiRhbSwgbGFiZWxzID0gYygiQXV0byIsICJNYW51YWwiKSkgIyAiYW0iIGZyb20gbnVtIHR5cGUgdG8gZmFjdG9yIHR5cGUNCnN0cihtdGNhcnNfZGYpDQpgYGANCg0KIyMjIFZpZGVvIDEuNS4zDQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNDc5L3N0ZXAvND91bml0PTI1MjENCg0KIyMjIyBtZWRpYW4sIG1lYW4sIHNkLCByYW5nZQ0KDQpgYGB7cn0NCm1lZGlhbihtdGNhcnNfZGYkbXBnKSAgIyBtZWRpYW4gbXBnDQptZWFuKG10Y2Fyc19kZiRkaXNwKSAgICMgbWVhbiBkaXNwbGFjZW1lbnQNCnNkKG10Y2Fyc19kZiRocCkgICAgICAgIyBzdGFuZGFyZCBkZXZpYXRpb24gb2YgaG9yc2UgcG93ZXINCnJhbmdlKG10Y2Fyc19kZiRjeWwpICAgIyByYW5nZQ0KYGBgDQoNCmBgYHtyfQ0KIyBtZWFuIG1wZyBvZiA2IGN5bCBjYXJzIHdpdGggVi1lbmdpbmVzDQptZWFuKG10Y2Fyc19kZiRtcGdbbXRjYXJzX2RmJGN5bCA9PSA2ICYgbXRjYXJzX2RmJHZzID09ICJWIl0pDQpgYGANCg0KYGBge3J9DQojIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgY2FycyB3aXRoIGF1dG9tYXRpYyB0cmFuc21pc3Npb24gYW5kIG51bWJlciBjeWxpbmRlcnMgaXMgbm90IGVxdWFsIDMNCnNkKG10Y2Fyc19kZiRocFttdGNhcnNfZGYkY3lsICE9IDMgJiBtdGNhcnNfZGYkYW0gPT0gIkF1dG8iXSkNCmBgYA0KDQojIyMgVGFzayAxLjUuMQ0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ3OS9zdGVwLzU/dW5pdD0yNTIxDQoNCtCS0L3QvtCy0Ywg0LLQtdGA0L3QtdC80YHRjyDQuiDQtNCw0L3QvdGL0LwgKiptdGNhcnMqKi4g0KDQsNGB0YHRh9C40YLQsNC50YLQtSDRgdGA0LXQtNC90LXQtSDQt9C90LDRh9C10L3QuNC1INCy0YDQtdC80LXQvdC4INGA0LDQt9Cz0L7QvdCwICgqcXNlYyopINC00LvRjyDQsNCy0YLQvtC80L7QsdC40LvQtdC5LCDRh9C40YHQu9C+INGG0LjQu9C40L3QtNGA0L7QsiAoKmN5bCopINGDINC60L7RgtC+0YDRi9GFINC90LUg0YDQsNCy0L3Rj9C10YLRgdGPIDMg0Lgg0L/QvtC60LDQt9Cw0YLQtdC70Ywg0LrQvtC70LjRh9C10YHRgtCy0LAg0LzQuNC70Ywg0L3QsCDQs9Cw0LvQu9C+0L0g0YLQvtC/0LvQuNCy0LAgKCptcGcqKSDQsdC+0LvRjNGI0LUgMjAuDQoNCtCf0L7Qu9GD0YfQuNCy0YjQuNC50YHRjyDRgNC10LfRg9C70YzRgtCw0YIgKNGB0YDQtdC00L3QtdC1INC30L3QsNGH0LXQvdC40LUpINGB0L7RhdGA0LDQvdC40YLQtSDQsiDQv9C10YDQtdC80LXQvdC90YPRjiAqKnJlc3VsdCoqLg0KDQpgYGB7cn0NCnJlc3VsdCA8LSBtZWFuKG10Y2Fyc19kZiRxc2VjW210Y2Fyc19kZiRjeWwgIT0gMyAmIG10Y2Fyc19kZiRtcGcgPiAyMF0pDQpyZXN1bHQNCmBgYA0KDQojIyMgVmlkZW8gMS41LjQNCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE0Nzkvc3RlcC82P3VuaXQ9MjUyMQ0KDQojIyMjIGFnZ3JlZ2F0ZQ0KDQo/YWdncmVnYXRlDQoNCmBgYHtyfQ0KYWdnIDwtIGFnZ3JlZ2F0ZSh4ID0gbXRjYXJzX2RmJGhwLCBieSA9IGxpc3QobXRjYXJzX2RmJHZzKSwgRlVOID0gbWVhbikgICMgbWVhbiBob3JzZSBwb3dlciBmb3IgViBhbmQgUyBlbmdpbmVzDQphZ2cNCmBgYA0KDQpgYGB7cn0NCm1lYW5faHBfdnMgPC0gYWdncmVnYXRlKHggPSBtdGNhcnNfZGYkaHAsIGJ5ID0gbGlzdChtdGNhcnNfZGYkdnMpLCBGVU4gPSBtZWFuKQ0KY29sbmFtZXMobWVhbl9ocF92cykgPC0gYygiVlMiLCAiTWVhbiBIUCIpICAjIHJlbmFtZSBjb2x1bW5zDQptZWFuX2hwX3ZzDQpgYGANCg0KYGBge3J9DQojIGVxdWl2YWxlbnQgc2NyaXB0IChzaG9ydGVyIHdheSkNCmFnZ3JlZ2F0ZShocCB+IHZzLCBtdGNhcnNfZGYsIG1lYW4pDQpgYGANCg0KYGBge3J9DQphZ2dyZWdhdGUoaHAgfiB2cyArIGFtLCBtdGNhcnNfZGYsIG1lYW4pICMgd2l0aCB0d28gJ2J5JyBjYXRlZ29yaWVzDQojIGVxdWl2YWxlbnQgbG9uZ2VyIHNjcmlwdA0KIyBhZ2dyZWdhdGUoeCA9IG10Y2Fyc19kZiRocCwgYnkgPSBsaXN0KG10Y2Fyc19kZiR2cywgbXRjYXJzX2RmJGFtKSwgRlVOID0gbWVhbikNCmBgYA0KDQpgYGB7cn0NCiMgYWxsIG1lZGlhbiB2YWx1ZXMgb2YgbnVtIHR5cGUgY29sdW1ucw0KYWdnIDwtIGFnZ3JlZ2F0ZSh4ID0gbXRjYXJzX2RmWywgLWMoOCwgOSldLCBieSA9IGxpc3QobXRjYXJzX2RmJHZzLCBtdGNhcnNfZGYkYW0pLCBGVU4gPSBtZWRpYW4pDQphZ2cNCmBgYA0KDQpgYGB7cn0NCiMgc2QgdmFsdWVzIG9mIG1wZyBhbmQgZGlzcCBjb2x1bW5zDQphZ2cgPC0gYWdncmVnYXRlKHggPSBtdGNhcnNfZGZbLCBjKCJtcGciLCAiZGlzcCIpXSwgYnkgPSBsaXN0KG10Y2Fyc19kZiR2cywgbXRjYXJzX2RmJGFtKSwgRlVOID0gc2QpDQphZ2cNCiMgZXF1aXZhbGVudCBzaG9ydGVyIHNjcmlwdA0KI2FnZ3JlZ2F0ZShjYmluZChtcGcsIGRpc3ApIH4gYW0gKyB2cywgbXRjYXJzX2RmLCBzZCkNCmBgYA0KDQojIyMgVGFzayAxLjUuMg0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ3OS9zdGVwLzc/dW5pdD0yNTIxDQoNCtCf0YDQuCDQv9C+0LzQvtGJ0Lgg0YTRg9C90LrRhtC40LggKmFnZ3JlZ2F0ZSog0YDQsNGB0YHRh9C40YLQsNC50YLQtSDRgdGC0LDQvdC00LDRgNGC0L3QvtC1INC+0YLQutC70L7QvdC10L3QuNC1INC/0LXRgNC10LzQtdC90L3QvtC5ICpocCogKNC70L7RiNCw0LTQuNC90YvQtSDRgdC40LvRiykg0Lgg0L/QtdGA0LXQvNC10L3QvdC+0LkgKmRpc3AqICjQstC80LXRgdGC0LjQvNC+0YHRgtC4INC00LLQuNCz0LDRgtC10LvRjykgINGDINC80LDRiNC40L0g0YEg0LDQstGC0L7QvNCw0YLQuNGH0LXRgdC60L7QuSDQuCDRgNGD0YfQvdC+0Lkg0LrQvtGA0L7QsdC60L7QuSDQv9C10YDQtdC00LDRhy4gDQoNCtCf0L7Qu9GD0YfQtdC90L3Ri9C1INGA0LXQt9GD0LvRjNGC0LDRgtGLICjRgNC10LfRg9C70YzRgtCw0YLRiyDQstGL0L/QvtC70L3QtdC90LjRjyDRhNGD0L3QutGG0LjQuCAqYWdncmVnYXRlKikg0YHQvtGF0YDQsNC90LjRgtC1INCyINC/0LXRgNC10LzQtdC90L3Rg9GOICoqZGVzY3JpcHRpb25zX3N0YXQqKi4NCg0KYGBge3J9DQpkZXNjcmlwdGlvbnNfc3RhdCA8LSBhZ2dyZWdhdGUoY2JpbmQoaHAsIGRpc3ApIH4gYW0sIG10Y2Fycywgc2QpDQpkZXNjcmlwdGlvbnNfc3RhdA0KYGBgDQoNCiMjIyBWaWRlbyAxLjUuNQ0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ3OS9zdGVwLzg/dW5pdD0yNTIxDQoNCiMjIyMgZGVzY3JpYmUNCj9kZXNjcmliZQ0KDQpgYGB7cn0NCmQgPC0gZGVzY3JpYmUoeCA9IG10Y2Fyc19kZlssIC1jKDgsIDkpXSkNCmQNCmBgYA0KDQojIyMgVmlkZW8gMS41LjYNCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE0Nzkvc3RlcC85P3VuaXQ9MjUyMQ0KDQojIyMjIGRlc2NyaWJlQnkNCj9kZXNjcmliZUJ5DQpgYGB7cn0NCmxpYnJhcnkocHN5Y2gpDQojICdtYXQnIHByb3ZpZGVzIG91dHB1dCB0byBvbmUgbWF0cml4DQpkIDwtIGRlc2NyaWJlQnkoeCA9IG10Y2Fyc19kZlssIC1jKDgsIDkpXSwgZ3JvdXAgPSBtdGNhcnNfZGYkdnMsIG1hdCA9ICBULCBkaWdpdHMgPSAyKQ0KZA0KYGBgDQoNCmBgYHtyfQ0KIyAnZmFzdCcgcHJvdmlkZXMgbGVzcyBjb2x1bW5zIG9mIGRlc2NyaWJlIGZ1bmN0aW9uDQpkIDwtIGRlc2NyaWJlQnkoeCA9IG10Y2Fyc19kZlssIC1jKDgsIDkpXSwgZ3JvdXAgPSBtdGNhcnNfZGYkdnMsIG1hdCA9ICBULCBkaWdpdHMgPSAyLCBmYXN0ID0gVCkNCmQNCmBgYA0KIA0KYGBge3J9DQpkIDwtIGRlc2NyaWJlQnkobXRjYXJzX2RmJHFzZWMsIGdyb3VwID0gbGlzdChtdGNhcnNfZGYkdnMsIG10Y2Fyc19kZiRhbSksIG1hdCA9IFQsIGRpZ2l0cyA9IDEsIGZhc3QgPSBUKQ0KZA0KYGBgDQoNCiMjIyBWaWRlbyAxLjUuNw0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ3OS9zdGVwLzEwP3VuaXQ9MjUyMQ0KDQojIyMjIGNoZWNrIE4vQSB2YWx1ZXMNCg0KYGBge3J9DQpzdW0oaXMubmEobXRjYXJzX2RmKSkgICMgaWYgMCBpdCBtZWFucyB0aGF0IGFsbCB2YWx1ZXMgYXJlIGF2YWlsYWJsZQ0KYGBgDQoNCmBgYHtyfQ0KbWVhbihtdGNhcnNfZGYkbXBnLCBuYS5ybSA9IFQpICAjIG5hLnJtID4gbmEgcmVtb3ZlOiByZW1vdmUgYWxsIG5hIHZhbHVlcw0KIyBhZ2dyZWdhdGUgZnVuY3Rpb24gcmVtb3ZlcyBuYSB2YWx1ZXMgYnkgZGVmYXVsdA0KYGBgDQoNCiMjIyBUYXNrIDEuNS4zDQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNDc5L3N0ZXAvMTE/dW5pdD0yNTIxDQoNCtCS0L7RgdC/0L7Qu9GM0LfRg9C10LzRgdGPINCy0YHRgtGA0L7QtdC90L3Ri9C80Lgg0LTQsNC90L3Ri9C80LggKiphaXJxdWFsaXR5KiouINCSINC90L7QstGD0Y4g0L/QtdGA0LXQvNC10L3QvdGD0Y4g0YHQvtGF0YDQsNC90LjRgtC1ICpzdWJzZXQqINC40YHRhdC+0LTQvdGL0YUg0LTQsNC90L3Ri9GFLCDQvtGB0YLQsNCy0LjQsiDQvdCw0LHQu9GO0LTQtdC90LjRjyDRgtC+0LvRjNC60L4g0LTQu9GPINC80LXRgdGP0YbQtdCyIDcsIDgg0LggOS4NCg0K0J/RgNC4INC/0L7QvNC+0YnQuCDRhNGD0L3QutGG0LjQuCAqYWdncmVnYXRlKiDRgNCw0YHRgdGH0LjRgtCw0LnRgtC1INC60L7Qu9C40YfQtdGB0YLQstC+INC90LXQv9GA0L7Qv9GD0YnQtdC90L3Ri9GFINC90LDQsdC70Y7QtNC10L3QuNC5INC/0L4g0L/QtdGA0LXQvNC10L3QvdC+0LkgKk96b25lKiDQsiA3LCA4INC4IDkg0LzQtdGB0Y/RhtC1LiDQlNC70Y8g0L7Qv9GA0LXQtNC10LvQtdC90LjRjyDQutC+0LvQuNGH0LXRgdGC0LLQsCDQvdCw0LHQu9GO0LTQtdC90LjQuSDQuNGB0L/QvtC70YzQt9GD0LnRgtC1INGE0YPQvdC60YbQuNGOICpsZW5ndGgoKSouDQoNCtCg0LXQt9GD0LvRjNGC0LDRgiDQstGL0L/QvtC70L3QtdC90LjRjyDRhNGD0L3QutGG0LjQuCAqYWdncmVnYXRlKiDRgdC+0YXRgNCw0L3QuNGC0LUg0LIg0L/QtdGA0LXQvNC10L3QvdGD0Y4gKipyZXN1bHQqKi4NCg0KYGBge3J9DQpyZXN1bHQgPC0gYWdncmVnYXRlKE96b25lIH4gTW9udGgsIHN1YnNldChhaXJxdWFsaXR5LCBNb250aCAlaW4lIGMoNywgOCwgOSkpLCBsZW5ndGgpDQpyZXN1bHQNCmBgYA0KDQojIyMgVGFzayAxLjUuNA0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ3OS9zdGVwLzEyP3VuaXQ9MjUyMQ0KDQrQn9GA0LjQvNC10L3QuNGC0LUg0YTRg9C90LrRhtC40Y4gKmRlc2NyaWJlQnkqINC6INC60L7Qu9C40YfQtdGB0YLQstC10L3QvdGL0Lwg0L/QtdGA0LXQvNC10L3QvdGL0Lwg0LTQsNC90L3Ri9GFICoqYWlycXVhbGl0eSoqLCDQs9GA0YPQv9C/0LjRgNGD0Y8g0L3QsNCx0LvRjtC00LXQvdC40Y8g0L/QviDQv9C10YDQtdC80LXQvdC90L7QuSAqTW9udGgqLiDQp9C10LzRgyDRgNCw0LLQtdC9INC60L7RjdGE0YTQuNGG0LjQtdC90YIg0LDRgdC40LzQvNC10YLRgNC40LggKCpza2V3Kikg0L/QtdGA0LXQvNC10L3QvdC+0LkgKldpbmQqINCyINCy0L7RgdGM0LzQvtC8INC80LXRgdGP0YbQtT8NCg0KYGBge3J9DQojIHN0cihhaXJxdWFsaXR5KQ0KZCA8LSBkZXNjcmliZUJ5KGFpcnF1YWxpdHkkV2luZCwgZ3JvdXAgPSBsaXN0KGFpcnF1YWxpdHkkTW9udGgpLCBtYXQgPSBULCBkaWdpdHMgPSAzKQ0KZCRza2V3W2QkZ3JvdXAxID09IDhdDQpgYGANCg0KYGBge3J9DQojIGxpYnJhcnkocHN5Y2gpDQpkIDwtIHNrZXcoc3Vic2V0KGFpcnF1YWxpdHksIE1vbnRoID09IDgpJFdpbmQpDQpkDQpgYGANCg0KDQojIyMgVGFzayAxLjUuNQ0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTQ3OS9zdGVwLzEzP3VuaXQ9MjUyMQ0KDQrQntCx0YDQsNGC0LjQvNGB0Y8g0Log0LLRgdGC0YDQvtC10L3QvdGL0Lwg0LTQsNC90L3Ri9C8ICoqaXJpcyoqLiDQodC+0L7RgtC90LXRgdC40YLQtSDQt9C90LDRh9C10L3QuNGPICrRgdGC0LDQvdC00LDRgNGC0L3QvtCz0L4g0L7RgtC60LvQvtC90LXQvdC40Y8qINC/0LXRgNC10LzQtdC90L3Ri9GFLg0KDQpgYGB7cn0NCnJlcyA8LSBkZXNjcmliZShpcmlzKQ0KcmVzWydzZCddDQpgYGANCg0KIyMjIFRhc2sgMS41LjYNCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE0Nzkvc3RlcC8xND91bml0PTI1MjENCg0K0JIg0LTQsNC90L3Ri9GFICoqaXJpcyoqINGA0LDRgdC/0L7Qu9C+0LbQuNGC0LUg0L/QviDRg9Cx0YvQstCw0L3QuNGOINC30L3QsNGH0LXQvdC40Y8g0LzQtdC00LjQsNC9INC60L7Qu9C40YfQtdGB0YLQstC10L3QvdGL0YUg0L/QtdGA0LXQvNC10L3QvdGL0YUg0LIg0LPRgNGD0L/Qv9C1ICp2aXJnaW5pY2EqLg0KDQpgYGB7cn0NCiNzb3J0KHNhcHBseShpcmlzW2lyaXMkU3BlY2llcyA9PSd2aXJnaW5pY2EnLF1bLTVdLCBGVU49bWVkaWFuKSkNCmQgPC0gZGVzY3JpYmVCeShpcmlzWywxOjRdLCBncm91cCA9IGlyaXMkU3BlY2llcykkJ3ZpcmdpbmljYSdbJ21lZGlhbiddDQpkDQpgYGANCg0KIyMjIFRhc2sgMS41LjcNCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE0Nzkvc3RlcC8xNT91bml0PTI1MjENCg0K0JIg0L/QtdGA0LXQvNC10L3QvdC+0LkgKipteV92ZWN0b3IqKiDRgdC+0YXRgNCw0L3QtdC9INCy0LXQutGC0L7RgCDRgSDQv9GA0L7Qv9GD0YnQtdC90L3Ri9C80Lgg0LfQvdCw0YfQtdC90LjRj9C80LguINCS0LDQvCDQvdGD0LbQvdC+INGB0L7Qt9C00LDRgtGMINC90L7QstGL0Lkg0LLQtdC60YLQvtGAICpmaXhlZF92ZWN0b3IqLCDQsiDQutC+0YLQvtGA0L7QvCDQstGB0LUg0L/RgNC+0L/Rg9GJ0LXQvdC90YvQtSDQt9C90LDRh9C10L3QuNGPINCy0LXQutGC0L7RgNCwICoqbXlfdmVjdG9yKiog0LHRg9C00YPRgiDQt9Cw0LzQtdC90LXQvdGLINC90LAgKtGB0YDQtdC00L3QtdC1INC30L3QsNGH0LXQvdC40LUqINC/0L4g0LjQvNC10Y7RidC40LzRgdGPINC90LDQsdC70Y7QtNC10L3QuNGP0LwuDQoNCtCf0YDQuCDRjdGC0L7QvCDQuNGB0YXQvtC00L3Ri9C5INCy0LXQutGC0L7RgCDQvtGB0YLQsNCy0YzRgtC1INCx0LXQtyDQuNC30LzQtdC90LXQvdC40LkhDQoNCj9yZXBsYWNlDQoNCj9hZ2dyZWdhdGUNCg0KYGBge3J9DQpteV92ZWN0b3IgPC0gcm5vcm0oMzApDQpteV92ZWN0b3Jbc2FtcGxlKDE6MzAsIDEwKV0gPC0gTkEgICMg0L3QsCDQtNC10YHRj9GC0Ywg0YHQu9GD0YfQsNC50L3Ri9GFINC/0L7Qt9C40YbQuNC5INC/0L7QvNC10YHRgtC40LwgTkENCm15X3ZlY3Rvcl9tZWFuID0gbWVhbihteV92ZWN0b3JbIWlzLm5hKG15X3ZlY3RvcildKSAgIyBtZWFuIHZhbHVlIG9mIG15X3ZlY3RvciBlbGVtZW50cw0KDQpmaXhlZF92ZWN0b3IgPSByZXBsYWNlKG15X3ZlY3RvciwgaXMubmEobXlfdmVjdG9yKSwgbXlfdmVjdG9yX21lYW4pICAjIHJlcGxhY2UgbmEgZWxlbWVudHMgd2l0aCBtZWFuIHZhbHVlDQoNCiMgYW5vdGhlciBzb2x1dGlvbg0KIyBmaXhlZF92ZWN0b3IgPC0gcmVwbGFjZShteV92ZWN0b3IsIGlzLm5hKG15X3ZlY3RvciksIG1lYW4obXlfdmVjdG9yLCBuYS5ybSA9IFQpKQ0KDQpmaXhlZF92ZWN0b3INCmBgYA0KDQoNCiMjIDEuNiDQntC/0LjRgdCw0YLQtdC70YzQvdGL0LUg0YHRgtCw0YLQuNGB0YLQuNC60LguINCT0YDQsNGE0LjQutC4DQoNCiMjIyBWaWRlbyAxLjYuMQ0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTc4Ny9zdGVwLzI/dW5pdD0yNjcxDQoNCmBgYHtyfQ0KIyBSZWFkIGRhdGENCmRmIDwtIG10Y2Fycw0KZGYkdnMgPC0gZmFjdG9yKGRmJHZzLCBsYWJlbHMgPSBjKCJWIiwgIlMiKSkNCmRmJGFtIDwtIGZhY3RvcihkZiRhbSwgbGFiZWxzID0gYygiQXV0byIsICJNYW51YWwiKSkNCmBgYA0KDQojIyMjIGhpc3RvZ3JhbQ0KDQo/aGlzdA0KDQpgYGB7cn0NCmRpYWdyYW0gPC0gaGlzdChkZiRtcGcsIGJyZWFrcyA9IDUsIG1haW4gPSAiSGlzdG9ncmFtIG9mIE1QRyIsIHhsYWIgPSAiTVBHIikNCmRpYWdyYW0NCmBgYA0KDQojIyMjIGJveHBsb3QNCg0KP2JveHBsb3QNCg0KYGBge3J9DQpkaWFncmFtIDwtIGJveHBsb3QobXBnIH4gYW0sIGRmLCB4bGFiID0gIkdlYXJib3ggdHlwZSIsIHlsYWIgPSAiTVBHIikNCmRpYWdyYW0NCmBgYA0KDQojIyMjIHBsb3QNCg0KYGBge3J9DQpkaWFncmFtIDwtIHBsb3QoZGYkbXBnLCBkZiRocCkNCmRpYWdyYW0NCmBgYA0KDQojIyMgVmlkZW8gMS42LjINCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE3ODcvc3RlcC8zP3VuaXQ9MjY3MQ0KDQojIyMjIGdncGxvdDINCg0KbGlicmFyeShnZ3Bsb3QyKSAgIyBsb2FkIGxpYnJhcnkNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpICAjIGxvYWQgbGlicmFyeQ0KYGBgDQoNCiMjIyMgZ2VvbV9oaXN0b2dyYW0gKGdncGxvdDIpDQoNCmBgYHtyfQ0KZ2dwbG90KGRmLCBhZXMoeCA9IG1wZykpKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sID0gImJsYWNrIiwgYmlud2lkdGggPSAyKQ0KYGBgDQoNCiMjIyMgZ2VvbV9kb3RwbG90IChnZ3Bsb3QyKQ0KDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBtcGcsIGZpbGwgPSBhbSkpKw0KICBnZW9tX2RvdHBsb3QoYmlud2lkdGggPSAxKQ0KYGBgDQoNCiMjIyMgZ2VvbV9kZW5zaXR5IChnZ3Bsb3QyKQ0KDQpgYGB7cn0NCmdncGxvdChkZiwgYWVzKHggPSBtcGcsIGZpbGwgPSBhbSkpKw0KICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpDQpgYGANCg0KIyMjIFZpZGVvIDEuNi4zDQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNzg3L3N0ZXAvND91bml0PTI2NzENCg0KIyMjIyBnZW9tX2JveHBsb3QNCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh4ID0gYW0sIHkgPSBocCwgY29sID0gdnMpKSsNCiAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQojIyMjIGdlb21fcG9pbnQNCg0KYGBge3J9DQpnZ3Bsb3QoZGYsIGFlcyh4ID0gbXBnLCB5ID0gaHAsIGNvbCA9IHZzLCBzaXplID0gcXNlYykpKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQojIyMgVGFzayAxLjYuMQ0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTc4Ny9zdGVwLzU/dW5pdD0yNjcxDQoNCtCf0YDQuCDQv9C+0LzQvtGJ0Lgg0YTRg9C90LrRhtC40LggKmdncGxvdCgpKiDQuNC70LggKmJveHBsb3QoKSog0L/QvtGB0YLRgNC+0LnRgtC1INCz0YDQsNGE0LjQuiAqYm94cGxvdCosINC40YHQv9C+0LvRjNC30YPRjyDQstGB0YLRgNC+0LXQvdC90YvQtSDQsiBSINC00LDQvdC90YvQtSAqKmFpcnF1YWxpdHkqKi4g0J/QviDQvtGB0LggKip4Kiog0L7RgtC70L7QttC40YLQtSAq0L3QvtC80LXRgCDQvNC10YHRj9GG0LAqLCDQv9C+INC+0YHQuCAqKnkqKiDigJQg0LfQvdCw0YfQtdC90LjRjyDQv9C10YDQtdC80LXQvdC90L7QuSAqT3pvbmUuKg0KDQrQndCwINCz0YDQsNGE0LjQutC1ICpib3hwbG90KiDQvtGC0LTQtdC70YzQvdGL0LzQuCDRgtC+0YfQutCw0LzQuCDQvtGC0L7QsdGA0LDQttCw0Y7RgtGB0Y8g0L3QsNCx0LvRjtC00LXQvdC40Y8sINC+0YLQutC70L7QvdGP0Y7RidC40LXRgdGPINC+0YIgMSDQuNC70LggMyDQutCy0LDRgNGC0LjQu9GPINCx0L7Qu9GM0YjQtSDRh9C10Lwg0L3QsCDQv9C+0LvRgtC+0YDQsCDQvNC10LbQutCy0LDRgNGC0LjQu9GM0L3Ri9GFINGA0LDQt9C80LDRhdCwLiDQodC60L7Qu9GM0LrQviDRgtCw0LrQuNGFINC90LDQsdC70Y7QtNC10L3QuNC5INC/0YDQuNGB0YPRgtGB0YLQstGD0LXRgiDQsiDRgdC10L3RgtGP0LHRgNC1ICjQvNC10YHRj9GGIOKEljkpPw0KDQrQntCx0YDQsNGC0LjRgtC1INCy0L3QuNC80LDQvdC40LUsINGH0YLQviDQtNC70Y8g0LrQvtGA0YDQtdC60YLQvdC+0LPQviDQvtGC0L7QsdGA0LDQttC10L3QuNGPINCz0YDQsNGE0LjQutCwICpnZ3Bsb3QqINC+0LbQuNC00LDQtdGCINGE0LDQutGC0L7RgNC90YPRjiDQv9C10YDQtdC80LXQvdC90YPRjiDQv9C+INC+0YHQuCAqKngqKi4NCg0KYGBge3J9DQpkZiA8LSBhaXJxdWFsaXR5DQpkZiRNb250aCA8LSBmYWN0b3IoZGYkTW9udGgsIGxhYmVscyA9IDUgOiA5KQ0KYGBgDQoNCmBgYHtyfQ0KIyBsaWJyYXJ5KGdncGxvdDIpDQpnZ3Bsb3QoZGYsIGFlcyh4ID0gTW9udGgsIHkgPSBPem9uZSkpKw0KICBnZW9tX2JveHBsb3QobmEucm0gPSBUKQ0KYGBgDQoNCiMjIyBUYXNrIDEuNi4yDQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNzg3L3N0ZXAvNj91bml0PTI2NzENCg0K0JjRgdC/0L7Qu9GM0LfRg9C10Lwg0LfQvdCw0LrQvtC80YvQtSDQvdCw0Lwg0LTQsNC90L3Ri9C1ICoqbXRjYXJzKiouDQoNCtCd0YPQttC90L4g0L/QvtGB0YLRgNC+0LjRgtGMICpzY2F0dGVycGxvdCog0YEg0L/QvtC80L7RidGM0Y4gZ2dwbG90INC40LcgZ2dwbG90Miwg0L/QviDQvtGB0LggKip4Kiog0LrQvtGC0L7RgNC+0LPQviDQsdGD0LTQtdGCICptcGcqLCDQv9C+INC+0YHQuCAqKnkqKiAtICpkaXNwKiwg0LAg0YbQstC10YLQvtC8INC+0YLQvtCx0YDQsNC30LjRgtGMINC/0LXRgNC10LzQtdC90L3Rg9GOICgqaHAqKS4NCg0K0J/QvtC70YPRh9C10L3QvdGL0Lkg0LPRgNCw0YTQuNC6INC90YPQttC90L4g0YHQvtGF0YDQsNC90LjRgtGMINCyINC/0LXRgNC10LzQtdC90L3Rg9GOICoqcGxvdDEqKi4NCg0KYGBge3J9DQpwbG90MSA8LSBnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IG1wZywgeSA9IGRpc3AsIGNvbCA9IGhwKSkrDQogIGdlb21fcG9pbnQoKQ0KcGxvdDENCmBgYA0KDQojIyMgVGFzayAxLjYuMw0KaHR0cHM6Ly9zdGVwaWsub3JnL2xlc3Nvbi8xMTc4Ny9zdGVwLzc/dW5pdD0yNjcxDQoNCtCj0LrQsNC20LjRgtC1LCDQv9GA0Lgg0L/QvtC80L7RidC4INC60LDQutC+0LPQviDQstCw0YDQuNCw0L3RgtCwINC60L7QtNCwINC80Ysg0LzQvtC20LXQvCDQv9C+0YHRgtGA0L7QuNGC0Ywg0YHQu9C10LTRg9GO0YnQuNC5INCz0YDQsNGE0LjQuiDQv9C+INC00LDQvdC90YvQvCAqaXJpcyoqLg0K0JPQuNGB0YLQvtCz0YDQsNC80LzQsCDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjRjyDQv9C10YDQtdC80LXQvdC90L7QuSAqU2VwYWwuTGVuZ3RoKiwg0LIg0LrQvtGC0L7RgNC+0Lkg0YbQstC10YIg0LfQsNC/0L7Qu9C90LXQvdC40Y8g0YHRgtC+0LvQsdGG0L7QsiDQs9C40YHRgtC+0LPRgNCw0LzQvNGLINC30LDQstC40YHQuNGCINC+0YIg0LfQvdCw0YfQtdC90LjRjyDQv9C10YDQtdC80LXQvdC90L7QuSAqU3BlY2llcyouDQoNCmBgYHtyfQ0KIyBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCkpICsgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSBTcGVjaWVzKSkNCm9iaiA8LSBnZ3Bsb3QoaXJpcywgYWVzKFNlcGFsLkxlbmd0aCwgZmlsbCA9IFNwZWNpZXMpKSArIGdlb21faGlzdG9ncmFtKCkNCm9iag0KYGBgDQoNCiMjIyBUYXNrIDEuNi40DQpodHRwczovL3N0ZXBpay5vcmcvbGVzc29uLzExNzg3L3N0ZXAvOD91bml0PTI2NzENCg0K0KHRgtGD0LTQtdC90YIg0K/RgNC+0YHQu9Cw0LIg0L7Rh9C10L3RjCDQu9GO0LHQuNGCINGB0YLRgNC+0LjRgtGMINCz0YDQsNGE0LjQutC4INCyIFIuINCe0YHQvdC+0LLRi9Cy0LDRj9GB0Ywg0L3QsCDQtNCw0L3QvdGL0YUgKmlyaXMqKiDQvtC9INGF0L7Rh9C10YIg0L/QvtGB0YLRgNC+0LjRgtGMINGB0LvQtdC00YPRjtGJ0LjQuSDQs9GA0LDRhNC40LouDQoNClNjYXR0ZXJwbG90ICjQtNC40LDQs9GA0LDQvNC80LAg0YDQsNGB0YHQtdC40LLQsNC90LjRjyksINCz0LTQtSDQv9C+INC+0YHQuCAqKlgqKiDQsdGD0LTQtdGCINC+0YLQu9C+0LbQtdC90LAg0L/QtdGA0LXQvNC10L3QvdCw0Y8gKlNlcGFsLkxlbmd0aCosICDQv9C+INC+0YHQuCAqKlkqKiDQv9C10YDQtdC80LXQvdC90LDRjyAqU2VwYWwuV2lkdGgqLiDQl9CwINGG0LLQtdGCINGC0L7Rh9C10Log0LHRg9C00LXRgiDQvtGC0LLQtdGH0LDRgtGMINC/0LXRgNC10LzQtdC90L3QsNGPICAqU3BlY2llcyosINCwINC30LAg0YDQsNC30LzQtdGAINGC0L7Rh9C10Log0L/QtdGA0LXQvNC10L3QvdCw0Y8gKlBldGFsLkxlbmd0aCouDQoNCmBgYHtyfQ0KZ2dwbG90KGlyaXMsIGFlcyhTZXBhbC5MZW5ndGgsIFNlcGFsLldpZHRoLCBjb2wgPSBTcGVjaWVzLCBzaXplID0gUGV0YWwuTGVuZ3RoKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQojIyAxLjcg0KHQvtGF0YDQsNC90LXQvdC40LUg0YDQtdC30YPQu9GM0YLQsNGC0L7Qsg0KDQojIyMgVmlkZW8gMS43LjENCmh0dHBzOi8vc3RlcGlrLm9yZy9sZXNzb24vMTE0ODAvc3RlcC8yP3VuaXQ9MjUyMg0KDQpgYGB7cn0NCmdldHdkKCkgICMgc2hvdyB3b3JrIGRpcmVjdG9yeSAiQzovVXNlcnMveHh4L0RvY3VtZW50cyINCnNldHdkKCJ+L1IvZGF0YV9hbmFseXNpc18xIikgICMgc2V0IHdvcmsgZGlyZWN0b3J5DQoNCndyaXRlLmNzdihtdGNhcnMsICJtdGNhcnMuY3N2IikgICMgc2F2ZSBkYXRhIGZyYW1lIGFzIGNzdiBmaWxlDQpgYGANCg0KYGBge3J9DQpteV9tZWFuID0gbWVhbigxMF41IDogMTBeNykNCnNhdmUobXlfbWVhbiwgIm15X21lYW4uUkRhdGEiKSAgIyBzYXZlIHZhbHVlIGFzIGZpbGUgUkRhdGENCmBgYA0K