Modelele de tip prada-pradator modeleaza interactiunea dintre doua specii, una din ele reprezentand pradatorul, cel care beneficiaza de pe urma interactiunii, iar cealalta specie fiind prada, cea care pierde de pe urma interactiunii. Acest tip de modele au un rol important in ecologie si prezinta dinamica populatiilor din ecosisteme.

Modelul clasic Lotka-Volterra

Cel mai simplu model pentru interactiunea prada-pradator este cel propus de Lotka si Volterra si este definiti de 2 ecuatii diferentiale ordinare:

Dinamica populatiei speciei de prada
Dinamica populatiei speciei paradatoare

unde:

Pentru a intelege ce semnifica cele 2 ecuatii, le vom lua pe fiecare pe rand pentru a le analiza.

Ecuatia evolutiei populatiei prada

  • αx reprezinta functia de crestere a populatiei de prada in absenta pradatorilor. Astfel Δx = αx defineste o functie exponentiala de crestere, unde rata de crestere a populatiei este direct proportionala cu numarul de indivizi care exista in momentul respectiv.

    Astfel populatia de prada se presupune ca va creste nelimitat, intr-o maniera exponentiala in lipsa pradatorilor. De altfel, aceasta presupunere este naiva, iar vom incerca imbunatatirea modelului mai tarziu, folosind o functie diferita de crestere.

  • -βxy reprezinta functia raspuns la activitatea de vanatoare a pradatorilor asupra populatiei de prada. Este o functie ce depinde atat de numarul de pradatori, cat si de numarul de prazi care exista in momentul respectiv. Are semn negativ deoarece populatia de prada are de pierdut in urma interactiunii cu pradatorii.

    Astfel, activitatea de vanatoare a pradatorilor limiteaza si induce un trend descrescator in populatia prazii.

Ecuatia evolutiei populatiei pradator

  • -γy reprezinta functia de scadere a populatiei pradatorilor in absenta prazii. Astfel Δy = -γy defineste o functie exponentiala de scadere, unde rata de scadere a populatiei este direct proportionala cu numarul de pradatori existenti in acel moment.

    Aceasta scadere in randul pradatorilor simbolizeaza faptul ca, in lipsa unei prazi pe care o pot vana, pradatorii vor muri intr-un ritm exponential deoarece nu vor avea ce manca.

  • δxy reprezinta functia raspuns la activitatea de vanatoare a pradatorilor asupra populatiei lor. Functia depinde atat de numarul de pradatori, cat si de numarul de prazi care exista in momentul respectiv. Semnul pozitiv al functiei simbolizeaza o crestere a numarului de pradatorii in urma interactiunii cu prada.

    Astfel, doar in urma actiunii de vanatoare, populatia pradatorilor poate sa creasca

Implementarea modelului

Definim in R modelul Lotka-Volterra

lv_model <- function(t, state, params) {
  with(as.list(c(state, params)), {
    d_prey <- alpha * prey - beta * prey * predator
    d_predator <- delta * prey * predator - gamma * predator
    return(list(c(prey = d_prey, predator = d_predator)))
  })
}

Dam valori parametrilor ce caracterizeaza modelul si un numar initial al populatiei prada, respectiv pradator si putem rezolva pe un interval de timp sistemul ordinal de ecuatii diferentiale utilizand functia ode din bilbioteca deSolve.

library(deSolve)

parameters <- c(alpha = 0.2, beta = 0.01, delta = 0.005, gamma = 0.1)
init <- c(prey = 10, predator = 5)
times <- seq(0, 150, by=1)

lv_results <- ode(init, times, lv_model, parameters)

Dinamica populatiilor in timp se poate observa in graficul rezultat.

param_names = paste(names(parameters), parameters, sep = " = ", collapse = "; ")
title = "Model Lotka-Volterra, dinamica in timp a populatiilor"
plot(lv_results[,1], lv_results[,2], main=title, sub=param_names,col="red",type="l", lwd=2, xlab="Timp",ylab="Populatie")
points(lv_results[,1], lv_results[,3], col="blue",type="l",lwd=2, lty=2)
legend("topright",lwd=c(2,2),lty=c(1,2),col=c("red","blue"),legend=c("prada","pradator"),bty="n")

param_names = paste(names(parameters), parameters, sep = " = ", collapse = "; ")
title = "Model Lotka-Volterra, diagrama de faza a populatiilor"
plot(lv_results[,2], lv_results[,3], main=title, sub=param_names, col="#f1a340", type="l", lwd=2, xlab="Prada", ylab="Pradator")
points(lv_results[1,2], lv_results[1,3], col="#f1a340", pch=20, cex=3)

Diagrama de faza a populatiilor ne arata ca exista o relatie periodica intre populatiile de prada si pradator. De altfel, daca se iau Δx si Δy ca 0 in cele 2 ecuatii diferentiale, se pot calcula starile de echilibru ale sistemului. Astfel exista 2 stari de echilibru:

  • (0, 0), ceea ce reprezinta starea in care nu exista nici un individ, fie prada sau pradator, si s-ar intelege de ce e o stare de echilibru
  • (α/β, γ/δ), este punctul in care dezavantajul interactiunii cu pradatorul compenseaza exact cresterea naturala a populatiei prazii, respectiv avantajul obtinut din interactiunea cu prada compenseaza exact scaderea naturala a populatiei pradatorilor. Pentru modelul nostru parametrizat cu α=0.2, β=0.01, γ=0.1, δ=0.005 acest punct este (20, 20)

Punctele de echilibru se afla la intersectia celor 2 linii unde ecuatiile diferentiale din sistem se anuleaza (nullcline). Putem observa ca pentru acelasi model, indiferent de populatiile initiale, cele 2 populatii vor oscila periodic in jurul punctului de echilibru (α/β, γ/δ).

library(phaseR)

param_names = paste(names(parameters), parameters, sep = " = ", collapse = "; ")
title = "Model Lotka-Volterra, diagrama de faza a populatiilor"
xlim = c(0, 120)
ylim = c(0, 80)

plot(20, 20, main=title, sub=param_names, xlim=xlim, ylim=ylim, pch=20, xlab="Prada", ylab="Pradator")

invisible(nullclines(lv_model, xlim=xlim, ylim=ylim, parameters = parameters, col=c("red", "blue"), lty=5, system="two.dim", state.names=c("prey", "predator")))
invisible(flowField(lv_model, xlim=xlim, ylim=ylim, parameters = parameters, system="two.dim", state.names=c("prey", "predator")))


inits <- rbind(c(3,3), c(15, 18), c(30, 40), c(55, 45))
for(i in 1:nrow(inits)) {
  init <- c(prey = inits[i, 1], predator = inits[i, 2])
  lvr <- ode(init, times, lv_model, parameters)
  
  lines(lvr[,2], lvr[,3], main=title, sub=param_names, col="#f1a340", type="l", lwd=2, xlab="Prada", ylab="Pradator")
  points(lvr[1,2], lvr[1,3], col="#f1a340", pch=20, cex=3)
}

Imbunatatiri ale modelului clasic

Din multe privinte modelul clasic prada-pradator Lotvka-Volterra este naiv si prezinta lucrurile intr-o maniera mult simplificata. De altfel, el functioneaza pe baza urmatoarelor presupuneri:

1. Introducem capacitate maxima pentru populatia de prada

Folosind o functie de crestere logistica, in loc de cea exponentiala, putem limita populatia de prada la o capacitate maxima. Acesta este un scenariu mult mai realist, pentru ca in lumea reala, chiar si in lipsa pradatorilor, exista constrangeri legate de resursele existente.

Modelul logistic de crestere unde K reprezinta capacitatea maxima.

Ecuatiile modelului vor arata astfel acum:

Definim modelul in R:

lv_improved_1 <- function(t, state, params) {
  with(as.list(c(state, params)), {
    d_prey <- alpha * prey * (1 - prey / kappa) - beta * prey * predator
    d_predator <- delta * prey * predator - gamma * predator
    return(list(c(prey = d_prey, predator = d_predator)))
  })
}

Simularea modelului:

parameters_1 <- c(alpha = 0.6, beta = 0.01, delta = 0.005, gamma = 0.1, kappa = 250)
init_1 <- c(prey = 10, predator = 5)
times_1 <- seq(0, 150, by=1)

