Importation des packages utiles

rm(list=ls()) # Nettoyage de l'environnement
# Liste des packages requis
packages <- c("deSolve", "tidyverse", "rgl", "cowplot", "tidyr")

# Vérification des packages installés
for (i in packages) {
  if (!requireNamespace(i, quietly = TRUE)) {
    # Installation du package s'il n'est pas installé
    install.packages(i)
  }
}

# Chargement des packages
library(deSolve) # package de la fonction ode pour les equation différentielles ordinaires
library(tidyverse) # pour les manupilation et les graphiques
library(rgl) # pour les graphiques en 3D
library(cowplot)
library(tidyr)

1 Visualisation des fonction \(k\), \(a\) et \(s\) selon le phénotype

Tout d’abord regardons l’allure des fonctions \(k\), la capacité de charge pour chaque phénotype, et la fonction de compétition \(a\) pour différentes valeurs de \(\sigma^2\).

a <- function(x1,x2, sigmaA){
  return(exp(-.5*(x1-x2)**2/sigmaA))
}

K <- function(x){
  return(K0 - lambda*(x - x0)**2)
}

x0=1
K0=1
lambda = 1

x = seq(0.1,1.9,.1)

sigma2 = c(0.1, 0.5, 1, 10)
plot_list = list()
for (i in 1:length(sigma2)){
  sigmaA = sigma2[i]
  data1 = data.frame(X=x, val_K = K(x), 
                     A_x5=a(x[5],x, sigmaA),
                     A_x9=a(x[9],x, sigmaA),
                     A_x10=a(x[10],x, sigmaA), 
                     A_x11=a(x[11],x, sigmaA),
                     A_x15=a(x[15],x, sigmaA)) %>%
    pivot_longer(-X, names_to="func",values_to = "value")

  plot_list[[i]] = ggplot(data=data1, aes(x=X, y=value, col=func))+
    geom_line() +
    xlab("Phénotype")+
    ggtitle(paste0("Allure des fonctions pour sigma²=", sigmaA))+
    theme_bw()+
    theme(line = element_blank(), 
        axis.line = element_line(colour = "black"),
        panel.border = element_blank(),
        axis.ticks =  element_line(colour = "black"),
        axis.text.x = element_text(colour = "black", size=10),
        axis.text.y = element_text(colour = "black", size=10),
        axis.title=element_text(size=10),
        legend.text = element_text(size = 7),  
        legend.title = element_text(size = 7)) +
    scale_color_manual(
      name = "Fonction",
      values = c("A_x10" = "red","A_x9" = "orange","A_x11" = "magenta", "A_x5" = "blue", "A_x15" = "green", "val_K" = "black"),
      labels = c("A(1,x)","A(0.9,x)","A(1.1,x)", "A(0.5,x)", "A(1.5,x)", "K(x)"))
    
  
}

plot_grid(plotlist = plot_list)

Lorsque \(\sigma^2\) est faible la courbure pour la fonction de compétition est assez faible et plusieurs phénotypes semblent pouvoir cohabiter à des valeurs de K non maximales (en quelque sorte rentrer sous K), plus \(\sigma^2\) augmente, plus la fonction de compétition s’applatit et la coexistence devrait devenir plus compliquée. On s’attend donc à une relation négative entre \(\sigma^2\) et le nombre de phénotype pouvant coexister.

On va maintenant faire une représentation en 3D pour la fitness d’invasion, qui s’exprime : \(s(x_{1},x_{2})=r.(1-a(x_{1},x_{2}).\frac{K(x_{2})}{K(x_{1})})\)

a <- function(x1,x2, sigmaA){
  return()
}

S <- function(x1,x2){
  r=1
  a = exp(-.5*(x1-x2)**2/sigmaA)
  return(r*(1-a*K(x2)/K(x1)))
}
x0=1
K0=1
lambda = 1
sigmaA = 1

x1 <- seq(0.1,1.9,.1)
x2 <- seq(0.1,1.9,.1)
#create z-values
z = outer(x1, x2, S)

#create 3D plot
persp(x1, x2, z, xlab='x', ylab='y', zlab='s(x,y)',
      main='Invasion fitness', col='lightblue', shade=.4, theta = 60, phi = 25, ticktype='simple')

2 Simulations de la dynamique adaptative

2.1 Définition des fonctions utilisées dans la suite

  • Créations des fonctions de base pour la simulation
# Fonction pour calculer K
calculeK <- function(X,K0,x0, lambda){
  return(max(K0 - lambda*(X - x0)**2, 10**(-9)))
}


# Fonction pour obtenir les valeurs de la matrice A
calculeA <- function(X, nbSpecies, sigmaA_squared){
  D = matrix(data=X,ncol=nbSpecies,nrow=nbSpecies) # matrice
  C = t(D) - D
  return(exp(-C**2/(2*sigmaA_squared)))
}


# Fonction pour calculer la matrice B
calculeB <- function(X, nbSpecies, K0, x0, lambda){
  vectK = sapply(X, FUN= function(x) { calculeK(x, K0, x0, lambda) })
  B = matrix(data = 1/vectK, nrow=nbSpecies, ncol=nbSpecies, byrow=TRUE)
  return(B)
}


# Fonction par calucle la matrice M qui est le produit de A et B
calculeM = function(X, nbSpecies, sigmaA_squared, K0=1, lambda=1){
  x0 = min(X) + (max(X) - min(X)) / 2  
  
  A = calculeA(X, nbSpecies, sigmaA_squared) # matrice A
  B = calculeB(X, nbSpecies, K0, x0, lambda) # matrice B
  
  M=A*B # produit terme a terme de A avec B
  return(M)
}


# Fonctions pour définition l'equation differentielle 
calculeGradientLV = function(t, Ni, params){
  with(as.list(params), {
    
    dNi = r*Ni*(1 - Ni%*%M)

    return(list(dNi))
  })
}


# Fonctions pour resoudre l'equation differentielle avec ode du package deSolve
resoutEqd <- function(parameters, densite_t0, vect_time, vect_phenotypes){
  df_densite = ode(y = densite_t0, times = vect_time, func = calculeGradientLV, parameters) %>%
    as.data.frame() %>%
    pivot_longer(-time, values_to = "density", names_to = "species")

  df_densite$X = vect_phenotypes[as.numeric(df_densite$species)]
    
  return(df_densite)# retourne sortie modifier de ode
}


# Définition de la fonction pour simuler le dynamique de la poppulation
simuleDynamique <- function(nbSpecies, sigmaA_squared, vect_temps, K0=1, r=1){
  n0 = rep(K0/nbSpecies, nbSpecies) 
  vect_phenotypes = seq(0, 1, length.out = nbSpecies)

  M = calculeM(vect_phenotypes, nbSpecies, sigmaA_squared, K0)
  params = list(r = r, M = M)
  df_densite = resoutEqd(params, n0, vect_temps, vect_phenotypes)
  
  return(df_densite) # la densité de la population obtenu avec ode
}
  • Les fonctions pour les représentations graphiques
# définition des thèmes
main_theme = theme_bw()+
  theme(line = element_blank(), 
        axis.line = element_line(colour = "black"),
        panel.border = element_blank(),
        axis.ticks =  element_line(colour = "black"),
        axis.text.x = element_text(colour = "black", size=10),
        axis.text.y = element_text(colour = "black", size=10),
        legend.position="none",
        axis.title=element_text(size=10))

# fonction pour illustrer le dynamique de la population selon le temps
plotDynamique <- function(df_density, plot_theme=main_theme, title= ""){
  plot = ggplot(df_density)+
    geom_line(aes(time, density, col = species)) +
    ggtitle(label = title) +
    xlab("Temps") +
    ylab("Phénotypes") +
    plot_theme
  return(plot)
}

# fonction pour illustrer l'évolution de la dynamique selon les phénotypes
plotEvolutiontrait <- function(df_density, plot_theme=main_theme){
  plot = ggplot(data = df_density) +
    geom_tile(aes(x = X, y = time, fill = density))+
    scale_fill_gradient2(low = "white" , high = "red")+
    xlab("Phénotypes") +
    ylab("Temps") +
    plot_theme
  
  return(plot)
}

2.2 Simulation sans mutation

vect_temps = 0:2000 # temps de simulation
K0 = 1             # capacite de charge du milieu tout phenotype confondu
sigmaA_squared = 1 # varie entre 0.1 et 10
r=1                # taux de croissance, le même pour toutes les espèces


# Simulation de la dynamique avec 10 phenotypes
nbSpecies = 10 # nombre d'especes
df_densite10 = simuleDynamique(nbSpecies, sigmaA_squared, vect_temps)

plot1 = plotDynamique(df_densite10, title="Evolution des phénotypes (N = 10)" )
plot2 = plotEvolutiontrait(df_densite10)


# Simulation de la dynamique avec 100 phenotypes
nbSpecies = 100 # nombre d'especes
df_densite100 = simuleDynamique(nbSpecies, sigmaA_squared, vect_temps)

