Objective

This notebook explores the data from the Hawaii Restoration Project. The goal is to understand the structure of the data, identify any missing values, and summarize and visualize key variables.

Experimental Design

The Hawaii Restoration Project consists of two main experiments: the FOG experiment and the NURSE experiment.

The Fog Experiment

The fog experiment examined the effect of fog capture on the restoration success of native Hawaiian plants. The experiment was set up at two different sites.

The Nurse Plant Experiment

The nurse experiment examined the effect of nurse plants on the restoration success of native Hawaiian plants. The experiment was set up at two different sites.

Load Data

First step is to load the data. The data will be in the data directory. The data is an excel workbook with three sheets: Metadata, FOG, and NURSE.

library(readxl)
# Load the FOG data
fog_data <- readxl::read_excel(file.path(data_dir, "KBC_data_SY.xlsx"), sheet = "FOG")
# Load the NURSE data
nurse_data <- readxl::read_excel(file.path(data_dir, "KBC_data_SY.xlsx"), sheet = "NURSE")

Explore the Raw Data

Let’s take a look at the data straight out of the excel file.

Data table structure

# Check the structure of the FOG data

DataExplorer::plot_str(list(FOG=fog_data,
                            NURSE=nurse_data))
NA

Column Name and Type Adjustments

We want to drop the columns 19-24 in both data frames since they are empty. We should drop the volume columns so that we can show our work for reproducibility. We will still use the volume measure in the original data set (a rectangular prism) with in cubic decimeters (Height X Width X Depth X 1/1,000). Unless, we want to present other volumes. However, this is just scaling the volume (HxWxD and has little effect since we will scale all the variables if used as predictors and everything else will just change the beta coefficients). We can also drop the notes columns since they are not needed for the analysis. We should make all column names lower case. We should rename mauka/makai to position so that we don’t have a slash in our column names. I also want to rename plant1 and species1 to just plant and species. We should convert site, replicate, treatment, species, plant and position to factors. Survival should converted to 0 and 1.


# Adjust column names and types for FOG data
fog_data <- fog_data %>%
  select(-c(19:24)) %>%
  select(-c(Volume_T1, Volume_T2)) %>%
  rename_with(tolower) %>%
  rename(position = `mauka/makai`,plant = plant1, species = species1,
         note1 = 'notes 1', note2 = 'notes 2') %>%
  mutate(across(c(site, replicate, treatment, species, plant, position), as.factor),
         survival_t1 = ifelse(survival_t1 == "Y", 1, 0),
         survival_t2 = ifelse(survival_t2 == "Y", 1, 0))


# Adjust column names and types for NURSE data
nurse_data <- nurse_data %>%
  select(-c(18:24)) %>%
  select(-c(Volume_T1, Volume_T2)) %>%
  rename_with(tolower) %>% 
  rename(plant = plant1, note1= 'notes...10',note2= 'notes...15') %>%
  mutate(across(c(site, replicate, treatment, species, plant), as.factor),
         survival_t1 = ifelse(survival_t1 == "y", 1, 0),
         survival_t2 = ifelse(survival_t2 == "y", 1, 0))

# Check the structure again after adjustments
DataExplorer::plot_str(list(fog_data, nurse_data))

FOG Data Exploration

A quick description of the fog data


# Get a quick description of the data
DataExplorer::introduce(fog_data %>% select(-c(note1, note2))) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", values_to = "value") %>% 
  kbl() %>% 
  kable_minimal(full_width = F)
variable value
rows 960
columns 14
discrete_columns 6
continuous_columns 8
all_missing_columns 0
total_missing_values 0
complete_rows 960
total_observations 13440
memory_usage 92264
NA

# Visualize the data description for FOG data
DataExplorer::plot_intro(fog_data %>% select(-c(note1, note2)), title = "FOG Data Overview")

Looking at missing values for FOG data

# Visualize missing data for FOG data
DataExplorer::plot_missing(fog_data %>% select(-c(note1, note2)), title = "Missing Data in FOG Data")

We know there’s an issue here the widths and height variables are coded as 0 when they were not present. We will need to address this in data cleaning. For now, this is super groovy!

Quick look at the distribution of the variables in the FOG data

Discrete variables


# Visualize the distribution of discrete variables in FOG data
DataExplorer::plot_bar(fog_data %>% select(-c(note1, note2)), ncol = 3, title = "Distribution of Discrete Variables in FOG Data")

Need to fix mauka (1/18/2022) and to Mauka. THIS NEEDS TO BE FIXED IN THE DATA CLEANING STEP!

# Fix the mauka/makai values in FOG data
fog_data <- fog_data %>%
  mutate(position = recode(position, "mauka (1/18/2022)" = "mauka"))

DataExplorer::plot_bar(fog_data %>% select(-c(note1, note2)), ncol = 3, title = "Distribution of Discrete Variables in FOG Data")

Continuous variables

# Visualize the distribution of continuous variables in FOG data

# Visualize the distribution of continuous variables in FOG data

DataExplorer::plot_histogram(fog_data %>% select(-c(note1, note2)),
                             ncol = 3, title = "Distribution of Continuous Variables in FOG Data")

For the data cleaning we need to address the 0 values in the width and height variables. While looking at this we have options:

  1. We can replace the 0 values with NA.
  2. We can replace widths and heights were the survival is 0 with NA.

I think for now we will go with option 1. We can always revisit this later if we need to.

NURSE Data Exploration

A quick description of the nurse data


# Get a quick description of the data
DataExplorer::introduce(nurse_data %>% select(-c(note1, note2))) %>% 
  pivot_longer(cols = everything(),
               names_to = "variable", values_to = "value") %>% 
  kbl() %>% 
  kable_minimal(full_width = F)
variable value
rows 452
columns 13
discrete_columns 5
continuous_columns 8
all_missing_columns 0
total_missing_values 6
complete_rows 446
total_observations 5876
memory_usage 44920
NA

# Visualize the data description for FOG data
DataExplorer::plot_intro(nurse_data %>% select(-c(note1, note2)), title = "NURSE Data Overview")

Looking at missing values for NURSE data

# Visualize missing data for FOG data
DataExplorer::plot_missing(nurse_data %>% select(-c(note1, note2)), title = "Missing Data in FOG Data")

We know there’s an issue here the widths and height variables are coded as 0 when they were not present. We need to determine what is missing in the plant column.


# Print a table of the missing plant number in the NURSE data
nurse_data %>%
  filter(is.na(plant)) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant ht_t1 w1_t1 w2_t1 survival_t1 note1 ht_t2 w1_t2 w2_t2 survival_t2 note2
upper 9 nurse upper aalii NA 0 0 0 0 nf 0 0 0 0 nf
upper 9 nurse upper aweoweo NA 0 0 0 0 nf 0 0 0 0 nf
upper 9 nurse upper mamane NA 0 0 0 0 nf 0 0 0 0 nf
upper 9 nurse upper pawale NA 0 0 0 0 nf 0 0 0 0 nf
upper 9 nurse upper pawale NA 0 0 0 0 nf 0 0 0 0 nf
upper 9 nurse upper naenae NA 0 0 0 0 nf 0 0 0 0 nf

Okay these are all plants from the upper site, replicate 9 and have no measurements. Notes indicate not found.

Quick look at the distribution of the variables in the FOG data

Discrete variables


# Visualize the distribution of discrete variables in FOG data
DataExplorer::plot_bar(nurse_data %>% select(-c(note1, note2)), ncol = 3, title = "Distribution of Discrete Variables in NURSE Data")

We probably want to change nurse upper to align with the naming protocol of the lower site. I also need to get rid of the space in the treatment variable value. Nurse k = koa; nurse m = mamane; control = control and nurse = upper = UNSURE!?!.

Continuous variables


# Visualize the distribution of continuous variables in NURSE data

DataExplorer::plot_histogram(nurse_data %>% select(-c(note1, note2)),
                             ncol = 3,
                             title = "Distribution of Continuous Variables in NURSE Data")

For the data cleaning we need to address the 0 values in the width and height variables. While looking at this we have options:

  1. We can replace the 0 values with NA.
  2. We can replace widths and heights were the survival is 0 with NA.

I think for now we will go with option 1. We can always revisit this later if we need to.

Data issues

FOG Data Issues

Duplicates

The easiest issue to identify the problem is duplicates. Let’s check for duplicates in the FOG data.

# Check for duplicates in FOG data
duplicates_fog <- fog_data %>%
  janitor::get_dupes(site, replicate, treatment, species, plant, position)

writexl::write_xlsx(duplicates_fog, file.path(notes_dir, "FOG_dupes.xlsx"))

duplicates_fog %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant position dupe_count ht_t1 w1_t1 w2_t1 survival_t1 note1 ht_t2 w1_t2 w2_t2 survival_t2 note2
lower 1 0 pawale 1 mauka 2 40.0 66.0 56.0 1 NA 50.0 34.5 39.0 1 NA
lower 1 0 pawale 1 mauka 2 50.5 86.0 63.0 1 NA 0.0 0.0 0.0 0 dead
lower 1 40 aalii 1 makai 2 45.5 13.0 17.0 1 NA 0.0 0.0 0.0 0 NA
lower 1 40 aalii 1 makai 2 0.0 0.0 0.0 0 dead 0.0 0.0 0.0 0 NA
lower 1 40 aalii 1 mauka 2 40.0 10.0 18.5 1 NA 41.0 12.0 16.0 1 NA
lower 1 40 aalii 1 mauka 2 29.0 28.5 28.0 1 3 different stems 33.0 21.0 37.0 1 fruiting
lower 1 40 aalii 2 makai 2 39.0 11.0 15.0 1 NA 0.0 0.0 0.0 0 NA
lower 1 40 aalii 2 makai 2 0.0 0.0 0.0 0 dead 0.0 0.0 0.0 0 NA
lower 1 40 aalii 2 mauka 2 47.0 21.0 10.0 1 2 stems 48.0 18.0 25.0 1 NA
lower 1 40 aalii 2 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 1 40 aweoweo 1 makai 2 54.0 13.5 14.5 1 NA 59.0 11.0 8.5 1 NA
lower 1 40 aweoweo 1 makai 2 0.0 0.0 0.0 0 broken stem just died 0.0 0.0 0.0 0 NA
lower 1 40 aweoweo 1 mauka 2 13.0 3.0 2.0 1 dead 43.0 34.0 33.0 1 dying/ not found measure 1
lower 1 40 aweoweo 1 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 nf
lower 1 40 aweoweo 2 makai 2 58.0 40.0 77.5 1 NA 110.0 67.0 47.0 1 NA
lower 1 40 aweoweo 2 makai 2 94.0 55.0 53.0 1 NA 42.0 83.0 65.0 1 NA
lower 1 40 aweoweo 2 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 nf
lower 1 40 aweoweo 2 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 1 40 mamane 1 makai 2 7.0 2.5 2.5 1 looks dead coming back 10.0 7.0 9.0 1 NA
lower 1 40 mamane 1 makai 2 30.0 21.0 8.5 1 NA 39.5 14.0 13.5 1 NA
lower 1 40 mamane 1 mauka 2 24.0 9.0 6.5 1 NA 0.0 0.0 0.0 0 nf
lower 1 40 mamane 1 mauka 2 41.0 11.0 13.0 1 NA 0.0 0.0 0.0 0 NA
lower 1 40 mamane 2 makai 2 27.0 7.0 6.0 1 NA 30.5 16.0 13.5 1 NA
lower 1 40 mamane 2 makai 2 32.0 13.0 7.0 1 NA 36.5 16.0 20.0 1 NA
lower 1 40 mamane 2 mauka 2 40.0 10.0 11.0 1 NA 45.0 15.0 16.0 1 NA
lower 1 40 mamane 2 mauka 2 0.0 0.0 0.0 0 dead 0.0 0.0 0.0 0 nf
lower 1 40 naenae 1 makai 2 67.0 32.0 27.0 1 NA 73.0 44.0 35.0 1 big mama
lower 1 40 naenae 1 makai 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 1 40 naenae 1 mauka 2 33.5 19.0 23.5 1 NA 27.5 11.0 9.0 1 NA
lower 1 40 naenae 1 mauka 2 42.5 20.0 28.0 1 pro 52.0 69.0 45.0 1 flowering
lower 1 40 pawale 1 makai 2 37.0 39.0 34.0 1 NA 51.5 27.0 25.0 1 NA
lower 1 40 pawale 1 makai 2 70.0 70.5 62.0 1 NA 65.0 73.0 70.0 1 NA
lower 1 40 pawale 1 mauka 2 32.0 43.5 33.0 1 NA 37.0 38.0 72.0 1 NA
lower 1 40 pawale 1 mauka 2 62.0 96.0 31.0 1 NA 52.0 38.0 59.0 1 NA
lower 1 40 pawale 2 makai 2 30.5 26.0 35.0 1 NA 22.0 26.5 15.0 1 NA
lower 1 40 pawale 2 makai 2 63.0 37.0 44.5 1 NA 84.0 37.0 46.0 1 NA
lower 1 40 pawale 2 mauka 2 41.0 63.0 70.0 1 NA 61.0 37.0 36.0 1 NA
lower 1 40 pawale 2 mauka 2 81.0 55.0 57.0 1 NA 63.0 70.0 102.0 1 mostly dead, 3 green leaves
lower 2 0 aalii 1 mauka 2 45.0 0.0 0.0 0 just died 0.0 0.0 0.0 0 NA
lower 2 0 aalii 1 mauka 2 34.0 0.0 0.0 0 just died 0.0 0.0 0.0 0 dead
lower 2 0 pawale 1 mauka 2 51.0 30.0 18.0 1 NA 35.0 15.0 28.0 1 NA
lower 2 0 pawale 1 mauka 2 66.0 87.0 80.0 1 NA 87.0 102.0 93.0 1 NA
lower 3 40 mamane 1 mauka 2 41.0 11.0 4.0 1 NA 44.0 10.5 9.0 1 NA
lower 3 40 mamane 1 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 4 100 aalii 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 4 100 aalii 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 4 100 aweoweo 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 4 100 aweoweo 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 4 100 mamane 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 4 100 mamane 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 4 100 pawale 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 4 100 pawale 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
lower 5 40 aalii 1 mauka 2 37.0 0.0 0.0 0 just died 0.0 0.0 0.0 0 dead
lower 5 40 aalii 1 mauka 2 28.0 0.0 0.0 0 just died 0.0 0.0 0.0 0 dead
lower 5 63 pawale 1 makai 2 29.0 32.0 28.0 1 NA 44.0 29.0 13.0 1 NA
lower 5 63 pawale 1 makai 2 29.0 32.0 28.0 1 NA 44.0 29.0 13.0 1 NA
upper 1 0 mamane 1 mauka 2 21.5 3.0 2.0 1 NA 0.0 0.0 0.0 0 dead
upper 1 0 mamane 1 mauka 2 0.0 0.0 0.0 0 dead 0.0 0.0 0.0 0 dead
upper 1 40 aweoweo 1 mauka 2 59.0 25.0 42.0 1 NA 35.0 28.0 33.0 1 next to mullen, pro
upper 1 40 aweoweo 1 mauka 2 88.0 42.0 20.0 1 NA 123.0 62.0 52.0 1 big boy
upper 2 63 aweoweo 1 mauka 2 13.0 3.0 2.0 1 dead 87.0 50.0 70.0 1 fence in way, not found on first measurement
upper 2 63 aweoweo 1 mauka 2 61.0 32.0 45.0 1 NA 148.0 152.0 142.0 1 NA
upper 2 63 mamane 1 mauka 2 38.0 12.0 9.0 1 NA 51.0 18.0 12.0 1 NA
upper 2 63 mamane 1 mauka 2 0.0 0.0 0.0 0 dead 0.0 0.0 0.0 0 NA
upper 4 63 aweoweo 1 makai 2 17.0 21.0 28.0 1 dead 0.0 0.0 0.0 0 dead
upper 4 63 aweoweo 1 makai 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
upper 4 100 mamane 1 control 2 4.0 1.0 1.5 1 dead 0.0 0.0 0.0 0 dead
upper 4 100 mamane 1 control 2 4.0 1.0 1.0 1 NA 0.0 0.0 0.0 0 dead
upper 6 100 aalii 1 control 2 26.0 12.0 15.0 1 NA 33.0 3.0 3.0 1 NA
upper 6 100 aalii 1 control 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 dead
upper 7 40 mamane 1 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
upper 7 40 mamane 1 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
upper 8 0 mamane 1 makai 2 0.0 0.0 0.0 0 dead 0.0 0.0 0.0 0 NA
upper 8 0 mamane 1 makai 2 0.0 0.0 0.0 0 dead 0.0 0.0 0.0 0 NA
upper 8 100 aweoweo 1 control 2 23.0 19.0 15.0 1 NA 46.0 4.0 3.0 1 NA
upper 8 100 aweoweo 1 control 2 54.0 17.0 20.0 1 NA 32.0 6.0 6.0 1 NA
upper 8 100 mamane 1 control 2 4.0 3.0 8.0 1 NA 0.0 0.0 0.0 0 dead
upper 8 100 mamane 1 control 2 11.0 7.0 2.0 1 NA 0.0 0.0 0.0 0 dead
upper 9 40 aalii 1 makai 2 28.0 7.0 4.0 1 NA 0.0 0.0 0.0 0 NA
upper 9 40 aalii 1 makai 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA
upper 9 63 aweoweo 2 mauka 2 49.0 82.5 39.5 1 NA 106.0 174.0 75.0 1 NA
upper 9 63 aweoweo 2 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 nf
upper 10 0 mamane 1 mauka 2 2.0 3.0 1.0 1 NA 0.0 0.0 0.0 0 NA
upper 10 0 mamane 1 mauka 2 0.0 0.0 0.0 0 NA 0.0 0.0 0.0 0 NA

