Элементы функционального программирования

R сочетает в себе несколько парадигм программирования. Если попытаться выделить какую-то главную, то пожалуй - это элементы функционального проаграммирования. Эта тема относится к продвинутому уровню. Это нормально чувствовать себя на этом уровне некомфортно и неуверенно

Объектно-ориентированные системы

Когда мы говорим об объектах языка R таких как вектор, функция и т.д., то это не то же самое когда речь идет об объектно-ориентированном программировании.

Что входит в понятие объектино-ориентированного программирования?:
1. Классы

  1. Поля классов

  2. Методы, определенные для классов

Классы

В R можно создавать классы. Стандартно их сразу 3 в R:
* S3
- нет формальной деклараци класса
- функция может иметь разное поведение в зависимости от класса (method dispatch)
- такие функции называются generic
Вторые два класса встречаются значительно реже, прочитать про них подробнее можно в книге Advanced R * S4
- строгое определеие класса и его полей
- больше возможностей для поведения методов
* Reference classes

Generic функции

Generic функции следует разобрать подробнее, т.к. всречаются повсеместно. Можно проверить является ли функция print generic функцией

length(methods(print))
[1] 217

Если мы получили много вариантов, значит функция может себя по-разному вести в зависимости от того, чтобы приходит ей в качестве аргумента. Какие могут быть примеры?

Если берем print(x), то если:
- x - дата-фрейм, то вызывается print.data.frame(x);
- если x - функция, то print.function(x) и так далее
- если ни один из методов не подходит, то print.default(x)

Функции без сторонних эффектов

В R нет указателей на объекты, все объекты передаются “по значению” (хотя есть нюансы!)

f <- function(k) {
  k <- k+1
  a <- a + k*2
  a
}
k <- 5
f(k)
[1]  13  14  54 111

Примеры функций, отражающие функциональное программирование в языке R

Функция **replicate*
В задачах моделирования случайных процессов приходится встречаться с запросом повторить один и тот же выхов несколько раз, при этом каждый новый вызов может иметь новый результат (зависящий, например, датчика случайных чисел)

Фунции семейтсва apply apply, sapply. lapply, tapply

mapply - многомерная версия sapply. Она приниает на вход функцию и произвольное количество списков аргументов, которые необходиом перебрать. Например:

mapply(seq, from = 1:4, to = 2:5, by = 1/(1 + 1:4))
[[1]]
[1] 1.0 1.5 2.0

[[2]]
[1] 2.000000 2.333333 2.666667
[4] 3.000000

[[3]]
[1] 3.00 3.25 3.50 3.75 4.00

[[4]]
[1] 4.0 4.2 4.4 4.6 4.8 5.0

Получаем тоже самое, что получили бы при последовательном вызове функции seq для каждого первого элемента аргументов и далее каждого n-го элемента аргументов

outer

перебор всех возможных комбинаций аргументов.
Допустим, хочу применить paste0 ко всем возможным аргументам массива LETTERS в нижнем и в ВЕРХНЕМ регистре. Тогда можно воспользоваться функцией outer

m <- outer(letters, LETTERS, paste0)

Теперь можно увидеть, что m - это матрица размером 26*26. При этом диагональные элементы будут содержать в себе одинаковые буквы

diag(m)
 [1] "aA" "bB" "cC" "dD" "eE"
 [6] "fF" "gG" "hH" "iI" "jJ"
[11] "kK" "lL" "mM" "nN" "oO"
[16] "pP" "qQ" "rR" "sS" "tT"
[21] "uU" "vV" "wW" "xX" "yY"
[26] "zZ"
m[1:5,1:4]
     [,1] [,2] [,3] [,4]
[1,] "aA" "aB" "aC" "aD"
[2,] "bA" "bB" "bC" "bD"
[3,] "cA" "cB" "cC" "cD"
[4,] "dA" "dB" "dC" "dD"
[5,] "eA" "eB" "eC" "eD"

Vectorize

Не все функции векторизированы по умолчанию, то их можно сделать такими с помощью функции Vectorize

do.call

Последняя функция из списка функционального прогрммирования. Она осуществляет вызов некоторой функции на списке аргументов. Например, пришел дата-фрейм, но он сегментирован на 3 отдельных файла, которые нужно объединить между собой. Можно их соединить вместе с помощью rbind, а можно с помощью do.call

df1 <- data.frame(ld = 1:2, value = rnorm(2))
df2 <- data.frame(ld = 3:4, value = runif(2))
df3 <- data.frame(ld = 222, value = 7)  
do.call(rbind, list(df1,df2, df3))

Но зачем тогда вообще do.call, если в этом примере можно было обойтись без него просто точно так же склеив дата фреймы через rbind.
Функция нужна, когда мы не знаем сколько именно объектов нужно будет склеить

do.call(rbind,lapply(list.files(), function(file) read.csv(file)))
cannot open file 'ASUS': Permission deniedError in file(file, "rt") : cannot open the connection

Глосаррий

  • S3, S4, reference class
  • generic function
  • copy-on-modify semantics
  • ?replicate
  • ?mapply
  • ?outer
  • Vectorize
  • do.call

Моделирование траектории случайного процесса

Упражнение может иметь реальное применение в физике элементарных частиц.

# Random walk with absorption
simulate_walk <- function(lower = -10, upper = 10, n_max = 200, p = 1e-3) {
   # на каждом шаге есть позиция, начинаем из центра интервала
  current_position <- (lower + upper) / 2
  for (i in 1:n_max) {
    #сначала определяем вероятность поглощения частицы
    is_absorbed <- rbinom(1, 1, p)
    #если произошло - симуляция заканчивается
    if (is_absorbed) return(list(status = "Absorbed", 
                                 position = current_position, 
                                 steps = i))
    #если поглощение не произошло - совершаем 1 случайный шаг
    current_position <- current_position + rnorm(1)
    if (current_position < lower) return(list(status = "Left breach", 
                                              position = current_position, 
                                              steps = i))
    if (current_position > upper) return(list(status = "Right breach", 
                                              position = current_position, 
                                              steps = i))
  }
  #возвращаем такое сообщение при достижении максимального количества шагов
  return(list(status = "Max steps reached", 
              position = current_position,#позиция, на которой завершилась симуляция
              steps = n_max)) #количество шагов, на которой завершилась симуляция
}

Есть одномерная область от -10 до 10, в которой может находится элементарная частица. Частица может прыгать либо вправо, либо влево и на каждом шаге есть вероятность того, что она поглотится (p = 1e-3). Общее количество переходов ограничиваем 200 шагами (n_max = 200).
Почему использовали цикл for, хотя для R - это один из медленных способов, к которому нужно относится с осторожностью. Дело в том, что на каждом из шагов симуляция может быть прервана и закончится - каждое определение перехода это какая-то сложная вычислительная заадача. Если генерировать такую ситуацию через векторизацию, то мы генерируем сразу 200 переходов (потенциальный максимум), тогда как цикл может прерваться уже на первой итерации в случае поглощения частицы. Т.е. в такой ситуации цикл for позволяет сэкономить те переходы, которые выпадают за границы симуляции