plot3 = plotDynamique(df_densite100, title="Evolution des phénotypes (N = 100)")
plot4 = plotEvolutiontrait(df_densite100)

plot_grid(labels = "auto",plotlist=list(plot1,plot2,plot3,plot4))

Avec \(\sigma^2 = 1\), une valeur intermédiaire, on observe que toutes les espèces s’éloignant trop de l’optimum sont éliminées. La tendance semble donc plutôt être l’élimination par compétition que la coexistence de phénotype.

2.3 Coexistence des phénotypes et valeurs de sigma

On peut se poser la question suivante : Combien d’espèces persisteront dans le temps selon la valeur de sigma ?

vect_temps = 0:2000 # temps de simulation
sigmaA_squared = c(0.1, 0.5, 1, 10) # Les differents valeurs de sigma

plot_list = list()

nbSpecies = 100
for (i in 1:length(sigmaA_squared)){
  df_densite = simuleDynamique(nbSpecies, sigmaA_squared[i], vect_temps)
  plot_list[[i]] = plotDynamique(df_densite, title=paste("Cas N= 100, sigma² = ", sigmaA_squared[i]))
  #plot_list[[i]] = plotEvolutiontrait(df_densite))
}

plot_grid(labels = "auto",plotlist = plot_list)

Avec \(N = 100\) On constate qu’à mesure que \(\sigma^2\) augmente, le nombre de phénotypes qui persistent à long terme augmente entre 0.1 et 0.5. Lorsque \(\sigma^2\) est supérieur à \(1\), certains phénotypes subsistent initialement mais finissent rapidement par s’éteindre, et au bout de \(2000\) pas de temps de simulation, un seul phénotype subsiste.

Regardons maintenant le nombre de phénotypes qui persistent selon plusieurs valeurs de \(sigma²\).

density_min = 0.005 # en dessous de cette densité le phénotype est considéré disparu

vect_temps = 0:2000 # temps de simulation
sigmaA_squared = c(seq(0.1,1, by=0.1), seq(2,10)) # les valeurs de sigma² a tester

df_persitance = data.frame()
nbSpecies = 100 # Nombre d'espèces
for (i in 1:length(sigmaA_squared)){
  df_densite = simuleDynamique(nbSpecies, sigmaA_squared[i], vect_temps)
  nb_persistant = df_densite[df_densite$time == max(df_densite$time),] %>%
    filter(density>density_min) %>%
    nrow()
  
  df_persitance = rbind(df_persitance,c(sigmaA_squared[i],nb_persistant))
}

colnames(df_persitance) = c("sigma_square", "nb_persistant")

ggplot(data = df_persitance, aes(x = sigma_square, y = nb_persistant)) +
  geom_point() +
  geom_line() +
  labs(title = "Nombre de phénotypes persistants selon sigma²",
       subtitle = "Cas N = 100",
    x = "Sigma²",
    y = "Nombre de phénotypes"
  ) +
  main_theme

Comme on s’y attendait le nombre d’espèces persistantes diminue lorsque \(\sigma^2\) augmente.

Y a-t-il une limite au nombre d’espèces qui peuvent coexister dans ce modèle ? A priori il n’y pas de limite car on prend une fonction gaussienne, mais une limite pourrait apparaitre si on prenais d’autres distributions.

2.4 Simulation avec ajout de mutations dans la dynamique

Définition des fonction pour simuler la mutation.

Pour simuler des mutations, le principe va être le suivant :

  1. On tire les temps auxquels les mutations vont avoir lieu de manière aléatoire

2.a On simule la dynamique entre t0 et le premier temps de mutation

2.b Au temps de mutation, on récupère les densités finales puis on applique la mutation, qui consiste à “décaler” les phénotypes vers la droite ou la gauche en déplaçant une certaines proportion de la densité dans le phénotype juste à droite ou juste à gauche.

2.c On retourne à l’étape 2.a et on simule la dynamique entre le temps de mutation et le suivant en prenant comme condition initiale le vecteur des densités mutées.

Définition des fonctions utiles :

# fonction pour obtenir les temps auxquels il y aura une mutation 
getMutationTime <- function(vect_temps, taux_mutation){
  SimulationDuration = max(vect_temps)
  # Determination des instants auxquels il y aura une mutation
  nb_mutations_moy = SimulationDuration * taux_mutation 
  nb_mutations_sd = nb_mutations_moy / 4
  nb_mutations = max(1,floor(rnorm(1, mean=nb_mutations_moy, sd=nb_mutations_sd)))
  
  temps_mutation = c(0,sort(sample(x = vect_temps, size = nb_mutations)),SimulationDuration)
  return(temps_mutation)
}


# fonction pour le décalage (droite ou gauche) de la mutation
mutationParDecalage <- function(df_densite, proportionMutant) {
  
  n0 = df_densite$density[df_densite$time==max(df_densite$time)] 
  nbSpecies = length(n0)
  # mutation : decalage d'une partie des effectifs
  p = runif(1) # tirage aléatoire entre 0 et 1

  if (p < .7){
  # decalage à droite
  effectifMutants = n0[1:(nbSpecies-1)]*proportionMutant
    
  n0[1:(nbSpecies-1)] = n0[1:(nbSpecies-1)] - effectifMutants
  n0[2:nbSpecies] = n0[2:nbSpecies] + effectifMutants
  }
  else{
  # decalage a gauche
  effectifMutants = n0[2:nbSpecies]*proportionMutant
    
  n0[1:(nbSpecies-1)] = n0[1:(nbSpecies-1)] + effectifMutants
  n0[2:nbSpecies] = n0[2:nbSpecies] - effectifMutants
  }
  return(n0)
}


# fonction pour la simulation de la mutation
simuleDynamiqueMutation <- function(nbSpecies, sigmaA_squared, temps_mutation, K0=1, r=1){
  
  # Calcule des phenotypes et de M
  vect_phenotypes = seq(0, 1, length.out = nbSpecies)
  M = calculeM(vect_phenotypes, nbSpecies, sigmaA_squared, K0)
  params = list(r = r, M = M)
  
  # Initialisation de n0 et de df_densite
  n0 = c(K0, rep(0, nbSpecies-1))
  df_densite = data.frame()

  # Simulation de la dynamique pour tout les temps de mutation
  for (i in 2:length(temps_mutation)){
    if (temps_mutation[i]-temps_mutation[i-1]>1){
      # Entre 2 mutation on simule en resolvant l'equation differentielle
      sample_vect_temps = seq(temps_mutation[i-1],temps_mutation[i]-1)
      df_densite = rbind(df_densite,resoutEqd(params, n0, sample_vect_temps, vect_phenotypes))
      # A chaque temps contenu dans temps_mutation une mutation a lieu 
      n0 = mutationParDecalage(df_densite, proportionMutant) # no devient le no avec mutation
    }
    
  }
  return(df_densite) # pop tot avec mutation
}

Visualisation de la dynamique avec 10 et 100 espèces, et un taux de mutation de 1/100, soit en moyenne une mutations tous les 100 pas de temps.

vect_temps = 0:2000 # Temps de simulation
sigmaA_squared = 0.4 # simga²
proportionMutant = 0.1 # Proportion du nombre de phénotype mutant
taux_mutation = 1/100 # taux de mutation


# Simulation de la dynamique avec 10 phenotypes
nbSpecies = 10
temps_mutation = getMutationTime(vect_temps, taux_mutation)
df_densite = simuleDynamiqueMutation(nbSpecies, sigmaA_squared, temps_mutation)

plot1 = plotDynamique(df_densite, title="Cas N = 10") 
plot1 = plot1 + geom_vline(xintercept=temps_mutation, linetype='18', color='red')

plot2 = plotEvolutiontrait(df_densite)



# Simulation de la dynamique avec 100 phenotypes
nbSpecies = 100
temps_mutation = getMutationTime(vect_temps, taux_mutation)
df_densite100 = simuleDynamiqueMutation(nbSpecies, sigmaA_squared, temps_mutation)
  
plot3 = plotDynamique(df_densite100, title="Cas N = 100") 
plot3 = plot3 + geom_vline(xintercept=temps_mutation, linetype='18', color='red')

plot4 = plotEvolutiontrait(df_densite100)

plot_grid(plotlist=list(plot1,plot2,plot3,plot4))

Lorsque l’on fait une simulation avec des mutation, on s’attend à ce que les mutations ramènent progressivement les valeurs de phénotypes vers l’optimum. Dans certains cas, en dynamique adaptative on peut observer des points de branchement. Pour cela il faut qu’un point singulier de la dynamique soit attracteur mais instable, et qu’il puisse être envahi par des phénotype de valeur inférieure et supérieure à sa valeur.

On a l’impression d’oberver un point de branchement avec nos 10 phénotypes. En revanche, on observe que avec 100 phénotypes, on n’a pas le temps de rejoindre l’optimum. On va refaire une simulation avec 100 phénotypes en prenant 2 taux de mutations différents.

vect_temps = 0:10000
sigmaA_squared = 0.4
proportionMutant = 0.1