We have 84 duplicates in the FOG data. We will need to address these in the data cleaning step. For now, we will just note them.

T1 Measurements

Survivors with no measurement

Let’s check for survivors that have no measurements. This could indicate a problem with the data collection or entry.

#' Filter survivors with missing or zero measurements
#'
#' This function filters a data frame or tibble to include only rows where the
#' specified survival column equals 1 and at least one of the specified measurement
#' columns is either zero or (optionally) NA. It returns a subset of selected columns.
#'
#' @param df A data frame or tibble containing the data.
#' @param survival_col A string specifying the name of the survival column.
#'    Default is `"survival_t1"`.
#' @param measurement_cols A character vector of column names to check
#'    for zero or missing values.
#'                         Default is `c("ht_t1", "w1_t1", "w2_t1")`.
#' @param select_cols A character vector of column names to include in the output.
#'                    Default includes:
#'                      site, replicate, treatment, species, plant, position.
#' @param treat_na_as_missing Logical. If `TRUE`,
#'    NA values in measurement columns are treated as missing
#'                            and included in the filter. Default is `TRUE`.
#'
#' @return A tibble containing the filtered rows and selected columns.
#' @examples
#' filter_survivors_no_measurement(fog_data)
#' filter_survivors_no_measurement(fog_data, treat_na_as_missing = FALSE)
#' filter_survivors_no_measurement(fog_data,
#'                                 survival_col = "alive_t1",
#'                                 measurement_cols = c("height_t1", "width1_t1", "width2_t1"),
#'                                 select_cols = c("site", "species", "plant", "height_t1", "width1_t1", "width2_t1"))
filter_survivors_no_measurement <- function(df,
                                            survival_col = "survival_t1",
                                            measurement_cols =
                                              c("ht_t1", "w1_t1", "w2_t1"),
                                            select_cols =
                                              c("site",
                                                "replicate",
                                                "treatment",
                                                "species",
                                                "plant",
                                                "position"),
                                            treat_na_as_missing = TRUE) {
  # Check for required columns
  required <- c(survival_col, measurement_cols, select_cols)
  missing <- setdiff(required, names(df))
  if (length(missing) > 0) {
    stop("Missing required columns: ", paste(missing, collapse = ", "))
  }
  
  # Build dynamic filter expression
  zero_or_na_exprs <- lapply(measurement_cols, function(col) {
    if (treat_na_as_missing) {
      rlang::expr((!!rlang::sym(col) == 0) | is.na(!!rlang::sym(col)))
    } else {
      rlang::expr(!!rlang::sym(col) == 0)
    }
  })
  
  combined_measurement_expr <- purrr::reduce(zero_or_na_exprs, function(x, y)
    rlang::expr(!!x | !!y))
  
  full_filter_expr <- rlang::expr(!!rlang::sym(survival_col) == 1 &
                                    (!!combined_measurement_expr))
  
  df %>%
    filter(!!full_filter_expr) %>%
    select(all_of(select_cols))
}

# Check for survivors at t1 with no measurements in FOG data
filter_survivors_no_measurement(
  fog_data,
  survival_col = "survival_t1",
  measurement_cols = c("ht_t1", "w1_t1", "w2_t1"),
  select_cols = c("site", "replicate", "treatment",
                  "species", "plant", "position",
                  "ht_t1", "w1_t1", "w2_t1")) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant position ht_t1 w1_t1 w2_t1
lower 4 63 aalii 1 mauka 51 0 0
upper 5 40 aweoweo 1 makai 61 10 0
upper 4 63 pawale 1 makai 0 0 0

Just 3, we can address these in the data cleaning step. But we need to choose how we want to handle these. We can either remove them or replace the 0 values with NA or replace all the measurements with NA. For now, we will just note them.

lower-4-63-aalii-mauka-plant1: Note says just died. I vote we code survival as 0 and measurements as NA.

upper-5-40-aweoweo-plant1: Note says eaten. No leaves. I vote we code as 0 and measurements as NA. It is dead at the next time step.

upper-4-63-pawale-plant1: Note says not found. It was found at time step 2. I vote I NA the measurements but code as 1. I’ll Drop.NA before analysis so it won’t be included in any analyses with measurements.

Died but with measurements

Let’s check for plants that died but have measurements. This could indicate a problem with the data collection or entry or could be a valid observation if dead plants were measured.


#' Filter non-survivors with non-zero measurements
#'
#' This function filters a data frame or tibble to include only rows where the
#' specified survival column equals 0 and at least one of the specified measurement
#' columns is greater than zero or (optionally) not NA. It returns a subset of selected columns.
#'
#' @param df A data frame or tibble containing the data.
#' @param survival_col A string specifying the name of the survival column. Default is `"survival_t1"`.
#' @param measurement_cols A character vector of column names to check for non-zero or non-missing values.
#'                         Default is `c("ht_t1", "w1_t1", "w2_t1")`.
#' @param select_cols A character vector of column names to include in the output.
#'                    Default includes site, replicate, treatment, species, plant, position, and measurements.
#' @param treat_na_as_present Logical. If `TRUE`, NA values in measurement columns are treated as present
#'                            and included in the filter. Default is `FALSE`.
#'
#' @return A tibble containing the filtered rows and selected columns.
#' @examples
#' filter_non_survivors_with_measurement(fog_data)
#' filter_non_survivors_with_measurement(fog_data, treat_na_as_present = TRUE)
#' filter_non_survivors_with_measurement(fog_data,
#'                                       survival_col = "alive_t1",
#'                                       measurement_cols = c("height_t1", "width1_t1", "width2_t1"),
#'                                       select_cols = c("site", "species", "plant", "height_t1", "width1_t1", "width2_t1"))
filter_non_survivors_with_measurement <- function(df,
                                                  survival_col = "survival_t1",
                                                  measurement_cols = c("ht_t1", "w1_t1", "w2_t1"),
                                                  select_cols = c("site", "replicate", "treatment", "species", "plant", "position",
                                                                  "ht_t1", "w1_t1", "w2_t1"),
                                                  treat_na_as_present = FALSE) {
  # Check for required columns
  required <- c(survival_col, measurement_cols, select_cols)
  missing <- setdiff(required, names(df))
  if (length(missing) > 0) {
    stop("Missing required columns: ", paste(missing, collapse = ", "))
  }

  # Build dynamic filter expression
  present_exprs <- lapply(measurement_cols, function(col) {
    if (treat_na_as_present) {
      rlang::expr((!!rlang::sym(col) > 0) | is.na(!!rlang::sym(col)))
    } else {
      rlang::expr(!!rlang::sym(col) > 0)
    }
  })

  combined_measurement_expr <- purrr::reduce(present_exprs, function(x, y) rlang::expr(!!x | !!y))

  full_filter_expr <- rlang::expr(
    !!rlang::sym(survival_col) == 0 & (!!combined_measurement_expr)
  )

  df %>%
    filter(!!full_filter_expr) %>%
    select(all_of(select_cols))
}
# Check for plants that died but have measurements in FOG data
filter_non_survivors_with_measurement(
  fog_data,
  survival_col = "survival_t1",
  measurement_cols = c("ht_t1", "w1_t1", "w2_t1"),
  select_cols = c("site", "replicate", "treatment",
                  "species", "plant", "position",
                  "ht_t1", "w1_t1", "w2_t1")) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant position ht_t1 w1_t1 w2_t1
lower 2 0 aalii 1 mauka 45 0 0
lower 2 0 aalii 1 mauka 34 0 0
lower 5 0 aalii 1 mauka 28 0 0
lower 5 40 aalii 1 mauka 37 0 0
lower 5 40 aalii 1 mauka 28 0 0
lower 5 100 aalii 1 control 17 0 0

So 6 observations with 2 duplicate observations. We can address these in the data cleaning step. But we need to choose how we want to handle these. We can either remove them or replace the 0 values with NA, replace all the measurements with NA or leave them but then how do we want to deal with them in the analysis. For now, we will just note them. ### T2 Measurements

lower-2-0-aalii-mauka-p1: Both entries are recorded as just died in notes. I vote they be coded as 0 with measurements NA. We can keep the duplicate records if we can confirm that there are two plants and that plant2 was just miss entered. Or we can delete one of them if we are unsure.

lower-5-0-aalii-mauka-p1: Recorded as just died in notes. I vote code as 0 with measurements, NA.

lower-5-40-aalii-mauka-p1: Both, entries are recorded as just died in notes. I vote code as 0 with measurements, NA. We can keep the duplicate records if we can confirm that there are two plants and that plant2 was just miss entered. Or we can delete one of them if we are unsure.

lower-5-100-aalii-control-p1: Recorded as just died. I vote coded as 0 with measurements, NA.

Survivors with no measurement


# Check for survivors at t2 with no measurements in FOG data
filter_survivors_no_measurement(
  fog_data,
  survival_col = "survival_t2",
  measurement_cols = c("ht_t2", "w1_t2", "w2_t2"),
  select_cols = c("site", "species", "plant", "ht_t2", "w1_t2", "w2_t2")) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site species plant ht_t2 w1_t2 w2_t2

None! that’s great! We can move on to the next step.

Died but with measurements

Let’s check for plants that died but have measurements. This could indicate a problem with the data collection or entry or could be a valid observation if dead plants were measured.