lv_improved_1_results <- ode(init_1, times_1, lv_improved_1, parameters_1)
param_names = paste(names(parameters_1), parameters_1, sep = " = ", collapse = "; ")
title = "Model prada-pradator imbunatatit 1, dinamica in timp a populatiilor"
plot(lv_improved_1_results[,1], lv_improved_1_results[,2], main=title, sub=param_names,col="red",type="l", lwd=2, xlab="Timp",ylab="Populatie")
points(lv_improved_1_results[,1], lv_improved_1_results[,3], col="blue",type="l",lwd=2, lty=2)
legend("topright",lwd=c(2,2),lty=c(1,2),col=c("red","blue"),legend=c("prada","pradator"),bty="n")

In graficul ce reprezinta dinamica in timp a populatiilor se observa cum oscilatiile se reduc si tind spre o stare de echilibru.


param_names = paste(names(parameters_1), parameters_1, sep = " = ", collapse = "; ")
title = "Model prada-pradator imbunatatit 1, diagrama de faza a populatiilor"
xlim = c(0, 160)
ylim = c(0, 130)

plot(20, 55.2, main=title, sub=param_names, xlim=xlim, ylim=ylim, pch=20, xlab="Prada", ylab="Pradator")

invisible(nullclines(lv_improved_1, xlim=xlim, ylim=ylim, parameters = parameters_1, col=c("red", "blue"), lty=5, system="two.dim", state.names=c("prey", "predator")))
invisible(flowField(lv_improved_1, xlim=xlim, ylim=ylim, parameters = parameters_1, system="two.dim", state.names=c("prey", "predator")))

colors <- c("#f1a340",  "#998ec3")
inits_1 <- rbind(c(3,3), c(95, 18))
for(i in 1:nrow(inits_1)) {
  init <- c(prey = inits_1[i, 1], predator = inits_1[i, 2])
  lvr <- ode(init, times_1, lv_improved_1, parameters_1)
  
  lines(lvr[,2], lvr[,3], main=title, sub=param_names, col=colors[i], type="l", lwd=2, xlab="Prada", ylab="Pradator")
  points(lvr[1,2], lvr[1,3], col=colors[i], pch=20, cex=3)
}

Diagrama de faza a populatiilor reflecta faptul ca oscilatiile populatiilor nu sunt periodice, iar acestea tind spre unul din punctele de echilibru unde atat pradatorii, cat si prazile se afla in numar egal.

Pentru modelul acesta se pot calcula 3 stari de echilibru: - (0,0) - (γ/δ, α(δK-γ)/(βδK)) care la nivelul diagramei de mai sus reprezinta punctul (20, 55.2) - (K, 0) poate cel mai interesat care da de inteles ca exista o stare in care pradatorii au disparut si mai exista doar populatie prada

2. Schimbam functia de raspuns la interactiunea prada-pradator

In modelul actual se presupune ca numarul de prazi consumate de catre pradatori creste intr-o maniera lineara pe masura ce populatia de prazi creste. Presupunerea acesata este naiva pentru ca inseamna ca pradatorii nu se satura niciodata. Acest tip de functie de raspuns are tipul I.Pe langa aceasta, voi prezenta si functia de raspuns de tipul II si III.

type1F <- function(x, beta) {
  x*beta
}

type2F <- function(x, beta, h) {
  (beta*x)/(1+beta*h*x)
}

type3F <- function(x, beta, h) {
  (beta*x*x)/(1+beta*h*x*x)
}


Tipul I: unde:

curve(type1F(x, 0.1), 0, 1000, main="Functie raspuns la interactiunea prada-pradator de tip I", col="red", type="l", lwd=2, xlab="Populatie prada",ylab="Indivizi prada mancati / pradator")


Tipul II: unde:

Acest raspuns tine cont de satietatea pradatorului. Numarul de prazi consumate de pradator creste proportional cu populatia prazilor, dar pana la un punct.

curve(type2F(x, 0.1, 0.1), 0, 1000, main="Functie raspuns la interactiunea prada-pradator de tip II", col="red", type="l", lwd=2, xlab="Populatie prada",ylab="Indivizi prada mancati / pradator")


Tipul III: unde:

In plus fata de raspunsul de tip II, pradatorul isi formeaza o imagine a prazii pe masura prezentei acesteia. Astfel, daca populatia prada este redusa, pradatorul nu o va cunoaste si nu o va cauta, dar pe masura ce apar tot mai multe prazii, se va familiariza cu aceasta.

curve(type3F(x, 0.0001, 0.1), 0, 1000, main="Functie raspuns la interactiunea prada-pradator de tip III", col="red", type="l", lwd=2, xlab="Populatie prada",ylab="Indivizi prada mancati / pradator")

Folosind functia raspuns de tip III, modelul imbunatatit va fi:

lv_improved_2 <- function(t, state, params) {
  with(as.list(c(state, params)), {
    d_prey <- alpha * prey * (1 - prey / kappa) - type3F(prey, beta, h) * predator
    d_predator <- type3F(prey, beta, h) * predator - gamma * predator
    return(list(c(prey = d_prey, predator = d_predator)))
  })
}
parameters_2 <- c(alpha = 0.1, beta = 0.0012, delta = 0.05, gamma = 0.9, kappa = 250, h = 0.1)
init_2 <- c(prey = 10, predator = 5)
times_2 <- seq(0, 150, by=1)

lv_improved_2_results <- ode(init_2, times_2, lv_improved_2, parameters_2)
param_names = paste(names(parameters_2), parameters_2, sep = " = ", collapse = "; ")
title = "Model prada-pradator imbunatatit 2, dinamica in timp a populatiilor"
plot(lv_improved_2_results[,1], lv_improved_2_results[,2], main=title, sub=param_names,col="red",type="l", lwd=2, xlab="Timp",ylab="Populatie", ylim=c(0, 50))
points(lv_improved_2_results[,1], lv_improved_2_results[,3], col="blue",type="l",lwd=2, lty=2)
legend("topright",lwd=c(2,2),lty=c(1,2),col=c("red","blue"),legend=c("prada","pradator"),bty="n")

param_names = paste(names(parameters_2), parameters_2, sep = " = ", collapse = "; ")
title = "Model prada-pradator imbunatatit 2, diagrama de faza a populatiilor"
plot(lv_improved_2_results[,2], lv_improved_2_results[,3], main=title, sub=param_names, col="#f1a340", type="l", lwd=2, xlab="Prada", ylab="Pradator")
points(lv_improved_2_results[1,2], lv_improved_2_results[1,3], col="#f1a340", pch=20, cex=3)

Bibliografie