# Simulation de la dynamique avec 50 phenotypes et taux_mutation = 1/200
nbSpecies = 50
taux_mutation = 1/200
temps_mutation = getMutationTime(vect_temps, taux_mutation)
df_densite = simuleDynamiqueMutation(nbSpecies, sigmaA_squared, temps_mutation)

plot1 = plotDynamique(df_densite, title="Taux de mutation = 0.005") 
plot1 = plot1

plot2 = plotEvolutiontrait(df_densite)



# Simulation de la dynamique avec 100 phenotypes et taux_mutation = 1/50
nbSpecies = 50
taux_mutation = 1/50
temps_mutation = getMutationTime(vect_temps, taux_mutation)
df_densite100 = simuleDynamiqueMutation(nbSpecies, sigmaA_squared, temps_mutation)
  
plot3 = plotDynamique(df_densite100, title="Taux de mutation = 0.02") 
plot3 = plot3

plot4 = plotEvolutiontrait(df_densite100)

plot_grid(plotlist=list(plot1,plot2,plot3,plot4))

Avec un taux de mutation faible, la densité est toujours très resserrée autour de peu de phénotype dominant (le trait est bien rouge et pas étalé) et on se déplace très lentement vers l’optimum. Si le taux de mutation est plus élevé en revanche, on observe un point de branchement et plus de phénotypes qui coexistent en plus faible densité.