Теперь можно с помощью функции replicate повторить n-ое число таких симуляций

# Simulate results
result <- replicate(1000, simulate_walk(), simplify = FALSE)
result <- data.frame(
  status = sapply(result, function(x) x$status),
  position = sapply(result, function(x) x$position),
  steps = sapply(result, function(x) x$steps)
)

Приводим результат в том виде, который нам удобен. В данной конкретной ситуации превращаем его в дата-фрейм

Можно посмотреть, что примерно получилось

str(result)
'data.frame':   1000 obs. of  3 variables:
 $ status  : chr  "Left breach" "Absorbed" "Left breach" "Left breach" ...
 $ position: num  -10.17 -6.95 -10.65 -11.12 10.04 ...
 $ steps   : num  56 27 84 167 20 87 100 79 26 121 ...

Теперь, когда у нас есть какая-то разумная статистика, то можно ее определеить, например, с помощью функции tapply

tapply(result$position, result$status, length)
         Absorbed       Left breach Max steps reached      Right breach 
               99               389               120               392 
tapply(result$steps, result$status, mean)
         Absorbed       Left breach Max steps reached      Right breach 
         77.54545          79.48329         200.00000          82.80612 

Больший интерес представляют задачи с блужданием по плоскости, то есть в размерности 2.
Возьмите написанную мной функцию и измените её так, чтобы блуждание начиналось в центре координат (0, 0), а все переходы по координатам x и y были бы независимы и имели стандартное нормальное распределение. Если вас пугают эти слова, то это то же самое, что делал я, только отдельно по x и по y.

В ответе укажите вероятность вылета частицы в процентах, с точностью до целых процентов, в виде XX (например, 14, без указания значка процентов). Вероятность должна получиться больше 50%.

simulate_walk <- function(n_max = 100, p = 1e-2) {
  x<-0
  y<-0
  current_position <- 0 
  for (i in 1:n_max) {
    is_absorbed <- rbinom(1, 1, p)
    if (is_absorbed) return(list(status = "Absorbed", 
                                 position = current_position, 
                                 steps = i))
    x<-x+rnorm(1)
    y<-y+rnorm(1)
    current_position <- sqrt(x**2 + y**2)
    if (current_position > 6) return(list(status = "breach", 
                                              position = current_position, 
                                              steps = i))
  }
  return(list(status = "Max steps reached", 
              position = current_position,
              steps = n_max))
}

# Simulate results
result <- replicate(100000, simulate_walk(), simplify = FALSE)
result <- data.frame(
  status = sapply(result, function(x) x$status),
  position = sapply(result, function(x) x$position),
  steps = sapply(result, function(x) x$steps)
)
# Inspect results
tapply(result$position, result$status, length)
         Absorbed            breach 
            18934             80997 
Max steps reached 
               69 
a
[1] 81.05293

Общий глоссарий для этого урока:

S3, S4, reference classes
Generic function
Copy-on-modify semantics
?replicate
?mapply
?outer
?Vectorize
?do.call