LS0tDQp0aXRsZTogIk1vZGVsZSBQcmFkYSAtIFByYWRhdG9yIg0KYXV0aG9yOiAiRGFuaWVsIEluY2ljYXUiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmZvbnRzaXplOiAxMnB0DQotLS0NCg0KTW9kZWxlbGUgZGUgdGlwIHByYWRhLXByYWRhdG9yIG1vZGVsZWF6YSBpbnRlcmFjdGl1bmVhIGRpbnRyZSBkb3VhIHNwZWNpaSwgdW5hIGRpbiBlbGUgcmVwcmV6ZW50YW5kIHByYWRhdG9ydWwsIGNlbCBjYXJlIGJlbmVmaWNpYXphIGRlIHBlIHVybWEgaW50ZXJhY3RpdW5paSwgaWFyIGNlYWxhbHRhIHNwZWNpZSBmaWluZCBwcmFkYSwgY2VhIGNhcmUgcGllcmRlIGRlIHBlIHVybWEgaW50ZXJhY3RpdW5paS4NCkFjZXN0IHRpcCBkZSBtb2RlbGUgYXUgdW4gcm9sIGltcG9ydGFudCBpbiBlY29sb2dpZSBzaSBwcmV6aW50YSBkaW5hbWljYSBwb3B1bGF0aWlsb3IgZGluIGVjb3Npc3RlbWUuDQoNCiMjIyAqKk1vZGVsdWwgY2xhc2ljIExvdGthLVZvbHRlcnJhKioNCg0KQ2VsIG1haSBzaW1wbHUgbW9kZWwgcGVudHJ1IGludGVyYWN0aXVuZWEgcHJhZGEtcHJhZGF0b3IgZXN0ZSBjZWwgcHJvcHVzIGRlIExvdGthIHNpIFZvbHRlcnJhIHNpIGVzdGUgZGVmaW5pdGkgZGUgMiBlY3VhdGlpIGRpZmVyZW50aWFsZSBvcmRpbmFyZToNCg0KPGNlbnRlcj4hW0RpbmFtaWNhIHBvcHVsYXRpZWkgc3BlY2llaSBkZSBwcmFkYV0oaHR0cHM6Ly93aWtpbWVkaWEub3JnL2FwaS9yZXN0X3YxL21lZGlhL21hdGgvcmVuZGVyL3N2Zy83NmI4MjlkMmFhZmM2ZTI5NGRmZjZjNWVkNzU3Y2RjYTQ0NjRhODI2KTwvY2VudGVyPg0KDQo8Y2VudGVyPiFbRGluYW1pY2EgcG9wdWxhdGllaSBzcGVjaWVpIHBhcmFkYXRvYXJlXShodHRwczovL3dpa2ltZWRpYS5vcmcvYXBpL3Jlc3RfdjEvbWVkaWEvbWF0aC9yZW5kZXIvc3ZnLzlhYjE0Y2E3ZjI1MmMxOTViZWM5ZjdkNzEzYzEwZjFjYzFiZTFlZDgpPC9jZW50ZXI+DQoNCnVuZGU6DQoNCiAgLSAqKngqKiByZXByZXppbnRhIG51bWFydWwgZGUgaW5kaXZpemkgY2UgcmVwcmV6aW50YSBwcmFkYQ0KICAtICoqeSoqIHJlcHJlemludGEgbnVtYXJ1bCBkZSBpbmRpdml6aSBjZSByZXByZXppbnRhIHByYWRhdG9yaWkNCiAgLSAqKnQqKiByZXByZXppbnRhIHRpbXB1bA0KICAtICoqzrEsIM6yLCDOtCwgzrMqKiByZXByZXppbnRhIHBhcmFtZXRyaWkgc2lzdGVtdWx1aSBjZSBkZXNjcml1IGludGVyYWN0aXVuZWEgZGludHJlIGNlbGUgMiBzcGVjaWkgKG51bWVyZSByZWFsZSBwb3ppdGl2ZSkNCiAgLSAqKiFbXShodHRwczovL3dpa2ltZWRpYS5vcmcvYXBpL3Jlc3RfdjEvbWVkaWEvbWF0aC9yZW5kZXIvc3ZnLzQ4MWE2YjgwZThlYWQyYWM3YzQ3NzNmNGIwNDg0YjJlZmU2ZWZlMjgpKiogc2kgKiohW10oaHR0cHM6Ly93aWtpbWVkaWEub3JnL2FwaS9yZXN0X3YxL21lZGlhL21hdGgvcmVuZGVyL3N2Zy83NjJkODg0MzU3NTI1YjQ0ZTAxM2QxMDU3ZjQzY2Q1ZWQzY2YyYjZiKSoqIHJlcHJlemludGEgcmF0ZWxlIGRlIG1vZGlmaWNhcmUgKGNyZXN0ZXJlIHNhdSBzY2FkZXJlKSBpbnN0YXRhbmVlIGFsZSBjZWxvciAyIHBvcHVsYXRpaQ0KICANClBlbnRydSBhIGludGVsZWdlIGNlIHNlbW5pZmljYSBjZWxlIDIgZWN1YXRpaSwgbGUgdm9tIGx1YSBwZSBmaWVjYXJlIHBlIHJhbmQgcGVudHJ1IGEgbGUgYW5hbGl6YS4NCg0KDQojIyMjICoqRWN1YXRpYSBldm9sdXRpZWkgcG9wdWxhdGllaSBwcmFkYSoqDQohW10oaHR0cHM6Ly93aWtpbWVkaWEub3JnL2FwaS9yZXN0X3YxL21lZGlhL21hdGgvcmVuZGVyL3N2Zy83NmI4MjlkMmFhZmM2ZTI5NGRmZjZjNWVkNzU3Y2RjYTQ0NjRhODI2KQ0KDQotICAgKirOsXgqKiByZXByZXppbnRhIGZ1bmN0aWEgZGUgY3Jlc3RlcmUgYSBwb3B1bGF0aWVpIGRlIHByYWRhIGluIGFic2VudGEgcHJhZGF0b3JpbG9yLiBBc3RmZWwgKirOlHggPSDOsXgqKiBkZWZpbmVzdGUgbyBmdW5jdGllIGV4cG9uZW50aWFsYSBkZSBjcmVzdGVyZSwgdW5kZSByYXRhIGRlIGNyZXN0ZXJlIGEgcG9wdWxhdGllaSBlc3RlIGRpcmVjdCBwcm9wb3J0aW9uYWxhIGN1IG51bWFydWwgZGUgaW5kaXZpemkgY2FyZSBleGlzdGEgaW4gbW9tZW50dWwgcmVzcGVjdGl2Lg0KDQogICAgQXN0ZmVsIHBvcHVsYXRpYSBkZSBwcmFkYSBzZSBwcmVzdXB1bmUgY2EgdmEgY3Jlc3RlIG5lbGltaXRhdCwgaW50ci1vIG1hbmllcmEgZXhwb25lbnRpYWxhIGluIGxpcHNhIHByYWRhdG9yaWxvci4gRGUgYWx0ZmVsLCBhY2Vhc3RhIHByZXN1cHVuZXJlIGVzdGUgbmFpdmEsIGlhciB2b20gaW5jZXJjYSBpbWJ1bmF0YXRpcmVhIG1vZGVsdWx1aSBtYWkgdGFyeml1LCBmb2xvc2luZCBvIGZ1bmN0aWUgZGlmZXJpdGEgZGUgY3Jlc3RlcmUuDQogICAgDQotICAgKiotzrJ4eSoqIHJlcHJlemludGEgZnVuY3RpYSByYXNwdW5zIGxhIGFjdGl2aXRhdGVhIGRlIHZhbmF0b2FyZSBhIHByYWRhdG9yaWxvciBhc3VwcmEgcG9wdWxhdGllaSBkZSBwcmFkYS4gRXN0ZSBvIGZ1bmN0aWUgY2UgZGVwaW5kZSBhdGF0IGRlIG51bWFydWwgZGUgcHJhZGF0b3JpLCBjYXQgc2kgZGUgbnVtYXJ1bCBkZSBwcmF6aSBjYXJlIGV4aXN0YSBpbiBtb21lbnR1bCByZXNwZWN0aXYuIEFyZSBzZW1uIG5lZ2F0aXYgZGVvYXJlY2UgcG9wdWxhdGlhIGRlIHByYWRhIGFyZSBkZSBwaWVyZHV0IGluIHVybWEgaW50ZXJhY3RpdW5paSBjdSBwcmFkYXRvcmlpLg0KICANCiAgICBBc3RmZWwsIGFjdGl2aXRhdGVhIGRlIHZhbmF0b2FyZSBhIHByYWRhdG9yaWxvciBsaW1pdGVhemEgc2kgaW5kdWNlIHVuIHRyZW5kIGRlc2NyZXNjYXRvciBpbiBwb3B1bGF0aWEgcHJhemlpLg0KDQojIyMjICoqRWN1YXRpYSBldm9sdXRpZWkgcG9wdWxhdGllaSBwcmFkYXRvcioqDQohW10oaHR0cHM6Ly93aWtpbWVkaWEub3JnL2FwaS9yZXN0X3YxL21lZGlhL21hdGgvcmVuZGVyL3N2Zy85YWIxNGNhN2YyNTJjMTk1YmVjOWY3ZDcxM2MxMGYxY2MxYmUxZWQ4KQ0KDQotICAgKiotzrN5KiogcmVwcmV6aW50YSBmdW5jdGlhIGRlIHNjYWRlcmUgYSBwb3B1bGF0aWVpIHByYWRhdG9yaWxvciBpbiBhYnNlbnRhIHByYXppaS4gQXN0ZmVsICoqzpR5ID0gLc6zeSoqIGRlZmluZXN0ZSBvIGZ1bmN0aWUgZXhwb25lbnRpYWxhIGRlIHNjYWRlcmUsIHVuZGUgcmF0YSBkZSBzY2FkZXJlIGEgcG9wdWxhdGllaSBlc3RlIGRpcmVjdCBwcm9wb3J0aW9uYWxhIGN1IG51bWFydWwgZGUgcHJhZGF0b3JpIGV4aXN0ZW50aSBpbiBhY2VsIG1vbWVudC4NCg0KICAgIEFjZWFzdGEgc2NhZGVyZSBpbiByYW5kdWwgcHJhZGF0b3JpbG9yIHNpbWJvbGl6ZWF6YSBmYXB0dWwgY2EsIGluIGxpcHNhIHVuZWkgcHJhemkgcGUgY2FyZSBvIHBvdCB2YW5hLCBwcmFkYXRvcmlpIHZvciBtdXJpIGludHItdW4gcml0bSBleHBvbmVudGlhbCBkZW9hcmVjZSBudSB2b3IgYXZlYSBjZSBtYW5jYS4NCiAgICANCi0gICAqKs60eHkqKiByZXByZXppbnRhIGZ1bmN0aWEgcmFzcHVucyBsYSBhY3Rpdml0YXRlYSBkZSB2YW5hdG9hcmUgYSBwcmFkYXRvcmlsb3IgYXN1cHJhIHBvcHVsYXRpZWkgbG9yLiBGdW5jdGlhIGRlcGluZGUgYXRhdCBkZSBudW1hcnVsIGRlIHByYWRhdG9yaSwgY2F0IHNpIGRlIG51bWFydWwgZGUgcHJhemkgY2FyZSBleGlzdGEgaW4gbW9tZW50dWwgcmVzcGVjdGl2LiBTZW1udWwgcG96aXRpdiBhbCBmdW5jdGllaSBzaW1ib2xpemVhemEgbyBjcmVzdGVyZSBhIG51bWFydWx1aSBkZSBwcmFkYXRvcmlpIGluIHVybWEgaW50ZXJhY3RpdW5paSBjdSBwcmFkYS4NCg0KICAgIEFzdGZlbCwgZG9hciBpbiB1cm1hIGFjdGl1bmlpIGRlIHZhbmF0b2FyZSwgcG9wdWxhdGlhIHByYWRhdG9yaWxvciBwb2F0ZSBzYSBjcmVhc2NhDQoNCiMjIyMgKipJbXBsZW1lbnRhcmVhIG1vZGVsdWx1aSoqDQoNCkRlZmluaW0gaW4gUiBtb2RlbHVsIExvdGthLVZvbHRlcnJhDQoNCmBgYHtyfQ0KbHZfbW9kZWwgPC0gZnVuY3Rpb24odCwgc3RhdGUsIHBhcmFtcykgew0KICB3aXRoKGFzLmxpc3QoYyhzdGF0ZSwgcGFyYW1zKSksIHsNCiAgICBkX3ByZXkgPC0gYWxwaGEgKiBwcmV5IC0gYmV0YSAqIHByZXkgKiBwcmVkYXRvcg0KICAgIGRfcHJlZGF0b3IgPC0gZGVsdGEgKiBwcmV5ICogcHJlZGF0b3IgLSBnYW1tYSAqIHByZWRhdG9yDQogICAgcmV0dXJuKGxpc3QoYyhwcmV5ID0gZF9wcmV5LCBwcmVkYXRvciA9IGRfcHJlZGF0b3IpKSkNCiAgfSkNCn0NCmBgYA0KDQpEYW0gdmFsb3JpIHBhcmFtZXRyaWxvciBjZSBjYXJhY3Rlcml6ZWF6YSBtb2RlbHVsIHNpIHVuIG51bWFyIGluaXRpYWwgYWwgcG9wdWxhdGllaSBwcmFkYSwgcmVzcGVjdGl2IHByYWRhdG9yIHNpIHB1dGVtIHJlem9sdmEgcGUgdW4gaW50ZXJ2YWwgZGUgdGltcCBzaXN0ZW11bCBvcmRpbmFsIGRlIGVjdWF0aWkgZGlmZXJlbnRpYWxlIHV0aWxpemFuZCBmdW5jdGlhIGBvZGVgIGRpbiBiaWxiaW90ZWNhIGBkZVNvbHZlYC4gDQoNCmBgYHtyfQ0KbGlicmFyeShkZVNvbHZlKQ0KDQpwYXJhbWV0ZXJzIDwtIGMoYWxwaGEgPSAwLjIsIGJldGEgPSAwLjAxLCBkZWx0YSA9IDAuMDA1LCBnYW1tYSA9IDAuMSkNCmluaXQgPC0gYyhwcmV5ID0gMTAsIHByZWRhdG9yID0gNSkNCnRpbWVzIDwtIHNlcSgwLCAxNTAsIGJ5PTEpDQoNCmx2X3Jlc3VsdHMgPC0gb2RlKGluaXQsIHRpbWVzLCBsdl9tb2RlbCwgcGFyYW1ldGVycykNCg0KYGBgDQoNCkRpbmFtaWNhIHBvcHVsYXRpaWxvciBpbiB0aW1wIHNlIHBvYXRlIG9ic2VydmEgaW4gZ3JhZmljdWwgcmV6dWx0YXQuDQoNCmBgYHtyfQ0KcGFyYW1fbmFtZXMgPSBwYXN0ZShuYW1lcyhwYXJhbWV0ZXJzKSwgcGFyYW1ldGVycywgc2VwID0gIiA9ICIsIGNvbGxhcHNlID0gIjsgIikNCnRpdGxlID0gIk1vZGVsIExvdGthLVZvbHRlcnJhLCBkaW5hbWljYSBpbiB0aW1wIGEgcG9wdWxhdGlpbG9yIg0KcGxvdChsdl9yZXN1bHRzWywxXSwgbHZfcmVzdWx0c1ssMl0sIG1haW49dGl0bGUsIHN1Yj1wYXJhbV9uYW1lcyxjb2w9InJlZCIsdHlwZT0ibCIsIGx3ZD0yLCB4bGFiPSJUaW1wIix5bGFiPSJQb3B1bGF0aWUiKQ0KcG9pbnRzKGx2X3Jlc3VsdHNbLDFdLCBsdl9yZXN1bHRzWywzXSwgY29sPSJibHVlIix0eXBlPSJsIixsd2Q9MiwgbHR5PTIpDQpsZWdlbmQoInRvcHJpZ2h0Iixsd2Q9YygyLDIpLGx0eT1jKDEsMiksY29sPWMoInJlZCIsImJsdWUiKSxsZWdlbmQ9YygicHJhZGEiLCJwcmFkYXRvciIpLGJ0eT0ibiIpDQoNCmBgYA0KDQpgYGB7cn0NCnBhcmFtX25hbWVzID0gcGFzdGUobmFtZXMocGFyYW1ldGVycyksIHBhcmFtZXRlcnMsIHNlcCA9ICIgPSAiLCBjb2xsYXBzZSA9ICI7ICIpDQp0aXRsZSA9ICJNb2RlbCBMb3RrYS1Wb2x0ZXJyYSwgZGlhZ3JhbWEgZGUgZmF6YSBhIHBvcHVsYXRpaWxvciINCnBsb3QobHZfcmVzdWx0c1ssMl0sIGx2X3Jlc3VsdHNbLDNdLCBtYWluPXRpdGxlLCBzdWI9cGFyYW1fbmFtZXMsIGNvbD0iI2YxYTM0MCIsIHR5cGU9ImwiLCBsd2Q9MiwgeGxhYj0iUHJhZGEiLCB5bGFiPSJQcmFkYXRvciIpDQpwb2ludHMobHZfcmVzdWx0c1sxLDJdLCBsdl9yZXN1bHRzWzEsM10sIGNvbD0iI2YxYTM0MCIsIHBjaD0yMCwgY2V4PTMpDQoNCmBgYA0KRGlhZ3JhbWEgZGUgZmF6YSBhIHBvcHVsYXRpaWxvciBuZSBhcmF0YSBjYSBleGlzdGEgbyByZWxhdGllIHBlcmlvZGljYSBpbnRyZSBwb3B1bGF0aWlsZSBkZSBwcmFkYSBzaSBwcmFkYXRvci4gRGUgYWx0ZmVsLCBkYWNhIHNlIGlhdSAqKs6UeCoqIHNpICoqzpR5KiogY2EgMCBpbiBjZWxlIDIgZWN1YXRpaSBkaWZlcmVudGlhbGUsIHNlIHBvdCBjYWxjdWxhIHN0YXJpbGUgZGUgZWNoaWxpYnJ1IGFsZSBzaXN0ZW11bHVpLiBBc3RmZWwgZXhpc3RhIDIgc3RhcmkgZGUgZWNoaWxpYnJ1Og0KDQotICAgKiooMCwgMCkqKiwgY2VlYSBjZSByZXByZXppbnRhIHN0YXJlYSBpbiBjYXJlIG51IGV4aXN0YSBuaWNpIHVuIGluZGl2aWQsIGZpZSBwcmFkYSBzYXUgcHJhZGF0b3IsIHNpIHMtYXIgaW50ZWxlZ2UgZGUgY2UgZSBvIHN0YXJlIGRlIGVjaGlsaWJydQ0KLSAgICoqKM6xL86yLCDOsy/OtCkqKiwgZXN0ZSBwdW5jdHVsIGluIGNhcmUgZGV6YXZhbnRhanVsIGludGVyYWN0aXVuaWkgY3UgcHJhZGF0b3J1bCBjb21wZW5zZWF6YSBleGFjdCBjcmVzdGVyZWEgbmF0dXJhbGEgYSBwb3B1bGF0aWVpIHByYXppaSwgcmVzcGVjdGl2IGF2YW50YWp1bCBvYnRpbnV0IGRpbiBpbnRlcmFjdGl1bmVhIGN1IHByYWRhIGNvbXBlbnNlYXphIGV4YWN0IHNjYWRlcmVhIG5hdHVyYWxhIGEgcG9wdWxhdGllaSBwcmFkYXRvcmlsb3IuIFBlbnRydSBtb2RlbHVsIG5vc3RydSBwYXJhbWV0cml6YXQgY3UgKioqzrE9MC4yKioqLCAqKirOsj0wLjAxKioqLCAqKirOsz0wLjEqKiosICoqKs60PTAuMDA1KioqIGFjZXN0IHB1bmN0IGVzdGUgKiooMjAsIDIwKSoqDQoNClB1bmN0ZWxlIGRlIGVjaGlsaWJydSBzZSBhZmxhIGxhIGludGVyc2VjdGlhIGNlbG9yIDIgbGluaWkgdW5kZSBlY3VhdGlpbGUgZGlmZXJlbnRpYWxlIGRpbiBzaXN0ZW0gc2UgYW51bGVhemEgKCpudWxsY2xpbmUqKS4gUHV0ZW0gb2JzZXJ2YSBjYSBwZW50cnUgYWNlbGFzaSBtb2RlbCwgaW5kaWZlcmVudCBkZSBwb3B1bGF0aWlsZSBpbml0aWFsZSwgY2VsZSAyIHBvcHVsYXRpaSB2b3Igb3NjaWxhIHBlcmlvZGljIGluIGp1cnVsIHB1bmN0dWx1aSBkZSBlY2hpbGlicnUgKioozrEvzrIsIM6zL860KSoqLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShwaGFzZVIpDQoNCnBhcmFtX25hbWVzID0gcGFzdGUobmFtZXMocGFyYW1ldGVycyksIHBhcmFtZXRlcnMsIHNlcCA9ICIgPSAiLCBjb2xsYXBzZSA9ICI7ICIpDQp0aXRsZSA9ICJNb2RlbCBMb3RrYS1Wb2x0ZXJyYSwgZGlhZ3JhbWEgZGUgZmF6YSBhIHBvcHVsYXRpaWxvciINCnhsaW0gPSBjKDAsIDEyMCkNCnlsaW0gPSBjKDAsIDgwKQ0KDQpwbG90KDIwLCAyMCwgbWFpbj10aXRsZSwgc3ViPXBhcmFtX25hbWVzLCB4bGltPXhsaW0sIHlsaW09eWxpbSwgcGNoPTIwLCB4bGFiPSJQcmFkYSIsIHlsYWI9IlByYWRhdG9yIikNCg0KaW52aXNpYmxlKG51bGxjbGluZXMobHZfbW9kZWwsIHhsaW09eGxpbSwgeWxpbT15bGltLCBwYXJhbWV0ZXJzID0gcGFyYW1ldGVycywgY29sPWMoInJlZCIsICJibHVlIiksIGx0eT01LCBzeXN0ZW09InR3by5kaW0iLCBzdGF0ZS5uYW1lcz1jKCJwcmV5IiwgInByZWRhdG9yIikpKQ0KaW52aXNpYmxlKGZsb3dGaWVsZChsdl9tb2RlbCwgeGxpbT14bGltLCB5bGltPXlsaW0sIHBhcmFtZXRlcnMgPSBwYXJhbWV0ZXJzLCBzeXN0ZW09InR3by5kaW0iLCBzdGF0ZS5uYW1lcz1jKCJwcmV5IiwgInByZWRhdG9yIikpKQ0KDQoNCmluaXRzIDwtIHJiaW5kKGMoMywzKSwgYygxNSwgMTgpLCBjKDMwLCA0MCksIGMoNTUsIDQ1KSkNCmZvcihpIGluIDE6bnJvdyhpbml0cykpIHsNCiAgaW5pdCA8LSBjKHByZXkgPSBpbml0c1tpLCAxXSwgcHJlZGF0b3IgPSBpbml0c1tpLCAyXSkNCiAgbHZyIDwtIG9kZShpbml0LCB0aW1lcywgbHZfbW9kZWwsIHBhcmFtZXRlcnMpDQogIA0KICBsaW5lcyhsdnJbLDJdLCBsdnJbLDNdLCBtYWluPXRpdGxlLCBzdWI9cGFyYW1fbmFtZXMsIGNvbD0iI2YxYTM0MCIsIHR5cGU9ImwiLCBsd2Q9MiwgeGxhYj0iUHJhZGEiLCB5bGFiPSJQcmFkYXRvciIpDQogIHBvaW50cyhsdnJbMSwyXSwgbHZyWzEsM10sIGNvbD0iI2YxYTM0MCIsIHBjaD0yMCwgY2V4PTMpDQp9DQogIA0KYGBgDQojIyMgKipJbWJ1bmF0YXRpcmkgYWxlIG1vZGVsdWx1aSBjbGFzaWMqKg0KDQpEaW4gbXVsdGUgcHJpdmludGUgbW9kZWx1bCBjbGFzaWMgcHJhZGEtcHJhZGF0b3IgTG90dmthLVZvbHRlcnJhIGVzdGUgbmFpdiBzaSBwcmV6aW50YSBsdWNydXJpbGUgaW50ci1vIG1hbmllcmEgbXVsdCBzaW1wbGlmaWNhdGEuIERlIGFsdGZlbCwgZWwgZnVuY3Rpb25lYXphIHBlIGJhemEgdXJtYXRvYXJlbG9yIHByZXN1cHVuZXJpOg0KDQotICAgcG9wdWxhdGlhIGRlIHByYWRhIHNlIGlubXVsdGVzdGUgbGEgbmVzZmFyc2l0IHNpIGVzdGUgbGltaXRhdGEgZG9hciBkZSBpbnRlcmFjdGl1bmVhIGN1IHByYWRhdG9yaWkNCi0gICBwcmFkYXRvcmlpIHBvdCBtYW5jYSBsYSBpbmZpbml0IGRpbiBwb3B1bGF0aWEgcHJhZGEsIGlhciBpbmZvbWV0YXJlYSBsb3IgbnUgZSBzYXRpc2ZhY3V0YSBuaWNpb2RhdGENCi0gICBwb3B1bGF0aWEgZGUgcHJhZGF0b3JpIHN1cHJhdmlldHVpZXNjIGhyYW5pbmR1LXNlIGRvYXIgY3UgbyBzcGVjaWUgZGUgcHJhZGENCi0gICBudSBleGlzdGEgY29tcGV0aXRpZSBpbnRyZSBwcmFkYXRvcmkgcGVudHJ1IHByYWRhDQotICAgcHJhZGEgbnUgc2UgcG9hdGUgYXNjdW5kZSBkaW4gY2FsZWEgcHJhZGF0b3JpbG9yDQoNCg0KIyMjIDEuIEludHJvZHVjZW0gY2FwYWNpdGF0ZSBtYXhpbWEgcGVudHJ1IHBvcHVsYXRpYSBkZSBwcmFkYQ0KDQpGb2xvc2luZCBvIGZ1bmN0aWUgZGUgY3Jlc3RlcmUgbG9naXN0aWNhLCBpbiBsb2MgZGUgY2VhIGV4cG9uZW50aWFsYSwgcHV0ZW0gbGltaXRhIHBvcHVsYXRpYSBkZSBwcmFkYSBsYSBvIGNhcGFjaXRhdGUgbWF4aW1hLiBBY2VzdGEgZXN0ZSB1biBzY2VuYXJpdSBtdWx0IG1haSByZWFsaXN0LCBwZW50cnUgY2EgaW4gbHVtZWEgcmVhbGEsIGNoaWFyIHNpIGluIGxpcHNhIHByYWRhdG9yaWxvciwgZXhpc3RhIGNvbnN0cmFuZ2VyaSBsZWdhdGUgZGUgcmVzdXJzZWxlIGV4aXN0ZW50ZS4NCg0KIVtdKGh0dHBzOi8vd2lraW1lZGlhLm9yZy9hcGkvcmVzdF92MS9tZWRpYS9tYXRoL3JlbmRlci9zdmcvMDE2YmU1OGM0MzkwMTBiNDM1OGZmNmRhMGNlZTc2MDdiZTJiNGQ0YikgTW9kZWx1bCBsb2dpc3RpYyBkZSBjcmVzdGVyZSB1bmRlICoqSyoqIHJlcHJlemludGEgY2FwYWNpdGF0ZWEgbWF4aW1hLg0KDQoNCkVjdWF0aWlsZSBtb2RlbHVsdWkgdm9yIGFyYXRhIGFzdGZlbCBhY3VtOg0KDQo8Y2VudGVyPiFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vRGFuSW5jaS9wcmV5LXByZWRhdG9yLW1vZGVsL21haW4vbW9kZWwtaW1wcm92ZWQtMS5zdmcpPC9jZW50ZXI+DQoNCkRlZmluaW0gbW9kZWx1bCBpbiBSOg0KDQpgYGB7cn0NCmx2X2ltcHJvdmVkXzEgPC0gZnVuY3Rpb24odCwgc3RhdGUsIHBhcmFtcykgew0KICB3aXRoKGFzLmxpc3QoYyhzdGF0ZSwgcGFyYW1zKSksIHsNCiAgICBkX3ByZXkgPC0gYWxwaGEgKiBwcmV5ICogKDEgLSBwcmV5IC8ga2FwcGEpIC0gYmV0YSAqIHByZXkgKiBwcmVkYXRvcg0KICAgIGRfcHJlZGF0b3IgPC0gZGVsdGEgKiBwcmV5ICogcHJlZGF0b3IgLSBnYW1tYSAqIHByZWRhdG9yDQogICAgcmV0dXJuKGxpc3QoYyhwcmV5ID0gZF9wcmV5LCBwcmVkYXRvciA9IGRfcHJlZGF0b3IpKSkNCiAgfSkNCn0NCmBgYA0KDQpTaW11bGFyZWEgbW9kZWx1bHVpOg0KDQpgYGB7cn0NCnBhcmFtZXRlcnNfMSA8LSBjKGFscGhhID0gMC42LCBiZXRhID0gMC4wMSwgZGVsdGEgPSAwLjAwNSwgZ2FtbWEgPSAwLjEsIGthcHBhID0gMjUwKQ0KaW5pdF8xIDwtIGMocHJleSA9IDEwLCBwcmVkYXRvciA9IDUpDQp0aW1lc18xIDwtIHNlcSgwLCAxNTAsIGJ5PTEpDQoNCmx2X2ltcHJvdmVkXzFfcmVzdWx0cyA8LSBvZGUoaW5pdF8xLCB0aW1lc18xLCBsdl9pbXByb3ZlZF8xLCBwYXJhbWV0ZXJzXzEpDQoNCmBgYA0KDQpgYGB7cn0NCnBhcmFtX25hbWVzID0gcGFzdGUobmFtZXMocGFyYW1ldGVyc18xKSwgcGFyYW1ldGVyc18xLCBzZXAgPSAiID0gIiwgY29sbGFwc2UgPSAiOyAiKQ0KdGl0bGUgPSAiTW9kZWwgcHJhZGEtcHJhZGF0b3IgaW1idW5hdGF0aXQgMSwgZGluYW1pY2EgaW4gdGltcCBhIHBvcHVsYXRpaWxvciINCnBsb3QobHZfaW1wcm92ZWRfMV9yZXN1bHRzWywxXSwgbHZfaW1wcm92ZWRfMV9yZXN1bHRzWywyXSwgbWFpbj10aXRsZSwgc3ViPXBhcmFtX25hbWVzLGNvbD0icmVkIix0eXBlPSJsIiwgbHdkPTIsIHhsYWI9IlRpbXAiLHlsYWI9IlBvcHVsYXRpZSIpDQpwb2ludHMobHZfaW1wcm92ZWRfMV9yZXN1bHRzWywxXSwgbHZfaW1wcm92ZWRfMV9yZXN1bHRzWywzXSwgY29sPSJibHVlIix0eXBlPSJsIixsd2Q9MiwgbHR5PTIpDQpsZWdlbmQoInRvcHJpZ2h0Iixsd2Q9YygyLDIpLGx0eT1jKDEsMiksY29sPWMoInJlZCIsImJsdWUiKSxsZWdlbmQ9YygicHJhZGEiLCJwcmFkYXRvciIpLGJ0eT0ibiIpDQoNCmBgYA0KSW4gZ3JhZmljdWwgY2UgcmVwcmV6aW50YSBkaW5hbWljYSBpbiB0aW1wIGEgcG9wdWxhdGlpbG9yIHNlIG9ic2VydmEgY3VtIG9zY2lsYXRpaWxlIHNlIHJlZHVjIHNpIHRpbmQgc3ByZSBvIHN0YXJlIGRlIGVjaGlsaWJydS4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCg0KcGFyYW1fbmFtZXMgPSBwYXN0ZShuYW1lcyhwYXJhbWV0ZXJzXzEpLCBwYXJhbWV0ZXJzXzEsIHNlcCA9ICIgPSAiLCBjb2xsYXBzZSA9ICI7ICIpDQp0aXRsZSA9ICJNb2RlbCBwcmFkYS1wcmFkYXRvciBpbWJ1bmF0YXRpdCAxLCBkaWFncmFtYSBkZSBmYXphIGEgcG9wdWxhdGlpbG9yIg0KeGxpbSA9IGMoMCwgMTYwKQ0KeWxpbSA9IGMoMCwgMTMwKQ0KDQpwbG90KDIwLCA1NS4yLCBtYWluPXRpdGxlLCBzdWI9cGFyYW1fbmFtZXMsIHhsaW09eGxpbSwgeWxpbT15bGltLCBwY2g9MjAsIHhsYWI9IlByYWRhIiwgeWxhYj0iUHJhZGF0b3IiKQ0KDQppbnZpc2libGUobnVsbGNsaW5lcyhsdl9pbXByb3ZlZF8xLCB4bGltPXhsaW0sIHlsaW09eWxpbSwgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfMSwgY29sPWMoInJlZCIsICJibHVlIiksIGx0eT01LCBzeXN0ZW09InR3by5kaW0iLCBzdGF0ZS5uYW1lcz1jKCJwcmV5IiwgInByZWRhdG9yIikpKQ0KaW52aXNpYmxlKGZsb3dGaWVsZChsdl9pbXByb3ZlZF8xLCB4bGltPXhsaW0sIHlsaW09eWxpbSwgcGFyYW1ldGVycyA9IHBhcmFtZXRlcnNfMSwgc3lzdGVtPSJ0d28uZGltIiwgc3RhdGUubmFtZXM9YygicHJleSIsICJwcmVkYXRvciIpKSkNCg0KY29sb3JzIDwtIGMoIiNmMWEzNDAiLCAgIiM5OThlYzMiKQ0KaW5pdHNfMSA8LSByYmluZChjKDMsMyksIGMoOTUsIDE4KSkNCmZvcihpIGluIDE6bnJvdyhpbml0c18xKSkgew0KICBpbml0IDwtIGMocHJleSA9IGluaXRzXzFbaSwgMV0sIHByZWRhdG9yID0gaW5pdHNfMVtpLCAyXSkNCiAgbHZyIDwtIG9kZShpbml0LCB0aW1lc18xLCBsdl9pbXByb3ZlZF8xLCBwYXJhbWV0ZXJzXzEpDQogIA0KICBsaW5lcyhsdnJbLDJdLCBsdnJbLDNdLCBtYWluPXRpdGxlLCBzdWI9cGFyYW1fbmFtZXMsIGNvbD1jb2xvcnNbaV0sIHR5cGU9ImwiLCBsd2Q9MiwgeGxhYj0iUHJhZGEiLCB5bGFiPSJQcmFkYXRvciIpDQogIHBvaW50cyhsdnJbMSwyXSwgbHZyWzEsM10sIGNvbD1jb2xvcnNbaV0sIHBjaD0yMCwgY2V4PTMpDQp9DQogIA0KYGBgDQpEaWFncmFtYSBkZSBmYXphIGEgcG9wdWxhdGlpbG9yIHJlZmxlY3RhIGZhcHR1bCBjYSBvc2NpbGF0aWlsZSBwb3B1bGF0aWlsb3IgbnUgc3VudCBwZXJpb2RpY2UsIGlhciBhY2VzdGVhIHRpbmQgc3ByZSB1bnVsIGRpbiBwdW5jdGVsZSBkZSBlY2hpbGlicnUgdW5kZSBhdGF0IHByYWRhdG9yaWksIGNhdCBzaSBwcmF6aWxlIHNlIGFmbGEgaW4gbnVtYXIgZWdhbC4NCg0KUGVudHJ1IG1vZGVsdWwgYWNlc3RhIHNlIHBvdCBjYWxjdWxhIDMgc3RhcmkgZGUgZWNoaWxpYnJ1Og0KLSAgICoqKDAsMCkqKg0KLSAgICoqKM6zL860LCDOsSjOtEstzrMpLyjOss60SykpKiogY2FyZSBsYSBuaXZlbHVsIGRpYWdyYW1laSBkZSBtYWkgc3VzIHJlcHJlemludGEgcHVuY3R1bCAqKigyMCwgNTUuMikqKg0KLSAgICoqKEssIDApKiogcG9hdGUgY2VsIG1haSBpbnRlcmVzYXQgY2FyZSBkYSBkZSBpbnRlbGVzIGNhIGV4aXN0YSBvIHN0YXJlIGluIGNhcmUgcHJhZGF0b3JpaSBhdSBkaXNwYXJ1dCBzaSBtYWkgZXhpc3RhIGRvYXIgcG9wdWxhdGllIHByYWRhDQoNCiMjIyAyLiBTY2hpbWJhbSBmdW5jdGlhIGRlIHJhc3B1bnMgbGEgaW50ZXJhY3RpdW5lYSBwcmFkYS1wcmFkYXRvcg0KDQpJbiBtb2RlbHVsIGFjdHVhbCBzZSBwcmVzdXB1bmUgY2EgbnVtYXJ1bCBkZSBwcmF6aSBjb25zdW1hdGUgZGUgY2F0cmUgcHJhZGF0b3JpIGNyZXN0ZSBpbnRyLW8gbWFuaWVyYSBsaW5lYXJhIHBlIG1hc3VyYSBjZSBwb3B1bGF0aWEgZGUgcHJhemkgY3Jlc3RlLiBQcmVzdXB1bmVyZWEgYWNlc2F0YSBlc3RlIG5haXZhIHBlbnRydSBjYSBpbnNlYW1uYSBjYSBwcmFkYXRvcmlpIG51IHNlIHNhdHVyYSBuaWNpb2RhdGEuIEFjZXN0IHRpcCBkZSBmdW5jdGllIGRlIHJhc3B1bnMgYXJlIHRpcHVsIEkuUGUgbGFuZ2EgYWNlYXN0YSwgdm9pIHByZXplbnRhIHNpIGZ1bmN0aWEgZGUgcmFzcHVucyBkZSB0aXB1bCBJSSBzaSBJSUkuDQoNCmBgYHtyfQ0KdHlwZTFGIDwtIGZ1bmN0aW9uKHgsIGJldGEpIHsNCiAgeCpiZXRhDQp9DQoNCnR5cGUyRiA8LSBmdW5jdGlvbih4LCBiZXRhLCBoKSB7DQogIChiZXRhKngpLygxK2JldGEqaCp4KQ0KfQ0KDQp0eXBlM0YgPC0gZnVuY3Rpb24oeCwgYmV0YSwgaCkgew0KICAoYmV0YSp4KngpLygxK2JldGEqaCp4KngpDQp9DQpgYGANCiAgDQo8YnIvPg0KKipUaXB1bCBJOioqICFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vRGFuSW5jaS9wcmV5LXByZWRhdG9yLW1vZGVsL21haW4vdHlwZTEtcmVzcG9uc2Uuc3ZnKSB1bmRlOg0KDQotICAgzrIgY2FyYWN0ZXJpemVhemEgcmF0YSBkZSBhdGFjIGEgcHJhZGF0b3J1bHVpDQogIA0KYGBge3J9DQpjdXJ2ZSh0eXBlMUYoeCwgMC4xKSwgMCwgMTAwMCwgbWFpbj0iRnVuY3RpZSByYXNwdW5zIGxhIGludGVyYWN0aXVuZWEgcHJhZGEtcHJhZGF0b3IgZGUgdGlwIEkiLCBjb2w9InJlZCIsIHR5cGU9ImwiLCBsd2Q9MiwgeGxhYj0iUG9wdWxhdGllIHByYWRhIix5bGFiPSJJbmRpdml6aSBwcmFkYSBtYW5jYXRpIC8gcHJhZGF0b3IiKQ0KYGBgDQo8YnIvPg0KKipUaXB1bCBJSToqKiAhW10oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL0RhbkluY2kvcHJleS1wcmVkYXRvci1tb2RlbC9tYWluL3R5cGUyLXJlc3BvbnNlLnN2ZykgdW5kZToNCg0KLSAgIM6yIGNhcmFjdGVyaXplYXphIHJhdGEgZGUgYXRhYyBhIHByYWRhdG9ydWx1aQ0KLSAgIGggcmVwcmV6aW50YSB0aW1wdWwgbmVjZXNhciBjYSBwcmFkYXRvcnVsIHNhIGlzaSBhdGFjZSB2aWN0aW1hDQoNCkFjZXN0IHJhc3B1bnMgdGluZSBjb250IGRlIHNhdGlldGF0ZWEgcHJhZGF0b3J1bHVpLiBOdW1hcnVsIGRlIHByYXppIGNvbnN1bWF0ZSBkZSBwcmFkYXRvciBjcmVzdGUgcHJvcG9ydGlvbmFsIGN1IHBvcHVsYXRpYSBwcmF6aWxvciwgZGFyIHBhbmEgbGEgdW4gcHVuY3QuDQoNCmBgYHtyfQ0KY3VydmUodHlwZTJGKHgsIDAuMSwgMC4xKSwgMCwgMTAwMCwgbWFpbj0iRnVuY3RpZSByYXNwdW5zIGxhIGludGVyYWN0aXVuZWEgcHJhZGEtcHJhZGF0b3IgZGUgdGlwIElJIiwgY29sPSJyZWQiLCB0eXBlPSJsIiwgbHdkPTIsIHhsYWI9IlBvcHVsYXRpZSBwcmFkYSIseWxhYj0iSW5kaXZpemkgcHJhZGEgbWFuY2F0aSAvIHByYWRhdG9yIikNCmBgYA0KPGJyLz4NCioqVGlwdWwgSUlJOioqICFbXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vRGFuSW5jaS9wcmV5LXByZWRhdG9yLW1vZGVsL21haW4vdHlwZTMtcmVzcG9uc2Uuc3ZnKSB1bmRlOg0KDQotICAgzrIgY2FyYWN0ZXJpemVhemEgcmF0YSBkZSBhdGFjIGEgcHJhZGF0b3J1bHVpDQotICAgaCByZXByZXppbnRhIHRpbXB1bCBuZWNlc2FyIGNhIHByYWRhdG9ydWwgc2EgYXRhY2UgbyBwcmFkYQ0KDQpJbiBwbHVzIGZhdGEgZGUgcmFzcHVuc3VsIGRlIHRpcCBJSSwgcHJhZGF0b3J1bCBpc2kgZm9ybWVhemEgbyBpbWFnaW5lIGEgcHJhemlpIHBlIG1hc3VyYSBwcmV6ZW50ZWkgYWNlc3RlaWEuIEFzdGZlbCwgZGFjYSBwb3B1bGF0aWEgcHJhZGEgZXN0ZSByZWR1c2EsIHByYWRhdG9ydWwgbnUgbyB2YSBjdW5vYXN0ZSBzaSBudSBvIHZhIGNhdXRhLCBkYXIgcGUgbWFzdXJhIGNlIGFwYXIgdG90IG1haSBtdWx0ZSBwcmF6aWksIHNlIHZhIGZhbWlsaWFyaXphIGN1IGFjZWFzdGEuDQoNCmBgYHtyfQ0KY3VydmUodHlwZTNGKHgsIDAuMDAwMSwgMC4xKSwgMCwgMTAwMCwgbWFpbj0iRnVuY3RpZSByYXNwdW5zIGxhIGludGVyYWN0aXVuZWEgcHJhZGEtcHJhZGF0b3IgZGUgdGlwIElJSSIsIGNvbD0icmVkIiwgdHlwZT0ibCIsIGx3ZD0yLCB4bGFiPSJQb3B1bGF0aWUgcHJhZGEiLHlsYWI9IkluZGl2aXppIHByYWRhIG1hbmNhdGkgLyBwcmFkYXRvciIpDQpgYGANCg0KRm9sb3NpbmQgZnVuY3RpYSByYXNwdW5zIGRlIHRpcCBJSUksIG1vZGVsdWwgaW1idW5hdGF0aXQgdmEgZmk6DQoNCjxjZW50ZXI+IVtdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9EYW5JbmNpL3ByZXktcHJlZGF0b3ItbW9kZWwvbWFpbi9tb2RlbC1pbXByb3ZlZC0yLnN2Zyk8L2NlbnRlcj4NCg0KYGBge3J9DQpsdl9pbXByb3ZlZF8yIDwtIGZ1bmN0aW9uKHQsIHN0YXRlLCBwYXJhbXMpIHsNCiAgd2l0aChhcy5saXN0KGMoc3RhdGUsIHBhcmFtcykpLCB7DQogICAgZF9wcmV5IDwtIGFscGhhICogcHJleSAqICgxIC0gcHJleSAvIGthcHBhKSAtIHR5cGUzRihwcmV5LCBiZXRhLCBoKSAqIHByZWRhdG9yDQogICAgZF9wcmVkYXRvciA8LSB0eXBlM0YocHJleSwgYmV0YSwgaCkgKiBwcmVkYXRvciAtIGdhbW1hICogcHJlZGF0b3INCiAgICByZXR1cm4obGlzdChjKHByZXkgPSBkX3ByZXksIHByZWRhdG9yID0gZF9wcmVkYXRvcikpKQ0KICB9KQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KcGFyYW1ldGVyc18yIDwtIGMoYWxwaGEgPSAwLjEsIGJldGEgPSAwLjAwMTIsIGRlbHRhID0gMC4wNSwgZ2FtbWEgPSAwLjksIGthcHBhID0gMjUwLCBoID0gMC4xKQ0KaW5pdF8yIDwtIGMocHJleSA9IDEwLCBwcmVkYXRvciA9IDUpDQp0aW1lc18yIDwtIHNlcSgwLCAxNTAsIGJ5PTEpDQoNCmx2X2ltcHJvdmVkXzJfcmVzdWx0cyA8LSBvZGUoaW5pdF8yLCB0aW1lc18yLCBsdl9pbXByb3ZlZF8yLCBwYXJhbWV0ZXJzXzIpDQoNCmBgYA0KDQpgYGB7cn0NCnBhcmFtX25hbWVzID0gcGFzdGUobmFtZXMocGFyYW1ldGVyc18yKSwgcGFyYW1ldGVyc18yLCBzZXAgPSAiID0gIiwgY29sbGFwc2UgPSAiOyAiKQ0KdGl0bGUgPSAiTW9kZWwgcHJhZGEtcHJhZGF0b3IgaW1idW5hdGF0aXQgMiwgZGluYW1pY2EgaW4gdGltcCBhIHBvcHVsYXRpaWxvciINCnBsb3QobHZfaW1wcm92ZWRfMl9yZXN1bHRzWywxXSwgbHZfaW1wcm92ZWRfMl9yZXN1bHRzWywyXSwgbWFpbj10aXRsZSwgc3ViPXBhcmFtX25hbWVzLGNvbD0icmVkIix0eXBlPSJsIiwgbHdkPTIsIHhsYWI9IlRpbXAiLHlsYWI9IlBvcHVsYXRpZSIsIHlsaW09YygwLCA1MCkpDQpwb2ludHMobHZfaW1wcm92ZWRfMl9yZXN1bHRzWywxXSwgbHZfaW1wcm92ZWRfMl9yZXN1bHRzWywzXSwgY29sPSJibHVlIix0eXBlPSJsIixsd2Q9MiwgbHR5PTIpDQpsZWdlbmQoInRvcHJpZ2h0Iixsd2Q9YygyLDIpLGx0eT1jKDEsMiksY29sPWMoInJlZCIsImJsdWUiKSxsZWdlbmQ9YygicHJhZGEiLCJwcmFkYXRvciIpLGJ0eT0ibiIpDQoNCmBgYA0KDQpgYGB7cn0NCnBhcmFtX25hbWVzID0gcGFzdGUobmFtZXMocGFyYW1ldGVyc18yKSwgcGFyYW1ldGVyc18yLCBzZXAgPSAiID0gIiwgY29sbGFwc2UgPSAiOyAiKQ0KdGl0bGUgPSAiTW9kZWwgcHJhZGEtcHJhZGF0b3IgaW1idW5hdGF0aXQgMiwgZGlhZ3JhbWEgZGUgZmF6YSBhIHBvcHVsYXRpaWxvciINCnBsb3QobHZfaW1wcm92ZWRfMl9yZXN1bHRzWywyXSwgbHZfaW1wcm92ZWRfMl9yZXN1bHRzWywzXSwgbWFpbj10aXRsZSwgc3ViPXBhcmFtX25hbWVzLCBjb2w9IiNmMWEzNDAiLCB0eXBlPSJsIiwgbHdkPTIsIHhsYWI9IlByYWRhIiwgeWxhYj0iUHJhZGF0b3IiKQ0KcG9pbnRzKGx2X2ltcHJvdmVkXzJfcmVzdWx0c1sxLDJdLCBsdl9pbXByb3ZlZF8yX3Jlc3VsdHNbMSwzXSwgY29sPSIjZjFhMzQwIiwgcGNoPTIwLCBjZXg9MykNCg0KYGBgDQoNCiMjIyAqKkJpYmxpb2dyYWZpZSoqDQoNCi0gICBUaGUgcHJleS1wcmVkYXRvciBtb2RlbDogaHR0cHM6Ly9ycHVicy5jb20vSmVldDE5OTQvUHJleS1wcmVkYXRvci1tb2RlbA0KLSAgIE1hdGhlbWF0aWNhbCBiaW9sb2d5LCBMZWN0dXJlIE5vdGVzIGZvciBNQVRIIDQzMzMsIEplZmZyZXksIFIuIENoYXNub3YsIENoYXB0ZXIgMS40LiAiVGhlIExvdGthLVZvbHRlcnJhIHByZWRhdG9yLXByZXkgbW9kZWwiOiBodHRwczovL3d3dy5tYXRoLnVzdC5oay9+bWFjaGFzL21hdGhlbWF0aWNhbC1iaW9sb2d5LnBkZg0KLSAgIEFwcGxpZWQgUG9wdWxhdGlvbiBFY29sb2d5LCBMZWN0dXJlIE5vdGVzLCBLZXZpbiBTaG9lbWFrZXIsIFNwZWNpZXMgSW50ZXJhY3Rpb25zOiBwcmV5LXByZWRhdG9yOiBodHRwczovL2tldmludHNob2VtYWtlci5naXRodWIuaW8vTlJFUy00NzAvTEVDVFVSRTE3Lmh0bWwNCi0gICBVbml2ZXJzaXR5IG9mIENhbGlmb3JuaWEgSXJ2aW5lIE9wZW5Db3Vyc2VXYXJlLCBJbnRyb2R1Y3Rpb24gdG8gTWF0aGVtYXRpY2FsIE1vZGVsaW5nIGluIEJpb2xvZ3k6IFByZWRhdG9yIFByZXkgTW9kZWw6IGh0dHA6Ly9vY3cudWNpLmVkdS9sZWN0dXJlcy9tYXRoXzExM2JfbGVjXzE0X2ludHJvZHVjdGlvbl90b19tYXRoZW1hdGljYWxfbW9kZWxpbmdfaW5fYmlvbG9neV9wcmVkYXRvcl9wcmV5X21vZGVsLmh0bWwNCg==