packages:

library("readxl")
library("dplyr")
library("ggplot2")
library("patchwork")
library("RColorBrewer")
library("deSolve")

set directions

# raw data direction
if (!dir.exists("../data")) {
  dir.create("../data", recursive = TRUE)
}

# output direction
if (!dir.exists("../output")) {
  dir.create("../output", recursive = TRUE)
}

# plot direction
if (!dir.exists("../plot")) {
  dir.create("../plot", recursive = TRUE)
}
data_dir <- "../data"
output_dir <- "../output"
plot_dir <- "../plot"

Read data and preliminary operation

read data sheet

file_path <- file.path(data_dir, "WP2_2_clean.xlsx")
data<-read_xlsx(file_path, sheet = "Sheet1")

# data <- data %>% select(-date, -grazer_treatment,-diversity_level,-comment)

colnames(data)
 [1] "identifier"               "system"                  
 [3] "experiment"               "date"                    
 [5] "day"                      "grazer_treatment"        
 [7] "nutrient_treatment"       "species_richness"        
 [9] "diversity_level"          "ID_bucket"               
[11] "number_bucket"            "number_bag"              
[13] "name"                     "group"                   
[15] "species"                  "Fix_Area_bag"            
[17] "Chla_bag"                 "Fix_Area_recalc"         
[19] "Fix_Area_accrual"         "Chla_recalc"             
[21] "Fv/Fm"                    "ABS/RC"                  
[23] "Eto/RC"                   "GR"                      
[25] "GR_total"                 "Fix_Area_recalc_expected"
[27] "RYi_Fix_Area"             "RYi_Fix_Area_accrual"    
[29] "PP"                       "filteredVol_PP_ml"       
[31] "PP_µg/L"                  "PP_µmol/L"               
[33] "%N"                       "%C"                      
[35] "filteredVol_CN_ml"        "N_mg/L"                  
[37] "C_mg/L"                   "N_µmol/L"                
[39] "C_µmol/L"                 "C:N"                     
[41] "C:P"                      "N:P"                     
[43] "C_accrual"                "RYi_C"                   
[45] "C:Fix_Area"               "Fix_Area:N"              
[47] "Fix_Area:P"               "cells/L_species"         
[49] "vol/cell_µm3"             "source"                  
[51] "biovolume_µm3/L_species"  "biovolume_mm3/L_species" 
[53] "biovol_perc_species"      "cells/L_total"           
[55] "biovolume_mm3/L_total"    "comment"                 

take necessary columns:

time, treatment class, species, labels and group, population size (Fix_Area_Recalc)

And in each treatment, bucket, we sum the same species up.

df <- data %>% select(day, nutrient_treatment, species_richness, number_bucket, number_bag, group, species, Fix_Area_recalc)
df <- df[df$species!="Medium",]
df$number_bag <- as.factor(df$number_bag)
df$species <- as.factor(df$species)
df$Fix_Area_recalc <- as.numeric(as.character(df$Fix_Area_recalc))

df_total <- df %>%
  group_by(day, nutrient_treatment, species_richness, number_bucket, group, species) %>%
  summarise(Fix_Area_recalc = sum(Fix_Area_recalc, na.rm = TRUE), .groups = "drop")

colnames(df)
[1] "day"                "nutrient_treatment" "species_richness"  
[4] "number_bucket"      "number_bag"         "group"             
[7] "species"            "Fix_Area_recalc"   

Basic plot

We then set a project from species to color:

display.brewer.all(type = "qual")

species <- unique(df$species)

species_colors <- brewer.pal(n = length(species), name = "Set2")
names(species_colors) <- species

par(mar = c(2, 15, 2, 10))

barplot(
  height = rep(1, length(species_colors)),
  names.arg = names(species_colors),
  col = species_colors,
  border = NA,
  horiz = TRUE,    
  las = 1          
)

Now, the species is related to the color shown above.

Then we divide the data by treatment: N pulse, N limited

df_Npulse <- df[df$nutrient_treatment == "N pulse",]
df_Nlimited <- df[df$nutrient_treatment == "N limited",]

Then split the data by species richness as well as bucket.

split_bucket <- function(df) {
  return(lapply(split(df, df$species_richness), function(sub_df){
    split(sub_df, sub_df$number_bucket)
  }))
}

draw_splited_bucket <- function(df){
  
  data_list <- split_bucket(df)
  
  df_total <- df %>%
    group_by(day, nutrient_treatment, species_richness, number_bucket, group, species) %>%
    summarise(Fix_Area_recalc = mean(Fix_Area_recalc, na.rm = TRUE), .groups = "drop")
  
  sum_list <- split_bucket(df_total)
  
  plot_list <- list()
  sum_plot_list <- list()
  
  dummy <- data.frame(
    species = factor(species, levels = species),
    day = NA_real_,
    Fix_Area_recalc = NA_real_,
    number_bag = NA_real_
    )
  
  plot_geoms <- list(
    geom_line(linewidth = 1.2, show.legend = FALSE),
    geom_point(size = 2, show.legend = FALSE)
  )

  dummy_legend <- list(
    geom_line(
        data = dummy,
        aes(x = day, y = Fix_Area_recalc, color = species),
        linewidth = 1.2,
        alpha = 0, 
        show.legend = TRUE,
        na.rm = TRUE
      )
  )

  plot_theme <- list(
    theme_minimal(),
    theme(
      axis.title = element_blank(),
      axis.ticks = element_blank()
    )
  )

  species_scale <- list(
    scale_color_manual(
      name = "Species",
      values = species_colors,
      drop = FALSE,
      guide = guide_legend(
        override.aes = list(alpha = 1, size = 1, 
                            linetype = 1, shape = 16)
      )
    )
  )
  
  for (richness in names(data_list)) {
    p_richness_list <- list()
    s_richness_list <- list()
    for (bucket in names(data_list[[richness]])) {
    
      p1 <- ggplot(data_list[[richness]][[bucket]],
                 aes(x = day, y = Fix_Area_recalc, 
                     group = number_bag, color = species)) +
        plot_geoms+
        plot_theme+
        dummy_legend+
        species_scale+
        labs(title = bucket)
      
      p_richness_list[[bucket]]<-p1
      
      p_sum<- ggplot(sum_list[[richness]][[bucket]], 
               aes(x = day, 
                   y = Fix_Area_recalc, 
                   color = species)) +
        plot_geoms+
        plot_theme+
        dummy_legend+
        species_scale+
        labs(title = bucket)
        
      s_richness_list[[bucket]]<-p_sum
    }
    plot_list[[richness]]<- p_richness_list
    sum_plot_list[[richness]] <- s_richness_list
  }
  return(list(
    data_list,
    sum_list,
    plot_list,
    sum_plot_list
  ))
}

draw_Npulse <- draw_splited_bucket(df_Npulse)
Npulse_list <- draw_Npulse[[1]]
Npulse_sum <- draw_Npulse[[2]]
Npulse_plots <- draw_Npulse[[3]]
Npulse_sumplots <- draw_Npulse[[4]]

draw_Nlimited <- draw_splited_bucket(df_Nlimited)
Nlimited_list <- draw_Nlimited[[1]]
Nlimited_sum <- draw_Nlimited[[2]]
Nlimited_plots <- draw_Nlimited[[3]]
Nlimited_sumplots <- draw_Nlimited[[4]]

Now, each experiment is organized in a table. _list is the list of all experiment, _sum is the sum of each species, plots is the population size change plot, as well as sumplot is the plot of sum species population (or average). For example:

(Npulse_plots[["1"]][["D1_1"]]|Npulse_plots[["8"]][["D8_1"]])+
  plot_layout(guides = 'collect') &
  theme(legend.position = "bottom")

We first show the single species case.

draw_all_species <- function(draw_list){
  (draw_list[["1"]][["D1_1"]] | draw_list[["1"]][["D1_2"]] | draw_list[["1"]][["D1_3"]] | draw_list[["1"]][["D1_4"]])/
  (draw_list[["1"]][["D1_5"]] | draw_list[["1"]][["D1_6"]] | draw_list[["1"]][["D1_7"]] | draw_list[["1"]][["D1_8"]]) +
  plot_layout(guides = 'collect') &
  theme(legend.position = "bottom")
}
draw_all_species(Npulse_plots)& 
  plot_annotation(
    title = "Npulse single species"
  )

The 8 curves in each graph is the data of 8 bags. We could note that each species shows the similar curve. However, species 1, 4, 6 first decrease then increase, species 3 shows two convex curve, as well as species 7 first increase to a peak then decrease. Only 2, 5, 8 might follows a logistic increase.

Compare to the Nlimited case:

draw_all_species(Nlimited_plots)& 
  plot_annotation(
    title = "Nlimited single species"
  )

Shows the similar trend, but with more noise in D1_4, D1_7, D1_8.

Then we plot the population change in co-cultivate cases:

draw_multi_species <- function(draw_list){
  (draw_list[["2"]][["D2_1"]] | draw_list[["2"]][["D2_2"]] | draw_list[["2"]][["D2_3"]] | draw_list[["2"]][["D2_4"]]) /
    (draw_list[["4"]][["D4_1"]] | draw_list[["4"]][["D4_2"]] | draw_list[["4"]][["D4_3"]] | draw_list[["4"]][["D4_4"]]) /
    (draw_list[["8"]][["D8_1"]] | draw_list[["8"]][["D8_2"]] | draw_list[["8"]][["D8_3"]] | draw_list[["8"]][["D8_4"]])+
  plot_layout(guides = 'collect') &
  theme(legend.position = "right")
}
draw_multi_species(Npulse_plots)& 
  plot_annotation(
    title = "Npulse multi-species"
  )

Species Chlamydomonas (7) and Monoraphidium (8) shows a much higher population size compared to others, and their changing trend seems keep the same. We may need to rescale this two species later.

draw_multi_species(Nlimited_plots)& 
  plot_annotation(
    title = "Nlimited multi-species"
  )

Nlimited case shows more complex results.

We may also worried that species do not interact to each other. If this is true, then the population curve would keep similar in all cases. We plot each curve for single species:

ggplot(df[df$species==species[3] & df$nutrient_treatment=="N pulse",],aes(x = day, y = Fix_Area_recalc, group = paste(number_bucket, number_bag), colour = number_bucket))+
  geom_line()+
  geom_point()+
  theme_minimal()

Species shows similar change in each combinations, indicate the interactions might very weak.

To make the graph more clear, we show the sum plot below, for Npulse treatment:

draw_all_species(Npulse_sumplots)& 
  plot_annotation(
    title = "Npulse single species sum"
  )

draw_multi_species(Npulse_sumplots)& 
  plot_annotation(
    title = "Npulse multi-species sum"
  )

draw_all_species(Nlimited_sumplots)& 
  plot_annotation(
    title = "Nlimited single species sum"
  )

draw_multi_species(Nlimited_sumplots)& 
  plot_annotation(
    title = "Npulse multi-species sum"
  )

ggplot(df_total[df_total$species==species[6] & df_total$nutrient_treatment=="N pulse",],aes(x = day, y = Fix_Area_recalc*species_richness, color = number_bucket))+
  geom_line()+
  geom_point()

Model selection and parameter inference

We write the Logistic model:

\[ \frac{\mathrm{d}N(t)}{\mathrm{d}t} = r N ( 1 - \frac{N}{K} ) \]

Here are only two parameters to be determined, intrinsic growth rate \(r\) and environment capacity \(K\). We could infer these parameters from the 5 points data. However, logistic model could only show very limited dynamics. As what we show below, population size either grow to the environment capacity, or decrease to it.

Ks <- 10
t <- seq(0, 10, by = 0.05)

N0_1 <- 0.1
rs_1 <- c(1, 2, 5, 10)

N0_2 <- 20
rs_2 <- c(0.1, 0.2, 0.5, 1)

logistic <- function(K, N0, r, t) {
  K / (1 + ((K - N0)/N0) * exp(-r * t))
}

df1 <- do.call(rbind, lapply(rs_1, function(r) {
  data.frame(
    t = t,
    N = logistic(Ks, N0_1, r, t),
    r = factor(paste0("r=", r)),
    group = "N0=0.1"
  )
}))

df2 <- do.call(rbind, lapply(rs_2, function(r) {
  data.frame(
    t = t,
    N = logistic(Ks, N0_2, r, t),
    r = factor(paste0("r=", r)),
    group = "N0=15"
  )
}))

df_logi <- rbind(df1, df2)

p <- ggplot(df_logi, aes(x = t, y = N, color = r, linetype = group)) +
  geom_line(linewidth = 1.1) +
  geom_hline(yintercept = Ks, linetype = "dashed") +
  geom_hline(yintercept = Ks/2, linetype = "dotted", color = "grey60") +
  labs(
    title = "Logistic Growth Curves",
    x = "t", 
    y = "N(t)",
    color = "Growth rate r",
    linetype = "Initial N0"
  ) +
  theme_minimal() +
  theme(
    legend.position = "right"
  )

print(p)

The increase curve is the general logistic curve where increase rate first increase then decrease. the decrease curve could only decrease with a decreasing rate. As a result, only species 2, 5, 8 could somehow fit to the logistic model. For the others, we may need

  1. model the initial decline. which could be modeled by the change of intrinsic rate, which represent the adaption to a new environment

\[ \frac{\mathrm{d}N(t)}{\mathrm{d}t} = r(t) N ( 1 - \frac{N}{K} )\\ r(t) = r_{\infty} - (r_{\infty} - r_{0})e^{-\alpha t} \]

Here \(r(0) = r_{0}\) and \(r(t)_{t \rightarrow \infty} \rightarrow r_{ \infty }\) . But this model introduced 3 more parameters, makes total parameters 5, which is the same number of the observed data. This case we may not be able to give a efficient prediction.

  1. model the increase-decrease trend. This could be modeled by lead in an inhibitor

\[ \frac{\mathrm{d}N(t)}{\mathrm{d}t} = r N ( 1 - \frac{N}{K} ) - \gamma N W\\ \frac{\mathrm{d}W(t)}{\mathrm{d}t} = \mu N - \kappa W \]

Here \(W\) could be some toxic substances. But again, here need 5 parameters.

As a result, we first try to infer the parameters for logistic model of species 2,5,8.

We could solve the logistic model as

\[ N(t) = \frac{K}{1 + A e^{-rt}} \]

where \(A\) depends on the initial state \(A = \frac{K}{N_0} - 1\). If we view \(N_0\) as a known parameter, which is the population size at day \(=0\). But if we think the initial state also an observed data, we actually need to infer 3 parameters here.

we plot these three species again

(Npulse_sumplots[["1"]][["D1_2"]]|Npulse_sumplots[["1"]][["D1_5"]]|Npulse_sumplots[["1"]][["D1_8"]])+
plot_layout(guides = 'collect') &
  theme(legend.position = "bottom")

nonlinear least squares

We first use the nonlinear least squares method to estimate the parameters.

3 parameters:

df_N_t_2 <- Npulse_sum[["1"]][["D1_2"]][c("day","Fix_Area_recalc")]
fit2 <- nls(
  Fix_Area_recalc ~ K / (1 + A * exp(-r * day )),
  data = df_N_t_2,
  start = list(
    K = 42000,
    r = 0.5,
    A = -0.45
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)

df_N_t_5 <- Npulse_sum[["1"]][["D1_5"]][c("day","Fix_Area_recalc")]
fit5 <- nls(
  Fix_Area_recalc ~ K / (1 + A * exp(-r * day )),
  data = df_N_t_5,
  start = list(
    K = 310000,
    r = 0.36,
    A = 5.2
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)

df_N_t_8 <- Npulse_sum[["1"]][["D1_8"]][c("day","Fix_Area_recalc")]
fit8 <- nls(
  Fix_Area_recalc ~ K / (1 + A * exp(-r * day )),
  data = df_N_t_8,
  start = list(
    K = 1400000,
    r = 0.9,
    A = 65
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)
coeff_pulse <- rbind(coef(fit2),coef(fit5),coef(fit8))
coeff_pulse
              K         r          A
[1,]   5215.622 0.5079697 -0.4557603
[2,]  39175.577 0.3614746  5.2230213
[3,] 177963.965 0.9050188 67.8913708
summary(fit2)

Formula: Fix_Area_recalc ~ K/(1 + A * exp(-r * day))

Parameters:
    Estimate Std. Error t value Pr(>|t|)   
K 5215.62184  170.11494  30.659  0.00106 **
r    0.50797    0.11304   4.494  0.04612 * 
A   -0.45576    0.02048 -22.250  0.00201 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 191.7 on 2 degrees of freedom

Number of iterations to convergence: 5 
Achieved convergence tolerance: 1.674e-06
summary(fit5)

Formula: Fix_Area_recalc ~ K/(1 + A * exp(-r * day))

Parameters:
   Estimate Std. Error t value Pr(>|t|)  
K 3.918e+04  6.798e+03   5.763   0.0288 *
r 3.615e-01  7.626e-02   4.740   0.0417 *
A 5.223e+00  9.090e-01   5.746   0.0290 *
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1245 on 2 degrees of freedom

Number of iterations to convergence: 4 
Achieved convergence tolerance: 5.103e-06
summary(fit8)

Formula: Fix_Area_recalc ~ K/(1 + A * exp(-r * day))

Parameters:
   Estimate Std. Error t value Pr(>|t|)   
K 1.780e+05  7.550e+03  23.570   0.0018 **
r 9.050e-01  1.032e-01   8.769   0.0128 * 
A 6.789e+01  2.762e+01   2.458   0.1332   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 4783 on 2 degrees of freedom

Number of iterations to convergence: 5 
Achieved convergence tolerance: 2.352e-06
fit2_l <- nls(
  Fix_Area_recalc ~ K / (1 + (K/df_N_t_2$Fix_Area_recalc[1] - 1) * exp(-r * day )),
  data = df_N_t_2,
  start = list(
    K = 41000,
    r = 0.5
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)

fit5_l <- nls(
  Fix_Area_recalc ~ K / (1 + (K/df_N_t_5$Fix_Area_recalc[1] - 1) * exp(-r * day )),
  data = df_N_t_5,
  start = list(
    K = 350000,
    r = 0.3
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)

fit8_l <- nls(
  Fix_Area_recalc ~ K / (1 + (K/df_N_t_8$Fix_Area_recalc[1] - 1) * exp(-r * day )),
  data = df_N_t_8,
  start = list(
    K = 1400000,
    r = 0.9
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)
coeff_pulse_l <- rbind(coef(fit2_l),coef(fit5_l),coef(fit8_l))
coeff_pulse_l
             K           r
[1,] 223951.10 -0.07770653
[2,]  44746.51  0.29877416
[3,] 192622.86  0.69485278
coeff_pulse
              K         r          A
[1,]   5215.622 0.5079697 -0.4557603
[2,]  39175.577 0.3614746  5.2230213
[3,] 177963.965 0.9050188 67.8913708
coeff_pulse_l
             K           r
[1,] 223951.10 -0.07770653
[2,]  44746.51  0.29877416
[3,] 192622.86  0.69485278
summary(fit2_l)

Formula: Fix_Area_recalc ~ K/(1 + (K/df_N_t_2$Fix_Area_recalc[1] - 1) * 
    exp(-r * day))

Parameters:
    Estimate Std. Error t value Pr(>|t|)
K  2.240e+05  2.933e+07   0.008    0.994
r -7.771e-02  3.627e-01  -0.214    0.844

Residual standard error: 1560 on 3 degrees of freedom

Number of iterations till stop: 32 
Achieved convergence tolerance: 7.503
Reason stopped: 算法的步因素0.000488281的大小被减少到小于0.000976563的'minFactor'值
summary(fit5_l)

Formula: Fix_Area_recalc ~ K/(1 + (K/df_N_t_5$Fix_Area_recalc[1] - 1) * 
    exp(-r * day))

Parameters:
   Estimate Std. Error t value Pr(>|t|)   
K 4.475e+04  9.524e+03   4.698  0.01824 * 
r 2.988e-01  4.239e-02   7.048  0.00587 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1249 on 3 degrees of freedom

Number of iterations to convergence: 8 
Achieved convergence tolerance: 5.27e-06
summary(fit8_l)

Formula: Fix_Area_recalc ~ K/(1 + (K/df_N_t_8$Fix_Area_recalc[1] - 1) * 
    exp(-r * day))

Parameters:
   Estimate Std. Error t value Pr(>|t|)    
K 1.926e+05  1.364e+04   14.12 0.000770 ***
r 6.948e-01  3.721e-02   18.68 0.000335 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 7732 on 3 degrees of freedom

Number of iterations to convergence: 6 
Achieved convergence tolerance: 5.035e-06
plot(df_N_t_5$day, df_N_t_5$Fix_Area_recalc)
curve( coef(fit5)[["K"]] / (1 + coef(fit5)[["A"]] * exp(- coef(fit5)[["r"]] * x )),
      add = TRUE, lwd = 2, col = "blue")
curve( coef(fit5_l)[["K"]] / (1 + (coef(fit5_l)[["K"]]/df_N_t_5$Fix_Area_recalc[1] - 1) * exp(- coef(fit5_l)[["r"]] * x )),
      add = TRUE, lwd = 2, col = "green")

(Nlimited_sumplots[["1"]][["D1_2"]]|Nlimited_sumplots[["1"]][["D1_5"]]|Nlimited_sumplots[["1"]][["D1_8"]])+
plot_layout(guides = 'collect') &
  theme(legend.position = "bottom")

d_N_t_2 <- Nlimited_sum[["1"]][["D1_2"]][c("day","Fix_Area_recalc")]
fit_limit2 <- nls(
  Fix_Area_recalc ~ K / (1 + A * exp(-r * day )),
  data = d_N_t_2,
  start = list(
    K = 38000,
    r = 1.6,
    A = -0.22
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)
d_N_t_5 <- Nlimited_sum[["1"]][["D1_5"]][c("day","Fix_Area_recalc")]
fit_limit5 <- nls(
  Fix_Area_recalc ~ K / (1 + A * exp(-r * day )),
  data = d_N_t_5,
  start = list(
    K = 58000,
    r = 0.3,
    A = 0.6
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)
d_N_t_8 <- Nlimited_sum[["1"]][["D1_8"]][c("day","Fix_Area_recalc")]
fit_limit8 <- nls(
  Fix_Area_recalc ~ K / (1 + A * exp(-r * day )),
  data = d_N_t_8,
  start = list(
    K = 220000,
    r = 0.45,
    A = 3.5
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)
coeff_limit <- rbind(coef(fit_limit2),coef(fit_limit5),coef(fit_limit8))
coeff_limit
             K         r          A
[1,]  4786.891 1.6346296 -0.2162845
[2,]  7302.432 0.3120379  0.6700429
[3,] 28109.213 0.4463273  3.5334045
fit_limit2_l <- nls(
  Fix_Area_recalc ~ K / (1 + (K/d_N_t_2$Fix_Area_recalc[1] - 1) * exp(-r * day )),
  data = d_N_t_2,
  start = list(
    K = 38000,
    r = 1.6
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)

fit_limit5_l <- nls(
  Fix_Area_recalc ~ K / (1 + (K/d_N_t_5$Fix_Area_recalc[1] - 1) * exp(-r * day )),
  data = d_N_t_5,
  start = list(
    K = 58000,
    r = 0.3
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)

fit_limit8_l <- nls(
  Fix_Area_recalc ~ K / (1 + (K/d_N_t_8$Fix_Area_recalc[1] - 1) * exp(-r * day )),
  data = d_N_t_8,
  start = list(
    K = 220000,
    r = 0.45
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)
coeff_limit_l <- rbind(coef(fit_limit2_l),coef(fit_limit5_l),coef(fit_limit8_l))
coeff_limit_l
             K         r
[1,]  4786.891 1.6346229
[2,]  7355.162 0.2939016
[3,] 28454.925 0.4247108
plot(d_N_t_5$day, d_N_t_5$Fix_Area_recalc)
curve( coef(fit_limit5)[["K"]] / (1 + coef(fit_limit5)[["A"]] * exp(- coef(fit_limit5)[["r"]] * x )),
      add = TRUE, lwd = 2, col = "blue")
curve( coef(fit_limit5_l)[["K"]] / (1 + (coef(fit_limit5_l)[["K"]]/d_N_t_5$Fix_Area_recalc[1] - 1) * exp(- coef(fit_limit5_l)[["r"]] * x )),
      add = TRUE, lwd = 2, col = "green")

((Npulse_sumplots[["1"]][["D1_2"]] + ylim(0,40000))|(Npulse_sumplots[["2"]][["D2_3"]] + ylim(0,40000))|(Npulse_sumplots[["1"]][["D1_5"]]) + ylim(0,40000))+
plot_layout(guides = 'collect') &
  theme(legend.position = "bottom")

((Npulse_plots[["1"]][["D1_2"]] + ylim(0,40000))|(Npulse_plots[["2"]][["D2_3"]] + ylim(0,40000))|(Npulse_plots[["1"]][["D1_5"]]) + ylim(0,40000))+
plot_layout(guides = 'collect') &
  theme(legend.position = "bottom")

((Nlimited_sumplots[["1"]][["D1_2"]] + ylim(0,10000))|(Nlimited_sumplots[["2"]][["D2_3"]] + ylim(0,10000))|(Nlimited_sumplots[["1"]][["D1_5"]]) + ylim(0,10000))+
plot_layout(guides = 'collect') &
  theme(legend.position = "bottom")

df_2_total <- Npulse_list[["1"]][["D1_2"]][c("day","Fix_Area_recalc")]
fit2_all <- nls(
  Fix_Area_recalc ~ K / (1 + A * exp(-r * day )),
  data = df_2_total,
  start = list(
    K = 42000,
    r = 0.5,
    A = -0.45
    ), 
  control = nls.control(maxiter = 100, warnOnly = TRUE)
)

summary(fit2)

Formula: Fix_Area_recalc ~ K/(1 + A * exp(-r * day))

Parameters:
    Estimate Std. Error t value Pr(>|t|)   
K 5215.62184  170.11494  30.659  0.00106 **
r    0.50797    0.11304   4.494  0.04612 * 
A   -0.45576    0.02048 -22.250  0.00201 **
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 191.7 on 2 degrees of freedom

Number of iterations to convergence: 5 
Achieved convergence tolerance: 1.674e-06
summary(fit2_all)

Formula: Fix_Area_recalc ~ K/(1 + A * exp(-r * day))

Parameters:
    Estimate Std. Error t value Pr(>|t|)    
K 5215.62650  338.26445   15.42  < 2e-16 ***
r    0.50797    0.22477    2.26   0.0298 *  
A   -0.45576    0.04073  -11.19 1.96e-13 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1078 on 37 degrees of freedom

Number of iterations to convergence: 4 
Achieved convergence tolerance: 2.85e-06

Interaction parameters

We have the data of co-cultivate of species 2 and 5. we consider the generalized Lotka-Volterra model:

\[ \frac{\mathrm{d}N_{1}(t)}{\mathrm{d}t} = N_{1} ( r_{1} - \alpha_{1,1}N_{1} - \alpha_{1,2}N_{2} )\\ \frac{\mathrm{d}N_{2}(t)}{\mathrm{d}t} = N_{2} ( r_{2} - \alpha_{2,2}N_{2} - \alpha_{2,1}N_{1} ) \]

We already get the \(r_1\) and \(r_2\) from the former inference of single species. For paramter \(\alpha_{i,i}\) it is actually \(r_i/K_i\). As a result, we only need to infer the parameter \(\alpha_{1,2}\) and \(\alpha_{2,1}\), which are the inter-species interaction strength.

We could also use the nonlinear least squares method. But this time, we should first use linear regression on the difference approximation of the data, to estimate a rough region of parameters. This step is done by hand in the former part, since parameters in logistic curve is quite obvious.

To simplify the model, as well as make the computation more efficient, we rescale the logistic model:

\[ \frac{\mathrm{d}N}{\mathrm{d}t} = r N ( 1 - \frac{N}{K} ) \]

Because of the significant different of \(K\) for each species, directly using these values in gLV will makes the interaction a very small value, which is hard to evaluate. As a result, we rewrite it as:

\[ \frac{\mathrm{d}N_{K}}{\mathrm{d}t} = r N_{K} ( 1 - N_{K} )\\ \frac{\mathrm{d}N_{rK}}{\mathrm{d}t} = N_{rK} ( r- N_{rK} ) \]

where \(N_{K} = N / K\),or , \(N_{rK} = r N /K\), while \(r\) kept the same. now all species are comparable. We use the rescaled population size to write the gLV:

\[ \frac{\mathrm{d}N_{1}}{\mathrm{d}t} = N_{1} ( r_{1} - N_{1} - \alpha_{1,2}N_{2} )\\ \frac{\mathrm{d}N_{2}}{\mathrm{d}t} = N_{2} ( r_{2} - N_{2} - \alpha_{2,1}N_{1} ) \]

###
#observed data:
### 
df_inter <- Npulse_sum[["2"]][["D2_3"]]
#t  <- c(0, 2, 4, 6, 8) 
t <- df_inter[df_inter$species == species[2],]$day
N1 <- df_inter[df_inter$species == species[2],]$Fix_Area_recalc
N2 <- df_inter[df_inter$species == species[5],]$Fix_Area_recalc

r1 <- coef(fit2)[["r"]]
r2 <- coef(fit5)[["r"]]
K1 <- coef(fit2)[["K"]]
K2 <- coef(fit5)[["K"]]
N1 <- r1 * N1 / K1
N2 <- r2 * N2 / K2


###############################################
### difference linear regression
###############################################
dN1 <- diff(N1) / diff(t)
#dN1 <- c(dN1, dN1[length(dN1)])
M1 <- N1[-length(N1)] + diff(N1)/2

dN2 <- diff(N2) / diff(t)
#dN2 <- c(dN2, dN2[length(dN2)])
M2 <- N2[-length(N2)] + diff(N2)/2

x  <- M1 * M2
y1 <- r1*M1 - M1^2 - dN1
y2 <- r2*M2 - M2^2 - dN2 

# linear regression
fit12 <- lm(y1 ~ 0 + x)
fit21 <- lm(y2 ~ 0 + x)

a12_init <- as.numeric(coef(fit12)[1])
a21_init <- as.numeric(coef(fit21)[1])

summary(fit12)

Call:
lm(formula = y1 ~ 0 + x)

Residuals:
       1        2        3        4 
 0.09948  0.01199 -0.02928 -0.02034 

Coefficients:
  Estimate Std. Error t value Pr(>|t|)
x -0.08902    0.48658  -0.183    0.867

Residual standard error: 0.0614 on 3 degrees of freedom
Multiple R-squared:  0.01103,   Adjusted R-squared:  -0.3186 
F-statistic: 0.03347 on 1 and 3 DF,  p-value: 0.8665
summary(fit21)

Call:
lm(formula = y2 ~ 0 + x)

Residuals:
        1         2         3         4 
 0.006637 -0.004077 -0.006187  0.003453 

Coefficients:
  Estimate Std. Error t value Pr(>|t|)  
x  0.12502    0.04817   2.595   0.0807 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.006079 on 3 degrees of freedom
Multiple R-squared:  0.6918,    Adjusted R-squared:  0.5891 
F-statistic: 6.734 on 1 and 3 DF,  p-value: 0.08072
estimate_initial_interaction <- function(t,N1,N2,r1,r2){

  ###############################################
  ### difference linear regression
  ###############################################
  dN1 <- diff(N1) / diff(t)
  M1 <- N1[-length(N1)] + diff(N1)/2
  dN2 <- diff(N2) / diff(t)
  M2 <- N2[-length(N2)] + diff(N2)/2

  x  <- M1 * M2
  y1 <- r1*M1 - M1^2 - dN1
  y2 <- r2*M2 - M2^2 - dN2 

  # linear regression
  fit12 <- lm(y1 ~ 0 + x)
  fit21 <- lm(y2 ~ 0 + x)
  
  return(list(fit12,fit21))
}
gLV <- function(t, y, params) {
    n1 <- y[1]; n2 <- y[2]
    a12 <- params["a12"]
    a21 <- params["a21"]

    dn1 <- r1*n1 - n1*n1 - a12*n1*n2
    dn2 <- r2*n2 - n2*n2 - a21*n1*n2

    list(c(dn1, dn2))
}

estimate_nls_interaction <- function(t,N1,N2,r1,r2, a12_init, a21_init){

  ###############################################
  ### nls + ODE(deSolve)
  ###############################################

  simulate_gLV <- function(a12, a21) {
    params <- c(a12=a12, a21=a21)
    y0 <- c(N1[1], N2[1])
    out <- ode(y=y0, times=t, func=gLV, parms=params)
    return(out[,2:3])
  }

  residual_fn <- function(p) {
    a12 <- p[1]; a21 <- p[2]
    pred <- simulate_gLV(a12, a21)
    res <- c(pred[,1] - N1, pred[,2] - N2)
    return(res)
  }

  obj_fn <- function(p) sum(residual_fn(p)^2)

  p0 <- c(a12_init, a21_init)

  fit <- optim(p0, obj_fn)
  
  return(fit)
}
df_inter<-Npulse_sum[["2"]][["D2_3"]]
df_inter1 <- df_inter[df_inter$species == species[2],]
df_inter2 <- df_inter[df_inter$species == species[5],]

t <- df_inter1$day
N1 <- df_inter1$Fix_Area_recalc
N2 <- df_inter2$Fix_Area_recalc
r1 <- coef(fit2)[["r"]]
r2 <- coef(fit5)[["r"]]
K1 <- coef(fit2)[["K"]]
K2 <- coef(fit5)[["K"]]
N1 <- r1 * N1 / K1
N2 <- r2 * N2 / K2

pulse_fit_inter <- estimate_initial_interaction(t,N1,N2,r1,r2)

pulse_a12_init <- as.numeric(coef(pulse_fit_inter[[1]])[1])
pulse_a21_init <- as.numeric(coef(pulse_fit_inter[[2]])[1])

pulse_nls_inter <- estimate_nls_interaction(t,N1,N2,r1,r2, pulse_a12_init,pulse_a21_init)

summary(pulse_fit_inter[[1]])

Call:
lm(formula = y1 ~ 0 + x)

Residuals:
       1        2        3        4 
 0.09948  0.01199 -0.02928 -0.02034 

Coefficients:
  Estimate Std. Error t value Pr(>|t|)
x -0.08902    0.48658  -0.183    0.867

Residual standard error: 0.0614 on 3 degrees of freedom
Multiple R-squared:  0.01103,   Adjusted R-squared:  -0.3186 
F-statistic: 0.03347 on 1 and 3 DF,  p-value: 0.8665
summary(pulse_fit_inter[[2]])

Call:
lm(formula = y2 ~ 0 + x)

Residuals:
        1         2         3         4 
 0.006637 -0.004077 -0.006187  0.003453 

Coefficients:
  Estimate Std. Error t value Pr(>|t|)  
x  0.12502    0.04817   2.595   0.0807 .
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.006079 on 3 degrees of freedom
Multiple R-squared:  0.6918,    Adjusted R-squared:  0.5891 
F-statistic: 6.734 on 1 and 3 DF,  p-value: 0.08072
pulse_nls_inter$par
[1] -0.001995256  0.130470828
pulse_a12_hat <- pulse_nls_inter$par[1]
pulse_a21_hat <- pulse_nls_inter$par[2]
simulate_gLV <- function(a12, a21) {
    params <- c(a12=a12, a21=a21)
    y0 <- c(N1[1], N2[1])
    out <- ode(y=y0, times=t, func=gLV, parms=params)
    return(out[,2:3])
}

pred <- simulate_gLV(pulse_a12_hat, pulse_a21_hat)

df <- data.frame(
  time = rep(t, 4),
  abundance = c(N1, pred[,1], N2, pred[,2]),
  type = factor(rep(c("N1 obs", "N1 fit", "N2 obs", "N2 fit"), 
                    each = length(t))),
  species = rep(c("N1", "N1", "N2", "N2"), each = length(t))
)

# 为 obs/fit 区分点和线
df$mode <- ifelse(grepl("obs", df$type), "obs", "fit")

# 绘图
p_inter25_pulse <- ggplot(df, aes(x = time, y = abundance, color = species)) +
  # 实测点
  geom_point(data = subset(df, mode == "obs"), size = 2) +
  # 拟合线
  geom_line(data = subset(df, mode == "fit"), linewidth = 1) +
  labs(
    title = "gLV NLS Npulse",
    x = "time",
    y = "abundance",
    color = "Species"
  ) +
  scale_color_manual(values = c("N1" = "blue", "N2" = "red")) +
  theme_minimal() +
  theme(
    legend.position = "bottom"
  )

print(p_inter25_pulse)

df_inter<-Nlimited_sum[["2"]][["D2_3"]]
df_inter1 <- df_inter[df_inter$species == species[2],]
df_inter2 <- df_inter[df_inter$species == species[5],]
t <- df_inter1$day
N1 <- df_inter1$Fix_Area_recalc
N2 <- df_inter2$Fix_Area_recalc
r1 <- coef(fit_limit2)[["r"]]
r2 <- coef(fit_limit5)[["r"]]
K1 <- coef(fit_limit2)[["K"]]
K2 <- coef(fit_limit5)[["K"]]
N1 <- r1 * N1 / K1
N2 <- r2 * N2 / K2

limited_fit_inter <- estimate_initial_interaction(t,N1,N2,r1,r2)

limit_a12_init <- as.numeric(coef(limited_fit_inter[[1]])[1])
limit_a21_init <- as.numeric(coef(limited_fit_inter[[2]])[1])

limit_nls_inter <- estimate_nls_interaction(t,N1,N2,r1,r2, limit_a12_init,limit_a21_init)

summary(limited_fit_inter[[1]])

Call:
lm(formula = y1 ~ 0 + x)

Residuals:
      1       2       3       4 
-0.7864  0.3549  0.3253  0.2978 

Coefficients:
  Estimate Std. Error t value Pr(>|t|)
x  -1.0974     0.4793  -2.289    0.106

Residual standard error: 0.5594 on 3 degrees of freedom
Multiple R-squared:  0.636, Adjusted R-squared:  0.5147 
F-statistic: 5.242 on 1 and 3 DF,  p-value: 0.106
summary(limited_fit_inter[[2]])

Call:
lm(formula = y2 ~ 0 + x)

Residuals:
        1         2         3         4 
-0.017142 -0.012457  0.001443  0.034461 

Coefficients:
   Estimate Std. Error t value Pr(>|t|)
x 0.0006912  0.0200245   0.035    0.975

Residual standard error: 0.02337 on 3 degrees of freedom
Multiple R-squared:  0.000397,  Adjusted R-squared:  -0.3328 
F-statistic: 0.001192 on 1 and 3 DF,  p-value: 0.9746
limit_nls_inter$par
[1] -0.51443631 -0.00219877
limit_a12_hat <- limit_nls_inter$par[1]
limit_a21_hat <- limit_nls_inter$par[2]
simulate_gLV <- function(a12, a21) {
    params <- c(a12=a12, a21=a21)
    y0 <- c(N1[1], N2[1])
    out <- ode(y=y0, times=t, func=gLV, parms=params)
    return(out[,2:3])
}

pred <- simulate_gLV(limit_a12_hat, limit_a21_hat)

df <- data.frame(
  time = rep(t, 4),
  abundance = c(N1, pred[,1], N2, pred[,2]),
  type = factor(rep(c("N1 obs", "N1 fit", "N2 obs", "N2 fit"), 
                    each = length(t))),
  species = rep(c("N1", "N1", "N2", "N2"), each = length(t))
)

# 为 obs/fit 区分点和线
df$mode <- ifelse(grepl("obs", df$type), "obs", "fit")

# 绘图
p_inter25_limited <- ggplot(df, aes(x = time, y = abundance, color = species)) +
  # 实测点
  geom_point(data = subset(df, mode == "obs"), size = 2) +
  # 拟合线
  geom_line(data = subset(df, mode == "fit"), linewidth = 1) +
  labs(
    title = "gLV NLS Nlimited",
    x = "time",
    y = "abundance",
    color = "Species"
  ) +
  scale_color_manual(values = c("N1" = "blue", "N2" = "red")) +
  theme_minimal() +
  theme(
    legend.position = "bottom"
  )

print(p_inter25_limited)

we then change back:

now the functions are

\[ \frac{\mathrm{d}N_{1}}{\mathrm{d}t} = N_{1} ( r_{1} - N_{1} - \alpha_{1,2}N_{2} )\\ \frac{\mathrm{d}N_{2}}{\mathrm{d}t} = N_{2} ( r_{2} - N_{2} - \alpha_{2,1}N_{1} ) \]

where \(N1,N2\) are \(N_{rK} = r N_{K}\).

\[ \frac{\mathrm{d}N_{1}/r_1}{\mathrm{d}t} = N_{1}/r_1 ( r_{1} - r_1 N_{1}/r_1 - r_1 r_2 \alpha_{1,2} N_{2}/(r_1 r_2) )\\ \Rightarrow \frac{\mathrm{d}N_{1}}{\mathrm{d}t} = r_{1}N_{1} ( 1 - N_{1} - r_{2}/r_{1} \alpha_{1,2} N_{2} ) \\ \frac{\mathrm{d}N_{2}}{\mathrm{d}t} = r_{2} N_{2} ( 1 - N_{2} - r_{1}/r_{2} \alpha_{2,1}N_{1} ) \]

As a result, we write \(\alpha_{1,2} = \frac{r_2}{r_1}\alpha_{1,2}\), \(\alpha_{2,1} = \frac{r_1}{r_2}\alpha_{2,1}\),

At the positive stable equilibrium, we have:

\[ r_{1} - N^*_{1} - \alpha_{1,2} N^*_{2} = 0\\ r_{2} - N^*_{2} - \alpha_{2,1} N^*_{1} = 0 \]

As a result, we could solve:

\[ N^*_{1} = \frac{r_{1} - \alpha_{1,2}r_{2}}{1 - \alpha_{1,2} \alpha_{2,1}}\\ N^*_{2} = \frac{r_{2} - \alpha_{2,1}r_{1}}{1 - \alpha_{1,2} \alpha_{2,1}} \]

To make sure \(N^*>0\), with the condition that \(1-\alpha_{1,2}\alpha{2,1} >0\), we need to make sure

\[ r_{1} - \alpha_{1,2}r_{2}>0\\ r_{2} - \alpha_{2,1}r_{1}>0 \]

This region is the feasible domain of \(r_1,r_2\) here.


a12 <- pulse_a12_hat
a21 <- pulse_a21_hat

# 网格分辨率(越小越精细,但越慢)
step <- 0.01

# r1,r2 范围
r1_seq <- seq(-2, 2, by = step)
r2_seq <- seq(-2, 2, by = step)

# 生成网格并计算不等式
grid <- expand.grid(r1 = r1_seq, r2 = r2_seq)
grid$cond1 <- with(grid, r1 > a12 * r2)      # r1 - a12*r2 > 0
grid$cond2 <- with(grid, r2 > a21 * r1)      # r2 - a21*r1 > 0

# 目标点
pt <- data.frame(r1 = as.numeric(coef(fit2)["r"]), r2 = as.numeric(coef(fit5)["r"]))

# 绘图
p_pulse <- ggplot() +
  # 条件1 的区域(半透明蓝)
  geom_tile(
    data = subset(grid, cond1),
    aes(x = r1, y = r2),
    fill = "steelblue",
    alpha = 0.28
  ) +
  # 条件2 的区域(半透明红)
  geom_tile(
    data = subset(grid, cond2),
    aes(x = r1, y = r2),
    fill = "firebrick",
    alpha = 0.28
  ) +
  # 边界直线: r1 = a12 * r2 -> r2 = r1 / a12
  geom_abline(slope = 1 / a12, intercept = 0, color = "steelblue4", linetype = "dashed") +
  # 边界直线: r2 = a21 * r1 -> slope = a21
  geom_abline(slope = a21, intercept = 0, color = "firebrick4", linetype = "dashed") +
  # 标注目标点
  geom_point(data = pt, aes(x = r1, y = r2), color = "black", size = 3) +
  geom_text(data = pt, aes(x = r1, y = r2, label = "(r1,r2)"),
            nudge_x = 0.12, nudge_y = 0.05, size = 3.5, hjust = 0) +
  coord_equal(xlim = c(-2, 2), ylim = c(-2, 2), expand = FALSE) +
  labs(
    #title = expression("Regions: " ~ r[1] - alpha[1,2] * r[2] > 0 ~ "  and  " ~ r[2] - alpha[2,1] * r[1] > 0),
    title = "Npulse",
    x = expression(r[1]),
    y = expression(r[2])
  ) +
  theme_minimal(base_size = 14) +
  theme(
    panel.grid = element_line(color = "grey90"),
    legend.position = "none"
  )

print(p_pulse)

a12 <- limit_a12_hat
a21 <- limit_a21_hat

# 网格分辨率(越小越精细,但越慢)
step <- 0.01

# r1,r2 范围
r1_seq <- seq(-2, 2, by = step)
r2_seq <- seq(-2, 2, by = step)

# 生成网格并计算不等式
grid <- expand.grid(r1 = r1_seq, r2 = r2_seq)
grid$cond1 <- with(grid, r1 > a12 * r2)      # r1 - a12*r2 > 0
grid$cond2 <- with(grid, r2 > a21 * r1)      # r2 - a21*r1 > 0

# 目标点
pt <- data.frame(r1 = as.numeric(coef(fit_limit2)["r"]), r2 = as.numeric(coef(fit_limit5)["r"]))

# 绘图
p_limited <- ggplot() +
  # 条件1 的区域(半透明蓝)
  geom_tile(
    data = subset(grid, cond1),
    aes(x = r1, y = r2),
    fill = "steelblue",
    alpha = 0.28
  ) +
  # 条件2 的区域(半透明红)
  geom_tile(
    data = subset(grid, cond2),
    aes(x = r1, y = r2),
    fill = "firebrick",
    alpha = 0.28
  ) +
  # 边界直线: r1 = a12 * r2 -> r2 = r1 / a12
  geom_abline(slope = 1 / a12, intercept = 0, color = "steelblue4", linetype = "dashed") +
  # 边界直线: r2 = a21 * r1 -> slope = a21
  geom_abline(slope = a21, intercept = 0, color = "firebrick4", linetype = "dashed") +
  # 标注目标点
  geom_point(data = pt, aes(x = r1, y = r2), color = "black", size = 3) +
  geom_text(data = pt, aes(x = r1, y = r2, label = "(r1,r2)"),
            nudge_x = -0.2, nudge_y = 0.15, size = 3.5, hjust = 0) +
  coord_equal(xlim = c(-2, 2), ylim = c(-2, 2), expand = FALSE) +
  labs(
    #title = expression("Regions: " ~ r[1] - alpha[1,2] * r[2] > 0 ~ "  and  " ~ r[2] - alpha[2,1] * r[1] > 0),
    title = "Nlimited",
    x = expression(r[1]),
    y = expression(r[2])
  ) +
  theme_minimal(base_size = 14) +
  theme(
    panel.grid = element_line(color = "grey90"),
    legend.position = "none"
  )

print(p_limited)

p_pulse | p_limited

LS0tDQp0aXRsZTogIlRoZSBzdHJ1Y3R1cmFsIHN0YWJpbGl0eSBvZiBuZXN0ZWQgZXhwZXJpbWVudGFsIHBsYW5rdG9uIHN5c3RlbSINCmF1dGhvcjogIkxvbmd4aWFvIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNCnBhY2thZ2VzOg0KDQpgYGB7cn0NCmxpYnJhcnkoInJlYWR4bCIpDQpsaWJyYXJ5KCJkcGx5ciIpDQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmxpYnJhcnkoInBhdGNod29yayIpDQpsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKQ0KbGlicmFyeSgiZGVTb2x2ZSIpDQpgYGANCg0Kc2V0IGRpcmVjdGlvbnMNCg0KYGBge3J9DQojIHJhdyBkYXRhIGRpcmVjdGlvbg0KaWYgKCFkaXIuZXhpc3RzKCIuLi9kYXRhIikpIHsNCiAgZGlyLmNyZWF0ZSgiLi4vZGF0YSIsIHJlY3Vyc2l2ZSA9IFRSVUUpDQp9DQoNCiMgb3V0cHV0IGRpcmVjdGlvbg0KaWYgKCFkaXIuZXhpc3RzKCIuLi9vdXRwdXQiKSkgew0KICBkaXIuY3JlYXRlKCIuLi9vdXRwdXQiLCByZWN1cnNpdmUgPSBUUlVFKQ0KfQ0KDQojIHBsb3QgZGlyZWN0aW9uDQppZiAoIWRpci5leGlzdHMoIi4uL3Bsb3QiKSkgew0KICBkaXIuY3JlYXRlKCIuLi9wbG90IiwgcmVjdXJzaXZlID0gVFJVRSkNCn0NCg0KYGBgDQoNCmBgYHtyfQ0KZGF0YV9kaXIgPC0gIi4uL2RhdGEiDQpvdXRwdXRfZGlyIDwtICIuLi9vdXRwdXQiDQpwbG90X2RpciA8LSAiLi4vcGxvdCINCmBgYA0KDQojIFJlYWQgZGF0YSBhbmQgcHJlbGltaW5hcnkgb3BlcmF0aW9uDQoNCnJlYWQgZGF0YSBzaGVldA0KDQpgYGB7cn0NCmZpbGVfcGF0aCA8LSBmaWxlLnBhdGgoZGF0YV9kaXIsICJXUDJfMl9jbGVhbi54bHN4IikNCmRhdGE8LXJlYWRfeGxzeChmaWxlX3BhdGgsIHNoZWV0ID0gIlNoZWV0MSIpDQoNCiMgZGF0YSA8LSBkYXRhICU+JSBzZWxlY3QoLWRhdGUsIC1ncmF6ZXJfdHJlYXRtZW50LC1kaXZlcnNpdHlfbGV2ZWwsLWNvbW1lbnQpDQoNCmNvbG5hbWVzKGRhdGEpDQpgYGANCg0KdGFrZSBuZWNlc3NhcnkgY29sdW1uczoNCg0KdGltZSwgdHJlYXRtZW50IGNsYXNzLCBzcGVjaWVzLCBsYWJlbHMgYW5kIGdyb3VwLCBwb3B1bGF0aW9uIHNpemUgKEZpeF9BcmVhX1JlY2FsYykNCg0KQW5kIGluIGVhY2ggdHJlYXRtZW50LCBidWNrZXQsIHdlIHN1bSB0aGUgc2FtZSBzcGVjaWVzIHVwLg0KDQpgYGB7cn0NCmRmIDwtIGRhdGEgJT4lIHNlbGVjdChkYXksIG51dHJpZW50X3RyZWF0bWVudCwgc3BlY2llc19yaWNobmVzcywgbnVtYmVyX2J1Y2tldCwgbnVtYmVyX2JhZywgZ3JvdXAsIHNwZWNpZXMsIEZpeF9BcmVhX3JlY2FsYykNCmRmIDwtIGRmW2RmJHNwZWNpZXMhPSJNZWRpdW0iLF0NCmRmJG51bWJlcl9iYWcgPC0gYXMuZmFjdG9yKGRmJG51bWJlcl9iYWcpDQpkZiRzcGVjaWVzIDwtIGFzLmZhY3RvcihkZiRzcGVjaWVzKQ0KZGYkRml4X0FyZWFfcmVjYWxjIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGRmJEZpeF9BcmVhX3JlY2FsYykpDQoNCmRmX3RvdGFsIDwtIGRmICU+JQ0KICBncm91cF9ieShkYXksIG51dHJpZW50X3RyZWF0bWVudCwgc3BlY2llc19yaWNobmVzcywgbnVtYmVyX2J1Y2tldCwgZ3JvdXAsIHNwZWNpZXMpICU+JQ0KICBzdW1tYXJpc2UoRml4X0FyZWFfcmVjYWxjID0gc3VtKEZpeF9BcmVhX3JlY2FsYywgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikNCg0KY29sbmFtZXMoZGYpDQpgYGANCg0KIyBCYXNpYyBwbG90DQoNCldlIHRoZW4gc2V0IGEgcHJvamVjdCBmcm9tIHNwZWNpZXMgdG8gY29sb3I6DQoNCmBgYHtyfQ0KZGlzcGxheS5icmV3ZXIuYWxsKHR5cGUgPSAicXVhbCIpDQpgYGANCg0KYGBge3J9DQpzcGVjaWVzIDwtIHVuaXF1ZShkZiRzcGVjaWVzKQ0KDQpzcGVjaWVzX2NvbG9ycyA8LSBicmV3ZXIucGFsKG4gPSBsZW5ndGgoc3BlY2llcyksIG5hbWUgPSAiU2V0MiIpDQpuYW1lcyhzcGVjaWVzX2NvbG9ycykgPC0gc3BlY2llcw0KDQpwYXIobWFyID0gYygyLCAxNSwgMiwgMTApKQ0KDQpiYXJwbG90KA0KICBoZWlnaHQgPSByZXAoMSwgbGVuZ3RoKHNwZWNpZXNfY29sb3JzKSksDQogIG5hbWVzLmFyZyA9IG5hbWVzKHNwZWNpZXNfY29sb3JzKSwNCiAgY29sID0gc3BlY2llc19jb2xvcnMsDQogIGJvcmRlciA9IE5BLA0KICBob3JpeiA9IFRSVUUsICAgIA0KICBsYXMgPSAxICAgICAgICAgIA0KKQ0KYGBgDQoNCk5vdywgdGhlIHNwZWNpZXMgaXMgcmVsYXRlZCB0byB0aGUgY29sb3Igc2hvd24gYWJvdmUuDQoNClRoZW4gd2UgZGl2aWRlIHRoZSBkYXRhIGJ5IHRyZWF0bWVudDogTiBwdWxzZSwgTiBsaW1pdGVkDQoNCmBgYHtyfQ0KZGZfTnB1bHNlIDwtIGRmW2RmJG51dHJpZW50X3RyZWF0bWVudCA9PSAiTiBwdWxzZSIsXQ0KZGZfTmxpbWl0ZWQgPC0gZGZbZGYkbnV0cmllbnRfdHJlYXRtZW50ID09ICJOIGxpbWl0ZWQiLF0NCmBgYA0KDQpUaGVuIHNwbGl0IHRoZSBkYXRhIGJ5IHNwZWNpZXMgcmljaG5lc3MgYXMgd2VsbCBhcyBidWNrZXQuDQoNCmBgYHtyfQ0Kc3BsaXRfYnVja2V0IDwtIGZ1bmN0aW9uKGRmKSB7DQogIHJldHVybihsYXBwbHkoc3BsaXQoZGYsIGRmJHNwZWNpZXNfcmljaG5lc3MpLCBmdW5jdGlvbihzdWJfZGYpew0KICAgIHNwbGl0KHN1Yl9kZiwgc3ViX2RmJG51bWJlcl9idWNrZXQpDQogIH0pKQ0KfQ0KDQpkcmF3X3NwbGl0ZWRfYnVja2V0IDwtIGZ1bmN0aW9uKGRmKXsNCiAgDQogIGRhdGFfbGlzdCA8LSBzcGxpdF9idWNrZXQoZGYpDQogIA0KICBkZl90b3RhbCA8LSBkZiAlPiUNCiAgICBncm91cF9ieShkYXksIG51dHJpZW50X3RyZWF0bWVudCwgc3BlY2llc19yaWNobmVzcywgbnVtYmVyX2J1Y2tldCwgZ3JvdXAsIHNwZWNpZXMpICU+JQ0KICAgIHN1bW1hcmlzZShGaXhfQXJlYV9yZWNhbGMgPSBtZWFuKEZpeF9BcmVhX3JlY2FsYywgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICJkcm9wIikNCiAgDQogIHN1bV9saXN0IDwtIHNwbGl0X2J1Y2tldChkZl90b3RhbCkNCiAgDQogIHBsb3RfbGlzdCA8LSBsaXN0KCkNCiAgc3VtX3Bsb3RfbGlzdCA8LSBsaXN0KCkNCiAgDQogIGR1bW15IDwtIGRhdGEuZnJhbWUoDQogICAgc3BlY2llcyA9IGZhY3RvcihzcGVjaWVzLCBsZXZlbHMgPSBzcGVjaWVzKSwNCiAgICBkYXkgPSBOQV9yZWFsXywNCiAgICBGaXhfQXJlYV9yZWNhbGMgPSBOQV9yZWFsXywNCiAgICBudW1iZXJfYmFnID0gTkFfcmVhbF8NCiAgICApDQogIA0KICBwbG90X2dlb21zIDwtIGxpc3QoDQogICAgZ2VvbV9saW5lKGxpbmV3aWR0aCA9IDEuMiwgc2hvdy5sZWdlbmQgPSBGQUxTRSksDQogICAgZ2VvbV9wb2ludChzaXplID0gMiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkNCiAgKQ0KDQogIGR1bW15X2xlZ2VuZCA8LSBsaXN0KA0KICAgIGdlb21fbGluZSgNCiAgICAgICAgZGF0YSA9IGR1bW15LA0KICAgICAgICBhZXMoeCA9IGRheSwgeSA9IEZpeF9BcmVhX3JlY2FsYywgY29sb3IgPSBzcGVjaWVzKSwNCiAgICAgICAgbGluZXdpZHRoID0gMS4yLA0KICAgICAgICBhbHBoYSA9IDAsIA0KICAgICAgICBzaG93LmxlZ2VuZCA9IFRSVUUsDQogICAgICAgIG5hLnJtID0gVFJVRQ0KICAgICAgKQ0KICApDQoNCiAgcGxvdF90aGVtZSA8LSBsaXN0KA0KICAgIHRoZW1lX21pbmltYWwoKSwNCiAgICB0aGVtZSgNCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpDQogICAgKQ0KICApDQoNCiAgc3BlY2llc19zY2FsZSA8LSBsaXN0KA0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCgNCiAgICAgIG5hbWUgPSAiU3BlY2llcyIsDQogICAgICB2YWx1ZXMgPSBzcGVjaWVzX2NvbG9ycywNCiAgICAgIGRyb3AgPSBGQUxTRSwNCiAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKA0KICAgICAgICBvdmVycmlkZS5hZXMgPSBsaXN0KGFscGhhID0gMSwgc2l6ZSA9IDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMSwgc2hhcGUgPSAxNikNCiAgICAgICkNCiAgICApDQogICkNCiAgDQogIGZvciAocmljaG5lc3MgaW4gbmFtZXMoZGF0YV9saXN0KSkgew0KICAgIHBfcmljaG5lc3NfbGlzdCA8LSBsaXN0KCkNCiAgICBzX3JpY2huZXNzX2xpc3QgPC0gbGlzdCgpDQogICAgZm9yIChidWNrZXQgaW4gbmFtZXMoZGF0YV9saXN0W1tyaWNobmVzc11dKSkgew0KICAgIA0KICAgICAgcDEgPC0gZ2dwbG90KGRhdGFfbGlzdFtbcmljaG5lc3NdXVtbYnVja2V0XV0sDQogICAgICAgICAgICAgICAgIGFlcyh4ID0gZGF5LCB5ID0gRml4X0FyZWFfcmVjYWxjLCANCiAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gbnVtYmVyX2JhZywgY29sb3IgPSBzcGVjaWVzKSkgKw0KICAgICAgICBwbG90X2dlb21zKw0KICAgICAgICBwbG90X3RoZW1lKw0KICAgICAgICBkdW1teV9sZWdlbmQrDQogICAgICAgIHNwZWNpZXNfc2NhbGUrDQogICAgICAgIGxhYnModGl0bGUgPSBidWNrZXQpDQogICAgICANCiAgICAgIHBfcmljaG5lc3NfbGlzdFtbYnVja2V0XV08LXAxDQogICAgICANCiAgICAgIHBfc3VtPC0gZ2dwbG90KHN1bV9saXN0W1tyaWNobmVzc11dW1tidWNrZXRdXSwgDQogICAgICAgICAgICAgICBhZXMoeCA9IGRheSwgDQogICAgICAgICAgICAgICAgICAgeSA9IEZpeF9BcmVhX3JlY2FsYywgDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSBzcGVjaWVzKSkgKw0KICAgICAgICBwbG90X2dlb21zKw0KICAgICAgICBwbG90X3RoZW1lKw0KICAgICAgICBkdW1teV9sZWdlbmQrDQogICAgICAgIHNwZWNpZXNfc2NhbGUrDQogICAgICAgIGxhYnModGl0bGUgPSBidWNrZXQpDQogICAgICAgIA0KICAgICAgc19yaWNobmVzc19saXN0W1tidWNrZXRdXTwtcF9zdW0NCiAgICB9DQogICAgcGxvdF9saXN0W1tyaWNobmVzc11dPC0gcF9yaWNobmVzc19saXN0DQogICAgc3VtX3Bsb3RfbGlzdFtbcmljaG5lc3NdXSA8LSBzX3JpY2huZXNzX2xpc3QNCiAgfQ0KICByZXR1cm4obGlzdCgNCiAgICBkYXRhX2xpc3QsDQogICAgc3VtX2xpc3QsDQogICAgcGxvdF9saXN0LA0KICAgIHN1bV9wbG90X2xpc3QNCiAgKSkNCn0NCg0KZHJhd19OcHVsc2UgPC0gZHJhd19zcGxpdGVkX2J1Y2tldChkZl9OcHVsc2UpDQpOcHVsc2VfbGlzdCA8LSBkcmF3X05wdWxzZVtbMV1dDQpOcHVsc2Vfc3VtIDwtIGRyYXdfTnB1bHNlW1syXV0NCk5wdWxzZV9wbG90cyA8LSBkcmF3X05wdWxzZVtbM11dDQpOcHVsc2Vfc3VtcGxvdHMgPC0gZHJhd19OcHVsc2VbWzRdXQ0KDQpkcmF3X05saW1pdGVkIDwtIGRyYXdfc3BsaXRlZF9idWNrZXQoZGZfTmxpbWl0ZWQpDQpObGltaXRlZF9saXN0IDwtIGRyYXdfTmxpbWl0ZWRbWzFdXQ0KTmxpbWl0ZWRfc3VtIDwtIGRyYXdfTmxpbWl0ZWRbWzJdXQ0KTmxpbWl0ZWRfcGxvdHMgPC0gZHJhd19ObGltaXRlZFtbM11dDQpObGltaXRlZF9zdW1wbG90cyA8LSBkcmF3X05saW1pdGVkW1s0XV0NCmBgYA0KDQpOb3csIGVhY2ggZXhwZXJpbWVudCBpcyBvcmdhbml6ZWQgaW4gYSB0YWJsZS4gXF9saXN0IGlzIHRoZSBsaXN0IG9mIGFsbCBleHBlcmltZW50LCBcX3N1bSBpcyB0aGUgc3VtIG9mIGVhY2ggc3BlY2llcywgcGxvdHMgaXMgdGhlIHBvcHVsYXRpb24gc2l6ZSBjaGFuZ2UgcGxvdCwgYXMgd2VsbCBhcyBzdW1wbG90IGlzIHRoZSBwbG90IG9mIHN1bSBzcGVjaWVzIHBvcHVsYXRpb24gKG9yIGF2ZXJhZ2UpLiBGb3IgZXhhbXBsZToNCg0KYGBge3J9DQooTnB1bHNlX3Bsb3RzW1siMSJdXVtbIkQxXzEiXV18TnB1bHNlX3Bsb3RzW1siOCJdXVtbIkQ4XzEiXV0pKw0KICBwbG90X2xheW91dChndWlkZXMgPSAnY29sbGVjdCcpICYNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpgYGANCg0KV2UgZmlyc3Qgc2hvdyB0aGUgc2luZ2xlIHNwZWNpZXMgY2FzZS4NCg0KYGBge3J9DQpkcmF3X2FsbF9zcGVjaWVzIDwtIGZ1bmN0aW9uKGRyYXdfbGlzdCl7DQogIChkcmF3X2xpc3RbWyIxIl1dW1siRDFfMSJdXSB8IGRyYXdfbGlzdFtbIjEiXV1bWyJEMV8yIl1dIHwgZHJhd19saXN0W1siMSJdXVtbIkQxXzMiXV0gfCBkcmF3X2xpc3RbWyIxIl1dW1siRDFfNCJdXSkvDQogIChkcmF3X2xpc3RbWyIxIl1dW1siRDFfNSJdXSB8IGRyYXdfbGlzdFtbIjEiXV1bWyJEMV82Il1dIHwgZHJhd19saXN0W1siMSJdXVtbIkQxXzciXV0gfCBkcmF3X2xpc3RbWyIxIl1dW1siRDFfOCJdXSkgKw0KICBwbG90X2xheW91dChndWlkZXMgPSAnY29sbGVjdCcpICYNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQp9DQpkcmF3X2FsbF9zcGVjaWVzKE5wdWxzZV9wbG90cykmIA0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiTnB1bHNlIHNpbmdsZSBzcGVjaWVzIg0KICApDQpgYGANCg0KVGhlIDggY3VydmVzIGluIGVhY2ggZ3JhcGggaXMgdGhlIGRhdGEgb2YgOCBiYWdzLiBXZSBjb3VsZCBub3RlIHRoYXQgZWFjaCBzcGVjaWVzIHNob3dzIHRoZSBzaW1pbGFyIGN1cnZlLiBIb3dldmVyLCBzcGVjaWVzIDEsIDQsIDYgZmlyc3QgZGVjcmVhc2UgdGhlbiBpbmNyZWFzZSwgc3BlY2llcyAzIHNob3dzIHR3byBjb252ZXggY3VydmUsIGFzIHdlbGwgYXMgc3BlY2llcyA3IGZpcnN0IGluY3JlYXNlIHRvIGEgcGVhayB0aGVuIGRlY3JlYXNlLiBPbmx5IDIsIDUsIDggbWlnaHQgZm9sbG93cyBhIGxvZ2lzdGljIGluY3JlYXNlLg0KDQpDb21wYXJlIHRvIHRoZSBObGltaXRlZCBjYXNlOg0KDQpgYGB7cn0NCmRyYXdfYWxsX3NwZWNpZXMoTmxpbWl0ZWRfcGxvdHMpJiANCiAgcGxvdF9hbm5vdGF0aW9uKA0KICAgIHRpdGxlID0gIk5saW1pdGVkIHNpbmdsZSBzcGVjaWVzIg0KICApDQpgYGANCg0KU2hvd3MgdGhlIHNpbWlsYXIgdHJlbmQsIGJ1dCB3aXRoIG1vcmUgbm9pc2UgaW4gRDFfNCwgRDFfNywgRDFfOC4NCg0KVGhlbiB3ZSBwbG90IHRoZSBwb3B1bGF0aW9uIGNoYW5nZSBpbiBjby1jdWx0aXZhdGUgY2FzZXM6DQoNCmBgYHtyfQ0KZHJhd19tdWx0aV9zcGVjaWVzIDwtIGZ1bmN0aW9uKGRyYXdfbGlzdCl7DQogIChkcmF3X2xpc3RbWyIyIl1dW1siRDJfMSJdXSB8IGRyYXdfbGlzdFtbIjIiXV1bWyJEMl8yIl1dIHwgZHJhd19saXN0W1siMiJdXVtbIkQyXzMiXV0gfCBkcmF3X2xpc3RbWyIyIl1dW1siRDJfNCJdXSkgLw0KICAgIChkcmF3X2xpc3RbWyI0Il1dW1siRDRfMSJdXSB8IGRyYXdfbGlzdFtbIjQiXV1bWyJENF8yIl1dIHwgZHJhd19saXN0W1siNCJdXVtbIkQ0XzMiXV0gfCBkcmF3X2xpc3RbWyI0Il1dW1siRDRfNCJdXSkgLw0KICAgIChkcmF3X2xpc3RbWyI4Il1dW1siRDhfMSJdXSB8IGRyYXdfbGlzdFtbIjgiXV1bWyJEOF8yIl1dIHwgZHJhd19saXN0W1siOCJdXVtbIkQ4XzMiXV0gfCBkcmF3X2xpc3RbWyI4Il1dW1siRDhfNCJdXSkrDQogIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICdjb2xsZWN0JykgJg0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQ0KfQ0KZHJhd19tdWx0aV9zcGVjaWVzKE5wdWxzZV9wbG90cykmIA0KICBwbG90X2Fubm90YXRpb24oDQogICAgdGl0bGUgPSAiTnB1bHNlIG11bHRpLXNwZWNpZXMiDQogICkNCmBgYA0KDQpTcGVjaWVzIENobGFteWRvbW9uYXMgKDcpIGFuZCBNb25vcmFwaGlkaXVtICg4KSBzaG93cyBhIG11Y2ggaGlnaGVyIHBvcHVsYXRpb24gc2l6ZSBjb21wYXJlZCB0byBvdGhlcnMsIGFuZCB0aGVpciBjaGFuZ2luZyB0cmVuZCBzZWVtcyBrZWVwIHRoZSBzYW1lLiBXZSBtYXkgbmVlZCB0byByZXNjYWxlIHRoaXMgdHdvIHNwZWNpZXMgbGF0ZXIuDQoNCmBgYHtyfQ0KZHJhd19tdWx0aV9zcGVjaWVzKE5saW1pdGVkX3Bsb3RzKSYgDQogIHBsb3RfYW5ub3RhdGlvbigNCiAgICB0aXRsZSA9ICJObGltaXRlZCBtdWx0aS1zcGVjaWVzIg0KICApDQpgYGANCg0KTmxpbWl0ZWQgY2FzZSBzaG93cyBtb3JlIGNvbXBsZXggcmVzdWx0cy4NCg0KV2UgbWF5IGFsc28gd29ycmllZCB0aGF0IHNwZWNpZXMgZG8gbm90IGludGVyYWN0IHRvIGVhY2ggb3RoZXIuIElmIHRoaXMgaXMgdHJ1ZSwgdGhlbiB0aGUgcG9wdWxhdGlvbiBjdXJ2ZSB3b3VsZCBrZWVwIHNpbWlsYXIgaW4gYWxsIGNhc2VzLiBXZSBwbG90IGVhY2ggY3VydmUgZm9yIHNpbmdsZSBzcGVjaWVzOg0KDQpgYGB7cn0NCmdncGxvdChkZltkZiRzcGVjaWVzPT1zcGVjaWVzWzNdICYgZGYkbnV0cmllbnRfdHJlYXRtZW50PT0iTiBwdWxzZSIsXSxhZXMoeCA9IGRheSwgeSA9IEZpeF9BcmVhX3JlY2FsYywgZ3JvdXAgPSBwYXN0ZShudW1iZXJfYnVja2V0LCBudW1iZXJfYmFnKSwgY29sb3VyID0gbnVtYmVyX2J1Y2tldCkpKw0KICBnZW9tX2xpbmUoKSsNCiAgZ2VvbV9wb2ludCgpKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpTcGVjaWVzIHNob3dzIHNpbWlsYXIgY2hhbmdlIGluIGVhY2ggY29tYmluYXRpb25zLCBpbmRpY2F0ZSB0aGUgaW50ZXJhY3Rpb25zIG1pZ2h0IHZlcnkgd2Vhay4NCg0KVG8gbWFrZSB0aGUgZ3JhcGggbW9yZSBjbGVhciwgd2Ugc2hvdyB0aGUgc3VtIHBsb3QgYmVsb3csIGZvciBOcHVsc2UgdHJlYXRtZW50Og0KDQpgYGB7cn0NCmRyYXdfYWxsX3NwZWNpZXMoTnB1bHNlX3N1bXBsb3RzKSYgDQogIHBsb3RfYW5ub3RhdGlvbigNCiAgICB0aXRsZSA9ICJOcHVsc2Ugc2luZ2xlIHNwZWNpZXMgc3VtIg0KICApDQpgYGANCg0KYGBge3J9DQpkcmF3X211bHRpX3NwZWNpZXMoTnB1bHNlX3N1bXBsb3RzKSYgDQogIHBsb3RfYW5ub3RhdGlvbigNCiAgICB0aXRsZSA9ICJOcHVsc2UgbXVsdGktc3BlY2llcyBzdW0iDQogICkNCmBgYA0KDQpgYGB7cn0NCmRyYXdfYWxsX3NwZWNpZXMoTmxpbWl0ZWRfc3VtcGxvdHMpJiANCiAgcGxvdF9hbm5vdGF0aW9uKA0KICAgIHRpdGxlID0gIk5saW1pdGVkIHNpbmdsZSBzcGVjaWVzIHN1bSINCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KZHJhd19tdWx0aV9zcGVjaWVzKE5saW1pdGVkX3N1bXBsb3RzKSYgDQogIHBsb3RfYW5ub3RhdGlvbigNCiAgICB0aXRsZSA9ICJOcHVsc2UgbXVsdGktc3BlY2llcyBzdW0iDQogICkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkZl90b3RhbFtkZl90b3RhbCRzcGVjaWVzPT1zcGVjaWVzWzZdICYgZGZfdG90YWwkbnV0cmllbnRfdHJlYXRtZW50PT0iTiBwdWxzZSIsXSxhZXMoeCA9IGRheSwgeSA9IEZpeF9BcmVhX3JlY2FsYypzcGVjaWVzX3JpY2huZXNzLCBjb2xvciA9IG51bWJlcl9idWNrZXQpKSsNCiAgZ2VvbV9saW5lKCkrDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCiMgTW9kZWwgc2VsZWN0aW9uIGFuZCBwYXJhbWV0ZXIgaW5mZXJlbmNlDQoNCldlIHdyaXRlIHRoZSBMb2dpc3RpYyBtb2RlbDoNCg0KJCQNClxmcmFje1xtYXRocm17ZH1OKHQpfXtcbWF0aHJte2R9dH0gPSByIE4gKCAxIC0gXGZyYWN7Tn17S30gKQ0KJCQNCg0KSGVyZSBhcmUgb25seSB0d28gcGFyYW1ldGVycyB0byBiZSBkZXRlcm1pbmVkLCBpbnRyaW5zaWMgZ3Jvd3RoIHJhdGUgJHIkIGFuZCBlbnZpcm9ubWVudCBjYXBhY2l0eSAkSyQuIFdlIGNvdWxkIGluZmVyIHRoZXNlIHBhcmFtZXRlcnMgZnJvbSB0aGUgNSBwb2ludHMgZGF0YS4gSG93ZXZlciwgbG9naXN0aWMgbW9kZWwgY291bGQgb25seSBzaG93IHZlcnkgbGltaXRlZCBkeW5hbWljcy4gQXMgd2hhdCB3ZSBzaG93IGJlbG93LCBwb3B1bGF0aW9uIHNpemUgZWl0aGVyIGdyb3cgdG8gdGhlIGVudmlyb25tZW50IGNhcGFjaXR5LCBvciBkZWNyZWFzZSB0byBpdC4NCg0KYGBge3J9DQpLcyA8LSAxMA0KdCA8LSBzZXEoMCwgMTAsIGJ5ID0gMC4wNSkNCg0KTjBfMSA8LSAwLjENCnJzXzEgPC0gYygxLCAyLCA1LCAxMCkNCg0KTjBfMiA8LSAyMA0KcnNfMiA8LSBjKDAuMSwgMC4yLCAwLjUsIDEpDQoNCmxvZ2lzdGljIDwtIGZ1bmN0aW9uKEssIE4wLCByLCB0KSB7DQogIEsgLyAoMSArICgoSyAtIE4wKS9OMCkgKiBleHAoLXIgKiB0KSkNCn0NCg0KZGYxIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShyc18xLCBmdW5jdGlvbihyKSB7DQogIGRhdGEuZnJhbWUoDQogICAgdCA9IHQsDQogICAgTiA9IGxvZ2lzdGljKEtzLCBOMF8xLCByLCB0KSwNCiAgICByID0gZmFjdG9yKHBhc3RlMCgicj0iLCByKSksDQogICAgZ3JvdXAgPSAiTjA9MC4xIg0KICApDQp9KSkNCg0KZGYyIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShyc18yLCBmdW5jdGlvbihyKSB7DQogIGRhdGEuZnJhbWUoDQogICAgdCA9IHQsDQogICAgTiA9IGxvZ2lzdGljKEtzLCBOMF8yLCByLCB0KSwNCiAgICByID0gZmFjdG9yKHBhc3RlMCgicj0iLCByKSksDQogICAgZ3JvdXAgPSAiTjA9MTUiDQogICkNCn0pKQ0KDQpkZl9sb2dpIDwtIHJiaW5kKGRmMSwgZGYyKQ0KDQpwIDwtIGdncGxvdChkZl9sb2dpLCBhZXMoeCA9IHQsIHkgPSBOLCBjb2xvciA9IHIsIGxpbmV0eXBlID0gZ3JvdXApKSArDQogIGdlb21fbGluZShsaW5ld2lkdGggPSAxLjEpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gS3MsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gS3MvMiwgbGluZXR5cGUgPSAiZG90dGVkIiwgY29sb3IgPSAiZ3JleTYwIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkxvZ2lzdGljIEdyb3d0aCBDdXJ2ZXMiLA0KICAgIHggPSAidCIsIA0KICAgIHkgPSAiTih0KSIsDQogICAgY29sb3IgPSAiR3Jvd3RoIHJhdGUgciIsDQogICAgbGluZXR5cGUgPSAiSW5pdGlhbCBOMCINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCINCiAgKQ0KDQpwcmludChwKQ0KYGBgDQoNClRoZSBpbmNyZWFzZSBjdXJ2ZSBpcyB0aGUgZ2VuZXJhbCBsb2dpc3RpYyBjdXJ2ZSB3aGVyZSBpbmNyZWFzZSByYXRlIGZpcnN0IGluY3JlYXNlIHRoZW4gZGVjcmVhc2UuIHRoZSBkZWNyZWFzZSBjdXJ2ZSBjb3VsZCBvbmx5IGRlY3JlYXNlIHdpdGggYSBkZWNyZWFzaW5nIHJhdGUuIEFzIGEgcmVzdWx0LCBvbmx5IHNwZWNpZXMgMiwgNSwgOCBjb3VsZCBzb21laG93IGZpdCB0byB0aGUgbG9naXN0aWMgbW9kZWwuIEZvciB0aGUgb3RoZXJzLCB3ZSBtYXkgbmVlZA0KDQoxKSAgbW9kZWwgdGhlIGluaXRpYWwgZGVjbGluZS4gd2hpY2ggY291bGQgYmUgbW9kZWxlZCBieSB0aGUgY2hhbmdlIG9mIGludHJpbnNpYyByYXRlLCB3aGljaCByZXByZXNlbnQgdGhlIGFkYXB0aW9uIHRvIGEgbmV3IGVudmlyb25tZW50DQoNCiQkDQpcZnJhY3tcbWF0aHJte2R9Tih0KX17XG1hdGhybXtkfXR9ID0gcih0KSBOICggMSAtIFxmcmFje059e0t9IClcXA0Kcih0KSA9IHJfe1xpbmZ0eX0gLSAocl97XGluZnR5fSAtIHJfezB9KWVeey1cYWxwaGEgdH0NCiQkDQoNCkhlcmUgJHIoMCkgPSByX3swfSQgYW5kICRyKHQpX3t0IFxyaWdodGFycm93IFxpbmZ0eX0gXHJpZ2h0YXJyb3cgcl97IFxpbmZ0eSB9JCAuIEJ1dCB0aGlzIG1vZGVsIGludHJvZHVjZWQgMyBtb3JlIHBhcmFtZXRlcnMsIG1ha2VzIHRvdGFsIHBhcmFtZXRlcnMgNSwgd2hpY2ggaXMgdGhlIHNhbWUgbnVtYmVyIG9mIHRoZSBvYnNlcnZlZCBkYXRhLiBUaGlzIGNhc2Ugd2UgbWF5IG5vdCBiZSBhYmxlIHRvIGdpdmUgYSBlZmZpY2llbnQgcHJlZGljdGlvbi4NCg0KMikgIG1vZGVsIHRoZSBpbmNyZWFzZS1kZWNyZWFzZSB0cmVuZC4gVGhpcyBjb3VsZCBiZSBtb2RlbGVkIGJ5IGxlYWQgaW4gYW4gaW5oaWJpdG9yDQoNCiQkDQpcZnJhY3tcbWF0aHJte2R9Tih0KX17XG1hdGhybXtkfXR9ID0gciBOICggMSAtIFxmcmFje059e0t9ICkgLSBcZ2FtbWEgTiBXXFwNClxmcmFje1xtYXRocm17ZH1XKHQpfXtcbWF0aHJte2R9dH0gPSBcbXUgTiAtIFxrYXBwYSBXDQokJA0KDQpIZXJlICRXJCBjb3VsZCBiZSBzb21lIHRveGljIHN1YnN0YW5jZXMuIEJ1dCBhZ2FpbiwgaGVyZSBuZWVkIDUgcGFyYW1ldGVycy4NCg0KQXMgYSByZXN1bHQsIHdlIGZpcnN0IHRyeSB0byBpbmZlciB0aGUgcGFyYW1ldGVycyBmb3IgbG9naXN0aWMgbW9kZWwgb2Ygc3BlY2llcyAyLDUsOC4NCg0KV2UgY291bGQgc29sdmUgdGhlIGxvZ2lzdGljIG1vZGVsIGFzDQoNCiQkDQpOKHQpID0gXGZyYWN7S317MSArIEEgZV57LXJ0fX0NCiQkDQoNCndoZXJlICRBJCBkZXBlbmRzIG9uIHRoZSBpbml0aWFsIHN0YXRlICRBID0gXGZyYWN7S317Tl8wfSAtIDEkLiBJZiB3ZSB2aWV3ICROXzAkIGFzIGEga25vd24gcGFyYW1ldGVyLCB3aGljaCBpcyB0aGUgcG9wdWxhdGlvbiBzaXplIGF0IGRheSAkPTAkLiBCdXQgaWYgd2UgdGhpbmsgdGhlIGluaXRpYWwgc3RhdGUgYWxzbyBhbiBvYnNlcnZlZCBkYXRhLCB3ZSBhY3R1YWxseSBuZWVkIHRvIGluZmVyIDMgcGFyYW1ldGVycyBoZXJlLg0KDQp3ZSBwbG90IHRoZXNlIHRocmVlIHNwZWNpZXMgYWdhaW4NCg0KYGBge3J9DQooTnB1bHNlX3N1bXBsb3RzW1siMSJdXVtbIkQxXzIiXV18TnB1bHNlX3N1bXBsb3RzW1siMSJdXVtbIkQxXzUiXV18TnB1bHNlX3N1bXBsb3RzW1siMSJdXVtbIkQxXzgiXV0pKw0KcGxvdF9sYXlvdXQoZ3VpZGVzID0gJ2NvbGxlY3QnKSAmDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCiMjIG5vbmxpbmVhciBsZWFzdCBzcXVhcmVzDQoNCldlIGZpcnN0IHVzZSB0aGUgbm9ubGluZWFyIGxlYXN0IHNxdWFyZXMgbWV0aG9kIHRvIGVzdGltYXRlIHRoZSBwYXJhbWV0ZXJzLg0KDQozIHBhcmFtZXRlcnM6DQoNCmBgYHtyfQ0KZGZfTl90XzIgPC0gTnB1bHNlX3N1bVtbIjEiXV1bWyJEMV8yIl1dW2MoImRheSIsIkZpeF9BcmVhX3JlY2FsYyIpXQ0KZml0MiA8LSBubHMoDQogIEZpeF9BcmVhX3JlY2FsYyB+IEsgLyAoMSArIEEgKiBleHAoLXIgKiBkYXkgKSksDQogIGRhdGEgPSBkZl9OX3RfMiwNCiAgc3RhcnQgPSBsaXN0KA0KICAgIEsgPSA0MjAwMCwNCiAgICByID0gMC41LA0KICAgIEEgPSAtMC40NQ0KICAgICksIA0KICBjb250cm9sID0gbmxzLmNvbnRyb2wobWF4aXRlciA9IDEwMCwgd2Fybk9ubHkgPSBUUlVFKQ0KKQ0KDQpkZl9OX3RfNSA8LSBOcHVsc2Vfc3VtW1siMSJdXVtbIkQxXzUiXV1bYygiZGF5IiwiRml4X0FyZWFfcmVjYWxjIildDQpmaXQ1IDwtIG5scygNCiAgRml4X0FyZWFfcmVjYWxjIH4gSyAvICgxICsgQSAqIGV4cCgtciAqIGRheSApKSwNCiAgZGF0YSA9IGRmX05fdF81LA0KICBzdGFydCA9IGxpc3QoDQogICAgSyA9IDMxMDAwMCwNCiAgICByID0gMC4zNiwNCiAgICBBID0gNS4yDQogICAgKSwgDQogIGNvbnRyb2wgPSBubHMuY29udHJvbChtYXhpdGVyID0gMTAwLCB3YXJuT25seSA9IFRSVUUpDQopDQoNCmRmX05fdF84IDwtIE5wdWxzZV9zdW1bWyIxIl1dW1siRDFfOCJdXVtjKCJkYXkiLCJGaXhfQXJlYV9yZWNhbGMiKV0NCmZpdDggPC0gbmxzKA0KICBGaXhfQXJlYV9yZWNhbGMgfiBLIC8gKDEgKyBBICogZXhwKC1yICogZGF5ICkpLA0KICBkYXRhID0gZGZfTl90XzgsDQogIHN0YXJ0ID0gbGlzdCgNCiAgICBLID0gMTQwMDAwMCwNCiAgICByID0gMC45LA0KICAgIEEgPSA2NQ0KICAgICksIA0KICBjb250cm9sID0gbmxzLmNvbnRyb2wobWF4aXRlciA9IDEwMCwgd2Fybk9ubHkgPSBUUlVFKQ0KKQ0KY29lZmZfcHVsc2UgPC0gcmJpbmQoY29lZihmaXQyKSxjb2VmKGZpdDUpLGNvZWYoZml0OCkpDQpjb2VmZl9wdWxzZQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShmaXQyKQ0Kc3VtbWFyeShmaXQ1KQ0Kc3VtbWFyeShmaXQ4KQ0KYGBgDQoNCmBgYHtyfQ0KZml0Ml9sIDwtIG5scygNCiAgRml4X0FyZWFfcmVjYWxjIH4gSyAvICgxICsgKEsvZGZfTl90XzIkRml4X0FyZWFfcmVjYWxjWzFdIC0gMSkgKiBleHAoLXIgKiBkYXkgKSksDQogIGRhdGEgPSBkZl9OX3RfMiwNCiAgc3RhcnQgPSBsaXN0KA0KICAgIEsgPSA0MTAwMCwNCiAgICByID0gMC41DQogICAgKSwgDQogIGNvbnRyb2wgPSBubHMuY29udHJvbChtYXhpdGVyID0gMTAwLCB3YXJuT25seSA9IFRSVUUpDQopDQoNCmZpdDVfbCA8LSBubHMoDQogIEZpeF9BcmVhX3JlY2FsYyB+IEsgLyAoMSArIChLL2RmX05fdF81JEZpeF9BcmVhX3JlY2FsY1sxXSAtIDEpICogZXhwKC1yICogZGF5ICkpLA0KICBkYXRhID0gZGZfTl90XzUsDQogIHN0YXJ0ID0gbGlzdCgNCiAgICBLID0gMzUwMDAwLA0KICAgIHIgPSAwLjMNCiAgICApLCANCiAgY29udHJvbCA9IG5scy5jb250cm9sKG1heGl0ZXIgPSAxMDAsIHdhcm5Pbmx5ID0gVFJVRSkNCikNCg0KZml0OF9sIDwtIG5scygNCiAgRml4X0FyZWFfcmVjYWxjIH4gSyAvICgxICsgKEsvZGZfTl90XzgkRml4X0FyZWFfcmVjYWxjWzFdIC0gMSkgKiBleHAoLXIgKiBkYXkgKSksDQogIGRhdGEgPSBkZl9OX3RfOCwNCiAgc3RhcnQgPSBsaXN0KA0KICAgIEsgPSAxNDAwMDAwLA0KICAgIHIgPSAwLjkNCiAgICApLCANCiAgY29udHJvbCA9IG5scy5jb250cm9sKG1heGl0ZXIgPSAxMDAsIHdhcm5Pbmx5ID0gVFJVRSkNCikNCmNvZWZmX3B1bHNlX2wgPC0gcmJpbmQoY29lZihmaXQyX2wpLGNvZWYoZml0NV9sKSxjb2VmKGZpdDhfbCkpDQpjb2VmZl9wdWxzZV9sDQpgYGANCg0KYGBge3J9DQpjb2VmZl9wdWxzZQ0KY29lZmZfcHVsc2VfbA0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeShmaXQyX2wpDQpzdW1tYXJ5KGZpdDVfbCkNCnN1bW1hcnkoZml0OF9sKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChkZl9OX3RfNSRkYXksIGRmX05fdF81JEZpeF9BcmVhX3JlY2FsYykNCmN1cnZlKCBjb2VmKGZpdDUpW1siSyJdXSAvICgxICsgY29lZihmaXQ1KVtbIkEiXV0gKiBleHAoLSBjb2VmKGZpdDUpW1siciJdXSAqIHggKSksDQogICAgICBhZGQgPSBUUlVFLCBsd2QgPSAyLCBjb2wgPSAiYmx1ZSIpDQpjdXJ2ZSggY29lZihmaXQ1X2wpW1siSyJdXSAvICgxICsgKGNvZWYoZml0NV9sKVtbIksiXV0vZGZfTl90XzUkRml4X0FyZWFfcmVjYWxjWzFdIC0gMSkgKiBleHAoLSBjb2VmKGZpdDVfbClbWyJyIl1dICogeCApKSwNCiAgICAgIGFkZCA9IFRSVUUsIGx3ZCA9IDIsIGNvbCA9ICJncmVlbiIpDQpgYGANCg0KYGBge3J9DQooTmxpbWl0ZWRfc3VtcGxvdHNbWyIxIl1dW1siRDFfMiJdXXxObGltaXRlZF9zdW1wbG90c1tbIjEiXV1bWyJEMV81Il1dfE5saW1pdGVkX3N1bXBsb3RzW1siMSJdXVtbIkQxXzgiXV0pKw0KcGxvdF9sYXlvdXQoZ3VpZGVzID0gJ2NvbGxlY3QnKSAmDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCmBgYHtyfQ0KZF9OX3RfMiA8LSBObGltaXRlZF9zdW1bWyIxIl1dW1siRDFfMiJdXVtjKCJkYXkiLCJGaXhfQXJlYV9yZWNhbGMiKV0NCmZpdF9saW1pdDIgPC0gbmxzKA0KICBGaXhfQXJlYV9yZWNhbGMgfiBLIC8gKDEgKyBBICogZXhwKC1yICogZGF5ICkpLA0KICBkYXRhID0gZF9OX3RfMiwNCiAgc3RhcnQgPSBsaXN0KA0KICAgIEsgPSAzODAwMCwNCiAgICByID0gMS42LA0KICAgIEEgPSAtMC4yMg0KICAgICksIA0KICBjb250cm9sID0gbmxzLmNvbnRyb2wobWF4aXRlciA9IDEwMCwgd2Fybk9ubHkgPSBUUlVFKQ0KKQ0KZF9OX3RfNSA8LSBObGltaXRlZF9zdW1bWyIxIl1dW1siRDFfNSJdXVtjKCJkYXkiLCJGaXhfQXJlYV9yZWNhbGMiKV0NCmZpdF9saW1pdDUgPC0gbmxzKA0KICBGaXhfQXJlYV9yZWNhbGMgfiBLIC8gKDEgKyBBICogZXhwKC1yICogZGF5ICkpLA0KICBkYXRhID0gZF9OX3RfNSwNCiAgc3RhcnQgPSBsaXN0KA0KICAgIEsgPSA1ODAwMCwNCiAgICByID0gMC4zLA0KICAgIEEgPSAwLjYNCiAgICApLCANCiAgY29udHJvbCA9IG5scy5jb250cm9sKG1heGl0ZXIgPSAxMDAsIHdhcm5Pbmx5ID0gVFJVRSkNCikNCmRfTl90XzggPC0gTmxpbWl0ZWRfc3VtW1siMSJdXVtbIkQxXzgiXV1bYygiZGF5IiwiRml4X0FyZWFfcmVjYWxjIildDQpmaXRfbGltaXQ4IDwtIG5scygNCiAgRml4X0FyZWFfcmVjYWxjIH4gSyAvICgxICsgQSAqIGV4cCgtciAqIGRheSApKSwNCiAgZGF0YSA9IGRfTl90XzgsDQogIHN0YXJ0ID0gbGlzdCgNCiAgICBLID0gMjIwMDAwLA0KICAgIHIgPSAwLjQ1LA0KICAgIEEgPSAzLjUNCiAgICApLCANCiAgY29udHJvbCA9IG5scy5jb250cm9sKG1heGl0ZXIgPSAxMDAsIHdhcm5Pbmx5ID0gVFJVRSkNCikNCmNvZWZmX2xpbWl0IDwtIHJiaW5kKGNvZWYoZml0X2xpbWl0MiksY29lZihmaXRfbGltaXQ1KSxjb2VmKGZpdF9saW1pdDgpKQ0KY29lZmZfbGltaXQNCmBgYA0KDQpgYGB7cn0NCmZpdF9saW1pdDJfbCA8LSBubHMoDQogIEZpeF9BcmVhX3JlY2FsYyB+IEsgLyAoMSArIChLL2RfTl90XzIkRml4X0FyZWFfcmVjYWxjWzFdIC0gMSkgKiBleHAoLXIgKiBkYXkgKSksDQogIGRhdGEgPSBkX05fdF8yLA0KICBzdGFydCA9IGxpc3QoDQogICAgSyA9IDM4MDAwLA0KICAgIHIgPSAxLjYNCiAgICApLCANCiAgY29udHJvbCA9IG5scy5jb250cm9sKG1heGl0ZXIgPSAxMDAsIHdhcm5Pbmx5ID0gVFJVRSkNCikNCg0KZml0X2xpbWl0NV9sIDwtIG5scygNCiAgRml4X0FyZWFfcmVjYWxjIH4gSyAvICgxICsgKEsvZF9OX3RfNSRGaXhfQXJlYV9yZWNhbGNbMV0gLSAxKSAqIGV4cCgtciAqIGRheSApKSwNCiAgZGF0YSA9IGRfTl90XzUsDQogIHN0YXJ0ID0gbGlzdCgNCiAgICBLID0gNTgwMDAsDQogICAgciA9IDAuMw0KICAgICksIA0KICBjb250cm9sID0gbmxzLmNvbnRyb2wobWF4aXRlciA9IDEwMCwgd2Fybk9ubHkgPSBUUlVFKQ0KKQ0KDQpmaXRfbGltaXQ4X2wgPC0gbmxzKA0KICBGaXhfQXJlYV9yZWNhbGMgfiBLIC8gKDEgKyAoSy9kX05fdF84JEZpeF9BcmVhX3JlY2FsY1sxXSAtIDEpICogZXhwKC1yICogZGF5ICkpLA0KICBkYXRhID0gZF9OX3RfOCwNCiAgc3RhcnQgPSBsaXN0KA0KICAgIEsgPSAyMjAwMDAsDQogICAgciA9IDAuNDUNCiAgICApLCANCiAgY29udHJvbCA9IG5scy5jb250cm9sKG1heGl0ZXIgPSAxMDAsIHdhcm5Pbmx5ID0gVFJVRSkNCikNCmNvZWZmX2xpbWl0X2wgPC0gcmJpbmQoY29lZihmaXRfbGltaXQyX2wpLGNvZWYoZml0X2xpbWl0NV9sKSxjb2VmKGZpdF9saW1pdDhfbCkpDQpjb2VmZl9saW1pdF9sDQpgYGANCg0KYGBge3J9DQpwbG90KGRfTl90XzUkZGF5LCBkX05fdF81JEZpeF9BcmVhX3JlY2FsYykNCmN1cnZlKCBjb2VmKGZpdF9saW1pdDUpW1siSyJdXSAvICgxICsgY29lZihmaXRfbGltaXQ1KVtbIkEiXV0gKiBleHAoLSBjb2VmKGZpdF9saW1pdDUpW1siciJdXSAqIHggKSksDQogICAgICBhZGQgPSBUUlVFLCBsd2QgPSAyLCBjb2wgPSAiYmx1ZSIpDQpjdXJ2ZSggY29lZihmaXRfbGltaXQ1X2wpW1siSyJdXSAvICgxICsgKGNvZWYoZml0X2xpbWl0NV9sKVtbIksiXV0vZF9OX3RfNSRGaXhfQXJlYV9yZWNhbGNbMV0gLSAxKSAqIGV4cCgtIGNvZWYoZml0X2xpbWl0NV9sKVtbInIiXV0gKiB4ICkpLA0KICAgICAgYWRkID0gVFJVRSwgbHdkID0gMiwgY29sID0gImdyZWVuIikNCmBgYA0KDQpgYGB7cn0NCigoTnB1bHNlX3N1bXBsb3RzW1siMSJdXVtbIkQxXzIiXV0gKyB5bGltKDAsNDAwMDApKXwoTnB1bHNlX3N1bXBsb3RzW1siMiJdXVtbIkQyXzMiXV0gKyB5bGltKDAsNDAwMDApKXwoTnB1bHNlX3N1bXBsb3RzW1siMSJdXVtbIkQxXzUiXV0pICsgeWxpbSgwLDQwMDAwKSkrDQpwbG90X2xheW91dChndWlkZXMgPSAnY29sbGVjdCcpICYNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQpgYGANCg0KYGBge3J9DQooKE5wdWxzZV9wbG90c1tbIjEiXV1bWyJEMV8yIl1dICsgeWxpbSgwLDQwMDAwKSl8KE5wdWxzZV9wbG90c1tbIjIiXV1bWyJEMl8zIl1dICsgeWxpbSgwLDQwMDAwKSl8KE5wdWxzZV9wbG90c1tbIjEiXV1bWyJEMV81Il1dKSArIHlsaW0oMCw0MDAwMCkpKw0KcGxvdF9sYXlvdXQoZ3VpZGVzID0gJ2NvbGxlY3QnKSAmDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KYGBgDQoNCmBgYHtyfQ0KKChObGltaXRlZF9zdW1wbG90c1tbIjEiXV1bWyJEMV8yIl1dICsgeWxpbSgwLDEwMDAwKSl8KE5saW1pdGVkX3N1bXBsb3RzW1siMiJdXVtbIkQyXzMiXV0gKyB5bGltKDAsMTAwMDApKXwoTmxpbWl0ZWRfc3VtcGxvdHNbWyIxIl1dW1siRDFfNSJdXSkgKyB5bGltKDAsMTAwMDApKSsNCnBsb3RfbGF5b3V0KGd1aWRlcyA9ICdjb2xsZWN0JykgJg0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikNCmBgYA0KDQpgYGB7cn0NCmRmXzJfdG90YWwgPC0gTnB1bHNlX2xpc3RbWyIxIl1dW1siRDFfMiJdXVtjKCJkYXkiLCJGaXhfQXJlYV9yZWNhbGMiKV0NCmZpdDJfYWxsIDwtIG5scygNCiAgRml4X0FyZWFfcmVjYWxjIH4gSyAvICgxICsgQSAqIGV4cCgtciAqIGRheSApKSwNCiAgZGF0YSA9IGRmXzJfdG90YWwsDQogIHN0YXJ0ID0gbGlzdCgNCiAgICBLID0gNDIwMDAsDQogICAgciA9IDAuNSwNCiAgICBBID0gLTAuNDUNCiAgICApLCANCiAgY29udHJvbCA9IG5scy5jb250cm9sKG1heGl0ZXIgPSAxMDAsIHdhcm5Pbmx5ID0gVFJVRSkNCikNCg0Kc3VtbWFyeShmaXQyKQ0Kc3VtbWFyeShmaXQyX2FsbCkNCg0KYGBgDQoNCiMgSW50ZXJhY3Rpb24gcGFyYW1ldGVycw0KDQpXZSBoYXZlIHRoZSBkYXRhIG9mIGNvLWN1bHRpdmF0ZSBvZiBzcGVjaWVzIDIgYW5kIDUuIHdlIGNvbnNpZGVyIHRoZSBnZW5lcmFsaXplZCBMb3RrYS1Wb2x0ZXJyYSBtb2RlbDoNCg0KJCQNClxmcmFje1xtYXRocm17ZH1OX3sxfSh0KX17XG1hdGhybXtkfXR9ID0gTl97MX0gKCByX3sxfSAtIFxhbHBoYV97MSwxfU5fezF9IC0gXGFscGhhX3sxLDJ9Tl97Mn0gKVxcDQpcZnJhY3tcbWF0aHJte2R9Tl97Mn0odCl9e1xtYXRocm17ZH10fSA9IE5fezJ9ICggcl97Mn0gLSBcYWxwaGFfezIsMn1OX3syfSAtIFxhbHBoYV97MiwxfU5fezF9ICkNCiQkDQoNCldlIGFscmVhZHkgZ2V0IHRoZSAkcl8xJCBhbmQgJHJfMiQgZnJvbSB0aGUgZm9ybWVyIGluZmVyZW5jZSBvZiBzaW5nbGUgc3BlY2llcy4gRm9yIHBhcmFtdGVyICRcYWxwaGFfe2ksaX0kIGl0IGlzIGFjdHVhbGx5ICRyX2kvS19pJC4gQXMgYSByZXN1bHQsIHdlIG9ubHkgbmVlZCB0byBpbmZlciB0aGUgcGFyYW1ldGVyICRcYWxwaGFfezEsMn0kIGFuZCAkXGFscGhhX3syLDF9JCwgd2hpY2ggYXJlIHRoZSBpbnRlci1zcGVjaWVzIGludGVyYWN0aW9uIHN0cmVuZ3RoLg0KDQpXZSBjb3VsZCBhbHNvIHVzZSB0aGUgbm9ubGluZWFyIGxlYXN0IHNxdWFyZXMgbWV0aG9kLiBCdXQgdGhpcyB0aW1lLCB3ZSBzaG91bGQgZmlyc3QgdXNlIGxpbmVhciByZWdyZXNzaW9uIG9uIHRoZSBkaWZmZXJlbmNlIGFwcHJveGltYXRpb24gb2YgdGhlIGRhdGEsIHRvIGVzdGltYXRlIGEgcm91Z2ggcmVnaW9uIG9mIHBhcmFtZXRlcnMuIFRoaXMgc3RlcCBpcyBkb25lIGJ5IGhhbmQgaW4gdGhlIGZvcm1lciBwYXJ0LCBzaW5jZSBwYXJhbWV0ZXJzIGluIGxvZ2lzdGljIGN1cnZlIGlzIHF1aXRlIG9idmlvdXMuDQoNClRvIHNpbXBsaWZ5IHRoZSBtb2RlbCwgYXMgd2VsbCBhcyBtYWtlIHRoZSBjb21wdXRhdGlvbiBtb3JlIGVmZmljaWVudCwgd2UgcmVzY2FsZSB0aGUgbG9naXN0aWMgbW9kZWw6DQoNCiQkDQpcZnJhY3tcbWF0aHJte2R9Tn17XG1hdGhybXtkfXR9ID0gciBOICggMSAtIFxmcmFje059e0t9ICkNCiQkDQoNCkJlY2F1c2Ugb2YgdGhlIHNpZ25pZmljYW50IGRpZmZlcmVudCBvZiAkSyQgZm9yIGVhY2ggc3BlY2llcywgZGlyZWN0bHkgdXNpbmcgdGhlc2UgdmFsdWVzIGluIGdMViB3aWxsIG1ha2VzIHRoZSBpbnRlcmFjdGlvbiBhIHZlcnkgc21hbGwgdmFsdWUsIHdoaWNoIGlzIGhhcmQgdG8gZXZhbHVhdGUuIEFzIGEgcmVzdWx0LCB3ZSByZXdyaXRlIGl0IGFzOg0KDQokJA0KXGZyYWN7XG1hdGhybXtkfU5fe0t9fXtcbWF0aHJte2R9dH0gPSByIE5fe0t9ICggMSAtIE5fe0t9IClcXA0KXGZyYWN7XG1hdGhybXtkfU5fe3JLfX17XG1hdGhybXtkfXR9ID0gTl97ckt9ICggci0gTl97ckt9ICkNCiQkDQoNCndoZXJlICROX3tLfSA9IE4gLyBLJCxvciAsICROX3tyS30gPSByIE4gL0skLCB3aGlsZSAkciQga2VwdCB0aGUgc2FtZS4gbm93IGFsbCBzcGVjaWVzIGFyZSBjb21wYXJhYmxlLiBXZSB1c2UgdGhlIHJlc2NhbGVkIHBvcHVsYXRpb24gc2l6ZSB0byB3cml0ZSB0aGUgZ0xWOg0KDQokJA0KXGZyYWN7XG1hdGhybXtkfU5fezF9fXtcbWF0aHJte2R9dH0gPSBOX3sxfSAoIHJfezF9IC0gTl97MX0gLSBcYWxwaGFfezEsMn1OX3syfSApXFwNClxmcmFje1xtYXRocm17ZH1OX3syfX17XG1hdGhybXtkfXR9ID0gTl97Mn0gKCByX3syfSAtIE5fezJ9IC0gXGFscGhhX3syLDF9Tl97MX0gKQ0KJCQNCg0KYGBge3J9DQojIyMNCiNvYnNlcnZlZCBkYXRhOg0KIyMjIA0KZGZfaW50ZXIgPC0gTnB1bHNlX3N1bVtbIjIiXV1bWyJEMl8zIl1dDQojdCAgPC0gYygwLCAyLCA0LCA2LCA4KSANCnQgPC0gZGZfaW50ZXJbZGZfaW50ZXIkc3BlY2llcyA9PSBzcGVjaWVzWzJdLF0kZGF5DQpOMSA8LSBkZl9pbnRlcltkZl9pbnRlciRzcGVjaWVzID09IHNwZWNpZXNbMl0sXSRGaXhfQXJlYV9yZWNhbGMNCk4yIDwtIGRmX2ludGVyW2RmX2ludGVyJHNwZWNpZXMgPT0gc3BlY2llc1s1XSxdJEZpeF9BcmVhX3JlY2FsYw0KDQpyMSA8LSBjb2VmKGZpdDIpW1siciJdXQ0KcjIgPC0gY29lZihmaXQ1KVtbInIiXV0NCksxIDwtIGNvZWYoZml0MilbWyJLIl1dDQpLMiA8LSBjb2VmKGZpdDUpW1siSyJdXQ0KTjEgPC0gcjEgKiBOMSAvIEsxDQpOMiA8LSByMiAqIE4yIC8gSzINCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIGRpZmZlcmVuY2UgbGluZWFyIHJlZ3Jlc3Npb24NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpkTjEgPC0gZGlmZihOMSkgLyBkaWZmKHQpDQojZE4xIDwtIGMoZE4xLCBkTjFbbGVuZ3RoKGROMSldKQ0KTTEgPC0gTjFbLWxlbmd0aChOMSldICsgZGlmZihOMSkvMg0KDQpkTjIgPC0gZGlmZihOMikgLyBkaWZmKHQpDQojZE4yIDwtIGMoZE4yLCBkTjJbbGVuZ3RoKGROMildKQ0KTTIgPC0gTjJbLWxlbmd0aChOMildICsgZGlmZihOMikvMg0KDQp4ICA8LSBNMSAqIE0yDQp5MSA8LSByMSpNMSAtIE0xXjIgLSBkTjENCnkyIDwtIHIyKk0yIC0gTTJeMiAtIGROMiANCg0KIyBsaW5lYXIgcmVncmVzc2lvbg0KZml0MTIgPC0gbG0oeTEgfiAwICsgeCkNCmZpdDIxIDwtIGxtKHkyIH4gMCArIHgpDQoNCmExMl9pbml0IDwtIGFzLm51bWVyaWMoY29lZihmaXQxMilbMV0pDQphMjFfaW5pdCA8LSBhcy5udW1lcmljKGNvZWYoZml0MjEpWzFdKQ0KDQpzdW1tYXJ5KGZpdDEyKQ0Kc3VtbWFyeShmaXQyMSkNCg0KYGBgDQoNCmBgYHtyfQ0KZXN0aW1hdGVfaW5pdGlhbF9pbnRlcmFjdGlvbiA8LSBmdW5jdGlvbih0LE4xLE4yLHIxLHIyKXsNCg0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KICAjIyMgZGlmZmVyZW5jZSBsaW5lYXIgcmVncmVzc2lvbg0KICAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KICBkTjEgPC0gZGlmZihOMSkgLyBkaWZmKHQpDQogIE0xIDwtIE4xWy1sZW5ndGgoTjEpXSArIGRpZmYoTjEpLzINCiAgZE4yIDwtIGRpZmYoTjIpIC8gZGlmZih0KQ0KICBNMiA8LSBOMlstbGVuZ3RoKE4yKV0gKyBkaWZmKE4yKS8yDQoNCiAgeCAgPC0gTTEgKiBNMg0KICB5MSA8LSByMSpNMSAtIE0xXjIgLSBkTjENCiAgeTIgPC0gcjIqTTIgLSBNMl4yIC0gZE4yIA0KDQogICMgbGluZWFyIHJlZ3Jlc3Npb24NCiAgZml0MTIgPC0gbG0oeTEgfiAwICsgeCkNCiAgZml0MjEgPC0gbG0oeTIgfiAwICsgeCkNCiAgDQogIHJldHVybihsaXN0KGZpdDEyLGZpdDIxKSkNCn0NCmBgYA0KDQpgYGB7cn0NCmdMViA8LSBmdW5jdGlvbih0LCB5LCBwYXJhbXMpIHsNCiAgICBuMSA8LSB5WzFdOyBuMiA8LSB5WzJdDQogICAgYTEyIDwtIHBhcmFtc1siYTEyIl0NCiAgICBhMjEgPC0gcGFyYW1zWyJhMjEiXQ0KDQogICAgZG4xIDwtIHIxKm4xIC0gbjEqbjEgLSBhMTIqbjEqbjINCiAgICBkbjIgPC0gcjIqbjIgLSBuMipuMiAtIGEyMSpuMSpuMg0KDQogICAgbGlzdChjKGRuMSwgZG4yKSkNCn0NCg0KZXN0aW1hdGVfbmxzX2ludGVyYWN0aW9uIDwtIGZ1bmN0aW9uKHQsTjEsTjIscjEscjIsIGExMl9pbml0LCBhMjFfaW5pdCl7DQoNCiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiAgIyMjIG5scyArIE9ERe+8iGRlU29sdmXvvIkNCiAgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KICBzaW11bGF0ZV9nTFYgPC0gZnVuY3Rpb24oYTEyLCBhMjEpIHsNCiAgICBwYXJhbXMgPC0gYyhhMTI9YTEyLCBhMjE9YTIxKQ0KICAgIHkwIDwtIGMoTjFbMV0sIE4yWzFdKQ0KICAgIG91dCA8LSBvZGUoeT15MCwgdGltZXM9dCwgZnVuYz1nTFYsIHBhcm1zPXBhcmFtcykNCiAgICByZXR1cm4ob3V0WywyOjNdKQ0KICB9DQoNCiAgcmVzaWR1YWxfZm4gPC0gZnVuY3Rpb24ocCkgew0KICAgIGExMiA8LSBwWzFdOyBhMjEgPC0gcFsyXQ0KICAgIHByZWQgPC0gc2ltdWxhdGVfZ0xWKGExMiwgYTIxKQ0KICAgIHJlcyA8LSBjKHByZWRbLDFdIC0gTjEsIHByZWRbLDJdIC0gTjIpDQogICAgcmV0dXJuKHJlcykNCiAgfQ0KDQogIG9ial9mbiA8LSBmdW5jdGlvbihwKSBzdW0ocmVzaWR1YWxfZm4ocCleMikNCg0KICBwMCA8LSBjKGExMl9pbml0LCBhMjFfaW5pdCkNCg0KICBmaXQgPC0gb3B0aW0ocDAsIG9ial9mbikNCiAgDQogIHJldHVybihmaXQpDQp9DQpgYGANCg0KYGBge3J9DQpkZl9pbnRlcjwtTnB1bHNlX3N1bVtbIjIiXV1bWyJEMl8zIl1dDQpkZl9pbnRlcjEgPC0gZGZfaW50ZXJbZGZfaW50ZXIkc3BlY2llcyA9PSBzcGVjaWVzWzJdLF0NCmRmX2ludGVyMiA8LSBkZl9pbnRlcltkZl9pbnRlciRzcGVjaWVzID09IHNwZWNpZXNbNV0sXQ0KDQp0IDwtIGRmX2ludGVyMSRkYXkNCk4xIDwtIGRmX2ludGVyMSRGaXhfQXJlYV9yZWNhbGMNCk4yIDwtIGRmX2ludGVyMiRGaXhfQXJlYV9yZWNhbGMNCnIxIDwtIGNvZWYoZml0MilbWyJyIl1dDQpyMiA8LSBjb2VmKGZpdDUpW1siciJdXQ0KSzEgPC0gY29lZihmaXQyKVtbIksiXV0NCksyIDwtIGNvZWYoZml0NSlbWyJLIl1dDQpOMSA8LSByMSAqIE4xIC8gSzENCk4yIDwtIHIyICogTjIgLyBLMg0KDQpwdWxzZV9maXRfaW50ZXIgPC0gZXN0aW1hdGVfaW5pdGlhbF9pbnRlcmFjdGlvbih0LE4xLE4yLHIxLHIyKQ0KDQpwdWxzZV9hMTJfaW5pdCA8LSBhcy5udW1lcmljKGNvZWYocHVsc2VfZml0X2ludGVyW1sxXV0pWzFdKQ0KcHVsc2VfYTIxX2luaXQgPC0gYXMubnVtZXJpYyhjb2VmKHB1bHNlX2ZpdF9pbnRlcltbMl1dKVsxXSkNCg0KcHVsc2VfbmxzX2ludGVyIDwtIGVzdGltYXRlX25sc19pbnRlcmFjdGlvbih0LE4xLE4yLHIxLHIyLCBwdWxzZV9hMTJfaW5pdCxwdWxzZV9hMjFfaW5pdCkNCg0Kc3VtbWFyeShwdWxzZV9maXRfaW50ZXJbWzFdXSkNCnN1bW1hcnkocHVsc2VfZml0X2ludGVyW1syXV0pDQoNCnB1bHNlX25sc19pbnRlciRwYXINCg0KcHVsc2VfYTEyX2hhdCA8LSBwdWxzZV9ubHNfaW50ZXIkcGFyWzFdDQpwdWxzZV9hMjFfaGF0IDwtIHB1bHNlX25sc19pbnRlciRwYXJbMl0NCmBgYA0KDQpgYGB7cn0NCnNpbXVsYXRlX2dMViA8LSBmdW5jdGlvbihhMTIsIGEyMSkgew0KICAgIHBhcmFtcyA8LSBjKGExMj1hMTIsIGEyMT1hMjEpDQogICAgeTAgPC0gYyhOMVsxXSwgTjJbMV0pDQogICAgb3V0IDwtIG9kZSh5PXkwLCB0aW1lcz10LCBmdW5jPWdMViwgcGFybXM9cGFyYW1zKQ0KICAgIHJldHVybihvdXRbLDI6M10pDQp9DQoNCnByZWQgPC0gc2ltdWxhdGVfZ0xWKHB1bHNlX2ExMl9oYXQsIHB1bHNlX2EyMV9oYXQpDQoNCmRmIDwtIGRhdGEuZnJhbWUoDQogIHRpbWUgPSByZXAodCwgNCksDQogIGFidW5kYW5jZSA9IGMoTjEsIHByZWRbLDFdLCBOMiwgcHJlZFssMl0pLA0KICB0eXBlID0gZmFjdG9yKHJlcChjKCJOMSBvYnMiLCAiTjEgZml0IiwgIk4yIG9icyIsICJOMiBmaXQiKSwgDQogICAgICAgICAgICAgICAgICAgIGVhY2ggPSBsZW5ndGgodCkpKSwNCiAgc3BlY2llcyA9IHJlcChjKCJOMSIsICJOMSIsICJOMiIsICJOMiIpLCBlYWNoID0gbGVuZ3RoKHQpKQ0KKQ0KDQojIOS4uiBvYnMvZml0IOWMuuWIhueCueWSjOe6vw0KZGYkbW9kZSA8LSBpZmVsc2UoZ3JlcGwoIm9icyIsIGRmJHR5cGUpLCAib2JzIiwgImZpdCIpDQoNCiMg57uY5Zu+DQpwX2ludGVyMjVfcHVsc2UgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IHRpbWUsIHkgPSBhYnVuZGFuY2UsIGNvbG9yID0gc3BlY2llcykpICsNCiAgIyDlrp7mtYvngrkNCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRmLCBtb2RlID09ICJvYnMiKSwgc2l6ZSA9IDIpICsNCiAgIyDmi5/lkIjnur8NCiAgZ2VvbV9saW5lKGRhdGEgPSBzdWJzZXQoZGYsIG1vZGUgPT0gImZpdCIpLCBsaW5ld2lkdGggPSAxKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiZ0xWIE5MUyBOcHVsc2UiLA0KICAgIHggPSAidGltZSIsDQogICAgeSA9ICJhYnVuZGFuY2UiLA0KICAgIGNvbG9yID0gIlNwZWNpZXMiDQogICkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTjEiID0gImJsdWUiLCAiTjIiID0gInJlZCIpKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iDQogICkNCg0KcHJpbnQocF9pbnRlcjI1X3B1bHNlKQ0KDQpgYGANCg0KYGBge3J9DQpkZl9pbnRlcjwtTmxpbWl0ZWRfc3VtW1siMiJdXVtbIkQyXzMiXV0NCmRmX2ludGVyMSA8LSBkZl9pbnRlcltkZl9pbnRlciRzcGVjaWVzID09IHNwZWNpZXNbMl0sXQ0KZGZfaW50ZXIyIDwtIGRmX2ludGVyW2RmX2ludGVyJHNwZWNpZXMgPT0gc3BlY2llc1s1XSxdDQp0IDwtIGRmX2ludGVyMSRkYXkNCk4xIDwtIGRmX2ludGVyMSRGaXhfQXJlYV9yZWNhbGMNCk4yIDwtIGRmX2ludGVyMiRGaXhfQXJlYV9yZWNhbGMNCnIxIDwtIGNvZWYoZml0X2xpbWl0MilbWyJyIl1dDQpyMiA8LSBjb2VmKGZpdF9saW1pdDUpW1siciJdXQ0KSzEgPC0gY29lZihmaXRfbGltaXQyKVtbIksiXV0NCksyIDwtIGNvZWYoZml0X2xpbWl0NSlbWyJLIl1dDQpOMSA8LSByMSAqIE4xIC8gSzENCk4yIDwtIHIyICogTjIgLyBLMg0KDQpsaW1pdGVkX2ZpdF9pbnRlciA8LSBlc3RpbWF0ZV9pbml0aWFsX2ludGVyYWN0aW9uKHQsTjEsTjIscjEscjIpDQoNCmxpbWl0X2ExMl9pbml0IDwtIGFzLm51bWVyaWMoY29lZihsaW1pdGVkX2ZpdF9pbnRlcltbMV1dKVsxXSkNCmxpbWl0X2EyMV9pbml0IDwtIGFzLm51bWVyaWMoY29lZihsaW1pdGVkX2ZpdF9pbnRlcltbMl1dKVsxXSkNCg0KbGltaXRfbmxzX2ludGVyIDwtIGVzdGltYXRlX25sc19pbnRlcmFjdGlvbih0LE4xLE4yLHIxLHIyLCBsaW1pdF9hMTJfaW5pdCxsaW1pdF9hMjFfaW5pdCkNCg0Kc3VtbWFyeShsaW1pdGVkX2ZpdF9pbnRlcltbMV1dKQ0Kc3VtbWFyeShsaW1pdGVkX2ZpdF9pbnRlcltbMl1dKQ0KDQpsaW1pdF9ubHNfaW50ZXIkcGFyDQoNCmxpbWl0X2ExMl9oYXQgPC0gbGltaXRfbmxzX2ludGVyJHBhclsxXQ0KbGltaXRfYTIxX2hhdCA8LSBsaW1pdF9ubHNfaW50ZXIkcGFyWzJdDQpgYGANCg0KYGBge3J9DQpzaW11bGF0ZV9nTFYgPC0gZnVuY3Rpb24oYTEyLCBhMjEpIHsNCiAgICBwYXJhbXMgPC0gYyhhMTI9YTEyLCBhMjE9YTIxKQ0KICAgIHkwIDwtIGMoTjFbMV0sIE4yWzFdKQ0KICAgIG91dCA8LSBvZGUoeT15MCwgdGltZXM9dCwgZnVuYz1nTFYsIHBhcm1zPXBhcmFtcykNCiAgICByZXR1cm4ob3V0WywyOjNdKQ0KfQ0KDQpwcmVkIDwtIHNpbXVsYXRlX2dMVihsaW1pdF9hMTJfaGF0LCBsaW1pdF9hMjFfaGF0KQ0KDQpkZiA8LSBkYXRhLmZyYW1lKA0KICB0aW1lID0gcmVwKHQsIDQpLA0KICBhYnVuZGFuY2UgPSBjKE4xLCBwcmVkWywxXSwgTjIsIHByZWRbLDJdKSwNCiAgdHlwZSA9IGZhY3RvcihyZXAoYygiTjEgb2JzIiwgIk4xIGZpdCIsICJOMiBvYnMiLCAiTjIgZml0IiksIA0KICAgICAgICAgICAgICAgICAgICBlYWNoID0gbGVuZ3RoKHQpKSksDQogIHNwZWNpZXMgPSByZXAoYygiTjEiLCAiTjEiLCAiTjIiLCAiTjIiKSwgZWFjaCA9IGxlbmd0aCh0KSkNCikNCg0KIyDkuLogb2JzL2ZpdCDljLrliIbngrnlkoznur8NCmRmJG1vZGUgPC0gaWZlbHNlKGdyZXBsKCJvYnMiLCBkZiR0eXBlKSwgIm9icyIsICJmaXQiKQ0KDQojIOe7mOWbvg0KcF9pbnRlcjI1X2xpbWl0ZWQgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IHRpbWUsIHkgPSBhYnVuZGFuY2UsIGNvbG9yID0gc3BlY2llcykpICsNCiAgIyDlrp7mtYvngrkNCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KGRmLCBtb2RlID09ICJvYnMiKSwgc2l6ZSA9IDIpICsNCiAgIyDmi5/lkIjnur8NCiAgZ2VvbV9saW5lKGRhdGEgPSBzdWJzZXQoZGYsIG1vZGUgPT0gImZpdCIpLCBsaW5ld2lkdGggPSAxKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiZ0xWIE5MUyBObGltaXRlZCIsDQogICAgeCA9ICJ0aW1lIiwNCiAgICB5ID0gImFidW5kYW5jZSIsDQogICAgY29sb3IgPSAiU3BlY2llcyINCiAgKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJOMSIgPSAiYmx1ZSIsICJOMiIgPSAicmVkIikpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSINCiAgKQ0KDQpwcmludChwX2ludGVyMjVfbGltaXRlZCkNCmBgYA0KDQp3ZSB0aGVuIGNoYW5nZSBiYWNrOg0KDQpub3cgdGhlIGZ1bmN0aW9ucyBhcmUNCg0KJCQNClxmcmFje1xtYXRocm17ZH1OX3sxfX17XG1hdGhybXtkfXR9ID0gTl97MX0gKCByX3sxfSAtIE5fezF9IC0gXGFscGhhX3sxLDJ9Tl97Mn0gKVxcDQpcZnJhY3tcbWF0aHJte2R9Tl97Mn19e1xtYXRocm17ZH10fSA9IE5fezJ9ICggcl97Mn0gLSBOX3syfSAtIFxhbHBoYV97MiwxfU5fezF9ICkNCiQkDQoNCndoZXJlICROMSxOMiQgYXJlICROX3tyS30gPSByIE5fe0t9JC4NCg0KJCQNClxmcmFje1xtYXRocm17ZH1OX3sxfS9yXzF9e1xtYXRocm17ZH10fSA9IE5fezF9L3JfMSAoIHJfezF9IC0gcl8xIE5fezF9L3JfMSAtIHJfMSByXzIgXGFscGhhX3sxLDJ9IE5fezJ9LyhyXzEgcl8yKSApXFwNClxSaWdodGFycm93IFxmcmFje1xtYXRocm17ZH1OX3sxfX17XG1hdGhybXtkfXR9ID0gcl97MX1OX3sxfSAoIDEgLSBOX3sxfSAtIHJfezJ9L3JfezF9IFxhbHBoYV97MSwyfSBOX3syfSApDQpcXA0KXGZyYWN7XG1hdGhybXtkfU5fezJ9fXtcbWF0aHJte2R9dH0gPSByX3syfSBOX3syfSAoIDEgLSBOX3syfSAtIHJfezF9L3JfezJ9IFxhbHBoYV97MiwxfU5fezF9ICkNCiQkDQoNCkFzIGEgcmVzdWx0LCB3ZSB3cml0ZSAkXGFscGhhX3sxLDJ9ID0gXGZyYWN7cl8yfXtyXzF9XGFscGhhX3sxLDJ9JCwgJFxhbHBoYV97MiwxfSA9IFxmcmFje3JfMX17cl8yfVxhbHBoYV97MiwxfSQsDQoNCkF0IHRoZSBwb3NpdGl2ZSBzdGFibGUgZXF1aWxpYnJpdW0sIHdlIGhhdmU6DQoNCiQkDQpyX3sxfSAtIE5eKl97MX0gLSBcYWxwaGFfezEsMn0gTl4qX3syfSA9IDBcXA0Kcl97Mn0gLSBOXipfezJ9IC0gXGFscGhhX3syLDF9IE5eKl97MX0gPSAwDQokJA0KDQpBcyBhIHJlc3VsdCwgd2UgY291bGQgc29sdmU6DQoNCiQkDQpOXipfezF9ID0gXGZyYWN7cl97MX0gLSBcYWxwaGFfezEsMn1yX3syfX17MSAtIFxhbHBoYV97MSwyfSBcYWxwaGFfezIsMX19XFwNCk5eKl97Mn0gPSBcZnJhY3tyX3syfSAtIFxhbHBoYV97MiwxfXJfezF9fXsxIC0gXGFscGhhX3sxLDJ9IFxhbHBoYV97MiwxfX0NCiQkDQoNClRvIG1ha2Ugc3VyZSAkTl4qPjAkLCB3aXRoIHRoZSBjb25kaXRpb24gdGhhdCAkMS1cYWxwaGFfezEsMn1cYWxwaGF7MiwxfSA+MCQsIHdlIG5lZWQgdG8gbWFrZSBzdXJlDQoNCiQkDQpyX3sxfSAtIFxhbHBoYV97MSwyfXJfezJ9PjBcXA0Kcl97Mn0gLSBcYWxwaGFfezIsMX1yX3sxfT4wDQokJA0KDQpUaGlzIHJlZ2lvbiBpcyB0aGUgZmVhc2libGUgZG9tYWluIG9mICRyXzEscl8yJCBoZXJlLg0KDQpgYGB7cn0NCg0KYTEyIDwtIHB1bHNlX2ExMl9oYXQNCmEyMSA8LSBwdWxzZV9hMjFfaGF0DQoNCiMg572R5qC85YiG6L6o546H77yI6LaK5bCP6LaK57K+57uG77yM5L2G6LaK5oWi77yJDQpzdGVwIDwtIDAuMDENCg0KIyByMSxyMiDojIPlm7QNCnIxX3NlcSA8LSBzZXEoLTIsIDIsIGJ5ID0gc3RlcCkNCnIyX3NlcSA8LSBzZXEoLTIsIDIsIGJ5ID0gc3RlcCkNCg0KIyDnlJ/miJDnvZHmoLzlubborqHnrpfkuI3nrYnlvI8NCmdyaWQgPC0gZXhwYW5kLmdyaWQocjEgPSByMV9zZXEsIHIyID0gcjJfc2VxKQ0KZ3JpZCRjb25kMSA8LSB3aXRoKGdyaWQsIHIxID4gYTEyICogcjIpICAgICAgIyByMSAtIGExMipyMiA+IDANCmdyaWQkY29uZDIgPC0gd2l0aChncmlkLCByMiA+IGEyMSAqIHIxKSAgICAgICMgcjIgLSBhMjEqcjEgPiAwDQoNCiMg55uu5qCH54K5DQpwdCA8LSBkYXRhLmZyYW1lKHIxID0gYXMubnVtZXJpYyhjb2VmKGZpdDIpWyJyIl0pLCByMiA9IGFzLm51bWVyaWMoY29lZihmaXQ1KVsiciJdKSkNCg0KIyDnu5jlm74NCnBfcHVsc2UgPC0gZ2dwbG90KCkgKw0KICAjIOadoeS7tjEg55qE5Yy65Z+f77yI5Y2K6YCP5piO6JOd77yJDQogIGdlb21fdGlsZSgNCiAgICBkYXRhID0gc3Vic2V0KGdyaWQsIGNvbmQxKSwNCiAgICBhZXMoeCA9IHIxLCB5ID0gcjIpLA0KICAgIGZpbGwgPSAic3RlZWxibHVlIiwNCiAgICBhbHBoYSA9IDAuMjgNCiAgKSArDQogICMg5p2h5Lu2MiDnmoTljLrln5/vvIjljYrpgI/mmI7nuqLvvIkNCiAgZ2VvbV90aWxlKA0KICAgIGRhdGEgPSBzdWJzZXQoZ3JpZCwgY29uZDIpLA0KICAgIGFlcyh4ID0gcjEsIHkgPSByMiksDQogICAgZmlsbCA9ICJmaXJlYnJpY2siLA0KICAgIGFscGhhID0gMC4yOA0KICApICsNCiAgIyDovrnnlYznm7Tnur/vvJogcjEgPSBhMTIgKiByMiAtPiByMiA9IHIxIC8gYTEyDQogIGdlb21fYWJsaW5lKHNsb3BlID0gMSAvIGExMiwgaW50ZXJjZXB0ID0gMCwgY29sb3IgPSAic3RlZWxibHVlNCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgIyDovrnnlYznm7Tnur/vvJogcjIgPSBhMjEgKiByMSAtPiBzbG9wZSA9IGEyMQ0KICBnZW9tX2FibGluZShzbG9wZSA9IGEyMSwgaW50ZXJjZXB0ID0gMCwgY29sb3IgPSAiZmlyZWJyaWNrNCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsNCiAgIyDmoIfms6jnm67moIfngrkNCiAgZ2VvbV9wb2ludChkYXRhID0gcHQsIGFlcyh4ID0gcjEsIHkgPSByMiksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSBwdCwgYWVzKHggPSByMSwgeSA9IHIyLCBsYWJlbCA9ICIocjEscjIpIiksDQogICAgICAgICAgICBudWRnZV94ID0gMC4xMiwgbnVkZ2VfeSA9IDAuMDUsIHNpemUgPSAzLjUsIGhqdXN0ID0gMCkgKw0KICBjb29yZF9lcXVhbCh4bGltID0gYygtMiwgMiksIHlsaW0gPSBjKC0yLCAyKSwgZXhwYW5kID0gRkFMU0UpICsNCiAgbGFicygNCiAgICAjdGl0bGUgPSBleHByZXNzaW9uKCJSZWdpb25zOiAiIH4gclsxXSAtIGFscGhhWzEsMl0gKiByWzJdID4gMCB+ICIgIGFuZCAgIiB+IHJbMl0gLSBhbHBoYVsyLDFdICogclsxXSA+IDApLA0KICAgIHRpdGxlID0gIk5wdWxzZSIsDQogICAgeCA9IGV4cHJlc3Npb24oclsxXSksDQogICAgeSA9IGV4cHJlc3Npb24oclsyXSkNCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpICsNCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5OTAiKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KDQpwcmludChwX3B1bHNlKQ0KDQpgYGANCg0KYGBge3J9DQphMTIgPC0gbGltaXRfYTEyX2hhdA0KYTIxIDwtIGxpbWl0X2EyMV9oYXQNCg0KIyDnvZHmoLzliIbovqjnjofvvIjotorlsI/otornsr7nu4bvvIzkvYbotormhaLvvIkNCnN0ZXAgPC0gMC4wMQ0KDQojIHIxLHIyIOiMg+WbtA0KcjFfc2VxIDwtIHNlcSgtMiwgMiwgYnkgPSBzdGVwKQ0KcjJfc2VxIDwtIHNlcSgtMiwgMiwgYnkgPSBzdGVwKQ0KDQojIOeUn+aIkOe9keagvOW5tuiuoeeul+S4jeetieW8jw0KZ3JpZCA8LSBleHBhbmQuZ3JpZChyMSA9IHIxX3NlcSwgcjIgPSByMl9zZXEpDQpncmlkJGNvbmQxIDwtIHdpdGgoZ3JpZCwgcjEgPiBhMTIgKiByMikgICAgICAjIHIxIC0gYTEyKnIyID4gMA0KZ3JpZCRjb25kMiA8LSB3aXRoKGdyaWQsIHIyID4gYTIxICogcjEpICAgICAgIyByMiAtIGEyMSpyMSA+IDANCg0KIyDnm67moIfngrkNCnB0IDwtIGRhdGEuZnJhbWUocjEgPSBhcy5udW1lcmljKGNvZWYoZml0X2xpbWl0MilbInIiXSksIHIyID0gYXMubnVtZXJpYyhjb2VmKGZpdF9saW1pdDUpWyJyIl0pKQ0KDQojIOe7mOWbvg0KcF9saW1pdGVkIDwtIGdncGxvdCgpICsNCiAgIyDmnaHku7YxIOeahOWMuuWfn++8iOWNiumAj+aYjuiTne+8iQ0KICBnZW9tX3RpbGUoDQogICAgZGF0YSA9IHN1YnNldChncmlkLCBjb25kMSksDQogICAgYWVzKHggPSByMSwgeSA9IHIyKSwNCiAgICBmaWxsID0gInN0ZWVsYmx1ZSIsDQogICAgYWxwaGEgPSAwLjI4DQogICkgKw0KICAjIOadoeS7tjIg55qE5Yy65Z+f77yI5Y2K6YCP5piO57qi77yJDQogIGdlb21fdGlsZSgNCiAgICBkYXRhID0gc3Vic2V0KGdyaWQsIGNvbmQyKSwNCiAgICBhZXMoeCA9IHIxLCB5ID0gcjIpLA0KICAgIGZpbGwgPSAiZmlyZWJyaWNrIiwNCiAgICBhbHBoYSA9IDAuMjgNCiAgKSArDQogICMg6L6555WM55u057q/77yaIHIxID0gYTEyICogcjIgLT4gcjIgPSByMSAvIGExMg0KICBnZW9tX2FibGluZShzbG9wZSA9IDEgLyBhMTIsIGludGVyY2VwdCA9IDAsIGNvbG9yID0gInN0ZWVsYmx1ZTQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogICMg6L6555WM55u057q/77yaIHIyID0gYTIxICogcjEgLT4gc2xvcGUgPSBhMjENCiAgZ2VvbV9hYmxpbmUoc2xvcGUgPSBhMjEsIGludGVyY2VwdCA9IDAsIGNvbG9yID0gImZpcmVicmljazQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArDQogICMg5qCH5rOo55uu5qCH54K5DQogIGdlb21fcG9pbnQoZGF0YSA9IHB0LCBhZXMoeCA9IHIxLCB5ID0gcjIpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzKSArDQogIGdlb21fdGV4dChkYXRhID0gcHQsIGFlcyh4ID0gcjEsIHkgPSByMiwgbGFiZWwgPSAiKHIxLHIyKSIpLA0KICAgICAgICAgICAgbnVkZ2VfeCA9IC0wLjIsIG51ZGdlX3kgPSAwLjE1LCBzaXplID0gMy41LCBoanVzdCA9IDApICsNCiAgY29vcmRfZXF1YWwoeGxpbSA9IGMoLTIsIDIpLCB5bGltID0gYygtMiwgMiksIGV4cGFuZCA9IEZBTFNFKSArDQogIGxhYnMoDQogICAgI3RpdGxlID0gZXhwcmVzc2lvbigiUmVnaW9uczogIiB+IHJbMV0gLSBhbHBoYVsxLDJdICogclsyXSA+IDAgfiAiICBhbmQgICIgfiByWzJdIC0gYWxwaGFbMiwxXSAqIHJbMV0gPiAwKSwNCiAgICB0aXRsZSA9ICJObGltaXRlZCIsDQogICAgeCA9IGV4cHJlc3Npb24oclsxXSksDQogICAgeSA9IGV4cHJlc3Npb24oclsyXSkNCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTQpICsNCiAgdGhlbWUoDQogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5OTAiKSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KDQpwcmludChwX2xpbWl0ZWQpDQpgYGANCg0KYGBge3J9DQpwX3B1bHNlIHwgcF9saW1pdGVkDQpgYGANCg==