# Check for plants that died but have measurements in FOG data
# Check for plants that died but have measurements in FOG data
filter_non_survivors_with_measurement(
  fog_data,
  survival_col = "survival_t2",
  measurement_cols = c("ht_t2", "w1_t2", "w2_t2"),
  select_cols = c("site", "replicate", "treatment",
                  "species", "plant", "position",
                  "ht_t2", "w1_t2", "w2_t2")) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant position ht_t2 w1_t2 w2_t2

None! that’s great! We can move on to the next step.

Lazurus Plants

Let’s check for plants that were dead in T1 but were alive in T2. This could indicate a problem with the data collection or entry or could be a valid observation if plants were added between T1 and T2.

# Check for lazarus plants in FOG data
lazarus_plants_fog <- fog_data %>%
  filter(survival_t1 == 0 & survival_t2 == 1) %>%
  select(site, replicate, treatment, species, plant, position)

lazarus_plants_fog %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant position

They have NOT risen!! We have no lazarus plants in the FOG data. That’s great! We can move on to the next step.

NURSE Data Issues

Duplicates

The easiest issue to identify problem is duplicates. Let’s check for duplicates in the FOG data.

# Check for duplicates in FOG data
nurse_duplicates <- nurse_data %>%
  janitor::get_dupes(site, replicate, treatment, species, plant)

writexl::write_xlsx(nurse_duplicates, file.path(notes_dir, "NURSE_dupes.xlsx"))

nurse_duplicates %>% 
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant dupe_count ht_t1 w1_t1 w2_t1 survival_t1 note1 ht_t2 w1_t2 w2_t2 survival_t2 note2
lower 1 control aalii 1 2 33 7 8.0 1 NA 45 6 5 1 NA
lower 1 control aalii 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control aalii 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control aalii 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control aweoweo 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control aweoweo 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control aweoweo 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control aweoweo 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control mamane 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control mamane 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control mamane 2 2 10 2 2.5 1 NA 17 3 3 1 NA
lower 1 control mamane 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control naenae 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control naenae 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control pawale 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control pawale 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 1 control pawale 2 2 0 0 0.0 0 NA 0 0 0 0 NA
lower 1 control pawale 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 2 nurse m mamane 2 2 26 6 5.0 1 NA 27 13 9 1 NA
lower 2 nurse m mamane 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 3 nurse m naenae 1 2 37 20 12.0 1 lower slope 48 27 18 1 NA
lower 3 nurse m naenae 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control aalii 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control aalii 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control aalii 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control aalii 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control aweoweo 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control aweoweo 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control aweoweo 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control aweoweo 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control mamane 1 2 10 2 2.5 1 nf 23 7 4 1 not found on first measurement
lower 8 control mamane 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control mamane 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control mamane 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control naenae 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control naenae 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control pawale 1 2 0 0 0.0 0 dead 0 0 0 0 dead
lower 8 control pawale 1 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 8 control pawale 2 2 0 0 0.0 0 dead 0 0 0 0 dead
lower 8 control pawale 2 2 0 0 0.0 0 nf 0 0 0 0 nf
lower 9 nurse k naenae 1 2 17 10 6.0 1 NA 11 7 5 1 pro
lower 9 nurse k naenae 1 2 0 0 0.0 0 nf 0 0 0 0 nf
upper 9 nurse upper pawale NA 2 0 0 0.0 0 nf 0 0 0 0 nf
upper 9 nurse upper pawale NA 2 0 0 0.0 0 nf 0 0 0 0 nf

We have 44 duplicates in the NURSE data. We will need to address these in the data cleaning step. For now, we will just note them.

T1 Measurements

Survivors with no measurement

Let’s check for survivors that have no measurements. This could indicate a problem with the data collection or entry.


# Check for survivors at t1 with no measurements in FOG data
filter_survivors_no_measurement(
  nurse_data,
  survival_col = "survival_t1",
  measurement_cols = c("ht_t1", "w1_t1", "w2_t1"),
  select_cols = c("site", "replicate", "treatment", "species", "plant",
                  "ht_t1", "w1_t1", "w2_t1")) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant ht_t1 w1_t1 w2_t1
lower 6 nurse m aweoweo 1 67 12 0

No Notes for this record at T1. At T2, eaten no foliage. measured stem.

Died but with measurements

Let’s check for plants that died but have measurements. This could indicate a problem with the data collection or entry or could be a valid observation if dead plants were measured.

# Check for plants that died but have measurements in FOG data
filter_non_survivors_with_measurement(
  nurse_data,
  survival_col = "survival_t1",
  measurement_cols = c("ht_t1", "w1_t1", "w2_t1"),
  select_cols = c("site", "replicate", "treatment", "species", "plant",
                  "ht_t1", "w1_t1", "w2_t1", "survival_t1")) %>%
  arrange(site, replicate, treatment, species, plant) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant ht_t1 w1_t1 w2_t1 survival_t1
upper 1 control aalii 2 32.5 11 5 0

Quite a few. We can address these in the data cleaning step. But we need to choose how we want to handle these. We can either remove them or replace the 0 values with NA, replace all the measurements with NA or leave them but then how do we want to deal with them in the analysis. For now, we will just note them. ### T2 Measurements

upper-1-control-aalii-2: No notes.

Survivors with no measurement


# Check for survivors at t2 with no measurements in FOG data
filter_survivors_no_measurement(
  nurse_data,
  survival_col = "survival_t2",
  measurement_cols = c("ht_t2", "w1_t2", "w2_t2"),
  select_cols = c("site", "replicate", "treatment", "species", "plant",
                  "ht_t2", "w1_t2", "w2_t2")) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant ht_t2 w1_t2 w2_t2

None! that’s great! We can move on to the next step.

Died but with measurements

Let’s check for plants that died but have measurements. This could indicate a problem with the data collection or entry or could be a valid observation if dead plants were measured.

# Check for plants that died but have measurements in FOG data
# Check for plants that died but have measurements in FOG data
filter_non_survivors_with_measurement(
  nurse_data,
  survival_col = "survival_t2",
  measurement_cols = c("ht_t2", "w1_t2", "w2_t2"),
  select_cols = c("site", "replicate", "treatment", "species", "plant",
                  "ht_t2", "w1_t2", "w2_t2", "survival_t2")) %>%
  arrange(site, replicate, treatment, species, plant) %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant ht_t2 w1_t2 w2_t2 survival_t2
lower 6 nurse m aweoweo 1 48 0 0 0

Quite a few. We can address these in the data cleaning step. But we need to choose how we want to handle these. We can either remove them or replace the 0 values with NA, replace all the measurements with NA or leave them but then how do we want to deal with them in the analysis. For now, we will just note them.

lower-6-nurse m-aweoweo-plant1: No notes.

Lazurus Plants

Let’s check for plants that were dead in T1 but were alive in T2. This could indicate a problem with the data collection or entry or could be a valid observation if plants were added between T1 and T2.

# Check for lazarus plants in FOG data
lazarus_plants_fog <- nurse_data %>%
  filter(survival_t1 == 0 & survival_t2 == 1) %>%
  select("site", "replicate", "treatment", "species", "plant")

lazarus_plants_fog %>%
  kbl() %>%
  kable_minimal(full_width = F)
site replicate treatment species plant
upper 10 nurse upper pawale 1
upper 9 nurse upper aalii 1

We have two Lazarus plants in the Nurse data.

upper-10-nurse upper-pawale-plant1: No notes.

upper-9-nurse upper-aalii-plant1: Note: not found on measurement 1.

