Code by TomMakesThings




Dual Reporter Method

A modified gene produces two pieces of mRNA (\(x_{1}\)) which stochastically transcribe different reporter proteins (\(x_{2}\), \(x_{3}\)), e.g. CFP and GFP, independently of one another. This dual reporter method can be modeled by the following reaction scheme:

\(\text{Transcription: } x_{1} \xrightarrow[]{f(x_{1})} x_{1} + 1\)

\(\text{mRNA degradation: } \text{ } x_{1} \xrightarrow[]{\beta_{1} x_{1}} x_{1} - 1\)

\(\text{Translation of } x_{2} \text{: } x_{2} \xrightarrow[]{\lambda_{2} x_{1}} x_{2} + 1\)

\(\text{Degradation of } x_{2} \text{: } x_{2} \xrightarrow[]{\beta_{2} x_{2}} x_{2} - 1\)

\(\text{Translation of } x_{3} \text{: } x_{3} \xrightarrow[]{\lambda_{2} x_{1}} x_{3} + 1\)

\(\text{Degradation of } x_{3} \text{: } x_{3} \xrightarrow[]{\beta_{2} x_{3}} x_{3} - 1\)


mRNA Production Rate

Two different situations are modeled, whereby mRNA transcription is either constant or self-repressive. In both, mRNA are degraded one at a time at a rate \(\beta_{1} x_{1}\):

  1. \(f(x_{1}) = \lambda_{1}\)
    • mRNA are transcribed one at a time in a Poission process at a rate \(\lambda_{1}\)
    • The chemical master equation (CME), describing the time evolution of component \(x_{1}\), is simply: \(\frac{d P(x_{1})}{dt} = \underbrace{\lambda_{1} (P(x_{1} - 1) - \lambda_{1} P(x_{1})}_{\text{Synthesis}} + \underbrace{\beta_{1} (x_{1} + 1) P(x_{1} + 1) - \beta x_{1} P(x_{1})}_{\text{Degradation}}\)

  2. \(f(x_{1}) = \lambda_{1} \frac{K}{K + x_{1}}\)
    • Setting the production rate of mRNA as a function \(f(x_{1})\), allows the model to incorporate positive / negative feedback
    • In this case, mRNA is self-repressive as its transcription is inversely proportional to its own concentration \((x_{1})\) and a rate constant \(K\)


Doob-Gillespie Algorithm

To simulate the system over time, the Doob-Gillespie algorithm is applied:

  1. For a system at time \(t = t_{0}\) with \(n\) chemical species, initialize the system’s state by setting the initial numbers of molecules \(x = x_{1}, x_{2}, ..., x_{n}\).
  2. Set the \(p\) reactions rates \(r_{1}(x), r_{2}(x), ..., r_{p}(x)\), where reaction \(k\) adds \(\delta_{jk}\) molecules.
  3. Calculate the total reaction rate \(r_{T} = \sum^{N}_{k=1}r_{k}(x)\) for the system at time \(t\).
  4. Select the waiting time until the next reaction from an exponential distribution, with cumulative distribution function \(F(t) = 1 - e^{-r_{T}t}\).
  5. Select a reaction \(i\) with probability \(\frac{r_{i}(x)}{r_{T}(x)}\).
  6. For each molecular species effected by reaction \(i\), update its concentration in the system \(x_{j} \xrightarrow[]{}{} x_{j} + \delta_{ji}\).
  7. If the maximum number of iterations is reached, stop simulation. Otherwise go back to step 3.


Code

Set Up

Import the following libraries, set a seed and define the working directory.

library(SciViews)
library(ggplot2)
library(stringr)
library(ggpubr)

# Set seed for reproducability
set.seed(123)
# Set working directory
setwd("C:/Users/redds/Documents/GitHub/Gillespie")

Initialise System’s State

At time \(t_{i} = t_{0}\), the initial number of molecules is set so that each molecular species with at 50. The stoichiometry of the system (the number of molecules created or degraded per reaction) is encoded as \(\delta\) values. For example, reaction \(r_{2}\) entails degradation of a single \(x_{1}\) molecule, and so \(\delta_{11} = -1\).

# Set initial number of molecules
X_initial <- list(x1 = 50, x2 = 50, x3 = 50)

# Change in molecule numbers per reaction
reaction_deltas <- list(c(x1 = 1, x2 = 0, x3 = 0), # x1 transcription
                        c(x1 = -1, x2 = 0, x3 = 0), #x1 degradation
                        c(x1 = 0, x2 = 1, x3 = 0), # x2 translation
                        c(x1 = 0, x2 = -1, x3 = 0), # x2 degradation
                        c(x1 = 0, x2 = 0, x3 = 1), # x3 translation
                        c(x1 = 0, x2 = 0, x3 = -1)) # x3 degradation

Doob-Gillespie Algorithm

Helper Functions

The following are helper functions used to calculate reaction rates and generate random jump times.

# Dynamically calculate the reaction rate given an equation
calculateRate <- function(rate_equation, rate_constants, X) {
  # Converts string to equation and evaluates it
  # E.g. if variables beta = 10 and x1 = 5, then "beta * X$x1" becomes beta * x1 and 50 is returned
  eval(parse(text = rate_equation))
}

# Generate random wait time between reactions
calculateJumpTime <- function(rT) {
  # Generate random number between 0 - 1 from a uniform distribution
  u_time <- runif(1, 0, 1)
  # Calculate the random jump time by converting from a uniform to exponentially distributed number
  jump_time <- - ln(u_time) / rT
  
  return(jump_time)
}

# Calculate normalised variation and covariance
calculateStats <- function(X_over_time, rate_constants, jump_times,
                           function_type = "A") {
  
  # Extract the rate constant parameters
  lambda_1 <- rate_constants$lambda_1
  lambda_2 <- rate_constants$lambda_2
  beta_1 <- rate_constants$beta_1
  beta_2 <- rate_constants$beta_2
  K <- rate_constants$K
  
  # Calculate the average number of x1, x2 and x3 molecules
  x_averages <- list()
  
  for (x in rownames(X_over_time)) {
    x_averages[[x]] <- weighted.mean(x = unlist(X_over_time[x,]), w = jump_times)
  }
  
  # Calculate analytic etas
  if (toupper(function_type) == "A") {
    # Calculate normalised variance for function (a)
    eta_11 <- 1 / x_averages[["x1"]]
    
    # Normalised co-variance
    eta_12 <- eta_11 * (beta_2 / (beta_1 + beta_2))
    eta_23 <- eta_12
    
  } else {
    # Calculate placeholder alpha for function (b)
    alpha <- x_averages[["x1"]] / (K + x_averages[["x1"]])
    
    eta_11 <- 1 / (x_averages[["x1"]] + alpha)
    eta_12 <- eta_11 * (beta_2 / (beta_1 + (beta_1 * alpha) + beta_2))
    eta_23 <- eta_12
  }
  
  # Calculate weighted covariance between x1, x2 and x3 molecules
  abundance_df <- data.frame(t(X_over_time))
  abundance_df <- data.frame(lapply(abundance_df, as.numeric))
  molecule_cov <- cov.wt(abundance_df, as.vector(jump_times))$cov
  
  # Calculate numeric etas
  num_eta11 <- molecule_cov["x1", "x1"] / (x_averages[["x1"]] * x_averages[["x1"]])
  num_eta12 <- molecule_cov["x1", "x2"] / (x_averages[["x1"]] * x_averages[["x2"]])
  num_eta23 <- molecule_cov["x2", "x3"] / (x_averages[["x2"]] * x_averages[["x3"]])
  
  # Record results
  results <- list(analytical = list(eta_11 = eta_11,
                                    eta_12 = eta_12,
                                    eta_23 = eta_23),
                  numerical = list(eta_11 = num_eta11,
                                   eta_12 = num_eta12,
                                   eta_23 = num_eta23))
  
  return(results)
}

Doob-Gillespie Implementation

Within the Doob-Gillespie algorithm, the number of molecules over time, jump time between each reaction and a trace-back of which reactions are selected are recorded. The simulation is run for \(N\) iterations, each of which represents a time step in which a reaction occurs. The reaction rate \(r_{k}\) for each reaction \(k\) is updated for each time step. This is because the degradation reactions dynamically change depending on the concentration of each type of molecule. The total rate \(r_{T}\) must therefore also be recalculated.

\(r_{T} = \sum^{N}_{k=1} r_{k}(x)\)

The jump times between reactions are derived from an exponential distribution. In R, this is achieved through generating a random number between 0 - 1 from a uniform distribution, and then converting this to a time \(t_{i}\) through inverting the exponential cumulative distribution function.

\(u_{time} = F(t) = 1 - e^{-r_{T}t}\)

\(t = - \frac{ln(u_{time})}{r_{T}}\)

Next the probability of each reaction \(k\) occurring is evaluated based upon the reaction rates, \(r_{k}/ r_{T}\). The cumulative sums of reaction probabilities are calculated to partition the probabilities into sections. Another number is then generated from a uniform distribution and compared against the partition to determine which reaction will occur. The number of molecules is updated according to the chosen reaction by adding the \(\delta_{k}\) values to the current state of the system.

Since it is unrealistic for the number of molecules to become negative, a check has been included to prevent impossible degradation reactions from being selected at each given time point. For example, a reaction involving \(x_{1} - 1\) cannot occur if \(x_{1} = 0\) at time \(t_{i}\).

# Doob-Gillespie algorithm
# X: initial number of molecules
# rate_constants: list of constant parameters for calculating rates
# deltas: list of vectors for the number of molecules that will change per reaction
# rates: formulas for reaction rates per reaction
# N: max number of iterations
# stop_at_stationary: whether to stop the simulation early if reached stationarity condition
# min_N: minimum number of iterations is stopping when reached stationarity
# flux_threshold: minimum difference between <R+> and <R-> for stationarity
# verbose: set as > 0 for debugging messages
gillespie <- function(X, rate_constants, deltas, rates, N = 100000,
                      stop_at_stationary = FALSE, min_N = 10000, 
                      flux_threshold = 0.1, verbose = 0) {
  # Record number of molecules over time in a matrix
  X_over_time <- matrix(data = X, nrow = length(X))
  # Record which reaction occurred at each time step t
  reaction_trace <- c()
  # Record time t between reactions
  times <- c(0)
  
  # Determine which reactions contains molecule degradation
  degradation_reactions <- which(unlist(lapply(deltas, function(x) any(x < 0))))
  
  # Calculate the magnitude of the highest degradation delta value
  degradation_deltas <- unlist(deltas[degradation_reactions])
  max_degration <- abs(degradation_deltas[which.max(abs(degradation_deltas))])
  
  n <- 0
  stationarity_reached <- FALSE
  
  # Run simulation N times or until stationarity is reached
  while (n < N & !stationarity_reached) {
    n <- n + 1
    
    # Get the number of molecules at time step t
    current_molecules <- unlist(X_over_time[, dim(X_over_time)[2]])
    names(current_molecules) <- names(X)
    
    # Initially set all reactions as possible at the current time step
    possible_reactions <- 1:length(rates)
    
    # Check if any degradation reactions will cause a negative number of molecules
    if (any(current_molecules - rep(max_degration, length(current_molecules)) < 0)) {
      # Find which reactions cannot occur
      impossible_reactions <- c()
      
      for (reaction_idx in degradation_reactions) {
        # Calculate number of molecules left if reaction occurs
        n_molecules <- unlist(X_over_time[, dim(X_over_time)[2]]) + deltas[[reaction_idx]]
        
        # Record reaction as impossible if the number of any molecule is below zero
        if (any(n_molecules < 0)) {
          impossible_reactions <- c(impossible_reactions, reaction_idx)
        }
      }
      # Update list of possible reactions to remove impossible ones
      possible_reactions <- possible_reactions[!possible_reactions %in% impossible_reactions]
    }
    
    # Calculate the current reaction rates for the system at time t, excluding impossible reactions
    current_rates <- unname(unlist(lapply(rates[possible_reactions],
                                          function(rate_equation) calculateRate(rate_equation,
                                                                                rate_constants,
                                                                                current_molecules))))
    # Calculate the total reaction rate
    r_total <- sum(current_rates)
    
    # Generate the jump time for the next reaction
    times <- c(times, calculateJumpTime(r_total))
    
    # Calculate the probability of each reaction occurring
    reaction_probs <- current_rates / r_total
    prob_partitions <- cumsum(reaction_probs)

    # Select a random reaction (relative to the probability of each occurring)
    u_reaction <- runif(1, 0, 1)
    reaction_idx <- min(which(prob_partitions > u_reaction))
    
    # Calculate the molecule numbers after the chosen reaction
    n_molecules <- current_molecules + deltas[possible_reactions][[reaction_idx]]
    
    # Record which reaction occurred
    reaction_trace <- c(reaction_trace, reaction_idx)
    # Update the number of molecules by the chosen reaction
    X_over_time <- cbind(X_over_time, n_molecules)
    
    if (verbose > 0 & n %% 10000 == 0) {
      message(paste("Reached iteration", n))
    }
    
    if (stop_at_stationary & n %% 500 == 0 & n >= min_N) {
      # Check if difference in birth and death flux is less than a threshold
      stats <- calculateStats(X_over_time, rate_constants, times,
                              flux_threshold = flux_threshold,
                              calculate_efficiency = FALSE,
                              calculate_variation = FALSE,
                              calculate_covariance = FALSE)
      
      # Stop simulation if TRUE
      stationarity_reached <- stats$stationarity_reached
      # flux_differences <- stats$flux_differences
      
      if (stationarity_reached) {
        if (verbose > 0) {
          message(paste("Stationarity reached after", n, "iterations"))
        }
      }
    }
  }
  
  # Rename columns to time t for each reaction
  colnames(X_over_time) <- cumsum(times)
  
  return(list(molecules_over_time = X_over_time,
              reaction_trace = reaction_trace,
              jump_times = times,
              n_iterations = n))
}

Testing mRNA Production Rates

The Doob-Gillespie algorithm is set to run for 250,000 iterations separately with functions (a) and (b) as the mRNA transcription rates. As each simulation takes a while to run, it is saved to file.

# Set the functions to test
test_funcs <- c("A", "B")
# Max number of iterations to run the Gillespie algorithm
N_iterations <- 250000
# Set as > 0 for debugging messages
sim_verbose <- 0

# Create folder (if doesn't exist) to store simulations
sim_folder <- file.path(getwd(), "Simulations")
dir.create(sim_folder)

For both simulations, reaction rate constants are set with \(\beta_{1} = \lambda_{2} = 1\) and \(\beta_{2} = 0.1\). To ensure that the average mRNA concentration converges to the same number in each simulation, e.g. \(\left\langle x_{1} \right\rangle \simeq 20\), values of \(\lambda_{1}\) and \(K\) were determined for functions (a) and (b) separately. These were calculated using the principal that at stationarity, the average rate of production equals the average rate of degradation, i.e. \(\left\langle R^{+} \right\rangle = \left\langle R^{-} \right\rangle\).

  1. \(f(x_{1}) = \lambda_{1}\)
    • \(\lambda_{1} = \beta_{1} \left\langle x_{1} \right\rangle\)
    • \(\lambda_{1} = 1 \cdot 20\)
    • \(\therefore \lambda_{1} = 20\)

  2. \(f(x_{1}) = \lambda_{1} \frac{K}{K + x_{1}}\)
    • \(\lambda_{1} \frac{K}{K + x_{1}} = \beta_{1} \left\langle x_{1} \right\rangle\)
    • \(\lambda_{1} K = \left\langle x_{1} \right\rangle (K + \left\langle x_{1} \right\rangle)\)
    • \(\lambda_{1} K = 20 (K + 20)\)
    • \(K(\lambda_{1} - 20) = 400\)
    • \(K = \frac{400}{\lambda_{1} - 20}\)
    • \(\therefore \lambda_{1} \ne 20\)

For function (a), there is only one solution of \(\lambda_{1} = 20\). For (b), multiple solutions are possible, though when \(\lambda_{1} = 20\), then \(K \rightarrow \infty\). Therefore \(\lambda_{1} = 30\) was arbitrarily chosen and other parameter set as \(K = 40\).

# Test functions A and B for mRNA transcription
for (func in test_funcs) {
  # Set parameters for function A, where mRNA is transcribed at a constant rate
  if (toupper(func) == "A") {
    # Set rates for production, degradation and complex formation
    rate_parameters <- list(lambda_1 = 20, beta_1 = 1, lambda_2 = 1, beta_2 = 0.1)

    # Rate formulas per reaction
    # Values are calculated using parameters specified in the gillespie function
    reaction_rates = c("rate_constants$lambda_1", # mRNA (x1) transcription
                       "rate_constants$beta_1 * X[[\"x1\"]]", # mRNA (x1) degradation
                       "rate_constants$lambda_2 * X[[\"x1\"]]", # x2 translation
                       "rate_constants$beta_2 * X[[\"x2\"]]", # x2 degradation
                       "rate_constants$lambda_2 * X[[\"x1\"]]", # x3 translation
                       "rate_constants$beta_2 * X[[\"x3\"]]") # x3 degradation

  } else {
    # Set parameters for function B, where mRNA is self-repressed
    rate_parameters <- list(lambda_1 = 30, K = 40, beta_1 = 1, lambda_2 = 1, beta_2 = 0.1)

    reaction_rates = c("rate_constants$lambda_1 * (rate_constants$K) / (rate_constants$K + X[[\"x1\"]])", # mRNA (x1) transcription
                       "rate_constants$beta_1 * X[[\"x1\"]]", # mRNA (x1) degradation
                       "rate_constants$lambda_2 * X[[\"x1\"]]", # x2 translation
                       "rate_constants$beta_2 * X[[\"x2\"]]", # x2 degradation
                       "rate_constants$lambda_2 * X[[\"x1\"]]", # x3 translation
                       "rate_constants$beta_2 * X[[\"x3\"]]") # x3 degradation
  }

  # Run the Gillespie algorithm
  simulation_results <- gillespie(X = X_initial,
                                  rate_constants = rate_parameters,
                                  deltas = reaction_deltas,
                                  rates = reaction_rates,
                                  N = N_iterations,
                                  verbose = sim_verbose)

  if (toupper(func) == "A") {
    saveRDS(simulation_results, file = file.path(sim_folder, "sim_A.rds"))
  } else {
    saveRDS(simulation_results, file = file.path(sim_folder, "sim_B.rds"))
  }
}

Molecule Abundance Over Time

The function below plots a time trace of molecule abundance over time.

# Create plot of molecule abundance over time
timePlot <- function(molecule_counts, plot_colours = c("red", "deepskyblue", "purple"),
                     title = "Number of Molecules Over Time",
                     xaxis_label = "Time (s)", yaxis_label = "Number of Molecules",
                     xlimits = NA, ylimits = NA) {
  # Convert data into form for plotting
  molecules_time_df <- data.frame(n_molecules = unlist(c(molecule_counts)),
                                  molecule_type = row.names(molecule_counts),
                                  time = as.numeric(rep(colnames(molecule_counts),
                                                        each = nrow(molecule_counts))))
  # Plot molecules over time
  time_plot <- ggplot(data = molecules_time_df,
                      aes(x = time, y = n_molecules,
                          group = molecule_type,
                          color = molecule_type)) +
    geom_line() +
    labs(title = title,
         x = xaxis_label,
         y = yaxis_label) +
    guides(color = guide_legend(title = "Molecule Type")) +
    scale_color_manual(labels = c(expression("mRNA (" ~ x[1] ~ ")"),
                                  expression("Protein (" ~ x[2] ~ ")"),
                                  expression("Protein (" ~ x[3] ~ ")")),
                       values = plot_colours)
  
  if (!is.na(xlimits)) {
    # Set x-axis limits
    time_plot <- time_plot + scale_x_continuous(limits = xlimits)
  }
  if (!is.na(ylimits)) {
    # Set y-axis limits
    time_plot <- time_plot + scale_y_continuous(limits = ylimits)
  }
  
  return(time_plot)
}

After running the simulations, the average number of molecules of each species is printed. This confirms that for both functions, the calculated set of parameters leads to \(\left\langle x_{1} \right\rangle \simeq 20\) after the system reaches stationarity.

# Store run simulations and plots
simulations <- list()
time_traces <- list()

for (func in test_funcs) {
  # Open the saved simulations for functions (a) and (b)
  if (func == "A") {
    simulations[[func]] <- readRDS(file = file.path(sim_folder, "sim_A.rds"))
  } else {
    simulations[[func]] <- readRDS(file = file.path(sim_folder, "sim_B.rds"))
  }
  
  # Print average molecule abundance and efficiency
  message(paste0("Function ", func, ":",
                 "\nAverage <x1>: ", signif(mean(unlist(simulations[[func]]$molecules_over_time["x1",])), 5),
                 "\nAverage <x2>: ", signif(mean(unlist(simulations[[func]]$molecules_over_time["x2",])), 5),
                 "\nAverage <x3>: ", signif(mean(unlist(simulations[[func]]$molecules_over_time["x3",])), 5),
                 "\n"))
  
  # # Calculate efficiency for the simulation
  # simulation_stats <- calculateStats(X_over_time = simulations[[func]]$molecules_over_time,
  #                                    rate_constants = rate_parameters,
  #                                    jump_times = simulations[[func]]$jump_times,
  #                                    function_type = func)
  # message(simulation_stats)
  
  # Create plot of molecules over time
  time_traces[[func]] <- timePlot(simulations[[func]]$molecules_over_time, 
                                  title = paste("Function", func),
                                  xlimits = c(0, 2100),
                                  ylimits = c(0, 300))
}
Function A:
Average <x1>: 20.794
Average <x2>: 203.61
Average <x3>: 201.78

Function B:
Average <x1>: 20.455
Average <x2>: 199.47
Average <x3>: 200.73

Results

In the plot of molecules over time, the number of mRNA molecules quickly drops to around 20 before remaining fairly stable with little fluctuation for both functions. Function (b) converges to 20 slightly faster due to negative feedback in mRNA production rate which adjusts the system back to a steady state. For both, reporter protein concentration grows rapidly before reaching a stable state around 200 in which the molecules fluctuate between approximately 150 - 250. This model therefore captures a common situation in a cell where mRNA has low abundance relative to protein.

The approximately 10 fold increase between mRNA and protein abundance can be explained as \(\beta_{1} = 10 \beta_{2}\). This means mRNA molecules are degraded at a much faster rate than proteins, reflecting their instability within a cell. By contrast, proteins are modeled to be much more stable as in reality they are held together by strong covalent peptide bonds.

In general, both reporters are positively correlated with one another as fluctuations rise and fall within similar periods of time. This effect likely propagates from small perturbations in mRNA abundance. However, it is averaged across time causing a delay from the time of transcription and degradation of mRNA. Occasionally protein fluctuations become out of sync reflecting the stochasticity of protein synthesis and proteolysis.

# Create folder to save plots
plot_folder <- file.path(getwd(), "Plots")
dir.create(plot_folder)

# Combine traces as subplots
time_subplots <- ggarrange(time_traces[["A"]], time_traces[["B"]],
                           nrow = 2, widths = c(1, 1),
                           common.legend = TRUE, legend = "right")
time_subplots <- annotate_figure(time_subplots,
                                 top = text_grob("Molecules Over Time",
                                                 face = "bold", size = 14))

# Save the plot
pdf(file.path(plot_folder, "Molecules_Over_Time.pdf"), width = 18, height = 8)
print(time_subplots)
invisible(dev.off())


print(time_subplots)

Algorithm Statistics

The normalised variance of average mRNA abundance, \(\eta_{11}\), and normalized covariances \(\eta_{12}\), \(\eta_{23}\) for the functions are as follows:

  1. \(f(x_{1}) = \lambda_{1}\)
    • \(\eta_{11} = \frac{1}{\left\langle x_{1} \right\rangle}\)
    • \(\eta_{12} = \frac{1}{\left\langle x_{1} \right\rangle} \frac{\beta_{2}}{\beta_{1} + \beta_{2}}\)
    • \(\eta_{23} = \frac{1}{\left\langle x_{1} \right\rangle} \frac{\beta_{2}}{\beta_{1} + \beta_{2}}\)

  2. \(f(x_{1}) = \lambda_{1} \frac{K}{K + x_{1}}\)
    • ${11} = \(</li> <li>\){11} = \(</li> <li>\)_{11} = $
# Calculates run time statistics as normalized variance and covariance
calculateStats <- function(X_over_time, rate_constants, jump_times,
                           function_type = "A") {
  
  # Extract the rate constant parameters
  lambda_1 <- rate_constants$lambda_1
  lambda_2 <- rate_constants$lambda_2
  beta_1 <- rate_constants$beta_1
  beta_2 <- rate_constants$beta_2
  K <- rate_constants$K
  
  # Calculate the average number of x1, x2 and x3 molecules
  x_averages <- list()
  
  for (x in rownames(X_over_time)) {
    x_averages[[x]] <- weighted.mean(x = unlist(X_over_time[x,]), w = jump_times)
  }
  
  # Calculate analytic etas
  if (toupper(function_type) == "A") {
    # Calculate normalised variance for function (a)
    eta_11 <- 1 / x_averages[["x1"]]
    
    # Normalised co-variance
    eta_12 <- eta_11 * (beta_2 / (beta_1 + beta_2))
    eta_23 <- eta_12
    
  } else {
    # Calculate placeholder alpha for function (b)
    alpha <- x_averages[["x1"]] / (K + x_averages[["x1"]])
    
    eta_11 <- 1 / (x_averages[["x1"]] + alpha)
    eta_12 <- eta_11 * (beta_2 / (beta_1 + (beta_1 * alpha) + beta_2))
    eta_23 <- eta_12
  }
  
  # Calculate weighted covariance between x1, x2 and x3 molecules
  abundance_df <- data.frame(t(X_over_time))
  abundance_df <- data.frame(lapply(abundance_df, as.numeric))
  molecule_cov <- cov.wt(abundance_df, as.vector(jump_times))$cov
  
  # Calculate numeric etas
  num_eta11 <- molecule_cov["x1", "x1"] / (x_averages[["x1"]] * x_averages[["x1"]])
  num_eta12 <- molecule_cov["x1", "x2"] / (x_averages[["x1"]] * x_averages[["x2"]])
  num_eta23 <- molecule_cov["x2", "x3"] / (x_averages[["x2"]] * x_averages[["x3"]])
  
  # Record results
  results <- list(analytical = list(eta_11 = eta_11,
                                    eta_12 = eta_12,
                                    eta_23 = eta_23),
                  numerical = list(eta_11 = num_eta11,
                                   eta_12 = num_eta12,
                                   eta_23 = num_eta23))
  
  return(results)
}

A parameter search was performed for functions (a) and (b) through testing 60 \(\lambda_{1}\) values within the range [0.1, 100] for (a) and 64 combinations of \(\lambda_{1}\) and \(K\) values within the range [0.1, 100] for (b). For each parameter set, the Gillespie algorithm was run for 100,000 iterations. The normalized variance of the mRNA (\(\eta_{11}\)), normalized covariance between the mRNA and a reporter protein (\(\eta_{12}\)) and the normalized covariance between the two reporter proteins (\(\eta_{23}\)) were calculated using the analytical \(\eta\) values for function (a) and above for function (b). \

Additionally, a second method for calculating \(\eta_{11}\), \(\eta_{12}\) and \(\eta_{23}\), referred to as the numerical method, is included for comparison. Covariance between abundances of two molecular species weighted by jump times between reactions was calculated and normalised by the average molecule abundances based on the principal that…

# Test different parameters
parameterSearch <- function(test_lambdas_1 = c(1), test_lambdas_2 = c(1),
                            test_betas_1 = c(1), test_betas_2 = c(1),
                            test_Ks = c(NA), X, deltas, rates, N = 50000,
                            flux_threshold = 0.1, stop_at_stationary = FALSE,
                            min_N = 10000, function_type = "A", verbose = 0,
                            previous_search_stats = data.frame()) {
  
  # Record parameters and their results
  parameter_combination <- data.frame()
  sim_results <- list()
  stats_results <- list()
  
  # Assign an ID for the combination
  id <- 0
  
  # Test each parameter combination
  for (l1 in test_lambdas_1) {
    for (l2 in test_lambdas_2) {
      for (b1 in test_betas_1) {
        for (b2 in test_betas_2) {
          for (k in test_Ks) {
            
            new_params <- list(lambda_1 = l1,
                               lambda_2 = l2,
                               beta_1 = b1,
                               beta_2 = b2,
                               K = k)
            
            # Check whether combination was tested previously in a different run
            continue_search <- TRUE
            
            if (nrow(previous_search_stats) > 0) {
              if (nrow(merge(new_params, previous_search_stats)) > 0) {
                continue_search <- FALSE
              }
            }
            
            if (continue_search) {
              # Set unique ID
              id <- id + 1
              
              if (verbose > 0) {
                message(paste("Reached step", id))
                message(paste("Testing", toString(new_params)))
              }
              
              # Record the lambdas, betas and K
              parameter_combination <- rbind(parameter_combination, new_params)
              
              # Run the Gillespie algorithm
              test_simulation <- gillespie(X = X_initial,
                                           rate_constants = new_params,
                                           deltas = deltas,
                                           rates = rates,
                                           N = N,
                                           flux_threshold = flux_threshold,
                                           stop_at_stationary = stop_at_stationary,
                                           min_N = min_N)
              
              # Calculate normalised variance and covariances
              stats <- calculateStats(X_over_time = test_simulation$molecules_over_time,
                                      rate_constants = new_params,
                                      jump_times = test_simulation$jump_times,
                                      function_type = function_type)
              
              # Record the results for the parameter combination
              sim_results[[id]] <- test_simulation
              stats_results[[id]] <- stats
            }
          }
        }
      }
    }
  }
  
  # Combine parameters with stats
  parameter_stats <- cbind(parameter_combination, do.call(rbind, stats_results))
  parameter_stats <- data.frame(lapply(parameter_stats, as.numeric))
  
  return(list(sim_results = sim_results,
              parameter_stats = parameter_stats))
}
LS0tDQp0aXRsZTogPGI+R2lsbGVzcGllIG1STkEgU2ltdWxhdG9yPC9iPg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9IA0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IFRSVUUpIA0KYGBgDQoNCjxpbWcgc3JjPSJodHRwczovL2F2YXRhcnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3UvNjEzNTQ4MzMiIGFsaWduPSJsZWZ0IiB3aWR0aD0iNzAiIGhlaWdodD0iNzAiPg0KDQpDb2RlIGJ5IFtUb21NYWtlc1RoaW5nc10oaHR0cHM6Ly9naXRodWIuY29tL1RvbU1ha2VzVGhpbmdzL0dlbm9tZS1TZXF1ZW5jZS1BbmFseXNpcy1ITU0pDQoNCjxicj48YnI+PGJyPg0KDQojIyBEdWFsIFJlcG9ydGVyIE1ldGhvZA0KQSBtb2RpZmllZCBnZW5lIHByb2R1Y2VzIHR3byBwaWVjZXMgb2YgbVJOQSAoPHNwYW4gc3R5bGU9ImNvbG9yOnJlZCI+JHhfezF9JDwvc3Bhbj4pIHdoaWNoIHN0b2NoYXN0aWNhbGx5IHRyYW5zY3JpYmUgZGlmZmVyZW50IHJlcG9ydGVyIHByb3RlaW5zICg8c3BhbiBzdHlsZT0iY29sb3I6ZGVlcHNreWJsdWUiPiR4X3syfSQ8L3NwYW4+LCA8c3BhbiBzdHlsZT0iY29sb3I6cHVycGxlIj4keF97M30kPC9zcGFuPiksIGUuZy4gQ0ZQIGFuZCBHRlAsIGluZGVwZW5kZW50bHkgb2Ygb25lIGFub3RoZXIuIFRoaXMgZHVhbCByZXBvcnRlciBtZXRob2QgY2FuIGJlIG1vZGVsZWQgYnkgdGhlIGZvbGxvd2luZyByZWFjdGlvbiBzY2hlbWU6DQoNCiRcdGV4dHtUcmFuc2NyaXB0aW9uOiB9IHhfezF9IFx4cmlnaHRhcnJvd1tde2YoeF97MX0pfSB4X3sxfSArIDEkDQoNCiRcdGV4dHttUk5BIGRlZ3JhZGF0aW9uOiB9IFx0ZXh0eyB9IHhfezF9IFx4cmlnaHRhcnJvd1tde1xiZXRhX3sxfSB4X3sxfX0geF97MX0gLSAxJA0KDQokXHRleHR7VHJhbnNsYXRpb24gb2YgfSB4X3syfSBcdGV4dHs6IH0geF97Mn0gXHhyaWdodGFycm93W117XGxhbWJkYV97Mn0geF97MX19IHhfezJ9ICsgMSQNCg0KJFx0ZXh0e0RlZ3JhZGF0aW9uIG9mIH0geF97Mn0gXHRleHR7OiB9IHhfezJ9IFx4cmlnaHRhcnJvd1tde1xiZXRhX3syfSB4X3syfX0geF97Mn0gLSAxJA0KDQokXHRleHR7VHJhbnNsYXRpb24gb2YgfSB4X3szfSBcdGV4dHs6IH0geF97M30gXHhyaWdodGFycm93W117XGxhbWJkYV97Mn0geF97MX19IHhfezN9ICsgMSQNCg0KJFx0ZXh0e0RlZ3JhZGF0aW9uIG9mIH0geF97M30gXHRleHR7OiB9IHhfezN9IFx4cmlnaHRhcnJvd1tde1xiZXRhX3syfSB4X3szfX0geF97M30gLSAxJA0KDQo8YnI+DQoNCiMjIyBtUk5BIFByb2R1Y3Rpb24gUmF0ZQ0KVHdvIGRpZmZlcmVudCBzaXR1YXRpb25zIGFyZSBtb2RlbGVkLCB3aGVyZWJ5IG1STkEgdHJhbnNjcmlwdGlvbiBpcyBlaXRoZXIgY29uc3RhbnQgb3Igc2VsZi1yZXByZXNzaXZlLiBJbiBib3RoLCBtUk5BIGFyZSBkZWdyYWRlZCBvbmUgYXQgYSB0aW1lIGF0IGEgcmF0ZSAkXGJldGFfezF9IHhfezF9JDoNCg0KPG9sIHR5cGU9ImEiPg0KICA8bGk+PHNwYW4gc3R5bGU9ImNvbG9yOiNmYzk4MDMiPiRmKHhfezF9KSA9IFxsYW1iZGFfezF9JDwvc3Bhbj48L2xpPg0KICAgIDx1bD4NCiAgICAgIDxsaT5tUk5BIGFyZSB0cmFuc2NyaWJlZCBvbmUgYXQgYSB0aW1lIGluIGEgUG9pc3Npb24gcHJvY2VzcyBhdCBhIHJhdGUgJFxsYW1iZGFfezF9JDwvbGk+DQogICAgICA8bGk+VGhlIGNoZW1pY2FsIG1hc3RlciBlcXVhdGlvbiAoQ01FKSwgZGVzY3JpYmluZyB0aGUgdGltZSBldm9sdXRpb24gb2YgY29tcG9uZW50ICR4X3sxfSQsIGlzIHNpbXBseTogJFxmcmFje2QgUCh4X3sxfSl9e2R0fSA9IFx1bmRlcmJyYWNle1xsYW1iZGFfezF9IChQKHhfezF9IC0gMSkgLSBcbGFtYmRhX3sxfSBQKHhfezF9KX1fe1x0ZXh0e1N5bnRoZXNpc319ICsgXHVuZGVyYnJhY2V7XGJldGFfezF9ICh4X3sxfSArIDEpIFAoeF97MX0gKyAxKSAtIFxiZXRhIHhfezF9IFAoeF97MX0pfV97XHRleHR7RGVncmFkYXRpb259fSQ8L2xpPg0KICAgIDwvdWw+DQogIDxicj4NCiAgPGxpPjxzcGFuIHN0eWxlPSJjb2xvcjojMjQ4YWZmIj4kZih4X3sxfSkgPSBcbGFtYmRhX3sxfSBcZnJhY3tLfXtLICsgeF97MX19JDwvc3Bhbj48L2xpPg0KICAgIDx1bD4NCiAgICAgIDxsaT5TZXR0aW5nIHRoZSBwcm9kdWN0aW9uIHJhdGUgb2YgbVJOQSBhcyBhIGZ1bmN0aW9uICRmKHhfezF9KSQsIGFsbG93cyB0aGUgbW9kZWwgdG8gaW5jb3Jwb3JhdGUgcG9zaXRpdmUgLw0KICAgICAgbmVnYXRpdmUgZmVlZGJhY2s8L2xpPg0KICAgICAgPGxpPkluIHRoaXMgY2FzZSwgbVJOQSBpcyBzZWxmLXJlcHJlc3NpdmUgYXMgaXRzIHRyYW5zY3JpcHRpb24gaXMgaW52ZXJzZWx5IHByb3BvcnRpb25hbCB0byBpdHMgb3duIGNvbmNlbnRyYXRpb24NCiAgICAgICQoeF97MX0pJCBhbmQgYSByYXRlIGNvbnN0YW50ICRLJDwvbGk+DQogICAgPC91bD4NCjwvb2w+DQoNCjxicj4NCg0KIyMjIERvb2ItR2lsbGVzcGllIEFsZ29yaXRobQ0KVG8gc2ltdWxhdGUgdGhlIHN5c3RlbSBvdmVyIHRpbWUsIHRoZSBEb29iLUdpbGxlc3BpZSBhbGdvcml0aG0gaXMgYXBwbGllZDoNCg0KPG9sPg0KICA8bGk+Rm9yIGEgc3lzdGVtIGF0IHRpbWUgJHQgPSB0X3swfSQgd2l0aCAkbiQgY2hlbWljYWwgc3BlY2llcywgaW5pdGlhbGl6ZSB0aGUgc3lzdGVtJ3Mgc3RhdGUgYnkgc2V0dGluZyB0aGUgaW5pdGlhbA0KICBudW1iZXJzIG9mIG1vbGVjdWxlcyAkeCA9IHhfezF9LCB4X3syfSwgLi4uLCB4X3tufSQuPC9saT4NCiAgPGxpPlNldCB0aGUgJHAkIHJlYWN0aW9ucyByYXRlcyAkcl97MX0oeCksIHJfezJ9KHgpLCAuLi4sIHJfe3B9KHgpJCwgd2hlcmUgcmVhY3Rpb24gJGskIGFkZHMgJFxkZWx0YV97amt9JA0KICBtb2xlY3VsZXMuPC9saT4NCiAgPGxpPkNhbGN1bGF0ZSB0aGUgdG90YWwgcmVhY3Rpb24gcmF0ZSAkcl97VH0gPSBcc3VtXntOfV97az0xfXJfe2t9KHgpJCBmb3IgdGhlIHN5c3RlbSBhdCB0aW1lICR0JC48L2xpPg0KICA8bGk+U2VsZWN0IHRoZSB3YWl0aW5nIHRpbWUgdW50aWwgdGhlIG5leHQgcmVhY3Rpb24gZnJvbSBhbiBleHBvbmVudGlhbCBkaXN0cmlidXRpb24sIHdpdGggY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gJEYodCkgPSAxIC0gZV57LXJfe1R9dH0kLjwvbGk+DQogIDxsaT5TZWxlY3QgYSByZWFjdGlvbiAkaSQgd2l0aCBwcm9iYWJpbGl0eSAkXGZyYWN7cl97aX0oeCl9e3Jfe1R9KHgpfSQuPC9saT4NCiAgPGxpPkZvciBlYWNoIG1vbGVjdWxhciBzcGVjaWVzIGVmZmVjdGVkIGJ5IHJlYWN0aW9uICRpJCwgdXBkYXRlIGl0cyBjb25jZW50cmF0aW9uIGluIHRoZSBzeXN0ZW0gJHhfe2p9ICBceHJpZ2h0YXJyb3dbXXt9e30geF97an0gKyBcZGVsdGFfe2ppfSQuPC9saT4NCiAgPGxpPklmIHRoZSBtYXhpbXVtIG51bWJlciBvZiBpdGVyYXRpb25zIGlzIHJlYWNoZWQsIHN0b3Agc2ltdWxhdGlvbi4gT3RoZXJ3aXNlIGdvIGJhY2sgdG8gc3RlcCAzLjwvbGk+DQo8L29sPg0KDQo8YnI+DQoNCiMjIENvZGUNCiMjIyBTZXQgVXANCkltcG9ydCB0aGUgZm9sbG93aW5nIGxpYnJhcmllcywgc2V0IGEgc2VlZCBhbmQgZGVmaW5lIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4NCmBgYHtyfQ0KbGlicmFyeShTY2lWaWV3cykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkoZ2dwdWJyKQ0KDQojIFNldCBzZWVkIGZvciByZXByb2R1Y2FiaWxpdHkNCnNldC5zZWVkKDEyMykNCiMgU2V0IHdvcmtpbmcgZGlyZWN0b3J5DQpzZXR3ZCgiQzovVXNlcnMvcmVkZHMvRG9jdW1lbnRzL0dpdEh1Yi9HaWxsZXNwaWUiKQ0KYGBgDQoNCiMjIyBJbml0aWFsaXNlIFN5c3RlbSdzIFN0YXRlDQpBdCB0aW1lICR0X3tpfSA9IHRfezB9JCwgdGhlIGluaXRpYWwgbnVtYmVyIG9mIG1vbGVjdWxlcyBpcyBzZXQgc28gdGhhdCBlYWNoIG1vbGVjdWxhciBzcGVjaWVzIHdpdGggYXQgNTAuIFRoZSBzdG9pY2hpb21ldHJ5IG9mIHRoZSBzeXN0ZW0gKHRoZSBudW1iZXIgb2YgbW9sZWN1bGVzIGNyZWF0ZWQgb3IgZGVncmFkZWQgcGVyIHJlYWN0aW9uKSBpcyBlbmNvZGVkIGFzICRcZGVsdGEkIHZhbHVlcy4gRm9yIGV4YW1wbGUsIHJlYWN0aW9uICRyX3syfSQgZW50YWlscyBkZWdyYWRhdGlvbiBvZiBhIHNpbmdsZSAkeF97MX0kIG1vbGVjdWxlLCBhbmQgc28gJFxkZWx0YV97MTF9ID0gLTEkLg0KYGBge3J9DQojIFNldCBpbml0aWFsIG51bWJlciBvZiBtb2xlY3VsZXMNClhfaW5pdGlhbCA8LSBsaXN0KHgxID0gNTAsIHgyID0gNTAsIHgzID0gNTApDQoNCiMgQ2hhbmdlIGluIG1vbGVjdWxlIG51bWJlcnMgcGVyIHJlYWN0aW9uDQpyZWFjdGlvbl9kZWx0YXMgPC0gbGlzdChjKHgxID0gMSwgeDIgPSAwLCB4MyA9IDApLCAjIHgxIHRyYW5zY3JpcHRpb24NCiAgICAgICAgICAgICAgICAgICAgICAgIGMoeDEgPSAtMSwgeDIgPSAwLCB4MyA9IDApLCAjeDEgZGVncmFkYXRpb24NCiAgICAgICAgICAgICAgICAgICAgICAgIGMoeDEgPSAwLCB4MiA9IDEsIHgzID0gMCksICMgeDIgdHJhbnNsYXRpb24NCiAgICAgICAgICAgICAgICAgICAgICAgIGMoeDEgPSAwLCB4MiA9IC0xLCB4MyA9IDApLCAjIHgyIGRlZ3JhZGF0aW9uDQogICAgICAgICAgICAgICAgICAgICAgICBjKHgxID0gMCwgeDIgPSAwLCB4MyA9IDEpLCAjIHgzIHRyYW5zbGF0aW9uDQogICAgICAgICAgICAgICAgICAgICAgICBjKHgxID0gMCwgeDIgPSAwLCB4MyA9IC0xKSkgIyB4MyBkZWdyYWRhdGlvbg0KYGBgDQoNCiMjIyBEb29iLUdpbGxlc3BpZSBBbGdvcml0aG0NCiMjIyMgSGVscGVyIEZ1bmN0aW9ucw0KVGhlIGZvbGxvd2luZyBhcmUgaGVscGVyIGZ1bmN0aW9ucyB1c2VkIHRvIGNhbGN1bGF0ZSByZWFjdGlvbiByYXRlcyBhbmQgZ2VuZXJhdGUgcmFuZG9tIGp1bXAgdGltZXMuDQpgYGB7cn0NCiMgRHluYW1pY2FsbHkgY2FsY3VsYXRlIHRoZSByZWFjdGlvbiByYXRlIGdpdmVuIGFuIGVxdWF0aW9uDQpjYWxjdWxhdGVSYXRlIDwtIGZ1bmN0aW9uKHJhdGVfZXF1YXRpb24sIHJhdGVfY29uc3RhbnRzLCBYKSB7DQogICMgQ29udmVydHMgc3RyaW5nIHRvIGVxdWF0aW9uIGFuZCBldmFsdWF0ZXMgaXQNCiAgIyBFLmcuIGlmIHZhcmlhYmxlcyBiZXRhID0gMTAgYW5kIHgxID0gNSwgdGhlbiAiYmV0YSAqIFgkeDEiIGJlY29tZXMgYmV0YSAqIHgxIGFuZCA1MCBpcyByZXR1cm5lZA0KICBldmFsKHBhcnNlKHRleHQgPSByYXRlX2VxdWF0aW9uKSkNCn0NCg0KIyBHZW5lcmF0ZSByYW5kb20gd2FpdCB0aW1lIGJldHdlZW4gcmVhY3Rpb25zDQpjYWxjdWxhdGVKdW1wVGltZSA8LSBmdW5jdGlvbihyVCkgew0KICAjIEdlbmVyYXRlIHJhbmRvbSBudW1iZXIgYmV0d2VlbiAwIC0gMSBmcm9tIGEgdW5pZm9ybSBkaXN0cmlidXRpb24NCiAgdV90aW1lIDwtIHJ1bmlmKDEsIDAsIDEpDQogICMgQ2FsY3VsYXRlIHRoZSByYW5kb20ganVtcCB0aW1lIGJ5IGNvbnZlcnRpbmcgZnJvbSBhIHVuaWZvcm0gdG8gZXhwb25lbnRpYWxseSBkaXN0cmlidXRlZCBudW1iZXINCiAganVtcF90aW1lIDwtIC0gbG4odV90aW1lKSAvIHJUDQogIA0KICByZXR1cm4oanVtcF90aW1lKQ0KfQ0KYGBgDQoNCiMjIyMgRG9vYi1HaWxsZXNwaWUgSW1wbGVtZW50YXRpb24NCldpdGhpbiB0aGUgRG9vYi1HaWxsZXNwaWUgYWxnb3JpdGhtLCB0aGUgbnVtYmVyIG9mIG1vbGVjdWxlcyBvdmVyIHRpbWUsIGp1bXAgdGltZSBiZXR3ZWVuIGVhY2ggcmVhY3Rpb24gYW5kIGEgdHJhY2UtYmFjayBvZiB3aGljaCByZWFjdGlvbnMgYXJlIHNlbGVjdGVkIGFyZSByZWNvcmRlZC4gVGhlIHNpbXVsYXRpb24gaXMgcnVuIGZvciAkTiQgaXRlcmF0aW9ucywgZWFjaCBvZiB3aGljaCByZXByZXNlbnRzIGEgdGltZSBzdGVwIGluIHdoaWNoIGEgcmVhY3Rpb24gb2NjdXJzLiBUaGUgcmVhY3Rpb24gcmF0ZSAkcl97a30kIGZvciBlYWNoIHJlYWN0aW9uICRrJCBpcyB1cGRhdGVkIGZvciBlYWNoIHRpbWUgc3RlcC4gVGhpcyBpcyBiZWNhdXNlIHRoZSBkZWdyYWRhdGlvbiByZWFjdGlvbnMgZHluYW1pY2FsbHkgY2hhbmdlIGRlcGVuZGluZyBvbiB0aGUgY29uY2VudHJhdGlvbiBvZiBlYWNoIHR5cGUgb2YgbW9sZWN1bGUuIFRoZSB0b3RhbCByYXRlICRyX3tUfSQgbXVzdCB0aGVyZWZvcmUgYWxzbyBiZSByZWNhbGN1bGF0ZWQuDQoNCiRyX3tUfSA9IFxzdW1ee059X3trPTF9IHJfe2t9KHgpJA0KDQpUaGUganVtcCB0aW1lcyBiZXR3ZWVuIHJlYWN0aW9ucyBhcmUgZGVyaXZlZCBmcm9tIGFuIGV4cG9uZW50aWFsIGRpc3RyaWJ1dGlvbi4gSW4gUiwgdGhpcyBpcyBhY2hpZXZlZCB0aHJvdWdoIGdlbmVyYXRpbmcgYSByYW5kb20gbnVtYmVyIGJldHdlZW4gMCAtIDEgZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uLCBhbmQgdGhlbiBjb252ZXJ0aW5nIHRoaXMgdG8gYSB0aW1lICR0X3tpfSQgdGhyb3VnaCBpbnZlcnRpbmcgdGhlIGV4cG9uZW50aWFsIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uLg0KDQokdV97dGltZX0gPSBGKHQpID0gMSAtIGVeey1yX3tUfXR9JA0KDQokdCA9IC0gXGZyYWN7bG4odV97dGltZX0pfXtyX3tUfX0kDQoNCk5leHQgdGhlIHByb2JhYmlsaXR5IG9mIGVhY2ggcmVhY3Rpb24gJGskIG9jY3VycmluZyBpcyBldmFsdWF0ZWQgYmFzZWQgdXBvbiB0aGUgcmVhY3Rpb24gcmF0ZXMsICRyX3trfS8gcl97VH0kLiBUaGUgY3VtdWxhdGl2ZSBzdW1zIG9mIHJlYWN0aW9uIHByb2JhYmlsaXRpZXMgYXJlIGNhbGN1bGF0ZWQgdG8gcGFydGl0aW9uIHRoZSBwcm9iYWJpbGl0aWVzIGludG8gc2VjdGlvbnMuIEFub3RoZXIgbnVtYmVyIGlzIHRoZW4gZ2VuZXJhdGVkIGZyb20gYSB1bmlmb3JtIGRpc3RyaWJ1dGlvbiBhbmQgY29tcGFyZWQgYWdhaW5zdCB0aGUgcGFydGl0aW9uIHRvIGRldGVybWluZSB3aGljaCByZWFjdGlvbiB3aWxsIG9jY3VyLiBUaGUgbnVtYmVyIG9mIG1vbGVjdWxlcyBpcyB1cGRhdGVkIGFjY29yZGluZyB0byB0aGUgY2hvc2VuIHJlYWN0aW9uIGJ5IGFkZGluZyB0aGUgJFxkZWx0YV97a30kIHZhbHVlcyB0byB0aGUgY3VycmVudCBzdGF0ZSBvZiB0aGUgc3lzdGVtLg0KDQpTaW5jZSBpdCBpcyB1bnJlYWxpc3RpYyBmb3IgdGhlIG51bWJlciBvZiBtb2xlY3VsZXMgdG8gYmVjb21lIG5lZ2F0aXZlLCBhIGNoZWNrIGhhcyBiZWVuIGluY2x1ZGVkIHRvIHByZXZlbnQgaW1wb3NzaWJsZSBkZWdyYWRhdGlvbiByZWFjdGlvbnMgZnJvbSBiZWluZyBzZWxlY3RlZCBhdCBlYWNoIGdpdmVuIHRpbWUgcG9pbnQuIEZvciBleGFtcGxlLCBhIHJlYWN0aW9uIGludm9sdmluZyAkeF97MX0gLSAxJCBjYW5ub3Qgb2NjdXIgaWYgJHhfezF9ID0gMCQgYXQgdGltZSAkdF97aX0kLg0KDQpgYGB7cn0NCiMgRG9vYi1HaWxsZXNwaWUgYWxnb3JpdGhtDQojIFg6IGluaXRpYWwgbnVtYmVyIG9mIG1vbGVjdWxlcw0KIyByYXRlX2NvbnN0YW50czogbGlzdCBvZiBjb25zdGFudCBwYXJhbWV0ZXJzIGZvciBjYWxjdWxhdGluZyByYXRlcw0KIyBkZWx0YXM6IGxpc3Qgb2YgdmVjdG9ycyBmb3IgdGhlIG51bWJlciBvZiBtb2xlY3VsZXMgdGhhdCB3aWxsIGNoYW5nZSBwZXIgcmVhY3Rpb24NCiMgcmF0ZXM6IGZvcm11bGFzIGZvciByZWFjdGlvbiByYXRlcyBwZXIgcmVhY3Rpb24NCiMgTjogbWF4IG51bWJlciBvZiBpdGVyYXRpb25zDQojIHZlcmJvc2U6IHNldCBhcyA+IDAgZm9yIGRlYnVnZ2luZyBtZXNzYWdlcw0KZ2lsbGVzcGllIDwtIGZ1bmN0aW9uKFgsIHJhdGVfY29uc3RhbnRzLCBkZWx0YXMsIHJhdGVzLCBOID0gMTAwMDAwLCANCiAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gMCkgew0KICAjIFJlY29yZCBudW1iZXIgb2YgbW9sZWN1bGVzIG92ZXIgdGltZSBpbiBhIG1hdHJpeA0KICBYX292ZXJfdGltZSA8LSBtYXRyaXgoZGF0YSA9IFgsIG5yb3cgPSBsZW5ndGgoWCkpDQogICMgUmVjb3JkIHdoaWNoIHJlYWN0aW9uIG9jY3VycmVkIGF0IGVhY2ggdGltZSBzdGVwIHQNCiAgcmVhY3Rpb25fdHJhY2UgPC0gYygpDQogICMgUmVjb3JkIHRpbWUgdCBiZXR3ZWVuIHJlYWN0aW9ucw0KICB0aW1lcyA8LSBjKDApDQogIA0KICAjIERldGVybWluZSB3aGljaCByZWFjdGlvbnMgY29udGFpbnMgbW9sZWN1bGUgZGVncmFkYXRpb24NCiAgZGVncmFkYXRpb25fcmVhY3Rpb25zIDwtIHdoaWNoKHVubGlzdChsYXBwbHkoZGVsdGFzLCBmdW5jdGlvbih4KSBhbnkoeCA8IDApKSkpDQogIA0KICAjIENhbGN1bGF0ZSB0aGUgbWFnbml0dWRlIG9mIHRoZSBoaWdoZXN0IGRlZ3JhZGF0aW9uIGRlbHRhIHZhbHVlDQogIGRlZ3JhZGF0aW9uX2RlbHRhcyA8LSB1bmxpc3QoZGVsdGFzW2RlZ3JhZGF0aW9uX3JlYWN0aW9uc10pDQogIG1heF9kZWdyYXRpb24gPC0gYWJzKGRlZ3JhZGF0aW9uX2RlbHRhc1t3aGljaC5tYXgoYWJzKGRlZ3JhZGF0aW9uX2RlbHRhcykpXSkNCiAgDQogIG4gPC0gMA0KICBzdGF0aW9uYXJpdHlfcmVhY2hlZCA8LSBGQUxTRQ0KICANCiAgIyBSdW4gc2ltdWxhdGlvbiBOIHRpbWVzIG9yIHVudGlsIHN0YXRpb25hcml0eSBpcyByZWFjaGVkDQogIHdoaWxlIChuIDwgTiAmICFzdGF0aW9uYXJpdHlfcmVhY2hlZCkgew0KICAgIG4gPC0gbiArIDENCiAgICANCiAgICAjIEdldCB0aGUgbnVtYmVyIG9mIG1vbGVjdWxlcyBhdCB0aW1lIHN0ZXAgdA0KICAgIGN1cnJlbnRfbW9sZWN1bGVzIDwtIHVubGlzdChYX292ZXJfdGltZVssIGRpbShYX292ZXJfdGltZSlbMl1dKQ0KICAgIG5hbWVzKGN1cnJlbnRfbW9sZWN1bGVzKSA8LSBuYW1lcyhYKQ0KICAgIA0KICAgICMgSW5pdGlhbGx5IHNldCBhbGwgcmVhY3Rpb25zIGFzIHBvc3NpYmxlIGF0IHRoZSBjdXJyZW50IHRpbWUgc3RlcA0KICAgIHBvc3NpYmxlX3JlYWN0aW9ucyA8LSAxOmxlbmd0aChyYXRlcykNCiAgICANCiAgICAjIENoZWNrIGlmIGFueSBkZWdyYWRhdGlvbiByZWFjdGlvbnMgd2lsbCBjYXVzZSBhIG5lZ2F0aXZlIG51bWJlciBvZiBtb2xlY3VsZXMNCiAgICBpZiAoYW55KGN1cnJlbnRfbW9sZWN1bGVzIC0gcmVwKG1heF9kZWdyYXRpb24sIGxlbmd0aChjdXJyZW50X21vbGVjdWxlcykpIDwgMCkpIHsNCiAgICAgICMgRmluZCB3aGljaCByZWFjdGlvbnMgY2Fubm90IG9jY3VyDQogICAgICBpbXBvc3NpYmxlX3JlYWN0aW9ucyA8LSBjKCkNCiAgICAgIA0KICAgICAgZm9yIChyZWFjdGlvbl9pZHggaW4gZGVncmFkYXRpb25fcmVhY3Rpb25zKSB7DQogICAgICAgICMgQ2FsY3VsYXRlIG51bWJlciBvZiBtb2xlY3VsZXMgbGVmdCBpZiByZWFjdGlvbiBvY2N1cnMNCiAgICAgICAgbl9tb2xlY3VsZXMgPC0gdW5saXN0KFhfb3Zlcl90aW1lWywgZGltKFhfb3Zlcl90aW1lKVsyXV0pICsgZGVsdGFzW1tyZWFjdGlvbl9pZHhdXQ0KICAgICAgICANCiAgICAgICAgIyBSZWNvcmQgcmVhY3Rpb24gYXMgaW1wb3NzaWJsZSBpZiB0aGUgbnVtYmVyIG9mIGFueSBtb2xlY3VsZSBpcyBiZWxvdyB6ZXJvDQogICAgICAgIGlmIChhbnkobl9tb2xlY3VsZXMgPCAwKSkgew0KICAgICAgICAgIGltcG9zc2libGVfcmVhY3Rpb25zIDwtIGMoaW1wb3NzaWJsZV9yZWFjdGlvbnMsIHJlYWN0aW9uX2lkeCkNCiAgICAgICAgfQ0KICAgICAgfQ0KICAgICAgIyBVcGRhdGUgbGlzdCBvZiBwb3NzaWJsZSByZWFjdGlvbnMgdG8gcmVtb3ZlIGltcG9zc2libGUgb25lcw0KICAgICAgcG9zc2libGVfcmVhY3Rpb25zIDwtIHBvc3NpYmxlX3JlYWN0aW9uc1shcG9zc2libGVfcmVhY3Rpb25zICVpbiUgaW1wb3NzaWJsZV9yZWFjdGlvbnNdDQogICAgfQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIHRoZSBjdXJyZW50IHJlYWN0aW9uIHJhdGVzIGZvciB0aGUgc3lzdGVtIGF0IHRpbWUgdCwgZXhjbHVkaW5nIGltcG9zc2libGUgcmVhY3Rpb25zDQogICAgY3VycmVudF9yYXRlcyA8LSB1bm5hbWUodW5saXN0KGxhcHBseShyYXRlc1twb3NzaWJsZV9yZWFjdGlvbnNdLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb24ocmF0ZV9lcXVhdGlvbikgY2FsY3VsYXRlUmF0ZShyYXRlX2VxdWF0aW9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYXRlX2NvbnN0YW50cywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudF9tb2xlY3VsZXMpKSkpDQogICAgIyBDYWxjdWxhdGUgdGhlIHRvdGFsIHJlYWN0aW9uIHJhdGUNCiAgICByX3RvdGFsIDwtIHN1bShjdXJyZW50X3JhdGVzKQ0KICAgIA0KICAgICMgR2VuZXJhdGUgdGhlIGp1bXAgdGltZSBmb3IgdGhlIG5leHQgcmVhY3Rpb24NCiAgICB0aW1lcyA8LSBjKHRpbWVzLCBjYWxjdWxhdGVKdW1wVGltZShyX3RvdGFsKSkNCiAgICANCiAgICAjIENhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgb2YgZWFjaCByZWFjdGlvbiBvY2N1cnJpbmcNCiAgICByZWFjdGlvbl9wcm9icyA8LSBjdXJyZW50X3JhdGVzIC8gcl90b3RhbA0KICAgIHByb2JfcGFydGl0aW9ucyA8LSBjdW1zdW0ocmVhY3Rpb25fcHJvYnMpDQoNCiAgICAjIFNlbGVjdCBhIHJhbmRvbSByZWFjdGlvbiAocmVsYXRpdmUgdG8gdGhlIHByb2JhYmlsaXR5IG9mIGVhY2ggb2NjdXJyaW5nKQ0KICAgIHVfcmVhY3Rpb24gPC0gcnVuaWYoMSwgMCwgMSkNCiAgICByZWFjdGlvbl9pZHggPC0gbWluKHdoaWNoKHByb2JfcGFydGl0aW9ucyA+IHVfcmVhY3Rpb24pKQ0KICAgIA0KICAgICMgQ2FsY3VsYXRlIHRoZSBtb2xlY3VsZSBudW1iZXJzIGFmdGVyIHRoZSBjaG9zZW4gcmVhY3Rpb24NCiAgICBuX21vbGVjdWxlcyA8LSBjdXJyZW50X21vbGVjdWxlcyArIGRlbHRhc1twb3NzaWJsZV9yZWFjdGlvbnNdW1tyZWFjdGlvbl9pZHhdXQ0KICAgIA0KICAgICMgUmVjb3JkIHdoaWNoIHJlYWN0aW9uIG9jY3VycmVkDQogICAgcmVhY3Rpb25fdHJhY2UgPC0gYyhyZWFjdGlvbl90cmFjZSwgcmVhY3Rpb25faWR4KQ0KICAgICMgVXBkYXRlIHRoZSBudW1iZXIgb2YgbW9sZWN1bGVzIGJ5IHRoZSBjaG9zZW4gcmVhY3Rpb24NCiAgICBYX292ZXJfdGltZSA8LSBjYmluZChYX292ZXJfdGltZSwgbl9tb2xlY3VsZXMpDQogICAgDQogICAgaWYgKHZlcmJvc2UgPiAwICYgbiAlJSAxMDAwMCA9PSAwKSB7DQogICAgICBtZXNzYWdlKHBhc3RlKCJSZWFjaGVkIGl0ZXJhdGlvbiIsIG4pKQ0KICAgIH0NCiAgfQ0KICANCiAgIyBSZW5hbWUgY29sdW1ucyB0byB0aW1lIHQgZm9yIGVhY2ggcmVhY3Rpb24NCiAgY29sbmFtZXMoWF9vdmVyX3RpbWUpIDwtIGN1bXN1bSh0aW1lcykNCiAgDQogIHJldHVybihsaXN0KG1vbGVjdWxlc19vdmVyX3RpbWUgPSBYX292ZXJfdGltZSwNCiAgICAgICAgICAgICAgcmVhY3Rpb25fdHJhY2UgPSByZWFjdGlvbl90cmFjZSwNCiAgICAgICAgICAgICAganVtcF90aW1lcyA9IHRpbWVzLA0KICAgICAgICAgICAgICBuX2l0ZXJhdGlvbnMgPSBuKSkNCn0NCmBgYA0KDQojIyMgVGVzdGluZyBtUk5BIFByb2R1Y3Rpb24gUmF0ZXMNClRoZSBEb29iLUdpbGxlc3BpZSBhbGdvcml0aG0gaXMgc2V0IHRvIHJ1biBmb3IgMjUwLDAwMCBpdGVyYXRpb25zIHNlcGFyYXRlbHkgd2l0aCBmdW5jdGlvbnMgKGEpIGFuZCAoYikgYXMgdGhlIG1STkEgdHJhbnNjcmlwdGlvbiByYXRlcy4gQXMgZWFjaCBzaW11bGF0aW9uIHRha2VzIGEgd2hpbGUgdG8gcnVuLCBpdCBpcyBzYXZlZCB0byBmaWxlLg0KDQpgYGB7cn0NCiMgU2V0IHRoZSBmdW5jdGlvbnMgdG8gdGVzdA0KdGVzdF9mdW5jcyA8LSBjKCJBIiwgIkIiKQ0KIyBNYXggbnVtYmVyIG9mIGl0ZXJhdGlvbnMgdG8gcnVuIHRoZSBHaWxsZXNwaWUgYWxnb3JpdGhtDQpOX2l0ZXJhdGlvbnMgPC0gMjUwMDAwDQojIFNldCBhcyA+IDAgZm9yIGRlYnVnZ2luZyBtZXNzYWdlcw0Kc2ltX3ZlcmJvc2UgPC0gMA0KDQojIENyZWF0ZSBmb2xkZXIgKGlmIGRvZXNuJ3QgZXhpc3QpIHRvIHN0b3JlIHNpbXVsYXRpb25zDQpzaW1fZm9sZGVyIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAiU2ltdWxhdGlvbnMiKQ0KZGlyLmNyZWF0ZShzaW1fZm9sZGVyKQ0KYGBgDQoNCkZvciBib3RoIHNpbXVsYXRpb25zLCByZWFjdGlvbiByYXRlIGNvbnN0YW50cyBhcmUgc2V0IHdpdGggJFxiZXRhX3sxfSA9IFxsYW1iZGFfezJ9ID0gMSQgYW5kICRcYmV0YV97Mn0gPSAwLjEkLiBUbyBlbnN1cmUgdGhhdCB0aGUgYXZlcmFnZSBtUk5BIGNvbmNlbnRyYXRpb24gY29udmVyZ2VzIHRvIHRoZSBzYW1lIG51bWJlciBpbiBlYWNoIHNpbXVsYXRpb24sIGUuZy4gJFxsZWZ0XGxhbmdsZSB4X3sxfSBccmlnaHRccmFuZ2xlIFxzaW1lcSAyMCQsIHZhbHVlcyBvZiAkXGxhbWJkYV97MX0kIGFuZCAkSyQgd2VyZSBkZXRlcm1pbmVkIGZvciBmdW5jdGlvbnMgKGEpIGFuZCAoYikgc2VwYXJhdGVseS4gVGhlc2Ugd2VyZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBwcmluY2lwYWwgdGhhdCBhdCBzdGF0aW9uYXJpdHksIHRoZSBhdmVyYWdlIHJhdGUgb2YgcHJvZHVjdGlvbiBlcXVhbHMgdGhlIGF2ZXJhZ2UgcmF0ZSBvZiBkZWdyYWRhdGlvbiwgaS5lLiAkXGxlZnRcbGFuZ2xlIFJeeyt9IFxyaWdodFxyYW5nbGUgPSBcbGVmdFxsYW5nbGUgUl57LX0gXHJpZ2h0XHJhbmdsZSQuDQoNCjxvbCB0eXBlPSJhIj4NCiAgPGxpPjxzcGFuIHN0eWxlPSJjb2xvcjojZmM5ODAzIj4kZih4X3sxfSkgPSBcbGFtYmRhX3sxfSQ8L3NwYW4+PC9saT4NCiAgICA8dWwgc3R5bGU9Imxpc3Qtc3R5bGUtdHlwZTogbm9uZTsiPg0KICAgICAgPGxpPiRcbGFtYmRhX3sxfSA9IFxiZXRhX3sxfSBcbGVmdFxsYW5nbGUgeF97MX0gXHJpZ2h0XHJhbmdsZSQ8L2xpPg0KICAgICAgPGxpPiRcbGFtYmRhX3sxfSA9IDEgXGNkb3QgMjAkPC9saT4NCiAgICAgIDxsaS8+JFx0aGVyZWZvcmUgXGxhbWJkYV97MX0gPSAyMCQ8L2xpPg0KICAgIDwvdWw+DQogIDxicj4NCiAgPGxpPjxzcGFuIHN0eWxlPSJjb2xvcjojMjQ4YWZmIj4kZih4X3sxfSkgPSBcbGFtYmRhX3sxfSBcZnJhY3tLfXtLICsgeF97MX19JDwvc3Bhbj48L2xpPg0KICAgIDx1bCBzdHlsZT0ibGlzdC1zdHlsZS10eXBlOiBub25lOyI+DQogICAgICA8bGk+JFxsYW1iZGFfezF9IFxmcmFje0t9e0sgKyB4X3sxfX0gPSBcYmV0YV97MX0gXGxlZnRcbGFuZ2xlIHhfezF9IFxyaWdodFxyYW5nbGUkPC9saT4NCiAgICAgIDxsaT4kXGxhbWJkYV97MX0gSyA9IFxsZWZ0XGxhbmdsZSB4X3sxfSBccmlnaHRccmFuZ2xlIChLICsgXGxlZnRcbGFuZ2xlIHhfezF9IFxyaWdodFxyYW5nbGUpJDwvbGk+DQogICAgICA8bGk+JFxsYW1iZGFfezF9IEsgPSAyMCAoSyArIDIwKSQ8L2xpPg0KICAgICAgPGxpPiRLKFxsYW1iZGFfezF9IC0gMjApID0gNDAwJDwvbGk+DQogICAgICA8bGk+JEsgPSBcZnJhY3s0MDB9e1xsYW1iZGFfezF9IC0gMjB9JDwvbGk+DQogICAgICA8bGk+JFx0aGVyZWZvcmUgXGxhbWJkYV97MX0gXG5lIDIwJDwvbGk+DQogICAgPC91bD4NCjwvb2w+DQoNCkZvciBmdW5jdGlvbiAoYSksIHRoZXJlIGlzIG9ubHkgb25lIHNvbHV0aW9uIG9mICRcbGFtYmRhX3sxfSA9IDIwJC4gRm9yIChiKSwgbXVsdGlwbGUgc29sdXRpb25zIGFyZSBwb3NzaWJsZSwgdGhvdWdoIHdoZW4gJFxsYW1iZGFfezF9ID0gMjAkLCB0aGVuICRLIFxyaWdodGFycm93IFxpbmZ0eSQuIFRoZXJlZm9yZSAkXGxhbWJkYV97MX0gPSAzMCQgd2FzIGFyYml0cmFyaWx5IGNob3NlbiBhbmQgb3RoZXIgcGFyYW1ldGVyIHNldCBhcyAkSyA9IDQwJC4NCg0KYGBge3J9DQojIFRlc3QgZnVuY3Rpb25zIEEgYW5kIEIgZm9yIG1STkEgdHJhbnNjcmlwdGlvbg0KZm9yIChmdW5jIGluIHRlc3RfZnVuY3MpIHsNCiAgIyBTZXQgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24gQSwgd2hlcmUgbVJOQSBpcyB0cmFuc2NyaWJlZCBhdCBhIGNvbnN0YW50IHJhdGUNCiAgaWYgKHRvdXBwZXIoZnVuYykgPT0gIkEiKSB7DQogICAgIyBTZXQgcmF0ZXMgZm9yIHByb2R1Y3Rpb24sIGRlZ3JhZGF0aW9uIGFuZCBjb21wbGV4IGZvcm1hdGlvbg0KICAgIHJhdGVfcGFyYW1ldGVycyA8LSBsaXN0KGxhbWJkYV8xID0gMjAsIGJldGFfMSA9IDEsIGxhbWJkYV8yID0gMSwgYmV0YV8yID0gMC4xKQ0KDQogICAgIyBSYXRlIGZvcm11bGFzIHBlciByZWFjdGlvbg0KICAgICMgVmFsdWVzIGFyZSBjYWxjdWxhdGVkIHVzaW5nIHBhcmFtZXRlcnMgc3BlY2lmaWVkIGluIHRoZSBnaWxsZXNwaWUgZnVuY3Rpb24NCiAgICByZWFjdGlvbl9yYXRlcyA9IGMoInJhdGVfY29uc3RhbnRzJGxhbWJkYV8xIiwgIyBtUk5BICh4MSkgdHJhbnNjcmlwdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAicmF0ZV9jb25zdGFudHMkYmV0YV8xICogWFtbXCJ4MVwiXV0iLCAjIG1STkEgKHgxKSBkZWdyYWRhdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAicmF0ZV9jb25zdGFudHMkbGFtYmRhXzIgKiBYW1tcIngxXCJdXSIsICMgeDIgdHJhbnNsYXRpb24NCiAgICAgICAgICAgICAgICAgICAgICAgInJhdGVfY29uc3RhbnRzJGJldGFfMiAqIFhbW1wieDJcIl1dIiwgIyB4MiBkZWdyYWRhdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAicmF0ZV9jb25zdGFudHMkbGFtYmRhXzIgKiBYW1tcIngxXCJdXSIsICMgeDMgdHJhbnNsYXRpb24NCiAgICAgICAgICAgICAgICAgICAgICAgInJhdGVfY29uc3RhbnRzJGJldGFfMiAqIFhbW1wieDNcIl1dIikgIyB4MyBkZWdyYWRhdGlvbg0KDQogIH0gZWxzZSB7DQogICAgIyBTZXQgcGFyYW1ldGVycyBmb3IgZnVuY3Rpb24gQiwgd2hlcmUgbVJOQSBpcyBzZWxmLXJlcHJlc3NlZA0KICAgIHJhdGVfcGFyYW1ldGVycyA8LSBsaXN0KGxhbWJkYV8xID0gMzAsIEsgPSA0MCwgYmV0YV8xID0gMSwgbGFtYmRhXzIgPSAxLCBiZXRhXzIgPSAwLjEpDQoNCiAgICByZWFjdGlvbl9yYXRlcyA9IGMoInJhdGVfY29uc3RhbnRzJGxhbWJkYV8xICogKHJhdGVfY29uc3RhbnRzJEspIC8gKHJhdGVfY29uc3RhbnRzJEsgKyBYW1tcIngxXCJdXSkiLCAjIG1STkEgKHgxKSB0cmFuc2NyaXB0aW9uDQogICAgICAgICAgICAgICAgICAgICAgICJyYXRlX2NvbnN0YW50cyRiZXRhXzEgKiBYW1tcIngxXCJdXSIsICMgbVJOQSAoeDEpIGRlZ3JhZGF0aW9uDQogICAgICAgICAgICAgICAgICAgICAgICJyYXRlX2NvbnN0YW50cyRsYW1iZGFfMiAqIFhbW1wieDFcIl1dIiwgIyB4MiB0cmFuc2xhdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAicmF0ZV9jb25zdGFudHMkYmV0YV8yICogWFtbXCJ4MlwiXV0iLCAjIHgyIGRlZ3JhZGF0aW9uDQogICAgICAgICAgICAgICAgICAgICAgICJyYXRlX2NvbnN0YW50cyRsYW1iZGFfMiAqIFhbW1wieDFcIl1dIiwgIyB4MyB0cmFuc2xhdGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAicmF0ZV9jb25zdGFudHMkYmV0YV8yICogWFtbXCJ4M1wiXV0iKSAjIHgzIGRlZ3JhZGF0aW9uDQogIH0NCg0KICAjIFJ1biB0aGUgR2lsbGVzcGllIGFsZ29yaXRobQ0KICBzaW11bGF0aW9uX3Jlc3VsdHMgPC0gZ2lsbGVzcGllKFggPSBYX2luaXRpYWwsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmF0ZV9jb25zdGFudHMgPSByYXRlX3BhcmFtZXRlcnMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsdGFzID0gcmVhY3Rpb25fZGVsdGFzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhdGVzID0gcmVhY3Rpb25fcmF0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiA9IE5faXRlcmF0aW9ucywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gc2ltX3ZlcmJvc2UpDQoNCiAgaWYgKHRvdXBwZXIoZnVuYykgPT0gIkEiKSB7DQogICAgc2F2ZVJEUyhzaW11bGF0aW9uX3Jlc3VsdHMsIGZpbGUgPSBmaWxlLnBhdGgoc2ltX2ZvbGRlciwgInNpbV9BLnJkcyIpKQ0KICB9IGVsc2Ugew0KICAgIHNhdmVSRFMoc2ltdWxhdGlvbl9yZXN1bHRzLCBmaWxlID0gZmlsZS5wYXRoKHNpbV9mb2xkZXIsICJzaW1fQi5yZHMiKSkNCiAgfQ0KfQ0KYGBgDQoNCiMjIyBNb2xlY3VsZSBBYnVuZGFuY2UgT3ZlciBUaW1lDQpUaGUgZnVuY3Rpb24gYmVsb3cgcGxvdHMgYSB0aW1lIHRyYWNlIG9mIG1vbGVjdWxlIGFidW5kYW5jZSBvdmVyIHRpbWUuDQpgYGB7cn0NCiMgQ3JlYXRlIHBsb3Qgb2YgbW9sZWN1bGUgYWJ1bmRhbmNlIG92ZXIgdGltZQ0KdGltZVBsb3QgPC0gZnVuY3Rpb24obW9sZWN1bGVfY291bnRzLCBwbG90X2NvbG91cnMgPSBjKCJyZWQiLCAiZGVlcHNreWJsdWUiLCAicHVycGxlIiksDQogICAgICAgICAgICAgICAgICAgICB0aXRsZSA9ICJOdW1iZXIgb2YgTW9sZWN1bGVzIE92ZXIgVGltZSIsDQogICAgICAgICAgICAgICAgICAgICB4YXhpc19sYWJlbCA9ICJUaW1lIChzKSIsIHlheGlzX2xhYmVsID0gIk51bWJlciBvZiBNb2xlY3VsZXMiLA0KICAgICAgICAgICAgICAgICAgICAgeGxpbWl0cyA9IE5BLCB5bGltaXRzID0gTkEpIHsNCiAgIyBDb252ZXJ0IGRhdGEgaW50byBmb3JtIGZvciBwbG90dGluZw0KICBtb2xlY3VsZXNfdGltZV9kZiA8LSBkYXRhLmZyYW1lKG5fbW9sZWN1bGVzID0gdW5saXN0KGMobW9sZWN1bGVfY291bnRzKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9sZWN1bGVfdHlwZSA9IHJvdy5uYW1lcyhtb2xlY3VsZV9jb3VudHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpbWUgPSBhcy5udW1lcmljKHJlcChjb2xuYW1lcyhtb2xlY3VsZV9jb3VudHMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlYWNoID0gbnJvdyhtb2xlY3VsZV9jb3VudHMpKSkpDQogICMgUGxvdCBtb2xlY3VsZXMgb3ZlciB0aW1lDQogIHRpbWVfcGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IG1vbGVjdWxlc190aW1lX2RmLA0KICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IG5fbW9sZWN1bGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IG1vbGVjdWxlX3R5cGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gbW9sZWN1bGVfdHlwZSkpICsNCiAgICBnZW9tX2xpbmUoKSArDQogICAgbGFicyh0aXRsZSA9IHRpdGxlLA0KICAgICAgICAgeCA9IHhheGlzX2xhYmVsLA0KICAgICAgICAgeSA9IHlheGlzX2xhYmVsKSArDQogICAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIk1vbGVjdWxlIFR5cGUiKSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbChsYWJlbHMgPSBjKGV4cHJlc3Npb24oIm1STkEgKCIgfiB4WzFdIH4gIikiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHByZXNzaW9uKCJQcm90ZWluICgiIH4geFsyXSB+ICIpIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwcmVzc2lvbigiUHJvdGVpbiAoIiB+IHhbM10gfiAiKSIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gcGxvdF9jb2xvdXJzKQ0KICANCiAgaWYgKCFpcy5uYSh4bGltaXRzKSkgew0KICAgICMgU2V0IHgtYXhpcyBsaW1pdHMNCiAgICB0aW1lX3Bsb3QgPC0gdGltZV9wbG90ICsgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IHhsaW1pdHMpDQogIH0NCiAgaWYgKCFpcy5uYSh5bGltaXRzKSkgew0KICAgICMgU2V0IHktYXhpcyBsaW1pdHMNCiAgICB0aW1lX3Bsb3QgPC0gdGltZV9wbG90ICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IHlsaW1pdHMpDQogIH0NCiAgDQogIHJldHVybih0aW1lX3Bsb3QpDQp9DQpgYGANCg0KDQpBZnRlciBydW5uaW5nIHRoZSBzaW11bGF0aW9ucywgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIG1vbGVjdWxlcyBvZiBlYWNoIHNwZWNpZXMgaXMgcHJpbnRlZC4gVGhpcyBjb25maXJtcyB0aGF0IGZvciBib3RoIGZ1bmN0aW9ucywgdGhlIGNhbGN1bGF0ZWQgc2V0IG9mIHBhcmFtZXRlcnMgbGVhZHMgdG8gJFxsZWZ0XGxhbmdsZSB4X3sxfSBccmlnaHRccmFuZ2xlIFxzaW1lcSAyMCQgYWZ0ZXIgdGhlIHN5c3RlbSByZWFjaGVzIHN0YXRpb25hcml0eS4NCg0KYGBge3J9DQojIFN0b3JlIHJ1biBzaW11bGF0aW9ucyBhbmQgcGxvdHMNCnNpbXVsYXRpb25zIDwtIGxpc3QoKQ0KdGltZV90cmFjZXMgPC0gbGlzdCgpDQoNCmZvciAoZnVuYyBpbiB0ZXN0X2Z1bmNzKSB7DQogICMgT3BlbiB0aGUgc2F2ZWQgc2ltdWxhdGlvbnMgZm9yIGZ1bmN0aW9ucyAoYSkgYW5kIChiKQ0KICBpZiAoZnVuYyA9PSAiQSIpIHsNCiAgICBzaW11bGF0aW9uc1tbZnVuY11dIDwtIHJlYWRSRFMoZmlsZSA9IGZpbGUucGF0aChzaW1fZm9sZGVyLCAic2ltX0EucmRzIikpDQogIH0gZWxzZSB7DQogICAgc2ltdWxhdGlvbnNbW2Z1bmNdXSA8LSByZWFkUkRTKGZpbGUgPSBmaWxlLnBhdGgoc2ltX2ZvbGRlciwgInNpbV9CLnJkcyIpKQ0KICB9DQogIA0KICAjIFByaW50IGF2ZXJhZ2UgbW9sZWN1bGUgYWJ1bmRhbmNlIGFuZCBlZmZpY2llbmN5DQogIG1lc3NhZ2UocGFzdGUwKCJGdW5jdGlvbiAiLCBmdW5jLCAiOiIsDQogICAgICAgICAgICAgICAgICJcbkF2ZXJhZ2UgPHgxPjogIiwgc2lnbmlmKG1lYW4odW5saXN0KHNpbXVsYXRpb25zW1tmdW5jXV0kbW9sZWN1bGVzX292ZXJfdGltZVsieDEiLF0pKSwgNSksDQogICAgICAgICAgICAgICAgICJcbkF2ZXJhZ2UgPHgyPjogIiwgc2lnbmlmKG1lYW4odW5saXN0KHNpbXVsYXRpb25zW1tmdW5jXV0kbW9sZWN1bGVzX292ZXJfdGltZVsieDIiLF0pKSwgNSksDQogICAgICAgICAgICAgICAgICJcbkF2ZXJhZ2UgPHgzPjogIiwgc2lnbmlmKG1lYW4odW5saXN0KHNpbXVsYXRpb25zW1tmdW5jXV0kbW9sZWN1bGVzX292ZXJfdGltZVsieDMiLF0pKSwgNSksDQogICAgICAgICAgICAgICAgICJcbiIpKQ0KICANCiAgIyBDcmVhdGUgcGxvdCBvZiBtb2xlY3VsZXMgb3ZlciB0aW1lDQogIHRpbWVfdHJhY2VzW1tmdW5jXV0gPC0gdGltZVBsb3Qoc2ltdWxhdGlvbnNbW2Z1bmNdXSRtb2xlY3VsZXNfb3Zlcl90aW1lLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aXRsZSA9IHBhc3RlKCJGdW5jdGlvbiIsIGZ1bmMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsaW1pdHMgPSBjKDAsIDIxMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHlsaW1pdHMgPSBjKDAsIDMwMCkpDQp9DQpgYGANCiMjIyMgUmVzdWx0cw0KSW4gdGhlIHBsb3Qgb2YgbW9sZWN1bGVzIG92ZXIgdGltZSwgdGhlIG51bWJlciBvZiBtUk5BIG1vbGVjdWxlcyBxdWlja2x5IGRyb3BzIHRvIGFyb3VuZCAyMCBiZWZvcmUgcmVtYWluaW5nIGZhaXJseSBzdGFibGUgd2l0aCBsaXR0bGUgZmx1Y3R1YXRpb24gZm9yIGJvdGggZnVuY3Rpb25zLiBGdW5jdGlvbiAoYikgY29udmVyZ2VzIHRvIDIwIHNsaWdodGx5IGZhc3RlciBkdWUgdG8gbmVnYXRpdmUgZmVlZGJhY2sgaW4gbVJOQSBwcm9kdWN0aW9uIHJhdGUgd2hpY2ggYWRqdXN0cyB0aGUgc3lzdGVtIGJhY2sgdG8gYSBzdGVhZHkgc3RhdGUuIEZvciBib3RoLCByZXBvcnRlciBwcm90ZWluIGNvbmNlbnRyYXRpb24gZ3Jvd3MgcmFwaWRseSBiZWZvcmUgcmVhY2hpbmcgYSBzdGFibGUgc3RhdGUgYXJvdW5kIDIwMCBpbiB3aGljaCB0aGUgbW9sZWN1bGVzIGZsdWN0dWF0ZSBiZXR3ZWVuIGFwcHJveGltYXRlbHkgMTUwIC0gMjUwLiBUaGlzIG1vZGVsIHRoZXJlZm9yZSBjYXB0dXJlcyBhIGNvbW1vbiBzaXR1YXRpb24gaW4gYSBjZWxsIHdoZXJlIG1STkEgaGFzIGxvdyBhYnVuZGFuY2UgcmVsYXRpdmUgdG8gcHJvdGVpbi4NCg0KVGhlIGFwcHJveGltYXRlbHkgMTAgZm9sZCBpbmNyZWFzZSBiZXR3ZWVuIG1STkEgYW5kIHByb3RlaW4gYWJ1bmRhbmNlIGNhbiBiZSBleHBsYWluZWQgYXMgJFxiZXRhX3sxfSA9IDEwIFxiZXRhX3syfSQuIFRoaXMgbWVhbnMgbVJOQSBtb2xlY3VsZXMgYXJlIGRlZ3JhZGVkIGF0IGEgbXVjaCBmYXN0ZXIgcmF0ZSB0aGFuIHByb3RlaW5zLCByZWZsZWN0aW5nIHRoZWlyIGluc3RhYmlsaXR5IHdpdGhpbiBhIGNlbGwuIEJ5IGNvbnRyYXN0LCBwcm90ZWlucyBhcmUgbW9kZWxlZCB0byBiZSBtdWNoIG1vcmUgc3RhYmxlIGFzIGluIHJlYWxpdHkgdGhleSBhcmUgaGVsZCB0b2dldGhlciBieSBzdHJvbmcgY292YWxlbnQgcGVwdGlkZSBib25kcy4NCg0KSW4gZ2VuZXJhbCwgYm90aCByZXBvcnRlcnMgYXJlIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIG9uZSBhbm90aGVyIGFzIGZsdWN0dWF0aW9ucyByaXNlIGFuZCBmYWxsIHdpdGhpbiBzaW1pbGFyIHBlcmlvZHMgb2YgdGltZS4gVGhpcyBlZmZlY3QgbGlrZWx5IHByb3BhZ2F0ZXMgZnJvbSBzbWFsbCBwZXJ0dXJiYXRpb25zIGluIG1STkEgYWJ1bmRhbmNlLiBIb3dldmVyLCBpdCBpcyBhdmVyYWdlZCBhY3Jvc3MgdGltZSBjYXVzaW5nIGEgZGVsYXkgZnJvbSB0aGUgdGltZSBvZiB0cmFuc2NyaXB0aW9uIGFuZCBkZWdyYWRhdGlvbiBvZiBtUk5BLiBPY2Nhc2lvbmFsbHkgcHJvdGVpbiBmbHVjdHVhdGlvbnMgYmVjb21lIG91dCBvZiBzeW5jIHJlZmxlY3RpbmcgdGhlIHN0b2NoYXN0aWNpdHkgb2YgcHJvdGVpbiBzeW50aGVzaXMgYW5kIHByb3Rlb2x5c2lzLg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGZvbGRlciB0byBzYXZlIHBsb3RzDQpwbG90X2ZvbGRlciA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwgIlBsb3RzIikNCmRpci5jcmVhdGUocGxvdF9mb2xkZXIpDQoNCiMgQ29tYmluZSB0cmFjZXMgYXMgc3VicGxvdHMNCnRpbWVfc3VicGxvdHMgPC0gZ2dhcnJhbmdlKHRpbWVfdHJhY2VzW1siQSJdXSwgdGltZV90cmFjZXNbWyJCIl1dLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbnJvdyA9IDIsIHdpZHRocyA9IGMoMSwgMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBjb21tb24ubGVnZW5kID0gVFJVRSwgbGVnZW5kID0gInJpZ2h0IikNCnRpbWVfc3VicGxvdHMgPC0gYW5ub3RhdGVfZmlndXJlKHRpbWVfc3VicGxvdHMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3AgPSB0ZXh0X2dyb2IoIk1vbGVjdWxlcyBPdmVyIFRpbWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxNCkpDQoNCiMgU2F2ZSB0aGUgcGxvdA0KcGRmKGZpbGUucGF0aChwbG90X2ZvbGRlciwgIk1vbGVjdWxlc19PdmVyX1RpbWUucGRmIiksIHdpZHRoID0gMTgsIGhlaWdodCA9IDgpDQpwcmludCh0aW1lX3N1YnBsb3RzKQ0KaW52aXNpYmxlKGRldi5vZmYoKSkNCg0KcHJpbnQodGltZV9zdWJwbG90cykNCmBgYA0KDQojIyMjIEFsZ29yaXRobSBTdGF0aXN0aWNzDQpUaGUgbm9ybWFsaXNlZCB2YXJpYW5jZSBvZiBhdmVyYWdlIG1STkEgYWJ1bmRhbmNlLCAkXGV0YV97MTF9JCwgYW5kIG5vcm1hbGl6ZWQgY292YXJpYW5jZXMgJFxldGFfezEyfSQsICRcZXRhX3syM30kIGZvciB0aGUgZnVuY3Rpb25zIGFyZSBhcyBmb2xsb3dzOg0KDQo8b2wgdHlwZT0iYSI+DQogIDxsaT48c3BhbiBzdHlsZT0iY29sb3I6I2ZjOTgwMyI+JGYoeF97MX0pID0gXGxhbWJkYV97MX0kPC9zcGFuPjwvbGk+DQogICAgPHVsIHN0eWxlPSJsaXN0LXN0eWxlLXR5cGU6IG5vbmU7Ij4NCiAgICAgIDxsaT4kXGV0YV97MTF9ID0gXGZyYWN7MX17XGxlZnRcbGFuZ2xlIHhfezF9IFxyaWdodFxyYW5nbGV9JDwvbGk+DQogICAgICA8bGk+JFxldGFfezEyfSA9IFxmcmFjezF9e1xsZWZ0XGxhbmdsZSB4X3sxfSBccmlnaHRccmFuZ2xlfSBcZnJhY3tcYmV0YV97Mn19e1xiZXRhX3sxfSArIFxiZXRhX3syfX0kPC9saT4NCiAgICAgIDxsaT4kXGV0YV97MjN9ID0gXGZyYWN7MX17XGxlZnRcbGFuZ2xlIHhfezF9IFxyaWdodFxyYW5nbGV9IFxmcmFje1xiZXRhX3syfX17XGJldGFfezF9ICsgXGJldGFfezJ9fSQ8L2xpPg0KICAgIDwvdWw+DQogIDxicj4NCiAgPGxpPjxzcGFuIHN0eWxlPSJjb2xvcjojMjQ4YWZmIj4kZih4X3sxfSkgPSBcbGFtYmRhX3sxfSBcZnJhY3tLfXtLICsgeF97MX19JDwvc3Bhbj48L2xpPg0KICAgIDx1bCBzdHlsZT0ibGlzdC1zdHlsZS10eXBlOiBub25lOyI+DQogICAgICA8bGk+JFxldGFfezExfSA9ICQ8L2xpPg0KICAgICAgPGxpPiRcZXRhX3sxMX0gPSAkPC9saT4NCiAgICAgIDxsaT4kXGV0YV97MTF9ID0gJDwvbGk+DQogICAgPC91bD4NCjwvb2w+DQoNCmBgYHtyfQ0KIyBDYWxjdWxhdGVzIHJ1biB0aW1lIHN0YXRpc3RpY3MgYXMgbm9ybWFsaXplZCB2YXJpYW5jZSBhbmQgY292YXJpYW5jZQ0KY2FsY3VsYXRlU3RhdHMgPC0gZnVuY3Rpb24oWF9vdmVyX3RpbWUsIHJhdGVfY29uc3RhbnRzLCBqdW1wX3RpbWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZnVuY3Rpb25fdHlwZSA9ICJBIikgew0KICANCiAgIyBFeHRyYWN0IHRoZSByYXRlIGNvbnN0YW50IHBhcmFtZXRlcnMNCiAgbGFtYmRhXzEgPC0gcmF0ZV9jb25zdGFudHMkbGFtYmRhXzENCiAgbGFtYmRhXzIgPC0gcmF0ZV9jb25zdGFudHMkbGFtYmRhXzINCiAgYmV0YV8xIDwtIHJhdGVfY29uc3RhbnRzJGJldGFfMQ0KICBiZXRhXzIgPC0gcmF0ZV9jb25zdGFudHMkYmV0YV8yDQogIEsgPC0gcmF0ZV9jb25zdGFudHMkSw0KICANCiAgIyBDYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgbnVtYmVyIG9mIHgxLCB4MiBhbmQgeDMgbW9sZWN1bGVzDQogIHhfYXZlcmFnZXMgPC0gbGlzdCgpDQogIA0KICBmb3IgKHggaW4gcm93bmFtZXMoWF9vdmVyX3RpbWUpKSB7DQogICAgeF9hdmVyYWdlc1tbeF1dIDwtIHdlaWdodGVkLm1lYW4oeCA9IHVubGlzdChYX292ZXJfdGltZVt4LF0pLCB3ID0ganVtcF90aW1lcykNCiAgfQ0KICANCiAgIyBDYWxjdWxhdGUgYW5hbHl0aWMgZXRhcw0KICBpZiAodG91cHBlcihmdW5jdGlvbl90eXBlKSA9PSAiQSIpIHsNCiAgICAjIENhbGN1bGF0ZSBub3JtYWxpc2VkIHZhcmlhbmNlIGZvciBmdW5jdGlvbiAoYSkNCiAgICBldGFfMTEgPC0gMSAvIHhfYXZlcmFnZXNbWyJ4MSJdXQ0KICAgIA0KICAgICMgTm9ybWFsaXNlZCBjby12YXJpYW5jZQ0KICAgIGV0YV8xMiA8LSBldGFfMTEgKiAoYmV0YV8yIC8gKGJldGFfMSArIGJldGFfMikpDQogICAgZXRhXzIzIDwtIGV0YV8xMg0KICAgIA0KICB9IGVsc2Ugew0KICAgICMgQ2FsY3VsYXRlIHBsYWNlaG9sZGVyIGFscGhhIGZvciBmdW5jdGlvbiAoYikNCiAgICBhbHBoYSA8LSB4X2F2ZXJhZ2VzW1sieDEiXV0gLyAoSyArIHhfYXZlcmFnZXNbWyJ4MSJdXSkNCiAgICANCiAgICBldGFfMTEgPC0gMSAvICh4X2F2ZXJhZ2VzW1sieDEiXV0gKyBhbHBoYSkNCiAgICBldGFfMTIgPC0gZXRhXzExICogKGJldGFfMiAvIChiZXRhXzEgKyAoYmV0YV8xICogYWxwaGEpICsgYmV0YV8yKSkNCiAgICBldGFfMjMgPC0gZXRhXzEyDQogIH0NCiAgDQogICMgQ2FsY3VsYXRlIHdlaWdodGVkIGNvdmFyaWFuY2UgYmV0d2VlbiB4MSwgeDIgYW5kIHgzIG1vbGVjdWxlcw0KICBhYnVuZGFuY2VfZGYgPC0gZGF0YS5mcmFtZSh0KFhfb3Zlcl90aW1lKSkNCiAgYWJ1bmRhbmNlX2RmIDwtIGRhdGEuZnJhbWUobGFwcGx5KGFidW5kYW5jZV9kZiwgYXMubnVtZXJpYykpDQogIG1vbGVjdWxlX2NvdiA8LSBjb3Yud3QoYWJ1bmRhbmNlX2RmLCBhcy52ZWN0b3IoanVtcF90aW1lcykpJGNvdg0KICANCiAgIyBDYWxjdWxhdGUgbnVtZXJpYyBldGFzDQogIG51bV9ldGExMSA8LSBtb2xlY3VsZV9jb3ZbIngxIiwgIngxIl0gLyAoeF9hdmVyYWdlc1tbIngxIl1dICogeF9hdmVyYWdlc1tbIngxIl1dKQ0KICBudW1fZXRhMTIgPC0gbW9sZWN1bGVfY292WyJ4MSIsICJ4MiJdIC8gKHhfYXZlcmFnZXNbWyJ4MSJdXSAqIHhfYXZlcmFnZXNbWyJ4MiJdXSkNCiAgbnVtX2V0YTIzIDwtIG1vbGVjdWxlX2NvdlsieDIiLCAieDMiXSAvICh4X2F2ZXJhZ2VzW1sieDIiXV0gKiB4X2F2ZXJhZ2VzW1sieDMiXV0pDQogIA0KICAjIFJlY29yZCByZXN1bHRzDQogIHJlc3VsdHMgPC0gbGlzdChhbmFseXRpY2FsID0gbGlzdChldGFfMTEgPSBldGFfMTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldGFfMTIgPSBldGFfMTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBldGFfMjMgPSBldGFfMjMpLA0KICAgICAgICAgICAgICAgICAgbnVtZXJpY2FsID0gbGlzdChldGFfMTEgPSBudW1fZXRhMTEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV0YV8xMiA9IG51bV9ldGExMiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXRhXzIzID0gbnVtX2V0YTIzKSkNCiAgDQogIHJldHVybihyZXN1bHRzKQ0KfQ0KYGBgDQoNCkEgcGFyYW1ldGVyIHNlYXJjaCB3YXMgcGVyZm9ybWVkIGZvciBmdW5jdGlvbnMgKGEpIGFuZCAoYikgdGhyb3VnaCB0ZXN0aW5nIDYwICRcbGFtYmRhX3sxfSQgdmFsdWVzIHdpdGhpbiB0aGUgcmFuZ2UgWzAuMSwgMTAwXSBmb3IgKGEpIGFuZCA2NCBjb21iaW5hdGlvbnMgb2YgJFxsYW1iZGFfezF9JCBhbmQgJEskIHZhbHVlcyB3aXRoaW4gdGhlIHJhbmdlIFswLjEsIDEwMF0gZm9yIChiKS4gRm9yIGVhY2ggcGFyYW1ldGVyIHNldCwgdGhlIEdpbGxlc3BpZSBhbGdvcml0aG0gd2FzIHJ1biBmb3IgMTAwLDAwMCBpdGVyYXRpb25zLiBUaGUgbm9ybWFsaXplZCB2YXJpYW5jZSBvZiB0aGUgbVJOQSAoJFxldGFfezExfSQpLCBub3JtYWxpemVkIGNvdmFyaWFuY2UgYmV0d2VlbiB0aGUgbVJOQSBhbmQgYSByZXBvcnRlciBwcm90ZWluICgkXGV0YV97MTJ9JCkgYW5kIHRoZSBub3JtYWxpemVkIGNvdmFyaWFuY2UgYmV0d2VlbiB0aGUgdHdvIHJlcG9ydGVyIHByb3RlaW5zICgkXGV0YV97MjN9JCkgd2VyZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBhbmFseXRpY2FsICRcZXRhJCB2YWx1ZXMgZm9yIGZ1bmN0aW9uIChhKSBhbmQgYWJvdmUgZm9yIGZ1bmN0aW9uIChiKS4gXFwNCg0KQWRkaXRpb25hbGx5LCBhIHNlY29uZCBtZXRob2QgZm9yIGNhbGN1bGF0aW5nICRcZXRhX3sxMX0kLCAkXGV0YV97MTJ9JCBhbmQgJFxldGFfezIzfSQsIHJlZmVycmVkIHRvIGFzIHRoZSBudW1lcmljYWwgbWV0aG9kLCBpcyBpbmNsdWRlZCBmb3IgY29tcGFyaXNvbi4gQ292YXJpYW5jZSBiZXR3ZWVuIGFidW5kYW5jZXMgb2YgdHdvIG1vbGVjdWxhciBzcGVjaWVzIHdlaWdodGVkIGJ5IGp1bXAgdGltZXMgYmV0d2VlbiByZWFjdGlvbnMgd2FzIGNhbGN1bGF0ZWQgYW5kIG5vcm1hbGlzZWQgYnkgdGhlIGF2ZXJhZ2UgbW9sZWN1bGUgYWJ1bmRhbmNlcyBiYXNlZCBvbiB0aGUgcHJpbmNpcGFsIHRoYXQuLi4NCg0KYGBge3J9DQojIFRlc3QgZGlmZmVyZW50IHBhcmFtZXRlcnMNCnBhcmFtZXRlclNlYXJjaCA8LSBmdW5jdGlvbih0ZXN0X2xhbWJkYXNfMSA9IGMoMSksIHRlc3RfbGFtYmRhc18yID0gYygxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0X2JldGFzXzEgPSBjKDEpLCB0ZXN0X2JldGFzXzIgPSBjKDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlc3RfS3MgPSBjKE5BKSwgWCwgZGVsdGFzLCByYXRlcywgTiA9IDUwMDAwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZsdXhfdGhyZXNob2xkID0gMC4xLCBzdG9wX2F0X3N0YXRpb25hcnkgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5fTiA9IDEwMDAwLCBmdW5jdGlvbl90eXBlID0gIkEiLCB2ZXJib3NlID0gMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2aW91c19zZWFyY2hfc3RhdHMgPSBkYXRhLmZyYW1lKCkpIHsNCiAgDQogICMgUmVjb3JkIHBhcmFtZXRlcnMgYW5kIHRoZWlyIHJlc3VsdHMNCiAgcGFyYW1ldGVyX2NvbWJpbmF0aW9uIDwtIGRhdGEuZnJhbWUoKQ0KICBzaW1fcmVzdWx0cyA8LSBsaXN0KCkNCiAgc3RhdHNfcmVzdWx0cyA8LSBsaXN0KCkNCiAgDQogICMgQXNzaWduIGFuIElEIGZvciB0aGUgY29tYmluYXRpb24NCiAgaWQgPC0gMA0KICANCiAgIyBUZXN0IGVhY2ggcGFyYW1ldGVyIGNvbWJpbmF0aW9uDQogIGZvciAobDEgaW4gdGVzdF9sYW1iZGFzXzEpIHsNCiAgICBmb3IgKGwyIGluIHRlc3RfbGFtYmRhc18yKSB7DQogICAgICBmb3IgKGIxIGluIHRlc3RfYmV0YXNfMSkgew0KICAgICAgICBmb3IgKGIyIGluIHRlc3RfYmV0YXNfMikgew0KICAgICAgICAgIGZvciAoayBpbiB0ZXN0X0tzKSB7DQogICAgICAgICAgICANCiAgICAgICAgICAgIG5ld19wYXJhbXMgPC0gbGlzdChsYW1iZGFfMSA9IGwxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhbWJkYV8yID0gbDIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmV0YV8xID0gYjEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmV0YV8yID0gYjIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSyA9IGspDQogICAgICAgICAgICANCiAgICAgICAgICAgICMgQ2hlY2sgd2hldGhlciBjb21iaW5hdGlvbiB3YXMgdGVzdGVkIHByZXZpb3VzbHkgaW4gYSBkaWZmZXJlbnQgcnVuDQogICAgICAgICAgICBjb250aW51ZV9zZWFyY2ggPC0gVFJVRQ0KICAgICAgICAgICAgDQogICAgICAgICAgICBpZiAobnJvdyhwcmV2aW91c19zZWFyY2hfc3RhdHMpID4gMCkgew0KICAgICAgICAgICAgICBpZiAobnJvdyhtZXJnZShuZXdfcGFyYW1zLCBwcmV2aW91c19zZWFyY2hfc3RhdHMpKSA+IDApIHsNCiAgICAgICAgICAgICAgICBjb250aW51ZV9zZWFyY2ggPC0gRkFMU0UNCiAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgICAgDQogICAgICAgICAgICBpZiAoY29udGludWVfc2VhcmNoKSB7DQogICAgICAgICAgICAgICMgU2V0IHVuaXF1ZSBJRA0KICAgICAgICAgICAgICBpZCA8LSBpZCArIDENCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIGlmICh2ZXJib3NlID4gMCkgew0KICAgICAgICAgICAgICAgIG1lc3NhZ2UocGFzdGUoIlJlYWNoZWQgc3RlcCIsIGlkKSkNCiAgICAgICAgICAgICAgICBtZXNzYWdlKHBhc3RlKCJUZXN0aW5nIiwgdG9TdHJpbmcobmV3X3BhcmFtcykpKQ0KICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAjIFJlY29yZCB0aGUgbGFtYmRhcywgYmV0YXMgYW5kIEsNCiAgICAgICAgICAgICAgcGFyYW1ldGVyX2NvbWJpbmF0aW9uIDwtIHJiaW5kKHBhcmFtZXRlcl9jb21iaW5hdGlvbiwgbmV3X3BhcmFtcykNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICMgUnVuIHRoZSBHaWxsZXNwaWUgYWxnb3JpdGhtDQogICAgICAgICAgICAgIHRlc3Rfc2ltdWxhdGlvbiA8LSBnaWxsZXNwaWUoWCA9IFhfaW5pdGlhbCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYXRlX2NvbnN0YW50cyA9IG5ld19wYXJhbXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsdGFzID0gZGVsdGFzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhdGVzID0gcmF0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiA9IE4sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmx1eF90aHJlc2hvbGQgPSBmbHV4X3RocmVzaG9sZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9wX2F0X3N0YXRpb25hcnkgPSBzdG9wX2F0X3N0YXRpb25hcnksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluX04gPSBtaW5fTikNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICMgQ2FsY3VsYXRlIG5vcm1hbGlzZWQgdmFyaWFuY2UgYW5kIGNvdmFyaWFuY2VzDQogICAgICAgICAgICAgIHN0YXRzIDwtIGNhbGN1bGF0ZVN0YXRzKFhfb3Zlcl90aW1lID0gdGVzdF9zaW11bGF0aW9uJG1vbGVjdWxlc19vdmVyX3RpbWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJhdGVfY29uc3RhbnRzID0gbmV3X3BhcmFtcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAganVtcF90aW1lcyA9IHRlc3Rfc2ltdWxhdGlvbiRqdW1wX3RpbWVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbl90eXBlID0gZnVuY3Rpb25fdHlwZSkNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICMgUmVjb3JkIHRoZSByZXN1bHRzIGZvciB0aGUgcGFyYW1ldGVyIGNvbWJpbmF0aW9uDQogICAgICAgICAgICAgIHNpbV9yZXN1bHRzW1tpZF1dIDwtIHRlc3Rfc2ltdWxhdGlvbg0KICAgICAgICAgICAgICBzdGF0c19yZXN1bHRzW1tpZF1dIDwtIHN0YXRzDQogICAgICAgICAgICB9DQogICAgICAgICAgfQ0KICAgICAgICB9DQogICAgICB9DQogICAgfQ0KICB9DQogIA0KICAjIENvbWJpbmUgcGFyYW1ldGVycyB3aXRoIHN0YXRzDQogIHBhcmFtZXRlcl9zdGF0cyA8LSBjYmluZChwYXJhbWV0ZXJfY29tYmluYXRpb24sIGRvLmNhbGwocmJpbmQsIHN0YXRzX3Jlc3VsdHMpKQ0KICBwYXJhbWV0ZXJfc3RhdHMgPC0gZGF0YS5mcmFtZShsYXBwbHkocGFyYW1ldGVyX3N0YXRzLCBhcy5udW1lcmljKSkNCiAgDQogIHJldHVybihsaXN0KHNpbV9yZXN1bHRzID0gc2ltX3Jlc3VsdHMsDQogICAgICAgICAgICAgIHBhcmFtZXRlcl9zdGF0cyA9IHBhcmFtZXRlcl9zdGF0cykpDQp9DQpgYGANCg0K