LS0tDQp0aXRsZTogItCk0YPQvdC60YbQuNC+0L3QsNC70YzQvdC+0LUg0L/RgNC+0LPRgNCw0LzQvNC40YDQvtCy0LDQvdC40LUg0LIgUiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMg0K3Qu9C10LzQtdC90YLRiyDRhNGD0L3QutGG0LjQvtC90LDQu9GM0L3QvtCz0L4g0L/RgNC+0LPRgNCw0LzQvNC40YDQvtCy0LDQvdC40Y8NClIg0YHQvtGH0LXRgtCw0LXRgiDQsiDRgdC10LHQtSDQvdC10YHQutC+0LvRjNC60L4g0L/QsNGA0LDQtNC40LPQvCDQv9GA0L7Qs9GA0LDQvNC80LjRgNC+0LLQsNC90LjRjy4g0JXRgdC70Lgg0L/QvtC/0YvRgtCw0YLRjNGB0Y8g0LLRi9C00LXQu9C40YLRjCDQutCw0LrRg9GOLdGC0L4g0LPQu9Cw0LLQvdGD0Y4sINGC0L4g0L/QvtC20LDQu9GD0LkgLSDRjdGC0L4g0Y3Qu9C10LzQtdC90YLRiyDRhNGD0L3QutGG0LjQvtC90LDQu9GM0L3QvtCz0L4g0L/RgNC+0LDQs9GA0LDQvNC80LjRgNC+0LLQsNC90LjRjy4g0K3RgtCwINGC0LXQvNCwINC+0YLQvdC+0YHQuNGC0YHRjyDQuiDQv9GA0L7QtNCy0LjQvdGD0YLQvtC80YMg0YPRgNC+0LLQvdGOLiDQrdGC0L4g0L3QvtGA0LzQsNC70YzQvdC+INGH0YPQstGB0YLQstC+0LLQsNGC0Ywg0YHQtdCx0Y8g0L3QsCDRjdGC0L7QvCDRg9GA0L7QstC90LUg0L3QtdC60L7QvNGE0L7RgNGC0L3QviDQuCDQvdC10YPQstC10YDQtdC90L3Qvg0KDQojIyDQntCx0YrQtdC60YLQvdC+LdC+0YDQuNC10L3RgtC40YDQvtCy0LDQvdC90YvQtSDRgdC40YHRgtC10LzRiw0K0JrQvtCz0LTQsCDQvNGLINCz0L7QstC+0YDQuNC8INC+0LEg0L7QsdGK0LXQutGC0LDRhSDRj9C30YvQutCwIFIg0YLQsNC60LjRhSDQutCw0Log0LLQtdC60YLQvtGALCDRhNGD0L3QutGG0LjRjyDQuCDRgi7QtC4sINGC0L4g0Y3RgtC+INC90LUg0YLQviDQttC1INGB0LDQvNC+0LUg0LrQvtCz0LTQsCDRgNC10YfRjCDQuNC00LXRgiDQvtCxINC+0LHRitC10LrRgtC90L4t0L7RgNC40LXQvdGC0LjRgNC+0LLQsNC90L3QvtC8INC/0YDQvtCz0YDQsNC80LzQuNGA0L7QstCw0L3QuNC4LiAgDQogIA0K0KfRgtC+INCy0YXQvtC00LjRgiDQsiDQv9C+0L3Rj9GC0LjQtSAqKtC+0LHRitC10LrRgtC40L3Qvi3QvtGA0LjQtdC90YLQuNGA0L7QstCw0L3QvdC+0LPQviDQv9GA0L7Qs9GA0LDQvNC80LjRgNC+0LLQsNC90LjRjyoqPzogIA0KMS4g0JrQu9Cw0YHRgdGLICANCg0KMi4g0J/QvtC70Y8g0LrQu9Cw0YHRgdC+0LIgIA0KDQozLiDQnNC10YLQvtC00YssINC+0L/RgNC10LTQtdC70LXQvdC90YvQtSDQtNC70Y8g0LrQu9Cw0YHRgdC+0LIgIA0KDQojINCa0LvQsNGB0YHRiw0K0JIgUiDQvNC+0LbQvdC+INGB0L7Qt9C00LDQstCw0YLRjCDQutC70LDRgdGB0YsuINCh0YLQsNC90LTQsNGA0YLQvdC+INC40YUg0YHRgNCw0LfRgyAzINCyIFI6ICANCiogKlMzKiAgDQotINC90LXRgiDRhNC+0YDQvNCw0LvRjNC90L7QuSDQtNC10LrQu9Cw0YDQsNGG0Lgg0LrQu9Cw0YHRgdCwICANCi0g0YTRg9C90LrRhtC40Y8g0LzQvtC20LXRgiDQuNC80LXRgtGMINGA0LDQt9C90L7QtSAgINC/0L7QstC10LTQtdC90LjQtSDQsiDQt9Cw0LLQuNGB0LjQvNC+0YHRgtC4INC+0YIg0LrQu9Cw0YHRgdCwICgqKm1ldGhvZCBkaXNwYXRjaCoqKSAgDQotINGC0LDQutC40LUg0YTRg9C90LrRhtC40Lgg0L3QsNC30YvQstCw0Y7RgtGB0Y8gKmdlbmVyaWMqICAgDQrQktGC0L7RgNGL0LUg0LTQstCwINC60LvQsNGB0YHQsCDQstGB0YLRgNC10YfQsNGO0YLRgdGPINC30L3QsNGH0LjRgtC10LvRjNC90L4g0YDQtdC20LUsINC/0YDQvtGH0LjRgtCw0YLRjCDQv9GA0L4g0L3QuNGFINC/0L7QtNGA0L7QsdC90LXQtSDQvNC+0LbQvdC+INCyINC60L3QuNCz0LUgKkFkdmFuY2VkIFIqDQoqICpTNCogIA0KLSDRgdGC0YDQvtCz0L7QtSDQvtC/0YDQtdC00LXQu9C10LjQtSDQutC70LDRgdGB0LAg0Lgg0LXQs9C+INC/0L7Qu9C10LkgIA0KLSDQsdC+0LvRjNGI0LUg0LLQvtC30LzQvtC20L3QvtGB0YLQtdC5INC00LvRjyDQv9C+0LLQtdC00LXQvdC40Y8g0LzQtdGC0L7QtNC+0LIgICANCiogUmVmZXJlbmNlIGNsYXNzZXMgDQoNCiMjIEdlbmVyaWMg0YTRg9C90LrRhtC40LgNCkdlbmVyaWMg0YTRg9C90LrRhtC40Lgg0YHQu9C10LTRg9C10YIg0YDQsNC30L7QsdGA0LDRgtGMINC/0L7QtNGA0L7QsdC90LXQtSwg0YIu0LouINCy0YHRgNC10YfQsNGO0YLRgdGPINC/0L7QstGB0LXQvNC10YHRgtC90L4uINCc0L7QttC90L4g0L/RgNC+0LLQtdGA0LjRgtGMINGP0LLQu9GP0LXRgtGB0Y8g0LvQuCDRhNGD0L3QutGG0LjRjyAqcHJpbnQqIGdlbmVyaWMg0YTRg9C90LrRhtC40LXQuQ0KYGBge3J9DQpsZW5ndGgobWV0aG9kcyhwcmludCkpDQpgYGANCtCV0YHQu9C4INC80Ysg0L/QvtC70YPRh9C40LvQuCDQvNC90L7Qs9C+INCy0LDRgNC40LDQvdGC0L7Qsiwg0LfQvdCw0YfQuNGCINGE0YPQvdC60YbQuNGPINC80L7QttC10YIg0YHQtdCx0Y8g0L/Qvi3RgNCw0LfQvdC+0LzRgyDQstC10YHRgtC4INCyINC30LDQstC40YHQuNC80L7RgdGC0Lgg0L7RgiDRgtC+0LPQviwg0YfRgtC+0LHRiyDQv9GA0LjRhdC+0LTQuNGCINC10Lkg0LIg0LrQsNGH0LXRgdGC0LLQtSDQsNGA0LPRg9C80LXQvdGC0LAuINCa0LDQutC40LUg0LzQvtCz0YPRgiDQsdGL0YLRjCDQv9GA0LjQvNC10YDRiz8gIA0KICANCtCV0YHQu9C4INCx0LXRgNC10LwgcHJpbnQoeCksINGC0L4g0LXRgdC70Lg6ICANCi0geCAtINC00LDRgtCwLdGE0YDQtdC50LwsINGC0L4g0LLRi9C30YvQstCw0LXRgtGB0Y8gKnByaW50LmRhdGEuZnJhbWUoeCkqOyAgDQotINC10YHQu9C4IHggLSDRhNGD0L3QutGG0LjRjywg0YLQviAqcHJpbnQuZnVuY3Rpb24oeCkqINC4INGC0LDQuiDQtNCw0LvQtdC1ICANCi0g0LXRgdC70Lgg0L3QuCDQvtC00LjQvSDQuNC3INC80LXRgtC+0LTQvtCyINC90LUg0L/QvtC00YXQvtC00LjRgiwg0YLQviAqcHJpbnQuZGVmYXVsdCh4KSogIA0KICANCiMg0KTRg9C90LrRhtC40Lgg0LHQtdC3INGB0YLQvtGA0L7QvdC90LjRhSDRjdGE0YTQtdC60YLQvtCyDQrQkiBSINC90LXRgiDRg9C60LDQt9Cw0YLQtdC70LXQuSDQvdCwINC+0LHRitC10LrRgtGLLCDQstGB0LUg0L7QsdGK0LXQutGC0Ysg0L/QtdGA0LXQtNCw0Y7RgtGB0Y8gItC/0L4g0LfQvdCw0YfQtdC90LjRjiIgKNGF0L7RgtGPINC10YHRgtGMINC90Y7QsNC90YHRiyEpDQpgYGB7cn0NCmYgPC0gZnVuY3Rpb24oaykgew0KICBrIDwtIGsrMQ0KICBhIDwtIGEgKyBrKjINCiAgYQ0KfQ0KayA8LSA1DQpmKGspDQpgYGANCg0KIyMg0J/RgNC40LzQtdGA0Ysg0YTRg9C90LrRhtC40LksINC+0YLRgNCw0LbQsNGO0YnQuNC1INGE0YPQvdC60YbQuNC+0L3QsNC70YzQvdC+0LUg0L/RgNC+0LPRgNCw0LzQvNC40YDQvtCy0LDQvdC40LUg0LIg0Y/Qt9GL0LrQtSBSDQoNCiMjIyDQpNGD0L3QutGG0LjRjyAqKnJlcGxpY2F0ZSogIA0K0JIg0LfQsNC00LDRh9Cw0YUg0LzQvtC00LXQu9C40YDQvtCy0LDQvdC40Y8g0YHQu9GD0YfQsNC50L3Ri9GFINC/0YDQvtGG0LXRgdGB0L7QsiDQv9GA0LjRhdC+0LTQuNGC0YHRjyDQstGB0YLRgNC10YfQsNGC0YzRgdGPINGBINC30LDQv9GA0L7RgdC+0Lwg0L/QvtCy0YLQvtGA0LjRgtGMINC+0LTQuNC9INC4INGC0L7RgiDQttC1INCy0YvRhdC+0LIg0L3QtdGB0LrQvtC70YzQutC+INGA0LDQtywg0L/RgNC4INGN0YLQvtC8INC60LDQttC00YvQuSDQvdC+0LLRi9C5INCy0YvQt9C+0LIg0LzQvtC20LXRgiDQuNC80LXRgtGMINC90L7QstGL0Lkg0YDQtdC30YPQu9GM0YLQsNGCICjQt9Cw0LLQuNGB0Y/RidC40LksINC90LDQv9GA0LjQvNC10YAsINC00LDRgtGH0LjQutCwINGB0LvRg9GH0LDQudC90YvRhSDRh9C40YHQtdC7KSAgDQogIA0KIyMjINCk0YPQvdGG0LjQuCDRgdC10LzQtdC50YLRgdCy0LAgYXBwbHkgIGFwcGx5LCBzYXBwbHkuIGxhcHBseSwgdGFwcGx5DQoqbWFwcGx5KiAtINC80L3QvtCz0L7QvNC10YDQvdCw0Y8g0LLQtdGA0YHQuNGPIHNhcHBseS4g0J7QvdCwINC/0YDQuNC90LjQsNC10YIg0L3QsCDQstGF0L7QtCDRhNGD0L3QutGG0LjRjiDQuCDQv9GA0L7QuNC30LLQvtC70YzQvdC+0LUg0LrQvtC70LjRh9C10YHRgtCy0L4g0YHQv9C40YHQutC+0LIg0LDRgNCz0YPQvNC10L3RgtC+0LIsINC60L7RgtC+0YDRi9C1INC90LXQvtCx0YXQvtC00LjQvtC8INC/0LXRgNC10LHRgNCw0YLRjC4g0J3QsNC/0YDQuNC80LXRgDogDQpgYGB7cn0NCm1hcHBseShzZXEsIGZyb20gPSAxOjQsIHRvID0gMjo1LCBieSA9IDEvKDEgKyAxOjQpKQ0KYGBgDQrQn9C+0LvRg9GH0LDQtdC8INGC0L7QttC1INGB0LDQvNC+0LUsINGH0YLQviDQv9C+0LvRg9GH0LjQu9C4INCx0Ysg0L/RgNC4INC/0L7RgdC70LXQtNC+0LLQsNGC0LXQu9GM0L3QvtC8INCy0YvQt9C+0LLQtSDRhNGD0L3QutGG0LjQuCBzZXEg0LTQu9GPINC60LDQttC00L7Qs9C+INC/0LXRgNCy0L7Qs9C+INGN0LvQtdC80LXQvdGC0LAg0LDRgNCz0YPQvNC10L3RgtC+0LIg0Lgg0LTQsNC70LXQtSDQutCw0LbQtNC+0LPQviBuLdCz0L4g0Y3Qu9C10LzQtdC90YLQsCDQsNGA0LPRg9C80LXQvdGC0L7Qsg0KDQojIyMgb3V0ZXINCtC/0LXRgNC10LHQvtGAINCy0YHQtdGFINCy0L7Qt9C80L7QttC90YvRhSDQutC+0LzQsdC40L3QsNGG0LjQuSDQsNGA0LPRg9C80LXQvdGC0L7Qsi4gIA0K0JTQvtC/0YPRgdGC0LjQvCwg0YXQvtGH0YMg0L/RgNC40LzQtdC90LjRgtGMICpwYXN0ZTAqINC60L4g0LLRgdC10Lwg0LLQvtC30LzQvtC20L3Ri9C8INCw0YDQs9GD0LzQtdC90YLQsNC8INC80LDRgdGB0LjQstCwIExFVFRFUlMg0LIg0L3QuNC20L3QtdC8INC4INCyINCS0JXQoNCl0J3QldCcINGA0LXQs9C40YHRgtGA0LUuINCi0L7Qs9C00LAg0LzQvtC20L3QviDQstC+0YHQv9C+0LvRjNC30L7QstCw0YLRjNGB0Y8g0YTRg9C90LrRhtC40LXQuSBvdXRlciAgDQpgYGB7cn0NCm0gPC0gb3V0ZXIobGV0dGVycywgTEVUVEVSUywgcGFzdGUwKQ0KYGBgDQrQotC10L/QtdGA0Ywg0LzQvtC20L3QviDRg9Cy0LjQtNC10YLRjCwg0YfRgtC+ICptKiAtINGN0YLQviDQvNCw0YLRgNC40YbQsCDRgNCw0LfQvNC10YDQvtC8IDI2KjI2LiAg0J/RgNC4INGN0YLQvtC8INC00LjQsNCz0L7QvdCw0LvRjNC90YvQtSDRjdC70LXQvNC10L3RgtGLINCx0YPQtNGD0YIg0YHQvtC00LXRgNC20LDRgtGMINCyINGB0LXQsdC1INC+0LTQuNC90LDQutC+0LLRi9C1INCx0YPQutCy0YsgIA0KYGBge3J9DQpkaWFnKG0pDQpgYGANCmBgYHtyfQ0KbVsxOjUsMTo0XQ0KYGBgDQoNCiMjIyBWZWN0b3JpemUgIA0K0J3QtSDQstGB0LUg0YTRg9C90LrRhtC40Lgg0LLQtdC60YLQvtGA0LjQt9C40YDQvtCy0LDQvdGLINC/0L4g0YPQvNC+0LvRh9Cw0L3QuNGOLCDRgtC+INC40YUg0LzQvtC20L3QviDRgdC00LXQu9Cw0YLRjCDRgtCw0LrQuNC80Lgg0YEg0L/QvtC80L7RidGM0Y4g0YTRg9C90LrRhtC40LggKlZlY3Rvcml6ZSogIA0KICANCiMjIyBkby5jYWxsICANCtCf0L7RgdC70LXQtNC90Y/RjyDRhNGD0L3QutGG0LjRjyDQuNC3INGB0L/QuNGB0LrQsCDRhNGD0L3QutGG0LjQvtC90LDQu9GM0L3QvtCz0L4g0L/RgNC+0LPRgNC80LzQuNGA0L7QstCw0L3QuNGPLiDQntC90LAg0L7RgdGD0YnQtdGB0YLQstC70Y/QtdGCINCy0YvQt9C+0LIg0L3QtdC60L7RgtC+0YDQvtC5INGE0YPQvdC60YbQuNC4INC90LAg0YHQv9C40YHQutC1INCw0YDQs9GD0LzQtdC90YLQvtCyLiDQndCw0L/RgNC40LzQtdGALCDQv9GA0LjRiNC10Lsg0LTQsNGC0LAt0YTRgNC10LnQvCwg0L3QviDQvtC9INGB0LXQs9C80LXQvdGC0LjRgNC+0LLQsNC9INC90LAgMyDQvtGC0LTQtdC70YzQvdGL0YUg0YTQsNC50LvQsCwg0LrQvtGC0L7RgNGL0LUg0L3Rg9C20L3QviDQvtCx0YrQtdC00LjQvdC40YLRjCDQvNC10LbQtNGDINGB0L7QsdC+0LkuINCc0L7QttC90L4g0LjRhSDRgdC+0LXQtNC40L3QuNGC0Ywg0LLQvNC10YHRgtC1INGBINC/0L7QvNC+0YnRjNGOIHJiaW5kLCDQsCDQvNC+0LbQvdC+INGBINC/0L7QvNC+0YnRjNGOIGRvLmNhbGwNCmBgYHtyfQ0KZGYxIDwtIGRhdGEuZnJhbWUobGQgPSAxOjIsIHZhbHVlID0gcm5vcm0oMikpDQpkZjIgPC0gZGF0YS5mcmFtZShsZCA9IDM6NCwgdmFsdWUgPSBydW5pZigyKSkNCmRmMyA8LSBkYXRhLmZyYW1lKGxkID0gMjIyLCB2YWx1ZSA9IDcpICANCmRvLmNhbGwocmJpbmQsIGxpc3QoZGYxLGRmMiwgZGYzKSkNCmBgYA0K0J3QviDQt9Cw0YfQtdC8INGC0L7Qs9C00LAg0LLQvtC+0LHRidC1IGRvLmNhbGwsINC10YHQu9C4INCyINGN0YLQvtC8INC/0YDQuNC80LXRgNC1INC80L7QttC90L4g0LHRi9C70L4g0L7QsdC+0LnRgtC40YHRjCDQsdC10Lcg0L3QtdCz0L4g0L/RgNC+0YHRgtC+INGC0L7Rh9C90L4g0YLQsNC6INC20LUg0YHQutC70LXQuNCyINC00LDRgtCwINGE0YDQtdC50LzRiyDRh9C10YDQtdC3IHJiaW5kLiAgDQrQpNGD0L3QutGG0LjRjyDQvdGD0LbQvdCwLCDQutC+0LPQtNCwINC80Ysg0L3QtSDQt9C90LDQtdC8INGB0LrQvtC70YzQutC+INC40LzQtdC90L3QviDQvtCx0YrQtdC60YLQvtCyINC90YPQttC90L4g0LHRg9C00LXRgiDRgdC60LvQtdC40YLRjCAgDQpgYGB7cn0NCmRvLmNhbGwocmJpbmQsbGFwcGx5KGxpc3QuZmlsZXMoKSwgZnVuY3Rpb24oZmlsZSkgcmVhZC5jc3YoZmlsZSkpKQ0KYGBgDQoNCiMjIyDQk9C70L7RgdCw0YDRgNC40LkgIA0KLSBTMywgUzQsIHJlZmVyZW5jZSBjbGFzcyAgDQotIGdlbmVyaWMgZnVuY3Rpb24gIA0KLSBjb3B5LW9uLW1vZGlmeSBzZW1hbnRpY3MgIA0KLSA/cmVwbGljYXRlICANCi0gP21hcHBseSAgDQotID9vdXRlciAgDQotIFZlY3Rvcml6ZSAgDQotIGRvLmNhbGwgIA0KICANCiAgICANCiMg0JzQvtC00LXQu9C40YDQvtCy0LDQvdC40LUg0YLRgNCw0LXQutGC0L7RgNC40Lgg0YHQu9GD0YfQsNC50L3QvtCz0L4g0L/RgNC+0YbQtdGB0YHQsA0K0KPQv9GA0LDQttC90LXQvdC40LUg0LzQvtC20LXRgiDQuNC80LXRgtGMINGA0LXQsNC70YzQvdC+0LUg0L/RgNC40LzQtdC90LXQvdC40LUg0LIg0YTQuNC30LjQutC1INGN0LvQtdC80LXQvdGC0LDRgNC90YvRhSDRh9Cw0YHRgtC40YYuDQoNCmBgYHtyfQ0KIyBSYW5kb20gd2FsayB3aXRoIGFic29ycHRpb24NCnNpbXVsYXRlX3dhbGsgPC0gZnVuY3Rpb24obG93ZXIgPSAtMTAsIHVwcGVyID0gMTAsIG5fbWF4ID0gMjAwLCBwID0gMWUtMykgew0KICAgIyDQvdCwINC60LDQttC00L7QvCDRiNCw0LPQtSDQtdGB0YLRjCDQv9C+0LfQuNGG0LjRjywg0L3QsNGH0LjQvdCw0LXQvCDQuNC3INGG0LXQvdGC0YDQsCDQuNC90YLQtdGA0LLQsNC70LANCiAgY3VycmVudF9wb3NpdGlvbiA8LSAobG93ZXIgKyB1cHBlcikgLyAyDQogIGZvciAoaSBpbiAxOm5fbWF4KSB7DQogICAgI9GB0L3QsNGH0LDQu9CwINC+0L/RgNC10LTQtdC70Y/QtdC8INCy0LXRgNC+0Y/RgtC90L7RgdGC0Ywg0L/QvtCz0LvQvtGJ0LXQvdC40Y8g0YfQsNGB0YLQuNGG0YsNCiAgICBpc19hYnNvcmJlZCA8LSByYmlub20oMSwgMSwgcCkNCiAgICAj0LXRgdC70Lgg0L/RgNC+0LjQt9C+0YjQu9C+IC0g0YHQuNC80YPQu9GP0YbQuNGPINC30LDQutCw0L3Rh9C40LLQsNC10YLRgdGPDQogICAgaWYgKGlzX2Fic29yYmVkKSByZXR1cm4obGlzdChzdGF0dXMgPSAiQWJzb3JiZWQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gY3VycmVudF9wb3NpdGlvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwcyA9IGkpKQ0KICAgICPQtdGB0LvQuCDQv9C+0LPQu9C+0YnQtdC90LjQtSDQvdC1INC/0YDQvtC40LfQvtGI0LvQviAtINGB0L7QstC10YDRiNCw0LXQvCAxINGB0LvRg9GH0LDQudC90YvQuSDRiNCw0LMNCiAgICBjdXJyZW50X3Bvc2l0aW9uIDwtIGN1cnJlbnRfcG9zaXRpb24gKyBybm9ybSgxKQ0KICAgIGlmIChjdXJyZW50X3Bvc2l0aW9uIDwgbG93ZXIpIHJldHVybihsaXN0KHN0YXR1cyA9ICJMZWZ0IGJyZWFjaCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gY3VycmVudF9wb3NpdGlvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcHMgPSBpKSkNCiAgICBpZiAoY3VycmVudF9wb3NpdGlvbiA+IHVwcGVyKSByZXR1cm4obGlzdChzdGF0dXMgPSAiUmlnaHQgYnJlYWNoIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSBjdXJyZW50X3Bvc2l0aW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwcyA9IGkpKQ0KICB9DQogICPQstC+0LfQstGA0LDRidCw0LXQvCDRgtCw0LrQvtC1INGB0L7QvtCx0YnQtdC90LjQtSDQv9GA0Lgg0LTQvtGB0YLQuNC20LXQvdC40Lgg0LzQsNC60YHQuNC80LDQu9GM0L3QvtCz0L4g0LrQvtC70LjRh9C10YHRgtCy0LAg0YjQsNCz0L7Qsg0KICByZXR1cm4obGlzdChzdGF0dXMgPSAiTWF4IHN0ZXBzIHJlYWNoZWQiLCANCiAgICAgICAgICAgICAgcG9zaXRpb24gPSBjdXJyZW50X3Bvc2l0aW9uLCPQv9C+0LfQuNGG0LjRjywg0L3QsCDQutC+0YLQvtGA0L7QuSDQt9Cw0LLQtdGA0YjQuNC70LDRgdGMINGB0LjQvNGD0LvRj9GG0LjRjw0KICAgICAgICAgICAgICBzdGVwcyA9IG5fbWF4KSkgI9C60L7Qu9C40YfQtdGB0YLQstC+INGI0LDQs9C+0LIsINC90LAg0LrQvtGC0L7RgNC+0Lkg0LfQsNCy0LXRgNGI0LjQu9Cw0YHRjCDRgdC40LzRg9C70Y/RhtC40Y8NCn0NCmBgYA0K0JXRgdGC0Ywg0L7QtNC90L7QvNC10YDQvdCw0Y8g0L7QsdC70LDRgdGC0Ywg0L7RgiAtMTAg0LTQviAxMCwg0LIg0LrQvtGC0L7RgNC+0Lkg0LzQvtC20LXRgiDQvdCw0YXQvtC00LjRgtGB0Y8g0Y3Qu9C10LzQtdC90YLQsNGA0L3QsNGPINGH0LDRgdGC0LjRhtCwLiDQp9Cw0YHRgtC40YbQsCDQvNC+0LbQtdGCINC/0YDRi9Cz0LDRgtGMINC70LjQsdC+INCy0L/RgNCw0LLQviwg0LvQuNCx0L4g0LLQu9C10LLQviDQuCDQvdCwINC60LDQttC00L7QvCDRiNCw0LPQtSDQtdGB0YLRjCDQstC10YDQvtGP0YLQvdC+0YHRgtGMINGC0L7Qs9C+LCDRh9GC0L4g0L7QvdCwINC/0L7Qs9C70L7RgtC40YLRgdGPIChwID0gMWUtMykuINCe0LHRidC10LUg0LrQvtC70LjRh9C10YHRgtCy0L4g0L/QtdGA0LXRhdC+0LTQvtCyINC+0LPRgNCw0L3QuNGH0LjQstCw0LXQvCAyMDAg0YjQsNCz0LDQvNC4IChuX21heCA9IDIwMCkuICAgICANCtCf0L7Rh9C10LzRgyDQuNGB0L/QvtC70YzQt9C+0LLQsNC70Lgg0YbQuNC60LsgKmZvciosINGF0L7RgtGPINC00LvRjyBSIC0g0Y3RgtC+INC+0LTQuNC9INC40Lcg0LzQtdC00LvQtdC90L3Ri9GFINGB0L/QvtGB0L7QsdC+0LIsINC6INC60L7RgtC+0YDQvtC80YMg0L3Rg9C20L3QviDQvtGC0L3QvtGB0LjRgtGB0Y8g0YEg0L7RgdGC0L7RgNC+0LbQvdC+0YHRgtGM0Y4uINCU0LXQu9C+INCyINGC0L7QvCwg0YfRgtC+INC90LAg0LrQsNC20LTQvtC8INC40Lcg0YjQsNCz0L7QsiDRgdC40LzRg9C70Y/RhtC40Y8g0LzQvtC20LXRgiDQsdGL0YLRjCDQv9GA0LXRgNCy0LDQvdCwINC4INC30LDQutC+0L3Rh9C40YLRgdGPIC0g0LrQsNC20LTQvtC1INC+0L/RgNC10LTQtdC70LXQvdC40LUg0L/QtdGA0LXRhdC+0LTQsCDRjdGC0L4g0LrQsNC60LDRjy3RgtC+INGB0LvQvtC20L3QsNGPINCy0YvRh9C40YHQu9C40YLQtdC70YzQvdCw0Y8g0LfQsNCw0LTQsNGH0LAuINCV0YHQu9C4INCz0LXQvdC10YDQuNGA0L7QstCw0YLRjCDRgtCw0LrRg9GOINGB0LjRgtGD0LDRhtC40Y4g0YfQtdGA0LXQtyDQstC10LrRgtC+0YDQuNC30LDRhtC40Y4sINGC0L4g0LzRiyDQs9C10L3QtdGA0LjRgNGD0LXQvCDRgdGA0LDQt9GDIDIwMCDQv9C10YDQtdGF0L7QtNC+0LIgKNC/0L7RgtC10L3RhtC40LDQu9GM0L3Ri9C5INC80LDQutGB0LjQvNGD0LwpLCDRgtC+0LPQtNCwINC60LDQuiDRhtC40LrQuyDQvNC+0LbQtdGCINC/0YDQtdGA0LLQsNGC0YzRgdGPINGD0LbQtSDQvdCwINC/0LXRgNCy0L7QuSDQuNGC0LXRgNCw0YbQuNC4INCyINGB0LvRg9GH0LDQtSDQv9C+0LPQu9C+0YnQtdC90LjRjyDRh9Cw0YHRgtC40YbRiy4g0KIu0LUuINCyINGC0LDQutC+0Lkg0YHQuNGC0YPQsNGG0LjQuCDRhtC40LrQuyBmb3Ig0L/QvtC30LLQvtC70Y/QtdGCINGB0Y3QutC+0L3QvtC80LjRgtGMINGC0LUg0L/QtdGA0LXRhdC+0LTRiywg0LrQvtGC0L7RgNGL0LUg0LLRi9C/0LDQtNCw0Y7RgiDQt9CwINCz0YDQsNC90LjRhtGLINGB0LjQvNGD0LvRj9GG0LjQuCAgIA0KICANCiAgICANCtCi0LXQv9C10YDRjCDQvNC+0LbQvdC+INGBINC/0L7QvNC+0YnRjNGOINGE0YPQvdC60YbQuNC4ICpyZXBsaWNhdGUqINC/0L7QstGC0L7RgNC40YLRjCBuLdC+0LUg0YfQuNGB0LvQviDRgtCw0LrQuNGFINGB0LjQvNGD0LvRj9GG0LjQuQ0KYGBge3J9DQojIFNpbXVsYXRlIHJlc3VsdHMNCnJlc3VsdCA8LSByZXBsaWNhdGUoMTAwMCwgc2ltdWxhdGVfd2FsaygpLCBzaW1wbGlmeSA9IEZBTFNFKQ0KcmVzdWx0IDwtIGRhdGEuZnJhbWUoDQogIHN0YXR1cyA9IHNhcHBseShyZXN1bHQsIGZ1bmN0aW9uKHgpIHgkc3RhdHVzKSwNCiAgcG9zaXRpb24gPSBzYXBwbHkocmVzdWx0LCBmdW5jdGlvbih4KSB4JHBvc2l0aW9uKSwNCiAgc3RlcHMgPSBzYXBwbHkocmVzdWx0LCBmdW5jdGlvbih4KSB4JHN0ZXBzKQ0KKQ0KYGBgDQrQn9GA0LjQstC+0LTQuNC8INGA0LXQt9GD0LvRjNGC0LDRgiDQsiDRgtC+0Lwg0LLQuNC00LUsINC60L7RgtC+0YDRi9C5INC90LDQvCDRg9C00L7QsdC10L0uINCSINC00LDQvdC90L7QuSDQutC+0L3QutGA0LXRgtC90L7QuSDRgdC40YLRg9Cw0YbQuNC4INC/0YDQtdCy0YDQsNGJ0LDQtdC8INC10LPQviDQsiDQtNCw0YLQsC3RhNGA0LXQudC8DQoNCg0K0JzQvtC20L3QviDQv9C+0YHQvNC+0YLRgNC10YLRjCwg0YfRgtC+INC/0YDQuNC80LXRgNC90L4g0L/QvtC70YPRh9C40LvQvtGB0YwNCmBgYHtyfQ0Kc3RyKHJlc3VsdCkNCmBgYA0KYGBge3J9DQpoZWFkKHJlc3VsdCkNCmBgYA0K0KLQtdC/0LXRgNGMLCDQutC+0LPQtNCwINGDINC90LDRgSDQtdGB0YLRjCDQutCw0LrQsNGPLdGC0L4g0YDQsNC30YPQvNC90LDRjyDRgdGC0LDRgtC40YHRgtC40LrQsCwg0YLQviDQvNC+0LbQvdC+INC10LUg0L7Qv9GA0LXQtNC10LvQtdC40YLRjCwg0L3QsNC/0YDQuNC80LXRgCwg0YEg0L/QvtC80L7RidGM0Y4g0YTRg9C90LrRhtC40LggKnRhcHBseSoNCmBgYHtyfQ0KdGFwcGx5KHJlc3VsdCRwb3NpdGlvbiwgcmVzdWx0JHN0YXR1cywgbGVuZ3RoKQ0KdGFwcGx5KHJlc3VsdCRzdGVwcywgcmVzdWx0JHN0YXR1cywgbWVhbikNCmBgYA0KDQrQkdC+0LvRjNGI0LjQuSDQuNC90YLQtdGA0LXRgSDQv9GA0LXQtNGB0YLQsNCy0LvRj9GO0YIg0LfQsNC00LDRh9C4INGBINCx0LvRg9C20LTQsNC90LjQtdC8INC/0L4g0L/Qu9C+0YHQutC+0YHRgtC4LCDRgtC+INC10YHRgtGMINCyINGA0LDQt9C80LXRgNC90L7RgdGC0LggMi4gIA0K0JLQvtC30YzQvNC40YLQtSDQvdCw0L/QuNGB0LDQvdC90YPRjiDQvNC90L7QuSDRhNGD0L3QutGG0LjRjiDQuCDQuNC30LzQtdC90LjRgtC1INC10ZEg0YLQsNC6LCDRh9GC0L7QsdGLINCx0LvRg9C20LTQsNC90LjQtSDQvdCw0YfQuNC90LDQu9C+0YHRjCDQsiDRhtC10L3RgtGA0LUg0LrQvtC+0YDQtNC40L3QsNGCICgwLCAwKSwg0LAg0LLRgdC1INC/0LXRgNC10YXQvtC00Ysg0L/QviDQutC+0L7RgNC00LjQvdCw0YLQsNC8IHgg0LggeSDQsdGL0LvQuCDQsdGLINC90LXQt9Cw0LLQuNGB0LjQvNGLINC4INC40LzQtdC70Lgg0YHRgtCw0L3QtNCw0YDRgtC90L7QtSDQvdC+0YDQvNCw0LvRjNC90L7QtSDRgNCw0YHQv9GA0LXQtNC10LvQtdC90LjQtS4g0JXRgdC70Lgg0LLQsNGBINC/0YPQs9Cw0Y7RgiDRjdGC0Lgg0YHQu9C+0LLQsCwg0YLQviDRjdGC0L4g0YLQviDQttC1INGB0LDQvNC+0LUsINGH0YLQviDQtNC10LvQsNC7INGPLCDRgtC+0LvRjNC60L4g0L7RgtC00LXQu9GM0L3QviDQv9C+IHgg0Lgg0L/QviB5LiAgDQogIA0KKiDQn9GA0L7RhtC10YHRgSDQvtCx0YDRi9Cy0LDQtdGC0YHRjyDQsiDQvNC+0LzQtdC90YIg0LLRi9GF0L7QtNCwINC30LAg0LPRgNCw0L3QuNGG0YMg0LrRgNGD0LPQsCDRgSDRhtC10L3RgtGA0L7QvCDQsiAoMCwgMCkg0Lgg0YDQsNC00LjRg9GB0L7QvCA2ICANCiog0JLQtdGA0L7Rj9GC0L3QvtGB0YLRjCDQv9C+0LPQu9C+0YnQtdC90LjRjyDQvdCwINC60LDQttC00L7QvCDRiNCw0LPQtSDRgNCw0LLQvdCwIDAuMDEgIA0KKiDQnNCw0LrRgdC40LzQsNC70YzQvdC+0LUg0LrQvtC70LjRh9C10YHRgtCy0L4g0YjQsNCz0L7QsiDigJQgMTAwICANCiog0KDQsNGB0YHRgtC+0Y/QvdC40LUsINGA0LDQt9GD0LzQtdC10YLRgdGPLCDQtdCy0LrQu9C40LTQvtCy0L46INGA0LDRgdGB0YLQvtGP0L3QuNC1INC+0YIg0YLQvtGH0LrQuCAoeCwgeSkg0LTQviAoMCwgMCkg0LXRgdGC0YwgXHNxcnR7eF4yICsgeV4yfSAgDQoqICDQntC00LjQvSDRiNCw0LMg0L/RgNC+0YbQtdGB0YHQsCDQv9C+0LTRgNCw0LfRg9C80LXQstCw0LXRgiDQuNC30LzQtdC90LXQvdC40LUg0L7QsdC10LjRhSDQutC+0L7RgNC00LjQvdCw0YIg0L7QtNC90L7QstGA0LXQvNC10L3QvdC+IQ0KICANCtCSINC+0YLQstC10YLQtSDRg9C60LDQttC40YLQtSDQstC10YDQvtGP0YLQvdC+0YHRgtGMINCy0YvQu9C10YLQsCDRh9Cw0YHRgtC40YbRiyDQsiDQv9GA0L7RhtC10L3RgtCw0YUsINGBINGC0L7Rh9C90L7RgdGC0YzRjiDQtNC+INGG0LXQu9GL0YUg0L/RgNC+0YbQtdC90YLQvtCyLCDQsiDQstC40LTQtSBYWCAo0L3QsNC/0YDQuNC80LXRgCwgMTQsINCx0LXQtyDRg9C60LDQt9Cw0L3QuNGPINC30L3QsNGH0LrQsCDQv9GA0L7RhtC10L3RgtC+0LIpLiDQktC10YDQvtGP0YLQvdC+0YHRgtGMINC00L7Qu9C20L3QsCDQv9C+0LvRg9GH0LjRgtGM0YHRjyDQsdC+0LvRjNGI0LUgNTAlLg0KDQpgYGB7cn0NCnNpbXVsYXRlX3dhbGsgPC0gZnVuY3Rpb24obl9tYXggPSAxMDAsIHAgPSAxZS0yKSB7DQogIHg8LTANCiAgeTwtMA0KICBjdXJyZW50X3Bvc2l0aW9uIDwtIDAgDQogIGZvciAoaSBpbiAxOm5fbWF4KSB7DQogICAgaXNfYWJzb3JiZWQgPC0gcmJpbm9tKDEsIDEsIHApDQogICAgaWYgKGlzX2Fic29yYmVkKSByZXR1cm4obGlzdChzdGF0dXMgPSAiQWJzb3JiZWQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gY3VycmVudF9wb3NpdGlvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdGVwcyA9IGkpKQ0KICAgIHg8LXgrcm5vcm0oMSkNCiAgICB5PC15K3Jub3JtKDEpDQogICAgY3VycmVudF9wb3NpdGlvbiA8LSBzcXJ0KHgqKjIgKyB5KioyKQ0KICAgIGlmIChjdXJyZW50X3Bvc2l0aW9uID4gNikgcmV0dXJuKGxpc3Qoc3RhdHVzID0gImJyZWFjaCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uID0gY3VycmVudF9wb3NpdGlvbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RlcHMgPSBpKSkNCiAgfQ0KICByZXR1cm4obGlzdChzdGF0dXMgPSAiTWF4IHN0ZXBzIHJlYWNoZWQiLCANCiAgICAgICAgICAgICAgcG9zaXRpb24gPSBjdXJyZW50X3Bvc2l0aW9uLA0KICAgICAgICAgICAgICBzdGVwcyA9IG5fbWF4KSkNCn0NCg0KYGBgDQoNCmBgYHtyfQ0KDQojIFNpbXVsYXRlIHJlc3VsdHMNCnJlc3VsdCA8LSByZXBsaWNhdGUoMTAwMDAwLCBzaW11bGF0ZV93YWxrKCksIHNpbXBsaWZ5ID0gRkFMU0UpDQpyZXN1bHQgPC0gZGF0YS5mcmFtZSgNCiAgc3RhdHVzID0gc2FwcGx5KHJlc3VsdCwgZnVuY3Rpb24oeCkgeCRzdGF0dXMpLA0KICBwb3NpdGlvbiA9IHNhcHBseShyZXN1bHQsIGZ1bmN0aW9uKHgpIHgkcG9zaXRpb24pLA0KICBzdGVwcyA9IHNhcHBseShyZXN1bHQsIGZ1bmN0aW9uKHgpIHgkc3RlcHMpDQopDQoNCmBgYA0KDQpgYGB7cn0NCiMgSW5zcGVjdCByZXN1bHRzDQp0YXBwbHkocmVzdWx0JHBvc2l0aW9uLCByZXN1bHQkc3RhdHVzLCBsZW5ndGgpDQp0YXBwbHkocmVzdWx0JHN0ZXBzLCByZXN1bHQkc3RhdHVzLCBtZWFuKQ0KYGBgDQpgYGB7cn0NCmEgPC0gODA5OTcvKDE4OTM0ICsgODA5OTcpKjEwMA0KYQ0KYGBgDQoNCtCe0LHRidC40Lkg0LPQu9C+0YHRgdCw0YDQuNC5INC00LvRjyDRjdGC0L7Qs9C+INGD0YDQvtC60LA6ICANCg0KUzMsIFM0LCByZWZlcmVuY2UgY2xhc3NlcyAgDQpHZW5lcmljIGZ1bmN0aW9uICANCkNvcHktb24tbW9kaWZ5IHNlbWFudGljcyAgDQo/cmVwbGljYXRlICANCj9tYXBwbHkgIA0KP291dGVyICANCj9WZWN0b3JpemUgIA0KP2RvLmNhbGwgIA==