LS0tDQp0aXRsZTogIkhhd2FpaSBSZXN0b3JhdGlvbiBQcm9qZWN0IERhdGEgRXhwbG9yYXRpb24iDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogIHdvcmRfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoaGVyZSkNCmxpYnJhcnkoRGF0YUV4cGxvcmVyKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCg0KIyBTZXR1cCBhbmQgbmFtZSB0aGUgZGlyZWN0b3J5IHN0cnVjdHVyZQ0KcHJval9kaXIgPC0gaGVyZTo6aGVyZSgpDQpkYXRhX2RpciA8LSBmaWxlLnBhdGgocHJval9kaXIsICJkYXRhIikNCm5vdGVib29rX2RpciA8LSBmaWxlLnBhdGgocHJval9kaXIsICJub3RlYm9va3MiKQ0KZmlndXJlc19kaXIgPC0gZmlsZS5wYXRoKHByb2pfZGlyLCAiZmlndXJlcyIpDQp0YWJsZXNfZGlyIDwtIGZpbGUucGF0aChwcm9qX2RpciwgInRhYmxlcyIpDQpub3Rlc19kaXIgPC0gZmlsZS5wYXRoKHByb2pfZGlyLCAibm90ZXMiKQ0KDQoNCmBgYA0KDQojIE9iamVjdGl2ZQ0KDQpUaGlzIG5vdGVib29rIGV4cGxvcmVzIHRoZSBkYXRhIGZyb20gdGhlIEhhd2FpaSBSZXN0b3JhdGlvbiBQcm9qZWN0LiBUaGUgZ29hbCBpcyB0byB1bmRlcnN0YW5kIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEsIGlkZW50aWZ5IGFueSBtaXNzaW5nIHZhbHVlcywgYW5kIHN1bW1hcml6ZSBhbmQgdmlzdWFsaXplIGtleSB2YXJpYWJsZXMuDQoNCiMgRXhwZXJpbWVudGFsIERlc2lnbg0KDQpUaGUgSGF3YWlpIFJlc3RvcmF0aW9uIFByb2plY3QgY29uc2lzdHMgb2YgdHdvIG1haW4gZXhwZXJpbWVudHM6IHRoZSBGT0cgZXhwZXJpbWVudCBhbmQgdGhlIE5VUlNFIGV4cGVyaW1lbnQuDQoNCiMjIFRoZSBGb2cgRXhwZXJpbWVudA0KDQpUaGUgZm9nIGV4cGVyaW1lbnQgZXhhbWluZWQgdGhlIGVmZmVjdCBvZiBmb2cgY2FwdHVyZSBvbiB0aGUgcmVzdG9yYXRpb24gc3VjY2VzcyBvZiBuYXRpdmUgSGF3YWlpYW4gcGxhbnRzLiBUaGUgZXhwZXJpbWVudCB3YXMgc2V0IHVwIGF0IHR3byBkaWZmZXJlbnQgc2l0ZXMuDQoNCiMjIFRoZSBOdXJzZSBQbGFudCBFeHBlcmltZW50DQoNClRoZSBudXJzZSBleHBlcmltZW50IGV4YW1pbmVkIHRoZSBlZmZlY3Qgb2YgbnVyc2UgcGxhbnRzIG9uIHRoZSByZXN0b3JhdGlvbiBzdWNjZXNzIG9mIG5hdGl2ZSBIYXdhaWlhbiBwbGFudHMuIFRoZSBleHBlcmltZW50IHdhcyBzZXQgdXAgYXQgdHdvIGRpZmZlcmVudCBzaXRlcy4NCg0KIyBMb2FkIERhdGENCg0KRmlyc3Qgc3RlcCBpcyB0byBsb2FkIHRoZSBkYXRhLiBUaGUgZGF0YSB3aWxsIGJlIGluIHRoZSBkYXRhIGRpcmVjdG9yeS4gVGhlIGRhdGEgaXMgYW4gZXhjZWwgd29ya2Jvb2sgd2l0aCB0aHJlZSBzaGVldHM6IE1ldGFkYXRhLCBGT0csIGFuZCBOVVJTRS4NCg0KLSAgIE1ldGFkYXRhIC0gY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHZhcmlhYmxlcyBhbmQgaXMgbG9vc2UgZGF0YSBkaWN0aW9uYXJ5IGZvciBlYWNoIG9mIHRoZSB2YXJpYWJsZXMNCi0gICBGT0cgLSBjb250YWlucyBkYXRhIGZyb20gdGhlIGZvZyBleHBlcmltZW50DQotICAgTlVSU0UgLSBjb250YWlucyBkYXRhIGZyb20gdGhlIG51cnNlIHBsYW50IGV4cGVyaW1lbnQNCg0KYGBge3IgbG9hZC1kYXRhfQ0KbGlicmFyeShyZWFkeGwpDQojIExvYWQgdGhlIEZPRyBkYXRhDQpmb2dfZGF0YSA8LSByZWFkeGw6OnJlYWRfZXhjZWwoZmlsZS5wYXRoKGRhdGFfZGlyLCAiS0JDX2RhdGFfU1kueGxzeCIpLCBzaGVldCA9ICJGT0ciKQ0KIyBMb2FkIHRoZSBOVVJTRSBkYXRhDQpudXJzZV9kYXRhIDwtIHJlYWR4bDo6cmVhZF9leGNlbChmaWxlLnBhdGgoZGF0YV9kaXIsICJLQkNfZGF0YV9TWS54bHN4IiksIHNoZWV0ID0gIk5VUlNFIikNCg0KYGBgDQoNCiMgRXhwbG9yZSB0aGUgUmF3IERhdGENCg0KTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGRhdGEgc3RyYWlnaHQgb3V0IG9mIHRoZSBleGNlbCBmaWxlLg0KDQojIyBEYXRhIHRhYmxlIHN0cnVjdHVyZQ0KDQpgYGB7ciBkYXRhLXN0cnVjdHVyZX0NCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZSBvZiB0aGUgRk9HIGRhdGENCg0KRGF0YUV4cGxvcmVyOjpwbG90X3N0cihsaXN0KEZPRz1mb2dfZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBOVVJTRT1udXJzZV9kYXRhKSkNCg0KYGBgDQoNCiMjIyBDb2x1bW4gTmFtZSBhbmQgVHlwZSBBZGp1c3RtZW50cw0KDQpXZSB3YW50IHRvIGRyb3AgdGhlIGNvbHVtbnMgMTktMjQgaW4gYm90aCBkYXRhIGZyYW1lcyBzaW5jZSB0aGV5IGFyZSBlbXB0eS4gV2Ugc2hvdWxkIGRyb3AgdGhlIHZvbHVtZSBjb2x1bW5zIHNvIHRoYXQgd2UgY2FuIHNob3cgb3VyIHdvcmsgZm9yIHJlcHJvZHVjaWJpbGl0eS4gV2Ugd2lsbCBzdGlsbCB1c2UgdGhlIHZvbHVtZSBtZWFzdXJlIGluIHRoZSBvcmlnaW5hbCBkYXRhIHNldCAoYSByZWN0YW5ndWxhciBwcmlzbSkgd2l0aCBpbiBjdWJpYyBkZWNpbWV0ZXJzIChIZWlnaHQgWCBXaWR0aCBYIERlcHRoIFggMS8xLDAwMCkuIFVubGVzcywgd2Ugd2FudCB0byBwcmVzZW50IG90aGVyIHZvbHVtZXMuIEhvd2V2ZXIsIHRoaXMgaXMganVzdCBzY2FsaW5nIHRoZSB2b2x1bWUgKEh4V3hEIGFuZCBoYXMgbGl0dGxlIGVmZmVjdCBzaW5jZSB3ZSB3aWxsIHNjYWxlIGFsbCB0aGUgdmFyaWFibGVzIGlmIHVzZWQgYXMgcHJlZGljdG9ycyBhbmQgZXZlcnl0aGluZyBlbHNlIHdpbGwganVzdCBjaGFuZ2UgdGhlIGJldGEgY29lZmZpY2llbnRzKS4gV2UgY2FuIGFsc28gZHJvcCB0aGUgbm90ZXMgY29sdW1ucyBzaW5jZSB0aGV5IGFyZSBub3QgbmVlZGVkIGZvciB0aGUgYW5hbHlzaXMuIFdlIHNob3VsZCBtYWtlIGFsbCBjb2x1bW4gbmFtZXMgbG93ZXIgY2FzZS4gV2Ugc2hvdWxkIHJlbmFtZSBtYXVrYS9tYWthaSB0byBwb3NpdGlvbiBzbyB0aGF0IHdlIGRvbid0IGhhdmUgYSBzbGFzaCBpbiBvdXIgY29sdW1uIG5hbWVzLiBJIGFsc28gd2FudCB0byByZW5hbWUgcGxhbnQxIGFuZCBzcGVjaWVzMSB0byBqdXN0IHBsYW50IGFuZCBzcGVjaWVzLiBXZSBzaG91bGQgY29udmVydCBzaXRlLCByZXBsaWNhdGUsIHRyZWF0bWVudCwgc3BlY2llcywgcGxhbnQgYW5kIHBvc2l0aW9uIHRvIGZhY3RvcnMuIFN1cnZpdmFsIHNob3VsZCBjb252ZXJ0ZWQgdG8gMCBhbmQgMS4NCg0KYGBge3IgYWRqdXN0LWNvbHVtbi1uYW1lc30NCg0KIyBBZGp1c3QgY29sdW1uIG5hbWVzIGFuZCB0eXBlcyBmb3IgRk9HIGRhdGENCmZvZ19kYXRhIDwtIGZvZ19kYXRhICU+JQ0KICBzZWxlY3QoLWMoMTk6MjQpKSAlPiUNCiAgc2VsZWN0KC1jKFZvbHVtZV9UMSwgVm9sdW1lX1QyKSkgJT4lDQogIHJlbmFtZV93aXRoKHRvbG93ZXIpICU+JQ0KICByZW5hbWUocG9zaXRpb24gPSBgbWF1a2EvbWFrYWlgLHBsYW50ID0gcGxhbnQxLCBzcGVjaWVzID0gc3BlY2llczEsDQogICAgICAgICBub3RlMSA9ICdub3RlcyAxJywgbm90ZTIgPSAnbm90ZXMgMicpICU+JQ0KICBtdXRhdGUoYWNyb3NzKGMoc2l0ZSwgcmVwbGljYXRlLCB0cmVhdG1lbnQsIHNwZWNpZXMsIHBsYW50LCBwb3NpdGlvbiksIGFzLmZhY3RvciksDQogICAgICAgICBzdXJ2aXZhbF90MSA9IGlmZWxzZShzdXJ2aXZhbF90MSA9PSAiWSIsIDEsIDApLA0KICAgICAgICAgc3Vydml2YWxfdDIgPSBpZmVsc2Uoc3Vydml2YWxfdDIgPT0gIlkiLCAxLCAwKSkNCg0KDQojIEFkanVzdCBjb2x1bW4gbmFtZXMgYW5kIHR5cGVzIGZvciBOVVJTRSBkYXRhDQpudXJzZV9kYXRhIDwtIG51cnNlX2RhdGEgJT4lDQogIHNlbGVjdCgtYygxODoyNCkpICU+JQ0KICBzZWxlY3QoLWMoVm9sdW1lX1QxLCBWb2x1bWVfVDIpKSAlPiUNCiAgcmVuYW1lX3dpdGgodG9sb3dlcikgJT4lIA0KICByZW5hbWUocGxhbnQgPSBwbGFudDEsIG5vdGUxPSAnbm90ZXMuLi4xMCcsbm90ZTI9ICdub3Rlcy4uLjE1JykgJT4lDQogIG11dGF0ZShhY3Jvc3MoYyhzaXRlLCByZXBsaWNhdGUsIHRyZWF0bWVudCwgc3BlY2llcywgcGxhbnQpLCBhcy5mYWN0b3IpLA0KICAgICAgICAgc3Vydml2YWxfdDEgPSBpZmVsc2Uoc3Vydml2YWxfdDEgPT0gInkiLCAxLCAwKSwNCiAgICAgICAgIHN1cnZpdmFsX3QyID0gaWZlbHNlKHN1cnZpdmFsX3QyID09ICJ5IiwgMSwgMCkpDQoNCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZSBhZ2FpbiBhZnRlciBhZGp1c3RtZW50cw0KRGF0YUV4cGxvcmVyOjpwbG90X3N0cihsaXN0KGZvZ19kYXRhLCBudXJzZV9kYXRhKSkNCmBgYA0KDQojIyBGT0cgRGF0YSBFeHBsb3JhdGlvbg0KDQojIyMgQSBxdWljayBkZXNjcmlwdGlvbiBvZiB0aGUgZm9nIGRhdGENCg0KYGBge3IgZm9nLWRhdGEtZGVzY3JpcHRpb259DQoNCiMgR2V0IGEgcXVpY2sgZGVzY3JpcHRpb24gb2YgdGhlIGRhdGENCkRhdGFFeHBsb3Jlcjo6aW50cm9kdWNlKGZvZ19kYXRhICU+JSBzZWxlY3QoLWMobm90ZTEsIG5vdGUyKSkpICU+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSANCiAga2JsKCkgJT4lIA0KICBrYWJsZV9taW5pbWFsKGZ1bGxfd2lkdGggPSBGKQ0KDQpgYGANCg0KYGBge3IgdmlzdWFsaXplLWZvZy1kYXRhLWRlc2NyaXB0aW9ufQ0KDQojIFZpc3VhbGl6ZSB0aGUgZGF0YSBkZXNjcmlwdGlvbiBmb3IgRk9HIGRhdGENCkRhdGFFeHBsb3Jlcjo6cGxvdF9pbnRybyhmb2dfZGF0YSAlPiUgc2VsZWN0KC1jKG5vdGUxLCBub3RlMikpLCB0aXRsZSA9ICJGT0cgRGF0YSBPdmVydmlldyIpDQpgYGANCg0KIyMjIExvb2tpbmcgYXQgbWlzc2luZyB2YWx1ZXMgZm9yIEZPRyBkYXRhDQoNCmBgYHtyIHZpc3VsaXplLW1pc3NpbmctZGF0YS1mb2d9DQojIFZpc3VhbGl6ZSBtaXNzaW5nIGRhdGEgZm9yIEZPRyBkYXRhDQpEYXRhRXhwbG9yZXI6OnBsb3RfbWlzc2luZyhmb2dfZGF0YSAlPiUgc2VsZWN0KC1jKG5vdGUxLCBub3RlMikpLCB0aXRsZSA9ICJNaXNzaW5nIERhdGEgaW4gRk9HIERhdGEiKQ0KYGBgDQoNCldlIGtub3cgdGhlcmUncyBhbiBpc3N1ZSBoZXJlIHRoZSB3aWR0aHMgYW5kIGhlaWdodCB2YXJpYWJsZXMgYXJlIGNvZGVkIGFzIDAgd2hlbiB0aGV5IHdlcmUgbm90IHByZXNlbnQuIFdlIHdpbGwgbmVlZCB0byBhZGRyZXNzIHRoaXMgaW4gZGF0YSBjbGVhbmluZy4gRm9yIG5vdywgdGhpcyBpcyBzdXBlciBncm9vdnkhDQoNCiMjIyBRdWljayBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHZhcmlhYmxlcyBpbiB0aGUgRk9HIGRhdGENCg0KIyMjIyBEaXNjcmV0ZSB2YXJpYWJsZXMNCg0KYGBge3IgdmlzdWFsaXplLWZvZy1kaXNjcmV0ZS1kYXRhLWRpc3RyaWJ1dGlvbn0NCg0KIyBWaXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkaXNjcmV0ZSB2YXJpYWJsZXMgaW4gRk9HIGRhdGENCkRhdGFFeHBsb3Jlcjo6cGxvdF9iYXIoZm9nX2RhdGEgJT4lIHNlbGVjdCgtYyhub3RlMSwgbm90ZTIpKSwgbmNvbCA9IDMsIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBEaXNjcmV0ZSBWYXJpYWJsZXMgaW4gRk9HIERhdGEiKQ0KYGBgDQoNCk5lZWQgdG8gZml4IG1hdWthICgxLzE4LzIwMjIpIGFuZCB0byBNYXVrYS4gVEhJUyBORUVEUyBUTyBCRSBGSVhFRCBJTiBUSEUgREFUQSBDTEVBTklORyBTVEVQIQ0KDQpgYGB7ciBmaXgtbWF1a2EsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEZpeCB0aGUgbWF1a2EvbWFrYWkgdmFsdWVzIGluIEZPRyBkYXRhDQpmb2dfZGF0YSA8LSBmb2dfZGF0YSAlPiUNCiAgbXV0YXRlKHBvc2l0aW9uID0gcmVjb2RlKHBvc2l0aW9uLCAibWF1a2EgKDEvMTgvMjAyMikiID0gIm1hdWthIikpDQoNCkRhdGFFeHBsb3Jlcjo6cGxvdF9iYXIoZm9nX2RhdGEgJT4lIHNlbGVjdCgtYyhub3RlMSwgbm90ZTIpKSwgbmNvbCA9IDMsIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBEaXNjcmV0ZSBWYXJpYWJsZXMgaW4gRk9HIERhdGEiKQ0KYGBgDQoNCiMjIyMgQ29udGludW91cyB2YXJpYWJsZXMNCg0KYGBge3IgdmlzdWFsaXplLWZvZy1jb250aW51b3VzLWRhdGEtZGlzdHJpYnV0aW9ufQ0KIyBWaXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjb250aW51b3VzIHZhcmlhYmxlcyBpbiBGT0cgZGF0YQ0KDQojIFZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIGNvbnRpbnVvdXMgdmFyaWFibGVzIGluIEZPRyBkYXRhDQoNCkRhdGFFeHBsb3Jlcjo6cGxvdF9oaXN0b2dyYW0oZm9nX2RhdGEgJT4lIHNlbGVjdCgtYyhub3RlMSwgbm90ZTIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMsIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb250aW51b3VzIFZhcmlhYmxlcyBpbiBGT0cgRGF0YSIpDQpgYGANCg0KRm9yIHRoZSBkYXRhIGNsZWFuaW5nIHdlIG5lZWQgdG8gYWRkcmVzcyB0aGUgMCB2YWx1ZXMgaW4gdGhlIHdpZHRoIGFuZCBoZWlnaHQgdmFyaWFibGVzLiBXaGlsZSBsb29raW5nIGF0IHRoaXMgd2UgaGF2ZSBvcHRpb25zOg0KDQoxLiAgV2UgY2FuIHJlcGxhY2UgdGhlIDAgdmFsdWVzIHdpdGggTkEuDQoyLiAgV2UgY2FuIHJlcGxhY2Ugd2lkdGhzIGFuZCBoZWlnaHRzIHdlcmUgdGhlIHN1cnZpdmFsIGlzIDAgd2l0aCBOQS4NCg0KSSB0aGluayBmb3Igbm93IHdlIHdpbGwgZ28gd2l0aCBvcHRpb24gMS4gV2UgY2FuIGFsd2F5cyByZXZpc2l0IHRoaXMgbGF0ZXIgaWYgd2UgbmVlZCB0by4NCg0KIyMgTlVSU0UgRGF0YSBFeHBsb3JhdGlvbg0KDQojIyMgQSBxdWljayBkZXNjcmlwdGlvbiBvZiB0aGUgbnVyc2UgZGF0YQ0KDQpgYGB7ciBudXJzZS1kYXRhLWRlc2NyaXB0aW9ufQ0KDQojIEdldCBhIHF1aWNrIGRlc2NyaXB0aW9uIG9mIHRoZSBkYXRhDQpEYXRhRXhwbG9yZXI6OmludHJvZHVjZShudXJzZV9kYXRhICU+JSBzZWxlY3QoLWMobm90ZTEsIG5vdGUyKSkpICU+JSANCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBldmVyeXRoaW5nKCksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSANCiAga2JsKCkgJT4lIA0KICBrYWJsZV9taW5pbWFsKGZ1bGxfd2lkdGggPSBGKQ0KDQpgYGANCg0KYGBge3IgdmlzdWFsaXplLW51cnNlLWRhdGEtZGVzY3JpcHRpb259DQoNCiMgVmlzdWFsaXplIHRoZSBkYXRhIGRlc2NyaXB0aW9uIGZvciBGT0cgZGF0YQ0KRGF0YUV4cGxvcmVyOjpwbG90X2ludHJvKG51cnNlX2RhdGEgJT4lIHNlbGVjdCgtYyhub3RlMSwgbm90ZTIpKSwgdGl0bGUgPSAiTlVSU0UgRGF0YSBPdmVydmlldyIpDQpgYGANCg0KIyMjIExvb2tpbmcgYXQgbWlzc2luZyB2YWx1ZXMgZm9yIE5VUlNFIGRhdGENCg0KYGBge3IgdmlzdWxpemUtbWlzc2luZy1kYXRhLW51cnNlfQ0KIyBWaXN1YWxpemUgbWlzc2luZyBkYXRhIGZvciBGT0cgZGF0YQ0KRGF0YUV4cGxvcmVyOjpwbG90X21pc3NpbmcobnVyc2VfZGF0YSAlPiUgc2VsZWN0KC1jKG5vdGUxLCBub3RlMikpLCB0aXRsZSA9ICJNaXNzaW5nIERhdGEgaW4gRk9HIERhdGEiKQ0KYGBgDQoNCldlIGtub3cgdGhlcmUncyBhbiBpc3N1ZSBoZXJlIHRoZSB3aWR0aHMgYW5kIGhlaWdodCB2YXJpYWJsZXMgYXJlIGNvZGVkIGFzIDAgd2hlbiB0aGV5IHdlcmUgbm90IHByZXNlbnQuIFdlIG5lZWQgdG8gZGV0ZXJtaW5lIHdoYXQgaXMgbWlzc2luZyBpbiB0aGUgcGxhbnQgY29sdW1uLg0KDQpgYGB7ciBtaXNzaW5nLXBsYW50cy1udXJzZX0NCg0KIyBQcmludCBhIHRhYmxlIG9mIHRoZSBtaXNzaW5nIHBsYW50IG51bWJlciBpbiB0aGUgTlVSU0UgZGF0YQ0KbnVyc2VfZGF0YSAlPiUNCiAgZmlsdGVyKGlzLm5hKHBsYW50KSkgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9taW5pbWFsKGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCk9rYXkgdGhlc2UgYXJlIGFsbCBwbGFudHMgZnJvbSB0aGUgdXBwZXIgc2l0ZSwgcmVwbGljYXRlIDkgYW5kIGhhdmUgbm8gbWVhc3VyZW1lbnRzLiBOb3RlcyBpbmRpY2F0ZSBub3QgZm91bmQuDQoNCiMjIFF1aWNrIGxvb2sgYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdmFyaWFibGVzIGluIHRoZSBGT0cgZGF0YQ0KDQojIyMgRGlzY3JldGUgdmFyaWFibGVzDQoNCmBgYHtyIHZpc3VhbGl6ZS1udXJzZS1kaXNjcmV0ZS1kYXRhLWRpc3RyaWJ1dGlvbn0NCg0KIyBWaXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkaXNjcmV0ZSB2YXJpYWJsZXMgaW4gRk9HIGRhdGENCkRhdGFFeHBsb3Jlcjo6cGxvdF9iYXIobnVyc2VfZGF0YSAlPiUgc2VsZWN0KC1jKG5vdGUxLCBub3RlMikpLCBuY29sID0gMywgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIERpc2NyZXRlIFZhcmlhYmxlcyBpbiBOVVJTRSBEYXRhIikNCmBgYA0KDQpXZSBwcm9iYWJseSB3YW50IHRvIGNoYW5nZSBudXJzZSB1cHBlciB0byBhbGlnbiB3aXRoIHRoZSBuYW1pbmcgcHJvdG9jb2wgb2YgdGhlIGxvd2VyIHNpdGUuIEkgYWxzbyBuZWVkIHRvIGdldCByaWQgb2YgdGhlIHNwYWNlIGluIHRoZSB0cmVhdG1lbnQgdmFyaWFibGUgdmFsdWUuIE51cnNlIGsgPSBrb2E7IG51cnNlIG0gPSBtYW1hbmU7IGNvbnRyb2wgPSBjb250cm9sIGFuZCBudXJzZSA9IHVwcGVyID0gVU5TVVJFIT8hLg0KDQojIyMgQ29udGludW91cyB2YXJpYWJsZXMNCg0KYGBge3IgdmlzdWFsaXplLW51cnNlLWNvbnRpbnVvdXMtZGF0YS1kaXN0cmlidXRpb259DQoNCiMgVmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgY29udGludW91cyB2YXJpYWJsZXMgaW4gTlVSU0UgZGF0YQ0KDQpEYXRhRXhwbG9yZXI6OnBsb3RfaGlzdG9ncmFtKG51cnNlX2RhdGEgJT4lIHNlbGVjdCgtYyhub3RlMSwgbm90ZTIpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBDb250aW51b3VzIFZhcmlhYmxlcyBpbiBOVVJTRSBEYXRhIikNCmBgYA0KDQpGb3IgdGhlIGRhdGEgY2xlYW5pbmcgd2UgbmVlZCB0byBhZGRyZXNzIHRoZSAwIHZhbHVlcyBpbiB0aGUgd2lkdGggYW5kIGhlaWdodCB2YXJpYWJsZXMuIFdoaWxlIGxvb2tpbmcgYXQgdGhpcyB3ZSBoYXZlIG9wdGlvbnM6DQoNCjEuICBXZSBjYW4gcmVwbGFjZSB0aGUgMCB2YWx1ZXMgd2l0aCBOQS4NCjIuICBXZSBjYW4gcmVwbGFjZSB3aWR0aHMgYW5kIGhlaWdodHMgd2VyZSB0aGUgc3Vydml2YWwgaXMgMCB3aXRoIE5BLg0KDQpJIHRoaW5rIGZvciBub3cgd2Ugd2lsbCBnbyB3aXRoIG9wdGlvbiAxLiBXZSBjYW4gYWx3YXlzIHJldmlzaXQgdGhpcyBsYXRlciBpZiB3ZSBuZWVkIHRvLg0KDQojIERhdGEgaXNzdWVzDQoNCiMjIEZPRyBEYXRhIElzc3Vlcw0KDQojIyMgRHVwbGljYXRlcw0KDQpUaGUgZWFzaWVzdCBpc3N1ZSB0byBpZGVudGlmeSB0aGUgcHJvYmxlbSBpcyBkdXBsaWNhdGVzLiBMZXQncyBjaGVjayBmb3IgZHVwbGljYXRlcyBpbiB0aGUgRk9HIGRhdGEuDQoNCmBgYHtyIGNoZWNrLWR1cGxpY2F0ZXMtZm9nfQ0KIyBDaGVjayBmb3IgZHVwbGljYXRlcyBpbiBGT0cgZGF0YQ0KZHVwbGljYXRlc19mb2cgPC0gZm9nX2RhdGEgJT4lDQogIGphbml0b3I6OmdldF9kdXBlcyhzaXRlLCByZXBsaWNhdGUsIHRyZWF0bWVudCwgc3BlY2llcywgcGxhbnQsIHBvc2l0aW9uKQ0KDQp3cml0ZXhsOjp3cml0ZV94bHN4KGR1cGxpY2F0ZXNfZm9nLCBmaWxlLnBhdGgobm90ZXNfZGlyLCAiRk9HX2R1cGVzLnhsc3giKSkNCg0KZHVwbGljYXRlc19mb2cgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9taW5pbWFsKGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCldlIGhhdmUgODQgZHVwbGljYXRlcyBpbiB0aGUgRk9HIGRhdGEuIFdlIHdpbGwgbmVlZCB0byBhZGRyZXNzIHRoZXNlIGluIHRoZSBkYXRhIGNsZWFuaW5nIHN0ZXAuIEZvciBub3csIHdlIHdpbGwganVzdCBub3RlIHRoZW0uDQoNCiMjIyBUMSBNZWFzdXJlbWVudHMNCg0KIyMjIyBTdXJ2aXZvcnMgd2l0aCBubyBtZWFzdXJlbWVudA0KDQpMZXQncyBjaGVjayBmb3Igc3Vydml2b3JzIHRoYXQgaGF2ZSBubyBtZWFzdXJlbWVudHMuIFRoaXMgY291bGQgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggdGhlIGRhdGEgY29sbGVjdGlvbiBvciBlbnRyeS4NCg0KYGBge3Igc3Vydml2b3JzLW5vLW1lYXN1cmVtZW50LWZ4fQ0KIycgRmlsdGVyIHN1cnZpdm9ycyB3aXRoIG1pc3Npbmcgb3IgemVybyBtZWFzdXJlbWVudHMNCiMnDQojJyBUaGlzIGZ1bmN0aW9uIGZpbHRlcnMgYSBkYXRhIGZyYW1lIG9yIHRpYmJsZSB0byBpbmNsdWRlIG9ubHkgcm93cyB3aGVyZSB0aGUNCiMnIHNwZWNpZmllZCBzdXJ2aXZhbCBjb2x1bW4gZXF1YWxzIDEgYW5kIGF0IGxlYXN0IG9uZSBvZiB0aGUgc3BlY2lmaWVkIG1lYXN1cmVtZW50DQojJyBjb2x1bW5zIGlzIGVpdGhlciB6ZXJvIG9yIChvcHRpb25hbGx5KSBOQS4gSXQgcmV0dXJucyBhIHN1YnNldCBvZiBzZWxlY3RlZCBjb2x1bW5zLg0KIycNCiMnIEBwYXJhbSBkZiBBIGRhdGEgZnJhbWUgb3IgdGliYmxlIGNvbnRhaW5pbmcgdGhlIGRhdGEuDQojJyBAcGFyYW0gc3Vydml2YWxfY29sIEEgc3RyaW5nIHNwZWNpZnlpbmcgdGhlIG5hbWUgb2YgdGhlIHN1cnZpdmFsIGNvbHVtbi4NCiMnICAgIERlZmF1bHQgaXMgYCJzdXJ2aXZhbF90MSJgLg0KIycgQHBhcmFtIG1lYXN1cmVtZW50X2NvbHMgQSBjaGFyYWN0ZXIgdmVjdG9yIG9mIGNvbHVtbiBuYW1lcyB0byBjaGVjaw0KIycgICAgZm9yIHplcm8gb3IgbWlzc2luZyB2YWx1ZXMuDQojJyAgICAgICAgICAgICAgICAgICAgICAgICBEZWZhdWx0IGlzIGBjKCJodF90MSIsICJ3MV90MSIsICJ3Ml90MSIpYC4NCiMnIEBwYXJhbSBzZWxlY3RfY29scyBBIGNoYXJhY3RlciB2ZWN0b3Igb2YgY29sdW1uIG5hbWVzIHRvIGluY2x1ZGUgaW4gdGhlIG91dHB1dC4NCiMnICAgICAgICAgICAgICAgICAgICBEZWZhdWx0IGluY2x1ZGVzOg0KIycgICAgICAgICAgICAgICAgICAgICAgc2l0ZSwgcmVwbGljYXRlLCB0cmVhdG1lbnQsIHNwZWNpZXMsIHBsYW50LCBwb3NpdGlvbi4NCiMnIEBwYXJhbSB0cmVhdF9uYV9hc19taXNzaW5nIExvZ2ljYWwuIElmIGBUUlVFYCwNCiMnICAgIE5BIHZhbHVlcyBpbiBtZWFzdXJlbWVudCBjb2x1bW5zIGFyZSB0cmVhdGVkIGFzIG1pc3NpbmcNCiMnICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuZCBpbmNsdWRlZCBpbiB0aGUgZmlsdGVyLiBEZWZhdWx0IGlzIGBUUlVFYC4NCiMnDQojJyBAcmV0dXJuIEEgdGliYmxlIGNvbnRhaW5pbmcgdGhlIGZpbHRlcmVkIHJvd3MgYW5kIHNlbGVjdGVkIGNvbHVtbnMuDQojJyBAZXhhbXBsZXMNCiMnIGZpbHRlcl9zdXJ2aXZvcnNfbm9fbWVhc3VyZW1lbnQoZm9nX2RhdGEpDQojJyBmaWx0ZXJfc3Vydml2b3JzX25vX21lYXN1cmVtZW50KGZvZ19kYXRhLCB0cmVhdF9uYV9hc19taXNzaW5nID0gRkFMU0UpDQojJyBmaWx0ZXJfc3Vydml2b3JzX25vX21lYXN1cmVtZW50KGZvZ19kYXRhLA0KIycgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2aXZhbF9jb2wgPSAiYWxpdmVfdDEiLA0KIycgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlbWVudF9jb2xzID0gYygiaGVpZ2h0X3QxIiwgIndpZHRoMV90MSIsICJ3aWR0aDJfdDEiKSwNCiMnICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0X2NvbHMgPSBjKCJzaXRlIiwgInNwZWNpZXMiLCAicGxhbnQiLCAiaGVpZ2h0X3QxIiwgIndpZHRoMV90MSIsICJ3aWR0aDJfdDEiKSkNCmZpbHRlcl9zdXJ2aXZvcnNfbm9fbWVhc3VyZW1lbnQgPC0gZnVuY3Rpb24oZGYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnZpdmFsX2NvbCA9ICJzdXJ2aXZhbF90MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYXN1cmVtZW50X2NvbHMgPQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImh0X3QxIiwgIncxX3QxIiwgIncyX3QxIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdF9jb2xzID0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJzaXRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyZXBsaWNhdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRyZWF0bWVudCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3BlY2llcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGxhbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBvc2l0aW9uIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyZWF0X25hX2FzX21pc3NpbmcgPSBUUlVFKSB7DQogICMgQ2hlY2sgZm9yIHJlcXVpcmVkIGNvbHVtbnMNCiAgcmVxdWlyZWQgPC0gYyhzdXJ2aXZhbF9jb2wsIG1lYXN1cmVtZW50X2NvbHMsIHNlbGVjdF9jb2xzKQ0KICBtaXNzaW5nIDwtIHNldGRpZmYocmVxdWlyZWQsIG5hbWVzKGRmKSkNCiAgaWYgKGxlbmd0aChtaXNzaW5nKSA+IDApIHsNCiAgICBzdG9wKCJNaXNzaW5nIHJlcXVpcmVkIGNvbHVtbnM6ICIsIHBhc3RlKG1pc3NpbmcsIGNvbGxhcHNlID0gIiwgIikpDQogIH0NCiAgDQogICMgQnVpbGQgZHluYW1pYyBmaWx0ZXIgZXhwcmVzc2lvbg0KICB6ZXJvX29yX25hX2V4cHJzIDwtIGxhcHBseShtZWFzdXJlbWVudF9jb2xzLCBmdW5jdGlvbihjb2wpIHsNCiAgICBpZiAodHJlYXRfbmFfYXNfbWlzc2luZykgew0KICAgICAgcmxhbmc6OmV4cHIoKCEhcmxhbmc6OnN5bShjb2wpID09IDApIHwgaXMubmEoISFybGFuZzo6c3ltKGNvbCkpKQ0KICAgIH0gZWxzZSB7DQogICAgICBybGFuZzo6ZXhwcighIXJsYW5nOjpzeW0oY29sKSA9PSAwKQ0KICAgIH0NCiAgfSkNCiAgDQogIGNvbWJpbmVkX21lYXN1cmVtZW50X2V4cHIgPC0gcHVycnI6OnJlZHVjZSh6ZXJvX29yX25hX2V4cHJzLCBmdW5jdGlvbih4LCB5KQ0KICAgIHJsYW5nOjpleHByKCEheCB8ICEheSkpDQogIA0KICBmdWxsX2ZpbHRlcl9leHByIDwtIHJsYW5nOjpleHByKCEhcmxhbmc6OnN5bShzdXJ2aXZhbF9jb2wpID09IDEgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKCEhY29tYmluZWRfbWVhc3VyZW1lbnRfZXhwcikpDQogIA0KICBkZiAlPiUNCiAgICBmaWx0ZXIoISFmdWxsX2ZpbHRlcl9leHByKSAlPiUNCiAgICBzZWxlY3QoYWxsX29mKHNlbGVjdF9jb2xzKSkNCn0NCmBgYA0KDQpgYGB7ciBzdXJ2aXZvcnMtdDEtbm8tbWVhc3VyZW1lbnQtZm9nfQ0KDQojIENoZWNrIGZvciBzdXJ2aXZvcnMgYXQgdDEgd2l0aCBubyBtZWFzdXJlbWVudHMgaW4gRk9HIGRhdGENCmZpbHRlcl9zdXJ2aXZvcnNfbm9fbWVhc3VyZW1lbnQoDQogIGZvZ19kYXRhLA0KICBzdXJ2aXZhbF9jb2wgPSAic3Vydml2YWxfdDEiLA0KICBtZWFzdXJlbWVudF9jb2xzID0gYygiaHRfdDEiLCAidzFfdDEiLCAidzJfdDEiKSwNCiAgc2VsZWN0X2NvbHMgPSBjKCJzaXRlIiwgInJlcGxpY2F0ZSIsICJ0cmVhdG1lbnQiLA0KICAgICAgICAgICAgICAgICAgInNwZWNpZXMiLCAicGxhbnQiLCAicG9zaXRpb24iLA0KICAgICAgICAgICAgICAgICAgImh0X3QxIiwgIncxX3QxIiwgIncyX3QxIikpICU+JQ0KICBrYmwoKSAlPiUNCiAga2FibGVfbWluaW1hbChmdWxsX3dpZHRoID0gRikNCmBgYA0KDQpKdXN0IDMsIHdlIGNhbiBhZGRyZXNzIHRoZXNlIGluIHRoZSBkYXRhIGNsZWFuaW5nIHN0ZXAuIEJ1dCB3ZSBuZWVkIHRvIGNob29zZSBob3cgd2Ugd2FudCB0byBoYW5kbGUgdGhlc2UuIFdlIGNhbiBlaXRoZXIgcmVtb3ZlIHRoZW0gb3IgcmVwbGFjZSB0aGUgMCB2YWx1ZXMgd2l0aCBOQSBvciByZXBsYWNlIGFsbCB0aGUgbWVhc3VyZW1lbnRzIHdpdGggTkEuIEZvciBub3csIHdlIHdpbGwganVzdCBub3RlIHRoZW0uDQoNCioqbG93ZXItNC02My1hYWxpaS1tYXVrYS1wbGFudDEqKjogTm90ZSBzYXlzIGp1c3QgZGllZC4gSSB2b3RlIHdlIGNvZGUgc3Vydml2YWwgYXMgMCBhbmQgbWVhc3VyZW1lbnRzIGFzIE5BLg0KDQoqKnVwcGVyLTUtNDAtYXdlb3dlby1wbGFudDEqKjogTm90ZSBzYXlzIGVhdGVuLiBObyBsZWF2ZXMuIEkgdm90ZSB3ZSBjb2RlIGFzIDAgYW5kIG1lYXN1cmVtZW50cyBhcyBOQS4gSXQgaXMgZGVhZCBhdCB0aGUgbmV4dCB0aW1lIHN0ZXAuDQoNCioqdXBwZXItNC02My1wYXdhbGUtcGxhbnQxKio6IE5vdGUgc2F5cyBub3QgZm91bmQuIEl0IHdhcyBmb3VuZCBhdCB0aW1lIHN0ZXAgMi4gSSB2b3RlIEkgTkEgdGhlIG1lYXN1cmVtZW50cyBidXQgY29kZSBhcyAxLiBJJ2xsIERyb3AuTkEgYmVmb3JlIGFuYWx5c2lzIHNvIGl0IHdvbid0IGJlIGluY2x1ZGVkIGluIGFueSBhbmFseXNlcyB3aXRoIG1lYXN1cmVtZW50cy4NCg0KIyMjIyBEaWVkIGJ1dCB3aXRoIG1lYXN1cmVtZW50cw0KDQpMZXQncyBjaGVjayBmb3IgcGxhbnRzIHRoYXQgZGllZCBidXQgaGF2ZSBtZWFzdXJlbWVudHMuIFRoaXMgY291bGQgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggdGhlIGRhdGEgY29sbGVjdGlvbiBvciBlbnRyeSBvciBjb3VsZCBiZSBhIHZhbGlkIG9ic2VydmF0aW9uIGlmIGRlYWQgcGxhbnRzIHdlcmUgbWVhc3VyZWQuDQoNCmBgYHtyIGRpZWQtd2l0aC1tZWFzdXJlbWVudC1meH0NCg0KIycgRmlsdGVyIG5vbi1zdXJ2aXZvcnMgd2l0aCBub24temVybyBtZWFzdXJlbWVudHMNCiMnDQojJyBUaGlzIGZ1bmN0aW9uIGZpbHRlcnMgYSBkYXRhIGZyYW1lIG9yIHRpYmJsZSB0byBpbmNsdWRlIG9ubHkgcm93cyB3aGVyZSB0aGUNCiMnIHNwZWNpZmllZCBzdXJ2aXZhbCBjb2x1bW4gZXF1YWxzIDAgYW5kIGF0IGxlYXN0IG9uZSBvZiB0aGUgc3BlY2lmaWVkIG1lYXN1cmVtZW50DQojJyBjb2x1bW5zIGlzIGdyZWF0ZXIgdGhhbiB6ZXJvIG9yIChvcHRpb25hbGx5KSBub3QgTkEuIEl0IHJldHVybnMgYSBzdWJzZXQgb2Ygc2VsZWN0ZWQgY29sdW1ucy4NCiMnDQojJyBAcGFyYW0gZGYgQSBkYXRhIGZyYW1lIG9yIHRpYmJsZSBjb250YWluaW5nIHRoZSBkYXRhLg0KIycgQHBhcmFtIHN1cnZpdmFsX2NvbCBBIHN0cmluZyBzcGVjaWZ5aW5nIHRoZSBuYW1lIG9mIHRoZSBzdXJ2aXZhbCBjb2x1bW4uIERlZmF1bHQgaXMgYCJzdXJ2aXZhbF90MSJgLg0KIycgQHBhcmFtIG1lYXN1cmVtZW50X2NvbHMgQSBjaGFyYWN0ZXIgdmVjdG9yIG9mIGNvbHVtbiBuYW1lcyB0byBjaGVjayBmb3Igbm9uLXplcm8gb3Igbm9uLW1pc3NpbmcgdmFsdWVzLg0KIycgICAgICAgICAgICAgICAgICAgICAgICAgRGVmYXVsdCBpcyBgYygiaHRfdDEiLCAidzFfdDEiLCAidzJfdDEiKWAuDQojJyBAcGFyYW0gc2VsZWN0X2NvbHMgQSBjaGFyYWN0ZXIgdmVjdG9yIG9mIGNvbHVtbiBuYW1lcyB0byBpbmNsdWRlIGluIHRoZSBvdXRwdXQuDQojJyAgICAgICAgICAgICAgICAgICAgRGVmYXVsdCBpbmNsdWRlcyBzaXRlLCByZXBsaWNhdGUsIHRyZWF0bWVudCwgc3BlY2llcywgcGxhbnQsIHBvc2l0aW9uLCBhbmQgbWVhc3VyZW1lbnRzLg0KIycgQHBhcmFtIHRyZWF0X25hX2FzX3ByZXNlbnQgTG9naWNhbC4gSWYgYFRSVUVgLCBOQSB2YWx1ZXMgaW4gbWVhc3VyZW1lbnQgY29sdW1ucyBhcmUgdHJlYXRlZCBhcyBwcmVzZW50DQojJyAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmQgaW5jbHVkZWQgaW4gdGhlIGZpbHRlci4gRGVmYXVsdCBpcyBgRkFMU0VgLg0KIycNCiMnIEByZXR1cm4gQSB0aWJibGUgY29udGFpbmluZyB0aGUgZmlsdGVyZWQgcm93cyBhbmQgc2VsZWN0ZWQgY29sdW1ucy4NCiMnIEBleGFtcGxlcw0KIycgZmlsdGVyX25vbl9zdXJ2aXZvcnNfd2l0aF9tZWFzdXJlbWVudChmb2dfZGF0YSkNCiMnIGZpbHRlcl9ub25fc3Vydml2b3JzX3dpdGhfbWVhc3VyZW1lbnQoZm9nX2RhdGEsIHRyZWF0X25hX2FzX3ByZXNlbnQgPSBUUlVFKQ0KIycgZmlsdGVyX25vbl9zdXJ2aXZvcnNfd2l0aF9tZWFzdXJlbWVudChmb2dfZGF0YSwNCiMnICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vydml2YWxfY29sID0gImFsaXZlX3QxIiwNCiMnICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhc3VyZW1lbnRfY29scyA9IGMoImhlaWdodF90MSIsICJ3aWR0aDFfdDEiLCAid2lkdGgyX3QxIiksDQojJyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdF9jb2xzID0gYygic2l0ZSIsICJzcGVjaWVzIiwgInBsYW50IiwgImhlaWdodF90MSIsICJ3aWR0aDFfdDEiLCAid2lkdGgyX3QxIikpDQpmaWx0ZXJfbm9uX3N1cnZpdm9yc193aXRoX21lYXN1cmVtZW50IDwtIGZ1bmN0aW9uKGRmLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdXJ2aXZhbF9jb2wgPSAic3Vydml2YWxfdDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFzdXJlbWVudF9jb2xzID0gYygiaHRfdDEiLCAidzFfdDEiLCAidzJfdDEiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0X2NvbHMgPSBjKCJzaXRlIiwgInJlcGxpY2F0ZSIsICJ0cmVhdG1lbnQiLCAic3BlY2llcyIsICJwbGFudCIsICJwb3NpdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaHRfdDEiLCAidzFfdDEiLCAidzJfdDEiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJlYXRfbmFfYXNfcHJlc2VudCA9IEZBTFNFKSB7DQogICMgQ2hlY2sgZm9yIHJlcXVpcmVkIGNvbHVtbnMNCiAgcmVxdWlyZWQgPC0gYyhzdXJ2aXZhbF9jb2wsIG1lYXN1cmVtZW50X2NvbHMsIHNlbGVjdF9jb2xzKQ0KICBtaXNzaW5nIDwtIHNldGRpZmYocmVxdWlyZWQsIG5hbWVzKGRmKSkNCiAgaWYgKGxlbmd0aChtaXNzaW5nKSA+IDApIHsNCiAgICBzdG9wKCJNaXNzaW5nIHJlcXVpcmVkIGNvbHVtbnM6ICIsIHBhc3RlKG1pc3NpbmcsIGNvbGxhcHNlID0gIiwgIikpDQogIH0NCg0KICAjIEJ1aWxkIGR5bmFtaWMgZmlsdGVyIGV4cHJlc3Npb24NCiAgcHJlc2VudF9leHBycyA8LSBsYXBwbHkobWVhc3VyZW1lbnRfY29scywgZnVuY3Rpb24oY29sKSB7DQogICAgaWYgKHRyZWF0X25hX2FzX3ByZXNlbnQpIHsNCiAgICAgIHJsYW5nOjpleHByKCghIXJsYW5nOjpzeW0oY29sKSA+IDApIHwgaXMubmEoISFybGFuZzo6c3ltKGNvbCkpKQ0KICAgIH0gZWxzZSB7DQogICAgICBybGFuZzo6ZXhwcighIXJsYW5nOjpzeW0oY29sKSA+IDApDQogICAgfQ0KICB9KQ0KDQogIGNvbWJpbmVkX21lYXN1cmVtZW50X2V4cHIgPC0gcHVycnI6OnJlZHVjZShwcmVzZW50X2V4cHJzLCBmdW5jdGlvbih4LCB5KSBybGFuZzo6ZXhwcighIXggfCAhIXkpKQ0KDQogIGZ1bGxfZmlsdGVyX2V4cHIgPC0gcmxhbmc6OmV4cHIoDQogICAgISFybGFuZzo6c3ltKHN1cnZpdmFsX2NvbCkgPT0gMCAmICghIWNvbWJpbmVkX21lYXN1cmVtZW50X2V4cHIpDQogICkNCg0KICBkZiAlPiUNCiAgICBmaWx0ZXIoISFmdWxsX2ZpbHRlcl9leHByKSAlPiUNCiAgICBzZWxlY3QoYWxsX29mKHNlbGVjdF9jb2xzKSkNCn0NCmBgYA0KDQpgYGB7ciBkaWVkLXQxLXdpdGgtbWVhc3VyZW1lbnQtZm9nfQ0KIyBDaGVjayBmb3IgcGxhbnRzIHRoYXQgZGllZCBidXQgaGF2ZSBtZWFzdXJlbWVudHMgaW4gRk9HIGRhdGENCmZpbHRlcl9ub25fc3Vydml2b3JzX3dpdGhfbWVhc3VyZW1lbnQoDQogIGZvZ19kYXRhLA0KICBzdXJ2aXZhbF9jb2wgPSAic3Vydml2YWxfdDEiLA0KICBtZWFzdXJlbWVudF9jb2xzID0gYygiaHRfdDEiLCAidzFfdDEiLCAidzJfdDEiKSwNCiAgc2VsZWN0X2NvbHMgPSBjKCJzaXRlIiwgInJlcGxpY2F0ZSIsICJ0cmVhdG1lbnQiLA0KICAgICAgICAgICAgICAgICAgInNwZWNpZXMiLCAicGxhbnQiLCAicG9zaXRpb24iLA0KICAgICAgICAgICAgICAgICAgImh0X3QxIiwgIncxX3QxIiwgIncyX3QxIikpICU+JQ0KICBrYmwoKSAlPiUNCiAga2FibGVfbWluaW1hbChmdWxsX3dpZHRoID0gRikNCmBgYA0KDQpTbyA2IG9ic2VydmF0aW9ucyB3aXRoIDIgZHVwbGljYXRlIG9ic2VydmF0aW9ucy4gV2UgY2FuIGFkZHJlc3MgdGhlc2UgaW4gdGhlIGRhdGEgY2xlYW5pbmcgc3RlcC4gQnV0IHdlIG5lZWQgdG8gY2hvb3NlIGhvdyB3ZSB3YW50IHRvIGhhbmRsZSB0aGVzZS4gV2UgY2FuIGVpdGhlciByZW1vdmUgdGhlbSBvciByZXBsYWNlIHRoZSAwIHZhbHVlcyB3aXRoIE5BLCByZXBsYWNlIGFsbCB0aGUgbWVhc3VyZW1lbnRzIHdpdGggTkEgb3IgbGVhdmUgdGhlbSBidXQgdGhlbiBob3cgZG8gd2Ugd2FudCB0byBkZWFsIHdpdGggdGhlbSBpbiB0aGUgYW5hbHlzaXMuIEZvciBub3csIHdlIHdpbGwganVzdCBub3RlIHRoZW0uIFwjIyMgVDIgTWVhc3VyZW1lbnRzDQoNCioqbG93ZXItMi0wLWFhbGlpLW1hdWthLXAxOioqIEJvdGggZW50cmllcyBhcmUgcmVjb3JkZWQgYXMganVzdCBkaWVkIGluIG5vdGVzLiBJIHZvdGUgdGhleSBiZSBjb2RlZCBhcyAwIHdpdGggbWVhc3VyZW1lbnRzIE5BLiBXZSBjYW4ga2VlcCB0aGUgZHVwbGljYXRlIHJlY29yZHMgaWYgd2UgY2FuIGNvbmZpcm0gdGhhdCB0aGVyZSBhcmUgdHdvIHBsYW50cyBhbmQgdGhhdCBwbGFudDIgd2FzIGp1c3QgbWlzcyBlbnRlcmVkLiBPciB3ZSBjYW4gZGVsZXRlIG9uZSBvZiB0aGVtIGlmIHdlIGFyZSB1bnN1cmUuDQoNCioqbG93ZXItNS0wLWFhbGlpLW1hdWthLXAxOioqIFJlY29yZGVkIGFzIGp1c3QgZGllZCBpbiBub3Rlcy4gSSB2b3RlIGNvZGUgYXMgMCB3aXRoIG1lYXN1cmVtZW50cywgTkEuDQoNCioqbG93ZXItNS00MC1hYWxpaS1tYXVrYS1wMToqKiBCb3RoLCBlbnRyaWVzIGFyZSByZWNvcmRlZCBhcyBqdXN0IGRpZWQgaW4gbm90ZXMuIEkgdm90ZSBjb2RlIGFzIDAgd2l0aCBtZWFzdXJlbWVudHMsIE5BLiBXZSBjYW4ga2VlcCB0aGUgZHVwbGljYXRlIHJlY29yZHMgaWYgd2UgY2FuIGNvbmZpcm0gdGhhdCB0aGVyZSBhcmUgdHdvIHBsYW50cyBhbmQgdGhhdCBwbGFudDIgd2FzIGp1c3QgbWlzcyBlbnRlcmVkLiBPciB3ZSBjYW4gZGVsZXRlIG9uZSBvZiB0aGVtIGlmIHdlIGFyZSB1bnN1cmUuDQoNCioqbG93ZXItNS0xMDAtYWFsaWktY29udHJvbC1wMToqKiBSZWNvcmRlZCBhcyBqdXN0IGRpZWQuIEkgdm90ZSBjb2RlZCBhcyAwIHdpdGggbWVhc3VyZW1lbnRzLCBOQS4NCg0KIyMjIyBTdXJ2aXZvcnMgd2l0aCBubyBtZWFzdXJlbWVudA0KDQpgYGB7ciBzdXJ2aXZvcnMtdDItbm8tbWVhc3VyZW1lbnQtZm9nfQ0KDQojIENoZWNrIGZvciBzdXJ2aXZvcnMgYXQgdDIgd2l0aCBubyBtZWFzdXJlbWVudHMgaW4gRk9HIGRhdGENCmZpbHRlcl9zdXJ2aXZvcnNfbm9fbWVhc3VyZW1lbnQoDQogIGZvZ19kYXRhLA0KICBzdXJ2aXZhbF9jb2wgPSAic3Vydml2YWxfdDIiLA0KICBtZWFzdXJlbWVudF9jb2xzID0gYygiaHRfdDIiLCAidzFfdDIiLCAidzJfdDIiKSwNCiAgc2VsZWN0X2NvbHMgPSBjKCJzaXRlIiwgInNwZWNpZXMiLCAicGxhbnQiLCAiaHRfdDIiLCAidzFfdDIiLCAidzJfdDIiKSkgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9taW5pbWFsKGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCk5vbmUhIHRoYXQncyBncmVhdCEgV2UgY2FuIG1vdmUgb24gdG8gdGhlIG5leHQgc3RlcC4NCg0KIyMjIyBEaWVkIGJ1dCB3aXRoIG1lYXN1cmVtZW50cw0KDQpMZXQncyBjaGVjayBmb3IgcGxhbnRzIHRoYXQgZGllZCBidXQgaGF2ZSBtZWFzdXJlbWVudHMuIFRoaXMgY291bGQgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggdGhlIGRhdGEgY29sbGVjdGlvbiBvciBlbnRyeSBvciBjb3VsZCBiZSBhIHZhbGlkIG9ic2VydmF0aW9uIGlmIGRlYWQgcGxhbnRzIHdlcmUgbWVhc3VyZWQuDQoNCmBgYHtyIGRpZWQtdDItd2l0aC1tZWFzdXJlbWVudC1mb2d9DQojIENoZWNrIGZvciBwbGFudHMgdGhhdCBkaWVkIGJ1dCBoYXZlIG1lYXN1cmVtZW50cyBpbiBGT0cgZGF0YQ0KIyBDaGVjayBmb3IgcGxhbnRzIHRoYXQgZGllZCBidXQgaGF2ZSBtZWFzdXJlbWVudHMgaW4gRk9HIGRhdGENCmZpbHRlcl9ub25fc3Vydml2b3JzX3dpdGhfbWVhc3VyZW1lbnQoDQogIGZvZ19kYXRhLA0KICBzdXJ2aXZhbF9jb2wgPSAic3Vydml2YWxfdDIiLA0KICBtZWFzdXJlbWVudF9jb2xzID0gYygiaHRfdDIiLCAidzFfdDIiLCAidzJfdDIiKSwNCiAgc2VsZWN0X2NvbHMgPSBjKCJzaXRlIiwgInJlcGxpY2F0ZSIsICJ0cmVhdG1lbnQiLA0KICAgICAgICAgICAgICAgICAgInNwZWNpZXMiLCAicGxhbnQiLCAicG9zaXRpb24iLA0KICAgICAgICAgICAgICAgICAgImh0X3QyIiwgIncxX3QyIiwgIncyX3QyIikpICU+JQ0KICBrYmwoKSAlPiUNCiAga2FibGVfbWluaW1hbChmdWxsX3dpZHRoID0gRikNCmBgYA0KDQpOb25lISB0aGF0J3MgZ3JlYXQhIFdlIGNhbiBtb3ZlIG9uIHRvIHRoZSBuZXh0IHN0ZXAuDQoNCiMjIyBMYXp1cnVzIFBsYW50cw0KDQpMZXQncyBjaGVjayBmb3IgcGxhbnRzIHRoYXQgd2VyZSBkZWFkIGluIFQxIGJ1dCB3ZXJlIGFsaXZlIGluIFQyLiBUaGlzIGNvdWxkIGluZGljYXRlIGEgcHJvYmxlbSB3aXRoIHRoZSBkYXRhIGNvbGxlY3Rpb24gb3IgZW50cnkgb3IgY291bGQgYmUgYSB2YWxpZCBvYnNlcnZhdGlvbiBpZiBwbGFudHMgd2VyZSBhZGRlZCBiZXR3ZWVuIFQxIGFuZCBUMi4NCg0KYGBge3IgbGF6YXJ1cy1wbGFudHMtZm9nfQ0KIyBDaGVjayBmb3IgbGF6YXJ1cyBwbGFudHMgaW4gRk9HIGRhdGENCmxhemFydXNfcGxhbnRzX2ZvZyA8LSBmb2dfZGF0YSAlPiUNCiAgZmlsdGVyKHN1cnZpdmFsX3QxID09IDAgJiBzdXJ2aXZhbF90MiA9PSAxKSAlPiUNCiAgc2VsZWN0KHNpdGUsIHJlcGxpY2F0ZSwgdHJlYXRtZW50LCBzcGVjaWVzLCBwbGFudCwgcG9zaXRpb24pDQoNCmxhemFydXNfcGxhbnRzX2ZvZyAlPiUNCiAga2JsKCkgJT4lDQogIGthYmxlX21pbmltYWwoZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KVGhleSBoYXZlIE5PVCByaXNlbiEhIFdlIGhhdmUgbm8gbGF6YXJ1cyBwbGFudHMgaW4gdGhlIEZPRyBkYXRhLiBUaGF0J3MgZ3JlYXQhIFdlIGNhbiBtb3ZlIG9uIHRvIHRoZSBuZXh0IHN0ZXAuDQoNCiMjIE5VUlNFIERhdGEgSXNzdWVzDQoNCiMjIyBEdXBsaWNhdGVzDQoNClRoZSBlYXNpZXN0IGlzc3VlIHRvIGlkZW50aWZ5IHByb2JsZW0gaXMgZHVwbGljYXRlcy4gTGV0J3MgY2hlY2sgZm9yIGR1cGxpY2F0ZXMgaW4gdGhlIEZPRyBkYXRhLg0KDQpgYGB7ciBjaGVjay1kdXBsaWNhdGVzLU5VUlNFfQ0KIyBDaGVjayBmb3IgZHVwbGljYXRlcyBpbiBGT0cgZGF0YQ0KbnVyc2VfZHVwbGljYXRlcyA8LSBudXJzZV9kYXRhICU+JQ0KICBqYW5pdG9yOjpnZXRfZHVwZXMoc2l0ZSwgcmVwbGljYXRlLCB0cmVhdG1lbnQsIHNwZWNpZXMsIHBsYW50KQ0KDQp3cml0ZXhsOjp3cml0ZV94bHN4KG51cnNlX2R1cGxpY2F0ZXMsIGZpbGUucGF0aChub3Rlc19kaXIsICJOVVJTRV9kdXBlcy54bHN4IikpDQoNCm51cnNlX2R1cGxpY2F0ZXMgJT4lIA0KICBrYmwoKSAlPiUNCiAga2FibGVfbWluaW1hbChmdWxsX3dpZHRoID0gRikNCmBgYA0KDQpXZSBoYXZlIDQ0IGR1cGxpY2F0ZXMgaW4gdGhlIE5VUlNFIGRhdGEuIFdlIHdpbGwgbmVlZCB0byBhZGRyZXNzIHRoZXNlIGluIHRoZSBkYXRhIGNsZWFuaW5nIHN0ZXAuIEZvciBub3csIHdlIHdpbGwganVzdCBub3RlIHRoZW0uDQoNCiMjIyBUMSBNZWFzdXJlbWVudHMNCg0KIyMjIyBTdXJ2aXZvcnMgd2l0aCBubyBtZWFzdXJlbWVudA0KDQpMZXQncyBjaGVjayBmb3Igc3Vydml2b3JzIHRoYXQgaGF2ZSBubyBtZWFzdXJlbWVudHMuIFRoaXMgY291bGQgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggdGhlIGRhdGEgY29sbGVjdGlvbiBvciBlbnRyeS4NCg0KYGBge3Igc3Vydml2b3JzLXQxLW5vLW1lYXN1cmVtZW50LU5VUlNFfQ0KDQojIENoZWNrIGZvciBzdXJ2aXZvcnMgYXQgdDEgd2l0aCBubyBtZWFzdXJlbWVudHMgaW4gRk9HIGRhdGENCmZpbHRlcl9zdXJ2aXZvcnNfbm9fbWVhc3VyZW1lbnQoDQogIG51cnNlX2RhdGEsDQogIHN1cnZpdmFsX2NvbCA9ICJzdXJ2aXZhbF90MSIsDQogIG1lYXN1cmVtZW50X2NvbHMgPSBjKCJodF90MSIsICJ3MV90MSIsICJ3Ml90MSIpLA0KICBzZWxlY3RfY29scyA9IGMoInNpdGUiLCAicmVwbGljYXRlIiwgInRyZWF0bWVudCIsICJzcGVjaWVzIiwgInBsYW50IiwNCiAgICAgICAgICAgICAgICAgICJodF90MSIsICJ3MV90MSIsICJ3Ml90MSIpKSAlPiUNCiAga2JsKCkgJT4lDQogIGthYmxlX21pbmltYWwoZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KTm8gTm90ZXMgZm9yIHRoaXMgcmVjb3JkIGF0IFQxLiBBdCBUMiwgZWF0ZW4gbm8gZm9saWFnZS4gbWVhc3VyZWQgc3RlbS4NCg0KIyMjIyBEaWVkIGJ1dCB3aXRoIG1lYXN1cmVtZW50cw0KDQpMZXQncyBjaGVjayBmb3IgcGxhbnRzIHRoYXQgZGllZCBidXQgaGF2ZSBtZWFzdXJlbWVudHMuIFRoaXMgY291bGQgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggdGhlIGRhdGEgY29sbGVjdGlvbiBvciBlbnRyeSBvciBjb3VsZCBiZSBhIHZhbGlkIG9ic2VydmF0aW9uIGlmIGRlYWQgcGxhbnRzIHdlcmUgbWVhc3VyZWQuDQoNCmBgYHtyIGRpZWQtdDEtd2l0aC1tZWFzdXJlbWVudC1OVVJTRX0NCiMgQ2hlY2sgZm9yIHBsYW50cyB0aGF0IGRpZWQgYnV0IGhhdmUgbWVhc3VyZW1lbnRzIGluIEZPRyBkYXRhDQpmaWx0ZXJfbm9uX3N1cnZpdm9yc193aXRoX21lYXN1cmVtZW50KA0KICBudXJzZV9kYXRhLA0KICBzdXJ2aXZhbF9jb2wgPSAic3Vydml2YWxfdDEiLA0KICBtZWFzdXJlbWVudF9jb2xzID0gYygiaHRfdDEiLCAidzFfdDEiLCAidzJfdDEiKSwNCiAgc2VsZWN0X2NvbHMgPSBjKCJzaXRlIiwgInJlcGxpY2F0ZSIsICJ0cmVhdG1lbnQiLCAic3BlY2llcyIsICJwbGFudCIsDQogICAgICAgICAgICAgICAgICAiaHRfdDEiLCAidzFfdDEiLCAidzJfdDEiLCAic3Vydml2YWxfdDEiKSkgJT4lDQogIGFycmFuZ2Uoc2l0ZSwgcmVwbGljYXRlLCB0cmVhdG1lbnQsIHNwZWNpZXMsIHBsYW50KSAlPiUNCiAga2JsKCkgJT4lDQogIGthYmxlX21pbmltYWwoZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KUXVpdGUgYSBmZXcuIFdlIGNhbiBhZGRyZXNzIHRoZXNlIGluIHRoZSBkYXRhIGNsZWFuaW5nIHN0ZXAuIEJ1dCB3ZSBuZWVkIHRvIGNob29zZSBob3cgd2Ugd2FudCB0byBoYW5kbGUgdGhlc2UuIFdlIGNhbiBlaXRoZXIgcmVtb3ZlIHRoZW0gb3IgcmVwbGFjZSB0aGUgMCB2YWx1ZXMgd2l0aCBOQSwgcmVwbGFjZSBhbGwgdGhlIG1lYXN1cmVtZW50cyB3aXRoIE5BIG9yIGxlYXZlIHRoZW0gYnV0IHRoZW4gaG93IGRvIHdlIHdhbnQgdG8gZGVhbCB3aXRoIHRoZW0gaW4gdGhlIGFuYWx5c2lzLiBGb3Igbm93LCB3ZSB3aWxsIGp1c3Qgbm90ZSB0aGVtLiBcIyMjIFQyIE1lYXN1cmVtZW50cw0KDQoqKnVwcGVyLTEtY29udHJvbC1hYWxpaS0yOioqIE5vIG5vdGVzLg0KDQojIyMjIFN1cnZpdm9ycyB3aXRoIG5vIG1lYXN1cmVtZW50DQoNCmBgYHtyIHN1cnZpdm9ycy10Mi1uby1tZWFzdXJlbWVudC1OVVJTRX0NCg0KIyBDaGVjayBmb3Igc3Vydml2b3JzIGF0IHQyIHdpdGggbm8gbWVhc3VyZW1lbnRzIGluIEZPRyBkYXRhDQpmaWx0ZXJfc3Vydml2b3JzX25vX21lYXN1cmVtZW50KA0KICBudXJzZV9kYXRhLA0KICBzdXJ2aXZhbF9jb2wgPSAic3Vydml2YWxfdDIiLA0KICBtZWFzdXJlbWVudF9jb2xzID0gYygiaHRfdDIiLCAidzFfdDIiLCAidzJfdDIiKSwNCiAgc2VsZWN0X2NvbHMgPSBjKCJzaXRlIiwgInJlcGxpY2F0ZSIsICJ0cmVhdG1lbnQiLCAic3BlY2llcyIsICJwbGFudCIsDQogICAgICAgICAgICAgICAgICAiaHRfdDIiLCAidzFfdDIiLCAidzJfdDIiKSkgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9taW5pbWFsKGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCk5vbmUhIHRoYXQncyBncmVhdCEgV2UgY2FuIG1vdmUgb24gdG8gdGhlIG5leHQgc3RlcC4NCg0KIyMjIyBEaWVkIGJ1dCB3aXRoIG1lYXN1cmVtZW50cw0KDQpMZXQncyBjaGVjayBmb3IgcGxhbnRzIHRoYXQgZGllZCBidXQgaGF2ZSBtZWFzdXJlbWVudHMuIFRoaXMgY291bGQgaW5kaWNhdGUgYSBwcm9ibGVtIHdpdGggdGhlIGRhdGEgY29sbGVjdGlvbiBvciBlbnRyeSBvciBjb3VsZCBiZSBhIHZhbGlkIG9ic2VydmF0aW9uIGlmIGRlYWQgcGxhbnRzIHdlcmUgbWVhc3VyZWQuDQoNCmBgYHtyIGRpZWQtdDItd2l0aC1tZWFzdXJlbWVudC1OVVJTRX0NCiMgQ2hlY2sgZm9yIHBsYW50cyB0aGF0IGRpZWQgYnV0IGhhdmUgbWVhc3VyZW1lbnRzIGluIEZPRyBkYXRhDQojIENoZWNrIGZvciBwbGFudHMgdGhhdCBkaWVkIGJ1dCBoYXZlIG1lYXN1cmVtZW50cyBpbiBGT0cgZGF0YQ0KZmlsdGVyX25vbl9zdXJ2aXZvcnNfd2l0aF9tZWFzdXJlbWVudCgNCiAgbnVyc2VfZGF0YSwNCiAgc3Vydml2YWxfY29sID0gInN1cnZpdmFsX3QyIiwNCiAgbWVhc3VyZW1lbnRfY29scyA9IGMoImh0X3QyIiwgIncxX3QyIiwgIncyX3QyIiksDQogIHNlbGVjdF9jb2xzID0gYygic2l0ZSIsICJyZXBsaWNhdGUiLCAidHJlYXRtZW50IiwgInNwZWNpZXMiLCAicGxhbnQiLA0KICAgICAgICAgICAgICAgICAgImh0X3QyIiwgIncxX3QyIiwgIncyX3QyIiwgInN1cnZpdmFsX3QyIikpICU+JQ0KICBhcnJhbmdlKHNpdGUsIHJlcGxpY2F0ZSwgdHJlYXRtZW50LCBzcGVjaWVzLCBwbGFudCkgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9taW5pbWFsKGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNClF1aXRlIGEgZmV3LiBXZSBjYW4gYWRkcmVzcyB0aGVzZSBpbiB0aGUgZGF0YSBjbGVhbmluZyBzdGVwLiBCdXQgd2UgbmVlZCB0byBjaG9vc2UgaG93IHdlIHdhbnQgdG8gaGFuZGxlIHRoZXNlLiBXZSBjYW4gZWl0aGVyIHJlbW92ZSB0aGVtIG9yIHJlcGxhY2UgdGhlIDAgdmFsdWVzIHdpdGggTkEsIHJlcGxhY2UgYWxsIHRoZSBtZWFzdXJlbWVudHMgd2l0aCBOQSBvciBsZWF2ZSB0aGVtIGJ1dCB0aGVuIGhvdyBkbyB3ZSB3YW50IHRvIGRlYWwgd2l0aCB0aGVtIGluIHRoZSBhbmFseXNpcy4gRm9yIG5vdywgd2Ugd2lsbCBqdXN0IG5vdGUgdGhlbS4NCg0KKipsb3dlci02LW51cnNlIG0tYXdlb3dlby1wbGFudDE6KiogTm8gbm90ZXMuDQoNCiMjIyBMYXp1cnVzIFBsYW50cw0KDQpMZXQncyBjaGVjayBmb3IgcGxhbnRzIHRoYXQgd2VyZSBkZWFkIGluIFQxIGJ1dCB3ZXJlIGFsaXZlIGluIFQyLiBUaGlzIGNvdWxkIGluZGljYXRlIGEgcHJvYmxlbSB3aXRoIHRoZSBkYXRhIGNvbGxlY3Rpb24gb3IgZW50cnkgb3IgY291bGQgYmUgYSB2YWxpZCBvYnNlcnZhdGlvbiBpZiBwbGFudHMgd2VyZSBhZGRlZCBiZXR3ZWVuIFQxIGFuZCBUMi4NCg0KYGBge3IgbGF6YXJ1cy1wbGFudHMtTlVSU0V9DQojIENoZWNrIGZvciBsYXphcnVzIHBsYW50cyBpbiBGT0cgZGF0YQ0KbGF6YXJ1c19wbGFudHNfZm9nIDwtIG51cnNlX2RhdGEgJT4lDQogIGZpbHRlcihzdXJ2aXZhbF90MSA9PSAwICYgc3Vydml2YWxfdDIgPT0gMSkgJT4lDQogIHNlbGVjdCgic2l0ZSIsICJyZXBsaWNhdGUiLCAidHJlYXRtZW50IiwgInNwZWNpZXMiLCAicGxhbnQiKQ0KDQpsYXphcnVzX3BsYW50c19mb2cgJT4lDQogIGtibCgpICU+JQ0KICBrYWJsZV9taW5pbWFsKGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCldlIGhhdmUgdHdvIExhemFydXMgcGxhbnRzIGluIHRoZSBOdXJzZSBkYXRhLg0KDQoqKnVwcGVyLTEwLW51cnNlIHVwcGVyLXBhd2FsZS1wbGFudDE6KiogTm8gbm90ZXMuDQoNCioqdXBwZXItOS1udXJzZSB1cHBlci1hYWxpaS1wbGFudDE6KiogTm90ZTogbm90IGZvdW5kIG9uIG1lYXN1cmVtZW50IDEuDQo=