LS0tDQp0aXRsZTogJ0NvbXB0ZSByZW5kdSBkZXMgVFAgZGUgbCcnVUUgTURFVDogRHluYW1pcXVlIGFkYXB0YXRpdmUnDQphdXRob3I6ICJMb3VpcyBTY2hyb2xsICYgQWJkb3VyYWhtYW5lIERpYWxsbyINCmRhdGU6ICIyMDIzLTExLTA1Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2ZvbGQ6IGhpZGUNCiAgICB3YXJuaW5nOiBubw0KICAgIG1lc3NhZ2U6IG5vDQogICAgdGhlbWU6IHVuaXRlZA0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgc2VjX251bWJlcl9zdWZmaXg6ICcuICAgJw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHJlc3VsdHMgPSAiaGlkZSIpDQpgYGANCg0KDQojIEltcG9ydGF0aW9uIGRlcyBwYWNrYWdlcyB1dGlsZXMgIHsudW5udW1iZXJlZH0NCg0KYGBge3IgUGFja2FnZXMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCnJtKGxpc3Q9bHMoKSkgIyBOZXR0b3lhZ2UgZGUgbCdlbnZpcm9ubmVtZW50DQojIExpc3RlIGRlcyBwYWNrYWdlcyByZXF1aXMNCnBhY2thZ2VzIDwtIGMoImRlU29sdmUiLCAidGlkeXZlcnNlIiwgInJnbCIsICJjb3dwbG90IiwgInRpZHlyIikNCg0KIyBWw6lyaWZpY2F0aW9uIGRlcyBwYWNrYWdlcyBpbnN0YWxsw6lzDQpmb3IgKGkgaW4gcGFja2FnZXMpIHsNCiAgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKGksIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgICMgSW5zdGFsbGF0aW9uIGR1IHBhY2thZ2UgcydpbCBuJ2VzdCBwYXMgaW5zdGFsbMOpDQogICAgaW5zdGFsbC5wYWNrYWdlcyhpKQ0KICB9DQp9DQoNCiMgQ2hhcmdlbWVudCBkZXMgcGFja2FnZXMNCmxpYnJhcnkoZGVTb2x2ZSkgIyBwYWNrYWdlIGRlIGxhIGZvbmN0aW9uIG9kZSBwb3VyIGxlcyBlcXVhdGlvbiBkaWZmw6lyZW50aWVsbGVzIG9yZGluYWlyZXMNCmxpYnJhcnkodGlkeXZlcnNlKSAjIHBvdXIgbGVzIG1hbnVwaWxhdGlvbiBldCBsZXMgZ3JhcGhpcXVlcw0KbGlicmFyeShyZ2wpICMgcG91ciBsZXMgZ3JhcGhpcXVlcyBlbiAzRA0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeSh0aWR5cikNCmBgYA0KDQoNCg0KIyBWaXN1YWxpc2F0aW9uIGRlcyBmb25jdGlvbiAkayQsICRhJCBldCAkcyQgc2Vsb24gbGUgcGjDqW5vdHlwZQ0KDQpUb3V0IGQnYWJvcmQgcmVnYXJkb25zIGwnYWxsdXJlIGRlcyBmb25jdGlvbnMgJGskLCBsYSBjYXBhY2l0w6kgZGUgY2hhcmdlIHBvdXIgY2hhcXVlIHBow6lub3R5cGUsIGV0IGxhIGZvbmN0aW9uIGRlIGNvbXDDqXRpdGlvbiAkYSQgcG91ciBkaWZmw6lyZW50ZXMgdmFsZXVycyBkZSAkXHNpZ21hXjIkLg0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KYSA8LSBmdW5jdGlvbih4MSx4Miwgc2lnbWFBKXsNCiAgcmV0dXJuKGV4cCgtLjUqKHgxLXgyKSoqMi9zaWdtYUEpKQ0KfQ0KDQpLIDwtIGZ1bmN0aW9uKHgpew0KICByZXR1cm4oSzAgLSBsYW1iZGEqKHggLSB4MCkqKjIpDQp9DQoNCngwPTENCkswPTENCmxhbWJkYSA9IDENCg0KeCA9IHNlcSgwLjEsMS45LC4xKQ0KDQpzaWdtYTIgPSBjKDAuMSwgMC41LCAxLCAxMCkNCnBsb3RfbGlzdCA9IGxpc3QoKQ0KZm9yIChpIGluIDE6bGVuZ3RoKHNpZ21hMikpew0KICBzaWdtYUEgPSBzaWdtYTJbaV0NCiAgZGF0YTEgPSBkYXRhLmZyYW1lKFg9eCwgdmFsX0sgPSBLKHgpLCANCiAgICAgICAgICAgICAgICAgICAgIEFfeDU9YSh4WzVdLHgsIHNpZ21hQSksDQogICAgICAgICAgICAgICAgICAgICBBX3g5PWEoeFs5XSx4LCBzaWdtYUEpLA0KICAgICAgICAgICAgICAgICAgICAgQV94MTA9YSh4WzEwXSx4LCBzaWdtYUEpLCANCiAgICAgICAgICAgICAgICAgICAgIEFfeDExPWEoeFsxMV0seCwgc2lnbWFBKSwNCiAgICAgICAgICAgICAgICAgICAgIEFfeDE1PWEoeFsxNV0seCwgc2lnbWFBKSkgJT4lDQogICAgcGl2b3RfbG9uZ2VyKC1YLCBuYW1lc190bz0iZnVuYyIsdmFsdWVzX3RvID0gInZhbHVlIikNCg0KICBwbG90X2xpc3RbW2ldXSA9IGdncGxvdChkYXRhPWRhdGExLCBhZXMoeD1YLCB5PXZhbHVlLCBjb2w9ZnVuYykpKw0KICAgIGdlb21fbGluZSgpICsNCiAgICB4bGFiKCJQaMOpbm90eXBlIikrDQogICAgZ2d0aXRsZShwYXN0ZTAoIkFsbHVyZSBkZXMgZm9uY3Rpb25zIHBvdXIgc2lnbWHCsj0iLCBzaWdtYUEpKSsNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKGxpbmUgPSBlbGVtZW50X2JsYW5rKCksIA0KICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksDQogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcyA9ICBlbGVtZW50X2xpbmUoY29sb3VyID0gImJsYWNrIiksDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIHNpemU9MTApLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBzaXplPTEwKSwNCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksDQogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3KSwgIA0KICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKA0KICAgICAgbmFtZSA9ICJGb25jdGlvbiIsDQogICAgICB2YWx1ZXMgPSBjKCJBX3gxMCIgPSAicmVkIiwiQV94OSIgPSAib3JhbmdlIiwiQV94MTEiID0gIm1hZ2VudGEiLCAiQV94NSIgPSAiYmx1ZSIsICJBX3gxNSIgPSAiZ3JlZW4iLCAidmFsX0siID0gImJsYWNrIiksDQogICAgICBsYWJlbHMgPSBjKCJBKDEseCkiLCJBKDAuOSx4KSIsIkEoMS4xLHgpIiwgIkEoMC41LHgpIiwgIkEoMS41LHgpIiwgIksoeCkiKSkNCiAgICANCiAgDQp9DQoNCnBsb3RfZ3JpZChwbG90bGlzdCA9IHBsb3RfbGlzdCkNCg0KYGBgDQoNCkxvcnNxdWUgJFxzaWdtYV4yJCBlc3QgZmFpYmxlIGxhIGNvdXJidXJlIHBvdXIgbGEgZm9uY3Rpb24gZGUgY29tcMOpdGl0aW9uIGVzdCBhc3NleiBmYWlibGUgZXQgcGx1c2lldXJzIHBow6lub3R5cGVzIHNlbWJsZW50IHBvdXZvaXIgY29oYWJpdGVyIMOgIGRlcyB2YWxldXJzIGRlIEsgbm9uIG1heGltYWxlcyAoZW4gcXVlbHF1ZSBzb3J0ZSByZW50cmVyIHNvdXMgSyksIHBsdXMgJFxzaWdtYV4yJCBhdWdtZW50ZSwgcGx1cyBsYSBmb25jdGlvbiBkZSBjb21ww6l0aXRpb24gcydhcHBsYXRpdCBldCBsYSBjb2V4aXN0ZW5jZSBkZXZyYWl0IGRldmVuaXIgcGx1cyBjb21wbGlxdcOpZS4gT24gcydhdHRlbmQgZG9uYyDDoCB1bmUgcmVsYXRpb24gbsOpZ2F0aXZlIGVudHJlICRcc2lnbWFeMiQgZXQgbGUgbm9tYnJlIGRlIHBow6lub3R5cGUgcG91dmFudCBjb2V4aXN0ZXIuIA0KDQpPbiB2YSBtYWludGVuYW50IGZhaXJlIHVuZSByZXByw6lzZW50YXRpb24gZW4gM0QgcG91ciBsYSBmaXRuZXNzIGQnaW52YXNpb24sIHF1aSBzJ2V4cHJpbWUgOiAkcyh4X3sxfSx4X3syfSk9ci4oMS1hKHhfezF9LHhfezJ9KS5cZnJhY3tLKHhfezJ9KX17Syh4X3sxfSl9KSQNCg0KYGBge3J9DQphIDwtIGZ1bmN0aW9uKHgxLHgyLCBzaWdtYUEpew0KICByZXR1cm4oKQ0KfQ0KDQpTIDwtIGZ1bmN0aW9uKHgxLHgyKXsNCiAgcj0xDQogIGEgPSBleHAoLS41Kih4MS14MikqKjIvc2lnbWFBKQ0KICByZXR1cm4ociooMS1hKksoeDIpL0soeDEpKSkNCn0NCngwPTENCkswPTENCmxhbWJkYSA9IDENCnNpZ21hQSA9IDENCg0KeDEgPC0gc2VxKDAuMSwxLjksLjEpDQp4MiA8LSBzZXEoMC4xLDEuOSwuMSkNCiNjcmVhdGUgei12YWx1ZXMNCnogPSBvdXRlcih4MSwgeDIsIFMpDQoNCiNjcmVhdGUgM0QgcGxvdA0KcGVyc3AoeDEsIHgyLCB6LCB4bGFiPSd4JywgeWxhYj0neScsIHpsYWI9J3MoeCx5KScsDQogICAgICBtYWluPSdJbnZhc2lvbiBmaXRuZXNzJywgY29sPSdsaWdodGJsdWUnLCBzaGFkZT0uNCwgdGhldGEgPSA2MCwgcGhpID0gMjUsIHRpY2t0eXBlPSdzaW1wbGUnKQ0KDQpgYGANCg0KDQojIFNpbXVsYXRpb25zIGRlIGxhIGR5bmFtaXF1ZSBhZGFwdGF0aXZlDQoNCiMjIETDqWZpbml0aW9uIGRlcyBmb25jdGlvbnMgdXRpbGlzw6llcyBkYW5zIGxhIHN1aXRlDQoNCiogQ3LDqWF0aW9ucyBkZXMgZm9uY3Rpb25zIGRlIGJhc2UgcG91ciBsYSBzaW11bGF0aW9uDQpgYGB7ciB9DQojIEZvbmN0aW9uIHBvdXIgY2FsY3VsZXIgSw0KY2FsY3VsZUsgPC0gZnVuY3Rpb24oWCxLMCx4MCwgbGFtYmRhKXsNCiAgcmV0dXJuKG1heChLMCAtIGxhbWJkYSooWCAtIHgwKSoqMiwgMTAqKigtOSkpKQ0KfQ0KDQoNCiMgRm9uY3Rpb24gcG91ciBvYnRlbmlyIGxlcyB2YWxldXJzIGRlIGxhIG1hdHJpY2UgQQ0KY2FsY3VsZUEgPC0gZnVuY3Rpb24oWCwgbmJTcGVjaWVzLCBzaWdtYUFfc3F1YXJlZCl7DQogIEQgPSBtYXRyaXgoZGF0YT1YLG5jb2w9bmJTcGVjaWVzLG5yb3c9bmJTcGVjaWVzKSAjIG1hdHJpY2UNCiAgQyA9IHQoRCkgLSBEDQogIHJldHVybihleHAoLUMqKjIvKDIqc2lnbWFBX3NxdWFyZWQpKSkNCn0NCg0KDQojIEZvbmN0aW9uIHBvdXIgY2FsY3VsZXIgbGEgbWF0cmljZSBCDQpjYWxjdWxlQiA8LSBmdW5jdGlvbihYLCBuYlNwZWNpZXMsIEswLCB4MCwgbGFtYmRhKXsNCiAgdmVjdEsgPSBzYXBwbHkoWCwgRlVOPSBmdW5jdGlvbih4KSB7IGNhbGN1bGVLKHgsIEswLCB4MCwgbGFtYmRhKSB9KQ0KICBCID0gbWF0cml4KGRhdGEgPSAxL3ZlY3RLLCBucm93PW5iU3BlY2llcywgbmNvbD1uYlNwZWNpZXMsIGJ5cm93PVRSVUUpDQogIHJldHVybihCKQ0KfQ0KDQoNCiMgRm9uY3Rpb24gcGFyIGNhbHVjbGUgbGEgbWF0cmljZSBNIHF1aSBlc3QgbGUgcHJvZHVpdCBkZSBBIGV0IEINCmNhbGN1bGVNID0gZnVuY3Rpb24oWCwgbmJTcGVjaWVzLCBzaWdtYUFfc3F1YXJlZCwgSzA9MSwgbGFtYmRhPTEpew0KICB4MCA9IG1pbihYKSArIChtYXgoWCkgLSBtaW4oWCkpIC8gMiAgDQogIA0KICBBID0gY2FsY3VsZUEoWCwgbmJTcGVjaWVzLCBzaWdtYUFfc3F1YXJlZCkgIyBtYXRyaWNlIEENCiAgQiA9IGNhbGN1bGVCKFgsIG5iU3BlY2llcywgSzAsIHgwLCBsYW1iZGEpICMgbWF0cmljZSBCDQogIA0KICBNPUEqQiAjIHByb2R1aXQgdGVybWUgYSB0ZXJtZSBkZSBBIGF2ZWMgQg0KICByZXR1cm4oTSkNCn0NCg0KDQojIEZvbmN0aW9ucyBwb3VyIGTDqWZpbml0aW9uIGwnZXF1YXRpb24gZGlmZmVyZW50aWVsbGUgDQpjYWxjdWxlR3JhZGllbnRMViA9IGZ1bmN0aW9uKHQsIE5pLCBwYXJhbXMpew0KICB3aXRoKGFzLmxpc3QocGFyYW1zKSwgew0KICAgIA0KICAgIGROaSA9IHIqTmkqKDEgLSBOaSUqJU0pDQoNCiAgICByZXR1cm4obGlzdChkTmkpKQ0KICB9KQ0KfQ0KDQoNCiMgRm9uY3Rpb25zIHBvdXIgcmVzb3VkcmUgbCdlcXVhdGlvbiBkaWZmZXJlbnRpZWxsZSBhdmVjIG9kZSBkdSBwYWNrYWdlIGRlU29sdmUNCnJlc291dEVxZCA8LSBmdW5jdGlvbihwYXJhbWV0ZXJzLCBkZW5zaXRlX3QwLCB2ZWN0X3RpbWUsIHZlY3RfcGhlbm90eXBlcyl7DQogIGRmX2RlbnNpdGUgPSBvZGUoeSA9IGRlbnNpdGVfdDAsIHRpbWVzID0gdmVjdF90aW1lLCBmdW5jID0gY2FsY3VsZUdyYWRpZW50TFYsIHBhcmFtZXRlcnMpICU+JQ0KICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUNCiAgICBwaXZvdF9sb25nZXIoLXRpbWUsIHZhbHVlc190byA9ICJkZW5zaXR5IiwgbmFtZXNfdG8gPSAic3BlY2llcyIpDQoNCiAgZGZfZGVuc2l0ZSRYID0gdmVjdF9waGVub3R5cGVzW2FzLm51bWVyaWMoZGZfZGVuc2l0ZSRzcGVjaWVzKV0NCiAgICANCiAgcmV0dXJuKGRmX2RlbnNpdGUpIyByZXRvdXJuZSBzb3J0aWUgbW9kaWZpZXIgZGUgb2RlDQp9DQoNCg0KIyBEw6lmaW5pdGlvbiBkZSBsYSBmb25jdGlvbiBwb3VyIHNpbXVsZXIgbGUgZHluYW1pcXVlIGRlIGxhIHBvcHB1bGF0aW9uDQpzaW11bGVEeW5hbWlxdWUgPC0gZnVuY3Rpb24obmJTcGVjaWVzLCBzaWdtYUFfc3F1YXJlZCwgdmVjdF90ZW1wcywgSzA9MSwgcj0xKXsNCiAgbjAgPSByZXAoSzAvbmJTcGVjaWVzLCBuYlNwZWNpZXMpIA0KICB2ZWN0X3BoZW5vdHlwZXMgPSBzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IG5iU3BlY2llcykNCg0KICBNID0gY2FsY3VsZU0odmVjdF9waGVub3R5cGVzLCBuYlNwZWNpZXMsIHNpZ21hQV9zcXVhcmVkLCBLMCkNCiAgcGFyYW1zID0gbGlzdChyID0gciwgTSA9IE0pDQogIGRmX2RlbnNpdGUgPSByZXNvdXRFcWQocGFyYW1zLCBuMCwgdmVjdF90ZW1wcywgdmVjdF9waGVub3R5cGVzKQ0KICANCiAgcmV0dXJuKGRmX2RlbnNpdGUpICMgbGEgZGVuc2l0w6kgZGUgbGEgcG9wdWxhdGlvbiBvYnRlbnUgYXZlYyBvZGUNCn0NCg0KYGBgDQoNCg0KDQoqIExlcyBmb25jdGlvbnMgcG91ciBsZXMgcmVwcsOpc2VudGF0aW9ucyBncmFwaGlxdWVzDQpgYGB7ciB9DQoNCiMgZMOpZmluaXRpb24gZGVzIHRow6htZXMNCm1haW5fdGhlbWUgPSB0aGVtZV9idygpKw0KICB0aGVtZShsaW5lID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MgPSAgZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCBzaXplPTEwKSwNCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoY29sb3VyID0gImJsYWNrIiwgc2l6ZT0xMCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsDQogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTApKQ0KDQojIGZvbmN0aW9uIHBvdXIgaWxsdXN0cmVyIGxlIGR5bmFtaXF1ZSBkZSBsYSBwb3B1bGF0aW9uIHNlbG9uIGxlIHRlbXBzDQpwbG90RHluYW1pcXVlIDwtIGZ1bmN0aW9uKGRmX2RlbnNpdHksIHBsb3RfdGhlbWU9bWFpbl90aGVtZSwgdGl0bGU9ICIiKXsNCiAgcGxvdCA9IGdncGxvdChkZl9kZW5zaXR5KSsNCiAgICBnZW9tX2xpbmUoYWVzKHRpbWUsIGRlbnNpdHksIGNvbCA9IHNwZWNpZXMpKSArDQogICAgZ2d0aXRsZShsYWJlbCA9IHRpdGxlKSArDQogICAgeGxhYigiVGVtcHMiKSArDQogICAgeWxhYigiUGjDqW5vdHlwZXMiKSArDQogICAgcGxvdF90aGVtZQ0KICByZXR1cm4ocGxvdCkNCn0NCg0KIyBmb25jdGlvbiBwb3VyIGlsbHVzdHJlciBsJ8Opdm9sdXRpb24gZGUgbGEgZHluYW1pcXVlIHNlbG9uIGxlcyBwaMOpbm90eXBlcw0KcGxvdEV2b2x1dGlvbnRyYWl0IDwtIGZ1bmN0aW9uKGRmX2RlbnNpdHksIHBsb3RfdGhlbWU9bWFpbl90aGVtZSl7DQogIHBsb3QgPSBnZ3Bsb3QoZGF0YSA9IGRmX2RlbnNpdHkpICsNCiAgICBnZW9tX3RpbGUoYWVzKHggPSBYLCB5ID0gdGltZSwgZmlsbCA9IGRlbnNpdHkpKSsNCiAgICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiICwgaGlnaCA9ICJyZWQiKSsNCiAgICB4bGFiKCJQaMOpbm90eXBlcyIpICsNCiAgICB5bGFiKCJUZW1wcyIpICsNCiAgICBwbG90X3RoZW1lDQogIA0KICByZXR1cm4ocGxvdCkNCn0NCg0KYGBgDQoNCiANCg0KIyMgU2ltdWxhdGlvbiBzYW5zIG11dGF0aW9uDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQp2ZWN0X3RlbXBzID0gMDoyMDAwICMgdGVtcHMgZGUgc2ltdWxhdGlvbg0KSzAgPSAxICAgICAgICAgICAgICMgY2FwYWNpdGUgZGUgY2hhcmdlIGR1IG1pbGlldSB0b3V0IHBoZW5vdHlwZSBjb25mb25kdQ0Kc2lnbWFBX3NxdWFyZWQgPSAxICMgdmFyaWUgZW50cmUgMC4xIGV0IDEwDQpyPTEgICAgICAgICAgICAgICAgIyB0YXV4IGRlIGNyb2lzc2FuY2UsIGxlIG3Dqm1lIHBvdXIgdG91dGVzIGxlcyBlc3DDqGNlcw0KDQoNCiMgU2ltdWxhdGlvbiBkZSBsYSBkeW5hbWlxdWUgYXZlYyAxMCBwaGVub3R5cGVzDQpuYlNwZWNpZXMgPSAxMCAjIG5vbWJyZSBkJ2VzcGVjZXMNCmRmX2RlbnNpdGUxMCA9IHNpbXVsZUR5bmFtaXF1ZShuYlNwZWNpZXMsIHNpZ21hQV9zcXVhcmVkLCB2ZWN0X3RlbXBzKQ0KDQpwbG90MSA9IHBsb3REeW5hbWlxdWUoZGZfZGVuc2l0ZTEwLCB0aXRsZT0iRXZvbHV0aW9uIGRlcyBwaMOpbm90eXBlcyAoTiA9IDEwKSIgKQ0KcGxvdDIgPSBwbG90RXZvbHV0aW9udHJhaXQoZGZfZGVuc2l0ZTEwKQ0KDQoNCiMgU2ltdWxhdGlvbiBkZSBsYSBkeW5hbWlxdWUgYXZlYyAxMDAgcGhlbm90eXBlcw0KbmJTcGVjaWVzID0gMTAwICMgbm9tYnJlIGQnZXNwZWNlcw0KZGZfZGVuc2l0ZTEwMCA9IHNpbXVsZUR5bmFtaXF1ZShuYlNwZWNpZXMsIHNpZ21hQV9zcXVhcmVkLCB2ZWN0X3RlbXBzKQ0KDQpwbG90MyA9IHBsb3REeW5hbWlxdWUoZGZfZGVuc2l0ZTEwMCwgdGl0bGU9IkV2b2x1dGlvbiBkZXMgcGjDqW5vdHlwZXMgKE4gPSAxMDApIikNCnBsb3Q0ID0gcGxvdEV2b2x1dGlvbnRyYWl0KGRmX2RlbnNpdGUxMDApDQoNCnBsb3RfZ3JpZChsYWJlbHMgPSAiYXV0byIscGxvdGxpc3Q9bGlzdChwbG90MSxwbG90MixwbG90MyxwbG90NCkpDQoNCmBgYA0KDQpBdmVjICRcc2lnbWFeMiA9IDEkLCB1bmUgdmFsZXVyIGludGVybcOpZGlhaXJlLCBvbiBvYnNlcnZlIHF1ZSB0b3V0ZXMgbGVzIGVzcMOoY2VzIHMnw6lsb2lnbmFudCB0cm9wIGRlIGwnb3B0aW11bSBzb250IMOpbGltaW7DqWVzLiBMYSB0ZW5kYW5jZSBzZW1ibGUgZG9uYyBwbHV0w7R0IMOqdHJlIGwnw6lsaW1pbmF0aW9uIHBhciBjb21ww6l0aXRpb24gcXVlIGxhIGNvZXhpc3RlbmNlIGRlIHBow6lub3R5cGUuDQoNCg0KIyMgQ29leGlzdGVuY2UgZGVzIHBow6lub3R5cGVzIGV0IHZhbGV1cnMgZGUgc2lnbWENCg0KT24gcGV1dCBzZSBwb3NlciBsYSBxdWVzdGlvbiBzdWl2YW50ZSA6IENvbWJpZW4gZCdlc3DDqGNlcyBwZXJzaXN0ZXJvbnQgZGFucyBsZSB0ZW1wcyBzZWxvbiBsYSB2YWxldXIgZGUgc2lnbWEgPw0KDQpgYGB7ciwgZmlnLmFsaWduPSdjZW50ZXInfQ0KdmVjdF90ZW1wcyA9IDA6MjAwMCAjIHRlbXBzIGRlIHNpbXVsYXRpb24NCnNpZ21hQV9zcXVhcmVkID0gYygwLjEsIDAuNSwgMSwgMTApICMgTGVzIGRpZmZlcmVudHMgdmFsZXVycyBkZSBzaWdtYQ0KDQpwbG90X2xpc3QgPSBsaXN0KCkNCg0KbmJTcGVjaWVzID0gMTAwDQpmb3IgKGkgaW4gMTpsZW5ndGgoc2lnbWFBX3NxdWFyZWQpKXsNCiAgZGZfZGVuc2l0ZSA9IHNpbXVsZUR5bmFtaXF1ZShuYlNwZWNpZXMsIHNpZ21hQV9zcXVhcmVkW2ldLCB2ZWN0X3RlbXBzKQ0KICBwbG90X2xpc3RbW2ldXSA9IHBsb3REeW5hbWlxdWUoZGZfZGVuc2l0ZSwgdGl0bGU9cGFzdGUoIkNhcyBOPSAxMDAsIHNpZ21hwrIgPSAiLCBzaWdtYUFfc3F1YXJlZFtpXSkpDQogICNwbG90X2xpc3RbW2ldXSA9IHBsb3RFdm9sdXRpb250cmFpdChkZl9kZW5zaXRlKSkNCn0NCg0KcGxvdF9ncmlkKGxhYmVscyA9ICJhdXRvIixwbG90bGlzdCA9IHBsb3RfbGlzdCkNCmBgYA0KDQpBdmVjICROID0gMTAwJCBPbiBjb25zdGF0ZSBxdSfDoCBtZXN1cmUgcXVlICRcc2lnbWFeMiQgYXVnbWVudGUsIGxlIG5vbWJyZSBkZSBwaMOpbm90eXBlcyBxdWkgcGVyc2lzdGVudCDDoCBsb25nIHRlcm1lIGF1Z21lbnRlIGVudHJlIDAuMSBldCAwLjUuIExvcnNxdWUgJFxzaWdtYV4yJCBlc3Qgc3Vww6lyaWV1ciDDoCAkMSQsIGNlcnRhaW5zIHBow6lub3R5cGVzIHN1YnNpc3RlbnQgaW5pdGlhbGVtZW50IG1haXMgZmluaXNzZW50IHJhcGlkZW1lbnQgcGFyIHMnw6l0ZWluZHJlLCBldCBhdSBib3V0IGRlICQyMDAwJCBwYXMgZGUgdGVtcHMgZGUgc2ltdWxhdGlvbiwgdW4gc2V1bCBwaMOpbm90eXBlIHN1YnNpc3RlLg0KDQoNClJlZ2FyZG9ucyBtYWludGVuYW50IGxlIG5vbWJyZSBkZSBwaMOpbm90eXBlcyBxdWkgcGVyc2lzdGVudCBzZWxvbiBwbHVzaWV1cnMgdmFsZXVycyBkZSAkc2lnbWHCsiQuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcicsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpkZW5zaXR5X21pbiA9IDAuMDA1ICMgZW4gZGVzc291cyBkZSBjZXR0ZSBkZW5zaXTDqSBsZSBwaMOpbm90eXBlIGVzdCBjb25zaWTDqXLDqSBkaXNwYXJ1DQoNCnZlY3RfdGVtcHMgPSAwOjIwMDAgIyB0ZW1wcyBkZSBzaW11bGF0aW9uDQpzaWdtYUFfc3F1YXJlZCA9IGMoc2VxKDAuMSwxLCBieT0wLjEpLCBzZXEoMiwxMCkpICMgbGVzIHZhbGV1cnMgZGUgc2lnbWHCsiBhIHRlc3Rlcg0KDQpkZl9wZXJzaXRhbmNlID0gZGF0YS5mcmFtZSgpDQpuYlNwZWNpZXMgPSAxMDAgIyBOb21icmUgZCdlc3DDqGNlcw0KZm9yIChpIGluIDE6bGVuZ3RoKHNpZ21hQV9zcXVhcmVkKSl7DQogIGRmX2RlbnNpdGUgPSBzaW11bGVEeW5hbWlxdWUobmJTcGVjaWVzLCBzaWdtYUFfc3F1YXJlZFtpXSwgdmVjdF90ZW1wcykNCiAgbmJfcGVyc2lzdGFudCA9IGRmX2RlbnNpdGVbZGZfZGVuc2l0ZSR0aW1lID09IG1heChkZl9kZW5zaXRlJHRpbWUpLF0gJT4lDQogICAgZmlsdGVyKGRlbnNpdHk+ZGVuc2l0eV9taW4pICU+JQ0KICAgIG5yb3coKQ0KICANCiAgZGZfcGVyc2l0YW5jZSA9IHJiaW5kKGRmX3BlcnNpdGFuY2UsYyhzaWdtYUFfc3F1YXJlZFtpXSxuYl9wZXJzaXN0YW50KSkNCn0NCg0KY29sbmFtZXMoZGZfcGVyc2l0YW5jZSkgPSBjKCJzaWdtYV9zcXVhcmUiLCAibmJfcGVyc2lzdGFudCIpDQoNCmdncGxvdChkYXRhID0gZGZfcGVyc2l0YW5jZSwgYWVzKHggPSBzaWdtYV9zcXVhcmUsIHkgPSBuYl9wZXJzaXN0YW50KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiTm9tYnJlIGRlIHBow6lub3R5cGVzIHBlcnNpc3RhbnRzIHNlbG9uIHNpZ21hwrIiLA0KICAgICAgIHN1YnRpdGxlID0gIkNhcyBOID0gMTAwIiwNCiAgICB4ID0gIlNpZ21hwrIiLA0KICAgIHkgPSAiTm9tYnJlIGRlIHBow6lub3R5cGVzIg0KICApICsNCiAgbWFpbl90aGVtZQ0KDQoNCg0KYGBgDQoNCkNvbW1lIG9uIHMneSBhdHRlbmRhaXQgbGUgbm9tYnJlIGQnZXNww6hjZXMgcGVyc2lzdGFudGVzIGRpbWludWUgbG9yc3F1ZSAkXHNpZ21hXjIkIGF1Z21lbnRlLg0KDQpZIGEtdC1pbCB1bmUgbGltaXRlIGF1IG5vbWJyZSBkJ2VzcMOoY2VzIHF1aSBwZXV2ZW50IGNvZXhpc3RlciBkYW5zIGNlIG1vZMOobGUgPyBBIHByaW9yaSBpbCBuJ3kgcGFzIGRlIGxpbWl0ZSBjYXIgb24gcHJlbmQgdW5lIGZvbmN0aW9uIGdhdXNzaWVubmUsIG1haXMgdW5lIGxpbWl0ZSBwb3VycmFpdCBhcHBhcmFpdHJlIHNpIG9uIHByZW5haXMgZCdhdXRyZXMgZGlzdHJpYnV0aW9ucy4gDQoNCg0KIyMgU2ltdWxhdGlvbiBhdmVjIGFqb3V0IGRlIG11dGF0aW9ucyBkYW5zIGxhIGR5bmFtaXF1ZQ0KDQpEw6lmaW5pdGlvbiBkZXMgZm9uY3Rpb24gcG91ciBzaW11bGVyIGxhIG11dGF0aW9uLg0KDQpQb3VyIHNpbXVsZXIgZGVzIG11dGF0aW9ucywgbGUgcHJpbmNpcGUgdmEgw6p0cmUgbGUgc3VpdmFudCA6DQogIA0KICAxLiBPbiB0aXJlIGxlcyB0ZW1wcyBhdXhxdWVscyBsZXMgbXV0YXRpb25zIHZvbnQgYXZvaXIgbGlldSBkZSBtYW5pw6hyZSBhbMOpYXRvaXJlDQogIA0KICANCiAgMi5hIE9uIHNpbXVsZSBsYSBkeW5hbWlxdWUgZW50cmUgdDAgZXQgbGUgcHJlbWllciB0ZW1wcyBkZSBtdXRhdGlvbg0KICANCiAgDQogIDIuYiBBdSB0ZW1wcyBkZSBtdXRhdGlvbiwgb24gcsOpY3Vww6hyZSBsZXMgZGVuc2l0w6lzIGZpbmFsZXMgcHVpcyBvbiBhcHBsaXF1ZSBsYSBtdXRhdGlvbiwgcXVpIGNvbnNpc3RlIMOgICJkw6ljYWxlciIgbGVzIHBow6lub3R5cGVzIHZlcnMgbGEgZHJvaXRlIG91IGxhIGdhdWNoZSBlbiBkw6lwbGHDp2FudCB1bmUgY2VydGFpbmVzIHByb3BvcnRpb24gZGUgbGEgZGVuc2l0w6kgZGFucyBsZSBwaMOpbm90eXBlIGp1c3RlIMOgIGRyb2l0ZSBvdSBqdXN0ZSDDoCBnYXVjaGUuIA0KICANCiAgDQogIDIuYyBPbiByZXRvdXJuZSDDoCBsJ8OpdGFwZSAyLmEgZXQgb24gc2ltdWxlIGxhIGR5bmFtaXF1ZSBlbnRyZSBsZSB0ZW1wcyBkZSBtdXRhdGlvbiBldCBsZSBzdWl2YW50IGVuIHByZW5hbnQgY29tbWUgY29uZGl0aW9uIGluaXRpYWxlIGxlIHZlY3RldXIgZGVzIGRlbnNpdMOpcyBtdXTDqWVzLg0KICANCiAgDQogIA0KKipEw6lmaW5pdGlvbiBkZXMgZm9uY3Rpb25zIHV0aWxlcyA6KioNCg0KYGBge3IgfQ0KDQojIGZvbmN0aW9uIHBvdXIgb2J0ZW5pciBsZXMgdGVtcHMgYXV4cXVlbHMgaWwgeSBhdXJhIHVuZSBtdXRhdGlvbiANCmdldE11dGF0aW9uVGltZSA8LSBmdW5jdGlvbih2ZWN0X3RlbXBzLCB0YXV4X211dGF0aW9uKXsNCiAgU2ltdWxhdGlvbkR1cmF0aW9uID0gbWF4KHZlY3RfdGVtcHMpDQogICMgRGV0ZXJtaW5hdGlvbiBkZXMgaW5zdGFudHMgYXV4cXVlbHMgaWwgeSBhdXJhIHVuZSBtdXRhdGlvbg0KICBuYl9tdXRhdGlvbnNfbW95ID0gU2ltdWxhdGlvbkR1cmF0aW9uICogdGF1eF9tdXRhdGlvbiANCiAgbmJfbXV0YXRpb25zX3NkID0gbmJfbXV0YXRpb25zX21veSAvIDQNCiAgbmJfbXV0YXRpb25zID0gbWF4KDEsZmxvb3Iocm5vcm0oMSwgbWVhbj1uYl9tdXRhdGlvbnNfbW95LCBzZD1uYl9tdXRhdGlvbnNfc2QpKSkNCiAgDQogIHRlbXBzX211dGF0aW9uID0gYygwLHNvcnQoc2FtcGxlKHggPSB2ZWN0X3RlbXBzLCBzaXplID0gbmJfbXV0YXRpb25zKSksU2ltdWxhdGlvbkR1cmF0aW9uKQ0KICByZXR1cm4odGVtcHNfbXV0YXRpb24pDQp9DQoNCg0KIyBmb25jdGlvbiBwb3VyIGxlIGTDqWNhbGFnZSAoZHJvaXRlIG91IGdhdWNoZSkgZGUgbGEgbXV0YXRpb24NCm11dGF0aW9uUGFyRGVjYWxhZ2UgPC0gZnVuY3Rpb24oZGZfZGVuc2l0ZSwgcHJvcG9ydGlvbk11dGFudCkgew0KICANCiAgbjAgPSBkZl9kZW5zaXRlJGRlbnNpdHlbZGZfZGVuc2l0ZSR0aW1lPT1tYXgoZGZfZGVuc2l0ZSR0aW1lKV0gDQogIG5iU3BlY2llcyA9IGxlbmd0aChuMCkNCiAgIyBtdXRhdGlvbiA6IGRlY2FsYWdlIGQndW5lIHBhcnRpZSBkZXMgZWZmZWN0aWZzDQogIHAgPSBydW5pZigxKSAjIHRpcmFnZSBhbMOpYXRvaXJlIGVudHJlIDAgZXQgMQ0KDQogIGlmIChwIDwgLjcpew0KICAjIGRlY2FsYWdlIMOgIGRyb2l0ZQ0KICBlZmZlY3RpZk11dGFudHMgPSBuMFsxOihuYlNwZWNpZXMtMSldKnByb3BvcnRpb25NdXRhbnQNCiAgICANCiAgbjBbMToobmJTcGVjaWVzLTEpXSA9IG4wWzE6KG5iU3BlY2llcy0xKV0gLSBlZmZlY3RpZk11dGFudHMNCiAgbjBbMjpuYlNwZWNpZXNdID0gbjBbMjpuYlNwZWNpZXNdICsgZWZmZWN0aWZNdXRhbnRzDQogIH0NCiAgZWxzZXsNCiAgIyBkZWNhbGFnZSBhIGdhdWNoZQ0KICBlZmZlY3RpZk11dGFudHMgPSBuMFsyOm5iU3BlY2llc10qcHJvcG9ydGlvbk11dGFudA0KICAgIA0KICBuMFsxOihuYlNwZWNpZXMtMSldID0gbjBbMToobmJTcGVjaWVzLTEpXSArIGVmZmVjdGlmTXV0YW50cw0KICBuMFsyOm5iU3BlY2llc10gPSBuMFsyOm5iU3BlY2llc10gLSBlZmZlY3RpZk11dGFudHMNCiAgfQ0KICByZXR1cm4objApDQp9DQoNCg0KIyBmb25jdGlvbiBwb3VyIGxhIHNpbXVsYXRpb24gZGUgbGEgbXV0YXRpb24NCnNpbXVsZUR5bmFtaXF1ZU11dGF0aW9uIDwtIGZ1bmN0aW9uKG5iU3BlY2llcywgc2lnbWFBX3NxdWFyZWQsIHRlbXBzX211dGF0aW9uLCBLMD0xLCByPTEpew0KICANCiAgIyBDYWxjdWxlIGRlcyBwaGVub3R5cGVzIGV0IGRlIE0NCiAgdmVjdF9waGVub3R5cGVzID0gc2VxKDAsIDEsIGxlbmd0aC5vdXQgPSBuYlNwZWNpZXMpDQogIE0gPSBjYWxjdWxlTSh2ZWN0X3BoZW5vdHlwZXMsIG5iU3BlY2llcywgc2lnbWFBX3NxdWFyZWQsIEswKQ0KICBwYXJhbXMgPSBsaXN0KHIgPSByLCBNID0gTSkNCiAgDQogICMgSW5pdGlhbGlzYXRpb24gZGUgbjAgZXQgZGUgZGZfZGVuc2l0ZQ0KICBuMCA9IGMoSzAsIHJlcCgwLCBuYlNwZWNpZXMtMSkpDQogIGRmX2RlbnNpdGUgPSBkYXRhLmZyYW1lKCkNCg0KICAjIFNpbXVsYXRpb24gZGUgbGEgZHluYW1pcXVlIHBvdXIgdG91dCBsZXMgdGVtcHMgZGUgbXV0YXRpb24NCiAgZm9yIChpIGluIDI6bGVuZ3RoKHRlbXBzX211dGF0aW9uKSl7DQogICAgaWYgKHRlbXBzX211dGF0aW9uW2ldLXRlbXBzX211dGF0aW9uW2ktMV0+MSl7DQogICAgICAjIEVudHJlIDIgbXV0YXRpb24gb24gc2ltdWxlIGVuIHJlc29sdmFudCBsJ2VxdWF0aW9uIGRpZmZlcmVudGllbGxlDQogICAgICBzYW1wbGVfdmVjdF90ZW1wcyA9IHNlcSh0ZW1wc19tdXRhdGlvbltpLTFdLHRlbXBzX211dGF0aW9uW2ldLTEpDQogICAgICBkZl9kZW5zaXRlID0gcmJpbmQoZGZfZGVuc2l0ZSxyZXNvdXRFcWQocGFyYW1zLCBuMCwgc2FtcGxlX3ZlY3RfdGVtcHMsIHZlY3RfcGhlbm90eXBlcykpDQogICAgICAjIEEgY2hhcXVlIHRlbXBzIGNvbnRlbnUgZGFucyB0ZW1wc19tdXRhdGlvbiB1bmUgbXV0YXRpb24gYSBsaWV1IA0KICAgICAgbjAgPSBtdXRhdGlvblBhckRlY2FsYWdlKGRmX2RlbnNpdGUsIHByb3BvcnRpb25NdXRhbnQpICMgbm8gZGV2aWVudCBsZSBubyBhdmVjIG11dGF0aW9uDQogICAgfQ0KICAgIA0KICB9DQogIHJldHVybihkZl9kZW5zaXRlKSAjIHBvcCB0b3QgYXZlYyBtdXRhdGlvbg0KfQ0KDQoNCmBgYA0KDQoNCioqVmlzdWFsaXNhdGlvbiBkZSBsYSBkeW5hbWlxdWUgYXZlYyAxMCBldCAxMDAgZXNww6hjZXMsIGV0IHVuIHRhdXggZGUgbXV0YXRpb24gZGUgMS8xMDAsIHNvaXQgZW4gbW95ZW5uZSB1bmUgbXV0YXRpb25zIHRvdXMgbGVzIDEwMCBwYXMgZGUgdGVtcHMuKiogDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcid9DQp2ZWN0X3RlbXBzID0gMDoyMDAwICMgVGVtcHMgZGUgc2ltdWxhdGlvbg0Kc2lnbWFBX3NxdWFyZWQgPSAwLjQgIyBzaW1nYcKyDQpwcm9wb3J0aW9uTXV0YW50ID0gMC4xICMgUHJvcG9ydGlvbiBkdSBub21icmUgZGUgcGjDqW5vdHlwZSBtdXRhbnQNCnRhdXhfbXV0YXRpb24gPSAxLzEwMCAjIHRhdXggZGUgbXV0YXRpb24NCg0KDQojIFNpbXVsYXRpb24gZGUgbGEgZHluYW1pcXVlIGF2ZWMgMTAgcGhlbm90eXBlcw0KbmJTcGVjaWVzID0gMTANCnRlbXBzX211dGF0aW9uID0gZ2V0TXV0YXRpb25UaW1lKHZlY3RfdGVtcHMsIHRhdXhfbXV0YXRpb24pDQpkZl9kZW5zaXRlID0gc2ltdWxlRHluYW1pcXVlTXV0YXRpb24obmJTcGVjaWVzLCBzaWdtYUFfc3F1YXJlZCwgdGVtcHNfbXV0YXRpb24pDQoNCnBsb3QxID0gcGxvdER5bmFtaXF1ZShkZl9kZW5zaXRlLCB0aXRsZT0iQ2FzIE4gPSAxMCIpIA0KcGxvdDEgPSBwbG90MSArIGdlb21fdmxpbmUoeGludGVyY2VwdD10ZW1wc19tdXRhdGlvbiwgbGluZXR5cGU9JzE4JywgY29sb3I9J3JlZCcpDQoNCnBsb3QyID0gcGxvdEV2b2x1dGlvbnRyYWl0KGRmX2RlbnNpdGUpDQoNCg0KDQojIFNpbXVsYXRpb24gZGUgbGEgZHluYW1pcXVlIGF2ZWMgMTAwIHBoZW5vdHlwZXMNCm5iU3BlY2llcyA9IDEwMA0KdGVtcHNfbXV0YXRpb24gPSBnZXRNdXRhdGlvblRpbWUodmVjdF90ZW1wcywgdGF1eF9tdXRhdGlvbikNCmRmX2RlbnNpdGUxMDAgPSBzaW11bGVEeW5hbWlxdWVNdXRhdGlvbihuYlNwZWNpZXMsIHNpZ21hQV9zcXVhcmVkLCB0ZW1wc19tdXRhdGlvbikNCiAgDQpwbG90MyA9IHBsb3REeW5hbWlxdWUoZGZfZGVuc2l0ZTEwMCwgdGl0bGU9IkNhcyBOID0gMTAwIikgDQpwbG90MyA9IHBsb3QzICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0PXRlbXBzX211dGF0aW9uLCBsaW5ldHlwZT0nMTgnLCBjb2xvcj0ncmVkJykNCg0KcGxvdDQgPSBwbG90RXZvbHV0aW9udHJhaXQoZGZfZGVuc2l0ZTEwMCkNCg0KcGxvdF9ncmlkKHBsb3RsaXN0PWxpc3QocGxvdDEscGxvdDIscGxvdDMscGxvdDQpKQ0KDQpgYGANCg0KTG9yc3F1ZSBsJ29uIGZhaXQgdW5lIHNpbXVsYXRpb24gYXZlYyBkZXMgbXV0YXRpb24sIG9uIHMnYXR0ZW5kIMOgIGNlIHF1ZSBsZXMgbXV0YXRpb25zIHJhbcOobmVudCBwcm9ncmVzc2l2ZW1lbnQgbGVzIHZhbGV1cnMgZGUgcGjDqW5vdHlwZXMgdmVycyBsJ29wdGltdW0uIERhbnMgY2VydGFpbnMgY2FzLCBlbiBkeW5hbWlxdWUgYWRhcHRhdGl2ZSBvbiBwZXV0IG9ic2VydmVyIGRlcyBwb2ludHMgZGUgYnJhbmNoZW1lbnQuIFBvdXIgY2VsYSBpbCBmYXV0IHF1J3VuIHBvaW50IHNpbmd1bGllciBkZSBsYSBkeW5hbWlxdWUgc29pdCBhdHRyYWN0ZXVyIG1haXMgaW5zdGFibGUsIGV0IHF1J2lsIHB1aXNzZSDDqnRyZSBlbnZhaGkgcGFyIGRlcyBwaMOpbm90eXBlIGRlIHZhbGV1ciBpbmbDqXJpZXVyZSBldCBzdXDDqXJpZXVyZSDDoCBzYSB2YWxldXIuIA0KDQpPbiBhIGwnaW1wcmVzc2lvbiBkJ29iZXJ2ZXIgdW4gcG9pbnQgZGUgYnJhbmNoZW1lbnQgYXZlYyBub3MgMTAgcGjDqW5vdHlwZXMuIEVuIHJldmFuY2hlLCBvbiBvYnNlcnZlIHF1ZSBhdmVjIDEwMCBwaMOpbm90eXBlcywgb24gbidhIHBhcyBsZSB0ZW1wcyBkZSByZWpvaW5kcmUgbCdvcHRpbXVtLiBPbiB2YSByZWZhaXJlIHVuZSBzaW11bGF0aW9uIGF2ZWMgMTAwIHBow6lub3R5cGVzIGVuIHByZW5hbnQgMiB0YXV4IGRlIG11dGF0aW9ucyBkaWZmw6lyZW50cy4NCg0KYGBge3IsIGZpZy5hbGlnbj0nY2VudGVyJ30NCnZlY3RfdGVtcHMgPSAwOjEwMDAwDQpzaWdtYUFfc3F1YXJlZCA9IDAuNA0KcHJvcG9ydGlvbk11dGFudCA9IDAuMQ0KDQoNCiMgU2ltdWxhdGlvbiBkZSBsYSBkeW5hbWlxdWUgYXZlYyA1MCBwaGVub3R5cGVzIGV0IHRhdXhfbXV0YXRpb24gPSAxLzIwMA0KbmJTcGVjaWVzID0gNTANCnRhdXhfbXV0YXRpb24gPSAxLzIwMA0KdGVtcHNfbXV0YXRpb24gPSBnZXRNdXRhdGlvblRpbWUodmVjdF90ZW1wcywgdGF1eF9tdXRhdGlvbikNCmRmX2RlbnNpdGUgPSBzaW11bGVEeW5hbWlxdWVNdXRhdGlvbihuYlNwZWNpZXMsIHNpZ21hQV9zcXVhcmVkLCB0ZW1wc19tdXRhdGlvbikNCg0KcGxvdDEgPSBwbG90RHluYW1pcXVlKGRmX2RlbnNpdGUsIHRpdGxlPSJUYXV4IGRlIG11dGF0aW9uID0gMC4wMDUiKSANCnBsb3QxID0gcGxvdDENCg0KcGxvdDIgPSBwbG90RXZvbHV0aW9udHJhaXQoZGZfZGVuc2l0ZSkNCg0KDQoNCiMgU2ltdWxhdGlvbiBkZSBsYSBkeW5hbWlxdWUgYXZlYyAxMDAgcGhlbm90eXBlcyBldCB0YXV4X211dGF0aW9uID0gMS81MA0KbmJTcGVjaWVzID0gNTANCnRhdXhfbXV0YXRpb24gPSAxLzUwDQp0ZW1wc19tdXRhdGlvbiA9IGdldE11dGF0aW9uVGltZSh2ZWN0X3RlbXBzLCB0YXV4X211dGF0aW9uKQ0KZGZfZGVuc2l0ZTEwMCA9IHNpbXVsZUR5bmFtaXF1ZU11dGF0aW9uKG5iU3BlY2llcywgc2lnbWFBX3NxdWFyZWQsIHRlbXBzX211dGF0aW9uKQ0KICANCnBsb3QzID0gcGxvdER5bmFtaXF1ZShkZl9kZW5zaXRlMTAwLCB0aXRsZT0iVGF1eCBkZSBtdXRhdGlvbiA9IDAuMDIiKSANCnBsb3QzID0gcGxvdDMNCg0KcGxvdDQgPSBwbG90RXZvbHV0aW9udHJhaXQoZGZfZGVuc2l0ZTEwMCkNCg0KcGxvdF9ncmlkKHBsb3RsaXN0PWxpc3QocGxvdDEscGxvdDIscGxvdDMscGxvdDQpKQ0KDQpgYGANCg0KQXZlYyB1biB0YXV4IGRlIG11dGF0aW9uIGZhaWJsZSwgbGEgZGVuc2l0w6kgZXN0IHRvdWpvdXJzIHRyw6hzIHJlc3NlcnLDqWUgYXV0b3VyIGRlIHBldSBkZSBwaMOpbm90eXBlIGRvbWluYW50IChsZSB0cmFpdCBlc3QgYmllbiByb3VnZSBldCBwYXMgw6l0YWzDqSkgZXQgb24gc2UgZMOpcGxhY2UgdHLDqHMgbGVudGVtZW50IHZlcnMgbCdvcHRpbXVtLiBTaSBsZSB0YXV4IGRlIG11dGF0aW9uIGVzdCBwbHVzIMOpbGV2w6kgZW4gcmV2YW5jaGUsIG9uIG9ic2VydmUgdW4gcG9pbnQgZGUgYnJhbmNoZW1lbnQgZXQgcGx1cyBkZSBwaMOpbm90eXBlcyBxdWkgY29leGlzdGVudCBlbiBwbHVzIGZhaWJsZSBkZW5zaXTDqS4gDQoNCg==